5 Commits

Author SHA1 Message Date
Fabrice Drouin
567f411e12 Verify musig2 secret nonces (#108)
* Verify musig2 secret nonces

Trying to generate a musig2 partial signature with a secret nonce that was generated with a public key that does not match the signing key's public key will trigger secp256k1's illegal callback (which calls abort()) and crash the application.

=> Here we verify that the secret nonce matches the signing key before we call secp256k1_musig_partial_sign().
The verification method is a bit hackish (we extract the public key from the secret nonce blob) because secp256k1 does not export the methods we need to do this cleanly.
2024-04-18 09:54:51 +02:00
Fabrice Drouin
eb92fccbd6 Build a universal JNI binary for macos (#106)
Universal libraries embed both arm64 and x64 binaries.
2024-03-12 10:09:34 +01:00
Fabrice Drouin
d30e2a1b59 Set version to 0.15.0 (#105) 2024-03-04 19:29:52 +01:00
Fabrice Drouin
bae4744bcb Add macos build targets (#104)
* Add macos build targets

* Update snapshot publishing script
2024-03-04 17:58:20 +01:00
Fabrice Drouin
b2eb68bf83 Post release cleanup (#103)
* Set version to 0.15.0-SNAPSHOT

* Update publishing scripts
2024-02-14 18:28:19 +01:00
16 changed files with 181 additions and 79 deletions

View File

@@ -22,7 +22,7 @@ buildscript {
allprojects {
group = "fr.acinq.secp256k1"
version = "0.14.0"
version = "0.16.0-SNAPSHOT"
repositories {
google()
@@ -58,6 +58,14 @@ kotlin {
secp256k1CInterop("host")
}
macosX64 {
secp256k1CInterop("host")
}
macosArm64 {
secp256k1CInterop("host")
}
iosX64 {
secp256k1CInterop("ios")
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -117,6 +117,7 @@ public object NativeSecp256k1 : Secp256k1 {
}
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)
}

View File

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

View File

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

View File

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

View File

@@ -21,45 +21,52 @@ mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/conte
-Djavadoc=$ARTIFACT_ID_BASE-$VERSION-javadoc.jar
popd
pushd .
for i in iosarm64 iossimulatorarm64 iosx64 jni-android jni-common jni-jvm-darwin jni-jvm-extract jni-jvm-linux jni-jvm-mingw jni-jvm jvm linuxx64; do
for i in iosarm64 iossimulatorarm64 iosx64 macosarm64 macosx64 jni-android jni-common jni-jvm-darwin jni-jvm-extract jni-jvm-linux jni-jvm-mingw jni-jvm jvm linuxx64; do
cd fr/acinq/secp256k1/secp256k1-kmp-$i/$VERSION
if [ $i == iosarm64 ] || [ $i == iossimulatorarm64 ] || [ $i == iosx64 ]; then
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.klib \
-Dfiles=$ARTIFACT_ID_BASE-$i-$VERSION-metadata.jar,$ARTIFACT_ID_BASE-$i-$VERSION.module,$ARTIFACT_ID_BASE-$i-$VERSION-cinterop-libsecp256k1.klib \
-Dtypes=jar,module,klib \
-Dclassifiers=metadata,,cinterop-libsecp256k1 \
-Dsources=$ARTIFACT_ID_BASE-$i-$VERSION-sources.jar \
-Djavadoc=$ARTIFACT_ID_BASE-$i-$VERSION-javadoc.jar
elif [ $i == linuxx64 ]; then
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.klib \
-Dfiles=$ARTIFACT_ID_BASE-$i-$VERSION.module,$ARTIFACT_ID_BASE-$i-$VERSION-cinterop-libsecp256k1.klib \
-Dtypes=module,klib \
-Dclassifiers=,cinterop-libsecp256k1 \
-Dsources=$ARTIFACT_ID_BASE-$i-$VERSION-sources.jar \
-Djavadoc=$ARTIFACT_ID_BASE-$i-$VERSION-javadoc.jar
elif [ $i == jni-android ]; then
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.aar \
-Dfiles=$ARTIFACT_ID_BASE-$i-$VERSION.module \
-Dtypes=module \
-Dclassifiers= \
-Dsources=$ARTIFACT_ID_BASE-$i-$VERSION-sources.jar \
-Djavadoc=$ARTIFACT_ID_BASE-$i-$VERSION-javadoc.jar
else
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.jar \
-Dfiles=$ARTIFACT_ID_BASE-$i-$VERSION.module \
-Dtypes=module \
-Dclassifiers= \
-Dsources=$ARTIFACT_ID_BASE-$i-$VERSION-sources.jar \
-Djavadoc=$ARTIFACT_ID_BASE-$i-$VERSION-javadoc.jar
fi
case $i in
iosarm64 | iossimulatorarm64 | iosx64 | macosarm64 | macosx64)
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.klib \
-Dfiles=$ARTIFACT_ID_BASE-$i-$VERSION-metadata.jar,$ARTIFACT_ID_BASE-$i-$VERSION.module,$ARTIFACT_ID_BASE-$i-$VERSION-cinterop-libsecp256k1.klib \
-Dtypes=jar,module,klib \
-Dclassifiers=metadata,,cinterop-libsecp256k1 \
-Dsources=$ARTIFACT_ID_BASE-$i-$VERSION-sources.jar \
-Djavadoc=$ARTIFACT_ID_BASE-$i-$VERSION-javadoc.jar
;;
linuxx64)
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.klib \
-Dfiles=$ARTIFACT_ID_BASE-$i-$VERSION.module,$ARTIFACT_ID_BASE-$i-$VERSION-cinterop-libsecp256k1.klib \
-Dtypes=module,klib \
-Dclassifiers=,cinterop-libsecp256k1 \
-Dsources=$ARTIFACT_ID_BASE-$i-$VERSION-sources.jar \
-Djavadoc=$ARTIFACT_ID_BASE-$i-$VERSION-javadoc.jar
;;
jni-android)
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.aar \
-Dfiles=$ARTIFACT_ID_BASE-$i-$VERSION.module \
-Dtypes=module \
-Dclassifiers= \
-Dsources=$ARTIFACT_ID_BASE-$i-$VERSION-sources.jar \
-Djavadoc=$ARTIFACT_ID_BASE-$i-$VERSION-javadoc.jar
;;
*)
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.jar \
-Dfiles=$ARTIFACT_ID_BASE-$i-$VERSION.module \
-Dtypes=module \
-Dclassifiers= \
-Dsources=$ARTIFACT_ID_BASE-$i-$VERSION-sources.jar \
-Djavadoc=$ARTIFACT_ID_BASE-$i-$VERSION-javadoc.jar
;;
esac
popd
pushd .
done

View File

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

View File

@@ -217,6 +217,26 @@ public interface Secp256k1 {
*/
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.
*

View File

@@ -7,6 +7,14 @@ 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.macos_arm64 = libsecp256k1.a
libraryPaths.macos_arm64 = c/secp256k1/build/darwin/ native/build/darwin/
linkerOpts.macos_arm64 = -framework Security -framework Foundation
staticLibraries.ios = libsecp256k1.a
libraryPaths.ios_x64 = c/secp256k1/build/ios/ /usr/local/lib native/build/ios/
libraryPaths.ios_arm64 = c/secp256k1/build/ios/ /usr/local/lib native/build/ios/

View File

@@ -81,7 +81,7 @@ public object Secp256k1Native : Secp256k1 {
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() }
@@ -112,7 +112,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)
@@ -307,7 +307,16 @@ public object Secp256k1Native : Secp256k1 {
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")
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)
@@ -339,7 +348,7 @@ public object Secp256k1Native : Secp256k1 {
n
}
secp256k1_musig_pubkey_agg(ctx, combined.ptr, nKeyAggCache?.ptr, nPubkeys.toCValues(), pubkeys.size.convert()).requireSuccess("secp256k1_musig_nonce_agg() failed")
val agg = serializeXonlyPubkey(combined)
val agg = serializeXonlyPubkey(combined)
keyaggCache?.let { blob -> nKeyAggCache?.let { memcpy(toNat(blob), it.ptr, Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong()) } }
return agg
}
@@ -386,13 +395,14 @@ public object Secp256k1Native : Secp256k1 {
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>()

View File

@@ -57,6 +57,8 @@ kotlin {
}
linuxX64()
macosX64()
macosArm64()
iosX64()
iosArm64()
iosSimulatorArm64()

View File

@@ -5,6 +5,14 @@ import kotlin.test.*
class Secp256k1Test {
val random = Random.Default
fun randomBytes(length: Int): ByteArray {
val buffer = ByteArray(length)
random.nextBytes(buffer)
return buffer
}
@Test
fun verifyValidPrivateKey() {
val priv = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".lowercase())
@@ -454,40 +462,55 @@ class Secp256k1Test {
@Test
fun testMusig2SigningSession() {
val privkeys = listOf(
"0101010101010101010101010101010101010101010101010101010101010101",
"0202020202020202020202020202020202020202020202020202020202020202",
).map { Hex.decode(it) }.toTypedArray()
val privkeys = listOf(randomBytes(32), randomBytes(32))
val sessionId = randomBytes(32)
val msg32 = randomBytes(32)
val pubkeys = privkeys.map { Secp256k1.pubkeyCreate(it) }
val sessionId = Hex.decode("0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F")
val nonces = pubkeys.map { Secp256k1.musigNonceGen(sessionId, null, it, null, null, null) }
val 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]))
assertContentEquals(keyaggCaches[0], keyaggCaches[1])
assertContentEquals(aggpubkey, Secp256k1.musigPubkeyAgg(pubkeys.toTypedArray(), keyaggCaches[1]), testData)
assertContentEquals(keyaggCaches[0], keyaggCaches[1], testData)
val msg32 = Hex.decode("0303030303030303030303030303030303030303030303030303030303030303")
val sessions = (0 until 2).map { Secp256k1.musigNonceProcess(aggnonce, msg32, keyaggCaches[it]) }
val psigs = (0 until 2).map {
val psig = Secp256k1.musigPartialSign(secnonces[it], privkeys[it], keyaggCaches[it], sessions[it])
assertEquals(1, Secp256k1.musigPartialSigVerify(psig, pubnonces[it], pubkeys[it], keyaggCaches[it], sessions[it]))
assertEquals(0, Secp256k1.musigPartialSigVerify(Random.nextBytes(32), pubnonces[it], pubkeys[it], keyaggCaches[it], sessions[it]))
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()))
assertTrue(Secp256k1.verifySchnorr(sig, msg32, aggpubkey))
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))
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))
assertFalse(Secp256k1.verifySchnorr(invalidSig2, msg32, aggpubkey), testData)
}
@Test
@@ -523,15 +546,14 @@ class Secp256k1Test {
}
@Test
fun fuzzEcdsaSignVerify() {
val random = Random.Default
fun randomBytes(length: Int): ByteArray {
val buffer = ByteArray(length)
random.nextBytes(buffer)
return buffer
fun fuzzMusig2SigningSession() {
repeat(1000) {
testMusig2SigningSession()
}
}
@Test
fun fuzzEcdsaSignVerify() {
repeat(200) {
val priv = randomBytes(32)
assertTrue(Secp256k1.secKeyVerify(priv))