Skip to content

Commit f8cf388

Browse files
author
Yasuo Ohgaki
committed
Add str_*_compare() functions to play with
1 parent 73fa25a commit f8cf388

File tree

6 files changed

+689
-108
lines changed

6 files changed

+689
-108
lines changed

ext/standard/basic_functions.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2778,6 +2778,10 @@ const zend_function_entry basic_functions[] = { /* {{{ */
27782778
PHP_FE(str_split, arginfo_str_split)
27792779
PHP_FE(strpbrk, arginfo_strpbrk)
27802780
PHP_FE(substr_compare, arginfo_substr_compare)
2781+
PHP_FE(str_siphash_compare, arginfo_str_compare)
2782+
PHP_FE(str_xxhash32_compare, arginfo_str_compare)
2783+
PHP_FE(str_md5_compare, arginfo_str_compare)
2784+
PHP_FE(str_byte_compare, arginfo_str_compare)
27812785
PHP_FE(str_compare, arginfo_str_compare)
27822786

27832787
#ifdef HAVE_STRCOLL

ext/standard/php_string.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ PHP_FUNCTION(str_word_count);
9393
PHP_FUNCTION(str_split);
9494
PHP_FUNCTION(strpbrk);
9595
PHP_FUNCTION(substr_compare);
96+
PHP_FUNCTION(str_siphash_compare);
97+
PHP_FUNCTION(str_xxhash32_compare);
98+
PHP_FUNCTION(str_md5_compare);
99+
PHP_FUNCTION(str_byte_compare);
96100
PHP_FUNCTION(str_compare);
97101
#ifdef HAVE_STRCOLL
98102
PHP_FUNCTION(strcoll);

ext/standard/siphash.c

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/* <MIT License>
2+
Copyright (c) 2013 Marek Majkowski <[email protected]>
3+
4+
Permission is hereby granted, free of charge, to any person obtaining a copy
5+
of this software and associated documentation files (the "Software"), to deal
6+
in the Software without restriction, including without limitation the rights
7+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
copies of the Software, and to permit persons to whom the Software is
9+
furnished to do so, subject to the following conditions:
10+
11+
The above copyright notice and this permission notice shall be included in
12+
all copies or substantial portions of the Software.
13+
14+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+
THE SOFTWARE.
21+
</MIT License>
22+
23+
Original location:
24+
https://github.com/majek/csiphash/
25+
26+
Solution inspired by code from:
27+
Samuel Neves (supercop/crypto_auth/siphash24/little)
28+
djb (supercop/crypto_auth/siphash24/little2)
29+
Jean-Philippe Aumasson (https://131002.net/siphash/siphash24.c)
30+
*/
31+
32+
#include <stdint.h>
33+
34+
#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
35+
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
36+
# define _le64toh(x) ((uint64_t)(x))
37+
#elif defined(_WIN32)
38+
/* Windows is always little endian, unless you're on xbox360
39+
http://msdn.microsoft.com/en-us/library/b0084kay(v=vs.80).aspx */
40+
# define _le64toh(x) ((uint64_t)(x))
41+
#elif defined(__APPLE__)
42+
# include <libkern/OSByteOrder.h>
43+
# define _le64toh(x) OSSwapLittleToHostInt64(x)
44+
#else
45+
46+
/* See: http://sourceforge.net/p/predef/wiki/Endianness/ */
47+
# if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
48+
# include <sys/endian.h>
49+
# else
50+
# include <endian.h>
51+
# endif
52+
# if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \
53+
__BYTE_ORDER == __LITTLE_ENDIAN
54+
# define _le64toh(x) ((uint64_t)(x))
55+
# else
56+
# define _le64toh(x) le64toh(x)
57+
# endif
58+
59+
#endif
60+
61+
62+
#define ROTATE(x, b) (uint64_t)( ((x) << (b)) | ( (x) >> (64 - (b))) )
63+
64+
#define HALF_ROUND(a,b,c,d,s,t) \
65+
a += b; c += d; \
66+
b = ROTATE(b, s) ^ a; \
67+
d = ROTATE(d, t) ^ c; \
68+
a = ROTATE(a, 32);
69+
70+
#define DOUBLE_ROUND(v0,v1,v2,v3) \
71+
HALF_ROUND(v0,v1,v2,v3,13,16); \
72+
HALF_ROUND(v2,v1,v0,v3,17,21); \
73+
HALF_ROUND(v0,v1,v2,v3,13,16); \
74+
HALF_ROUND(v2,v1,v0,v3,17,21);
75+
76+
77+
uint64_t php_siphash(const void *src, unsigned long src_sz, const char key[16]) {
78+
const uint64_t *_key = (uint64_t *)key;
79+
uint64_t k0 = _le64toh(_key[0]);
80+
uint64_t k1 = _le64toh(_key[1]);
81+
uint64_t b = (uint64_t)src_sz << 56;
82+
const uint64_t *in = (uint64_t*)src;
83+
84+
uint64_t v0 = k0 ^ 0x736f6d6570736575ULL;
85+
uint64_t v1 = k1 ^ 0x646f72616e646f6dULL;
86+
uint64_t v2 = k0 ^ 0x6c7967656e657261ULL;
87+
uint64_t v3 = k1 ^ 0x7465646279746573ULL;
88+
89+
while (src_sz >= 8) {
90+
uint64_t mi = _le64toh(*in);
91+
in += 1; src_sz -= 8;
92+
v3 ^= mi;
93+
DOUBLE_ROUND(v0,v1,v2,v3);
94+
v0 ^= mi;
95+
}
96+
97+
uint64_t t = 0; uint8_t *pt = (uint8_t *)&t; uint8_t *m = (uint8_t *)in;
98+
switch (src_sz) {
99+
case 7: pt[6] = m[6];
100+
case 6: pt[5] = m[5];
101+
case 5: pt[4] = m[4];
102+
case 4: *((uint32_t*)&pt[0]) = *((uint32_t*)&m[0]); break;
103+
case 3: pt[2] = m[2];
104+
case 2: pt[1] = m[1];
105+
case 1: pt[0] = m[0];
106+
}
107+
b |= _le64toh(t);
108+
109+
v3 ^= b;
110+
DOUBLE_ROUND(v0,v1,v2,v3);
111+
v0 ^= b; v2 ^= 0xff;
112+
DOUBLE_ROUND(v0,v1,v2,v3);
113+
DOUBLE_ROUND(v0,v1,v2,v3);
114+
return (v0 ^ v1) ^ (v2 ^ v3);
115+
}
116+

ext/standard/string.c

Lines changed: 95 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
#include "php_rand.h"
2828
#include "php_string.h"
2929
#include "php_variables.h"
30+
#include "md5.h"
31+
#include "xxhash.h"
3032
#ifdef HAVE_LOCALE_H
3133
# include <locale.h>
3234
#endif
@@ -5616,146 +5618,130 @@ PHP_FUNCTION(substr_compare)
56165618
}
56175619
/* }}} */
56185620

5621+
uint64_t php_siphash(const void *src, unsigned long src_sz, const char key[16]);
56195622

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-
*/
5623+
/* {{{ proto bool str_compare(string str1, string str2)
5624+
Timing safe string compare */
5625+
PHP_FUNCTION(str_siphash_compare)
5626+
{
5627+
zval *s1, *s2;
5628+
char key[16] = {0,1,2,3,4,5,6,7,8,9,0xa,0xb,0xc,0xd,0xe,0xf};
56535629

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
5630+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &s1, &s2) == FAILURE) {
5631+
RETURN_FALSE;
5632+
}
56675633

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
5634+
if (Z_TYPE_P(s1) != IS_STRING || Z_TYPE_P(s2) != IS_STRING) {
5635+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Paremeters must be string");
5636+
RETURN_FALSE;
5637+
}
5638+
if (Z_STRLEN_P(s1) != Z_STRLEN_P(s2)) {
5639+
RETURN_FALSE;
5640+
}
5641+
56805642

5681-
#endif
5643+
RETURN_BOOL(php_siphash(Z_STRVAL_P(s1), Z_STRLEN_P(s1), key) == php_siphash(Z_STRVAL_P(s2), Z_STRLEN_P(s2), key));
5644+
}
5645+
/* }}} */
56825646

56835647

5684-
#define ROTATE(x, b) (uint64_t)( ((x) << (b)) | ( (x) >> (64 - (b))) )
5648+
/* {{{ proto bool str_compare(string str1, string str2)
5649+
Timing safe string compare */
5650+
PHP_FUNCTION(str_xxhash32_compare)
5651+
{
5652+
zval *s1, *s2;
56855653

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);
5654+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &s1, &s2) == FAILURE) {
5655+
RETURN_FALSE;
5656+
}
5657+
5658+
if (Z_TYPE_P(s1) != IS_STRING || Z_TYPE_P(s2) != IS_STRING) {
5659+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Paremeters must be string");
5660+
RETURN_FALSE;
5661+
}
5662+
if (Z_STRLEN_P(s1) != Z_STRLEN_P(s2)) {
5663+
RETURN_FALSE;
5664+
}
5665+
56915666

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);
5667+
RETURN_BOOL(XXH32(Z_STRVAL_P(s1), Z_STRLEN_P(s1), 12345) == XXH32(Z_STRVAL_P(s2), Z_STRLEN_P(s2), 12345));
5668+
}
5669+
/* }}} */
56975670

56985671

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;
57055672

5706-
uint64_t v0 = k0 ^ 0x736f6d6570736575ULL;
5707-
uint64_t v1 = k1 ^ 0x646f72616e646f6dULL;
5708-
uint64_t v2 = k0 ^ 0x6c7967656e657261ULL;
5709-
uint64_t v3 = k1 ^ 0x7465646279746573ULL;
5673+
/* {{{ proto bool str_compare(string str1, string str2)
5674+
string compare */
5675+
PHP_FUNCTION(str_md5_compare)
5676+
{
5677+
zval *s1, *s2;
5678+
PHP_MD5_CTX context;
5679+
unsigned char d1[16], d2[16];
57105680

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;
5681+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &s1, &s2) == FAILURE) {
5682+
RETURN_FALSE;
57175683
}
57185684

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];
5685+
if (Z_TYPE_P(s1) != IS_STRING || Z_TYPE_P(s2) != IS_STRING) {
5686+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Paremeters must be string");
5687+
RETURN_FALSE;
57285688
}
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);
5689+
if (Z_STRLEN_P(s1) != Z_STRLEN_P(s2)) {
5690+
RETURN_FALSE;
5691+
}
5692+
5693+
PHP_MD5Init(&context);
5694+
PHP_MD5Update(&context, Z_STRVAL_P(s1), Z_STRLEN_P(s1));
5695+
PHP_MD5Final(d1, &context);
5696+
PHP_MD5Init(&context);
5697+
PHP_MD5Update(&context, Z_STRVAL_P(s2), Z_STRLEN_P(s2));
5698+
PHP_MD5Final(d2, &context);
5699+
5700+
RETURN_BOOL(memcmp(d1, d2, 16) == 0);
57375701
}
5738-
/* END SipHash */
5702+
/* }}} */
57395703

57405704
/* Timing safe compare */
5741-
PHPAPI int php_compare(const void *b1, const void *b2, size_t n) /* {{{ */
5705+
PHPAPI int php_byte_compare(const void *b1, const void *b2, size_t n) /* {{{ */
57425706
{
57435707
const unsigned char *p1 = b1, *p2 = b2;
57445708
int ret = 0;
57455709

57465710
for (; n > 0; n--) {
57475711
ret |= *p1++ ^ *p2++;
57485712
}
5749-
return (ret != 0);
5713+
return (ret == 0);
57505714
}
57515715
/* }}} */
57525716

57535717
/* {{{ proto bool str_compare(string str1, string str2)
57545718
Timing safe string compare */
5719+
PHP_FUNCTION(str_byte_compare)
5720+
{
5721+
zval *s1, *s2;
5722+
5723+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &s1, &s2) == FAILURE) {
5724+
RETURN_FALSE;
5725+
}
5726+
5727+
if (Z_TYPE_P(s1) != IS_STRING || Z_TYPE_P(s2) != IS_STRING) {
5728+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Paremeters must be string");
5729+
RETURN_FALSE;
5730+
}
5731+
if (Z_STRLEN_P(s1) != Z_STRLEN_P(s2)) {
5732+
RETURN_FALSE;
5733+
}
5734+
5735+
5736+
RETURN_BOOL(php_byte_compare(Z_STRVAL_P(s1), Z_STRVAL_P(s2), Z_STRLEN_P(s2)));
5737+
}
5738+
/* }}} */
5739+
5740+
/* {{{ proto bool str_compare(string str1, string str2)
5741+
strncmp string compare */
57555742
PHP_FUNCTION(str_compare)
57565743
{
57575744
zval *s1, *s2;
5758-
char key[16] = {0,1,2,3,4,5,6,7,8,9,0xa,0xb,0xc,0xd,0xe,0xf};
57595745

57605746
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &s1, &s2) == FAILURE) {
57615747
RETURN_FALSE;
@@ -5770,10 +5756,11 @@ PHP_FUNCTION(str_compare)
57705756
}
57715757

57725758

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));
5759+
RETURN_BOOL(strncmp(Z_STRVAL_P(s1), Z_STRVAL_P(s2), Z_STRLEN_P(s2)) == 0);
57745760
}
57755761
/* }}} */
57765762

5763+
57775764
/*
57785765
* Local variables:
57795766
* tab-width: 4

0 commit comments

Comments
 (0)