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] = &[