Skip to content

Commit 73fa25a

Browse files
author
Yasuo Ohgaki
committed
Timing safe str_compare() using SipHash
1 parent 363ff60 commit 73fa25a

File tree

3 files changed

+167
-0
lines changed

3 files changed

+167
-0
lines changed

ext/standard/basic_functions.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2496,6 +2496,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_substr_compare, 0, 0, 3)
24962496
ZEND_ARG_INFO(0, length)
24972497
ZEND_ARG_INFO(0, case_sensitivity)
24982498
ZEND_END_ARG_INFO()
2499+
2500+
ZEND_BEGIN_ARG_INFO_EX(arginfo_str_compare, 0, 0, 2)
2501+
ZEND_ARG_INFO(0, str1)
2502+
ZEND_ARG_INFO(0, str2)
2503+
ZEND_END_ARG_INFO()
24992504
/* }}} */
25002505
/* {{{ syslog.c */
25012506
#ifdef HAVE_SYSLOG_H
@@ -2773,6 +2778,7 @@ const zend_function_entry basic_functions[] = { /* {{{ */
27732778
PHP_FE(str_split, arginfo_str_split)
27742779
PHP_FE(strpbrk, arginfo_strpbrk)
27752780
PHP_FE(substr_compare, arginfo_substr_compare)
2781+
PHP_FE(str_compare, arginfo_str_compare)
27762782

27772783
#ifdef HAVE_STRCOLL
27782784
PHP_FE(strcoll, arginfo_strcoll)

ext/standard/php_string.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ PHP_FUNCTION(str_word_count);
9393
PHP_FUNCTION(str_split);
9494
PHP_FUNCTION(strpbrk);
9595
PHP_FUNCTION(substr_compare);
96+
PHP_FUNCTION(str_compare);
9697
#ifdef HAVE_STRCOLL
9798
PHP_FUNCTION(strcoll);
9899
#endif
@@ -147,6 +148,8 @@ PHPAPI int string_natural_compare_function_ex(zval *result, zval *op1, zval *op2
147148
PHPAPI int string_natural_compare_function(zval *result, zval *op1, zval *op2 TSRMLS_DC);
148149
PHPAPI int string_natural_case_compare_function(zval *result, zval *op1, zval *op2 TSRMLS_DC);
149150

151+
PHPAPI int php_byte_compare(const void *b1, const void *b2, size_t n);
152+
150153
#ifndef HAVE_STRERROR
151154
PHPAPI char *php_strerror(int errnum);
152155
#define strerror php_strerror

ext/standard/string.c

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5616,6 +5616,164 @@ PHP_FUNCTION(substr_compare)
56165616
}
56175617
/* }}} */
56185618

5619+
5620+
/* XXX: Just embed here for the time being
5621+
https://github.com/majek/csiphash/blob/master/csiphash.c
5622+
*/
5623+
/* <MIT License>
5624+
Copyright (c) 2013 Marek Majkowski <[email protected]>
5625+
5626+
Permission is hereby granted, free of charge, to any person obtaining a copy
5627+
of this software and associated documentation files (the "Software"), to deal
5628+
in the Software without restriction, including without limitation the rights
5629+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
5630+
copies of the Software, and to permit persons to whom the Software is
5631+
furnished to do so, subject to the following conditions:
5632+
5633+
The above copyright notice and this permission notice shall be included in
5634+
all copies or substantial portions of the Software.
5635+
5636+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
5637+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5638+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
5639+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
5640+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
5641+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
5642+
THE SOFTWARE.
5643+
</MIT License>
5644+
5645+
Original location:
5646+
https://github.com/majek/csiphash/
5647+
5648+
Solution inspired by code from:
5649+
Samuel Neves (supercop/crypto_auth/siphash24/little)
5650+
djb (supercop/crypto_auth/siphash24/little2)
5651+
Jean-Philippe Aumasson (https://131002.net/siphash/siphash24.c)
5652+
*/
5653+
5654+
/* #include <stdint.h> */
5655+
5656+
#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
5657+
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
5658+
# define _le64toh(x) ((uint64_t)(x))
5659+
#elif defined(_WIN32)
5660+
/* Windows is always little endian, unless you're on xbox360
5661+
http://msdn.microsoft.com/en-us/library/b0084kay(v=vs.80).aspx */
5662+
# define _le64toh(x) ((uint64_t)(x))
5663+
#elif defined(__APPLE__)
5664+
# include <libkern/OSByteOrder.h>
5665+
# define _le64toh(x) OSSwapLittleToHostInt64(x)
5666+
#else
5667+
5668+
/* See: http://sourceforge.net/p/predef/wiki/Endianness/ */
5669+
# if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
5670+
# include <sys/endian.h>
5671+
# else
5672+
# include <endian.h>
5673+
# endif
5674+
# if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \
5675+
__BYTE_ORDER == __LITTLE_ENDIAN
5676+
# define _le64toh(x) ((uint64_t)(x))
5677+
# else
5678+
# define _le64toh(x) le64toh(x)
5679+
# endif
5680+
5681+
#endif
5682+
5683+
5684+
#define ROTATE(x, b) (uint64_t)( ((x) << (b)) | ( (x) >> (64 - (b))) )
5685+
5686+
#define HALF_ROUND(a,b,c,d,s,t) \
5687+
a += b; c += d; \
5688+
b = ROTATE(b, s) ^ a; \
5689+
d = ROTATE(d, t) ^ c; \
5690+
a = ROTATE(a, 32);
5691+
5692+
#define DOUBLE_ROUND(v0,v1,v2,v3) \
5693+
HALF_ROUND(v0,v1,v2,v3,13,16); \
5694+
HALF_ROUND(v2,v1,v0,v3,17,21); \
5695+
HALF_ROUND(v0,v1,v2,v3,13,16); \
5696+
HALF_ROUND(v2,v1,v0,v3,17,21);
5697+
5698+
5699+
static uint64_t php_siphash(const void *src, unsigned long src_sz, const char key[16]) {
5700+
const uint64_t *_key = (uint64_t *)key;
5701+
uint64_t k0 = _le64toh(_key[0]);
5702+
uint64_t k1 = _le64toh(_key[1]);
5703+
uint64_t b = (uint64_t)src_sz << 56;
5704+
const uint64_t *in = (uint64_t*)src;
5705+
5706+
uint64_t v0 = k0 ^ 0x736f6d6570736575ULL;
5707+
uint64_t v1 = k1 ^ 0x646f72616e646f6dULL;
5708+
uint64_t v2 = k0 ^ 0x6c7967656e657261ULL;
5709+
uint64_t v3 = k1 ^ 0x7465646279746573ULL;
5710+
5711+
while (src_sz >= 8) {
5712+
uint64_t mi = _le64toh(*in);
5713+
in += 1; src_sz -= 8;
5714+
v3 ^= mi;
5715+
DOUBLE_ROUND(v0,v1,v2,v3);
5716+
v0 ^= mi;
5717+
}
5718+
5719+
uint64_t t = 0; uint8_t *pt = (uint8_t *)&t; uint8_t *m = (uint8_t *)in;
5720+
switch (src_sz) {
5721+
case 7: pt[6] = m[6];
5722+
case 6: pt[5] = m[5];
5723+
case 5: pt[4] = m[4];
5724+
case 4: *((uint32_t*)&pt[0]) = *((uint32_t*)&m[0]); break;
5725+
case 3: pt[2] = m[2];
5726+
case 2: pt[1] = m[1];
5727+
case 1: pt[0] = m[0];
5728+
}
5729+
b |= _le64toh(t);
5730+
5731+
v3 ^= b;
5732+
DOUBLE_ROUND(v0,v1,v2,v3);
5733+
v0 ^= b; v2 ^= 0xff;
5734+
DOUBLE_ROUND(v0,v1,v2,v3);
5735+
DOUBLE_ROUND(v0,v1,v2,v3);
5736+
return (v0 ^ v1) ^ (v2 ^ v3);
5737+
}
5738+
/* END SipHash */
5739+
5740+
/* Timing safe compare */
5741+
PHPAPI int php_compare(const void *b1, const void *b2, size_t n) /* {{{ */
5742+
{
5743+
const unsigned char *p1 = b1, *p2 = b2;
5744+
int ret = 0;
5745+
5746+
for (; n > 0; n--) {
5747+
ret |= *p1++ ^ *p2++;
5748+
}
5749+
return (ret != 0);
5750+
}
5751+
/* }}} */
5752+
5753+
/* {{{ proto bool str_compare(string str1, string str2)
5754+
Timing safe string compare */
5755+
PHP_FUNCTION(str_compare)
5756+
{
5757+
zval *s1, *s2;
5758+
char key[16] = {0,1,2,3,4,5,6,7,8,9,0xa,0xb,0xc,0xd,0xe,0xf};
5759+
5760+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &s1, &s2) == FAILURE) {
5761+
RETURN_FALSE;
5762+
}
5763+
5764+
if (Z_TYPE_P(s1) != IS_STRING || Z_TYPE_P(s2) != IS_STRING) {
5765+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Paremeters must be string");
5766+
RETURN_FALSE;
5767+
}
5768+
if (Z_STRLEN_P(s1) != Z_STRLEN_P(s2)) {
5769+
RETURN_FALSE;
5770+
}
5771+
5772+
5773+
RETURN_BOOL(php_siphash(Z_STRVAL_P(s1), Z_STRLEN_P(s1), key) == php_siphash(Z_STRVAL_P(s2), Z_STRLEN_P(s2), key));
5774+
}
5775+
/* }}} */
5776+
56195777
/*
56205778
* Local variables:
56215779
* tab-width: 4

0 commit comments

Comments
 (0)