rust/pv: Add new attestation additional-data flags

Add additional data for the Attestation request. The secret store hash
measures all added secrets and the state of the store (locked or not)
with a single hash. The hash is computed by concatenating all add-secret
request tags (16 bytes each) and a byte stating the locked state (1 for
locked, 0 for not locked). The firmware state is an opaque state
description of the systems firmware status to be interpreted by an IBM
service. Add request flags and fields in the additional data structure.
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Reviewed-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
This commit is contained in:
Steffen Eiden
2024-11-18 14:50:09 +01:00
parent 48f1e0da0a
commit 339ad5c2ed
2 changed files with 103 additions and 12 deletions

View File

@@ -14,8 +14,10 @@ use super::arcb::AttestationFlags;
/// Hash for additional-data stuff used for parsing [`AdditionalData`]
pub(super) const PHKH_SIZE: u32 = 0x20;
static_assert!(Keyslot::PHKH_SIZE == PHKH_SIZE);
pub(super) const SECRET_STORE_HASH_SIZE: u32 = 0x40;
pub(super) const FW_STATE_SIZE: u32 = 0x140;
/// Struct describing the additional-data of an Attestation Request
/// Additional-data of an Attestation Request
#[derive(Serialize, Debug)]
#[serde(default)]
pub struct AdditionalData<T>
@@ -27,6 +29,10 @@ where
#[serde(skip_serializing_if = "Option::is_none")]
attestation_phkh: Option<T>,
#[serde(skip_serializing_if = "Option::is_none")]
secret_store_hash: Option<T>,
#[serde(skip_serializing_if = "Option::is_none")]
firmware_state: Option<T>,
#[serde(skip_serializing_if = "Option::is_none")]
unrecognized: Option<T>,
}
@@ -51,6 +57,8 @@ where
}
write_field(f, "Image PHKH", &self.image_phkh)?;
write_field(f, "Attestation PHKH", &self.attestation_phkh)?;
write_field(f, "Secret store hash", &self.secret_store_hash)?;
write_field(f, "Firmware state", &self.firmware_state)?;
write_field(f, "Unrecognized", &self.unrecognized)?;
Ok(())
}
@@ -71,7 +79,7 @@ fn read_value<'a>(
}
impl<T: Serialize> AdditionalData<T> {
/// Provides a reference to the image public host key hash.
/// Provides a reference to the image public host key hash, if any.
///
/// This is the hash of the public host key of the corresponding private machine key that
/// decrypted the Secure Execution guest.
@@ -80,7 +88,7 @@ impl<T: Serialize> AdditionalData<T> {
self.image_phkh.as_ref()
}
/// Provides a reference to the attestation public host key hash.
/// Provides a reference to the attestation public host key hash, if any.
///
/// This is the hash of the public host key of the corresponding private machine key that
/// decrypted the Attestation request.
@@ -89,6 +97,24 @@ impl<T: Serialize> AdditionalData<T> {
self.attestation_phkh.as_ref()
}
/// Provides a reference to the secret store hash, if any.
///
/// This is a hash over the state of the guest's UV secret store. A SHA512 hash over the
/// concatenated request tags of all Add-secret requests and a 0 or 1 byte for the locked
/// state.
///
/// SHA512 (n*[Add-secret-request]|locked?)
pub fn secret_store_hash(&self) -> Option<&T> {
self.secret_store_hash.as_ref()
}
/// Provides a reference to the firmware state, if any.
///
/// This represents the state of selected firmware parts to be interpreted by an IBM service.
pub fn firmware_state(&self) -> Option<&T> {
self.firmware_state.as_ref()
}
/// Provides a reference to the data not known by this implementation.
pub fn unrecognized(&self) -> Option<&T> {
self.unrecognized.as_ref()
@@ -101,11 +127,15 @@ impl<'a, T: Serialize + From<&'a [u8]> + Sized> AdditionalData<T> {
let AdditionalData {
image_phkh,
attestation_phkh,
secret_store_hash,
firmware_state: firmware_hash,
unrecognized,
} = other;
Self {
image_phkh: image_phkh.map(|i| i.into()),
attestation_phkh: attestation_phkh.map(|i| i.into()),
secret_store_hash: secret_store_hash.map(|i| i.into()),
firmware_state: firmware_hash.map(|i| i.into()),
unrecognized: unrecognized.map(|i| i.into()),
}
}
@@ -125,11 +155,21 @@ impl<'a> AdditionalData<&'a [u8]> {
let (image_phkh, data) = read_value(data, PHKH_SIZE, flags.image_phkh(), "Image PHKH")?;
let (attestation_phkh, data) =
read_value(data, PHKH_SIZE, flags.attest_phkh(), "Attestation PHKH")?;
let (secret_store_hash, data) = read_value(
data,
SECRET_STORE_HASH_SIZE,
flags.secret_store_hash(),
"Secret store state",
)?;
let (firmware_state, data) =
read_value(data, FW_STATE_SIZE, flags.firmware_state(), "Firmware hash")?;
let unrecognized = (!data.is_empty()).then_some(data);
Ok(Self {
image_phkh,
attestation_phkh,
secret_store_hash,
firmware_state,
unrecognized,
})
}
@@ -145,7 +185,9 @@ mod test {
let add = AdditionalData {
image_phkh: 0_u8.into(),
attestation_phkh: 1_u8.into(),
unrecognized: 2_u8.into(),
secret_store_hash: 2_u8.into(),
firmware_state: 3_u8.into(),
unrecognized: 4_u8.into(),
};
serde_test::assert_ser_tokens(
@@ -153,7 +195,7 @@ mod test {
&[
Token::Struct {
name: "AdditionalData",
len: 3,
len: 5,
},
Token::Str("image_phkh"),
Token::Some,
@@ -161,19 +203,27 @@ mod test {
Token::Str("attestation_phkh"),
Token::Some,
Token::U8(1),
Token::Str("unrecognized"),
Token::Str("secret_store_hash"),
Token::Some,
Token::U8(2),
Token::Str("firmware_state"),
Token::Some,
Token::U8(3),
Token::Str("unrecognized"),
Token::Some,
Token::U8(4),
Token::StructEnd,
],
);
}
#[test]
fn ser_no_att() {
fn ser_no_miss() {
let add = AdditionalData {
image_phkh: 0_u8.into(),
attestation_phkh: None,
secret_store_hash: None,
firmware_state: None,
unrecognized: 2_u8.into(),
};
@@ -200,6 +250,8 @@ mod test {
let add = AdditionalData {
image_phkh: 0_u8.into(),
attestation_phkh: 1_u8.into(),
secret_store_hash: None,
firmware_state: None,
unrecognized: None,
};
@@ -225,6 +277,8 @@ mod test {
let add: AdditionalData<u8> = AdditionalData {
image_phkh: None,
attestation_phkh: None,
secret_store_hash: None,
firmware_state: None,
unrecognized: None,
};

View File

@@ -1,7 +1,11 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2024
use super::{additional::PHKH_SIZE, AttNonce};
use openssl::pkey::{PKey, Public};
use std::mem::size_of;
use zerocopy::{AsBytes, BigEndian, FromBytes, FromZeroes, U32};
use crate::{
assert_size,
attest::{AttestationMagic, AttestationMeasAlg},
@@ -13,9 +17,11 @@ use crate::{
uv::UvFlags,
Error, Result,
};
use openssl::pkey::{PKey, Public};
use std::mem::size_of;
use zerocopy::{AsBytes, BigEndian, FromBytes, FromZeroes, U32};
use super::{
additional::{FW_STATE_SIZE, PHKH_SIZE, SECRET_STORE_HASH_SIZE},
AttNonce,
};
#[cfg(doc)]
use crate::{
@@ -310,7 +316,14 @@ 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, PHKH_SIZE, PHKH_SIZE];
pub(crate) const FLAG_TO_ADD_SIZE: [u32; 6] = [
0,
0,
PHKH_SIZE,
PHKH_SIZE,
SECRET_STORE_HASH_SIZE,
FW_STATE_SIZE,
];
/// Returns the maximum size this flag requires for additional data
pub fn expected_additional_size(&self) -> u32 {
@@ -353,6 +366,30 @@ impl AttestationFlags {
pub fn attest_phkh(&self) -> bool {
self.0.is_set(3)
}
/// Flag 4 - request the state of the secret store
///
/// Asks the Ultravisor to provide the hash of the added secret requests. Requires 64 bytes.
pub fn set_secret_store_hash(&mut self) {
self.0.set_bit(4);
}
/// Check weather the hash of the added secret requests flag is on
pub fn secret_store_hash(&self) -> bool {
self.0.is_set(4)
}
/// Flag 5 - request the firmware hash
///
/// Asks the Ultravisor to provide the hash of the firmware. Requires 320 bytes.
pub fn set_firmware_state(&mut self) {
self.0.set_bit(5);
}
/// Check weather the hash of the added secret requests flag is on
pub fn firmware_state(&self) -> bool {
self.0.is_set(5)
}
}
#[repr(C)]