charlesportwoodii / php-argon2-ext

PHP7 extension for Argon2
Other
33 stars 4 forks source link

Question: Argon2 Hash Length missing, why? #9

Closed JBx0 closed 3 years ago

JBx0 commented 3 years ago

I've already asked on Stackoverflow but figured this might be the right place to ask :) https://stackoverflow.com/questions/65180567/argon2-php-vs-java-hash-len

Is there a specific reason why we don't have the option of setting a hash length in PHP?

My Environment

macOS Debian etc php 7 - 8

Expected Behavior

Being able to set the argon2 hash length.

Actual Behavior

PHP

$options = [
"memory_cost" => 1024,
"time_cost" => 2,
"threads" => 2
];
password_hash('password', PASSWORD_ARGON2I, $options);

There is no option for the hash length for example like in java or python etc:

Java:

argon2.hash_password(
"password",
memory_cost=512,
time_cost=2,
parallelism=2,
hash_len=24
)

Python:

 hash = argon2.hash_password_raw(
        time_cost=16, memory_cost=2**15, parallelism=2, hash_len=32,
        password=b'password', salt=b'some salt', type=argon2.low_level.Type.ID)
    print("Argon2 raw hash:", binascii.hexlify(hash))

    argon2Hasher = argon2.PasswordHasher(
        time_cost=16, memory_cost=2**15, parallelism=2, hash_len=32, salt_len=16)
    hash = argon2Hasher.hash("password")

php + python kinda solves it.

Reproduction Steps

Execute the code from above

Further Information:

PHP Password implementation https://github.com/php/php-src/blob/5b01c4863fe9e4bc2702b2bbf66d292d23001a18/ext/standard/password.c

https://github.com/p-h-c/phc-winner-argon2 The argon2 hash has the option for a hash length, which defaults to 32.

Usage:  ./argon2 [-h] salt [-i|-d|-id] [-t iterations] [-m memory] [-p parallelism] [-l hash length] [-e|-r] [-v (10|13)]
        Password is read from stdin
Parameters:
        salt            The salt to use, at least 8 characters
        -i              Use Argon2i (this is the default)
        -d              Use Argon2d instead of Argon2i
        -id             Use Argon2id instead of Argon2i
        -t N            Sets the number of iterations to N (default = 3)
        -m N            Sets the memory usage of 2^N KiB (default 12)
        -p N            Sets parallelism to N threads (default 1)
        -l N            Sets hash output length to N bytes (default 32)
        -e              Output only encoded hash
        -r              Output only the raw bytes of the hash
        -v (10|13)      Argon2 version (defaults to the most recent version, currently 13)
        -h              Print argon2 usage

        So any clue why there is no option for the php implementation?
charlesportwoodii commented 3 years ago

This implementation uses argon2_hash which unlike argon2id_hash_raw doesn't expose the hash length, so in short that's why the hash length isn't exposed - the library method for argon2_hash doesn't support it.

The implementation in PHP itself actually uses libsodium now, so crypto_pwhash_str_alg from libsodium is ultimately what is called. The API for password_hash doesn't expose a hash length option but if you have libsodium installed anyways you can just use libsodium to generate the hash via crypto_pwhash_str_alg.

This library is deprecated in favor of libsodium providing Argon2 functionality, you should use it instead. In PHP you'd want to use sodium_crypto_pwhash.

Off the top of my head, a simple example with sodium_crypto_pwhash() gets you:

\sodium_crypto_pwhash(24, 'test', \random_bytes(SODIUM_CRYPTO_PWHASH_SALTBYTES), 1, 1<<16, SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13);

Then from there you can construct the str format from the generated data. crypto_pwhash_str doesn't expose the hash length so you'd have to construct the pretty ASCII string manually which isn't too terribly complicated to do.

$argon2id$v=19$m=<memory>,t=<ops>,p=1$<salt>$P<hash>

password_verify can validate hashes with any given length, it just can't generate them because it's a simple API. If you want something more complex use libsodium's APIs.

JBx0 commented 3 years ago

The implementation in PHP itself actually uses libsodium now, so crypto_pwhash_str_alg from libsodium is ultimately what is called. The API for password_hash doesn't expose a hash length option but if you have libsodium installed anyways you can just use libsodium to generate the hash via crypto_pwhash_str_alg.

Thank you!!