aboutsummaryrefslogtreecommitdiffstats
path: root/jni/ed25519/additions/curve_sigs.c
diff options
context:
space:
mode:
authorMoxie Marlinspike <moxie@thoughtcrime.org>2014-11-24 12:54:30 -0800
committerMoxie Marlinspike <moxie@thoughtcrime.org>2014-11-24 12:54:30 -0800
commit60800e155612bea797eed93c67046a23d26054cc (patch)
treed88368c1c26162e27e790195133ca2b526597afe /jni/ed25519/additions/curve_sigs.c
Break out into separate repo.
Diffstat (limited to 'jni/ed25519/additions/curve_sigs.c')
-rw-r--r--jni/ed25519/additions/curve_sigs.c116
1 files changed, 116 insertions, 0 deletions
diff --git a/jni/ed25519/additions/curve_sigs.c b/jni/ed25519/additions/curve_sigs.c
new file mode 100644
index 00000000..51f2052d
--- /dev/null
+++ b/jni/ed25519/additions/curve_sigs.c
@@ -0,0 +1,116 @@
+#include <string.h>
+#include "ge.h"
+#include "curve_sigs.h"
+#include "crypto_sign.h"
+
+void curve25519_keygen(unsigned char* curve25519_pubkey_out,
+ const unsigned char* curve25519_privkey_in)
+{
+ ge_p3 ed; /* Ed25519 pubkey point */
+ fe ed_y, ed_y_plus_one, one_minus_ed_y, inv_one_minus_ed_y;
+ fe mont_x;
+
+ /* Perform a fixed-base multiplication of the Edwards base point,
+ (which is efficient due to precalculated tables), then convert
+ to the Curve25519 montgomery-format public key. In particular,
+ convert Curve25519's "montgomery" x-coordinate into an Ed25519
+ "edwards" y-coordinate:
+
+ mont_x = (ed_y + 1) / (1 - ed_y)
+
+ with projective coordinates:
+
+ mont_x = (ed_y + ed_z) / (ed_z - ed_y)
+
+ NOTE: ed_y=1 is converted to mont_x=0 since fe_invert is mod-exp
+ */
+
+ ge_scalarmult_base(&ed, curve25519_privkey_in);
+ fe_add(ed_y_plus_one, ed.Y, ed.Z);
+ fe_sub(one_minus_ed_y, ed.Z, ed.Y);
+ fe_invert(inv_one_minus_ed_y, one_minus_ed_y);
+ fe_mul(mont_x, ed_y_plus_one, inv_one_minus_ed_y);
+ fe_tobytes(curve25519_pubkey_out, mont_x);
+}
+
+int curve25519_sign(unsigned char* signature_out,
+ const unsigned char* curve25519_privkey,
+ const unsigned char* msg, const unsigned long msg_len,
+ const unsigned char* random)
+{
+ ge_p3 ed_pubkey_point; /* Ed25519 pubkey point */
+ unsigned char ed_pubkey[32]; /* Ed25519 encoded pubkey */
+ unsigned char sigbuf[MAX_MSG_LEN + 128]; /* working buffer */
+ unsigned char sign_bit = 0;
+
+ if (msg_len > MAX_MSG_LEN) {
+ memset(signature_out, 0, 64);
+ return -1;
+ }
+
+ /* Convert the Curve25519 privkey to an Ed25519 public key */
+ ge_scalarmult_base(&ed_pubkey_point, curve25519_privkey);
+ ge_p3_tobytes(ed_pubkey, &ed_pubkey_point);
+ sign_bit = ed_pubkey[31] & 0x80;
+
+ /* Perform an Ed25519 signature with explicit private key */
+ crypto_sign_modified(sigbuf, msg, msg_len, curve25519_privkey,
+ ed_pubkey, random);
+ memmove(signature_out, sigbuf, 64);
+
+ /* Encode the sign bit into signature (in unused high bit of S) */
+ signature_out[63] &= 0x7F; /* bit should be zero already, but just in case */
+ signature_out[63] |= sign_bit;
+ return 0;
+}
+
+int curve25519_verify(const unsigned char* signature,
+ const unsigned char* curve25519_pubkey,
+ const unsigned char* msg, const unsigned long msg_len)
+{
+ fe mont_x, mont_x_minus_one, mont_x_plus_one, inv_mont_x_plus_one;
+ fe one;
+ fe ed_y;
+ unsigned char ed_pubkey[32];
+ unsigned long long some_retval;
+ unsigned char verifybuf[MAX_MSG_LEN + 64]; /* working buffer */
+ unsigned char verifybuf2[MAX_MSG_LEN + 64]; /* working buffer #2 */
+
+ if (msg_len > MAX_MSG_LEN) {
+ return -1;
+ }
+
+ /* Convert the Curve25519 public key into an Ed25519 public key. In
+ particular, convert Curve25519's "montgomery" x-coordinate into an
+ Ed25519 "edwards" y-coordinate:
+
+ ed_y = (mont_x - 1) / (mont_x + 1)
+
+ NOTE: mont_x=-1 is converted to ed_y=0 since fe_invert is mod-exp
+
+ Then move the sign bit into the pubkey from the signature.
+ */
+ fe_frombytes(mont_x, curve25519_pubkey);
+ fe_1(one);
+ fe_sub(mont_x_minus_one, mont_x, one);
+ fe_add(mont_x_plus_one, mont_x, one);
+ fe_invert(inv_mont_x_plus_one, mont_x_plus_one);
+ fe_mul(ed_y, mont_x_minus_one, inv_mont_x_plus_one);
+ fe_tobytes(ed_pubkey, ed_y);
+
+ /* Copy the sign bit, and remove it from signature */
+ ed_pubkey[31] &= 0x7F; /* bit should be zero already, but just in case */
+ ed_pubkey[31] |= (signature[63] & 0x80);
+ memmove(verifybuf, signature, 64);
+ verifybuf[63] &= 0x7F;
+
+ memmove(verifybuf+64, msg, msg_len);
+
+ /* Then perform a normal Ed25519 verification, return 0 on success */
+ /* The below call has a strange API: */
+ /* verifybuf = R || S || message */
+ /* verifybuf2 = internal to next call gets a copy of verifybuf, S gets
+ replaced with pubkey for hashing, then the whole thing gets zeroized
+ (if bad sig), or contains a copy of msg (good sig) */
+ return crypto_sign_open(verifybuf2, &some_retval, verifybuf, 64 + msg_len, ed_pubkey);
+}