diff --git a/rust/pv/src/crypto.rs b/rust/pv/src/crypto.rs index 89ee827e..eb2a5e24 100644 --- a/rust/pv/src/crypto.rs +++ b/rust/pv/src/crypto.rs @@ -21,6 +21,7 @@ use std::convert::TryInto; /// An AES256-key that will purge itself out of the memory when going out of scope /// pub type Aes256Key = Secret<[u8; 32]>; +pub(crate) const AES_256_GCM_TAG_SIZE: usize = 16; /// Types of symmetric keys, to specify during construction. /// @@ -182,7 +183,7 @@ pub fn encrypt_aes(key: &SymKey, iv: &[u8], conf: &[u8]) -> Result> { /// /// This function will return an error if the data could not be encrypted by OpenSSL. pub fn encrypt_aes_gcm(key: &SymKey, iv: &[u8], aad: &[u8], conf: &[u8]) -> Result> { - let mut tag = vec![0xff; 16]; + let mut tag = vec![0xff; AES_256_GCM_TAG_SIZE]; let encr = match key { SymKey::Aes256(key) => encrypt_aead( Cipher::aes_256_gcm(), diff --git a/rust/pv/src/error.rs b/rust/pv/src/error.rs index fd6795f1..359c6bdd 100644 --- a/rust/pv/src/error.rs +++ b/rust/pv/src/error.rs @@ -38,6 +38,36 @@ pub enum Error { #[error("Verifying signatures is only supported for EC and RSA keys")] UnsupportedVerificationKey, + #[error("Provided binary request is too small")] + BinRequestSmall, + + #[error("No Config UID found: {0}")] + NoCuid(String), + + // errors from request types + #[error("Customer Communication Key must be 32 bytes long")] + CckSize, + + #[error("Invalid {0} user-data for signing provided. Max {} bytes allowed", .0.max())] + AsrcbInvSgnUserData(UserDataType), + + #[error("Unsupported user data signing key provided. Only EC(secp521r1) and RSA(2048 & 3072 bit) are supported")] + BinAsrcbUnsupportedUserDataSgnKey, + + #[error("No user-key for verification provided and user-data is signed")] + BinAsrcbNoUserDataSgnKey, + + #[error("Input does not contain an add-secret request version 1")] + BinAsrcbInvVersion, + + #[error("Provided user-data key type ({key}) does not match with the user-data ({kind})")] + AsrcbUserDataKeyMismatch { key: String, kind: UserDataType }, + + #[error( + "The user-defined request signature could not be verified with the provided certificate" + )] + AsrcbUserDataSgnFail, + // errors from other crates #[error(transparent)] PvCore(#[from] pv_core::Error), @@ -96,3 +126,5 @@ macro_rules! bail_hkd_verify { }; } pub(crate) use bail_hkd_verify; + +use crate::request::uvsecret::UserDataType; diff --git a/rust/pv/src/lib.rs b/rust/pv/src/lib.rs index 9e97ff88..ccf40f64 100644 --- a/rust/pv/src/lib.rs +++ b/rust/pv/src/lib.rs @@ -65,7 +65,7 @@ pub mod request { pub use crate::crypto::{hash, hkdf_rfc_5869}; pub use crate::crypto::{sign_msg, verify_signature}; pub use crate::crypto::{Aes256Key, SymKey, SymKeyType}; - pub use crate::req::{Aad, Encrypt, Keyslot, ReqEncrCtx, Request}; + pub use crate::req::{Aad, BinReqValues, Encrypt, Keyslot, ReqEncrCtx, Request}; pub use crate::secret::{Secret, Zeroize}; pub use crate::verify::{CertVerifier, HkdVerifier, NoVerifyHkd}; @@ -83,6 +83,7 @@ pub mod request { asrcb::{AddSecretFlags, AddSecretRequest, AddSecretVersion}, ext_secret::ExtSecret, guest_secret::GuestSecret, + user_data::verify_asrcb_and_get_user_data, }; pub use pv_core::request::uvsecret::AddSecretMagic; pub use pv_core::request::uvsecret::UserDataType; diff --git a/rust/pv/src/req.rs b/rust/pv/src/req.rs index 0dc952a4..f88aeb93 100644 --- a/rust/pv/src/req.rs +++ b/rust/pv/src/req.rs @@ -2,6 +2,7 @@ // // Copyright IBM Corp. 2023 +use crate::crypto::AES_256_GCM_TAG_SIZE; use crate::misc::to_u32; use crate::request::{ derive_key, encrypt_aes, encrypt_aes_gcm, gen_ec_key, random_array, SymKey, SymKeyType, @@ -14,6 +15,8 @@ use openssl::hash::{hash, MessageDigest}; use openssl::pkey::{PKey, PKeyRef, Private, Public}; use pv_core::request::{RequestMagic, RequestVersion}; use std::convert::TryInto; +use std::mem::size_of; +use utils::assert_size; use zerocopy::{AsBytes, BigEndian, FromBytes, FromZeroes, U32}; /// Encrypt a _secret_ using self and a given private key. @@ -322,6 +325,7 @@ struct RequestHdr { reserved28: u32, sea: U32, } +assert_size!(RequestHdr, 48); impl RequestHdr { fn new(rqvn: u32, rql: u32, iv: [u8; 12], nks: u8, sea: u32, magic: Option<[u8; 8]>) -> Self { @@ -380,6 +384,67 @@ pub trait Request { fn add_hostkey(&mut self, hostkey: PKey); } +/// A struct to represent some parts of a binary/encrypted request. +#[derive(Debug)] +#[allow(unused)] +#[allow(clippy::len_without_is_empty)] +pub struct BinReqValues<'a> { + iv: &'a [u8], + aad: &'a [u8], + req_dep_aad: &'a [u8], + encr: &'a [u8], + tag: &'a [u8], + version: u32, + len: usize, +} +impl<'a> BinReqValues<'a> { + pub(crate) const TAG_LEN: usize = AES_256_GCM_TAG_SIZE; + + /// Get the locations from this request. + /// + /// Does minimal sanity test, just tests to prevent panics. + /// `req` may be larger than the actual request. + pub fn get(req: &'a [u8]) -> Result { + let hdr = RequestHdr::read_from_prefix(req).ok_or(Error::BinRequestSmall)?; + let rql = hdr.rql.get() as usize; + let sea = hdr.sea.get() as usize; + + if rql < req.len() || sea + Self::TAG_LEN > rql { + return Err(Error::BinRequestSmall); + } + let aad_size = rql - sea - Self::TAG_LEN; + if aad_size < size_of::() { + return Err(Error::BinRequestSmall); + } + + let iv = &req[0x10..0x1c]; + let aad = &req[..aad_size]; + let req_dep_aad = &req[size_of::()..aad_size]; + let encr = &req[aad_size..(aad_size + sea)]; + let tag = &req[rql - Self::TAG_LEN..]; + + Ok(Self { + iv, + aad, + req_dep_aad, + encr, + tag, + version: hdr.rqvn.get(), + len: rql, + }) + } + + /// Returns the version of this [`BinReqValues`]. + pub fn version(&self) -> u32 { + self.version + } + + /// Returns the length of this [`BinReqValues`]. + pub fn len(&self) -> usize { + self.len + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/rust/pv/src/uvsecret.rs b/rust/pv/src/uvsecret.rs index 0e9b3e44..343e4b05 100644 --- a/rust/pv/src/uvsecret.rs +++ b/rust/pv/src/uvsecret.rs @@ -10,3 +10,4 @@ pub mod asrcb; pub mod ext_secret; pub mod guest_secret; +pub mod user_data; diff --git a/rust/pv/src/uvsecret/asrcb.rs b/rust/pv/src/uvsecret/asrcb.rs index dd2481bd..006feac8 100644 --- a/rust/pv/src/uvsecret/asrcb.rs +++ b/rust/pv/src/uvsecret/asrcb.rs @@ -176,6 +176,10 @@ pub struct AddSecretRequest { } impl AddSecretRequest { + #[allow(unused)] + /// Offset of the user-data in the add-secret request in bytes + pub(super) const V1_USER_DATA_OFFS: usize = 0x218; + /// Create a new add-secret request. /// /// The request has no extension secret, no configuration UID, no host-keys, diff --git a/rust/pv/src/uvsecret/user_data.rs b/rust/pv/src/uvsecret/user_data.rs new file mode 100644 index 00000000..e44942b7 --- /dev/null +++ b/rust/pv/src/uvsecret/user_data.rs @@ -0,0 +1,612 @@ +#![allow(unused)] +use crate::{ + crypto::{sign_msg, verify_signature}, + req::BinReqValues, + request::{ + openssl::{ + pkey::{PKey, Private}, + MessageDigest, + }, + uvsecret::{AddSecretRequest, AddSecretVersion}, + RequestMagic, + }, + Error, Result, +}; +use openssl::{ + nid::Nid, + pkey::{HasParams, HasPublic, Id, PKeyRef, Public}, +}; +use pv_core::request::uvsecret::AddSecretMagic; +use pv_core::request::uvsecret::UserDataType; +use utils::assert_size; +use zerocopy::{AsBytes, BigEndian, FromBytes, FromZeroes, U16}; + +/// User data. +/// +/// User defined data can be: +/// - 512 bytes arbitrary data +/// - 256 bytes arbitrary data + EC(secp521r1) signature +/// ```none +/// LAYOUT +/// |------------------------| +/// | user-data (256) | +/// | ec signature (139) | +/// | reserved (5) | +/// | signature size (2) (BE)| +/// | reserved (110) | +/// |------------------------| +/// ``` +/// - 256 bytes arbitrary data + RSA2048 signature +/// ```none +/// LAYOUT +/// |---------------------| +/// | user-data (256) | +/// | rsa signature (256) | +/// |---------------------| +/// ``` +/// - 128 bytes arbitrary data + RSA3072 signature +/// ```none +/// LAYOUT +/// |---------------------| +/// | user-data (128) | +/// | rsa signature (384) | +/// |---------------------| +/// ``` +/// +/// Ensures that the data+signature fits into 512 bytes +/// must be created via functions! +#[derive(Debug, Clone)] +pub(super) enum UserData { + Null, + Unsigned(Vec), + Signed(SignedUserData), +} + +#[repr(C)] +#[derive(Debug, AsBytes, FromBytes, FromZeroes)] +struct EcUserData { + data: [u8; 256], + signature: [u8; EC_SIGN_MAX_SIZE], + res_18b: [u8; 5], + sgn_size: U16, + res_192: [u8; 110], +} +assert_size!(EcUserData, USER_DATA_SIZE); +const USER_DATA_SIZE: usize = 0x200; +const EC_SIGN_MAX_SIZE: usize = 139; + +impl EcUserData { + // Sets the signature to this data. + // + //# Panic + // Panics if `sgn` is longer than 139 bytes + fn set_signature(&mut self, sgn: &[u8]) { + debug_assert!(sgn.len() <= EC_SIGN_MAX_SIZE); + self.signature.fill(0); + self.signature[..sgn.len()].copy_from_slice(sgn); + + self.res_18b.fill(0); + self.sgn_size = (sgn.len() as u16).into(); + self.res_192.fill(0); + } +} + +#[derive(Debug, Clone)] +pub(super) struct SignedUserData { + sign_key: PKey, + data: Vec, +} + +impl UserData { + const USER_DATA_SIZE: usize = 0x200; + + fn user_data_type(sign_key: &PKeyRef

) -> Result { + fn check_curve(pkey: &PKeyRef

) -> Result { + let nid = pkey.ec_key()?.group().curve_name(); + match nid { + Some(nid) => Ok(nid == Nid::SECP521R1), + None => Ok(false), + } + } + match sign_key.id() { + Id::EC if check_curve(sign_key)? => Ok(UserDataType::SgnEcSECP521R1), + Id::RSA if sign_key.rsa()?.size() == 2048 / 8 => Ok(UserDataType::SgnRsa2048), + Id::RSA if sign_key.rsa()?.size() == 3072 / 8 => Ok(UserDataType::SgnRsa3072), + _ => Err(Error::BinAsrcbUnsupportedUserDataSgnKey), + } + } + + pub(super) fn magic(&self) -> RequestMagic { + let magic: AddSecretMagic = self.data_type().into(); + magic.get() + } + /// Creates new user data + /// + /// Verifies that the provided data + signature fits into 512 bytes + /// + /// #Error + /// An error is reported if the provided data and the signature would not fit into 512 bytes + /// An error is reported if the key is not of type RSA (2048|3072) or EC(specp521r1) + pub(super) fn new(sign_key: Option>, data: Vec) -> Result { + let sign_key = match sign_key { + None => { + return match data.len() > UserDataType::Unsigned.max() { + true => Err(Error::AsrcbInvSgnUserData(UserDataType::Unsigned)), + false => Ok(Self::Unsigned(data)), + }; + } + Some(skey) => skey, + }; + + let kind = Self::user_data_type(&sign_key)?; + + // does the data fit into the arbitrary buffer? + if data.len() > kind.max() { + return Err(Error::AsrcbInvSgnUserData(kind)); + } + + Ok(Self::Signed(SignedUserData { sign_key, data })) + } + + /// Signs data in buf, writes signature to buf+user_data_offset+sign_offset if applicable. + /// + /// Uses [`MessageDigest::sha512`] as digest. Does not modify the abritary user data buffer. + /// + /// * buf: user data buffer, must be at least 512 bytes long + /// + /// # Panic + /// panics if `buf` is smaller than 512 bytes + /// + /// # Errors + /// Returns an error if signature could not be calculated. + /// It is considered no error if no signature is required by user data type + pub(super) fn sign(&self, buf: &mut [u8], user_data_offset: usize) -> Result<()> { + // get signing info or return if no signature is required + let signed_data = match self { + UserData::Null | UserData::Unsigned(_) => return Ok(()), + UserData::Signed(s) => s, + }; + debug_assert!(buf.len() >= USER_DATA_SIZE); + + // clear the signature area + let sgn_offset = user_data_offset + self.data_type().max(); + buf[sgn_offset..user_data_offset + USER_DATA_SIZE].fill(0); + + // calculate signature + let sgn = sign_msg(&signed_data.sign_key, MessageDigest::sha512(), buf)?; + + // insert signature + if let UserDataType::SgnEcSECP521R1 = self.data_type() { + // Panic: will not panic buffer is 512+ bytes long + let buf_ec = EcUserData::mut_from_prefix(&mut buf[user_data_offset..]).unwrap(); + buf_ec.set_signature(&sgn); + } else { + // Panic: will not panic buffer is 512+ bytes long + buf[sgn_offset..sgn_offset + sgn.len()].copy_from_slice(&sgn); + } + Ok(()) + } + + fn data_type(&self) -> UserDataType { + match self { + Self::Null => UserDataType::Null, + Self::Unsigned(_) => UserDataType::Unsigned, + Self::Signed(data) => Self::user_data_type(&data.sign_key).unwrap(), + } + } + + /// returns a slice for the abitraty user data as first tuple part if User data is available + /// the second part contains a vector, created on the fly, which contains enough zeros to fill + /// the missing bytes to fill 512 bytes of space or None if the first slice already contains + /// 512 bytes + pub(super) fn data(&self) -> (Option<&[u8]>, Option>) { + let buf = match self { + UserData::Null => None, + UserData::Unsigned(d) => Some(d), + UserData::Signed(SignedUserData { data, .. }) => Some(data), + }; + + let remaining_size = Self::USER_DATA_SIZE - buf.map(|b| b.len()).unwrap_or(0); + let remaining = match remaining_size > 0 { + true => Some(vec![0; remaining_size]), + false => None, + }; + + (buf.map(|b| b.as_ref()), remaining) + } +} + +fn format_vrfy_key(key: &PKeyRef) -> String { + let id = key.id(); + match key.rsa() { + Ok(key) => format!("RSA {}", key.size() * 8), + Err(_) if id == Id::EC => "EC".to_string(), + Err(_) => "Unknown".to_string(), + } +} + +fn check_key_format(kind: UserDataType, key: &PKeyRef) -> Result<()> { + let other_kind = + UserData::user_data_type(key).map_err(|_| Error::AsrcbUserDataKeyMismatch { + key: format_vrfy_key(key), + kind, + })?; + if other_kind == kind { + Ok(()) + } else { + Err(Error::AsrcbUserDataKeyMismatch { + key: format_vrfy_key(key), + kind, + }) + } +} + +/// Verify the user data contained in the add-secret request. +/// +/// First checks that the provided data contains a sound add-secret request. +/// Then performs the inverse action that happened during the add-secret generation with user-data +/// signature: +/// - extract and replace the signature with zeros +/// - verify the signature of the request until, but not including the request tag +/// +/// # Returns +/// +/// Extracrted user-data if available +/// +/// #Errors +/// +/// returns an error if +/// - No sound add-secret request presented +/// - Sinned user-data indicated, but no key provided +/// - Another keytype provided than indicated in the request +/// - Signature could not be verified by the provided key +/// - any OpenSSL error that might happen during the verification process +pub fn verify_asrcb_and_get_user_data( + mut asrcb: Vec, + key: Option>, +) -> Result>> { + // check that the provided buffer contains an Add Secret request + let magic = AddSecretMagic::try_from_bytes(&asrcb)?; + let req = BinReqValues::get(&asrcb)?; + if req.version() != AddSecretVersion::One as u32 { + return Err(Error::BinAsrcbInvVersion); + } + + //preventing the two lines after the truncate from panicking + let req_len = req.len(); + if asrcb.len() < req_len + || req_len < AddSecretRequest::V1_USER_DATA_OFFS + UserData::USER_DATA_SIZE + { + return Err(pv_core::Error::NoAsrcb.into()); + } + // forget the tag (and all additional data that might be behind the tag) + asrcb.truncate(req_len - BinReqValues::TAG_LEN); + // get a mutable refrenence on the 512 bytes of user data + let (_, user_data) = asrcb.split_at_mut(AddSecretRequest::V1_USER_DATA_OFFS); + let user_data = &mut user_data[..UserData::USER_DATA_SIZE]; + + // depending on the user_data_type do: + // Null -> exit w/o user data + // Unsigned -> exit return all user data + // Signed -> + // - check that provided key matches user data keytype + // - extract user data& signature + let (key, user_data) = match (key, magic.kind()) { + (_, UserDataType::Null) => return Ok(None), + (None, UserDataType::Unsigned) => return Ok(Some(user_data.to_vec())), + (Some(key), UserDataType::Unsigned) => { + return Err(Error::AsrcbUserDataKeyMismatch { + key: format_vrfy_key(&key), + kind: UserDataType::Unsigned, + }) + } + (Some(key), _) => { + check_key_format(magic.kind(), &key)?; + (key, VerifiedUserData::new(user_data, magic.kind())) + } + (None, _) => return Err(Error::BinAsrcbNoUserDataSgnKey), + }; + + match verify_signature(&key, MessageDigest::sha512(), &asrcb, user_data.signature())? { + false => Err(Error::AsrcbUserDataSgnFail), + true => Ok(Some(user_data.into())), + } +} + +// Internal representation of the 512 bytes of user-data, signing-algorithm agnostic +struct VerifiedUserData { + data: Vec, + signature: Vec, +} + +impl VerifiedUserData { + /// Reads user-data from buf depending on the indicated user data type. + /// Overwrites the signature in the buf with zeros. + /// + /// #Panics + /// Panics it provided buffer is smaller that 512 bytes or kind is Null or Unsigned + fn new(buf: &mut [u8], kind: UserDataType) -> Self { + assert!(buf.len() >= 0x200); + + let (ret, sgn) = match kind { + UserDataType::SgnEcSECP521R1 => { + let EcUserData { + data, + signature, + sgn_size, + .. + } = EcUserData::mut_from_prefix(buf).unwrap(); + let data_len: usize = data.len(); + let data = data.to_vec(); + let mut signature = signature.to_vec(); + signature.truncate(sgn_size.get() as usize); + (Self { data, signature }, &mut buf[data_len..]) + } + UserDataType::SgnRsa2048 => ( + Self { + data: buf[..0x100].to_vec(), + signature: buf[0x100..].to_vec(), + }, + &mut buf[0x100..], + ), + UserDataType::SgnRsa3072 => ( + Self { + data: buf[..0x80].to_vec(), + signature: buf[0x80..].to_vec(), + }, + &mut buf[0x80..], + ), + UserDataType::Null => unreachable!(), + UserDataType::Unsigned => unreachable!(), + }; + + //overwrite signature field with zeros + sgn.fill(0); + ret + } + + fn signature(&self) -> &[u8] { + self.signature.as_ref() + } +} + +impl From for Vec { + fn from(value: VerifiedUserData) -> Self { + value.data + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{get_test_asset, test_utils::get_test_keys}; + + #[test] + fn sign_null() { + let mut buf = vec![17; 0x200]; + + let user_data = UserData::Null; + let (data, _) = user_data.data(); + assert!(data.is_none()); + + user_data.sign(&mut buf, 0).unwrap(); + + // sign should not touch the buffer + assert_eq!(buf, vec![17; 0x200]); + } + + #[test] + fn sign_unsigned() { + let user_data = UserData::Unsigned(vec![0x11; 0x200]); + let (data, _) = user_data.data(); + assert_eq!(data.unwrap(), &[0x11; 0x200]); + + let mut buf = vec![17; 0x200]; + user_data.sign(&mut buf, 0).unwrap(); + + // sign should not touch the buffer + assert_eq!(buf, vec![17; 0x200]); + } + + #[test] + fn sign_rsa2048() { + let rsa = get_test_asset!("keys/rsa2048key.pem"); + let rsa = PKey::private_key_from_pem(rsa).unwrap(); + + let mut buf = vec![0x17; 0x200]; + + let user_data = UserData::new(Some(rsa.clone()), vec![0x11; 0x100]).unwrap(); + let (data, _) = user_data.data(); + let data = data.unwrap(); + buf[..0x100].copy_from_slice(data); + + user_data.sign(&mut buf, 0).unwrap(); + + let vrf_user_data = VerifiedUserData::new(&mut buf, UserDataType::SgnRsa2048); + let res = verify_signature( + &rsa, + MessageDigest::sha512(), + &buf, + vrf_user_data.signature(), + ) + .unwrap(); + assert!(res); + } + + #[test] + fn sign_rsa3072() { + let rsa = get_test_asset!("keys/rsa3072key.pem"); + let rsa = PKey::private_key_from_pem(rsa).unwrap(); + + let mut buf = vec![0x17; 0x200]; + + let user_data = UserData::new(Some(rsa.clone()), vec![0x11; 0x80]).unwrap(); + let (data, _) = user_data.data(); + let data = data.unwrap(); + buf[..0x80].copy_from_slice(data); + + user_data.sign(&mut buf, 0).unwrap(); + + let vrf_user_data = VerifiedUserData::new(&mut buf, UserDataType::SgnRsa3072); + let res = verify_signature( + &rsa, + MessageDigest::sha512(), + &buf, + vrf_user_data.signature(), + ) + .unwrap(); + assert!(res); + } + + #[test] + fn sign_rsa4096_fail() { + let rsa = get_test_asset!("keys/rsa4096key.pem"); + let rsa = PKey::private_key_from_pem(rsa).unwrap(); + + let user_data = UserData::new(Some(rsa.clone()), vec![]); + assert!(matches!( + user_data.unwrap_err(), + Error::BinAsrcbUnsupportedUserDataSgnKey + )); + } + + #[test] + fn sign_ec() { + let (ec, _) = get_test_keys(); + + let mut buf = vec![0x11; 0x200]; + + let user_data = UserData::new(Some(ec.clone()), vec![0x11; 0x80]).unwrap(); + let (data, _) = user_data.data(); + let data = data.unwrap(); + buf[..0x80].copy_from_slice(data); + + user_data.sign(&mut buf, 0).unwrap(); + let buf_ec = EcUserData::mut_from(&mut buf).unwrap(); + let EcUserData { + data, + signature, + res_18b, + sgn_size, + res_192, + } = buf_ec; + assert_eq!(data, &[0x11u8; 256]); + assert_ne!(signature, &[0u8; 139]); + assert_eq!(res_18b, &[0u8; 5]); + assert!(sgn_size.get() <= 139); + assert_eq!(res_192, &[0u8; 110]); + + let vrf_user_data = VerifiedUserData::new(&mut buf, UserDataType::SgnEcSECP521R1); + let res = verify_signature( + &ec, + MessageDigest::sha512(), + &buf, + vrf_user_data.signature(), + ) + .unwrap(); + assert!(res); + } + + #[test] + fn sign_ec_fail() { + let ec = get_test_asset!("keys/ecsecp256k1.pem"); + let ec = PKey::private_key_from_pem(ec).unwrap(); + + let user_data = UserData::new(Some(ec.clone()), vec![]); + assert!(matches!( + user_data.unwrap_err(), + Error::BinAsrcbUnsupportedUserDataSgnKey + )); + } + + #[test] + fn check_format() { + let (_, ec) = get_test_keys(); + check_key_format(UserDataType::SgnEcSECP521R1, &ec).unwrap(); + let res = check_key_format(UserDataType::SgnRsa2048, &ec); + assert!(matches!(res, Err(Error::AsrcbUserDataKeyMismatch { .. }))); + + let rsa = get_test_asset!("keys/rsa2048key.pub.pem"); + let rsa = PKey::public_key_from_pem(rsa).unwrap(); + check_key_format(UserDataType::SgnRsa2048, &rsa).unwrap(); + + let rsa = get_test_asset!("keys/rsa3072key.pub.pem"); + let rsa = PKey::public_key_from_pem(rsa).unwrap(); + check_key_format(UserDataType::SgnRsa3072, &rsa).unwrap(); + let res = check_key_format(UserDataType::SgnRsa2048, &rsa); + assert!(matches!(res, Err(Error::AsrcbUserDataKeyMismatch { .. }))); + + let rsa = get_test_asset!("keys/rsa4096key.pem"); + let rsa = PKey::private_key_from_pem(rsa).unwrap(); + let rsa = PKey::public_key_from_pem(&rsa.public_key_to_pem().unwrap()).unwrap(); + let res = check_key_format(UserDataType::SgnRsa2048, &rsa); + assert!(matches!(res, Err(Error::AsrcbUserDataKeyMismatch { .. }))); + } + + #[test] + fn kind() { + let (ec, _) = get_test_keys(); + let kind = UserData::user_data_type(&ec).unwrap(); + assert_eq!(kind, UserDataType::SgnEcSECP521R1); + + let rsa = get_test_asset!("keys/rsa2048key.pem"); + let rsa = PKey::private_key_from_pem(rsa).unwrap(); + let kind = UserData::user_data_type(&rsa).unwrap(); + assert_eq!(kind, UserDataType::SgnRsa2048); + + let rsa = get_test_asset!("keys/rsa3072key.pem"); + let rsa = PKey::private_key_from_pem(rsa).unwrap(); + let kind = UserData::user_data_type(&rsa).unwrap(); + assert_eq!(kind, UserDataType::SgnRsa3072); + + let rsa = get_test_asset!("keys/rsa4096key.pem"); + let rsa = PKey::private_key_from_pem(rsa).unwrap(); + let kind = UserData::user_data_type(&rsa).unwrap_err(); + assert!(matches!(kind, Error::BinAsrcbUnsupportedUserDataSgnKey)); + } + + #[test] + fn new() { + let (ec, _) = get_test_keys(); + let user_data = UserData::new( + Some(ec.clone()), + vec![1; UserDataType::SgnEcSECP521R1.max()], + ) + .unwrap(); + assert!(matches!(user_data, UserData::Signed(_))); + + let user_data = UserData::new(Some(ec), vec![1; UserDataType::SgnEcSECP521R1.max() + 1]); + assert!(matches!( + user_data, + Err(Error::AsrcbInvSgnUserData(UserDataType::SgnEcSECP521R1)) + )); + + let user_data = UserData::new(None, vec![1; UserDataType::Unsigned.max()]).unwrap(); + assert!(matches!(user_data, UserData::Unsigned(_))); + + let user_data = UserData::new(None, vec![1; UserDataType::Unsigned.max() + 1]); + assert!(matches!( + user_data, + Err(Error::AsrcbInvSgnUserData(UserDataType::Unsigned)) + )); + } + #[test] + fn data() { + let (ec, _) = get_test_keys(); + let data_in = vec![1; UserDataType::SgnEcSECP521R1.max()]; + let user_data = UserData::new(Some(ec.clone()), data_in.clone()).unwrap(); + let exp_pad = Some(vec![0; UserData::USER_DATA_SIZE - data_in.len()]); + + let (data_out, pad) = user_data.data(); + assert_eq!(data_out, Some(data_in.as_ref())); + assert_eq!(pad, exp_pad); + + let data_in = vec![1; UserDataType::SgnEcSECP521R1.max() - 1]; + let user_data = UserData::new(Some(ec.clone()), data_in.clone()).unwrap(); + let exp_pad = Some(vec![0; UserData::USER_DATA_SIZE - data_in.len()]); + + let (data_out, pad) = user_data.data(); + assert_eq!(data_out, Some(data_in.as_ref())); + assert_eq!(pad, exp_pad); + } +} diff --git a/rust/pv/tests/add_secret_request.rs b/rust/pv/tests/add_secret_request.rs index 0621d01f..adb741af 100644 --- a/rust/pv/tests/add_secret_request.rs +++ b/rust/pv/tests/add_secret_request.rs @@ -5,7 +5,10 @@ use pv::{ get_test_asset, request::{ openssl::pkey::{PKey, Public}, - uvsecret::{AddSecretFlags, AddSecretRequest, AddSecretVersion, ExtSecret, GuestSecret}, + uvsecret::{ + verify_asrcb_and_get_user_data, AddSecretFlags, AddSecretRequest, AddSecretVersion, + ExtSecret, GuestSecret, + }, BootHdrTags, ReqEncrCtx, Request, SymKey, }, test_utils::get_test_keys, @@ -153,3 +156,12 @@ fn null_none_default_cuid_seven() { let exp = get_test_asset!("exp/asrcb/null_none_default_cuid_seven"); assert_eq!(asrcb, exp); } + +#[test] +fn verify_no_user_data() { + let req = get_test_asset!("exp/asrcb/null_none_default_ncuid_one"); + assert!(matches!( + verify_asrcb_and_get_user_data(req.to_vec(), None), + Ok(None) + )) +} diff --git a/rust/pv/tests/assets/keys/ecsecp256k1.pem b/rust/pv/tests/assets/keys/ecsecp256k1.pem new file mode 100644 index 00000000..a3b98abc --- /dev/null +++ b/rust/pv/tests/assets/keys/ecsecp256k1.pem @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BgUrgQQACg== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHQCAQEEIAiQERmhEIwksExNpW2CsaQeFPKFEN6PIdMWUiX3Mu7FoAcGBSuBBAAK +oUQDQgAEO9U+NDdHcIZ59RzTq/6lDH2RA7sEyRlvFVQsj7feiuXh4Q4qeZNCb2sn +s7Hd1lUjFexH+Cp97/ggwSa3q2y/0Q== +-----END EC PRIVATE KEY----- diff --git a/rust/pv/tests/assets/keys/rsa4096key.pem b/rust/pv/tests/assets/keys/rsa4096key.pem new file mode 100644 index 00000000..6dd37066 --- /dev/null +++ b/rust/pv/tests/assets/keys/rsa4096key.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +io4wIYBrX/SPmp7tQvWI18Ike/IDtA== +-----END PRIVATE KEY----- diff --git a/rust/pv_core/src/uvsecret.rs b/rust/pv_core/src/uvsecret.rs index 5a91d2fd..a90bb964 100644 --- a/rust/pv_core/src/uvsecret.rs +++ b/rust/pv_core/src/uvsecret.rs @@ -67,6 +67,13 @@ impl AddSecretMagic { let kind = UserDataType::try_from(kind)?; Ok(Self::from(kind)) } + + /// Returns the [`UserDataType`] of this [`AddSecretMagic`]. + pub fn kind(&self) -> UserDataType { + // Panic: Will never panic. The value is cheched during construcion of the object for + // beeing one of the enum values. + self.kind.get().try_into().unwrap() + } } /// Types of (non architectured) user data for an add-secret request