mod+internal: copy MuSig2 v0.4.0 code from btcd/btcec/v2

With this commit we copy the exact code of the MuSig2 code as found in
github.com/btcsuite/btcec/v2/schnorr/musig2 at the tag btcec/v2.2.2.
This corresponds to the MuSig2 BIP specification version of v0.4.0.
This commit is contained in:
Oliver Gugger 2023-01-27 16:13:10 +01:00
parent ff8f1371b5
commit 3012f5e17d
No known key found for this signature in database
GPG Key ID: 8E4256593F177720
8 changed files with 4435 additions and 2 deletions

View File

@ -2,11 +2,13 @@ run:
# timeout for analysis
deadline: 10m
# Skip autogenerated files for mobile and gRPC.
# Skip autogenerated files for mobile and gRPC as well as copied code for
# internal use.
skip-files:
- "mobile\\/.*generated\\.go"
- "\\.pb\\.go$"
- "\\.pb\\.gw\\.go$"
- "internal\\/musig2v040"
skip-dirs:
- channeldb/migration_01_to_11

2
go.mod
View File

@ -16,6 +16,7 @@ require (
github.com/btcsuite/btcwallet/wtxmgr v1.5.0
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f
github.com/davecgh/go-spew v1.1.1
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1
github.com/go-errors/errors v1.0.1
github.com/golang/protobuf v1.5.2
github.com/gorilla/websocket v1.4.2
@ -76,7 +77,6 @@ require (
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/decred/dcrd/lru v1.0.0 // indirect
github.com/dsnet/compress v0.0.1 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect

View File

@ -0,0 +1,313 @@
// Copyright 2013-2022 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package musig2
import (
"encoding/hex"
"fmt"
"testing"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
)
var (
testPrivBytes = hexToModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d")
testMsg = hexToBytes("c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7")
)
func hexToBytes(s string) []byte {
b, err := hex.DecodeString(s)
if err != nil {
panic("invalid hex in source file: " + s)
}
return b
}
func hexToModNScalar(s string) *btcec.ModNScalar {
b, err := hex.DecodeString(s)
if err != nil {
panic("invalid hex in source file: " + s)
}
var scalar btcec.ModNScalar
if overflow := scalar.SetByteSlice(b); overflow {
panic("hex in source file overflows mod N scalar: " + s)
}
return &scalar
}
func genSigner(t *testing.B) signer {
privKey, err := btcec.NewPrivateKey()
if err != nil {
t.Fatalf("unable to gen priv key: %v", err)
}
pubKey, err := schnorr.ParsePubKey(
schnorr.SerializePubKey(privKey.PubKey()),
)
if err != nil {
t.Fatalf("unable to gen key: %v", err)
}
nonces, err := GenNonces()
if err != nil {
t.Fatalf("unable to gen nonces: %v", err)
}
return signer{
privKey: privKey,
pubKey: pubKey,
nonces: nonces,
}
}
var (
testSig *PartialSignature
testErr error
)
// BenchmarkPartialSign benchmarks how long it takes to generate a partial
// signature factoring in if the keys are sorted and also if we're in fast sign
// mode.
func BenchmarkPartialSign(b *testing.B) {
for _, numSigners := range []int{10, 100} {
for _, fastSign := range []bool{true, false} {
for _, sortKeys := range []bool{true, false} {
name := fmt.Sprintf("num_signers=%v/fast_sign=%v/sort=%v",
numSigners, fastSign, sortKeys)
signers := make(signerSet, numSigners)
for i := 0; i < numSigners; i++ {
signers[i] = genSigner(b)
}
combinedNonce, err := AggregateNonces(signers.pubNonces())
if err != nil {
b.Fatalf("unable to generate combined nonce: %v", err)
}
var sig *PartialSignature
var msg [32]byte
copy(msg[:], testMsg[:])
keys := signers.keys()
b.Run(name, func(b *testing.B) {
var signOpts []SignOption
if fastSign {
signOpts = append(signOpts, WithFastSign())
}
if sortKeys {
signOpts = append(signOpts, WithSortedKeys())
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
sig, err = Sign(
signers[0].nonces.SecNonce, signers[0].privKey,
combinedNonce, keys, msg, signOpts...,
)
if err != nil {
b.Fatalf("unable to generate sig: %v", err)
}
}
testSig = sig
testErr = err
})
}
}
}
}
// TODO(roasbeef): add impact of sorting ^
var sigOk bool
// BenchmarkPartialVerify benchmarks how long it takes to verify a partial
// signature.
func BenchmarkPartialVerify(b *testing.B) {
for _, numSigners := range []int{10, 100} {
for _, sortKeys := range []bool{true, false} {
name := fmt.Sprintf("sort_keys=%v/num_signers=%v",
sortKeys, numSigners)
signers := make(signerSet, numSigners)
for i := 0; i < numSigners; i++ {
signers[i] = genSigner(b)
}
combinedNonce, err := AggregateNonces(
signers.pubNonces(),
)
if err != nil {
b.Fatalf("unable to generate combined "+
"nonce: %v", err)
}
var sig *PartialSignature
var msg [32]byte
copy(msg[:], testMsg[:])
b.ReportAllocs()
b.ResetTimer()
sig, err = Sign(
signers[0].nonces.SecNonce, signers[0].privKey,
combinedNonce, signers.keys(), msg,
)
if err != nil {
b.Fatalf("unable to generate sig: %v", err)
}
keys := signers.keys()
pubKey := signers[0].pubKey
b.Run(name, func(b *testing.B) {
var signOpts []SignOption
if sortKeys {
signOpts = append(
signOpts, WithSortedKeys(),
)
}
b.ResetTimer()
b.ReportAllocs()
var ok bool
for i := 0; i < b.N; i++ {
ok = sig.Verify(
signers[0].nonces.PubNonce, combinedNonce,
keys, pubKey, msg,
)
if !ok {
b.Fatalf("generated invalid sig!")
}
}
sigOk = ok
})
}
}
}
var finalSchnorrSig *schnorr.Signature
// BenchmarkCombineSigs benchmarks how long it takes to combine a set amount of
// signatures.
func BenchmarkCombineSigs(b *testing.B) {
for _, numSigners := range []int{10, 100} {
signers := make(signerSet, numSigners)
for i := 0; i < numSigners; i++ {
signers[i] = genSigner(b)
}
combinedNonce, err := AggregateNonces(signers.pubNonces())
if err != nil {
b.Fatalf("unable to generate combined nonce: %v", err)
}
var msg [32]byte
copy(msg[:], testMsg[:])
var finalNonce *btcec.PublicKey
for i := range signers {
signer := signers[i]
partialSig, err := Sign(
signer.nonces.SecNonce, signer.privKey,
combinedNonce, signers.keys(), msg,
)
if err != nil {
b.Fatalf("unable to generate partial sig: %v",
err)
}
signers[i].partialSig = partialSig
if finalNonce == nil {
finalNonce = partialSig.R
}
}
sigs := signers.partialSigs()
name := fmt.Sprintf("num_signers=%v", numSigners)
b.Run(name, func(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
finalSig := CombineSigs(finalNonce, sigs)
finalSchnorrSig = finalSig
})
}
}
var testNonce [PubNonceSize]byte
// BenchmarkAggregateNonces benchmarks how long it takes to combine nonces.
func BenchmarkAggregateNonces(b *testing.B) {
for _, numSigners := range []int{10, 100} {
signers := make(signerSet, numSigners)
for i := 0; i < numSigners; i++ {
signers[i] = genSigner(b)
}
nonces := signers.pubNonces()
name := fmt.Sprintf("num_signers=%v", numSigners)
b.Run(name, func(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
pubNonce, err := AggregateNonces(nonces)
if err != nil {
b.Fatalf("unable to generate nonces: %v", err)
}
testNonce = pubNonce
})
}
}
var testKey *btcec.PublicKey
// BenchmarkAggregateKeys benchmarks how long it takes to aggregate public
// keys.
func BenchmarkAggregateKeys(b *testing.B) {
for _, numSigners := range []int{10, 100} {
for _, sortKeys := range []bool{true, false} {
signers := make(signerSet, numSigners)
for i := 0; i < numSigners; i++ {
signers[i] = genSigner(b)
}
signerKeys := signers.keys()
name := fmt.Sprintf("num_signers=%v/sort_keys=%v",
numSigners, sortKeys)
uniqueKeyIndex := secondUniqueKeyIndex(signerKeys, false)
b.Run(name, func(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
aggKey, _, _, _ := AggregateKeys(
signerKeys, sortKeys,
WithUniqueKeyIndex(uniqueKeyIndex),
)
testKey = aggKey.FinalKey
})
}
}
}

670
internal/musig2/context.go Normal file
View File

@ -0,0 +1,670 @@
// Copyright (c) 2013-2022 The btcsuite developers
package musig2
import (
"fmt"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
)
var (
// ErrSignersNotSpecified is returned when a caller attempts to create
// a context without specifying either the total number of signers, or
// the complete set of singers.
ErrSignersNotSpecified = fmt.Errorf("total number of signers or all " +
"signers must be known")
// ErrSignerNotInKeySet is returned when a the private key for a signer
// isn't included in the set of signing public keys.
ErrSignerNotInKeySet = fmt.Errorf("signing key is not found in key" +
" set")
// ErrAlredyHaveAllNonces is called when RegisterPubNonce is called too
// many times for a given signing session.
ErrAlredyHaveAllNonces = fmt.Errorf("already have all nonces")
// ErrNotEnoughSigners is returned when a caller attempts to create a
// session from a context, but before all the required signers are
// known.
ErrNotEnoughSigners = fmt.Errorf("not enough signers")
// ErrAlredyHaveAllNonces is returned when a caller attempts to
// register a signer, once we already have the total set of known
// signers.
ErrAlreadyHaveAllSigners = fmt.Errorf("all signers registered")
// ErrAlredyHaveAllSigs is called when CombineSig is called too many
// times for a given signing session.
ErrAlredyHaveAllSigs = fmt.Errorf("already have all sigs")
// ErrSigningContextReuse is returned if a user attempts to sign using
// the same signing context more than once.
ErrSigningContextReuse = fmt.Errorf("nonce already used")
// ErrFinalSigInvalid is returned when the combined signature turns out
// to be invalid.
ErrFinalSigInvalid = fmt.Errorf("final signature is invalid")
// ErrCombinedNonceUnavailable is returned when a caller attempts to
// sign a partial signature, without first having collected all the
// required combined nonces.
ErrCombinedNonceUnavailable = fmt.Errorf("missing combined nonce")
// ErrTaprootInternalKeyUnavailable is returned when a user attempts to
// obtain the
ErrTaprootInternalKeyUnavailable = fmt.Errorf("taproot tweak not used")
// ErrNotEnoughSigners is returned if a caller attempts to obtain an
// early nonce when it wasn't specified
ErrNoEarlyNonce = fmt.Errorf("no early nonce available")
)
// Context is a managed signing context for musig2. It takes care of things
// like securely generating secret nonces, aggregating keys and nonces, etc.
type Context struct {
// signingKey is the key we'll use for signing.
signingKey *btcec.PrivateKey
// pubKey is our even-y coordinate public key.
pubKey *btcec.PublicKey
// combinedKey is the aggregated public key.
combinedKey *AggregateKey
// uniqueKeyIndex is the index of the second unique key in the keySet.
// This is used to speed up signing and verification computations.
uniqueKeyIndex int
// keysHash is the hash of all the keys as defined in musig2.
keysHash []byte
// opts is the set of options for the context.
opts *contextOptions
// shouldSort keeps track of if the public keys should be sorted before
// any operations.
shouldSort bool
// sessionNonce will be populated if the earlyNonce option is true.
// After the first session is created, this nonce will be blanked out.
sessionNonce *Nonces
}
// ContextOption is a functional option argument that allows callers to modify
// the musig2 signing is done within a context.
type ContextOption func(*contextOptions)
// contextOptions houses the set of functional options that can be used to
// musig2 signing protocol.
type contextOptions struct {
// tweaks is the set of optinoal tweaks to apply to the combined public
// key.
tweaks []KeyTweakDesc
// taprootTweak specifies the taproot tweak. If specified, then we'll
// use this as the script root for the BIP 341 taproot (x-only) tweak.
// Normally we'd just apply the raw 32 byte tweak, but for taproot, we
// first need to compute the aggregated key before tweaking, and then
// use it as the internal key. This is required as the taproot tweak
// also commits to the public key, which in this case is the aggregated
// key before the tweak.
taprootTweak []byte
// bip86Tweak if true, then the weak will just be
// h_tapTweak(internalKey) as there is no true script root.
bip86Tweak bool
// keySet is the complete set of signers for this context.
keySet []*btcec.PublicKey
// numSigners is the total number of signers that will eventually be a
// part of the context.
numSigners int
// earlyNonce determines if a nonce should be generated during context
// creation, to be automatically passed to the created session.
earlyNonce bool
}
// defaultContextOptions returns the default context options.
func defaultContextOptions() *contextOptions {
return &contextOptions{}
}
// WithTweakedContext specifies that within the context, the aggregated public
// key should be tweaked with the specified tweaks.
func WithTweakedContext(tweaks ...KeyTweakDesc) ContextOption {
return func(o *contextOptions) {
o.tweaks = tweaks
}
}
// WithTaprootTweakCtx specifies that within this context, the final key should
// use the taproot tweak as defined in BIP 341: outputKey = internalKey +
// h_tapTweak(internalKey || scriptRoot). In this case, the aggreaged key
// before the tweak will be used as the internal key.
func WithTaprootTweakCtx(scriptRoot []byte) ContextOption {
return func(o *contextOptions) {
o.taprootTweak = scriptRoot
}
}
// WithBip86TweakCtx specifies that within this context, the final key should
// use the taproot tweak as defined in BIP 341, with the BIP 86 modification:
// outputKey = internalKey + h_tapTweak(internalKey)*G. In this case, the
// aggreaged key before the tweak will be used as the internal key.
func WithBip86TweakCtx() ContextOption {
return func(o *contextOptions) {
o.bip86Tweak = true
}
}
// WithKnownSigners is an optional parameter that should be used if a session
// can be created as soon as all the singers are known.
func WithKnownSigners(signers []*btcec.PublicKey) ContextOption {
return func(o *contextOptions) {
o.keySet = signers
o.numSigners = len(signers)
}
}
// WithNumSigners is a functional option used to specify that a context should
// be created without knowing all the signers. Instead the total number of
// signers is specified to ensure that a session can only be created once all
// the signers are known.
//
// NOTE: Either WithKnownSigners or WithNumSigners MUST be specified.
func WithNumSigners(n int) ContextOption {
return func(o *contextOptions) {
o.numSigners = n
}
}
// WithEarlyNonceGen allow a caller to specify that a nonce should be generated
// early, before the session is created. This should be used in protocols that
// require some partial nonce exchange before all the signers are known.
//
// NOTE: This option must only be specified with the WithNumSigners option.
func WithEarlyNonceGen() ContextOption {
return func(o *contextOptions) {
o.earlyNonce = true
}
}
// NewContext creates a new signing context with the passed singing key and set
// of public keys for each of the other signers.
//
// NOTE: This struct should be used over the raw Sign API whenever possible.
func NewContext(signingKey *btcec.PrivateKey, shouldSort bool,
ctxOpts ...ContextOption) (*Context, error) {
// First, parse the set of optional context options.
opts := defaultContextOptions()
for _, option := range ctxOpts {
option(opts)
}
pubKey, err := schnorr.ParsePubKey(
schnorr.SerializePubKey(signingKey.PubKey()),
)
if err != nil {
return nil, err
}
ctx := &Context{
signingKey: signingKey,
pubKey: pubKey,
opts: opts,
shouldSort: shouldSort,
}
switch {
// We know all the signers, so we can compute the aggregated key, along
// with all the other intermediate state we need to do signing and
// verification.
case opts.keySet != nil:
if err := ctx.combineSignerKeys(); err != nil {
return nil, err
}
// The total signers are known, so we add ourselves, and skip key
// aggregation.
case opts.numSigners != 0:
// Otherwise, we'll add ourselves as the only known signer, and
// await further calls to RegisterSigner before a session can
// be created.
opts.keySet = make([]*btcec.PublicKey, 0, opts.numSigners)
opts.keySet = append(opts.keySet, pubKey)
// If early nonce generation is specified, then we'll generate
// the nonce now to pass in to the session once all the callers
// are known.
if opts.earlyNonce {
ctx.sessionNonce, err = GenNonces()
if err != nil {
return nil, err
}
}
default:
return nil, ErrSignersNotSpecified
}
return ctx, nil
}
// combineSignerKeys is used to compute the aggregated signer key once all the
// signers are known.
func (c *Context) combineSignerKeys() error {
// As a sanity check, make sure the signing key is actually
// amongst the sit of signers.
var keyFound bool
for _, key := range c.opts.keySet {
if key.IsEqual(c.pubKey) {
keyFound = true
break
}
}
if !keyFound {
return ErrSignerNotInKeySet
}
// Now that we know that we're actually a signer, we'll
// generate the key hash finger print and second unique key
// index so we can speed up signing later.
c.keysHash = keyHashFingerprint(c.opts.keySet, c.shouldSort)
c.uniqueKeyIndex = secondUniqueKeyIndex(
c.opts.keySet, c.shouldSort,
)
keyAggOpts := []KeyAggOption{
WithKeysHash(c.keysHash),
WithUniqueKeyIndex(c.uniqueKeyIndex),
}
switch {
case c.opts.bip86Tweak:
keyAggOpts = append(
keyAggOpts, WithBIP86KeyTweak(),
)
case c.opts.taprootTweak != nil:
keyAggOpts = append(
keyAggOpts, WithTaprootKeyTweak(c.opts.taprootTweak),
)
case len(c.opts.tweaks) != 0:
keyAggOpts = append(keyAggOpts, WithKeyTweaks(c.opts.tweaks...))
}
// Next, we'll use this information to compute the aggregated
// public key that'll be used for signing in practice.
var err error
c.combinedKey, _, _, err = AggregateKeys(
c.opts.keySet, c.shouldSort, keyAggOpts...,
)
if err != nil {
return err
}
return nil
}
// EarlySessionNonce returns the early session nonce, if available.
func (c *Context) EarlySessionNonce() (*Nonces, error) {
if c.sessionNonce == nil {
return nil, ErrNoEarlyNonce
}
return c.sessionNonce, nil
}
// RegisterSigner allows a caller to register a signer after the context has
// been created. This will be used in scenarios where the total number of
// signers is known, but nonce exchange needs to happen before all the signers
// are known.
//
// A bool is returned which indicates if all the signers have been registered.
//
// NOTE: If the set of keys are not to be sorted during signing, then the
// ordering each key is registered with MUST match the desired ordering.
func (c *Context) RegisterSigner(pub *btcec.PublicKey) (bool, error) {
haveAllSigners := len(c.opts.keySet) == c.opts.numSigners
if haveAllSigners {
return false, ErrAlreadyHaveAllSigners
}
c.opts.keySet = append(c.opts.keySet, pub)
// If we have the expected number of signers at this point, then we can
// generate the aggregated key and other necessary information.
haveAllSigners = len(c.opts.keySet) == c.opts.numSigners
if haveAllSigners {
if err := c.combineSignerKeys(); err != nil {
return false, err
}
}
return haveAllSigners, nil
}
// NumRegisteredSigners returns the total number of registered signers.
func (c *Context) NumRegisteredSigners() int {
return len(c.opts.keySet)
}
// CombinedKey returns the combined public key that will be used to generate
// multi-signatures against.
func (c *Context) CombinedKey() (*btcec.PublicKey, error) {
// If the caller hasn't registered all the signers at this point, then
// the combined key won't be available.
if c.combinedKey == nil {
return nil, ErrNotEnoughSigners
}
return c.combinedKey.FinalKey, nil
}
// PubKey returns the public key of the signer of this session.
func (c *Context) PubKey() btcec.PublicKey {
return *c.pubKey
}
// SigningKeys returns the set of keys used for signing.
func (c *Context) SigningKeys() []*btcec.PublicKey {
keys := make([]*btcec.PublicKey, len(c.opts.keySet))
copy(keys, c.opts.keySet)
return keys
}
// TaprootInternalKey returns the internal taproot key, which is the aggregated
// key _before_ the tweak is applied. If a taproot tweak was specified, then
// CombinedKey() will return the fully tweaked output key, with this method
// returning the internal key. If a taproot tweak wasn't specified, then this
// method will return an error.
func (c *Context) TaprootInternalKey() (*btcec.PublicKey, error) {
// If the caller hasn't registered all the signers at this point, then
// the combined key won't be available.
if c.combinedKey == nil {
return nil, ErrNotEnoughSigners
}
if c.opts.taprootTweak == nil && !c.opts.bip86Tweak {
return nil, ErrTaprootInternalKeyUnavailable
}
return c.combinedKey.PreTweakedKey, nil
}
// SessionOption is a functional option argument that allows callers to modify
// the musig2 signing is done within a session.
type SessionOption func(*sessionOptions)
// sessionOptions houses the set of functional options that can be used to
// modify the musig2 signing protocol.
type sessionOptions struct {
externalNonce *Nonces
}
// defaultSessionOptions returns the default session options.
func defaultSessionOptions() *sessionOptions {
return &sessionOptions{}
}
// WithPreGeneratedNonce allows a caller to start a session using a nonce
// they've generated themselves. This may be useful in protocols where all the
// signer keys may not be known before nonce exchange needs to occur.
func WithPreGeneratedNonce(nonce *Nonces) SessionOption {
return func(o *sessionOptions) {
o.externalNonce = nonce
}
}
// Session represents a musig2 signing session. A new instance should be
// created each time a multi-signature is needed. The session struct handles
// nonces management, incremental partial sig vitrifaction, as well as final
// signature combination. Errors are returned when unsafe behavior such as
// nonce re-use is attempted.
//
// NOTE: This struct should be used over the raw Sign API whenever possible.
type Session struct {
opts *sessionOptions
ctx *Context
localNonces *Nonces
pubNonces [][PubNonceSize]byte
combinedNonce *[PubNonceSize]byte
msg [32]byte
ourSig *PartialSignature
sigs []*PartialSignature
finalSig *schnorr.Signature
}
// NewSession creates a new musig2 signing session.
func (c *Context) NewSession(options ...SessionOption) (*Session, error) {
opts := defaultSessionOptions()
for _, opt := range options {
opt(opts)
}
// At this point we verify that we know of all the signers, as
// otherwise we can't proceed with the session. This check is intended
// to catch misuse of the API wherein a caller forgets to register the
// remaining signers if they're doing nonce generation ahead of time.
if len(c.opts.keySet) != c.opts.numSigners {
return nil, ErrNotEnoughSigners
}
// If an early nonce was specified, then we'll automatically add the
// corresponding session option for the caller.
var localNonces *Nonces
if c.sessionNonce != nil {
// Apply the early nonce to the session, and also blank out the
// session nonce on the context to ensure it isn't ever re-used
// for another session.
localNonces = c.sessionNonce
c.sessionNonce = nil
} else if opts.externalNonce != nil {
// Otherwise if there's a custom nonce passed in via the
// session options, then use that instead.
localNonces = opts.externalNonce
}
// Now that we know we have enough signers, we'll either use the caller
// specified nonce, or generate a fresh set.
var err error
if localNonces == nil {
// At this point we need to generate a fresh nonce. We'll pass
// in some auxiliary information to strengthen the nonce
// generated.
localNonces, err = GenNonces(
WithNonceSecretKeyAux(c.signingKey),
WithNonceCombinedKeyAux(c.combinedKey.FinalKey),
)
if err != nil {
return nil, err
}
}
s := &Session{
opts: opts,
ctx: c,
localNonces: localNonces,
pubNonces: make([][PubNonceSize]byte, 0, c.opts.numSigners),
sigs: make([]*PartialSignature, 0, c.opts.numSigners),
}
s.pubNonces = append(s.pubNonces, localNonces.PubNonce)
return s, nil
}
// PublicNonce returns the public nonce for a signer. This should be sent to
// other parties before signing begins, so they can compute the aggregated
// public nonce.
func (s *Session) PublicNonce() [PubNonceSize]byte {
return s.localNonces.PubNonce
}
// NumRegisteredNonces returns the total number of nonces that have been
// regsitered so far.
func (s *Session) NumRegisteredNonces() int {
return len(s.pubNonces)
}
// RegisterPubNonce should be called for each public nonce from the set of
// signers. This method returns true once all the public nonces have been
// accounted for.
func (s *Session) RegisterPubNonce(nonce [PubNonceSize]byte) (bool, error) {
// If we already have all the nonces, then this method was called too
// many times.
haveAllNonces := len(s.pubNonces) == s.ctx.opts.numSigners
if haveAllNonces {
return false, ErrAlredyHaveAllNonces
}
// Add this nonce and check again if we already have tall the nonces we
// need.
s.pubNonces = append(s.pubNonces, nonce)
haveAllNonces = len(s.pubNonces) == s.ctx.opts.numSigners
// If we have all the nonces, then we can go ahead and combine them
// now.
if haveAllNonces {
combinedNonce, err := AggregateNonces(s.pubNonces)
if err != nil {
return false, err
}
s.combinedNonce = &combinedNonce
}
return haveAllNonces, nil
}
// Sign generates a partial signature for the target message, using the target
// context. If this method is called more than once per context, then an error
// is returned, as that means a nonce was re-used.
func (s *Session) Sign(msg [32]byte,
signOpts ...SignOption) (*PartialSignature, error) {
switch {
// If no local nonce is present, then this means we already signed, so
// we'll return an error to prevent nonce re-use.
case s.localNonces == nil:
return nil, ErrSigningContextReuse
// We also need to make sure we have the combined nonce, otherwise this
// funciton was called too early.
case s.combinedNonce == nil:
return nil, ErrCombinedNonceUnavailable
}
switch {
case s.ctx.opts.bip86Tweak:
signOpts = append(
signOpts, WithBip86SignTweak(),
)
case s.ctx.opts.taprootTweak != nil:
signOpts = append(
signOpts, WithTaprootSignTweak(s.ctx.opts.taprootTweak),
)
case len(s.ctx.opts.tweaks) != 0:
signOpts = append(signOpts, WithTweaks(s.ctx.opts.tweaks...))
}
partialSig, err := Sign(
s.localNonces.SecNonce, s.ctx.signingKey, *s.combinedNonce,
s.ctx.opts.keySet, msg, signOpts...,
)
// Now that we've generated our signature, we'll make sure to blank out
// our signing nonce.
s.localNonces = nil
if err != nil {
return nil, err
}
s.msg = msg
s.ourSig = partialSig
s.sigs = append(s.sigs, partialSig)
return partialSig, nil
}
// CombineSig buffers a partial signature received from a signing party. The
// method returns true once all the signatures are available, and can be
// combined into the final signature.
func (s *Session) CombineSig(sig *PartialSignature) (bool, error) {
// First check if we already have all the signatures we need. We
// already accumulated our own signature when we generated the sig.
haveAllSigs := len(s.sigs) == len(s.ctx.opts.keySet)
if haveAllSigs {
return false, ErrAlredyHaveAllSigs
}
// TODO(roasbeef): incremental check for invalid sig, or just detect at
// the very end?
// Accumulate this sig, and check again if we have all the sigs we
// need.
s.sigs = append(s.sigs, sig)
haveAllSigs = len(s.sigs) == len(s.ctx.opts.keySet)
// If we have all the signatures, then we can combine them all into the
// final signature.
if haveAllSigs {
var combineOpts []CombineOption
switch {
case s.ctx.opts.bip86Tweak:
combineOpts = append(
combineOpts, WithBip86TweakedCombine(
s.msg, s.ctx.opts.keySet,
s.ctx.shouldSort,
),
)
case s.ctx.opts.taprootTweak != nil:
combineOpts = append(
combineOpts, WithTaprootTweakedCombine(
s.msg, s.ctx.opts.keySet,
s.ctx.opts.taprootTweak, s.ctx.shouldSort,
),
)
case len(s.ctx.opts.tweaks) != 0:
combineOpts = append(
combineOpts, WithTweakedCombine(
s.msg, s.ctx.opts.keySet,
s.ctx.opts.tweaks, s.ctx.shouldSort,
),
)
}
finalSig := CombineSigs(s.ourSig.R, s.sigs, combineOpts...)
// We'll also verify the signature at this point to ensure it's
// valid.
//
// TODO(roasbef): allow skipping?
if !finalSig.Verify(s.msg[:], s.ctx.combinedKey.FinalKey) {
return false, ErrFinalSigInvalid
}
s.finalSig = finalSig
}
return haveAllSigs, nil
}
// FinalSig returns the final combined multi-signature, if present.
func (s *Session) FinalSig() *schnorr.Signature {
return s.finalSig
}

459
internal/musig2/keys.go Normal file
View File

@ -0,0 +1,459 @@
// Copyright 2013-2022 The btcsuite developers
package musig2
import (
"bytes"
"fmt"
"sort"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/chaincfg/chainhash"
secp "github.com/decred/dcrd/dcrec/secp256k1/v4"
)
var (
// KeyAggTagList is the tagged hash tag used to compute the hash of the
// list of sorted public keys.
KeyAggTagList = []byte("KeyAgg list")
// KeyAggTagCoeff is the tagged hash tag used to compute the key
// aggregation coefficient for each key.
KeyAggTagCoeff = []byte("KeyAgg coefficient")
// ErrTweakedKeyIsInfinity is returned if while tweaking a key, we end
// up with the point at infinity.
ErrTweakedKeyIsInfinity = fmt.Errorf("tweaked key is infinity point")
// ErrTweakedKeyOverflows is returned if a tweaking key is larger than
// 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141.
ErrTweakedKeyOverflows = fmt.Errorf("tweaked key is to large")
)
// sortableKeys defines a type of slice of public keys that implements the sort
// interface for BIP 340 keys.
type sortableKeys []*btcec.PublicKey
// Less reports whether the element with index i must sort before the element
// with index j.
func (s sortableKeys) Less(i, j int) bool {
// TODO(roasbeef): more efficient way to compare...
keyIBytes := schnorr.SerializePubKey(s[i])
keyJBytes := schnorr.SerializePubKey(s[j])
return bytes.Compare(keyIBytes, keyJBytes) == -1
}
// Swap swaps the elements with indexes i and j.
func (s sortableKeys) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// Len is the number of elements in the collection.
func (s sortableKeys) Len() int {
return len(s)
}
// sortKeys takes a set of schnorr public keys and returns a new slice that is
// a copy of the keys sorted in lexicographical order bytes on the x-only
// pubkey serialization.
func sortKeys(keys []*btcec.PublicKey) []*btcec.PublicKey {
keySet := sortableKeys(keys)
if sort.IsSorted(keySet) {
return keys
}
sort.Sort(keySet)
return keySet
}
// keyHashFingerprint computes the tagged hash of the series of (sorted) public
// keys passed as input. This is used to compute the aggregation coefficient
// for each key. The final computation is:
// - H(tag=KeyAgg list, pk1 || pk2..)
func keyHashFingerprint(keys []*btcec.PublicKey, sort bool) []byte {
if sort {
keys = sortKeys(keys)
}
// We'll create a single buffer and slice into that so the bytes buffer
// doesn't continually need to grow the underlying buffer.
keyAggBuf := make([]byte, 32*len(keys))
keyBytes := bytes.NewBuffer(keyAggBuf[0:0])
for _, key := range keys {
keyBytes.Write(schnorr.SerializePubKey(key))
}
h := chainhash.TaggedHash(KeyAggTagList, keyBytes.Bytes())
return h[:]
}
// keyBytesEqual returns true if two keys are the same from the PoV of BIP
// 340's 32-byte x-only public keys.
func keyBytesEqual(a, b *btcec.PublicKey) bool {
return bytes.Equal(
schnorr.SerializePubKey(a),
schnorr.SerializePubKey(b),
)
}
// aggregationCoefficient computes the key aggregation coefficient for the
// specified target key. The coefficient is computed as:
// - H(tag=KeyAgg coefficient, keyHashFingerprint(pks) || pk)
func aggregationCoefficient(keySet []*btcec.PublicKey,
targetKey *btcec.PublicKey, keysHash []byte,
secondKeyIdx int) *btcec.ModNScalar {
var mu btcec.ModNScalar
// If this is the second key, then this coefficient is just one.
if secondKeyIdx != -1 && keyBytesEqual(keySet[secondKeyIdx], targetKey) {
return mu.SetInt(1)
}
// Otherwise, we'll compute the full finger print hash for this given
// key and then use that to compute the coefficient tagged hash:
// * H(tag=KeyAgg coefficient, keyHashFingerprint(pks, pk) || pk)
var coefficientBytes [64]byte
copy(coefficientBytes[:], keysHash[:])
copy(coefficientBytes[32:], schnorr.SerializePubKey(targetKey))
muHash := chainhash.TaggedHash(KeyAggTagCoeff, coefficientBytes[:])
mu.SetByteSlice(muHash[:])
return &mu
}
// secondUniqueKeyIndex returns the index of the second unique key. If all keys
// are the same, then a value of -1 is returned.
func secondUniqueKeyIndex(keySet []*btcec.PublicKey, sort bool) int {
if sort {
keySet = sortKeys(keySet)
}
// Find the first key that isn't the same as the very first key (second
// unique key).
for i := range keySet {
if !keyBytesEqual(keySet[i], keySet[0]) {
return i
}
}
// A value of negative one is used to indicate that all the keys in the
// sign set are actually equal, which in practice actually makes musig2
// useless, but we need a value to distinguish this case.
return -1
}
// KeyTweakDesc describes a tweak to be applied to the aggregated public key
// generation and signing process. The IsXOnly specifies if the target key
// should be converted to an x-only public key before tweaking.
type KeyTweakDesc struct {
// Tweak is the 32-byte value that will modify the public key.
Tweak [32]byte
// IsXOnly if true, then the public key will be mapped to an x-only key
// before the tweaking operation is applied.
IsXOnly bool
}
// KeyAggOption is a functional option argument that allows callers to specify
// more or less information that has been pre-computed to the main routine.
type KeyAggOption func(*keyAggOption)
// keyAggOption houses the set of functional options that modify key
// aggregation.
type keyAggOption struct {
// keyHash is the output of keyHashFingerprint for a given set of keys.
keyHash []byte
// uniqueKeyIndex is the pre-computed index of the second unique key.
uniqueKeyIndex *int
// tweaks specifies a series of tweaks to be applied to the aggregated
// public key.
tweaks []KeyTweakDesc
// taprootTweak controls if the tweaks above should be applied in a BIP
// 340 style.
taprootTweak bool
// bip86Tweak specifies that the taproot tweak should be done in a BIP
// 86 style, where we don't expect an actual tweak and instead just
// commit to the public key itself.
bip86Tweak bool
}
// WithKeysHash allows key aggregation to be optimize, by allowing the caller
// to specify the hash of all the keys.
func WithKeysHash(keyHash []byte) KeyAggOption {
return func(o *keyAggOption) {
o.keyHash = keyHash
}
}
// WithUniqueKeyIndex allows the caller to specify the index of the second
// unique key.
func WithUniqueKeyIndex(idx int) KeyAggOption {
return func(o *keyAggOption) {
i := idx
o.uniqueKeyIndex = &i
}
}
// WithKeyTweaks allows a caller to specify a series of 32-byte tweaks that
// should be applied to the final aggregated public key.
func WithKeyTweaks(tweaks ...KeyTweakDesc) KeyAggOption {
return func(o *keyAggOption) {
o.tweaks = tweaks
}
}
// WithTaprootKeyTweak specifies that within this context, the final key should
// use the taproot tweak as defined in BIP 341: outputKey = internalKey +
// h_tapTweak(internalKey || scriptRoot). In this case, the aggregated key
// before the tweak will be used as the internal key.
//
// This option should be used instead of WithKeyTweaks when the aggregated key
// is intended to be used as a taproot output key that commits to a script
// root.
func WithTaprootKeyTweak(scriptRoot []byte) KeyAggOption {
return func(o *keyAggOption) {
var tweak [32]byte
copy(tweak[:], scriptRoot[:])
o.tweaks = []KeyTweakDesc{
{
Tweak: tweak,
IsXOnly: true,
},
}
o.taprootTweak = true
}
}
// WithBIP86KeyTweak specifies that then during key aggregation, the BIP 86
// tweak which just commits to the hash of the serialized public key should be
// used. This option should be used when signing with a key that was derived
// using BIP 86.
func WithBIP86KeyTweak() KeyAggOption {
return func(o *keyAggOption) {
o.tweaks = []KeyTweakDesc{
{
IsXOnly: true,
},
}
o.taprootTweak = true
o.bip86Tweak = true
}
}
// defaultKeyAggOptions returns the set of default arguments for key
// aggregation.
func defaultKeyAggOptions() *keyAggOption {
return &keyAggOption{}
}
// hasEvenY returns true if the affine representation of the passed jacobian
// point has an even y coordinate.
//
// TODO(roasbeef): double check, can just check the y coord even not jacobian?
func hasEvenY(pJ btcec.JacobianPoint) bool {
pJ.ToAffine()
p := btcec.NewPublicKey(&pJ.X, &pJ.Y)
keyBytes := p.SerializeCompressed()
return keyBytes[0] == secp.PubKeyFormatCompressedEven
}
// tweakKey applies a tweaks to the passed public key using the specified
// tweak. The parityAcc and tweakAcc are returned (in that order) which
// includes the accumulate ration of the parity factor and the tweak multiplied
// by the parity factor. The xOnly bool specifies if this is to be an x-only
// tweak or not.
func tweakKey(keyJ btcec.JacobianPoint, parityAcc btcec.ModNScalar, tweak [32]byte,
tweakAcc btcec.ModNScalar,
xOnly bool) (btcec.JacobianPoint, btcec.ModNScalar, btcec.ModNScalar, error) {
// First we'll compute the new parity factor for this key. If the key has
// an odd y coordinate (not even), then we'll need to negate it (multiply
// by -1 mod n, in this case).
var parityFactor btcec.ModNScalar
if xOnly && !hasEvenY(keyJ) {
parityFactor.SetInt(1).Negate()
} else {
parityFactor.SetInt(1)
}
// Next, map the tweak into a mod n integer so we can use it for
// manipulations below.
tweakInt := new(btcec.ModNScalar)
overflows := tweakInt.SetBytes(&tweak)
if overflows == 1 {
return keyJ, parityAcc, tweakAcc, ErrTweakedKeyOverflows
}
// Next, we'll compute: Q_i = g*Q + t*G, where g is our parityFactor and t
// is the tweakInt above. We'll space things out a bit to make it easier to
// follow.
//
// First compute t*G:
var tweakedGenerator btcec.JacobianPoint
btcec.ScalarBaseMultNonConst(tweakInt, &tweakedGenerator)
// Next compute g*Q:
btcec.ScalarMultNonConst(&parityFactor, &keyJ, &keyJ)
// Finally add both of them together to get our final
// tweaked point.
btcec.AddNonConst(&tweakedGenerator, &keyJ, &keyJ)
// As a sanity check, make sure that we didn't just end up with the
// point at infinity.
if keyJ == infinityPoint {
return keyJ, parityAcc, tweakAcc, ErrTweakedKeyIsInfinity
}
// As a final wrap up step, we'll accumulate the parity
// factor and also this tweak into the final set of accumulators.
parityAcc.Mul(&parityFactor)
tweakAcc.Mul(&parityFactor).Add(tweakInt)
return keyJ, parityAcc, tweakAcc, nil
}
// AggregateKey is a final aggregated key along with a possible version of the
// key without any tweaks applied.
type AggregateKey struct {
// FinalKey is the final aggregated key which may include one or more
// tweaks applied to it.
FinalKey *btcec.PublicKey
// PreTweakedKey is the aggregated *before* any tweaks have been
// applied. This should be used as the internal key in taproot
// contexts.
PreTweakedKey *btcec.PublicKey
}
// AggregateKeys takes a list of possibly unsorted keys and returns a single
// aggregated key as specified by the musig2 key aggregation algorithm. A nil
// value can be passed for keyHash, which causes this function to re-derive it.
// In addition to the combined public key, the parity accumulator and the tweak
// accumulator are returned as well.
func AggregateKeys(keys []*btcec.PublicKey, sort bool,
keyOpts ...KeyAggOption) (
*AggregateKey, *btcec.ModNScalar, *btcec.ModNScalar, error) {
// First, parse the set of optional signing options.
opts := defaultKeyAggOptions()
for _, option := range keyOpts {
option(opts)
}
// Sort the set of public key so we know we're working with them in
// sorted order for all the routines below.
if sort {
keys = sortKeys(keys)
}
// The caller may provide the hash of all the keys as an optimization
// during signing, as it already needs to be computed.
if opts.keyHash == nil {
opts.keyHash = keyHashFingerprint(keys, sort)
}
// A caller may also specify the unique key index themselves so we
// don't need to re-compute it.
if opts.uniqueKeyIndex == nil {
idx := secondUniqueKeyIndex(keys, sort)
opts.uniqueKeyIndex = &idx
}
// For each key, we'll compute the intermediate blinded key: a_i*P_i,
// where a_i is the aggregation coefficient for that key, and P_i is
// the key itself, then accumulate that (addition) into the main final
// key: P = P_1 + P_2 ... P_N.
var finalKeyJ btcec.JacobianPoint
for _, key := range keys {
// Port the key over to Jacobian coordinates as we need it in
// this format for the routines below.
var keyJ btcec.JacobianPoint
key.AsJacobian(&keyJ)
// Compute the aggregation coefficient for the key, then
// multiply it by the key itself: P_i' = a_i*P_i.
var tweakedKeyJ btcec.JacobianPoint
a := aggregationCoefficient(
keys, key, opts.keyHash, *opts.uniqueKeyIndex,
)
btcec.ScalarMultNonConst(a, &keyJ, &tweakedKeyJ)
// Finally accumulate this into the final key in an incremental
// fashion.
btcec.AddNonConst(&finalKeyJ, &tweakedKeyJ, &finalKeyJ)
}
// We'll copy over the key at this point, since this represents the
// aggregated key before any tweaks have been applied. This'll be used
// as the internal key for script path proofs.
finalKeyJ.ToAffine()
combinedKey := btcec.NewPublicKey(&finalKeyJ.X, &finalKeyJ.Y)
// At this point, if this is a taproot tweak, then we'll modify the
// base tweak value to use the BIP 341 tweak value.
if opts.taprootTweak {
// Emulate the same behavior as txscript.ComputeTaprootOutputKey
// which only operates on the x-only public key.
key, _ := schnorr.ParsePubKey(schnorr.SerializePubKey(
combinedKey,
))
// We only use the actual tweak bytes if we're not committing
// to a BIP-0086 key only spend output. Otherwise, we just
// commit to the internal key and an empty byte slice as the
// root hash.
tweakBytes := []byte{}
if !opts.bip86Tweak {
tweakBytes = opts.tweaks[0].Tweak[:]
}
// Compute the taproot key tagged hash of:
// h_tapTweak(internalKey || scriptRoot). We only do this for
// the first one, as you can only specify a single tweak when
// using the taproot mode with this API.
tapTweakHash := chainhash.TaggedHash(
chainhash.TagTapTweak, schnorr.SerializePubKey(key),
tweakBytes,
)
opts.tweaks[0].Tweak = *tapTweakHash
}
var (
err error
tweakAcc btcec.ModNScalar
parityAcc btcec.ModNScalar
)
parityAcc.SetInt(1)
// In this case we have a set of tweaks, so we'll incrementally apply
// each one, until we have our final tweaked key, and the related
// accumulators.
for i := 1; i <= len(opts.tweaks); i++ {
finalKeyJ, parityAcc, tweakAcc, err = tweakKey(
finalKeyJ, parityAcc, opts.tweaks[i-1].Tweak, tweakAcc,
opts.tweaks[i-1].IsXOnly,
)
if err != nil {
return nil, nil, nil, err
}
}
finalKeyJ.ToAffine()
finalKey := btcec.NewPublicKey(&finalKeyJ.X, &finalKeyJ.Y)
return &AggregateKey{
PreTweakedKey: combinedKey,
FinalKey: finalKey,
}, &parityAcc, &tweakAcc, nil
}

File diff suppressed because it is too large Load Diff

389
internal/musig2/nonces.go Normal file
View File

@ -0,0 +1,389 @@
// Copyright 2013-2022 The btcsuite developers
package musig2
import (
"bytes"
"crypto/rand"
"encoding/binary"
"io"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/chaincfg/chainhash"
)
const (
// PubNonceSize is the size of the public nonces. Each public nonce is
// serialized the full compressed encoding, which uses 32 bytes for each
// nonce.
PubNonceSize = 66
// SecNonceSize is the size of the secret nonces for musig2. The secret
// nonces are the corresponding private keys to the public nonce points.
SecNonceSize = 64
)
var (
// NonceAuxTag is the tag used to optionally mix in the secret key with
// the set of aux randomness.
NonceAuxTag = []byte("MuSig/aux")
// NonceGenTag is used to generate the value (from a set of required an
// optional field) that will be used as the part of the secret nonce.
NonceGenTag = []byte("MuSig/nonce")
byteOrder = binary.BigEndian
)
// zeroSecNonce is a secret nonce that's all zeroes. This is used to check that
// we're not attempting to re-use a nonce, and also protect callers from it.
var zeroSecNonce [SecNonceSize]byte
// Nonces holds the public and secret nonces required for musig2.
//
// TODO(roasbeef): methods on this to help w/ parsing, etc?
type Nonces struct {
// PubNonce holds the two 33-byte compressed encoded points that serve
// as the public set of nonces.
PubNonce [PubNonceSize]byte
// SecNonce holds the two 32-byte scalar values that are the private
// keys to the two public nonces.
SecNonce [SecNonceSize]byte
}
// secNonceToPubNonce takes our two secrete nonces, and produces their two
// corresponding EC points, serialized in compressed format.
func secNonceToPubNonce(secNonce [SecNonceSize]byte) [PubNonceSize]byte {
var k1Mod, k2Mod btcec.ModNScalar
k1Mod.SetByteSlice(secNonce[:btcec.PrivKeyBytesLen])
k2Mod.SetByteSlice(secNonce[btcec.PrivKeyBytesLen:])
var r1, r2 btcec.JacobianPoint
btcec.ScalarBaseMultNonConst(&k1Mod, &r1)
btcec.ScalarBaseMultNonConst(&k2Mod, &r2)
// Next, we'll convert the key in jacobian format to a normal public
// key expressed in affine coordinates.
r1.ToAffine()
r2.ToAffine()
r1Pub := btcec.NewPublicKey(&r1.X, &r1.Y)
r2Pub := btcec.NewPublicKey(&r2.X, &r2.Y)
var pubNonce [PubNonceSize]byte
// The public nonces are serialized as: R1 || R2, where both keys are
// serialized in compressed format.
copy(pubNonce[:], r1Pub.SerializeCompressed())
copy(
pubNonce[btcec.PubKeyBytesLenCompressed:],
r2Pub.SerializeCompressed(),
)
return pubNonce
}
// NonceGenOption is a function option that allows callers to modify how nonce
// generation happens.
type NonceGenOption func(*nonceGenOpts)
// nonceGenOpts is the set of options that control how nonce generation
// happens.
type nonceGenOpts struct {
// randReader is what we'll use to generate a set of random bytes. If
// unspecified, then the normal crypto/rand rand.Read method will be
// used in place.
randReader io.Reader
// secretKey is an optional argument that's used to further augment the
// generated nonce by xor'ing it with this secret key.
secretKey []byte
// combinedKey is an optional argument that if specified, will be
// combined along with the nonce generation.
combinedKey []byte
// msg is an optional argument that will be mixed into the nonce
// derivation algorithm.
msg []byte
// auxInput is an optional argument that will be mixed into the nonce
// derivation algorithm.
auxInput []byte
}
// cryptoRandAdapter is an adapter struct that allows us to pass in the package
// level Read function from crypto/rand into a context that accepts an
// io.Reader.
type cryptoRandAdapter struct {
}
// Read implements the io.Reader interface for the crypto/rand package. By
// default, we always use the crypto/rand reader, but the caller is able to
// specify their own generation, which can be useful for deterministic tests.
func (c *cryptoRandAdapter) Read(p []byte) (n int, err error) {
return rand.Read(p)
}
// defaultNonceGenOpts returns the default set of nonce generation options.
func defaultNonceGenOpts() *nonceGenOpts {
return &nonceGenOpts{
randReader: &cryptoRandAdapter{},
}
}
// WithCustomRand allows a caller to use a custom random number generator in
// place for crypto/rand. This should only really be used to generate
// determinstic tests.
func WithCustomRand(r io.Reader) NonceGenOption {
return func(o *nonceGenOpts) {
o.randReader = r
}
}
// WithNonceSecretKeyAux allows a caller to optionally specify a secret key
// that should be used to augment the randomness used to generate the nonces.
func WithNonceSecretKeyAux(secKey *btcec.PrivateKey) NonceGenOption {
return func(o *nonceGenOpts) {
o.secretKey = secKey.Serialize()
}
}
// WithNonceCombinedKeyAux allows a caller to optionally specify the combined
// key used in this signing session to further augment the randomness used to
// generate nonces.
func WithNonceCombinedKeyAux(combinedKey *btcec.PublicKey) NonceGenOption {
return func(o *nonceGenOpts) {
o.combinedKey = schnorr.SerializePubKey(combinedKey)
}
}
// WithNonceMessageAux allows a caller to optionally specify a message to be
// mixed into the randomness generated to create the nonce.
func WithNonceMessageAux(msg [32]byte) NonceGenOption {
return func(o *nonceGenOpts) {
o.msg = msg[:]
}
}
// WithNonceAuxInput is a set of auxiliary randomness, similar to BIP 340 that
// can be used to further augment the nonce generation process.
func WithNonceAuxInput(aux []byte) NonceGenOption {
return func(o *nonceGenOpts) {
o.auxInput = aux
}
}
// withCustomOptions allows a caller to pass a complete set of custom
// nonceGenOpts, without needing to create custom and checked structs such as
// *btcec.PrivateKey. This is mainly used to match the testcases provided by
// the MuSig2 BIP.
func withCustomOptions(customOpts nonceGenOpts) NonceGenOption {
return func(o *nonceGenOpts) {
o.randReader = customOpts.randReader
o.secretKey = customOpts.secretKey
o.combinedKey = customOpts.combinedKey
o.msg = customOpts.msg
o.auxInput = customOpts.auxInput
}
}
// lengthWriter is a function closure that allows a caller to control how the
// length prefix of a byte slice is written.
type lengthWriter func(w io.Writer, b []byte) error
// uint8Writer is an implementation of lengthWriter that writes the length of
// the byte slice using 1 byte.
func uint8Writer(w io.Writer, b []byte) error {
return binary.Write(w, byteOrder, uint8(len(b)))
}
// uint32Writer is an implementation of lengthWriter that writes the length of
// the byte slice using 4 bytes.
func uint32Writer(w io.Writer, b []byte) error {
return binary.Write(w, byteOrder, uint32(len(b)))
}
// writeBytesPrefix is used to write out: len(b) || b, to the passed io.Writer.
// The lengthWriter function closure is used to allow the caller to specify the
// precise byte packing of the length.
func writeBytesPrefix(w io.Writer, b []byte, lenWriter lengthWriter) error {
// Write out the length of the byte first, followed by the set of bytes
// itself.
if err := lenWriter(w, b); err != nil {
return err
}
if _, err := w.Write(b); err != nil {
return err
}
return nil
}
// genNonceAuxBytes writes out the full byte string used to derive a secret
// nonce based on some initial randomness as well as the series of optional
// fields. The byte string used for derivation is:
// - tagged_hash("MuSig/nonce", rand || len(aggpk) || aggpk || len(m)
// || m || len(in) || in || i).
//
// where i is the ith secret nonce being generated.
func genNonceAuxBytes(rand []byte, i int,
opts *nonceGenOpts) (*chainhash.Hash, error) {
var w bytes.Buffer
// First, write out the randomness generated in the prior step.
if _, err := w.Write(rand); err != nil {
return nil, err
}
// Next, we'll write out: len(aggpk) || aggpk.
err := writeBytesPrefix(&w, opts.combinedKey, uint8Writer)
if err != nil {
return nil, err
}
// Next, we'll write out the length prefixed message.
err = writeBytesPrefix(&w, opts.msg, uint8Writer)
if err != nil {
return nil, err
}
// Finally we'll write out the auxiliary input.
err = writeBytesPrefix(&w, opts.auxInput, uint32Writer)
if err != nil {
return nil, err
}
// Next we'll write out the interaction/index number which will
// uniquely generate two nonces given the rest of the possibly static
// parameters.
if err := binary.Write(&w, byteOrder, uint8(i)); err != nil {
return nil, err
}
// With the message buffer complete, we'll now derive the tagged hash
// using our set of params.
return chainhash.TaggedHash(NonceGenTag, w.Bytes()), nil
}
// GenNonces generates the secret nonces, as well as the public nonces which
// correspond to an EC point generated using the secret nonce as a private key.
func GenNonces(options ...NonceGenOption) (*Nonces, error) {
opts := defaultNonceGenOpts()
for _, opt := range options {
opt(opts)
}
// First, we'll start out by generating 32 random bytes drawn from our
// CSPRNG.
var randBytes [32]byte
if _, err := opts.randReader.Read(randBytes[:]); err != nil {
return nil, err
}
// If the options contain a secret key, we XOR it with with the tagged
// random bytes.
if len(opts.secretKey) == 32 {
taggedHash := chainhash.TaggedHash(NonceAuxTag, randBytes[:])
for i := 0; i < chainhash.HashSize; i++ {
randBytes[i] = opts.secretKey[i] ^ taggedHash[i]
}
}
// Using our randomness and the set of optional params, generate our
// two secret nonces: k1 and k2.
k1, err := genNonceAuxBytes(randBytes[:], 0, opts)
if err != nil {
return nil, err
}
k2, err := genNonceAuxBytes(randBytes[:], 1, opts)
if err != nil {
return nil, err
}
var k1Mod, k2Mod btcec.ModNScalar
k1Mod.SetBytes((*[32]byte)(k1))
k2Mod.SetBytes((*[32]byte)(k2))
// The secret nonces are serialized as the concatenation of the two 32
// byte secret nonce values.
var nonces Nonces
k1Mod.PutBytesUnchecked(nonces.SecNonce[:])
k2Mod.PutBytesUnchecked(nonces.SecNonce[btcec.PrivKeyBytesLen:])
// Next, we'll generate R_1 = k_1*G and R_2 = k_2*G. Along the way we
// need to map our nonce values into mod n scalars so we can work with
// the btcec API.
nonces.PubNonce = secNonceToPubNonce(nonces.SecNonce)
return &nonces, nil
}
// AggregateNonces aggregates the set of a pair of public nonces for each party
// into a single aggregated nonces to be used for multi-signing.
func AggregateNonces(pubNonces [][PubNonceSize]byte) ([PubNonceSize]byte, error) {
// combineNonces is a helper function that aggregates (adds) up a
// series of nonces encoded in compressed format. It uses a slicing
// function to extra 33 bytes at a time from the packed 2x public
// nonces.
type nonceSlicer func([PubNonceSize]byte) []byte
combineNonces := func(slicer nonceSlicer) (btcec.JacobianPoint, error) {
// Convert the set of nonces into jacobian coordinates we can
// use to accumulate them all into each other.
pubNonceJs := make([]*btcec.JacobianPoint, len(pubNonces))
for i, pubNonceBytes := range pubNonces {
// Using the slicer, extract just the bytes we need to
// decode.
var nonceJ btcec.JacobianPoint
nonceJ, err := btcec.ParseJacobian(slicer(pubNonceBytes))
if err != nil {
return btcec.JacobianPoint{}, err
}
pubNonceJs[i] = &nonceJ
}
// Now that we have the set of complete nonces, we'll aggregate
// them: R = R_i + R_i+1 + ... + R_i+n.
var aggregateNonce btcec.JacobianPoint
for _, pubNonceJ := range pubNonceJs {
btcec.AddNonConst(
&aggregateNonce, pubNonceJ, &aggregateNonce,
)
}
aggregateNonce.ToAffine()
return aggregateNonce, nil
}
// The final nonce public nonce is actually two nonces, one that
// aggregate the first nonce of all the parties, and the other that
// aggregates the second nonce of all the parties.
var finalNonce [PubNonceSize]byte
combinedNonce1, err := combineNonces(func(n [PubNonceSize]byte) []byte {
return n[:btcec.PubKeyBytesLenCompressed]
})
if err != nil {
return finalNonce, err
}
combinedNonce2, err := combineNonces(func(n [PubNonceSize]byte) []byte {
return n[btcec.PubKeyBytesLenCompressed:]
})
if err != nil {
return finalNonce, err
}
copy(finalNonce[:], btcec.JacobianToByteSlice(combinedNonce1))
copy(
finalNonce[btcec.PubKeyBytesLenCompressed:],
btcec.JacobianToByteSlice(combinedNonce2),
)
return finalNonce, nil
}

703
internal/musig2/sign.go Normal file
View File

@ -0,0 +1,703 @@
// Copyright 2013-2022 The btcsuite developers
package musig2
import (
"bytes"
"fmt"
"io"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/chaincfg/chainhash"
secp "github.com/decred/dcrd/dcrec/secp256k1/v4"
)
var (
// NonceBlindTag is that tag used to construct the value b, which
// blinds the second public nonce of each party.
NonceBlindTag = []byte("MuSig/noncecoef")
// ChallengeHashTag is the tag used to construct the challenge hash
ChallengeHashTag = []byte("BIP0340/challenge")
// ErrNoncePointAtInfinity is returned if during signing, the fully
// combined public nonce is the point at infinity.
ErrNoncePointAtInfinity = fmt.Errorf("signing nonce is the infinity " +
"point")
// ErrPrivKeyZero is returned when the private key for signing is
// actually zero.
ErrPrivKeyZero = fmt.Errorf("priv key is zero")
// ErrPartialSigInvalid is returned when a partial is found to be
// invalid.
ErrPartialSigInvalid = fmt.Errorf("partial signature is invalid")
// ErrSecretNonceZero is returned when a secret nonce is passed in a
// zero.
ErrSecretNonceZero = fmt.Errorf("secret nonce is blank")
)
// infinityPoint is the jacobian representation of the point at infinity.
var infinityPoint btcec.JacobianPoint
// PartialSignature reprints a partial (s-only) musig2 multi-signature. This
// isn't a valid schnorr signature by itself, as it needs to be aggregated
// along with the other partial signatures to be completed.
type PartialSignature struct {
S *btcec.ModNScalar
R *btcec.PublicKey
}
// NewPartialSignature returns a new instances of the partial sig struct.
func NewPartialSignature(s *btcec.ModNScalar,
r *btcec.PublicKey) PartialSignature {
return PartialSignature{
S: s,
R: r,
}
}
// Encode writes a serialized version of the partial signature to the passed
// io.Writer
func (p *PartialSignature) Encode(w io.Writer) error {
var sBytes [32]byte
p.S.PutBytes(&sBytes)
if _, err := w.Write(sBytes[:]); err != nil {
return err
}
return nil
}
// Decode attempts to parse a serialized PartialSignature stored in the passed
// io reader.
func (p *PartialSignature) Decode(r io.Reader) error {
p.S = new(btcec.ModNScalar)
var sBytes [32]byte
if _, err := io.ReadFull(r, sBytes[:]); err != nil {
return nil
}
overflows := p.S.SetBytes(&sBytes)
if overflows == 1 {
return ErrPartialSigInvalid
}
return nil
}
// SignOption is a functional option argument that allows callers to modify the
// way we generate musig2 schnorr signatures.
type SignOption func(*signOptions)
// signOptions houses the set of functional options that can be used to modify
// the method used to generate the musig2 partial signature.
type signOptions struct {
// fastSign determines if we'll skip the check at the end of the
// routine where we attempt to verify the produced signature.
fastSign bool
// sortKeys determines if the set of keys should be sorted before doing
// key aggregation.
sortKeys bool
// tweaks specifies a series of tweaks to be applied to the aggregated
// public key, which also partially carries over into the signing
// process.
tweaks []KeyTweakDesc
// taprootTweak specifies a taproot specific tweak. of the tweaks
// specified above. Normally we'd just apply the raw 32 byte tweak, but
// for taproot, we first need to compute the aggregated key before
// tweaking, and then use it as the internal key. This is required as
// the taproot tweak also commits to the public key, which in this case
// is the aggregated key before the tweak.
taprootTweak []byte
// bip86Tweak specifies that the taproot tweak should be done in a BIP
// 86 style, where we don't expect an actual tweak and instead just
// commit to the public key itself.
bip86Tweak bool
}
// defaultSignOptions returns the default set of signing operations.
func defaultSignOptions() *signOptions {
return &signOptions{}
}
// WithFastSign forces signing to skip the extra verification step at the end.
// Performance sensitive applications may opt to use this option to speed up
// the signing operation.
func WithFastSign() SignOption {
return func(o *signOptions) {
o.fastSign = true
}
}
// WithSortedKeys determines if the set of signing public keys are to be sorted
// or not before doing key aggregation.
func WithSortedKeys() SignOption {
return func(o *signOptions) {
o.sortKeys = true
}
}
// WithTweaks determines if the aggregated public key used should apply a
// series of tweaks before key aggregation.
func WithTweaks(tweaks ...KeyTweakDesc) SignOption {
return func(o *signOptions) {
o.tweaks = tweaks
}
}
// WithTaprootSignTweak allows a caller to specify a tweak that should be used
// in a bip 340 manner when signing. This differs from WithTweaks as the tweak
// will be assumed to always be x-only and the intermediate aggregate key
// before tweaking will be used to generate part of the tweak (as the taproot
// tweak also commits to the internal key).
//
// This option should be used in the taproot context to create a valid
// signature for the keypath spend for taproot, when the output key is actually
// committing to a script path, or some other data.
func WithTaprootSignTweak(scriptRoot []byte) SignOption {
return func(o *signOptions) {
o.taprootTweak = scriptRoot
}
}
// WithBip86SignTweak allows a caller to specify a tweak that should be used in
// a bip 340 manner when signing, factoring in BIP 86 as well. This differs
// from WithTaprootSignTweak as no true script root will be committed to,
// instead we just commit to the internal key.
//
// This option should be used in the taproot context to create a valid
// signature for the keypath spend for taproot, when the output key was
// generated using BIP 86.
func WithBip86SignTweak() SignOption {
return func(o *signOptions) {
o.bip86Tweak = true
}
}
// Sign generates a musig2 partial signature given the passed key set, secret
// nonce, public nonce, and private keys. This method returns an error if the
// generated nonces are either too large, or end up mapping to the point at
// infinity.
func Sign(secNonce [SecNonceSize]byte, privKey *btcec.PrivateKey,
combinedNonce [PubNonceSize]byte, pubKeys []*btcec.PublicKey,
msg [32]byte, signOpts ...SignOption) (*PartialSignature, error) {
// First, parse the set of optional signing options.
opts := defaultSignOptions()
for _, option := range signOpts {
option(opts)
}
// Compute the hash of all the keys here as we'll need it do aggregate
// the keys and also at the final step of signing.
keysHash := keyHashFingerprint(pubKeys, opts.sortKeys)
uniqueKeyIndex := secondUniqueKeyIndex(pubKeys, opts.sortKeys)
keyAggOpts := []KeyAggOption{
WithKeysHash(keysHash), WithUniqueKeyIndex(uniqueKeyIndex),
}
switch {
case opts.bip86Tweak:
keyAggOpts = append(
keyAggOpts, WithBIP86KeyTweak(),
)
case opts.taprootTweak != nil:
keyAggOpts = append(
keyAggOpts, WithTaprootKeyTweak(opts.taprootTweak),
)
case len(opts.tweaks) != 0:
keyAggOpts = append(keyAggOpts, WithKeyTweaks(opts.tweaks...))
}
// Next we'll construct the aggregated public key based on the set of
// signers.
combinedKey, parityAcc, _, err := AggregateKeys(
pubKeys, opts.sortKeys, keyAggOpts...,
)
if err != nil {
return nil, err
}
// Next we'll compute the value b, that blinds our second public
// nonce:
// * b = h(tag=NonceBlindTag, combinedNonce || combinedKey || m).
var (
nonceMsgBuf bytes.Buffer
nonceBlinder btcec.ModNScalar
)
nonceMsgBuf.Write(combinedNonce[:])
nonceMsgBuf.Write(schnorr.SerializePubKey(combinedKey.FinalKey))
nonceMsgBuf.Write(msg[:])
nonceBlindHash := chainhash.TaggedHash(
NonceBlindTag, nonceMsgBuf.Bytes(),
)
nonceBlinder.SetByteSlice(nonceBlindHash[:])
// Next, we'll parse the public nonces into R1 and R2.
r1J, err := btcec.ParseJacobian(
combinedNonce[:btcec.PubKeyBytesLenCompressed],
)
if err != nil {
return nil, err
}
r2J, err := btcec.ParseJacobian(
combinedNonce[btcec.PubKeyBytesLenCompressed:],
)
if err != nil {
return nil, err
}
// With our nonce blinding value, we'll now combine both the public
// nonces, using the blinding factor to tweak the second nonce:
// * R = R_1 + b*R_2
var nonce btcec.JacobianPoint
btcec.ScalarMultNonConst(&nonceBlinder, &r2J, &r2J)
btcec.AddNonConst(&r1J, &r2J, &nonce)
// If the combined nonce it eh point at infinity, then we'll bail out.
if nonce == infinityPoint {
G := btcec.Generator()
G.AsJacobian(&nonce)
}
// Next we'll parse out our two secret nonces, which we'll be using in
// the core signing process below.
var k1, k2 btcec.ModNScalar
k1.SetByteSlice(secNonce[:btcec.PrivKeyBytesLen])
k2.SetByteSlice(secNonce[btcec.PrivKeyBytesLen:])
if k1.IsZero() || k2.IsZero() {
return nil, ErrSecretNonceZero
}
nonce.ToAffine()
nonceKey := btcec.NewPublicKey(&nonce.X, &nonce.Y)
// If the nonce R has an odd y coordinate, then we'll negate both our
// secret nonces.
if nonce.Y.IsOdd() {
k1.Negate()
k2.Negate()
}
privKeyScalar := privKey.Key
if privKeyScalar.IsZero() {
return nil, ErrPrivKeyZero
}
pubKey := privKey.PubKey()
pubKeyYIsOdd := func() bool {
pubKeyBytes := pubKey.SerializeCompressed()
return pubKeyBytes[0] == secp.PubKeyFormatCompressedOdd
}()
combinedKeyYIsOdd := func() bool {
combinedKeyBytes := combinedKey.FinalKey.SerializeCompressed()
return combinedKeyBytes[0] == secp.PubKeyFormatCompressedOdd
}()
// Next we'll compute our two parity factors for Q the combined public
// key, and P, the public key we're signing with. If the keys are odd,
// then we'll negate them.
parityCombinedKey := new(btcec.ModNScalar).SetInt(1)
paritySignKey := new(btcec.ModNScalar).SetInt(1)
if combinedKeyYIsOdd {
parityCombinedKey.Negate()
}
if pubKeyYIsOdd {
paritySignKey.Negate()
}
// Before we sign below, we'll multiply by our various parity factors
// to ensure that the signing key is properly negated (if necessary):
// * d = gv⋅gaccv⋅gp⋅d'
privKeyScalar.Mul(parityCombinedKey).Mul(paritySignKey).Mul(parityAcc)
// Next we'll create the challenge hash that commits to the combined
// nonce, combined public key and also the message:
// * e = H(tag=ChallengeHashTag, R || Q || m) mod n
var challengeMsg bytes.Buffer
challengeMsg.Write(schnorr.SerializePubKey(nonceKey))
challengeMsg.Write(schnorr.SerializePubKey(combinedKey.FinalKey))
challengeMsg.Write(msg[:])
challengeBytes := chainhash.TaggedHash(
ChallengeHashTag, challengeMsg.Bytes(),
)
var e btcec.ModNScalar
e.SetByteSlice(challengeBytes[:])
// Next, we'll compute a, our aggregation coefficient for the key that
// we're signing with.
a := aggregationCoefficient(pubKeys, pubKey, keysHash, uniqueKeyIndex)
// With mu constructed, we can finally generate our partial signature
// as: s = (k1_1 + b*k_2 + e*a*d) mod n.
s := new(btcec.ModNScalar)
s.Add(&k1).Add(k2.Mul(&nonceBlinder)).Add(e.Mul(a).Mul(&privKeyScalar))
sig := NewPartialSignature(s, nonceKey)
// If we're not in fast sign mode, then we'll also validate our partial
// signature.
if !opts.fastSign {
pubNonce := secNonceToPubNonce(secNonce)
sigValid := sig.Verify(
pubNonce, combinedNonce, pubKeys, pubKey, msg,
signOpts...,
)
if !sigValid {
return nil, fmt.Errorf("sig is invalid!")
}
}
return &sig, nil
}
// Verify implements partial signature verification given the public nonce for
// the signer, aggregate nonce, signer set and finally the message being
// signed.
func (p *PartialSignature) Verify(pubNonce [PubNonceSize]byte,
combinedNonce [PubNonceSize]byte, keySet []*btcec.PublicKey,
signingKey *btcec.PublicKey, msg [32]byte, signOpts ...SignOption) bool {
pubKey := schnorr.SerializePubKey(signingKey)
return verifyPartialSig(
p, pubNonce, combinedNonce, keySet, pubKey, msg, signOpts...,
) == nil
}
// verifyPartialSig attempts to verify a partial schnorr signature given the
// necessary parameters. This is the internal version of Verify that returns
// detailed errors. signed.
func verifyPartialSig(partialSig *PartialSignature, pubNonce [PubNonceSize]byte,
combinedNonce [PubNonceSize]byte, keySet []*btcec.PublicKey,
pubKey []byte, msg [32]byte, signOpts ...SignOption) error {
opts := defaultSignOptions()
for _, option := range signOpts {
option(opts)
}
// First we'll map the internal partial signature back into something
// we can manipulate.
s := partialSig.S
// Next we'll parse out the two public nonces into something we can
// use.
//
// Compute the hash of all the keys here as we'll need it do aggregate
// the keys and also at the final step of verification.
keysHash := keyHashFingerprint(keySet, opts.sortKeys)
uniqueKeyIndex := secondUniqueKeyIndex(keySet, opts.sortKeys)
keyAggOpts := []KeyAggOption{
WithKeysHash(keysHash), WithUniqueKeyIndex(uniqueKeyIndex),
}
switch {
case opts.bip86Tweak:
keyAggOpts = append(
keyAggOpts, WithBIP86KeyTweak(),
)
case opts.taprootTweak != nil:
keyAggOpts = append(
keyAggOpts, WithTaprootKeyTweak(opts.taprootTweak),
)
case len(opts.tweaks) != 0:
keyAggOpts = append(keyAggOpts, WithKeyTweaks(opts.tweaks...))
}
// Next we'll construct the aggregated public key based on the set of
// signers.
combinedKey, parityAcc, _, err := AggregateKeys(
keySet, opts.sortKeys, keyAggOpts...,
)
if err != nil {
return err
}
// Next we'll compute the value b, that blinds our second public
// nonce:
// * b = h(tag=NonceBlindTag, combinedNonce || combinedKey || m).
var (
nonceMsgBuf bytes.Buffer
nonceBlinder btcec.ModNScalar
)
nonceMsgBuf.Write(combinedNonce[:])
nonceMsgBuf.Write(schnorr.SerializePubKey(combinedKey.FinalKey))
nonceMsgBuf.Write(msg[:])
nonceBlindHash := chainhash.TaggedHash(NonceBlindTag, nonceMsgBuf.Bytes())
nonceBlinder.SetByteSlice(nonceBlindHash[:])
r1J, err := btcec.ParseJacobian(
combinedNonce[:btcec.PubKeyBytesLenCompressed],
)
if err != nil {
return err
}
r2J, err := btcec.ParseJacobian(
combinedNonce[btcec.PubKeyBytesLenCompressed:],
)
if err != nil {
return err
}
// With our nonce blinding value, we'll now combine both the public
// nonces, using the blinding factor to tweak the second nonce:
// * R = R_1 + b*R_2
var nonce btcec.JacobianPoint
btcec.ScalarMultNonConst(&nonceBlinder, &r2J, &r2J)
btcec.AddNonConst(&r1J, &r2J, &nonce)
// Next, we'll parse out the set of public nonces this signer used to
// generate the signature.
pubNonce1J, err := btcec.ParseJacobian(
pubNonce[:btcec.PubKeyBytesLenCompressed],
)
if err != nil {
return err
}
pubNonce2J, err := btcec.ParseJacobian(
pubNonce[btcec.PubKeyBytesLenCompressed:],
)
if err != nil {
return err
}
// If the nonce is the infinity point we set it to the Generator.
if nonce == infinityPoint {
btcec.GeneratorJacobian(&nonce)
} else {
nonce.ToAffine()
}
// We'll perform a similar aggregation and blinding operator as we did
// above for the combined nonces: R' = R_1' + b*R_2'.
var pubNonceJ btcec.JacobianPoint
btcec.ScalarMultNonConst(&nonceBlinder, &pubNonce2J, &pubNonce2J)
btcec.AddNonConst(&pubNonce1J, &pubNonce2J, &pubNonceJ)
pubNonceJ.ToAffine()
// If the combined nonce used in the challenge hash has an odd y
// coordinate, then we'll negate our final public nonce.
if nonce.Y.IsOdd() {
pubNonceJ.Y.Negate(1)
pubNonceJ.Y.Normalize()
}
// Next we'll create the challenge hash that commits to the combined
// nonce, combined public key and also the message:
// * e = H(tag=ChallengeHashTag, R || Q || m) mod n
var challengeMsg bytes.Buffer
challengeMsg.Write(schnorr.SerializePubKey(btcec.NewPublicKey(
&nonce.X, &nonce.Y,
)))
challengeMsg.Write(schnorr.SerializePubKey(combinedKey.FinalKey))
challengeMsg.Write(msg[:])
challengeBytes := chainhash.TaggedHash(
ChallengeHashTag, challengeMsg.Bytes(),
)
var e btcec.ModNScalar
e.SetByteSlice(challengeBytes[:])
signingKey, err := schnorr.ParsePubKey(pubKey)
if err != nil {
return err
}
// Next, we'll compute a, our aggregation coefficient for the key that
// we're signing with.
a := aggregationCoefficient(keySet, signingKey, keysHash, uniqueKeyIndex)
// If the combined key has an odd y coordinate, then we'll negate
// parity factor for the signing key.
paritySignKey := new(btcec.ModNScalar).SetInt(1)
combinedKeyBytes := combinedKey.FinalKey.SerializeCompressed()
if combinedKeyBytes[0] == secp.PubKeyFormatCompressedOdd {
paritySignKey.Negate()
}
// Next, we'll construct the final parity factor by multiplying the
// sign key parity factor with the accumulated parity factor for all
// the keys.
finalParityFactor := paritySignKey.Mul(parityAcc)
// Now we'll multiply the parity factor by our signing key, which'll
// take care of the amount of negation needed.
var signKeyJ btcec.JacobianPoint
signingKey.AsJacobian(&signKeyJ)
btcec.ScalarMultNonConst(finalParityFactor, &signKeyJ, &signKeyJ)
// In the final set, we'll check that: s*G == R' + e*a*P.
var sG, rP btcec.JacobianPoint
btcec.ScalarBaseMultNonConst(s, &sG)
btcec.ScalarMultNonConst(e.Mul(a), &signKeyJ, &rP)
btcec.AddNonConst(&rP, &pubNonceJ, &rP)
sG.ToAffine()
rP.ToAffine()
if sG != rP {
return ErrPartialSigInvalid
}
return nil
}
// CombineOption is a functional option argument that allows callers to modify the
// way we combine musig2 schnorr signatures.
type CombineOption func(*combineOptions)
// combineOptions houses the set of functional options that can be used to
// modify the method used to combine the musig2 partial signatures.
type combineOptions struct {
msg [32]byte
combinedKey *btcec.PublicKey
tweakAcc *btcec.ModNScalar
}
// defaultCombineOptions returns the default set of signing operations.
func defaultCombineOptions() *combineOptions {
return &combineOptions{}
}
// WithTweakedCombine is a functional option that allows callers to specify
// that the signature was produced using a tweaked aggregated public key. In
// order to properly aggregate the partial signatures, the caller must specify
// enough information to reconstruct the challenge, and also the final
// accumulated tweak value.
func WithTweakedCombine(msg [32]byte, keys []*btcec.PublicKey,
tweaks []KeyTweakDesc, sort bool) CombineOption {
return func(o *combineOptions) {
combinedKey, _, tweakAcc, _ := AggregateKeys(
keys, sort, WithKeyTweaks(tweaks...),
)
o.msg = msg
o.combinedKey = combinedKey.FinalKey
o.tweakAcc = tweakAcc
}
}
// WithTaprootTweakedCombine is similar to the WithTweakedCombine option, but
// assumes a BIP 341 context where the final tweaked key is to be used as the
// output key, where the internal key is the aggregated key pre-tweak.
//
// This option should be used over WithTweakedCombine when attempting to
// aggregate signatures for a top-level taproot keyspend, where the output key
// commits to a script root.
func WithTaprootTweakedCombine(msg [32]byte, keys []*btcec.PublicKey,
scriptRoot []byte, sort bool) CombineOption {
return func(o *combineOptions) {
combinedKey, _, tweakAcc, _ := AggregateKeys(
keys, sort, WithTaprootKeyTweak(scriptRoot),
)
o.msg = msg
o.combinedKey = combinedKey.FinalKey
o.tweakAcc = tweakAcc
}
}
// WithBip86TweakedCombine is similar to the WithTaprootTweakedCombine option,
// but assumes a BIP 341 + BIP 86 context where the final tweaked key is to be
// used as the output key, where the internal key is the aggregated key
// pre-tweak.
//
// This option should be used over WithTaprootTweakedCombine when attempting to
// aggregate signatures for a top-level taproot keyspend, where the output key
// was generated using BIP 86.
func WithBip86TweakedCombine(msg [32]byte, keys []*btcec.PublicKey,
sort bool) CombineOption {
return func(o *combineOptions) {
combinedKey, _, tweakAcc, _ := AggregateKeys(
keys, sort, WithBIP86KeyTweak(),
)
o.msg = msg
o.combinedKey = combinedKey.FinalKey
o.tweakAcc = tweakAcc
}
}
// CombineSigs combines the set of public keys given the final aggregated
// nonce, and the series of partial signatures for each nonce.
func CombineSigs(combinedNonce *btcec.PublicKey,
partialSigs []*PartialSignature,
combineOpts ...CombineOption) *schnorr.Signature {
// First, parse the set of optional combine options.
opts := defaultCombineOptions()
for _, option := range combineOpts {
option(opts)
}
// If signer keys and tweaks are specified, then we need to carry out
// some intermediate steps before we can combine the signature.
var tweakProduct *btcec.ModNScalar
if opts.combinedKey != nil && opts.tweakAcc != nil {
// Next, we'll construct the parity factor of the combined key,
// negating it if the combined key has an even y coordinate.
parityFactor := new(btcec.ModNScalar).SetInt(1)
combinedKeyBytes := opts.combinedKey.SerializeCompressed()
if combinedKeyBytes[0] == secp.PubKeyFormatCompressedOdd {
parityFactor.Negate()
}
// Next we'll reconstruct e the challenge has based on the
// nonce and combined public key.
// * e = H(tag=ChallengeHashTag, R || Q || m) mod n
var challengeMsg bytes.Buffer
challengeMsg.Write(schnorr.SerializePubKey(combinedNonce))
challengeMsg.Write(schnorr.SerializePubKey(opts.combinedKey))
challengeMsg.Write(opts.msg[:])
challengeBytes := chainhash.TaggedHash(
ChallengeHashTag, challengeMsg.Bytes(),
)
var e btcec.ModNScalar
e.SetByteSlice(challengeBytes[:])
tweakProduct = new(btcec.ModNScalar).Set(&e)
tweakProduct.Mul(opts.tweakAcc).Mul(parityFactor)
}
// Finally, the tweak factor also needs to be re-computed as well.
var combinedSig btcec.ModNScalar
for _, partialSig := range partialSigs {
combinedSig.Add(partialSig.S)
}
// If the tweak product was set above, then we'll need to add the value
// at the very end in order to produce a valid signature under the
// final tweaked key.
if tweakProduct != nil {
combinedSig.Add(tweakProduct)
}
// TODO(roasbeef): less verbose way to get the x coord...
var nonceJ btcec.JacobianPoint
combinedNonce.AsJacobian(&nonceJ)
nonceJ.ToAffine()
return schnorr.NewSignature(&nonceJ.X, &combinedSig)
}