1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
|
--- a/ext/standard/crypt_blowfish.c
+++ b/ext/standard/crypt_blowfish.c
@@ -7,6 +7,7 @@
* cracking removed.
*
* Written by Solar Designer <solar at openwall.com> in 1998-2002 and
+ * placed in the public domain. Quick self-test added in 2011 and also
* placed in the public domain.
*
* There's absolutely no warranty.
@@ -51,6 +52,13 @@
#define __CONST __const
#endif
+/*
+ * Please keep this enabled. We really don't want incompatible hashes to be
+ * produced. The performance cost of this quick self-test is around 0.6% at
+ * the "$2a$08" setting.
+ */
+#define BF_SELF_TEST
+
#ifdef __i386__
#define BF_ASM 0
#define BF_SCALE 1
@@ -63,6 +71,7 @@
#endif
typedef unsigned int BF_word;
+typedef signed int BF_word_signed;
/* Number of Blowfish rounds, this is also hardcoded into a few places */
#define BF_N 16
@@ -555,7 +564,8 @@ static void BF_swap(BF_word *x, int coun
} while (ptr < &data.ctx.S[3][0xFF]);
#endif
-static void BF_set_key(__CONST char *key, BF_key expanded, BF_key initial)
+static void BF_set_key(__CONST char *key, BF_key expanded, BF_key initial,
+ int sign_extension_bug)
{
__CONST char *ptr = key;
int i, j;
@@ -565,7 +575,10 @@ static void BF_set_key(__CONST char *key
tmp = 0;
for (j = 0; j < 4; j++) {
tmp <<= 8;
- tmp |= *ptr;
+ if (sign_extension_bug)
+ tmp |= (BF_word_signed)(signed char)*ptr;
+ else
+ tmp |= (unsigned char)*ptr;
if (!*ptr) ptr = key; else ptr++;
}
@@ -575,8 +588,9 @@ static void BF_set_key(__CONST char *key
}
}
-char *php_crypt_blowfish_rn(__CONST char *key, __CONST char *setting,
- char *output, int size)
+static char *BF_crypt(__CONST char *key, __CONST char *setting,
+ char *output, int size,
+ BF_word min)
{
#if BF_ASM
extern void _BF_body_r(BF_ctx *ctx);
@@ -602,7 +616,7 @@ char *php_crypt_blowfish_rn(__CONST char
if (setting[0] != '$' ||
setting[1] != '2' ||
- setting[2] != 'a' ||
+ (setting[2] != 'a' && setting[2] != 'x') ||
setting[3] != '$' ||
setting[4] < '0' || setting[4] > '3' ||
setting[5] < '0' || setting[5] > '9' ||
@@ -613,7 +627,7 @@ char *php_crypt_blowfish_rn(__CONST char
}
count = (BF_word)1 << ((setting[4] - '0') * 10 + (setting[5] - '0'));
- if (count < 16 || BF_decode(data.binary.salt, &setting[7], 16)) {
+ if (count < min || BF_decode(data.binary.salt, &setting[7], 16)) {
clean(data.binary.salt, sizeof(data.binary.salt));
__set_errno(EINVAL);
return NULL;
@@ -621,7 +635,7 @@ char *php_crypt_blowfish_rn(__CONST char
BF_swap(data.binary.salt, 4);
- BF_set_key(key, data.expanded_key, data.ctx.P);
+ BF_set_key(key, data.expanded_key, data.ctx.P, setting[2] == 'x');
memcpy(data.ctx.S, BF_init_state.S, sizeof(data.ctx.S));
@@ -721,14 +735,59 @@ char *php_crypt_blowfish_rn(__CONST char
BF_encode(&output[7 + 22], data.binary.output, 23);
output[7 + 22 + 31] = '\0';
+#ifndef BF_SELF_TEST
/* Overwrite the most obvious sensitive data we have on the stack. Note
* that this does not guarantee there's no sensitive data left on the
* stack and/or in registers; I'm not aware of portable code that does. */
clean(&data, sizeof(data));
+#endif
return output;
}
+char *php_crypt_blowfish_rn(__CONST char *key, __CONST char *setting,
+ char *output, int size)
+{
+#ifdef BF_SELF_TEST
+ __CONST char *test_key = "8b \xd0\xc1\xd2\xcf\xcc\xd8";
+ __CONST char *test_2a =
+ "$2a$00$abcdefghijklmnopqrstuui1D709vfamulimlGcq0qq3UvuUasvEa"
+ "\0"
+ "canary";
+ __CONST char *test_2x =
+ "$2x$00$abcdefghijklmnopqrstuuVUrPmXD6q/nVSSp7pNDhCR9071IfIRe"
+ "\0"
+ "canary";
+ __CONST char *test_hash, *p;
+ int ok;
+ char buf[7 + 22 + 31 + 1 + 6 + 1];
+
+ output = BF_crypt(key, setting, output, size, 16);
+
+/* Do a quick self-test. This also happens to overwrite BF_crypt()'s data. */
+ test_hash = (setting[2] == 'x') ? test_2x : test_2a;
+ memcpy(buf, test_hash, sizeof(buf));
+ memset(buf, -1, sizeof(buf) - (6 + 1)); /* keep "canary" only */
+ p = BF_crypt(test_key, test_hash, buf, sizeof(buf) - 6, 1);
+
+ ok = (p == buf && !memcmp(p, test_hash, sizeof(buf)));
+
+/* This could reveal what hash type we were using last. Unfortunately, we
+ * can't reliably clean the test_hash pointer. */
+ clean(&buf, sizeof(buf));
+
+ if (ok)
+ return output;
+
+/* Should not happen */
+ __set_errno(EINVAL); /* pretend we don't support this hash type */
+ return NULL;
+#else
+#warning Self-test is disabled, please enable
+ return BF_crypt(key, setting, output, size, 16);
+#endif
+}
+
char *php_crypt_gensalt_blowfish_rn(unsigned long count,
__CONST char *input, int size, char *output, int output_size)
{
--- a/ext/standard/crypt.c
+++ b/ext/standard/crypt.c
@@ -241,7 +241,7 @@ PHP_FUNCTION(crypt)
} else if (
salt[0] == '$' &&
salt[1] == '2' &&
- salt[2] == 'a' &&
+ (salt[2] == 'a' || salt[2] == 'x') &&
salt[3] == '$' &&
salt[6] == '$') {
/* CRYPT_BLOWFISH */
|