Compare commits

...

5 Commits

Author SHA1 Message Date
sstone
0364ec762e
Use local secp256k1 callbacks in kotlin native code 2023-12-12 12:08:13 +01:00
sstone
aadffabe42
Set version to 0.12.0-SNAPSHOT 2023-12-11 19:08:54 +01:00
sstone
8e17e7030a
Implement custom secp256k1 error callbacks
These callbacks are only triggered either by arguments do not match explicit rquirements of the sepc256k1 library, or by hardware failures, memory corruption
or bug in secp256k1, and not by misuse of the library.
In theory we do not need to implement them, except to find bugs in our own code, but the default callbacks print a message to stderr and call abort() which
is not nice especially on mobile apps.

=> Here we introduce 2 specific exceptions, Secp256k1ErrorCallbackException and Secp256k1IllegalCallbackException, which are thrown when the error callback or illegal callback are called.
2023-12-11 19:05:46 +01:00
sstone
929e2cda40
Check that the recovery id is valid
It must be 0,1,2 or 3, this is an explicit requirement of the secp256k1 library.
2023-12-11 11:54:07 +01:00
sstone
41eac9273f
Reformat JNI c code (no functional changes) 2023-12-11 10:46:43 +01:00
5 changed files with 855 additions and 605 deletions

View File

@ -22,7 +22,7 @@ buildscript {
allprojects { allprojects {
group = "fr.acinq.secp256k1" group = "fr.acinq.secp256k1"
version = "0.11.0" version = "0.12.0-SNAPSHOT"
repositories { repositories {
google() google()

View File

@ -14,28 +14,82 @@
#define SIG_FORMAT_COMPACT 1 #define SIG_FORMAT_COMPACT 1
#define SIG_FORMAT_DER 2 #define SIG_FORMAT_DER 2
void JNI_ThrowByName(JNIEnv *penv, const char* name, const char* msg) void JNI_ThrowByName(JNIEnv *penv, const char *name, const char *msg)
{ {
jclass cls = (*penv)->FindClass(penv, name); jclass cls = (*penv)->FindClass(penv, name);
if (cls != NULL) { if (cls != NULL)
{
(*penv)->ThrowNew(penv, cls, msg); (*penv)->ThrowNew(penv, cls, msg);
(*penv)->DeleteLocalRef(penv, cls); (*penv)->DeleteLocalRef(penv, cls);
} }
}
/**
* secp256k1 uses callbacks for errors that are either hw pbs or bugs in the calling library, for example
* passing parameters with values that are explicitly defined as illegal in the API, and should never be called for normal operations
* But if they are, default behaviour is to print an error to stderr and abort which is not what we want especially in mobile apps
* => we set up string pointers in every method, and custom callback that will set them to the message passed in by sec256k1's callbacks, which
* we turn into specific Sec256k1 exceptions
*/
#define SETUP_ERROR_CALLBACKS \
char *error_callback_message = NULL; \
char *illegal_callback_message = NULL; \
secp256k1_context_set_error_callback(ctx, my_error_callback_fn, &error_callback_message); \
secp256k1_context_set_illegal_callback(ctx, my_illegal_callback_fn, &illegal_callback_message);
#define CHECKRESULT(errorcheck, message) \
{ \
if (error_callback_message) \
{ \
JNI_ThrowByName(penv, "fr/acinq/secp256k1/Secp256k1ErrorCallbackException", error_callback_message); \
return 0; \
} \
if (illegal_callback_message) \
{ \
JNI_ThrowByName(penv, "fr/acinq/secp256k1/Secp256k1IllegalCallbackException", illegal_callback_message); \
return 0; \
} \
if (errorcheck) \
{ \
JNI_ThrowByName(penv, "fr/acinq/secp256k1/Secp256k1Exception", message); \
return 0; \
} \
} }
#define CHECKRESULT(errorcheck, message) { \ #define CHECKRESULT1(errorcheck, message, dosomething) \
if (errorcheck) { \ { \
if (error_callback_message) \
{ \
dosomething; \
JNI_ThrowByName(penv, "fr/acinq/secp256k1/Secp256k1ErrorCallbackException", error_callback_message); \
return 0; \
} \
if (illegal_callback_message) \
{ \
dosomething; \
JNI_ThrowByName(penv, "fr/acinq/secp256k1/Secp256k1IllegalCallbackException", illegal_callback_message); \
return 0; \
} \
if (errorcheck) \
{ \
JNI_ThrowByName(penv, "fr/acinq/secp256k1/Secp256k1Exception", message); \ JNI_ThrowByName(penv, "fr/acinq/secp256k1/Secp256k1Exception", message); \
return 0; \ return 0; \
} \ } \
}
void my_illegal_callback_fn(const char *str, void *data)
{
if (data != NULL)
{
*(char **)data = str;
}
} }
#define CHECKRESULT1(errorcheck, message, dosomething) { \ void my_error_callback_fn(const char *str, void *data)
if (errorcheck) { \ {
dosomething; \ if (data != NULL)
JNI_ThrowByName(penv, "fr/acinq/secp256k1/Secp256k1Exception", message); \ {
return 0; \ *(char **)data = str;
} \ }
} }
/* /*
@ -43,10 +97,9 @@ void JNI_ThrowByName(JNIEnv *penv, const char* name, const char* msg)
* Method: secp256k1_context_create * Method: secp256k1_context_create
* Signature: (I)J * Signature: (I)J
*/ */
JNIEXPORT jlong JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1context_1create JNIEXPORT jlong JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1context_1create(JNIEnv *penv, jclass clazz, jint flags)
(JNIEnv *penv, jclass clazz, jint flags)
{ {
return (jlong) secp256k1_context_create(flags); return (jlong)secp256k1_context_create(flags);
} }
/* /*
@ -54,11 +107,11 @@ JNIEXPORT jlong JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1c
* Method: secp256k1_context_destroy * Method: secp256k1_context_destroy
* Signature: (J)V * Signature: (J)V
*/ */
JNIEXPORT void JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1context_1destroy JNIEXPORT void JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1context_1destroy(JNIEnv *penv, jclass clazz, jlong ctx)
(JNIEnv *penv, jclass clazz, jlong ctx)
{ {
if (ctx != 0) { if (ctx != 0)
secp256k1_context_destroy((secp256k1_context*)ctx); {
secp256k1_context_destroy((secp256k1_context *)ctx);
} }
} }
@ -67,19 +120,23 @@ JNIEXPORT void JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1co
* Method: secp256k1_ec_seckey_verify * Method: secp256k1_ec_seckey_verify
* Signature: (J[B)I * Signature: (J[B)I
*/ */
JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1seckey_1verify JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1seckey_1verify(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jseckey)
(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jseckey)
{ {
secp256k1_context* ctx = (secp256k1_context *)jctx; secp256k1_context *ctx = (secp256k1_context *)jctx;
jbyte *seckey; jbyte *seckey;
int result = 0; int result = 0;
if (jctx == 0) return 0; if (jctx == 0)
if (jseckey == NULL) return 0; return 0;
if ((*penv)->GetArrayLength(penv, jseckey) != 32) return 0; if (jseckey == NULL)
return 0;
if ((*penv)->GetArrayLength(penv, jseckey) != 32)
return 0;
SETUP_ERROR_CALLBACKS
seckey = (*penv)->GetByteArrayElements(penv, jseckey, 0); seckey = (*penv)->GetByteArrayElements(penv, jseckey, 0);
result = secp256k1_ec_seckey_verify(ctx, (unsigned char*)seckey); result = secp256k1_ec_seckey_verify(ctx, (unsigned char *)seckey);
(*penv)->ReleaseByteArrayElements(penv, jseckey, seckey, 0); (*penv)->ReleaseByteArrayElements(penv, jseckey, seckey, 0);
return result; return result;
} }
@ -89,30 +146,33 @@ JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec
* Method: secp256k1_ec_pubkey_parse * Method: secp256k1_ec_pubkey_parse
* Signature: (J[B)[B * Signature: (J[B)[B
*/ */
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1parse JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1parse(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jpubkey)
(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jpubkey)
{ {
secp256k1_context* ctx = (secp256k1_context *)jctx; secp256k1_context *ctx = (secp256k1_context *)jctx;
jbyte *pubkeyBytes; jbyte *pubkeyBytes;
secp256k1_pubkey pubkey; secp256k1_pubkey pubkey;
size_t size; size_t size;
int result = 0; int result = 0;
if (jctx == 0) return 0; if (jctx == 0)
if (jpubkey == NULL) return 0; return 0;
if (jpubkey == NULL)
return 0;
SETUP_ERROR_CALLBACKS
size = (*penv)->GetArrayLength(penv, jpubkey); size = (*penv)->GetArrayLength(penv, jpubkey);
CHECKRESULT((size != 33) && (size != 65), "invalid public key size"); CHECKRESULT((size != 33) && (size != 65), "invalid public key size");
pubkeyBytes = (*penv)->GetByteArrayElements(penv, jpubkey, 0); pubkeyBytes = (*penv)->GetByteArrayElements(penv, jpubkey, 0);
result = secp256k1_ec_pubkey_parse(ctx, &pubkey, (unsigned char*) pubkeyBytes, size); result = secp256k1_ec_pubkey_parse(ctx, &pubkey, (unsigned char *)pubkeyBytes, size);
(*penv)->ReleaseByteArrayElements(penv, jpubkey, pubkeyBytes, 0); (*penv)->ReleaseByteArrayElements(penv, jpubkey, pubkeyBytes, 0);
CHECKRESULT(!result, "secp256k1_ec_pubkey_parse failed"); CHECKRESULT(!result, "secp256k1_ec_pubkey_parse failed");
size = 65; size = 65;
jpubkey = (*penv)->NewByteArray(penv, 65); jpubkey = (*penv)->NewByteArray(penv, 65);
pubkeyBytes = (*penv)->GetByteArrayElements(penv, jpubkey, 0); pubkeyBytes = (*penv)->GetByteArrayElements(penv, jpubkey, 0);
result = secp256k1_ec_pubkey_serialize(ctx, (unsigned char*) pubkeyBytes, &size, &pubkey, SECP256K1_EC_UNCOMPRESSED); result = secp256k1_ec_pubkey_serialize(ctx, (unsigned char *)pubkeyBytes, &size, &pubkey, SECP256K1_EC_UNCOMPRESSED);
(*penv)->ReleaseByteArrayElements(penv, jpubkey, pubkeyBytes, 0); (*penv)->ReleaseByteArrayElements(penv, jpubkey, pubkeyBytes, 0);
CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed"); CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed");
return jpubkey; return jpubkey;
@ -123,28 +183,31 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
* Method: secp256k1_ec_pubkey_create * Method: secp256k1_ec_pubkey_create
* Signature: (J[B)[B * Signature: (J[B)[B
*/ */
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1create JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1create(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jseckey)
(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jseckey)
{ {
secp256k1_context* ctx = (secp256k1_context *)jctx; secp256k1_context *ctx = (secp256k1_context *)jctx;
jbyte *seckey, *pubkey; jbyte *seckey, *pubkey;
secp256k1_pubkey pub; secp256k1_pubkey pub;
int result = 0; int result = 0;
size_t len; size_t len;
jbyteArray jpubkey = 0; jbyteArray jpubkey = 0;
if (jseckey == NULL) return NULL; if (jseckey == NULL)
if (jctx == 0) return NULL; return NULL;
if (jctx == 0)
return NULL;
SETUP_ERROR_CALLBACKS
CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "secret key must be 32 bytes"); CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "secret key must be 32 bytes");
seckey = (*penv)->GetByteArrayElements(penv, jseckey, 0); seckey = (*penv)->GetByteArrayElements(penv, jseckey, 0);
result = secp256k1_ec_pubkey_create(ctx, &pub, (unsigned char*)seckey); result = secp256k1_ec_pubkey_create(ctx, &pub, (unsigned char *)seckey);
(*penv)->ReleaseByteArrayElements(penv, jseckey, seckey, 0); (*penv)->ReleaseByteArrayElements(penv, jseckey, seckey, 0);
CHECKRESULT(!result, "secp256k1_ec_pubkey_create failed"); CHECKRESULT(!result, "secp256k1_ec_pubkey_create failed");
jpubkey = (*penv)->NewByteArray(penv, 65); jpubkey = (*penv)->NewByteArray(penv, 65);
pubkey = (*penv)->GetByteArrayElements(penv, jpubkey, 0); pubkey = (*penv)->GetByteArrayElements(penv, jpubkey, 0);
len = 65; len = 65;
result = secp256k1_ec_pubkey_serialize(ctx, (unsigned char*)pubkey, &len, &pub, SECP256K1_EC_UNCOMPRESSED); result = secp256k1_ec_pubkey_serialize(ctx, (unsigned char *)pubkey, &len, &pub, SECP256K1_EC_UNCOMPRESSED);
(*penv)->ReleaseByteArrayElements(penv, jpubkey, pubkey, 0); (*penv)->ReleaseByteArrayElements(penv, jpubkey, pubkey, 0);
CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed"); CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed");
return jpubkey; return jpubkey;
@ -155,32 +218,36 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
* Method: secp256k1_ecdsa_sign * Method: secp256k1_ecdsa_sign
* Signature: (J[B[B)[B * Signature: (J[B[B)[B
*/ */
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ecdsa_1sign JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ecdsa_1sign(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jmsg, jbyteArray jseckey)
(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jmsg, jbyteArray jseckey)
{ {
secp256k1_context* ctx = (secp256k1_context *)jctx; secp256k1_context *ctx = (secp256k1_context *)jctx;
jbyte *seckey, *msg, *sig; jbyte *seckey, *msg, *sig;
secp256k1_ecdsa_signature signature; secp256k1_ecdsa_signature signature;
int result = 0; int result = 0;
jbyteArray jsig; jbyteArray jsig;
if (jctx == 0) return NULL; if (jctx == 0)
if (jmsg == NULL) return NULL; return NULL;
if (jseckey == NULL) return NULL; if (jmsg == NULL)
return NULL;
if (jseckey == NULL)
return NULL;
SETUP_ERROR_CALLBACKS
CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "secret key must be 32 bytes"); CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "secret key must be 32 bytes");
CHECKRESULT((*penv)->GetArrayLength(penv, jmsg) != 32, "message key must be 32 bytes"); CHECKRESULT((*penv)->GetArrayLength(penv, jmsg) != 32, "message key must be 32 bytes");
seckey = (*penv)->GetByteArrayElements(penv, jseckey, 0); seckey = (*penv)->GetByteArrayElements(penv, jseckey, 0);
msg = (*penv)->GetByteArrayElements(penv, jmsg, 0); msg = (*penv)->GetByteArrayElements(penv, jmsg, 0);
result = secp256k1_ecdsa_sign(ctx, &signature, (unsigned char*)msg, (unsigned char*)seckey, NULL, NULL); result = secp256k1_ecdsa_sign(ctx, &signature, (unsigned char *)msg, (unsigned char *)seckey, NULL, NULL);
(*penv)->ReleaseByteArrayElements(penv, jseckey, seckey, 0); (*penv)->ReleaseByteArrayElements(penv, jseckey, seckey, 0);
(*penv)->ReleaseByteArrayElements(penv, jmsg, msg, 0); (*penv)->ReleaseByteArrayElements(penv, jmsg, msg, 0);
CHECKRESULT(!result, "secp256k1_ecdsa_sign failed"); CHECKRESULT(!result, "secp256k1_ecdsa_sign failed");
jsig = (*penv)->NewByteArray(penv, 64); jsig = (*penv)->NewByteArray(penv, 64);
sig = (*penv)->GetByteArrayElements(penv, jsig, 0); sig = (*penv)->GetByteArrayElements(penv, jsig, 0);
result = secp256k1_ecdsa_signature_serialize_compact(ctx, (unsigned char*)sig, &signature); result = secp256k1_ecdsa_signature_serialize_compact(ctx, (unsigned char *)sig, &signature);
(*penv)->ReleaseByteArrayElements(penv, jsig, sig, 0); (*penv)->ReleaseByteArrayElements(penv, jsig, sig, 0);
CHECKRESULT(!result, "secp256k1_ecdsa_signature_serialize_compact failed"); CHECKRESULT(!result, "secp256k1_ecdsa_signature_serialize_compact failed");
return jsig; return jsig;
@ -188,8 +255,10 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
int GetSignatureFormat(size_t size) int GetSignatureFormat(size_t size)
{ {
if (size == 64) return SIG_FORMAT_COMPACT; if (size == 64)
if (size < 64) return SIG_FORMAT_UNKNOWN; return SIG_FORMAT_COMPACT;
if (size < 64)
return SIG_FORMAT_UNKNOWN;
return SIG_FORMAT_DER; return SIG_FORMAT_DER;
} }
@ -198,20 +267,25 @@ int GetSignatureFormat(size_t size)
* Method: secp256k1_ecdsa_verify * Method: secp256k1_ecdsa_verify
* Signature: (J[B[B[B)I * Signature: (J[B[B[B)I
*/ */
JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ecdsa_1verify JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ecdsa_1verify(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jsig, jbyteArray jmsg, jbyteArray jpubkey)
(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jsig, jbyteArray jmsg, jbyteArray jpubkey)
{ {
secp256k1_context* ctx = (secp256k1_context *)jctx; secp256k1_context *ctx = (secp256k1_context *)jctx;
jbyte *pub, *msg, *sig; jbyte *pub, *msg, *sig;
secp256k1_ecdsa_signature signature; secp256k1_ecdsa_signature signature;
secp256k1_pubkey pubkey; secp256k1_pubkey pubkey;
size_t sigSize, pubSize; size_t sigSize, pubSize;
int result = 0; int result = 0;
if (jctx == 0) return 0; if (jctx == 0)
if (jsig == NULL) return 0; return 0;
if (jmsg == NULL) return 0; if (jsig == NULL)
if (jpubkey == NULL) return 0; return 0;
if (jmsg == NULL)
return 0;
if (jpubkey == NULL)
return 0;
SETUP_ERROR_CALLBACKS
sigSize = (*penv)->GetArrayLength(penv, jsig); sigSize = (*penv)->GetArrayLength(penv, jsig);
int sigFormat = GetSignatureFormat(sigSize); int sigFormat = GetSignatureFormat(sigSize);
@ -223,26 +297,27 @@ JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec
CHECKRESULT((*penv)->GetArrayLength(penv, jmsg) != 32, "message must be 32 bytes"); CHECKRESULT((*penv)->GetArrayLength(penv, jmsg) != 32, "message must be 32 bytes");
sig = (*penv)->GetByteArrayElements(penv, jsig, 0); sig = (*penv)->GetByteArrayElements(penv, jsig, 0);
switch(sigFormat) { switch (sigFormat)
{
case SIG_FORMAT_COMPACT: case SIG_FORMAT_COMPACT:
result = secp256k1_ecdsa_signature_parse_compact(ctx, &signature, (unsigned char*)sig); result = secp256k1_ecdsa_signature_parse_compact(ctx, &signature, (unsigned char *)sig);
(*penv)->ReleaseByteArrayElements(penv, jsig, sig, 0); (*penv)->ReleaseByteArrayElements(penv, jsig, sig, 0);
CHECKRESULT(!result, "secp256k1_ecdsa_signature_parse_compact failed"); CHECKRESULT(!result, "secp256k1_ecdsa_signature_parse_compact failed");
break; break;
case SIG_FORMAT_DER: case SIG_FORMAT_DER:
result = secp256k1_ecdsa_signature_parse_der(ctx, &signature, (unsigned char*)sig, sigSize); result = secp256k1_ecdsa_signature_parse_der(ctx, &signature, (unsigned char *)sig, sigSize);
(*penv)->ReleaseByteArrayElements(penv, jsig, sig, 0); (*penv)->ReleaseByteArrayElements(penv, jsig, sig, 0);
CHECKRESULT(!result, "secp256k1_ecdsa_signature_parse_der failed"); CHECKRESULT(!result, "secp256k1_ecdsa_signature_parse_der failed");
break; break;
} }
pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0); pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0);
result = secp256k1_ec_pubkey_parse(ctx, &pubkey, (unsigned char*)pub, pubSize); result = secp256k1_ec_pubkey_parse(ctx, &pubkey, (unsigned char *)pub, pubSize);
(*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0); (*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0);
CHECKRESULT(!result, "secp256k1_ec_pubkey_parse failed"); CHECKRESULT(!result, "secp256k1_ec_pubkey_parse failed");
msg = (*penv)->GetByteArrayElements(penv, jmsg, 0); msg = (*penv)->GetByteArrayElements(penv, jmsg, 0);
result = secp256k1_ecdsa_verify(ctx, &signature, (unsigned char*)msg, &pubkey); result = secp256k1_ecdsa_verify(ctx, &signature, (unsigned char *)msg, &pubkey);
(*penv)->ReleaseByteArrayElements(penv, jmsg, msg, 0); (*penv)->ReleaseByteArrayElements(penv, jmsg, msg, 0);
return result; return result;
} }
@ -252,10 +327,9 @@ JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec
* Method: secp256k1_ecdsa_signature_normalize * Method: secp256k1_ecdsa_signature_normalize
* Signature: (J[B[B)I * Signature: (J[B[B)I
*/ */
JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ecdsa_1signature_1normalize JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ecdsa_1signature_1normalize(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jsigin, jbyteArray jsigout)
(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jsigin, jbyteArray jsigout)
{ {
secp256k1_context* ctx = (secp256k1_context *)jctx; secp256k1_context *ctx = (secp256k1_context *)jctx;
jbyte *sig; jbyte *sig;
secp256k1_ecdsa_signature signature_in, signature_out; secp256k1_ecdsa_signature signature_in, signature_out;
size_t size; size_t size;
@ -263,9 +337,14 @@ JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec
int return_value = 0; int return_value = 0;
int sigFormat = SIG_FORMAT_UNKNOWN; int sigFormat = SIG_FORMAT_UNKNOWN;
if (jctx == 0) return 0; if (jctx == 0)
if (jsigin == NULL) return 0; return 0;
if (jsigout == NULL) return 0; if (jsigin == NULL)
return 0;
if (jsigout == NULL)
return 0;
SETUP_ERROR_CALLBACKS
size = (*penv)->GetArrayLength(penv, jsigin); size = (*penv)->GetArrayLength(penv, jsigin);
sigFormat = GetSignatureFormat(size); sigFormat = GetSignatureFormat(size);
@ -273,21 +352,22 @@ JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec
CHECKRESULT((*penv)->GetArrayLength(penv, jsigout) != 64, "output signature length must be 64 bytes"); CHECKRESULT((*penv)->GetArrayLength(penv, jsigout) != 64, "output signature length must be 64 bytes");
sig = (*penv)->GetByteArrayElements(penv, jsigin, 0); sig = (*penv)->GetByteArrayElements(penv, jsigin, 0);
switch(sigFormat) { switch (sigFormat)
{
case SIG_FORMAT_COMPACT: case SIG_FORMAT_COMPACT:
result = secp256k1_ecdsa_signature_parse_compact(ctx, &signature_in, (unsigned char*)sig); result = secp256k1_ecdsa_signature_parse_compact(ctx, &signature_in, (unsigned char *)sig);
(*penv)->ReleaseByteArrayElements(penv, jsigin, sig, 0); (*penv)->ReleaseByteArrayElements(penv, jsigin, sig, 0);
CHECKRESULT(!result, "secp256k1_ecdsa_signature_parse_compact failed"); CHECKRESULT(!result, "secp256k1_ecdsa_signature_parse_compact failed");
break; break;
case SIG_FORMAT_DER: case SIG_FORMAT_DER:
result = secp256k1_ecdsa_signature_parse_der(ctx, &signature_in, (unsigned char*)sig, size); result = secp256k1_ecdsa_signature_parse_der(ctx, &signature_in, (unsigned char *)sig, size);
(*penv)->ReleaseByteArrayElements(penv, jsigin, sig, 0); (*penv)->ReleaseByteArrayElements(penv, jsigin, sig, 0);
CHECKRESULT(!result, "secp256k1_ecdsa_signature_parse_der failed"); CHECKRESULT(!result, "secp256k1_ecdsa_signature_parse_der failed");
break; break;
} }
return_value = secp256k1_ecdsa_signature_normalize(ctx, &signature_out, &signature_in); return_value = secp256k1_ecdsa_signature_normalize(ctx, &signature_out, &signature_in);
sig = (*penv)->GetByteArrayElements(penv, jsigout, 0); sig = (*penv)->GetByteArrayElements(penv, jsigout, 0);
result = secp256k1_ecdsa_signature_serialize_compact(ctx, (unsigned char*)sig, &signature_out); result = secp256k1_ecdsa_signature_serialize_compact(ctx, (unsigned char *)sig, &signature_out);
(*penv)->ReleaseByteArrayElements(penv, jsigout, sig, 0); (*penv)->ReleaseByteArrayElements(penv, jsigout, sig, 0);
CHECKRESULT(!result, "secp256k1_ecdsa_signature_serialize_compact failed"); CHECKRESULT(!result, "secp256k1_ecdsa_signature_serialize_compact failed");
@ -299,18 +379,22 @@ JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec
* Method: secp256k1_ec_privkey_negate * Method: secp256k1_ec_privkey_negate
* Signature: (J[B)[B * Signature: (J[B)[B
*/ */
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1privkey_1negate JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1privkey_1negate(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jseckey)
(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jseckey)
{ {
secp256k1_context* ctx = (secp256k1_context *)jctx; secp256k1_context *ctx = (secp256k1_context *)jctx;
jbyte *seckey; jbyte *seckey;
int result = 0; int result = 0;
if (jctx == 0) return 0; if (jctx == 0)
if (jseckey == NULL) return 0; return 0;
if (jseckey == NULL)
return 0;
SETUP_ERROR_CALLBACKS
CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "secret key must be 32 bytes"); CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "secret key must be 32 bytes");
seckey = (*penv)->GetByteArrayElements(penv, jseckey, 0); seckey = (*penv)->GetByteArrayElements(penv, jseckey, 0);
result = secp256k1_ec_seckey_negate(ctx, (unsigned char*)seckey); result = secp256k1_ec_seckey_negate(ctx, (unsigned char *)seckey);
(*penv)->ReleaseByteArrayElements(penv, jseckey, seckey, 0); (*penv)->ReleaseByteArrayElements(penv, jseckey, seckey, 0);
CHECKRESULT(!result, "secp256k1_ec_seckey_negate failed"); CHECKRESULT(!result, "secp256k1_ec_seckey_negate failed");
return jseckey; return jseckey;
@ -321,22 +405,25 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
* Method: secp256k1_ec_pubkey_negate * Method: secp256k1_ec_pubkey_negate
* Signature: (J[B)[B * Signature: (J[B)[B
*/ */
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1negate JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1negate(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jpubkey)
(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jpubkey)
{ {
secp256k1_context* ctx = (secp256k1_context *)jctx; secp256k1_context *ctx = (secp256k1_context *)jctx;
jbyte *pub; jbyte *pub;
secp256k1_pubkey pubkey; secp256k1_pubkey pubkey;
size_t size; size_t size;
int result = 0; int result = 0;
if (jctx == 0) return 0; if (jctx == 0)
if (jpubkey == NULL) return 0; return 0;
if (jpubkey == NULL)
return 0;
SETUP_ERROR_CALLBACKS
size = (*penv)->GetArrayLength(penv, jpubkey); size = (*penv)->GetArrayLength(penv, jpubkey);
CHECKRESULT((size != 33) && (size != 65), "invalid public key size"); CHECKRESULT((size != 33) && (size != 65), "invalid public key size");
pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0); pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0);
result = secp256k1_ec_pubkey_parse(ctx, &pubkey, (unsigned char*)pub, size); result = secp256k1_ec_pubkey_parse(ctx, &pubkey, (unsigned char *)pub, size);
(*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0); (*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0);
CHECKRESULT(!result, "secp256k1_ec_pubkey_parse failed"); CHECKRESULT(!result, "secp256k1_ec_pubkey_parse failed");
@ -346,7 +433,7 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
size = 65; size = 65;
jpubkey = (*penv)->NewByteArray(penv, 65); jpubkey = (*penv)->NewByteArray(penv, 65);
pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0); pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0);
result = secp256k1_ec_pubkey_serialize(ctx, (unsigned char*)pub, &size, &pubkey, SECP256K1_EC_UNCOMPRESSED); result = secp256k1_ec_pubkey_serialize(ctx, (unsigned char *)pub, &size, &pubkey, SECP256K1_EC_UNCOMPRESSED);
(*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0); (*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0);
CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed"); CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed");
return jpubkey; return jpubkey;
@ -357,22 +444,26 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
* Method: secp256k1_ec_privkey_tweak_add * Method: secp256k1_ec_privkey_tweak_add
* Signature: (J[B[B)[B * Signature: (J[B[B)[B
*/ */
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1privkey_1tweak_1add JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1privkey_1tweak_1add(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jseckey, jbyteArray jtweak)
(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jseckey, jbyteArray jtweak)
{ {
secp256k1_context* ctx = (secp256k1_context *)jctx; secp256k1_context *ctx = (secp256k1_context *)jctx;
jbyte *seckey, *tweak; jbyte *seckey, *tweak;
int result = 0; int result = 0;
if (jctx == 0) return NULL; if (jctx == 0)
if (jseckey == NULL) return NULL; return NULL;
if (jtweak == NULL) return NULL; if (jseckey == NULL)
return NULL;
if (jtweak == NULL)
return NULL;
SETUP_ERROR_CALLBACKS
CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "secret key must be 32 bytes"); CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "secret key must be 32 bytes");
CHECKRESULT((*penv)->GetArrayLength(penv, jtweak) != 32, "tweak must be 32 bytes"); CHECKRESULT((*penv)->GetArrayLength(penv, jtweak) != 32, "tweak must be 32 bytes");
seckey = (*penv)->GetByteArrayElements(penv, jseckey, 0); seckey = (*penv)->GetByteArrayElements(penv, jseckey, 0);
tweak = (*penv)->GetByteArrayElements(penv, jtweak, 0); tweak = (*penv)->GetByteArrayElements(penv, jtweak, 0);
result = secp256k1_ec_seckey_tweak_add(ctx, (unsigned char*)seckey, (unsigned char*)tweak); result = secp256k1_ec_seckey_tweak_add(ctx, (unsigned char *)seckey, (unsigned char *)tweak);
(*penv)->ReleaseByteArrayElements(penv, jseckey, seckey, 0); (*penv)->ReleaseByteArrayElements(penv, jseckey, seckey, 0);
(*penv)->ReleaseByteArrayElements(penv, jtweak, tweak, 0); (*penv)->ReleaseByteArrayElements(penv, jtweak, tweak, 0);
CHECKRESULT(!result, "secp256k1_ec_seckey_tweak_add failed"); CHECKRESULT(!result, "secp256k1_ec_seckey_tweak_add failed");
@ -384,37 +475,41 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
* Method: secp256k1_ec_pubkey_tweak_add * Method: secp256k1_ec_pubkey_tweak_add
* Signature: (J[B[B)[B * Signature: (J[B[B)[B
*/ */
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1tweak_1add JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1tweak_1add(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jpubkey, jbyteArray jtweak)
(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jpubkey, jbyteArray jtweak)
{ {
secp256k1_context* ctx = (secp256k1_context *)jctx; secp256k1_context *ctx = (secp256k1_context *)jctx;
jbyte *pub, *tweak; jbyte *pub, *tweak;
secp256k1_pubkey pubkey; secp256k1_pubkey pubkey;
size_t size; size_t size;
int result = 0; int result = 0;
if (jctx == 0) return NULL; if (jctx == 0)
if (jpubkey == NULL) return NULL; return NULL;
if (jtweak == NULL) return NULL; if (jpubkey == NULL)
return NULL;
if (jtweak == NULL)
return NULL;
SETUP_ERROR_CALLBACKS
size = (*penv)->GetArrayLength(penv, jpubkey); size = (*penv)->GetArrayLength(penv, jpubkey);
CHECKRESULT((size != 33) && (size != 65), "invalid public key size"); CHECKRESULT((size != 33) && (size != 65), "invalid public key size");
CHECKRESULT((*penv)->GetArrayLength(penv, jtweak) != 32, "tweak must be 32 bytes"); CHECKRESULT((*penv)->GetArrayLength(penv, jtweak) != 32, "tweak must be 32 bytes");
pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0); pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0);
result = secp256k1_ec_pubkey_parse(ctx, &pubkey, (unsigned char*)pub, size); result = secp256k1_ec_pubkey_parse(ctx, &pubkey, (unsigned char *)pub, size);
(*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0); (*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0);
CHECKRESULT(!result, "secp256k1_ec_pubkey_parse failed"); CHECKRESULT(!result, "secp256k1_ec_pubkey_parse failed");
tweak = (*penv)->GetByteArrayElements(penv, jtweak, 0); tweak = (*penv)->GetByteArrayElements(penv, jtweak, 0);
result = secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, (unsigned char*)tweak); result = secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, (unsigned char *)tweak);
(*penv)->ReleaseByteArrayElements(penv, jtweak, tweak, 0); (*penv)->ReleaseByteArrayElements(penv, jtweak, tweak, 0);
CHECKRESULT(!result, "secp256k1_ec_pubkey_tweak_add failed"); CHECKRESULT(!result, "secp256k1_ec_pubkey_tweak_add failed");
size = 65; size = 65;
jpubkey = (*penv)->NewByteArray(penv, 65); jpubkey = (*penv)->NewByteArray(penv, 65);
pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0); pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0);
result = secp256k1_ec_pubkey_serialize(ctx, (unsigned char*)pub, &size, &pubkey, SECP256K1_EC_UNCOMPRESSED); result = secp256k1_ec_pubkey_serialize(ctx, (unsigned char *)pub, &size, &pubkey, SECP256K1_EC_UNCOMPRESSED);
(*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0); (*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0);
CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed"); CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed");
return jpubkey; return jpubkey;
@ -425,22 +520,26 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
* Method: secp256k1_ec_privkey_tweak_mul * Method: secp256k1_ec_privkey_tweak_mul
* Signature: (J[B[B)[B * Signature: (J[B[B)[B
*/ */
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1privkey_1tweak_1mul JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1privkey_1tweak_1mul(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jseckey, jbyteArray jtweak)
(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jseckey, jbyteArray jtweak)
{ {
secp256k1_context* ctx = (secp256k1_context *)jctx; secp256k1_context *ctx = (secp256k1_context *)jctx;
jbyte *seckey, *tweak; jbyte *seckey, *tweak;
int result = 0; int result = 0;
if (jctx == 0) return NULL; if (jctx == 0)
if (jseckey == NULL) return NULL; return NULL;
if (jtweak == NULL) return NULL; if (jseckey == NULL)
return NULL;
if (jtweak == NULL)
return NULL;
SETUP_ERROR_CALLBACKS
CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "secret key must be 32 bytes"); CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "secret key must be 32 bytes");
CHECKRESULT((*penv)->GetArrayLength(penv, jtweak) != 32, "tweak must be 32 bytes"); CHECKRESULT((*penv)->GetArrayLength(penv, jtweak) != 32, "tweak must be 32 bytes");
seckey = (*penv)->GetByteArrayElements(penv, jseckey, 0); seckey = (*penv)->GetByteArrayElements(penv, jseckey, 0);
tweak = (*penv)->GetByteArrayElements(penv, jtweak, 0); tweak = (*penv)->GetByteArrayElements(penv, jtweak, 0);
result = secp256k1_ec_seckey_tweak_mul(ctx, (unsigned char*)seckey, (unsigned char*)tweak); result = secp256k1_ec_seckey_tweak_mul(ctx, (unsigned char *)seckey, (unsigned char *)tweak);
CHECKRESULT(!result, "secp256k1_ec_seckey_tweak_mul failed"); CHECKRESULT(!result, "secp256k1_ec_seckey_tweak_mul failed");
(*penv)->ReleaseByteArrayElements(penv, jseckey, seckey, 0); (*penv)->ReleaseByteArrayElements(penv, jseckey, seckey, 0);
(*penv)->ReleaseByteArrayElements(penv, jtweak, tweak, 0); (*penv)->ReleaseByteArrayElements(penv, jtweak, tweak, 0);
@ -452,36 +551,40 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
* Method: secp256k1_ec_pubkey_tweak_mul * Method: secp256k1_ec_pubkey_tweak_mul
* Signature: (J[B[B)[B * Signature: (J[B[B)[B
*/ */
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1tweak_1mul JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1tweak_1mul(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jpubkey, jbyteArray jtweak)
(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jpubkey, jbyteArray jtweak)
{ {
secp256k1_context* ctx = (secp256k1_context *)jctx; secp256k1_context *ctx = (secp256k1_context *)jctx;
jbyte *pub, *tweak; jbyte *pub, *tweak;
secp256k1_pubkey pubkey; secp256k1_pubkey pubkey;
size_t size; size_t size;
int result = 0; int result = 0;
if (jctx == 0) return NULL; if (jctx == 0)
if (jpubkey == NULL) return NULL; return NULL;
if (jtweak == NULL) return NULL; if (jpubkey == NULL)
return NULL;
if (jtweak == NULL)
return NULL;
SETUP_ERROR_CALLBACKS
size = (*penv)->GetArrayLength(penv, jpubkey); size = (*penv)->GetArrayLength(penv, jpubkey);
CHECKRESULT((size != 33) && (size != 65), "invalid public key size"); CHECKRESULT((size != 33) && (size != 65), "invalid public key size");
CHECKRESULT((*penv)->GetArrayLength(penv, jtweak) != 32, "tweak must be 32 bytes"); CHECKRESULT((*penv)->GetArrayLength(penv, jtweak) != 32, "tweak must be 32 bytes");
pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0); pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0);
result = secp256k1_ec_pubkey_parse(ctx, &pubkey, (unsigned char*)pub, size); result = secp256k1_ec_pubkey_parse(ctx, &pubkey, (unsigned char *)pub, size);
(*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0); (*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0);
CHECKRESULT(!result, "secp256k1_ec_pubkey_parse failed"); CHECKRESULT(!result, "secp256k1_ec_pubkey_parse failed");
tweak = (*penv)->GetByteArrayElements(penv, jtweak, 0); tweak = (*penv)->GetByteArrayElements(penv, jtweak, 0);
result = secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, (unsigned char*)tweak); result = secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, (unsigned char *)tweak);
(*penv)->ReleaseByteArrayElements(penv, jtweak, tweak, 0); (*penv)->ReleaseByteArrayElements(penv, jtweak, tweak, 0);
CHECKRESULT(!result, "secp256k1_ec_pubkey_tweak_mul failed"); CHECKRESULT(!result, "secp256k1_ec_pubkey_tweak_mul failed");
size = 65; size = 65;
jpubkey = (*penv)->NewByteArray(penv, 65); jpubkey = (*penv)->NewByteArray(penv, 65);
pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0); pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0);
result = secp256k1_ec_pubkey_serialize(ctx, (unsigned char*)pub, &size, &pubkey, SECP256K1_EC_UNCOMPRESSED); result = secp256k1_ec_pubkey_serialize(ctx, (unsigned char *)pub, &size, &pubkey, SECP256K1_EC_UNCOMPRESSED);
(*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0); (*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0);
CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed"); CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed");
return jpubkey; return jpubkey;
@ -490,8 +593,10 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
void free_pubkeys(secp256k1_pubkey **pubkeys, size_t count) void free_pubkeys(secp256k1_pubkey **pubkeys, size_t count)
{ {
size_t i; size_t i;
for(i = 0; i < count; i++) { for (i = 0; i < count; i++)
if (pubkeys[i] != NULL) free(pubkeys[i]); {
if (pubkeys[i] != NULL)
free(pubkeys[i]);
} }
free(pubkeys); free(pubkeys);
} }
@ -501,10 +606,9 @@ void free_pubkeys(secp256k1_pubkey **pubkeys, size_t count)
* Method: secp256k1_ec_pubkey_combine * Method: secp256k1_ec_pubkey_combine
* Signature: (J[[B)[B * Signature: (J[[B)[B
*/ */
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1combine JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1combine(JNIEnv *penv, jclass clazz, jlong jctx, jobjectArray jpubkeys)
(JNIEnv *penv, jclass clazz, jlong jctx, jobjectArray jpubkeys)
{ {
secp256k1_context* ctx = (secp256k1_context *)jctx; secp256k1_context *ctx = (secp256k1_context *)jctx;
jbyte *pub; jbyte *pub;
secp256k1_pubkey **pubkeys; secp256k1_pubkey **pubkeys;
secp256k1_pubkey combined; secp256k1_pubkey combined;
@ -513,30 +617,35 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
size_t i; size_t i;
int result = 0; int result = 0;
if (jctx == 0) return NULL; if (jctx == 0)
if (jpubkeys == NULL) return NULL; return NULL;
if (jpubkeys == NULL)
return NULL;
SETUP_ERROR_CALLBACKS
count = (*penv)->GetArrayLength(penv, jpubkeys); count = (*penv)->GetArrayLength(penv, jpubkeys);
pubkeys = calloc(count, sizeof(secp256k1_pubkey*)); pubkeys = calloc(count, sizeof(secp256k1_pubkey *));
for(i = 0; i < count; i++) { for (i = 0; i < count; i++)
{
pubkeys[i] = calloc(1, sizeof(secp256k1_pubkey)); pubkeys[i] = calloc(1, sizeof(secp256k1_pubkey));
jpubkey = (jbyteArray) (*penv)->GetObjectArrayElement(penv, jpubkeys, i); jpubkey = (jbyteArray)(*penv)->GetObjectArrayElement(penv, jpubkeys, i);
size = (*penv)->GetArrayLength(penv, jpubkey); size = (*penv)->GetArrayLength(penv, jpubkey);
CHECKRESULT1((size != 33) && (size != 65), "invalid public key size", free_pubkeys(pubkeys, count)); CHECKRESULT1((size != 33) && (size != 65), "invalid public key size", free_pubkeys(pubkeys, count));
pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0); pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0);
result = secp256k1_ec_pubkey_parse(ctx, pubkeys[i], (unsigned char*)pub, size); result = secp256k1_ec_pubkey_parse(ctx, pubkeys[i], (unsigned char *)pub, size);
(*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0); (*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0);
CHECKRESULT1(!result, "secp256k1_ec_pubkey_parse failed", free_pubkeys(pubkeys, count)); CHECKRESULT1(!result, "secp256k1_ec_pubkey_parse failed", free_pubkeys(pubkeys, count));
} }
result = secp256k1_ec_pubkey_combine(ctx, &combined, (const secp256k1_pubkey * const *)pubkeys, count); result = secp256k1_ec_pubkey_combine(ctx, &combined, (const secp256k1_pubkey *const *)pubkeys, count);
free_pubkeys(pubkeys, count); free_pubkeys(pubkeys, count);
CHECKRESULT(!result, "secp256k1_ec_pubkey_combine failed"); CHECKRESULT(!result, "secp256k1_ec_pubkey_combine failed");
size = 65; size = 65;
jpubkey = (*penv)->NewByteArray(penv, 65); jpubkey = (*penv)->NewByteArray(penv, 65);
pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0); pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0);
result = secp256k1_ec_pubkey_serialize(ctx, (unsigned char*)pub, &size, &combined, SECP256K1_EC_UNCOMPRESSED); result = secp256k1_ec_pubkey_serialize(ctx, (unsigned char *)pub, &size, &combined, SECP256K1_EC_UNCOMPRESSED);
(*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0); (*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0);
CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed"); CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed");
return jpubkey; return jpubkey;
@ -547,33 +656,37 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
* Method: secp256k1_ecdh * Method: secp256k1_ecdh
* Signature: (J[B[B)[B * Signature: (J[B[B)[B
*/ */
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ecdh JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ecdh(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jseckey, jbyteArray jpubkey)
(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jseckey, jbyteArray jpubkey)
{ {
secp256k1_context* ctx = (secp256k1_context *)jctx; secp256k1_context *ctx = (secp256k1_context *)jctx;
jbyte* seckeyBytes, *pubkeyBytes, *output; jbyte *seckeyBytes, *pubkeyBytes, *output;
secp256k1_pubkey pubkey; secp256k1_pubkey pubkey;
jbyteArray joutput; jbyteArray joutput;
size_t size; size_t size;
int result; int result;
if (jctx == 0) return NULL; if (jctx == 0)
if (jseckey == NULL) return NULL; return NULL;
if (jpubkey == NULL) return NULL; if (jseckey == NULL)
return NULL;
if (jpubkey == NULL)
return NULL;
SETUP_ERROR_CALLBACKS
CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "invalid private key size"); CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "invalid private key size");
size = (*penv)->GetArrayLength(penv, jpubkey); size = (*penv)->GetArrayLength(penv, jpubkey);
CHECKRESULT((size != 33) && (size != 65), "invalid public key size"); CHECKRESULT((size != 33) && (size != 65), "invalid public key size");
pubkeyBytes = (*penv)->GetByteArrayElements(penv, jpubkey, 0); pubkeyBytes = (*penv)->GetByteArrayElements(penv, jpubkey, 0);
result = secp256k1_ec_pubkey_parse(ctx, &pubkey, (unsigned char*)pubkeyBytes, size); result = secp256k1_ec_pubkey_parse(ctx, &pubkey, (unsigned char *)pubkeyBytes, size);
(*penv)->ReleaseByteArrayElements(penv, jpubkey, pubkeyBytes, 0); (*penv)->ReleaseByteArrayElements(penv, jpubkey, pubkeyBytes, 0);
CHECKRESULT(!result, "secp256k1_ec_pubkey_parse failed"); CHECKRESULT(!result, "secp256k1_ec_pubkey_parse failed");
seckeyBytes = (*penv)->GetByteArrayElements(penv, jseckey, 0); seckeyBytes = (*penv)->GetByteArrayElements(penv, jseckey, 0);
joutput = (*penv)->NewByteArray(penv, 32); joutput = (*penv)->NewByteArray(penv, 32);
output = (*penv)->GetByteArrayElements(penv, joutput, 0); output = (*penv)->GetByteArrayElements(penv, joutput, 0);
result = secp256k1_ecdh(ctx, (unsigned char*)output, &pubkey, (unsigned char*)seckeyBytes, NULL, NULL); result = secp256k1_ecdh(ctx, (unsigned char *)output, &pubkey, (unsigned char *)seckeyBytes, NULL, NULL);
(*penv)->ReleaseByteArrayElements(penv, joutput, output, 0); (*penv)->ReleaseByteArrayElements(penv, joutput, output, 0);
(*penv)->ReleaseByteArrayElements(penv, jseckey, seckeyBytes, 0); (*penv)->ReleaseByteArrayElements(penv, jseckey, seckeyBytes, 0);
return joutput; return joutput;
@ -584,11 +697,10 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
* Method: secp256k1_ecdsa_recover * Method: secp256k1_ecdsa_recover
* Signature: (J[B[BI)[B * Signature: (J[B[BI)[B
*/ */
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ecdsa_1recover JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ecdsa_1recover(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jsig, jbyteArray jmsg, jint recid)
(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jsig, jbyteArray jmsg, jint recid)
{ {
secp256k1_context* ctx = (secp256k1_context *)jctx; secp256k1_context *ctx = (secp256k1_context *)jctx;
jbyte* sig, *msg, *pub; jbyte *sig, *msg, *pub;
jbyteArray jpubkey; jbyteArray jpubkey;
secp256k1_pubkey pubkey; secp256k1_pubkey pubkey;
secp256k1_ecdsa_recoverable_signature signature; secp256k1_ecdsa_recoverable_signature signature;
@ -597,23 +709,32 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
size_t sigSize, size; size_t sigSize, size;
int result; int result;
if (jctx == 0) return NULL; if (jctx == 0)
if (jsig == NULL) return NULL; return NULL;
if (jmsg == NULL) return NULL; if (jsig == NULL)
return NULL;
if (jmsg == NULL)
return NULL;
SETUP_ERROR_CALLBACKS
// we do not check that recid is valid, which should trigger our illegal callback handler to throw a Secp256k1IllegalCallbackException
// CHECKRESULT(recid < 0 || recid > 3, "recid must be 0, 1, 2 or 3")
sigSize = (*penv)->GetArrayLength(penv, jsig); sigSize = (*penv)->GetArrayLength(penv, jsig);
int sigFormat = GetSignatureFormat(sigSize); int sigFormat = GetSignatureFormat(sigSize);
CHECKRESULT(sigFormat == SIG_FORMAT_UNKNOWN, "invalid signature size"); CHECKRESULT(sigFormat == SIG_FORMAT_UNKNOWN, "invalid signature size");
CHECKRESULT((*penv)->GetArrayLength(penv, jmsg) != 32, "message must be 32 bytes"); CHECKRESULT((*penv)->GetArrayLength(penv, jmsg) != 32, "message must be 32 bytes");
sig = (*penv)->GetByteArrayElements(penv, jsig, 0); sig = (*penv)->GetByteArrayElements(penv, jsig, 0);
switch(sigFormat) { switch (sigFormat)
{
case SIG_FORMAT_COMPACT: case SIG_FORMAT_COMPACT:
result = secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &signature, (unsigned char*)sig, recid); result = secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &signature, (unsigned char *)sig, recid);
(*penv)->ReleaseByteArrayElements(penv, jsig, sig, 0); (*penv)->ReleaseByteArrayElements(penv, jsig, sig, 0);
CHECKRESULT(!result, "secp256k1_ecdsa_recoverable_signature_parse_compact failed"); CHECKRESULT(!result, "secp256k1_ecdsa_recoverable_signature_parse_compact failed");
break; break;
case SIG_FORMAT_DER: case SIG_FORMAT_DER:
result = secp256k1_ecdsa_signature_parse_der(ctx, &dummy, (unsigned char*)sig, sigSize); result = secp256k1_ecdsa_signature_parse_der(ctx, &dummy, (unsigned char *)sig, sigSize);
(*penv)->ReleaseByteArrayElements(penv, jsig, sig, 0); (*penv)->ReleaseByteArrayElements(penv, jsig, sig, 0);
CHECKRESULT(!result, "secp256k1_ecdsa_signature_parse_der failed"); CHECKRESULT(!result, "secp256k1_ecdsa_signature_parse_der failed");
result = secp256k1_ecdsa_signature_serialize_compact(ctx, dummyBytes, &dummy); result = secp256k1_ecdsa_signature_serialize_compact(ctx, dummyBytes, &dummy);
@ -623,14 +744,14 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
break; break;
} }
msg = (*penv)->GetByteArrayElements(penv, jmsg, 0); msg = (*penv)->GetByteArrayElements(penv, jmsg, 0);
result = secp256k1_ecdsa_recover(ctx, &pubkey, &signature, (unsigned char*)msg); result = secp256k1_ecdsa_recover(ctx, &pubkey, &signature, (unsigned char *)msg);
(*penv)->ReleaseByteArrayElements(penv, jmsg, msg, 0); (*penv)->ReleaseByteArrayElements(penv, jmsg, msg, 0);
CHECKRESULT(!result, "secp256k1_ecdsa_recover failed"); CHECKRESULT(!result, "secp256k1_ecdsa_recover failed");
size = 65; size = 65;
jpubkey = (*penv)->NewByteArray(penv, 65); jpubkey = (*penv)->NewByteArray(penv, 65);
pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0); pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0);
result = secp256k1_ec_pubkey_serialize(ctx, (unsigned char*)pub, &size, &pubkey, SECP256K1_EC_UNCOMPRESSED); result = secp256k1_ec_pubkey_serialize(ctx, (unsigned char *)pub, &size, &pubkey, SECP256K1_EC_UNCOMPRESSED);
(*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0); (*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0);
CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed"); CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed");
return jpubkey; return jpubkey;
@ -641,23 +762,27 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
* Method: secp256k1_compact_to_der * Method: secp256k1_compact_to_der
* Signature: (J[B)[B * Signature: (J[B)[B
*/ */
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1compact_1to_1der JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1compact_1to_1der(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jsig)
(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jsig)
{ {
secp256k1_context* ctx = (secp256k1_context *)jctx; secp256k1_context *ctx = (secp256k1_context *)jctx;
jbyte *sig; jbyte *sig;
secp256k1_ecdsa_signature signature;; secp256k1_ecdsa_signature signature;
unsigned char der[73]; unsigned char der[73];
size_t size; size_t size;
int result = 0; int result = 0;
if (jctx == 0) return 0; if (jctx == 0)
if (jsig == NULL) return 0; return 0;
if (jsig == NULL)
return 0;
SETUP_ERROR_CALLBACKS
CHECKRESULT((*penv)->GetArrayLength(penv, jsig) != 64, "invalid signature size"); CHECKRESULT((*penv)->GetArrayLength(penv, jsig) != 64, "invalid signature size");
size = (*penv)->GetArrayLength(penv, jsig); size = (*penv)->GetArrayLength(penv, jsig);
sig = (*penv)->GetByteArrayElements(penv, jsig, 0); sig = (*penv)->GetByteArrayElements(penv, jsig, 0);
result = secp256k1_ecdsa_signature_parse_compact(ctx, &signature, (unsigned char*)sig); result = secp256k1_ecdsa_signature_parse_compact(ctx, &signature, (unsigned char *)sig);
(*penv)->ReleaseByteArrayElements(penv, jsig, sig, 0); (*penv)->ReleaseByteArrayElements(penv, jsig, sig, 0);
CHECKRESULT(!result, "secp256k1_ecdsa_signature_parse_compact failed"); CHECKRESULT(!result, "secp256k1_ecdsa_signature_parse_compact failed");
@ -676,23 +801,28 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
* Method: secp256k1_schnorrsig_sign * Method: secp256k1_schnorrsig_sign
* Signature: (J[B[B[B)[B * Signature: (J[B[B[B)[B
*/ */
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1schnorrsig_1sign JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1schnorrsig_1sign(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jmsg, jbyteArray jseckey, jbyteArray jauxrand32)
(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jmsg, jbyteArray jseckey, jbyteArray jauxrand32)
{ {
secp256k1_context* ctx = (secp256k1_context *)jctx; secp256k1_context *ctx = (secp256k1_context *)jctx;
jbyte *seckey, *msg, *sig, *auxrand32 = NULL; jbyte *seckey, *msg, *sig, *auxrand32 = NULL;
secp256k1_keypair keypair; secp256k1_keypair keypair;
unsigned char signature[64]; unsigned char signature[64];
int result = 0; int result = 0;
jbyteArray jsig; jbyteArray jsig;
if (jctx == 0) return NULL; if (jctx == 0)
if (jmsg == NULL) return NULL; return NULL;
if (jseckey == NULL) return NULL; if (jmsg == NULL)
return NULL;
if (jseckey == NULL)
return NULL;
SETUP_ERROR_CALLBACKS
CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "secret key must be 32 bytes"); CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "secret key must be 32 bytes");
CHECKRESULT((*penv)->GetArrayLength(penv, jmsg) != 32, "message must be 32 bytes"); CHECKRESULT((*penv)->GetArrayLength(penv, jmsg) != 32, "message must be 32 bytes");
if (jauxrand32 != 0) { if (jauxrand32 != 0)
{
CHECKRESULT((*penv)->GetArrayLength(penv, jauxrand32) != 32, "auxiliary random data must be 32 bytes"); CHECKRESULT((*penv)->GetArrayLength(penv, jauxrand32) != 32, "auxiliary random data must be 32 bytes");
} }
seckey = (*penv)->GetByteArrayElements(penv, jseckey, 0); seckey = (*penv)->GetByteArrayElements(penv, jseckey, 0);
@ -701,13 +831,15 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
CHECKRESULT(!result, "secp256k1_keypair_create failed"); CHECKRESULT(!result, "secp256k1_keypair_create failed");
msg = (*penv)->GetByteArrayElements(penv, jmsg, 0); msg = (*penv)->GetByteArrayElements(penv, jmsg, 0);
if (jauxrand32 != 0) { if (jauxrand32 != 0)
{
auxrand32 = (*penv)->GetByteArrayElements(penv, jauxrand32, 0); auxrand32 = (*penv)->GetByteArrayElements(penv, jauxrand32, 0);
} }
result = secp256k1_schnorrsig_sign32(ctx, signature, (unsigned char*)msg, &keypair, auxrand32); result = secp256k1_schnorrsig_sign32(ctx, signature, (unsigned char *)msg, &keypair, auxrand32);
(*penv)->ReleaseByteArrayElements(penv, jmsg, msg, 0); (*penv)->ReleaseByteArrayElements(penv, jmsg, msg, 0);
if (auxrand32 != 0) { if (auxrand32 != 0)
{
(*penv)->ReleaseByteArrayElements(penv, jauxrand32, auxrand32, 0); (*penv)->ReleaseByteArrayElements(penv, jauxrand32, auxrand32, 0);
} }
CHECKRESULT(!result, "secp256k1_schnorrsig_sign failed"); CHECKRESULT(!result, "secp256k1_schnorrsig_sign failed");
@ -724,31 +856,36 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
* Method: secp256k1_schnorrsig_verify * Method: secp256k1_schnorrsig_verify
* Signature: (J[B[B[B)I * Signature: (J[B[B[B)I
*/ */
JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1schnorrsig_1verify JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1schnorrsig_1verify(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jsig, jbyteArray jmsg, jbyteArray jpubkey)
(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jsig, jbyteArray jmsg, jbyteArray jpubkey)
{ {
secp256k1_context* ctx = (secp256k1_context *)jctx; secp256k1_context *ctx = (secp256k1_context *)jctx;
jbyte *pub, *msg, *sig; jbyte *pub, *msg, *sig;
secp256k1_xonly_pubkey pubkey; secp256k1_xonly_pubkey pubkey;
int result = 0; int result = 0;
if (jctx == 0) return 0; if (jctx == 0)
if (jsig == NULL) return 0; return 0;
if (jmsg == NULL) return 0; if (jsig == NULL)
if (jpubkey == NULL) return 0; return 0;
if (jmsg == NULL)
return 0;
if (jpubkey == NULL)
return 0;
SETUP_ERROR_CALLBACKS
CHECKRESULT((*penv)->GetArrayLength(penv, jsig) != 64, "signature must be 64 bytes"); CHECKRESULT((*penv)->GetArrayLength(penv, jsig) != 64, "signature must be 64 bytes");
CHECKRESULT((*penv)->GetArrayLength(penv, jpubkey) != 32, "public key must be 32 bytes"); CHECKRESULT((*penv)->GetArrayLength(penv, jpubkey) != 32, "public key must be 32 bytes");
CHECKRESULT((*penv)->GetArrayLength(penv, jmsg) != 32, "message must be 32 bytes"); CHECKRESULT((*penv)->GetArrayLength(penv, jmsg) != 32, "message must be 32 bytes");
pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0); pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0);
result = secp256k1_xonly_pubkey_parse(ctx, &pubkey, (unsigned char*)pub); result = secp256k1_xonly_pubkey_parse(ctx, &pubkey, (unsigned char *)pub);
(*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0); (*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0);
CHECKRESULT(!result, "secp256k1_ec_pubkey_parse failed"); CHECKRESULT(!result, "secp256k1_ec_pubkey_parse failed");
sig = (*penv)->GetByteArrayElements(penv, jsig, 0); sig = (*penv)->GetByteArrayElements(penv, jsig, 0);
msg = (*penv)->GetByteArrayElements(penv, jmsg, 0); msg = (*penv)->GetByteArrayElements(penv, jmsg, 0);
result = secp256k1_schnorrsig_verify(ctx, (unsigned char*)sig, (unsigned char*)msg, 32, &pubkey); result = secp256k1_schnorrsig_verify(ctx, (unsigned char *)sig, (unsigned char *)msg, 32, &pubkey);
(*penv)->ReleaseByteArrayElements(penv, jsig, sig, 0); (*penv)->ReleaseByteArrayElements(penv, jsig, sig, 0);
(*penv)->ReleaseByteArrayElements(penv, jmsg, msg, 0); (*penv)->ReleaseByteArrayElements(penv, jmsg, msg, 0);
return result; return result;

View File

@ -166,7 +166,17 @@ public interface Secp256k1 {
internal expect fun getSecpk256k1(): Secp256k1 internal expect fun getSecpk256k1(): Secp256k1
public class Secp256k1Exception : RuntimeException { public open class Secp256k1Exception : RuntimeException {
public constructor() : super()
public constructor(message: String?) : super(message)
}
public class Secp256k1ErrorCallbackException : Secp256k1Exception {
public constructor() : super()
public constructor(message: String?) : super(message)
}
public class Secp256k1IllegalCallbackException : Secp256k1Exception {
public constructor() : super() public constructor() : super()
public constructor(message: String?) : super(message) public constructor(message: String?) : super(message)
} }

View File

@ -4,15 +4,67 @@ import kotlinx.cinterop.*
import platform.posix.size_tVar import platform.posix.size_tVar
import secp256k1.* import secp256k1.*
@OptIn(ExperimentalUnsignedTypes::class) private typealias Secp256k1CallbackHandler = (String) -> Unit
@OptIn(ExperimentalStdlibApi::class)
private class CallbackHandler(ctx: CPointer<secp256k1_context>) : AutoCloseable {
var illegalCallBackMessage: String? = null
val illegalHandler: Secp256k1CallbackHandler = { x: String -> illegalCallBackMessage = x }
val illegalCallbackRef = StableRef.create(illegalHandler)
var errorCallBackMessage: String? = null
val errorHandler: Secp256k1CallbackHandler = { x: String -> errorCallBackMessage = x }
val errorCallbackRef = StableRef.create(errorHandler)
init {
secp256k1_context_set_error_callback(
ctx, staticCFunction { buffer: CPointer<ByteVar>?, data: COpaquePointer? ->
if (data != null) {
val callback = data.asStableRef<Secp256k1CallbackHandler>().get()
callback(buffer?.toKString() ?: "error callback triggered")
}
},
errorCallbackRef.asCPointer()
)
secp256k1_context_set_illegal_callback(
ctx, staticCFunction { buffer: CPointer<ByteVar>?, data: COpaquePointer? ->
if (data != null) {
val callback = data.asStableRef<Secp256k1CallbackHandler>().get()
callback(buffer?.toKString() ?: "illegal callback triggered")
}
},
illegalCallbackRef.asCPointer()
)
}
fun checkForErrors() {
errorCallBackMessage?.let { throw Secp256k1ErrorCallbackException(it) }
illegalCallBackMessage?.let { throw Secp256k1IllegalCallbackException(it) }
}
override fun close() {
// StableRef instances have to be disposed of manually
illegalCallbackRef.dispose()
errorCallbackRef.dispose()
}
}
@OptIn(ExperimentalUnsignedTypes::class, ExperimentalStdlibApi::class)
public object Secp256k1Native : Secp256k1 { public object Secp256k1Native : Secp256k1 {
private val ctx: CPointer<secp256k1_context> by lazy { private val ctx: CPointer<secp256k1_context> by lazy {
secp256k1_context_create((SECP256K1_FLAGS_TYPE_CONTEXT or SECP256K1_FLAGS_BIT_CONTEXT_SIGN or SECP256K1_FLAGS_BIT_CONTEXT_VERIFY).toUInt()) secp256k1_context_create((SECP256K1_FLAGS_TYPE_CONTEXT or SECP256K1_FLAGS_BIT_CONTEXT_SIGN or SECP256K1_FLAGS_BIT_CONTEXT_VERIFY).toUInt())
?: error("Could not create secp256k1 context") ?: error("Could not create secp256k1 context")
} }
private fun Int.requireSuccess(message: String): Int = if (this != 1) throw Secp256k1Exception(message) else this private fun Int.requireSuccess(message: String): Int {
return if (this != 1) throw Secp256k1Exception(message) else this
}
private fun Int.requireSuccess(callbackHandler: CallbackHandler, message: String): Int {
callbackHandler.checkForErrors()
return if (this != 1) throw Secp256k1Exception(message) else this
}
private fun MemScope.allocSignature(input: ByteArray): secp256k1_ecdsa_signature { private fun MemScope.allocSignature(input: ByteArray): secp256k1_ecdsa_signature {
val sig = alloc<secp256k1_ecdsa_signature>() val sig = alloc<secp256k1_ecdsa_signature>()
@ -58,181 +110,225 @@ public object Secp256k1Native : Secp256k1 {
public override fun verify(signature: ByteArray, message: ByteArray, pubkey: ByteArray): Boolean { public override fun verify(signature: ByteArray, message: ByteArray, pubkey: ByteArray): Boolean {
require(message.size == 32) require(message.size == 32)
require(pubkey.size == 33 || pubkey.size == 65) require(pubkey.size == 33 || pubkey.size == 65)
CallbackHandler(ctx).use { callbackHandler ->
memScoped { memScoped {
val nPubkey = allocPublicKey(pubkey) val nPubkey = allocPublicKey(pubkey)
val nMessage = toNat(message) val nMessage = toNat(message)
val nSig = allocSignature(signature) val nSig = allocSignature(signature)
return secp256k1_ecdsa_verify(ctx, nSig.ptr, nMessage, nPubkey.ptr) == 1 val verify = secp256k1_ecdsa_verify(ctx, nSig.ptr, nMessage, nPubkey.ptr)
callbackHandler.checkForErrors()
return verify == 1
}
} }
} }
public override fun sign(message: ByteArray, privkey: ByteArray): ByteArray { public override fun sign(message: ByteArray, privkey: ByteArray): ByteArray {
require(privkey.size == 32) require(privkey.size == 32)
require(message.size == 32) require(message.size == 32)
CallbackHandler(ctx).use { callbackHandler ->
memScoped { memScoped {
val nPrivkey = toNat(privkey) val nPrivkey = toNat(privkey)
val nMessage = toNat(message) val nMessage = toNat(message)
val nSig = alloc<secp256k1_ecdsa_signature>() val nSig = alloc<secp256k1_ecdsa_signature>()
secp256k1_ecdsa_sign(ctx, nSig.ptr, nMessage, nPrivkey, null, null).requireSuccess("secp256k1_ecdsa_sign() failed") secp256k1_ecdsa_sign(ctx, nSig.ptr, nMessage, nPrivkey, null, null).requireSuccess(callbackHandler, "secp256k1_ecdsa_sign() failed")
return serializeSignature(nSig) return serializeSignature(nSig)
} }
} }
}
public override fun signatureNormalize(sig: ByteArray): Pair<ByteArray, Boolean> { public override fun signatureNormalize(sig: ByteArray): Pair<ByteArray, Boolean> {
require(sig.size >= 64){ "invalid signature ${Hex.encode(sig)}" } require(sig.size >= 64) { "invalid signature ${Hex.encode(sig)}" }
CallbackHandler(ctx).use { callbackHandler ->
memScoped { memScoped {
val nSig = allocSignature(sig) val nSig = allocSignature(sig)
val isHighS = secp256k1_ecdsa_signature_normalize(ctx, nSig.ptr, nSig.ptr) val isHighS = secp256k1_ecdsa_signature_normalize(ctx, nSig.ptr, nSig.ptr)
callbackHandler.checkForErrors()
return Pair(serializeSignature(nSig), isHighS == 1) return Pair(serializeSignature(nSig), isHighS == 1)
} }
} }
}
public override fun secKeyVerify(privkey: ByteArray): Boolean { public override fun secKeyVerify(privkey: ByteArray): Boolean {
if (privkey.size != 32) return false if (privkey.size != 32) return false
CallbackHandler(ctx).use { callbackHandler ->
memScoped { memScoped {
val nPrivkey = toNat(privkey) val nPrivkey = toNat(privkey)
return secp256k1_ec_seckey_verify(ctx, nPrivkey) == 1 val result = secp256k1_ec_seckey_verify(ctx, nPrivkey) == 1
callbackHandler.checkForErrors()
return result
}
} }
} }
public override fun pubkeyCreate(privkey: ByteArray): ByteArray { public override fun pubkeyCreate(privkey: ByteArray): ByteArray {
require(privkey.size == 32) require(privkey.size == 32)
CallbackHandler(ctx).use { callbackHandler ->
memScoped { memScoped {
val nPrivkey = toNat(privkey) val nPrivkey = toNat(privkey)
val nPubkey = alloc<secp256k1_pubkey>() val nPubkey = alloc<secp256k1_pubkey>()
secp256k1_ec_pubkey_create(ctx, nPubkey.ptr, nPrivkey).requireSuccess("secp256k1_ec_pubkey_create() failed") secp256k1_ec_pubkey_create(ctx, nPubkey.ptr, nPrivkey).requireSuccess(callbackHandler, "secp256k1_ec_pubkey_create() failed")
return serializePubkey(nPubkey) return serializePubkey(nPubkey)
} }
} }
}
public override fun pubkeyParse(pubkey: ByteArray): ByteArray { public override fun pubkeyParse(pubkey: ByteArray): ByteArray {
require(pubkey.size == 33 || pubkey.size == 65) require(pubkey.size == 33 || pubkey.size == 65)
CallbackHandler(ctx).use { callbackHandler ->
memScoped { memScoped {
val nPubkey = allocPublicKey(pubkey) val nPubkey = allocPublicKey(pubkey)
return serializePubkey(nPubkey) val result = serializePubkey(nPubkey)
callbackHandler.checkForErrors()
return result
}
} }
} }
public override fun privKeyNegate(privkey: ByteArray): ByteArray { public override fun privKeyNegate(privkey: ByteArray): ByteArray {
require(privkey.size == 32) require(privkey.size == 32)
CallbackHandler(ctx).use { callbackHandler ->
memScoped { memScoped {
val negated = privkey.copyOf() val negated = privkey.copyOf()
val negPriv = toNat(negated) val negPriv = toNat(negated)
secp256k1_ec_seckey_negate(ctx, negPriv).requireSuccess("secp256k1_ec_seckey_negate() failed") secp256k1_ec_seckey_negate(ctx, negPriv).requireSuccess(callbackHandler, "secp256k1_ec_seckey_negate() failed")
return negated return negated
} }
} }
}
public override fun privKeyTweakAdd(privkey: ByteArray, tweak: ByteArray): ByteArray { public override fun privKeyTweakAdd(privkey: ByteArray, tweak: ByteArray): ByteArray {
require(privkey.size == 32) require(privkey.size == 32)
CallbackHandler(ctx).use { callbackHandler ->
memScoped { memScoped {
val added = privkey.copyOf() val added = privkey.copyOf()
val natAdd = toNat(added) val natAdd = toNat(added)
val natTweak = toNat(tweak) val natTweak = toNat(tweak)
secp256k1_ec_seckey_tweak_add(ctx, natAdd, natTweak).requireSuccess("secp256k1_ec_seckey_tweak_add() failed") secp256k1_ec_seckey_tweak_add(ctx, natAdd, natTweak).requireSuccess(callbackHandler, "secp256k1_ec_seckey_tweak_add() failed")
return added return added
} }
} }
}
public override fun privKeyTweakMul(privkey: ByteArray, tweak: ByteArray): ByteArray { public override fun privKeyTweakMul(privkey: ByteArray, tweak: ByteArray): ByteArray {
require(privkey.size == 32) require(privkey.size == 32)
CallbackHandler(ctx).use { callbackHandler ->
memScoped { memScoped {
val multiplied = privkey.copyOf() val multiplied = privkey.copyOf()
val natMul = toNat(multiplied) val natMul = toNat(multiplied)
val natTweak = toNat(tweak) val natTweak = toNat(tweak)
secp256k1_ec_privkey_tweak_mul(ctx, natMul, natTweak).requireSuccess("secp256k1_ec_privkey_tweak_mul() failed") secp256k1_ec_privkey_tweak_mul(ctx, natMul, natTweak).requireSuccess(callbackHandler, "secp256k1_ec_privkey_tweak_mul() failed")
return multiplied return multiplied
} }
} }
}
public override fun pubKeyNegate(pubkey: ByteArray): ByteArray { public override fun pubKeyNegate(pubkey: ByteArray): ByteArray {
require(pubkey.size == 33 || pubkey.size == 65) require(pubkey.size == 33 || pubkey.size == 65)
CallbackHandler(ctx).use { callbackHandler ->
memScoped { memScoped {
val nPubkey = allocPublicKey(pubkey) val nPubkey = allocPublicKey(pubkey)
secp256k1_ec_pubkey_negate(ctx, nPubkey.ptr).requireSuccess("secp256k1_ec_pubkey_negate() failed") secp256k1_ec_pubkey_negate(ctx, nPubkey.ptr).requireSuccess(callbackHandler, "secp256k1_ec_pubkey_negate() failed")
return serializePubkey(nPubkey) return serializePubkey(nPubkey)
} }
} }
}
public override fun pubKeyTweakAdd(pubkey: ByteArray, tweak: ByteArray): ByteArray { public override fun pubKeyTweakAdd(pubkey: ByteArray, tweak: ByteArray): ByteArray {
require(pubkey.size == 33 || pubkey.size == 65) require(pubkey.size == 33 || pubkey.size == 65)
CallbackHandler(ctx).use { callbackHandler ->
memScoped { memScoped {
val nPubkey = allocPublicKey(pubkey) val nPubkey = allocPublicKey(pubkey)
val nTweak = toNat(tweak) val nTweak = toNat(tweak)
secp256k1_ec_pubkey_tweak_add(ctx, nPubkey.ptr, nTweak).requireSuccess("secp256k1_ec_pubkey_tweak_add() failed") secp256k1_ec_pubkey_tweak_add(ctx, nPubkey.ptr, nTweak).requireSuccess(callbackHandler, "secp256k1_ec_pubkey_tweak_add() failed")
return serializePubkey(nPubkey) return serializePubkey(nPubkey)
} }
} }
}
public override fun pubKeyTweakMul(pubkey: ByteArray, tweak: ByteArray): ByteArray { public override fun pubKeyTweakMul(pubkey: ByteArray, tweak: ByteArray): ByteArray {
require(pubkey.size == 33 || pubkey.size == 65) require(pubkey.size == 33 || pubkey.size == 65)
CallbackHandler(ctx).use { callbackHandler ->
memScoped { memScoped {
val nPubkey = allocPublicKey(pubkey) val nPubkey = allocPublicKey(pubkey)
val nTweak = toNat(tweak) val nTweak = toNat(tweak)
secp256k1_ec_pubkey_tweak_mul(ctx, nPubkey.ptr, nTweak).requireSuccess("secp256k1_ec_pubkey_tweak_mul() failed") secp256k1_ec_pubkey_tweak_mul(ctx, nPubkey.ptr, nTweak).requireSuccess(callbackHandler, "secp256k1_ec_pubkey_tweak_mul() failed")
return serializePubkey(nPubkey) return serializePubkey(nPubkey)
} }
} }
}
public override fun pubKeyCombine(pubkeys: Array<ByteArray>): ByteArray { public override fun pubKeyCombine(pubkeys: Array<ByteArray>): ByteArray {
pubkeys.forEach { require(it.size == 33 || it.size == 65) } pubkeys.forEach { require(it.size == 33 || it.size == 65) }
CallbackHandler(ctx).use { callbackHandler ->
memScoped { memScoped {
val nPubkeys = pubkeys.map { allocPublicKey(it).ptr } val nPubkeys = pubkeys.map { allocPublicKey(it).ptr }
val combined = alloc<secp256k1_pubkey>() val combined = alloc<secp256k1_pubkey>()
secp256k1_ec_pubkey_combine(ctx, combined.ptr, nPubkeys.toCValues(), pubkeys.size.convert()).requireSuccess("secp256k1_ec_pubkey_combine() failed") secp256k1_ec_pubkey_combine(ctx, combined.ptr, nPubkeys.toCValues(), pubkeys.size.convert()).requireSuccess(callbackHandler, "secp256k1_ec_pubkey_combine() failed")
return serializePubkey(combined) return serializePubkey(combined)
} }
} }
}
public override fun ecdh(privkey: ByteArray, pubkey: ByteArray): ByteArray { public override fun ecdh(privkey: ByteArray, pubkey: ByteArray): ByteArray {
require(privkey.size == 32) require(privkey.size == 32)
require(pubkey.size == 33 || pubkey.size == 65) require(pubkey.size == 33 || pubkey.size == 65)
CallbackHandler(ctx).use { callbackHandler ->
memScoped { memScoped {
val nPubkey = allocPublicKey(pubkey) val nPubkey = allocPublicKey(pubkey)
val nPrivkey = toNat(privkey) val nPrivkey = toNat(privkey)
val output = allocArray<UByteVar>(32) val output = allocArray<UByteVar>(32)
secp256k1_ecdh(ctx, output, nPubkey.ptr, nPrivkey, null, null).requireSuccess("secp256k1_ecdh() failed") secp256k1_ecdh(ctx, output, nPubkey.ptr, nPrivkey, null, null).requireSuccess(callbackHandler, "secp256k1_ecdh() failed")
return output.readBytes(32) return output.readBytes(32)
} }
} }
}
public override fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int): ByteArray { public override fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int): ByteArray {
require(sig.size == 64) require(sig.size == 64)
require(message.size == 32) require(message.size == 32)
// we do not check that recid is valid, which should trigger our illegal callback handler to throw a Secp256k1IllegalCallbackException
// require(recid in 0..3)
CallbackHandler(ctx).use { callbackHandler ->
memScoped { memScoped {
val nSig = toNat(sig) val nSig = toNat(sig)
val rSig = alloc<secp256k1_ecdsa_recoverable_signature>() val rSig = alloc<secp256k1_ecdsa_recoverable_signature>()
secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, rSig.ptr, nSig, recid).requireSuccess("secp256k1_ecdsa_recoverable_signature_parse_compact() failed") secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, rSig.ptr, nSig, recid).requireSuccess(callbackHandler, "secp256k1_ecdsa_recoverable_signature_parse_compact() failed")
val nMessage = toNat(message) val nMessage = toNat(message)
val pubkey = alloc<secp256k1_pubkey>() val pubkey = alloc<secp256k1_pubkey>()
secp256k1_ecdsa_recover(ctx, pubkey.ptr, rSig.ptr, nMessage).requireSuccess("secp256k1_ecdsa_recover() failed") secp256k1_ecdsa_recover(ctx, pubkey.ptr, rSig.ptr, nMessage).requireSuccess(callbackHandler, "secp256k1_ecdsa_recover() failed")
return serializePubkey(pubkey) return serializePubkey(pubkey)
} }
} }
}
public override fun compact2der(sig: ByteArray): ByteArray { public override fun compact2der(sig: ByteArray): ByteArray {
require(sig.size == 64) require(sig.size == 64)
CallbackHandler(ctx).use { callbackHandler ->
memScoped { memScoped {
val nSig = allocSignature(sig) val nSig = allocSignature(sig)
val natOutput = allocArray<UByteVar>(73) val natOutput = allocArray<UByteVar>(73)
val len = alloc<size_tVar>() val len = alloc<size_tVar>()
len.value = 73.convert() len.value = 73.convert()
secp256k1_ecdsa_signature_serialize_der(ctx, natOutput, len.ptr, nSig.ptr).requireSuccess("secp256k1_ecdsa_signature_serialize_der() failed") secp256k1_ecdsa_signature_serialize_der(ctx, natOutput, len.ptr, nSig.ptr).requireSuccess(callbackHandler, "secp256k1_ecdsa_signature_serialize_der() failed")
return natOutput.readBytes(len.value.toInt()) return natOutput.readBytes(len.value.toInt())
} }
} }
}
override fun verifySchnorr(signature: ByteArray, data: ByteArray, pub: ByteArray): Boolean { override fun verifySchnorr(signature: ByteArray, data: ByteArray, pub: ByteArray): Boolean {
require(signature.size == 64) require(signature.size == 64)
require(data.size == 32) require(data.size == 32)
require(pub.size == 32) require(pub.size == 32)
CallbackHandler(ctx).use { callbackHandler ->
memScoped { memScoped {
val nPub = toNat(pub) val nPub = toNat(pub)
val pubkey = alloc<secp256k1_xonly_pubkey>() val pubkey = alloc<secp256k1_xonly_pubkey>()
secp256k1_xonly_pubkey_parse(ctx, pubkey.ptr, nPub).requireSuccess("secp256k1_xonly_pubkey_parse() failed") secp256k1_xonly_pubkey_parse(ctx, pubkey.ptr, nPub).requireSuccess(callbackHandler, "secp256k1_xonly_pubkey_parse() failed")
val nData = toNat(data) val nData = toNat(data)
val nSig = toNat(signature) val nSig = toNat(signature)
return secp256k1_schnorrsig_verify(ctx, nSig, nData, 32, pubkey.ptr) == 1 return secp256k1_schnorrsig_verify(ctx, nSig, nData, 32u, pubkey.ptr) == 1
}
} }
} }
@ -240,17 +336,19 @@ public object Secp256k1Native : Secp256k1 {
require(sec.size == 32) require(sec.size == 32)
require(data.size == 32) require(data.size == 32)
auxrand32?.let { require(it.size == 32) } auxrand32?.let { require(it.size == 32) }
CallbackHandler(ctx).use { callbackHandler ->
memScoped { memScoped {
val nSec = toNat(sec) val nSec = toNat(sec)
val nData = toNat(data) val nData = toNat(data)
val nAuxrand32 = auxrand32?.let { toNat(it) } val nAuxrand32 = auxrand32?.let { toNat(it) }
val nSig = allocArray<UByteVar>(64) val nSig = allocArray<UByteVar>(64)
val keypair = alloc<secp256k1_keypair>() val keypair = alloc<secp256k1_keypair>()
secp256k1_keypair_create(ctx, keypair.ptr, nSec).requireSuccess("secp256k1_keypair_create() failed") secp256k1_keypair_create(ctx, keypair.ptr, nSec).requireSuccess(callbackHandler, "secp256k1_keypair_create() failed")
secp256k1_schnorrsig_sign32(ctx, nSig, nData, keypair.ptr, nAuxrand32).requireSuccess("secp256k1_ecdsa_sign() failed") secp256k1_schnorrsig_sign32(ctx, nSig, nData, keypair.ptr, nAuxrand32).requireSuccess(callbackHandler, "secp256k1_ecdsa_sign() failed")
return nSig.readBytes(64) return nSig.readBytes(64)
} }
} }
}
public override fun cleanup() { public override fun cleanup() {
secp256k1_context_destroy(ctx) secp256k1_context_destroy(ctx)

View File

@ -275,6 +275,11 @@ class Secp256k1Test {
val pub0 = Secp256k1.ecdsaRecover(sig, message, 0) val pub0 = Secp256k1.ecdsaRecover(sig, message, 0)
val pub1 = Secp256k1.ecdsaRecover(sig, message, 1) val pub1 = Secp256k1.ecdsaRecover(sig, message, 1)
assertTrue(pub.contentEquals(pub0) || pub.contentEquals(pub1)) assertTrue(pub.contentEquals(pub0) || pub.contentEquals(pub1))
// this is a special case, ecdsaRecover explicitly does not check that recid is valid, which triggers our illegal callback handler
assertFailsWith(Secp256k1IllegalCallbackException::class) {
Secp256k1.ecdsaRecover(sig, message, 4)
}
} }
@Test @Test