Skip to content

Commit

Permalink
tests: Add noble-curves secp256k1 point validity and de/encoding test…
Browse files Browse the repository at this point in the history
… vectors (#32)
  • Loading branch information
obatirou authored Oct 15, 2024
1 parent 9548f31 commit f7d6034
Show file tree
Hide file tree
Showing 4 changed files with 10,977 additions and 0 deletions.
2 changes: 2 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ src = 'src'
out = 'out'
libs = ['lib']
ffi = true
# Permissions
fs_permissions = [{ access = "read", path = "./test" }]

# Compilation
evm_version = "shanghai"
Expand Down
4 changes: 4 additions & 0 deletions src/onchain/secp256k1/Secp256k1Arithmetic.sol
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ library Secp256k1Arithmetic {
///
/// @dev Note that the identity is on the curve.
function isOnCurve(Point memory point) internal pure returns (bool) {
if (point.x >= P || point.y >= P) {
return false;
}

if (point.isIdentity()) {
return true;
}
Expand Down
156 changes: 156 additions & 0 deletions test/onchain/secp256k1/Secp256k1Arithmetic.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity ^0.8.16;

import {Test} from "forge-std/Test.sol";
import {console2 as console} from "forge-std/console2.sol";
import {stdJson} from "forge-std/StdJson.sol";

import {Secp256k1Offchain} from "src/offchain/secp256k1/Secp256k1Offchain.sol";
import {
Expand All @@ -29,6 +30,7 @@ contract Secp256k1ArithmeticTest is Test {
using Secp256k1 for Point;
using Secp256k1Arithmetic for Point;
using Secp256k1Arithmetic for ProjectivePoint;
using stdJson for string;

// Uncompressed Generator G.
// Copied from [SEC-2 v2].
Expand Down Expand Up @@ -108,6 +110,56 @@ contract Secp256k1ArithmeticTest is Test {
assertFalse(wrapper.isOnCurve(point));
}

struct IsPointCase {
string P;
bool expected;
}

function testVectorsNobleCurves_Point_isPoint() public {
string memory root = vm.projectRoot();
string memory path = string.concat(
root, "/test/onchain/secp256k1/test-vectors/points.json"
);
string memory json = vm.readFile(path);
bytes memory data = json.parseRaw(".valid.isPoint");
IsPointCase[] memory cases = abi.decode(data, (IsPointCase[]));
for (uint i; i < cases.length; i++) {
IsPointCase memory c = cases[i];
bytes memory parsedPoint = vm.parseBytes(c.P);
Point memory point;
bool expectedOnCurve = c.expected;
// If normal encoded.
if (parsedPoint[0] == 0x04) {
if (expectedOnCurve) {
point = wrapper.pointFromEncoded(parsedPoint);
assertEq(wrapper.isOnCurve(point), expectedOnCurve);
continue;
} else {
vm.expectRevert();
wrapper.pointFromEncoded(parsedPoint);
continue;
}
}
// If compressed encoded.
if (parsedPoint[0] == 0x02 || parsedPoint[0] == 0x03) {
if (expectedOnCurve) {
point = wrapper.pointFromCompressedEncoded(parsedPoint);
assertEq(wrapper.isOnCurve(point), expectedOnCurve);
continue;
} else {
vm.expectRevert();
wrapper.pointFromCompressedEncoded(parsedPoint);
continue;
}
}
// Otherwise invalid.
vm.expectRevert();
wrapper.pointFromEncoded(parsedPoint);
vm.expectRevert();
wrapper.pointFromCompressedEncoded(parsedPoint);
}
}

// -- yParity

function testFuzz_Point_yParity(uint x, uint y) public view {
Expand Down Expand Up @@ -238,6 +290,53 @@ contract Secp256k1ArithmeticTest is Test {
}
}

struct PointAddCase {
string P;
string Q;
string expected;
}

function testVectorsNobleCurves_ProjectivePoint_add() public view {
string memory root = vm.projectRoot();
string memory path = string.concat(
root, "/test/onchain/secp256k1/test-vectors/points.json"
);
string memory json = vm.readFile(path);
bytes memory data = json.parseRaw(".valid.pointAdd");
PointAddCase[] memory cases = abi.decode(data, (PointAddCase[]));
for (uint i; i < cases.length; i++) {
PointAddCase memory c = cases[i];
bytes memory parsedP = vm.parseBytes(c.P);
bytes memory parsedQ = vm.parseBytes(c.Q);
bytes memory parsedExpected;
try vm.parseBytes(c.expected) returns (bytes memory parsedTry) {
parsedExpected = parsedTry;
} catch {
// "description": "1 + -1 == 0/Infinity",
// "P": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
// "Q": "0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
// "expected": null
assertEq(
parsedP,
hex"0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"
);
assertEq(
parsedQ,
hex"0379BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"
);
parsedExpected = hex"00";
}
ProjectivePoint memory p =
wrapper.pointFromCompressedEncoded(parsedP).toProjectivePoint();
ProjectivePoint memory q =
wrapper.pointFromCompressedEncoded(parsedQ).toProjectivePoint();
Point memory expected =
wrapper.pointFromCompressedEncoded(parsedExpected);
Point memory got = wrapper.add(p, q).intoPoint();
assertTrue(got.eq(expected));
}
}

function test_ProjectivePoint_add_Identity() public view {
ProjectivePoint memory g = Secp256k1Arithmetic.G().toProjectivePoint();
ProjectivePoint memory id = Secp256k1Arithmetic.ProjectiveIdentity();
Expand Down Expand Up @@ -278,6 +377,35 @@ contract Secp256k1ArithmeticTest is Test {
assertTrue(want.eq(got));
}

struct PointMulCase {
string P;
string d;
string description;
string expected;
}

function testVectorsNobleCurves_ProjectivePoint_mul() public view {
string memory root = vm.projectRoot();
string memory path = string.concat(
root, "/test/onchain/secp256k1/test-vectors/points.json"
);
string memory json = vm.readFile(path);
bytes memory data = json.parseRaw(".valid.pointMultiply");
PointMulCase[] memory cases = abi.decode(data, (PointMulCase[]));
for (uint i; i < cases.length; i++) {
PointMulCase memory c = cases[i];
bytes memory parsedP = vm.parseBytes(c.P);
uint parsedD = vm.parseUint(c.d);
bytes memory parsedExpected = vm.parseBytes(c.expected);
Point memory expected =
wrapper.pointFromCompressedEncoded(parsedExpected);
ProjectivePoint memory p =
wrapper.pointFromCompressedEncoded(parsedP).toProjectivePoint();
Point memory got = wrapper.mul(p, parsedD).intoPoint();
assertTrue(got.eq(expected));
}
}

function testVectors_ProjectivePoint_mul() public view {
ProjectivePoint memory g = Secp256k1Arithmetic.G().toProjectivePoint();

Expand Down Expand Up @@ -610,6 +738,34 @@ contract Secp256k1ArithmeticTest is Test {
// byte encoding.
}

struct InvalidPointCase {
string P;
bool compress;
string description;
string exception;
}

function testVectorsNobleCurves_Point_invalid() public {
string memory root = vm.projectRoot();
string memory path = string.concat(
root, "/test/onchain/secp256k1/test-vectors/points.json"
);
string memory json = vm.readFile(path);
bytes memory data = json.parseRaw(".invalid.pointCompress");
InvalidPointCase[] memory cases = abi.decode(data, (InvalidPointCase[]));
for (uint i; i < cases.length; i++) {
InvalidPointCase memory c = cases[i];
bytes memory parsedP = vm.parseBytes(c.P);
if (parsedP.length != 33) {
vm.expectRevert();
wrapper.pointFromEncoded(parsedP);
} else {
vm.expectRevert();
wrapper.pointFromCompressedEncoded(parsedP);
}
}
}

function test_Point_toCompressedEncoded_IfyParityEven() public view {
// Some point, ie [2]G.
Point memory point = Point({
Expand Down
Loading

0 comments on commit f7d6034

Please sign in to comment.