This is an automated email from the ASF dual-hosted git repository. ccollins pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mynewt-artifact.git
commit 1447da7b29f30f7909aa739ae455744d1d850a42 Author: Christopher Collins <ccoll...@apache.org> AuthorDate: Thu Jan 10 13:08:51 2019 -0800 Create new package: artifact/sec This package contains generic security functionality. Since mfgimages now require signatures (not just normal images), it doesn't make sense to keep all the security functionality in the `image` package. --- image/create.go | 111 ++++++++++++++++++++++---------------- image/keys_test.go | 63 +++++++--------------- image/v1.go | 27 +++++----- sec/encrypt.go | 42 +++++++++++++++ {image => sec}/key.go | 98 ++++++++++----------------------- image/encrypted.go => sec/pkcs.go | 3 +- 6 files changed, 167 insertions(+), 177 deletions(-) diff --git a/image/create.go b/image/create.go index 76e701d..3ec8770 100644 --- a/image/create.go +++ b/image/create.go @@ -22,8 +22,6 @@ package image import ( "bytes" "crypto" - "crypto/aes" - "crypto/cipher" "crypto/ecdsa" "crypto/rand" "crypto/rsa" @@ -31,17 +29,17 @@ import ( "encoding/asn1" "encoding/binary" "encoding/hex" - "io" "io/ioutil" "math/big" + "mynewt.apache.org/newt/artifact/sec" "mynewt.apache.org/newt/util" ) type ImageCreator struct { Body []byte Version ImageVersion - SigKeys []ImageSigKey + SigKeys []sec.SignKey PlainSecret []byte CipherSecret []byte HeaderSize int @@ -53,7 +51,7 @@ type ImageCreateOpts struct { SrcBinFilename string SrcEncKeyFilename string Version ImageVersion - SigKeys []ImageSigKey + SigKeys []sec.SignKey LoaderHash []byte } @@ -69,6 +67,23 @@ func NewImageCreator() ImageCreator { } } +func sigTlvType(key sec.SignKey) uint8 { + key.AssertValid() + + if key.Rsa != nil { + return IMAGE_TLV_RSA2048 + } else { + switch key.Ec.Curve.Params().Name { + case "P-224": + return IMAGE_TLV_ECDSA224 + case "P-256": + return IMAGE_TLV_ECDSA256 + default: + return 0 + } + } +} + func GenerateEncTlv(cipherSecret []byte) (ImageTlv, error) { var encType uint8 @@ -90,7 +105,7 @@ func GenerateEncTlv(cipherSecret []byte) (ImageTlv, error) { }, nil } -func GenerateSigRsa(key ImageSigKey, hash []byte) ([]byte, error) { +func GenerateSigRsa(key sec.SignKey, hash []byte) ([]byte, error) { opts := rsa.PSSOptions{ SaltLength: rsa.PSSSaltLengthEqualsHash, } @@ -103,7 +118,7 @@ func GenerateSigRsa(key ImageSigKey, hash []byte) ([]byte, error) { return signature, nil } -func GenerateSigEc(key ImageSigKey, hash []byte) ([]byte, error) { +func GenerateSigEc(key sec.SignKey, hash []byte) ([]byte, error) { r, s, err := ecdsa.Sign(rand.Reader, key.Ec, hash) if err != nil { return nil, util.FmtNewtError("Failed to compute signature: %s", err) @@ -119,7 +134,7 @@ func GenerateSigEc(key ImageSigKey, hash []byte) ([]byte, error) { return nil, util.FmtNewtError("Failed to construct signature: %s", err) } - sigLen := key.sigLen() + sigLen := key.SigLen() if len(signature) > int(sigLen) { return nil, util.FmtNewtError("Something is really wrong\n") } @@ -130,8 +145,8 @@ func GenerateSigEc(key ImageSigKey, hash []byte) ([]byte, error) { return signature, nil } -func GenerateSig(key ImageSigKey, hash []byte) ([]byte, error) { - key.assertValid() +func GenerateSig(key sec.SignKey, hash []byte) ([]byte, error) { + key.AssertValid() if key.Rsa != nil { return GenerateSigRsa(key, hash) @@ -141,7 +156,7 @@ func GenerateSig(key ImageSigKey, hash []byte) ([]byte, error) { } func BuildKeyHashTlv(keyBytes []byte) ImageTlv { - data := RawKeyHash(keyBytes) + data := sec.RawKeyHash(keyBytes) return ImageTlv{ Header: ImageTlvHdr{ Type: IMAGE_TLV_KEYHASH, @@ -152,11 +167,11 @@ func BuildKeyHashTlv(keyBytes []byte) ImageTlv { } } -func BuildSigTlvs(keys []ImageSigKey, hash []byte) ([]ImageTlv, error) { +func BuildSigTlvs(keys []sec.SignKey, hash []byte) ([]ImageTlv, error) { var tlvs []ImageTlv for _, key := range keys { - key.assertValid() + key.AssertValid() // Key hash TLV. pubKey, err := key.PubBytes() @@ -173,7 +188,7 @@ func BuildSigTlvs(keys []ImageSigKey, hash []byte) ([]ImageTlv, error) { } tlv = ImageTlv{ Header: ImageTlvHdr{ - Type: key.sigTlvType(), + Type: sigTlvType(key), Len: uint16(len(sig)), }, Data: sig, @@ -184,6 +199,40 @@ func BuildSigTlvs(keys []ImageSigKey, hash []byte) ([]ImageTlv, error) { return tlvs, nil } +func GeneratePlainSecret() ([]byte, error) { + plainSecret := make([]byte, 16) + if _, err := rand.Read(plainSecret); err != nil { + return nil, util.FmtNewtError( + "Random generation error: %s\n", err) + } + + return plainSecret, nil +} + +func GenerateCipherSecret(pubKeBytes []byte, + plainSecret []byte) ([]byte, error) { + + // Try reading as PEM (asymetric key). + rsaPubKe, err := sec.ParsePubKePem(pubKeBytes) + if err != nil { + return nil, err + } + if rsaPubKe != nil { + return sec.EncryptSecretRsa(rsaPubKe, plainSecret) + } + + // Not PEM; assume this is a base64 encoded symetric key + aesPubKe, err := sec.ParseKeBase64(pubKeBytes) + if err != nil { + return nil, err + } + if aesPubKe != nil { + return sec.EncryptSecretAes(aesPubKe, plainSecret) + } + + return nil, util.FmtNewtError("Invalid image-crypt key") +} + func GenerateImage(opts ImageCreateOpts) (Image, error) { ic := NewImageCreator() @@ -279,38 +328,6 @@ func calcHash(initialHash []byte, hdr ImageHdr, pad []byte, return hash.Sum(nil), nil } -func EncryptImageBody(imageBody []byte, secret []byte) ([]byte, error) { - block, err := aes.NewCipher(secret) - if err != nil { - return nil, util.NewNewtError("Failed to create block cipher") - } - nonce := make([]byte, 16) - stream := cipher.NewCTR(block, nonce) - - dataBuf := make([]byte, 16) - encBuf := make([]byte, 16) - r := bytes.NewReader(imageBody) - w := bytes.Buffer{} - for { - cnt, err := r.Read(dataBuf) - if err != nil && err != io.EOF { - return nil, util.FmtNewtError( - "Failed to read from image body: %s", err.Error()) - } - if cnt == 0 { - break - } - - stream.XORKeyStream(encBuf, dataBuf[0:cnt]) - if _, err = w.Write(encBuf[0:cnt]); err != nil { - return nil, util.FmtNewtError( - "Failed to write to image body: %s", err.Error()) - } - } - - return w.Bytes(), nil -} - func (ic *ImageCreator) Create() (Image, error) { img := Image{} @@ -354,7 +371,7 @@ func (ic *ImageCreator) Create() (Image, error) { // Followed by data. if ic.CipherSecret != nil { - encBody, err := EncryptImageBody(ic.Body, ic.PlainSecret) + encBody, err := sec.EncryptAES(ic.Body, ic.PlainSecret) if err != nil { return img, err } diff --git a/image/keys_test.go b/image/keys_test.go index 577d9cd..769848a 100644 --- a/image/keys_test.go +++ b/image/keys_test.go @@ -22,8 +22,10 @@ package image_test import ( "io/ioutil" "os" - "path" "testing" + + "mynewt.apache.org/newt/artifact/image" + "mynewt.apache.org/newt/artifact/sec" ) func TestRSA(t *testing.T) { @@ -43,15 +45,15 @@ func TestPlainEcdsaPkcs8(t *testing.T) { } func TestEncryptedRSA(t *testing.T) { - image.KeyPassword = []byte("sample") + sec.KeyPassword = []byte("sample") signatureTest(t, rsaEncryptedPrivate) - image.KeyPassword = []byte{} + sec.KeyPassword = []byte{} } func TestEncryptedEcdsa(t *testing.T) { - image.KeyPassword = []byte("sample") + sec.KeyPassword = []byte("sample") signatureTest(t, ecdsaEncryptedPrivate) - image.KeyPassword = []byte{} + sec.KeyPassword = []byte{} } func signatureTest(t *testing.T, privateKey []byte) { @@ -65,58 +67,29 @@ func signatureTest(t *testing.T, privateKey []byte) { // much, since the header will be placed on it by the image // tool. - simpleName := path.Join(tmpdir, "simple.bin") - hashedName := path.Join(tmpdir, "simple-hashed.bin") - signedName := path.Join(tmpdir, "simple-signed.bin") - keyName := path.Join(tmpdir, "private.pem") - - tmp := make([]byte, 256) - for i := 0; i < len(tmp); i++ { - tmp[i] = byte(i & 0xFF) - } - err = ioutil.WriteFile(simpleName, tmp, 0644) - if err != nil { - t.Fatal(err) + body := make([]byte, 256) + for i := 0; i < len(body); i++ { + body[i] = byte(i) } - img, err := image.NewImage(simpleName, hashedName) - if err != nil { - t.Fatal(err) - } - - img.SetVersion("1.5") - if err != nil { - t.Fatal(err) - } + ic := image.NewImageCreator() + ic.Version = image.ImageVersion{1, 5, 0, 0} + ic.Body = body - img.Generate(nil) - if err != nil { + if _, err := ic.Create(); err != nil { t.Fatal(err) } // Now try with a signature. - err = ioutil.WriteFile(keyName, privateKey, 0644) + key, err := sec.BuildPrivateKey(privateKey) if err != nil { t.Fatal(err) } + ic.SigKeys = append(ic.SigKeys, key) - img, err = image.NewImage(simpleName, signedName) - if err != nil { - t.Fatal(err) - } - - err = img.SetSigningKey(keyName, 0) - if err != nil { - t.Fatal(err) - } - - err = img.SetVersion("1.6") - if err != nil { - t.Fatal(err) - } + ic.Version = image.ImageVersion{1, 6, 0, 0} - err = img.Generate(nil) - if err != nil { + if _, err := ic.Create(); err != nil { t.Fatal(err) } } diff --git a/image/v1.go b/image/v1.go index 0dc10a5..67a451f 100644 --- a/image/v1.go +++ b/image/v1.go @@ -34,6 +34,7 @@ import ( "io" "io/ioutil" + "mynewt.apache.org/newt/artifact/sec" "mynewt.apache.org/newt/util" ) @@ -157,8 +158,8 @@ func (img *ImageV1) Write(w io.Writer) (int, error) { return offs.TotalSize, nil } -func (key *ImageSigKey) sigHdrTypeV1() (uint32, error) { - key.assertValid() +func sigHdrTypeV1(key sec.SignKey) (uint32, error) { + key.AssertValid() if key.Rsa != nil { if UseRsaPss { @@ -178,8 +179,8 @@ func (key *ImageSigKey) sigHdrTypeV1() (uint32, error) { } } -func (key *ImageSigKey) sigTlvTypeV1() uint8 { - key.assertValid() +func sigTlvTypeV1(key sec.SignKey) uint8 { + key.AssertValid() if key.Rsa != nil { return IMAGEv1_TLV_RSA2048 @@ -216,7 +217,7 @@ func generateV1SigRsa(key *rsa.PrivateKey, hash []byte) ([]byte, error) { return signature, nil } -func generateV1SigTlvRsa(key ImageSigKey, hash []byte) (ImageTlv, error) { +func generateV1SigTlvRsa(key sec.SignKey, hash []byte) (ImageTlv, error) { sig, err := generateV1SigRsa(key.Rsa, hash) if err != nil { return ImageTlv{}, err @@ -224,7 +225,7 @@ func generateV1SigTlvRsa(key ImageSigKey, hash []byte) (ImageTlv, error) { return ImageTlv{ Header: ImageTlvHdr{ - Type: key.sigTlvTypeV1(), + Type: sigTlvTypeV1(key), Pad: 0, Len: 256, /* 2048 bits */ }, @@ -232,13 +233,13 @@ func generateV1SigTlvRsa(key ImageSigKey, hash []byte) (ImageTlv, error) { }, nil } -func generateV1SigTlvEc(key ImageSigKey, hash []byte) (ImageTlv, error) { +func generateV1SigTlvEc(key sec.SignKey, hash []byte) (ImageTlv, error) { sig, err := GenerateSigEc(key, hash) if err != nil { return ImageTlv{}, err } - sigLen := key.sigLen() + sigLen := key.SigLen() if len(sig) > int(sigLen) { return ImageTlv{}, util.FmtNewtError("Something is really wrong\n") } @@ -258,7 +259,7 @@ func generateV1SigTlvEc(key ImageSigKey, hash []byte) (ImageTlv, error) { return ImageTlv{ Header: ImageTlvHdr{ - Type: key.sigTlvTypeV1(), + Type: sigTlvTypeV1(key), Pad: 0, Len: sigLen + uint16(len(pad)), }, @@ -266,8 +267,8 @@ func generateV1SigTlvEc(key ImageSigKey, hash []byte) (ImageTlv, error) { }, nil } -func generateV1SigTlv(key ImageSigKey, hash []byte) (ImageTlv, error) { - key.assertValid() +func generateV1SigTlv(key sec.SignKey, hash []byte) (ImageTlv, error) { + key.AssertValid() if key.Rsa != nil { return generateV1SigTlvRsa(key, hash) @@ -356,12 +357,12 @@ func (ic *ImageCreator) CreateV1() (ImageV1, error) { } if len(ic.SigKeys) > 0 { - keyFlag, err := ic.SigKeys[0].sigHdrTypeV1() + keyFlag, err := sigHdrTypeV1(ic.SigKeys[0]) if err != nil { return ri, err } hdr.Flags |= keyFlag - hdr.TlvSz = 4 + ic.SigKeys[0].sigLen() + hdr.TlvSz = 4 + ic.SigKeys[0].SigLen() } hdr.TlvSz += 4 + 32 diff --git a/sec/encrypt.go b/sec/encrypt.go new file mode 100644 index 0000000..bd99594 --- /dev/null +++ b/sec/encrypt.go @@ -0,0 +1,42 @@ +package sec + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "io" + + "mynewt.apache.org/newt/util" +) + +func EncryptAES(plain []byte, secret []byte) ([]byte, error) { + block, err := aes.NewCipher(secret) + if err != nil { + return nil, util.NewNewtError("Failed to create block cipher") + } + nonce := make([]byte, 16) + stream := cipher.NewCTR(block, nonce) + + dataBuf := make([]byte, 16) + encBuf := make([]byte, 16) + r := bytes.NewReader(plain) + w := bytes.Buffer{} + for { + cnt, err := r.Read(dataBuf) + if err != nil && err != io.EOF { + return nil, util.FmtNewtError( + "Failed to read from plaintext: %s", err.Error()) + } + if cnt == 0 { + break + } + + stream.XORKeyStream(encBuf, dataBuf[0:cnt]) + if _, err = w.Write(encBuf[0:cnt]); err != nil { + return nil, util.FmtNewtError( + "Failed to write ciphertext: %s", err.Error()) + } + } + + return w.Bytes(), nil +} diff --git a/image/key.go b/sec/key.go similarity index 77% rename from image/key.go rename to sec/key.go index a343e2d..89b5f49 100644 --- a/image/key.go +++ b/sec/key.go @@ -17,7 +17,7 @@ * under the License. */ -package image +package sec import ( "crypto/aes" @@ -37,7 +37,7 @@ import ( "mynewt.apache.org/newt/util" ) -type ImageSigKey struct { +type SignKey struct { // Only one of these members is non-nil. Rsa *rsa.PrivateKey Ec *ecdsa.PrivateKey @@ -92,24 +92,21 @@ func ParsePrivateKey(keyBytes []byte) (interface{}, error) { // encryption. privKey, err = parseEncryptedPrivateKey(block.Bytes) if err != nil { - return nil, util.FmtNewtError("Unable to decode encrypted private key: %s", err) + return nil, util.FmtNewtError( + "Unable to decode encrypted private key: %s", err) } } if privKey == nil { - return nil, util.NewNewtError("Unknown private key format, EC/RSA private " + - "key in PEM format only.") + return nil, util.NewNewtError( + "Unknown private key format, EC/RSA private " + + "key in PEM format only.") } return privKey, nil } -func ReadKey(filename string) (ImageSigKey, error) { - key := ImageSigKey{} - - keyBytes, err := ioutil.ReadFile(filename) - if err != nil { - return key, util.FmtNewtError("Error reading key file: %s", err) - } +func BuildPrivateKey(keyBytes []byte) (SignKey, error) { + key := SignKey{} privKey, err := ParsePrivateKey(keyBytes) if err != nil { @@ -128,8 +125,18 @@ func ReadKey(filename string) (ImageSigKey, error) { return key, nil } -func ReadKeys(filenames []string) ([]ImageSigKey, error) { - keys := make([]ImageSigKey, len(filenames)) +func ReadKey(filename string) (SignKey, error) { + keyBytes, err := ioutil.ReadFile(filename) + if err != nil { + return SignKey{}, util.FmtNewtError( + "Error reading key file: %s", err) + } + + return BuildPrivateKey(keyBytes) +} + +func ReadKeys(filenames []string) ([]SignKey, error) { + keys := make([]SignKey, len(filenames)) for i, filename := range filenames { key, err := ReadKey(filename) @@ -143,7 +150,7 @@ func ReadKeys(filenames []string) ([]ImageSigKey, error) { return keys, nil } -func (key *ImageSigKey) assertValid() { +func (key *SignKey) AssertValid() { if key.Rsa == nil && key.Ec == nil { panic("invalid key; neither RSA nor ECC") } @@ -153,8 +160,8 @@ func (key *ImageSigKey) assertValid() { } } -func (key *ImageSigKey) PubBytes() ([]uint8, error) { - key.assertValid() +func (key *SignKey) PubBytes() ([]uint8, error) { + key.AssertValid() var pubkey []byte @@ -179,8 +186,8 @@ func RawKeyHash(pubKeyBytes []byte) []byte { return sum[:4] } -func (key *ImageSigKey) sigLen() uint16 { - key.assertValid() +func (key *SignKey) SigLen() uint16 { + key.AssertValid() if key.Rsa != nil { return 256 @@ -196,23 +203,6 @@ func (key *ImageSigKey) sigLen() uint16 { } } -func (key *ImageSigKey) sigTlvType() uint8 { - key.assertValid() - - if key.Rsa != nil { - return IMAGE_TLV_RSA2048 - } else { - switch key.Ec.Curve.Params().Name { - case "P-224": - return IMAGE_TLV_ECDSA224 - case "P-256": - return IMAGE_TLV_ECDSA256 - default: - return 0 - } - } -} - func ParsePubKePem(keyBytes []byte) (*rsa.PublicKey, error) { b, _ := pem.Decode(keyBytes) if b == nil { @@ -297,7 +287,7 @@ func ParseKeBase64(keyBytes []byte) (cipher.Block, error) { return cipher, nil } -func encryptSecretAes(c cipher.Block, plainSecret []byte) ([]byte, error) { +func EncryptSecretAes(c cipher.Block, plainSecret []byte) ([]byte, error) { cipherSecret, err := keywrap.Wrap(c, plainSecret) if err != nil { return nil, util.FmtNewtError("Error key-wrapping: %s", err.Error()) @@ -305,37 +295,3 @@ func encryptSecretAes(c cipher.Block, plainSecret []byte) ([]byte, error) { return cipherSecret, nil } - -func GeneratePlainSecret() ([]byte, error) { - plainSecret := make([]byte, 16) - if _, err := rand.Read(plainSecret); err != nil { - return nil, util.FmtNewtError( - "Random generation error: %s\n", err) - } - - return plainSecret, nil -} - -func GenerateCipherSecret(pubKeBytes []byte, - plainSecret []byte) ([]byte, error) { - - // Try reading as PEM (asymetric key). - rsaPubKe, err := ParsePubKePem(pubKeBytes) - if err != nil { - return nil, err - } - if rsaPubKe != nil { - return EncryptSecretRsa(rsaPubKe, plainSecret) - } - - // Not PEM; assume this is a base64 encoded symetric key - aesPubKe, err := ParseKeBase64(pubKeBytes) - if err != nil { - return nil, err - } - if aesPubKe != nil { - return encryptSecretAes(aesPubKe, plainSecret) - } - - return nil, util.FmtNewtError("Invalid image-crypt key") -} diff --git a/image/encrypted.go b/sec/pkcs.go similarity index 99% rename from image/encrypted.go rename to sec/pkcs.go index 0547e2f..3cf709f 100644 --- a/image/encrypted.go +++ b/sec/pkcs.go @@ -18,7 +18,7 @@ */ // Decoder for PKCS#5 encrypted PKCS#8 private keys. -package image +package sec import ( "crypto/aes" @@ -78,6 +78,7 @@ type hashFunc func() hash.Hash func parseEncryptedPrivateKey(der []byte) (key interface{}, err error) { var wrapper pkcs5 + fmt.Printf("unmarshalling %v\n", der) if _, err = asn1.Unmarshal(der, &wrapper); err != nil { return nil, err }