From c3b808e1d1956a8603ae3dd11ec54015f03d015c Mon Sep 17 00:00:00 2001 From: Direktor799 Date: Sat, 12 Oct 2024 04:55:19 +0800 Subject: [PATCH 1/4] feat: add HKDF mechanisms Signed-off-by: Direktor799 --- cryptoki/src/mechanism/hkdf.rs | 116 +++++++++++++++++++++++++++++++++ cryptoki/src/mechanism/mod.rs | 37 ++++++++++- cryptoki/src/object.rs | 6 ++ 3 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 cryptoki/src/mechanism/hkdf.rs diff --git a/cryptoki/src/mechanism/hkdf.rs b/cryptoki/src/mechanism/hkdf.rs new file mode 100644 index 00000000..a3c8ad6f --- /dev/null +++ b/cryptoki/src/mechanism/hkdf.rs @@ -0,0 +1,116 @@ +//! Mechanisms of hash-based key derive function (HKDF) +//! See: + +use std::{convert::TryInto, marker::PhantomData, ptr::null_mut, slice}; + +use cryptoki_sys::{CKF_HKDF_SALT_DATA, CKF_HKDF_SALT_KEY, CKF_HKDF_SALT_NULL}; + +use crate::object::ObjectHandle; + +use super::MechanismType; + +#[derive(Debug, Clone, Copy)] +/// The salt for the extract stage. +pub enum HkdfSalt<'a> { + /// CKF_HKDF_SALT_NULL no salt is supplied. + Null, + /// CKF_HKDF_SALT_DATA salt is supplied as a data in pSalt with length ulSaltLen. + Data(&'a [u8]), + /// CKF_HKDF_SALT_KEY salt is supplied as a key in hSaltKey + Key(ObjectHandle), +} + +/// HKDF parameters. +/// +/// This structure wraps a `CK_HKDF_PARAMS` structure. +#[derive(Debug, Clone, Copy)] +#[repr(transparent)] +pub struct HkdfParams<'a> { + inner: cryptoki_sys::CK_HKDF_PARAMS, + /// Marker type to ensure we don't outlive the data + _marker: PhantomData<&'a [u8]>, +} + +impl<'a> HkdfParams<'a> { + /// Construct parameters for hash-based key derive function (HKDF). + /// + /// # Arguments + /// + /// * `extract` - Whether to execute the extract portion of HKDF. + /// + /// * `expand` - Whether to execute the expand portion of HKDF. + /// + /// * `prf_hash_mechanism` - The base hash used for the HMAC in the underlying HKDF operation + /// + /// * `salt` - The salt for the extract stage. + /// + /// * `info` - The info string for the expand stage. + pub fn new( + extract: bool, + expand: bool, + prf_hash_mechanism: MechanismType, + salt: HkdfSalt, + info: &'a [u8], + ) -> Self { + Self { + inner: cryptoki_sys::CK_HKDF_PARAMS { + bExtract: extract as u8, + bExpand: expand as u8, + prfHashMechanism: *prf_hash_mechanism, + ulSaltType: match salt { + HkdfSalt::Null => CKF_HKDF_SALT_NULL, + HkdfSalt::Data(_) => CKF_HKDF_SALT_DATA, + HkdfSalt::Key(_) => CKF_HKDF_SALT_KEY, + }, + pSalt: match salt { + HkdfSalt::Data(data) => data.as_ptr() as *mut _, + _ => null_mut(), + }, + ulSaltLen: match salt { + HkdfSalt::Data(data) => data + .len() + .try_into() + .expect("salt length does not fit in CK_ULONG"), + _ => 0, + }, + hSaltKey: match salt { + HkdfSalt::Key(key) => key.handle(), + _ => 0, + }, + pInfo: info.as_ptr() as *mut _, + ulInfoLen: info + .len() + .try_into() + .expect("info length does not fit in CK_ULONG"), + }, + _marker: PhantomData, + } + } + + /// Whether to execute the extract portion of HKDF. + pub fn extract(&self) -> bool { + self.inner.bExtract != 0 + } + + /// Whether to execute the expand portion of HKDF. + pub fn expand(&self) -> bool { + self.inner.bExpand != 0 + } + + /// The salt for the extract stage. + pub fn salt(&self) -> HkdfSalt<'a> { + match self.inner.ulSaltType { + CKF_HKDF_SALT_NULL => HkdfSalt::Null, + CKF_HKDF_SALT_DATA => HkdfSalt::Data(unsafe { + slice::from_raw_parts(self.inner.pSalt, self.inner.ulSaltLen as _) + }), + CKF_HKDF_SALT_KEY => HkdfSalt::Key(ObjectHandle::new(self.inner.hSaltKey)), + _ => unreachable!(), + } + } + + /// The info string for the expand stage. + pub fn info(&self) -> &'a [u8] { + unsafe { slice::from_raw_parts(self.inner.pInfo, self.inner.ulInfoLen as _) } + } +} diff --git a/cryptoki/src/mechanism/mod.rs b/cryptoki/src/mechanism/mod.rs index 975a09cd..c91ece76 100644 --- a/cryptoki/src/mechanism/mod.rs +++ b/cryptoki/src/mechanism/mod.rs @@ -5,6 +5,7 @@ pub mod aead; pub mod ekdf; pub mod elliptic_curve; +pub mod hkdf; mod mechanism_info; pub mod rsa; @@ -285,6 +286,18 @@ impl MechanismType { val: CKM_GENERIC_SECRET_KEY_GEN, }; + // HKDF + /// HKDF key generation mechanism + pub const HKDF_KEY_GEN: MechanismType = MechanismType { + val: CKM_HKDF_KEY_GEN, + }; + /// HKDF-DERIVE mechanism + pub const HKDF_DERIVE: MechanismType = MechanismType { + val: CKM_HKDF_DERIVE, + }; + /// HKDF-DATA mechanism + pub const HKDF_DATA: MechanismType = MechanismType { val: CKM_HKDF_DATA }; + pub(crate) fn stringify(mech: CK_MECHANISM_TYPE) -> String { match mech { CKM_RSA_PKCS_KEY_PAIR_GEN => String::from(stringify!(CKM_RSA_PKCS_KEY_PAIR_GEN)), @@ -641,6 +654,9 @@ impl MechanismType { String::from(stringify!(CKM_EC_MONTGOMERY_KEY_PAIR_GEN)) } CKM_EDDSA => String::from(stringify!(CKM_EDDSA)), + CKM_HKDF_KEY_GEN => String::from(stringify!(CKM_HKDF_KEY_GEN)), + CKM_HKDF_DERIVE => String::from(stringify!(CKM_HKDF_DERIVE)), + CKM_HKDF_DATA => String::from(stringify!(CKM_HKDF_DATA)), _ => format!("unknown {mech:08x}"), } } @@ -717,6 +733,9 @@ impl TryFrom for MechanismType { CKM_SHA384_HMAC => Ok(MechanismType::SHA384_HMAC), CKM_SHA512_HMAC => Ok(MechanismType::SHA512_HMAC), CKM_GENERIC_SECRET_KEY_GEN => Ok(MechanismType::GENERIC_SECRET_KEY_GEN), + CKM_HKDF_KEY_GEN => Ok(MechanismType::HKDF_KEY_GEN), + CKM_HKDF_DERIVE => Ok(MechanismType::HKDF_DERIVE), + CKM_HKDF_DATA => Ok(MechanismType::HKDF_DATA), other => { error!("Mechanism type {} is not supported.", other); Err(Error::NotSupported) @@ -910,6 +929,14 @@ pub enum Mechanism<'a> { /// GENERIC-SECRET-KEY-GEN mechanism GenericSecretKeyGen, + + // HKDF + /// HKDF key gen mechanism + HkdfKeyGen, + /// HKDF-DERIVE mechanism + HkdfDerive(hkdf::HkdfParams<'a>), + /// HKDF-DATA mechanism + HkdfData(hkdf::HkdfParams<'a>), } impl Mechanism<'_> { @@ -977,6 +1004,10 @@ impl Mechanism<'_> { Mechanism::Sha512Hmac => MechanismType::SHA512_HMAC, Mechanism::GenericSecretKeyGen => MechanismType::GENERIC_SECRET_KEY_GEN, + + Mechanism::HkdfKeyGen => MechanismType::HKDF_KEY_GEN, + Mechanism::HkdfDerive(_) => MechanismType::HKDF_DERIVE, + Mechanism::HkdfData(_) => MechanismType::HKDF_DATA, } } } @@ -1008,6 +1039,9 @@ impl From<&Mechanism<'_>> for CK_MECHANISM { | Mechanism::Sha512RsaPkcsPss(params) => make_mechanism(mechanism, params), Mechanism::RsaPkcsOaep(params) => make_mechanism(mechanism, params), Mechanism::Ecdh1Derive(params) => make_mechanism(mechanism, params), + Mechanism::HkdfDerive(params) | Mechanism::HkdfData(params) => { + make_mechanism(mechanism, params) + } // Mechanisms without parameters Mechanism::AesKeyGen | Mechanism::AesEcb @@ -1047,7 +1081,8 @@ impl From<&Mechanism<'_>> for CK_MECHANISM { | Mechanism::Sha256Hmac | Mechanism::Sha384Hmac | Mechanism::Sha512Hmac - | Mechanism::GenericSecretKeyGen => CK_MECHANISM { + | Mechanism::GenericSecretKeyGen + | Mechanism::HkdfKeyGen => CK_MECHANISM { mechanism, pParameter: null_mut(), ulParameterLen: 0, diff --git a/cryptoki/src/object.rs b/cryptoki/src/object.rs index d66d97c4..19c74de5 100644 --- a/cryptoki/src/object.rs +++ b/cryptoki/src/object.rs @@ -1192,6 +1192,9 @@ impl KeyType { val: CKK_EC_MONTGOMERY, }; + /// HKDF key + pub const HKDF: KeyType = KeyType { val: CKK_HKDF }; + fn stringify(key_type: CK_KEY_TYPE) -> String { match key_type { CKK_RSA => String::from(stringify!(CKK_RSA)), @@ -1236,6 +1239,8 @@ impl KeyType { CKK_GOSTR3411 => String::from(stringify!(CKK_GOSTR3411)), CKK_GOST28147 => String::from(stringify!(CKK_GOST28147)), CKK_EC_EDWARDS => String::from(stringify!(CKK_EC_EDWARDS)), + CKK_EC_MONTGOMERY => String::from(stringify!(CKK_EC_MONTGOMERY)), + CKK_HKDF => String::from(stringify!(CKK_HKDF)), _ => format!("unknown ({key_type:08x})"), } } @@ -1309,6 +1314,7 @@ impl TryFrom for KeyType { CKK_GOST28147 => Ok(KeyType::GOST28147), CKK_EC_EDWARDS => Ok(KeyType::EC_EDWARDS), CKK_EC_MONTGOMERY => Ok(KeyType::EC_MONTGOMERY), + CKK_HKDF => Ok(KeyType::HKDF), _ => { error!("Key type {} is not supported.", key_type); Err(Error::NotSupported) From 0fb0aaae8729867fb604aed4d450c712c22cd14a Mon Sep 17 00:00:00 2001 From: Direktor799 Date: Wed, 23 Oct 2024 18:24:23 +0800 Subject: [PATCH 2/4] style: add header, change match style Signed-off-by: Direktor799 --- cryptoki/src/mechanism/hkdf.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/cryptoki/src/mechanism/hkdf.rs b/cryptoki/src/mechanism/hkdf.rs index a3c8ad6f..14ad53a0 100644 --- a/cryptoki/src/mechanism/hkdf.rs +++ b/cryptoki/src/mechanism/hkdf.rs @@ -1,3 +1,5 @@ +// Copyright 2021 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 //! Mechanisms of hash-based key derive function (HKDF) //! See: @@ -9,8 +11,8 @@ use crate::object::ObjectHandle; use super::MechanismType; -#[derive(Debug, Clone, Copy)] /// The salt for the extract stage. +#[derive(Debug, Clone, Copy)] pub enum HkdfSalt<'a> { /// CKF_HKDF_SALT_NULL no salt is supplied. Null, @@ -62,16 +64,17 @@ impl<'a> HkdfParams<'a> { HkdfSalt::Data(_) => CKF_HKDF_SALT_DATA, HkdfSalt::Key(_) => CKF_HKDF_SALT_KEY, }, - pSalt: match salt { - HkdfSalt::Data(data) => data.as_ptr() as *mut _, - _ => null_mut(), + pSalt: if let HkdfSalt::Data(data) = salt { + data.as_ptr() as *mut _ + } else { + null_mut() }, - ulSaltLen: match salt { - HkdfSalt::Data(data) => data - .len() + ulSaltLen: if let HkdfSalt::Data(data) = salt { + data.len() .try_into() - .expect("salt length does not fit in CK_ULONG"), - _ => 0, + .expect("salt length does not fit in CK_ULONG") + } else { + 0 }, hSaltKey: match salt { HkdfSalt::Key(key) => key.handle(), From 31d345980a32698879397940d2b90cc39d12d9d8 Mon Sep 17 00:00:00 2001 From: Direktor799 Date: Wed, 23 Oct 2024 18:45:27 +0800 Subject: [PATCH 3/4] refactor: use Option instead of bool in HkdfParams Signed-off-by: Direktor799 --- cryptoki/src/mechanism/hkdf.rs | 52 ++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/cryptoki/src/mechanism/hkdf.rs b/cryptoki/src/mechanism/hkdf.rs index 14ad53a0..5ef139b9 100644 --- a/cryptoki/src/mechanism/hkdf.rs +++ b/cryptoki/src/mechanism/hkdf.rs @@ -38,53 +38,55 @@ impl<'a> HkdfParams<'a> { /// /// # Arguments /// - /// * `extract` - Whether to execute the extract portion of HKDF. - /// - /// * `expand` - Whether to execute the expand portion of HKDF. - /// /// * `prf_hash_mechanism` - The base hash used for the HMAC in the underlying HKDF operation /// - /// * `salt` - The salt for the extract stage. + /// * `salt` - The salt for the extract stage, skip extract if `None`. /// - /// * `info` - The info string for the expand stage. + /// * `info` - The info string for the expand stage, skip expand if `None`. pub fn new( - extract: bool, - expand: bool, prf_hash_mechanism: MechanismType, - salt: HkdfSalt, - info: &'a [u8], + salt: Option, + info: Option<&'a [u8]>, ) -> Self { Self { inner: cryptoki_sys::CK_HKDF_PARAMS { - bExtract: extract as u8, - bExpand: expand as u8, + bExtract: salt.is_some() as u8, + bExpand: info.is_some() as u8, prfHashMechanism: *prf_hash_mechanism, ulSaltType: match salt { - HkdfSalt::Null => CKF_HKDF_SALT_NULL, - HkdfSalt::Data(_) => CKF_HKDF_SALT_DATA, - HkdfSalt::Key(_) => CKF_HKDF_SALT_KEY, + None | Some(HkdfSalt::Null) => CKF_HKDF_SALT_NULL, + Some(HkdfSalt::Data(_)) => CKF_HKDF_SALT_DATA, + Some(HkdfSalt::Key(_)) => CKF_HKDF_SALT_KEY, }, - pSalt: if let HkdfSalt::Data(data) = salt { + pSalt: if let Some(HkdfSalt::Data(data)) = salt { data.as_ptr() as *mut _ } else { null_mut() }, - ulSaltLen: if let HkdfSalt::Data(data) = salt { + ulSaltLen: if let Some(HkdfSalt::Data(data)) = salt { data.len() .try_into() .expect("salt length does not fit in CK_ULONG") } else { 0 }, - hSaltKey: match salt { - HkdfSalt::Key(key) => key.handle(), - _ => 0, + hSaltKey: if let Some(HkdfSalt::Key(key)) = salt { + key.handle() + } else { + 0 + }, + pInfo: if let Some(info) = info { + info.as_ptr() as *mut _ + } else { + null_mut() + }, + ulInfoLen: if let Some(info) = info { + info.len() + .try_into() + .expect("salt length does not fit in CK_ULONG") + } else { + 0 }, - pInfo: info.as_ptr() as *mut _, - ulInfoLen: info - .len() - .try_into() - .expect("info length does not fit in CK_ULONG"), }, _marker: PhantomData, } From ed6e72ef160cd590cd84cbcd69217aa60e19e83b Mon Sep 17 00:00:00 2001 From: Direktor799 Date: Wed, 23 Oct 2024 20:57:37 +0800 Subject: [PATCH 4/4] chore: update copyright header year Signed-off-by: Direktor799 --- cryptoki/src/mechanism/hkdf.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cryptoki/src/mechanism/hkdf.rs b/cryptoki/src/mechanism/hkdf.rs index 5ef139b9..83b78529 100644 --- a/cryptoki/src/mechanism/hkdf.rs +++ b/cryptoki/src/mechanism/hkdf.rs @@ -1,4 +1,4 @@ -// Copyright 2021 Contributors to the Parsec project. +// Copyright 2024 Contributors to the Parsec project. // SPDX-License-Identifier: Apache-2.0 //! Mechanisms of hash-based key derive function (HKDF) //! See: