diff --git a/rust/pv/src/crypto.rs b/rust/pv/src/crypto.rs index eb2a5e24..36bdb16e 100644 --- a/rust/pv/src/crypto.rs +++ b/rust/pv/src/crypto.rs @@ -16,7 +16,7 @@ use openssl::{ sign::{Signer, Verifier}, symm::{encrypt, encrypt_aead, Cipher}, }; -use std::convert::TryInto; +use std::{convert::TryInto, ops::Range}; /// An AES256-key that will purge itself out of the memory when going out of scope /// @@ -182,7 +182,12 @@ pub fn encrypt_aes(key: &SymKey, iv: &[u8], conf: &[u8]) -> Result> { /// # Errors /// /// 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> { +pub fn encrypt_aes_gcm( + key: &SymKey, + iv: &[u8], + aad: &[u8], + conf: &[u8], +) -> Result<(Vec, Range, Range, Range)> { let mut tag = vec![0xff; AES_256_GCM_TAG_SIZE]; let encr = match key { SymKey::Aes256(key) => encrypt_aead( @@ -195,12 +200,24 @@ pub fn encrypt_aes_gcm(key: &SymKey, iv: &[u8], aad: &[u8], conf: &[u8]) -> Resu )?, }; - let mut res = vec![0; aad.len() + encr.len() + 16]; - res[0..aad.len()].copy_from_slice(aad); - res[aad.len()..aad.len() + encr.len()].copy_from_slice(&encr); - res[aad.len() + encr.len()..aad.len() + encr.len() + 16].copy_from_slice(&tag); + let mut res = vec![0; aad.len() + encr.len() + tag.len()]; + let aad_range = Range { + start: 0, + end: aad.len(), + }; + let encr_range = Range { + start: aad.len(), + end: aad.len() + encr.len(), + }; + let tag_range = Range { + start: aad.len() + encr.len(), + end: aad.len() + encr.len() + tag.len(), + }; - Ok(res) + res[aad_range.clone()].copy_from_slice(aad); + res[encr_range.clone()].copy_from_slice(&encr); + res[tag_range.clone()].copy_from_slice(&tag); + Ok((res, aad_range, encr_range, tag_range)) } /// Calculate the hash of a slice. @@ -367,7 +384,7 @@ mod tests { 0xee, 0x62, 0x98, 0xf7, 0x7e, 0x0c, ]; - let res = encrypt_aes_gcm( + let (res, ..) = encrypt_aes_gcm( &SymKey::Aes256(aes_gcm_key.into()), &aes_gcm_iv, &aes_gcm_aad, diff --git a/rust/pv/src/req.rs b/rust/pv/src/req.rs index f88aeb93..2f021992 100644 --- a/rust/pv/src/req.rs +++ b/rust/pv/src/req.rs @@ -16,6 +16,7 @@ use openssl::pkey::{PKey, PKeyRef, Private, Public}; use pv_core::request::{RequestMagic, RequestVersion}; use std::convert::TryInto; use std::mem::size_of; +use std::ops::Range; use utils::assert_size; use zerocopy::{AsBytes, BigEndian, FromBytes, FromZeroes, U32}; @@ -97,7 +98,7 @@ impl Encrypt for Keyslot { to: &mut Vec, ) -> Result<()> { let derived_key = derive_key(priv_key, &self.0)?; - let mut wrpk_and_kst = encrypt_aes_gcm(&derived_key.into(), &[0; 12], &[], prot_key)?; + let (mut wrpk_and_kst, ..) = encrypt_aes_gcm(&derived_key.into(), &[0; 12], &[], prot_key)?; let phk: EcdhPubkeyCoord = self.0.as_ref().try_into()?; to.reserve(80); @@ -264,7 +265,11 @@ impl ReqEncrCtx { /// # Errors /// /// This function will return an error if the data could not be encrypted by OpenSSL. - pub fn encrypt_aead(&self, aad: &[u8], conf: &[u8]) -> Result> { + pub fn encrypt_aead( + &self, + aad: &[u8], + conf: &[u8], + ) -> Result<(Vec, Range, Range, Range)> { encrypt_aes_gcm(&self.prot_key, &self.iv, aad, conf) } } diff --git a/rust/pv/src/uvsecret/asrcb.rs b/rust/pv/src/uvsecret/asrcb.rs index 006feac8..ced2285f 100644 --- a/rust/pv/src/uvsecret/asrcb.rs +++ b/rust/pv/src/uvsecret/asrcb.rs @@ -2,13 +2,14 @@ // // Copyright IBM Corp. 2023 +use super::user_data::UserData; use crate::{ assert_size, misc::Flags, request::{ hkdf_rfc_5869, openssl::{ - pkey::{PKey, Public}, + pkey::{PKey, Private, Public}, Md, }, uvsecret::{ExtSecret, GuestSecret}, @@ -17,10 +18,7 @@ use crate::{ uv::{ConfigUid, UvFlags}, Result, }; -use pv_core::request::{ - uvsecret::{AddSecretMagic, UserDataType}, - RequestVersion, -}; +use pv_core::request::RequestVersion; use zerocopy::AsBytes; /// Internal wrapper for Guest Secret, so that we can dump it in the form the UV wants it to be @@ -54,6 +52,7 @@ impl From for BinGuestSecret { } } +/// Authenticated data w/o user data #[repr(C)] #[derive(Debug, Clone, Copy, AsBytes)] struct ReqAuthData { @@ -61,9 +60,8 @@ struct ReqAuthData { boot_tags: BootHdrTags, cuid: ConfigUid, reserved90: [u8; 0x100], - prog_res190: [u8; 0x200], } -assert_size!(ReqAuthData, 0x3e8); +assert_size!(ReqAuthData, 0x1e8); impl ReqAuthData { fn new>(boot_tags: BootHdrTags, flags: F) -> Self { @@ -72,7 +70,6 @@ impl ReqAuthData { boot_tags, cuid: [0; 0x10], reserved90: [0; 0x100], - prog_res190: [0; 0x200], } } } @@ -168,15 +165,14 @@ impl From for RequestVersion { ///``` #[derive(Clone, Debug)] pub struct AddSecretRequest { - magic: AddSecretMagic, version: AddSecretVersion, aad: ReqAuthData, keyslots: Vec, conf: ReqConfData, + user_data: UserData, } 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; @@ -199,7 +195,7 @@ impl AddSecretRequest { aad: ReqAuthData::new(boot_tags, flags), keyslots: vec![], version, - magic: UserDataType::Null.into(), + user_data: UserData::Null, } } @@ -233,18 +229,41 @@ impl AddSecretRequest { &self.conf.secret.0 } + /// Add user-data to the Add-Secret request + /// + /// (Signed) user-data is a non-architectual feature. It allows to add arbitrary + /// data (message) to the request, that is signed optionally with an user defined key. + /// Allowed keys are: + /// - no key (up to 512 bytes of message) + /// - EC SECP521R1 (up to 256 byte message) + /// - RSA 2048 bit (up to 256 byte message) + /// - RSA 3072 bit (up to 128 byte message) + /// + /// The signature can be verified during the verification of the secret-request on the target machine. + pub fn set_user_data(&mut self, msg: Vec, skey: Option>) -> Result<()> { + self.user_data = UserData::new(skey, msg)?; + Ok(()) + } + /// compiles the authenticated area of this request fn aad(&self, ctx: &ReqEncrCtx, conf_len: usize) -> Result> { let cust_pub_key = ctx.key_coords()?; let secr_auth = self.conf.secret.dump_auth(); + let user_data = self.user_data.data(); - let mut aad: Vec = Vec::with_capacity(3 + self.keyslots.len()); + let mut aad: Vec = Vec::with_capacity(5 + self.keyslots.len()); aad.push(Aad::Plain(self.aad.as_bytes())); + if let Some(data) = user_data.0 { + aad.push(Aad::Plain(data)); + } + if let Some(data) = &user_data.1 { + aad.push(Aad::Plain(data)); + } aad.push(Aad::Plain(cust_pub_key.as_ref())); self.keyslots.iter().for_each(|k| aad.push(Aad::Ks(k))); aad.push(Aad::Plain(&secr_auth)); - ctx.build_aad(self.version.into(), &aad, conf_len, self.magic.get()) + ctx.build_aad(self.version.into(), &aad, conf_len, self.user_data.magic()) } #[doc(hidden)] @@ -263,13 +282,41 @@ impl AddSecretRequest { res.append(&mut vec![0x24; 32]); Ok(res) } + + /// encrypt data, sign request with user-provided signing key, insert signature into aad, + /// calculate request tag + fn encrypt_with_signed_user_data(&self, ctx: &ReqEncrCtx) -> Result> { + //encrypt data w/o aead + let conf = self.conf.to_bytes(); + let aad = self.aad(ctx, conf.value().len())?; + let (mut buf, aad_range, encr_range, _) = ctx.encrypt_aead(&aad, conf.value())?; + + drop(aad); + + // sign aad+encrypted data (w/o tag) with user signning key + // add signature to authenticated data starting with USER_DATA_OFFS + self.user_data.sign( + &mut buf[aad_range.start..encr_range.end], + Self::V1_USER_DATA_OFFS, + )?; + + // encrypt again with signed data + buf[encr_range.clone()].copy_from_slice(conf.value()); + ctx.encrypt_aead(&buf[aad_range], &buf[encr_range]) + .map(|(buf, ..)| buf) + } } impl Request for AddSecretRequest { fn encrypt(&self, ctx: &ReqEncrCtx) -> Result> { - let conf = self.conf.to_bytes(); - let aad = self.aad(ctx, conf.value().len())?; - ctx.encrypt_aead(&aad, conf.value()) + match self.user_data { + UserData::Null | UserData::Unsigned(_) => { + let conf = self.conf.to_bytes(); + let aad = self.aad(ctx, conf.value().len())?; + ctx.encrypt_aead(&aad, conf.value()).map(|(buf, ..)| buf) + } + _ => self.encrypt_with_signed_user_data(ctx), + } } fn add_hostkey(&mut self, hostkey: PKey) { diff --git a/rust/pv/src/uvsecret/user_data.rs b/rust/pv/src/uvsecret/user_data.rs index e44942b7..9f650291 100644 --- a/rust/pv/src/uvsecret/user_data.rs +++ b/rust/pv/src/uvsecret/user_data.rs @@ -1,4 +1,3 @@ -#![allow(unused)] use crate::{ crypto::{sign_msg, verify_signature}, req::BinReqValues, @@ -491,7 +490,7 @@ mod test { res_192, } = buf_ec; assert_eq!(data, &[0x11u8; 256]); - assert_ne!(signature, &[0u8; 139]); + assert_ne!(signature, &[0x11u8; 139]); assert_eq!(res_18b, &[0u8; 5]); assert!(sgn_size.get() <= 139); assert_eq!(res_192, &[0u8; 110]); diff --git a/rust/pv/tests/add_secret_request.rs b/rust/pv/tests/add_secret_request.rs index adb741af..e7774965 100644 --- a/rust/pv/tests/add_secret_request.rs +++ b/rust/pv/tests/add_secret_request.rs @@ -1,6 +1,11 @@ // SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 +use openssl::{ + ec::{EcGroup, EcKey}, + nid::Nid, + pkey::Private, +}; use pv::{ get_test_asset, request::{ @@ -86,6 +91,93 @@ fn no_flag() -> AddSecretFlags { AddSecretFlags::default() } +fn create_signed_asrcb(skey: PKey, user_data: Vec) -> Vec { + let (host_key, ctx) = get_crypto(); + let mut asrcb = + AddSecretRequest::new(AddSecretVersion::One, GuestSecret::Null, TAGS, no_flag()); + + asrcb.add_hostkey(host_key); + asrcb.set_user_data(user_data, Some(skey)).unwrap(); + asrcb.encrypt(&ctx).unwrap() +} + +#[test] +fn null_none_default_ncuid_one_user_unsgn() { + let user_data_orig = vec![0x56; 0x183]; + let (host_key, ctx) = get_crypto(); + let mut asrcb = + AddSecretRequest::new(AddSecretVersion::One, GuestSecret::Null, TAGS, no_flag()); + + asrcb.add_hostkey(host_key); + asrcb.set_user_data(user_data_orig.clone(), None).unwrap(); + let asrcb = asrcb.encrypt(&ctx).unwrap(); + + let user_data = verify_asrcb_and_get_user_data(asrcb, None).unwrap(); + + assert_eq!( + user_data_orig.as_slice(), + &user_data.as_ref().unwrap()[..user_data_orig.len()] + ); +} +#[test] +fn null_none_default_ncuid_one_user_ec() { + let (usr_sgn_key, _) = get_test_keys(); + + let usr_vrfy_key = usr_sgn_key.ec_key().unwrap(); + let usr_vrfy_key = usr_vrfy_key.public_key(); + let usr_vrfy_key = PKey::from_ec_key( + EcKey::from_public_key( + &EcGroup::from_curve_name(Nid::SECP521R1).unwrap(), + usr_vrfy_key, + ) + .unwrap(), + ) + .unwrap(); + + let user_data_orig = vec![0x56; 0x100]; + let asrcb = create_signed_asrcb(usr_sgn_key, user_data_orig.clone()); + + let user_data = verify_asrcb_and_get_user_data(asrcb, Some(usr_vrfy_key)).unwrap(); + assert_eq!( + user_data_orig.as_slice(), + &user_data.as_ref().unwrap()[..user_data_orig.len()] + ); +} + +#[test] +fn null_none_default_ncuid_one_user_rsa2048() { + let usr_sgn_key = get_test_asset!("keys/rsa2048key.pem"); + let usr_sgn_key = PKey::private_key_from_pem(usr_sgn_key).unwrap(); + let user_data_orig = vec![0x56; 0x100]; + let asrcb = create_signed_asrcb(usr_sgn_key, user_data_orig.clone()); + + let usr_vrfy_key = get_test_asset!("keys/rsa2048key.pub.pem"); + let usr_vrfy_key = PKey::public_key_from_pem(usr_vrfy_key).unwrap(); + + let user_data = verify_asrcb_and_get_user_data(asrcb, Some(usr_vrfy_key)).unwrap(); + assert_eq!( + user_data_orig.as_slice(), + &user_data.as_ref().unwrap()[..user_data_orig.len()] + ); +} + +#[test] +fn null_none_default_ncuid_one_user_rsa3072() { + let usr_sgn_key = get_test_asset!("keys/rsa3072key.pem"); + let usr_sgn_key = PKey::private_key_from_pem(usr_sgn_key).unwrap(); + let user_data_orig = vec![0x56; 0x80]; + let asrcb = create_signed_asrcb(usr_sgn_key, user_data_orig.clone()); + + let usr_vrfy_key = get_test_asset!("keys/rsa3072key.pub.pem"); + let usr_vrfy_key = PKey::public_key_from_pem(usr_vrfy_key).unwrap(); + + let user_data = verify_asrcb_and_get_user_data(asrcb, Some(usr_vrfy_key)).unwrap(); + assert_eq!( + user_data_orig.as_slice(), + &user_data.as_ref().unwrap()[..user_data_orig.len()] + ); +} + #[test] fn null_none_default_cuid_one() { let asrcb = gen_asrcb(GuestSecret::Null, None, no_flag(), true).unwrap();