/* Copyright (c) 2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "or.h" #include "crypto_ed25519.h" #include "test.h" #include "torcert.h" #include "hs_common.h" #include "hs_test_helpers.h" hs_desc_intro_point_t * hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now, const char *addr, int legacy) { int ret; ed25519_keypair_t auth_kp; hs_desc_intro_point_t *intro_point = NULL; hs_desc_intro_point_t *ip = hs_desc_intro_point_new(); { hs_desc_link_specifier_t *ls = tor_malloc_zero(sizeof(*ls)); if (legacy) { ls->type = LS_LEGACY_ID; memcpy(ls->u.legacy_id, "0299F268FCA9D55CD157976D39AE92B4B455B3A8", DIGEST_LEN); } else { ls->u.ap.port = 9001; int family = tor_addr_parse(&ls->u.ap.addr, addr); switch (family) { case AF_INET: ls->type = LS_IPV4; break; case AF_INET6: ls->type = LS_IPV6; break; default: /* Stop the test, not suppose to have an error. */ tt_int_op(family, OP_EQ, AF_INET); } } smartlist_add(ip->link_specifiers, ls); } ret = ed25519_keypair_generate(&auth_kp, 0); tt_int_op(ret, ==, 0); ip->auth_key_cert = tor_cert_create(signing_kp, CERT_TYPE_AUTH_HS_IP_KEY, &auth_kp.pubkey, now, HS_DESC_CERT_LIFETIME, CERT_FLAG_INCLUDE_SIGNING_KEY); tt_assert(ip->auth_key_cert); if (legacy) { ip->legacy.key = crypto_pk_new(); tt_assert(ip->legacy.key); ret = crypto_pk_generate_key(ip->legacy.key); tt_int_op(ret, ==, 0); ssize_t cert_len = tor_make_rsa_ed25519_crosscert( &signing_kp->pubkey, ip->legacy.key, now + HS_DESC_CERT_LIFETIME, &ip->legacy.cert.encoded); tt_assert(ip->legacy.cert.encoded); tt_u64_op(cert_len, OP_GT, 0); ip->legacy.cert.len = cert_len; } /* Encryption key. */ { int signbit; curve25519_keypair_t curve25519_kp; ed25519_keypair_t ed25519_kp; tor_cert_t *cross_cert; ret = curve25519_keypair_generate(&curve25519_kp, 0); tt_int_op(ret, ==, 0); ed25519_keypair_from_curve25519_keypair(&ed25519_kp, &signbit, &curve25519_kp); cross_cert = tor_cert_create(signing_kp, CERT_TYPE_CROSS_HS_IP_KEYS, &ed25519_kp.pubkey, time(NULL), HS_DESC_CERT_LIFETIME, CERT_FLAG_INCLUDE_SIGNING_KEY); tt_assert(cross_cert); ip->enc_key_cert = cross_cert; } intro_point = ip; done: return intro_point; } /* Return a valid hs_descriptor_t object. If no_ip is set, no introduction * points are added. */ static hs_descriptor_t * hs_helper_build_hs_desc_impl(unsigned int no_ip, const ed25519_keypair_t *signing_kp) { time_t now = approx_time(); ed25519_keypair_t blinded_kp; hs_descriptor_t *descp = NULL, *desc = tor_malloc_zero(sizeof(*desc)); desc->plaintext_data.version = HS_DESC_SUPPORTED_FORMAT_VERSION_MAX; /* Copy only the public key into the descriptor. */ memcpy(&desc->plaintext_data.signing_pubkey, &signing_kp->pubkey, sizeof(ed25519_public_key_t)); uint64_t current_time_period = hs_get_time_period_num(approx_time()); hs_build_blinded_keypair(signing_kp, NULL, 0, current_time_period, &blinded_kp); /* Copy only the public key into the descriptor. */ memcpy(&desc->plaintext_data.blinded_pubkey, &blinded_kp.pubkey, sizeof(ed25519_public_key_t)); desc->plaintext_data.signing_key_cert = tor_cert_create(&blinded_kp, CERT_TYPE_SIGNING_HS_DESC, &signing_kp->pubkey, now, 3600, CERT_FLAG_INCLUDE_SIGNING_KEY); tt_assert(desc->plaintext_data.signing_key_cert); desc->plaintext_data.revision_counter = 42; desc->plaintext_data.lifetime_sec = 3 * 60 * 60; hs_get_subcredential(&signing_kp->pubkey, &blinded_kp.pubkey, desc->subcredential); /* Setup encrypted data section. */ desc->encrypted_data.create2_ntor = 1; desc->encrypted_data.intro_auth_types = smartlist_new(); desc->encrypted_data.single_onion_service = 1; smartlist_add(desc->encrypted_data.intro_auth_types, tor_strdup("ed25519")); desc->encrypted_data.intro_points = smartlist_new(); if (!no_ip) { /* Add four intro points. */ smartlist_add(desc->encrypted_data.intro_points, hs_helper_build_intro_point(signing_kp, now, "1.2.3.4", 0)); smartlist_add(desc->encrypted_data.intro_points, hs_helper_build_intro_point(signing_kp, now, "[2600::1]", 0)); smartlist_add(desc->encrypted_data.intro_points, hs_helper_build_intro_point(signing_kp, now, "3.2.1.4", 1)); smartlist_add(desc->encrypted_data.intro_points, hs_helper_build_intro_point(signing_kp, now, "", 1)); } descp = desc; done: return descp; } /** Helper function to get the HS subcredential using the identity keypair of * an HS. Used to decrypt descriptors in unittests. */ void hs_helper_get_subcred_from_identity_keypair(ed25519_keypair_t *signing_kp, uint8_t *subcred_out) { ed25519_keypair_t blinded_kp; uint64_t current_time_period = hs_get_time_period_num(approx_time()); hs_build_blinded_keypair(signing_kp, NULL, 0, current_time_period, &blinded_kp); hs_get_subcredential(&signing_kp->pubkey, &blinded_kp.pubkey, subcred_out); } /* Build a descriptor with introduction points. */ hs_descriptor_t * hs_helper_build_hs_desc_with_ip(const ed25519_keypair_t *signing_kp) { return hs_helper_build_hs_desc_impl(0, signing_kp); } /* Build a descriptor without any introduction points. */ hs_descriptor_t * hs_helper_build_hs_desc_no_ip(const ed25519_keypair_t *signing_kp) { return hs_helper_build_hs_desc_impl(1, signing_kp); } void hs_helper_desc_equal(const hs_descriptor_t *desc1, const hs_descriptor_t *desc2) { char *addr1 = NULL, *addr2 = NULL; /* Plaintext data section. */ tt_int_op(desc1->plaintext_data.version, OP_EQ, desc2->plaintext_data.version); tt_uint_op(desc1->plaintext_data.lifetime_sec, OP_EQ, desc2->plaintext_data.lifetime_sec); tt_assert(tor_cert_eq(desc1->plaintext_data.signing_key_cert, desc2->plaintext_data.signing_key_cert)); tt_mem_op(desc1->plaintext_data.signing_pubkey.pubkey, OP_EQ, desc2->plaintext_data.signing_pubkey.pubkey, ED25519_PUBKEY_LEN); tt_mem_op(desc1->plaintext_data.blinded_pubkey.pubkey, OP_EQ, desc2->plaintext_data.blinded_pubkey.pubkey, ED25519_PUBKEY_LEN); tt_u64_op(desc1->plaintext_data.revision_counter, ==, desc2->plaintext_data.revision_counter); /* NOTE: We can't compare the encrypted blob because when encoding the * descriptor, the object is immutable thus we don't update it with the * encrypted blob. As contrast to the decoding process where we populate a * descriptor object. */ /* Encrypted data section. */ tt_uint_op(desc1->encrypted_data.create2_ntor, ==, desc2->encrypted_data.create2_ntor); /* Authentication type. */ tt_int_op(!!desc1->encrypted_data.intro_auth_types, ==, !!desc2->encrypted_data.intro_auth_types); if (desc1->encrypted_data.intro_auth_types && desc2->encrypted_data.intro_auth_types) { tt_int_op(smartlist_len(desc1->encrypted_data.intro_auth_types), ==, smartlist_len(desc2->encrypted_data.intro_auth_types)); for (int i = 0; i < smartlist_len(desc1->encrypted_data.intro_auth_types); i++) { tt_str_op(smartlist_get(desc1->encrypted_data.intro_auth_types, i),OP_EQ, smartlist_get(desc2->encrypted_data.intro_auth_types, i)); } } /* Introduction points. */ { tt_assert(desc1->encrypted_data.intro_points); tt_assert(desc2->encrypted_data.intro_points); tt_int_op(smartlist_len(desc1->encrypted_data.intro_points), ==, smartlist_len(desc2->encrypted_data.intro_points)); for (int i=0; i < smartlist_len(desc1->encrypted_data.intro_points); i++) { hs_desc_intro_point_t *ip1 = smartlist_get(desc1->encrypted_data .intro_points, i), *ip2 = smartlist_get(desc2->encrypted_data .intro_points, i); tt_assert(tor_cert_eq(ip1->auth_key_cert, ip2->auth_key_cert)); if (ip1->legacy.key) { tt_int_op(crypto_pk_cmp_keys(ip1->legacy.key, ip2->legacy.key), OP_EQ, 0); } else { tt_mem_op(&ip1->enc_key, OP_EQ, &ip2->enc_key, CURVE25519_PUBKEY_LEN); } tt_int_op(smartlist_len(ip1->link_specifiers), ==, smartlist_len(ip2->link_specifiers)); for (int j = 0; j < smartlist_len(ip1->link_specifiers); j++) { hs_desc_link_specifier_t *ls1 = smartlist_get(ip1->link_specifiers, j), *ls2 = smartlist_get(ip2->link_specifiers, j); tt_int_op(ls1->type, ==, ls2->type); switch (ls1->type) { case LS_IPV4: case LS_IPV6: { addr1 = tor_addr_to_str_dup(&ls1->u.ap.addr); addr2 = tor_addr_to_str_dup(&ls2->u.ap.addr); tt_str_op(addr1, OP_EQ, addr2); tor_free(addr1); tor_free(addr2); tt_int_op(ls1->u.ap.port, ==, ls2->u.ap.port); } break; case LS_LEGACY_ID: tt_mem_op(ls1->u.legacy_id, OP_EQ, ls2->u.legacy_id, sizeof(ls1->u.legacy_id)); break; default: /* Unknown type, caught it and print its value. */ tt_int_op(ls1->type, OP_EQ, -1); } } } } done: tor_free(addr1); tor_free(addr2); }