1use crate::{Error, KdfAlg, Result};
6use encoding::{CheckedSum, Decode, Encode, Reader, Writer};
7
8#[cfg(feature = "alloc")]
9use alloc::vec::Vec;
10
11#[cfg(feature = "encryption")]
12use {crate::Cipher, bcrypt_pbkdf::bcrypt_pbkdf, rand_core::TryCryptoRng, zeroize::Zeroizing};
13
14#[cfg(feature = "encryption")]
16const DEFAULT_BCRYPT_ROUNDS: u32 = 16;
17
18#[cfg(feature = "encryption")]
20const DEFAULT_SALT_SIZE: usize = 16;
21
22#[derive(Clone, Debug, Eq, PartialEq)]
24#[non_exhaustive]
25#[derive(Default)]
26pub enum Kdf {
27 #[default]
29 None,
30
31 #[cfg(feature = "alloc")]
33 Bcrypt {
34 salt: Vec<u8>,
36
37 rounds: u32,
39 },
40}
41
42impl Kdf {
43 #[cfg(feature = "encryption")]
48 pub fn new<R: TryCryptoRng + ?Sized>(algorithm: KdfAlg, rng: &mut R) -> Result<Self> {
49 let mut salt = vec![0u8; DEFAULT_SALT_SIZE];
50 rng.try_fill_bytes(&mut salt)
51 .map_err(|_| Error::RngFailure)?;
52
53 match algorithm {
54 KdfAlg::None => {
55 Err(Error::AlgorithmUnknown)
57 }
58 KdfAlg::Bcrypt => Ok(Kdf::Bcrypt {
59 salt,
60 rounds: DEFAULT_BCRYPT_ROUNDS,
61 }),
62 }
63 }
64
65 #[must_use]
67 pub fn algorithm(&self) -> KdfAlg {
68 match self {
69 Self::None => KdfAlg::None,
70 #[cfg(feature = "alloc")]
71 Self::Bcrypt { .. } => KdfAlg::Bcrypt,
72 }
73 }
74
75 #[cfg(feature = "encryption")]
81 pub fn derive(&self, password: impl AsRef<[u8]>, output: &mut [u8]) -> Result<()> {
82 match self {
83 Kdf::None => Err(Error::Decrypted),
84 Kdf::Bcrypt { salt, rounds } => {
85 bcrypt_pbkdf(password, salt, *rounds, output).map_err(|_| Error::Crypto)?;
86 Ok(())
87 }
88 }
89 }
90
91 #[cfg(feature = "encryption")]
99 pub fn derive_key_and_iv(
100 &self,
101 cipher: Cipher,
102 password: impl AsRef<[u8]>,
103 ) -> Result<(Zeroizing<Vec<u8>>, Vec<u8>)> {
104 let (key_size, iv_size) = cipher.key_and_iv_size().ok_or(Error::Decrypted)?;
105
106 let okm_size = key_size
107 .checked_add(iv_size)
108 .ok_or(encoding::Error::Length)?;
109
110 let mut okm = Zeroizing::new(vec![0u8; okm_size]);
111 self.derive(password, &mut okm)?;
112 let mut iv = okm.split_off(key_size);
113
114 if cipher == Cipher::ChaCha20Poly1305 {
118 iv.copy_from_slice(&cipher::ChaChaNonce::default());
119 }
120
121 Ok((okm, iv))
122 }
123
124 #[must_use]
126 pub fn is_none(&self) -> bool {
127 self == &Self::None
128 }
129
130 #[must_use]
132 pub fn is_some(&self) -> bool {
133 !self.is_none()
134 }
135
136 #[cfg(feature = "alloc")]
138 #[must_use]
139 pub fn is_bcrypt(&self) -> bool {
140 matches!(self, Self::Bcrypt { .. })
141 }
142}
143
144impl Decode for Kdf {
145 type Error = Error;
146
147 fn decode(reader: &mut impl Reader) -> Result<Self> {
148 match KdfAlg::decode(reader)? {
149 KdfAlg::None => {
150 if usize::decode(reader)? == 0 {
151 Ok(Self::None)
152 } else {
153 Err(Error::AlgorithmUnknown)
154 }
155 }
156 KdfAlg::Bcrypt => {
157 #[cfg(not(feature = "alloc"))]
158 return Err(Error::AlgorithmUnknown);
159
160 #[cfg(feature = "alloc")]
161 reader.read_prefixed(|reader| {
162 Ok(Self::Bcrypt {
163 salt: Vec::decode(reader)?,
164 rounds: u32::decode(reader)?,
165 })
166 })
167 }
168 }
169 }
170}
171
172impl Encode for Kdf {
173 fn encoded_len(&self) -> encoding::Result<usize> {
174 let kdfopts_prefixed_len = match self {
175 Self::None => 4,
176 #[cfg(feature = "alloc")]
177 Self::Bcrypt { salt, .. } => [12, salt.len()].checked_sum()?,
178 };
179
180 [self.algorithm().encoded_len()?, kdfopts_prefixed_len].checked_sum()
181 }
182
183 fn encode(&self, writer: &mut impl Writer) -> encoding::Result<()> {
184 self.algorithm().encode(writer)?;
185
186 match self {
187 Self::None => 0usize.encode(writer)?,
188 #[cfg(feature = "alloc")]
189 Self::Bcrypt { salt, rounds } => {
190 [8, salt.len()].checked_sum()?.encode(writer)?;
191 salt.encode(writer)?;
192 rounds.encode(writer)?;
193 }
194 }
195
196 Ok(())
197 }
198}