1#![no_std]
2#![doc = include_str!("../README.md")]
3
4use core::fmt;
5use digest::{array::typenum::Unsigned, Digest, FixedOutputReset};
6
7#[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 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 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 let mut counter: u32 = 1;
48
49 for chunk in key.chunks_mut(D::OutputSize::USIZE) {
51 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 counter += 1;
58 }
59
60 Ok(())
61}
62
63#[derive(Clone, Copy, Debug, PartialEq)]
65pub enum Error {
66 NoSecret,
68 NoOutput,
70 InputOverflow,
72 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 {}