From 339ad5c2ed703fee83b0acca76bbfd6301a18b26 Mon Sep 17 00:00:00 2001 From: Steffen Eiden Date: Mon, 18 Nov 2024 14:50:09 +0100 Subject: [PATCH] rust/pv: Add new attestation additional-data flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Marc Hartmayer Signed-off-by: Steffen Eiden --- rust/pv/src/uvattest/additional.rs | 68 +++++++++++++++++++++++++++--- rust/pv/src/uvattest/arcb.rs | 47 ++++++++++++++++++--- 2 files changed, 103 insertions(+), 12 deletions(-) diff --git a/rust/pv/src/uvattest/additional.rs b/rust/pv/src/uvattest/additional.rs index cf663b07..2370a35b 100644 --- a/rust/pv/src/uvattest/additional.rs +++ b/rust/pv/src/uvattest/additional.rs @@ -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 @@ -27,6 +29,10 @@ where #[serde(skip_serializing_if = "Option::is_none")] attestation_phkh: Option, #[serde(skip_serializing_if = "Option::is_none")] + secret_store_hash: Option, + #[serde(skip_serializing_if = "Option::is_none")] + firmware_state: Option, + #[serde(skip_serializing_if = "Option::is_none")] unrecognized: Option, } @@ -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 AdditionalData { - /// 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 AdditionalData { 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 AdditionalData { 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 { 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 = AdditionalData { image_phkh: None, attestation_phkh: None, + secret_store_hash: None, + firmware_state: None, unrecognized: None, }; diff --git a/rust/pv/src/uvattest/arcb.rs b/rust/pv/src/uvattest/arcb.rs index 07dd3e89..1cb7cb90 100644 --- a/rust/pv/src/uvattest/arcb.rs +++ b/rust/pv/src/uvattest/arcb.rs @@ -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)]