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
};