| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package image |
| |
| import ( |
| "crypto/aes" |
| "crypto/cipher" |
| "crypto/ecdsa" |
| "crypto/rand" |
| "crypto/rsa" |
| "crypto/sha256" |
| "crypto/x509" |
| "encoding/asn1" |
| "encoding/base64" |
| "encoding/pem" |
| "io/ioutil" |
| |
| keywrap "github.com/NickBall/go-aes-key-wrap" |
| |
| "mynewt.apache.org/newt/util" |
| ) |
| |
| type ImageSigKey struct { |
| // Only one of these members is non-nil. |
| Rsa *rsa.PrivateKey |
| Ec *ecdsa.PrivateKey |
| } |
| |
| func ParsePrivateKey(keyBytes []byte) (interface{}, error) { |
| var privKey interface{} |
| var err error |
| |
| block, data := pem.Decode(keyBytes) |
| if block != nil && block.Type == "EC PARAMETERS" { |
| /* |
| * Openssl prepends an EC PARAMETERS block before the |
| * key itself. If we see this first, just skip it, |
| * and go on to the data block. |
| */ |
| block, _ = pem.Decode(data) |
| } |
| if block != nil && block.Type == "RSA PRIVATE KEY" { |
| /* |
| * ParsePKCS1PrivateKey returns an RSA private key from its ASN.1 |
| * PKCS#1 DER encoded form. |
| */ |
| privKey, err = x509.ParsePKCS1PrivateKey(block.Bytes) |
| if err != nil { |
| return nil, util.FmtNewtError( |
| "Private key parsing failed: %s", err) |
| } |
| } |
| if block != nil && block.Type == "EC PRIVATE KEY" { |
| /* |
| * ParseECPrivateKey returns a EC private key |
| */ |
| privKey, err = x509.ParseECPrivateKey(block.Bytes) |
| if err != nil { |
| return nil, util.FmtNewtError( |
| "Private key parsing failed: %s", err) |
| } |
| } |
| if block != nil && block.Type == "PRIVATE KEY" { |
| // This indicates a PKCS#8 unencrypted private key. |
| // The particular type of key will be indicated within |
| // the key itself. |
| privKey, err = x509.ParsePKCS8PrivateKey(block.Bytes) |
| if err != nil { |
| return nil, util.FmtNewtError( |
| "Private key parsing failed: %s", err) |
| } |
| } |
| if block != nil && block.Type == "ENCRYPTED PRIVATE KEY" { |
| // This indicates a PKCS#8 key wrapped with PKCS#5 |
| // encryption. |
| privKey, err = parseEncryptedPrivateKey(block.Bytes) |
| if err != nil { |
| 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 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) |
| } |
| |
| privKey, err := ParsePrivateKey(keyBytes) |
| if err != nil { |
| return key, err |
| } |
| |
| switch priv := privKey.(type) { |
| case *rsa.PrivateKey: |
| key.Rsa = priv |
| case *ecdsa.PrivateKey: |
| key.Ec = priv |
| default: |
| return key, util.NewNewtError("Unknown private key format") |
| } |
| |
| return key, nil |
| } |
| |
| func ReadKeys(filenames []string) ([]ImageSigKey, error) { |
| keys := make([]ImageSigKey, len(filenames)) |
| |
| for i, filename := range filenames { |
| key, err := ReadKey(filename) |
| if err != nil { |
| return nil, err |
| } |
| |
| keys[i] = key |
| } |
| |
| return keys, nil |
| } |
| |
| func (key *ImageSigKey) assertValid() { |
| if key.Rsa == nil && key.Ec == nil { |
| panic("invalid key; neither RSA nor ECC") |
| } |
| |
| if key.Rsa != nil && key.Ec != nil { |
| panic("invalid key; neither RSA nor ECC") |
| } |
| } |
| |
| func (key *ImageSigKey) PubBytes() ([]uint8, error) { |
| key.assertValid() |
| |
| var pubkey []byte |
| |
| if key.Rsa != nil { |
| pubkey, _ = asn1.Marshal(key.Rsa.PublicKey) |
| } else { |
| switch key.Ec.Curve.Params().Name { |
| case "P-224": |
| fallthrough |
| case "P-256": |
| pubkey, _ = x509.MarshalPKIXPublicKey(&key.Ec.PublicKey) |
| default: |
| return nil, util.NewNewtError("Unsupported ECC curve") |
| } |
| } |
| |
| return pubkey, nil |
| } |
| |
| func RawKeyHash(pubKeyBytes []byte) []byte { |
| sum := sha256.Sum256(pubKeyBytes) |
| return sum[:4] |
| } |
| |
| func (key *ImageSigKey) sigLen() uint16 { |
| key.assertValid() |
| |
| if key.Rsa != nil { |
| return 256 |
| } else { |
| switch key.Ec.Curve.Params().Name { |
| case "P-224": |
| return 68 |
| case "P-256": |
| return 72 |
| default: |
| return 0 |
| } |
| } |
| } |
| |
| 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 { |
| return nil, nil |
| } |
| |
| if b.Type != "PUBLIC KEY" && b.Type != "RSA PUBLIC KEY" { |
| return nil, util.NewNewtError("Invalid PEM file") |
| } |
| |
| pub, err := x509.ParsePKIXPublicKey(b.Bytes) |
| if err != nil { |
| return nil, util.FmtNewtError( |
| "Error parsing pubkey file: %s", err.Error()) |
| } |
| |
| var pubk *rsa.PublicKey |
| switch pub.(type) { |
| case *rsa.PublicKey: |
| pubk = pub.(*rsa.PublicKey) |
| default: |
| return nil, util.FmtNewtError( |
| "Error parsing pubkey file: %s", err.Error()) |
| } |
| |
| return pubk, nil |
| } |
| |
| func ParsePrivKeDer(keyBytes []byte) (*rsa.PrivateKey, error) { |
| privKey, err := x509.ParsePKCS1PrivateKey(keyBytes) |
| if err != nil { |
| return nil, util.FmtNewtError( |
| "Error parsing private key file: %s", err.Error()) |
| } |
| |
| return privKey, nil |
| } |
| |
| func EncryptSecretRsa(pubk *rsa.PublicKey, plainSecret []byte) ([]byte, error) { |
| rng := rand.Reader |
| cipherSecret, err := rsa.EncryptOAEP( |
| sha256.New(), rng, pubk, plainSecret, nil) |
| if err != nil { |
| return nil, util.FmtNewtError( |
| "Error from encryption: %s\n", err.Error()) |
| } |
| |
| return cipherSecret, nil |
| } |
| |
| func DecryptSecretRsa(privk *rsa.PrivateKey, |
| cipherSecret []byte) ([]byte, error) { |
| |
| rng := rand.Reader |
| plainSecret, err := rsa.DecryptOAEP( |
| sha256.New(), rng, privk, cipherSecret, nil) |
| if err != nil { |
| return nil, util.FmtNewtError( |
| "Error from encryption: %s\n", err.Error()) |
| } |
| |
| return plainSecret, nil |
| } |
| |
| func ParseKeBase64(keyBytes []byte) (cipher.Block, error) { |
| kek, err := base64.StdEncoding.DecodeString(string(keyBytes)) |
| if err != nil { |
| return nil, util.FmtNewtError( |
| "Error decoding kek: %s", err.Error()) |
| } |
| if len(kek) != 16 { |
| return nil, util.FmtNewtError( |
| "Unexpected key size: %d != 16", len(kek)) |
| } |
| |
| cipher, err := aes.NewCipher(kek) |
| if err != nil { |
| return nil, util.FmtNewtError( |
| "Error creating keywrap cipher: %s", err.Error()) |
| } |
| |
| return cipher, nil |
| } |
| |
| 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()) |
| } |
| |
| 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") |
| } |