1use crypto_core::{CryptoError, KdfAlgorithm, KdfFailureKind, KdfProfile};
6use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
7
8use crate::constants::{
9 PBKDF2_MAX_ITERATIONS, PBKDF2_MAX_PASSWORD_LENGTH, PBKDF2_MAX_SALT_LENGTH,
10 PBKDF2_MIN_PASSWORD_LENGTH, PBKDF2_MIN_SALT_LENGTH, PBKDF2_MODERN_MIN_ITERATIONS,
11 PBKDF2_STANDARD_MIN_ITERATIONS,
12};
13#[cfg(any(feature = "native", feature = "wasm"))]
14use crate::constants::{PBKDF2_MAX_OUTPUT_LENGTH, PBKDF2_MIN_OUTPUT_LENGTH};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum Pbkdf2Prf {
19 HmacSha256,
21 HmacSha512,
23}
24
25impl Pbkdf2Prf {
26 pub(crate) fn profile(self) -> KdfProfile {
27 match self {
28 Pbkdf2Prf::HmacSha256 => KdfProfile::Pbkdf2HmacSha256,
29 Pbkdf2Prf::HmacSha512 => KdfProfile::Pbkdf2HmacSha512,
30 }
31 }
32}
33
34#[derive(Zeroize, ZeroizeOnDrop)]
36pub struct Pbkdf2Password {
37 bytes: Zeroizing<Vec<u8>>,
38}
39
40impl Pbkdf2Password {
41 pub fn from_slice(input: &[u8], prf: Pbkdf2Prf) -> Result<Self, CryptoError> {
43 if !(PBKDF2_MIN_PASSWORD_LENGTH..=PBKDF2_MAX_PASSWORD_LENGTH).contains(&input.len()) {
44 return Err(kdf_error(prf, KdfFailureKind::InvalidSecretLength));
45 }
46
47 Ok(Self {
48 bytes: Zeroizing::new(input.to_vec()),
49 })
50 }
51
52 pub fn as_bytes(&self) -> &[u8] {
54 &self.bytes
55 }
56}
57
58#[derive(Zeroize, ZeroizeOnDrop)]
60pub struct Pbkdf2Salt {
61 bytes: Zeroizing<Vec<u8>>,
62}
63
64impl Pbkdf2Salt {
65 pub fn from_slice(input: &[u8], prf: Pbkdf2Prf) -> Result<Self, CryptoError> {
67 if !(PBKDF2_MIN_SALT_LENGTH..=PBKDF2_MAX_SALT_LENGTH).contains(&input.len()) {
68 return Err(kdf_error(prf, KdfFailureKind::InvalidSaltLength));
69 }
70
71 Ok(Self {
72 bytes: Zeroizing::new(input.to_vec()),
73 })
74 }
75
76 pub fn as_bytes(&self) -> &[u8] {
78 &self.bytes
79 }
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
84pub struct Pbkdf2Iterations {
85 value: u32,
86}
87
88impl Pbkdf2Iterations {
89 pub fn from_u32(value: u32, prf: Pbkdf2Prf) -> Result<Self, CryptoError> {
91 if !(PBKDF2_STANDARD_MIN_ITERATIONS..=PBKDF2_MAX_ITERATIONS).contains(&value) {
92 return Err(kdf_error(prf, KdfFailureKind::InvalidIterationCount));
93 }
94 Ok(Self { value })
95 }
96
97 pub fn from_u32_modern(value: u32, prf: Pbkdf2Prf) -> Result<Self, CryptoError> {
103 if !(PBKDF2_MODERN_MIN_ITERATIONS..=PBKDF2_MAX_ITERATIONS).contains(&value) {
104 return Err(kdf_error(prf, KdfFailureKind::InvalidIterationCount));
105 }
106 Ok(Self { value })
107 }
108
109 pub fn as_u32(self) -> u32 {
111 self.value
112 }
113}
114
115pub struct Pbkdf2Output {
117 bytes: Zeroizing<Vec<u8>>,
118}
119
120impl Zeroize for Pbkdf2Output {
121 fn zeroize(&mut self) {
122 self.bytes.zeroize();
123 }
124}
125
126impl ZeroizeOnDrop for Pbkdf2Output {}
129
130impl Pbkdf2Output {
131 #[cfg(any(feature = "native", feature = "wasm"))]
132 pub(crate) fn from_zeroizing(bytes: Zeroizing<Vec<u8>>) -> Self {
133 Self { bytes }
134 }
135
136 pub fn as_bytes(&self) -> &[u8] {
138 &self.bytes
139 }
140
141 pub fn len(&self) -> usize {
143 self.bytes.len()
144 }
145
146 pub fn is_empty(&self) -> bool {
148 self.bytes.is_empty()
149 }
150
151 pub fn into_zeroizing(self) -> Zeroizing<Vec<u8>> {
153 self.bytes
154 }
155}
156
157impl core::fmt::Debug for Pbkdf2Output {
158 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
159 write!(f, "Pbkdf2Output(len={})", self.len())
160 }
161}
162
163#[cfg(any(feature = "native", feature = "wasm"))]
164pub(crate) fn validate_output_len(len: usize, prf: Pbkdf2Prf) -> Result<(), CryptoError> {
165 if !(PBKDF2_MIN_OUTPUT_LENGTH..=PBKDF2_MAX_OUTPUT_LENGTH).contains(&len) {
166 return Err(kdf_error(prf, KdfFailureKind::InvalidOutputLength));
167 }
168 Ok(())
169}
170
171pub(crate) fn kdf_error(prf: Pbkdf2Prf, kind: KdfFailureKind) -> CryptoError {
172 CryptoError::Kdf {
173 algorithm: KdfAlgorithm::Pbkdf2,
174 profile: prf.profile(),
175 kind,
176 }
177}