From e331f9af73ba6e34fd757f368fdf0d9a585f763c Mon Sep 17 00:00:00 2001 From: Leigh Date: Tue, 7 Oct 2014 11:23:24 +0100 Subject: [PATCH 1/4] Upgrade crypt_blowfish to version 1.3 --- ext/standard/crypt_blowfish.c | 43 +++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/ext/standard/crypt_blowfish.c b/ext/standard/crypt_blowfish.c index e010352b55e..43f35f661e9 100644 --- a/ext/standard/crypt_blowfish.c +++ b/ext/standard/crypt_blowfish.c @@ -8,11 +8,11 @@ * and crypt(3) interfaces added, but optimizations specific to password * cracking removed. * - * Written by Solar Designer in 1998-2011. + * Written by Solar Designer in 1998-2014. * No copyright is claimed, and the software is hereby placed in the public * domain. In case this attempt to disclaim copyright and place the software * in the public domain is deemed null and void, then the software is - * Copyright (c) 1998-2011 Solar Designer and it is hereby released to the + * Copyright (c) 1998-2014 Solar Designer and it is hereby released to the * general public under the following terms: * * Redistribution and use in source and binary forms, with or without @@ -28,12 +28,12 @@ * you place this code and any modifications you make under a license * of your choice. * - * This implementation is mostly compatible with OpenBSD's bcrypt.c (prefix - * "$2a$") by Niels Provos , and uses some of his - * ideas. The password hashing algorithm was designed by David Mazieres - * . For more information on the level of compatibility, - * please refer to the comments in BF_set_key() below and to the crypt(3) - * man page included in the crypt_blowfish tarball. + * This implementation is fully compatible with OpenBSD's bcrypt.c for prefix + * "$2b$", originally by Niels Provos , and it uses + * some of his ideas. The password hashing algorithm was designed by David + * Mazieres . For information on the level of + * compatibility for bcrypt hash prefixes other than "$2b$", please refer to + * the comments in BF_set_key() below and to the included crypt(3) man page. * * There's a paper on the algorithm that explains its design decisions: * @@ -583,6 +583,7 @@ static void BF_set_key(const char *key, BF_key expanded, BF_key initial, * Valid combinations of settings are: * * Prefix "$2a$": bug = 0, safety = 0x10000 + * Prefix "$2b$": bug = 0, safety = 0 * Prefix "$2x$": bug = 1, safety = 0 * Prefix "$2y$": bug = 0, safety = 0 */ @@ -646,6 +647,10 @@ static void BF_set_key(const char *key, BF_key expanded, BF_key initial, initial[0] ^= sign; } +static const unsigned char flags_by_subtype[26] = + {2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 0}; + static char *BF_crypt(const char *key, const char *setting, char *output, int size, BF_word min) @@ -653,9 +658,6 @@ static char *BF_crypt(const char *key, const char *setting, #if BF_ASM extern void _BF_body_r(BF_ctx *ctx); #endif - static const unsigned char flags_by_subtype[26] = - {2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 0}; struct { BF_ctx ctx; BF_key expanded_key; @@ -821,9 +823,10 @@ char *php_crypt_blowfish_rn(const char *key, const char *setting, { const char *test_key = "8b \xd0\xc1\xd2\xcf\xcc\xd8"; const char *test_setting = "$2a$00$abcdefghijklmnopqrstuu"; - static const char * const test_hash[2] = - {"VUrPmXD6q/nVSSp7pNDhCR9071IfIRe\0\x55", /* $2x$ */ - "i1D709vfamulimlGcq0qq3UvuUasvEa\0\x55"}; /* $2a$, $2y$ */ + static const char * const test_hashes[2] = + {"i1D709vfamulimlGcq0qq3UvuUasvEa\0\x55", /* 'a', 'b', 'y' */ + "VUrPmXD6q/nVSSp7pNDhCR9071IfIRe\0\x55"}; /* 'x' */ + const char *test_hash = test_hashes[0]; char *retval; const char *p; int save_errno, ok; @@ -845,17 +848,19 @@ char *php_crypt_blowfish_rn(const char *key, const char *setting, * detected by the self-test. */ memcpy(buf.s, test_setting, sizeof(buf.s)); - if (retval) + if (retval) { + unsigned int flags = flags_by_subtype[ + (unsigned int)(unsigned char)setting[2] - 'a']; + test_hash = test_hashes[flags & 1]; buf.s[2] = setting[2]; + } memset(buf.o, 0x55, sizeof(buf.o)); buf.o[sizeof(buf.o) - 1] = 0; p = BF_crypt(test_key, buf.s, buf.o, sizeof(buf.o) - (1 + 1), 1); ok = (p == buf.o && !memcmp(p, buf.s, 7 + 22) && - !memcmp(p + (7 + 22), - test_hash[(unsigned int)(unsigned char)buf.s[2] & 1], - 31 + 1 + 1 + 1)); + !memcmp(p + (7 + 22), test_hash, 31 + 1 + 1 + 1)); { const char *k = "\xff\xa3" "34" "\xff\xff\xff\xa3" "345"; @@ -885,7 +890,7 @@ char *_crypt_gensalt_blowfish_rn(const char *prefix, unsigned long count, if (size < 16 || output_size < 7 + 22 + 1 || (count && (count < 4 || count > 31)) || prefix[0] != '$' || prefix[1] != '2' || - (prefix[2] != 'a' && prefix[2] != 'y')) { + (prefix[2] != 'a' && prefix[2] != 'b' && prefix[2] != 'y')) { if (output_size > 0) output[0] = '\0'; __set_errno((output_size < 7 + 22 + 1) ? ERANGE : EINVAL); return NULL; From a0420a72afe064d3cb04735c0966eba053a868a7 Mon Sep 17 00:00:00 2001 From: Leigh Date: Tue, 7 Oct 2014 11:50:36 +0100 Subject: [PATCH 2/4] Add tests from 1.3. Add missing tests. 3 of the missing tests fail. // TODO --- .../tests/strings/crypt_blowfish.phpt | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/ext/standard/tests/strings/crypt_blowfish.phpt b/ext/standard/tests/strings/crypt_blowfish.phpt index 20a6a2750af..2bb2b5ff34e 100644 --- a/ext/standard/tests/strings/crypt_blowfish.phpt +++ b/ext/standard/tests/strings/crypt_blowfish.phpt @@ -18,8 +18,10 @@ $tests =array( array('$2x$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e', "\xff\xff\xa3"), array('$2y$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e', "\xff\xff\xa3"), array('$2a$05$/OK.fbVrR/bpIqNJ5ianF.nqd1wy.pTMdcvrRWxyiGL2eMz.2a85.', "\xff\xff\xa3"), + array('$2b$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e', "\xff\xff\xa3"), array('$2y$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq', "\xa3"), array('$2a$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq', "\xa3"), + array('$2b$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq', "\xa3"), array('$2x$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi', "1\xa3345"), array('$2x$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi', "\xff\xa3345"), array('$2x$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi', "\xff\xa334\xff\xff\xff\xa3345"), @@ -36,14 +38,35 @@ $tests =array( array('$2a$05$/OK.fbVrR/bpIqNJ5ianF.R9xrDjiycxMbQE2bp.vgqlYpW5wx2yy', "\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"), array('$2a$05$/OK.fbVrR/bpIqNJ5ianF.9tQZzcJfm3uj2NvJ/n5xkhpqLrMpWCe', "\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"), array('$2a$05$CCCCCCCCCCCCCCCCCCCCC.7uG0VCzI2bS7j6ymqJi9CdcdxiRTWNy', ''), - ); + +$tests2 = array( + array('$2a$03$CCCCCCCCCCCCCCCCCCCCC.', '*0'), + array('$2a$32$CCCCCCCCCCCCCCCCCCCCC.', '*0'), + array('$2c$05$CCCCCCCCCCCCCCCCCCCCC.', '*0'), + array('$2z$05$CCCCCCCCCCCCCCCCCCCCC.', '*0'), + array('$2`$05$CCCCCCCCCCCCCCCCCCCCC.', '*0'), + array('$2{$05$CCCCCCCCCCCCCCCCCCCCC.', '*0'), + array('*0', '*1'), +); + $i=0; foreach($tests as $test) { - if(crypt($test[1], $test[0]) == $test[0]) { + $result = crypt($test[1], $test[0]); + if($result === $test[0]) { echo "$i. OK\n"; } else { - echo "$i. Not OK: $test[0] ".crypt($test[1], $test[0])."\n"; + echo "$i. Not OK: $test[0] $result\n"; + } + $i++; +} + +foreach($tests2 as $test) { + $result = crypt('', $test[0]); + if($result === $test[1]) { + echo "$i. OK\n"; + } else { + echo "$i. Not OK: $test[0] $result\n"; } $i++; } @@ -76,3 +99,9 @@ foreach($tests as $test) { 23. OK 24. OK 25. OK +26. OK +27. OK +28. OK +29. OK +30. OK +31. OK From 4e8c8761206f595691eaeee8a179fa9be286dcf3 Mon Sep 17 00:00:00 2001 From: Leigh Date: Tue, 7 Oct 2014 12:27:57 +0100 Subject: [PATCH 3/4] Bug fixes in light of failing bcrypt tests --- ext/standard/crypt.c | 3 +-- ext/standard/tests/strings/crypt_blowfish.phpt | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ext/standard/crypt.c b/ext/standard/crypt.c index 8750378b53e..f9920426eaf 100644 --- a/ext/standard/crypt.c +++ b/ext/standard/crypt.c @@ -196,7 +196,6 @@ PHPAPI int php_crypt(const char *password, const int pass_len, const char *salt, } else if ( salt[0] == '$' && salt[1] == '2' && - salt[2] >= 'a' && salt[2] <= 'z' && salt[3] == '$' && salt[4] >= '0' && salt[4] <= '3' && salt[5] >= '0' && salt[5] <= '9' && @@ -219,7 +218,7 @@ PHPAPI int php_crypt(const char *password, const int pass_len, const char *salt, _crypt_extended_init_r(); crypt_res = _crypt_extended_r(password, salt, &buffer); - if (!crypt_res) { + if (!crypt_res || (salt[0] == '*' && salt[1] == '0')) { return FAILURE; } else { *result = estrdup(crypt_res); diff --git a/ext/standard/tests/strings/crypt_blowfish.phpt b/ext/standard/tests/strings/crypt_blowfish.phpt index 2bb2b5ff34e..0bf0d1949e1 100644 --- a/ext/standard/tests/strings/crypt_blowfish.phpt +++ b/ext/standard/tests/strings/crypt_blowfish.phpt @@ -105,3 +105,6 @@ foreach($tests2 as $test) { 29. OK 30. OK 31. OK +32. OK +33. OK +34. OK From f66013df94f9555f73aea4dd48aee668701f87d9 Mon Sep 17 00:00:00 2001 From: Leigh Date: Tue, 7 Oct 2014 13:12:38 +0100 Subject: [PATCH 4/4] Apply error-code-salt fix to Windows too --- ext/standard/crypt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/standard/crypt.c b/ext/standard/crypt.c index f9920426eaf..40b9e6d3b6a 100644 --- a/ext/standard/crypt.c +++ b/ext/standard/crypt.c @@ -239,7 +239,7 @@ PHPAPI int php_crypt(const char *password, const int pass_len, const char *salt, # error Data struct used by crypt_r() is unknown. Please report. # endif crypt_res = crypt_r(password, salt, &buffer); - if (!crypt_res) { + if (!crypt_res || (salt[0] == '*' && salt[1] == '0')) { return FAILURE; } else { *result = estrdup(crypt_res);