diff --git a/rust/pv/src/lib.rs b/rust/pv/src/lib.rs index c601bf6b..737753eb 100644 --- a/rust/pv/src/lib.rs +++ b/rust/pv/src/lib.rs @@ -95,7 +95,7 @@ pub mod request { AeadDecryptionResult, AeadEncryptionResult, Aes256GcmKey, Aes256XtsKey, SymKey, SymKeyType, SHA_512_HASH_LEN, }; - pub use crate::req::{EcPubKeyCoord, Encrypt, Keyslot, ReqEncrCtx, Request}; + pub use crate::req::{EcPubKeyCoord, Encrypt, HostKey, Keyslot, ReqEncrCtx, Request}; pub use crate::verify::{CertVerifier, HkdVerifier, NoVerifyHkd}; /// Reexports some useful OpenSSL symbols diff --git a/rust/pv/src/req/context.rs b/rust/pv/src/req/context.rs index 1e49ef67..0a052167 100644 --- a/rust/pv/src/req/context.rs +++ b/rust/pv/src/req/context.rs @@ -11,9 +11,7 @@ use openssl::pkey::{PKey, Private}; use pv_core::request::{RequestMagic, RequestVersion}; use zerocopy::IntoBytes; -use super::ec_coord::EcPubKeyCoord; -use super::encrypt::{Aad, Encrypt}; -use super::header::RequestHdr; +use super::{Aad, EcPubKeyCoord, Encrypt, RequestHdr}; use crate::crypto::{ encrypt_aead, gen_ec_key, random_array, AeadEncryptionResult, SymKey, SymKeyType, }; @@ -175,6 +173,7 @@ impl ReqEncrCtx { mod tests { use super::*; use crate::get_test_asset; + use crate::req::hostkey::HostKey; use crate::req::keyslot::Keyslot; use crate::request::SymKey; use crate::test_utils::*; @@ -184,7 +183,7 @@ mod tests { #[test] fn encr_build_aad() { let (cust_key, host_key) = get_test_keys(); - let ks = Keyslot::new(host_key); + let ks = Keyslot::new(HostKey::V1(host_key)); let ctx = ReqEncrCtx::new_aes_256( Some([0x11; 12]), Some(cust_key), @@ -227,7 +226,9 @@ mod tests { let (_, host_key) = get_test_keys(); let ctx = ReqEncrCtx::new_aes_256(Some([0x11; 12]), None, None).unwrap(); - let ks: Vec = (0..257).map(|_| Keyslot::new(host_key.clone())).collect(); + let ks: Vec = (0..257) + .map(|_| Keyslot::new(HostKey::V1(host_key.clone()))) + .collect(); let mut aad = Vec::::new(); ks.iter().for_each(|ks| aad.push(Aad::Ks(ks))); @@ -238,6 +239,7 @@ mod tests { #[test] fn encr_build_aad_nks() { let (_, host_key) = get_test_keys(); + let host_key = HostKey::V1(host_key); let ctx = ReqEncrCtx::new_aes_256(Some([0x11; 12]), None, None).unwrap(); let ks = [ diff --git a/rust/pv/src/req/hostkey.rs b/rust/pv/src/req/hostkey.rs new file mode 100644 index 00000000..9edf673e --- /dev/null +++ b/rust/pv/src/req/hostkey.rs @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +// +// Copyright IBM Corp. + +//! Host key types for UV requests + +use openssl::pkey::{PKey, Public}; + +/// Versioned host keys container +#[non_exhaustive] +#[derive(Clone, Debug)] +pub enum HostKey { + /// ECDH public key + V1(PKey), +} + +impl HostKey { + /// Return the ECDH public key + pub fn ec_key(&self) -> &PKey { + match self { + HostKey::V1(ec_key) => ec_key, + } + } +} + +impl AsRef for HostKey { + fn as_ref(&self) -> &HostKey { + self + } +} diff --git a/rust/pv/src/req/keyslot.rs b/rust/pv/src/req/keyslot.rs index 7989ee48..768a967e 100644 --- a/rust/pv/src/req/keyslot.rs +++ b/rust/pv/src/req/keyslot.rs @@ -10,6 +10,7 @@ use openssl::pkey::{PKey, PKeyRef, Private, Public}; use super::ec_coord::EcPubKeyCoord; use super::encrypt::Encrypt; use crate::crypto::{derive_aes256_gcm_key, encrypt_aead, hash}; +use crate::request::HostKey; use crate::Result; /// IBM Z Host key-slot @@ -23,11 +24,13 @@ use crate::Result; /// |_____________________________________________________________| /// ``` #[derive(Debug, Clone)] -pub struct Keyslot(PKey); +pub struct KeyslotV1(PKey); -impl Keyslot { +impl KeyslotV1 { /// Size of a host-key hash pub const PHKH_SIZE: u32 = 0x20; + /// Size of complete V1 keyslot in bytes + pub const SIZE: usize = 80; /// Creates a new Keyslot from the provided public key pub fn new(hostkey: PKey) -> Self { @@ -35,7 +38,7 @@ impl Keyslot { } } -impl Encrypt for Keyslot { +impl Encrypt for KeyslotV1 { /// Encrypts the given request protection key `prot_key`. /// /// The AES256 encryption key is derived from `self` as public key, and `priv_key` as private @@ -56,15 +59,75 @@ impl Encrypt for Keyslot { let derived_key = derive_aes256_gcm_key(priv_key, &self.0)?; let mut wrpk_and_kst = encrypt_aead(&derived_key.into(), &[0; 12], &[], prot_key)?.into_buf(); + assert_eq!(wrpk_and_kst.len(), 48); let phk: EcPubKeyCoord = self.0.as_ref().try_into()?; to.reserve(80); - to.extend_from_slice(&hash(MessageDigest::sha256(), phk.as_ref())?); + let hash = hash(MessageDigest::sha256(), phk.as_ref())?; + assert_eq!(hash.len(), 32); + to.extend_from_slice(&hash); to.append(&mut wrpk_and_kst); Ok(()) } } +/// Versioned keyslot container +#[non_exhaustive] +#[derive(Debug, Clone)] +pub enum Keyslot { + /// V1 key-slots with ECDH keys + V1(KeyslotV1), +} + +impl Keyslot { + /// Return a keyslot with the same key-type as the given host-key + pub fn new(hostkey: HostKey) -> Self { + match hostkey { + HostKey::V1(key) => Keyslot::V1(KeyslotV1::new(key)), + } + } + + /// Return the public host key hash size for the given version of the key-slot in bytes + pub fn phkh_size(&self) -> u32 { + match self { + Keyslot::V1(_) => KeyslotV1::PHKH_SIZE, + } + } + + /// Return the size of the key-slot in bytes + pub fn size(&self) -> usize { + match self { + Keyslot::V1(_) => KeyslotV1::SIZE, + } + } + + /// Return whether the key-slot uses hybrid keys + pub fn is_hybrid(&self) -> bool { + match self { + Keyslot::V1(_) => false, + } + } +} + +impl Encrypt for Keyslot { + fn encrypt_to( + &self, + secret: &[u8], + priv_key: &PKeyRef, + to: &mut Vec, + ) -> Result<()> { + match self { + Keyslot::V1(ks) => ks.encrypt_to(secret, priv_key, to), + } + } +} + +impl From> for Keyslot { + fn from(key: PKey) -> Self { + Keyslot::V1(KeyslotV1::new(key)) + } +} + #[cfg(test)] mod tests { use super::*; @@ -76,7 +139,7 @@ mod tests { let (cust_key, host_key) = get_test_keys(); let exp_keyslot = get_test_asset!("exp/keyslot.bin").to_vec(); - let keyslot = Keyslot::new(host_key); + let keyslot = KeyslotV1(host_key); let encr_ks = keyslot.encrypt(&[0x17u8; 32], &cust_key).unwrap(); assert_eq!(exp_keyslot, encr_ks); diff --git a/rust/pv/src/req/mod.rs b/rust/pv/src/req/mod.rs index 4cd8999a..5bdadacd 100644 --- a/rust/pv/src/req/mod.rs +++ b/rust/pv/src/req/mod.rs @@ -11,6 +11,7 @@ mod context; mod ec_coord; mod encrypt; mod header; +mod hostkey; mod keyslot; mod request; @@ -18,5 +19,7 @@ mod request; pub use context::ReqEncrCtx; pub use ec_coord::EcPubKeyCoord; pub use encrypt::{Aad, Encrypt}; -pub use keyslot::Keyslot; +pub use header::RequestHdr; +pub use hostkey::HostKey; +pub use keyslot::{Keyslot, KeyslotV1}; pub use request::{BinReqValues, Request}; diff --git a/rust/pv/src/req/request.rs b/rust/pv/src/req/request.rs index e8f74903..1084b7e2 100644 --- a/rust/pv/src/req/request.rs +++ b/rust/pv/src/req/request.rs @@ -3,9 +3,9 @@ // Copyright IBM Corp. use std::mem::size_of; -use openssl::pkey::{PKey, Public}; use zerocopy::{FromBytes, Immutable, KnownLayout}; +use super::HostKey; use crate::crypto::{decrypt_aead, SymKey, SymKeyType}; use crate::req::context::ReqEncrCtx; use crate::req::header::RequestHdr; @@ -47,10 +47,11 @@ pub trait Request { /// This function will return an error if the encryption fails, the request does not have at /// least a hostkey, or other implementation dependent contracts are not met. fn encrypt(&self, ctx: &ReqEncrCtx) -> Result>; + /// Add a host-key to this request /// /// Must be called at least once, otherwise {`Request::encrypt`} will fail - fn add_hostkey(&mut self, hostkey: PKey); + fn add_hostkey(&mut self, hostkey: HostKey); } /// A struct to represent some parts of a binary/encrypted request. @@ -149,7 +150,7 @@ mod tests { use super::*; use crate::get_test_asset; use crate::req::header::RequestHdr; - use crate::req::{Aad, Keyslot, ReqEncrCtx}; + use crate::req::{Aad, HostKey, Keyslot, ReqEncrCtx}; use crate::request::SymKey; use crate::test_utils::*; @@ -158,7 +159,7 @@ mod tests { #[test] fn encr_build_aad() { let (cust_key, host_key) = get_test_keys(); - let ks = Keyslot::new(host_key); + let ks = Keyslot::new(HostKey::V1(host_key)); let ctx = ReqEncrCtx::new_aes_256( Some([0x11; 12]), Some(cust_key), @@ -199,6 +200,7 @@ mod tests { #[test] fn encr_build_aad_nks_many() { let (_, host_key) = get_test_keys(); + let host_key = HostKey::V1(host_key); let ctx = ReqEncrCtx::new_aes_256(Some([0x11; 12]), None, None).unwrap(); let ks: Vec = (0..257).map(|_| Keyslot::new(host_key.clone())).collect(); @@ -211,6 +213,7 @@ mod tests { #[test] fn encr_build_aad_nks() { let (_, host_key) = get_test_keys(); + let host_key = HostKey::V1(host_key); let ctx = ReqEncrCtx::new_aes_256(Some([0x11; 12]), None, None).unwrap(); let ks = [ diff --git a/rust/pv/src/uvattest/additional.rs b/rust/pv/src/uvattest/additional.rs index a056e096..63a20c08 100644 --- a/rust/pv/src/uvattest/additional.rs +++ b/rust/pv/src/uvattest/additional.rs @@ -7,12 +7,12 @@ use std::fmt::Display; use serde::Serialize; use super::arcb::AttestationFlags; -use crate::req::Keyslot; +use crate::req::KeyslotV1; use crate::{static_assert, Error, Result}; /// Hash for additional-data stuff used for parsing [`AdditionalData`] pub(super) const PHKH_SIZE: u32 = 0x20; -static_assert!(Keyslot::PHKH_SIZE == PHKH_SIZE); +static_assert!(KeyslotV1::PHKH_SIZE == PHKH_SIZE); pub(super) const SECRET_STORE_HASH_SIZE: u32 = 0x40; pub(super) const FW_STATE_SIZE: u32 = 0x140; diff --git a/rust/pv/src/uvattest/arcb.rs b/rust/pv/src/uvattest/arcb.rs index 19ea4d93..ede31044 100644 --- a/rust/pv/src/uvattest/arcb.rs +++ b/rust/pv/src/uvattest/arcb.rs @@ -4,7 +4,6 @@ use std::mem::size_of; -use openssl::pkey::{PKey, Public}; use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, KnownLayout, U32}; use super::additional::{FW_STATE_SIZE, PHKH_SIZE, SECRET_STORE_HASH_SIZE}; @@ -12,7 +11,7 @@ use super::AttNonce; use crate::attest::{AttestationMagic, AttestationMeasAlg}; use crate::crypto::random_array; use crate::misc::Flags; -use crate::req::{Aad, BinReqValues, Keyslot, ReqEncrCtx}; +use crate::req::{Aad, BinReqValues, HostKey, Keyslot, ReqEncrCtx}; use crate::request::{Confidential, MagicValue, Request, RequestVersion, SymKey, Zeroize}; use crate::uv::UvFlags; use crate::{assert_size, static_assert, Error, Result}; @@ -52,7 +51,7 @@ use crate::{ /// /// ```rust,no_run /// # use s390_pv::attest::{AttestationFlags, AttestationMeasAlg, AttestationRequest, AttestationVersion}; -/// # use s390_pv::request::{SymKeyType, Request, ReqEncrCtx}; +/// # use s390_pv::request::{SymKeyType, Request, ReqEncrCtx, HostKey}; /// # fn main() -> s390_pv::Result<()> { /// let att_version = AttestationVersion::One; /// let meas_alg = AttestationMeasAlg::HmacSha512; @@ -61,11 +60,11 @@ use crate::{ /// let hkd = s390_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); +/// arcb.add_hostkey(HostKey::V1(hkd)); /// // you can add multiple hostkeys /// // arcb.add_hostkey(another_hkd); /// // encrypt it -/// let ctx = ReqEncrCtx::random(SymKeyType::Aes256)?; +/// let ctx = ReqEncrCtx::random(SymKeyType::Aes256Gcm)?; /// let arcb = arcb.encrypt(&ctx)?; /// # Ok(()) /// # } @@ -236,7 +235,7 @@ impl Request for AttestationRequest { ctx.encrypt_aead(&aad, conf).map(|res| res.into_buf()) } - fn add_hostkey(&mut self, hostkey: PKey) { + fn add_hostkey(&mut self, hostkey: HostKey) { self.keyslots.push(Keyslot::new(hostkey)) } } @@ -444,7 +443,7 @@ mod test { arcb.conf.value_mut().nonce = NONCE; arcb.conf.value_mut().meas_key = MEAS; - arcb.add_hostkey(host_key); + arcb.add_hostkey(HostKey::V1(host_key)); arcb.encrypt(&ctx).unwrap() } diff --git a/rust/pv/src/uvsecret/asrcb.rs b/rust/pv/src/uvsecret/asrcb.rs index e571aada..774ca3b8 100644 --- a/rust/pv/src/uvsecret/asrcb.rs +++ b/rust/pv/src/uvsecret/asrcb.rs @@ -3,7 +3,7 @@ // Copyright IBM Corp. 2023 use openssl::md::Md; -use openssl::pkey::{PKey, Private, Public}; +use openssl::pkey::{PKey, Private}; use pv_core::request::RequestVersion; use pv_core::secret::AddSecretMagic; use pv_core::uv::SecretId; @@ -13,7 +13,7 @@ use super::guest_secret::ListableSecretHdr; use super::user_data::UserData; use crate::crypto::{hkdf_rfc_5869, AeadEncryptionResult}; use crate::misc::Flags; -use crate::req::{Aad, BinReqValues, Keyslot, ReqEncrCtx}; +use crate::req::{Aad, BinReqValues, HostKey, Keyslot, ReqEncrCtx}; use crate::request::{BootHdrTags, Confidential, Request}; use crate::secret::{ExtSecret, GuestSecret}; use crate::uv::{ConfigUid, UvFlags}; @@ -309,7 +309,7 @@ impl Request for AddSecretRequest { } } - fn add_hostkey(&mut self, hostkey: PKey) { + fn add_hostkey(&mut self, hostkey: HostKey) { self.keyslots.push(Keyslot::new(hostkey)) } } diff --git a/rust/pv/tests/add_secret_request.rs b/rust/pv/tests/add_secret_request.rs index 9d2f9cc8..31aa1726 100644 --- a/rust/pv/tests/add_secret_request.rs +++ b/rust/pv/tests/add_secret_request.rs @@ -7,7 +7,7 @@ use openssl::ec::{EcGroup, EcKey}; use openssl::nid::Nid; use openssl::pkey::Private; use s390_pv::request::openssl::pkey::{PKey, Public}; -use s390_pv::request::{BootHdrTags, ReqEncrCtx, Request, SymKey}; +use s390_pv::request::{BootHdrTags, HostKey, ReqEncrCtx, Request, SymKey}; use s390_pv::secret::{ verify_asrcb_and_get_user_data, AddSecretFlags, AddSecretRequest, AddSecretVersion, ExtSecret, GuestSecret, @@ -26,7 +26,7 @@ fn create_asrcb( ext_secret: Option, flags: AddSecretFlags, cuid: Option, - hkd: PKey, + hkd: HostKey, ctx: &ReqEncrCtx, ) -> Result> { let mut asrcb = AddSecretRequest::new(AddSecretVersion::One, guest_secret, TAGS, flags); @@ -67,7 +67,14 @@ where true => Some(CUID), false => None, }; - create_asrcb(guest_secret, ext_secret.into(), flags, cuid, host_key, &ctx) + create_asrcb( + guest_secret, + ext_secret.into(), + flags, + cuid, + HostKey::V1(host_key), + &ctx, + ) } fn association() -> GuestSecret { @@ -91,7 +98,7 @@ fn create_signed_asrcb(skey: PKey, user_data: Vec) -> Vec { let mut asrcb = AddSecretRequest::new(AddSecretVersion::One, GuestSecret::Null, TAGS, no_flag()); - asrcb.add_hostkey(host_key); + asrcb.add_hostkey(HostKey::V1(host_key)); asrcb.set_user_data(user_data, Some(skey)).unwrap(); asrcb.encrypt(&ctx).unwrap() } @@ -103,7 +110,7 @@ fn null_none_default_ncuid_one_user_unsgn() { let mut asrcb = AddSecretRequest::new(AddSecretVersion::One, GuestSecret::Null, TAGS, no_flag()); - asrcb.add_hostkey(host_key); + asrcb.add_hostkey(HostKey::V1(host_key)); asrcb.set_user_data(user_data_orig.clone(), None).unwrap(); let asrcb = asrcb.encrypt(&ctx).unwrap(); @@ -236,7 +243,7 @@ fn null_none_default_cuid_seven() { let (hkd, ctx) = get_crypto(); let mut asrcb = AddSecretRequest::new(AddSecretVersion::One, GuestSecret::Null, TAGS, no_flag()); - (0..7).for_each(|_| asrcb.add_hostkey(hkd.clone())); + (0..7).for_each(|_| asrcb.add_hostkey(HostKey::V1(hkd.clone()))); asrcb.set_cuid(CUID); let asrcb = asrcb.encrypt(&ctx).unwrap(); diff --git a/rust/pvattest/src/cmd/create.rs b/rust/pvattest/src/cmd/create.rs index 7b5a0b6e..9f89139f 100644 --- a/rust/pvattest/src/cmd/create.rs +++ b/rust/pvattest/src/cmd/create.rs @@ -8,7 +8,7 @@ use anyhow::{bail, Context, Result}; use log::{debug, warn}; use pv::attest::{AttestationFlags, AttestationMeasAlg, AttestationRequest, AttestationVersion}; use pv::misc::{create_file, write_file}; -use pv::request::{ReqEncrCtx, Request, SymKey, SymKeyType}; +use pv::request::{HostKey, ReqEncrCtx, Request, SymKey, SymKeyType}; use crate::cli::{AttAddFlags, CreateAttOpt}; use crate::exchange::{ExchangeFormatRequest, ExchangeFormatVersion}; @@ -37,7 +37,7 @@ pub fn create(opt: &CreateAttOpt) -> Result { opt.certificate_args .get_verified_hkds("attestation request")? .into_iter() - .for_each(|k| arcb.add_hostkey(k)); + .for_each(|k| arcb.add_hostkey(HostKey::V1(k))); debug!("Added all host-keys"); let encr_ctx = diff --git a/rust/pvimg/src/pv_utils/se_hdr/hdr_v1.rs b/rust/pvimg/src/pv_utils/se_hdr/hdr_v1.rs index 71b232a8..1ad4db20 100644 --- a/rust/pvimg/src/pv_utils/se_hdr/hdr_v1.rs +++ b/rust/pvimg/src/pv_utils/se_hdr/hdr_v1.rs @@ -11,8 +11,8 @@ use openssl::nid::Nid; use openssl::pkey::{PKeyRef, Public}; use pv::request::openssl::pkey::{PKey, Private}; use pv::request::{ - gen_ec_key, random_array, Aes256XtsKey, Confidential, EcPubKeyCoord, Encrypt, Keyslot, SymKey, - SymKeyType, Zeroize, SHA_512_HASH_LEN, + gen_ec_key, random_array, Aes256XtsKey, Confidential, EcPubKeyCoord, Encrypt, HostKey, Keyslot, + SymKey, SymKeyType, Zeroize, SHA_512_HASH_LEN, }; use serde::{Deserialize, Serialize}; use utils::HexSlice; @@ -419,7 +419,7 @@ impl KeyExchangeBuilderTrait for SeHdrDataV1 { aead_key: &SymKey, priv_key: &PKeyRef, ) -> Result<()> { - let keyslot = Keyslot::new(hostkey.to_owned()); + let keyslot = Keyslot::new(HostKey::V1(hostkey.to_owned())); let keyslot_bin = keyslot.encrypt(aead_key.value(), priv_key)?.try_into()?; let keyslot_bin_size = u32::try_from(size_of_val(&keyslot_bin)).unwrap(); self.aad.keyslots.push(keyslot_bin); diff --git a/rust/pvsecret/src/cmd/create.rs b/rust/pvsecret/src/cmd/create.rs index 345ddb99..8cc890f4 100644 --- a/rust/pvsecret/src/cmd/create.rs +++ b/rust/pvsecret/src/cmd/create.rs @@ -13,7 +13,9 @@ use pv::misc::{ try_parse_u128, try_parse_u64, write, }; use pv::request::openssl::pkey::{PKey, Private}; -use pv::request::{openssl, BootHdrTags, PolicyReference, ReqEncrCtx, Request, SymKeyType}; +use pv::request::{ + openssl, BootHdrTags, HostKey, PolicyReference, ReqEncrCtx, Request, SymKeyType, +}; use pv::secret::{AddSecretFlags, AddSecretRequest, AddSecretVersion, ExtSecret, GuestSecret}; use pv::uv::ConfigUid; use serde_yaml::Value; @@ -99,7 +101,7 @@ pub fn create(opt: &CreateSecretOpt) -> Result<()> { opt.certificate_args .get_verified_hkds("secret")? .into_iter() - .for_each(|k| asrcb.add_hostkey(k)); + .for_each(|k| asrcb.add_hostkey(HostKey::V1(k))); debug!("Added all host-keys");