Skip to main content
This is unreleased documentation for the main (development) branch of crypto-glue.

kbkdf/
lib.rs

1#![no_std]
2#![doc = include_str!("../README.md")]
3#![doc(
4    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
5    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
6)]
7#![cfg_attr(docsrs, feature(doc_cfg))]
8#![forbid(unsafe_code)]
9#![warn(missing_docs)]
10
11use core::{fmt, marker::PhantomData, ops::Mul};
12use digest::{
13    KeyInit, Mac,
14    array::{Array, ArraySize, typenum::Unsigned},
15    common::KeySizeUser,
16    consts::{U8, U32},
17    typenum::op,
18};
19
20pub mod sealed;
21
22/// KBKDF error type.
23#[derive(Debug, PartialEq)]
24pub enum Error {
25    /// Indicates that the requested length of the derived key is too large for the value of R specified.
26    InvalidRequestSize,
27}
28
29impl fmt::Display for Error {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        match self {
32            Error::InvalidRequestSize => write!(
33                f,
34                "Request output size is too large for the value of R specified"
35            ),
36        }
37    }
38}
39
40impl core::error::Error for Error {}
41
42/// Parameters used for KBKDF.
43///
44/// For more details, read the official specification: [NIST SP 800-108r1](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-108r1.pdf).
45pub struct Params<'k, 'l, 'c> {
46    /// Key-derivation key.
47    ///
48    /// key that is used as an input to a key-derivation function (along with other input data) to derive keying material.
49    pub kin: &'k [u8],
50    /// A string that identifies the purpose for the derived keying material, which is encoded as a bit string.
51    ///
52    /// The encoding method for the Label is defined in a larger context, for example, in the protocol that uses a KDF.
53    pub label: &'l [u8],
54    /// A bit string containing the information related to the derived keying material.
55    ///
56    /// It may include the identities of the parties who are deriving and/or using the derived keying material and,
57    /// optionally, a nonce known by the parties who derive the keys.
58    pub context: &'c [u8],
59    /// A flag indicating whether to update the Prf with the requested key length.
60    pub use_l: bool,
61    /// A flag indicating whether to separate the label from the context with a NULL byte.
62    pub use_separator: bool,
63    /// A flag indicating whether to update the Prf with the iteration counter.
64    pub use_counter: bool,
65}
66
67impl<'k, 'l, 'c> Params<'k, 'l, 'c> {
68    /// Create a new builder for [`Params`]
69    pub fn builder(kin: &'k [u8]) -> ParamsBuilder<'k, 'l, 'c> {
70        let params = Params {
71            kin,
72            label: &[],
73            context: &[],
74            use_l: true,
75            use_separator: true,
76            use_counter: true,
77        };
78        ParamsBuilder(params)
79    }
80}
81
82/// Parameters builders for [`Params`].
83pub struct ParamsBuilder<'k, 'l, 'c>(Params<'k, 'l, 'c>);
84
85impl<'k, 'l, 'c> ParamsBuilder<'k, 'l, 'c> {
86    /// Return the built [`Params`]
87    pub fn build(self) -> Params<'k, 'l, 'c> {
88        self.0
89    }
90
91    /// Set the label for the parameters
92    pub fn with_label(mut self, label: &'l [u8]) -> Self {
93        self.0.label = label;
94        self
95    }
96
97    /// Set the context for the parameters
98    pub fn with_context(mut self, context: &'c [u8]) -> Self {
99        self.0.context = context;
100        self
101    }
102
103    /// During the iterations, append the length of the Prf
104    pub fn use_l(mut self, use_l: bool) -> Self {
105        self.0.use_l = use_l;
106        self
107    }
108
109    /// During the iterations, separate the label from the context with a NULL byte
110    pub fn use_separator(mut self, use_separator: bool) -> Self {
111        self.0.use_separator = use_separator;
112        self
113    }
114
115    /// During the iterations, update the Prf with the iteration counter
116    pub fn use_counter(mut self, use_counter: bool) -> Self {
117        self.0.use_counter = use_counter;
118        self
119    }
120}
121
122// Helper structure along with [`KbkdfUser`] to compute values of L and H.
123struct KbkdfCore<OutputLen, PrfOutputLen> {
124    _marker: PhantomData<(OutputLen, PrfOutputLen)>,
125}
126
127trait KbkdfUser {
128    // L - An integer specifying the requested length (in bits) of the derived keying material
129    // KOUT. L is represented as a bit string when it is an input to a key-derivation function. The
130    // length of the bit string is specified by the encoding method for the input data.
131    type L;
132
133    // h - An integer that indicates the length (in bits) of the output of a single invocation of the
134    // PRF.
135    type H;
136}
137
138impl<OutputLen, PrfOutputLen> KbkdfUser for KbkdfCore<OutputLen, PrfOutputLen>
139where
140    OutputLen: ArraySize + Mul<U8>,
141    <OutputLen as Mul<U8>>::Output: Unsigned,
142    PrfOutputLen: ArraySize + Mul<U8>,
143    <PrfOutputLen as Mul<U8>>::Output: Unsigned,
144{
145    type L = op!(OutputLen * U8);
146    type H = op!(PrfOutputLen * U8);
147}
148
149/// [`Kbkdf`] is a trait representing a mode of KBKDF.
150/// It takes multiple arguments:
151///  - Prf - the Pseudorandom Function to derive keys from
152///  - K - the expected output length of the newly derived key
153///  - R - An integer (1 <= r <= 32) that indicates the length of the binary encoding of the counter i
154///    as an integer in the interval [1, 2r − 1].
155pub trait Kbkdf<Prf, K, R: sealed::R>
156where
157    Prf: Mac + KeyInit,
158    K: KeySizeUser,
159    K::KeySize: ArraySize + Mul<U8>,
160    <K::KeySize as Mul<U8>>::Output: Unsigned,
161    Prf::OutputSize: ArraySize + Mul<U8>,
162    <Prf::OutputSize as Mul<U8>>::Output: Unsigned,
163{
164    /// Derives `key` from `kin` and other parameters.
165    fn derive(&self, params: Params<'_, '_, '_>) -> Result<Array<u8, K::KeySize>, Error> {
166        // n - An integer whose value is the number of iterations of the PRF needed to generate L
167        // bits of keying material
168        let n: u32 = <KbkdfCore<K::KeySize, Prf::OutputSize> as KbkdfUser>::L::U32
169            .div_ceil(<KbkdfCore<K::KeySize, Prf::OutputSize> as KbkdfUser>::H::U32);
170
171        if n as usize > 2usize.pow(R::U32) - 1 {
172            return Err(Error::InvalidRequestSize);
173        }
174
175        let mut output = Array::<u8, K::KeySize>::default();
176        let mut builder = output.as_mut_slice();
177
178        let mut ki = None;
179        self.input_iv(&mut ki);
180        let mut a = {
181            let mut h = Prf::new_from_slice(params.kin).unwrap();
182            h.update(params.label);
183            if params.use_separator {
184                h.update(&[0]);
185            }
186            h.update(params.context);
187            h.finalize().into_bytes()
188        };
189
190        for counter in 1..=n {
191            if counter > 1 {
192                a = {
193                    let mut h = Prf::new_from_slice(params.kin).unwrap();
194                    h.update(a.as_slice());
195                    h.finalize().into_bytes()
196                };
197            }
198
199            let mut h = Prf::new_from_slice(params.kin).unwrap();
200
201            if Self::FEEDBACK_KI {
202                if let Some(ki) = ki {
203                    h.update(ki.as_slice());
204                }
205            }
206
207            if Self::DOUBLE_PIPELINE {
208                h.update(a.as_slice());
209            }
210            if params.use_counter {
211                // counter encoded as big endian u32
212                // Type parameter R encodes how large the value is to be (either U8, U16, U24, or U32)
213                //
214                // counter = 1u32 ([0, 0, 0, 1])
215                //                     \-------/
216                //                      R = u24
217                h.update(&counter.to_be_bytes()[(4 - R::USIZE / 8)..]);
218            }
219
220            // Fixed input data
221            h.update(params.label);
222            if params.use_separator {
223                h.update(&[0]);
224            }
225            h.update(params.context);
226            if params.use_l {
227                h.update(
228                    &(<KbkdfCore<K::KeySize, Prf::OutputSize> as KbkdfUser>::L::U32).to_be_bytes()
229                        [..],
230                );
231            }
232
233            let buf = h.finalize().into_bytes();
234            ki = Some(buf.clone());
235
236            let remaining = usize::min(buf.len(), builder.len());
237
238            builder[..remaining].copy_from_slice(&buf[..remaining]);
239            builder = &mut builder[remaining..];
240        }
241
242        assert_eq!(builder.len(), 0, "output has uninitialized bytes");
243
244        Ok(output)
245    }
246
247    /// Input the IV in the PRF.
248    fn input_iv(&self, _ki: &mut Option<Array<u8, Prf::OutputSize>>) {}
249
250    /// Whether the KI should be reinjected every round.
251    ///
252    /// Or, in other words, whether the KBKDF is in Feedback Mode.
253    const FEEDBACK_KI: bool = false;
254
255    /// Whether the KBKDF is in Double-Pipeline Mode.
256    const DOUBLE_PIPELINE: bool = false;
257}
258
259/// KBKDF in Counter Mode.
260pub struct Counter<Prf, K, R = U32> {
261    _marker: PhantomData<(Prf, K, R)>,
262}
263
264impl<Prf, K, R> Default for Counter<Prf, K, R> {
265    fn default() -> Self {
266        Self {
267            _marker: PhantomData,
268        }
269    }
270}
271
272impl<Prf, K, R> Kbkdf<Prf, K, R> for Counter<Prf, K, R>
273where
274    Prf: Mac + KeyInit,
275    K: KeySizeUser,
276    K::KeySize: ArraySize + Mul<U8>,
277    <K::KeySize as Mul<U8>>::Output: Unsigned,
278    Prf::OutputSize: ArraySize + Mul<U8>,
279    <Prf::OutputSize as Mul<U8>>::Output: Unsigned,
280    R: sealed::R,
281{
282}
283
284/// KBKDF in Feedback Mode.
285pub struct Feedback<'a, Prf, K, R = U32>
286where
287    Prf: Mac,
288{
289    iv: Option<&'a Array<u8, Prf::OutputSize>>,
290    _marker: PhantomData<(Prf, K, R)>,
291}
292
293impl<'a, Prf, K, R> Feedback<'a, Prf, K, R>
294where
295    Prf: Mac,
296{
297    /// Creates a new [`Feedback`] instance with an optional IV.
298    pub fn new(iv: Option<&'a Array<u8, Prf::OutputSize>>) -> Self {
299        Self {
300            iv,
301            _marker: PhantomData,
302        }
303    }
304}
305
306impl<Prf, K, R> Kbkdf<Prf, K, R> for Feedback<'_, Prf, K, R>
307where
308    Prf: Mac + KeyInit,
309    K: KeySizeUser,
310    K::KeySize: ArraySize + Mul<U8>,
311    <K::KeySize as Mul<U8>>::Output: Unsigned,
312    Prf::OutputSize: ArraySize + Mul<U8>,
313    <Prf::OutputSize as Mul<U8>>::Output: Unsigned,
314    R: sealed::R,
315{
316    fn input_iv(&self, ki: &mut Option<Array<u8, Prf::OutputSize>>) {
317        if let Some(iv) = self.iv {
318            *ki = Some(iv.clone())
319        }
320    }
321
322    const FEEDBACK_KI: bool = true;
323}
324
325/// KBKDF in Double-Pipeline Mode.
326pub struct DoublePipeline<Prf, K, R = U32>
327where
328    Prf: Mac,
329{
330    _marker: PhantomData<(Prf, K, R)>,
331}
332
333impl<Prf, K, R> Default for DoublePipeline<Prf, K, R>
334where
335    Prf: Mac,
336{
337    fn default() -> Self {
338        Self {
339            _marker: PhantomData,
340        }
341    }
342}
343
344impl<Prf, K, R> Kbkdf<Prf, K, R> for DoublePipeline<Prf, K, R>
345where
346    Prf: Mac + KeyInit,
347    K: KeySizeUser,
348    K::KeySize: ArraySize + Mul<U8>,
349    <K::KeySize as Mul<U8>>::Output: Unsigned,
350    Prf::OutputSize: ArraySize + Mul<U8>,
351    <Prf::OutputSize as Mul<U8>>::Output: Unsigned,
352    R: sealed::R,
353{
354    const DOUBLE_PIPELINE: bool = true;
355}
356
357#[cfg(test)]
358mod tests;