From 0764460eafdfd91f78bbed5ac818f19d60e14b70 Mon Sep 17 00:00:00 2001 From: Steffen Eiden Date: Fri, 15 Dec 2023 11:30:14 +0100 Subject: [PATCH] rust/pv: Provide access for SecretList members MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds getter for SecretList and SecretEntry. Adds enum to represent secret types. Add Iterator functionality for SecretList. While at it, make the datatype of the capacity of the list transparent for users. Signed-off-by: Steffen Eiden Signed-off-by: Jan Höppner --- rust/pv/src/lib.rs | 2 +- rust/pv/src/uvsecret/secret_list.rs | 170 ++++++++++++++++++++++++---- 2 files changed, 151 insertions(+), 21 deletions(-) diff --git a/rust/pv/src/lib.rs b/rust/pv/src/lib.rs index d72ac93e..ed891408 100644 --- a/rust/pv/src/lib.rs +++ b/rust/pv/src/lib.rs @@ -70,7 +70,7 @@ pub mod uv { }; #[cfg(feature = "uvsecret")] pub use crate::uvsecret::{ - secret_list::SecretList, + secret_list::{ListableSecretType, SecretEntry, SecretList}, uvc::{AddCmd, ListCmd, LockCmd}, }; } diff --git a/rust/pv/src/uvsecret/secret_list.rs b/rust/pv/src/uvsecret/secret_list.rs index 6943bd31..72a05b27 100644 --- a/rust/pv/src/uvsecret/secret_list.rs +++ b/rust/pv/src/uvsecret/secret_list.rs @@ -2,13 +2,14 @@ // // Copyright IBM Corp. 2023 -use crate::{misc::to_u16, uv::ListCmd, uvdevice::UvCmd, Error, Result}; +use crate::{assert_size, misc::to_u16, uv::ListCmd, uvdevice::UvCmd, Error, Result}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use serde::{Serialize, Serializer}; -use std::usize; use std::{ fmt::Display, io::{Cursor, Read, Seek, Write}, + slice::Iter, + vec::IntoIter, }; use zerocopy::{AsBytes, FromBytes, U16, U32}; @@ -19,16 +20,79 @@ use super::ser_gsid; /// Requires the `uvsecret` feature. #[derive(Debug, PartialEq, Eq, Serialize)] pub struct SecretList { - total_num_secrets: u16, + total_num_secrets: usize, secrets: Vec, } +impl<'a> IntoIterator for &'a SecretList { + type Item = &'a SecretEntry; + type IntoIter = Iter<'a, SecretEntry>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl IntoIterator for SecretList { + type Item = SecretEntry; + type IntoIter = IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.secrets.into_iter() + } +} + +impl FromIterator for SecretList { + fn from_iter>(iter: T) -> Self { + let secrets: Vec<_> = iter.into_iter().collect(); + let total_num_secrets = secrets.len() as u16; + Self::new(total_num_secrets, secrets) + } +} + impl SecretList { + #[doc(hidden)] + /// For testing purposes. + pub fn new(total_num_secrets: u16, secrets: Vec) -> Self { + Self { + total_num_secrets: total_num_secrets as usize, + secrets, + } + } + + /// Returns an iterator over the slice. + /// + /// The iterator yields all secret entries from start to end. + pub fn iter(&self) -> Iter<'_, SecretEntry> { + self.secrets.iter() + } + + /// Returns the length of this [`SecretList`]. + pub fn len(&self) -> usize { + self.secrets.len() + } + + /// Check for is_empty of this [`SecretList`]. + pub fn is_empty(&self) -> bool { + self.secrets.is_empty() + } + + /// Reports the number of secrets stored in UV + /// + /// This number may be not equal to the provided number of [`SecretEntry`] + pub fn total_num_secrets(&self) -> usize { + self.total_num_secrets + } + /// Encodes the list in the same binary format the UV would do pub fn encode(&self, w: &mut T) -> Result<()> { let num_s = to_u16(self.secrets.len()).ok_or(Error::ManySecrets)?; w.write_u16::(num_s)?; - w.write_u16::(self.total_num_secrets)?; + w.write_u16::( + self.total_num_secrets + .try_into() + .map_err(|_| Error::ManySecrets)?, + )?; w.write_all(&[0u8; 12])?; for secret in &self.secrets { w.write_all(secret.as_bytes())?; @@ -39,10 +103,10 @@ impl SecretList { /// Decodes the list from the binary format of the UV into this internal representation pub fn decode(r: &mut R) -> std::io::Result { let num_s = r.read_u16::()?; - let total_num_secrets = r.read_u16::()?; + let total_num_secrets = r.read_u16::()? as usize; let mut v: Vec = Vec::with_capacity(num_s as usize); r.seek(std::io::SeekFrom::Current(12))?; //skip reserved bytes - let mut buf = [0u8; SECRET_ENTRY_SIZE]; + let mut buf = [0u8; SecretEntry::STRUCT_SIZE]; for _ in 0..num_s { r.read_exact(&mut buf)?; //cannot fail. buffer has the same size as the secret entry @@ -84,9 +148,56 @@ fn ser_u16(v: &U16, ser: S) -> Result ser.serialize_u16(v.get()) } +/// Secret types that can appear in a [`SecretList`] +#[non_exhaustive] +#[derive(PartialEq, Eq)] +pub enum ListableSecretType { + /// Association Secret + Association, + /// Invalid secret type, that should never appear in a list + /// + /// 0 is reserved + /// 1 is Null secret, with no id and not listable + Invalid(u16), + /// Unknown secret type + Unknown(u16), +} +impl ListableSecretType { + const ASSOCIATION: u16 = 0x0002; +} + +impl Display for ListableSecretType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Association => write!(f, "Association"), + Self::Invalid(n) => write!(f, "Invalid({n})"), + Self::Unknown(n) => write!(f, "Unknown({n})"), + } + } +} + +impl From> for ListableSecretType { + fn from(value: U16) -> Self { + match value.get() { + 0x0000 => Self::Invalid(0), + 0x0001 => Self::Invalid(1), + Self::ASSOCIATION => ListableSecretType::Association, + n => Self::Unknown(n), + } + } +} + +impl From for U16 { + fn from(value: ListableSecretType) -> Self { + match value { + ListableSecretType::Association => ListableSecretType::ASSOCIATION, + ListableSecretType::Invalid(n) | ListableSecretType::Unknown(n) => n, + } + .into() + } +} + /// A secret in a [`SecretList`] -/// -/// Fields are in big endian #[repr(C)] #[derive(Debug, PartialEq, Eq, AsBytes, FromBytes, Serialize)] pub struct SecretEntry { @@ -101,20 +212,43 @@ pub struct SecretEntry { #[serde(serialize_with = "ser_gsid")] id: [u8; 32], } -const SECRET_ENTRY_SIZE: usize = 0x30; +assert_size!(SecretEntry, SecretEntry::STRUCT_SIZE); -fn stype_str(stype: u16) -> String { - match stype { - // should never match (not incl in list), but here for completeness - 1 => "Null".to_string(), - 2 => "Association".to_string(), - n => format!("Unknown {n}"), +impl SecretEntry { + const STRUCT_SIZE: usize = 0x30; + + #[doc(hidden)] + /// For testing purposes. + pub fn new(index: u16, stype: ListableSecretType, id: [u8; 32], secret_len: u32) -> Self { + Self { + index: index.into(), + stype: stype.into(), + len: secret_len.into(), + res_8: 0, + id, + } + } + + /// Returns the index of this [`SecretEntry`]. + pub fn index(&self) -> u16 { + self.index.get() + } + + /// Returns the secret type of this [`SecretEntry`]. + pub fn stype(&self) -> ListableSecretType { + self.stype.into() + } + + /// Returns a reference to the id of this [`SecretEntry`]. + pub fn id(&self) -> &[u8] { + &self.id } } impl Display for SecretEntry { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!(f, "{} {}:", self.index, stype_str(self.stype.get()))?; + let stype: ListableSecretType = self.stype.into(); + writeln!(f, "{} {}:", self.index, stype)?; write!(f, " ")?; for b in self.id { write!(f, "{b:02x}")?; @@ -128,10 +262,6 @@ mod test { use super::*; use std::io::{BufReader, BufWriter, Cursor}; - #[test] - fn secret_entry_size() { - assert_eq!(::std::mem::size_of::(), SECRET_ENTRY_SIZE); - } #[test] fn dump_secret_entry() { const EXP: &[u8] = &[