mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
rust/pv: Use a SecretId struct instead of an array
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 <seiden@linux.ibm.com> Reviewed-by: Julian Ruess <julianr@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
committed by
Jan Höppner
parent
81a1e13f3b
commit
c53dfa9754
@@ -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
|
||||
|
||||
@@ -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<AddSecretVersion> 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();
|
||||
|
||||
@@ -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<Option<[u8; SECRET_SIZE]>>,
|
||||
{
|
||||
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<E>(self, s: &str) -> Result<Self::Value, E>
|
||||
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(),
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<S>(&self, ser: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
//calls Display at one point
|
||||
ser.serialize_str(&self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for SecretId {
|
||||
fn deserialize<D>(de: D) -> std::result::Result<Self, D::Error>
|
||||
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<BigEndian>,
|
||||
#[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<ListableSecretType> for U16<BigEndian> {
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub const SECRET_ID_SIZE: usize = 32;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn ser_gsid<S>(id: &[u8; SECRET_ID_SIZE], ser: S) -> Result<S::Ok, S::Error>
|
||||
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<E>(self, s: &str) -> Result<Self::Value, E>
|
||||
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",
|
||||
)],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<SecretEntry> {
|
||||
|
||||
Reference in New Issue
Block a user