rust/pv: Provide access for SecretList members

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 <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
Steffen Eiden
2023-12-15 11:30:14 +01:00
committed by Jan Höppner
parent 6fd02279da
commit 0764460eaf
2 changed files with 151 additions and 21 deletions

View File

@@ -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},
};
}

View File

@@ -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<SecretEntry>,
}
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<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.secrets.into_iter()
}
}
impl FromIterator<SecretEntry> for SecretList {
fn from_iter<T: IntoIterator<Item = SecretEntry>>(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<SecretEntry>) -> 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<T: Write>(&self, w: &mut T) -> Result<()> {
let num_s = to_u16(self.secrets.len()).ok_or(Error::ManySecrets)?;
w.write_u16::<BigEndian>(num_s)?;
w.write_u16::<BigEndian>(self.total_num_secrets)?;
w.write_u16::<BigEndian>(
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: Read + Seek>(r: &mut R) -> std::io::Result<Self> {
let num_s = r.read_u16::<BigEndian>()?;
let total_num_secrets = r.read_u16::<BigEndian>()?;
let total_num_secrets = r.read_u16::<BigEndian>()? as usize;
let mut v: Vec<SecretEntry> = 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<S: Serializer>(v: &U16<BigEndian>, ser: S) -> Result<S::Ok, S::Error>
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<U16<BigEndian>> for ListableSecretType {
fn from(value: U16<BigEndian>) -> Self {
match value.get() {
0x0000 => Self::Invalid(0),
0x0001 => Self::Invalid(1),
Self::ASSOCIATION => ListableSecretType::Association,
n => Self::Unknown(n),
}
}
}
impl From<ListableSecretType> for U16<BigEndian> {
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::<SecretEntry>(), SECRET_ENTRY_SIZE);
}
#[test]
fn dump_secret_entry() {
const EXP: &[u8] = &[