mirror of
https://github.com/btcsuite/btcd.git
synced 2024-11-19 01:40:07 +01:00
btcec/schnorr/musig2: Allow infinity nonces
This commit updates the musig2 module to allow infinity nonces, as per Musig2 0.4.0.
This commit is contained in:
parent
4b2fe9f83e
commit
44eb8c64f8
@ -4,6 +4,8 @@
|
||||
package btcec
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
secp "github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
)
|
||||
|
||||
@ -11,6 +13,9 @@ import (
|
||||
// Jacobian projective coordinates and thus represents a point on the curve.
|
||||
type JacobianPoint = secp.JacobianPoint
|
||||
|
||||
// infinityPoint is the jacobian representation of the point at infinity.
|
||||
var infinityPoint JacobianPoint
|
||||
|
||||
// MakeJacobianPoint returns a Jacobian point with the provided X, Y, and Z
|
||||
// coordinates.
|
||||
func MakeJacobianPoint(x, y, z *FieldVal) JacobianPoint {
|
||||
@ -61,3 +66,50 @@ func ScalarBaseMultNonConst(k *ModNScalar, result *JacobianPoint) {
|
||||
func ScalarMultNonConst(k *ModNScalar, point, result *JacobianPoint) {
|
||||
secp.ScalarMultNonConst(k, point, result)
|
||||
}
|
||||
|
||||
// ParseJacobian parses a byte slice point as a secp.Publickey and returns the
|
||||
// pubkey as a JacobianPoint. If the nonce is a zero slice, the infinityPoint
|
||||
// is returned.
|
||||
func ParseJacobian(point []byte) (JacobianPoint, error) {
|
||||
var result JacobianPoint
|
||||
|
||||
if len(point) != 33 {
|
||||
str := fmt.Sprintf("invalid nonce: invalid length: %v",
|
||||
len(point))
|
||||
return JacobianPoint{}, makeError(secp.ErrPubKeyInvalidLen, str)
|
||||
}
|
||||
|
||||
if point[0] == 0x00 {
|
||||
return infinityPoint, nil
|
||||
}
|
||||
|
||||
noncePk, err := secp.ParsePubKey(point)
|
||||
if err != nil {
|
||||
return JacobianPoint{}, err
|
||||
}
|
||||
noncePk.AsJacobian(&result)
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// JacobianToByteSlice converts the passed JacobianPoint to a Pubkey
|
||||
// and serializes that to a byte slice. If the JacobianPoint is the infinity
|
||||
// point, a zero slice is returned.
|
||||
func JacobianToByteSlice(point JacobianPoint) []byte {
|
||||
if point.X == infinityPoint.X && point.Y == infinityPoint.Y {
|
||||
return make([]byte, 33)
|
||||
}
|
||||
|
||||
point.ToAffine()
|
||||
|
||||
return NewPublicKey(
|
||||
&point.X, &point.Y,
|
||||
).SerializeCompressed()
|
||||
}
|
||||
|
||||
// GeneratorJacobian sets the passed JacobianPoint to the Generator Point.
|
||||
func GeneratorJacobian(jacobian *JacobianPoint) {
|
||||
var k ModNScalar
|
||||
k.SetInt(1)
|
||||
ScalarBaseMultNonConst(&k, jacobian)
|
||||
}
|
||||
|
@ -17,3 +17,8 @@ type Error = secp.Error
|
||||
// errors.As, so the caller can directly check against an error kind when
|
||||
// determining the reason for an error.
|
||||
type ErrorKind = secp.ErrorKind
|
||||
|
||||
// makeError creates an secp.Error given a set of arguments.
|
||||
func makeError(kind ErrorKind, desc string) Error {
|
||||
return Error{Err: kind, Description: desc}
|
||||
}
|
||||
|
@ -1461,7 +1461,7 @@ func TestMusig2AggregateNoncesTestVectors(t *testing.T) {
|
||||
},
|
||||
expectedNonce: append(
|
||||
append([]byte{}, expectedNonce[0:33]...),
|
||||
getGBytes()...,
|
||||
getInfinityBytes()...,
|
||||
),
|
||||
},
|
||||
}
|
||||
@ -1829,6 +1829,10 @@ func getNegGBytes() []byte {
|
||||
return pk
|
||||
}
|
||||
|
||||
func getInfinityBytes() []byte {
|
||||
return make([]byte, 33)
|
||||
}
|
||||
|
||||
func mustParseHex32(str string) [32]byte {
|
||||
b, err := hex.DecodeString(str)
|
||||
if err != nil {
|
||||
|
@ -331,7 +331,7 @@ func AggregateNonces(pubNonces [][PubNonceSize]byte) ([PubNonceSize]byte, error)
|
||||
// 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.PublicKey, error) {
|
||||
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))
|
||||
@ -339,14 +339,12 @@ func AggregateNonces(pubNonces [][PubNonceSize]byte) ([PubNonceSize]byte, error)
|
||||
// Using the slicer, extract just the bytes we need to
|
||||
// decode.
|
||||
var nonceJ btcec.JacobianPoint
|
||||
pubNonce, err := btcec.ParsePubKey(
|
||||
slicer(pubNonceBytes),
|
||||
)
|
||||
|
||||
nonceJ, err := btcec.ParseJacobian(slicer(pubNonceBytes))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return btcec.JacobianPoint{}, err
|
||||
}
|
||||
|
||||
pubNonce.AsJacobian(&nonceJ)
|
||||
pubNonceJs[i] = &nonceJ
|
||||
}
|
||||
|
||||
@ -359,27 +357,8 @@ func AggregateNonces(pubNonces [][PubNonceSize]byte) ([PubNonceSize]byte, error)
|
||||
)
|
||||
}
|
||||
|
||||
// Now that we've aggregated all the points, we need to check
|
||||
// if this point is the point at infinity, if so, then we'll
|
||||
// just return the generator. At a later step, the malicious
|
||||
// party will be detected.
|
||||
if aggregateNonce == infinityPoint {
|
||||
// TODO(roasbeef): better way to get the generator w/
|
||||
// the new API? -- via old curve params instead?
|
||||
var generator btcec.JacobianPoint
|
||||
one := new(btcec.ModNScalar).SetInt(1)
|
||||
btcec.ScalarBaseMultNonConst(one, &generator)
|
||||
|
||||
generator.ToAffine()
|
||||
return btcec.NewPublicKey(
|
||||
&generator.X, &generator.Y,
|
||||
), nil
|
||||
}
|
||||
|
||||
aggregateNonce.ToAffine()
|
||||
return btcec.NewPublicKey(
|
||||
&aggregateNonce.X, &aggregateNonce.Y,
|
||||
), nil
|
||||
return aggregateNonce, nil
|
||||
}
|
||||
|
||||
// The final nonce public nonce is actually two nonces, one that
|
||||
@ -392,6 +371,7 @@ func AggregateNonces(pubNonces [][PubNonceSize]byte) ([PubNonceSize]byte, error)
|
||||
if err != nil {
|
||||
return finalNonce, err
|
||||
}
|
||||
|
||||
combinedNonce2, err := combineNonces(func(n [PubNonceSize]byte) []byte {
|
||||
return n[btcec.PubKeyBytesLenCompressed:]
|
||||
})
|
||||
@ -399,10 +379,10 @@ func AggregateNonces(pubNonces [][PubNonceSize]byte) ([PubNonceSize]byte, error)
|
||||
return finalNonce, err
|
||||
}
|
||||
|
||||
copy(finalNonce[:], combinedNonce1.SerializeCompressed())
|
||||
copy(finalNonce[:], btcec.JacobianToByteSlice(combinedNonce1))
|
||||
copy(
|
||||
finalNonce[btcec.PubKeyBytesLenCompressed:],
|
||||
combinedNonce2.SerializeCompressed(),
|
||||
btcec.JacobianToByteSlice(combinedNonce2),
|
||||
)
|
||||
|
||||
return finalNonce, nil
|
||||
|
@ -200,20 +200,6 @@ func Sign(secNonce [SecNonceSize]byte, privKey *btcec.PrivateKey,
|
||||
option(opts)
|
||||
}
|
||||
|
||||
// Next, we'll parse the public nonces into R1 and R2.
|
||||
r1, err := btcec.ParsePubKey(
|
||||
combinedNonce[:btcec.PubKeyBytesLenCompressed],
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r2, err := btcec.ParsePubKey(
|
||||
combinedNonce[btcec.PubKeyBytesLenCompressed:],
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 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)
|
||||
@ -259,19 +245,31 @@ func Sign(secNonce [SecNonceSize]byte, privKey *btcec.PrivateKey,
|
||||
)
|
||||
nonceBlinder.SetByteSlice(nonceBlindHash[:])
|
||||
|
||||
var nonce, r1J, r2J btcec.JacobianPoint
|
||||
r1.AsJacobian(&r1J)
|
||||
r2.AsJacobian(&r2J)
|
||||
// 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 {
|
||||
return nil, ErrNoncePointAtInfinity
|
||||
G := btcec.Generator()
|
||||
G.AsJacobian(&nonce)
|
||||
}
|
||||
|
||||
// Next we'll parse out our two secret nonces, which we'll be using in
|
||||
@ -375,6 +373,7 @@ func (p *PartialSignature) Verify(pubNonce [PubNonceSize]byte,
|
||||
signingKey *btcec.PublicKey, msg [32]byte, signOpts ...SignOption) bool {
|
||||
|
||||
pubKey := schnorr.SerializePubKey(signingKey)
|
||||
|
||||
return verifyPartialSig(
|
||||
p, pubNonce, combinedNonce, keySet, pubKey, msg, signOpts...,
|
||||
) == nil
|
||||
@ -399,19 +398,6 @@ func verifyPartialSig(partialSig *PartialSignature, pubNonce [PubNonceSize]byte,
|
||||
// Next we'll parse out the two public nonces into something we can
|
||||
// use.
|
||||
//
|
||||
// TODO(roasbeef): consolidate, new method
|
||||
r1, err := btcec.ParsePubKey(
|
||||
combinedNonce[:btcec.PubKeyBytesLenCompressed],
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r2, err := btcec.ParsePubKey(
|
||||
combinedNonce[btcec.PubKeyBytesLenCompressed:],
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 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.
|
||||
@ -456,45 +442,61 @@ func verifyPartialSig(partialSig *PartialSignature, pubNonce [PubNonceSize]byte,
|
||||
nonceBlindHash := chainhash.TaggedHash(NonceBlindTag, nonceMsgBuf.Bytes())
|
||||
nonceBlinder.SetByteSlice(nonceBlindHash[:])
|
||||
|
||||
var nonce, r1J, r2J btcec.JacobianPoint
|
||||
r1.AsJacobian(&r1J)
|
||||
r2.AsJacobian(&r2J)
|
||||
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.
|
||||
pubNonce1, err := btcec.ParsePubKey(
|
||||
pubNonce1J, err := btcec.ParseJacobian(
|
||||
pubNonce[:btcec.PubKeyBytesLenCompressed],
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pubNonce2, err := btcec.ParsePubKey(
|
||||
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, pubNonce1J, pubNonce2J btcec.JacobianPoint
|
||||
pubNonce1.AsJacobian(&pubNonce1J)
|
||||
pubNonce2.AsJacobian(&pubNonce2J)
|
||||
var pubNonceJ btcec.JacobianPoint
|
||||
|
||||
btcec.ScalarMultNonConst(&nonceBlinder, &pubNonce2J, &pubNonce2J)
|
||||
btcec.AddNonConst(&pubNonce1J, &pubNonce2J, &pubNonceJ)
|
||||
|
||||
nonce.ToAffine()
|
||||
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.ToAffine()
|
||||
pubNonceJ.Y.Negate(1)
|
||||
pubNonceJ.Y.Normalize()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user