From c53dfa9754ee85d1bf7e93a243870dcb5d3cf198 Mon Sep 17 00:00:00 2001 From: Steffen Eiden Date: Thu, 15 Feb 2024 17:06:04 +0100 Subject: [PATCH] rust/pv: Use a SecretId struct instead of an array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This streamlines and unifies the use and (de)serialization of structs using a secret id. As a bonus, the hidden `for_pv` module is not longer needed. Signed-off-by: Steffen Eiden Reviewed-by: Julian Ruess Signed-off-by: Jan Höppner --- rust/pv/src/lib.rs | 6 +- rust/pv/src/uvsecret/asrcb.rs | 10 +- rust/pv/src/uvsecret/guest_secret.rs | 49 ++----- rust/pv_core/src/lib.rs | 9 +- rust/pv_core/src/uvdevice/secret_list.rs | 159 ++++++++++++++++++++--- rust/pvapconfig/src/main.rs | 4 +- 6 files changed, 161 insertions(+), 76 deletions(-) diff --git a/rust/pv/src/lib.rs b/rust/pv/src/lib.rs index 37fd00fc..bd839620 100644 --- a/rust/pv/src/lib.rs +++ b/rust/pv/src/lib.rs @@ -35,11 +35,7 @@ const PAGESIZE: usize = 0x1000; /// Definitions and functions for interacting with the Ultravisor pub mod uv { - pub use pv_core::uv::{ - uv_ioctl, ConfigUid, UvCmd, UvDevice, UvDeviceInfo, UvFlags, UvcSuccess, - }; - pub use pv_core::uv::{AddCmd, ListCmd, LockCmd}; - pub use pv_core::uv::{ListableSecretType, SecretEntry, SecretList}; + pub use pv_core::uv::*; } /// Miscellaneous functions and definitions diff --git a/rust/pv/src/uvsecret/asrcb.rs b/rust/pv/src/uvsecret/asrcb.rs index 39584577..6c4b71ab 100644 --- a/rust/pv/src/uvsecret/asrcb.rs +++ b/rust/pv/src/uvsecret/asrcb.rs @@ -23,7 +23,7 @@ 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 -#[derive(Debug, Clone)] +#[derive(Debug)] struct BinGuestSecret(GuestSecret); impl BinGuestSecret { /// Reference to the confidential data @@ -40,7 +40,7 @@ impl BinGuestSecret { let mut buf = vec![0; 48]; buf[3] = 2; buf[7] = 0x20; - buf[16..48].copy_from_slice(id.as_slice()); + buf[16..48].copy_from_slice(id.as_ref()); buf } } @@ -75,7 +75,7 @@ impl ReqAuthData { } } -#[derive(Debug, Clone)] +#[derive(Debug)] struct ReqConfData { secret: BinGuestSecret, extension_secret: Secret<[u8; 32]>, @@ -164,7 +164,7 @@ impl From for RequestVersion { /// | AES GCM Tag (16) | /// |_____________________________________________________________| ///``` -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct AddSecretRequest { version: AddSecretVersion, aad: ReqAuthData, @@ -348,7 +348,7 @@ mod test { fn guest_secret_bin_ap() { let gs: BinGuestSecret = GuestSecret::Association { name: "test".to_string(), - id: [1; 32], + id: [1; 32].into(), secret: [2; 32].into(), } .into(); diff --git a/rust/pv/src/uvsecret/guest_secret.rs b/rust/pv/src/uvsecret/guest_secret.rs index 8e826a7c..cf2d9a02 100644 --- a/rust/pv/src/uvsecret/guest_secret.rs +++ b/rust/pv/src/uvsecret/guest_secret.rs @@ -8,13 +8,14 @@ use crate::{ request::{hash, openssl::MessageDigest, random_array, Secret}, Result, }; -use pv_core::for_pv::{ser_gsid, SECRET_ID_SIZE}; +use pv_core::uv::SecretId; use serde::{Deserialize, Serialize}; use std::convert::TryInto; const SECRET_SIZE: usize = 32; + /// A Secret to be added in [`AddSecretRequest`] -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] pub enum GuestSecret { /// No guest secret Null, @@ -24,10 +25,9 @@ pub enum GuestSecret { Association { /// Name of the secret name: String, - #[serde(serialize_with = "ser_gsid", deserialize_with = "de_gsid")] /// SHA256 hash of [`GuestSecret::Association::name`] - id: [u8; SECRET_ID_SIZE], - /// Confidential actual assocuiation secret (32 bytes) + id: SecretId, + /// Confidential actual association secret (32 bytes) #[serde(skip)] secret: Secret<[u8; SECRET_SIZE]>, }, @@ -46,7 +46,10 @@ impl GuestSecret { where O: Into>, { - let id = hash(MessageDigest::sha256(), name.as_bytes())?.to_vec(); + let id: [u8; SecretId::ID_SIZE] = hash(MessageDigest::sha256(), name.as_bytes())? + .to_vec() + .try_into() + .unwrap(); let secret = match secret.into() { Some(s) => s, None => random_array()?, @@ -54,39 +57,11 @@ impl GuestSecret { Ok(GuestSecret::Association { name: name.to_string(), - id: id.try_into().unwrap(), + id: id.into(), secret: secret.into(), }) } } -fn de_gsid<'de, D>(de: D) -> Result<[u8; 32], D::Error> -where - D: serde::Deserializer<'de>, -{ - struct FieldVisitor; - - impl<'de> serde::de::Visitor<'de> for FieldVisitor { - type Value = [u8; SECRET_ID_SIZE]; - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a `32 bytes long hexstring` prepended with 0x") - } - fn visit_str(self, s: &str) -> Result - where - E: serde::de::Error, - { - if s.len() != SECRET_ID_SIZE * 2 + 2 { - return Err(serde::de::Error::invalid_length(s.len(), &self)); - } - let nb = s.strip_prefix("0x").ok_or_else(|| { - serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self) - })?; - crate::misc::parse_hex(nb) - .try_into() - .map_err(|_| serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self)) - } - } - de.deserialize_identifier(FieldVisitor) -} #[cfg(test)] mod test { @@ -106,7 +81,7 @@ mod test { let secret = GuestSecret::association("association secret", secret_value).unwrap(); let exp = GuestSecret::Association { name, - id: exp_id, + id: exp_id.into(), secret: secret_value.into(), }; assert_eq!(secret, exp); @@ -121,7 +96,7 @@ mod test { ]; let asc = GuestSecret::Association { name: "test123".to_string(), - id, + id: id.into(), secret: [0; 32].into(), }; diff --git a/rust/pv_core/src/lib.rs b/rust/pv_core/src/lib.rs index 72ddde0b..d8dd7018 100644 --- a/rust/pv_core/src/lib.rs +++ b/rust/pv_core/src/lib.rs @@ -37,7 +37,7 @@ pub mod misc { /// Definitions and functions for interacting with the Ultravisor pub mod uv { pub use crate::uvdevice::secret::{AddCmd, ListCmd, LockCmd}; - pub use crate::uvdevice::secret_list::{ListableSecretType, SecretEntry, SecretList}; + pub use crate::uvdevice::secret_list::{ListableSecretType, SecretEntry, SecretId, SecretList}; pub use crate::uvdevice::{ uv_ioctl, ConfigUid, UvCmd, UvDevice, UvDeviceInfo, UvFlags, UvcSuccess, }; @@ -84,10 +84,3 @@ pub const fn crate_info() -> &'static str { const PAGESIZE: usize = 0x1000; use ::utils::assert_size; use ::utils::static_assert; - -#[doc(hidden)] -/// stuff pv_core and pv share. Not intended for other users -pub mod for_pv { - pub use crate::uvdevice::secret_list::ser_gsid; - pub use crate::uvdevice::secret_list::SECRET_ID_SIZE; -} diff --git a/rust/pv_core/src/uvdevice/secret_list.rs b/rust/pv_core/src/uvdevice/secret_list.rs index a12ed39b..8128310b 100644 --- a/rust/pv_core/src/uvdevice/secret_list.rs +++ b/rust/pv_core/src/uvdevice/secret_list.rs @@ -3,7 +3,7 @@ // Copyright IBM Corp. 2024 use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; -use serde::{Serialize, Serializer}; +use serde::{Deserialize, Serialize, Serializer}; use std::{ fmt::Display, io::{Cursor, Read, Seek, Write}, @@ -15,6 +15,64 @@ use zerocopy::{AsBytes, FromBytes, FromZeroes, U16, U32}; use crate::{misc::to_u16, uv::ListCmd, uvdevice::UvCmd, Error, Result}; +/// The 32 byte long ID of an UV secret +/// +/// (de)serializes itself in/from a hex-string +#[repr(C)] +#[derive(PartialEq, Eq, AsBytes, FromZeroes, FromBytes, Debug, Clone)] +pub struct SecretId([u8; Self::ID_SIZE]); +assert_size!(SecretId, SecretId::ID_SIZE); + +impl SecretId { + /// Size in bytes of the [`SecretId`] + pub const ID_SIZE: usize = 32; + + /// Create a [`SecretId`] forom a buffer. + pub fn from(buf: [u8; Self::ID_SIZE]) -> Self { + buf.into() + } +} + +impl Serialize for SecretId { + fn serialize(&self, ser: S) -> std::result::Result + where + S: Serializer, + { + //calls Display at one point + ser.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for SecretId { + fn deserialize(de: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + de_gsid(de).map(|id| id.into()) + } +} + +impl Display for SecretId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut s = String::with_capacity(32 * 2 + 2); + s.push_str("0x"); + let s = self.0.iter().fold(s, |acc, e| acc + &format!("{e:02x}")); + write!(f, "{s}") + } +} + +impl From<[u8; SecretId::ID_SIZE]> for SecretId { + fn from(value: [u8; SecretId::ID_SIZE]) -> Self { + Self(value) + } +} + +impl AsRef<[u8]> for SecretId { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + /// A secret in a [`SecretList`] #[repr(C)] #[derive(Debug, PartialEq, Eq, AsBytes, FromZeroes, FromBytes, Serialize)] @@ -27,8 +85,7 @@ pub struct SecretEntry { len: U32, #[serde(skip)] res_8: u64, - #[serde(serialize_with = "ser_gsid")] - id: [u8; SECRET_ID_SIZE], + id: SecretId, } assert_size!(SecretEntry, SecretEntry::STRUCT_SIZE); @@ -39,7 +96,7 @@ impl SecretEntry { /// /// The content of this entry will very liekly not represent the status of the guest in the /// Ultravisor. Use of [`SecretList::decode`] in any non-test environments is encuraged. - pub fn new(index: u16, stype: ListableSecretType, id: [u8; 32], secret_len: u32) -> Self { + pub fn new(index: u16, stype: ListableSecretType, id: SecretId, secret_len: u32) -> Self { Self { index: index.into(), stype: stype.into(), @@ -60,8 +117,16 @@ impl SecretEntry { } /// Returns a reference to the id of this [`SecretEntry`]. + /// + /// The slice is guaranteed to be 32 bytes long. + /// ```rust + /// # use pv_core::uv::SecretEntry; + /// # use zerocopy::FromZeroes; + /// # let secr = SecretEntry::new_zeroed(); + /// # assert_eq!(secr.id().len(), 32); + /// ``` pub fn id(&self) -> &[u8] { - &self.id + self.id.as_ref() } } @@ -70,7 +135,7 @@ impl Display for SecretEntry { let stype: ListableSecretType = self.stype.into(); writeln!(f, "{} {}:", self.index, stype)?; write!(f, " ")?; - for b in self.id { + for b in self.id.as_ref() { write!(f, "{b:02x}")?; } Ok(()) @@ -263,23 +328,40 @@ impl From for U16 { } } -#[doc(hidden)] -pub const SECRET_ID_SIZE: usize = 32; - -#[doc(hidden)] -pub fn ser_gsid(id: &[u8; SECRET_ID_SIZE], ser: S) -> Result +fn de_gsid<'de, D>(de: D) -> Result<[u8; 32], D::Error> where - S: serde::Serializer, + D: serde::Deserializer<'de>, { - let mut s = String::with_capacity(32 * 2 + 2); - s.push_str("0x"); - let s = id.iter().fold(s, |acc, e| acc + &format!("{e:02x}")); - ser.serialize_str(&s) + struct FieldVisitor; + + impl<'de> serde::de::Visitor<'de> for FieldVisitor { + type Value = [u8; SecretId::ID_SIZE]; + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a `32 bytes long hexstring` prepended with 0x") + } + fn visit_str(self, s: &str) -> Result + where + E: serde::de::Error, + { + if s.len() != SecretId::ID_SIZE * 2 + 2 { + return Err(serde::de::Error::invalid_length(s.len(), &self)); + } + let nb = s.strip_prefix("0x").ok_or_else(|| { + serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self) + })?; + crate::misc::parse_hex(nb) + .try_into() + .map_err(|_| serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self)) + } + } + de.deserialize_identifier(FieldVisitor) } #[cfg(test)] mod test { + use serde_test::{assert_ser_tokens, assert_tokens, Token}; + use super::*; use std::io::{BufReader, BufWriter, Cursor}; @@ -299,7 +381,7 @@ mod test { stype: 2.into(), len: 32.into(), res_8: 0, - id: [0; 32], + id: SecretId::from([0; 32]), }; assert_eq!(s.as_bytes(), EXP); @@ -328,7 +410,7 @@ mod test { stype: 2.into(), len: 32.into(), res_8: 0, - id: [0; 32], + id: SecretId::from([0; 32]), }], }; @@ -360,7 +442,7 @@ mod test { stype: 2.into(), len: 32.into(), res_8: 0, - id: [0; 32], + id: SecretId::from([0; 32]), }], }; @@ -372,4 +454,43 @@ mod test { println!("list: {sl:?}"); assert_eq!(buf, EXP); } + + #[test] + fn secret_entry_ser() { + let entry = SecretEntry::new_zeroed(); + + assert_ser_tokens( + &entry, + &[ + Token::Struct { + name: "SecretEntry", + len: (4), + }, + Token::String("index"), + Token::U16(0), + Token::String("stype"), + Token::U16(0), + Token::String("len"), + Token::U32(0), + Token::String("id"), + Token::String("0x0000000000000000000000000000000000000000000000000000000000000000"), + Token::StructEnd, + ], + ) + } + + #[test] + fn secret_id_serde() { + let id = SecretId::from([ + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, + 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, + 0x89, 0xab, 0xcd, 0xef, + ]); + assert_tokens( + &id, + &[Token::String( + "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", + )], + ) + } } diff --git a/rust/pvapconfig/src/main.rs b/rust/pvapconfig/src/main.rs index 40018e02..a3a0bb30 100644 --- a/rust/pvapconfig/src/main.rs +++ b/rust/pvapconfig/src/main.rs @@ -528,8 +528,8 @@ mod tests { fn make_assoc_secretentry(idx: u16, hexidstr: &str) -> SecretEntry { let id = hexstring_to_u8(hexidstr); let idlen: u32 = id.len().try_into().unwrap(); - let idarray = <&[u8; 32]>::try_from(id.as_slice()).unwrap(); - SecretEntry::new(idx, ListableSecretType::Association, *idarray, idlen) + let idarray: [u8; 32] = id.try_into().unwrap(); + SecretEntry::new(idx, ListableSecretType::Association, idarray.into(), idlen) } fn make_test_secrets() -> Vec {