Parcourir la source

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?)
Nick Mathewson il y a 9 ans
Parent
commit
4caa6fad4c

+ 58 - 0
src/common/crypto_ed25519.c

@@ -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,

+ 9 - 0
src/common/crypto_ed25519.h

@@ -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

+ 5 - 0
src/ext/ed25519/ref10/ed25519_ref10.h

@@ -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

+ 37 - 0
src/ext/ed25519/ref10/keyconv.c

@@ -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;
+}

+ 2 - 1
src/ext/include.am

@@ -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 \

+ 40 - 0
src/test/test_crypto.c

@@ -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