Skip to content

Commit

Permalink
Merge pull request bitcoin-core#7 from nkohen/ecdsa_with_entropy
Browse files Browse the repository at this point in the history
Added signing with entropy specified to JNI
  • Loading branch information
nkohen authored Jul 28, 2020
2 parents e023925 + c9bab11 commit 551d4dd
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 0 deletions.
48 changes: 48 additions & 0 deletions src/java/org/bitcoin/NativeSecp256k1.java
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,52 @@ public static byte[] sign(byte[] data, byte[] seckey) throws AssertFailException
return retVal == 0 ? new byte[0] : sigArr;
}

/**
* libsecp256k1 Create an ECDSA signature adding specified entropy.
*
* This can be used to include your own entropy to nonce generation
* in addition to the message and private key, while still doing so deterministically.
*
* In particular, this is used when generating low R signatures.
* See https://github.com/bitcoin/bitcoin/pull/13666/
*
* @param data Message hash, 32 bytes
* @param seckey ECDSA Secret key, 32 bytes
* @param entropy 32 bytes of entropy
* @return sig byte array of signature
*/
public static byte[] signWithEntropy(byte[] data, byte[] seckey, byte[] entropy) throws AssertFailException{
checkArgument(data.length == 32 && seckey.length == 32 && entropy.length == 32);

ByteBuffer byteBuff = nativeECDSABuffer.get();
if (byteBuff == null || byteBuff.capacity() < 32 + 32 + 32) {
byteBuff = ByteBuffer.allocateDirect(32 + 32 + 32);
byteBuff.order(ByteOrder.nativeOrder());
nativeECDSABuffer.set(byteBuff);
}
byteBuff.rewind();
byteBuff.put(data);
byteBuff.put(seckey);
byteBuff.put(entropy);

byte[][] retByteArray;

r.lock();
try {
retByteArray = secp256k1_ecdsa_sign_with_entropy(byteBuff, Secp256k1Context.getContext());
} finally {
r.unlock();
}

byte[] sigArr = retByteArray[0];
int sigLen = new BigInteger(new byte[] { retByteArray[1][0] }).intValue();
int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();

assertEquals(sigArr.length, sigLen, "Got bad signature length.");

return retVal == 0 ? new byte[0] : sigArr;
}

/**
* libsecp256k1 Seckey Verify - Verifies an ECDSA secret key
*
Expand Down Expand Up @@ -482,6 +528,8 @@ public static synchronized boolean randomize(byte[] seed) {

private static native byte[][] secp256k1_ecdsa_sign(ByteBuffer byteBuff, long context);

private static native byte[][] secp256k1_ecdsa_sign_with_entropy(ByteBuffer byteBuff, long context);

private static native int secp256k1_ec_seckey_verify(ByteBuffer byteBuff, long context);

private static native byte[][] secp256k1_ec_pubkey_create(ByteBuffer byteBuff, long context, boolean compressed);
Expand Down
31 changes: 31 additions & 0 deletions src/java/org/bitcoin/NativeSecp256k1Test.java
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,33 @@ public static void testSignNeg() throws AssertFailException{
assertEquals( sigString, "" , "testSignNeg");
}

/**
* This tests signWithEntropy() for a valid secretkey
*/
public static void testSignWithEntropyPos() throws AssertFailException{

byte[] data = DatatypeConverter.parseHexBinary("53702647283D86B3D6410ADEF184EC608372CC3DD8B9202795D731EB1EA54275");
byte[] sec = DatatypeConverter.parseHexBinary("B4F62DE42D38D5D24B66FF01761C3FD0A6E7C8B719E0DC54D168FA013BFAF97F");
byte[] entropy = DatatypeConverter.parseHexBinary("EDF312C904B610B11442320FFB94C4F976831051A481A17176CE2B81EB3A8B6F");

byte[] resultArr = NativeSecp256k1.signWithEntropy(data, sec, entropy);
String sigString = DatatypeConverter.printHexBinary(resultArr);
assertEquals( sigString, "30450221009D9714BE0CE9A3FD08497125C6D01362FDE2FF118FC817FDB14EE4C38CADFB7A022033B082E161F7D75ABC25642ED71226049DC59EC14AB19DF2A8EFEA47A6C75FAC" , "testSignWithEntropyPos");
}

/**
* This tests signWithEntropy() for a invalid secretkey
*/
public static void testSignWithEntropyNeg() throws AssertFailException{
byte[] data = DatatypeConverter.parseHexBinary("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90"); //sha256hash of "testing"
byte[] sec = DatatypeConverter.parseHexBinary("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
byte[] entropy = DatatypeConverter.parseHexBinary("EDF312C904B610B11442320FFB94C4F976831051A481A17176CE2B81EB3A8B6F");

byte[] resultArr = NativeSecp256k1.signWithEntropy(data, sec, entropy);
String sigString = DatatypeConverter.printHexBinary(resultArr);
assertEquals( sigString, "" , "testSignWithEntropyNeg");
}

/**
* This tests private key tweak-add
*/
Expand Down Expand Up @@ -232,6 +259,10 @@ public static void main(String[] args) throws AssertFailException{
testSignPos();
testSignNeg();

//Test signWithEntropy() success/fail
testSignWithEntropyPos();
testSignWithEntropyNeg();

//Test privKeyTweakAdd()
testPrivKeyTweakAdd();

Expand Down
43 changes: 43 additions & 0 deletions src/java/org_bitcoin_NativeSecp256k1.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,49 @@ SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1e
return retArray;
}

SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign_1with_1entropy
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
{
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
unsigned char* secKey = (unsigned char*) (data + 32);
unsigned char* entropy = (unsigned char*) (secKey + 32);

jobjectArray retArray;
jbyteArray sigArray, intsByteArray;
unsigned char intsarray[2];

secp256k1_ecdsa_signature sig;

int ret = secp256k1_ecdsa_sign(ctx, &sig, data, secKey, NULL, entropy);

unsigned char outputSer[72];
size_t outputLen = 72;

if( ret ) {
int ret2 = secp256k1_ecdsa_signature_serialize_der(ctx,outputSer, &outputLen, &sig ); (void)ret2;
}

intsarray[0] = outputLen;
intsarray[1] = ret;

retArray = (*env)->NewObjectArray(env, 2,
(*env)->FindClass(env, "[B"),
(*env)->NewByteArray(env, 1));

sigArray = (*env)->NewByteArray(env, outputLen);
(*env)->SetByteArrayRegion(env, sigArray, 0, outputLen, (jbyte*)outputSer);
(*env)->SetObjectArrayElement(env, retArray, 0, sigArray);

intsByteArray = (*env)->NewByteArray(env, 2);
(*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
(*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);

(void)classObject;

return retArray;
}

SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1seckey_1verify
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
{
Expand Down
8 changes: 8 additions & 0 deletions src/java/org_bitcoin_NativeSecp256k1.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 551d4dd

Please sign in to comment.