mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
rust/pv: Add-Secret: Add user-data and user-defined signing keys
User-data can be added to add-secret requests. User-data can be unsigned or containing a signature of the request using a custom private key. The user-data is placed at offset 0x218 and 0x200 bytes long. Reviewed-by: Marc Hartmayer <mhartmay@linux.ibm.com> Signed-off-by: Steffen Eiden <seiden@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
committed by
Jan Höppner
parent
34bef977e8
commit
4990f643c1
+25
-8
@@ -16,7 +16,7 @@ use openssl::{
|
||||
sign::{Signer, Verifier},
|
||||
symm::{encrypt, encrypt_aead, Cipher},
|
||||
};
|
||||
use std::convert::TryInto;
|
||||
use std::{convert::TryInto, ops::Range};
|
||||
|
||||
/// An AES256-key that will purge itself out of the memory when going out of scope
|
||||
///
|
||||
@@ -182,7 +182,12 @@ pub fn encrypt_aes(key: &SymKey, iv: &[u8], conf: &[u8]) -> Result<Vec<u8>> {
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the data could not be encrypted by OpenSSL.
|
||||
pub fn encrypt_aes_gcm(key: &SymKey, iv: &[u8], aad: &[u8], conf: &[u8]) -> Result<Vec<u8>> {
|
||||
pub fn encrypt_aes_gcm(
|
||||
key: &SymKey,
|
||||
iv: &[u8],
|
||||
aad: &[u8],
|
||||
conf: &[u8],
|
||||
) -> Result<(Vec<u8>, Range<usize>, Range<usize>, Range<usize>)> {
|
||||
let mut tag = vec![0xff; AES_256_GCM_TAG_SIZE];
|
||||
let encr = match key {
|
||||
SymKey::Aes256(key) => encrypt_aead(
|
||||
@@ -195,12 +200,24 @@ pub fn encrypt_aes_gcm(key: &SymKey, iv: &[u8], aad: &[u8], conf: &[u8]) -> Resu
|
||||
)?,
|
||||
};
|
||||
|
||||
let mut res = vec![0; aad.len() + encr.len() + 16];
|
||||
res[0..aad.len()].copy_from_slice(aad);
|
||||
res[aad.len()..aad.len() + encr.len()].copy_from_slice(&encr);
|
||||
res[aad.len() + encr.len()..aad.len() + encr.len() + 16].copy_from_slice(&tag);
|
||||
let mut res = vec![0; aad.len() + encr.len() + tag.len()];
|
||||
let aad_range = Range {
|
||||
start: 0,
|
||||
end: aad.len(),
|
||||
};
|
||||
let encr_range = Range {
|
||||
start: aad.len(),
|
||||
end: aad.len() + encr.len(),
|
||||
};
|
||||
let tag_range = Range {
|
||||
start: aad.len() + encr.len(),
|
||||
end: aad.len() + encr.len() + tag.len(),
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
res[aad_range.clone()].copy_from_slice(aad);
|
||||
res[encr_range.clone()].copy_from_slice(&encr);
|
||||
res[tag_range.clone()].copy_from_slice(&tag);
|
||||
Ok((res, aad_range, encr_range, tag_range))
|
||||
}
|
||||
|
||||
/// Calculate the hash of a slice.
|
||||
@@ -367,7 +384,7 @@ mod tests {
|
||||
0xee, 0x62, 0x98, 0xf7, 0x7e, 0x0c,
|
||||
];
|
||||
|
||||
let res = encrypt_aes_gcm(
|
||||
let (res, ..) = encrypt_aes_gcm(
|
||||
&SymKey::Aes256(aes_gcm_key.into()),
|
||||
&aes_gcm_iv,
|
||||
&aes_gcm_aad,
|
||||
|
||||
+7
-2
@@ -16,6 +16,7 @@ use openssl::pkey::{PKey, PKeyRef, Private, Public};
|
||||
use pv_core::request::{RequestMagic, RequestVersion};
|
||||
use std::convert::TryInto;
|
||||
use std::mem::size_of;
|
||||
use std::ops::Range;
|
||||
use utils::assert_size;
|
||||
use zerocopy::{AsBytes, BigEndian, FromBytes, FromZeroes, U32};
|
||||
|
||||
@@ -97,7 +98,7 @@ impl Encrypt for Keyslot {
|
||||
to: &mut Vec<u8>,
|
||||
) -> Result<()> {
|
||||
let derived_key = derive_key(priv_key, &self.0)?;
|
||||
let mut wrpk_and_kst = encrypt_aes_gcm(&derived_key.into(), &[0; 12], &[], prot_key)?;
|
||||
let (mut wrpk_and_kst, ..) = encrypt_aes_gcm(&derived_key.into(), &[0; 12], &[], prot_key)?;
|
||||
let phk: EcdhPubkeyCoord = self.0.as_ref().try_into()?;
|
||||
|
||||
to.reserve(80);
|
||||
@@ -264,7 +265,11 @@ impl ReqEncrCtx {
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the data could not be encrypted by OpenSSL.
|
||||
pub fn encrypt_aead(&self, aad: &[u8], conf: &[u8]) -> Result<Vec<u8>> {
|
||||
pub fn encrypt_aead(
|
||||
&self,
|
||||
aad: &[u8],
|
||||
conf: &[u8],
|
||||
) -> Result<(Vec<u8>, Range<usize>, Range<usize>, Range<usize>)> {
|
||||
encrypt_aes_gcm(&self.prot_key, &self.iv, aad, conf)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
//
|
||||
// Copyright IBM Corp. 2023
|
||||
|
||||
use super::user_data::UserData;
|
||||
use crate::{
|
||||
assert_size,
|
||||
misc::Flags,
|
||||
request::{
|
||||
hkdf_rfc_5869,
|
||||
openssl::{
|
||||
pkey::{PKey, Public},
|
||||
pkey::{PKey, Private, Public},
|
||||
Md,
|
||||
},
|
||||
uvsecret::{ExtSecret, GuestSecret},
|
||||
@@ -17,10 +18,7 @@ use crate::{
|
||||
uv::{ConfigUid, UvFlags},
|
||||
Result,
|
||||
};
|
||||
use pv_core::request::{
|
||||
uvsecret::{AddSecretMagic, UserDataType},
|
||||
RequestVersion,
|
||||
};
|
||||
use pv_core::request::RequestVersion;
|
||||
use zerocopy::AsBytes;
|
||||
|
||||
/// Internal wrapper for Guest Secret, so that we can dump it in the form the UV wants it to be
|
||||
@@ -54,6 +52,7 @@ impl From<GuestSecret> for BinGuestSecret {
|
||||
}
|
||||
}
|
||||
|
||||
/// Authenticated data w/o user data
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, AsBytes)]
|
||||
struct ReqAuthData {
|
||||
@@ -61,9 +60,8 @@ struct ReqAuthData {
|
||||
boot_tags: BootHdrTags,
|
||||
cuid: ConfigUid,
|
||||
reserved90: [u8; 0x100],
|
||||
prog_res190: [u8; 0x200],
|
||||
}
|
||||
assert_size!(ReqAuthData, 0x3e8);
|
||||
assert_size!(ReqAuthData, 0x1e8);
|
||||
|
||||
impl ReqAuthData {
|
||||
fn new<F: Into<UvFlags>>(boot_tags: BootHdrTags, flags: F) -> Self {
|
||||
@@ -72,7 +70,6 @@ impl ReqAuthData {
|
||||
boot_tags,
|
||||
cuid: [0; 0x10],
|
||||
reserved90: [0; 0x100],
|
||||
prog_res190: [0; 0x200],
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -168,15 +165,14 @@ impl From<AddSecretVersion> for RequestVersion {
|
||||
///```
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AddSecretRequest {
|
||||
magic: AddSecretMagic,
|
||||
version: AddSecretVersion,
|
||||
aad: ReqAuthData,
|
||||
keyslots: Vec<Keyslot>,
|
||||
conf: ReqConfData,
|
||||
user_data: UserData,
|
||||
}
|
||||
|
||||
impl AddSecretRequest {
|
||||
#[allow(unused)]
|
||||
/// Offset of the user-data in the add-secret request in bytes
|
||||
pub(super) const V1_USER_DATA_OFFS: usize = 0x218;
|
||||
|
||||
@@ -199,7 +195,7 @@ impl AddSecretRequest {
|
||||
aad: ReqAuthData::new(boot_tags, flags),
|
||||
keyslots: vec![],
|
||||
version,
|
||||
magic: UserDataType::Null.into(),
|
||||
user_data: UserData::Null,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,18 +229,41 @@ impl AddSecretRequest {
|
||||
&self.conf.secret.0
|
||||
}
|
||||
|
||||
/// Add user-data to the Add-Secret request
|
||||
///
|
||||
/// (Signed) user-data is a non-architectual feature. It allows to add arbitrary
|
||||
/// data (message) to the request, that is signed optionally with an user defined key.
|
||||
/// Allowed keys are:
|
||||
/// - no key (up to 512 bytes of message)
|
||||
/// - EC SECP521R1 (up to 256 byte message)
|
||||
/// - RSA 2048 bit (up to 256 byte message)
|
||||
/// - RSA 3072 bit (up to 128 byte message)
|
||||
///
|
||||
/// The signature can be verified during the verification of the secret-request on the target machine.
|
||||
pub fn set_user_data(&mut self, msg: Vec<u8>, skey: Option<PKey<Private>>) -> Result<()> {
|
||||
self.user_data = UserData::new(skey, msg)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// compiles the authenticated area of this request
|
||||
fn aad(&self, ctx: &ReqEncrCtx, conf_len: usize) -> Result<Vec<u8>> {
|
||||
let cust_pub_key = ctx.key_coords()?;
|
||||
let secr_auth = self.conf.secret.dump_auth();
|
||||
let user_data = self.user_data.data();
|
||||
|
||||
let mut aad: Vec<Aad> = Vec::with_capacity(3 + self.keyslots.len());
|
||||
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 {
|
||||
aad.push(Aad::Plain(data));
|
||||
}
|
||||
if let Some(data) = &user_data.1 {
|
||||
aad.push(Aad::Plain(data));
|
||||
}
|
||||
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));
|
||||
|
||||
ctx.build_aad(self.version.into(), &aad, conf_len, self.magic.get())
|
||||
ctx.build_aad(self.version.into(), &aad, conf_len, self.user_data.magic())
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
@@ -263,13 +282,41 @@ impl AddSecretRequest {
|
||||
res.append(&mut vec![0x24; 32]);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// encrypt data, sign request with user-provided signing key, insert signature into aad,
|
||||
/// calculate request tag
|
||||
fn encrypt_with_signed_user_data(&self, ctx: &ReqEncrCtx) -> Result<Vec<u8>> {
|
||||
//encrypt data w/o aead
|
||||
let conf = self.conf.to_bytes();
|
||||
let aad = self.aad(ctx, conf.value().len())?;
|
||||
let (mut buf, aad_range, encr_range, _) = ctx.encrypt_aead(&aad, conf.value())?;
|
||||
|
||||
drop(aad);
|
||||
|
||||
// sign aad+encrypted data (w/o tag) with user signning key
|
||||
// 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,
|
||||
)?;
|
||||
|
||||
// encrypt again with signed data
|
||||
buf[encr_range.clone()].copy_from_slice(conf.value());
|
||||
ctx.encrypt_aead(&buf[aad_range], &buf[encr_range])
|
||||
.map(|(buf, ..)| buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl Request for AddSecretRequest {
|
||||
fn encrypt(&self, ctx: &ReqEncrCtx) -> Result<Vec<u8>> {
|
||||
let conf = self.conf.to_bytes();
|
||||
let aad = self.aad(ctx, conf.value().len())?;
|
||||
ctx.encrypt_aead(&aad, conf.value())
|
||||
match self.user_data {
|
||||
UserData::Null | UserData::Unsigned(_) => {
|
||||
let conf = self.conf.to_bytes();
|
||||
let aad = self.aad(ctx, conf.value().len())?;
|
||||
ctx.encrypt_aead(&aad, conf.value()).map(|(buf, ..)| buf)
|
||||
}
|
||||
_ => self.encrypt_with_signed_user_data(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
fn add_hostkey(&mut self, hostkey: PKey<Public>) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#![allow(unused)]
|
||||
use crate::{
|
||||
crypto::{sign_msg, verify_signature},
|
||||
req::BinReqValues,
|
||||
@@ -491,7 +490,7 @@ mod test {
|
||||
res_192,
|
||||
} = buf_ec;
|
||||
assert_eq!(data, &[0x11u8; 256]);
|
||||
assert_ne!(signature, &[0u8; 139]);
|
||||
assert_ne!(signature, &[0x11u8; 139]);
|
||||
assert_eq!(res_18b, &[0u8; 5]);
|
||||
assert!(sgn_size.get() <= 139);
|
||||
assert_eq!(res_192, &[0u8; 110]);
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2023
|
||||
use openssl::{
|
||||
ec::{EcGroup, EcKey},
|
||||
nid::Nid,
|
||||
pkey::Private,
|
||||
};
|
||||
use pv::{
|
||||
get_test_asset,
|
||||
request::{
|
||||
@@ -86,6 +91,93 @@ fn no_flag() -> AddSecretFlags {
|
||||
AddSecretFlags::default()
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
asrcb.add_hostkey(host_key);
|
||||
asrcb.set_user_data(user_data, Some(skey)).unwrap();
|
||||
asrcb.encrypt(&ctx).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
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());
|
||||
|
||||
asrcb.add_hostkey(host_key);
|
||||
asrcb.set_user_data(user_data_orig.clone(), None).unwrap();
|
||||
let asrcb = asrcb.encrypt(&ctx).unwrap();
|
||||
|
||||
let user_data = verify_asrcb_and_get_user_data(asrcb, None).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
user_data_orig.as_slice(),
|
||||
&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();
|
||||
|
||||
let usr_vrfy_key = usr_sgn_key.ec_key().unwrap();
|
||||
let usr_vrfy_key = usr_vrfy_key.public_key();
|
||||
let usr_vrfy_key = PKey::from_ec_key(
|
||||
EcKey::from_public_key(
|
||||
&EcGroup::from_curve_name(Nid::SECP521R1).unwrap(),
|
||||
usr_vrfy_key,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let user_data_orig = vec![0x56; 0x100];
|
||||
let asrcb = create_signed_asrcb(usr_sgn_key, user_data_orig.clone());
|
||||
|
||||
let user_data = verify_asrcb_and_get_user_data(asrcb, Some(usr_vrfy_key)).unwrap();
|
||||
assert_eq!(
|
||||
user_data_orig.as_slice(),
|
||||
&user_data.as_ref().unwrap()[..user_data_orig.len()]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn null_none_default_ncuid_one_user_rsa2048() {
|
||||
let usr_sgn_key = get_test_asset!("keys/rsa2048key.pem");
|
||||
let usr_sgn_key = PKey::private_key_from_pem(usr_sgn_key).unwrap();
|
||||
let user_data_orig = vec![0x56; 0x100];
|
||||
let asrcb = create_signed_asrcb(usr_sgn_key, user_data_orig.clone());
|
||||
|
||||
let usr_vrfy_key = get_test_asset!("keys/rsa2048key.pub.pem");
|
||||
let usr_vrfy_key = PKey::public_key_from_pem(usr_vrfy_key).unwrap();
|
||||
|
||||
let user_data = verify_asrcb_and_get_user_data(asrcb, Some(usr_vrfy_key)).unwrap();
|
||||
assert_eq!(
|
||||
user_data_orig.as_slice(),
|
||||
&user_data.as_ref().unwrap()[..user_data_orig.len()]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn null_none_default_ncuid_one_user_rsa3072() {
|
||||
let usr_sgn_key = get_test_asset!("keys/rsa3072key.pem");
|
||||
let usr_sgn_key = PKey::private_key_from_pem(usr_sgn_key).unwrap();
|
||||
let user_data_orig = vec![0x56; 0x80];
|
||||
let asrcb = create_signed_asrcb(usr_sgn_key, user_data_orig.clone());
|
||||
|
||||
let usr_vrfy_key = get_test_asset!("keys/rsa3072key.pub.pem");
|
||||
let usr_vrfy_key = PKey::public_key_from_pem(usr_vrfy_key).unwrap();
|
||||
|
||||
let user_data = verify_asrcb_and_get_user_data(asrcb, Some(usr_vrfy_key)).unwrap();
|
||||
assert_eq!(
|
||||
user_data_orig.as_slice(),
|
||||
&user_data.as_ref().unwrap()[..user_data_orig.len()]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn null_none_default_cuid_one() {
|
||||
let asrcb = gen_asrcb(GuestSecret::Null, None, no_flag(), true).unwrap();
|
||||
|
||||
Reference in New Issue
Block a user