------------------------------------------------------------------------------- Password Hashing This is a guide in hashing a password for storing it in a uniX password file. That is not to say the password couldn't be stored in some other manner. It is closely related to Key Derivation (KDF, see "key_derivation.txt" ) which deals with hashing a password to for a cryptographic key. And typically the same process is used. All user passwords should saved in some way that makes the password irrecoverable from the data that is stored... That is it is one way hashed. The original password should not be recoverable If the password is reversible (encrypted) then you are only obfuscating the password, and that is not recommended. See "passwd_obfuscation.txt" Note that the password store can be encrypted, but the passwords in that store still not be recoverable. To validate a users password, the input password MUST be hashed in the same way as the stored password, and then the two hashes compared to see if what the user presented matches the one stored, so as to authenticate the user. UNIX Password (and by extension Web Server "htpasswd" files) files have always used this method, and have slowly been updated to include newer more secure hashing algorithms. The hashed password should also use a different randomly selected 'salt', and perhaps (though not common with UNIX passwords). This prevents the use of prepared 'rainbow tables' to compare against multiple (thousands) passwords, simultaneously. A second common 'salt' that comes from a separate source (perhaps from off-site server, and saved only in memory (such as a kernel keyring), would enhance security even more. This this is not done under UNIX. The hashing itself can also benefit from a randomised looped iteration, such as used for PBKDF v2 password derivation, can these days can be part of the password hashing method used. The number of 'rounds' must also then be stored. ------------------------------------------------------------------------------- Password into Hashed Password... NOTE: The MD5 password ($1$), is not the same as a normal md5sum output. The MD5 hashing is also no longer thought as a secure hashing function, as MD5 was designed to be quick, as a checksum method (MAC) and not as a key derivation function (KDF). Use 'crypt()' -- man crypt The UNIX crypt() function (see "man 3 crypt") is the traditional way of hashing a password under UNIX. The first argument is the un-hashed password to be hashed. The second argument provides the method, salt and other arguments the hashing method needs to perform the hash. It does this in the form of the string that it generates a part of the hashed password. See "man 5 crypt" for details of the string format used for the hashed password and thus the string used as the second argument. Perl makes the system crypt() available on the CLI. The resulting string can then be compared to the stored password (that was used to hash the original password) to verify it. Or it can be saved away as the hashed copy of the users password. For example... perl -e 'print crypt( "heslo", "\$1\$salted\$" ), "\n"' # => $1$salted$FkAEbTCXKub0PsMYTmzZl1 perl -e 'print crypt( "heslo", "\$5\$round=12345\$salted\$" ), "\n"' # => $5$round=12345$WB8Z6UT1Lr.VURqrsbZY4zoCLRwH/FM6hNqeQowwTDD Some formats allow you to iterate the hash, multiple 'rounds', to slow down the hashing process. perl -e 'print crypt( "heslo", "\$5\$round=12345\$salted\$" ), "\n"' # => $5$round=12345$WB8Z6UT1Lr.VURqrsbZY4zoCLRwH/FM6hNqeQowwTDD Note how the first part of the second argument is also part of the result! The '$' in the resulting string separates that various arguments of the encryption. The whole string is then prefixed with 'loginname:', and any extra fields needed is added after a second ':', such as user info, date, and expiry times. The line is generally saved to the appropriate file like that. For example... user:$1$salted$FkAEbTCXKub0PsMYTmzZl1:100:100 User John:/home/user:/bin/sh As you can guess the salt, and generated hash, do not use '$' or ':' characters. Using mkpasswd mkpasswd -m sha256crypt Other options -S salt_to-use -R rounds Openssl can also do this openssl passwd # Des encryption (add -salt for compare) openssl passwd -1 # MD5 hashing function (random salt) openssl passwd -6 # SHA-512 password hashing Other options -salt salt-to-use Old Grub can also generate md5 passwords (grub v1) /sbin/grub --batch --device-map=/dev/null md5crypt heslo quit ------------------------------------------------------------------------------- OpenLDAP MD5 Password Hash.... That is OpenLDAP password is simply a direct md5 password saved using base64 without any salt! I hope they improve this situation. From "Exporting MD5 from LDAP to Shadow" http://lists.fedoraproject.org/pipermail/389-users/2009-January/008805.html A password in OpenLDAP slappasswd -h {MD5} -s "heslo" {MD5}lV2wuB7xmJtKTf6ugGGppg== Ignoring the padding, the password is a MD5 hash, that has been converted from hexadecimal to base64... direct convertion using md5 in PHP we get... echo '' | php lV2wuB7xmJtKTf6ugGGppg== The OpenLDAP passwd converted from base64 to hexadecimal... echo '' | php 955db0b81ef1989b4a4dfeae8061a9a6 which is the same as the md5 hash as hexadecimal... echo -n "heslo" | md5sum 955db0b81ef1989b4a4dfeae8061a9a6 echo -n "heslo"|openssl dgst -md5 -hex 955db0b81ef1989b4a4dfeae8061a9a6 php -r 'echo md5("heslo");' 955db0b81ef1989b4a4dfeae8061a9a6 ------------------------------------------------------------------------------- Argon2 Hashing There are 3 variants, argon2i, argon2d, argon2id Then they have arguments for parallelism (p), memory (m), iterations (t) version (which you should not need to worry about). Hashing a password (see shell below for more) echo -n "wand~odd~half~creek" | argon2 ThisIsASalt -t 10 -id -e # # => $argon2id$v=19$m=4096,t=10, # p=1$VGhpc0lzQVNhbHQ$GUiNMkT86QIO+DuyuRP49vd8yFenbpaDl1fRg08yeoo # above line is broken at comma for readability # the command argon2-cli can generate a salt if missing The above command always generates the same arguments and resulting hash Note the salt is base64 encoded between the two '$' after the "p=1" salt_base64=$( echo '...above string...' | grep -oP 'p=.\$\K[^$]+' ) echo "$salt_base64" # => VGhpc0lzQVNhbHQ salt=$( echo "$salt_base64" | b64decode ) echo "$salt" # => ThisIsASalt As usual password verification is by rehashing the password with the same arguments. There is a warning that you should use a verifier that always takes the same amount of time, and not return quickly on first miss-match. Optimal Parameters... Install argon python library dnf install python-argon2-cffi or pip install argon2-cffi and run... python3 -m argon2 which will give you some good parameters to use for your machine. Or open /usr/share/doc/python-argon2-cffi-doc/html/parameters.html =======8<--------CUT HERE---------- from argon2 import PasswordHasher from argon2.exceptions import VerifyMismatchError ph = PasswordHasher() password = 'Hello' passwordHash = '$argon2id$v=19$m=65536,t=4,p=2$Sk05cXZxbUhJQ1E5em02dQ$K8U9+ltV14jERcG5v8fMpcRFg75dz49AipKwEQseuoc' try: isValid = ph.verify(passwordHash, password) except VerifyMismatchError: isValid = False print(isValid) =======8<--------CUT HERE---------- # => True Shell "argon2" command Install dnf install argon2 echo 'passwd" | argon2 TheSalt Without parameters, this works out some parameters, the resulting binary hash in Hexidecimal, password file encoding of the hash. Note "argon2" command can't decode the encoded hash itself! Do do that you will need to first split encoding on '$' to get.. (undef, variant, version, options, salt, hash) = split(/\$/,encoded); then further separate variant, option values, and b64decode the salt. Arrrggghhh.... NOTES: * Salt must be given BEFORE other options! * v= is not the same as command line -v version. * Use -k for the m= value found the hash. The -m" is a power-of-2 value. EG: -m 12 -> 2^12 -> m=4096 in the encoded hash PHP =======8<--------CUT HERE----------