[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