From d32033ada4a4e5821baef20958ae6de0b54622b2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 30 Nov 2016 14:24:08 +1030 Subject: [PATCH] crypto/hmac_sha256: new module. Signed-off-by: Rusty Russell --- ccan/crypto/hmac_sha256/LICENSE | 1 + ccan/crypto/hmac_sha256/_info | 52 +++++++ ccan/crypto/hmac_sha256/hmac_sha256.c | 132 ++++++++++++++++++ ccan/crypto/hmac_sha256/hmac_sha256.h | 34 +++++ ccan/crypto/hmac_sha256/test/api-rfc4231.c | 150 +++++++++++++++++++++ 5 files changed, 369 insertions(+) create mode 120000 ccan/crypto/hmac_sha256/LICENSE create mode 100644 ccan/crypto/hmac_sha256/_info create mode 100644 ccan/crypto/hmac_sha256/hmac_sha256.c create mode 100644 ccan/crypto/hmac_sha256/hmac_sha256.h create mode 100644 ccan/crypto/hmac_sha256/test/api-rfc4231.c diff --git a/ccan/crypto/hmac_sha256/LICENSE b/ccan/crypto/hmac_sha256/LICENSE new file mode 120000 index 00000000..2b1feca5 --- /dev/null +++ b/ccan/crypto/hmac_sha256/LICENSE @@ -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 index 00000000..38612387 --- /dev/null +++ b/ccan/crypto/hmac_sha256/_info @@ -0,0 +1,52 @@ +#include "config.h" +#include +#include + +/** + * crypto/hmac_sha256 - RFC2104 HMAC using SHA256. + * + * This code implements RFC2104, which is a fairly standard HMAC. + * + * License: BSD-MIT + * Maintainer: Rusty Russell + * + * Example: + * #include + * #include + * #include + * #include + * + * // 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 ", 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 index 00000000..d2522955 --- /dev/null +++ b/ccan/crypto/hmac_sha256/hmac_sha256.c @@ -0,0 +1,132 @@ +/* MIT (BSD) license - see LICENSE file for details */ +#include +#include + +#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 index 00000000..2ddcbe32 --- /dev/null +++ b/ccan/crypto/hmac_sha256/hmac_sha256.h @@ -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 +#include +#include + +/* 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 +#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 index 00000000..0c2b2ee5 --- /dev/null +++ b/ccan/crypto/hmac_sha256/test/api-rfc4231.c @@ -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 +#include +#include +#include +#include + +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(); +} -- 2.39.2