mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
rust/pv_core: Attestation support
Add functionality for: * sending attestation requests to the uvdevice and retrieve the response * create/read the attestation exchange format file format Acked-by: Marc Hartmayer <mhartmay@linux.ibm.com> Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2023
|
||||
// Copyright IBM Corp. 2023, 2024
|
||||
|
||||
/// Result type for this crate
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
@@ -49,6 +49,18 @@ pub enum Error {
|
||||
#[error("Input contains unsupported user-data type: {0:#06x}")]
|
||||
UnsupportedUserData(u16),
|
||||
|
||||
#[error("The input has not the correct format: {field} is too large. Maximal size {max_size}")]
|
||||
AttDataSizeLarge { field: &'static str, max_size: u32 },
|
||||
|
||||
#[error("The input has not the correct format: {field} is too small. Minimal size {min_size}")]
|
||||
AttDataSizeSmall { field: &'static str, min_size: u32 },
|
||||
|
||||
#[error("The attestation request has an unknown algorithm type (.0)")]
|
||||
BinArcbInvAlgorithm(u32),
|
||||
|
||||
#[error("The attestation request does not specify a measurement size or measurement data.")]
|
||||
BinArcbNoMeasurement,
|
||||
|
||||
// errors from other crates
|
||||
#[error(transparent)]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
@@ -6,11 +6,17 @@
|
||||
mod error;
|
||||
mod macros;
|
||||
mod utils;
|
||||
mod uvattest;
|
||||
mod uvdevice;
|
||||
mod uvsecret;
|
||||
|
||||
pub use error::{Error, FileAccessErrorType, FileIoErrorType, Result};
|
||||
|
||||
/// Functionalities for reading attestation requests
|
||||
pub mod attest {
|
||||
pub use crate::uvattest::{AttestationMagic, AttestationMeasAlg};
|
||||
}
|
||||
|
||||
/// Miscellaneous functions and definitions
|
||||
pub mod misc {
|
||||
pub use crate::utils::pv_guest_bit_set;
|
||||
@@ -25,6 +31,7 @@ pub mod misc {
|
||||
/// For detailed Information on how to send Ultravisor Commands see [`crate::uv::UvDevice`] and
|
||||
/// [`crate::uv::UvCmd`]
|
||||
pub mod uv {
|
||||
pub use crate::uvdevice::attest::AttestationCmd;
|
||||
pub use crate::uvdevice::secret::{AddCmd, ListCmd, LockCmd};
|
||||
pub use crate::uvdevice::secret_list::{ListableSecretType, SecretEntry, SecretId, SecretList};
|
||||
pub use crate::uvdevice::{ConfigUid, UvCmd, UvDevice, UvDeviceInfo, UvFlags, UvcSuccess};
|
||||
|
||||
61
rust/pv_core/src/uvattest.rs
Normal file
61
rust/pv_core/src/uvattest.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
use crate::{request::MagicValue, Error};
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
use zerocopy::U32;
|
||||
|
||||
/// The magic value used to identify an attestation request
|
||||
///
|
||||
/// The magic value is ASCII:
|
||||
/// ```rust
|
||||
/// # use pv_core::attest::AttestationMagic;
|
||||
/// # use pv_core::request::MagicValue;
|
||||
/// # fn main() {
|
||||
/// # let magic = &
|
||||
/// [0u8; 8]
|
||||
/// # ;
|
||||
/// # assert!(AttestationMagic::starts_with_magic(magic));
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct AttestationMagic;
|
||||
impl MagicValue<8> for AttestationMagic {
|
||||
const MAGIC: [u8; 8] = [0; 8];
|
||||
}
|
||||
|
||||
/// Identifier for the used measurement algorithm
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum AttestationMeasAlg {
|
||||
/// Use HMAC with SHA512 as measurement algorithm
|
||||
HmacSha512 = 1,
|
||||
}
|
||||
|
||||
impl AttestationMeasAlg {
|
||||
/// Report the expected size for a given measurement algorithm
|
||||
pub const fn exp_size(&self) -> u32 {
|
||||
match self {
|
||||
AttestationMeasAlg::HmacSha512 => 64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: ByteOrder> TryFrom<U32<E>> for AttestationMeasAlg {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: U32<E>) -> Result<Self, Self::Error> {
|
||||
if value.get() == AttestationMeasAlg::HmacSha512 as u32 {
|
||||
Ok(Self::HmacSha512)
|
||||
} else {
|
||||
Err(Error::BinArcbInvAlgorithm(value.get()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AttestationMeasAlg> for U32<BigEndian> {
|
||||
fn from(value: AttestationMeasAlg) -> Self {
|
||||
(value as u32).into()
|
||||
}
|
||||
}
|
||||
@@ -24,11 +24,13 @@ mod ffi;
|
||||
mod info;
|
||||
mod test;
|
||||
pub(crate) use ffi::uv_ioctl;
|
||||
pub mod attest;
|
||||
pub mod secret;
|
||||
pub mod secret_list;
|
||||
|
||||
pub use info::UvDeviceInfo;
|
||||
#[allow(dead_code)] //TODO rm when pv learns attestation
|
||||
|
||||
/// User data for the attestation UVC
|
||||
pub type AttestationUserData = [u8; ffi::UVIO_ATT_USER_DATA_LEN];
|
||||
|
||||
///Configuration Unique Id of the Secure Execution guest
|
||||
|
||||
269
rust/pv_core/src/uvdevice/attest.rs
Normal file
269
rust/pv_core/src/uvdevice/attest.rs
Normal file
@@ -0,0 +1,269 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
use super::{ffi, AttestationUserData, ConfigUid, UvCmd};
|
||||
use crate::{Error, Result};
|
||||
use std::ptr;
|
||||
use zerocopy::{AsBytes, FromZeroes};
|
||||
|
||||
/// _Retrieve Attestation Measurement_ UVC
|
||||
///
|
||||
/// The Attestation Request has two input and three outputs.
|
||||
/// ARCB and User Data are inputs for the UV.
|
||||
/// Measurement, Additional Data, and the Configuration Unique ID are outputs generated by UV.
|
||||
///
|
||||
/// The Attestation Request Control Block (ARCB) is a cryptographically verified
|
||||
/// and secured request to UV and User Data is some plaintext data which is
|
||||
/// going to be included in the Attestation Measurement calculation.
|
||||
///
|
||||
/// Measurement is a cryptographic measurement of the callers properties,
|
||||
/// optional data configured by the ARCB and the user data. If specified by the
|
||||
/// ARCB, UV will add some Additional Data to the measurement calculation.
|
||||
/// This Additional Data is then returned as well.
|
||||
///
|
||||
/// If the Retrieve Attestation Measurement UV facility is not present,
|
||||
/// UV will return invalid command rc.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use pv_core::uv::UvDevice;
|
||||
/// # use pv_core::uv::AttestationCmd;
|
||||
/// # fn main() -> pv_core::Result<()> {
|
||||
/// let arcb = std::fs::read("arcb")?.into();
|
||||
/// let user_data = vec![0, 1, 2, 3];
|
||||
/// // Hard-coded example
|
||||
/// let mut cmd = AttestationCmd::new_request(arcb, Some(user_data), 64, 0)?;
|
||||
/// let uv = UvDevice::open()?;
|
||||
/// # uv.send_cmd(&mut cmd)?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct AttestationCmd {
|
||||
// all sizes are guaranteed to fit in the exchange format/UV-Call at any time
|
||||
// attestation data, these must not changed by this tooling, this is an invariant of this
|
||||
// struct, so that the raw pointer stay valid through the lifetime of this struct.
|
||||
// no mutable references are ever passed from this struct
|
||||
arcb: Box<[u8]>,
|
||||
measurement: Vec<u8>,
|
||||
additional: Option<Vec<u8>>,
|
||||
// raw IOCTL struct
|
||||
uvio_attest: ffi::uvio_attest,
|
||||
}
|
||||
|
||||
impl AttestationCmd {
|
||||
/// Maximum size for Additional-data
|
||||
pub const ADDITIONAL_MAX_SIZE: u32 = ffi::UVIO_ATT_ADDITIONAL_MAX_LEN as u32;
|
||||
/// Maximum size for Attestation Request Control Block
|
||||
pub const ARCB_MAX_SIZE: u32 = ffi::UVIO_ATT_ARCB_MAX_LEN as u32;
|
||||
/// Maximum size for Configuration UID
|
||||
pub const CUID_SIZE: u32 = ffi::UVIO_ATT_UID_LEN as u32;
|
||||
/// Maximum size for Measurement-data
|
||||
pub const MEASUREMENT_MAX_SIZE: u32 = ffi::UVIO_ATT_MEASUREMENT_MAX_LEN as u32;
|
||||
/// Maximum size for user-data
|
||||
pub const USER_MAX_SIZE: u32 = ffi::UVIO_ATT_USER_DATA_LEN as u32;
|
||||
|
||||
fn verify_size(size: u32, min_size: u32, max_size: u32, field: &'static str) -> Result<()> {
|
||||
if size < min_size {
|
||||
return Err(Error::AttDataSizeSmall { field, min_size });
|
||||
}
|
||||
if size > max_size {
|
||||
return Err(Error::AttDataSizeLarge { field, max_size });
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_slice(val: &[u8], max_size: u32, field: &'static str) -> Result<()> {
|
||||
if val.len() > max_size as usize {
|
||||
Err(Error::AttDataSizeLarge { field, max_size })
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new [`AttestationCmd`]
|
||||
///
|
||||
/// * `arcb` - binary attestation request
|
||||
/// * `user_data` - optional, up to 256 bytes of arbitrary data
|
||||
/// * `exp_measurement` - expected size the Attestation measurement requires
|
||||
/// * `exp_additional` - expected size of the additional data.
|
||||
///
|
||||
/// Creates a new Retrieve Attestation Measurement UVC.
|
||||
pub fn new_request(
|
||||
arcb: Box<[u8]>,
|
||||
user_data: Option<Vec<u8>>,
|
||||
exp_measurement: u32,
|
||||
exp_additional: u32,
|
||||
) -> Result<Self> {
|
||||
Self::verify_size(
|
||||
exp_measurement,
|
||||
1,
|
||||
AttestationCmd::MEASUREMENT_MAX_SIZE,
|
||||
"Expected measurement size",
|
||||
)?;
|
||||
Self::verify_size(
|
||||
exp_additional,
|
||||
0,
|
||||
AttestationCmd::ADDITIONAL_MAX_SIZE,
|
||||
"Expected additional data size",
|
||||
)?;
|
||||
Self::verify_slice(&arcb, AttestationCmd::ARCB_MAX_SIZE, "Attestation request")?;
|
||||
if let Some(ref data) = user_data {
|
||||
Self::verify_slice(data, AttestationCmd::USER_MAX_SIZE, "User data")?;
|
||||
}
|
||||
|
||||
let (user_len, user_data) = match user_data {
|
||||
// enforced by tests before invariants
|
||||
Some(user) => (Some(user.len() as u16), {
|
||||
let mut user_data = AttestationUserData::new_zeroed();
|
||||
user_data[0..user.len()].clone_from_slice(&user);
|
||||
Some(user_data)
|
||||
}),
|
||||
None => (None, None),
|
||||
};
|
||||
|
||||
let mut additional = match exp_additional {
|
||||
0 => None,
|
||||
size => Some(vec![0u8; size as usize]),
|
||||
};
|
||||
let mut measurement = vec![0u8; exp_measurement as usize];
|
||||
let uvio_attest = unsafe {
|
||||
ffi::uvio_attest::new(
|
||||
&arcb,
|
||||
&mut measurement,
|
||||
additional.as_deref_mut(),
|
||||
user_data,
|
||||
user_len,
|
||||
)
|
||||
};
|
||||
Ok(Self {
|
||||
arcb,
|
||||
measurement,
|
||||
additional,
|
||||
uvio_attest,
|
||||
})
|
||||
}
|
||||
|
||||
/// Provides the additional data calculated by UV after a successful UVC
|
||||
///
|
||||
/// Truncates the additional data to the correct length in place.
|
||||
/// If called before a successful attestation the data in this buffer is undefined.
|
||||
pub fn additional(&mut self) -> Option<&[u8]> {
|
||||
// truncate the add size to the UV reported size
|
||||
match &mut self.additional {
|
||||
Some(ref mut a) => a.truncate(self.uvio_attest.add_data_len as usize),
|
||||
None => (),
|
||||
}
|
||||
self.additional.as_deref()
|
||||
}
|
||||
|
||||
/// Copies the additional data calculated by UV after a successful UVC into a Vec
|
||||
///
|
||||
/// Truncates the additional data to the correct length.
|
||||
/// If called before a successful attestation the data in this buffer is undefined.
|
||||
pub fn additional_owned(&self) -> Option<Vec<u8>> {
|
||||
let mut additional = self.additional.clone()?;
|
||||
additional.truncate(self.uvio_attest.add_data_len as usize);
|
||||
Some(additional)
|
||||
}
|
||||
|
||||
/// Provides the Configuration Unique Identifier received from UV after a successful UVC
|
||||
///
|
||||
/// If called before a successful attestation the data in this buffer is undefined.
|
||||
pub fn cuid(&self) -> &ConfigUid {
|
||||
&self.uvio_attest.config_uid
|
||||
}
|
||||
|
||||
/// Provides the attestation measurement calculated by UV after a successful UVC
|
||||
///
|
||||
/// If called before a successful attestation the data in this buffer is undefined.
|
||||
pub fn measurement(&self) -> &[u8] {
|
||||
&self.measurement
|
||||
}
|
||||
|
||||
/// Returns a reference to the request of this [`AttestationCmd`].
|
||||
pub fn arcb(&self) -> &[u8] {
|
||||
self.arcb.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl UvCmd for AttestationCmd {
|
||||
const UV_IOCTL_NR: u8 = ffi::UVIO_IOCTL_ATT_NR;
|
||||
|
||||
fn rc_fmt(&self, rc: u16, _rrc: u16) -> Option<&'static str> {
|
||||
match rc {
|
||||
// should not happen, uvdevice local value
|
||||
0x0101 => Some("Invalid continuation token specified"),
|
||||
0x010a => Some("Unsupported plaintext attestation flag set"),
|
||||
0x010b => Some("Unsupported measurement algorithm specified."),
|
||||
0x010c => Some("Unable to decrypt attestation request control block. Probably no valid host-key was provided"),
|
||||
0x0106 => Some("Unsupported attestation request version"),
|
||||
// should not happen, protected by AttestationCmd constructors
|
||||
0x0102 => Some("User data length is greater than 256"),
|
||||
// should not happen, uvdevice ensures this
|
||||
0x0103 => Some("Access exception recognized when accessing the attestation request control block"),
|
||||
// should not happen, uvdevice ensures this
|
||||
0x0104 => Some("Access exception recognized when accessing the measurement data area"),
|
||||
// should not happen, uvdevice ensures this
|
||||
0x0105 => Some("Access exception recognized when accessing the additional data area"),
|
||||
// should not happen, ensured by Attestation Request builder
|
||||
0x0107 => Some("Invalid attestation request length for the specified attestation request version"),
|
||||
// 0 case should not happen, ensured by Attestation Request builder
|
||||
0x0108 => Some("Number of key slots is either equal to 0 or greater than the maximum number supported by the specified attestation request version"),
|
||||
// should not happen, ensured by Attestation Request builder
|
||||
0x0109 => Some("Size of encrypted area does not match measurement length plus any optional items"),
|
||||
// should not happen, ensured by Attestation Request builder
|
||||
0x010d => Some("Measurement data length is not large enough to store measurement"),
|
||||
// should not happen, ensured by Attestation Request builder
|
||||
0x010e => Some("Additional data length not large enough to hold all requested additional data"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn data(&mut self) -> Option<&mut [u8]> {
|
||||
Some(self.uvio_attest.as_bytes_mut())
|
||||
}
|
||||
}
|
||||
|
||||
fn opt_to_mut_ptr_u64(opt: &mut Option<&mut [u8]>) -> u64 {
|
||||
(match opt {
|
||||
Some(v) => v.as_mut_ptr(),
|
||||
None => ptr::null_mut(),
|
||||
}) as u64
|
||||
}
|
||||
|
||||
impl ffi::uvio_attest {
|
||||
/// Create a new attestation IOCTL control block
|
||||
///
|
||||
/// Happily converts slice lengths into u32/u16 without verifying.
|
||||
/// Therefore marked as unsafe.
|
||||
///
|
||||
/// # SAFETY
|
||||
/// It is safe to call this function iff:
|
||||
/// - arcb.len() < u32::MAX
|
||||
/// - additional.len() < u32::MAX
|
||||
/// - user_len() <= 256
|
||||
/// - pointyer fit into an u64
|
||||
unsafe fn new(
|
||||
arcb: &[u8],
|
||||
measurement: &mut [u8],
|
||||
mut additional: Option<&mut [u8]>,
|
||||
user: Option<AttestationUserData>,
|
||||
user_len: Option<u16>,
|
||||
) -> Self {
|
||||
Self {
|
||||
arcb_addr: arcb.as_ptr() as u64,
|
||||
meas_addr: measurement.as_ptr() as u64,
|
||||
add_data_addr: opt_to_mut_ptr_u64(&mut additional),
|
||||
user_data: user.unwrap_or([0; 256]),
|
||||
config_uid: [0; 16],
|
||||
arcb_len: arcb.len() as u32,
|
||||
meas_len: measurement.len() as u32,
|
||||
add_data_len: additional.unwrap_or_default().len() as u32,
|
||||
user_data_len: user_len.unwrap_or_default(),
|
||||
reserved136: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,13 +74,13 @@ pub const UVIO_ATT_UID_LEN: usize = 0x10;
|
||||
/// Measurement and Additional Data are outputs generated by UV.
|
||||
///
|
||||
/// The Attestation Request Control Block (ARCB) is a cryptographically verified
|
||||
/// and secured request to UV and User Data is some plaintext data which is
|
||||
/// and secured request to UV and user-data is some plaintext data which is
|
||||
/// going to be included in the Attestation Measurement calculation.
|
||||
///
|
||||
/// Measurement is a cryptographic measurement of the callers properties,
|
||||
/// optional data configured by the ARCB and the user data. If specified by the
|
||||
/// ARCB, UV will add some Additional Data to the measurement calculation.
|
||||
/// This Additional Data is then returned as well.
|
||||
/// ARCB, UV will add some additional-data to the measurement calculation.
|
||||
/// This additional-data is then returned as well.
|
||||
///
|
||||
/// If the Retrieve Attestation Measurement UV facility is not present,
|
||||
/// UV will return invalid command rc.
|
||||
@@ -101,13 +101,6 @@ pub struct uvio_attest {
|
||||
}
|
||||
assert_size!(uvio_attest, 0x138);
|
||||
|
||||
#[allow(dead_code)] //TODO rm when pv learns attestation
|
||||
impl uvio_attest {
|
||||
pub const ARCB_MAX_LEN: usize = UVIO_ATT_ARCB_MAX_LEN;
|
||||
pub const MEASUREMENT_MAX_LEN: usize = UVIO_ATT_MEASUREMENT_MAX_LEN;
|
||||
pub const ADDITIONAL_MAX_LEN: usize = UVIO_ATT_ADDITIONAL_MAX_LEN;
|
||||
}
|
||||
|
||||
/// corresponds to the UV_IOCTL macro
|
||||
pub(crate) const fn uv_ioctl(nr: u8) -> u64 {
|
||||
iowr(UVIO_TYPE_UVC, nr, std::mem::size_of::<uvio_ioctl_cb>())
|
||||
|
||||
Reference in New Issue
Block a user