From f362e7a873f4a582e01371f001665eaaf0382434 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 5 Aug 2015 21:09:21 -0400 Subject: [PATCH 01/11] Checkpoint work on ed25519 keygen improvements. Needs changes file, documentation, test integration, more tests. --- src/or/config.c | 39 +++++ src/or/or.h | 7 + src/or/routerkeys.c | 106 +++++++++++-- src/test/test_keygen.sh | 332 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 468 insertions(+), 16 deletions(-) create mode 100755 src/test/test_keygen.sh diff --git a/src/or/config.c b/src/or/config.c index 618c941fb4..d22452bfd4 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -1912,6 +1912,8 @@ static const struct { { "--dump-config", ARGUMENT_OPTIONAL }, { "--list-fingerprint", TAKES_NO_ARGUMENT }, { "--keygen", TAKES_NO_ARGUMENT }, + { "--no-passphrase", TAKES_NO_ARGUMENT }, + { "--passphrase-fd", ARGUMENT_NECESSARY }, { "--verify-config", TAKES_NO_ARGUMENT }, { "--ignore-missing-torrc", TAKES_NO_ARGUMENT }, { "--quiet", TAKES_NO_ARGUMENT }, @@ -4492,6 +4494,43 @@ options_init_from_torrc(int argc, char **argv) retval = options_init_from_string(cf_defaults, cf, command, command_arg, &errmsg); + if (retval < 0) + goto err; + + if (config_line_find(cmdline_only_options, "--no-passphrase")) { + if (command == CMD_KEYGEN) { + get_options_mutable()->keygen_force_passphrase = FORCE_PASSPHRASE_OFF; + } else { + log_err(LD_CONFIG, "--no-passphrase specified without --keygen!"); + exit(1); + } + } + + { + const config_line_t *fd_line = config_line_find(cmdline_only_options, + "--passphrase-fd"); + if (fd_line) { + if (get_options()->keygen_force_passphrase == FORCE_PASSPHRASE_OFF) { + log_err(LD_CONFIG, "--no-passphrase specified with --passphrase-fd!"); + exit(1); + } else if (command != CMD_KEYGEN) { + log_err(LD_CONFIG, "--passphrase-fd specified without --keygen!"); + exit(1); + } else { + const char *v = fd_line->value; + int ok = 1; + long fd = tor_parse_long(v, 10, 0, INT_MAX, &ok, NULL); + if (fd < 0 || ok == 0) { + log_err(LD_CONFIG, "Invalid --passphrase-fd value %s", escaped(v)); + exit(1); + } + get_options_mutable()->keygen_passphrase_fd = (int)fd; + get_options_mutable()->use_keygen_passphrase_fd = 1; + get_options_mutable()->keygen_force_passphrase = FORCE_PASSPHRASE_ON; + } + } + } + err: tor_free(cf); diff --git a/src/or/or.h b/src/or/or.h index f6aee13f5b..96e69bc9ba 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -4292,6 +4292,13 @@ typedef struct { /** How long before auth keys expire will we try to make a new one? */ int TestingAuthKeySlop; + enum { + FORCE_PASSPHRASE_AUTO=0, + FORCE_PASSPHRASE_ON, + FORCE_PASSPHRASE_OFF + } keygen_force_passphrase; + int use_keygen_passphrase_fd; + int keygen_passphrase_fd; } or_options_t; /** Persistent state for an onion router, as saved to disk. */ diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c index 47a6c89534..b15d31bc61 100644 --- a/src/or/routerkeys.c +++ b/src/or/routerkeys.c @@ -11,6 +11,72 @@ #define ENC_KEY_HEADER "Boxed Ed25519 key" #define ENC_KEY_TAG "master" +static ssize_t +do_getpass(const char *prompt, char *buf, size_t buflen, + int twice, const or_options_t *options) +{ + if (options->keygen_force_passphrase == FORCE_PASSPHRASE_OFF) { + tor_assert(buflen); + buf[0] = 0; + return 0; + } + + char *prompt2 = NULL; + char *buf2 = NULL; + int fd = -1; + ssize_t length = -1; + + if (options->use_keygen_passphrase_fd) { + twice = 0; + fd = options->keygen_passphrase_fd; + length = read_all(fd, buf, buflen-1, 0); + if (length >= 0) + buf[length] = 0; + goto done_reading; + } + + if (twice) { + const char msg[] = "One more time:"; + size_t p2len = strlen(prompt) + 1; + if (p2len < sizeof(msg)) + p2len = sizeof(msg); + prompt2 = tor_malloc(strlen(prompt)+1); + memset(prompt2, ' ', p2len); + memcpy(prompt2 + p2len - sizeof(msg), msg, sizeof(msg)); + + buf2 = tor_malloc_zero(buflen); + } + + while (1) { + length = tor_getpass(prompt, buf, buflen); + if (length < 0) + goto done_reading; + + if (! twice) + break; + + ssize_t length2 = tor_getpass(prompt2, buf2, buflen); + + if (length != length2 || tor_memneq(buf, buf2, length)) { + fprintf(stderr, "That didn't match.\n"); + } else { + break; + } + } + + done_reading: + if (twice) { + tor_free(prompt2); + memwipe(buf2, 0, buflen); + tor_free(buf2); + } + + if (options->keygen_force_passphrase == FORCE_PASSPHRASE_ON && length == 0) + return -1; + + return length; +} + int read_encrypted_secret_key(ed25519_secret_key_t *out, const char *fname) @@ -41,22 +107,24 @@ read_encrypted_secret_key(ed25519_secret_key_t *out, while (1) { ssize_t pwlen = - tor_getpass("Enter pasphrase for master key:", pwbuf, sizeof(pwbuf)); + do_getpass("Enter pasphrase for master key:", pwbuf, sizeof(pwbuf), 0, + get_options()); if (pwlen < 0) { saved_errno = EINVAL; goto done; } - const int r = crypto_unpwbox(&secret, &secret_len, encrypted_key, encrypted_len, pwbuf, pwlen); if (r == UNPWBOX_CORRUPTED) { log_err(LD_OR, "%s is corrupted.", fname); + puts("E"); saved_errno = EINVAL; goto done; } else if (r == UNPWBOX_OKAY) { break; } + /* Otherwise, passphrase is bad, so try again till user does ctrl-c or gets * it right. */ } @@ -87,22 +155,23 @@ write_encrypted_secret_key(const ed25519_secret_key_t *key, const char *fname) { int r = -1; - char pwbuf0[256], pwbuf1[256]; + char pwbuf0[256]; uint8_t *encrypted_key = NULL; size_t encrypted_len = 0; - while (1) { - if (tor_getpass("Enter passphrase:", pwbuf0, sizeof(pwbuf0)) < 0) - return -1; - if (tor_getpass(" One more time:", pwbuf1, sizeof(pwbuf1)) < 0) - return -1; - - if (!strcmp(pwbuf0, pwbuf1)) - break; - fprintf(stderr, "That didn't match.\n"); + if (do_getpass("Enter new passphrase:", pwbuf0, sizeof(pwbuf0), 1, + get_options()) < 0) { + log_warn(LD_OR, "NO/failed passphrase"); + return -1; } - if (0 == strlen(pwbuf0)) - return 0; + + if (strlen(pwbuf0) == 0) { + if (get_options()->keygen_force_passphrase == FORCE_PASSPHRASE_ON) + return -1; + else + return 0; + } + if (crypto_pwbox(&encrypted_key, &encrypted_len, key->seckey, sizeof(key->seckey), pwbuf0, strlen(pwbuf0), 0) < 0) { @@ -121,7 +190,6 @@ write_encrypted_secret_key(const ed25519_secret_key_t *key, tor_free(encrypted_key); } memwipe(pwbuf0, 0, sizeof(pwbuf0)); - memwipe(pwbuf1, 0, sizeof(pwbuf1)); return r; } @@ -134,7 +202,9 @@ write_secret_key(const ed25519_secret_key_t *key, int encrypted, if (encrypted) { int r = write_encrypted_secret_key(key, encrypted_fname); if (r != 0) - return r; + return r; /* Either succeeded or failed unrecoverably */ + + fprintf(stderr, "Not encrypting the secret key.\n"); } return ed25519_seckey_write_to_file(key, fname, fname_tag); } @@ -628,6 +698,9 @@ load_ed_keys(const or_options_t *options, time_t now) * it, if we loaded it in the first place. */ memwipe(id->seckey.seckey, 0, sizeof(id->seckey)); + if (options->command == CMD_KEYGEN) + goto end; + if (!rsa_ed_crosscert && server_mode(options)) { uint8_t *crosscert; ssize_t crosscert_len = tor_make_rsa_ed25519_crosscert(&id->pubkey, @@ -651,6 +724,7 @@ load_ed_keys(const or_options_t *options, time_t now) /* We've generated or loaded everything. Put them in memory. */ + end: if (! master_identity_key) { SET_KEY(master_identity_key, id); } else { diff --git a/src/test/test_keygen.sh b/src/test/test_keygen.sh new file mode 100755 index 0000000000..7c97a7d2d3 --- /dev/null +++ b/src/test/test_keygen.sh @@ -0,0 +1,332 @@ +#!/bin/sh + +# Note: some of this code is lifted from zero_length_keys.sh, and could be +# unified. + +umask 077 +set -e + +if [ $# -eq 0 ] || [ ! -f ${1} ] || [ ! -x ${1} ]; then + echo "Usage: ${0} PATH_TO_TOR [case-number]" + exit 1 +elif [ $# -ge 1 ]; then + TOR_BINARY="${1}" + shift + + if [ $# -ge 1 ]; then + dflt=0 + else + dflt=1 + fi + + CASE2A=$dflt + CASE2B=$dflt + CASE3A=$dflt + CASE3B=$dflt + CASE3C=$dflt + CASE4=$dflt + CASE5=$dflt + CASE6=$dflt + CASE7=$dflt + CASE8=$dflt + CASE9=$dflt + CASE10=$dflt + + if [ $# -ge 1 ]; then + eval "CASE${1}"=1 + fi +fi + +die() { echo "$1" >&2 ; exit 5; } +check_dir() { [ -d "$1" ] || die "$1 did not exist"; } +check_file() { [ -e "$1" ] || die "$1 did not exist"; } +check_no_file() { [ -e "$1" ] && die "$1 was not supposed to exist" || true; } +check_files_eq() { cmp "$1" "$2" || die "$1 and $2 did not match"; } +check_keys_eq() { check_files_eq "${SRC}/keys/${1}" "${ME}/keys/${1}"; } + +DATA_DIR=`mktemp -d -t tor_keygen_tests.XXXXXX` +if [ -z "$DATA_DIR" ]; then + echo "Failure: mktemp invocation returned empty string" >&2 + exit 3 +fi +if [ ! -d "$DATA_DIR" ]; then + echo "Failure: mktemp invocation result doesn't point to directory" >&2 + exit 3 +fi +trap "rm -rf '$DATA_DIR'" 0 + +touch "${DATA_DIR}/empty_torrc" + +TOR="${TOR_BINARY} --hush --DisableNetwork 1 --ShutdownWaitLength 0 --ORPort 12345 --ExitRelay 0 -f ${DATA_DIR}/empty_torrc" + +# Step 1: Start Tor with --list-fingerprint. Make sure everything is there. +mkdir "${DATA_DIR}/orig" +${TOR} --DataDirectory "${DATA_DIR}/orig" --list-fingerprint > /dev/null + +check_dir "${DATA_DIR}/orig/keys" +check_file "${DATA_DIR}/orig/keys/ed25519_master_id_public_key" +check_file "${DATA_DIR}/orig/keys/ed25519_master_id_secret_key" +check_file "${DATA_DIR}/orig/keys/ed25519_signing_cert" +check_file "${DATA_DIR}/orig/keys/ed25519_signing_secret_key" + +# Step 2: Start Tor with --keygen. Make sure everything is there. +mkdir "${DATA_DIR}/keygen" +${TOR} --DataDirectory "${DATA_DIR}/keygen" --keygen --no-passphrase + +check_dir "${DATA_DIR}/keygen/keys" +check_file "${DATA_DIR}/keygen/keys/ed25519_master_id_public_key" +check_file "${DATA_DIR}/keygen/keys/ed25519_master_id_secret_key" +check_file "${DATA_DIR}/keygen/keys/ed25519_signing_cert" +check_file "${DATA_DIR}/keygen/keys/ed25519_signing_secret_key" + +# Step 3: Start Tor with --keygen and a passphrase. +# Make sure everything is there. +mkdir "${DATA_DIR}/encrypted" +echo "passphrase" | ${TOR} --DataDirectory "${DATA_DIR}/encrypted" --keygen --passphrase-fd 0 + +check_dir "${DATA_DIR}/encrypted/keys" +check_file "${DATA_DIR}/encrypted/keys/ed25519_master_id_public_key" +check_file "${DATA_DIR}/encrypted/keys/ed25519_master_id_secret_key_encrypted" +check_file "${DATA_DIR}/encrypted/keys/ed25519_signing_cert" +check_file "${DATA_DIR}/encrypted/keys/ed25519_signing_secret_key" + + +echo "KEY GENERATION WAS SUCCESSFUL." + +# +# The "case X" numbers below come from s7r's email on +# https://lists.torproject.org/pipermail/tor-dev/2015-August/009204.html + + +# Case 2a: Missing secret key, public key exists, start tor. + +if [ "$CASE2A" = 1 ]; then + +ME="${DATA_DIR}/case2a" +SRC="${DATA_DIR}/orig" +mkdir -p "${ME}/keys" +cp "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/" +${TOR} --DataDirectory "${ME}" --list-fingerprint && die "Somehow succeeded when missing secret key, certs" || true +check_files_eq "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/ed25519_master_id_public_key" + +echo "==== Case 2A ok" +fi + +# Case 2b: Encrypted secret key, public key exists, start tor. + +if [ "$CASE2B" = 1 ]; then + +ME="${DATA_DIR}/case2b" +SRC="${DATA_DIR}/encrypted" + +mkdir -p "${ME}/keys" +cp "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/" +cp "${SRC}/keys/ed25519_master_id_secret_key_encrypted" "${ME}/keys/" +${TOR} --DataDirectory "${ME}" --list-fingerprint && dir "Somehow succeeded with encrypted secret key, missing certs" + +check_files_eq "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/ed25519_master_id_public_key" +check_files_eq "${SRC}/keys/ed25519_master_id_secret_key_encrypted" "${ME}/keys/ed25519_master_id_secret_key_encrypted" + +echo "==== Case 2B ok" + +fi + +# Case 3a: Start Tor with only master key. + +if [ "$CASE3A" = 1 ]; then + +ME="${DATA_DIR}/case3a" +SRC="${DATA_DIR}/orig" + +mkdir -p "${ME}/keys" +cp "${SRC}/keys/ed25519_master_id_"* "${ME}/keys/" +${TOR} --DataDirectory "${ME}" --list-fingerprint || die "Tor failed when starting with only master key" +check_files_eq "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/ed25519_master_id_public_key" +check_files_eq "${SRC}/keys/ed25519_master_id_secret_key" "${ME}/keys/ed25519_master_id_secret_key" +check_file "${ME}/keys/ed25519_signing_cert" +check_file "${ME}/keys/ed25519_signing_secret_key" + +echo "==== Case 3A ok" + +fi + +# Case 3b: Call keygen with only unencrypted master key. + +if [ "$CASE3B" = 1 ]; then + +ME="${DATA_DIR}/case3b" +SRC="${DATA_DIR}/orig" + +mkdir -p "${ME}/keys" +cp "${SRC}/keys/ed25519_master_id_"* "${ME}/keys/" +${TOR} --DataDirectory "${ME}" --keygen || die "Keygen failed with only master key" +check_files_eq "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/ed25519_master_id_public_key" +check_files_eq "${SRC}/keys/ed25519_master_id_secret_key" "${ME}/keys/ed25519_master_id_secret_key" +check_file "${ME}/keys/ed25519_signing_cert" +check_file "${ME}/keys/ed25519_signing_secret_key" + +echo "==== Case 3B ok" + +fi + +# Case 3c: Call keygen with only encrypted master key. + +if [ "$CASE3C" = 1 ]; then + +ME="${DATA_DIR}/case3c" +SRC="${DATA_DIR}/encrypted" + +mkdir -p "${ME}/keys" +cp "${SRC}/keys/ed25519_master_id_"* "${ME}/keys/" +echo "passphrase" | ${TOR} --DataDirectory "${ME}" --keygen --passphrase-fd 0 || die "Keygen failed with only encrypted master key" +check_files_eq "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/ed25519_master_id_public_key" +check_files_eq "${SRC}/keys/ed25519_master_id_secret_key_encrypted" "${ME}/keys/ed25519_master_id_secret_key_encrypted" +check_file "${ME}/keys/ed25519_signing_cert" +check_file "${ME}/keys/ed25519_signing_secret_key" + +echo "==== Case 3C ok" + +fi + +# Case 4: Make a new data directory with only an unencrypted secret key. +# Then start tor. The rest should become correct. + +if [ "$CASE4" = 1 ]; then + +ME="${DATA_DIR}/case4" +SRC="${DATA_DIR}/orig" + +mkdir -p "${ME}/keys" +cp "${SRC}/keys/ed25519_master_id_secret_key" "${ME}/keys/" +${TOR} --DataDirectory "${ME}" --list-fingerprint || die "Tor wouldn't start with only unencrypted secret key" +check_file "${ME}/keys/ed25519_master_id_public_key" +check_file "${ME}/keys/ed25519_master_id_signing_cert" +check_file "${ME}/keys/ed25519_master_id_signing_secret_key" +${TOR} --DataDirectory "${ME}" --list-fingerprint || die "Tor wouldn't start again after starting once with only unencrypted secret key." + +echo "==== Case 4 ok" + +fi + +# Case 5: Make a new data directory with only an encrypted secret key. + +if [ "$CASE5" = 1 ]; then + +ME="${DATA_DIR}/case5" +SRC="${DATA_DIR}/encrypted" + +mkdir -p "${ME}/keys" +cp "${SRC}/keys/ed25519_master_id_secret_key_encrypted" "${ME}/keys/" +${TOR} --DataDirectory "${ME}" --list-fingerprint || die "Tor wouldn't start with only encrypted secret key" +check_files_eq "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/ed25519_master_id_public_key" + +echo "==== Case 5 ok" + +fi + +# Case 6: Make a new data directory with encrypted secret key and public key + +if [ "$CASE6" = 1 ]; then + +ME="${DATA_DIR}/case6" +SRC="${DATA_DIR}/encrypted" + +mkdir -p "${ME}/keys" +cp "${SRC}/keys/ed25519_master_id_secret_key_encrypted" "${ME}/keys/" +cp "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/" +${TOR} --DataDirectory "${ME}" --list-fingerprint && die "Tor started with encrypted secret key and no certs" || true +check_no_file "${ME}/keys/ed25519_signing_cert" +check_no_file "${ME}/keys/ed25519_signing_secret_key" + +echo "==== Case 6 ok" + +fi + +# Case 7: Make a new data directory with unencrypted secret key and +# certificates; missing master public. + +if [ "$CASE7" = 1 ]; then + +ME="${DATA_DIR}/case7" +SRC="${DATA_DIR}/keygen" + +mkdir -p "${ME}/keys" +cp "${SRC}/keys/ed25519_master_id_secret_key" "${ME}/keys/" +cp "${SRC}/keys/ed25519_signing_cert" "${ME}/keys/" +cp "${SRC}/keys/ed25519_signing_secret_key" "${ME}/keys/" + +${TOR} --DataDirectory "${ME}" --list-fingerprint || die "Failed when starting with missing public key" +check_keys_eq ed25519_master_id_secret_key +check_keys_eq ed25519_master_id_public_key +check_keys_eq ed25519_signing_secret_key +check_keys_eq ed25519_signing_cert + +echo "==== Case 7 ok" + +fi + +# Case 8: offline master secret key. + +if [ "$CASE8" = 1 ]; then + +ME="${DATA_DIR}/case8" +SRC="${DATA_DIR}/keygen" + +mkdir -p "${ME}/keys" +cp "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/" +cp "${SRC}/keys/ed25519_signing_cert" "${ME}/keys/" +cp "${SRC}/keys/ed25519_signing_secret_key" "${ME}/keys/" + +${TOR} --DataDirectory "${ME}" --list-fingerprint || die "Failed when starting with offline secret key" +check_no_file "${ME}/keys/ed25519_master_id_secret_key" +check_keys_eq ed25519_master_id_public_key +check_keys_eq ed25519_signing_secret_key +check_keys_eq ed25519_signing_cert + +echo "==== Case 8 ok" + +fi + +# Case 9: signing cert and secret key provided; could infer master key. + +if [ "$CASE9" = 1 ]; then + +ME="${DATA_DIR}/case9" +SRC="${DATA_DIR}/keygen" + +mkdir -p "${ME}/keys" +cp "${SRC}/keys/ed25519_signing_cert" "${ME}/keys/" +cp "${SRC}/keys/ed25519_signing_secret_key" "${ME}/keys/" + +${TOR} --DataDirectory "${ME}" --list-fingerprint || die "Failed when starting with only signing material" +check_no_file "${ME}/keys/ed25519_master_id_secret_key" +check_keys_eq ed25519_master_id_public_key +check_keys_eq ed25519_signing_secret_key +check_keys_eq ed25519_signing_cert + +echo "==== Case 9 ok" + +fi + + +# Case 10: key mismatch. + +if [ "$CASE10" = 1 ]; then + +ME="${DATA_DIR}/case10" +SRC="${DATA_DIR}/keygen" +OTHER="${DATA_DIR}/orig" + +mkdir -p "${ME}/keys" +cp "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/" +cp "${OTHER}/keys/ed25519_master_id_secret_key" "${ME}/keys/" + +${TOR} --DataDirectory "${ME}" --list-fingerprint && die "Successfully started with mismatched keys!?" || true + +echo "==== Case 10 ok" + +fi + + +# Check cert-only. + From 8f6f1544c900ec12d0dd2710a7daf13713f063d4 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 6 Aug 2015 11:12:44 -0400 Subject: [PATCH 02/11] Resolve failing test_keygen tests. --- src/or/main.c | 2 +- src/or/routerkeys.c | 66 +++++++++++++++++++++++++++++++++-------- src/test/test_keygen.sh | 21 +++++++------ 3 files changed, 67 insertions(+), 22 deletions(-) diff --git a/src/or/main.c b/src/or/main.c index 5bff82b3cf..90f78b6576 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -2830,7 +2830,7 @@ do_list_fingerprint(void) } tor_assert(nickname); if (init_keys() < 0) { - log_err(LD_BUG,"Error initializing keys; can't display fingerprint"); + log_err(LD_GENERAL,"Error initializing keys; exiting."); return -1; } if (!(k = get_server_identity_key())) { diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c index b15d31bc61..478f6c719a 100644 --- a/src/or/routerkeys.c +++ b/src/or/routerkeys.c @@ -118,7 +118,6 @@ read_encrypted_secret_key(ed25519_secret_key_t *out, pwbuf, pwlen); if (r == UNPWBOX_CORRUPTED) { log_err(LD_OR, "%s is corrupted.", fname); - puts("E"); saved_errno = EINVAL; goto done; } else if (r == UNPWBOX_OKAY) { @@ -245,7 +244,8 @@ write_secret_key(const ed25519_secret_key_t *key, int encrypted, * and consider encrypting any new secret key. * * If INIT_ED_KEY_NO_REPAIR is set, and there is any issue loading the keys - * from disk _other than their absence_, we do not try to replace them. + * from disk _other than their absence_ (full or partial), we do not try to + * replace them. */ ed25519_keypair_t * ed_key_init_from_file(const char *fname, uint32_t flags, @@ -266,6 +266,7 @@ ed_key_init_from_file(const char *fname, uint32_t flags, const int encrypt_key = !! (flags & INIT_ED_KEY_TRY_ENCRYPTED); const int norepair = !! (flags & INIT_ED_KEY_NO_REPAIR); const int split = !! (flags & INIT_ED_KEY_SPLIT); + const int omit_secret = !! (flags & INIT_ED_KEY_OMIT_SECRET); /* we don't support setting both of these flags at once. */ tor_assert((flags & (INIT_ED_KEY_NO_REPAIR|INIT_ED_KEY_NEEDCERT)) != @@ -285,8 +286,7 @@ ed_key_init_from_file(const char *fname, uint32_t flags, /* Try to read the secret key. */ int have_secret = 0; - if (try_to_load && - !(flags & INIT_ED_KEY_OMIT_SECRET)) { + if (try_to_load && (!omit_secret || file_status(public_fname)==FN_NOENT )) { int rv = ed25519_seckey_read_from_file(&keypair->seckey, &got_tag, secret_fname); if (rv == 0) { @@ -302,18 +302,26 @@ ed_key_init_from_file(const char *fname, uint32_t flags, } /* Should we try for an encrypted key? */ + int have_encrypted_secret_file = 0; if (!have_secret && try_to_load && encrypt_key) { int r = read_encrypted_secret_key(&keypair->seckey, encrypted_secret_fname); if (r > 0) { have_secret = 1; + have_encrypted_secret_file = 1; got_tag = tor_strdup(tag); loaded_secret_fname = encrypted_secret_fname; } else if (errno != ENOENT && norepair) { - tor_log(severity, LD_OR, "Unable to read %s: %s", encrypted_secret_fname, - strerror(errno)); + tor_log(severity, LD_OR, "Unable to read %s: %s", + encrypted_secret_fname, strerror(errno)); goto err; } + } else { + if (try_to_load) { + /* Check if it's there anyway, so we don't replace it. */ + if (file_status(encrypted_secret_fname) != FN_NOENT) + have_encrypted_secret_file = 1; + } } if (have_secret) { @@ -329,10 +337,9 @@ ed_key_init_from_file(const char *fname, uint32_t flags, } } - /* If it's absent and that's okay, or if we do split keys here, try to re - * the pubkey. */ + /* If we do split keys here, try to read the pubkey. */ int found_public = 0; - if ((!have_secret && try_to_load) || (have_secret && split)) { + if (try_to_load && (!have_secret || split)) { ed25519_public_key_t pubkey_tmp; tor_free(got_tag); found_public = ed25519_pubkey_read_from_file(&pubkey_tmp, @@ -356,9 +363,23 @@ ed_key_init_from_file(const char *fname, uint32_t flags, goto err; } } else { + /* We only have the public key; better use that. */ tor_assert(split); memcpy(&keypair->pubkey, &pubkey_tmp, sizeof(pubkey_tmp)); } + } else { + /* We have no public key file, but we do have a secret key, make the + * public key file! */ + if (have_secret) { + if (ed25519_pubkey_write_to_file(&keypair->pubkey, public_fname, tag) + < 0) { + tor_log(severity, LD_OR, "Couldn't repair %s", public_fname); + goto err; + } else { + tor_log(severity, LD_OR, "Found secret key but not %s. Regenerating.", + public_fname); + } + } } } @@ -370,6 +391,15 @@ ed_key_init_from_file(const char *fname, uint32_t flags, if (!have_secret && !found_public && !(flags & INIT_ED_KEY_CREATE)) goto err; + /* If the secret key is absent, but the encrypted key would be present, + * that's an error */ + if (!have_secret && !found_public && have_encrypted_secret_file) { + tor_assert(!encrypt_key); + tor_log(severity, LD_OR, "Found an encrypted secret key, " + "but not public key file %s!", public_fname); + goto err; + } + /* if it's absent, make a new keypair and save it. */ if (!have_secret && !found_public) { tor_free(keypair); @@ -610,8 +640,10 @@ load_ed_keys(const or_options_t *options, time_t now) { uint32_t flags = - (INIT_ED_KEY_CREATE|INIT_ED_KEY_SPLIT| + (INIT_ED_KEY_SPLIT| INIT_ED_KEY_EXTRA_STRONG|INIT_ED_KEY_NO_REPAIR); + if (! use_signing) + flags |= INIT_ED_KEY_CREATE; if (! need_new_signing_key) flags |= INIT_ED_KEY_MISSING_SECRET_OK; if (! want_new_signing_key) @@ -638,8 +670,18 @@ load_ed_keys(const or_options_t *options, time_t now) flags, LOG_WARN, NULL, 0, 0, 0, NULL); tor_free(fname); - if (!id) - FAIL("Missing identity key"); + if (!id) { + if (need_new_signing_key) { + FAIL("Missing identity key"); + } else { + log_warn(LD_OR, "master public key was absent; inferring from " + "public key in signing certificate"); + tor_assert(check_signing_cert); + id = tor_malloc_zero(sizeof(*id)); + memcpy(&id->pubkey, &check_signing_cert->signing_key, + sizeof(ed25519_public_key_t)); + } + } if (tor_mem_is_zero((char*)id->seckey.seckey, sizeof(id->seckey))) sign_signing_key_with_id = NULL; else diff --git a/src/test/test_keygen.sh b/src/test/test_keygen.sh index 7c97a7d2d3..469b0bad32 100755 --- a/src/test/test_keygen.sh +++ b/src/test/test_keygen.sh @@ -57,7 +57,8 @@ trap "rm -rf '$DATA_DIR'" 0 touch "${DATA_DIR}/empty_torrc" -TOR="${TOR_BINARY} --hush --DisableNetwork 1 --ShutdownWaitLength 0 --ORPort 12345 --ExitRelay 0 -f ${DATA_DIR}/empty_torrc" +QUIETLY="--hush" +TOR="${TOR_BINARY} ${QUIETLY} --DisableNetwork 1 --ShutdownWaitLength 0 --ORPort 12345 --ExitRelay 0 -f ${DATA_DIR}/empty_torrc" # Step 1: Start Tor with --list-fingerprint. Make sure everything is there. mkdir "${DATA_DIR}/orig" @@ -71,7 +72,8 @@ check_file "${DATA_DIR}/orig/keys/ed25519_signing_secret_key" # Step 2: Start Tor with --keygen. Make sure everything is there. mkdir "${DATA_DIR}/keygen" -${TOR} --DataDirectory "${DATA_DIR}/keygen" --keygen --no-passphrase +${TOR} --DataDirectory "${DATA_DIR}/keygen" --keygen --no-passphrase 2>"${DATA_DIR}/keygen/stderr" +grep "Not encrypting the secret key" "${DATA_DIR}/keygen/stderr" >/dev/null || die "Tor didn't declare that there would be no encryption" check_dir "${DATA_DIR}/keygen/keys" check_file "${DATA_DIR}/keygen/keys/ed25519_master_id_public_key" @@ -91,7 +93,7 @@ check_file "${DATA_DIR}/encrypted/keys/ed25519_signing_cert" check_file "${DATA_DIR}/encrypted/keys/ed25519_signing_secret_key" -echo "KEY GENERATION WAS SUCCESSFUL." +echo "=== Starting tests." # # The "case X" numbers below come from s7r's email on @@ -200,8 +202,8 @@ mkdir -p "${ME}/keys" cp "${SRC}/keys/ed25519_master_id_secret_key" "${ME}/keys/" ${TOR} --DataDirectory "${ME}" --list-fingerprint || die "Tor wouldn't start with only unencrypted secret key" check_file "${ME}/keys/ed25519_master_id_public_key" -check_file "${ME}/keys/ed25519_master_id_signing_cert" -check_file "${ME}/keys/ed25519_master_id_signing_secret_key" +check_file "${ME}/keys/ed25519_signing_cert" +check_file "${ME}/keys/ed25519_signing_secret_key" ${TOR} --DataDirectory "${ME}" --list-fingerprint || die "Tor wouldn't start again after starting once with only unencrypted secret key." echo "==== Case 4 ok" @@ -217,8 +219,9 @@ SRC="${DATA_DIR}/encrypted" mkdir -p "${ME}/keys" cp "${SRC}/keys/ed25519_master_id_secret_key_encrypted" "${ME}/keys/" -${TOR} --DataDirectory "${ME}" --list-fingerprint || die "Tor wouldn't start with only encrypted secret key" -check_files_eq "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/ed25519_master_id_public_key" +${TOR} --DataDirectory "${ME}" --list-fingerprint && die "Tor started with only encrypted secret key!" +check_no_file "${ME}/keys/ed25519_master_id_public_key" +check_no_file "${ME}/keys/ed25519_master_id_public_key" echo "==== Case 5 ok" @@ -300,7 +303,7 @@ cp "${SRC}/keys/ed25519_signing_secret_key" "${ME}/keys/" ${TOR} --DataDirectory "${ME}" --list-fingerprint || die "Failed when starting with only signing material" check_no_file "${ME}/keys/ed25519_master_id_secret_key" -check_keys_eq ed25519_master_id_public_key +check_no_file "${ME}/keys/ed25519_master_id_public_key" check_keys_eq ed25519_signing_secret_key check_keys_eq ed25519_signing_cert @@ -309,7 +312,7 @@ echo "==== Case 9 ok" fi -# Case 10: key mismatch. +# Case 10: master key mismatch. if [ "$CASE10" = 1 ]; then From 4ce0b7e9163e255f2d71fb7111f032ca25acf22a Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 6 Aug 2015 11:27:25 -0400 Subject: [PATCH 03/11] Integreate test_keygen.sh into 'make check'. --- Makefile.am | 2 ++ src/test/include.am | 2 +- src/test/test_keygen.sh | 17 +++++++++++++---- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Makefile.am b/Makefile.am index 5b4d59c514..8f6b38aa0f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -25,6 +25,8 @@ include src/include.am include doc/include.am include contrib/include.am +export TESTING_TOR_BINARY + EXTRA_DIST+= \ ChangeLog \ INSTALL \ diff --git a/src/test/include.am b/src/test/include.am index 7406c84a1f..3fe4f50edf 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -1,5 +1,5 @@ -TESTSCRIPTS = src/test/test_zero_length_keys.sh +TESTSCRIPTS = src/test/test_zero_length_keys.sh src/test/test_keygen.sh if USEPYTHON TESTSCRIPTS += src/test/test_ntor.sh src/test/test_bt.sh diff --git a/src/test/test_keygen.sh b/src/test/test_keygen.sh index 469b0bad32..1f1cef5f02 100755 --- a/src/test/test_keygen.sh +++ b/src/test/test_keygen.sh @@ -7,11 +7,20 @@ umask 077 set -e if [ $# -eq 0 ] || [ ! -f ${1} ] || [ ! -x ${1} ]; then - echo "Usage: ${0} PATH_TO_TOR [case-number]" - exit 1 -elif [ $# -ge 1 ]; then + if [ "$TESTING_TOR_BINARY" = ""] ; then + echo "Usage: ${0} PATH_TO_TOR [case-number]" + exit 1 + fi +fi + +if [ $# -ge 1 ]; then TOR_BINARY="${1}" shift +else + TOR_BINARY="${TESTING_TOR_BINARY}" +fi + + if [ $# -ge 1 ]; then dflt=0 @@ -35,7 +44,7 @@ elif [ $# -ge 1 ]; then if [ $# -ge 1 ]; then eval "CASE${1}"=1 fi -fi + die() { echo "$1" >&2 ; exit 5; } check_dir() { [ -d "$1" ] || die "$1 did not exist"; } From a1e543f908f058a3a665cd4736d95a6769f300a8 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 6 Aug 2015 11:30:04 -0400 Subject: [PATCH 04/11] Suppress fingerprint display in test_keygen.sh --- src/test/test_keygen.sh | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/test/test_keygen.sh b/src/test/test_keygen.sh index 1f1cef5f02..50d54aaa63 100755 --- a/src/test/test_keygen.sh +++ b/src/test/test_keygen.sh @@ -151,7 +151,7 @@ SRC="${DATA_DIR}/orig" mkdir -p "${ME}/keys" cp "${SRC}/keys/ed25519_master_id_"* "${ME}/keys/" -${TOR} --DataDirectory "${ME}" --list-fingerprint || die "Tor failed when starting with only master key" +${TOR} --DataDirectory "${ME}" --list-fingerprint >/dev/null || die "Tor failed when starting with only master key" check_files_eq "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/ed25519_master_id_public_key" check_files_eq "${SRC}/keys/ed25519_master_id_secret_key" "${ME}/keys/ed25519_master_id_secret_key" check_file "${ME}/keys/ed25519_signing_cert" @@ -209,11 +209,13 @@ SRC="${DATA_DIR}/orig" mkdir -p "${ME}/keys" cp "${SRC}/keys/ed25519_master_id_secret_key" "${ME}/keys/" -${TOR} --DataDirectory "${ME}" --list-fingerprint || die "Tor wouldn't start with only unencrypted secret key" +${TOR} --DataDirectory "${ME}" --list-fingerprint > "${ME}/fp1" || die "Tor wouldn't start with only unencrypted secret key" check_file "${ME}/keys/ed25519_master_id_public_key" check_file "${ME}/keys/ed25519_signing_cert" check_file "${ME}/keys/ed25519_signing_secret_key" -${TOR} --DataDirectory "${ME}" --list-fingerprint || die "Tor wouldn't start again after starting once with only unencrypted secret key." +${TOR} --DataDirectory "${ME}" --list-fingerprint > "${ME}/fp2" || die "Tor wouldn't start again after starting once with only unencrypted secret key." + +check_files_eq "${ME}/fp1" "${ME}/fp2" echo "==== Case 4 ok" @@ -267,7 +269,7 @@ cp "${SRC}/keys/ed25519_master_id_secret_key" "${ME}/keys/" cp "${SRC}/keys/ed25519_signing_cert" "${ME}/keys/" cp "${SRC}/keys/ed25519_signing_secret_key" "${ME}/keys/" -${TOR} --DataDirectory "${ME}" --list-fingerprint || die "Failed when starting with missing public key" +${TOR} --DataDirectory "${ME}" --list-fingerprint >/dev/null || die "Failed when starting with missing public key" check_keys_eq ed25519_master_id_secret_key check_keys_eq ed25519_master_id_public_key check_keys_eq ed25519_signing_secret_key @@ -289,7 +291,7 @@ cp "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/" cp "${SRC}/keys/ed25519_signing_cert" "${ME}/keys/" cp "${SRC}/keys/ed25519_signing_secret_key" "${ME}/keys/" -${TOR} --DataDirectory "${ME}" --list-fingerprint || die "Failed when starting with offline secret key" +${TOR} --DataDirectory "${ME}" --list-fingerprint >/dev/null || die "Failed when starting with offline secret key" check_no_file "${ME}/keys/ed25519_master_id_secret_key" check_keys_eq ed25519_master_id_public_key check_keys_eq ed25519_signing_secret_key @@ -310,7 +312,7 @@ mkdir -p "${ME}/keys" cp "${SRC}/keys/ed25519_signing_cert" "${ME}/keys/" cp "${SRC}/keys/ed25519_signing_secret_key" "${ME}/keys/" -${TOR} --DataDirectory "${ME}" --list-fingerprint || die "Failed when starting with only signing material" +${TOR} --DataDirectory "${ME}" --list-fingerprint >/dev/null || die "Failed when starting with only signing material" check_no_file "${ME}/keys/ed25519_master_id_secret_key" check_no_file "${ME}/keys/ed25519_master_id_public_key" check_keys_eq ed25519_signing_secret_key From 138b28dc35c96ad4d8206a1e78a75a1072df0aa4 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 6 Aug 2015 11:57:00 -0400 Subject: [PATCH 05/11] Add test_keygen tests for all log outputs; improve keygen outputs. --- src/or/routerkeys.c | 34 ++++++++++++++++++++++++++++------ src/test/test_keygen.sh | 20 +++++++++++++++----- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c index 478f6c719a..125fe7545d 100644 --- a/src/or/routerkeys.c +++ b/src/or/routerkeys.c @@ -376,7 +376,8 @@ ed_key_init_from_file(const char *fname, uint32_t flags, tor_log(severity, LD_OR, "Couldn't repair %s", public_fname); goto err; } else { - tor_log(severity, LD_OR, "Found secret key but not %s. Regenerating.", + tor_log(LOG_NOTICE, LD_OR, + "Found secret key but not %s. Regenerating.", public_fname); } } @@ -384,12 +385,29 @@ ed_key_init_from_file(const char *fname, uint32_t flags, } /* If the secret key is absent and it's not allowed to be, fail. */ - if (!have_secret && found_public && !(flags & INIT_ED_KEY_MISSING_SECRET_OK)) + if (!have_secret && found_public && + !(flags & INIT_ED_KEY_MISSING_SECRET_OK)) { + if (have_encrypted_secret_file) { + tor_log(severity, LD_OR, "We needed to load a secret key from %s, " + "but it was encrypted. Try tor --keygen instead.", + secret_fname); + } else { + tor_log(severity, LD_OR, "We needed to load a secret key from %s, " + "but couldn't find it.", secret_fname); + } goto err; + } /* If it's absent, and we're not supposed to make a new keypair, fail. */ - if (!have_secret && !found_public && !(flags & INIT_ED_KEY_CREATE)) + if (!have_secret && !found_public && !(flags & INIT_ED_KEY_CREATE)) { + if (split) { + tor_log(severity, LD_OR, "No key found in %s or %s.", + secret_fname, public_fname); + } else { + tor_log(severity, LD_OR, "No key found in %s.", secret_fname); + } goto err; + } /* If the secret key is absent, but the encrypted key would be present, * that's an error */ @@ -471,8 +489,10 @@ ed_key_init_from_file(const char *fname, uint32_t flags, goto done; /* If we didn't get a cert, and we're not supposed to make one, fail. */ - if (!signing_key || !(flags & INIT_ED_KEY_CREATE)) + if (!signing_key || !(flags & INIT_ED_KEY_CREATE)) { + tor_log(severity, LD_OR, "Without signing key, can't create certificate"); goto err; + } /* We have keys but not a certificate, so make one. */ uint32_t cert_flags = 0; @@ -483,8 +503,10 @@ ed_key_init_from_file(const char *fname, uint32_t flags, now, lifetime, cert_flags); - if (! cert) + if (! cert) { + tor_log(severity, LD_OR, "Couldn't create certificate"); goto err; + } /* Write it to disk. */ created_cert = 1; @@ -674,7 +696,7 @@ load_ed_keys(const or_options_t *options, time_t now) if (need_new_signing_key) { FAIL("Missing identity key"); } else { - log_warn(LD_OR, "master public key was absent; inferring from " + log_warn(LD_OR, "Master public key was absent; inferring from " "public key in signing certificate"); tor_assert(check_signing_cert); id = tor_malloc_zero(sizeof(*id)); diff --git a/src/test/test_keygen.sh b/src/test/test_keygen.sh index 50d54aaa63..1005bb8ea8 100755 --- a/src/test/test_keygen.sh +++ b/src/test/test_keygen.sh @@ -117,9 +117,11 @@ ME="${DATA_DIR}/case2a" SRC="${DATA_DIR}/orig" mkdir -p "${ME}/keys" cp "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/" -${TOR} --DataDirectory "${ME}" --list-fingerprint && die "Somehow succeeded when missing secret key, certs" || true +${TOR} --DataDirectory "${ME}" --list-fingerprint > "${ME}/stdout" && die "Somehow succeeded when missing secret key, certs" || true check_files_eq "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/ed25519_master_id_public_key" +grep "We needed to load a secret key.*but couldn't find it" "${ME}/stdout" >/dev/null || die "Tor didn't declare that it was missing a secret key" + echo "==== Case 2A ok" fi @@ -133,11 +135,13 @@ SRC="${DATA_DIR}/encrypted" mkdir -p "${ME}/keys" cp "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/" cp "${SRC}/keys/ed25519_master_id_secret_key_encrypted" "${ME}/keys/" -${TOR} --DataDirectory "${ME}" --list-fingerprint && dir "Somehow succeeded with encrypted secret key, missing certs" +${TOR} --DataDirectory "${ME}" --list-fingerprint > "${ME}/stdout" && dir "Somehow succeeded with encrypted secret key, missing certs" check_files_eq "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/ed25519_master_id_public_key" check_files_eq "${SRC}/keys/ed25519_master_id_secret_key_encrypted" "${ME}/keys/ed25519_master_id_secret_key_encrypted" +grep "We needed to load a secret key.*but it was encrypted.*--keygen" "${ME}/stdout" >/dev/null || die "Tor didn't declare that it was missing a secret key and suggest --keygen." + echo "==== Case 2B ok" fi @@ -230,10 +234,12 @@ SRC="${DATA_DIR}/encrypted" mkdir -p "${ME}/keys" cp "${SRC}/keys/ed25519_master_id_secret_key_encrypted" "${ME}/keys/" -${TOR} --DataDirectory "${ME}" --list-fingerprint && die "Tor started with only encrypted secret key!" +${TOR} --DataDirectory "${ME}" --list-fingerprint >"${ME}/stdout" && die "Tor started with only encrypted secret key!" check_no_file "${ME}/keys/ed25519_master_id_public_key" check_no_file "${ME}/keys/ed25519_master_id_public_key" +grep "but not public key file" "${ME}/stdout" >/dev/null || die "Tor didn't declare it couldn't find a public key." + echo "==== Case 5 ok" fi @@ -248,10 +254,12 @@ SRC="${DATA_DIR}/encrypted" mkdir -p "${ME}/keys" cp "${SRC}/keys/ed25519_master_id_secret_key_encrypted" "${ME}/keys/" cp "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/" -${TOR} --DataDirectory "${ME}" --list-fingerprint && die "Tor started with encrypted secret key and no certs" || true +${TOR} --DataDirectory "${ME}" --list-fingerprint > "${ME}/stdout" && die "Tor started with encrypted secret key and no certs" || true check_no_file "${ME}/keys/ed25519_signing_cert" check_no_file "${ME}/keys/ed25519_signing_secret_key" +grep "but it was encrypted" "${ME}/stdout" >/dev/null || die "Tor didn't declare that the secret key was encrypted." + echo "==== Case 6 ok" fi @@ -335,7 +343,9 @@ mkdir -p "${ME}/keys" cp "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/" cp "${OTHER}/keys/ed25519_master_id_secret_key" "${ME}/keys/" -${TOR} --DataDirectory "${ME}" --list-fingerprint && die "Successfully started with mismatched keys!?" || true +${TOR} --DataDirectory "${ME}" --list-fingerprint >"${ME}/stdout" && die "Successfully started with mismatched keys!?" || true + +grep "public_key does not match.*secret_key" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a key mismatch" echo "==== Case 10 ok" From 5d76acfcb302619dd97f6f2519889752bfa60e2e Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 6 Aug 2015 11:58:50 -0400 Subject: [PATCH 06/11] Document test_keygen.sh a little more --- src/test/test_keygen.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/test/test_keygen.sh b/src/test/test_keygen.sh index 1005bb8ea8..565e0a4944 100755 --- a/src/test/test_keygen.sh +++ b/src/test/test_keygen.sh @@ -69,6 +69,13 @@ touch "${DATA_DIR}/empty_torrc" QUIETLY="--hush" TOR="${TOR_BINARY} ${QUIETLY} --DisableNetwork 1 --ShutdownWaitLength 0 --ORPort 12345 --ExitRelay 0 -f ${DATA_DIR}/empty_torrc" +##### SETUP +# +# Here we create three sets of keys: one using "tor", one using "tor +# --keygen", and one using "tor --keygen" and encryption. We'll be +# copying them into different keys directories in order to simulate +# different kinds of configuration problems/issues. + # Step 1: Start Tor with --list-fingerprint. Make sure everything is there. mkdir "${DATA_DIR}/orig" ${TOR} --DataDirectory "${DATA_DIR}/orig" --list-fingerprint > /dev/null From 76ec8915721fa825e019821a4b045359156f8cc5 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 7 Aug 2015 15:22:23 -0400 Subject: [PATCH 07/11] When we infer the master key from the certificate, save it to disk. --- src/or/routerkeys.c | 11 ++++++++++- src/test/test_keygen.sh | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c index 125fe7545d..2b967dd893 100644 --- a/src/or/routerkeys.c +++ b/src/or/routerkeys.c @@ -697,11 +697,20 @@ load_ed_keys(const or_options_t *options, time_t now) FAIL("Missing identity key"); } else { log_warn(LD_OR, "Master public key was absent; inferring from " - "public key in signing certificate"); + "public key in signing certificate and saving to disk."); tor_assert(check_signing_cert); id = tor_malloc_zero(sizeof(*id)); memcpy(&id->pubkey, &check_signing_cert->signing_key, sizeof(ed25519_public_key_t)); + fname = options_get_datadir_fname2(options, "keys", + "ed25519_master_id_public_key"); + if (ed25519_pubkey_write_to_file(&id->pubkey, fname, "type0") < 0) { + log_warn(LD_OR, "Error while attempting to write master public key " + "to disk"); + tor_free(fname); + goto err; + } + tor_free(fname); } } if (tor_mem_is_zero((char*)id->seckey.seckey, sizeof(id->seckey))) diff --git a/src/test/test_keygen.sh b/src/test/test_keygen.sh index 565e0a4944..e8e3c3d219 100755 --- a/src/test/test_keygen.sh +++ b/src/test/test_keygen.sh @@ -7,7 +7,7 @@ umask 077 set -e if [ $# -eq 0 ] || [ ! -f ${1} ] || [ ! -x ${1} ]; then - if [ "$TESTING_TOR_BINARY" = ""] ; then + if [ "$TESTING_TOR_BINARY" = "" ] ; then echo "Usage: ${0} PATH_TO_TOR [case-number]" exit 1 fi @@ -329,7 +329,7 @@ cp "${SRC}/keys/ed25519_signing_secret_key" "${ME}/keys/" ${TOR} --DataDirectory "${ME}" --list-fingerprint >/dev/null || die "Failed when starting with only signing material" check_no_file "${ME}/keys/ed25519_master_id_secret_key" -check_no_file "${ME}/keys/ed25519_master_id_public_key" +check_file "${ME}/keys/ed25519_master_id_public_key" check_keys_eq ed25519_signing_secret_key check_keys_eq ed25519_signing_cert From a1b5e8b30b277c8856b3109038afcc5dd7e4c82c Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 10 Aug 2015 10:02:33 -0400 Subject: [PATCH 08/11] Don' call failure to get keys a bug; it's possible now. --- src/or/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/or/main.c b/src/or/main.c index 90f78b6576..214f5f6329 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -2017,7 +2017,7 @@ do_main_loop(void) * TLS context. */ if (! client_identity_key_is_set()) { if (init_keys() < 0) { - log_err(LD_BUG,"Error initializing keys; exiting"); + log_err(LD_OR, "Error initializing keys; exiting"); return -1; } } From c88a8a7ca38e3dd43dca913fbe22aee819420c4a Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 10 Aug 2015 10:35:47 -0400 Subject: [PATCH 09/11] Explain better why we are about to load the master key. --- src/or/routerkeys.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c index 2b967dd893..025fa6f514 100644 --- a/src/or/routerkeys.c +++ b/src/or/routerkeys.c @@ -660,6 +660,20 @@ load_ed_keys(const or_options_t *options, time_t now) need_new_signing_key || EXPIRES_SOON(check_signing_cert, options->TestingSigningKeySlop); + if (need_new_signing_key) { + log_notice(LD_OR, "It looks like I need to generate and sign a new " + "medium-term signing key, because %s. To do that, I need to " + "load (or create) the permanent master identity key.", + (NULL == use_signing) ? "I don't have one" : + EXPIRES_SOON(check_signing_cert, 0) ? "the one I have is expired" : + "you asked me to make one with --keygen"); + } else if (want_new_signing_key) { + log_notice(LD_OR, "It looks like I should try to generate and sign a " + "new medium-term signing key, because the one I have is " + "going to expire soon. To do that, I'm going to have to try to " + "load the permanent master identity key."); + } + { uint32_t flags = (INIT_ED_KEY_SPLIT| From 426ef9c8eb1d88914ebff544d6704d5c6de23ba1 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 10 Aug 2015 11:04:53 -0400 Subject: [PATCH 10/11] More log messages for keygen problems --- src/or/routerkeys.c | 21 +++++++++++++++++---- src/or/routerkeys.h | 1 + 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c index 025fa6f514..80b26e6bf8 100644 --- a/src/or/routerkeys.c +++ b/src/or/routerkeys.c @@ -246,6 +246,9 @@ write_secret_key(const ed25519_secret_key_t *key, int encrypted, * If INIT_ED_KEY_NO_REPAIR is set, and there is any issue loading the keys * from disk _other than their absence_ (full or partial), we do not try to * replace them. + * + * If INIT_ED_KEY_SUGGEST_KEYGEN is set, have log messages about failures + * refer to the --keygen option. */ ed25519_keypair_t * ed_key_init_from_file(const char *fname, uint32_t flags, @@ -358,8 +361,12 @@ ed_key_init_from_file(const char *fname, uint32_t flags, /* If we have a secret key and we're reloading the public key, * the key must match! */ if (! ed25519_pubkey_eq(&keypair->pubkey, &pubkey_tmp)) { - tor_log(severity, LD_OR, "%s does not match %s!", - public_fname, loaded_secret_fname); + tor_log(severity, LD_OR, "%s does not match %s! If you are trying " + "to restore from backup, make sure you didn't mix up the " + "key files. If you are absolutely sure that %s is the right " + "key for this relay, delete %s or move it out of the way.", + public_fname, loaded_secret_fname, + loaded_secret_fname, public_fname); goto err; } } else { @@ -389,11 +396,17 @@ ed_key_init_from_file(const char *fname, uint32_t flags, !(flags & INIT_ED_KEY_MISSING_SECRET_OK)) { if (have_encrypted_secret_file) { tor_log(severity, LD_OR, "We needed to load a secret key from %s, " - "but it was encrypted. Try tor --keygen instead.", + "but it was encrypted. Try 'tor --keygen' instead, so you " + "can enter the passphrase.", secret_fname); } else { tor_log(severity, LD_OR, "We needed to load a secret key from %s, " - "but couldn't find it.", secret_fname); + "but couldn't find it. %s", secret_fname, + (flags & INIT_ED_KEY_SUGGEST_KEYGEN) ? + "If you're keeping your master secret key offline, you will " + "need to run 'tor --keygen' to generate new signing keys." : + "Did you forget to copy it over when you copied the rest of the " + "signing key material?"); } goto err; } diff --git a/src/or/routerkeys.h b/src/or/routerkeys.h index 9b93358ae3..b4e73aa33f 100644 --- a/src/or/routerkeys.h +++ b/src/or/routerkeys.h @@ -16,6 +16,7 @@ #define INIT_ED_KEY_OMIT_SECRET (1u<<7) #define INIT_ED_KEY_TRY_ENCRYPTED (1u<<8) #define INIT_ED_KEY_NO_REPAIR (1u<<9) +#define INIT_ED_KEY_SUGGEST_KEYGEN (1u<<10) struct tor_cert_st; ed25519_keypair_t *ed_key_init_from_file(const char *fname, uint32_t flags, From 8589c4704908c6089285226db7ae361634f8b843 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 11 Aug 2015 11:21:03 -0400 Subject: [PATCH 11/11] changes file for ed25519_keygen branch --- changes/16679_16685_etc | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 changes/16679_16685_etc diff --git a/changes/16679_16685_etc b/changes/16679_16685_etc new file mode 100644 index 0000000000..8284614278 --- /dev/null +++ b/changes/16679_16685_etc @@ -0,0 +1,13 @@ + + o Major features (relay, Ed25519): + - Significant improvements to the usability of relay-side Ed25519 + key management. Log messages are better, and the code can + recover from far more failure conditions. Thanks to "s7r" for + reporting and diagnosing so many of these! + + o Major bugfixes (relay, Ed25519): + - Avoid crashing on 'tor --keygen'. Fixes bug 16679; bugfix on + 0.2.7.2-alpha. Reported by "s7r". + - Improve handling of expired signing keys along with offline + master keys. Fixes bug 16685; bugfix on 0.2.7.2-alpha. Reported + by "s7r".