Installation

Warning

This is alpha software under active development and as such it is not suitable for production use. It was tested only
on Python 2.6/2.7 on a 32-bit Mac and 64-bit CentOS 6.
It probably will not run on Python
2.5 since Py-Scrypt doesn't run on interpreters earlier than
Python 2.6.

Installing Django-Scrypt into your system-wide Python's site-packages directory is not recommended. Instead, use virtualenv and virtualenvwrapper to create isolated virtual Python environments and then install this software into the isolated virtualenv's site-packages directory.

Install it into your site-packages (if you install it in your system's
site-packages you will probably need to be root or you will probably
need to use sudo):

$ python setup.py install

Test your installation (seriously, please do this):

$ python setup.py test

Keep in mind that the development tip will always be the least stable and the
least tested version of the software. Please excuse the mess.

Usage

Basic Usage

Warning

This software depends on Py-Scrypt version 0.5.5 to reveal
the Scrypt hashing function. Unfortunately, it contains a bug
that can result in incorrect hashing when run on 64-bit Linux systems. View
the Py-Scrypt issue tracker for the latest information on this issue. [1]

To use Scrypt as your default password storage algorithm in Django 1.4/1.5,
install it and make the following changes. In your Django 1.4/1.5 application's
settings.py file, modify the PASSWORD_HASHERS tuple (or add it if it
is missing) to include ScryptPasswordHasher as the first hasher in the
tuple. It needs to be at the very top.

You have now changed your app to use Scrypt as the default storage
algorithm. As users login to your system they will automatically upgrade their hashed passwords to Scrypt hashes.

Note

You need to keep the other hasher entries in this list or else Django
won't be able to upgrade the passwords!

Advanced Usage

If you use this software in a resource constrained environment or if you want a higher degree of protection, you can create custom ScryptPasswordHashers by subclassing the provided ScryptPasswordHasher. Subclassing will allow you to tune the Scrypt parameters to meet your needs.

The first thing to do is create a new custom hasher. Let's assume that you create a new file named my_hashers.py and you put it into your application root (my_app). In that file you can subclass the default hasher:

You can change any (or all) of the class variables N, r, or p. The algorithm class variable must also be changed to a short unique string since it is used to identify and upgrade the hashing scheme in the stored password hash.

To use BigMemNScryptHasher as your default password storage algorithm make the following changes. In your application's settings.py file, modify the PASSWORD_HASHERS tuple (or add it if it
is missing) to include BigMemNScryptHasher as the first hasher in the
tuple. It needs to be at the very top:

If you want to change the parameters again in the future, simply repeat the process with another subclass and another unique algorithm class variable. Add it to the top of the tuple and your users will have their password hashes migrated to the new scheme as they log in.

Django-Scrypt stores Nexp in the encoded hash, but not N. The positive integer Nexp is the exponent used to generate N which is calculated as needed (N = 2 ** Nexp). Doing this saves space in the database row. These default values lead to Scrypt using 128 * 8 * 2 ** 14 = 16M bytes of memory.

The values of N, r and p affect running time proportionately; however, p can be used to independently tune the running time since it has a smaller influence on memory usage.

The final parameter buflen has been proposed for Py-Scrypt but is not implemented. The value will be used to change the size of the returned hash. Currently, Py-Scrypt'shash function returns a message digest of length 64-bytes or 512-bits.

Caveats

Hash Format Changes As 'N' Removed

In an attempt to shorten the length of the encoded hash, I removed the
N-value and replaced it with an N-exponent value named Nexp.
The reason for this is that N must be a power of
two {2, 4, 8, ... 16384, ...etc...} and those digits take up room in a
128 character hash storage space. It makes more sense to me to store the exponent and just make the actual integer on the fly.

N == 16384 == 2 ** 14 therefore Nexp == 14

The old encoded hash format that got stored in Django's database was

scrypt$salt$16384$8$1$64$Base64Hash==

The new and shorter encoded hash format is

scrypt$salt$14$8$1$64$Base64Hash==

The good news is that "14" is three characters shorter than "16384". The bad news
is that this introduces a backwards incompatible change as of version 0.2.0.

If you see your application generating HTTP 500 Server Errors with an Exception raised with
error: 'hash parameters are wrong (r*p should be < 2**30, and N should
be a power of two > 1)' then you should suspect that an old hash is telling
Scrypt to use N = 2 ** 16384 which is way, way, way too large. The
solution is to replace the 16384 in the old hashes with 14. You might have to alter your database manually or write some custom code to fix this change.

Django Password Field Character Length Limits

By default, Django limits password field lengths to 128 characters. Using
the default settings in Django-Scrypt with Django's salting
implementation should yield encoded hashes less than 128 characters (approx 119 characters); however, if you override the ScryptPasswordHasher
class variables you might end up overflowing the default password field.

The solution is to increase the size of the password field using SQL. You
should consult your database documentation for the correct commands necessary to alter your database.

Bugs! Help!!

If you find any bugs in this software please report them via the BitBucket
issue tracker [3] or send an email to code@kelvinwong.ca. Any serious
security bugs should be reported via email only.