mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
rust/pv: User-data signing and verifying
Add the ability to generate signed user-data and to verify the signature. 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
f36c34038b
commit
34bef977e8
@@ -21,6 +21,7 @@ use std::convert::TryInto;
|
||||
/// An AES256-key that will purge itself out of the memory when going out of scope
|
||||
///
|
||||
pub type Aes256Key = Secret<[u8; 32]>;
|
||||
pub(crate) const AES_256_GCM_TAG_SIZE: usize = 16;
|
||||
|
||||
/// Types of symmetric keys, to specify during construction.
|
||||
///
|
||||
@@ -182,7 +183,7 @@ pub fn encrypt_aes(key: &SymKey, iv: &[u8], conf: &[u8]) -> Result<Vec<u8>> {
|
||||
///
|
||||
/// 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>> {
|
||||
let mut tag = vec![0xff; 16];
|
||||
let mut tag = vec![0xff; AES_256_GCM_TAG_SIZE];
|
||||
let encr = match key {
|
||||
SymKey::Aes256(key) => encrypt_aead(
|
||||
Cipher::aes_256_gcm(),
|
||||
|
||||
@@ -38,6 +38,36 @@ pub enum Error {
|
||||
#[error("Verifying signatures is only supported for EC and RSA keys")]
|
||||
UnsupportedVerificationKey,
|
||||
|
||||
#[error("Provided binary request is too small")]
|
||||
BinRequestSmall,
|
||||
|
||||
#[error("No Config UID found: {0}")]
|
||||
NoCuid(String),
|
||||
|
||||
// errors from request types
|
||||
#[error("Customer Communication Key must be 32 bytes long")]
|
||||
CckSize,
|
||||
|
||||
#[error("Invalid {0} user-data for signing provided. Max {} bytes allowed", .0.max())]
|
||||
AsrcbInvSgnUserData(UserDataType),
|
||||
|
||||
#[error("Unsupported user data signing key provided. Only EC(secp521r1) and RSA(2048 & 3072 bit) are supported")]
|
||||
BinAsrcbUnsupportedUserDataSgnKey,
|
||||
|
||||
#[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("Provided user-data key type ({key}) does not match with the user-data ({kind})")]
|
||||
AsrcbUserDataKeyMismatch { key: String, kind: UserDataType },
|
||||
|
||||
#[error(
|
||||
"The user-defined request signature could not be verified with the provided certificate"
|
||||
)]
|
||||
AsrcbUserDataSgnFail,
|
||||
|
||||
// errors from other crates
|
||||
#[error(transparent)]
|
||||
PvCore(#[from] pv_core::Error),
|
||||
@@ -96,3 +126,5 @@ macro_rules! bail_hkd_verify {
|
||||
};
|
||||
}
|
||||
pub(crate) use bail_hkd_verify;
|
||||
|
||||
use crate::request::uvsecret::UserDataType;
|
||||
|
||||
@@ -65,7 +65,7 @@ pub mod request {
|
||||
pub use crate::crypto::{hash, hkdf_rfc_5869};
|
||||
pub use crate::crypto::{sign_msg, verify_signature};
|
||||
pub use crate::crypto::{Aes256Key, SymKey, SymKeyType};
|
||||
pub use crate::req::{Aad, Encrypt, Keyslot, ReqEncrCtx, Request};
|
||||
pub use crate::req::{Aad, BinReqValues, Encrypt, Keyslot, ReqEncrCtx, Request};
|
||||
pub use crate::secret::{Secret, Zeroize};
|
||||
pub use crate::verify::{CertVerifier, HkdVerifier, NoVerifyHkd};
|
||||
|
||||
@@ -83,6 +83,7 @@ pub mod request {
|
||||
asrcb::{AddSecretFlags, AddSecretRequest, AddSecretVersion},
|
||||
ext_secret::ExtSecret,
|
||||
guest_secret::GuestSecret,
|
||||
user_data::verify_asrcb_and_get_user_data,
|
||||
};
|
||||
pub use pv_core::request::uvsecret::AddSecretMagic;
|
||||
pub use pv_core::request::uvsecret::UserDataType;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
//
|
||||
// Copyright IBM Corp. 2023
|
||||
|
||||
use crate::crypto::AES_256_GCM_TAG_SIZE;
|
||||
use crate::misc::to_u32;
|
||||
use crate::request::{
|
||||
derive_key, encrypt_aes, encrypt_aes_gcm, gen_ec_key, random_array, SymKey, SymKeyType,
|
||||
@@ -14,6 +15,8 @@ use openssl::hash::{hash, MessageDigest};
|
||||
use openssl::pkey::{PKey, PKeyRef, Private, Public};
|
||||
use pv_core::request::{RequestMagic, RequestVersion};
|
||||
use std::convert::TryInto;
|
||||
use std::mem::size_of;
|
||||
use utils::assert_size;
|
||||
use zerocopy::{AsBytes, BigEndian, FromBytes, FromZeroes, U32};
|
||||
|
||||
/// Encrypt a _secret_ using self and a given private key.
|
||||
@@ -322,6 +325,7 @@ struct RequestHdr {
|
||||
reserved28: u32,
|
||||
sea: U32<BigEndian>,
|
||||
}
|
||||
assert_size!(RequestHdr, 48);
|
||||
|
||||
impl RequestHdr {
|
||||
fn new(rqvn: u32, rql: u32, iv: [u8; 12], nks: u8, sea: u32, magic: Option<[u8; 8]>) -> Self {
|
||||
@@ -380,6 +384,67 @@ pub trait Request {
|
||||
fn add_hostkey(&mut self, hostkey: PKey<Public>);
|
||||
}
|
||||
|
||||
/// A struct to represent some parts of a binary/encrypted request.
|
||||
#[derive(Debug)]
|
||||
#[allow(unused)]
|
||||
#[allow(clippy::len_without_is_empty)]
|
||||
pub struct BinReqValues<'a> {
|
||||
iv: &'a [u8],
|
||||
aad: &'a [u8],
|
||||
req_dep_aad: &'a [u8],
|
||||
encr: &'a [u8],
|
||||
tag: &'a [u8],
|
||||
version: u32,
|
||||
len: usize,
|
||||
}
|
||||
impl<'a> BinReqValues<'a> {
|
||||
pub(crate) const TAG_LEN: usize = AES_256_GCM_TAG_SIZE;
|
||||
|
||||
/// Get the locations from this request.
|
||||
///
|
||||
/// Does minimal sanity test, just tests to prevent panics.
|
||||
/// `req` may be larger than the actual request.
|
||||
pub fn get(req: &'a [u8]) -> Result<Self> {
|
||||
let hdr = RequestHdr::read_from_prefix(req).ok_or(Error::BinRequestSmall)?;
|
||||
let rql = hdr.rql.get() as usize;
|
||||
let sea = hdr.sea.get() as usize;
|
||||
|
||||
if rql < req.len() || sea + Self::TAG_LEN > rql {
|
||||
return Err(Error::BinRequestSmall);
|
||||
}
|
||||
let aad_size = rql - sea - Self::TAG_LEN;
|
||||
if aad_size < size_of::<RequestHdr>() {
|
||||
return Err(Error::BinRequestSmall);
|
||||
}
|
||||
|
||||
let iv = &req[0x10..0x1c];
|
||||
let aad = &req[..aad_size];
|
||||
let req_dep_aad = &req[size_of::<RequestHdr>()..aad_size];
|
||||
let encr = &req[aad_size..(aad_size + sea)];
|
||||
let tag = &req[rql - Self::TAG_LEN..];
|
||||
|
||||
Ok(Self {
|
||||
iv,
|
||||
aad,
|
||||
req_dep_aad,
|
||||
encr,
|
||||
tag,
|
||||
version: hdr.rqvn.get(),
|
||||
len: rql,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the version of this [`BinReqValues`].
|
||||
pub fn version(&self) -> u32 {
|
||||
self.version
|
||||
}
|
||||
|
||||
/// Returns the length of this [`BinReqValues`].
|
||||
pub fn len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -10,3 +10,4 @@
|
||||
pub mod asrcb;
|
||||
pub mod ext_secret;
|
||||
pub mod guest_secret;
|
||||
pub mod user_data;
|
||||
|
||||
@@ -176,6 +176,10 @@ pub struct AddSecretRequest {
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/// Create a new add-secret request.
|
||||
///
|
||||
/// The request has no extension secret, no configuration UID, no host-keys,
|
||||
|
||||
612
rust/pv/src/uvsecret/user_data.rs
Normal file
612
rust/pv/src/uvsecret/user_data.rs
Normal file
@@ -0,0 +1,612 @@
|
||||
#![allow(unused)]
|
||||
use crate::{
|
||||
crypto::{sign_msg, verify_signature},
|
||||
req::BinReqValues,
|
||||
request::{
|
||||
openssl::{
|
||||
pkey::{PKey, Private},
|
||||
MessageDigest,
|
||||
},
|
||||
uvsecret::{AddSecretRequest, AddSecretVersion},
|
||||
RequestMagic,
|
||||
},
|
||||
Error, Result,
|
||||
};
|
||||
use openssl::{
|
||||
nid::Nid,
|
||||
pkey::{HasParams, HasPublic, Id, PKeyRef, Public},
|
||||
};
|
||||
use pv_core::request::uvsecret::AddSecretMagic;
|
||||
use pv_core::request::uvsecret::UserDataType;
|
||||
use utils::assert_size;
|
||||
use zerocopy::{AsBytes, BigEndian, FromBytes, FromZeroes, U16};
|
||||
|
||||
/// User data.
|
||||
///
|
||||
/// User defined data can be:
|
||||
/// - 512 bytes arbitrary data
|
||||
/// - 256 bytes arbitrary data + EC(secp521r1) signature
|
||||
/// ```none
|
||||
/// LAYOUT
|
||||
/// |------------------------|
|
||||
/// | user-data (256) |
|
||||
/// | ec signature (139) |
|
||||
/// | reserved (5) |
|
||||
/// | signature size (2) (BE)|
|
||||
/// | reserved (110) |
|
||||
/// |------------------------|
|
||||
/// ```
|
||||
/// - 256 bytes arbitrary data + RSA2048 signature
|
||||
/// ```none
|
||||
/// LAYOUT
|
||||
/// |---------------------|
|
||||
/// | user-data (256) |
|
||||
/// | rsa signature (256) |
|
||||
/// |---------------------|
|
||||
/// ```
|
||||
/// - 128 bytes arbitrary data + RSA3072 signature
|
||||
/// ```none
|
||||
/// LAYOUT
|
||||
/// |---------------------|
|
||||
/// | user-data (128) |
|
||||
/// | rsa signature (384) |
|
||||
/// |---------------------|
|
||||
/// ```
|
||||
///
|
||||
/// Ensures that the data+signature fits into 512 bytes
|
||||
/// must be created via functions!
|
||||
#[derive(Debug, Clone)]
|
||||
pub(super) enum UserData {
|
||||
Null,
|
||||
Unsigned(Vec<u8>),
|
||||
Signed(SignedUserData),
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, AsBytes, FromBytes, FromZeroes)]
|
||||
struct EcUserData {
|
||||
data: [u8; 256],
|
||||
signature: [u8; EC_SIGN_MAX_SIZE],
|
||||
res_18b: [u8; 5],
|
||||
sgn_size: U16<BigEndian>,
|
||||
res_192: [u8; 110],
|
||||
}
|
||||
assert_size!(EcUserData, USER_DATA_SIZE);
|
||||
const USER_DATA_SIZE: usize = 0x200;
|
||||
const EC_SIGN_MAX_SIZE: usize = 139;
|
||||
|
||||
impl EcUserData {
|
||||
// Sets the signature to this data.
|
||||
//
|
||||
//# Panic
|
||||
// Panics if `sgn` is longer than 139 bytes
|
||||
fn set_signature(&mut self, sgn: &[u8]) {
|
||||
debug_assert!(sgn.len() <= EC_SIGN_MAX_SIZE);
|
||||
self.signature.fill(0);
|
||||
self.signature[..sgn.len()].copy_from_slice(sgn);
|
||||
|
||||
self.res_18b.fill(0);
|
||||
self.sgn_size = (sgn.len() as u16).into();
|
||||
self.res_192.fill(0);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(super) struct SignedUserData {
|
||||
sign_key: PKey<Private>,
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl UserData {
|
||||
const USER_DATA_SIZE: usize = 0x200;
|
||||
|
||||
fn user_data_type<P: HasPublic>(sign_key: &PKeyRef<P>) -> Result<UserDataType> {
|
||||
fn check_curve<P: HasParams>(pkey: &PKeyRef<P>) -> Result<bool> {
|
||||
let nid = pkey.ec_key()?.group().curve_name();
|
||||
match nid {
|
||||
Some(nid) => Ok(nid == Nid::SECP521R1),
|
||||
None => Ok(false),
|
||||
}
|
||||
}
|
||||
match sign_key.id() {
|
||||
Id::EC if check_curve(sign_key)? => Ok(UserDataType::SgnEcSECP521R1),
|
||||
Id::RSA if sign_key.rsa()?.size() == 2048 / 8 => Ok(UserDataType::SgnRsa2048),
|
||||
Id::RSA if sign_key.rsa()?.size() == 3072 / 8 => Ok(UserDataType::SgnRsa3072),
|
||||
_ => Err(Error::BinAsrcbUnsupportedUserDataSgnKey),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn magic(&self) -> RequestMagic {
|
||||
let magic: AddSecretMagic = self.data_type().into();
|
||||
magic.get()
|
||||
}
|
||||
/// Creates new user data
|
||||
///
|
||||
/// Verifies that the provided data + signature fits into 512 bytes
|
||||
///
|
||||
/// #Error
|
||||
/// An error is reported if the provided data and the signature would not fit into 512 bytes
|
||||
/// An error is reported if the key is not of type RSA (2048|3072) or EC(specp521r1)
|
||||
pub(super) fn new(sign_key: Option<PKey<Private>>, data: Vec<u8>) -> Result<Self> {
|
||||
let sign_key = match sign_key {
|
||||
None => {
|
||||
return match data.len() > UserDataType::Unsigned.max() {
|
||||
true => Err(Error::AsrcbInvSgnUserData(UserDataType::Unsigned)),
|
||||
false => Ok(Self::Unsigned(data)),
|
||||
};
|
||||
}
|
||||
Some(skey) => skey,
|
||||
};
|
||||
|
||||
let kind = Self::user_data_type(&sign_key)?;
|
||||
|
||||
// does the data fit into the arbitrary buffer?
|
||||
if data.len() > kind.max() {
|
||||
return Err(Error::AsrcbInvSgnUserData(kind));
|
||||
}
|
||||
|
||||
Ok(Self::Signed(SignedUserData { sign_key, data }))
|
||||
}
|
||||
|
||||
/// Signs data in buf, writes signature to buf+user_data_offset+sign_offset if applicable.
|
||||
///
|
||||
/// Uses [`MessageDigest::sha512`] as digest. Does not modify the abritary user data buffer.
|
||||
///
|
||||
/// * buf: user data buffer, must be at least 512 bytes long
|
||||
///
|
||||
/// # Panic
|
||||
/// panics if `buf` is smaller than 512 bytes
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns an error if signature could not be calculated.
|
||||
/// It is considered no error if no signature is required by user data type
|
||||
pub(super) fn sign(&self, buf: &mut [u8], user_data_offset: usize) -> Result<()> {
|
||||
// get signing info or return if no signature is required
|
||||
let signed_data = match self {
|
||||
UserData::Null | UserData::Unsigned(_) => return Ok(()),
|
||||
UserData::Signed(s) => s,
|
||||
};
|
||||
debug_assert!(buf.len() >= USER_DATA_SIZE);
|
||||
|
||||
// clear the signature area
|
||||
let sgn_offset = user_data_offset + self.data_type().max();
|
||||
buf[sgn_offset..user_data_offset + USER_DATA_SIZE].fill(0);
|
||||
|
||||
// calculate signature
|
||||
let sgn = sign_msg(&signed_data.sign_key, MessageDigest::sha512(), buf)?;
|
||||
|
||||
// insert signature
|
||||
if let UserDataType::SgnEcSECP521R1 = self.data_type() {
|
||||
// Panic: will not panic buffer is 512+ bytes long
|
||||
let buf_ec = EcUserData::mut_from_prefix(&mut buf[user_data_offset..]).unwrap();
|
||||
buf_ec.set_signature(&sgn);
|
||||
} else {
|
||||
// Panic: will not panic buffer is 512+ bytes long
|
||||
buf[sgn_offset..sgn_offset + sgn.len()].copy_from_slice(&sgn);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn data_type(&self) -> UserDataType {
|
||||
match self {
|
||||
Self::Null => UserDataType::Null,
|
||||
Self::Unsigned(_) => UserDataType::Unsigned,
|
||||
Self::Signed(data) => Self::user_data_type(&data.sign_key).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
/// returns a slice for the abitraty user data as first tuple part if User data is available
|
||||
/// the second part contains a vector, created on the fly, which contains enough zeros to fill
|
||||
/// the missing bytes to fill 512 bytes of space or None if the first slice already contains
|
||||
/// 512 bytes
|
||||
pub(super) fn data(&self) -> (Option<&[u8]>, Option<Vec<u8>>) {
|
||||
let buf = match self {
|
||||
UserData::Null => None,
|
||||
UserData::Unsigned(d) => Some(d),
|
||||
UserData::Signed(SignedUserData { data, .. }) => Some(data),
|
||||
};
|
||||
|
||||
let remaining_size = Self::USER_DATA_SIZE - buf.map(|b| b.len()).unwrap_or(0);
|
||||
let remaining = match remaining_size > 0 {
|
||||
true => Some(vec![0; remaining_size]),
|
||||
false => None,
|
||||
};
|
||||
|
||||
(buf.map(|b| b.as_ref()), remaining)
|
||||
}
|
||||
}
|
||||
|
||||
fn format_vrfy_key(key: &PKeyRef<Public>) -> String {
|
||||
let id = key.id();
|
||||
match key.rsa() {
|
||||
Ok(key) => format!("RSA {}", key.size() * 8),
|
||||
Err(_) if id == Id::EC => "EC".to_string(),
|
||||
Err(_) => "Unknown".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_key_format(kind: UserDataType, key: &PKeyRef<Public>) -> Result<()> {
|
||||
let other_kind =
|
||||
UserData::user_data_type(key).map_err(|_| Error::AsrcbUserDataKeyMismatch {
|
||||
key: format_vrfy_key(key),
|
||||
kind,
|
||||
})?;
|
||||
if other_kind == kind {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::AsrcbUserDataKeyMismatch {
|
||||
key: format_vrfy_key(key),
|
||||
kind,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify the user data contained in the add-secret request.
|
||||
///
|
||||
/// First checks that the provided data contains a sound add-secret request.
|
||||
/// Then performs the inverse action that happened during the add-secret generation with user-data
|
||||
/// signature:
|
||||
/// - extract and replace the signature with zeros
|
||||
/// - verify the signature of the request until, but not including the request tag
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Extracrted user-data if available
|
||||
///
|
||||
/// #Errors
|
||||
///
|
||||
/// returns an error if
|
||||
/// - No sound add-secret request presented
|
||||
/// - Sinned user-data indicated, but no key provided
|
||||
/// - Another keytype provided than indicated in the request
|
||||
/// - Signature could not be verified by the provided key
|
||||
/// - any OpenSSL error that might happen during the verification process
|
||||
pub fn verify_asrcb_and_get_user_data(
|
||||
mut asrcb: Vec<u8>,
|
||||
key: Option<PKey<Public>>,
|
||||
) -> Result<Option<Vec<u8>>> {
|
||||
// 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
|
||||
{
|
||||
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 = &mut user_data[..UserData::USER_DATA_SIZE];
|
||||
|
||||
// depending on the user_data_type do:
|
||||
// Null -> exit w/o user data
|
||||
// Unsigned -> exit return all user data
|
||||
// Signed ->
|
||||
// - check that provided key matches user data keytype
|
||||
// - extract user data& signature
|
||||
let (key, user_data) = match (key, magic.kind()) {
|
||||
(_, UserDataType::Null) => return Ok(None),
|
||||
(None, UserDataType::Unsigned) => return Ok(Some(user_data.to_vec())),
|
||||
(Some(key), UserDataType::Unsigned) => {
|
||||
return Err(Error::AsrcbUserDataKeyMismatch {
|
||||
key: format_vrfy_key(&key),
|
||||
kind: UserDataType::Unsigned,
|
||||
})
|
||||
}
|
||||
(Some(key), _) => {
|
||||
check_key_format(magic.kind(), &key)?;
|
||||
(key, VerifiedUserData::new(user_data, magic.kind()))
|
||||
}
|
||||
(None, _) => return Err(Error::BinAsrcbNoUserDataSgnKey),
|
||||
};
|
||||
|
||||
match verify_signature(&key, MessageDigest::sha512(), &asrcb, user_data.signature())? {
|
||||
false => Err(Error::AsrcbUserDataSgnFail),
|
||||
true => Ok(Some(user_data.into())),
|
||||
}
|
||||
}
|
||||
|
||||
// Internal representation of the 512 bytes of user-data, signing-algorithm agnostic
|
||||
struct VerifiedUserData {
|
||||
data: Vec<u8>,
|
||||
signature: Vec<u8>,
|
||||
}
|
||||
|
||||
impl VerifiedUserData {
|
||||
/// Reads user-data from buf depending on the indicated user data type.
|
||||
/// Overwrites the signature in the buf with zeros.
|
||||
///
|
||||
/// #Panics
|
||||
/// Panics it provided buffer is smaller that 512 bytes or kind is Null or Unsigned
|
||||
fn new(buf: &mut [u8], kind: UserDataType) -> Self {
|
||||
assert!(buf.len() >= 0x200);
|
||||
|
||||
let (ret, sgn) = match kind {
|
||||
UserDataType::SgnEcSECP521R1 => {
|
||||
let EcUserData {
|
||||
data,
|
||||
signature,
|
||||
sgn_size,
|
||||
..
|
||||
} = EcUserData::mut_from_prefix(buf).unwrap();
|
||||
let data_len: usize = data.len();
|
||||
let data = data.to_vec();
|
||||
let mut signature = signature.to_vec();
|
||||
signature.truncate(sgn_size.get() as usize);
|
||||
(Self { data, signature }, &mut buf[data_len..])
|
||||
}
|
||||
UserDataType::SgnRsa2048 => (
|
||||
Self {
|
||||
data: buf[..0x100].to_vec(),
|
||||
signature: buf[0x100..].to_vec(),
|
||||
},
|
||||
&mut buf[0x100..],
|
||||
),
|
||||
UserDataType::SgnRsa3072 => (
|
||||
Self {
|
||||
data: buf[..0x80].to_vec(),
|
||||
signature: buf[0x80..].to_vec(),
|
||||
},
|
||||
&mut buf[0x80..],
|
||||
),
|
||||
UserDataType::Null => unreachable!(),
|
||||
UserDataType::Unsigned => unreachable!(),
|
||||
};
|
||||
|
||||
//overwrite signature field with zeros
|
||||
sgn.fill(0);
|
||||
ret
|
||||
}
|
||||
|
||||
fn signature(&self) -> &[u8] {
|
||||
self.signature.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<VerifiedUserData> for Vec<u8> {
|
||||
fn from(value: VerifiedUserData) -> Self {
|
||||
value.data
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::{get_test_asset, test_utils::get_test_keys};
|
||||
|
||||
#[test]
|
||||
fn sign_null() {
|
||||
let mut buf = vec![17; 0x200];
|
||||
|
||||
let user_data = UserData::Null;
|
||||
let (data, _) = user_data.data();
|
||||
assert!(data.is_none());
|
||||
|
||||
user_data.sign(&mut buf, 0).unwrap();
|
||||
|
||||
// sign should not touch the buffer
|
||||
assert_eq!(buf, vec![17; 0x200]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sign_unsigned() {
|
||||
let user_data = UserData::Unsigned(vec![0x11; 0x200]);
|
||||
let (data, _) = user_data.data();
|
||||
assert_eq!(data.unwrap(), &[0x11; 0x200]);
|
||||
|
||||
let mut buf = vec![17; 0x200];
|
||||
user_data.sign(&mut buf, 0).unwrap();
|
||||
|
||||
// sign should not touch the buffer
|
||||
assert_eq!(buf, vec![17; 0x200]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sign_rsa2048() {
|
||||
let rsa = get_test_asset!("keys/rsa2048key.pem");
|
||||
let rsa = PKey::private_key_from_pem(rsa).unwrap();
|
||||
|
||||
let mut buf = vec![0x17; 0x200];
|
||||
|
||||
let user_data = UserData::new(Some(rsa.clone()), vec![0x11; 0x100]).unwrap();
|
||||
let (data, _) = user_data.data();
|
||||
let data = data.unwrap();
|
||||
buf[..0x100].copy_from_slice(data);
|
||||
|
||||
user_data.sign(&mut buf, 0).unwrap();
|
||||
|
||||
let vrf_user_data = VerifiedUserData::new(&mut buf, UserDataType::SgnRsa2048);
|
||||
let res = verify_signature(
|
||||
&rsa,
|
||||
MessageDigest::sha512(),
|
||||
&buf,
|
||||
vrf_user_data.signature(),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(res);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sign_rsa3072() {
|
||||
let rsa = get_test_asset!("keys/rsa3072key.pem");
|
||||
let rsa = PKey::private_key_from_pem(rsa).unwrap();
|
||||
|
||||
let mut buf = vec![0x17; 0x200];
|
||||
|
||||
let user_data = UserData::new(Some(rsa.clone()), vec![0x11; 0x80]).unwrap();
|
||||
let (data, _) = user_data.data();
|
||||
let data = data.unwrap();
|
||||
buf[..0x80].copy_from_slice(data);
|
||||
|
||||
user_data.sign(&mut buf, 0).unwrap();
|
||||
|
||||
let vrf_user_data = VerifiedUserData::new(&mut buf, UserDataType::SgnRsa3072);
|
||||
let res = verify_signature(
|
||||
&rsa,
|
||||
MessageDigest::sha512(),
|
||||
&buf,
|
||||
vrf_user_data.signature(),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(res);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sign_rsa4096_fail() {
|
||||
let rsa = get_test_asset!("keys/rsa4096key.pem");
|
||||
let rsa = PKey::private_key_from_pem(rsa).unwrap();
|
||||
|
||||
let user_data = UserData::new(Some(rsa.clone()), vec![]);
|
||||
assert!(matches!(
|
||||
user_data.unwrap_err(),
|
||||
Error::BinAsrcbUnsupportedUserDataSgnKey
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sign_ec() {
|
||||
let (ec, _) = get_test_keys();
|
||||
|
||||
let mut buf = vec![0x11; 0x200];
|
||||
|
||||
let user_data = UserData::new(Some(ec.clone()), vec![0x11; 0x80]).unwrap();
|
||||
let (data, _) = user_data.data();
|
||||
let data = data.unwrap();
|
||||
buf[..0x80].copy_from_slice(data);
|
||||
|
||||
user_data.sign(&mut buf, 0).unwrap();
|
||||
let buf_ec = EcUserData::mut_from(&mut buf).unwrap();
|
||||
let EcUserData {
|
||||
data,
|
||||
signature,
|
||||
res_18b,
|
||||
sgn_size,
|
||||
res_192,
|
||||
} = buf_ec;
|
||||
assert_eq!(data, &[0x11u8; 256]);
|
||||
assert_ne!(signature, &[0u8; 139]);
|
||||
assert_eq!(res_18b, &[0u8; 5]);
|
||||
assert!(sgn_size.get() <= 139);
|
||||
assert_eq!(res_192, &[0u8; 110]);
|
||||
|
||||
let vrf_user_data = VerifiedUserData::new(&mut buf, UserDataType::SgnEcSECP521R1);
|
||||
let res = verify_signature(
|
||||
&ec,
|
||||
MessageDigest::sha512(),
|
||||
&buf,
|
||||
vrf_user_data.signature(),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(res);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sign_ec_fail() {
|
||||
let ec = get_test_asset!("keys/ecsecp256k1.pem");
|
||||
let ec = PKey::private_key_from_pem(ec).unwrap();
|
||||
|
||||
let user_data = UserData::new(Some(ec.clone()), vec![]);
|
||||
assert!(matches!(
|
||||
user_data.unwrap_err(),
|
||||
Error::BinAsrcbUnsupportedUserDataSgnKey
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_format() {
|
||||
let (_, ec) = get_test_keys();
|
||||
check_key_format(UserDataType::SgnEcSECP521R1, &ec).unwrap();
|
||||
let res = check_key_format(UserDataType::SgnRsa2048, &ec);
|
||||
assert!(matches!(res, Err(Error::AsrcbUserDataKeyMismatch { .. })));
|
||||
|
||||
let rsa = get_test_asset!("keys/rsa2048key.pub.pem");
|
||||
let rsa = PKey::public_key_from_pem(rsa).unwrap();
|
||||
check_key_format(UserDataType::SgnRsa2048, &rsa).unwrap();
|
||||
|
||||
let rsa = get_test_asset!("keys/rsa3072key.pub.pem");
|
||||
let rsa = PKey::public_key_from_pem(rsa).unwrap();
|
||||
check_key_format(UserDataType::SgnRsa3072, &rsa).unwrap();
|
||||
let res = check_key_format(UserDataType::SgnRsa2048, &rsa);
|
||||
assert!(matches!(res, Err(Error::AsrcbUserDataKeyMismatch { .. })));
|
||||
|
||||
let rsa = get_test_asset!("keys/rsa4096key.pem");
|
||||
let rsa = PKey::private_key_from_pem(rsa).unwrap();
|
||||
let rsa = PKey::public_key_from_pem(&rsa.public_key_to_pem().unwrap()).unwrap();
|
||||
let res = check_key_format(UserDataType::SgnRsa2048, &rsa);
|
||||
assert!(matches!(res, Err(Error::AsrcbUserDataKeyMismatch { .. })));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind() {
|
||||
let (ec, _) = get_test_keys();
|
||||
let kind = UserData::user_data_type(&ec).unwrap();
|
||||
assert_eq!(kind, UserDataType::SgnEcSECP521R1);
|
||||
|
||||
let rsa = get_test_asset!("keys/rsa2048key.pem");
|
||||
let rsa = PKey::private_key_from_pem(rsa).unwrap();
|
||||
let kind = UserData::user_data_type(&rsa).unwrap();
|
||||
assert_eq!(kind, UserDataType::SgnRsa2048);
|
||||
|
||||
let rsa = get_test_asset!("keys/rsa3072key.pem");
|
||||
let rsa = PKey::private_key_from_pem(rsa).unwrap();
|
||||
let kind = UserData::user_data_type(&rsa).unwrap();
|
||||
assert_eq!(kind, UserDataType::SgnRsa3072);
|
||||
|
||||
let rsa = get_test_asset!("keys/rsa4096key.pem");
|
||||
let rsa = PKey::private_key_from_pem(rsa).unwrap();
|
||||
let kind = UserData::user_data_type(&rsa).unwrap_err();
|
||||
assert!(matches!(kind, Error::BinAsrcbUnsupportedUserDataSgnKey));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new() {
|
||||
let (ec, _) = get_test_keys();
|
||||
let user_data = UserData::new(
|
||||
Some(ec.clone()),
|
||||
vec![1; UserDataType::SgnEcSECP521R1.max()],
|
||||
)
|
||||
.unwrap();
|
||||
assert!(matches!(user_data, UserData::Signed(_)));
|
||||
|
||||
let user_data = UserData::new(Some(ec), vec![1; UserDataType::SgnEcSECP521R1.max() + 1]);
|
||||
assert!(matches!(
|
||||
user_data,
|
||||
Err(Error::AsrcbInvSgnUserData(UserDataType::SgnEcSECP521R1))
|
||||
));
|
||||
|
||||
let user_data = UserData::new(None, vec![1; UserDataType::Unsigned.max()]).unwrap();
|
||||
assert!(matches!(user_data, UserData::Unsigned(_)));
|
||||
|
||||
let user_data = UserData::new(None, vec![1; UserDataType::Unsigned.max() + 1]);
|
||||
assert!(matches!(
|
||||
user_data,
|
||||
Err(Error::AsrcbInvSgnUserData(UserDataType::Unsigned))
|
||||
));
|
||||
}
|
||||
#[test]
|
||||
fn data() {
|
||||
let (ec, _) = get_test_keys();
|
||||
let data_in = vec![1; UserDataType::SgnEcSECP521R1.max()];
|
||||
let user_data = UserData::new(Some(ec.clone()), data_in.clone()).unwrap();
|
||||
let exp_pad = Some(vec![0; UserData::USER_DATA_SIZE - data_in.len()]);
|
||||
|
||||
let (data_out, pad) = user_data.data();
|
||||
assert_eq!(data_out, Some(data_in.as_ref()));
|
||||
assert_eq!(pad, exp_pad);
|
||||
|
||||
let data_in = vec![1; UserDataType::SgnEcSECP521R1.max() - 1];
|
||||
let user_data = UserData::new(Some(ec.clone()), data_in.clone()).unwrap();
|
||||
let exp_pad = Some(vec![0; UserData::USER_DATA_SIZE - data_in.len()]);
|
||||
|
||||
let (data_out, pad) = user_data.data();
|
||||
assert_eq!(data_out, Some(data_in.as_ref()));
|
||||
assert_eq!(pad, exp_pad);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,10 @@ use pv::{
|
||||
get_test_asset,
|
||||
request::{
|
||||
openssl::pkey::{PKey, Public},
|
||||
uvsecret::{AddSecretFlags, AddSecretRequest, AddSecretVersion, ExtSecret, GuestSecret},
|
||||
uvsecret::{
|
||||
verify_asrcb_and_get_user_data, AddSecretFlags, AddSecretRequest, AddSecretVersion,
|
||||
ExtSecret, GuestSecret,
|
||||
},
|
||||
BootHdrTags, ReqEncrCtx, Request, SymKey,
|
||||
},
|
||||
test_utils::get_test_keys,
|
||||
@@ -153,3 +156,12 @@ fn null_none_default_cuid_seven() {
|
||||
let exp = get_test_asset!("exp/asrcb/null_none_default_cuid_seven");
|
||||
assert_eq!(asrcb, exp);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_no_user_data() {
|
||||
let req = get_test_asset!("exp/asrcb/null_none_default_ncuid_one");
|
||||
assert!(matches!(
|
||||
verify_asrcb_and_get_user_data(req.to_vec(), None),
|
||||
Ok(None)
|
||||
))
|
||||
}
|
||||
|
||||
8
rust/pv/tests/assets/keys/ecsecp256k1.pem
Normal file
8
rust/pv/tests/assets/keys/ecsecp256k1.pem
Normal file
@@ -0,0 +1,8 @@
|
||||
-----BEGIN EC PARAMETERS-----
|
||||
BgUrgQQACg==
|
||||
-----END EC PARAMETERS-----
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHQCAQEEIAiQERmhEIwksExNpW2CsaQeFPKFEN6PIdMWUiX3Mu7FoAcGBSuBBAAK
|
||||
oUQDQgAEO9U+NDdHcIZ59RzTq/6lDH2RA7sEyRlvFVQsj7feiuXh4Q4qeZNCb2sn
|
||||
s7Hd1lUjFexH+Cp97/ggwSa3q2y/0Q==
|
||||
-----END EC PRIVATE KEY-----
|
||||
52
rust/pv/tests/assets/keys/rsa4096key.pem
Normal file
52
rust/pv/tests/assets/keys/rsa4096key.pem
Normal file
@@ -0,0 +1,52 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
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
|
||||
io4wIYBrX/SPmp7tQvWI18Ike/IDtA==
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -67,6 +67,13 @@ impl AddSecretMagic {
|
||||
let kind = UserDataType::try_from(kind)?;
|
||||
Ok(Self::from(kind))
|
||||
}
|
||||
|
||||
/// Returns the [`UserDataType`] of this [`AddSecretMagic`].
|
||||
pub fn kind(&self) -> UserDataType {
|
||||
// Panic: Will never panic. The value is cheched during construcion of the object for
|
||||
// beeing one of the enum values.
|
||||
self.kind.get().try_into().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Types of (non architectured) user data for an add-secret request
|
||||
|
||||
Reference in New Issue
Block a user