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
This commit is contained in:
parent
b7a80920e2
commit
f3f6ecef48
|
@ -78,10 +78,12 @@ Changes in version 0.2.1.1-alpha - 2008-??-??
|
|||
- Make dumpstats() log the fullness and size of openssl-internal
|
||||
buffers.
|
||||
- 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
|
||||
trust it. Otherwise a 404 error is sent back. Clients use
|
||||
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):
|
||||
- Reject requests for reverse-dns lookup of names in a private
|
||||
|
|
|
@ -301,6 +301,7 @@ static config_var_t _option_vars[] = {
|
|||
V(V3AuthVoteDelay, INTERVAL, "5 minutes"),
|
||||
V(V3AuthDistDelay, INTERVAL, "5 minutes"),
|
||||
V(V3AuthNIntervalsValid, UINT, "3"),
|
||||
V(V3AuthUseLegacyKey, BOOL, "0"),
|
||||
VAR("VersioningAuthoritativeDirectory",BOOL,VersioningAuthoritativeDir, "0"),
|
||||
V(VirtualAddrNetwork, STRING, "127.192.0.0/10"),
|
||||
V(WarnPlaintextPorts, CSV, "23,109,110,143"),
|
||||
|
|
|
@ -2285,6 +2285,13 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_env_t *private_key,
|
|||
voter->or_port = options->ORPort;
|
||||
voter->contact = tor_strdup(contact);
|
||||
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();
|
||||
smartlist_add(v3_out->voters, voter);
|
||||
v3_out->cert = authority_cert_dup(cert);
|
||||
|
|
126
src/or/dirvote.c
126
src/or/dirvote.c
|
@ -105,7 +105,7 @@ format_networkstatus_vote(crypto_pk_env_t *private_signing_key,
|
|||
tor_snprintf(status, len,
|
||||
"network-status-version 3\n"
|
||||
"vote-status vote\n"
|
||||
"consensus-methods 1 2\n"
|
||||
"consensus-methods 1 2 3\n"
|
||||
"published %s\n"
|
||||
"valid-after %s\n"
|
||||
"fresh-until %s\n"
|
||||
|
@ -125,6 +125,14 @@ format_networkstatus_vote(crypto_pk_env_t *private_signing_key,
|
|||
tor_free(flags);
|
||||
outp = status + strlen(status);
|
||||
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);
|
||||
memcpy(outp, cert->cache_info.signed_descriptor_body,
|
||||
cert->cache_info.signed_descriptor_len);
|
||||
|
@ -207,6 +215,12 @@ get_voter(const networkstatus_t *vote)
|
|||
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
|
||||
* hash of their voters' identity digests. */
|
||||
static int
|
||||
|
@ -217,6 +231,19 @@ _compare_votes_by_authority_id(const void **_a, const void **_b)
|
|||
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>
|
||||
* that occurs more than <b>min</b> times. */
|
||||
static void
|
||||
|
@ -416,7 +443,7 @@ compute_consensus_method(smartlist_t *votes)
|
|||
static int
|
||||
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
|
||||
|
@ -581,33 +608,64 @@ networkstatus_compute_consensus(smartlist_t *votes,
|
|||
/* Sort the votes. */
|
||||
smartlist_sort(votes, _compare_votes_by_authority_id);
|
||||
/* Add the authority sections. */
|
||||
SMARTLIST_FOREACH(votes, networkstatus_t *, v,
|
||||
{
|
||||
char buf[1024];
|
||||
struct in_addr in;
|
||||
char ip[INET_NTOA_BUF_LEN];
|
||||
char fingerprint[HEX_DIGEST_LEN+1];
|
||||
char votedigest[HEX_DIGEST_LEN+1];
|
||||
networkstatus_voter_info_t *voter = get_voter(v);
|
||||
smartlist_t *dir_sources = smartlist_create();
|
||||
SMARTLIST_FOREACH(votes, networkstatus_t *, v,
|
||||
{
|
||||
dir_src_ent_t *e = tor_malloc_zero(sizeof(dir_src_ent_t));
|
||||
e->v = 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);
|
||||
tor_inet_ntoa(&in, ip, sizeof(ip));
|
||||
base16_encode(fingerprint, sizeof(fingerprint), voter->identity_digest,
|
||||
DIGEST_LEN);
|
||||
base16_encode(votedigest, sizeof(votedigest), voter->vote_digest,
|
||||
DIGEST_LEN);
|
||||
SMARTLIST_FOREACH(dir_sources, const dir_src_ent_t *, e,
|
||||
{
|
||||
char buf[1024];
|
||||
struct in_addr in;
|
||||
char ip[INET_NTOA_BUF_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);
|
||||
|
||||
tor_snprintf(buf, sizeof(buf),
|
||||
"dir-source %s %s %s %s %d %d\n"
|
||||
"contact %s\n"
|
||||
"vote-digest %s\n",
|
||||
voter->nickname, fingerprint, voter->address, ip,
|
||||
voter->dir_port,
|
||||
voter->or_port,
|
||||
voter->contact,
|
||||
votedigest);
|
||||
smartlist_add(chunks, tor_strdup(buf));
|
||||
});
|
||||
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),
|
||||
"dir-source %s%s %s %s %s %d %d\n",
|
||||
voter->nickname, e->is_legacy ? "-legacy" : "",
|
||||
fingerprint, voter->address, ip,
|
||||
voter->dir_port,
|
||||
voter->or_port);
|
||||
smartlist_add(chunks, tor_strdup(buf));
|
||||
if (! e->is_legacy) {
|
||||
tor_snprintf(buf, sizeof(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. */
|
||||
{
|
||||
|
@ -904,6 +962,22 @@ networkstatus_compute_consensus(smartlist_t *votes,
|
|||
return NULL; /* This leaks, but it should never happen. */
|
||||
}
|
||||
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);
|
||||
|
|
|
@ -1461,6 +1461,7 @@ typedef struct networkstatus_voter_info_t {
|
|||
uint16_t or_port; /**< OR port of this voter */
|
||||
char *contact; /**< Contact information for this voter. */
|
||||
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. */
|
||||
char signing_key_digest[DIGEST_LEN]; /**< Declared digest of signing key
|
||||
|
@ -2339,6 +2340,10 @@ typedef struct {
|
|||
/** The number of intervals we think a consensus should be valid. */
|
||||
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
|
||||
* cached. */
|
||||
char *FallbackNetworkstatusFile;
|
||||
|
@ -3772,6 +3777,8 @@ crypto_pk_env_t *get_identity_key(void);
|
|||
int identity_key_is_set(void);
|
||||
authority_cert_t *get_my_v3_authority_cert(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 rotate_onion_key(void);
|
||||
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;
|
|||
* authorities. */
|
||||
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",
|
||||
* 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
|
||||
|
@ -144,6 +147,18 @@ get_my_v3_authority_signing_key(void)
|
|||
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
|
||||
* a new previous onion key. Immediately after calling this function,
|
||||
* the OR should:
|
||||
|
@ -258,26 +273,26 @@ init_key_from_file(const char *fname, int generate, int severity)
|
|||
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
|
||||
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;
|
||||
const char *eos = NULL;
|
||||
crypto_pk_env_t *signing_key = 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);
|
||||
if (!signing_key) {
|
||||
log_warn(LD_DIR, "No version 3 directory key found in %s", fname);
|
||||
goto done;
|
||||
}
|
||||
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);
|
||||
if (!cert) {
|
||||
log_warn(LD_DIR, "Signing key found, but no certificate found in %s",
|
||||
|
@ -298,18 +313,16 @@ init_v3_authority_keys(void)
|
|||
parsed->cache_info.signed_descriptor_len = eos-cert;
|
||||
cert = NULL;
|
||||
|
||||
/* Free old values... */
|
||||
if (authority_key_certificate)
|
||||
authority_cert_free(authority_key_certificate);
|
||||
if (authority_signing_key)
|
||||
crypto_free_pk_env(authority_signing_key);
|
||||
/* ...and replace them. */
|
||||
authority_key_certificate = parsed;
|
||||
authority_signing_key = signing_key;
|
||||
parsed = NULL;
|
||||
signing_key = NULL;
|
||||
|
||||
if (*key_out)
|
||||
crypto_free_pk_env(*key_out);
|
||||
if (*cert_out)
|
||||
authority_cert_free(*cert_out);
|
||||
*key_out = signing_key;
|
||||
*cert_out = parsed;
|
||||
r = 0;
|
||||
signing_key = NULL;
|
||||
parsed = NULL;
|
||||
|
||||
done:
|
||||
tor_free(fname);
|
||||
tor_free(cert);
|
||||
|
@ -320,6 +333,24 @@ init_v3_authority_keys(void)
|
|||
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
|
||||
* likely to expire soon. Warn if we do, but not too often. */
|
||||
void
|
||||
|
@ -1957,6 +1988,10 @@ router_free_all(void)
|
|||
crypto_free_pk_env(authority_signing_key);
|
||||
if (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) {
|
||||
SMARTLIST_FOREACH(warned_nonexistent_family, char *, cp, tor_free(cp));
|
||||
|
|
|
@ -79,6 +79,7 @@ typedef enum {
|
|||
K_CONSENSUS_DIGEST,
|
||||
K_CONSENSUS_METHODS,
|
||||
K_CONSENSUS_METHOD,
|
||||
K_LEGACY_DIR_KEY,
|
||||
|
||||
A_PURPOSE,
|
||||
_A_UNKNOWN,
|
||||
|
@ -365,6 +366,7 @@ static token_rule_t networkstatus_token_table[] = {
|
|||
T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
|
||||
T1( "contact", K_CONTACT, CONCAT_ARGS, 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 ),
|
||||
T01("client-versions", K_CLIENT_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,
|
|||
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. */
|
||||
rs_tokens = smartlist_create();
|
||||
rs_area = memarea_new(512);
|
||||
|
|
Loading…
Reference in New Issue