diff --git a/Cargo.lock b/Cargo.lock index 98e70a7e..37ed7654 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -951,7 +951,6 @@ dependencies = [ "hmac", "k256", "log", - "num-bigint-dig", "num-traits", "once_cell", "p256", diff --git a/Cargo.toml b/Cargo.toml index d403a92b..12822fac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,6 @@ tiny_http = { version = "0.12", optional = true } [dev-dependencies] ed25519-dalek = "2" -num-bigint = { version = "0.8.2", features = ["i128", "prime", "zeroize"], default-features = false, package = "num-bigint-dig" } once_cell = "1" p256 = { version = "0.13", features = ["ecdsa"] } diff --git a/src/asymmetric/commands/put_key.rs b/src/asymmetric/commands/put_key.rs index 9b7bde3a..1a93faab 100644 --- a/src/asymmetric/commands/put_key.rs +++ b/src/asymmetric/commands/put_key.rs @@ -7,16 +7,22 @@ use crate::{ object, response::Response, }; -use serde::{Deserialize, Serialize}; +use serde::{de, Deserialize, Serialize}; +use std::{fmt, marker::PhantomData}; /// Request parameters for `command::put_asymmetric_key` -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Debug)] pub(crate) struct PutAsymmetricKeyCommand { /// Common parameters to all put object commands pub params: object::put::Params, /// Serialized object pub data: Vec, + + /// Serialized second parameter + /// This is only used for RSA. + #[serde(skip_serializing_if = "Vec::is_empty")] + pub extra_data: Vec, } impl Command for PutAsymmetricKeyCommand { @@ -33,3 +39,80 @@ pub(crate) struct PutAsymmetricKeyResponse { impl Response for PutAsymmetricKeyResponse { const COMMAND_CODE: command::Code = command::Code::PutAsymmetricKey; } + +impl<'de> Deserialize<'de> for PutAsymmetricKeyCommand { + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + #[doc(hidden)] + struct Visitor<'de> { + marker: PhantomData, + lifetime: PhantomData<&'de ()>, + } + impl<'de> de::Visitor<'de> for Visitor<'de> { + type Value = PutAsymmetricKeyCommand; + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(formatter, "struct PutAsymmetricKeyCommand") + } + + #[inline] + fn visit_seq(self, mut seq: A) -> Result + where + A: de::SeqAccess<'de>, + { + let params = match de::SeqAccess::next_element::(&mut seq)? { + Some(v) => v, + None => { + return Err(de::Error::invalid_length( + 0usize, + &"struct PutAsymmetricKeyCommand with 3 elements", + )); + } + }; + + let data = match de::SeqAccess::next_element::>(&mut seq)? { + Some(v) => v, + None => { + return Err(de::Error::invalid_length( + 1usize, + &"struct PutAsymmetricKeyCommand with 3 elements", + )); + } + }; + + let extra_data = if params.algorithm.rsa().is_some() { + match de::SeqAccess::next_element::>(&mut seq)? { + Some(v) => v, + None => { + return Err(de::Error::invalid_length( + 2usize, + &"struct PutAsymmetricKeyCommand with 3 elements", + )); + } + } + } else { + Vec::new() + }; + + Ok(PutAsymmetricKeyCommand { + params, + data, + extra_data, + }) + } + } + + #[doc(hidden)] + const FIELDS: &'static [&'static str] = &["params", "data", "extra_data"]; + de::Deserializer::deserialize_struct( + deserializer, + "PutAsymmetricKeyCommand", + FIELDS, + Visitor { + marker: PhantomData::, + lifetime: PhantomData, + }, + ) + } +} diff --git a/src/client.rs b/src/client.rs index 0dae6bca..55aa5124 100644 --- a/src/client.rs +++ b/src/client.rs @@ -555,7 +555,8 @@ impl Client { where K: Into>, { - let data = key_bytes.into(); + let mut data = key_bytes.into(); + let mut extra_data = Vec::new(); if data.len() != algorithm.key_len() { fail!( @@ -567,6 +568,10 @@ impl Client { ); } + if algorithm.is_rsa() { + extra_data = data.split_off(algorithm.key_len() / 2); + } + Ok(self .send_command(PutAsymmetricKeyCommand { params: object::put::Params { @@ -577,6 +582,7 @@ impl Client { algorithm: algorithm.into(), }, data, + extra_data, })? .key_id) } diff --git a/src/mockhsm/command.rs b/src/mockhsm/command.rs index 048b4eec..59063129 100644 --- a/src/mockhsm/command.rs +++ b/src/mockhsm/command.rs @@ -484,9 +484,18 @@ fn list_objects(state: &State, cmd_data: &[u8]) -> response::Message { /// Put an existing asymmetric key into the HSM fn put_asymmetric_key(state: &mut State, cmd_data: &[u8]) -> response::Message { - let PutAsymmetricKeyCommand { params, data } = deserialize(cmd_data) + let PutAsymmetricKeyCommand { + params, + data, + extra_data, + } = deserialize(cmd_data) .unwrap_or_else(|e| panic!("error parsing Code::PutAsymmetricKey: {e:?}")); + let mut buf = data.clone(); + if !extra_data.is_empty() { + buf.extend_from_slice(&extra_data); + } + state.objects.put( params.id, object::Type::AsymmetricKey, diff --git a/src/mockhsm/object/payload.rs b/src/mockhsm/object/payload.rs index 2eb27050..721eb943 100644 --- a/src/mockhsm/object/payload.rs +++ b/src/mockhsm/object/payload.rs @@ -4,8 +4,9 @@ use crate::{algorithm::Algorithm, asymmetric, authentication, hmac, opaque, wrap}; use ecdsa::elliptic_curve::sec1::ToEncodedPoint; use ed25519_dalek as ed25519; +use num_traits::cast::FromPrimitive; use rand_core::{OsRng, RngCore}; -use rsa::traits::PublicKeyParts; +use rsa::{traits::PublicKeyParts, BigUint}; /// Loaded instances of a cryptographic primitives in the MockHsm #[derive(Debug)] @@ -53,6 +54,17 @@ impl Payload { assert_eq!(data.len(), ed25519::SECRET_KEY_LENGTH); Payload::Ed25519Key(ed25519::SigningKey::try_from(data).unwrap()) } + asymmetric::Algorithm::Rsa2048 + | asymmetric::Algorithm::Rsa3072 + | asymmetric::Algorithm::Rsa4096 => { + assert_eq!(data.len(), asymmetric_alg.key_len()); + let exp = BigUint::from_u64(65537).expect("invalid static exponent"); + let p = BigUint::from_bytes_be(&data[..asymmetric_alg.key_len() / 2]); + let q = BigUint::from_bytes_be(&data[asymmetric_alg.key_len() / 2..]); + + let key = rsa::RsaPrivateKey::from_p_q(p, q, exp).unwrap(); + Payload::RsaKey(key) + } _ => { panic!("MockHsm doesn't support this asymmetric algorithm: {asymmetric_alg:?}") }