sim: add testing of ECIES-P256 images
Signed-off-by: Fabio Utzig <utzig@apache.org>
This commit is contained in:
parent
cbd58e3f83
commit
90f449ee5c
|
@ -15,6 +15,7 @@ overwrite-only = ["mcuboot-sys/overwrite-only"]
|
|||
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"]
|
||||
|
||||
|
|
|
@ -36,6 +36,9 @@ enc-rsa = []
|
|||
# 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 = []
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ fn main() {
|
|||
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 @@ fn main() {
|
|||
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 @@ fn main() {
|
|||
}
|
||||
}
|
||||
|
||||
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>"));
|
||||
|
|
|
@ -42,7 +42,7 @@ use crate::depends::{
|
|||
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 @@ impl Images {
|
|||
|
||||
// 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 @@ fn install_image(flash: &mut SimMultiFlash, slot: &SlotInfo, len: usize,
|
|||
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 @@ fn make_tlv() -> TlvGen {
|
|||
} 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 @@ impl ImageData {
|
|||
/// 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,
|
||||
|
|
132
sim/src/tlv.rs
132
sim/src/tlv.rs
|
@ -14,7 +14,7 @@ use byteorder::{
|
|||
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 @@ use ring::signature::{
|
|||
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 @@ pub enum TlvKinds {
|
|||
ED25519 = 0x24,
|
||||
ENCRSA2048 = 0x30,
|
||||
ENCKW128 = 0x31,
|
||||
ENCEC256 = 0x32,
|
||||
DEPENDENCY = 0x40,
|
||||
}
|
||||
|
||||
|
@ -72,6 +80,12 @@ pub trait ManifestGen {
|
|||
|
||||
/// 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 @@ pub struct TlvGen {
|
|||
protect_size: u16,
|
||||
payload: Vec<u8>,
|
||||
dependencies: Vec<Dependency>,
|
||||
enc_key: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -105,6 +120,7 @@ impl TlvGen {
|
|||
protect_size: 0,
|
||||
payload: vec![],
|
||||
dependencies: vec![],
|
||||
enc_key: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,6 +133,7 @@ impl TlvGen {
|
|||
protect_size: 0,
|
||||
payload: vec![],
|
||||
dependencies: vec![],
|
||||
enc_key: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,6 +146,7 @@ impl TlvGen {
|
|||
protect_size: 0,
|
||||
payload: vec![],
|
||||
dependencies: vec![],
|
||||
enc_key: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,6 +159,7 @@ impl TlvGen {
|
|||
protect_size: 0,
|
||||
payload: vec![],
|
||||
dependencies: vec![],
|
||||
enc_key: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -153,6 +172,7 @@ impl TlvGen {
|
|||
protect_size: 0,
|
||||
payload: vec![],
|
||||
dependencies: vec![],
|
||||
enc_key: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,6 +185,7 @@ impl TlvGen {
|
|||
protect_size: 0,
|
||||
payload: vec![],
|
||||
dependencies: vec![],
|
||||
enc_key: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,6 +198,7 @@ impl TlvGen {
|
|||
protect_size: 0,
|
||||
payload: vec![],
|
||||
dependencies: vec![],
|
||||
enc_key: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -189,6 +211,7 @@ impl TlvGen {
|
|||
protect_size: 0,
|
||||
payload: vec![],
|
||||
dependencies: vec![],
|
||||
enc_key: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -201,6 +224,7 @@ impl TlvGen {
|
|||
protect_size: 0,
|
||||
payload: vec![],
|
||||
dependencies: vec![],
|
||||
enc_key: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,6 +237,20 @@ impl TlvGen {
|
|||
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 @@ impl ManifestGen for TlvGen {
|
|||
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 @@ impl ManifestGen for TlvGen {
|
|||
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 @@ impl ManifestGen for TlvGen {
|
|||
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 @@ impl ManifestGen for TlvGen {
|
|||
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");
|
||||
|
|
Loading…
Reference in New Issue