mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-10 21:23:58 +01:00
Merge branch 'ed25519_keygen_squashed'
This commit is contained in:
commit
428bb2d1c8
@ -25,6 +25,8 @@ include src/include.am
|
||||
include doc/include.am
|
||||
include contrib/include.am
|
||||
|
||||
export TESTING_TOR_BINARY
|
||||
|
||||
EXTRA_DIST+= \
|
||||
ChangeLog \
|
||||
INSTALL \
|
||||
|
13
changes/16679_16685_etc
Normal file
13
changes/16679_16685_etc
Normal file
@ -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".
|
@ -1918,6 +1918,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 },
|
||||
@ -4498,6 +4500,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);
|
||||
|
@ -2057,7 +2057,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;
|
||||
}
|
||||
}
|
||||
@ -2916,7 +2916,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())) {
|
||||
|
@ -4295,6 +4295,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. */
|
||||
|
@ -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,12 +107,12 @@ 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);
|
||||
@ -57,6 +123,7 @@ read_encrypted_secret_key(ed25519_secret_key_t *out,
|
||||
} else if (r == UNPWBOX_OKAY) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Otherwise, passphrase is bad, so try again till user does ctrl-c or gets
|
||||
* it right. */
|
||||
}
|
||||
@ -87,22 +154,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 +189,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 +201,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);
|
||||
}
|
||||
@ -175,7 +244,11 @@ 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.
|
||||
*
|
||||
* 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,
|
||||
@ -196,6 +269,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)) !=
|
||||
@ -215,8 +289,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) {
|
||||
@ -232,18 +305,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) {
|
||||
@ -259,10 +340,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,
|
||||
@ -281,24 +361,75 @@ 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 {
|
||||
/* 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(LOG_NOTICE, LD_OR,
|
||||
"Found secret key but not %s. Regenerating.",
|
||||
public_fname);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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, 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. %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;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
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) {
|
||||
@ -371,8 +502,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;
|
||||
@ -383,8 +516,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;
|
||||
@ -538,10 +673,26 @@ 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_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)
|
||||
@ -568,8 +719,27 @@ 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 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)))
|
||||
sign_signing_key_with_id = NULL;
|
||||
else
|
||||
@ -628,6 +798,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 +824,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 {
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
363
src/test/test_keygen.sh
Executable file
363
src/test/test_keygen.sh
Executable file
@ -0,0 +1,363 @@
|
||||
#!/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
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
|
||||
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"
|
||||
|
||||
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
|
||||
|
||||
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 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"
|
||||
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 "=== Starting tests."
|
||||
|
||||
#
|
||||
# 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 > "${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
|
||||
|
||||
# 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 > "${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
|
||||
|
||||
# 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 >/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"
|
||||
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 > "${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 > "${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"
|
||||
|
||||
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 >"${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
|
||||
|
||||
# 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 > "${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
|
||||
|
||||
# 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 >/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
|
||||
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 >/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
|
||||
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 >/dev/null || die "Failed when starting with only signing material"
|
||||
check_no_file "${ME}/keys/ed25519_master_id_secret_key"
|
||||
check_file "${ME}/keys/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: master 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 >"${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"
|
||||
|
||||
fi
|
||||
|
||||
|
||||
# Check cert-only.
|
||||
|
Loading…
Reference in New Issue
Block a user