]> granicus.if.org Git - php/commitdiff
Add HMAC support
authorSara Golemon <pollita@php.net>
Fri, 18 Nov 2005 20:58:54 +0000 (20:58 +0000)
committerSara Golemon <pollita@php.net>
Fri, 18 Nov 2005 20:58:54 +0000 (20:58 +0000)
ext/hash/hash.c
ext/hash/hash_md.c
ext/hash/hash_ripemd.c
ext/hash/hash_sha.c
ext/hash/php_hash.h
ext/hash/tests/hmac-md5.phpt [new file with mode: 0644]

index b0fe0c03232ea4d44cdc2b4f54bb41b32f8af2b9..01cb5af7011df40cf80ddbee07b5dd30fd48e9de 100644 (file)
@@ -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;
 }
 /* }}} */
index b5a53f7c214568427c7fb4fcdebbbe380e8ce4f2..2641919934f364b777f38b14138a3d734fc7f01c 100644 (file)
@@ -26,6 +26,7 @@ php_hash_ops php_hash_md5_ops = {
        PHP_MD5Update,
        PHP_MD5Final,
        16,
+       64,
        sizeof(PHP_MD5_CTX)
 };
 
index 21414a40fdb82e2eb95360be5088ad8b9b7fc644..0efa62fc58ba350230bda2396ade0e66d573a9a9 100644 (file)
@@ -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)
 };
 
index c5507e046d5d6b4ebb8c711e5bb58442bd529671..6fbdab170946a89638be2c35e766b26c1b441344 100644 (file)
@@ -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)
 };
 
index 4490211c941259cec2298438e8eb3f4a86f30b06..6651fdde43e502904db503743a3fe65f22a5d6cc 100644 (file)
 #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 (file)
index 0000000..24939e3
--- /dev/null
@@ -0,0 +1,22 @@
+--TEST--
+hmac-md5 algorithm
+--SKIPIF--
+<?php if(!extension_loaded("hash")) print "skip"; ?>
+--FILE--
+<?php
+/* Test Vectors from RFC 2104 */
+$ctx = hash_init('md5',HASH_HMAC,str_repeat(chr(0x0b), 16));
+hash_update($ctx, 'Hi There');
+echo hash_final($ctx) . "\n";
+
+$ctx = hash_init('md5',HASH_HMAC,'Jefe');
+hash_update($ctx, 'what do ya want for nothing?');
+echo hash_final($ctx) . "\n";
+
+$ctx = hash_init('md5',HASH_HMAC,str_repeat(chr(0xAA), 16));
+hash_update($ctx, str_repeat(chr(0xDD), 50));
+echo hash_final($ctx) . "\n";
+--EXPECTF--
+9294727a3638bb1c13f48ef8158bfc9d
+750c783e6ab0b503eaa86e310a5db738
+56be34521d144c88dbb8c733f0e8b3f6