Skip to content

Commit bf90bb7

Browse files
authored
pbkdf2: add new wrapper functions (#337)
1 parent a630c5b commit bf90bb7

31 files changed

+313
-207
lines changed

Cargo.lock

Lines changed: 20 additions & 21 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bcrypt-pbkdf/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,9 @@ pub fn bcrypt_pbkdf(
152152
&mut stack_buf[..stride * BHASH_OUTPUT_SIZE]
153153
};
154154

155-
// Run the regular PBKDF2 algorithm with bhash as the MAC.
156-
pbkdf2::pbkdf2::<Bhash>(&Sha512::digest(passphrase), salt, rounds, generated);
155+
// Run the regular PBKDF2 algorithm with bhash as the PRF.
156+
pbkdf2::pbkdf2::<Bhash>(&Sha512::digest(passphrase), salt, rounds, generated)
157+
.expect("Bhash can be initialized with any key length");
157158

158159
// Apply the bcrypt_pbkdf non-linear transformation on the output.
159160
for (i, out_byte) in output.iter_mut().enumerate() {

pbkdf2/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## Unreleased
99
### Changed
1010
- `simple` feature is no longer enabled by default ([#336])
11+
- Add new wrapper functions: `pbkdf2_array`, `pbkdf2_hmac`, and
12+
`pbkdf2_hmac_array`. `pbkdf2` and `pbkdf2_array` now return
13+
`Result<(), InvalidLength>` instead of unwrapping it internally. ([#337])
1114

1215
[#336]: https://github.com/RustCrypto/password-hashes/pull/336
16+
[#337]: https://github.com/RustCrypto/password-hashes/pull/337
1317

1418
## 0.11.0 (2022-03-28)
1519
### Changed

pbkdf2/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,13 @@ sha2 = { version = "0.10", default-features = false, optional = true }
2424

2525
[dev-dependencies]
2626
hmac = "0.12"
27-
hex-literal = "0.3"
27+
hex-literal = "0.3.3"
2828
sha1 = { version = "0.10", package = "sha-1" }
2929
sha2 = "0.10"
3030
streebog = "0.10"
3131

3232
[features]
33+
default = ["hmac"]
3334
parallel = ["rayon", "std"]
3435
simple = ["hmac", "password-hash", "sha2"]
3536
std = ["password-hash/std"]

pbkdf2/src/lib.rs

Lines changed: 159 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,48 @@
11
//! This crate implements the PBKDF2 key derivation function as specified
22
//! in [RFC 2898](https://tools.ietf.org/html/rfc2898).
33
//!
4-
//! If you are only using the low-level [`pbkdf2`] function instead of the
5-
//! higher-level [`Pbkdf2`] struct to produce/verify hash strings,
6-
//! it's recommended to disable default features in your `Cargo.toml`:
4+
//! # Examples
75
//!
8-
//! ```toml
9-
//! [dependencies]
10-
//! pbkdf2 = { version = "0.11", default-features = false }
6+
//! PBKDF2 is defined in terms of a keyed pseudo-random function (PRF). Most
7+
//! commonly HMAC is used as this PRF. In such cases you can use [`pbkdf2_hmac`]
8+
//! and [`pbkdf2_hmac_array`] functions. The former accepts a byte slice which
9+
//! gets filled with generated key, while the former returns an array with
10+
//! generated key of requested length.
11+
//!
12+
//! ```
13+
//! # #[cfg(feature = "hmac")] {
14+
//! use hex_literal::hex;
15+
//! use pbkdf2::{pbkdf2_hmac, pbkdf2_hmac_array};
16+
//! use sha2::Sha256;
17+
//!
18+
//! let password = b"password";
19+
//! let salt = b"salt";
20+
//! // number of iterations
21+
//! let n = 4096;
22+
//! // Expected value of generated key
23+
//! let expected = hex!("c5e478d59288c841aa530db6845c4c8d962893a0");
24+
//!
25+
//! let mut key1 = [0u8; 20];
26+
//! pbkdf2_hmac::<Sha256>(password, salt, n, &mut key1);
27+
//! assert_eq!(key1, expected);
28+
//!
29+
//! let key2 = pbkdf2_hmac_array::<Sha256, 20>(password, salt, n);
30+
//! assert_eq!(key2, expected);
31+
//! # }
1132
//! ```
1233
//!
13-
//! # Usage (simple with default params)
34+
//! If you want to use a different PRF, then you can use [`pbkdf2`][crate::pbkdf2]
35+
//! and [`pbkdf2_array`] functions.
1436
//!
15-
//! Note: this example requires the `rand_core` crate with the `std` feature
16-
//! enabled for `rand_core::OsRng` (embedded platforms can substitute their
17-
//! own RNG)
37+
//! This crates also provides the high-level password-hashing API through
38+
//! the [`Pbkdf2`] struct and traits defined in the
39+
//! [`password-hash`][password_hash] crate.
1840
//!
1941
//! Add the following to your crate's `Cargo.toml` to import it:
2042
//!
2143
//! ```toml
2244
//! [dependencies]
23-
//! pbkdf2 = "0.10"
45+
//! pbkdf2 = { version = "0.12", features = ["simple"] }
2446
//! rand_core = { version = "0.6", features = ["std"] }
2547
//! ```
2648
//!
@@ -78,7 +100,15 @@ pub use crate::simple::{Algorithm, Params, Pbkdf2};
78100
#[cfg(feature = "parallel")]
79101
use rayon::prelude::*;
80102

81-
use digest::{generic_array::typenum::Unsigned, FixedOutput, KeyInit, Update};
103+
use digest::{generic_array::typenum::Unsigned, FixedOutput, InvalidLength, KeyInit, Update};
104+
105+
#[cfg(feature = "hmac")]
106+
use digest::{
107+
block_buffer::Eager,
108+
core_api::{BlockSizeUser, BufferKindUser, CoreProxy, FixedOutputCore, UpdateCore},
109+
generic_array::typenum::{IsLess, Le, NonZero, U256},
110+
HashMarker,
111+
};
82112

83113
#[inline(always)]
84114
fn xor(res: &mut [u8], salt: &[u8]) {
@@ -89,7 +119,7 @@ fn xor(res: &mut [u8], salt: &[u8]) {
89119
#[inline(always)]
90120
fn pbkdf2_body<PRF>(i: u32, chunk: &mut [u8], prf: &PRF, salt: &[u8], rounds: u32)
91121
where
92-
PRF: KeyInit + Update + FixedOutput + Clone,
122+
PRF: Update + FixedOutput + Clone,
93123
{
94124
for v in chunk.iter_mut() {
95125
*v = 0;
@@ -114,34 +144,136 @@ where
114144
}
115145
}
116146

117-
/// Generic implementation of PBKDF2 algorithm.
118-
#[cfg(not(feature = "parallel"))]
147+
/// Generic implementation of PBKDF2 algorithm which accepts an arbitrary keyed PRF.
148+
///
149+
/// ```
150+
/// use hex_literal::hex;
151+
/// use pbkdf2::pbkdf2;
152+
/// use hmac::Hmac;
153+
/// use sha2::Sha256;
154+
///
155+
/// let mut buf = [0u8; 20];
156+
/// pbkdf2::<Hmac<Sha256>>(b"password", b"salt", 4096, &mut buf)
157+
/// .expect("HMAC can be initialized with any key length");
158+
/// assert_eq!(buf, hex!("c5e478d59288c841aa530db6845c4c8d962893a0"));
159+
/// ```
119160
#[inline]
120-
pub fn pbkdf2<PRF>(password: &[u8], salt: &[u8], rounds: u32, res: &mut [u8])
161+
pub fn pbkdf2<PRF>(
162+
password: &[u8],
163+
salt: &[u8],
164+
rounds: u32,
165+
res: &mut [u8],
166+
) -> Result<(), InvalidLength>
121167
where
122168
PRF: KeyInit + Update + FixedOutput + Clone + Sync,
123169
{
124170
let n = PRF::OutputSize::to_usize();
125171
// note: HMAC can be initialized with keys of any size,
126172
// so this panic never happens with it
127-
let prf = PRF::new_from_slice(password).expect("PRF initialization failure");
173+
let prf = PRF::new_from_slice(password)?;
128174

129-
for (i, chunk) in res.chunks_mut(n).enumerate() {
130-
pbkdf2_body(i as u32, chunk, &prf, salt, rounds);
175+
#[cfg(not(feature = "parallel"))]
176+
{
177+
for (i, chunk) in res.chunks_mut(n).enumerate() {
178+
pbkdf2_body(i as u32, chunk, &prf, salt, rounds);
179+
}
180+
}
181+
#[cfg(feature = "parallel")]
182+
{
183+
res.par_chunks_mut(n).enumerate().for_each(|(i, chunk)| {
184+
pbkdf2_body(i as u32, chunk, &prf, salt, rounds);
185+
});
131186
}
187+
188+
Ok(())
132189
}
133190

134-
/// Generic implementation of PBKDF2 algorithm.
135-
#[cfg(feature = "parallel")]
191+
/// A variant of the [`pbkdf2`][crate::pbkdf2] function which returns an array
192+
/// instead of filling an input slice.
193+
///
194+
/// ```
195+
/// use hex_literal::hex;
196+
/// use pbkdf2::pbkdf2_array;
197+
/// use hmac::Hmac;
198+
/// use sha2::Sha256;
199+
///
200+
/// let res = pbkdf2_array::<Hmac<Sha256>, 20>(b"password", b"salt", 4096)
201+
/// .expect("HMAC can be initialized with any key length");
202+
/// assert_eq!(res, hex!("c5e478d59288c841aa530db6845c4c8d962893a0"));
203+
/// ```
136204
#[inline]
137-
pub fn pbkdf2<PRF>(password: &[u8], salt: &[u8], rounds: u32, res: &mut [u8])
205+
pub fn pbkdf2_array<PRF, const N: usize>(
206+
password: &[u8],
207+
salt: &[u8],
208+
rounds: u32,
209+
) -> Result<[u8; N], InvalidLength>
138210
where
139211
PRF: KeyInit + Update + FixedOutput + Clone + Sync,
140212
{
141-
let n = PRF::OutputSize::to_usize();
142-
let prf = PRF::new_from_slice(password).expect("PRF initialization failure");
213+
let mut buf = [0u8; N];
214+
pbkdf2::<PRF>(password, salt, rounds, &mut buf).map(|()| buf)
215+
}
216+
217+
/// A variant of the [`pbkdf2`][crate::pbkdf2] function which uses HMAC for PRF.
218+
/// It's generic over (eager) hash functions.
219+
///
220+
/// ```
221+
/// use hex_literal::hex;
222+
/// use pbkdf2::pbkdf2_hmac;
223+
/// use sha2::Sha256;
224+
///
225+
/// let mut buf = [0u8; 20];
226+
/// pbkdf2_hmac::<Sha256>(b"password", b"salt", 4096, &mut buf);
227+
/// assert_eq!(buf, hex!("c5e478d59288c841aa530db6845c4c8d962893a0"));
228+
/// ```
229+
#[cfg(feature = "hmac")]
230+
#[cfg_attr(docsrs, doc(cfg(feature = "hmac")))]
231+
pub fn pbkdf2_hmac<D>(password: &[u8], salt: &[u8], rounds: u32, res: &mut [u8])
232+
where
233+
D: CoreProxy,
234+
D::Core: Sync
235+
+ HashMarker
236+
+ UpdateCore
237+
+ FixedOutputCore
238+
+ BufferKindUser<BufferKind = Eager>
239+
+ Default
240+
+ Clone,
241+
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
242+
Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
243+
{
244+
crate::pbkdf2::<hmac::Hmac<D>>(password, salt, rounds, res)
245+
.expect("HMAC can be initialized with any key length");
246+
}
143247

144-
res.par_chunks_mut(n).enumerate().for_each(|(i, chunk)| {
145-
pbkdf2_body(i as u32, chunk, &prf, salt, rounds);
146-
});
248+
/// A variant of the [`pbkdf2_hmac`] function which returns an array
249+
/// instead of filling an input slice.
250+
///
251+
/// ```
252+
/// use hex_literal::hex;
253+
/// use pbkdf2::pbkdf2_hmac_array;
254+
/// use sha2::Sha256;
255+
///
256+
/// assert_eq!(
257+
/// pbkdf2_hmac_array::<Sha256, 20>(b"password", b"salt", 4096),
258+
/// hex!("c5e478d59288c841aa530db6845c4c8d962893a0"),
259+
/// );
260+
/// ```
261+
#[cfg(feature = "hmac")]
262+
#[cfg_attr(docsrs, doc(cfg(feature = "hmac")))]
263+
pub fn pbkdf2_hmac_array<D, const N: usize>(password: &[u8], salt: &[u8], rounds: u32) -> [u8; N]
264+
where
265+
D: CoreProxy,
266+
D::Core: Sync
267+
+ HashMarker
268+
+ UpdateCore
269+
+ FixedOutputCore
270+
+ BufferKindUser<BufferKind = Eager>
271+
+ Default
272+
+ Clone,
273+
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
274+
Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
275+
{
276+
let mut buf = [0u8; N];
277+
pbkdf2_hmac::<D>(password, salt, rounds, &mut buf);
278+
buf
147279
}

0 commit comments

Comments
 (0)