Implement Jacobian mixed addition (#142)

This commit is contained in:
Mamy Ratsimbazafy 2021-01-30 14:21:55 +01:00 committed by GitHub
parent b91ec1cb15
commit d12d5faf21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 157 additions and 13 deletions

View File

@ -48,6 +48,7 @@ proc main() =
addBench(ECP_ShortW_Proj[Fp[curve], NotOnTwist], Iters)
addBench(ECP_ShortW_Jac[Fp[curve], NotOnTwist], Iters)
mixedAddBench(ECP_ShortW_Proj[Fp[curve], NotOnTwist], Iters)
mixedAddBench(ECP_ShortW_Jac[Fp[curve], NotOnTwist], Iters)
doublingBench(ECP_ShortW_Proj[Fp[curve], NotOnTwist], Iters)
doublingBench(ECP_ShortW_Jac[Fp[curve], NotOnTwist], Iters)
separator()

View File

@ -49,6 +49,7 @@ proc main() =
addBench(ECP_ShortW_Proj[Fp2[curve], OnTwist], Iters)
addBench(ECP_ShortW_Jac[Fp2[curve], OnTwist], Iters)
mixedAddBench(ECP_ShortW_Proj[Fp2[curve], OnTwist], Iters)
mixedAddBench(ECP_ShortW_Jac[Fp2[curve], OnTwist], Iters)
doublingBench(ECP_ShortW_Proj[Fp2[curve], OnTwist], Iters)
doublingBench(ECP_ShortW_Jac[Fp2[curve], OnTwist], Iters)
separator()

View File

@ -17,7 +17,11 @@ import
../constantine/config/[curves, common],
../constantine/arithmetic,
../constantine/io/io_bigints,
../constantine/elliptic/[ec_shortweierstrass_affine, ec_shortweierstrass_projective, ec_scalar_mul, ec_endomorphism_accel],
../constantine/elliptic/[
ec_shortweierstrass_affine,
ec_shortweierstrass_projective,
ec_shortweierstrass_jacobian,
ec_scalar_mul, ec_endomorphism_accel],
# Helpers
../helpers/[prng_unsafe, static_for],
./platforms,
@ -64,7 +68,10 @@ proc mixedAddBench*(T: typedesc, iters: int) =
let P = rng.random_unsafe(T)
let Q = rng.random_unsafe(T)
var Qaff: ECP_ShortW_Aff[T.F, T.Tw]
when Q is ECP_ShortW_Proj:
Qaff.affineFromProjective(Q)
else:
Qaff.affineFromJacobian(Q)
bench("EC Mixed Addition " & G1_or_G2, T, iters):
r.madd(P, Qaff)

View File

@ -75,7 +75,7 @@ const testDesc: seq[tuple[path: string, useGMP: bool]] = @[
# ("tests/t_ec_shortw_jac_g1_mul_sanity.nim", false),
# ("tests/t_ec_shortw_jac_g1_mul_distri.nim", false),
("tests/t_ec_shortw_jac_g1_mul_vs_ref.nim", false),
# mixed_add
("tests/t_ec_shortw_jac_g1_mixed_add.nim", false),
# Elliptic curve arithmetic G2
# ----------------------------------------------------------
@ -107,25 +107,25 @@ const testDesc: seq[tuple[path: string, useGMP: bool]] = @[
# ("tests/t_ec_shortw_jac_g2_mul_sanity_bn254_snarks.nim", false),
# ("tests/t_ec_shortw_jac_g2_mul_distri_bn254_snarks.nim", false),
("tests/t_ec_shortw_jac_g2_mul_vs_ref_bn254_snarks.nim", false),
# mixed_add
("tests/t_ec_shortw_jac_g2_mixed_add_bn254_snarks.nim", false),
# ("tests/t_ec_shortw_jac_g2_add_double_bls12_381.nim", false),
# ("tests/t_ec_shortw_jac_g2_mul_sanity_bls12_381.nim", false),
# ("tests/t_ec_shortw_jac_g2_mul_distri_bls12_381.nim", false),
("tests/t_ec_shortw_jac_g2_mul_vs_ref_bls12_381.nim", false),
# mixed_add
("tests/t_ec_shortw_jac_g2_mixed_add_bls12_381.nim", false),
# ("tests/t_ec_shortw_jac_g2_add_double_bls12_377.nim", false),
# ("tests/t_ec_shortw_jac_g2_mul_sanity_bls12_377.nim", false),
# ("tests/t_ec_shortw_jac_g2_mul_distri_bls12_377.nim", false),
("tests/t_ec_shortw_jac_g2_mul_vs_ref_bls12_377.nim", false),
# mixed_add
("tests/t_ec_shortw_jac_g2_mixed_add_bls12_377.nim", false),
# ("tests/t_ec_shortw_jac_g2_add_double_bw6_761.nim", false),
# ("tests/t_ec_shortw_jac_g2_mul_sanity_bw6_761.nim", false),
# ("tests/t_ec_shortw_jac_g2_mul_distri_bw6_761.nim", false),
("tests/t_ec_shortw_jac_g2_mul_vs_ref_bw6_761.nim", false),
# mixed_add
("tests/t_ec_shortw_jac_g2_mixed_add_bw6_761.nim", false),
# Elliptic curve arithmetic vs Sagemath
# ----------------------------------------------------------

View File

@ -63,13 +63,14 @@ when UseASM_X86_64:
func setZero*(a: var Limbs) =
## Set ``a`` to 0
zeroMem(a[0].addr, sizeof(a))
for i in 0 ..< a.len:
a[i] = Zero
func setOne*(a: var Limbs) =
## Set ``a`` to 1
a[0] = One
when a.len > 1:
zeroMem(a[1].addr, (a.len - 1) * sizeof(SecretWord))
for i in 1 ..< a.len:
a[i] = Zero
func setUint*(a: var Limbs, n: SomeUnsignedInt) =
## set ``a`` to an unsigned integer ``n``

View File

@ -32,6 +32,16 @@ type
## over a field F
x*, y*: F
func isInf*(P: ECP_ShortW_Aff): SecretBool =
## Returns true if P is an infinity point
## and false otherwise
##
## Note: the jacobian coordinates equation is
## Y² = X³ + aXZ⁴ + bZ⁶
## A "zero" point is any point with coordinates X and Z = 0
## Y can be anything
result = P.x.isZero() and P.y.isZero()
func curve_eq_rhs*[F](y2: var F, x: F, Tw: static Twisted) =
## Compute the curve equation right-hand-side from field element `x`
## i.e. `y²` in `y² = x³ + a x + b`

View File

@ -291,6 +291,85 @@ func sum*[F; Tw: static Twisted](
r.ccopy(Q, P.isInf())
r.ccopy(P, Q.isInf())
func madd*[F; Tw: static Twisted](
r: var ECP_ShortW_Jac[F, Tw],
P: ECP_ShortW_Jac[F, Tw],
Q: ECP_ShortW_Aff[F, Tw]
) =
## Elliptic curve mixed addition for Short Weierstrass curves
## with p in Jacobian coordinates and Q in affine coordinates
##
## R = P + Q
# "madd-2007-bl" mixed addition formula - https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-madd-2007-bl
# with conditional copies to handle infinity points
# Assumptions: Z2=1.
# Cost: 7M + 4S + 9add + 3*2 + 1*4.
# Source: 2007 BernsteinLange.
# Explicit formulas:
#
# Z1Z1 = Z1²
# U2 = X2*Z1Z1
# S2 = Y2*Z1*Z1Z1
# H = U2-X1
# HH = H2
# I = 4*HH
# J = H*I
# r = 2*(S2-Y1)
# V = X1*I
# X3 = r²-J-2*V
# Y3 = r*(V-X3)-2*Y1*J
# Z3 = (Z1+H)²-Z1Z1-HH
var Z1Z1 {.noInit.}, H {.noInit.}, HH {.noInit.}, I{.noInit.}, J {.noInit.}: F
# Preload P and Q in cache
let pIsInf = P.isInf()
let qIsInf = Q.isInf()
Z1Z1.square(P.z) # Z₁Z₁ = Z₁²
r.z.prod(P.z, Z1Z1) # P.Z is hot in cache, keep it in same register.
r.z *= Q.y # S₂ = Y₂Z₁Z₁Z₁ -- r.z used as S₂
H.prod(Q.x, Z1Z1) # U₂ = X₂Z₁Z₁
H -= P.x # H = U₂ - X₁
HH.square(H) # HH = H²
I.double(HH)
I.double() # I = 4HH
J.prod(H, I) # J = H*I
r.y.prod(P.x, I) # V = X₁*I -- r.y used as V
r.z -= P.y #
r.z.double() # r = 2*(S₂-Y₁) -- r.z used as r
r.x.square(r.z) # r²
r.x -= J
r.x -= r.y
r.x -= r.y # X₃ = r²-J-2*V -- r.x computed
r.y -= r.x # V-X₃
r.y *= r.z # r*(V-X₃)
J *= P.y # Y₁J -- J reused as Y₁J
r.y -= J
r.y -= J # Y₃ = r*(V-X₃) - 2*Y₁J -- r.y computed
r.z.sum(P.z, H) # Z₁ + H
r.z.square()
r.z -= Z1Z1
r.z -= HH # Z₃ = (Z1+H)²-Z1Z1-HH
# Now handle points at infinity
proc one(): F =
result.setOne()
r.x.ccopy(Q.x, pIsInf)
r.y.ccopy(Q.y, pIsInf)
r.z.ccopy(static(one()), pIsInf)
r.ccopy(P, qIsInf)
func double*[F; Tw: static Twisted](
r: var ECP_ShortW_Jac[F, Tw],
P: ECP_ShortW_Jac[F, Tw]
@ -373,13 +452,13 @@ func diff*(r: var ECP_ShortW_Jac,
func affineFromJacobian*[F; Tw](
aff: var ECP_ShortW_Aff[F, Tw],
jac: ECP_ShortW_Jac[F, Tw]) =
var invZ {.noInit.}, invZ2: F
var invZ {.noInit.}, invZ2{.noInit.}: F
invZ.inv(jac.z)
invZ2.square(invZ)
aff.x.prod(jac.x, invZ2)
aff.y.prod(jac.y, invZ)
aff.y.prod(jac.y, invZ2)
aff.y *= invZ2
func jacobianFromAffine*[F; Tw](
jac: var ECP_ShortW_Jac[F, Tw],

View File

@ -0,0 +1,42 @@
# Constantine
# Copyright (c) 2018-2019 Status Research & Development GmbH
# Copyright (c) 2020-Present Mamy André-Ratsimbazafy
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
# Internals
../constantine/config/curves,
../constantine/elliptic/ec_shortweierstrass_jacobian,
../constantine/arithmetic,
# Test utilities
./t_ec_template
const
Iters = 12
run_EC_mixed_add_impl(
ec = ECP_ShortW_Jac[Fp[BN254_Snarks], NotOnTwist],
Iters = Iters,
moduleName = "test_ec_shortweierstrass_jacobian_mixed_add_" & $BN254_Snarks
)
run_EC_mixed_add_impl(
ec = ECP_ShortW_Jac[Fp[BLS12_381], NotOnTwist],
Iters = Iters,
moduleName = "test_ec_shortweierstrass_jacobian_mixed_add_" & $BLS12_381
)
run_EC_mixed_add_impl(
ec = ECP_ShortW_Jac[Fp[BLS12_377], NotOnTwist],
Iters = Iters,
moduleName = "test_ec_shortweierstrass_jacobian_mixed_add_" & $BLS12_377
)
run_EC_mixed_add_impl(
ec = ECP_ShortW_Jac[Fp[BW6_761], NotOnTwist],
Iters = Iters,
moduleName = "test_ec_shortweierstrass_jacobian_mixed_add_" & $BW6_761
)

View File

@ -441,7 +441,10 @@ proc run_EC_mixed_add_impl*(
let a = rng.random_point(EC, randZ, gen)
let b = rng.random_point(EC, randZ, gen)
var bAff: ECP_ShortW_Aff[EC.F, EC.Tw]
when b is ECP_ShortW_Proj:
bAff.affineFromProjective(b)
else:
bAff.affineFromJacobian(b)
var r_generic, r_mixed: EC