From 8e5df40018acd0bd80626073b16b6cc070129109 Mon Sep 17 00:00:00 2001 From: teor Date: Mon, 18 Feb 2019 17:37:47 +1000 Subject: [PATCH] test_dir: Test rsa + ed25519 extrainfo creation and parsing Also fix a missing mock in rsa-only parsing. --- src/feature/relay/router.c | 5 +- src/feature/relay/router.h | 2 + src/feature/relay/routerkeys.c | 8 +- src/feature/relay/routerkeys.h | 4 +- src/test/test_dir.c | 196 ++++++++++++++++++++++++++++++++- 5 files changed, 206 insertions(+), 9 deletions(-) diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c index 18c8375299..cbd5f2f91e 100644 --- a/src/feature/relay/router.c +++ b/src/feature/relay/router.c @@ -271,11 +271,12 @@ expire_old_onion_keys(void) /** Return the current secret onion key for the ntor handshake. Must only * be called from the main thread. */ -static const curve25519_keypair_t * -get_current_curve25519_keypair(void) +MOCK_IMPL(STATIC const struct curve25519_keypair_t *, +get_current_curve25519_keypair,(void)) { return &curve25519_onion_key; } + /** Return a map from KEYID (the key itself) to keypairs for use in the ntor * handshake. Must only be called from the main thread. */ di_digest256_map_t * diff --git a/src/feature/relay/router.h b/src/feature/relay/router.h index d4ad52c9d9..f17e6a9438 100644 --- a/src/feature/relay/router.h +++ b/src/feature/relay/router.h @@ -124,6 +124,8 @@ STATIC smartlist_t *get_my_declared_family(const or_options_t *options); extern time_t desc_clean_since; extern const char *desc_dirty_reason; void set_server_identity_key_digest_testing(const uint8_t *digest); +MOCK_DECL(STATIC const struct curve25519_keypair_t *, + get_current_curve25519_keypair,(void)); MOCK_DECL(STATIC int, router_build_fresh_unsigned_routerinfo,(routerinfo_t **ri_out)); diff --git a/src/feature/relay/routerkeys.c b/src/feature/relay/routerkeys.c index 876f908d41..bdd7a82b58 100644 --- a/src/feature/relay/routerkeys.c +++ b/src/feature/relay/routerkeys.c @@ -631,14 +631,14 @@ get_master_identity_keypair(void) } #endif /* defined(TOR_UNIT_TESTS) */ -const ed25519_keypair_t * -get_master_signing_keypair(void) +MOCK_IMPL(const ed25519_keypair_t *, +get_master_signing_keypair,(void)) { return master_signing_key; } -const struct tor_cert_st * -get_master_signing_key_cert(void) +MOCK_IMPL(const struct tor_cert_st *, +get_master_signing_key_cert,(void)) { return signing_key_cert; } diff --git a/src/feature/relay/routerkeys.h b/src/feature/relay/routerkeys.h index 0badd34191..cde07b52c3 100644 --- a/src/feature/relay/routerkeys.h +++ b/src/feature/relay/routerkeys.h @@ -7,8 +7,8 @@ #include "lib/crypt_ops/crypto_ed25519.h" const ed25519_public_key_t *get_master_identity_key(void); -const ed25519_keypair_t *get_master_signing_keypair(void); -const struct tor_cert_st *get_master_signing_key_cert(void); +MOCK_DECL(const ed25519_keypair_t *, get_master_signing_keypair,(void)); +MOCK_DECL(const struct tor_cert_st *, get_master_signing_key_cert,(void)); const ed25519_keypair_t *get_current_auth_keypair(void); const struct tor_cert_st *get_current_link_cert_cert(void); diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 785d114f77..ad8ab87e9f 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -196,6 +196,45 @@ mock_get_onion_key(void) return mocked_onionkey; } +static routerinfo_t *mocked_routerinfo = NULL; + +/* Returns 0 and sets ri_out to mocked_routerinfo. + * ri_out must not be NULL. There are no other checks. */ +static int +mock_router_build_fresh_unsigned_routerinfo(routerinfo_t **ri_out) +{ + tor_assert(ri_out); + *ri_out = mocked_routerinfo; + return 0; +} + +static ed25519_keypair_t *mocked_master_signing_key = NULL; + +/* Returns mocked_master_signing_key with no checks. */ +static const ed25519_keypair_t * +mock_get_master_signing_keypair(void) +{ + return mocked_master_signing_key; +} + +static struct tor_cert_st *mocked_signing_key_cert = NULL; + +/* Returns mocked_signing_key_cert with no checks. */ +static const struct tor_cert_st * +mock_get_master_signing_key_cert(void) +{ + return mocked_signing_key_cert; +} + +static curve25519_keypair_t *mocked_curve25519_onion_key = NULL; + +/* Returns mocked_curve25519_onion_key with no checks. */ +static const curve25519_keypair_t * +mock_get_current_curve25519_keypair(void) +{ + return mocked_curve25519_onion_key; +} + /** Run unit tests for router descriptor generation logic for a RSA-only * router. Tor versions without ed25519 (0.2.6 and earlier) are no longer * officially supported, but the authorities still accept their descriptors. @@ -372,6 +411,22 @@ test_dir_formats_rsa(void *arg) mocked_onionkey = pk1; MOCK(get_onion_key, mock_get_onion_key); + /* Fake just enough of an ORPort and DirPort to get by */ + MOCK(get_configured_ports, mock_get_configured_ports); + mocked_configured_ports = smartlist_new(); + + memset(&orport, 0, sizeof(orport)); + orport.type = CONN_TYPE_OR_LISTENER; + orport.addr.family = AF_INET; + orport.port = 9000; + smartlist_add(mocked_configured_ports, &orport); + + memset(&dirport, 0, sizeof(dirport)); + dirport.type = CONN_TYPE_DIR_LISTENER; + dirport.addr.family = AF_INET; + dirport.port = 9003; + smartlist_add(mocked_configured_ports, &dirport); + /* Test some of the low-level static functions. */ e1 = router_build_fresh_signed_extrainfo(r1); tt_assert(e1); @@ -392,6 +447,10 @@ test_dir_formats_rsa(void *arg) mocked_onionkey = NULL; UNMOCK(get_onion_key); + UNMOCK(get_configured_ports); + smartlist_free(mocked_configured_ports); + mocked_configured_ports = NULL; + /* Test that the signed ri is parseable */ tt_assert(r1->cache_info.signed_descriptor_body); cp = r1->cache_info.signed_descriptor_body; @@ -468,6 +527,7 @@ test_dir_formats_rsa_ed25519(void *arg) time_t now = time(NULL); port_cfg_t orport; char cert_buf[256]; + int rv = -1; (void)arg; pk1 = pk_generate(0); @@ -478,7 +538,10 @@ test_dir_formats_rsa_ed25519(void *arg) hibernate_set_state_for_testing_(HIBERNATE_STATE_LIVE); get_platform_str(platform, sizeof(platform)); - /* r2 is a RSA + ed25519 descriptor, with an exit policy */ + + /* We can't use init_mock_ed_keys() here, because the keys are seeded */ + + /* r2 is a RSA + ed25519 descriptor, with an exit policy */ ex1 = tor_malloc_zero(sizeof(addr_policy_t)); ex2 = tor_malloc_zero(sizeof(addr_policy_t)); ex1->policy_type = ADDR_POLICY_ACCEPT; @@ -662,6 +725,137 @@ test_dir_formats_rsa_ed25519(void *arg) tt_int_op(p->prt_min,OP_EQ, 24); tt_int_op(p->prt_max,OP_EQ, 24); + routerinfo_free(rp2); + + /* Test extrainfo creation. */ + + /* router_build_fresh_descriptor() requires + * router_build_fresh_unsigned_routerinfo(), but the implementation is + * too complex. Instead, we re-use r2. + */ + mocked_routerinfo = r2; + MOCK(router_build_fresh_unsigned_routerinfo, + mock_router_build_fresh_unsigned_routerinfo); + + /* router_build_fresh_signed_extrainfo() requires options->Nickname */ + tor_free(options->Nickname); + options->Nickname = tor_strdup(r2->nickname); + /* router_build_fresh_signed_extrainfo() requires get_server_identity_key(). + * Use the same one as the call to router_dump_router_to_string() above. + * For the second router, the keys are swapped. + */ + mocked_server_identitykey = pk1; + MOCK(get_server_identity_key, mock_get_server_identity_key); + /* router_dump_and_sign_routerinfo_descriptor_body() requires + * get_onion_key(). Use the same one as r1. + */ + mocked_onionkey = pk2; + MOCK(get_onion_key, mock_get_onion_key); + + /* r2 uses ed25519, so we need to mock the ed key functions */ + mocked_master_signing_key = &kp2; + MOCK(get_master_signing_keypair, mock_get_master_signing_keypair); + + mocked_signing_key_cert = r2->cache_info.signing_key_cert; + MOCK(get_master_signing_key_cert, mock_get_master_signing_key_cert); + + mocked_curve25519_onion_key = &r2_onion_keypair; + MOCK(get_current_curve25519_keypair, mock_get_current_curve25519_keypair); + + /* Fake just enough of an ORPort to get by */ + MOCK(get_configured_ports, mock_get_configured_ports); + mocked_configured_ports = smartlist_new(); + + memset(&orport, 0, sizeof(orport)); + orport.type = CONN_TYPE_OR_LISTENER; + orport.addr.family = AF_INET; + orport.port = 9005; + smartlist_add(mocked_configured_ports, &orport); + + /* Test the high-level interface. */ + rv = router_build_fresh_descriptor(&r2_out, &e2); + if (rv < 0) { + /* router_build_fresh_descriptor() frees r2 on failure. */ + r2 = NULL; + /* Get rid of an alias to rp2 */ + r2_out = NULL; + } + tt_assert(rv == 0); + tt_assert(r2_out); + tt_assert(e2); + /* Guaranteed by mock_router_build_fresh_unsigned_routerinfo() */ + tt_ptr_op(r2_out, OP_EQ, r2); + /* Get rid of an alias to r2 */ + r2_out = NULL; + + /* Now cleanup */ + mocked_routerinfo = NULL; + UNMOCK(router_build_fresh_unsigned_routerinfo); + tor_free(options->Nickname); + mocked_server_identitykey = NULL; + UNMOCK(get_server_identity_key); + mocked_onionkey = NULL; + UNMOCK(get_onion_key); + mocked_master_signing_key = NULL; + UNMOCK(get_master_signing_keypair); + mocked_signing_key_cert = NULL; + UNMOCK(get_master_signing_key_cert); + mocked_curve25519_onion_key = NULL; + UNMOCK(get_current_curve25519_keypair); + + UNMOCK(get_configured_ports); + smartlist_free(mocked_configured_ports); + mocked_configured_ports = NULL; + + /* Test that the signed ri is parseable */ + tt_assert(r2->cache_info.signed_descriptor_body); + cp = r2->cache_info.signed_descriptor_body; + rp2 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL,NULL); + tt_assert(rp2); + tt_int_op(rp2->addr,OP_EQ, r2->addr); + tt_int_op(rp2->or_port,OP_EQ, r2->or_port); + tt_int_op(rp2->dir_port,OP_EQ, r2->dir_port); + tt_int_op(rp2->bandwidthrate,OP_EQ, r2->bandwidthrate); + tt_int_op(rp2->bandwidthburst,OP_EQ, r2->bandwidthburst); + tt_int_op(rp2->bandwidthcapacity,OP_EQ, r2->bandwidthcapacity); + tt_mem_op(rp2->onion_curve25519_pkey->public_key,OP_EQ, + r2->onion_curve25519_pkey->public_key, + CURVE25519_PUBKEY_LEN); + onion_pkey = router_get_rsa_onion_pkey(rp2->onion_pkey, + rp2->onion_pkey_len); + tt_int_op(crypto_pk_cmp_keys(onion_pkey, pk2), OP_EQ, 0); + crypto_pk_free(onion_pkey); + tt_int_op(crypto_pk_cmp_keys(rp2->identity_pkey, pk1), OP_EQ, 0); + tt_assert(rp2->supports_tunnelled_dir_requests); + + tt_int_op(smartlist_len(rp2->exit_policy),OP_EQ, 2); + + p = smartlist_get(rp2->exit_policy, 0); + tt_int_op(p->policy_type,OP_EQ, ADDR_POLICY_ACCEPT); + tt_assert(tor_addr_is_null(&p->addr)); + tt_int_op(p->maskbits,OP_EQ, 0); + tt_int_op(p->prt_min,OP_EQ, 80); + tt_int_op(p->prt_max,OP_EQ, 80); + + p = smartlist_get(rp2->exit_policy, 1); + tt_int_op(p->policy_type,OP_EQ, ADDR_POLICY_REJECT); + tt_assert(tor_addr_eq(&p->addr, &ex2->addr)); + tt_int_op(p->maskbits,OP_EQ, 8); + tt_int_op(p->prt_min,OP_EQ, 24); + tt_int_op(p->prt_max,OP_EQ, 24); + + routerinfo_free(rp2); + + /* Test that the signed ei is parseable */ + tt_assert(e2->cache_info.signed_descriptor_body); + cp = e2->cache_info.signed_descriptor_body; + ep2 = extrainfo_parse_entry_from_string((const char*)cp,NULL,1,NULL,NULL); + tt_assert(ep2); + tt_str_op(ep2->nickname, OP_EQ, r2->nickname); + /* In future tests, we could check the actual extrainfo statistics. */ + + extrainfo_free(ep2); + #if 0 /* Okay, now for the directories. */ {