/** Convenience function to represent HMAC_SHA256 as our instantiation of * ntor's "tweaked hash'. Hash the <b>inp_len</b> bytes at <b>inp</b> into * a DIGEST256_LEN-byte digest at <b>out</b>, with the hash changing * depending on the value of <b>tweak</b>. */ static void h_tweak(uint8_t *out, const uint8_t *inp, size_t inp_len, const char *tweak) { size_t tweak_len = strlen(tweak); crypto_hmac_sha256((char*)out, tweak, tweak_len, (const char*)inp, inp_len); }
/** * Generate CURVE25519_SECKEY_LEN random bytes in <b>out</b>. If * <b>extra_strong</b> is true, this key is possibly going to get used more * than once, so use a better-than-usual RNG. Return 0 on success, -1 on * failure. * * This function does not adjust the output of the RNG at all; the will caller * will need to clear or set the appropriate bits to make curve25519 work. */ int curve25519_rand_seckey_bytes(uint8_t *out, int extra_strong) { uint8_t k_tmp[CURVE25519_SECKEY_LEN]; if (crypto_rand((char*)out, CURVE25519_SECKEY_LEN) < 0) return -1; 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 entropy rather than using it directly, * just in case the extra-strong entropy is less amazing than we hoped. */ crypto_hmac_sha256((char*) out, (const char *)k_tmp, sizeof(k_tmp), (const char *)out, CURVE25519_SECKEY_LEN); } memwipe(k_tmp, 0, sizeof(k_tmp)); return 0; }
/** Called when we get an AUTHCHALLENGE command. */ int handle_control_authchallenge(control_connection_t *conn, uint32_t len, const char *body) { const char *cp = body; char *client_nonce; size_t client_nonce_len; char server_hash[DIGEST256_LEN]; char server_hash_encoded[HEX_DIGEST256_LEN+1]; char server_nonce[SAFECOOKIE_SERVER_NONCE_LEN]; char server_nonce_encoded[(2*SAFECOOKIE_SERVER_NONCE_LEN) + 1]; cp += strspn(cp, " \t\n\r"); if (!strcasecmpstart(cp, "SAFECOOKIE")) { cp += strlen("SAFECOOKIE"); } else { connection_write_str_to_buf("513 AUTHCHALLENGE only supports SAFECOOKIE " "authentication\r\n", conn); connection_mark_for_close(TO_CONN(conn)); return -1; } if (!authentication_cookie_is_set) { connection_write_str_to_buf("515 Cookie authentication is disabled\r\n", conn); connection_mark_for_close(TO_CONN(conn)); return -1; } cp += strspn(cp, " \t\n\r"); if (*cp == '"') { const char *newcp = decode_escaped_string(cp, len - (cp - body), &client_nonce, &client_nonce_len); if (newcp == NULL) { connection_write_str_to_buf("513 Invalid quoted client nonce\r\n", conn); connection_mark_for_close(TO_CONN(conn)); return -1; } cp = newcp; } else { size_t client_nonce_encoded_len = strspn(cp, "0123456789ABCDEFabcdef"); client_nonce_len = client_nonce_encoded_len / 2; client_nonce = tor_malloc_zero(client_nonce_len); if (base16_decode(client_nonce, client_nonce_len, cp, client_nonce_encoded_len) != (int) client_nonce_len) { connection_write_str_to_buf("513 Invalid base16 client nonce\r\n", conn); connection_mark_for_close(TO_CONN(conn)); tor_free(client_nonce); return -1; } cp += client_nonce_encoded_len; } cp += strspn(cp, " \t\n\r"); if (*cp != '\0' || cp != body + len) { connection_write_str_to_buf("513 Junk at end of AUTHCHALLENGE command\r\n", conn); connection_mark_for_close(TO_CONN(conn)); tor_free(client_nonce); return -1; } crypto_rand(server_nonce, SAFECOOKIE_SERVER_NONCE_LEN); /* Now compute and send the server-to-controller response, and the * server's nonce. */ tor_assert(authentication_cookie != NULL); { size_t tmp_len = (AUTHENTICATION_COOKIE_LEN + client_nonce_len + SAFECOOKIE_SERVER_NONCE_LEN); char *tmp = tor_malloc_zero(tmp_len); char *client_hash = tor_malloc_zero(DIGEST256_LEN); memcpy(tmp, authentication_cookie, AUTHENTICATION_COOKIE_LEN); memcpy(tmp + AUTHENTICATION_COOKIE_LEN, client_nonce, client_nonce_len); memcpy(tmp + AUTHENTICATION_COOKIE_LEN + client_nonce_len, server_nonce, SAFECOOKIE_SERVER_NONCE_LEN); crypto_hmac_sha256(server_hash, SAFECOOKIE_SERVER_TO_CONTROLLER_CONSTANT, strlen(SAFECOOKIE_SERVER_TO_CONTROLLER_CONSTANT), tmp, tmp_len); crypto_hmac_sha256(client_hash, SAFECOOKIE_CONTROLLER_TO_SERVER_CONSTANT, strlen(SAFECOOKIE_CONTROLLER_TO_SERVER_CONSTANT), tmp, tmp_len); conn->safecookie_client_hash = client_hash; tor_free(tmp); } base16_encode(server_hash_encoded, sizeof(server_hash_encoded), server_hash, sizeof(server_hash)); base16_encode(server_nonce_encoded, sizeof(server_nonce_encoded), server_nonce, sizeof(server_nonce)); connection_printf_to_buf(conn, "250 AUTHCHALLENGE SERVERHASH=%s " "SERVERNONCE=%s\r\n", server_hash_encoded, server_nonce_encoded); tor_free(client_nonce); return 0; }
/** Run unit tests for our SHA-1 functionality */ static void test_crypto_sha(void) { crypto_digest_t *d1 = NULL, *d2 = NULL; int i; char key[160]; char digest[32]; char data[50]; char d_out1[DIGEST_LEN], d_out2[DIGEST256_LEN]; char *mem_op_hex_tmp=NULL; /* Test SHA-1 with a test vector from the specification. */ i = crypto_digest(data, "abc", 3); test_memeq_hex(data, "A9993E364706816ABA3E25717850C26C9CD0D89D"); tt_int_op(i, ==, 0); /* Test SHA-256 with a test vector from the specification. */ i = crypto_digest256(data, "abc", 3, DIGEST_SHA256); test_memeq_hex(data, "BA7816BF8F01CFEA414140DE5DAE2223B00361A3" "96177A9CB410FF61F20015AD"); tt_int_op(i, ==, 0); /* Test HMAC-SHA-1 with test cases from RFC2202. */ /* Case 1. */ memset(key, 0x0b, 20); crypto_hmac_sha1(digest, key, 20, "Hi There", 8); test_streq(hex_str(digest, 20), "B617318655057264E28BC0B6FB378C8EF146BE00"); /* Case 2. */ crypto_hmac_sha1(digest, "Jefe", 4, "what do ya want for nothing?", 28); test_streq(hex_str(digest, 20), "EFFCDF6AE5EB2FA2D27416D5F184DF9C259A7C79"); /* Case 4. */ base16_decode(key, 25, "0102030405060708090a0b0c0d0e0f10111213141516171819", 50); memset(data, 0xcd, 50); crypto_hmac_sha1(digest, key, 25, data, 50); test_streq(hex_str(digest, 20), "4C9007F4026250C6BC8414F9BF50C86C2D7235DA"); /* Case 5. */ memset(key, 0xaa, 80); crypto_hmac_sha1(digest, key, 80, "Test Using Larger Than Block-Size Key - Hash Key First", 54); 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_digest_new(); test_assert(d1); crypto_digest_add_bytes(d1, "abcdef", 6); d2 = crypto_digest_dup(d1); test_assert(d2); crypto_digest_add_bytes(d2, "ghijkl", 6); crypto_digest_get_digest(d2, d_out1, sizeof(d_out1)); crypto_digest(d_out2, "abcdefghijkl", 12); test_memeq(d_out1, 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_digest(d_out2, "abcdefmno", 9); test_memeq(d_out1, d_out2, DIGEST_LEN); crypto_digest_get_digest(d1, d_out1, sizeof(d_out1)); crypto_digest(d_out2, "abcdef", 6); test_memeq(d_out1, d_out2, DIGEST_LEN); crypto_digest_free(d1); crypto_digest_free(d2); /* Incremental digest code with sha256 */ d1 = crypto_digest256_new(DIGEST_SHA256); test_assert(d1); crypto_digest_add_bytes(d1, "abcdef", 6); d2 = crypto_digest_dup(d1); test_assert(d2); crypto_digest_add_bytes(d2, "ghijkl", 6); crypto_digest_get_digest(d2, d_out1, sizeof(d_out1)); crypto_digest256(d_out2, "abcdefghijkl", 12, DIGEST_SHA256); test_memeq(d_out1, 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_digest256(d_out2, "abcdefmno", 9, DIGEST_SHA256); test_memeq(d_out1, d_out2, DIGEST_LEN); crypto_digest_get_digest(d1, d_out1, sizeof(d_out1)); crypto_digest256(d_out2, "abcdef", 6, DIGEST_SHA256); test_memeq(d_out1, d_out2, DIGEST_LEN); done: if (d1) crypto_digest_free(d1); if (d2) crypto_digest_free(d2); tor_free(mem_op_hex_tmp); }
/** * Make an authenticated passphrase-encrypted blob to encode the * <b>input_len</b> bytes in <b>input</b> using the passphrase * <b>secret</b> of <b>secret_len</b> bytes. Allocate a new chunk of memory * to hold the encrypted data, and store a pointer to that memory in * *<b>out</b>, and its size in <b>outlen_out</b>. Use <b>s2k_flags</b> as an * argument to the passphrase-hashing function. */ int crypto_pwbox(uint8_t **out, size_t *outlen_out, const uint8_t *input, size_t input_len, const char *secret, size_t secret_len, unsigned s2k_flags) { uint8_t *result = NULL, *encrypted_portion; size_t encrypted_len = 128 * CEIL_DIV(input_len+4, 128); ssize_t result_len; int spec_len; uint8_t keys[CIPHER_KEY_LEN + DIGEST256_LEN]; pwbox_encoded_t *enc = NULL; ssize_t enc_len; crypto_cipher_t *cipher; int rv; enc = pwbox_encoded_new(); pwbox_encoded_setlen_skey_header(enc, S2K_MAXLEN); spec_len = secret_to_key_make_specifier( pwbox_encoded_getarray_skey_header(enc), S2K_MAXLEN, s2k_flags); if (BUG(spec_len < 0 || spec_len > S2K_MAXLEN)) goto err; pwbox_encoded_setlen_skey_header(enc, spec_len); enc->header_len = spec_len; crypto_rand((char*)enc->iv, sizeof(enc->iv)); pwbox_encoded_setlen_data(enc, encrypted_len); encrypted_portion = pwbox_encoded_getarray_data(enc); set_uint32(encrypted_portion, htonl((uint32_t)input_len)); memcpy(encrypted_portion+4, input, input_len); /* Now that all the data is in position, derive some keys, encrypt, and * digest */ const int s2k_rv = secret_to_key_derivekey(keys, sizeof(keys), pwbox_encoded_getarray_skey_header(enc), spec_len, secret, secret_len); if (BUG(s2k_rv < 0)) goto err; cipher = crypto_cipher_new_with_iv((char*)keys, (char*)enc->iv); crypto_cipher_crypt_inplace(cipher, (char*)encrypted_portion, encrypted_len); crypto_cipher_free(cipher); result_len = pwbox_encoded_encoded_len(enc); if (BUG(result_len < 0)) goto err; result = tor_malloc(result_len); enc_len = pwbox_encoded_encode(result, result_len, enc); if (BUG(enc_len < 0)) goto err; tor_assert(enc_len == result_len); crypto_hmac_sha256((char*) result + result_len - 32, (const char*)keys + CIPHER_KEY_LEN, DIGEST256_LEN, (const char*)result, result_len - 32); *out = result; *outlen_out = result_len; rv = 0; goto out; err: /* LCOV_EXCL_START This error case is often unreachable if we're correctly coded, unless somebody adds a new error case somewhere, or unless you're building without scrypto support. - make_specifier can't fail, unless S2K_MAX_LEN is too short. - secret_to_key_derivekey can't really fail unless we're missing scrypt, or the underlying function fails, or we pass it a bogus algorithm or parameters. - pwbox_encoded_encoded_len can't fail unless we're using trunnel incorrectly. - pwbox_encoded_encode can't fail unless we're using trunnel wrong, or it's buggy. */ tor_free(result); rv = -1; /* LCOV_EXCL_STOP */ out: pwbox_encoded_free(enc); memwipe(keys, 0, sizeof(keys)); return rv; }
/** * Try to decrypt the passphrase-encrypted blob of <b>input_len</b> bytes in * <b>input</b> using the passphrase <b>secret</b> of <b>secret_len</b> bytes. * On success, return 0 and allocate a new chunk of memory to hold the * decrypted data, and store a pointer to that memory in *<b>out</b>, and its * size in <b>outlen_out</b>. On failure, return UNPWBOX_BAD_SECRET if * the passphrase might have been wrong, and UNPWBOX_CORRUPT if the object is * definitely corrupt. */ int crypto_unpwbox(uint8_t **out, size_t *outlen_out, const uint8_t *inp, size_t input_len, const char *secret, size_t secret_len) { uint8_t *result = NULL; const uint8_t *encrypted; uint8_t keys[CIPHER_KEY_LEN + DIGEST256_LEN]; uint8_t hmac[DIGEST256_LEN]; uint32_t result_len; size_t encrypted_len; crypto_cipher_t *cipher = NULL; int rv = UNPWBOX_CORRUPTED; ssize_t got_len; pwbox_encoded_t *enc = NULL; got_len = pwbox_encoded_parse(&enc, inp, input_len); if (got_len < 0 || (size_t)got_len != input_len) goto err; /* Now derive the keys and check the hmac. */ if (secret_to_key_derivekey(keys, sizeof(keys), pwbox_encoded_getarray_skey_header(enc), pwbox_encoded_getlen_skey_header(enc), secret, secret_len) < 0) goto err; crypto_hmac_sha256((char *)hmac, (const char*)keys + CIPHER_KEY_LEN, DIGEST256_LEN, (const char*)inp, input_len - DIGEST256_LEN); if (tor_memneq(hmac, enc->hmac, DIGEST256_LEN)) { rv = UNPWBOX_BAD_SECRET; goto err; } /* How long is the plaintext? */ encrypted = pwbox_encoded_getarray_data(enc); encrypted_len = pwbox_encoded_getlen_data(enc); if (encrypted_len < 4) goto err; cipher = crypto_cipher_new_with_iv((char*)keys, (char*)enc->iv); crypto_cipher_decrypt(cipher, (char*)&result_len, (char*)encrypted, 4); result_len = ntohl(result_len); if (encrypted_len < result_len + 4) goto err; /* Allocate a buffer and decrypt */ result = tor_malloc_zero(result_len); crypto_cipher_decrypt(cipher, (char*)result, (char*)encrypted+4, result_len); *out = result; *outlen_out = result_len; rv = UNPWBOX_OKAY; goto out; err: tor_free(result); out: crypto_cipher_free(cipher); pwbox_encoded_free(enc); memwipe(keys, 0, sizeof(keys)); return rv; }
static void test_ext_or_cookie_auth(void *arg) { char *reply=NULL, *reply2=NULL, *client_hash=NULL, *client_hash2=NULL; size_t reply_len=0; char hmac1[32], hmac2[32]; const char client_nonce[32] = "Who is the third who walks alway"; char server_hash_input[] = "ExtORPort authentication server-to-client hash" "Who is the third who walks alway" "................................"; char client_hash_input[] = "ExtORPort authentication client-to-server hash" "Who is the third who walks alway" "................................"; (void)arg; tt_int_op(strlen(client_hash_input), OP_EQ, 46+32+32); tt_int_op(strlen(server_hash_input), OP_EQ, 46+32+32); ext_or_auth_cookie = tor_malloc_zero(32); memcpy(ext_or_auth_cookie, "s beside you? When I count, ther", 32); ext_or_auth_cookie_is_set = 1; /* For this authentication, the client sends 32 random bytes (ClientNonce) * The server replies with 32 byte ServerHash and 32 byte ServerNonce, * where ServerHash is: * HMAC-SHA256(CookieString, * "ExtORPort authentication server-to-client hash" | ClientNonce | * ServerNonce)" * The client must reply with 32-byte ClientHash, which we compute as: * ClientHash is computed as: * HMAC-SHA256(CookieString, * "ExtORPort authentication client-to-server hash" | ClientNonce | * ServerNonce) */ /* Wrong length */ tt_int_op(-1, OP_EQ, handle_client_auth_nonce(client_nonce, 33, &client_hash, &reply, &reply_len)); tt_int_op(-1, OP_EQ, handle_client_auth_nonce(client_nonce, 31, &client_hash, &reply, &reply_len)); /* Now let's try this for real! */ tt_int_op(0, OP_EQ, handle_client_auth_nonce(client_nonce, 32, &client_hash, &reply, &reply_len)); tt_int_op(reply_len, OP_EQ, 64); tt_ptr_op(reply, OP_NE, NULL); tt_ptr_op(client_hash, OP_NE, NULL); /* Fill in the server nonce into the hash inputs... */ memcpy(server_hash_input+46+32, reply+32, 32); memcpy(client_hash_input+46+32, reply+32, 32); /* Check the HMACs are correct... */ crypto_hmac_sha256(hmac1, (char*)ext_or_auth_cookie, 32, server_hash_input, 46+32+32); crypto_hmac_sha256(hmac2, (char*)ext_or_auth_cookie, 32, client_hash_input, 46+32+32); tt_mem_op(hmac1,OP_EQ, reply, 32); tt_mem_op(hmac2,OP_EQ, client_hash, 32); /* Now do it again and make sure that the results are *different* */ tt_int_op(0, OP_EQ, handle_client_auth_nonce(client_nonce, 32, &client_hash2, &reply2, &reply_len)); tt_mem_op(reply2,OP_NE, reply, reply_len); tt_mem_op(client_hash2,OP_NE, client_hash, 32); /* But that this one checks out too. */ memcpy(server_hash_input+46+32, reply2+32, 32); memcpy(client_hash_input+46+32, reply2+32, 32); /* Check the HMACs are correct... */ crypto_hmac_sha256(hmac1, (char*)ext_or_auth_cookie, 32, server_hash_input, 46+32+32); crypto_hmac_sha256(hmac2, (char*)ext_or_auth_cookie, 32, client_hash_input, 46+32+32); tt_mem_op(hmac1,OP_EQ, reply2, 32); tt_mem_op(hmac2,OP_EQ, client_hash2, 32); done: tor_free(reply); tor_free(client_hash); tor_free(reply2); tor_free(client_hash2); }