|
@@ -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 (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 (crypto_pk_write_private_key_to_filename(prkey, fname)) {
|
|
|
- log_err(LD_FS,"Couldn't write generated onion key to \"%s\".", fname);
|
|
|
+ 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);
|