diff --git a/build.gradle.kts b/build.gradle.kts index ba7d9b6..7486b25 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,7 +13,7 @@ plugins { val currentOs = org.gradle.internal.os.OperatingSystem.current() group = "fr.acinq.bitcoin" -version = "0.23.0" +version = "0.24.0" repositories { google() diff --git a/src/commonMain/kotlin/fr/acinq/bitcoin/crypto/musig2/Musig2.kt b/src/commonMain/kotlin/fr/acinq/bitcoin/crypto/musig2/Musig2.kt index 03b5050..1e5b90f 100644 --- a/src/commonMain/kotlin/fr/acinq/bitcoin/crypto/musig2/Musig2.kt +++ b/src/commonMain/kotlin/fr/acinq/bitcoin/crypto/musig2/Musig2.kt @@ -165,8 +165,8 @@ public data class SecretNonce(internal val data: ByteVector) { * @return secret nonce and the corresponding public nonce. */ @JvmStatic - public fun generateWithCounter(nonRepeatingCounter: ULong, privateKey: PrivateKey, message: ByteVector32?, keyAggCache: KeyAggCache?, extraInput: ByteVector32?): Pair { - val nonce = Secp256k1.musigNonceGenCounter(nonRepeatingCounter, privateKey.value.toByteArray(), message?.toByteArray(), keyAggCache?.toByteArray(), extraInput?.toByteArray()) + public fun generateWithCounter(nonRepeatingCounter: Long, privateKey: PrivateKey, message: ByteVector32?, keyAggCache: KeyAggCache?, extraInput: ByteVector32?): Pair { + val nonce = Secp256k1.musigNonceGenCounter(nonRepeatingCounter.toULong(), privateKey.value.toByteArray(), message?.toByteArray(), keyAggCache?.toByteArray(), extraInput?.toByteArray()) val secretNonce = SecretNonce(nonce.copyOfRange(0, Secp256k1.MUSIG2_SECRET_NONCE_SIZE)) val publicNonce = IndividualNonce(nonce.copyOfRange(Secp256k1.MUSIG2_SECRET_NONCE_SIZE, Secp256k1.MUSIG2_SECRET_NONCE_SIZE + Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)) return Pair(secretNonce, publicNonce) @@ -236,16 +236,32 @@ public object Musig2 { */ @JvmStatic public fun aggregateKeys(publicKeys: List): XonlyPublicKey = KeyAggCache.create(publicKeys).first - + /** * @param sessionId a random, unique session ID. + * @param privateKey signer's private key + * @param publicKey signer's public key + * @param publicKeys public keys of all participants: callers must verify that all public keys are valid. + * @param message (optional) message that will be signed, if already known. + * @param extraInput (optional) additional random data. + */ + @JvmStatic + public fun generateNonce(sessionId: ByteVector32, privateKey: PrivateKey?, publicKey: PublicKey, publicKeys: List, message: ByteVector32?, extraInput: ByteVector32?): Pair { + val (_, keyAggCache) = KeyAggCache.create(publicKeys) + return SecretNonce.generate(sessionId, privateKey, publicKey, message, keyAggCache, extraInput) + } + + /** + * @param nonRepeatingCounter non-repeating counter that must never be reused with the same private key. * @param privateKey signer's private key. * @param publicKeys public keys of all participants: callers must verify that all public keys are valid. + * @param message (optional) message that will be signed, if already known. + * @param extraInput (optional) additional random data. */ @JvmStatic - public fun generateNonce(sessionId: ByteVector32, privateKey: PrivateKey, publicKeys: List): Pair { + public fun generateNonceWithCounter(nonRepeatingCounter: Long, privateKey: PrivateKey, publicKeys: List, message: ByteVector32?, extraInput: ByteVector32?): Pair { val (_, keyAggCache) = KeyAggCache.create(publicKeys) - return SecretNonce.generate(sessionId, privateKey, privateKey.publicKey(), message = null, keyAggCache, extraInput = null) + return SecretNonce.generateWithCounter(nonRepeatingCounter, privateKey, message, keyAggCache, extraInput) } /** diff --git a/src/commonTest/kotlin/fr/acinq/bitcoin/crypto/musig2/Musig2TestsCommon.kt b/src/commonTest/kotlin/fr/acinq/bitcoin/crypto/musig2/Musig2TestsCommon.kt index 5ceafcc..45e85b0 100644 --- a/src/commonTest/kotlin/fr/acinq/bitcoin/crypto/musig2/Musig2TestsCommon.kt +++ b/src/commonTest/kotlin/fr/acinq/bitcoin/crypto/musig2/Musig2TestsCommon.kt @@ -87,21 +87,21 @@ class Musig2TestsCommon { fun `generate secret nonce from counter`() { val privateKey = PrivateKey.fromHex("EEC1CB7D1B7254C5CAB0D9C61AB02E643D464A59FE6C96A7EFE871F07C5AEF54") run { - val nonce = SecretNonce.generateWithCounter(0UL, privateKey, null, null, null) + val nonce = SecretNonce.generateWithCounter(0L, privateKey, null, null, null) assertEquals(ByteVector.fromHex("03A5B9B6907942EACDDA49A366016EC2E62404A1BF4AB6D4DB82067BC3ADF086D7033205DB9EB34D5C7CE02848CAC68A83ED73E3883477F563F23CE9A11A7721EC64"), nonce.second.data) - val nonce1 = SecretNonce.generateWithCounter(0UL, privateKey, null, null, null) + val nonce1 = SecretNonce.generateWithCounter(0L, privateKey, null, null, null) assertEquals(nonce, nonce1) - val nonce2 = SecretNonce.generateWithCounter(0UL, PrivateKey.fromHex("EEC1CB7D1B7254C5CAB0D9C61AB02E643D464A59FE6C96A7EFE871F07C5AEF55"), null, null, null) + val nonce2 = SecretNonce.generateWithCounter(0L, PrivateKey.fromHex("EEC1CB7D1B7254C5CAB0D9C61AB02E643D464A59FE6C96A7EFE871F07C5AEF55"), null, null, null) assertNotEquals(nonce, nonce2) } run { - val nonce = SecretNonce.generateWithCounter(0UL, privateKey, ByteVector32.fromValidHex("380CD17A198FC3DAD3B7DA7492941F46976F2702FF7C66F24F472036AF1DA3F9"), null, null) + val nonce = SecretNonce.generateWithCounter(0L, privateKey, ByteVector32.fromValidHex("380CD17A198FC3DAD3B7DA7492941F46976F2702FF7C66F24F472036AF1DA3F9"), null, null) assertEquals(ByteVector.fromHex("0390B0553BA461A5BAC3F72BB86338D5FE8BB833ED7A21D3E21498C068D9A6E802020D641B37264FD22AC5E2F9FB868BAB49EB02FCB81AEC247FFD057DE37E1CB173"), nonce.second.data) } run { - val nonce = SecretNonce.generateWithCounter(1UL, privateKey, null, null, null) + val nonce = SecretNonce.generateWithCounter(1L, privateKey, null, null, null) assertEquals(ByteVector.fromHex("0340A08273BBC9ED0A2BFBDBDAFCCB43073865643593988841F67E665864767047037844A24EC0B763CE73F8252445DDDDFB7CD10498D796AD7217B841882A3A9961"), nonce.second.data) } } @@ -316,8 +316,8 @@ class Musig2TestsCommon { // The first step of a musig2 signing session is to exchange nonces. // If participants are disconnected before the end of the signing session, they must start again with fresh nonces. - val aliceNonce = Musig2.generateNonce(Random.Default.nextBytes(32).byteVector32(), alicePrivKey, listOf(alicePubKey, bobPubKey)) - val bobNonce = Musig2.generateNonce(Random.Default.nextBytes(32).byteVector32(), bobPrivKey, listOf(alicePubKey, bobPubKey)) + val aliceNonce = Musig2.generateNonce(Random.Default.nextBytes(32).byteVector32(), alicePrivKey, alicePrivKey.publicKey(), listOf(alicePubKey, bobPubKey), null, null) + val bobNonce = Musig2.generateNonce(Random.Default.nextBytes(32).byteVector32(), bobPrivKey, bobPrivKey.publicKey(), listOf(alicePubKey, bobPubKey), null, null) // Once they have each other's public nonce, they can produce partial signatures. val publicNonces = listOf(aliceNonce.second, bobNonce.second) @@ -354,8 +354,8 @@ class Musig2TestsCommon { val tx = Transaction(2, listOf(), listOf(TxOut(10_000.sat(), Script.pay2tr(commonPubKey))), 0) val spendingTx = Transaction(2, listOf(TxIn(OutPoint(tx, 0), sequence = 0)), listOf(TxOut(10_000.sat(), Script.pay2wpkh(alicePubKey))), 0) - val aliceNonce = Musig2.generateNonce(Random.Default.nextBytes(32).byteVector32(), alicePrivKey, listOf(alicePubKey, bobPubKey)) - val bobNonce = Musig2.generateNonce(Random.Default.nextBytes(32).byteVector32(), bobPrivKey, listOf(alicePubKey, bobPubKey)) + val aliceNonce = Musig2.generateNonce(Random.Default.nextBytes(32).byteVector32(), alicePrivKey, alicePrivKey.publicKey(),listOf(alicePubKey, bobPubKey), null, null) + val bobNonce = Musig2.generateNonce(Random.Default.nextBytes(32).byteVector32(), bobPrivKey, bobPrivKey.publicKey(), listOf(alicePubKey, bobPubKey), null, null) val publicNonces = listOf(aliceNonce.second, bobNonce.second) val aliceSig = Musig2.signTaprootInput(alicePrivKey, spendingTx, 0, listOf(tx.txOut[0]), listOf(alicePubKey, bobPubKey), aliceNonce.first, publicNonces, scriptTree = null).right @@ -411,8 +411,8 @@ class Musig2TestsCommon { ) // The first step of a musig2 signing session is to exchange nonces. // If participants are disconnected before the end of the signing session, they must start again with fresh nonces. - val userNonce = Musig2.generateNonce(Random.Default.nextBytes(32).byteVector32(), userPrivateKey, listOf(userPublicKey, serverPublicKey)) - val serverNonce = Musig2.generateNonce(Random.Default.nextBytes(32).byteVector32(), serverPrivateKey, listOf(userPublicKey, serverPublicKey)) + val userNonce = Musig2.generateNonce(Random.Default.nextBytes(32).byteVector32(), userPrivateKey, userPrivateKey.publicKey(), listOf(userPublicKey, serverPublicKey), null, null) + val serverNonce = Musig2.generateNonce(Random.Default.nextBytes(32).byteVector32(), serverPrivateKey, serverPrivateKey.publicKey(), listOf(userPublicKey, serverPublicKey), null, null) // Once they have each other's public nonce, they can produce partial signatures. val publicNonces = listOf(userNonce.second, serverNonce.second)