concat_kdf/
lib.rs

1//! An implementation of Concat KDF, the Concatenation Key Derivation Function.
2//!
3//! This function is described in the section 5.8.1 of [NIST SP 800-56A, Recommendation
4//! for Pair-Wise Key Establishment Schemes Using Discrete Logarithm Cryptography][1].
5//!
6//! # Usage
7//!
8//! The most common way to use Concat KDF is as follows: you generate a shared secret
9//! with other party (e.g. via Diffie-Hellman algorithm) and use key derivation function
10//! to derive a shared key.
11//!
12//! ```rust
13//! let mut key = [0u8; 32];
14//! concat_kdf::derive_key_into::<sha2::Sha256>(b"shared-secret", b"other-info", &mut key).unwrap();
15//! ```
16//!
17//! [1]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-56ar.pdf
18
19#![no_std]
20#![cfg_attr(docsrs, feature(doc_cfg))]
21
22use core::fmt;
23use digest::{generic_array::typenum::Unsigned, Digest, FixedOutputReset, Update};
24
25#[cfg(feature = "std")]
26extern crate std;
27
28/// Concat KDF errors.
29#[derive(Clone, Copy, Debug, PartialEq)]
30pub enum Error {
31    /// The length of the secret is zero.
32    NoSecret,
33    /// The length of the output is zero.
34    NoOutput,
35    /// The length of the output is too big.
36    CounterOverflow,
37}
38
39impl fmt::Display for Error {
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
41        f.write_str(match self {
42            Error::NoSecret => "Buffer for secret has zero length.",
43            Error::NoOutput => "Buffer for key has zero length.",
44            Error::CounterOverflow => "Requested key length is to big.",
45        })
46    }
47}
48
49#[cfg(feature = "std")]
50#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
51impl ::std::error::Error for Error {}
52
53/// Derives `key` in-place from `secret` and `other_info`.
54/// ```rust
55/// let mut key = [0u8; 42];
56/// concat_kdf::derive_key_into::<sha2::Sha256>(b"top-secret", b"info", &mut key).unwrap();
57/// ```
58pub fn derive_key_into<D>(secret: &[u8], other_info: &[u8], key: &mut [u8]) -> Result<(), Error>
59where
60    D: Digest + FixedOutputReset,
61{
62    if secret.is_empty() {
63        return Err(Error::NoSecret);
64    }
65
66    if key.is_empty() {
67        return Err(Error::NoOutput);
68    }
69
70    // Counter overflow is possible only on architectures with usize bigger than 4 bytes.
71    const OVERFLOW_IS_POSSIBLE: bool = core::mem::size_of::<usize>() > 4;
72
73    // Key length shall be less than or equal to hash output length * (2^32 - 1).
74    if OVERFLOW_IS_POSSIBLE && (key.len() >= D::OutputSize::USIZE * (u32::MAX as usize)) {
75        return Err(Error::CounterOverflow);
76    }
77
78    let mut digest = D::new();
79    let mut counter: u32 = 1;
80
81    for chunk in key.chunks_mut(D::OutputSize::USIZE) {
82        Update::update(&mut digest, &counter.to_be_bytes());
83        Update::update(&mut digest, secret);
84        Update::update(&mut digest, other_info);
85        chunk.copy_from_slice(&digest.finalize_reset()[..chunk.len()]);
86        counter += 1;
87    }
88
89    Ok(())
90}
91
92/// Derives and returns `length` bytes key from `secret` and `other_info`.
93/// ```rust
94/// let key = concat_kdf::derive_key::<sha2::Sha256>(b"top-secret", b"info", 42).unwrap();
95/// ```
96#[cfg(feature = "std")]
97#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
98pub fn derive_key<D>(
99    secret: &[u8],
100    other_info: &[u8],
101    length: usize,
102) -> Result<std::vec::Vec<u8>, Error>
103where
104    D: Digest + FixedOutputReset,
105{
106    let mut key = std::vec![0u8; length];
107    derive_key_into::<D>(secret, other_info, &mut key)?;
108    Ok(key)
109}