API Reference¶
argon2_cffi
comes with an high-level API and hopefully reasonable defaults for Argon2 parameters that result in a verification time of 40–50ms on recent-ish hardware.
Unless you have any special needs, all you need to know is:
>>> from argon2 import PasswordHasher
>>> ph = PasswordHasher()
>>> hash = ph.hash("s3kr3tp4ssw0rd")
>>> hash
'$argon2id$v=19$m=102400,t=2,p=8$tSm+JOWigOgPZx/g44K5fQ$WDyus6py50bVFIPkjA28lQ'
>>> ph.verify(hash, "s3kr3tp4ssw0rd")
True
>>> ph.check_needs_rehash(hash)
False
>>> ph.verify(hash, "t0t411ywr0ng")
Traceback (most recent call last):
...
argon2.exceptions.VerifyMismatchError: The password does not match the supplied hash
A login function could thus look like this:
import argon2
ph = argon2.PasswordHasher()
def login(db, user, password):
hash = db.get_password_hash_for_user(user)
# Verify password, raises exception if wrong.
ph.verify(hash, password)
# Now that we have the cleartext password,
# check the hash's parameters and if outdated,
# rehash the user's password in the database.
if ph.check_needs_rehash(hash):
db.set_password_hash_for_user(user, ph.hash(password))
While the PasswordHasher
class has the aspiration to be good to use out of the box, it has all the parametrization you’ll need:
If you don’t specify any parameters, the following constants are used:
- argon2.DEFAULT_RANDOM_SALT_LENGTH¶
- argon2.DEFAULT_HASH_LENGTH¶
- argon2.DEFAULT_TIME_COST¶
- argon2.DEFAULT_MEMORY_COST¶
- argon2.DEFAULT_PARALLELISM¶
You can see their values in PasswordHasher
.
Exceptions¶
- exception argon2.exceptions.VerificationError¶
Verification failed.
You can find the original error message from Argon2 in
args[0]
.
- exception argon2.exceptions.VerifyMismatchError¶
The secret does not match the hash.
Subclass of
argon2.exceptions.VerificationError
.New in version 16.1.0.
- exception argon2.exceptions.HashingError¶
Raised if hashing failed.
You can find the original error message from Argon2 in
args[0]
.
- exception argon2.exceptions.InvalidHash¶
Raised if the hash is invalid before passing it to Argon2.
New in version 18.2.0.
Utilities¶
Low Level¶
>>> import argon2
>>> argon2.low_level.hash_secret(
... b"secret", b"somesalt",
... time_cost=1, memory_cost=8, parallelism=1, hash_len=64, type=argon2.low_level.Type.D
... )
b'$argon2d$v=19$m=8,t=1,p=1$c29tZXNhbHQ$ba2qC75j0+JAunZZ/L0hZdQgCv+tOieBuKKXSrQiWm7nlkRcK+YqWr0i0m0WABJKelU8qHJp0SZzH0b1Z+ITvQ'
The raw hash can also be computed:
>>> argon2.low_level.hash_secret_raw(
... b"secret", b"somesalt",
... time_cost=1, memory_cost=8, parallelism=1, hash_len=8, type=argon2.low_level.Type.D
... )
b'\xe4n\xf5\xc8|\xa3>\x1d'
The super low-level argon2_core()
function is exposed too if you need access to very specific options:
In order to use core()
, you need access to argon2_cffi
’s FFI objects.
Therefore it is OK to use argon2.low_level.ffi
and argon2.low_level.lib
when working with it:
>>> from argon2.low_level import ARGON2_VERSION, Type, core, ffi, lib
>>> pwd = b"secret"
>>> salt = b"12345678"
>>> hash_len = 8
>>> # Make sure you keep FFI objects alive until *after* the core call!
>>> cout = ffi.new("uint8_t[]", hash_len)
>>> cpwd = ffi.new("uint8_t[]", pwd)
>>> csalt = ffi.new("uint8_t[]", salt)
>>> ctx = ffi.new(
... "argon2_context *", dict(
... version=ARGON2_VERSION,
... out=cout, outlen=hash_len,
... pwd=cpwd, pwdlen=len(pwd),
... salt=csalt, saltlen=len(salt),
... secret=ffi.NULL, secretlen=0,
... ad=ffi.NULL, adlen=0,
... t_cost=1,
... m_cost=8,
... lanes=1, threads=1,
... allocate_cbk=ffi.NULL, free_cbk=ffi.NULL,
... flags=lib.ARGON2_DEFAULT_FLAGS,
... )
... )
>>> ctx
<cdata 'struct Argon2_Context *' owning 120 bytes>
>>> core(ctx, Type.D.value)
0
>>> out = bytes(ffi.buffer(ctx.out, ctx.outlen))
>>> out
b'\xb4\xe2HjO\x14d\x9b'
>>> out == argon2.low_level.hash_secret_raw(pwd, salt, 1, 8, 1, 8, Type.D)
True
All constants and types on argon2.low_level.lib
are guaranteed to stay as long they are not altered by Argon2 itself.
Deprecated APIs¶
These APIs are from the first release of argon2_cffi
and proved to live in an unfortunate mid-level.
On one hand they have defaults and check parameters but on the other hand they only consume byte strings.
Therefore the decision has been made to replace them by a high-level (argon2.PasswordHasher
) and a low-level (argon2.low_level
) solution.
There are no immediate plans to remove them though.