routerkeys: Add cmdline option for learning signing key expiration.

* CLOSES #17639.
 * ADDS new --key-expiration commandline option which prints when the
   signing key expires.
This commit is contained in:
Isis Lovecruft 2017-07-14 01:25:01 +00:00
parent fabc3deb75
commit b2a7e8df90
No known key found for this signature in database
GPG Key ID: C8E8B1AA4CF3844E
9 changed files with 259 additions and 1 deletions

4
changes/bug17639 Normal file
View File

@ -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.

View File

@ -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

View File

@ -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")) {

View File

@ -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;

View File

@ -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. */

View File

@ -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)
{

View File

@ -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);

View File

@ -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 \

129
src/test/test_key_expiration.sh Executable file
View File

@ -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