[tor-commits] [tor/maint-0.2.2] Add a sha256 hmac function, with tests

nickm at torproject.org nickm at torproject.org
Mon Mar 26 18:02:47 UTC 2012


commit a5704b1c624c9a808f52f3a125339f00e2b9a378
Author: Nick Mathewson <nickm at torproject.org>
Date:   Tue Sep 13 11:38:13 2011 -0400

    Add a sha256 hmac function, with tests
    (cherry picked from commit fdbb9cdf746bbf0c39c34188baa8872471183ff7)
---
 src/common/crypto.c    |   68 +++++++++++++++++++++++++++++++++++++++++++++
 src/common/crypto.h    |    3 ++
 src/test/test_crypto.c |   71 +++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 141 insertions(+), 1 deletions(-)

diff --git a/src/common/crypto.c b/src/common/crypto.c
index 235bd88..88e48ef 100644
--- a/src/common/crypto.c
+++ b/src/common/crypto.c
@@ -1714,6 +1714,74 @@ crypto_hmac_sha1(char *hmac_out,
        (unsigned char*)hmac_out, NULL);
 }
 
+/** Compute the HMAC-SHA-256 of the <b>msg_len</b> bytes in <b>msg</b>, using
+ * the <b>key</b> of length <b>key_len</b>.  Store the DIGEST_LEN-byte result
+ * in <b>hmac_out</b>.
+ */
+void
+crypto_hmac_sha256(char *hmac_out,
+                   const char *key, size_t key_len,
+                   const char *msg, size_t msg_len)
+{
+#if (OPENSSL_VERSION_NUMBER >= 0x00908000l)
+  /* If we've got OpenSSL >=0.9.8 we can use its hmac implementation. */
+  tor_assert(key_len < INT_MAX);
+  tor_assert(msg_len < INT_MAX);
+  HMAC(EVP_sha256(), key, (int)key_len, (unsigned char*)msg, (int)msg_len,
+       (unsigned char*)hmac_out, NULL);
+#else
+  /* OpenSSL doesn't have an EVP implementation for SHA256. We'll need
+     to do HMAC on our own.
+
+     HMAC isn't so hard: To compute HMAC(key, msg):
+      1. If len(key) > blocksize, key = H(key).
+      2. If len(key) < blocksize, right-pad key up to blocksize with 0 bytes.
+      3. let ipad = key xor 0x363636363636....36
+         let opad = key xor 0x5c5c5c5c5c5c....5c
+         The result is H(opad | H( ipad | msg ) )
+  */
+#define BLOCKSIZE 64
+#define DIGESTSIZE 32
+  uint8_t k[BLOCKSIZE];
+  uint8_t pad[BLOCKSIZE];
+  uint8_t d[DIGESTSIZE];
+  int i;
+  SHA256_CTX st;
+
+  tor_assert(key_len < INT_MAX);
+  tor_assert(msg_len < INT_MAX);
+
+  if (key_len <= BLOCKSIZE) {
+    memset(k, 0, sizeof(k));
+    memcpy(k, key, key_len); /* not time invariant in key_len */
+  } else {
+    SHA256((const uint8_t *)key, key_len, k);
+    memset(k+DIGESTSIZE, 0, sizeof(k)-DIGESTSIZE);
+  }
+  for (i = 0; i < BLOCKSIZE; ++i)
+    pad[i] = k[i] ^ 0x36;
+  SHA256_Init(&st);
+  SHA256_Update(&st, pad, BLOCKSIZE);
+  SHA256_Update(&st, (uint8_t*)msg, msg_len);
+  SHA256_Final(d, &st);
+
+  for (i = 0; i < BLOCKSIZE; ++i)
+    pad[i] = k[i] ^ 0x5c;
+  SHA256_Init(&st);
+  SHA256_Update(&st, pad, BLOCKSIZE);
+  SHA256_Update(&st, d, DIGESTSIZE);
+  SHA256_Final((uint8_t*)hmac_out, &st);
+
+  /* Now clear everything. */
+  memset(k, 0, sizeof(k));
+  memset(pad, 0, sizeof(pad));
+  memset(d, 0, sizeof(d));
+  memset(&st, 0, sizeof(st));
+#undef BLOCKSIZE
+#undef DIGESTSIZE
+#endif
+}
+
 /* DH */
 
 /** Shared P parameter for our circuit-crypto DH key exchanges. */
diff --git a/src/common/crypto.h b/src/common/crypto.h
index 1a8c81f..a0ee075 100644
--- a/src/common/crypto.h
+++ b/src/common/crypto.h
@@ -195,6 +195,9 @@ void crypto_digest_assign(crypto_digest_env_t *into,
 void crypto_hmac_sha1(char *hmac_out,
                       const char *key, size_t key_len,
                       const char *msg, size_t msg_len);
+void crypto_hmac_sha256(char *hmac_out,
+                        const char *key, size_t key_len,
+                        const char *msg, size_t msg_len);
 
 /* Key negotiation */
 #define DH_TYPE_CIRCUIT 1
diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c
index 121af27..85a4e92 100644
--- a/src/test/test_crypto.c
+++ b/src/test/test_crypto.c
@@ -231,7 +231,7 @@ test_crypto_sha(void)
 {
   crypto_digest_env_t *d1 = NULL, *d2 = NULL;
   int i;
-  char key[80];
+  char key[160];
   char digest[32];
   char data[50];
   char d_out1[DIGEST_LEN], d_out2[DIGEST256_LEN];
@@ -276,6 +276,75 @@ test_crypto_sha(void)
   test_streq(hex_str(digest, 20),
              "AA4AE5E15272D00E95705637CE8A3B55ED402112");
 
+  /* Test HMAC-SHA256 with test cases from wikipedia and RFC 4231 */
+
+  /* Case empty (wikipedia) */
+  crypto_hmac_sha256(digest, "", 0, "", 0);
+  test_streq(hex_str(digest, 32),
+           "B613679A0814D9EC772F95D778C35FC5FF1697C493715653C6C712144292C5AD");
+
+  /* Case quick-brown (wikipedia) */
+  crypto_hmac_sha256(digest, "key", 3,
+                     "The quick brown fox jumps over the lazy dog", 43);
+  test_streq(hex_str(digest, 32),
+           "F7BC83F430538424B13298E6AA6FB143EF4D59A14946175997479DBC2D1A3CD8");
+
+  /* "Test Case 1" from RFC 4231 */
+  memset(key, 0x0b, 20);
+  crypto_hmac_sha256(digest, key, 20, "Hi There", 8);
+  test_memeq_hex(digest,
+                 "b0344c61d8db38535ca8afceaf0bf12b"
+                 "881dc200c9833da726e9376c2e32cff7");
+
+  /* "Test Case 2" from RFC 4231 */
+  memset(key, 0x0b, 20);
+  crypto_hmac_sha256(digest, "Jefe", 4, "what do ya want for nothing?", 28);
+  test_memeq_hex(digest,
+                 "5bdcc146bf60754e6a042426089575c7"
+                 "5a003f089d2739839dec58b964ec3843");
+
+  /* "Test case 3" from RFC 4231 */
+  memset(key, 0xaa, 20);
+  memset(data, 0xdd, 50);
+  crypto_hmac_sha256(digest, key, 20, data, 50);
+  test_memeq_hex(digest,
+                 "773ea91e36800e46854db8ebd09181a7"
+                 "2959098b3ef8c122d9635514ced565fe");
+
+  /* "Test case 4" from RFC 4231 */
+  base16_decode(key, 25,
+                "0102030405060708090a0b0c0d0e0f10111213141516171819", 50);
+  memset(data, 0xcd, 50);
+  crypto_hmac_sha256(digest, key, 25, data, 50);
+  test_memeq_hex(digest,
+                 "82558a389a443c0ea4cc819899f2083a"
+                 "85f0faa3e578f8077a2e3ff46729665b");
+
+  /* "Test case 5" from RFC 4231 */
+  memset(key, 0x0c, 20);
+  crypto_hmac_sha256(digest, key, 20, "Test With Truncation", 20);
+  test_memeq_hex(digest,
+                 "a3b6167473100ee06e0c796c2955552b");
+
+  /* "Test case 6" from RFC 4231 */
+  memset(key, 0xaa, 131);
+  crypto_hmac_sha256(digest, key, 131,
+                     "Test Using Larger Than Block-Size Key - Hash Key First",
+                     54);
+  test_memeq_hex(digest,
+                 "60e431591ee0b67f0d8a26aacbf5b77f"
+                 "8e0bc6213728c5140546040f0ee37f54");
+
+  /* "Test case 7" from RFC 4231 */
+  memset(key, 0xaa, 131);
+  crypto_hmac_sha256(digest, key, 131,
+                     "This is a test using a larger than block-size key and a "
+                     "larger than block-size data. The key needs to be hashed "
+                     "before being used by the HMAC algorithm.", 152);
+  test_memeq_hex(digest,
+                 "9b09ffa71b942fcb27635fbcd5b0e944"
+                 "bfdc63644f0713938a7f51535c3a35e2");
+
   /* Incremental digest code. */
   d1 = crypto_new_digest_env();
   test_assert(d1);





More information about the tor-commits mailing list