Active Wintergreen Porpoise
High
Incorrect Y Coordinate Check will cause signature verification to fail unexpectedly for legitimate signatures.
The misplaced Y coordinate check in encVerify will cause signature verification to fail unexpectedly for legitimate signatures as the check is performed on the wrong point.
func encVerify(
sig *AdaptorSignature,
m []byte,
pubKeyBytes []byte,
t *btcec.JacobianPoint,
) error {
// Fail if m is not 32 bytes
if len(m) != chainhash.HashSize {
return fmt.Errorf("wrong size for message (got %v, want %v)",
len(m), chainhash.HashSize)
}
// R' = R-T (or R+T if it needs negation)
R := &sig.r // NOTE: R is an affine point
var RHat btcec.JacobianPoint
if sig.needNegation {
btcec.AddNonConst(R, t, &RHat)
} else {
btcec.AddNonConst(R, negatePoint(t), &RHat)
}
RHat.ToAffine()
...
expRHat.ToAffine()
if R.Y.IsOdd() { //-----------> The misplaced Y coordinate check
return fmt.Errorf("expected R.y is odd")
}
In encVerify the Y coordinate check if RHat.Y.IsOdd() is performed on RHat before the point R is checked. The correct order is to check R first, then RHat after the affine conversion.
- A valid adaptor signature needs to be generated.
- The encVerify function needs to be called with this signature.
None
- A valid adaptor signature is generated for a given message and public key.
- The encVerify function is called to verify the signature.
- The encVerify function checks RHat.Y.IsOdd()
- If RHat.Y happens to be odd, the verification will fail even if the signature is valid.
Legitimate users will be unable to have their signatures verified, preventing them from performing actions that require a valid signature.
No response
Moved the R.Y.IsOdd()check to RHat.Y.IsOdd() and placed it after RHat.ToAffine()
func encVerify(
sig *AdaptorSignature,
m []byte,
pubKeyBytes []byte,
t *btcec.JacobianPoint,
) error {
// Fail if m is not 32 bytes
if len(m) != chainhash.HashSize {
return fmt.Errorf("wrong size for message (got %v, want %v)",
len(m), chainhash.HashSize)
}
// R' = R-T (or R+T if it needs negation)
R := &sig.r // NOTE: R is an affine point
var RHat btcec.JacobianPoint
if sig.needNegation {
btcec.AddNonConst(R, t, &RHat)
} else {
btcec.AddNonConst(R, negatePoint(t), &RHat)
}
RHat.ToAffine()
// Check if RHat.Y is odd after converting to affine coordinates
if RHat.Y.IsOdd() {
return fmt.Errorf("expected RHat.y is odd")
}
// P = lift_x(int(pk))
pubKey, err := schnorr.ParsePubKey(pubKeyBytes)
if err != nil {
return err
}
// Fail if P is not a point on the curve
if !pubKey.IsOnCurve() {
return fmt.Errorf("pubkey point is not on curve")
}
// e = int(tagged_hash("BIP0340/challenge", bytes(R) || bytes(P) || M)) mod n.
var rBytes [chainhash.HashSize]byte
R.X.PutBytesUnchecked(rBytes[:])
pBytes := schnorr.SerializePubKey(pubKey)
commitment := chainhash.TaggedHash(
chainhash.TagBIP0340Challenge, rBytes[:], pBytes, m,
)
var e btcec.ModNScalar
e.SetBytes((*[ModNScalarSize]byte)(commitment))
// Negate e here so we can use AddNonConst below to subtract the s'*G
// point from e*P.
e.Negate()
// expected R' = s'*G - e*P
var P, expRHat, sHatG, eP btcec.JacobianPoint
pubKey.AsJacobian(&P)
btcec.ScalarBaseMultNonConst(&sig.sHat, &sHatG) // s'*G
btcec.ScalarMultNonConst(&e, &P, &eP) // -e*P
btcec.AddNonConst(&sHatG, &eP, &expRHat) // R' = s'*G-e*P
// Fail if expected R' is the point at infinity
if (expRHat.X.IsZero() && expRHat.Y.IsZero()) || expRHat.Z.IsZero() {
return fmt.Errorf("expected R' point is at infinity")
}
expRHat.ToAffine()
// ensure R' is same as the expected R' = s'*G - e*P
if !expRHat.X.Equals(&RHat.X) {
return fmt.Errorf("expected R' = s'*G - e*P is different from the actual R'")
}
return nil
}