Skip to content

Commit ce58f85

Browse files
committed
Added option of using fastpbkdf2.
1 parent 318c536 commit ce58f85

File tree

4 files changed

+79
-4
lines changed

4 files changed

+79
-4
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
All notable changes to this project will be documented in this file.
44
This project adheres to [Semantic Versioning](http://semver.org/).
55

6+
## [0.2.0] - 2016-03-22
7+
8+
### Added
9+
10+
- Option of using fastpbkdf2 (requires OpenSSL to build).
11+
612
## [0.1.0] - 2016-01-01
713

814
### Added

Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "djangohashers"
3-
version = "0.1.0"
3+
version = "0.2.0"
44
authors = ["Ronaldo Racum <ronaldo@racum.com>"]
55
license = "BSD-3-Clause"
66
readme = "README.md"
@@ -13,8 +13,13 @@ doc = true
1313
doctest = false
1414
bench = true
1515

16+
[features]
17+
default = []
18+
fpbkdf2 = ["fastpbkdf2"]
19+
1620
[dependencies]
1721
rust-crypto = "^0.2"
1822
bcrypt = "^0.1"
1923
rustc-serialize = "^0.3"
2024
rand = "^0.3"
25+
fastpbkdf2 = { version = "0.1.0", optional = true }

README.md

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Add the dependency to your `Cargo.toml`:
1414

1515
```toml
1616
[dependencies]
17-
djangohashers = "^0.1"
17+
djangohashers = "0.2.0"
1818
```
1919

2020
Reference and import:
@@ -29,6 +29,47 @@ use djangohashers::*;
2929
use djangohashers::{check_password, make_password, Algorithm};
3030
```
3131

32+
## Fast PBKDF2 Version
33+
34+
Unfortunately rust-crypto’s implementation of PBKDF2 is not properly optimized: it does not adheres to the loop inlines and buffering used in [modern implementations](https://jbp.io/2015/08/11/pbkdf2-performance-matters/). The package [fastpbkdf2](https://github.com/ctz/rust-fastpbkdf2) uses a C-binding of a [library](https://github.com/ctz/fastpbkdf2) that requires OpenSSL.
35+
36+
### Instalation
37+
38+
Add the dependency to your `Cargo.toml` declaring the feature:
39+
40+
```toml
41+
[dependencies.djangohashers]
42+
version = "0.2.0"
43+
features = ["fpbkdf2"]
44+
```
45+
46+
You need to install OpenSSL and set the environment variable to make it visible to the compiler; this changes depending on the operation system and package manager, for example, in OS X with MacPorts you may need to do something like this:
47+
48+
```
49+
$ sudo port install openssl
50+
$ CFLAGS="-I/opt/local/include" cargo ...
51+
```
52+
53+
For other OSs and package managers, [follow the guide](https://cryptography.io/en/latest/installation/) of how to install Python’s **Cryptography** dependencies, that also links against OpenSSL.
54+
55+
### Performance
56+
57+
Method | Encode or Check | Performance
58+
------- | --------------- | -------
59+
Django 1.9.4 | 29.5ms | Baseline
60+
djangohashers with rust-crypto 0.2.34 (default) | 41.7ms | 41% slower
61+
djangohashers with fastpbkdf2 0.1.0 | 23.1ms | 28% faster
62+
63+
Notes:
64+
65+
* Best of 5 rounds of 100 events.
66+
* Built with `--release`.
67+
* PBKDF2 using SHA256 and iteration count set to 24000.
68+
* Django version tested with CPython 3.5.1
69+
* Rust/fastpbkdf2 version tested with Rust 1.6.0 and OpenSSL 1.0.2g.
70+
* iMac Mid 2010 with an Intel Core i3 3.2Ghz and 16GB of RAM, running OS X 10.11.3.
71+
72+
3273
## Compatibility
3374

3475
DjangoHashers passes all relevant unit tests from Django 1.9, there is even a [line-by-line translation](https://github.com/Racum/rust-djangohashers/blob/master/tests/django.rs) of [tests/auth_tests/test_hashers.py](https://github.com/django/django/blob/e403f22/tests/auth_tests/test_hashers.py).

src/crypto_utils.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,55 @@ extern crate rustc_serialize;
44
extern crate crypto;
55
extern crate bcrypt;
66

7+
#[cfg(fpbkdf2)]
8+
extern crate fastpbkdf2;
9+
710
use self::rustc_serialize::base64::{STANDARD, ToBase64};
811
use self::crypto::digest::Digest;
9-
use self::crypto::hmac::Hmac;
1012
use self::crypto::sha2::Sha256;
1113
use self::crypto::sha1::Sha1;
1214
use self::crypto::md5::Md5;
15+
16+
#[cfg(not(fpbkdf2))]
17+
use self::crypto::hmac::Hmac;
18+
#[cfg(not(fpbkdf2))]
1319
use self::crypto::pbkdf2::pbkdf2;
20+
#[cfg(fpbkdf2)]
21+
use self::fastpbkdf2::{pbkdf2_hmac_sha1, pbkdf2_hmac_sha256};
1422

1523
pub use self::bcrypt::hash as hash_bcrypt;
1624
pub use self::bcrypt::verify as verify_bcrypt;
1725

18-
26+
#[cfg(not(fpbkdf2))]
1927
pub fn hash_pbkdf2_sha256(password: &str, salt: &str, iterations: u32) -> String {
2028
let mut mac = Hmac::new(Sha256::new(), &password.as_bytes());
2129
let mut result = [0u8; 32];
2230
pbkdf2(&mut mac, &salt.as_bytes(), iterations, &mut result);
2331
result.to_base64(STANDARD)
2432
}
2533

34+
#[cfg(fpbkdf2)]
35+
pub fn hash_pbkdf2_sha256(password: &str, salt: &str, iterations: u32) -> String {
36+
let mut result = [0u8; 32];
37+
pbkdf2_hmac_sha256(&password.as_bytes(), &salt.as_bytes(), iterations, &mut result);
38+
result.to_base64(STANDARD)
39+
}
40+
41+
#[cfg(not(fpbkdf2))]
2642
pub fn hash_pbkdf2_sha1(password: &str, salt: &str, iterations: u32) -> String {
2743
let mut mac = Hmac::new(Sha1::new(), &password.as_bytes());
2844
let mut result = [0u8; 20];
2945
pbkdf2(&mut mac, &salt.as_bytes(), iterations, &mut result);
3046
result.to_base64(STANDARD)
3147
}
3248

49+
#[cfg(fpbkdf2)]
50+
pub fn hash_pbkdf2_sha1(password: &str, salt: &str, iterations: u32) -> String {
51+
let mut result = [0u8; 20];
52+
pbkdf2_hmac_sha1(&password.as_bytes(), &salt.as_bytes(), iterations, &mut result);
53+
result.to_base64(STANDARD)
54+
}
55+
3356
pub fn hash_sha1(password: &str, salt: &str) -> String {
3457
let mut sha = Sha1::new();
3558
sha.input_str(salt);

0 commit comments

Comments
 (0)