diff --git a/src/StdSecp256k1.sol b/src/StdSecp256k1.sol index 05977e3d..e493bbb1 100644 --- a/src/StdSecp256k1.sol +++ b/src/StdSecp256k1.sol @@ -65,11 +65,12 @@ library StdSecp256k1 { return (GX, GY, 1); } - /// @notice Returns whether the affine point `(x, y)` is on the curve `y² ≡ x³ + 7 (mod P)`. + /// @notice Returns whether the affine point `(x, y)` is on the curve `y² ≡ x³ + 7 (mod P)` + /// with `x` and `y` in `[0, P)`. /// @dev Uses `mulmod`/`addmod` for field arithmetic. function isOnCurve(uint256 x, uint256 y) internal pure returns (bool) { // https://github.com/ethereum/go-ethereum/blob/v1.17.5/crypto/secp256k1/curve.go#L75 - return mulmod(y, y, P) == addmod(mulmod(x, mulmod(x, x, P), P), B, P); + return x < P && y < P && mulmod(y, y, P) == addmod(mulmod(x, mulmod(x, x, P), P), B, P); } /// @notice Returns whether `scalar` is canonically valid (`scalar` in `[0, N)`). diff --git a/test/StdSecp256k1.t.sol b/test/StdSecp256k1.t.sol index eac0756a..07afcb05 100644 --- a/test/StdSecp256k1.t.sol +++ b/test/StdSecp256k1.t.sol @@ -53,6 +53,18 @@ contract StdSecp256k1Test is Test { assertFalse(StdSecp256k1.isIdentityProjective(projectiveX, projectiveY, z)); } + function test_IsOnCurveRejectsNonCanonicalCoordinates() external pure { + uint256 x1 = 1; + uint256 y1 = 0x4218F20AE6C646B363DB68605822FB14264CA8D2587FDD6FBC750D587E76A7EE; + assertTrue(StdSecp256k1.isOnCurve(x1, y1)); + assertFalse(StdSecp256k1.isOnCurve(x1 + StdSecp256k1.P, y1)); + + uint256 x2 = 0x1FE1E5EF3FCEB5C135AB7741333CE5A6E80D68167653F6B2B24BCBCFAAAFF507; + uint256 y2 = 1; + assertTrue(StdSecp256k1.isOnCurve(x2, y2)); + assertFalse(StdSecp256k1.isOnCurve(x2, y2 + StdSecp256k1.P)); + } + function test_ScalarBounds() external pure { assertTrue(StdSecp256k1.isValidScalar(0)); assertFalse(StdSecp256k1.isValidNonZeroScalar(0));