xref: /PHP-5.3/ext/hash/hash.c (revision a2045ff3)
1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2013 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Author: Sara Golemon <pollita@php.net>                               |
16   |         Scott MacVicar <scottmac@php.net>                            |
17   +----------------------------------------------------------------------+
18 */
19 
20 /* $Id$ */
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include "php_hash.h"
27 #include "ext/standard/info.h"
28 #include "ext/standard/file.h"
29 
30 static int php_hash_le_hash;
31 HashTable php_hash_hashtable;
32 
33 #if (PHP_MAJOR_VERSION >= 5)
34 # define DEFAULT_CONTEXT FG(default_context)
35 #else
36 # define DEFAULT_CONTEXT NULL
37 #endif
38 
39 #ifdef PHP_MHASH_BC
40 struct mhash_bc_entry {
41 	char *mhash_name;
42 	char *hash_name;
43 	int value;
44 };
45 
46 #define MHASH_NUM_ALGOS 29
47 
48 static struct mhash_bc_entry mhash_to_hash[MHASH_NUM_ALGOS] = {
49 	{"CRC32", "crc32", 0},
50 	{"MD5", "md5", 1},
51 	{"SHA1", "sha1", 2},
52 	{"HAVAL256", "haval256,3", 3},
53 	{NULL, NULL, 4},
54 	{"RIPEMD160", "ripemd160", 5},
55 	{NULL, NULL, 6},
56 	{"TIGER", "tiger192,3", 7},
57 	{"GOST", "gost", 8},
58 	{"CRC32B", "crc32b", 9},
59 	{"HAVAL224", "haval224,3", 10},
60 	{"HAVAL192", "haval192,3", 11},
61 	{"HAVAL160", "haval160,3", 12},
62 	{"HAVAL128", "haval128,3", 13},
63 	{"TIGER128", "tiger128,3", 14},
64 	{"TIGER160", "tiger160,3", 15},
65 	{"MD4", "md4", 16},
66 	{"SHA256", "sha256", 17},
67 	{"ADLER32", "adler32", 18},
68 	{"SHA224", "sha224", 19},
69 	{"SHA512", "sha512", 20},
70 	{"SHA384", "sha384", 21},
71 	{"WHIRLPOOL", "whirlpool", 22},
72 	{"RIPEMD128", "ripemd128", 23},
73 	{"RIPEMD256", "ripemd256", 24},
74 	{"RIPEMD320", "ripemd320", 25},
75 	{NULL, NULL, 26}, /* support needs to be added for snefru 128 */
76 	{"SNEFRU256", "snefru256", 27},
77 	{"MD2", "md2", 28}
78 };
79 #endif
80 
81 /* Hash Registry Access */
82 
php_hash_fetch_ops(const char * algo,int algo_len)83 PHP_HASH_API const php_hash_ops *php_hash_fetch_ops(const char *algo, int algo_len) /* {{{ */
84 {
85 	php_hash_ops *ops;
86 	char *lower = estrndup(algo, algo_len);
87 
88 	zend_str_tolower(lower, algo_len);
89 	if (SUCCESS != zend_hash_find(&php_hash_hashtable, lower, algo_len + 1, (void*)&ops)) {
90 		ops = NULL;
91 	}
92 	efree(lower);
93 
94 	return ops;
95 }
96 /* }}} */
97 
php_hash_register_algo(const char * algo,const php_hash_ops * ops)98 PHP_HASH_API void php_hash_register_algo(const char *algo, const php_hash_ops *ops) /* {{{ */
99 {
100 	int algo_len = strlen(algo);
101 	char *lower = estrndup(algo, algo_len);
102 
103 	zend_str_tolower(lower, algo_len);
104 	zend_hash_add(&php_hash_hashtable, lower, algo_len + 1, (void*)ops, sizeof(php_hash_ops), NULL);
105 	efree(lower);
106 }
107 /* }}} */
108 
php_hash_copy(const void * ops,void * orig_context,void * dest_context)109 PHP_HASH_API int php_hash_copy(const void *ops, void *orig_context, void *dest_context) /* {{{ */
110 {
111 	php_hash_ops *hash_ops = (php_hash_ops *)ops;
112 
113 	memcpy(dest_context, orig_context, hash_ops->context_size);
114 	return SUCCESS;
115 }
116 /* }}} */
117 
118 /* Userspace */
119 
php_hash_do_hash(INTERNAL_FUNCTION_PARAMETERS,int isfilename,zend_bool raw_output_default)120 static void php_hash_do_hash(INTERNAL_FUNCTION_PARAMETERS, int isfilename, zend_bool raw_output_default) /* {{{ */
121 {
122 	char *algo, *data, *digest;
123 	int algo_len, data_len;
124 	zend_bool raw_output = raw_output_default;
125 	const php_hash_ops *ops;
126 	void *context;
127 	php_stream *stream = NULL;
128 
129 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|b", &algo, &algo_len, &data, &data_len, &raw_output) == FAILURE) {
130 		return;
131 	}
132 
133 	ops = php_hash_fetch_ops(algo, algo_len);
134 	if (!ops) {
135 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown hashing algorithm: %s", algo);
136 		RETURN_FALSE;
137 	}
138 	if (isfilename) {
139 		stream = php_stream_open_wrapper_ex(data, "rb", REPORT_ERRORS | ENFORCE_SAFE_MODE, NULL, DEFAULT_CONTEXT);
140 		if (!stream) {
141 			/* Stream will report errors opening file */
142 			RETURN_FALSE;
143 		}
144 	}
145 
146 	context = emalloc(ops->context_size);
147 	ops->hash_init(context);
148 
149 	if (isfilename) {
150 		char buf[1024];
151 		int n;
152 
153 		while ((n = php_stream_read(stream, buf, sizeof(buf))) > 0) {
154 			ops->hash_update(context, (unsigned char *) buf, n);
155 		}
156 		php_stream_close(stream);
157 	} else {
158 		ops->hash_update(context, (unsigned char *) data, data_len);
159 	}
160 
161 	digest = emalloc(ops->digest_size + 1);
162 	ops->hash_final((unsigned char *) digest, context);
163 	efree(context);
164 
165 	if (raw_output) {
166 		digest[ops->digest_size] = 0;
167 		RETURN_STRINGL(digest, ops->digest_size, 0);
168 	} else {
169 		char *hex_digest = safe_emalloc(ops->digest_size, 2, 1);
170 
171 		php_hash_bin2hex(hex_digest, (unsigned char *) digest, ops->digest_size);
172 		hex_digest[2 * ops->digest_size] = 0;
173 		efree(digest);
174 		RETURN_STRINGL(hex_digest, 2 * ops->digest_size, 0);
175 	}
176 }
177 /* }}} */
178 
179 /* {{{ proto string hash(string algo, string data[, bool raw_output = false])
180 Generate a hash of a given input string
181 Returns lowercase hexits by default */
PHP_FUNCTION(hash)182 PHP_FUNCTION(hash)
183 {
184 	php_hash_do_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0);
185 }
186 /* }}} */
187 
188 /* {{{ proto string hash_file(string algo, string filename[, bool raw_output = false])
189 Generate a hash of a given file
190 Returns lowercase hexits by default */
PHP_FUNCTION(hash_file)191 PHP_FUNCTION(hash_file)
192 {
193 	php_hash_do_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 0);
194 }
195 /* }}} */
196 
php_hash_do_hash_hmac(INTERNAL_FUNCTION_PARAMETERS,int isfilename,zend_bool raw_output_default)197 static void php_hash_do_hash_hmac(INTERNAL_FUNCTION_PARAMETERS, int isfilename, zend_bool raw_output_default) /* {{{ */
198 {
199 	char *algo, *data, *digest, *key, *K;
200 	int algo_len, data_len, key_len, i;
201 	zend_bool raw_output = raw_output_default;
202 	const php_hash_ops *ops;
203 	void *context;
204 	php_stream *stream = NULL;
205 
206 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|b", &algo, &algo_len, &data, &data_len,
207 																  &key, &key_len, &raw_output) == FAILURE) {
208 		return;
209 	}
210 
211 	ops = php_hash_fetch_ops(algo, algo_len);
212 	if (!ops) {
213 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown hashing algorithm: %s", algo);
214 		RETURN_FALSE;
215 	}
216 	if (isfilename) {
217 		stream = php_stream_open_wrapper_ex(data, "rb", REPORT_ERRORS | ENFORCE_SAFE_MODE, NULL, DEFAULT_CONTEXT);
218 		if (!stream) {
219 			/* Stream will report errors opening file */
220 			RETURN_FALSE;
221 		}
222 	}
223 
224 	context = emalloc(ops->context_size);
225 	ops->hash_init(context);
226 
227 	K = emalloc(ops->block_size);
228 	memset(K, 0, ops->block_size);
229 
230 	if (key_len > ops->block_size) {
231 		/* Reduce the key first */
232 		ops->hash_update(context, (unsigned char *) key, key_len);
233 		ops->hash_final((unsigned char *) K, context);
234 		/* Make the context ready to start over */
235 		ops->hash_init(context);
236 	} else {
237 		memcpy(K, key, key_len);
238 	}
239 
240 	/* XOR ipad */
241 	for(i=0; i < ops->block_size; i++) {
242 		K[i] ^= 0x36;
243 	}
244 	ops->hash_update(context, (unsigned char *) K, ops->block_size);
245 
246 	if (isfilename) {
247 		char buf[1024];
248 		int n;
249 
250 		while ((n = php_stream_read(stream, buf, sizeof(buf))) > 0) {
251 			ops->hash_update(context, (unsigned char *) buf, n);
252 		}
253 		php_stream_close(stream);
254 	} else {
255 		ops->hash_update(context, (unsigned char *) data, data_len);
256 	}
257 
258 	digest = emalloc(ops->digest_size + 1);
259 	ops->hash_final((unsigned char *) digest, context);
260 
261 	/* Convert K to opad -- 0x6A = 0x36 ^ 0x5C */
262 	for(i=0; i < ops->block_size; i++) {
263 		K[i] ^= 0x6A;
264 	}
265 
266 	/* Feed this result into the outter hash */
267 	ops->hash_init(context);
268 	ops->hash_update(context, (unsigned char *) K, ops->block_size);
269 	ops->hash_update(context, (unsigned char *) digest, ops->digest_size);
270 	ops->hash_final((unsigned char *) digest, context);
271 
272 	/* Zero the key */
273 	memset(K, 0, ops->block_size);
274 	efree(K);
275 	efree(context);
276 
277 	if (raw_output) {
278 		digest[ops->digest_size] = 0;
279 		RETURN_STRINGL(digest, ops->digest_size, 0);
280 	} else {
281 		char *hex_digest = safe_emalloc(ops->digest_size, 2, 1);
282 
283 		php_hash_bin2hex(hex_digest, (unsigned char *) digest, ops->digest_size);
284 		hex_digest[2 * ops->digest_size] = 0;
285 		efree(digest);
286 		RETURN_STRINGL(hex_digest, 2 * ops->digest_size, 0);
287 	}
288 }
289 /* }}} */
290 
291 /* {{{ proto string hash_hmac(string algo, string data, string key[, bool raw_output = false])
292 Generate a hash of a given input string with a key using HMAC
293 Returns lowercase hexits by default */
PHP_FUNCTION(hash_hmac)294 PHP_FUNCTION(hash_hmac)
295 {
296 	php_hash_do_hash_hmac(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0);
297 }
298 /* }}} */
299 
300 /* {{{ proto string hash_hmac_file(string algo, string filename, string key[, bool raw_output = false])
301 Generate a hash of a given file with a key using HMAC
302 Returns lowercase hexits by default */
PHP_FUNCTION(hash_hmac_file)303 PHP_FUNCTION(hash_hmac_file)
304 {
305 	php_hash_do_hash_hmac(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 0);
306 }
307 /* }}} */
308 
309 
310 /* {{{ proto resource hash_init(string algo[, int options, string key])
311 Initialize a hashing context */
PHP_FUNCTION(hash_init)312 PHP_FUNCTION(hash_init)
313 {
314 	char *algo, *key = NULL;
315 	int algo_len, key_len = 0, argc = ZEND_NUM_ARGS();
316 	long options = 0;
317 	void *context;
318 	const php_hash_ops *ops;
319 	php_hash_data *hash;
320 
321 	if (zend_parse_parameters(argc TSRMLS_CC, "s|ls", &algo, &algo_len, &options, &key, &key_len) == FAILURE) {
322 		return;
323 	}
324 
325 	ops = php_hash_fetch_ops(algo, algo_len);
326 	if (!ops) {
327 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown hashing algorithm: %s", algo);
328 		RETURN_FALSE;
329 	}
330 
331 	if (options & PHP_HASH_HMAC &&
332 		key_len <= 0) {
333 		/* Note: a zero length key is no key at all */
334 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "HMAC requested without a key");
335 		RETURN_FALSE;
336 	}
337 
338 	context = emalloc(ops->context_size);
339 	ops->hash_init(context);
340 
341 	hash = emalloc(sizeof(php_hash_data));
342 	hash->ops = ops;
343 	hash->context = context;
344 	hash->options = options;
345 	hash->key = NULL;
346 
347 	if (options & PHP_HASH_HMAC) {
348 		char *K = emalloc(ops->block_size);
349 		int i;
350 
351 		memset(K, 0, ops->block_size);
352 
353 		if (key_len > ops->block_size) {
354 			/* Reduce the key first */
355 			ops->hash_update(context, (unsigned char *) key, key_len);
356 			ops->hash_final((unsigned char *) K, context);
357 			/* Make the context ready to start over */
358 			ops->hash_init(context);
359 		} else {
360 			memcpy(K, key, key_len);
361 		}
362 
363 		/* XOR ipad */
364 		for(i=0; i < ops->block_size; i++) {
365 			K[i] ^= 0x36;
366 		}
367 		ops->hash_update(context, (unsigned char *) K, ops->block_size);
368 		hash->key = (unsigned char *) K;
369 	}
370 
371 	ZEND_REGISTER_RESOURCE(return_value, hash, php_hash_le_hash);
372 }
373 /* }}} */
374 
375 /* {{{ proto bool hash_update(resource context, string data)
376 Pump data into the hashing algorithm */
PHP_FUNCTION(hash_update)377 PHP_FUNCTION(hash_update)
378 {
379 	zval *zhash;
380 	php_hash_data *hash;
381 	char *data;
382 	int data_len;
383 
384 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &zhash, &data, &data_len) == FAILURE) {
385 		return;
386 	}
387 
388 	ZEND_FETCH_RESOURCE(hash, php_hash_data*, &zhash, -1, PHP_HASH_RESNAME, php_hash_le_hash);
389 
390 	hash->ops->hash_update(hash->context, (unsigned char *) data, data_len);
391 
392 	RETURN_TRUE;
393 }
394 /* }}} */
395 
396 /* {{{ proto int hash_update_stream(resource context, resource handle[, integer length])
397 Pump data into the hashing algorithm from an open stream */
PHP_FUNCTION(hash_update_stream)398 PHP_FUNCTION(hash_update_stream)
399 {
400 	zval *zhash, *zstream;
401 	php_hash_data *hash;
402 	php_stream *stream = NULL;
403 	long length = -1, didread = 0;
404 
405 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr|l", &zhash, &zstream, &length) == FAILURE) {
406 		return;
407 	}
408 
409 	ZEND_FETCH_RESOURCE(hash, php_hash_data*, &zhash, -1, PHP_HASH_RESNAME, php_hash_le_hash);
410 	php_stream_from_zval(stream, &zstream);
411 
412 	while (length) {
413 		char buf[1024];
414 		long n, toread = 1024;
415 
416 		if (length > 0 && toread > length) {
417 			toread = length;
418 		}
419 
420 		if ((n = php_stream_read(stream, buf, toread)) <= 0) {
421 			/* Nada mas */
422 			RETURN_LONG(didread);
423 		}
424 		hash->ops->hash_update(hash->context, (unsigned char *) buf, n);
425 		length -= n;
426 		didread += n;
427 	}
428 
429 	RETURN_LONG(didread);
430 }
431 /* }}} */
432 
433 /* {{{ proto bool hash_update_file(resource context, string filename[, resource context])
434 Pump data into the hashing algorithm from a file */
PHP_FUNCTION(hash_update_file)435 PHP_FUNCTION(hash_update_file)
436 {
437 	zval *zhash, *zcontext = NULL;
438 	php_hash_data *hash;
439 	php_stream_context *context;
440 	php_stream *stream;
441 	char *filename, buf[1024];
442 	int filename_len, n;
443 
444 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|r", &zhash, &filename, &filename_len, &zcontext) == FAILURE) {
445 		return;
446 	}
447 
448 	ZEND_FETCH_RESOURCE(hash, php_hash_data*, &zhash, -1, PHP_HASH_RESNAME, php_hash_le_hash);
449 	context = php_stream_context_from_zval(zcontext, 0);
450 
451 	stream = php_stream_open_wrapper_ex(filename, "rb", REPORT_ERRORS | ENFORCE_SAFE_MODE, NULL, context);
452 	if (!stream) {
453 		/* Stream will report errors opening file */
454 		RETURN_FALSE;
455 	}
456 
457 	while ((n = php_stream_read(stream, buf, sizeof(buf))) > 0) {
458 		hash->ops->hash_update(hash->context, (unsigned char *) buf, n);
459 	}
460 	php_stream_close(stream);
461 
462 	RETURN_TRUE;
463 }
464 /* }}} */
465 
466 /* {{{ proto string hash_final(resource context[, bool raw_output=false])
467 Output resulting digest */
PHP_FUNCTION(hash_final)468 PHP_FUNCTION(hash_final)
469 {
470 	zval *zhash;
471 	php_hash_data *hash;
472 	zend_bool raw_output = 0;
473 	zend_rsrc_list_entry *le;
474 	char *digest;
475 	int digest_len;
476 
477 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|b", &zhash, &raw_output) == FAILURE) {
478 		return;
479 	}
480 
481 	ZEND_FETCH_RESOURCE(hash, php_hash_data*, &zhash, -1, PHP_HASH_RESNAME, php_hash_le_hash);
482 
483 	digest_len = hash->ops->digest_size;
484 	digest = emalloc(digest_len + 1);
485 	hash->ops->hash_final((unsigned char *) digest, hash->context);
486 	if (hash->options & PHP_HASH_HMAC) {
487 		int i;
488 
489 		/* Convert K to opad -- 0x6A = 0x36 ^ 0x5C */
490 		for(i=0; i < hash->ops->block_size; i++) {
491 			hash->key[i] ^= 0x6A;
492 		}
493 
494 		/* Feed this result into the outter hash */
495 		hash->ops->hash_init(hash->context);
496 		hash->ops->hash_update(hash->context, (unsigned char *) hash->key, hash->ops->block_size);
497 		hash->ops->hash_update(hash->context, (unsigned char *) digest, hash->ops->digest_size);
498 		hash->ops->hash_final((unsigned char *) digest, hash->context);
499 
500 		/* Zero the key */
501 		memset(hash->key, 0, hash->ops->block_size);
502 		efree(hash->key);
503 		hash->key = NULL;
504 	}
505 	digest[digest_len] = 0;
506 	efree(hash->context);
507 	hash->context = NULL;
508 
509 	/* zend_list_REAL_delete() */
510 	if (zend_hash_index_find(&EG(regular_list), Z_RESVAL_P(zhash), (void *) &le)==SUCCESS) {
511 		/* This is a hack to avoid letting the resource hide elsewhere (like in separated vars)
512 			FETCH_RESOURCE is intelligent enough to handle dealing with any issues this causes */
513 		le->refcount = 1;
514 	} /* FAILURE is not an option */
515 	zend_list_delete(Z_RESVAL_P(zhash));
516 
517 	if (raw_output) {
518 		RETURN_STRINGL(digest, digest_len, 0);
519 	} else {
520 		char *hex_digest = safe_emalloc(digest_len,2,1);
521 
522 		php_hash_bin2hex(hex_digest, (unsigned char *) digest, digest_len);
523 		hex_digest[2 * digest_len] = 0;
524 		efree(digest);
525 		RETURN_STRINGL(hex_digest, 2 * digest_len, 0);
526 	}
527 }
528 /* }}} */
529 
530 /* {{{ proto resource hash_copy(resource context)
531 Copy hash resource */
PHP_FUNCTION(hash_copy)532 PHP_FUNCTION(hash_copy)
533 {
534 	zval *zhash;
535 	php_hash_data *hash, *copy_hash;
536 	void *context;
537 	int res;
538 
539 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zhash) == FAILURE) {
540 		return;
541 	}
542 
543 	ZEND_FETCH_RESOURCE(hash, php_hash_data*, &zhash, -1, PHP_HASH_RESNAME, php_hash_le_hash);
544 
545 
546 	context = emalloc(hash->ops->context_size);
547 	hash->ops->hash_init(context);
548 
549 	res = hash->ops->hash_copy(hash->ops, hash->context, context);
550 	if (res != SUCCESS) {
551 		efree(context);
552 		RETURN_FALSE;
553 	}
554 
555 	copy_hash = emalloc(sizeof(php_hash_data));
556 	copy_hash->ops = hash->ops;
557 	copy_hash->context = context;
558 	copy_hash->options = hash->options;
559 	copy_hash->key = ecalloc(1, hash->ops->block_size);
560 	if (hash->key) {
561 		memcpy(copy_hash->key, hash->key, hash->ops->block_size);
562 	}
563 	ZEND_REGISTER_RESOURCE(return_value, copy_hash, php_hash_le_hash);
564 }
565 /* }}} */
566 
567 /* {{{ proto array hash_algos(void)
568 Return a list of registered hashing algorithms */
PHP_FUNCTION(hash_algos)569 PHP_FUNCTION(hash_algos)
570 {
571 	HashPosition pos;
572 	char *str;
573 	uint str_len;
574 	long type;
575 	ulong idx;
576 
577 	array_init(return_value);
578 	for(zend_hash_internal_pointer_reset_ex(&php_hash_hashtable, &pos);
579 		(type = zend_hash_get_current_key_ex(&php_hash_hashtable, &str, &str_len, &idx, 0, &pos)) != HASH_KEY_NON_EXISTANT;
580 		zend_hash_move_forward_ex(&php_hash_hashtable, &pos)) {
581 		add_next_index_stringl(return_value, str, str_len-1, 1);
582 	}
583 }
584 /* }}} */
585 
586 /* Module Housekeeping */
587 
php_hash_dtor(zend_rsrc_list_entry * rsrc TSRMLS_DC)588 static void php_hash_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
589 {
590 	php_hash_data *hash = (php_hash_data*)rsrc->ptr;
591 
592 	/* Just in case the algo has internally allocated resources */
593 	if (hash->context) {
594 		unsigned char *dummy = emalloc(hash->ops->digest_size);
595 		hash->ops->hash_final(dummy, hash->context);
596 		efree(dummy);
597 		efree(hash->context);
598 	}
599 
600 	if (hash->key) {
601 		memset(hash->key, 0, hash->ops->block_size);
602 		efree(hash->key);
603 	}
604 	efree(hash);
605 }
606 /* }}} */
607 
608 #define PHP_HASH_HAVAL_REGISTER(p,b)	php_hash_register_algo("haval" #b "," #p , &php_hash_##p##haval##b##_ops);
609 
610 #ifdef PHP_MHASH_BC
611 
PHP_MINFO_FUNCTION(mhash)612 PHP_MINFO_FUNCTION(mhash)
613 {
614 	php_info_print_table_start();
615 	php_info_print_table_row(2, "MHASH support", "Enabled");
616 	php_info_print_table_row(2, "MHASH API Version", "Emulated Support");
617 	php_info_print_table_end();
618 }
619 
620 zend_module_entry mhash_module_entry = {
621 	STANDARD_MODULE_HEADER,
622 	"mhash",
623 	NULL,
624 	NULL,
625 	NULL,
626 	NULL,
627 	NULL,
628 	PHP_MINFO(mhash),
629 	NO_VERSION_YET,
630 	STANDARD_MODULE_PROPERTIES,
631 };
632 
mhash_init(INIT_FUNC_ARGS)633 static void mhash_init(INIT_FUNC_ARGS)
634 {
635 	char buf[128];
636 	int len;
637 	int algo_number = 0;
638 
639 	for (algo_number = 0; algo_number < MHASH_NUM_ALGOS; algo_number++) {
640 		struct mhash_bc_entry algorithm = mhash_to_hash[algo_number];
641 		if (algorithm.mhash_name == NULL) {
642 			continue;
643 		}
644 
645 		len = slprintf(buf, 127, "MHASH_%s", algorithm.mhash_name, strlen(algorithm.mhash_name));
646 		zend_register_long_constant(buf, len + 1, algorithm.value, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
647 	}
648 	zend_register_internal_module(&mhash_module_entry TSRMLS_CC);
649 }
650 
651 /* {{{ proto string mhash(int hash, string data [, string key])
652    Hash data with hash */
PHP_FUNCTION(mhash)653 PHP_FUNCTION(mhash)
654 {
655 	zval **z_algorithm;
656 	long algorithm;
657 
658 	if (zend_parse_parameters(1 TSRMLS_CC, "Z", &z_algorithm) == FAILURE) {
659 		return;
660 	}
661 
662 	SEPARATE_ZVAL(z_algorithm);
663 	convert_to_long_ex(z_algorithm);
664 	algorithm = Z_LVAL_PP(z_algorithm);
665 
666 	/* need to convert the first parameter from int constant to string algorithm name */
667 	if (algorithm >= 0 && algorithm < MHASH_NUM_ALGOS) {
668 		struct mhash_bc_entry algorithm_lookup = mhash_to_hash[algorithm];
669 		if (algorithm_lookup.hash_name) {
670 			ZVAL_STRING(*z_algorithm, algorithm_lookup.hash_name, 1);
671 		}
672 	}
673 
674 	if (ZEND_NUM_ARGS() == 3) {
675 		php_hash_do_hash_hmac(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 1);
676 	} else if (ZEND_NUM_ARGS() == 2) {
677 		php_hash_do_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 1);
678 	} else {
679 		WRONG_PARAM_COUNT;
680 	}
681 }
682 /* }}} */
683 
684 /* {{{ proto string mhash_get_hash_name(int hash)
685    Gets the name of hash */
PHP_FUNCTION(mhash_get_hash_name)686 PHP_FUNCTION(mhash_get_hash_name)
687 {
688 	long algorithm;
689 
690 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &algorithm) == FAILURE) {
691 		return;
692 	}
693 
694 	if (algorithm >= 0 && algorithm  < MHASH_NUM_ALGOS) {
695 		struct mhash_bc_entry algorithm_lookup = mhash_to_hash[algorithm];
696 		if (algorithm_lookup.mhash_name) {
697 			RETURN_STRING(algorithm_lookup.mhash_name, 1);
698 		}
699 	}
700 	RETURN_FALSE;
701 }
702 /* }}} */
703 
704 /* {{{ proto int mhash_count(void)
705    Gets the number of available hashes */
PHP_FUNCTION(mhash_count)706 PHP_FUNCTION(mhash_count)
707 {
708 	if (zend_parse_parameters_none() == FAILURE) {
709 		return;
710 	}
711 	RETURN_LONG(MHASH_NUM_ALGOS - 1);
712 }
713 /* }}} */
714 
715 /* {{{ proto int mhash_get_block_size(int hash)
716    Gets the block size of hash */
PHP_FUNCTION(mhash_get_block_size)717 PHP_FUNCTION(mhash_get_block_size)
718 {
719 	long algorithm;
720 
721 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &algorithm) == FAILURE) {
722 		return;
723 	}
724 	RETVAL_FALSE;
725 
726 	if (algorithm >= 0 && algorithm  < MHASH_NUM_ALGOS) {
727 		struct mhash_bc_entry algorithm_lookup = mhash_to_hash[algorithm];
728 		if (algorithm_lookup.mhash_name) {
729 			const php_hash_ops *ops = php_hash_fetch_ops(algorithm_lookup.hash_name, strlen(algorithm_lookup.hash_name));
730 			if (ops) {
731 				RETVAL_LONG(ops->digest_size);
732 			}
733 		}
734 	}
735 }
736 /* }}} */
737 
738 #define SALT_SIZE 8
739 
740 /* {{{ proto string mhash_keygen_s2k(int hash, string input_password, string salt, int bytes)
741    Generates a key using hash functions */
PHP_FUNCTION(mhash_keygen_s2k)742 PHP_FUNCTION(mhash_keygen_s2k)
743 {
744 	long algorithm, l_bytes;
745 	int bytes;
746 	char *password, *salt;
747 	int password_len, salt_len;
748 	char padded_salt[SALT_SIZE];
749 
750 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lssl", &algorithm, &password, &password_len, &salt, &salt_len, &l_bytes) == FAILURE) {
751 		return;
752 	}
753 
754 	bytes = (int)l_bytes;
755 	if (bytes <= 0){
756 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "the byte parameter must be greater than 0");
757 		RETURN_FALSE;
758 	}
759 
760 	salt_len = MIN(salt_len, SALT_SIZE);
761 
762 	memcpy(padded_salt, salt, salt_len);
763 	if (salt_len < SALT_SIZE) {
764 		memset(padded_salt + salt_len, 0, SALT_SIZE - salt_len);
765 	}
766 	salt_len = SALT_SIZE;
767 
768 	RETVAL_FALSE;
769 	if (algorithm >= 0 && algorithm < MHASH_NUM_ALGOS) {
770 		struct mhash_bc_entry algorithm_lookup = mhash_to_hash[algorithm];
771 		if (algorithm_lookup.mhash_name) {
772 			const php_hash_ops *ops = php_hash_fetch_ops(algorithm_lookup.hash_name, strlen(algorithm_lookup.hash_name));
773 			if (ops) {
774 				unsigned char null = '\0';
775 				void *context;
776 				char *key, *digest;
777 				int i = 0, j = 0;
778 				int block_size = ops->digest_size;
779 				int times = bytes / block_size;
780 				if (bytes % block_size  != 0) times++;
781 
782 				context = emalloc(ops->context_size);
783 				ops->hash_init(context);
784 
785 				key = ecalloc(1, times * block_size);
786 				digest = emalloc(ops->digest_size + 1);
787 
788 				for (i = 0; i < times; i++) {
789 					ops->hash_init(context);
790 
791 					for (j=0;j<i;j++) {
792 						ops->hash_update(context, &null, 1);
793 					}
794 					ops->hash_update(context, (unsigned char *)padded_salt, salt_len);
795 					ops->hash_update(context, (unsigned char *)password, password_len);
796 					ops->hash_final((unsigned char *)digest, context);
797 					memcpy( &key[i*block_size], digest, block_size);
798 				}
799 
800 				RETVAL_STRINGL(key, bytes, 1);
801 				memset(key, 0, bytes);
802 				efree(digest);
803 				efree(context);
804 				efree(key);
805 			}
806 		}
807 	}
808 }
809 /* }}} */
810 
811 #endif
812 
813 /* {{{ PHP_MINIT_FUNCTION
814  */
PHP_MINIT_FUNCTION(hash)815 PHP_MINIT_FUNCTION(hash)
816 {
817 	php_hash_le_hash = zend_register_list_destructors_ex(php_hash_dtor, NULL, PHP_HASH_RESNAME, module_number);
818 
819 	zend_hash_init(&php_hash_hashtable, 35, NULL, NULL, 1);
820 
821 	php_hash_register_algo("md2",			&php_hash_md2_ops);
822 	php_hash_register_algo("md4",			&php_hash_md4_ops);
823 	php_hash_register_algo("md5",			&php_hash_md5_ops);
824 	php_hash_register_algo("sha1",			&php_hash_sha1_ops);
825 	php_hash_register_algo("sha224",		&php_hash_sha224_ops);
826 	php_hash_register_algo("sha256",		&php_hash_sha256_ops);
827 	php_hash_register_algo("sha384",		&php_hash_sha384_ops);
828 	php_hash_register_algo("sha512",		&php_hash_sha512_ops);
829 	php_hash_register_algo("ripemd128",		&php_hash_ripemd128_ops);
830 	php_hash_register_algo("ripemd160",		&php_hash_ripemd160_ops);
831 	php_hash_register_algo("ripemd256",		&php_hash_ripemd256_ops);
832 	php_hash_register_algo("ripemd320",		&php_hash_ripemd320_ops);
833 	php_hash_register_algo("whirlpool",		&php_hash_whirlpool_ops);
834 	php_hash_register_algo("tiger128,3",	&php_hash_3tiger128_ops);
835 	php_hash_register_algo("tiger160,3",	&php_hash_3tiger160_ops);
836 	php_hash_register_algo("tiger192,3",	&php_hash_3tiger192_ops);
837 	php_hash_register_algo("tiger128,4",	&php_hash_4tiger128_ops);
838 	php_hash_register_algo("tiger160,4",	&php_hash_4tiger160_ops);
839 	php_hash_register_algo("tiger192,4",	&php_hash_4tiger192_ops);
840 	php_hash_register_algo("snefru",		&php_hash_snefru_ops);
841 	php_hash_register_algo("snefru256",		&php_hash_snefru_ops);
842 	php_hash_register_algo("gost",			&php_hash_gost_ops);
843 	php_hash_register_algo("adler32",		&php_hash_adler32_ops);
844 	php_hash_register_algo("crc32",			&php_hash_crc32_ops);
845 	php_hash_register_algo("crc32b",		&php_hash_crc32b_ops);
846 	php_hash_register_algo("salsa10",		&php_hash_salsa10_ops);
847 	php_hash_register_algo("salsa20",		&php_hash_salsa20_ops);
848 
849 	PHP_HASH_HAVAL_REGISTER(3,128);
850 	PHP_HASH_HAVAL_REGISTER(3,160);
851 	PHP_HASH_HAVAL_REGISTER(3,192);
852 	PHP_HASH_HAVAL_REGISTER(3,224);
853 	PHP_HASH_HAVAL_REGISTER(3,256);
854 
855 	PHP_HASH_HAVAL_REGISTER(4,128);
856 	PHP_HASH_HAVAL_REGISTER(4,160);
857 	PHP_HASH_HAVAL_REGISTER(4,192);
858 	PHP_HASH_HAVAL_REGISTER(4,224);
859 	PHP_HASH_HAVAL_REGISTER(4,256);
860 
861 	PHP_HASH_HAVAL_REGISTER(5,128);
862 	PHP_HASH_HAVAL_REGISTER(5,160);
863 	PHP_HASH_HAVAL_REGISTER(5,192);
864 	PHP_HASH_HAVAL_REGISTER(5,224);
865 	PHP_HASH_HAVAL_REGISTER(5,256);
866 
867 	REGISTER_LONG_CONSTANT("HASH_HMAC",		PHP_HASH_HMAC,	CONST_CS | CONST_PERSISTENT);
868 
869 #ifdef PHP_MHASH_BC
870 	mhash_init(INIT_FUNC_ARGS_PASSTHRU);
871 #endif
872 
873 	return SUCCESS;
874 }
875 /* }}} */
876 
877 /* {{{ PHP_MSHUTDOWN_FUNCTION
878  */
PHP_MSHUTDOWN_FUNCTION(hash)879 PHP_MSHUTDOWN_FUNCTION(hash)
880 {
881 	zend_hash_destroy(&php_hash_hashtable);
882 
883 	return SUCCESS;
884 }
885 /* }}} */
886 
887 /* {{{ PHP_MINFO_FUNCTION
888  */
PHP_MINFO_FUNCTION(hash)889 PHP_MINFO_FUNCTION(hash)
890 {
891 	HashPosition pos;
892 	char buffer[2048];
893 	char *s = buffer, *e = s + sizeof(buffer), *str;
894 	ulong idx;
895 	long type;
896 
897 	for(zend_hash_internal_pointer_reset_ex(&php_hash_hashtable, &pos);
898 		(type = zend_hash_get_current_key_ex(&php_hash_hashtable, &str, NULL, &idx, 0, &pos)) != HASH_KEY_NON_EXISTANT;
899 		zend_hash_move_forward_ex(&php_hash_hashtable, &pos)) {
900 		s += slprintf(s, e - s, "%s ", str);
901 	}
902 	*s = 0;
903 
904 	php_info_print_table_start();
905 	php_info_print_table_row(2, "hash support", "enabled");
906 	php_info_print_table_row(2, "Hashing Engines", buffer);
907 	php_info_print_table_end();
908 }
909 /* }}} */
910 
911 /* {{{ arginfo */
912 #ifdef PHP_HASH_MD5_NOT_IN_CORE
913 ZEND_BEGIN_ARG_INFO_EX(arginfo_hash_md5, 0, 0, 1)
914 	ZEND_ARG_INFO(0, str)
915 	ZEND_ARG_INFO(0, raw_output)
916 ZEND_END_ARG_INFO()
917 
918 ZEND_BEGIN_ARG_INFO_EX(arginfo_hash_md5_file, 0, 0, 1)
919 	ZEND_ARG_INFO(0, filename)
920 	ZEND_ARG_INFO(0, raw_output)
921 ZEND_END_ARG_INFO()
922 #endif
923 
924 #ifdef PHP_HASH_SHA1_NOT_IN_CORE
925 ZEND_BEGIN_ARG_INFO_EX(arginfo_hash_sha1, 0, 0, 1)
926 	ZEND_ARG_INFO(0, str)
927 	ZEND_ARG_INFO(0, raw_output)
928 ZEND_END_ARG_INFO()
929 
930 ZEND_BEGIN_ARG_INFO_EX(arginfo_hash_sha1_file, 0, 0, 1)
931 	ZEND_ARG_INFO(0, filename)
932 	ZEND_ARG_INFO(0, raw_output)
933 ZEND_END_ARG_INFO()
934 #endif
935 
936 ZEND_BEGIN_ARG_INFO_EX(arginfo_hash, 0, 0, 2)
937 	ZEND_ARG_INFO(0, algo)
938 	ZEND_ARG_INFO(0, data)
939 	ZEND_ARG_INFO(0, raw_output)
940 ZEND_END_ARG_INFO()
941 
942 ZEND_BEGIN_ARG_INFO_EX(arginfo_hash_file, 0, 0, 2)
943 	ZEND_ARG_INFO(0, algo)
944 	ZEND_ARG_INFO(0, filename)
945 	ZEND_ARG_INFO(0, raw_output)
946 ZEND_END_ARG_INFO()
947 
948 ZEND_BEGIN_ARG_INFO_EX(arginfo_hash_hmac, 0, 0, 3)
949 	ZEND_ARG_INFO(0, algo)
950 	ZEND_ARG_INFO(0, data)
951 	ZEND_ARG_INFO(0, key)
952 	ZEND_ARG_INFO(0, raw_output)
953 ZEND_END_ARG_INFO()
954 
955 ZEND_BEGIN_ARG_INFO_EX(arginfo_hash_hmac_file, 0, 0, 3)
956 	ZEND_ARG_INFO(0, algo)
957 	ZEND_ARG_INFO(0, filename)
958 	ZEND_ARG_INFO(0, key)
959 	ZEND_ARG_INFO(0, raw_output)
960 ZEND_END_ARG_INFO()
961 
962 ZEND_BEGIN_ARG_INFO_EX(arginfo_hash_init, 0, 0, 1)
963 	ZEND_ARG_INFO(0, algo)
964 	ZEND_ARG_INFO(0, options)
965 	ZEND_ARG_INFO(0, key)
966 ZEND_END_ARG_INFO()
967 
968 ZEND_BEGIN_ARG_INFO(arginfo_hash_update, 0)
969 	ZEND_ARG_INFO(0, context)
970 	ZEND_ARG_INFO(0, data)
971 ZEND_END_ARG_INFO()
972 
973 ZEND_BEGIN_ARG_INFO_EX(arginfo_hash_update_stream, 0, 0, 2)
974 	ZEND_ARG_INFO(0, context)
975 	ZEND_ARG_INFO(0, handle)
976 	ZEND_ARG_INFO(0, length)
977 ZEND_END_ARG_INFO()
978 
979 ZEND_BEGIN_ARG_INFO_EX(arginfo_hash_update_file, 0, 0, 2)
980 	ZEND_ARG_INFO(0, context)
981 	ZEND_ARG_INFO(0, filename)
982 	ZEND_ARG_INFO(0, context)
983 ZEND_END_ARG_INFO()
984 
985 ZEND_BEGIN_ARG_INFO_EX(arginfo_hash_final, 0, 0, 1)
986 	ZEND_ARG_INFO(0, context)
987 	ZEND_ARG_INFO(0, raw_output)
988 ZEND_END_ARG_INFO()
989 
990 ZEND_BEGIN_ARG_INFO(arginfo_hash_copy, 0)
991 	ZEND_ARG_INFO(0, context)
992 ZEND_END_ARG_INFO()
993 
994 ZEND_BEGIN_ARG_INFO(arginfo_hash_algos, 0)
995 ZEND_END_ARG_INFO()
996 
997 /* BC Land */
998 #ifdef PHP_MHASH_BC
999 ZEND_BEGIN_ARG_INFO(arginfo_mhash_get_block_size, 0)
1000 	ZEND_ARG_INFO(0, hash)
1001 ZEND_END_ARG_INFO()
1002 
1003 ZEND_BEGIN_ARG_INFO(arginfo_mhash_get_hash_name, 0)
1004 	ZEND_ARG_INFO(0, hash)
1005 ZEND_END_ARG_INFO()
1006 
1007 ZEND_BEGIN_ARG_INFO(arginfo_mhash_keygen_s2k, 0)
1008 	ZEND_ARG_INFO(0, hash)
1009 	ZEND_ARG_INFO(0, input_password)
1010 	ZEND_ARG_INFO(0, salt)
1011 	ZEND_ARG_INFO(0, bytes)
1012 ZEND_END_ARG_INFO()
1013 
1014 ZEND_BEGIN_ARG_INFO(arginfo_mhash_count, 0)
1015 ZEND_END_ARG_INFO()
1016 
1017 ZEND_BEGIN_ARG_INFO_EX(arginfo_mhash, 0, 0, 2)
1018 	ZEND_ARG_INFO(0, hash)
1019 	ZEND_ARG_INFO(0, data)
1020 	ZEND_ARG_INFO(0, key)
1021 ZEND_END_ARG_INFO()
1022 #endif
1023 
1024 /* }}} */
1025 
1026 /* {{{ hash_functions[]
1027  */
1028 const zend_function_entry hash_functions[] = {
1029 	PHP_FE(hash,									arginfo_hash)
1030 	PHP_FE(hash_file,								arginfo_hash_file)
1031 
1032 	PHP_FE(hash_hmac,								arginfo_hash_hmac)
1033 	PHP_FE(hash_hmac_file,							arginfo_hash_hmac_file)
1034 
1035 	PHP_FE(hash_init,								arginfo_hash_init)
1036 	PHP_FE(hash_update,								arginfo_hash_update)
1037 	PHP_FE(hash_update_stream,						arginfo_hash_update_stream)
1038 	PHP_FE(hash_update_file,						arginfo_hash_update_file)
1039 	PHP_FE(hash_final,								arginfo_hash_final)
1040 	PHP_FE(hash_copy,								arginfo_hash_copy)
1041 
1042 	PHP_FE(hash_algos,								arginfo_hash_algos)
1043 
1044 	/* BC Land */
1045 #ifdef PHP_HASH_MD5_NOT_IN_CORE
1046 	PHP_NAMED_FE(md5, php_if_md5,					arginfo_hash_md5)
1047 	PHP_NAMED_FE(md5_file, php_if_md5_file,			arginfo_hash_md5_file)
1048 #endif /* PHP_HASH_MD5_NOT_IN_CORE */
1049 
1050 #ifdef PHP_HASH_SHA1_NOT_IN_CORE
1051 	PHP_NAMED_FE(sha1, php_if_sha1,					arginfo_hash_sha1)
1052 	PHP_NAMED_FE(sha1_file, php_if_sha1_file,		arginfo_hash_sha1_file)
1053 #endif /* PHP_HASH_SHA1_NOT_IN_CORE */
1054 
1055 #ifdef PHP_MHASH_BC
1056 	PHP_FE(mhash_keygen_s2k, arginfo_mhash_keygen_s2k)
1057 	PHP_FE(mhash_get_block_size, arginfo_mhash_get_block_size)
1058 	PHP_FE(mhash_get_hash_name, arginfo_mhash_get_hash_name)
1059 	PHP_FE(mhash_count, arginfo_mhash_count)
1060 	PHP_FE(mhash, arginfo_mhash)
1061 #endif
1062 
1063 	PHP_FE_END
1064 };
1065 /* }}} */
1066 
1067 /* {{{ hash_module_entry
1068  */
1069 zend_module_entry hash_module_entry = {
1070 #if ZEND_MODULE_API_NO >= 20010901
1071 	STANDARD_MODULE_HEADER,
1072 #endif
1073 	PHP_HASH_EXTNAME,
1074 	hash_functions,
1075 	PHP_MINIT(hash),
1076 	PHP_MSHUTDOWN(hash),
1077 	NULL, /* RINIT */
1078 	NULL, /* RSHUTDOWN */
1079 	PHP_MINFO(hash),
1080 #if ZEND_MODULE_API_NO >= 20010901
1081 	PHP_HASH_EXTVER, /* Replace with version number for your extension */
1082 #endif
1083 	STANDARD_MODULE_PROPERTIES
1084 };
1085 /* }}} */
1086 
1087 #ifdef COMPILE_DL_HASH
1088 ZEND_GET_MODULE(hash)
1089 #endif
1090 
1091 /*
1092  * Local variables:
1093  * tab-width: 4
1094  * c-basic-offset: 4
1095  * End:
1096  * vim600: noet sw=4 ts=4 fdm=marker
1097  * vim<600: noet sw=4 ts=4
1098  */
1099