1#![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#[derive(Clone, Copy, Debug, PartialEq)]
30pub enum Error {
31 NoSecret,
33 NoOutput,
35 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
53pub 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 const OVERFLOW_IS_POSSIBLE: bool = core::mem::size_of::<usize>() > 4;
72
73 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#[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}