Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add signRecoverable to the JNI interface. #517

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added .stamp-java
Empty file.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ env:
- EXTRAFLAGS=CPPFLAGS=-DDETERMINISTIC
- EXTRAFLAGS=CFLAGS=-O0
- BUILD=check-java JNI=yes ECDH=yes EXPERIMENTAL=yes
- BUILD=check-java JNI=yes ECDH=yes EXPERIMENTAL=yes RECOVERY=yes
matrix:
fast_finish: true
include:
Expand Down
4 changes: 4 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ JAVA_GUAVA=$(srcdir)/$(JAVAROOT)/guava/guava-18.0.jar
CLASSPATH_ENV=CLASSPATH=$(JAVA_GUAVA)
JAVA_FILES= \
$(JAVAROOT)/$(JAVAORG)/NativeSecp256k1.java \
$(JAVAROOT)/$(JAVAORG)/NativeSecp256k1RecoveryTest.java \
$(JAVAROOT)/$(JAVAORG)/NativeSecp256k1Test.java \
$(JAVAROOT)/$(JAVAORG)/NativeSecp256k1Util.java \
$(JAVAROOT)/$(JAVAORG)/Secp256k1Context.java
Expand All @@ -145,6 +146,9 @@ if USE_TESTS

check-java: libsecp256k1.la $(JAVA_GUAVA) .stamp-java
$(AM_V_at)java -Djava.library.path="./:./src:./src/.libs:.libs/" -cp "$(JAVA_GUAVA):$(JAVAROOT)" $(JAVAORG)/NativeSecp256k1Test
if ENABLE_MODULE_RECOVERY
$(AM_V_at)java -Djava.library.path="./:./src:./src/.libs:.libs/" -cp "$(JAVA_GUAVA):$(JAVAROOT)" $(JAVAORG)/NativeSecp256k1RecoveryTest
endif

endif
endif
Expand Down
Binary file added src/java/guava/guava-18.0.jar
Binary file not shown.
Binary file added src/java/org/bitcoin/NativeSecp256k1.class
Binary file not shown.
42 changes: 42 additions & 0 deletions src/java/org/bitcoin/NativeSecp256k1.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,46 @@ public static byte[] sign(byte[] data, byte[] sec) throws AssertFailException{
return retVal == 0 ? new byte[0] : sigArr;
}

/**
* libsecp256k1 Create a recoverable ECDSA signature.
*
* @param data Message hash, 32 bytes
* @param sec Secret key, at most 32 bytes
*
* Return values
* @return byte array of signature in compact form (64 bytes + recovery id)
*/
public static byte[] signRecoverable(byte[] data, byte[] sec) throws AssertFailException{
Preconditions.checkArgument(data.length == 32 && sec.length <= 32);

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

byte[][] retByteArray;

r.lock();
try {
retByteArray = secp256k1_ecdsa_sign_recoverable(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 - returns 1 if valid, 0 if invalid
*
Expand Down Expand Up @@ -435,6 +475,8 @@ public static synchronized boolean randomize(byte[] seed) throws AssertFailExcep

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

private static native byte[][] secp256k1_ecdsa_sign_recoverable(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);
Expand Down
53 changes: 53 additions & 0 deletions src/java/org/bitcoin/NativeSecp256k1RecoveryTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package org.bitcoin;

import com.google.common.io.BaseEncoding;
import java.util.Arrays;
import java.math.BigInteger;
import javax.xml.bind.DatatypeConverter;
import static org.bitcoin.NativeSecp256k1Util.*;

/**
* This class holds test cases defined for testing this library.
*/
public class NativeSecp256k1RecoveryTest {
/**
* This tests signRecoverable() for a valid secretkey. Unlike sign, which uses DER format,
* signRecoverable uses compact format for the signature.
*/
public static void testSignRecoverablePos() throws AssertFailException{

byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing"
byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());

byte[] resultArr = NativeSecp256k1.signRecoverable(data, sec);
String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
assertEquals( sigString, "182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A21C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E901" , "testSignRecoverablePos");
}

/**
* This tests sign() for a invalid secretkey
*/
public static void testSignRecoverableNeg() throws AssertFailException{
byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing"
byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase());

byte[] resultArr = NativeSecp256k1.signRecoverable(data, sec);
String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
assertEquals( sigString, "" , "testSignRecoverableNeg");
}

public static void main(String[] args) throws AssertFailException{
System.out.println("\n libsecp256k1 enabled: " + Secp256k1Context.isEnabled() + "\n");

assertEquals( Secp256k1Context.isEnabled(), true, "isEnabled" );

//Test signRecoverable() success/fail
testSignRecoverablePos();
testSignRecoverableNeg();

NativeSecp256k1.cleanup();

System.out.println(" All recovery tests passed." );

}
}
26 changes: 26 additions & 0 deletions src/java/org/bitcoin/NativeSecp256k1Test.java
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,32 @@ public static void testSignNeg() throws AssertFailException{
assertEquals( sigString, "" , "testSignNeg");
}

/**
* This tests signRecoverable() for a valid secretkey. Unlike sign, which uses DER format,
* signRecoverable uses compact format for the signature.
*/
public static void testSignRecoverablePos() throws AssertFailException{

byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing"
byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());

byte[] resultArr = NativeSecp256k1.signRecoverable(data, sec);
String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
assertEquals( sigString, "182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A21C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E900" , "testSignRecoverablePos");
}

/**
* This tests sign() for a invalid secretkey
*/
public static void testSignRecoverableNeg() throws AssertFailException{
byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing"
byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase());

byte[] resultArr = NativeSecp256k1.signRecoverable(data, sec);
String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
assertEquals( sigString, "" , "testSignRecoverableNeg");
}

/**
* This tests private key tweak-add
*/
Expand Down
Binary file not shown.
Binary file added src/java/org/bitcoin/NativeSecp256k1Util.class
Binary file not shown.
Binary file added src/java/org/bitcoin/Secp256k1Context.class
Binary file not shown.
44 changes: 44 additions & 0 deletions src/java/org_bitcoin_NativeSecp256k1.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,50 @@ SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1e
return retArray;
}

SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign_1recoverable
(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);

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

secp256k1_ecdsa_recoverable_signature sig[65];

int ret = secp256k1_ecdsa_sign_recoverable(ctx, sig, data, secKey, NULL, NULL );

unsigned char outputSer[65];
size_t outputLen = 65;
int recid = 0;

if( ret ) {
int ret2 = secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx,outputSer, &recid, sig ); (void)ret2;
}
outputSer[64] = recid;

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.