diff --git a/cryptoki/src/mechanism/kbkdf.rs b/cryptoki/src/mechanism/kbkdf.rs new file mode 100644 index 00000000..0ec0672f --- /dev/null +++ b/cryptoki/src/mechanism/kbkdf.rs @@ -0,0 +1,373 @@ +// Copyright 2025 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 +//! Mechanisms of NIST key-based key derive functions (SP 800-108, informally KBKDF) +//! See: + +use core::{convert::TryInto, marker::PhantomData, ptr}; +use std::num::NonZeroUsize; + +use cryptoki_sys::{ + CK_ATTRIBUTE, CK_ATTRIBUTE_PTR, CK_DERIVED_KEY, CK_DERIVED_KEY_PTR, CK_INVALID_HANDLE, + CK_OBJECT_HANDLE, CK_OBJECT_HANDLE_PTR, CK_PRF_DATA_PARAM, CK_PRF_DATA_PARAM_PTR, + CK_SP800_108_BYTE_ARRAY, CK_SP800_108_COUNTER, CK_SP800_108_COUNTER_FORMAT, + CK_SP800_108_DKM_LENGTH, CK_SP800_108_DKM_LENGTH_FORMAT, CK_SP800_108_DKM_LENGTH_SUM_OF_KEYS, + CK_SP800_108_DKM_LENGTH_SUM_OF_SEGMENTS, CK_SP800_108_FEEDBACK_KDF_PARAMS, + CK_SP800_108_ITERATION_VARIABLE, CK_SP800_108_KDF_PARAMS, CK_ULONG, +}; + +use crate::object::{Attribute, ObjectHandle}; + +use super::MechanismType; + +/// Endianness of byte representation of data. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Endianness { + /// Little endian. + Little, + /// Big endian. + Big, +} + +/// Defines encoding format for a counter value. +/// +/// This structure wraps a `CK_SP800_108_COUNTER_FORMAT` structure. +#[derive(Debug, Clone, Copy)] +#[repr(transparent)] +pub struct KbkdfCounterFormat(CK_SP800_108_COUNTER_FORMAT); + +impl KbkdfCounterFormat { + /// Construct encoding format for KDF's internal counter variable. + /// + /// # Arguments + /// + /// * `endianness` - The endianness of the counter's bit representation. + /// + /// * `width_in_bits` - The number of bits used to represent the counter value. + pub fn new(endianness: Endianness, width_in_bits: NonZeroUsize) -> Self { + Self(CK_SP800_108_COUNTER_FORMAT { + bLittleEndian: (endianness == Endianness::Little).into(), + ulWidthInBits: width_in_bits + .get() + .try_into() + .expect("bit width of KBKDF internal counter does not fit in CK_ULONG"), + }) + } +} + +/// Method for calculating length of DKM (derived key material). +/// +/// Corresponds to CK_SP800_108_DKM_LENGTH_METHOD. +#[derive(Debug, Clone, Copy)] +pub enum KbkdfDkmLengthMethod { + /// Sum of length of all keys derived by given invocation of KDF. + SumOfKeys, + /// Sum of length of all segments of output produced by PRF in given invocation of KDF. + SumOfSegments, +} + +/// Defines encoding format for DKM (derived key material). +/// +/// This structure wraps a `CK_SP800_108_DKM_LENGTH_FORMAT` structure. +#[derive(Debug, Clone, Copy)] +#[repr(transparent)] +pub struct KbkdfDkmLengthFormat(CK_SP800_108_DKM_LENGTH_FORMAT); + +impl KbkdfDkmLengthFormat { + /// Construct encoding format for length value of DKM (derived key material) from KDF. + /// + /// # Arguments + /// + /// * `dkm_length_method` - The method used to calculate the DKM length value. + /// + /// * `endianness` - The endianness of the DKM length value's bit representation. + /// + /// * `width_in_bits` - The number of bits used to represent the DKM length value. + pub fn new( + dkm_length_method: KbkdfDkmLengthMethod, + endianness: Endianness, + width_in_bits: NonZeroUsize, + ) -> Self { + Self(CK_SP800_108_DKM_LENGTH_FORMAT { + dkmLengthMethod: match dkm_length_method { + KbkdfDkmLengthMethod::SumOfKeys => CK_SP800_108_DKM_LENGTH_SUM_OF_KEYS, + KbkdfDkmLengthMethod::SumOfSegments => CK_SP800_108_DKM_LENGTH_SUM_OF_SEGMENTS, + }, + bLittleEndian: (endianness == Endianness::Little).into(), + ulWidthInBits: width_in_bits + .get() + .try_into() + .expect("bit width of KBKDF DKM length value does not fit in CK_ULONG"), + }) + } +} + +/// The type of a segment of input data for the PRF, for a KBKDF operating in feedback- or double pipeline-mode. +#[derive(Debug, Clone, Copy)] +pub enum PrfDataParamType<'a> { + /// Identifies location of predefined iteration variable in constructed PRF input data. + /// + /// For counter-mode, this must contain a [`KbkdfCounterFormat`]. + /// For feedback- and double-pipeline mode, this must contain [`None`]. + IterationVariable(Option<&'a KbkdfCounterFormat>), + /// Identifies location of counter in constructed PRF input data. + Counter(&'a KbkdfCounterFormat), + /// Identifies location of DKM (derived key material) length value in constructed PRF input data. + DkmLength(&'a KbkdfDkmLengthFormat), + /// Identifies location and value of byte array of data in constructed PRF input data. + ByteArray(&'a [u8]), +} + +/// A segment of input data for the PRF, to be used to construct a sequence of input. +/// +/// This structure wraps a `CK_PRF_DATA_PARAM` structure. +/// +/// * [`PrfDataParamType::IterationVariable`] is required for the KDF in all modes. +/// * In counter-mode, [`PrfDataParamType::IterationVariable`] must contain [`KbkdfCounterFormat`]. +/// In feedback- and double pipeline-mode, it must contain [`None`]. +/// * [`PrfDataParamType::Counter`] must not be present in counter-mode, and can be present at most +/// once in feedback- and double-pipeline modes. +/// * [`PrfDataParamType::DkmLength`] can be present at most once, in any mode. +/// * [`PrfDataParamType::ByteArray`] can be present any amount of times, in any mode. +#[derive(Debug, Clone, Copy)] +#[repr(transparent)] +pub struct PrfDataParam<'a> { + inner: CK_PRF_DATA_PARAM, + /// Marker type to ensure we don't outlive the data + _marker: PhantomData<&'a [u8]>, +} + +impl<'a> PrfDataParam<'a> { + /// Construct data parameter for input of the PRF internal to the KBKDF. + /// + /// # Arguments + /// + /// * `type_` - The specific type and parameters for the data parameter. + pub fn new(type_: PrfDataParamType<'a>) -> Self { + Self { + inner: match type_ { + PrfDataParamType::IterationVariable(None) => CK_PRF_DATA_PARAM { + type_: CK_SP800_108_ITERATION_VARIABLE, + pValue: ptr::null_mut(), + ulValueLen: 0, + }, + PrfDataParamType::IterationVariable(Some(counter_format)) => CK_PRF_DATA_PARAM { + type_: CK_SP800_108_ITERATION_VARIABLE, + pValue: counter_format as *const _ as *mut _, + ulValueLen: size_of::() as CK_ULONG, + }, + PrfDataParamType::Counter(counter_format) => CK_PRF_DATA_PARAM { + type_: CK_SP800_108_COUNTER, + pValue: counter_format as *const _ as *mut _, + ulValueLen: size_of::() as CK_ULONG, + }, + PrfDataParamType::DkmLength(dkm_length_format) => CK_PRF_DATA_PARAM { + type_: CK_SP800_108_DKM_LENGTH, + pValue: dkm_length_format as *const _ as *mut _, + ulValueLen: size_of::() as CK_ULONG, + }, + PrfDataParamType::ByteArray(data) => CK_PRF_DATA_PARAM { + type_: CK_SP800_108_BYTE_ARRAY, + pValue: data.as_ptr() as *mut _, + ulValueLen: data + .len() + .try_into() + .expect("length of PRF data parameter does not fit in CK_ULONG"), + }, + }, + _marker: PhantomData, + } + } +} + +/// Container for information on an additional key to be derived. +#[derive(Debug)] +pub struct DerivedKey { + /// Holds own data so that we have a contiguous memory region for backend to reference. + /// Because of this, the address of this allocation must remain stable during its lifetime. + template: Box<[CK_ATTRIBUTE]>, + handle: CK_OBJECT_HANDLE, +} + +impl DerivedKey { + /// Construct template for additional key to be derived by KDF. + /// + /// # Arguments + /// + /// * `template` - The template for the key to be derived. + pub fn new(template: &[Attribute]) -> Self { + let template: Box<[CK_ATTRIBUTE]> = template.iter().map(Into::into).collect(); + + Self { + template, + handle: CK_INVALID_HANDLE, + } + } + + /// Return handle for derived key, if it has been created yet + pub fn handle(&self) -> Option { + if self.handle == CK_INVALID_HANDLE { + None + } else { + Some(ObjectHandle::new(self.handle)) + } + } +} + +impl From<&mut DerivedKey> for CK_DERIVED_KEY { + fn from(value: &mut DerivedKey) -> Self { + CK_DERIVED_KEY { + pTemplate: value.template.as_ptr() as CK_ATTRIBUTE_PTR, + ulAttributeCount: value + .template + .len() + .try_into() + .expect("number of attributes in template does not fit in CK_ULONG"), + phKey: &mut value.handle as CK_OBJECT_HANDLE_PTR, + } + } +} + +/// NIST SP 800-108 (aka KBKDF) counter and double pipeline-mode parameters. +/// +/// This structure wraps a `CK_SP800_108_KDF_PARAMS` structure. +#[derive(Debug)] +pub struct KbkdfParams<'a> { + /// Holds own data so that we have a contiguous memory region for backend to reference. + /// Because of this, the address of this allocation must remain stable during its lifetime. + _additional_derived_keys: Option>, + + inner: CK_SP800_108_KDF_PARAMS, + /// Marker type to ensure we don't outlive the data + _marker: PhantomData<&'a mut [u8]>, +} + +impl<'a> KbkdfParams<'a> { + /// Construct parameters for NIST SP 800-108 KDF (aka KBKDF) pseudorandom function-based key + /// derivation function, in counter or double pipeline-mode. + /// + /// # Arguments + /// + /// * `prf_mechanism` - The pseudorandom function that underlies the KBKDF operation. + /// + /// * `prf_data_params` - The sequence of data segments used as input data for the PRF. Requires at least [`PrfDataParamType::IterationVariable`]. + /// + /// * `additional_derived_keys` - Any additional keys to be generated by the KDF from the base key. + pub fn new( + prf_mechanism: MechanismType, + prf_data_params: &'a [PrfDataParam<'a>], + additional_derived_keys: Option<&'a mut [DerivedKey]>, + ) -> Self { + let mut additional_derived_keys = additional_derived_keys.map(|keys| { + keys.iter_mut() + .map(Into::into) + .collect::>() + }); + + let inner = CK_SP800_108_KDF_PARAMS { + prfType: prf_mechanism.into(), + ulNumberOfDataParams: prf_data_params + .len() + .try_into() + .expect("number of PRF data parameters does not fit in CK_ULONG"), + pDataParams: prf_data_params.as_ptr() as CK_PRF_DATA_PARAM_PTR, + ulAdditionalDerivedKeys: additional_derived_keys.as_ref().map_or(0, |keys| { + keys.len() + .try_into() + .expect("number of additional derived keys does not fit in CK_ULONG") + }), + pAdditionalDerivedKeys: additional_derived_keys + .as_mut() + .map_or(ptr::null_mut(), |keys| { + keys.as_mut_ptr() as CK_DERIVED_KEY_PTR + }), + }; + + Self { + _additional_derived_keys: additional_derived_keys, + + inner, + _marker: PhantomData, + } + } + + pub(crate) fn inner(&self) -> &CK_SP800_108_KDF_PARAMS { + &self.inner + } +} + +/// NIST SP 800-108 (aka KBKDF) feedback-mode parameters. +/// +/// This structure wraps a `CK_SP800_108_FEEDBACK_KDF_PARAMS` structure. +#[derive(Debug)] +pub struct KbkdfFeedbackParams<'a> { + /// Holds own data so that we have a contiguous memory region for backend to reference. + /// Because of this, the address of this allocation must remain stable during its lifetime. + _additional_derived_keys: Option>, + + inner: CK_SP800_108_FEEDBACK_KDF_PARAMS, + /// Marker type to ensure we don't outlive the data + _marker: PhantomData<&'a mut [u8]>, +} + +impl<'a> KbkdfFeedbackParams<'a> { + /// Construct parameters for NIST SP 800-108 KDF (aka KBKDF) pseuderandom function-based key + /// derivation function, in feedback-mode. + /// + /// # Arguments + /// + /// * `prf_mechanism` - The pseudorandom function that underlies the KBKDF operation. + /// + /// * `prf_data_params` - The sequence of data segments used as input data for the PRF. Requires at least [`PrfDataParamType::IterationVariable`]. + /// + /// * `iv` - The IV to be used for the feedback-mode KDF. + /// + /// * `additional_derived_keys` - Any additional keys to be generated by the KDF from the base key. + pub fn new( + prf_mechanism: MechanismType, + prf_data_params: &'a [PrfDataParam<'a>], + iv: Option<&'a [u8]>, + additional_derived_keys: Option<&'a mut [DerivedKey]>, + ) -> Self { + let mut additional_derived_keys = additional_derived_keys.map(|keys| { + keys.iter_mut() + .map(Into::into) + .collect::>() + }); + + let inner = CK_SP800_108_FEEDBACK_KDF_PARAMS { + prfType: prf_mechanism.into(), + ulNumberOfDataParams: prf_data_params + .len() + .try_into() + .expect("number of PRF data parameters does not fit in CK_ULONG"), + pDataParams: prf_data_params.as_ptr() as CK_PRF_DATA_PARAM_PTR, + ulIVLen: iv.map_or(0, |iv| { + iv.len() + .try_into() + .expect("IV length does not fit in CK_ULONG") + }), + pIV: iv.map_or(ptr::null_mut(), |iv| iv.as_ptr() as *mut _), + ulAdditionalDerivedKeys: additional_derived_keys.as_ref().map_or(0, |keys| { + keys.len() + .try_into() + .expect("number of additional derived keys does not fit in CK_ULONG") + }), + pAdditionalDerivedKeys: additional_derived_keys + .as_mut() + .map_or(ptr::null_mut(), |keys| { + keys.as_mut_ptr() as CK_DERIVED_KEY_PTR + }), + }; + + Self { + _additional_derived_keys: additional_derived_keys, + + inner, + _marker: PhantomData, + } + } + + pub(crate) fn inner(&self) -> &CK_SP800_108_FEEDBACK_KDF_PARAMS { + &self.inner + } +} diff --git a/cryptoki/src/mechanism/mod.rs b/cryptoki/src/mechanism/mod.rs index b5ca489d..e5086dca 100644 --- a/cryptoki/src/mechanism/mod.rs +++ b/cryptoki/src/mechanism/mod.rs @@ -7,11 +7,11 @@ pub mod eddsa; pub mod ekdf; pub mod elliptic_curve; pub mod hkdf; +pub mod kbkdf; mod mechanism_info; pub mod rsa; pub mod vendor_defined; -use crate::error::Error; use cryptoki_sys::*; use log::error; use std::convert::{TryFrom, TryInto}; @@ -22,6 +22,7 @@ use std::ops::Deref; use std::ptr::null_mut; use vendor_defined::VendorDefinedMechanism; +use crate::error::Error; use crate::mechanism::rsa::PkcsOaepParams; pub use mechanism_info::MechanismInfo; @@ -326,6 +327,20 @@ impl MechanismType { /// HKDF-DATA mechanism pub const HKDF_DATA: MechanismType = MechanismType { val: CKM_HKDF_DATA }; + // NIST SP 800-108 KDF (aka KBKDF) + /// NIST SP 800-108 KDF (aka KBKDF) mechanism in counter-mode + pub const SP800_108_COUNTER_KDF: MechanismType = MechanismType { + val: CKM_SP800_108_COUNTER_KDF, + }; + /// NIST SP 800-108 KDF (aka KBKDF) mechanism in feedback-mode + pub const SP800_108_FEEDBACK_KDF: MechanismType = MechanismType { + val: CKM_SP800_108_FEEDBACK_KDF, + }; + /// NIST SP 800-108 KDF (aka KBKDF) mechanism in double pipeline-mode + pub const SP800_108_DOUBLE_PIPELINE_KDF: MechanismType = MechanismType { + val: CKM_SP800_108_DOUBLE_PIPELINE_KDF, + }; + /// Create vendor defined mechanism /// /// # Arguments @@ -715,6 +730,11 @@ impl MechanismType { 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)), + CKM_SP800_108_COUNTER_KDF => String::from(stringify!(CKM_SP800_108_COUNTER_KDF)), + CKM_SP800_108_FEEDBACK_KDF => String::from(stringify!(CKM_SP800_108_FEEDBACK_KDF)), + CKM_SP800_108_DOUBLE_PIPELINE_KDF => { + String::from(stringify!(CKM_SP800_108_DOUBLE_PIPELINE_KDF)) + } _ => format!("unknown {mech:08x}"), } } @@ -799,6 +819,9 @@ impl TryFrom for MechanismType { CKM_HKDF_KEY_GEN => Ok(MechanismType::HKDF_KEY_GEN), CKM_HKDF_DERIVE => Ok(MechanismType::HKDF_DERIVE), CKM_HKDF_DATA => Ok(MechanismType::HKDF_DATA), + CKM_SP800_108_COUNTER_KDF => Ok(MechanismType::SP800_108_COUNTER_KDF), + CKM_SP800_108_FEEDBACK_KDF => Ok(MechanismType::SP800_108_FEEDBACK_KDF), + CKM_SP800_108_DOUBLE_PIPELINE_KDF => Ok(MechanismType::SP800_108_DOUBLE_PIPELINE_KDF), other => { error!("Mechanism type {} is not supported.", other); Err(Error::NotSupported) @@ -1021,6 +1044,14 @@ pub enum Mechanism<'a> { /// HKDF-DATA mechanism HkdfData(hkdf::HkdfParams<'a>), + // NIST SP 800-108 KDF (aka KBKDF) + /// NIST SP 800-108 KDF (aka KBKDF) mechanism in counter-mode + KbkdfCounter(kbkdf::KbkdfParams<'a>), + /// NIST SP 800-108 KDF (aka KBKDF) mechanism in feedback-mode + KbkdfFeedback(kbkdf::KbkdfFeedbackParams<'a>), + /// NIST SP 800-108 KDF (aka KBKDF) mechanism in double pipeline-mode + KbkdfDoublePipeline(kbkdf::KbkdfParams<'a>), + /// Vendor defined mechanism VendorDefined(VendorDefinedMechanism<'a>), } @@ -1102,6 +1133,10 @@ impl Mechanism<'_> { Mechanism::HkdfDerive(_) => MechanismType::HKDF_DERIVE, Mechanism::HkdfData(_) => MechanismType::HKDF_DATA, + Mechanism::KbkdfCounter(_) => MechanismType::SP800_108_COUNTER_KDF, + Mechanism::KbkdfFeedback(_) => MechanismType::SP800_108_FEEDBACK_KDF, + Mechanism::KbkdfDoublePipeline(_) => MechanismType::SP800_108_DOUBLE_PIPELINE_KDF, + Mechanism::VendorDefined(vm) => MechanismType { val: vm.inner.mechanism, }, @@ -1154,6 +1189,10 @@ impl From<&Mechanism<'_>> for CK_MECHANISM { Mechanism::HkdfDerive(params) | Mechanism::HkdfData(params) => { make_mechanism(mechanism, params) } + Mechanism::KbkdfCounter(params) | Mechanism::KbkdfDoublePipeline(params) => { + make_mechanism(mechanism, params.inner()) + } + Mechanism::KbkdfFeedback(params) => make_mechanism(mechanism, params.inner()), // Mechanisms without parameters Mechanism::AesKeyGen | Mechanism::AesEcb @@ -1213,9 +1252,18 @@ impl From<&Mechanism<'_>> for CK_MECHANISM { fn make_mechanism(mechanism: CK_MECHANISM_TYPE, param: &T) -> CK_MECHANISM { CK_MECHANISM { mechanism, - // SAFETY: Although the type signature says *mut, none of the - // mechanisms we support involve mutating the parameter, so - // this cast is OK. + /* + * SAFETY: Parameters that expect to have some part of themselves + * mutated should indicate this to the end user by marking both the + * relevant constructor parameters and the type's PhantomData as mut. + * Otherwise, we should generally not expect the backend to mutate the + * parameters, so this cast is fine. + * The list of such mutable parameter types so far: + * - aead::GcmParams + * - aead::GcmMessageParams + * - kbkdf::KbkdfParams + * - kbkdf::KbkdfFeedbackParams + */ pParameter: param as *const T as *mut c_void, ulParameterLen: size_of::() .try_into() diff --git a/cryptoki/tests/basic.rs b/cryptoki/tests/basic.rs index e3f418ec..b243a892 100644 --- a/cryptoki/tests/basic.rs +++ b/cryptoki/tests/basic.rs @@ -8,6 +8,10 @@ use cryptoki::context::Function; use cryptoki::error::{Error, RvError}; use cryptoki::mechanism::aead::{GcmMessageParams, GcmParams, GeneratorFunction}; use cryptoki::mechanism::eddsa::{EddsaParams, EddsaSignatureScheme}; +use cryptoki::mechanism::kbkdf::{ + DerivedKey, Endianness, KbkdfCounterFormat, KbkdfDkmLengthFormat, KbkdfDkmLengthMethod, + KbkdfFeedbackParams, KbkdfParams, PrfDataParam, PrfDataParamType, +}; use cryptoki::mechanism::rsa::{PkcsMgfType, PkcsOaepParams, PkcsOaepSource}; use cryptoki::mechanism::{Mechanism, MechanismType, MessageParam}; use cryptoki::object::{ @@ -24,6 +28,7 @@ use cryptoki::mechanism::ekdf::AesCbcDeriveParams; use testresult::TestResult; const AES128_BLOCK_SIZE: usize = 128 / 8; +const AES256_BLOCK_SIZE: usize = 256 / 8; #[test] #[serial] @@ -2170,6 +2175,1193 @@ fn ekdf_aes_cbc_encrypt_data() -> TestResult { Ok(()) } +#[test] +#[serial] +fn kbkdf_counter_mode() -> TestResult { + /* SoftHSM does not support NIST SP800-108 KDF yet */ + if is_softhsm() { + return Ok(()); + } + + let (pkcs11, slot) = init_pins(); + + // Open a session and log in + let session = pkcs11.open_rw_session(slot)?; + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + // Generate base key + let base_template = [ + Attribute::Token(true), + Attribute::Private(false), + Attribute::ValueLen((AES256_BLOCK_SIZE as u64).into()), + Attribute::Derive(true), + ]; + let base_key = session.generate_key(&Mechanism::AesKeyGen, &base_template)?; + + // The template of the key to derive + let derived_key_template = [ + Attribute::Token(false), + Attribute::Private(false), + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::AES), + Attribute::ValueLen((AES256_BLOCK_SIZE as u64).into()), + Attribute::Encrypt(true), + Attribute::Decrypt(true), + ]; + + // Some variables we will use throughout + let counter_format = KbkdfCounterFormat::new(Endianness::Big, NonZeroUsize::new(16).unwrap()); + let dkm_length_format = KbkdfDkmLengthFormat::new( + KbkdfDkmLengthMethod::SumOfKeys, + Endianness::Big, + NonZeroUsize::new(16).unwrap(), + ); + + // Instantiate KBKDF in counter-mode without additional keys + let data_params = [ + PrfDataParam::new(PrfDataParamType::IterationVariable(Some(&counter_format))), + PrfDataParam::new(PrfDataParamType::ByteArray(b"test_label")), + PrfDataParam::new(PrfDataParamType::ByteArray(&[0])), + PrfDataParam::new(PrfDataParamType::ByteArray(b"\xfe\xed\xbe\xef")), + PrfDataParam::new(PrfDataParamType::DkmLength(&dkm_length_format)), + ]; + let params = KbkdfParams::new(MechanismType::AES_CMAC, &data_params, None); + + // Derive key + let derived_key = session.derive_key( + &Mechanism::KbkdfCounter(params), + base_key, + &derived_key_template, + )?; + + // Verify derive key matches template + let attributes_to_check = [ + AttributeType::Class, + AttributeType::KeyType, + AttributeType::ValueLen, + AttributeType::Encrypt, + AttributeType::Decrypt, + AttributeType::Sign, + AttributeType::Verify, + AttributeType::Derive, + ]; + let wanted_attributes = [ + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::AES), + Attribute::ValueLen((AES256_BLOCK_SIZE as u64).into()), + Attribute::Encrypt(true), + Attribute::Decrypt(true), + Attribute::Sign(false), + Attribute::Verify(false), + Attribute::Derive(false), + ]; + let have_attributes = session.get_attributes(derived_key, &attributes_to_check)?; + + for (value_wanted, value_have) in wanted_attributes.iter().zip(have_attributes.iter()) { + assert_eq!(value_wanted, value_have); + } + + // Delete keys + session.destroy_object(derived_key)?; + session.destroy_object(base_key)?; + + Ok(()) +} + +#[test] +#[serial] +fn kbkdf_feedback_mode() -> TestResult { + /* SoftHSM does not support NIST SP800-108 KDF yet */ + if is_softhsm() { + return Ok(()); + } + + let (pkcs11, slot) = init_pins(); + + // Open a session and log in + let session = pkcs11.open_rw_session(slot)?; + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + // Generate base key + let base_template = [ + Attribute::Token(true), + Attribute::Private(false), + Attribute::ValueLen((AES256_BLOCK_SIZE as u64).into()), + Attribute::Derive(true), + ]; + let base_key = session.generate_key(&Mechanism::AesKeyGen, &base_template)?; + + // The template of the key to derive + let derived_key_template = [ + Attribute::Token(false), + Attribute::Private(false), + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::AES), + Attribute::ValueLen((AES256_BLOCK_SIZE as u64).into()), + Attribute::Encrypt(true), + Attribute::Decrypt(true), + ]; + + // Some variables we will use throughout + let counter_format = KbkdfCounterFormat::new(Endianness::Big, NonZeroUsize::new(16).unwrap()); + let dkm_length_format = KbkdfDkmLengthFormat::new( + KbkdfDkmLengthMethod::SumOfKeys, + Endianness::Big, + NonZeroUsize::new(16).unwrap(), + ); + + /* FEEDBACK-MODE - no IV */ + + // Instantiate KBKDF in feedback-mode without additional keys or an IV + let data_params = [ + PrfDataParam::new(PrfDataParamType::IterationVariable(None)), + PrfDataParam::new(PrfDataParamType::Counter(&counter_format)), + PrfDataParam::new(PrfDataParamType::DkmLength(&dkm_length_format)), + ]; + let params = KbkdfFeedbackParams::new(MechanismType::AES_CMAC, &data_params, None, None); + + // Derive key + let derived_key_feedback_no_iv = session.derive_key( + &Mechanism::KbkdfFeedback(params), + base_key, + &derived_key_template, + )?; + + /* FEEDBACK-MODE - with IV */ + + // Instantiate KBKDF in feedback-mode without additional keys but with an IV + let data_params = [ + PrfDataParam::new(PrfDataParamType::IterationVariable(None)), + PrfDataParam::new(PrfDataParamType::Counter(&counter_format)), + ]; + let params = KbkdfFeedbackParams::new( + MechanismType::AES_CMAC, + &data_params, + Some(b"some_initialization_vector"), + None, + ); + + // Derive key + let derived_key_feedback_iv = session.derive_key( + &Mechanism::KbkdfFeedback(params), + base_key, + &derived_key_template, + )?; + + // Verify derived keys match template + let derived_keys = [derived_key_feedback_no_iv, derived_key_feedback_iv]; + + let attributes_to_check = [ + AttributeType::Class, + AttributeType::KeyType, + AttributeType::ValueLen, + AttributeType::Encrypt, + AttributeType::Decrypt, + AttributeType::Sign, + AttributeType::Verify, + AttributeType::Derive, + ]; + let wanted_attributes = [ + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::AES), + Attribute::ValueLen((AES256_BLOCK_SIZE as u64).into()), + Attribute::Encrypt(true), + Attribute::Decrypt(true), + Attribute::Sign(false), + Attribute::Verify(false), + Attribute::Derive(false), + ]; + + for key in derived_keys { + let have_attributes = session.get_attributes(key, &attributes_to_check)?; + + for (value_wanted, value_have) in wanted_attributes.iter().zip(have_attributes.iter()) { + assert_eq!(value_wanted, value_have); + } + } + + // Delete all keys + for key in derived_keys { + session.destroy_object(key)?; + } + session.destroy_object(base_key)?; + + Ok(()) +} + +#[test] +#[serial] +#[ignore = "unsupported by both SoftHSM and Kryoptic for the moment"] +fn kbkdf_double_pipeline_mode() -> TestResult { + let (pkcs11, slot) = init_pins(); + + // Open a session and log in + let session = pkcs11.open_rw_session(slot)?; + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + // Generate base key + let base_template = [ + Attribute::Token(true), + Attribute::Private(false), + Attribute::ValueLen((AES256_BLOCK_SIZE as u64).into()), + Attribute::Derive(true), + ]; + let base_key = session.generate_key(&Mechanism::AesKeyGen, &base_template)?; + + // The template of the key to derive + let derived_key_template = [ + Attribute::Token(false), + Attribute::Private(false), + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::AES), + Attribute::ValueLen((AES256_BLOCK_SIZE as u64).into()), + Attribute::Encrypt(true), + Attribute::Decrypt(true), + ]; + + // Some variables we will use throughout + let dkm_length_format = KbkdfDkmLengthFormat::new( + KbkdfDkmLengthMethod::SumOfKeys, + Endianness::Big, + NonZeroUsize::new(16).unwrap(), + ); + + // Instantiate KBKDF in feedback-mode without additional keys + let data_params = [ + PrfDataParam::new(PrfDataParamType::IterationVariable(None)), + PrfDataParam::new(PrfDataParamType::DkmLength(&dkm_length_format)), + ]; + let params = KbkdfParams::new(MechanismType::AES_CMAC, &data_params, None); + + // Derive key + let derived_key = session.derive_key( + &Mechanism::KbkdfDoublePipeline(params), + base_key, + &derived_key_template, + )?; + + // Verify derive key matches template + let attributes_to_check = [ + AttributeType::Class, + AttributeType::KeyType, + AttributeType::ValueLen, + AttributeType::Encrypt, + AttributeType::Decrypt, + AttributeType::Sign, + AttributeType::Verify, + AttributeType::Derive, + ]; + let wanted_attributes = [ + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::AES), + Attribute::ValueLen((AES256_BLOCK_SIZE as u64).into()), + Attribute::Encrypt(true), + Attribute::Decrypt(true), + Attribute::Sign(false), + Attribute::Verify(false), + Attribute::Derive(false), + ]; + let have_attributes = session.get_attributes(derived_key, &attributes_to_check)?; + + for (value_wanted, value_have) in wanted_attributes.iter().zip(have_attributes.iter()) { + assert_eq!(value_wanted, value_have); + } + + // Delete keys + session.destroy_object(derived_key)?; + session.destroy_object(base_key)?; + + Ok(()) +} + +#[test] +#[serial] +fn kbkdf_additional_keys_counter_mode() -> TestResult { + /* SoftHSM does not support NIST SP800-108 KDF yet */ + if is_softhsm() { + return Ok(()); + } + + let (pkcs11, slot) = init_pins(); + + // Open a session and log in + let session = pkcs11.open_rw_session(slot)?; + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + // Generate base key + let base_template = [ + Attribute::Token(true), + Attribute::Private(false), + Attribute::ValueLen((AES256_BLOCK_SIZE as u64).into()), + Attribute::Derive(true), + ]; + let base_key = session.generate_key(&Mechanism::AesKeyGen, &base_template)?; + + // The templates of the keys to derive. We supply more than one to test deriving additional keys + let derived_key_templates = [ + vec![ + Attribute::Token(false), + Attribute::Private(false), + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::AES), + Attribute::ValueLen((AES256_BLOCK_SIZE as u64).into()), + Attribute::Encrypt(true), + Attribute::Decrypt(true), + ], + vec![ + Attribute::Token(true), + Attribute::Private(false), + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::AES), + Attribute::ValueLen((AES128_BLOCK_SIZE as u64).into()), + Attribute::Sign(true), + Attribute::Verify(true), + ], + vec![ + Attribute::Token(true), + Attribute::Private(false), + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::GENERIC_SECRET), + Attribute::ValueLen(1.into()), + Attribute::Derive(true), + ], + ]; + + // Some variables we will use throughout + let counter_format = KbkdfCounterFormat::new(Endianness::Big, NonZeroUsize::new(16).unwrap()); + let dkm_length_format = KbkdfDkmLengthFormat::new( + KbkdfDkmLengthMethod::SumOfKeys, + Endianness::Big, + NonZeroUsize::new(16).unwrap(), + ); + + // Instantiate KBKDF in counter-mode without additional keys + let data_params = [ + PrfDataParam::new(PrfDataParamType::IterationVariable(Some(&counter_format))), + PrfDataParam::new(PrfDataParamType::ByteArray(b"test_label")), + PrfDataParam::new(PrfDataParamType::ByteArray(&[0])), + PrfDataParam::new(PrfDataParamType::ByteArray(b"\xfe\xed\xbe\xef")), + PrfDataParam::new(PrfDataParamType::DkmLength(&dkm_length_format)), + ]; + let mut additional_derived_keys = derived_key_templates[1..] + .iter() + .map(|template| DerivedKey::new(template)) + .collect::>(); + let params = KbkdfParams::new( + MechanismType::AES_CMAC, + &data_params, + Some(&mut additional_derived_keys), + ); + + let mut derived_keys = vec![]; + + // Derive all keys + let main_derived_key = session.derive_key( + &Mechanism::KbkdfCounter(params), + base_key, + &derived_key_templates[0], + )?; + let additional_derived_keys = additional_derived_keys + .iter() + .filter_map(|key| key.handle()) + .collect::>(); + + derived_keys.push(main_derived_key); + derived_keys.extend(additional_derived_keys); + + // Verify all derived keys match template + let attributes_to_check = [ + AttributeType::Class, + AttributeType::KeyType, + AttributeType::ValueLen, + AttributeType::Encrypt, + AttributeType::Decrypt, + AttributeType::Sign, + AttributeType::Verify, + AttributeType::Derive, + ]; + let wanted_attributes = [ + vec![ + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::AES), + Attribute::ValueLen((AES256_BLOCK_SIZE as u64).into()), + Attribute::Encrypt(true), + Attribute::Decrypt(true), + Attribute::Sign(false), + Attribute::Verify(false), + Attribute::Derive(false), + ], + vec![ + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::AES), + Attribute::ValueLen((AES128_BLOCK_SIZE as u64).into()), + Attribute::Encrypt(false), + Attribute::Decrypt(false), + Attribute::Sign(true), + Attribute::Verify(true), + Attribute::Derive(false), + ], + vec![ + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::GENERIC_SECRET), + Attribute::ValueLen(1.into()), + Attribute::Encrypt(false), + Attribute::Decrypt(false), + Attribute::Sign(false), + Attribute::Verify(false), + Attribute::Derive(true), + ], + ]; + + for (key, wanted_attributes) in derived_keys.iter().zip(wanted_attributes.iter().cycle()) { + let have_attributes = session.get_attributes(*key, &attributes_to_check)?; + + for (value_wanted, value_have) in wanted_attributes.iter().zip(have_attributes.iter()) { + assert_eq!(value_wanted, value_have); + } + } + + // Delete all keys + for key in derived_keys { + session.destroy_object(key)?; + } + session.destroy_object(base_key)?; + + Ok(()) +} + +#[test] +#[serial] +fn kbkdf_additional_keys_feedback_mode() -> TestResult { + /* SoftHSM does not support NIST SP800-108 KDF yet */ + if is_softhsm() { + return Ok(()); + } + + let (pkcs11, slot) = init_pins(); + + // Open a session and log in + let session = pkcs11.open_rw_session(slot)?; + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + // Generate base key + let base_template = [ + Attribute::Token(true), + Attribute::Private(false), + Attribute::ValueLen((AES256_BLOCK_SIZE as u64).into()), + Attribute::Derive(true), + ]; + let base_key = session.generate_key(&Mechanism::AesKeyGen, &base_template)?; + + // The templates of the keys to derive. We supply more than one to test deriving additional keys + let derived_key_templates = [ + vec![ + Attribute::Token(false), + Attribute::Private(false), + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::AES), + Attribute::ValueLen((AES256_BLOCK_SIZE as u64).into()), + Attribute::Encrypt(true), + Attribute::Decrypt(true), + ], + vec![ + Attribute::Token(true), + Attribute::Private(false), + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::AES), + Attribute::ValueLen((AES128_BLOCK_SIZE as u64).into()), + Attribute::Sign(true), + Attribute::Verify(true), + ], + vec![ + Attribute::Token(true), + Attribute::Private(false), + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::GENERIC_SECRET), + Attribute::ValueLen(1.into()), + Attribute::Derive(true), + ], + ]; + + // Some variables we will use throughout + let counter_format = KbkdfCounterFormat::new(Endianness::Big, NonZeroUsize::new(16).unwrap()); + let dkm_length_format = KbkdfDkmLengthFormat::new( + KbkdfDkmLengthMethod::SumOfKeys, + Endianness::Big, + NonZeroUsize::new(16).unwrap(), + ); + + let mut derived_keys = vec![]; + + /* FEEDBACK-MODE - no IV */ + + // Instantiate KBKDF in feedback-mode without additional keys or an IV + let data_params = [ + PrfDataParam::new(PrfDataParamType::IterationVariable(None)), + PrfDataParam::new(PrfDataParamType::Counter(&counter_format)), + PrfDataParam::new(PrfDataParamType::DkmLength(&dkm_length_format)), + ]; + let mut additional_derived_keys = derived_key_templates[1..] + .iter() + .map(|template| DerivedKey::new(template)) + .collect::>(); + let params = KbkdfFeedbackParams::new( + MechanismType::AES_CMAC, + &data_params, + None, + Some(&mut additional_derived_keys), + ); + + // Derive all keys + let main_derived_key = session.derive_key( + &Mechanism::KbkdfFeedback(params), + base_key, + &derived_key_templates[0], + )?; + let additional_derived_keys = additional_derived_keys + .iter() + .filter_map(|key| key.handle()) + .collect::>(); + + derived_keys.push(main_derived_key); + derived_keys.extend(additional_derived_keys); + + /* FEEDBACK-MODE - with IV */ + + // Instantiate KBKDF in feedback-mode without additional keys but with an IV + let data_params = [ + PrfDataParam::new(PrfDataParamType::IterationVariable(None)), + PrfDataParam::new(PrfDataParamType::Counter(&counter_format)), + ]; + let mut additional_derived_keys = derived_key_templates[1..] + .iter() + .map(|template| DerivedKey::new(template)) + .collect::>(); + let params = KbkdfFeedbackParams::new( + MechanismType::AES_CMAC, + &data_params, + Some(b"some_initialization_vector"), + Some(&mut additional_derived_keys), + ); + + // Derive all keys + let main_derived_key = session.derive_key( + &Mechanism::KbkdfFeedback(params), + base_key, + &derived_key_templates[0], + )?; + let additional_derived_keys = additional_derived_keys + .iter() + .filter_map(|key| key.handle()) + .collect::>(); + + derived_keys.push(main_derived_key); + derived_keys.extend(additional_derived_keys); + + // Verify all derived keys match template + let attributes_to_check = [ + AttributeType::Class, + AttributeType::KeyType, + AttributeType::ValueLen, + AttributeType::Encrypt, + AttributeType::Decrypt, + AttributeType::Sign, + AttributeType::Verify, + AttributeType::Derive, + ]; + let wanted_attributes = [ + vec![ + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::AES), + Attribute::ValueLen((AES256_BLOCK_SIZE as u64).into()), + Attribute::Encrypt(true), + Attribute::Decrypt(true), + Attribute::Sign(false), + Attribute::Verify(false), + Attribute::Derive(false), + ], + vec![ + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::AES), + Attribute::ValueLen((AES128_BLOCK_SIZE as u64).into()), + Attribute::Encrypt(false), + Attribute::Decrypt(false), + Attribute::Sign(true), + Attribute::Verify(true), + Attribute::Derive(false), + ], + vec![ + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::GENERIC_SECRET), + Attribute::ValueLen(1.into()), + Attribute::Encrypt(false), + Attribute::Decrypt(false), + Attribute::Sign(false), + Attribute::Verify(false), + Attribute::Derive(true), + ], + ]; + + for (key, wanted_attributes) in derived_keys.iter().zip(wanted_attributes.iter().cycle()) { + let have_attributes = session.get_attributes(*key, &attributes_to_check)?; + + for (value_wanted, value_have) in wanted_attributes.iter().zip(have_attributes.iter()) { + assert_eq!(value_wanted, value_have); + } + } + + // Delete all keys + for key in derived_keys { + session.destroy_object(key)?; + } + session.destroy_object(base_key)?; + + Ok(()) +} + +#[test] +#[serial] +#[ignore = "unsupported by both SoftHSM and Kryoptic for the moment"] +fn kbkdf_additional_keys_double_pipeline_mode() -> TestResult { + let (pkcs11, slot) = init_pins(); + + // Open a session and log in + let session = pkcs11.open_rw_session(slot)?; + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + // Generate base key + let base_template = [ + Attribute::Token(true), + Attribute::Private(false), + Attribute::ValueLen((AES256_BLOCK_SIZE as u64).into()), + Attribute::Derive(true), + ]; + let base_key = session.generate_key(&Mechanism::AesKeyGen, &base_template)?; + + // The templates of the keys to derive. We supply more than one to test deriving additional keys + let derived_key_templates = [ + vec![ + Attribute::Token(false), + Attribute::Private(false), + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::AES), + Attribute::ValueLen((AES256_BLOCK_SIZE as u64).into()), + Attribute::Encrypt(true), + Attribute::Decrypt(true), + ], + vec![ + Attribute::Token(true), + Attribute::Private(false), + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::AES), + Attribute::ValueLen((AES128_BLOCK_SIZE as u64).into()), + Attribute::Sign(true), + Attribute::Verify(true), + ], + vec![ + Attribute::Token(true), + Attribute::Private(false), + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::GENERIC_SECRET), + Attribute::ValueLen(1.into()), + Attribute::Derive(true), + ], + ]; + + // Some variables we will use throughout + let dkm_length_format = KbkdfDkmLengthFormat::new( + KbkdfDkmLengthMethod::SumOfKeys, + Endianness::Big, + NonZeroUsize::new(16).unwrap(), + ); + + // Instantiate KBKDF in feedback-mode without additional keys + let data_params = [ + PrfDataParam::new(PrfDataParamType::IterationVariable(None)), + PrfDataParam::new(PrfDataParamType::DkmLength(&dkm_length_format)), + ]; + let mut additional_derived_keys = derived_key_templates[1..] + .iter() + .map(|template| DerivedKey::new(template)) + .collect::>(); + let params = KbkdfParams::new( + MechanismType::AES_CMAC, + &data_params, + Some(&mut additional_derived_keys), + ); + + let mut derived_keys = vec![]; + + // Derive all keys + let main_derived_key = session.derive_key( + &Mechanism::KbkdfDoublePipeline(params), + base_key, + &derived_key_templates[0], + )?; + let additional_derived_keys = additional_derived_keys + .iter() + .filter_map(|key| key.handle()) + .collect::>(); + + derived_keys.push(main_derived_key); + derived_keys.extend(additional_derived_keys); + + // Verify all derived keys match template + let attributes_to_check = [ + AttributeType::Class, + AttributeType::KeyType, + AttributeType::ValueLen, + AttributeType::Encrypt, + AttributeType::Decrypt, + AttributeType::Sign, + AttributeType::Verify, + AttributeType::Derive, + ]; + let wanted_attributes = [ + vec![ + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::AES), + Attribute::ValueLen((AES256_BLOCK_SIZE as u64).into()), + Attribute::Encrypt(true), + Attribute::Decrypt(true), + Attribute::Sign(false), + Attribute::Verify(false), + Attribute::Derive(false), + ], + vec![ + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::AES), + Attribute::ValueLen((AES128_BLOCK_SIZE as u64).into()), + Attribute::Encrypt(false), + Attribute::Decrypt(false), + Attribute::Sign(true), + Attribute::Verify(true), + Attribute::Derive(false), + ], + vec![ + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::GENERIC_SECRET), + Attribute::ValueLen(1.into()), + Attribute::Encrypt(false), + Attribute::Decrypt(false), + Attribute::Sign(false), + Attribute::Verify(false), + Attribute::Derive(true), + ], + ]; + + for (key, wanted_attributes) in derived_keys.iter().zip(wanted_attributes.iter().cycle()) { + let have_attributes = session.get_attributes(*key, &attributes_to_check)?; + + for (value_wanted, value_have) in wanted_attributes.iter().zip(have_attributes.iter()) { + assert_eq!(value_wanted, value_have); + } + } + + // Delete all keys + for key in derived_keys { + session.destroy_object(key)?; + } + session.destroy_object(base_key)?; + + Ok(()) +} + +#[test] +#[serial] +fn kbkdf_invalid_data_params_counter_mode() -> TestResult { + /* SoftHSM does not support NIST SP800-108 KDF yet */ + if is_softhsm() { + return Ok(()); + } + + let (pkcs11, slot) = init_pins(); + + // Open a session and log in + let session = pkcs11.open_rw_session(slot)?; + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + // Generate base key + let base_template = [ + Attribute::Token(true), + Attribute::Private(false), + Attribute::ValueLen((AES256_BLOCK_SIZE as u64).into()), + Attribute::Derive(true), + ]; + let base_key = session.generate_key(&Mechanism::AesKeyGen, &base_template)?; + + // The template of the key to derive + let derived_key_template = [ + Attribute::Token(false), + Attribute::Private(false), + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::AES), + Attribute::ValueLen((AES256_BLOCK_SIZE as u64).into()), + Attribute::Encrypt(true), + Attribute::Decrypt(true), + ]; + + // Some variables we will use throughout + let counter_format = KbkdfCounterFormat::new(Endianness::Big, NonZeroUsize::new(16).unwrap()); + let dkm_length_format = KbkdfDkmLengthFormat::new( + KbkdfDkmLengthMethod::SumOfKeys, + Endianness::Big, + NonZeroUsize::new(16).unwrap(), + ); + + /* MISSING ITERATION VARIABLE */ + + // Instantiate KBKDF + let data_params = [ + PrfDataParam::new(PrfDataParamType::ByteArray(b"test_label")), + PrfDataParam::new(PrfDataParamType::ByteArray(&[0])), + PrfDataParam::new(PrfDataParamType::ByteArray(b"\xfe\xed\xbe\xef")), + PrfDataParam::new(PrfDataParamType::DkmLength(&dkm_length_format)), + ]; + let params = KbkdfParams::new(MechanismType::AES_CMAC, &data_params, None); + + // Derive key + let result = session.derive_key( + &Mechanism::KbkdfCounter(params), + base_key, + &derived_key_template, + ); + + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + Error::Pkcs11(RvError::MechanismParamInvalid, Function::DeriveKey) + )); + + /* MISSING COUNTER FORMAT IN ITERATION VARIABLE */ + + // Instantiate KBKDF + let data_params = [ + PrfDataParam::new(PrfDataParamType::IterationVariable(None)), + PrfDataParam::new(PrfDataParamType::ByteArray(b"test_label")), + PrfDataParam::new(PrfDataParamType::ByteArray(&[0])), + PrfDataParam::new(PrfDataParamType::ByteArray(b"\xfe\xed\xbe\xef")), + PrfDataParam::new(PrfDataParamType::DkmLength(&dkm_length_format)), + ]; + let params = KbkdfParams::new(MechanismType::AES_CMAC, &data_params, None); + + // Derive key + let result = session.derive_key( + &Mechanism::KbkdfCounter(params), + base_key, + &derived_key_template, + ); + + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + Error::Pkcs11(RvError::MechanismParamInvalid, Function::DeriveKey) + )); + + /* INCLUDING COUNTER */ + + // Instantiate KBKDF + let data_params = [ + PrfDataParam::new(PrfDataParamType::IterationVariable(Some(&counter_format))), + PrfDataParam::new(PrfDataParamType::Counter(&counter_format)), + PrfDataParam::new(PrfDataParamType::ByteArray(b"test_label")), + PrfDataParam::new(PrfDataParamType::ByteArray(&[0])), + PrfDataParam::new(PrfDataParamType::ByteArray(b"\xfe\xed\xbe\xef")), + PrfDataParam::new(PrfDataParamType::DkmLength(&dkm_length_format)), + ]; + let params = KbkdfParams::new(MechanismType::AES_CMAC, &data_params, None); + + // Derive key + let result = session.derive_key( + &Mechanism::KbkdfCounter(params), + base_key, + &derived_key_template, + ); + + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + Error::Pkcs11(RvError::MechanismParamInvalid, Function::DeriveKey) + )); + + /* INCLUDING MORE THAN 1 DKM LENGTH FORMAT */ + + // Instantiate KBKDF + let data_params = [ + PrfDataParam::new(PrfDataParamType::IterationVariable(Some(&counter_format))), + PrfDataParam::new(PrfDataParamType::ByteArray(b"test_label")), + PrfDataParam::new(PrfDataParamType::ByteArray(&[0])), + PrfDataParam::new(PrfDataParamType::ByteArray(b"\xfe\xed\xbe\xef")), + PrfDataParam::new(PrfDataParamType::DkmLength(&dkm_length_format)), + PrfDataParam::new(PrfDataParamType::DkmLength(&dkm_length_format)), + ]; + let params = KbkdfParams::new(MechanismType::AES_CMAC, &data_params, None); + + // Derive key + let result = session.derive_key( + &Mechanism::KbkdfCounter(params), + base_key, + &derived_key_template, + ); + + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + Error::Pkcs11(RvError::MechanismParamInvalid, Function::DeriveKey) + )); + + // Delete base key + session.destroy_object(base_key)?; + + Ok(()) +} + +#[test] +#[serial] +fn kbkdf_invalid_data_params_feedback_mode() -> TestResult { + /* SoftHSM does not support NIST SP800-108 KDF yet */ + if is_softhsm() { + return Ok(()); + } + + let (pkcs11, slot) = init_pins(); + + // Open a session and log in + let session = pkcs11.open_rw_session(slot)?; + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + // Generate base key + let base_template = [ + Attribute::Token(true), + Attribute::Private(false), + Attribute::ValueLen((AES256_BLOCK_SIZE as u64).into()), + Attribute::Derive(true), + ]; + let base_key = session.generate_key(&Mechanism::AesKeyGen, &base_template)?; + + // The template of the key to derive + let derived_key_template = [ + Attribute::Token(false), + Attribute::Private(false), + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::AES), + Attribute::ValueLen((AES256_BLOCK_SIZE as u64).into()), + Attribute::Encrypt(true), + Attribute::Decrypt(true), + ]; + + // Some variables we will use throughout + let counter_format = KbkdfCounterFormat::new(Endianness::Big, NonZeroUsize::new(16).unwrap()); + let dkm_length_format = KbkdfDkmLengthFormat::new( + KbkdfDkmLengthMethod::SumOfKeys, + Endianness::Big, + NonZeroUsize::new(16).unwrap(), + ); + + /* MISSING ITERATION VARIABLE */ + + // Instantiate KBKDF + let data_params = [ + PrfDataParam::new(PrfDataParamType::ByteArray(b"test_label")), + PrfDataParam::new(PrfDataParamType::ByteArray(&[0])), + PrfDataParam::new(PrfDataParamType::ByteArray(b"\xfe\xed\xbe\xef")), + PrfDataParam::new(PrfDataParamType::DkmLength(&dkm_length_format)), + ]; + let params = KbkdfFeedbackParams::new(MechanismType::AES_CMAC, &data_params, None, None); + + // Derive key + let result = session.derive_key( + &Mechanism::KbkdfFeedback(params), + base_key, + &derived_key_template, + ); + + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + Error::Pkcs11(RvError::MechanismParamInvalid, Function::DeriveKey) + )); + + /* INCLUDING COUNTER FORMAT IN ITERATION VARIABLE */ + + // Instantiate KBKDF + let data_params = [ + PrfDataParam::new(PrfDataParamType::IterationVariable(Some(&counter_format))), + PrfDataParam::new(PrfDataParamType::ByteArray(b"test_label")), + PrfDataParam::new(PrfDataParamType::ByteArray(&[0])), + PrfDataParam::new(PrfDataParamType::ByteArray(b"\xfe\xed\xbe\xef")), + PrfDataParam::new(PrfDataParamType::DkmLength(&dkm_length_format)), + ]; + let params = KbkdfFeedbackParams::new(MechanismType::AES_CMAC, &data_params, None, None); + + // Derive key + let result = session.derive_key( + &Mechanism::KbkdfFeedback(params), + base_key, + &derived_key_template, + ); + + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + Error::Pkcs11(RvError::MechanismParamInvalid, Function::DeriveKey) + )); + + /* INCLUDING MORE THAN 1 DKM LENGTH FORMAT */ + + // Instantiate KBKDF + let data_params = [ + PrfDataParam::new(PrfDataParamType::IterationVariable(Some(&counter_format))), + PrfDataParam::new(PrfDataParamType::ByteArray(b"test_label")), + PrfDataParam::new(PrfDataParamType::ByteArray(&[0])), + PrfDataParam::new(PrfDataParamType::ByteArray(b"\xfe\xed\xbe\xef")), + PrfDataParam::new(PrfDataParamType::DkmLength(&dkm_length_format)), + PrfDataParam::new(PrfDataParamType::DkmLength(&dkm_length_format)), + ]; + let params = KbkdfFeedbackParams::new(MechanismType::AES_CMAC, &data_params, None, None); + + // Derive key + let result = session.derive_key( + &Mechanism::KbkdfFeedback(params), + base_key, + &derived_key_template, + ); + + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + Error::Pkcs11(RvError::MechanismParamInvalid, Function::DeriveKey) + )); + + // Delete base key + session.destroy_object(base_key)?; + + Ok(()) +} + +#[test] +#[serial] +fn kbkdf_invalid_data_params_double_pipeline_mode() -> TestResult { + /* FIXME: NIST SP800-108 in double-pipeline mode is not yet supported by SoftHSM or Kryoptic */ + if true { + return Ok(()); + } + + let (pkcs11, slot) = init_pins(); + + // Open a session and log in + let session = pkcs11.open_rw_session(slot)?; + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + // Generate base key + let base_template = [ + Attribute::Token(true), + Attribute::Private(false), + Attribute::ValueLen((AES256_BLOCK_SIZE as u64).into()), + Attribute::Derive(true), + ]; + let base_key = session.generate_key(&Mechanism::AesKeyGen, &base_template)?; + + // The template of the key to derive + let derived_key_template = [ + Attribute::Token(false), + Attribute::Private(false), + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::AES), + Attribute::ValueLen((AES256_BLOCK_SIZE as u64).into()), + Attribute::Encrypt(true), + Attribute::Decrypt(true), + ]; + + // Some variables we will use throughout + let counter_format = KbkdfCounterFormat::new(Endianness::Big, NonZeroUsize::new(16).unwrap()); + let dkm_length_format = KbkdfDkmLengthFormat::new( + KbkdfDkmLengthMethod::SumOfKeys, + Endianness::Big, + NonZeroUsize::new(16).unwrap(), + ); + + /* MISSING ITERATION VARIABLE */ + + // Instantiate KBKDF + let data_params = [ + PrfDataParam::new(PrfDataParamType::ByteArray(b"test_label")), + PrfDataParam::new(PrfDataParamType::ByteArray(&[0])), + PrfDataParam::new(PrfDataParamType::ByteArray(b"\xfe\xed\xbe\xef")), + PrfDataParam::new(PrfDataParamType::DkmLength(&dkm_length_format)), + ]; + let params = KbkdfParams::new(MechanismType::AES_CMAC, &data_params, None); + + // Derive key + let result = session.derive_key( + &Mechanism::KbkdfDoublePipeline(params), + base_key, + &derived_key_template, + ); + + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + Error::Pkcs11(RvError::MechanismParamInvalid, Function::DeriveKey) + )); + + /* INCLUDING COUNTER FORMAT IN ITERATION VARIABLE */ + + // Instantiate KBKDF + let data_params = [ + PrfDataParam::new(PrfDataParamType::IterationVariable(Some(&counter_format))), + PrfDataParam::new(PrfDataParamType::ByteArray(b"test_label")), + PrfDataParam::new(PrfDataParamType::ByteArray(&[0])), + PrfDataParam::new(PrfDataParamType::ByteArray(b"\xfe\xed\xbe\xef")), + PrfDataParam::new(PrfDataParamType::DkmLength(&dkm_length_format)), + ]; + let params = KbkdfParams::new(MechanismType::AES_CMAC, &data_params, None); + + // Derive key + let result = session.derive_key( + &Mechanism::KbkdfDoublePipeline(params), + base_key, + &derived_key_template, + ); + + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + Error::Pkcs11(RvError::MechanismParamInvalid, Function::DeriveKey) + )); + + /* INCLUDING MORE THAN 1 DKM LENGTH FORMAT */ + + // Instantiate KBKDF + let data_params = [ + PrfDataParam::new(PrfDataParamType::IterationVariable(Some(&counter_format))), + PrfDataParam::new(PrfDataParamType::ByteArray(b"test_label")), + PrfDataParam::new(PrfDataParamType::ByteArray(&[0])), + PrfDataParam::new(PrfDataParamType::ByteArray(b"\xfe\xed\xbe\xef")), + PrfDataParam::new(PrfDataParamType::DkmLength(&dkm_length_format)), + PrfDataParam::new(PrfDataParamType::DkmLength(&dkm_length_format)), + ]; + let params = KbkdfParams::new(MechanismType::AES_CMAC, &data_params, None); + + // Derive key + let result = session.derive_key( + &Mechanism::KbkdfDoublePipeline(params), + base_key, + &derived_key_template, + ); + + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + Error::Pkcs11(RvError::MechanismParamInvalid, Function::DeriveKey) + )); + + // Delete base key + session.destroy_object(base_key)?; + + Ok(()) +} + #[test] #[serial] fn sign_verify_sha1_hmac() -> TestResult {