• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 11216766535

07 Oct 2024 01:37PM UTC coverage: 57.817% (-1.0%) from 58.817%
11216766535

Pull #9148

github

ProofOfKeags
lnwire: remove kickoff feerate from propose/commit
Pull Request #9148: DynComms [2/n]: lnwire: add authenticated wire messages for Dyn*

571 of 879 new or added lines in 16 files covered. (64.96%)

23253 existing lines in 251 files now uncovered.

99022 of 171268 relevant lines covered (57.82%)

38420.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.84
/internal/musig2v040/sign.go
1
// Copyright 2013-2022 The btcsuite developers
2

3
package musig2v040
4

5
import (
6
        "bytes"
7
        "fmt"
8
        "io"
9

10
        "github.com/btcsuite/btcd/btcec/v2"
11
        "github.com/btcsuite/btcd/btcec/v2/schnorr"
12
        "github.com/btcsuite/btcd/chaincfg/chainhash"
13
        secp "github.com/decred/dcrd/dcrec/secp256k1/v4"
14
)
15

16
var (
17
        // NonceBlindTag is that tag used to construct the value b, which
18
        // blinds the second public nonce of each party.
19
        NonceBlindTag = []byte("MuSig/noncecoef")
20

21
        // ChallengeHashTag is the tag used to construct the challenge hash
22
        ChallengeHashTag = []byte("BIP0340/challenge")
23

24
        // ErrNoncePointAtInfinity is returned if during signing, the fully
25
        // combined public nonce is the point at infinity.
26
        ErrNoncePointAtInfinity = fmt.Errorf("signing nonce is the infinity " +
27
                "point")
28

29
        // ErrPrivKeyZero is returned when the private key for signing is
30
        // actually zero.
31
        ErrPrivKeyZero = fmt.Errorf("priv key is zero")
32

33
        // ErrPartialSigInvalid is returned when a partial is found to be
34
        // invalid.
35
        ErrPartialSigInvalid = fmt.Errorf("partial signature is invalid")
36

37
        // ErrSecretNonceZero is returned when a secret nonce is passed in a
38
        // zero.
39
        ErrSecretNonceZero = fmt.Errorf("secret nonce is blank")
40
)
41

42
// infinityPoint is the jacobian representation of the point at infinity.
43
var infinityPoint btcec.JacobianPoint
44

45
// PartialSignature reprints a partial (s-only) musig2 multi-signature. This
46
// isn't a valid schnorr signature by itself, as it needs to be aggregated
47
// along with the other partial signatures to be completed.
48
type PartialSignature struct {
49
        S *btcec.ModNScalar
50

51
        R *btcec.PublicKey
52
}
53

54
// NewPartialSignature returns a new instances of the partial sig struct.
55
func NewPartialSignature(s *btcec.ModNScalar,
56
        r *btcec.PublicKey) PartialSignature {
510✔
57

510✔
58
        return PartialSignature{
510✔
59
                S: s,
510✔
60
                R: r,
510✔
61
        }
510✔
62
}
510✔
63

64
// Encode writes a serialized version of the partial signature to the passed
65
// io.Writer
66
func (p *PartialSignature) Encode(w io.Writer) error {
×
67
        var sBytes [32]byte
×
68
        p.S.PutBytes(&sBytes)
×
69

×
70
        if _, err := w.Write(sBytes[:]); err != nil {
×
71
                return err
×
72
        }
×
73

74
        return nil
×
75
}
76

77
// Decode attempts to parse a serialized PartialSignature stored in the passed
78
// io reader.
79
func (p *PartialSignature) Decode(r io.Reader) error {
21✔
80
        p.S = new(btcec.ModNScalar)
21✔
81

21✔
82
        var sBytes [32]byte
21✔
83
        if _, err := io.ReadFull(r, sBytes[:]); err != nil {
21✔
84
                return nil
×
85
        }
×
86

87
        overflows := p.S.SetBytes(&sBytes)
21✔
88
        if overflows == 1 {
23✔
89
                return ErrPartialSigInvalid
2✔
90
        }
2✔
91

92
        return nil
19✔
93
}
94

95
// SignOption is a functional option argument that allows callers to modify the
96
// way we generate musig2 schnorr signatures.
97
type SignOption func(*signOptions)
98

99
// signOptions houses the set of functional options that can be used to modify
100
// the method used to generate the musig2 partial signature.
101
type signOptions struct {
102
        // fastSign determines if we'll skip the check at the end of the
103
        // routine where we attempt to verify the produced signature.
104
        fastSign bool
105

106
        // sortKeys determines if the set of keys should be sorted before doing
107
        // key aggregation.
108
        sortKeys bool
109

110
        // tweaks specifies a series of tweaks to be applied to the aggregated
111
        // public key, which also partially carries over into the signing
112
        // process.
113
        tweaks []KeyTweakDesc
114

115
        // taprootTweak specifies a taproot specific tweak.  of the tweaks
116
        // specified above. Normally we'd just apply the raw 32 byte tweak, but
117
        // for taproot, we first need to compute the aggregated key before
118
        // tweaking, and then use it as the internal key. This is required as
119
        // the taproot tweak also commits to the public key, which in this case
120
        // is the aggregated key before the tweak.
121
        taprootTweak []byte
122

123
        // bip86Tweak specifies that the taproot tweak should be done in a BIP
124
        // 86 style, where we don't expect an actual tweak and instead just
125
        // commit to the public key itself.
126
        bip86Tweak bool
127
}
128

129
// defaultSignOptions returns the default set of signing operations.
130
func defaultSignOptions() *signOptions {
1,033✔
131
        return &signOptions{}
1,033✔
132
}
1,033✔
133

134
// WithFastSign forces signing to skip the extra verification step at the end.
135
// Performance sensitive applications may opt to use this option to speed up
136
// the signing operation.
137
func WithFastSign() SignOption {
×
138
        return func(o *signOptions) {
×
139
                o.fastSign = true
×
140
        }
×
141
}
142

143
// WithSortedKeys determines if the set of signing public keys are to be sorted
144
// or not before doing key aggregation.
UNCOV
145
func WithSortedKeys() SignOption {
×
UNCOV
146
        return func(o *signOptions) {
×
UNCOV
147
                o.sortKeys = true
×
UNCOV
148
        }
×
149
}
150

151
// WithTweaks determines if the aggregated public key used should apply a
152
// series of tweaks before key aggregation.
153
func WithTweaks(tweaks ...KeyTweakDesc) SignOption {
8✔
154
        return func(o *signOptions) {
20✔
155
                o.tweaks = tweaks
12✔
156
        }
12✔
157
}
158

159
// WithTaprootSignTweak allows a caller to specify a tweak that should be used
160
// in a bip 340 manner when signing. This differs from WithTweaks as the tweak
161
// will be assumed to always be x-only and the intermediate aggregate key
162
// before tweaking will be used to generate part of the tweak (as the taproot
163
// tweak also commits to the internal key).
164
//
165
// This option should be used in the taproot context to create a valid
166
// signature for the keypath spend for taproot, when the output key is actually
167
// committing to a script path, or some other data.
168
func WithTaprootSignTweak(scriptRoot []byte) SignOption {
100✔
169
        return func(o *signOptions) {
300✔
170
                o.taprootTweak = scriptRoot
200✔
171
        }
200✔
172
}
173

174
// WithBip86SignTweak allows a caller to specify a tweak that should be used in
175
// a bip 340 manner when signing, factoring in BIP 86 as well. This differs
176
// from WithTaprootSignTweak as no true script root will be committed to,
177
// instead we just commit to the internal key.
178
//
179
// This option should be used in the taproot context to create a valid
180
// signature for the keypath spend for taproot, when the output key was
181
// generated using BIP 86.
182
func WithBip86SignTweak() SignOption {
400✔
183
        return func(o *signOptions) {
1,200✔
184
                o.bip86Tweak = true
800✔
185
        }
800✔
186
}
187

188
// Sign generates a musig2 partial signature given the passed key set, secret
189
// nonce, public nonce, and private keys. This method returns an error if the
190
// generated nonces are either too large, or end up mapping to the point at
191
// infinity.
192
func Sign(secNonce [SecNonceSize]byte, privKey *btcec.PrivateKey,
193
        combinedNonce [PubNonceSize]byte, pubKeys []*btcec.PublicKey,
194
        msg [32]byte, signOpts ...SignOption) (*PartialSignature, error) {
513✔
195

513✔
196
        // First, parse the set of optional signing options.
513✔
197
        opts := defaultSignOptions()
513✔
198
        for _, option := range signOpts {
1,017✔
199
                option(opts)
504✔
200
        }
504✔
201

202
        // Compute the hash of all the keys here as we'll need it do aggregate
203
        // the keys and also at the final step of signing.
204
        keysHash := keyHashFingerprint(pubKeys, opts.sortKeys)
513✔
205
        uniqueKeyIndex := secondUniqueKeyIndex(pubKeys, opts.sortKeys)
513✔
206

513✔
207
        keyAggOpts := []KeyAggOption{
513✔
208
                WithKeysHash(keysHash), WithUniqueKeyIndex(uniqueKeyIndex),
513✔
209
        }
513✔
210
        switch {
513✔
211
        case opts.bip86Tweak:
400✔
212
                keyAggOpts = append(
400✔
213
                        keyAggOpts, WithBIP86KeyTweak(),
400✔
214
                )
400✔
215
        case opts.taprootTweak != nil:
100✔
216
                keyAggOpts = append(
100✔
217
                        keyAggOpts, WithTaprootKeyTweak(opts.taprootTweak),
100✔
218
                )
100✔
219
        case len(opts.tweaks) != 0:
4✔
220
                keyAggOpts = append(keyAggOpts, WithKeyTweaks(opts.tweaks...))
4✔
221
        }
222

223
        // Next we'll construct the aggregated public key based on the set of
224
        // signers.
225
        combinedKey, parityAcc, _, err := AggregateKeys(
513✔
226
                pubKeys, opts.sortKeys, keyAggOpts...,
513✔
227
        )
513✔
228
        if err != nil {
513✔
229
                return nil, err
×
230
        }
×
231

232
        // Next we'll compute the value b, that blinds our second public
233
        // nonce:
234
        //  * b = h(tag=NonceBlindTag, combinedNonce || combinedKey || m).
235
        var (
513✔
236
                nonceMsgBuf  bytes.Buffer
513✔
237
                nonceBlinder btcec.ModNScalar
513✔
238
        )
513✔
239
        nonceMsgBuf.Write(combinedNonce[:])
513✔
240
        nonceMsgBuf.Write(schnorr.SerializePubKey(combinedKey.FinalKey))
513✔
241
        nonceMsgBuf.Write(msg[:])
513✔
242
        nonceBlindHash := chainhash.TaggedHash(
513✔
243
                NonceBlindTag, nonceMsgBuf.Bytes(),
513✔
244
        )
513✔
245
        nonceBlinder.SetByteSlice(nonceBlindHash[:])
513✔
246

513✔
247
        // Next, we'll parse the public nonces into R1 and R2.
513✔
248
        r1J, err := btcec.ParseJacobian(
513✔
249
                combinedNonce[:btcec.PubKeyBytesLenCompressed],
513✔
250
        )
513✔
251
        if err != nil {
514✔
252
                return nil, err
1✔
253
        }
1✔
254
        r2J, err := btcec.ParseJacobian(
512✔
255
                combinedNonce[btcec.PubKeyBytesLenCompressed:],
512✔
256
        )
512✔
257
        if err != nil {
514✔
258
                return nil, err
2✔
259
        }
2✔
260

261
        // With our nonce blinding value, we'll now combine both the public
262
        // nonces, using the blinding factor to tweak the second nonce:
263
        //  * R = R_1 + b*R_2
264
        var nonce btcec.JacobianPoint
510✔
265
        btcec.ScalarMultNonConst(&nonceBlinder, &r2J, &r2J)
510✔
266
        btcec.AddNonConst(&r1J, &r2J, &nonce)
510✔
267

510✔
268
        // If the combined nonce it eh point at infinity, then we'll bail out.
510✔
269
        if nonce == infinityPoint {
511✔
270
                G := btcec.Generator()
1✔
271
                G.AsJacobian(&nonce)
1✔
272
        }
1✔
273

274
        // Next we'll parse out our two secret nonces, which we'll be using in
275
        // the core signing process below.
276
        var k1, k2 btcec.ModNScalar
510✔
277
        k1.SetByteSlice(secNonce[:btcec.PrivKeyBytesLen])
510✔
278
        k2.SetByteSlice(secNonce[btcec.PrivKeyBytesLen:])
510✔
279

510✔
280
        if k1.IsZero() || k2.IsZero() {
510✔
281
                return nil, ErrSecretNonceZero
×
282
        }
×
283

284
        nonce.ToAffine()
510✔
285

510✔
286
        nonceKey := btcec.NewPublicKey(&nonce.X, &nonce.Y)
510✔
287

510✔
288
        // If the nonce R has an odd y coordinate, then we'll negate both our
510✔
289
        // secret nonces.
510✔
290
        if nonce.Y.IsOdd() {
715✔
291
                k1.Negate()
205✔
292
                k2.Negate()
205✔
293
        }
205✔
294

295
        privKeyScalar := privKey.Key
510✔
296
        if privKeyScalar.IsZero() {
510✔
297
                return nil, ErrPrivKeyZero
×
298
        }
×
299

300
        pubKey := privKey.PubKey()
510✔
301
        pubKeyYIsOdd := func() bool {
1,020✔
302
                pubKeyBytes := pubKey.SerializeCompressed()
510✔
303
                return pubKeyBytes[0] == secp.PubKeyFormatCompressedOdd
510✔
304
        }()
510✔
305
        combinedKeyYIsOdd := func() bool {
1,020✔
306
                combinedKeyBytes := combinedKey.FinalKey.SerializeCompressed()
510✔
307
                return combinedKeyBytes[0] == secp.PubKeyFormatCompressedOdd
510✔
308
        }()
510✔
309

310
        // Next we'll compute our two parity factors for Q the combined public
311
        // key, and P, the public key we're signing with. If the keys are odd,
312
        // then we'll negate them.
313
        parityCombinedKey := new(btcec.ModNScalar).SetInt(1)
510✔
314
        paritySignKey := new(btcec.ModNScalar).SetInt(1)
510✔
315
        if combinedKeyYIsOdd {
714✔
316
                parityCombinedKey.Negate()
204✔
317
        }
204✔
318
        if pubKeyYIsOdd {
770✔
319
                paritySignKey.Negate()
260✔
320
        }
260✔
321

322
        // Before we sign below, we'll multiply by our various parity factors
323
        // to ensure that the signing key is properly negated (if necessary):
324
        //  * d = gvâ‹…gaccvâ‹…gpâ‹…d'
325
        privKeyScalar.Mul(parityCombinedKey).Mul(paritySignKey).Mul(parityAcc)
510✔
326

510✔
327
        // Next we'll create the challenge hash that commits to the combined
510✔
328
        // nonce, combined public key and also the message:
510✔
329
        // * e = H(tag=ChallengeHashTag, R || Q || m) mod n
510✔
330
        var challengeMsg bytes.Buffer
510✔
331
        challengeMsg.Write(schnorr.SerializePubKey(nonceKey))
510✔
332
        challengeMsg.Write(schnorr.SerializePubKey(combinedKey.FinalKey))
510✔
333
        challengeMsg.Write(msg[:])
510✔
334
        challengeBytes := chainhash.TaggedHash(
510✔
335
                ChallengeHashTag, challengeMsg.Bytes(),
510✔
336
        )
510✔
337
        var e btcec.ModNScalar
510✔
338
        e.SetByteSlice(challengeBytes[:])
510✔
339

510✔
340
        // Next, we'll compute a, our aggregation coefficient for the key that
510✔
341
        // we're signing with.
510✔
342
        a := aggregationCoefficient(pubKeys, pubKey, keysHash, uniqueKeyIndex)
510✔
343

510✔
344
        // With mu constructed, we can finally generate our partial signature
510✔
345
        // as: s = (k1_1 + b*k_2 + e*a*d) mod n.
510✔
346
        s := new(btcec.ModNScalar)
510✔
347
        s.Add(&k1).Add(k2.Mul(&nonceBlinder)).Add(e.Mul(a).Mul(&privKeyScalar))
510✔
348

510✔
349
        sig := NewPartialSignature(s, nonceKey)
510✔
350

510✔
351
        // If we're not in fast sign mode, then we'll also validate our partial
510✔
352
        // signature.
510✔
353
        if !opts.fastSign {
1,020✔
354
                pubNonce := secNonceToPubNonce(secNonce)
510✔
355
                sigValid := sig.Verify(
510✔
356
                        pubNonce, combinedNonce, pubKeys, pubKey, msg,
510✔
357
                        signOpts...,
510✔
358
                )
510✔
359
                if !sigValid {
510✔
360
                        return nil, fmt.Errorf("sig is invalid!")
×
361
                }
×
362
        }
363

364
        return &sig, nil
510✔
365
}
366

367
// Verify implements partial signature verification given the public nonce for
368
// the signer, aggregate nonce, signer set and finally the message being
369
// signed.
370
func (p *PartialSignature) Verify(pubNonce [PubNonceSize]byte,
371
        combinedNonce [PubNonceSize]byte, keySet []*btcec.PublicKey,
372
        signingKey *btcec.PublicKey, msg [32]byte, signOpts ...SignOption) bool {
510✔
373

510✔
374
        pubKey := schnorr.SerializePubKey(signingKey)
510✔
375

510✔
376
        return verifyPartialSig(
510✔
377
                p, pubNonce, combinedNonce, keySet, pubKey, msg, signOpts...,
510✔
378
        ) == nil
510✔
379
}
510✔
380

381
// verifyPartialSig attempts to verify a partial schnorr signature given the
382
// necessary parameters. This is the internal version of Verify that returns
383
// detailed errors.  signed.
384
func verifyPartialSig(partialSig *PartialSignature, pubNonce [PubNonceSize]byte,
385
        combinedNonce [PubNonceSize]byte, keySet []*btcec.PublicKey,
386
        pubKey []byte, msg [32]byte, signOpts ...SignOption) error {
520✔
387

520✔
388
        opts := defaultSignOptions()
520✔
389
        for _, option := range signOpts {
1,028✔
390
                option(opts)
508✔
391
        }
508✔
392

393
        // First we'll map the internal partial signature back into something
394
        // we can manipulate.
395
        s := partialSig.S
520✔
396

520✔
397
        // Next we'll parse out the two public nonces into something we can
520✔
398
        // use.
520✔
399
        //
520✔
400

520✔
401
        // Compute the hash of all the keys here as we'll need it do aggregate
520✔
402
        // the keys and also at the final step of verification.
520✔
403
        keysHash := keyHashFingerprint(keySet, opts.sortKeys)
520✔
404
        uniqueKeyIndex := secondUniqueKeyIndex(keySet, opts.sortKeys)
520✔
405

520✔
406
        keyAggOpts := []KeyAggOption{
520✔
407
                WithKeysHash(keysHash), WithUniqueKeyIndex(uniqueKeyIndex),
520✔
408
        }
520✔
409
        switch {
520✔
410
        case opts.bip86Tweak:
400✔
411
                keyAggOpts = append(
400✔
412
                        keyAggOpts, WithBIP86KeyTweak(),
400✔
413
                )
400✔
414
        case opts.taprootTweak != nil:
100✔
415
                keyAggOpts = append(
100✔
416
                        keyAggOpts, WithTaprootKeyTweak(opts.taprootTweak),
100✔
417
                )
100✔
418
        case len(opts.tweaks) != 0:
8✔
419
                keyAggOpts = append(keyAggOpts, WithKeyTweaks(opts.tweaks...))
8✔
420
        }
421

422
        // Next we'll construct the aggregated public key based on the set of
423
        // signers.
424
        combinedKey, parityAcc, _, err := AggregateKeys(
520✔
425
                keySet, opts.sortKeys, keyAggOpts...,
520✔
426
        )
520✔
427
        if err != nil {
520✔
428
                return err
×
429
        }
×
430

431
        // Next we'll compute the value b, that blinds our second public
432
        // nonce:
433
        //  * b = h(tag=NonceBlindTag, combinedNonce || combinedKey || m).
434
        var (
520✔
435
                nonceMsgBuf  bytes.Buffer
520✔
436
                nonceBlinder btcec.ModNScalar
520✔
437
        )
520✔
438
        nonceMsgBuf.Write(combinedNonce[:])
520✔
439
        nonceMsgBuf.Write(schnorr.SerializePubKey(combinedKey.FinalKey))
520✔
440
        nonceMsgBuf.Write(msg[:])
520✔
441
        nonceBlindHash := chainhash.TaggedHash(NonceBlindTag, nonceMsgBuf.Bytes())
520✔
442
        nonceBlinder.SetByteSlice(nonceBlindHash[:])
520✔
443

520✔
444
        r1J, err := btcec.ParseJacobian(
520✔
445
                combinedNonce[:btcec.PubKeyBytesLenCompressed],
520✔
446
        )
520✔
447
        if err != nil {
520✔
448
                return err
×
449
        }
×
450
        r2J, err := btcec.ParseJacobian(
520✔
451
                combinedNonce[btcec.PubKeyBytesLenCompressed:],
520✔
452
        )
520✔
453
        if err != nil {
520✔
454
                return err
×
455
        }
×
456

457
        // With our nonce blinding value, we'll now combine both the public
458
        // nonces, using the blinding factor to tweak the second nonce:
459
        //  * R = R_1 + b*R_2
460

461
        var nonce btcec.JacobianPoint
520✔
462
        btcec.ScalarMultNonConst(&nonceBlinder, &r2J, &r2J)
520✔
463
        btcec.AddNonConst(&r1J, &r2J, &nonce)
520✔
464

520✔
465
        // Next, we'll parse out the set of public nonces this signer used to
520✔
466
        // generate the signature.
520✔
467
        pubNonce1J, err := btcec.ParseJacobian(
520✔
468
                pubNonce[:btcec.PubKeyBytesLenCompressed],
520✔
469
        )
520✔
470
        if err != nil {
520✔
471
                return err
×
472
        }
×
473
        pubNonce2J, err := btcec.ParseJacobian(
520✔
474
                pubNonce[btcec.PubKeyBytesLenCompressed:],
520✔
475
        )
520✔
476
        if err != nil {
520✔
477
                return err
×
478
        }
×
479

480
        // If the nonce is the infinity point we set it to the Generator.
481
        if nonce == infinityPoint {
522✔
482
                btcec.GeneratorJacobian(&nonce)
2✔
483
        } else {
520✔
484
                nonce.ToAffine()
518✔
485
        }
518✔
486

487
        // We'll perform a similar aggregation and blinding operator as we did
488
        // above for the combined nonces: R' = R_1' + b*R_2'.
489
        var pubNonceJ btcec.JacobianPoint
520✔
490

520✔
491
        btcec.ScalarMultNonConst(&nonceBlinder, &pubNonce2J, &pubNonce2J)
520✔
492
        btcec.AddNonConst(&pubNonce1J, &pubNonce2J, &pubNonceJ)
520✔
493

520✔
494
        pubNonceJ.ToAffine()
520✔
495

520✔
496
        // If the combined nonce used in the challenge hash has an odd y
520✔
497
        // coordinate, then we'll negate our final public nonce.
520✔
498
        if nonce.Y.IsOdd() {
730✔
499
                pubNonceJ.Y.Negate(1)
210✔
500
                pubNonceJ.Y.Normalize()
210✔
501
        }
210✔
502

503
        // Next we'll create the challenge hash that commits to the combined
504
        // nonce, combined public key and also the message:
505
        //  * e = H(tag=ChallengeHashTag, R || Q || m) mod n
506
        var challengeMsg bytes.Buffer
520✔
507
        challengeMsg.Write(schnorr.SerializePubKey(btcec.NewPublicKey(
520✔
508
                &nonce.X, &nonce.Y,
520✔
509
        )))
520✔
510
        challengeMsg.Write(schnorr.SerializePubKey(combinedKey.FinalKey))
520✔
511
        challengeMsg.Write(msg[:])
520✔
512
        challengeBytes := chainhash.TaggedHash(
520✔
513
                ChallengeHashTag, challengeMsg.Bytes(),
520✔
514
        )
520✔
515
        var e btcec.ModNScalar
520✔
516
        e.SetByteSlice(challengeBytes[:])
520✔
517

520✔
518
        signingKey, err := schnorr.ParsePubKey(pubKey)
520✔
519
        if err != nil {
520✔
520
                return err
×
521
        }
×
522

523
        // Next, we'll compute a, our aggregation coefficient for the key that
524
        // we're signing with.
525
        a := aggregationCoefficient(keySet, signingKey, keysHash, uniqueKeyIndex)
520✔
526

520✔
527
        // If the combined key has an odd y coordinate, then we'll negate
520✔
528
        // parity factor for the signing key.
520✔
529
        paritySignKey := new(btcec.ModNScalar).SetInt(1)
520✔
530
        combinedKeyBytes := combinedKey.FinalKey.SerializeCompressed()
520✔
531
        if combinedKeyBytes[0] == secp.PubKeyFormatCompressedOdd {
730✔
532
                paritySignKey.Negate()
210✔
533
        }
210✔
534

535
        // Next, we'll construct the final parity factor by multiplying the
536
        // sign key parity factor with the accumulated parity factor for all
537
        // the keys.
538
        finalParityFactor := paritySignKey.Mul(parityAcc)
520✔
539

520✔
540
        // Now we'll multiply the parity factor by our signing key, which'll
520✔
541
        // take care of the amount of negation needed.
520✔
542
        var signKeyJ btcec.JacobianPoint
520✔
543
        signingKey.AsJacobian(&signKeyJ)
520✔
544
        btcec.ScalarMultNonConst(finalParityFactor, &signKeyJ, &signKeyJ)
520✔
545

520✔
546
        // In the final set, we'll check that: s*G == R' + e*a*P.
520✔
547
        var sG, rP btcec.JacobianPoint
520✔
548
        btcec.ScalarBaseMultNonConst(s, &sG)
520✔
549
        btcec.ScalarMultNonConst(e.Mul(a), &signKeyJ, &rP)
520✔
550
        btcec.AddNonConst(&rP, &pubNonceJ, &rP)
520✔
551

520✔
552
        sG.ToAffine()
520✔
553
        rP.ToAffine()
520✔
554

520✔
555
        if sG != rP {
522✔
556
                return ErrPartialSigInvalid
2✔
557
        }
2✔
558

559
        return nil
518✔
560
}
561

562
// CombineOption is a functional option argument that allows callers to modify the
563
// way we combine musig2 schnorr signatures.
564
type CombineOption func(*combineOptions)
565

566
// combineOptions houses the set of functional options that can be used to
567
// modify the method used to combine the musig2 partial signatures.
568
type combineOptions struct {
569
        msg [32]byte
570

571
        combinedKey *btcec.PublicKey
572

573
        tweakAcc *btcec.ModNScalar
574
}
575

576
// defaultCombineOptions returns the default set of signing operations.
577
func defaultCombineOptions() *combineOptions {
10✔
578
        return &combineOptions{}
10✔
579
}
10✔
580

581
// WithTweakedCombine is a functional option that allows callers to specify
582
// that the signature was produced using a tweaked aggregated public key. In
583
// order to properly aggregate the partial signatures, the caller must specify
584
// enough information to reconstruct the challenge, and also the final
585
// accumulated tweak value.
586
func WithTweakedCombine(msg [32]byte, keys []*btcec.PublicKey,
587
        tweaks []KeyTweakDesc, sort bool) CombineOption {
2✔
588

2✔
589
        return func(o *combineOptions) {
4✔
590
                combinedKey, _, tweakAcc, _ := AggregateKeys(
2✔
591
                        keys, sort, WithKeyTweaks(tweaks...),
2✔
592
                )
2✔
593

2✔
594
                o.msg = msg
2✔
595
                o.combinedKey = combinedKey.FinalKey
2✔
596
                o.tweakAcc = tweakAcc
2✔
597
        }
2✔
598
}
599

600
// WithTaprootTweakedCombine is similar to the WithTweakedCombine option, but
601
// assumes a BIP 341 context where the final tweaked key is to be used as the
602
// output key, where the internal key is the aggregated key pre-tweak.
603
//
604
// This option should be used over WithTweakedCombine when attempting to
605
// aggregate signatures for a top-level taproot keyspend, where the output key
606
// commits to a script root.
607
func WithTaprootTweakedCombine(msg [32]byte, keys []*btcec.PublicKey,
608
        scriptRoot []byte, sort bool) CombineOption {
1✔
609

1✔
610
        return func(o *combineOptions) {
2✔
611
                combinedKey, _, tweakAcc, _ := AggregateKeys(
1✔
612
                        keys, sort, WithTaprootKeyTweak(scriptRoot),
1✔
613
                )
1✔
614

1✔
615
                o.msg = msg
1✔
616
                o.combinedKey = combinedKey.FinalKey
1✔
617
                o.tweakAcc = tweakAcc
1✔
618
        }
1✔
619
}
620

621
// WithBip86TweakedCombine is similar to the WithTaprootTweakedCombine option,
622
// but assumes a BIP 341 + BIP 86 context where the final tweaked key is to be
623
// used as the output key, where the internal key is the aggregated key
624
// pre-tweak.
625
//
626
// This option should be used over WithTaprootTweakedCombine when attempting to
627
// aggregate signatures for a top-level taproot keyspend, where the output key
628
// was generated using BIP 86.
629
func WithBip86TweakedCombine(msg [32]byte, keys []*btcec.PublicKey,
630
        sort bool) CombineOption {
4✔
631

4✔
632
        return func(o *combineOptions) {
8✔
633
                combinedKey, _, tweakAcc, _ := AggregateKeys(
4✔
634
                        keys, sort, WithBIP86KeyTweak(),
4✔
635
                )
4✔
636

4✔
637
                o.msg = msg
4✔
638
                o.combinedKey = combinedKey.FinalKey
4✔
639
                o.tweakAcc = tweakAcc
4✔
640
        }
4✔
641
}
642

643
// CombineSigs combines the set of public keys given the final aggregated
644
// nonce, and the series of partial signatures for each nonce.
645
func CombineSigs(combinedNonce *btcec.PublicKey,
646
        partialSigs []*PartialSignature,
647
        combineOpts ...CombineOption) *schnorr.Signature {
10✔
648

10✔
649
        // First, parse the set of optional combine options.
10✔
650
        opts := defaultCombineOptions()
10✔
651
        for _, option := range combineOpts {
17✔
652
                option(opts)
7✔
653
        }
7✔
654

655
        // If signer keys and tweaks are specified, then we need to carry out
656
        // some intermediate steps before we can combine the signature.
657
        var tweakProduct *btcec.ModNScalar
10✔
658
        if opts.combinedKey != nil && opts.tweakAcc != nil {
17✔
659
                // Next, we'll construct the parity factor of the combined key,
7✔
660
                // negating it if the combined key has an even y coordinate.
7✔
661
                parityFactor := new(btcec.ModNScalar).SetInt(1)
7✔
662
                combinedKeyBytes := opts.combinedKey.SerializeCompressed()
7✔
663
                if combinedKeyBytes[0] == secp.PubKeyFormatCompressedOdd {
10✔
664
                        parityFactor.Negate()
3✔
665
                }
3✔
666

667
                // Next we'll reconstruct e the challenge has based on the
668
                // nonce and combined public key.
669
                //  * e = H(tag=ChallengeHashTag, R || Q || m) mod n
670
                var challengeMsg bytes.Buffer
7✔
671
                challengeMsg.Write(schnorr.SerializePubKey(combinedNonce))
7✔
672
                challengeMsg.Write(schnorr.SerializePubKey(opts.combinedKey))
7✔
673
                challengeMsg.Write(opts.msg[:])
7✔
674
                challengeBytes := chainhash.TaggedHash(
7✔
675
                        ChallengeHashTag, challengeMsg.Bytes(),
7✔
676
                )
7✔
677
                var e btcec.ModNScalar
7✔
678
                e.SetByteSlice(challengeBytes[:])
7✔
679

7✔
680
                tweakProduct = new(btcec.ModNScalar).Set(&e)
7✔
681
                tweakProduct.Mul(opts.tweakAcc).Mul(parityFactor)
7✔
682
        }
683

684
        // Finally, the tweak factor also needs to be re-computed as well.
685
        var combinedSig btcec.ModNScalar
10✔
686
        for _, partialSig := range partialSigs {
520✔
687
                combinedSig.Add(partialSig.S)
510✔
688
        }
510✔
689

690
        // If the tweak product was set above, then we'll need to add the value
691
        // at the very end in order to produce a valid signature under the
692
        // final tweaked key.
693
        if tweakProduct != nil {
17✔
694
                combinedSig.Add(tweakProduct)
7✔
695
        }
7✔
696

697
        // TODO(roasbeef): less verbose way to get the x coord...
698
        var nonceJ btcec.JacobianPoint
10✔
699
        combinedNonce.AsJacobian(&nonceJ)
10✔
700
        nonceJ.ToAffine()
10✔
701

10✔
702
        return schnorr.NewSignature(&nonceJ.X, &combinedSig)
10✔
703
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc