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