[tor-commits] [tor/master] routerkeys: Add cmdline option for learning signing key expiration.
nickm at torproject.org
nickm at torproject.org
Fri Aug 4 00:17:50 UTC 2017
commit b2a7e8df900eabe41d6e866f8b66aadd8a0d31d7
Author: Isis Lovecruft <isis at torproject.org>
Date: Fri Jul 14 01:25:01 2017 +0000
routerkeys: Add cmdline option for learning signing key expiration.
* CLOSES #17639.
* ADDS new --key-expiration commandline option which prints when the
signing key expires.
---
changes/bug17639 | 4 ++
doc/tor.1.txt | 10 ++++
src/or/config.c | 4 ++
src/or/main.c | 5 ++
src/or/or.h | 3 +-
src/or/routerkeys.c | 102 +++++++++++++++++++++++++++++++
src/or/routerkeys.h | 1 +
src/test/include.am | 2 +
src/test/test_key_expiration.sh | 129 ++++++++++++++++++++++++++++++++++++++++
9 files changed, 259 insertions(+), 1 deletion(-)
diff --git a/changes/bug17639 b/changes/bug17639
new file mode 100644
index 000000000..4073514fd
--- /dev/null
+++ b/changes/bug17639
@@ -0,0 +1,4 @@
+ o Minor features:
+ - Add a new commandline option, --key-expiration, which prints when
+ the current signing key is going to expire. Implements ticket
+ 17639; patch by Isis Lovecruft.
diff --git a/doc/tor.1.txt b/doc/tor.1.txt
index c8d7688a9..b4a3cc5f7 100644
--- a/doc/tor.1.txt
+++ b/doc/tor.1.txt
@@ -128,6 +128,16 @@ COMMAND-LINE OPTIONS
the passphrase, including any trailing newlines.
Default: read from the terminal.
+[[opt-key-expiration]] **--key-expiration** [**purpose**]::
+ The **purpose** specifies which type of key certificate to determine
+ the expiration of. The only currently recognised **purpose** is
+ "sign". +
+ +
+ Running "tor --key-expiration sign" will attempt to find your signing
+ key certificate and will output, both in the logs as well as to stdout,
+ the signing key certificate's expiration time in ISO-8601 format.
+ For example, the output sent to stdout will be of the form:
+ "signing-cert-expiry: 2017-07-25 08:30:15 UTC"
Other options can be specified on the command-line in the format "--option
value", in the format "option value", or in a configuration file. For
diff --git a/src/or/config.c b/src/or/config.c
index 53fc2795c..9b6bf40eb 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -2137,6 +2137,7 @@ static const struct {
{ "--dump-config", ARGUMENT_OPTIONAL },
{ "--list-fingerprint", TAKES_NO_ARGUMENT },
{ "--keygen", TAKES_NO_ARGUMENT },
+ { "--key-expiration", ARGUMENT_OPTIONAL },
{ "--newpass", TAKES_NO_ARGUMENT },
{ "--no-passphrase", TAKES_NO_ARGUMENT },
{ "--passphrase-fd", ARGUMENT_NECESSARY },
@@ -4932,6 +4933,9 @@ options_init_from_torrc(int argc, char **argv)
for (p_index = cmdline_only_options; p_index; p_index = p_index->next) {
if (!strcmp(p_index->key,"--keygen")) {
command = CMD_KEYGEN;
+ } else if (!strcmp(p_index->key, "--key-expiration")) {
+ command = CMD_KEY_EXPIRATION;
+ command_arg = p_index->value;
} else if (!strcmp(p_index->key,"--list-fingerprint")) {
command = CMD_LIST_FINGERPRINT;
} else if (!strcmp(p_index->key, "--hash-password")) {
diff --git a/src/or/main.c b/src/or/main.c
index dc2318496..0267f4dae 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -3758,6 +3758,11 @@ tor_main(int argc, char *argv[])
case CMD_KEYGEN:
result = load_ed_keys(get_options(), time(NULL)) < 0;
break;
+ case CMD_KEY_EXPIRATION:
+ init_keys();
+ result = log_cert_expiration();
+ result = 0;
+ break;
case CMD_LIST_FINGERPRINT:
result = do_list_fingerprint();
break;
diff --git a/src/or/or.h b/src/or/or.h
index f6c42b7a9..32b4cd1b7 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -3588,7 +3588,8 @@ typedef struct {
enum {
CMD_RUN_TOR=0, CMD_LIST_FINGERPRINT, CMD_HASH_PASSWORD,
CMD_VERIFY_CONFIG, CMD_RUN_UNITTESTS, CMD_DUMP_CONFIG,
- CMD_KEYGEN
+ CMD_KEYGEN,
+ CMD_KEY_EXPIRATION,
} command;
char *command_arg; /**< Argument for command-line option. */
diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c
index 71889d272..2f20758b5 100644
--- a/src/or/routerkeys.c
+++ b/src/or/routerkeys.c
@@ -1136,6 +1136,108 @@ init_mock_ed_keys(const crypto_pk_t *rsa_identity_key)
#undef MAKECERT
#endif
+/**
+ * Print the ISO8601-formated <b>expiration</b> for a certificate with
+ * some <b>description</b> to stdout.
+ *
+ * For example, for a signing certificate, this might print out:
+ * signing-cert-expiry: 2017-07-25 08:30:15 UTC
+ */
+static void
+print_cert_expiration(const char *expiration,
+ const char *description)
+{
+ fprintf(stderr, "%s-cert-expiry: %s\n", description, expiration);
+}
+
+/**
+ * Log when a certificate, <b>cert</b>, with some <b>description</b> and
+ * stored in a file named <b>fname</b>, is going to expire.
+ */
+static void
+log_ed_cert_expiration(const tor_cert_t *cert,
+ const char *description,
+ const char *fname) {
+ char expiration[ISO_TIME_LEN+1];
+
+ if (BUG(!cert)) { /* If the specified key hasn't been loaded */
+ log_warn(LD_OR, "No %s key loaded; can't get certificate expiration.",
+ description);
+ } else {
+ format_local_iso_time(expiration, cert->valid_until);
+ log_notice(LD_OR, "The %s certificate stored in %s is valid until %s.",
+ description, fname, expiration);
+ print_cert_expiration(expiration, description);
+ }
+}
+
+/**
+ * Log when our master signing key certificate expires. Used when tor is given
+ * the --key-expiration command-line option.
+ *
+ * Returns 0 on success and 1 on failure.
+ */
+static int
+log_master_signing_key_cert_expiration(const or_options_t *options)
+{
+ const tor_cert_t *signing_key;
+ char *fn = NULL;
+ int failed = 0;
+ time_t now = approx_time();
+
+ fn = options_get_datadir_fname2(options, "keys", "ed25519_signing_cert");
+
+ /* Try to grab our cached copy of the key. */
+ signing_key = get_master_signing_key_cert();
+
+ tor_assert(server_identity_key_is_set());
+
+ /* Load our keys from disk, if necessary. */
+ if (!signing_key) {
+ failed = load_ed_keys(options, now) < 0;
+ signing_key = get_master_signing_key_cert();
+ }
+
+ /* If we do have a signing key, log the expiration time. */
+ if (signing_key) {
+ log_ed_cert_expiration(signing_key, "signing", fn);
+ } else {
+ log_warn(LD_OR, "Could not load signing key certificate from %s, so " \
+ "we couldn't learn anything about certificate expiration.", fn);
+ }
+
+ tor_free(fn);
+
+ return failed;
+}
+
+/**
+ * Log when a key certificate expires. Used when tor is given the
+ * --key-expiration command-line option.
+ *
+ * If an command argument is given, which should specify the type of
+ * key to get expiry information about (currently supported arguments
+ * are "sign"), get info about that type of certificate. Otherwise,
+ * print info about the supported arguments.
+ *
+ * Returns 0 on success and -1 on failure.
+ */
+int
+log_cert_expiration(void)
+{
+ const or_options_t *options = get_options();
+ const char *arg = options->command_arg;
+
+ if (!strcmp(arg, "sign")) {
+ return log_master_signing_key_cert_expiration(options);
+ } else {
+ fprintf(stderr, "No valid argument to --key-expiration found!\n");
+ fprintf(stderr, "Currently recognised arguments are: 'sign'\n");
+
+ return -1;
+ }
+}
+
const ed25519_public_key_t *
get_master_identity_key(void)
{
diff --git a/src/or/routerkeys.h b/src/or/routerkeys.h
index c10cf32a7..0cf13e760 100644
--- a/src/or/routerkeys.h
+++ b/src/or/routerkeys.h
@@ -63,6 +63,7 @@ MOCK_DECL(int, check_tap_onion_key_crosscert,(const uint8_t *crosscert,
const ed25519_public_key_t *master_id_pkey,
const uint8_t *rsa_id_digest));
+int log_cert_expiration(void);
int load_ed_keys(const or_options_t *options, time_t now);
int should_make_new_ed_keys(const or_options_t *options, const time_t now);
diff --git a/src/test/include.am b/src/test/include.am
index 2e448c8b3..230a72201 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -34,6 +34,7 @@ endif
TESTS += src/test/test src/test/test-slow src/test/test-memwipe \
src/test/test_workqueue \
src/test/test_keygen.sh \
+ src/test/test_key_expiration.sh \
src/test/test-timers \
$(TESTSCRIPTS)
@@ -325,6 +326,7 @@ EXTRA_DIST += \
src/test/slownacl_curve25519.py \
src/test/zero_length_keys.sh \
src/test/test_keygen.sh \
+ src/test/test_key_expiration.sh \
src/test/test_zero_length_keys.sh \
src/test/test_ntor.sh src/test/test_hs_ntor.sh src/test/test_bt.sh \
src/test/test-network.sh \
diff --git a/src/test/test_key_expiration.sh b/src/test/test_key_expiration.sh
new file mode 100755
index 000000000..95d7911f0
--- /dev/null
+++ b/src/test/test_key_expiration.sh
@@ -0,0 +1,129 @@
+#!/bin/sh
+
+# Note: some of this code is lifted from zero_length_keys.sh and
+# test_keygen.sh, and could be unified.
+
+umask 077
+set -e
+
+if [ $# -eq 0 ] || [ ! -f ${1} ] || [ ! -x ${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
+else
+ dflt=1
+fi
+
+CASE1=$dflt
+CASE2=$dflt
+CASE3=$dflt
+
+if [ $# -ge 1 ]; then
+ eval "CASE${1}"=1
+fi
+
+
+dump() { xxd -p "$1" | tr -d '\n '; }
+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: `dump $1` vs `dump $2`"; }
+check_keys_eq() { check_files_eq "${SRC}/keys/${1}" "${ME}/keys/${1}"; }
+
+DATA_DIR=`mktemp -d -t tor_key_expiration_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
+
+# Use an absolute path for this or Tor will complain
+DATA_DIR=`cd "${DATA_DIR}" && pwd`
+
+touch "${DATA_DIR}/empty_torrc"
+
+QUIETLY="--hush"
+SILENTLY="--quiet"
+TOR="${TOR_BINARY} --DisableNetwork 1 --ShutdownWaitLength 0 --ORPort 12345 --ExitRelay 0 -f ${DATA_DIR}/empty_torrc --DataDirectory ${DATA_DIR}"
+
+##### SETUP
+#
+# Here we create a set of keys.
+
+# Step 1: Start Tor with --list-fingerprint --quiet. Make sure everything is there.
+echo "Setup step #1"
+${TOR} --list-fingerprint ${SILENTLY} > /dev/null
+
+check_dir "${DATA_DIR}/keys"
+check_file "${DATA_DIR}/keys/ed25519_master_id_public_key"
+check_file "${DATA_DIR}/keys/ed25519_master_id_secret_key"
+check_file "${DATA_DIR}/keys/ed25519_signing_cert"
+check_file "${DATA_DIR}/keys/ed25519_signing_secret_key"
+check_file "${DATA_DIR}/keys/secret_id_key"
+check_file "${DATA_DIR}/keys/secret_onion_key"
+check_file "${DATA_DIR}/keys/secret_onion_key_ntor"
+
+##### TEST CASES
+
+echo "=== Starting key expiration tests."
+
+FN="${DATA_DIR}/stderr"
+
+if [ "$CASE1" = 1 ]; then
+ echo "==== Case 1: Test --key-expiration without argument and ensure usage"
+ echo " instructions are printed."
+
+ ${TOR} ${QUIETLY} --key-expiration 2>"$FN"
+ grep "No valid argument to --key-expiration found!" "$FN" >/dev/null || \
+ die "Tor didn't mention supported --key-expiration argmuents"
+
+ echo "==== Case 1: ok"
+fi
+
+if [ "$CASE2" = 1 ]; then
+ echo "==== Case 2: Start Tor with --key-expiration 'sign' and make sure it prints an expiration."
+
+ ${TOR} ${QUIETLY} --key-expiration sign 2>"$FN"
+ grep "signing-cert-expiry:" "$FN" >/dev/null || \
+ die "Tor didn't print an expiration"
+
+ echo "==== Case 2: ok"
+fi
+
+if [ "$CASE3" = 1 ]; then
+ echo "==== Case 3: Start Tor with --key-expiration 'sign', when there is no"
+ echo " signing key, and make sure that Tor generates a new key"
+ echo " and prints its certificate's expiration."
+
+ mv "${DATA_DIR}/keys/ed25519_signing_cert" \
+ "${DATA_DIR}/keys/ed25519_signing_cert.bak"
+
+ ${TOR} --key-expiration sign > "$FN" 2>&1
+ grep "It looks like I need to generate and sign a new medium-term signing key" "$FN" >/dev/null || \
+ die "Tor didn't create a new signing key"
+ check_file "${DATA_DIR}/keys/ed25519_signing_cert"
+ grep "signing-cert-expiry:" "$FN" >/dev/null || \
+ die "Tor didn't print an expiration"
+
+ mv "${DATA_DIR}/keys/ed25519_signing_cert.bak" \
+ "${DATA_DIR}/keys/ed25519_signing_cert"
+
+ echo "==== Case 3: ok"
+fi
More information about the tor-commits
mailing list