/** * Given a curve25519 keypair in <b>inp</b>, generate a corresponding * ed25519 keypair in <b>out</b>, and set <b>signbit_out</b> to the * sign bit of the X coordinate of the ed25519 key. * * NOTE THAT IT IS PROBABLY NOT SAFE TO USE THE GENERATED KEY FOR ANYTHING * OUTSIDE OF WHAT'S PRESENTED IN PROPOSAL 228. In particular, it's probably * not a great idea to use it to sign attacker-supplied anything. */ int ed25519_keypair_from_curve25519_keypair(ed25519_keypair_t *out, int *signbit_out, const curve25519_keypair_t *inp) { const char string[] = "Derive high part of ed25519 key from curve25519 key"; ed25519_public_key_t pubkey_check; SHA512_CTX ctx; uint8_t sha512_output[64]; memcpy(out->seckey.seckey, inp->seckey.secret_key, 32); SHA512_Init(&ctx); SHA512_Update(&ctx, out->seckey.seckey, 32); SHA512_Update(&ctx, string, sizeof(string)); SHA512_Final(sha512_output, &ctx); memcpy(out->seckey.seckey + 32, sha512_output, 32); ed25519_public_key_generate(&out->pubkey, &out->seckey); *signbit_out = out->pubkey.pubkey[31] >> 7; ed25519_public_key_from_curve25519_public_key(&pubkey_check, &inp->pubkey, *signbit_out); tor_assert(fast_memeq(pubkey_check.pubkey, out->pubkey.pubkey, 32)); memwipe(&pubkey_check, 0, sizeof(pubkey_check)); memwipe(&ctx, 0, sizeof(ctx)); memwipe(sha512_output, 0, sizeof(sha512_output)); return 0; }
static void bench_ed25519_impl(void) { uint64_t start, end; const int iters = 1<<12; int i; const uint8_t msg[] = "but leaving, could not tell what they had heard"; ed25519_signature_t sig; ed25519_keypair_t kp; curve25519_keypair_t curve_kp; ed25519_public_key_t pubkey_tmp; ed25519_secret_key_generate(&kp.seckey, 0); start = perftime(); for (i = 0; i < iters; ++i) { ed25519_public_key_generate(&kp.pubkey, &kp.seckey); } end = perftime(); printf("Generate public key: %.2f usec\n", MICROCOUNT(start, end, iters)); start = perftime(); for (i = 0; i < iters; ++i) { ed25519_sign(&sig, msg, sizeof(msg), &kp); } end = perftime(); printf("Sign a short message: %.2f usec\n", MICROCOUNT(start, end, iters)); start = perftime(); for (i = 0; i < iters; ++i) { ed25519_checksig(&sig, msg, sizeof(msg), &kp.pubkey); } end = perftime(); printf("Verify signature: %.2f usec\n", MICROCOUNT(start, end, iters)); curve25519_keypair_generate(&curve_kp, 0); start = perftime(); for (i = 0; i < iters; ++i) { ed25519_public_key_from_curve25519_public_key(&pubkey_tmp, &curve_kp.pubkey, 1); } end = perftime(); printf("Convert public point from curve25519: %.2f usec\n", MICROCOUNT(start, end, iters)); curve25519_keypair_generate(&curve_kp, 0); start = perftime(); for (i = 0; i < iters; ++i) { ed25519_public_blind(&pubkey_tmp, &kp.pubkey, msg); } end = perftime(); printf("Blind a public key: %.2f usec\n", MICROCOUNT(start, end, iters)); }
/** Generate a new ed25519 keypair in <b>keypair_out</b>. If * <b>extra_strong</b> is set, try to mix some system entropy into the key * generation process. Return 0 on success, -1 on failure. */ int ed25519_keypair_generate(ed25519_keypair_t *keypair_out, int extra_strong) { if (ed25519_secret_key_generate(&keypair_out->seckey, extra_strong) < 0) return -1; if (ed25519_public_key_generate(&keypair_out->pubkey, &keypair_out->seckey)<0) return -1; return 0; }
/** * Given an ed25519 keypair in <b>inp</b>, generate a corresponding * ed25519 keypair in <b>out</b>, blinded by the corresponding 32-byte input * in 'param'. * * Tor uses key blinding for the "next-generation" hidden services design: * service descriptors are encrypted with a key derived from the service's * long-term public key, and then signed with (and stored at a position * indexed by) a short-term key derived by blinding the long-term keys. */ int ed25519_keypair_blind(ed25519_keypair_t *out, const ed25519_keypair_t *inp, const uint8_t *param) { ed25519_public_key_t pubkey_check; get_ed_impl()->blind_secret_key(out->seckey.seckey, inp->seckey.seckey, param); ed25519_public_blind(&pubkey_check, &inp->pubkey, param); ed25519_public_key_generate(&out->pubkey, &out->seckey); tor_assert(fast_memeq(pubkey_check.pubkey, out->pubkey.pubkey, 32)); memwipe(&pubkey_check, 0, sizeof(pubkey_check)); return 0; }
/** * 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_crypto_ed25519_fuzz_donna(void *arg) { const unsigned iters = 1024; uint8_t msg[1024]; unsigned i; (void)arg; tt_uint_op(iters, OP_EQ, sizeof(msg)); crypto_rand((char*) msg, sizeof(msg)); /* Fuzz Ed25519-donna vs ref10, alternating the implementation used to * generate keys/sign per iteration. */ for (i = 0; i < iters; ++i) { const int use_donna = i & 1; uint8_t blinding[32]; curve25519_keypair_t ckp; ed25519_keypair_t kp, kp_blind, kp_curve25519; ed25519_public_key_t pk, pk_blind, pk_curve25519; ed25519_signature_t sig, sig_blind; int bit = 0; crypto_rand((char*) blinding, sizeof(blinding)); /* Impl. A: * 1. Generate a keypair. * 2. Blinded the keypair. * 3. Sign a message (unblinded). * 4. Sign a message (blinded). * 5. Generate a curve25519 keypair, and convert it to Ed25519. */ ed25519_set_impl_params(use_donna); tt_int_op(0, OP_EQ, ed25519_keypair_generate(&kp, i&1)); tt_int_op(0, OP_EQ, ed25519_keypair_blind(&kp_blind, &kp, blinding)); tt_int_op(0, OP_EQ, ed25519_sign(&sig, msg, i, &kp)); tt_int_op(0, OP_EQ, ed25519_sign(&sig_blind, msg, i, &kp_blind)); tt_int_op(0, OP_EQ, curve25519_keypair_generate(&ckp, i&1)); tt_int_op(0, OP_EQ, ed25519_keypair_from_curve25519_keypair( &kp_curve25519, &bit, &ckp)); /* Impl. B: * 1. Validate the public key by rederiving it. * 2. Validate the blinded public key by rederiving it. * 3. Validate the unblinded signature (and test a invalid signature). * 4. Validate the blinded signature. * 5. Validate the public key (from Curve25519) by rederiving it. */ ed25519_set_impl_params(!use_donna); tt_int_op(0, OP_EQ, ed25519_public_key_generate(&pk, &kp.seckey)); tt_mem_op(pk.pubkey, OP_EQ, kp.pubkey.pubkey, 32); tt_int_op(0, OP_EQ, ed25519_public_blind(&pk_blind, &kp.pubkey, blinding)); tt_mem_op(pk_blind.pubkey, OP_EQ, kp_blind.pubkey.pubkey, 32); tt_int_op(0, OP_EQ, ed25519_checksig(&sig, msg, i, &pk)); sig.sig[0] ^= 15; tt_int_op(-1, OP_EQ, ed25519_checksig(&sig, msg, sizeof(msg), &pk)); tt_int_op(0, OP_EQ, ed25519_checksig(&sig_blind, msg, i, &pk_blind)); tt_int_op(0, OP_EQ, ed25519_public_key_from_curve25519_public_key( &pk_curve25519, &ckp.pubkey, bit)); tt_mem_op(pk_curve25519.pubkey, OP_EQ, kp_curve25519.pubkey.pubkey, 32); } done: ; }