mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
pvsecret: Use hybrid keys
Allow the creation of Add-secret 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
9dca2d3181
commit
50808edb7c
@@ -64,8 +64,11 @@ pub enum Error {
|
||||
#[error("No user-key for verification provided and user-data is signed")]
|
||||
BinAsrcbNoUserDataSgnKey,
|
||||
|
||||
#[error("Input does not contain an add-secret request version 1")]
|
||||
BinAsrcbInvVersion,
|
||||
#[error("Input contains an unknown add-secret request version {0}")]
|
||||
BinAsrcbInvVersion(u32),
|
||||
|
||||
#[error("Unsupported add-secret request version: {0}")]
|
||||
UnsupportedAddSecretVersion(u32),
|
||||
|
||||
#[error("Provided user-data key type ({key}) does not match with the user-data ({kind})")]
|
||||
AsrcbUserDataKeyMismatch { key: String, kind: UserDataType },
|
||||
|
||||
@@ -6,31 +6,32 @@ use openssl::md::Md;
|
||||
use openssl::pkey::{PKey, Private};
|
||||
use pv_core::request::RequestVersion;
|
||||
use pv_core::secret::AddSecretMagic;
|
||||
use pv_core::static_assert;
|
||||
use pv_core::uv::SecretId;
|
||||
use zerocopy::{Immutable, IntoBytes};
|
||||
|
||||
use super::guest_secret::ListableSecretHdr;
|
||||
use super::guest_secret::{ListableSecretHdr, SecretAuth};
|
||||
use super::user_data::UserData;
|
||||
use crate::crypto::{hkdf_rfc_5869, AeadEncryptionResult};
|
||||
use crate::misc::Flags;
|
||||
use crate::req::{Aad, BinReqValues, HostKey, Keyslot, ReqEncrCtx};
|
||||
use crate::req::{Aad, BinReqValues, HostKey, Keyslot, ReqEncrCtx, RequestHdr};
|
||||
use crate::request::{BootHdrTags, Confidential, Request};
|
||||
use crate::secret::{ExtSecret, GuestSecret};
|
||||
use crate::uv::{ConfigUid, UvFlags};
|
||||
use crate::{assert_size, Result};
|
||||
use crate::{assert_size, Error, Result};
|
||||
|
||||
/// Authenticated data w/o user data
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, IntoBytes, Immutable)]
|
||||
struct ReqAuthData {
|
||||
struct ReqAuthDataV1 {
|
||||
flags: UvFlags,
|
||||
boot_tags: BootHdrTags,
|
||||
cuid: ConfigUid,
|
||||
reserved90: [u8; 0x100],
|
||||
}
|
||||
assert_size!(ReqAuthData, 0x1e8);
|
||||
assert_size!(ReqAuthDataV1, 0x1e8);
|
||||
|
||||
impl ReqAuthData {
|
||||
impl ReqAuthDataV1 {
|
||||
fn new<F: Into<UvFlags>>(boot_tags: BootHdrTags, flags: F) -> Self {
|
||||
Self {
|
||||
flags: flags.into(),
|
||||
@@ -41,6 +42,30 @@ impl ReqAuthData {
|
||||
}
|
||||
}
|
||||
|
||||
/// Authenticated data w/o user data for v2 header: move up secret header 2
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, IntoBytes, Immutable)]
|
||||
struct ReqAuthDataV2 {
|
||||
flags: UvFlags,
|
||||
boot_tags: BootHdrTags,
|
||||
cuid: ConfigUid,
|
||||
secr_auth: [u8; 0x30],
|
||||
reservedd0: [u8; 0x100 - 0x30],
|
||||
}
|
||||
assert_size!(ReqAuthDataV2, 0x1e8);
|
||||
|
||||
impl ReqAuthDataV2 {
|
||||
fn new<F: Into<UvFlags>>(boot_tags: BootHdrTags, flags: F, secr_auth: [u8; 0x30]) -> Self {
|
||||
Self {
|
||||
flags: flags.into(),
|
||||
boot_tags,
|
||||
cuid: [0; 0x10],
|
||||
secr_auth,
|
||||
reservedd0: [0; 0xd0],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ReqConfData {
|
||||
secret: GuestSecret,
|
||||
@@ -92,6 +117,8 @@ impl From<AddSecretFlags> for UvFlags {
|
||||
pub enum AddSecretVersion {
|
||||
/// Version 1 (= 0x0100)
|
||||
One = 0x0100,
|
||||
/// Version 2 (= 0x0200)
|
||||
Two = 0x0200,
|
||||
|
||||
#[cfg(not(doc))]
|
||||
#[cfg(any(debug_assertions, test))]
|
||||
@@ -99,17 +126,182 @@ pub enum AddSecretVersion {
|
||||
Inv = 0,
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for AddSecretVersion {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: u32) -> Result<Self> {
|
||||
if value == Self::One as u32 {
|
||||
Ok(Self::One)
|
||||
} else if value == Self::Two as u32 {
|
||||
Ok(Self::Two)
|
||||
} else {
|
||||
Err(Error::BinAsrcbInvVersion(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AddSecretVersion> for RequestVersion {
|
||||
fn from(val: AddSecretVersion) -> Self {
|
||||
val as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::request::SeHdrVersion> for AddSecretVersion {
|
||||
fn from(val: crate::request::SeHdrVersion) -> Self {
|
||||
match val {
|
||||
crate::request::SeHdrVersion::One => AddSecretVersion::One,
|
||||
crate::request::SeHdrVersion::Two => AddSecretVersion::Two,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for authenticated data in add-secret requests.
|
||||
///
|
||||
/// This trait provides a common interface for different versions of authenticated data,
|
||||
/// allowing flexible addition of new versions in the future.
|
||||
trait ReqAuthData: IntoBytes + Immutable {
|
||||
/// Get the configuration UID
|
||||
fn cuid(&self) -> &ConfigUid;
|
||||
|
||||
/// Set the configuration UID
|
||||
fn set_cuid(&mut self, cuid: ConfigUid);
|
||||
|
||||
/// Get the boot tags
|
||||
fn boot_tags(&self) -> &BootHdrTags;
|
||||
|
||||
/// Get the flags
|
||||
fn flags(&self) -> &UvFlags;
|
||||
}
|
||||
|
||||
impl ReqAuthData for ReqAuthDataV1 {
|
||||
fn cuid(&self) -> &ConfigUid {
|
||||
&self.cuid
|
||||
}
|
||||
|
||||
fn set_cuid(&mut self, cuid: ConfigUid) {
|
||||
self.cuid = cuid;
|
||||
}
|
||||
|
||||
fn boot_tags(&self) -> &BootHdrTags {
|
||||
&self.boot_tags
|
||||
}
|
||||
|
||||
fn flags(&self) -> &UvFlags {
|
||||
&self.flags
|
||||
}
|
||||
}
|
||||
|
||||
impl ReqAuthData for ReqAuthDataV2 {
|
||||
fn cuid(&self) -> &ConfigUid {
|
||||
&self.cuid
|
||||
}
|
||||
|
||||
fn set_cuid(&mut self, cuid: ConfigUid) {
|
||||
self.cuid = cuid;
|
||||
}
|
||||
|
||||
fn boot_tags(&self) -> &BootHdrTags {
|
||||
&self.boot_tags
|
||||
}
|
||||
|
||||
fn flags(&self) -> &UvFlags {
|
||||
&self.flags
|
||||
}
|
||||
}
|
||||
|
||||
/// Enum holding version-specific authenticated data
|
||||
#[derive(Debug)]
|
||||
enum ReqAuthDataVersion {
|
||||
V1(ReqAuthDataV1),
|
||||
V2(ReqAuthDataV2),
|
||||
}
|
||||
|
||||
impl ReqAuthDataVersion {
|
||||
fn new(
|
||||
version: AddSecretVersion,
|
||||
boot_tags: BootHdrTags,
|
||||
flags: AddSecretFlags,
|
||||
conf_data: &SecretAuth,
|
||||
) -> Result<Self> {
|
||||
Ok(match version {
|
||||
AddSecretVersion::One => Self::V1(ReqAuthDataV1::new(boot_tags, flags)),
|
||||
AddSecretVersion::Two => Self::V2(ReqAuthDataV2::new(
|
||||
boot_tags,
|
||||
flags,
|
||||
conf_data.get(version).try_into().expect(
|
||||
"SecretAuth::get() must return exactly 0x30 bytes for AddSecretVersion::Two",
|
||||
),
|
||||
)),
|
||||
#[cfg(any(debug_assertions, test))]
|
||||
AddSecretVersion::Inv => panic!("Invalid version for production use"),
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn cuid(&self) -> &ConfigUid {
|
||||
match self {
|
||||
Self::V1(v) => v.cuid(),
|
||||
Self::V2(v) => v.cuid(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_cuid(&mut self, cuid: ConfigUid) {
|
||||
match self {
|
||||
Self::V1(v) => v.set_cuid(cuid),
|
||||
Self::V2(v) => v.set_cuid(cuid),
|
||||
}
|
||||
}
|
||||
|
||||
fn boot_tags(&self) -> &BootHdrTags {
|
||||
match self {
|
||||
Self::V1(v) => v.boot_tags(),
|
||||
Self::V2(v) => v.boot_tags(),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn flags(&self) -> &UvFlags {
|
||||
match self {
|
||||
Self::V1(v) => v.flags(),
|
||||
Self::V2(v) => v.flags(),
|
||||
}
|
||||
}
|
||||
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
match self {
|
||||
Self::V1(v) => v.as_bytes(),
|
||||
Self::V2(v) => v.as_bytes(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Add-secret request Control Block
|
||||
///
|
||||
/// An ASRCB wraps a secret to securely transport it to the Ultravisor.
|
||||
///
|
||||
/// Layout:
|
||||
/// Layout V2:
|
||||
/// ```none
|
||||
/// _______________________________________________________________
|
||||
/// | generic header (48)
|
||||
/// | --------------------------------------------------- |
|
||||
/// | Plaintext Add-Secret flags (8) |
|
||||
/// | SE header tags: PLD(64) ALD(64) TLD(64) HeaderTag(16) |
|
||||
/// | Configuration unique ID(16) (Attestation) |
|
||||
/// | Optional, defaults to 0 |
|
||||
/// | Secret header (48) |
|
||||
/// | Reserved(208) |
|
||||
/// | User Data(512) (reserved) |
|
||||
/// | Customer Public Key (160) generated for each request |
|
||||
/// | N Keyslots(1680 each) |
|
||||
/// | --------------------------------------------------- |
|
||||
/// | Secret to add (Secret type dependent)(may be 0 bytes) | Encrypted
|
||||
/// | Extension secret(32) Optional, defaults to 0 | Encrypted
|
||||
/// | --------------------------------------------------- |
|
||||
/// | AES GCM Tag (16) |
|
||||
/// |_____________________________________________________________|
|
||||
/// ```
|
||||
///
|
||||
/// Layout V1:
|
||||
/// ```none
|
||||
/// _______________________________________________________________
|
||||
/// | generic header (48)
|
||||
@@ -133,15 +325,19 @@ impl From<AddSecretVersion> for RequestVersion {
|
||||
#[derive(Debug)]
|
||||
pub struct AddSecretRequest {
|
||||
version: AddSecretVersion,
|
||||
aad: ReqAuthData,
|
||||
aad: ReqAuthDataVersion,
|
||||
keyslots: Vec<Keyslot>,
|
||||
conf: ReqConfData,
|
||||
user_data: UserData,
|
||||
}
|
||||
static_assert!(AddSecretRequest::USER_DATA_OFFS == 0x218);
|
||||
static_assert!(
|
||||
AddSecretRequest::USER_DATA_OFFS == size_of::<RequestHdr>() + size_of::<ReqAuthDataV2>()
|
||||
);
|
||||
|
||||
impl AddSecretRequest {
|
||||
/// Offset of the user-data in the add-secret request in bytes
|
||||
pub(super) const V1_USER_DATA_OFFS: usize = 0x218;
|
||||
pub(super) const USER_DATA_OFFS: usize = size_of::<RequestHdr>() + size_of::<ReqAuthDataV1>();
|
||||
|
||||
/// Create a new add-secret request.
|
||||
///
|
||||
@@ -152,22 +348,23 @@ impl AddSecretRequest {
|
||||
secret: GuestSecret,
|
||||
boot_tags: BootHdrTags,
|
||||
flags: AddSecretFlags,
|
||||
) -> Self {
|
||||
Self {
|
||||
conf: ReqConfData {
|
||||
extension_secret: Confidential::new([0; 32]),
|
||||
secret,
|
||||
},
|
||||
aad: ReqAuthData::new(boot_tags, flags),
|
||||
) -> Result<Self> {
|
||||
let conf = ReqConfData {
|
||||
extension_secret: Confidential::new([0; 32]),
|
||||
secret,
|
||||
};
|
||||
Ok(Self {
|
||||
aad: ReqAuthDataVersion::new(version, boot_tags, flags, &conf.secret.auth())?,
|
||||
conf,
|
||||
keyslots: vec![],
|
||||
version,
|
||||
user_data: UserData::Null,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Sets the Configuration Unique Id of this [`AddSecretRequest`].
|
||||
pub fn set_cuid(&mut self, cuid: ConfigUid) {
|
||||
self.aad.cuid = cuid;
|
||||
self.aad.set_cuid(cuid);
|
||||
}
|
||||
|
||||
/// Sets the extension secret of this [`AddSecretRequest`].
|
||||
@@ -182,7 +379,7 @@ impl AddSecretRequest {
|
||||
ExtSecret::Derived(cck) => hkdf_rfc_5869(
|
||||
Md::sha512(),
|
||||
cck.value(),
|
||||
self.aad.boot_tags.tag(),
|
||||
self.aad.boot_tags().tag(),
|
||||
DER_EXT_SECRET_INFO,
|
||||
)?
|
||||
.into(),
|
||||
@@ -221,7 +418,6 @@ impl AddSecretRequest {
|
||||
let cust_pub_key = ctx.key_coords()?;
|
||||
let secr_auth = self.conf.secret.auth();
|
||||
let user_data = self.user_data.data();
|
||||
|
||||
let mut aad: Vec<Aad> = Vec::with_capacity(5 + self.keyslots.len());
|
||||
aad.push(Aad::Plain(self.aad.as_bytes()));
|
||||
if let Some(data) = user_data.0 {
|
||||
@@ -232,7 +428,13 @@ impl AddSecretRequest {
|
||||
}
|
||||
aad.push(Aad::Plain(cust_pub_key.as_ref()));
|
||||
self.keyslots.iter().for_each(|k| aad.push(Aad::Ks(k)));
|
||||
aad.push(Aad::Plain(secr_auth.get()));
|
||||
// write secret header (1) only for v1
|
||||
match self.version {
|
||||
AddSecretVersion::One => aad.push(Aad::Plain(secr_auth.get(AddSecretVersion::One))),
|
||||
AddSecretVersion::Two => {}
|
||||
#[cfg(any(debug_assertions, test))]
|
||||
_ => return Err(Error::UnsupportedAddSecretVersion(self.version as u32)),
|
||||
}
|
||||
|
||||
ctx.build_aad(self.version.into(), &aad, conf_len, self.user_data.magic())
|
||||
}
|
||||
@@ -273,7 +475,7 @@ impl AddSecretRequest {
|
||||
// add signature to authenticated data starting with USER_DATA_OFFS
|
||||
self.user_data.sign(
|
||||
&mut buf[aad_range.start..encr_range.end],
|
||||
Self::V1_USER_DATA_OFFS,
|
||||
Self::USER_DATA_OFFS,
|
||||
)?;
|
||||
|
||||
// encrypt again with signed data
|
||||
|
||||
@@ -16,6 +16,7 @@ use super::asrcb::AddSecretRequest;
|
||||
use crate::crypto::{hash, random_array, SymKeyType};
|
||||
use crate::request::openssl::{NID_ED25519, NID_ED448};
|
||||
use crate::request::Confidential;
|
||||
use crate::secret::AddSecretVersion;
|
||||
use crate::uv::{
|
||||
AesSizes, AesXtsSizes, EcCurves, HmacShaSizes, ListableSecretType, RetrievableSecret,
|
||||
RetrieveCmd, SecretId,
|
||||
@@ -388,12 +389,18 @@ pub(crate) enum SecretAuth {
|
||||
}
|
||||
|
||||
impl SecretAuth {
|
||||
const NULL_HDR: NullSecretHdr = NullSecretHdr::new();
|
||||
const NULL_HDR_V1: NullSecretHdrV1 = NullSecretHdrV1::new();
|
||||
const NULL_HDR_V2: NullSecretHdrV2 = NullSecretHdrV2::new();
|
||||
const UPDATE_CCK_HDR: UpdateCckHdr = UpdateCckHdr::new();
|
||||
|
||||
pub fn get(&self) -> &[u8] {
|
||||
pub fn get(&self, version: AddSecretVersion) -> &[u8] {
|
||||
match self {
|
||||
Self::Null => Self::NULL_HDR.as_bytes(),
|
||||
Self::Null => match version {
|
||||
AddSecretVersion::One => Self::NULL_HDR_V1.as_bytes(),
|
||||
AddSecretVersion::Two => Self::NULL_HDR_V2.as_bytes(),
|
||||
#[cfg(any(debug_assertions, test))]
|
||||
_ => panic!("Invalid AddSecretVersion"),
|
||||
},
|
||||
Self::Listable(h) => h.as_bytes(),
|
||||
Self::UpdateCck => Self::UPDATE_CCK_HDR.as_bytes(),
|
||||
}
|
||||
@@ -402,15 +409,15 @@ impl SecretAuth {
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, IntoBytes, FromBytes, Immutable, KnownLayout)]
|
||||
struct NullSecretHdr {
|
||||
struct NullSecretHdrV1 {
|
||||
res0: u16,
|
||||
kind: U16<BigEndian>,
|
||||
secret_len: U32<BigEndian>,
|
||||
res8: u64,
|
||||
}
|
||||
assert_size!(NullSecretHdr, 0x10);
|
||||
assert_size!(NullSecretHdrV1, 0x10);
|
||||
|
||||
impl NullSecretHdr {
|
||||
impl NullSecretHdrV1 {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
res0: 0,
|
||||
@@ -421,6 +428,29 @@ impl NullSecretHdr {
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, IntoBytes, FromBytes, Immutable, KnownLayout)]
|
||||
struct NullSecretHdrV2 {
|
||||
res0: u16,
|
||||
kind: U16<BigEndian>,
|
||||
secret_len: U32<BigEndian>,
|
||||
res8: u64,
|
||||
reserved1: [u8; 32],
|
||||
}
|
||||
assert_size!(NullSecretHdrV2, 0x30);
|
||||
|
||||
impl NullSecretHdrV2 {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
res0: 0,
|
||||
kind: U16::new(ListableSecretType::NULL),
|
||||
secret_len: U32::ZERO,
|
||||
res8: 0,
|
||||
reserved1: [0; 32],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, IntoBytes, FromBytes, Immutable, KnownLayout)]
|
||||
pub(crate) struct ListableSecretHdr {
|
||||
@@ -701,7 +731,7 @@ mod test {
|
||||
let gs_bytes = gs.auth();
|
||||
let exp = vec![0u8, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
|
||||
assert_eq!(exp, gs_bytes.get());
|
||||
assert_eq!(exp, gs_bytes.get(AddSecretVersion::One));
|
||||
assert_eq!(&Vec::<u8>::new(), gs.confidential())
|
||||
}
|
||||
|
||||
@@ -716,7 +746,7 @@ mod test {
|
||||
let mut exp = vec![0u8, 0, 0, 2, 0, 0, 0, 0x20, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
exp.extend([1; 32]);
|
||||
|
||||
assert_eq!(exp, gs_bytes_auth.get());
|
||||
assert_eq!(exp, gs_bytes_auth.get(AddSecretVersion::One));
|
||||
assert_eq!(&[2; 32], gs.confidential());
|
||||
}
|
||||
|
||||
@@ -729,7 +759,7 @@ mod test {
|
||||
secret: vec![2; 32].into(),
|
||||
};
|
||||
let auth = gs.auth();
|
||||
let gs_bytes_auth = auth.get();
|
||||
let gs_bytes_auth = auth.get(AddSecretVersion::One);
|
||||
let mut exp = vec![0u8, 0, 0, 3, 0, 0, 0, 0x20, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
exp.extend([1; 32]);
|
||||
|
||||
@@ -746,7 +776,7 @@ mod test {
|
||||
let mut exp = vec![0u8, 0, 0, 0x16, 0, 0, 0, 0x20];
|
||||
exp.extend([0; 40]);
|
||||
|
||||
assert_eq!(exp, gs_bytes_auth.get());
|
||||
assert_eq!(exp, gs_bytes_auth.get(AddSecretVersion::One));
|
||||
assert_eq!(&[2; 32], gs.confidential());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,6 +133,7 @@ impl RetrievedSecret {
|
||||
RetrievedSecret::ProtectedKey(p) => p.into_bytes(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the data in PEM format.
|
||||
///
|
||||
/// # Errors
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::crypto::{sign_msg, verify_signature};
|
||||
use crate::req::BinReqValues;
|
||||
use crate::request::openssl::pkey::{HasParams, HasPublic, Id, PKey, PKeyRef, Private, Public};
|
||||
use crate::request::RequestMagic;
|
||||
use crate::secret::{AddSecretMagic, AddSecretRequest, AddSecretVersion, UserDataType};
|
||||
use crate::secret::{AddSecretMagic, AddSecretRequest, UserDataType};
|
||||
use crate::{assert_size, Error, Result};
|
||||
|
||||
/// User data.
|
||||
@@ -261,21 +261,19 @@ pub fn verify_asrcb_and_get_user_data(
|
||||
// check that the provided buffer contains an Add Secret request
|
||||
let magic = AddSecretMagic::try_from_bytes(&asrcb)?;
|
||||
let req = BinReqValues::get(&asrcb)?;
|
||||
if req.version() != AddSecretVersion::One as u32 {
|
||||
return Err(Error::BinAsrcbInvVersion);
|
||||
}
|
||||
|
||||
// preventing the two lines after the truncate from panicking
|
||||
let req_len = req.len();
|
||||
if asrcb.len() < req_len
|
||||
|| req_len < AddSecretRequest::V1_USER_DATA_OFFS + UserData::USER_DATA_SIZE
|
||||
|| req_len < AddSecretRequest::USER_DATA_OFFS + UserData::USER_DATA_SIZE
|
||||
{
|
||||
return Err(pv_core::Error::NoAsrcb.into());
|
||||
}
|
||||
|
||||
// forget the tag (and all additional data that might be behind the tag)
|
||||
asrcb.truncate(req_len - BinReqValues::TAG_LEN);
|
||||
// get a mutable refrenence on the 512 bytes of user data
|
||||
let (_, user_data) = asrcb.split_at_mut(AddSecretRequest::V1_USER_DATA_OFFS);
|
||||
let (_, user_data) = asrcb.split_at_mut(AddSecretRequest::USER_DATA_OFFS);
|
||||
let user_data = &mut user_data[..UserData::USER_DATA_SIZE];
|
||||
|
||||
// depending on the user_data_type do:
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
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, HostKey, ReqEncrCtx, Request, SymKey};
|
||||
use s390_pv::request::openssl::pkey::PKey;
|
||||
use s390_pv::request::{BootHdrTags, HostKey, HybridPKey, ReqEncrCtx, Request, SymKey};
|
||||
use s390_pv::secret::{
|
||||
verify_asrcb_and_get_user_data, AddSecretFlags, AddSecretRequest, AddSecretVersion, ExtSecret,
|
||||
GuestSecret,
|
||||
};
|
||||
use s390_pv::test_utils::get_test_keys;
|
||||
use s390_pv::test_utils::{get_test_keys, get_test_keys_hybrid, DeterministicTestRandGuard};
|
||||
use s390_pv::uv::ConfigUid;
|
||||
use s390_pv::{get_test_asset, Result};
|
||||
|
||||
@@ -29,8 +29,13 @@ fn create_asrcb(
|
||||
hkd: HostKey,
|
||||
ctx: &ReqEncrCtx,
|
||||
) -> Result<Vec<u8>> {
|
||||
let mut asrcb = AddSecretRequest::new(AddSecretVersion::One, guest_secret, TAGS, flags);
|
||||
let asrcb = match hkd {
|
||||
HostKey::V1(_) => AddSecretRequest::new(AddSecretVersion::One, guest_secret, TAGS, flags),
|
||||
HostKey::V2(_) => AddSecretRequest::new(AddSecretVersion::Two, guest_secret, TAGS, flags),
|
||||
_ => unreachable!("Unknown HostKey version"),
|
||||
};
|
||||
|
||||
let mut asrcb = asrcb?;
|
||||
if let Some(s) = ext_secret {
|
||||
asrcb.set_ext_secret(s)?
|
||||
};
|
||||
@@ -42,8 +47,21 @@ fn create_asrcb(
|
||||
asrcb.encrypt(ctx)
|
||||
}
|
||||
|
||||
fn get_crypto() -> (PKey<Public>, ReqEncrCtx) {
|
||||
fn get_crypto() -> (HostKey, ReqEncrCtx) {
|
||||
let (cust_key, host_key) = get_test_keys();
|
||||
let host_key = HostKey::V1(host_key);
|
||||
let ctx = ReqEncrCtx::new_aes_256(
|
||||
Some([0x55; 12]),
|
||||
Some(cust_key),
|
||||
Some(SymKey::Aes256([0x17; 32].into())),
|
||||
)
|
||||
.unwrap();
|
||||
(host_key, ctx)
|
||||
}
|
||||
|
||||
fn get_crypto_v2() -> (HostKey, ReqEncrCtx) {
|
||||
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),
|
||||
@@ -67,14 +85,25 @@ where
|
||||
true => Some(CUID),
|
||||
false => None,
|
||||
};
|
||||
create_asrcb(
|
||||
guest_secret,
|
||||
ext_secret.into(),
|
||||
flags,
|
||||
cuid,
|
||||
HostKey::V1(host_key),
|
||||
&ctx,
|
||||
)
|
||||
create_asrcb(guest_secret, ext_secret.into(), flags, cuid, host_key, &ctx)
|
||||
}
|
||||
|
||||
fn gen_asrcb_v2<E>(
|
||||
guest_secret: GuestSecret,
|
||||
ext_secret: E,
|
||||
flags: AddSecretFlags,
|
||||
cuid: bool,
|
||||
) -> Result<Vec<u8>>
|
||||
where
|
||||
E: Into<Option<ExtSecret>>,
|
||||
{
|
||||
let _guard = DeterministicTestRandGuard::install(&[0x42; 4096], &[0x11; 32]).unwrap();
|
||||
let (host_key, ctx) = get_crypto_v2();
|
||||
let cuid = match cuid {
|
||||
true => Some(CUID),
|
||||
false => None,
|
||||
};
|
||||
create_asrcb(guest_secret, ext_secret.into(), flags, cuid, host_key, &ctx)
|
||||
}
|
||||
|
||||
fn association() -> GuestSecret {
|
||||
@@ -96,9 +125,21 @@ fn no_flag() -> AddSecretFlags {
|
||||
fn create_signed_asrcb(skey: PKey<Private>, user_data: Vec<u8>) -> Vec<u8> {
|
||||
let (host_key, ctx) = get_crypto();
|
||||
let mut asrcb =
|
||||
AddSecretRequest::new(AddSecretVersion::One, GuestSecret::Null, TAGS, no_flag());
|
||||
AddSecretRequest::new(AddSecretVersion::One, GuestSecret::Null, TAGS, no_flag())
|
||||
.expect("AddSecretRequest::new failed");
|
||||
|
||||
asrcb.add_hostkey(HostKey::V1(host_key));
|
||||
asrcb.add_hostkey(host_key);
|
||||
asrcb.set_user_data(user_data, Some(skey)).unwrap();
|
||||
asrcb.encrypt(&ctx).unwrap()
|
||||
}
|
||||
|
||||
fn create_signed_asrcb_v2(skey: PKey<Private>, user_data: Vec<u8>) -> Vec<u8> {
|
||||
let (host_key, ctx) = get_crypto_v2();
|
||||
let mut asrcb =
|
||||
AddSecretRequest::new(AddSecretVersion::Two, GuestSecret::Null, TAGS, no_flag())
|
||||
.expect("AddSecretRequest::new failed");
|
||||
|
||||
asrcb.add_hostkey(host_key);
|
||||
asrcb.set_user_data(user_data, Some(skey)).unwrap();
|
||||
asrcb.encrypt(&ctx).unwrap()
|
||||
}
|
||||
@@ -108,9 +149,10 @@ fn null_none_default_ncuid_one_user_unsgn() {
|
||||
let user_data_orig = vec![0x56; 0x183];
|
||||
let (host_key, ctx) = get_crypto();
|
||||
let mut asrcb =
|
||||
AddSecretRequest::new(AddSecretVersion::One, GuestSecret::Null, TAGS, no_flag());
|
||||
AddSecretRequest::new(AddSecretVersion::One, GuestSecret::Null, TAGS, no_flag())
|
||||
.expect("AddSecretRequest::new failed");
|
||||
|
||||
asrcb.add_hostkey(HostKey::V1(host_key));
|
||||
asrcb.add_hostkey(host_key);
|
||||
asrcb.set_user_data(user_data_orig.clone(), None).unwrap();
|
||||
let asrcb = asrcb.encrypt(&ctx).unwrap();
|
||||
|
||||
@@ -121,6 +163,7 @@ fn null_none_default_ncuid_one_user_unsgn() {
|
||||
&user_data.as_ref().unwrap()[..user_data_orig.len()]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn null_none_default_ncuid_one_user_ec() {
|
||||
let (usr_sgn_key, _) = get_test_keys();
|
||||
@@ -242,8 +285,9 @@ fn null_none_default_ncuid_one() {
|
||||
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(HostKey::V1(hkd.clone())));
|
||||
AddSecretRequest::new(AddSecretVersion::One, GuestSecret::Null, TAGS, no_flag())
|
||||
.expect("AddSecretRequest::new failed");
|
||||
(0..7).for_each(|_| asrcb.add_hostkey(hkd.clone()));
|
||||
asrcb.set_cuid(CUID);
|
||||
let asrcb = asrcb.encrypt(&ctx).unwrap();
|
||||
|
||||
|
||||
@@ -7,7 +7,8 @@ use std::fmt::Display;
|
||||
use clap::error::ErrorKind::ValueValidation;
|
||||
use clap::{ArgGroup, Args, CommandFactory, Parser, Subcommand, ValueEnum, ValueHint};
|
||||
use utils::{
|
||||
combined_path_opt, combined_path_req, CertificateOptions, DeprecatedVerbosityOptions, STDOUT,
|
||||
combined_path_opt, combined_path_req, AutoOrExplicit, CertificateOptions,
|
||||
DeprecatedVerbosityOptions, HkdVersion, ValueEnumDisplay, ValueEnumFromStr, STDOUT,
|
||||
};
|
||||
|
||||
/// Manage secrets for IBM Secure Execution guests.
|
||||
@@ -34,6 +35,28 @@ pub enum CreateSecretFlags {
|
||||
DisableDump,
|
||||
}
|
||||
|
||||
/// Secure Execution add secret version for CLI
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum, ValueEnumDisplay, ValueEnumFromStr)]
|
||||
pub enum SecretVersion {
|
||||
#[value(name = "1")]
|
||||
/// Version 1 - uses traditional cryptographic keys
|
||||
V1,
|
||||
#[value(name = "2")]
|
||||
/// Version 2 - uses hybrid (post-quantum) cryptographic keys
|
||||
V2,
|
||||
}
|
||||
|
||||
pub type SecretVersionSelection = AutoOrExplicit<SecretVersion>;
|
||||
|
||||
impl From<SecretVersion> for HkdVersion {
|
||||
fn from(val: SecretVersion) -> Self {
|
||||
match val {
|
||||
SecretVersion::V1 => Self::Classical,
|
||||
SecretVersion::V2 => Self::Hybrid,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
#[command(group(ArgGroup::new("as-ext").args(["cck", "extension_secret"])),)]
|
||||
pub struct CreateSecretOpt {
|
||||
@@ -173,6 +196,10 @@ pub struct CreateSecretOpt {
|
||||
/// Ignored for meta-secrets.
|
||||
#[arg(long)]
|
||||
pub use_name: bool,
|
||||
|
||||
/// Specify the Add-secret version to use.
|
||||
#[arg(long = "secret-version", value_name = "VERSION", default_value_t = SecretVersion::V1)]
|
||||
pub secret_version: SecretVersion,
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
|
||||
@@ -8,7 +8,7 @@ pub use create::create;
|
||||
mod verify;
|
||||
pub use verify::verify;
|
||||
|
||||
pub const CMD_FN: &[&str] = &["+create", "+verify"];
|
||||
pub const CMD_FN: &[&str] = &["+create", "+verify", "+quantumsafe"];
|
||||
|
||||
#[cfg(target_arch = "s390x")]
|
||||
mod add;
|
||||
|
||||
@@ -22,7 +22,10 @@ use serde_yaml::Value;
|
||||
use utils::get_writer_from_cli_file_arg;
|
||||
use zerocopy::IntoBytes;
|
||||
|
||||
use crate::cli::{AddSecretType, CreateSecretFlags, CreateSecretOpt, RetrieveableSecretInpKind};
|
||||
use crate::cli::{
|
||||
AddSecretType, CreateSecretFlags, CreateSecretOpt, RetrieveableSecretInpKind, SecretVersion,
|
||||
SecretVersionSelection,
|
||||
};
|
||||
|
||||
fn write_out<P, D>(path: &P, data: D, ctx: &str) -> pv::Result<()>
|
||||
where
|
||||
@@ -83,6 +86,41 @@ fn retrievable(name: &str, secret: &str, kind: &RetrieveableSecretInpKind) -> Re
|
||||
.map_err(Error::from)
|
||||
}
|
||||
|
||||
/// Auto-detect the Add-secret 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]) -> AddSecretVersion {
|
||||
let use_hybrid_keys = host_keys.iter().any(|k: &HostKey| k.is_hybrid());
|
||||
if use_hybrid_keys {
|
||||
AddSecretVersion::Two
|
||||
} else {
|
||||
AddSecretVersion::One
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SecretVersion> for AddSecretVersion {
|
||||
fn from(value: SecretVersion) -> Self {
|
||||
match value {
|
||||
SecretVersion::V1 => Self::One,
|
||||
SecretVersion::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: SecretVersionSelection,
|
||||
host_keys: &[HostKey],
|
||||
) -> AddSecretVersion {
|
||||
match cli_version {
|
||||
SecretVersionSelection::Auto => auto_detect_version(host_keys),
|
||||
SecretVersionSelection::Explicit(att_version) => att_version.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Prepare an add-secret request
|
||||
pub fn create(opt: &CreateSecretOpt) -> Result<()> {
|
||||
if pv_guest_bit_set() {
|
||||
@@ -94,17 +132,9 @@ pub fn create(opt: &CreateSecretOpt) -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
let mut asrcb = build_asrcb(opt)?;
|
||||
let asrcb = build_asrcb(opt)?;
|
||||
debug!("Generated Add-secret request");
|
||||
|
||||
// Add host-key documents
|
||||
opt.certificate_args
|
||||
.get_verified_hkds("secret")?
|
||||
.into_iter()
|
||||
.for_each(|k| asrcb.add_hostkey(HostKey::V1(k)));
|
||||
|
||||
debug!("Added all host-keys");
|
||||
|
||||
// build + encrypt the request
|
||||
let rq =
|
||||
ReqEncrCtx::random(SymKeyType::Aes256Gcm).context("Failed to generate random input")?;
|
||||
@@ -168,9 +198,21 @@ fn build_asrcb(opt: &CreateSecretOpt) -> Result<AddSecretRequest> {
|
||||
debug!("FLAGS: {flags:x?}");
|
||||
|
||||
let mut se_hdr = open_file(&opt.hdr)?;
|
||||
let (tags, _) = BootHdrTags::from_se_image(&mut se_hdr)
|
||||
let (boot_tags, _) = BootHdrTags::from_se_image(&mut se_hdr)
|
||||
.with_context(|| format!("Provided SE-header in '{}' is malformed", &opt.hdr))?;
|
||||
let mut asrcb = AddSecretRequest::new(AddSecretVersion::One, secret, tags, flags);
|
||||
|
||||
let hkds = opt.certificate_args.get_verified_hkds_new(
|
||||
"secret",
|
||||
SecretVersionSelection::Explicit(opt.secret_version).map(|v| v.into()),
|
||||
)?;
|
||||
let secret_version =
|
||||
determine_version(SecretVersionSelection::Explicit(opt.secret_version), &hkds);
|
||||
|
||||
let mut asrcb = AddSecretRequest::new(secret_version, secret, boot_tags, flags)?;
|
||||
|
||||
hkds.into_iter().for_each(|k| asrcb.add_hostkey(k));
|
||||
|
||||
debug!("Added all host-keys");
|
||||
|
||||
// Set CUID
|
||||
read_cuid(&mut asrcb, opt)?;
|
||||
|
||||
Reference in New Issue
Block a user