diff --git a/libaxolotl/jni/curve25519-jni.c b/libaxolotl/jni/curve25519-jni.c index 305370ac69..5b4cc2f432 100644 --- a/libaxolotl/jni/curve25519-jni.c +++ b/libaxolotl/jni/curve25519-jni.c @@ -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); diff --git a/libaxolotl/jni/ed25519/additions/curve_sigs.c b/libaxolotl/jni/ed25519/additions/curve_sigs.c index b002dc8748..b92b136cd3 100644 --- a/libaxolotl/jni/ed25519/additions/curve_sigs.c +++ b/libaxolotl/jni/ed25519/additions/curve_sigs.c @@ -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); } diff --git a/libaxolotl/jni/ed25519/additions/curve_sigs.h b/libaxolotl/jni/ed25519/additions/curve_sigs.h index 706f7db644..97d8a165f8 100644 --- a/libaxolotl/jni/ed25519/additions/curve_sigs.h +++ b/libaxolotl/jni/ed25519/additions/curve_sigs.h @@ -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 diff --git a/libaxolotl/jni/ed25519/additions/sha512.c b/libaxolotl/jni/ed25519/additions/sha512.c index 9540ef467f..b3c3867fd9 100644 --- a/libaxolotl/jni/ed25519/additions/sha512.c +++ b/libaxolotl/jni/ed25519/additions/sha512.c @@ -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; } - diff --git a/libaxolotl/jni/ed25519/additions/sign_modified.c b/libaxolotl/jni/ed25519/additions/sign_modified.c index c4b2d6f23e..5bc5cc7d5e 100644 --- a/libaxolotl/jni/ed25519/additions/sign_modified.c +++ b/libaxolotl/jni/ed25519/additions/sign_modified.c @@ -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; } diff --git a/libaxolotl/jni/ed25519/additions/zeroize.c b/libaxolotl/jni/ed25519/additions/zeroize.c new file mode 100644 index 0000000000..a0de1ef2fe --- /dev/null +++ b/libaxolotl/jni/ed25519/additions/zeroize.c @@ -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; +} diff --git a/libaxolotl/jni/ed25519/additions/zeroize.h b/libaxolotl/jni/ed25519/additions/zeroize.h new file mode 100644 index 0000000000..f4318795f6 --- /dev/null +++ b/libaxolotl/jni/ed25519/additions/zeroize.h @@ -0,0 +1,6 @@ +#ifndef __ZEROIZE_H__ +#define __ZEROIZE_H__ + +void zeroize(unsigned char* b, unsigned long len); + +#endif diff --git a/libaxolotl/jni/ed25519/main/main.c b/libaxolotl/jni/ed25519/main/main.c index 657b59f39a..ab451bf4e6 100644 --- a/libaxolotl/jni/ed25519/main/main.c +++ b/libaxolotl/jni/ed25519/main/main.c @@ -1,6 +1,6 @@ #include #include -#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; } diff --git a/libaxolotl/libs/armeabi-v7a/libcurve25519.so b/libaxolotl/libs/armeabi-v7a/libcurve25519.so index fdde5d0672..1dd438f09c 100755 Binary files a/libaxolotl/libs/armeabi-v7a/libcurve25519.so and b/libaxolotl/libs/armeabi-v7a/libcurve25519.so differ diff --git a/libaxolotl/libs/armeabi/libcurve25519.so b/libaxolotl/libs/armeabi/libcurve25519.so index 7cecff027c..714f898191 100755 Binary files a/libaxolotl/libs/armeabi/libcurve25519.so and b/libaxolotl/libs/armeabi/libcurve25519.so differ diff --git a/libaxolotl/libs/x86/libcurve25519.so b/libaxolotl/libs/x86/libcurve25519.so index 55b968a35d..1bff1ccf1e 100755 Binary files a/libaxolotl/libs/x86/libcurve25519.so and b/libaxolotl/libs/x86/libcurve25519.so differ diff --git a/libaxolotl/src/main/java/org/whispersystems/libaxolotl/ecc/Curve25519.java b/libaxolotl/src/main/java/org/whispersystems/libaxolotl/ecc/Curve25519.java index 8c8d7ef9ce..18152d395a 100644 --- a/libaxolotl/src/main/java/org/whispersystems/libaxolotl/ecc/Curve25519.java +++ b/libaxolotl/src/main/java/org/whispersystems/libaxolotl/ecc/Curve25519.java @@ -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); + } + } + }