diff --git a/changes/feature17663 b/changes/feature17663 new file mode 100644 index 0000000000..f867c038df --- /dev/null +++ b/changes/feature17663 @@ -0,0 +1,2 @@ + o Minor feature: + - Adds SHA512 support to crypto.c diff --git a/src/common/crypto.c b/src/common/crypto.c index ffbeb81089..72e08c98ce 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 len bytes in data stored in m, + * using the algorithm algorithm. Write the DIGEST_LEN512-byte result + * into digest. 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 ds_out to contain every digest on the * len bytes in m 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 algorithm. */ 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 algorithm. */ +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 out. - * out_len must be \<= DIGEST256_LEN. + * out_len 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 digest_out to the hash of the concatenation of those strings, * plus the optional string append, computed with the algorithm * alg. - * out_len must be \<= DIGEST256_LEN. */ + * out_len 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 prepend, those strings, * and the optional string append, computed with the algorithm * alg. - * out_len must be \<= DIGEST256_LEN. */ + * len_out must be \<= DIGEST512_LEN. */ void crypto_digest_smartlist_prefix(char *digest_out, size_t len_out, const char *prepend, @@ -1814,11 +1862,25 @@ crypto_digest_smartlist_prefix(char *digest_out, size_t len_out, const char *append, digest_algorithm_t alg) { - crypto_digest_t *d; - if (alg == DIGEST_SHA1) - d = crypto_digest_new(); - else - d = crypto_digest256_new(alg); + crypto_digest_t *d = NULL; + switch (alg) { + case DIGEST_SHA1: + d = crypto_digest_new(); + break; + case DIGEST_SHA256: + d = crypto_digest256_new(alg); + break; + case DIGEST_SHA512: + d = crypto_digest512_new(alg); + break; + default: + log_warn(LD_BUG, "Called with unknown algorithm %d", alg); + /* If fragile_assert is not enabled, wipe output and return + * without running any calculations */ + memwipe(digest_out, 0xff, len_out); + tor_fragile_assert(); + goto free; + } if (prepend) crypto_digest_add_bytes(d, prepend, strlen(prepend)); SMARTLIST_FOREACH(lst, const char *, cp, @@ -1826,7 +1888,10 @@ crypto_digest_smartlist_prefix(char *digest_out, size_t len_out, if (append) crypto_digest_add_bytes(d, append, strlen(append)); crypto_digest_get_digest(d, digest_out, len_out); - crypto_digest_free(d); + + free: + if (d != NULL) + crypto_digest_free(d); } /** Compute the HMAC-SHA-256 of the msg_len bytes in msg, using diff --git a/src/common/crypto.h b/src/common/crypto.h index 96e202d187..4d231d81b3 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 dbaec61ee9..fcce2c52cb 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)