commit ff54cc8481f88ecbc40a8abc0a1812fcf583ea73
Author: George Tankersley <george.tankersley(a)gmail.com>
Date: Wed Nov 18 08:37:05 2015 +0000
add SHA512 support to crypto
---
src/common/crypto.c | 67 ++++++++++++++++++++++++++++++++++++++++++------
src/common/crypto.h | 19 +++++++++++---
src/test/test_crypto.c | 28 ++++++++++++++++++++
3 files changed, 102 insertions(+), 12 deletions(-)
diff --git a/src/common/crypto.c b/src/common/crypto.c
index ffbeb81..161ea37 100644
--- a/src/common/crypto.c
+++ b/src/common/crypto.c
@@ -1609,6 +1609,19 @@ crypto_digest256(char *digest, const char *m, size_t len,
return (SHA256((const unsigned char*)m,len,(unsigned char*)digest) == NULL);
}
+/** Compute a 512-bit digest of <b>len</b> bytes in data stored in <b>m</b>,
+ * using the algorithm <b>algorithm</b>. Write the DIGEST_LEN512-byte result
+ * into <b>digest</b>. Return 0 on success, -1 on failure. */
+int
+crypto_digest512(char *digest, const char *m, size_t len,
+ digest_algorithm_t algorithm)
+{
+ tor_assert(m);
+ tor_assert(digest);
+ tor_assert(algorithm == DIGEST_SHA512);
+ return (SHA512((const unsigned char*)m,len,(unsigned char*)digest) == NULL);
+}
+
/** Set the digests_t in <b>ds_out</b> to contain every digest on the
* <b>len</b> bytes in <b>m</b> that we know how to compute. Return 0 on
* success, -1 on failure. */
@@ -1621,8 +1634,18 @@ crypto_digest_all(digests_t *ds_out, const char *m, size_t len)
if (crypto_digest(ds_out->d[DIGEST_SHA1], m, len) < 0)
return -1;
for (i = DIGEST_SHA256; i < N_DIGEST_ALGORITHMS; ++i) {
- if (crypto_digest256(ds_out->d[i], m, len, i) < 0)
- return -1;
+ switch (i) {
+ case DIGEST_SHA256:
+ if (crypto_digest256(ds_out->d[i], m, len, i) < 0)
+ return -1;
+ break;
+ case DIGEST_SHA512:
+ if (crypto_digest512(ds_out->d[i], m, len, i) < 0)
+ return -1;
+ break;
+ default:
+ return -1;
+ }
}
return 0;
}
@@ -1636,6 +1659,8 @@ crypto_digest_algorithm_get_name(digest_algorithm_t alg)
return "sha1";
case DIGEST_SHA256:
return "sha256";
+ case DIGEST_SHA512:
+ return "sha512";
default:
tor_fragile_assert();
return "??unknown_digest??";
@@ -1651,6 +1676,8 @@ crypto_digest_algorithm_parse_name(const char *name)
return DIGEST_SHA1;
else if (!strcmp(name, "sha256"))
return DIGEST_SHA256;
+ else if (!strcmp(name, "sha512"))
+ return DIGEST_SHA512;
else
return -1;
}
@@ -1660,6 +1687,7 @@ struct crypto_digest_t {
union {
SHA_CTX sha1; /**< state for SHA1 */
SHA256_CTX sha2; /**< state for SHA256 */
+ SHA512_CTX sha512; /**< state for SHA512 */
} d; /**< State for the digest we're using. Only one member of the
* union is usable, depending on the value of <b>algorithm</b>. */
digest_algorithm_bitfield_t algorithm : 8; /**< Which algorithm is in use? */
@@ -1690,6 +1718,19 @@ crypto_digest256_new(digest_algorithm_t algorithm)
return r;
}
+/** Allocate and return a new digest object to compute 512-bit digests
+ * using <b>algorithm</b>. */
+crypto_digest_t *
+crypto_digest512_new(digest_algorithm_t algorithm)
+{
+ crypto_digest_t *r;
+ tor_assert(algorithm == DIGEST_SHA512);
+ r = tor_malloc(sizeof(crypto_digest_t));
+ SHA512_Init(&r->d.sha512);
+ r->algorithm = algorithm;
+ return r;
+}
+
/** Deallocate a digest object.
*/
void
@@ -1721,6 +1762,9 @@ crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
case DIGEST_SHA256:
SHA256_Update(&digest->d.sha2, (void*)data, len);
break;
+ case DIGEST_SHA512:
+ SHA512_Update(&digest->d.sha512, (void*)data, len);
+ break;
default:
tor_fragile_assert();
break;
@@ -1729,13 +1773,13 @@ crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
/** Compute the hash of the data that has been passed to the digest
* object; write the first out_len bytes of the result to <b>out</b>.
- * <b>out_len</b> must be \<= DIGEST256_LEN.
+ * <b>out_len</b> must be \<= DIGEST512_LEN.
*/
void
crypto_digest_get_digest(crypto_digest_t *digest,
char *out, size_t out_len)
{
- unsigned char r[DIGEST256_LEN];
+ unsigned char r[DIGEST512_LEN];
crypto_digest_t tmpenv;
tor_assert(digest);
tor_assert(out);
@@ -1750,6 +1794,10 @@ crypto_digest_get_digest(crypto_digest_t *digest,
tor_assert(out_len <= DIGEST256_LEN);
SHA256_Final(r, &tmpenv.d.sha2);
break;
+ case DIGEST_SHA512:
+ tor_assert(out_len <= DIGEST512_LEN);
+ SHA512_Final(r, &tmpenv.d.sha512);
+ break;
default:
log_warn(LD_BUG, "Called with unknown algorithm %d", digest->algorithm);
/* If fragile_assert is not enabled, then we should at least not
@@ -1791,7 +1839,7 @@ crypto_digest_assign(crypto_digest_t *into,
* at <b>digest_out</b> to the hash of the concatenation of those strings,
* plus the optional string <b>append</b>, computed with the algorithm
* <b>alg</b>.
- * <b>out_len</b> must be \<= DIGEST256_LEN. */
+ * <b>out_len</b> must be \<= DIGEST512_LEN. */
void
crypto_digest_smartlist(char *digest_out, size_t len_out,
const smartlist_t *lst,
@@ -1806,7 +1854,7 @@ crypto_digest_smartlist(char *digest_out, size_t len_out,
* optional string <b>prepend</b>, those strings,
* and the optional string <b>append</b>, computed with the algorithm
* <b>alg</b>.
- * <b>out_len</b> must be \<= DIGEST256_LEN. */
+ * <b>out_len</b> must be \<= DIGEST512_LEN. */
void
crypto_digest_smartlist_prefix(char *digest_out, size_t len_out,
const char *prepend,
@@ -1815,10 +1863,13 @@ crypto_digest_smartlist_prefix(char *digest_out, size_t len_out,
digest_algorithm_t alg)
{
crypto_digest_t *d;
- if (alg == DIGEST_SHA1)
+ if (alg == DIGEST_SHA1) {
d = crypto_digest_new();
- else
+ } else if (alg == DIGEST_SHA512) {
+ d = crypto_digest512_new(alg);
+ } else {
d = crypto_digest256_new(alg);
+ }
if (prepend)
crypto_digest_add_bytes(d, prepend, strlen(prepend));
SMARTLIST_FOREACH(lst, const char *, cp,
diff --git a/src/common/crypto.h b/src/common/crypto.h
index 96e202d..4d231d8 100644
--- a/src/common/crypto.h
+++ b/src/common/crypto.h
@@ -54,6 +54,8 @@
/** Length of the output of our second (improved) message digests. (For now
* this is just sha256, but it could be any other 256-bit digest.) */
#define DIGEST256_LEN 32
+/** Length of the output of our 64-bit optimized message digests (SHA512). */
+#define DIGEST512_LEN 64
/** Length of our symmetric cipher's keys. */
#define CIPHER_KEY_LEN 16
/** Length of our symmetric cipher's IV. */
@@ -69,6 +71,9 @@
/** Length of a sha256 message digest when encoded in base64 with trailing =
* signs removed. */
#define BASE64_DIGEST256_LEN 43
+/** Length of a sha512 message digest when encoded in base64 with trailing =
+ * signs removed. */
+#define BASE64_DIGEST512_LEN 86
/** Constant used to indicate OAEP padding for public-key encryption */
#define PK_PKCS1_OAEP_PADDING 60002
@@ -83,24 +88,27 @@
#define HEX_DIGEST_LEN 40
/** Length of hex encoding of SHA256 digest, not including final NUL. */
#define HEX_DIGEST256_LEN 64
+/** Length of hex encoding of SHA512 digest, not including final NUL. */
+#define HEX_DIGEST512_LEN 128
typedef enum {
DIGEST_SHA1 = 0,
DIGEST_SHA256 = 1,
+ DIGEST_SHA512 = 2,
} digest_algorithm_t;
-#define N_DIGEST_ALGORITHMS (DIGEST_SHA256+1)
+#define N_DIGEST_ALGORITHMS (DIGEST_SHA512+1)
#define digest_algorithm_bitfield_t ENUM_BF(digest_algorithm_t)
/** A set of all the digests we know how to compute, taken on a single
- * string. Any digests that are shorter than 256 bits are right-padded
+ * string. Any digests that are shorter than 512 bits are right-padded
* with 0 bits.
*
- * Note that this representation wastes 12 bytes for the SHA1 case, so
+ * Note that this representation wastes 44 bytes for the SHA1 case, so
* don't use it for anything where we need to allocate a whole bunch at
* once.
**/
typedef struct {
- char d[N_DIGEST_ALGORITHMS][DIGEST256_LEN];
+ char d[N_DIGEST_ALGORITHMS][DIGEST512_LEN];
} digests_t;
typedef struct crypto_pk_t crypto_pk_t;
@@ -207,6 +215,8 @@ int crypto_cipher_decrypt_with_iv(const char *key,
int crypto_digest(char *digest, const char *m, size_t len);
int crypto_digest256(char *digest, const char *m, size_t len,
digest_algorithm_t algorithm);
+int crypto_digest512(char *digest, const char *m, size_t len,
+ digest_algorithm_t algorithm);
int crypto_digest_all(digests_t *ds_out, const char *m, size_t len);
struct smartlist_t;
void crypto_digest_smartlist_prefix(char *digest_out, size_t len_out,
@@ -221,6 +231,7 @@ const char *crypto_digest_algorithm_get_name(digest_algorithm_t alg);
int crypto_digest_algorithm_parse_name(const char *name);
crypto_digest_t *crypto_digest_new(void);
crypto_digest_t *crypto_digest256_new(digest_algorithm_t algorithm);
+crypto_digest_t *crypto_digest512_new(digest_algorithm_t algorithm);
void crypto_digest_free(crypto_digest_t *digest);
void crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
size_t len);
diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c
index dbaec61..fcce2c5 100644
--- a/src/test/test_crypto.c
+++ b/src/test/test_crypto.c
@@ -302,6 +302,13 @@ test_crypto_sha(void *arg)
"96177A9CB410FF61F20015AD");
tt_int_op(i, OP_EQ, 0);
+ /* Test SHA-512 with a test vector from the specification. */
+ i = crypto_digest512(data, "abc", 3, DIGEST_SHA512);
+ test_memeq_hex(data, "ddaf35a193617abacc417349ae20413112e6fa4e89a97"
+ "ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3"
+ "feebbd454d4423643ce80e2a9ac94fa54ca49f");
+ tt_int_op(i, OP_EQ, 0);
+
/* Test HMAC-SHA256 with test cases from wikipedia and RFC 4231 */
/* Case empty (wikipedia) */
@@ -410,6 +417,27 @@ test_crypto_sha(void *arg)
crypto_digest_get_digest(d1, d_out1, sizeof(d_out1));
crypto_digest256(d_out2, "abcdef", 6, DIGEST_SHA256);
tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST_LEN);
+ crypto_digest_free(d1);
+ crypto_digest_free(d2);
+
+ /* Incremental digest code with sha512 */
+ d1 = crypto_digest512_new(DIGEST_SHA512);
+ tt_assert(d1);
+ crypto_digest_add_bytes(d1, "abcdef", 6);
+ d2 = crypto_digest_dup(d1);
+ tt_assert(d2);
+ crypto_digest_add_bytes(d2, "ghijkl", 6);
+ crypto_digest_get_digest(d2, d_out1, sizeof(d_out1));
+ crypto_digest512(d_out2, "abcdefghijkl", 12, DIGEST_SHA512);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST_LEN);
+ crypto_digest_assign(d2, d1);
+ crypto_digest_add_bytes(d2, "mno", 3);
+ crypto_digest_get_digest(d2, d_out1, sizeof(d_out1));
+ crypto_digest512(d_out2, "abcdefmno", 9, DIGEST_SHA512);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST_LEN);
+ crypto_digest_get_digest(d1, d_out1, sizeof(d_out1));
+ crypto_digest512(d_out2, "abcdef", 6, DIGEST_SHA512);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST_LEN);
done:
if (d1)