From 3ee2635d939ec589d268c0db31b452a76a56551a Mon Sep 17 00:00:00 2001 From: Fabrice Drouin Date: Thu, 2 Jul 2020 17:52:21 +0200 Subject: [PATCH] Upgrade JNI interface (#1) * Upgrade JNI interface * Input signatures can be DER or compact format, output signatures are always in compact format * Input public keys can be compressed or uncompressed, output public keys are always uncompressed * Name and parameters match libsecp256k1's * JNI implementation is now straightforward No more ByteBuffers Exceptions are thrown in case of failures * Update src/nativeMain/kotlin/fr/acinq/secp256k1/Secp256k1Native.kt * Add public key compression method Co-authored-by: Salomon BRYS --- jni/android/src/main/CMakeLists.txt | 2 +- .../secp256k1/jni/NativeSecp256k1Loader.kt | 2 +- jni/build.sh | 2 +- .../fr_acinq_secp256k1_Secp256k1CFunctions.h | 157 ++++ .../java/org_bitcoin_Secp256k1CFunctions.h | 157 ---- .../fr_acinq_secp256k1_Secp256k1CFunctions.c | 689 ++++++++++++++++++ jni/c/src/org_bitcoin_Secp256k1CFunctions.c | 658 ----------------- .../secp256k1/jni/NativeSecp256k1Loader.kt | 2 +- .../acinq/secp256k1/Secp256k1CFunctions.java | 67 ++ .../java/org/bitcoin/Secp256k1CFunctions.java | 24 - .../fr/acinq/secp256k1/NativeSecp256k1.kt | 86 +++ .../acinq/secp256k1}/NativeSecp256k1Util.kt | 2 +- .../acinq/secp256k1}/Secp256k1Context.kt | 4 +- .../kotlin/org/bitcoin/NativeSecp256k1.kt | 533 -------------- .../kotlin/fr/acinq/secp256k1/Secp256k1.kt | 35 +- .../fr/acinq/secp256k1/Secp256k1Native.kt | 100 +-- .../fr/acinq/secp256k1/Secp256k1Test.kt | 108 +-- 17 files changed, 1099 insertions(+), 1529 deletions(-) create mode 100644 jni/c/headers/java/fr_acinq_secp256k1_Secp256k1CFunctions.h delete mode 100644 jni/c/headers/java/org_bitcoin_Secp256k1CFunctions.h create mode 100644 jni/c/src/fr_acinq_secp256k1_Secp256k1CFunctions.c delete mode 100644 jni/c/src/org_bitcoin_Secp256k1CFunctions.c create mode 100644 jni/src/main/java/fr/acinq/secp256k1/Secp256k1CFunctions.java delete mode 100644 jni/src/main/java/org/bitcoin/Secp256k1CFunctions.java create mode 100644 jni/src/main/kotlin/fr/acinq/secp256k1/NativeSecp256k1.kt rename jni/src/main/kotlin/{org/bitcoin => fr/acinq/secp256k1}/NativeSecp256k1Util.kt (97%) rename jni/src/main/kotlin/{org/bitcoin => fr/acinq/secp256k1}/Secp256k1Context.kt (85%) delete mode 100644 jni/src/main/kotlin/org/bitcoin/NativeSecp256k1.kt diff --git a/jni/android/src/main/CMakeLists.txt b/jni/android/src/main/CMakeLists.txt index aca194d..dec6fd7 100644 --- a/jni/android/src/main/CMakeLists.txt +++ b/jni/android/src/main/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.10.0) add_library( secp256k1-jni SHARED - ${CMAKE_CURRENT_LIST_DIR}/../../../c/src/org_bitcoin_Secp256k1CFunctions.c + ${CMAKE_CURRENT_LIST_DIR}/../../../c/src/fr_acinq_secp256k1_Secp256k1CFunctions.c ) target_include_directories( secp256k1-jni diff --git a/jni/android/src/main/kotlin/fr/acinq/secp256k1/jni/NativeSecp256k1Loader.kt b/jni/android/src/main/kotlin/fr/acinq/secp256k1/jni/NativeSecp256k1Loader.kt index e5ecac1..bb761fd 100644 --- a/jni/android/src/main/kotlin/fr/acinq/secp256k1/jni/NativeSecp256k1Loader.kt +++ b/jni/android/src/main/kotlin/fr/acinq/secp256k1/jni/NativeSecp256k1Loader.kt @@ -1,7 +1,7 @@ package fr.acinq.secp256k1.jni import fr.acinq.secp256k1.Secp256k1 -import org.bitcoin.NativeSecp256k1 +import fr.acinq.secp256k1.NativeSecp256k1 public object NativeSecp256k1Loader { diff --git a/jni/build.sh b/jni/build.sh index a3add99..b56bb20 100755 --- a/jni/build.sh +++ b/jni/build.sh @@ -29,7 +29,7 @@ fi mkdir -p build/jni/$TARGET -$CC -shared $CC_OPTS -o build/jni/$TARGET/$OUTFILE c/src/org_bitcoin_Secp256k1CFunctions.c -Ic/headers/ -Ic/headers/java -Ic/headers/$JNI_HEADERS/ -I../native/secp256k1/ -lsecp256k1 -L../native/build/$TARGET/ $ADD_LIB +$CC -shared $CC_OPTS -o build/jni/$TARGET/$OUTFILE c/src/fr_acinq_secp256k1_Secp256k1CFunctions.c -Ic/headers/ -Ic/headers/java -Ic/headers/$JNI_HEADERS/ -I../native/secp256k1/ -lsecp256k1 -L../native/build/$TARGET/ $ADD_LIB [[ ! -z "$TO_UID" ]] && chown -R $TO_UID:$TO_UID . diff --git a/jni/c/headers/java/fr_acinq_secp256k1_Secp256k1CFunctions.h b/jni/c/headers/java/fr_acinq_secp256k1_Secp256k1CFunctions.h new file mode 100644 index 0000000..c1eb8c3 --- /dev/null +++ b/jni/c/headers/java/fr_acinq_secp256k1_Secp256k1CFunctions.h @@ -0,0 +1,157 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class fr_acinq_secp256k1_Secp256k1CFunctions */ + +#ifndef _Included_fr_acinq_secp256k1_Secp256k1CFunctions +#define _Included_fr_acinq_secp256k1_Secp256k1CFunctions +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: fr_acinq_secp256k1_Secp256k1CFunctions + * Method: secp256k1_context_create + * Signature: (I)J + */ +JNIEXPORT jlong JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1context_1create + (JNIEnv *, jclass, jint); + +/* + * Class: fr_acinq_secp256k1_Secp256k1CFunctions + * Method: secp256k1_context_destroy + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1context_1destroy + (JNIEnv *, jclass, jlong); + +/* + * Class: fr_acinq_secp256k1_Secp256k1CFunctions + * Method: secp256k1_ec_seckey_verify + * Signature: (J[B)I + */ +JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1seckey_1verify + (JNIEnv *, jclass, jlong, jbyteArray); + +/* + * Class: fr_acinq_secp256k1_Secp256k1CFunctions + * Method: secp256k1_ec_pubkey_parse + * Signature: (J[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1parse + (JNIEnv *, jclass, jlong, jbyteArray); + +/* + * Class: fr_acinq_secp256k1_Secp256k1CFunctions + * Method: secp256k1_ec_pubkey_create + * Signature: (J[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1create + (JNIEnv *, jclass, jlong, jbyteArray); + +/* + * Class: fr_acinq_secp256k1_Secp256k1CFunctions + * Method: secp256k1_ecdsa_sign + * Signature: (J[B[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ecdsa_1sign + (JNIEnv *, jclass, jlong, jbyteArray, jbyteArray); + +/* + * Class: fr_acinq_secp256k1_Secp256k1CFunctions + * Method: secp256k1_ecdsa_verify + * Signature: (J[B[B[B)I + */ +JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ecdsa_1verify + (JNIEnv *, jclass, jlong, jbyteArray, jbyteArray, jbyteArray); + +/* + * Class: fr_acinq_secp256k1_Secp256k1CFunctions + * Method: secp256k1_ecdsa_signature_normalize + * Signature: (J[B[B)I + */ +JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ecdsa_1signature_1normalize + (JNIEnv *, jclass, jlong, jbyteArray, jbyteArray); + +/* + * Class: fr_acinq_secp256k1_Secp256k1CFunctions + * Method: secp256k1_ec_privkey_negate + * Signature: (J[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1privkey_1negate + (JNIEnv *, jclass, jlong, jbyteArray); + +/* + * Class: fr_acinq_secp256k1_Secp256k1CFunctions + * Method: secp256k1_ec_pubkey_negate + * Signature: (J[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1negate + (JNIEnv *, jclass, jlong, jbyteArray); + +/* + * Class: fr_acinq_secp256k1_Secp256k1CFunctions + * Method: secp256k1_ec_privkey_tweak_add + * Signature: (J[B[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1privkey_1tweak_1add + (JNIEnv *, jclass, jlong, jbyteArray, jbyteArray); + +/* + * Class: fr_acinq_secp256k1_Secp256k1CFunctions + * Method: secp256k1_ec_pubkey_tweak_add + * Signature: (J[B[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1tweak_1add + (JNIEnv *, jclass, jlong, jbyteArray, jbyteArray); + +/* + * Class: fr_acinq_secp256k1_Secp256k1CFunctions + * Method: secp256k1_ec_privkey_tweak_mul + * Signature: (J[B[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1privkey_1tweak_1mul + (JNIEnv *, jclass, jlong, jbyteArray, jbyteArray); + +/* + * Class: fr_acinq_secp256k1_Secp256k1CFunctions + * Method: secp256k1_ec_pubkey_tweak_mul + * Signature: (J[B[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1tweak_1mul + (JNIEnv *, jclass, jlong, jbyteArray, jbyteArray); + +/* + * Class: fr_acinq_secp256k1_Secp256k1CFunctions + * Method: secp256k1_ec_pubkey_add + * Signature: (J[B[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1add + (JNIEnv *, jclass, jlong, jbyteArray, jbyteArray); + +/* + * Class: fr_acinq_secp256k1_Secp256k1CFunctions + * Method: secp256k1_ec_pubkey_combine + * Signature: (J[[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1combine + (JNIEnv *, jclass, jlong, jobjectArray); + +/* + * Class: fr_acinq_secp256k1_Secp256k1CFunctions + * Method: secp256k1_ecdh + * Signature: (J[B[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ecdh + (JNIEnv *, jclass, jlong, jbyteArray, jbyteArray); + +/* + * Class: fr_acinq_secp256k1_Secp256k1CFunctions + * Method: secp256k1_ecdsa_recover + * Signature: (J[B[BI)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ecdsa_1recover + (JNIEnv *, jclass, jlong, jbyteArray, jbyteArray, jint); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/jni/c/headers/java/org_bitcoin_Secp256k1CFunctions.h b/jni/c/headers/java/org_bitcoin_Secp256k1CFunctions.h deleted file mode 100644 index 73dfd07..0000000 --- a/jni/c/headers/java/org_bitcoin_Secp256k1CFunctions.h +++ /dev/null @@ -1,157 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* Header for class org_bitcoin_Secp256k1CFunctions */ - -#ifndef _Included_org_bitcoin_Secp256k1CFunctions -#define _Included_org_bitcoin_Secp256k1CFunctions -#ifdef __cplusplus -extern "C" { -#endif -/* - * Class: org_bitcoin_Secp256k1CFunctions - * Method: secp256k1_init_context - * Signature: ()J - */ -JNIEXPORT jlong JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1init_1context - (JNIEnv *, jclass); - -/* - * Class: org_bitcoin_Secp256k1CFunctions - * Method: secp256k1_context_randomize - * Signature: (Ljava/nio/ByteBuffer;J)I - */ -JNIEXPORT jint JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1context_1randomize - (JNIEnv *, jclass, jobject, jlong); - -/* - * Class: org_bitcoin_Secp256k1CFunctions - * Method: secp256k1_privkey_negate - * Signature: (Ljava/nio/ByteBuffer;J)[[B - */ -JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1privkey_1negate - (JNIEnv *, jclass, jobject, jlong); - -/* - * Class: org_bitcoin_Secp256k1CFunctions - * Method: secp256k1_privkey_tweak_add - * Signature: (Ljava/nio/ByteBuffer;J)[[B - */ -JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1privkey_1tweak_1add - (JNIEnv *, jclass, jobject, jlong); - -/* - * Class: org_bitcoin_Secp256k1CFunctions - * Method: secp256k1_privkey_tweak_mul - * Signature: (Ljava/nio/ByteBuffer;J)[[B - */ -JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1privkey_1tweak_1mul - (JNIEnv *, jclass, jobject, jlong); - -/* - * Class: org_bitcoin_Secp256k1CFunctions - * Method: secp256k1_pubkey_negate - * Signature: (Ljava/nio/ByteBuffer;JI)[[B - */ -JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1pubkey_1negate - (JNIEnv *, jclass, jobject, jlong, jint); - -/* - * Class: org_bitcoin_Secp256k1CFunctions - * Method: secp256k1_pubkey_tweak_add - * Signature: (Ljava/nio/ByteBuffer;JI)[[B - */ -JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1pubkey_1tweak_1add - (JNIEnv *, jclass, jobject, jlong, jint); - -/* - * Class: org_bitcoin_Secp256k1CFunctions - * Method: secp256k1_pubkey_tweak_mul - * Signature: (Ljava/nio/ByteBuffer;JI)[[B - */ -JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1pubkey_1tweak_1mul - (JNIEnv *, jclass, jobject, jlong, jint); - -/* - * Class: org_bitcoin_Secp256k1CFunctions - * Method: secp256k1_destroy_context - * Signature: (J)V - */ -JNIEXPORT void JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1destroy_1context - (JNIEnv *, jclass, jlong); - -/* - * Class: org_bitcoin_Secp256k1CFunctions - * Method: secp256k1_ecdsa_verify - * Signature: (Ljava/nio/ByteBuffer;JII)I - */ -JNIEXPORT jint JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1ecdsa_1verify - (JNIEnv *, jclass, jobject, jlong, jint, jint); - -/* - * Class: org_bitcoin_Secp256k1CFunctions - * Method: secp256k1_ecdsa_sign - * Signature: (Ljava/nio/ByteBuffer;ZJ)[[B - */ -JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1ecdsa_1sign - (JNIEnv *, jclass, jobject, jboolean, jlong); - -/* - * Class: org_bitcoin_Secp256k1CFunctions - * Method: secp256k1_ecdsa_normalize - * Signature: (Ljava/nio/ByteBuffer;IZJ)[[B - */ -JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1ecdsa_1normalize - (JNIEnv *, jclass, jobject, jint, jboolean, jlong); - -/* - * Class: org_bitcoin_Secp256k1CFunctions - * Method: secp256k1_ec_seckey_verify - * Signature: (Ljava/nio/ByteBuffer;J)I - */ -JNIEXPORT jint JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1ec_1seckey_1verify - (JNIEnv *, jclass, jobject, jlong); - -/* - * Class: org_bitcoin_Secp256k1CFunctions - * Method: secp256k1_ec_pubkey_create - * Signature: (Ljava/nio/ByteBuffer;ZJ)[[B - */ -JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1create - (JNIEnv *, jclass, jobject, jboolean, jlong); - -/* - * Class: org_bitcoin_Secp256k1CFunctions - * Method: secp256k1_ec_pubkey_parse - * Signature: (Ljava/nio/ByteBuffer;JIZ)[[B - */ -JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1parse - (JNIEnv *, jclass, jobject, jlong, jint, jboolean); - -/* - * Class: org_bitcoin_Secp256k1CFunctions - * Method: secp256k1_ec_pubkey_add - * Signature: (Ljava/nio/ByteBuffer;JII)[[B - */ -JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1add - (JNIEnv *, jclass, jobject, jlong, jint, jint); - -/* - * Class: org_bitcoin_Secp256k1CFunctions - * Method: secp256k1_ecdh - * Signature: (Ljava/nio/ByteBuffer;JI)[[B - */ -JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1ecdh - (JNIEnv *, jclass, jobject, jlong, jint); - -/* - * Class: org_bitcoin_Secp256k1CFunctions - * Method: secp256k1_ecdsa_recover - * Signature: (Ljava/nio/ByteBuffer;JIZ)[[B - */ -JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1ecdsa_1recover - (JNIEnv *, jclass, jobject, jlong, jint, jboolean); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/jni/c/src/fr_acinq_secp256k1_Secp256k1CFunctions.c b/jni/c/src/fr_acinq_secp256k1_Secp256k1CFunctions.c new file mode 100644 index 0000000..3c4856f --- /dev/null +++ b/jni/c/src/fr_acinq_secp256k1_Secp256k1CFunctions.c @@ -0,0 +1,689 @@ +#include +#include + +#include "include/secp256k1.h" +#include "include/secp256k1_ecdh.h" +#include "include/secp256k1_recovery.h" +#include "fr_acinq_secp256k1_Secp256k1CFunctions.h" + +#define SIG_FORMAT_UNKNOWN 0 +#define SIG_FORMAT_COMPACT 1 +#define SIG_FORMAT_DER 2 + +void JNI_ThrowByName(JNIEnv *penv, const char* name, const char* msg) + { + jclass cls = (*penv)->FindClass(penv, name); + if (cls != NULL) { + (*penv)->ThrowNew(penv, cls, msg); + (*penv)->DeleteLocalRef(penv, cls); + } + } + +#define CHECKRESULT(errorcheck, message) { \ + if (errorcheck) { \ + JNI_ThrowByName(penv, "fr/acinq/secp256k1/Secp256k1Exception", message); \ + return 0; \ + } \ +} + +#define CHECKRESULT1(errorcheck, message, dosomething) { \ + if (errorcheck) { \ + dosomething; \ + JNI_ThrowByName(penv, "fr/acinq/secp256k1/Secp256k1Exception", message); \ + return 0; \ + } \ +} + +/* + * Class: fr_acinq_bitcoin_Secp256k1Bindings + * Method: secp256k1_context_create + * Signature: (I)J + */ +JNIEXPORT jlong JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1context_1create + (JNIEnv *penv, jclass clazz, jint flags) +{ + return (jlong) secp256k1_context_create(flags); +} + +/* + * Class: fr_acinq_bitcoin_Secp256k1Bindings + * Method: secp256k1_context_destroy + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1context_1destroy + (JNIEnv *penv, jclass clazz, jlong ctx) +{ + if (ctx != 0) { + secp256k1_context_destroy((secp256k1_context*)ctx); + } +} + +/* + * Class: fr_acinq_secp256k1_Secp256k1CFunctions + * Method: secp256k1_ec_seckey_verify + * Signature: (J[B)I + */ +JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1seckey_1verify + (JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jseckey) +{ + secp256k1_context* ctx = (secp256k1_context *)jctx; + jbyte *seckey; + int result = 0; + + if (jctx == 0) return 0; + if (jseckey == NULL) return 0; + CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "secret key must be 32 bytes"); + seckey = (*penv)->GetByteArrayElements(penv, jseckey, 0); + result = secp256k1_ec_seckey_verify(ctx, (unsigned char*)seckey); + (*penv)->ReleaseByteArrayElements(penv, jseckey, seckey, 0); + return result; +} + +/* + * Class: fr_acinq_bitcoin_Secp256k1Bindings + * Method: secp256k1_ec_pubkey_parse + * Signature: (J[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1parse + (JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jpubkey) +{ + secp256k1_context* ctx = (secp256k1_context *)jctx; + jbyte *pubkeyBytes; + secp256k1_pubkey pubkey; + size_t size; + int result = 0; + + if (jctx == 0) return 0; + if (jpubkey == NULL) return 0; + + size = (*penv)->GetArrayLength(penv, jpubkey); + CHECKRESULT((size != 33) && (size != 65), "invalid public key size"); + + pubkeyBytes = (*penv)->GetByteArrayElements(penv, jpubkey, 0); + result = secp256k1_ec_pubkey_parse(ctx, &pubkey, (unsigned char*) pubkeyBytes, size); + (*penv)->ReleaseByteArrayElements(penv, jpubkey, pubkeyBytes, 0); + CHECKRESULT(!result, "secp256k1_ec_pubkey_parse failed"); + + size = 65; + jpubkey = (*penv)->NewByteArray(penv, 65); + pubkeyBytes = (*penv)->GetByteArrayElements(penv, jpubkey, 0); + result = secp256k1_ec_pubkey_serialize(ctx, (unsigned char*) pubkeyBytes, &size, &pubkey, SECP256K1_EC_UNCOMPRESSED); + (*penv)->ReleaseByteArrayElements(penv, jpubkey, pubkeyBytes, 0); + CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed"); + return jpubkey; +} + +/* + * Class: fr_acinq_bitcoin_Secp256k1Bindings + * Method: secp256k1_ec_pubkey_create + * Signature: (J[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1create + (JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jseckey) +{ + secp256k1_context* ctx = (secp256k1_context *)jctx; + jbyte *seckey, *pubkey; + secp256k1_pubkey pub; + int result = 0; + size_t len; + jbyteArray jpubkey = 0; + + if (jseckey == NULL) return NULL; + if (jctx == 0) return NULL; + + CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "secret key must be 32 bytes"); + seckey = (*penv)->GetByteArrayElements(penv, jseckey, 0); + jpubkey = (*penv)->NewByteArray(penv, 65); + pubkey = (*penv)->GetByteArrayElements(penv, jpubkey, 0); + result = secp256k1_ec_pubkey_create(ctx, &pub, (unsigned char*)seckey); + (*penv)->ReleaseByteArrayElements(penv, jseckey, seckey, 0); + (*penv)->ReleaseByteArrayElements(penv, jpubkey, pubkey, 0); + CHECKRESULT(!result, "secp256k1_ec_pubkey_create failed"); + jpubkey = (*penv)->NewByteArray(penv, 65); + pubkey = (*penv)->GetByteArrayElements(penv, jpubkey, 0); + len = 65; + result = secp256k1_ec_pubkey_serialize(ctx, (unsigned char*)pubkey, &len, &pub, SECP256K1_EC_UNCOMPRESSED); + (*penv)->ReleaseByteArrayElements(penv, jpubkey, pubkey, 0); + CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed"); + return jpubkey; +} + +/* + * Class: fr_acinq_bitcoin_Secp256k1Bindings + * Method: secp256k1_ecdsa_sign + * Signature: (J[B[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ecdsa_1sign + (JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jmsg, jbyteArray jseckey) +{ + secp256k1_context* ctx = (secp256k1_context *)jctx; + jbyte *seckey, *msg, *sig; + secp256k1_ecdsa_signature signature; + int result = 0; + jbyteArray jsig; + + if (jctx == 0) return NULL; + if (jmsg == NULL) return NULL; + if (jseckey == NULL) return NULL; + + CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "secret key must be 32 bytes"); + CHECKRESULT((*penv)->GetArrayLength(penv, jmsg) != 32, "message key must be 32 bytes"); + seckey = (*penv)->GetByteArrayElements(penv, jseckey, 0); + msg = (*penv)->GetByteArrayElements(penv, jmsg, 0); + + result = secp256k1_ecdsa_sign(ctx, &signature, (unsigned char*)msg, (unsigned char*)seckey, NULL, NULL); + (*penv)->ReleaseByteArrayElements(penv, jseckey, seckey, 0); + (*penv)->ReleaseByteArrayElements(penv, jmsg, msg, 0); + CHECKRESULT(!result, "secp256k1_ecdsa_sign failed"); + + jsig = (*penv)->NewByteArray(penv, 64); + sig = (*penv)->GetByteArrayElements(penv, jsig, 0); + result = secp256k1_ecdsa_signature_serialize_compact(ctx, (unsigned char*)sig, &signature); + (*penv)->ReleaseByteArrayElements(penv, jsig, sig, 0); + CHECKRESULT(!result, "secp256k1_ecdsa_signature_serialize_compact failed"); + return jsig; +} + +int GetSignatureFormat(size_t size) +{ + switch(size) { + case 64: + return SIG_FORMAT_COMPACT; + break; + case 70: + case 71: + case 72: + case 73: + return SIG_FORMAT_DER; + break; + default: return SIG_FORMAT_UNKNOWN; + } +} + +/* + * Class: fr_acinq_bitcoin_Secp256k1Bindings + * Method: secp256k1_ecdsa_verify + * Signature: (J[B[B[B)I + */ +JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ecdsa_1verify + (JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jsig, jbyteArray jmsg, jbyteArray jpubkey) +{ + secp256k1_context* ctx = (secp256k1_context *)jctx; + jbyte *pub, *msg, *sig; + secp256k1_ecdsa_signature signature; + secp256k1_pubkey pubkey; + size_t sigSize, pubSize; + int result = 0; + + if (jctx == 0) return 0; + if (jsig == NULL) return 0; + if (jmsg == NULL) return 0; + if (jpubkey == NULL) return 0; + + sigSize = (*penv)->GetArrayLength(penv, jsig); + int sigFormat = GetSignatureFormat(sigSize); + CHECKRESULT(sigFormat == SIG_FORMAT_UNKNOWN, "invalid signature size"); + + pubSize = (*penv)->GetArrayLength(penv, jpubkey); + CHECKRESULT((pubSize != 33) && (pubSize != 65), "invalid public key size"); + + CHECKRESULT((*penv)->GetArrayLength(penv, jmsg) != 32, "message must be 32 bytes"); + + sig = (*penv)->GetByteArrayElements(penv, jsig, 0); + switch(sigFormat) { + case SIG_FORMAT_COMPACT: + result = secp256k1_ecdsa_signature_parse_compact(ctx, &signature, (unsigned char*)sig); + (*penv)->ReleaseByteArrayElements(penv, jsig, sig, 0); + CHECKRESULT(!result, "secp256k1_ecdsa_signature_parse_compact failed"); + break; + case SIG_FORMAT_DER: + result = secp256k1_ecdsa_signature_parse_der(ctx, &signature, (unsigned char*)sig, sigSize); + (*penv)->ReleaseByteArrayElements(penv, jsig, sig, 0); + CHECKRESULT(!result, "secp256k1_ecdsa_signature_parse_der failed"); + break; + } + + pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0); + result = secp256k1_ec_pubkey_parse(ctx, &pubkey, (unsigned char*)pub, pubSize); + (*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0); + CHECKRESULT(!result, "secp256k1_ec_pubkey_parse failed"); + + msg = (*penv)->GetByteArrayElements(penv, jmsg, 0); + result = secp256k1_ecdsa_verify(ctx, &signature, (unsigned char*)msg, &pubkey); + (*penv)->ReleaseByteArrayElements(penv, jmsg, msg, 0); + return result; +} + +/* + * Class: fr_acinq_bitcoin_Secp256k1Bindings + * Method: secp256k1_ecdsa_signature_normalize + * Signature: (J[B[B)I + */ +JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ecdsa_1signature_1normalize + (JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jsigin, jbyteArray jsigout) +{ + secp256k1_context* ctx = (secp256k1_context *)jctx; + jbyte *sig; + secp256k1_ecdsa_signature signature_in, signature_out; + size_t size; + int result = 0; + int return_value = 0; + int sigFormat = SIG_FORMAT_UNKNOWN; + + if (jctx == 0) return 0; + if (jsigin == NULL) return 0; + if (jsigout == NULL) return 0; + + size = (*penv)->GetArrayLength(penv, jsigin); + sigFormat = GetSignatureFormat(size); + CHECKRESULT(sigFormat == SIG_FORMAT_UNKNOWN, "invalid signature size"); + CHECKRESULT((*penv)->GetArrayLength(penv, jsigout) != 64, "output signature length must be 64 bytes"); + + sig = (*penv)->GetByteArrayElements(penv, jsigin, 0); + switch(sigFormat) { + case SIG_FORMAT_COMPACT: + result = secp256k1_ecdsa_signature_parse_compact(ctx, &signature_in, (unsigned char*)sig); + (*penv)->ReleaseByteArrayElements(penv, jsigin, sig, 0); + CHECKRESULT(!result, "secp256k1_ecdsa_signature_parse_compact failed"); + break; + case SIG_FORMAT_DER: + result = secp256k1_ecdsa_signature_parse_der(ctx, &signature_in, (unsigned char*)sig, size); + (*penv)->ReleaseByteArrayElements(penv, jsigin, sig, 0); + CHECKRESULT(!result, "secp256k1_ecdsa_signature_parse_der failed"); + break; + } + return_value = secp256k1_ecdsa_signature_normalize(ctx, &signature_out, &signature_in); + sig = (*penv)->GetByteArrayElements(penv, jsigout, 0); + result = secp256k1_ecdsa_signature_serialize_compact(ctx, (unsigned char*)sig, &signature_out); + (*penv)->ReleaseByteArrayElements(penv, jsigout, sig, 0); + CHECKRESULT(!result, "secp256k1_ecdsa_signature_serialize_compact failed"); + + return return_value; +} + +/* + * Class: fr_acinq_bitcoin_Secp256k1Bindings + * Method: secp256k1_ec_privkey_negate + * Signature: (J[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1privkey_1negate + (JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jseckey) +{ + secp256k1_context* ctx = (secp256k1_context *)jctx; + jbyte *seckey; + int result = 0; + + if (jctx == 0) return 0; + if (jseckey == NULL) return 0; + CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "secret key must be 32 bytes"); + seckey = (*penv)->GetByteArrayElements(penv, jseckey, 0); + result = secp256k1_ec_privkey_negate(ctx, (unsigned char*)seckey); + (*penv)->ReleaseByteArrayElements(penv, jseckey, seckey, 0); + CHECKRESULT(!result, "secp256k1_ec_privkey_negate failed"); + return jseckey; +} + +/* + * Class: fr_acinq_bitcoin_Secp256k1Bindings + * Method: secp256k1_ec_pubkey_negate + * Signature: (J[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1negate + (JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jpubkey) +{ + secp256k1_context* ctx = (secp256k1_context *)jctx; + jbyte *pub; + secp256k1_pubkey pubkey; + size_t size; + int result = 0; + + if (jctx == 0) return 0; + if (jpubkey == NULL) return 0; + + size = (*penv)->GetArrayLength(penv, jpubkey); + CHECKRESULT((size != 33) && (size != 65), "invalid public key size"); + pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0); + result = secp256k1_ec_pubkey_parse(ctx, &pubkey, (unsigned char*)pub, size); + (*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0); + CHECKRESULT(!result, "secp256k1_ec_pubkey_parse failed"); + + result = secp256k1_ec_pubkey_negate(ctx, &pubkey); + CHECKRESULT(!result, "secp256k1_ec_pubkey_negate failed"); + + size = 65; + jpubkey = (*penv)->NewByteArray(penv, 65); + pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0); + result = secp256k1_ec_pubkey_serialize(ctx, (unsigned char*)pub, &size, &pubkey, SECP256K1_EC_UNCOMPRESSED); + (*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0); + CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed"); + return jpubkey; +} + +/* + * Class: fr_acinq_bitcoin_Secp256k1Bindings + * Method: secp256k1_ec_privkey_tweak_add + * Signature: (J[B[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1privkey_1tweak_1add + (JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jseckey, jbyteArray jtweak) +{ + secp256k1_context* ctx = (secp256k1_context *)jctx; + jbyte *seckey, *tweak; + int result = 0; + + if (jctx == 0) return NULL; + if (jseckey == NULL) return NULL; + if (jtweak == NULL) return NULL; + + CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "secret key must be 32 bytes"); + CHECKRESULT((*penv)->GetArrayLength(penv, jtweak) != 32, "tweak must be 32 bytes"); + seckey = (*penv)->GetByteArrayElements(penv, jseckey, 0); + tweak = (*penv)->GetByteArrayElements(penv, jtweak, 0); + result = secp256k1_ec_privkey_tweak_add(ctx, (unsigned char*)seckey, (unsigned char*)tweak); + (*penv)->ReleaseByteArrayElements(penv, jseckey, seckey, 0); + (*penv)->ReleaseByteArrayElements(penv, jtweak, tweak, 0); + CHECKRESULT(!result, "secp256k1_ec_privkey_tweak_add failed"); + return jseckey; +} + +/* + * Class: fr_acinq_bitcoin_Secp256k1Bindings + * Method: secp256k1_ec_pubkey_tweak_add + * Signature: (J[B[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1tweak_1add + (JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jpubkey, jbyteArray jtweak) +{ + secp256k1_context* ctx = (secp256k1_context *)jctx; + jbyte *pub, *tweak; + secp256k1_pubkey pubkey; + size_t size; + int result = 0; + + if (jctx == 0) return NULL; + if (jpubkey == NULL) return NULL; + if (jtweak == NULL) return NULL; + + size = (*penv)->GetArrayLength(penv, jpubkey); + CHECKRESULT((size != 33) && (size != 65), "invalid public key size"); + CHECKRESULT((*penv)->GetArrayLength(penv, jtweak) != 32, "tweak must be 32 bytes"); + + pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0); + result = secp256k1_ec_pubkey_parse(ctx, &pubkey, (unsigned char*)pub, size); + (*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0); + CHECKRESULT(!result, "secp256k1_ec_pubkey_parse failed"); + + tweak = (*penv)->GetByteArrayElements(penv, jtweak, 0); + result = secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, (unsigned char*)tweak); + (*penv)->ReleaseByteArrayElements(penv, jtweak, tweak, 0); + CHECKRESULT(!result, "secp256k1_ec_pubkey_tweak_add failed"); + + size = 65; + jpubkey = (*penv)->NewByteArray(penv, 65); + pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0); + result = secp256k1_ec_pubkey_serialize(ctx, (unsigned char*)pub, &size, &pubkey, SECP256K1_EC_UNCOMPRESSED); + (*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0); + CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed"); + return jpubkey; +} + +/* + * Class: fr_acinq_bitcoin_Secp256k1Bindings + * Method: secp256k1_ec_privkey_tweak_mul + * Signature: (J[B[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1privkey_1tweak_1mul + (JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jseckey, jbyteArray jtweak) +{ + secp256k1_context* ctx = (secp256k1_context *)jctx; + jbyte *seckey, *tweak; + int result = 0; + + if (jctx == 0) return NULL; + if (jseckey == NULL) return NULL; + if (jtweak == NULL) return NULL; + + CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "secret key must be 32 bytes"); + CHECKRESULT((*penv)->GetArrayLength(penv, jtweak) != 32, "tweak must be 32 bytes"); + seckey = (*penv)->GetByteArrayElements(penv, jseckey, 0); + tweak = (*penv)->GetByteArrayElements(penv, jtweak, 0); + result = secp256k1_ec_privkey_tweak_mul(ctx, (unsigned char*)seckey, (unsigned char*)tweak); + CHECKRESULT(!result, "secp256k1_ec_privkey_tweak_mul failed"); + (*penv)->ReleaseByteArrayElements(penv, jseckey, seckey, 0); + (*penv)->ReleaseByteArrayElements(penv, jtweak, tweak, 0); + return jseckey; +} + +/* + * Class: fr_acinq_bitcoin_Secp256k1Bindings + * Method: secp256k1_ec_pubkey_tweak_mul + * Signature: (J[B[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1tweak_1mul + (JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jpubkey, jbyteArray jtweak) +{ + secp256k1_context* ctx = (secp256k1_context *)jctx; + jbyte *pub, *tweak; + secp256k1_pubkey pubkey; + size_t size; + int result = 0; + + if (jctx == 0) return NULL; + if (jpubkey == NULL) return NULL; + if (jtweak == NULL) return NULL; + + size = (*penv)->GetArrayLength(penv, jpubkey); + CHECKRESULT((size != 33) && (size != 65), "invalid public key size"); + CHECKRESULT((*penv)->GetArrayLength(penv, jtweak) != 32, "tweak must be 32 bytes"); + pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0); + result = secp256k1_ec_pubkey_parse(ctx, &pubkey, (unsigned char*)pub, size); + (*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0); + CHECKRESULT(!result, "secp256k1_ec_pubkey_parse failed"); + + tweak = (*penv)->GetByteArrayElements(penv, jtweak, 0); + result = secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, (unsigned char*)tweak); + (*penv)->ReleaseByteArrayElements(penv, jtweak, tweak, 0); + CHECKRESULT(!result, "secp256k1_ec_pubkey_tweak_mul failed"); + + size = 65; + jpubkey = (*penv)->NewByteArray(penv, 65); + pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0); + result = secp256k1_ec_pubkey_serialize(ctx, (unsigned char*)pub, &size, &pubkey, SECP256K1_EC_UNCOMPRESSED); + (*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0); + CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed"); + return jpubkey; +} + +void free_pubkeys(secp256k1_pubkey **pubkeys, size_t count) +{ + size_t i; + for(i = 0; i < count; i++) { + if (pubkeys[i] != NULL) free(pubkeys[i]); + } + free(pubkeys); +} + +/* + * Class: fr_acinq_bitcoin_Secp256k1Bindings + * Method: secp256k1_ec_pubkey_add + * Signature: (J[B[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1add + (JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jpubkey1, jbyteArray jpubkey2) +{ + secp256k1_context* ctx = (secp256k1_context *)jctx; + jbyte *pub1, *pub2; + secp256k1_pubkey pubkey1, pubkey2, combined; + secp256k1_pubkey const *pubkeys[2] = { &pubkey1, &pubkey2 }; + size_t size = 0; + int result = 0; + + if (jctx == 0) return NULL; + if (jpubkey1 == NULL) return NULL; + if (jpubkey2 == NULL) return NULL; + + size = (*penv)->GetArrayLength(penv, jpubkey1); + CHECKRESULT((size != 33) && (size != 65), "invalid public key size"); + pub1 = (*penv)->GetByteArrayElements(penv, jpubkey1, 0); + result = secp256k1_ec_pubkey_parse(ctx, &pubkey1, (unsigned char*)pub1, size); + (*penv)->ReleaseByteArrayElements(penv, jpubkey1, pub1, 0); + CHECKRESULT(!result, "secp256k1_ec_pubkey_parse failed"); + + size = (*penv)->GetArrayLength(penv, jpubkey2); + CHECKRESULT((size != 33) && (size != 65), "invalid public key size"); + pub2 = (*penv)->GetByteArrayElements(penv, jpubkey2, 0); + result = secp256k1_ec_pubkey_parse(ctx, &pubkey2, (unsigned char*)pub2, size); + (*penv)->ReleaseByteArrayElements(penv, jpubkey2, pub2, 0); + CHECKRESULT(!result, "secp256k1_ec_pubkey_parse failed"); + + result = secp256k1_ec_pubkey_combine(ctx, &combined, pubkeys, 2); + CHECKRESULT(!result, "secp256k1_ec_pubkey_combine failed"); + + size = 65; + jpubkey1 = (*penv)->NewByteArray(penv, 65); + pub1 = (*penv)->GetByteArrayElements(penv, jpubkey1, 0); + result = secp256k1_ec_pubkey_serialize(ctx, (unsigned char*)pub1, &size, &combined, SECP256K1_EC_UNCOMPRESSED); + (*penv)->ReleaseByteArrayElements(penv, jpubkey1, pub1, 0); + CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed"); + return jpubkey1; +} + +/* + * Class: fr_acinq_bitcoin_Secp256k1Bindings + * Method: secp256k1_ec_pubkey_combine + * Signature: (J[[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1combine + (JNIEnv *penv, jclass clazz, jlong jctx, jobjectArray jpubkeys) +{ + secp256k1_context* ctx = (secp256k1_context *)jctx; + jbyte *pub; + secp256k1_pubkey **pubkeys; + secp256k1_pubkey combined; + jbyteArray jpubkey; + size_t size, count; + size_t i; + int result = 0; + + if (jctx == 0) return NULL; + if (jpubkeys == NULL) return NULL; + + count = (*penv)->GetArrayLength(penv, jpubkeys); + pubkeys = calloc(count, sizeof(secp256k1_pubkey*)); + + for(i = 0; i < count; i++) { + pubkeys[i] = calloc(1, sizeof(secp256k1_pubkey)); + jpubkey = (jbyteArray) (*penv)->GetObjectArrayElement(penv, jpubkeys, i); + size = (*penv)->GetArrayLength(penv, jpubkey); + CHECKRESULT1((size != 33) && (size != 65), "invalid public key size", free_pubkeys(pubkeys, count)); + pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0); + result = secp256k1_ec_pubkey_parse(ctx, pubkeys[i], (unsigned char*)pub, size); + (*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0); + CHECKRESULT1(!result, "secp256k1_ec_pubkey_parse failed", free_pubkeys(pubkeys, count)); + } + result = secp256k1_ec_pubkey_combine(ctx, &combined, (const secp256k1_pubkey * const *)pubkeys, count); + free_pubkeys(pubkeys, count); + CHECKRESULT(!result, "secp256k1_ec_pubkey_combine failed"); + + size = 65; + jpubkey = (*penv)->NewByteArray(penv, 65); + pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0); + result = secp256k1_ec_pubkey_serialize(ctx, (unsigned char*)pub, &size, &combined, SECP256K1_EC_UNCOMPRESSED); + (*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0); + CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed"); + return jpubkey; +} + +/* + * Class: fr_acinq_bitcoin_Secp256k1Bindings + * Method: secp256k1_ecdh + * Signature: (J[B[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ecdh + (JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jseckey, jbyteArray jpubkey) +{ + secp256k1_context* ctx = (secp256k1_context *)jctx; + jbyte* seckeyBytes, *pubkeyBytes, *output; + secp256k1_pubkey pubkey; + jbyteArray joutput; + size_t size; + int result; + + if (jctx == 0) return NULL; + if (jseckey == NULL) return NULL; + if (jpubkey == NULL) return NULL; + + CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "invalid private key size"); + + size = (*penv)->GetArrayLength(penv, jpubkey); + CHECKRESULT((size != 33) && (size != 65), "invalid public key size"); + pubkeyBytes = (*penv)->GetByteArrayElements(penv, jpubkey, 0); + result = secp256k1_ec_pubkey_parse(ctx, &pubkey, (unsigned char*)pubkeyBytes, size); + (*penv)->ReleaseByteArrayElements(penv, jpubkey, pubkeyBytes, 0); + CHECKRESULT(!result, "secp256k1_ec_pubkey_parse failed"); + + seckeyBytes = (*penv)->GetByteArrayElements(penv, jseckey, 0); + joutput = (*penv)->NewByteArray(penv, 32); + output = (*penv)->GetByteArrayElements(penv, joutput, 0); + result = secp256k1_ecdh(ctx, (unsigned char*)output, &pubkey, (unsigned char*)seckeyBytes, NULL, NULL); + (*penv)->ReleaseByteArrayElements(penv, joutput, output, 0); + (*penv)->ReleaseByteArrayElements(penv, jseckey, seckeyBytes, 0); + return joutput; +} + +/* + * Class: fr_acinq_bitcoin_Secp256k1Bindings + * Method: secp256k1_ecdsa_recover + * Signature: (J[B[BI)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ecdsa_1recover + (JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jsig, jbyteArray jmsg, jint recid) +{ + secp256k1_context* ctx = (secp256k1_context *)jctx; + jbyte* sig, *msg, *pub; + jbyteArray jpubkey; + secp256k1_pubkey pubkey; + secp256k1_ecdsa_recoverable_signature signature; + secp256k1_ecdsa_signature dummy; + unsigned char dummyBytes[64]; + size_t sigSize, size; + int result; + + if (jctx == 0) return NULL; + if (jsig == NULL) return NULL; + if (jmsg == NULL) return NULL; + + sigSize = (*penv)->GetArrayLength(penv, jsig); + int sigFormat = GetSignatureFormat(sigSize); + CHECKRESULT(sigFormat == SIG_FORMAT_UNKNOWN, "invalid signature size"); + CHECKRESULT((*penv)->GetArrayLength(penv, jmsg) != 32, "message must be 32 bytes"); + sig = (*penv)->GetByteArrayElements(penv, jsig, 0); + switch(sigFormat) { + case SIG_FORMAT_COMPACT: + result = secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &signature, (unsigned char*)sig, recid); + (*penv)->ReleaseByteArrayElements(penv, jsig, sig, 0); + CHECKRESULT(!result, "secp256k1_ecdsa_recoverable_signature_parse_compact failed"); + break; + case SIG_FORMAT_DER: + result = secp256k1_ecdsa_signature_parse_der(ctx, &dummy, (unsigned char*)sig, sigSize); + (*penv)->ReleaseByteArrayElements(penv, jsig, sig, 0); + CHECKRESULT(!result, "secp256k1_ecdsa_signature_parse_der failed"); + result = secp256k1_ecdsa_signature_serialize_compact(ctx, dummyBytes, &dummy); + CHECKRESULT(!result, "secp256k1_ecdsa_signature_serialize_compact failed"); + result = secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &signature, dummyBytes, recid); + CHECKRESULT(!result, "secp256k1_ecdsa_recoverable_signature_parse_compact failed"); + break; + } + msg = (*penv)->GetByteArrayElements(penv, jmsg, 0); + result = secp256k1_ecdsa_recover(ctx, &pubkey, &signature, (unsigned char*)msg); + (*penv)->ReleaseByteArrayElements(penv, jmsg, msg, 0); + CHECKRESULT(!result, "secp256k1_ecdsa_recover failed"); + + size = 65; + jpubkey = (*penv)->NewByteArray(penv, 65); + pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0); + result = secp256k1_ec_pubkey_serialize(ctx, (unsigned char*)pub, &size, &pubkey, SECP256K1_EC_UNCOMPRESSED); + (*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0); + CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed"); + return jpubkey; +} diff --git a/jni/c/src/org_bitcoin_Secp256k1CFunctions.c b/jni/c/src/org_bitcoin_Secp256k1CFunctions.c deleted file mode 100644 index e7d54ec..0000000 --- a/jni/c/src/org_bitcoin_Secp256k1CFunctions.c +++ /dev/null @@ -1,658 +0,0 @@ -#include -#include -#include -#include "org_bitcoin_Secp256k1CFunctions.h" -#include "include/secp256k1.h" -#include "include/secp256k1_ecdh.h" -#include "include/secp256k1_recovery.h" - - -SECP256K1_API jlong JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1init_1context - (JNIEnv* env, jclass classObject) -{ - secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); - - (void)classObject;(void)env; - - return (uintptr_t)ctx; -} - -SECP256K1_API jlong JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1ctx_1clone - (JNIEnv* env, jclass classObject, jlong ctx_l) -{ - const secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; - - jlong ctx_clone_l = (uintptr_t) secp256k1_context_clone(ctx); - - (void)classObject;(void)env; - - return ctx_clone_l; - -} - -SECP256K1_API jint JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1context_1randomize - (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) -{ - secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; - - const unsigned char* seed = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); - - (void)classObject; - - return secp256k1_context_randomize(ctx, seed); - -} - -SECP256K1_API void JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1destroy_1context - (JNIEnv* env, jclass classObject, jlong ctx_l) -{ - secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; - - secp256k1_context_destroy(ctx); - - (void)classObject;(void)env; -} - -SECP256K1_API jint JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1ecdsa_1verify - (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint siglen, jint publen) -{ - secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; - - unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); - const unsigned char* sigdata = { (unsigned char*) (data + 32) }; - const unsigned char* pubdata = { (unsigned char*) (data + siglen + 32) }; - - secp256k1_ecdsa_signature sig; - secp256k1_pubkey pubkey; - - int ret = 0; - if (siglen == 64) { - ret = secp256k1_ecdsa_signature_parse_compact(ctx, &sig, sigdata); - } else { - ret = secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigdata, siglen); - } - - if( ret ) { - ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pubdata, publen); - - if( ret ) { - ret = secp256k1_ecdsa_verify(ctx, &sig, data, &pubkey); - } - } - - (void)classObject; - - return ret; -} - -SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1ecdsa_1sign - (JNIEnv* env, jclass classObject, jobject byteBufferObject, jboolean compact, 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_signature sig; - - int ret = secp256k1_ecdsa_sign(ctx, &sig, data, secKey, NULL, NULL); - - unsigned char outputSer[72]; - size_t outputLen = compact ? 64 : 72; - - if( ret ) { - if (compact) { - int ret2 = secp256k1_ecdsa_signature_serialize_compact(ctx, outputSer, &sig ); (void)ret2; - } else { - 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 jobjectArray JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1ecdsa_1normalize - (JNIEnv* env, jclass classObject, jobject byteBufferObject, jint siglen, jboolean compact, jlong ctx_l) -{ - secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; - unsigned char* sigdata = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); - - jobjectArray retArray; - jbyteArray sigArray, intsByteArray; - unsigned char intsarray[3]; - - secp256k1_ecdsa_signature sig; - - int ret = 0; - if (siglen == 64) { - ret = secp256k1_ecdsa_signature_parse_compact(ctx, &sig, sigdata); - } else { - ret = secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigdata, siglen); - } - - int ret2 = 0; - if (ret) { - ret2 = secp256k1_ecdsa_signature_normalize(ctx, &sig, &sig); - } - - unsigned char outputSer[72]; - size_t outputLen = compact ? 64 : 72; - - if( ret ) { - if (compact) { - int ret3 = secp256k1_ecdsa_signature_serialize_compact(ctx, outputSer, &sig ); (void)ret3; - } else { - int ret3 = secp256k1_ecdsa_signature_serialize_der(ctx, outputSer, &outputLen, &sig ); (void)ret3; - } - } - - intsarray[0] = outputLen; - intsarray[1] = ret; - intsarray[2] = ret2; - - 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, 3); - (*env)->SetByteArrayRegion(env, intsByteArray, 0, 3, (jbyte*)intsarray); - (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); - - (void)classObject; - - return retArray; - -} - -SECP256K1_API jint JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1ec_1seckey_1verify - (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) -{ - secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; - unsigned char* secKey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); - - (void)classObject; - - return secp256k1_ec_seckey_verify(ctx, secKey); -} - -SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1create - (JNIEnv* env, jclass classObject, jobject byteBufferObject, jboolean compressed, jlong ctx_l) -{ - secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; - const unsigned char* secKey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); - - secp256k1_pubkey pubkey; - - jobjectArray retArray; - jbyteArray pubkeyArray, intsByteArray; - unsigned char intsarray[2]; - - int ret = secp256k1_ec_pubkey_create(ctx, &pubkey, secKey); - - unsigned char outputSer[65]; - size_t outputLen = compressed ? 33 : 65; - - if( ret ) { - int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey, compressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED );(void)ret2; - } - - intsarray[0] = outputLen; - intsarray[1] = ret; - - retArray = (*env)->NewObjectArray(env, 2, - (*env)->FindClass(env, "[B"), - (*env)->NewByteArray(env, 1)); - - pubkeyArray = (*env)->NewByteArray(env, outputLen); - (*env)->SetByteArrayRegion(env, pubkeyArray, 0, outputLen, (jbyte*)outputSer); - (*env)->SetObjectArrayElement(env, retArray, 0, pubkeyArray); - - 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 jobjectArray JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1parse - (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint inputlen, jboolean compressed) -{ - secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; - const unsigned char* pubkeydata = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); - - secp256k1_pubkey pubkey; - - jobjectArray retArray; - jbyteArray pubkeyArray, intsByteArray; - unsigned char intsarray[2]; - - int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeydata, inputlen); - - unsigned char outputSer[65]; - size_t outputLen = compressed ? 33 : 65; - - if( ret ) { - int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey, compressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED );(void)ret2; - } - - intsarray[0] = outputLen; - intsarray[1] = ret; - - retArray = (*env)->NewObjectArray(env, 2, - (*env)->FindClass(env, "[B"), - (*env)->NewByteArray(env, 1)); - - pubkeyArray = (*env)->NewByteArray(env, outputLen); - (*env)->SetByteArrayRegion(env, pubkeyArray, 0, outputLen, (jbyte*)outputSer); - (*env)->SetObjectArrayElement(env, retArray, 0, pubkeyArray); - - 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 jobjectArray JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1privkey_1negate -(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) -{ - secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; - unsigned char* privkey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); - - jobjectArray retArray; - jbyteArray privArray, intsByteArray; - unsigned char intsarray[2]; - - int privkeylen = 32; - - int ret = secp256k1_ec_privkey_negate(ctx, privkey); - - intsarray[0] = privkeylen; - intsarray[1] = ret; - - retArray = (*env)->NewObjectArray(env, 2, - (*env)->FindClass(env, "[B"), - (*env)->NewByteArray(env, 1)); - - privArray = (*env)->NewByteArray(env, privkeylen); - (*env)->SetByteArrayRegion(env, privArray, 0, privkeylen, (jbyte*)privkey); - (*env)->SetObjectArrayElement(env, retArray, 0, privArray); - - 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 jobjectArray JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1privkey_1tweak_1add - (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) -{ - secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; - unsigned char* privkey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); - const unsigned char* tweak = (unsigned char*) (privkey + 32); - - jobjectArray retArray; - jbyteArray privArray, intsByteArray; - unsigned char intsarray[2]; - - int privkeylen = 32; - - int ret = secp256k1_ec_privkey_tweak_add(ctx, privkey, tweak); - - intsarray[0] = privkeylen; - intsarray[1] = ret; - - retArray = (*env)->NewObjectArray(env, 2, - (*env)->FindClass(env, "[B"), - (*env)->NewByteArray(env, 1)); - - privArray = (*env)->NewByteArray(env, privkeylen); - (*env)->SetByteArrayRegion(env, privArray, 0, privkeylen, (jbyte*)privkey); - (*env)->SetObjectArrayElement(env, retArray, 0, privArray); - - 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 jobjectArray JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1privkey_1tweak_1mul - (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) -{ - secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; - unsigned char* privkey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); - const unsigned char* tweak = (unsigned char*) (privkey + 32); - - jobjectArray retArray; - jbyteArray privArray, intsByteArray; - unsigned char intsarray[2]; - - int privkeylen = 32; - - int ret = secp256k1_ec_privkey_tweak_mul(ctx, privkey, tweak); - - intsarray[0] = privkeylen; - intsarray[1] = ret; - - retArray = (*env)->NewObjectArray(env, 2, - (*env)->FindClass(env, "[B"), - (*env)->NewByteArray(env, 1)); - - privArray = (*env)->NewByteArray(env, privkeylen); - (*env)->SetByteArrayRegion(env, privArray, 0, privkeylen, (jbyte*)privkey); - (*env)->SetObjectArrayElement(env, retArray, 0, privArray); - - intsByteArray = (*env)->NewByteArray(env, 2); - (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); - (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); - - (void)classObject; - - return retArray; -} - -JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1pubkey_1negate -(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen) -{ - secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; - unsigned char* pkey = (*env)->GetDirectBufferAddress(env, byteBufferObject); - - jobjectArray retArray; - jbyteArray pubArray, intsByteArray; - unsigned char intsarray[2]; - unsigned char outputSer[65]; - size_t outputLen = publen; - - secp256k1_pubkey pubkey; - int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pkey, publen); - - if( ret ) { - ret = secp256k1_ec_pubkey_negate(ctx, &pubkey); - } - - if( ret ) { - int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey, publen == 33 ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED );(void)ret2; - } - - intsarray[0] = outputLen; - intsarray[1] = ret; - - retArray = (*env)->NewObjectArray(env, 2, - (*env)->FindClass(env, "[B"), - (*env)->NewByteArray(env, 1)); - - pubArray = (*env)->NewByteArray(env, outputLen); - (*env)->SetByteArrayRegion(env, pubArray, 0, outputLen, (jbyte*)outputSer); - (*env)->SetObjectArrayElement(env, retArray, 0, pubArray); - - 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 jobjectArray JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1pubkey_1tweak_1add - (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen) -{ - secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; -/* secp256k1_pubkey* pubkey = (secp256k1_pubkey*) (*env)->GetDirectBufferAddress(env, byteBufferObject);*/ - unsigned char* pkey = (*env)->GetDirectBufferAddress(env, byteBufferObject); - const unsigned char* tweak = (unsigned char*) (pkey + publen); - - jobjectArray retArray; - jbyteArray pubArray, intsByteArray; - unsigned char intsarray[2]; - unsigned char outputSer[65]; - size_t outputLen = publen; - - secp256k1_pubkey pubkey; - int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pkey, publen); - - if( ret ) { - ret = secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, tweak); - } - - if( ret ) { - int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey, publen == 33 ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED);(void)ret2; - } - - intsarray[0] = outputLen; - intsarray[1] = ret; - - retArray = (*env)->NewObjectArray(env, 2, - (*env)->FindClass(env, "[B"), - (*env)->NewByteArray(env, 1)); - - pubArray = (*env)->NewByteArray(env, outputLen); - (*env)->SetByteArrayRegion(env, pubArray, 0, outputLen, (jbyte*)outputSer); - (*env)->SetObjectArrayElement(env, retArray, 0, pubArray); - - 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 jobjectArray JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1pubkey_1tweak_1mul - (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen) -{ - secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; - unsigned char* pkey = (*env)->GetDirectBufferAddress(env, byteBufferObject); - const unsigned char* tweak = (unsigned char*) (pkey + publen); - - jobjectArray retArray; - jbyteArray pubArray, intsByteArray; - unsigned char intsarray[2]; - unsigned char outputSer[65]; - size_t outputLen = publen; - - secp256k1_pubkey pubkey; - int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pkey, publen); - - if ( ret ) { - ret = secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, tweak); - } - - if( ret ) { - int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey, publen == 33 ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED);(void)ret2; - } - - intsarray[0] = outputLen; - intsarray[1] = ret; - - retArray = (*env)->NewObjectArray(env, 2, - (*env)->FindClass(env, "[B"), - (*env)->NewByteArray(env, 1)); - - pubArray = (*env)->NewByteArray(env, outputLen); - (*env)->SetByteArrayRegion(env, pubArray, 0, outputLen, (jbyte*)outputSer); - (*env)->SetObjectArrayElement(env, retArray, 0, pubArray); - - intsByteArray = (*env)->NewByteArray(env, 2); - (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); - (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); - - (void)classObject; - - return retArray; -} - -JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1add -(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen1, jint publen2) -{ - secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; - const unsigned char* pubdata1 = (*env)->GetDirectBufferAddress(env, byteBufferObject); - const unsigned char* pubdata2 = (const unsigned char*) (pubdata1 + publen1); - jobjectArray retArray; - jbyteArray pubArray, intsByteArray; - unsigned char intsarray[2]; - secp256k1_pubkey pubkey1, pubkey2; - const secp256k1_pubkey *pubkeys[2]; - unsigned char outputSer[65]; - size_t outputLen = publen1; - - int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey1, pubdata1, publen1); - if (ret) { - ret = secp256k1_ec_pubkey_parse(ctx, &pubkey2, pubdata2, publen2); - } - secp256k1_pubkey result; - if (ret) { - pubkeys[0] = &pubkey1; - pubkeys[1] = &pubkey2; - ret = secp256k1_ec_pubkey_combine(ctx, &result, pubkeys, 2); - } - if (ret) { - ret = secp256k1_ec_pubkey_serialize(ctx, outputSer, &outputLen, &result, publen1 == 33 ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED ); - } - intsarray[0] = outputLen; - intsarray[1] = ret; - - retArray = (*env)->NewObjectArray(env, 2, - (*env)->FindClass(env, "[B"), - (*env)->NewByteArray(env, 1)); - - pubArray = (*env)->NewByteArray(env, outputLen); - (*env)->SetByteArrayRegion(env, pubArray, 0, outputLen, (jbyte*)outputSer); - (*env)->SetObjectArrayElement(env, retArray, 0, pubArray); - - 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 jobjectArray JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1ecdh - (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen) -{ - secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; - const unsigned char* secdata = (*env)->GetDirectBufferAddress(env, byteBufferObject); - const unsigned char* pubdata = (const unsigned char*) (secdata + 32); - - jobjectArray retArray; - jbyteArray outArray, intsByteArray; - unsigned char intsarray[1]; - secp256k1_pubkey pubkey; - unsigned char nonce_res[32]; - size_t outputLen = 32; - - int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pubdata, publen); - - if (ret) { - ret = secp256k1_ecdh( - ctx, - nonce_res, - &pubkey, - secdata, - NULL, - NULL - ); - } - - intsarray[0] = ret; - - retArray = (*env)->NewObjectArray(env, 2, - (*env)->FindClass(env, "[B"), - (*env)->NewByteArray(env, 1)); - - outArray = (*env)->NewByteArray(env, outputLen); - (*env)->SetByteArrayRegion(env, outArray, 0, 32, (jbyte*)nonce_res); - (*env)->SetObjectArrayElement(env, retArray, 0, outArray); - - intsByteArray = (*env)->NewByteArray(env, 1); - (*env)->SetByteArrayRegion(env, intsByteArray, 0, 1, (jbyte*)intsarray); - (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); - - (void)classObject; - - return retArray; -} - -/* - * Class: org_bitcoin_NativeSecp256k1 - * Method: secp256k1_ecdsa_recover - * Signature: (Ljava/nio/ByteBuffer;JI)[[B - */ -JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_Secp256k1CFunctions_secp256k1_1ecdsa_1recover - (JNIEnv *env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint recid, jboolean compressed) -{ - secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; - const unsigned char* sigdata = (*env)->GetDirectBufferAddress(env, byteBufferObject); - const unsigned char* msgdata = (const unsigned char*)(sigdata + 64); - secp256k1_ecdsa_recoverable_signature sig; - secp256k1_pubkey pub; - unsigned char outputSer[65]; - size_t outputLen = compressed ? 33 : 65; - jobjectArray retArray; - jbyteArray pubArray, intsByteArray; - unsigned char intsarray[1]; - - int ret = secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &sig, sigdata, recid); - if (ret) { - ret = secp256k1_ecdsa_recover(ctx, &pub, &sig, msgdata); - if (ret) { - ret = secp256k1_ec_pubkey_serialize(ctx, outputSer, &outputLen, &pub, compressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED ); - } - } - - intsarray[0] = ret; - - retArray = (*env)->NewObjectArray(env, 2, - (*env)->FindClass(env, "[B"), - (*env)->NewByteArray(env, 1)); - - pubArray = (*env)->NewByteArray(env, outputLen); - (*env)->SetByteArrayRegion(env, pubArray, 0, outputLen, (jbyte*)outputSer); - (*env)->SetObjectArrayElement(env, retArray, 0, pubArray); - - intsByteArray = (*env)->NewByteArray(env, 1); - (*env)->SetByteArrayRegion(env, intsByteArray, 0, 1, (jbyte*)intsarray); - (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); - - (void)classObject; - - return retArray; -} - diff --git a/jni/jvm/src/main/kotlin/fr/acinq/secp256k1/jni/NativeSecp256k1Loader.kt b/jni/jvm/src/main/kotlin/fr/acinq/secp256k1/jni/NativeSecp256k1Loader.kt index d40b199..e9e4efd 100644 --- a/jni/jvm/src/main/kotlin/fr/acinq/secp256k1/jni/NativeSecp256k1Loader.kt +++ b/jni/jvm/src/main/kotlin/fr/acinq/secp256k1/jni/NativeSecp256k1Loader.kt @@ -1,7 +1,7 @@ package fr.acinq.secp256k1.jni import fr.acinq.secp256k1.Secp256k1 -import org.bitcoin.NativeSecp256k1 +import fr.acinq.secp256k1.NativeSecp256k1 import java.io.* import java.util.* diff --git a/jni/src/main/java/fr/acinq/secp256k1/Secp256k1CFunctions.java b/jni/src/main/java/fr/acinq/secp256k1/Secp256k1CFunctions.java new file mode 100644 index 0000000..34e1ce5 --- /dev/null +++ b/jni/src/main/java/fr/acinq/secp256k1/Secp256k1CFunctions.java @@ -0,0 +1,67 @@ +package fr.acinq.secp256k1; + +public class Secp256k1CFunctions { + /** + * All flags' lower 8 bits indicate what they're for. Do not use directly. + */ + public static int SECP256K1_FLAGS_TYPE_MASK = ((1 << 8) - 1); + public static int SECP256K1_FLAGS_TYPE_CONTEXT = (1 << 0); + public static int SECP256K1_FLAGS_TYPE_COMPRESSION = (1 << 1); + + /** + * The higher bits contain the actual data. Do not use directly. + */ + public static int SECP256K1_FLAGS_BIT_CONTEXT_VERIFY = (1 << 8); + public static int SECP256K1_FLAGS_BIT_CONTEXT_SIGN = (1 << 9); + public static int SECP256K1_FLAGS_BIT_COMPRESSION = (1 << 8); + + /** + * Flags to pass to secp256k1_context_create, secp256k1_context_preallocated_size, and + * secp256k1_context_preallocated_create. + */ + public static int SECP256K1_CONTEXT_VERIFY = (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_VERIFY); + public static int SECP256K1_CONTEXT_SIGN = (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_SIGN); + public static int SECP256K1_CONTEXT_NONE = (SECP256K1_FLAGS_TYPE_CONTEXT); + + /** + * Flag to pass to secp256k1_ec_pubkey_serialize. + */ + public static int SECP256K1_EC_COMPRESSED = (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION); + public static int SECP256K1_EC_UNCOMPRESSED = (SECP256K1_FLAGS_TYPE_COMPRESSION); + + public static native long secp256k1_context_create(int flags); + + public static native void secp256k1_context_destroy(long ctx); + + public static native int secp256k1_ec_seckey_verify(long ctx, byte[] seckey); + + public static native byte[] secp256k1_ec_pubkey_parse(long ctx, byte[] pubkey); + + public static native byte[] secp256k1_ec_pubkey_create(long ctx, byte[] seckey); + + public static native byte[] secp256k1_ecdsa_sign(long ctx, byte[] msg, byte[] seckey); + + public static native int secp256k1_ecdsa_verify(long ctx, byte[] sig, byte[] msg, byte[] pubkey); + + public static native int secp256k1_ecdsa_signature_normalize(long ctx, byte[] sigin, byte[] sigout); + + public static native byte[] secp256k1_ec_privkey_negate(long ctx, byte[] privkey); + + public static native byte[] secp256k1_ec_pubkey_negate(long ctx, byte[] pubkey); + + public static native byte[] secp256k1_ec_privkey_tweak_add(long ctx, byte[] seckey, byte[] tweak); + + public static native byte[] secp256k1_ec_pubkey_tweak_add(long ctx, byte[] pubkey, byte[] tweak); + + public static native byte[] secp256k1_ec_privkey_tweak_mul(long ctx, byte[] seckey, byte[] tweak); + + public static native byte[] secp256k1_ec_pubkey_tweak_mul(long ctx, byte[] pubkey, byte[] tweak); + + public static native byte[] secp256k1_ec_pubkey_add(long ctx, byte[] pubkey1, byte[] pubkey2); + + public static native byte[] secp256k1_ec_pubkey_combine(long ctx, byte[][] pubkeys); + + public static native byte[] secp256k1_ecdh(long ctx, byte[] seckey, byte[] pubkey); + + public static native byte[] secp256k1_ecdsa_recover(long ctx, byte[] sig, byte[] msg32, int recid); +} diff --git a/jni/src/main/java/org/bitcoin/Secp256k1CFunctions.java b/jni/src/main/java/org/bitcoin/Secp256k1CFunctions.java deleted file mode 100644 index 0a0de79..0000000 --- a/jni/src/main/java/org/bitcoin/Secp256k1CFunctions.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.bitcoin; - -import java.nio.ByteBuffer; - -public class Secp256k1CFunctions { - static native long secp256k1_init_context(); - static native int secp256k1_context_randomize(ByteBuffer byteBuff, long context); - static native byte[][] secp256k1_privkey_negate(ByteBuffer byteBuff, long context); - static native byte[][] secp256k1_privkey_tweak_add(ByteBuffer byteBuff, long context); - static native byte[][] secp256k1_privkey_tweak_mul(ByteBuffer byteBuff, long context); - static native byte[][] secp256k1_pubkey_negate(ByteBuffer byteBuff, long context, int pubLen); - static native byte[][] secp256k1_pubkey_tweak_add(ByteBuffer byteBuff, long context, int pubLen); - static native byte[][] secp256k1_pubkey_tweak_mul(ByteBuffer byteBuff, long context, int pubLen); - static native void secp256k1_destroy_context(long context); - static native int secp256k1_ecdsa_verify(ByteBuffer byteBuff, long context, int sigLen, int pubLen); - static native byte[][] secp256k1_ecdsa_sign(ByteBuffer byteBuff, boolean compact, long context); - static native byte[][] secp256k1_ecdsa_normalize(ByteBuffer byteBuff, int sigLen, boolean compact, long context); - static native int secp256k1_ec_seckey_verify(ByteBuffer byteBuff, long context); - static native byte[][] secp256k1_ec_pubkey_create(ByteBuffer byteBuff, boolean compressed, long context); - static native byte[][] secp256k1_ec_pubkey_parse(ByteBuffer byteBuff, long context, int inputLen, boolean compressed); - static native byte[][] secp256k1_ec_pubkey_add(ByteBuffer byteBuff, long context, int lent1, int len2); - static native byte[][] secp256k1_ecdh(ByteBuffer byteBuff, long context, int inputLen); - static native byte[][] secp256k1_ecdsa_recover(ByteBuffer byteBuff, long context, int recid, boolean compressed); -} diff --git a/jni/src/main/kotlin/fr/acinq/secp256k1/NativeSecp256k1.kt b/jni/src/main/kotlin/fr/acinq/secp256k1/NativeSecp256k1.kt new file mode 100644 index 0000000..b604090 --- /dev/null +++ b/jni/src/main/kotlin/fr/acinq/secp256k1/NativeSecp256k1.kt @@ -0,0 +1,86 @@ +/* + * Copyright 2013 Google Inc. + * Copyright 2014-2016 the libsecp256k1 contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package fr.acinq.secp256k1 + +public object NativeSecp256k1 : Secp256k1 { + override fun verify(signature: ByteArray, data: ByteArray, pub: ByteArray): Boolean { + return Secp256k1CFunctions.secp256k1_ecdsa_verify(Secp256k1Context.getContext(), signature, data, pub) == 1 + } + + override fun sign(data: ByteArray, sec: ByteArray): ByteArray { + return Secp256k1CFunctions.secp256k1_ecdsa_sign(Secp256k1Context.getContext(), data, sec) + } + + override fun signatureNormalize(sig: ByteArray): Pair { + val sigout = ByteArray(64) + val result = Secp256k1CFunctions.secp256k1_ecdsa_signature_normalize(Secp256k1Context.getContext(), sig, sigout) + return Pair(sigout, result == 1) + } + + override fun secKeyVerify(seckey: ByteArray): Boolean { + val result = Secp256k1CFunctions.secp256k1_ec_seckey_verify(Secp256k1Context.getContext(), seckey); + return result == 1; + } + + override fun pubkeyCreate(seckey: ByteArray): ByteArray { + return Secp256k1CFunctions.secp256k1_ec_pubkey_create(Secp256k1Context.getContext(), seckey) + } + + override fun pubkeyParse(pubkey: ByteArray): ByteArray { + return Secp256k1CFunctions.secp256k1_ec_pubkey_parse(Secp256k1Context.getContext(), pubkey) + } + + override fun cleanup() { + return Secp256k1CFunctions.secp256k1_context_destroy(Secp256k1Context.getContext()) + } + + override fun privKeyNegate(privkey: ByteArray): ByteArray { + return Secp256k1CFunctions.secp256k1_ec_privkey_negate(Secp256k1Context.getContext(), privkey) + } + + override fun privKeyTweakMul(privkey: ByteArray, tweak: ByteArray): ByteArray { + return Secp256k1CFunctions.secp256k1_ec_privkey_tweak_mul(Secp256k1Context.getContext(), privkey, tweak) + } + + override fun privKeyTweakAdd(privkey: ByteArray, tweak: ByteArray): ByteArray { + return Secp256k1CFunctions.secp256k1_ec_privkey_tweak_add(Secp256k1Context.getContext(), privkey, tweak) + } + + override fun pubKeyNegate(pubkey: ByteArray): ByteArray { + return Secp256k1CFunctions.secp256k1_ec_pubkey_negate(Secp256k1Context.getContext(), pubkey) + } + + override fun pubKeyTweakAdd(pubkey: ByteArray, tweak: ByteArray): ByteArray { + return Secp256k1CFunctions.secp256k1_ec_pubkey_tweak_add(Secp256k1Context.getContext(), pubkey, tweak) + } + + override fun pubKeyTweakMul(pubkey: ByteArray, tweak: ByteArray): ByteArray { + return Secp256k1CFunctions.secp256k1_ec_pubkey_tweak_mul(Secp256k1Context.getContext(), pubkey, tweak) + } + + override fun pubKeyAdd(pubkey1: ByteArray, pubkey2: ByteArray): ByteArray { + return Secp256k1CFunctions.secp256k1_ec_pubkey_add(Secp256k1Context.getContext(), pubkey1, pubkey2) + } + + override fun ecdh(seckey: ByteArray, pubkey: ByteArray): ByteArray { + return Secp256k1CFunctions.secp256k1_ecdh(Secp256k1Context.getContext(), seckey, pubkey) + } + + override fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int): ByteArray { + return Secp256k1CFunctions.secp256k1_ecdsa_recover(Secp256k1Context.getContext(), sig, message, recid) + } +} diff --git a/jni/src/main/kotlin/org/bitcoin/NativeSecp256k1Util.kt b/jni/src/main/kotlin/fr/acinq/secp256k1/NativeSecp256k1Util.kt similarity index 97% rename from jni/src/main/kotlin/org/bitcoin/NativeSecp256k1Util.kt rename to jni/src/main/kotlin/fr/acinq/secp256k1/NativeSecp256k1Util.kt index e1f7fe0..7bfa9fc 100644 --- a/jni/src/main/kotlin/org/bitcoin/NativeSecp256k1Util.kt +++ b/jni/src/main/kotlin/fr/acinq/secp256k1/NativeSecp256k1Util.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.bitcoin +package fr.acinq.secp256k1 import java.lang.Exception diff --git a/jni/src/main/kotlin/org/bitcoin/Secp256k1Context.kt b/jni/src/main/kotlin/fr/acinq/secp256k1/Secp256k1Context.kt similarity index 85% rename from jni/src/main/kotlin/org/bitcoin/Secp256k1Context.kt rename to jni/src/main/kotlin/fr/acinq/secp256k1/Secp256k1Context.kt index 832b788..ba1a9a2 100644 --- a/jni/src/main/kotlin/org/bitcoin/Secp256k1Context.kt +++ b/jni/src/main/kotlin/fr/acinq/secp256k1/Secp256k1Context.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.bitcoin +package fr.acinq.secp256k1 /** * This class holds the context reference used in native methods @@ -31,6 +31,6 @@ public object Secp256k1Context { init { //static initializer isEnabled = true - context = Secp256k1CFunctions.secp256k1_init_context() + context = Secp256k1CFunctions.secp256k1_context_create(Secp256k1CFunctions.SECP256K1_CONTEXT_SIGN or Secp256k1CFunctions.SECP256K1_CONTEXT_VERIFY) } } diff --git a/jni/src/main/kotlin/org/bitcoin/NativeSecp256k1.kt b/jni/src/main/kotlin/org/bitcoin/NativeSecp256k1.kt deleted file mode 100644 index c595706..0000000 --- a/jni/src/main/kotlin/org/bitcoin/NativeSecp256k1.kt +++ /dev/null @@ -1,533 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * Copyright 2014-2016 the libsecp256k1 contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.bitcoin - -import fr.acinq.secp256k1.PubKeyFormat -import fr.acinq.secp256k1.Secp256k1 -import fr.acinq.secp256k1.SigFormat -import org.bitcoin.NativeSecp256k1Util.AssertFailException -import java.math.BigInteger -import java.nio.ByteBuffer -import java.nio.ByteOrder -import java.util.concurrent.locks.Lock -import java.util.concurrent.locks.ReentrantReadWriteLock - -/** - * - * This class holds native methods to handle ECDSA verification. - * - * - * You can find an example library that can be used for this at https://github.com/bitcoin/secp256k1 - * - * - * To build secp256k1 for use with bitcoinj, run - * `./configure --enable-jni --enable-experimental --enable-module-ecdh` - * and `make` then copy `.libs/libsecp256k1.so` to your system library path - * or point the JVM to the folder containing it with -Djava.library.path - * - */ -public object NativeSecp256k1 : Secp256k1 { - private val rwl = ReentrantReadWriteLock() - private val r: Lock = rwl.readLock() - private val w: Lock = rwl.writeLock() - private val nativeECDSABuffer = ThreadLocal() - - private fun pack(vararg buffers: ByteArray): ByteBuffer { - var size = 0 - for (i in buffers.indices) { - size += buffers[i].size - } - - val byteBuff = nativeECDSABuffer.get()?.takeIf { it.capacity() >= size } - ?: ByteBuffer.allocateDirect(size).also { - it.order(ByteOrder.nativeOrder()) - nativeECDSABuffer.set(it) - } - byteBuff.rewind() - for (i in buffers.indices) { - byteBuff.put(buffers[i]) - } - return byteBuff - } - - /** - * Verifies the given secp256k1 signature in native code. - * Calling when enabled == false is undefined (probably library not loaded) - * - * @param data The data which was signed, must be exactly 32 bytes - * @param signature The signature - * @param pub The public key which did the signing - * @return true if the signature is valid - * @throws AssertFailException in case of failure - */ - @Throws(AssertFailException::class) - override fun verify(data: ByteArray, signature: ByteArray, pub: ByteArray): Boolean { - require(data.size == 32 && signature.size <= 520 && pub.size <= 520) - val byteBuff = pack(data, signature, pub) - r.lock() - return try { - Secp256k1CFunctions.secp256k1_ecdsa_verify( - byteBuff, - Secp256k1Context.getContext(), - signature.size, - pub.size - ) == 1 - } finally { - r.unlock() - } - } - - /** - * libsecp256k1 Create an ECDSA signature. - * - * @param data Message hash, 32 bytes - * @param sec Secret key, 32 bytes - * @param compact True for compact signature, false for DER - * @return a signature, or an empty array is signing failed - * @throws AssertFailException in case of failure - */ - @Throws(AssertFailException::class) - override fun sign(data: ByteArray, sec: ByteArray, format: SigFormat): ByteArray { - require(data.size == 32 && sec.size <= 32) - val byteBuff = pack(data, sec) - val retByteArray: Array - r.lock() - retByteArray = try { - Secp256k1CFunctions.secp256k1_ecdsa_sign( - byteBuff, - format == SigFormat.COMPACT, - Secp256k1Context.getContext() - ) - } finally { - r.unlock() - } - val sigArr = retByteArray[0] - val sigLen = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() - val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt() - NativeSecp256k1Util.assertEquals( - sigArr.size, - sigLen, - "Got bad signature length." - ) - return if (retVal == 0) ByteArray(0) else sigArr - } - - @Throws(AssertFailException::class) - override fun signatureNormalize(sig: ByteArray, format: SigFormat): Pair { - require(sig.size == 64 || sig.size in 70..73) - val byteBuff = pack(sig) - val retByteArray: Array - r.lock() - retByteArray = try { - Secp256k1CFunctions.secp256k1_ecdsa_normalize( - byteBuff, - sig.size, - format == SigFormat.COMPACT, - Secp256k1Context.getContext() - ) - } finally { - r.unlock() - } - val sigArr = retByteArray[0] - val sigLen = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() - val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt() - val retBool = BigInteger(byteArrayOf(retByteArray[1][2])).toInt() - NativeSecp256k1Util.assertEquals( - sigArr.size, - sigLen, - "Got bad signature length." - ) - return (if (retVal == 0) ByteArray(0) else sigArr) to (retBool == 1) - } - - /** - * libsecp256k1 Seckey Verify - returns 1 if valid, 0 if invalid - * - * @param seckey ECDSA Secret key, 32 bytes - * @return true if seckey is valid - */ - override fun secKeyVerify(seckey: ByteArray): Boolean { - require(seckey.size == 32) - val byteBuff = pack(seckey) - r.lock() - return try { - Secp256k1CFunctions.secp256k1_ec_seckey_verify( - byteBuff, - Secp256k1Context.getContext() - ) == 1 - } finally { - r.unlock() - } - } - - /** - * libsecp256k1 Compute Pubkey - computes public key from secret key - * - * @param seckey ECDSA Secret key, 32 bytes - * @throws AssertFailException if parameters are not valid - * @return the corresponding public key (uncompressed) - */ - //TODO add a 'compressed' arg - @Throws(AssertFailException::class) - override fun computePubkey(seckey: ByteArray, format: PubKeyFormat): ByteArray { - require(seckey.size == 32) - val byteBuff = pack(seckey) - val retByteArray: Array - r.lock() - retByteArray = try { - Secp256k1CFunctions.secp256k1_ec_pubkey_create( - byteBuff, - format == PubKeyFormat.COMPRESSED, - Secp256k1Context.getContext() - ) - } finally { - r.unlock() - } - val pubArr = retByteArray[0] - val pubLen = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() - val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt() - NativeSecp256k1Util.assertEquals(pubArr.size, pubLen, "Got bad pubkey length.") - return if (retVal == 0) ByteArray(0) else pubArr - } - - /** - * @param pubkey public key - * @return the input public key (uncompressed) if valid, or an empty array - * @throws AssertFailException in case of failure - */ - @Throws(AssertFailException::class) - override fun parsePubkey(pubkey: ByteArray, format: PubKeyFormat): ByteArray { - require(pubkey.size == 33 || pubkey.size == 65) - val byteBuff = pack(pubkey) - val retByteArray: Array - r.lock() - retByteArray = try { - Secp256k1CFunctions.secp256k1_ec_pubkey_parse( - byteBuff, - Secp256k1Context.getContext(), - pubkey.size, - format == PubKeyFormat.COMPRESSED - ) - } finally { - r.unlock() - } - val pubArr = retByteArray[0] - BigInteger(byteArrayOf(retByteArray[1][0])).toInt() - val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt() - NativeSecp256k1Util.assertEquals( - pubArr.size, - format.maxSize, - "Got bad pubkey length." - ) - return if (retVal == 0) ByteArray(0) else pubArr - } - - /** - * libsecp256k1 Cleanup - This destroys the secp256k1 context object - * This should be called at the end of the program for proper cleanup of the context. - */ - @Synchronized - override fun cleanup() { - w.lock() - try { - Secp256k1CFunctions.secp256k1_destroy_context(Secp256k1Context.getContext()) - } finally { - w.unlock() - } - } - - @Throws(AssertFailException::class) - override fun privKeyNegate(privkey: ByteArray): ByteArray { - require(privkey.size == 32) - val byteBuff = pack(privkey) - val retByteArray: Array - r.lock() - retByteArray = try { - Secp256k1CFunctions.secp256k1_privkey_negate( - byteBuff, - Secp256k1Context.getContext() - ) - } finally { - r.unlock() - } - val privArr = retByteArray[0] - val privLen: Int = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() and 0xFF - val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt() - NativeSecp256k1Util.assertEquals( - privArr.size, - privLen, - "Got bad privkey length." - ) - NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.") - return privArr - } - - /** - * libsecp256k1 PrivKey Tweak-Mul - Tweak privkey by multiplying to it - * - * @param privkey 32-byte seckey - * @param tweak some bytes to tweak with - * @return privkey * tweak - * @throws AssertFailException in case of failure - */ - @Throws(AssertFailException::class) - override fun privKeyTweakMul(privkey: ByteArray, tweak: ByteArray): ByteArray { - require(privkey.size == 32) - val byteBuff = pack(privkey, tweak) - val retByteArray: Array - r.lock() - retByteArray = try { - Secp256k1CFunctions.secp256k1_privkey_tweak_mul( - byteBuff, - Secp256k1Context.getContext() - ) - } finally { - r.unlock() - } - val privArr = retByteArray[0] - val privLen: Int = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() and 0xFF - val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt() - NativeSecp256k1Util.assertEquals( - privArr.size, - privLen, - "Got bad privkey length." - ) - NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.") - return privArr - } - - /** - * libsecp256k1 PrivKey Tweak-Add - Tweak privkey by adding to it - * - * @param privkey 32-byte seckey - * @param tweak some bytes to tweak with - * @return privkey + tweak - * @throws AssertFailException in case of failure - */ - @Throws(AssertFailException::class) - override fun privKeyTweakAdd(privkey: ByteArray, tweak: ByteArray): ByteArray { - require(privkey.size == 32) - val byteBuff = pack(privkey, tweak) - val retByteArray: Array - r.lock() - retByteArray = try { - Secp256k1CFunctions.secp256k1_privkey_tweak_add( - byteBuff, - Secp256k1Context.getContext() - ) - } finally { - r.unlock() - } - val privArr = retByteArray[0] - val privLen: Int = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() and 0xFF - val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt() - NativeSecp256k1Util.assertEquals(privArr.size, privLen, "Got bad pubkey length.") - NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.") - return privArr - } - - @Throws(AssertFailException::class) - override fun pubKeyNegate(pubkey: ByteArray): ByteArray { - require(pubkey.size == 33 || pubkey.size == 65) - val byteBuff = pack(pubkey) - val retByteArray: Array - r.lock() - retByteArray = try { - Secp256k1CFunctions.secp256k1_pubkey_negate( - byteBuff, - Secp256k1Context.getContext(), - pubkey.size - ) - } finally { - r.unlock() - } - val pubArr = retByteArray[0] - val pubLen: Int = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() and 0xFF - val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt() - NativeSecp256k1Util.assertEquals(pubArr.size, pubLen, "Got bad pubkey length.") - NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.") - return pubArr - } - - /** - * libsecp256k1 PubKey Tweak-Add - Tweak pubkey by adding to it - * - * @param tweak some bytes to tweak with - * @param pubkey 32-byte seckey - * @return pubkey + tweak - * @throws AssertFailException in case of failure - */ - @Throws(AssertFailException::class) - override fun pubKeyTweakAdd(pubkey: ByteArray, tweak: ByteArray): ByteArray { - require(pubkey.size == 33 || pubkey.size == 65) - val byteBuff = pack(pubkey, tweak) - val retByteArray: Array - r.lock() - retByteArray = try { - Secp256k1CFunctions.secp256k1_pubkey_tweak_add( - byteBuff, - Secp256k1Context.getContext(), - pubkey.size - ) - } finally { - r.unlock() - } - val pubArr = retByteArray[0] - val pubLen: Int = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() and 0xFF - val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt() - NativeSecp256k1Util.assertEquals(pubArr.size, pubLen, "Got bad pubkey length.") - NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.") - return pubArr - } - - /** - * libsecp256k1 PubKey Tweak-Mul - Tweak pubkey by multiplying to it - * - * @param tweak some bytes to tweak with - * @param pubkey 32-byte seckey - * @return pubkey * tweak - * @throws AssertFailException in case of failure - */ - @Throws(AssertFailException::class) - override fun pubKeyTweakMul(pubkey: ByteArray, tweak: ByteArray): ByteArray { - require(pubkey.size == 33 || pubkey.size == 65) - val byteBuff = pack(pubkey, tweak) - val retByteArray: Array - r.lock() - retByteArray = try { - Secp256k1CFunctions.secp256k1_pubkey_tweak_mul( - byteBuff, - Secp256k1Context.getContext(), - pubkey.size - ) - } finally { - r.unlock() - } - val pubArr = retByteArray[0] - val pubLen: Int = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() and 0xFF - val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt() - NativeSecp256k1Util.assertEquals(pubArr.size, pubLen, "Got bad pubkey length.") - NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.") - return pubArr - } - - @Throws(AssertFailException::class) - override fun pubKeyAdd(pubkey1: ByteArray, pubkey2: ByteArray): ByteArray { - require(pubkey1.size == 33 || pubkey1.size == 65) - require(pubkey2.size == 33 || pubkey2.size == 65) - val byteBuff = pack(pubkey1, pubkey2) - val retByteArray: Array - r.lock() - retByteArray = try { - Secp256k1CFunctions.secp256k1_ec_pubkey_add( - byteBuff, - Secp256k1Context.getContext(), - pubkey1.size, - pubkey2.size - ) - } finally { - r.unlock() - } - val pubArr = retByteArray[0] - val pubLen: Int = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() and 0xFF - val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt() - NativeSecp256k1Util.assertEquals(pubkey1.size, pubLen, "Got bad pubkey length.") - NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.") - return pubArr - } - - /** - * libsecp256k1 create ECDH secret - constant time ECDH calculation - * - * @param seckey byte array of secret key used in exponentiaion - * @param pubkey byte array of public key used in exponentiaion - * @return ecdh(sedckey, pubkey) - * @throws AssertFailException in case of failure - */ - @Throws(AssertFailException::class) - override fun createECDHSecret(seckey: ByteArray, pubkey: ByteArray): ByteArray { - require(seckey.size <= 32 && pubkey.size <= 65) - val byteBuff = pack(seckey, pubkey) - val retByteArray: Array - r.lock() - retByteArray = try { - Secp256k1CFunctions.secp256k1_ecdh( - byteBuff, - Secp256k1Context.getContext(), - pubkey.size - ) - } finally { - r.unlock() - } - val resArr = retByteArray[0] - val retVal = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() - NativeSecp256k1Util.assertEquals(resArr.size, 32, "Got bad result length.") - NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.") - return resArr - } - - @Throws(AssertFailException::class) - override fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int, format: PubKeyFormat): ByteArray { - require(sig.size == 64) - require(message.size == 32) - val byteBuff = pack(sig, message) - val retByteArray: Array - r.lock() - retByteArray = try { - Secp256k1CFunctions.secp256k1_ecdsa_recover( - byteBuff, - Secp256k1Context.getContext(), - recid, - format == PubKeyFormat.COMPRESSED - ) - } finally { - r.unlock() - } - val resArr = retByteArray[0] - val retVal = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() - NativeSecp256k1Util.assertEquals( - resArr.size, - format.maxSize, - "Got bad result length." - ) - NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.") - return resArr - } - - /** - * libsecp256k1 randomize - updates the context randomization - * - * @param seed 32-byte random seed - * @return true if successful - * @throws AssertFailException in case of failure - */ - @Synchronized - @Throws(AssertFailException::class) - override fun randomize(seed: ByteArray): Boolean { - require(seed.size == 32) - val byteBuff = pack(seed) - w.lock() - return try { - Secp256k1CFunctions.secp256k1_context_randomize( - byteBuff, - Secp256k1Context.getContext() - ) == 1 - } finally { - w.unlock() - } - } -} diff --git a/src/commonMain/kotlin/fr/acinq/secp256k1/Secp256k1.kt b/src/commonMain/kotlin/fr/acinq/secp256k1/Secp256k1.kt index 89159db..0a312c2 100644 --- a/src/commonMain/kotlin/fr/acinq/secp256k1/Secp256k1.kt +++ b/src/commonMain/kotlin/fr/acinq/secp256k1/Secp256k1.kt @@ -18,23 +18,19 @@ package fr.acinq.secp256k1 import kotlin.jvm.JvmStatic -public enum class SigFormat(public val maxSize: Int) { COMPACT(64), DER(72) } - -public enum class PubKeyFormat(public val maxSize: Int) { COMPRESSED(33), UNCOMPRESSED(65) } - public interface Secp256k1 { - public fun verify(data: ByteArray, signature: ByteArray, pub: ByteArray): Boolean + public fun verify(signature: ByteArray, data: ByteArray, pub: ByteArray): Boolean - public fun sign(data: ByteArray, sec: ByteArray, format: SigFormat): ByteArray + public fun sign(data: ByteArray, sec: ByteArray): ByteArray - public fun signatureNormalize(sig: ByteArray, format: SigFormat): Pair + public fun signatureNormalize(sig: ByteArray): Pair public fun secKeyVerify(seckey: ByteArray): Boolean - public fun computePubkey(seckey: ByteArray, format: PubKeyFormat): ByteArray + public fun pubkeyCreate(seckey: ByteArray): ByteArray - public fun parsePubkey(pubkey: ByteArray, format: PubKeyFormat): ByteArray + public fun pubkeyParse(pubkey: ByteArray): ByteArray public fun cleanup() @@ -52,11 +48,21 @@ public interface Secp256k1 { public fun pubKeyAdd(pubkey1: ByteArray, pubkey2: ByteArray): ByteArray - public fun createECDHSecret(seckey: ByteArray, pubkey: ByteArray): ByteArray + public fun ecdh(seckey: ByteArray, pubkey: ByteArray): ByteArray - public fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int, format: PubKeyFormat): ByteArray + public fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int): ByteArray - public fun randomize(seed: ByteArray): Boolean + public fun pubKeyCompress(pubkey: ByteArray) : ByteArray { + return when { + pubkey.size == 33 && (pubkey[0] == 2.toByte() || pubkey[0] == 3.toByte()) -> pubkey + pubkey.size == 65 && pubkey[0] == 4.toByte() -> { + val pub1 = pubkey.copyOf(33) + pub1[0] = if (pubkey.last() % 2 == 0) 2.toByte() else 3.toByte() + pub1 + } + else -> throw RuntimeException("invalid public key") + } + } public companion object : Secp256k1 by getSecpk256k1() { @JvmStatic public fun get(): Secp256k1 = this @@ -64,3 +70,8 @@ public interface Secp256k1 { } internal expect fun getSecpk256k1(): Secp256k1 + +public class Secp256k1Exception : RuntimeException { + public constructor() : super() {} + public constructor(message: String?) : super(message) {} +} \ No newline at end of file diff --git a/src/nativeMain/kotlin/fr/acinq/secp256k1/Secp256k1Native.kt b/src/nativeMain/kotlin/fr/acinq/secp256k1/Secp256k1Native.kt index 5f124ff..952068d 100644 --- a/src/nativeMain/kotlin/fr/acinq/secp256k1/Secp256k1Native.kt +++ b/src/nativeMain/kotlin/fr/acinq/secp256k1/Secp256k1Native.kt @@ -12,7 +12,7 @@ public object Secp256k1Native : Secp256k1 { ?: error("Could not create segp256k1 context") } - private fun Int.requireSuccess() = require(this == 1) { "secp256k1 native function call failed" } + private fun Int.requireSuccess(message: String): Int = if (this != 1) throw Secp256k1Exception(message) else this private fun MemScope.allocSignature(input: ByteArray): secp256k1_ecdsa_signature { val sig = alloc() @@ -21,40 +21,30 @@ public object Secp256k1Native : Secp256k1 { val result = when (input.size) { 64 -> secp256k1_ecdsa_signature_parse_compact(ctx, sig.ptr, nativeBytes) in 70..73 -> secp256k1_ecdsa_signature_parse_der(ctx, sig.ptr, nativeBytes, input.size.convert()) - else -> error("Unknown signature format") + else -> throw Secp256k1Exception("Unknown signature format") } - require(result == 1) { "cannot parse signature (size = ${input.size} sig = ${Hex.encode(input)}" } + result.requireSuccess("cannot parse signature (size = ${input.size} sig = ${Hex.encode(input)}") return sig } - private fun MemScope.serializeSignature(signature: secp256k1_ecdsa_signature, format: SigFormat): ByteArray { - val natOutput = allocArray(format.maxSize) - when (format) { - SigFormat.DER -> { - val outputLen = alloc() - outputLen.value = 72.convert() - secp256k1_ecdsa_signature_serialize_der(ctx, natOutput, outputLen.ptr, signature.ptr).requireSuccess() - return natOutput.readBytes(outputLen.value.toInt()) - } - SigFormat.COMPACT -> { - secp256k1_ecdsa_signature_serialize_compact(ctx, natOutput, signature.ptr).requireSuccess() - return natOutput.readBytes(64) - } - } + private fun MemScope.serializeSignature(signature: secp256k1_ecdsa_signature): ByteArray { + val natOutput = allocArray(64) + secp256k1_ecdsa_signature_serialize_compact(ctx, natOutput, signature.ptr).requireSuccess("secp256k1_ecdsa_signature_serialize_compact() failed") + return natOutput.readBytes(64) } private fun MemScope.allocPublicKey(pubkey: ByteArray): secp256k1_pubkey { val natPub = toNat(pubkey) val pub = alloc() - secp256k1_ec_pubkey_parse(ctx, pub.ptr, natPub, pubkey.size.convert()).requireSuccess() + secp256k1_ec_pubkey_parse(ctx, pub.ptr, natPub, pubkey.size.convert()).requireSuccess("secp256k1_ec_pubkey_parse() failed") return pub } - private fun MemScope.serializePubkey(pubkey: secp256k1_pubkey, len: Int): ByteArray { - val serialized = allocArray(len) + private fun MemScope.serializePubkey(pubkey: secp256k1_pubkey): ByteArray { + val serialized = allocArray(65) val outputLen = alloc() - outputLen.value = len.convert() - secp256k1_ec_pubkey_serialize(ctx, serialized, outputLen.ptr, pubkey.ptr, (if (len == 33) SECP256K1_EC_COMPRESSED else SECP256K1_EC_UNCOMPRESSED).convert()).requireSuccess() + outputLen.value = 65.convert() + secp256k1_ec_pubkey_serialize(ctx, serialized, outputLen.ptr, pubkey.ptr, SECP256K1_EC_UNCOMPRESSED.convert()).requireSuccess("secp256k1_ec_pubkey_serialize() failed") return serialized.readBytes(outputLen.value.convert()) } @@ -65,7 +55,7 @@ public object Secp256k1Native : Secp256k1 { return pinned.addressOf(0) } - public override fun verify(data: ByteArray, signature: ByteArray, pub: ByteArray): Boolean { + public override fun verify(signature: ByteArray, data: ByteArray, pub: ByteArray): Boolean { require(data.size == 32) require(pub.size == 33 || pub.size == 65) memScoped { @@ -76,25 +66,24 @@ public object Secp256k1Native : Secp256k1 { } } - public override fun sign(data: ByteArray, sec: ByteArray, format: SigFormat): ByteArray { + public override fun sign(data: ByteArray, sec: ByteArray): ByteArray { require(sec.size == 32) require(data.size == 32) memScoped { val nSec = toNat(sec) val nData = toNat(data) val nSig = alloc() - val result = secp256k1_ecdsa_sign(ctx, nSig.ptr, nData, nSec, null, null) - if (result == 0) return ByteArray(0) - return serializeSignature(nSig, format) + secp256k1_ecdsa_sign(ctx, nSig.ptr, nData, nSec, null, null).requireSuccess("secp256k1_ecdsa_sign() failed") + return serializeSignature(nSig) } } - public override fun signatureNormalize(sig: ByteArray, format: SigFormat): Pair { + public override fun signatureNormalize(sig: ByteArray): Pair { require(sig.size == 64 || sig.size in 70..73) memScoped { val nSig = allocSignature(sig) val isHighS = secp256k1_ecdsa_signature_normalize(ctx, nSig.ptr, nSig.ptr) - return Pair(serializeSignature(nSig, format), isHighS == 1) + return Pair(serializeSignature(nSig), isHighS == 1) } } @@ -106,22 +95,21 @@ public object Secp256k1Native : Secp256k1 { } } - public override fun computePubkey(seckey: ByteArray, format: PubKeyFormat): ByteArray { + public override fun pubkeyCreate(seckey: ByteArray): ByteArray { require(seckey.size == 32) memScoped { val nSec = toNat(seckey) val nPubkey = alloc() - val result = secp256k1_ec_pubkey_create(ctx, nPubkey.ptr, nSec) - if (result == 0) return ByteArray(0) - return serializePubkey(nPubkey, format.maxSize) + secp256k1_ec_pubkey_create(ctx, nPubkey.ptr, nSec).requireSuccess("secp256k1_ec_pubkey_create() failed") + return serializePubkey(nPubkey) } } - public override fun parsePubkey(pubkey: ByteArray, format: PubKeyFormat): ByteArray { + public override fun pubkeyParse(pubkey: ByteArray): ByteArray { require(pubkey.size == 33 || pubkey.size == 65) memScoped { val nPubkey = allocPublicKey(pubkey) - return serializePubkey(nPubkey, format.maxSize) + return serializePubkey(nPubkey) } } @@ -134,7 +122,7 @@ public object Secp256k1Native : Secp256k1 { memScoped { val negated = privkey.copyOf() val negPriv = toNat(negated) - secp256k1_ec_privkey_negate(ctx, negPriv).requireSuccess() + secp256k1_ec_privkey_negate(ctx, negPriv).requireSuccess("secp256k1_ec_privkey_negate() failed") return negated } } @@ -145,7 +133,7 @@ public object Secp256k1Native : Secp256k1 { val multiplied = privkey.copyOf() val natMul = toNat(multiplied) val natTweak = toNat(tweak) - secp256k1_ec_privkey_tweak_mul(ctx, natMul, natTweak).requireSuccess() + secp256k1_ec_privkey_tweak_mul(ctx, natMul, natTweak).requireSuccess("secp256k1_ec_privkey_tweak_mul() failed") return multiplied } } @@ -156,7 +144,7 @@ public object Secp256k1Native : Secp256k1 { val added = privkey.copyOf() val natAdd = toNat(added) val natTweak = toNat(tweak) - secp256k1_ec_privkey_tweak_add(ctx, natAdd, natTweak).requireSuccess() + secp256k1_ec_privkey_tweak_add(ctx, natAdd, natTweak).requireSuccess("secp256k1_ec_privkey_tweak_add() failed") return added } } @@ -165,8 +153,8 @@ public object Secp256k1Native : Secp256k1 { require(pubkey.size == 33 || pubkey.size == 65) memScoped { val nPubkey = allocPublicKey(pubkey) - secp256k1_ec_pubkey_negate(ctx, nPubkey.ptr).requireSuccess() - return serializePubkey(nPubkey, pubkey.size) + secp256k1_ec_pubkey_negate(ctx, nPubkey.ptr).requireSuccess("secp256k1_ec_pubkey_negate() failed") + return serializePubkey(nPubkey) } } @@ -175,8 +163,8 @@ public object Secp256k1Native : Secp256k1 { memScoped { val nPubkey = allocPublicKey(pubkey) val nTweak = toNat(tweak) - secp256k1_ec_pubkey_tweak_add(ctx, nPubkey.ptr, nTweak).requireSuccess() - return serializePubkey(nPubkey, pubkey.size) + secp256k1_ec_pubkey_tweak_add(ctx, nPubkey.ptr, nTweak).requireSuccess("secp256k1_ec_pubkey_tweak_add() failed") + return serializePubkey(nPubkey) } } @@ -185,8 +173,8 @@ public object Secp256k1Native : Secp256k1 { memScoped { val nPubkey = allocPublicKey(pubkey) val nTweak = toNat(tweak) - secp256k1_ec_pubkey_tweak_mul(ctx, nPubkey.ptr, nTweak).requireSuccess() - return serializePubkey(nPubkey, pubkey.size) + secp256k1_ec_pubkey_tweak_mul(ctx, nPubkey.ptr, nTweak).requireSuccess("secp256k1_ec_pubkey_tweak_mul() failed") + return serializePubkey(nPubkey) } } @@ -197,42 +185,34 @@ public object Secp256k1Native : Secp256k1 { val nPubkey1 = allocPublicKey(pubkey1) val nPubkey2 = allocPublicKey(pubkey2) val combined = alloc() - secp256k1_ec_pubkey_combine(ctx, combined.ptr, cValuesOf(nPubkey1.ptr, nPubkey2.ptr), 2.convert()).requireSuccess() - return serializePubkey(combined, pubkey1.size) + secp256k1_ec_pubkey_combine(ctx, combined.ptr, cValuesOf(nPubkey1.ptr, nPubkey2.ptr), 2.convert()).requireSuccess("secp256k1_ec_pubkey_combine() failed") + return serializePubkey(combined) } } - public override fun createECDHSecret(seckey: ByteArray, pubkey: ByteArray): ByteArray { + public override fun ecdh(seckey: ByteArray, pubkey: ByteArray): ByteArray { require(seckey.size == 32) require(pubkey.size == 33 || pubkey.size == 65) memScoped { val nPubkey = allocPublicKey(pubkey) val nSeckey = toNat(seckey) val output = allocArray(32) - secp256k1_ecdh(ctx, output, nPubkey.ptr, nSeckey, null, null).requireSuccess() + secp256k1_ecdh(ctx, output, nPubkey.ptr, nSeckey, null, null).requireSuccess("secp256k1_ecdh() failed") return output.readBytes(32) } } - public override fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int, format: PubKeyFormat): ByteArray { + public override fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int): ByteArray { require(sig.size == 64) require(message.size == 32) memScoped { val nSig = toNat(sig) val rSig = alloc() - secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, rSig.ptr, nSig, recid).requireSuccess() + secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, rSig.ptr, nSig, recid).requireSuccess("secp256k1_ecdsa_recoverable_signature_parse_compact() failed") val nMessage = toNat(message) val pubkey = alloc() - secp256k1_ecdsa_recover(ctx, pubkey.ptr, rSig.ptr, nMessage).requireSuccess() - return serializePubkey(pubkey, format.maxSize) - } - } - - public override fun randomize(seed: ByteArray): Boolean { - require(seed.size == 32) - memScoped { - val nSeed = toNat(seed) - return secp256k1_context_randomize(ctx, nSeed) == 1 + secp256k1_ecdsa_recover(ctx, pubkey.ptr, rSig.ptr, nMessage).requireSuccess("secp256k1_ecdsa_recover() failed") + return serializePubkey(pubkey) } } } diff --git a/tests/src/commonTest/kotlin/fr/acinq/secp256k1/Secp256k1Test.kt b/tests/src/commonTest/kotlin/fr/acinq/secp256k1/Secp256k1Test.kt index d8f09d1..034fb0c 100644 --- a/tests/src/commonTest/kotlin/fr/acinq/secp256k1/Secp256k1Test.kt +++ b/tests/src/commonTest/kotlin/fr/acinq/secp256k1/Secp256k1Test.kt @@ -1,9 +1,6 @@ package fr.acinq.secp256k1 -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertTrue +import kotlin.test.* /** @@ -11,67 +8,50 @@ import kotlin.test.assertTrue */ class Secp256k1Test { //TODO improve comments/add more tests - /** - * This tests verify() for a valid signature - */ + @Test fun testVerifyPos() { var result: Boolean val data: ByteArray = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()) //sha256hash of "testing" val sig: ByteArray = Hex.decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase()) val pub: ByteArray = Hex.decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()) - result = Secp256k1.verify(data, sig, pub) + result = Secp256k1.verify(sig, data, pub) assertTrue(result, "testVerifyPos") val sigCompact: ByteArray = Hex.decode("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase()) - result = Secp256k1.verify(data, sigCompact, pub) + result = Secp256k1.verify(sigCompact, data, pub) assertTrue(result, "testVerifyPos") } - /** - * This tests verify() for a non-valid signature - */ @Test fun testVerifyNeg() { var result: Boolean val data: ByteArray = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A91".toLowerCase()) //sha256hash of "testing" val sig: ByteArray = Hex.decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase()) val pub: ByteArray = Hex.decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()) - result = Secp256k1.verify(data, sig, pub) - //System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16)); + result = Secp256k1.verify(sig, data, pub) assertFalse(result, "testVerifyNeg") } - /** - * This tests secret key verify() for a valid secretkey - */ @Test fun testSecKeyVerifyPos() { var result: Boolean val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()) result = Secp256k1.secKeyVerify(sec) - //System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16)); assertTrue(result, "testSecKeyVerifyPos") } - /** - * This tests secret key verify() for an invalid secretkey - */ @Test fun testSecKeyVerifyNeg() { var result: Boolean val sec: ByteArray = Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase()) result = Secp256k1.secKeyVerify(sec) - //System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16)); assertFalse(result, "testSecKeyVerifyNeg") } - /** - * This tests public key create() for a valid secretkey - */ @Test fun testPubKeyCreatePos() { val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()) - val resultArr: ByteArray = Secp256k1.computePubkey(sec, PubKeyFormat.UNCOMPRESSED) + val resultArr: ByteArray = Secp256k1.pubkeyCreate(sec) val pubkeyString: String = Hex.encode(resultArr).toUpperCase() assertEquals( "04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D2103ED494718C697AC9AEBCFD19612E224DB46661011863ED2FC54E71861E2A6", @@ -80,21 +60,25 @@ class Secp256k1Test { ) } - /** - * This tests public key create() for a invalid secretkey - */ @Test fun testPubKeyCreateNeg() { val sec: ByteArray = Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase()) - val resultArr: ByteArray = Secp256k1.computePubkey(sec, PubKeyFormat.UNCOMPRESSED) - val pubkeyString: String = Hex.encode(resultArr).toUpperCase() - assertEquals("", pubkeyString, "testPubKeyCreateNeg") + assertFailsWith { + Secp256k1.pubkeyCreate(sec) + } + } + + @Test + fun testPubkeyCompress() { + val pub = Hex.decode("04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D2103ED494718C697AC9AEBCFD19612E224DB46661011863ED2FC54E71861E2A6") + val compressed = Secp256k1.pubKeyCompress(pub) + assertEquals("02C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D", Hex.encode(compressed).toUpperCase()) } @Test fun testPubKeyNegatePos() { val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()) - val pubkey: ByteArray = Secp256k1.computePubkey(sec, PubKeyFormat.UNCOMPRESSED) + val pubkey: ByteArray = Secp256k1.pubkeyCreate(sec) val pubkeyString: String = Hex.encode(pubkey).toUpperCase() assertEquals( "04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D2103ED494718C697AC9AEBCFD19612E224DB46661011863ED2FC54E71861E2A6", @@ -110,13 +94,10 @@ class Secp256k1Test { ) } - /** - * This tests public key create() for a valid secretkey - */ @Test fun testPubKeyParse() { val pub: ByteArray = Hex.decode("02C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D".toLowerCase()) - val resultArr: ByteArray = Secp256k1.parsePubkey(pub, PubKeyFormat.UNCOMPRESSED) + val resultArr: ByteArray = Secp256k1.pubkeyParse(pub) val pubkeyString: String = Hex.encode(resultArr).toUpperCase() assertEquals( "04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D2103ED494718C697AC9AEBCFD19612E224DB46661011863ED2FC54E71861E2A6", @@ -138,17 +119,14 @@ class Secp256k1Test { ) } - /** - * This tests sign() for a valid secretkey - */ @Test fun testSignPos() { val data: ByteArray = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()) //sha256hash of "testing" val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()) - val resultArr: ByteArray = Secp256k1.sign(data, sec, SigFormat.DER) + val resultArr: ByteArray = Secp256k1.sign(data, sec) val sigString: String = Hex.encode(resultArr).toUpperCase() assertEquals( - "30440220182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A202201C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9", + "182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A21C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9", sigString, "testSignPos" ) @@ -157,7 +135,7 @@ class Secp256k1Test { @Test fun testSignatureNormalize() { val data: ByteArray = Hex.decode("30440220182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A202201C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9".toLowerCase()) - val (resultArr, isHighS) = Secp256k1.signatureNormalize(data, SigFormat.COMPACT) + val (resultArr, isHighS) = Secp256k1.signatureNormalize(data) val sigString: String = Hex.encode(resultArr).toUpperCase() assertEquals( "182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A21C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9", @@ -167,30 +145,26 @@ class Secp256k1Test { assertFalse(isHighS, "isHighS") } - /** - * This tests sign() for a invalid secretkey - */ @Test fun testSignNeg() { val data: ByteArray = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()) //sha256hash of "testing" val sec: ByteArray = Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase()) - val resultArr: ByteArray = Secp256k1.sign(data, sec, SigFormat.DER) - val sigString: String = Hex.encode(resultArr) - assertEquals("", sigString, "testSignNeg") + assertFailsWith { + Secp256k1.sign(data, sec) + } } @Test fun testSignCompactPos() { val data: ByteArray = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()) //sha256hash of "testing" val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()) - val resultArr: ByteArray = Secp256k1.sign(data, sec, SigFormat.COMPACT) + val resultArr: ByteArray = Secp256k1.sign(data, sec) val sigString: String = Hex.encode(resultArr).toUpperCase() assertEquals( "182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A21C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9", sigString, "testSignCompactPos" ) - //assertEquals( sigString, "30 44 02 20 182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A2 02 20 1C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9" , "testSignPos"); } @Test @@ -206,9 +180,6 @@ class Secp256k1Test { assertTrue(sec.contentEquals(sec2)) } - /** - * This tests private key tweak-add - */ @Test fun testPrivKeyTweakAdd_1() { val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()) @@ -222,9 +193,6 @@ class Secp256k1Test { ) } - /** - * This tests private key tweak-mul - */ @Test fun testPrivKeyTweakMul_1() { val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()) @@ -238,9 +206,6 @@ class Secp256k1Test { ) } - /** - * This tests private key tweak-add uncompressed - */ @Test fun testPrivKeyTweakAdd_2() { val pub: ByteArray = Hex.decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()) @@ -254,9 +219,6 @@ class Secp256k1Test { ) } - /** - * This tests private key tweak-mul uncompressed - */ @Test fun testPrivKeyTweakMul_2() { val pub: ByteArray = Hex.decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()) @@ -270,21 +232,11 @@ class Secp256k1Test { ) } - /** - * This tests seed randomization - */ - @Test - fun testRandomize() { - val seed: ByteArray = Hex.decode("A441B15FE9A3CF56661190A0B93B9DEC7D04127288CC87250967CF3B52894D11".toLowerCase()) //sha256hash of "random" - val result: Boolean = Secp256k1.randomize(seed) - assertTrue(result, "testRandomize") - } - @Test fun testCreateECDHSecret() { val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()) val pub: ByteArray = Hex.decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()) - val resultArr: ByteArray = Secp256k1.createECDHSecret(sec, pub) + val resultArr: ByteArray = Secp256k1.ecdh(sec, pub) val ecdhString: String = Hex.encode(resultArr).toUpperCase() assertEquals( "2A2A67007A926E6594AF3EB564FC74005B37A9C8AEF2033C4552051B5C87F043", @@ -297,10 +249,10 @@ class Secp256k1Test { fun testEcdsaRecover() { val data: ByteArray = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()) //sha256hash of "testing" val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()) - val pub: ByteArray = Secp256k1.computePubkey(sec, PubKeyFormat.UNCOMPRESSED) - val sig: ByteArray = Secp256k1.sign(data, sec, SigFormat.COMPACT) - val pub0: ByteArray = Secp256k1.ecdsaRecover(sig, data, 0, PubKeyFormat.UNCOMPRESSED) - val pub1: ByteArray = Secp256k1.ecdsaRecover(sig, data, 1, PubKeyFormat.UNCOMPRESSED) + val pub: ByteArray = Secp256k1.pubkeyCreate(sec) + val sig: ByteArray = Secp256k1.sign(data, sec) + val pub0: ByteArray = Secp256k1.ecdsaRecover(sig, data, 0) + val pub1: ByteArray = Secp256k1.ecdsaRecover(sig, data, 1) assertTrue(pub.contentEquals(pub0) || pub.contentEquals(pub1), "testEcdsaRecover") } }