Compare commits
5 Commits
snapshot/s
...
snapshot/i
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0364ec762e | ||
|
|
aadffabe42 | ||
|
|
8e17e7030a | ||
|
|
929e2cda40 | ||
|
|
41eac9273f |
10
.github/workflows/release.yml
vendored
10
.github/workflows/release.yml
vendored
@@ -58,11 +58,15 @@ jobs:
|
||||
base-devel
|
||||
autotools
|
||||
mingw-w64-x86_64-gcc
|
||||
- name: Set up JDK 8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 8
|
||||
- name: Setup Android
|
||||
if: matrix.os != 'windows-latest'
|
||||
shell: bash
|
||||
run: |
|
||||
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "ndk;$ANDROID_NDK_VERSION"
|
||||
$ANDROID_HOME/tools/bin/sdkmanager "ndk;$ANDROID_NDK_VERSION"
|
||||
- name: Setup Android
|
||||
if: matrix.os == 'windows-latest'
|
||||
shell: msys2 {0}
|
||||
@@ -82,7 +86,7 @@ jobs:
|
||||
- name: Check Linux
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
shell: bash
|
||||
run: ./gradlew linuxX64Test
|
||||
run: ./gradlew linuxTest
|
||||
- name: Check iOS
|
||||
if: matrix.os == 'macOS-latest'
|
||||
shell: bash
|
||||
@@ -99,7 +103,7 @@ jobs:
|
||||
- name: Publish Linux
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
shell: bash
|
||||
run: ./gradlew publishLinuxX64PublicationToMavenLocal :jni:jvm:linux:publishJvmPublicationToMavenLocal
|
||||
run: ./gradlew publishLinuxPublicationToMavenLocal :jni:jvm:linux:publishJvmPublicationToMavenLocal
|
||||
- name: Publish Windows
|
||||
if: matrix.os == 'windows-latest'
|
||||
shell: msys2 {0}
|
||||
|
||||
10
.github/workflows/snapshot.yml
vendored
10
.github/workflows/snapshot.yml
vendored
@@ -67,11 +67,15 @@ jobs:
|
||||
base-devel
|
||||
autotools
|
||||
mingw-w64-x86_64-gcc
|
||||
- name: Set up JDK 8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 8
|
||||
- name: Setup Android
|
||||
if: matrix.os != 'windows-latest'
|
||||
shell: bash
|
||||
run: |
|
||||
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "ndk;$ANDROID_NDK_VERSION"
|
||||
$ANDROID_HOME/tools/bin/sdkmanager "ndk;$ANDROID_NDK_VERSION"
|
||||
- name: Setup Android
|
||||
if: matrix.os == 'windows-latest'
|
||||
shell: msys2 {0}
|
||||
@@ -91,7 +95,7 @@ jobs:
|
||||
- name: Check Linux
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
shell: bash
|
||||
run: ./gradlew linuxX64Test
|
||||
run: ./gradlew linuxTest
|
||||
- name: Check iOS
|
||||
if: matrix.os == 'macOS-latest'
|
||||
shell: bash
|
||||
@@ -108,7 +112,7 @@ jobs:
|
||||
- name: Publish Linux
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
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
|
||||
if: matrix.os == 'windows-latest'
|
||||
shell: msys2 {0}
|
||||
|
||||
8
.github/workflows/test.yml
vendored
8
.github/workflows/test.yml
vendored
@@ -73,11 +73,15 @@ jobs:
|
||||
base-devel
|
||||
autotools
|
||||
mingw-w64-x86_64-gcc
|
||||
- name: Set up JDK 8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 8
|
||||
- name: Setup Android
|
||||
if: matrix.os != 'windows-latest'
|
||||
shell: bash
|
||||
run: |
|
||||
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "ndk;$ANDROID_NDK_VERSION"
|
||||
$ANDROID_HOME/tools/bin/sdkmanager "ndk;$ANDROID_NDK_VERSION"
|
||||
- name: Setup Android
|
||||
if: matrix.os == 'windows-latest'
|
||||
shell: msys2 {0}
|
||||
@@ -97,7 +101,7 @@ jobs:
|
||||
- name: Check Linux
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
shell: bash
|
||||
run: ./gradlew linuxX64Test
|
||||
run: ./gradlew linuxTest
|
||||
- name: Check iOS
|
||||
if: matrix.os == 'macOS-latest'
|
||||
shell: bash
|
||||
|
||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -1,3 +1,3 @@
|
||||
[submodule "native/secp256k1"]
|
||||
path = native/secp256k1
|
||||
url = https://github.com/jonasnick/secp256k1.git
|
||||
url = https://github.com/bitcoin-core/secp256k1.git
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[](http://kotlinlang.org)
|
||||
[](http://kotlinlang.org)
|
||||
[](https://search.maven.org/search?q=g:fr.acinq.secp256k1%20a:secp256k1-kmp*)
|
||||

|
||||
[](https://github.com/ACINQ/secp256k1-kmp/blob/master/LICENSE)
|
||||
|
||||
@@ -3,8 +3,8 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
|
||||
import org.jetbrains.dokka.Platform
|
||||
|
||||
plugins {
|
||||
kotlin("multiplatform") version "1.9.22"
|
||||
id("org.jetbrains.dokka") version "1.9.10"
|
||||
kotlin("multiplatform") version "1.8.21"
|
||||
id("org.jetbrains.dokka") version "1.8.10"
|
||||
`maven-publish`
|
||||
}
|
||||
|
||||
@@ -16,13 +16,13 @@ buildscript {
|
||||
|
||||
dependencies {
|
||||
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 {
|
||||
group = "fr.acinq.secp256k1"
|
||||
version = "0.16.0-SNAPSHOT"
|
||||
version = "0.12.0-SNAPSHOT"
|
||||
|
||||
repositories {
|
||||
google()
|
||||
@@ -52,30 +52,20 @@ kotlin {
|
||||
}
|
||||
}
|
||||
|
||||
val nativeMain by sourceSets.creating
|
||||
val nativeMain by sourceSets.creating { dependsOn(commonMain) }
|
||||
|
||||
linuxX64 {
|
||||
linuxX64("linux") {
|
||||
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")
|
||||
}
|
||||
|
||||
macosX64 {
|
||||
secp256k1CInterop("host")
|
||||
}
|
||||
|
||||
macosArm64 {
|
||||
secp256k1CInterop("host")
|
||||
}
|
||||
|
||||
iosX64 {
|
||||
secp256k1CInterop("ios")
|
||||
}
|
||||
|
||||
iosArm64 {
|
||||
secp256k1CInterop("ios")
|
||||
}
|
||||
|
||||
iosSimulatorArm64 {
|
||||
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 {
|
||||
@@ -90,9 +80,9 @@ allprojects {
|
||||
val currentOs = OperatingSystem.current()
|
||||
val targets = when {
|
||||
currentOs.isLinux -> listOf()
|
||||
currentOs.isMacOsX -> listOf("linuxX64")
|
||||
currentOs.isWindows -> listOf("linuxX64")
|
||||
else -> listOf("linuxX64")
|
||||
currentOs.isMacOsX -> listOf("linux")
|
||||
currentOs.isWindows -> listOf("linux")
|
||||
else -> listOf("linux")
|
||||
}.mapNotNull { kotlin.targets.findByName(it) as? KotlinNativeTarget }
|
||||
|
||||
configure(targets) {
|
||||
|
||||
@@ -6,7 +6,6 @@ org.gradle.parallel = true
|
||||
kotlin.code.style = official
|
||||
kotlin.native.ignoreDisabledTargets = true
|
||||
kotlin.mpp.enableCInteropCommonization=true
|
||||
kotlin.native.cacheKind.macosArm64=none
|
||||
|
||||
# Android
|
||||
android.useAndroidX = true
|
||||
@@ -6,10 +6,11 @@ import fr.acinq.secp256k1.NativeSecp256k1
|
||||
import java.util.*
|
||||
|
||||
public object NativeSecp256k1AndroidLoader {
|
||||
|
||||
@JvmStatic
|
||||
@Synchronized
|
||||
@Throws(Exception::class)
|
||||
public fun load(): Secp256k1 {
|
||||
fun load(): Secp256k1 {
|
||||
try {
|
||||
System.loadLibrary("secp256k1-jni")
|
||||
return NativeSecp256k1
|
||||
@@ -26,4 +27,5 @@ public object NativeSecp256k1AndroidLoader {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,34 +7,6 @@
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#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
|
||||
* 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
|
||||
(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
|
||||
*/
|
||||
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1nonce_1process
|
||||
(JNIEnv *, jclass, jlong, 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
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "include/secp256k1_ecdh.h"
|
||||
#include "include/secp256k1_recovery.h"
|
||||
#include "include/secp256k1_schnorrsig.h"
|
||||
#include "include/secp256k1_musig.h"
|
||||
#include "fr_acinq_secp256k1_Secp256k1CFunctions.h"
|
||||
|
||||
#define SIG_FORMAT_UNKNOWN 0
|
||||
@@ -24,29 +23,73 @@ void JNI_ThrowByName(JNIEnv *penv, const char *name, const char *msg)
|
||||
(*penv)->DeleteLocalRef(penv, cls);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* secp256k1 uses callbacks for errors that are either hw pbs or bugs in the calling library, for example
|
||||
* passing parameters with values that are explicitly defined as illegal in the API, and should never be called for normal operations
|
||||
* But if they are, default behaviour is to print an error to stderr and abort which is not what we want especially in mobile apps
|
||||
* => we set up string pointers in every method, and custom callback that will set them to the message passed in by sec256k1's callbacks, which
|
||||
* we turn into specific Sec256k1 exceptions
|
||||
*/
|
||||
#define SETUP_ERROR_CALLBACKS \
|
||||
char *error_callback_message = NULL; \
|
||||
char *illegal_callback_message = NULL; \
|
||||
secp256k1_context_set_error_callback(ctx, my_error_callback_fn, &error_callback_message); \
|
||||
secp256k1_context_set_illegal_callback(ctx, my_illegal_callback_fn, &illegal_callback_message);
|
||||
|
||||
#define CHECKRESULT(errorcheck, message) \
|
||||
{ \
|
||||
if (errorcheck) \
|
||||
{ \
|
||||
JNI_ThrowByName(penv, "fr/acinq/secp256k1/Secp256k1Exception", message); \
|
||||
return 0; \
|
||||
} \
|
||||
#define CHECKRESULT(errorcheck, message) \
|
||||
{ \
|
||||
if (error_callback_message) \
|
||||
{ \
|
||||
JNI_ThrowByName(penv, "fr/acinq/secp256k1/Secp256k1ErrorCallbackException", error_callback_message); \
|
||||
return 0; \
|
||||
} \
|
||||
if (illegal_callback_message) \
|
||||
{ \
|
||||
JNI_ThrowByName(penv, "fr/acinq/secp256k1/Secp256k1IllegalCallbackException", illegal_callback_message); \
|
||||
return 0; \
|
||||
} \
|
||||
if (errorcheck) \
|
||||
{ \
|
||||
JNI_ThrowByName(penv, "fr/acinq/secp256k1/Secp256k1Exception", message); \
|
||||
return 0; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define CHECKRESULT1(errorcheck, message, dosomething) \
|
||||
{ \
|
||||
if (errorcheck) \
|
||||
{ \
|
||||
dosomething; \
|
||||
JNI_ThrowByName(penv, "fr/acinq/secp256k1/Secp256k1Exception", message); \
|
||||
return 0; \
|
||||
} \
|
||||
#define CHECKRESULT1(errorcheck, message, dosomething) \
|
||||
{ \
|
||||
if (error_callback_message) \
|
||||
{ \
|
||||
dosomething; \
|
||||
JNI_ThrowByName(penv, "fr/acinq/secp256k1/Secp256k1ErrorCallbackException", error_callback_message); \
|
||||
return 0; \
|
||||
} \
|
||||
if (illegal_callback_message) \
|
||||
{ \
|
||||
dosomething; \
|
||||
JNI_ThrowByName(penv, "fr/acinq/secp256k1/Secp256k1IllegalCallbackException", illegal_callback_message); \
|
||||
return 0; \
|
||||
} \
|
||||
if (errorcheck) \
|
||||
{ \
|
||||
JNI_ThrowByName(penv, "fr/acinq/secp256k1/Secp256k1Exception", message); \
|
||||
return 0; \
|
||||
} \
|
||||
}
|
||||
|
||||
static void secp256k1_noop_illegal_callback_fn(const char* str, void* data) {
|
||||
(void)str;
|
||||
(void)data;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -56,9 +99,7 @@ static void secp256k1_noop_illegal_callback_fn(const char* str, void* data) {
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1context_1create(JNIEnv *penv, jclass clazz, jint flags)
|
||||
{
|
||||
jlong ctx = (jlong)secp256k1_context_create(flags);
|
||||
secp256k1_context_set_illegal_callback(ctx, &secp256k1_noop_illegal_callback_fn, NULL);
|
||||
return ctx;
|
||||
return (jlong)secp256k1_context_create(flags);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -92,6 +133,8 @@ JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec
|
||||
if ((*penv)->GetArrayLength(penv, jseckey) != 32)
|
||||
return 0;
|
||||
|
||||
SETUP_ERROR_CALLBACKS
|
||||
|
||||
seckey = (*penv)->GetByteArrayElements(penv, jseckey, 0);
|
||||
result = secp256k1_ec_seckey_verify(ctx, (unsigned char *)seckey);
|
||||
(*penv)->ReleaseByteArrayElements(penv, jseckey, seckey, 0);
|
||||
@@ -116,6 +159,8 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
|
||||
if (jpubkey == NULL)
|
||||
return 0;
|
||||
|
||||
SETUP_ERROR_CALLBACKS
|
||||
|
||||
size = (*penv)->GetArrayLength(penv, jpubkey);
|
||||
CHECKRESULT((size != 33) && (size != 65), "invalid public key size");
|
||||
|
||||
@@ -152,6 +197,8 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
|
||||
if (jctx == 0)
|
||||
return NULL;
|
||||
|
||||
SETUP_ERROR_CALLBACKS
|
||||
|
||||
CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "secret key must be 32 bytes");
|
||||
seckey = (*penv)->GetByteArrayElements(penv, jseckey, 0);
|
||||
result = secp256k1_ec_pubkey_create(ctx, &pub, (unsigned char *)seckey);
|
||||
@@ -186,6 +233,8 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
|
||||
if (jseckey == NULL)
|
||||
return NULL;
|
||||
|
||||
SETUP_ERROR_CALLBACKS
|
||||
|
||||
CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "secret key must be 32 bytes");
|
||||
CHECKRESULT((*penv)->GetArrayLength(penv, jmsg) != 32, "message key must be 32 bytes");
|
||||
seckey = (*penv)->GetByteArrayElements(penv, jseckey, 0);
|
||||
@@ -236,6 +285,8 @@ JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec
|
||||
if (jpubkey == NULL)
|
||||
return 0;
|
||||
|
||||
SETUP_ERROR_CALLBACKS
|
||||
|
||||
sigSize = (*penv)->GetArrayLength(penv, jsig);
|
||||
int sigFormat = GetSignatureFormat(sigSize);
|
||||
CHECKRESULT(sigFormat == SIG_FORMAT_UNKNOWN, "invalid signature size");
|
||||
@@ -293,6 +344,8 @@ JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec
|
||||
if (jsigout == NULL)
|
||||
return 0;
|
||||
|
||||
SETUP_ERROR_CALLBACKS
|
||||
|
||||
size = (*penv)->GetArrayLength(penv, jsigin);
|
||||
sigFormat = GetSignatureFormat(size);
|
||||
CHECKRESULT(sigFormat == SIG_FORMAT_UNKNOWN, "invalid signature size");
|
||||
@@ -336,6 +389,9 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
|
||||
return 0;
|
||||
if (jseckey == NULL)
|
||||
return 0;
|
||||
|
||||
SETUP_ERROR_CALLBACKS
|
||||
|
||||
CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "secret key must be 32 bytes");
|
||||
seckey = (*penv)->GetByteArrayElements(penv, jseckey, 0);
|
||||
result = secp256k1_ec_seckey_negate(ctx, (unsigned char *)seckey);
|
||||
@@ -362,6 +418,8 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
|
||||
if (jpubkey == NULL)
|
||||
return 0;
|
||||
|
||||
SETUP_ERROR_CALLBACKS
|
||||
|
||||
size = (*penv)->GetArrayLength(penv, jpubkey);
|
||||
CHECKRESULT((size != 33) && (size != 65), "invalid public key size");
|
||||
pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0);
|
||||
@@ -399,6 +457,8 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
|
||||
if (jtweak == NULL)
|
||||
return NULL;
|
||||
|
||||
SETUP_ERROR_CALLBACKS
|
||||
|
||||
CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "secret key must be 32 bytes");
|
||||
CHECKRESULT((*penv)->GetArrayLength(penv, jtweak) != 32, "tweak must be 32 bytes");
|
||||
seckey = (*penv)->GetByteArrayElements(penv, jseckey, 0);
|
||||
@@ -430,6 +490,8 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
|
||||
if (jtweak == NULL)
|
||||
return NULL;
|
||||
|
||||
SETUP_ERROR_CALLBACKS
|
||||
|
||||
size = (*penv)->GetArrayLength(penv, jpubkey);
|
||||
CHECKRESULT((size != 33) && (size != 65), "invalid public key size");
|
||||
CHECKRESULT((*penv)->GetArrayLength(penv, jtweak) != 32, "tweak must be 32 bytes");
|
||||
@@ -471,6 +533,8 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
|
||||
if (jtweak == NULL)
|
||||
return NULL;
|
||||
|
||||
SETUP_ERROR_CALLBACKS
|
||||
|
||||
CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "secret key must be 32 bytes");
|
||||
CHECKRESULT((*penv)->GetArrayLength(penv, jtweak) != 32, "tweak must be 32 bytes");
|
||||
seckey = (*penv)->GetByteArrayElements(penv, jseckey, 0);
|
||||
@@ -502,6 +566,8 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
|
||||
if (jtweak == NULL)
|
||||
return NULL;
|
||||
|
||||
SETUP_ERROR_CALLBACKS
|
||||
|
||||
size = (*penv)->GetArrayLength(penv, jpubkey);
|
||||
CHECKRESULT((size != 33) && (size != 65), "invalid public key size");
|
||||
CHECKRESULT((*penv)->GetArrayLength(penv, jtweak) != 32, "tweak must be 32 bytes");
|
||||
@@ -556,9 +622,10 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
|
||||
if (jpubkeys == NULL)
|
||||
return NULL;
|
||||
|
||||
count = (*penv)->GetArrayLength(penv, jpubkeys);
|
||||
CHECKRESULT(count < 1, "pubkey array cannot be empty")
|
||||
pubkeys = calloc(count, sizeof(secp256k1_pubkey *));
|
||||
SETUP_ERROR_CALLBACKS
|
||||
|
||||
count = (*penv)->GetArrayLength(penv, jpubkeys);
|
||||
pubkeys = calloc(count, sizeof(secp256k1_pubkey *));
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
@@ -605,6 +672,8 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
|
||||
if (jpubkey == NULL)
|
||||
return NULL;
|
||||
|
||||
SETUP_ERROR_CALLBACKS
|
||||
|
||||
CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "invalid private key size");
|
||||
|
||||
size = (*penv)->GetArrayLength(penv, jpubkey);
|
||||
@@ -646,7 +715,11 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
|
||||
return NULL;
|
||||
if (jmsg == 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);
|
||||
int sigFormat = GetSignatureFormat(sigSize);
|
||||
@@ -702,6 +775,9 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
|
||||
return 0;
|
||||
if (jsig == NULL)
|
||||
return 0;
|
||||
|
||||
SETUP_ERROR_CALLBACKS
|
||||
|
||||
CHECKRESULT((*penv)->GetArrayLength(penv, jsig) != 64, "invalid signature size");
|
||||
|
||||
size = (*penv)->GetArrayLength(penv, jsig);
|
||||
@@ -741,6 +817,8 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
|
||||
if (jseckey == NULL)
|
||||
return NULL;
|
||||
|
||||
SETUP_ERROR_CALLBACKS
|
||||
|
||||
CHECKRESULT((*penv)->GetArrayLength(penv, jseckey) != 32, "secret key must be 32 bytes");
|
||||
CHECKRESULT((*penv)->GetArrayLength(penv, jmsg) != 32, "message must be 32 bytes");
|
||||
if (jauxrand32 != 0)
|
||||
@@ -794,6 +872,8 @@ JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1sc
|
||||
if (jpubkey == NULL)
|
||||
return 0;
|
||||
|
||||
SETUP_ERROR_CALLBACKS
|
||||
|
||||
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, jmsg) != 32, "message must be 32 bytes");
|
||||
@@ -810,536 +890,3 @@ JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1sc
|
||||
(*penv)->ReleaseByteArrayElements(penv, jmsg, msg, 0);
|
||||
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 private key 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");
|
||||
|
||||
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)
|
||||
{
|
||||
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 keyagg cache 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;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ if [ "$TARGET" == "linux" ]; then
|
||||
CC_OPTS="-fPIC"
|
||||
elif [ "$TARGET" == "darwin" ]; then
|
||||
OUTFILE=libsecp256k1-jni.dylib
|
||||
CC_OPTS="-arch arm64 -arch x86_64"
|
||||
elif [ "$TARGET" == "mingw" ]; then
|
||||
OUTFILE=secp256k1-jni.dll
|
||||
CC=x86_64-w64-mingw32-gcc
|
||||
|
||||
@@ -12,8 +12,12 @@ dependencies {
|
||||
val copyJni by tasks.creating(Sync::class) {
|
||||
onlyIf { org.gradle.internal.os.OperatingSystem.current().isMacOsX }
|
||||
dependsOn(":jni:jvm:buildNativeHost")
|
||||
val arch = when (System.getProperty("os.arch")) {
|
||||
"aarch64" -> "aarch64"
|
||||
else -> "x86_64"
|
||||
}
|
||||
from(rootDir.resolve("jni/jvm/build/darwin/libsecp256k1-jni.dylib"))
|
||||
into(buildDir.resolve("jniResources/fr/acinq/secp256k1/jni/native/darwin"))
|
||||
into(buildDir.resolve("jniResources/fr/acinq/secp256k1/jni/native/darwin-$arch"))
|
||||
}
|
||||
|
||||
(tasks["processResources"] as ProcessResources).apply {
|
||||
|
||||
@@ -19,8 +19,7 @@ internal object OSInfo {
|
||||
private const val PPC = "ppc"
|
||||
private const val PPC64 = "ppc64"
|
||||
|
||||
// on macos we build a universal library that contains arm64 and x64 binaries
|
||||
@JvmStatic val nativeSuffix: String get() = if (os == "darwin") os else "$os-$arch"
|
||||
@JvmStatic val nativeSuffix: String get() = "$os-$arch"
|
||||
|
||||
@JvmStatic val os: String get() = translateOSName(System.getProperty("os.name"))
|
||||
|
||||
|
||||
@@ -29,32 +29,12 @@ public class Secp256k1CFunctions {
|
||||
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);
|
||||
|
||||
/**
|
||||
* A musig2 public nonce is simply two elliptic curve points.
|
||||
*/
|
||||
public static final int SECP256K1_MUSIG_PUBLIC_NONCE_SIZE = 66;
|
||||
|
||||
/**
|
||||
* A musig2 private nonce is basically two scalars, but should be treated as an opaque blob.
|
||||
*/
|
||||
public static final int SECP256K1_MUSIG_SECRET_NONCE_SIZE = 132;
|
||||
|
||||
/**
|
||||
* When aggregating public keys, we cache information in an opaque blob (must not be interpreted).
|
||||
*/
|
||||
public static final int SECP256K1_MUSIG_KEYAGG_CACHE_SIZE = 197;
|
||||
|
||||
/**
|
||||
* When creating partial signatures and aggregating them, session data is kept in an opaque blob (must not be interpreted).
|
||||
*/
|
||||
public static final int SECP256K1_MUSIG_SESSION_SIZE = 133;
|
||||
|
||||
public static native long secp256k1_context_create(int flags);
|
||||
|
||||
public static native void secp256k1_context_destroy(long ctx);
|
||||
|
||||
public static native int secp256k1_ec_seckey_verify(long ctx, byte[] seckey);
|
||||
|
||||
|
||||
public static native byte[] secp256k1_ec_pubkey_parse(long ctx, byte[] pubkey);
|
||||
|
||||
public static native byte[] secp256k1_ec_pubkey_create(long ctx, byte[] seckey);
|
||||
@@ -88,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 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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -92,42 +92,6 @@ public object NativeSecp256k1 : Secp256k1 {
|
||||
return Secp256k1CFunctions.secp256k1_schnorrsig_sign(Secp256k1Context.getContext(), data, sec, auxrand32)
|
||||
}
|
||||
|
||||
override fun musigNonceGen(sessionId32: ByteArray, privkey: ByteArray?, aggpubkey: ByteArray, msg32: ByteArray?, keyaggCache: ByteArray?, extraInput32: ByteArray?): ByteArray {
|
||||
return Secp256k1CFunctions.secp256k1_musig_nonce_gen(Secp256k1Context.getContext(), sessionId32, privkey, aggpubkey, msg32, keyaggCache, extraInput32)
|
||||
}
|
||||
|
||||
override fun musigNonceAgg(pubnonces: Array<ByteArray>): ByteArray {
|
||||
return Secp256k1CFunctions.secp256k1_musig_nonce_agg(Secp256k1Context.getContext(), pubnonces)
|
||||
}
|
||||
|
||||
override fun musigPubkeyAgg(pubkeys: Array<ByteArray>, keyaggCache: ByteArray?): ByteArray {
|
||||
return Secp256k1CFunctions.secp256k1_musig_pubkey_agg(Secp256k1Context.getContext(), pubkeys, keyaggCache)
|
||||
}
|
||||
|
||||
override fun musigPubkeyTweakAdd(keyaggCache: ByteArray, tweak32: ByteArray): ByteArray {
|
||||
return Secp256k1CFunctions.secp256k1_musig_pubkey_ec_tweak_add(Secp256k1Context.getContext(), keyaggCache, tweak32)
|
||||
}
|
||||
|
||||
override fun musigPubkeyXonlyTweakAdd(keyaggCache: ByteArray, tweak32: ByteArray): ByteArray {
|
||||
return Secp256k1CFunctions.secp256k1_musig_pubkey_xonly_tweak_add(Secp256k1Context.getContext(), keyaggCache, tweak32)
|
||||
}
|
||||
|
||||
override fun musigNonceProcess(aggnonce: ByteArray, msg32: ByteArray, keyaggCache: ByteArray): ByteArray {
|
||||
return Secp256k1CFunctions.secp256k1_musig_nonce_process(Secp256k1Context.getContext(), aggnonce, msg32, keyaggCache)
|
||||
}
|
||||
|
||||
override fun musigPartialSign(secnonce: ByteArray, privkey: ByteArray, keyaggCache: ByteArray, session: ByteArray): ByteArray {
|
||||
return Secp256k1CFunctions.secp256k1_musig_partial_sign(Secp256k1Context.getContext(), secnonce, privkey, keyaggCache, session)
|
||||
}
|
||||
|
||||
override fun musigPartialSigVerify(psig: ByteArray, pubnonce: ByteArray, pubkey: ByteArray, keyaggCache: ByteArray, session: ByteArray): Int {
|
||||
return Secp256k1CFunctions.secp256k1_musig_partial_sig_verify(Secp256k1Context.getContext(), psig, pubnonce, pubkey, keyaggCache, session)
|
||||
}
|
||||
|
||||
override fun musigPartialSigAgg(session: ByteArray, psigs: Array<ByteArray>): ByteArray {
|
||||
return Secp256k1CFunctions.secp256k1_musig_partial_sig_agg(Secp256k1Context.getContext(), session, psigs)
|
||||
}
|
||||
|
||||
override fun cleanup() {
|
||||
return Secp256k1CFunctions.secp256k1_context_destroy(Secp256k1Context.getContext())
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ export STRIP=$ANDROID_NDK/toolchains/llvm/prebuilt/$TOOLCHAIN/bin/llvm-strip
|
||||
cd secp256k1
|
||||
|
||||
./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
|
||||
|
||||
|
||||
@@ -6,13 +6,10 @@ cp xconfigure.sh secp256k1
|
||||
cd secp256k1
|
||||
|
||||
./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
|
||||
cp -v _build/universal/ios/* ../build/ios/
|
||||
|
||||
mkdir -p ../build/iosSimulatorArm64
|
||||
cp -v _build/universal/iosSimulatorArm64/* ../build/iosSimulatorArm64/
|
||||
cp -v _build/universal/* ../build/ios/
|
||||
|
||||
rm -rf _build
|
||||
make clean
|
||||
|
||||
@@ -12,20 +12,18 @@ cd "$(dirname "$0")"
|
||||
cd secp256k1
|
||||
|
||||
if [ "$TARGET" == "mingw" ]; then
|
||||
CFLAGS="-fPIC"
|
||||
CONF_OPTS=" --host=x86_64-w64-mingw32"
|
||||
CONF_OPTS="CFLAGS=-fPIC --host=x86_64-w64-mingw32"
|
||||
elif [ "$TARGET" == "linux" ]; then
|
||||
CFLAGS="-fPIC"
|
||||
CONF_OPTS="CFLAGS=-fPIC"
|
||||
elif [ "$TARGET" == "darwin" ]; then
|
||||
CFLAGS="-arch arm64 -arch x86_64"
|
||||
LDFLAGS="-arch arm64 -arch x86_64"
|
||||
CONF_OPTS=""
|
||||
else
|
||||
echo "Unknown TARGET=$TARGET"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
./autogen.sh
|
||||
CFLAGS="$CFLAGS" LDFLAGS="$LDFLAGS" ./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
|
||||
|
||||
|
||||
Submodule native/secp256k1 updated: dd4932b67b...199d27cea3
@@ -69,22 +69,9 @@ HOST_FLAGS="${ARCH_FLAGS} -mios-simulator-version-min=${MIN_IOS_VERSION} -isysro
|
||||
CHOST="x86_64-apple-darwin"
|
||||
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
|
||||
cd "${PLATFORMS}/${PLATFORM_ARM}/lib"
|
||||
LIB_NAME=`find . -iname *.a`
|
||||
cd -
|
||||
mkdir -p "${UNIVERSAL}/ios" &> /dev/null
|
||||
mkdir -p "${UNIVERSAL}/iosSimulatorArm64" &> /dev/null
|
||||
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}"
|
||||
mkdir -p "${UNIVERSAL}" &> /dev/null
|
||||
lipo -create -output "${UNIVERSAL}/${LIB_NAME}" "${PLATFORMS}/${PLATFORM_ARM}/lib/${LIB_NAME}" "${PLATFORMS}/${PLATFORM_ISIM}/lib/${LIB_NAME}"
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
#!/bin/bash -x
|
||||
|
||||
if [[ -z "${VERSION}" ]]; then
|
||||
echo "VERSION is not defined"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ $# -eq 0 ]
|
||||
then
|
||||
echo "specify either snapshot or release"
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
#!/bin/bash -x
|
||||
|
||||
if [[ -z "${VERSION}" ]]; then
|
||||
echo "VERSION is not defined"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ $# -eq 0 ]
|
||||
then
|
||||
echo "specify either snapshot or release"
|
||||
|
||||
@@ -21,52 +21,45 @@ mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/conte
|
||||
-Djavadoc=$ARTIFACT_ID_BASE-$VERSION-javadoc.jar
|
||||
popd
|
||||
pushd .
|
||||
for i in iosarm64 iossimulatorarm64 iosx64 macosarm64 macosx64 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
|
||||
|
||||
case $i in
|
||||
iosarm64 | iossimulatorarm64 | iosx64 | macosarm64 | macosx64)
|
||||
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
|
||||
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
|
||||
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.klib \
|
||||
-Dfiles=$ARTIFACT_ID_BASE-$i-$VERSION-metadata.jar,$ARTIFACT_ID_BASE-$i-$VERSION.module,$ARTIFACT_ID_BASE-$i-$VERSION-cinterop-libsecp256k1.klib \
|
||||
-Dtypes=jar,module,klib \
|
||||
-Dclassifiers=metadata,,cinterop-libsecp256k1 \
|
||||
-Dsources=$ARTIFACT_ID_BASE-$i-$VERSION-sources.jar \
|
||||
-Djavadoc=$ARTIFACT_ID_BASE-$i-$VERSION-javadoc.jar
|
||||
;;
|
||||
linuxx64)
|
||||
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
|
||||
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
|
||||
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.klib \
|
||||
-Dfiles=$ARTIFACT_ID_BASE-$i-$VERSION.module,$ARTIFACT_ID_BASE-$i-$VERSION-cinterop-libsecp256k1.klib \
|
||||
-Dtypes=module,klib \
|
||||
-Dclassifiers=,cinterop-libsecp256k1 \
|
||||
-Dsources=$ARTIFACT_ID_BASE-$i-$VERSION-sources.jar \
|
||||
-Djavadoc=$ARTIFACT_ID_BASE-$i-$VERSION-javadoc.jar
|
||||
;;
|
||||
jni-android)
|
||||
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
|
||||
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
|
||||
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.aar \
|
||||
-Dfiles=$ARTIFACT_ID_BASE-$i-$VERSION.module \
|
||||
-Dtypes=module \
|
||||
-Dclassifiers= \
|
||||
-Dsources=$ARTIFACT_ID_BASE-$i-$VERSION-sources.jar \
|
||||
-Djavadoc=$ARTIFACT_ID_BASE-$i-$VERSION-javadoc.jar
|
||||
;;
|
||||
*)
|
||||
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
|
||||
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
|
||||
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.jar \
|
||||
-Dfiles=$ARTIFACT_ID_BASE-$i-$VERSION.module \
|
||||
-Dtypes=module \
|
||||
-Dclassifiers= \
|
||||
-Dsources=$ARTIFACT_ID_BASE-$i-$VERSION-sources.jar \
|
||||
-Djavadoc=$ARTIFACT_ID_BASE-$i-$VERSION-javadoc.jar
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ $i == iosarm64 ] || [ $i == iosx64 ]; then
|
||||
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
|
||||
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
|
||||
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.klib \
|
||||
-Dfiles=$ARTIFACT_ID_BASE-$i-$VERSION-metadata.jar,$ARTIFACT_ID_BASE-$i-$VERSION.module,$ARTIFACT_ID_BASE-$i-$VERSION-cinterop-libsecp256k1.klib \
|
||||
-Dtypes=jar,module,klib \
|
||||
-Dclassifiers=metadata,,cinterop-libsecp256k1 \
|
||||
-Dsources=$ARTIFACT_ID_BASE-$i-$VERSION-sources.jar \
|
||||
-Djavadoc=$ARTIFACT_ID_BASE-$i-$VERSION-javadoc.jar
|
||||
elif [ $i == linux ]; then
|
||||
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
|
||||
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
|
||||
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.klib \
|
||||
-Dfiles=$ARTIFACT_ID_BASE-$i-$VERSION.module,$ARTIFACT_ID_BASE-$i-$VERSION-cinterop-libsecp256k1.klib \
|
||||
-Dtypes=module,klib \
|
||||
-Dclassifiers=,cinterop-libsecp256k1 \
|
||||
-Dsources=$ARTIFACT_ID_BASE-$i-$VERSION-sources.jar \
|
||||
-Djavadoc=$ARTIFACT_ID_BASE-$i-$VERSION-javadoc.jar
|
||||
elif [ $i == jni-android ]; then
|
||||
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
|
||||
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
|
||||
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.aar \
|
||||
-Dfiles=$ARTIFACT_ID_BASE-$i-$VERSION.module \
|
||||
-Dtypes=module \
|
||||
-Dclassifiers= \
|
||||
-Dsources=$ARTIFACT_ID_BASE-$i-$VERSION-sources.jar \
|
||||
-Djavadoc=$ARTIFACT_ID_BASE-$i-$VERSION-javadoc.jar
|
||||
else
|
||||
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
|
||||
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
|
||||
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.jar \
|
||||
-Dfiles=$ARTIFACT_ID_BASE-$i-$VERSION.module \
|
||||
-Dtypes=module \
|
||||
-Dclassifiers= \
|
||||
-Dsources=$ARTIFACT_ID_BASE-$i-$VERSION-sources.jar \
|
||||
-Djavadoc=$ARTIFACT_ID_BASE-$i-$VERSION-javadoc.jar
|
||||
fi
|
||||
popd
|
||||
pushd .
|
||||
done
|
||||
|
||||
@@ -3,22 +3,9 @@
|
||||
# first you must sign all files:
|
||||
# find release -type f -print -exec gpg -ab {} \;
|
||||
|
||||
if [[ -z "${VERSION}" ]]; then
|
||||
echo "VERSION is not defined"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "${OSS_USER}" ]]; then
|
||||
echo "OSS_USER is not defined"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
read -p "Password : " -s OSS_PASSWORD
|
||||
|
||||
|
||||
VERSION=0.6.2
|
||||
for i in secp256k1-kmp \
|
||||
secp256k1-kmp-iosarm64 \
|
||||
secp256k1-kmp-iossimulatorarm64 \
|
||||
secp256k1-kmp-iosx64 \
|
||||
secp256k1-kmp-jni-android \
|
||||
secp256k1-kmp-jni-common \
|
||||
@@ -28,14 +15,14 @@ for i in secp256k1-kmp \
|
||||
secp256k1-kmp-jni-jvm-linux \
|
||||
secp256k1-kmp-jni-jvm-mingw \
|
||||
secp256k1-kmp-jvm \
|
||||
secp256k1-kmp-linuxx64
|
||||
secp256k1-kmp-linux
|
||||
do
|
||||
pushd .
|
||||
cd release/fr/acinq/secp256k1/$i/$VERSION
|
||||
pwd
|
||||
jar -cvf bundle.jar *
|
||||
# use correct sonatype credentials here
|
||||
curl -v -XPOST -u $OSS_USER:$OSS_PASSWORD --upload-file bundle.jar https://oss.sonatype.org/service/local/staging/bundle_upload
|
||||
curl -v -XPOST -u USER:PASSWORD --upload-file bundle.jar https://oss.sonatype.org/service/local/staging/bundle_upload
|
||||
popd
|
||||
done
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ public interface Secp256k1 {
|
||||
*/
|
||||
public fun signSchnorr(data: ByteArray, sec: ByteArray, auxrand32: ByteArray?): ByteArray
|
||||
|
||||
/**
|
||||
/**
|
||||
* Convert an ECDSA signature to a normalized lower-S form (bitcoin standardness rule).
|
||||
* Returns the normalized signature and a boolean set to true if the input signature was not normalized.
|
||||
*
|
||||
@@ -149,108 +149,10 @@ public interface Secp256k1 {
|
||||
compressed[0] = if (pubkey.last() % 2 == 0) 2.toByte() else 3.toByte()
|
||||
compressed
|
||||
}
|
||||
|
||||
else -> throw Secp256k1Exception("invalid public key")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a secret nonce to be used in a musig2 signing session.
|
||||
* This nonce must never be persisted or reused across signing sessions.
|
||||
* All optional arguments exist to enrich the quality of the randomness used, which is critical for security.
|
||||
*
|
||||
* @param sessionId32 unique 32-byte session ID.
|
||||
* @param privkey (optional) signer's private key.
|
||||
* @param aggpubkey aggregated public key of all participants in the signing session.
|
||||
* @param msg32 (optional) 32-byte message that will be signed, if already known.
|
||||
* @param keyaggCache (optional) key aggregation cache data from the signing session.
|
||||
* @param extraInput32 (optional) additional 32-byte random data.
|
||||
* @return serialized version of the secret nonce and the corresponding public nonce.
|
||||
*/
|
||||
public fun musigNonceGen(sessionId32: ByteArray, privkey: ByteArray?, aggpubkey: ByteArray, msg32: ByteArray?, keyaggCache: ByteArray?, extraInput32: ByteArray?): ByteArray
|
||||
|
||||
/**
|
||||
* Aggregate public nonces from all participants of a signing session.
|
||||
*
|
||||
* @param pubnonces public nonces (one per participant).
|
||||
* @return 66-byte aggregate public nonce (two public keys) or throws an exception is a nonce is invalid.
|
||||
*/
|
||||
public fun musigNonceAgg(pubnonces: Array<ByteArray>): ByteArray
|
||||
|
||||
/**
|
||||
* Aggregate public keys from all participants of a signing session.
|
||||
*
|
||||
* @param pubkeys public keys of all participants in the signing session.
|
||||
* @param keyaggCache (optional) key aggregation cache data from the signing session. If an empty byte array is
|
||||
* provided, it will be filled with key aggregation data that can be used for the next steps of the signing process.
|
||||
* @return 32-byte x-only public key.
|
||||
*/
|
||||
public fun musigPubkeyAgg(pubkeys: Array<ByteArray>, keyaggCache: ByteArray?): ByteArray
|
||||
|
||||
/**
|
||||
* Tweak the aggregated public key of a signing session.
|
||||
*
|
||||
* @param keyaggCache key aggregation cache filled by [musigPubkeyAgg].
|
||||
* @param tweak32 private key tweak to apply.
|
||||
* @return P + tweak32 * G (where P is the aggregated public key from [keyaggCache]). The key aggregation cache will
|
||||
* be updated with the tweaked public key.
|
||||
*/
|
||||
public fun musigPubkeyTweakAdd(keyaggCache: ByteArray, tweak32: ByteArray): ByteArray
|
||||
|
||||
/**
|
||||
* Tweak the aggregated public key of a signing session, treating it as an x-only public key (e.g. when using taproot).
|
||||
*
|
||||
* @param keyaggCache key aggregation cache filled by [musigPubkeyAgg].
|
||||
* @param tweak32 private key tweak to apply.
|
||||
* @return with_even_y(P) + tweak32 * G (where P is the aggregated public key from [keyaggCache]). The key aggregation
|
||||
* cache will be updated with the tweaked public key.
|
||||
*/
|
||||
public fun musigPubkeyXonlyTweakAdd(keyaggCache: ByteArray, tweak32: ByteArray): ByteArray
|
||||
|
||||
/**
|
||||
* Create a signing session context based on the public information from all participants.
|
||||
*
|
||||
* @param aggnonce aggregated public nonce (see [musigNonceAgg]).
|
||||
* @param msg32 32-byte message that will be signed.
|
||||
* @param keyaggCache aggregated public key cache filled by calling [musigPubkeyAgg] with the public keys of all participants.
|
||||
* @return signing session context that can be used to create partial signatures and aggregate them.
|
||||
*/
|
||||
public fun musigNonceProcess(aggnonce: ByteArray, msg32: ByteArray, keyaggCache: ByteArray): ByteArray
|
||||
|
||||
/**
|
||||
* Create a partial signature.
|
||||
*
|
||||
* @param secnonce signer's secret nonce (see [musigNonceGen]).
|
||||
* @param privkey signer's private key.
|
||||
* @param keyaggCache aggregated public key cache filled by calling [musigPubkeyAgg] with the public keys of all participants.
|
||||
* @param session signing session context (see [musigNonceProcess]).
|
||||
* @return 32-byte partial signature.
|
||||
*/
|
||||
public fun musigPartialSign(secnonce: ByteArray, privkey: ByteArray, keyaggCache: ByteArray, session: ByteArray): ByteArray
|
||||
|
||||
/**
|
||||
* Verify the partial signature from one of the signing session's participants.
|
||||
*
|
||||
* @param psig 32-byte partial signature.
|
||||
* @param pubnonce individual public nonce of the signing participant.
|
||||
* @param pubkey individual public key of the signing participant.
|
||||
* @param keyaggCache aggregated public key cache filled by calling [musigPubkeyAgg] with the public keys of all participants.
|
||||
* @param session signing session context (see [musigNonceProcess]).
|
||||
* @return result code (1 if the partial signature is valid, 0 otherwise).
|
||||
*/
|
||||
public fun musigPartialSigVerify(psig: ByteArray, pubnonce: ByteArray, pubkey: ByteArray, keyaggCache: ByteArray, session: ByteArray): Int
|
||||
|
||||
/**
|
||||
* Aggregate partial signatures from all participants into a single schnorr signature. If some of the partial
|
||||
* signatures are invalid, this function will return an invalid aggregated signature without raising an error.
|
||||
* It is recommended to use [musigPartialSigVerify] to verify partial signatures first.
|
||||
*
|
||||
* @param session signing session context (see [musigNonceProcess]).
|
||||
* @param psigs list of 32-byte partial signatures.
|
||||
* @return 64-byte aggregated schnorr signature.
|
||||
*/
|
||||
public fun musigPartialSigAgg(session: ByteArray, psigs: Array<ByteArray>): ByteArray
|
||||
|
||||
/**
|
||||
* Delete the secp256k1 context from dynamic memory.
|
||||
*/
|
||||
@@ -259,19 +161,22 @@ public interface Secp256k1 {
|
||||
public companion object : Secp256k1 by getSecpk256k1() {
|
||||
@JvmStatic
|
||||
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
|
||||
|
||||
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(message: String?) : super(message)
|
||||
}
|
||||
@@ -1,22 +1,10 @@
|
||||
package = secp256k1
|
||||
|
||||
headers = secp256k1.h secp256k1_ecdh.h secp256k1_recovery.h secp256k1_extrakeys.h secp256k1_schnorrsig.h secp256k1_musig.h
|
||||
headerFilter = secp256k1/** secp256k1_ecdh.h secp256k1_recovery.h secp256k1_extrakeys.h secp256k1_schnorrsig.h secp256k1_musig.h secp256k1.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.h
|
||||
|
||||
staticLibraries.linux = libsecp256k1.a
|
||||
libraryPaths.linux = c/secp256k1/build/linux/ native/build/linux/ native/build/darwin/
|
||||
libraryPaths.linux = c/secp256k1/build/linux/
|
||||
linkerOpts.linux = -L/usr/lib64 -L/usr/lib/x86_64-linux-gnu -L/usr/local/lib
|
||||
|
||||
staticLibraries.macos_x64 = libsecp256k1.a
|
||||
libraryPaths.macos_x64 = c/secp256k1/build/darwin/ native/build/darwin/
|
||||
linkerOpts.macos_x64 = -framework Security -framework Foundation
|
||||
|
||||
staticLibraries.macos_arm64 = libsecp256k1.a
|
||||
libraryPaths.macos_arm64 = c/secp256k1/build/darwin/ native/build/darwin/
|
||||
linkerOpts.macos_arm64 = -framework Security -framework Foundation
|
||||
|
||||
staticLibraries.ios = libsecp256k1.a
|
||||
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/
|
||||
libraryPaths.ios = c/secp256k1/build/ios/ /usr/local/lib
|
||||
linkerOpts.ios = -framework Security -framework Foundation
|
||||
|
||||
@@ -1,22 +1,70 @@
|
||||
package fr.acinq.secp256k1
|
||||
|
||||
import kotlinx.cinterop.*
|
||||
import platform.posix.memcpy
|
||||
import platform.posix.size_tVar
|
||||
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 {
|
||||
|
||||
private val ctx: CPointer<secp256k1_context> by lazy {
|
||||
val c = 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")
|
||||
val callback = staticCFunction { _: CPointer<ByteVar>?, _: COpaquePointer? -> }
|
||||
secp256k1_context_set_illegal_callback(c, callback, null)
|
||||
c
|
||||
}
|
||||
|
||||
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 {
|
||||
val sig = alloc<secp256k1_ecdsa_signature>()
|
||||
@@ -44,20 +92,6 @@ public object Secp256k1Native : Secp256k1 {
|
||||
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 {
|
||||
val serialized = allocArray<UByteVar>(65)
|
||||
val outputLen = alloc<size_tVar>()
|
||||
@@ -66,24 +100,6 @@ public object Secp256k1Native : Secp256k1 {
|
||||
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> {
|
||||
val ubytes = bytes.asUByteArray()
|
||||
val pinned = ubytes.pin()
|
||||
@@ -94,173 +110,209 @@ public object Secp256k1Native : Secp256k1 {
|
||||
public override fun verify(signature: ByteArray, message: ByteArray, pubkey: ByteArray): Boolean {
|
||||
require(message.size == 32)
|
||||
require(pubkey.size == 33 || pubkey.size == 65)
|
||||
memScoped {
|
||||
val nPubkey = allocPublicKey(pubkey)
|
||||
val nMessage = toNat(message)
|
||||
val nSig = allocSignature(signature)
|
||||
return secp256k1_ecdsa_verify(ctx, nSig.ptr, nMessage, nPubkey.ptr) == 1
|
||||
CallbackHandler(ctx).use { callbackHandler ->
|
||||
memScoped {
|
||||
val nPubkey = allocPublicKey(pubkey)
|
||||
val nMessage = toNat(message)
|
||||
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 {
|
||||
require(privkey.size == 32)
|
||||
require(message.size == 32)
|
||||
memScoped {
|
||||
val nPrivkey = toNat(privkey)
|
||||
val nMessage = toNat(message)
|
||||
val nSig = alloc<secp256k1_ecdsa_signature>()
|
||||
secp256k1_ecdsa_sign(ctx, nSig.ptr, nMessage, nPrivkey, null, null).requireSuccess("secp256k1_ecdsa_sign() failed")
|
||||
return serializeSignature(nSig)
|
||||
CallbackHandler(ctx).use { callbackHandler ->
|
||||
memScoped {
|
||||
val nPrivkey = toNat(privkey)
|
||||
val nMessage = toNat(message)
|
||||
val nSig = alloc<secp256k1_ecdsa_signature>()
|
||||
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> {
|
||||
require(sig.size >= 64){ "invalid signature ${Hex.encode(sig)}" }
|
||||
memScoped {
|
||||
val nSig = allocSignature(sig)
|
||||
val isHighS = secp256k1_ecdsa_signature_normalize(ctx, nSig.ptr, nSig.ptr)
|
||||
return Pair(serializeSignature(nSig), isHighS == 1)
|
||||
require(sig.size >= 64) { "invalid signature ${Hex.encode(sig)}" }
|
||||
CallbackHandler(ctx).use { callbackHandler ->
|
||||
memScoped {
|
||||
val nSig = allocSignature(sig)
|
||||
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 {
|
||||
if (privkey.size != 32) return false
|
||||
memScoped {
|
||||
val nPrivkey = toNat(privkey)
|
||||
return secp256k1_ec_seckey_verify(ctx, nPrivkey) == 1
|
||||
CallbackHandler(ctx).use { callbackHandler ->
|
||||
memScoped {
|
||||
val nPrivkey = toNat(privkey)
|
||||
val result = secp256k1_ec_seckey_verify(ctx, nPrivkey) == 1
|
||||
callbackHandler.checkForErrors()
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override fun pubkeyCreate(privkey: ByteArray): ByteArray {
|
||||
require(privkey.size == 32)
|
||||
memScoped {
|
||||
val nPrivkey = toNat(privkey)
|
||||
val nPubkey = alloc<secp256k1_pubkey>()
|
||||
secp256k1_ec_pubkey_create(ctx, nPubkey.ptr, nPrivkey).requireSuccess("secp256k1_ec_pubkey_create() failed")
|
||||
return serializePubkey(nPubkey)
|
||||
CallbackHandler(ctx).use { callbackHandler ->
|
||||
memScoped {
|
||||
val nPrivkey = toNat(privkey)
|
||||
val nPubkey = alloc<secp256k1_pubkey>()
|
||||
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 {
|
||||
require(pubkey.size == 33 || pubkey.size == 65)
|
||||
memScoped {
|
||||
val nPubkey = allocPublicKey(pubkey)
|
||||
return serializePubkey(nPubkey)
|
||||
CallbackHandler(ctx).use { callbackHandler ->
|
||||
memScoped {
|
||||
val nPubkey = allocPublicKey(pubkey)
|
||||
val result = serializePubkey(nPubkey)
|
||||
callbackHandler.checkForErrors()
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override fun privKeyNegate(privkey: ByteArray): ByteArray {
|
||||
require(privkey.size == 32)
|
||||
memScoped {
|
||||
val negated = privkey.copyOf()
|
||||
val negPriv = toNat(negated)
|
||||
secp256k1_ec_seckey_negate(ctx, negPriv).requireSuccess("secp256k1_ec_seckey_negate() failed")
|
||||
return negated
|
||||
CallbackHandler(ctx).use { callbackHandler ->
|
||||
memScoped {
|
||||
val negated = privkey.copyOf()
|
||||
val negPriv = toNat(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 {
|
||||
require(privkey.size == 32)
|
||||
require(tweak.size == 32)
|
||||
memScoped {
|
||||
val added = privkey.copyOf()
|
||||
val natAdd = toNat(added)
|
||||
val natTweak = toNat(tweak)
|
||||
secp256k1_ec_seckey_tweak_add(ctx, natAdd, natTweak).requireSuccess("secp256k1_ec_seckey_tweak_add() failed")
|
||||
return added
|
||||
CallbackHandler(ctx).use { callbackHandler ->
|
||||
memScoped {
|
||||
val added = privkey.copyOf()
|
||||
val natAdd = toNat(added)
|
||||
val natTweak = toNat(tweak)
|
||||
secp256k1_ec_seckey_tweak_add(ctx, natAdd, natTweak).requireSuccess(callbackHandler, "secp256k1_ec_seckey_tweak_add() failed")
|
||||
return added
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override fun privKeyTweakMul(privkey: ByteArray, tweak: ByteArray): ByteArray {
|
||||
require(privkey.size == 32)
|
||||
require(tweak.size == 32)
|
||||
memScoped {
|
||||
val multiplied = privkey.copyOf()
|
||||
val natMul = toNat(multiplied)
|
||||
val natTweak = toNat(tweak)
|
||||
secp256k1_ec_privkey_tweak_mul(ctx, natMul, natTweak).requireSuccess("secp256k1_ec_privkey_tweak_mul() failed")
|
||||
return multiplied
|
||||
CallbackHandler(ctx).use { callbackHandler ->
|
||||
memScoped {
|
||||
val multiplied = privkey.copyOf()
|
||||
val natMul = toNat(multiplied)
|
||||
val natTweak = toNat(tweak)
|
||||
secp256k1_ec_privkey_tweak_mul(ctx, natMul, natTweak).requireSuccess(callbackHandler, "secp256k1_ec_privkey_tweak_mul() failed")
|
||||
return multiplied
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override fun pubKeyNegate(pubkey: ByteArray): ByteArray {
|
||||
require(pubkey.size == 33 || pubkey.size == 65)
|
||||
memScoped {
|
||||
val nPubkey = allocPublicKey(pubkey)
|
||||
secp256k1_ec_pubkey_negate(ctx, nPubkey.ptr).requireSuccess("secp256k1_ec_pubkey_negate() failed")
|
||||
return serializePubkey(nPubkey)
|
||||
CallbackHandler(ctx).use { callbackHandler ->
|
||||
memScoped {
|
||||
val nPubkey = allocPublicKey(pubkey)
|
||||
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 {
|
||||
require(pubkey.size == 33 || pubkey.size == 65)
|
||||
require(tweak.size == 32)
|
||||
memScoped {
|
||||
val nPubkey = allocPublicKey(pubkey)
|
||||
val nTweak = toNat(tweak)
|
||||
secp256k1_ec_pubkey_tweak_add(ctx, nPubkey.ptr, nTweak).requireSuccess("secp256k1_ec_pubkey_tweak_add() failed")
|
||||
return serializePubkey(nPubkey)
|
||||
CallbackHandler(ctx).use { callbackHandler ->
|
||||
memScoped {
|
||||
val nPubkey = allocPublicKey(pubkey)
|
||||
val nTweak = toNat(tweak)
|
||||
secp256k1_ec_pubkey_tweak_add(ctx, nPubkey.ptr, nTweak).requireSuccess(callbackHandler, "secp256k1_ec_pubkey_tweak_add() failed")
|
||||
return serializePubkey(nPubkey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override fun pubKeyTweakMul(pubkey: ByteArray, tweak: ByteArray): ByteArray {
|
||||
require(pubkey.size == 33 || pubkey.size == 65)
|
||||
require(tweak.size == 32)
|
||||
memScoped {
|
||||
val nPubkey = allocPublicKey(pubkey)
|
||||
val nTweak = toNat(tweak)
|
||||
secp256k1_ec_pubkey_tweak_mul(ctx, nPubkey.ptr, nTweak).requireSuccess("secp256k1_ec_pubkey_tweak_mul() failed")
|
||||
return serializePubkey(nPubkey)
|
||||
CallbackHandler(ctx).use { callbackHandler ->
|
||||
memScoped {
|
||||
val nPubkey = allocPublicKey(pubkey)
|
||||
val nTweak = toNat(tweak)
|
||||
secp256k1_ec_pubkey_tweak_mul(ctx, nPubkey.ptr, nTweak).requireSuccess(callbackHandler, "secp256k1_ec_pubkey_tweak_mul() failed")
|
||||
return serializePubkey(nPubkey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override fun pubKeyCombine(pubkeys: Array<ByteArray>): ByteArray {
|
||||
require(pubkeys.isNotEmpty())
|
||||
pubkeys.forEach { require(it.size == 33 || it.size == 65) }
|
||||
memScoped {
|
||||
val nPubkeys = pubkeys.map { allocPublicKey(it).ptr }
|
||||
val combined = alloc<secp256k1_pubkey>()
|
||||
secp256k1_ec_pubkey_combine(ctx, combined.ptr, nPubkeys.toCValues(), pubkeys.size.convert()).requireSuccess("secp256k1_ec_pubkey_combine() failed")
|
||||
return serializePubkey(combined)
|
||||
CallbackHandler(ctx).use { callbackHandler ->
|
||||
memScoped {
|
||||
val nPubkeys = pubkeys.map { allocPublicKey(it).ptr }
|
||||
val combined = alloc<secp256k1_pubkey>()
|
||||
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 {
|
||||
require(privkey.size == 32)
|
||||
require(pubkey.size == 33 || pubkey.size == 65)
|
||||
memScoped {
|
||||
val nPubkey = allocPublicKey(pubkey)
|
||||
val nPrivkey = toNat(privkey)
|
||||
val output = allocArray<UByteVar>(32)
|
||||
secp256k1_ecdh(ctx, output, nPubkey.ptr, nPrivkey, null, null).requireSuccess("secp256k1_ecdh() failed")
|
||||
return output.readBytes(32)
|
||||
CallbackHandler(ctx).use { callbackHandler ->
|
||||
memScoped {
|
||||
val nPubkey = allocPublicKey(pubkey)
|
||||
val nPrivkey = toNat(privkey)
|
||||
val output = allocArray<UByteVar>(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 {
|
||||
require(sig.size == 64)
|
||||
require(message.size == 32)
|
||||
require(recid in 0..3)
|
||||
memScoped {
|
||||
val nSig = toNat(sig)
|
||||
val rSig = alloc<secp256k1_ecdsa_recoverable_signature>()
|
||||
secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, rSig.ptr, nSig, recid).requireSuccess("secp256k1_ecdsa_recoverable_signature_parse_compact() failed")
|
||||
val nMessage = toNat(message)
|
||||
val pubkey = alloc<secp256k1_pubkey>()
|
||||
secp256k1_ecdsa_recover(ctx, pubkey.ptr, rSig.ptr, nMessage).requireSuccess("secp256k1_ecdsa_recover() failed")
|
||||
return serializePubkey(pubkey)
|
||||
// we do not check that recid is valid, which should trigger our illegal callback handler to throw a Secp256k1IllegalCallbackException
|
||||
// require(recid in 0..3)
|
||||
|
||||
CallbackHandler(ctx).use { callbackHandler ->
|
||||
memScoped {
|
||||
val nSig = toNat(sig)
|
||||
val rSig = alloc<secp256k1_ecdsa_recoverable_signature>()
|
||||
secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, rSig.ptr, nSig, recid).requireSuccess(callbackHandler, "secp256k1_ecdsa_recoverable_signature_parse_compact() failed")
|
||||
val nMessage = toNat(message)
|
||||
val 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 {
|
||||
require(sig.size == 64)
|
||||
memScoped {
|
||||
val nSig = allocSignature(sig)
|
||||
val natOutput = allocArray<UByteVar>(73)
|
||||
val len = alloc<size_tVar>()
|
||||
len.value = 73.convert()
|
||||
secp256k1_ecdsa_signature_serialize_der(ctx, natOutput, len.ptr, nSig.ptr).requireSuccess("secp256k1_ecdsa_signature_serialize_der() failed")
|
||||
return natOutput.readBytes(len.value.toInt())
|
||||
CallbackHandler(ctx).use { callbackHandler ->
|
||||
memScoped {
|
||||
val nSig = allocSignature(sig)
|
||||
val natOutput = allocArray<UByteVar>(73)
|
||||
val len = alloc<size_tVar>()
|
||||
len.value = 73.convert()
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,13 +320,15 @@ public object Secp256k1Native : Secp256k1 {
|
||||
require(signature.size == 64)
|
||||
require(data.size == 32)
|
||||
require(pub.size == 32)
|
||||
memScoped {
|
||||
val nPub = toNat(pub)
|
||||
val pubkey = alloc<secp256k1_xonly_pubkey>()
|
||||
secp256k1_xonly_pubkey_parse(ctx, pubkey.ptr, nPub).requireSuccess("secp256k1_xonly_pubkey_parse() failed")
|
||||
val nData = toNat(data)
|
||||
val nSig = toNat(signature)
|
||||
return secp256k1_schnorrsig_verify(ctx, nSig, nData, 32u, pubkey.ptr) == 1
|
||||
CallbackHandler(ctx).use { callbackHandler ->
|
||||
memScoped {
|
||||
val nPub = toNat(pub)
|
||||
val pubkey = alloc<secp256k1_xonly_pubkey>()
|
||||
secp256k1_xonly_pubkey_parse(ctx, pubkey.ptr, nPub).requireSuccess(callbackHandler, "secp256k1_xonly_pubkey_parse() failed")
|
||||
val nData = toNat(data)
|
||||
val nSig = toNat(signature)
|
||||
return secp256k1_schnorrsig_verify(ctx, nSig, nData, 32u, pubkey.ptr) == 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,174 +336,25 @@ public object Secp256k1Native : Secp256k1 {
|
||||
require(sec.size == 32)
|
||||
require(data.size == 32)
|
||||
auxrand32?.let { require(it.size == 32) }
|
||||
memScoped {
|
||||
val nSec = toNat(sec)
|
||||
val nData = toNat(data)
|
||||
val nAuxrand32 = auxrand32?.let { toNat(it) }
|
||||
val nSig = allocArray<UByteVar>(64)
|
||||
val keypair = alloc<secp256k1_keypair>()
|
||||
secp256k1_keypair_create(ctx, keypair.ptr, nSec).requireSuccess("secp256k1_keypair_create() failed")
|
||||
secp256k1_schnorrsig_sign32(ctx, nSig, nData, keypair.ptr, nAuxrand32).requireSuccess("secp256k1_ecdsa_sign() failed")
|
||||
return nSig.readBytes(64)
|
||||
}
|
||||
}
|
||||
|
||||
override fun musigNonceGen(sessionId32: ByteArray, privkey: ByteArray?, aggpubkey: ByteArray, msg32: ByteArray?, keyaggCache: ByteArray?, extraInput32: ByteArray?): ByteArray {
|
||||
require(sessionId32.size == 32)
|
||||
privkey?.let { require(it.size == 32) }
|
||||
msg32?.let { require(it.size == 32) }
|
||||
keyaggCache?.let { require(it.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE) }
|
||||
extraInput32?.let { require(it.size == 32) }
|
||||
|
||||
val nonce = memScoped {
|
||||
val secnonce = alloc<secp256k1_musig_secnonce>()
|
||||
val pubnonce = alloc<secp256k1_musig_pubnonce>()
|
||||
val nPubkey = allocPublicKey(aggpubkey)
|
||||
val nKeyAggCache = keyaggCache?.let {
|
||||
val n = alloc<secp256k1_musig_keyagg_cache>()
|
||||
memcpy(n.ptr, toNat(it), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
|
||||
n
|
||||
CallbackHandler(ctx).use { callbackHandler ->
|
||||
memScoped {
|
||||
val nSec = toNat(sec)
|
||||
val nData = toNat(data)
|
||||
val nAuxrand32 = auxrand32?.let { toNat(it) }
|
||||
val nSig = allocArray<UByteVar>(64)
|
||||
val keypair = alloc<secp256k1_keypair>()
|
||||
secp256k1_keypair_create(ctx, keypair.ptr, nSec).requireSuccess(callbackHandler, "secp256k1_keypair_create() failed")
|
||||
secp256k1_schnorrsig_sign32(ctx, nSig, nData, keypair.ptr, nAuxrand32).requireSuccess(callbackHandler, "secp256k1_ecdsa_sign() failed")
|
||||
return nSig.readBytes(64)
|
||||
}
|
||||
secp256k1_musig_nonce_gen(ctx, secnonce.ptr, pubnonce.ptr, toNat(sessionId32), privkey?.let { toNat(it) }, nPubkey.ptr, msg32?.let { toNat(it) },nKeyAggCache?.ptr, extraInput32?.let { toNat(it) }).requireSuccess("secp256k1_musig_nonce_gen() failed")
|
||||
val nPubnonce = allocArray<UByteVar>(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
|
||||
secp256k1_musig_pubnonce_serialize(ctx, nPubnonce, pubnonce.ptr).requireSuccess("secp256k1_musig_pubnonce_serialize failed")
|
||||
secnonce.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 musigPubkeyAgg(pubkeys: Array<ByteArray>, keyaggCache: ByteArray?): ByteArray {
|
||||
require(pubkeys.isNotEmpty())
|
||||
pubkeys.forEach { require(it.size == 33 || it.size == 65) }
|
||||
keyaggCache?.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 = keyaggCache?.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)
|
||||
keyaggCache?.let { blob -> nKeyAggCache?.let { memcpy(toNat(blob), it.ptr, Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong()) } }
|
||||
return agg
|
||||
}
|
||||
}
|
||||
|
||||
override fun musigPubkeyTweakAdd(keyaggCache: ByteArray, tweak32: ByteArray): ByteArray {
|
||||
require(keyaggCache.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
|
||||
require(tweak32.size == 32)
|
||||
memScoped {
|
||||
val nKeyAggCache = alloc<secp256k1_musig_keyagg_cache>()
|
||||
memcpy(nKeyAggCache.ptr, toNat(keyaggCache), 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(keyaggCache), nKeyAggCache.ptr, Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
|
||||
return serializePubkey(nPubkey)
|
||||
}
|
||||
}
|
||||
|
||||
override fun musigPubkeyXonlyTweakAdd(keyaggCache: ByteArray, tweak32: ByteArray): ByteArray {
|
||||
require(keyaggCache.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
|
||||
require(tweak32.size == 32)
|
||||
memScoped {
|
||||
val nKeyAggCache = alloc<secp256k1_musig_keyagg_cache>()
|
||||
memcpy(nKeyAggCache.ptr, toNat(keyaggCache), 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(keyaggCache), nKeyAggCache.ptr, Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
|
||||
return serializePubkey(nPubkey)
|
||||
}
|
||||
}
|
||||
|
||||
override fun musigNonceProcess(aggnonce: ByteArray, msg32: ByteArray, keyaggCache: ByteArray): ByteArray {
|
||||
require(aggnonce.size == Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
|
||||
require(keyaggCache.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
|
||||
require(msg32.size == 32)
|
||||
memScoped {
|
||||
val nKeyAggCache = alloc<secp256k1_musig_keyagg_cache>()
|
||||
memcpy(nKeyAggCache.ptr, toNat(keyaggCache), 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, keyaggCache: ByteArray, session: ByteArray): ByteArray {
|
||||
require(secnonce.size == Secp256k1.MUSIG2_SECRET_NONCE_SIZE)
|
||||
require(privkey.size == 32)
|
||||
require(keyaggCache.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(keyaggCache), 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, keyaggCache: ByteArray, session: ByteArray): Int {
|
||||
require(psig.size == 32)
|
||||
require(pubnonce.size == Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
|
||||
require(pubkey.size == 33 || pubkey.size == 65)
|
||||
require(keyaggCache.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(keyaggCache), 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() {
|
||||
secp256k1_context_destroy(ctx)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
internal actual fun getSecpk256k1(): Secp256k1 = Secp256k1Native
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmTest
|
||||
import org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeHostTest
|
||||
import org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeSimulatorTest
|
||||
|
||||
plugins {
|
||||
kotlin("multiplatform")
|
||||
@@ -23,8 +20,6 @@ kotlin {
|
||||
dependencies {
|
||||
implementation(kotlin("test-common"))
|
||||
implementation(kotlin("test-annotations-common"))
|
||||
implementation("org.kodein.memory:klio-files:0.12.0")
|
||||
api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,14 +36,14 @@ kotlin {
|
||||
}
|
||||
|
||||
if (includeAndroid) {
|
||||
androidTarget {
|
||||
android {
|
||||
compilations.all {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
sourceSets["androidMain"].dependencies {
|
||||
implementation(project(":jni:android"))
|
||||
}
|
||||
sourceSets["androidUnitTest"].dependencies {
|
||||
sourceSets["androidTest"].dependencies {
|
||||
implementation(kotlin("test-junit"))
|
||||
implementation("androidx.test.ext:junit:1.1.2")
|
||||
implementation("androidx.test.espresso:espresso-core:3.3.0")
|
||||
@@ -56,20 +51,17 @@ kotlin {
|
||||
}
|
||||
}
|
||||
|
||||
linuxX64()
|
||||
macosX64()
|
||||
macosArm64()
|
||||
iosX64()
|
||||
iosArm64()
|
||||
iosSimulatorArm64()
|
||||
linuxX64("linux")
|
||||
|
||||
ios()
|
||||
}
|
||||
|
||||
val includeAndroid = System.getProperty("includeAndroid")?.toBoolean() ?: true
|
||||
if (includeAndroid) {
|
||||
extensions.configure<com.android.build.gradle.LibraryExtension>("android") {
|
||||
defaultConfig {
|
||||
compileSdk = 30
|
||||
minSdk = 21
|
||||
compileSdkVersion(30)
|
||||
minSdkVersion(21)
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
@@ -86,26 +78,4 @@ if (includeAndroid) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
tasks.withType<AbstractTestTask> {
|
||||
testLogging {
|
||||
events("passed", "skipped", "failed", "standard_out", "standard_error")
|
||||
showExceptions = true
|
||||
showStackTraces = true
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<KotlinJvmTest> {
|
||||
environment("TEST_RESOURCES_PATH", projectDir.resolve("src/commonTest/resources"))
|
||||
}
|
||||
|
||||
tasks.withType<KotlinNativeHostTest> {
|
||||
environment("TEST_RESOURCES_PATH", projectDir.resolve("src/commonTest/resources"))
|
||||
}
|
||||
|
||||
tasks.withType<KotlinNativeSimulatorTest> {
|
||||
environment("SIMCTL_CHILD_TEST_RESOURCES_PATH", projectDir.resolve("src/commonTest/resources"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,298 +0,0 @@
|
||||
package fr.acinq.secp256k1
|
||||
|
||||
import kotlinx.serialization.json.*
|
||||
import org.kodein.memory.file.FileSystem
|
||||
import org.kodein.memory.file.Path
|
||||
import org.kodein.memory.file.openReadableFile
|
||||
import org.kodein.memory.file.resolve
|
||||
import org.kodein.memory.system.Environment
|
||||
import org.kodein.memory.text.readString
|
||||
import org.kodein.memory.use
|
||||
import kotlin.test.*
|
||||
|
||||
class Musig2Test {
|
||||
fun resourcesDir() =
|
||||
Environment.findVariable("TEST_RESOURCES_PATH")?.let { Path(it) }
|
||||
?: FileSystem.workingDir().resolve("src/commonTest/resources")
|
||||
|
||||
fun readData(filename: String): JsonElement {
|
||||
val file = resourcesDir().resolve(filename)
|
||||
val raw = file.openReadableFile().use { it.readString() }
|
||||
val format = Json { ignoreUnknownKeys = true }
|
||||
return format.parseToJsonElement(raw)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `aggregate public keys`() {
|
||||
val tests = readData("musig2/key_agg_vectors.json")
|
||||
val pubkeys = tests.jsonObject["pubkeys"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
||||
val tweaks = tests.jsonObject["tweaks"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
||||
|
||||
tests.jsonObject["valid_test_cases"]!!.jsonArray.forEach {
|
||||
val keyIndices = it.jsonObject["key_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
||||
val expected = Hex.decode(it.jsonObject["expected"]!!.jsonPrimitive.content)
|
||||
val keyAggCache = ByteArray(Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
|
||||
val aggkey = Secp256k1.musigPubkeyAgg(keyIndices.map { pubkeys[it] }.toTypedArray(), keyAggCache)
|
||||
assertContentEquals(expected, aggkey)
|
||||
}
|
||||
tests.jsonObject["error_test_cases"]!!.jsonArray.forEach {
|
||||
val keyIndices = it.jsonObject["key_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
||||
val tweakIndex = it.jsonObject["tweak_indices"]!!.jsonArray.map { it.jsonPrimitive.int }.firstOrNull()
|
||||
val isXonly = it.jsonObject["is_xonly"]!!.jsonArray.map { it.jsonPrimitive.boolean }
|
||||
when (tweakIndex) {
|
||||
null -> {
|
||||
// One of the public keys is invalid, so key aggregation will fail.
|
||||
// Callers must verify that public keys are valid before aggregating them.
|
||||
assertFails {
|
||||
val keyAggCache = ByteArray(Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
|
||||
Secp256k1.musigPubkeyAgg(keyIndices.map { pubkeys[it] }.toTypedArray(), keyAggCache)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
// The tweak cannot be applied, it would result in an invalid public key.
|
||||
assertFails {
|
||||
val keyAggCache = ByteArray(Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
|
||||
Secp256k1.musigPubkeyAgg(keyIndices.map { pubkeys[it] }.toTypedArray(), keyAggCache)
|
||||
if (isXonly[0])
|
||||
Secp256k1.musigPubkeyXonlyTweakAdd(keyAggCache, tweaks[tweakIndex])
|
||||
else
|
||||
Secp256k1.musigPubkeyTweakAdd(keyAggCache, tweaks[tweakIndex])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Secret nonces in test vectors use a custom encoding. */
|
||||
private fun deserializeSecretNonce(hex: String): ByteArray {
|
||||
val serialized = Hex.decode(hex)
|
||||
require(serialized.size == 97) { "secret nonce from test vector should be serialized using 97 bytes" }
|
||||
// In test vectors, secret nonces are serialized as: <scalar_1> <scalar_2> <compressed_public_key>
|
||||
val compressedPublicKey = serialized.takeLast(33).toByteArray()
|
||||
// We expect secret nonces serialized as: <magic> <scalar_1> <scalar_2> <public_key_x> <public_key_y>
|
||||
// Where we use a different endianness for the public key coordinates than the test vectors.
|
||||
val uncompressedPublicKey = Secp256k1.pubkeyParse(compressedPublicKey)
|
||||
val publicKeyX = uncompressedPublicKey.drop(1).take(32).reversed().toByteArray()
|
||||
val publicKeyY = uncompressedPublicKey.takeLast(32).reversed().toByteArray()
|
||||
val magic = Hex.decode("220EDCF1")
|
||||
return magic + serialized.take(64) + publicKeyX + publicKeyY
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `generate secret nonce`() {
|
||||
val tests = readData("musig2/nonce_gen_vectors.json")
|
||||
tests.jsonObject["test_cases"]!!.jsonArray.forEach {
|
||||
val randprime = Hex.decode(it.jsonObject["rand_"]!!.jsonPrimitive.content)
|
||||
val sk = it.jsonObject["sk"]?.jsonPrimitive?.contentOrNull?.let { Hex.decode(it) }
|
||||
val pk = Hex.decode(it.jsonObject["pk"]!!.jsonPrimitive.content)
|
||||
val keyagg = it.jsonObject["aggpk"]?.jsonPrimitive?.contentOrNull?.let {
|
||||
// The test vectors directly provide an aggregated public key: we must manually create the corresponding
|
||||
// key aggregation cache to correctly test.
|
||||
val agg = ByteArray(1) { 2.toByte() } + Hex.decode(it)
|
||||
val magic = Hex.decode("f4adbbdf")
|
||||
magic + Secp256k1.pubkeyParse(agg).drop(1) + ByteArray(129) { 0x00 }
|
||||
}
|
||||
val msg = it.jsonObject["msg"]?.jsonPrimitive?.contentOrNull?.let { Hex.decode(it) }
|
||||
val extraInput = it.jsonObject["extra_in"]?.jsonPrimitive?.contentOrNull?.let { Hex.decode(it) }
|
||||
val expectedSecnonce = deserializeSecretNonce(it.jsonObject["expected_secnonce"]!!.jsonPrimitive.content)
|
||||
val expectedPubnonce = Hex.decode(it.jsonObject["expected_pubnonce"]!!.jsonPrimitive.content)
|
||||
// secp256k1 only supports signing 32-byte messages (when provided), which excludes some of the test vectors.
|
||||
if (msg == null || msg.size == 32) {
|
||||
val nonce = Secp256k1.musigNonceGen(randprime, sk, pk, msg, keyagg, extraInput)
|
||||
val secnonce = nonce.copyOfRange(0, Secp256k1.MUSIG2_SECRET_NONCE_SIZE)
|
||||
val pubnonce = nonce.copyOfRange(Secp256k1.MUSIG2_SECRET_NONCE_SIZE, Secp256k1.MUSIG2_SECRET_NONCE_SIZE + Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
|
||||
assertContentEquals(expectedPubnonce, pubnonce)
|
||||
assertContentEquals(expectedSecnonce, secnonce)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `aggregate nonces`() {
|
||||
val tests = readData("musig2/nonce_agg_vectors.json")
|
||||
val nonces = tests.jsonObject["pnonces"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
||||
tests.jsonObject["valid_test_cases"]!!.jsonArray.forEach {
|
||||
val nonceIndices = it.jsonObject["pnonce_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
||||
val expected = Hex.decode(it.jsonObject["expected"]!!.jsonPrimitive.content)
|
||||
val agg = Secp256k1.musigNonceAgg(nonceIndices.map { nonces[it] }.toTypedArray())
|
||||
assertNotNull(agg)
|
||||
assertContentEquals(expected, agg)
|
||||
}
|
||||
tests.jsonObject["error_test_cases"]!!.jsonArray.forEach {
|
||||
val nonceIndices = it.jsonObject["pnonce_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
||||
assertFails {
|
||||
Secp256k1.musigNonceAgg(nonceIndices.map { nonces[it] }.toTypedArray())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sign() {
|
||||
val tests = readData("musig2/sign_verify_vectors.json")
|
||||
val sk = Hex.decode(tests.jsonObject["sk"]!!.jsonPrimitive.content)
|
||||
val pubkeys = tests.jsonObject["pubkeys"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
||||
val secnonces = tests.jsonObject["secnonces"]!!.jsonArray.map { deserializeSecretNonce(it.jsonPrimitive.content) }
|
||||
val pnonces = tests.jsonObject["pnonces"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
||||
val aggnonces = tests.jsonObject["aggnonces"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
||||
val msgs = tests.jsonObject["msgs"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
||||
|
||||
tests.jsonObject["valid_test_cases"]!!.jsonArray.forEach {
|
||||
val keyIndices = it.jsonObject["key_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
||||
val nonceIndices = it.jsonObject["nonce_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
||||
val expected = Hex.decode(it.jsonObject["expected"]!!.jsonPrimitive.content)
|
||||
val signerIndex = it.jsonObject["signer_index"]!!.jsonPrimitive.int
|
||||
val messageIndex = it.jsonObject["msg_index"]!!.jsonPrimitive.int
|
||||
val aggnonce = Secp256k1.musigNonceAgg(nonceIndices.map { pnonces[it] }.toTypedArray())
|
||||
assertNotNull(aggnonce)
|
||||
assertContentEquals(aggnonces[it.jsonObject["aggnonce_index"]!!.jsonPrimitive.int], aggnonce)
|
||||
val keyagg = ByteArray(Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
|
||||
Secp256k1.musigPubkeyAgg(keyIndices.map { pubkeys[it] }.toTypedArray(), keyagg)
|
||||
// We only support signing 32-byte messages.
|
||||
if (msgs[messageIndex].size == 32) {
|
||||
val session = Secp256k1.musigNonceProcess(aggnonce, msgs[messageIndex], keyagg)
|
||||
assertNotNull(session)
|
||||
val psig = Secp256k1.musigPartialSign(secnonces[keyIndices[signerIndex]], sk, keyagg, session)
|
||||
assertContentEquals(expected, psig)
|
||||
assertEquals(1, Secp256k1.musigPartialSigVerify(psig, pnonces[nonceIndices[signerIndex]], pubkeys[keyIndices[signerIndex]], keyagg, session))
|
||||
}
|
||||
}
|
||||
tests.jsonObject["verify_fail_test_cases"]!!.jsonArray.forEach {
|
||||
val psig = Hex.decode(it.jsonObject["sig"]!!.jsonPrimitive.content)
|
||||
val keyIndices = it.jsonObject["key_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
||||
val nonceIndices = it.jsonObject["nonce_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
||||
val signerIndex = it.jsonObject["signer_index"]!!.jsonPrimitive.int
|
||||
val messageIndex = it.jsonObject["msg_index"]!!.jsonPrimitive.int
|
||||
if (msgs[messageIndex].size == 32) {
|
||||
val aggnonce = Secp256k1.musigNonceAgg(nonceIndices.map { pnonces[it] }.toTypedArray())
|
||||
assertNotNull(aggnonce)
|
||||
val keyagg = ByteArray(Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
|
||||
Secp256k1.musigPubkeyAgg(keyIndices.map { pubkeys[it] }.toTypedArray(), keyagg)
|
||||
val session = Secp256k1.musigNonceProcess(aggnonce, msgs[messageIndex], keyagg)
|
||||
assertNotNull(session)
|
||||
assertFails {
|
||||
require(Secp256k1.musigPartialSigVerify(psig, pnonces[nonceIndices[signerIndex]], pubkeys[keyIndices[signerIndex]], keyagg, session) == 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `aggregate signatures`() {
|
||||
val tests = readData("musig2/sig_agg_vectors.json")
|
||||
val pubkeys = tests.jsonObject["pubkeys"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
||||
val pnonces = tests.jsonObject["pnonces"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
||||
val tweaks = tests.jsonObject["tweaks"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
||||
val psigs = tests.jsonObject["psigs"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
||||
val msg = Hex.decode(tests.jsonObject["msg"]!!.jsonPrimitive.content)
|
||||
|
||||
tests.jsonObject["valid_test_cases"]!!.jsonArray.forEach {
|
||||
val keyIndices = it.jsonObject["key_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
||||
val nonceIndices = it.jsonObject["nonce_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
||||
val psigIndices = it.jsonObject["psig_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
||||
val expected = Hex.decode(it.jsonObject["expected"]!!.jsonPrimitive.content)
|
||||
val aggnonce = Secp256k1.musigNonceAgg(nonceIndices.map { pnonces[it] }.toTypedArray())
|
||||
assertNotNull(aggnonce)
|
||||
val tweakIndices = it.jsonObject["tweak_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
||||
val isXonly = it.jsonObject["is_xonly"]!!.jsonArray.map { it.jsonPrimitive.boolean }
|
||||
assertContentEquals(Hex.decode(it.jsonObject["aggnonce"]!!.jsonPrimitive.content), aggnonce)
|
||||
val keyagg = ByteArray(Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
|
||||
Secp256k1.musigPubkeyAgg(keyIndices.map { pubkeys[it] }.toTypedArray(), keyagg)
|
||||
tweakIndices
|
||||
.zip(isXonly)
|
||||
.map { tweaks[it.first] to it.second }
|
||||
.forEach {
|
||||
if (it.second)
|
||||
Secp256k1.musigPubkeyXonlyTweakAdd(keyagg, it.first)
|
||||
else
|
||||
Secp256k1.musigPubkeyTweakAdd(keyagg, it.first)
|
||||
}
|
||||
val session = Secp256k1.musigNonceProcess(aggnonce, msg, keyagg)
|
||||
val aggsig = Secp256k1.musigPartialSigAgg(session, psigIndices.map { psigs[it] }.toTypedArray())
|
||||
assertContentEquals(expected, aggsig)
|
||||
}
|
||||
tests.jsonObject["error_test_cases"]!!.jsonArray.forEach {
|
||||
val keyIndices = it.jsonObject["key_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
||||
val nonceIndices = it.jsonObject["nonce_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
||||
val psigIndices = it.jsonObject["psig_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
||||
val aggnonce = Secp256k1.musigNonceAgg(nonceIndices.map { pnonces[it] }.toTypedArray())
|
||||
assertNotNull(aggnonce)
|
||||
val tweakIndices = it.jsonObject["tweak_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
||||
val isXonly = it.jsonObject["is_xonly"]!!.jsonArray.map { it.jsonPrimitive.boolean }
|
||||
assertContentEquals(Hex.decode(it.jsonObject["aggnonce"]!!.jsonPrimitive.content), aggnonce)
|
||||
val keyagg = ByteArray(Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
|
||||
Secp256k1.musigPubkeyAgg(keyIndices.map { pubkeys[it] }.toTypedArray(), keyagg)
|
||||
tweakIndices
|
||||
.zip(isXonly)
|
||||
.map { tweaks[it.first] to it.second }
|
||||
.forEach {
|
||||
if (it.second)
|
||||
Secp256k1.musigPubkeyXonlyTweakAdd(keyagg, it.first)
|
||||
else
|
||||
Secp256k1.musigPubkeyTweakAdd(keyagg, it.first)
|
||||
}
|
||||
val session = Secp256k1.musigNonceProcess(aggnonce, msg, keyagg)
|
||||
assertFails {
|
||||
Secp256k1.musigPartialSigAgg(session, psigIndices.map { psigs[it] }.toTypedArray())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `tweak tests`() {
|
||||
val tests = readData("musig2/tweak_vectors.json")
|
||||
val sk = Hex.decode(tests.jsonObject["sk"]!!.jsonPrimitive.content)
|
||||
val pubkeys = tests.jsonObject["pubkeys"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
||||
val pnonces = tests.jsonObject["pnonces"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
||||
val tweaks = tests.jsonObject["tweaks"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
||||
val msg = Hex.decode(tests.jsonObject["msg"]!!.jsonPrimitive.content)
|
||||
|
||||
val secnonce = deserializeSecretNonce(tests.jsonObject["secnonce"]!!.jsonPrimitive.content)
|
||||
val aggnonce = Hex.decode(tests.jsonObject["aggnonce"]!!.jsonPrimitive.content)
|
||||
|
||||
assertContentEquals(aggnonce, Secp256k1.musigNonceAgg(arrayOf(pnonces[0], pnonces[1], pnonces[2])))
|
||||
|
||||
tests.jsonObject["valid_test_cases"]!!.jsonArray.forEach {
|
||||
val keyIndices = it.jsonObject["key_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
||||
val nonceIndices = it.jsonObject["nonce_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
||||
val expected = Hex.decode(it.jsonObject["expected"]!!.jsonPrimitive.content)
|
||||
assertContentEquals(aggnonce, Secp256k1.musigNonceAgg(nonceIndices.map { pnonces[it] }.toTypedArray()))
|
||||
val tweakIndices = it.jsonObject["tweak_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
||||
val isXonly = it.jsonObject["is_xonly"]!!.jsonArray.map { it.jsonPrimitive.boolean }
|
||||
val signerIndex = it.jsonObject["signer_index"]!!.jsonPrimitive.int
|
||||
val keyagg = ByteArray(Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
|
||||
Secp256k1.musigPubkeyAgg(keyIndices.map { pubkeys[it] }.toTypedArray(), keyagg)
|
||||
tweakIndices
|
||||
.zip(isXonly)
|
||||
.map { tweaks[it.first] to it.second }
|
||||
.forEach {
|
||||
if (it.second)
|
||||
Secp256k1.musigPubkeyXonlyTweakAdd(keyagg, it.first)
|
||||
else
|
||||
Secp256k1.musigPubkeyTweakAdd(keyagg, it.first)
|
||||
}
|
||||
val session = Secp256k1.musigNonceProcess(aggnonce, msg, keyagg)
|
||||
assertNotNull(session)
|
||||
val psig = Secp256k1.musigPartialSign(secnonce, sk, keyagg, session)
|
||||
assertContentEquals(expected, psig)
|
||||
assertEquals(1, Secp256k1.musigPartialSigVerify(psig, pnonces[nonceIndices[signerIndex]], pubkeys[keyIndices[signerIndex]], keyagg, session))
|
||||
}
|
||||
tests.jsonObject["error_test_cases"]!!.jsonArray.forEach {
|
||||
val keyIndices = it.jsonObject["key_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
||||
val nonceIndices = it.jsonObject["nonce_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
||||
assertContentEquals(aggnonce, Secp256k1.musigNonceAgg(nonceIndices.map { pnonces[it] }.toTypedArray()))
|
||||
val tweakIndices = it.jsonObject["tweak_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
||||
assertEquals(1, tweakIndices.size)
|
||||
val tweak = tweaks[tweakIndices.first()]
|
||||
val isXonly = it.jsonObject["is_xonly"]!!.jsonArray.map { it.jsonPrimitive.boolean }.first()
|
||||
val keyagg = ByteArray(Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
|
||||
Secp256k1.musigPubkeyAgg(keyIndices.map { pubkeys[it] }.toTypedArray(), keyagg)
|
||||
assertFails {
|
||||
if (isXonly)
|
||||
Secp256k1.musigPubkeyXonlyTweakAdd(keyagg, tweak)
|
||||
else
|
||||
Secp256k1.musigPubkeyTweakAdd(keyagg, tweak)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -275,6 +275,11 @@ class Secp256k1Test {
|
||||
val pub0 = Secp256k1.ecdsaRecover(sig, message, 0)
|
||||
val pub1 = Secp256k1.ecdsaRecover(sig, message, 1)
|
||||
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
|
||||
@@ -352,199 +357,6 @@ class Secp256k1Test {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMusig2GenerateNonce() {
|
||||
val privkey = Hex.decode("0000000000000000000000000000000000000000000000000000000000000003")
|
||||
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)
|
||||
assertNotEquals(nonce, Secp256k1.musigNonceGen(sessionId, privkey, pubkey, null, null, null))
|
||||
assertNotEquals(nonce, Secp256k1.musigNonceGen(sessionId, null, pubkey, sessionId, null, null))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMusig2AggregateNonce() {
|
||||
val nonces = listOf(
|
||||
"020151C80F435648DF67A22B749CD798CE54E0321D034B92B709B567D60A42E66603BA47FBC1834437B3212E89A84D8425E7BF12E0245D98262268EBDCB385D50641",
|
||||
"03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B833",
|
||||
"020151C80F435648DF67A22B749CD798CE54E0321D034B92B709B567D60A42E6660279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
|
||||
"03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60379BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
|
||||
// The following nonces are invalid.
|
||||
"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.musigPubkeyAgg(arrayOf(pubkeys[0], pubkeys[1], pubkeys[2]), null)
|
||||
assertEquals("90539EEDE565F5D054F32CC0C220126889ED1E5D193BAF15AEF344FE59D4610C", Hex.encode(agg1).uppercase())
|
||||
|
||||
// We provide an empty cache, which will be filled when aggregating public keys.
|
||||
val keyaggCache = ByteArray(Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
|
||||
val agg2 = Secp256k1.musigPubkeyAgg(arrayOf(pubkeys[0], pubkeys[1], pubkeys[2]), keyaggCache)
|
||||
assertEquals("90539EEDE565F5D054F32CC0C220126889ED1E5D193BAF15AEF344FE59D4610C", Hex.encode(agg2).uppercase())
|
||||
assertTrue(keyaggCache.count { it.toInt() != 0 } > 100) // the cache has been filled with key aggregation data
|
||||
|
||||
// We can reuse the key aggregation cache to speed up computation.
|
||||
val agg3 = Secp256k1.musigPubkeyAgg(arrayOf(pubkeys[0], pubkeys[1], pubkeys[2]), keyaggCache)
|
||||
assertEquals("90539EEDE565F5D054F32CC0C220126889ED1E5D193BAF15AEF344FE59D4610C", Hex.encode(agg3).uppercase())
|
||||
|
||||
val agg4 = Secp256k1.musigPubkeyAgg(arrayOf(pubkeys[2], pubkeys[1], pubkeys[0]), null)
|
||||
assertEquals("6204DE8B083426DC6EAF9502D27024D53FC826BF7D2012148A0575435DF54B2B", Hex.encode(agg4).uppercase())
|
||||
|
||||
val agg5 = Secp256k1.musigPubkeyAgg(arrayOf(pubkeys[0], pubkeys[0], pubkeys[0]), null)
|
||||
assertEquals("B436E3BAD62B8CD409969A224731C193D051162D8C5AE8B109306127DA3AA935", Hex.encode(agg5).uppercase())
|
||||
|
||||
val agg6 = Secp256k1.musigPubkeyAgg(arrayOf(pubkeys[0], pubkeys[0], pubkeys[1], pubkeys[1]), null)
|
||||
assertEquals("69BC22BFA5D106306E48A20679DE1D7389386124D07571D0D872686028C26A3E", Hex.encode(agg6).uppercase())
|
||||
|
||||
// If we provide the key aggregation cache for a different session, it is ignored and overwritten.
|
||||
val agg7 = Secp256k1.musigPubkeyAgg(arrayOf(pubkeys[0], pubkeys[0], pubkeys[1], pubkeys[1]), keyaggCache)
|
||||
assertEquals("69BC22BFA5D106306E48A20679DE1D7389386124D07571D0D872686028C26A3E", Hex.encode(agg7).uppercase())
|
||||
|
||||
// If we provide random data in the key aggregation cache, it is ignored and overwritten.
|
||||
val agg8 = Secp256k1.musigPubkeyAgg(arrayOf(pubkeys[0], pubkeys[0], pubkeys[1], pubkeys[1]), Random.nextBytes(Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE))
|
||||
assertEquals("69BC22BFA5D106306E48A20679DE1D7389386124D07571D0D872686028C26A3E", Hex.encode(agg8).uppercase())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMusig2TweakPubkeys() {
|
||||
val pubkeys = listOf(
|
||||
"031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f",
|
||||
"024d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766",
|
||||
"02531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe337"
|
||||
).map { Hex.decode(it) }.toTypedArray()
|
||||
val cache = ByteArray(Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
|
||||
val agg1 = Secp256k1.musigPubkeyAgg(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 keyaggCaches = (0 until 2).map { ByteArray(Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE) }
|
||||
val aggpubkey = Secp256k1.musigPubkeyAgg(pubkeys.toTypedArray(), keyaggCaches[0])
|
||||
assertContentEquals(aggpubkey, Secp256k1.musigPubkeyAgg(pubkeys.toTypedArray(), keyaggCaches[1]))
|
||||
assertContentEquals(keyaggCaches[0], keyaggCaches[1])
|
||||
|
||||
val msg32 = Hex.decode("0303030303030303030303030303030303030303030303030303030303030303")
|
||||
val sessions = (0 until 2).map { Secp256k1.musigNonceProcess(aggnonce, msg32, keyaggCaches[it]) }
|
||||
val psigs = (0 until 2).map {
|
||||
val psig = Secp256k1.musigPartialSign(secnonces[it], privkeys[it], keyaggCaches[it], sessions[it])
|
||||
assertEquals(1, Secp256k1.musigPartialSigVerify(psig, pubnonces[it], pubkeys[it], keyaggCaches[it], sessions[it]))
|
||||
assertEquals(0, Secp256k1.musigPartialSigVerify(Random.nextBytes(32), pubnonces[it], pubkeys[it], keyaggCaches[it], sessions[it]))
|
||||
psig
|
||||
}
|
||||
|
||||
val sig = Secp256k1.musigPartialSigAgg(sessions[0], psigs.toTypedArray())
|
||||
assertContentEquals(sig, Secp256k1.musigPartialSigAgg(sessions[1], psigs.toTypedArray()))
|
||||
assertTrue(Secp256k1.verifySchnorr(sig, msg32, aggpubkey))
|
||||
|
||||
val invalidSig1 = Secp256k1.musigPartialSigAgg(sessions[0], arrayOf(psigs[0], psigs[0]))
|
||||
assertFalse(Secp256k1.verifySchnorr(invalidSig1, msg32, aggpubkey))
|
||||
val invalidSig2 = Secp256k1.musigPartialSigAgg(sessions[0], arrayOf(Random.nextBytes(32), Random.nextBytes(32)))
|
||||
assertFalse(Secp256k1.verifySchnorr(invalidSig2, msg32, aggpubkey))
|
||||
}
|
||||
|
||||
@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
|
||||
)
|
||||
}
|
||||
assertFails {
|
||||
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 keyaggCaches = (0 until 2).map { ByteArray(Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE) }
|
||||
val aggpubkey = Secp256k1.musigPubkeyAgg(pubkeys.toTypedArray(), keyaggCaches[0])
|
||||
assertContentEquals(aggpubkey, Secp256k1.musigPubkeyAgg(pubkeys.toTypedArray(), keyaggCaches[1]))
|
||||
assertContentEquals(keyaggCaches[0], keyaggCaches[1])
|
||||
val msg32 = Hex.decode("0303030303030303030303030303030303030303030303030303030303030303")
|
||||
val sessions = (0 until 2).map { Secp256k1.musigNonceProcess(aggnonce, msg32, keyaggCaches[it]) }
|
||||
|
||||
// we sign with the wrong secret nonce. it should fail (i.e. trigger an exception) but not crash the JVM
|
||||
Secp256k1.musigPartialSign(secnonces[1], privkeys[0], keyaggCaches[0], sessions[0])
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun fuzzEcdsaSignVerify() {
|
||||
val random = Random.Default
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
{
|
||||
"sk": "7FB9E0E687ADA1EEBF7ECFE2F21E73EBDB51A7D450948DFE8D76D7F2D1007671",
|
||||
"pubkeys": [
|
||||
"03935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9",
|
||||
"02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
|
||||
"02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659",
|
||||
"020000000000000000000000000000000000000000000000000000000000000007"
|
||||
],
|
||||
"msgs": [
|
||||
"F95466D086770E689964664219266FE5ED215C92AE20BAB5C9D79ADDDDF3C0CF",
|
||||
"2626262626262626262626262626262626262626262626262626262626262626262626262626"
|
||||
],
|
||||
"valid_test_cases": [
|
||||
{
|
||||
"rand": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"aggothernonce": "0337C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480",
|
||||
"key_indices": [0, 1, 2],
|
||||
"tweaks": [],
|
||||
"is_xonly": [],
|
||||
"msg_index": 0,
|
||||
"signer_index": 0,
|
||||
"expected": [
|
||||
"03D96275257C2FCCBB6EEB77BDDF51D3C88C26EE1626C6CDA8999B9D34F4BA13A60309BE2BF883C6ABE907FA822D9CA166D51A3DCC28910C57528F6983FC378B7843",
|
||||
"41EA65093F71D084785B20DC26A887CD941C9597860A21660CBDB9CC2113CAD3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"rand": null,
|
||||
"aggothernonce": "0337C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480",
|
||||
"key_indices": [1, 0, 2],
|
||||
"tweaks": [],
|
||||
"is_xonly": [],
|
||||
"msg_index": 0,
|
||||
"signer_index": 1,
|
||||
"expected": [
|
||||
"028FBCCF5BB73A7B61B270BAD15C0F9475D577DD85C2157C9D38BEF1EC922B48770253BE3638C87369BC287E446B7F2C8CA5BEB9FFBD1EA082C62913982A65FC214D",
|
||||
"AEAA31262637BFA88D5606679018A0FEEEC341F3107D1199857F6C81DE61B8DD"
|
||||
]
|
||||
},
|
||||
{
|
||||
"rand": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
|
||||
"aggothernonce": "0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
|
||||
"key_indices": [1, 2, 0],
|
||||
"tweaks": [],
|
||||
"is_xonly": [],
|
||||
"msg_index": 1,
|
||||
"signer_index": 2,
|
||||
"expected": [
|
||||
"024FA8D774F0C8743FAA77AFB4D08EE5A013C2E8EEAD8A6F08A77DDD2D28266DB803050905E8C994477F3F2981861A2E3791EF558626E645FBF5AA131C5D6447C2C2",
|
||||
"FEE28A56B8556B7632E42A84122C51A4861B1F2DEC7E81B632195E56A52E3E13"
|
||||
],
|
||||
"comment": "Message longer than 32 bytes"
|
||||
},
|
||||
{
|
||||
"rand": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"aggothernonce": "032DE2662628C90B03F5E720284EB52FF7D71F4284F627B68A853D78C78E1FFE9303E4C5524E83FFE1493B9077CF1CA6BEB2090C93D930321071AD40B2F44E599046",
|
||||
"key_indices": [0, 1, 2],
|
||||
"tweaks": ["E8F791FF9225A2AF0102AFFF4A9A723D9612A682A25EBE79802B263CDFCD83BB"],
|
||||
"is_xonly": [true],
|
||||
"msg_index": 0,
|
||||
"signer_index": 0,
|
||||
"expected": [
|
||||
"031E07C0D11A0134E55DB1FC16095ADCBD564236194374AA882BFB3C78273BF673039D0336E8CA6288C00BFC1F8B594563529C98661172B9BC1BE85C23A4CE1F616B",
|
||||
"7B1246C5889E59CB0375FA395CC86AC42D5D7D59FD8EAB4FDF1DCAB2B2F006EA"
|
||||
],
|
||||
"comment": "Tweaked public key"
|
||||
}
|
||||
],
|
||||
"error_test_cases": [
|
||||
{
|
||||
"rand": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"aggothernonce": "0337C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480",
|
||||
"key_indices": [1, 0, 3],
|
||||
"tweaks": [],
|
||||
"is_xonly": [],
|
||||
"msg_index": 0,
|
||||
"signer_index": 1,
|
||||
"error": {
|
||||
"type": "invalid_contribution",
|
||||
"signer": 2,
|
||||
"contrib": "pubkey"
|
||||
},
|
||||
"comment": "Signer 2 provided an invalid public key"
|
||||
},
|
||||
{
|
||||
"rand": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"aggothernonce": "0337C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480",
|
||||
"key_indices": [1, 2],
|
||||
"tweaks": [],
|
||||
"is_xonly": [],
|
||||
"msg_index": 0,
|
||||
"signer_index": 1,
|
||||
"error": {
|
||||
"type": "value",
|
||||
"message": "The signer's pubkey must be included in the list of pubkeys."
|
||||
},
|
||||
"comment": "The signers pubkey is not in the list of pubkeys"
|
||||
},
|
||||
{
|
||||
"rand": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"aggothernonce": "0437C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480",
|
||||
"key_indices": [1, 2, 0],
|
||||
"tweaks": [],
|
||||
"is_xonly": [],
|
||||
"msg_index": 0,
|
||||
"signer_index": 2,
|
||||
"error": {
|
||||
"type": "invalid_contribution",
|
||||
"signer": null,
|
||||
"contrib": "aggothernonce"
|
||||
},
|
||||
"comment": "aggothernonce is invalid due wrong tag, 0x04, in the first half"
|
||||
},
|
||||
{
|
||||
"rand": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"aggothernonce": "0000000000000000000000000000000000000000000000000000000000000000000287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480",
|
||||
"key_indices": [1, 2, 0],
|
||||
"tweaks": [],
|
||||
"is_xonly": [],
|
||||
"msg_index": 0,
|
||||
"signer_index": 2,
|
||||
"error": {
|
||||
"type": "invalid_contribution",
|
||||
"signer": null,
|
||||
"contrib": "aggothernonce"
|
||||
},
|
||||
"comment": "aggothernonce is invalid because first half corresponds to point at infinity"
|
||||
},
|
||||
{
|
||||
"rand": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"aggothernonce": "0337C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480",
|
||||
"key_indices": [1, 2, 0],
|
||||
"tweaks": ["FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"],
|
||||
"is_xonly": [false],
|
||||
"msg_index": 0,
|
||||
"signer_index": 2,
|
||||
"error": {
|
||||
"type": "value",
|
||||
"message": "The tweak must be less than n."
|
||||
},
|
||||
"comment": "Tweak is invalid because it exceeds group size"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
{
|
||||
"pubkeys": [
|
||||
"02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
|
||||
"03DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659",
|
||||
"023590A94E768F8E1815C2F24B4D80A8E3149316C3518CE7B7AD338368D038CA66",
|
||||
"020000000000000000000000000000000000000000000000000000000000000005",
|
||||
"02FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30",
|
||||
"04F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
|
||||
"03935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9"
|
||||
],
|
||||
"tweaks": [
|
||||
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",
|
||||
"252E4BD67410A76CDF933D30EAA1608214037F1B105A013ECCD3C5C184A6110B"
|
||||
],
|
||||
"valid_test_cases": [
|
||||
{
|
||||
"key_indices": [0, 1, 2],
|
||||
"expected": "90539EEDE565F5D054F32CC0C220126889ED1E5D193BAF15AEF344FE59D4610C"
|
||||
},
|
||||
{
|
||||
"key_indices": [2, 1, 0],
|
||||
"expected": "6204DE8B083426DC6EAF9502D27024D53FC826BF7D2012148A0575435DF54B2B"
|
||||
},
|
||||
{
|
||||
"key_indices": [0, 0, 0],
|
||||
"expected": "B436E3BAD62B8CD409969A224731C193D051162D8C5AE8B109306127DA3AA935"
|
||||
},
|
||||
{
|
||||
"key_indices": [0, 0, 1, 1],
|
||||
"expected": "69BC22BFA5D106306E48A20679DE1D7389386124D07571D0D872686028C26A3E"
|
||||
}
|
||||
],
|
||||
"error_test_cases": [
|
||||
{
|
||||
"key_indices": [0, 3],
|
||||
"tweak_indices": [],
|
||||
"is_xonly": [],
|
||||
"error": {
|
||||
"type": "invalid_contribution",
|
||||
"signer": 1,
|
||||
"contrib": "pubkey"
|
||||
},
|
||||
"comment": "Invalid public key"
|
||||
},
|
||||
{
|
||||
"key_indices": [0, 4],
|
||||
"tweak_indices": [],
|
||||
"is_xonly": [],
|
||||
"error": {
|
||||
"type": "invalid_contribution",
|
||||
"signer": 1,
|
||||
"contrib": "pubkey"
|
||||
},
|
||||
"comment": "Public key exceeds field size"
|
||||
},
|
||||
{
|
||||
"key_indices": [5, 0],
|
||||
"tweak_indices": [],
|
||||
"is_xonly": [],
|
||||
"error": {
|
||||
"type": "invalid_contribution",
|
||||
"signer": 0,
|
||||
"contrib": "pubkey"
|
||||
},
|
||||
"comment": "First byte of public key is not 2 or 3"
|
||||
},
|
||||
{
|
||||
"key_indices": [0, 1],
|
||||
"tweak_indices": [0],
|
||||
"is_xonly": [true],
|
||||
"error": {
|
||||
"type": "value",
|
||||
"message": "The tweak must be less than n."
|
||||
},
|
||||
"comment": "Tweak is out of range"
|
||||
},
|
||||
{
|
||||
"key_indices": [6],
|
||||
"tweak_indices": [1],
|
||||
"is_xonly": [false],
|
||||
"error": {
|
||||
"type": "value",
|
||||
"message": "The result of tweaking cannot be infinity."
|
||||
},
|
||||
"comment": "Intermediate tweaking result is point at infinity"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"pubkeys": [
|
||||
"02DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8",
|
||||
"02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
|
||||
"03DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659",
|
||||
"023590A94E768F8E1815C2F24B4D80A8E3149316C3518CE7B7AD338368D038CA66",
|
||||
"02DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EFF",
|
||||
"02DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8"
|
||||
],
|
||||
"sorted_pubkeys": [
|
||||
"023590A94E768F8E1815C2F24B4D80A8E3149316C3518CE7B7AD338368D038CA66",
|
||||
"02DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8",
|
||||
"02DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8",
|
||||
"02DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EFF",
|
||||
"02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
|
||||
"03DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"
|
||||
]
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
{
|
||||
"pnonces": [
|
||||
"020151C80F435648DF67A22B749CD798CE54E0321D034B92B709B567D60A42E66603BA47FBC1834437B3212E89A84D8425E7BF12E0245D98262268EBDCB385D50641",
|
||||
"03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B833",
|
||||
"020151C80F435648DF67A22B749CD798CE54E0321D034B92B709B567D60A42E6660279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
|
||||
"03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60379BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
|
||||
"04FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B833",
|
||||
"03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B831",
|
||||
"03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A602FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30"
|
||||
],
|
||||
"valid_test_cases": [
|
||||
{
|
||||
"pnonce_indices": [0, 1],
|
||||
"expected": "035FE1873B4F2967F52FEA4A06AD5A8ECCBE9D0FD73068012C894E2E87CCB5804B024725377345BDE0E9C33AF3C43C0A29A9249F2F2956FA8CFEB55C8573D0262DC8"
|
||||
},
|
||||
{
|
||||
"pnonce_indices": [2, 3],
|
||||
"expected": "035FE1873B4F2967F52FEA4A06AD5A8ECCBE9D0FD73068012C894E2E87CCB5804B000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"comment": "Sum of second points encoded in the nonces is point at infinity which is serialized as 33 zero bytes"
|
||||
}
|
||||
],
|
||||
"error_test_cases": [
|
||||
{
|
||||
"pnonce_indices": [0, 4],
|
||||
"error": {
|
||||
"type": "invalid_contribution",
|
||||
"signer": 1,
|
||||
"contrib": "pubnonce"
|
||||
},
|
||||
"comment": "Public nonce from signer 1 is invalid due wrong tag, 0x04, in the first half"
|
||||
},
|
||||
{
|
||||
"pnonce_indices": [5, 1],
|
||||
"error": {
|
||||
"type": "invalid_contribution",
|
||||
"signer": 0,
|
||||
"contrib": "pubnonce"
|
||||
},
|
||||
"comment": "Public nonce from signer 0 is invalid because the second half does not correspond to an X coordinate"
|
||||
},
|
||||
{
|
||||
"pnonce_indices": [6, 1],
|
||||
"error": {
|
||||
"type": "invalid_contribution",
|
||||
"signer": 0,
|
||||
"contrib": "pubnonce"
|
||||
},
|
||||
"comment": "Public nonce from signer 0 is invalid because second half exceeds field size"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
{
|
||||
"test_cases": [
|
||||
{
|
||||
"rand_": "0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F",
|
||||
"sk": "0202020202020202020202020202020202020202020202020202020202020202",
|
||||
"pk": "024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766",
|
||||
"aggpk": "0707070707070707070707070707070707070707070707070707070707070707",
|
||||
"msg": "0101010101010101010101010101010101010101010101010101010101010101",
|
||||
"extra_in": "0808080808080808080808080808080808080808080808080808080808080808",
|
||||
"expected_secnonce": "B114E502BEAA4E301DD08A50264172C84E41650E6CB726B410C0694D59EFFB6495B5CAF28D045B973D63E3C99A44B807BDE375FD6CB39E46DC4A511708D0E9D2024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766",
|
||||
"expected_pubnonce": "02F7BE7089E8376EB355272368766B17E88E7DB72047D05E56AA881EA52B3B35DF02C29C8046FDD0DED4C7E55869137200FBDBFE2EB654267B6D7013602CAED3115A"
|
||||
},
|
||||
{
|
||||
"rand_": "0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F",
|
||||
"sk": "0202020202020202020202020202020202020202020202020202020202020202",
|
||||
"pk": "024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766",
|
||||
"aggpk": "0707070707070707070707070707070707070707070707070707070707070707",
|
||||
"msg": "",
|
||||
"extra_in": "0808080808080808080808080808080808080808080808080808080808080808",
|
||||
"expected_secnonce": "E862B068500320088138468D47E0E6F147E01B6024244AE45EAC40ACE5929B9F0789E051170B9E705D0B9EB49049A323BBBBB206D8E05C19F46C6228742AA7A9024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766",
|
||||
"expected_pubnonce": "023034FA5E2679F01EE66E12225882A7A48CC66719B1B9D3B6C4DBD743EFEDA2C503F3FD6F01EB3A8E9CB315D73F1F3D287CAFBB44AB321153C6287F407600205109"
|
||||
},
|
||||
{
|
||||
"rand_": "0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F",
|
||||
"sk": "0202020202020202020202020202020202020202020202020202020202020202",
|
||||
"pk": "024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766",
|
||||
"aggpk": "0707070707070707070707070707070707070707070707070707070707070707",
|
||||
"msg": "2626262626262626262626262626262626262626262626262626262626262626262626262626",
|
||||
"extra_in": "0808080808080808080808080808080808080808080808080808080808080808",
|
||||
"expected_secnonce": "3221975ACBDEA6820EABF02A02B7F27D3A8EF68EE42787B88CBEFD9AA06AF3632EE85B1A61D8EF31126D4663A00DD96E9D1D4959E72D70FE5EBB6E7696EBA66F024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766",
|
||||
"expected_pubnonce": "02E5BBC21C69270F59BD634FCBFA281BE9D76601295345112C58954625BF23793A021307511C79F95D38ACACFF1B4DA98228B77E65AA216AD075E9673286EFB4EAF3"
|
||||
},
|
||||
{
|
||||
"rand_": "0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F",
|
||||
"sk": null,
|
||||
"pk": "02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
|
||||
"aggpk": null,
|
||||
"msg": null,
|
||||
"extra_in": null,
|
||||
"expected_secnonce": "89BDD787D0284E5E4D5FC572E49E316BAB7E21E3B1830DE37DFE80156FA41A6D0B17AE8D024C53679699A6FD7944D9C4A366B514BAF43088E0708B1023DD289702F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
|
||||
"expected_pubnonce": "02C96E7CB1E8AA5DAC64D872947914198F607D90ECDE5200DE52978AD5DED63C000299EC5117C2D29EDEE8A2092587C3909BE694D5CFF0667D6C02EA4059F7CD9786"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
{
|
||||
"pubkeys": [
|
||||
"03935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9",
|
||||
"02D2DC6F5DF7C56ACF38C7FA0AE7A759AE30E19B37359DFDE015872324C7EF6E05",
|
||||
"03C7FB101D97FF930ACD0C6760852EF64E69083DE0B06AC6335724754BB4B0522C",
|
||||
"02352433B21E7E05D3B452B81CAE566E06D2E003ECE16D1074AABA4289E0E3D581"
|
||||
],
|
||||
"pnonces": [
|
||||
"036E5EE6E28824029FEA3E8A9DDD2C8483F5AF98F7177C3AF3CB6F47CAF8D94AE902DBA67E4A1F3680826172DA15AFB1A8CA85C7C5CC88900905C8DC8C328511B53E",
|
||||
"03E4F798DA48A76EEC1C9CC5AB7A880FFBA201A5F064E627EC9CB0031D1D58FC5103E06180315C5A522B7EC7C08B69DCD721C313C940819296D0A7AB8E8795AC1F00",
|
||||
"02C0068FD25523A31578B8077F24F78F5BD5F2422AFF47C1FADA0F36B3CEB6C7D202098A55D1736AA5FCC21CF0729CCE852575C06C081125144763C2C4C4A05C09B6",
|
||||
"031F5C87DCFBFCF330DEE4311D85E8F1DEA01D87A6F1C14CDFC7E4F1D8C441CFA40277BF176E9F747C34F81B0D9F072B1B404A86F402C2D86CF9EA9E9C69876EA3B9",
|
||||
"023F7042046E0397822C4144A17F8B63D78748696A46C3B9F0A901D296EC3406C302022B0B464292CF9751D699F10980AC764E6F671EFCA15069BBE62B0D1C62522A",
|
||||
"02D97DDA5988461DF58C5897444F116A7C74E5711BF77A9446E27806563F3B6C47020CBAD9C363A7737F99FA06B6BE093CEAFF5397316C5AC46915C43767AE867C00"
|
||||
],
|
||||
"tweaks": [
|
||||
"B511DA492182A91B0FFB9A98020D55F260AE86D7ECBD0399C7383D59A5F2AF7C",
|
||||
"A815FE049EE3C5AAB66310477FBC8BCCCAC2F3395F59F921C364ACD78A2F48DC",
|
||||
"75448A87274B056468B977BE06EB1E9F657577B7320B0A3376EA51FD420D18A8"
|
||||
],
|
||||
"psigs": [
|
||||
"B15D2CD3C3D22B04DAE438CE653F6B4ECF042F42CFDED7C41B64AAF9B4AF53FB",
|
||||
"6193D6AC61B354E9105BBDC8937A3454A6D705B6D57322A5A472A02CE99FCB64",
|
||||
"9A87D3B79EC67228CB97878B76049B15DBD05B8158D17B5B9114D3C226887505",
|
||||
"66F82EA90923689B855D36C6B7E032FB9970301481B99E01CDB4D6AC7C347A15",
|
||||
"4F5AEE41510848A6447DCD1BBC78457EF69024944C87F40250D3EF2C25D33EFE",
|
||||
"DDEF427BBB847CC027BEFF4EDB01038148917832253EBC355FC33F4A8E2FCCE4",
|
||||
"97B890A26C981DA8102D3BC294159D171D72810FDF7C6A691DEF02F0F7AF3FDC",
|
||||
"53FA9E08BA5243CBCB0D797C5EE83BC6728E539EB76C2D0BF0F971EE4E909971",
|
||||
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"
|
||||
],
|
||||
"msg": "599C67EA410D005B9DA90817CF03ED3B1C868E4DA4EDF00A5880B0082C237869",
|
||||
"valid_test_cases": [
|
||||
{
|
||||
"aggnonce": "0341432722C5CD0268D829C702CF0D1CBCE57033EED201FD335191385227C3210C03D377F2D258B64AADC0E16F26462323D701D286046A2EA93365656AFD9875982B",
|
||||
"nonce_indices": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"key_indices": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"tweak_indices": [],
|
||||
"is_xonly": [],
|
||||
"psig_indices": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"expected": "041DA22223CE65C92C9A0D6C2CAC828AAF1EEE56304FEC371DDF91EBB2B9EF0912F1038025857FEDEB3FF696F8B99FA4BB2C5812F6095A2E0004EC99CE18DE1E"
|
||||
},
|
||||
{
|
||||
"aggnonce": "0224AFD36C902084058B51B5D36676BBA4DC97C775873768E58822F87FE437D792028CB15929099EEE2F5DAE404CD39357591BA32E9AF4E162B8D3E7CB5EFE31CB20",
|
||||
"nonce_indices": [
|
||||
0,
|
||||
2
|
||||
],
|
||||
"key_indices": [
|
||||
0,
|
||||
2
|
||||
],
|
||||
"tweak_indices": [],
|
||||
"is_xonly": [],
|
||||
"psig_indices": [
|
||||
2,
|
||||
3
|
||||
],
|
||||
"expected": "1069B67EC3D2F3C7C08291ACCB17A9C9B8F2819A52EB5DF8726E17E7D6B52E9F01800260A7E9DAC450F4BE522DE4CE12BA91AEAF2B4279219EF74BE1D286ADD9"
|
||||
},
|
||||
{
|
||||
"aggnonce": "0208C5C438C710F4F96A61E9FF3C37758814B8C3AE12BFEA0ED2C87FF6954FF186020B1816EA104B4FCA2D304D733E0E19CEAD51303FF6420BFD222335CAA402916D",
|
||||
"nonce_indices": [
|
||||
0,
|
||||
3
|
||||
],
|
||||
"key_indices": [
|
||||
0,
|
||||
2
|
||||
],
|
||||
"tweak_indices": [
|
||||
0
|
||||
],
|
||||
"is_xonly": [
|
||||
false
|
||||
],
|
||||
"psig_indices": [
|
||||
4,
|
||||
5
|
||||
],
|
||||
"expected": "5C558E1DCADE86DA0B2F02626A512E30A22CF5255CAEA7EE32C38E9A71A0E9148BA6C0E6EC7683B64220F0298696F1B878CD47B107B81F7188812D593971E0CC"
|
||||
},
|
||||
{
|
||||
"aggnonce": "02B5AD07AFCD99B6D92CB433FBD2A28FDEB98EAE2EB09B6014EF0F8197CD58403302E8616910F9293CF692C49F351DB86B25E352901F0E237BAFDA11F1C1CEF29FFD",
|
||||
"nonce_indices": [
|
||||
0,
|
||||
4
|
||||
],
|
||||
"key_indices": [
|
||||
0,
|
||||
3
|
||||
],
|
||||
"tweak_indices": [
|
||||
0,
|
||||
1,
|
||||
2
|
||||
],
|
||||
"is_xonly": [
|
||||
true,
|
||||
false,
|
||||
true
|
||||
],
|
||||
"psig_indices": [
|
||||
6,
|
||||
7
|
||||
],
|
||||
"expected": "839B08820B681DBA8DAF4CC7B104E8F2638F9388F8D7A555DC17B6E6971D7426CE07BF6AB01F1DB50E4E33719295F4094572B79868E440FB3DEFD3FAC1DB589E"
|
||||
}
|
||||
],
|
||||
"error_test_cases": [
|
||||
{
|
||||
"aggnonce": "02B5AD07AFCD99B6D92CB433FBD2A28FDEB98EAE2EB09B6014EF0F8197CD58403302E8616910F9293CF692C49F351DB86B25E352901F0E237BAFDA11F1C1CEF29FFD",
|
||||
"nonce_indices": [
|
||||
0,
|
||||
4
|
||||
],
|
||||
"key_indices": [
|
||||
0,
|
||||
3
|
||||
],
|
||||
"tweak_indices": [
|
||||
0,
|
||||
1,
|
||||
2
|
||||
],
|
||||
"is_xonly": [
|
||||
true,
|
||||
false,
|
||||
true
|
||||
],
|
||||
"psig_indices": [
|
||||
7,
|
||||
8
|
||||
],
|
||||
"error": {
|
||||
"type": "invalid_contribution",
|
||||
"signer": 1
|
||||
},
|
||||
"comment": "Partial signature is invalid because it exceeds group size"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
{
|
||||
"sk": "7FB9E0E687ADA1EEBF7ECFE2F21E73EBDB51A7D450948DFE8D76D7F2D1007671",
|
||||
"pubkeys": [
|
||||
"03935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9",
|
||||
"02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
|
||||
"02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA661",
|
||||
"020000000000000000000000000000000000000000000000000000000000000007"
|
||||
],
|
||||
"secnonces": [
|
||||
"508B81A611F100A6B2B6B29656590898AF488BCF2E1F55CF22E5CFB84421FE61FA27FD49B1D50085B481285E1CA205D55C82CC1B31FF5CD54A489829355901F703935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9",
|
||||
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9"
|
||||
],
|
||||
"pnonces": [
|
||||
"0337C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480",
|
||||
"0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
|
||||
"032DE2662628C90B03F5E720284EB52FF7D71F4284F627B68A853D78C78E1FFE9303E4C5524E83FFE1493B9077CF1CA6BEB2090C93D930321071AD40B2F44E599046",
|
||||
"0237C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0387BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480",
|
||||
"0200000000000000000000000000000000000000000000000000000000000000090287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480"
|
||||
],
|
||||
"aggnonces": [
|
||||
"028465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD61037496A3CC86926D452CAFCFD55D25972CA1675D549310DE296BFF42F72EEEA8C9",
|
||||
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"048465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD61037496A3CC86926D452CAFCFD55D25972CA1675D549310DE296BFF42F72EEEA8C9",
|
||||
"028465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD61020000000000000000000000000000000000000000000000000000000000000009",
|
||||
"028465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD6102FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30"
|
||||
],
|
||||
"msgs": [
|
||||
"F95466D086770E689964664219266FE5ED215C92AE20BAB5C9D79ADDDDF3C0CF",
|
||||
"",
|
||||
"2626262626262626262626262626262626262626262626262626262626262626262626262626"
|
||||
],
|
||||
"valid_test_cases": [
|
||||
{
|
||||
"key_indices": [0, 1, 2],
|
||||
"nonce_indices": [0, 1, 2],
|
||||
"aggnonce_index": 0,
|
||||
"msg_index": 0,
|
||||
"signer_index": 0,
|
||||
"expected": "012ABBCB52B3016AC03AD82395A1A415C48B93DEF78718E62A7A90052FE224FB"
|
||||
},
|
||||
{
|
||||
"key_indices": [1, 0, 2],
|
||||
"nonce_indices": [1, 0, 2],
|
||||
"aggnonce_index": 0,
|
||||
"msg_index": 0,
|
||||
"signer_index": 1,
|
||||
"expected": "9FF2F7AAA856150CC8819254218D3ADEEB0535269051897724F9DB3789513A52"
|
||||
},
|
||||
{
|
||||
"key_indices": [1, 2, 0],
|
||||
"nonce_indices": [1, 2, 0],
|
||||
"aggnonce_index": 0,
|
||||
"msg_index": 0,
|
||||
"signer_index": 2,
|
||||
"expected": "FA23C359F6FAC4E7796BB93BC9F0532A95468C539BA20FF86D7C76ED92227900"
|
||||
},
|
||||
{
|
||||
"key_indices": [0, 1],
|
||||
"nonce_indices": [0, 3],
|
||||
"aggnonce_index": 1,
|
||||
"msg_index": 0,
|
||||
"signer_index": 0,
|
||||
"expected": "AE386064B26105404798F75DE2EB9AF5EDA5387B064B83D049CB7C5E08879531",
|
||||
"comment": "Both halves of aggregate nonce correspond to point at infinity"
|
||||
},
|
||||
{
|
||||
"key_indices": [0, 1, 2],
|
||||
"nonce_indices": [0, 1, 2],
|
||||
"aggnonce_index": 0,
|
||||
"msg_index": 1,
|
||||
"signer_index": 0,
|
||||
"expected": "D7D63FFD644CCDA4E62BC2BC0B1D02DD32A1DC3030E155195810231D1037D82D",
|
||||
"comment": "Empty message"
|
||||
},
|
||||
{
|
||||
"key_indices": [0, 1, 2],
|
||||
"nonce_indices": [0, 1, 2],
|
||||
"aggnonce_index": 0,
|
||||
"msg_index": 2,
|
||||
"signer_index": 0,
|
||||
"expected": "E184351828DA5094A97C79CABDAAA0BFB87608C32E8829A4DF5340A6F243B78C",
|
||||
"comment": "38-byte message"
|
||||
}
|
||||
],
|
||||
"sign_error_test_cases": [
|
||||
{
|
||||
"key_indices": [1, 2],
|
||||
"aggnonce_index": 0,
|
||||
"msg_index": 0,
|
||||
"secnonce_index": 0,
|
||||
"error": {
|
||||
"type": "value",
|
||||
"message": "The signer's pubkey must be included in the list of pubkeys."
|
||||
},
|
||||
"comment": "The signers pubkey is not in the list of pubkeys. This test case is optional: it can be skipped by implementations that do not check that the signer's pubkey is included in the list of pubkeys."
|
||||
},
|
||||
{
|
||||
"key_indices": [1, 0, 3],
|
||||
"aggnonce_index": 0,
|
||||
"msg_index": 0,
|
||||
"secnonce_index": 0,
|
||||
"error": {
|
||||
"type": "invalid_contribution",
|
||||
"signer": 2,
|
||||
"contrib": "pubkey"
|
||||
},
|
||||
"comment": "Signer 2 provided an invalid public key"
|
||||
},
|
||||
{
|
||||
"key_indices": [1, 2, 0],
|
||||
"aggnonce_index": 2,
|
||||
"msg_index": 0,
|
||||
"secnonce_index": 0,
|
||||
"error": {
|
||||
"type": "invalid_contribution",
|
||||
"signer": null,
|
||||
"contrib": "aggnonce"
|
||||
},
|
||||
"comment": "Aggregate nonce is invalid due wrong tag, 0x04, in the first half"
|
||||
},
|
||||
{
|
||||
"key_indices": [1, 2, 0],
|
||||
"aggnonce_index": 3,
|
||||
"msg_index": 0,
|
||||
"secnonce_index": 0,
|
||||
"error": {
|
||||
"type": "invalid_contribution",
|
||||
"signer": null,
|
||||
"contrib": "aggnonce"
|
||||
},
|
||||
"comment": "Aggregate nonce is invalid because the second half does not correspond to an X coordinate"
|
||||
},
|
||||
{
|
||||
"key_indices": [1, 2, 0],
|
||||
"aggnonce_index": 4,
|
||||
"msg_index": 0,
|
||||
"secnonce_index": 0,
|
||||
"error": {
|
||||
"type": "invalid_contribution",
|
||||
"signer": null,
|
||||
"contrib": "aggnonce"
|
||||
},
|
||||
"comment": "Aggregate nonce is invalid because second half exceeds field size"
|
||||
},
|
||||
{
|
||||
"key_indices": [0, 1, 2],
|
||||
"aggnonce_index": 0,
|
||||
"msg_index": 0,
|
||||
"signer_index": 0,
|
||||
"secnonce_index": 1,
|
||||
"error": {
|
||||
"type": "value",
|
||||
"message": "first secnonce value is out of range."
|
||||
},
|
||||
"comment": "Secnonce is invalid which may indicate nonce reuse"
|
||||
}
|
||||
],
|
||||
"verify_fail_test_cases": [
|
||||
{
|
||||
"sig": "97AC833ADCB1AFA42EBF9E0725616F3C9A0D5B614F6FE283CEAAA37A8FFAF406",
|
||||
"key_indices": [0, 1, 2],
|
||||
"nonce_indices": [0, 1, 2],
|
||||
"msg_index": 0,
|
||||
"signer_index": 0,
|
||||
"comment": "Wrong signature (which is equal to the negation of valid signature)"
|
||||
},
|
||||
{
|
||||
"sig": "68537CC5234E505BD14061F8DA9E90C220A181855FD8BDB7F127BB12403B4D3B",
|
||||
"key_indices": [0, 1, 2],
|
||||
"nonce_indices": [0, 1, 2],
|
||||
"msg_index": 0,
|
||||
"signer_index": 1,
|
||||
"comment": "Wrong signer"
|
||||
},
|
||||
{
|
||||
"sig": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",
|
||||
"key_indices": [0, 1, 2],
|
||||
"nonce_indices": [0, 1, 2],
|
||||
"msg_index": 0,
|
||||
"signer_index": 0,
|
||||
"comment": "Signature exceeds group size"
|
||||
}
|
||||
],
|
||||
"verify_error_test_cases": [
|
||||
{
|
||||
"sig": "68537CC5234E505BD14061F8DA9E90C220A181855FD8BDB7F127BB12403B4D3B",
|
||||
"key_indices": [0, 1, 2],
|
||||
"nonce_indices": [4, 1, 2],
|
||||
"msg_index": 0,
|
||||
"signer_index": 0,
|
||||
"error": {
|
||||
"type": "invalid_contribution",
|
||||
"signer": 0,
|
||||
"contrib": "pubnonce"
|
||||
},
|
||||
"comment": "Invalid pubnonce"
|
||||
},
|
||||
{
|
||||
"sig": "68537CC5234E505BD14061F8DA9E90C220A181855FD8BDB7F127BB12403B4D3B",
|
||||
"key_indices": [3, 1, 2],
|
||||
"nonce_indices": [0, 1, 2],
|
||||
"msg_index": 0,
|
||||
"signer_index": 0,
|
||||
"error": {
|
||||
"type": "invalid_contribution",
|
||||
"signer": 0,
|
||||
"contrib": "pubkey"
|
||||
},
|
||||
"comment": "Invalid pubkey"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
{
|
||||
"sk": "7FB9E0E687ADA1EEBF7ECFE2F21E73EBDB51A7D450948DFE8D76D7F2D1007671",
|
||||
"pubkeys": [
|
||||
"03935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9",
|
||||
"02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
|
||||
"02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"
|
||||
],
|
||||
"secnonce": "508B81A611F100A6B2B6B29656590898AF488BCF2E1F55CF22E5CFB84421FE61FA27FD49B1D50085B481285E1CA205D55C82CC1B31FF5CD54A489829355901F703935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9",
|
||||
"pnonces": [
|
||||
"0337C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480",
|
||||
"0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
|
||||
"032DE2662628C90B03F5E720284EB52FF7D71F4284F627B68A853D78C78E1FFE9303E4C5524E83FFE1493B9077CF1CA6BEB2090C93D930321071AD40B2F44E599046"
|
||||
],
|
||||
"aggnonce": "028465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD61037496A3CC86926D452CAFCFD55D25972CA1675D549310DE296BFF42F72EEEA8C9",
|
||||
"tweaks": [
|
||||
"E8F791FF9225A2AF0102AFFF4A9A723D9612A682A25EBE79802B263CDFCD83BB",
|
||||
"AE2EA797CC0FE72AC5B97B97F3C6957D7E4199A167A58EB08BCAFFDA70AC0455",
|
||||
"F52ECBC565B3D8BEA2DFD5B75A4F457E54369809322E4120831626F290FA87E0",
|
||||
"1969AD73CC177FA0B4FCED6DF1F7BF9907E665FDE9BA196A74FED0A3CF5AEF9D",
|
||||
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"
|
||||
],
|
||||
"msg": "F95466D086770E689964664219266FE5ED215C92AE20BAB5C9D79ADDDDF3C0CF",
|
||||
"valid_test_cases": [
|
||||
{
|
||||
"key_indices": [1, 2, 0],
|
||||
"nonce_indices": [1, 2, 0],
|
||||
"tweak_indices": [0],
|
||||
"is_xonly": [true],
|
||||
"signer_index": 2,
|
||||
"expected": "E28A5C66E61E178C2BA19DB77B6CF9F7E2F0F56C17918CD13135E60CC848FE91",
|
||||
"comment": "A single x-only tweak"
|
||||
},
|
||||
{
|
||||
"key_indices": [1, 2, 0],
|
||||
"nonce_indices": [1, 2, 0],
|
||||
"tweak_indices": [0],
|
||||
"is_xonly": [false],
|
||||
"signer_index": 2,
|
||||
"expected": "38B0767798252F21BF5702C48028B095428320F73A4B14DB1E25DE58543D2D2D",
|
||||
"comment": "A single plain tweak"
|
||||
},
|
||||
{
|
||||
"key_indices": [1, 2, 0],
|
||||
"nonce_indices": [1, 2, 0],
|
||||
"tweak_indices": [0, 1],
|
||||
"is_xonly": [false, true],
|
||||
"signer_index": 2,
|
||||
"expected": "408A0A21C4A0F5DACAF9646AD6EB6FECD7F7A11F03ED1F48DFFF2185BC2C2408",
|
||||
"comment": "A plain tweak followed by an x-only tweak"
|
||||
},
|
||||
{
|
||||
"key_indices": [1, 2, 0],
|
||||
"nonce_indices": [1, 2, 0],
|
||||
"tweak_indices": [0, 1, 2, 3],
|
||||
"is_xonly": [false, false, true, true],
|
||||
"signer_index": 2,
|
||||
"expected": "45ABD206E61E3DF2EC9E264A6FEC8292141A633C28586388235541F9ADE75435",
|
||||
"comment": "Four tweaks: plain, plain, x-only, x-only."
|
||||
},
|
||||
{
|
||||
"key_indices": [1, 2, 0],
|
||||
"nonce_indices": [1, 2, 0],
|
||||
"tweak_indices": [0, 1, 2, 3],
|
||||
"is_xonly": [true, false, true, false],
|
||||
"signer_index": 2,
|
||||
"expected": "B255FDCAC27B40C7CE7848E2D3B7BF5EA0ED756DA81565AC804CCCA3E1D5D239",
|
||||
"comment": "Four tweaks: x-only, plain, x-only, plain. If an implementation prohibits applying plain tweaks after x-only tweaks, it can skip this test vector or return an error."
|
||||
}
|
||||
],
|
||||
"error_test_cases": [
|
||||
{
|
||||
"key_indices": [1, 2, 0],
|
||||
"nonce_indices": [1, 2, 0],
|
||||
"tweak_indices": [4],
|
||||
"is_xonly": [false],
|
||||
"signer_index": 2,
|
||||
"error": {
|
||||
"type": "value",
|
||||
"message": "The tweak must be less than n."
|
||||
},
|
||||
"comment": "Tweak is invalid because it exceeds group size"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user