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 if (!try_convert_to_string(_secret)) {
178 return;
179 }
180 size_t len = Z_STRLEN_P(_secret);
181 if (len < PHP_XXH3_SECRET_SIZE_MIN) {
182 zend_throw_error(NULL, "%s: Secret length must be >= %u bytes, %zu bytes passed", algo_name, XXH3_SECRET_SIZE_MIN, len);
183 return;
184 }
185 if (len > sizeof(ctx->secret)) {
186 len = sizeof(ctx->secret);
187 php_error_docref(NULL, E_WARNING, "%s: Secret content exceeding %zu bytes discarded", algo_name, sizeof(ctx->secret));
188 }
189 memcpy((unsigned char *)ctx->secret, Z_STRVAL_P(_secret), len);
190 func_init_secret(&ctx->s, ctx->secret, len);
191 return;
192 }
193 }
194
195 func_init_seed(&ctx->s, 0);
196 }
197
PHP_XXH3_64_Init(PHP_XXH3_64_CTX * ctx,HashTable * args)198 PHP_HASH_API void PHP_XXH3_64_Init(PHP_XXH3_64_CTX *ctx, HashTable *args)
199 {
200 _PHP_XXH3_Init(ctx, args, XXH3_64bits_reset_withSeed, XXH3_64bits_reset_withSecret, "xxh3");
201 }
202
PHP_XXH3_64_Update(PHP_XXH3_64_CTX * ctx,const unsigned char * in,size_t len)203 PHP_HASH_API void PHP_XXH3_64_Update(PHP_XXH3_64_CTX *ctx, const unsigned char *in, size_t len)
204 {
205 XXH3_64bits_update(&ctx->s, in, len);
206 }
207
PHP_XXH3_64_Final(unsigned char digest[8],PHP_XXH3_64_CTX * ctx)208 PHP_HASH_API void PHP_XXH3_64_Final(unsigned char digest[8], PHP_XXH3_64_CTX *ctx)
209 {
210 XXH64_canonicalFromHash((XXH64_canonical_t*)digest, XXH3_64bits_digest(&ctx->s));
211 }
212
PHP_XXH3_64_Copy(const php_hash_ops * ops,PHP_XXH3_64_CTX * orig_context,PHP_XXH3_64_CTX * copy_context)213 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)
214 {
215 copy_context->s = orig_context->s;
216 return SUCCESS;
217 }
218
php_hash_xxh64_unserialize(php_hashcontext_object * hash,zend_long magic,const zval * zv)219 static int php_hash_xxh64_unserialize(
220 php_hashcontext_object *hash, zend_long magic, const zval *zv)
221 {
222 PHP_XXH64_CTX *ctx = (PHP_XXH64_CTX *) hash->context;
223 int r = FAILURE;
224 if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC
225 && (r = php_hash_unserialize_spec(hash, zv, PHP_XXH64_SPEC)) == SUCCESS
226 && ctx->s.memsize < 32) {
227 return SUCCESS;
228 } else {
229 return r != SUCCESS ? r : -2000;
230 }
231 }
232
233 const php_hash_ops php_hash_xxh3_128_ops = {
234 "xxh128",
235 (php_hash_init_func_t) PHP_XXH3_128_Init,
236 (php_hash_update_func_t) PHP_XXH3_128_Update,
237 (php_hash_final_func_t) PHP_XXH3_128_Final,
238 (php_hash_copy_func_t) PHP_XXH3_128_Copy,
239 php_hash_serialize,
240 php_hash_unserialize,
241 NULL,
242 16,
243 8,
244 sizeof(PHP_XXH3_128_CTX),
245 0
246 };
247
PHP_XXH3_128_Init(PHP_XXH3_128_CTX * ctx,HashTable * args)248 PHP_HASH_API void PHP_XXH3_128_Init(PHP_XXH3_128_CTX *ctx, HashTable *args)
249 {
250 _PHP_XXH3_Init(ctx, args, XXH3_128bits_reset_withSeed, XXH3_128bits_reset_withSecret, "xxh128");
251 }
252
PHP_XXH3_128_Update(PHP_XXH3_128_CTX * ctx,const unsigned char * in,size_t len)253 PHP_HASH_API void PHP_XXH3_128_Update(PHP_XXH3_128_CTX *ctx, const unsigned char *in, size_t len)
254 {
255 XXH3_128bits_update(&ctx->s, in, len);
256 }
257
PHP_XXH3_128_Final(unsigned char digest[16],PHP_XXH3_128_CTX * ctx)258 PHP_HASH_API void PHP_XXH3_128_Final(unsigned char digest[16], PHP_XXH3_128_CTX *ctx)
259 {
260 XXH128_canonicalFromHash((XXH128_canonical_t*)digest, XXH3_128bits_digest(&ctx->s));
261 }
262
PHP_XXH3_128_Copy(const php_hash_ops * ops,PHP_XXH3_128_CTX * orig_context,PHP_XXH3_128_CTX * copy_context)263 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)
264 {
265 copy_context->s = orig_context->s;
266 return SUCCESS;
267 }
268
269