JS ed25519

This commit is contained in:
Matt Corallo 2014-07-20 08:54:00 +00:00
parent b94ba6b726
commit b5c6e3d101
18 changed files with 172 additions and 145 deletions

View File

@ -77,11 +77,11 @@ window.textsecure.crypto = function() {
if (textsecure.nacl.USE_NACL) {
return textsecure.nacl.postNaclMessage({command: "bytesToPriv", priv: privKey}).then(function(message) {
var priv = message.res;
var priv = message.res.slice(0, 32);
if (!isIdentity)
new Uint8Array(priv)[0] |= 0x01;
return textsecure.nacl.postNaclMessage({command: "privToPub", priv: priv}).then(function(message) {
return { pubKey: prependVersion(message.res), privKey: priv };
return { pubKey: prependVersion(message.res.slice(0, 32)), privKey: priv };
});
});
} else {
@ -239,21 +239,25 @@ window.textsecure.crypto = function() {
/*****************************
*** Internal Crypto stuff ***
*****************************/
var validatePubKeyFormat = function(pubKey) {
if (pubKey === undefined || ((pubKey.byteLength != 33 || new Uint8Array(pubKey)[0] != 5) && pubKey.byteLength != 32))
throw new Error("Invalid public key");
if (pubKey.byteLength == 33)
return pubKey.slice(1);
else
console.error("WARNING: Expected pubkey of length 33, please report the ST and client that generated the pubkey");
}
testing_only.ECDHE = function(pubKey, privKey) {
if (privKey === undefined || privKey.byteLength != 32)
throw new Error("Invalid private key");
if (pubKey === undefined || ((pubKey.byteLength != 33 || new Uint8Array(pubKey)[0] != 5) && pubKey.byteLength != 32))
throw new Error("Invalid public key");
if (pubKey.byteLength == 33)
pubKey = pubKey.slice(1);
else
console.error("WARNING: Expected pubkey of length 33, please report the ST and client that generated the pubkey");
pubKey = validatePubKeyFormat(pubKey);
return new Promise(function(resolve) {
if (textsecure.nacl.USE_NACL) {
textsecure.nacl.postNaclMessage({command: "ECDHE", priv: privKey, pub: pubKey}).then(function(message) {
resolve(message.res);
resolve(message.res.slice(0, 32));
});
} else {
resolve(toArrayBuffer(curve25519(new Uint16Array(privKey), new Uint16Array(pubKey))));
@ -262,6 +266,49 @@ window.textsecure.crypto = function() {
}
var ECDHE = function(pubKey, privKey) { return testing_only.ECDHE(pubKey, privKey); }
testing_only.Ed25519Sign = function(privKey, message) {
if (privKey === undefined || privKey.byteLength != 32)
throw new Error("Invalid private key");
if (message === undefined)
throw new Error("Invalid message");
if (textsecure.nacl.USE_NACL) {
return textsecure.nacl.postNaclMessage({command: "Ed25519Sign", priv: privKey, msg: message}).then(function(message) {
return message.res;
});
} else {
throw new Error("Ed25519 in JS not yet supported");
}
}
var Ed25519Sign = function(privKey, pubKeyToSign) {
pubKeyToSign = validatePubKeyFormat(pubKeyToSign);
return testing_only.Ed25519Sign(privKey, pubKeyToSign);
}
testing_only.Ed25519Verify = function(pubKey, msg, sig) {
pubKey = validatePubKeyFormat(pubKey);
if (msg === undefined)
throw new Error("Invalid message");
if (sig === undefined || sig.byteLength != 64)
throw new Error("Invalid signature");
if (textsecure.nacl.USE_NACL) {
return textsecure.nacl.postNaclMessage({command: "Ed25519Verify", pub: pubKey, msg: msg, sig: sig}).then(function(message) {
if (!message.res)
throw new Error("Invalid signature");
});
} else {
throw new Error("Ed25519 in JS not yet supported");
}
}
var Ed25519Verify = function(pubKey, signedPubKey, sig) {
signedPubKey = validatePubKeyFormat(signedPubKey);
return testing_only.Ed25519Verify(pubKey, signedPubKey, sig);
}
testing_only.HKDF = function(input, salt, info) {
// Specific implementation of RFC 5869 that only returns exactly 64 bytes
return HmacSHA256(salt, input).then(function(PRK) {

View File

@ -163,7 +163,7 @@ textsecure.registerOnLoadFunction(function() {
target[0] &= 248;
target[31] &= 127;
target[31] |= 64;
if (String.fromCharCode.apply(null, new Uint8Array(aliceKeyPair.privKey)) != String.fromCharCode.apply(null, target))
if (getString(aliceKeyPair.privKey) != getString(target))
return false;
return textsecure.crypto.testing_only.privToPub(bob_priv, true).then(function(bobKeyPair) {
@ -171,21 +171,21 @@ textsecure.registerOnLoadFunction(function() {
target[0] &= 248;
target[31] &= 127;
target[31] |= 64;
if (String.fromCharCode.apply(null, new Uint8Array(bobKeyPair.privKey)) != String.fromCharCode.apply(null, target))
if (getString(bobKeyPair.privKey) != getString(target))
return false;
if (String.fromCharCode.apply(null, new Uint8Array(aliceKeyPair.pubKey)) != String.fromCharCode.apply(null, new Uint8Array(alice_pub)))
if (getString(aliceKeyPair.pubKey) != getString(alice_pub))
return false;
if (String.fromCharCode.apply(null, new Uint8Array(bobKeyPair.pubKey)) != String.fromCharCode.apply(null, new Uint8Array(bob_pub)))
if (getString(bobKeyPair.pubKey) != getString(bob_pub))
return false;
return textsecure.crypto.testing_only.ECDHE(bobKeyPair.pubKey, aliceKeyPair.privKey).then(function(ss) {
if (String.fromCharCode.apply(null, new Uint16Array(ss)) != String.fromCharCode.apply(null, new Uint16Array(shared_sec)))
if (getString(ss) != getString(shared_sec))
return false;
return textsecure.crypto.testing_only.ECDHE(aliceKeyPair.pubKey, bobKeyPair.privKey).then(function(ss) {
if (String.fromCharCode.apply(null, new Uint16Array(ss)) != String.fromCharCode.apply(null, new Uint16Array(shared_sec)))
if (getString(ss) != getString(shared_sec))
return false;
else
return true;
@ -193,7 +193,30 @@ textsecure.registerOnLoadFunction(function() {
});
});
});
}, "Simple Curve25519 test vector");
}, "Simple Curve25519 test vectors");
TEST(function() {
// Some self-generated test vectors
var priv = hexToArrayBuffer("48a8892cc4e49124b7b57d94fa15becfce071830d6449004685e387c62409973");
var pub = hexToArrayBuffer("0555f1bfede27b6a03e0dd389478ffb01462e5c52dbbac32cf870f00af1ed9af3a");
var msg = hexToArrayBuffer("617364666173646661736466");
var sig = hexToArrayBuffer("2bc06c745acb8bae10fbc607ee306084d0c28e2b3bb819133392473431291fd0"+
"dfa9c7f11479996cf520730d2901267387e08d85bbf2af941590e3035a545285");
return textsecure.crypto.testing_only.privToPub(priv, false).then(function(pubCalc) {
//if (getString(pub) != getString(pubCalc))
// return false;
return textsecure.crypto.testing_only.Ed25519Sign(priv, msg).then(function(sigCalc) {
if (getString(sig) != getString(sigCalc))
return false;
return textsecure.crypto.testing_only.Ed25519Verify(pub, msg, sig).then(function() {
return true;
});
});
});
}, "Simple Ed25519 tests");
// TextSecure implements a slightly tweaked version of RFC 5869 and thus this test fails
// If you tweak the HKDF as noted in the comment there, this test passes

View File

@ -5,7 +5,7 @@ include $(NACL_SDK_ROOT)/tools/common.mk
TARGET = curve25519
LIBS = ppapi_cpp ppapi
CFLAGS = -Wall -Ied25519/nacl_includes -Ied25519 -Ied25519/sha512
CFLAGS = -Wall -Werror -Ied25519/nacl_includes -Ied25519 -Ied25519/sha512
SOURCES = curve25519-donna.c curve25519-donna-wrapper.cpp $(wildcard ed25519/*.c) $(wildcard ed25519/additions/*.c) ed25519/sha512/sha2big.c
# Build rules generated by macros from common.mk:

View File

@ -15,6 +15,7 @@
*/
#include "curve25519-donna.h"
#include "ed25519/additions/curve_sigs.h"
#include <ppapi/cpp/instance.h>
#include <ppapi/cpp/module.h>
@ -26,6 +27,37 @@
const unsigned char basepoint[32] = {9};
template<int Length>
class AutoArrayBufferObject {
private:
pp::VarArrayBuffer buf;
unsigned char* map;
public:
AutoArrayBufferObject(pp::Var v) : buf(v), map(NULL) {}
unsigned char *Get() {
if (map)
return map;
if (buf.is_null())
return NULL;
if (Length > 0 && buf.ByteLength() != Length)
return NULL;
map = static_cast<unsigned char*>(buf.Map());
return map;
}
long GetLength() {
return buf.is_null() ? -1 : buf.ByteLength();
}
~AutoArrayBufferObject() {
if (map)
buf.Unmap();
}
};
class Curve25519Instance : public pp::Instance {
public:
explicit Curve25519Instance(PP_Instance instance) : pp::Instance(instance) {}
@ -35,37 +67,61 @@ public:
return; // Go away broken client
pp::VarDictionary dictionary(var_message);
pp::VarArrayBuffer privArrBuff(dictionary.Get("priv"));
if (privArrBuff.is_null() || privArrBuff.ByteLength() != 32)
return; // Go away broken client
unsigned char* priv = static_cast<unsigned char*>(privArrBuff.Map());
std::string command = dictionary.Get("command").AsString();
pp::VarArrayBuffer resBuffer(32);
pp::VarDictionary returnMessage;
pp::VarArrayBuffer resBuffer(64);
unsigned char* res = static_cast<unsigned char*>(resBuffer.Map());
std::string command = dictionary.Get("command").AsString();
if (command == "bytesToPriv") {
memcpy(res, priv, 32);
AutoArrayBufferObject<32> priv(dictionary.Get("priv"));
if (!priv.Get())
return; // Go away broken client
memcpy(res, priv.Get(), 32);
res[0] &= 248;
res[31] &= 127;
res[31] |= 64;
} else if (command == "privToPub") {
curve25519_donna(res, priv, basepoint);
AutoArrayBufferObject<32> priv(dictionary.Get("priv"));
if (!priv.Get())
return; // Go away broken client
curve25519_donna(res, priv.Get(), basepoint);
} else if (command == "ECDHE") {
pp::VarArrayBuffer pubArrBuff(dictionary.Get("pub"));
if (!pubArrBuff.is_null() && pubArrBuff.ByteLength() == 32) {
unsigned char* pub = static_cast<unsigned char*>(pubArrBuff.Map());
curve25519_donna(res, priv, pub);
pubArrBuff.Unmap();
}
AutoArrayBufferObject<32> priv(dictionary.Get("priv"));
AutoArrayBufferObject<32> pub(dictionary.Get("pub"));
if (!priv.Get() || !pub.Get())
return; // Go away broken client
curve25519_donna(res, priv.Get(), pub.Get());
} else if (command == "Ed25519Sign") {
AutoArrayBufferObject<32> priv(dictionary.Get("priv"));
AutoArrayBufferObject<-1> msg(dictionary.Get("msg"));
if (!priv.Get() || !msg.Get())
return; // Go away broken client
curve25519_sign(res, priv.Get(), msg.Get(), msg.GetLength());
}
resBuffer.Unmap();
privArrBuff.Unmap();
pp::VarDictionary returnMessage;
if (command != "Ed25519Verify")
returnMessage.Set("res", resBuffer);
else {
AutoArrayBufferObject<32> pub(dictionary.Get("pub"));
AutoArrayBufferObject<-1> msg(dictionary.Get("msg"));
AutoArrayBufferObject<64> sig(dictionary.Get("sig"));
if (!pub.Get() || !msg.Get() || !sig.Get())
return; // Go away broken client
bool res = curve25519_verify(sig.Get(), pub.Get(), msg.Get(), msg.GetLength()) == 0;
returnMessage.Set("res", res);
}
returnMessage.Set("call_id", dictionary.Get("call_id").AsInt());
returnMessage.Set("res", resBuffer);
PostMessage(returnMessage);
}
};

View File

@ -1,111 +0,0 @@
/**
* Copyright (C) 2013-2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <stdint.h>
#include <jni.h>
#include "curve25519-donna.h"
#include "curve_sigs.h"
JNIEXPORT jbyteArray JNICALL Java_org_whispersystems_libaxolotl_ecc_Curve25519_generatePrivateKey
(JNIEnv *env, jclass clazz, jbyteArray random, jboolean ephemeral)
{
uint8_t* privateKey = (uint8_t*)(*env)->GetByteArrayElements(env, random, 0);
privateKey[0] &= 248;
if (ephemeral) {
privateKey[0] |= 1;
}
privateKey[31] &= 127;
privateKey[31] |= 64;
(*env)->ReleaseByteArrayElements(env, random, privateKey, 0);
return random;
}
JNIEXPORT jbyteArray JNICALL Java_org_whispersystems_libaxolotl_ecc_Curve25519_generatePublicKey
(JNIEnv *env, jclass clazz, jbyteArray privateKey)
{
static const uint8_t basepoint[32] = {9};
jbyteArray publicKey = (*env)->NewByteArray(env, 32);
uint8_t* publicKeyBytes = (uint8_t*)(*env)->GetByteArrayElements(env, publicKey, 0);
uint8_t* privateKeyBytes = (uint8_t*)(*env)->GetByteArrayElements(env, privateKey, 0);
curve25519_donna(publicKeyBytes, privateKeyBytes, basepoint);
(*env)->ReleaseByteArrayElements(env, publicKey, publicKeyBytes, 0);
(*env)->ReleaseByteArrayElements(env, privateKey, privateKeyBytes, 0);
return publicKey;
}
JNIEXPORT jbyteArray JNICALL Java_org_whispersystems_libaxolotl_ecc_Curve25519_calculateAgreement
(JNIEnv *env, jclass clazz, jbyteArray privateKey, jbyteArray publicKey)
{
jbyteArray sharedKey = (*env)->NewByteArray(env, 32);
uint8_t* sharedKeyBytes = (uint8_t*)(*env)->GetByteArrayElements(env, sharedKey, 0);
uint8_t* privateKeyBytes = (uint8_t*)(*env)->GetByteArrayElements(env, privateKey, 0);
uint8_t* publicKeyBytes = (uint8_t*)(*env)->GetByteArrayElements(env, publicKey, 0);
curve25519_donna(sharedKeyBytes, privateKeyBytes, publicKeyBytes);
(*env)->ReleaseByteArrayElements(env, sharedKey, sharedKeyBytes, 0);
(*env)->ReleaseByteArrayElements(env, publicKey, publicKeyBytes, 0);
(*env)->ReleaseByteArrayElements(env, privateKey, privateKeyBytes, 0);
return sharedKey;
}
JNIEXPORT jbyteArray JNICALL Java_org_whispersystems_libaxolotl_ecc_Curve25519_calculateSignature
(JNIEnv *env, jclass clazz, jbyteArray privateKey, jbyteArray message)
{
jbyteArray signature = (*env)->NewByteArray(env, 64);
uint8_t* signatureBytes = (uint8_t*)(*env)->GetByteArrayElements(env, signature, 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);
(*env)->ReleaseByteArrayElements(env, signature, signatureBytes, 0);
(*env)->ReleaseByteArrayElements(env, privateKey, privateKeyBytes, 0);
(*env)->ReleaseByteArrayElements(env, message, messageBytes, 0);
return signature;
}
JNIEXPORT jboolean JNICALL Java_org_whispersystems_libaxolotl_ecc_Curve25519_verifySignature
(JNIEnv *env, jclass clazz, jbyteArray publicKey, jbyteArray message, jbyteArray signature)
{
uint8_t* signatureBytes = (uint8_t*)(*env)->GetByteArrayElements(env, signature, 0);
uint8_t* publicKeyBytes = (uint8_t*)(*env)->GetByteArrayElements(env, publicKey, 0);
uint8_t* messageBytes = (uint8_t*)(*env)->GetByteArrayElements(env, message, 0);
jsize messageLength = (*env)->GetArrayLength(env, message);
jboolean result = (curve25519_verify(signatureBytes, publicKeyBytes, messageBytes, messageLength) == 0);
(*env)->ReleaseByteArrayElements(env, signature, signatureBytes, 0);
(*env)->ReleaseByteArrayElements(env, publicKey, publicKeyBytes, 0);
(*env)->ReleaseByteArrayElements(env, message, messageBytes, 0);
return result;
}

View File

@ -2,6 +2,10 @@
#ifndef __CURVE_SIGS_H__
#define __CURVE_SIGS_H__
#ifdef __cplusplus
extern "C" {
#endif
void curve25519_keygen(unsigned char* curve25519_pubkey_out,
unsigned char* curve25519_privkey_in);
@ -22,4 +26,8 @@ int crypto_sign_modified(
const unsigned char *sk
);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,6 +1,7 @@
# Updated by fix_deps.py
pnacl/Debug/curve25519-donna-wrapper_pnacl.o: \
curve25519-donna-wrapper.cpp curve25519-donna.h \
ed25519/additions/curve_sigs.h \
/home/matt/nacl_sdk/pepper_31//include/ppapi/cpp/instance.h \
/home/matt/nacl_sdk/pepper_31//include/ppapi/c/pp_instance.h \
/home/matt/nacl_sdk/pepper_31//include/ppapi/c/pp_macros.h \
@ -34,6 +35,7 @@ pnacl/Debug/curve25519-donna-wrapper_pnacl.o: \
:
curve25519-donna-wrapper.cpp:
curve25519-donna.h:
ed25519/additions/curve_sigs.h:
/home/matt/nacl_sdk/pepper_31//include/ppapi/cpp/instance.h:
/home/matt/nacl_sdk/pepper_31//include/ppapi/c/pp_instance.h:
/home/matt/nacl_sdk/pepper_31//include/ppapi/c/pp_macros.h:

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,6 +1,7 @@
# Updated by fix_deps.py
pnacl/Release/curve25519-donna-wrapper_pnacl.o: \
curve25519-donna-wrapper.cpp curve25519-donna.h \
ed25519/additions/curve_sigs.h \
/home/matt/nacl_sdk/pepper_31//include/ppapi/cpp/instance.h \
/home/matt/nacl_sdk/pepper_31//include/ppapi/c/pp_instance.h \
/home/matt/nacl_sdk/pepper_31//include/ppapi/c/pp_macros.h \
@ -34,6 +35,7 @@ pnacl/Release/curve25519-donna-wrapper_pnacl.o: \
:
curve25519-donna-wrapper.cpp:
curve25519-donna.h:
ed25519/additions/curve_sigs.h:
/home/matt/nacl_sdk/pepper_31//include/ppapi/cpp/instance.h:
/home/matt/nacl_sdk/pepper_31//include/ppapi/c/pp_instance.h:
/home/matt/nacl_sdk/pepper_31//include/ppapi/c/pp_macros.h:

Binary file not shown.