diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 7eabf62..f8a9b7f 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -18,6 +18,7 @@ Changes: - Fix a bunch of warnings with new ``cffi`` versions and Python 3.6. `#14 `_ `#16 `_ +- Add low-level bindings for Argon2id functions. ---- diff --git a/docs/api.rst b/docs/api.rst index 4997f87..8686193 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -53,7 +53,7 @@ Low Level .. automodule:: argon2.low_level .. autoclass:: Type - :members: D, I + :members: D, I, ID .. autodata:: ARGON2_VERSION diff --git a/docs/argon2.rst b/docs/argon2.rst index b39cf01..33eed0e 100644 --- a/docs/argon2.rst +++ b/docs/argon2.rst @@ -12,7 +12,7 @@ It is designed to have both a configurable runtime as well as memory consumption This means that you can decide how long it takes to hash a password and how much memory is required. -Argon2 comes in two variants: +Argon2 comes in three variants: Argon2d is faster and uses data-depending memory access, which makes it less suitable for hashing secrets and more suitable for cryptocurrencies and applications with no threats from side-channel timing attacks. @@ -21,6 +21,9 @@ Argon2i uses data-independent memory access, which is preferred for password hashing and password-based key derivation. Argon2i is slower as it makes more passes over the memory to protect from tradeoff attacks. +Argon2id + is a hybrid of Argon2i and Argon2d, using a combination of data-depending and data-independent memory accesses, which gives some of Argon2i's resistance to side-channel cache timing attacks and much of Argon2d's resistance to GPU cracking attacks. + Why “just use bcrypt” Is Not the Best Answer (Anymore) ------------------------------------------------------ diff --git a/src/argon2/_ffi_build.py b/src/argon2/_ffi_build.py index 4a13cb9..cec4469 100644 --- a/src/argon2/_ffi_build.py +++ b/src/argon2/_ffi_build.py @@ -30,7 +30,11 @@ ) ffi.cdef("""\ -typedef enum Argon2_type { Argon2_d = ..., Argon2_i = ... } argon2_type; +typedef enum Argon2_type { + Argon2_d = ..., + Argon2_i = ..., + Argon2_id = ..., +} argon2_type; typedef enum Argon2_version { ARGON2_VERSION_10 = ..., ARGON2_VERSION_13 = ..., diff --git a/src/argon2/low_level.py b/src/argon2/low_level.py index 1272b20..53804a3 100644 --- a/src/argon2/low_level.py +++ b/src/argon2/low_level.py @@ -52,6 +52,15 @@ class Type(Enum): password hashing and password-based key derivation. Argon2i is slower as it makes more passes over the memory to protect from tradeoff attacks. """ + ID = lib.Argon2_id + r""" + Argon2\ **id** is a hybrid of Argon2i and Argon2d, using a combination of + data-depending and data-independent memory accesses, which gives some of + Argon2i's resistance to side-channel cache timing attacks and much of + Argon2d's resistance to GPU cracking attacks. + + .. versionadded:: 16.3.0 + """ def hash_secret(secret, salt, time_cost, memory_cost, parallelism, hash_len, diff --git a/tests/test_low_level.py b/tests/test_low_level.py index 511f22e..7d8976b 100644 --- a/tests/test_low_level.py +++ b/tests/test_low_level.py @@ -39,6 +39,16 @@ # c29tZXNhbHQ$cZn5d+rFh+ZfuRhm2iGUGgcrW5YLeM6q7L3vBsdmFA0 # 0.119 seconds # Verification ok +# +# Type: Argon2id +# Iterations: 2 +# Memory: 65536 KiB +# Parallelism: 4 +# Hash: 1a9677b0afe81fda7b548895e7a1bfeb8668ffc19a530e37e088a668fab1c02a +# Encoded: $argon2id$v=19$m=65536,t=2,p=4$ +# c29tZXNhbHQ$GpZ3sK/oH9p7VIiV56G/64Zo/8GaUw434IimaPqxwCo +# 0.154 seconds +# Verification ok TEST_HASH_I_OLD = ( b"$argon2i$m=65536,t=2,p=4" @@ -53,12 +63,19 @@ b"$argon2d$v=19$m=65536,t=2,p=4$" b"c29tZXNhbHQ$cZn5d+rFh+ZfuRhm2iGUGgcrW5YLeM6q7L3vBsdmFA0" ) +TEST_HASH_ID = ( + b"$argon2id$v=19$m=65536,t=2,p=4$" + b"c29tZXNhbHQ$GpZ3sK/oH9p7VIiV56G/64Zo/8GaUw434IimaPqxwCo" +) TEST_RAW_I = binascii.unhexlify( b"20c8adf6a90550b08c03f5628b32f9edc9d32ce6b90e254cf5e330a40bcfc2be" ) TEST_RAW_D = binascii.unhexlify( b"7199f977eac587e65fb91866da21941a072b5b960b78ceaaecbdef06c766140d" ) +TEST_RAW_ID = binascii.unhexlify( + b"1a9677b0afe81fda7b548895e7a1bfeb8668ffc19a530e37e088a668fab1c02a" +) TEST_PASSWORD = b"password" TEST_SALT_LEN = 16 @@ -71,10 +88,12 @@ i_and_d_encoded = pytest.mark.parametrize("type,hash", [ (Type.I, TEST_HASH_I,), (Type.D, TEST_HASH_D,), + (Type.ID, TEST_HASH_ID,), ]) i_and_d_raw = pytest.mark.parametrize("type,hash", [ (Type.I, TEST_RAW_I,), (Type.D, TEST_RAW_D,), + (Type.ID, TEST_RAW_ID,), ]) both_hash_funcs = pytest.mark.parametrize("func", [