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]
25pub enum Kdf {
26 None,
28
29 #[cfg(feature = "alloc")]
31 Bcrypt {
32 salt: Vec<u8>,
34
35 rounds: u32,
37 },
38}
39
40impl Kdf {
41 #[cfg(feature = "encryption")]
43 pub fn new<R: TryCryptoRng + ?Sized>(algorithm: KdfAlg, rng: &mut R) -> Result<Self> {
44 let mut salt = vec![0u8; DEFAULT_SALT_SIZE];
45 rng.try_fill_bytes(&mut salt)
46 .map_err(|_| Error::RngFailure)?;
47
48 match algorithm {
49 KdfAlg::None => {
50 Err(Error::AlgorithmUnknown)
52 }
53 KdfAlg::Bcrypt => Ok(Kdf::Bcrypt {
54 salt,
55 rounds: DEFAULT_BCRYPT_ROUNDS,
56 }),
57 }
58 }
59
60 pub fn algorithm(&self) -> KdfAlg {
62 match self {
63 Self::None => KdfAlg::None,
64 #[cfg(feature = "alloc")]
65 Self::Bcrypt { .. } => KdfAlg::Bcrypt,
66 }
67 }
68
69 #[cfg(feature = "encryption")]
71 pub fn derive(&self, password: impl AsRef<[u8]>, output: &mut [u8]) -> Result<()> {
72 match self {
73 Kdf::None => Err(Error::Decrypted),
74 Kdf::Bcrypt { salt, rounds } => {
75 bcrypt_pbkdf(password, salt, *rounds, output).map_err(|_| Error::Crypto)?;
76 Ok(())
77 }
78 }
79 }
80
81 #[cfg(feature = "encryption")]
85 pub fn derive_key_and_iv(
86 &self,
87 cipher: Cipher,
88 password: impl AsRef<[u8]>,
89 ) -> Result<(Zeroizing<Vec<u8>>, Vec<u8>)> {
90 let (key_size, iv_size) = cipher.key_and_iv_size().ok_or(Error::Decrypted)?;
91
92 let okm_size = key_size
93 .checked_add(iv_size)
94 .ok_or(encoding::Error::Length)?;
95
96 let mut okm = Zeroizing::new(vec![0u8; okm_size]);
97 self.derive(password, &mut okm)?;
98 let mut iv = okm.split_off(key_size);
99
100 if cipher == Cipher::ChaCha20Poly1305 {
104 iv.copy_from_slice(&cipher::ChaChaNonce::default());
105 }
106
107 Ok((okm, iv))
108 }
109
110 pub fn is_none(&self) -> bool {
112 self == &Self::None
113 }
114
115 pub fn is_some(&self) -> bool {
117 !self.is_none()
118 }
119
120 #[cfg(feature = "alloc")]
122 pub fn is_bcrypt(&self) -> bool {
123 matches!(self, Self::Bcrypt { .. })
124 }
125}
126
127impl Default for Kdf {
128 fn default() -> Self {
129 Self::None
130 }
131}
132
133impl Decode for Kdf {
134 type Error = Error;
135
136 fn decode(reader: &mut impl Reader) -> Result<Self> {
137 match KdfAlg::decode(reader)? {
138 KdfAlg::None => {
139 if usize::decode(reader)? == 0 {
140 Ok(Self::None)
141 } else {
142 Err(Error::AlgorithmUnknown)
143 }
144 }
145 KdfAlg::Bcrypt => {
146 #[cfg(not(feature = "alloc"))]
147 return Err(Error::AlgorithmUnknown);
148
149 #[cfg(feature = "alloc")]
150 reader.read_prefixed(|reader| {
151 Ok(Self::Bcrypt {
152 salt: Vec::decode(reader)?,
153 rounds: u32::decode(reader)?,
154 })
155 })
156 }
157 }
158 }
159}
160
161impl Encode for Kdf {
162 fn encoded_len(&self) -> encoding::Result<usize> {
163 let kdfopts_prefixed_len = match self {
164 Self::None => 4,
165 #[cfg(feature = "alloc")]
166 Self::Bcrypt { salt, .. } => [12, salt.len()].checked_sum()?,
167 };
168
169 [self.algorithm().encoded_len()?, kdfopts_prefixed_len].checked_sum()
170 }
171
172 fn encode(&self, writer: &mut impl Writer) -> encoding::Result<()> {
173 self.algorithm().encode(writer)?;
174
175 match self {
176 Self::None => 0usize.encode(writer)?,
177 #[cfg(feature = "alloc")]
178 Self::Bcrypt { salt, rounds } => {
179 [8, salt.len()].checked_sum()?.encode(writer)?;
180 salt.encode(writer)?;
181 rounds.encode(writer)?
182 }
183 }
184
185 Ok(())
186 }
187}