Compare commits
53 Commits
snapshot/i
...
frost
Author | SHA1 | Date | |
---|---|---|---|
|
c368691bf9 | ||
|
83dff95ece | ||
|
9a06440277 | ||
|
9541124a4a | ||
|
8cacf80491 | ||
|
64e15574db | ||
|
d74895079d | ||
|
e6cba2b505 | ||
|
5117507f25 | ||
|
c39590a859 | ||
|
83141916e8 | ||
|
f5ab7fdeb7 | ||
|
08f0304d4c | ||
|
af3f44f426 | ||
|
be7f11d657 | ||
|
6e578d43d7 | ||
|
b1e96f329b | ||
|
e7f074d36e | ||
|
32ee077995 | ||
|
41ddd40691 | ||
|
3efca867e6 | ||
|
45c713d56e | ||
|
8b56662685 | ||
|
28d255daca | ||
|
1b29cfa2d7 | ||
|
b439181376 | ||
|
5c4186770f | ||
|
86d7d9835f | ||
|
23739e13a8 | ||
|
e7ec7b7478 | ||
|
eb71fd5ff4 | ||
|
b064c0bcf0 | ||
|
9e287feb26 | ||
|
3c01a2aad4 | ||
|
4826863644 | ||
|
099dcccf49 | ||
|
600431caa1 | ||
|
30d690ee6e | ||
|
4e1b38d163 | ||
|
567f411e12 | ||
|
eb92fccbd6 | ||
|
d30e2a1b59 | ||
|
bae4744bcb | ||
|
b2eb68bf83 | ||
|
c582396b96 | ||
|
202b0c94b6 | ||
|
780f97e46d | ||
|
da2cf93fcf | ||
|
f5e1655ff0 | ||
|
8ba5d4652e | ||
|
e94e41b896 | ||
|
1a4c8b37cb | ||
|
f242b4ffe8 |
10
.github/workflows/release.yml
vendored
10
.github/workflows/release.yml
vendored
@ -58,15 +58,11 @@ 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_HOME/tools/bin/sdkmanager "ndk;$ANDROID_NDK_VERSION"
|
||||
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "ndk;$ANDROID_NDK_VERSION"
|
||||
- name: Setup Android
|
||||
if: matrix.os == 'windows-latest'
|
||||
shell: msys2 {0}
|
||||
@ -86,7 +82,7 @@ jobs:
|
||||
- name: Check Linux
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
shell: bash
|
||||
run: ./gradlew linuxTest
|
||||
run: ./gradlew linuxX64Test
|
||||
- name: Check iOS
|
||||
if: matrix.os == 'macOS-latest'
|
||||
shell: bash
|
||||
@ -103,7 +99,7 @@ jobs:
|
||||
- name: Publish Linux
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
shell: bash
|
||||
run: ./gradlew publishLinuxPublicationToMavenLocal :jni:jvm:linux:publishJvmPublicationToMavenLocal
|
||||
run: ./gradlew publishLinuxX64PublicationToMavenLocal :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,15 +67,11 @@ 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_HOME/tools/bin/sdkmanager "ndk;$ANDROID_NDK_VERSION"
|
||||
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "ndk;$ANDROID_NDK_VERSION"
|
||||
- name: Setup Android
|
||||
if: matrix.os == 'windows-latest'
|
||||
shell: msys2 {0}
|
||||
@ -95,7 +91,7 @@ jobs:
|
||||
- name: Check Linux
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
shell: bash
|
||||
run: ./gradlew linuxTest
|
||||
run: ./gradlew linuxX64Test
|
||||
- name: Check iOS
|
||||
if: matrix.os == 'macOS-latest'
|
||||
shell: bash
|
||||
@ -112,7 +108,7 @@ jobs:
|
||||
- name: Publish Linux
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
shell: bash
|
||||
run: ./gradlew publishLinuxPublicationToMavenLocal :jni:jvm:linux:publishJvmPublicationToMavenLocal -PsnapshotNumber=${{ github.run_number }} -PgitRef=${{ github.ref }}
|
||||
run: ./gradlew publishLinuxX64PublicationToMavenLocal :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,15 +73,11 @@ 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_HOME/tools/bin/sdkmanager "ndk;$ANDROID_NDK_VERSION"
|
||||
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "ndk;$ANDROID_NDK_VERSION"
|
||||
- name: Setup Android
|
||||
if: matrix.os == 'windows-latest'
|
||||
shell: msys2 {0}
|
||||
@ -101,7 +97,7 @@ jobs:
|
||||
- name: Check Linux
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
shell: bash
|
||||
run: ./gradlew linuxTest
|
||||
run: ./gradlew linuxX64Test
|
||||
- name: Check iOS
|
||||
if: matrix.os == 'macOS-latest'
|
||||
shell: bash
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -13,3 +13,6 @@ local.properties
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
jni/bin/*
|
||||
jni/jvm/bin/*
|
||||
/jni/src/main/java/fr/acinq/secp256k1/Secp256k1CFunctions.class
|
||||
|
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -1,3 +1,3 @@
|
||||
[submodule "native/secp256k1"]
|
||||
path = native/secp256k1
|
||||
url = https://github.com/bitcoin-core/secp256k1.git
|
||||
url = https://code.sigidli.com/frost/secp256k1-zkp.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.8.21"
|
||||
id("org.jetbrains.dokka") version "1.8.10"
|
||||
kotlin("multiplatform") version "1.9.22"
|
||||
id("org.jetbrains.dokka") version "1.9.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.8.10")
|
||||
classpath("org.jetbrains.dokka:dokka-gradle-plugin:1.9.10")
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
group = "fr.acinq.secp256k1"
|
||||
version = "0.11.0"
|
||||
version = "frost-SNAPSHOT"
|
||||
|
||||
repositories {
|
||||
google()
|
||||
@ -52,22 +52,35 @@ kotlin {
|
||||
}
|
||||
}
|
||||
|
||||
val nativeMain by sourceSets.creating { dependsOn(commonMain) }
|
||||
val nativeMain by sourceSets.creating
|
||||
|
||||
linuxX64("linux") {
|
||||
linuxX64 {
|
||||
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")
|
||||
}
|
||||
|
||||
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")
|
||||
if (currentOs.isMacOsX) {
|
||||
macosX64 {
|
||||
secp256k1CInterop("host")
|
||||
}
|
||||
|
||||
macosArm64 {
|
||||
secp256k1CInterop("host")
|
||||
}
|
||||
|
||||
iosX64 {
|
||||
secp256k1CInterop("ios")
|
||||
}
|
||||
|
||||
iosArm64 {
|
||||
secp256k1CInterop("ios")
|
||||
}
|
||||
|
||||
iosSimulatorArm64 {
|
||||
secp256k1CInterop("ios")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sourceSets.all {
|
||||
languageSettings.optIn("kotlin.RequiresOptIn")
|
||||
}
|
||||
@ -80,9 +93,9 @@ allprojects {
|
||||
val currentOs = OperatingSystem.current()
|
||||
val targets = when {
|
||||
currentOs.isLinux -> listOf()
|
||||
currentOs.isMacOsX -> listOf("linux")
|
||||
currentOs.isWindows -> listOf("linux")
|
||||
else -> listOf("linux")
|
||||
currentOs.isMacOsX -> listOf("linuxX64")
|
||||
currentOs.isWindows -> listOf("linuxX64")
|
||||
else -> listOf("linuxX64")
|
||||
}.mapNotNull { kotlin.targets.findByName(it) as? KotlinNativeTarget }
|
||||
|
||||
configure(targets) {
|
||||
|
@ -6,6 +6,7 @@ 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,11 +6,10 @@ import fr.acinq.secp256k1.NativeSecp256k1
|
||||
import java.util.*
|
||||
|
||||
public object NativeSecp256k1AndroidLoader {
|
||||
|
||||
@JvmStatic
|
||||
@Synchronized
|
||||
@Throws(Exception::class)
|
||||
fun load(): Secp256k1 {
|
||||
public fun load(): Secp256k1 {
|
||||
try {
|
||||
System.loadLibrary("secp256k1-jni")
|
||||
return NativeSecp256k1
|
||||
@ -27,5 +26,4 @@ public object NativeSecp256k1AndroidLoader {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,6 +7,43 @@
|
||||
#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
|
||||
|
||||
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FROST_SECRET_NONCE_SIZE
|
||||
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FROST_SECRET_NONCE_SIZE 68L
|
||||
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FROST_PUBLIC_NONCE_SIZE
|
||||
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FROST_PUBLIC_NONCE_SIZE 66L
|
||||
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FROST_AGGREGATE_SHARE_SIZE
|
||||
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FROST_AGGREGATE_SHARE_SIZE 32L
|
||||
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FROST_SESSION_SIZE
|
||||
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FROST_SESSION_SIZE 133L
|
||||
/*
|
||||
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
|
||||
* Method: secp256k1_context_create
|
||||
@ -167,6 +204,174 @@ 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);
|
||||
|
||||
/*
|
||||
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
|
||||
* Method: secp256k1_frost_shares_gen
|
||||
* Signature: (J[B[BII[[B)[B
|
||||
*/
|
||||
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1frost_1shares_1gen
|
||||
(JNIEnv *, jclass, jlong, jbyteArray, jbyteArray, jint, jint, jobjectArray);
|
||||
|
||||
/*
|
||||
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
|
||||
* Method: secp256k1_frost_share_agg
|
||||
* Signature: (J[[B[[[BII[B)[B
|
||||
*/
|
||||
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1frost_1share_1agg
|
||||
(JNIEnv *, jclass, jlong, jobjectArray, jobjectArray, jint, jint, jbyteArray);
|
||||
|
||||
/*
|
||||
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
|
||||
* Method: secp256k1_frost_share_verify
|
||||
* Signature: (JI[B[B[[B)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1frost_1share_1verify
|
||||
(JNIEnv *, jclass, jlong, jint, jbyteArray, jbyteArray, jobjectArray);
|
||||
|
||||
/*
|
||||
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
|
||||
* Method: secp256k1_frost_compute_pubshare
|
||||
* Signature: (JI[B[[[BI)[B
|
||||
*/
|
||||
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1frost_1compute_1pubshare
|
||||
(JNIEnv *, jclass, jlong, jint, jbyteArray, jobjectArray, jint);
|
||||
|
||||
/*
|
||||
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
|
||||
* Method: secp256k1_frost_pubkey_tweak
|
||||
* Signature: (J[B)[B
|
||||
*/
|
||||
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1frost_1pubkey_1tweak
|
||||
(JNIEnv *, jclass, jlong, jbyteArray);
|
||||
|
||||
/*
|
||||
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
|
||||
* Method: secp256k1_frost_pubkey_ec_tweak_add
|
||||
* Signature: (J[B[B)[B
|
||||
*/
|
||||
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1frost_1pubkey_1ec_1tweak_1add
|
||||
(JNIEnv *, jclass, jlong, jbyteArray, jbyteArray);
|
||||
|
||||
/*
|
||||
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
|
||||
* Method: secp256k1_frost_pubkey_xonly_tweak_add
|
||||
* Signature: (J[B[B)[B
|
||||
*/
|
||||
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1frost_1pubkey_1xonly_1tweak_1add
|
||||
(JNIEnv *, jclass, jlong, jbyteArray, jbyteArray);
|
||||
|
||||
/*
|
||||
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
|
||||
* Method: secp256k1_frost_nonce_gen
|
||||
* Signature: (J[B[B[B[B[B)[B
|
||||
*/
|
||||
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1frost_1nonce_1gen
|
||||
(JNIEnv *, jclass, jlong, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jbyteArray);
|
||||
|
||||
/*
|
||||
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
|
||||
* Method: secp256k1_frost_nonce_process
|
||||
* Signature: (J[[BI[B[B[B[[B[B[B)[B
|
||||
*/
|
||||
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1frost_1nonce_1process
|
||||
(JNIEnv *, jclass, jlong, jobjectArray, jint, jbyteArray, jbyteArray, jbyteArray, jobjectArray, jbyteArray, jbyteArray);
|
||||
|
||||
/*
|
||||
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
|
||||
* Method: secp256k1_frost_partial_sign
|
||||
* Signature: (J[B[B[B[B)[B
|
||||
*/
|
||||
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1frost_1partial_1sign
|
||||
(JNIEnv *, jclass, jlong, jbyteArray, jbyteArray, jbyteArray, jbyteArray);
|
||||
|
||||
/*
|
||||
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
|
||||
* Method: secp256k1_frost_partial_sig_verify
|
||||
* Signature: (J[B[B[B[B[B)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1frost_1partial_1sig_1verify
|
||||
(JNIEnv *, jclass, jlong, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jbyteArray);
|
||||
|
||||
/*
|
||||
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
|
||||
* Method: secp256k1_frost_partial_sig_agg
|
||||
* Signature: (J[B[[BI)[B
|
||||
*/
|
||||
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1frost_1partial_1sig_1agg
|
||||
(JNIEnv *, jclass, jlong, jbyteArray, jobjectArray, jint);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,13 +1,25 @@
|
||||
import org.gradle.internal.os.OperatingSystem
|
||||
|
||||
plugins {
|
||||
`java-library`
|
||||
id("org.jetbrains.dokka")
|
||||
`maven-publish`
|
||||
}
|
||||
val currentOs = OperatingSystem.current()
|
||||
|
||||
dependencies {
|
||||
api(project(":jni:jvm:darwin"))
|
||||
api(project(":jni:jvm:linux"))
|
||||
api(project(":jni:jvm:mingw"))
|
||||
val publishModeEnabled = rootProject.hasProperty("publishMode") // TODO: Add a -PpublishMode argument to the build script specifically for publishing a release (not when publishing local).
|
||||
println("publishModeEnabled: $publishModeEnabled")
|
||||
|
||||
if (publishModeEnabled || currentOs.isMacOsX) {
|
||||
api(project(":jni:jvm:darwin"))
|
||||
}
|
||||
if (publishModeEnabled || currentOs.isLinux) {
|
||||
api(project(":jni:jvm:linux"))
|
||||
}
|
||||
if (publishModeEnabled || currentOs.isWindows) {
|
||||
api(project(":jni:jvm:mingw"))
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
|
@ -17,6 +17,7 @@ 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,12 +12,8 @@ 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-$arch"))
|
||||
into(buildDir.resolve("jniResources/fr/acinq/secp256k1/jni/native/darwin"))
|
||||
}
|
||||
|
||||
(tasks["processResources"] as ProcessResources).apply {
|
||||
|
@ -19,7 +19,8 @@ internal object OSInfo {
|
||||
private const val PPC = "ppc"
|
||||
private const val PPC64 = "ppc64"
|
||||
|
||||
@JvmStatic val nativeSuffix: String get() = "$os-$arch"
|
||||
// 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 os: String get() = translateOSName(System.getProperty("os.name"))
|
||||
|
||||
|
@ -29,6 +29,26 @@ 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);
|
||||
@ -68,4 +88,311 @@ 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);
|
||||
|
||||
/**
|
||||
* Creates key shares
|
||||
*
|
||||
* To generate a key, each participant generates a share for each other
|
||||
* participant. For example, in the case of 2 particpants, Alice and Bob, they
|
||||
* each generate 2 shares, distribute 1 share to each other using a secure
|
||||
* channel, and keep 1 for themselves.
|
||||
*
|
||||
* Each participant must transmit shares over secure channels to each other
|
||||
* participant.
|
||||
*
|
||||
* Each call to this function must have a UNIQUE and uniformly RANDOM seed32
|
||||
* that must NOT BE REUSED in subsequent calls to this function and
|
||||
* must be KEPT SECRET (even from other participants).
|
||||
*
|
||||
* @param ctx pointer to a context object (not secp256k1_context_static)
|
||||
* @param seed32 32-byte random seed as explained above. Must be unique to this call to secp256k1_frost_shares_gen
|
||||
* and must be uniformly random.
|
||||
* @param threshold the minimum number of signers required to produce a signature
|
||||
* @param total_signers the total number of participants
|
||||
* @param ids33 array of 33-byte participant IDs
|
||||
*
|
||||
* @return
|
||||
* [0] shares: pointer to the key shares
|
||||
* [1] vss_commitment: pointer to the VSS commitment
|
||||
* [2] pok64: pointer to the proof of knowledge
|
||||
*/
|
||||
public static native byte[] secp256k1_frost_shares_gen(long ctx, byte[] pok64, byte[] seed32, int threshold, int total_signers, byte[][] ids33);
|
||||
|
||||
/**
|
||||
* Aggregates shares
|
||||
*
|
||||
* As part of the key generation protocol, each participant receives a share
|
||||
* from each participant, including a share they "receive" from themselves.
|
||||
* This function verifies those shares against their VSS commitments,
|
||||
* aggregates the shares, and then aggregates the commitments to each
|
||||
* participant's first polynomial coefficient to derive the aggregate public
|
||||
* key.
|
||||
*
|
||||
* If this function returns an error, `secp256k1_frost_share_verify` can be
|
||||
* called on each share to determine which participants submitted faulty
|
||||
* shares.
|
||||
*
|
||||
* @param ctx pointer to a context object (not secp256k1_context_static)
|
||||
* @param shares all key generation shares for the partcipant's index
|
||||
* @param vss_commitments coefficient commitments of all participants ordered by the x-only pubkeys of the participants
|
||||
* @param totalShareCount the total number of shares
|
||||
* @param threshold the minimum number of shares required to produce a signature
|
||||
* @param id33 the 33-byte ID of the participant whose shares are being aggregated
|
||||
*
|
||||
* @return
|
||||
* [0] agg_share: the aggregated share
|
||||
* [1] agg_pk: the aggregated x-only public key
|
||||
*/
|
||||
public static native byte[] secp256k1_frost_share_agg(long ctx, byte[][] shares, byte[][][] vss_commitments, int totalShareCount, int threshold, byte[] id33);
|
||||
|
||||
/**
|
||||
* Verifies a share received during a key generation session
|
||||
*
|
||||
* The signature is verified against the VSS commitment received with the
|
||||
* share. This is only useful for purposes of determining which share(s) are
|
||||
* invalid if share_agg returns an error.
|
||||
*
|
||||
* @param ctx pointer to a context object (not secp256k1_context_static)
|
||||
* @param threshold the minimum number of signers required to produce a signature
|
||||
* @param id33 the 33-byte participant ID of the share recipient
|
||||
* @param share pointer to a key generation share
|
||||
* @param vss_commitment the VSS commitment associated with the share
|
||||
*
|
||||
* @return 0 if the arguments are invalid or the share does not verify, 1 otherwise
|
||||
*/
|
||||
public static native int secp256k1_frost_share_verify(long ctx, int threshold, byte[] id33, byte[] share, byte[][] vss_commitment);
|
||||
|
||||
/**
|
||||
* Computes a public verification share used for verifying partial signatures
|
||||
*
|
||||
* @param ctx pointer to a context object (not secp256k1_context_static)
|
||||
* @param threshold the minimum number of signers required to produce a signature
|
||||
* @param id33 the 33-byte participant ID of the participant whose partial signature will be verified with
|
||||
* the pubshare
|
||||
* @param vss_commitments coefficient commitments of all participants
|
||||
* @param totalSignersCount the total number of participants
|
||||
*
|
||||
* @return pubshare: pointer to a struct to store the public verification share
|
||||
*/
|
||||
public static native byte[] secp256k1_frost_compute_pubshare(long ctx, int threshold, byte[] id33, byte[][][] vss_commitments, int totalSignersCount);
|
||||
|
||||
/**
|
||||
* Initializes a tweak cache used for applying tweaks to a FROST key
|
||||
*
|
||||
* @param ctx pointer to a context object (not secp256k1_context_static)
|
||||
* @param public_key the aggregated x-only public key that is the output of `secp256k1_frost_share_agg`
|
||||
*
|
||||
* @return tweak_cache: pointer to a frost_tweak_cache struct that is required for key tweaking
|
||||
*/
|
||||
public static native byte[] secp256k1_frost_pubkey_tweak(long ctx, byte[] public_key);
|
||||
|
||||
/**
|
||||
* Apply ordinary "EC" tweaking to a public key in a given tweak_cache by
|
||||
* adding the generator multiplied with `tweak32` to it. This is useful for
|
||||
* deriving child keys from an aggregate public key via BIP32.
|
||||
*
|
||||
* The tweaking method is the same as `secp256k1_ec_pubkey_tweak_add`. So after
|
||||
* the following pseudocode buf and buf2 have identical contents (absent
|
||||
* earlier failures).
|
||||
*
|
||||
* secp256k1_frost_share_agg(..., xonly_agg_pk, ...)
|
||||
* secp256k1_frost_pubkey_tweak(..., tweak_cache, xonly_agg_pk)
|
||||
* secp256k1_frost_pubkey_ec_tweak_add(..., output_pk, tweak_cache, tweak32)
|
||||
* secp256k1_ec_pubkey_serialize(..., buf, output_pk)
|
||||
* secp256k1_frost_pubkey_get(..., ec_agg_pk, xonly_agg_pk)
|
||||
* secp256k1_ec_pubkey_tweak_add(..., ec_agg_pk, tweak32)
|
||||
* secp256k1_ec_pubkey_serialize(..., buf2, ec_agg_pk)
|
||||
*
|
||||
* This function is required if you want to _sign_ for a tweaked aggregate key.
|
||||
* On the other hand, if you are only computing a public key, but not intending
|
||||
* to create a signature for it, you can just use
|
||||
* `secp256k1_ec_pubkey_tweak_add`.
|
||||
*
|
||||
* @param ctx pointer to a context object (not secp256k1_context_static)
|
||||
* @param tweakCache pointer to a `frost_tweak_cache` struct initialized by `frost_pubkey_tweak`
|
||||
* @param tweak32 pointer to a 32-byte tweak. If the tweak is invalid according to `secp256k1_ec_seckey_verify`,
|
||||
* this function returns 0. For uniformly random 32-byte arrays the chance of being invalid
|
||||
* is negligible (around 1 in 2^128).
|
||||
*
|
||||
* @return output_pubkey: pointer to a public key to store the result. Will be set
|
||||
* to an invalid value if this function returns 0. If you
|
||||
* do not need it, this arg can be NULL.
|
||||
*/
|
||||
public static native byte[] secp256k1_frost_pubkey_ec_tweak_add(long ctx, byte[] tweakCache, byte[] tweak32);
|
||||
|
||||
/**
|
||||
* Apply x-only tweaking to a public key in a given tweak_cache by adding the
|
||||
* generator multiplied with `tweak32` to it. This is useful for creating
|
||||
* Taproot outputs.
|
||||
*
|
||||
* The tweaking method is the same as `secp256k1_xonly_pubkey_tweak_add`. So in
|
||||
* the following pseudocode xonly_pubkey_tweak_add_check (absent earlier
|
||||
* failures) returns 1.
|
||||
*
|
||||
* secp256k1_frost_share_agg(..., agg_pk, ...)
|
||||
* secp256k1_frost_pubkey_tweak(..., tweak_cache, agg_pk)
|
||||
* secp256k1_frost_pubkey_xonly_tweak_add(..., output_pk, tweak_cache, tweak32)
|
||||
* secp256k1_xonly_pubkey_serialize(..., buf, output_pk)
|
||||
* secp256k1_xonly_pubkey_tweak_add_check(..., buf, ..., agg_pk, tweak32)
|
||||
*
|
||||
* This function is required if you want to _sign_ for a tweaked aggregate key.
|
||||
* On the other hand, if you are only computing a public key, but not intending
|
||||
* to create a signature for it, you can just use
|
||||
* `secp256k1_xonly_pubkey_tweak_add`.
|
||||
*
|
||||
* @param ctx pointer to a context object (not secp256k1_context_static)
|
||||
* @param tweakCache pointer to a `frost_tweak_cache` struct initialized by `frost_pubkey_tweak`
|
||||
* @param tweak32 pointer to a 32-byte tweak. If the tweak is invalid according to secp256k1_ec_seckey_verify,
|
||||
* this function returns 0. For uniformly random 32-byte arrays the
|
||||
* chance of being invalid is negligible (around 1 in 2^128).
|
||||
*
|
||||
* @return
|
||||
* [0] output_pubkey: pointer to a public key to store the result. Will be set to an invalid value if this
|
||||
* function returns 0. If you do not need it, this arg can be NULL.
|
||||
* [1] tweak_cache: pointer to a `frost_tweak_cache` struct initialized by `frost_pubkey_tweak`
|
||||
*/
|
||||
public static native byte[] secp256k1_frost_pubkey_xonly_tweak_add(long ctx, byte[] tweakCache, byte[] tweak32);
|
||||
|
||||
/**
|
||||
* Starts a signing session by generating a nonce
|
||||
*
|
||||
* This function outputs a secret nonce that will be required for signing and a
|
||||
* corresponding public nonce that is intended to be sent to other signers.
|
||||
*
|
||||
* FROST, like MuSig, differs from regular Schnorr signing in that
|
||||
* implementers _must_ take special care to not reuse a nonce. This can be
|
||||
* ensured by following these rules:
|
||||
*
|
||||
* 1. Each call to this function must have a UNIQUE session_id32 that must NOT BE
|
||||
* REUSED in subsequent calls to this function.
|
||||
* If you do not provide a seckey, session_id32 _must_ be UNIFORMLY RANDOM
|
||||
* AND KEPT SECRET (even from other signers). If you do provide a seckey,
|
||||
* session_id32 can instead be a counter (that must never repeat!). However,
|
||||
* it is recommended to always choose session_id32 uniformly at random.
|
||||
* 2. If you already know the seckey, message or aggregate public key, they
|
||||
* can be optionally provided to derive the nonce and increase
|
||||
* misuse-resistance. The extra_input32 argument can be used to provide
|
||||
* additional data that does not repeat in normal scenarios, such as the
|
||||
* current time.
|
||||
* 3. Avoid copying (or serializing) the secnonce. This reduces the possibility
|
||||
* that it is used more than once for signing.
|
||||
*
|
||||
* Remember that nonce reuse will leak the secret key!
|
||||
* Note that using the same agg_share for multiple FROST sessions is fine.
|
||||
*
|
||||
* @param ctx pointer to a context object (not secp256k1_context_static)
|
||||
* @param sessionId32 a 32-byte session_id32 as explained above. Must be unique to this call to
|
||||
* secp256k1_frost_nonce_gen and must be uniformly random unless you really know what you
|
||||
* are doing.
|
||||
* @param share the aggregated share that will later be used for signing, if already known (can be NULL)
|
||||
* @param msg32 the 32-byte message that will later be signed, if already known (can be NULL)
|
||||
* @param publicKey the FROST-aggregated public key (can be NULL)
|
||||
* @param extraInput32 an optional 32-byte array that is input to the nonce derivation function (can be NULL)
|
||||
* @return
|
||||
* [0] secnonce: pointer to a structure to store the secret nonce
|
||||
* [1] pubnonce: pointer to a structure to store the public nonce
|
||||
*/
|
||||
public static native byte[] secp256k1_frost_nonce_gen(long ctx, byte[] sessionId32, byte[] share, byte[] msg32, byte[] publicKey, byte[] extraInput32);
|
||||
|
||||
/**
|
||||
* Takes the public nonces of all signers and computes a session that is
|
||||
* required for signing and verification of partial signatures. The participant
|
||||
* IDs can be sorted before combining, but the corresponding pubnonces must be
|
||||
* resorted as well. All signers must use the same sorting of pubnonces,
|
||||
* otherwise signing will fail.
|
||||
*
|
||||
* @param ctx pointer to a context object (not secp256k1_context_static)
|
||||
* @param pubnonces array of pointers to public nonces sent by the signers
|
||||
* @param msg32 the 32-byte message to sign
|
||||
* @param publicKey the FROST-aggregated public key
|
||||
* @param id33 the 33-byte ID of the participant who will use the session for signing
|
||||
* @param ids33 array of the 33-byte participant IDs of the signers
|
||||
* @param tweakCache pointer to frost_tweak_cache struct (can be NULL)
|
||||
* @param adaptor optional pointer to an adaptor point encoded as a public key if this signing session is part
|
||||
* of an adaptor signature protocol (can be NULL)
|
||||
*
|
||||
* @return session: pointer to a struct to store the session
|
||||
*/
|
||||
public static native byte[] secp256k1_frost_nonce_process(long ctx, byte[][] pubnonces, int n_pubnonces, byte[] msg32, byte[] publicKey, byte[] id33, byte[][] ids33, byte[] tweakCache, byte[] adaptor);
|
||||
|
||||
/**
|
||||
* Produces a partial signature
|
||||
*
|
||||
* This function overwrites the given secnonce with zeros and will abort if given a
|
||||
* secnonce that is all zeros. This is a best effort attempt to protect against nonce
|
||||
* reuse. However, this is of course easily defeated if the secnonce has been
|
||||
* copied (or serialized). Remember that nonce reuse will leak the secret key!
|
||||
*
|
||||
* @param ctx pointer to a context object (not secp256k1_context_static)
|
||||
* @param secnonce (IS ALSO OUTPUT)pointer to the secnonce struct created in frost_nonce_gen that has been never used in a
|
||||
* partial_sign call before
|
||||
* @param share the aggregated share
|
||||
* @param session pointer to the session that was created with frost_nonce_process
|
||||
* @param tweak_cache pointer to frost_tweak_cache struct (can be NULL)
|
||||
*
|
||||
* @return
|
||||
* partial_sig: pointer to struct to store the partial signature
|
||||
* TODO: [1] secnonce: pointer to the secnonce struct created in frost_nonce_gen that has been never used in a
|
||||
* partial_sign call before
|
||||
*
|
||||
*/
|
||||
public static native byte[] secp256k1_frost_partial_sign(long ctx, byte[] secnonce, byte[] share, byte[] session, byte[] tweak_cache);
|
||||
|
||||
/**
|
||||
* Verifies an individual signer's partial signature
|
||||
*
|
||||
* The signature is verified for a specific signing session. In order to avoid
|
||||
* accidentally verifying a signature from a different or non-existing signing
|
||||
* session, you must ensure the following:
|
||||
* 1. The `tweak_cache` argument is identical to the one used to create the
|
||||
* `session` with `frost_nonce_process`.
|
||||
* 2. The `pubshare` argument must be the output of
|
||||
* `secp256k1_frost_compute_pubshare` for the signer's ID.
|
||||
* 3. The `pubnonce` argument must be identical to the one sent by the
|
||||
* signer and used to create the `session` with `frost_nonce_process`.
|
||||
*
|
||||
* This function can be used to assign blame for a failed signature.
|
||||
*
|
||||
* @param ctx pointer to a context object (not secp256k1_context_static)
|
||||
* @param partialSig pointer to partial signature to verify, sent by the signer associated with `pubnonce` and `pubkey`
|
||||
* @param publicNonce public nonce of the signer in the signing session
|
||||
* @param publicShare public verification share of the signer in the signing session that is the output of
|
||||
* `secp256k1_frost_compute_pubshare`
|
||||
* @param session pointer to the session that was created with `frost_nonce_process`
|
||||
* @param tweakCache pointer to frost_tweak_cache struct (can be NULL)
|
||||
*
|
||||
* @return 0 if the arguments are invalid or the partial signature does not verify, 1 otherwise
|
||||
*/
|
||||
public static native int secp256k1_frost_partial_sig_verify(long ctx, byte[] partialSig, byte[] publicNonce, byte[] publicShare, byte[] session, byte[] tweakCache);
|
||||
|
||||
/**
|
||||
* Aggregates partial signatures
|
||||
*
|
||||
* @param ctx pointer to a context object (not secp256k1_context_static)
|
||||
* @param session pointer to the session that was created with frost_nonce_process
|
||||
* @param partialSignatures array of pointers to partial signatures to aggregate
|
||||
* @param n_sigs number of elements in the partial_sigs array. Must be greater than 0.
|
||||
*
|
||||
* @return sig64: complete (but possibly invalid) Schnorr signature
|
||||
*/
|
||||
public static native byte[] secp256k1_frost_partial_sig_agg(long ctx, byte[] session, byte[][] partialSignatures, int n_sigs);
|
||||
|
||||
}
|
||||
|
@ -92,6 +92,248 @@ 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 {
|
||||
require(musigNonceValidate(secnonce, pubkeyCreate(privkey)))
|
||||
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 frostSharesGen(
|
||||
seed32: ByteArray,
|
||||
threshold: Int,
|
||||
totalSigners: Int,
|
||||
ids33: Array<ByteArray>
|
||||
): Triple<Array<ByteArray>, Array<ByteArray>, ByteArray> {
|
||||
val pok64 = ByteArray(64)
|
||||
|
||||
val result = Secp256k1CFunctions.secp256k1_frost_shares_gen(
|
||||
Secp256k1Context.getContext(),
|
||||
pok64,
|
||||
seed32,
|
||||
threshold,
|
||||
totalSigners,
|
||||
ids33
|
||||
)
|
||||
println(Hex.encode(result))
|
||||
|
||||
val shares = Array(totalSigners) { index ->
|
||||
val startIndex = 0 + (index*32);
|
||||
val endIndex = 31 + (index*32);
|
||||
|
||||
result.sliceArray(
|
||||
startIndex..endIndex
|
||||
)
|
||||
}
|
||||
|
||||
val sharesOffset = totalSigners*32;
|
||||
|
||||
val vssCommitment = Array(threshold) { index ->
|
||||
val startIndex = sharesOffset + (index*65);
|
||||
val endIndex = sharesOffset + 64 + (index*65);
|
||||
|
||||
result.sliceArray(
|
||||
startIndex..endIndex
|
||||
)
|
||||
}
|
||||
|
||||
return Triple(
|
||||
shares,
|
||||
vssCommitment,
|
||||
pok64
|
||||
)
|
||||
}
|
||||
|
||||
override fun frostShareAggregate(
|
||||
totalShares: Array<ByteArray>,
|
||||
vssCommitments: Array<Array<ByteArray>>,
|
||||
totalShareCount: Int,
|
||||
threshold: Int,
|
||||
id33: ByteArray
|
||||
): Pair<ByteArray, ByteArray> {
|
||||
val result = Secp256k1CFunctions.secp256k1_frost_share_agg(
|
||||
Secp256k1Context.getContext(),
|
||||
totalShares,
|
||||
vssCommitments,
|
||||
totalShareCount,
|
||||
threshold,
|
||||
id33
|
||||
)
|
||||
return Pair(
|
||||
result.take(Secp256k1.FROST_SERIALIZED_SHARE_SIZE).toByteArray(), // agg_share
|
||||
result.takeLast(Secp256k1.SERIALIZED_X_ONLY_PUBKEY_SIZE).toByteArray() // agg_pk
|
||||
)
|
||||
}
|
||||
|
||||
override fun frostShareVerify(
|
||||
threshold: Int,
|
||||
id33: ByteArray,
|
||||
share: ByteArray,
|
||||
vssCommitment: Array<ByteArray>
|
||||
): Int {
|
||||
return Secp256k1CFunctions.secp256k1_frost_share_verify(
|
||||
Secp256k1Context.getContext(),
|
||||
threshold,
|
||||
id33,
|
||||
share,
|
||||
vssCommitment
|
||||
)
|
||||
}
|
||||
|
||||
override fun frostComputePublicShare(
|
||||
threshold: Int,
|
||||
id33: ByteArray,
|
||||
vssCommitments: Array<Array<ByteArray>>,
|
||||
totalSignersCount: Int
|
||||
): ByteArray {
|
||||
return Secp256k1CFunctions.secp256k1_frost_compute_pubshare(
|
||||
Secp256k1Context.getContext(),
|
||||
threshold,
|
||||
id33,
|
||||
vssCommitments,
|
||||
totalSignersCount
|
||||
)
|
||||
}
|
||||
|
||||
override fun frostPublicKeyTweak(xOnlyPublicKey: ByteArray): ByteArray {
|
||||
return Secp256k1CFunctions.secp256k1_frost_pubkey_tweak(
|
||||
Secp256k1Context.getContext(),
|
||||
xOnlyPublicKey
|
||||
)
|
||||
}
|
||||
|
||||
override fun frostPublicKeyEcTweakAdd(tweakCache: ByteArray, tweak32: ByteArray): ByteArray {
|
||||
return Secp256k1CFunctions.secp256k1_frost_pubkey_ec_tweak_add(
|
||||
Secp256k1Context.getContext(),
|
||||
tweakCache,
|
||||
tweak32
|
||||
)
|
||||
}
|
||||
|
||||
override fun frostPublicKeyXonlyTweakAdd(tweakCache: ByteArray, tweak32: ByteArray): ByteArray? {
|
||||
return Secp256k1CFunctions.secp256k1_frost_pubkey_xonly_tweak_add(
|
||||
Secp256k1Context.getContext(),
|
||||
tweakCache,
|
||||
tweak32
|
||||
)
|
||||
}
|
||||
|
||||
override fun frostNonceGen(
|
||||
sessionId32: ByteArray,
|
||||
share: ByteArray?,
|
||||
msg32: ByteArray?,
|
||||
publicKey: ByteArray?,
|
||||
extraInput32: ByteArray?
|
||||
): Pair<ByteArray, ByteArray> {
|
||||
val result = Secp256k1CFunctions.secp256k1_frost_nonce_gen(
|
||||
Secp256k1Context.getContext(),
|
||||
sessionId32,
|
||||
share,
|
||||
msg32,
|
||||
publicKey,
|
||||
extraInput32
|
||||
)
|
||||
|
||||
return Pair(
|
||||
result.take(Secp256k1.FROST_SECNONCE_SIZE).toByteArray(), // secnonce
|
||||
result.takeLast(Secp256k1.FROST_SERIALIZED_PUBNONCE_SIZE).toByteArray() // pubnonce
|
||||
)
|
||||
}
|
||||
|
||||
override fun frostNonceProcess(
|
||||
publicNonces: Array<ByteArray>,
|
||||
threshold: Int,
|
||||
msg32: ByteArray,
|
||||
publicKey: ByteArray,
|
||||
id33: ByteArray,
|
||||
ids33: Array<ByteArray>,
|
||||
tweakCache: ByteArray?,
|
||||
adaptor: ByteArray?
|
||||
): ByteArray {
|
||||
return Secp256k1CFunctions.secp256k1_frost_nonce_process(
|
||||
Secp256k1Context.getContext(),
|
||||
publicNonces,
|
||||
threshold,
|
||||
msg32,
|
||||
publicKey,
|
||||
id33,
|
||||
ids33,
|
||||
tweakCache,
|
||||
adaptor
|
||||
)
|
||||
}
|
||||
|
||||
override fun frostPartialSign(
|
||||
secnonce: ByteArray,
|
||||
share: ByteArray,
|
||||
session: ByteArray,
|
||||
tweakCache: ByteArray?
|
||||
): ByteArray {
|
||||
return Secp256k1CFunctions.secp256k1_frost_partial_sign(
|
||||
Secp256k1Context.getContext(),
|
||||
secnonce,
|
||||
share,
|
||||
session,
|
||||
tweakCache
|
||||
)
|
||||
}
|
||||
|
||||
override fun frostPartialSignatureVerify(
|
||||
partialSig: ByteArray,
|
||||
publicNonce: ByteArray,
|
||||
publicShare: ByteArray,
|
||||
session: ByteArray,
|
||||
tweakCache: ByteArray?
|
||||
): Int {
|
||||
return Secp256k1CFunctions.secp256k1_frost_partial_sig_verify(
|
||||
Secp256k1Context.getContext(),
|
||||
partialSig,
|
||||
publicNonce,
|
||||
publicShare,
|
||||
session,
|
||||
tweakCache
|
||||
)
|
||||
}
|
||||
|
||||
override fun frostPartialSignatureAggregate(session: ByteArray, partialSignatures: Array<ByteArray>, threshold: Int): ByteArray {
|
||||
return Secp256k1CFunctions.secp256k1_frost_partial_sig_agg(
|
||||
Secp256k1Context.getContext(),
|
||||
session,
|
||||
partialSignatures,
|
||||
threshold
|
||||
)
|
||||
}
|
||||
|
||||
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-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-module-musig --enable-module-frost --enable-benchmark=no --enable-shared=no --enable-exhaustive-tests=no --enable-tests=no
|
||||
make clean
|
||||
make
|
||||
|
||||
|
@ -6,10 +6,13 @@ cp xconfigure.sh secp256k1
|
||||
cd secp256k1
|
||||
|
||||
./autogen.sh
|
||||
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
|
||||
sh xconfigure.sh --enable-experimental --enable-module_ecdh --enable-module-recovery --enable-module-schnorrsig --enable-module-musig --enable-module-frost --enable-benchmark=no --enable-shared=no --enable-exhaustive-tests=no --enable-tests=no
|
||||
|
||||
mkdir -p ../build/ios
|
||||
cp -v _build/universal/* ../build/ios/
|
||||
cp -v _build/universal/ios/* ../build/ios/
|
||||
|
||||
mkdir -p ../build/iosSimulatorArm64
|
||||
cp -v _build/universal/iosSimulatorArm64/* ../build/iosSimulatorArm64/
|
||||
|
||||
rm -rf _build
|
||||
make clean
|
||||
|
@ -12,18 +12,20 @@ cd "$(dirname "$0")"
|
||||
cd secp256k1
|
||||
|
||||
if [ "$TARGET" == "mingw" ]; then
|
||||
CONF_OPTS="CFLAGS=-fPIC --host=x86_64-w64-mingw32"
|
||||
CFLAGS="-fPIC"
|
||||
CONF_OPTS=" --host=x86_64-w64-mingw32"
|
||||
elif [ "$TARGET" == "linux" ]; then
|
||||
CONF_OPTS="CFLAGS=-fPIC"
|
||||
CFLAGS="-fPIC"
|
||||
elif [ "$TARGET" == "darwin" ]; then
|
||||
CONF_OPTS=""
|
||||
CFLAGS="-arch arm64 -arch x86_64"
|
||||
LDFLAGS="-arch arm64 -arch x86_64"
|
||||
else
|
||||
echo "Unknown TARGET=$TARGET"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
./autogen.sh
|
||||
./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
|
||||
CFLAGS="$CFLAGS" LDFLAGS="$LDFLAGS" ./configure $CONF_OPTS --enable-experimental --enable-module_ecdh --enable-module-recovery --enable-module-schnorrsig --enable-module-musig --enable-module-frost --enable-benchmark=no --enable-shared=no --enable-exhaustive-tests=no --enable-tests=no
|
||||
make clean
|
||||
make
|
||||
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 199d27cea32203b224b208627533c2e813cd3b21
|
||||
Subproject commit 69d5e6bdc1ec8b472138aaba9804b93d46f397bb
|
@ -69,9 +69,22 @@ 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}" &> /dev/null
|
||||
lipo -create -output "${UNIVERSAL}/${LIB_NAME}" "${PLATFORMS}/${PLATFORM_ARM}/lib/${LIB_NAME}" "${PLATFORMS}/${PLATFORM_ISIM}/lib/${LIB_NAME}"
|
||||
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}"
|
@ -1,5 +1,10 @@
|
||||
#!/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,5 +1,10 @@
|
||||
#!/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,45 +21,52 @@ 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 iosx64 jni-android jni-common jni-jvm-darwin jni-jvm-extract jni-jvm-linux jni-jvm-mingw jni-jvm jvm linux; do
|
||||
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
|
||||
cd fr/acinq/secp256k1/secp256k1-kmp-$i/$VERSION
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
popd
|
||||
pushd .
|
||||
done
|
||||
|
@ -3,9 +3,22 @@
|
||||
# first you must sign all files:
|
||||
# find release -type f -print -exec gpg -ab {} \;
|
||||
|
||||
VERSION=0.6.2
|
||||
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
|
||||
|
||||
|
||||
for i in secp256k1-kmp \
|
||||
secp256k1-kmp-iosarm64 \
|
||||
secp256k1-kmp-iossimulatorarm64 \
|
||||
secp256k1-kmp-iosx64 \
|
||||
secp256k1-kmp-jni-android \
|
||||
secp256k1-kmp-jni-common \
|
||||
@ -15,14 +28,14 @@ for i in secp256k1-kmp \
|
||||
secp256k1-kmp-jni-jvm-linux \
|
||||
secp256k1-kmp-jni-jvm-mingw \
|
||||
secp256k1-kmp-jvm \
|
||||
secp256k1-kmp-linux
|
||||
secp256k1-kmp-linuxx64
|
||||
do
|
||||
pushd .
|
||||
cd release/fr/acinq/secp256k1/$i/$VERSION
|
||||
pwd
|
||||
jar -cvf bundle.jar *
|
||||
# use correct sonatype credentials here
|
||||
curl -v -XPOST -u USER:PASSWORD --upload-file bundle.jar https://oss.sonatype.org/service/local/staging/bundle_upload
|
||||
curl -v -XPOST -u $OSS_USER:$OSS_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,10 +149,166 @@ 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
|
||||
|
||||
/**
|
||||
* Check that a secret nonce was generated with a public key that matches the private key used for signing.
|
||||
* @param secretnonce secret nonce.
|
||||
* @param pubkey public key for the private key that will be used, with the secret nonce, to generate a partial signature.
|
||||
* @return false if the secret nonce does not match the public key.
|
||||
*/
|
||||
public fun musigNonceValidate(secretnonce: ByteArray, pubkey: ByteArray): Boolean {
|
||||
if (secretnonce.size != MUSIG2_SECRET_NONCE_SIZE) return false
|
||||
if (pubkey.size != 33 && pubkey.size != 65) return false
|
||||
val pk = Secp256k1.pubkeyParse(pubkey)
|
||||
// this is a bit hackish but the secp256k1 library does not export methods to do this cleanly
|
||||
val x = secretnonce.copyOfRange(68, 68 + 32)
|
||||
x.reverse()
|
||||
val y = secretnonce.copyOfRange(68 + 32, 68 + 32 + 32)
|
||||
y.reverse()
|
||||
val pkx = pk.copyOfRange(1, 1 + 32)
|
||||
val pky = pk.copyOfRange(33, 33 + 32)
|
||||
return x.contentEquals(pkx) && y.contentEquals(pky)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
public fun frostSharesGen(seed32: ByteArray, threshold: Int, totalSigners: Int, ids33: Array<ByteArray>): Triple<Array<ByteArray>,Array<ByteArray>, ByteArray>
|
||||
|
||||
/**
|
||||
*
|
||||
* total signers is deduced from the size of the totalShares array.
|
||||
* threshold is deduced from the vssCommitments (all vssCommitments should have the same length... which is the threshold)
|
||||
*/
|
||||
public fun frostShareAggregate(totalShares: Array<ByteArray>, vssCommitments: Array<Array<ByteArray>>, totalShareCount: Int, threshold: Int, id33: ByteArray): Pair<ByteArray, ByteArray>
|
||||
|
||||
public fun frostShareVerify(threshold: Int, id33: ByteArray, share: ByteArray, vssCommitment: Array<ByteArray>): Int
|
||||
|
||||
/**
|
||||
*
|
||||
* total signers is deduced from the size of the vssCommitments array.
|
||||
* threshold is deduced from the vssCommitments (all vssCommitments should have the same length... which is the threshold)
|
||||
*/
|
||||
public fun frostComputePublicShare(threshold: Int, id33: ByteArray, vssCommitments: Array<Array<ByteArray>>, totalSignersCount: Int): ByteArray
|
||||
|
||||
public fun frostPublicKeyTweak(xOnlyPublicKey: ByteArray): ByteArray
|
||||
|
||||
public fun frostPublicKeyEcTweakAdd(tweakCache: ByteArray, tweak32: ByteArray): ByteArray?
|
||||
|
||||
public fun frostPublicKeyXonlyTweakAdd(tweakCache: ByteArray, tweak32: ByteArray): ByteArray?
|
||||
|
||||
public fun frostNonceGen(sessionId32: ByteArray, share: ByteArray?, msg32: ByteArray?, publicKey: ByteArray?, extraInput32: ByteArray?): Pair<ByteArray, ByteArray>
|
||||
|
||||
/**
|
||||
*
|
||||
* threshold can be deduced from the size of the pubnonces array.
|
||||
*/
|
||||
public fun frostNonceProcess(publicNonces: Array<ByteArray>, threshold: Int, msg32: ByteArray, publicKey: ByteArray, id33: ByteArray, ids33: Array<ByteArray>, tweakCache: ByteArray?, adaptor: ByteArray?): ByteArray
|
||||
|
||||
public fun frostPartialSign(secnonce: ByteArray, share: ByteArray, session: ByteArray, tweakCache: ByteArray?): ByteArray
|
||||
|
||||
public fun frostPartialSignatureVerify(partialSig: ByteArray, publicNonce: ByteArray, publicShare: ByteArray, session: ByteArray, tweakCache: ByteArray?): Int
|
||||
|
||||
public fun frostPartialSignatureAggregate(session: ByteArray, partialSignatures: Array<ByteArray>, threshold: Int): ByteArray
|
||||
|
||||
/**
|
||||
* Delete the secp256k1 context from dynamic memory.
|
||||
*/
|
||||
@ -161,6 +317,27 @@ public interface Secp256k1 {
|
||||
public companion object : Secp256k1 by getSecpk256k1() {
|
||||
@JvmStatic
|
||||
public fun get(): Secp256k1 = this
|
||||
|
||||
// @formatter:off
|
||||
public const val X_ONLY_PUBKEY_SIZE: Int = 64
|
||||
public const val SERIALIZED_X_ONLY_PUBKEY_SIZE: Int = 32
|
||||
|
||||
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
|
||||
|
||||
public const val FROST_PARTIAL_SIGNATURE_SIZE: Int = 36
|
||||
|
||||
public const val FROST_SHARE_SIZE: Int = 36
|
||||
public const val FROST_TWEAK_CACHE_SIZE: Int = 101
|
||||
public const val FROST_SESSION_SIZE: Int = 133
|
||||
public const val FROST_SECNONCE_SIZE: Int = 68
|
||||
public const val FROST_PUBNONCE_SIZE: Int = 132
|
||||
public const val FROST_SERIALIZED_PARTIAL_SIGNATURE_SIZE: Int = 32
|
||||
public const val FROST_SERIALIZED_SHARE_SIZE: Int = 32
|
||||
public const val FROST_SERIALIZED_PUBNONCE_SIZE: Int = 66
|
||||
// @formatter:on
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,22 @@
|
||||
package = secp256k1
|
||||
|
||||
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
|
||||
headers = secp256k1.h secp256k1_ecdh.h secp256k1_recovery.h secp256k1_extrakeys.h secp256k1_schnorrsig.h secp256k1_musig.h secp256k1_frost.h
|
||||
headerFilter = secp256k1/** secp256k1_ecdh.h secp256k1_recovery.h secp256k1_extrakeys.h secp256k1_schnorrsig.h secp256k1_musig.h secp256k1_frost.h secp256k1.h
|
||||
|
||||
libraryPaths.linux = c/secp256k1/build/linux/
|
||||
staticLibraries.linux = libsecp256k1.a
|
||||
libraryPaths.linux = c/secp256k1/build/linux/ native/build/linux/ native/build/darwin/
|
||||
linkerOpts.linux = -L/usr/lib64 -L/usr/lib/x86_64-linux-gnu -L/usr/local/lib
|
||||
|
||||
libraryPaths.ios = c/secp256k1/build/ios/ /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/
|
||||
linkerOpts.ios = -framework Security -framework Foundation
|
||||
|
@ -1,10 +1,13 @@
|
||||
package fr.acinq.secp256k1
|
||||
|
||||
import fr.acinq.secp256k1.Secp256k1Native.toNat
|
||||
import kotlinx.cinterop.*
|
||||
import kotlinx.cinterop.ptr
|
||||
import platform.posix.memcpy
|
||||
import platform.posix.size_tVar
|
||||
import secp256k1.*
|
||||
|
||||
@OptIn(ExperimentalUnsignedTypes::class)
|
||||
@OptIn(ExperimentalUnsignedTypes::class, ExperimentalForeignApi::class)
|
||||
public object Secp256k1Native : Secp256k1 {
|
||||
|
||||
private val ctx: CPointer<secp256k1_context> by lazy {
|
||||
@ -40,6 +43,27 @@ public object Secp256k1Native : Secp256k1 {
|
||||
return pub
|
||||
}
|
||||
|
||||
private fun MemScope.allocXonlyPublicKey(pubkey: ByteArray): secp256k1_xonly_pubkey {
|
||||
val natPub = toNat(pubkey)
|
||||
val pub = alloc<secp256k1_xonly_pubkey>()
|
||||
secp256k1_xonly_pubkey_parse(ctx, pub.ptr, natPub).requireSuccess("secp256k1_xonly_pubkey_parse() failed")
|
||||
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>()
|
||||
@ -48,7 +72,25 @@ public object Secp256k1Native : Secp256k1 {
|
||||
return serialized.readBytes(outputLen.value.convert())
|
||||
}
|
||||
|
||||
private fun DeferScope.toNat(bytes: ByteArray): CPointer<UByteVar> {
|
||||
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()
|
||||
this.defer { pinned.unpin() }
|
||||
@ -79,7 +121,7 @@ public object Secp256k1Native : Secp256k1 {
|
||||
}
|
||||
|
||||
public override fun signatureNormalize(sig: ByteArray): Pair<ByteArray, Boolean> {
|
||||
require(sig.size >= 64){ "invalid signature ${Hex.encode(sig)}" }
|
||||
require(sig.size >= 64) { "invalid signature ${Hex.encode(sig)}" }
|
||||
memScoped {
|
||||
val nSig = allocSignature(sig)
|
||||
val isHighS = secp256k1_ecdsa_signature_normalize(ctx, nSig.ptr, nSig.ptr)
|
||||
@ -125,6 +167,7 @@ public object Secp256k1Native : Secp256k1 {
|
||||
|
||||
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)
|
||||
@ -136,6 +179,7 @@ public object Secp256k1Native : Secp256k1 {
|
||||
|
||||
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)
|
||||
@ -156,6 +200,7 @@ public object Secp256k1Native : Secp256k1 {
|
||||
|
||||
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)
|
||||
@ -166,6 +211,7 @@ public object Secp256k1Native : Secp256k1 {
|
||||
|
||||
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)
|
||||
@ -175,6 +221,7 @@ public object Secp256k1Native : Secp256k1 {
|
||||
}
|
||||
|
||||
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 }
|
||||
@ -199,6 +246,7 @@ public object Secp256k1Native : Secp256k1 {
|
||||
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>()
|
||||
@ -232,7 +280,7 @@ public object Secp256k1Native : Secp256k1 {
|
||||
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, 32, pubkey.ptr) == 1
|
||||
return secp256k1_schnorrsig_verify(ctx, nSig, nData, 32u, pubkey.ptr) == 1
|
||||
}
|
||||
}
|
||||
|
||||
@ -252,11 +300,698 @@ public object Secp256k1Native : Secp256k1 {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
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, null, 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, null).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)
|
||||
require(musigNonceValidate(secnonce, pubkeyCreate(privkey)))
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
private fun MemScope.allocFrostShare(share: ByteArray): secp256k1_frost_share {
|
||||
val nat = toNat(share)
|
||||
val nFrostShare = alloc<secp256k1_frost_share>()
|
||||
secp256k1_frost_share_parse(ctx, nFrostShare.ptr, nat).requireSuccess("secp256k1_frost_share_parse() failed")
|
||||
return nFrostShare
|
||||
}
|
||||
|
||||
private fun MemScope.allocFrostPartialSignature(partialSignature: ByteArray): secp256k1_frost_partial_sig {
|
||||
val nat = toNat(partialSignature)
|
||||
val nPartialSignature = alloc<secp256k1_frost_partial_sig>()
|
||||
secp256k1_frost_partial_sig_parse(ctx, nPartialSignature.ptr, nat).requireSuccess("secp256k1_frost_partial_sig_parse() failed")
|
||||
return nPartialSignature
|
||||
}
|
||||
|
||||
private fun MemScope.allocFrostPublicNonce(pubnonce: ByteArray): secp256k1_frost_pubnonce {
|
||||
val nat = toNat(pubnonce)
|
||||
val nPublicNonce = alloc<secp256k1_frost_pubnonce>()
|
||||
secp256k1_frost_pubnonce_parse(ctx, nPublicNonce.ptr, nat).requireSuccess("secp256k1_frost_pubnonce_parse() failed")
|
||||
return nPublicNonce
|
||||
}
|
||||
|
||||
override fun frostSharesGen(
|
||||
seed32: ByteArray,
|
||||
threshold: Int,
|
||||
totalSigners: Int,
|
||||
ids33: Array<ByteArray>
|
||||
): Triple<Array<ByteArray>, Array<ByteArray>, ByteArray> {
|
||||
require(seed32.size == 32) { "seed size should be 32" }
|
||||
require(threshold > 1) { "threshold cannot be less then 1" }
|
||||
require(threshold <= totalSigners) { "threshold($threshold) cannot be greater then totalSigners ($totalSigners)" }
|
||||
require(ids33.size == totalSigners) { "ids33 size need to be the same as totalSigners size" }
|
||||
ids33.forEach { require(it.size == 33) { "id33 size must be 33" } }
|
||||
|
||||
memScoped {
|
||||
val nShares = allocArray<secp256k1_frost_share>(ids33.size)
|
||||
val nVssCommitment = allocArray<secp256k1_pubkey>(threshold)
|
||||
val pok64 = ByteArray(64)
|
||||
|
||||
val nIds33s = ids33.map { toNat(it) }
|
||||
|
||||
secp256k1_frost_shares_gen(
|
||||
ctx = ctx,
|
||||
shares = nShares,
|
||||
vss_commitment = nVssCommitment,
|
||||
pok64 = toNat(pok64),
|
||||
seed32 = toNat(seed32),
|
||||
threshold = threshold.convert(),
|
||||
n_participants = ids33.size.convert(),
|
||||
ids33 = nIds33s.toCValues()
|
||||
)
|
||||
|
||||
return Triple(
|
||||
ids33.indices.map { serializeFrostShare(nShares[it]) }.toTypedArray(),
|
||||
(0 until threshold).map { serializePubkey(nVssCommitment[it]) }.toTypedArray(),
|
||||
pok64
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MemScope.serializeFrostShare(nFrostShare: secp256k1_frost_share): ByteArray {
|
||||
val natOutput = allocArray<UByteVar>(Secp256k1.FROST_SERIALIZED_SHARE_SIZE)
|
||||
secp256k1_frost_share_serialize(ctx, natOutput, nFrostShare.ptr).requireSuccess("secp256k1_frost_share_serialize() failed")
|
||||
return natOutput.readBytes(Secp256k1.FROST_SERIALIZED_SHARE_SIZE)
|
||||
}
|
||||
|
||||
override fun frostShareAggregate(
|
||||
totalShares: Array<ByteArray>,
|
||||
vssCommitments: Array<Array<ByteArray>>,
|
||||
totalShareCount: Int,
|
||||
threshold: Int,
|
||||
id33: ByteArray
|
||||
): Pair<ByteArray, ByteArray> {
|
||||
require(totalShares.size == totalShareCount) { "totalShare array size (${totalShares.size}) should be the same as total share count ($totalShareCount)" }
|
||||
totalShares.forEach { require(it.size == 32) { "all shares should be of size 32" } }
|
||||
require(vssCommitments.size == totalShareCount) { "vssCommitments array size ${vssCommitments.size} should be the same as total share count ($totalShareCount)" }
|
||||
vssCommitments.forEach { vssCommitment ->
|
||||
require(vssCommitment.size == threshold) { "all vss commitment array size (${vssCommitment.size}) should be the same as the threshold size ($threshold)" }
|
||||
vssCommitment.forEach { publicKey ->
|
||||
require(publicKey.size == 33 || publicKey.size == 65) { "vss commitment data size should be 33 or 65" }
|
||||
}
|
||||
}
|
||||
require(threshold > 1) { "threshold should be greater then 1" }
|
||||
require(threshold <= totalShareCount) { "Threshold can not be greater then the total share count" }
|
||||
require(id33.size == 33) { "id size should be 33" }
|
||||
|
||||
memScoped {
|
||||
val nAggregateShare = alloc<secp256k1_frost_share>()
|
||||
val nAggregatePublicKey = alloc<secp256k1_xonly_pubkey>()
|
||||
|
||||
val nTotalShares = totalShares.map { allocFrostShare(it).ptr }
|
||||
|
||||
val nVssCommitments = allocArray<CPointerVar<secp256k1_pubkey>>(vssCommitments.size)
|
||||
|
||||
vssCommitments.forEachIndexed { index, commitments ->
|
||||
val pubkeyArray = allocArray<secp256k1_pubkey>(commitments.size)
|
||||
commitments.forEachIndexed { commitmentIndex, pubkeyData ->
|
||||
if (secp256k1_ec_pubkey_parse(ctx, pubkeyArray[commitmentIndex].ptr, toNat(pubkeyData), pubkeyData.size.convert()) == 0) {
|
||||
error("Failed to parse public key")
|
||||
}
|
||||
}
|
||||
nVssCommitments[index] = pubkeyArray
|
||||
}
|
||||
|
||||
val result = secp256k1_frost_share_agg(
|
||||
ctx = ctx,
|
||||
agg_share = nAggregateShare.ptr,
|
||||
agg_pk = nAggregatePublicKey.ptr,
|
||||
shares = nTotalShares.toCValues(),
|
||||
vss_commitments = nVssCommitments,
|
||||
n_shares = totalShareCount.convert(),
|
||||
threshold = threshold.convert(),
|
||||
id33 = toNat(id33)
|
||||
)
|
||||
println("Aggregate Result: $result")
|
||||
|
||||
return Pair(
|
||||
serializeFrostShare(nAggregateShare),
|
||||
serializeXonlyPubkey(nAggregatePublicKey)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun frostShareVerify(
|
||||
threshold: Int,
|
||||
id33: ByteArray,
|
||||
share: ByteArray,
|
||||
vssCommitment: Array<ByteArray>
|
||||
): Int {
|
||||
require(threshold > 1) { "threshold should be greater then 1" }
|
||||
require(id33.size == 33) { "id size (${id33.size}) should be 33" }
|
||||
require(share.size == Secp256k1.FROST_SERIALIZED_SHARE_SIZE) { "share size (${share.size}) should be of size ${Secp256k1.FROST_SERIALIZED_SHARE_SIZE}" }
|
||||
|
||||
require(vssCommitment.size == threshold) { "vss commitment array size (${vssCommitment.size}) should be the same as the threshold size ($threshold)" }
|
||||
vssCommitment.forEach { publicKey ->
|
||||
require(publicKey.size == 33 || publicKey.size == 65) { "vss commitment data size should be 33 or 65" }
|
||||
}
|
||||
|
||||
memScoped {
|
||||
val nFrostShare = allocFrostShare(share)
|
||||
val nVssCommitments = allocArray<CPointerVar<secp256k1_pubkey>>(1)
|
||||
|
||||
val pubkeyArray = allocArray<secp256k1_pubkey>(vssCommitment.size)
|
||||
vssCommitment.forEachIndexed { commitmentIndex, pubkeyData ->
|
||||
if (secp256k1_ec_pubkey_parse(ctx, pubkeyArray[commitmentIndex].ptr, toNat(pubkeyData), pubkeyData.size.convert()) == 0) {
|
||||
error("Failed to parse public key")
|
||||
}
|
||||
}
|
||||
nVssCommitments[0] = pubkeyArray
|
||||
|
||||
return secp256k1_frost_share_verify(
|
||||
ctx = ctx,
|
||||
threshold = threshold.convert(),
|
||||
id33 = toNat(id33),
|
||||
share = nFrostShare.ptr,
|
||||
vss_commitment = nVssCommitments
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun frostComputePublicShare(
|
||||
threshold: Int,
|
||||
id33: ByteArray,
|
||||
vssCommitments: Array<Array<ByteArray>>,
|
||||
totalSignersCount: Int
|
||||
): ByteArray {
|
||||
require(threshold > 1)
|
||||
require(threshold <= totalSignersCount)
|
||||
require(id33.size == 33)
|
||||
|
||||
require(vssCommitments.size == totalSignersCount)
|
||||
vssCommitments.forEach { vssCommitment ->
|
||||
require(vssCommitment.size == threshold)
|
||||
vssCommitment.forEach { publicKey ->
|
||||
require(publicKey.size == 33 || publicKey.size == 65)
|
||||
}
|
||||
}
|
||||
|
||||
memScoped {
|
||||
val nPublicShare = alloc<secp256k1_pubkey>()
|
||||
|
||||
val nVssCommitments = allocArray<CPointerVar<secp256k1_pubkey>>(vssCommitments.size)
|
||||
|
||||
vssCommitments.forEachIndexed { index, commitments ->
|
||||
val pubkeyArray = allocArray<secp256k1_pubkey>(commitments.size)
|
||||
commitments.forEachIndexed { commitmentIndex, pubkeyData ->
|
||||
if (secp256k1_ec_pubkey_parse(ctx, pubkeyArray[commitmentIndex].ptr, toNat(pubkeyData), pubkeyData.size.convert()) == 0) {
|
||||
error("Failed to parse public key")
|
||||
}
|
||||
}
|
||||
nVssCommitments[index] = pubkeyArray
|
||||
}
|
||||
|
||||
val result = secp256k1_frost_compute_pubshare(
|
||||
ctx = ctx,
|
||||
pubshare = nPublicShare.ptr,
|
||||
threshold = threshold.convert(),
|
||||
id33 = toNat(id33),
|
||||
vss_commitments = nVssCommitments,
|
||||
n_participants = totalSignersCount.convert()
|
||||
)
|
||||
|
||||
println("Compute pubshare result: $result")
|
||||
|
||||
return serializePubkey(nPublicShare)
|
||||
}
|
||||
}
|
||||
|
||||
override fun frostPublicKeyTweak(xOnlyPublicKey: ByteArray): ByteArray {
|
||||
require(xOnlyPublicKey.size == Secp256k1.SERIALIZED_X_ONLY_PUBKEY_SIZE) { "pubkey size (${xOnlyPublicKey.size}) should be ${Secp256k1.SERIALIZED_X_ONLY_PUBKEY_SIZE}" }
|
||||
|
||||
memScoped {
|
||||
val nTweakCache = alloc<secp256k1_frost_tweak_cache>()
|
||||
val nPublicKey = allocXonlyPublicKey(xOnlyPublicKey)
|
||||
|
||||
secp256k1_frost_pubkey_tweak(
|
||||
ctx = ctx,
|
||||
tweak_cache = nTweakCache.ptr,
|
||||
agg_pk = nPublicKey.ptr
|
||||
)
|
||||
|
||||
return serializeFrostTweakCache(nTweakCache)
|
||||
}
|
||||
}
|
||||
|
||||
override fun frostPublicKeyEcTweakAdd(tweakCache: ByteArray, tweak32: ByteArray): ByteArray {
|
||||
require(tweakCache.size == Secp256k1.FROST_TWEAK_CACHE_SIZE)
|
||||
require(tweak32.size == 32)
|
||||
|
||||
memScoped {
|
||||
val nTweakCache = alloc<secp256k1_frost_tweak_cache>()
|
||||
memcpy(nTweakCache.ptr, toNat(tweakCache), Secp256k1.FROST_TWEAK_CACHE_SIZE.toULong())
|
||||
val nPublicKey = alloc<secp256k1_pubkey>()
|
||||
|
||||
secp256k1_frost_pubkey_ec_tweak_add(
|
||||
ctx = ctx,
|
||||
output_pubkey = nPublicKey.ptr,
|
||||
tweak_cache = nTweakCache.ptr,
|
||||
tweak32 = toNat(tweak32)
|
||||
)
|
||||
return serializePubkey(nPublicKey)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MemScope.serializeFrostTweakCache(nTweakCache: secp256k1_frost_tweak_cache): ByteArray {
|
||||
val natOutput = ByteArray(Secp256k1.FROST_TWEAK_CACHE_SIZE)
|
||||
memcpy(toNat(natOutput), nTweakCache.ptr, Secp256k1.FROST_TWEAK_CACHE_SIZE.toULong())
|
||||
return natOutput
|
||||
}
|
||||
|
||||
override fun frostPublicKeyXonlyTweakAdd(tweakCache: ByteArray, tweak32: ByteArray): ByteArray? {
|
||||
require(tweakCache.size == Secp256k1.FROST_TWEAK_CACHE_SIZE)
|
||||
require(tweak32.size == 32)
|
||||
|
||||
memScoped {
|
||||
val nPublicKey = alloc<secp256k1_pubkey>()
|
||||
|
||||
val nTweakCache = alloc<secp256k1_frost_tweak_cache>()
|
||||
memcpy(nTweakCache.ptr, toNat(tweakCache), Secp256k1.FROST_TWEAK_CACHE_SIZE.toULong())
|
||||
|
||||
secp256k1_frost_pubkey_xonly_tweak_add(
|
||||
ctx = ctx,
|
||||
output_pubkey = nPublicKey.ptr,
|
||||
tweak_cache = nTweakCache.ptr,
|
||||
tweak32 = toNat(tweak32)
|
||||
)
|
||||
|
||||
return serializePubkey(nPublicKey)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun MemScope.serializeFrostSecnonce(nTweakCache: secp256k1_frost_secnonce): ByteArray {
|
||||
val natOutput = ByteArray(Secp256k1.FROST_SECNONCE_SIZE)
|
||||
memcpy(toNat(natOutput), nTweakCache.ptr, Secp256k1.FROST_SECNONCE_SIZE.toULong())
|
||||
return natOutput
|
||||
}
|
||||
|
||||
private fun MemScope.serializeFrostPubnonce(nPubnonce: secp256k1_frost_pubnonce): ByteArray {
|
||||
val natOutput = allocArray<UByteVar>(Secp256k1.FROST_SERIALIZED_PUBNONCE_SIZE)
|
||||
secp256k1_frost_pubnonce_serialize(ctx, natOutput, nPubnonce.ptr).requireSuccess("secp256k1_frost_pubnonce_serialize() failed")
|
||||
return natOutput.readBytes(Secp256k1.FROST_SERIALIZED_PUBNONCE_SIZE)
|
||||
}
|
||||
|
||||
override fun frostNonceGen(
|
||||
sessionId32: ByteArray,
|
||||
share: ByteArray?,
|
||||
msg32: ByteArray?,
|
||||
publicKey: ByteArray?,
|
||||
extraInput32: ByteArray?
|
||||
): Pair<ByteArray, ByteArray> {
|
||||
require(sessionId32.size == 32) { "session id (${sessionId32.size}) size should be 32" }
|
||||
share?.let {
|
||||
require(share.size == Secp256k1.FROST_SERIALIZED_SHARE_SIZE) { "share size (${share.size}) should be ${Secp256k1.FROST_SERIALIZED_SHARE_SIZE}" }
|
||||
}
|
||||
msg32?.let {
|
||||
require(msg32.size == 32) { "msg32 (${sessionId32.size}) size should be 32" }
|
||||
}
|
||||
publicKey?.let {
|
||||
require(publicKey.size == 32) { "public key (${publicKey.size}) should be 32" }
|
||||
}
|
||||
extraInput32?.let {
|
||||
require(it.size == 32) { "extraInput32 (${extraInput32.size}) size should be 32" }
|
||||
}
|
||||
|
||||
memScoped {
|
||||
val nFrostSecnonce = alloc<secp256k1_frost_secnonce>()
|
||||
val nPublicNonce = alloc<secp256k1_frost_pubnonce>()
|
||||
|
||||
val nShare = share?.let { allocFrostShare(it) }
|
||||
val nPublicKey = publicKey?.let { allocXonlyPublicKey(it) }
|
||||
val nExtraInput32 = extraInput32?.let {
|
||||
toNat(it)
|
||||
}
|
||||
|
||||
secp256k1_frost_nonce_gen(
|
||||
ctx = ctx,
|
||||
secnonce = nFrostSecnonce.ptr,
|
||||
pubnonce = nPublicNonce.ptr,
|
||||
session_id32 = toNat(sessionId32),
|
||||
agg_share = nShare?.ptr,
|
||||
msg32 = msg32?.let { toNat(it) },
|
||||
agg_pk = nPublicKey?.ptr,
|
||||
extra_input32 = nExtraInput32
|
||||
)
|
||||
|
||||
return Pair(
|
||||
serializeFrostSecnonce(nFrostSecnonce),
|
||||
serializeFrostPubnonce(nPublicNonce)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MemScope.serializeFrostSession(nSession: secp256k1_frost_session): ByteArray {
|
||||
val natOutput = ByteArray(Secp256k1.FROST_SESSION_SIZE)
|
||||
memcpy(toNat(natOutput), nSession.ptr, Secp256k1.FROST_SESSION_SIZE.toULong())
|
||||
return natOutput
|
||||
}
|
||||
|
||||
override fun frostNonceProcess(
|
||||
publicNonces: Array<ByteArray>,
|
||||
threshold: Int,
|
||||
msg32: ByteArray,
|
||||
publicKey: ByteArray,
|
||||
id33: ByteArray,
|
||||
ids33: Array<ByteArray>,
|
||||
tweakCache: ByteArray?,
|
||||
adaptor: ByteArray?
|
||||
): ByteArray {
|
||||
publicNonces.forEach { publicNonce ->
|
||||
require(publicNonce.size == Secp256k1.FROST_SERIALIZED_PUBNONCE_SIZE) { "pubnonce size (${publicNonce.size}) size should be ${Secp256k1.FROST_SERIALIZED_PUBNONCE_SIZE}" }
|
||||
}
|
||||
require(msg32.size == 32) { "msg32 (${msg32.size}) size should be 32" }
|
||||
require(publicKey.size == Secp256k1.SERIALIZED_X_ONLY_PUBKEY_SIZE) { "publicKey size (${publicKey.size}) size should be ${Secp256k1.SERIALIZED_X_ONLY_PUBKEY_SIZE}" }
|
||||
require(ids33.size == threshold) { "ids33 array size much match public nonces array size"}
|
||||
ids33.forEach {
|
||||
require(it.size == 33) { "id33 (${it.size}) size should be 33" }
|
||||
}
|
||||
tweakCache?.let {
|
||||
require(tweakCache.size == Secp256k1.FROST_TWEAK_CACHE_SIZE) { "tweak cache size (${tweakCache.size}) size should be ${Secp256k1.FROST_TWEAK_CACHE_SIZE}" }
|
||||
}
|
||||
adaptor?.let {
|
||||
require(it.size == 33 || it.size == 65) { "adaptor public key size (${it.size}) should be 33 or 65" }
|
||||
}
|
||||
|
||||
memScoped {
|
||||
val nSession = alloc<secp256k1_frost_session>();
|
||||
|
||||
val nPublicNonces = publicNonces.map { allocFrostPublicNonce(it).ptr }
|
||||
|
||||
val nPublicKey = allocXonlyPublicKey(publicKey)
|
||||
|
||||
val nIds33 = ids33.map { toNat(it) }
|
||||
|
||||
val nTweakCache = tweakCache?.let {
|
||||
alloc<secp256k1_frost_tweak_cache>()
|
||||
}?.also { nTweakCache ->
|
||||
memcpy(nTweakCache.ptr, toNat(tweakCache) , Secp256k1.FROST_TWEAK_CACHE_SIZE.toULong())
|
||||
}
|
||||
|
||||
val nAdaptor = adaptor?.let {
|
||||
allocPublicKey(it).ptr
|
||||
}
|
||||
|
||||
secp256k1_frost_nonce_process(
|
||||
ctx = ctx,
|
||||
session = nSession.ptr,
|
||||
pubnonces = nPublicNonces.toCValues(),
|
||||
n_pubnonces = publicNonces.size.convert(),
|
||||
msg32 = toNat(msg32),
|
||||
agg_pk = nPublicKey.ptr,
|
||||
my_id33 = toNat(id33),
|
||||
ids33 = nIds33.toCValues(),
|
||||
tweak_cache = nTweakCache?.ptr,
|
||||
adaptor = nAdaptor
|
||||
)
|
||||
|
||||
return serializeFrostSession(
|
||||
nSession = nSession
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private fun MemScope.serializeFrostPartialSignature(nPartialSignature: secp256k1_frost_partial_sig): ByteArray {
|
||||
val natOutput = allocArray<UByteVar>(Secp256k1.FROST_SERIALIZED_PARTIAL_SIGNATURE_SIZE)
|
||||
secp256k1_frost_partial_sig_serialize(ctx, natOutput, nPartialSignature.ptr).requireSuccess("secp256k1_frost_partial_sig_serialize() failed")
|
||||
return natOutput.readBytes(Secp256k1.FROST_SERIALIZED_PARTIAL_SIGNATURE_SIZE)
|
||||
}
|
||||
|
||||
override fun frostPartialSign(
|
||||
secnonce: ByteArray,
|
||||
share: ByteArray,
|
||||
session: ByteArray,
|
||||
tweakCache: ByteArray?
|
||||
): ByteArray {
|
||||
require(secnonce.size == Secp256k1.FROST_SECNONCE_SIZE) { "secnonce size (${secnonce.size}) should be of size ${Secp256k1.FROST_SECNONCE_SIZE}" }
|
||||
require(share.size == Secp256k1.FROST_SERIALIZED_SHARE_SIZE) { "share size (${share.size}) should be of size ${Secp256k1.FROST_SERIALIZED_SHARE_SIZE}" }
|
||||
require(session.size == Secp256k1.FROST_SESSION_SIZE) { "session size (${share.size}) should be of size ${Secp256k1.FROST_SESSION_SIZE}" }
|
||||
tweakCache?.let {
|
||||
require(tweakCache.size == Secp256k1.FROST_TWEAK_CACHE_SIZE) { "tweak cache size (${tweakCache.size}) size should be ${Secp256k1.FROST_TWEAK_CACHE_SIZE}" }
|
||||
}
|
||||
|
||||
|
||||
memScoped {
|
||||
val nPartialSignature = alloc<secp256k1_frost_partial_sig>();
|
||||
|
||||
val nSecnonce = alloc<secp256k1_frost_secnonce>()
|
||||
memcpy(nSecnonce.ptr, toNat(secnonce), Secp256k1.FROST_SECNONCE_SIZE.toULong())
|
||||
|
||||
val nShare = allocFrostShare(share)
|
||||
|
||||
val nSession = alloc<secp256k1_frost_session>()
|
||||
memcpy(nSession.ptr, toNat(session), Secp256k1.FROST_SESSION_SIZE.toULong())
|
||||
|
||||
val nTweakCache = tweakCache?.let {
|
||||
alloc<secp256k1_frost_tweak_cache>()
|
||||
}?.also { nTweakCache ->
|
||||
memcpy(nTweakCache.ptr, toNat(tweakCache) , Secp256k1.FROST_TWEAK_CACHE_SIZE.toULong())
|
||||
}
|
||||
|
||||
|
||||
secp256k1_frost_partial_sign(
|
||||
ctx,
|
||||
nPartialSignature.ptr,
|
||||
nSecnonce.ptr,
|
||||
nShare.ptr,
|
||||
nSession.ptr,
|
||||
nTweakCache?.ptr
|
||||
)
|
||||
|
||||
return serializeFrostPartialSignature(nPartialSignature)
|
||||
}
|
||||
}
|
||||
|
||||
override fun frostPartialSignatureVerify(
|
||||
partialSig: ByteArray,
|
||||
publicNonce: ByteArray,
|
||||
publicShare: ByteArray,
|
||||
session: ByteArray,
|
||||
tweakCache: ByteArray?
|
||||
): Int {
|
||||
require(partialSig.size == 32) { "partialSig (${partialSig.size}) size should be 32" }
|
||||
require(publicNonce.size == Secp256k1.FROST_SERIALIZED_PUBNONCE_SIZE) { "public nonce (${publicNonce.size}) size should be ${Secp256k1.FROST_SERIALIZED_PUBNONCE_SIZE}" }
|
||||
require(publicShare.size == 33 || publicShare.size == 65) { "public share size (${partialSig.size}) should be 33 or 65" }
|
||||
require(session.size == Secp256k1.FROST_SESSION_SIZE) { "session size (${session.size}) size should be ${Secp256k1.FROST_SESSION_SIZE}" }
|
||||
tweakCache?.let {
|
||||
require(tweakCache.size == Secp256k1.FROST_TWEAK_CACHE_SIZE) { "tweak cache size (${tweakCache.size}) size should be ${Secp256k1.FROST_TWEAK_CACHE_SIZE}" }
|
||||
}
|
||||
|
||||
|
||||
memScoped {
|
||||
val nPartialSignature = allocFrostPartialSignature(partialSig)
|
||||
val nPublicNonce = allocFrostPublicNonce(publicNonce)
|
||||
val nPublicShare = allocPublicKey(publicShare)
|
||||
val nSession = alloc<secp256k1_frost_session>()
|
||||
memcpy(nSession.ptr, toNat(session), Secp256k1.FROST_SESSION_SIZE.toULong())
|
||||
val nTweakCache = tweakCache?.let {
|
||||
alloc<secp256k1_frost_tweak_cache>()
|
||||
}?.also { nTweakCache ->
|
||||
memcpy(nTweakCache.ptr, toNat(tweakCache) , Secp256k1.FROST_TWEAK_CACHE_SIZE.toULong())
|
||||
}
|
||||
|
||||
return secp256k1_frost_partial_sig_verify(
|
||||
ctx,
|
||||
nPartialSignature.ptr,
|
||||
nPublicNonce.ptr,
|
||||
nPublicShare.ptr,
|
||||
nSession.ptr,
|
||||
nTweakCache?.ptr
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun frostPartialSignatureAggregate(
|
||||
session: ByteArray,
|
||||
partialSignatures: Array<ByteArray>,
|
||||
threshold: Int
|
||||
): ByteArray {
|
||||
require(threshold > 1) { "threshold must be greater then 1" }
|
||||
require(session.size == Secp256k1.FROST_SESSION_SIZE) { "session size (${session.size}) should be ${Secp256k1.FROST_SESSION_SIZE}" }
|
||||
require(partialSignatures.size == threshold) { "partialSignatures array size should match the threshold size" }
|
||||
partialSignatures.forEach { partialSig ->
|
||||
require(partialSig.size == 32) { "partialSignatures size (${partialSig.size}) should be 32" }
|
||||
}
|
||||
|
||||
memScoped {
|
||||
val sig64 = ByteArray(64)
|
||||
|
||||
val nSession = alloc<secp256k1_frost_session>();
|
||||
memcpy(nSession.ptr, toNat(session), Secp256k1.FROST_SESSION_SIZE.toULong())
|
||||
val nPartialSignatures = partialSignatures.map { allocFrostPartialSignature(it).ptr }
|
||||
|
||||
secp256k1_frost_partial_sig_agg(
|
||||
ctx,
|
||||
toNat(sig64),
|
||||
nSession.ptr,
|
||||
nPartialSignatures.toCValues(),
|
||||
threshold.convert()
|
||||
)
|
||||
|
||||
return sig64
|
||||
}
|
||||
}
|
||||
|
||||
public override fun cleanup() {
|
||||
secp256k1_context_destroy(ctx)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
internal actual fun getSecpk256k1(): Secp256k1 = Secp256k1Native
|
||||
|
@ -1,3 +1,6 @@
|
||||
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")
|
||||
@ -20,6 +23,8 @@ 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")
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,14 +41,14 @@ kotlin {
|
||||
}
|
||||
|
||||
if (includeAndroid) {
|
||||
android {
|
||||
androidTarget {
|
||||
compilations.all {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
sourceSets["androidMain"].dependencies {
|
||||
implementation(project(":jni:android"))
|
||||
}
|
||||
sourceSets["androidTest"].dependencies {
|
||||
sourceSets["androidUnitTest"].dependencies {
|
||||
implementation(kotlin("test-junit"))
|
||||
implementation("androidx.test.ext:junit:1.1.2")
|
||||
implementation("androidx.test.espresso:espresso-core:3.3.0")
|
||||
@ -51,17 +56,20 @@ kotlin {
|
||||
}
|
||||
}
|
||||
|
||||
linuxX64("linux")
|
||||
|
||||
ios()
|
||||
linuxX64()
|
||||
macosX64()
|
||||
macosArm64()
|
||||
iosX64()
|
||||
iosArm64()
|
||||
iosSimulatorArm64()
|
||||
}
|
||||
|
||||
val includeAndroid = System.getProperty("includeAndroid")?.toBoolean() ?: true
|
||||
if (includeAndroid) {
|
||||
extensions.configure<com.android.build.gradle.LibraryExtension>("android") {
|
||||
defaultConfig {
|
||||
compileSdkVersion(30)
|
||||
minSdkVersion(21)
|
||||
compileSdk = 30
|
||||
minSdk = 21
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
@ -79,3 +87,25 @@ 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"))
|
||||
}
|
||||
}
|
||||
|
24
tests/src/commonTest/kotlin/fr/acinq/secp256k1/BaseTest.kt
Normal file
24
tests/src/commonTest/kotlin/fr/acinq/secp256k1/BaseTest.kt
Normal file
@ -0,0 +1,24 @@
|
||||
package fr.acinq.secp256k1
|
||||
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
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
|
||||
|
||||
abstract class BaseTest {
|
||||
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)
|
||||
}
|
||||
}
|
510
tests/src/commonTest/kotlin/fr/acinq/secp256k1/FrostTest.kt
Normal file
510
tests/src/commonTest/kotlin/fr/acinq/secp256k1/FrostTest.kt
Normal file
@ -0,0 +1,510 @@
|
||||
package fr.acinq.secp256k1
|
||||
|
||||
import kotlinx.serialization.json.*
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
class FrostTest: BaseTest() {
|
||||
val msg32 = "this_could_be_the_hash_of_a_msg!".encodeToByteArray()
|
||||
|
||||
@Test
|
||||
fun `frost share generation test cases`() {
|
||||
val tests = readData("frost/share_gen_vectors.json")
|
||||
val pubkeys = tests.jsonObject["pubkeys"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
||||
|
||||
tests.jsonObject["valid_share_gen_test_cases"]!!.jsonArray.forEach { validTestCases ->
|
||||
|
||||
println("Testing ${validTestCases.jsonObject["seed"]!!.jsonPrimitive.content}") // Hack to slow things down... :(
|
||||
val keyIndices = validTestCases.jsonObject["key_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
||||
|
||||
val seed32 = Hex.decode(validTestCases.jsonObject["seed"]!!.jsonPrimitive.content)
|
||||
val nParticipants = keyIndices.size
|
||||
val threshold = validTestCases.jsonObject["threshold"]!!.jsonPrimitive.int
|
||||
val ids33 = keyIndices.map { pubkeys[it] }.toTypedArray()
|
||||
|
||||
val result = Secp256k1.frostSharesGen(
|
||||
seed32,
|
||||
threshold,
|
||||
nParticipants,
|
||||
ids33
|
||||
)
|
||||
|
||||
val expected = validTestCases.jsonObject["expected"]!!;
|
||||
|
||||
val expectedShare = expected.jsonObject["frost_share"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
||||
val expectedVSSCommitment = expected.jsonObject["vss_commitment"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
||||
val expectedPoK64 = Hex.decode(expected.jsonObject["pok64"]!!.jsonPrimitive.content)
|
||||
|
||||
|
||||
result.first.forEachIndexed { index, share ->
|
||||
assertEquals(
|
||||
expected = Hex.encode(expectedShare[index]),
|
||||
actual = Hex.encode(share),
|
||||
"Unexpected $index:share for $keyIndices test case"
|
||||
)
|
||||
}
|
||||
result.second.forEachIndexed { index, vssCommitment ->
|
||||
assertEquals(
|
||||
expected = Hex.encode(expectedVSSCommitment[index]),
|
||||
actual = Hex.encode(vssCommitment),
|
||||
"Unexpected $index:vss_commitment for the $keyIndices test case"
|
||||
)
|
||||
}
|
||||
assertEquals(
|
||||
expected = Hex.encode(expectedPoK64),
|
||||
actual = Hex.encode(result.third),
|
||||
message = "Unexpected pok64 for $keyIndices test case"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `frost share generation signers`() {
|
||||
val tests = readData("frost/share_gen_signers_vectors.json")
|
||||
val pubkeys = tests.jsonObject["pubkeys"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
||||
|
||||
val signerShareGenTestCase = tests.jsonObject["valid_signers_share_gen_test_case"]!!
|
||||
|
||||
val keyIndices = signerShareGenTestCase.jsonObject["key_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
||||
|
||||
val nParticipants = keyIndices.size
|
||||
val threshold = signerShareGenTestCase.jsonObject["threshold"]!!.jsonPrimitive.int
|
||||
val ids33 = keyIndices.map { pubkeys[it] }.toTypedArray()
|
||||
|
||||
signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.forEachIndexed { signerIndex, signer ->
|
||||
val seed32 = Hex.decode(signer.jsonObject["seed"]!!.jsonPrimitive.content)
|
||||
println("Testing ${signer.jsonObject["seed"]!!.jsonPrimitive.content}") // Hack to slow things down... :(
|
||||
// There seems to be a bug that causes a crash if we call frost_share_gen too often
|
||||
|
||||
val result = Secp256k1.frostSharesGen(
|
||||
seed32,
|
||||
threshold,
|
||||
nParticipants,
|
||||
ids33
|
||||
)
|
||||
|
||||
val expected = signer.jsonObject["expected"]!!;
|
||||
|
||||
val expectedShare = expected.jsonObject["frost_share"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
||||
val expectedVSSCommitment = expected.jsonObject["vss_commitment"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
||||
val expectedPoK64 = Hex.decode(expected.jsonObject["pok64"]!!.jsonPrimitive.content)
|
||||
|
||||
result.first.forEachIndexed { index, share ->
|
||||
assertEquals(
|
||||
expected = Hex.encode(expectedShare[index]),
|
||||
actual = Hex.encode(share),
|
||||
"Unexpected $signerIndex:signer $index:share for $keyIndices test case"
|
||||
)
|
||||
}
|
||||
result.second.forEachIndexed { index, vssCommitment ->
|
||||
assertEquals(
|
||||
expected = Hex.encode(expectedVSSCommitment[index]),
|
||||
actual = Hex.encode(vssCommitment),
|
||||
"Unexpected $signerIndex:signer $index:vss_commitment for the $keyIndices test case"
|
||||
)
|
||||
}
|
||||
assertEquals(
|
||||
expected = Hex.encode(expectedPoK64),
|
||||
actual = Hex.encode(result.third),
|
||||
message = "Unexpected $signerIndex:signer pok64 for $keyIndices test case"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `frost share aggregation`() {
|
||||
val shareGenTests = readData("frost/share_gen_vectors.json")
|
||||
val tests = readData("frost/share_agg_vectors.json")
|
||||
|
||||
val expectedAggregatePublicKey = tests.jsonObject["aggregate_public_key"]!!.jsonPrimitive.content
|
||||
val publicKeys = shareGenTests.jsonObject["pubkeys"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
||||
|
||||
val signerShareGenTestCase = shareGenTests.jsonObject["valid_signers_share_gen_test_case"]!!;
|
||||
|
||||
val keyIndices = signerShareGenTestCase.jsonObject["key_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
||||
val nParticipants = keyIndices.size
|
||||
val threshold = signerShareGenTestCase.jsonObject["threshold"]!!.jsonPrimitive.int
|
||||
val ids33 = keyIndices.map { publicKeys[it] }.toTypedArray()
|
||||
|
||||
val vssCommitments = signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.map { signer ->
|
||||
signer.jsonObject["expected"]!!.jsonObject["vss_commitment"]!!.jsonArray.map {
|
||||
Hex.decode(it.jsonPrimitive.content)
|
||||
}.toTypedArray()
|
||||
}
|
||||
|
||||
signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.forEachIndexed { index, _ ->
|
||||
val assignedShares = signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.map {
|
||||
Hex.decode(
|
||||
it.jsonObject["expected"]!!.jsonObject["frost_share"]!!.jsonArray[index].jsonPrimitive.content
|
||||
)
|
||||
}
|
||||
|
||||
val result = Secp256k1.frostShareAggregate(
|
||||
assignedShares.toTypedArray(),
|
||||
vssCommitments.toTypedArray(),
|
||||
nParticipants,
|
||||
threshold,
|
||||
ids33[index]
|
||||
)
|
||||
|
||||
val expected = tests.jsonObject["expected"]!!.jsonArray[index];
|
||||
|
||||
val expectedAggregateShare = expected.jsonObject["aggregate_share"]!!.jsonPrimitive.content
|
||||
|
||||
assertEquals(
|
||||
expected = expectedAggregateShare,
|
||||
actual = Hex.encode(result.first),
|
||||
"Unexpected $index:aggregate_share"
|
||||
)
|
||||
assertEquals(
|
||||
expected = expectedAggregatePublicKey,
|
||||
actual = Hex.encode(result.second),
|
||||
"Unexpected $index:aggregate_public_key"
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `frost share verify`() {
|
||||
val shareGenTests = readData("frost/share_gen_vectors.json")
|
||||
|
||||
val publicKeys = shareGenTests.jsonObject["pubkeys"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
||||
|
||||
val signerShareGenTestCase = shareGenTests.jsonObject["valid_signers_share_gen_test_case"]!!;
|
||||
|
||||
val keyIndices = signerShareGenTestCase.jsonObject["key_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
||||
val threshold = signerShareGenTestCase.jsonObject["threshold"]!!.jsonPrimitive.int
|
||||
val ids33 = keyIndices.map { publicKeys[it] }.toTypedArray()
|
||||
|
||||
val vssCommitments = signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.map { signer ->
|
||||
signer.jsonObject["expected"]!!.jsonObject["vss_commitment"]!!.jsonArray.map {
|
||||
Hex.decode(it.jsonPrimitive.content)
|
||||
}.toTypedArray()
|
||||
}
|
||||
|
||||
signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.forEachIndexed { index, _ ->
|
||||
val assignedShares = signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.map {
|
||||
Hex.decode(
|
||||
it.jsonObject["expected"]!!.jsonObject["frost_share"]!!.jsonArray[index].jsonPrimitive.content
|
||||
)
|
||||
}
|
||||
|
||||
assertEquals(
|
||||
expected = 1,
|
||||
actual = Secp256k1.frostShareVerify(
|
||||
threshold,
|
||||
ids33[index],
|
||||
assignedShares[index],
|
||||
vssCommitments[index]
|
||||
),
|
||||
message = "Couldn't verify share from $index signer"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `frost compute pubshare`() {
|
||||
val shareGenTests = readData("frost/share_gen_vectors.json")
|
||||
val tests = readData("frost/share_agg_vectors.json")
|
||||
|
||||
val publicKeys = shareGenTests.jsonObject["pubkeys"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
||||
|
||||
val signerShareGenTestCase = shareGenTests.jsonObject["valid_signers_share_gen_test_case"]!!;
|
||||
|
||||
val keyIndices = signerShareGenTestCase.jsonObject["key_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
||||
val nParticipants = keyIndices.size
|
||||
val threshold = signerShareGenTestCase.jsonObject["threshold"]!!.jsonPrimitive.int
|
||||
val ids33 = keyIndices.map { publicKeys[it] }.toTypedArray()
|
||||
|
||||
val vssCommitments = signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.map { signer ->
|
||||
signer.jsonObject["expected"]!!.jsonObject["vss_commitment"]!!.jsonArray.map {
|
||||
Hex.decode(it.jsonPrimitive.content)
|
||||
}.toTypedArray()
|
||||
}
|
||||
|
||||
signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.forEachIndexed { index, _ ->
|
||||
|
||||
val expected = tests.jsonObject["expected"]!!.jsonArray[index];
|
||||
|
||||
val expectedPublicShare = expected.jsonObject["public_share"]!!.jsonPrimitive.content
|
||||
|
||||
assertEquals(
|
||||
expected = expectedPublicShare,
|
||||
actual = Hex.encode(
|
||||
Secp256k1.frostComputePublicShare(
|
||||
threshold,
|
||||
ids33[index],
|
||||
vssCommitments.toTypedArray(),
|
||||
nParticipants
|
||||
)
|
||||
),
|
||||
message = "Couldn't verify share from $index signer"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Frost Tweak
|
||||
@Test
|
||||
fun `frost pubkey tweak`() {
|
||||
val tests = readData("frost/share_agg_vectors.json")
|
||||
|
||||
val expectedAggregatePublicKey = Hex.decode(
|
||||
tests.jsonObject["aggregate_public_key"]!!.jsonPrimitive.content
|
||||
)
|
||||
|
||||
val expectedTweakCache = tests.jsonObject["tweak_cache"]!!.jsonPrimitive.content
|
||||
|
||||
assertEquals(
|
||||
expected = expectedTweakCache,
|
||||
actual = Hex.encode(
|
||||
Secp256k1.frostPublicKeyTweak(
|
||||
expectedAggregatePublicKey
|
||||
)
|
||||
),
|
||||
message = "Tweak cache incorrect."
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `frost pubkey ec tweak add`() {
|
||||
val tests = readData("frost/frost_tweak_vectors.json")
|
||||
|
||||
val tweakCache = Hex.decode(
|
||||
tests.jsonObject["tweak_cache"]!!.jsonPrimitive.content
|
||||
)
|
||||
val ordinaryTweak = "this could be a BIP32 tweak.....".encodeToByteArray()
|
||||
|
||||
assertEquals(
|
||||
expected = tests.jsonObject["ec_tweak_add"]!!.jsonPrimitive.content,
|
||||
actual = Secp256k1.frostPublicKeyEcTweakAdd(
|
||||
tweakCache,
|
||||
ordinaryTweak
|
||||
)?.let {
|
||||
Hex.encode(
|
||||
it
|
||||
)
|
||||
},
|
||||
message = "EC Tweak Add incorrect."
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `frost pubkey xonly tweak add`() {
|
||||
val tests = readData("frost/frost_tweak_vectors.json")
|
||||
|
||||
val tweakCache = Hex.decode(
|
||||
tests.jsonObject["tweak_cache"]!!.jsonPrimitive.content
|
||||
)
|
||||
val ordinaryTweak = "this could be a BIP32 tweak.....".encodeToByteArray()
|
||||
|
||||
assertEquals(
|
||||
expected = tests.jsonObject["ec_tweak_add"]!!.jsonPrimitive.content,
|
||||
// TODO: Return public key
|
||||
actual = Secp256k1.frostPublicKeyXonlyTweakAdd(
|
||||
tweakCache,
|
||||
ordinaryTweak
|
||||
)?.let {
|
||||
Hex.encode(
|
||||
it
|
||||
)
|
||||
},
|
||||
message = "EC Tweak Add incorrect."
|
||||
)
|
||||
}
|
||||
|
||||
// Frost Sign functionality
|
||||
@Test
|
||||
fun `frost nonce gen`() {
|
||||
val tests = readData("frost/frost_nonce_vectors.json")
|
||||
|
||||
val sessionId = Hex.decode(
|
||||
tests.jsonObject["session_id"]!!.jsonPrimitive.content
|
||||
)
|
||||
val aggregatePublicKey = Hex.decode(
|
||||
tests.jsonObject["aggregate_public_key"]!!.jsonPrimitive.content
|
||||
)
|
||||
|
||||
tests.jsonObject["signers"]!!.jsonArray.forEachIndexed { index, signers ->
|
||||
val aggregateShare = Hex.decode(
|
||||
signers.jsonObject["aggregate_share"]!!.jsonPrimitive.content
|
||||
)
|
||||
|
||||
val (secNonce, pubNonce) = Secp256k1.frostNonceGen(
|
||||
sessionId,
|
||||
aggregateShare,
|
||||
msg32,
|
||||
aggregatePublicKey,
|
||||
null
|
||||
)
|
||||
|
||||
val expectedSecNonce = signers.jsonObject["secnonce"]!!.jsonPrimitive.content
|
||||
val expectedPubNonce = signers.jsonObject["pubnonce"]!!.jsonPrimitive.content
|
||||
|
||||
assertEquals(
|
||||
expected = expectedSecNonce,
|
||||
actual = Hex.encode(secNonce),
|
||||
message = "Invalid $index:secnonce"
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expected = expectedPubNonce,
|
||||
actual = Hex.encode(pubNonce),
|
||||
message = "Invalid $index:pubnonce"
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `frost nonce process`() {
|
||||
val tests = readData("frost/frost_nonce_vectors.json")
|
||||
|
||||
val threshold = tests.jsonObject["threshold"]!!.jsonPrimitive.int
|
||||
|
||||
val pubnonces = tests.jsonObject["signers"]!!.jsonArray.take(threshold).map {
|
||||
Hex.decode(it.jsonObject["pubnonce"]!!.jsonPrimitive.content)
|
||||
}
|
||||
|
||||
val aggregatePublicKey = Hex.decode(
|
||||
tests.jsonObject["aggregate_public_key"]!!.jsonPrimitive.content
|
||||
)
|
||||
|
||||
val pubkeys = tests.jsonObject["pubkeys"]!!.jsonArray.take(threshold).map {
|
||||
Hex.decode(it.jsonPrimitive.content)
|
||||
}
|
||||
|
||||
val tweakCache = Hex.decode(
|
||||
tests.jsonObject["tweak_cache"]!!.jsonPrimitive.content
|
||||
)
|
||||
|
||||
tests.jsonObject["signers"]!!.jsonArray.take(threshold).forEachIndexed { signerIndex, signer ->
|
||||
|
||||
val session = Secp256k1.frostNonceProcess(
|
||||
pubnonces.toTypedArray(),
|
||||
threshold,
|
||||
msg32,
|
||||
aggregatePublicKey,
|
||||
pubkeys[signerIndex],
|
||||
pubkeys.toTypedArray(),
|
||||
tweakCache,
|
||||
null
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expected = signer.jsonObject["session"]!!.jsonPrimitive.content,
|
||||
actual = Hex.encode(session),
|
||||
message = "Invalid $signerIndex:session"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `frost partial sign`() {
|
||||
val tests = readData("frost/frost_nonce_vectors.json")
|
||||
|
||||
|
||||
val threshold = tests.jsonObject["threshold"]!!.jsonPrimitive.int
|
||||
|
||||
val tweakCache = Hex.decode(
|
||||
tests.jsonObject["tweak_cache"]!!.jsonPrimitive.content
|
||||
)
|
||||
|
||||
tests.jsonObject["signers"]!!.jsonArray.take(threshold).forEachIndexed { signerIndex, signer ->
|
||||
|
||||
val secNonce = Hex.decode(
|
||||
signer.jsonObject["secnonce"]!!.jsonPrimitive.content
|
||||
)
|
||||
|
||||
val aggregateShare = Hex.decode(
|
||||
signer.jsonObject["aggregate_share"]!!.jsonPrimitive.content
|
||||
)
|
||||
|
||||
val session = Hex.decode(
|
||||
signer.jsonObject["session"]!!.jsonPrimitive.content
|
||||
)
|
||||
|
||||
val partialSignature = Secp256k1.frostPartialSign(
|
||||
secNonce,
|
||||
aggregateShare,
|
||||
session,
|
||||
tweakCache
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expected = signer.jsonObject["partial_signature"]!!.jsonPrimitive.content,
|
||||
actual = Hex.encode(partialSignature),
|
||||
message = "Invalid $signerIndex:partial signature"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `frost partial signature verify`() {
|
||||
val tests = readData("frost/frost_nonce_vectors.json")
|
||||
|
||||
val threshold = tests.jsonObject["threshold"]!!.jsonPrimitive.int
|
||||
val tweakCache = Hex.decode(
|
||||
tests.jsonObject["tweak_cache"]!!.jsonPrimitive.content
|
||||
)
|
||||
|
||||
tests.jsonObject["signers"]!!.jsonArray.take(threshold).forEach { signer ->
|
||||
val partialSignature = Hex.decode(
|
||||
signer.jsonObject["partial_signature"]!!.jsonPrimitive.content
|
||||
)
|
||||
|
||||
val pubNonce = Hex.decode(
|
||||
signer.jsonObject["pubnonce"]!!.jsonPrimitive.content
|
||||
)
|
||||
|
||||
val publicShare = Hex.decode(
|
||||
signer.jsonObject["public_share"]!!.jsonPrimitive.content
|
||||
)
|
||||
|
||||
val session = Hex.decode(
|
||||
signer.jsonObject["session"]!!.jsonPrimitive.content
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expected = 1,
|
||||
actual = Secp256k1.frostPartialSignatureVerify(
|
||||
partialSignature,
|
||||
pubNonce,
|
||||
publicShare,
|
||||
session,
|
||||
tweakCache
|
||||
),
|
||||
message = "Failed to verify partial signature"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `frost partial signature aggregation`() {
|
||||
val tests = readData("frost/frost_nonce_vectors.json")
|
||||
|
||||
val threshold = tests.jsonObject["threshold"]!!.jsonPrimitive.int
|
||||
|
||||
val partialSignatures = tests.jsonObject["signers"]!!.jsonArray.take(threshold).map {
|
||||
Hex.decode(it.jsonObject["partial_signature"]!!.jsonPrimitive.content)
|
||||
}
|
||||
|
||||
tests.jsonObject["signers"]!!.jsonArray.take(threshold).forEach { signer ->
|
||||
val session = Hex.decode(
|
||||
signer.jsonObject["session"]!!.jsonPrimitive.content
|
||||
)
|
||||
|
||||
val aggregatedSignature = Secp256k1.frostPartialSignatureAggregate(
|
||||
session,
|
||||
partialSignatures.toTypedArray(),
|
||||
threshold
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expected = tests.jsonObject["aggregate_signature"]!!.jsonPrimitive.content,
|
||||
actual = Hex.encode(aggregatedSignature),
|
||||
message = "Invalid aggregate partial signature"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
288
tests/src/commonTest/kotlin/fr/acinq/secp256k1/Musig2Test.kt
Normal file
288
tests/src/commonTest/kotlin/fr/acinq/secp256k1/Musig2Test.kt
Normal file
@ -0,0 +1,288 @@
|
||||
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: BaseTest() {
|
||||
|
||||
@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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -5,6 +5,14 @@ import kotlin.test.*
|
||||
|
||||
class Secp256k1Test {
|
||||
|
||||
val random = Random.Default
|
||||
|
||||
fun randomBytes(length: Int): ByteArray {
|
||||
val buffer = ByteArray(length)
|
||||
random.nextBytes(buffer)
|
||||
return buffer
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyValidPrivateKey() {
|
||||
val priv = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".lowercase())
|
||||
@ -353,15 +361,199 @@ class Secp256k1Test {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun fuzzEcdsaSignVerify() {
|
||||
val random = Random.Default
|
||||
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))
|
||||
}
|
||||
|
||||
fun randomBytes(length: Int): ByteArray {
|
||||
val buffer = ByteArray(length)
|
||||
random.nextBytes(buffer)
|
||||
return buffer
|
||||
@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(randomBytes(32), randomBytes(32))
|
||||
val sessionId = randomBytes(32)
|
||||
val msg32 = randomBytes(32)
|
||||
val pubkeys = privkeys.map { Secp256k1.pubkeyCreate(it) }
|
||||
val nonces = pubkeys.map { Secp256k1.musigNonceGen(sessionId, null, it, null, null, null) }
|
||||
val testData = run {
|
||||
val builder = StringBuilder()
|
||||
builder.append("private keys\n")
|
||||
privkeys.forEach { builder.append(Hex.encode(it)).append("\n") }
|
||||
builder.append("sessionId ${Hex.encode(sessionId)}\n")
|
||||
builder.append("msg32 ${Hex.encode(msg32)}\n")
|
||||
builder.append("nonces\n")
|
||||
nonces.forEach { builder.append(Hex.encode(it)).append("\n") }
|
||||
builder.toString()
|
||||
}
|
||||
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]), testData)
|
||||
assertContentEquals(keyaggCaches[0], keyaggCaches[1], testData)
|
||||
|
||||
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]), testData)
|
||||
assertEquals(0, Secp256k1.musigPartialSigVerify(Random.nextBytes(32), pubnonces[it], pubkeys[it], keyaggCaches[it], sessions[it]), testData)
|
||||
psig
|
||||
}
|
||||
|
||||
// signing fails if the secret nonce does not match the private key's public key
|
||||
assertFails(testData) {
|
||||
Secp256k1.musigPartialSign(secnonces[1], privkeys[0], keyaggCaches[0], sessions[0])
|
||||
}
|
||||
|
||||
assertFails(testData) {
|
||||
Secp256k1.musigPartialSign(secnonces[0], privkeys[1], keyaggCaches[1], sessions[1])
|
||||
}
|
||||
|
||||
val sig = Secp256k1.musigPartialSigAgg(sessions[0], psigs.toTypedArray())
|
||||
assertContentEquals(sig, Secp256k1.musigPartialSigAgg(sessions[1], psigs.toTypedArray()), testData)
|
||||
assertTrue(Secp256k1.verifySchnorr(sig, msg32, aggpubkey), testData)
|
||||
|
||||
val invalidSig1 = Secp256k1.musigPartialSigAgg(sessions[0], arrayOf(psigs[0], psigs[0]))
|
||||
assertFalse(Secp256k1.verifySchnorr(invalidSig1, msg32, aggpubkey), testData)
|
||||
val invalidSig2 = Secp256k1.musigPartialSigAgg(sessions[0], arrayOf(Random.nextBytes(32), Random.nextBytes(32)))
|
||||
assertFalse(Secp256k1.verifySchnorr(invalidSig2, msg32, aggpubkey), testData)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInvalidArguments() {
|
||||
assertFails {
|
||||
Secp256k1.pubkeyCreate(ByteArray(32))
|
||||
}
|
||||
assertFails {
|
||||
Secp256k1.pubkeyCreate(Hex.decode("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
|
||||
}
|
||||
assertFails {
|
||||
Secp256k1.pubkeyParse(ByteArray(33))
|
||||
}
|
||||
assertFails {
|
||||
Secp256k1.pubkeyParse(Hex.decode("03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
|
||||
}
|
||||
assertFails {
|
||||
Secp256k1.pubKeyCombine(arrayOf())
|
||||
}
|
||||
assertFails {
|
||||
Secp256k1.pubKeyCombine(arrayOf(ByteArray(0)))
|
||||
}
|
||||
assertFails {
|
||||
Secp256k1.signSchnorr(ByteArray(0), Hex.decode("0101010101010101010101010101010101010101010101010101010101010101"), null)
|
||||
}
|
||||
assertFails {
|
||||
Secp256k1.ecdsaRecover(
|
||||
Hex.decode("01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101"),
|
||||
Hex.decode("0202020202020202020202020202020202020202020202020202020202020202"),
|
||||
-1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun fuzzMusig2SigningSession() {
|
||||
repeat(1000) {
|
||||
testMusig2SigningSession()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun fuzzEcdsaSignVerify() {
|
||||
repeat(200) {
|
||||
val priv = randomBytes(32)
|
||||
assertTrue(Secp256k1.secKeyVerify(priv))
|
||||
|
@ -0,0 +1,52 @@
|
||||
{
|
||||
"pubkeys": [
|
||||
"023590a94e768f8e1815c2f24b4d80a8e3149316c3518ce7b7ad338368d038ca66",
|
||||
"02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9",
|
||||
"03935f972da013f80ae011890fa89b67a27b7be6ccb24d3274d18b2d4067f261a9",
|
||||
"03dff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659",
|
||||
"04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9"
|
||||
],
|
||||
"threshold": 3,
|
||||
"aggregate_public_key": "bd5561ef6dbff52d3f73b8cb0c065328988b71d3386d23890744a0dd6ad27c15",
|
||||
"aggregate_signature": "2fd5bc8fa46b0097f48e0b5734ec557a17a72f8c5989bdf5f387f01b5edc149f02a340d5ca0348b54448aecc67c1bdd532e43f5295a5382e063fe7b4425259ac",
|
||||
"session_id": "7FB9E0E687ADA1EEBF7ECFE2F21E73EBDB51A7D450948DFE8D76D7F2D1007671",
|
||||
"tweak_cache": "40252e41157cd26adda0440789236d38d3718b982853060ccbb8733f2df5bf6def6155bdeefcc6b83fe690d621b793b08f5b1562a7bef628c4db33f2d0b3f3bf2f541aaa000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"signers": [
|
||||
{
|
||||
"partial_signature": "82b98b609479595b861db8704b1b1af5d34defb52fb75023a2fb53a8c66e8ba7",
|
||||
"session": "5c11a803002fd5bc8fa46b0097f48e0b5734ec557a17a72f8c5989bdf5f387f01b5edc149fe6d54b152392da71ec234b35804aeb36c6829cb0a0201aa7a94ec586a1147f0c18901c3b3d604349539d6cd24a2c1e7a676a7bce95cb810dbc346133c117a7a60000000000000000000000000000000000000000000000000000000000000000",
|
||||
"secnonce": "847d4625fe87e00f9562351a9b7de8fc2420caba09535db177fc4fbac5b69b84c8700ae143946a0fffff4083d6377ee19a6448a55241160fbc7c793aace02f289a2fec8f",
|
||||
"pubnonce": "03203a0450540686854df68f6c1d15661772e4d05c4442ee1e437d86842779ef2202d03839fd99faf7a11ccc319a9adc965c5e094ca3728455059a4911ae96192fae",
|
||||
"aggregate_share": "1cfa28492e84e945343f1167401cdce061202a59e47e050c0c2f7f0c56e8e148",
|
||||
"public_share": "0493effba7e50d3885bb0c4665149abd4fd13622047412f1da4c0e3754ecb1a9183aaadfdf0f2f82e24641e6ed7a0f7ee22a4a8a47c6d2df66daad37a4880fffe2"
|
||||
},
|
||||
{
|
||||
"partial_signature": "62bb46458d4307cc96280159873fac35285e65049196c0df03954da79e7924e5",
|
||||
"session": "5c11a803002fd5bc8fa46b0097f48e0b5734ec557a17a72f8c5989bdf5f387f01b5edc149fe6d54b152392da71ec234b35804aeb36c6829cb0a0201aa7a94ec586a1147f0c69b1d2e667d72b4a220100d9229740f24d9891f55f41918a07831d382aeba5e80000000000000000000000000000000000000000000000000000000000000000",
|
||||
"secnonce": "847d4625e6af707cb69026251afdfc2570a3fbdbbc7e72530354f0777fe2192c6aa8b23969172508dff48f7a21827935cce2ed019c570bb5552f9a3269a8ec34cfd23e2c",
|
||||
"pubnonce": "035c8f36d2cf868b9ccab3221b3f5eca64d60469a50245d6edc3b4f4bfb4358892023e832dcc0b5b55562ef6f1536679a2e996827747d3b824ca335daf00d51ab788",
|
||||
"aggregate_share": "dd82fcc1806f1a968228c794a7001c18d209871fb3441bae80fd8229f6a9b0dd",
|
||||
"public_share": "048482e27b533879d4f3d68bdb2038bf9480d4ce4cc614d7133238e55179c65a175c684afb7f983e60139542b80f0f12815f3194082f07c93e1f87f3cd1b1c0d8b"
|
||||
},
|
||||
{
|
||||
"partial_signature": "1d2e6f2fa846e78d2802f5029566f6a8f1e6c77f839fc7671f81a4f0ada0ea61",
|
||||
"session": "5c11a803002fd5bc8fa46b0097f48e0b5734ec557a17a72f8c5989bdf5f387f01b5edc149fe6d54b152392da71ec234b35804aeb36c6829cb0a0201aa7a94ec586a1147f0c7c15772acc75c702d2cb960a98c793090de73e18e5fd5dbf08818afcab855fa10000000000000000000000000000000000000000000000000000000000000000",
|
||||
"secnonce": "847d4625bd32503f81d016175829db4df8475660c77e28cf6dc7bc8b2f6e3fe6f67282b1cf81dcf2dabd867053461cf602f3e3345f42119066f4c493b85a0744ae7beb08",
|
||||
"pubnonce": "028b80bb46028338d41101deacd7910e09ba148f75d2c01e9f8f767fa9cdcbbc7e02b0cec1ba331750a22bf3bb8d1b724bd2874f7f0c19c70227f64c463c8c6bcab2",
|
||||
"aggregate_share": "5fe629d5f34fdb3ea2f6e545fc3d2cf1f5ce23a504b144e6ebe928793cc85cb4",
|
||||
"public_share": "04d71784b58d8958141f8f405d56026f214a736e73f9c1f70776fc2e49f4e90fc0a9396bcc7471a83caf4076a18cb6ab4264aa37174ca19e142259aa5f6bb7fea3"
|
||||
},
|
||||
{
|
||||
"secnonce": "847d4625f2128d893c4b8b62818bbc972d158b0aa96b08dcacba149dd7517b7fc7ddb89b70234941338c242dbca6e27ebf337ae458381ef83bea4ba2baab8df3d7f6b773",
|
||||
"pubnonce": "030b4d942e88b7674819f3020c290db1162beda60bae05964bb344378166ec61a20221e3e76bfa50c22b98832d451d51e3b7cdef420a2b06e9a29373449aa77aa46c",
|
||||
"aggregate_share": "71181e5b46742333f12672d85d0e1472770a082f0a62d3d204c9e191fb45ef91",
|
||||
"public_share": "04ec0fb2b4c1ac2d9b761f32cb2972e6d6fb74ed4195d872aeaaf4306bb64eb465580d03102849363ec49c3d1eecdd239337d0a66cdfc4d74c29c824c0f941832a"
|
||||
},
|
||||
{
|
||||
"secnonce": "847d4625ee1f0f41a485d2399b024b05d9a9b7cbb846cf107dcbfa125a136448cbd20441a5bbea0b8908781249bbd7a5f6c429e1678338d8a2f5a9095a85fd541cb49884",
|
||||
"pubnonce": "034a67a3dbe320486110fac55f4e7ef4f5c5216766b8e4d635c6f1119a5c5e75a3026ada48e6491d2c9890f65fd3f9d675f644df9224b5beb5fc41b2934bc28d9bcc",
|
||||
"aggregate_share": "15bc5e3eeb4ec318a718b3015b78e8496cc5ede81c05727936ade625532dce55",
|
||||
"public_share": "04b3ad3909e919f1a27faff7a3aec8f04a9ca54a065d9774ae37c3b9903ad4a19b71f11e148b549ef168465d065279f773175b254f64573e7ce30f4aba0954be19"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
{
|
||||
"aggregate_public_key": "bd5561ef6dbff52d3f73b8cb0c065328988b71d3386d23890744a0dd6ad27c15",
|
||||
"tweak_cache": "40252e41157cd26adda0440789236d38d3718b982853060ccbb8733f2df5bf6def6155bdeefcc6b83fe690d621b793b08f5b1562a7bef628c4db33f2d0b3f3bf2f541aaa000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"ec_tweak_add": "0444536a3cc348adb38660f06de15a6168c06d11a0ad00ca6ea34ff58a8300e2cc086d50550ee52f1d0334e31dfc67fa55201541078d89dcf79bf0c46a53e3af9b"
|
||||
}
|
26
tests/src/commonTest/resources/frost/share_agg_vectors.json
Normal file
26
tests/src/commonTest/resources/frost/share_agg_vectors.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"aggregate_public_key": "bd5561ef6dbff52d3f73b8cb0c065328988b71d3386d23890744a0dd6ad27c15",
|
||||
"tweak_cache": "40252e41157cd26adda0440789236d38d3718b982853060ccbb8733f2df5bf6def6155bdeefcc6b83fe690d621b793b08f5b1562a7bef628c4db33f2d0b3f3bf2f541aaa000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"expected": [
|
||||
{
|
||||
"aggregate_share": "1cfa28492e84e945343f1167401cdce061202a59e47e050c0c2f7f0c56e8e148",
|
||||
"public_share": "0493effba7e50d3885bb0c4665149abd4fd13622047412f1da4c0e3754ecb1a9183aaadfdf0f2f82e24641e6ed7a0f7ee22a4a8a47c6d2df66daad37a4880fffe2"
|
||||
},
|
||||
{
|
||||
"aggregate_share": "dd82fcc1806f1a968228c794a7001c18d209871fb3441bae80fd8229f6a9b0dd",
|
||||
"public_share": "048482e27b533879d4f3d68bdb2038bf9480d4ce4cc614d7133238e55179c65a175c684afb7f983e60139542b80f0f12815f3194082f07c93e1f87f3cd1b1c0d8b"
|
||||
},
|
||||
{
|
||||
"aggregate_share": "5fe629d5f34fdb3ea2f6e545fc3d2cf1f5ce23a504b144e6ebe928793cc85cb4",
|
||||
"public_share": "04d71784b58d8958141f8f405d56026f214a736e73f9c1f70776fc2e49f4e90fc0a9396bcc7471a83caf4076a18cb6ab4264aa37174ca19e142259aa5f6bb7fea3"
|
||||
},
|
||||
{
|
||||
"aggregate_share": "71181e5b46742333f12672d85d0e1472770a082f0a62d3d204c9e191fb45ef91",
|
||||
"public_share": "04ec0fb2b4c1ac2d9b761f32cb2972e6d6fb74ed4195d872aeaaf4306bb64eb465580d03102849363ec49c3d1eecdd239337d0a66cdfc4d74c29c824c0f941832a"
|
||||
},
|
||||
{
|
||||
"aggregate_share": "15bc5e3eeb4ec318a718b3015b78e8496cc5ede81c05727936ade625532dce55",
|
||||
"public_share": "04b3ad3909e919f1a27faff7a3aec8f04a9ca54a065d9774ae37c3b9903ad4a19b71f11e148b549ef168465d065279f773175b254f64573e7ce30f4aba0954be19"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
{
|
||||
"pubkeys": [
|
||||
"023590a94e768f8e1815c2f24b4d80a8e3149316c3518ce7b7ad338368d038ca66",
|
||||
"02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9",
|
||||
"03935f972da013f80ae011890fa89b67a27b7be6ccb24d3274d18b2d4067f261a9",
|
||||
"03dff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659",
|
||||
"04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9"
|
||||
],
|
||||
"valid_signers_share_gen_test_case": {
|
||||
"key_indices": [0, 1, 2, 3, 4],
|
||||
"threshold": 3,
|
||||
"signers": [
|
||||
{
|
||||
"seed": "0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F",
|
||||
"expected": {
|
||||
"frost_share": [
|
||||
"9a04e37bc40df0e1c3e05b82e7a7af6b7cdaadf337ec3eaa2b50bb9ffdb2da99",
|
||||
"8d7b24fa2421a9157ce01b7a900fe4b06dbb922def5d0f8a6b6f420e94310d27",
|
||||
"8cda987d0a9817ecb544f1ffcd7912006c6406bb95c9a9a45b70d641f6a0300c",
|
||||
"bf0eaac669eac6ac43d094bb2e07e4fa7fd4b1d317188c690aad7ea211b49bdb",
|
||||
"00c266074c34720f6d9a8511e4ec82bed44e104f93f20d9bbfbff8e2edf44400"
|
||||
],
|
||||
"vss_commitment": [
|
||||
"04bc2f60d5a7494d506e6517c49db2104b05e087536ccb1cb2730282f469782bb93e2c0029d733beeea75120e831ed71255adde4ddbd0be049419572502d7b73b9",
|
||||
"04ced2029d64827253175b5382cb327123fd2cdcdb5b2092e66020e9b6ece639f675029e36604347735eef9bf64137474b14d92d2996e67f5721705ee574c916a1",
|
||||
"044f34156e0a6d49c96298a845fd07a122490dee82b80f090b3214162ed2b030c88a2cec6cac580d63d770f1d2933f21e58ec8d4b94ca1939e590add1616f6ea38"
|
||||
],
|
||||
"pok64": "8f63ac15582fd77b4c50eef4ca7f5810e9d31ff86a5e7c0c14483edc076290bcfdb481e5c41b24419c65ac7525560a8d3bbbcc303c8c232d63650252e7aff576"
|
||||
}
|
||||
},
|
||||
{
|
||||
"seed": "F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0",
|
||||
"expected": {
|
||||
"frost_share": [
|
||||
"7adaa6dbd9df3f0944a516d13b6d7a826a956d05412a9f656b26b36e6507ff0c",
|
||||
"69cedd07db8a28a4cfd70fcd672b573b842675a9a0222239173c2dcc2f300dd1",
|
||||
"ca1df886e57b1ede4501a09b2c8e433f4503bdf9627784369650fd76a7ada20c",
|
||||
"f53e5fb436fae00581f5077cb4736af110d5fe1ceb9c4047852adfea6fa2e8b0",
|
||||
"8addd4121d313a0458b3649466f27dafadc5cb6dba441f7e1e5c4f2fb62139c0"
|
||||
],
|
||||
"vss_commitment": [
|
||||
"0498fce8a40dd9fc23fd87cc4fecd572bd87a6d961576ba7eef3e140f61655c9ee276942ef761cf0cd2845d1c6f149c73e411c2869ac8e4faeedcc6235d0954460",
|
||||
"04581308e2c7b7367f5f711a95c6f72a99b53f138cf8eafd6c0119ea038e18b51ec8e1bab854e22c42f56e0ddf6922bcac4064254700194a5a9abcdb619e96e701",
|
||||
"04ebb8b0c4fcecb70d91b9e65ea897ed8930f6d7b41cdcd150718ebbcef40b50f7fb9817fd425b598e09f3133a9100d513e9ba97bcb26b8ef371ada9669a6ed11f"
|
||||
],
|
||||
"pok64": "b2d68140014812cfa83116100deba8a6152a20a243ea73e3e4edf7f25031805f25f3dd78e6d921de1bcebc0d9a57069af905d3f1d613896f6ef69820c601e651"
|
||||
}
|
||||
},
|
||||
{
|
||||
"seed": "7FB9E0E687ADA1EEBF7ECFE2F21E73EBDB51A7D450948DFE8D76D7F2D1007671",
|
||||
"expected": {
|
||||
"frost_share": [
|
||||
"7bde839ba686bdadce0dd854f1f71455c2dd9257b2bfd1995a92e7601dc8ebb8",
|
||||
"15b62e7af6dfbc1035197fa1439b0b93639798e52a52efb964ff8eb118f0218e",
|
||||
"6a4179474660abdda21b9cb0c7f27f487b18dd96b7a78f24b2b3b2e7d53504f6",
|
||||
"97c53a07bd8b7a27a1ba83e3be7b913d55d3112a9ece472c0e3ab2f3e18e34df",
|
||||
"f5bb1c36ec28f1a2845dcfb10af91772609f401d5ab4357a2a23e4c4d4e1be0d"
|
||||
],
|
||||
"vss_commitment": [
|
||||
"046b17a9bca13b4cec2ae589973ab0471915fd77c93374d7d5dfcfacac9da7132bf2d07f55f98caf84927c97fe2946174d73609afe7d9aba0364b0abe2d174d544",
|
||||
"04abaab1242f669205aa0eb26a876a89cbe80c6c7364b1fba0d095d8181767b96d3d44e5014c2d5a82cc9cf7b9e5ae269e5bc22ffe0212ef1a0b7a2379778be500",
|
||||
"0415d97c46268a919acbfadcdc0a23fb463d94473787365a6bdd8f5bce95d6d83156448f2582bddcbdbb9bfc58cde1db47e5fb6350530bfb1102b7d0d44a70b5c0"
|
||||
],
|
||||
"pok64": "5cca5711ca05df39c82fe518e4afcd2871654bbfe6fed31b97101bfc27e1d0083d1cbc2cb3d3fa7a972bd84c7c025f71042a3a0aec27311732ae8b679bdebda3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"seed": "F95466D086770E689964664219266FE5ED215C92AE20BAB5C9D79ADDDDF3C0CF",
|
||||
"expected": {
|
||||
"frost_share": [
|
||||
"28b9d1730cd16190c0e9903144bc93f1ea5febf0046a290e00887540c7f726cf",
|
||||
"d423aafb512cb983b00bdca3e8b398d2f197db8339889b4d0934852f3a0be1cb",
|
||||
"3d71eab243f39cb2a83ccf78cb611f24544d01dc53a14616cfcf7044f4cfd8c3",
|
||||
"78b75d47b540a9f29937e7a9187abc31286e7fcafb112c25829ddec7ae73f2d8",
|
||||
"0a917790664eb85b54b2527449fe445c93a9c384b31ccc8b9ade6638ff55dd96"
|
||||
],
|
||||
"vss_commitment": [
|
||||
"04919b0ecff3c10aebec20d55f6eff6736df2cb9b0e549d633ea1711eea4def643c00c89025bd567b79e7fd808c821bca9e6516542ebd689a06f97f52bb8937b7c",
|
||||
"041583c3a25ebab0fbddbc9c9c491f889ff1df5991b0963c3c6363c6040eb505f2a9e279987f70d30415ddae2bcace1ccf2f96f1d220016c526a363aebed6c02a8",
|
||||
"04e53ec77bf28672cb36a527fd5ac2ec90b88ab99dfd54c734b4837ea84759332ff7259e9044af6a9847c9432f023a5c7039f95313d564572a326b0e34b3e2299d"
|
||||
],
|
||||
"pok64": "ff6bf4b90faf7a66dc54bfa2cd047be44e1c42622410d6e3a23697d30212d3bde11fc7fb4e61b2bc1e4310ad86089721ba912191d7ca561b9c5dcbcb604116fc"
|
||||
}
|
||||
},
|
||||
{
|
||||
"seed": "012ABBCB52B3016AC03AD82395A1A415C48B93DEF78718E62A7A90052FE224FB",
|
||||
"expected": {
|
||||
"frost_share": [
|
||||
"638248e2dd3f9a1b9cc2368ce6540aa841d04ae712ce6ccc9a417076aeda779e",
|
||||
"fc5f214938b6d348504c400783763bc40055c4ad1e7a9f5c0fc2bb8880b9150e",
|
||||
"613a34d878e85be35e57e6816ee23942ea5e394a5fb88247f748eead74e22f65",
|
||||
"ac4e7c9132c25867f06e6b13a39c7714982a5dfd7ba8748323900cf05a8f0712",
|
||||
"89cf905e2f716d0707baa735baa28c096bc6c8561e8f83d11334102e7b4d3774"
|
||||
],
|
||||
"vss_commitment": [
|
||||
"04f63b636f91fda4fa756c64cd34c186e4595a5b4a6ed6351ba0e99961f2a189001f6f708312ed0ac8c1e291d6064e236e265f2b1b561925a29a39c2d3ad9f866b",
|
||||
"040269fedd5bdb0d7bed5dd50f9fec28415d51694441dc6c5b0b3e2fd577429c40832c6263e9d218f6ae8cc6a87f72d69d3dc0096a8779d7530fec4983ed6c95b1",
|
||||
"04fa8cf2f9a25d2d5b52872dd27afd89dfff5bad33f3335726fc56b49989f64c041e765acd4719bad295620393b00ec3f1b5420f02bad4794998d5f26c435deee5"
|
||||
],
|
||||
"pok64": "6888a2ffd199e69806e2c067befc41bd58de0605407542163031e8d75dd1cf80b7fae9e12c8e618081cf79a84d2a3a293dd260234383e5ab587f4459c5963d15"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
182
tests/src/commonTest/resources/frost/share_gen_vectors.json
Normal file
182
tests/src/commonTest/resources/frost/share_gen_vectors.json
Normal file
@ -0,0 +1,182 @@
|
||||
{
|
||||
"pubkeys": [
|
||||
"023590a94e768f8e1815c2f24b4d80a8e3149316c3518ce7b7ad338368d038ca66",
|
||||
"02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9",
|
||||
"03935f972da013f80ae011890fa89b67a27b7be6ccb24d3274d18b2d4067f261a9",
|
||||
"03dff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659",
|
||||
"04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9"
|
||||
],
|
||||
"valid_share_gen_test_cases": [
|
||||
{
|
||||
"seed": "0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F",
|
||||
"key_indices": [0, 1, 2, 3, 4],
|
||||
"threshold": 3,
|
||||
"expected": {
|
||||
"frost_share": [
|
||||
"9a04e37bc40df0e1c3e05b82e7a7af6b7cdaadf337ec3eaa2b50bb9ffdb2da99",
|
||||
"8d7b24fa2421a9157ce01b7a900fe4b06dbb922def5d0f8a6b6f420e94310d27",
|
||||
"8cda987d0a9817ecb544f1ffcd7912006c6406bb95c9a9a45b70d641f6a0300c",
|
||||
"bf0eaac669eac6ac43d094bb2e07e4fa7fd4b1d317188c690aad7ea211b49bdb",
|
||||
"00c266074c34720f6d9a8511e4ec82bed44e104f93f20d9bbfbff8e2edf44400"
|
||||
],
|
||||
"vss_commitment": [
|
||||
"04bc2f60d5a7494d506e6517c49db2104b05e087536ccb1cb2730282f469782bb93e2c0029d733beeea75120e831ed71255adde4ddbd0be049419572502d7b73b9",
|
||||
"04ced2029d64827253175b5382cb327123fd2cdcdb5b2092e66020e9b6ece639f675029e36604347735eef9bf64137474b14d92d2996e67f5721705ee574c916a1",
|
||||
"044f34156e0a6d49c96298a845fd07a122490dee82b80f090b3214162ed2b030c88a2cec6cac580d63d770f1d2933f21e58ec8d4b94ca1939e590add1616f6ea38"
|
||||
],
|
||||
"pok64": "8f63ac15582fd77b4c50eef4ca7f5810e9d31ff86a5e7c0c14483edc076290bcfdb481e5c41b24419c65ac7525560a8d3bbbcc303c8c232d63650252e7aff576"
|
||||
}
|
||||
},
|
||||
{
|
||||
"seed": "0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F",
|
||||
"key_indices": [4, 3, 2, 1, 0],
|
||||
"threshold": 3,
|
||||
"expected": {
|
||||
"frost_share": [
|
||||
"7f3d4b3c13fe574ce2b24412aff67f19cc3d198b40e6bb57865df4f519ef65fa",
|
||||
"9a46587ac615fc8afd0bc3a234075fb190f20a951df57ce21c7c7f6331117ab3",
|
||||
"9188f7516271f4e4d9c71837db17f3a4e9ee1f979ca0245405ed13f88d8d54f2",
|
||||
"afea9adf149439f663bb787190f7503984e632823607433e7c0d86884ba3ac58",
|
||||
"11492bef4d7f35e038a1270a3a1a0a55c8e72a313d6086fec4bbdbe0522b9575"
|
||||
],
|
||||
"vss_commitment": [
|
||||
"04ba2dd42da281b300358ed004da8755cc5b93c2ea56c8b79d63492501f2a8e69e4beb0f7a7adb01f2edec1ec84e338db286319f5be9faa82c2ac9ccd976e459eb",
|
||||
"04d0be30dbc1da6ed9c84e33bf976671b7fadd38ee2243ec1c86780456092b4b5504416548cfae342746b8cde08a3aa3323c338a125031fbb31693e4fa94d4c3d9",
|
||||
"04146aa11a44a2d2e1c53eb91f274c2145ff9d69d7af27ec85d34fb0df318bc4c9ab75f2d473b03fb133b0d8c1166dd6116c934fa7ff1498df2e6a51650a32940a"
|
||||
],
|
||||
"pok64": "c5fc820d0d64d447eec6c5f5eade3c08f2a0dac2fa244be7558247989c71a14dd51bfe784983a84fefc76fd4d049434d88df02b5474c14304150b9dce304e842"
|
||||
}
|
||||
},
|
||||
{
|
||||
"seed": "0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F",
|
||||
"key_indices": [0, 0, 0],
|
||||
"threshold": 2,
|
||||
"expected": {
|
||||
"frost_share": [
|
||||
"6a2ca571b1b83fbe65bb589949538f2d4c6cada3aa914232b96160584642262b",
|
||||
"6a2ca571b1b83fbe65bb589949538f2d4c6cada3aa914232b96160584642262b",
|
||||
"6a2ca571b1b83fbe65bb589949538f2d4c6cada3aa914232b96160584642262b"
|
||||
],
|
||||
"vss_commitment": [
|
||||
"04a17464b9a1c4e7fb1f22221ba552640d891fdaca04b6a4c9570be4bfb8fdc0c0ca13853d1ea1eccf8e8f81d79d1500dac44a53282f58d17468e358ff78bc7e14",
|
||||
"04af3c0779299cd72cf5f5dbe919068f4d9d3b525a48665363fa8b1ca26d6908cf477c156e3603f6cfe3d6f394d2a9e9f55e86d7b90a2ad2f0ea62f6ac4bd1f4b2"
|
||||
],
|
||||
"pok64": "a006b9b996888d552ce756bf7f217228e487fe510473c08b025ff145ccae6c015265e4f3ca39d488068f385afe0766b636fcbd3a5ac4cdd96f90e704dcce58d7"
|
||||
}
|
||||
},
|
||||
{
|
||||
"seed": "0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F",
|
||||
"key_indices": [0, 0, 1, 1],
|
||||
"threshold": 2,
|
||||
"expected": {
|
||||
"frost_share": [
|
||||
"9b84df35de7d30724c5ef2593c40e349e086e43bcbc4534034e0312510e55e6d",
|
||||
"9b84df35de7d30724c5ef2593c40e349e086e43bcbc4534034e0312510e55e6d",
|
||||
"ade61b5f4aec435ac0c4ff7ef61280a9f00150f88068cd391be3ea2f50128199",
|
||||
"ade61b5f4aec435ac0c4ff7ef61280a9f00150f88068cd391be3ea2f50128199"
|
||||
],
|
||||
"vss_commitment": [
|
||||
"04840febfcb959d1425df55ae74b78dbd1f9641438fbc6d5e5fc8a315dfdca30106b8846c149a27b2896a312e598cd4b447b375a46b5787f01ecd0a8b642bf5017",
|
||||
"04935008aeb03eef3dfee5b9c42d1575dd53c8ab8078cabf38d8bd1768e1ee1f92ffe6bb6997b085104a048cac6ba7d7fa6a57c41c639ebfde516ce6d9292dc84b"
|
||||
],
|
||||
"pok64": "9d3c1f08ff8215d65e25ea8678ac923d089f10d7be7a77d2fa2f802174094d62d0f713dd874570ad4bdcbfc17cf02464523387893d85c134f201fee7cdbe3b14"
|
||||
}
|
||||
}
|
||||
],
|
||||
"valid_signers_share_gen_test_case": {
|
||||
"key_indices": [0, 1, 2, 3, 4],
|
||||
"threshold": 3,
|
||||
"signers": [
|
||||
{
|
||||
"seed": "0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F",
|
||||
"expected": {
|
||||
"frost_share": [
|
||||
"9a04e37bc40df0e1c3e05b82e7a7af6b7cdaadf337ec3eaa2b50bb9ffdb2da99",
|
||||
"8d7b24fa2421a9157ce01b7a900fe4b06dbb922def5d0f8a6b6f420e94310d27",
|
||||
"8cda987d0a9817ecb544f1ffcd7912006c6406bb95c9a9a45b70d641f6a0300c",
|
||||
"bf0eaac669eac6ac43d094bb2e07e4fa7fd4b1d317188c690aad7ea211b49bdb",
|
||||
"00c266074c34720f6d9a8511e4ec82bed44e104f93f20d9bbfbff8e2edf44400"
|
||||
],
|
||||
"vss_commitment": [
|
||||
"04bc2f60d5a7494d506e6517c49db2104b05e087536ccb1cb2730282f469782bb93e2c0029d733beeea75120e831ed71255adde4ddbd0be049419572502d7b73b9",
|
||||
"04ced2029d64827253175b5382cb327123fd2cdcdb5b2092e66020e9b6ece639f675029e36604347735eef9bf64137474b14d92d2996e67f5721705ee574c916a1",
|
||||
"044f34156e0a6d49c96298a845fd07a122490dee82b80f090b3214162ed2b030c88a2cec6cac580d63d770f1d2933f21e58ec8d4b94ca1939e590add1616f6ea38"
|
||||
],
|
||||
"pok64": "8f63ac15582fd77b4c50eef4ca7f5810e9d31ff86a5e7c0c14483edc076290bcfdb481e5c41b24419c65ac7525560a8d3bbbcc303c8c232d63650252e7aff576"
|
||||
}
|
||||
},
|
||||
{
|
||||
"seed": "F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0",
|
||||
"expected": {
|
||||
"frost_share": [
|
||||
"7adaa6dbd9df3f0944a516d13b6d7a826a956d05412a9f656b26b36e6507ff0c",
|
||||
"69cedd07db8a28a4cfd70fcd672b573b842675a9a0222239173c2dcc2f300dd1",
|
||||
"ca1df886e57b1ede4501a09b2c8e433f4503bdf9627784369650fd76a7ada20c",
|
||||
"f53e5fb436fae00581f5077cb4736af110d5fe1ceb9c4047852adfea6fa2e8b0",
|
||||
"8addd4121d313a0458b3649466f27dafadc5cb6dba441f7e1e5c4f2fb62139c0"
|
||||
],
|
||||
"vss_commitment": [
|
||||
"0498fce8a40dd9fc23fd87cc4fecd572bd87a6d961576ba7eef3e140f61655c9ee276942ef761cf0cd2845d1c6f149c73e411c2869ac8e4faeedcc6235d0954460",
|
||||
"04581308e2c7b7367f5f711a95c6f72a99b53f138cf8eafd6c0119ea038e18b51ec8e1bab854e22c42f56e0ddf6922bcac4064254700194a5a9abcdb619e96e701",
|
||||
"04ebb8b0c4fcecb70d91b9e65ea897ed8930f6d7b41cdcd150718ebbcef40b50f7fb9817fd425b598e09f3133a9100d513e9ba97bcb26b8ef371ada9669a6ed11f"
|
||||
],
|
||||
"pok64": "b2d68140014812cfa83116100deba8a6152a20a243ea73e3e4edf7f25031805f25f3dd78e6d921de1bcebc0d9a57069af905d3f1d613896f6ef69820c601e651"
|
||||
}
|
||||
},
|
||||
{
|
||||
"seed": "7FB9E0E687ADA1EEBF7ECFE2F21E73EBDB51A7D450948DFE8D76D7F2D1007671",
|
||||
"expected": {
|
||||
"frost_share": [
|
||||
"7bde839ba686bdadce0dd854f1f71455c2dd9257b2bfd1995a92e7601dc8ebb8",
|
||||
"15b62e7af6dfbc1035197fa1439b0b93639798e52a52efb964ff8eb118f0218e",
|
||||
"6a4179474660abdda21b9cb0c7f27f487b18dd96b7a78f24b2b3b2e7d53504f6",
|
||||
"97c53a07bd8b7a27a1ba83e3be7b913d55d3112a9ece472c0e3ab2f3e18e34df",
|
||||
"f5bb1c36ec28f1a2845dcfb10af91772609f401d5ab4357a2a23e4c4d4e1be0d"
|
||||
],
|
||||
"vss_commitment": [
|
||||
"046b17a9bca13b4cec2ae589973ab0471915fd77c93374d7d5dfcfacac9da7132bf2d07f55f98caf84927c97fe2946174d73609afe7d9aba0364b0abe2d174d544",
|
||||
"04abaab1242f669205aa0eb26a876a89cbe80c6c7364b1fba0d095d8181767b96d3d44e5014c2d5a82cc9cf7b9e5ae269e5bc22ffe0212ef1a0b7a2379778be500",
|
||||
"0415d97c46268a919acbfadcdc0a23fb463d94473787365a6bdd8f5bce95d6d83156448f2582bddcbdbb9bfc58cde1db47e5fb6350530bfb1102b7d0d44a70b5c0"
|
||||
],
|
||||
"pok64": "5cca5711ca05df39c82fe518e4afcd2871654bbfe6fed31b97101bfc27e1d0083d1cbc2cb3d3fa7a972bd84c7c025f71042a3a0aec27311732ae8b679bdebda3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"seed": "F95466D086770E689964664219266FE5ED215C92AE20BAB5C9D79ADDDDF3C0CF",
|
||||
"expected": {
|
||||
"frost_share": [
|
||||
"28b9d1730cd16190c0e9903144bc93f1ea5febf0046a290e00887540c7f726cf",
|
||||
"d423aafb512cb983b00bdca3e8b398d2f197db8339889b4d0934852f3a0be1cb",
|
||||
"3d71eab243f39cb2a83ccf78cb611f24544d01dc53a14616cfcf7044f4cfd8c3",
|
||||
"78b75d47b540a9f29937e7a9187abc31286e7fcafb112c25829ddec7ae73f2d8",
|
||||
"0a917790664eb85b54b2527449fe445c93a9c384b31ccc8b9ade6638ff55dd96"
|
||||
],
|
||||
"vss_commitment": [
|
||||
"04919b0ecff3c10aebec20d55f6eff6736df2cb9b0e549d633ea1711eea4def643c00c89025bd567b79e7fd808c821bca9e6516542ebd689a06f97f52bb8937b7c",
|
||||
"041583c3a25ebab0fbddbc9c9c491f889ff1df5991b0963c3c6363c6040eb505f2a9e279987f70d30415ddae2bcace1ccf2f96f1d220016c526a363aebed6c02a8",
|
||||
"04e53ec77bf28672cb36a527fd5ac2ec90b88ab99dfd54c734b4837ea84759332ff7259e9044af6a9847c9432f023a5c7039f95313d564572a326b0e34b3e2299d"
|
||||
],
|
||||
"pok64": "ff6bf4b90faf7a66dc54bfa2cd047be44e1c42622410d6e3a23697d30212d3bde11fc7fb4e61b2bc1e4310ad86089721ba912191d7ca561b9c5dcbcb604116fc"
|
||||
}
|
||||
},
|
||||
{
|
||||
"seed": "012ABBCB52B3016AC03AD82395A1A415C48B93DEF78718E62A7A90052FE224FB",
|
||||
"expected": {
|
||||
"frost_share": [
|
||||
"638248e2dd3f9a1b9cc2368ce6540aa841d04ae712ce6ccc9a417076aeda779e",
|
||||
"fc5f214938b6d348504c400783763bc40055c4ad1e7a9f5c0fc2bb8880b9150e",
|
||||
"613a34d878e85be35e57e6816ee23942ea5e394a5fb88247f748eead74e22f65",
|
||||
"ac4e7c9132c25867f06e6b13a39c7714982a5dfd7ba8748323900cf05a8f0712",
|
||||
"89cf905e2f716d0707baa735baa28c096bc6c8561e8f83d11334102e7b4d3774"
|
||||
],
|
||||
"vss_commitment": [
|
||||
"04f63b636f91fda4fa756c64cd34c186e4595a5b4a6ed6351ba0e99961f2a189001f6f708312ed0ac8c1e291d6064e236e265f2b1b561925a29a39c2d3ad9f866b",
|
||||
"040269fedd5bdb0d7bed5dd50f9fec28415d51694441dc6c5b0b3e2fd577429c40832c6263e9d218f6ae8cc6a87f72d69d3dc0096a8779d7530fec4983ed6c95b1",
|
||||
"04fa8cf2f9a25d2d5b52872dd27afd89dfff5bad33f3335726fc56b49989f64c041e765acd4719bad295620393b00ec3f1b5420f02bad4794998d5f26c435deee5"
|
||||
],
|
||||
"pok64": "6888a2ffd199e69806e2c067befc41bd58de0605407542163031e8d75dd1cf80b7fae9e12c8e618081cf79a84d2a3a293dd260234383e5ab587f4459c5963d15"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
144
tests/src/commonTest/resources/musig2/det_sign_vectors.json
Normal file
144
tests/src/commonTest/resources/musig2/det_sign_vectors.json
Normal file
@ -0,0 +1,144 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
88
tests/src/commonTest/resources/musig2/key_agg_vectors.json
Normal file
88
tests/src/commonTest/resources/musig2/key_agg_vectors.json
Normal file
@ -0,0 +1,88 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
18
tests/src/commonTest/resources/musig2/key_sort_vectors.json
Normal file
18
tests/src/commonTest/resources/musig2/key_sort_vectors.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"pubkeys": [
|
||||
"02DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8",
|
||||
"02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
|
||||
"03DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659",
|
||||
"023590A94E768F8E1815C2F24B4D80A8E3149316C3518CE7B7AD338368D038CA66",
|
||||
"02DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EFF",
|
||||
"02DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8"
|
||||
],
|
||||
"sorted_pubkeys": [
|
||||
"023590A94E768F8E1815C2F24B4D80A8E3149316C3518CE7B7AD338368D038CA66",
|
||||
"02DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8",
|
||||
"02DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8",
|
||||
"02DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EFF",
|
||||
"02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
|
||||
"03DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"
|
||||
]
|
||||
}
|
51
tests/src/commonTest/resources/musig2/nonce_agg_vectors.json
Normal file
51
tests/src/commonTest/resources/musig2/nonce_agg_vectors.json
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
44
tests/src/commonTest/resources/musig2/nonce_gen_vectors.json
Normal file
44
tests/src/commonTest/resources/musig2/nonce_gen_vectors.json
Normal file
@ -0,0 +1,44 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
151
tests/src/commonTest/resources/musig2/sig_agg_vectors.json
Normal file
151
tests/src/commonTest/resources/musig2/sig_agg_vectors.json
Normal file
@ -0,0 +1,151 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
212
tests/src/commonTest/resources/musig2/sign_verify_vectors.json
Normal file
212
tests/src/commonTest/resources/musig2/sign_verify_vectors.json
Normal file
@ -0,0 +1,212 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
84
tests/src/commonTest/resources/musig2/tweak_vectors.json
Normal file
84
tests/src/commonTest/resources/musig2/tweak_vectors.json
Normal file
@ -0,0 +1,84 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user