Hashing a password for unix shadow file - hash

So, I have this entry in a linux shadow file
google.:$1$8KdNUQ4R$ZYyUXVGhvLgVNpfqus.GX/::1:0:1:1:::
The password for this user is com
As I understand the $1$ means it is hashed using md5;
8KdNUQ4R is the salt; and ZYyUXVGhvLgVNpfqus.GX/ is the hashed password itself.
What confuses me is that md5 generators I found online produce a hex value that is 32 characters long, but the hashed password in this case is only 22 chars long, and definitely not a hex.
What steps do I have to go through to get from com to ZYyUXVGhvLgVNpfqus.GX/ using the given salt ?
Edit
So, I found my answer. My problem was that I should have used md5crypt instead of md5.

If you need to calculate hash for particular password use crypt(3):
#include <iostream>
#include <unistd.h>
int main()
{
std::cout << crypt( "com", "$1$8KdNUQ4R" ) << std::endl;
return 0;
}
If you want to know how to get it - result of md5 hash of salt + password is converted to string by base64.
slava#bird:~$ g++ crypt.cpp -lcrypt -o crypt_com
slava#bird:~$ ./crypt_com
$1$8KdNUQ4R$ZYyUXVGhvLgVNpfqus.GX/

Related

flask_bcrypt does not allow encoded (Byte) password with '\x00' in the middle

I have some code that accepts a password in string format, and hash it with SHA3-512 before passing it to flask_bcrypt for hashing. However, by some coincidence, I found a test case that produces a hash that contains '\x00' in the middle of the hash result, as seen below:
password_str = 'tes15!tes15!tes15!tes15!tes15!tes15!tes15!tes15!tes15!tes15!tes15!tes15!.'
password_bytes = password_str.encode('utf-8')
hashed = hashlib.sha3_512(password_bytes).digest()
The result of the hash is:
b'u~"\x98\xac\xc8E2eV\xbb\x8e#}\x92R\xdc\xa2\xab\xab\xcb\x8d.~\x9f\x82a\xbf\xec]k\xdb\xc55\x1d\xa4\x00\xe8\x03\x94\xb0\x91\x14\xf0\x9ec\x9a\x9ay\xfeP\xe3\x07J\x00\xb5\xbd\xba\xcb(\xf5\xdb\xab\x1a'
As we can see, there is an '\x00' found in the middle of the hash, but not at the end. flask_bcrypt detects this a ValueError exception:
if b"\x00" in password:
raise ValueError("password may not contain NUL bytes")
I understand that '\x00' is a reserved character and possible attack vector due to hash-length extension attacks. However, I do not think that I have done anything outside the ordinary. Is there a best practice that sanitizes the hash prior to passing it to bcrypt or flask_bcrypt that I am missing, or is there another type of byte-encoding that should be used with passwords?
It would be weird to reject a normal password just because it's derived hash conflicts with the auth library used.. Any help or advice is greatly appreciated. Thank you!
BCrypt was designed to hash user passwords, this is not the same as e.g. SHA which is meant to hash binary input like a file. A user entered password actually never contains a \0 character, after all it is the user who desides about his own password, not a hacker trying to exploit a security vulnerability.
In other words, you should pass in the password as string directly, and avoid the UTF-8 encoding which converts it to a binary value.

Perl Digest Bcrypt, generating a proper hash

I have written a test program that generates a Bcrypt hash. This hash later needs to be verified by a PHP backend.
This is my perl code:
use Digest;
#use Data::Entropy::Algorithms qw(rand_bits);
#my $bcrypt = Digest->new('Bcrypt', cost=>10, salt=>rand_bits(16*8));
my $bcrypt = Digest->new('Bcrypt', cost=>10, salt=>'1111111111111111');
my $settings = $bcrypt->settings(); # save for later checks.
my $pass_hash = $bcrypt->add('bob')->b64digest;
print $settings.$pass_hash."\n";
This prints
$2a$10$KRCvKRCvKRCvKRCvKRCvKOoFxCE1d/OZTKQqhet3bKOq6ZVIACXBU
This does not validate as a proper hash if I use an online bcrypt tool such as https://bcrypt-generator.com
Can someone point out the error? Thanks.
Figured out the problem. I have to use bcrypt_b64digest instead of b64digest. I wish the perl documentation was clearer in which one needs to be used so that other bcrypt implementations can "get it".
my $pass_hash = $bcrypt->add('bob')->bcrypt_b64digest;
From https://metacpan.org/pod/Digest::Bcrypt#bcrypt_b64digest
Same as "digest", but will return the digest base64 encoded using the
alphabet that is commonly used with bcrypt. The length of the returned
string will be 31 and will only contain characters from the ranges
'0'..'9', 'A'..'Z', 'a'..'z', '+', and '.'
The base64 encoded string returned is not padded to be a multiple of 4
bytes long. Note: This is bcrypt's own non-standard base64 alphabet,
It is not compatible with the standard MIME base64 encoding.

Dovecot password hashing

Can anyone tell me how the Dovecot administration tool (doveadm pw) hashes passwords when using SHA-512. $6$ indicates SHA-512, followed by a salt, then the hash. How exactly does Dovecot generate the salt? Does it use an own algorithm? As far as I can see it uses /dev/random or /dev/urandom, but how does it deal with non-ASCII characters?
Nevermind, found out in password-scheme.c.
It reads data from /dev/urandom and has an array with allowed characters (static const char salt_chars[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";).
The salt is generated by using one of the characters from that array; precisely, it takes the byte from /dev/urandom modulo the length of salt_chars - 1 and uses that as index to pick a char from salt_chars.

Play! hash password returns bad result

I'm using Play 1.2.1. I want to hash my users password. I thought that Crypto.passwordHash will be good, but it isn't. passwordHash documentation says it returns MD5 password hash. I created some user accounts in fixture, where I put md5 password hash:
...
User(admin):
login: admin
password: f1682b54de57d202ba947a0af26399fd
fullName: Administrator
...
The problem is, when I try to log in, with something like this:
user.password.equals(Crypto.passwordHash(password))
and it doesn't work. So I put a log statement in my autentify method:
Logger.info("\nUser hashed password is %s " +
"\nPassed password is %s " +
"\nHashed passed password is %s",
user.password, password, Crypto.passwordHash(password));
And the password hashes are indeed different, but hey! The output of passwordHash method isn't even an MD5 hash:
15:02:16,164 INFO ~
User hashed password is f1682b54de57d202ba947a0af26399fd
Passed password is <you don't have to know this :P>
Hashed passed password is 8WgrVN5X0gK6lHoK8mOZ/Q==
How about that? How to fix it? Or maybe I have to implement my own solution?
Crypto.passwordHash returns base64-encoded password hash, while you are comparing to hex-encoded.
MD5 outputs a sequence of 16 bytes, each byte having (potentially) any value between 0 and 255 (inclusive). When you want to print the value, you need to convert the bytes to a sequence of "printable characters". There are several possible conventions, the two main being hexadecimal and Base64.
In hexadecimal notation, each byte value is represented as two "hexadecimal digits": such a digit is either a decimal digit ('0' to '9') or a letter (from 'a' to 'f', case is irrelevant). The 16 bytes thus become 32 characters.
In Base64 encoding, each group of three successive bytes is encoded as four characters, taken in a list of 64 possible characters (digits, lowercase letters, uppercase letters, '+' and '/'). One or two final '=' signs may be added so that the encoded string consists in a number of characters which is multiple of 4.
Here, '8WgrVN5X0gK6lHoK8mOZ/Q==' is the Base64 encoding of a sequence of 16 bytes, the first one having value 241, the second one 104, then 43, and so on. In hexadecimal notation, the first byte would be represented by 'f1', the second by '68', the third by '2b'... and the hexadecimal notation of the complete sequence of 16 bytes is then 'f1682b54de57d202ba947a0af26399fd', the value that you expected.
The play.libs.Codec class contains methods for decoding and encoding Base64 and hexadecimal notations. It also contains Codec.hexMD5() which performs MD5 hashing and returns the value in hexadecimal notation instead of Base64.
as Nickolay said you are comparing Hex vs Base-64 strings. Also, I would recommend using BCrypt for that, not the Crypto tool of Play.

Verifying salted hashes with Perls unpack()

I'm trying to verify salted passwords with Perl and am stuck with unpack.
I've got a salted hashed password, e.g. for SHA256: SSHA256 = SHA256('password' + 'salt') + 'salt'
Base64 encoded that gets '
{SSHA256}eje4XIkY6sGakInA+loqtNzj+QUo3N7sEIsj3fNge5lzYWx0'.
I store this string in my user database. When a user logs in need to separate the salt from the hash to hash the supplied password with the salt and compare the result to the one retrieved from the db. This is where I'm stuck. I don't seem to have the right unpack template separate the hash (8-bit binary, fixed length, in this case 32 byte) from the salt (8-bit binary, variable length).
I have tried something like
my ($hash, $salt) = unpack('N32 N*', $data);
but that doesn't seem to work out.
My question is: How can I unpack this hash (after it has been Base64 decoded) to get the fixed length hash in one and the variable length salt in another variable?
I think you're needlessly re-inventing the wheel.
You could use e.g. Crypt::SaltedHash to easily verify it, for instance:
my $password_entered = $cgi->param('password');
my $valid = Crypt::SaltedHash->validate($salted, $password_entered);
A longer example, showing using Crypt::SaltedHash to generate the salted password in the first instance, too:
my $csh = Crypt::SaltedHash->new(algorithm => 'SHA-256');
$csh->add('secretpassword');
my $salted = $csh->generate;
# $salted will contain the salted hash (Crypt::SaltedHash picks random
# salt for you automatically)
# for example:
DB x $salted = $csh->generate;
0 '{SSHA256}H1WaxHcyAB81iyIPwib/cCUtjqCm2sxQNA1QvGeh/iT3m51w'
# validating that against the plaintext 'secretpassword' shows it's right:
DB x Crypt::SaltedHash->validate($salted, 'secretpassword');
0 1
# and trying it with an incorrect password:
DB x Crypt::SaltedHash->validate($salted, 'wrongpassword');
0 ''
No reason to re-invent all of this yourself.
You seem to be doing RFC2307 the hard way and also manage to introduce bugs. Those + do not mean what you think.
Subclass Authen::Passphrase::SaltedDigest instead.
Not sure the whole picture is present, but the unpack template you have specified -'N32 N*'- is for 32 unsigned long (32-bit) (big-endian) integers (see pack docs).
Looks like you may instead need unsigned chars: '32C C*'