mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
pv: Add hybrid key derivation support
Add derive_aes256_gcm_key_hybrid() function that derives an Aes256GcmKey and ML-KEM1024 ciphertext from a private ECDH customer key, a public ECDH target key, and a public ML-KEM target key. This enables hybrid post-quantum cryptography support. Assisted-by: IBM Bob:1.0.5 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> Acked-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
1746d2bb6a
commit
ebe56592c8
@@ -7,12 +7,14 @@ use std::fmt::Display;
|
||||
use std::ops::Range;
|
||||
|
||||
use enum_dispatch::enum_dispatch;
|
||||
use openssl::bn::BigNumContext;
|
||||
use openssl::derive::Deriver;
|
||||
use openssl::ec::{EcGroup, EcKey};
|
||||
use openssl::ec::{EcGroup, EcKey, EcPoint};
|
||||
use openssl::error::ErrorStack;
|
||||
use openssl::hash::{DigestBytes, MessageDigest};
|
||||
use openssl::md::MdRef;
|
||||
use openssl::nid::Nid;
|
||||
use openssl::pkey::{HasPublic, Id, PKey, PKeyRef, Private, Public};
|
||||
use openssl::pkey::{HasPublic, Id, KeyType, PKey, PKeyRef, Private, Public};
|
||||
use openssl::pkey_ctx::{HkdfMode, PkeyCtx};
|
||||
use openssl::rand::rand_bytes;
|
||||
use openssl::rsa::Padding;
|
||||
@@ -23,8 +25,51 @@ use openssl::symm::{
|
||||
use pv_core::request::Confidential;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::openssl_extensions::PkeyEncapsulateContext;
|
||||
use crate::req::get_pub_ecdh_points;
|
||||
use crate::request::EcPubKeyCoord;
|
||||
use crate::Error;
|
||||
|
||||
/// Compute ECDH shared secret from public and private keys
|
||||
///
|
||||
/// It is expected that the public and private key are with respect to EC-P521.
|
||||
///
|
||||
/// Note that the output is the concatenation of the 80-bytes-left-padded x and
|
||||
/// 80-bytes-left-padded y coordinate.
|
||||
fn ecdh_shared_secret(
|
||||
pub_key: &PKeyRef<Public>,
|
||||
priv_key: &PKeyRef<Private>,
|
||||
) -> Result<[u8; 160], ErrorStack> {
|
||||
let pub_key = pub_key.ec_key()?;
|
||||
let priv_key = priv_key.ec_key()?;
|
||||
|
||||
// Verify both keys use the EC-P521 curve (SECP521R1)
|
||||
assert_eq!(
|
||||
pub_key.group().curve_name(),
|
||||
Some(Nid::SECP521R1),
|
||||
"Public key must use EC-P521 curve"
|
||||
);
|
||||
assert_eq!(
|
||||
priv_key.group().curve_name(),
|
||||
Some(Nid::SECP521R1),
|
||||
"Private key must use EC-P521 curve"
|
||||
);
|
||||
|
||||
pub_key.check_key()?;
|
||||
priv_key.check_key()?;
|
||||
let group = pub_key.group();
|
||||
let mut bn_ctx = BigNumContext::new()?;
|
||||
let mut point = EcPoint::new(group)?;
|
||||
point.mul2(
|
||||
group,
|
||||
pub_key.public_key(),
|
||||
priv_key.private_key(),
|
||||
&mut bn_ctx,
|
||||
)?;
|
||||
let coord = get_pub_ecdh_points(&point, group)?;
|
||||
Ok(coord)
|
||||
}
|
||||
|
||||
/// An AES256-GCM key that will purge itself out of the memory when going out of scope
|
||||
pub type Aes256GcmKey = Confidential<[u8; SymKeyType::AES_256_GCM_KEY_LEN]>;
|
||||
/// An AES256-XTS key that will purge itself out of the memory when going out of scope
|
||||
@@ -214,6 +259,220 @@ pub fn derive_aes256_gcm_key(k1: &PKeyRef<Private>, k2: &PKeyRef<Public>) -> Res
|
||||
))
|
||||
}
|
||||
|
||||
/// Determines the KeyType of a given PKey by testing against all known key types.
|
||||
///
|
||||
/// This function iterates through all known OpenSSL key types and uses the `is_a()`
|
||||
/// method to identify which type the provided key matches. This is more reliable than
|
||||
/// using `Id` to `KeyType` conversion, especially for newer key types like ML-KEM
|
||||
/// that may not have a direct `Id` mapping.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `key` - A reference to the PKey to identify
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Some(KeyType)` - If the key matches one of the known key types
|
||||
/// * `None` - If the key type is not recognized or doesn't match any known types
|
||||
fn pkey_to_keytype<T>(key: &PKeyRef<T>) -> Option<KeyType> {
|
||||
const KNOWN_KEY_TYPES: &[KeyType] = &[
|
||||
KeyType::RSA,
|
||||
KeyType::RSA_PSS,
|
||||
KeyType::DSA,
|
||||
KeyType::DH,
|
||||
KeyType::EC,
|
||||
KeyType::HMAC,
|
||||
KeyType::CMAC,
|
||||
KeyType::X25519,
|
||||
KeyType::ED25519,
|
||||
KeyType::X448,
|
||||
KeyType::ED448,
|
||||
KeyType::ML_KEM_512,
|
||||
KeyType::ML_KEM_768,
|
||||
KeyType::ML_KEM_1024,
|
||||
];
|
||||
|
||||
KNOWN_KEY_TYPES
|
||||
.iter()
|
||||
.find(|&&key_type| key.is_a(key_type))
|
||||
.copied()
|
||||
}
|
||||
|
||||
fn key_type_str(t: KeyType) -> &'static str {
|
||||
if t == KeyType::RSA {
|
||||
"RSA"
|
||||
} else if t == KeyType::RSA_PSS {
|
||||
"RSA-PSS"
|
||||
} else if t == KeyType::DSA {
|
||||
"DSA"
|
||||
} else if t == KeyType::DH {
|
||||
"DH"
|
||||
} else if t == KeyType::EC {
|
||||
"EC"
|
||||
} else if t == KeyType::HMAC {
|
||||
"HMAC"
|
||||
} else if t == KeyType::CMAC {
|
||||
"CMAC"
|
||||
} else if t == KeyType::X25519 {
|
||||
"X25519"
|
||||
} else if t == KeyType::ED25519 {
|
||||
"ED25519"
|
||||
} else if t == KeyType::X448 {
|
||||
"X448"
|
||||
} else if t == KeyType::ED448 {
|
||||
"ED448"
|
||||
} else if t == KeyType::ML_KEM_512 {
|
||||
"ML-KEM-512"
|
||||
} else if t == KeyType::ML_KEM_768 {
|
||||
"ML-KEM-768"
|
||||
} else if t == KeyType::ML_KEM_1024 {
|
||||
"ML-KEM-1024"
|
||||
} else {
|
||||
"unknown"
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates that a key matches the expected key type.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the key doesn't match the expected type.
|
||||
pub(crate) fn validate_key_type<T: HasPublic>(
|
||||
key: &PKeyRef<T>,
|
||||
key_name: &str,
|
||||
expected_type: KeyType,
|
||||
) -> Result<()> {
|
||||
if !key.is_a(expected_type) {
|
||||
return Err(Error::RetrInvKey {
|
||||
what: "key type",
|
||||
kind: key_name.to_string(),
|
||||
value: pkey_to_keytype(key)
|
||||
.map(key_type_str)
|
||||
.unwrap_or("unknown")
|
||||
.to_string(),
|
||||
exp: key_type_str(expected_type).to_string(),
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validates that a key is an EC key with the specified curve.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the key is not an EC key or doesn't use the expected curve.
|
||||
pub(crate) fn validate_ec_key<T: HasPublic>(
|
||||
key: &PKeyRef<T>,
|
||||
key_name: &str,
|
||||
expected_curve: Nid,
|
||||
) -> Result<()> {
|
||||
if key.id() != Id::EC {
|
||||
return Err(Error::RetrInvKey {
|
||||
what: "key type",
|
||||
kind: key_name.to_string(),
|
||||
value: pkey_to_keytype(key)
|
||||
.map(key_type_str)
|
||||
.unwrap_or("unknown")
|
||||
.to_string(),
|
||||
exp: format!("EC ({})", expected_curve.long_name().unwrap_or("unknown")),
|
||||
});
|
||||
}
|
||||
let ec_key = key.ec_key()?;
|
||||
if ec_key.group().curve_name() != Some(expected_curve) {
|
||||
return Err(Error::RetrInvKey {
|
||||
what: "curve",
|
||||
kind: key_name.to_string(),
|
||||
value: ec_key
|
||||
.group()
|
||||
.curve_name()
|
||||
.and_then(|nid| nid.long_name().ok())
|
||||
.unwrap_or("unknown")
|
||||
.to_string(),
|
||||
exp: expected_curve.long_name().unwrap_or("unknown").to_string(),
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Derive a symmetric AES 256 GCM key from a private target key, a public
|
||||
/// customer key, and a public ML-KEM target key.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// The derived key and the ML-KEM ciphertext (KC).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if something went bad in OpenSSL or the
|
||||
/// wrong key types were used.
|
||||
pub fn derive_aes256_gcm_key_hybrid(
|
||||
priv_ecdh_cust_key: &PKeyRef<Private>,
|
||||
pub_ecdh_target_key: &PKeyRef<Public>,
|
||||
pub_mlkem_target_key: &PKeyRef<Public>,
|
||||
) -> Result<(Aes256GcmKey, Vec<u8>)> {
|
||||
let mut buffer: Vec<u8> = vec![0, 0, 0, 1];
|
||||
|
||||
validate_ec_key(priv_ecdh_cust_key, "ECDH customer key", Nid::SECP521R1)?;
|
||||
validate_ec_key(pub_ecdh_target_key, "ECDH target key", Nid::SECP521R1)?;
|
||||
validate_key_type(
|
||||
pub_mlkem_target_key,
|
||||
"ML-KEM target key",
|
||||
KeyType::ML_KEM_1024,
|
||||
)?;
|
||||
|
||||
// Derive the ECDH shared secret
|
||||
let ecdh_derived_secret = ecdh_shared_secret(pub_ecdh_target_key, priv_ecdh_cust_key)?;
|
||||
assert_eq!(ecdh_derived_secret.as_ref().len(), 160);
|
||||
buffer.extend_from_slice(ecdh_derived_secret.as_ref());
|
||||
|
||||
// Derive the ML-KEM shared secret
|
||||
let mut ctx = PkeyCtx::new(pub_mlkem_target_key)?;
|
||||
ctx.encapsulate_init()?;
|
||||
let (mut ciphertext, mut shared_secret) = (vec![], vec![]);
|
||||
ctx.encapsulate_to_vec(&mut ciphertext, &mut shared_secret)?;
|
||||
assert_eq!(ciphertext.len(), 1568);
|
||||
assert_eq!(shared_secret.len(), 32);
|
||||
buffer.extend_from_slice(&shared_secret);
|
||||
|
||||
// Append the private ECDH customer key
|
||||
let pub_ecdh_cust_key = EcPubKeyCoord::try_from(priv_ecdh_cust_key)?;
|
||||
assert_eq!(pub_ecdh_cust_key.as_ref().len(), 160);
|
||||
buffer.extend_from_slice(pub_ecdh_cust_key.as_ref());
|
||||
|
||||
// Append the ciphertext
|
||||
buffer.extend_from_slice(&ciphertext);
|
||||
|
||||
// Append the public ECDH target key
|
||||
let pub_ecdh_target_key: EcPubKeyCoord = pub_ecdh_target_key.try_into()?;
|
||||
assert_eq!(pub_ecdh_target_key.as_ref().len(), 160);
|
||||
buffer.extend_from_slice(pub_ecdh_target_key.as_ref());
|
||||
|
||||
// Append the public ML-KEM target key
|
||||
assert_eq!(pub_mlkem_target_key.raw_public_key()?.len(), 1568);
|
||||
buffer.extend_from_slice(&pub_mlkem_target_key.raw_public_key()?);
|
||||
|
||||
// Append the magic string
|
||||
const STRING: &str = "PQC Secure Execution with Format-2 Key Slots KS2";
|
||||
assert_eq!(STRING.len(), 48);
|
||||
buffer.extend_from_slice(STRING.as_bytes());
|
||||
|
||||
// Sanity check
|
||||
assert_eq!(buffer.len(), 4 + 160 + 32 + 160 + 1568 + 160 + 1568 + 48);
|
||||
|
||||
let secr = Confidential::new(buffer);
|
||||
|
||||
// Panic: does not panic as SHA256 digest is 32 bytes long
|
||||
Ok((
|
||||
Aes256GcmKey::new(
|
||||
hash(MessageDigest::sha256(), secr.value())?
|
||||
.as_ref()
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
),
|
||||
ciphertext,
|
||||
))
|
||||
}
|
||||
|
||||
/// Generate a random array.
|
||||
///
|
||||
/// # Errors
|
||||
@@ -760,6 +1019,112 @@ mod tests {
|
||||
assert_eq!(&calc_key, &exp_key);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derive_aes256_gcm_key_hybrid() {
|
||||
let (cust_key, host_key_1, host_key_2) = get_test_keys_hybrid();
|
||||
let entropy = [0x5au8; 4096];
|
||||
let nonce = [0xa5u8; 48];
|
||||
|
||||
let _rng = DeterministicTestRandGuard::install(&entropy, &nonce).unwrap();
|
||||
|
||||
let exp_key: Aes256GcmKey = [
|
||||
197, 167, 157, 112, 186, 112, 72, 125, 192, 219, 168, 132, 178, 167, 249, 123, 149, 3,
|
||||
151, 166, 162, 66, 120, 39, 41, 230, 143, 54, 172, 10, 200, 143,
|
||||
]
|
||||
.into();
|
||||
let exp_kc = [
|
||||
54, 117, 96, 77, 148, 147, 170, 100, 34, 177, 95, 7, 35, 243, 145, 115, 7, 87, 178, 9,
|
||||
169, 99, 193, 99, 244, 195, 23, 78, 11, 153, 221, 196, 5, 192, 253, 192, 86, 49, 194,
|
||||
236, 43, 69, 183, 125, 166, 87, 158, 188, 13, 152, 19, 6, 253, 29, 194, 0, 101, 236,
|
||||
28, 171, 3, 236, 53, 186, 191, 109, 7, 83, 220, 93, 126, 29, 19, 203, 201, 39, 59, 7,
|
||||
131, 51, 81, 73, 254, 69, 105, 185, 214, 179, 155, 194, 189, 122, 106, 130, 249, 48, 4,
|
||||
33, 245, 170, 163, 4, 223, 208, 138, 224, 203, 119, 105, 59, 187, 153, 235, 90, 79,
|
||||
127, 29, 136, 230, 142, 78, 83, 27, 131, 58, 126, 76, 53, 129, 20, 85, 108, 86, 64,
|
||||
244, 90, 84, 177, 239, 105, 90, 41, 118, 189, 88, 174, 224, 216, 29, 10, 123, 81, 212,
|
||||
203, 197, 120, 20, 190, 3, 45, 37, 194, 208, 249, 232, 221, 67, 10, 62, 121, 143, 169,
|
||||
227, 165, 17, 30, 85, 223, 44, 141, 114, 142, 105, 119, 187, 41, 46, 8, 6, 17, 29, 165,
|
||||
117, 254, 92, 174, 231, 25, 117, 69, 112, 216, 80, 73, 185, 54, 50, 119, 145, 220, 174,
|
||||
26, 105, 81, 114, 210, 144, 148, 109, 218, 64, 78, 231, 196, 229, 88, 46, 128, 106,
|
||||
125, 204, 58, 184, 127, 193, 207, 86, 163, 98, 164, 57, 242, 29, 59, 251, 227, 185, 60,
|
||||
18, 68, 74, 47, 203, 61, 164, 78, 245, 100, 87, 148, 210, 97, 158, 252, 79, 78, 50,
|
||||
143, 35, 231, 215, 211, 75, 133, 214, 227, 140, 27, 21, 46, 221, 84, 89, 165, 161, 227,
|
||||
46, 117, 193, 254, 190, 237, 130, 28, 57, 52, 14, 235, 154, 115, 172, 185, 67, 116, 34,
|
||||
242, 158, 209, 0, 126, 196, 93, 224, 29, 246, 145, 65, 73, 185, 196, 4, 107, 124, 241,
|
||||
157, 230, 168, 244, 238, 84, 188, 173, 17, 238, 26, 161, 24, 176, 229, 226, 33, 244,
|
||||
167, 41, 107, 156, 29, 226, 248, 64, 146, 191, 210, 234, 76, 144, 219, 92, 136, 173,
|
||||
241, 98, 0, 71, 135, 214, 196, 116, 63, 243, 73, 71, 130, 171, 86, 204, 149, 69, 164,
|
||||
20, 177, 122, 95, 226, 95, 126, 106, 160, 59, 97, 137, 8, 73, 113, 189, 172, 24, 114,
|
||||
60, 62, 249, 193, 3, 99, 34, 153, 42, 238, 77, 181, 80, 185, 223, 39, 8, 44, 215, 119,
|
||||
214, 30, 136, 19, 215, 35, 184, 69, 94, 10, 170, 179, 51, 183, 105, 237, 237, 48, 199,
|
||||
122, 159, 87, 183, 71, 230, 87, 102, 77, 81, 116, 28, 126, 195, 72, 50, 157, 223, 243,
|
||||
83, 36, 16, 168, 111, 209, 132, 12, 96, 56, 140, 57, 144, 75, 253, 119, 123, 168, 2,
|
||||
79, 214, 121, 80, 154, 93, 235, 222, 130, 181, 166, 97, 51, 106, 21, 138, 224, 8, 144,
|
||||
223, 162, 152, 183, 6, 80, 64, 144, 21, 155, 56, 255, 108, 248, 125, 196, 46, 99, 119,
|
||||
94, 104, 63, 46, 15, 165, 30, 98, 75, 212, 193, 116, 151, 189, 65, 42, 83, 253, 183,
|
||||
41, 195, 45, 206, 178, 66, 36, 215, 197, 105, 236, 79, 91, 135, 164, 71, 187, 199, 200,
|
||||
150, 226, 182, 254, 6, 234, 109, 3, 17, 116, 249, 44, 211, 184, 61, 189, 44, 181, 249,
|
||||
8, 58, 230, 236, 8, 188, 14, 178, 100, 120, 250, 29, 1, 204, 158, 46, 161, 39, 66, 76,
|
||||
42, 114, 149, 160, 31, 87, 254, 181, 224, 17, 162, 163, 99, 11, 34, 149, 50, 203, 205,
|
||||
224, 38, 18, 233, 161, 49, 7, 151, 63, 81, 68, 71, 174, 49, 22, 143, 93, 50, 0, 154,
|
||||
152, 178, 134, 147, 152, 118, 196, 241, 233, 67, 102, 149, 179, 213, 176, 118, 64, 172,
|
||||
143, 134, 196, 232, 154, 110, 129, 155, 159, 103, 117, 202, 11, 35, 75, 104, 5, 11,
|
||||
160, 147, 174, 49, 248, 45, 247, 16, 7, 64, 209, 255, 170, 243, 242, 40, 158, 94, 239,
|
||||
194, 225, 113, 24, 90, 243, 73, 137, 217, 175, 130, 50, 133, 139, 250, 145, 190, 76,
|
||||
151, 183, 30, 86, 146, 59, 171, 214, 211, 135, 203, 192, 42, 189, 90, 47, 152, 132,
|
||||
168, 252, 175, 71, 234, 118, 207, 161, 176, 254, 189, 54, 174, 160, 178, 158, 133, 122,
|
||||
63, 75, 95, 201, 55, 139, 2, 208, 232, 110, 74, 201, 196, 135, 244, 156, 87, 208, 101,
|
||||
203, 121, 187, 16, 106, 80, 120, 165, 44, 147, 182, 114, 173, 186, 185, 255, 99, 85,
|
||||
88, 26, 27, 43, 203, 176, 207, 88, 20, 253, 169, 210, 168, 109, 75, 234, 239, 8, 243,
|
||||
244, 65, 164, 193, 255, 240, 215, 54, 158, 188, 93, 93, 54, 46, 77, 152, 78, 174, 154,
|
||||
67, 248, 24, 235, 172, 240, 83, 224, 17, 100, 217, 15, 172, 176, 46, 85, 107, 105, 127,
|
||||
147, 158, 202, 255, 145, 237, 84, 223, 100, 214, 38, 133, 169, 112, 227, 138, 220, 125,
|
||||
72, 197, 5, 227, 94, 245, 42, 70, 33, 209, 243, 70, 229, 37, 118, 214, 147, 43, 87, 39,
|
||||
241, 107, 26, 169, 28, 72, 223, 133, 145, 44, 248, 213, 52, 127, 250, 99, 193, 115,
|
||||
113, 147, 89, 112, 237, 199, 208, 36, 155, 106, 144, 73, 249, 8, 116, 198, 107, 120,
|
||||
233, 145, 11, 155, 178, 7, 66, 157, 255, 206, 128, 155, 233, 111, 148, 194, 214, 238,
|
||||
252, 230, 96, 119, 30, 37, 73, 133, 129, 87, 185, 149, 251, 156, 17, 8, 83, 106, 207,
|
||||
98, 203, 100, 39, 199, 127, 253, 59, 37, 121, 161, 216, 146, 6, 178, 183, 243, 191, 91,
|
||||
106, 243, 132, 111, 216, 163, 87, 210, 197, 173, 146, 65, 131, 194, 96, 70, 6, 7, 192,
|
||||
45, 173, 71, 44, 134, 122, 60, 173, 208, 238, 22, 187, 208, 212, 51, 191, 185, 174, 3,
|
||||
125, 28, 134, 216, 209, 4, 224, 199, 16, 15, 56, 70, 188, 216, 92, 24, 96, 57, 125,
|
||||
138, 151, 73, 254, 245, 106, 53, 4, 150, 74, 43, 42, 4, 157, 238, 125, 168, 41, 224,
|
||||
22, 249, 45, 117, 32, 180, 161, 41, 39, 180, 96, 24, 2, 102, 57, 116, 34, 75, 90, 72,
|
||||
134, 176, 2, 196, 59, 143, 182, 201, 117, 178, 153, 81, 108, 167, 122, 139, 71, 197,
|
||||
55, 114, 60, 161, 130, 14, 29, 79, 152, 55, 136, 62, 190, 228, 202, 53, 126, 4, 173,
|
||||
99, 28, 190, 224, 255, 134, 123, 166, 162, 244, 55, 26, 81, 120, 207, 193, 10, 103,
|
||||
153, 215, 220, 12, 71, 67, 217, 154, 212, 44, 200, 232, 0, 178, 39, 44, 22, 7, 14, 215,
|
||||
183, 192, 104, 51, 46, 93, 102, 195, 65, 9, 191, 241, 237, 151, 5, 64, 103, 228, 162,
|
||||
41, 123, 29, 5, 80, 203, 198, 234, 230, 107, 53, 60, 58, 253, 47, 152, 22, 77, 81, 86,
|
||||
215, 132, 152, 135, 6, 218, 46, 92, 192, 218, 198, 234, 76, 178, 25, 203, 48, 61, 76,
|
||||
215, 96, 6, 49, 195, 37, 225, 10, 175, 222, 186, 133, 63, 50, 236, 215, 247, 17, 199,
|
||||
8, 134, 64, 246, 194, 167, 105, 15, 57, 62, 50, 51, 243, 192, 242, 122, 11, 46, 202,
|
||||
47, 10, 71, 153, 212, 226, 38, 12, 90, 150, 154, 152, 233, 7, 172, 111, 185, 160, 246,
|
||||
0, 166, 113, 90, 37, 203, 166, 43, 53, 255, 211, 127, 139, 73, 7, 10, 164, 1, 168, 223,
|
||||
87, 127, 43, 47, 87, 68, 84, 247, 223, 108, 108, 113, 36, 17, 50, 98, 236, 48, 10, 219,
|
||||
182, 107, 240, 198, 207, 20, 178, 9, 142, 14, 93, 163, 166, 147, 38, 176, 172, 156, 73,
|
||||
174, 238, 175, 231, 130, 159, 51, 128, 76, 34, 37, 138, 19, 3, 59, 71, 78, 144, 238,
|
||||
226, 214, 188, 27, 42, 142, 245, 238, 131, 190, 211, 240, 41, 122, 69, 124, 171, 75,
|
||||
115, 45, 144, 133, 176, 19, 81, 125, 230, 149, 235, 159, 6, 155, 195, 119, 62, 140, 50,
|
||||
52, 209, 124, 3, 93, 232, 20, 130, 138, 110, 60, 183, 177, 161, 52, 114, 91, 19, 211,
|
||||
156, 185, 202, 200, 36, 103, 253, 113, 45, 245, 177, 238, 43, 144, 38, 221, 0, 102, 50,
|
||||
255, 20, 154, 56, 156, 155, 92, 157, 57, 209, 77, 84, 88, 24, 116, 116, 54, 213, 222,
|
||||
76, 212, 193, 168, 216, 247, 125, 135, 114, 226, 128, 140, 250, 103, 82, 215, 238, 32,
|
||||
74, 252, 45, 224, 23, 95, 126, 124, 135, 124, 128, 53, 203, 40, 65, 222, 8, 83, 178,
|
||||
211, 64, 141, 64, 98, 188, 134, 100, 65, 166, 52, 249, 1, 206, 58, 55, 195, 23, 218,
|
||||
239, 41, 73, 88, 113, 148, 132, 209, 93, 37, 205, 58, 92, 14, 1, 133, 168, 162, 192,
|
||||
147, 70, 167, 101, 170, 152, 159, 0, 212, 26, 97, 49, 43, 217, 173, 38, 215, 136, 26,
|
||||
208, 244, 19, 83, 207, 38, 224, 254, 92, 169, 219, 236, 172, 49, 55, 98, 55, 15, 187,
|
||||
173, 114, 99, 130, 211, 78, 168, 221, 209, 250, 88, 189, 17, 186, 172, 129, 56, 90,
|
||||
238, 120, 23, 176, 87, 133, 81, 244, 29, 2, 215, 34, 88, 247, 231, 167, 56,
|
||||
];
|
||||
|
||||
let (exc_key, kc) =
|
||||
super::derive_aes256_gcm_key_hybrid(&cust_key, &host_key_1, &host_key_2).unwrap();
|
||||
|
||||
assert_eq!(exc_key, exp_key);
|
||||
assert_eq!(kc, exp_kc);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hkdf_rfc_5869() {
|
||||
use openssl::md::Md;
|
||||
@@ -933,4 +1298,94 @@ mod tests {
|
||||
}))
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_ec_key_valid() {
|
||||
let (cust_key, host_key) = get_test_keys();
|
||||
|
||||
// Both test keys are SECP521R1 EC keys
|
||||
assert!(validate_ec_key(&cust_key, "customer key", Nid::SECP521R1).is_ok());
|
||||
assert!(validate_ec_key(&host_key, "host key", Nid::SECP521R1).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_ec_key_wrong_curve() {
|
||||
let (cust_key, _) = get_test_keys();
|
||||
|
||||
// Test key is SECP521R1, but we expect SECP384R1
|
||||
let result = validate_ec_key(&cust_key, "customer key", Nid::SECP384R1);
|
||||
assert!(result.is_err());
|
||||
|
||||
if let Err(Error::RetrInvKey {
|
||||
what,
|
||||
kind,
|
||||
value,
|
||||
exp,
|
||||
}) = result
|
||||
{
|
||||
assert_eq!(what, "curve");
|
||||
assert_eq!(kind, "customer key");
|
||||
assert_eq!(value, "secp521r1");
|
||||
assert_eq!(exp, "secp384r1");
|
||||
} else {
|
||||
panic!("Expected RetrInvKey error");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_ec_key_not_ec() {
|
||||
let keypair = crate::get_test_asset!("keys/rsa2048key.pem");
|
||||
let keypair = PKey::private_key_from_pem(keypair).unwrap();
|
||||
|
||||
// RSA key is not an EC key
|
||||
let result = validate_ec_key(&keypair, "EC key", Nid::SECP521R1);
|
||||
assert!(result.is_err());
|
||||
|
||||
if let Err(Error::RetrInvKey {
|
||||
what,
|
||||
kind,
|
||||
value,
|
||||
exp,
|
||||
}) = result
|
||||
{
|
||||
assert_eq!(what, "key type");
|
||||
assert_eq!(kind, "EC key");
|
||||
assert_eq!(value, "RSA");
|
||||
assert_eq!(exp, "EC (secp521r1)");
|
||||
} else {
|
||||
panic!("Expected RetrInvKey error");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_mlkem_key_valid() {
|
||||
let (_, _, mlkem_key) = get_test_keys_hybrid();
|
||||
|
||||
// The third key from get_test_keys_hybrid is ML-KEM-1024
|
||||
assert!(validate_key_type(&mlkem_key, "ML-KEM key", KeyType::ML_KEM_1024).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_mlkem_key_wrong_type() {
|
||||
let (ec_key, _) = get_test_keys();
|
||||
|
||||
// EC key is not ML-KEM
|
||||
let result = validate_key_type(&ec_key, "EC key", KeyType::ML_KEM_1024);
|
||||
assert!(result.is_err());
|
||||
|
||||
if let Err(Error::RetrInvKey {
|
||||
what,
|
||||
kind,
|
||||
value,
|
||||
exp,
|
||||
}) = result
|
||||
{
|
||||
assert_eq!(what, "key type");
|
||||
assert_eq!(kind, "EC key");
|
||||
assert_eq!(value, "EC");
|
||||
assert_eq!(exp, "ML-KEM-1024");
|
||||
} else {
|
||||
panic!("Expected RetrInvKey error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,5 +14,4 @@ mod stackable_crl;
|
||||
pub use akid::*;
|
||||
pub use bio::*;
|
||||
pub use crl::*;
|
||||
#[expect(unused_imports)]
|
||||
pub use ml_kem::*;
|
||||
|
||||
@@ -47,7 +47,10 @@ impl EcPubKeyCoord {
|
||||
|
||||
/// Get the pub ECDH coordinates in the format the Ultravisor expects it:
|
||||
/// The two coordinates are padded to 80 bytes each.
|
||||
fn get_pub_ecdh_points(pkey: &EcPointRef, grp: &EcGroupRef) -> Result<[u8; 160], ErrorStack> {
|
||||
pub(crate) fn get_pub_ecdh_points(
|
||||
pkey: &EcPointRef,
|
||||
grp: &EcGroupRef,
|
||||
) -> Result<[u8; 160], ErrorStack> {
|
||||
let mut x = BigNum::new()?;
|
||||
let mut y = BigNum::new()?;
|
||||
let mut bn_ctx = BigNumContext::new()?;
|
||||
|
||||
@@ -9,7 +9,8 @@ use openssl::pkey::{PKey, PKeyRef, Private, Public};
|
||||
|
||||
use super::ec_coord::EcPubKeyCoord;
|
||||
use super::encrypt::Encrypt;
|
||||
use crate::crypto::{derive_aes256_gcm_key, encrypt_aead, hash};
|
||||
#[expect(unused)]
|
||||
use crate::crypto::{derive_aes256_gcm_key, derive_aes256_gcm_key_hybrid, encrypt_aead, hash};
|
||||
use crate::request::HostKey;
|
||||
use crate::Result;
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ mod request;
|
||||
|
||||
// Re-export public types
|
||||
pub use context::ReqEncrCtx;
|
||||
pub(crate) use ec_coord::get_pub_ecdh_points;
|
||||
pub use ec_coord::EcPubKeyCoord;
|
||||
pub use encrypt::{Aad, Encrypt};
|
||||
pub use header::RequestHdr;
|
||||
|
||||
@@ -89,6 +89,27 @@ pub fn get_test_key_and_cert() -> (PKey<Private>, X509) {
|
||||
(cust_key, host_key)
|
||||
}
|
||||
|
||||
pub fn get_test_key_and_cert_hybrid() -> (PKey<Private>, X509, X509) {
|
||||
let pub_key = get_test_asset!("keys/public_cust.bin");
|
||||
let priv_key = get_test_asset!("keys/private_cust.bin");
|
||||
let host_key = get_test_asset!("keys/host.ec.crt");
|
||||
let host_keys = get_test_asset!("keys/host.hybrid.crt");
|
||||
|
||||
assert_eq!(pub_key.len(), 160);
|
||||
assert_eq!(priv_key.len(), 80);
|
||||
|
||||
let cust_key = get_keypair(pub_key, priv_key).unwrap();
|
||||
let host_key1 = X509::from_pem(host_key).unwrap();
|
||||
let host_keys = X509::stack_from_pem(host_keys).unwrap();
|
||||
assert_eq!(host_keys.len(), 2);
|
||||
|
||||
println!("host_key1 = {host_key1:?}");
|
||||
println!("host_keys[0] = {:?}", host_keys[0]);
|
||||
println!("host_keys[1] = {:?}", host_keys[1]);
|
||||
|
||||
(cust_key, host_key1, host_keys[1].clone())
|
||||
}
|
||||
|
||||
/// TEST ONLY! Get a fixed private/public pair and a fixed public key
|
||||
///
|
||||
/// Intended for TESTING only. All parts of the key including the private key are checked in git and
|
||||
@@ -98,6 +119,15 @@ pub fn get_test_keys() -> (PKey<Private>, PKey<Public>) {
|
||||
(cust_key, host.public_key().unwrap())
|
||||
}
|
||||
|
||||
pub fn get_test_keys_hybrid() -> (PKey<Private>, PKey<Public>, PKey<Public>) {
|
||||
let (cust_key, host_key_1, host_key_2) = get_test_key_and_cert_hybrid();
|
||||
(
|
||||
cust_key,
|
||||
host_key_1.public_key().unwrap(),
|
||||
host_key_2.public_key().unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
fn read_ecdh_pubkey(coords: &[u8]) -> Result<PKey<Public>, ErrorStack> {
|
||||
assert!(coords.len() == 160);
|
||||
let x = BigNum::from_slice(&coords[..80])?;
|
||||
|
||||
Reference in New Issue
Block a user