Compare commits

..

3 Commits

Author SHA1 Message Date
sstone
71c5574e8d
Update cinterop def file 2024-02-14 09:57:15 +01:00
sstone
d0c7d79d9b
Update CI check script 2024-02-14 09:52:01 +01:00
sstone
6892b43203
Update snapshot publishing script 2024-02-14 09:47:36 +01:00
43 changed files with 585 additions and 6094 deletions

View File

@ -87,7 +87,7 @@ jobs:
- name: Check JVM (Windows)
if: matrix.os == 'windows-latest'
shell: msys2 {0}
run: ./gradlew jvmTest
run: ./gradlew mingwX64Test jvmTest
- name: Check Linux
if: matrix.os == 'ubuntu-latest'
shell: bash
@ -95,7 +95,7 @@ jobs:
- name: Check iOS
if: matrix.os == 'macOS-latest'
shell: bash
run: ./gradlew iosX64Test
run: ./gradlew macosX64Test iosX64Test
- name: Check Android
if: matrix.os == 'macOS-latest'
uses: reactivecircus/android-emulator-runner@v2
@ -112,7 +112,7 @@ jobs:
- name: Publish Windows
if: matrix.os == 'windows-latest'
shell: msys2 {0}
run: ./gradlew :jni:jvm:mingw:publishToMavenLocal -PsnapshotNumber=${{ github.run_number }} -PgitRef=${{ github.ref }}
run: ./gradlew publishMingwX64PublicationToMavenLocal :jni:jvm:mingw:publishToMavenLocal -PsnapshotNumber=${{ github.run_number }} -PgitRef=${{ github.ref }}
- name: Publish MacOS
if: matrix.os == 'macOS-latest'
shell: bash

View File

@ -93,7 +93,7 @@ jobs:
- name: Check JVM (Windows)
if: matrix.os == 'windows-latest'
shell: msys2 {0}
run: ./gradlew jvmTest
run: ./gradlew jvmTest mingwX64Test
- name: Check Linux
if: matrix.os == 'ubuntu-latest'
shell: bash
@ -101,7 +101,7 @@ jobs:
- name: Check iOS
if: matrix.os == 'macOS-latest'
shell: bash
run: ./gradlew iosX64Test
run: ./gradlew iosX64Test macosX64Test
- name: Check Android
if: matrix.os == 'macOS-latest'
uses: reactivecircus/android-emulator-runner@v2

3
.gitignore vendored
View File

@ -13,6 +13,3 @@ local.properties
# OS
.DS_Store
jni/bin/*
jni/jvm/bin/*
/jni/src/main/java/fr/acinq/secp256k1/Secp256k1CFunctions.class

2
.gitmodules vendored
View File

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

View File

@ -22,7 +22,7 @@ buildscript {
allprojects {
group = "fr.acinq.secp256k1"
version = "frost-SNAPSHOT"
version = "0.14.0-SNAPSHOT"
repositories {
google()
@ -58,28 +58,29 @@ kotlin {
secp256k1CInterop("host")
}
if (currentOs.isMacOsX) {
macosX64 {
secp256k1CInterop("host")
}
macosArm64 {
secp256k1CInterop("host")
}
iosX64 {
secp256k1CInterop("ios")
}
iosArm64 {
secp256k1CInterop("ios")
}
iosSimulatorArm64 {
secp256k1CInterop("ios")
}
macosX64 {
secp256k1CInterop("host")
}
macosArm64 {
secp256k1CInterop("host")
}
mingwX64 {
secp256k1CInterop("host")
}
iosX64 {
secp256k1CInterop("ios")
}
iosArm64 {
secp256k1CInterop("ios")
}
iosSimulatorArm64 {
secp256k1CInterop("ios")
}
sourceSets.all {
languageSettings.optIn("kotlin.RequiresOptIn")

View File

@ -6,7 +6,6 @@ org.gradle.parallel = true
kotlin.code.style = official
kotlin.native.ignoreDisabledTargets = true
kotlin.mpp.enableCInteropCommonization=true
kotlin.native.cacheKind.macosArm64=none
# Android
android.useAndroidX = true

View File

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

View File

@ -1,25 +1,13 @@
import org.gradle.internal.os.OperatingSystem
plugins {
`java-library`
id("org.jetbrains.dokka")
`maven-publish`
}
val currentOs = OperatingSystem.current()
dependencies {
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"))
}
api(project(":jni:jvm:darwin"))
api(project(":jni:jvm:linux"))
api(project(":jni:jvm:mingw"))
}
publishing {

View File

@ -17,7 +17,6 @@ if [ "$TARGET" == "linux" ]; then
CC_OPTS="-fPIC"
elif [ "$TARGET" == "darwin" ]; then
OUTFILE=libsecp256k1-jni.dylib
CC_OPTS="-arch arm64 -arch x86_64"
elif [ "$TARGET" == "mingw" ]; then
OUTFILE=secp256k1-jni.dll
CC=x86_64-w64-mingw32-gcc

View File

@ -12,8 +12,12 @@ dependencies {
val copyJni by tasks.creating(Sync::class) {
onlyIf { org.gradle.internal.os.OperatingSystem.current().isMacOsX }
dependsOn(":jni:jvm:buildNativeHost")
val arch = when (System.getProperty("os.arch")) {
"aarch64" -> "aarch64"
else -> "x86_64"
}
from(rootDir.resolve("jni/jvm/build/darwin/libsecp256k1-jni.dylib"))
into(buildDir.resolve("jniResources/fr/acinq/secp256k1/jni/native/darwin"))
into(buildDir.resolve("jniResources/fr/acinq/secp256k1/jni/native/darwin-$arch"))
}
(tasks["processResources"] as ProcessResources).apply {

View File

@ -19,8 +19,7 @@ internal object OSInfo {
private const val PPC = "ppc"
private const val PPC64 = "ppc64"
// on macos we build a universal library that contains arm64 and x64 binaries
@JvmStatic val nativeSuffix: String get() = if (os == "darwin") os else "$os-$arch"
@JvmStatic val nativeSuffix: String get() = "$os-$arch"
@JvmStatic val os: String get() = translateOSName(System.getProperty("os.name"))

View File

@ -29,32 +29,12 @@ public class Secp256k1CFunctions {
public static final int SECP256K1_EC_COMPRESSED = (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION);
public static final int SECP256K1_EC_UNCOMPRESSED = (SECP256K1_FLAGS_TYPE_COMPRESSION);
/**
* A musig2 public nonce is simply two elliptic curve points.
*/
public static final int SECP256K1_MUSIG_PUBLIC_NONCE_SIZE = 66;
/**
* A musig2 private nonce is basically two scalars, but should be treated as an opaque blob.
*/
public static final int SECP256K1_MUSIG_SECRET_NONCE_SIZE = 132;
/**
* When aggregating public keys, we cache information in an opaque blob (must not be interpreted).
*/
public static final int SECP256K1_MUSIG_KEYAGG_CACHE_SIZE = 197;
/**
* When creating partial signatures and aggregating them, session data is kept in an opaque blob (must not be interpreted).
*/
public static final int SECP256K1_MUSIG_SESSION_SIZE = 133;
public static native long secp256k1_context_create(int flags);
public static native void secp256k1_context_destroy(long ctx);
public static native int secp256k1_ec_seckey_verify(long ctx, byte[] seckey);
public static native byte[] secp256k1_ec_pubkey_parse(long ctx, byte[] pubkey);
public static native byte[] secp256k1_ec_pubkey_create(long ctx, byte[] seckey);
@ -88,311 +68,4 @@ public class Secp256k1CFunctions {
public static native byte[] secp256k1_schnorrsig_sign(long ctx, byte[] msg, byte[] seckey, byte[] aux_rand32);
public static native int secp256k1_schnorrsig_verify(long ctx, byte[] sig, byte[] msg, byte[] pubkey);
public static native byte[] secp256k1_musig_nonce_gen(long ctx, byte[] session_id32, byte[] seckey, byte[] pubkey, byte[] msg32, byte[] keyagg_cache, byte[] extra_input32);
public static native byte[] secp256k1_musig_nonce_agg(long ctx, byte[][] nonces);
public static native byte[] secp256k1_musig_pubkey_agg(long ctx, byte[][] pubkeys, byte[] keyagg_cache);
public static native byte[] secp256k1_musig_pubkey_ec_tweak_add(long ctx, byte[] keyagg_cache, byte[] tweak32);
public static native byte[] secp256k1_musig_pubkey_xonly_tweak_add(long ctx, byte[] keyagg_cache, byte[] tweak32);
public static native byte[] secp256k1_musig_nonce_process(long ctx, byte[] aggnonce, byte[] msg32, byte[] keyagg_cache);
public static native byte[] secp256k1_musig_partial_sign(long ctx, byte[] secnonce, byte[] privkey, byte[] keyagg_cache, byte[] session);
public static native int secp256k1_musig_partial_sig_verify(long ctx, byte[] psig, byte[] pubnonce, byte[] pubkey, byte[] keyagg_cache, byte[] session);
public static native byte[] secp256k1_musig_partial_sig_agg(long ctx, byte[] session, byte[][] psigs);
/**
* 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);
}

View File

@ -92,248 +92,6 @@ public object NativeSecp256k1 : Secp256k1 {
return Secp256k1CFunctions.secp256k1_schnorrsig_sign(Secp256k1Context.getContext(), data, sec, auxrand32)
}
override fun musigNonceGen(sessionId32: ByteArray, privkey: ByteArray?, aggpubkey: ByteArray, msg32: ByteArray?, keyaggCache: ByteArray?, extraInput32: ByteArray?): ByteArray {
return Secp256k1CFunctions.secp256k1_musig_nonce_gen(Secp256k1Context.getContext(), sessionId32, privkey, aggpubkey, msg32, keyaggCache, extraInput32)
}
override fun musigNonceAgg(pubnonces: Array<ByteArray>): ByteArray {
return Secp256k1CFunctions.secp256k1_musig_nonce_agg(Secp256k1Context.getContext(), pubnonces)
}
override fun musigPubkeyAgg(pubkeys: Array<ByteArray>, keyaggCache: ByteArray?): ByteArray {
return Secp256k1CFunctions.secp256k1_musig_pubkey_agg(Secp256k1Context.getContext(), pubkeys, keyaggCache)
}
override fun musigPubkeyTweakAdd(keyaggCache: ByteArray, tweak32: ByteArray): ByteArray {
return Secp256k1CFunctions.secp256k1_musig_pubkey_ec_tweak_add(Secp256k1Context.getContext(), keyaggCache, tweak32)
}
override fun musigPubkeyXonlyTweakAdd(keyaggCache: ByteArray, tweak32: ByteArray): ByteArray {
return Secp256k1CFunctions.secp256k1_musig_pubkey_xonly_tweak_add(Secp256k1Context.getContext(), keyaggCache, tweak32)
}
override fun musigNonceProcess(aggnonce: ByteArray, msg32: ByteArray, keyaggCache: ByteArray): ByteArray {
return Secp256k1CFunctions.secp256k1_musig_nonce_process(Secp256k1Context.getContext(), aggnonce, msg32, keyaggCache)
}
override fun musigPartialSign(secnonce: ByteArray, privkey: ByteArray, keyaggCache: ByteArray, session: ByteArray): ByteArray {
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())
}

View File

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

View File

@ -6,7 +6,7 @@ cp xconfigure.sh secp256k1
cd secp256k1
./autogen.sh
sh xconfigure.sh --enable-experimental --enable-module_ecdh --enable-module-recovery --enable-module-schnorrsig --enable-module-musig --enable-module-frost --enable-benchmark=no --enable-shared=no --enable-exhaustive-tests=no --enable-tests=no
sh xconfigure.sh --enable-experimental --enable-module_ecdh --enable-module-recovery --enable-module-schnorrsig --enable-benchmark=no --enable-shared=no --enable-exhaustive-tests=no --enable-tests=no
mkdir -p ../build/ios
cp -v _build/universal/ios/* ../build/ios/

View File

@ -12,20 +12,18 @@ cd "$(dirname "$0")"
cd secp256k1
if [ "$TARGET" == "mingw" ]; then
CFLAGS="-fPIC"
CONF_OPTS=" --host=x86_64-w64-mingw32"
CONF_OPTS="CFLAGS=-fPIC --host=x86_64-w64-mingw32"
elif [ "$TARGET" == "linux" ]; then
CFLAGS="-fPIC"
CONF_OPTS="CFLAGS=-fPIC"
elif [ "$TARGET" == "darwin" ]; then
CFLAGS="-arch arm64 -arch x86_64"
LDFLAGS="-arch arm64 -arch x86_64"
CONF_OPTS=""
else
echo "Unknown TARGET=$TARGET"
exit 1
fi
./autogen.sh
CFLAGS="$CFLAGS" LDFLAGS="$LDFLAGS" ./configure $CONF_OPTS --enable-experimental --enable-module_ecdh --enable-module-recovery --enable-module-schnorrsig --enable-module-musig --enable-module-frost --enable-benchmark=no --enable-shared=no --enable-exhaustive-tests=no --enable-tests=no
./configure $CONF_OPTS --enable-experimental --enable-module_ecdh --enable-module-recovery --enable-module-schnorrsig --enable-benchmark=no --enable-shared=no --enable-exhaustive-tests=no --enable-tests=no
make clean
make

@ -1 +1 @@
Subproject commit 69d5e6bdc1ec8b472138aaba9804b93d46f397bb
Subproject commit 1ad5185cd42c0636104129fcc9f6a4bf9c67cc40

View File

@ -1,10 +1,5 @@
#!/bin/bash -x
if [[ -z "${VERSION}" ]]; then
echo "VERSION is not defined"
exit 1
fi
if [ $# -eq 0 ]
then
echo "specify either snapshot or release"

View File

@ -1,10 +1,5 @@
#!/bin/bash -x
if [[ -z "${VERSION}" ]]; then
echo "VERSION is not defined"
exit 1
fi
if [ $# -eq 0 ]
then
echo "specify either snapshot or release"

View File

@ -21,21 +21,22 @@ mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/conte
-Djavadoc=$ARTIFACT_ID_BASE-$VERSION-javadoc.jar
popd
pushd .
for i in iosarm64 iossimulatorarm64 iosx64 macosarm64 macosx64 jni-android jni-common jni-jvm-darwin jni-jvm-extract jni-jvm-linux jni-jvm-mingw jni-jvm jvm linuxx64; do
for i in iosarm64 iossimulatorarm64 iosx64 jni-android jni-common jni-jvm-darwin jni-jvm-extract jni-jvm-linux jni-jvm-mingw jni-jvm jvm linuxx64 macosarm64 macosx64 mingwx64; do
cd fr/acinq/secp256k1/secp256k1-kmp-$i/$VERSION
case $i in
iosarm64 | iossimulatorarm64 | iosx64 | macosarm64 | macosx64)
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.klib \
-Dfiles=$ARTIFACT_ID_BASE-$i-$VERSION-metadata.jar,$ARTIFACT_ID_BASE-$i-$VERSION.module,$ARTIFACT_ID_BASE-$i-$VERSION-cinterop-libsecp256k1.klib \
-Dtypes=jar,module,klib \
-Dclassifiers=metadata,,cinterop-libsecp256k1 \
-Dsources=$ARTIFACT_ID_BASE-$i-$VERSION-sources.jar \
-Djavadoc=$ARTIFACT_ID_BASE-$i-$VERSION-javadoc.jar
;;
linuxx64)
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.klib \
-Dfiles=$ARTIFACT_ID_BASE-$i-$VERSION-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 | mingwx64)
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 \
@ -45,6 +46,7 @@ for i in iosarm64 iossimulatorarm64 iosx64 macosarm64 macosx64 jni-android jni-c
-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 \
@ -55,6 +57,7 @@ for i in iosarm64 iossimulatorarm64 iosx64 macosarm64 macosx64 jni-android jni-c
-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 \

View File

@ -3,22 +3,9 @@
# first you must sign all files:
# find release -type f -print -exec gpg -ab {} \;
if [[ -z "${VERSION}" ]]; then
echo "VERSION is not defined"
exit 1
fi
if [[ -z "${OSS_USER}" ]]; then
echo "OSS_USER is not defined"
exit 1
fi
read -p "Password : " -s OSS_PASSWORD
VERSION=0.6.2
for i in secp256k1-kmp \
secp256k1-kmp-iosarm64 \
secp256k1-kmp-iossimulatorarm64 \
secp256k1-kmp-iosx64 \
secp256k1-kmp-jni-android \
secp256k1-kmp-jni-common \
@ -28,14 +15,14 @@ for i in secp256k1-kmp \
secp256k1-kmp-jni-jvm-linux \
secp256k1-kmp-jni-jvm-mingw \
secp256k1-kmp-jvm \
secp256k1-kmp-linuxx64
secp256k1-kmp-linux
do
pushd .
cd release/fr/acinq/secp256k1/$i/$VERSION
pwd
jar -cvf bundle.jar *
# use correct sonatype credentials here
curl -v -XPOST -u $OSS_USER:$OSS_PASSWORD --upload-file bundle.jar https://oss.sonatype.org/service/local/staging/bundle_upload
curl -v -XPOST -u USER:PASSWORD --upload-file bundle.jar https://oss.sonatype.org/service/local/staging/bundle_upload
popd
done

View File

@ -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,166 +149,10 @@ public interface Secp256k1 {
compressed[0] = if (pubkey.last() % 2 == 0) 2.toByte() else 3.toByte()
compressed
}
else -> throw Secp256k1Exception("invalid public key")
}
}
/**
* Generate a secret nonce to be used in a musig2 signing session.
* This nonce must never be persisted or reused across signing sessions.
* All optional arguments exist to enrich the quality of the randomness used, which is critical for security.
*
* @param sessionId32 unique 32-byte session ID.
* @param privkey (optional) signer's private key.
* @param aggpubkey aggregated public key of all participants in the signing session.
* @param msg32 (optional) 32-byte message that will be signed, if already known.
* @param keyaggCache (optional) key aggregation cache data from the signing session.
* @param extraInput32 (optional) additional 32-byte random data.
* @return serialized version of the secret nonce and the corresponding public nonce.
*/
public fun musigNonceGen(sessionId32: ByteArray, privkey: ByteArray?, aggpubkey: ByteArray, msg32: ByteArray?, keyaggCache: ByteArray?, extraInput32: ByteArray?): ByteArray
/**
* Aggregate public nonces from all participants of a signing session.
*
* @param pubnonces public nonces (one per participant).
* @return 66-byte aggregate public nonce (two public keys) or throws an exception is a nonce is invalid.
*/
public fun musigNonceAgg(pubnonces: Array<ByteArray>): ByteArray
/**
* Aggregate public keys from all participants of a signing session.
*
* @param pubkeys public keys of all participants in the signing session.
* @param keyaggCache (optional) key aggregation cache data from the signing session. If an empty byte array is
* provided, it will be filled with key aggregation data that can be used for the next steps of the signing process.
* @return 32-byte x-only public key.
*/
public fun musigPubkeyAgg(pubkeys: Array<ByteArray>, keyaggCache: ByteArray?): ByteArray
/**
* Tweak the aggregated public key of a signing session.
*
* @param keyaggCache key aggregation cache filled by [musigPubkeyAgg].
* @param tweak32 private key tweak to apply.
* @return P + tweak32 * G (where P is the aggregated public key from [keyaggCache]). The key aggregation cache will
* be updated with the tweaked public key.
*/
public fun musigPubkeyTweakAdd(keyaggCache: ByteArray, tweak32: ByteArray): ByteArray
/**
* Tweak the aggregated public key of a signing session, treating it as an x-only public key (e.g. when using taproot).
*
* @param keyaggCache key aggregation cache filled by [musigPubkeyAgg].
* @param tweak32 private key tweak to apply.
* @return with_even_y(P) + tweak32 * G (where P is the aggregated public key from [keyaggCache]). The key aggregation
* cache will be updated with the tweaked public key.
*/
public fun musigPubkeyXonlyTweakAdd(keyaggCache: ByteArray, tweak32: ByteArray): ByteArray
/**
* Create a signing session context based on the public information from all participants.
*
* @param aggnonce aggregated public nonce (see [musigNonceAgg]).
* @param msg32 32-byte message that will be signed.
* @param keyaggCache aggregated public key cache filled by calling [musigPubkeyAgg] with the public keys of all participants.
* @return signing session context that can be used to create partial signatures and aggregate them.
*/
public fun musigNonceProcess(aggnonce: ByteArray, msg32: ByteArray, keyaggCache: ByteArray): ByteArray
/**
* 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.
*/
@ -317,27 +161,6 @@ 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
}
}

View File

@ -1,19 +1,17 @@
package = secp256k1
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
headers = secp256k1.h secp256k1_ecdh.h secp256k1_recovery.h secp256k1_extrakeys.h secp256k1_schnorrsig.h
headerFilter = secp256k1/** secp256k1_ecdh.h secp256k1_recovery.h secp256k1_extrakeys.h secp256k1_schnorrsig.h secp256k1.h
staticLibraries.linux = libsecp256k1.a
libraryPaths.linux = c/secp256k1/build/linux/ native/build/linux/ native/build/darwin/
linkerOpts.linux = -L/usr/lib64 -L/usr/lib/x86_64-linux-gnu -L/usr/local/lib
staticLibraries.macos_x64 = libsecp256k1.a
libraryPaths.macos_x64 = c/secp256k1/build/darwin/ native/build/darwin/
linkerOpts.macos_x64 = -framework Security -framework Foundation
staticLibraries.mingw = libsecp256k1.a
libraryPaths.mingw = c/secp256k1/build/mingw/ native/build/mingw/
staticLibraries.macos_arm64 = libsecp256k1.a
libraryPaths.macos_arm64 = c/secp256k1/build/darwin/ native/build/darwin/
linkerOpts.macos_arm64 = -framework Security -framework Foundation
staticLibraries.macos = libsecp256k1.a
libraryPaths.macos = c/secp256k1/build/macos/ native/build/macos/
staticLibraries.ios = libsecp256k1.a
libraryPaths.ios_x64 = c/secp256k1/build/ios/ /usr/local/lib native/build/ios/

View File

@ -1,9 +1,6 @@
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.*
@ -43,27 +40,6 @@ 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>()
@ -72,25 +48,7 @@ public object Secp256k1Native : Secp256k1 {
return serialized.readBytes(outputLen.value.convert())
}
private fun MemScope.serializeXonlyPubkey(pubkey: secp256k1_xonly_pubkey): ByteArray {
val serialized = allocArray<UByteVar>(32)
secp256k1_xonly_pubkey_serialize(ctx, serialized, pubkey.ptr).requireSuccess("secp256k1_xonly_pubkey_serialize() failed")
return serialized.readBytes(32)
}
private fun MemScope.serializePubnonce(pubnonce: secp256k1_musig_pubnonce): ByteArray {
val serialized = allocArray<UByteVar>(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
secp256k1_musig_pubnonce_serialize(ctx, serialized, pubnonce.ptr).requireSuccess("secp256k1_musig_pubnonce_serialize() failed")
return serialized.readBytes(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
}
private fun MemScope.serializeAggnonce(aggnonce: secp256k1_musig_aggnonce): ByteArray {
val serialized = allocArray<UByteVar>(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
secp256k1_musig_aggnonce_serialize(ctx, serialized, aggnonce.ptr).requireSuccess("secp256k1_musig_aggnonce_serialize() failed")
return serialized.readBytes(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
}
private fun DeferScope.toNat(bytes: ByteArray): CPointer<UByteVar> {
private fun DeferScope.toNat(bytes: ByteArray): CPointer<UByteVar> {
val ubytes = bytes.asUByteArray()
val pinned = ubytes.pin()
this.defer { pinned.unpin() }
@ -121,7 +79,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)
@ -299,699 +257,12 @@ public object Secp256k1Native : Secp256k1 {
return nSig.readBytes(64)
}
}
override fun musigNonceGen(sessionId32: ByteArray, privkey: ByteArray?, aggpubkey: ByteArray, msg32: ByteArray?, keyaggCache: ByteArray?, extraInput32: ByteArray?): ByteArray {
require(sessionId32.size == 32)
privkey?.let { require(it.size == 32) }
msg32?.let { require(it.size == 32) }
keyaggCache?.let { require(it.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE) }
extraInput32?.let { require(it.size == 32) }
val nonce = memScoped {
val secnonce = alloc<secp256k1_musig_secnonce>()
val pubnonce = alloc<secp256k1_musig_pubnonce>()
val nPubkey = allocPublicKey(aggpubkey)
val nKeyAggCache = keyaggCache?.let {
val n = alloc<secp256k1_musig_keyagg_cache>()
memcpy(n.ptr, toNat(it), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
n
}
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

View File

@ -1,7 +1,3 @@
import org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmTest
import org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeHostTest
import org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeSimulatorTest
plugins {
kotlin("multiplatform")
if (System.getProperty("includeAndroid")?.toBoolean() == true) {
@ -23,8 +19,6 @@ kotlin {
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
implementation("org.kodein.memory:klio-files:0.12.0")
api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
}
}
@ -57,8 +51,6 @@ kotlin {
}
linuxX64()
macosX64()
macosArm64()
iosX64()
iosArm64()
iosSimulatorArm64()
@ -86,26 +78,4 @@ if (includeAndroid) {
}
}
}
}
afterEvaluate {
tasks.withType<AbstractTestTask> {
testLogging {
events("passed", "skipped", "failed", "standard_out", "standard_error")
showExceptions = true
showStackTraces = true
}
}
tasks.withType<KotlinJvmTest> {
environment("TEST_RESOURCES_PATH", projectDir.resolve("src/commonTest/resources"))
}
tasks.withType<KotlinNativeHostTest> {
environment("TEST_RESOURCES_PATH", projectDir.resolve("src/commonTest/resources"))
}
tasks.withType<KotlinNativeSimulatorTest> {
environment("SIMCTL_CHILD_TEST_RESOURCES_PATH", projectDir.resolve("src/commonTest/resources"))
}
}
}

View File

@ -1,24 +0,0 @@
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)
}
}

View File

@ -1,510 +0,0 @@
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"
)
}
}
}

View File

@ -1,288 +0,0 @@
package fr.acinq.secp256k1
import kotlinx.serialization.json.*
import org.kodein.memory.file.FileSystem
import org.kodein.memory.file.Path
import org.kodein.memory.file.openReadableFile
import org.kodein.memory.file.resolve
import org.kodein.memory.system.Environment
import org.kodein.memory.text.readString
import org.kodein.memory.use
import kotlin.test.*
class Musig2Test: 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)
}
}
}
}

View File

@ -5,14 +5,6 @@ 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())
@ -360,159 +352,6 @@ class Secp256k1Test {
}
}
@Test
fun testMusig2GenerateNonce() {
val privkey = Hex.decode("0000000000000000000000000000000000000000000000000000000000000003")
val pubkey = Hex.decode("02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9")
val sessionId = Hex.decode("0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F")
val nonce = Secp256k1.musigNonceGen(sessionId, null, pubkey, null, null, null)
val pubnonce = Hex.encode(nonce.copyOfRange(132, 132 + 66)).uppercase()
assertEquals("02C96E7CB1E8AA5DAC64D872947914198F607D90ECDE5200DE52978AD5DED63C000299EC5117C2D29EDEE8A2092587C3909BE694D5CFF0667D6C02EA4059F7CD9786", pubnonce)
assertNotEquals(nonce, Secp256k1.musigNonceGen(sessionId, privkey, pubkey, null, null, null))
assertNotEquals(nonce, Secp256k1.musigNonceGen(sessionId, null, pubkey, sessionId, null, null))
}
@Test
fun testMusig2AggregateNonce() {
val nonces = listOf(
"020151C80F435648DF67A22B749CD798CE54E0321D034B92B709B567D60A42E66603BA47FBC1834437B3212E89A84D8425E7BF12E0245D98262268EBDCB385D50641",
"03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B833",
"020151C80F435648DF67A22B749CD798CE54E0321D034B92B709B567D60A42E6660279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
"03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60379BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
// The following nonces are invalid.
"04FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B833",
"03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B831",
"03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A602FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30"
).map { Hex.decode(it) }
val agg1 = Secp256k1.musigNonceAgg(arrayOf(nonces[0], nonces[1]))
assertEquals("035FE1873B4F2967F52FEA4A06AD5A8ECCBE9D0FD73068012C894E2E87CCB5804B024725377345BDE0E9C33AF3C43C0A29A9249F2F2956FA8CFEB55C8573D0262DC8", Hex.encode(agg1).uppercase())
val agg2 = Secp256k1.musigNonceAgg(arrayOf(nonces[2], nonces[3]))
assertEquals("035FE1873B4F2967F52FEA4A06AD5A8ECCBE9D0FD73068012C894E2E87CCB5804B000000000000000000000000000000000000000000000000000000000000000000", Hex.encode(agg2).uppercase())
assertFails {
Secp256k1.musigNonceAgg(arrayOf(nonces[0], nonces[4]))
}
assertFails {
Secp256k1.musigNonceAgg(arrayOf(nonces[5], nonces[1]))
}
assertFails {
Secp256k1.musigNonceAgg(arrayOf(nonces[6], nonces[1]))
}
}
@Test
fun testMusig2AggregatePubkey() {
val pubkeys = listOf(
"02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
"03DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659",
"023590A94E768F8E1815C2F24B4D80A8E3149316C3518CE7B7AD338368D038CA66",
"020000000000000000000000000000000000000000000000000000000000000005",
"02FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30",
"04F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
"03935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9"
).map { Hex.decode(it) }
val agg1 = Secp256k1.musigPubkeyAgg(arrayOf(pubkeys[0], pubkeys[1], pubkeys[2]), null)
assertEquals("90539EEDE565F5D054F32CC0C220126889ED1E5D193BAF15AEF344FE59D4610C", Hex.encode(agg1).uppercase())
// We provide an empty cache, which will be filled when aggregating public keys.
val keyaggCache = ByteArray(Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
val agg2 = Secp256k1.musigPubkeyAgg(arrayOf(pubkeys[0], pubkeys[1], pubkeys[2]), keyaggCache)
assertEquals("90539EEDE565F5D054F32CC0C220126889ED1E5D193BAF15AEF344FE59D4610C", Hex.encode(agg2).uppercase())
assertTrue(keyaggCache.count { it.toInt() != 0 } > 100) // the cache has been filled with key aggregation data
// We can reuse the key aggregation cache to speed up computation.
val agg3 = Secp256k1.musigPubkeyAgg(arrayOf(pubkeys[0], pubkeys[1], pubkeys[2]), keyaggCache)
assertEquals("90539EEDE565F5D054F32CC0C220126889ED1E5D193BAF15AEF344FE59D4610C", Hex.encode(agg3).uppercase())
val agg4 = Secp256k1.musigPubkeyAgg(arrayOf(pubkeys[2], pubkeys[1], pubkeys[0]), null)
assertEquals("6204DE8B083426DC6EAF9502D27024D53FC826BF7D2012148A0575435DF54B2B", Hex.encode(agg4).uppercase())
val agg5 = Secp256k1.musigPubkeyAgg(arrayOf(pubkeys[0], pubkeys[0], pubkeys[0]), null)
assertEquals("B436E3BAD62B8CD409969A224731C193D051162D8C5AE8B109306127DA3AA935", Hex.encode(agg5).uppercase())
val agg6 = Secp256k1.musigPubkeyAgg(arrayOf(pubkeys[0], pubkeys[0], pubkeys[1], pubkeys[1]), null)
assertEquals("69BC22BFA5D106306E48A20679DE1D7389386124D07571D0D872686028C26A3E", Hex.encode(agg6).uppercase())
// If we provide the key aggregation cache for a different session, it is ignored and overwritten.
val agg7 = Secp256k1.musigPubkeyAgg(arrayOf(pubkeys[0], pubkeys[0], pubkeys[1], pubkeys[1]), keyaggCache)
assertEquals("69BC22BFA5D106306E48A20679DE1D7389386124D07571D0D872686028C26A3E", Hex.encode(agg7).uppercase())
// If we provide random data in the key aggregation cache, it is ignored and overwritten.
val agg8 = Secp256k1.musigPubkeyAgg(arrayOf(pubkeys[0], pubkeys[0], pubkeys[1], pubkeys[1]), Random.nextBytes(Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE))
assertEquals("69BC22BFA5D106306E48A20679DE1D7389386124D07571D0D872686028C26A3E", Hex.encode(agg8).uppercase())
}
@Test
fun testMusig2TweakPubkeys() {
val pubkeys = listOf(
"031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f",
"024d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766",
"02531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe337"
).map { Hex.decode(it) }.toTypedArray()
val cache = ByteArray(Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
val agg1 = Secp256k1.musigPubkeyAgg(pubkeys, cache)
assertEquals("b6d830642403fc82511aca5ff98a5e76fcef0f89bffc1aadbe78ee74cd5a5716", Hex.encode(agg1))
val agg2 = Secp256k1.musigPubkeyTweakAdd(cache, Hex.decode("7468697320636f756c64206265206120424950333220747765616b2e2e2e2e00"))
assertEquals("04791e4f22a21f19bd9798eceab92ad2ccc18f2d6660e91ae4c0709aaebf1aa9023701f468b0eddf8973495a5327f2169d9c6a50eb6a0f87c0fbee90a4067eb230", Hex.encode(agg2))
val agg3 = Secp256k1.musigPubkeyXonlyTweakAdd(cache, Hex.decode("7468697320636f756c64206265206120746170726f6f7420747765616b2e2e00"))
assertEquals("04537a081a8d32ff700ca86aaa77a423e9b8d1480938076b645c68ee39d263c93948026928799b2d942cb5851db397015b26b1759de1b9ab2c691ced64a2eef836", Hex.encode(agg3))
}
@Test
fun testMusig2SigningSession() {
val privkeys = listOf(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 {
@ -545,15 +384,16 @@ class Secp256k1Test {
}
}
@Test
fun fuzzMusig2SigningSession() {
repeat(1000) {
testMusig2SigningSession()
}
}
@Test
fun fuzzEcdsaSignVerify() {
val random = Random.Default
fun randomBytes(length: Int): ByteArray {
val buffer = ByteArray(length)
random.nextBytes(buffer)
return buffer
}
repeat(200) {
val priv = randomBytes(32)
assertTrue(Secp256k1.secKeyVerify(priv))

View File

@ -1,52 +0,0 @@
{
"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"
}
]
}

View File

@ -1,5 +0,0 @@
{
"aggregate_public_key": "bd5561ef6dbff52d3f73b8cb0c065328988b71d3386d23890744a0dd6ad27c15",
"tweak_cache": "40252e41157cd26adda0440789236d38d3718b982853060ccbb8733f2df5bf6def6155bdeefcc6b83fe690d621b793b08f5b1562a7bef628c4db33f2d0b3f3bf2f541aaa000000000000000000000000000000000000000000000000000000000000000000",
"ec_tweak_add": "0444536a3cc348adb38660f06de15a6168c06d11a0ad00ca6ea34ff58a8300e2cc086d50550ee52f1d0334e31dfc67fa55201541078d89dcf79bf0c46a53e3af9b"
}

View File

@ -1,26 +0,0 @@
{
"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"
}
]
}

View File

@ -1,105 +0,0 @@
{
"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"
}
}
]
}
}

View File

@ -1,182 +0,0 @@
{
"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"
}
}
]
}
}

View File

@ -1,144 +0,0 @@
{
"sk": "7FB9E0E687ADA1EEBF7ECFE2F21E73EBDB51A7D450948DFE8D76D7F2D1007671",
"pubkeys": [
"03935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9",
"02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
"02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659",
"020000000000000000000000000000000000000000000000000000000000000007"
],
"msgs": [
"F95466D086770E689964664219266FE5ED215C92AE20BAB5C9D79ADDDDF3C0CF",
"2626262626262626262626262626262626262626262626262626262626262626262626262626"
],
"valid_test_cases": [
{
"rand": "0000000000000000000000000000000000000000000000000000000000000000",
"aggothernonce": "0337C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480",
"key_indices": [0, 1, 2],
"tweaks": [],
"is_xonly": [],
"msg_index": 0,
"signer_index": 0,
"expected": [
"03D96275257C2FCCBB6EEB77BDDF51D3C88C26EE1626C6CDA8999B9D34F4BA13A60309BE2BF883C6ABE907FA822D9CA166D51A3DCC28910C57528F6983FC378B7843",
"41EA65093F71D084785B20DC26A887CD941C9597860A21660CBDB9CC2113CAD3"
]
},
{
"rand": null,
"aggothernonce": "0337C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480",
"key_indices": [1, 0, 2],
"tweaks": [],
"is_xonly": [],
"msg_index": 0,
"signer_index": 1,
"expected": [
"028FBCCF5BB73A7B61B270BAD15C0F9475D577DD85C2157C9D38BEF1EC922B48770253BE3638C87369BC287E446B7F2C8CA5BEB9FFBD1EA082C62913982A65FC214D",
"AEAA31262637BFA88D5606679018A0FEEEC341F3107D1199857F6C81DE61B8DD"
]
},
{
"rand": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
"aggothernonce": "0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
"key_indices": [1, 2, 0],
"tweaks": [],
"is_xonly": [],
"msg_index": 1,
"signer_index": 2,
"expected": [
"024FA8D774F0C8743FAA77AFB4D08EE5A013C2E8EEAD8A6F08A77DDD2D28266DB803050905E8C994477F3F2981861A2E3791EF558626E645FBF5AA131C5D6447C2C2",
"FEE28A56B8556B7632E42A84122C51A4861B1F2DEC7E81B632195E56A52E3E13"
],
"comment": "Message longer than 32 bytes"
},
{
"rand": "0000000000000000000000000000000000000000000000000000000000000000",
"aggothernonce": "032DE2662628C90B03F5E720284EB52FF7D71F4284F627B68A853D78C78E1FFE9303E4C5524E83FFE1493B9077CF1CA6BEB2090C93D930321071AD40B2F44E599046",
"key_indices": [0, 1, 2],
"tweaks": ["E8F791FF9225A2AF0102AFFF4A9A723D9612A682A25EBE79802B263CDFCD83BB"],
"is_xonly": [true],
"msg_index": 0,
"signer_index": 0,
"expected": [
"031E07C0D11A0134E55DB1FC16095ADCBD564236194374AA882BFB3C78273BF673039D0336E8CA6288C00BFC1F8B594563529C98661172B9BC1BE85C23A4CE1F616B",
"7B1246C5889E59CB0375FA395CC86AC42D5D7D59FD8EAB4FDF1DCAB2B2F006EA"
],
"comment": "Tweaked public key"
}
],
"error_test_cases": [
{
"rand": "0000000000000000000000000000000000000000000000000000000000000000",
"aggothernonce": "0337C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480",
"key_indices": [1, 0, 3],
"tweaks": [],
"is_xonly": [],
"msg_index": 0,
"signer_index": 1,
"error": {
"type": "invalid_contribution",
"signer": 2,
"contrib": "pubkey"
},
"comment": "Signer 2 provided an invalid public key"
},
{
"rand": "0000000000000000000000000000000000000000000000000000000000000000",
"aggothernonce": "0337C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480",
"key_indices": [1, 2],
"tweaks": [],
"is_xonly": [],
"msg_index": 0,
"signer_index": 1,
"error": {
"type": "value",
"message": "The signer's pubkey must be included in the list of pubkeys."
},
"comment": "The signers pubkey is not in the list of pubkeys"
},
{
"rand": "0000000000000000000000000000000000000000000000000000000000000000",
"aggothernonce": "0437C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480",
"key_indices": [1, 2, 0],
"tweaks": [],
"is_xonly": [],
"msg_index": 0,
"signer_index": 2,
"error": {
"type": "invalid_contribution",
"signer": null,
"contrib": "aggothernonce"
},
"comment": "aggothernonce is invalid due wrong tag, 0x04, in the first half"
},
{
"rand": "0000000000000000000000000000000000000000000000000000000000000000",
"aggothernonce": "0000000000000000000000000000000000000000000000000000000000000000000287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480",
"key_indices": [1, 2, 0],
"tweaks": [],
"is_xonly": [],
"msg_index": 0,
"signer_index": 2,
"error": {
"type": "invalid_contribution",
"signer": null,
"contrib": "aggothernonce"
},
"comment": "aggothernonce is invalid because first half corresponds to point at infinity"
},
{
"rand": "0000000000000000000000000000000000000000000000000000000000000000",
"aggothernonce": "0337C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480",
"key_indices": [1, 2, 0],
"tweaks": ["FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"],
"is_xonly": [false],
"msg_index": 0,
"signer_index": 2,
"error": {
"type": "value",
"message": "The tweak must be less than n."
},
"comment": "Tweak is invalid because it exceeds group size"
}
]
}

View File

@ -1,88 +0,0 @@
{
"pubkeys": [
"02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
"03DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659",
"023590A94E768F8E1815C2F24B4D80A8E3149316C3518CE7B7AD338368D038CA66",
"020000000000000000000000000000000000000000000000000000000000000005",
"02FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30",
"04F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
"03935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9"
],
"tweaks": [
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",
"252E4BD67410A76CDF933D30EAA1608214037F1B105A013ECCD3C5C184A6110B"
],
"valid_test_cases": [
{
"key_indices": [0, 1, 2],
"expected": "90539EEDE565F5D054F32CC0C220126889ED1E5D193BAF15AEF344FE59D4610C"
},
{
"key_indices": [2, 1, 0],
"expected": "6204DE8B083426DC6EAF9502D27024D53FC826BF7D2012148A0575435DF54B2B"
},
{
"key_indices": [0, 0, 0],
"expected": "B436E3BAD62B8CD409969A224731C193D051162D8C5AE8B109306127DA3AA935"
},
{
"key_indices": [0, 0, 1, 1],
"expected": "69BC22BFA5D106306E48A20679DE1D7389386124D07571D0D872686028C26A3E"
}
],
"error_test_cases": [
{
"key_indices": [0, 3],
"tweak_indices": [],
"is_xonly": [],
"error": {
"type": "invalid_contribution",
"signer": 1,
"contrib": "pubkey"
},
"comment": "Invalid public key"
},
{
"key_indices": [0, 4],
"tweak_indices": [],
"is_xonly": [],
"error": {
"type": "invalid_contribution",
"signer": 1,
"contrib": "pubkey"
},
"comment": "Public key exceeds field size"
},
{
"key_indices": [5, 0],
"tweak_indices": [],
"is_xonly": [],
"error": {
"type": "invalid_contribution",
"signer": 0,
"contrib": "pubkey"
},
"comment": "First byte of public key is not 2 or 3"
},
{
"key_indices": [0, 1],
"tweak_indices": [0],
"is_xonly": [true],
"error": {
"type": "value",
"message": "The tweak must be less than n."
},
"comment": "Tweak is out of range"
},
{
"key_indices": [6],
"tweak_indices": [1],
"is_xonly": [false],
"error": {
"type": "value",
"message": "The result of tweaking cannot be infinity."
},
"comment": "Intermediate tweaking result is point at infinity"
}
]
}

View File

@ -1,18 +0,0 @@
{
"pubkeys": [
"02DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8",
"02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
"03DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659",
"023590A94E768F8E1815C2F24B4D80A8E3149316C3518CE7B7AD338368D038CA66",
"02DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EFF",
"02DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8"
],
"sorted_pubkeys": [
"023590A94E768F8E1815C2F24B4D80A8E3149316C3518CE7B7AD338368D038CA66",
"02DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8",
"02DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8",
"02DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EFF",
"02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
"03DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"
]
}

View File

@ -1,51 +0,0 @@
{
"pnonces": [
"020151C80F435648DF67A22B749CD798CE54E0321D034B92B709B567D60A42E66603BA47FBC1834437B3212E89A84D8425E7BF12E0245D98262268EBDCB385D50641",
"03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B833",
"020151C80F435648DF67A22B749CD798CE54E0321D034B92B709B567D60A42E6660279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
"03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60379BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
"04FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B833",
"03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B831",
"03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A602FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30"
],
"valid_test_cases": [
{
"pnonce_indices": [0, 1],
"expected": "035FE1873B4F2967F52FEA4A06AD5A8ECCBE9D0FD73068012C894E2E87CCB5804B024725377345BDE0E9C33AF3C43C0A29A9249F2F2956FA8CFEB55C8573D0262DC8"
},
{
"pnonce_indices": [2, 3],
"expected": "035FE1873B4F2967F52FEA4A06AD5A8ECCBE9D0FD73068012C894E2E87CCB5804B000000000000000000000000000000000000000000000000000000000000000000",
"comment": "Sum of second points encoded in the nonces is point at infinity which is serialized as 33 zero bytes"
}
],
"error_test_cases": [
{
"pnonce_indices": [0, 4],
"error": {
"type": "invalid_contribution",
"signer": 1,
"contrib": "pubnonce"
},
"comment": "Public nonce from signer 1 is invalid due wrong tag, 0x04, in the first half"
},
{
"pnonce_indices": [5, 1],
"error": {
"type": "invalid_contribution",
"signer": 0,
"contrib": "pubnonce"
},
"comment": "Public nonce from signer 0 is invalid because the second half does not correspond to an X coordinate"
},
{
"pnonce_indices": [6, 1],
"error": {
"type": "invalid_contribution",
"signer": 0,
"contrib": "pubnonce"
},
"comment": "Public nonce from signer 0 is invalid because second half exceeds field size"
}
]
}

View File

@ -1,44 +0,0 @@
{
"test_cases": [
{
"rand_": "0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F",
"sk": "0202020202020202020202020202020202020202020202020202020202020202",
"pk": "024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766",
"aggpk": "0707070707070707070707070707070707070707070707070707070707070707",
"msg": "0101010101010101010101010101010101010101010101010101010101010101",
"extra_in": "0808080808080808080808080808080808080808080808080808080808080808",
"expected_secnonce": "B114E502BEAA4E301DD08A50264172C84E41650E6CB726B410C0694D59EFFB6495B5CAF28D045B973D63E3C99A44B807BDE375FD6CB39E46DC4A511708D0E9D2024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766",
"expected_pubnonce": "02F7BE7089E8376EB355272368766B17E88E7DB72047D05E56AA881EA52B3B35DF02C29C8046FDD0DED4C7E55869137200FBDBFE2EB654267B6D7013602CAED3115A"
},
{
"rand_": "0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F",
"sk": "0202020202020202020202020202020202020202020202020202020202020202",
"pk": "024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766",
"aggpk": "0707070707070707070707070707070707070707070707070707070707070707",
"msg": "",
"extra_in": "0808080808080808080808080808080808080808080808080808080808080808",
"expected_secnonce": "E862B068500320088138468D47E0E6F147E01B6024244AE45EAC40ACE5929B9F0789E051170B9E705D0B9EB49049A323BBBBB206D8E05C19F46C6228742AA7A9024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766",
"expected_pubnonce": "023034FA5E2679F01EE66E12225882A7A48CC66719B1B9D3B6C4DBD743EFEDA2C503F3FD6F01EB3A8E9CB315D73F1F3D287CAFBB44AB321153C6287F407600205109"
},
{
"rand_": "0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F",
"sk": "0202020202020202020202020202020202020202020202020202020202020202",
"pk": "024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766",
"aggpk": "0707070707070707070707070707070707070707070707070707070707070707",
"msg": "2626262626262626262626262626262626262626262626262626262626262626262626262626",
"extra_in": "0808080808080808080808080808080808080808080808080808080808080808",
"expected_secnonce": "3221975ACBDEA6820EABF02A02B7F27D3A8EF68EE42787B88CBEFD9AA06AF3632EE85B1A61D8EF31126D4663A00DD96E9D1D4959E72D70FE5EBB6E7696EBA66F024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766",
"expected_pubnonce": "02E5BBC21C69270F59BD634FCBFA281BE9D76601295345112C58954625BF23793A021307511C79F95D38ACACFF1B4DA98228B77E65AA216AD075E9673286EFB4EAF3"
},
{
"rand_": "0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F",
"sk": null,
"pk": "02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
"aggpk": null,
"msg": null,
"extra_in": null,
"expected_secnonce": "89BDD787D0284E5E4D5FC572E49E316BAB7E21E3B1830DE37DFE80156FA41A6D0B17AE8D024C53679699A6FD7944D9C4A366B514BAF43088E0708B1023DD289702F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
"expected_pubnonce": "02C96E7CB1E8AA5DAC64D872947914198F607D90ECDE5200DE52978AD5DED63C000299EC5117C2D29EDEE8A2092587C3909BE694D5CFF0667D6C02EA4059F7CD9786"
}
]
}

View File

@ -1,151 +0,0 @@
{
"pubkeys": [
"03935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9",
"02D2DC6F5DF7C56ACF38C7FA0AE7A759AE30E19B37359DFDE015872324C7EF6E05",
"03C7FB101D97FF930ACD0C6760852EF64E69083DE0B06AC6335724754BB4B0522C",
"02352433B21E7E05D3B452B81CAE566E06D2E003ECE16D1074AABA4289E0E3D581"
],
"pnonces": [
"036E5EE6E28824029FEA3E8A9DDD2C8483F5AF98F7177C3AF3CB6F47CAF8D94AE902DBA67E4A1F3680826172DA15AFB1A8CA85C7C5CC88900905C8DC8C328511B53E",
"03E4F798DA48A76EEC1C9CC5AB7A880FFBA201A5F064E627EC9CB0031D1D58FC5103E06180315C5A522B7EC7C08B69DCD721C313C940819296D0A7AB8E8795AC1F00",
"02C0068FD25523A31578B8077F24F78F5BD5F2422AFF47C1FADA0F36B3CEB6C7D202098A55D1736AA5FCC21CF0729CCE852575C06C081125144763C2C4C4A05C09B6",
"031F5C87DCFBFCF330DEE4311D85E8F1DEA01D87A6F1C14CDFC7E4F1D8C441CFA40277BF176E9F747C34F81B0D9F072B1B404A86F402C2D86CF9EA9E9C69876EA3B9",
"023F7042046E0397822C4144A17F8B63D78748696A46C3B9F0A901D296EC3406C302022B0B464292CF9751D699F10980AC764E6F671EFCA15069BBE62B0D1C62522A",
"02D97DDA5988461DF58C5897444F116A7C74E5711BF77A9446E27806563F3B6C47020CBAD9C363A7737F99FA06B6BE093CEAFF5397316C5AC46915C43767AE867C00"
],
"tweaks": [
"B511DA492182A91B0FFB9A98020D55F260AE86D7ECBD0399C7383D59A5F2AF7C",
"A815FE049EE3C5AAB66310477FBC8BCCCAC2F3395F59F921C364ACD78A2F48DC",
"75448A87274B056468B977BE06EB1E9F657577B7320B0A3376EA51FD420D18A8"
],
"psigs": [
"B15D2CD3C3D22B04DAE438CE653F6B4ECF042F42CFDED7C41B64AAF9B4AF53FB",
"6193D6AC61B354E9105BBDC8937A3454A6D705B6D57322A5A472A02CE99FCB64",
"9A87D3B79EC67228CB97878B76049B15DBD05B8158D17B5B9114D3C226887505",
"66F82EA90923689B855D36C6B7E032FB9970301481B99E01CDB4D6AC7C347A15",
"4F5AEE41510848A6447DCD1BBC78457EF69024944C87F40250D3EF2C25D33EFE",
"DDEF427BBB847CC027BEFF4EDB01038148917832253EBC355FC33F4A8E2FCCE4",
"97B890A26C981DA8102D3BC294159D171D72810FDF7C6A691DEF02F0F7AF3FDC",
"53FA9E08BA5243CBCB0D797C5EE83BC6728E539EB76C2D0BF0F971EE4E909971",
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"
],
"msg": "599C67EA410D005B9DA90817CF03ED3B1C868E4DA4EDF00A5880B0082C237869",
"valid_test_cases": [
{
"aggnonce": "0341432722C5CD0268D829C702CF0D1CBCE57033EED201FD335191385227C3210C03D377F2D258B64AADC0E16F26462323D701D286046A2EA93365656AFD9875982B",
"nonce_indices": [
0,
1
],
"key_indices": [
0,
1
],
"tweak_indices": [],
"is_xonly": [],
"psig_indices": [
0,
1
],
"expected": "041DA22223CE65C92C9A0D6C2CAC828AAF1EEE56304FEC371DDF91EBB2B9EF0912F1038025857FEDEB3FF696F8B99FA4BB2C5812F6095A2E0004EC99CE18DE1E"
},
{
"aggnonce": "0224AFD36C902084058B51B5D36676BBA4DC97C775873768E58822F87FE437D792028CB15929099EEE2F5DAE404CD39357591BA32E9AF4E162B8D3E7CB5EFE31CB20",
"nonce_indices": [
0,
2
],
"key_indices": [
0,
2
],
"tweak_indices": [],
"is_xonly": [],
"psig_indices": [
2,
3
],
"expected": "1069B67EC3D2F3C7C08291ACCB17A9C9B8F2819A52EB5DF8726E17E7D6B52E9F01800260A7E9DAC450F4BE522DE4CE12BA91AEAF2B4279219EF74BE1D286ADD9"
},
{
"aggnonce": "0208C5C438C710F4F96A61E9FF3C37758814B8C3AE12BFEA0ED2C87FF6954FF186020B1816EA104B4FCA2D304D733E0E19CEAD51303FF6420BFD222335CAA402916D",
"nonce_indices": [
0,
3
],
"key_indices": [
0,
2
],
"tweak_indices": [
0
],
"is_xonly": [
false
],
"psig_indices": [
4,
5
],
"expected": "5C558E1DCADE86DA0B2F02626A512E30A22CF5255CAEA7EE32C38E9A71A0E9148BA6C0E6EC7683B64220F0298696F1B878CD47B107B81F7188812D593971E0CC"
},
{
"aggnonce": "02B5AD07AFCD99B6D92CB433FBD2A28FDEB98EAE2EB09B6014EF0F8197CD58403302E8616910F9293CF692C49F351DB86B25E352901F0E237BAFDA11F1C1CEF29FFD",
"nonce_indices": [
0,
4
],
"key_indices": [
0,
3
],
"tweak_indices": [
0,
1,
2
],
"is_xonly": [
true,
false,
true
],
"psig_indices": [
6,
7
],
"expected": "839B08820B681DBA8DAF4CC7B104E8F2638F9388F8D7A555DC17B6E6971D7426CE07BF6AB01F1DB50E4E33719295F4094572B79868E440FB3DEFD3FAC1DB589E"
}
],
"error_test_cases": [
{
"aggnonce": "02B5AD07AFCD99B6D92CB433FBD2A28FDEB98EAE2EB09B6014EF0F8197CD58403302E8616910F9293CF692C49F351DB86B25E352901F0E237BAFDA11F1C1CEF29FFD",
"nonce_indices": [
0,
4
],
"key_indices": [
0,
3
],
"tweak_indices": [
0,
1,
2
],
"is_xonly": [
true,
false,
true
],
"psig_indices": [
7,
8
],
"error": {
"type": "invalid_contribution",
"signer": 1
},
"comment": "Partial signature is invalid because it exceeds group size"
}
]
}

View File

@ -1,212 +0,0 @@
{
"sk": "7FB9E0E687ADA1EEBF7ECFE2F21E73EBDB51A7D450948DFE8D76D7F2D1007671",
"pubkeys": [
"03935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9",
"02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
"02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA661",
"020000000000000000000000000000000000000000000000000000000000000007"
],
"secnonces": [
"508B81A611F100A6B2B6B29656590898AF488BCF2E1F55CF22E5CFB84421FE61FA27FD49B1D50085B481285E1CA205D55C82CC1B31FF5CD54A489829355901F703935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9",
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9"
],
"pnonces": [
"0337C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480",
"0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
"032DE2662628C90B03F5E720284EB52FF7D71F4284F627B68A853D78C78E1FFE9303E4C5524E83FFE1493B9077CF1CA6BEB2090C93D930321071AD40B2F44E599046",
"0237C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0387BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480",
"0200000000000000000000000000000000000000000000000000000000000000090287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480"
],
"aggnonces": [
"028465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD61037496A3CC86926D452CAFCFD55D25972CA1675D549310DE296BFF42F72EEEA8C9",
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"048465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD61037496A3CC86926D452CAFCFD55D25972CA1675D549310DE296BFF42F72EEEA8C9",
"028465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD61020000000000000000000000000000000000000000000000000000000000000009",
"028465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD6102FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30"
],
"msgs": [
"F95466D086770E689964664219266FE5ED215C92AE20BAB5C9D79ADDDDF3C0CF",
"",
"2626262626262626262626262626262626262626262626262626262626262626262626262626"
],
"valid_test_cases": [
{
"key_indices": [0, 1, 2],
"nonce_indices": [0, 1, 2],
"aggnonce_index": 0,
"msg_index": 0,
"signer_index": 0,
"expected": "012ABBCB52B3016AC03AD82395A1A415C48B93DEF78718E62A7A90052FE224FB"
},
{
"key_indices": [1, 0, 2],
"nonce_indices": [1, 0, 2],
"aggnonce_index": 0,
"msg_index": 0,
"signer_index": 1,
"expected": "9FF2F7AAA856150CC8819254218D3ADEEB0535269051897724F9DB3789513A52"
},
{
"key_indices": [1, 2, 0],
"nonce_indices": [1, 2, 0],
"aggnonce_index": 0,
"msg_index": 0,
"signer_index": 2,
"expected": "FA23C359F6FAC4E7796BB93BC9F0532A95468C539BA20FF86D7C76ED92227900"
},
{
"key_indices": [0, 1],
"nonce_indices": [0, 3],
"aggnonce_index": 1,
"msg_index": 0,
"signer_index": 0,
"expected": "AE386064B26105404798F75DE2EB9AF5EDA5387B064B83D049CB7C5E08879531",
"comment": "Both halves of aggregate nonce correspond to point at infinity"
},
{
"key_indices": [0, 1, 2],
"nonce_indices": [0, 1, 2],
"aggnonce_index": 0,
"msg_index": 1,
"signer_index": 0,
"expected": "D7D63FFD644CCDA4E62BC2BC0B1D02DD32A1DC3030E155195810231D1037D82D",
"comment": "Empty message"
},
{
"key_indices": [0, 1, 2],
"nonce_indices": [0, 1, 2],
"aggnonce_index": 0,
"msg_index": 2,
"signer_index": 0,
"expected": "E184351828DA5094A97C79CABDAAA0BFB87608C32E8829A4DF5340A6F243B78C",
"comment": "38-byte message"
}
],
"sign_error_test_cases": [
{
"key_indices": [1, 2],
"aggnonce_index": 0,
"msg_index": 0,
"secnonce_index": 0,
"error": {
"type": "value",
"message": "The signer's pubkey must be included in the list of pubkeys."
},
"comment": "The signers pubkey is not in the list of pubkeys. This test case is optional: it can be skipped by implementations that do not check that the signer's pubkey is included in the list of pubkeys."
},
{
"key_indices": [1, 0, 3],
"aggnonce_index": 0,
"msg_index": 0,
"secnonce_index": 0,
"error": {
"type": "invalid_contribution",
"signer": 2,
"contrib": "pubkey"
},
"comment": "Signer 2 provided an invalid public key"
},
{
"key_indices": [1, 2, 0],
"aggnonce_index": 2,
"msg_index": 0,
"secnonce_index": 0,
"error": {
"type": "invalid_contribution",
"signer": null,
"contrib": "aggnonce"
},
"comment": "Aggregate nonce is invalid due wrong tag, 0x04, in the first half"
},
{
"key_indices": [1, 2, 0],
"aggnonce_index": 3,
"msg_index": 0,
"secnonce_index": 0,
"error": {
"type": "invalid_contribution",
"signer": null,
"contrib": "aggnonce"
},
"comment": "Aggregate nonce is invalid because the second half does not correspond to an X coordinate"
},
{
"key_indices": [1, 2, 0],
"aggnonce_index": 4,
"msg_index": 0,
"secnonce_index": 0,
"error": {
"type": "invalid_contribution",
"signer": null,
"contrib": "aggnonce"
},
"comment": "Aggregate nonce is invalid because second half exceeds field size"
},
{
"key_indices": [0, 1, 2],
"aggnonce_index": 0,
"msg_index": 0,
"signer_index": 0,
"secnonce_index": 1,
"error": {
"type": "value",
"message": "first secnonce value is out of range."
},
"comment": "Secnonce is invalid which may indicate nonce reuse"
}
],
"verify_fail_test_cases": [
{
"sig": "97AC833ADCB1AFA42EBF9E0725616F3C9A0D5B614F6FE283CEAAA37A8FFAF406",
"key_indices": [0, 1, 2],
"nonce_indices": [0, 1, 2],
"msg_index": 0,
"signer_index": 0,
"comment": "Wrong signature (which is equal to the negation of valid signature)"
},
{
"sig": "68537CC5234E505BD14061F8DA9E90C220A181855FD8BDB7F127BB12403B4D3B",
"key_indices": [0, 1, 2],
"nonce_indices": [0, 1, 2],
"msg_index": 0,
"signer_index": 1,
"comment": "Wrong signer"
},
{
"sig": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",
"key_indices": [0, 1, 2],
"nonce_indices": [0, 1, 2],
"msg_index": 0,
"signer_index": 0,
"comment": "Signature exceeds group size"
}
],
"verify_error_test_cases": [
{
"sig": "68537CC5234E505BD14061F8DA9E90C220A181855FD8BDB7F127BB12403B4D3B",
"key_indices": [0, 1, 2],
"nonce_indices": [4, 1, 2],
"msg_index": 0,
"signer_index": 0,
"error": {
"type": "invalid_contribution",
"signer": 0,
"contrib": "pubnonce"
},
"comment": "Invalid pubnonce"
},
{
"sig": "68537CC5234E505BD14061F8DA9E90C220A181855FD8BDB7F127BB12403B4D3B",
"key_indices": [3, 1, 2],
"nonce_indices": [0, 1, 2],
"msg_index": 0,
"signer_index": 0,
"error": {
"type": "invalid_contribution",
"signer": 0,
"contrib": "pubkey"
},
"comment": "Invalid pubkey"
}
]
}

View File

@ -1,84 +0,0 @@
{
"sk": "7FB9E0E687ADA1EEBF7ECFE2F21E73EBDB51A7D450948DFE8D76D7F2D1007671",
"pubkeys": [
"03935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9",
"02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
"02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"
],
"secnonce": "508B81A611F100A6B2B6B29656590898AF488BCF2E1F55CF22E5CFB84421FE61FA27FD49B1D50085B481285E1CA205D55C82CC1B31FF5CD54A489829355901F703935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9",
"pnonces": [
"0337C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480",
"0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
"032DE2662628C90B03F5E720284EB52FF7D71F4284F627B68A853D78C78E1FFE9303E4C5524E83FFE1493B9077CF1CA6BEB2090C93D930321071AD40B2F44E599046"
],
"aggnonce": "028465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD61037496A3CC86926D452CAFCFD55D25972CA1675D549310DE296BFF42F72EEEA8C9",
"tweaks": [
"E8F791FF9225A2AF0102AFFF4A9A723D9612A682A25EBE79802B263CDFCD83BB",
"AE2EA797CC0FE72AC5B97B97F3C6957D7E4199A167A58EB08BCAFFDA70AC0455",
"F52ECBC565B3D8BEA2DFD5B75A4F457E54369809322E4120831626F290FA87E0",
"1969AD73CC177FA0B4FCED6DF1F7BF9907E665FDE9BA196A74FED0A3CF5AEF9D",
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"
],
"msg": "F95466D086770E689964664219266FE5ED215C92AE20BAB5C9D79ADDDDF3C0CF",
"valid_test_cases": [
{
"key_indices": [1, 2, 0],
"nonce_indices": [1, 2, 0],
"tweak_indices": [0],
"is_xonly": [true],
"signer_index": 2,
"expected": "E28A5C66E61E178C2BA19DB77B6CF9F7E2F0F56C17918CD13135E60CC848FE91",
"comment": "A single x-only tweak"
},
{
"key_indices": [1, 2, 0],
"nonce_indices": [1, 2, 0],
"tweak_indices": [0],
"is_xonly": [false],
"signer_index": 2,
"expected": "38B0767798252F21BF5702C48028B095428320F73A4B14DB1E25DE58543D2D2D",
"comment": "A single plain tweak"
},
{
"key_indices": [1, 2, 0],
"nonce_indices": [1, 2, 0],
"tweak_indices": [0, 1],
"is_xonly": [false, true],
"signer_index": 2,
"expected": "408A0A21C4A0F5DACAF9646AD6EB6FECD7F7A11F03ED1F48DFFF2185BC2C2408",
"comment": "A plain tweak followed by an x-only tweak"
},
{
"key_indices": [1, 2, 0],
"nonce_indices": [1, 2, 0],
"tweak_indices": [0, 1, 2, 3],
"is_xonly": [false, false, true, true],
"signer_index": 2,
"expected": "45ABD206E61E3DF2EC9E264A6FEC8292141A633C28586388235541F9ADE75435",
"comment": "Four tweaks: plain, plain, x-only, x-only."
},
{
"key_indices": [1, 2, 0],
"nonce_indices": [1, 2, 0],
"tweak_indices": [0, 1, 2, 3],
"is_xonly": [true, false, true, false],
"signer_index": 2,
"expected": "B255FDCAC27B40C7CE7848E2D3B7BF5EA0ED756DA81565AC804CCCA3E1D5D239",
"comment": "Four tweaks: x-only, plain, x-only, plain. If an implementation prohibits applying plain tweaks after x-only tweaks, it can skip this test vector or return an error."
}
],
"error_test_cases": [
{
"key_indices": [1, 2, 0],
"nonce_indices": [1, 2, 0],
"tweak_indices": [4],
"is_xonly": [false],
"signer_index": 2,
"error": {
"type": "value",
"message": "The tweak must be less than n."
},
"comment": "Tweak is invalid because it exceeds group size"
}
]
}