Browse Source

Add hash_equals() to perform string comparisons that are not vulnerable to timing attacks.

pull/608/head
Rouven Weßling 13 years ago
parent
commit
2148a88b3d
  1. 2
      NEWS
  2. 6
      UPGRADING
  3. 46
      ext/hash/hash.c
  4. 1
      ext/hash/php_hash.h
  5. 43
      ext/hash/tests/hash_equals.phpt

2
NEWS

@ -30,6 +30,8 @@ PHP NEWS
- Hash:
. Fixed bug #66698 (Missing FNV1a32 and FNV1a64 hash functions).
(Michael M Slusarz).
. Implemented timing attack safe string comparison function
(RFC: https://wiki.php.net/rfc/timing_attack). (Rouven Weßling)
- Intl:
. Fixed bug #66873 (A reproductible crash in UConverter when given invalid

6
UPGRADING

@ -71,6 +71,9 @@ PHP 5.6 UPGRADE NOTES
- Added use function and use const.
(https://wiki.php.net/rfc/use_function)
- Added a function for timing attack safe string comparison
(https://wiki.php.net/rfc/timing_attack)
- Added gost-crypto (CryptoPro S-box) hash algorithm.
- Stream wrappers verify peer certificates and host names by default in
@ -182,6 +185,9 @@ PHP 5.6 UPGRADE NOTES
5. New Functions
========================================
- Hash
Added hash_equals($known_string, $user_string)
- GMP:
Added gmp_root($a, $nth) and gmp_rootrem($a, $nth) for calculating nth roots.

46
ext/hash/hash.c

@ -728,6 +728,46 @@ PHP_FUNCTION(hash_pbkdf2)
}
/* }}} */
/* {{{ proto bool hash_equals(string known_string, string user_string)
Compares two strings using the same time whether they're equal or not.
A difference in length will leak */
PHP_FUNCTION(hash_equals)
{
zval *known_zval, *user_zval;
char *known_str, *user_str;
int result = 0, j;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &known_zval, &user_zval) == FAILURE) {
return;
}
/* We only allow comparing string to prevent unexpected results. */
if (Z_TYPE_P(known_zval) != IS_STRING) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected known_string to be a string, %s given", zend_zval_type_name(known_zval));
RETURN_FALSE;
}
if (Z_TYPE_P(user_zval) != IS_STRING) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected user_string to be a string, %s given", zend_zval_type_name(user_zval));
RETURN_FALSE;
}
if (Z_STRLEN_P(known_zval) != Z_STRLEN_P(user_zval)) {
RETURN_FALSE;
}
known_str = Z_STRVAL_P(known_zval);
user_str = Z_STRVAL_P(user_zval);
/* This is security sensitive code. Do not optimize this for speed. */
for (j = 0; j < Z_STRLEN_P(known_zval); j++) {
result |= known_str[j] ^ user_str[j];
}
RETURN_BOOL(0 == result);
}
/* }}} */
/* Module Housekeeping */
static void php_hash_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
@ -1152,6 +1192,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_hash_pbkdf2, 0, 0, 4)
ZEND_ARG_INFO(0, raw_output)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_hash_equals, 0)
ZEND_ARG_INFO(0, known_string)
ZEND_ARG_INFO(0, user_string)
ZEND_END_ARG_INFO()
/* BC Land */
#ifdef PHP_MHASH_BC
ZEND_BEGIN_ARG_INFO(arginfo_mhash_get_block_size, 0)
@ -1199,6 +1244,7 @@ const zend_function_entry hash_functions[] = {
PHP_FE(hash_algos, arginfo_hash_algos)
PHP_FE(hash_pbkdf2, arginfo_hash_pbkdf2)
PHP_FE(hash_equals, arginfo_hash_equals)
/* BC Land */
#ifdef PHP_HASH_MD5_NOT_IN_CORE

1
ext/hash/php_hash.h

@ -136,6 +136,7 @@ PHP_FUNCTION(hash_update_file);
PHP_FUNCTION(hash_final);
PHP_FUNCTION(hash_algos);
PHP_FUNCTION(hash_pbkdf2);
PHP_FUNCTION(hash_equals);
PHP_HASH_API const php_hash_ops *php_hash_fetch_ops(const char *algo, int algo_len);
PHP_HASH_API void php_hash_register_algo(const char *algo, const php_hash_ops *ops);

43
ext/hash/tests/hash_equals.phpt

@ -0,0 +1,43 @@
--TEST--
hash_equals() function
--FILE--
<?php
var_dump(hash_equals("same", "same"));
var_dump(hash_equals("not1same", "not2same"));
var_dump(hash_equals("short", "longer"));
var_dump(hash_equals("longer", "short"));
var_dump(hash_equals("", "notempty"));
var_dump(hash_equals("notempty", ""));
var_dump(hash_equals("", ""));
var_dump(hash_equals(123, "NaN"));
var_dump(hash_equals("NaN", 123));
var_dump(hash_equals(123, 123));
var_dump(hash_equals(null, ""));
var_dump(hash_equals(null, 123));
var_dump(hash_equals(null, null));
--EXPECTF--
bool(true)
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
bool(true)
Warning: hash_equals(): Expected known_string to be a string, integer given in %s on line %d
bool(false)
Warning: hash_equals(): Expected user_string to be a string, integer given in %s on line %d
bool(false)
Warning: hash_equals(): Expected known_string to be a string, integer given in %s on line %d
bool(false)
Warning: hash_equals(): Expected known_string to be a string, null given in %s on line %d
bool(false)
Warning: hash_equals(): Expected known_string to be a string, null given in %s on line %d
bool(false)
Warning: hash_equals(): Expected known_string to be a string, null given in %s on line %d
bool(false)
Loading…
Cancel
Save