Update to latest ref10-extract ed25519

This commit is contained in:
Moxie Marlinspike 2014-07-24 11:24:54 -07:00
parent 5ea3b3038e
commit 1eb3884b7a
12 changed files with 188 additions and 54 deletions

View File

@ -76,17 +76,19 @@ JNIEXPORT jbyteArray JNICALL Java_org_whispersystems_libaxolotl_ecc_Curve25519_c
}
JNIEXPORT jbyteArray JNICALL Java_org_whispersystems_libaxolotl_ecc_Curve25519_calculateSignature
(JNIEnv *env, jclass clazz, jbyteArray privateKey, jbyteArray message)
(JNIEnv *env, jclass clazz, jbyteArray random, jbyteArray privateKey, jbyteArray message)
{
jbyteArray signature = (*env)->NewByteArray(env, 64);
uint8_t* signatureBytes = (uint8_t*)(*env)->GetByteArrayElements(env, signature, 0);
uint8_t* randomBytes = (uint8_t*)(*env)->GetByteArrayElements(env, random, 0);
uint8_t* privateKeyBytes = (uint8_t*)(*env)->GetByteArrayElements(env, privateKey, 0);
uint8_t* messageBytes = (uint8_t*)(*env)->GetByteArrayElements(env, message, 0);
jsize messageLength = (*env)->GetArrayLength(env, message);
curve25519_sign(signatureBytes, privateKeyBytes, messageBytes, messageLength);
curve25519_sign(signatureBytes, privateKeyBytes, messageBytes, messageLength, randomBytes);
(*env)->ReleaseByteArrayElements(env, signature, signatureBytes, 0);
(*env)->ReleaseByteArrayElements(env, random, randomBytes, 0);
(*env)->ReleaseByteArrayElements(env, privateKey, privateKeyBytes, 0);
(*env)->ReleaseByteArrayElements(env, message, messageBytes, 0);

View File

@ -4,11 +4,10 @@
#include "crypto_sign.h"
void curve25519_keygen(unsigned char* curve25519_pubkey_out,
unsigned char* curve25519_privkey_in)
const unsigned char* curve25519_privkey_in)
{
ge_p3 ed_pubkey_point; /* Ed25519 pubkey point */
unsigned char ed_pubkey[32]; /* privkey followed by pubkey */
fe ed_y, one, ed_y_plus_one, one_minus_ed_y, inv_one_minus_ed_y;
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,
@ -17,49 +16,49 @@ void curve25519_keygen(unsigned char* curve25519_pubkey_out,
convert Curve25519's "montgomery" x-coordinate into an Ed25519
"edwards" y-coordinate:
mont_x = (ed_y +1 1) / (1 - ed_y)
mont_x = (ed_y + 1) / (1 - ed_y)
with projective coordinates:
mont_x = (ed_y + ed_z) / (ed_z - ed_y)
*/
ge_scalarmult_base(&ed_pubkey_point, curve25519_privkey_in);
ge_p3_tobytes(ed_pubkey, &ed_pubkey_point);
ed_pubkey[31] = ed_pubkey[31] & 0x7F; /* Mask off sign bit */
fe_frombytes(ed_y, ed_pubkey);
fe_1(one);
fe_add(ed_y_plus_one, ed_y, one);
fe_sub(one_minus_ed_y, one, ed_y);
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);
fe_tobytes(curve25519_pubkey_out, mont_x);
}
void curve25519_sign(unsigned char* signature_out,
unsigned char* curve25519_privkey,
unsigned char* msg, unsigned long msg_len)
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_keypair[64]; /* privkey followed by pubkey */
unsigned char ed_pubkey[32]; /* Ed25519 encoded pubkey */
unsigned char sigbuf[msg_len + 64]; /* working buffer */
unsigned long long sigbuf_out_len = 0;
unsigned char sign_bit = 0;
/* Convert the Curve25519 privkey to an Ed25519 keypair */
memmove(ed_keypair, curve25519_privkey, 32);
/* Convert the Curve25519 privkey to an Ed25519 public key */
ge_scalarmult_base(&ed_pubkey_point, curve25519_privkey);
ge_p3_tobytes(ed_keypair + 32, &ed_pubkey_point);
sign_bit = ed_keypair[63] & 0x80;
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, &sigbuf_out_len, msg, msg_len, ed_keypair);
crypto_sign_modified(sigbuf, &sigbuf_out_len, 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] |= sign_bit;
}
int curve25519_verify(unsigned char* signature,
unsigned char* curve25519_pubkey,
unsigned char* msg, unsigned long msg_len)
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;
@ -87,11 +86,15 @@ int curve25519_verify(unsigned char* signature,
/* Copy the sign bit, and remove it from signature */
ed_pubkey[31] |= (signature[63] & 0x80);
signature[63] &= 0x7F;
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 */
return crypto_sign_open(verifybuf2, &some_retval, verifybuf, 64 + msg_len, ed_pubkey);
}

View File

@ -2,24 +2,45 @@
#ifndef __CURVE_SIGS_H__
#define __CURVE_SIGS_H__
void curve25519_keygen(unsigned char* curve25519_pubkey_out,
unsigned char* curve25519_privkey_in);
void curve25519_keygen(unsigned char* curve25519_pubkey_out, /* 32 bytes */
const unsigned char* curve25519_privkey_in); /* 32 bytes */
void curve25519_sign(unsigned char* signature_out,
unsigned char* curve25519_privkey,
unsigned char* msg, unsigned long msg_len);
void curve25519_sign(unsigned char* signature_out, /* 64 bytes */
const unsigned char* curve25519_privkey, /* 32 bytes */
const unsigned char* msg, const unsigned long msg_len,
const unsigned char* random); /* 64 bytes */
/* returns 0 on success */
int curve25519_verify(unsigned char* signature,
unsigned char* curve25519_pubkey,
unsigned char* msg, unsigned long msg_len);
int curve25519_verify(const unsigned char* signature, /* 64 bytes */
const unsigned char* curve25519_pubkey, /* 32 bytes */
const unsigned char* msg, const unsigned long msg_len);
/* helper function - modified version of crypto_sign() to use
explicit private key */
explicit private key. In particular:
sk : private key
pk : public key
m : message
prefix : 0xFE || [0xFF]*31
q : main subgroup order
The prefix is chosen to distinguish the two SHA512 uses below, since
prefix is an invalid encoding for R (it would encode a "field element"
of 2^255 - 2). 0xFF*32 is set aside for use in ECDH protocols, which
is why the first byte here ix 0xFE.
sig_nonce = (random XOR SHA512(prefix || sk || m)) % q
R = g^sig_nonce
M = SHA512(R || pk || m)
S = sig_nonce + (m * sk)
signature = (R || S)
*/
int crypto_sign_modified(
unsigned char *sm,unsigned long long *smlen,
const unsigned char *m,unsigned long long mlen,
const unsigned char *sk
const unsigned char *sk, /* Curve/Ed25519 private key */
const unsigned char *pk, /* Ed25519 public key */
const unsigned char *random /* 64 bytes random to XOR into nonce */
);
#endif

View File

@ -1,5 +1,6 @@
#include "sha512.h"
#include "sph_sha2.h"
#include "zeroize.h"
int crypto_hash_sha512_ref(unsigned char *output ,const unsigned char *input,
unsigned long long len)
@ -8,6 +9,6 @@ int crypto_hash_sha512_ref(unsigned char *output ,const unsigned char *input,
sph_sha512_init(&ctx);
sph_sha512(&ctx, input, len);
sph_sha512_close(&ctx, output);
zeroize((unsigned char*)&ctx, sizeof(ctx));
return 0;
}

View File

@ -3,6 +3,7 @@
#include "crypto_hash_sha512.h"
#include "ge.h"
#include "sc.h"
#include "zeroize.h"
/* NEW: Compare to pristine crypto_sign()
Uses explicit private key for nonce derivation and as scalar,
@ -11,23 +12,31 @@
int crypto_sign_modified(
unsigned char *sm,unsigned long long *smlen,
const unsigned char *m,unsigned long long mlen,
const unsigned char *sk
const unsigned char *sk, const unsigned char* pk,
const unsigned char* random
)
{
unsigned char pk[32];
unsigned char az[64];
unsigned char nonce[64];
unsigned char hram[64];
ge_p3 R;
memmove(pk,sk + 32,32);
int count=0;
*smlen = mlen + 64;
memmove(sm + 64,m,mlen);
memmove(sm + 32,sk,32); /* NEW: Use privkey directly for nonce derivation */
crypto_hash_sha512(nonce,sm + 32,mlen + 32);
/* NEW : add prefix to separate hash uses - see .h */
sm[0] = 0xFE;
for (count = 1; count < 32; count++)
sm[count] = 0xFF;
crypto_hash_sha512(nonce,sm,mlen + 64);
memmove(sm + 32,pk,32);
/* NEW: XOR random into nonce */
for (count=0; count < 64; count++)
nonce[count] ^= random[count];
sc_reduce(nonce);
ge_scalarmult_base(&R,nonce);
ge_p3_tobytes(sm,&R);
@ -36,5 +45,11 @@ int crypto_sign_modified(
sc_reduce(hram);
sc_muladd(sm + 32,hram,sk,nonce); /* NEW: Use privkey directly */
/* NEW: Dummy call to hopefully erase any traces of privkey or
nonce left in the stack from prev call to this func. */
volatile unsigned char* p = sm+64;
sc_muladd(sm+64,hram,hram,hram);
zeroize(nonce, 64);
return 0;
}

View File

@ -0,0 +1,10 @@
void zeroize(unsigned char* b, unsigned long len)
{
unsigned long count = 0;
unsigned long retval = 0;
volatile unsigned char *p = b;
for (count = 0; count < len; count++)
p[count] = 0;
}

View File

@ -0,0 +1,6 @@
#ifndef __ZEROIZE_H__
#define __ZEROIZE_H__
void zeroize(unsigned char* b, unsigned long len);
#endif

View File

@ -1,6 +1,6 @@
#include <stdio.h>
#include <string.h>
#include "sha512.h"
#include "crypto_hash_sha512.h"
#include "curve_sigs.h"
int main(int argc, char* argv[])
@ -10,6 +10,7 @@ int main(int argc, char* argv[])
unsigned char signature[64];
unsigned char msg[100];
unsigned long long msg_len = 100;
unsigned char random[64];
/* Initialize pubkey, privkey, msg */
memset(msg, 0, 100);
@ -21,21 +22,84 @@ int main(int argc, char* argv[])
privkey[8] = 189; /* just so there's some bits set */
/* SHA512 test */
unsigned char sha512_input[112] = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu";
unsigned char sha512_correct_output[64] =
{
0x8E, 0x95, 0x9B, 0x75, 0xDA, 0xE3, 0x13, 0xDA,
0x8C, 0xF4, 0xF7, 0x28, 0x14, 0xFC, 0x14, 0x3F,
0x8F, 0x77, 0x79, 0xC6, 0xEB, 0x9F, 0x7F, 0xA1,
0x72, 0x99, 0xAE, 0xAD, 0xB6, 0x88, 0x90, 0x18,
0x50, 0x1D, 0x28, 0x9E, 0x49, 0x00, 0xF7, 0xE4,
0x33, 0x1B, 0x99, 0xDE, 0xC4, 0xB5, 0x43, 0x3A,
0xC7, 0xD3, 0x29, 0xEE, 0xB6, 0xDD, 0x26, 0x54,
0x5E, 0x96, 0xE5, 0x5B, 0x87, 0x4B, 0xE9, 0x09
};
unsigned char sha512_actual_output[64];
crypto_hash_sha512_ref(sha512_actual_output, sha512_input, sizeof(sha512_input));
if (memcmp(sha512_actual_output, sha512_correct_output, 64) != 0)
printf("SHA512 bad #1\n");
else
printf("SHA512 good #1\n");
sha512_input[111] ^= 1;
crypto_hash_sha512_ref(sha512_actual_output, sha512_input, sizeof(sha512_input));
if (memcmp(sha512_actual_output, sha512_correct_output, 64) != 0)
printf("SHA512 good #2\n");
else
printf("SHA512 bad #2\n");
/* Signature test */
curve25519_keygen(pubkey, privkey);
curve25519_sign(signature, privkey, msg, msg_len);
curve25519_sign(signature, privkey, msg, msg_len, random);
if (curve25519_verify(signature, pubkey, msg, msg_len) == 0)
printf("success #1\n");
printf("Signature good #1\n");
else
printf("failure #1\n");
printf("Signature bad #1\n");
signature[0] ^= 1;
if (curve25519_verify(signature, pubkey, msg, msg_len) == 0)
printf("failure #2\n");
printf("Signature bad #2\n");
else
printf("success #2\n");
printf("Signature good #2\n");
printf("Random testing...\n");
for (int count = 0; count < 10000; count++) {
unsigned char b[64];
crypto_hash_sha512_ref(b, privkey, 32);
memmove(privkey, b, 32);
crypto_hash_sha512_ref(b, privkey, 32);
memmove(random, b, 64);
privkey[0] &= 248;
privkey[31] &= 63;
privkey[31] |= 64;
curve25519_keygen(pubkey, privkey);
curve25519_sign(signature, privkey, msg, msg_len, random);
if (curve25519_verify(signature, pubkey, msg, msg_len) != 0) {
printf("failure #1 %d\n", count);
return -1;
}
if (b[63] & 1)
signature[count % 64] ^= 1;
else
msg[count % 100] ^= 1;
if (curve25519_verify(signature, pubkey, msg, msg_len) == 0) {
printf("failure #2 %d\n", count);
return -1;
}
}
printf("OK\n");
return 1;
}

Binary file not shown.

View File

@ -39,7 +39,7 @@ public class Curve25519 {
private static native byte[] generatePublicKey(byte[] privateKey);
private static native byte[] generatePrivateKey(byte[] random, boolean ephemeral);
private static native byte[] calculateSignature(byte[] privateKey, byte[] message);
private static native byte[] calculateSignature(byte[] random, byte[] privateKey, byte[] message);
private static native boolean verifySignature(byte[] publicKey, byte[] message, byte[] signature);
public static ECKeyPair generateKeyPair(boolean ephemeral) {
@ -55,7 +55,8 @@ public class Curve25519 {
}
static byte[] calculateSignature(ECPrivateKey privateKey, byte[] message) {
return calculateSignature(((DjbECPrivateKey)privateKey).getPrivateKey(), message);
byte[] random = getRandom(32);
return calculateSignature(random, ((DjbECPrivateKey)privateKey).getPrivateKey(), message);
}
static boolean verifySignature(ECPublicKey publicKey, byte[] message, byte[] signature) {
@ -83,4 +84,15 @@ public class Curve25519 {
return generatePrivateKey(privateKey, ephemeral);
}
private static byte[] getRandom(int size) {
try {
byte[] random = new byte[size];
SecureRandom.getInstance("SHA1PRNG").nextBytes(random);
return random;
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
}
}