Skip to content

JNI ECDSA adaptor sigs #2

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

Closed
wants to merge 5 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,4 @@ build-aux/compile
build-aux/test-driver
src/stamp-h1
libsecp256k1.pc
.idea
5 changes: 5 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -181,3 +181,8 @@ endif
if ENABLE_MODULE_RECOVERY
include src/modules/recovery/Makefile.am.include
endif

if ENABLE_MODULE_ECDSA_ADAPTOR
include src/modules/ecdsa_adaptor/Makefile.am.include
endif

11 changes: 11 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ AC_ARG_ENABLE(module_recovery,
[enable_module_recovery=$enableval],
[enable_module_recovery=no])

AC_ARG_ENABLE(module_ecdsa-adaptor,
AS_HELP_STRING([--enable-module-ecdsa-adaptor],[enable ECDSA adaptor module [default=no]]),
[enable_module_ecdsa_adaptor=$enableval],
[enable_module_ecdsa_adaptor=no])

AC_ARG_ENABLE(jni,
AS_HELP_STRING([--enable-jni],[enable libsecp256k1_jni (default is no)]),
[use_jni=$enableval],
Expand Down Expand Up @@ -435,6 +440,10 @@ if test x"$enable_module_recovery" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module])
fi

if test x"$enable_module_ecdsa_adaptor" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_ECDSA_ADAPTOR, 1, [Define this symbol to enable the ECDSA adaptor module])
fi

AC_C_BIGENDIAN()

if test x"$use_external_asm" = x"yes"; then
Expand All @@ -451,6 +460,7 @@ AC_MSG_NOTICE([Building benchmarks: $use_benchmark])
AC_MSG_NOTICE([Building for coverage analysis: $enable_coverage])
AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
AC_MSG_NOTICE([Building ECDSA pubkey recovery module: $enable_module_recovery])
AC_MSG_NOTICE([Building ECDSA adaptor module: $enable_module_ecdsa_adaptor])
AC_MSG_NOTICE([Using jni: $use_jni])

if test x"$enable_experimental" = x"yes"; then
Expand Down Expand Up @@ -482,6 +492,7 @@ AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"])
AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_ECDSA_ADAPTOR], [test x"$enable_module_ecdsa_adaptor" = x"yes"])
AM_CONDITIONAL([USE_JNI], [test x"$use_jni" == x"yes"])
AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"])
AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"])
Expand Down
87 changes: 87 additions & 0 deletions include/secp256k1_ecdsa_adaptor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#ifndef SECP256K1_ECDSA_ADAPTOR_H
#define SECP256K1_ECDSA_ADAPTOR_H

#ifdef __cplusplus
extern "C" {
#endif

/* note: adaptor_proof97 is 97 because 1*R' + 1*e + 1*s_dleq = 33 + 32 + 32 */

/** Adaptor sign
* TODO: add nonce function
*
* Returns: 1 on success, 0 on failure
* Args: ctx: a secp256k1 context object
* Out: adaptor_sig65: pointer to 65-byte to store the returned signature
* adaptor_proof97: pointer to 97-byte to store the adaptor proof
* In: adaptor: adaptor point
* msg32: the 32-byte message to sign
*
*/
SECP256K1_API int secp256k1_ecdsa_adaptor_sign(
const secp256k1_context* ctx,
unsigned char *adaptor_sig65,
unsigned char *adaptor_proof97,
const secp256k1_pubkey *adaptor,
const unsigned char *msg32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);

/** Adaptor verify
*
* Returns: 1 on success, 0 on failure
* Args: ctx: a secp256k1 context object
* In: adaptor_sig65: pointer to 65-byte signature to verify
* pubkey: pubkey
* msg32: 32-byte message
* adaptor: adaptor point
* adaptor_proof97: pointer to 97-byte adaptor proof
*
*/
SECP256K1_API int secp256k1_ecdsa_adaptor_sig_verify(
const secp256k1_context* ctx,
const unsigned char *adaptor_sig65,
const secp256k1_pubkey *pubkey,
const unsigned char *msg32,
const secp256k1_pubkey *adaptor,
const unsigned char *adaptor_proof97
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);

/** Adapt aka complete
*
* Returns: 1 on success, 0 on failure
* Args: ctx: a secp256k1 context object
* Out: sig: ecdsa signature
* In: adaptor_secret32: pointer to 32-byte byte adaptor secret of the adaptor point
* adaptor_sig65: pointer to 65-byte byte adaptor sig
*
*/
SECP256K1_API int secp256k1_ecdsa_adaptor_adapt(
const secp256k1_context* ctx,
secp256k1_ecdsa_signature *sig,
const unsigned char *adaptor_secret32,
const unsigned char *adaptor_sig65
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);

/** Adaptor extract
*
* Returns: 1 on success, 0 on failure
* Args: ctx: a secp256k1 context object
* Out: adaptor_secret32: pointer to 32-byte adaptor secret of the adaptor point
* In: sig: ecdsa signature to extract the adaptor_secret from
* adaptor_sig: adaptor sig to extract the adaptor_secret from
* adaptor: adaptor point
*
*/
SECP256K1_API int secp256k1_ecdsa_adaptor_extract_secret(
const secp256k1_context* ctx,
unsigned char *adaptor_secret32,
const secp256k1_ecdsa_signature *sig,
const unsigned char *adaptor_sig65,
const secp256k1_pubkey *adaptor
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);

#ifdef __cplusplus
}
#endif

#endif /* SECP256K1_ADAPTOR_H */
103 changes: 103 additions & 0 deletions src/java/org/bitcoin/NativeSecp256k1.java
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,104 @@ public static synchronized boolean randomize(byte[] seed) throws AssertFailExcep
}
}

public static boolean adaptorVerify(byte[] data, byte[] adaptorSig, byte[] pub, byte[] adaptorPoint, byte[] adaptorProof) throws AssertFailException{
/*
* In: adaptor_sig32: pointer to 32-byte signature to verify
* pubkey: pubkey
* msg32: 32-byte message
* adaptor: adaptor point
* adaptor_proof129: pointer to 129-byte adaptor proof
*/
Preconditions.checkArgument(data.length <= 32 &&
adaptorSig.length <= 65 &&
pub.length <= 520 &&
adaptorPoint.length <= 64 &&
adaptorProof.length <= 97);

ByteBuffer byteBuff = nativeECDSABuffer.get();
int buffLen = 32 + 65 + 520 + 64 + 97;
if (byteBuff == null || byteBuff.capacity() < buffLen) {
byteBuff = ByteBuffer.allocateDirect(buffLen);
byteBuff.order(ByteOrder.nativeOrder());
nativeECDSABuffer.set(byteBuff);
}
byteBuff.rewind();
byteBuff.put(data);
byteBuff.put(adaptorSig);
byteBuff.put(adaptorProof);
byteBuff.put(adaptorPoint);
byteBuff.put(pub);

r.lock();
try {
return secp256k1_ecdsa_adaptor_sig_verify(byteBuff, Secp256k1Context.getContext()) == 1;
} finally {
r.unlock();
}
}

public static byte[][] adaptorSign(byte[] data, byte[] adaptorPoint) throws AssertFailException{
Preconditions.checkArgument(data.length == 32 &&
adaptorPoint.length <= 64);

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

byte[][] retByteArray;

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

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

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

public static byte[] adaptorAdapt(byte[] adaptorSec, byte[] adaptorSig) throws AssertFailException{
Preconditions.checkArgument(adaptorSec.length <= 32 && adaptorSig.length <= 65);

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

byte[][] retByteArray;

r.lock();
try {
retByteArray = secp256k1_ecdsa_adaptor_adapt(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 adaptor signature length.");

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


private static native long secp256k1_ctx_clone(long context);

private static native int secp256k1_context_randomize(ByteBuffer byteBuff, long context);
Expand All @@ -443,4 +541,9 @@ public static synchronized boolean randomize(byte[] seed) throws AssertFailExcep

private static native byte[][] secp256k1_ecdh(ByteBuffer byteBuff, long context, int inputLen);

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

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

private static native byte[][] secp256k1_ecdsa_adaptor_adapt(ByteBuffer byteBuff, long context);
}
105 changes: 105 additions & 0 deletions src/java/org_bitcoin_NativeSecp256k1.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "include/secp256k1.h"
#include "include/secp256k1_ecdh.h"
#include "include/secp256k1_recovery.h"
#include "include/secp256k1_ecdsa_adaptor.h"


SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ctx_1clone
Expand Down Expand Up @@ -375,3 +376,107 @@ SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1e

return retArray;
}

/*
* Class: org_bitcoin_NativeSecp256k1
* Method: secp256k1_ecdsa_adaptor_sign
* Signature: (Ljava/nio/ByteBuffer;J)[[B
*/
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1adaptor_1sign
(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);
secp256k1_pubkey* adaptor = (secp256k1_pubkey*) (data + 32);

jobjectArray retArray;
jbyteArray sigArray, proofArray, intsByteArray;

unsigned char intsarray[1];
unsigned char adaptor_sig65[65];
unsigned char adaptor_proof129[97];

int ret = secp256k1_ecdsa_adaptor_sign(ctx, adaptor_sig65, adaptor_proof129, adaptor, data);

intsarray[0] = ret;

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

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

proofArray = (*env)->NewByteArray(env, 97);
(*env)->SetByteArrayRegion(env, proofArray, 0, 97, (jbyte*)adaptor_proof129);
(*env)->SetObjectArrayElement(env, retArray, 1, proofArray);

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

(void)classObject;

return retArray;
}

/*
* Class: org_bitcoin_NativeSecp256k1
* Method: secp256k1_ecdsa_adaptor_sig_verify
* Signature: (Ljava/nio/ByteBuffer;JII)I
*/
SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1adaptor_1sig_1verify
(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* adaptor_sig65 = (unsigned char*) (data + 32);
unsigned char* adaptor_proof129 = (unsigned char*) (adaptor_sig65 + 65);
secp256k1_pubkey* adaptor64 = (secp256k1_pubkey*) (adaptor_proof129 + 129);
secp256k1_pubkey* pubkey = (secp256k1_pubkey*) (adaptor64 + 64);

(void)classObject;

return secp256k1_ecdsa_adaptor_sig_verify(ctx, adaptor_sig65, pubkey, data, adaptor64, adaptor_proof129);
}

/*
* Class: org_bitcoin_NativeSecp256k1
* Method: secp256k1_ecdsa_adaptor_adapt
* Signature: (Ljava/nio/ByteBuffer;J)[[B
*/
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1adaptor_1adapt
(JNIEnv *env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
{
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
unsigned char *adaptor_secret32 = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
unsigned char *adaptor_sig65 = (unsigned char*) (adaptor_secret32 + 32);

jobjectArray retArray;
jbyteArray sigArray, intsByteArray;

unsigned char intsarray[1];
secp256k1_ecdsa_signature sig[72];

int ret = secp256k1_ecdsa_adaptor_adapt(ctx, sig, adaptor_secret32, adaptor_sig65);

intsarray[0] = ret;

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

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

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

(void)classObject;

return retArray;
}

Loading