mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
c6f621d0dc
Add a `pv` crate that bundles useful functions and structs for creating requests like `Attestation`, `Add Secret`, or even `Boot` a.k.a. Secure Execution Image. Note pv includes a subcrate `openssl_extensions` that (temporarily) bundles some needed `openssl-rust` functionalities that are not upstream yet. The plan is to remove these, when they become upstream. The pv crate has multiple features: * request - code to generate requests * uvsecret - code to access the UV-secret api with request enabled also generating requests is possible Signed-off-by: Steffen Eiden <seiden@linux.ibm.com> Acked-by: Jan Höppner <hoeppner@linux.ibm.com> Acked-by: Marc Hartmayer <mhartmay@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
326 lines
9.5 KiB
Rust
326 lines
9.5 KiB
Rust
// SPDX-License-Identifier: MIT
|
|
//
|
|
// Copyright IBM Corp. 2023
|
|
|
|
use crate::requires_feat;
|
|
use crate::{error::Result, secret::Secret, Error};
|
|
use openssl::rand::rand_bytes;
|
|
use openssl::{
|
|
derive::Deriver,
|
|
ec::{EcGroup, EcKey},
|
|
hash::{DigestBytes, MessageDigest},
|
|
md::MdRef,
|
|
nid::Nid,
|
|
pkey::{Id, PKey, Private, Public},
|
|
pkey_ctx::{HkdfMode, PkeyCtx},
|
|
symm::{encrypt, encrypt_aead, Cipher},
|
|
};
|
|
use std::convert::TryInto;
|
|
|
|
/// An AES256-key that will purge itself out of the memory when going out of scope
|
|
///
|
|
#[doc = requires_feat!(request)]
|
|
pub type Aes256Key = Secret<[u8; 32]>;
|
|
|
|
/// Types of symmetric keys, to specify during construction.
|
|
///
|
|
#[doc = requires_feat!(request)]
|
|
#[non_exhaustive]
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum SymKeyType {
|
|
/// AES 256 key (32 bytes)
|
|
Aes256,
|
|
}
|
|
|
|
/// Types of symmetric keys
|
|
///
|
|
#[doc = requires_feat!(request)]
|
|
#[non_exhaustive]
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub enum SymKey {
|
|
/// AES 256 key (32 bytes)
|
|
Aes256(Aes256Key),
|
|
}
|
|
|
|
impl SymKey {
|
|
/// Generates a random symmetric key.
|
|
///
|
|
/// * `key_tp` - type of the symmetric key
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// This function will return an error if the Key cannot be generated.
|
|
pub fn random(key_tp: SymKeyType) -> Result<Self> {
|
|
match key_tp {
|
|
SymKeyType::Aes256 => Ok(Self::Aes256(random_array().map(|v| v.into())?)),
|
|
}
|
|
}
|
|
|
|
/// Returns a reference to the value of this [`SymKey`].
|
|
pub fn value(&self) -> &[u8] {
|
|
match self {
|
|
Self::Aes256(key) => key.value(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Aes256Key {
|
|
/// Generates an AES256 key from an digest (hash).
|
|
///
|
|
/// # Panics
|
|
///
|
|
/// Panics if `digset` is not 32 bytes long.
|
|
fn from_digest(digest: DigestBytes) -> Self {
|
|
let key: [u8; 32] = digest
|
|
.as_ref()
|
|
.try_into()
|
|
.expect("Unexpected OpenSSl Error. Sha256 hash not 32 bytes long");
|
|
key.into()
|
|
}
|
|
}
|
|
|
|
impl From<Aes256Key> for SymKey {
|
|
fn from(value: Aes256Key) -> Self {
|
|
Self::Aes256(value)
|
|
}
|
|
}
|
|
|
|
/// Performs an hkdf according to RFC 5869.
|
|
/// See [`OpenSSL HKDF`]()
|
|
///
|
|
#[doc = requires_feat!(request)]
|
|
/// # Errors
|
|
///
|
|
/// This function will return an OpenSSL error if the key could not be generated.
|
|
pub fn hkdf_rfc_5869<const COUNT: usize>(
|
|
md: &MdRef,
|
|
ikm: &[u8],
|
|
salt: &[u8],
|
|
info: &[u8],
|
|
) -> Result<[u8; COUNT]> {
|
|
let mut ctx = PkeyCtx::new_id(Id::HKDF)?;
|
|
ctx.derive_init()?;
|
|
ctx.set_hkdf_mode(HkdfMode::EXTRACT_THEN_EXPAND)?;
|
|
ctx.set_hkdf_md(md)?;
|
|
ctx.set_hkdf_salt(salt)?;
|
|
ctx.set_hkdf_key(ikm)?;
|
|
ctx.add_hkdf_info(info)?;
|
|
|
|
let mut res = [0; COUNT];
|
|
ctx.derive(Some(&mut res))?;
|
|
Ok(res)
|
|
}
|
|
|
|
/// Derive a symmetric key from a private and a public key.
|
|
///
|
|
#[doc = requires_feat!(request)]
|
|
/// # Errors
|
|
///
|
|
/// This function will return an error if something went bad in OpenSSL.
|
|
pub fn derive_key(k1: &PKey<Private>, k2: &PKey<Public>) -> Result<Aes256Key> {
|
|
let mut der = Deriver::new(k1)?;
|
|
der.set_peer(k2)?;
|
|
let mut key = der.derive_to_vec()?;
|
|
key.extend([0, 0, 0, 1]);
|
|
let secr = Secret::new(key);
|
|
|
|
Ok(Aes256Key::from_digest(hash(
|
|
MessageDigest::sha256(),
|
|
secr.value(),
|
|
)?))
|
|
}
|
|
|
|
/// Generate a random array.
|
|
///
|
|
#[doc = requires_feat!(request)]
|
|
/// # Errors
|
|
///
|
|
/// This function will return an error if the entropy source fails or is not available.
|
|
pub fn random_array<const COUNT: usize>() -> Result<[u8; COUNT]> {
|
|
let mut rand = [0; COUNT];
|
|
rand_bytes(&mut rand)?;
|
|
Ok(rand)
|
|
}
|
|
|
|
/// Generate a new random EC-SECP521R1 key.
|
|
///
|
|
#[doc = requires_feat!(request)]
|
|
/// # Errors
|
|
///
|
|
/// This function will return an error if the key could not be generated by OpenSSL.
|
|
pub fn gen_ec_key() -> Result<PKey<Private>> {
|
|
let group = EcGroup::from_curve_name(Nid::SECP521R1)?;
|
|
let key: EcKey<Private> = EcKey::generate(&group)?;
|
|
PKey::from_ec_key(key).map_err(Error::Crypto)
|
|
}
|
|
|
|
/// Encrypt confidential Data with a symmetric key.
|
|
///
|
|
/// * `key` - symmetric key used for encryption
|
|
/// * `iv` - initialisation vector
|
|
/// * `conf` - data to be encrypted
|
|
///
|
|
#[doc = requires_feat!(request)]
|
|
/// # Errors
|
|
///
|
|
/// This function will return an error if the data could not be encrypted by OpenSSL.
|
|
pub fn encrypt_aes(key: &SymKey, iv: &[u8], conf: &[u8]) -> Result<Vec<u8>> {
|
|
match key {
|
|
SymKey::Aes256(key) => {
|
|
encrypt(Cipher::aes_256_gcm(), key.value(), Some(iv), conf).map_err(Error::Crypto)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Encrypt confidential Data with a symmetric key and provida a gcm tag.
|
|
///
|
|
/// * `key` - symmetric key used for encryption
|
|
/// * `iv` - initialisation vector
|
|
/// * `aad` - additional authentic data
|
|
/// * `conf` - data to be encrypted
|
|
///
|
|
#[doc = requires_feat!(request)]
|
|
/// # Returns
|
|
/// [`Vec<u8>`] with the following content:
|
|
/// 1. `aad`
|
|
/// 2. `encr(conf)`
|
|
/// 3. `aes gcm tag`
|
|
///
|
|
/// # 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>> {
|
|
let mut tag = vec![0xff; 16];
|
|
let encr = match key {
|
|
SymKey::Aes256(key) => encrypt_aead(
|
|
Cipher::aes_256_gcm(),
|
|
key.value(),
|
|
Some(iv),
|
|
aad,
|
|
conf,
|
|
&mut tag,
|
|
)?,
|
|
};
|
|
|
|
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);
|
|
|
|
Ok(res)
|
|
}
|
|
|
|
/// Calculate the hash of a slice.
|
|
///
|
|
#[doc = requires_feat!(request)]
|
|
/// # Errors
|
|
///
|
|
/// This function will return an error if OpenSSL could not compute the hash.
|
|
pub fn hash(t: MessageDigest, data: &[u8]) -> Result<DigestBytes> {
|
|
openssl::hash::hash(t, data).map_err(Error::Crypto)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::test_utils::*;
|
|
|
|
#[test]
|
|
fn derive_key() {
|
|
let (cust_key, host_key) = get_test_keys();
|
|
|
|
let exp_key: Aes256Key = [
|
|
0x75, 0x32, 0x77, 0x55, 0x8f, 0x3b, 0x60, 0x3, 0x41, 0x9e, 0xf2, 0x49, 0xae, 0x3c,
|
|
0x4b, 0x55, 0xaa, 0xd7, 0x7d, 0x9, 0xd9, 0x7f, 0xdd, 0x1f, 0xc8, 0x8f, 0xd8, 0xf0,
|
|
0xcf, 0x22, 0xf1, 0x49,
|
|
]
|
|
.into();
|
|
|
|
let calc_key = super::derive_key(&cust_key, &host_key).unwrap();
|
|
|
|
assert_eq!(&calc_key, &exp_key);
|
|
}
|
|
|
|
#[test]
|
|
fn hkdf_rfc_5869() {
|
|
use openssl::md::Md;
|
|
// RFC 6869 test vector 1
|
|
let ikm = [0x0bu8; 22];
|
|
let salt: [u8; 13] = [
|
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
|
|
];
|
|
let info: [u8; 10] = [0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9];
|
|
let exp: [u8; 42] = [
|
|
0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36,
|
|
0x2f, 0x2a, 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, 0x5d, 0xb0, 0x2d, 0x56,
|
|
0xec, 0xc4, 0xc5, 0xbf, 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, 0x58, 0x65,
|
|
];
|
|
let res: [u8; 42] = super::hkdf_rfc_5869(Md::sha256(), &ikm, &salt, &info).unwrap();
|
|
|
|
assert_eq!(exp, res);
|
|
}
|
|
|
|
#[test]
|
|
fn encrypt_aes_256_gcm() {
|
|
let aes_gcm_key = [
|
|
0xee, 0xbc, 0x1f, 0x57, 0x48, 0x7f, 0x51, 0x92, 0x1c, 0x04, 0x65, 0x66, 0x5f, 0x8a,
|
|
0xe6, 0xd1, 0x65, 0x8b, 0xb2, 0x6d, 0xe6, 0xf8, 0xa0, 0x69, 0xa3, 0x52, 0x02, 0x93,
|
|
0xa5, 0x72, 0x07, 0x8f,
|
|
];
|
|
let aes_gcm_iv = [
|
|
0x99, 0xaa, 0x3e, 0x68, 0xed, 0x81, 0x73, 0xa0, 0xee, 0xd0, 0x66, 0x84,
|
|
];
|
|
let aes_gcm_plain = [
|
|
0xf5, 0x6e, 0x87, 0x05, 0x5b, 0xc3, 0x2d, 0x0e, 0xeb, 0x31, 0xb2, 0xea, 0xcc, 0x2b,
|
|
0xf2, 0xa5,
|
|
];
|
|
let aes_gcm_aad = [
|
|
0x4d, 0x23, 0xc3, 0xce, 0xc3, 0x34, 0xb4, 0x9b, 0xdb, 0x37, 0x0c, 0x43, 0x7f, 0xec,
|
|
0x78, 0xde,
|
|
];
|
|
let aes_gcm_res = vec![
|
|
0x4d, 0x23, 0xc3, 0xce, 0xc3, 0x34, 0xb4, 0x9b, 0xdb, 0x37, 0x0c, 0x43, 0x7f, 0xec,
|
|
0x78, 0xde, 0xf7, 0x26, 0x44, 0x13, 0xa8, 0x4c, 0x0e, 0x7c, 0xd5, 0x36, 0x86, 0x7e,
|
|
0xb9, 0xf2, 0x17, 0x36, 0x67, 0xba, 0x05, 0x10, 0x26, 0x2a, 0xe4, 0x87, 0xd7, 0x37,
|
|
0xee, 0x62, 0x98, 0xf7, 0x7e, 0x0c,
|
|
];
|
|
|
|
let res = encrypt_aes_gcm(
|
|
&SymKey::Aes256(aes_gcm_key.into()),
|
|
&aes_gcm_iv,
|
|
&aes_gcm_aad,
|
|
&aes_gcm_plain,
|
|
)
|
|
.unwrap();
|
|
assert_eq!(res, aes_gcm_res);
|
|
}
|
|
|
|
#[test]
|
|
fn encrypt_aes_256() {
|
|
let aes_gcm_key = [
|
|
0xee, 0xbc, 0x1f, 0x57, 0x48, 0x7f, 0x51, 0x92, 0x1c, 0x04, 0x65, 0x66, 0x5f, 0x8a,
|
|
0xe6, 0xd1, 0x65, 0x8b, 0xb2, 0x6d, 0xe6, 0xf8, 0xa0, 0x69, 0xa3, 0x52, 0x02, 0x93,
|
|
0xa5, 0x72, 0x07, 0x8f,
|
|
];
|
|
let aes_gcm_iv = [
|
|
0x99, 0xaa, 0x3e, 0x68, 0xed, 0x81, 0x73, 0xa0, 0xee, 0xd0, 0x66, 0x84,
|
|
];
|
|
let aes_gcm_plain = [
|
|
0xf5, 0x6e, 0x87, 0x05, 0x5b, 0xc3, 0x2d, 0x0e, 0xeb, 0x31, 0xb2, 0xea, 0xcc, 0x2b,
|
|
0xf2, 0xa5,
|
|
];
|
|
let aes_gcm_res = vec![
|
|
0xf7, 0x26, 0x44, 0x13, 0xa8, 0x4c, 0x0e, 0x7c, 0xd5, 0x36, 0x86, 0x7e, 0xb9, 0xf2,
|
|
0x17, 0x36,
|
|
];
|
|
|
|
let res = encrypt_aes(
|
|
&&SymKey::Aes256(aes_gcm_key.into()),
|
|
&aes_gcm_iv,
|
|
&aes_gcm_plain,
|
|
)
|
|
.unwrap();
|
|
assert_eq!(res, aes_gcm_res);
|
|
}
|
|
}
|