Skip to content

Commit bfe6f9e

Browse files
committed
Introduction of timing attack safe bcmp implementation.
Nothing new but to refactor usage b/w hash and password extensions but using volatile pointers to be a bit safer, allowing to expand its usage eventually.
1 parent 8aa7e20 commit bfe6f9e

File tree

6 files changed

+52
-19
lines changed

6 files changed

+52
-19
lines changed

configure.ac

+1-1
Original file line numberDiff line numberDiff line change
@@ -1616,7 +1616,7 @@ PHP_ADD_SOURCES(main, main.c snprintf.c spprintf.c \
16161616
php_ini_builder.c \
16171617
php_ini.c SAPI.c rfc1867.c php_content_types.c strlcpy.c \
16181618
strlcat.c explicit_bzero.c reentrancy.c php_variables.c php_ticks.c \
1619-
network.c php_open_temporary_file.c php_odbc_utils.c \
1619+
network.c php_open_temporary_file.c php_odbc_utils.c safe_bcmp.c \
16201620
output.c getopt.c php_syslog.c, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
16211621

16221622
PHP_ADD_SOURCES_X(main, fastcgi.c, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1, PHP_FASTCGI_OBJS, no)

ext/hash/hash.c

+1-12
Original file line numberDiff line numberDiff line change
@@ -1111,9 +1111,7 @@ PHP_FUNCTION(hash_pbkdf2)
11111111
PHP_FUNCTION(hash_equals)
11121112
{
11131113
zval *known_zval, *user_zval;
1114-
char *known_str, *user_str;
11151114
int result = 0;
1116-
size_t j;
11171115

11181116
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &known_zval, &user_zval) == FAILURE) {
11191117
RETURN_THROWS();
@@ -1130,17 +1128,8 @@ PHP_FUNCTION(hash_equals)
11301128
RETURN_THROWS();
11311129
}
11321130

1133-
if (Z_STRLEN_P(known_zval) != Z_STRLEN_P(user_zval)) {
1134-
RETURN_FALSE;
1135-
}
1136-
1137-
known_str = Z_STRVAL_P(known_zval);
1138-
user_str = Z_STRVAL_P(user_zval);
1139-
11401131
/* This is security sensitive code. Do not optimize this for speed. */
1141-
for (j = 0; j < Z_STRLEN_P(known_zval); j++) {
1142-
result |= known_str[j] ^ user_str[j];
1143-
}
1132+
result = php_safe_bcmp(Z_STR_P(known_zval), Z_STR_P(user_zval));
11441133

11451134
RETURN_BOOL(0 == result);
11461135
}

ext/standard/password.c

+2-5
Original file line numberDiff line numberDiff line change
@@ -152,15 +152,14 @@ static bool php_password_bcrypt_needs_rehash(const zend_string *hash, zend_array
152152
}
153153

154154
static bool php_password_bcrypt_verify(const zend_string *password, const zend_string *hash) {
155-
size_t i;
156155
int status = 0;
157156
zend_string *ret = php_crypt(ZSTR_VAL(password), (int)ZSTR_LEN(password), ZSTR_VAL(hash), (int)ZSTR_LEN(hash), 1);
158157

159158
if (!ret) {
160159
return 0;
161160
}
162161

163-
if (ZSTR_LEN(ret) != ZSTR_LEN(hash) || ZSTR_LEN(hash) < 13) {
162+
if (ZSTR_LEN(hash) < 13) {
164163
zend_string_free(ret);
165164
return 0;
166165
}
@@ -169,9 +168,7 @@ static bool php_password_bcrypt_verify(const zend_string *password, const zend_s
169168
* resistance towards timing attacks. This is a constant time
170169
* equality check that will always check every byte of both
171170
* values. */
172-
for (i = 0; i < ZSTR_LEN(hash); i++) {
173-
status |= (ZSTR_VAL(ret)[i] ^ ZSTR_VAL(hash)[i]);
174-
}
171+
status = php_safe_bcmp(ret, hash);
175172

176173
zend_string_free(ret);
177174
return status == 0;

main/php.h

+4
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,10 @@ END_EXTERN_C()
179179
#define explicit_bzero php_explicit_bzero
180180
#endif
181181

182+
BEGIN_EXTERN_C()
183+
PHPAPI int php_safe_bcmp(const zend_string *a, const zend_string *b);
184+
END_EXTERN_C()
185+
182186
#ifndef HAVE_STRTOK_R
183187
BEGIN_EXTERN_C()
184188
char *strtok_r(char *s, const char *delim, char **last);

main/safe_bcmp.c

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
+----------------------------------------------------------------------+
3+
| Copyright (c) The PHP Group |
4+
+----------------------------------------------------------------------+
5+
| This source file is subject to version 3.01 of the PHP license, |
6+
| that is bundled with this package in the file LICENSE, and is |
7+
| available through the world-wide-web at the following url: |
8+
| http://www.php.net/license/3_01.txt |
9+
| If you did not receive a copy of the PHP license and are unable to |
10+
| obtain it through the world-wide-web, please send a note to |
11+
| [email protected] so we can mail you a copy immediately. |
12+
+----------------------------------------------------------------------+
13+
| Author: David Carlier <[email protected]> |
14+
+----------------------------------------------------------------------+
15+
*/
16+
17+
#include "php.h"
18+
19+
#include <string.h>
20+
21+
/*
22+
* Returns 0 if both inputs match, 1 if they don't.
23+
* Returns -1 early if inputs do not have the same lengths.
24+
*
25+
*/
26+
PHPAPI int php_safe_bcmp(const zend_string *a, const zend_string *b)
27+
{
28+
const volatile unsigned char *ua = (const volatile unsigned char *)ZSTR_VAL(a);
29+
const volatile unsigned char *ub = (const volatile unsigned char *)ZSTR_VAL(b);
30+
size_t i = 0;
31+
int r = 0;
32+
33+
if (ZSTR_LEN(a) != ZSTR_LEN(b)) {
34+
return -1;
35+
}
36+
37+
while (i < ZSTR_LEN(a)) {
38+
r |= ua[i] ^ ub[i];
39+
++i;
40+
}
41+
42+
return r;
43+
}

win32/build/config.w32

+1-1
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ ADD_SOURCES("main", "main.c snprintf.c spprintf.c getopt.c fopen_wrappers.c \
265265
php_scandir.c php_ini.c SAPI.c rfc1867.c php_content_types.c strlcpy.c \
266266
strlcat.c reentrancy.c php_variables.c php_ticks.c network.c \
267267
php_open_temporary_file.c output.c internal_functions.c \
268-
php_syslog.c php_odbc_utils.c");
268+
php_syslog.c php_odbc_utils.c safe_bcmp.c");
269269
ADD_FLAG("CFLAGS_BD_MAIN", "/D ZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
270270
if (VS_TOOLSET && VCVERS >= 1914) {
271271
ADD_FLAG("CFLAGS_BD_MAIN", "/d2FuncCache1");

0 commit comments

Comments
 (0)