static void bench_onion_ntor_impl(void) { const int iters = 1<<10; int i; curve25519_keypair_t keypair1, keypair2; uint64_t start, end; uint8_t os[NTOR_ONIONSKIN_LEN]; uint8_t or[NTOR_REPLY_LEN]; ntor_handshake_state_t *state = NULL; uint8_t nodeid[DIGEST_LEN]; di_digest256_map_t *keymap = NULL; curve25519_secret_key_generate(&keypair1.seckey, 0); curve25519_public_key_generate(&keypair1.pubkey, &keypair1.seckey); curve25519_secret_key_generate(&keypair2.seckey, 0); curve25519_public_key_generate(&keypair2.pubkey, &keypair2.seckey); dimap_add_entry(&keymap, keypair1.pubkey.public_key, &keypair1); dimap_add_entry(&keymap, keypair2.pubkey.public_key, &keypair2); reset_perftime(); start = perftime(); for (i = 0; i < iters; ++i) { onion_skin_ntor_create(nodeid, &keypair1.pubkey, &state, os); ntor_handshake_state_free(state); state = NULL; } end = perftime(); printf("Client-side, part 1: %f usec.\n", NANOCOUNT(start, end, iters)/1e3); state = NULL; onion_skin_ntor_create(nodeid, &keypair1.pubkey, &state, os); start = perftime(); for (i = 0; i < iters; ++i) { uint8_t key_out[CPATH_KEY_MATERIAL_LEN]; onion_skin_ntor_server_handshake(os, keymap, NULL, nodeid, or, key_out, sizeof(key_out)); } end = perftime(); printf("Server-side: %f usec\n", NANOCOUNT(start, end, iters)/1e3); start = perftime(); for (i = 0; i < iters; ++i) { uint8_t key_out[CPATH_KEY_MATERIAL_LEN]; int s; s = onion_skin_ntor_client_handshake(state, or, key_out, sizeof(key_out), NULL); tor_assert(s == 0); } end = perftime(); printf("Client-side, part 2: %f usec.\n", NANOCOUNT(start, end, iters)/1e3); ntor_handshake_state_free(state); dimap_free(keymap, NULL); }
static void test_crypto_curve25519_encode(void *arg) { curve25519_secret_key_t seckey; curve25519_public_key_t key1, key2, key3; char buf[64]; (void)arg; curve25519_secret_key_generate(&seckey, 0); curve25519_public_key_generate(&key1, &seckey); tt_int_op(0, ==, curve25519_public_to_base64(buf, &key1)); tt_int_op(CURVE25519_BASE64_PADDED_LEN, ==, strlen(buf)); tt_int_op(0, ==, curve25519_public_from_base64(&key2, buf)); test_memeq(key1.public_key, key2.public_key, CURVE25519_PUBKEY_LEN); buf[CURVE25519_BASE64_PADDED_LEN - 1] = '\0'; tt_int_op(CURVE25519_BASE64_PADDED_LEN-1, ==, strlen(buf)); tt_int_op(0, ==, curve25519_public_from_base64(&key3, buf)); test_memeq(key1.public_key, key3.public_key, CURVE25519_PUBKEY_LEN); /* Now try bogus parses. */ strlcpy(buf, "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$=", sizeof(buf)); tt_int_op(-1, ==, curve25519_public_from_base64(&key3, buf)); strlcpy(buf, "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$", sizeof(buf)); tt_int_op(-1, ==, curve25519_public_from_base64(&key3, buf)); strlcpy(buf, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", sizeof(buf)); tt_int_op(-1, ==, curve25519_public_from_base64(&key3, buf)); done: ; }
/** * Compute the first client-side step of the ntor handshake for communicating * with a server whose DIGEST_LEN-byte server identity is <b>router_id</b>, * and whose onion key is <b>router_key</b>. Store the NTOR_ONIONSKIN_LEN-byte * message in <b>onion_skin_out</b>, and store the handshake state in * *<b>handshake_state_out</b>. Return 0 on success, -1 on failure. */ int onion_skin_ntor_create(const uint8_t *router_id, const curve25519_public_key_t *router_key, ntor_handshake_state_t **handshake_state_out, uint8_t *onion_skin_out) { ntor_handshake_state_t *state; uint8_t *op; state = tor_malloc_zero(sizeof(ntor_handshake_state_t)); memcpy(state->router_id, router_id, DIGEST_LEN); memcpy(&state->pubkey_B, router_key, sizeof(curve25519_public_key_t)); if (curve25519_secret_key_generate(&state->seckey_x, 0) < 0) { tor_free(state); return -1; } curve25519_public_key_generate(&state->pubkey_X, &state->seckey_x); op = onion_skin_out; APPEND(op, router_id, DIGEST_LEN); APPEND(op, router_key->public_key, CURVE25519_PUBKEY_LEN); APPEND(op, state->pubkey_X.public_key, CURVE25519_PUBKEY_LEN); tor_assert(op == onion_skin_out + NTOR_ONIONSKIN_LEN); *handshake_state_out = state; return 0; }
/* DOCDOC */ int curve25519_keypair_read_from_file(curve25519_keypair_t *keypair_out, char **tag_out, const char *fname) { uint8_t content[CURVE25519_SECKEY_LEN + CURVE25519_PUBKEY_LEN]; ssize_t len; int r = -1; len = crypto_read_tagged_contents_from_file(fname, "c25519v1", tag_out, content, sizeof(content)); if (len != sizeof(content)) goto end; memcpy(keypair_out->seckey.secret_key, content, CURVE25519_SECKEY_LEN); curve25519_public_key_generate(&keypair_out->pubkey, &keypair_out->seckey); if (tor_memneq(keypair_out->pubkey.public_key, content + CURVE25519_SECKEY_LEN, CURVE25519_PUBKEY_LEN)) goto end; r = 0; end: memwipe(content, 0, sizeof(content)); if (r != 0) { memset(keypair_out, 0, sizeof(*keypair_out)); tor_free(*tag_out); } return r; }
static void test_ntor_handshake(void *arg) { /* client-side */ ntor_handshake_state_t *c_state = NULL; uint8_t c_buf[NTOR_ONIONSKIN_LEN]; uint8_t c_keys[400]; /* server-side */ di_digest256_map_t *s_keymap=NULL; curve25519_keypair_t s_keypair; uint8_t s_buf[NTOR_REPLY_LEN]; uint8_t s_keys[400]; /* shared */ const curve25519_public_key_t *server_pubkey; uint8_t node_id[20] = "abcdefghijklmnopqrst"; (void) arg; /* Make the server some keys */ curve25519_secret_key_generate(&s_keypair.seckey, 0); curve25519_public_key_generate(&s_keypair.pubkey, &s_keypair.seckey); dimap_add_entry(&s_keymap, s_keypair.pubkey.public_key, &s_keypair); server_pubkey = &s_keypair.pubkey; /* client handshake 1. */ memset(c_buf, 0, NTOR_ONIONSKIN_LEN); tt_int_op(0, OP_EQ, onion_skin_ntor_create(node_id, server_pubkey, &c_state, c_buf)); /* server handshake */ memset(s_buf, 0, NTOR_REPLY_LEN); memset(s_keys, 0, 40); tt_int_op(0, OP_EQ, onion_skin_ntor_server_handshake(c_buf, s_keymap, NULL, node_id, s_buf, s_keys, 400)); /* client handshake 2 */ memset(c_keys, 0, 40); tt_int_op(0, OP_EQ, onion_skin_ntor_client_handshake(c_state, s_buf, c_keys, 400, NULL)); tt_mem_op(c_keys,OP_EQ, s_keys, 400); memset(s_buf, 0, 40); tt_mem_op(c_keys,OP_NE, s_buf, 40); /* Now try with a bogus server response. Zero input should trigger * All The Problems. */ memset(c_keys, 0, 400); memset(s_buf, 0, NTOR_REPLY_LEN); const char *msg = NULL; tt_int_op(-1, OP_EQ, onion_skin_ntor_client_handshake(c_state, s_buf, c_keys, 400, &msg)); tt_str_op(msg, OP_EQ, "Zero output from curve25519 handshake"); done: ntor_handshake_state_free(c_state); dimap_free(s_keymap, NULL); }
/** * Compute the first client-side step of the ntor handshake for communicating * with a server whose DIGEST_LEN-byte server identity is <b>router_id</b>, * and whose onion key is <b>router_key</b>. Store the NTOR_ONIONSKIN_LEN-byte * message in <b>onion_skin_out</b>, and store the handshake state in * *<b>handshake_state_out</b>. Return 0 on success, -1 on failure. */ int onion_skin_ntor_create(const uint8_t *router_id, const curve25519_public_key_t *router_key, ntor_handshake_state_t **handshake_state_out, uint8_t *onion_skin_out) { ntor_handshake_state_t *state; uint8_t *op; state = tor_malloc_zero(sizeof(ntor_handshake_state_t)); memcpy(state->router_id, router_id, DIGEST_LEN); memcpy(&state->pubkey_B, router_key, sizeof(curve25519_public_key_t)); if (curve25519_secret_key_generate(&state->seckey_x, 0) < 0) { /* LCOV_EXCL_START * Secret key generation should be unable to fail when the key isn't * marked as "extra-strong" */ tor_assert_nonfatal_unreached(); tor_free(state); return -1; /* LCOV_EXCL_STOP */ } curve25519_public_key_generate(&state->pubkey_X, &state->seckey_x); op = onion_skin_out; APPEND(op, router_id, DIGEST_LEN); APPEND(op, router_key->public_key, CURVE25519_PUBKEY_LEN); APPEND(op, state->pubkey_X.public_key, CURVE25519_PUBKEY_LEN); tor_assert(op == onion_skin_out + NTOR_ONIONSKIN_LEN); *handshake_state_out = state; return 0; }
int curve25519_keypair_generate(curve25519_keypair_t *keypair_out, int extra_strong) { if (curve25519_secret_key_generate(&keypair_out->seckey, extra_strong) < 0) return -1; curve25519_public_key_generate(&keypair_out->pubkey, &keypair_out->seckey); return 0; }
static void test_crypto_curve25519_wrappers(void *arg) { curve25519_public_key_t pubkey1, pubkey2; curve25519_secret_key_t seckey1, seckey2; uint8_t output1[CURVE25519_OUTPUT_LEN]; uint8_t output2[CURVE25519_OUTPUT_LEN]; (void)arg; /* Test a simple handshake, serializing and deserializing some stuff. */ curve25519_secret_key_generate(&seckey1, 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)); test_assert(curve25519_public_key_is_ok(&pubkey2)); curve25519_handshake(output1, &seckey1, &pubkey2); curve25519_handshake(output2, &seckey2, &pubkey1); test_memeq(output1, output2, sizeof(output1)); done: ; }
static int server1(int argc, char **argv) { uint8_t msg_in[NTOR_ONIONSKIN_LEN]; curve25519_keypair_t kp; di_digest256_map_t *keymap=NULL; uint8_t node_id[DIGEST_LEN]; int keybytes; uint8_t msg_out[NTOR_REPLY_LEN]; uint8_t *keys = NULL; char *hexkeys = NULL; int result = 0; char buf[256]; /* server1: b nodeID msg N -> msg keys */ N_ARGS(6); BASE16(2, kp.seckey.secret_key, CURVE25519_SECKEY_LEN); BASE16(3, node_id, DIGEST_LEN); BASE16(4, msg_in, NTOR_ONIONSKIN_LEN); INT(5, keybytes); curve25519_public_key_generate(&kp.pubkey, &kp.seckey); dimap_add_entry(&keymap, kp.pubkey.public_key, &kp); keys = tor_malloc(keybytes); hexkeys = tor_malloc(keybytes*2+1); if (onion_skin_ntor_server_handshake( msg_in, keymap, NULL, node_id, msg_out, keys, (size_t)keybytes)<0) { fprintf(stderr, "handshake failed"); result = 2; goto done; } base16_encode(buf, sizeof(buf), (const char*)msg_out, sizeof(msg_out)); printf("%s\n", buf); base16_encode(hexkeys, keybytes*2+1, (const char*)keys, keybytes); printf("%s\n", hexkeys); done: tor_free(keys); tor_free(hexkeys); dimap_free(keymap, NULL); return result; }
/** * Perform the server side of an ntor handshake. Given an * NTOR_ONIONSKIN_LEN-byte message in <b>onion_skin</b>, our own identity * fingerprint as <b>my_node_id</b>, and an associative array mapping public * onion keys to curve25519_keypair_t in <b>private_keys</b>, attempt to * perform the handshake. Use <b>junk_keys</b> if present if the handshake * indicates an unrecognized public key. Write an NTOR_REPLY_LEN-byte * message to send back to the client into <b>handshake_reply_out</b>, and * generate <b>key_out_len</b> bytes of key material in <b>key_out</b>. Return * 0 on success, -1 on failure. */ int onion_skin_ntor_server_handshake(const uint8_t *onion_skin, const di_digest256_map_t *private_keys, const curve25519_keypair_t *junk_keys, const uint8_t *my_node_id, uint8_t *handshake_reply_out, uint8_t *key_out, size_t key_out_len) { const tweakset_t *T = &proto1_tweaks; /* Sensitive stack-allocated material. Kept in an anonymous struct to make * it easy to wipe. */ struct { uint8_t secret_input[SECRET_INPUT_LEN]; uint8_t auth_input[AUTH_INPUT_LEN]; curve25519_public_key_t pubkey_X; curve25519_secret_key_t seckey_y; curve25519_public_key_t pubkey_Y; uint8_t verify[DIGEST256_LEN]; } s; uint8_t *si = s.secret_input, *ai = s.auth_input; const curve25519_keypair_t *keypair_bB; int bad; /* Decode the onion skin */ /* XXXX Does this possible early-return business threaten our security? */ if (tor_memneq(onion_skin, my_node_id, DIGEST_LEN)) return -1; /* Note that on key-not-found, we go through with this operation anyway, * using "junk_keys". This will result in failed authentication, but won't * leak whether we recognized the key. */ keypair_bB = dimap_search(private_keys, onion_skin + DIGEST_LEN, (void*)junk_keys); if (!keypair_bB) return -1; memcpy(s.pubkey_X.public_key, onion_skin+DIGEST_LEN+DIGEST256_LEN, CURVE25519_PUBKEY_LEN); /* Make y, Y */ curve25519_secret_key_generate(&s.seckey_y, 0); curve25519_public_key_generate(&s.pubkey_Y, &s.seckey_y); /* NOTE: If we ever use a group other than curve25519, or a different * representation for its points, we may need to perform different or * additional checks on X here and on Y in the client handshake, or lose our * security properties. What checks we need would depend on the properties * of the group and its representation. * * In short: if you use anything other than curve25519, this aspect of the * code will need to be reconsidered carefully. */ /* build secret_input */ curve25519_handshake(si, &s.seckey_y, &s.pubkey_X); bad = safe_mem_is_zero(si, CURVE25519_OUTPUT_LEN); si += CURVE25519_OUTPUT_LEN; curve25519_handshake(si, &keypair_bB->seckey, &s.pubkey_X); bad |= safe_mem_is_zero(si, CURVE25519_OUTPUT_LEN); si += CURVE25519_OUTPUT_LEN; APPEND(si, my_node_id, DIGEST_LEN); APPEND(si, keypair_bB->pubkey.public_key, CURVE25519_PUBKEY_LEN); APPEND(si, s.pubkey_X.public_key, CURVE25519_PUBKEY_LEN); APPEND(si, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN); APPEND(si, PROTOID, PROTOID_LEN); tor_assert(si == s.secret_input + sizeof(s.secret_input)); /* Compute hashes of secret_input */ h_tweak(s.verify, s.secret_input, sizeof(s.secret_input), T->t_verify); /* Compute auth_input */ APPEND(ai, s.verify, DIGEST256_LEN); APPEND(ai, my_node_id, DIGEST_LEN); APPEND(ai, keypair_bB->pubkey.public_key, CURVE25519_PUBKEY_LEN); APPEND(ai, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN); APPEND(ai, s.pubkey_X.public_key, CURVE25519_PUBKEY_LEN); APPEND(ai, PROTOID, PROTOID_LEN); APPEND(ai, SERVER_STR, SERVER_STR_LEN); tor_assert(ai == s.auth_input + sizeof(s.auth_input)); /* Build the reply */ memcpy(handshake_reply_out, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN); h_tweak(handshake_reply_out+CURVE25519_PUBKEY_LEN, s.auth_input, sizeof(s.auth_input), T->t_mac); /* Generate the key material */ crypto_expand_key_material_rfc5869_sha256( s.secret_input, sizeof(s.secret_input), (const uint8_t*)T->t_key, strlen(T->t_key), (const uint8_t*)T->m_expand, strlen(T->m_expand), key_out, key_out_len); /* Wipe all of our local state */ memwipe(&s, 0, sizeof(s)); return bad ? -1 : 0; }