encryption/kdf/
pbkdf2_sha256.rs1use sha2::Sha256;
9
10use crate::contract::{Identified, PasswordBasedKdf};
11use crate::error::{Error, Result};
12
13const MAX_OUTPUT_LEN: usize = u32::MAX as usize;
17
18#[derive(Debug, Clone, Copy)]
20pub struct Pbkdf2Sha256 {
21 iterations: u32,
22}
23
24impl Pbkdf2Sha256 {
25 pub const IDENTIFIER: &'static str = "pbkdf2-sha256";
26
27 pub const DEFAULT_ITERATIONS: u32 = 310_000;
29
30 pub fn new(iterations: u32) -> Result<Self> {
32 if iterations == 0 {
33 return Err(Error::InvalidIterations);
34 }
35 Ok(Self { iterations })
36 }
37
38 pub fn with_default_iterations() -> Self {
40 Self {
41 iterations: Self::DEFAULT_ITERATIONS,
42 }
43 }
44
45 pub fn iterations(&self) -> u32 {
46 self.iterations
47 }
48}
49
50impl Default for Pbkdf2Sha256 {
51 fn default() -> Self {
52 Self::with_default_iterations()
53 }
54}
55
56impl Identified for Pbkdf2Sha256 {
57 fn identifier(&self) -> &str {
58 Self::IDENTIFIER
59 }
60}
61
62impl PasswordBasedKdf for Pbkdf2Sha256 {
63 fn derive_from_password(&self, password: &[u8], salt: &[u8], length: usize) -> Result<Vec<u8>> {
64 if length == 0 || length > MAX_OUTPUT_LEN {
65 return Err(Error::InvalidOutputLength {
66 requested: length,
67 max: MAX_OUTPUT_LEN,
68 });
69 }
70
71 let mut output = vec![0u8; length];
72 pbkdf2::pbkdf2_hmac::<Sha256>(password, salt, self.iterations, &mut output);
73 Ok(output)
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
83 fn matches_known_vector() {
84 let kdf = Pbkdf2Sha256::new(1).unwrap();
85 let derived = kdf.derive_from_password(b"password", b"salt", 32).unwrap();
86 assert_eq!(
87 crate::internal::hex_encode(&derived),
88 "120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b"
89 );
90 }
91
92 #[test]
93 fn same_inputs_same_output_and_length_validation() {
94 let kdf = Pbkdf2Sha256::with_default_iterations();
95 assert_eq!(kdf.iterations(), 310_000);
96 let first = kdf.derive_from_password(b"pw", b"salt", 16).unwrap();
97 let second = kdf.derive_from_password(b"pw", b"salt", 16).unwrap();
98 assert_eq!(first, second);
99 assert_eq!(
100 kdf.derive_from_password(b"pw", b"salt", 0).unwrap_err(),
101 Error::InvalidOutputLength {
102 requested: 0,
103 max: MAX_OUTPUT_LEN
104 }
105 );
106 assert_eq!(
109 kdf.derive_from_password(b"pw", b"salt", usize::MAX / 2)
110 .unwrap_err(),
111 Error::InvalidOutputLength {
112 requested: usize::MAX / 2,
113 max: MAX_OUTPUT_LEN
114 }
115 );
116 assert_eq!(
118 kdf.derive_from_password(b"pw", b"salt", MAX_OUTPUT_LEN + 1)
119 .unwrap_err(),
120 Error::InvalidOutputLength {
121 requested: MAX_OUTPUT_LEN + 1,
122 max: MAX_OUTPUT_LEN
123 }
124 );
125 }
126
127 #[test]
128 fn zero_iterations_is_rejected() {
129 assert_eq!(Pbkdf2Sha256::new(0).unwrap_err(), Error::InvalidIterations);
130 }
131}