mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
pvattest: Use hybrid keys
Allow the creation of Attestation requests using hybrid (=quantum safe) keys. This results in using the headers in version 2 (0x200). Co-developed-by: Marc Hartmayer <marc@linux.ibm.com> Signed-off-by: Marc Hartmayer <marc@linux.ibm.com> Signed-off-by: Timo Keller <tkeller@linux.ibm.com> Reviewed-by: Steffen Eiden <seiden@linux.ibm.com> Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
This commit is contained in:
committed by
Steffen Eiden
parent
fc853f3259
commit
9dca2d3181
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
|
||||||
|
use pv_core::static_assert;
|
||||||
use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, KnownLayout, U32};
|
use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, KnownLayout, U32};
|
||||||
|
|
||||||
use super::additional::{FW_STATE_SIZE, PHKH_SIZE, SECRET_STORE_HASH_SIZE};
|
use super::additional::{FW_STATE_SIZE, PHKH_SIZE, SECRET_STORE_HASH_SIZE};
|
||||||
@@ -14,7 +15,7 @@ use crate::misc::Flags;
|
|||||||
use crate::req::{Aad, BinReqValues, HostKey, Keyslot, ReqEncrCtx};
|
use crate::req::{Aad, BinReqValues, HostKey, Keyslot, ReqEncrCtx};
|
||||||
use crate::request::{Confidential, MagicValue, Request, RequestVersion, SymKey, Zeroize};
|
use crate::request::{Confidential, MagicValue, Request, RequestVersion, SymKey, Zeroize};
|
||||||
use crate::uv::UvFlags;
|
use crate::uv::UvFlags;
|
||||||
use crate::{assert_size, static_assert, Error, Result};
|
use crate::{assert_size, Error, Result};
|
||||||
#[cfg(doc)]
|
#[cfg(doc)]
|
||||||
use crate::{
|
use crate::{
|
||||||
request::SymKeyType,
|
request::SymKeyType,
|
||||||
@@ -62,7 +63,7 @@ use crate::{
|
|||||||
/// let hkd = hkd.first().unwrap().public_key()?;
|
/// let hkd = hkd.first().unwrap().public_key()?;
|
||||||
/// arcb.add_hostkey(HostKey::V1(hkd));
|
/// arcb.add_hostkey(HostKey::V1(hkd));
|
||||||
/// // you can add multiple hostkeys
|
/// // you can add multiple hostkeys
|
||||||
/// // arcb.add_hostkey(another_hkd);
|
/// // arcb.add_hostkey(HostKey::V1(another_hkd));
|
||||||
/// // encrypt it
|
/// // encrypt it
|
||||||
/// let ctx = ReqEncrCtx::random(SymKeyType::Aes256Gcm)?;
|
/// let ctx = ReqEncrCtx::random(SymKeyType::Aes256Gcm)?;
|
||||||
/// let arcb = arcb.encrypt(&ctx)?;
|
/// let arcb = arcb.encrypt(&ctx)?;
|
||||||
@@ -106,7 +107,13 @@ impl AttestationRequest {
|
|||||||
|
|
||||||
/// Returns a reference to the flags of this [`AttestationRequest`].
|
/// Returns a reference to the flags of this [`AttestationRequest`].
|
||||||
pub fn flags(&self) -> &AttestationFlags {
|
pub fn flags(&self) -> &AttestationFlags {
|
||||||
&self.aad.flags
|
self.aad.flags()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the request version, derived from the type of added host-keys.
|
||||||
|
/// Returns [`AttestationVersion::One`] if no host-keys have been added yet.
|
||||||
|
pub fn version(&self) -> AttestationVersion {
|
||||||
|
self.version
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a copy of the confidential data of this [`AttestationRequest`].
|
/// Returns a copy of the confidential data of this [`AttestationRequest`].
|
||||||
@@ -142,6 +149,7 @@ impl AttestationRequest {
|
|||||||
let values = BinReqValues::get(arcb)?;
|
let values = BinReqValues::get(arcb)?;
|
||||||
match values.version().try_into()? {
|
match values.version().try_into()? {
|
||||||
AttestationVersion::One => (),
|
AttestationVersion::One => (),
|
||||||
|
AttestationVersion::Two => (),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(values)
|
Ok(values)
|
||||||
@@ -246,6 +254,8 @@ impl Request for AttestationRequest {
|
|||||||
pub enum AttestationVersion {
|
pub enum AttestationVersion {
|
||||||
/// Version 1 (= 0x0100)
|
/// Version 1 (= 0x0100)
|
||||||
One = 0x0100,
|
One = 0x0100,
|
||||||
|
/// Version 2 (= 0x0200)
|
||||||
|
Two = 0x0200,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<u32> for AttestationVersion {
|
impl TryFrom<u32> for AttestationVersion {
|
||||||
@@ -254,6 +264,8 @@ impl TryFrom<u32> for AttestationVersion {
|
|||||||
fn try_from(value: u32) -> Result<Self> {
|
fn try_from(value: u32) -> Result<Self> {
|
||||||
if value == Self::One as u32 {
|
if value == Self::One as u32 {
|
||||||
Ok(Self::One)
|
Ok(Self::One)
|
||||||
|
} else if value == Self::Two as u32 {
|
||||||
|
Ok(Self::Two)
|
||||||
} else {
|
} else {
|
||||||
Err(Error::BinArcbInvVersion(value))
|
Err(Error::BinArcbInvVersion(value))
|
||||||
}
|
}
|
||||||
@@ -412,8 +424,8 @@ impl Zeroize for ReqConfData {
|
|||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::get_test_asset;
|
use crate::get_test_asset;
|
||||||
use crate::request::SymKey;
|
use crate::request::{HybridPKey, SymKey};
|
||||||
use crate::test_utils::get_test_keys;
|
use crate::test_utils::{get_test_keys, get_test_keys_hybrid};
|
||||||
|
|
||||||
const ARPK: [u8; 32] = [0x17; 32];
|
const ARPK: [u8; 32] = [0x17; 32];
|
||||||
const NONCE: [u8; 16] = [0xab; 16];
|
const NONCE: [u8; 16] = [0xab; 16];
|
||||||
@@ -421,6 +433,8 @@ mod test {
|
|||||||
|
|
||||||
fn mk_arcb() -> Vec<u8> {
|
fn mk_arcb() -> Vec<u8> {
|
||||||
let (cust_key, host_key) = get_test_keys();
|
let (cust_key, host_key) = get_test_keys();
|
||||||
|
let host_key = HostKey::V1(host_key);
|
||||||
|
|
||||||
let ctx = ReqEncrCtx::new_aes_256(
|
let ctx = ReqEncrCtx::new_aes_256(
|
||||||
Some([0x55; 12]),
|
Some([0x55; 12]),
|
||||||
Some(cust_key),
|
Some(cust_key),
|
||||||
@@ -443,7 +457,37 @@ mod test {
|
|||||||
arcb.conf.value_mut().nonce = NONCE;
|
arcb.conf.value_mut().nonce = NONCE;
|
||||||
arcb.conf.value_mut().meas_key = MEAS;
|
arcb.conf.value_mut().meas_key = MEAS;
|
||||||
|
|
||||||
arcb.add_hostkey(HostKey::V1(host_key));
|
arcb.add_hostkey(host_key);
|
||||||
|
arcb.encrypt(&ctx).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mk_arcb_v2() -> Vec<u8> {
|
||||||
|
let (cust_key, host_key1, host_key2) = get_test_keys_hybrid();
|
||||||
|
let host_key = HostKey::V2(HybridPKey::new(host_key1, host_key2).unwrap());
|
||||||
|
|
||||||
|
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::Two,
|
||||||
|
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()
|
arcb.encrypt(&ctx).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -455,6 +499,32 @@ mod test {
|
|||||||
assert_eq!(request, exp);
|
assert_eq!(request, exp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn arcb_v2() {
|
||||||
|
let request = mk_arcb_v2();
|
||||||
|
// Expected bytes for a V2 ARCB: rqvn = 0x0200 (bytes 8-11), rest deterministic.
|
||||||
|
// The first 288 bytes cover header + customer-public-key + one V2 keyslot header.
|
||||||
|
let exp: [u8; 288] = [
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 7, 208, 85, 85, 85, 85, 85, 85, 85, 85, 85,
|
||||||
|
85, 85, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 80, 112, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199,
|
||||||
|
93, 52, 249, 22, 82, 219, 69, 123, 11, 32, 156, 70, 164, 145, 164, 78, 226, 177, 110,
|
||||||
|
35, 194, 216, 218, 241, 22, 103, 138, 98, 242, 76, 227, 50, 197, 153, 95, 8, 69, 107,
|
||||||
|
102, 177, 109, 213, 90, 146, 197, 7, 241, 227, 26, 247, 140, 100, 168, 46, 122, 84, 27,
|
||||||
|
21, 19, 80, 21, 242, 2, 134, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 64, 128, 88,
|
||||||
|
167, 241, 165, 195, 80, 151, 83, 58, 2, 169, 56, 121, 231, 222, 103, 186, 40, 11, 206,
|
||||||
|
131, 101, 236, 148, 178, 185, 8, 245, 137, 195, 169, 152, 216, 190, 30, 99, 7, 215, 74,
|
||||||
|
224, 26, 220, 70, 130, 95, 246, 187, 111, 160, 92, 17, 71, 207, 226, 204, 244, 162, 79,
|
||||||
|
61, 131, 61, 218, 112, 255, 94, 191, 53, 220, 196, 47, 37, 93, 227, 234, 101, 1, 174,
|
||||||
|
171, 68, 42, 136, 92, 238, 72, 6, 17, 77, 231, 225, 174, 22, 222, 188, 212, 15, 248,
|
||||||
|
145, 72, 126, 139, 17, 233, 225, 156, 46, 233, 151, 54, 2, 175, 88, 215, 254, 243, 222,
|
||||||
|
37, 81, 50, 110, 18, 76, 252, 12, 210, 146, 66, 23,
|
||||||
|
];
|
||||||
|
|
||||||
|
// only compare non-randomized part
|
||||||
|
assert_eq!(request[..288], exp[..288]);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn auth_bin() {
|
fn auth_bin() {
|
||||||
let request = mk_arcb();
|
let request = mk_arcb();
|
||||||
@@ -464,6 +534,15 @@ mod test {
|
|||||||
assert_eq!(exp, auth_bin.as_bytes());
|
assert_eq!(exp, auth_bin.as_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn auth_bin_v2() {
|
||||||
|
let request = mk_arcb_v2();
|
||||||
|
let auth_bin = AttestationRequest::auth_bin(&request).unwrap();
|
||||||
|
let exp = &request[0x30..0x40];
|
||||||
|
|
||||||
|
assert_eq!(exp, auth_bin.as_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn decrypt_bin() {
|
fn decrypt_bin() {
|
||||||
let request = mk_arcb();
|
let request = mk_arcb();
|
||||||
@@ -473,6 +552,99 @@ mod test {
|
|||||||
assert_eq!(conf.nonce().as_ref().unwrap().value(), &NONCE);
|
assert_eq!(conf.nonce().as_ref().unwrap().value(), &NONCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn decrypt_bin_v2() {
|
||||||
|
let request = mk_arcb_v2();
|
||||||
|
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 arcb_v1_version() {
|
||||||
|
// Without any host-keys, version defaults to One
|
||||||
|
let arcb = AttestationRequest::new(
|
||||||
|
AttestationVersion::One,
|
||||||
|
AttestationMeasAlg::HmacSha512,
|
||||||
|
AttestationFlags::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(arcb.version(), AttestationVersion::One);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn arcb_v2_version() {
|
||||||
|
// After adding a V2 host-key, version is Two
|
||||||
|
let mut arcb = AttestationRequest::new(
|
||||||
|
AttestationVersion::Two,
|
||||||
|
AttestationMeasAlg::HmacSha512,
|
||||||
|
AttestationFlags::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let (_, host_key1, host_key2) = get_test_keys_hybrid();
|
||||||
|
arcb.add_hostkey(HostKey::V2(HybridPKey::new(host_key1, host_key2).unwrap()));
|
||||||
|
|
||||||
|
assert_eq!(arcb.version(), AttestationVersion::Two);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn attestation_version_try_from() {
|
||||||
|
// Test version conversion from u32
|
||||||
|
assert_eq!(
|
||||||
|
AttestationVersion::try_from(0x0100).unwrap(),
|
||||||
|
AttestationVersion::One
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
AttestationVersion::try_from(0x0200).unwrap(),
|
||||||
|
AttestationVersion::Two
|
||||||
|
);
|
||||||
|
|
||||||
|
// Invalid version should error
|
||||||
|
assert!(AttestationVersion::try_from(0x0300).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn attestation_flags_expected_size() {
|
||||||
|
// Test expected additional data size calculation for V1
|
||||||
|
let mut flags = AttestationFlags::default();
|
||||||
|
|
||||||
|
// Image PHKH flag - should be 32 bytes
|
||||||
|
flags.set_image_phkh();
|
||||||
|
assert_eq!(flags.expected_additional_size(), 32);
|
||||||
|
|
||||||
|
// Add attest PHKH flag - should be 64 bytes (32 + 32)
|
||||||
|
flags.set_attest_phkh();
|
||||||
|
assert_eq!(flags.expected_additional_size(), 64);
|
||||||
|
|
||||||
|
// Add secret store hash - should be 128 bytes (64 + 64)
|
||||||
|
flags.set_secret_store_hash();
|
||||||
|
assert_eq!(flags.expected_additional_size(), 128);
|
||||||
|
|
||||||
|
// Add firmware state - should be 448 bytes (128 + 320)
|
||||||
|
flags.set_firmware_state();
|
||||||
|
assert_eq!(flags.expected_additional_size(), 448);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn confidential_data_v2() {
|
||||||
|
// Test confidential data extraction (version-independent)
|
||||||
|
let arcb = AttestationRequest::new(
|
||||||
|
AttestationVersion::Two,
|
||||||
|
AttestationMeasAlg::HmacSha512,
|
||||||
|
AttestationFlags::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let conf = arcb.confidential_data();
|
||||||
|
|
||||||
|
// Should have measurement key and nonce
|
||||||
|
assert_eq!(conf.measurement_key().len(), 64);
|
||||||
|
assert!(conf.nonce().is_some());
|
||||||
|
assert_eq!(conf.nonce().as_ref().unwrap().value().len(), 16);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn decrypt_bin_fail_magic() {
|
fn decrypt_bin_fail_magic() {
|
||||||
let arpk = SymKey::Aes256(ARPK.into());
|
let arpk = SymKey::Aes256(ARPK.into());
|
||||||
@@ -484,6 +656,17 @@ mod test {
|
|||||||
assert!(matches!(ret, Err(Error::NoArcb)));
|
assert!(matches!(ret, Err(Error::NoArcb)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn decrypt_bin_fail_magic_v2() {
|
||||||
|
let arpk = SymKey::Aes256(ARPK.into());
|
||||||
|
let mut tamp_arcb = mk_arcb_v2();
|
||||||
|
|
||||||
|
// tamper magic
|
||||||
|
tamp_arcb[0] = 17;
|
||||||
|
let ret = AttestationRequest::decrypt_bin(&tamp_arcb, &arpk);
|
||||||
|
assert!(matches!(ret, Err(Error::NoArcb)));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn decrypt_bin_fail_mai() {
|
fn decrypt_bin_fail_mai() {
|
||||||
let arpk = SymKey::Aes256(ARPK.into());
|
let arpk = SymKey::Aes256(ARPK.into());
|
||||||
@@ -499,6 +682,21 @@ mod test {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn decrypt_bin_fail_mai_v2() {
|
||||||
|
let arpk = SymKey::Aes256(ARPK.into());
|
||||||
|
let mut tamp_arcb = mk_arcb_v2();
|
||||||
|
|
||||||
|
// 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]
|
#[test]
|
||||||
fn decrypt_bin_fail_aad() {
|
fn decrypt_bin_fail_aad() {
|
||||||
let arpk = SymKey::Aes256(ARPK.into());
|
let arpk = SymKey::Aes256(ARPK.into());
|
||||||
@@ -509,4 +707,15 @@ mod test {
|
|||||||
let ret = AttestationRequest::decrypt_bin(&tamp_arcb, &arpk);
|
let ret = AttestationRequest::decrypt_bin(&tamp_arcb, &arpk);
|
||||||
assert!(matches!(ret, Err(Error::GcmTagMismatch)));
|
assert!(matches!(ret, Err(Error::GcmTagMismatch)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn decrypt_bin_fail_aad_v2() {
|
||||||
|
let arpk = SymKey::Aes256(ARPK.into());
|
||||||
|
let mut tamp_arcb = mk_arcb_v2();
|
||||||
|
|
||||||
|
// tamper AAD
|
||||||
|
tamp_arcb[0x3c] = 17;
|
||||||
|
let ret = AttestationRequest::decrypt_bin(&tamp_arcb, &arpk);
|
||||||
|
assert!(matches!(ret, Err(Error::GcmTagMismatch)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use std::mem::size_of;
|
|||||||
|
|
||||||
use openssl::hash::MessageDigest;
|
use openssl::hash::MessageDigest;
|
||||||
use openssl::pkey::{PKeyRef, Private};
|
use openssl::pkey::{PKeyRef, Private};
|
||||||
|
use pv_core::misc::write_file;
|
||||||
use zerocopy::{BigEndian, IntoBytes, U16, U32};
|
use zerocopy::{BigEndian, IntoBytes, U16, U32};
|
||||||
|
|
||||||
use super::AttNonce;
|
use super::AttNonce;
|
||||||
@@ -101,6 +102,7 @@ impl AttestationItems {
|
|||||||
}
|
}
|
||||||
items.extend_from_slice(additional);
|
items.extend_from_slice(additional);
|
||||||
assert!(items.len() == size);
|
assert!(items.len() == size);
|
||||||
|
write_file("additional_data_parsed", &items, "blah").unwrap();
|
||||||
Self(items.into())
|
Self(items.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,10 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use clap::{Args, Parser, Subcommand, ValueEnum, ValueHint};
|
use clap::{Args, Parser, Subcommand, ValueEnum, ValueHint};
|
||||||
use utils::{CertificateOptions, DeprecatedVerbosityOptions};
|
use utils::{
|
||||||
|
AutoOrExplicit, AutoOrExplicitParser, CertificateOptions, DeprecatedVerbosityOptions,
|
||||||
|
HkdVersion, ValueEnumDisplay, ValueEnumFromStr,
|
||||||
|
};
|
||||||
|
|
||||||
/// create, perform, and verify attestation measurements
|
/// create, perform, and verify attestation measurements
|
||||||
///
|
///
|
||||||
@@ -67,6 +70,29 @@ pub enum Command {
|
|||||||
Version,
|
Version,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Secure Execution attestation version for CLI
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum, ValueEnumDisplay, ValueEnumFromStr)]
|
||||||
|
pub enum AttVersion {
|
||||||
|
#[value(name = "1")]
|
||||||
|
/// Version 1 - uses traditional cryptographic keys
|
||||||
|
V1,
|
||||||
|
#[value(name = "2")]
|
||||||
|
/// Version 2 - uses hybrid (post-quantum) cryptographic keys
|
||||||
|
V2,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type AttVersionSelection = AutoOrExplicit<AttVersion>;
|
||||||
|
pub type AttVersionSelectionParser = AutoOrExplicitParser<AttVersion>;
|
||||||
|
|
||||||
|
impl From<AttVersion> for HkdVersion {
|
||||||
|
fn from(val: AttVersion) -> Self {
|
||||||
|
match val {
|
||||||
|
AttVersion::V1 => Self::Classical,
|
||||||
|
AttVersion::V2 => Self::Hybrid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Args, Debug)]
|
#[derive(Args, Debug)]
|
||||||
pub struct CreateAttOpt {
|
pub struct CreateAttOpt {
|
||||||
#[command(flatten)]
|
#[command(flatten)]
|
||||||
@@ -94,6 +120,10 @@ pub struct CreateAttOpt {
|
|||||||
value_delimiter = ','
|
value_delimiter = ','
|
||||||
)]
|
)]
|
||||||
pub add_data: Vec<AttAddFlags>,
|
pub add_data: Vec<AttAddFlags>,
|
||||||
|
|
||||||
|
/// Specify the Attestation Request version to use.
|
||||||
|
#[arg(long = "att-version", value_name = "VERSION", default_value_t = AttVersionSelection::Explicit(AttVersion::V1), value_parser = AttVersionSelectionParser::default())]
|
||||||
|
pub att_version: AttVersionSelection,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, ValueEnum, Clone, Copy)]
|
#[derive(Debug, ValueEnum, Clone, Copy)]
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ pub use check::check;
|
|||||||
pub use create::create;
|
pub use create::create;
|
||||||
pub use verify::verify;
|
pub use verify::verify;
|
||||||
|
|
||||||
pub const CMD_FN: &[&str] = &["+create", "+verify"];
|
pub const CMD_FN: &[&str] = &["+create", "+verify", "+quantumsafe"];
|
||||||
// s390 branch
|
// s390 branch
|
||||||
#[cfg(target_arch = "s390x")]
|
#[cfg(target_arch = "s390x")]
|
||||||
mod uv_cmd {
|
mod uv_cmd {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use pv::attest::{AttestationFlags, AttestationMeasAlg, AttestationRequest, Attes
|
|||||||
use pv::misc::{create_file, write_file};
|
use pv::misc::{create_file, write_file};
|
||||||
use pv::request::{HostKey, ReqEncrCtx, Request, SymKey, SymKeyType};
|
use pv::request::{HostKey, ReqEncrCtx, Request, SymKey, SymKeyType};
|
||||||
|
|
||||||
use crate::cli::{AttAddFlags, CreateAttOpt};
|
use crate::cli::{AttAddFlags, AttVersion, AttVersionSelection, CreateAttOpt};
|
||||||
use crate::exchange::{ExchangeFormatRequest, ExchangeFormatVersion};
|
use crate::exchange::{ExchangeFormatRequest, ExchangeFormatVersion};
|
||||||
|
|
||||||
fn flags(cli_flags: &[AttAddFlags]) -> AttestationFlags {
|
fn flags(cli_flags: &[AttAddFlags]) -> AttestationFlags {
|
||||||
@@ -26,18 +26,54 @@ fn flags(cli_flags: &[AttAddFlags]) -> AttestationFlags {
|
|||||||
att_flags
|
att_flags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Auto-detect the attestation version based on the host keys.
|
||||||
|
///
|
||||||
|
/// Returns Two if any host key is a hybrid key, otherwise returns V1.
|
||||||
|
fn auto_detect_version(host_keys: &[HostKey]) -> AttestationVersion {
|
||||||
|
let use_hybrid_keys = host_keys.iter().any(|k: &HostKey| k.is_hybrid());
|
||||||
|
if use_hybrid_keys {
|
||||||
|
AttestationVersion::Two
|
||||||
|
} else {
|
||||||
|
AttestationVersion::One
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<AttVersion> for AttestationVersion {
|
||||||
|
fn from(value: AttVersion) -> Self {
|
||||||
|
match value {
|
||||||
|
AttVersion::V1 => Self::One,
|
||||||
|
AttVersion::V2 => Self::Two,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determine the attestation version to use.
|
||||||
|
///
|
||||||
|
/// If an explicit version is provided via CLI, use that.
|
||||||
|
/// Otherwise, auto-detect based on the host key types.
|
||||||
|
fn determine_version(
|
||||||
|
cli_version: AttVersionSelection,
|
||||||
|
host_keys: &[HostKey],
|
||||||
|
) -> AttestationVersion {
|
||||||
|
match cli_version {
|
||||||
|
AttVersionSelection::Auto => auto_detect_version(host_keys),
|
||||||
|
AttVersionSelection::Explicit(att_version) => att_version.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn create(opt: &CreateAttOpt) -> Result<ExitCode> {
|
pub fn create(opt: &CreateAttOpt) -> Result<ExitCode> {
|
||||||
let att_version = AttestationVersion::One;
|
let hkds = opt
|
||||||
|
.certificate_args
|
||||||
|
.get_verified_hkds_new("attestation request", opt.att_version.map(|v| v.into()))?;
|
||||||
|
|
||||||
|
let att_version = determine_version(opt.att_version, &hkds);
|
||||||
let meas_alg = AttestationMeasAlg::HmacSha512;
|
let meas_alg = AttestationMeasAlg::HmacSha512;
|
||||||
|
|
||||||
let mut arcb = AttestationRequest::new(att_version, meas_alg, flags(&opt.add_data))?;
|
let mut arcb = AttestationRequest::new(att_version, meas_alg, flags(&opt.add_data))?;
|
||||||
debug!("Generated Attestation request");
|
debug!("Generated Attestation request");
|
||||||
|
|
||||||
// Add host-key documents
|
// Add host-key documents
|
||||||
opt.certificate_args
|
hkds.into_iter().for_each(|k| arcb.add_hostkey(k));
|
||||||
.get_verified_hkds("attestation request")?
|
|
||||||
.into_iter()
|
|
||||||
.for_each(|k| arcb.add_hostkey(HostKey::V1(k)));
|
|
||||||
debug!("Added all host-keys");
|
debug!("Added all host-keys");
|
||||||
|
|
||||||
let encr_ctx =
|
let encr_ctx =
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ use pv::request::MagicValue;
|
|||||||
use pv::uv::{AttestationCmd, ConfigUid};
|
use pv::uv::{AttestationCmd, ConfigUid};
|
||||||
use zerocopy::{BigEndian, ByteOrder, FromBytes, Immutable, IntoBytes, KnownLayout, U32, U64};
|
use zerocopy::{BigEndian, ByteOrder, FromBytes, Immutable, IntoBytes, KnownLayout, U32, U64};
|
||||||
|
|
||||||
|
use crate::additional;
|
||||||
|
|
||||||
const INV_EXCHANGE_FMT_ERROR_TEXT: &str = "The input has not the correct format:";
|
const INV_EXCHANGE_FMT_ERROR_TEXT: &str = "The input has not the correct format:";
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@@ -99,10 +101,18 @@ impl ExchangeFormatV1Hdr {
|
|||||||
let measurement_entry = Entry::from_exp(Some(measurement));
|
let measurement_entry = Entry::from_exp(Some(measurement));
|
||||||
let exp_add = match additional {
|
let exp_add = match additional {
|
||||||
0 => None,
|
0 => None,
|
||||||
size => Some(size),
|
size => {
|
||||||
|
if size > AttestationCmd::ADDITIONAL_MAX_SIZE {
|
||||||
|
bail!(
|
||||||
|
"Additional data size ({}) exceeds maximum allowed size ({})",
|
||||||
|
size,
|
||||||
|
AttestationCmd::ADDITIONAL_MAX_SIZE
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Some(size)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
// TODO min and max size check?
|
let additional_entry = Entry::from_exp(exp_add);
|
||||||
let additional_entry = Entry::from_exp(exp_add); //, AttestationCmd::ADDITIONAL_MAX_SIZE, &mut offset);
|
|
||||||
let user_entry = Entry::from_none();
|
let user_entry = Entry::from_none();
|
||||||
let cuid_entry = Entry::from_none();
|
let cuid_entry = Entry::from_none();
|
||||||
|
|
||||||
@@ -530,7 +540,6 @@ impl ExchangeFormatResponse {
|
|||||||
"{INV_EXCHANGE_FMT_ERROR_TEXT} Contains no attestation request.",
|
"{INV_EXCHANGE_FMT_ERROR_TEXT} Contains no attestation request.",
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
// TODO remove unwrap
|
|
||||||
let measurement = hdr.measurement.read(reader)?.data().ok_or(anyhow!(
|
let measurement = hdr.measurement.read(reader)?.data().ok_or(anyhow!(
|
||||||
"{INV_EXCHANGE_FMT_ERROR_TEXT} Contains no attestation response (Measurement missing).",
|
"{INV_EXCHANGE_FMT_ERROR_TEXT} Contains no attestation response (Measurement missing).",
|
||||||
))?;
|
))?;
|
||||||
@@ -693,6 +702,50 @@ mod test {
|
|||||||
ExchangeFormatRequest::new(ARCB.to_vec(), 0, ADDITIONAL.len() as u32).unwrap_err();
|
ExchangeFormatRequest::new(ARCB.to_vec(), 0, ADDITIONAL.len() as u32).unwrap_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_additional_data_size_validation() {
|
||||||
|
// Test for TODO 1 fix: Additional data size validation
|
||||||
|
let arcb = ARCB.to_vec();
|
||||||
|
|
||||||
|
// Test with valid size at maximum
|
||||||
|
let result = ExchangeFormatV1Hdr::new_request(
|
||||||
|
&arcb,
|
||||||
|
MEASUREMENT.len() as u32,
|
||||||
|
AttestationCmd::ADDITIONAL_MAX_SIZE,
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
result.is_ok(),
|
||||||
|
"Maximum additional data size should be accepted"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test with size exceeding maximum
|
||||||
|
let result = ExchangeFormatV1Hdr::new_request(
|
||||||
|
&arcb,
|
||||||
|
MEASUREMENT.len() as u32,
|
||||||
|
AttestationCmd::ADDITIONAL_MAX_SIZE + 1,
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
result.is_err(),
|
||||||
|
"Additional data size exceeding maximum should fail"
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Err(e) = result {
|
||||||
|
let error_msg = e.to_string();
|
||||||
|
assert!(
|
||||||
|
error_msg.contains("exceeds maximum"),
|
||||||
|
"Error should mention exceeding maximum: {}",
|
||||||
|
error_msg
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test with zero size (no additional data)
|
||||||
|
let result = ExchangeFormatV1Hdr::new_request(&arcb, MEASUREMENT.len() as u32, 0);
|
||||||
|
assert!(
|
||||||
|
result.is_ok(),
|
||||||
|
"Zero additional data size should be accepted"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn min_req() {
|
fn min_req() {
|
||||||
test_read_write_request(
|
test_read_write_request(
|
||||||
|
|||||||
Reference in New Issue
Block a user