Skip to main content

one_step_kdf/
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;
12use digest::{Digest, FixedOutputReset, Update, array::typenum::Unsigned};
13
14/// Derives `key` in-place from `secret` and `other_info`.
15///
16/// # Example
17/// ```rust
18/// use hex_literal::hex;
19/// use sha2::Sha256;
20///
21/// let mut key = [0u8; 16];
22/// one_step_kdf::derive_key_into::<Sha256>(b"secret", b"shared-info", &mut key).unwrap();
23/// assert_eq!(key, hex!("960db2c549ab16d71a7b008e005c2bdc"));
24/// ```
25pub fn derive_key_into<D>(secret: &[u8], other_info: &[u8], key: &mut [u8]) -> Result<(), Error>
26where
27    D: Digest + FixedOutputReset,
28{
29    if secret.is_empty() {
30        return Err(Error::NoSecret);
31    }
32
33    if key.is_empty() {
34        return Err(Error::NoOutput);
35    }
36
37    // Key length shall be less than or equal to hash output length * (2^32 - 1).
38    if (key.len() as u64) >= D::OutputSize::U64 * (u32::MAX as u64) {
39        return Err(Error::CounterOverflow);
40    }
41
42    let mut digest = D::new();
43    let mut counter: u32 = 1;
44
45    for chunk in key.chunks_mut(D::OutputSize::USIZE) {
46        Update::update(&mut digest, &counter.to_be_bytes());
47        Update::update(&mut digest, secret);
48        Update::update(&mut digest, other_info);
49        chunk.copy_from_slice(&digest.finalize_reset()[..chunk.len()]);
50        counter += 1;
51    }
52
53    Ok(())
54}
55
56/// One-Step KDF errors.
57#[derive(Clone, Copy, Debug, PartialEq)]
58pub enum Error {
59    /// The length of the secret is zero.
60    NoSecret,
61    /// The length of the output is zero.
62    NoOutput,
63    /// The length of the output is too big.
64    CounterOverflow,
65}
66
67impl fmt::Display for Error {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
69        f.write_str(match self {
70            Error::NoSecret => "Buffer for secret has zero length.",
71            Error::NoOutput => "Buffer for key has zero length.",
72            Error::CounterOverflow => "Requested key length is to big.",
73        })
74    }
75}
76
77impl core::error::Error for Error {}