Add support for image encryption using ECIES-P256

This adds support for encrypting images using ECIES-P256. The MCUBoot
implementation was described here:

https://github.com/JuulLabs-OSS/mcuboot/blob/master/docs/encrypted_images.md#ecies-p256-encryption

Signed-off-by: Fabio Utzig <utzig@apache.org>
diff --git a/image/create.go b/image/create.go
index 0ee3bb9..6fc0f27 100644
--- a/image/create.go
+++ b/image/create.go
@@ -129,6 +129,8 @@
 
 	if len(cipherSecret) == 256 {
 		encType = IMAGE_TLV_ENC_RSA
+	} else if len(cipherSecret) == 113 {
+		encType = IMAGE_TLV_ENC_EC256
 	} else if len(cipherSecret) == 24 {
 		encType = IMAGE_TLV_ENC_KEK
 	} else {
diff --git a/image/image.go b/image/image.go
index 7cd1e1a..f0a033e 100644
--- a/image/image.go
+++ b/image/image.go
@@ -64,6 +64,7 @@
 	IMAGE_TLV_ED25519   = 0x24
 	IMAGE_TLV_ENC_RSA   = 0x30
 	IMAGE_TLV_ENC_KEK   = 0x31
+	IMAGE_TLV_ENC_EC256 = 0x32
 	IMAGE_TLV_AES_NONCE = 0x50
 	IMAGE_TLV_SECRET_ID = 0x60
 )
@@ -78,6 +79,7 @@
 	IMAGE_TLV_ED25519:   "ED25519",
 	IMAGE_TLV_ENC_RSA:   "ENC_RSA",
 	IMAGE_TLV_ENC_KEK:   "ENC_KEK",
+	IMAGE_TLV_ENC_EC256: "ENC_EC256",
 	IMAGE_TLV_AES_NONCE: "AES_NONCE",
 	IMAGE_TLV_SECRET_ID: "SEC_KEY_ID",
 }
@@ -171,7 +173,8 @@
 
 func ImageTlvTypeIsSecret(tlvType uint8) bool {
 	return tlvType == IMAGE_TLV_ENC_RSA ||
-		tlvType == IMAGE_TLV_ENC_KEK
+		tlvType == IMAGE_TLV_ENC_KEK ||
+		tlvType == IMAGE_TLV_ENC_EC256
 }
 
 func (ver ImageVersion) String() string {
diff --git a/sec/encrypt.go b/sec/encrypt.go
index b9a579e..85d4571 100644
--- a/sec/encrypt.go
+++ b/sec/encrypt.go
@@ -23,11 +23,15 @@
 	"bytes"
 	"crypto/aes"
 	"crypto/cipher"
+	"crypto/ecdsa"
+	"crypto/elliptic"
+	"crypto/hmac"
 	"crypto/rand"
 	"crypto/rsa"
 	"crypto/sha256"
 	"crypto/x509"
 	"encoding/base64"
+	"golang.org/x/crypto/hkdf"
 	"io"
 
 	keywrap "github.com/NickBall/go-aes-key-wrap"
@@ -49,6 +53,7 @@
 
 type PubEncKey struct {
 	Rsa *rsa.PublicKey
+	Ec  *ecdsa.PublicKey
 	Aes cipher.Block
 }
 
@@ -88,6 +93,8 @@
 	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)
@@ -130,8 +137,8 @@
 }
 
 func (key *PubEncKey) AssertValid() {
-	if key.Rsa == nil && key.Aes == nil {
-		panic("invalid public encryption key; neither RSA nor AES")
+	if key.Rsa == nil && key.Aes == nil && key.Ec == nil {
+		panic("invalid public encryption key; neither RSA nor AES nor EC-P256")
 	}
 }
 
@@ -163,6 +170,40 @@
 	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 {
@@ -177,6 +218,8 @@
 
 	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)
 	}