mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 01:43:16 +01:00
0154226233
Since we explicitly keep an old version of a library in lnd for backward compatibility we want to make sure the purpose and version of it is clear and not misleading.
460 lines
15 KiB
Go
460 lines
15 KiB
Go
// Copyright 2013-2022 The btcsuite developers
|
|
|
|
package musig2v040
|
|
|
|
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
|
|
}
|