sim: add testing of ECIES-P256 images

Signed-off-by: Fabio Utzig <utzig@apache.org>
diff --git a/sim/Cargo.toml b/sim/Cargo.toml
index a42d72a..da26dfb 100644
--- a/sim/Cargo.toml
+++ b/sim/Cargo.toml
@@ -15,6 +15,7 @@
 validate-primary-slot = ["mcuboot-sys/validate-primary-slot"]
 enc-rsa = ["mcuboot-sys/enc-rsa"]
 enc-kw = ["mcuboot-sys/enc-kw"]
+enc-ec256 = ["mcuboot-sys/enc-ec256"]
 bootstrap = ["mcuboot-sys/bootstrap"]
 multiimage = ["mcuboot-sys/multiimage"]
 
diff --git a/sim/mcuboot-sys/Cargo.toml b/sim/mcuboot-sys/Cargo.toml
index d20a169..d161eb3 100644
--- a/sim/mcuboot-sys/Cargo.toml
+++ b/sim/mcuboot-sys/Cargo.toml
@@ -36,6 +36,9 @@
 # Encrypt image in the secondary slot using AES-KW-128
 enc-kw = []
 
+# Encrypt image in the secondary slot using ECIES-P256
+enc-ec256 = []
+
 # Allow bootstrapping an empty/invalid primary slot from a valid secondary slot
 bootstrap = []
 
diff --git a/sim/mcuboot-sys/build.rs b/sim/mcuboot-sys/build.rs
index 7d82c48..197f3ac 100644
--- a/sim/mcuboot-sys/build.rs
+++ b/sim/mcuboot-sys/build.rs
@@ -18,6 +18,7 @@
                   env::var("CARGO_FEATURE_VALIDATE_PRIMARY_SLOT").is_ok();
     let enc_rsa = env::var("CARGO_FEATURE_ENC_RSA").is_ok();
     let enc_kw = env::var("CARGO_FEATURE_ENC_KW").is_ok();
+    let enc_ec256 = env::var("CARGO_FEATURE_ENC_EC256").is_ok();
     let bootstrap = env::var("CARGO_FEATURE_BOOTSTRAP").is_ok();
     let multiimage = env::var("CARGO_FEATURE_MULTIIMAGE").is_ok();
 
@@ -96,9 +97,10 @@
         conf.file("../../ext/mbedtls/library/platform.c");
         conf.file("../../ext/mbedtls/library/platform_util.c");
         conf.file("../../ext/mbedtls/library/asn1parse.c");
-    } else {
-        // Neither signature type, only verify sha256. The default
+    } else if !enc_ec256 {
+        // No signature type, only sha256 validation. The default
         // configuration file bundled with mbedTLS is sufficient.
+        // When using ECIES-P256 rely on Tinycrypt.
         conf.define("MCUBOOT_USE_MBED_TLS", None);
         conf.include("../../ext/mbedtls/include");
         conf.file("../../ext/mbedtls/library/sha256.c");
@@ -167,11 +169,41 @@
         }
     }
 
+    if enc_ec256 {
+        conf.define("MCUBOOT_ENCRYPT_EC256", None);
+        conf.define("MCUBOOT_ENC_IMAGES", None);
+        conf.define("MCUBOOT_USE_TINYCRYPT", None);
+
+        conf.file("../../boot/bootutil/src/encrypted.c");
+        conf.file("csupport/keys.c");
+
+        conf.include("../../ext/mbedtls-asn1/include");
+        conf.include("../../ext/tinycrypt/lib/include");
+
+        /* FIXME: fail with other signature schemes ? */
+
+        conf.file("../../ext/tinycrypt/lib/source/utils.c");
+        conf.file("../../ext/tinycrypt/lib/source/sha256.c");
+        conf.file("../../ext/tinycrypt/lib/source/ecc.c");
+        conf.file("../../ext/tinycrypt/lib/source/ecc_dsa.c");
+        conf.file("../../ext/tinycrypt/lib/source/ecc_platform_specific.c");
+
+        conf.file("../../ext/mbedtls-asn1/src/platform_util.c");
+        conf.file("../../ext/mbedtls-asn1/src/asn1parse.c");
+
+        conf.file("../../ext/tinycrypt/lib/source/aes_encrypt.c");
+        conf.file("../../ext/tinycrypt/lib/source/aes_decrypt.c");
+        conf.file("../../ext/tinycrypt/lib/source/ctr_mode.c");
+        conf.file("../../ext/tinycrypt/lib/source/hmac.c");
+        conf.file("../../ext/tinycrypt/lib/source/ecc_dh.c");
+    }
+
+
     if sig_rsa && enc_kw {
         conf.define("MBEDTLS_CONFIG_FILE", Some("<config-rsa-kw.h>"));
     } else if sig_rsa || sig_rsa3072 || enc_rsa {
         conf.define("MBEDTLS_CONFIG_FILE", Some("<config-rsa.h>"));
-    } else if sig_ecdsa && !enc_kw {
+    } else if (sig_ecdsa || enc_ec256) && !enc_kw {
         conf.define("MBEDTLS_CONFIG_FILE", Some("<config-asn1.h>"));
     } else if sig_ed25519 {
         conf.define("MBEDTLS_CONFIG_FILE", Some("<config-ed25519.h>"));
diff --git a/sim/src/image.rs b/sim/src/image.rs
index aa4b070..60369ef 100644
--- a/sim/src/image.rs
+++ b/sim/src/image.rs
@@ -42,7 +42,7 @@
     PairDep,
     UpgradeInfo,
 };
-use crate::tlv::{ManifestGen, TlvGen, TlvFlags, AES_SEC_KEY};
+use crate::tlv::{ManifestGen, TlvGen, TlvFlags};
 
 /// A builder for Images.  This describes a single run of the simulator,
 /// capturing the configuration of a particular set of devices, including
@@ -652,7 +652,8 @@
 
     // FIXME: could get status sz from bootloader
     fn status_sz(&self, align: usize) -> usize {
-        let bias = if Caps::EncRsa.present() || Caps::EncKw.present() {
+        let bias = if Caps::EncRsa.present() || Caps::EncKw.present() ||
+                Caps::EncEc256.present() {
             32
         } else {
             0
@@ -1138,7 +1139,9 @@
     let is_encrypted = (tlv.get_flags() & flag) == flag;
     let mut b_encimg = vec![];
     if is_encrypted {
-        let key = GenericArray::from_slice(AES_SEC_KEY);
+        tlv.generate_enc_key();
+        let enc_key = tlv.get_enc_key();
+        let key = GenericArray::from_slice(enc_key.as_slice());
         let nonce = GenericArray::from_slice(&[0; 16]);
         let mut cipher = Aes128Ctr::new(&key, &nonce);
         b_encimg = b_img.clone();
@@ -1258,6 +1261,9 @@
         } else {
             TlvGen::new_enc_rsa()
         }
+    } else if Caps::EncEc256.present() {
+        //FIXME: should fail with RSA signature?
+        TlvGen::new_ecdsa_ecies_p256()
     } else {
         // The non-encrypted configuration.
         if Caps::RSA2048.present() {
@@ -1278,7 +1284,8 @@
     /// Find the image contents for the given slot.  This assumes that slot 0
     /// is unencrypted, and slot 1 is encrypted.
     fn find(&self, slot: usize) -> &Vec<u8> {
-        let encrypted = Caps::EncRsa.present() || Caps::EncKw.present();
+        let encrypted = Caps::EncRsa.present() || Caps::EncKw.present() ||
+            Caps::EncEc256.present();
         match (encrypted, slot) {
             (false, _) => &self.plain,
             (true, 0) => &self.plain,
diff --git a/sim/src/tlv.rs b/sim/src/tlv.rs
index 8d21e84..716ad09 100644
--- a/sim/src/tlv.rs
+++ b/sim/src/tlv.rs
@@ -14,7 +14,7 @@
 use crate::image::ImageVersion;
 use pem;
 use base64;
-use ring::{digest, rand};
+use ring::{digest, rand, agreement, hkdf, hmac};
 use ring::signature::{
     RsaKeyPair,
     RSA_PSS_SHA256,
@@ -22,7 +22,14 @@
     ECDSA_P256_SHA256_ASN1_SIGNING,
     Ed25519KeyPair,
 };
-use untrusted;
+use aes_ctr::{
+    Aes128Ctr,
+    stream_cipher::{
+        generic_array::GenericArray,
+        NewFixStreamCipher,
+        StreamCipherCore,
+    },
+};
 use mcuboot_sys::c;
 
 #[repr(u8)]
@@ -38,6 +45,7 @@
     ED25519 = 0x24,
     ENCRSA2048 = 0x30,
     ENCKW128 = 0x31,
+    ENCEC256 = 0x32,
     DEPENDENCY = 0x40,
 }
 
@@ -72,6 +80,12 @@
 
     /// Construct the manifest for this payload.
     fn make_tlv(self: Box<Self>) -> Vec<u8>;
+
+    /// TODO: Generate a new encryption random key
+    fn generate_enc_key(&mut self) -> bool;
+
+    /// Return the current encryption key
+    fn get_enc_key(&self) -> Vec<u8>;
 }
 
 #[derive(Debug)]
@@ -84,6 +98,7 @@
     protect_size: u16,
     payload: Vec<u8>,
     dependencies: Vec<Dependency>,
+    enc_key: Vec<u8>,
 }
 
 #[derive(Debug)]
@@ -105,6 +120,7 @@
             protect_size: 0,
             payload: vec![],
             dependencies: vec![],
+            enc_key: vec![],
         }
     }
 
@@ -117,6 +133,7 @@
             protect_size: 0,
             payload: vec![],
             dependencies: vec![],
+            enc_key: vec![],
         }
     }
 
@@ -129,6 +146,7 @@
             protect_size: 0,
             payload: vec![],
             dependencies: vec![],
+            enc_key: vec![],
         }
     }
 
@@ -141,6 +159,7 @@
             protect_size: 0,
             payload: vec![],
             dependencies: vec![],
+            enc_key: vec![],
         }
     }
 
@@ -153,6 +172,7 @@
             protect_size: 0,
             payload: vec![],
             dependencies: vec![],
+            enc_key: vec![],
         }
     }
 
@@ -165,6 +185,7 @@
             protect_size: 0,
             payload: vec![],
             dependencies: vec![],
+            enc_key: vec![],
         }
     }
 
@@ -177,6 +198,7 @@
             protect_size: 0,
             payload: vec![],
             dependencies: vec![],
+            enc_key: vec![],
         }
     }
 
@@ -189,6 +211,7 @@
             protect_size: 0,
             payload: vec![],
             dependencies: vec![],
+            enc_key: vec![],
         }
     }
 
@@ -201,6 +224,7 @@
             protect_size: 0,
             payload: vec![],
             dependencies: vec![],
+            enc_key: vec![],
         }
     }
 
@@ -213,6 +237,20 @@
             protect_size: 0,
             payload: vec![],
             dependencies: vec![],
+            enc_key: vec![],
+        }
+    }
+
+    #[allow(dead_code)]
+    pub fn new_ecdsa_ecies_p256() -> TlvGen {
+        TlvGen {
+            flags: TlvFlags::ENCRYPTED as u32,
+            kinds: vec![TlvKinds::SHA256, TlvKinds::ECDSA256, TlvKinds::ENCEC256],
+            size: 4 + 32 + 4 + 32 + 4 + 72 + 4 + 113,
+            protect_size: 0,
+            payload: vec![],
+            dependencies: vec![],
+            enc_key: vec![],
         }
     }
 
@@ -341,8 +379,7 @@
                 pem::parse(include_bytes!("../../root-rsa-3072.pem").as_ref()).unwrap()
             };
             assert_eq!(key_bytes.tag, "RSA PRIVATE KEY");
-            let key_bytes = untrusted::Input::from(&key_bytes.contents);
-            let key_pair = RsaKeyPair::from_der(key_bytes).unwrap();
+            let key_pair = RsaKeyPair::from_der(&key_bytes.contents).unwrap();
             let rng = rand::SystemRandom::new();
             let mut signature = vec![0; key_pair.public_modulus_len()];
             if is_rsa2048 {
@@ -376,12 +413,10 @@
             let key_bytes = pem::parse(include_bytes!("../../root-ec-p256-pkcs8.pem").as_ref()).unwrap();
             assert_eq!(key_bytes.tag, "PRIVATE KEY");
 
-            let key_bytes = untrusted::Input::from(&key_bytes.contents);
             let key_pair = EcdsaKeyPair::from_pkcs8(&ECDSA_P256_SHA256_ASN1_SIGNING,
-                                                    key_bytes).unwrap();
+                                                    &key_bytes.contents).unwrap();
             let rng = rand::SystemRandom::new();
-            let payload = untrusted::Input::from(&sig_payload);
-            let signature = key_pair.sign(&rng, payload).unwrap();
+            let signature = key_pair.sign(&rng, &sig_payload).unwrap();
 
             result.push(TlvKinds::ECDSA256 as u8);
             result.push(0);
@@ -415,9 +450,8 @@
             let key_bytes = pem::parse(include_bytes!("../../root-ed25519.pem").as_ref()).unwrap();
             assert_eq!(key_bytes.tag, "PRIVATE KEY");
 
-            let seed = untrusted::Input::from(&key_bytes.contents[16..48]);
-            let public = untrusted::Input::from(&ED25519_PUB_KEY[12..44]);
-            let key_pair = Ed25519KeyPair::from_seed_and_public_key(seed, public).unwrap();
+            let key_pair = Ed25519KeyPair::from_seed_and_public_key(
+                &key_bytes.contents[16..48], &ED25519_PUB_KEY[12..44]).unwrap();
             let signature = key_pair.sign(&hash);
 
             result.push(TlvKinds::ED25519 as u8);
@@ -463,8 +497,84 @@
             result.extend_from_slice(&encbuf);
         }
 
+        if self.kinds.contains(&TlvKinds::ENCEC256) {
+            let key_bytes = pem::parse(include_bytes!("../../enc-ec256-pub.pem").as_ref()).unwrap();
+            assert_eq!(key_bytes.tag, "PUBLIC KEY");
+
+            let rng = rand::SystemRandom::new();
+            let pk = match agreement::EphemeralPrivateKey::generate(&agreement::ECDH_P256, &rng) {
+                Ok(v) => v,
+                Err(_) => panic!("Failed to generate ephemeral keypair"),
+            };
+
+            let pubk = match pk.compute_public_key() {
+                Ok(pubk) => pubk,
+                Err(_) => panic!("Failed computing ephemeral public key"),
+            };
+
+            let peer_pubk = agreement::UnparsedPublicKey::new(&agreement::ECDH_P256, &key_bytes.contents[26..]);
+
+            #[derive(Debug, PartialEq)]
+            struct OkmLen<T: core::fmt::Debug + PartialEq>(T);
+
+            impl hkdf::KeyType for OkmLen<usize> {
+                fn len(&self) -> usize {
+                    self.0
+                }
+            }
+
+            let derived_key = match agreement::agree_ephemeral(
+                pk, &peer_pubk, ring::error::Unspecified, |shared| {
+                    let salt = hkdf::Salt::new(hkdf::HKDF_SHA256, &[]);
+                    let prk = salt.extract(&shared);
+                    let okm = match prk.expand(&[b"MCUBoot_ECIES_v1"], OkmLen(48)) {
+                        Ok(okm) => okm,
+                        Err(_) => panic!("Failed building HKDF OKM"),
+                    };
+                    let mut buf = [0u8; 48];
+                    match okm.fill(&mut buf) {
+                        Ok(_) => Ok(buf),
+                        Err(_) => panic!("Failed generating HKDF output"),
+                    }
+                },
+            ) {
+                Ok(v) => v,
+                Err(_) => panic!("Failed building HKDF"),
+            };
+
+            let key = GenericArray::from_slice(&derived_key[..16]);
+            let nonce = GenericArray::from_slice(&[0; 16]);
+            let mut cipher = Aes128Ctr::new(&key, &nonce);
+            let mut cipherkey = self.get_enc_key();
+            cipher.apply_keystream(&mut cipherkey);
+
+            let key = hmac::Key::new(hmac::HMAC_SHA256, &derived_key[16..]);
+            let tag = hmac::sign(&key, &cipherkey);
+
+            let mut buf = vec![];
+            buf.append(&mut pubk.as_ref().to_vec());
+            buf.append(&mut tag.as_ref().to_vec());
+            buf.append(&mut cipherkey);
+
+            assert!(buf.len() == 113);
+            result.push(TlvKinds::ENCEC256 as u8);
+            result.push(0);
+            result.push(113);
+            result.push(0);
+            result.extend_from_slice(&buf);
+        }
+
         result
     }
+
+    fn generate_enc_key(&mut self) -> bool {
+        self.enc_key = AES_SEC_KEY.to_vec();
+        true
+    }
+
+    fn get_enc_key(&self) -> Vec<u8> {
+        return self.enc_key.clone();
+    }
 }
 
 include!("rsa_pub_key-rs.txt");