Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/3-bit #127

Merged
merged 5 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
275 changes: 135 additions & 140 deletions contracts/libs/crypto/ECDSA384.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
* @notice Cryptography module
*
* This library provides functionality for ECDSA verification over any 384-bit curve. Currently,
* this is the most efficient implementation out there, consuming ~8.1 million gas per call.
* this is the most efficient implementation out there, consuming ~8.025 million gas per call.
*
* The approach is Strauss-Shamir double scalar multiplication with 4 bits of precompute + affine coordinates.
* The approach is Strauss-Shamir double scalar multiplication with 6 bits of precompute + affine coordinates.
* For reference, naive implementation uses ~400 billion gas, which is 48000 times more expensive.
dovgopoly marked this conversation as resolved.
Show resolved Hide resolved
*
* We also tried using projective coordinates, however, the gas consumption rose to ~9 million gas.
Expand Down Expand Up @@ -119,8 +119,8 @@
{
uint256 three = U384.init(3);

/// We use 4-bit masks where the first 2 bits refer to `scalar1` and the last 2 bits refer to `scalar2`.
uint256[2][16] memory points_ = _precomputePointsTable(
/// We use 6-bit masks where the first 3 bits refer to `scalar1` and the last 3 bits refer to `scalar2`.
uint256[2][64] memory points_ = _precomputePointsTable(
call,
params_.p,
three,
Expand All @@ -142,6 +142,8 @@
);
}

U384.modAssign(call, scalar1, params_.n);

return U384.eq(scalar1, inputs_.r);
}
}
Expand Down Expand Up @@ -185,7 +187,7 @@
uint256 p,
uint256 three,
uint256 a,
uint256[2][16] memory points,
uint256[2][64] memory points,
uint256 scalar1,
uint256 scalar2
) private view returns (uint256 x, uint256 y) {
Expand All @@ -199,15 +201,32 @@
scalar2Bits_ := mload(scalar2)
}

for (uint256 word = 2; word <= 184; word += 2) {
(x, y) = _qaudAffine(call, p, three, a, x, y);
(x, y) = _twiceAffine(call, p, three, a, x, y);

mask_ = ((scalar1Bits_ >> 183) << 3) | (scalar2Bits_ >> 183);

if (mask_ != 0) {
(x, y) = _addAffine(call, p, three, a, points[mask_][0], points[mask_][1], x, y);

Check warning on line 209 in contracts/libs/crypto/ECDSA384.sol

View check run for this annotation

Codecov / codecov/patch

contracts/libs/crypto/ECDSA384.sol#L209

Added line #L209 was not covered by tests
}

for (uint256 word = 4; word <= 184; word += 3) {
(x, y) = _twice3Affine(call, p, three, a, x, y);

mask_ =
(((scalar1Bits_ >> (184 - word)) & 0x03) << 2) |
((scalar2Bits_ >> (184 - word)) & 0x03);
(((scalar1Bits_ >> (184 - word)) & 0x07) << 3) |
((scalar2Bits_ >> (184 - word)) & 0x07);

if (mask_ != 0) {
(x, y) = _addAffine(call, p, points[mask_][0], points[mask_][1], x, y);
(x, y) = _addAffine(
call,
p,
three,
a,
points[mask_][0],
points[mask_][1],
x,
y
);
}
}

Expand All @@ -216,15 +235,32 @@
scalar2Bits_ := mload(add(scalar2, 0x20))
}

for (uint256 word = 2; word <= 256; word += 2) {
(x, y) = _qaudAffine(call, p, three, a, x, y);
(x, y) = _twiceAffine(call, p, three, a, x, y);

mask_ = ((scalar1Bits_ >> 255) << 3) | (scalar2Bits_ >> 255);

if (mask_ != 0) {
(x, y) = _addAffine(call, p, three, a, points[mask_][0], points[mask_][1], x, y);
}

for (uint256 word = 4; word <= 256; word += 3) {
(x, y) = _twice3Affine(call, p, three, a, x, y);

mask_ =
(((scalar1Bits_ >> (256 - word)) & 0x03) << 2) |
((scalar2Bits_ >> (256 - word)) & 0x03);
(((scalar1Bits_ >> (256 - word)) & 0x07) << 3) |
((scalar2Bits_ >> (256 - word)) & 0x07);

if (mask_ != 0) {
(x, y) = _addAffine(call, p, points[mask_][0], points[mask_][1], x, y);
(x, y) = _addAffine(
call,
p,
three,
a,
points[mask_][0],
points[mask_][1],
x,
y
);
}
}
}
Expand Down Expand Up @@ -268,9 +304,9 @@
}

/**
* @dev Quads an elliptic curve point in affine coordinates.
* @dev Doubles an elliptic curve point 3 times in affine coordinates.
*/
function _qaudAffine(
function _twice3Affine(
uint256 call,
uint256 p,
uint256 three,
Expand Down Expand Up @@ -321,7 +357,24 @@
U384.modmulAssign(call, y1, m1);
U384.modsubAssign(y1, y2, p);

return (x1, y1);
if (U384.eqInteger(y1, 0)) {
return (0, 0);

Check warning on line 361 in contracts/libs/crypto/ECDSA384.sol

View check run for this annotation

Codecov / codecov/patch

contracts/libs/crypto/ECDSA384.sol#L361

Added line #L361 was not covered by tests
}

U384.modexpAssignTo(call, m1, x1, 2);
U384.modmulAssign(call, m1, three);
U384.modaddAssign(m1, a, p);

U384.modshl1AssignTo(m2, y1, p);
U384.moddivAssign(call, m1, m2);

U384.modexpAssignTo(call, x2, m1, 2);
U384.modsubAssign(x2, x1, p);
U384.modsubAssign(x2, x1, p);

U384.modsubAssignTo(y2, x1, x2, p);
U384.modmulAssign(call, y2, m1);
U384.modsubAssign(y2, y1, p);
}
}

Expand All @@ -331,6 +384,8 @@
function _addAffine(
uint256 call,
uint256 p,
uint256 three,
uint256 a,
uint256 x1,
uint256 y1,
uint256 x2,
Expand All @@ -346,6 +401,10 @@
}

if (U384.eq(x1, x2)) {
if (U384.eq(y1, y2)) {
return _twiceAffine(call, p, three, a, x1, y1);
}

return (0, 0);
}

Expand Down Expand Up @@ -373,128 +432,49 @@
uint256 gy,
uint256 hx,
uint256 hy
) private view returns (uint256[2][16] memory points_) {
/// 0b0100: 1G + 0H
(points_[0x04][0], points_[0x04][1]) = (gx.copy(), gy.copy());
/// 0b1000: 2G + 0H
(points_[0x08][0], points_[0x08][1]) = _twiceAffine(
call,
p,
three,
a,
points_[0x04][0],
points_[0x04][1]
);
/// 0b1100: 3G + 0H
(points_[0x0C][0], points_[0x0C][1]) = _addAffine(
call,
p,
points_[0x04][0],
points_[0x04][1],
points_[0x08][0],
points_[0x08][1]
);
/// 0b0001: 0G + 1H
(points_[0x01][0], points_[0x01][1]) = (hx.copy(), hy.copy());
/// 0b0010: 0G + 2H
(points_[0x02][0], points_[0x02][1]) = _twiceAffine(
call,
p,
three,
a,
points_[0x01][0],
points_[0x01][1]
);
/// 0b0011: 0G + 3H
(points_[0x03][0], points_[0x03][1]) = _addAffine(
call,
p,
points_[0x01][0],
points_[0x01][1],
points_[0x02][0],
points_[0x02][1]
);
/// 0b0101: 1G + 1H
(points_[0x05][0], points_[0x05][1]) = _addAffine(
call,
p,
points_[0x04][0],
points_[0x04][1],
points_[0x01][0],
points_[0x01][1]
);
/// 0b0110: 1G + 2H
(points_[0x06][0], points_[0x06][1]) = _addAffine(
call,
p,
points_[0x04][0],
points_[0x04][1],
points_[0x02][0],
points_[0x02][1]
);
/// 0b0111: 1G + 3H
(points_[0x07][0], points_[0x07][1]) = _addAffine(
call,
p,
points_[0x04][0],
points_[0x04][1],
points_[0x03][0],
points_[0x03][1]
);
/// 0b1001: 2G + 1H
(points_[0x09][0], points_[0x09][1]) = _addAffine(
call,
p,
points_[0x08][0],
points_[0x08][1],
points_[0x01][0],
points_[0x01][1]
);
/// 0b1010: 2G + 2H
(points_[0x0A][0], points_[0x0A][1]) = _addAffine(
call,
p,
points_[0x08][0],
points_[0x08][1],
points_[0x02][0],
points_[0x02][1]
);
/// 0b1011: 2G + 3H
(points_[0x0B][0], points_[0x0B][1]) = _addAffine(
call,
p,
points_[0x08][0],
points_[0x08][1],
points_[0x03][0],
points_[0x03][1]
);
/// 0b1101: 3G + 1H
(points_[0x0D][0], points_[0x0D][1]) = _addAffine(
call,
p,
points_[0x0C][0],
points_[0x0C][1],
points_[0x01][0],
points_[0x01][1]
);
/// 0b1110: 3G + 2H
(points_[0x0E][0], points_[0x0E][1]) = _addAffine(
call,
p,
points_[0x0C][0],
points_[0x0C][1],
points_[0x02][0],
points_[0x02][1]
);
/// 0b1111: 3G + 3H
(points_[0x0F][0], points_[0x0F][1]) = _addAffine(
call,
p,
points_[0x0C][0],
points_[0x0C][1],
points_[0x03][0],
points_[0x03][1]
);
) private view returns (uint256[2][64] memory points_) {
unchecked {
(points_[0x01][0], points_[0x01][1]) = (hx.copy(), hy.copy());
(points_[0x08][0], points_[0x08][1]) = (gx.copy(), gy.copy());

for (uint256 i = 0; i < 8; ++i) {
for (uint256 j = 0; j < 8; ++j) {
if (i + j < 2) {
continue;
}

uint256 maskTo = (i << 3) | j;

if (i != 0) {
uint256 maskFrom = ((i - 1) << 3) | j;

(points_[maskTo][0], points_[maskTo][1]) = _addAffine(
call,
p,
three,
a,
points_[maskFrom][0],
points_[maskFrom][1],
gx,
gy
);
} else {
uint256 maskFrom = (i << 3) | (j - 1);

(points_[maskTo][0], points_[maskTo][1]) = _addAffine(
call,
p,
three,
a,
points_[maskFrom][0],
points_[maskFrom][1],
hx,
hy
);
}
}
}
}
}
}

Expand Down Expand Up @@ -657,6 +637,21 @@
}
}

function modAssign(uint256 call_, uint256 a_, uint256 m_) internal view {
assembly {
mstore(call_, 0x40)
mstore(add(0x20, call_), 0x20)
mstore(add(0x40, call_), 0x40)
mstore(add(0x60, call_), mload(a_))
mstore(add(0x80, call_), mload(add(a_, 0x20)))
mstore(add(0xA0, call_), 0x01)
mstore(add(0xC0, call_), mload(m_))
mstore(add(0xE0, call_), mload(add(m_, 0x20)))

pop(staticcall(gas(), 0x5, call_, 0x0100, a_, 0x40))
}
}

function modexp(
uint256 call_,
uint256 b_,
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@solarity/solidity-lib",
"version": "2.7.14",
"version": "2.7.15",
"license": "MIT",
"author": "Distributed Lab",
"readme": "README.md",
Expand Down
Loading