1use std::pin::Pin;
2
3use hmac::digest::OutputSizeUser;
4use hybrid_array::{Array, ArraySize};
5use rand::{
6 RngExt,
7 distr::{Alphanumeric, Distribution, SampleString, StandardUniform},
8};
9use typenum::Unsigned;
10use zeroize::{Zeroize, Zeroizing};
11
12use crate::Result;
13
14pub(crate) type PbkdfSha256Hmac = hmac::Hmac<sha2::Sha256>;
15
16const CLEARED_STACK_SIZE: usize = 16 * 1024;
18
19#[inline(never)]
25pub(crate) fn clear_stack() {
26 std::hint::black_box([0u8; CLEARED_STACK_SIZE]);
27}
28pub(crate) const PBKDF_SHA256_HMAC_OUT_SIZE: usize =
29 <<PbkdfSha256Hmac as OutputSizeUser>::OutputSize as Unsigned>::USIZE;
30
31#[derive(Debug)]
32pub(crate) enum HkdfExpandError {
33 InvalidInputLegth,
34 InvalidOutputLength,
35}
36
37pub(crate) fn hkdf_expand<T: ArraySize>(
39 prk: &[u8],
40 info: Option<&str>,
41) -> Result<Pin<Box<Array<u8, T>>>, HkdfExpandError> {
42 let hkdf = hkdf::Hkdf::<sha2::Sha256>::from_prk(prk)
43 .map_err(|_| HkdfExpandError::InvalidInputLegth)?;
44 let mut key = Box::<Array<u8, T>>::default();
45
46 let i = info.map(|i| i.as_bytes()).unwrap_or(&[]);
47 hkdf.expand(i, key.as_mut_slice())
48 .map_err(|_| HkdfExpandError::InvalidOutputLength)?;
49
50 Ok(Box::into_pin(key))
51}
52
53pub fn generate_random_bytes<T>() -> Zeroizing<T>
55where
56 StandardUniform: Distribution<T>,
57 T: Zeroize,
58{
59 Zeroizing::new(bitwarden_random::rng().random::<T>())
60}
61
62pub fn generate_random_alphanumeric(len: usize) -> String {
67 Alphanumeric.sample_string(&mut bitwarden_random::rng(), len)
68}
69
70pub fn pbkdf2(password: &[u8], salt: &[u8], rounds: u32) -> [u8; PBKDF_SHA256_HMAC_OUT_SIZE] {
72 pbkdf2::pbkdf2_array::<PbkdfSha256Hmac, PBKDF_SHA256_HMAC_OUT_SIZE>(password, salt, rounds)
73 .expect("hash is a valid fixed size")
74}
75
76#[cfg(test)]
77mod tests {
78 use typenum::U64;
79
80 use super::*;
81
82 #[test]
83 fn test_hkdf_expand() {
84 let prk = &[
85 23, 152, 120, 41, 214, 16, 156, 133, 71, 226, 178, 135, 208, 255, 66, 101, 189, 70,
86 173, 30, 39, 215, 175, 236, 38, 180, 180, 62, 196, 4, 159, 70,
87 ];
88 let info = Some("info");
89
90 let result: Pin<Box<Array<u8, U64>>> = hkdf_expand(prk, info).unwrap();
91
92 let expected_output: [u8; 64] = [
93 6, 114, 42, 38, 87, 231, 30, 109, 30, 255, 104, 129, 255, 94, 92, 108, 124, 145, 215,
94 208, 17, 60, 135, 22, 70, 158, 40, 53, 45, 182, 8, 63, 65, 87, 239, 234, 185, 227, 153,
95 122, 115, 205, 144, 56, 102, 149, 92, 139, 217, 102, 119, 57, 37, 57, 251, 178, 18, 52,
96 94, 77, 132, 215, 239, 100,
97 ];
98
99 assert_eq!(result.as_slice(), expected_output);
100 }
101
102 #[test]
103 fn test_hkdf_expand_invalid_input_length() {
104 let prk = &[1, 2, 3, 4, 5];
106 let info = Some("info");
107
108 let result: Result<Pin<Box<Array<u8, U64>>>, HkdfExpandError> = hkdf_expand(prk, info);
109
110 assert!(matches!(result, Err(HkdfExpandError::InvalidInputLegth)));
111 }
112
113 #[test]
114 fn test_hkdf_expand_invalid_output_length() {
115 let prk = &[
116 23, 152, 120, 41, 214, 16, 156, 133, 71, 226, 178, 135, 208, 255, 66, 101, 189, 70,
117 173, 30, 39, 215, 175, 236, 38, 180, 180, 62, 196, 4, 159, 70,
118 ];
119 let info = Some("info");
120
121 type TooLarge = typenum::U8192;
123 let result: Result<Pin<Box<Array<u8, TooLarge>>>, HkdfExpandError> = hkdf_expand(prk, info);
124
125 assert!(matches!(result, Err(HkdfExpandError::InvalidOutputLength)));
126 }
127}