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

lightningnetwork / lnd / 13211764208

08 Feb 2025 03:08AM UTC coverage: 49.288% (-9.5%) from 58.815%
13211764208

Pull #9489

github

calvinrzachman
itest: verify switchrpc server enforces send then track

We prevent the rpc server from allowing onion dispatches for
attempt IDs which have already been tracked by rpc clients.

This helps protect the client from leaking a duplicate onion
attempt. NOTE: This is not the only method for solving this
issue! The issue could be addressed via careful client side
programming which accounts for the uncertainty and async
nature of dispatching onions to a remote process via RPC.
This would require some lnd ChannelRouter changes for how
we intend to use these RPCs though.
Pull Request #9489: multi: add BuildOnion, SendOnion, and TrackOnion RPCs

474 of 990 new or added lines in 11 files covered. (47.88%)

27321 existing lines in 435 files now uncovered.

101192 of 205306 relevant lines covered (49.29%)

1.54 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