rust/pv*: Add support for CCK update

The ultravisor supports a new secret type in `add-secret` to update
the customer communication key (CCK). Support this new secret
type (0x16).

[seiden@linux.ibm.com: Constify CCK Header struct usage]
Acked-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jakob Naucke <naucke@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
Jakob Naucke
2025-04-25 15:49:29 +02:00
committed by Jan Höppner
parent b11bb64732
commit 443652dcd2
6 changed files with 152 additions and 4 deletions
+92 -3
View File
@@ -29,6 +29,7 @@ use zerocopy::{BigEndian, KnownLayout};
use zerocopy::{FromBytes, Immutable, IntoBytes, U16, U32}; use zerocopy::{FromBytes, Immutable, IntoBytes, U16, U32};
const ASSOC_SECRET_SIZE: usize = 32; const ASSOC_SECRET_SIZE: usize = 32;
const CCK_SIZE: usize = 32;
/// Maximum size of a plain-text secret payload (8190) /// Maximum size of a plain-text secret payload (8190)
pub(crate) const MAX_SIZE_PLAIN_PAYLOAD: usize = RetrieveCmd::MAX_SIZE - 2; pub(crate) const MAX_SIZE_PLAIN_PAYLOAD: usize = RetrieveCmd::MAX_SIZE - 2;
static_assert!(MAX_SIZE_PLAIN_PAYLOAD == 8190); static_assert!(MAX_SIZE_PLAIN_PAYLOAD == 8190);
@@ -65,6 +66,14 @@ pub enum GuestSecret {
#[serde(skip)] #[serde(skip)]
secret: Confidential<Vec<u8>>, secret: Confidential<Vec<u8>>,
}, },
/// CCK update
///
/// Create CCK updates using [`GuestSecret::update_cck`]
UpdateCck {
/// Confidential actual CCK (32 bytes)
#[serde(skip)]
secret: Confidential<[u8; CCK_SIZE]>,
},
} }
macro_rules! retr_constructor { macro_rules! retr_constructor {
@@ -136,10 +145,19 @@ impl GuestSecret {
retr_constructor!(#[doc = r"This function will return an error if OpenSSL cannot create a hash or the curve is invalid"] retr_constructor!(#[doc = r"This function will return an error if OpenSSL cannot create a hash or the curve is invalid"]
| #[doc = r"EC PRIVATE Key"] => PKey<Private>, ec); | #[doc = r"EC PRIVATE Key"] => PKey<Private>, ec);
/// Create a new [`GuestSecret::UpdateCck`].
///
/// * `secret` - New CCK.
pub fn update_cck(secret: [u8; CCK_SIZE]) -> Self {
Self::UpdateCck {
secret: secret.into(),
}
}
/// Use the name as ID, do not hash it /// Use the name as ID, do not hash it
pub fn no_hash_name(&mut self) { pub fn no_hash_name(&mut self) {
match self { match self {
Self::Null => (), Self::Null | Self::UpdateCck { .. } => (),
Self::Association { Self::Association {
name, ref mut id, .. name, ref mut id, ..
} }
@@ -155,6 +173,7 @@ impl GuestSecret {
Self::Null => &[], Self::Null => &[],
Self::Association { secret, .. } => secret.value().as_slice(), Self::Association { secret, .. } => secret.value().as_slice(),
Self::Retrievable { secret, .. } => secret.value(), Self::Retrievable { secret, .. } => secret.value(),
Self::UpdateCck { secret, .. } => secret.value(),
} }
} }
@@ -162,7 +181,8 @@ impl GuestSecret {
pub(crate) fn auth(&self) -> SecretAuth { pub(crate) fn auth(&self) -> SecretAuth {
match &self { match &self {
Self::Null => SecretAuth::Null, Self::Null => SecretAuth::Null,
// Panic: every non null secret type is list-able -> no panic Self::UpdateCck { .. } => SecretAuth::UpdateCck,
// Panic: other secret types are list-able -> no panic
listable => { listable => {
SecretAuth::Listable(ListableSecretHdr::from_guest_secret(listable).unwrap()) SecretAuth::Listable(ListableSecretHdr::from_guest_secret(listable).unwrap())
} }
@@ -176,6 +196,7 @@ impl GuestSecret {
Self::Null => ListableSecretType::NULL, Self::Null => ListableSecretType::NULL,
Self::Association { .. } => ListableSecretType::ASSOCIATION, Self::Association { .. } => ListableSecretType::ASSOCIATION,
Self::Retrievable { kind, .. } => kind.into(), Self::Retrievable { kind, .. } => kind.into(),
Self::UpdateCck { .. } => ListableSecretType::UPDATE_CCK,
} }
} }
@@ -185,13 +206,14 @@ impl GuestSecret {
Self::Null => 0, Self::Null => 0,
Self::Association { secret, .. } => secret.value().len() as u32, Self::Association { secret, .. } => secret.value().len() as u32,
Self::Retrievable { secret, .. } => secret.value().len() as u32, Self::Retrievable { secret, .. } => secret.value().len() as u32,
Self::UpdateCck { secret } => secret.value().len() as u32,
} }
} }
/// Returns the ID of the secret type (if any) /// Returns the ID of the secret type (if any)
fn id(&self) -> Option<SecretId> { fn id(&self) -> Option<SecretId> {
match self { match self {
Self::Null => None, Self::Null | Self::UpdateCck { .. } => None,
Self::Association { id, .. } | Self::Retrievable { id, .. } => Some(id.to_owned()), Self::Association { id, .. } | Self::Retrievable { id, .. } => Some(id.to_owned()),
} }
} }
@@ -368,15 +390,18 @@ impl Display for GuestSecret {
pub(crate) enum SecretAuth { pub(crate) enum SecretAuth {
Null, Null,
Listable(ListableSecretHdr), Listable(ListableSecretHdr),
UpdateCck,
} }
impl SecretAuth { impl SecretAuth {
const NULL_HDR: NullSecretHdr = NullSecretHdr::new(); const NULL_HDR: NullSecretHdr = NullSecretHdr::new();
const UPDATE_CCK_HDR: UpdateCckHdr = UpdateCckHdr::new();
pub fn get(&self) -> &[u8] { pub fn get(&self) -> &[u8] {
match self { match self {
Self::Null => Self::NULL_HDR.as_bytes(), Self::Null => Self::NULL_HDR.as_bytes(),
Self::Listable(h) => h.as_bytes(), Self::Listable(h) => h.as_bytes(),
Self::UpdateCck => Self::UPDATE_CCK_HDR.as_bytes(),
} }
} }
} }
@@ -425,6 +450,29 @@ impl ListableSecretHdr {
} }
} }
#[repr(C)]
#[derive(Debug, IntoBytes, Default, Immutable)]
struct UpdateCckHdr {
res0: u16,
kind: U16<BigEndian>,
secret_len: U32<BigEndian>,
res8: u64,
res10: [u8; 0x20],
}
assert_size!(UpdateCckHdr, 0x30);
impl UpdateCckHdr {
const fn new() -> Self {
Self {
res0: 0,
kind: U16::new(ListableSecretType::UPDATE_CCK),
secret_len: U32::new(CCK_SIZE as u32),
res8: 0,
res10: [0; 0x20],
}
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
@@ -484,6 +532,16 @@ mod test {
retr_test!(retr_aes_hmac_256, hmac_sha, 64, HmacSha(HmacSizes::Sha256)); retr_test!(retr_aes_hmac_256, hmac_sha, 64, HmacSha(HmacSizes::Sha256));
retr_test!(retr_aes_hmac_512, hmac_sha, 128, HmacSha(HmacSizes::Sha512)); retr_test!(retr_aes_hmac_512, hmac_sha, 128, HmacSha(HmacSizes::Sha512));
#[test]
fn update_cck() {
let new_cck = [11; 32];
let req = GuestSecret::update_cck(new_cck);
let exp = GuestSecret::UpdateCck {
secret: new_cck.into(),
};
assert_eq!(req, exp);
}
#[test] #[test]
fn plaintext_no_pad() { fn plaintext_no_pad() {
let key = vec![0, 14, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7]; let key = vec![0, 14, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7];
@@ -625,6 +683,24 @@ mod test {
); );
} }
#[test]
fn update_cck_parse() {
let cck = GuestSecret::UpdateCck {
secret: [0; 32].into(),
};
assert_tokens(
&cck,
&[
Token::StructVariant {
name: "GuestSecret",
variant: "UpdateCck",
len: 0,
},
Token::StructVariantEnd,
],
)
}
#[test] #[test]
fn guest_secret_bin_null() { fn guest_secret_bin_null() {
let gs = GuestSecret::Null; let gs = GuestSecret::Null;
@@ -666,4 +742,17 @@ mod test {
assert_eq!(exp, gs_bytes_auth); assert_eq!(exp, gs_bytes_auth);
assert_eq!(&[2; 32], gs.confidential()); assert_eq!(&[2; 32], gs.confidential());
} }
#[test]
fn guest_secret_bin_cck() {
let gs = GuestSecret::UpdateCck {
secret: [2; 32].into(),
};
let gs_bytes_auth = gs.auth();
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!(&[2; 32], gs.confidential());
}
} }
+4
View File
@@ -398,6 +398,7 @@ pub enum ListableSecretType {
/// ///
/// 0 is reserved /// 0 is reserved
/// 1 is Null secret, with no id and not list-able /// 1 is Null secret, with no id and not list-able
/// 21 is Update CCK secret, with no id and not list-able
Invalid(u16), Invalid(u16),
/// Unknown secret type /// Unknown secret type
Unknown(u16), Unknown(u16),
@@ -436,6 +437,8 @@ impl ListableSecretType {
pub const ECDSA_ED25519_KEY: u16 = 0x0014; pub const ECDSA_ED25519_KEY: u16 = 0x0014;
/// UV secret-type id for an ed448-private-key secret /// UV secret-type id for an ed448-private-key secret
pub const ECDSA_ED448_KEY: u16 = 0x0015; pub const ECDSA_ED448_KEY: u16 = 0x0015;
/// UV secret-type id for a new customer communication key
pub const UPDATE_CCK: u16 = 0x0016;
} }
impl Display for ListableSecretType { impl Display for ListableSecretType {
@@ -482,6 +485,7 @@ impl From<u16> for ListableSecretType {
Self::ECDSA_P521_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Secp521R1)), Self::ECDSA_P521_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Secp521R1)),
Self::ECDSA_ED25519_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Ed25519)), Self::ECDSA_ED25519_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Ed25519)),
Self::ECDSA_ED448_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Ed448)), Self::ECDSA_ED448_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Ed448)),
Self::UPDATE_CCK => Self::Invalid(Self::UPDATE_CCK),
n => Self::Unknown(n), n => Self::Unknown(n),
} }
} }
@@ -0,0 +1,35 @@
.\" Copyright 2025 IBM Corp.
.\" s390-tools is free software; you can redistribute it and/or modify
.\" it under the terms of the MIT license. See LICENSE for details.
.\"
.TH "PVSECRET-CREATE-UPDATE-CCK" "1" "2025-02-19" "s390-tools" "UV-Secret Manual"
.nh
.ad l
.SH NAME
pvsecret-create-update-cck \- Update customer communication key.
.SH SYNOPSIS
.nf
.fam C
pvsecret create update-cck [OPTIONS] \-\-secret <CCK\-FILE>
.fam C
.fi
.SH DESCRIPTION
Insert a customer communication key into a guest.
.SH OPTIONS
.PP
\-\-secret <CCK\-FILE>
.RS 4
Use CCK\-FILE as new CCK
.RE
.RE
.PP
\-h, \-\-help
.RS 4
Print help (see a summary with \fB\-h\fR).
.RE
.RE
.SH "SEE ALSO"
.sp
\fBpvsecret\fR(1) \fBpvsecret-create\fR(1)
+6 -1
View File
@@ -3,7 +3,7 @@
.\" it under the terms of the MIT license. See LICENSE for details. .\" it under the terms of the MIT license. See LICENSE for details.
.\" .\"
.TH "PVSECRET-CREATE" "1" "2024-12-19" "s390-tools" "UV-Secret Manual" .TH "PVSECRET-CREATE" "1" "2025-04-25" "s390-tools" "UV-Secret Manual"
.nh .nh
.ad l .ad l
.SH NAME .SH NAME
@@ -47,6 +47,11 @@ Create an association secret
Create a retrievable secret Create a retrievable secret
.RE .RE
\fBpvsecret create-update-cck(1)\fR
.RS 4
Update customer communication key
.RE
.SH OPTIONS .SH OPTIONS
.PP .PP
\-k, \-\-host\-key\-document <FILE> \-k, \-\-host\-key\-document <FILE>
+12
View File
@@ -217,6 +217,16 @@ pub enum AddSecretType {
#[arg(long = "type", value_name = "TYPE")] #[arg(long = "type", value_name = "TYPE")]
kind: RetrieveableSecretInpKind, kind: RetrieveableSecretInpKind,
}, },
/// Update customer communication key.
///
/// Insert a customer communication key into a guest.
#[command(visible_alias = "cck")]
UpdateCck {
/// Use CCK-FILE as new CCK.
#[arg(long, value_name = "CCK-FILE", value_hint = ValueHint::FilePath)]
secret: String,
},
} }
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)] #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)]
@@ -474,6 +484,7 @@ mod test {
vec!["pvsecret", "add"], vec!["pvsecret", "add"],
vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "meta"], vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "meta"],
vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "association", "name" ], vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "association", "name" ],
vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "update-cck", "--secret", "abc"],
// verify that arguments stay backwards compatible // verify that arguments stay backwards compatible
vec!["pvsecret", "create", "-k", "abc,cdef", "--hdr", "abc", "-o", "abc", "-C", "uuu,ggg", "--crl", "yyy,hhh", "--root-ca", "tttt", vec!["pvsecret", "create", "-k", "abc,cdef", "--hdr", "abc", "-o", "abc", "-C", "uuu,ggg", "--crl", "yyy,hhh", "--root-ca", "tttt",
"--extension-secret", "fff", "--cuid", "cuid", "--flags", "disable-dump", "meta"], "--extension-secret", "fff", "--cuid", "cuid", "--flags", "disable-dump", "meta"],
@@ -501,6 +512,7 @@ mod test {
vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "--cuid", "abc", "--cuid_hex", "9", "null"], vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "--cuid", "abc", "--cuid_hex", "9", "null"],
vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "association"], vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "association"],
vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "association", "name", "--output-secret", "secret", "--input-secret", "secret"], vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "association", "name", "--output-secret", "secret", "--input-secret", "secret"],
vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "update-cck"],
]; ];
for arg in valid_args { for arg in valid_args {
let res = CliOptions::try_parse_from(&arg); let res = CliOptions::try_parse_from(&arg);
+3
View File
@@ -109,6 +109,9 @@ fn build_asrcb(opt: &CreateSecretOpt) -> Result<AddSecretRequest> {
AddSecretType::Retrievable { AddSecretType::Retrievable {
name, secret, kind, .. name, secret, kind, ..
} => retrievable(name, secret, kind)?, } => retrievable(name, secret, kind)?,
AddSecretType::UpdateCck { secret } => {
GuestSecret::update_cck(read_exact_file(secret, "CCK file")?)
}
}; };
trace!("AddSecret: {secret:x?}"); trace!("AddSecret: {secret:x?}");