diff --git a/src/common/crypto.c b/src/common/crypto.c index 06631e1604..05dc43213c 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -830,7 +830,7 @@ crypto_pk_public_exponent_ok(crypto_pk_t *env) * Note that this may leak information about the keys through timing. */ int -crypto_pk_cmp_keys(crypto_pk_t *a, crypto_pk_t *b) +crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b) { int result; char a_is_non_null = (a != NULL) && (a->key != NULL); @@ -856,19 +856,19 @@ crypto_pk_cmp_keys(crypto_pk_t *a, crypto_pk_t *b) * Note that this may leak information about the keys through timing. */ int -crypto_pk_eq_keys(crypto_pk_t *a, crypto_pk_t *b) +crypto_pk_eq_keys(const crypto_pk_t *a, const crypto_pk_t *b) { return (crypto_pk_cmp_keys(a, b) == 0); } /** Return the size of the public key modulus in env, in bytes. */ size_t -crypto_pk_keysize(crypto_pk_t *env) +crypto_pk_keysize(const crypto_pk_t *env) { tor_assert(env); tor_assert(env->key); - return (size_t) RSA_size(env->key); + return (size_t) RSA_size((RSA*)env->key); } /** Return the size of the public key modulus of env, in bits. */ @@ -997,7 +997,7 @@ crypto_pk_private_decrypt(crypto_pk_t *env, char *to, * at least the length of the modulus of env. */ int -crypto_pk_public_checksig(crypto_pk_t *env, char *to, +crypto_pk_public_checksig(const crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen) { @@ -1069,7 +1069,7 @@ crypto_pk_public_checksig_digest(crypto_pk_t *env, const char *data, * at least the length of the modulus of env. */ int -crypto_pk_private_sign(crypto_pk_t *env, char *to, size_t tolen, +crypto_pk_private_sign(const crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen) { int r; @@ -1084,7 +1084,7 @@ crypto_pk_private_sign(crypto_pk_t *env, char *to, size_t tolen, r = RSA_private_encrypt((int)fromlen, (unsigned char*)from, (unsigned char*)to, - env->key, RSA_PKCS1_PADDING); + (RSA*)env->key, RSA_PKCS1_PADDING); if (r<0) { crypto_log_errors(LOG_WARN, "generating RSA signature"); return -1; @@ -1298,7 +1298,7 @@ crypto_pk_get_digest(const crypto_pk_t *pk, char *digest_out) unsigned char *buf = NULL; int len; - len = i2d_RSAPublicKey(pk->key, &buf); + len = i2d_RSAPublicKey((RSA*)pk->key, &buf); if (len < 0 || buf == NULL) return -1; if (crypto_digest(digest_out, (char*)buf, len) < 0) { diff --git a/src/common/crypto.h b/src/common/crypto.h index 526619766d..15d1f6e3a9 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -147,9 +147,9 @@ int crypto_pk_write_private_key_to_filename(crypto_pk_t *env, const char *fname); int crypto_pk_check_key(crypto_pk_t *env); -int crypto_pk_cmp_keys(crypto_pk_t *a, crypto_pk_t *b); -int crypto_pk_eq_keys(crypto_pk_t *a, crypto_pk_t *b); -size_t crypto_pk_keysize(crypto_pk_t *env); +int crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b); +int crypto_pk_eq_keys(const crypto_pk_t *a, const crypto_pk_t *b); +size_t crypto_pk_keysize(const crypto_pk_t *env); int crypto_pk_num_bits(crypto_pk_t *env); crypto_pk_t *crypto_pk_dup_key(crypto_pk_t *orig); crypto_pk_t *crypto_pk_copy_full(crypto_pk_t *orig); @@ -161,11 +161,11 @@ int crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen, int crypto_pk_private_decrypt(crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen, int padding, int warnOnFailure); -int crypto_pk_public_checksig(crypto_pk_t *env, char *to, size_t tolen, +int crypto_pk_public_checksig(const crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen); int crypto_pk_public_checksig_digest(crypto_pk_t *env, const char *data, size_t datalen, const char *sig, size_t siglen); -int crypto_pk_private_sign(crypto_pk_t *env, char *to, size_t tolen, +int crypto_pk_private_sign(const crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen); int crypto_pk_private_sign_digest(crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen); diff --git a/src/or/or.h b/src/or/or.h index 437183e727..d030189b61 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2023,6 +2023,9 @@ typedef struct { curve25519_public_key_t *onion_curve25519_pkey; /** Certificate for ed25519 signing key */ struct tor_cert_st *signing_key_cert; + /** What's the earliest expiration time on all the certs in this + * routerinfo? */ + time_t cert_expiration_time; char *platform; /**< What software/operating system is this OR using? */ @@ -5043,6 +5046,8 @@ typedef enum was_router_added_t { /* Router descriptor was rejected because it was older than * OLD_ROUTER_DESC_MAX_AGE. */ ROUTER_WAS_TOO_OLD = -7, /* note contrast with 'NOT_NEW' */ + /* DOCDOC */ + ROUTER_CERTS_EXPIRED = -8 } was_router_added_t; /********************************* routerparse.c ************************/ diff --git a/src/or/router.c b/src/or/router.c index 6b2a238140..2087a25fca 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -2000,6 +2000,8 @@ router_rebuild_descriptor(int force) } if (! (ri->cache_info.signed_descriptor_body = router_dump_router_to_string(ri, get_server_identity_key(), + get_onion_key(), + get_current_curve25519_keypair(), get_master_signing_keypair())) ) { log_warn(LD_BUG, "Couldn't generate router descriptor."); routerinfo_free(ri); @@ -2306,7 +2308,9 @@ get_platform_str(char *platform, size_t len) */ char * router_dump_router_to_string(routerinfo_t *router, - crypto_pk_t *ident_key, + const crypto_pk_t *ident_key, + const crypto_pk_t *tap_key, + const curve25519_keypair_t *ntor_keypair, const ed25519_keypair_t *signing_keypair) { char *address = NULL; @@ -2325,6 +2329,8 @@ router_dump_router_to_string(routerinfo_t *router, char *output = NULL; const int emit_ed_sigs = signing_keypair && router->signing_key_cert; char *ed_cert_line = NULL; + char *rsa_tap_cc_line = NULL; + char *ntor_cc_line = NULL; /* Make sure the identity key matches the one in the routerinfo. */ if (!crypto_pk_eq_keys(ident_key, router->identity_pkey)) { @@ -2377,6 +2383,67 @@ router_dump_router_to_string(routerinfo_t *router, goto err; } + /* Cross-certify with RSA key */ + if (tap_key && router->signing_key_cert && + router->signing_key_cert->signing_key_included) { + char buf[256]; + int tap_cc_len = 0; + uint8_t *tap_cc = + make_tap_onion_key_crosscert(tap_key, + &router->signing_key_cert->signing_key, + router->identity_pkey, + &tap_cc_len); + if (!tap_cc) { + log_warn(LD_BUG,"make_tap_onion_key_crosscert failed!"); + goto err; + } + + if (base64_encode(buf, sizeof(buf), (const char*)tap_cc, tap_cc_len) < 0) { + log_warn(LD_BUG,"base64_encode(rsa_crosscert) failed!"); + tor_free(tap_cc); + goto err; + } + tor_free(tap_cc); + + tor_asprintf(&rsa_tap_cc_line, + "onion-key-crosscert\n" + "-----BEGIN CROSSCERT-----\n" + "%s" + "-----END CROSSCERT-----\n", buf); + } + + /* Cross-certify with onion keys */ + if (ntor_keypair && router->signing_key_cert && + router->signing_key_cert->signing_key_included) { + int sign = 0; + char buf[256]; + /* XXXX Base the expiration date on the actual onion key expiration time?*/ + tor_cert_t *cert = + make_ntor_onion_key_crosscert(ntor_keypair, + &router->signing_key_cert->signing_key, + router->cache_info.published_on, + MIN_ONION_KEY_LIFETIME, &sign); + if (!cert) { + log_warn(LD_BUG,"make_ntor_onion_key_crosscert failed!"); + goto err; + } + tor_assert(sign == 0 || sign == 1); + + if (base64_encode(buf, sizeof(buf), + (const char*)cert->encoded, cert->encoded_len)<0) { + log_warn(LD_BUG,"base64_encode(ntor_crosscert) failed!"); + tor_cert_free(cert); + goto err; + } + tor_cert_free(cert); + + tor_asprintf(&ntor_cc_line, + "ntor-onion-key-crosscert %d\n" + "-----BEGIN ED25519 CERT-----\n" + "%s" + "-----END ED25519 CERT-----\n", sign, buf); + } + /* Encode the publication time. */ format_iso_time(published, router->cache_info.published_on); @@ -2426,6 +2493,7 @@ router_dump_router_to_string(routerinfo_t *router, "%s%s%s%s" "onion-key\n%s" "signing-key\n%s" + "%s%s" "%s%s%s%s", router->nickname, address, @@ -2446,6 +2514,8 @@ router_dump_router_to_string(routerinfo_t *router, (options->DownloadExtraInfo || options->V3AuthoritativeDir) ? "caches-extra-info\n" : "", onion_pkey, identity_pkey, + rsa_tap_cc_line ? rsa_tap_cc_line : "", + ntor_cc_line ? ntor_cc_line : "", family_line, we_are_hibernating() ? "hibernating 1\n" : "", options->HidServDirectoryV2 ? "hidden-service-dir\n" : "", diff --git a/src/or/router.h b/src/or/router.h index c53a104ebc..cb813c6813 100644 --- a/src/or/router.h +++ b/src/or/router.h @@ -91,7 +91,9 @@ int router_is_me(const routerinfo_t *router); int router_pick_published_address(const or_options_t *options, uint32_t *addr); int router_rebuild_descriptor(int force); char *router_dump_router_to_string(routerinfo_t *router, - crypto_pk_t *ident_key, + const crypto_pk_t *ident_key, + const crypto_pk_t *tap_key, + const curve25519_keypair_t *ntor_keypair, const ed25519_keypair_t *signing_keypair); char *router_dump_exit_policy_to_string(const routerinfo_t *router, int include_ipv4, diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c index 6609d89311..9ad5a89479 100644 --- a/src/or/routerkeys.c +++ b/src/or/routerkeys.c @@ -435,6 +435,93 @@ get_current_auth_key_cert(void) return auth_key_cert; } +/** Construct cross-certification for the master identity key with + * the ntor onion key. Store the sign of the corresponding ed25519 public key + * in *sign_out. */ +tor_cert_t * +make_ntor_onion_key_crosscert(const curve25519_keypair_t *onion_key, + const ed25519_public_key_t *master_id_key, time_t now, time_t lifetime, + int *sign_out) +{ + tor_cert_t *cert = NULL; + ed25519_keypair_t ed_onion_key; + + if (ed25519_keypair_from_curve25519_keypair(&ed_onion_key, sign_out, + onion_key) < 0) + goto end; + + cert = tor_cert_create(&ed_onion_key, CERT_TYPE_ONION_ID, master_id_key, + now, lifetime, 0); + + end: + memwipe(&ed_onion_key, 0, sizeof(ed_onion_key)); + return cert; +} + +/** Construct and return an RSA signature for the TAP onion key to + * cross-certify the RSA and Ed25519 identity keys. Set len_out to its + * length. */ +uint8_t * +make_tap_onion_key_crosscert(const crypto_pk_t *onion_key, + const ed25519_public_key_t *master_id_key, + const crypto_pk_t *rsa_id_key, + int *len_out) +{ + uint8_t signature[PK_BYTES]; + uint8_t signed_data[DIGEST_LEN + ED25519_PUBKEY_LEN]; + + *len_out = 0; + crypto_pk_get_digest(rsa_id_key, (char*)signed_data); + memcpy(signed_data + DIGEST_LEN, master_id_key->pubkey, ED25519_PUBKEY_LEN); + + int r = crypto_pk_private_sign(onion_key, + (char*)signature, sizeof(signature), + (const char*)signed_data, sizeof(signed_data)); + if (r < 0) + return NULL; + + *len_out = r; + + return tor_memdup(signature, r); +} + +/** Check whether an RSA-TAP cross-certification is correct. Return 0 if it + * is, -1 if it isn't. */ +int +check_tap_onion_key_crosscert(const uint8_t *crosscert, + int crosscert_len, + const crypto_pk_t *onion_pkey, + const ed25519_public_key_t *master_id_pkey, + const uint8_t *rsa_id_digest) +{ + uint8_t *cc = tor_malloc(crypto_pk_keysize(onion_pkey)); + int cc_len = + crypto_pk_public_checksig(onion_pkey, + (char*)cc, + crypto_pk_keysize(onion_pkey), + (const char*)crosscert, + crosscert_len); + if (cc_len < 0) { + goto err; + } + if (cc_len < DIGEST_LEN + ED25519_PUBKEY_LEN) { + log_warn(LD_DIR, "Short signature on cross-certification with TAP key"); + goto err; + } + if (tor_memneq(cc, rsa_id_digest, DIGEST_LEN) || + tor_memneq(cc + DIGEST_LEN, master_id_pkey->pubkey, + ED25519_PUBKEY_LEN)) { + log_warn(LD_DIR, "Incorrect cross-certification with TAP key"); + goto err; + } + + tor_free(cc); + return 0; + err: + tor_free(cc); + return -1; +} + void routerkeys_free_all(void) { diff --git a/src/or/routerkeys.h b/src/or/routerkeys.h index eb21401d56..5e2ef45314 100644 --- a/src/or/routerkeys.h +++ b/src/or/routerkeys.h @@ -37,6 +37,22 @@ const ed25519_keypair_t *get_current_auth_keypair(void); const struct tor_cert_st *get_current_link_key_cert(void); const struct tor_cert_st *get_current_auth_key_cert(void); +struct tor_cert_st *make_ntor_onion_key_crosscert( + const curve25519_keypair_t *onion_key, + const ed25519_public_key_t *master_id_key, + time_t now, time_t lifetime, + int *sign_out); +uint8_t *make_tap_onion_key_crosscert(const crypto_pk_t *onion_key, + const ed25519_public_key_t *master_id_key, + const crypto_pk_t *rsa_id_key, + int *len_out); + +int check_tap_onion_key_crosscert(const uint8_t *crosscert, + int crosscert_len, + const crypto_pk_t *onion_pkey, + const ed25519_public_key_t *master_id_pkey, + const uint8_t *rsa_id_digest); + int load_ed_keys(const or_options_t *options, time_t now); void routerkeys_free_all(void); diff --git a/src/or/routerlist.c b/src/or/routerlist.c index 069f70d662..dbb93e4a64 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -3294,6 +3294,11 @@ router_add_to_routerlist(routerinfo_t *router, const char **msg, old_router = router_get_mutable_by_digest(id_digest); + /* Make sure that it isn't expired. */ + if (router->cert_expiration_time < approx_time()) { + return ROUTER_CERTS_EXPIRED; + } + /* Make sure that we haven't already got this exact descriptor. */ if (sdmap_get(routerlist->desc_digest_map, router->cache_info.signed_descriptor_digest)) { diff --git a/src/or/routerlist.h b/src/or/routerlist.h index 78c3fbb880..200533fe91 100644 --- a/src/or/routerlist.h +++ b/src/or/routerlist.h @@ -118,13 +118,15 @@ WRA_WAS_ADDED(was_router_added_t s) { * - not in the consensus * - neither in the consensus nor in any networkstatus document * - it was outdated. + * - its certificates were expired. */ static INLINE int WRA_WAS_OUTDATED(was_router_added_t s) { return (s == ROUTER_WAS_TOO_OLD || s == ROUTER_IS_ALREADY_KNOWN || s == ROUTER_NOT_IN_CONSENSUS || - s == ROUTER_NOT_IN_CONSENSUS_OR_NETWORKSTATUS); + s == ROUTER_NOT_IN_CONSENSUS_OR_NETWORKSTATUS || + s == ROUTER_CERTS_EXPIRED); } /** Return true iff the outcome code in s indicates that the descriptor * was flat-out rejected. */ @@ -138,7 +140,8 @@ static INLINE int WRA_NEVER_DOWNLOADABLE(was_router_added_t s) { return (s == ROUTER_AUTHDIR_REJECTS || s == ROUTER_BAD_EI || - s == ROUTER_WAS_TOO_OLD); + s == ROUTER_WAS_TOO_OLD || + s == ROUTER_CERTS_EXPIRED); } was_router_added_t router_add_to_routerlist(routerinfo_t *router, const char **msg, diff --git a/src/or/routerparse.c b/src/or/routerparse.c index dbd6fdd472..b3bb565e58 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -24,6 +24,7 @@ #include "microdesc.h" #include "networkstatus.h" #include "rephist.h" +#include "routerkeys.h" #include "routerparse.h" #include "entrynodes.h" #include "torcert.h" @@ -87,6 +88,8 @@ typedef enum { K_IPV6_POLICY, K_ROUTER_SIG_ED25519, K_IDENTITY_ED25519, + K_ONION_KEY_CROSSCERT, + K_NTOR_ONION_KEY_CROSSCERT, K_DIRREQ_END, K_DIRREQ_V2_IPS, @@ -299,6 +302,9 @@ static token_rule_t routerdesc_token_table[] = { T01("hidden-service-dir", K_HIDDEN_SERVICE_DIR, NO_ARGS, NO_OBJ ), T01("identity-ed25519", K_IDENTITY_ED25519, NO_ARGS, NEED_OBJ ), T01("router-sig-ed25519", K_ROUTER_SIG_ED25519, GE(1), NO_OBJ ), + T01("onion-key-crosscert", K_ONION_KEY_CROSSCERT, NO_ARGS, NEED_OBJ ), + T01("ntor-onion-key-crosscert", K_NTOR_ONION_KEY_CROSSCERT, + EQ(1), NEED_OBJ ), T01("allow-single-hop-exits",K_ALLOW_SINGLE_HOP_EXITS, NO_ARGS, NO_OBJ ), @@ -648,7 +654,7 @@ router_get_extrainfo_hash(const char *s, size_t s_len, char *digest) char * router_get_dirobj_signature(const char *digest, size_t digest_len, - crypto_pk_t *private_key) + const crypto_pk_t *private_key) { char *signature; size_t i, keysize; @@ -868,8 +874,8 @@ check_signature_token(const char *digest, tor_free(signed_digest); return -1; } -// log_debug(LD_DIR,"Signed %s hash starts %s", doctype, -// hex_str(signed_digest,4)); + // log_debug(LD_DIR,"Signed %s hash starts %s", doctype, + // hex_str(signed_digest,4)); if (tor_memneq(digest, signed_digest, digest_len)) { log_warn(LD_DIR, "Error reading %s: signature does not match.", doctype); tor_free(signed_digest); @@ -1116,6 +1122,7 @@ router_parse_entry_from_string(const char *s, const char *end, size_t prepend_len = prepend_annotations ? strlen(prepend_annotations) : 0; int ok = 1; memarea_t *area = NULL; + tor_cert_t *ntor_cc_cert = NULL; /* Do not set this to '1' until we have parsed everything that we intend to * parse that's covered by the hash. */ int can_dl_again = 0; @@ -1191,6 +1198,7 @@ router_parse_entry_from_string(const char *s, const char *end, tor_assert(tok->n_args >= 5); router = tor_malloc_zero(sizeof(routerinfo_t)); + router->cert_expiration_time = TIME_MAX; router->cache_info.routerlist_index = -1; router->cache_info.annotations_len = s-start_of_annotations + prepend_len; router->cache_info.signed_descriptor_len = end-s; @@ -1313,16 +1321,30 @@ router_parse_entry_from_string(const char *s, const char *end, tor_memdup(&k, sizeof(curve25519_public_key_t)); } + tok = find_by_keyword(tokens, K_SIGNING_KEY); + router->identity_pkey = tok->key; + tok->key = NULL; /* Prevent free */ + if (crypto_pk_get_digest(router->identity_pkey, + router->cache_info.identity_digest)) { + log_warn(LD_DIR, "Couldn't calculate key digest"); goto err; + } + { - directory_token_t *ed_sig_tok, *ed_cert_tok; + directory_token_t *ed_sig_tok, *ed_cert_tok, *cc_tap_tok, *cc_ntor_tok; ed_sig_tok = find_opt_by_keyword(tokens, K_ROUTER_SIG_ED25519); ed_cert_tok = find_opt_by_keyword(tokens, K_IDENTITY_ED25519); - if (!ed_sig_tok != !ed_cert_tok) { - log_warn(LD_DIR, "Router descriptor with only partial ed25519 support"); + cc_tap_tok = find_opt_by_keyword(tokens, K_ONION_KEY_CROSSCERT); + cc_ntor_tok = find_opt_by_keyword(tokens, K_NTOR_ONION_KEY_CROSSCERT); + int n_ed_toks = !!ed_sig_tok + !!ed_cert_tok + + !!cc_tap_tok + !!cc_ntor_tok; + if ((n_ed_toks != 0 && n_ed_toks != 4) || + (n_ed_toks == 4 && !router->onion_curve25519_pkey)) { + log_warn(LD_DIR, "Router descriptor with only partial ed25519/" + "cross-certification support"); goto err; } if (ed_sig_tok) { - tor_assert(ed_cert_tok); + tor_assert(ed_cert_tok && cc_tap_tok && cc_ntor_tok); if (ed_cert_tok != smartlist_get(tokens, 0) && ed_cert_tok != smartlist_get(tokens, 1)) { log_warn(LD_DIR, "Ed25519 certificate in wrong position"); @@ -1336,10 +1358,25 @@ router_parse_entry_from_string(const char *s, const char *end, log_warn(LD_DIR, "Wrong object type on identity-ed25519 in decriptor"); goto err; } + if (strcmp(cc_ntor_tok->object_type, "ED25519 CERT")) { + log_warn(LD_DIR, "Wrong object type on ntor-onion-key-crosscert " + "in decriptor"); + goto err; + } + if (strcmp(cc_tap_tok->object_type, "CROSSCERT")) { + log_warn(LD_DIR, "Wrong object type on onion-key-crosscert " + "in decriptor"); + goto err; + } + if (strcmp(cc_ntor_tok->args[0], "0") && + strcmp(cc_ntor_tok->args[0], "1")) { + log_warn(LD_DIR, "Bad sign bit on ntor-onion-key-crosscert"); + goto err; + } + int ntor_cc_sign_bit = !strcmp(cc_ntor_tok->args[0], "1"); uint8_t d256[DIGEST256_LEN]; const char *signed_start, *signed_end; - tor_cert_t *cert = tor_cert_parse( (const uint8_t*)ed_cert_tok->object_body, ed_cert_tok->object_size); @@ -1347,13 +1384,33 @@ router_parse_entry_from_string(const char *s, const char *end, log_warn(LD_DIR, "Couldn't parse ed25519 cert"); goto err; } - router->signing_key_cert = cert; + router->signing_key_cert = cert; /* makes sure it gets freed. */ if (cert->cert_type != CERT_TYPE_ID_SIGNING || ! cert->signing_key_included) { log_warn(LD_DIR, "Invalid form for ed25519 cert"); goto err; } + ntor_cc_cert = tor_cert_parse((const uint8_t*)cc_ntor_tok->object_body, + cc_ntor_tok->object_size); + if (!cc_ntor_tok) { + log_warn(LD_DIR, "Couldn't parse ntor-onion-key-crosscert cert"); + goto err; + } + if (ntor_cc_cert->cert_type != CERT_TYPE_ONION_ID || + ! ed25519_pubkey_eq(&ntor_cc_cert->signed_key, &cert->signing_key)) { + log_warn(LD_DIR, "Invalid contents for ntor-onion-key-crosscert cert"); + goto err; + } + + ed25519_public_key_t ntor_cc_pk; + if (ed25519_public_key_from_curve25519_public_key(&ntor_cc_pk, + router->onion_curve25519_pkey, + ntor_cc_sign_bit)<0) { + log_warn(LD_DIR, "Error converting onion key to ed25519"); + goto err; + } + if (router_get_hash_impl_helper(s, end-s, "router ", "\nrouter-sig-ed25519", ' ', &signed_start, &signed_end) < 0) { @@ -1367,40 +1424,50 @@ router_parse_entry_from_string(const char *s, const char *end, crypto_digest_get_digest(d, (char*)d256, sizeof(d256)); crypto_digest_free(d); - ed25519_checkable_t check[2]; - int check_ok[2]; + ed25519_checkable_t check[3]; + int check_ok[3]; if (tor_cert_get_checkable_sig(&check[0], cert, NULL) < 0) { log_err(LD_BUG, "Couldn't create 'checkable' for cert."); goto err; } - if (ed25519_signature_from_base64(&check[1].signature, + if (tor_cert_get_checkable_sig(&check[1], + ntor_cc_cert, &ntor_cc_pk) < 0) { + log_err(LD_BUG, "Couldn't create 'checkable' for ntor_cc_cert."); + goto err; + } + + if (ed25519_signature_from_base64(&check[2].signature, ed_sig_tok->args[0])<0) { log_warn(LD_DIR, "Couldn't decode ed25519 signature"); goto err; } - check[1].pubkey = &cert->signed_key; - check[1].msg = d256; - check[1].len = DIGEST256_LEN; + check[2].pubkey = &cert->signed_key; + check[2].msg = d256; + check[2].len = DIGEST256_LEN; - if (ed25519_checksig_batch(check_ok, check, 2) < 0) { - log_warn(LD_DIR, "Incorrect ed25519 signatures"); + if (ed25519_checksig_batch(check_ok, check, 3) < 0) { + log_warn(LD_DIR, "Incorrect ed25519 signature(s)"); goto err; } - if (cert->valid_until < time(NULL)) { - log_warn(LD_DIR, "Expired ed25519 certificate in router descriptor"); + + if (check_tap_onion_key_crosscert( + (const uint8_t*)cc_tap_tok->object_body, + (int)cc_tap_tok->object_size, + router->onion_pkey, + &cert->signing_key, + (const uint8_t*)router->cache_info.identity_digest)<0) { + log_warn(LD_DIR, "Incorrect TAP cross-verification"); goto err; } + + /* We check this before adding it to the routerlist. */ + if (cert->valid_until < ntor_cc_cert->valid_until) + router->cert_expiration_time = cert->valid_until; + else + router->cert_expiration_time = ntor_cc_cert->valid_until; } } - tok = find_by_keyword(tokens, K_SIGNING_KEY); - router->identity_pkey = tok->key; - tok->key = NULL; /* Prevent free */ - if (crypto_pk_get_digest(router->identity_pkey, - router->cache_info.identity_digest)) { - log_warn(LD_DIR, "Couldn't calculate key digest"); goto err; - } - if ((tok = find_opt_by_keyword(tokens, K_FINGERPRINT))) { /* If there's a fingerprint line, it must match the identity digest. */ char d[DIGEST_LEN]; @@ -1527,6 +1594,7 @@ router_parse_entry_from_string(const char *s, const char *end, routerinfo_free(router); router = NULL; done: + tor_cert_free(ntor_cc_cert); if (tokens) { SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t)); smartlist_free(tokens); diff --git a/src/or/routerparse.h b/src/or/routerparse.h index 52e53a833c..7776063f03 100644 --- a/src/or/routerparse.h +++ b/src/or/routerparse.h @@ -19,7 +19,7 @@ int router_get_extrainfo_hash(const char *s, size_t s_len, char *digest); #define DIROBJ_MAX_SIG_LEN 256 char *router_get_dirobj_signature(const char *digest, size_t digest_len, - crypto_pk_t *private_key); + const crypto_pk_t *private_key); int router_append_dirobj_signature(char *buf, size_t buf_len, const char *digest, size_t digest_len, diff --git a/src/or/torcert.h b/src/or/torcert.h index 644cbf812d..ae9361ff16 100644 --- a/src/or/torcert.h +++ b/src/or/torcert.h @@ -11,6 +11,7 @@ #define CERT_TYPE_ID_SIGNING 0x04 #define CERT_TYPE_SIGNING_LINK 0x05 #define CERT_TYPE_SIGNING_AUTH 0x06 +#define CERT_TYPE_ONION_ID 0x0A #define CERT_FLAG_INCLUDE_SIGNING_KEY 0x1 diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 85ca40f3de..d7b5e1b7c4 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -21,6 +21,7 @@ #include "hibernate.h" #include "networkstatus.h" #include "router.h" +#include "routerkeys.h" #include "routerlist.h" #include "routerparse.h" #include "test.h" @@ -89,6 +90,8 @@ test_dir_formats(void *arg) routerinfo_t *rp1 = NULL, *rp2 = NULL; addr_policy_t *ex1, *ex2; routerlist_t *dir1 = NULL, *dir2 = NULL; + uint8_t *rsa_cc = NULL; + tor_cert_t *ntor_cc = NULL; or_options_t *options = get_options_mutable(); const addr_policy_t *p; time_t now = time(NULL); @@ -152,8 +155,10 @@ test_dir_formats(void *arg) r2->dir_port = 0; r2->onion_pkey = crypto_pk_dup_key(pk2); r2->onion_curve25519_pkey = tor_malloc_zero(sizeof(curve25519_public_key_t)); - curve25519_public_from_base64(r2->onion_curve25519_pkey, - "skyinAnvardNostarsNomoonNowindormistsorsnow"); + curve25519_keypair_t r2_onion_keypair; + curve25519_keypair_generate(&r2_onion_keypair, 0); + r2->onion_curve25519_pkey = tor_memdup(&r2_onion_keypair.pubkey, + sizeof(curve25519_public_key_t)); r2->identity_pkey = crypto_pk_dup_key(pk1); r2->bandwidthrate = r2->bandwidthburst = r2->bandwidthcapacity = 3000; r2->exit_policy = smartlist_new(); @@ -169,7 +174,7 @@ test_dir_formats(void *arg) /* XXXX025 router_dump_to_string should really take this from ri.*/ options->ContactInfo = tor_strdup("Magri White " ""); - buf = router_dump_router_to_string(r1, pk2, NULL); + buf = router_dump_router_to_string(r1, pk2, NULL, NULL, NULL); tor_free(options->ContactInfo); tt_assert(buf); @@ -202,7 +207,7 @@ test_dir_formats(void *arg) tt_str_op(buf,OP_EQ, buf2); tor_free(buf); - buf = router_dump_router_to_string(r1, pk2, NULL); + buf = router_dump_router_to_string(r1, pk2, NULL, NULL, NULL); tt_assert(buf); cp = buf; rp1 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL,NULL); @@ -238,20 +243,49 @@ test_dir_formats(void *arg) strlcat(buf2, pk2_str, sizeof(buf2)); strlcat(buf2, "signing-key\n", sizeof(buf2)); strlcat(buf2, pk1_str, sizeof(buf2)); + int rsa_cc_len; + rsa_cc = make_tap_onion_key_crosscert(pk2, + &kp1.pubkey, + pk1, + &rsa_cc_len); + tt_assert(rsa_cc); + base64_encode(cert_buf, sizeof(cert_buf), (char*)rsa_cc, rsa_cc_len); + strlcat(buf2, "onion-key-crosscert\n" + "-----BEGIN CROSSCERT-----\n", sizeof(buf2)); + strlcat(buf2, cert_buf, sizeof(buf2)); + strlcat(buf2, "-----END CROSSCERT-----\n", sizeof(buf2)); + int ntor_cc_sign; + ntor_cc = make_ntor_onion_key_crosscert(&r2_onion_keypair, + &kp1.pubkey, + r2->cache_info.published_on, + MIN_ONION_KEY_LIFETIME, + &ntor_cc_sign); + tt_assert(ntor_cc); + base64_encode(cert_buf, sizeof(cert_buf), + (char*)ntor_cc->encoded, ntor_cc->encoded_len); + tor_snprintf(buf2+strlen(buf2), sizeof(buf2)-strlen(buf2), + "ntor-onion-key-crosscert %d\n" + "-----BEGIN ED25519 CERT-----\n" + "%s" + "-----END ED25519 CERT-----\n", ntor_cc_sign, cert_buf); + strlcat(buf2, "hidden-service-dir\n", sizeof(buf2)); - strlcat(buf2, "ntor-onion-key " - "skyinAnvardNostarsNomoonNowindormistsorsnow=\n", sizeof(buf2)); + strlcat(buf2, "ntor-onion-key ", sizeof(buf2)); + base64_encode(cert_buf, sizeof(cert_buf), + (const char*)r2_onion_keypair.pubkey.public_key, 32); + strlcat(buf2, cert_buf, sizeof(buf2)); strlcat(buf2, "accept *:80\nreject 18.0.0.0/8:24\n", sizeof(buf2)); strlcat(buf2, "router-sig-ed25519 ", sizeof(buf2)); - buf = router_dump_router_to_string(r2, pk1, &kp2); + buf = router_dump_router_to_string(r2, pk1, pk2, &r2_onion_keypair, &kp2); tt_assert(buf); buf[strlen(buf2)] = '\0'; /* Don't compare the sig; it's never the same * twice */ - tt_str_op(buf,OP_EQ, buf2); + + tt_str_op(buf, OP_EQ, buf2); tor_free(buf); - buf = router_dump_router_to_string(r2, pk1, NULL); + buf = router_dump_router_to_string(r2, pk1, NULL, NULL, NULL); cp = buf; rp2 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL,NULL); tt_assert(rp2); @@ -304,6 +338,7 @@ test_dir_formats(void *arg) if (rp2) routerinfo_free(rp2); + tor_free(rsa_cc); tor_free(buf); tor_free(pk1_str); tor_free(pk2_str); @@ -1418,6 +1453,7 @@ generate_ri_from_rs(const vote_routerstatus_t *vrs) static time_t published = 0; r = tor_malloc_zero(sizeof(routerinfo_t)); + r->cert_expiration_time = TIME_MAX; memcpy(r->cache_info.identity_digest, rs->identity_digest, DIGEST_LEN); memcpy(r->cache_info.signed_descriptor_digest, rs->descriptor_digest, DIGEST_LEN); diff --git a/src/test/test_routerkeys.c b/src/test/test_routerkeys.c index 8780213a29..2434255b63 100644 --- a/src/test/test_routerkeys.c +++ b/src/test/test_routerkeys.c @@ -514,6 +514,74 @@ test_routerkeys_ed_keys_init_all(void *arg) routerkeys_free_all(); } +static void +test_routerkeys_cross_certify_ntor(void *args) +{ + (void) args; + + tor_cert_t *cert = NULL; + curve25519_keypair_t onion_keys; + ed25519_public_key_t master_key; + ed25519_public_key_t onion_check_key; + time_t now = time(NULL); + int sign; + + tt_int_op(0, ==, ed25519_public_from_base64(&master_key, + "IamwritingthesetestsOnARainyAfternoonin2014")); + tt_int_op(0, ==, curve25519_keypair_generate(&onion_keys, 0)); + cert = make_ntor_onion_key_crosscert(&onion_keys, + &master_key, + now, 10000, + &sign); + tt_assert(cert); + tt_assert(sign == 0 || sign == 1); + tt_int_op(cert->cert_type, ==, CERT_TYPE_ONION_ID); + tt_int_op(1, ==, ed25519_pubkey_eq(&cert->signed_key, &master_key)); + tt_int_op(0, ==, ed25519_public_key_from_curve25519_public_key( + &onion_check_key, &onion_keys.pubkey, sign)); + tt_int_op(0, ==, tor_cert_checksig(cert, &onion_check_key, now)); + + done: + tor_cert_free(cert); +} + +static void +test_routerkeys_cross_certify_tap(void *args) +{ + (void)args; + uint8_t *cc = NULL; + int cc_len; + ed25519_public_key_t master_key; + crypto_pk_t *onion_key = pk_generate(2), *id_key = pk_generate(1); + char digest[20]; + char buf[128]; + int n; + + tt_int_op(0, ==, ed25519_public_from_base64(&master_key, + "IAlreadyWroteTestsForRouterdescsUsingTheseX")); + + cc = make_tap_onion_key_crosscert(onion_key, + &master_key, + id_key, &cc_len); + tt_assert(cc); + tt_assert(cc_len); + + n = crypto_pk_public_checksig(onion_key, buf, sizeof(buf), + (char*)cc, cc_len); + tt_int_op(n,>,0); + tt_int_op(n,==,52); + + crypto_pk_get_digest(id_key, digest); + tt_mem_op(buf,==,digest,20); + tt_mem_op(buf+20,==,master_key.pubkey,32); + + tt_int_op(0, ==, check_tap_onion_key_crosscert(cc, cc_len, + onion_key, &master_key, (uint8_t*)digest)); + + done: + tor_free(cc); +} + #define TEST(name, flags) \ { #name , test_routerkeys_ ## name, (flags), NULL, NULL } @@ -524,6 +592,8 @@ struct testcase_t routerkeys_tests[] = { TEST(ed_key_init_basic, TT_FORK), TEST(ed_key_init_split, TT_FORK), TEST(ed_keys_init_all, TT_FORK), + TEST(cross_certify_ntor, 0), + TEST(cross_certify_tap, 0), END_OF_TESTCASES };