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

lightningnetwork / lnd / 15561477203

10 Jun 2025 01:54PM UTC coverage: 58.351% (-10.1%) from 68.487%
15561477203

Pull #9356

github

web-flow
Merge 6440b25db into c6d6d4c0b
Pull Request #9356: lnrpc: add incoming/outgoing channel ids filter to forwarding history request

33 of 36 new or added lines in 2 files covered. (91.67%)

28366 existing lines in 455 files now uncovered.

97715 of 167461 relevant lines covered (58.35%)

1.81 hits per line

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

82.41
/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 {
3✔
57

3✔
58
        return PartialSignature{
3✔
59
                S: s,
3✔
60
                R: r,
3✔
61
        }
3✔
62
}
3✔
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.
UNCOV
79
func (p *PartialSignature) Decode(r io.Reader) error {
×
UNCOV
80
        p.S = new(btcec.ModNScalar)
×
UNCOV
81

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

UNCOV
87
        overflows := p.S.SetBytes(&sBytes)
×
UNCOV
88
        if overflows == 1 {
×
UNCOV
89
                return ErrPartialSigInvalid
×
UNCOV
90
        }
×
91

UNCOV
92
        return nil
×
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 {
3✔
131
        return &signOptions{}
3✔
132
}
3✔
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.
UNCOV
153
func WithTweaks(tweaks ...KeyTweakDesc) SignOption {
×
UNCOV
154
        return func(o *signOptions) {
×
UNCOV
155
                o.tweaks = tweaks
×
UNCOV
156
        }
×
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 {
3✔
169
        return func(o *signOptions) {
6✔
170
                o.taprootTweak = scriptRoot
3✔
171
        }
3✔
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 {
3✔
183
        return func(o *signOptions) {
6✔
184
                o.bip86Tweak = true
3✔
185
        }
3✔
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) {
3✔
195

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

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

223
        // Next we'll construct the aggregated public key based on the set of
224
        // signers.
225
        combinedKey, parityAcc, _, err := AggregateKeys(
3✔
226
                pubKeys, opts.sortKeys, keyAggOpts...,
3✔
227
        )
3✔
228
        if err != nil {
3✔
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 (
3✔
236
                nonceMsgBuf  bytes.Buffer
3✔
237
                nonceBlinder btcec.ModNScalar
3✔
238
        )
3✔
239
        nonceMsgBuf.Write(combinedNonce[:])
3✔
240
        nonceMsgBuf.Write(schnorr.SerializePubKey(combinedKey.FinalKey))
3✔
241
        nonceMsgBuf.Write(msg[:])
3✔
242
        nonceBlindHash := chainhash.TaggedHash(
3✔
243
                NonceBlindTag, nonceMsgBuf.Bytes(),
3✔
244
        )
3✔
245
        nonceBlinder.SetByteSlice(nonceBlindHash[:])
3✔
246

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

3✔
268
        // If the combined nonce it eh point at infinity, then we'll bail out.
3✔
269
        if nonce == infinityPoint {
3✔
UNCOV
270
                G := btcec.Generator()
×
UNCOV
271
                G.AsJacobian(&nonce)
×
UNCOV
272
        }
×
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
3✔
277
        k1.SetByteSlice(secNonce[:btcec.PrivKeyBytesLen])
3✔
278
        k2.SetByteSlice(secNonce[btcec.PrivKeyBytesLen:])
3✔
279

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

284
        nonce.ToAffine()
3✔
285

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

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

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

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

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

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

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

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

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

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

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

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

3✔
388
        opts := defaultSignOptions()
3✔
389
        for _, option := range signOpts {
6✔
390
                option(opts)
3✔
391
        }
3✔
392

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

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

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

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

422
        // Next we'll construct the aggregated public key based on the set of
423
        // signers.
424
        combinedKey, parityAcc, _, err := AggregateKeys(
3✔
425
                keySet, opts.sortKeys, keyAggOpts...,
3✔
426
        )
3✔
427
        if err != nil {
3✔
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 (
3✔
435
                nonceMsgBuf  bytes.Buffer
3✔
436
                nonceBlinder btcec.ModNScalar
3✔
437
        )
3✔
438
        nonceMsgBuf.Write(combinedNonce[:])
3✔
439
        nonceMsgBuf.Write(schnorr.SerializePubKey(combinedKey.FinalKey))
3✔
440
        nonceMsgBuf.Write(msg[:])
3✔
441
        nonceBlindHash := chainhash.TaggedHash(NonceBlindTag, nonceMsgBuf.Bytes())
3✔
442
        nonceBlinder.SetByteSlice(nonceBlindHash[:])
3✔
443

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

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

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

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

3✔
494
        pubNonceJ.ToAffine()
3✔
495

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

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

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

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

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

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

3✔
555
        if sG != rP {
3✔
UNCOV
556
                return ErrPartialSigInvalid
×
UNCOV
557
        }
×
558

559
        return nil
3✔
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 {
3✔
578
        return &combineOptions{}
3✔
579
}
3✔
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,
UNCOV
587
        tweaks []KeyTweakDesc, sort bool) CombineOption {
×
UNCOV
588

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

×
UNCOV
594
                o.msg = msg
×
UNCOV
595
                o.combinedKey = combinedKey.FinalKey
×
UNCOV
596
                o.tweakAcc = tweakAcc
×
UNCOV
597
        }
×
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 {
3✔
609

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

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

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

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

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

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

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

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

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