Rust DjangoHashers
A Rust port of the password primitives used in Django Project.
Django's django.contrib.auth.models.User
class has a few methods to deal with passwords, like set_password()
and check_password()
; DjangoHashers implements the primitive functions behind that methods. All Django's built-in hashers (except UNIX's crypt(3)
) are supported.
This library was conceived for Django integration, but is not limited to it; you can use the password hash algorithm in any Rust project (or FFI integration), since its security model is already battle-tested.
Instalation
Add the dependency to your Cargo.toml
:
[]
= "0.2.2"
Reference and import:
extern crate djangohashers;
// Everything (it's not much):
use *;
// Or, just what you need:
use ;
Fast PBKDF2 Version
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. The package fastpbkdf2 uses a C-binding of a library that requires OpenSSL.
Instalation
Add the dependency to your Cargo.toml
declaring the feature:
[]
= "0.2.2"
= ["fpbkdf2"]
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:
$ sudo port install openssl
$ CFLAGS="-I/opt/local/include" cargo ...
For other OSs and package managers, follow the guide of how to install Python’s Cryptography dependencies, that also links against OpenSSL.
Performance
Method | Encode or Check | Performance |
---|---|---|
Django 1.9.4 | 29.5ms | Baseline |
djangohashers with rust-crypto 0.2.34 (default) | 41.7ms | 41% slower |
djangohashers with fastpbkdf2 0.1.0 | 23.1ms | 28% faster |
Notes:
- Best of 5 rounds of 100 events.
- Built with
--release
. - PBKDF2 using SHA256 and iteration count set to 24000.
- Django version tested with CPython 3.5.1
- Rust/fastpbkdf2 version tested with Rust 1.6.0 and OpenSSL 1.0.2g.
- iMac Mid 2010 with an Intel Core i3 3.2Ghz and 16GB of RAM, running OS X 10.11.3.
Compatibility
DjangoHashers passes all relevant unit tests from Django 1.9, there is even a line-by-line translation of tests/auth_tests/test_hashers.py.
What is not covered:
- Upgrade/Downgrade callbacks.
- Any 3rd-party hasher outside Django's code.
- Some tests that makes no sense in idiomatic Rust.
Usage
API Documentation, thanks to crates.fyi project!
Verifying a Hashed Password
Function signatures:
Complete version:
let password = "KRONOS"; // Sent by the user.
let encoded = "pbkdf2_sha256$24000$..."; // Fetched from DB.
match check_password
Possible Errors:
HasherError::UnknownAlgorithm
: anything not recognizable as an algorithm.HasherError::InvalidIterations
: number of iterations is not a positive integer.HasherError::EmptyHash
: hash string is empty.
If you want to automatically assume all errors as "invalid password", there is a shortcut for that:
if check_password_tolerant else
Generating a Hashed Password
Function signatures:
Available algorithms:
Algorithm::PBKDF2
(default)Algorithm::PBKDF2SHA1
Algorithm::BCryptSHA256
Algorithm::BCrypt
Algorithm::SHA1
Algorithm::MD5
Algorithm::UnsaltedSHA1
Algorithm::UnsaltedMD5
Algorithm::Crypt
The algorithms follow the same Django naming model, minus the PasswordHasher
suffix.
Using default settings (PBKDF2 algorithm, random salt):
let encoded = make_password;
// Returns something like:
// pbkdf2_sha256$24000$go9s3b1y1BTe$Pksk4EptJ84KDnI7ciocmhzFAb5lFoFwd6qlPOwwW4Q=
Using a defined algorithm (random salt):
let encoded = make_password_with_algorithm;
// Returns something like:
// bcrypt_sha256$$2b$12$e5C3zfswn.CowOBbbb7ngeYbxKzJePCDHwo8AMr/SZeZCoGrk7oue
Using a defined algorithm and salt (not recommended, use it only for debug):
let encoded = make_password_with_settings;
// Returns exactly this (remember, the salt is fixed!):
// pbkdf2_sha1$24000$seasalt$F+kiWNHXbMBcwgxsvSKFCWHnZZ0=
Generating a Hashed Password based on a Django version
New in
0.2.1
.
Django versions can have different number of iterations for hashers based on PBKDF2 and BCrypt algorithms; this abstraction makes possible to generate a password with the same number of iterations used in that versions.
use ;
let django = Django ; // Django 1.8.
let encoded = django.make_password;
// Returns something like:
// pbkdf2_sha256$20000$u0C1E8jrnAYx$7KIo/fAuBJpswQyL7pTxO06ccrSjGdIe7iSqzdVub1w=
// |||||
// ...notice the 20000 iterations, used in Django 1.8.
Available versions:
Version::Current
Current Django version (1.9
for DjangoHashers0.2.1
).Version::V14
Django 1.4Version::V15
Django 1.5Version::V16
Django 1.6Version::V17
Django 1.7Version::V18
Django 1.8Version::V19
Django 1.9Version::V110
Django 1.10
Verifying a Hash Format (pre-crypto)
Function signature:
You can check if the password hash is properly formatted before running the expensive cryto stuff:
let encoded = "pbkdf2_sha256$24000$..."; // Fetched from DB.
if is_password_usable else
Contributing
- Be patient with me, I’m new to Rust and this is my first project.
- Don't go nuts with your mad-rust-skillz, legibility is a priority.
- Please use rustfmt in your code.
- Always include some test case.
License
Rust DjangoHashers is released under the 3-Clause BSD License.
tl;dr: "free to use as long as you credit me".