From 9a0092a959825db2b6e7ad288d8d88d456e86265 Mon Sep 17 00:00:00 2001 From: Jacob Prud'homme <2160185+jacobprudhomme@users.noreply.github.com> Date: Sun, 7 Sep 2025 23:02:33 +0200 Subject: [PATCH 01/10] Create new parameters for simple key derivation mechanisms These include the CKM_{CONCATENATE,XOR}_{BASE,DATA}_AND_{DATA,BASE}, CKM_CONCATENATE_BASE_AND_KEY and CKM_EXTRACT_KEY_FROM_KEY mechanisms Signed-off-by: Jacob Prud'homme <2160185+jacobprudhomme@users.noreply.github.com> --- cryptoki/src/mechanism/misc.rs | 63 ++++++++++++++++++++++++++++++++++ cryptoki/src/mechanism/mod.rs | 1 + 2 files changed, 64 insertions(+) create mode 100644 cryptoki/src/mechanism/misc.rs diff --git a/cryptoki/src/mechanism/misc.rs b/cryptoki/src/mechanism/misc.rs new file mode 100644 index 00000000..9aacbe68 --- /dev/null +++ b/cryptoki/src/mechanism/misc.rs @@ -0,0 +1,63 @@ +// Copyright 2025 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 +//! Miscellaneous mechanisms: +//! - Simple key derivation mechanisms +//! See: + +use std::marker::PhantomData; + +use cryptoki_sys::*; + +/// A parameter used as input for one of the simple key derivation mechanisms +/// that takes a bytestring as input (CKM_CONCATENATE_BASE_AND_DATA, +/// CKM_CONCATENATE_DATA_AND_BASE, CKM_XOR_BASE_AND_DATA). +#[derive(Debug, Clone, Copy)] +#[repr(transparent)] +pub struct KeyDerivationStringData<'a> { + inner: CK_KEY_DERIVATION_STRING_DATA, + /// Marker type to ensure we don't outlive the data + _marker: PhantomData<&'a [u8]>, +} + +impl<'a> KeyDerivationStringData<'a> { + /// Construct parameter for simple key derivation mechanisms that take a + /// bytestring as one of their inputs. + /// + /// # Arguments + /// + /// * `data` - The bytestring to use as input to the key derivation method. + pub fn new(data: &'a [u8]) -> Self { + Self { + inner: CK_KEY_DERIVATION_STRING_DATA { + pData: data.as_ptr() as *mut _, + ulLen: data + .len() + .try_into() + .expect("length of data does not fit in CK_ULONG"), + }, + _marker: PhantomData, + } + } +} + +/// A parameter indicating the index of the base key from which to extract the +/// derived key. +#[derive(Debug, Clone, Copy)] +#[repr(transparent)] +pub struct ExtractParams(CK_EXTRACT_PARAMS); + +impl ExtractParams { + /// Construct parameter from index to extract the derived key from the base + /// key. + /// + /// # Arguments + /// + /// * `index` - The index from which to extract the derived key from the base key. + pub fn new(index: usize) -> Self { + Self( + index + .try_into() + .expect("given usize value does not fit into CK_ULONG"), + ) + } +} diff --git a/cryptoki/src/mechanism/mod.rs b/cryptoki/src/mechanism/mod.rs index 143b6652..9d9a0277 100644 --- a/cryptoki/src/mechanism/mod.rs +++ b/cryptoki/src/mechanism/mod.rs @@ -10,6 +10,7 @@ pub mod elliptic_curve; pub mod hkdf; pub mod kbkdf; mod mechanism_info; +pub mod misc; pub mod rsa; pub mod vendor_defined; From 2f6a5abe5eabd5fc3c331d9a5886d79759761e1a Mon Sep 17 00:00:00 2001 From: Jacob Prud'homme <2160185+jacobprudhomme@users.noreply.github.com> Date: Tue, 9 Sep 2025 11:16:01 +0200 Subject: [PATCH 02/10] Add mechanisms for simple key derivation Signed-off-by: Jacob Prud'homme <2160185+jacobprudhomme@users.noreply.github.com> --- cryptoki/src/mechanism/mod.rs | 52 +++++++++++++++++++++++++++++++++++ cryptoki/src/object.rs | 1 + 2 files changed, 53 insertions(+) diff --git a/cryptoki/src/mechanism/mod.rs b/cryptoki/src/mechanism/mod.rs index 9d9a0277..744c6b17 100644 --- a/cryptoki/src/mechanism/mod.rs +++ b/cryptoki/src/mechanism/mod.rs @@ -25,7 +25,9 @@ use std::ptr::null_mut; use vendor_defined::VendorDefinedMechanism; use crate::error::Error; +use crate::mechanism::misc::{ExtractParams, KeyDerivationStringData}; use crate::mechanism::rsa::PkcsOaepParams; +use crate::object::ObjectHandle; pub use mechanism_info::MechanismInfo; #[derive(Copy, Debug, Clone, PartialEq, Eq)] @@ -343,6 +345,28 @@ impl MechanismType { val: CKM_SP800_108_DOUBLE_PIPELINE_KDF, }; + // Other simple key derivation mechanisms + /// Concatenation of a base key and another key + pub const CONCATENATE_BASE_AND_KEY: MechanismType = MechanismType { + val: CKM_CONCATENATE_BASE_AND_KEY, + }; + /// Concatenation of a base key and data (i.e. data appended) + pub const CONCATENATE_BASE_AND_DATA: MechanismType = MechanismType { + val: CKM_CONCATENATE_BASE_AND_DATA, + }; + /// Concatenation of data and a base key (i.e. data prepended) + pub const CONCATENATE_DATA_AND_BASE: MechanismType = MechanismType { + val: CKM_CONCATENATE_DATA_AND_BASE, + }; + /// XOR-ing of a base key and data + pub const XOR_BASE_AND_DATA: MechanismType = MechanismType { + val: CKM_XOR_BASE_AND_DATA, + }; + /// Extraction of a key from bits of another key + pub const EXTRACT_KEY_FROM_KEY: MechanismType = MechanismType { + val: CKM_EXTRACT_KEY_FROM_KEY, + }; + // ML-KEM /// ML-KEM key pair generation mechanism pub const ML_KEM_KEY_PAIR_GEN: MechanismType = MechanismType { @@ -964,6 +988,11 @@ impl TryFrom for MechanismType { 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), + CKM_CONCATENATE_BASE_AND_KEY => Ok(MechanismType::CONCATENATE_BASE_AND_KEY), + CKM_CONCATENATE_BASE_AND_DATA => Ok(MechanismType::CONCATENATE_BASE_AND_DATA), + CKM_CONCATENATE_DATA_AND_BASE => Ok(MechanismType::CONCATENATE_DATA_AND_BASE), + CKM_XOR_BASE_AND_DATA => Ok(MechanismType::XOR_BASE_AND_DATA), + CKM_EXTRACT_KEY_FROM_KEY => Ok(MechanismType::EXTRACT_KEY_FROM_KEY), CKM_ML_KEM_KEY_PAIR_GEN => Ok(MechanismType::ML_KEM_KEY_PAIR_GEN), CKM_ML_KEM => Ok(MechanismType::ML_KEM), CKM_ML_DSA_KEY_PAIR_GEN => Ok(MechanismType::ML_DSA_KEY_PAIR_GEN), @@ -1220,6 +1249,18 @@ pub enum Mechanism<'a> { /// NIST SP 800-108 KDF (aka KBKDF) mechanism in double pipeline-mode KbkdfDoublePipeline(kbkdf::KbkdfParams<'a>), + // Other simple key derivation mechanisms + /// Concatenation of a base key and another key + ConcatenateBaseAndKey(ObjectHandle), + /// Concatenation of a base key and data (i.e. data appended) + ConcatenateBaseAndData(KeyDerivationStringData<'a>), + /// Concatenation of data and a base key (i.e. data prepended) + ConcatenateDataAndBase(KeyDerivationStringData<'a>), + /// XOR-ing of a base key and data + XorBaseAndData(KeyDerivationStringData<'a>), + /// Extraction of a key from bits of another key + ExtractKeyFromKey(ExtractParams), + // ML-KEM /// ML-KEM key pair generation mechanism MlKemKeyPairGen, @@ -1367,6 +1408,12 @@ impl Mechanism<'_> { Mechanism::KbkdfFeedback(_) => MechanismType::SP800_108_FEEDBACK_KDF, Mechanism::KbkdfDoublePipeline(_) => MechanismType::SP800_108_DOUBLE_PIPELINE_KDF, + Mechanism::ConcatenateBaseAndKey(_) => MechanismType::CONCATENATE_BASE_AND_KEY, + Mechanism::ConcatenateBaseAndData(_) => MechanismType::CONCATENATE_BASE_AND_DATA, + Mechanism::ConcatenateDataAndBase(_) => MechanismType::CONCATENATE_DATA_AND_BASE, + Mechanism::XorBaseAndData(_) => MechanismType::XOR_BASE_AND_DATA, + Mechanism::ExtractKeyFromKey(_) => MechanismType::EXTRACT_KEY_FROM_KEY, + Mechanism::MlKemKeyPairGen => MechanismType::ML_KEM_KEY_PAIR_GEN, Mechanism::MlKem => MechanismType::ML_KEM, @@ -1454,6 +1501,11 @@ impl From<&Mechanism<'_>> for CK_MECHANISM { make_mechanism(mechanism, params.inner()) } Mechanism::KbkdfFeedback(params) => make_mechanism(mechanism, params.inner()), + Mechanism::ConcatenateBaseAndKey(params) => make_mechanism(mechanism, params), + Mechanism::ConcatenateBaseAndData(params) + | Mechanism::ConcatenateDataAndBase(params) + | Mechanism::XorBaseAndData(params) => make_mechanism(mechanism, params), + Mechanism::ExtractKeyFromKey(params) => make_mechanism(mechanism, params), Mechanism::HashMlDsa(params) => make_mechanism(mechanism, params), Mechanism::MlDsa(params) | Mechanism::HashMlDsaSha224(params) diff --git a/cryptoki/src/object.rs b/cryptoki/src/object.rs index 69072e95..974e8f44 100644 --- a/cryptoki/src/object.rs +++ b/cryptoki/src/object.rs @@ -1019,6 +1019,7 @@ impl TryFrom for Attribute { } #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] /// Token specific identifier for an object pub struct ObjectHandle { handle: CK_OBJECT_HANDLE, From 3f1568ee4e6c91a002152a38f80356b2531b1852 Mon Sep 17 00:00:00 2001 From: Jacob Prud'homme <2160185+jacobprudhomme@users.noreply.github.com> Date: Thu, 11 Sep 2025 12:20:51 +0200 Subject: [PATCH 03/10] Add tests for simple key derivation mechanisms Signed-off-by: Jacob Prud'homme <2160185+jacobprudhomme@users.noreply.github.com> --- cryptoki/tests/basic.rs | 252 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 252 insertions(+) diff --git a/cryptoki/tests/basic.rs b/cryptoki/tests/basic.rs index a54b5d4a..f08476b3 100644 --- a/cryptoki/tests/basic.rs +++ b/cryptoki/tests/basic.rs @@ -12,6 +12,7 @@ use cryptoki::mechanism::kbkdf::{ DerivedKey, Endianness, KbkdfCounterFormat, KbkdfDkmLengthFormat, KbkdfDkmLengthMethod, KbkdfFeedbackParams, KbkdfParams, PrfDataParam, PrfDataParamType, }; +use cryptoki::mechanism::misc::{ExtractParams, KeyDerivationStringData}; use cryptoki::mechanism::rsa::{PkcsMgfType, PkcsOaepParams, PkcsOaepSource}; use cryptoki::mechanism::{Mechanism, MechanismType, MessageParam}; use cryptoki::object::{ @@ -772,6 +773,257 @@ fn derive_key_sp800() -> TestResult { Ok(()) } +#[test] +#[serial] +#[ignore = "unsupported by both SoftHSM and Kryoptic for the moment. See latchset/kryoptic#323 to track support"] +fn derive_key_concatenation_two_keys() -> 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())))?; + + // Define keys to concatenate + let key_value = [0x12, 0x34, 0x56, 0x78, 0x90, 0x01]; + + let key1_template = [ + Attribute::Token(true), + Attribute::Private(false), + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::GENERIC_SECRET), + Attribute::Value(key_value[..3].to_vec()), + Attribute::Derive(true), + ]; + let key2_template = [ + Attribute::Token(true), + Attribute::Private(false), + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::GENERIC_SECRET), + Attribute::Value(key_value[3..].to_vec()), + Attribute::Derive(true), + ]; + + let key1 = session.create_object(&key1_template)?; + let key2 = session.create_object(&key2_template)?; + + // Derive key from two input keys + let derived_key_template = [ + Attribute::Token(true), + Attribute::Private(false), + Attribute::Sensitive(false), + Attribute::Extractable(true), + ]; + let derived_key = session.derive_key( + &Mechanism::ConcatenateBaseAndKey(key2), + key1, + &derived_key_template, + )?; + + let derived_key_value = session + .get_attributes(derived_key, &[AttributeType::Value])? + .remove(0); + let derived_key_value = if let Attribute::Value(value) = derived_key_value { + value + } else { + panic!("Expected value attribute."); + }; + + assert_eq!(&derived_key_value, &key_value); + + // Delete keys + session.destroy_object(key1)?; + session.destroy_object(key2)?; + session.destroy_object(derived_key)?; + + Ok(()) +} + +#[test] +#[serial] +#[ignore = "unsupported by both SoftHSM and Kryoptic for the moment. See latchset/kryoptic#323 to track support"] +fn derive_key_concatenation_key_and_data() -> 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())))?; + + // Define key/data to concatenate + let mut data_value = [0x12, 0x34, 0x56, 0x78, 0x90, 0x01]; + + let key_template = [ + Attribute::Token(true), + Attribute::Private(false), + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::GENERIC_SECRET), + Attribute::Value(data_value[..3].to_vec()), + Attribute::Derive(true), + ]; + + let key = session.create_object(&key_template)?; + + // Derive keys from input key and data, both appended and prepended + let derived_key_template = [ + Attribute::Token(true), + Attribute::Private(false), + Attribute::Sensitive(false), + Attribute::Extractable(true), + ]; + let data = KeyDerivationStringData::new(&data_value[3..]); + let derived_key1 = session.derive_key( + &Mechanism::ConcatenateBaseAndData(data), + key, + &derived_key_template, + )?; + let derived_key2 = session.derive_key( + &Mechanism::ConcatenateDataAndBase(data), + key, + &derived_key_template, + )?; + + let derived_key1_value = session + .get_attributes(derived_key1, &[AttributeType::Value])? + .remove(0); + let derived_key1_value = if let Attribute::Value(value) = derived_key1_value { + value + } else { + panic!("Expected value attribute."); + }; + let derived_key2_value = session + .get_attributes(derived_key2, &[AttributeType::Value])? + .remove(0); + let derived_key2_value = if let Attribute::Value(value) = derived_key2_value { + value + } else { + panic!("Expected value attribute."); + }; + + assert_eq!(&derived_key1_value, &data_value); + // Swap halves of the data_value in-place + let (first_half, second_half) = data_value.split_at_mut(3); + first_half.swap_with_slice(second_half); + assert_eq!(&derived_key2_value, &data_value); + + // Delete keys + session.destroy_object(key)?; + session.destroy_object(derived_key1)?; + session.destroy_object(derived_key2)?; + + Ok(()) +} + +#[test] +#[serial] +#[ignore = "unsupported by both SoftHSM and Kryoptic for the moment. See latchset/kryoptic#323 to track support"] +fn derive_key_xor_key_and_data() -> 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())))?; + + // Define key/data to xor + let data_value = [0x12, 0x34, 0x56, 0x78, 0x90, 0x01]; + + let key_template = [ + Attribute::Token(true), + Attribute::Private(false), + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::GENERIC_SECRET), + Attribute::Value(data_value.to_vec()), + Attribute::Derive(true), + ]; + + let key = session.create_object(&key_template)?; + + // Derive key by xor-ing input key and data + let derived_key_template = [ + Attribute::Token(true), + Attribute::Private(false), + Attribute::Sensitive(false), + Attribute::Extractable(true), + ]; + let data = KeyDerivationStringData::new(&data_value); + let derived_key = session.derive_key( + &Mechanism::XorBaseAndData(data), + key, + &derived_key_template, + )?; + + let derived_key_value = session + .get_attributes(derived_key, &[AttributeType::Value])? + .remove(0); + let derived_key_value = if let Attribute::Value(value) = derived_key_value { + value + } else { + panic!("Expected value attribute."); + }; + + assert_eq!(&derived_key_value, &[0; 6]); + + // Delete keys + session.destroy_object(key)?; + session.destroy_object(derived_key)?; + + Ok(()) +} + +#[test] +#[serial] +#[ignore = "unsupported by both SoftHSM and Kryoptic for the moment. See latchset/kryoptic#323 to track support"] +fn derive_key_extract_from_key() -> 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())))?; + + // Define key to extract from + let data_value = [0x12, 0x34, 0x56, 0x78, 0x90, 0x01]; + + let key_template = [ + Attribute::Token(true), + Attribute::Private(false), + Attribute::Class(ObjectClass::SECRET_KEY), + Attribute::KeyType(KeyType::GENERIC_SECRET), + Attribute::Value(data_value.to_vec()), + Attribute::Derive(true), + ]; + + let key = session.create_object(&key_template)?; + + // Derive key by extracting subset of base key + let derived_key_template = [ + Attribute::Token(true), + Attribute::Private(false), + Attribute::Sensitive(false), + Attribute::Extractable(true), + ]; + let params = ExtractParams::new(3); + let derived_key = session.derive_key( + &Mechanism::ExtractKeyFromKey(params), + key, + &derived_key_template, + )?; + + let derived_key_value = session + .get_attributes(derived_key, &[AttributeType::Value])? + .remove(0); + let derived_key_value = if let Attribute::Value(value) = derived_key_value { + value + } else { + panic!("Expected value attribute."); + }; + + assert_eq!(&derived_key_value, &data_value[3..]); + + // Delete keys + session.destroy_object(key)?; + session.destroy_object(derived_key)?; + + Ok(()) +} + #[test] #[serial] fn import_export() -> TestResult { From 5a3ed95c6d03a68ed774761af11dd8a4486113d2 Mon Sep 17 00:00:00 2001 From: Jacob Prud'homme <2160185+jacobprudhomme@users.noreply.github.com> Date: Thu, 11 Sep 2025 12:29:58 +0200 Subject: [PATCH 04/10] Normalize making a CK_MECHANISM object from params for AEAD Despite the fact that we can use `make_mechanism()` to create a `CK_MECHANISM` object from the GcmParams and GcmMessageParams, we were not doing so. This fixes this Signed-off-by: Jacob Prud'homme <2160185+jacobprudhomme@users.noreply.github.com> --- cryptoki/src/mechanism/mod.rs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/cryptoki/src/mechanism/mod.rs b/cryptoki/src/mechanism/mod.rs index 744c6b17..e4b823ae 100644 --- a/cryptoki/src/mechanism/mod.rs +++ b/cryptoki/src/mechanism/mod.rs @@ -1465,20 +1465,8 @@ impl From<&Mechanism<'_>> for CK_MECHANISM { | Mechanism::Des3Cbc(params) | Mechanism::DesCbcPad(params) | Mechanism::Des3CbcPad(params) => make_mechanism(mechanism, params), - Mechanism::AesGcm(params) => CK_MECHANISM { - mechanism, - pParameter: params as *const _ as *mut c_void, - ulParameterLen: size_of::() - .try_into() - .expect("usize can not fit in CK_ULONG"), - }, - Mechanism::AesGcmMessage(params) => CK_MECHANISM { - mechanism, - pParameter: params as *const _ as *mut c_void, - ulParameterLen: size_of::() - .try_into() - .expect("usize can not fit in CK_ULONG"), - }, + Mechanism::AesGcm(params) => make_mechanism(mechanism, params), + Mechanism::AesGcmMessage(params) => make_mechanism(mechanism, params), Mechanism::RsaPkcsPss(params) | Mechanism::Sha1RsaPkcsPss(params) | Mechanism::Sha256RsaPkcsPss(params) From 2acd556d8afc8eb0f4a1b8f099ac6544e109ea73 Mon Sep 17 00:00:00 2001 From: Jacob Prud'homme <2160185+jacobprudhomme@users.noreply.github.com> Date: Thu, 11 Sep 2025 12:57:04 +0200 Subject: [PATCH 05/10] Make lint fixes Signed-off-by: Jacob Prud'homme <2160185+jacobprudhomme@users.noreply.github.com> --- cryptoki/src/mechanism/misc.rs | 2 +- cryptoki/tests/basic.rs | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/cryptoki/src/mechanism/misc.rs b/cryptoki/src/mechanism/misc.rs index 9aacbe68..a4c8d3da 100644 --- a/cryptoki/src/mechanism/misc.rs +++ b/cryptoki/src/mechanism/misc.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 //! Miscellaneous mechanisms: //! - Simple key derivation mechanisms -//! See: +//! See: use std::marker::PhantomData; diff --git a/cryptoki/tests/basic.rs b/cryptoki/tests/basic.rs index f08476b3..91506316 100644 --- a/cryptoki/tests/basic.rs +++ b/cryptoki/tests/basic.rs @@ -944,11 +944,8 @@ fn derive_key_xor_key_and_data() -> TestResult { Attribute::Extractable(true), ]; let data = KeyDerivationStringData::new(&data_value); - let derived_key = session.derive_key( - &Mechanism::XorBaseAndData(data), - key, - &derived_key_template, - )?; + let derived_key = + session.derive_key(&Mechanism::XorBaseAndData(data), key, &derived_key_template)?; let derived_key_value = session .get_attributes(derived_key, &[AttributeType::Value])? From 02218844b934ffc2defaadca38687d6e7abccd12 Mon Sep 17 00:00:00 2001 From: Jacob Prud'homme <2160185+jacobprudhomme@users.noreply.github.com> Date: Tue, 23 Sep 2025 22:05:55 +0200 Subject: [PATCH 06/10] Added value-len attribute when extracting key from key Signed-off-by: Jacob Prud'homme <2160185+jacobprudhomme@users.noreply.github.com> --- cryptoki/tests/basic.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cryptoki/tests/basic.rs b/cryptoki/tests/basic.rs index 91506316..c1912e8f 100644 --- a/cryptoki/tests/basic.rs +++ b/cryptoki/tests/basic.rs @@ -993,6 +993,7 @@ fn derive_key_extract_from_key() -> TestResult { let derived_key_template = [ Attribute::Token(true), Attribute::Private(false), + Attribute::ValueLen(2.into()), Attribute::Sensitive(false), Attribute::Extractable(true), ]; @@ -1012,7 +1013,12 @@ fn derive_key_extract_from_key() -> TestResult { panic!("Expected value attribute."); }; - assert_eq!(&derived_key_value, &data_value[3..]); + // Manually extract exactly the same part of the original value, to compare + let mut result_value = u32::from_be_bytes(data_value[..4].try_into().unwrap()); + result_value <<= 3; + result_value &= 0xFFFF0000; + + assert_eq!(&derived_key_value, &result_value.to_be_bytes()[..2]); // Delete keys session.destroy_object(key)?; From 319272302defb2bf3dde0dbeb1bcb250a5ff2691 Mon Sep 17 00:00:00 2001 From: Jacob Prud'homme <2160185+jacobprudhomme@users.noreply.github.com> Date: Tue, 23 Sep 2025 22:09:42 +0200 Subject: [PATCH 07/10] Explain that index used in key extract derivation method is bit index Signed-off-by: Jacob Prud'homme <2160185+jacobprudhomme@users.noreply.github.com> --- cryptoki/src/mechanism/misc.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cryptoki/src/mechanism/misc.rs b/cryptoki/src/mechanism/misc.rs index a4c8d3da..bbca8aa4 100644 --- a/cryptoki/src/mechanism/misc.rs +++ b/cryptoki/src/mechanism/misc.rs @@ -40,19 +40,19 @@ impl<'a> KeyDerivationStringData<'a> { } } -/// A parameter indicating the index of the base key from which to extract the -/// derived key. +/// A parameter indicating the index of the bit in the the base key from which +/// to extract the derived key. #[derive(Debug, Clone, Copy)] #[repr(transparent)] pub struct ExtractParams(CK_EXTRACT_PARAMS); impl ExtractParams { - /// Construct parameter from index to extract the derived key from the base - /// key. + /// Construct parameter from index of bit from which to extract the derived + /// key from the base key. /// /// # Arguments /// - /// * `index` - The index from which to extract the derived key from the base key. + /// * `index` - The bit index from which to extract the derived key from the base key. pub fn new(index: usize) -> Self { Self( index From 71382d5dc93e933f098f5b4a0fd003b8715506eb Mon Sep 17 00:00:00 2001 From: Jacob Prud'homme <2160185+jacobprudhomme@users.noreply.github.com> Date: Tue, 23 Sep 2025 22:20:12 +0200 Subject: [PATCH 08/10] Reinstate all simple key derivation tests The latest version of Kryoptic released to Fedora actually supports them now Signed-off-by: Jacob Prud'homme <2160185+jacobprudhomme@users.noreply.github.com> --- cryptoki/tests/basic.rs | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/cryptoki/tests/basic.rs b/cryptoki/tests/basic.rs index c1912e8f..f61035db 100644 --- a/cryptoki/tests/basic.rs +++ b/cryptoki/tests/basic.rs @@ -775,8 +775,13 @@ fn derive_key_sp800() -> TestResult { #[test] #[serial] -#[ignore = "unsupported by both SoftHSM and Kryoptic for the moment. See latchset/kryoptic#323 to track support"] fn derive_key_concatenation_two_keys() -> TestResult { + // Mechanism not supported by SoftHSM + if is_softhsm() { + /* return Ignore(); */ + return Ok(()); + } + let (pkcs11, slot) = init_pins(); // Open a session and log in @@ -840,8 +845,13 @@ fn derive_key_concatenation_two_keys() -> TestResult { #[test] #[serial] -#[ignore = "unsupported by both SoftHSM and Kryoptic for the moment. See latchset/kryoptic#323 to track support"] fn derive_key_concatenation_key_and_data() -> TestResult { + // Mechanism not supported by SoftHSM + if is_softhsm() { + /* return Ignore(); */ + return Ok(()); + } + let (pkcs11, slot) = init_pins(); // Open a session and log in @@ -914,8 +924,13 @@ fn derive_key_concatenation_key_and_data() -> TestResult { #[test] #[serial] -#[ignore = "unsupported by both SoftHSM and Kryoptic for the moment. See latchset/kryoptic#323 to track support"] fn derive_key_xor_key_and_data() -> TestResult { + // Mechanism not supported by SoftHSM + if is_softhsm() { + /* return Ignore(); */ + return Ok(()); + } + let (pkcs11, slot) = init_pins(); // Open a session and log in @@ -967,8 +982,13 @@ fn derive_key_xor_key_and_data() -> TestResult { #[test] #[serial] -#[ignore = "unsupported by both SoftHSM and Kryoptic for the moment. See latchset/kryoptic#323 to track support"] fn derive_key_extract_from_key() -> TestResult { + // Mechanism not supported by SoftHSM + if is_softhsm() { + /* return Ignore(); */ + return Ok(()); + } + let (pkcs11, slot) = init_pins(); // Open a session and log in From 728b7ac0c4509dc1b20cb0a91f6a421aa1f7664b Mon Sep 17 00:00:00 2001 From: Jacob Prud'homme <2160185+jacobprudhomme@users.noreply.github.com> Date: Tue, 23 Sep 2025 22:31:56 +0200 Subject: [PATCH 09/10] Complete derived key templates They were missing the object class attribute Signed-off-by: Jacob Prud'homme <2160185+jacobprudhomme@users.noreply.github.com> --- cryptoki/tests/basic.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cryptoki/tests/basic.rs b/cryptoki/tests/basic.rs index f61035db..488ed5ad 100644 --- a/cryptoki/tests/basic.rs +++ b/cryptoki/tests/basic.rs @@ -815,6 +815,7 @@ fn derive_key_concatenation_two_keys() -> TestResult { let derived_key_template = [ Attribute::Token(true), Attribute::Private(false), + Attribute::Class(ObjectClass::SECRET_KEY), Attribute::Sensitive(false), Attribute::Extractable(true), ]; @@ -876,6 +877,7 @@ fn derive_key_concatenation_key_and_data() -> TestResult { let derived_key_template = [ Attribute::Token(true), Attribute::Private(false), + Attribute::Class(ObjectClass::SECRET_KEY), Attribute::Sensitive(false), Attribute::Extractable(true), ]; @@ -955,6 +957,7 @@ fn derive_key_xor_key_and_data() -> TestResult { let derived_key_template = [ Attribute::Token(true), Attribute::Private(false), + Attribute::Class(ObjectClass::SECRET_KEY), Attribute::Sensitive(false), Attribute::Extractable(true), ]; @@ -1013,6 +1016,7 @@ fn derive_key_extract_from_key() -> TestResult { let derived_key_template = [ Attribute::Token(true), Attribute::Private(false), + Attribute::Class(ObjectClass::SECRET_KEY), Attribute::ValueLen(2.into()), Attribute::Sensitive(false), Attribute::Extractable(true), From 0a02b5dc465d8a9fc0c6ca6245f94cd3d7dc77f1 Mon Sep 17 00:00:00 2001 From: Jacob Prud'homme <2160185+jacobprudhomme@users.noreply.github.com> Date: Mon, 6 Oct 2025 16:49:59 +0200 Subject: [PATCH 10/10] Made source keys extractable This is needed for allowing people to use them as the secondary key in one of the simple key derivation mechanisms that take two keys --- cryptoki/tests/basic.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cryptoki/tests/basic.rs b/cryptoki/tests/basic.rs index 488ed5ad..b9718069 100644 --- a/cryptoki/tests/basic.rs +++ b/cryptoki/tests/basic.rs @@ -798,6 +798,8 @@ fn derive_key_concatenation_two_keys() -> TestResult { Attribute::KeyType(KeyType::GENERIC_SECRET), Attribute::Value(key_value[..3].to_vec()), Attribute::Derive(true), + Attribute::Sensitive(false), + Attribute::Extractable(true), ]; let key2_template = [ Attribute::Token(true), @@ -806,6 +808,8 @@ fn derive_key_concatenation_two_keys() -> TestResult { Attribute::KeyType(KeyType::GENERIC_SECRET), Attribute::Value(key_value[3..].to_vec()), Attribute::Derive(true), + Attribute::Sensitive(false), + Attribute::Extractable(true), ]; let key1 = session.create_object(&key1_template)?;