/** Helper for tor_cert_create(): signs any 32 bytes, not just an ed25519 * key. */ static tor_cert_t * tor_cert_sign_impl(const ed25519_keypair_t *signing_key, uint8_t cert_type, uint8_t signed_key_type, const uint8_t signed_key_info[32], time_t now, time_t lifetime, uint32_t flags) { tor_cert_t *torcert = NULL; ed25519_cert_t *cert = ed25519_cert_new(); cert->cert_type = cert_type; cert->exp_field = (uint32_t) CEIL_DIV(now + lifetime, 3600); cert->cert_key_type = signed_key_type; memcpy(cert->certified_key, signed_key_info, 32); if (flags & CERT_FLAG_INCLUDE_SIGNING_KEY) { ed25519_cert_extension_t *ext = ed25519_cert_extension_new(); ext->ext_type = CERTEXT_SIGNED_WITH_KEY; memcpy(ext->un_signing_key, signing_key->pubkey.pubkey, 32); ed25519_cert_add_ext(cert, ext); ++cert->n_extensions; } const ssize_t alloc_len = ed25519_cert_encoded_len(cert); tor_assert(alloc_len > 0); uint8_t *encoded = tor_malloc(alloc_len); const ssize_t real_len = ed25519_cert_encode(encoded, alloc_len, cert); if (real_len < 0) goto err; tor_assert(real_len == alloc_len); tor_assert(real_len > ED25519_SIG_LEN); uint8_t *sig = encoded + (real_len - ED25519_SIG_LEN); tor_assert(tor_mem_is_zero((char*)sig, ED25519_SIG_LEN)); ed25519_signature_t signature; if (ed25519_sign(&signature, encoded, real_len-ED25519_SIG_LEN, signing_key)<0) { log_warn(LD_BUG, "Can't sign certificate"); goto err; } memcpy(sig, signature.sig, ED25519_SIG_LEN); torcert = tor_cert_parse(encoded, real_len); if (! torcert) { log_warn(LD_BUG, "Generated a certificate we cannot parse"); goto err; } if (tor_cert_checksig(torcert, &signing_key->pubkey, now) < 0) { log_warn(LD_BUG, "Generated a certificate whose signature we can't check"); goto err; } tor_free(encoded); goto done; err: tor_cert_free(torcert); torcert = NULL; done: ed25519_cert_free(cert); tor_free(encoded); return torcert; }
/** * Read an ed25519 key and associated certificates from files beginning with * <b>fname</b>, with certificate type <b>cert_type</b>. On failure, return * NULL; on success return the keypair. * * If INIT_ED_KEY_CREATE is set in <b>flags</b>, then create the key (and * certificate if requested) if it doesn't exist, and save it to disk. * * If INIT_ED_KEY_NEEDCERT is set in <b>flags</b>, load/create a certificate * too and store it in *<b>cert_out</b>. Fail if the cert can't be * found/created. To create a certificate, <b>signing_key</b> must be set to * the key that should sign it; <b>now</b> to the current time, and * <b>lifetime</b> to the lifetime of the key. * * If INIT_ED_KEY_REPLACE is set in <b>flags</b>, then create and save new key * whether we can read the old one or not. * * If INIT_ED_KEY_EXTRA_STRONG is set in <b>flags</b>, set the extra_strong * flag when creating the secret key. * * If INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT is set in <b>flags</b>, and * we create a new certificate, create it with the signing key embedded. * * If INIT_ED_KEY_SPLIT is set in <b>flags</b>, and we create a new key, * store the public key in a separate file from the secret key. * * If INIT_ED_KEY_MISSING_SECRET_OK is set in <b>flags</b>, and we find a * public key file but no secret key file, return successfully anyway. * * If INIT_ED_KEY_OMIT_SECRET is set in <b>flags</b>, do not try to load a * secret key unless no public key is found. Do not return a secret key. (but * create and save one if needed). * * If INIT_ED_KEY_NO_LOAD_SECRET is set in <b>flags</b>, don't try to load * a secret key, no matter what. * * If INIT_ED_KEY_TRY_ENCRYPTED is set, we look for an encrypted secret key * 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_ (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. * * If INIT_ED_KEY_EXPLICIT_FNAME is set, use the provided file name for the * secret key file, encrypted or not. */ ed25519_keypair_t * ed_key_init_from_file(const char *fname, uint32_t flags, int severity, const ed25519_keypair_t *signing_key, time_t now, time_t lifetime, uint8_t cert_type, struct tor_cert_st **cert_out) { char *secret_fname = NULL; char *encrypted_secret_fname = NULL; char *public_fname = NULL; char *cert_fname = NULL; const char *loaded_secret_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); 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); const int offline_secret = !! (flags & INIT_ED_KEY_OFFLINE_SECRET); const int explicit_fname = !! (flags & INIT_ED_KEY_EXPLICIT_FNAME); /* we don't support setting both of these flags at once. */ tor_assert((flags & (INIT_ED_KEY_NO_REPAIR|INIT_ED_KEY_NEEDCERT)) != (INIT_ED_KEY_NO_REPAIR|INIT_ED_KEY_NEEDCERT)); char tag[8]; tor_snprintf(tag, sizeof(tag), "type%d", (int)cert_type); tor_cert_t *cert = NULL; char *got_tag = NULL; ed25519_keypair_t *keypair = tor_malloc_zero(sizeof(ed25519_keypair_t)); if (explicit_fname) { secret_fname = tor_strdup(fname); encrypted_secret_fname = tor_strdup(fname); } else { 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. */ int have_secret = 0; int load_secret = try_to_load && !offline_secret && (!omit_secret || file_status(public_fname)==FN_NOENT); if (load_secret) { int rv = ed25519_seckey_read_from_file(&keypair->seckey, &got_tag, secret_fname); if (rv == 0) { have_secret = 1; loaded_secret_fname = secret_fname; tor_assert(got_tag); } else { if (errno != ENOENT && norepair) { tor_log(severity, LD_OR, "Unable to read %s: %s", secret_fname, strerror(errno)); goto err; } } } /* 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; tor_free(got_tag); /* convince coverity we aren't leaking */ 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)); 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) { if (strcmp(got_tag, tag)) { tor_log(severity, LD_OR, "%s has wrong tag", loaded_secret_fname); goto err; } /* Derive the public key */ if (ed25519_public_key_generate(&keypair->pubkey, &keypair->seckey)<0) { tor_log(severity, LD_OR, "%s can't produce a public key", loaded_secret_fname); goto err; } } /* If we do split keys here, try to read the pubkey. */ int found_public = 0; 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, &got_tag, public_fname) == 0; if (!found_public && errno != ENOENT && norepair) { tor_log(severity, LD_OR, "Unable to read %s: %s", public_fname, strerror(errno)); goto err; } if (found_public && strcmp(got_tag, tag)) { tor_log(severity, LD_OR, "%s has wrong tag", public_fname); goto err; } if (found_public) { if (have_secret) { /* 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! 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_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 (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... */ if (!have_secret && !found_public) { tor_free(keypair); keypair = ed_key_new(signing_key, flags, now, lifetime, cert_type, &cert); if (!keypair) { tor_log(severity, LD_OR, "Couldn't create keypair"); goto err; } created_pk = created_sk = created_cert = 1; } /* Write it to disk if we're supposed to do with a new passphrase, or if * we just created it. */ if (created_sk || (have_secret && get_options()->change_key_passphrase)) { 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) || (cert && crypto_write_tagged_contents_to_file(cert_fname, "ed25519v1-cert", tag, cert->encoded, cert->encoded_len) < 0)) { tor_log(severity, LD_OR, "Couldn't write keys or cert to file."); goto err; } goto done; } /* If we're not supposed to get a cert, we're done. */ if (! (flags & INIT_ED_KEY_NEEDCERT)) goto done; /* Read a cert. */ tor_free(got_tag); uint8_t certbuf[256]; ssize_t cert_body_len = crypto_read_tagged_contents_from_file( cert_fname, "ed25519v1-cert", &got_tag, certbuf, sizeof(certbuf)); if (cert_body_len >= 0 && !strcmp(got_tag, tag)) cert = tor_cert_parse(certbuf, cert_body_len); /* If we got it, check it to the extent we can. */ int bad_cert = 0; if (! cert) { tor_log(severity, LD_OR, "Cert was unparseable"); bad_cert = 1; } else if (!tor_memeq(cert->signed_key.pubkey, keypair->pubkey.pubkey, ED25519_PUBKEY_LEN)) { tor_log(severity, LD_OR, "Cert was for wrong key"); bad_cert = 1; } else if (signing_key && tor_cert_checksig(cert, &signing_key->pubkey, now) < 0) { tor_log(severity, LD_OR, "Can't check certificate"); bad_cert = 1; } else if (cert->cert_expired) { tor_log(severity, LD_OR, "Certificate is expired"); bad_cert = 1; } else if (signing_key && cert->signing_key_included && ! ed25519_pubkey_eq(&signing_key->pubkey, &cert->signing_key)) { tor_log(severity, LD_OR, "Certificate signed by unexpectd key!"); bad_cert = 1; } if (bad_cert) { tor_cert_free(cert); cert = NULL; } /* If we got a cert, we're done. */ if (cert) 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)) { 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; if (flags & INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT) cert_flags |= CERT_FLAG_INCLUDE_SIGNING_KEY; cert = tor_cert_create(signing_key, cert_type, &keypair->pubkey, now, lifetime, cert_flags); if (! cert) { tor_log(severity, LD_OR, "Couldn't create certificate"); goto err; } /* Write it to disk. */ created_cert = 1; if (crypto_write_tagged_contents_to_file(cert_fname, "ed25519v1-cert", tag, cert->encoded, cert->encoded_len) < 0) { tor_log(severity, LD_OR, "Couldn't write cert to disk."); goto err; } done: if (cert_out) *cert_out = cert; else tor_cert_free(cert); goto cleanup; err: if (keypair) memwipe(keypair, 0, sizeof(*keypair)); tor_free(keypair); tor_cert_free(cert); if (cert_out) *cert_out = NULL; if (created_sk) unlink(secret_fname); if (created_pk) unlink(public_fname); if (created_cert) unlink(cert_fname); cleanup: tor_free(encrypted_secret_fname); tor_free(secret_fname); tor_free(public_fname); tor_free(cert_fname); tor_free(got_tag); return keypair; }
static void test_routerkeys_ed_certs(void *args) { (void)args; ed25519_keypair_t kp1, kp2; tor_cert_t *cert[2] = {NULL, NULL}, *nocert = NULL; tor_cert_t *parsed_cert[2] = {NULL, NULL}; time_t now = 1412094534; uint8_t *junk = NULL; char *base64 = NULL; tt_int_op(0,OP_EQ,ed25519_keypair_generate(&kp1, 0)); tt_int_op(0,OP_EQ,ed25519_keypair_generate(&kp2, 0)); for (int i = 0; i <= 1; ++i) { uint32_t flags = i ? CERT_FLAG_INCLUDE_SIGNING_KEY : 0; cert[i] = tor_cert_create(&kp1, 5, &kp2.pubkey, now, 10000, flags); tt_assert(cert[i]); tt_uint_op(cert[i]->sig_bad, OP_EQ, 0); tt_uint_op(cert[i]->sig_ok, OP_EQ, 1); tt_uint_op(cert[i]->cert_expired, OP_EQ, 0); tt_uint_op(cert[i]->cert_valid, OP_EQ, 1); tt_int_op(cert[i]->cert_type, OP_EQ, 5); tt_mem_op(cert[i]->signed_key.pubkey, OP_EQ, &kp2.pubkey.pubkey, 32); tt_mem_op(cert[i]->signing_key.pubkey, OP_EQ, &kp1.pubkey.pubkey, 32); tt_int_op(cert[i]->signing_key_included, OP_EQ, i); tt_assert(cert[i]->encoded); tt_int_op(cert[i]->encoded_len, OP_EQ, 104 + 36 * i); tt_int_op(cert[i]->encoded[0], OP_EQ, 1); tt_int_op(cert[i]->encoded[1], OP_EQ, 5); parsed_cert[i] = tor_cert_parse(cert[i]->encoded, cert[i]->encoded_len); tt_assert(parsed_cert[i]); tt_int_op(cert[i]->encoded_len, OP_EQ, parsed_cert[i]->encoded_len); tt_mem_op(cert[i]->encoded, OP_EQ, parsed_cert[i]->encoded, cert[i]->encoded_len); tt_uint_op(parsed_cert[i]->sig_bad, OP_EQ, 0); tt_uint_op(parsed_cert[i]->sig_ok, OP_EQ, 0); tt_uint_op(parsed_cert[i]->cert_expired, OP_EQ, 0); tt_uint_op(parsed_cert[i]->cert_valid, OP_EQ, 0); /* Expired */ tt_int_op(tor_cert_checksig(parsed_cert[i], &kp1.pubkey, now + 30000), OP_LT, 0); tt_uint_op(parsed_cert[i]->cert_expired, OP_EQ, 1); parsed_cert[i]->cert_expired = 0; /* Wrong key */ tt_int_op(tor_cert_checksig(parsed_cert[i], &kp2.pubkey, now), OP_LT, 0); tt_uint_op(parsed_cert[i]->sig_bad, OP_EQ, 1); parsed_cert[i]->sig_bad = 0; /* Missing key */ int ok = tor_cert_checksig(parsed_cert[i], NULL, now); tt_int_op(ok < 0, OP_EQ, i == 0); tt_uint_op(parsed_cert[i]->sig_bad, OP_EQ, 0); tt_assert(parsed_cert[i]->sig_ok == (i != 0)); tt_assert(parsed_cert[i]->cert_valid == (i != 0)); parsed_cert[i]->sig_bad = 0; parsed_cert[i]->sig_ok = 0; parsed_cert[i]->cert_valid = 0; /* Right key */ tt_int_op(tor_cert_checksig(parsed_cert[i], &kp1.pubkey, now), OP_EQ, 0); tt_uint_op(parsed_cert[i]->sig_bad, OP_EQ, 0); tt_uint_op(parsed_cert[i]->sig_ok, OP_EQ, 1); tt_uint_op(parsed_cert[i]->cert_expired, OP_EQ, 0); tt_uint_op(parsed_cert[i]->cert_valid, OP_EQ, 1); } /* Now try some junky certs. */ /* - Truncated */ nocert = tor_cert_parse(cert[0]->encoded, cert[0]->encoded_len-1); tt_ptr_op(NULL, OP_EQ, nocert); /* - First byte modified */ cert[0]->encoded[0] = 99; nocert = tor_cert_parse(cert[0]->encoded, cert[0]->encoded_len); tt_ptr_op(NULL, OP_EQ, nocert); cert[0]->encoded[0] = 1; /* - Extra byte at the end*/ junk = tor_malloc_zero(cert[0]->encoded_len + 1); memcpy(junk, cert[0]->encoded, cert[0]->encoded_len); nocert = tor_cert_parse(junk, cert[0]->encoded_len+1); tt_ptr_op(NULL, OP_EQ, nocert); /* - Multiple signing key instances */ tor_free(junk); junk = tor_malloc_zero(104 + 36 * 2); junk[0] = 1; /* version */ junk[1] = 5; /* cert type */ junk[6] = 1; /* key type */ junk[39] = 2; /* n_extensions */ junk[41] = 32; /* extlen */ junk[42] = 4; /* exttype */ junk[77] = 32; /* extlen */ junk[78] = 4; /* exttype */ nocert = tor_cert_parse(junk, 104 + 36 * 2); tt_ptr_op(NULL, OP_EQ, nocert); done: tor_cert_free(cert[0]); tor_cert_free(cert[1]); tor_cert_free(parsed_cert[0]); tor_cert_free(parsed_cert[1]); tor_cert_free(nocert); tor_free(junk); tor_free(base64); }