From: Sara Golemon Date: Fri, 18 Nov 2005 20:58:54 +0000 (+0000) Subject: Add HMAC support X-Git-Tag: RELEASE_2_0_2~174 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7cd42570edbf216c2d1d300feb277747fd19f0c1;p=php Add HMAC support --- diff --git a/ext/hash/hash.c b/ext/hash/hash.c index b0fe0c0323..01cb5af701 100644 --- a/ext/hash/hash.c +++ b/ext/hash/hash.c @@ -134,17 +134,18 @@ PHP_FUNCTION(hash_file) { } /* }}} */ -/* {{{ proto resource hash_init(string algo) +/* {{{ proto resource hash_init(string algo[, int options, string key]) Initialize a hashing context */ PHP_FUNCTION(hash_init) { - char *algo; - int algo_len; + char *algo, *key = NULL; + int algo_len, key_len = 0, argc = ZEND_NUM_ARGS(); + long options = 0; void *context; php_hash_ops *ops; php_hash_data *hash; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &algo, &algo_len) == FAILURE) { + if (zend_parse_parameters(argc TSRMLS_CC, "s|ls", &algo, &algo_len, &options, &key, &key_len) == FAILURE) { return; } @@ -154,12 +155,45 @@ PHP_FUNCTION(hash_init) RETURN_FALSE; } + if (options & PHP_HASH_HMAC && + key_len <= 0) { + /* Note: a zero length key is no key at all */ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "HMAC requested without a key"); + RETURN_FALSE; + } + context = emalloc(ops->context_size); ops->hash_init(context); hash = emalloc(sizeof(php_hash_data)); hash->ops = ops; hash->context = context; + hash->options = options; + hash->key = NULL; + + if (options & PHP_HASH_HMAC) { + char *K = emalloc(ops->block_size); + int i; + + memset(K, 0, ops->block_size); + + if (key_len > ops->block_size) { + /* Reduce the key first */ + ops->hash_update(context, key, key_len); + ops->hash_final(K, context); + /* Make the context ready to start over */ + ops->hash_init(context); + } else { + memcpy(K, key, key_len); + } + + /* XOR ipad */ + for(i=0; i < ops->block_size; i++) { + K[i] ^= 0x36; + } + ops->hash_update(context, K, ops->block_size); + hash->key = K; + } ZEND_REGISTER_RESOURCE(return_value, hash, php_hash_le_hash); } @@ -206,6 +240,25 @@ PHP_FUNCTION(hash_final) digest_len = hash->ops->digest_size; digest = emalloc(digest_len + 1); hash->ops->hash_final(digest, hash->context); + if (hash->options & PHP_HASH_HMAC) { + int i; + + /* Convert K to opad -- 0x6A = 0x36 ^ 0x5C */ + for(i=0; i < hash->ops->block_size; i++) { + hash->key[i] ^= 0x6A; + } + + /* Feed this result into the outter hash */ + hash->ops->hash_init(hash->context); + hash->ops->hash_update(hash->context, hash->key, hash->ops->block_size); + hash->ops->hash_update(hash->context, digest, hash->ops->digest_size); + hash->ops->hash_final(digest, hash->context); + + /* Zero the key */ + memset(hash->key, 0, hash->ops->block_size); + efree(hash->key); + hash->key = NULL; + } digest[digest_len] = 0; /* zend_list_REAL_delete() */ @@ -240,6 +293,10 @@ static void php_hash_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) hash->ops->hash_final(dummy, hash->context); efree(dummy); + if (hash->key) { + memset(hash->key, 0, hash->ops->block_size); + efree(hash->key); + } efree(hash->context); efree(hash); } @@ -260,6 +317,8 @@ PHP_MINIT_FUNCTION(hash) php_hash_register_algo("ripemd128", &php_hash_ripemd128_ops); php_hash_register_algo("ripemd160", &php_hash_ripemd160_ops); + REGISTER_LONG_CONSTANT("HASH_HMAC", PHP_HASH_HMAC, CONST_CS | CONST_PERSISTENT); + return SUCCESS; } /* }}} */ diff --git a/ext/hash/hash_md.c b/ext/hash/hash_md.c index b5a53f7c21..2641919934 100644 --- a/ext/hash/hash_md.c +++ b/ext/hash/hash_md.c @@ -26,6 +26,7 @@ php_hash_ops php_hash_md5_ops = { PHP_MD5Update, PHP_MD5Final, 16, + 64, sizeof(PHP_MD5_CTX) }; diff --git a/ext/hash/hash_ripemd.c b/ext/hash/hash_ripemd.c index 21414a40fd..0efa62fc58 100644 --- a/ext/hash/hash_ripemd.c +++ b/ext/hash/hash_ripemd.c @@ -30,6 +30,7 @@ php_hash_ops php_hash_ripemd128_ops = { PHP_RIPEMD128Update, PHP_RIPEMD128Final, 16, + 64, sizeof(PHP_RIPEMD128_CTX) }; @@ -38,6 +39,7 @@ php_hash_ops php_hash_ripemd160_ops = { PHP_RIPEMD160Update, PHP_RIPEMD160Final, 20, + 64, sizeof(PHP_RIPEMD160_CTX) }; diff --git a/ext/hash/hash_sha.c b/ext/hash/hash_sha.c index c5507e046d..6fbdab1709 100644 --- a/ext/hash/hash_sha.c +++ b/ext/hash/hash_sha.c @@ -71,6 +71,7 @@ php_hash_ops php_hash_sha1_ops = { PHP_SHA1Update, PHP_SHA1Final, 20, + 64, sizeof(PHP_SHA1_CTX) }; @@ -417,6 +418,7 @@ php_hash_ops php_hash_sha256_ops = { PHP_SHA256Update, PHP_SHA256Final, 32, + 64, sizeof(PHP_SHA256_CTX) }; @@ -811,6 +813,7 @@ php_hash_ops php_hash_sha384_ops = { PHP_SHA384Update, PHP_SHA384Final, 48, + 128, sizeof(PHP_SHA384_CTX) }; @@ -923,6 +926,7 @@ php_hash_ops php_hash_sha512_ops = { PHP_SHA512Update, PHP_SHA512Final, 64, + 128, sizeof(PHP_SHA512_CTX) }; diff --git a/ext/hash/php_hash.h b/ext/hash/php_hash.h index 4490211c94..6651fdde43 100644 --- a/ext/hash/php_hash.h +++ b/ext/hash/php_hash.h @@ -27,18 +27,24 @@ #define PHP_HASH_EXTVER "0.1" #define PHP_HASH_RESNAME "Hash Context" +#define PHP_HASH_HMAC 0x0001 + typedef struct _php_hash_ops { int (*hash_init)(void *context); int (*hash_update)(void *context, const unsigned char *buf, unsigned int count); int (*hash_final)(unsigned char *digest, void *context); int digest_size; + int block_size; int context_size; } php_hash_ops; typedef struct _php_hash_data { php_hash_ops *ops; void *context; + + long options; + unsigned char *key; } php_hash_data; extern php_hash_ops php_hash_md5_ops; diff --git a/ext/hash/tests/hmac-md5.phpt b/ext/hash/tests/hmac-md5.phpt new file mode 100644 index 0000000000..24939e3224 --- /dev/null +++ b/ext/hash/tests/hmac-md5.phpt @@ -0,0 +1,22 @@ +--TEST-- +hmac-md5 algorithm +--SKIPIF-- + +--FILE-- +