From 0ac2afad0dc99ff6ce15f4cf63dcd2b9b3c6b637 Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Wed, 13 Sep 2017 21:06:25 +0300 Subject: [PATCH] prop224 client-side: Start validating onion address pubkeys. Fix the test_build_address() test and its test vectors python script. They were both using a bogus pubkey for building an HS address which does not validate anymore. Also fix a few more unittests that were using bogus onion addresses and were failing the validation. I replaced the bogus address with the one generated from the test vector script. --- src/or/hs_common.c | 15 ++++++++++++--- src/test/hs_build_address.py | 5 +++-- src/test/test_entryconn.c | 4 ++-- src/test/test_hs_common.c | 28 ++++++++++++++++++---------- 4 files changed, 35 insertions(+), 17 deletions(-) diff --git a/src/or/hs_common.c b/src/or/hs_common.c index 291d8ae8d..c03dac985 100644 --- a/src/or/hs_common.c +++ b/src/or/hs_common.c @@ -914,22 +914,31 @@ hs_address_is_valid(const char *address) uint8_t version; uint8_t checksum[HS_SERVICE_ADDR_CHECKSUM_LEN_USED]; uint8_t target_checksum[DIGEST256_LEN]; - ed25519_public_key_t key; + ed25519_public_key_t service_pubkey; /* Parse the decoded address into the fields we need. */ - if (hs_parse_address(address, &key, checksum, &version) < 0) { + if (hs_parse_address(address, &service_pubkey, checksum, &version) < 0) { goto invalid; } /* Get the checksum it's suppose to be and compare it with what we have * encoded in the address. */ - build_hs_checksum(&key, version, target_checksum); + build_hs_checksum(&service_pubkey, version, target_checksum); if (tor_memcmp(checksum, target_checksum, sizeof(checksum))) { log_warn(LD_REND, "Service address %s invalid checksum.", escaped_safe_str(address)); goto invalid; } + /* Validate that this pubkey does not have a torsion component. We need to do + * this on the prop224 client-side so that attackers can't give equivalent + * forms of an onion address to users. */ + if (ed25519_validate_pubkey(&service_pubkey) < 0) { + log_warn(LD_REND, "Service address %s has bad pubkey .", + escaped_safe_str(address)); + goto invalid; + } + /* Valid address. */ return 1; invalid: diff --git a/src/test/hs_build_address.py b/src/test/hs_build_address.py index 7be9c8b85..7ff22c3a9 100644 --- a/src/test/hs_build_address.py +++ b/src/test/hs_build_address.py @@ -21,8 +21,9 @@ if TEST_VALUE != sha3.sha3_256(b"Hello World").hexdigest(): # Checksum is built like so: # CHECKSUM = SHA3(".onion checksum" || PUBKEY || VERSION) PREFIX = ".onion checksum".encode() -# 32 bytes ed25519 pubkey. -PUBKEY = ("\x42" * 32).encode() +# 32 bytes ed25519 pubkey from first test vector of +# https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-02#section-6 +PUBKEY = "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a".decode('hex') # Version 3 is proposal224 VERSION = 3 diff --git a/src/test/test_entryconn.c b/src/test/test_entryconn.c index 2d28f1d42..18b5fcc35 100644 --- a/src/test/test_entryconn.c +++ b/src/test/test_entryconn.c @@ -801,7 +801,7 @@ test_entryconn_rewrite_onion_v3(void *arg) /* Make a SOCKS request */ conn->socks_request->command = SOCKS_COMMAND_CONNECT; strlcpy(conn->socks_request->address, - "git.p3xnclpu4mu22dwaurjtsybyqk4xfjmcfz6z62yl24uwmhjatiwnlnad.onion", + "git.25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid.onion", sizeof(conn->socks_request->address)); /* Make an onion connection using the SOCKS request */ @@ -818,7 +818,7 @@ test_entryconn_rewrite_onion_v3(void *arg) tt_int_op(ENTRY_TO_CONN(conn)->state, OP_EQ, AP_CONN_STATE_CIRCUIT_WAIT); /* check that the address got rewritten */ tt_str_op(conn->socks_request->address, OP_EQ, - "p3xnclpu4mu22dwaurjtsybyqk4xfjmcfz6z62yl24uwmhjatiwnlnad"); + "25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid"); /* check that HS information got attached to the connection */ tt_assert(ENTRY_TO_EDGE_CONN(conn)->hs_ident); tt_assert(!ENTRY_TO_EDGE_CONN(conn)->rend_data); diff --git a/src/test/test_hs_common.c b/src/test/test_hs_common.c index da592eb08..dc146326b 100644 --- a/src/test/test_hs_common.c +++ b/src/test/test_hs_common.c @@ -77,7 +77,7 @@ test_validate_address(void *arg) /* Valid address. */ ret = hs_address_is_valid( - "p3xnclpu4mu22dwaurjtsybyqk4xfjmcfz6z62yl24uwmhjatiwnlnad"); + "25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid"); tt_int_op(ret, OP_EQ, 1); done: @@ -90,19 +90,23 @@ mock_write_str_to_file(const char *path, const char *str, int bin) (void)bin; tt_str_op(path, OP_EQ, "/double/five"PATH_SEPARATOR"squared"); tt_str_op(str, OP_EQ, - "ijbeeqscijbeeqscijbeeqscijbeeqscijbeeqscijbeeqscijbezhid.onion\n"); + "25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid.onion\n"); done: return 0; } -/** Test building HS v3 onion addresses */ +/** Test building HS v3 onion addresses. Uses test vectors from the + * ./hs_build_address.py script. */ static void test_build_address(void *arg) { int ret; char onion_addr[HS_SERVICE_ADDR_LEN_BASE32 + 1]; ed25519_public_key_t pubkey; + /* hex-encoded ed25519 pubkey used in hs_build_address.py */ + char pubkey_hex[] = + "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a"; hs_service_t *service = NULL; (void) arg; @@ -112,11 +116,11 @@ test_build_address(void *arg) /* The following has been created with hs_build_address.py script that * follows proposal 224 specification to build an onion address. */ static const char *test_addr = - "ijbeeqscijbeeqscijbeeqscijbeeqscijbeeqscijbeeqscijbezhid"; + "25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid"; - /* Let's try to build the same onion address that the script can do. Key is - * a long set of very random \x42 :). */ - memset(&pubkey, '\x42', sizeof(pubkey)); + /* Let's try to build the same onion address as the script */ + base16_decode((char*)pubkey.pubkey, sizeof(pubkey.pubkey), + pubkey_hex, strlen(pubkey_hex)); hs_build_address(&pubkey, HS_VERSION_THREE, onion_addr); tt_str_op(test_addr, OP_EQ, onion_addr); /* Validate that address. */ @@ -474,9 +478,13 @@ test_desc_reupload_logic(void *arg) /* Let's start by building our descriptor and service */ hs_service_descriptor_t *desc = service_descriptor_new(); hs_service_t *service = NULL; + /* hex-encoded ed25519 pubkey used in hs_build_address.py */ + char pubkey_hex[] = + "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a"; char onion_addr[HS_SERVICE_ADDR_LEN_BASE32 + 1]; ed25519_public_key_t pubkey; - memset(&pubkey, '\x42', sizeof(pubkey)); + base16_decode((char*)pubkey.pubkey, sizeof(pubkey.pubkey), + pubkey_hex, strlen(pubkey_hex)); hs_build_address(&pubkey, HS_VERSION_THREE, onion_addr); service = tor_malloc_zero(sizeof(hs_service_t)); memcpy(service->onion_address, onion_addr, sizeof(service->onion_address)); @@ -758,7 +766,7 @@ test_parse_extended_hostname(void *arg) char address6[] = "foo.bar.abcdefghijklmnop.onion"; char address7[] = ".abcdefghijklmnop.onion"; char address8[] = - "www.p3xnclpu4mu22dwaurjtsybyqk4xfjmcfz6z62yl24uwmhjatiwnlnad.onion"; + "www.25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid.onion"; tt_assert(BAD_HOSTNAME == parse_extended_hostname(address1)); tt_assert(ONION_V2_HOSTNAME == parse_extended_hostname(address2)); @@ -772,7 +780,7 @@ test_parse_extended_hostname(void *arg) tt_assert(BAD_HOSTNAME == parse_extended_hostname(address7)); tt_assert(ONION_V3_HOSTNAME == parse_extended_hostname(address8)); tt_str_op(address8, OP_EQ, - "p3xnclpu4mu22dwaurjtsybyqk4xfjmcfz6z62yl24uwmhjatiwnlnad"); + "25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid"); done: ; }