1use std::{pin::Pin, str::FromStr};
2
3use ::aes::cipher::{ArrayLength, Unsigned};
4use generic_array::GenericArray;
5use hmac::digest::OutputSizeUser;
6use rand::{
7 distributions::{Alphanumeric, DistString, Distribution, Standard},
8 Rng,
9};
10use zeroize::{Zeroize, Zeroizing};
11
12use crate::{CryptoError, Result};
13
14pub(crate) type PbkdfSha256Hmac = hmac::Hmac<sha2::Sha256>;
15pub(crate) const PBKDF_SHA256_HMAC_OUT_SIZE: usize =
16 <<PbkdfSha256Hmac as OutputSizeUser>::OutputSize as Unsigned>::USIZE;
17
18pub(crate) fn hkdf_expand<T: ArrayLength<u8>>(
20 prk: &[u8],
21 info: Option<&str>,
22) -> Result<Pin<Box<GenericArray<u8, T>>>> {
23 let hkdf = hkdf::Hkdf::<sha2::Sha256>::from_prk(prk).map_err(|_| CryptoError::InvalidKeyLen)?;
24 let mut key = Box::<GenericArray<u8, T>>::default();
25
26 let i = info.map(|i| i.as_bytes()).unwrap_or(&[]);
27 hkdf.expand(i, &mut key)
28 .map_err(|_| CryptoError::InvalidKeyLen)?;
29
30 Ok(Box::into_pin(key))
31}
32
33pub fn generate_random_bytes<T>() -> Zeroizing<T>
35where
36 Standard: Distribution<T>,
37 T: Zeroize,
38{
39 Zeroizing::new(rand::thread_rng().r#gen::<T>())
40}
41
42pub fn generate_random_alphanumeric(len: usize) -> String {
47 Alphanumeric.sample_string(&mut rand::thread_rng(), len)
48}
49
50pub fn pbkdf2(password: &[u8], salt: &[u8], rounds: u32) -> [u8; PBKDF_SHA256_HMAC_OUT_SIZE] {
52 pbkdf2::pbkdf2_array::<PbkdfSha256Hmac, PBKDF_SHA256_HMAC_OUT_SIZE>(password, salt, rounds)
53 .expect("hash is a valid fixed size")
54}
55
56pub(crate) struct FromStrVisitor<T>(std::marker::PhantomData<T>);
57impl<T> FromStrVisitor<T> {
58 pub(crate) fn new() -> Self {
59 Self(Default::default())
60 }
61}
62impl<T: FromStr> serde::de::Visitor<'_> for FromStrVisitor<T>
63where
64 T::Err: std::fmt::Debug,
65{
66 type Value = T;
67
68 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
69 write!(f, "a valid string")
70 }
71
72 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
73 where
74 E: serde::de::Error,
75 {
76 T::from_str(v).map_err(|e| E::custom(format!("{:?}", e)))
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use typenum::U64;
83
84 use super::*;
85
86 #[test]
87 fn test_hkdf_expand() {
88 let prk = &[
89 23, 152, 120, 41, 214, 16, 156, 133, 71, 226, 178, 135, 208, 255, 66, 101, 189, 70,
90 173, 30, 39, 215, 175, 236, 38, 180, 180, 62, 196, 4, 159, 70,
91 ];
92 let info = Some("info");
93
94 let result: Pin<Box<GenericArray<u8, U64>>> = hkdf_expand(prk, info).unwrap();
95
96 let expected_output: [u8; 64] = [
97 6, 114, 42, 38, 87, 231, 30, 109, 30, 255, 104, 129, 255, 94, 92, 108, 124, 145, 215,
98 208, 17, 60, 135, 22, 70, 158, 40, 53, 45, 182, 8, 63, 65, 87, 239, 234, 185, 227, 153,
99 122, 115, 205, 144, 56, 102, 149, 92, 139, 217, 102, 119, 57, 37, 57, 251, 178, 18, 52,
100 94, 77, 132, 215, 239, 100,
101 ];
102
103 assert_eq!(result.as_slice(), expected_output);
104 }
105}