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 | https://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 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Anatol Belski <ab@php.net> |
14 +----------------------------------------------------------------------+
15 */
16
17 #include "php_hash.h"
18 #include "php_hash_xxhash.h"
19
20 static int php_hash_xxh32_unserialize(
21 php_hashcontext_object *hash, zend_long magic, const zval *zv);
22 static int php_hash_xxh64_unserialize(
23 php_hashcontext_object *hash, zend_long magic, const zval *zv);
24
25 const php_hash_ops php_hash_xxh32_ops = {
26 "xxh32",
27 (php_hash_init_func_t) PHP_XXH32Init,
28 (php_hash_update_func_t) PHP_XXH32Update,
29 (php_hash_final_func_t) PHP_XXH32Final,
30 (php_hash_copy_func_t) PHP_XXH32Copy,
31 php_hash_serialize,
32 php_hash_xxh32_unserialize,
33 PHP_XXH32_SPEC,
34 4,
35 4,
36 sizeof(PHP_XXH32_CTX),
37 0
38 };
39
PHP_XXH32Init(PHP_XXH32_CTX * ctx,HashTable * args)40 PHP_HASH_API void PHP_XXH32Init(PHP_XXH32_CTX *ctx, HashTable *args)
41 {
42 /* XXH32_createState() is not used intentionally. */
43 memset(&ctx->s, 0, sizeof ctx->s);
44
45 if (args) {
46 zval *seed = zend_hash_str_find_deref(args, "seed", sizeof("seed") - 1);
47 /* This might be a bit too restrictive, but thinking that a seed might be set
48 once and for all, it should be done a clean way. */
49 if (seed && IS_LONG == Z_TYPE_P(seed)) {
50 XXH32_reset(&ctx->s, (XXH32_hash_t)Z_LVAL_P(seed));
51 } else {
52 XXH32_reset(&ctx->s, 0);
53 }
54 } else {
55 XXH32_reset(&ctx->s, 0);
56 }
57 }
58
PHP_XXH32Update(PHP_XXH32_CTX * ctx,const unsigned char * in,size_t len)59 PHP_HASH_API void PHP_XXH32Update(PHP_XXH32_CTX *ctx, const unsigned char *in, size_t len)
60 {
61 XXH32_update(&ctx->s, in, len);
62 }
63
PHP_XXH32Final(unsigned char digest[4],PHP_XXH32_CTX * ctx)64 PHP_HASH_API void PHP_XXH32Final(unsigned char digest[4], PHP_XXH32_CTX *ctx)
65 {
66 XXH32_canonicalFromHash((XXH32_canonical_t*)digest, XXH32_digest(&ctx->s));
67 }
68
PHP_XXH32Copy(const php_hash_ops * ops,PHP_XXH32_CTX * orig_context,PHP_XXH32_CTX * copy_context)69 PHP_HASH_API int PHP_XXH32Copy(const php_hash_ops *ops, PHP_XXH32_CTX *orig_context, PHP_XXH32_CTX *copy_context)
70 {
71 copy_context->s = orig_context->s;
72 return SUCCESS;
73 }
74
php_hash_xxh32_unserialize(php_hashcontext_object * hash,zend_long magic,const zval * zv)75 static int php_hash_xxh32_unserialize(
76 php_hashcontext_object *hash, zend_long magic, const zval *zv)
77 {
78 PHP_XXH32_CTX *ctx = (PHP_XXH32_CTX *) hash->context;
79 int r = FAILURE;
80 if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC
81 && (r = php_hash_unserialize_spec(hash, zv, PHP_XXH32_SPEC)) == SUCCESS
82 && ctx->s.memsize < 16) {
83 return SUCCESS;
84 } else {
85 return r != SUCCESS ? r : -2000;
86 }
87 }
88
89 const php_hash_ops php_hash_xxh64_ops = {
90 "xxh64",
91 (php_hash_init_func_t) PHP_XXH64Init,
92 (php_hash_update_func_t) PHP_XXH64Update,
93 (php_hash_final_func_t) PHP_XXH64Final,
94 (php_hash_copy_func_t) PHP_XXH64Copy,
95 php_hash_serialize,
96 php_hash_xxh64_unserialize,
97 PHP_XXH64_SPEC,
98 8,
99 8,
100 sizeof(PHP_XXH64_CTX),
101 0
102 };
103
PHP_XXH64Init(PHP_XXH64_CTX * ctx,HashTable * args)104 PHP_HASH_API void PHP_XXH64Init(PHP_XXH64_CTX *ctx, HashTable *args)
105 {
106 /* XXH64_createState() is not used intentionally. */
107 memset(&ctx->s, 0, sizeof ctx->s);
108
109 if (args) {
110 zval *seed = zend_hash_str_find_deref(args, "seed", sizeof("seed") - 1);
111 /* This might be a bit too restrictive, but thinking that a seed might be set
112 once and for all, it should be done a clean way. */
113 if (seed && IS_LONG == Z_TYPE_P(seed)) {
114 XXH64_reset(&ctx->s, (XXH64_hash_t)Z_LVAL_P(seed));
115 } else {
116 XXH64_reset(&ctx->s, 0);
117 }
118 } else {
119 XXH64_reset(&ctx->s, 0);
120 }
121 }
122
PHP_XXH64Update(PHP_XXH64_CTX * ctx,const unsigned char * in,size_t len)123 PHP_HASH_API void PHP_XXH64Update(PHP_XXH64_CTX *ctx, const unsigned char *in, size_t len)
124 {
125 XXH64_update(&ctx->s, in, len);
126 }
127
PHP_XXH64Final(unsigned char digest[8],PHP_XXH64_CTX * ctx)128 PHP_HASH_API void PHP_XXH64Final(unsigned char digest[8], PHP_XXH64_CTX *ctx)
129 {
130 XXH64_canonicalFromHash((XXH64_canonical_t*)digest, XXH64_digest(&ctx->s));
131 }
132
PHP_XXH64Copy(const php_hash_ops * ops,PHP_XXH64_CTX * orig_context,PHP_XXH64_CTX * copy_context)133 PHP_HASH_API int PHP_XXH64Copy(const php_hash_ops *ops, PHP_XXH64_CTX *orig_context, PHP_XXH64_CTX *copy_context)
134 {
135 copy_context->s = orig_context->s;
136 return SUCCESS;
137 }
138
139 const php_hash_ops php_hash_xxh3_64_ops = {
140 "xxh3",
141 (php_hash_init_func_t) PHP_XXH3_64_Init,
142 (php_hash_update_func_t) PHP_XXH3_64_Update,
143 (php_hash_final_func_t) PHP_XXH3_64_Final,
144 (php_hash_copy_func_t) PHP_XXH3_64_Copy,
145 php_hash_serialize,
146 php_hash_unserialize,
147 NULL,
148 8,
149 8,
150 sizeof(PHP_XXH3_64_CTX),
151 0
152 };
153
154 typedef XXH_errorcode (*xxh3_reset_with_secret_func_t)(XXH3_state_t*, const void*, size_t);
155 typedef XXH_errorcode (*xxh3_reset_with_seed_func_t)(XXH3_state_t*, XXH64_hash_t);
156
_PHP_XXH3_Init(PHP_XXH3_64_CTX * ctx,HashTable * args,xxh3_reset_with_seed_func_t func_init_seed,xxh3_reset_with_secret_func_t func_init_secret,const char * algo_name)157 zend_always_inline static void _PHP_XXH3_Init(PHP_XXH3_64_CTX *ctx, HashTable *args,
158 xxh3_reset_with_seed_func_t func_init_seed, xxh3_reset_with_secret_func_t func_init_secret, const char* algo_name)
159 {
160 memset(&ctx->s, 0, sizeof ctx->s);
161
162 if (args) {
163 zval *_seed = zend_hash_str_find_deref(args, "seed", sizeof("seed") - 1);
164 zval *_secret = zend_hash_str_find_deref(args, "secret", sizeof("secret") - 1);
165
166 if (_seed && _secret) {
167 zend_throw_error(NULL, "%s: Only one of seed or secret is to be passed for initialization", algo_name);
168 return;
169 }
170
171 if (_seed && IS_LONG == Z_TYPE_P(_seed)) {
172 /* This might be a bit too restrictive, but thinking that a seed might be set
173 once and for all, it should be done a clean way. */
174 func_init_seed(&ctx->s, (XXH64_hash_t)Z_LVAL_P(_seed));
175 return;
176 } else if (_secret) {
177 zend_string *secret_string = zval_try_get_string(_secret);
178 if (UNEXPECTED(!secret_string)) {
179 ZEND_ASSERT(EG(exception));
180 return;
181 }
182 size_t len = ZSTR_LEN(secret_string);
183 if (len < PHP_XXH3_SECRET_SIZE_MIN) {
184 zend_string_release(secret_string);
185 zend_throw_error(NULL, "%s: Secret length must be >= %u bytes, %zu bytes passed", algo_name, XXH3_SECRET_SIZE_MIN, len);
186 return;
187 }
188 if (len > sizeof(ctx->secret)) {
189 len = sizeof(ctx->secret);
190 php_error_docref(NULL, E_WARNING, "%s: Secret content exceeding %zu bytes discarded", algo_name, sizeof(ctx->secret));
191 }
192 memcpy((unsigned char *)ctx->secret, ZSTR_VAL(secret_string), len);
193 zend_string_release(secret_string);
194 func_init_secret(&ctx->s, ctx->secret, len);
195 return;
196 }
197 }
198
199 func_init_seed(&ctx->s, 0);
200 }
201
PHP_XXH3_64_Init(PHP_XXH3_64_CTX * ctx,HashTable * args)202 PHP_HASH_API void PHP_XXH3_64_Init(PHP_XXH3_64_CTX *ctx, HashTable *args)
203 {
204 _PHP_XXH3_Init(ctx, args, XXH3_64bits_reset_withSeed, XXH3_64bits_reset_withSecret, "xxh3");
205 }
206
PHP_XXH3_64_Update(PHP_XXH3_64_CTX * ctx,const unsigned char * in,size_t len)207 PHP_HASH_API void PHP_XXH3_64_Update(PHP_XXH3_64_CTX *ctx, const unsigned char *in, size_t len)
208 {
209 XXH3_64bits_update(&ctx->s, in, len);
210 }
211
PHP_XXH3_64_Final(unsigned char digest[8],PHP_XXH3_64_CTX * ctx)212 PHP_HASH_API void PHP_XXH3_64_Final(unsigned char digest[8], PHP_XXH3_64_CTX *ctx)
213 {
214 XXH64_canonicalFromHash((XXH64_canonical_t*)digest, XXH3_64bits_digest(&ctx->s));
215 }
216
PHP_XXH3_64_Copy(const php_hash_ops * ops,PHP_XXH3_64_CTX * orig_context,PHP_XXH3_64_CTX * copy_context)217 PHP_HASH_API int PHP_XXH3_64_Copy(const php_hash_ops *ops, PHP_XXH3_64_CTX *orig_context, PHP_XXH3_64_CTX *copy_context)
218 {
219 copy_context->s = orig_context->s;
220 return SUCCESS;
221 }
222
php_hash_xxh64_unserialize(php_hashcontext_object * hash,zend_long magic,const zval * zv)223 static int php_hash_xxh64_unserialize(
224 php_hashcontext_object *hash, zend_long magic, const zval *zv)
225 {
226 PHP_XXH64_CTX *ctx = (PHP_XXH64_CTX *) hash->context;
227 int r = FAILURE;
228 if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC
229 && (r = php_hash_unserialize_spec(hash, zv, PHP_XXH64_SPEC)) == SUCCESS
230 && ctx->s.memsize < 32) {
231 return SUCCESS;
232 } else {
233 return r != SUCCESS ? r : -2000;
234 }
235 }
236
237 const php_hash_ops php_hash_xxh3_128_ops = {
238 "xxh128",
239 (php_hash_init_func_t) PHP_XXH3_128_Init,
240 (php_hash_update_func_t) PHP_XXH3_128_Update,
241 (php_hash_final_func_t) PHP_XXH3_128_Final,
242 (php_hash_copy_func_t) PHP_XXH3_128_Copy,
243 php_hash_serialize,
244 php_hash_unserialize,
245 NULL,
246 16,
247 8,
248 sizeof(PHP_XXH3_128_CTX),
249 0
250 };
251
PHP_XXH3_128_Init(PHP_XXH3_128_CTX * ctx,HashTable * args)252 PHP_HASH_API void PHP_XXH3_128_Init(PHP_XXH3_128_CTX *ctx, HashTable *args)
253 {
254 _PHP_XXH3_Init(ctx, args, XXH3_128bits_reset_withSeed, XXH3_128bits_reset_withSecret, "xxh128");
255 }
256
PHP_XXH3_128_Update(PHP_XXH3_128_CTX * ctx,const unsigned char * in,size_t len)257 PHP_HASH_API void PHP_XXH3_128_Update(PHP_XXH3_128_CTX *ctx, const unsigned char *in, size_t len)
258 {
259 XXH3_128bits_update(&ctx->s, in, len);
260 }
261
PHP_XXH3_128_Final(unsigned char digest[16],PHP_XXH3_128_CTX * ctx)262 PHP_HASH_API void PHP_XXH3_128_Final(unsigned char digest[16], PHP_XXH3_128_CTX *ctx)
263 {
264 XXH128_canonicalFromHash((XXH128_canonical_t*)digest, XXH3_128bits_digest(&ctx->s));
265 }
266
PHP_XXH3_128_Copy(const php_hash_ops * ops,PHP_XXH3_128_CTX * orig_context,PHP_XXH3_128_CTX * copy_context)267 PHP_HASH_API int PHP_XXH3_128_Copy(const php_hash_ops *ops, PHP_XXH3_128_CTX *orig_context, PHP_XXH3_128_CTX *copy_context)
268 {
269 copy_context->s = orig_context->s;
270 return SUCCESS;
271 }
272
273