ansi_x963_kdf/
lib.rs

1#![no_std]
2#![doc = include_str!("../README.md")]
3
4use core::fmt;
5use digest::{array::typenum::Unsigned, Digest, FixedOutputReset};
6
7/// Derives `key` in-place from `secret` and `shared_info`.
8///
9/// # Example
10/// ```
11/// use hex_literal::hex;
12/// use sha2::Sha256;
13///
14/// let mut key = [0u8; 16];
15/// ansi_x963_kdf::derive_key_into::<Sha256>(b"secret", b"shared-info", &mut key).unwrap();
16/// assert_eq!(key, hex!("8dbb1d50bcc7fc782abc9db5c64a2826"));
17/// ```
18#[inline]
19pub fn derive_key_into<D>(secret: &[u8], shared_info: &[u8], key: &mut [u8]) -> Result<(), Error>
20where
21    D: Digest + FixedOutputReset,
22{
23    if secret.is_empty() {
24        return Err(Error::NoSecret);
25    }
26
27    if key.is_empty() {
28        return Err(Error::NoOutput);
29    }
30
31    // 1. Check that |Z| + |SharedInfo| + 4 < hashmaxlen
32    // where "hashmaxlen denote the maximum length in octets of messages that can be hashed using Hash".
33    // N.B.: `D::OutputSize::U64 * (u32::MAX as u64)`` is currently used as an approximation of hashmaxlen.
34    if secret.len() as u64 + shared_info.len() as u64 + 4 >= D::OutputSize::U64 * (u32::MAX as u64)
35    {
36        return Err(Error::InputOverflow);
37    }
38
39    // 2. Check that keydatalen < hashlen × (2^32 − 1)
40    if key.len() as u64 >= D::OutputSize::U64 * (u32::MAX as u64) {
41        return Err(Error::CounterOverflow);
42    }
43
44    let mut digest = D::new();
45
46    // 3. Initiate a 4 octet, big-endian octet string Counter as 00000001
47    let mut counter: u32 = 1;
48
49    // 4. For i = 1 to keydatalen/hashlen,
50    for chunk in key.chunks_mut(D::OutputSize::USIZE) {
51        // 4.1 Compute Ki = Hash(Z ‖ Counter ‖ [SharedInfo]) using the selected hash function
52        Digest::update(&mut digest, secret);
53        Digest::update(&mut digest, counter.to_be_bytes());
54        Digest::update(&mut digest, shared_info);
55        chunk.copy_from_slice(&digest.finalize_reset()[..chunk.len()]);
56        // 4.2. Increment Counter
57        counter += 1;
58    }
59
60    Ok(())
61}
62
63/// ANSI-X9.63 KDF errors.
64#[derive(Clone, Copy, Debug, PartialEq)]
65pub enum Error {
66    /// The length of the secret is zero.
67    NoSecret,
68    /// The length of the output is zero.
69    NoOutput,
70    /// The length of the input is too big
71    InputOverflow,
72    /// The length of the output is too big.
73    CounterOverflow,
74}
75
76impl fmt::Display for Error {
77    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
78        f.write_str(match self {
79            Error::NoSecret => "Buffer for secret has zero length.",
80            Error::NoOutput => "Buffer for key has zero length.",
81            Error::InputOverflow => "Input length is to big.",
82            Error::CounterOverflow => "Requested key length is to big.",
83        })
84    }
85}
86
87impl core::error::Error for Error {}