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

lightningnetwork / lnd / 13586005509

28 Feb 2025 10:14AM UTC coverage: 68.629% (+9.9%) from 58.77%
13586005509

Pull #9521

github

web-flow
Merge 37d3a70a5 into 8532955b3
Pull Request #9521: unit: remove GOACC, use Go 1.20 native coverage functionality

129950 of 189351 relevant lines covered (68.63%)

23726.46 hits per line

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

91.81
/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 {
513✔
57

513✔
58
        return PartialSignature{
513✔
59
                S: s,
513✔
60
                R: r,
513✔
61
        }
513✔
62
}
513✔
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,036✔
131
        return &signOptions{}
1,036✔
132
}
1,036✔
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.
145
func WithSortedKeys() SignOption {
3✔
146
        return func(o *signOptions) {
6✔
147
                o.sortKeys = true
3✔
148
        }
3✔
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 {
103✔
169
        return func(o *signOptions) {
306✔
170
                o.taprootTweak = scriptRoot
203✔
171
        }
203✔
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 {
403✔
183
        return func(o *signOptions) {
1,206✔
184
                o.bip86Tweak = true
803✔
185
        }
803✔
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) {
516✔
195

516✔
196
        // First, parse the set of optional signing options.
516✔
197
        opts := defaultSignOptions()
516✔
198
        for _, option := range signOpts {
1,023✔
199
                option(opts)
507✔
200
        }
507✔
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)
516✔
205
        uniqueKeyIndex := secondUniqueKeyIndex(pubKeys, opts.sortKeys)
516✔
206

516✔
207
        keyAggOpts := []KeyAggOption{
516✔
208
                WithKeysHash(keysHash), WithUniqueKeyIndex(uniqueKeyIndex),
516✔
209
        }
516✔
210
        switch {
516✔
211
        case opts.bip86Tweak:
403✔
212
                keyAggOpts = append(
403✔
213
                        keyAggOpts, WithBIP86KeyTweak(),
403✔
214
                )
403✔
215
        case opts.taprootTweak != nil:
103✔
216
                keyAggOpts = append(
103✔
217
                        keyAggOpts, WithTaprootKeyTweak(opts.taprootTweak),
103✔
218
                )
103✔
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(
516✔
226
                pubKeys, opts.sortKeys, keyAggOpts...,
516✔
227
        )
516✔
228
        if err != nil {
516✔
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 (
516✔
236
                nonceMsgBuf  bytes.Buffer
516✔
237
                nonceBlinder btcec.ModNScalar
516✔
238
        )
516✔
239
        nonceMsgBuf.Write(combinedNonce[:])
516✔
240
        nonceMsgBuf.Write(schnorr.SerializePubKey(combinedKey.FinalKey))
516✔
241
        nonceMsgBuf.Write(msg[:])
516✔
242
        nonceBlindHash := chainhash.TaggedHash(
516✔
243
                NonceBlindTag, nonceMsgBuf.Bytes(),
516✔
244
        )
516✔
245
        nonceBlinder.SetByteSlice(nonceBlindHash[:])
516✔
246

516✔
247
        // Next, we'll parse the public nonces into R1 and R2.
516✔
248
        r1J, err := btcec.ParseJacobian(
516✔
249
                combinedNonce[:btcec.PubKeyBytesLenCompressed],
516✔
250
        )
516✔
251
        if err != nil {
517✔
252
                return nil, err
1✔
253
        }
1✔
254
        r2J, err := btcec.ParseJacobian(
515✔
255
                combinedNonce[btcec.PubKeyBytesLenCompressed:],
515✔
256
        )
515✔
257
        if err != nil {
517✔
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
513✔
265
        btcec.ScalarMultNonConst(&nonceBlinder, &r2J, &r2J)
513✔
266
        btcec.AddNonConst(&r1J, &r2J, &nonce)
513✔
267

513✔
268
        // If the combined nonce it eh point at infinity, then we'll bail out.
513✔
269
        if nonce == infinityPoint {
514✔
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
513✔
277
        k1.SetByteSlice(secNonce[:btcec.PrivKeyBytesLen])
513✔
278
        k2.SetByteSlice(secNonce[btcec.PrivKeyBytesLen:])
513✔
279

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

284
        nonce.ToAffine()
513✔
285

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

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

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

300
        pubKey := privKey.PubKey()
513✔
301
        pubKeyYIsOdd := func() bool {
1,026✔
302
                pubKeyBytes := pubKey.SerializeCompressed()
513✔
303
                return pubKeyBytes[0] == secp.PubKeyFormatCompressedOdd
513✔
304
        }()
513✔
305
        combinedKeyYIsOdd := func() bool {
1,026✔
306
                combinedKeyBytes := combinedKey.FinalKey.SerializeCompressed()
513✔
307
                return combinedKeyBytes[0] == secp.PubKeyFormatCompressedOdd
513✔
308
        }()
513✔
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)
513✔
314
        paritySignKey := new(btcec.ModNScalar).SetInt(1)
513✔
315
        if combinedKeyYIsOdd {
920✔
316
                parityCombinedKey.Negate()
407✔
317
        }
407✔
318
        if pubKeyYIsOdd {
759✔
319
                paritySignKey.Negate()
246✔
320
        }
246✔
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)
513✔
326

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

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

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

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

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

364
        return &sig, nil
513✔
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 {
513✔
373

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

513✔
376
        return verifyPartialSig(
513✔
377
                p, pubNonce, combinedNonce, keySet, pubKey, msg, signOpts...,
513✔
378
        ) == nil
513✔
379
}
513✔
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 {
523✔
387

523✔
388
        opts := defaultSignOptions()
523✔
389
        for _, option := range signOpts {
1,034✔
390
                option(opts)
511✔
391
        }
511✔
392

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

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

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

523✔
406
        keyAggOpts := []KeyAggOption{
523✔
407
                WithKeysHash(keysHash), WithUniqueKeyIndex(uniqueKeyIndex),
523✔
408
        }
523✔
409
        switch {
523✔
410
        case opts.bip86Tweak:
403✔
411
                keyAggOpts = append(
403✔
412
                        keyAggOpts, WithBIP86KeyTweak(),
403✔
413
                )
403✔
414
        case opts.taprootTweak != nil:
103✔
415
                keyAggOpts = append(
103✔
416
                        keyAggOpts, WithTaprootKeyTweak(opts.taprootTweak),
103✔
417
                )
103✔
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(
523✔
425
                keySet, opts.sortKeys, keyAggOpts...,
523✔
426
        )
523✔
427
        if err != nil {
523✔
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 (
523✔
435
                nonceMsgBuf  bytes.Buffer
523✔
436
                nonceBlinder btcec.ModNScalar
523✔
437
        )
523✔
438
        nonceMsgBuf.Write(combinedNonce[:])
523✔
439
        nonceMsgBuf.Write(schnorr.SerializePubKey(combinedKey.FinalKey))
523✔
440
        nonceMsgBuf.Write(msg[:])
523✔
441
        nonceBlindHash := chainhash.TaggedHash(NonceBlindTag, nonceMsgBuf.Bytes())
523✔
442
        nonceBlinder.SetByteSlice(nonceBlindHash[:])
523✔
443

523✔
444
        r1J, err := btcec.ParseJacobian(
523✔
445
                combinedNonce[:btcec.PubKeyBytesLenCompressed],
523✔
446
        )
523✔
447
        if err != nil {
523✔
448
                return err
×
449
        }
×
450
        r2J, err := btcec.ParseJacobian(
523✔
451
                combinedNonce[btcec.PubKeyBytesLenCompressed:],
523✔
452
        )
523✔
453
        if err != nil {
523✔
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
523✔
462
        btcec.ScalarMultNonConst(&nonceBlinder, &r2J, &r2J)
523✔
463
        btcec.AddNonConst(&r1J, &r2J, &nonce)
523✔
464

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

480
        // If the nonce is the infinity point we set it to the Generator.
481
        if nonce == infinityPoint {
525✔
482
                btcec.GeneratorJacobian(&nonce)
2✔
483
        } else {
523✔
484
                nonce.ToAffine()
521✔
485
        }
521✔
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
523✔
490

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

523✔
494
        pubNonceJ.ToAffine()
523✔
495

523✔
496
        // If the combined nonce used in the challenge hash has an odd y
523✔
497
        // coordinate, then we'll negate our final public nonce.
523✔
498
        if nonce.Y.IsOdd() {
534✔
499
                pubNonceJ.Y.Negate(1)
11✔
500
                pubNonceJ.Y.Normalize()
11✔
501
        }
11✔
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
523✔
507
        challengeMsg.Write(schnorr.SerializePubKey(btcec.NewPublicKey(
523✔
508
                &nonce.X, &nonce.Y,
523✔
509
        )))
523✔
510
        challengeMsg.Write(schnorr.SerializePubKey(combinedKey.FinalKey))
523✔
511
        challengeMsg.Write(msg[:])
523✔
512
        challengeBytes := chainhash.TaggedHash(
523✔
513
                ChallengeHashTag, challengeMsg.Bytes(),
523✔
514
        )
523✔
515
        var e btcec.ModNScalar
523✔
516
        e.SetByteSlice(challengeBytes[:])
523✔
517

523✔
518
        signingKey, err := schnorr.ParsePubKey(pubKey)
523✔
519
        if err != nil {
523✔
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)
523✔
526

523✔
527
        // If the combined key has an odd y coordinate, then we'll negate
523✔
528
        // parity factor for the signing key.
523✔
529
        paritySignKey := new(btcec.ModNScalar).SetInt(1)
523✔
530
        combinedKeyBytes := combinedKey.FinalKey.SerializeCompressed()
523✔
531
        if combinedKeyBytes[0] == secp.PubKeyFormatCompressedOdd {
936✔
532
                paritySignKey.Negate()
413✔
533
        }
413✔
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)
523✔
539

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

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

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

523✔
555
        if sG != rP {
525✔
556
                return ErrPartialSigInvalid
2✔
557
        }
2✔
558

559
        return nil
521✔
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 {
13✔
578
        return &combineOptions{}
13✔
579
}
13✔
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 {
4✔
609

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

4✔
615
                o.msg = msg
4✔
616
                o.combinedKey = combinedKey.FinalKey
4✔
617
                o.tweakAcc = tweakAcc
4✔
618
        }
4✔
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 {
7✔
631

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

7✔
637
                o.msg = msg
7✔
638
                o.combinedKey = combinedKey.FinalKey
7✔
639
                o.tweakAcc = tweakAcc
7✔
640
        }
7✔
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 {
13✔
648

13✔
649
        // First, parse the set of optional combine options.
13✔
650
        opts := defaultCombineOptions()
13✔
651
        for _, option := range combineOpts {
23✔
652
                option(opts)
10✔
653
        }
10✔
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
13✔
658
        if opts.combinedKey != nil && opts.tweakAcc != nil {
23✔
659
                // Next, we'll construct the parity factor of the combined key,
10✔
660
                // negating it if the combined key has an even y coordinate.
10✔
661
                parityFactor := new(btcec.ModNScalar).SetInt(1)
10✔
662
                combinedKeyBytes := opts.combinedKey.SerializeCompressed()
10✔
663
                if combinedKeyBytes[0] == secp.PubKeyFormatCompressedOdd {
17✔
664
                        parityFactor.Negate()
7✔
665
                }
7✔
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
10✔
671
                challengeMsg.Write(schnorr.SerializePubKey(combinedNonce))
10✔
672
                challengeMsg.Write(schnorr.SerializePubKey(opts.combinedKey))
10✔
673
                challengeMsg.Write(opts.msg[:])
10✔
674
                challengeBytes := chainhash.TaggedHash(
10✔
675
                        ChallengeHashTag, challengeMsg.Bytes(),
10✔
676
                )
10✔
677
                var e btcec.ModNScalar
10✔
678
                e.SetByteSlice(challengeBytes[:])
10✔
679

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

684
        // Finally, the tweak factor also needs to be re-computed as well.
685
        var combinedSig btcec.ModNScalar
13✔
686
        for _, partialSig := range partialSigs {
526✔
687
                combinedSig.Add(partialSig.S)
513✔
688
        }
513✔
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 {
23✔
694
                combinedSig.Add(tweakProduct)
10✔
695
        }
10✔
696

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

13✔
702
        return schnorr.NewSignature(&nonceJ.X, &combinedSig)
13✔
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