Skip to content

Commit f0a2974

Browse files
Added mechanisms for NIST SP 800-108 KDF and their parameters
Signed-off-by: Jacob Prud'homme <[email protected]>
1 parent 81ae20f commit f0a2974

File tree

2 files changed

+414
-0
lines changed

2 files changed

+414
-0
lines changed

cryptoki/src/mechanism/kbkdf.rs

Lines changed: 376 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,376 @@
1+
// Copyright 2025 Contributors to the Parsec project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
//! Mechanisms of NIST key-based key derive functions (SP 800-108, informally KBKDF)
4+
//! See: <https://docs.oasis-open.org/pkcs11/pkcs11-curr/v3.0/os/pkcs11-curr-v3.0-os.html#_Toc30061446>
5+
6+
use std::{convert::TryInto, marker::PhantomData, ptr};
7+
8+
use cryptoki_sys::{
9+
CK_ATTRIBUTE, CK_ATTRIBUTE_PTR, CK_DERIVED_KEY, CK_DERIVED_KEY_PTR, CK_OBJECT_HANDLE,
10+
CK_PRF_DATA_PARAM, CK_PRF_DATA_PARAM_PTR, CK_SP800_108_BYTE_ARRAY, CK_SP800_108_COUNTER,
11+
CK_SP800_108_COUNTER_FORMAT, CK_SP800_108_DKM_LENGTH, CK_SP800_108_DKM_LENGTH_FORMAT,
12+
CK_SP800_108_DKM_LENGTH_SUM_OF_KEYS, CK_SP800_108_DKM_LENGTH_SUM_OF_SEGMENTS,
13+
CK_SP800_108_ITERATION_VARIABLE, CK_ULONG,
14+
};
15+
16+
use crate::object::Attribute;
17+
18+
use super::MechanismType;
19+
20+
/// Endianness of byte representation of data.
21+
#[derive(Debug, Clone, Copy, PartialEq)]
22+
pub enum Endianness {
23+
/// Little endian.
24+
Little,
25+
/// Big endian.
26+
Big,
27+
}
28+
29+
/// Defines encoding format for a counter value.
30+
///
31+
/// Corresponds to CK_SP800_108_COUNTER_FORMAT.
32+
#[derive(Debug, Clone, Copy)]
33+
pub struct KbkdfCounterFormat {
34+
endianness: Endianness,
35+
width_in_bits: usize,
36+
}
37+
38+
impl From<KbkdfCounterFormat> for CK_SP800_108_COUNTER_FORMAT {
39+
fn from(value: KbkdfCounterFormat) -> Self {
40+
Self {
41+
bLittleEndian: (value.endianness == Endianness::Little).into(),
42+
ulWidthInBits: value
43+
.width_in_bits
44+
.try_into()
45+
.expect("bit width of KBKDF internal counter does not fit in CK_ULONG"),
46+
}
47+
}
48+
}
49+
50+
/// Method for calculating length of DKM (derived key material).
51+
///
52+
/// Corresponds to CK_SP800_108_DKM_LENGTH_METHOD.
53+
#[derive(Debug, Clone, Copy)]
54+
pub enum DkmLengthMethod {
55+
/// Sum of length of all keys derived by given invocation of KDF.
56+
SumOfKeys,
57+
/// Sum of length of all segments of output produced by PRF in given invocation of KDF.
58+
SumOfSegments,
59+
}
60+
61+
/// Defines encoding format for DKM (derived key material).
62+
///
63+
/// Corresponds to CK_SP800_108_DKM_LENGTH_FORMAT.
64+
#[derive(Debug, Clone, Copy)]
65+
pub struct KbkdfDkmLengthFormat {
66+
dkm_length_method: DkmLengthMethod,
67+
endianness: Endianness,
68+
width_in_bits: usize,
69+
}
70+
71+
impl From<KbkdfDkmLengthFormat> for CK_SP800_108_DKM_LENGTH_FORMAT {
72+
fn from(value: KbkdfDkmLengthFormat) -> Self {
73+
Self {
74+
dkmLengthMethod: match value.dkm_length_method {
75+
DkmLengthMethod::SumOfKeys => CK_SP800_108_DKM_LENGTH_SUM_OF_KEYS,
76+
DkmLengthMethod::SumOfSegments => CK_SP800_108_DKM_LENGTH_SUM_OF_SEGMENTS,
77+
},
78+
bLittleEndian: (value.endianness == Endianness::Little).into(),
79+
ulWidthInBits: value
80+
.width_in_bits
81+
.try_into()
82+
.expect("bit width of KBKDF derived key material does not fit in CK_ULONG"),
83+
}
84+
}
85+
}
86+
87+
/// A segment of input data for the PRF, to be used to construct a sequence of input.
88+
///
89+
/// Corresponds to CK_PRF_DATA_PARAM in the specific cases of the KDF operating in feedback- or double pipeline-mode.
90+
#[derive(Debug, Clone, Copy)]
91+
pub enum PrfDataParam<'a> {
92+
/// Identifies location of predefined iteration variable in constructed PRF input data.
93+
IterationVariable,
94+
/// Identifies location of counter in constructed PRF input data.
95+
Counter(KbkdfCounterFormat),
96+
/// Identifies location of DKM (derived key material) length in constructed PRF input data.
97+
DkmLength(KbkdfDkmLengthFormat),
98+
/// Identifies location and value of byte array of data in constructed PRF input data.
99+
ByteArray(&'a [u8]),
100+
}
101+
102+
/// A segment of input data for the PRF, to be used to construct a sequence of input.
103+
///
104+
/// Corresponds to CK_PRF_DATA_PARAM in the specific case of the KDF operating in counter-mode.
105+
#[derive(Debug, Clone, Copy)]
106+
pub enum PrfCounterDataParam<'a> {
107+
/// Identifies location of iteration variable (a counter in this case) in constructed PRF input data.
108+
IterationVariable(KbkdfCounterFormat),
109+
/// Identifies location of DKM (derived key material) length in constructed PRF input data.
110+
DkmLength(KbkdfDkmLengthFormat),
111+
/// Identifies location and value of byte array of data in constructed PRF input data.
112+
ByteArray(&'a [u8]),
113+
}
114+
115+
/// Parameters for additional key to be derived from base key.
116+
#[derive(Debug, Clone, Copy)]
117+
pub struct DerivedKey<'a> {
118+
template: &'a [Attribute],
119+
object_handle: CK_OBJECT_HANDLE,
120+
}
121+
122+
impl<'a> DerivedKey<'a> {
123+
/// Construct template for additional key to be derived by KDF.
124+
///
125+
/// # Arguments
126+
///
127+
/// * `template` - The template for the key to be derived.
128+
pub fn new(template: &'a [Attribute]) -> Self {
129+
Self {
130+
template,
131+
object_handle: 0,
132+
}
133+
}
134+
}
135+
136+
/// NIST SP 800-108 (aka KBKDF) counter-mode parameters.
137+
///
138+
/// This structure wraps a `CK_SP800_108_KDF_PARAMS` structure.
139+
#[derive(Debug, Clone, Copy)]
140+
#[repr(transparent)]
141+
pub struct KbkdfCounterParams<'a> {
142+
inner: cryptoki_sys::CK_SP800_108_KDF_PARAMS,
143+
/// Marker type to ensure we don't outlive the data
144+
_marker: PhantomData<&'a [u8]>,
145+
}
146+
147+
impl<'a> KbkdfCounterParams<'a> {
148+
/// Construct parameters for NIST SP 800-108 KDF (aka KBKDF) pseuderandom function-based key
149+
/// derivation function, in counter-mode.
150+
///
151+
/// # Arguments
152+
///
153+
/// * `prf_mechanism` - The pseudorandom function that underlies the KBKDF operation.
154+
///
155+
/// * `prf_data_params` - The sequence of data segments used as input data for the PRF. Requires at least [`PrfCounterDataParam::IterationVariable`].
156+
///
157+
/// * `additional_derived_keys` - Any additional keys to be generated by the KDF from the base key.
158+
pub fn new(
159+
prf_mechanism: MechanismType,
160+
prf_data_params: Vec<PrfDataParam<'a>>,
161+
additional_derived_keys: Vec<DerivedKey<'a>>,
162+
) -> Self {
163+
let prf_data_params: Vec<CK_PRF_DATA_PARAM> =
164+
prf_data_params.into_iter().map(Into::into).collect();
165+
let additional_derived_keys: Vec<CK_DERIVED_KEY> = additional_derived_keys
166+
.into_iter()
167+
.map(Into::into)
168+
.collect();
169+
170+
Self {
171+
inner: cryptoki_sys::CK_SP800_108_KDF_PARAMS {
172+
prfType: prf_mechanism.into(),
173+
ulNumberOfDataParams: prf_data_params
174+
.len()
175+
.try_into()
176+
.expect("number of data parameters does not fit in CK_ULONG"),
177+
pDataParams: prf_data_params.as_ptr() as CK_PRF_DATA_PARAM_PTR,
178+
ulAdditionalDerivedKeys: additional_derived_keys
179+
.len()
180+
.try_into()
181+
.expect("number of additional derived keys does not fit in CK_ULONG"),
182+
pAdditionalDerivedKeys: additional_derived_keys.as_ptr() as CK_DERIVED_KEY_PTR,
183+
},
184+
_marker: PhantomData,
185+
}
186+
}
187+
}
188+
189+
/// NIST SP 800-108 (aka KBKDF) feedback-mode parameters.
190+
///
191+
/// This structure wraps a `CK_SP800_108_FEEDBACK_KDF_PARAMS` structure.
192+
#[derive(Debug, Clone, Copy)]
193+
#[repr(transparent)]
194+
pub struct KbkdfFeedbackParams<'a> {
195+
inner: cryptoki_sys::CK_SP800_108_FEEDBACK_KDF_PARAMS,
196+
/// Marker type to ensure we don't outlive the data
197+
_marker: PhantomData<&'a [u8]>,
198+
}
199+
200+
impl<'a> KbkdfFeedbackParams<'a> {
201+
/// Construct parameters for NIST SP 800-108 KDF (aka KBKDF) pseuderandom function-based key
202+
/// derivation function, in feedback-mode.
203+
///
204+
/// # Arguments
205+
///
206+
/// * `prf_mechanism` - The pseudorandom function that underlies the KBKDF operation.
207+
///
208+
/// * `prf_data_params` - The sequence of data segments used as input data for the PRF. Requires at least [`PrfCounterDataParam::IterationVariable`].
209+
///
210+
/// * `iv` - The IV to be used for the feedback-mode KDF.
211+
///
212+
/// * `additional_derived_keys` - Any additional keys to be generated by the KDF from the base key.
213+
pub fn new(
214+
prf_mechanism: MechanismType,
215+
prf_data_params: Vec<PrfDataParam<'a>>,
216+
iv: Option<&'a [u8]>,
217+
additional_derived_keys: Vec<DerivedKey<'a>>,
218+
) -> Self {
219+
let prf_data_params: Vec<CK_PRF_DATA_PARAM> =
220+
prf_data_params.into_iter().map(Into::into).collect();
221+
let additional_derived_keys: Vec<CK_DERIVED_KEY> = additional_derived_keys
222+
.into_iter()
223+
.map(Into::into)
224+
.collect();
225+
226+
Self {
227+
inner: cryptoki_sys::CK_SP800_108_FEEDBACK_KDF_PARAMS {
228+
prfType: prf_mechanism.into(),
229+
ulNumberOfDataParams: prf_data_params
230+
.len()
231+
.try_into()
232+
.expect("number of data parameters does not fit in CK_ULONG"),
233+
pDataParams: prf_data_params.as_ptr() as CK_PRF_DATA_PARAM_PTR,
234+
ulIVLen: iv.map_or(0, |iv| {
235+
iv.len()
236+
.try_into()
237+
.expect("IV length does not fit in CK_ULONG")
238+
}),
239+
pIV: iv.map_or(ptr::null_mut(), |iv| iv.as_ptr() as *mut _),
240+
ulAdditionalDerivedKeys: additional_derived_keys
241+
.len()
242+
.try_into()
243+
.expect("number of additional derived keys does not fit in CK_ULONG"),
244+
pAdditionalDerivedKeys: additional_derived_keys.as_ptr() as CK_DERIVED_KEY_PTR,
245+
},
246+
_marker: PhantomData,
247+
}
248+
}
249+
}
250+
251+
/// NIST SP 800-108 (aka KBKDF) double pipeline-mode parameters.
252+
///
253+
/// This structure wraps a `CK_SP800_108_KDF_PARAMS` structure.
254+
#[derive(Debug, Clone, Copy)]
255+
#[repr(transparent)]
256+
pub struct KbkdfDoublePipelineParams<'a> {
257+
inner: cryptoki_sys::CK_SP800_108_KDF_PARAMS,
258+
/// Marker type to ensure we don't outlive the data
259+
_marker: PhantomData<&'a [u8]>,
260+
}
261+
262+
impl<'a> KbkdfDoublePipelineParams<'a> {
263+
/// Construct parameters for NIST SP 800-108 KDF (aka KBKDF) pseuderandom function-based key
264+
/// derivation function, in double pipeline-mode.
265+
///
266+
/// # Arguments
267+
///
268+
/// * `prf_mechanism` - The pseudorandom function that underlies the KBKDF operation.
269+
///
270+
/// * `prf_data_params` - The sequence of data segments used as input data for the PRF. Requires at least [`PrfCounterDataParam::IterationVariable`].
271+
///
272+
/// * `additional_derived_keys` - Any additional keys to be generated by the KDF from the base key.
273+
pub fn new(
274+
prf_mechanism: MechanismType,
275+
prf_data_params: Vec<PrfDataParam<'a>>,
276+
additional_derived_keys: Vec<DerivedKey<'a>>,
277+
) -> Self {
278+
let prf_data_params: Vec<CK_PRF_DATA_PARAM> =
279+
prf_data_params.into_iter().map(Into::into).collect();
280+
let additional_derived_keys: Vec<CK_DERIVED_KEY> = additional_derived_keys
281+
.into_iter()
282+
.map(Into::into)
283+
.collect();
284+
285+
Self {
286+
inner: cryptoki_sys::CK_SP800_108_KDF_PARAMS {
287+
prfType: prf_mechanism.into(),
288+
ulNumberOfDataParams: prf_data_params
289+
.len()
290+
.try_into()
291+
.expect("number of data parameters does not fit in CK_ULONG"),
292+
pDataParams: prf_data_params.as_ptr() as CK_PRF_DATA_PARAM_PTR,
293+
ulAdditionalDerivedKeys: additional_derived_keys
294+
.len()
295+
.try_into()
296+
.expect("number of additional derived keys does not fit in CK_ULONG"),
297+
pAdditionalDerivedKeys: additional_derived_keys.as_ptr() as CK_DERIVED_KEY_PTR,
298+
},
299+
_marker: PhantomData,
300+
}
301+
}
302+
}
303+
304+
impl<'a> From<PrfDataParam<'a>> for CK_PRF_DATA_PARAM {
305+
fn from(value: PrfDataParam<'a>) -> Self {
306+
Self {
307+
type_: match value {
308+
PrfDataParam::IterationVariable => CK_SP800_108_ITERATION_VARIABLE,
309+
PrfDataParam::Counter(_) => CK_SP800_108_COUNTER,
310+
PrfDataParam::DkmLength(_) => CK_SP800_108_DKM_LENGTH,
311+
PrfDataParam::ByteArray(_) => CK_SP800_108_BYTE_ARRAY,
312+
},
313+
pValue: match value {
314+
PrfDataParam::IterationVariable => ptr::null_mut(),
315+
PrfDataParam::Counter(inner) => &inner as *const _ as *mut _,
316+
PrfDataParam::DkmLength(inner) => &inner as *const _ as *mut _,
317+
PrfDataParam::ByteArray(data) => data.as_ptr() as *mut _,
318+
},
319+
ulValueLen: match value {
320+
PrfDataParam::IterationVariable => 0,
321+
PrfDataParam::Counter(_) => size_of::<CK_SP800_108_COUNTER_FORMAT>() as CK_ULONG,
322+
PrfDataParam::DkmLength(_) => {
323+
size_of::<CK_SP800_108_DKM_LENGTH_FORMAT>() as CK_ULONG
324+
}
325+
PrfDataParam::ByteArray(data) => data
326+
.len()
327+
.try_into()
328+
.expect("length of data parameter does not fit in CK_ULONG"),
329+
},
330+
}
331+
}
332+
}
333+
334+
impl<'a> From<PrfCounterDataParam<'a>> for CK_PRF_DATA_PARAM {
335+
fn from(value: PrfCounterDataParam<'a>) -> Self {
336+
Self {
337+
type_: match value {
338+
PrfCounterDataParam::IterationVariable(_) => CK_SP800_108_ITERATION_VARIABLE,
339+
PrfCounterDataParam::DkmLength(_) => CK_SP800_108_DKM_LENGTH,
340+
PrfCounterDataParam::ByteArray(_) => CK_SP800_108_BYTE_ARRAY,
341+
},
342+
pValue: match value {
343+
PrfCounterDataParam::IterationVariable(inner) => &inner as *const _ as *mut _,
344+
PrfCounterDataParam::DkmLength(inner) => &inner as *const _ as *mut _,
345+
PrfCounterDataParam::ByteArray(data) => data.as_ptr() as *mut _,
346+
},
347+
ulValueLen: match value {
348+
PrfCounterDataParam::IterationVariable(_) => {
349+
size_of::<CK_SP800_108_COUNTER_FORMAT>() as CK_ULONG
350+
}
351+
PrfCounterDataParam::DkmLength(_) => {
352+
size_of::<CK_SP800_108_DKM_LENGTH_FORMAT>() as CK_ULONG
353+
}
354+
PrfCounterDataParam::ByteArray(data) => data
355+
.len()
356+
.try_into()
357+
.expect("length of data parameter does not fit in CK_ULONG"),
358+
},
359+
}
360+
}
361+
}
362+
363+
impl<'a> From<DerivedKey<'a>> for CK_DERIVED_KEY {
364+
fn from(mut value: DerivedKey<'a>) -> Self {
365+
let template: Vec<CK_ATTRIBUTE> = value.template.iter().map(|attr| attr.into()).collect();
366+
367+
Self {
368+
pTemplate: template.as_ptr() as CK_ATTRIBUTE_PTR,
369+
ulAttributeCount: template
370+
.len()
371+
.try_into()
372+
.expect("number of attributes in template does not fit in CK_ULONG"),
373+
phKey: &mut value.object_handle,
374+
}
375+
}
376+
}

0 commit comments

Comments
 (0)