xref: /PHP-8.2/ext/hash/hash_xxhash.c (revision ca84662c)
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