commit b6eee531bb546683387fb471b754f24bc40580d0 Author: Nick Mathewson <ni...@torproject.org> Date: Sun Mar 1 16:40:02 2015 +0100
Support encrypted offline master keys with a new --keygen flag When --keygen is provided, we prompt for a passphrase when we make a new master key; if it is nonempty, we store the secret key in a new crypto_pwbox. Also, if --keygen is provided and there *is* an encrypted master key, we load it and prompt for a passphrase unconditionally. We make a new signing key unconditionally when --keygen is provided. We never overwrite a master key. --- src/or/config.c | 5 +- src/or/main.c | 3 ++ src/or/or.h | 3 +- src/or/routerkeys.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++++-- src/or/routerkeys.h | 6 +++ 5 files changed, 161 insertions(+), 5 deletions(-) diff --git a/src/or/config.c b/src/or/config.c index ef249a6..d81bc53 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -1909,6 +1909,7 @@ static const struct { { "--hash-password", ARGUMENT_NECESSARY }, { "--dump-config", ARGUMENT_OPTIONAL }, { "--list-fingerprint", TAKES_NO_ARGUMENT }, + { "--keygen", TAKES_NO_ARGUMENT }, { "--verify-config", TAKES_NO_ARGUMENT }, { "--ignore-missing-torrc", TAKES_NO_ARGUMENT }, { "--quiet", TAKES_NO_ARGUMENT }, @@ -4434,7 +4435,9 @@ options_init_from_torrc(int argc, char **argv) command = CMD_RUN_TOR; for (p_index = cmdline_only_options; p_index; p_index = p_index->next) { - if (!strcmp(p_index->key,"--list-fingerprint")) { + if (!strcmp(p_index->key,"--keygen")) { + command = CMD_KEYGEN; + } else if (!strcmp(p_index->key,"--list-fingerprint")) { command = CMD_LIST_FINGERPRINT; } else if (!strcmp(p_index->key, "--hash-password")) { command = CMD_HASH_PASSWORD; diff --git a/src/or/main.c b/src/or/main.c index bbee8e0..d9ef88b 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -3162,6 +3162,9 @@ tor_main(int argc, char *argv[]) #endif result = do_main_loop(); break; + case CMD_KEYGEN: + result = load_ed_keys(get_options(), time(NULL)); + break; case CMD_LIST_FINGERPRINT: result = do_list_fingerprint(); break; diff --git a/src/or/or.h b/src/or/or.h index 81e1c1c..230bc47 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -3405,7 +3405,8 @@ typedef struct { /** What should the tor process actually do? */ enum { CMD_RUN_TOR=0, CMD_LIST_FINGERPRINT, CMD_HASH_PASSWORD, - CMD_VERIFY_CONFIG, CMD_RUN_UNITTESTS, CMD_DUMP_CONFIG + CMD_VERIFY_CONFIG, CMD_RUN_UNITTESTS, CMD_DUMP_CONFIG, + CMD_KEYGEN } command; char *command_arg; /**< Argument for command-line option. */ diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c index 556ab45..63d61c3 100644 --- a/src/or/routerkeys.c +++ b/src/or/routerkeys.c @@ -4,9 +4,130 @@ #include "or.h" #include "config.h" #include "router.h" +#include "crypto_pwbox.h" #include "routerkeys.h" #include "torcert.h" +#define ENC_KEY_HEADER "Boxed Ed25519 key" +#define ENC_KEY_TAG "master" + +int +read_encrypted_secret_key(ed25519_secret_key_t *out, + const char *fname) +{ + int r = -1; + uint8_t *secret = NULL; + size_t secret_len = 0; + char pwbuf[256]; + uint8_t encrypted_key[256]; + char *tag = NULL; + + ssize_t encrypted_len = crypto_read_tagged_contents_from_file(fname, + ENC_KEY_HEADER, + &tag, + encrypted_key, + sizeof(encrypted_key)); + if (encrypted_len < 0) { + log_info(LD_OR, "%s is missing", fname); + return 0; + } + if (strcmp(tag, ENC_KEY_TAG)) + return -1; + + while (1) { + ssize_t pwlen = + tor_getpass("Enter pasphrase for master key:", pwbuf, sizeof(pwbuf)); + if (pwlen < 0) + 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); + goto done; + } else if (r == UNPWBOX_OKAY) { + break; + } + /* Otherwise, passphrase is bad, so try again till user does ctrl-c or gets + * it right. */ + } + + + if (secret_len != ED25519_SECKEY_LEN) { + log_err(LD_OR, "%s is corrupted.", fname); + goto done; + } + memcpy(out->seckey, secret, ED25519_SECKEY_LEN); + r = 1; + + done: + memwipe(encrypted_key, 0, encrypted_len); + memwipe(pwbuf, 0, sizeof(pwbuf)); + if (secret) { + memwipe(secret, 0, secret_len); + tor_free(secret); + } + return r; +} + +int +write_encrypted_secret_key(const ed25519_secret_key_t *key, + const char *fname) +{ + int r = -1; + char pwbuf0[256], pwbuf1[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 (0 == strlen(pwbuf0)) + return 0; + if (crypto_pwbox(&encrypted_key, &encrypted_len, + key->seckey, sizeof(key->seckey), + pwbuf0, strlen(pwbuf0), 0) < 0) { + log_warn(LD_OR, "crypto_pwbox failed!?"); + goto done; + } + if (crypto_write_tagged_contents_to_file(fname, + ENC_KEY_HEADER, + ENC_KEY_TAG, + encrypted_key, encrypted_len) < 0) + goto done; + r = 1; + done: + if (encrypted_key) { + memwipe(encrypted_key, 0, encrypted_len); + tor_free(encrypted_key); + } + memwipe(pwbuf0, 0, sizeof(pwbuf0)); + memwipe(pwbuf1, 0, sizeof(pwbuf1)); + return r; +} + +static int +write_secret_key(const ed25519_secret_key_t *key, int encrypted, + const char *fname, + const char *fname_tag, + const char *encrypted_fname) +{ + if (encrypted) { + int r = write_encrypted_secret_key(key, encrypted_fname); + if (r != 0) + return r; + } + return ed25519_seckey_write_to_file(key, fname, fname_tag); +} + /** * Read an ed25519 key and associated certificates from files beginning with * <b>fname</b>, with certificate type <b>cert_type</b>. On failure, return @@ -38,6 +159,9 @@ * * If INIT_ED_KEY_OMIT_SECRET is set in <b>flags</b>, do not even try to * load or return a secret key (but create and save on if needed). + * + * If INIT_ED_KEY_TRY_ENCRYPTED is set, we look for an encrypted secret key + * and consider encrypting any new secret key. */ ed25519_keypair_t * ed_key_init_from_file(const char *fname, uint32_t flags, @@ -49,10 +173,12 @@ ed_key_init_from_file(const char *fname, uint32_t flags, struct tor_cert_st **cert_out) { char *secret_fname = NULL; + char *encrypted_secret_fname = NULL; char *public_fname = NULL; char *cert_fname = NULL; int created_pk = 0, created_sk = 0, created_cert = 0; const int try_to_load = ! (flags & INIT_ED_KEY_REPLACE); + const int encrypt_key = (flags & INIT_ED_KEY_TRY_ENCRYPTED); char tag[8]; tor_snprintf(tag, sizeof(tag), "type%d", (int)cert_type); @@ -62,15 +188,26 @@ ed_key_init_from_file(const char *fname, uint32_t flags, ed25519_keypair_t *keypair = tor_malloc_zero(sizeof(ed25519_keypair_t)); tor_asprintf(&secret_fname, "%s_secret_key", fname); + tor_asprintf(&encrypted_secret_fname, "%s_secret_key_encrypted", fname); tor_asprintf(&public_fname, "%s_public_key", fname); tor_asprintf(&cert_fname, "%s_cert", fname); /* Try to read the secret key. */ - const int have_secret = try_to_load && + int have_secret = try_to_load && !(flags & INIT_ED_KEY_OMIT_SECRET) && ed25519_seckey_read_from_file(&keypair->seckey, &got_tag, secret_fname) == 0; + /* Should we try for an encrypted key? */ + 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; + got_tag = tor_strdup(tag); + } + } + if (have_secret) { if (strcmp(got_tag, tag)) { tor_log(severity, LD_OR, "%s has wrong tag", secret_fname); @@ -115,7 +252,9 @@ ed_key_init_from_file(const char *fname, uint32_t flags, } created_pk = created_sk = created_cert = 1; - if (ed25519_seckey_write_to_file(&keypair->seckey, secret_fname, tag) < 0 + if (write_secret_key(&keypair->seckey, + encrypt_key, + secret_fname, tag, encrypted_secret_fname) < 0 || (split && ed25519_pubkey_write_to_file(&keypair->pubkey, public_fname, tag) < 0) @@ -214,6 +353,7 @@ ed_key_init_from_file(const char *fname, uint32_t flags, unlink(cert_fname); cleanup: + tor_free(encrypted_secret_fname); tor_free(secret_fname); tor_free(public_fname); tor_free(cert_fname); @@ -324,7 +464,8 @@ load_ed_keys(const or_options_t *options, time_t now) const int need_new_signing_key = NULL == use_signing || - EXPIRES_SOON(check_signing_cert, 0); + EXPIRES_SOON(check_signing_cert, 0) || + options->command == CMD_KEYGEN; const int want_new_signing_key = need_new_signing_key || EXPIRES_SOON(check_signing_cert, options->TestingSigningKeySlop); @@ -337,6 +478,8 @@ load_ed_keys(const or_options_t *options, time_t now) flags |= INIT_ED_KEY_MISSING_SECRET_OK; if (! want_new_signing_key) flags |= INIT_ED_KEY_OMIT_SECRET; + if (options->command == CMD_KEYGEN) + flags |= INIT_ED_KEY_TRY_ENCRYPTED; id = ed_key_init_from_file( options_get_datadir_fname2(options, "keys", "ed25519_master_id"), diff --git a/src/or/routerkeys.h b/src/or/routerkeys.h index b45a22a..1e0199e 100644 --- a/src/or/routerkeys.h +++ b/src/or/routerkeys.h @@ -14,6 +14,7 @@ #define INIT_ED_KEY_EXTRA_STRONG (1u<<5) #define INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT (1u<<6) #define INIT_ED_KEY_OMIT_SECRET (1u<<7) +#define INIT_ED_KEY_TRY_ENCRYPTED (1u<<8) struct tor_cert_st; ed25519_keypair_t *ed_key_init_from_file(const char *fname, uint32_t flags, @@ -61,6 +62,11 @@ int should_make_new_ed_keys(const or_options_t *options, const time_t now); int generate_ed_link_cert(const or_options_t *options, time_t now); +int read_encrypted_secret_key(ed25519_secret_key_t *out, + const char *fname); +int write_encrypted_secret_key(const ed25519_secret_key_t *out, + const char *fname); + void routerkeys_free_all(void); #endif _______________________________________________ tor-commits mailing list tor-commits@lists.torproject.org https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits