aboutsummaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/crypto.c68
-rw-r--r--src/common/crypto.h3
2 files changed, 71 insertions, 0 deletions
diff --git a/src/common/crypto.c b/src/common/crypto.c
index 235bd88ff..88e48ef84 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 1a8c81f83..a0ee075ba 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