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

Implement P256 verification via RIP-7212 precompile with Solidity fallback #4881

Open
wants to merge 81 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
5e82076
Add P256 implementation and testing
Amxx Feb 7, 2024
da0f27e
enable optimizations by default
Amxx Feb 7, 2024
aa59c67
test recovering address
Amxx Feb 7, 2024
9512947
improved testing
Amxx Feb 7, 2024
a60bf48
spelling
Amxx Feb 7, 2024
9185026
fix lint
Amxx Feb 7, 2024
025e360
expose imports tick
Amxx Feb 7, 2024
803e735
fix lint
Amxx Feb 7, 2024
57fcecd
fix lint
Amxx Feb 7, 2024
4dae298
add changeset
Amxx Feb 7, 2024
6cf039d
improve doc
Amxx Feb 7, 2024
c094fa1
add envvar to force allowUnlimitedContractSize
Amxx Feb 7, 2024
20a03df
fix lint
Amxx Feb 7, 2024
15f1a6b
fix stack too deep error in coverage
Amxx Feb 7, 2024
e2040e4
reoder arguments to match ecrecover and EIP-7212
Amxx Feb 13, 2024
695b732
reduce diff
Amxx Mar 13, 2024
41aaf71
Merge branch 'master' into feature/P256
Amxx Mar 13, 2024
3bf4557
Update contracts/utils/cryptography/P256.sol
Amxx Apr 24, 2024
3cbf426
Merge branch 'master' into feature/P256
Amxx Apr 25, 2024
bba7fa3
update pseudocode reference
Amxx Apr 25, 2024
2812ed8
Update contracts/utils/cryptography/P256.sol
Amxx Apr 25, 2024
e0ef63b
refactor neutral element in jAdd
Amxx Apr 26, 2024
61a244d
add EIP-7212 support
Amxx May 17, 2024
910bc71
Merge branch 'master' into feature/P256
Amxx Jun 12, 2024
2e9d04d
Apply PR suggestions
ernestognw Jun 14, 2024
9062633
move invModPrime to Math.sol
Amxx Jun 17, 2024
a44bb71
update
Amxx Jun 17, 2024
3a6e1f5
update
Amxx Jun 17, 2024
3e71fad
codespell
Amxx Jun 17, 2024
887272b
test signature maleability
Amxx Jun 17, 2024
433548f
Iterate
ernestognw Jun 20, 2024
4f80ca0
Add more comments
ernestognw Jun 21, 2024
be69f5c
remove P256 public key to address derivation
Amxx Jun 21, 2024
fcde23f
Move publicKey from privateKey derivation function to tests
ernestognw Jun 21, 2024
5828566
Remove unnecessary test
ernestognw Jun 21, 2024
9362936
add wycheproof test
cairoeth Jun 21, 2024
921745b
Readd malleability check and rename
ernestognw Jun 22, 2024
2c113f4
Change arguments to bytes32
ernestognw Jun 22, 2024
fb7dc6f
remove unused malleable version
Amxx Jun 24, 2024
f264dae
Update contracts/utils/cryptography/P256.sol
Amxx Jun 24, 2024
2c9a137
Update contracts/utils/cryptography/P256.sol
Amxx Jun 24, 2024
f4cbf51
up
Amxx Jun 24, 2024
0227656
recovery malleability
Amxx Jun 24, 2024
e3a8338
fix bug (inverse return values)
Amxx Jun 24, 2024
cbd2ff5
better private key gen
cairoeth Jun 24, 2024
194f19a
Update contracts/utils/cryptography/P256.sol
ernestognw Jun 24, 2024
704a12e
Fix hardhat tests and add documentation
ernestognw Jun 24, 2024
61d52a5
Update test/utils/cryptography/P256.test.js
Amxx Jun 24, 2024
242c796
Ensure lower s in Foundry tests
ernestognw Jun 24, 2024
787834d
Lint
ernestognw Jun 24, 2024
d8f4f7e
fix bug for valid signatures with large `r` values
cairoeth Jun 24, 2024
fc54017
run original wycheproof in hardhat
Amxx Jun 24, 2024
5a7887b
Merge remote-tracking branch 'amxx/feature/P256' into feature/P256
Amxx Jun 24, 2024
cc82c17
Update test/utils/cryptography/P256.test.js
Amxx Jun 24, 2024
a67e5a2
Almost fix tests
ernestognw Jun 24, 2024
4c93009
Bound r to N so for lower s values
ernestognw Jun 24, 2024
046463c
Remove unnecessary comment
ernestognw Jun 24, 2024
e4df1d1
Remove foundry wycheproof
ernestognw Jun 24, 2024
1bddcf5
Tests nit
ernestognw Jun 24, 2024
e5ba358
Update .changeset/odd-lobsters-wash.md
ernestognw Jun 24, 2024
2eecacf
Update test/utils/cryptography/P256.t.sol
ernestognw Jun 24, 2024
ced4fb8
Update P256.t.sol
Amxx Jun 24, 2024
b82af11
Merge branch 'master' into feature/P256
ernestognw Jun 24, 2024
c6a86d9
Add more docs and nit
ernestognw Jun 24, 2024
9b24014
Manage to compile without via-ir
ernestognw Jun 25, 2024
3616771
Improve comments
ernestognw Jun 25, 2024
be078b1
Remove unnecessary CI flag
ernestognw Jun 25, 2024
ecd3aa2
cleanup _jAdd with memory
Amxx Jun 25, 2024
d83e707
up
Amxx Jun 25, 2024
fbc11f5
Update contracts/utils/cryptography/P256.sol
Amxx Jun 25, 2024
9c88101
Apply suggestions from code review
Amxx Jun 25, 2024
b5e6bd7
Update hardhat.config.js
Amxx Jun 25, 2024
db76353
Update hardhat.config.js
Amxx Jun 25, 2024
0722d93
Update hardhat.config.js
Amxx Jun 25, 2024
306a5f6
Revert all changes to hardhat.config.js
Amxx Jun 26, 2024
e67a456
uniform style
Amxx Jun 26, 2024
1a8cb63
add bound checks to isOnCurve
Amxx Jun 26, 2024
3c3fa27
rename isOnCurve -> isValidPublicKey + add _isProperSignature helper
Amxx Jun 26, 2024
2fe4a16
Update contracts/utils/cryptography/P256.sol
ernestognw Jun 27, 2024
2420d13
Update contracts/utils/cryptography/P256.sol
ernestognw Jul 1, 2024
49f3ad9
Merge branch 'master' into feature/P256
ernestognw Jul 2, 2024
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
5 changes: 5 additions & 0 deletions .changeset/odd-lobsters-wash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openzeppelin-solidity': minor
---

`P256`: Add a library for verification/recovery of Secp256r1 (Aka P256) signatures.
ernestognw marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 2 additions & 0 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ jobs:
run: bash scripts/upgradeable/transpile.sh
- name: Run tests
run: npm run test
env:
UNLIMITED: true
- name: Check linearisation of the inheritance graph
run: npm run test:inheritance
- name: Check storage layout
Expand Down
1 change: 1 addition & 0 deletions contracts/mocks/Stateless.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {ERC721Holder} from "../token/ERC721/utils/ERC721Holder.sol";
import {Math} from "../utils/math/Math.sol";
import {MerkleProof} from "../utils/cryptography/MerkleProof.sol";
import {MessageHashUtils} from "../utils/cryptography/MessageHashUtils.sol";
import {P256} from "../utils/cryptography/P256.sol";
import {Packing} from "../utils/Packing.sol";
import {SafeCast} from "../utils/math/SafeCast.sol";
import {SafeERC20} from "../token/ERC20/utils/SafeERC20.sol";
Expand Down
317 changes: 317 additions & 0 deletions contracts/utils/cryptography/P256.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,317 @@
// SPDX-License-Identifier: GPL-3.0
Amxx marked this conversation as resolved.
Show resolved Hide resolved
pragma solidity ^0.8.20;

import {Math} from "../math/Math.sol";

/**
* @dev Implementation of secp256r1 verification and recovery functions.
*
* Based on
* - https://github.com/itsobvioustech/aa-passkeys-wallet/blob/main/src/Secp256r1.sol
* Which is heavily inspired from
* - https://github.com/maxrobot/elliptic-solidity/blob/master/contracts/Secp256r1.sol
* - https://github.com/tdrerup/elliptic-curve-solidity/blob/master/contracts/curves/EllipticCurve.sol
ernestognw marked this conversation as resolved.
Show resolved Hide resolved
*/
library P256 {
struct JPoint {
uint256 x;
uint256 y;
uint256 z;
}

/// @dev Generator (x component)
uint256 internal constant GX = 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296;
/// @dev Generator (y component)
uint256 internal constant GY = 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5;
/// @dev P (size of the field)
uint256 internal constant P = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF;
/// @dev N (order of G)
uint256 internal constant N = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551;
/// @dev A parameter of the weierstrass equation
uint256 internal constant A = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC;
/// @dev B parameter of the weierstrass equation
uint256 internal constant B = 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B;
ernestognw marked this conversation as resolved.
Show resolved Hide resolved

uint256 private constant P2 = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFD;
uint256 private constant N2 = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63254F;
uint256 private constant P1DIV4 = 0x3fffffffc0000000400000000000000000000000400000000000000000000000;

/**
* @dev signature verification
* @param h - hashed message
* @param r - signature half R
* @param s - signature half S
* @param qx - public key coordinate X
* @param qy - public key coordinate Y
*/
function verify(uint256 h, uint256 r, uint256 s, uint256 qx, uint256 qy) internal view returns (bool) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason for representing the hash as uint256? I think we're generally more used to bytes32

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No real reason. All params could be either uint256 or bytes32.

I think that is since the verification is math heavy, we'll need to cast everything into uint256 anyway.

Copy link
Member

@ernestognw ernestognw Jun 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My concern is that users may need to cast in the frontend using Javascript where the encoding can be a mess. Although it's trivial to cast using ethers or viem, I don't think it's as straightforward as doing it natively.

I think that is since the verification is math heavy, we'll need to cast everything into uint256 anyway.

We can hide the interface and expose only bytes32

function verifySolidity(bytes32 h, bytes32 r, bytes32 s, bytes32 qx, bytes32 qy) internal view returns (bool) {. 
  return _verifySolidity(uint256(h), uint256(r), uint256(s), uint256(qx), uint256(qy));
}

function _verifySolidity(uint256 h, uint256 r, uint256 s, uint256 qx, uint256 qy) private view returns (bool) {
  // Current solidity verification
}

What do you think?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My concern is that users may need to cast in the frontend using Javascript

That really depend on the library you are using. In ethers.js, uint256 is BigNumberish which accepts many format, including hex string and buffers. It is actually less restictive than bytes32.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm be ok with changing the types, but not through one more variant of the function (override). We have enough already. More function is more complex testing, more confusion.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, agree on not adding more functions. Let's change only the types. I'll push a commit

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO should be bytes32. uint types have arithmetic operations which should not be done on these values.

if (r == 0 || r >= N || s == 0 || s >= N || !isOnCurve(qx, qy)) return false;
ernestognw marked this conversation as resolved.
Show resolved Hide resolved

JPoint[16] memory points = _preComputeJacobianPoints(qx, qy);
uint256 w = _invModN(s);
uint256 u1 = mulmod(h, w, N);
uint256 u2 = mulmod(r, w, N);
(uint256 x, ) = _jMultShamir(points, u1, u2);
return (x == r);
}

/**
* @dev public key recovery
* @param h - hashed message
* @param v - signature recovery param
* @param r - signature half R
* @param s - signature half S
*/
function recovery(uint256 h, uint8 v, uint256 r, uint256 s) internal view returns (uint256, uint256) {
if (r == 0 || r >= N || s == 0 || s >= N || v > 1) return (0, 0);

uint256 rx = r;
uint256 ry2 = addmod(mulmod(addmod(mulmod(rx, rx, P), A, P), rx, P), B, P); // weierstrass equation y² = x³ + a.x + b
uint256 ry = Math.modExp(ry2, P1DIV4, P); // This formula for sqrt work because P ≡ 3 (mod 4)
if (mulmod(ry, ry, P) != ry2) return (0, 0); // Sanity check
if (ry % 2 != v % 2) ry = P - ry;

JPoint[16] memory points = _preComputeJacobianPoints(rx, ry);
uint256 w = _invModN(r);
uint256 u1 = mulmod(N - (h % N), w, N);
uint256 u2 = mulmod(s, w, N);
(uint256 x, uint256 y) = _jMultShamir(points, u1, u2);
return (x, y);
}

/**
* @dev address recovery
* @param h - hashed message
* @param v - signature recovery param
* @param r - signature half R
* @param s - signature half S
*/
function recoveryAddress(uint256 h, uint8 v, uint256 r, uint256 s) internal view returns (address) {
(uint256 qx, uint256 qy) = recovery(h, v, r, s);
return getAddress(qx, qy);
}

/**
* @dev derivate public key
* @param privateKey - private key
*/
function getPublicKey(uint256 privateKey) internal view returns (uint256, uint256) {
ernestognw marked this conversation as resolved.
Show resolved Hide resolved
(uint256 x, uint256 y, uint256 z) = _jMult(GX, GY, 1, privateKey);
return _affineFromJacobian(x, y, z);
}
ernestognw marked this conversation as resolved.
Show resolved Hide resolved

/**
* @dev Hash public key into an address
* @param qx - public key coordinate X
* @param qy - public key coordinate Y
*/
function getAddress(uint256 qx, uint256 qy) internal pure returns (address result) {
/// @solidity memory-safe-assembly
assembly {
ernestognw marked this conversation as resolved.
Show resolved Hide resolved
mstore(0x00, qx)
mstore(0x20, qy)
result := keccak256(0x00, 0x40)
}
}

/**
* @dev check if a point is on the curve.
*/
function isOnCurve(uint256 x, uint256 y) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
ernestognw marked this conversation as resolved.
Show resolved Hide resolved
let p := P
let lhs := mulmod(y, y, p)
let rhs := addmod(mulmod(addmod(mulmod(x, x, p), A, p), x, p), B, p)
result := eq(lhs, rhs)
}
}

/**
* @dev Reduce from jacobian to affine coordinates
* @param jx - jacobian coordinate x
* @param jy - jacobian coordinate y
* @param jz - jacobian coordinate z
* @return ax - affine coordinate x
* @return ay - affine coordinate y
*/
function _affineFromJacobian(uint256 jx, uint256 jy, uint256 jz) private view returns (uint256 ax, uint256 ay) {
if (jz == 0) return (0, 0);
uint256 zinv = _invModP(jz);
uint256 zzinv = mulmod(zinv, zinv, P);
uint256 zzzinv = mulmod(zzinv, zinv, P);
ax = mulmod(jx, zzinv, P);
ay = mulmod(jy, zzzinv, P);
}

/**
* @dev Point addition on the jacobian coordinates
* Reference: https://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-add-1998-cmo-2
*/
function _jAdd(
uint256 x1,
uint256 y1,
uint256 z1,
uint256 x2,
uint256 y2,
uint256 z2
) private pure returns (uint256 x3, uint256 y3, uint256 z3) {
if (z1 == 0) {
return (x2, y2, z2);
}
if (z2 == 0) {
return (x1, y1, z1);
}
ernestognw marked this conversation as resolved.
Show resolved Hide resolved
/// @solidity memory-safe-assembly
assembly {
ernestognw marked this conversation as resolved.
Show resolved Hide resolved
let p := P
let zz1 := mulmod(z1, z1, p) // zz1 = z1²
let zz2 := mulmod(z2, z2, p) // zz2 = z2²
let u1 := mulmod(x1, zz2, p) // u1 = x1*z2²
let u2 := mulmod(x2, zz1, p) // u2 = x2*z1²
let s1 := mulmod(y1, mulmod(zz2, z2, p), p) // s1 = y1*z2³
let s2 := mulmod(y2, mulmod(zz1, z1, p), p) // s2 = y2*z1³
let h := addmod(u2, sub(p, u1), p) // h = u2-u1
ernestognw marked this conversation as resolved.
Show resolved Hide resolved
let hh := mulmod(h, h, p) // h²
let hhh := mulmod(h, hh, p) // h³
let r := addmod(s2, sub(p, s1), p) // r = s2-s1

// x' = r²-h³-2*u1*h²
x3 := addmod(addmod(mulmod(r, r, p), sub(p, hhh), p), sub(p, mulmod(2, mulmod(u1, hh, p), p)), p)
// y' = r*(u1*h²-x')-s1*h³
y3 := addmod(mulmod(r, addmod(mulmod(u1, hh, p), sub(p, x3), p), p), sub(p, mulmod(s1, hhh, p)), p)
// z' = h*z1*z2
z3 := mulmod(h, mulmod(z1, z2, p), p)
}
}

/**
* @dev Point doubling on the jacobian coordinates
* Reference: https://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-1998-cmo-2
*/
function _jDouble(uint256 x, uint256 y, uint256 z) private pure returns (uint256 x2, uint256 y2, uint256 z2) {
/// @solidity memory-safe-assembly
assembly {
ernestognw marked this conversation as resolved.
Show resolved Hide resolved
let p := P
let yy := mulmod(y, y, p)
ernestognw marked this conversation as resolved.
Show resolved Hide resolved
let zz := mulmod(z, z, p)
let s := mulmod(4, mulmod(x, yy, p), p) // s = 4*x*y²
let m := addmod(mulmod(3, mulmod(x, x, p), p), mulmod(A, mulmod(zz, zz, p), p), p) // m = 3*x²+a*z⁴
let t := addmod(mulmod(m, m, p), sub(p, mulmod(2, s, p)), p) // t = m²-2*s

// x' = t
x2 := t
// y' = m*(s-t)-8*y⁴
y2 := addmod(mulmod(m, addmod(s, sub(p, x2), p), p), sub(p, mulmod(8, mulmod(yy, yy, p), p)), p)
// z' = 2*y*z
z2 := mulmod(2, mulmod(y, z, p), p)
}
}

/**
* @dev Point multiplication on the jacobian coordinates
*/
function _jMult(
uint256 x,
uint256 y,
uint256 z,
uint256 k
) private pure returns (uint256 x2, uint256 y2, uint256 z2) {
unchecked {
for (uint256 i = 0; i < 256; ++i) {
if (z > 0) {
(x2, y2, z2) = _jDouble(x2, y2, z2);
}
if (k >> 255 > 0) {
(x2, y2, z2) = _jAdd(x2, y2, z2, x, y, z);
}
k <<= 1;
}
}
}

/**
* @dev Compute P·u1 + Q·u2 using the precomputed points for P and Q (see {_preComputeJacobianPoints}).
*
* Uses Strauss Shamir trick for EC multiplication
* https://stackoverflow.com/questions/50993471/ec-scalar-multiplication-with-strauss-shamir-method
* we optimise on this a bit to do with 2 bits at a time rather than a single bit
* the individual points for a single pass are precomputed
* overall this reduces the number of additions while keeping the same number of doublings
*/
function _jMultShamir(JPoint[16] memory points, uint256 u1, uint256 u2) private view returns (uint256, uint256) {
uint256 x = 0;
uint256 y = 0;
uint256 z = 0;
unchecked {
for (uint256 i = 0; i < 128; ++i) {
if (z > 0) {
(x, y, z) = _jDouble(x, y, z);
(x, y, z) = _jDouble(x, y, z);
}
// Read 2 bits of u1, and 2 bits of u2. Combining the two give a lookup index in the table.
uint256 pos = ((u1 >> 252) & 0xc) | ((u2 >> 254) & 0x3);
if (pos > 0) {
(x, y, z) = _jAdd(x, y, z, points[pos].x, points[pos].y, points[pos].z);
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So here the if is optional.

If we remove the if, we are going to load points[0] which is (0,0,0) ... and the _jAdd will skip that as the "neutral element". The if here as a cost. 15/16 we pay it for no real reason (and we still pay the check in _jAdd). 1/16 the if avoids the overhead of a function call.

I'm going to benchmark which one is better and comment that so we don't go back and forward.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked, skipping the mloads in 1/16 cases is a bigger gain than the loss of the if in the other 15/16 cases. Keeping the if is the more effective solution here

u1 <<= 2;
u2 <<= 2;
}
}
return _affineFromJacobian(x, y, z);
}

/**
* @dev Precompute a matrice of useful jacobian points associated with a given P. This can be seen as a 4x4 matrix
* that contains combination of P and G (generator) up to 3 times each. See the table below:
*
* ┌────┬─────────────────────┐
* │ i │ 0 1 2 3 │
* ├────┼─────────────────────┤
* │ 0 │ 0 p 2p 3p │
* │ 4 │ g g+p g+2p g+3p │
* │ 8 │ 2g 2g+p 2g+2p 2g+3p │
* │ 12 │ 3g 3g+p 3g+2p 3g+3p │
* └────┴─────────────────────┘
*/
function _preComputeJacobianPoints(uint256 px, uint256 py) private pure returns (JPoint[16] memory points) {
points[0x00] = JPoint(0, 0, 0);
points[0x01] = JPoint(px, py, 1);
points[0x04] = JPoint(GX, GY, 1);
points[0x02] = _jDoublePoint(points[0x01]);
points[0x08] = _jDoublePoint(points[0x04]);
points[0x03] = _jAddPoint(points[0x01], points[0x02]);
points[0x05] = _jAddPoint(points[0x01], points[0x04]);
points[0x06] = _jAddPoint(points[0x02], points[0x04]);
points[0x07] = _jAddPoint(points[0x03], points[0x04]);
points[0x09] = _jAddPoint(points[0x01], points[0x08]);
points[0x0a] = _jAddPoint(points[0x02], points[0x08]);
points[0x0b] = _jAddPoint(points[0x03], points[0x08]);
points[0x0c] = _jAddPoint(points[0x04], points[0x08]);
points[0x0d] = _jAddPoint(points[0x01], points[0x0c]);
points[0x0e] = _jAddPoint(points[0x02], points[0x0c]);
points[0x0f] = _jAddPoint(points[0x03], points[0x0C]);
}

function _jAddPoint(JPoint memory p1, JPoint memory p2) private pure returns (JPoint memory) {
(uint256 x, uint256 y, uint256 z) = _jAdd(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z);
return JPoint(x, y, z);
}

function _jDoublePoint(JPoint memory p) private pure returns (JPoint memory) {
(uint256 x, uint256 y, uint256 z) = _jDouble(p.x, p.y, p.z);
return JPoint(x, y, z);
}

/**
*@dev From Fermat's little theorem https://en.wikipedia.org/wiki/Fermat%27s_little_theorem:
* `a**(p-1) ≡ 1 mod p`. This means that `a**(p-2)` is an inverse of a in Fp.
*/
function _invModN(uint256 value) private view returns (uint256) {
return Math.modExp(value, N2, N);
}

function _invModP(uint256 value) private view returns (uint256) {
return Math.modExp(value, P2, P);
}
ernestognw marked this conversation as resolved.
Show resolved Hide resolved
}
Loading