Commit f3f6ecef authored by Nick Mathewson's avatar Nick Mathewson 🎨
Browse files

r19690@catbus: nickm | 2008-05-11 22:13:31 -0400

 Implement a proposal to let a directory authority migrate its identity key without ceasing to sign consensuses.


svn:r14584
parent b7a80920
...@@ -78,10 +78,12 @@ Changes in version 0.2.1.1-alpha - 2008-??-?? ...@@ -78,10 +78,12 @@ Changes in version 0.2.1.1-alpha - 2008-??-??
- Make dumpstats() log the fullness and size of openssl-internal - Make dumpstats() log the fullness and size of openssl-internal
buffers. buffers.
- Servers support a new URL scheme for consensus downloads that - Servers support a new URL scheme for consensus downloads that
allos the client to specify which authorities are trusted. allows the client to specify which authorities are trusted.
The server then only sends the consensus if the client will The server then only sends the consensus if the client will
trust it. Otherwise a 404 error is sent back. Clients use trust it. Otherwise a 404 error is sent back. Clients use
this new scheme when the server supports it. this new scheme when the server supports it.
- Add a new V3AuthUseLegacyKey option to make it easier for authorities
to change their identity keys if they have to.
o Minor features (security): o Minor features (security):
- Reject requests for reverse-dns lookup of names in a private - Reject requests for reverse-dns lookup of names in a private
......
...@@ -301,6 +301,7 @@ static config_var_t _option_vars[] = { ...@@ -301,6 +301,7 @@ static config_var_t _option_vars[] = {
V(V3AuthVoteDelay, INTERVAL, "5 minutes"), V(V3AuthVoteDelay, INTERVAL, "5 minutes"),
V(V3AuthDistDelay, INTERVAL, "5 minutes"), V(V3AuthDistDelay, INTERVAL, "5 minutes"),
V(V3AuthNIntervalsValid, UINT, "3"), V(V3AuthNIntervalsValid, UINT, "3"),
V(V3AuthUseLegacyKey, BOOL, "0"),
VAR("VersioningAuthoritativeDirectory",BOOL,VersioningAuthoritativeDir, "0"), VAR("VersioningAuthoritativeDirectory",BOOL,VersioningAuthoritativeDir, "0"),
V(VirtualAddrNetwork, STRING, "127.192.0.0/10"), V(VirtualAddrNetwork, STRING, "127.192.0.0/10"),
V(WarnPlaintextPorts, CSV, "23,109,110,143"), V(WarnPlaintextPorts, CSV, "23,109,110,143"),
......
...@@ -2285,6 +2285,13 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_env_t *private_key, ...@@ -2285,6 +2285,13 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_env_t *private_key,
voter->or_port = options->ORPort; voter->or_port = options->ORPort;
voter->contact = tor_strdup(contact); voter->contact = tor_strdup(contact);
memcpy(voter->signing_key_digest, signing_key_digest, DIGEST_LEN); memcpy(voter->signing_key_digest, signing_key_digest, DIGEST_LEN);
if (options->V3AuthUseLegacyKey) {
authority_cert_t *c = get_my_v3_legacy_cert();
if (c) {
crypto_pk_get_digest(c->identity_key, voter->legacy_id_digest);
}
}
v3_out->voters = smartlist_create(); v3_out->voters = smartlist_create();
smartlist_add(v3_out->voters, voter); smartlist_add(v3_out->voters, voter);
v3_out->cert = authority_cert_dup(cert); v3_out->cert = authority_cert_dup(cert);
......
...@@ -105,7 +105,7 @@ format_networkstatus_vote(crypto_pk_env_t *private_signing_key, ...@@ -105,7 +105,7 @@ format_networkstatus_vote(crypto_pk_env_t *private_signing_key,
tor_snprintf(status, len, tor_snprintf(status, len,
"network-status-version 3\n" "network-status-version 3\n"
"vote-status vote\n" "vote-status vote\n"
"consensus-methods 1 2\n" "consensus-methods 1 2 3\n"
"published %s\n" "published %s\n"
"valid-after %s\n" "valid-after %s\n"
"fresh-until %s\n" "fresh-until %s\n"
...@@ -125,6 +125,14 @@ format_networkstatus_vote(crypto_pk_env_t *private_signing_key, ...@@ -125,6 +125,14 @@ format_networkstatus_vote(crypto_pk_env_t *private_signing_key,
tor_free(flags); tor_free(flags);
outp = status + strlen(status); outp = status + strlen(status);
endp = status + len; endp = status + len;
if (!tor_digest_is_zero(voter->legacy_id_digest)) {
char fpbuf[HEX_DIGEST_LEN+1];
base16_encode(fpbuf, sizeof(fpbuf), voter->legacy_id_digest, DIGEST_LEN);
tor_snprintf(outp, endp-outp, "legacy-dir-key %s\n", fpbuf);
outp += strlen(outp);
}
tor_assert(outp + cert->cache_info.signed_descriptor_len < endp); tor_assert(outp + cert->cache_info.signed_descriptor_len < endp);
memcpy(outp, cert->cache_info.signed_descriptor_body, memcpy(outp, cert->cache_info.signed_descriptor_body,
cert->cache_info.signed_descriptor_len); cert->cache_info.signed_descriptor_len);
...@@ -207,6 +215,12 @@ get_voter(const networkstatus_t *vote) ...@@ -207,6 +215,12 @@ get_voter(const networkstatus_t *vote)
return smartlist_get(vote->voters, 0); return smartlist_get(vote->voters, 0);
} }
typedef struct {
networkstatus_t *v;
const char *digest;
int is_legacy;
} dir_src_ent_t;
/** Helper for sorting networkstatus_t votes (not consensuses) by the /** Helper for sorting networkstatus_t votes (not consensuses) by the
* hash of their voters' identity digests. */ * hash of their voters' identity digests. */
static int static int
...@@ -217,6 +231,19 @@ _compare_votes_by_authority_id(const void **_a, const void **_b) ...@@ -217,6 +231,19 @@ _compare_votes_by_authority_id(const void **_a, const void **_b)
get_voter(b)->identity_digest, DIGEST_LEN); get_voter(b)->identity_digest, DIGEST_LEN);
} }
static int
_compare_dir_src_ents_by_authority_id(const void **_a, const void **_b)
{
const dir_src_ent_t *a = *_a, *b = *_b;
const networkstatus_voter_info_t *a_v = get_voter(a->v),
*b_v = get_voter(b->v);
const char *a_id, *b_id;
a_id = a->is_legacy ? a_v->legacy_id_digest : a_v->identity_digest;
b_id = b->is_legacy ? b_v->legacy_id_digest : b_v->identity_digest;
return memcmp(a_id, b_id, DIGEST_LEN);
}
/** Given a sorted list of strings <b>in</b>, add every member to <b>out</b> /** Given a sorted list of strings <b>in</b>, add every member to <b>out</b>
* that occurs more than <b>min</b> times. */ * that occurs more than <b>min</b> times. */
static void static void
...@@ -416,7 +443,7 @@ compute_consensus_method(smartlist_t *votes) ...@@ -416,7 +443,7 @@ compute_consensus_method(smartlist_t *votes)
static int static int
consensus_method_is_supported(int method) consensus_method_is_supported(int method)
{ {
return (method >= 1) && (method <= 2); return (method >= 1) && (method <= 3);
} }
/** Given a list of vote networkstatus_t in <b>votes</b>, our public /** Given a list of vote networkstatus_t in <b>votes</b>, our public
...@@ -581,33 +608,64 @@ networkstatus_compute_consensus(smartlist_t *votes, ...@@ -581,33 +608,64 @@ networkstatus_compute_consensus(smartlist_t *votes,
/* Sort the votes. */ /* Sort the votes. */
smartlist_sort(votes, _compare_votes_by_authority_id); smartlist_sort(votes, _compare_votes_by_authority_id);
/* Add the authority sections. */ /* Add the authority sections. */
SMARTLIST_FOREACH(votes, networkstatus_t *, v,
{ {
char buf[1024]; smartlist_t *dir_sources = smartlist_create();
struct in_addr in; SMARTLIST_FOREACH(votes, networkstatus_t *, v,
char ip[INET_NTOA_BUF_LEN]; {
char fingerprint[HEX_DIGEST_LEN+1]; dir_src_ent_t *e = tor_malloc_zero(sizeof(dir_src_ent_t));
char votedigest[HEX_DIGEST_LEN+1]; e->v = v;
networkstatus_voter_info_t *voter = get_voter(v); e->digest = get_voter(v)->identity_digest;
e->is_legacy = 0;
smartlist_add(dir_sources, e);
if (consensus_method >= 3 &&
!tor_digest_is_zero(get_voter(v)->legacy_id_digest)) {
dir_src_ent_t *e_legacy = tor_malloc_zero(sizeof(dir_src_ent_t));
e_legacy->v = v;
e_legacy->digest = get_voter(v)->legacy_id_digest;
e_legacy->is_legacy = 1;
smartlist_add(dir_sources, e);
}
});
smartlist_sort(dir_sources, _compare_dir_src_ents_by_authority_id);
in.s_addr = htonl(voter->addr); SMARTLIST_FOREACH(dir_sources, const dir_src_ent_t *, e,
tor_inet_ntoa(&in, ip, sizeof(ip)); {
base16_encode(fingerprint, sizeof(fingerprint), voter->identity_digest, char buf[1024];
DIGEST_LEN); struct in_addr in;
base16_encode(votedigest, sizeof(votedigest), voter->vote_digest, char ip[INET_NTOA_BUF_LEN];
DIGEST_LEN); char fingerprint[HEX_DIGEST_LEN+1];
char votedigest[HEX_DIGEST_LEN+1];
networkstatus_t *v = e->v;
networkstatus_voter_info_t *voter = get_voter(v);
if (e->is_legacy)
tor_assert(consensus_method >= 2);
in.s_addr = htonl(voter->addr);
tor_inet_ntoa(&in, ip, sizeof(ip));
base16_encode(fingerprint, sizeof(fingerprint), e->digest, DIGEST_LEN);
base16_encode(votedigest, sizeof(votedigest), voter->vote_digest,
DIGEST_LEN);
tor_snprintf(buf, sizeof(buf), tor_snprintf(buf, sizeof(buf),
"dir-source %s %s %s %s %d %d\n" "dir-source %s%s %s %s %s %d %d\n",
"contact %s\n" voter->nickname, e->is_legacy ? "-legacy" : "",
"vote-digest %s\n", fingerprint, voter->address, ip,
voter->nickname, fingerprint, voter->address, ip, voter->dir_port,
voter->dir_port, voter->or_port);
voter->or_port, smartlist_add(chunks, tor_strdup(buf));
voter->contact, if (! e->is_legacy) {
votedigest); tor_snprintf(buf, sizeof(buf),
smartlist_add(chunks, tor_strdup(buf)); "contact %s\n"
}); "vote-digest %s\n",
voter->contact,
votedigest);
smartlist_add(chunks, tor_strdup(buf));
}
});
SMARTLIST_FOREACH(dir_sources, dir_src_ent_t *, e, tor_free(e));
smartlist_free(dir_sources);
}
/* Add the actual router entries. */ /* Add the actual router entries. */
{ {
...@@ -904,6 +962,22 @@ networkstatus_compute_consensus(smartlist_t *votes, ...@@ -904,6 +962,22 @@ networkstatus_compute_consensus(smartlist_t *votes,
return NULL; /* This leaks, but it should never happen. */ return NULL; /* This leaks, but it should never happen. */
} }
smartlist_add(chunks, tor_strdup(buf)); smartlist_add(chunks, tor_strdup(buf));
if (get_options()->V3AuthUseLegacyKey && consensus_method >= 3) {
crypto_pk_env_t *legacy_key = get_my_v3_legacy_signing_key();
authority_cert_t *legacy_cert = get_my_v3_legacy_cert();
smartlist_add(chunks, tor_strdup("directory-signature "));
crypto_pk_get_fingerprint(legacy_cert->identity_key, fingerprint, 0);
crypto_pk_get_fingerprint(legacy_key, signing_key_fingerprint, 0);
tor_snprintf(buf, sizeof(buf), "%s %s\n", fingerprint,
signing_key_fingerprint);
if (router_append_dirobj_signature(buf, sizeof(buf), digest,
signing_key)) {
log_warn(LD_BUG, "Couldn't sign consensus networkstatus.");
return NULL; /* This leaks, but it should never happen. */
}
smartlist_add(chunks, tor_strdup(buf));
}
} }
result = smartlist_join_strings(chunks, "", 0, NULL); result = smartlist_join_strings(chunks, "", 0, NULL);
......
...@@ -1461,6 +1461,7 @@ typedef struct networkstatus_voter_info_t { ...@@ -1461,6 +1461,7 @@ typedef struct networkstatus_voter_info_t {
uint16_t or_port; /**< OR port of this voter */ uint16_t or_port; /**< OR port of this voter */
char *contact; /**< Contact information for this voter. */ char *contact; /**< Contact information for this voter. */
char vote_digest[DIGEST_LEN]; /**< Digest of this voter's vote, as signed. */ char vote_digest[DIGEST_LEN]; /**< Digest of this voter's vote, as signed. */
char legacy_id_digest[DIGEST_LEN]; /**< From vote only. DOCDOC */
/* Nothing from here on is signed. */ /* Nothing from here on is signed. */
char signing_key_digest[DIGEST_LEN]; /**< Declared digest of signing key char signing_key_digest[DIGEST_LEN]; /**< Declared digest of signing key
...@@ -2339,6 +2340,10 @@ typedef struct { ...@@ -2339,6 +2340,10 @@ typedef struct {
/** The number of intervals we think a consensus should be valid. */ /** The number of intervals we think a consensus should be valid. */
int V3AuthNIntervalsValid; int V3AuthNIntervalsValid;
/** Should advertise and sign consensuses with a legacy key, for key
* migration purposes? */
int V3AuthUseLegacyKey;
/** File to check for a consensus networkstatus, if we don't have one /** File to check for a consensus networkstatus, if we don't have one
* cached. */ * cached. */
char *FallbackNetworkstatusFile; char *FallbackNetworkstatusFile;
...@@ -3772,6 +3777,8 @@ crypto_pk_env_t *get_identity_key(void); ...@@ -3772,6 +3777,8 @@ crypto_pk_env_t *get_identity_key(void);
int identity_key_is_set(void); int identity_key_is_set(void);
authority_cert_t *get_my_v3_authority_cert(void); authority_cert_t *get_my_v3_authority_cert(void);
crypto_pk_env_t *get_my_v3_authority_signing_key(void); crypto_pk_env_t *get_my_v3_authority_signing_key(void);
authority_cert_t *get_my_v3_legacy_cert(void);
crypto_pk_env_t *get_my_v3_legacy_signing_key(void);
void dup_onion_keys(crypto_pk_env_t **key, crypto_pk_env_t **last); void dup_onion_keys(crypto_pk_env_t **key, crypto_pk_env_t **last);
void rotate_onion_key(void); void rotate_onion_key(void);
crypto_pk_env_t *init_key_from_file(const char *fname, int generate, crypto_pk_env_t *init_key_from_file(const char *fname, int generate,
......
...@@ -45,6 +45,9 @@ static crypto_pk_env_t *authority_signing_key = NULL; ...@@ -45,6 +45,9 @@ static crypto_pk_env_t *authority_signing_key = NULL;
* authorities. */ * authorities. */
static authority_cert_t *authority_key_certificate = NULL; static authority_cert_t *authority_key_certificate = NULL;
static crypto_pk_env_t *legacy_signing_key = NULL;
static authority_cert_t *legacy_key_certificate = NULL;
/* (Note that v3 authorities also have a separate "authority identity key", /* (Note that v3 authorities also have a separate "authority identity key",
* but this key is never actually loaded by the Tor process. Instead, it's * but this key is never actually loaded by the Tor process. Instead, it's
* used by tor-gencert to sign new signing keys and make new key * used by tor-gencert to sign new signing keys and make new key
...@@ -144,6 +147,18 @@ get_my_v3_authority_signing_key(void) ...@@ -144,6 +147,18 @@ get_my_v3_authority_signing_key(void)
return authority_signing_key; return authority_signing_key;
} }
authority_cert_t *
get_my_v3_legacy_cert(void)
{
return legacy_key_certificate;
}
crypto_pk_env_t *
get_my_v3_legacy_signing_key(void)
{
return legacy_signing_key;
}
/** Replace the previous onion key with the current onion key, and generate /** Replace the previous onion key with the current onion key, and generate
* a new previous onion key. Immediately after calling this function, * a new previous onion key. Immediately after calling this function,
* the OR should: * the OR should:
...@@ -258,26 +273,26 @@ init_key_from_file(const char *fname, int generate, int severity) ...@@ -258,26 +273,26 @@ init_key_from_file(const char *fname, int generate, int severity)
return NULL; return NULL;
} }
/** Load the v3 (voting) authority signing key and certificate, if they are
* present. Return -1 if anything is missing, mismatched, or unloadable;
* return 0 on success. */
static int static int
init_v3_authority_keys(void) load_authority_keyset(int legacy, crypto_pk_env_t **key_out,
authority_cert_t **cert_out)
{ {
int r = -1;
char *fname = NULL, *cert = NULL; char *fname = NULL, *cert = NULL;
const char *eos = NULL; const char *eos = NULL;
crypto_pk_env_t *signing_key = NULL; crypto_pk_env_t *signing_key = NULL;
authority_cert_t *parsed = NULL; authority_cert_t *parsed = NULL;
int r = -1;
fname = get_datadir_fname2("keys", "authority_signing_key"); fname = get_datadir_fname2("keys",
legacy ? "legacy_signing_key" : "authority_signing_key");
signing_key = init_key_from_file(fname, 0, LOG_INFO); signing_key = init_key_from_file(fname, 0, LOG_INFO);
if (!signing_key) { if (!signing_key) {
log_warn(LD_DIR, "No version 3 directory key found in %s", fname); log_warn(LD_DIR, "No version 3 directory key found in %s", fname);
goto done; goto done;
} }
tor_free(fname); tor_free(fname);
fname = get_datadir_fname2("keys", "authority_certificate"); fname = get_datadir_fname2("keys",
legacy ? "legacy_certificate" : "authority_certificate");
cert = read_file_to_str(fname, 0, NULL); cert = read_file_to_str(fname, 0, NULL);
if (!cert) { if (!cert) {
log_warn(LD_DIR, "Signing key found, but no certificate found in %s", log_warn(LD_DIR, "Signing key found, but no certificate found in %s",
...@@ -298,18 +313,16 @@ init_v3_authority_keys(void) ...@@ -298,18 +313,16 @@ init_v3_authority_keys(void)
parsed->cache_info.signed_descriptor_len = eos-cert; parsed->cache_info.signed_descriptor_len = eos-cert;
cert = NULL; cert = NULL;
/* Free old values... */ if (*key_out)
if (authority_key_certificate) crypto_free_pk_env(*key_out);
authority_cert_free(authority_key_certificate); if (*cert_out)
if (authority_signing_key) authority_cert_free(*cert_out);
crypto_free_pk_env(authority_signing_key); *key_out = signing_key;
/* ...and replace them. */ *cert_out = parsed;
authority_key_certificate = parsed; r = 0;
authority_signing_key = signing_key;
parsed = NULL;
signing_key = NULL; signing_key = NULL;
parsed = NULL;
r = 0;
done: done:
tor_free(fname); tor_free(fname);
tor_free(cert); tor_free(cert);
...@@ -320,6 +333,24 @@ init_v3_authority_keys(void) ...@@ -320,6 +333,24 @@ init_v3_authority_keys(void)
return r; return r;
} }
/** Load the v3 (voting) authority signing key and certificate, if they are
* present. Return -1 if anything is missing, mismatched, or unloadable;
* return 0 on success. */
static int
init_v3_authority_keys(void)
{
if (load_authority_keyset(0, &authority_signing_key,
&authority_key_certificate)<0)
return -1;
if (get_options()->V3AuthUseLegacyKey &&
load_authority_keyset(0, &legacy_signing_key,
&legacy_key_certificate)<0)
return -1;
return 0;
}
/** If we're a v3 authority, check whether we have a certificate that's /** If we're a v3 authority, check whether we have a certificate that's
* likely to expire soon. Warn if we do, but not too often. */ * likely to expire soon. Warn if we do, but not too often. */
void void
...@@ -1957,6 +1988,10 @@ router_free_all(void) ...@@ -1957,6 +1988,10 @@ router_free_all(void)
crypto_free_pk_env(authority_signing_key); crypto_free_pk_env(authority_signing_key);
if (authority_key_certificate) if (authority_key_certificate)
authority_cert_free(authority_key_certificate); authority_cert_free(authority_key_certificate);
if (legacy_signing_key)
crypto_free_pk_env(legacy_signing_key);
if (legacy_key_certificate)
authority_cert_free(legacy_key_certificate);
if (warned_nonexistent_family) { if (warned_nonexistent_family) {
SMARTLIST_FOREACH(warned_nonexistent_family, char *, cp, tor_free(cp)); SMARTLIST_FOREACH(warned_nonexistent_family, char *, cp, tor_free(cp));
......
...@@ -79,6 +79,7 @@ typedef enum { ...@@ -79,6 +79,7 @@ typedef enum {
K_CONSENSUS_DIGEST, K_CONSENSUS_DIGEST,
K_CONSENSUS_METHODS, K_CONSENSUS_METHODS,
K_CONSENSUS_METHOD, K_CONSENSUS_METHOD,
K_LEGACY_DIR_KEY,
A_PURPOSE, A_PURPOSE,
_A_UNKNOWN, _A_UNKNOWN,
...@@ -365,6 +366,7 @@ static token_rule_t networkstatus_token_table[] = { ...@@ -365,6 +366,7 @@ static token_rule_t networkstatus_token_table[] = {
T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ), T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
T1( "contact", K_CONTACT, CONCAT_ARGS, NO_OBJ ), T1( "contact", K_CONTACT, CONCAT_ARGS, NO_OBJ ),
T1( "dir-source", K_DIR_SOURCE, GE(6), NO_OBJ ), T1( "dir-source", K_DIR_SOURCE, GE(6), NO_OBJ ),
T01("legacy-dir-key", K_LEGACY_DIR_KEY, GE(1), NO_OBJ ),
T1( "known-flags", K_KNOWN_FLAGS, CONCAT_ARGS, NO_OBJ ), T1( "known-flags", K_KNOWN_FLAGS, CONCAT_ARGS, NO_OBJ ),
T01("client-versions", K_CLIENT_VERSIONS, CONCAT_ARGS, NO_OBJ ), T01("client-versions", K_CLIENT_VERSIONS, CONCAT_ARGS, NO_OBJ ),
T01("server-versions", K_SERVER_VERSIONS, CONCAT_ARGS, NO_OBJ ), T01("server-versions", K_SERVER_VERSIONS, CONCAT_ARGS, NO_OBJ ),
...@@ -2282,6 +2284,23 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, ...@@ -2282,6 +2284,23 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
goto err; goto err;
} }
if (is_vote &&
(tok = find_first_by_keyword(tokens, K_LEGACY_DIR_KEY))) {
int bad = 1;
if (strlen(tok->args[0]) == HEX_DIGEST_LEN) {
networkstatus_voter_info_t *voter = smartlist_get(ns->voters, 0);
if (base16_decode(voter->legacy_id_digest, DIGEST_LEN,
tok->args[0], HEX_DIGEST_LEN)<0)
bad = 1;
else
bad = 0;
}
if (bad) {
log_warn(LD_DIR, "Invalid legacy key digest %s on vote.",
escaped(tok->args[0]));
}
}
/* Parse routerstatus lines. */ /* Parse routerstatus lines. */
rs_tokens = smartlist_create(); rs_tokens = smartlist_create();
rs_area = memarea_new(512); rs_area = memarea_new(512);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment