| /** |
| * 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 sec |
| |
| import ( |
| "bytes" |
| "crypto/aes" |
| "crypto/cipher" |
| "crypto/ecdsa" |
| "crypto/elliptic" |
| "crypto/hmac" |
| "crypto/rand" |
| "crypto/rsa" |
| "crypto/sha256" |
| "crypto/x509" |
| "encoding/base64" |
| "io" |
| |
| "golang.org/x/crypto/hkdf" |
| |
| keywrap "github.com/NickBall/go-aes-key-wrap" |
| "github.com/apache/mynewt-artifact/errors" |
| ) |
| |
| type EncType int |
| |
| const ( |
| ENC_TYPE_AES_128 EncType = iota |
| ENC_TYPE_AES_256 |
| ENC_TYPE_RSA_2048 |
| ) |
| |
| // XXX: Only RSA supported for now. |
| type PrivEncKey struct { |
| Rsa *rsa.PrivateKey |
| } |
| |
| type PubEncKey struct { |
| Rsa *rsa.PublicKey |
| Ec *ecdsa.PublicKey |
| Aes cipher.Block |
| } |
| |
| var encTypeNameMap = map[EncType]string{ |
| ENC_TYPE_AES_128: "aes128", |
| ENC_TYPE_AES_256: "aes256", |
| ENC_TYPE_RSA_2048: "rsa2048", |
| } |
| |
| func EncTypeString(typ EncType) string { |
| s := encTypeNameMap[typ] |
| if s == "" { |
| return "unknown" |
| } else { |
| return s |
| } |
| } |
| |
| func EncStringType(s string) (EncType, error) { |
| for k, v := range encTypeNameMap { |
| if s == v { |
| return k, nil |
| } |
| } |
| |
| return 0, errors.Errorf("unknown enc type name: \"%s\"", s) |
| } |
| |
| func parsePubKePem(b []byte) (PubEncKey, error) { |
| key := PubEncKey{} |
| |
| itf, err := parsePubPemKey(b) |
| if err != nil { |
| return key, err |
| } |
| |
| switch pub := itf.(type) { |
| case *rsa.PublicKey: |
| key.Rsa = pub |
| case *ecdsa.PublicKey: |
| key.Ec = pub |
| default: |
| return key, errors.Errorf( |
| "unknown public encryption key type: %T", pub) |
| } |
| |
| return key, nil |
| } |
| |
| func parsePubKeBase64(keyBytes []byte) (PubEncKey, error) { |
| if len(keyBytes) != 16 && len(keyBytes) != 32 { |
| return PubEncKey{}, errors.Errorf( |
| "unexpected key size: %d != 16 or 32", len(keyBytes)) |
| } |
| |
| cipher, err := aes.NewCipher(keyBytes) |
| if err != nil { |
| return PubEncKey{}, errors.Wrapf(err, |
| "error creating keywrap cipher") |
| } |
| |
| return PubEncKey{ |
| Aes: cipher, |
| }, nil |
| } |
| |
| func ParsePubEncKey(keyBytes []byte) (PubEncKey, error) { |
| b, err := base64.StdEncoding.DecodeString(string(keyBytes)) |
| if err == nil { |
| return parsePubKeBase64(b) |
| } |
| |
| // Not base64-encoded; assume it is PEM. |
| return parsePubKePem(keyBytes) |
| } |
| |
| func (key *PrivEncKey) PubEncKey() PubEncKey { |
| return PubEncKey{ |
| Rsa: key.Rsa.Public().(*rsa.PublicKey), |
| } |
| } |
| |
| func (key *PubEncKey) AssertValid() { |
| if key.Rsa == nil && key.Aes == nil && key.Ec == nil { |
| panic("invalid public encryption key; neither RSA nor AES nor EC-P256") |
| } |
| } |
| |
| func (key *PubEncKey) EncType() (EncType, error) { |
| if key.Rsa != nil { |
| return ENC_TYPE_RSA_2048, nil |
| } else if key.Aes != nil { |
| switch key.Aes.BlockSize() { |
| case 128 / 8: |
| return ENC_TYPE_AES_128, nil |
| case 256 / 8: |
| return ENC_TYPE_AES_256, nil |
| default: |
| return 0, errors.Errorf("illegal AES key block size: %d", key.Aes.BlockSize()) |
| } |
| } else { |
| return 0, errors.Errorf("invalid enc key: all members nil") |
| } |
| } |
| |
| func encryptRsa(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, errors.Wrapf(err, "Error from encryption") |
| } |
| |
| return cipherSecret, nil |
| } |
| |
| func encryptEc256(peerPubK *ecdsa.PublicKey, plainSecret []byte) ([]byte, error) { |
| pk, x, y, err := elliptic.GenerateKey(elliptic.P256(), rand.Reader) |
| if err != nil { |
| return nil, errors.Wrapf(err, "Could not generate ephemeral EC keypair") |
| } |
| |
| pubk := elliptic.Marshal(elliptic.P256(), x, y) |
| |
| shared, _ := elliptic.P256().ScalarMult(peerPubK.X, peerPubK.Y, pk) |
| |
| kdf := hkdf.New(sha256.New, shared.Bytes(), nil, []byte("MCUBoot_ECIES_v1")) |
| derived := make([]byte, 48) |
| _, err = kdf.Read(derived) |
| if err != nil { |
| return nil, errors.Wrapf(err, "Error during key derivation") |
| } |
| |
| cipherSecret, err := EncryptAES(plainSecret, derived[:16], nil) |
| if err != nil { |
| return nil, errors.Wrapf(err, "Error encrypting key") |
| } |
| |
| h := hmac.New(sha256.New, derived[16:]) |
| h.Write(cipherSecret) |
| mac := h.Sum(nil) |
| |
| var tlv []byte |
| tlv = append(tlv, pubk...) |
| tlv = append(tlv, mac...) |
| tlv = append(tlv, cipherSecret...) |
| |
| return tlv, nil |
| } |
| |
| func encryptAes(c cipher.Block, plain []byte) ([]byte, error) { |
| ciph, err := keywrap.Wrap(c, plain) |
| if err != nil { |
| return nil, errors.Wrapf(err, "error key-wrapping") |
| } |
| |
| return ciph, nil |
| } |
| |
| func (k *PubEncKey) Encrypt(plain []byte) ([]byte, error) { |
| k.AssertValid() |
| |
| if k.Rsa != nil { |
| return encryptRsa(k.Rsa, plain) |
| } else if k.Ec != nil { |
| return encryptEc256(k.Ec, plain) |
| } else { |
| return encryptAes(k.Aes, plain) |
| } |
| } |
| |
| func ParsePrivEncKey(keyBytes []byte) (PrivEncKey, error) { |
| rpk, err := x509.ParsePKCS1PrivateKey(keyBytes) |
| if err != nil { |
| return PrivEncKey{}, errors.Wrapf(err, "error parsing private key file") |
| } |
| |
| return PrivEncKey{ |
| Rsa: rpk, |
| }, nil |
| } |
| |
| func decryptRsa(privk *rsa.PrivateKey, ciph []byte) ([]byte, error) { |
| rng := rand.Reader |
| plain, err := rsa.DecryptOAEP(sha256.New(), rng, privk, ciph, nil) |
| if err != nil { |
| return nil, errors.Wrapf(err, "error from encryption") |
| } |
| |
| return plain, nil |
| } |
| |
| func (k *PrivEncKey) Decrypt(ciph []byte) ([]byte, error) { |
| return decryptRsa(k.Rsa, ciph) |
| } |
| |
| func EncryptAES(plain []byte, secret []byte, nonce []byte) ([]byte, error) { |
| if len(nonce) > 16 { |
| return nil, errors.Errorf("AES nonce has invalid length: have=%d want<=16", len(nonce)) |
| } |
| |
| blk, err := aes.NewCipher(secret) |
| if err != nil { |
| return nil, errors.Errorf("Failed to create block cipher") |
| } |
| |
| iv := nonce |
| for len(iv) < 16 { |
| iv = append(iv, 0) |
| } |
| |
| stream := cipher.NewCTR(blk, iv) |
| |
| 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, errors.Wrapf(err, "Failed to read from plaintext") |
| } |
| if cnt == 0 { |
| break |
| } |
| |
| stream.XORKeyStream(encBuf, dataBuf[0:cnt]) |
| if _, err = w.Write(encBuf[0:cnt]); err != nil { |
| return nil, errors.Wrapf(err, "failed to write ciphertext") |
| } |
| } |
| |
| return w.Bytes(), nil |
| } |