diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb955525b61..f53b2e8127a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,7 +49,7 @@ jobs: if: matrix.target == 'x86_64-unknown-linux-gnu' - name: Run tests - run: cargo test --features serde-extensions,virt + run: cargo test --features serde-extensions,virt,p384,p521 if: matrix.target == 'x86_64-unknown-linux-gnu' - name: Check formatting diff --git a/Cargo.toml b/Cargo.toml index 90cab17f18b..ba061451c56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,9 @@ littlefs2 = "0.4.0" p256-cortex-m4 = { version = "0.1.0-alpha.6", features = ["prehash", "sec1-signatures"] } salty = { version = "0.3.0", features = ["cose"] } serde-indexed = "0.1.0" +p384 = { version = "0.13.0", optional = true, default-features = false, features = ["sha384", "ecdh", "ecdsa"] } +p521 = { version = "0.13.3", optional = true, default-features = false, features = ["sha512", "ecdh", "ecdsa"] } +ecdsa = { version = "0.16.9", optional = true, default-features = false } [dev-dependencies] # Testing @@ -110,6 +113,8 @@ hmac-sha1 = [] hmac-sha256 = [] hmac-sha512 = [] p256 = [] +p384 = ["dep:p384"] +p521 = ["dep:p521", "dep:ecdsa"] sha256 = [] tdes = ["des"] totp = ["sha-1"] diff --git a/src/client/mechanisms.rs b/src/client/mechanisms.rs index c24a3413523..8aa1213973e 100644 --- a/src/client/mechanisms.rs +++ b/src/client/mechanisms.rs @@ -402,6 +402,185 @@ pub trait P256: CryptoClient { } } +#[cfg(feature = "p384")] +impl P384 for ClientImplementation {} + +#[cfg(feature = "p384")] +pub trait P384: CryptoClient { + fn generate_p384_private_key( + &mut self, + persistence: Location, + ) -> ClientResult<'_, reply::GenerateKey, Self> { + self.generate_key( + Mechanism::P384, + StorageAttributes::new().set_persistence(persistence), + ) + } + + fn derive_p384_public_key( + &mut self, + private_key: KeyId, + persistence: Location, + ) -> ClientResult<'_, reply::DeriveKey, Self> { + self.derive_key( + Mechanism::P384, + private_key, + None, + StorageAttributes::new().set_persistence(persistence), + ) + } + + fn deserialize_p384_key<'c>( + &'c mut self, + serialized_key: &[u8], + format: KeySerialization, + attributes: StorageAttributes, + ) -> ClientResult<'c, reply::DeserializeKey, Self> { + self.deserialize_key(Mechanism::P384, serialized_key, format, attributes) + } + + fn serialize_p384_key( + &mut self, + key: KeyId, + format: KeySerialization, + ) -> ClientResult<'_, reply::SerializeKey, Self> { + self.serialize_key(Mechanism::P384, key, format) + } + + // generally, don't offer multiple versions of a mechanism, if possible. + // try using the simplest when given the choice. + // hashing is something users can do themselves hopefully :) + // + // on the other hand: if users need sha256, then if the service runs in secure trustzone + // domain, we'll maybe need two copies of the sha2 code + fn sign_p384<'c>( + &'c mut self, + key: KeyId, + message: &[u8], + format: SignatureSerialization, + ) -> ClientResult<'c, reply::Sign, Self> { + self.sign(Mechanism::P384, key, message, format) + } + + fn verify_p384<'c>( + &'c mut self, + key: KeyId, + message: &[u8], + signature: &[u8], + ) -> ClientResult<'c, reply::Verify, Self> { + self.verify( + Mechanism::P384, + key, + message, + signature, + SignatureSerialization::Raw, + ) + } + + fn agree_p384( + &mut self, + private_key: KeyId, + public_key: KeyId, + persistence: Location, + ) -> ClientResult<'_, reply::Agree, Self> { + self.agree( + Mechanism::P384, + private_key, + public_key, + StorageAttributes::new().set_persistence(persistence), + ) + } +} + +#[cfg(feature = "p521")] +impl P521 for ClientImplementation {} + +pub trait P521: CryptoClient { + fn generate_p521_private_key( + &mut self, + persistence: Location, + ) -> ClientResult<'_, reply::GenerateKey, Self> { + self.generate_key( + Mechanism::P521, + StorageAttributes::new().set_persistence(persistence), + ) + } + + fn derive_p521_public_key( + &mut self, + private_key: KeyId, + persistence: Location, + ) -> ClientResult<'_, reply::DeriveKey, Self> { + self.derive_key( + Mechanism::P521, + private_key, + None, + StorageAttributes::new().set_persistence(persistence), + ) + } + + fn deserialize_p521_key<'c>( + &'c mut self, + serialized_key: &[u8], + format: KeySerialization, + attributes: StorageAttributes, + ) -> ClientResult<'c, reply::DeserializeKey, Self> { + self.deserialize_key(Mechanism::P521, serialized_key, format, attributes) + } + + fn serialize_p521_key( + &mut self, + key: KeyId, + format: KeySerialization, + ) -> ClientResult<'_, reply::SerializeKey, Self> { + self.serialize_key(Mechanism::P521, key, format) + } + + // generally, don't offer multiple versions of a mechanism, if possible. + // try using the simplest when given the choice. + // hashing is something users can do themselves hopefully :) + // + // on the other hand: if users need sha256, then if the service runs in secure trustzone + // domain, we'll maybe need two copies of the sha2 code + fn sign_p521<'c>( + &'c mut self, + key: KeyId, + message: &[u8], + format: SignatureSerialization, + ) -> ClientResult<'c, reply::Sign, Self> { + self.sign(Mechanism::P521, key, message, format) + } + + fn verify_p521<'c>( + &'c mut self, + key: KeyId, + message: &[u8], + signature: &[u8], + ) -> ClientResult<'c, reply::Verify, Self> { + self.verify( + Mechanism::P521, + key, + message, + signature, + SignatureSerialization::Raw, + ) + } + + fn agree_p521( + &mut self, + private_key: KeyId, + public_key: KeyId, + persistence: Location, + ) -> ClientResult<'_, reply::Agree, Self> { + self.agree( + Mechanism::P521, + private_key, + public_key, + StorageAttributes::new().set_persistence(persistence), + ) + } +} + #[cfg(feature = "sha256")] impl Sha256 for ClientImplementation {} diff --git a/src/key.rs b/src/key.rs index 96c6baa1446..b1c9d05379b 100644 --- a/src/key.rs +++ b/src/key.rs @@ -70,6 +70,11 @@ pub enum Kind { Rsa4096, Ed255, P256, + P384, + P521, + BrainpoolP256R1, + BrainpoolP384R1, + BrainpoolP512R1, X255, } @@ -211,6 +216,11 @@ impl Kind { Kind::Rsa2048 => 7, Kind::Rsa3072 => 8, Kind::Rsa4096 => 9, + Kind::P384 => 10, + Kind::P521 => 11, + Kind::BrainpoolP256R1 => 12, + Kind::BrainpoolP384R1 => 13, + Kind::BrainpoolP512R1 => 14, } } @@ -225,6 +235,11 @@ impl Kind { 7 => Kind::Rsa2048, 8 => Kind::Rsa3072, 9 => Kind::Rsa4096, + 10 => Kind::P384, + 11 => Kind::P521, + 12 => Kind::BrainpoolP256R1, + 13 => Kind::BrainpoolP384R1, + 14 => Kind::BrainpoolP512R1, _ => return Err(Error::InvalidSerializedKey), }) } diff --git a/src/mechanisms.rs b/src/mechanisms.rs index 67089a965e3..380ce65c40d 100644 --- a/src/mechanisms.rs +++ b/src/mechanisms.rs @@ -49,6 +49,14 @@ pub struct P256 {} pub struct P256Prehashed {} mod p256; +pub struct P384 {} +pub struct P384Prehashed {} +mod p384; + +pub struct P521 {} +pub struct P521Prehashed {} +mod p521; + pub struct Sha256 {} mod sha256; diff --git a/src/mechanisms/p384.rs b/src/mechanisms/p384.rs new file mode 100644 index 00000000000..4bf9705ea07 --- /dev/null +++ b/src/mechanisms/p384.rs @@ -0,0 +1,357 @@ +#[cfg(feature = "p384")] +mod impls { + use p384::{ + ecdh::diffie_hellman, + ecdsa::{ + signature::{hazmat::RandomizedPrehashSigner, RandomizedSigner, Verifier}, + SigningKey, VerifyingKey, + }, + elliptic_curve::sec1::ToEncodedPoint, + SecretKey, + }; + + use super::super::{P384Prehashed, P384}; + use crate::{ + api::{reply, request}, + key, + service::{ + Agree, DeriveKey, DeserializeKey, Exists, GenerateKey, SerializeKey, Sign, + UnsafeInjectKey, Verify, + }, + store::keystore::Keystore, + types::{KeyId, KeySerialization, SerializedKey, Signature, SignatureSerialization}, + Error, + }; + + const SCALAR_SIZE: usize = 48; + + #[inline(never)] + fn load_secret_key( + keystore: &mut impl Keystore, + key_id: &KeyId, + ) -> Result { + // info_now!("loading keypair"); + let secret_scalar: [u8; SCALAR_SIZE] = keystore + .load_key(key::Secrecy::Secret, Some(key::Kind::P384), key_id)? + .material + .as_slice() + .try_into() + .map_err(|_| Error::InternalError)?; + + let secret_key = p384::SecretKey::from_bytes((&secret_scalar).into()) + .map_err(|_| Error::InternalError)?; + Ok(secret_key) + } + + #[inline(never)] + fn load_public_key( + keystore: &mut impl Keystore, + key_id: &KeyId, + ) -> Result { + let compressed_public_key = keystore + .load_key(key::Secrecy::Public, Some(key::Kind::P384), key_id)? + .material; + + p384::PublicKey::from_sec1_bytes(&compressed_public_key).map_err(|_| Error::InternalError) + } + + fn to_sec1_bytes(public_key: &p384::PublicKey) -> heapless::Vec { + let encoded_point: p384::EncodedPoint = public_key.into(); + encoded_point.as_bytes().try_into().unwrap() + } + + impl Agree for P384 { + fn agree( + keystore: &mut impl Keystore, + request: &request::Agree, + ) -> Result { + let secret_key = load_secret_key(keystore, &request.private_key)?; + let public_key = load_public_key(keystore, &request.public_key)?; + let shared_secret: [u8; SCALAR_SIZE] = + (*diffie_hellman(secret_key.to_nonzero_scalar(), public_key.as_affine()) + .raw_secret_bytes()) + .into(); + let flags = if request.attributes.serializable { + key::Flags::SERIALIZABLE + } else { + key::Flags::empty() + }; + let info = key::Info { + kind: key::Kind::Shared(shared_secret.len()), + flags, + }; + + let key_id = keystore.store_key( + request.attributes.persistence, + key::Secrecy::Secret, + info, + &shared_secret, + )?; + + // return handle + Ok(reply::Agree { + shared_secret: key_id, + }) + } + } + impl DeriveKey for P384 { + #[inline(never)] + fn derive_key( + keystore: &mut impl Keystore, + request: &request::DeriveKey, + ) -> Result { + let base_id = request.base_key; + + let secret_key = load_secret_key(keystore, &base_id)?; + let public_key = secret_key.public_key(); + + let public_id = keystore.store_key( + request.attributes.persistence, + key::Secrecy::Public, + key::Kind::P384, + &to_sec1_bytes(&public_key), + )?; + + Ok(reply::DeriveKey { key: public_id }) + } + } + impl DeserializeKey for P384 { + #[inline(never)] + fn deserialize_key( + keystore: &mut impl Keystore, + request: &request::DeserializeKey, + ) -> Result { + // - mechanism: Mechanism + // - serialized_key: Message + // - attributes: StorageAttributes + + let public_key = match request.format { + KeySerialization::Raw => { + if request.serialized_key.len() != 2 * SCALAR_SIZE { + return Err(Error::InvalidSerializedKey); + } + + let mut serialized_key = [4; 2 * SCALAR_SIZE + 1]; + serialized_key[1..].copy_from_slice(&request.serialized_key[..2 * SCALAR_SIZE]); + + p384::PublicKey::from_sec1_bytes(&serialized_key) + .map_err(|_| Error::InvalidSerializedKey)? + } + + _ => { + return Err(Error::InternalError); + } + }; + + let public_id = keystore.store_key( + request.attributes.persistence, + key::Secrecy::Public, + key::Kind::P384, + &to_sec1_bytes(&public_key), + )?; + + Ok(reply::DeserializeKey { key: public_id }) + } + } + impl SerializeKey for P384 { + #[inline(never)] + fn serialize_key( + keystore: &mut impl Keystore, + request: &request::SerializeKey, + ) -> Result { + let key_id = request.key; + + let public_key = load_public_key(keystore, &key_id)?; + + let serialized_key = match request.format { + KeySerialization::Raw => { + let mut serialized_key = SerializedKey::new(); + let affine_point = public_key.as_affine().to_encoded_point(false); + serialized_key + .extend_from_slice(affine_point.x().ok_or(Error::InternalError)?) + .map_err(|_| Error::InternalError)?; + serialized_key + .extend_from_slice(affine_point.y().ok_or(Error::InternalError)?) + .map_err(|_| Error::InternalError)?; + serialized_key + } + KeySerialization::Sec1 => { + let mut serialized_key = SerializedKey::new(); + serialized_key + .extend_from_slice(&to_sec1_bytes(&public_key)) + .map_err(|_| Error::InternalError)?; + serialized_key + } + _ => return Err(Error::InvalidSerializationFormat), + }; + + Ok(reply::SerializeKey { serialized_key }) + } + } + impl Exists for P384 { + #[inline(never)] + fn exists( + keystore: &mut impl Keystore, + request: &request::Exists, + ) -> Result { + let key_id = request.key; + let exists = keystore.exists_key(key::Secrecy::Secret, Some(key::Kind::P384), &key_id); + Ok(reply::Exists { exists }) + } + } + + impl Sign for P384 { + #[inline(never)] + fn sign( + keystore: &mut impl Keystore, + request: &request::Sign, + ) -> Result { + let key_id = request.key; + + let secret_key = load_secret_key(keystore, &key_id)?; + let signing_key = SigningKey::from(secret_key); + let signature: p384::ecdsa::Signature = + signing_key.sign_with_rng(keystore.rng(), &request.message); + + // debug_now!("making signature"); + let serialized_signature = match request.format { + SignatureSerialization::Asn1Der => { + let der = signature.to_der(); + Signature::from_slice(der.as_bytes()).unwrap() + } + SignatureSerialization::Raw => { + Signature::from_slice(&signature.to_bytes()).unwrap() + } + }; + + // return signature + Ok(reply::Sign { + signature: serialized_signature, + }) + } + } + impl Sign for P384Prehashed { + #[inline(never)] + fn sign( + keystore: &mut impl Keystore, + request: &request::Sign, + ) -> Result { + let key_id = request.key; + + let secret_key = load_secret_key(keystore, &key_id)?; + let signing_key = SigningKey::from(secret_key); + let signature: p384::ecdsa::Signature = signing_key + .sign_prehash_with_rng(keystore.rng(), &request.message) + .map_err(|_| Error::InvalidSerializedRequest)?; + + // debug_now!("making signature"); + let serialized_signature = match request.format { + SignatureSerialization::Asn1Der => { + let der = signature.to_der(); + Signature::from_slice(der.as_bytes()).unwrap() + } + SignatureSerialization::Raw => { + Signature::from_slice(&signature.to_bytes()).unwrap() + } + }; + + // return signature + Ok(reply::Sign { + signature: serialized_signature, + }) + } + } + + impl UnsafeInjectKey for P384 { + fn unsafe_inject_key( + keystore: &mut impl Keystore, + request: &request::UnsafeInjectKey, + ) -> Result { + if request.format != KeySerialization::Raw { + return Err(Error::InvalidSerializationFormat); + } + + let sk = p384::SecretKey::from_bytes((&**request.raw_key).into()) + .map_err(|_| Error::InvalidSerializedKey)?; + + let info = key::Info { + flags: key::Flags::SENSITIVE, + kind: key::Kind::P384, + }; + + keystore + .store_key( + request.attributes.persistence, + key::Secrecy::Secret, + info, + &sk.to_bytes(), + ) + .map(|key| reply::UnsafeInjectKey { key }) + } + } + + impl Verify for P384 { + #[inline(never)] + fn verify( + keystore: &mut impl Keystore, + request: &request::Verify, + ) -> Result { + let key_id = request.key; + + let public_key = load_public_key(keystore, &key_id)?; + let verifying_key: VerifyingKey = public_key.into(); + + if let SignatureSerialization::Raw = request.format { + } else { + // well more TODO + return Err(Error::InvalidSerializationFormat); + } + + let signature_bytes = (&**request.signature).into(); + + let signature = p384::ecdsa::Signature::from_bytes(signature_bytes) + .map_err(|_| Error::InvalidSerializedRequest)?; + + let valid = verifying_key.verify(&request.message, &signature).is_ok(); + Ok(reply::Verify { valid }) + } + } + impl GenerateKey for P384 { + fn generate_key( + keystore: &mut impl Keystore, + request: &request::GenerateKey, + ) -> Result { + let private_key = SecretKey::random(keystore.rng()); + // store keys + let key_id = keystore.store_key( + request.attributes.persistence, + key::Secrecy::Secret, + key::Info::from(key::Kind::P384).with_local_flag(), + &private_key.to_bytes(), + )?; + + // return handle + Ok(reply::GenerateKey { key: key_id }) + } + } +} + +#[cfg(not(feature = "p384"))] +mod impls { + use super::super::{P384Prehashed, P384}; + use crate::service::{ + Agree, DeriveKey, DeserializeKey, Exists, GenerateKey, SerializeKey, Sign, UnsafeInjectKey, + Verify, + }; + + impl UnsafeInjectKey for P384 {} + impl Agree for P384 {} + impl Exists for P384 {} + impl DeriveKey for P384 {} + impl GenerateKey for P384 {} + impl DeserializeKey for P384 {} + impl SerializeKey for P384 {} + impl Sign for P384 {} + impl Sign for P384Prehashed {} + impl Verify for P384 {} +} diff --git a/src/mechanisms/p521.rs b/src/mechanisms/p521.rs new file mode 100644 index 00000000000..5605a33f0b2 --- /dev/null +++ b/src/mechanisms/p521.rs @@ -0,0 +1,361 @@ +#[cfg(feature = "p521")] +mod impls { + use p521::{ + ecdh::diffie_hellman, + ecdsa::{ + signature::{hazmat::RandomizedPrehashSigner, RandomizedSigner, Verifier}, + SigningKey, VerifyingKey, + }, + elliptic_curve::sec1::ToEncodedPoint, + SecretKey, + }; + + use super::super::{P521Prehashed, P521}; + use crate::{ + api::{reply, request}, + key, + service::{ + Agree, DeriveKey, DeserializeKey, Exists, GenerateKey, SerializeKey, Sign, + UnsafeInjectKey, Verify, + }, + store::keystore::Keystore, + types::{KeyId, KeySerialization, SerializedKey, Signature, SignatureSerialization}, + Error, + }; + + const SCALAR_SIZE: usize = 66; + + #[inline(never)] + fn load_secret_key( + keystore: &mut impl Keystore, + key_id: &KeyId, + ) -> Result { + // info_now!("loading keypair"); + let secret_scalar: [u8; SCALAR_SIZE] = keystore + .load_key(key::Secrecy::Secret, Some(key::Kind::P521), key_id)? + .material + .as_slice() + .try_into() + .map_err(|_| Error::InternalError)?; + + let secret_key = p521::SecretKey::from_bytes(secret_scalar.as_slice().into()) + .map_err(|_| Error::InternalError)?; + Ok(secret_key) + } + + #[inline(never)] + fn load_public_key( + keystore: &mut impl Keystore, + key_id: &KeyId, + ) -> Result { + let compressed_public_key = keystore + .load_key(key::Secrecy::Public, Some(key::Kind::P521), key_id)? + .material; + + p521::PublicKey::from_sec1_bytes(&compressed_public_key).map_err(|_| Error::InternalError) + } + + fn to_sec1_bytes(public_key: &p521::PublicKey) -> heapless::Vec { + let encoded_point: p521::EncodedPoint = public_key.into(); + encoded_point.as_bytes().try_into().unwrap() + } + + impl Agree for P521 { + fn agree( + keystore: &mut impl Keystore, + request: &request::Agree, + ) -> Result { + let secret_key = load_secret_key(keystore, &request.private_key)?; + let public_key = load_public_key(keystore, &request.public_key)?; + let shared_secret: [u8; SCALAR_SIZE] = + (*diffie_hellman(secret_key.to_nonzero_scalar(), public_key.as_affine()) + .raw_secret_bytes()) + .as_slice() + .try_into() + .expect("Older generic_array does not impl .into() for [T; 66]"); + let flags = if request.attributes.serializable { + key::Flags::SERIALIZABLE + } else { + key::Flags::empty() + }; + let info = key::Info { + kind: key::Kind::Shared(shared_secret.len()), + flags, + }; + + let key_id = keystore.store_key( + request.attributes.persistence, + key::Secrecy::Secret, + info, + &shared_secret, + )?; + + // return handle + Ok(reply::Agree { + shared_secret: key_id, + }) + } + } + impl DeriveKey for P521 { + #[inline(never)] + fn derive_key( + keystore: &mut impl Keystore, + request: &request::DeriveKey, + ) -> Result { + let base_id = request.base_key; + + let secret_key = load_secret_key(keystore, &base_id)?; + let public_key = secret_key.public_key(); + + let public_id = keystore.store_key( + request.attributes.persistence, + key::Secrecy::Public, + key::Kind::P521, + &to_sec1_bytes(&public_key), + )?; + + Ok(reply::DeriveKey { key: public_id }) + } + } + impl DeserializeKey for P521 { + #[inline(never)] + fn deserialize_key( + keystore: &mut impl Keystore, + request: &request::DeserializeKey, + ) -> Result { + // - mechanism: Mechanism + // - serialized_key: Message + // - attributes: StorageAttributes + + let public_key = match request.format { + KeySerialization::Raw => { + if request.serialized_key.len() != 2 * SCALAR_SIZE { + return Err(Error::InvalidSerializedKey); + } + + let mut serialized_key = [4; 2 * SCALAR_SIZE + 1]; + serialized_key[1..].copy_from_slice(&request.serialized_key[..2 * SCALAR_SIZE]); + + p521::PublicKey::from_sec1_bytes(&serialized_key) + .map_err(|_| Error::InvalidSerializedKey)? + } + + _ => { + return Err(Error::InternalError); + } + }; + + let public_id = keystore.store_key( + request.attributes.persistence, + key::Secrecy::Public, + key::Kind::P521, + &to_sec1_bytes(&public_key), + )?; + + Ok(reply::DeserializeKey { key: public_id }) + } + } + impl SerializeKey for P521 { + #[inline(never)] + fn serialize_key( + keystore: &mut impl Keystore, + request: &request::SerializeKey, + ) -> Result { + let key_id = request.key; + + let public_key = load_public_key(keystore, &key_id)?; + + let serialized_key = match request.format { + KeySerialization::Raw => { + let mut serialized_key = SerializedKey::new(); + let affine_point = public_key.as_affine().to_encoded_point(false); + serialized_key + .extend_from_slice(affine_point.x().ok_or(Error::InternalError)?) + .map_err(|_| Error::InternalError)?; + serialized_key + .extend_from_slice(affine_point.y().ok_or(Error::InternalError)?) + .map_err(|_| Error::InternalError)?; + serialized_key + } + KeySerialization::Sec1 => { + let mut serialized_key = SerializedKey::new(); + serialized_key + .extend_from_slice(&to_sec1_bytes(&public_key)) + .map_err(|_| Error::InternalError)?; + serialized_key + } + _ => return Err(Error::InvalidSerializationFormat), + }; + + Ok(reply::SerializeKey { serialized_key }) + } + } + impl Exists for P521 { + #[inline(never)] + fn exists( + keystore: &mut impl Keystore, + request: &request::Exists, + ) -> Result { + let key_id = request.key; + let exists = keystore.exists_key(key::Secrecy::Secret, Some(key::Kind::P521), &key_id); + Ok(reply::Exists { exists }) + } + } + + impl Sign for P521 { + #[inline(never)] + fn sign( + keystore: &mut impl Keystore, + request: &request::Sign, + ) -> Result { + let key_id = request.key; + + let secret_key = load_secret_key(keystore, &key_id)?; + // Why is this intermediate step necessary? + let signing_key = SigningKey::from(ecdsa::SigningKey::from(secret_key)); + let signature: p521::ecdsa::Signature = + signing_key.sign_with_rng(keystore.rng(), &request.message); + + // debug_now!("making signature"); + let serialized_signature = match request.format { + SignatureSerialization::Asn1Der => { + let der = signature.to_der(); + Signature::from_slice(der.as_bytes()).unwrap() + } + SignatureSerialization::Raw => { + Signature::from_slice(&signature.to_bytes()).unwrap() + } + }; + + // return signature + Ok(reply::Sign { + signature: serialized_signature, + }) + } + } + impl Sign for P521Prehashed { + #[inline(never)] + fn sign( + keystore: &mut impl Keystore, + request: &request::Sign, + ) -> Result { + let key_id = request.key; + + let secret_key = load_secret_key(keystore, &key_id)?; + let signing_key = SigningKey::from(ecdsa::SigningKey::from(secret_key)); + let signature: p521::ecdsa::Signature = signing_key + .sign_prehash_with_rng(keystore.rng(), &request.message) + .map_err(|_| Error::InvalidSerializedRequest)?; + + // debug_now!("making signature"); + let serialized_signature = match request.format { + SignatureSerialization::Asn1Der => { + let der = signature.to_der(); + Signature::from_slice(der.as_bytes()).unwrap() + } + SignatureSerialization::Raw => { + Signature::from_slice(&signature.to_bytes()).unwrap() + } + }; + + // return signature + Ok(reply::Sign { + signature: serialized_signature, + }) + } + } + + impl UnsafeInjectKey for P521 { + fn unsafe_inject_key( + keystore: &mut impl Keystore, + request: &request::UnsafeInjectKey, + ) -> Result { + if request.format != KeySerialization::Raw { + return Err(Error::InvalidSerializationFormat); + } + + let sk = p521::SecretKey::from_bytes((&**request.raw_key).into()) + .map_err(|_| Error::InvalidSerializedKey)?; + + let info = key::Info { + flags: key::Flags::SENSITIVE, + kind: key::Kind::P521, + }; + + keystore + .store_key( + request.attributes.persistence, + key::Secrecy::Secret, + info, + &sk.to_bytes(), + ) + .map(|key| reply::UnsafeInjectKey { key }) + } + } + + impl Verify for P521 { + #[inline(never)] + fn verify( + keystore: &mut impl Keystore, + request: &request::Verify, + ) -> Result { + let key_id = request.key; + + let public_key = load_public_key(keystore, &key_id)?; + let verifying_key = VerifyingKey::from(ecdsa::VerifyingKey::from(public_key)); + + if let SignatureSerialization::Raw = request.format { + } else { + // well more TODO + return Err(Error::InvalidSerializationFormat); + } + + let signature_bytes = (&**request.signature).into(); + + let signature = p521::ecdsa::Signature::from_bytes(signature_bytes) + .map_err(|_| Error::InvalidSerializedRequest)?; + + let valid = verifying_key.verify(&request.message, &signature).is_ok(); + Ok(reply::Verify { valid }) + } + } + + impl GenerateKey for P521 { + fn generate_key( + keystore: &mut impl Keystore, + request: &request::GenerateKey, + ) -> Result { + let private_key = SecretKey::random(keystore.rng()); + // store keys + let key_id = keystore.store_key( + request.attributes.persistence, + key::Secrecy::Secret, + key::Info::from(key::Kind::P521).with_local_flag(), + &private_key.to_bytes(), + )?; + + // return handle + Ok(reply::GenerateKey { key: key_id }) + } + } +} + +#[cfg(not(feature = "p521"))] +mod impls { + use super::super::{P521Prehashed, P521}; + use crate::service::{ + Agree, DeriveKey, DeserializeKey, Exists, GenerateKey, SerializeKey, Sign, UnsafeInjectKey, + Verify, + }; + + impl UnsafeInjectKey for P521 {} + impl Agree for P521 {} + impl Exists for P521 {} + impl DeriveKey for P521 {} + impl GenerateKey for P521 {} + impl DeserializeKey for P521 {} + impl SerializeKey for P521 {} + impl Sign for P521 {} + impl Sign for P521Prehashed {} + impl Verify for P521 {} +} diff --git a/src/service.rs b/src/service.rs index b5568de2bb8..57da1502f2d 100644 --- a/src/service.rs +++ b/src/service.rs @@ -171,6 +171,8 @@ impl ServiceResources

{ Request::DummyRequest => Ok(Reply::DummyReply), Request::Agree(request) => match request.mechanism { + Mechanism::P521 => mechanisms::P521::agree(&mut keystore(self, ctx)?, request), + Mechanism::P384 => mechanisms::P384::agree(&mut keystore(self, ctx)?, request), Mechanism::P256 => mechanisms::P256::agree(&mut keystore(self, ctx)?, request), Mechanism::X255 => mechanisms::X255::agree(&mut keystore(self, ctx)?, request), _ => Err(Error::MechanismNotAvailable), @@ -224,6 +226,8 @@ impl ServiceResources

{ Mechanism::Ed255 => { mechanisms::Ed255::derive_key(&mut keystore(self, ctx)?, request) } + Mechanism::P521 => mechanisms::P521::derive_key(&mut keystore(self, ctx)?, request), + Mechanism::P384 => mechanisms::P384::derive_key(&mut keystore(self, ctx)?, request), Mechanism::P256 => mechanisms::P256::derive_key(&mut keystore(self, ctx)?, request), Mechanism::Sha256 => { mechanisms::Sha256::derive_key(&mut keystore(self, ctx)?, request) @@ -237,6 +241,12 @@ impl ServiceResources

{ Mechanism::Ed255 => { mechanisms::Ed255::deserialize_key(&mut keystore(self, ctx)?, request) } + Mechanism::P521 => { + mechanisms::P521::deserialize_key(&mut keystore(self, ctx)?, request) + } + Mechanism::P384 => { + mechanisms::P384::deserialize_key(&mut keystore(self, ctx)?, request) + } Mechanism::P256 => { mechanisms::P256::deserialize_key(&mut keystore(self, ctx)?, request) } @@ -276,6 +286,8 @@ impl ServiceResources

{ Request::Exists(request) => match request.mechanism { Mechanism::Ed255 => mechanisms::Ed255::exists(&mut keystore(self, ctx)?, request), + Mechanism::P521 => mechanisms::P521::exists(&mut keystore(self, ctx)?, request), + Mechanism::P384 => mechanisms::P384::exists(&mut keystore(self, ctx)?, request), Mechanism::P256 => mechanisms::P256::exists(&mut keystore(self, ctx)?, request), Mechanism::Totp => mechanisms::Totp::exists(&mut keystore(self, ctx)?, request), Mechanism::X255 => mechanisms::X255::exists(&mut keystore(self, ctx)?, request), @@ -290,6 +302,12 @@ impl ServiceResources

{ Mechanism::Ed255 => { mechanisms::Ed255::generate_key(&mut keystore(self, ctx)?, request) } + Mechanism::P521 => { + mechanisms::P521::generate_key(&mut keystore(self, ctx)?, request) + } + Mechanism::P384 => { + mechanisms::P384::generate_key(&mut keystore(self, ctx)?, request) + } Mechanism::P256 => { mechanisms::P256::generate_key(&mut keystore(self, ctx)?, request) } @@ -321,6 +339,12 @@ impl ServiceResources

{ // deprecated Request::UnsafeInjectKey(request) => match request.mechanism { + Mechanism::P521 => { + mechanisms::P521::unsafe_inject_key(&mut keystore(self, ctx)?, request) + } + Mechanism::P384 => { + mechanisms::P384::unsafe_inject_key(&mut keystore(self, ctx)?, request) + } Mechanism::P256 => { mechanisms::P256::unsafe_inject_key(&mut keystore(self, ctx)?, request) } @@ -535,6 +559,12 @@ impl ServiceResources

{ Mechanism::Ed255 => { mechanisms::Ed255::serialize_key(&mut keystore(self, ctx)?, request) } + Mechanism::P521 => { + mechanisms::P521::serialize_key(&mut keystore(self, ctx)?, request) + } + Mechanism::P384 => { + mechanisms::P384::serialize_key(&mut keystore(self, ctx)?, request) + } Mechanism::P256 => { mechanisms::P256::serialize_key(&mut keystore(self, ctx)?, request) } @@ -562,6 +592,14 @@ impl ServiceResources

{ Mechanism::HmacSha512 => { mechanisms::HmacSha512::sign(&mut keystore(self, ctx)?, request) } + Mechanism::P521 => mechanisms::P521::sign(&mut keystore(self, ctx)?, request), + Mechanism::P521Prehashed => { + mechanisms::P521Prehashed::sign(&mut keystore(self, ctx)?, request) + } + Mechanism::P384 => mechanisms::P384::sign(&mut keystore(self, ctx)?, request), + Mechanism::P384Prehashed => { + mechanisms::P384Prehashed::sign(&mut keystore(self, ctx)?, request) + } Mechanism::P256 => mechanisms::P256::sign(&mut keystore(self, ctx)?, request), Mechanism::P256Prehashed => { mechanisms::P256Prehashed::sign(&mut keystore(self, ctx)?, request) @@ -586,6 +624,8 @@ impl ServiceResources

{ Request::Verify(request) => match request.mechanism { Mechanism::Ed255 => mechanisms::Ed255::verify(&mut keystore(self, ctx)?, request), + Mechanism::P521 => mechanisms::P521::verify(&mut keystore(self, ctx)?, request), + Mechanism::P384 => mechanisms::P384::verify(&mut keystore(self, ctx)?, request), Mechanism::P256 => mechanisms::P256::verify(&mut keystore(self, ctx)?, request), _ => Err(Error::MechanismNotAvailable), } diff --git a/src/tests.rs b/src/tests.rs index 7cd932df3f9..fd2fbb641e5 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,4 +1,3 @@ -#![cfg(test)] #![allow(static_mut_refs)] use chacha20::ChaCha20; @@ -10,7 +9,7 @@ use littlefs2::fs::{Allocation, Filesystem}; use littlefs2::io::Result as LfsResult; use rand_core::{CryptoRng, RngCore}; -#[cfg(feature = "p256")] +#[cfg(any(feature = "p256", feature = "p384", feature = "p521",))] use crate::types::{Mechanism, SignatureSerialization, StorageAttributes}; use crate::client::{CryptoClient as _, FilesystemClient as _}; @@ -115,45 +114,29 @@ macro_rules! create_memory { unsafe { &mut VOLATILE_STORAGE }, ) }}; - // Create a "copy" - ($memory: expr) => {{ - let mem_2 = unsafe { - &*(&$memory - as *const ( - &'static mut littlefs2::fs::Allocation, - &'static mut InternalStorage, - &'static mut littlefs2::fs::Allocation, - &'static mut ExternalStorage, - &'static mut littlefs2::fs::Allocation, - &'static mut VolatileStorage, - )) - }; - let mem_2 = ( - (mem_2.0 as *const littlefs2::fs::Allocation) as u64, - (mem_2.1 as *const InternalStorage) as u64, - (mem_2.2 as *const littlefs2::fs::Allocation) as u64, - (mem_2.3 as *const ExternalStorage) as u64, - (mem_2.4 as *const littlefs2::fs::Allocation) as u64, - (mem_2.5 as *const VolatileStorage) as u64, - ); - let mem_2: ( - &'static mut littlefs2::fs::Allocation, - &'static mut InternalStorage, - &'static mut littlefs2::fs::Allocation, - &'static mut ExternalStorage, - &'static mut littlefs2::fs::Allocation, - &'static mut VolatileStorage, - ) = ( - unsafe { std::mem::transmute(mem_2.0) }, - unsafe { std::mem::transmute(mem_2.1) }, - unsafe { std::mem::transmute(mem_2.2) }, - unsafe { std::mem::transmute(mem_2.3) }, - unsafe { std::mem::transmute(mem_2.4) }, - unsafe { std::mem::transmute(mem_2.5) }, - ); +} - mem_2 - }}; +type Memory = ( + &'static mut littlefs2::fs::Allocation, + &'static mut InternalStorage, + &'static mut littlefs2::fs::Allocation, + &'static mut ExternalStorage, + &'static mut littlefs2::fs::Allocation, + &'static mut VolatileStorage, +); + +/// Create a "copy" of a store +unsafe fn copy_memory(memory: &Memory) -> Memory { + unsafe { + ( + &mut *(memory.0 as *const _ as *mut _), + &mut *(memory.1 as *const _ as *mut _), + &mut *(memory.2 as *const _ as *mut _), + &mut *(memory.3 as *const _ as *mut _), + &mut *(memory.4 as *const _ as *mut _), + &mut *(memory.5 as *const _ as *mut _), + ) + } } // TODO: what's going on here? Duplicates code in `tests/client/mod.rs`. @@ -387,6 +370,252 @@ fn agree_p256() { .signature; } +#[cfg(feature = "p384")] +#[test] +#[serial] +fn sign_p384() { + use crate::client::mechanisms::P384 as _; + // let mut client = setup!(); + setup!(client); + let private_key = block!(client + .generate_p384_private_key(Location::External) + .expect("no client error")) + .expect("no errors") + .key; + println!("got a public key {:?}", &private_key); + let public_key = block!(client + .derive_p384_public_key(private_key, Location::Volatile) + .expect("no client error")) + .expect("no errors") + .key; + println!("got a public key {:?}", &public_key); + + let message = [1u8, 2u8, 3u8]; + let signature = block!(client + .sign_p384(private_key, &message, SignatureSerialization::Raw) + .expect("no client error")) + .expect("good signature") + .signature; + + // use core::convert::AsMut; + // let sig = signature.0.as_mut()[0] = 0; + let future = client.verify_p384(public_key, &message, &signature); + let future = future.expect("no client error"); + let result = block!(future); + if result.is_err() { + println!("error: {:?}", result); + } + let reply = result.expect("valid signature"); + let valid = reply.valid; + assert!(valid); +} + +#[cfg(feature = "p384")] +#[test] +#[serial] +fn agree_p384() { + // let mut client = setup!(); + use crate::client::mechanisms::P384; + setup!(client); + let plat_private_key = block!(client + .generate_p384_private_key(Location::Volatile) + .expect("no client error")) + .expect("no errors") + .key; + println!("got a public key {:?}", &plat_private_key); + let plat_public_key = block!(client + .derive_p384_public_key(plat_private_key, Location::Volatile) + .expect("no client error")) + .expect("no errors") + .key; + println!("got a public key {:?}", &plat_public_key); + + let auth_private_key = block!(client + .generate_p384_private_key(Location::Volatile) + .expect("no client error")) + .expect("no errors") + .key; + println!("got a public key {:?}", &auth_private_key); + let auth_public_key = block!(client + .derive_p384_public_key(auth_private_key, Location::Volatile) + .expect("no client error")) + .expect("no errors") + .key; + println!("got a public key {:?}", &auth_public_key); + + let shared_secret = block!(client + .agree( + Mechanism::P384, + auth_private_key, + plat_public_key, + StorageAttributes::new().set_persistence(Location::Volatile) + ) + .expect("no client error")) + .expect("no errors") + .shared_secret; + + let alt_shared_secret = block!(client + .agree( + Mechanism::P384, + plat_private_key, + auth_public_key, + StorageAttributes::new().set_persistence(Location::Volatile) + ) + .expect("no client error")) + .expect("no errors") + .shared_secret; + + // NB: we have no idea about the value of keys, these are just *different* handles + assert_ne!(&shared_secret, &alt_shared_secret); + + let symmetric_key = block!(client + .derive_key( + Mechanism::Sha256, + shared_secret, + None, + StorageAttributes::new().set_persistence(Location::Volatile) + ) + .expect("no client error")) + .expect("no errors") + .key; + + let new_pin_enc = [1u8, 2, 3]; + + let _tag = block!(client + .sign( + Mechanism::HmacSha256, + symmetric_key, + &new_pin_enc, + SignatureSerialization::Raw + ) + .expect("no client error")) + .expect("no errors") + .signature; +} + +#[cfg(feature = "p521")] +#[test] +#[serial] +fn sign_p521() { + use crate::client::mechanisms::P521 as _; + // let mut client = setup!(); + setup!(client); + let private_key = block!(client + .generate_p521_private_key(Location::External) + .expect("no client error")) + .expect("no errors") + .key; + println!("got a public key {:?}", &private_key); + let public_key = block!(client + .derive_p521_public_key(private_key, Location::Volatile) + .expect("no client error")) + .expect("no errors") + .key; + println!("got a public key {:?}", &public_key); + + let message = [1u8, 2u8, 3u8]; + let signature = block!(client + .sign_p521(private_key, &message, SignatureSerialization::Raw) + .expect("no client error")) + .expect("good signature") + .signature; + + // use core::convert::AsMut; + // let sig = signature.0.as_mut()[0] = 0; + let future = client.verify_p521(public_key, &message, &signature); + let future = future.expect("no client error"); + let result = block!(future); + if result.is_err() { + println!("error: {:?}", result); + } + let reply = result.expect("valid signature"); + let valid = reply.valid; + assert!(valid); +} + +#[cfg(feature = "p521")] +#[test] +#[serial] +fn agree_p521() { + // let mut client = setup!(); + use crate::client::mechanisms::P521; + setup!(client); + let plat_private_key = block!(client + .generate_p521_private_key(Location::Volatile) + .expect("no client error")) + .expect("no errors") + .key; + println!("got a public key {:?}", &plat_private_key); + let plat_public_key = block!(client + .derive_p521_public_key(plat_private_key, Location::Volatile) + .expect("no client error")) + .expect("no errors") + .key; + println!("got a public key {:?}", &plat_public_key); + + let auth_private_key = block!(client + .generate_p521_private_key(Location::Volatile) + .expect("no client error")) + .expect("no errors") + .key; + println!("got a public key {:?}", &auth_private_key); + let auth_public_key = block!(client + .derive_p521_public_key(auth_private_key, Location::Volatile) + .expect("no client error")) + .expect("no errors") + .key; + println!("got a public key {:?}", &auth_public_key); + + let shared_secret = block!(client + .agree( + Mechanism::P521, + auth_private_key, + plat_public_key, + StorageAttributes::new().set_persistence(Location::Volatile) + ) + .expect("no client error")) + .expect("no errors") + .shared_secret; + + let alt_shared_secret = block!(client + .agree( + Mechanism::P521, + plat_private_key, + auth_public_key, + StorageAttributes::new().set_persistence(Location::Volatile) + ) + .expect("no client error")) + .expect("no errors") + .shared_secret; + + // NB: we have no idea about the value of keys, these are just *different* handles + assert_ne!(&shared_secret, &alt_shared_secret); + + let symmetric_key = block!(client + .derive_key( + Mechanism::Sha256, + shared_secret, + None, + StorageAttributes::new().set_persistence(Location::Volatile) + ) + .expect("no client error")) + .expect("no errors") + .key; + + let new_pin_enc = [1u8, 2, 3]; + + let _tag = block!(client + .sign( + Mechanism::HmacSha256, + symmetric_key, + &new_pin_enc, + SignatureSerialization::Raw + ) + .expect("no client error")) + .expect("no errors") + .signature; +} + #[cfg(feature = "chacha8-poly1305")] #[test] #[serial] @@ -605,7 +834,7 @@ fn rng() { } let mem = create_memory!(); - let mem_copy = create_memory!(mem); + let mem_copy = unsafe { copy_memory(&mem) }; // Trussed saves the RNG state so it cannot produce the same RNG on different boots. setup!( diff --git a/src/types.rs b/src/types.rs index 1f4e60024a1..f533c9fe743 100644 --- a/src/types.rs +++ b/src/types.rs @@ -601,6 +601,16 @@ pub enum Mechanism { // P256XSha256, P256, P256Prehashed, + P384, + P384Prehashed, + P521, + P521Prehashed, + BrainpoolP256R1, + BrainpoolP256R1Prehashed, + BrainpoolP384R1, + BrainpoolP384R1Prehashed, + BrainpoolP512R1, + BrainpoolP512R1Prehashed, // clients can also do hashing by themselves Sha256, Tdes,