Wrangle curve25519 onion keys: generate, store, load, publish, republish

Here we try to handle curve25519 onion keys from generating them,
loading and storing them, publishing them in our descriptors, putting
them in microdescriptors, and so on.

This commit is untested and probably buggy like whoa
This commit is contained in:
Nick Mathewson 2012-12-04 15:58:18 -05:00
parent 6c883bc638
commit 5b3dd1610c
9 changed files with 272 additions and 8 deletions

View File

@ -74,7 +74,8 @@ static const struct consensus_method_range_t {
} microdesc_consensus_methods[] = {
{MIN_METHOD_FOR_MICRODESC, MIN_METHOD_FOR_A_LINES - 1},
{MIN_METHOD_FOR_A_LINES, MIN_METHOD_FOR_P6_LINES - 1},
{MIN_METHOD_FOR_P6_LINES, MAX_SUPPORTED_CONSENSUS_METHOD},
{MIN_METHOD_FOR_P6_LINES, MIN_METHOD_FOR_NTOR_KEY - 1},
{MIN_METHOD_FOR_NTOR_KEY, MAX_SUPPORTED_CONSENSUS_METHOD},
{-1, -1}
};

View File

@ -3554,6 +3554,15 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method)
smartlist_add_asprintf(chunks, "onion-key\n%s", key);
if (consensus_method >= MIN_METHOD_FOR_NTOR_KEY &&
ri->onion_curve25519_pkey) {
char kbuf[128];
base64_encode(kbuf, sizeof(kbuf),
(const char*)ri->onion_curve25519_pkey->public_key,
CURVE25519_PUBKEY_LEN);
smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf);
}
if (consensus_method >= MIN_METHOD_FOR_A_LINES &&
!tor_addr_is_null(&ri->ipv6_addr) && ri->ipv6_orport)
smartlist_add_asprintf(chunks, "a %s\n",

View File

@ -20,7 +20,7 @@
#define MIN_VOTE_INTERVAL 300
/** The highest consensus method that we currently support. */
#define MAX_SUPPORTED_CONSENSUS_METHOD 15
#define MAX_SUPPORTED_CONSENSUS_METHOD 16
/** Lowest consensus method that contains a 'directory-footer' marker */
#define MIN_METHOD_FOR_FOOTER 9
@ -48,6 +48,10 @@
/** Lowest consensus method where microdescs may include a "p6" line. */
#define MIN_METHOD_FOR_P6_LINES 15
/** Lowest consensus method where microdescs may include an onion-key-ntor
* line */
#define MIN_METHOD_FOR_NTOR_KEY 16
void dirvote_free_all(void);
/* vote manipulation */

View File

@ -575,6 +575,7 @@ microdesc_free(microdesc_t *md)
if (md->onion_pkey)
crypto_pk_free(md->onion_pkey);
tor_free(md->onion_curve25519_pkey);
if (md->body && md->saved_location != SAVED_IN_CACHE)
tor_free(md->body);

View File

@ -99,6 +99,7 @@
#include "compat_libevent.h"
#include "ht.h"
#include "replaycache.h"
#include "crypto_curve25519.h"
/* These signals are defined to help handle_control_signal work.
*/
@ -1893,6 +1894,8 @@ typedef struct {
crypto_pk_t *onion_pkey; /**< Public RSA key for onions. */
crypto_pk_t *identity_pkey; /**< Public RSA key for signing. */
/** Public curve25519 key for onions */
curve25519_public_key_t *onion_curve25519_pkey;
char *platform; /**< What software/operating system is this OR using? */
@ -2106,6 +2109,8 @@ typedef struct microdesc_t {
/** As routerinfo_t.onion_pkey */
crypto_pk_t *onion_pkey;
/** As routerinfo_t.onion_curve25519_pkey */
curve25519_public_key_t *onion_curve25519_pkey;
/** As routerinfo_t.ipv6_add */
tor_addr_t ipv6_addr;
/** As routerinfo_t.ipv6_orport */

View File

@ -13,6 +13,7 @@
#include "config.h"
#include "connection.h"
#include "control.h"
#include "crypto_curve25519.h"
#include "directory.h"
#include "dirserv.h"
#include "dns.h"
@ -54,6 +55,11 @@ static crypto_pk_t *onionkey=NULL;
/** Previous private onionskin decryption key: used to decode CREATE cells
* generated by clients that have an older version of our descriptor. */
static crypto_pk_t *lastonionkey=NULL;
#ifdef CURVE25519_ENABLED
/**DOCDOC*/
static curve25519_keypair_t curve25519_onion_key;
static curve25519_keypair_t last_curve25519_onion_key;
#endif
/** Private server "identity key": used to sign directory info and TLS
* certificates. Never changes. */
static crypto_pk_t *server_identitykey=NULL;
@ -99,6 +105,20 @@ set_onion_key(crypto_pk_t *k)
mark_my_descriptor_dirty("set onion key");
}
#if 0
/**DOCDOC*/
static void
set_curve25519_onion_key(const curve25519_keypair_t *kp)
{
if (tor_memeq(&curve25519_onion_key, kp, sizeof(curve25519_keypair_t)))
return;
tor_mutex_acquire(key_lock);
memcpy(&curve25519_onion_key, kp, sizeof(curve25519_keypair_t));
tor_mutex_release(key_lock);
}
#endif
/** Return the current onion key. Requires that the onion key has been
* loaded or generated. */
crypto_pk_t *
@ -126,6 +146,47 @@ dup_onion_keys(crypto_pk_t **key, crypto_pk_t **last)
tor_mutex_release(key_lock);
}
#ifdef CURVE25519_ENABLED
/**DOCDOC only in main thread*/
static const curve25519_keypair_t *
get_current_curve25519_keypair(void)
{
return &curve25519_onion_key;
}
di_digest256_map_t *
construct_ntor_key_map(void)
{
di_digest256_map_t *m = NULL;
dimap_add_entry(&m,
curve25519_onion_key.pubkey.public_key,
tor_memdup(&curve25519_onion_key,
sizeof(curve25519_keypair_t)));
if (!tor_mem_is_zero((const char*)
last_curve25519_onion_key.pubkey.public_key,
CURVE25519_PUBKEY_LEN)) {
dimap_add_entry(&m,
last_curve25519_onion_key.pubkey.public_key,
tor_memdup(&last_curve25519_onion_key,
sizeof(curve25519_keypair_t)));
}
return m;
}
static void
ntor_key_map_free_helper(void *arg)
{
curve25519_keypair_t *k = arg;
memwipe(k, 0, sizeof(*k));
tor_free(k);
}
void
ntor_key_map_free(di_digest256_map_t *map)
{
dimap_free(map, ntor_key_map_free_helper);
}
#endif
/** Return the time when the onion key was last set. This is either the time
* when the process launched, or the time of the most recent key rotation since
* the process launched.
@ -253,11 +314,18 @@ void
rotate_onion_key(void)
{
char *fname, *fname_prev;
crypto_pk_t *prkey;
crypto_pk_t *prkey = NULL;
or_state_t *state = get_or_state();
#ifdef CURVE25519_ENABLED
curve25519_keypair_t new_curve25519_keypair;
#endif
time_t now;
fname = get_datadir_fname2("keys", "secret_onion_key");
fname_prev = get_datadir_fname2("keys", "secret_onion_key.old");
if (file_status(fname) == FN_FILE) {
if (replace_file(fname, fname_prev))
goto error;
}
if (!(prkey = crypto_pk_new())) {
log_err(LD_GENERAL,"Error constructing rotated onion key");
goto error;
@ -266,19 +334,37 @@ rotate_onion_key(void)
log_err(LD_BUG,"Error generating onion key");
goto error;
}
if (file_status(fname) == FN_FILE) {
if (replace_file(fname, fname_prev))
goto error;
}
if (crypto_pk_write_private_key_to_filename(prkey, fname)) {
log_err(LD_FS,"Couldn't write generated onion key to \"%s\".", fname);
goto error;
}
#ifdef CURVE25519_ENABLED
tor_free(fname);
tor_free(fname_prev);
fname = get_datadir_fname2("keys", "secret_onion_key_ntor");
fname_prev = get_datadir_fname2("keys", "secret_onion_key_ntor.old");
curve25519_keypair_generate(&new_curve25519_keypair, 1);
if (file_status(fname) == FN_FILE) {
if (replace_file(fname, fname_prev))
goto error;
}
if (curve25519_keypair_write_to_file(&new_curve25519_keypair, fname,
"onion") < 0) {
log_err(LD_FS,"Couldn't write curve25519 onion key to \"%s\".",fname);
goto error;
}
#endif
log_info(LD_GENERAL, "Rotating onion key");
tor_mutex_acquire(key_lock);
crypto_pk_free(lastonionkey);
lastonionkey = onionkey;
onionkey = prkey;
#ifdef CURVE25519_ENABLED
memcpy(&last_curve25519_onion_key, &curve25519_onion_key,
sizeof(curve25519_keypair_t));
memcpy(&curve25519_onion_key, &new_curve25519_keypair,
sizeof(curve25519_keypair_t));
#endif
now = time(NULL);
state->LastRotatedOnionKey = onionkey_set_at = now;
tor_mutex_release(key_lock);
@ -290,6 +376,9 @@ rotate_onion_key(void)
if (prkey)
crypto_pk_free(prkey);
done:
#ifdef CURVE25519_ENABLED
memwipe(&new_curve25519_keypair, 0, sizeof(new_curve25519_keypair));
#endif
tor_free(fname);
tor_free(fname_prev);
}
@ -363,6 +452,72 @@ init_key_from_file(const char *fname, int generate, int severity)
return NULL;
}
#ifdef CURVE25519_ENABLED
/** DOCDOC */
static int
init_curve25519_keypair_from_file(curve25519_keypair_t *keys_out,
const char *fname,
int generate,
int severity,
const char *tag)
{
switch (file_status(fname)) {
case FN_DIR:
case FN_ERROR:
log(severity, LD_FS,"Can't read key from \"%s\"", fname);
goto error;
case FN_NOENT:
if (generate) {
if (!have_lockfile()) {
if (try_locking(get_options(), 0)<0) {
/* Make sure that --list-fingerprint only creates new keys
* if there is no possibility for a deadlock. */
log(severity, LD_FS, "Another Tor process has locked \"%s\". Not "
"writing any new keys.", fname);
/*XXXX The 'other process' might make a key in a second or two;
* maybe we should wait for it. */
goto error;
}
}
log_info(LD_GENERAL, "No key found in \"%s\"; generating fresh key.",
fname);
curve25519_keypair_generate(keys_out, 1);
if (curve25519_keypair_write_to_file(keys_out, fname, tag)<0) {
log(severity, LD_FS,
"Couldn't write generated key to \"%s\".", fname);
memset(keys_out, 0, sizeof(*keys_out));
goto error;
}
} else {
log_info(LD_GENERAL, "No key found in \"%s\"", fname);
}
return 0;
case FN_FILE:
{
char *tag_in=NULL;
if (curve25519_keypair_read_from_file(keys_out, &tag_in, fname) < 0) {
log(severity, LD_GENERAL,"Error loading private key.");
tor_free(tag_in);
goto error;
}
if (!tag_in || strcmp(tag_in, tag)) {
log(severity, LD_GENERAL,"Unexpected tag %s on private key.",
escaped(tag_in));
tor_free(tag_in);
goto error;
}
tor_free(tag_in);
return 0;
}
default:
tor_assert(0);
}
error:
return -1;
}
#endif
/** Try to load the vote-signing private key and certificate for being a v3
* directory authority, and make sure they match. If <b>legacy</b>, load a
* legacy key/cert set for emergency key migration; otherwise load the regular
@ -630,12 +785,35 @@ init_keys(void)
keydir = get_datadir_fname2("keys", "secret_onion_key.old");
if (!lastonionkey && file_status(keydir) == FN_FILE) {
prkey = init_key_from_file(keydir, 1, LOG_ERR);
prkey = init_key_from_file(keydir, 1, LOG_ERR); /* XXXX Why 1? */
if (prkey)
lastonionkey = prkey;
}
tor_free(keydir);
#ifdef CURVE25519_ENABLED
{
/* 2b. Load curve25519 onion keys. */
int r;
keydir = get_datadir_fname2("keys", "secret_onion_key_ntor");
r = init_curve25519_keypair_from_file(&curve25519_onion_key,
keydir, 1, LOG_ERR, "onion");
tor_free(keydir);
if (r<0)
return -1;
keydir = get_datadir_fname2("keys", "secret_onion_key_ntor.old");
if (tor_mem_is_zero((const char *)
last_curve25519_onion_key.pubkey.public_key,
CURVE25519_PUBKEY_LEN) &&
file_status(keydir) == FN_FILE) {
init_curve25519_keypair_from_file(&last_curve25519_onion_key,
keydir, 0, LOG_ERR, "onion");
}
tor_free(keydir);
}
#endif
/* 3. Initialize link key and TLS context. */
if (router_initialize_tls_context() < 0) {
log_err(LD_GENERAL,"Error initializing TLS context");
@ -1566,6 +1744,11 @@ router_rebuild_descriptor(int force)
ri->cache_info.published_on = time(NULL);
ri->onion_pkey = crypto_pk_dup_key(get_onion_key()); /* must invoke from
* main thread */
#ifdef CURVE25519_ENABLED
ri->onion_curve25519_pkey =
tor_memdup(&get_current_curve25519_keypair()->pubkey,
sizeof(curve25519_public_key_t));
#endif
/* For now, at most one IPv6 or-address is being advertised. */
{
@ -2146,6 +2329,22 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
written += result;
}
#ifdef CURVE25519_ENABLED
if (router->onion_curve25519_pkey) {
char kbuf[128];
base64_encode(kbuf, sizeof(kbuf),
(const char *)router->onion_curve25519_pkey->public_key,
CURVE25519_PUBKEY_LEN);
result = tor_snprintf(s+written,maxlen-written, "ntor-onion-key %s",
kbuf);
if (result<0) {
log_warn(LD_BUG,"descriptor snprintf ran out of room!");
return -1;
}
written += result;
}
#endif
/* Write the exit policy to the end of 's'. */
if (!router->exit_policy || !smartlist_len(router->exit_policy)) {
strlcat(s+written, "reject *:*\n", maxlen-written);
@ -2794,6 +2993,11 @@ router_free_all(void)
crypto_pk_free(legacy_signing_key);
authority_cert_free(legacy_key_certificate);
#ifdef CURVE25519_ENABLED
memwipe(&curve25519_onion_key, 0, sizeof(curve25519_onion_key));
memwipe(&last_curve25519_onion_key, 0, sizeof(last_curve25519_onion_key));
#endif
if (warned_nonexistent_family) {
SMARTLIST_FOREACH(warned_nonexistent_family, char *, cp, tor_free(cp));
smartlist_free(warned_nonexistent_family);

View File

@ -30,6 +30,11 @@ crypto_pk_t *init_key_from_file(const char *fname, int generate,
int severity);
void v3_authority_check_key_expiry(void);
#ifdef CURVE25519_ENABLED
di_digest256_map_t *construct_ntor_key_map(void);
void ntor_key_map_free(di_digest256_map_t *map);
#endif
int router_initialize_tls_context(void);
int init_keys(void);

View File

@ -2395,6 +2395,7 @@ routerinfo_free(routerinfo_t *router)
tor_free(router->contact_info);
if (router->onion_pkey)
crypto_pk_free(router->onion_pkey);
tor_free(router->onion_curve25519_pkey);
if (router->identity_pkey)
crypto_pk_free(router->identity_pkey);
if (router->declared_family) {

View File

@ -43,6 +43,7 @@ typedef enum {
K_SIGNED_DIRECTORY,
K_SIGNING_KEY,
K_ONION_KEY,
K_ONION_KEY_NTOR,
K_ROUTER_SIGNATURE,
K_PUBLISHED,
K_RUNNING_ROUTERS,
@ -276,6 +277,7 @@ static token_rule_t routerdesc_token_table[] = {
T01("ipv6-policy", K_IPV6_POLICY, CONCAT_ARGS, NO_OBJ),
T1( "signing-key", K_SIGNING_KEY, NO_ARGS, NEED_KEY_1024 ),
T1( "onion-key", K_ONION_KEY, NO_ARGS, NEED_KEY_1024 ),
T01("ntor-onion-key", K_ONION_KEY_NTOR, GE(1), NO_OBJ ),
T1_END( "router-signature", K_ROUTER_SIGNATURE, NO_ARGS, NEED_OBJ ),
T1( "published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ ),
T01("uptime", K_UPTIME, GE(1), NO_OBJ ),
@ -527,6 +529,7 @@ static token_rule_t networkstatus_detached_signature_token_table[] = {
/** List of tokens recognized in microdescriptors */
static token_rule_t microdesc_token_table[] = {
T1_START("onion-key", K_ONION_KEY, NO_ARGS, NEED_KEY_1024),
T01("ntor-onion-key", K_ONION_KEY_NTOR, GE(1), NO_OBJ ),
T0N("a", K_A, GE(1), NO_OBJ ),
T01("family", K_FAMILY, ARGS, NO_OBJ ),
T01("p", K_P, CONCAT_ARGS, NO_OBJ ),
@ -1516,6 +1519,21 @@ router_parse_entry_from_string(const char *s, const char *end,
router->onion_pkey = tok->key;
tok->key = NULL; /* Prevent free */
if ((tok = find_opt_by_keyword(tokens, K_ONION_KEY_NTOR))) {
uint8_t k[CURVE25519_PUBKEY_LEN+32];
int r;
tor_assert(tok->n_args >= 1);
r = base64_decode((char*)k, sizeof(k), tok->args[0], strlen(tok->args[0]));
if (r != CURVE25519_PUBKEY_LEN) {
log_warn(LD_DIR, "Bogus onion-key-ntor in routerinfo");
goto err;
}
router->onion_curve25519_pkey =
tor_malloc(sizeof(curve25519_public_key_t));
memcpy(router->onion_curve25519_pkey->public_key,
k, CURVE25519_PUBKEY_LEN);
}
tok = find_by_keyword(tokens, K_SIGNING_KEY);
router->identity_pkey = tok->key;
tok->key = NULL; /* Prevent free */
@ -4475,6 +4493,22 @@ microdescs_parse_from_string(const char *s, const char *eos,
md->onion_pkey = tok->key;
tok->key = NULL;
if ((tok = find_opt_by_keyword(tokens, K_ONION_KEY_NTOR))) {
uint8_t k[CURVE25519_PUBKEY_LEN+32];
int r;
tor_assert(tok->n_args >= 1);
r = base64_decode((char*)k, sizeof(k),
tok->args[0], strlen(tok->args[0]));
if (r != CURVE25519_PUBKEY_LEN) {
log_warn(LD_DIR, "Bogus onion-key-ntor in microdesc");
goto next;
}
md->onion_curve25519_pkey =
tor_malloc(sizeof(curve25519_public_key_t));
memcpy(md->onion_curve25519_pkey->public_key,
k, CURVE25519_PUBKEY_LEN);
}
{
smartlist_t *a_lines = find_all_by_keyword(tokens, K_A);
if (a_lines) {