/** Adjust the map at *<b>map</b>, adding an entry for <b>key</b> -> * <b>val</b>, where <b>key</b> is a DIGEST256_LEN-byte key. * * The caller MUST NOT add a key that already appears in the map. */ void dimap_add_entry(di_digest256_map_t **map, const uint8_t *key, void *val) { di_digest256_map_t *new_ent; { void *old_val = dimap_search(*map, key, NULL); tor_assert(! old_val); tor_assert(val); } new_ent = tor_malloc_zero(sizeof(di_digest256_map_t)); new_ent->next = *map; memcpy(new_ent->key, key, 32); new_ent->val = val; *map = new_ent; }
/** * 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; }
static void test_container_di_map(void *arg) { di_digest256_map_t *map = NULL; const uint8_t key1[] = "In view of the fact that it was "; const uint8_t key2[] = "superficially convincing, being "; const uint8_t key3[] = "properly enciphered in a one-tim"; const uint8_t key4[] = "e cipher scheduled for use today"; char *v1 = tor_strdup(", it came close to causing a disaster..."); char *v2 = tor_strdup("I regret to have to advise you that the mission"); char *v3 = tor_strdup("was actually initiated..."); /* -- John Brunner, _The Shockwave Rider_ */ (void)arg; /* Try searching on an empty map. */ tt_ptr_op(NULL, OP_EQ, dimap_search(map, key1, NULL)); tt_ptr_op(NULL, OP_EQ, dimap_search(map, key2, NULL)); tt_ptr_op(v3, OP_EQ, dimap_search(map, key2, v3)); dimap_free(map, NULL); map = NULL; /* Add a single entry. */ dimap_add_entry(&map, key1, v1); tt_ptr_op(NULL, OP_EQ, dimap_search(map, key2, NULL)); tt_ptr_op(v3, OP_EQ, dimap_search(map, key2, v3)); tt_ptr_op(v1, OP_EQ, dimap_search(map, key1, NULL)); /* Now try it with three entries in the map. */ dimap_add_entry(&map, key2, v2); dimap_add_entry(&map, key3, v3); tt_ptr_op(v1, OP_EQ, dimap_search(map, key1, NULL)); tt_ptr_op(v2, OP_EQ, dimap_search(map, key2, NULL)); tt_ptr_op(v3, OP_EQ, dimap_search(map, key3, NULL)); tt_ptr_op(NULL, OP_EQ, dimap_search(map, key4, NULL)); tt_ptr_op(v1, OP_EQ, dimap_search(map, key4, v1)); done: tor_free(v1); tor_free(v2); tor_free(v3); dimap_free(map, NULL); }