|
|
@ -185,6 +185,13 @@ library P256 { |
|
|
|
/** |
|
|
|
/** |
|
|
|
* @dev Point addition on the jacobian coordinates |
|
|
|
* @dev Point addition on the jacobian coordinates |
|
|
|
* Reference: https://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-add-1998-cmo-2 |
|
|
|
* Reference: https://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-add-1998-cmo-2 |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* Note that: |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* - `addition-add-1998-cmo-2` doesn't support identical input points. This version is modified to use |
|
|
|
|
|
|
|
* the `h` and `r` values computed by `addition-add-1998-cmo-2` to detect identical inputs, and fallback to |
|
|
|
|
|
|
|
* `doubling-dbl-1998-cmo-2` if needed. |
|
|
|
|
|
|
|
* - if one of the points is at infinity (i.e. `z=0`), the result is undefined. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
function _jAdd( |
|
|
|
function _jAdd( |
|
|
|
JPoint memory p1, |
|
|
|
JPoint memory p1, |
|
|
@ -197,9 +204,14 @@ library P256 { |
|
|
|
let z1 := mload(add(p1, 0x40)) |
|
|
|
let z1 := mload(add(p1, 0x40)) |
|
|
|
let zz1 := mulmod(z1, z1, p) // zz1 = z1² |
|
|
|
let zz1 := mulmod(z1, z1, p) // zz1 = z1² |
|
|
|
let s1 := mulmod(mload(add(p1, 0x20)), mulmod(mulmod(z2, z2, p), z2, p), p) // s1 = y1*z2³ |
|
|
|
let s1 := mulmod(mload(add(p1, 0x20)), mulmod(mulmod(z2, z2, p), z2, p), p) // s1 = y1*z2³ |
|
|
|
let r := addmod(mulmod(y2, mulmod(zz1, z1, p), p), sub(p, s1), p) // r = s2-s1 = y2*z1³-s1 |
|
|
|
let r := addmod(mulmod(y2, mulmod(zz1, z1, p), p), sub(p, s1), p) // r = s2-s1 = y2*z1³-s1 = y2*z1³-y1*z2³ |
|
|
|
let u1 := mulmod(mload(p1), mulmod(z2, z2, p), p) // u1 = x1*z2² |
|
|
|
let u1 := mulmod(mload(p1), mulmod(z2, z2, p), p) // u1 = x1*z2² |
|
|
|
let h := addmod(mulmod(x2, zz1, p), sub(p, u1), p) // h = u2-u1 = x2*z1²-u1 |
|
|
|
let h := addmod(mulmod(x2, zz1, p), sub(p, u1), p) // h = u2-u1 = x2*z1²-u1 = x2*z1²-x1*z2² |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// detect edge cases where inputs are identical |
|
|
|
|
|
|
|
switch and(iszero(r), iszero(h)) |
|
|
|
|
|
|
|
// case 0: points are different |
|
|
|
|
|
|
|
case 0 { |
|
|
|
let hh := mulmod(h, h, p) // h² |
|
|
|
let hh := mulmod(h, h, p) // h² |
|
|
|
|
|
|
|
|
|
|
|
// x' = r²-h³-2*u1*h² |
|
|
|
// x' = r²-h³-2*u1*h² |
|
|
@ -217,6 +229,29 @@ library P256 { |
|
|
|
// z' = h*z1*z2 |
|
|
|
// z' = h*z1*z2 |
|
|
|
rz := mulmod(h, mulmod(z1, z2, p), p) |
|
|
|
rz := mulmod(h, mulmod(z1, z2, p), p) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// case 1: points are equal |
|
|
|
|
|
|
|
case 1 { |
|
|
|
|
|
|
|
let x := x2 |
|
|
|
|
|
|
|
let y := y2 |
|
|
|
|
|
|
|
let z := z2 |
|
|
|
|
|
|
|
let yy := mulmod(y, y, p) |
|
|
|
|
|
|
|
let zz := mulmod(z, z, p) |
|
|
|
|
|
|
|
let m := addmod(mulmod(3, mulmod(x, x, p), p), mulmod(A, mulmod(zz, zz, p), p), p) // m = 3*x²+a*z⁴ |
|
|
|
|
|
|
|
let s := mulmod(4, mulmod(x, yy, p), p) // s = 4*x*y² |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// x' = t = m²-2*s |
|
|
|
|
|
|
|
rx := addmod(mulmod(m, m, p), sub(p, mulmod(2, s, p)), p) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// y' = m*(s-t)-8*y⁴ = m*(s-x')-8*y⁴ |
|
|
|
|
|
|
|
// cut the computation to avoid stack too deep |
|
|
|
|
|
|
|
let rytmp1 := sub(p, mulmod(8, mulmod(yy, yy, p), p)) // -8*y⁴ |
|
|
|
|
|
|
|
let rytmp2 := addmod(s, sub(p, rx), p) // s-x' |
|
|
|
|
|
|
|
ry := addmod(mulmod(m, rytmp2, p), rytmp1, p) // m*(s-x')-8*y⁴ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// z' = 2*y*z |
|
|
|
|
|
|
|
rz := mulmod(2, mulmod(y, z, p), p) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
@ -228,8 +263,8 @@ library P256 { |
|
|
|
let p := P |
|
|
|
let p := P |
|
|
|
let yy := mulmod(y, y, p) |
|
|
|
let yy := mulmod(y, y, p) |
|
|
|
let zz := mulmod(z, z, p) |
|
|
|
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 m := addmod(mulmod(3, mulmod(x, x, p), p), mulmod(A, mulmod(zz, zz, p), p), p) // m = 3*x²+a*z⁴ |
|
|
|
|
|
|
|
let s := mulmod(4, mulmod(x, yy, p), p) // s = 4*x*y² |
|
|
|
|
|
|
|
|
|
|
|
// x' = t = m²-2*s |
|
|
|
// x' = t = m²-2*s |
|
|
|
rx := addmod(mulmod(m, m, p), sub(p, mulmod(2, s, p)), p) |
|
|
|
rx := addmod(mulmod(m, m, p), sub(p, mulmod(2, s, p)), p) |
|
|
@ -244,10 +279,11 @@ library P256 { |
|
|
|
* @dev Compute G·u1 + P·u2 using the precomputed points for G and P (see {_preComputeJacobianPoints}). |
|
|
|
* @dev Compute G·u1 + P·u2 using the precomputed points for G and P (see {_preComputeJacobianPoints}). |
|
|
|
* |
|
|
|
* |
|
|
|
* Uses Strauss Shamir trick for EC multiplication |
|
|
|
* Uses Strauss Shamir trick for EC multiplication |
|
|
|
* https://stackoverflow.com/questions/50993471/ec-scalar-multiplication-with-strauss-shamir-method. |
|
|
|
* 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. |
|
|
|
* We optimize this for 2 bits at a time rather than a single bit. The individual points for a single pass are |
|
|
|
* Overall this reduces the number of additions while keeping the same number of doublings. |
|
|
|
* precomputed. Overall this reduces the number of additions while keeping the same number of |
|
|
|
|
|
|
|
* doublings |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
function _jMultShamir( |
|
|
|
function _jMultShamir( |
|
|
|
JPoint[16] memory points, |
|
|
|
JPoint[16] memory points, |
|
|
@ -263,9 +299,14 @@ library P256 { |
|
|
|
(x, y, z) = _jDouble(x, y, z); |
|
|
|
(x, y, z) = _jDouble(x, y, z); |
|
|
|
(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. |
|
|
|
// Read 2 bits of u1, and 2 bits of u2. Combining the two gives the lookup index in the table. |
|
|
|
uint256 pos = ((u1 >> 252) & 0xc) | ((u2 >> 254) & 0x3); |
|
|
|
uint256 pos = ((u1 >> 252) & 0xc) | ((u2 >> 254) & 0x3); |
|
|
|
if (pos > 0) { |
|
|
|
// Points that have z = 0 are points at infinity. They are the additive 0 of the group |
|
|
|
|
|
|
|
// - if the lookup point is a 0, we can skip it |
|
|
|
|
|
|
|
// - otherwise: |
|
|
|
|
|
|
|
// - if the current point (x, y, z) is 0, we use the lookup point as our new value (0+P=P) |
|
|
|
|
|
|
|
// - if the current point (x, y, z) is not 0, both points are valid and we can use `_jAdd` |
|
|
|
|
|
|
|
if (points[pos].z != 0) { |
|
|
|
if (z == 0) { |
|
|
|
if (z == 0) { |
|
|
|
(x, y, z) = (points[pos].x, points[pos].y, points[pos].z); |
|
|
|
(x, y, z) = (points[pos].x, points[pos].y, points[pos].z); |
|
|
|
} else { |
|
|
|
} else { |
|
|
@ -291,6 +332,11 @@ library P256 { |
|
|
|
* │ 8 │ 2g 2g+p 2g+2p 2g+3p │ |
|
|
|
* │ 8 │ 2g 2g+p 2g+2p 2g+3p │ |
|
|
|
* │ 12 │ 3g 3g+p 3g+2p 3g+3p │ |
|
|
|
* │ 12 │ 3g 3g+p 3g+2p 3g+3p │ |
|
|
|
* └────┴─────────────────────┘ |
|
|
|
* └────┴─────────────────────┘ |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* Note that `_jAdd` (and thus `_jAddPoint`) does not handle the case where one of the inputs is a point at |
|
|
|
|
|
|
|
* infinity (z = 0). However, we know that since `N ≡ 1 mod 2` and `N ≡ 1 mod 3`, there is no point P such that |
|
|
|
|
|
|
|
* 2P = 0 or 3P = 0. This guarantees that g, 2g, 3g, p, 2p, 3p are all non-zero, and that all `_jAddPoint` calls |
|
|
|
|
|
|
|
* have valid inputs. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
function _preComputeJacobianPoints(uint256 px, uint256 py) private pure returns (JPoint[16] memory points) { |
|
|
|
function _preComputeJacobianPoints(uint256 px, uint256 py) private pure returns (JPoint[16] memory points) { |
|
|
|
points[0x00] = JPoint(0, 0, 0); // 0,0 |
|
|
|
points[0x00] = JPoint(0, 0, 0); // 0,0 |
|
|
|