mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
rust/pv: Attestation generation and verification support
Add functionality to generate Attestation Measurement requests. Add functionality to verify Attestation Measurement responses. Acked-by: Marc Hartmayer <mhartmay@linux.ibm.com> Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
This commit is contained in:
@@ -237,7 +237,6 @@ pub(crate) fn encrypt_aes_gcm(
|
|||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// This function will return an error if the data could not be encrypted by OpenSSL.
|
/// This function will return an error if the data could not be encrypted by OpenSSL.
|
||||||
#[allow(unused)]
|
|
||||||
pub(crate) fn decrypt_aes_gcm(
|
pub(crate) fn decrypt_aes_gcm(
|
||||||
key: &SymKey,
|
key: &SymKey,
|
||||||
iv: &[u8],
|
iv: &[u8],
|
||||||
@@ -272,7 +271,6 @@ pub(crate) fn hash(t: MessageDigest, data: &[u8]) -> Result<DigestBytes> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the HMAC of the given message.
|
/// Calculate the HMAC of the given message.
|
||||||
#[allow(unused)]
|
|
||||||
pub(crate) fn calculate_hmac(
|
pub(crate) fn calculate_hmac(
|
||||||
hmac_key: &PKeyRef<Private>,
|
hmac_key: &PKeyRef<Private>,
|
||||||
dgst: MessageDigest,
|
dgst: MessageDigest,
|
||||||
|
|||||||
@@ -85,6 +85,25 @@ pub enum Error {
|
|||||||
#[error("Invalid input size ({0}) for boot hdr")]
|
#[error("Invalid input size ({0}) for boot hdr")]
|
||||||
InvBootHdrSize(usize),
|
InvBootHdrSize(usize),
|
||||||
|
|
||||||
|
#[error("Input does not contain an attestation request")]
|
||||||
|
NoArcb,
|
||||||
|
|
||||||
|
#[error("The attestation request has an unknown version (.0)")]
|
||||||
|
BinArcbInvVersion(u32),
|
||||||
|
|
||||||
|
#[error(
|
||||||
|
"The attestation request encrypted sice is to0 small (.0). Request probably tampered with."
|
||||||
|
)]
|
||||||
|
BinArcbSeaSmall(u32),
|
||||||
|
|
||||||
|
#[error("The input is missing the Configuration UID entry. It is probably not an attestation response")]
|
||||||
|
AttExCuidMissing,
|
||||||
|
|
||||||
|
#[error(
|
||||||
|
"Attestation flags indicating that the additional data contains {0}, but no data was provided."
|
||||||
|
)]
|
||||||
|
AddDataMissing(&'static str),
|
||||||
|
|
||||||
// errors from other crates
|
// errors from other crates
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
PvCore(#[from] pv_core::Error),
|
PvCore(#[from] pv_core::Error),
|
||||||
|
|||||||
@@ -20,12 +20,27 @@
|
|||||||
//!
|
//!
|
||||||
//! ## Lock
|
//! ## Lock
|
||||||
//! [`uv::UvDevice`] and [`uv::LockCmd`]
|
//! [`uv::UvDevice`] and [`uv::LockCmd`]
|
||||||
|
//!
|
||||||
|
//! # Attestation
|
||||||
|
//!
|
||||||
|
//! This crate provides functionalities for creating, performing, and verifying Attestation
|
||||||
|
//! measurements for _IBM Secure Execution for Linux_. See:
|
||||||
|
//!
|
||||||
|
//! ## Create
|
||||||
|
//! [`attest::AttestationRequest`]
|
||||||
|
//!
|
||||||
|
//! ## Perform
|
||||||
|
//! [`uv::UvDevice`] and [`uv::AttestationCmd`]
|
||||||
|
//!
|
||||||
|
//! # Verify
|
||||||
|
//! [`attest::AttestationItems`], [`attest::AttestationMeasurement`]
|
||||||
mod brcb;
|
mod brcb;
|
||||||
mod confidential;
|
mod confidential;
|
||||||
mod crypto;
|
mod crypto;
|
||||||
mod error;
|
mod error;
|
||||||
mod req;
|
mod req;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
mod uvattest;
|
||||||
mod uvsecret;
|
mod uvsecret;
|
||||||
mod verify;
|
mod verify;
|
||||||
|
|
||||||
@@ -45,6 +60,17 @@ pub mod uv {
|
|||||||
pub use pv_core::uv::*;
|
pub use pv_core::uv::*;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Functionalities for creating attestation requests
|
||||||
|
pub mod attest {
|
||||||
|
pub use crate::uvattest::{
|
||||||
|
additional::AdditionalData,
|
||||||
|
arcb::{AttestationAuthenticated, AttestationRequest},
|
||||||
|
arcb::{AttestationFlags, AttestationVersion},
|
||||||
|
attest::{AttestationItems, AttestationMeasurement},
|
||||||
|
};
|
||||||
|
pub use pv_core::attest::*;
|
||||||
|
}
|
||||||
|
|
||||||
/// Miscellaneous functions and definitions
|
/// Miscellaneous functions and definitions
|
||||||
pub mod misc {
|
pub mod misc {
|
||||||
pub use crate::utils::read_certs;
|
pub use crate::utils::read_certs;
|
||||||
@@ -53,6 +79,8 @@ pub mod misc {
|
|||||||
|
|
||||||
pub use crate::error::HkdVerifyErrorType;
|
pub use crate::error::HkdVerifyErrorType;
|
||||||
pub use error::{Error, Result};
|
pub use error::{Error, Result};
|
||||||
|
pub use pv_core::Error as PvCoreError;
|
||||||
|
pub use pv_core::{FileAccessErrorType, FileIoErrorType};
|
||||||
|
|
||||||
/// Functionalities to build UV requests
|
/// Functionalities to build UV requests
|
||||||
pub mod request {
|
pub mod request {
|
||||||
|
|||||||
+30
-3
@@ -4,10 +4,11 @@
|
|||||||
|
|
||||||
use crate::assert_size;
|
use crate::assert_size;
|
||||||
use crate::crypto::{
|
use crate::crypto::{
|
||||||
derive_key, encrypt_aes_gcm, gen_ec_key, random_array, AesGcmResult, SymKey, SymKeyType,
|
decrypt_aes_gcm, derive_key, encrypt_aes_gcm, gen_ec_key, random_array, AesGcmResult, SymKey,
|
||||||
AES_256_GCM_TAG_SIZE,
|
SymKeyType, AES_256_GCM_TAG_SIZE,
|
||||||
};
|
};
|
||||||
use crate::misc::to_u32;
|
use crate::misc::to_u32;
|
||||||
|
use crate::request::Confidential;
|
||||||
use crate::{Error, Result};
|
use crate::{Error, Result};
|
||||||
use openssl::bn::{BigNum, BigNumContext};
|
use openssl::bn::{BigNum, BigNumContext};
|
||||||
use openssl::ec::{EcGroupRef, EcPointRef};
|
use openssl::ec::{EcGroupRef, EcPointRef};
|
||||||
@@ -256,6 +257,11 @@ impl ReqEncrCtx {
|
|||||||
pub(crate) fn encrypt_aead(&self, aad: &[u8], conf: &[u8]) -> Result<AesGcmResult> {
|
pub(crate) fn encrypt_aead(&self, aad: &[u8], conf: &[u8]) -> Result<AesGcmResult> {
|
||||||
encrypt_aes_gcm(&self.prot_key, &self.iv, aad, conf)
|
encrypt_aes_gcm(&self.prot_key, &self.iv, aad, conf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the request protection key of this [`ReqEncrCtx`].
|
||||||
|
pub fn prot_key(&self) -> &SymKey {
|
||||||
|
&self.prot_key
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@@ -375,7 +381,6 @@ pub trait Request {
|
|||||||
|
|
||||||
/// A struct to represent some parts of a binary/encrypted request.
|
/// A struct to represent some parts of a binary/encrypted request.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[allow(unused)]
|
|
||||||
#[allow(clippy::len_without_is_empty)]
|
#[allow(clippy::len_without_is_empty)]
|
||||||
pub struct BinReqValues<'a> {
|
pub struct BinReqValues<'a> {
|
||||||
iv: &'a [u8],
|
iv: &'a [u8],
|
||||||
@@ -432,6 +437,28 @@ impl<'a> BinReqValues<'a> {
|
|||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.len
|
self.len
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the size of the encrypted area
|
||||||
|
pub fn sea(&self) -> u32 {
|
||||||
|
self.encr.len() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decrypts the encrypted area with the provided key
|
||||||
|
pub fn decrypt(&self, key: &SymKey) -> Result<Confidential<Vec<u8>>> {
|
||||||
|
decrypt_aes_gcm(key, self.iv, self.aad, self.encr, self.tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the request dependent authenticated area of this [`BinReqValues`]
|
||||||
|
/// already interpreted.
|
||||||
|
///
|
||||||
|
/// If target struct is larger than the request dependend-aad None is returned. See
|
||||||
|
/// [`FromBytes::ref_from_prefix`]
|
||||||
|
pub fn req_dep_aad<T>(&self) -> Option<&T>
|
||||||
|
where
|
||||||
|
T: FromBytes + Sized,
|
||||||
|
{
|
||||||
|
T::ref_from_prefix(self.req_dep_aad)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
pub mod additional;
|
||||||
|
pub mod arcb;
|
||||||
|
pub mod attest;
|
||||||
|
|
||||||
|
type AttNonce = [u8; 16];
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
//
|
||||||
|
// Copyright IBM Corp. 2024
|
||||||
|
use super::arcb::AttestationFlags;
|
||||||
|
use crate::req::Keyslot;
|
||||||
|
use crate::static_assert;
|
||||||
|
use crate::{Error, Result};
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::fmt::Display;
|
||||||
|
use zerocopy::FromBytes;
|
||||||
|
|
||||||
|
/// Hash for additional-data stuff used for parsing [`AdditionalData`]
|
||||||
|
pub(crate) type AttAddHash = [u8; ATT_ADD_HASH_SIZE as usize];
|
||||||
|
pub(crate) const ATT_ADD_HASH_SIZE: u32 = 0x20;
|
||||||
|
static_assert!(Keyslot::PHKH_SIZE == ATT_ADD_HASH_SIZE);
|
||||||
|
|
||||||
|
/// Struct describing the additional-data of an Attestation Request
|
||||||
|
#[derive(Serialize, Debug)]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct AdditionalData<T>
|
||||||
|
where
|
||||||
|
T: Serialize,
|
||||||
|
{
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
image_phkh: Option<T>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
attestation_phkh: Option<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Display for AdditionalData<T>
|
||||||
|
where
|
||||||
|
T: Display + Serialize,
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
fn write_field<T: Display>(
|
||||||
|
f: &mut std::fmt::Formatter<'_>,
|
||||||
|
name: &'static str,
|
||||||
|
s: &Option<T>,
|
||||||
|
) -> std::fmt::Result {
|
||||||
|
if let Some(hash) = s {
|
||||||
|
writeln!(f, "{name}")?;
|
||||||
|
match f.alternate() {
|
||||||
|
true => writeln!(f, "{hash:#}")?,
|
||||||
|
false => writeln!(f, "{hash}")?,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
write_field(f, "Image PHKH", &self.image_phkh)?;
|
||||||
|
write_field(f, "Attestation PHKH", &self.attestation_phkh)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_hash<'a>(
|
||||||
|
data: &'a [u8],
|
||||||
|
read: bool,
|
||||||
|
name: &'static str,
|
||||||
|
) -> Result<(Option<&'a AttAddHash>, &'a [u8])> {
|
||||||
|
match read {
|
||||||
|
true => {
|
||||||
|
let (v, data) =
|
||||||
|
AttAddHash::slice_from_prefix(data, 1).ok_or(Error::AddDataMissing(name))?;
|
||||||
|
// slice from prefix ensures that there is 1 element.
|
||||||
|
Ok((Some(&v[0]), data))
|
||||||
|
}
|
||||||
|
false => Ok((None, data)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Serialize> AdditionalData<T> {
|
||||||
|
/// Provides a reference to the image public host key hash.
|
||||||
|
///
|
||||||
|
/// This is the hash of the public host key of the corresponding private machine key that
|
||||||
|
/// decrypted the Secure Execution guest.
|
||||||
|
/// Contains a value if that value was requested by the attestation request.
|
||||||
|
pub fn image_public_host_key_hash(&self) -> Option<&T> {
|
||||||
|
self.image_phkh.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provides a reference to the attestation public host key hash.
|
||||||
|
///
|
||||||
|
/// This is the hash of the public host key of the corresponding private machine key that
|
||||||
|
/// decrypted the Attestation request.
|
||||||
|
/// Contains a value if that value was requested by the attestation request.
|
||||||
|
pub fn attestation_public_host_key_hash(&self) -> Option<&T> {
|
||||||
|
self.attestation_phkh.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: Serialize + From<&'a [u8]> + Sized> AdditionalData<T> {
|
||||||
|
/// Create Additional data from the u8-slice variant
|
||||||
|
pub fn from_other(other: AdditionalData<&'a [u8]>) -> Self {
|
||||||
|
let AdditionalData {
|
||||||
|
image_phkh,
|
||||||
|
attestation_phkh,
|
||||||
|
} = other;
|
||||||
|
Self {
|
||||||
|
image_phkh: image_phkh.map(|i| i.into()),
|
||||||
|
attestation_phkh: attestation_phkh.map(|i| i.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> AdditionalData<&'a [u8]> {
|
||||||
|
/// Create from a slice of additional-data
|
||||||
|
///
|
||||||
|
/// `flags`: Flags indicating which additional-data field is present.
|
||||||
|
///
|
||||||
|
/// # Error
|
||||||
|
///
|
||||||
|
/// Fails if there is a mismatch between the data and the flags. Should not happen after a
|
||||||
|
/// successful attestation verification.
|
||||||
|
pub fn from_slice(data: &'a [u8], flags: &AttestationFlags) -> Result<Self> {
|
||||||
|
let _data = data;
|
||||||
|
let (image_phkh, _data) = read_hash(data, flags.image_phkh(), "Image PHKH")?;
|
||||||
|
let (attestation_phkh, _data) = read_hash(data, flags.attest_phkh(), "Attestation PHKH")?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
image_phkh: image_phkh.map(|v| v.as_slice()),
|
||||||
|
attestation_phkh: attestation_phkh.map(|v| v.as_slice()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,456 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
//
|
||||||
|
// Copyright IBM Corp. 2024
|
||||||
|
use super::{additional::ATT_ADD_HASH_SIZE, AttNonce};
|
||||||
|
use crate::{
|
||||||
|
assert_size,
|
||||||
|
attest::{AttestationMagic, AttestationMeasAlg},
|
||||||
|
crypto::random_array,
|
||||||
|
misc::Flags,
|
||||||
|
req::{Aad, BinReqValues, Keyslot, ReqEncrCtx},
|
||||||
|
request::{Confidential, MagicValue, Request, RequestVersion, SymKey, Zeroize},
|
||||||
|
static_assert,
|
||||||
|
uv::UvFlags,
|
||||||
|
Error, Result,
|
||||||
|
};
|
||||||
|
use openssl::pkey::{PKey, Public};
|
||||||
|
use std::mem::size_of;
|
||||||
|
use zerocopy::{AsBytes, BigEndian, FromBytes, FromZeroes, U32};
|
||||||
|
|
||||||
|
#[cfg(doc)]
|
||||||
|
use crate::{
|
||||||
|
request::SymKeyType,
|
||||||
|
uv::AttestationCmd,
|
||||||
|
verify::{CertVerifier, HkdVerifier},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Retrieve Attestation Request Control Block
|
||||||
|
///
|
||||||
|
/// An ARCB holds an Attestation Measurement key to attest a SE-guest.
|
||||||
|
/// The (architectural optional) nonce is always used and freshly generated for a new
|
||||||
|
/// [`AttestationRequest`].
|
||||||
|
///
|
||||||
|
/// Layout:
|
||||||
|
/// ```none
|
||||||
|
/// _______________________________________________________________
|
||||||
|
/// | generic header (48)
|
||||||
|
/// | --------------------------------------------------- |
|
||||||
|
/// | Plaintext Attestation flags (8) |
|
||||||
|
/// | Measurement Algorithm Identifier (4) |
|
||||||
|
/// | Reserved(4) |
|
||||||
|
/// | Customer Public Key (160) generated for each request |
|
||||||
|
/// | N Keyslots(80 each) |
|
||||||
|
/// | --------------------------------------------------- |
|
||||||
|
/// | Measurement key (64) | Encrypted
|
||||||
|
/// | Optional Nonce (0 or 16) | Encrypted
|
||||||
|
/// | --------------------------------------------------- |
|
||||||
|
/// | AES GCM Tag (16) |
|
||||||
|
/// |_____________________________________________________________|
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// Create an Attestation request with default flags (= use a nonce)
|
||||||
|
///
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// use pv::attest::{AttestationFlags, AttestationMeasAlg, AttestationRequest, AttestationVersion};
|
||||||
|
/// use pv::request::{SymKeyType, Request, ReqEncrCtx};
|
||||||
|
/// # fn main() -> pv::Result<()> {
|
||||||
|
/// let att_version = AttestationVersion::One;
|
||||||
|
/// let meas_alg = AttestationMeasAlg::HmacSha512;
|
||||||
|
/// let mut arcb = AttestationRequest::new(att_version, meas_alg, AttestationFlags::default())?;
|
||||||
|
/// // read-in hostkey document(s). Not verified for brevity.
|
||||||
|
/// let hkd = pv::misc::read_certs(&std::fs::read("host-key-document.crt")?)?;
|
||||||
|
/// // IBM issued HKD certificates typically have one X509
|
||||||
|
/// let hkd = hkd.first().unwrap().public_key()?;
|
||||||
|
/// arcb.add_hostkey(hkd);
|
||||||
|
/// // you can add multiple hostkeys
|
||||||
|
/// // arcb.add_hostkey(another_hkd);
|
||||||
|
/// // encrypt it
|
||||||
|
/// let ctx = ReqEncrCtx::random(SymKeyType::Aes256)?;
|
||||||
|
/// let arcb = arcb.encrypt(&ctx)?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
/// # See Also
|
||||||
|
///
|
||||||
|
/// * [`AttestationFlags`]
|
||||||
|
/// * [`AttestationMeasAlg`]
|
||||||
|
/// * [`AttestationVersion`]
|
||||||
|
/// * [`SymKeyType`]
|
||||||
|
/// * [`Request`]
|
||||||
|
/// * [`ReqEncrCtx`]
|
||||||
|
/// * [`AttestationCmd`]
|
||||||
|
/// * [`HkdVerifier`], [`CertVerifier`]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AttestationRequest {
|
||||||
|
version: AttestationVersion,
|
||||||
|
aad: AttestationAuthenticated,
|
||||||
|
keyslots: Vec<Keyslot>,
|
||||||
|
conf: Confidential<ReqConfData>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AttestationRequest {
|
||||||
|
/// Create a new retrieve attestation measurement request
|
||||||
|
pub fn new(
|
||||||
|
version: AttestationVersion,
|
||||||
|
mai: AttestationMeasAlg,
|
||||||
|
mut flags: AttestationFlags,
|
||||||
|
) -> Result<Self> {
|
||||||
|
// This implementation enforces using a nonce
|
||||||
|
flags.set_nonce();
|
||||||
|
Ok(Self {
|
||||||
|
version,
|
||||||
|
aad: AttestationAuthenticated::new(flags, mai),
|
||||||
|
keyslots: vec![],
|
||||||
|
conf: ReqConfData::random()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the flags of this [`AttestationRequest`].
|
||||||
|
pub fn flags(&self) -> &AttestationFlags {
|
||||||
|
&self.aad.flags
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a copy of the confidential data of this [`AttestationRequest`].
|
||||||
|
///
|
||||||
|
/// Gives a copy of the confidential data of this request for further
|
||||||
|
/// processing. This data should be never exposed in cleartext to anyone but
|
||||||
|
/// the creator and the verifier of this request.
|
||||||
|
pub fn confidential_data(&self) -> AttestationConfidential {
|
||||||
|
let conf = self.conf.value();
|
||||||
|
AttestationConfidential::new(conf.meas_key.to_vec(), conf.nonce.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn aad(&self, ctx: &ReqEncrCtx) -> Result<Vec<u8>> {
|
||||||
|
let cust_pub_key = ctx.key_coords()?;
|
||||||
|
let mut aad: Vec<Aad> = Vec::with_capacity(self.keyslots.len() + 2);
|
||||||
|
aad.push(Aad::Plain(self.aad.as_bytes()));
|
||||||
|
aad.push(Aad::Plain(cust_pub_key.as_ref()));
|
||||||
|
self.keyslots.iter().for_each(|k| aad.push(Aad::Ks(k)));
|
||||||
|
ctx.build_aad(
|
||||||
|
self.version.into(),
|
||||||
|
&aad,
|
||||||
|
size_of::<ReqConfData>(),
|
||||||
|
AttestationMagic::MAGIC,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decrypts the request and extracts the authenticated and confidential data
|
||||||
|
///
|
||||||
|
/// Deconstructs the `arcb` and decrypts it using `arpk`
|
||||||
|
///
|
||||||
|
/// # Error
|
||||||
|
///
|
||||||
|
/// Returns an error if the request is malformed or the decryption failed
|
||||||
|
pub fn decrypt_bin(
|
||||||
|
arcb: &[u8],
|
||||||
|
arpk: &SymKey,
|
||||||
|
) -> Result<(AttestationAuthenticated, AttestationConfidential)> {
|
||||||
|
if !AttestationMagic::starts_with_magic(arcb) {
|
||||||
|
return Err(Error::NoArcb);
|
||||||
|
}
|
||||||
|
|
||||||
|
let values = BinReqValues::get(arcb)?;
|
||||||
|
|
||||||
|
match values.version().try_into()? {
|
||||||
|
AttestationVersion::One => (),
|
||||||
|
};
|
||||||
|
let auth: &AttestationAuthenticated = values.req_dep_aad().ok_or(Error::BinRequestSmall)?;
|
||||||
|
|
||||||
|
let mai = auth.mai.try_into()?;
|
||||||
|
let keysize = match mai {
|
||||||
|
v @ AttestationMeasAlg::HmacSha512 => v.exp_size(),
|
||||||
|
} as usize;
|
||||||
|
|
||||||
|
if keysize > values.sea() as usize {
|
||||||
|
return Err(Error::BinArcbSeaSmall(values.sea()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let decr = values.decrypt(arpk)?;
|
||||||
|
|
||||||
|
// size sanitized by fence before
|
||||||
|
let meas_key = &decr.value()[..keysize];
|
||||||
|
let nonce = if decr.value().len() == size_of::<ReqConfData>() {
|
||||||
|
Some(
|
||||||
|
(&decr.value()[keysize..decr.value().len()])
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let conf = AttestationConfidential::new(meas_key.to_vec(), nonce);
|
||||||
|
|
||||||
|
Ok((auth.to_owned(), conf))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Confidential Data of an attestation request
|
||||||
|
///
|
||||||
|
/// contains a measurement key and an optional nonce
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AttestationConfidential {
|
||||||
|
measurement_key: Confidential<Vec<u8>>,
|
||||||
|
nonce: Option<Confidential<AttNonce>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AttestationConfidential {
|
||||||
|
/// Returns a reference to the measurement key of this [`AttestationConfidential`].
|
||||||
|
pub fn measurement_key(&self) -> &[u8] {
|
||||||
|
self.measurement_key.value()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the nonce of this [`AttestationConfidential`].
|
||||||
|
pub fn nonce(&self) -> &Option<Confidential<AttNonce>> {
|
||||||
|
&self.nonce
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new(measurement_key: Vec<u8>, nonce: Option<AttNonce>) -> Self {
|
||||||
|
Self {
|
||||||
|
measurement_key: measurement_key.into(),
|
||||||
|
nonce: nonce.map(Confidential::new),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Request for AttestationRequest {
|
||||||
|
fn encrypt(&self, ctx: &ReqEncrCtx) -> Result<Vec<u8>> {
|
||||||
|
let conf = self.conf.value().as_bytes();
|
||||||
|
let aad = self.aad(ctx)?;
|
||||||
|
ctx.encrypt_aead(&aad, conf).map(|res| res.data())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_hostkey(&mut self, hostkey: PKey<Public>) {
|
||||||
|
self.keyslots.push(Keyslot::new(hostkey))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Versions for [`AttestationRequest`]
|
||||||
|
#[repr(u32)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum AttestationVersion {
|
||||||
|
/// Version 1 (= 0x0100)
|
||||||
|
One = 0x0100,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<u32> for AttestationVersion {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(value: u32) -> Result<Self> {
|
||||||
|
if value == Self::One as u32 {
|
||||||
|
Ok(Self::One)
|
||||||
|
} else {
|
||||||
|
Err(Error::BinArcbInvVersion(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<AttestationVersion> for RequestVersion {
|
||||||
|
fn from(val: AttestationVersion) -> Self {
|
||||||
|
val as RequestVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Authenticated additional Data of an [`AttestationRequest`]
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, AsBytes, FromZeroes, FromBytes, Clone, Copy)]
|
||||||
|
pub struct AttestationAuthenticated {
|
||||||
|
flags: AttestationFlags,
|
||||||
|
mai: U32<BigEndian>,
|
||||||
|
res: u32,
|
||||||
|
}
|
||||||
|
assert_size!(AttestationAuthenticated, 0x10);
|
||||||
|
|
||||||
|
impl AttestationAuthenticated {
|
||||||
|
fn new(flags: AttestationFlags, mai: AttestationMeasAlg) -> Self {
|
||||||
|
Self {
|
||||||
|
flags,
|
||||||
|
mai: mai.into(),
|
||||||
|
res: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the flags of this [`AttestationAuthenticated`].
|
||||||
|
pub fn flags(&self) -> &AttestationFlags {
|
||||||
|
&self.flags
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the [`AttestationMeasAlg`] of this [`AttestationAuthenticated`].
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the library failed to set up the MAI correctly.
|
||||||
|
pub fn mai(&self) -> AttestationMeasAlg {
|
||||||
|
AttestationMeasAlg::try_from(self.mai).expect("ReqAuthData invariant hurt. Invalid MAI")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attestation flags
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Default, Debug, AsBytes, FromZeroes, FromBytes, Clone, Copy)]
|
||||||
|
pub struct AttestationFlags(UvFlags);
|
||||||
|
static_assert!(AttestationFlags::FLAG_TO_ADD_SIZE.len() < 64);
|
||||||
|
|
||||||
|
impl AttestationFlags {
|
||||||
|
/// Maps the flag to the (maximum) required size for the additional data
|
||||||
|
pub(crate) const FLAG_TO_ADD_SIZE: [u32; 4] = [0, 0, ATT_ADD_HASH_SIZE, ATT_ADD_HASH_SIZE];
|
||||||
|
|
||||||
|
/// Returns the maximum size this flag requires for additional data
|
||||||
|
pub fn expected_additional_size(&self) -> u32 {
|
||||||
|
Self::FLAG_TO_ADD_SIZE
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.fold(0, |size, (b, s)| size + self.0.is_set(b as u8) as u32 * s)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flag 1 - use a nonce
|
||||||
|
///
|
||||||
|
/// This attestation implementation forces the use of a nonce, so this will always be on and
|
||||||
|
/// the function is non-public
|
||||||
|
fn set_nonce(&mut self) {
|
||||||
|
self.0.set_bit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flag 2 - request the image public host-key hash
|
||||||
|
///
|
||||||
|
/// Asks the Ultravisor to provide the host-key hash that unpacked the SE-image to be added in
|
||||||
|
/// additional data. Requires 32 bytes.
|
||||||
|
pub fn set_image_phkh(&mut self) {
|
||||||
|
self.0.set_bit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check weather the image public host key hash flag is on
|
||||||
|
pub fn image_phkh(&self) -> bool {
|
||||||
|
self.0.is_set(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flag 3 - request the attestation public host-key hash
|
||||||
|
///
|
||||||
|
/// Asks the Ultravisor to provide the host-key hash that unpacked the attestation request to
|
||||||
|
/// be added in additional data. Requires 32 bytes.
|
||||||
|
pub fn set_attest_phkh(&mut self) {
|
||||||
|
self.0.set_bit(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check weather the attestation public host key hash flag is on
|
||||||
|
pub fn attest_phkh(&self) -> bool {
|
||||||
|
self.0.is_set(3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, AsBytes)]
|
||||||
|
struct ReqConfData {
|
||||||
|
meas_key: [u8; 64],
|
||||||
|
nonce: AttNonce,
|
||||||
|
}
|
||||||
|
assert_size!(ReqConfData, 80);
|
||||||
|
|
||||||
|
impl ReqConfData {
|
||||||
|
fn random() -> Result<Confidential<Self>> {
|
||||||
|
Ok(Confidential::new(Self {
|
||||||
|
meas_key: random_array()?,
|
||||||
|
nonce: random_array()?,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Zeroize for ReqConfData {
|
||||||
|
fn zeroize(&mut self) {
|
||||||
|
self.meas_key.zeroize();
|
||||||
|
self.nonce.zeroize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use crate::{get_test_asset, request::SymKey, test_utils::get_test_keys};
|
||||||
|
|
||||||
|
const ARPK: [u8; 32] = [0x17; 32];
|
||||||
|
const NONCE: [u8; 16] = [0xab; 16];
|
||||||
|
const MEAS: [u8; 64] = [0x77; 64];
|
||||||
|
|
||||||
|
fn mk_arcb() -> Vec<u8> {
|
||||||
|
let (cust_key, host_key) = get_test_keys();
|
||||||
|
let ctx = ReqEncrCtx::new_aes_256(
|
||||||
|
Some([0x55; 12]),
|
||||||
|
Some(cust_key),
|
||||||
|
Some(SymKey::Aes256(ARPK.into())),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut flags = AttestationFlags::default();
|
||||||
|
flags.set_image_phkh();
|
||||||
|
flags.set_attest_phkh();
|
||||||
|
|
||||||
|
let mut arcb = AttestationRequest::new(
|
||||||
|
AttestationVersion::One,
|
||||||
|
AttestationMeasAlg::HmacSha512,
|
||||||
|
flags,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// manually set confidential data (API does not allow this)
|
||||||
|
arcb.conf.value_mut().nonce = NONCE;
|
||||||
|
arcb.conf.value_mut().meas_key = MEAS;
|
||||||
|
|
||||||
|
arcb.add_hostkey(host_key);
|
||||||
|
arcb.encrypt(&ctx).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn arcb() {
|
||||||
|
let request = mk_arcb();
|
||||||
|
let exp = get_test_asset!("exp/arcb.bin");
|
||||||
|
|
||||||
|
assert_eq!(request, exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn decrypt_bin() {
|
||||||
|
let request = mk_arcb();
|
||||||
|
let arpk = SymKey::Aes256(ARPK.into());
|
||||||
|
let (_, conf) = AttestationRequest::decrypt_bin(&request, &arpk).unwrap();
|
||||||
|
assert_eq!(conf.measurement_key(), &MEAS);
|
||||||
|
assert_eq!(conf.nonce().as_ref().unwrap().value(), &NONCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn decrypt_bin_fail_magic() {
|
||||||
|
let arpk = SymKey::Aes256(ARPK.into());
|
||||||
|
let mut tamp_arcb = mk_arcb();
|
||||||
|
|
||||||
|
// tamper magic
|
||||||
|
tamp_arcb[0] = 17;
|
||||||
|
let ret = AttestationRequest::decrypt_bin(&tamp_arcb, &arpk);
|
||||||
|
assert!(matches!(ret, Err(Error::NoArcb)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn decrypt_bin_fail_mai() {
|
||||||
|
let arpk = SymKey::Aes256(ARPK.into());
|
||||||
|
let mut tamp_arcb = mk_arcb();
|
||||||
|
|
||||||
|
// tamper MAI
|
||||||
|
tamp_arcb[0x3b] = 17;
|
||||||
|
let ret = AttestationRequest::decrypt_bin(&tamp_arcb, &arpk);
|
||||||
|
println!("{ret:?}");
|
||||||
|
assert!(matches!(
|
||||||
|
ret,
|
||||||
|
Err(Error::PvCore(pv_core::Error::BinArcbInvAlgorithm(17)))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn decrypt_bin_fail_aad() {
|
||||||
|
let arpk = SymKey::Aes256(ARPK.into());
|
||||||
|
let mut tamp_arcb = mk_arcb();
|
||||||
|
|
||||||
|
// tamper AAD
|
||||||
|
tamp_arcb[0x3c] = 17;
|
||||||
|
let ret = AttestationRequest::decrypt_bin(&tamp_arcb, &arpk);
|
||||||
|
assert!(matches!(ret, Err(Error::GcmTagMismatch)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,245 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
//
|
||||||
|
// Copyright IBM Corp. 2024
|
||||||
|
|
||||||
|
use super::AttNonce;
|
||||||
|
use crate::{
|
||||||
|
attest::AttestationMeasAlg, brcb::BootHdrTags, crypto::calculate_hmac, request::Confidential,
|
||||||
|
uv::ConfigUid, Result,
|
||||||
|
};
|
||||||
|
use openssl::{
|
||||||
|
hash::MessageDigest,
|
||||||
|
pkey::{PKeyRef, Private},
|
||||||
|
};
|
||||||
|
use std::mem::size_of;
|
||||||
|
use zerocopy::{AsBytes, BigEndian, U16, U32};
|
||||||
|
|
||||||
|
#[cfg(doc)]
|
||||||
|
use crate::attest::AttestationRequest;
|
||||||
|
|
||||||
|
/// Holds the data to be measured.
|
||||||
|
///
|
||||||
|
/// The Attestation measurement is an authentication code of the following data:
|
||||||
|
///
|
||||||
|
/// ```none
|
||||||
|
/// |-------------------------------|
|
||||||
|
/// | From SE-header: |
|
||||||
|
/// | Page List Digest (64) |
|
||||||
|
/// | Address List Digest (64) |
|
||||||
|
/// | Tweak List Digest (64) |
|
||||||
|
/// | SE Header Tag (16) |
|
||||||
|
/// | Configuration Unique Id (16) |
|
||||||
|
/// | user-data length (2) |
|
||||||
|
/// | zeros (2) |
|
||||||
|
/// | additional data length (4) |
|
||||||
|
/// | user-data (0-256) |
|
||||||
|
/// | optional nonce (0 or 16) |
|
||||||
|
/// | additional data (0+) |
|
||||||
|
/// |-------------------------------|
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AttestationItems(Confidential<Vec<u8>>);
|
||||||
|
|
||||||
|
// tags: BootHdrTags,
|
||||||
|
// cuid: ConfigUid,
|
||||||
|
// user_data_len: U16<BigEndian>,
|
||||||
|
// res: u16,
|
||||||
|
// additional_len: U32<BigEndian>,
|
||||||
|
// user_data: Vec<u8>,
|
||||||
|
// nonce: Option<[u8; 16]>,
|
||||||
|
// additional: Vec<u8>,
|
||||||
|
impl AttestationItems {
|
||||||
|
/// Create a new attestation item struct.
|
||||||
|
///
|
||||||
|
/// * `tags`: The tags from the SE header
|
||||||
|
/// * `cuid`: The Configuration Unique Id from the SE guest for which the Measurement was
|
||||||
|
/// calculated
|
||||||
|
/// * `user`: up to 256 bytes of arbitrary data generated on the SE-guest before measuring
|
||||||
|
/// * `nonce`: technically optional nonce, but [`AttestationRequest`] enforces it
|
||||||
|
/// * `additional`: additional data generated by the Firmware depending on the Attestation flags
|
||||||
|
///
|
||||||
|
/// If size values of `user` or `additional` are longer than 16/32 bit they are silently
|
||||||
|
/// truncated. `user-data` is limited to 256 bytes architecture wise, and additional data is
|
||||||
|
/// limited to 8 pages by the uvdevice. Larger sizes will produce invalid measurements
|
||||||
|
/// anyhow.
|
||||||
|
pub fn new(
|
||||||
|
tags: &BootHdrTags,
|
||||||
|
cuid: &ConfigUid,
|
||||||
|
user: Option<&[u8]>,
|
||||||
|
nonce: Option<&AttNonce>,
|
||||||
|
additional: Option<&[u8]>,
|
||||||
|
) -> Self {
|
||||||
|
// expectations are ensured by ExchangeCtx invariants
|
||||||
|
let user = user.unwrap_or(&[]);
|
||||||
|
let user_data_len: U16<BigEndian> = (user.len() as u16).into();
|
||||||
|
|
||||||
|
let additional = additional.unwrap_or(&[]);
|
||||||
|
let additional_len: U32<BigEndian> = (additional.len() as u32).into();
|
||||||
|
|
||||||
|
let size = size_of::<BootHdrTags>() // PLD ALD TLD TAG
|
||||||
|
+ size_of::<ConfigUid>()
|
||||||
|
+ size_of::<u16>() // user_len
|
||||||
|
+ size_of::<u16>() // reserved
|
||||||
|
+ size_of::<u32>() // additional_len
|
||||||
|
+ user.len()
|
||||||
|
+ match nonce {
|
||||||
|
Some(_) => size_of::<AttNonce>(),
|
||||||
|
None => 0,
|
||||||
|
}
|
||||||
|
+ additional.len();
|
||||||
|
|
||||||
|
let mut items = Vec::with_capacity(size);
|
||||||
|
items.extend_from_slice(tags.as_bytes());
|
||||||
|
items.extend_from_slice(cuid.as_bytes());
|
||||||
|
items.extend_from_slice(user_data_len.as_bytes());
|
||||||
|
items.extend_from_slice(&[0, 0]);
|
||||||
|
items.extend_from_slice(additional_len.as_bytes());
|
||||||
|
items.extend_from_slice(user);
|
||||||
|
if let Some(nonce) = nonce {
|
||||||
|
items.extend_from_slice(nonce);
|
||||||
|
}
|
||||||
|
items.extend_from_slice(additional);
|
||||||
|
assert!(items.len() == size);
|
||||||
|
Self(items.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Holds an attestation measurement
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[allow(clippy::len_without_is_empty)]
|
||||||
|
pub struct AttestationMeasurement(Vec<u8>);
|
||||||
|
impl AttestationMeasurement {
|
||||||
|
/// Calculate an attestation measurement
|
||||||
|
pub fn calculate(
|
||||||
|
items: AttestationItems,
|
||||||
|
mai: AttestationMeasAlg,
|
||||||
|
meas_key: &PKeyRef<Private>,
|
||||||
|
) -> Result<Self> {
|
||||||
|
match mai {
|
||||||
|
AttestationMeasAlg::HmacSha512 => {
|
||||||
|
calculate_hmac(meas_key, MessageDigest::sha512(), items.0.value()).map(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the length of the [`AttestationMeasurement`].
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Securely compares the calculated measurement with a given one
|
||||||
|
///
|
||||||
|
/// Exists early when sizes do not match
|
||||||
|
pub fn eq_secure(&self, other: &[u8]) -> bool {
|
||||||
|
if self.len() != other.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
openssl::memcmp::eq(&self.0, other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<[u8]> for AttestationMeasurement {
|
||||||
|
fn as_ref(&self) -> &[u8] {
|
||||||
|
self.0.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<Vec<u8>> for AttestationMeasurement {
|
||||||
|
fn from(value: Vec<u8>) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use openssl::pkey::PKey;
|
||||||
|
|
||||||
|
const M_KEY: [u8; 64] = [0x41; 64];
|
||||||
|
const BOOT_HDR_TAGS: BootHdrTags = BootHdrTags::new([1; 64], [2; 64], [3; 64], [4; 16]);
|
||||||
|
const CUID: [u8; 16] = [5; 16];
|
||||||
|
const USER: [u8; 256] = [7; 256];
|
||||||
|
const NONCE: [u8; 16] = [8; 16];
|
||||||
|
const ADDITIONAL: [u8; 128] = [9; 128];
|
||||||
|
|
||||||
|
// just for better output in case of a test failure
|
||||||
|
impl PartialEq<[u8]> for AttestationMeasurement {
|
||||||
|
fn eq(&self, other: &[u8]) -> bool {
|
||||||
|
self.eq_secure(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn measurement_all() {
|
||||||
|
const EXP_HMAC: [u8; 64] = [
|
||||||
|
0x88, 0x79, 0x4c, 0x62, 0xcc, 0xe7, 0xbc, 0xf2, 0x62, 0x16, 0xde, 0xb3, 0xf4, 0x8f,
|
||||||
|
0x13, 0xfe, 0xa6, 0x37, 0x4b, 0x6d, 0x7e, 0x35, 0xbc, 0xc5, 0xc2, 0xce, 0x68, 0x12,
|
||||||
|
0x1d, 0xb6, 0xf4, 0x5d, 0xfc, 0x8c, 0x17, 0x18, 0x56, 0x46, 0x35, 0x49, 0x40, 0x8b,
|
||||||
|
0xf8, 0xe7, 0xd1, 0xac, 0xa1, 0x1e, 0xfa, 0xd0, 0xa8, 0x78, 0xaf, 0x97, 0xdc, 0x9e,
|
||||||
|
0x21, 0xa1, 0xfc, 0x2a, 0x32, 0xf3, 0xa6, 0x75,
|
||||||
|
];
|
||||||
|
let items = AttestationItems::new(
|
||||||
|
&BOOT_HDR_TAGS,
|
||||||
|
&CUID,
|
||||||
|
Some(&USER),
|
||||||
|
Some(&NONCE),
|
||||||
|
Some(&ADDITIONAL),
|
||||||
|
);
|
||||||
|
let key = PKey::hmac(&M_KEY).unwrap();
|
||||||
|
let meas =
|
||||||
|
AttestationMeasurement::calculate(items, AttestationMeasAlg::HmacSha512, &key).unwrap();
|
||||||
|
assert_eq!(meas, EXP_HMAC[..]);
|
||||||
|
assert!(meas.eq_secure(&EXP_HMAC[..]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn measurement_user_add() {
|
||||||
|
const EXP_HMAC: [u8; 64] = [
|
||||||
|
0xfb, 0xd4, 0xf7, 0x38, 0xa3, 0x90, 0xed, 0xd9, 0x47, 0xcd, 0x4f, 0x11, 0xaf, 0x3a,
|
||||||
|
0x2f, 0x3b, 0xab, 0x2f, 0xdf, 0x8b, 0xf8, 0x9b, 0xf8, 0x1b, 0xeb, 0x49, 0x51, 0x17,
|
||||||
|
0xf4, 0x38, 0x2c, 0xf4, 0x2f, 0x07, 0x30, 0xc8, 0xc7, 0xd9, 0xe3, 0xca, 0x27, 0xfb,
|
||||||
|
0x25, 0xad, 0xfc, 0xeb, 0x21, 0x22, 0x4f, 0x57, 0xfd, 0xb3, 0x98, 0xdc, 0xf4, 0x1a,
|
||||||
|
0x83, 0xc1, 0x46, 0xe6, 0xa2, 0x3d, 0xb7, 0x60,
|
||||||
|
];
|
||||||
|
let items =
|
||||||
|
AttestationItems::new(&BOOT_HDR_TAGS, &CUID, Some(&USER), None, Some(&ADDITIONAL));
|
||||||
|
let key = PKey::hmac(&M_KEY).unwrap();
|
||||||
|
let meas =
|
||||||
|
AttestationMeasurement::calculate(items, AttestationMeasAlg::HmacSha512, &key).unwrap();
|
||||||
|
assert_eq!(meas, EXP_HMAC[..]);
|
||||||
|
assert!(meas.eq_secure(&EXP_HMAC[..]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn measurement_add() {
|
||||||
|
const EXP_HMAC: [u8; 64] = [
|
||||||
|
0x63, 0x67, 0x1f, 0xbf, 0x29, 0x50, 0x36, 0xeb, 0x10, 0x23, 0xea, 0x71, 0xf7, 0x18,
|
||||||
|
0x2e, 0x7d, 0x63, 0x43, 0xdc, 0x7b, 0x2d, 0xa5, 0x84, 0xe8, 0x24, 0xd0, 0xa7, 0xd1,
|
||||||
|
0x98, 0xab, 0x9c, 0xde, 0xd7, 0x56, 0xc9, 0x3b, 0x39, 0x05, 0x0f, 0xfb, 0x76, 0x45,
|
||||||
|
0x55, 0xb0, 0x1f, 0x88, 0xcb, 0x82, 0x01, 0x7a, 0x6a, 0x15, 0xc7, 0xe0, 0xba, 0xfc,
|
||||||
|
0x60, 0x05, 0xf1, 0xe4, 0xf7, 0x8a, 0xa1, 0x24,
|
||||||
|
];
|
||||||
|
let items = AttestationItems::new(&BOOT_HDR_TAGS, &CUID, None, None, Some(&ADDITIONAL));
|
||||||
|
let key = PKey::hmac(&M_KEY).unwrap();
|
||||||
|
let meas =
|
||||||
|
AttestationMeasurement::calculate(items, AttestationMeasAlg::HmacSha512, &key).unwrap();
|
||||||
|
assert_eq!(meas, EXP_HMAC[..]);
|
||||||
|
assert!(meas.eq_secure(&EXP_HMAC[..]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn measurement_minimal() {
|
||||||
|
const EXP_HMAC: [u8; 64] = [
|
||||||
|
0xc5, 0xc3, 0x4c, 0x93, 0x83, 0x5d, 0x1e, 0xc2, 0x3f, 0x5c, 0x2d, 0x77, 0x8d, 0xfa,
|
||||||
|
0x20, 0x12, 0x9b, 0x11, 0xb3, 0x05, 0x60, 0x17, 0x42, 0xcb, 0x2f, 0x38, 0xe0, 0xed,
|
||||||
|
0x98, 0x94, 0xdc, 0xdb, 0x73, 0xfc, 0x86, 0x95, 0xab, 0x6a, 0x8d, 0xba, 0xd0, 0x74,
|
||||||
|
0x40, 0x73, 0xdd, 0xc8, 0x1a, 0x5e, 0xaa, 0xfa, 0x52, 0xe4, 0xa1, 0x5a, 0xf8, 0xde,
|
||||||
|
0xb8, 0xd7, 0x61, 0x09, 0x19, 0x22, 0x84, 0x7f,
|
||||||
|
];
|
||||||
|
let items = AttestationItems::new(&BOOT_HDR_TAGS, &CUID, None, None, None);
|
||||||
|
let key = PKey::hmac(&M_KEY).unwrap();
|
||||||
|
let meas =
|
||||||
|
AttestationMeasurement::calculate(items, AttestationMeasAlg::HmacSha512, &key).unwrap();
|
||||||
|
assert_eq!(meas, EXP_HMAC[..]);
|
||||||
|
assert!(meas.eq_secure(&EXP_HMAC[..]));
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
@@ -9,17 +9,17 @@ use zerocopy::{AsBytes, FromZeroes};
|
|||||||
/// _Retrieve Attestation Measurement_ UVC
|
/// _Retrieve Attestation Measurement_ UVC
|
||||||
///
|
///
|
||||||
/// The Attestation Request has two input and three outputs.
|
/// The Attestation Request has two input and three outputs.
|
||||||
/// ARCB and User Data are inputs for the UV.
|
/// ARCB and user-data are inputs for the UV.
|
||||||
/// Measurement, Additional Data, and the Configuration Unique ID are outputs generated by UV.
|
/// Measurement, additional data, and the Configuration Unique ID are outputs generated by UV.
|
||||||
///
|
///
|
||||||
/// The Attestation Request Control Block (ARCB) is a cryptographically verified
|
/// 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.
|
/// going to be included in the Attestation Measurement calculation.
|
||||||
///
|
///
|
||||||
/// Measurement is a cryptographic measurement of the callers properties,
|
/// Measurement is a cryptographic measurement of the callers properties,
|
||||||
/// optional data configured by the ARCB and the user data. If specified by the
|
/// 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.
|
/// ARCB, UV will add some additional Data to the measurement calculation.
|
||||||
/// This Additional Data is then returned as well.
|
/// This additional data is then returned as well.
|
||||||
///
|
///
|
||||||
/// If the Retrieve Attestation Measurement UV facility is not present,
|
/// If the Retrieve Attestation Measurement UV facility is not present,
|
||||||
/// UV will return invalid command rc.
|
/// UV will return invalid command rc.
|
||||||
|
|||||||
@@ -70,8 +70,8 @@ pub const UVIO_ATT_UID_LEN: usize = 0x10;
|
|||||||
/// Request Attestation Measurement control block
|
/// Request Attestation Measurement control block
|
||||||
///
|
///
|
||||||
/// The Attestation Request has two input and two outputs.
|
/// The Attestation Request has two input and two outputs.
|
||||||
/// ARCB and User Data are inputs for the UV.
|
/// ARCB and user-data are inputs for the UV.
|
||||||
/// Measurement and Additional Data are outputs generated by UV.
|
/// Measurement and additional-data are outputs generated by UV.
|
||||||
///
|
///
|
||||||
/// The Attestation Request Control Block (ARCB) is a cryptographically verified
|
/// 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
|
||||||
|
|||||||
Reference in New Issue
Block a user