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
21 changed files with 371 additions and 1250 deletions

View File

@@ -86,7 +86,7 @@ jobs:
- name: Check Linux - name: Check Linux
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'ubuntu-latest'
shell: bash shell: bash
run: ./gradlew linuxX64Test run: ./gradlew linuxTest
- name: Check iOS - name: Check iOS
if: matrix.os == 'macOS-latest' if: matrix.os == 'macOS-latest'
shell: bash shell: bash
@@ -103,7 +103,7 @@ jobs:
- name: Publish Linux - name: Publish Linux
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'ubuntu-latest'
shell: bash shell: bash
run: ./gradlew publishLinuxX64PublicationToMavenLocal :jni:jvm:linux:publishJvmPublicationToMavenLocal run: ./gradlew publishLinuxPublicationToMavenLocal :jni:jvm:linux:publishJvmPublicationToMavenLocal
- name: Publish Windows - name: Publish Windows
if: matrix.os == 'windows-latest' if: matrix.os == 'windows-latest'
shell: msys2 {0} shell: msys2 {0}

View File

@@ -95,7 +95,7 @@ jobs:
- name: Check Linux - name: Check Linux
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'ubuntu-latest'
shell: bash shell: bash
run: ./gradlew linuxX64Test run: ./gradlew linuxTest
- name: Check iOS - name: Check iOS
if: matrix.os == 'macOS-latest' if: matrix.os == 'macOS-latest'
shell: bash shell: bash
@@ -112,7 +112,7 @@ jobs:
- name: Publish Linux - name: Publish Linux
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'ubuntu-latest'
shell: bash shell: bash
run: ./gradlew publishLinuxX64PublicationToMavenLocal :jni:jvm:linux:publishJvmPublicationToMavenLocal -PsnapshotNumber=${{ github.run_number }} -PgitRef=${{ github.ref }} run: ./gradlew publishLinuxPublicationToMavenLocal :jni:jvm:linux:publishJvmPublicationToMavenLocal -PsnapshotNumber=${{ github.run_number }} -PgitRef=${{ github.ref }}
- name: Publish Windows - name: Publish Windows
if: matrix.os == 'windows-latest' if: matrix.os == 'windows-latest'
shell: msys2 {0} shell: msys2 {0}

View File

@@ -101,7 +101,7 @@ jobs:
- name: Check Linux - name: Check Linux
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'ubuntu-latest'
shell: bash shell: bash
run: ./gradlew linuxX64Test run: ./gradlew linuxTest
- name: Check iOS - name: Check iOS
if: matrix.os == 'macOS-latest' if: matrix.os == 'macOS-latest'
shell: bash shell: bash

2
.gitmodules vendored
View File

@@ -1,3 +1,3 @@
[submodule "native/secp256k1"] [submodule "native/secp256k1"]
path = native/secp256k1 path = native/secp256k1
url = https://github.com/jonasnick/secp256k1.git url = https://github.com/bitcoin-core/secp256k1.git

View File

@@ -3,8 +3,8 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.dokka.Platform import org.jetbrains.dokka.Platform
plugins { plugins {
kotlin("multiplatform") version "1.9.21" kotlin("multiplatform") version "1.8.21"
id("org.jetbrains.dokka") version "1.9.10" id("org.jetbrains.dokka") version "1.8.10"
`maven-publish` `maven-publish`
} }
@@ -16,13 +16,13 @@ buildscript {
dependencies { dependencies {
classpath("com.android.tools.build:gradle:7.3.1") classpath("com.android.tools.build:gradle:7.3.1")
classpath("org.jetbrains.dokka:dokka-gradle-plugin:1.9.10") classpath("org.jetbrains.dokka:dokka-gradle-plugin:1.8.10")
} }
} }
allprojects { allprojects {
group = "fr.acinq.secp256k1" group = "fr.acinq.secp256k1"
version = "0.13.0-MUSIG2-SNAPSHOT" version = "0.12.0-SNAPSHOT"
repositories { repositories {
google() google()
@@ -52,22 +52,20 @@ kotlin {
} }
} }
val nativeMain by sourceSets.creating val nativeMain by sourceSets.creating { dependsOn(commonMain) }
linuxX64 { linuxX64("linux") {
secp256k1CInterop("host") secp256k1CInterop("host")
compilations["main"].defaultSourceSet.dependsOn(nativeMain)
// https://youtrack.jetbrains.com/issue/KT-39396
compilations["main"].kotlinOptions.freeCompilerArgs += listOf("-include-binary", "$rootDir/native/build/linux/libsecp256k1.a")
} }
iosX64 { ios {
secp256k1CInterop("ios")
}
iosArm64 {
secp256k1CInterop("ios")
}
iosSimulatorArm64 {
secp256k1CInterop("ios") secp256k1CInterop("ios")
compilations["main"].defaultSourceSet.dependsOn(nativeMain)
// https://youtrack.jetbrains.com/issue/KT-39396
compilations["main"].kotlinOptions.freeCompilerArgs += listOf("-include-binary", "$rootDir/native/build/ios/libsecp256k1.a")
} }
sourceSets.all { sourceSets.all {
@@ -82,9 +80,9 @@ allprojects {
val currentOs = OperatingSystem.current() val currentOs = OperatingSystem.current()
val targets = when { val targets = when {
currentOs.isLinux -> listOf() currentOs.isLinux -> listOf()
currentOs.isMacOsX -> listOf("linuxX64") currentOs.isMacOsX -> listOf("linux")
currentOs.isWindows -> listOf("linuxX64") currentOs.isWindows -> listOf("linux")
else -> listOf("linuxX64") else -> listOf("linux")
}.mapNotNull { kotlin.targets.findByName(it) as? KotlinNativeTarget } }.mapNotNull { kotlin.targets.findByName(it) as? KotlinNativeTarget }
configure(targets) { configure(targets) {

View File

@@ -6,10 +6,11 @@ import fr.acinq.secp256k1.NativeSecp256k1
import java.util.* import java.util.*
public object NativeSecp256k1AndroidLoader { public object NativeSecp256k1AndroidLoader {
@JvmStatic @JvmStatic
@Synchronized @Synchronized
@Throws(Exception::class) @Throws(Exception::class)
public fun load(): Secp256k1 { fun load(): Secp256k1 {
try { try {
System.loadLibrary("secp256k1-jni") System.loadLibrary("secp256k1-jni")
return NativeSecp256k1 return NativeSecp256k1
@@ -26,4 +27,5 @@ public object NativeSecp256k1AndroidLoader {
} }
} }
} }

View File

@@ -7,34 +7,6 @@
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FLAGS_TYPE_CONTEXT
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FLAGS_TYPE_CONTEXT 1L
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FLAGS_TYPE_COMPRESSION
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FLAGS_TYPE_COMPRESSION 2L
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FLAGS_BIT_CONTEXT_VERIFY
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FLAGS_BIT_CONTEXT_VERIFY 256L
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FLAGS_BIT_CONTEXT_SIGN
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FLAGS_BIT_CONTEXT_SIGN 512L
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FLAGS_BIT_COMPRESSION
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FLAGS_BIT_COMPRESSION 256L
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_CONTEXT_VERIFY
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_CONTEXT_VERIFY 257L
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_CONTEXT_SIGN
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_CONTEXT_SIGN 513L
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_CONTEXT_NONE
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_CONTEXT_NONE 1L
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_EC_COMPRESSED
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_EC_COMPRESSED 258L
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_EC_UNCOMPRESSED
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_EC_UNCOMPRESSED 2L
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_PUBLIC_NONCE_SIZE
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_PUBLIC_NONCE_SIZE 66L
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SECRET_NONCE_SIZE
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SECRET_NONCE_SIZE 132L
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_KEYAGG_CACHE_SIZE
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_KEYAGG_CACHE_SIZE 197L
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SESSION_SIZE
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SESSION_SIZE 133L
/* /*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions * Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_context_create * Method: secp256k1_context_create
@@ -195,78 +167,6 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1schnorrsig_1verify JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1schnorrsig_1verify
(JNIEnv *, jclass, jlong, jbyteArray, jbyteArray, jbyteArray); (JNIEnv *, jclass, jlong, jbyteArray, jbyteArray, jbyteArray);
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_nonce_gen
* Signature: (J[B[B[B[B[B[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1nonce_1gen
(JNIEnv *, jclass, jlong, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jbyteArray);
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_nonce_agg
* Signature: (J[[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1nonce_1agg
(JNIEnv *, jclass, jlong, jobjectArray);
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_pubkey_agg
* Signature: (J[[B[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1pubkey_1agg
(JNIEnv *, jclass, jlong, jobjectArray, jbyteArray);
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_pubkey_ec_tweak_add
* Signature: (J[B[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1pubkey_1ec_1tweak_1add
(JNIEnv *, jclass, jlong, jbyteArray, jbyteArray);
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_pubkey_xonly_tweak_add
* Signature: (J[B[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1pubkey_1xonly_1tweak_1add
(JNIEnv *, jclass, jlong, jbyteArray, jbyteArray);
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_nonce_process
* Signature: (J[B[B[B[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1nonce_1process
(JNIEnv *, jclass, jlong, jbyteArray, jbyteArray, jbyteArray, jbyteArray);
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_partial_sign
* Signature: (J[B[B[B[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1partial_1sign
(JNIEnv *, jclass, jlong, jbyteArray, jbyteArray, jbyteArray, jbyteArray);
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_partial_sig_verify
* Signature: (J[B[B[B[B[B)I
*/
JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1partial_1sig_1verify
(JNIEnv *, jclass, jlong, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jbyteArray);
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_partial_sig_agg
* Signature: (J[B[[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1partial_1sig_1agg
(JNIEnv *, jclass, jlong, jbyteArray, jobjectArray);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -8,7 +8,6 @@
#include "include/secp256k1_ecdh.h" #include "include/secp256k1_ecdh.h"
#include "include/secp256k1_recovery.h" #include "include/secp256k1_recovery.h"
#include "include/secp256k1_schnorrsig.h" #include "include/secp256k1_schnorrsig.h"
#include "include/secp256k1_musig.h"
#include "fr_acinq_secp256k1_Secp256k1CFunctions.h" #include "fr_acinq_secp256k1_Secp256k1CFunctions.h"
#define SIG_FORMAT_UNKNOWN 0 #define SIG_FORMAT_UNKNOWN 0
@@ -24,26 +23,75 @@ void JNI_ThrowByName(JNIEnv *penv, const char *name, const char *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) \ #define CHECKRESULT(errorcheck, message) \
{ \ { \
if (errorcheck) \ if (error_callback_message) \
{ \ { \
JNI_ThrowByName(penv, "fr/acinq/secp256k1/Secp256k1Exception", message); \ JNI_ThrowByName(penv, "fr/acinq/secp256k1/Secp256k1ErrorCallbackException", error_callback_message); \
return 0; \ 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 CHECKRESULT1(errorcheck, message, dosomething) \ #define CHECKRESULT1(errorcheck, message, dosomething) \
{ \ { \
if (errorcheck) \ if (error_callback_message) \
{ \ { \
dosomething; \ dosomething; \
JNI_ThrowByName(penv, "fr/acinq/secp256k1/Secp256k1Exception", message); \ JNI_ThrowByName(penv, "fr/acinq/secp256k1/Secp256k1ErrorCallbackException", error_callback_message); \
return 0; \ 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); \
return 0; \
} \
} }
void my_illegal_callback_fn(const char *str, void *data)
{
if (data != NULL)
{
*(char **)data = str;
}
}
void my_error_callback_fn(const char *str, void *data)
{
if (data != NULL)
{
*(char **)data = str;
}
}
/* /*
* Class: fr_acinq_bitcoin_Secp256k1Bindings * Class: fr_acinq_bitcoin_Secp256k1Bindings
* Method: secp256k1_context_create * Method: secp256k1_context_create
@@ -85,6 +133,8 @@ JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec
if ((*penv)->GetArrayLength(penv, jseckey) != 32) if ((*penv)->GetArrayLength(penv, jseckey) != 32)
return 0; 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);
@@ -109,6 +159,8 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
if (jpubkey == NULL) if (jpubkey == NULL)
return 0; 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");
@@ -145,6 +197,8 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
if (jctx == 0) if (jctx == 0)
return 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");
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);
@@ -179,6 +233,8 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
if (jseckey == NULL) if (jseckey == NULL)
return 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);
@@ -229,6 +285,8 @@ JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec
if (jpubkey == NULL) if (jpubkey == NULL)
return 0; return 0;
SETUP_ERROR_CALLBACKS
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");
@@ -286,6 +344,8 @@ JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec
if (jsigout == NULL) if (jsigout == NULL)
return 0; return 0;
SETUP_ERROR_CALLBACKS
size = (*penv)->GetArrayLength(penv, jsigin); size = (*penv)->GetArrayLength(penv, jsigin);
sigFormat = GetSignatureFormat(size); sigFormat = GetSignatureFormat(size);
CHECKRESULT(sigFormat == SIG_FORMAT_UNKNOWN, "invalid signature size"); CHECKRESULT(sigFormat == SIG_FORMAT_UNKNOWN, "invalid signature size");
@@ -329,6 +389,9 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
return 0; return 0;
if (jseckey == NULL) if (jseckey == NULL)
return 0; 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);
@@ -355,6 +418,8 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
if (jpubkey == NULL) if (jpubkey == NULL)
return 0; 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);
@@ -392,6 +457,8 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
if (jtweak == NULL) if (jtweak == NULL)
return 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);
@@ -423,6 +490,8 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
if (jtweak == NULL) if (jtweak == NULL)
return 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");
@@ -464,6 +533,8 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
if (jtweak == NULL) if (jtweak == NULL)
return 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);
@@ -495,6 +566,8 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
if (jtweak == NULL) if (jtweak == NULL)
return 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");
@@ -549,9 +622,10 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
if (jpubkeys == NULL) if (jpubkeys == NULL)
return NULL; return NULL;
count = (*penv)->GetArrayLength(penv, jpubkeys); SETUP_ERROR_CALLBACKS
CHECKRESULT(count < 1, "pubkey array cannot be empty")
pubkeys = calloc(count, sizeof(secp256k1_pubkey *)); count = (*penv)->GetArrayLength(penv, jpubkeys);
pubkeys = calloc(count, sizeof(secp256k1_pubkey *));
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
{ {
@@ -598,6 +672,8 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
if (jpubkey == NULL) if (jpubkey == NULL)
return 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);
@@ -639,7 +715,11 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
return NULL; return NULL;
if (jmsg == NULL) if (jmsg == NULL)
return NULL; return NULL;
CHECKRESULT(recid < 0 || recid > 3, "invalid recovery id");
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);
@@ -687,7 +767,6 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
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;
@@ -696,6 +775,9 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
return 0; return 0;
if (jsig == NULL) if (jsig == NULL)
return 0; 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);
@@ -735,6 +817,8 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
if (jseckey == NULL) if (jseckey == NULL)
return 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)
@@ -788,6 +872,8 @@ JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1sc
if (jpubkey == NULL) if (jpubkey == NULL)
return 0; 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");
@@ -804,537 +890,3 @@ JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1sc
(*penv)->ReleaseByteArrayElements(penv, jmsg, msg, 0); (*penv)->ReleaseByteArrayElements(penv, jmsg, msg, 0);
return result; return result;
} }
static void copy_bytes_from_java(JNIEnv *penv, jbyteArray source, size_t size, unsigned char *dest)
{
jbyte *ptr = NULL;
if (source == NULL)
return; // nothing to do
ptr = (*penv)->GetByteArrayElements(penv, source, 0);
memcpy(dest, ptr, size);
(*penv)->ReleaseByteArrayElements(penv, source, ptr, 0);
}
static void copy_bytes_to_java(JNIEnv *penv, jbyteArray dest, size_t size, unsigned char *source)
{
jbyte *ptr = (*penv)->GetByteArrayElements(penv, dest, 0);
memcpy(ptr, source, size);
(*penv)->ReleaseByteArrayElements(penv, dest, ptr, 0);
}
// session_id32: ByteArray, seckey: ByteArray?, pubkey: ByteArray, msg32: ByteArray?, keyagg_cache: ByteArray?, extra_input32: ByteArray?
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_nonce_gen
* Signature: (J[B[B[B[B[B[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1nonce_1gen(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jsession_id32, jbyteArray jseckey, jbyteArray jpubkey, jbyteArray jmsg32, jbyteArray jkeyaggcache, jbyteArray jextra_input32)
{
secp256k1_context *ctx = (secp256k1_context *)jctx;
int result = 0;
size_t size;
secp256k1_musig_pubnonce pubnonce;
secp256k1_musig_secnonce secnonce;
unsigned char session_id32[32];
jbyte *pubkey_ptr;
secp256k1_pubkey pubkey;
unsigned char seckey[32];
unsigned char msg32[32];
secp256k1_musig_keyagg_cache keyaggcache;
unsigned char extra_input32[32];
jbyteArray jnonce;
jbyte *nonce_ptr = NULL;
unsigned char nonce[fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SECRET_NONCE_SIZE + fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_PUBLIC_NONCE_SIZE];
if (jctx == 0)
return NULL;
if (jsession_id32 == 0)
return NULL;
size = (*penv)->GetArrayLength(penv, jsession_id32);
CHECKRESULT(size != 32, "invalid session_id size");
copy_bytes_from_java(penv, jsession_id32, size, session_id32);
if (jseckey != NULL)
{
size = (*penv)->GetArrayLength(penv, jseckey);
CHECKRESULT(size != 32, "invalid session_id size");
copy_bytes_from_java(penv, jseckey, size, seckey);
}
if (jpubkey == NULL)
return NULL;
size = (*penv)->GetArrayLength(penv, jpubkey);
CHECKRESULT((size != 33) && (size != 65), "invalid public key size");
pubkey_ptr = (*penv)->GetByteArrayElements(penv, jpubkey, 0);
result = secp256k1_ec_pubkey_parse(ctx, &pubkey, (unsigned char *)pubkey_ptr, size);
(*penv)->ReleaseByteArrayElements(penv, jpubkey, pubkey_ptr, 0);
CHECKRESULT(!result, "secp256k1_ec_pubkey_parse failed");
if (jmsg32 != NULL)
{
size = (*penv)->GetArrayLength(penv, jmsg32);
CHECKRESULT(size != 32, "invalid message size");
copy_bytes_from_java(penv, jmsg32, size, msg32);
}
if (jkeyaggcache != NULL)
{
size = (*penv)->GetArrayLength(penv, jkeyaggcache);
CHECKRESULT(size != sizeof(secp256k1_musig_keyagg_cache), "invalid keyagg cache size");
copy_bytes_from_java(penv, jkeyaggcache, size, keyaggcache.data);
}
if (jextra_input32 != NULL)
{
size = (*penv)->GetArrayLength(penv, jextra_input32);
CHECKRESULT(size != 32, "invalid extra input size");
copy_bytes_from_java(penv, jextra_input32, size, extra_input32);
}
result = secp256k1_musig_nonce_gen(ctx, &secnonce, &pubnonce, session_id32,
jseckey == NULL ? NULL : seckey, &pubkey,
jmsg32 == NULL ? NULL : msg32, jkeyaggcache == NULL ? NULL : &keyaggcache, jextra_input32 == NULL ? NULL : extra_input32);
CHECKRESULT(!result, "secp256k1_musig_nonce_gen failed");
memcpy(nonce, secnonce.data, fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SECRET_NONCE_SIZE);
result = secp256k1_musig_pubnonce_serialize(ctx, nonce + fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SECRET_NONCE_SIZE, &pubnonce);
CHECKRESULT(!result, "secp256k1_musig_pubnonce_serialize failed");
jnonce = (*penv)->NewByteArray(penv, sizeof(nonce));
nonce_ptr = (*penv)->GetByteArrayElements(penv, jnonce, 0);
memcpy(nonce_ptr, nonce, sizeof(nonce));
(*penv)->ReleaseByteArrayElements(penv, jnonce, nonce_ptr, 0);
return jnonce;
}
void free_nonces(secp256k1_musig_pubnonce **nonces, size_t count)
{
size_t i;
for (i = 0; i < count; i++)
{
if (nonces[i] != NULL)
free(nonces[i]);
}
free(nonces);
}
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_nonce_agg
* Signature: (J[[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1nonce_1agg(JNIEnv *penv, jclass clazz, jlong jctx, jobjectArray jnonces)
{
secp256k1_context *ctx = (secp256k1_context *)jctx;
jbyte *in66;
secp256k1_musig_pubnonce **pubnonces;
secp256k1_musig_aggnonce combined;
jbyteArray jnonce;
size_t size, count;
size_t i;
int result = 0;
if (jctx == 0)
return NULL;
if (jnonces == NULL)
return NULL;
count = (*penv)->GetArrayLength(penv, jnonces);
CHECKRESULT(count <= 0, "public nonces count cannot be 0");
pubnonces = calloc(count, sizeof(secp256k1_musig_pubnonce *));
for (i = 0; i < count; i++)
{
pubnonces[i] = calloc(1, sizeof(secp256k1_musig_pubnonce));
jnonce = (jbyteArray)(*penv)->GetObjectArrayElement(penv, jnonces, i);
size = (*penv)->GetArrayLength(penv, jnonce);
CHECKRESULT1(size != fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_PUBLIC_NONCE_SIZE, "invalid public nonce size", free_nonces(pubnonces, count));
in66 = (*penv)->GetByteArrayElements(penv, jnonce, 0);
result = secp256k1_musig_pubnonce_parse(ctx, pubnonces[i], (unsigned char *)in66);
(*penv)->ReleaseByteArrayElements(penv, jnonce, in66, 0);
CHECKRESULT1(!result, "secp256k1_musig_pubnonce_parse failed", free_nonces(pubnonces, count));
}
result = secp256k1_musig_nonce_agg(ctx, &combined, (const secp256k1_musig_pubnonce *const *)pubnonces, count);
free_nonces(pubnonces, count);
CHECKRESULT(!result, "secp256k1_musig_nonce_agg failed");
jnonce = (*penv)->NewByteArray(penv, fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_PUBLIC_NONCE_SIZE);
in66 = (*penv)->GetByteArrayElements(penv, jnonce, 0);
result = secp256k1_musig_aggnonce_serialize(ctx, (unsigned char *)in66, &combined);
(*penv)->ReleaseByteArrayElements(penv, jnonce, in66, 0);
CHECKRESULT(!result, "secp256k1_musig_aggnonce_serialize failed");
return jnonce;
}
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_pubkey_agg
* Signature: (J[[B[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1pubkey_1agg(JNIEnv *penv, jclass clazz, jlong jctx, jobjectArray jpubkeys, jbyteArray jkeyaggcache)
{
secp256k1_context *ctx = (secp256k1_context *)jctx;
jbyte *pub;
secp256k1_pubkey **pubkeys;
secp256k1_xonly_pubkey combined;
secp256k1_musig_keyagg_cache keyaggcache;
jbyteArray jpubkey;
size_t size, count;
size_t i;
int result = 0;
if (jctx == 0)
return NULL;
if (jpubkeys == NULL)
return NULL;
CHECKRESULT((*penv)->GetArrayLength(penv, jpubkeys) <= 0, "pubkeys count cannot be 0");
if (jkeyaggcache != NULL)
{
size = (*penv)->GetArrayLength(penv, jkeyaggcache);
CHECKRESULT(size != sizeof(secp256k1_musig_keyagg_cache), "invalid keyagg cache size");
copy_bytes_from_java(penv, jkeyaggcache, size, keyaggcache.data);
}
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_musig_pubkey_agg(ctx, &combined, jkeyaggcache == NULL ? NULL : &keyaggcache, (const secp256k1_pubkey *const *)pubkeys, count);
free_pubkeys(pubkeys, count);
CHECKRESULT(!result, "secp256k1_musig_pubkey_agg failed");
size = 32;
jpubkey = (*penv)->NewByteArray(penv, 32);
pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0);
result = secp256k1_xonly_pubkey_serialize(ctx, (unsigned char *)pub, &combined);
(*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0);
CHECKRESULT(!result, "secp256k1_xonly_pubkey_serialize failed");
if (jkeyaggcache != NULL)
{
pub = (*penv)->GetByteArrayElements(penv, jkeyaggcache, 0);
memcpy(pub, keyaggcache.data, sizeof(secp256k1_musig_keyagg_cache));
(*penv)->ReleaseByteArrayElements(penv, jkeyaggcache, pub, 0);
}
return jpubkey;
}
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_pubkey_ec_tweak_add
* Signature: (J[B[B[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1pubkey_1ec_1tweak_1add(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jkeyaggcache, jbyteArray jtweak32)
{
secp256k1_context *ctx = (secp256k1_context *)jctx;
jbyte *tweak32, *pub;
secp256k1_pubkey pubkey;
secp256k1_musig_keyagg_cache keyaggcache;
jbyteArray jpubkey;
size_t size;
int result = 0;
if (jctx == 0)
return NULL;
if (jkeyaggcache == NULL)
return NULL;
size = (*penv)->GetArrayLength(penv, jkeyaggcache);
CHECKRESULT(size != sizeof(secp256k1_musig_keyagg_cache), "invalid keyagg cache size");
copy_bytes_from_java(penv, jkeyaggcache, size, keyaggcache.data);
if (jtweak32 == NULL)
return NULL;
CHECKRESULT((*penv)->GetArrayLength(penv, jtweak32) != 32, "tweak must be 32 bytes");
tweak32 = (*penv)->GetByteArrayElements(penv, jtweak32, 0);
result = secp256k1_musig_pubkey_ec_tweak_add(ctx, &pubkey, &keyaggcache, tweak32);
(*penv)->ReleaseByteArrayElements(penv, jtweak32, tweak32, 0);
CHECKRESULT(!result, "secp256k1_musig_pubkey_ec_tweak_add failed");
jpubkey = (*penv)->NewByteArray(penv, 65);
pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0);
size = 65;
result = secp256k1_ec_pubkey_serialize(ctx, pub, &size, &pubkey, SECP256K1_EC_UNCOMPRESSED);
(*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0);
CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed");
pub = (*penv)->GetByteArrayElements(penv, jkeyaggcache, 0);
memcpy(pub, keyaggcache.data, sizeof(secp256k1_musig_keyagg_cache));
(*penv)->ReleaseByteArrayElements(penv, jkeyaggcache, pub, 0);
return jpubkey;
}
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_pubkey_xonly_tweak_add
* Signature: (J[B[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1pubkey_1xonly_1tweak_1add(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jkeyaggcache, jbyteArray jtweak32)
{
secp256k1_context *ctx = (secp256k1_context *)jctx;
jbyte *tweak32, *pub;
secp256k1_pubkey pubkey;
secp256k1_musig_keyagg_cache keyaggcache;
jbyteArray jpubkey;
size_t size;
int result = 0;
if (jctx == 0)
return NULL;
if (jkeyaggcache == NULL)
return NULL;
size = (*penv)->GetArrayLength(penv, jkeyaggcache);
CHECKRESULT(size != sizeof(secp256k1_musig_keyagg_cache), "invalid keyagg cache size");
copy_bytes_from_java(penv, jkeyaggcache, size, keyaggcache.data);
if (jtweak32 == NULL)
return NULL;
CHECKRESULT((*penv)->GetArrayLength(penv, jtweak32) != 32, "tweak must be 32 bytes");
tweak32 = (*penv)->GetByteArrayElements(penv, jtweak32, 0);
result = secp256k1_musig_pubkey_xonly_tweak_add(ctx, &pubkey, &keyaggcache, tweak32);
(*penv)->ReleaseByteArrayElements(penv, jtweak32, tweak32, 0);
CHECKRESULT(!result, "secp256k1_musig_pubkey_xonly_tweak_add failed");
jpubkey = (*penv)->NewByteArray(penv, 65);
pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0);
size = 65;
result = secp256k1_ec_pubkey_serialize(ctx, pub, &size, &pubkey, SECP256K1_EC_UNCOMPRESSED);
(*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0);
CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed");
pub = (*penv)->GetByteArrayElements(penv, jkeyaggcache, 0);
memcpy(pub, keyaggcache.data, sizeof(secp256k1_musig_keyagg_cache));
(*penv)->ReleaseByteArrayElements(penv, jkeyaggcache, pub, 0);
return jpubkey;
}
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_nonce_process
* Signature: (J[B[B[B[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1nonce_1process(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jaggnonce, jbyteArray jmsg32, jbyteArray jkeyaggcache, jbyteArray jadaptor)
{
secp256k1_context *ctx = (secp256k1_context *)jctx;
secp256k1_musig_keyagg_cache keyaggcache;
secp256k1_musig_aggnonce aggnonce;
secp256k1_musig_session session;
unsigned char msg32[32];
jbyteArray jsession;
jbyte *ptr;
size_t size;
int result = 0;
if (jctx == 0)
return NULL;
if (jaggnonce == NULL)
return NULL;
CHECKRESULT((*penv)->GetArrayLength(penv, jaggnonce) != fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_PUBLIC_NONCE_SIZE, "invalid nonce size");
if (jmsg32 == NULL)
return NULL;
CHECKRESULT((*penv)->GetArrayLength(penv, jmsg32) != 32, "invalid message size");
if (jkeyaggcache == NULL)
return NULL;
CHECKRESULT((*penv)->GetArrayLength(penv, jkeyaggcache) != fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_KEYAGG_CACHE_SIZE, "invalid nonce size");
ptr = (*penv)->GetByteArrayElements(penv, jaggnonce, 0);
result = secp256k1_musig_aggnonce_parse(ctx, &aggnonce, ptr);
(*penv)->ReleaseByteArrayElements(penv, jaggnonce, ptr, 0);
CHECKRESULT(!result, "secp256k1_musig_aggnonce_parse failed");
copy_bytes_from_java(penv, jmsg32, 32, msg32);
copy_bytes_from_java(penv, jkeyaggcache, fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_KEYAGG_CACHE_SIZE, keyaggcache.data);
result = secp256k1_musig_nonce_process(ctx, &session, &aggnonce, msg32, &keyaggcache);
CHECKRESULT(!result, "secp256k1_musig_nonce_process failed");
jsession = (*penv)->NewByteArray(penv, fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SESSION_SIZE);
copy_bytes_to_java(penv, jsession, fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SESSION_SIZE, session.data);
return jsession;
}
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_partial_sign
* Signature: (J[B[B[B[B[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1partial_1sign(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jsecnonce, jbyteArray jprivkey, jbyteArray jkeyaggcache, jbyteArray jsession)
{
secp256k1_context *ctx = (secp256k1_context *)jctx;
secp256k1_musig_partial_sig psig;
secp256k1_musig_secnonce secnonce;
unsigned char seckey[32];
secp256k1_keypair keypair;
secp256k1_musig_keyagg_cache keyaggcache;
secp256k1_musig_session session;
jbyteArray jpsig;
jbyte *ptr;
int result = 0;
if (jctx == 0)
return NULL;
if (jsecnonce == NULL)
return NULL;
CHECKRESULT((*penv)->GetArrayLength(penv, jsecnonce) != fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SECRET_NONCE_SIZE, "invalid secret nonce size");
if (jprivkey == NULL)
return NULL;
CHECKRESULT((*penv)->GetArrayLength(penv, jprivkey) != 32, "invalid private key size");
if (jkeyaggcache == NULL)
return NULL;
CHECKRESULT((*penv)->GetArrayLength(penv, jkeyaggcache) != fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_KEYAGG_CACHE_SIZE, "invalid cache size");
if (jsession == NULL)
return NULL;
CHECKRESULT((*penv)->GetArrayLength(penv, jsession) != fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SESSION_SIZE, "invalid session size");
copy_bytes_from_java(penv, jsecnonce, fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SECRET_NONCE_SIZE, secnonce.data);
copy_bytes_from_java(penv, jprivkey, 32, seckey);
result = secp256k1_keypair_create(ctx, &keypair, seckey);
CHECKRESULT(!result, "secp256k1_keypair_create failed");
copy_bytes_from_java(penv, jkeyaggcache, fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_KEYAGG_CACHE_SIZE, keyaggcache.data);
copy_bytes_from_java(penv, jsession, fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SESSION_SIZE, session.data);
result = secp256k1_musig_partial_sign(ctx, &psig, &secnonce, &keypair, &keyaggcache, &session);
CHECKRESULT(!result, "secp256k1_musig_partial_sign failed");
result = secp256k1_musig_partial_sig_serialize(ctx, seckey, &psig);
CHECKRESULT(!result, "secp256k1_musig_partial_sig_serialize failed");
jpsig = (*penv)->NewByteArray(penv, 32);
copy_bytes_to_java(penv, jpsig, 32, seckey);
return jpsig;
}
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_partial_sig_verify
* Signature: (J[B[B[B[B[B)I
*/
JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1partial_1sig_1verify(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jpsig, jbyteArray jpubnonce, jbyteArray jpubkey, jbyteArray jkeyaggcache, jbyteArray jsession)
{
secp256k1_context *ctx = (secp256k1_context *)jctx;
secp256k1_musig_partial_sig psig;
secp256k1_musig_pubnonce pubnonce;
secp256k1_pubkey pubkey;
secp256k1_musig_keyagg_cache keyaggcache;
secp256k1_musig_session session;
jbyte *ptr;
int result = 0;
if (jctx == 0)
return 0;
if (jpsig == NULL)
return 0;
CHECKRESULT((*penv)->GetArrayLength(penv, jpsig) != 32, "invalid partial signature size");
if (jpubnonce == NULL)
return 0;
CHECKRESULT((*penv)->GetArrayLength(penv, jpubnonce) != fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_PUBLIC_NONCE_SIZE, "invalid public nonce size");
if (jpubkey == NULL)
return 0;
CHECKRESULT(((*penv)->GetArrayLength(penv, jpubkey) != 33) && ((*penv)->GetArrayLength(penv, jpubkey) != 65), "invalid public key size");
if (jkeyaggcache == NULL)
return 0;
CHECKRESULT((*penv)->GetArrayLength(penv, jkeyaggcache) != fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_KEYAGG_CACHE_SIZE, "invalid cache size");
if (jsession == NULL)
return 0;
CHECKRESULT((*penv)->GetArrayLength(penv, jsession) != fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SESSION_SIZE, "invalid session size");
ptr = (*penv)->GetByteArrayElements(penv, jpsig, 0);
result = secp256k1_musig_partial_sig_parse(ctx, &psig, ptr);
(*penv)->ReleaseByteArrayElements(penv, jpsig, ptr, 0);
CHECKRESULT(!result, "secp256k1_musig_partial_sig_parse failed");
ptr = (*penv)->GetByteArrayElements(penv, jpubnonce, 0);
result = secp256k1_musig_pubnonce_parse(ctx, &pubnonce, ptr);
(*penv)->ReleaseByteArrayElements(penv, jpubnonce, ptr, 0);
CHECKRESULT(!result, "secp256k1_musig_pubnonce_parse failed");
ptr = (*penv)->GetByteArrayElements(penv, jpubkey, 0);
result = secp256k1_ec_pubkey_parse(ctx, &pubkey, ptr, (*penv)->GetArrayLength(penv, jpubkey));
(*penv)->ReleaseByteArrayElements(penv, jpubkey, ptr, 0);
CHECKRESULT(!result, "secp256k1_musig_pubkey_parse failed");
copy_bytes_from_java(penv, jkeyaggcache, fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_KEYAGG_CACHE_SIZE, keyaggcache.data);
copy_bytes_from_java(penv, jsession, fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SESSION_SIZE, session.data);
result = secp256k1_musig_partial_sig_verify(ctx, &psig, &pubnonce, &pubkey, &keyaggcache, &session);
return result;
}
void free_partial_sigs(secp256k1_musig_partial_sig **psigs, size_t count)
{
size_t i;
for (i = 0; i < count; i++)
{
if (psigs[i] != NULL)
free(psigs[i]);
}
free(psigs);
}
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_partial_sig_agg
* Signature: (J[B[[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1partial_1sig_1agg(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jsession, jobjectArray jpsigs)
{
secp256k1_context *ctx = (secp256k1_context *)jctx;
secp256k1_musig_session session;
secp256k1_musig_partial_sig **psigs;
unsigned char sig64[64];
secp256k1_musig_keyagg_cache keyaggcache;
jbyteArray jpsig;
jbyte *ptr;
size_t size, count;
size_t i;
int result = 0;
if (jctx == 0)
return NULL;
if (jsession == NULL)
return NULL;
CHECKRESULT((*penv)->GetArrayLength(penv, jsession) != fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SESSION_SIZE, "invalid session size");
copy_bytes_from_java(penv, jsession, fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SESSION_SIZE, session.data);
if (jpsigs == NULL)
return NULL;
count = (*penv)->GetArrayLength(penv, jpsigs);
CHECKRESULT(count <= 0, "partial sigs count cannot be 0");
psigs = calloc(count, sizeof(secp256k1_musig_partial_sig *));
for (i = 0; i < count; i++)
{
psigs[i] = calloc(1, sizeof(secp256k1_musig_partial_sig));
jpsig = (jbyteArray)(*penv)->GetObjectArrayElement(penv, jpsigs, i);
size = (*penv)->GetArrayLength(penv, jpsig);
CHECKRESULT1(size != 32, "invalid partial signature size", free_partial_sigs(psigs, count));
ptr = (*penv)->GetByteArrayElements(penv, jpsig, 0);
result = secp256k1_musig_partial_sig_parse(ctx, psigs[i], (unsigned char *)ptr);
(*penv)->ReleaseByteArrayElements(penv, jpsig, ptr, 0);
CHECKRESULT1(!result, "secp256k1_musig_partial_sig_parse failed", free_partial_sigs(psigs, count));
}
result = secp256k1_musig_partial_sig_agg(ctx, sig64, &session, (const secp256k1_musig_partial_sig *const *)psigs, count);
free_partial_sigs(psigs, count);
CHECKRESULT(!result, "secp256k1_musig_pubkey_agg failed");
jpsig = (*penv)->NewByteArray(penv, 64);
copy_bytes_to_java(penv, jpsig, 64, sig64);
return jpsig;
}

View File

@@ -29,14 +29,6 @@ public class Secp256k1CFunctions {
public static final int SECP256K1_EC_COMPRESSED = (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION); public static final int SECP256K1_EC_COMPRESSED = (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION);
public static final int SECP256K1_EC_UNCOMPRESSED = (SECP256K1_FLAGS_TYPE_COMPRESSION); public static final int SECP256K1_EC_UNCOMPRESSED = (SECP256K1_FLAGS_TYPE_COMPRESSION);
public static final int SECP256K1_MUSIG_PUBLIC_NONCE_SIZE = 66;
public static final int SECP256K1_MUSIG_SECRET_NONCE_SIZE = 132;
public static final int SECP256K1_MUSIG_KEYAGG_CACHE_SIZE = 197;
public static final int SECP256K1_MUSIG_SESSION_SIZE = 133;
public static native long secp256k1_context_create(int flags); public static native long secp256k1_context_create(int flags);
public static native void secp256k1_context_destroy(long ctx); public static native void secp256k1_context_destroy(long ctx);
@@ -76,22 +68,4 @@ public class Secp256k1CFunctions {
public static native byte[] secp256k1_schnorrsig_sign(long ctx, byte[] msg, byte[] seckey, byte[] aux_rand32); public static native byte[] secp256k1_schnorrsig_sign(long ctx, byte[] msg, byte[] seckey, byte[] aux_rand32);
public static native int secp256k1_schnorrsig_verify(long ctx, byte[] sig, byte[] msg, byte[] pubkey); public static native int secp256k1_schnorrsig_verify(long ctx, byte[] sig, byte[] msg, byte[] pubkey);
public static native byte[] secp256k1_musig_nonce_gen(long ctx, byte[] session_id32, byte[] seckey, byte[] pubkey, byte[] msg32, byte[] keyagg_cache, byte[] extra_input32);
public static native byte[] secp256k1_musig_nonce_agg(long ctx, byte[][] nonces);
public static native byte[] secp256k1_musig_pubkey_agg(long ctx, byte[][] pubkeys, byte[] keyagg_cache);
public static native byte[] secp256k1_musig_pubkey_ec_tweak_add(long ctx, byte[] keyagg_cache, byte[] tweak32);
public static native byte[] secp256k1_musig_pubkey_xonly_tweak_add(long ctx, byte[] keyagg_cache, byte[] tweak32);
public static native byte[] secp256k1_musig_nonce_process(long ctx, byte[] aggnonce, byte[] msg32, byte[] keyagg_cache, byte[] adaptor);
public static native byte[] secp256k1_musig_partial_sign(long ctx, byte[] secnonce, byte[] privkey, byte[] keyagg_cache, byte[] session);
public static native int secp256k1_musig_partial_sig_verify(long ctx, byte[] psig, byte[] pubnonce, byte[] pubkey, byte[] keyagg_cache, byte[] session);
public static native byte[] secp256k1_musig_partial_sig_agg(long ctx, byte[] session, byte[][] psigs);
} }

View File

@@ -92,42 +92,6 @@ public object NativeSecp256k1 : Secp256k1 {
return Secp256k1CFunctions.secp256k1_schnorrsig_sign(Secp256k1Context.getContext(), data, sec, auxrand32) return Secp256k1CFunctions.secp256k1_schnorrsig_sign(Secp256k1Context.getContext(), data, sec, auxrand32)
} }
override fun musigNonceGen(session_id32: ByteArray, seckey: ByteArray?, pubkey: ByteArray, msg32: ByteArray?, keyagg_cache: ByteArray?, extra_input32: ByteArray?): ByteArray {
return Secp256k1CFunctions.secp256k1_musig_nonce_gen(Secp256k1Context.getContext(), session_id32, seckey, pubkey, msg32, keyagg_cache, extra_input32)
}
override fun musigNonceAgg(pubnonces: Array<ByteArray>): ByteArray {
return Secp256k1CFunctions.secp256k1_musig_nonce_agg(Secp256k1Context.getContext(), pubnonces)
}
override fun musigPubkeyAdd(pubkeys: Array<ByteArray>, keyagg_cache: ByteArray?): ByteArray {
return Secp256k1CFunctions.secp256k1_musig_pubkey_agg(Secp256k1Context.getContext(), pubkeys, keyagg_cache)
}
override fun musigPubkeyTweakAdd(keyagg_cache: ByteArray, tweak32: ByteArray): ByteArray {
return Secp256k1CFunctions.secp256k1_musig_pubkey_ec_tweak_add(Secp256k1Context.getContext(), keyagg_cache, tweak32)
}
override fun musigPubkeyXonlyTweakAdd(keyagg_cache: ByteArray, tweak32: ByteArray): ByteArray {
return Secp256k1CFunctions.secp256k1_musig_pubkey_xonly_tweak_add(Secp256k1Context.getContext(), keyagg_cache, tweak32)
}
override fun musigNonceProcess(aggnonce: ByteArray, msg32: ByteArray, keyagg_cache: ByteArray, adaptor: ByteArray?): ByteArray {
return Secp256k1CFunctions.secp256k1_musig_nonce_process(Secp256k1Context.getContext(), aggnonce, msg32, keyagg_cache, adaptor)
}
override fun musigPartialSign(secnonce: ByteArray, privkey: ByteArray, keyagg_cache: ByteArray, session: ByteArray): ByteArray {
return Secp256k1CFunctions.secp256k1_musig_partial_sign(Secp256k1Context.getContext(), secnonce, privkey, keyagg_cache, session)
}
override fun musigPartialSigVerify(psig: ByteArray, pubnonce: ByteArray, pubkey: ByteArray, keyagg_cache: ByteArray, session: ByteArray): Int {
return Secp256k1CFunctions.secp256k1_musig_partial_sig_verify(Secp256k1Context.getContext(), psig, pubnonce, pubkey, keyagg_cache, session)
}
override fun musigPartialSigAgg(session: ByteArray, psigs: Array<ByteArray>): ByteArray {
return Secp256k1CFunctions.secp256k1_musig_partial_sig_agg(Secp256k1Context.getContext(), session, psigs)
}
override fun cleanup() { override fun cleanup() {
return Secp256k1CFunctions.secp256k1_context_destroy(Secp256k1Context.getContext()) return Secp256k1CFunctions.secp256k1_context_destroy(Secp256k1Context.getContext())
} }

View File

@@ -33,7 +33,7 @@ export STRIP=$ANDROID_NDK/toolchains/llvm/prebuilt/$TOOLCHAIN/bin/llvm-strip
cd secp256k1 cd secp256k1
./autogen.sh ./autogen.sh
./configure CFLAGS=-fpic --host=$TARGET --enable-experimental --enable-module_ecdh --enable-module-recovery --enable-module-schnorrsig --enable-module-musig --enable-benchmark=no --enable-shared=no --enable-exhaustive-tests=no --enable-tests=no ./configure CFLAGS=-fpic --host=$TARGET --enable-experimental --enable-module_ecdh --enable-module-recovery --enable-module-schnorrsig --enable-benchmark=no --enable-shared=no --enable-exhaustive-tests=no --enable-tests=no
make clean make clean
make make

View File

@@ -6,13 +6,10 @@ cp xconfigure.sh secp256k1
cd secp256k1 cd secp256k1
./autogen.sh ./autogen.sh
sh xconfigure.sh --enable-experimental --enable-module_ecdh --enable-module-recovery --enable-module-schnorrsig --enable-module-musig --enable-benchmark=no --enable-shared=no --enable-exhaustive-tests=no --enable-tests=no sh xconfigure.sh --enable-experimental --enable-module_ecdh --enable-module-recovery --enable-module-schnorrsig --enable-benchmark=no --enable-shared=no --enable-exhaustive-tests=no --enable-tests=no
mkdir -p ../build/ios mkdir -p ../build/ios
cp -v _build/universal/ios/* ../build/ios/ cp -v _build/universal/* ../build/ios/
mkdir -p ../build/iosSimulatorArm64
cp -v _build/universal/iosSimulatorArm64/* ../build/iosSimulatorArm64/
rm -rf _build rm -rf _build
make clean make clean

View File

@@ -23,7 +23,7 @@ else
fi fi
./autogen.sh ./autogen.sh
./configure $CONF_OPTS --enable-experimental --enable-module_ecdh --enable-module-recovery --enable-module-schnorrsig --enable-module-musig --enable-benchmark=no --enable-shared=no --enable-exhaustive-tests=no --enable-tests=no ./configure $CONF_OPTS --enable-experimental --enable-module_ecdh --enable-module-recovery --enable-module-schnorrsig --enable-benchmark=no --enable-shared=no --enable-exhaustive-tests=no --enable-tests=no
make clean make clean
make make

View File

@@ -69,22 +69,9 @@ HOST_FLAGS="${ARCH_FLAGS} -mios-simulator-version-min=${MIN_IOS_VERSION} -isysro
CHOST="x86_64-apple-darwin" CHOST="x86_64-apple-darwin"
Build "$@" Build "$@"
## Build for iphone M1/M2/Mx simulators
SDK="iphonesimulator"
PLATFORM="arm64-sim"
PLATFORM_SIM_ARM=${PLATFORM}
ARCH_FLAGS="-arch arm64"
HOST_FLAGS="${ARCH_FLAGS} -mios-simulator-version-min=${MIN_IOS_VERSION} -isysroot $(xcrun --sdk ${SDK} --show-sdk-path)"
CHOST="arm-apple-darwin"
Build "$@"
# Create universal binary # Create universal binary
cd "${PLATFORMS}/${PLATFORM_ARM}/lib" cd "${PLATFORMS}/${PLATFORM_ARM}/lib"
LIB_NAME=`find . -iname *.a` LIB_NAME=`find . -iname *.a`
cd - cd -
mkdir -p "${UNIVERSAL}/ios" &> /dev/null mkdir -p "${UNIVERSAL}" &> /dev/null
mkdir -p "${UNIVERSAL}/iosSimulatorArm64" &> /dev/null lipo -create -output "${UNIVERSAL}/${LIB_NAME}" "${PLATFORMS}/${PLATFORM_ARM}/lib/${LIB_NAME}" "${PLATFORMS}/${PLATFORM_ISIM}/lib/${LIB_NAME}"
lipo -create -output "${UNIVERSAL}/ios/${LIB_NAME}" "${PLATFORMS}/${PLATFORM_ARM}/lib/${LIB_NAME}" "${PLATFORMS}/${PLATFORM_ISIM}/lib/${LIB_NAME}"
# create a specific library for arm64 simulator: it cannot be included in the lib above which already contains an arm64 lib
lipo -create -output "${UNIVERSAL}/iosSimulatorArm64/${LIB_NAME}" "${PLATFORMS}/${PLATFORM_SIM_ARM}/lib/${LIB_NAME}"

View File

@@ -21,9 +21,9 @@ mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/conte
-Djavadoc=$ARTIFACT_ID_BASE-$VERSION-javadoc.jar -Djavadoc=$ARTIFACT_ID_BASE-$VERSION-javadoc.jar
popd popd
pushd . pushd .
for i in iosarm64 iossimulatorarm64 iosx64 jni-android jni-common jni-jvm-darwin jni-jvm-extract jni-jvm-linux jni-jvm-mingw jni-jvm jvm linuxx64; do for i in iosarm64 iosx64 jni-android jni-common jni-jvm-darwin jni-jvm-extract jni-jvm-linux jni-jvm-mingw jni-jvm jvm linux; do
cd fr/acinq/secp256k1/secp256k1-kmp-$i/$VERSION cd fr/acinq/secp256k1/secp256k1-kmp-$i/$VERSION
if [ $i == iosarm64 ] || [ $i == iossimulatorarm64 ] || [ $i == iosx64 ]; then if [ $i == iosarm64 ] || [ $i == iosx64 ]; then
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \ mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \ -DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.klib \ -Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.klib \
@@ -32,7 +32,7 @@ for i in iosarm64 iossimulatorarm64 iosx64 jni-android jni-common jni-jvm-darwin
-Dclassifiers=metadata,,cinterop-libsecp256k1 \ -Dclassifiers=metadata,,cinterop-libsecp256k1 \
-Dsources=$ARTIFACT_ID_BASE-$i-$VERSION-sources.jar \ -Dsources=$ARTIFACT_ID_BASE-$i-$VERSION-sources.jar \
-Djavadoc=$ARTIFACT_ID_BASE-$i-$VERSION-javadoc.jar -Djavadoc=$ARTIFACT_ID_BASE-$i-$VERSION-javadoc.jar
elif [ $i == linuxx64 ]; then elif [ $i == linux ]; then
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \ mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \ -DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.klib \ -Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.klib \

View File

@@ -153,25 +153,6 @@ public interface Secp256k1 {
} }
} }
public fun musigNonceGen(session_id32: ByteArray, seckey: ByteArray?, pubkey: ByteArray, msg32: ByteArray?, keyagg_cache: ByteArray?, extra_input32: ByteArray?): ByteArray
public fun musigNonceAgg(pubnonces: Array<ByteArray>): ByteArray
public fun musigPubkeyAdd(pubkeys: Array<ByteArray>, keyagg_cache: ByteArray?): ByteArray
public fun musigPubkeyTweakAdd(keyagg_cache: ByteArray, tweak32: ByteArray): ByteArray
public fun musigPubkeyXonlyTweakAdd(keyagg_cache: ByteArray, tweak32: ByteArray): ByteArray
public fun musigNonceProcess(aggnonce: ByteArray, msg32: ByteArray, keyagg_cache: ByteArray, adaptor: ByteArray?): ByteArray
public fun musigPartialSign(secnonce: ByteArray, privkey: ByteArray, keyagg_cache: ByteArray, session: ByteArray): ByteArray
public fun musigPartialSigVerify(psig: ByteArray, pubnonce: ByteArray, pubkey: ByteArray, keyagg_cache: ByteArray, session: ByteArray): Int
public fun musigPartialSigAgg(session: ByteArray, psigs: Array<ByteArray>): ByteArray
/** /**
* Delete the secp256k1 context from dynamic memory. * Delete the secp256k1 context from dynamic memory.
*/ */
@@ -180,19 +161,22 @@ public interface Secp256k1 {
public companion object : Secp256k1 by getSecpk256k1() { public companion object : Secp256k1 by getSecpk256k1() {
@JvmStatic @JvmStatic
public fun get(): Secp256k1 = this public fun get(): Secp256k1 = this
// @formatter:off
public const val MUSIG2_SECRET_NONCE_SIZE: Int = 132
public const val MUSIG2_PUBLIC_NONCE_SIZE: Int = 66
public const val MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE: Int = 197
public const val MUSIG2_PUBLIC_SESSION_SIZE: Int = 133
// @formatter:on
} }
} }
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

@@ -1,14 +1,10 @@
package = secp256k1 package = secp256k1
headers = secp256k1.h secp256k1_ecdh.h secp256k1_recovery.h secp256k1_extrakeys.h secp256k1_schnorrsig.h secp256k1_musig.h headers = secp256k1.h secp256k1_ecdh.h secp256k1_recovery.h secp256k1_extrakeys.h secp256k1_schnorrsig.h
headerFilter = secp256k1/** secp256k1_ecdh.h secp256k1_recovery.h secp256k1_extrakeys.h secp256k1_schnorrsig.h secp256k1_musig.h secp256k1.h headerFilter = secp256k1/** secp256k1_ecdh.h secp256k1_recovery.h secp256k1_extrakeys.h secp256k1_schnorrsig.h secp256k1.h
staticLibraries.linux = libsecp256k1.a libraryPaths.linux = c/secp256k1/build/linux/
libraryPaths.linux = c/secp256k1/build/linux/ native/build/linux/ native/build/darwin/
linkerOpts.linux = -L/usr/lib64 -L/usr/lib/x86_64-linux-gnu -L/usr/local/lib linkerOpts.linux = -L/usr/lib64 -L/usr/lib/x86_64-linux-gnu -L/usr/local/lib
staticLibraries.ios = libsecp256k1.a libraryPaths.ios = c/secp256k1/build/ios/ /usr/local/lib
libraryPaths.ios_x64 = c/secp256k1/build/ios/ /usr/local/lib native/build/ios/
libraryPaths.ios_arm64 = c/secp256k1/build/ios/ /usr/local/lib native/build/ios/
libraryPaths.ios_simulator_arm64 = c/secp256k1/build/ios/ /usr/local/lib native/build/iosSimulatorArm64/
linkerOpts.ios = -framework Security -framework Foundation linkerOpts.ios = -framework Security -framework Foundation

View File

@@ -1,19 +1,70 @@
package fr.acinq.secp256k1 package fr.acinq.secp256k1
import kotlinx.cinterop.* import kotlinx.cinterop.*
import platform.posix.memcpy
import platform.posix.size_tVar import platform.posix.size_tVar
import secp256k1.* import secp256k1.*
@OptIn(ExperimentalUnsignedTypes::class, ExperimentalForeignApi::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>()
@@ -41,20 +92,6 @@ public object Secp256k1Native : Secp256k1 {
return pub return pub
} }
private fun MemScope.allocPublicNonce(pubnonce: ByteArray): secp256k1_musig_pubnonce {
val nat = toNat(pubnonce)
val nPubnonce = alloc<secp256k1_musig_pubnonce>()
secp256k1_musig_pubnonce_parse(ctx, nPubnonce.ptr, nat).requireSuccess("secp256k1_musig_pubnonce_parse() failed")
return nPubnonce
}
private fun MemScope.allocPartialSig(psig: ByteArray): secp256k1_musig_partial_sig {
val nat = toNat(psig)
val nPsig = alloc<secp256k1_musig_partial_sig>()
secp256k1_musig_partial_sig_parse(ctx, nPsig.ptr, nat).requireSuccess("secp256k1_musig_partial_sig_parse() failed")
return nPsig
}
private fun MemScope.serializePubkey(pubkey: secp256k1_pubkey): ByteArray { private fun MemScope.serializePubkey(pubkey: secp256k1_pubkey): ByteArray {
val serialized = allocArray<UByteVar>(65) val serialized = allocArray<UByteVar>(65)
val outputLen = alloc<size_tVar>() val outputLen = alloc<size_tVar>()
@@ -63,24 +100,6 @@ public object Secp256k1Native : Secp256k1 {
return serialized.readBytes(outputLen.value.convert()) return serialized.readBytes(outputLen.value.convert())
} }
private fun MemScope.serializeXonlyPubkey(pubkey: secp256k1_xonly_pubkey): ByteArray {
val serialized = allocArray<UByteVar>(32)
secp256k1_xonly_pubkey_serialize(ctx, serialized, pubkey.ptr).requireSuccess("secp256k1_xonly_pubkey_serialize() failed")
return serialized.readBytes(32)
}
private fun MemScope.serializePubnonce(pubnonce: secp256k1_musig_pubnonce): ByteArray {
val serialized = allocArray<UByteVar>(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
secp256k1_musig_pubnonce_serialize(ctx, serialized, pubnonce.ptr).requireSuccess("secp256k1_musig_pubnonce_serialize() failed")
return serialized.readBytes(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
}
private fun MemScope.serializeAggnonce(aggnonce: secp256k1_musig_aggnonce): ByteArray {
val serialized = allocArray<UByteVar>(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
secp256k1_musig_aggnonce_serialize(ctx, serialized, aggnonce.ptr).requireSuccess("secp256k1_musig_aggnonce_serialize() failed")
return serialized.readBytes(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
}
private fun DeferScope.toNat(bytes: ByteArray): CPointer<UByteVar> { private fun DeferScope.toNat(bytes: ByteArray): CPointer<UByteVar> {
val ubytes = bytes.asUByteArray() val ubytes = bytes.asUByteArray()
val pinned = ubytes.pin() val pinned = ubytes.pin()
@@ -91,173 +110,209 @@ 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)
memScoped { CallbackHandler(ctx).use { callbackHandler ->
val nPubkey = allocPublicKey(pubkey) memScoped {
val nMessage = toNat(message) val nPubkey = allocPublicKey(pubkey)
val nSig = allocSignature(signature) val nMessage = toNat(message)
return secp256k1_ecdsa_verify(ctx, nSig.ptr, nMessage, nPubkey.ptr) == 1 val nSig = allocSignature(signature)
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)
memScoped { CallbackHandler(ctx).use { callbackHandler ->
val nPrivkey = toNat(privkey) memScoped {
val nMessage = toNat(message) val nPrivkey = toNat(privkey)
val nSig = alloc<secp256k1_ecdsa_signature>() val nMessage = toNat(message)
secp256k1_ecdsa_sign(ctx, nSig.ptr, nMessage, nPrivkey, null, null).requireSuccess("secp256k1_ecdsa_sign() failed") val nSig = alloc<secp256k1_ecdsa_signature>()
return serializeSignature(nSig) secp256k1_ecdsa_sign(ctx, nSig.ptr, nMessage, nPrivkey, null, null).requireSuccess(callbackHandler, "secp256k1_ecdsa_sign() failed")
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)}" }
memScoped { CallbackHandler(ctx).use { callbackHandler ->
val nSig = allocSignature(sig) memScoped {
val isHighS = secp256k1_ecdsa_signature_normalize(ctx, nSig.ptr, nSig.ptr) val nSig = allocSignature(sig)
return Pair(serializeSignature(nSig), isHighS == 1) val isHighS = secp256k1_ecdsa_signature_normalize(ctx, nSig.ptr, nSig.ptr)
callbackHandler.checkForErrors()
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
memScoped { CallbackHandler(ctx).use { callbackHandler ->
val nPrivkey = toNat(privkey) memScoped {
return secp256k1_ec_seckey_verify(ctx, nPrivkey) == 1 val nPrivkey = toNat(privkey)
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)
memScoped { CallbackHandler(ctx).use { callbackHandler ->
val nPrivkey = toNat(privkey) memScoped {
val nPubkey = alloc<secp256k1_pubkey>() val nPrivkey = toNat(privkey)
secp256k1_ec_pubkey_create(ctx, nPubkey.ptr, nPrivkey).requireSuccess("secp256k1_ec_pubkey_create() failed") val nPubkey = alloc<secp256k1_pubkey>()
return serializePubkey(nPubkey) secp256k1_ec_pubkey_create(ctx, nPubkey.ptr, nPrivkey).requireSuccess(callbackHandler, "secp256k1_ec_pubkey_create() failed")
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)
memScoped { CallbackHandler(ctx).use { callbackHandler ->
val nPubkey = allocPublicKey(pubkey) memScoped {
return serializePubkey(nPubkey) val nPubkey = allocPublicKey(pubkey)
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)
memScoped { CallbackHandler(ctx).use { callbackHandler ->
val negated = privkey.copyOf() memScoped {
val negPriv = toNat(negated) val negated = privkey.copyOf()
secp256k1_ec_seckey_negate(ctx, negPriv).requireSuccess("secp256k1_ec_seckey_negate() failed") val negPriv = toNat(negated)
return negated secp256k1_ec_seckey_negate(ctx, negPriv).requireSuccess(callbackHandler, "secp256k1_ec_seckey_negate() failed")
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)
require(tweak.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)
require(tweak.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)
memScoped { CallbackHandler(ctx).use { callbackHandler ->
val nPubkey = allocPublicKey(pubkey) memScoped {
secp256k1_ec_pubkey_negate(ctx, nPubkey.ptr).requireSuccess("secp256k1_ec_pubkey_negate() failed") val nPubkey = allocPublicKey(pubkey)
return serializePubkey(nPubkey) secp256k1_ec_pubkey_negate(ctx, nPubkey.ptr).requireSuccess(callbackHandler, "secp256k1_ec_pubkey_negate() failed")
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)
require(tweak.size == 32) 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)
require(tweak.size == 32) 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 {
require(pubkeys.isNotEmpty())
pubkeys.forEach { require(it.size == 33 || it.size == 65) } pubkeys.forEach { require(it.size == 33 || it.size == 65) }
memScoped { CallbackHandler(ctx).use { callbackHandler ->
val nPubkeys = pubkeys.map { allocPublicKey(it).ptr } memScoped {
val combined = alloc<secp256k1_pubkey>() val nPubkeys = pubkeys.map { allocPublicKey(it).ptr }
secp256k1_ec_pubkey_combine(ctx, combined.ptr, nPubkeys.toCValues(), pubkeys.size.convert()).requireSuccess("secp256k1_ec_pubkey_combine() failed") val combined = alloc<secp256k1_pubkey>()
return serializePubkey(combined) secp256k1_ec_pubkey_combine(ctx, combined.ptr, nPubkeys.toCValues(), pubkeys.size.convert()).requireSuccess(callbackHandler, "secp256k1_ec_pubkey_combine() failed")
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)
memScoped { CallbackHandler(ctx).use { callbackHandler ->
val nPubkey = allocPublicKey(pubkey) memScoped {
val nPrivkey = toNat(privkey) val nPubkey = allocPublicKey(pubkey)
val output = allocArray<UByteVar>(32) val nPrivkey = toNat(privkey)
secp256k1_ecdh(ctx, output, nPubkey.ptr, nPrivkey, null, null).requireSuccess("secp256k1_ecdh() failed") val output = allocArray<UByteVar>(32)
return output.readBytes(32) secp256k1_ecdh(ctx, output, nPubkey.ptr, nPrivkey, null, null).requireSuccess(callbackHandler, "secp256k1_ecdh() failed")
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)
require(recid in 0..3) // we do not check that recid is valid, which should trigger our illegal callback handler to throw a Secp256k1IllegalCallbackException
memScoped { // require(recid in 0..3)
val nSig = toNat(sig)
val rSig = alloc<secp256k1_ecdsa_recoverable_signature>() CallbackHandler(ctx).use { callbackHandler ->
secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, rSig.ptr, nSig, recid).requireSuccess("secp256k1_ecdsa_recoverable_signature_parse_compact() failed") memScoped {
val nMessage = toNat(message) val nSig = toNat(sig)
val pubkey = alloc<secp256k1_pubkey>() val rSig = alloc<secp256k1_ecdsa_recoverable_signature>()
secp256k1_ecdsa_recover(ctx, pubkey.ptr, rSig.ptr, nMessage).requireSuccess("secp256k1_ecdsa_recover() failed") secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, rSig.ptr, nSig, recid).requireSuccess(callbackHandler, "secp256k1_ecdsa_recoverable_signature_parse_compact() failed")
return serializePubkey(pubkey) val nMessage = toNat(message)
val pubkey = alloc<secp256k1_pubkey>()
secp256k1_ecdsa_recover(ctx, pubkey.ptr, rSig.ptr, nMessage).requireSuccess(callbackHandler, "secp256k1_ecdsa_recover() failed")
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)
memScoped { CallbackHandler(ctx).use { callbackHandler ->
val nSig = allocSignature(sig) memScoped {
val natOutput = allocArray<UByteVar>(73) val nSig = allocSignature(sig)
val len = alloc<size_tVar>() val natOutput = allocArray<UByteVar>(73)
len.value = 73.convert() val len = alloc<size_tVar>()
secp256k1_ecdsa_signature_serialize_der(ctx, natOutput, len.ptr, nSig.ptr).requireSuccess("secp256k1_ecdsa_signature_serialize_der() failed") len.value = 73.convert()
return natOutput.readBytes(len.value.toInt()) 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())
}
} }
} }
@@ -265,13 +320,15 @@ public object Secp256k1Native : Secp256k1 {
require(signature.size == 64) require(signature.size == 64)
require(data.size == 32) require(data.size == 32)
require(pub.size == 32) require(pub.size == 32)
memScoped { CallbackHandler(ctx).use { callbackHandler ->
val nPub = toNat(pub) memScoped {
val pubkey = alloc<secp256k1_xonly_pubkey>() val nPub = toNat(pub)
secp256k1_xonly_pubkey_parse(ctx, pubkey.ptr, nPub).requireSuccess("secp256k1_xonly_pubkey_parse() failed") val pubkey = alloc<secp256k1_xonly_pubkey>()
val nData = toNat(data) secp256k1_xonly_pubkey_parse(ctx, pubkey.ptr, nPub).requireSuccess(callbackHandler, "secp256k1_xonly_pubkey_parse() failed")
val nSig = toNat(signature) val nData = toNat(data)
return secp256k1_schnorrsig_verify(ctx, nSig, nData, 32u, pubkey.ptr) == 1 val nSig = toNat(signature)
return secp256k1_schnorrsig_verify(ctx, nSig, nData, 32u, pubkey.ptr) == 1
}
} }
} }
@@ -279,174 +336,25 @@ 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) }
memScoped { CallbackHandler(ctx).use { callbackHandler ->
val nSec = toNat(sec) memScoped {
val nData = toNat(data) val nSec = toNat(sec)
val nAuxrand32 = auxrand32?.let { toNat(it) } val nData = toNat(data)
val nSig = allocArray<UByteVar>(64) val nAuxrand32 = auxrand32?.let { toNat(it) }
val keypair = alloc<secp256k1_keypair>() val nSig = allocArray<UByteVar>(64)
secp256k1_keypair_create(ctx, keypair.ptr, nSec).requireSuccess("secp256k1_keypair_create() failed") val keypair = alloc<secp256k1_keypair>()
secp256k1_schnorrsig_sign32(ctx, nSig, nData, keypair.ptr, nAuxrand32).requireSuccess("secp256k1_ecdsa_sign() failed") secp256k1_keypair_create(ctx, keypair.ptr, nSec).requireSuccess(callbackHandler, "secp256k1_keypair_create() failed")
return nSig.readBytes(64) secp256k1_schnorrsig_sign32(ctx, nSig, nData, keypair.ptr, nAuxrand32).requireSuccess(callbackHandler, "secp256k1_ecdsa_sign() failed")
} return nSig.readBytes(64)
}
override fun musigNonceGen(session_id32: ByteArray, seckey: ByteArray?, pubkey: ByteArray, msg32: ByteArray?, keyagg_cache: ByteArray?, extra_input32: ByteArray?): ByteArray {
require(session_id32.size == 32)
seckey?.let { require(it.size == 32) }
msg32?.let { require(it.size == 32) }
keyagg_cache?.let { require(it.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE) }
extra_input32?.let { require(it.size == 32) }
val nonce = memScoped {
val secret_nonce = alloc<secp256k1_musig_secnonce>()
val public_nonce = alloc<secp256k1_musig_pubnonce>()
val nPubkey = allocPublicKey(pubkey)
val nKeyAggCache = keyagg_cache?.let {
val n = alloc<secp256k1_musig_keyagg_cache>()
memcpy(n.ptr, toNat(it), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
n
} }
secp256k1_musig_nonce_gen(ctx, secret_nonce.ptr, public_nonce.ptr, toNat(session_id32), seckey?.let { toNat(it) }, nPubkey.ptr, msg32?.let { toNat(it) },nKeyAggCache?.ptr, extra_input32?.let { toNat(it) }).requireSuccess("secp256k1_musig_nonce_gen() failed")
val nPubnonce = allocArray<UByteVar>(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
secp256k1_musig_pubnonce_serialize(ctx, nPubnonce, public_nonce.ptr).requireSuccess("secp256k1_musig_pubnonce_serialize failed")
secret_nonce.ptr.readBytes(Secp256k1.MUSIG2_SECRET_NONCE_SIZE) + nPubnonce.readBytes(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
}
return nonce
}
override fun musigNonceAgg(pubnonces: Array<ByteArray>): ByteArray {
require(pubnonces.isNotEmpty())
pubnonces.forEach { require(it.size == Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE) }
memScoped {
val nPubnonces = pubnonces.map { allocPublicNonce(it).ptr }
val combined = alloc<secp256k1_musig_aggnonce>()
secp256k1_musig_nonce_agg(ctx, combined.ptr, nPubnonces.toCValues(), pubnonces.size.convert()).requireSuccess("secp256k1_musig_nonce_agg() failed")
return serializeAggnonce(combined)
}
}
override fun musigPubkeyAdd(pubkeys: Array<ByteArray>, keyagg_cache: ByteArray?): ByteArray {
require(pubkeys.isNotEmpty())
pubkeys.forEach { require(it.size == 33 || it.size == 65) }
keyagg_cache?.let { require(it.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE) }
memScoped {
val nPubkeys = pubkeys.map { allocPublicKey(it).ptr }
val combined = alloc<secp256k1_xonly_pubkey>()
val nKeyAggCache = keyagg_cache?.let {
val n = alloc<secp256k1_musig_keyagg_cache>()
memcpy(n.ptr, toNat(it), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
n
}
secp256k1_musig_pubkey_agg(ctx, combined.ptr, nKeyAggCache?.ptr, nPubkeys.toCValues(), pubkeys.size.convert()).requireSuccess("secp256k1_musig_nonce_agg() failed")
val agg = serializeXonlyPubkey(combined)
keyagg_cache?.let { blob -> nKeyAggCache?.let { memcpy(toNat(blob), it.ptr, Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong()) } }
return agg
}
}
override fun musigPubkeyTweakAdd(keyagg_cache: ByteArray, tweak32: ByteArray): ByteArray {
require(keyagg_cache.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
require(tweak32.size == 32)
memScoped {
val nKeyAggCache = alloc<secp256k1_musig_keyagg_cache>()
memcpy(nKeyAggCache.ptr, toNat(keyagg_cache), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
val nPubkey = alloc<secp256k1_pubkey>()
secp256k1_musig_pubkey_ec_tweak_add(ctx, nPubkey.ptr, nKeyAggCache.ptr, toNat(tweak32)).requireSuccess("secp256k1_musig_pubkey_ec_tweak_add() failed")
memcpy(toNat(keyagg_cache), nKeyAggCache.ptr, Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
return serializePubkey(nPubkey)
}
}
override fun musigPubkeyXonlyTweakAdd(keyagg_cache: ByteArray, tweak32: ByteArray): ByteArray {
require(keyagg_cache.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
require(tweak32.size == 32)
memScoped {
val nKeyAggCache = alloc<secp256k1_musig_keyagg_cache>()
memcpy(nKeyAggCache.ptr, toNat(keyagg_cache), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
val nPubkey = alloc<secp256k1_pubkey>()
secp256k1_musig_pubkey_xonly_tweak_add(ctx, nPubkey.ptr, nKeyAggCache.ptr, toNat(tweak32)).requireSuccess("secp256k1_musig_pubkey_xonly_tweak_add() failed")
memcpy(toNat(keyagg_cache), nKeyAggCache.ptr, Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
return serializePubkey(nPubkey)
}
}
override fun musigNonceProcess(aggnonce: ByteArray, msg32: ByteArray, keyagg_cache: ByteArray, adaptor: ByteArray?): ByteArray {
require(aggnonce.size == Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
require(keyagg_cache.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
require(msg32.size == 32)
memScoped {
val nKeyAggCache = alloc<secp256k1_musig_keyagg_cache>()
memcpy(nKeyAggCache.ptr, toNat(keyagg_cache), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
val nSession = alloc<secp256k1_musig_session>()
val nAggnonce = alloc<secp256k1_musig_aggnonce>()
secp256k1_musig_aggnonce_parse(ctx, nAggnonce.ptr, toNat(aggnonce)).requireSuccess("secp256k1_musig_aggnonce_parse() failed")
secp256k1_musig_nonce_process(ctx, nSession.ptr, nAggnonce.ptr, toNat(msg32), nKeyAggCache.ptr).requireSuccess("secp256k1_musig_nonce_process() failed")
val session = ByteArray(Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE)
memcpy(toNat(session), nSession.ptr, Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE.toULong())
return session
}
}
override fun musigPartialSign(secnonce: ByteArray, privkey: ByteArray, keyagg_cache: ByteArray, session: ByteArray): ByteArray {
require(secnonce.size == Secp256k1.MUSIG2_SECRET_NONCE_SIZE)
require(privkey.size == 32)
require(keyagg_cache.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
require(session.size == Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE)
memScoped {
val nSecnonce = alloc<secp256k1_musig_secnonce>()
memcpy(nSecnonce.ptr, toNat(secnonce), Secp256k1.MUSIG2_SECRET_NONCE_SIZE.toULong())
val nKeypair = alloc<secp256k1_keypair>()
secp256k1_keypair_create(ctx, nKeypair.ptr, toNat(privkey))
val nPsig = alloc<secp256k1_musig_partial_sig>()
val nKeyAggCache = alloc<secp256k1_musig_keyagg_cache>()
memcpy(nKeyAggCache.ptr, toNat(keyagg_cache), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
val nSession = alloc<secp256k1_musig_session>()
memcpy(nSession.ptr, toNat(session), Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE.toULong())
secp256k1_musig_partial_sign(ctx, nPsig.ptr, nSecnonce.ptr, nKeypair.ptr, nKeyAggCache.ptr, nSession.ptr).requireSuccess("secp256k1_musig_partial_sign failed")
val psig = ByteArray(32)
secp256k1_musig_partial_sig_serialize(ctx, toNat(psig), nPsig.ptr).requireSuccess("secp256k1_musig_partial_sig_serialize() failed")
return psig
}
}
override fun musigPartialSigVerify(psig: ByteArray, pubnonce: ByteArray, pubkey: ByteArray, keyagg_cache: ByteArray, session: ByteArray): Int {
require(psig.size == 32)
require(pubnonce.size == Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
require(pubkey.size == 33 || pubkey.size == 65)
require(keyagg_cache.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
require(session.size == Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE)
memScoped {
val nPSig = allocPartialSig(psig)
val nPubnonce = allocPublicNonce(pubnonce)
val nPubkey = allocPublicKey(pubkey)
val nKeyAggCache = alloc<secp256k1_musig_keyagg_cache>()
memcpy(nKeyAggCache.ptr, toNat(keyagg_cache), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
val nSession = alloc<secp256k1_musig_session>()
memcpy(nSession.ptr, toNat(session), Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE.toULong())
return secp256k1_musig_partial_sig_verify(ctx, nPSig.ptr, nPubnonce.ptr, nPubkey.ptr, nKeyAggCache.ptr, nSession.ptr)
}
}
override fun musigPartialSigAgg(session: ByteArray, psigs: Array<ByteArray>): ByteArray {
require(session.size == Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE)
require(psigs.isNotEmpty())
psigs.forEach { require(it.size == 32) }
memScoped {
val nSession = alloc<secp256k1_musig_session>()
memcpy(nSession.ptr, toNat(session), Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE.toULong())
val nPsigs = psigs.map { allocPartialSig(it).ptr }
val sig64 = ByteArray(64)
secp256k1_musig_partial_sig_agg(ctx, toNat(sig64), nSession.ptr, nPsigs.toCValues(), psigs.size.convert()).requireSuccess("secp256k1_musig_partial_sig_agg() failed")
return sig64
} }
} }
public override fun cleanup() { public override fun cleanup() {
secp256k1_context_destroy(ctx) secp256k1_context_destroy(ctx)
} }
} }
internal actual fun getSecpk256k1(): Secp256k1 = Secp256k1Native internal actual fun getSecpk256k1(): Secp256k1 = Secp256k1Native

View File

@@ -36,14 +36,14 @@ kotlin {
} }
if (includeAndroid) { if (includeAndroid) {
androidTarget { android {
compilations.all { compilations.all {
kotlinOptions.jvmTarget = "1.8" kotlinOptions.jvmTarget = "1.8"
} }
sourceSets["androidMain"].dependencies { sourceSets["androidMain"].dependencies {
implementation(project(":jni:android")) implementation(project(":jni:android"))
} }
sourceSets["androidUnitTest"].dependencies { sourceSets["androidTest"].dependencies {
implementation(kotlin("test-junit")) implementation(kotlin("test-junit"))
implementation("androidx.test.ext:junit:1.1.2") implementation("androidx.test.ext:junit:1.1.2")
implementation("androidx.test.espresso:espresso-core:3.3.0") implementation("androidx.test.espresso:espresso-core:3.3.0")
@@ -51,18 +51,17 @@ kotlin {
} }
} }
linuxX64() linuxX64("linux")
iosX64()
iosArm64() ios()
iosSimulatorArm64()
} }
val includeAndroid = System.getProperty("includeAndroid")?.toBoolean() ?: true val includeAndroid = System.getProperty("includeAndroid")?.toBoolean() ?: true
if (includeAndroid) { if (includeAndroid) {
extensions.configure<com.android.build.gradle.LibraryExtension>("android") { extensions.configure<com.android.build.gradle.LibraryExtension>("android") {
defaultConfig { defaultConfig {
compileSdk =30 compileSdkVersion(30)
minSdk = 21 minSdkVersion(21)
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }

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
@@ -352,151 +357,6 @@ class Secp256k1Test {
} }
} }
@Test
fun testMusig2GenerateNonce() {
val pubkey = Hex.decode("02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9")
val sessionId = Hex.decode("0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F")
val nonce = Secp256k1.musigNonceGen(sessionId, null, pubkey, null, null, null)
val pubnonce = Hex.encode(nonce.copyOfRange(132, 132 + 66)).uppercase()
assertEquals("02C96E7CB1E8AA5DAC64D872947914198F607D90ECDE5200DE52978AD5DED63C000299EC5117C2D29EDEE8A2092587C3909BE694D5CFF0667D6C02EA4059F7CD9786", pubnonce)
}
@Test
fun testMusig2AggregateNonce() {
val nonces = listOf(
"020151C80F435648DF67A22B749CD798CE54E0321D034B92B709B567D60A42E66603BA47FBC1834437B3212E89A84D8425E7BF12E0245D98262268EBDCB385D50641",
"03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B833",
"020151C80F435648DF67A22B749CD798CE54E0321D034B92B709B567D60A42E6660279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
"03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60379BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
"04FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B833",
"03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B831",
"03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A602FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30"
).map { Hex.decode(it) }
val agg1 = Secp256k1.musigNonceAgg(arrayOf(nonces[0], nonces[1]))
assertEquals("035FE1873B4F2967F52FEA4A06AD5A8ECCBE9D0FD73068012C894E2E87CCB5804B024725377345BDE0E9C33AF3C43C0A29A9249F2F2956FA8CFEB55C8573D0262DC8", Hex.encode(agg1).uppercase())
val agg2 = Secp256k1.musigNonceAgg(arrayOf(nonces[2], nonces[3]))
assertEquals("035FE1873B4F2967F52FEA4A06AD5A8ECCBE9D0FD73068012C894E2E87CCB5804B000000000000000000000000000000000000000000000000000000000000000000", Hex.encode(agg2).uppercase())
assertFails {
Secp256k1.musigNonceAgg(arrayOf(nonces[0], nonces[4]))
}
assertFails {
Secp256k1.musigNonceAgg(arrayOf(nonces[5], nonces[1]))
}
assertFails {
Secp256k1.musigNonceAgg(arrayOf(nonces[6], nonces[1]))
}
}
@Test
fun testMusig2AggregatePubkey() {
val pubkeys = listOf(
"02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
"03DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659",
"023590A94E768F8E1815C2F24B4D80A8E3149316C3518CE7B7AD338368D038CA66",
"020000000000000000000000000000000000000000000000000000000000000005",
"02FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30",
"04F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
"03935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9"
).map { Hex.decode(it) }
val agg1 = Secp256k1.musigPubkeyAdd(arrayOf(pubkeys[0], pubkeys[1], pubkeys[2]), null)
assertEquals("90539EEDE565F5D054F32CC0C220126889ED1E5D193BAF15AEF344FE59D4610C", Hex.encode(agg1).uppercase())
val cache = ByteArray(197)
val agg2 = Secp256k1.musigPubkeyAdd(arrayOf(pubkeys[0], pubkeys[1], pubkeys[2]), cache)
assertEquals("90539EEDE565F5D054F32CC0C220126889ED1E5D193BAF15AEF344FE59D4610C", Hex.encode(agg2).uppercase())
val agg3 = Secp256k1.musigPubkeyAdd(arrayOf(pubkeys[2], pubkeys[1], pubkeys[0]), null)
assertEquals("6204DE8B083426DC6EAF9502D27024D53FC826BF7D2012148A0575435DF54B2B", Hex.encode(agg3).uppercase())
val agg4 = Secp256k1.musigPubkeyAdd(arrayOf(pubkeys[0], pubkeys[0], pubkeys[0]), null)
assertEquals("B436E3BAD62B8CD409969A224731C193D051162D8C5AE8B109306127DA3AA935", Hex.encode(agg4).uppercase())
val agg5 = Secp256k1.musigPubkeyAdd(arrayOf(pubkeys[0], pubkeys[0], pubkeys[1], pubkeys[1]), null)
assertEquals("69BC22BFA5D106306E48A20679DE1D7389386124D07571D0D872686028C26A3E", Hex.encode(agg5).uppercase())
}
@Test
fun testMusig2TweakPubkeys() {
val pubkeys = listOf(
"031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f",
"024d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766",
"02531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe337"
).map { Hex.decode(it) }.toTypedArray()
val cache = ByteArray(197)
val agg1 = Secp256k1.musigPubkeyAdd(pubkeys, cache)
assertEquals("b6d830642403fc82511aca5ff98a5e76fcef0f89bffc1aadbe78ee74cd5a5716", Hex.encode(agg1))
val agg2 = Secp256k1.musigPubkeyTweakAdd(cache, Hex.decode("7468697320636f756c64206265206120424950333220747765616b2e2e2e2e00"))
assertEquals("04791e4f22a21f19bd9798eceab92ad2ccc18f2d6660e91ae4c0709aaebf1aa9023701f468b0eddf8973495a5327f2169d9c6a50eb6a0f87c0fbee90a4067eb230", Hex.encode(agg2))
val agg3 = Secp256k1.musigPubkeyXonlyTweakAdd(cache, Hex.decode("7468697320636f756c64206265206120746170726f6f7420747765616b2e2e00"))
assertEquals("04537a081a8d32ff700ca86aaa77a423e9b8d1480938076b645c68ee39d263c93948026928799b2d942cb5851db397015b26b1759de1b9ab2c691ced64a2eef836", Hex.encode(agg3))
}
@Test
fun testMusig2SigningSession() {
val privkeys = listOf(
"0101010101010101010101010101010101010101010101010101010101010101",
"0202020202020202020202020202020202020202020202020202020202020202",
).map { Hex.decode(it) }.toTypedArray()
val pubkeys = privkeys.map { Secp256k1.pubkeyCreate(it) }
val sessionId = Hex.decode("0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F")
val nonces = pubkeys.map { Secp256k1.musigNonceGen(sessionId, null, it, null, null, null) }
val secnonces = nonces.map { it.copyOfRange(0, 132) }
val pubnonces = nonces.map { it.copyOfRange(132, 132 + 66) }
val aggnonce = Secp256k1.musigNonceAgg(pubnonces.toTypedArray())
val caches = (0 until 2).map { ByteArray(197) }
val aggpubkey = Secp256k1.musigPubkeyAdd(pubkeys.toTypedArray(), caches[0])
Secp256k1.musigPubkeyAdd(pubkeys.toTypedArray(), caches[1])
val msg32 = Hex.decode("0303030303030303030303030303030303030303030303030303030303030303")
val sessions = (0 until 2).map { Secp256k1.musigNonceProcess(aggnonce, msg32, caches[it], null) }
val psigs = (0 until 2).map {
val psig = Secp256k1.musigPartialSign(secnonces[it], privkeys[it], caches[it], sessions[it])
val check = Secp256k1.musigPartialSigVerify(psig, pubnonces[it], pubkeys[it], caches[it], sessions[it])
assertEquals(1, check)
psig
}
val sig = Secp256k1.musigPartialSigAgg(sessions[0], psigs.toTypedArray())
val check = Secp256k1.verifySchnorr(sig, msg32, aggpubkey)
assertTrue(check)
}
@Test
fun testInvalidArguments() {
assertFails {
Secp256k1.pubkeyCreate(ByteArray(32))
}
assertFails {
Secp256k1.pubkeyCreate(Hex.decode("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
}
assertFails {
Secp256k1.pubkeyParse(ByteArray(33))
}
assertFails {
Secp256k1.pubkeyParse(Hex.decode("03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
}
assertFails {
Secp256k1.pubKeyCombine(arrayOf())
}
assertFails {
Secp256k1.pubKeyCombine(arrayOf(ByteArray(0)))
}
assertFails {
Secp256k1.signSchnorr(ByteArray(0), Hex.decode("0101010101010101010101010101010101010101010101010101010101010101"), null)
}
assertFails {
Secp256k1.ecdsaRecover(
Hex.decode("01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101"),
Hex.decode("0202020202020202020202020202020202020202020202020202020202020202"),
-1
)
}
}
@Test @Test
fun fuzzEcdsaSignVerify() { fun fuzzEcdsaSignVerify() {
val random = Random.Default val random = Random.Default