]> git.ozlabs.org Git - ccan/commitdiff
crypto/hmac_sha256: new module.
authorRusty Russell <rusty@rustcorp.com.au>
Wed, 30 Nov 2016 03:54:08 +0000 (14:24 +1030)
committerRusty Russell <rusty@rustcorp.com.au>
Wed, 30 Nov 2016 03:54:08 +0000 (14:24 +1030)
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ccan/crypto/hmac_sha256/LICENSE [new symlink]
ccan/crypto/hmac_sha256/_info [new file with mode: 0644]
ccan/crypto/hmac_sha256/hmac_sha256.c [new file with mode: 0644]
ccan/crypto/hmac_sha256/hmac_sha256.h [new file with mode: 0644]
ccan/crypto/hmac_sha256/test/api-rfc4231.c [new file with mode: 0644]

diff --git a/ccan/crypto/hmac_sha256/LICENSE b/ccan/crypto/hmac_sha256/LICENSE
new file mode 120000 (symlink)
index 0000000..2b1feca
--- /dev/null
@@ -0,0 +1 @@
+../../../licenses/BSD-MIT
\ No newline at end of file
diff --git a/ccan/crypto/hmac_sha256/_info b/ccan/crypto/hmac_sha256/_info
new file mode 100644 (file)
index 0000000..3861238
--- /dev/null
@@ -0,0 +1,52 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * crypto/hmac_sha256 - RFC2104 HMAC using SHA256.
+ *
+ * This code implements RFC2104, which is a fairly standard HMAC.
+ *
+ * License: BSD-MIT
+ * Maintainer: Rusty Russell <rusty@rustcorp.com.au>
+ *
+ * Example:
+ *     #include <ccan/crypto/hmac_sha256/hmac_sha256.h>
+ *     #include <err.h>
+ *     #include <stdio.h>
+ *     #include <string.h>
+ *
+ *     // Simple demonstration: idential strings will have the same hash, but
+ *     // two different strings will not.
+ *     int main(int argc, char *argv[])
+ *     {
+ *             struct hmac_sha256 hash1, hash2;
+ *
+ *             if (argc != 3)
+ *                     errx(1, "Usage: %s <string1> <string2>", argv[0]);
+ *
+ *             hmac_sha256(&hash1, "key", 3, argv[1], strlen(argv[1]));
+ *             hmac_sha256(&hash2, "key", 3, argv[2], strlen(argv[2]));
+ *             printf("Hash is %s\n", memcmp(&hash1, &hash2, sizeof(hash1))
+ *                     ? "different" : "same");
+ *             return 0;
+ *     }
+ */
+int main(int argc, char *argv[])
+{
+       /* Expect exactly one argument */
+       if (argc != 2)
+               return 1;
+
+       if (strcmp(argv[1], "depends") == 0) {
+               printf("ccan/crypto/sha256\n");
+               return 0;
+       }
+
+       if (strcmp(argv[1], "testdepends") == 0) {
+               printf("ccan/str/hex\n");
+               return 0;
+       }
+
+       return 1;
+}
diff --git a/ccan/crypto/hmac_sha256/hmac_sha256.c b/ccan/crypto/hmac_sha256/hmac_sha256.c
new file mode 100644 (file)
index 0000000..d252295
--- /dev/null
@@ -0,0 +1,132 @@
+/* MIT (BSD) license - see LICENSE file for details */
+#include <ccan/crypto/hmac_sha256/hmac_sha256.h>
+#include <string.h>
+
+#define IPAD 0x3636363636363636ULL
+#define OPAD 0x5C5C5C5C5C5C5C5CULL
+
+#define BLOCK_U64S (64 / sizeof(uint64_t))
+
+static inline void xor_block(uint64_t block[BLOCK_U64S], uint64_t pad)
+{
+       size_t i;
+
+       for (i = 0; i < BLOCK_U64S; i++)
+               block[i] ^= pad;
+}
+
+#if 1
+void hmac_sha256(struct hmac_sha256 *hmac,
+                const void *k, size_t ksize,
+                const void *d, size_t dsize)
+{
+       struct sha256_ctx shactx;
+       uint64_t block[BLOCK_U64S];
+       struct sha256 hash, hashed_key;
+
+       /* (keys longer than B bytes are first hashed using H) */
+       if (ksize > sizeof(block)) {
+               sha256(&hashed_key, k, ksize);
+               k = &hashed_key;
+               ksize = sizeof(hashed_key);
+       }
+
+       /* From RFC2104:
+        *
+        * (1) append zeros to the end of K to create a B byte string
+        *  (e.g., if K is of length 20 bytes and B=64, then K will be
+        *   appended with 44 zero bytes 0x00)
+        */
+       memcpy(block, k, ksize);
+       memset((char *)block + ksize, 0, sizeof(block) - ksize);
+
+       /*
+        * (2) XOR (bitwise exclusive-OR) the B byte string computed
+        * in step (1) with ipad
+        */
+       xor_block(block, IPAD);
+
+       /*
+        * (3) append the stream of data 'text' to the B byte string resulting
+        * from step (2)
+        * (4) apply H to the stream generated in step (3)
+        */
+       sha256_init(&shactx);
+       sha256_update(&shactx, block, sizeof(block));
+       sha256_update(&shactx, d, dsize);
+       sha256_done(&shactx, &hash);
+
+       /*
+        * (5) XOR (bitwise exclusive-OR) the B byte string computed in
+        * step (1) with opad
+        */
+       xor_block(block, IPAD^OPAD);
+
+       /*
+        * (6) append the H result from step (4) to the B byte string
+        * resulting from step (5)
+        * (7) apply H to the stream generated in step (6) and output
+        * the result
+        */
+       sha256_init(&shactx);
+       sha256_update(&shactx, block, sizeof(block));
+       sha256_update(&shactx, &hash, sizeof(hash));
+       sha256_done(&shactx, &hmac->sha);
+}
+#else
+/* Direct mapping from MD5 example in RFC2104 */
+void hmac_sha256(struct hmac_sha256 *hmac,
+                const void *key, size_t key_len,
+                const void *text, size_t text_len)
+{
+       struct sha256_ctx context;
+        unsigned char k_ipad[65];    /* inner padding -
+                                      * key XORd with ipad
+                                      */
+        unsigned char k_opad[65];    /* outer padding -
+                                      * key XORd with opad
+                                      *//* start out by storing key in pads */
+       unsigned char tk[32];
+        int i;
+
+        /* if key is longer than 64 bytes reset it to key=MD5(key) */
+        if (key_len > 64) {
+
+                struct sha256_ctx      tctx;
+
+                sha256_init(&tctx);
+                sha256_update(&tctx, key, key_len);
+                sha256_done(&tctx, tk);
+
+                key = tk;
+                key_len = 32;
+        }
+        bzero( k_ipad, sizeof k_ipad);
+        bzero( k_opad, sizeof k_opad);
+        bcopy( key, k_ipad, key_len);
+        bcopy( key, k_opad, key_len);
+
+        /* XOR key with ipad and opad values */
+        for (i=0; i<64; i++) {
+                k_ipad[i] ^= 0x36;
+                k_opad[i] ^= 0x5c;
+        }
+        /*
+         * perform inner MD5
+         */
+        sha256_init(&context);                   /* init context for 1st
+                                              * pass */
+        sha256_update(&context, k_ipad, 64);      /* start with inner pad */
+        sha256_update(&context, text, text_len); /* then text of datagram */
+        sha256_done(&context, &hmac->sha);          /* finish up 1st pass */
+        /*
+         * perform outer MD5
+         */
+        sha256_init(&context);                   /* init context for 2nd
+                                              * pass */
+        sha256_update(&context, k_opad, 64);     /* start with outer pad */
+        sha256_update(&context, &hmac->sha, 32);     /* then results of 1st
+                                              * hash */
+        sha256_done(&context, &hmac->sha);          /* finish up 2nd pass */
+}
+#endif
diff --git a/ccan/crypto/hmac_sha256/hmac_sha256.h b/ccan/crypto/hmac_sha256/hmac_sha256.h
new file mode 100644 (file)
index 0000000..2ddcbe3
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef CCAN_CRYPTO_HMAC_SHA256_H
+#define CCAN_CRYPTO_HMAC_SHA256_H
+/* BSD-MIT - see LICENSE file for details */
+#include "config.h"
+#include <stdint.h>
+#include <stdlib.h>
+#include <ccan/crypto/sha256/sha256.h>
+
+/* Uncomment this to use openssl's HMAC routines (and link with -lcrypto) */
+/*#define CCAN_CRYPTO_HMAC_USE_OPENSSL 1*/
+
+#ifdef CCAN_CRYPTO_HMAC_USE_OPENSSL
+#include <openssl/hmac.h>
+#endif
+
+/**
+ * struct hmac_sha256 - structure representing a completed HMAC.
+ */
+struct hmac_sha256 {
+       struct sha256 sha;
+};
+
+/**
+ * hmac_sha256 - return hmac of an object with a key.
+ * @hmac: the hmac to fill in
+ * @k: pointer to the key,
+ * @ksize: the number of bytes pointed to by @k
+ * @d: pointer to memory,
+ * @dsize: the number of bytes pointed to by @d
+ */
+void hmac_sha256(struct hmac_sha256 *hmac,
+                const void *k, size_t ksize,
+                const void *d, size_t dsize);
+#endif /* CCAN_CRYPTO_HMAC_SHA256_H */
diff --git a/ccan/crypto/hmac_sha256/test/api-rfc4231.c b/ccan/crypto/hmac_sha256/test/api-rfc4231.c
new file mode 100644 (file)
index 0000000..0c2b2ee
--- /dev/null
@@ -0,0 +1,150 @@
+/* From RFC4231 "Identifiers and Test Vectors for HMAC-SHA-224, HMAC-SHA-256,
+ * HMAC-SHA-384, and HMAC-SHA-512"
+ *
+ * https://tools.ietf.org/html/rfc4231
+ */
+#include <ccan/crypto/hmac_sha256/hmac_sha256.h>
+#include <ccan/tap/tap.h>
+#include <ccan/str/hex/hex.h>
+#include <string.h>
+#include <assert.h>
+
+struct test {
+       const char *key, *data, *hmac;
+};
+
+static struct test tests[] = { {
+       /* Test Case 1 */
+       "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",     /* (20 bytes) */
+       "4869205468657265",                             /* ("Hi There") */
+       "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"
+       },
+       /* Test Case 2:
+          Test with a key shorter than the length of the HMAC output. */
+       {
+       "4a656665",                             /* ("Jefe") */
+       /*  ("what do ya want for nothing?") */
+       "7768617420646f2079612077616e7420666f72206e6f7468696e673f",
+       "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"
+       },
+       {
+        /* Test Case 3
+
+          Test with a combined length of key and data that is larger than 64
+          bytes (= block-size of SHA-224 and SHA-256).
+       */
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", /* (20 bytes) */
+       "dddddddddddddddddddddddddddddddd"
+       "dddddddddddddddddddddddddddddddd"
+       "dddddddddddddddddddddddddddddddd"
+       "dddd", /* (50 bytes) */
+       "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe"
+       },
+       {
+       /* Test Case 4
+
+          Test with a combined length of key and data that is larger than 64
+          bytes (= block-size of SHA-224 and SHA-256).
+       */
+       "0102030405060708090a0b0c0d0e0f10111213141516171819", /* (25 bytes) */
+       "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
+       "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
+       "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
+       "cdcd", /* (50 bytes) */
+       "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b"
+       },
+#if 0
+       {
+       /* Test Case 5
+
+          Test with a truncation of output to 128 bits.
+       */
+       "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", /* (20 bytes) */
+       "546573742057697468205472756e636174696f6e", /* ("Test With Truncation") */
+       "a3b6167473100ee06e0c796c2955552b"
+       },
+#endif
+       {
+       /* Test Case 6
+
+          Test with a key larger than 128 bytes (= block-size of SHA-384 and
+          SHA-512).
+       */
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaa", /* (131 bytes) */
+       "54657374205573696e67204c61726765"  /* ("Test Using Large") */
+       "72205468616e20426c6f636b2d53697a"  /* ("r Than Block-Siz") */
+       "65204b6579202d2048617368204b6579"  /* ("e Key - Hash Key") */
+       "204669727374",                     /* (" First") */
+       "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54"
+       },
+       {
+       /* Test Case 7
+
+          Test with a key and data that is larger than 128 bytes (= block-size
+          of SHA-384 and SHA-512). */
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaa", /* (131 bytes) */
+       "54686973206973206120746573742075" /* ("This is a test u") */
+       "73696e672061206c6172676572207468" /* ("sing a larger th") */
+       "616e20626c6f636b2d73697a65206b65" /* ("an block-size ke") */
+       "7920616e642061206c61726765722074" /* ("y and a larger t") */
+       "68616e20626c6f636b2d73697a652064" /* ("han block-size d") */
+       "6174612e20546865206b6579206e6565" /* ("ata. The key nee") */
+       "647320746f2062652068617368656420" /* ("ds to be hashed ") */
+       "6265666f7265206265696e6720757365" /* ("before being use") */
+       "642062792074686520484d414320616c" /* ("d by the HMAC al") */
+       "676f726974686d2e", /*                 ("gorithm.") */
+       "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2"
+       }
+};
+
+static void *fromhex(const char *str, size_t *len)
+{
+       void *p;
+
+       *len = hex_data_size(strlen(str));
+       p = malloc(*len);
+       if (!hex_decode(str, strlen(str), p, *len))
+               abort();
+       return p;
+}
+
+int main(void)
+{
+       size_t i;
+       struct hmac_sha256 hmac;
+
+       plan_tests(sizeof(tests) / sizeof(tests[0]));
+
+       for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
+               size_t ksize, dsize, hmacsize;
+               void *k, *d, *expect;
+
+               k = fromhex(tests[i].key, &ksize);
+               d = fromhex(tests[i].data, &dsize);
+               expect = fromhex(tests[i].hmac, &hmacsize);
+               assert(hmacsize == sizeof(hmac));
+               hmac_sha256(&hmac, k, ksize, d, dsize);
+               ok1(memcmp(&hmac, expect, hmacsize) == 0);
+               free(k);
+               free(d);
+               free(expect);
+       }
+
+       return exit_status();
+}