diff --git a/Cargo.toml b/Cargo.toml index 25befb1095b..51f12a1b92f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ des = { version = "0.7", optional = true } hmac = "0.11" sha-1 = { version = "0.9", default-features = false, optional = true } sha2 = { version = "0.9", default-features = false } +rsa = { version = "0.6.0", optional = true } # ours cosey = "0.3" @@ -90,6 +91,7 @@ default-mechanisms = [ "tdes", "totp", "trng", + "rsa2k" ] aes256-cbc = [] chacha8-poly1305 = [] @@ -104,6 +106,9 @@ sha256 = [] tdes = ["des"] totp = ["sha-1"] trng = ["sha-1"] +rsa2k = ["rsa"] +rsa3k = ["rsa"] +rsa4k = ["rsa"] clients-1 = [] clients-2 = [] diff --git a/src/client/mechanisms.rs b/src/client/mechanisms.rs index 630391286a1..cc9d835877a 100644 --- a/src/client/mechanisms.rs +++ b/src/client/mechanisms.rs @@ -245,6 +245,47 @@ pub trait P256: CryptoClient { } } +#[cfg(feature = "rsa2k")] +impl Rsa2kPkcs for ClientImplementation {} + +pub trait Rsa2kPkcs: CryptoClient { + fn generate_rsa2kpkcs_private_key(&mut self, persistence: Location) + -> ClientResult<'_, reply::GenerateKey, Self> + { + self.generate_key(Mechanism::Rsa2kPkcs, StorageAttributes::new().set_persistence(persistence)) + } + + fn derive_rsa2kpkcs_public_key(&mut self, shared_key: KeyId, persistence: Location) + -> ClientResult<'_, reply::DeriveKey, Self> + { + self.derive_key(Mechanism::Rsa2kPkcs, shared_key, None, StorageAttributes::new().set_persistence(persistence)) + } + + fn serialize_rsa2kpkcs_key(&mut self, key: KeyId, format: KeySerialization) + -> ClientResult<'_, reply::SerializeKey, Self> + { + self.serialize_key(Mechanism::Rsa2kPkcs, key, format) + } + + fn deserialize_rsa2kpkcs_key<'c>(&'c mut self, serialized_key: &[u8], format: KeySerialization, attributes: StorageAttributes) + -> ClientResult<'c, reply::DeserializeKey, Self> + { + self.deserialize_key(Mechanism::Rsa2kPkcs, serialized_key, format, attributes) + } + + fn sign_rsa2kpkcs<'c>(&'c mut self, key: KeyId, message: &[u8]) + -> ClientResult<'c, reply::Sign, Self> + { + self.sign(Mechanism::Rsa2kPkcs, key, message, SignatureSerialization::Raw) + } + + fn verify_rsa2kpkcs<'c>(&'c mut self, key: KeyId, message: &[u8], signature: &[u8]) + -> ClientResult<'c, reply::Verify, Self> + { + self.verify(Mechanism::Rsa2kPkcs, key, message, signature, SignatureSerialization::Raw) + } +} + #[cfg(feature = "sha256")] impl Sha256 for ClientImplementation {} diff --git a/src/config.rs b/src/config.rs index 29e452aa654..e26bc5b79f4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -8,14 +8,13 @@ use littlefs2::consts; pub type MAX_APPLICATION_NAME_LENGTH = consts::U256; pub const MAX_LONG_DATA_LENGTH: usize = 1024; -pub const MAX_MESSAGE_LENGTH: usize = 1024; pub type MAX_OBJECT_HANDLES = consts::U16; pub type MAX_LABEL_LENGTH = consts::U256; pub const MAX_MEDIUM_DATA_LENGTH: usize = 256; pub type MAX_PATH_LENGTH = consts::U256; -pub const MAX_KEY_MATERIAL_LENGTH: usize = 128; +//pub const MAX_KEY_MATERIAL_LENGTH: usize = 128; // must be above + 4 -pub const MAX_SERIALIZED_KEY_LENGTH: usize = 132; +//pub const MAX_SERIALIZED_KEY_LENGTH: usize = 132; cfg_if::cfg_if! { if #[cfg(feature = "clients-12")] { pub type MAX_SERVICE_CLIENTS = consts::U12; @@ -44,8 +43,28 @@ cfg_if::cfg_if! { } } pub const MAX_SHORT_DATA_LENGTH: usize = 128; + +#[cfg(any(feature = "rsa2k", feature = "rsa3k", feature = "rsa4k"))] +pub const MAX_SIGNATURE_LENGTH: usize = 512; +#[cfg(any(feature = "rsa2k", feature = "rsa3k", feature = "rsa4k"))] +// TODO: We use PKCS#8 DER format, this value was found empirically for 2K keys. Need to generalize. +pub const MAX_KEY_MATERIAL_LENGTH: usize = 1217; +#[cfg(any(feature = "rsa2k", feature = "rsa3k", feature = "rsa4k"))] +// This is due to the fact that KEY_MATERIAL_LENGTH is bigger than MESSAGE_LENGTH for RSA. +pub const MAX_MESSAGE_LENGTH: usize = MAX_KEY_MATERIAL_LENGTH; + + +#[cfg(not(any(feature = "rsa2k", feature = "rsa3k", feature = "rsa4k")))] pub const MAX_SIGNATURE_LENGTH: usize = 72; +#[cfg(not(any(feature = "rsa2k", feature = "rsa3k", feature = "rsa4k")))] +pub const MAX_KEY_MATERIAL_LENGTH: usize = 128; +#[cfg(not(any(feature = "rsa2k", feature = "rsa3k", feature = "rsa4k")))] +pub const MAX_MESSAGE_LENGTH: usize = 1024; + +// must be MAX_KEY_MATERIAL_LENGTH + 4 +pub const MAX_SERIALIZED_KEY_LENGTH: usize = MAX_KEY_MATERIAL_LENGTH + 4; pub const MAX_USER_ATTRIBUTE_LENGTH: usize = 256; + pub const USER_ATTRIBUTE_NUMBER: u8 = 37; diff --git a/src/key.rs b/src/key.rs index ce74889773d..1d94adaf618 100644 --- a/src/key.rs +++ b/src/key.rs @@ -63,6 +63,7 @@ pub enum Kind { Ed255, P256, X255, + Rsa2k, } bitflags::bitflags! { @@ -136,6 +137,9 @@ impl Kind { Kind::Ed255 => 4, Kind::P256 => 5, Kind::X255 => 6, + Kind::Rsa2k => 0x7, + //Kind::Rsa3k => 0xE0, + //Kind::Rsa4k => 0xE1, } } @@ -147,6 +151,11 @@ impl Kind { 4 => Self::Ed255, 5 => Self::P256, 6 => Self::X255, + + 0x7 => Self::Rsa2k, + //0xE0 => Kind::Rsa3k, + //0xE1 => Kind::Rsa4k, + _ => return Err(Error::InvalidSerializedKey), }) } diff --git a/src/mechanisms.rs b/src/mechanisms.rs index f55392a4260..9f38d7352a1 100644 --- a/src/mechanisms.rs +++ b/src/mechanisms.rs @@ -46,6 +46,10 @@ pub struct P256 {} pub struct P256Prehashed {} mod p256; +pub struct Rsa2kPkcs {} +// Later on we'll add: "pub struct Rsa2kPss {}" and so on +mod rsa2k; + pub struct Sha256 {} mod sha256; diff --git a/src/mechanisms/rsa2k.rs b/src/mechanisms/rsa2k.rs new file mode 100644 index 00000000000..6f3e8838f87 --- /dev/null +++ b/src/mechanisms/rsa2k.rs @@ -0,0 +1,251 @@ +use rsa::{ + RsaPrivateKey, + RsaPublicKey, + PublicKey, + pkcs8::{EncodePrivateKey, DecodePrivateKey, EncodePublicKey} +}; + +use crate::api::*; +// use crate::config::*; +// use crate::debug; +use crate::error::Error; +use crate::service::*; +use crate::types::*; + +#[cfg(feature = "rsa2k")] +impl DeriveKey for super::Rsa2kPkcs +{ + #[inline(never)] + fn derive_key(keystore: &mut impl Keystore, request: &request::DeriveKey) + -> Result + { + // Retrieve private key + let base_key_id = &request.base_key; + + // std::println!("Loading key: {:?}", base_key_id); + + let priv_key_der = keystore.load_key(key::Secrecy::Secret, Some(key::Kind::Rsa2k), base_key_id) + .expect("Failed to load an RSA 2K private key with the given ID") + .material; + + // std::println!("Loaded key material: {}", delog::hex_str!(&priv_key_der)); + // std::println!("Key material length is {}", priv_key_der.len()); + + let priv_key = DecodePrivateKey::from_pkcs8_der(&priv_key_der) + .expect("Failed to deserialize an RSA 2K private key from PKCS#8 DER"); + + // Derive and store public key + let pub_key_der = RsaPublicKey::from(&priv_key) + .to_public_key_der() + .expect("Failed to derive an RSA 2K public key or to serialize it to PKCS#8 DER"); + + let pub_key_id = keystore.store_key( + request.attributes.persistence, + key::Secrecy::Public, key::Kind::Rsa2k, + pub_key_der.as_ref())?; + + // Send a reply + Ok(reply::DeriveKey { + key: pub_key_id, + }) + } +} + +#[cfg(feature = "rsa2k")] +impl DeserializeKey for super::Rsa2kPkcs +{ + #[inline(never)] + fn deserialize_key(keystore: &mut impl Keystore, request: &request::DeserializeKey) + -> Result + { + // - mechanism: Mechanism + // - serialized_key: Message + // - attributes: StorageAttributes + + if request.format != KeySerialization::Raw { + return Err(Error::InternalError); + } + + let private_key: RsaPrivateKey = DecodePrivateKey::from_pkcs8_der(&request.serialized_key) + .map_err(|_| Error::InvalidSerializedKey)?; + + // We store our keys in PKCS#8 DER format + let private_key_der = private_key.to_pkcs8_der() + .expect("Failed to serialize an RSA 2K private key to PKCS#8 DER"); + + let private_key_id = keystore.store_key( + request.attributes.persistence, + key::Secrecy::Secret, key::Kind::Rsa2k, + private_key_der.as_ref())?; + + Ok(reply::DeserializeKey { + key: private_key_id, + }) + } +} + +#[cfg(feature = "rsa2k")] +impl GenerateKey for super::Rsa2kPkcs +{ + #[inline(never)] + fn generate_key(keystore: &mut impl Keystore, request: &request::GenerateKey) + -> Result + { + // We want an RSA 2K key + let bits = 2048; + + let priv_key = RsaPrivateKey::new(keystore.rng(), bits) + .expect("Failed to generate an RSA 2K private key"); + + // std::println!("Stored key material before DER: {:#?}", priv_key); + + let priv_key_der = priv_key.to_pkcs8_der() + .expect("Failed to serialize an RSA 2K private key to PKCS#8 DER"); + + // std::println!("Stored key material after DER: {}", delog::hex_str!(&priv_key_der)); + // std::println!("Key material length is {}", priv_key_der.as_ref().len()); + // #[cfg(all(test, feature = "verbose-tests"))] + // std::println!("rsa2k-pkcs private key = {:?}", &private_key); + + // store the key + let priv_key_id = keystore.store_key( + request.attributes.persistence, + key::Secrecy::Secret, + key::Info::from(key::Kind::Rsa2k).with_local_flag(), + priv_key_der.as_ref())?; + + // return handle + Ok(reply::GenerateKey { + key: priv_key_id, + }) + } +} + +#[cfg(feature = "rsa2k")] +impl SerializeKey for super::Rsa2kPkcs +{ + #[inline(never)] + fn serialize_key(keystore: &mut impl Keystore, request: &request::SerializeKey) + -> Result + { + let key_id = request.key; + + // We rely on the fact that we store the keys in the PKCS#8 DER format already + let priv_key_der = keystore.load_key(key::Secrecy::Secret, Some(key::Kind::Rsa2k), &key_id) + .expect("Failed to load an RSA 2K private key with the given ID") + .material; + + let serialized_key = match request.format { + KeySerialization::Raw => { + let mut serialized_key = Message::new(); + serialized_key.extend_from_slice(&priv_key_der).map_err(|_| Error::InternalError)?; + serialized_key + } + + _ => { return Err(Error::InternalError); } + }; + + Ok(reply::SerializeKey { serialized_key }) + } +} + +#[cfg(feature = "rsa2k")] +impl Exists for super::Rsa2kPkcs +{ + #[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::Rsa2k), &key_id); + Ok(reply::Exists { exists }) + } +} + +#[cfg(feature = "rsa2k")] +impl Sign for super::Rsa2kPkcs +{ + #[inline(never)] + fn sign(keystore: &mut impl Keystore, request: &request::Sign) + -> Result + { + // First, get the key + let key_id = request.key; + + // We rely on the fact that we store the keys in the PKCS#8 DER format already + let priv_key_der = keystore.load_key(key::Secrecy::Secret, Some(key::Kind::Rsa2k), &key_id) + .expect("Failed to load an RSA 2K private key with the given ID") + .material; + + let priv_key: RsaPrivateKey = DecodePrivateKey::from_pkcs8_der(&priv_key_der) + .expect("Failed to deserialize an RSA 2K private key from PKCS#8 DER"); + + // RSA lib takes in a hash value to sign, not raw data. + // We assume we get digest into this function, too. + + // TODO: Consider using .sign_blinded(), which is supposed to protect the private key from timing side channels + use rsa::padding::PaddingScheme; + use rsa::hash::Hash; + let native_signature = priv_key + .sign(PaddingScheme::new_pkcs1v15_sign(Some(Hash::SHA2_256)), &request.message) + .unwrap(); + let our_signature = Signature::from_slice(&native_signature).unwrap(); + + // std::println!("RSA2K-PKCS_v1.5 signature:"); + // std::println!("msg: {:?}", &request.message); + // std::println!("pk: {:?}", &priv_key); + // std::println!("sig: {:?}", &our_signature); + + // return signature + Ok(reply::Sign { signature: our_signature }) + } +} + +#[cfg(feature = "rsa2k")] +impl Verify for super::Rsa2kPkcs +{ + #[inline(never)] + fn verify(keystore: &mut impl Keystore, request: &request::Verify) + -> Result + { + if let SignatureSerialization::Raw = request.format { + } else { + return Err(Error::InvalidSerializationFormat); + } + + // TODO: This must not be a hardcoded magic number, convert when a common mechanism is available + if request.signature.len() != 256 { + return Err(Error::WrongSignatureLength); + } + + let key_id = request.key; + + let priv_key_der = keystore.load_key(key::Secrecy::Secret, Some(key::Kind::Rsa2k), &key_id) + .expect("Failed to load an RSA 2K private key with the given ID") + .material; + + let priv_key = DecodePrivateKey::from_pkcs8_der(&priv_key_der) + .expect("Failed to deserialize an RSA 2K private key from PKCS#8 DER"); + + // Get the public key + let pub_key = RsaPublicKey::from(&priv_key); + + use rsa::padding::PaddingScheme; + use rsa::hash::Hash; + let verification_ok = pub_key + .verify(PaddingScheme::new_pkcs1v15_sign(Some(Hash::SHA2_256)), &request.message, &request.signature) + .is_ok(); + + Ok(reply::Verify { valid: verification_ok }) + } +} + +#[cfg(not(feature = "rsa2k"))] +impl DeriveKey for super::Rsa2kPkcs {} +#[cfg(not(feature = "rsa2k"))] +impl GenerateKey for super::Rsa2kPkcs {} +#[cfg(not(feature = "rsa2k"))] +impl Sign for super::Rsa2kPkcs {} +#[cfg(not(feature = "rsa2k"))] +impl Verify for super::Rsa2kPkcs {} diff --git a/src/service.rs b/src/service.rs index 21a6d0c0721..80fd554078c 100644 --- a/src/service.rs +++ b/src/service.rs @@ -173,6 +173,7 @@ impl ServiceResources

{ Mechanism::P256 => mechanisms::P256::derive_key(keystore, request), Mechanism::Sha256 => mechanisms::Sha256::derive_key(keystore, request), Mechanism::X255 => mechanisms::X255::derive_key(keystore, request), + Mechanism::Rsa2kPkcs => mechanisms::Rsa2kPkcs::derive_key(keystore, request), _ => Err(Error::MechanismNotAvailable), }.map(Reply::DeriveKey) @@ -184,6 +185,7 @@ impl ServiceResources

{ Mechanism::Ed255 => mechanisms::Ed255::deserialize_key(keystore, request), Mechanism::P256 => mechanisms::P256::deserialize_key(keystore, request), Mechanism::X255 => mechanisms::X255::deserialize_key(keystore, request), + Mechanism::Rsa2kPkcs => mechanisms::Rsa2kPkcs::deserialize_key(keystore, request), _ => Err(Error::MechanismNotAvailable), }.map(Reply::DeserializeKey) @@ -217,6 +219,7 @@ impl ServiceResources

{ Mechanism::P256 => mechanisms::P256::exists(keystore, request), Mechanism::Totp => mechanisms::Totp::exists(keystore, request), Mechanism::X255 => mechanisms::X255::exists(keystore, request), + Mechanism::Rsa2kPkcs => mechanisms::Rsa2kPkcs::exists(keystore, request), _ => Err(Error::MechanismNotAvailable), }.map(Reply::Exists) @@ -228,6 +231,7 @@ impl ServiceResources

{ Mechanism::Ed255 => mechanisms::Ed255::generate_key(keystore, request), Mechanism::P256 => mechanisms::P256::generate_key(keystore, request), Mechanism::X255 => mechanisms::X255::generate_key(keystore, request), + Mechanism::Rsa2kPkcs => mechanisms::Rsa2kPkcs::generate_key(keystore, request), _ => Err(Error::MechanismNotAvailable), }.map(Reply::GenerateKey) }, @@ -431,6 +435,7 @@ impl ServiceResources

{ Mechanism::Ed255 => mechanisms::Ed255::serialize_key(keystore, request), Mechanism::P256 => mechanisms::P256::serialize_key(keystore, request), Mechanism::X255 => mechanisms::X255::serialize_key(keystore, request), + Mechanism::Rsa2kPkcs => mechanisms::Rsa2kPkcs::serialize_key(keystore, request), _ => Err(Error::MechanismNotAvailable), }.map(Reply::SerializeKey) @@ -447,6 +452,7 @@ impl ServiceResources

{ Mechanism::P256 => mechanisms::P256::sign(keystore, request), Mechanism::P256Prehashed => mechanisms::P256Prehashed::sign(keystore, request), Mechanism::Totp => mechanisms::Totp::sign(keystore, request), + Mechanism::Rsa2kPkcs => mechanisms::Rsa2kPkcs::sign(keystore, request), _ => Err(Error::MechanismNotAvailable), }.map(Reply::Sign) @@ -471,6 +477,7 @@ impl ServiceResources

{ Mechanism::Ed255 => mechanisms::Ed255::verify(keystore, request), Mechanism::P256 => mechanisms::P256::verify(keystore, request), + Mechanism::Rsa2kPkcs => mechanisms::Rsa2kPkcs::verify(keystore, request), _ => Err(Error::MechanismNotAvailable), }.map(Reply::Verify) diff --git a/src/store/keystore.rs b/src/store/keystore.rs index abec9a88423..f82113a4546 100644 --- a/src/store/keystore.rs +++ b/src/store/keystore.rs @@ -8,6 +8,7 @@ use crate::{ Platform, store::{self, Store as _}, types::{KeyId, Location}, + config::MAX_SERIALIZED_KEY_LENGTH, }; @@ -146,7 +147,10 @@ impl Keystore for ClientKeystore

{ let location = self.location(secrecy, id).ok_or(Error::NoSuchKey)?; + #[cfg(not(feature = "rsa2k"))] let bytes: Bytes<128> = store::read(self.store, location, &path)?; + #[cfg(feature = "rsa2k")] + let bytes: Bytes = store::read(self.store, location, &path)?; let key = key::Key::try_deserialize(&bytes)?; diff --git a/src/types.rs b/src/types.rs index 965a048d90e..ace1d31d354 100644 --- a/src/types.rs +++ b/src/types.rs @@ -508,6 +508,12 @@ pub enum Mechanism { Totp, Trng, X255, + Rsa2kPkcs, + Rsa2kPss, + Rsa3kPkcs, + Rsa3kPss, + Rsa4kPkcs, + Rsa4kPss, } pub type LongData = Bytes; diff --git a/tests/rsa2kpkcs.rs b/tests/rsa2kpkcs.rs new file mode 100644 index 00000000000..f4a621d556c --- /dev/null +++ b/tests/rsa2kpkcs.rs @@ -0,0 +1,88 @@ +use trussed::client::CryptoClient; +use trussed::client::mechanisms::Rsa2kPkcs; +use trussed::syscall; +use trussed::types::KeyId; + +mod client; + +use trussed::types::KeySerialization; +use trussed::types::Location::*; +use trussed::types::StorageAttributes; + +// Tests below can be run on a PC using the "virt" feature + +#[test] +fn rsa2kpkcs_generate_key() { + client::get(|client| { + let sk = syscall!(client.generate_rsa2kpkcs_private_key(Internal)).key; + + // This assumes we don't ever get a key with ID 0 + assert_ne!(sk, KeyId::from_special(0)); + }) +} + +#[test] +fn rsa2kpkcs_derive_key() { + client::get(|client| { + let sk = syscall!(client.generate_rsa2kpkcs_private_key(Internal)).key; + let pk = syscall!(client.derive_rsa2kpkcs_public_key(sk, Volatile)).key; + + // This assumes we don't ever get a key with ID 0 + assert_ne!(pk, KeyId::from_special(0)); + }) +} + +#[test] +fn rsa2kpkcs_exists_key() { + client::get(|client| { + let sk = syscall!(client.generate_rsa2kpkcs_private_key(Internal)).key; + let key_exists = syscall!(client.exists(trussed::types::Mechanism::Rsa2kPkcs, sk)).exists; + + assert!(key_exists); + }) +} + +#[test] +fn rsa2kpkcs_serialize_key() { + client::get(|client| { + let sk = syscall!(client.generate_rsa2kpkcs_private_key(Internal)).key; + + let serialized_key = syscall!(client.serialize_rsa2kpkcs_key(sk, KeySerialization::Raw)).serialized_key; + + assert!(!serialized_key.is_empty()); + }) +} + +#[test] +fn rsa2kpkcs_deserialize_key() { + client::get(|client| { + let sk = syscall!(client.generate_rsa2kpkcs_private_key(Internal)).key; + let serialized_key = syscall!(client.serialize_rsa2kpkcs_key(sk, KeySerialization::Raw)).serialized_key; + let location = StorageAttributes { persistence: Volatile }; + + let deserialized_key_id = syscall!(client.deserialize_rsa2kpkcs_key(&serialized_key, KeySerialization::Raw, location)).key; + + // This assumes we don't ever get a key with ID 0 + assert_ne!(deserialized_key_id, KeyId::from_special(0)); + }) +} + +#[test] +fn rsa2kpkcs_sign_verify() { + client::get(|client| { + let sk = syscall!(client.generate_rsa2kpkcs_private_key(Volatile)).key; + + let message = [1u8, 2u8, 3u8]; + use sha2::digest::Digest; + let digest_to_sign = sha2::Sha256::digest(&message); + let signature = syscall!(client.sign_rsa2kpkcs(sk, &digest_to_sign)).signature; + + // println!("Message: {:?}", &message); + // println!("Digest: {:?}", &digest_to_sign); + // println!("Signature (len={}): {:?}", signature.len(), &signature); + + let verify_ok = syscall!(client.verify_rsa2kpkcs(sk, &digest_to_sign, &signature)).valid; + + assert!(signature.len() == 256 && verify_ok); + }) +}