From 3c3084e1656888d43be3b92169734a6195ec6d75 Mon Sep 17 00:00:00 2001 From: Nick Mathewson <nickm@torproject.org> Date: Wed, 28 Nov 2012 15:38:37 -0500 Subject: Add a crypto_dh_dup, for benchmark support --- src/common/crypto.h | 1 + 1 file changed, 1 insertion(+) (limited to 'src/common/crypto.h') diff --git a/src/common/crypto.h b/src/common/crypto.h index 4c5fa6ad9..eb8b1de16 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -230,6 +230,7 @@ void crypto_hmac_sha256(char *hmac_out, #define DH_TYPE_REND 2 #define DH_TYPE_TLS 3 crypto_dh_t *crypto_dh_new(int dh_type); +crypto_dh_t *crypto_dh_dup(const crypto_dh_t *dh); int crypto_dh_get_bytes(crypto_dh_t *dh); int crypto_dh_generate_public(crypto_dh_t *dh); int crypto_dh_get_public(crypto_dh_t *dh, char *pubkey_out, -- cgit v1.2.3 From 6921d1fd2520df54b29125221eea06f230d78e61 Mon Sep 17 00:00:00 2001 From: Nick Mathewson <nickm@torproject.org> Date: Mon, 3 Dec 2012 12:20:05 -0500 Subject: Implement HKDF from RFC5869 This is a customizable extract-and-expand HMAC-KDF for deriving keys. It derives from RFC5869, which derives its rationale from Krawczyk, H., "Cryptographic Extraction and Key Derivation: The HKDF Scheme", Proceedings of CRYPTO 2010, 2010, <http://eprint.iacr.org/2010/264>. I'm also renaming the existing KDF, now that Tor has two of them. This is the key derivation scheme specified in ntor. There are also unit tests. --- src/common/crypto.c | 76 +++++++++++++++++++++++++++++++++++++++++++++----- src/common/crypto.h | 11 ++++++-- src/or/onion.c | 12 ++++---- src/test/test_crypto.c | 51 +++++++++++++++++++++++++++++++++ 4 files changed, 135 insertions(+), 15 deletions(-) (limited to 'src/common/crypto.h') diff --git a/src/common/crypto.c b/src/common/crypto.c index 37d843396..2147738b4 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -2184,8 +2184,8 @@ crypto_dh_compute_secret(int severity, crypto_dh_t *dh, goto error; } secret_len = result; - if (crypto_expand_key_material(secret_tmp, secret_len, - secret_out, secret_bytes_out)<0) + if (crypto_expand_key_material_TAP((uint8_t*)secret_tmp, secret_len, + (uint8_t*)secret_out, secret_bytes_out)<0) goto error; secret_len = secret_bytes_out; @@ -2211,15 +2211,18 @@ crypto_dh_compute_secret(int severity, crypto_dh_t *dh, * <b>key_out</b> by taking the first <b>key_out_len</b> bytes of * H(K | [00]) | H(K | [01]) | .... * + * This is the key expansion algorithm used in the "TAP" circuit extension + * mechanism; it shouldn't be used for new protocols. + * * Return 0 on success, -1 on failure. */ int -crypto_expand_key_material(const char *key_in, size_t key_in_len, - char *key_out, size_t key_out_len) +crypto_expand_key_material_TAP(const uint8_t *key_in, size_t key_in_len, + uint8_t *key_out, size_t key_out_len) { int i; - char *cp, *tmp = tor_malloc(key_in_len+1); - char digest[DIGEST_LEN]; + uint8_t *cp, *tmp = tor_malloc(key_in_len+1); + uint8_t digest[DIGEST_LEN]; /* If we try to get more than this amount of key data, we'll repeat blocks.*/ tor_assert(key_out_len <= DIGEST_LEN*256); @@ -2228,7 +2231,7 @@ crypto_expand_key_material(const char *key_in, size_t key_in_len, for (cp = key_out, i=0; cp < key_out+key_out_len; ++i, cp += DIGEST_LEN) { tmp[key_in_len] = i; - if (crypto_digest(digest, tmp, key_in_len+1)) + if (crypto_digest((char*)digest, (const char *)tmp, key_in_len+1)) goto err; memcpy(cp, digest, MIN(DIGEST_LEN, key_out_len-(cp-key_out))); } @@ -2244,6 +2247,65 @@ crypto_expand_key_material(const char *key_in, size_t key_in_len, return -1; } +/** Expand some secret key material according to RFC5869, using SHA256 as the + * underlying hash. The <b>key_in_len</b> bytes at <b>key_in</b> are the + * secret key material; the <b>salt_in_len</b> bytes at <b>salt_in</b> and the + * <b>info_in_len</b> bytes in <b>info_in_len</b> are the algorithm's "salt" + * and "info" parameters respectively. On success, write <b>key_out_len</b> + * bytes to <b>key_out</b> and return 0. On failure, return -1. + */ +int +crypto_expand_key_material_rfc5869_sha256( + const uint8_t *key_in, size_t key_in_len, + const uint8_t *salt_in, size_t salt_in_len, + const uint8_t *info_in, size_t info_in_len, + uint8_t *key_out, size_t key_out_len) +{ + uint8_t prk[DIGEST256_LEN]; + uint8_t tmp[DIGEST256_LEN + 128 + 1]; + uint8_t mac[DIGEST256_LEN]; + int i; + uint8_t *outp; + size_t tmp_len; + + crypto_hmac_sha256((char*)prk, + (const char*)salt_in, salt_in_len, + (const char*)key_in, key_in_len); + + /* If we try to get more than this amount of key data, we'll repeat blocks.*/ + tor_assert(key_out_len <= DIGEST256_LEN * 256); + tor_assert(info_in_len <= 128); + memset(tmp, 0, sizeof(tmp)); + outp = key_out; + i = 1; + + while (key_out_len) { + size_t n; + if (i > 1) { + memcpy(tmp, mac, DIGEST256_LEN); + memcpy(tmp+DIGEST256_LEN, info_in, info_in_len); + tmp[DIGEST256_LEN+info_in_len] = i; + tmp_len = DIGEST256_LEN + info_in_len + 1; + } else { + memcpy(tmp, info_in, info_in_len); + tmp[info_in_len] = i; + tmp_len = info_in_len + 1; + } + crypto_hmac_sha256((char*)mac, + (const char*)prk, DIGEST256_LEN, + (const char*)tmp, tmp_len); + n = key_out_len < DIGEST256_LEN ? key_out_len : DIGEST256_LEN; + memcpy(outp, mac, n); + key_out_len -= n; + outp += n; + ++i; + } + + memwipe(tmp, 0, sizeof(tmp)); + memwipe(mac, 0, sizeof(mac)); + return 0; +} + /** Free a DH key exchange object. */ void diff --git a/src/common/crypto.h b/src/common/crypto.h index eb8b1de16..2d31e8d8b 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -239,8 +239,15 @@ ssize_t crypto_dh_compute_secret(int severity, crypto_dh_t *dh, const char *pubkey, size_t pubkey_len, char *secret_out, size_t secret_out_len); void crypto_dh_free(crypto_dh_t *dh); -int crypto_expand_key_material(const char *key_in, size_t in_len, - char *key_out, size_t key_out_len); + +int crypto_expand_key_material_TAP(const uint8_t *key_in, + size_t key_in_len, + uint8_t *key_out, size_t key_out_len); +int crypto_expand_key_material_rfc5869_sha256( + const uint8_t *key_in, size_t key_in_len, + const uint8_t *salt_in, size_t salt_in_len, + const uint8_t *info_in, size_t info_in_len, + uint8_t *key_out, size_t key_out_len); /* random numbers */ int crypto_seed_rng(int startup); diff --git a/src/or/onion.c b/src/or/onion.c index cce4bdf73..472051585 100644 --- a/src/or/onion.c +++ b/src/or/onion.c @@ -362,8 +362,8 @@ fast_server_handshake(const uint8_t *key_in, /* DIGEST_LEN bytes */ uint8_t *key_out, size_t key_out_len) { - char tmp[DIGEST_LEN+DIGEST_LEN]; - char *out = NULL; + uint8_t tmp[DIGEST_LEN+DIGEST_LEN]; + uint8_t *out = NULL; size_t out_len; int r = -1; @@ -374,7 +374,7 @@ fast_server_handshake(const uint8_t *key_in, /* DIGEST_LEN bytes */ memcpy(tmp+DIGEST_LEN, handshake_reply_out, DIGEST_LEN); out_len = key_out_len+DIGEST_LEN; out = tor_malloc(out_len); - if (crypto_expand_key_material(tmp, sizeof(tmp), out, out_len)) { + if (crypto_expand_key_material_TAP(tmp, sizeof(tmp), out, out_len)) { goto done; } memcpy(handshake_reply_out+DIGEST_LEN, out, DIGEST_LEN); @@ -405,8 +405,8 @@ fast_client_handshake(const uint8_t *handshake_state,/*DIGEST_LEN bytes*/ uint8_t *key_out, size_t key_out_len) { - char tmp[DIGEST_LEN+DIGEST_LEN]; - char *out; + uint8_t tmp[DIGEST_LEN+DIGEST_LEN]; + uint8_t *out; size_t out_len; int r = -1; @@ -414,7 +414,7 @@ fast_client_handshake(const uint8_t *handshake_state,/*DIGEST_LEN bytes*/ memcpy(tmp+DIGEST_LEN, handshake_reply_out, DIGEST_LEN); out_len = key_out_len+DIGEST_LEN; out = tor_malloc(out_len); - if (crypto_expand_key_material(tmp, sizeof(tmp), out, out_len)) { + if (crypto_expand_key_material_TAP(tmp, sizeof(tmp), out, out_len)) { goto done; } if (tor_memneq(out, handshake_reply_out+DIGEST_LEN, DIGEST_LEN)) { diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index fd983de00..6c731386d 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -832,6 +832,56 @@ test_crypto_base32_decode(void) ; } +static void +test_crypto_hkdf_sha256(void *arg) +{ + uint8_t key_material[100]; + const uint8_t salt[] = "ntor-curve25519-sha256-1:key_extract"; + const size_t salt_len = strlen((char*)salt); + const uint8_t m_expand[] = "ntor-curve25519-sha256-1:key_expand"; + const size_t m_expand_len = strlen((char*)m_expand); + int r; + char *mem_op_hex_tmp = NULL; + + (void)arg; + +#define EXPAND(s) \ + r = crypto_expand_key_material_rfc5869_sha256( \ + (const uint8_t*)(s), strlen(s), \ + salt, salt_len, \ + m_expand, m_expand_len, \ + key_material, 100) + + /* Test vectors generated with ntor_ref.py */ + memset(key_material, 0, sizeof(key_material)); + EXPAND(""); + tt_int_op(r, ==, 0); + test_memeq_hex(key_material, + "d3490ed48b12a48f9547861583573fe3f19aafe3f81dc7fc75" + "eeed96d741b3290f941576c1f9f0b2d463d1ec7ab2c6bf71cd" + "d7f826c6298c00dbfe6711635d7005f0269493edf6046cc7e7" + "dcf6abe0d20c77cf363e8ffe358927817a3d3e73712cee28d8"); + + EXPAND("Tor"); + tt_int_op(r, ==, 0); + test_memeq_hex(key_material, + "5521492a85139a8d9107a2d5c0d9c91610d0f95989975ebee6" + "c02a4f8d622a6cfdf9b7c7edd3832e2760ded1eac309b76f8d" + "66c4a3c4d6225429b3a016e3c3d45911152fc87bc2de9630c3" + "961be9fdb9f93197ea8e5977180801926d3321fa21513e59ac"); + + EXPAND("AN ALARMING ITEM TO FIND ON YOUR CREDIT-RATING STATEMENT"); + tt_int_op(r, ==, 0); + test_memeq_hex(key_material, + "a2aa9b50da7e481d30463adb8f233ff06e9571a0ca6ab6df0f" + "b206fa34e5bc78d063fc291501beec53b36e5a0e434561200c" + "5f8bd13e0f88b3459600b4dc21d69363e2895321c06184879d" + "94b18f078411be70b767c7fc40679a9440a0c95ea83a23efbf"); + + done: + tor_free(mem_op_hex_tmp); +} + static void * pass_data_setup_fn(const struct testcase_t *testcase) { @@ -863,6 +913,7 @@ struct testcase_t crypto_tests[] = { { "aes_iv_AES", test_crypto_aes_iv, TT_FORK, &pass_data, (void*)"aes" }, { "aes_iv_EVP", test_crypto_aes_iv, TT_FORK, &pass_data, (void*)"evp" }, CRYPTO_LEGACY(base32_decode), + { "hkdf_sha256", test_crypto_hkdf_sha256, 0, NULL, NULL }, END_OF_TESTCASES }; -- cgit v1.2.3 From 25c05cb747eece7d720a3f79c172e83a0e79a3a1 Mon Sep 17 00:00:00 2001 From: Nick Mathewson <nickm@torproject.org> Date: Mon, 3 Dec 2012 23:31:07 -0500 Subject: Refactor strong os-RNG into its own function Previously, we only used the strong OS entropy source as part of seeding OpenSSL's RNG. But with curve25519, we'll have occasion to want to generate some keys using extremely-good entopy, as well as the means to do so. So let's! This patch refactors the OS-entropy wrapper into its own crypto_strongest_rand() function, and makes our new curve25519_secret_key_generate function try it as appropriate. --- src/common/crypto.c | 77 ++++++++++++++++++++++++------------------ src/common/crypto.h | 1 + src/common/crypto_curve25519.c | 13 +++++-- src/test/test_crypto.c | 2 +- 4 files changed, 58 insertions(+), 35 deletions(-) (limited to 'src/common/crypto.h') diff --git a/src/common/crypto.c b/src/common/crypto.c index 2147738b4..4d61f2d93 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -2344,22 +2344,16 @@ seed_weak_rng(void) tor_init_weak_random(seed); } -/** Seed OpenSSL's random number generator with bytes from the operating - * system. <b>startup</b> should be true iff we have just started Tor and - * have not yet allocated a bunch of fds. Return 0 on success, -1 on failure. +/** Try to get <b>out_len</b> bytes of the strongest entropy we can generate, + * storing it into <b>out</b>. */ int -crypto_seed_rng(int startup) +crypto_strongest_rand(uint8_t *out, size_t out_len) { - int rand_poll_status = 0; - - /* local variables */ #ifdef _WIN32 - unsigned char buf[ADD_ENTROPY]; static int provider_set = 0; static HCRYPTPROV provider; #else - char buf[ADD_ENTROPY]; static const char *filenames[] = { "/dev/srandom", "/dev/urandom", "/dev/random", NULL }; @@ -2367,58 +2361,77 @@ crypto_seed_rng(int startup) size_t n; #endif - /* OpenSSL has a RAND_poll function that knows about more kinds of - * entropy than we do. We'll try calling that, *and* calling our own entropy - * functions. If one succeeds, we'll accept the RNG as seeded. */ - if (startup || RAND_POLL_IS_SAFE) { - rand_poll_status = RAND_poll(); - if (rand_poll_status == 0) - log_warn(LD_CRYPTO, "RAND_poll() failed."); - } - #ifdef _WIN32 if (!provider_set) { if (!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { if ((unsigned long)GetLastError() != (unsigned long)NTE_BAD_KEYSET) { log_warn(LD_CRYPTO, "Can't get CryptoAPI provider [1]"); - return rand_poll_status ? 0 : -1; + return -1; } } provider_set = 1; } - if (!CryptGenRandom(provider, sizeof(buf), buf)) { + if (!CryptGenRandom(provider, out_len, out)) { log_warn(LD_CRYPTO, "Can't get entropy from CryptoAPI."); - return rand_poll_status ? 0 : -1; + return -1; } - RAND_seed(buf, sizeof(buf)); - memwipe(buf, 0, sizeof(buf)); - seed_weak_rng(); + return 0; #else for (i = 0; filenames[i]; ++i) { fd = open(filenames[i], O_RDONLY, 0); if (fd<0) continue; - log_info(LD_CRYPTO, "Seeding RNG from \"%s\"", filenames[i]); - n = read_all(fd, buf, sizeof(buf), 0); + log_info(LD_CRYPTO, "Reading entropy from \"%s\"", filenames[i]); + n = read_all(fd, (char*)out, out_len, 0); close(fd); - if (n != sizeof(buf)) { + if (n != out_len) { log_warn(LD_CRYPTO, "Error reading from entropy source (read only %lu bytes).", (unsigned long)n); return -1; } - RAND_seed(buf, (int)sizeof(buf)); - memwipe(buf, 0, sizeof(buf)); - seed_weak_rng(); + return 0; } - log_warn(LD_CRYPTO, "Cannot seed RNG -- no entropy source found."); - return rand_poll_status ? 0 : -1; + log_warn(LD_CRYPTO, "Cannot get strong entropy: no entropy source found."); + return -1; #endif } +/** Seed OpenSSL's random number generator with bytes from the operating + * system. <b>startup</b> should be true iff we have just started Tor and + * have not yet allocated a bunch of fds. Return 0 on success, -1 on failure. + */ +int +crypto_seed_rng(int startup) +{ + int rand_poll_ok = 0, load_entropy_ok = 0; + uint8_t buf[ADD_ENTROPY]; + + /* OpenSSL has a RAND_poll function that knows about more kinds of + * entropy than we do. We'll try calling that, *and* calling our own entropy + * functions. If one succeeds, we'll accept the RNG as seeded. */ + if (startup || RAND_POLL_IS_SAFE) { + rand_poll_ok = RAND_poll(); + if (rand_poll_ok == 0) + log_warn(LD_CRYPTO, "RAND_poll() failed."); + } + + load_entropy_ok = !crypto_strongest_rand(buf, sizeof(buf)); + if (load_entropy_ok) { + RAND_seed(buf, sizeof(buf)); + } + + memwipe(buf, 0, sizeof(buf)); + seed_weak_rng(); + if (rand_poll_ok || load_entropy_ok) + return 0; + else + return -1; +} + /** Write <b>n</b> bytes of strong random data to <b>to</b>. Return 0 on * success, -1 on failure. */ diff --git a/src/common/crypto.h b/src/common/crypto.h index 2d31e8d8b..b6e8e6c56 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -252,6 +252,7 @@ int crypto_expand_key_material_rfc5869_sha256( /* random numbers */ int crypto_seed_rng(int startup); int crypto_rand(char *to, size_t n); +int crypto_strongest_rand(uint8_t *out, size_t out_len); int crypto_rand_int(unsigned int max); uint64_t crypto_rand_uint64(uint64_t max); double crypto_rand_double(void); diff --git a/src/common/crypto_curve25519.c b/src/common/crypto_curve25519.c index 1985e8af2..ce0cd0d60 100644 --- a/src/common/crypto_curve25519.c +++ b/src/common/crypto_curve25519.c @@ -59,9 +59,18 @@ void curve25519_secret_key_generate(curve25519_secret_key_t *key_out, int extra_strong) { - (void)extra_strong; + uint8_t k_tmp[CURVE25519_SECKEY_LEN]; - crypto_rand((char*)key_out->secret_key, 32); + crypto_rand((char*)key_out->secret_key, CURVE25519_SECKEY_LEN); + if (extra_strong && !crypto_strongest_rand(k_tmp, CURVE25519_SECKEY_LEN)) { + /* If they asked for extra-strong entropy and we have some, use it as an + * HMAC key to improve not-so-good entopy rather than using it directly, + * just in case the extra-strong entropy is less amazing than we hoped. */ + crypto_hmac_sha256((char *)key_out->secret_key, + (const char *)k_tmp, sizeof(k_tmp), + (const char *)key_out->secret_key, CURVE25519_SECKEY_LEN); + } + memwipe(k_tmp, 0, sizeof(k_tmp)); key_out->secret_key[0] &= 248; key_out->secret_key[31] &= 127; key_out->secret_key[31] |= 64; diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index 8aadd979a..2b3229aff 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -993,7 +993,7 @@ test_crypto_curve25519_wrappers(void *arg) /* Test a simple handshake, serializing and deserializing some stuff. */ curve25519_secret_key_generate(&seckey1, 0); - curve25519_secret_key_generate(&seckey2, 0); + curve25519_secret_key_generate(&seckey2, 1); curve25519_public_key_generate(&pubkey1, &seckey1); curve25519_public_key_generate(&pubkey2, &seckey2); test_assert(curve25519_public_key_is_ok(&pubkey1)); -- cgit v1.2.3