- Notifications
You must be signed in to change notification settings - Fork9
A Rust port of the password primitives used in Django Project.
License
Racum/rust-djangohashers
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
A Rust port of the password primitives used inDjango Project.
Django'sdjango.contrib.auth.models.User
class has a few methods to deal with passwords, likeset_password()
andcheck_password()
;DjangoHashers implements the primitive functions behind those methods. All Django's built-in hashers 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.
Content ofexamples/tldr.rs
:
externcrate djangohashers;use djangohashers::*;fnmain(){let encoded =make_password("K2jitmJ3CBfo");println!("Hash: {:?}", encoded);let is_valid =check_password("K2jitmJ3CBfo",&encoded).unwrap();println!("Is valid: {:?}", is_valid);}
Output:
$ cargo run --quiet --example tldrHash: "pbkdf2_sha256$1000000$pQE1Pfr1CUpS$gDLIrbspb7isluj1zxcItegXxrE1BJP3sdg61S+72rw="Is valid: true
Add the dependency to yourCargo.toml
:
[dependencies]djangohashers ="^1.8"
Reference and import:
externcrate djangohashers;// Everything (it's not much):use djangohashers::*;// Or, just what you need:use djangohashers::{check_password, make_password,Algorithm};
By default all the hashers are enabled, but you can pick only the hashers that you need to avoid unneeded dependencies.
default
: all hashers.with_pbkdf2
: onlyPBKDF2 andPBKDF2SHA1.with_argon2
: onlyArgon2.with_scrypt
: onlyScrypt.with_bcrypt
: onlyBCrypt andBCryptSHA256.with_legacy
: onlySHA1,MD5,UnsaltedSHA1,UnsaltedMD5 andCrypt.fpbkdf2
: enablesFast PBKDF2 (requires OpenSSL, see below).fuzzy_tests
: only for development, enables fuzzy tests.
Depending on your platform, OS and version of libraries, it is possible that DjangoHashers can be slower than Python/Django's reference implementation. If performance is critical for your case, there is analternatice implementation: the packagefastpbkdf2 uses a C-binding of alibrary that requires OpenSSL. Ifring's implementation of PBKDF2 reaches this level of optiomization, thefastpbkdf2 version will be deprecated.
Add the dependency to yourCargo.toml
declaring the feature:
[dependencies.djangohashers]version ="^1.8"features = ["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 macOS you may need to do something like this:
$ brew install openssl$ export LIBRARY_PATH="$(brew --prefix openssl)/lib"$ export CFLAGS="-I$(brew --prefix openssl)/include"$ cargo ...
For other OSs and package managers,follow the guide of how to install Python’sCryptography dependencies, that also links against OpenSSL.
On a Apple M4 Pro:
Method | Encode or Check | Performance |
---|---|---|
Django 5.2.0 on Python 3.13.2 | 136ms | 100% (baseline) |
djangohashers with ring::pbkdf2 (default) | 77ms | 56.6% 🐇 |
djangohashers with fastpbkdf2 | 49ms | 36.0% 🐇 |
Replicate test above with Docker:
$ docker build -t rs-dj-hashers-profile ....$ docker run -t rs-dj-hashers-profileHashing time: 136ms (Python 3.13.2, Django 5.2.0).Hashing time: 77ms (Vanilla PBKDF2).Hashing time: 49ms (Fast PBKDF2).
DjangoHashers passes all relevant unit tests from Django 1.4 to 5.2 (and beta of 5.3), there is even aline-by-line translation oftests/auth_tests/test_hashers.py.
What isnot covered:
- Upgrade/Downgrade callbacks.
- Any 3rd-party hasher outside Django's code.
- Some tests that makes no sense in idiomatic Rust.
API Documentation, thanks todocs.rs project!
Function signatures:
pubfncheck_password(password:&str,encoded:&str) ->Result<bool,HasherError>{}pubfncheck_password_tolerant(password:&str,encoded:&str) ->bool{}
Complete version:
let password ="KRONOS";// Sent by the user.let encoded ="pbkdf2_sha256$24000$...";// Fetched from DB.matchcheck_password(password, encoded){Ok(valid) =>{if valid{// Log the user in.}else{// Ask the user to try again.}}Err(error) =>{// Deal with the error.}}
Possible Errors:
HasherError::UnknownAlgorithm
: anything not recognizable as an algorithm.HasherError::BadHash
: Hash string is corrupted.HasherError::InvalidIterations
: number of iterations is not a positive integer.HasherError::EmptyHash
: hash string is empty.HasherError::InvalidArgon2Salt
: Argon2 salt should be Base64 encoded.
If you want to automatically assume all errors as"invalid password", there is a shortcut for that:
ifcheck_password_tolerant(password, encoded){// Log the user in.}else{// Ask the user to try again.}
Function signatures:
pubfnmake_password(password:&str) ->String{}pubfnmake_password_with_algorithm(password:&str,algorithm:Algorithm) ->String{}pubfnmake_password_with_settings(password:&str,salt:&str,algorithm:Algorithm) ->String{}
Available algorithms:
Algorithm::PBKDF2
(default)Algorithm::PBKDF2SHA1
Algorithm::Argon2
Algorithm::Scrypt
Algorithm::BCryptSHA256
Algorithm::BCrypt
Algorithm::SHA1
Algorithm::MD5
Algorithm::UnsaltedSHA1
Algorithm::UnsaltedMD5
Algorithm::Crypt
The algorithms follow the same Django naming model, minus thePasswordHasher
suffix.
Using default settings (PBKDF2 algorithm, random salt):
let encoded =make_password("KRONOS");// Returns something like:// pbkdf2_sha256$24000$go9s3b1y1BTe$Pksk4EptJ84KDnI7ciocmhzFAb5lFoFwd6qlPOwwW4Q=
Using a defined algorithm (random salt):
let encoded =make_password_with_algorithm("KRONOS",Algorithm::BCryptSHA256);// 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("KRONOS","seasalt",Algorithm::PBKDF2SHA1);// Returns exactly this (remember, the salt is fixed!):// pbkdf2_sha1$24000$seasalt$F+kiWNHXbMBcwgxsvSKFCWHnZZ0=
Warning:make_password_with_settings
andmake_password_core
will both panic if salt is not only letters and numbers (^[A-Za-z0-9]*$
).
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 djangohashers::{Django,DjangoVersion};let django =Django{version:DjangoVersion::V1_8};// Django 1.8.let encoded = django.make_password("KRONOS");// Returns something like:// pbkdf2_sha256$20000$u0C1E8jrnAYx$7KIo/fAuBJpswQyL7pTxO06ccrSjGdIe7iSqzdVub1w=// |||||// ...notice the 20000 iterations, used in Django 1.8.
Available versions:
DjangoVersion::CURRENT
Current Django version (5.2
for DjangoHashers1.8.1
).DjangoVersion::V1_4
Django 1.4DjangoVersion::V1_5
Django 1.5DjangoVersion::V1_6
Django 1.6DjangoVersion::V1_7
Django 1.7DjangoVersion::V1_8
Django 1.8DjangoVersion::V1_9
Django 1.9DjangoVersion::V1_10
Django 1.10DjangoVersion::V1_11
Django 1.11DjangoVersion::V2_0
Django 2.0DjangoVersion::V2_1
Django 2.1DjangoVersion::V2_2
Django 2.2DjangoVersion::V3_0
Django 3.0DjangoVersion::V3_1
Django 3.1DjangoVersion::V3_2
Django 3.2DjangoVersion::V4_0
Django 4.0DjangoVersion::V4_1
Django 4.1DjangoVersion::V4_2
Django 4.2DjangoVersion::V5_0
Django 5.0DjangoVersion::V5_1
Django 5.1DjangoVersion::V5_2
Django 5.2DjangoVersion::V5_3
Django 5.3
Function signature:
pubfnis_password_usable(encoded:&str) ->bool{}
You can check if the password hash is properly formatted before running the expensive cryto stuff:
let encoded ="pbkdf2_sha256$24000$...";// Fetched from DB.ifis_password_usable(encoded){// Go ahead.}else{// Check your database or report an issue.}
- Be patient with me, I’m new to Rust and this is my first project.
- Don't go nuts with yourmad-rust-skillz, legibility is a priority.
- Please userustfmt in your code.
- Always include some test case.
Rust DjangoHashers is released under the3-Clause BSD License.
tl;dr:"free to use as long as you credit me".
About
A Rust port of the password primitives used in Django Project.
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.
Contributors3
Uh oh!
There was an error while loading.Please reload this page.