Add curve25519->ed25519 key conversion per proposal 228

For proposal 228, we need to cross-certify our identity with our
curve25519 key, so that we can prove at descriptor-generation time
that we own that key.  But how can we sign something with a key that
is only for doing Diffie-Hellman?  By converting it to the
corresponding ed25519 point.

See the ALL-CAPS warning in the documentation.  According to djb
(IIUC), it is safe to use these keys in the ways that ntor and prop228
are using them, but it might not be safe if we start providing crazy
oracle access.

(Unit tests included.  What kind of a monster do you take me for?)
This commit is contained in:
Nick Mathewson 2014-08-27 00:18:26 -04:00
parent ed48b0fe56
commit 4caa6fad4c
6 changed files with 151 additions and 1 deletions

View File

@ -17,6 +17,8 @@
#include "ed25519/ref10/ed25519_ref10.h"
#include <openssl/sha.h>
int
ed25519_secret_key_generate(ed25519_secret_key_t *seckey_out,
int extra_strong)
@ -161,6 +163,62 @@ ed25519_checksig_batch(int *okay_out,
return res;
}
/**
* 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;
}
/**
* Given a curve25519 public key and sign bit of X coordinate of the ed25519
* public key, generate the corresponding ed25519 public key.
*/
int
ed25519_public_key_from_curve25519_public_key(ed25519_public_key_t *pubkey,
const curve25519_public_key_t *pubkey_in,
int signbit)
{
return ed25519_ref10_pubkey_from_curve25519_pubkey(pubkey->pubkey,
pubkey_in->public_key,
signbit);
}
/** DOCDOC */
int
ed25519_seckey_write_to_file(const ed25519_secret_key_t *seckey,

View File

@ -67,6 +67,15 @@ typedef struct {
int ed25519_checksig_batch(int *okay_out,
const ed25519_checkable_t *checkable,
int n_checkable);
int ed25519_keypair_from_curve25519_keypair(ed25519_keypair_t *out,
int *signbit_out,
const curve25519_keypair_t *inp);
int ed25519_public_key_from_curve25519_public_key(ed25519_public_key_t *pubkey,
const curve25519_public_key_t *pubkey_in,
int signbit);
#endif
#define ED25519_BASE64_LEN 43

View File

@ -16,4 +16,9 @@ int ed25519_ref10_sign(
const unsigned char *m,uint64_t mlen,
const unsigned char *sk, const unsigned char *pk);
/* Added in Tor */
int ed25519_ref10_pubkey_from_curve25519_pubkey(unsigned char *out,
const unsigned char *inp,
int signbit);
#endif

View File

@ -0,0 +1,37 @@
/* Added to ref10 for Tor. We place this in the public domain. Alternatively,
* you may have it under the Creative Commons 0 "CC0" license. */
#include "fe.h"
#include "ed25519_ref10.h"
int ed25519_ref10_pubkey_from_curve25519_pubkey(unsigned char *out,
const unsigned char *inp,
int signbit)
{
fe u;
fe one;
fe y;
fe uplus1;
fe uminus1;
fe inv_uplus1;
/* From prop228:
Given a curve25519 x-coordinate (u), we can get the y coordinate
of the ed25519 key using
y = (u-1)/(u+1)
*/
fe_frombytes(u, inp);
fe_1(one);
fe_sub(uminus1, u, one);
fe_add(uplus1, u, one);
fe_invert(inv_uplus1, uplus1);
fe_mul(y, uminus1, inv_uplus1);
fe_tobytes(out, y);
/* propagate sign. */
out[31] |= (!!signbit) << 7;
return 0;
}

View File

@ -56,7 +56,8 @@ src_ext_ed25519_ref10_libed25519_ref10_a_SOURCES= \
src/ext/ed25519/ref10/open.c \
src/ext/ed25519/ref10/sc_muladd.c \
src/ext/ed25519/ref10/sc_reduce.c \
src/ext/ed25519/ref10/sign.c
src/ext/ed25519/ref10/sign.c \
src/ext/ed25519/ref10/keyconv.c
ED25519_REF10_HDRS = \
src/ext/ed25519/ref10/api.h \

View File

@ -1369,6 +1369,45 @@ test_crypto_ed25519_encode(void *arg)
tor_free(mem_op_hex_tmp);
}
static void
test_crypto_ed25519_convert(void *arg)
{
const uint8_t msg[] =
"The eyes are not here / There are no eyes here.";
const int N = 30;
int i;
(void)arg;
for (i = 0; i < N; ++i) {
curve25519_keypair_t curve25519_keypair;
ed25519_keypair_t ed25519_keypair;
ed25519_public_key_t ed25519_pubkey;
int bit=0;
ed25519_signature_t sig;
tt_int_op(0,==,curve25519_keypair_generate(&curve25519_keypair, i&1));
tt_int_op(0,==,ed25519_keypair_from_curve25519_keypair(
&ed25519_keypair, &bit, &curve25519_keypair));
tt_int_op(0,==,ed25519_public_key_from_curve25519_public_key(
&ed25519_pubkey, &curve25519_keypair.pubkey, bit));
tt_mem_op(ed25519_pubkey.pubkey, ==, ed25519_keypair.pubkey.pubkey, 32);
tt_int_op(0,==,ed25519_sign(&sig, msg, sizeof(msg), &ed25519_keypair));
tt_int_op(0,==,ed25519_checksig(&sig, msg, sizeof(msg),
&ed25519_pubkey));
tt_int_op(-1,==,ed25519_checksig(&sig, msg, sizeof(msg)-1,
&ed25519_pubkey));
sig.sig[0] ^= 15;
tt_int_op(-1,==,ed25519_checksig(&sig, msg, sizeof(msg),
&ed25519_pubkey));
}
done:
;
}
static void
test_crypto_siphash(void *arg)
{
@ -1509,6 +1548,7 @@ struct testcase_t crypto_tests[] = {
{ "ed25519_simple", test_crypto_ed25519_simple, 0, NULL, NULL },
{ "ed25519_test_vectors", test_crypto_ed25519_test_vectors, 0, NULL, NULL },
{ "ed25519_encode", test_crypto_ed25519_encode, 0, NULL, NULL },
{ "ed25519_convert", test_crypto_ed25519_convert, 0, NULL, NULL },
#endif
{ "siphash", test_crypto_siphash, 0, NULL, NULL },
END_OF_TESTCASES