use super ::{
super ::hpke::{Aead, Kdf},
p11::{
sys::{
self , CKA_DERIVE, CKF_HKDF_SALT_DATA, CKF_HKDF_SALT_NULL, CKM_AES_GCM,
CKM_CHACHA20_POLY1305, CKM_HKDF_DATA, CKM_HKDF_DERIVE, CKM_SHA256, CK_BBOOL,
CK_HKDF_PARAMS, CK_INVALID_HANDLE, CK_MECHANISM_TYPE, CK_OBJECT_HANDLE, CK_ULONG,
},
ParamItem, SymKey,
},
};
use crate ::err::Res;
use log::trace;
use std::{convert::TryFrom, os::raw::c_int, ptr::null_mut};
#[ derive(Clone, Copy)]
pub enum KeyMechanism {
Aead(Aead),
#[ allow(dead_code)]
// We don't use this one.
Hkdf,
}
impl KeyMechanism {
fn mech(
self ) -> CK_MECHANISM_TYPE {
CK_MECHANISM_TYPE::from(
match self {
Self ::Aead(Aead::Aes128Gcm) |
Self ::Aead(Aead::Aes256Gcm) => CKM_AES_GCM,
Self ::Aead(Aead::ChaCha20Poly1305) => CKM_CHACHA20_POLY1305,
Self ::Hkdf => CKM_HKDF_DERIVE,
})
}
fn len(
self ) -> usize {
match self {
Self ::Aead(a) => a.n_k(),
Self ::Hkdf =>
0 ,
// Let the underlying module decide.
}
}
}
pub struct Hkdf {
kdf: Kdf,
}
impl Hkdf {
pub fn new(kdf: Kdf) ->
Self {
Self { kdf }
}
#[ cfg(test)]
pub fn import_ikm(ikm: &[u8]) -> Res<SymKey> {
let slot =
super ::p11::Slot::internal()?;
let ptr =
unsafe {
sys::PK11_ImportSymKey(
*slot,
CK_MECHANISM_TYPE::from(sys::CKM_HKDF_KEY_GEN),
sys::PK11Origin::PK11_OriginUnwrap,
sys::CK_ATTRIBUTE_TYPE::from(sys::CKA_SIGN),
&
mut super ::p11::Item::wrap(ikm),
null_mut(),
)
};
SymKey::from_ptr(ptr)
}
fn mech(&
self ) -> CK_MECHANISM_TYPE {
CK_MECHANISM_TYPE::from(
match self .kdf {
Kdf::HkdfSha256 => CKM_SHA256,
_ => unimplemented!(),
})
}
pub fn extract(&
self , salt: &[u8], ikm: &SymKey) -> Res<SymKey> {
let salt_type =
if salt.is_empty() {
CKF_HKDF_SALT_NULL
}
else {
CKF_HKDF_SALT_DATA
};
let mut params = CK_HKDF_PARAMS {
bExtract: CK_BBOOL::from(
true ),
bExpand: CK_BBOOL::from(
false ),
prfHashMechanism:
self .mech(),
ulSaltType: CK_ULONG::from(salt_type),
pSalt: salt.as_ptr()
as *
mut _,
// const-cast = bad API
ulSaltLen: CK_ULONG::try_from(salt.len()).unwrap(),
hSaltKey: CK_OBJECT_HANDLE::from(CK_INVALID_HANDLE),
pInfo: null_mut(),
ulInfoLen:
0 ,
};
let mut params_item = ParamItem::new(&
mut params);
let ptr =
unsafe {
sys::PK11_Derive(
**ikm,
CK_MECHANISM_TYPE::from(CKM_HKDF_DERIVE),
params_item.ptr(),
CK_MECHANISM_TYPE::from(CKM_HKDF_DERIVE),
CK_MECHANISM_TYPE::from(CKA_DERIVE),
0 ,
)
};
let prk = SymKey::from_ptr(ptr)?;
trace!(
"HKDF extract: salt={} ikm={} prk={}" ,
hex::encode(salt),
hex::encode(ikm.key_data()?),
hex::encode(prk.key_data()?),
);
Ok(prk)
}
// NB: `info` must outlive the returned value.
fn expand_params(&
self , info: &[u8]) -> CK_HKDF_PARAMS {
CK_HKDF_PARAMS {
bExtract: CK_BBOOL::from(
false ),
bExpand: CK_BBOOL::from(
true ),
prfHashMechanism:
self .mech(),
ulSaltType: CK_ULONG::from(CKF_HKDF_SALT_NULL),
pSalt: null_mut(),
ulSaltLen:
0 ,
hSaltKey: CK_OBJECT_HANDLE::from(CK_INVALID_HANDLE),
pInfo: info.as_ptr()
as *
mut _,
// const-cast = bad API
ulInfoLen: CK_ULONG::try_from(info.len()).unwrap(),
}
}
pub fn expand_key(&
self , prk: &SymKey, info: &[u8], key_mech: KeyMecha
nism) -> Res<SymKey> {
let mut params = self .expand_params(info);
let mut params_item = ParamItem::new(&mut params);
let ptr = unsafe {
sys::PK11_Derive(
**prk,
CK_MECHANISM_TYPE::from(CKM_HKDF_DERIVE),
params_item.ptr(),
key_mech.mech(),
CK_MECHANISM_TYPE::from(CKA_DERIVE),
c_int::try_from(key_mech.len()).unwrap(),
)
};
let okm = SymKey::from_ptr(ptr)?;
trace!(
"HKDF expand_key: prk={} info={} okm={}" ,
hex::encode(prk.key_data()?),
hex::encode(info),
hex::encode(okm.key_data()?),
);
Ok(okm)
}
pub fn expand_data(&self , prk: &SymKey, info: &[u8], len: usize) -> Res<Vec<u8>> {
let mut params = self .expand_params(info);
let mut params_item = ParamItem::new(&mut params);
let ptr = unsafe {
sys::PK11_Derive(
**prk,
CK_MECHANISM_TYPE::from(CKM_HKDF_DATA),
params_item.ptr(),
CK_MECHANISM_TYPE::from(CKM_HKDF_DERIVE),
CK_MECHANISM_TYPE::from(CKA_DERIVE),
c_int::try_from(len).unwrap(),
)
};
let k = SymKey::from_ptr(ptr)?;
let r = Vec::from(k.key_data()?);
trace!(
"HKDF expand_data: prk={} info={} okm={}" ,
hex::encode(prk.key_data()?),
hex::encode(info),
hex::encode(&r),
);
Ok(r)
}
}
#[ cfg(test)]
mod test {
use super ::{super ::super ::hpke::Kdf, Hkdf};
use crate ::init;
fn sha256_example(
ikm: &[u8],
salt: &[u8],
info: &[u8],
l: usize,
expected_prk: &[u8],
expected_okm: &[u8],
) {
init();
let hkdf = Hkdf::new(Kdf::HkdfSha256);
let k_ikm = Hkdf::import_ikm(ikm).unwrap();
let prk = hkdf.extract(salt, &k_ikm).unwrap();
let prk_data = prk.key_data().unwrap();
assert_eq!(prk_data, expected_prk);
let out = hkdf.expand_data(&prk, info, l).unwrap();
assert_eq!(&out[..], expected_okm);
}
/// Example 1 from <https://tools.ietf.org/html/rfc5869#appendix-A.1 >
#[ test]
fn example1() {
const IKM: &[u8] = &[
0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b,
0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b,
];
const SALT: &[u8] = &[
0 x00, 0 x01, 0 x02, 0 x03, 0 x04, 0 x05, 0 x06, 0 x07, 0 x08, 0 x09, 0 x0a, 0 x0b, 0 x0c,
];
const INFO: &[u8] = &[0 xf0, 0 xf1, 0 xf2, 0 xf3, 0 xf4, 0 xf5, 0 xf6, 0 xf7, 0 xf8, 0 xf9];
const L: usize = 42 ;
const PRK: &[u8] = &[
0 x07, 0 x77, 0 x09, 0 x36, 0 x2c, 0 x2e, 0 x32, 0 xdf, 0 x0d, 0 xdc, 0 x3f, 0 x0d, 0 xc4, 0 x7b,
0 xba, 0 x63, 0 x90, 0 xb6, 0 xc7, 0 x3b, 0 xb5, 0 x0f, 0 x9c, 0 x31, 0 x22, 0 xec, 0 x84, 0 x4a,
0 xd7, 0 xc2, 0 xb3, 0 xe5,
];
const OKM: &[u8] = &[
0 x3c, 0 xb2, 0 x5f, 0 x25, 0 xfa, 0 xac, 0 xd5, 0 x7a, 0 x90, 0 x43, 0 x4f, 0 x64, 0 xd0, 0 x36,
0 x2f, 0 x2a, 0 x2d, 0 x2d, 0 x0a, 0 x90, 0 xcf, 0 x1a, 0 x5a, 0 x4c, 0 x5d, 0 xb0, 0 x2d, 0 x56,
0 xec, 0 xc4, 0 xc5, 0 xbf, 0 x34, 0 x00, 0 x72, 0 x08, 0 xd5, 0 xb8, 0 x87, 0 x18, 0 x58, 0 x65,
];
sha256_example(IKM, SALT, INFO, L, PRK, OKM);
}
/// Example 2 from <https://tools.ietf.org/html/rfc5869#appendix-A.2 >
#[ test]
fn example2() {
const IKM: &[u8] = &[
0 x00, 0 x01, 0 x02, 0 x03, 0 x04, 0 x05, 0 x06, 0 x07, 0 x08, 0 x09, 0 x0a, 0 x0b, 0 x0c, 0 x0d,
0 x0e, 0 x0f, 0 x10, 0 x11, 0 x12, 0 x13, 0 x14, 0 x15, 0 x16, 0 x17, 0 x18, 0 x19, 0 x1a, 0 x1b,
0 x1c, 0 x1d, 0 x1e, 0 x1f, 0 x20, 0 x21, 0 x22, 0 x23, 0 x24, 0 x25, 0 x26, 0 x27, 0 x28, 0 x29,
0 x2a, 0 x2b, 0 x2c, 0 x2d, 0 x2e, 0 x2f, 0 x30, 0 x31, 0 x32, 0 x33, 0 x34, 0 x35, 0 x36, 0 x37,
0 x38, 0 x39, 0 x3a, 0 x3b, 0 x3c, 0 x3d, 0 x3e, 0 x3f, 0 x40, 0 x41, 0 x42, 0 x43, 0 x44, 0 x45,
0 x46, 0 x47, 0 x48, 0 x49, 0 x4a, 0 x4b, 0 x4c, 0 x4d, 0 x4e, 0 x4f,
];
const SALT: &[u8] = &[
0 x60, 0 x61, 0 x62, 0 x63, 0 x64, 0 x65, 0 x66, 0 x67, 0 x68, 0 x69, 0 x6a, 0 x6b, 0 x6c, 0 x6d,
0 x6e, 0 x6f, 0 x70, 0 x71, 0 x72, 0 x73, 0 x74, 0 x75, 0 x76, 0 x77, 0 x78, 0 x79, 0 x7a, 0 x7b,
0 x7c, 0 x7d, 0 x7e, 0 x7f, 0 x80, 0 x81, 0 x82, 0 x83, 0 x84, 0 x85, 0 x86, 0 x87, 0 x88, 0 x89,
0 x8a, 0 x8b, 0 x8c, 0 x8d, 0 x8e, 0 x8f, 0 x90, 0 x91, 0 x92, 0 x93, 0 x94, 0 x95, 0 x96, 0 x97,
0 x98, 0 x99, 0 x9a, 0 x9b, 0 x9c, 0 x9d, 0 x9e, 0 x9f, 0 xa0, 0 xa1, 0 xa2, 0 xa3, 0 xa4, 0 xa5,
0 xa6, 0 xa7, 0 xa8, 0 xa9, 0 xaa, 0 xab, 0 xac, 0 xad, 0 xae, 0 xaf,
];
const INFO: &[u8] = &[
0 xb0, 0 xb1, 0 xb2, 0 xb3, 0 xb4, 0 xb5, 0 xb6, 0 xb7, 0 xb8, 0 xb9, 0 xba, 0 xbb, 0 xbc, 0 xbd,
0 xbe, 0 xbf, 0 xc0, 0 xc1, 0 xc2, 0 xc3, 0 xc4, 0 xc5, 0 xc6, 0 xc7, 0 xc8, 0 xc9, 0 xca, 0 xcb,
0 xcc, 0 xcd, 0 xce, 0 xcf, 0 xd0, 0 xd1, 0 xd2, 0 xd3, 0 xd4, 0 xd5, 0 xd6, 0 xd7, 0 xd8, 0 xd9,
0 xda, 0 xdb, 0 xdc, 0 xdd, 0 xde, 0 xdf, 0 xe0, 0 xe1, 0 xe2, 0 xe3, 0 xe4, 0 xe5, 0 xe6, 0 xe7,
0 xe8, 0 xe9, 0 xea, 0 xeb, 0 xec, 0 xed, 0 xee, 0 xef, 0 xf0, 0 xf1, 0 xf2, 0 xf3, 0 xf4, 0 xf5,
0 xf6, 0 xf7, 0 xf8, 0 xf9, 0 xfa, 0 xfb, 0 xfc, 0 xfd, 0 xfe, 0 xff,
];
const L: usize = 82 ;
const PRK: &[u8] = &[
0 x06, 0 xa6, 0 xb8, 0 x8c, 0 x58, 0 x53, 0 x36, 0 x1a, 0 x06, 0 x10, 0 x4c, 0 x9c, 0 xeb, 0 x35,
0 xb4, 0 x5c, 0 xef, 0 x76, 0 x00, 0 x14, 0 x90, 0 x46, 0 x71, 0 x01, 0 x4a, 0 x19, 0 x3f, 0 x40,
0 xc1, 0 x5f, 0 xc2, 0 x44,
];
const OKM: &[u8] = &[
0 xb1, 0 x1e, 0 x39, 0 x8d, 0 xc8, 0 x03, 0 x27, 0 xa1, 0 xc8, 0 xe7, 0 xf7, 0 x8c, 0 x59, 0 x6a,
0 x49, 0 x34, 0 x4f, 0 x01, 0 x2e, 0 xda, 0 x2d, 0 x4e, 0 xfa, 0 xd8, 0 xa0, 0 x50, 0 xcc, 0 x4c,
0 x19, 0 xaf, 0 xa9, 0 x7c, 0 x59, 0 x04, 0 x5a, 0 x99, 0 xca, 0 xc7, 0 x82, 0 x72, 0 x71, 0 xcb,
0 x41, 0 xc6, 0 x5e, 0 x59, 0 x0e, 0 x09, 0 xda, 0 x32, 0 x75, 0 x60, 0 x0c, 0 x2f, 0 x09, 0 xb8,
0 x36, 0 x77, 0 x93, 0 xa9, 0 xac, 0 xa3, 0 xdb, 0 x71, 0 xcc, 0 x30, 0 xc5, 0 x81, 0 x79, 0 xec,
0 x3e, 0 x87, 0 xc1, 0 x4c, 0 x01, 0 xd5, 0 xc1, 0 xf3, 0 x43, 0 x4f, 0 x1d, 0 x87,
];
sha256_example(IKM, SALT, INFO, L, PRK, OKM);
}
/// Example 3 from <https://tools.ietf.org/html/rfc5869#appendix-A.3 >
#[ test]
fn example3() {
const IKM: &[u8] = &[
0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b,
0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0b,
];
const SALT: &[u8] = &[];
const INFO: &[u8] = &[];
const L: usize = 42 ;
const PRK: &[u8] = &[
0 x19, 0 xef, 0 x24, 0 xa3, 0 x2c, 0 x71, 0 x7b, 0 x16, 0 x7f, 0 x33, 0 xa9, 0 x1d, 0 x6f, 0 x64,
0 x8b, 0 xdf, 0 x96, 0 x59, 0 x67, 0 x76, 0 xaf, 0 xdb, 0 x63, 0 x77, 0 xac, 0 x43, 0 x4c, 0 x1c,
0 x29, 0 x3c, 0 xcb, 0 x04,
];
const OKM: &[u8] = &[
0 x8d, 0 xa4, 0 xe7, 0 x75, 0 xa5, 0 x63, 0 xc1, 0 x8f, 0 x71, 0 x5f, 0 x80, 0 x2a, 0 x06, 0 x3c,
0 x5a, 0 x31, 0 xb8, 0 xa1, 0 x1f, 0 x5c, 0 x5e, 0 xe1, 0 x87, 0 x9e, 0 xc3, 0 x45, 0 x4e, 0 x5f,
0 x3c, 0 x73, 0 x8d, 0 x2d, 0 x9d, 0 x20, 0 x13, 0 x95, 0 xfa, 0 xa4, 0 xb6, 0 x1a, 0 x96, 0 xc8,
];
sha256_example(IKM, SALT, INFO, L, PRK, OKM);
}
}
Messung V0.5 in Prozent C=98 H=94 G=95
¤ Dauer der Verarbeitung: 0.9 Sekunden
(vorverarbeitet am 2026-06-19)
¤
*© Formatika GbR, Deutschland