Skip to content

Commit 652d256

Browse files
committed
Fix musig2 counter-based nonce generation API
We used kotlin's ULong type for the counter arguments which mangles method names when used from Scala. We also add an alternate method for nonce generation that includes new paramaters (message and additional data) and deprecate (but keep) the old one.
1 parent a524842 commit 652d256

File tree

3 files changed

+42
-15
lines changed

3 files changed

+42
-15
lines changed

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ plugins {
1313
val currentOs = org.gradle.internal.os.OperatingSystem.current()
1414

1515
group = "fr.acinq.bitcoin"
16-
version = "0.23.0"
16+
version = "0.24.0"
1717

1818
repositories {
1919
google()

src/commonMain/kotlin/fr/acinq/bitcoin/crypto/musig2/Musig2.kt

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,8 @@ public data class SecretNonce(internal val data: ByteVector) {
165165
* @return secret nonce and the corresponding public nonce.
166166
*/
167167
@JvmStatic
168-
public fun generateWithCounter(nonRepeatingCounter: ULong, privateKey: PrivateKey, message: ByteVector32?, keyAggCache: KeyAggCache?, extraInput: ByteVector32?): Pair<SecretNonce, IndividualNonce> {
169-
val nonce = Secp256k1.musigNonceGenCounter(nonRepeatingCounter, privateKey.value.toByteArray(), message?.toByteArray(), keyAggCache?.toByteArray(), extraInput?.toByteArray())
168+
public fun generateWithCounter(nonRepeatingCounter: Long, privateKey: PrivateKey, message: ByteVector32?, keyAggCache: KeyAggCache?, extraInput: ByteVector32?): Pair<SecretNonce, IndividualNonce> {
169+
val nonce = Secp256k1.musigNonceGenCounter(nonRepeatingCounter.toULong(), privateKey.value.toByteArray(), message?.toByteArray(), keyAggCache?.toByteArray(), extraInput?.toByteArray())
170170
val secretNonce = SecretNonce(nonce.copyOfRange(0, Secp256k1.MUSIG2_SECRET_NONCE_SIZE))
171171
val publicNonce = IndividualNonce(nonce.copyOfRange(Secp256k1.MUSIG2_SECRET_NONCE_SIZE, Secp256k1.MUSIG2_SECRET_NONCE_SIZE + Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE))
172172
return Pair(secretNonce, publicNonce)
@@ -242,10 +242,37 @@ public object Musig2 {
242242
* @param privateKey signer's private key.
243243
* @param publicKeys public keys of all participants: callers must verify that all public keys are valid.
244244
*/
245+
@Deprecated("use alternate generateNonce() method instead")
245246
@JvmStatic
246247
public fun generateNonce(sessionId: ByteVector32, privateKey: PrivateKey, publicKeys: List<PublicKey>): Pair<SecretNonce, IndividualNonce> {
248+
return generateNonce(sessionId, privateKey, privateKey.publicKey(), publicKeys, null, null)
249+
}
250+
251+
/**
252+
* @param sessionId a random, unique session ID.
253+
* @param privateKey signer's private key
254+
* @param publicKey signer's public key
255+
* @param publicKeys public keys of all participants: callers must verify that all public keys are valid.
256+
* @param message (optional) message that will be signed, if already known.
257+
* @param extraInput (optional) additional random data.
258+
*/
259+
@JvmStatic
260+
public fun generateNonce(sessionId: ByteVector32, privateKey: PrivateKey?, publicKey: PublicKey, publicKeys: List<PublicKey>, message: ByteVector32?, extraInput: ByteVector32?): Pair<SecretNonce, IndividualNonce> {
261+
val (_, keyAggCache) = KeyAggCache.create(publicKeys)
262+
return SecretNonce.generate(sessionId, privateKey, publicKey, message, keyAggCache, extraInput)
263+
}
264+
265+
/**
266+
* @param nonRepeatingCounter non-repeating counter that must never be reused with the same private key.
267+
* @param privateKey signer's private key.
268+
* @param publicKeys public keys of all participants: callers must verify that all public keys are valid.
269+
* @param message (optional) message that will be signed, if already known.
270+
* @param extraInput (optional) additional random data.
271+
*/
272+
@JvmStatic
273+
public fun generateNonceWithCounter(nonRepeatingCounter: Long, privateKey: PrivateKey, publicKeys: List<PublicKey>, message: ByteVector32?, extraInput: ByteVector32?): Pair<SecretNonce, IndividualNonce> {
247274
val (_, keyAggCache) = KeyAggCache.create(publicKeys)
248-
return SecretNonce.generate(sessionId, privateKey, privateKey.publicKey(), message = null, keyAggCache, extraInput = null)
275+
return SecretNonce.generateWithCounter(nonRepeatingCounter, privateKey, message, keyAggCache, extraInput)
249276
}
250277

251278
/**

src/commonTest/kotlin/fr/acinq/bitcoin/crypto/musig2/Musig2TestsCommon.kt

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -87,21 +87,21 @@ class Musig2TestsCommon {
8787
fun `generate secret nonce from counter`() {
8888
val privateKey = PrivateKey.fromHex("EEC1CB7D1B7254C5CAB0D9C61AB02E643D464A59FE6C96A7EFE871F07C5AEF54")
8989
run {
90-
val nonce = SecretNonce.generateWithCounter(0UL, privateKey, null, null, null)
90+
val nonce = SecretNonce.generateWithCounter(0L, privateKey, null, null, null)
9191
assertEquals(ByteVector.fromHex("03A5B9B6907942EACDDA49A366016EC2E62404A1BF4AB6D4DB82067BC3ADF086D7033205DB9EB34D5C7CE02848CAC68A83ED73E3883477F563F23CE9A11A7721EC64"), nonce.second.data)
9292

93-
val nonce1 = SecretNonce.generateWithCounter(0UL, privateKey, null, null, null)
93+
val nonce1 = SecretNonce.generateWithCounter(0L, privateKey, null, null, null)
9494
assertEquals(nonce, nonce1)
9595

96-
val nonce2 = SecretNonce.generateWithCounter(0UL, PrivateKey.fromHex("EEC1CB7D1B7254C5CAB0D9C61AB02E643D464A59FE6C96A7EFE871F07C5AEF55"), null, null, null)
96+
val nonce2 = SecretNonce.generateWithCounter(0L, PrivateKey.fromHex("EEC1CB7D1B7254C5CAB0D9C61AB02E643D464A59FE6C96A7EFE871F07C5AEF55"), null, null, null)
9797
assertNotEquals(nonce, nonce2)
9898
}
9999
run {
100-
val nonce = SecretNonce.generateWithCounter(0UL, privateKey, ByteVector32.fromValidHex("380CD17A198FC3DAD3B7DA7492941F46976F2702FF7C66F24F472036AF1DA3F9"), null, null)
100+
val nonce = SecretNonce.generateWithCounter(0L, privateKey, ByteVector32.fromValidHex("380CD17A198FC3DAD3B7DA7492941F46976F2702FF7C66F24F472036AF1DA3F9"), null, null)
101101
assertEquals(ByteVector.fromHex("0390B0553BA461A5BAC3F72BB86338D5FE8BB833ED7A21D3E21498C068D9A6E802020D641B37264FD22AC5E2F9FB868BAB49EB02FCB81AEC247FFD057DE37E1CB173"), nonce.second.data)
102102
}
103103
run {
104-
val nonce = SecretNonce.generateWithCounter(1UL, privateKey, null, null, null)
104+
val nonce = SecretNonce.generateWithCounter(1L, privateKey, null, null, null)
105105
assertEquals(ByteVector.fromHex("0340A08273BBC9ED0A2BFBDBDAFCCB43073865643593988841F67E665864767047037844A24EC0B763CE73F8252445DDDDFB7CD10498D796AD7217B841882A3A9961"), nonce.second.data)
106106
}
107107
}
@@ -316,8 +316,8 @@ class Musig2TestsCommon {
316316

317317
// The first step of a musig2 signing session is to exchange nonces.
318318
// If participants are disconnected before the end of the signing session, they must start again with fresh nonces.
319-
val aliceNonce = Musig2.generateNonce(Random.Default.nextBytes(32).byteVector32(), alicePrivKey, listOf(alicePubKey, bobPubKey))
320-
val bobNonce = Musig2.generateNonce(Random.Default.nextBytes(32).byteVector32(), bobPrivKey, listOf(alicePubKey, bobPubKey))
319+
val aliceNonce = Musig2.generateNonce(Random.Default.nextBytes(32).byteVector32(), alicePrivKey, alicePrivKey.publicKey(), listOf(alicePubKey, bobPubKey), null, null)
320+
val bobNonce = Musig2.generateNonce(Random.Default.nextBytes(32).byteVector32(), bobPrivKey, bobPrivKey.publicKey(), listOf(alicePubKey, bobPubKey), null, null)
321321

322322
// Once they have each other's public nonce, they can produce partial signatures.
323323
val publicNonces = listOf(aliceNonce.second, bobNonce.second)
@@ -354,8 +354,8 @@ class Musig2TestsCommon {
354354
val tx = Transaction(2, listOf(), listOf(TxOut(10_000.sat(), Script.pay2tr(commonPubKey))), 0)
355355
val spendingTx = Transaction(2, listOf(TxIn(OutPoint(tx, 0), sequence = 0)), listOf(TxOut(10_000.sat(), Script.pay2wpkh(alicePubKey))), 0)
356356

357-
val aliceNonce = Musig2.generateNonce(Random.Default.nextBytes(32).byteVector32(), alicePrivKey, listOf(alicePubKey, bobPubKey))
358-
val bobNonce = Musig2.generateNonce(Random.Default.nextBytes(32).byteVector32(), bobPrivKey, listOf(alicePubKey, bobPubKey))
357+
val aliceNonce = Musig2.generateNonce(Random.Default.nextBytes(32).byteVector32(), alicePrivKey, alicePrivKey.publicKey(),listOf(alicePubKey, bobPubKey), null, null)
358+
val bobNonce = Musig2.generateNonce(Random.Default.nextBytes(32).byteVector32(), bobPrivKey, bobPrivKey.publicKey(), listOf(alicePubKey, bobPubKey), null, null)
359359
val publicNonces = listOf(aliceNonce.second, bobNonce.second)
360360

361361
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 {
411411
)
412412
// The first step of a musig2 signing session is to exchange nonces.
413413
// If participants are disconnected before the end of the signing session, they must start again with fresh nonces.
414-
val userNonce = Musig2.generateNonce(Random.Default.nextBytes(32).byteVector32(), userPrivateKey, listOf(userPublicKey, serverPublicKey))
415-
val serverNonce = Musig2.generateNonce(Random.Default.nextBytes(32).byteVector32(), serverPrivateKey, listOf(userPublicKey, serverPublicKey))
414+
val userNonce = Musig2.generateNonce(Random.Default.nextBytes(32).byteVector32(), userPrivateKey, userPrivateKey.publicKey(), listOf(userPublicKey, serverPublicKey), null, null)
415+
val serverNonce = Musig2.generateNonce(Random.Default.nextBytes(32).byteVector32(), serverPrivateKey, serverPrivateKey.publicKey(), listOf(userPublicKey, serverPublicKey), null, null)
416416

417417
// Once they have each other's public nonce, they can produce partial signatures.
418418
val publicNonces = listOf(userNonce.second, serverNonce.second)

0 commit comments

Comments
 (0)