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");