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
14pub 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 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#[derive(Clone, Copy, Debug, PartialEq)]
58pub enum Error {
59 NoSecret,
61 NoOutput,
63 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 {}