1 // Copyright 2013-2022 The btcsuite developers
2 3 package musig2
4 5 import (
6 "bytes"
7 "fmt"
8 "io"
9 10 "next.orly.dev/pkg/nostr/crypto/ec"
11 "next.orly.dev/pkg/nostr/crypto/ec/chainhash"
12 "next.orly.dev/pkg/nostr/crypto/ec/schnorr"
13 "next.orly.dev/pkg/nostr/crypto/ec/secp256k1"
14 "next.orly.dev/pkg/nostr/utils"
15 "next.orly.dev/pkg/lol/chk"
16 )
17 18 var (
19 // NonceBlindTag is that tag used to construct the value b, which
20 // blinds the second public nonce of each party.
21 NonceBlindTag = []byte("MuSig/noncecoef")
22 23 // ChallengeHashTag is the tag used to construct the challenge hash
24 ChallengeHashTag = []byte("BIP0340/challenge")
25 26 // ErrNoncePointAtInfinity is returned if during signing, the fully
27 // combined public nonce is the point at infinity.
28 ErrNoncePointAtInfinity = fmt.Errorf(
29 "signing nonce is the infinity " +
30 "point",
31 )
32 33 // ErrSecKeyZero is returned when the secret key for signing is
34 // actually zero.
35 ErrSecKeyZero = fmt.Errorf("priv key is zero")
36 37 // ErrPartialSigInvalid is returned when a partial is found to be
38 // invalid.
39 ErrPartialSigInvalid = fmt.Errorf("partial signature is invalid")
40 41 // ErrSecretNonceZero is returned when a secret nonce is passed in a
42 // zero.
43 ErrSecretNonceZero = fmt.Errorf("secret nonce is blank")
44 45 // ErrSecNoncePubkey is returned when the signing key does not match the
46 // sec nonce pubkey
47 ErrSecNoncePubkey = fmt.Errorf("public key does not match secnonce")
48 49 // ErrPubkeyNotIncluded is returned when the signers pubkey is not included
50 // in the list of pubkeys.
51 ErrPubkeyNotIncluded = fmt.Errorf(
52 "signer's pubkey must be included" +
53 " in the list of pubkeys",
54 )
55 )
56 57 // infinityPoint is the jacobian representation of the point at infinity.
58 var infinityPoint btcec.JacobianPoint
59 60 // PartialSignature reprints a partial (s-only) musig2 multi-signature. This
61 // isn't a valid schnorr signature by itself, as it needs to be aggregated
62 // along with the other partial signatures to be completed.
63 type PartialSignature struct {
64 S *btcec.ModNScalar
65 66 R *btcec.PublicKey
67 }
68 69 // NewPartialSignature returns a new instances of the partial sig struct.
70 func NewPartialSignature(
71 s *btcec.ModNScalar,
72 r *btcec.PublicKey,
73 ) PartialSignature {
74 75 return PartialSignature{
76 S: s,
77 R: r,
78 }
79 }
80 81 // Encode writes a serialized version of the partial signature to the passed
82 // io.Writer
83 func (p *PartialSignature) Encode(w io.Writer) error {
84 var sBytes [32]byte
85 p.S.PutBytes(&sBytes)
86 87 if _, err := w.Write(sBytes[:]); chk.T(err) {
88 return err
89 }
90 91 return nil
92 }
93 94 // Decode attempts to parse a serialized PartialSignature stored in the io reader.
95 func (p *PartialSignature) Decode(r io.Reader) error {
96 p.S = new(btcec.ModNScalar)
97 98 var sBytes [32]byte
99 if _, err := io.ReadFull(r, sBytes[:]); chk.T(err) {
100 return nil
101 }
102 103 overflows := p.S.SetBytes(&sBytes)
104 if overflows == 1 {
105 return ErrPartialSigInvalid
106 }
107 108 return nil
109 }
110 111 // SignOption is a functional option argument that allows callers to modify the
112 // way we generate musig2 schnorr signatures.
113 type SignOption func(*signOptions)
114 115 // signOptions houses the set of functional options that can be used to modify
116 // the method used to generate the musig2 partial signature.
117 type signOptions struct {
118 // fastSign determines if we'll skip the check at the end of the
119 // routine where we attempt to verify the produced signature.
120 fastSign bool
121 122 // sortKeys determines if the set of keys should be sorted before doing
123 // key aggregation.
124 sortKeys bool
125 126 // tweaks specifies a series of tweaks to be applied to the aggregated
127 // public key, which also partially carries over into the signing
128 // process.
129 tweaks []KeyTweakDesc
130 131 // taprootTweak specifies a taproot specific tweak. of the tweaks
132 // specified above. Normally we'd just apply the raw 32 byte tweak, but
133 // for taproot, we first need to compute the aggregated key before
134 // tweaking, and then use it as the internal key. This is required as
135 // the taproot tweak also commits to the public key, which in this case
136 // is the aggregated key before the tweak.
137 taprootTweak []byte
138 139 // bip86Tweak specifies that the taproot tweak should be done in a BIP
140 // 86 style, where we don't expect an actual tweak and instead just
141 // commit to the public key itself.
142 bip86Tweak bool
143 }
144 145 // defaultSignOptions returns the default set of signing operations.
146 func defaultSignOptions() *signOptions {
147 return &signOptions{}
148 }
149 150 // WithFastSign forces signing to skip the extra verification step at the end.
151 // Performance sensitive applications may opt to use this option to speed up
152 // the signing operation.
153 func WithFastSign() SignOption {
154 return func(o *signOptions) {
155 o.fastSign = true
156 }
157 }
158 159 // WithSortedKeys determines if the set of signing public keys are to be sorted
160 // or not before doing key aggregation.
161 func WithSortedKeys() SignOption {
162 return func(o *signOptions) {
163 o.sortKeys = true
164 }
165 }
166 167 // WithTweaks determines if the aggregated public key used should apply a
168 // series of tweaks before key aggregation.
169 func WithTweaks(tweaks ...KeyTweakDesc) SignOption {
170 return func(o *signOptions) {
171 o.tweaks = tweaks
172 }
173 }
174 175 // WithTaprootSignTweak allows a caller to specify a tweak that should be used
176 // in a bip 340 manner when signing. This differs from WithTweaks as the tweak
177 // will be assumed to always be x-only and the intermediate aggregate key
178 // before tweaking will be used to generate part of the tweak (as the taproot
179 // tweak also commits to the internal key).
180 //
181 // This option should be used in the taproot context to create a valid
182 // signature for the keypath spend for taproot, when the output key is actually
183 // committing to a script path, or some other data.
184 func WithTaprootSignTweak(scriptRoot []byte) SignOption {
185 return func(o *signOptions) {
186 o.taprootTweak = scriptRoot
187 }
188 }
189 190 // WithBip86SignTweak allows a caller to specify a tweak that should be used in
191 // a bip 340 manner when signing, factoring in BIP 86 as well. This differs
192 // from WithTaprootSignTweak as no true script root will be committed to,
193 // instead we just commit to the internal key.
194 //
195 // This option should be used in the taproot context to create a valid
196 // signature for the keypath spend for taproot, when the output key was
197 // generated using BIP 86.
198 func WithBip86SignTweak() SignOption {
199 return func(o *signOptions) {
200 o.bip86Tweak = true
201 }
202 }
203 204 // computeSigningNonce calculates the final nonce used for signing. This will
205 // be the R value used in the final signature.
206 func computeSigningNonce(
207 combinedNonce [PubNonceSize]byte,
208 combinedKey *btcec.PublicKey, msg [32]byte,
209 ) (
210 *btcec.JacobianPoint, *btcec.ModNScalar, error,
211 ) {
212 213 // Next we'll compute the value b, that blinds our second public
214 // nonce:
215 // * b = h(tag=NonceBlindTag, combinedNonce || combinedKey || m).
216 var (
217 nonceMsgBuf bytes.Buffer
218 nonceBlinder btcec.ModNScalar
219 )
220 nonceMsgBuf.Write(combinedNonce[:])
221 nonceMsgBuf.Write(schnorr.SerializePubKey(combinedKey))
222 nonceMsgBuf.Write(msg[:])
223 nonceBlindHash := chainhash.TaggedHash(
224 NonceBlindTag, nonceMsgBuf.Bytes(),
225 )
226 nonceBlinder.SetByteSlice(nonceBlindHash[:])
227 228 // Next, we'll parse the public nonces into R1 and R2.
229 r1J, err := btcec.ParseJacobian(
230 combinedNonce[:btcec.PubKeyBytesLenCompressed],
231 )
232 if err != nil {
233 return nil, nil, err
234 }
235 r2J, err := btcec.ParseJacobian(
236 combinedNonce[btcec.PubKeyBytesLenCompressed:],
237 )
238 if err != nil {
239 return nil, nil, err
240 }
241 242 // With our nonce blinding value, we'll now combine both the public
243 // nonces, using the blinding factor to tweak the second nonce:
244 // * R = R_1 + b*R_2
245 var nonce btcec.JacobianPoint
246 btcec.ScalarMultNonConst(&nonceBlinder, &r2J, &r2J)
247 btcec.AddNonConst(&r1J, &r2J, &nonce)
248 249 // If the combined nonce is the point at infinity, we'll use the
250 // generator point instead.
251 if nonce == infinityPoint {
252 G := btcec.Generator()
253 G.AsJacobian(&nonce)
254 }
255 256 return &nonce, &nonceBlinder, nil
257 }
258 259 // Sign generates a musig2 partial signature given the passed key set, secret
260 // nonce, public nonce, and secret keys. This method returns an error if the
261 // generated nonces are either too large, or end up mapping to the point at
262 // infinity.
263 func Sign(
264 secNonce [SecNonceSize]byte, privKey *btcec.SecretKey,
265 combinedNonce [PubNonceSize]byte, pubKeys []*btcec.PublicKey,
266 msg [32]byte, signOpts ...SignOption,
267 ) (*PartialSignature, error) {
268 269 // First, parse the set of optional signing options.
270 opts := defaultSignOptions()
271 for _, option := range signOpts {
272 option(opts)
273 }
274 275 // Check that our signing key belongs to the secNonce
276 if !utils.FastEqual(
277 secNonce[btcec.SecKeyBytesLen*2:],
278 privKey.PubKey().SerializeCompressed(),
279 ) {
280 281 return nil, ErrSecNoncePubkey
282 }
283 284 // Check that the key set contains the public key to our secret key.
285 var containsSecKey bool
286 for _, pk := range pubKeys {
287 if privKey.PubKey().IsEqual(pk) {
288 containsSecKey = true
289 }
290 }
291 292 if !containsSecKey {
293 return nil, ErrPubkeyNotIncluded
294 }
295 296 // Compute the hash of all the keys here as we'll need it do aggregate
297 // the keys and also at the final step of signing.
298 keysHash := keyHashFingerprint(pubKeys, opts.sortKeys)
299 uniqueKeyIndex := secondUniqueKeyIndex(pubKeys, opts.sortKeys)
300 301 keyAggOpts := []KeyAggOption{
302 WithKeysHash(keysHash), WithUniqueKeyIndex(uniqueKeyIndex),
303 }
304 switch {
305 case opts.bip86Tweak:
306 keyAggOpts = append(
307 keyAggOpts, WithBIP86KeyTweak(),
308 )
309 case opts.taprootTweak != nil:
310 keyAggOpts = append(
311 keyAggOpts, WithTaprootKeyTweak(opts.taprootTweak),
312 )
313 case len(opts.tweaks) != 0:
314 keyAggOpts = append(keyAggOpts, WithKeyTweaks(opts.tweaks...))
315 }
316 317 // Next we'll construct the aggregated public key based on the set of
318 // signers.
319 combinedKey, parityAcc, _, err := AggregateKeys(
320 pubKeys, opts.sortKeys, keyAggOpts...,
321 )
322 if err != nil {
323 return nil, err
324 }
325 326 // We'll now combine both the public nonces, using the blinding factor
327 // to tweak the second nonce:
328 // * R = R_1 + b*R_2
329 nonce, nonceBlinder, err := computeSigningNonce(
330 combinedNonce, combinedKey.FinalKey, msg,
331 )
332 if err != nil {
333 return nil, err
334 }
335 336 // Next we'll parse out our two secret nonces, which we'll be using in
337 // the core signing process below.
338 var k1, k2 btcec.ModNScalar
339 k1.SetByteSlice(secNonce[:btcec.SecKeyBytesLen])
340 k2.SetByteSlice(secNonce[btcec.SecKeyBytesLen:])
341 342 if k1.IsZero() || k2.IsZero() {
343 return nil, ErrSecretNonceZero
344 }
345 346 nonce.ToAffine()
347 348 nonceKey := btcec.NewPublicKey(&nonce.X, &nonce.Y)
349 350 // If the nonce R has an odd y coordinate, then we'll negate both our
351 // secret nonces.
352 if nonce.Y.IsOdd() {
353 k1.Negate()
354 k2.Negate()
355 }
356 357 privKeyScalar := privKey.Key
358 if privKeyScalar.IsZero() {
359 return nil, ErrSecKeyZero
360 }
361 362 pubKey := privKey.PubKey()
363 combinedKeyYIsOdd := func() bool {
364 combinedKeyBytes := combinedKey.FinalKey.SerializeCompressed()
365 return combinedKeyBytes[0] == secp256k1.PubKeyFormatCompressedOdd
366 }()
367 368 // Next we'll compute the two parity factors for Q, the combined key.
369 // If the key is odd, then we'll negate it.
370 parityCombinedKey := new(btcec.ModNScalar).SetInt(1)
371 if combinedKeyYIsOdd {
372 parityCombinedKey.Negate()
373 }
374 375 // Before we sign below, we'll multiply by our various parity factors
376 // to ensure that the signing key is properly negated (if necessary):
377 // * d = g⋅gacc⋅d'
378 privKeyScalar.Mul(parityCombinedKey).Mul(parityAcc)
379 380 // Next we'll create the challenge hash that commits to the combined
381 // nonce, combined public key and also the message:
382 // * e = H(tag=ChallengeHashTag, R || Q || m) mod n
383 var challengeMsg bytes.Buffer
384 challengeMsg.Write(schnorr.SerializePubKey(nonceKey))
385 challengeMsg.Write(schnorr.SerializePubKey(combinedKey.FinalKey))
386 challengeMsg.Write(msg[:])
387 challengeBytes := chainhash.TaggedHash(
388 ChallengeHashTag, challengeMsg.Bytes(),
389 )
390 var e btcec.ModNScalar
391 e.SetByteSlice(challengeBytes[:])
392 393 // Next, we'll compute a, our aggregation coefficient for the key that
394 // we're signing with.
395 a := aggregationCoefficient(pubKeys, pubKey, keysHash, uniqueKeyIndex)
396 397 // With mu constructed, we can finally generate our partial signature
398 // as: s = (k1_1 + b*k_2 + e*a*d) mod n.
399 s := new(btcec.ModNScalar)
400 s.Add(&k1).Add(k2.Mul(nonceBlinder)).Add(e.Mul(a).Mul(&privKeyScalar))
401 402 sig := NewPartialSignature(s, nonceKey)
403 404 // If we're not in fast sign mode, then we'll also validate our partial
405 // signature.
406 if !opts.fastSign {
407 pubNonce := secNonceToPubNonce(secNonce)
408 sigValid := sig.Verify(
409 pubNonce, combinedNonce, pubKeys, pubKey, msg,
410 signOpts...,
411 )
412 if !sigValid {
413 return nil, fmt.Errorf("sig is invalid!")
414 }
415 }
416 417 return &sig, nil
418 }
419 420 // Verify implements partial signature verification given the public nonce for
421 // the signer, aggregate nonce, signer set and finally the message being
422 // signed.
423 func (p *PartialSignature) Verify(
424 pubNonce [PubNonceSize]byte,
425 combinedNonce [PubNonceSize]byte, keySet []*btcec.PublicKey,
426 signingKey *btcec.PublicKey, msg [32]byte, signOpts ...SignOption,
427 ) bool {
428 429 pubKey := signingKey.SerializeCompressed()
430 431 return verifyPartialSig(
432 p, pubNonce, combinedNonce, keySet, pubKey, msg, signOpts...,
433 ) == nil
434 }
435 436 // verifyPartialSig attempts to verify a partial schnorr signature given the
437 // necessary parameters. This is the internal version of Verify that returns
438 // detailed errors. signed.
439 func verifyPartialSig(
440 partialSig *PartialSignature, pubNonce [PubNonceSize]byte,
441 combinedNonce [PubNonceSize]byte, keySet []*btcec.PublicKey,
442 pubKey []byte, msg [32]byte, signOpts ...SignOption,
443 ) error {
444 445 opts := defaultSignOptions()
446 for _, option := range signOpts {
447 option(opts)
448 }
449 450 // First we'll map the internal partial signature back into something
451 // we can manipulate.
452 s := partialSig.S
453 454 // Next we'll parse out the two public nonces into something we can
455 // use.
456 //
457 // Compute the hash of all the keys here as we'll need it do aggregate
458 // the keys and also at the final step of verification.
459 keysHash := keyHashFingerprint(keySet, opts.sortKeys)
460 uniqueKeyIndex := secondUniqueKeyIndex(keySet, opts.sortKeys)
461 462 keyAggOpts := []KeyAggOption{
463 WithKeysHash(keysHash), WithUniqueKeyIndex(uniqueKeyIndex),
464 }
465 switch {
466 case opts.bip86Tweak:
467 keyAggOpts = append(
468 keyAggOpts, WithBIP86KeyTweak(),
469 )
470 case opts.taprootTweak != nil:
471 keyAggOpts = append(
472 keyAggOpts, WithTaprootKeyTweak(opts.taprootTweak),
473 )
474 case len(opts.tweaks) != 0:
475 keyAggOpts = append(keyAggOpts, WithKeyTweaks(opts.tweaks...))
476 }
477 478 // Next we'll construct the aggregated public key based on the set of
479 // signers.
480 combinedKey, parityAcc, _, err := AggregateKeys(
481 keySet, opts.sortKeys, keyAggOpts...,
482 )
483 if err != nil {
484 return err
485 }
486 487 // Next we'll compute the value b, that blinds our second public
488 // nonce:
489 // * b = h(tag=NonceBlindTag, combinedNonce || combinedKey || m).
490 var (
491 nonceMsgBuf bytes.Buffer
492 nonceBlinder btcec.ModNScalar
493 )
494 nonceMsgBuf.Write(combinedNonce[:])
495 nonceMsgBuf.Write(schnorr.SerializePubKey(combinedKey.FinalKey))
496 nonceMsgBuf.Write(msg[:])
497 nonceBlindHash := chainhash.TaggedHash(NonceBlindTag, nonceMsgBuf.Bytes())
498 nonceBlinder.SetByteSlice(nonceBlindHash[:])
499 500 r1J, err := btcec.ParseJacobian(
501 combinedNonce[:btcec.PubKeyBytesLenCompressed],
502 )
503 if err != nil {
504 return err
505 }
506 r2J, err := btcec.ParseJacobian(
507 combinedNonce[btcec.PubKeyBytesLenCompressed:],
508 )
509 if err != nil {
510 return err
511 }
512 513 // With our nonce blinding value, we'll now combine both the public
514 // nonces, using the blinding factor to tweak the second nonce:
515 // * R = R_1 + b*R_2
516 var nonce btcec.JacobianPoint
517 btcec.ScalarMultNonConst(&nonceBlinder, &r2J, &r2J)
518 btcec.AddNonConst(&r1J, &r2J, &nonce)
519 520 // Next, we'll parse out the set of public nonces this signer used to
521 // generate the signature.
522 pubNonce1J, err := btcec.ParseJacobian(
523 pubNonce[:btcec.PubKeyBytesLenCompressed],
524 )
525 if err != nil {
526 return err
527 }
528 pubNonce2J, err := btcec.ParseJacobian(
529 pubNonce[btcec.PubKeyBytesLenCompressed:],
530 )
531 if err != nil {
532 return err
533 }
534 535 // If the nonce is the infinity point we set it to the Generator.
536 if nonce == infinityPoint {
537 btcec.GeneratorJacobian(&nonce)
538 } else {
539 nonce.ToAffine()
540 }
541 542 // We'll perform a similar aggregation and blinding operator as we did
543 // above for the combined nonces: R' = R_1' + b*R_2'.
544 var pubNonceJ btcec.JacobianPoint
545 546 btcec.ScalarMultNonConst(&nonceBlinder, &pubNonce2J, &pubNonce2J)
547 btcec.AddNonConst(&pubNonce1J, &pubNonce2J, &pubNonceJ)
548 549 pubNonceJ.ToAffine()
550 551 // If the combined nonce used in the challenge hash has an odd y
552 // coordinate, then we'll negate our final public nonce.
553 if nonce.Y.IsOdd() {
554 pubNonceJ.Y.Negate(1)
555 pubNonceJ.Y.Normalize()
556 }
557 558 // Next we'll create the challenge hash that commits to the combined
559 // nonce, combined public key and also the message:
560 // * e = H(tag=ChallengeHashTag, R || Q || m) mod n
561 var challengeMsg bytes.Buffer
562 challengeMsg.Write(
563 schnorr.SerializePubKey(
564 btcec.NewPublicKey(
565 &nonce.X, &nonce.Y,
566 ),
567 ),
568 )
569 challengeMsg.Write(schnorr.SerializePubKey(combinedKey.FinalKey))
570 challengeMsg.Write(msg[:])
571 challengeBytes := chainhash.TaggedHash(
572 ChallengeHashTag, challengeMsg.Bytes(),
573 )
574 var e btcec.ModNScalar
575 e.SetByteSlice(challengeBytes[:])
576 577 signingKey, err := btcec.ParsePubKey(pubKey)
578 if err != nil {
579 return err
580 }
581 582 // Next, we'll compute a, our aggregation coefficient for the key that
583 // we're signing with.
584 a := aggregationCoefficient(keySet, signingKey, keysHash, uniqueKeyIndex)
585 586 // If the combined key has an odd y coordinate, then we'll negate
587 // parity factor for the signing key.
588 parityCombinedKey := new(btcec.ModNScalar).SetInt(1)
589 combinedKeyBytes := combinedKey.FinalKey.SerializeCompressed()
590 if combinedKeyBytes[0] == secp256k1.PubKeyFormatCompressedOdd {
591 parityCombinedKey.Negate()
592 }
593 594 // Next, we'll construct the final parity factor by multiplying the
595 // sign key parity factor with the accumulated parity factor for all
596 // the keys.
597 finalParityFactor := parityCombinedKey.Mul(parityAcc)
598 599 var signKeyJ btcec.JacobianPoint
600 signingKey.AsJacobian(&signKeyJ)
601 602 // In the final set, we'll check that: s*G == R' + e*a*g*P.
603 var sG, rP btcec.JacobianPoint
604 btcec.ScalarBaseMultNonConst(s, &sG)
605 btcec.ScalarMultNonConst(e.Mul(a).Mul(finalParityFactor), &signKeyJ, &rP)
606 btcec.AddNonConst(&rP, &pubNonceJ, &rP)
607 608 sG.ToAffine()
609 rP.ToAffine()
610 611 if sG != rP {
612 return ErrPartialSigInvalid
613 }
614 615 return nil
616 }
617 618 // CombineOption is a functional option argument that allows callers to modify the
619 // way we combine musig2 schnorr signatures.
620 type CombineOption func(*combineOptions)
621 622 // combineOptions houses the set of functional options that can be used to
623 // modify the method used to combine the musig2 partial signatures.
624 type combineOptions struct {
625 msg [32]byte
626 627 combinedKey *btcec.PublicKey
628 629 tweakAcc *btcec.ModNScalar
630 }
631 632 // defaultCombineOptions returns the default set of signing operations.
633 func defaultCombineOptions() *combineOptions {
634 return &combineOptions{}
635 }
636 637 // WithTweakedCombine is a functional option that allows callers to specify
638 // that the signature was produced using a tweaked aggregated public key. In
639 // order to properly aggregate the partial signatures, the caller must specify
640 // enough information to reconstruct the challenge, and also the final
641 // accumulated tweak value.
642 func WithTweakedCombine(
643 msg [32]byte, keys []*btcec.PublicKey,
644 tweaks []KeyTweakDesc, sort bool,
645 ) CombineOption {
646 647 return func(o *combineOptions) {
648 combinedKey, _, tweakAcc, _ := AggregateKeys(
649 keys, sort, WithKeyTweaks(tweaks...),
650 )
651 652 o.msg = msg
653 o.combinedKey = combinedKey.FinalKey
654 o.tweakAcc = tweakAcc
655 }
656 }
657 658 // WithTaprootTweakedCombine is similar to the WithTweakedCombine option, but
659 // assumes a BIP 341 context where the final tweaked key is to be used as the
660 // output key, where the internal key is the aggregated key pre-tweak.
661 //
662 // This option should be used over WithTweakedCombine when attempting to
663 // aggregate signatures for a top-level taproot keyspend, where the output key
664 // commits to a script root.
665 func WithTaprootTweakedCombine(
666 msg [32]byte, keys []*btcec.PublicKey,
667 scriptRoot []byte, sort bool,
668 ) CombineOption {
669 670 return func(o *combineOptions) {
671 combinedKey, _, tweakAcc, _ := AggregateKeys(
672 keys, sort, WithTaprootKeyTweak(scriptRoot),
673 )
674 675 o.msg = msg
676 o.combinedKey = combinedKey.FinalKey
677 o.tweakAcc = tweakAcc
678 }
679 }
680 681 // WithBip86TweakedCombine is similar to the WithTaprootTweakedCombine option,
682 // but assumes a BIP 341 + BIP 86 context where the final tweaked key is to be
683 // used as the output key, where the internal key is the aggregated key
684 // pre-tweak.
685 //
686 // This option should be used over WithTaprootTweakedCombine when attempting to
687 // aggregate signatures for a top-level taproot keyspend, where the output key
688 // was generated using BIP 86.
689 func WithBip86TweakedCombine(
690 msg [32]byte, keys []*btcec.PublicKey,
691 sort bool,
692 ) CombineOption {
693 694 return func(o *combineOptions) {
695 combinedKey, _, tweakAcc, _ := AggregateKeys(
696 keys, sort, WithBIP86KeyTweak(),
697 )
698 699 o.msg = msg
700 o.combinedKey = combinedKey.FinalKey
701 o.tweakAcc = tweakAcc
702 }
703 }
704 705 // CombineSigs combines the set of public keys given the final aggregated
706 // nonce, and the series of partial signatures for each nonce.
707 func CombineSigs(
708 combinedNonce *btcec.PublicKey,
709 partialSigs []*PartialSignature,
710 combineOpts ...CombineOption,
711 ) *schnorr.Signature {
712 713 // First, parse the set of optional combine options.
714 opts := defaultCombineOptions()
715 for _, option := range combineOpts {
716 option(opts)
717 }
718 719 // If signer keys and tweaks are specified, then we need to carry out
720 // some intermediate steps before we can combine the signature.
721 var tweakProduct *btcec.ModNScalar
722 if opts.combinedKey != nil && opts.tweakAcc != nil {
723 // Next, we'll construct the parity factor of the combined key,
724 // negating it if the combined key has an even y coordinate.
725 parityFactor := new(btcec.ModNScalar).SetInt(1)
726 combinedKeyBytes := opts.combinedKey.SerializeCompressed()
727 if combinedKeyBytes[0] == secp256k1.PubKeyFormatCompressedOdd {
728 parityFactor.Negate()
729 }
730 731 // Next we'll reconstruct e the challenge has based on the
732 // nonce and combined public key.
733 // * e = H(tag=ChallengeHashTag, R || Q || m) mod n
734 var challengeMsg bytes.Buffer
735 challengeMsg.Write(schnorr.SerializePubKey(combinedNonce))
736 challengeMsg.Write(schnorr.SerializePubKey(opts.combinedKey))
737 challengeMsg.Write(opts.msg[:])
738 challengeBytes := chainhash.TaggedHash(
739 ChallengeHashTag, challengeMsg.Bytes(),
740 )
741 var e btcec.ModNScalar
742 e.SetByteSlice(challengeBytes[:])
743 744 tweakProduct = new(btcec.ModNScalar).Set(&e)
745 tweakProduct.Mul(opts.tweakAcc).Mul(parityFactor)
746 }
747 748 // Finally, the tweak factor also needs to be re-computed as well.
749 var combinedSig btcec.ModNScalar
750 for _, partialSig := range partialSigs {
751 combinedSig.Add(partialSig.S)
752 }
753 754 // If the tweak product was set above, then we'll need to add the value
755 // at the very end in order to produce a valid signature under the
756 // final tweaked key.
757 if tweakProduct != nil {
758 combinedSig.Add(tweakProduct)
759 }
760 761 // TODO(roasbeef): less verbose way to get the x coord...
762 var nonceJ btcec.JacobianPoint
763 combinedNonce.AsJacobian(&nonceJ)
764 nonceJ.ToAffine()
765 766 return schnorr.NewSignature(&nonceJ.X, &combinedSig)
767 }
768