hash-to-curve BLS12-381 perf (#163)
* fp square noasm split from non-4 non-6 limbs fallback (40% speedup) * optimized cofactor clearing for BLS12-381 G2 * Support jacobian isogenies and point_add on isogenies * fuse addition and isogeny map * {.noInit.} and sparseMul * poly_eval_horner init * dedicated invsqrt + cleanup square root file * hash to field: reduce copy overhead and don't return arrays * h2c isogeny jacobian reuse pow 3 precomputed value * Fix sqrt bench
This commit is contained in:
parent
499f9605b2
commit
0bc228126a
|
@ -124,28 +124,32 @@ proc sqrtBench*(T: typedesc, iters: int) =
|
|||
discard r.sqrt_if_square()
|
||||
|
||||
proc sqrtP3mod4Bench*(T: typedesc, iters: int) =
|
||||
var r: T
|
||||
let x = rng.random_unsafe(T)
|
||||
bench("SquareRoot + isSquare (p ≡ 3 (mod 4) exponentiation)", T, iters):
|
||||
var r = x
|
||||
discard r.sqrt_if_square_p3mod4()
|
||||
bench("SquareRoot (p ≡ 3 (mod 4) exponentiation)", T, iters):
|
||||
r.invsqrt_p3mod4(x)
|
||||
r *= x
|
||||
|
||||
proc sqrtAddChainBench*(T: typedesc, iters: int) =
|
||||
var r: T
|
||||
let x = rng.random_unsafe(T)
|
||||
bench("SquareRoot + isSquare (addition chain)", T, iters):
|
||||
var r = x
|
||||
discard r.sqrt_if_square_addchain()
|
||||
bench("SquareRoot (addition chain)", T, iters):
|
||||
r.invsqrt_addchain(x)
|
||||
r *= x
|
||||
|
||||
proc sqrtTonelliBench*(T: typedesc, iters: int) =
|
||||
var r: T
|
||||
let x = rng.random_unsafe(T)
|
||||
bench("SquareRoot + isSquare (constant-time Tonelli-Shanks exponentiation)", T, iters):
|
||||
var r = x
|
||||
discard r.sqrt_if_square_tonelli_shanks(useAddChain = false)
|
||||
bench("SquareRoot (constant-time Tonelli-Shanks exponentiation)", T, iters):
|
||||
r.invsqrt_tonelli_shanks(x, useAddChain = false)
|
||||
r *= x
|
||||
|
||||
proc sqrtTonelliAddChainBench*(T: typedesc, iters: int) =
|
||||
var r: T
|
||||
let x = rng.random_unsafe(T)
|
||||
bench("SquareRoot + isSquare (constant-time Tonelli-Shanks addchain)", T, iters):
|
||||
var r = x
|
||||
discard r.sqrt_if_square_tonelli_shanks(useAddChain = true)
|
||||
bench("SquareRoot (constant-time Tonelli-Shanks addchain)", T, iters):
|
||||
r.invsqrt_tonelli_shanks(x, useAddChain = true)
|
||||
r *= x
|
||||
|
||||
proc powBench*(T: typedesc, iters: int) =
|
||||
let x = rng.random_unsafe(T)
|
||||
|
|
|
@ -263,26 +263,26 @@ proc runTests(requireGMP: bool, dumpCmdFile = false, test32bit = false, testASM
|
|||
flags &= sanitizers
|
||||
test flags, td.path, dumpCmdFile
|
||||
|
||||
proc buildAllBenches() =
|
||||
proc buildAllBenches(useAsm = true) =
|
||||
echo "\n\n------------------------------------------------------\n"
|
||||
echo "Building benchmarks to ensure they stay relevant ..."
|
||||
buildBench("bench_fp")
|
||||
buildBench("bench_fp_double_precision")
|
||||
buildBench("bench_fp2")
|
||||
buildBench("bench_fp6")
|
||||
buildBench("bench_fp12")
|
||||
buildBench("bench_ec_g1")
|
||||
buildBench("bench_ec_g2")
|
||||
buildBench("bench_pairing_bls12_377")
|
||||
buildBench("bench_pairing_bls12_381")
|
||||
buildBench("bench_pairing_bn254_nogami")
|
||||
buildBench("bench_pairing_bn254_snarks")
|
||||
buildBench("bench_summary_bls12_377")
|
||||
buildBench("bench_summary_bls12_381")
|
||||
buildBench("bench_summary_bn254_nogami")
|
||||
buildBench("bench_summary_bn254_snarks")
|
||||
buildBench("bench_sha256")
|
||||
buildBench("bench_hash_to_curve")
|
||||
buildBench("bench_fp", useAsm = useAsm)
|
||||
buildBench("bench_fp_double_precision", useAsm = useAsm)
|
||||
buildBench("bench_fp2", useAsm = useAsm)
|
||||
buildBench("bench_fp6", useAsm = useAsm)
|
||||
buildBench("bench_fp12", useAsm = useAsm)
|
||||
buildBench("bench_ec_g1", useAsm = useAsm)
|
||||
buildBench("bench_ec_g2", useAsm = useAsm)
|
||||
buildBench("bench_pairing_bls12_377", useAsm = useAsm)
|
||||
buildBench("bench_pairing_bls12_381", useAsm = useAsm)
|
||||
buildBench("bench_pairing_bn254_nogami", useAsm = useAsm)
|
||||
buildBench("bench_pairing_bn254_snarks", useAsm = useAsm)
|
||||
buildBench("bench_summary_bls12_377", useAsm = useAsm)
|
||||
buildBench("bench_summary_bls12_381", useAsm = useAsm)
|
||||
buildBench("bench_summary_bn254_nogami", useAsm = useAsm)
|
||||
buildBench("bench_summary_bn254_snarks", useAsm = useAsm)
|
||||
buildBench("bench_sha256", useAsm = useAsm)
|
||||
buildBench("bench_hash_to_curve", useAsm = useAsm)
|
||||
echo "All benchmarks compile successfully."
|
||||
|
||||
# Tasks
|
||||
|
@ -308,7 +308,7 @@ task test_no_assembler, "Run all tests":
|
|||
|
||||
# Ensure benchmarks stay relevant. Ignore Windows 32-bit at the moment
|
||||
if not defined(windows) or not (existsEnv"UCPU" or getEnv"UCPU" == "i686"):
|
||||
buildAllBenches()
|
||||
buildAllBenches(useASM = false)
|
||||
|
||||
task test_no_gmp, "Run tests that don't require GMP":
|
||||
# -d:testingCurves is configured in a *.nim.cfg for convenience
|
||||
|
@ -357,7 +357,7 @@ task test_parallel_no_assembler, "Run all tests (without macro assembler) in par
|
|||
# ignore Windows 32-bit for the moment
|
||||
# Ensure benchmarks stay relevant. Ignore Windows 32-bit at the moment
|
||||
if not defined(windows) or not (existsEnv"UCPU" or getEnv"UCPU" == "i686"):
|
||||
buildAllBenches()
|
||||
buildAllBenches(useASM = false)
|
||||
|
||||
task test_parallel_no_gmp, "Run all tests in parallel (via GNU parallel)":
|
||||
# -d:testingCurves is configured in a *.nim.cfg for convenience
|
||||
|
@ -395,7 +395,7 @@ task test_parallel_no_gmp_no_assembler, "Run all tests in parallel (via GNU para
|
|||
# ignore Windows 32-bit for the moment
|
||||
# Ensure benchmarks stay relevant. Ignore Windows 32-bit at the moment
|
||||
if not defined(windows) or not (existsEnv"UCPU" or getEnv"UCPU" == "i686"):
|
||||
buildAllBenches()
|
||||
buildAllBenches(useASM = false)
|
||||
|
||||
# Finite field 𝔽p
|
||||
# ------------------------------------------
|
||||
|
|
|
@ -54,8 +54,8 @@ func hasP3mod4_primeModulus(C: static Curve): static bool =
|
|||
## Returns true iff p ≡ 3 (mod 4)
|
||||
(BaseType(C.Mod.limbs[0]) and 3) == 3
|
||||
|
||||
func sqrt_p3mod4(a: var Fp) =
|
||||
## Compute the square root of ``a``
|
||||
func invsqrt_p3mod4*(r: var Fp, a: Fp) =
|
||||
## Compute the inverse square root of ``a``
|
||||
##
|
||||
## This requires ``a`` to be a square
|
||||
## and the prime field modulus ``p``: p ≡ 3 (mod 4)
|
||||
|
@ -65,14 +65,6 @@ func sqrt_p3mod4(a: var Fp) =
|
|||
## The square root, if it exist is multivalued,
|
||||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
static: doAssert BaseType(Fp.C.Mod.limbs[0]) mod 4 == 3
|
||||
a.powUnsafeExponent(Fp.getPrimePlus1div4_BE())
|
||||
|
||||
func sqrt_invsqrt_p3mod4(sqrt, invsqrt: var Fp, a: Fp) =
|
||||
## If ``a`` is a square, compute the square root of ``a`` in sqrt
|
||||
## and the inverse square root of a in invsqrt
|
||||
##
|
||||
## This assumes that the prime field modulus ``p``: p ≡ 3 (mod 4)
|
||||
# TODO: deterministic sign
|
||||
#
|
||||
# Algorithm
|
||||
|
@ -83,82 +75,12 @@ func sqrt_invsqrt_p3mod4(sqrt, invsqrt: var Fp, a: Fp) =
|
|||
# a^((p-3)/2)) ≡ 1/a (mod p)
|
||||
# a^((p-3)/4)) ≡ 1/√a (mod p) # Requires p ≡ 3 (mod 4)
|
||||
static: doAssert BaseType(Fp.C.Mod.limbs[0]) mod 4 == 3
|
||||
|
||||
invsqrt = a
|
||||
invsqrt.powUnsafeExponent(Fp.getPrimeMinus3div4_BE())
|
||||
# √a ≡ a * 1/√a ≡ a^((p+1)/4) (mod p)
|
||||
sqrt.prod(invsqrt, a)
|
||||
|
||||
func sqrt_invsqrt_if_square_p3mod4(sqrt, invsqrt: var Fp, a: Fp): SecretBool =
|
||||
## If ``a`` is a square, compute the square root of ``a`` in sqrt
|
||||
## and the inverse square root of a in invsqrt
|
||||
##
|
||||
## If a is not square, sqrt and invsqrt are undefined
|
||||
##
|
||||
## This assumes that the prime field modulus ``p``: p ≡ 3 (mod 4)
|
||||
sqrt_invsqrt_p3mod4(sqrt, invsqrt, a)
|
||||
var test {.noInit.}: Fp
|
||||
test.square(sqrt)
|
||||
result = test == a
|
||||
|
||||
func sqrt_if_square_p3mod4*(a: var Fp): SecretBool =
|
||||
## If ``a`` is a square, compute the square root of ``a``
|
||||
## if not, ``a`` is unmodified.
|
||||
##
|
||||
## This assumes that the prime field modulus ``p``: p ≡ 3 (mod 4)
|
||||
##
|
||||
## The result is undefined otherwise
|
||||
##
|
||||
## The square root, if it exist is multivalued,
|
||||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
var sqrt {.noInit.}, invsqrt {.noInit.}: Fp
|
||||
result = sqrt_invsqrt_if_square_p3mod4(sqrt, invsqrt, a)
|
||||
a.ccopy(sqrt, result)
|
||||
r = a
|
||||
r.powUnsafeExponent(Fp.getPrimeMinus3div4_BE())
|
||||
|
||||
# Specialized routines for addchain-based square roots
|
||||
# ------------------------------------------------------------
|
||||
|
||||
func sqrt_addchain(a: var Fp) =
|
||||
## Compute the square root of ``a``
|
||||
##
|
||||
## This requires ``a`` to be a square
|
||||
## The result is undefined otherwise
|
||||
##
|
||||
## The square root, if it exist is multivalued,
|
||||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
var invsqrt {.noInit.}: Fp
|
||||
invsqrt.invsqrt_addchain(a)
|
||||
a *= invsqrt
|
||||
|
||||
func sqrt_invsqrt_addchain(sqrt, invsqrt: var Fp, a: Fp) =
|
||||
## If ``a`` is a square, compute the square root of ``a`` in sqrt
|
||||
## and the inverse square root of a in invsqrt
|
||||
invsqrt.invsqrt_addchain(a)
|
||||
sqrt.prod(invsqrt, a)
|
||||
|
||||
func sqrt_invsqrt_if_square_addchain(sqrt, invsqrt: var Fp, a: Fp): SecretBool =
|
||||
## If ``a`` is a square, compute the square root of ``a`` in sqrt
|
||||
## and the inverse square root of a in invsqrt
|
||||
##
|
||||
## If a is not square, sqrt and invsqrt are undefined
|
||||
sqrt_invsqrt_addchain(sqrt, invsqrt, a)
|
||||
var test {.noInit.}: Fp
|
||||
test.square(sqrt)
|
||||
result = test == a
|
||||
|
||||
func sqrt_if_square_addchain*(a: var Fp): SecretBool =
|
||||
## If ``a`` is a square, compute the square root of ``a``
|
||||
## if not, ``a`` is unmodified.
|
||||
##
|
||||
## The square root, if it exist is multivalued,
|
||||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
var sqrt {.noInit.}, invsqrt {.noInit.}: Fp
|
||||
result = sqrt_invsqrt_if_square_addchain(sqrt, invsqrt, a)
|
||||
a.ccopy(sqrt, result)
|
||||
|
||||
{.pop.} # inline
|
||||
|
||||
# Tonelli Shanks for any prime
|
||||
|
@ -174,12 +96,15 @@ func precompute_tonelli_shanks(
|
|||
a_pre_exp.powUnsafeExponent(Fp.C.tonelliShanks(exponent))
|
||||
|
||||
func isSquare_tonelli_shanks(
|
||||
a, a_pre_exp: Fp): SecretBool =
|
||||
a, a_pre_exp: Fp): SecretBool {.used.} =
|
||||
## Returns if `a` is a quadratic residue
|
||||
## This uses common precomputation for
|
||||
## Tonelli-Shanks based square root and inverse square root
|
||||
##
|
||||
## a^((p-1-2^e)/(2*2^e))
|
||||
##
|
||||
## Note: if we need to compute a candidate square root anyway
|
||||
## it's faster to square it to check if we get ``a``
|
||||
const e = Fp.C.tonelliShanks(twoAdicity)
|
||||
var r {.noInit.}: Fp
|
||||
r.square(a_pre_exp) # a^(2(q-1-2^e)/(2*2^e)) = a^((q-1)/2^e - 1)
|
||||
|
@ -198,10 +123,10 @@ func isSquare_tonelli_shanks(
|
|||
r.isMinusOne()
|
||||
)
|
||||
|
||||
func sqrt_invsqrt_tonelli_shanks_pre(
|
||||
sqrt, invsqrt: var Fp,
|
||||
func invsqrt_tonelli_shanks_pre(
|
||||
invsqrt: var Fp,
|
||||
a, a_pre_exp: Fp) =
|
||||
## Compute the square_root and inverse_square_root
|
||||
## Compute the inverse_square_root
|
||||
## of `a` via constant-time Tonelli-Shanks
|
||||
##
|
||||
## a_pre_exp is a precomputation a^((p-1-2^e)/(2*2^e))
|
||||
|
@ -230,12 +155,8 @@ func sqrt_invsqrt_tonelli_shanks_pre(
|
|||
t.ccopy(buf, bNotOne)
|
||||
b = t
|
||||
|
||||
sqrt.prod(invsqrt, a)
|
||||
|
||||
# ----------------------------------------------
|
||||
|
||||
func sqrt_tonelli_shanks(a: var Fp, useAddChain: static bool) =
|
||||
## Compute the square root of ``a``
|
||||
func invsqrt_tonelli_shanks*(r: var Fp, a: Fp, useAddChain: static bool) =
|
||||
## Compute the inverse square root of ``a``
|
||||
##
|
||||
## This requires ``a`` to be a square
|
||||
##
|
||||
|
@ -245,54 +166,9 @@ func sqrt_tonelli_shanks(a: var Fp, useAddChain: static bool) =
|
|||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
## This procedure is constant-time
|
||||
var a_pre_exp{.noInit.}, sqrt{.noInit.}, invsqrt{.noInit.}: Fp
|
||||
a_pre_exp.precompute_tonelli_shanks(a, useAddChain)
|
||||
sqrt_invsqrt_tonelli_shanks_pre(sqrt, invsqrt, a, a_pre_exp)
|
||||
a = sqrt
|
||||
|
||||
func sqrt_invsqrt_tonelli_shanks(sqrt, invsqrt: var Fp, a: Fp, useAddChain: static bool) =
|
||||
## Compute the square root and inverse square root of ``a``
|
||||
##
|
||||
## This requires ``a`` to be a square
|
||||
##
|
||||
## The result is undefined otherwise
|
||||
##
|
||||
## The square root, if it exist is multivalued,
|
||||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
var a_pre_exp{.noInit.}: Fp
|
||||
a_pre_exp.precompute_tonelli_shanks(a, useAddChain)
|
||||
sqrt_invsqrt_tonelli_shanks_pre(sqrt, invsqrt, a, a_pre_exp)
|
||||
|
||||
func sqrt_invsqrt_if_square_tonelli_shanks(sqrt, invsqrt: var Fp, a: Fp, useAddChain: static bool): SecretBool =
|
||||
## Compute the square root and ivnerse square root of ``a``
|
||||
##
|
||||
## This returns true if ``a`` is square and sqrt/invsqrt contains the square root/inverse square root
|
||||
##
|
||||
## The result is undefined otherwise
|
||||
##
|
||||
## The square root, if it exist is multivalued,
|
||||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
var a_pre_exp{.noInit.}: Fp
|
||||
a_pre_exp.precompute_tonelli_shanks(a, useAddChain)
|
||||
result = isSquare_tonelli_shanks(a, a_pre_exp)
|
||||
sqrt_invsqrt_tonelli_shanks_pre(sqrt, invsqrt, a, a_pre_exp)
|
||||
a = sqrt
|
||||
|
||||
func sqrt_if_square_tonelli_shanks*(a: var Fp, useAddChain: static bool): SecretBool =
|
||||
## If ``a`` is a square, compute the square root of ``a``
|
||||
## if not, ``a`` is unmodified.
|
||||
##
|
||||
## The square root, if it exist is multivalued,
|
||||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
## This procedure is constant-time
|
||||
var a_pre_exp{.noInit.}, sqrt{.noInit.}, invsqrt{.noInit.}: Fp
|
||||
a_pre_exp.precompute_tonelli_shanks(a, useAddChain)
|
||||
result = isSquare_tonelli_shanks(a, a_pre_exp)
|
||||
sqrt_invsqrt_tonelli_shanks_pre(sqrt, invsqrt, a, a_pre_exp)
|
||||
a = sqrt
|
||||
invsqrt_tonelli_shanks_pre(r, a, a_pre_exp)
|
||||
|
||||
# Public routines
|
||||
# ------------------------------------------------------------
|
||||
|
@ -301,6 +177,24 @@ func sqrt_if_square_tonelli_shanks*(a: var Fp, useAddChain: static bool): Secret
|
|||
|
||||
{.push inline.}
|
||||
|
||||
func invsqrt*[C](r: var Fp[C], a: Fp[C]) =
|
||||
## Compute the inverse square root of ``a``
|
||||
##
|
||||
## This requires ``a`` to be a square
|
||||
##
|
||||
## The result is undefined otherwise
|
||||
##
|
||||
## The square root, if it exist is multivalued,
|
||||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
## This procedure is constant-time
|
||||
when C.hasSqrtAddchain():
|
||||
r.invsqrt_addchain(a)
|
||||
elif C.hasP3mod4_primeModulus():
|
||||
r.invsqrt_p3mod4(a)
|
||||
else:
|
||||
r.invsqrt_tonelli_shanks(a, useAddChain = C.hasTonelliShanksAddchain())
|
||||
|
||||
func sqrt*[C](a: var Fp[C]) =
|
||||
## Compute the square root of ``a``
|
||||
##
|
||||
|
@ -312,12 +206,9 @@ func sqrt*[C](a: var Fp[C]) =
|
|||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
## This procedure is constant-time
|
||||
when C.hasSqrtAddchain():
|
||||
sqrt_addchain(a)
|
||||
elif C.hasP3mod4_primeModulus():
|
||||
sqrt_p3mod4(a)
|
||||
else:
|
||||
sqrt_tonelli_shanks(a, useAddChain = C.hasTonelliShanksAddchain())
|
||||
var t {.noInit.}: Fp[C]
|
||||
t.invsqrt(a)
|
||||
a *= t
|
||||
|
||||
func sqrt_invsqrt*[C](sqrt, invsqrt: var Fp[C], a: Fp[C]) =
|
||||
## Compute the square root and inverse square root of ``a``
|
||||
|
@ -329,12 +220,8 @@ func sqrt_invsqrt*[C](sqrt, invsqrt: var Fp[C], a: Fp[C]) =
|
|||
## The square root, if it exist is multivalued,
|
||||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
when C.hasSqrtAddchain():
|
||||
sqrt_invsqrt_addchain(sqrt, invsqrt, a)
|
||||
elif C.hasP3mod4_primeModulus():
|
||||
sqrt_invsqrt_p3mod4(sqrt, invsqrt, a)
|
||||
else:
|
||||
sqrt_invsqrt_tonelli_shanks(sqrt, invsqrt, a, useAddChain = C.hasTonelliShanksAddchain())
|
||||
invsqrt.invsqrt(a)
|
||||
sqrt.prod(invsqrt, a)
|
||||
|
||||
func sqrt_invsqrt_if_square*[C](sqrt, invsqrt: var Fp[C], a: Fp[C]): SecretBool =
|
||||
## Compute the square root and ivnerse square root of ``a``
|
||||
|
@ -346,27 +233,33 @@ func sqrt_invsqrt_if_square*[C](sqrt, invsqrt: var Fp[C], a: Fp[C]): SecretBool
|
|||
## The square root, if it exist is multivalued,
|
||||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
when C.hasSqrtAddchain():
|
||||
result = sqrt_invsqrt_if_square_addchain(sqrt, invsqrt, a)
|
||||
elif C.hasP3mod4_primeModulus():
|
||||
result = sqrt_invsqrt_if_square_p3mod4(sqrt, invsqrt, a)
|
||||
else:
|
||||
result = sqrt_invsqrt_if_square_tonelli_shanks(sqrt, invsqrt, a, useAddChain = C.hasTonelliShanksAddchain())
|
||||
sqrt_invsqrt(sqrt, invsqrt, a)
|
||||
var test {.noInit.}: Fp[C]
|
||||
test.square(sqrt)
|
||||
result = test == a
|
||||
|
||||
func sqrt_if_square*[C](a: var Fp[C]): SecretBool =
|
||||
## If ``a`` is a square, compute the square root of ``a``
|
||||
## if not, ``a`` is unmodified.
|
||||
## if not, ``a`` is undefined.
|
||||
##
|
||||
## The square root, if it exist is multivalued,
|
||||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
## This procedure is constant-time
|
||||
when C.hasSqrtAddchain():
|
||||
result = sqrt_if_square_addchain(a)
|
||||
elif C.hasP3mod4_primeModulus():
|
||||
result = sqrt_if_square_p3mod4(a)
|
||||
else:
|
||||
result = sqrt_if_square_tonelli_shanks(a, useAddChain = C.hasTonelliShanksAddchain())
|
||||
var sqrt{.noInit.}, invsqrt{.noInit.}: Fp[C]
|
||||
result = sqrt_invsqrt_if_square(sqrt, invsqrt, a)
|
||||
a = sqrt
|
||||
|
||||
func invsqrt_if_square*[C](r: var Fp[C], a: Fp[C]): SecretBool =
|
||||
## If ``a`` is a square, compute the inverse square root of ``a``
|
||||
## if not, ``a`` is undefined.
|
||||
##
|
||||
## The square root, if it exist is multivalued,
|
||||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
## This procedure is constant-time
|
||||
var sqrt{.noInit.}: Fp[C]
|
||||
result = sqrt_invsqrt_if_square(sqrt, r, a)
|
||||
|
||||
{.pop.} # inline
|
||||
{.pop.} # raises no exceptions
|
||||
|
|
|
@ -398,10 +398,12 @@ func montySquare*[N](r: var Limbs[N], a, M: Limbs[N],
|
|||
montSquare_CIOS_asm_adx_bmi2(r, a, M, m0ninv, spareBits >= 1)
|
||||
else:
|
||||
montSquare_CIOS_asm(r, a, M, m0ninv, spareBits >= 1)
|
||||
else:
|
||||
elif UseASM_X86_64:
|
||||
var r2x {.noInit.}: Limbs[2*N]
|
||||
r2x.square(a)
|
||||
r.montyRedc2x(r2x, M, m0ninv, spareBits)
|
||||
else:
|
||||
montyMul(r, a, a, M, m0ninv, spareBits)
|
||||
|
||||
func redc*(r: var Limbs, a, one, M: Limbs,
|
||||
m0ninv: static BaseType, spareBits: static int) =
|
||||
|
|
|
@ -31,7 +31,7 @@ const BLS12_381_h2c_G2_Z* = Fp2[BLS12_381].fromHex( # -(2 + 𝑖)
|
|||
"0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaa9",
|
||||
"0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa"
|
||||
)
|
||||
const BLS12_381_h2c_G2_minusA* = Fp2[BLS12_381].fromHex( # -240𝑖
|
||||
const BLS12_381_h2c_G2_minus_A* = Fp2[BLS12_381].fromHex( # -240𝑖
|
||||
"0x0",
|
||||
"0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa9bb"
|
||||
)
|
||||
|
@ -60,22 +60,22 @@ const BLS12_381_h2c_G2_3_isogeny_map_xnum* = [
|
|||
# The polynomial is stored as an array of coefficients ordered from k₀ to kₙ
|
||||
|
||||
# 1
|
||||
Fp2[BLS12_381].fromHex(
|
||||
Fp2[BLS12_381].fromHex(
|
||||
"0x5c759507e8e333ebb5b7a9a47d7ed8532c52d39fd3a042a88b58423c50ae15d5c2638e343d9c71c6238aaaaaaaa97d6",
|
||||
"0x5c759507e8e333ebb5b7a9a47d7ed8532c52d39fd3a042a88b58423c50ae15d5c2638e343d9c71c6238aaaaaaaa97d6"
|
||||
),
|
||||
# x
|
||||
Fp2[BLS12_381].fromHex(
|
||||
Fp2[BLS12_381].fromHex(
|
||||
"0x0",
|
||||
"0x11560bf17baa99bc32126fced787c88f984f87adf7ae0c7f9a208c6b4f20a4181472aaa9cb8d555526a9ffffffffc71a"
|
||||
),
|
||||
# x^2
|
||||
Fp2[BLS12_381].fromHex(
|
||||
Fp2[BLS12_381].fromHex(
|
||||
"0x11560bf17baa99bc32126fced787c88f984f87adf7ae0c7f9a208c6b4f20a4181472aaa9cb8d555526a9ffffffffc71e",
|
||||
"0x8ab05f8bdd54cde190937e76bc3e447cc27c3d6fbd7063fcd104635a790520c0a395554e5c6aaaa9354ffffffffe38d"
|
||||
),
|
||||
# x^3
|
||||
Fp2[BLS12_381].fromHex(
|
||||
Fp2[BLS12_381].fromHex(
|
||||
"0x171d6541fa38ccfaed6dea691f5fb614cb14b4e7f4e810aa22d6108f142b85757098e38d0f671c7188e2aaaaaaaa5ed1",
|
||||
"0x0"
|
||||
)
|
||||
|
@ -85,17 +85,17 @@ const BLS12_381_h2c_G2_3_isogeny_map_xden* = [
|
|||
# The polynomial is stored as an array of coefficients ordered from k₀ to kₙ
|
||||
|
||||
# 1
|
||||
Fp2[BLS12_381].fromHex(
|
||||
Fp2[BLS12_381].fromHex(
|
||||
"0x0",
|
||||
"0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa63"
|
||||
),
|
||||
# x
|
||||
Fp2[BLS12_381].fromHex(
|
||||
Fp2[BLS12_381].fromHex(
|
||||
"0xc",
|
||||
"0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa9f"
|
||||
),
|
||||
# x^2
|
||||
Fp2[BLS12_381].fromHex(
|
||||
Fp2[BLS12_381].fromHex(
|
||||
"0x1",
|
||||
"0x0"
|
||||
)
|
||||
|
@ -105,22 +105,22 @@ const BLS12_381_h2c_G2_3_isogeny_map_ynum* = [
|
|||
# The polynomial is stored as an array of coefficients ordered from k₀ to kₙ
|
||||
|
||||
# y
|
||||
Fp2[BLS12_381].fromHex(
|
||||
Fp2[BLS12_381].fromHex(
|
||||
"0x1530477c7ab4113b59a4c18b076d11930f7da5d4a07f649bf54439d87d27e500fc8c25ebf8c92f6812cfc71c71c6d706",
|
||||
"0x1530477c7ab4113b59a4c18b076d11930f7da5d4a07f649bf54439d87d27e500fc8c25ebf8c92f6812cfc71c71c6d706"
|
||||
),
|
||||
# x*y
|
||||
Fp2[BLS12_381].fromHex(
|
||||
Fp2[BLS12_381].fromHex(
|
||||
"0x0",
|
||||
"0x5c759507e8e333ebb5b7a9a47d7ed8532c52d39fd3a042a88b58423c50ae15d5c2638e343d9c71c6238aaaaaaaa97be"
|
||||
),
|
||||
# x^2*y
|
||||
Fp2[BLS12_381].fromHex(
|
||||
Fp2[BLS12_381].fromHex(
|
||||
"0x11560bf17baa99bc32126fced787c88f984f87adf7ae0c7f9a208c6b4f20a4181472aaa9cb8d555526a9ffffffffc71c",
|
||||
"0x8ab05f8bdd54cde190937e76bc3e447cc27c3d6fbd7063fcd104635a790520c0a395554e5c6aaaa9354ffffffffe38f"
|
||||
),
|
||||
# x^3*y
|
||||
Fp2[BLS12_381].fromHex(
|
||||
Fp2[BLS12_381].fromHex(
|
||||
"0x124c9ad43b6cf79bfbf7043de3811ad0761b0f37a1e26286b0e977c69aa274524e79097a56dc4bd9e1b371c71c718b10",
|
||||
"0x0"
|
||||
)
|
||||
|
@ -130,23 +130,23 @@ const BLS12_381_h2c_G2_3_isogeny_map_yden* = [
|
|||
# The polynomial is stored as an array of coefficients ordered from k₀ to kₙ
|
||||
|
||||
# 1
|
||||
Fp2[BLS12_381].fromHex(
|
||||
Fp2[BLS12_381].fromHex(
|
||||
"0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa8fb",
|
||||
"0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa8fb"
|
||||
),
|
||||
# x
|
||||
Fp2[BLS12_381].fromHex(
|
||||
Fp2[BLS12_381].fromHex(
|
||||
"0x0",
|
||||
"0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa9d3"
|
||||
),
|
||||
# x^2
|
||||
Fp2[BLS12_381].fromHex(
|
||||
Fp2[BLS12_381].fromHex(
|
||||
"0x12",
|
||||
"0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa99"
|
||||
),
|
||||
# x^3
|
||||
Fp2[BLS12_381].fromHex(
|
||||
Fp2[BLS12_381].fromHex(
|
||||
"0x1",
|
||||
"0x0"
|
||||
)
|
||||
]
|
||||
]
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
# 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.
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Short Weierstrass Elliptic Curves
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
import
|
||||
elliptic/[
|
||||
ec_shortweierstrass_affine,
|
||||
ec_shortweierstrass_jacobian,
|
||||
ec_shortweierstrass_projective
|
||||
]
|
||||
|
||||
export ec_shortweierstrass_affine, ec_shortweierstrass_jacobian, ec_shortweierstrass_projective
|
||||
|
||||
func projectiveFromJacobian*[F; Tw](
|
||||
prj: var ECP_ShortW_Prj[F, Tw],
|
||||
jac: ECP_ShortW_Jac[F, Tw]) {.inline.} =
|
||||
prj.x.prod(jac.x, jac.z)
|
||||
prj.y = jac.y
|
||||
prj.z.square(jac.z)
|
||||
prj.z *= jac.z
|
||||
|
|
@ -34,6 +34,11 @@ type
|
|||
|
||||
SexticNonResidue* = NonResidue
|
||||
|
||||
func `==`*(P, Q: ECP_ShortW_Aff): SecretBool =
|
||||
## Constant-time equality check
|
||||
result = P.x == Q.x
|
||||
result = result and (P.y == Q.y)
|
||||
|
||||
func isInf*(P: ECP_ShortW_Aff): SecretBool =
|
||||
## Returns true if P is an infinity point
|
||||
## and false otherwise
|
||||
|
|
|
@ -134,11 +134,13 @@ func cneg*(P: var ECP_ShortW_Jac, ctl: CTBool) {.inline.} =
|
|||
## Negate if ``ctl`` is true
|
||||
P.y.cneg(ctl)
|
||||
|
||||
func sum*[F; Tw: static Twisted](
|
||||
template sumImpl[F; Tw: static Twisted](
|
||||
r: var ECP_ShortW_Jac[F, Tw],
|
||||
P, Q: ECP_ShortW_Jac[F, Tw]
|
||||
P, Q: ECP_ShortW_Jac[F, Tw],
|
||||
CoefA: untyped
|
||||
) =
|
||||
## Elliptic curve point addition for Short Weierstrass curves in Jacobian coordinates
|
||||
## with the curve ``a`` being a parameter for summing on isogenous curves.
|
||||
##
|
||||
## R = P + Q
|
||||
##
|
||||
|
@ -148,6 +150,8 @@ func sum*[F; Tw: static Twisted](
|
|||
## y² = x³ + a x + b
|
||||
##
|
||||
## ``r`` is initialized/overwritten with the sum
|
||||
## ``CoefA`` allows fast path for curve with a == 0 or a == -3
|
||||
## and also allows summing on curve isogenies.
|
||||
##
|
||||
## Implementation is constant-time, in particular it will not expose
|
||||
## that P == Q or P == -Q or P or Q are the infinity points
|
||||
|
@ -218,7 +222,16 @@ func sum*[F; Tw: static Twisted](
|
|||
V_or_S *= HH_or_YY # V = U₁*HH (add) or S = X₁*YY (dbl)
|
||||
|
||||
block: # Compute M for doubling
|
||||
when F.C.getCoefA() == 0:
|
||||
# "when" static evaluation doesn't shortcut booleans :/
|
||||
# which causes issues when CoefA isn't an int but Fp or Fp2
|
||||
when CoefA is int:
|
||||
const CoefA_eq_zero = CoefA == 0
|
||||
const CoefA_eq_minus3 = CoefA == -3
|
||||
else:
|
||||
const CoefA_eq_zero = false
|
||||
const CoefA_eq_minus3 = false
|
||||
|
||||
when CoefA_eq_zero:
|
||||
var a = H
|
||||
var b = HH_or_YY
|
||||
a.ccopy(P.x, isDbl) # H or X₁
|
||||
|
@ -230,7 +243,7 @@ func sum*[F; Tw: static Twisted](
|
|||
M += HHH_or_Mpre # 3X₁²/2
|
||||
R_or_M.ccopy(M, isDbl)
|
||||
|
||||
elif F.C.getCoefA() == -3:
|
||||
elif CoefA_eq_minus3:
|
||||
var a{.noInit.}, b{.noInit.}: F
|
||||
a.sum(P.x, Z1Z1)
|
||||
b.diff(P.z, Z1Z1)
|
||||
|
@ -247,7 +260,7 @@ func sum*[F; Tw: static Twisted](
|
|||
# TODO: Costly `a` coefficients can be computed
|
||||
# by merging their computation with Z₃ = Z₁*Z₂*H (add) or Z₃ = Y₁*Z₁ (dbl)
|
||||
var a = H
|
||||
var b = HH
|
||||
var b = HH_or_YY
|
||||
a.ccopy(P.x, isDbl)
|
||||
b.ccopy(P.x, isDbl)
|
||||
HHH_or_Mpre.prod(a, b) # HHH or X₁²
|
||||
|
@ -256,7 +269,8 @@ func sum*[F; Tw: static Twisted](
|
|||
a.square(HHH_or_Mpre)
|
||||
a *= HHH_or_Mpre # a = 3X₁²
|
||||
b.square(Z1Z1)
|
||||
b *= F.C.getCoefA() # b = αZZ, with α the "a" coefficient of the curve
|
||||
# b.mulCheckSparse(CoefA) # TODO: broken static compile-time type inference
|
||||
b *= CoefA # b = αZZ, with α the "a" coefficient of the curve
|
||||
|
||||
a += b
|
||||
a.div2()
|
||||
|
@ -292,6 +306,52 @@ func sum*[F; Tw: static Twisted](
|
|||
r.ccopy(Q, P.isInf())
|
||||
r.ccopy(P, Q.isInf())
|
||||
|
||||
func sum*[F; Tw: static Twisted](
|
||||
r: var ECP_ShortW_Jac[F, Tw],
|
||||
P, Q: ECP_ShortW_Jac[F, Tw],
|
||||
CoefA: static F
|
||||
) =
|
||||
## Elliptic curve point addition for Short Weierstrass curves in Jacobian coordinates
|
||||
## with the curve ``a`` being a parameter for summing on isogenous curves.
|
||||
##
|
||||
## R = P + Q
|
||||
##
|
||||
## Short Weierstrass curves have the following equation in Jacobian coordinates
|
||||
## Y² = X³ + aXZ⁴ + bZ⁶
|
||||
## from the affine equation
|
||||
## y² = x³ + a x + b
|
||||
##
|
||||
## ``r`` is initialized/overwritten with the sum
|
||||
## ``CoefA`` allows fast path for curve with a == 0 or a == -3
|
||||
## and also allows summing on curve isogenies.
|
||||
##
|
||||
## Implementation is constant-time, in particular it will not expose
|
||||
## that P == Q or P == -Q or P or Q are the infinity points
|
||||
## to simple side-channel attacks (SCA)
|
||||
## This is done by using a "complete" or "exception-free" addition law.
|
||||
r.sumImpl(P, Q, CoefA)
|
||||
|
||||
func sum*[F; Tw: static Twisted](
|
||||
r: var ECP_ShortW_Jac[F, Tw],
|
||||
P, Q: ECP_ShortW_Jac[F, Tw]
|
||||
) =
|
||||
## Elliptic curve point addition for Short Weierstrass curves in Jacobian coordinates
|
||||
##
|
||||
## R = P + Q
|
||||
##
|
||||
## Short Weierstrass curves have the following equation in Jacobian coordinates
|
||||
## Y² = X³ + aXZ⁴ + bZ⁶
|
||||
## from the affine equation
|
||||
## y² = x³ + a x + b
|
||||
##
|
||||
## ``r`` is initialized/overwritten with the sum
|
||||
##
|
||||
## Implementation is constant-time, in particular it will not expose
|
||||
## that P == Q or P == -Q or P or Q are the infinity points
|
||||
## to simple side-channel attacks (SCA)
|
||||
## This is done by using a "complete" or "exception-free" addition law.
|
||||
r.sumImpl(P, Q, F.C.getCoefA())
|
||||
|
||||
func madd*[F; Tw: static Twisted](
|
||||
r: var ECP_ShortW_Jac[F, Tw],
|
||||
P: ECP_ShortW_Jac[F, Tw],
|
||||
|
|
|
@ -14,7 +14,8 @@ import
|
|||
../towers,
|
||||
../config/curves,
|
||||
../io/io_bigints,
|
||||
../elliptic/[ec_shortweierstrass_projective, ec_scalar_mul]
|
||||
../elliptic/[ec_shortweierstrass_projective, ec_scalar_mul],
|
||||
../isogeny/frobenius
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
|
@ -96,3 +97,97 @@ func clearCofactorReference*(P: var ECP_ShortW_Prj[Fp[BW6_761], OnTwist]) {.inli
|
|||
## Clear the cofactor of BW6_761 G2
|
||||
# Endomorphism acceleration cannot be used if cofactor is not cleared
|
||||
P.scalarMulGeneric(Cofactor_Eff_BW6_761_G2)
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Clear Cofactor - Optimized
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
# BLS12 G2
|
||||
# ------------------------------------------------------------
|
||||
# From any point on the elliptic curve E2 of a BLS12 curve
|
||||
# Obtain a point in the G2 prime-order subgroup
|
||||
#
|
||||
# Described in https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-G.4
|
||||
#
|
||||
# Implementations, multiple implementations are possible in increasing order of speed:
|
||||
#
|
||||
# - The default, canonical, implementation is h_eff * P
|
||||
# - Scott et al, "Fast Hashing to G2 on Pairing-Friendly Curves", https://doi.org/10.1007/978-3-642-03298-1_8
|
||||
# - Fuentes-Castaneda et al, "Fast Hashing to G2 on Pairing-Friendly Curves", https://doi.org/10.1007/978-3-642-28496-0_25
|
||||
# - Budroni et al, "Hashing to G2 on BLS pairing-friendly curves", https://doi.org/10.1145/3313880.3313884
|
||||
# - Wahby et al "Fast and simple constant-time hashing to the BLS12-381 elliptic curve", https://eprint.iacr.org/2019/403
|
||||
# - IETF "Hashing to Elliptic Curves", https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-G.4
|
||||
#
|
||||
# In summary, the elliptic curve point multiplication is very expensive,
|
||||
# the fast methods uses endomorphism acceleration instead.
|
||||
#
|
||||
# The method described in Wahby et al is implemented by Riad Wahby
|
||||
# in C at: https://github.com/kwantam/bls12-381_hash/blob/23c1930039f58606138459557677668fabc8ce39/src/curve2/ops2.c#L106-L204
|
||||
# following Budroni et al, "Efficient hash maps to G2 on BLS curves"
|
||||
# https://eprint.iacr.org/2017/419
|
||||
#
|
||||
# "P -> [x² - x - 1] P + [x - 1] ψ(P) + ψ(ψ([2]P))"
|
||||
#
|
||||
# with Psi (ψ) - untwist-Frobenius-Twist function
|
||||
# and x the curve BLS parameter
|
||||
|
||||
func double_repeated*[EC](P: var EC, num: int) {.inline.} =
|
||||
## Repeated doublings
|
||||
for _ in 0 ..< num:
|
||||
P.double()
|
||||
|
||||
func pow_x(
|
||||
r{.noalias.}: var ECP_ShortW_Prj[Fp2[BLS12_381], OnTwist],
|
||||
P{.noalias.}: ECP_ShortW_Prj[Fp2[BLS12_381], OnTwist],
|
||||
) =
|
||||
## Does the scalar multiplication [x]P
|
||||
## with x the BLS12 curve parameter
|
||||
## For BLS12_381 [-0xd201000000010000]P
|
||||
## Requires r and P to not alias
|
||||
|
||||
# In binary
|
||||
# 0b11
|
||||
r.double(P)
|
||||
r += P
|
||||
# 0b1101
|
||||
r.double_repeated(2)
|
||||
r += P
|
||||
# 0b1101001
|
||||
r.double_repeated(3)
|
||||
r += P
|
||||
# 0b1101001000000001
|
||||
r.double_repeated(9)
|
||||
r += P
|
||||
# 0b110100100000000100000000000000000000000000000001
|
||||
r.double_repeated(32)
|
||||
r += P
|
||||
# 0b1101001000000001000000000000000000000000000000010000000000000000
|
||||
r.double_repeated(16)
|
||||
|
||||
# Negative, x = -0xd201000000010000
|
||||
r.neg(r)
|
||||
|
||||
|
||||
func clearCofactorFast*(P: var ECP_ShortW_Prj[Fp2[BLS12_381], OnTwist]) =
|
||||
## Clear the cofactor of BLS12_381 G2
|
||||
## Optimized using endomorphisms
|
||||
## P -> [x²-x-1]P + [x-1] ψ(P) + ψ²([2]P)
|
||||
|
||||
var xP{.noInit.}, x2P{.noInit.}: typeof(P)
|
||||
|
||||
xP.pow_x(P) # 1. xP = [x]P
|
||||
x2P.pow_x(xP) # 2. x2P = [x²]P
|
||||
|
||||
x2P.diff(x2P, xP) # 3. x2P = [x²-x]P
|
||||
x2P.diff(x2P, P) # 4. x2P = [x²-x-1]P
|
||||
|
||||
xP.diff(xP, P) # 5. xP = [x-1]P
|
||||
xP.frobenius_psi(xP) # 6. xP = ψ([x-1]P) = [x-1] ψ(P)
|
||||
|
||||
P.double(P) # 7. P = [2]P
|
||||
P.frobenius_psi(P, k=2) # 8. P = ψ²([2]P)
|
||||
|
||||
P.sum(P, x2P) # 9. P = [x²-x-1]P + ψ²([2]P)
|
||||
P.sum(P, xP) # 10. P = [x²-x-1]P + [x-1] ψ(P) + ψ²([2]P)
|
||||
|
|
|
@ -30,12 +30,11 @@ func ceilDiv(a, b: uint): uint =
|
|||
## ceil(a / b)
|
||||
(a + b - 1) div b
|
||||
|
||||
proc copyFrom[N](output: var openarray[byte], bi: array[N, byte], cur: var uint) =
|
||||
var b_index = 0'u
|
||||
while b_index < bi.len.uint and cur < output.len.uint:
|
||||
output[cur] = bi[b_index]
|
||||
inc cur
|
||||
inc b_index
|
||||
proc copyFrom[M, N: static int](output: var array[M, byte], bi: array[N, byte], cur: var uint) =
|
||||
static: doAssert M mod N == 0
|
||||
for i in 0'u ..< N:
|
||||
output[cur+i] = bi[i]
|
||||
cur += N.uint
|
||||
|
||||
template strxor(b_i: var array, b0: array): untyped =
|
||||
for i in 0 ..< b_i.len:
|
||||
|
@ -57,9 +56,9 @@ func shortDomainSepTag[DigestSize: static int, B: byte|char](
|
|||
ctx.update oversizedDST
|
||||
ctx.finish(output)
|
||||
|
||||
func expandMessageXMD*[B1, B2, B3: byte|char](
|
||||
func expandMessageXMD*[B1, B2, B3: byte|char, len_in_bytes: static int](
|
||||
H: type CryptoHash,
|
||||
output: var openarray[byte],
|
||||
output: var array[len_in_bytes, byte],
|
||||
augmentation: openarray[B1],
|
||||
message: openarray[B2],
|
||||
domainSepTag: openarray[B3]
|
||||
|
@ -110,23 +109,23 @@ func expandMessageXMD*[B1, B2, B3: byte|char](
|
|||
const DigestSize = Hash.digestSize()
|
||||
const BlockSize = Hash.internalBlockSize()
|
||||
|
||||
assert output.len mod 8 == 0
|
||||
static:
|
||||
doAssert output.len mod 8 == 0 # By spec
|
||||
doAssert output.len mod 32 == 0 # Assumed by copy optimization
|
||||
|
||||
let ell = ceilDiv(output.len.uint, DigestSize.uint)
|
||||
const zPad = default(array[BlockSize, byte])
|
||||
let l_i_b_str = output.len.uint16.toBytesBE()
|
||||
var l_i_b_str0 {.noInit.}: array[3, byte]
|
||||
l_i_b_str0.asBytesBE(output.len.uint16, pos = 0)
|
||||
l_i_b_str0[2] = 0
|
||||
|
||||
var b0 {.noinit, align: DigestSize.}: array[DigestSize, byte]
|
||||
func ctZpad(): Hash =
|
||||
# Compile-time precompute
|
||||
# TODO upstream: `toOpenArray` throws "cannot generate code for: mSlice"
|
||||
result.init()
|
||||
result.update zPad
|
||||
var ctx = ctZpad() # static(ctZpad())
|
||||
var ctx {.noInit.}: Hash
|
||||
ctx.initZeroPadded()
|
||||
ctx.update augmentation
|
||||
ctx.update message
|
||||
ctx.update l_i_b_str
|
||||
ctx.update [byte 0]
|
||||
ctx.update l_i_b_str0
|
||||
# ctx.update [byte 0] # already appended to l_i_b_str
|
||||
ctx.update domainSepTag
|
||||
ctx.update [byte domainSepTag.len] # DST_prime
|
||||
ctx.finish(b0)
|
||||
|
|
|
@ -109,8 +109,7 @@ func invsqrt_if_square[C: static Curve](
|
|||
t2 *= NonResidue
|
||||
t1 -= t2
|
||||
|
||||
# TODO: implement invsqrt alone
|
||||
result = sqrt_invsqrt_if_square(sqrt = r.c1, invsqrt = t3, t1) # 1/sqrt(a0² - β a1²)
|
||||
result = t3.invsqrt_if_square(t1) # 1/sqrt(a0² - β a1²)
|
||||
|
||||
# If input is not a square in Fp2, multiply by 1/Z³
|
||||
inp.prod(a, h2cConst(C, G2, inv_Z3)) # inp = a / Z³
|
||||
|
@ -131,8 +130,7 @@ func invsqrt_if_square[C: static Curve](
|
|||
t1.ccopy(t2, t1.isZero())
|
||||
t1.div2() # (a0 ± sqrt(a0² - βa1²))/2
|
||||
|
||||
# TODO: implement invsqrt alone
|
||||
sqrt_invsqrt(sqrt = r.c1, invsqrt = r.c0, t1)
|
||||
r.c0.invsqrt(t1)
|
||||
|
||||
r.c1 = inp.c1
|
||||
r.c1.div2()
|
||||
|
@ -152,19 +150,22 @@ func invsqrt_if_square[C: static Curve](
|
|||
|
||||
func mapToIsoCurve_sswuG2_opt9mod16*[C: static Curve](
|
||||
xn, xd, yn: var Fp2[C],
|
||||
u: Fp2[C]) =
|
||||
u: Fp2[C], xd3: var Fp2[C]) =
|
||||
## Given G2, the target prime order subgroup of E2 we want to hash to,
|
||||
## this function maps any field element of Fp2 to E'2
|
||||
## a curve isogenous to E2 using the Simplified Shallue-van de Woestijne method.
|
||||
##
|
||||
## This requires p² ≡ 9 (mod 16).
|
||||
#
|
||||
# Input:
|
||||
# - u, an Fp2 element
|
||||
# Output:
|
||||
# - (xn, xd, yn, yd) such that (x', y') = (xn/xd, yn/yd)
|
||||
# is a point of E'2
|
||||
# - yd is implied to be 1
|
||||
##
|
||||
## Input:
|
||||
## - u, an Fp2 element
|
||||
## Output:
|
||||
## - (xn, xd, yn, yd) such that (x', y') = (xn/xd, yn/yd)
|
||||
## is a point of E'2
|
||||
## - yd is implied to be 1
|
||||
## Scratchspace:
|
||||
## - xd3 is temporary scratchspace that will hold xd³
|
||||
## after execution (which might be useful for Jacobian coordinate conversion)
|
||||
#
|
||||
# Paper: https://eprint.iacr.org/2019/403
|
||||
# Spec: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-G.2.3
|
||||
|
@ -175,7 +176,6 @@ func mapToIsoCurve_sswuG2_opt9mod16*[C: static Curve](
|
|||
var
|
||||
uu {.noInit.}, tv2 {.noInit.}: Fp2[C]
|
||||
tv4 {.noInit.}, x2n {.noInit.}, gx1 {.noInit.}: Fp2[C]
|
||||
gxd {.noInit.}: Fp2[C]
|
||||
y2 {.noInit.}: Fp2[C]
|
||||
e1, e2: SecretBool
|
||||
|
||||
|
@ -184,6 +184,7 @@ func mapToIsoCurve_sswuG2_opt9mod16*[C: static Curve](
|
|||
template x1n: untyped = xn
|
||||
template y1: untyped = yn
|
||||
template Zuu: untyped = x2n
|
||||
template gxd: untyped = xd3
|
||||
|
||||
# x numerators
|
||||
uu.square(u) # uu = u²
|
||||
|
@ -203,7 +204,7 @@ func mapToIsoCurve_sswuG2_opt9mod16*[C: static Curve](
|
|||
# y numerators
|
||||
tv2.square(xd)
|
||||
gxd.prod(xd, tv2) # gxd = xd³
|
||||
tv2 *= h2CConst(C, G2, Aprime_E2)
|
||||
tv2.mulCheckSparse(h2CConst(C, G2, Aprime_E2))
|
||||
gx1.square(x1n)
|
||||
gx1 += tv2 # x1n² + A * xd²
|
||||
gx1 *= x1n # x1n³ + A * x1n * xd²
|
||||
|
|
|
@ -11,7 +11,7 @@ import
|
|||
../config/[common, curves],
|
||||
../primitives, ../arithmetic, ../towers,
|
||||
../curves/zoo_hash_to_curve,
|
||||
../elliptic/ec_shortweierstrass_projective,
|
||||
../ec_shortweierstrass,
|
||||
./h2c_hash_to_field,
|
||||
./h2c_map_to_isocurve_swu,
|
||||
./cofactors,
|
||||
|
@ -34,8 +34,28 @@ import
|
|||
# Map to curve
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
func mapToIsoCurve_sswuG2_opt9mod16[F; Tw: static Twisted](
|
||||
r: var ECP_ShortW_Jac[F, Tw],
|
||||
u: F) =
|
||||
var
|
||||
xn{.noInit.}, xd{.noInit.}: F
|
||||
yn{.noInit.}: F
|
||||
xd3{.noInit.}: F
|
||||
|
||||
mapToIsoCurve_sswuG2_opt9mod16(
|
||||
xn, xd,
|
||||
yn,
|
||||
u, xd3
|
||||
)
|
||||
|
||||
# Convert to Jacobian
|
||||
r.z = xd # Z = xd
|
||||
r.x.prod(xn, xd) # X = xZ² = xn/xd * xd² = xn*xd
|
||||
r.y.prod(yn, xd3) # Y = yZ³ = yn * xd³
|
||||
|
||||
func mapToCurve[F; Tw: static Twisted](
|
||||
r: var ECP_ShortW_Prj[F, Tw], u: F) =
|
||||
r: var (ECP_ShortW_Prj[F, Tw] or ECP_ShortW_Jac[F, Tw]),
|
||||
u: F) =
|
||||
## Map an element of the
|
||||
## finite or extension field F
|
||||
## to an elliptic curve E
|
||||
|
@ -48,11 +68,12 @@ func mapToCurve[F; Tw: static Twisted](
|
|||
var
|
||||
xn{.noInit.}, xd{.noInit.}: F
|
||||
yn{.noInit.}: F
|
||||
xd3{.noInit.}: F
|
||||
|
||||
mapToIsoCurve_sswuG2_opt9mod16(
|
||||
xn, xd,
|
||||
yn,
|
||||
u
|
||||
u, xd3
|
||||
)
|
||||
|
||||
# 2. Map from E'2 to E2
|
||||
|
@ -64,6 +85,39 @@ func mapToCurve[F; Tw: static Twisted](
|
|||
else:
|
||||
{.error: "Not implemented".}
|
||||
|
||||
func mapToCurve_fusedAdd[F; Tw: static Twisted](
|
||||
r: var ECP_ShortW_Jac[F, Tw],
|
||||
u0, u1: F) =
|
||||
## Map 2 elements of the
|
||||
## finite or extension field F
|
||||
## to an elliptic curve E
|
||||
## and add them
|
||||
# Optimization suggested in https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-6.6.3
|
||||
# Note that iso_map is a group homomorphism, meaning that point
|
||||
# addition commutes with iso_map. Thus, when using this mapping in the
|
||||
# hash_to_curve construction of Section 3, one can effect a small
|
||||
# optimization by first mapping u0 and u1 to E', adding the resulting
|
||||
# points on E', and then applying iso_map to the sum. This gives the
|
||||
# same result while requiring only one evaluation of iso_map.
|
||||
|
||||
# Jacobian formulae are independent of the curve equation B'
|
||||
# y² = x³ + A'*x + B'
|
||||
# unlike the complete projective formulae which heavily depends on it
|
||||
# So we use jacobian coordinates for computation on isogenies.
|
||||
|
||||
var P0{.noInit.}, P1{.noInit.}: ECP_ShortW_Jac[F, Tw]
|
||||
when F.C == BLS12_381 and F is Fp2:
|
||||
# 1. Map to E'2 isogenous to E2
|
||||
P0.mapToIsoCurve_sswuG2_opt9mod16(u0)
|
||||
P1.mapToIsoCurve_sswuG2_opt9mod16(u1)
|
||||
|
||||
P0.sum(P0, P1, h2CConst(F.C, G2, Aprime_E2))
|
||||
|
||||
# 2. Map from E'2 to E2
|
||||
r.h2c_isogeny_map(P0, isodegree = 3)
|
||||
else:
|
||||
{.error: "Not implemented".}
|
||||
|
||||
# Hash to curve
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
|
@ -102,9 +156,14 @@ func hashToCurve*[
|
|||
var u{.noInit.}: array[2, F]
|
||||
H.hashToField(k, u, augmentation, message, domainSepTag)
|
||||
|
||||
var P{.noInit.}: array[2, ECP_ShortW_Prj[F, Tw]]
|
||||
P[0].mapToCurve(u[0])
|
||||
P[1].mapToCurve(u[1])
|
||||
when false:
|
||||
var P{.noInit.}: array[2, ECP_ShortW_Prj[F, Tw]]
|
||||
P[0].mapToCurve(u[0])
|
||||
P[1].mapToCurve(u[1])
|
||||
output.sum(P[0], P[1])
|
||||
else:
|
||||
var Pjac{.noInit.}: ECP_ShortW_Jac[F, Tw]
|
||||
Pjac.mapToCurve_fusedAdd(u[0], u[1])
|
||||
output.projectiveFromJacobian(Pjac)
|
||||
|
||||
output.sum(P[0], P[1])
|
||||
output.clearCofactorReference() # TODO - fast cofactor clear
|
||||
output.clearCofactorFast()
|
||||
|
|
|
@ -315,14 +315,37 @@ func init*(ctx: var Sha256Context) =
|
|||
ctx.buf.setZero()
|
||||
ctx.bufIdx = 0
|
||||
|
||||
ctx.H[0] = 0x6a09e667'u32;
|
||||
ctx.H[1] = 0xbb67ae85'u32;
|
||||
ctx.H[2] = 0x3c6ef372'u32;
|
||||
ctx.H[3] = 0xa54ff53a'u32;
|
||||
ctx.H[4] = 0x510e527f'u32;
|
||||
ctx.H[5] = 0x9b05688c'u32;
|
||||
ctx.H[6] = 0x1f83d9ab'u32;
|
||||
ctx.H[7] = 0x5be0cd19'u32;
|
||||
ctx.H[0] = 0x6a09e667'u32
|
||||
ctx.H[1] = 0xbb67ae85'u32
|
||||
ctx.H[2] = 0x3c6ef372'u32
|
||||
ctx.H[3] = 0xa54ff53a'u32
|
||||
ctx.H[4] = 0x510e527f'u32
|
||||
ctx.H[5] = 0x9b05688c'u32
|
||||
ctx.H[6] = 0x1f83d9ab'u32
|
||||
ctx.H[7] = 0x5be0cd19'u32
|
||||
|
||||
func initZeroPadded*(ctx: var Sha256Context) =
|
||||
## Initialize a Sha256 context
|
||||
## with the result of
|
||||
## ctx.init()
|
||||
## ctx.update default(array[BlockSize, byte])
|
||||
#
|
||||
# This work arounds `toOpenArray`
|
||||
# not working in the Nim VM, preventing `sha256.update`
|
||||
# at compile-time
|
||||
|
||||
ctx.msgLen = 64
|
||||
ctx.buf.setZero()
|
||||
ctx.bufIdx = 0
|
||||
|
||||
ctx.H[0] = 0xda5698be'u32
|
||||
ctx.H[1] = 0x17b9b469'u32
|
||||
ctx.H[2] = 0x62335799'u32
|
||||
ctx.H[3] = 0x779fbeca'u32
|
||||
ctx.H[4] = 0x8ce5d491'u32
|
||||
ctx.H[5] = 0xc0d26243'u32
|
||||
ctx.H[6] = 0xbafef9ea'u32
|
||||
ctx.H[7] = 0x1837a9d8'u32
|
||||
|
||||
func update*[T: char|byte](ctx: var Sha256Context, message: openarray[T]) =
|
||||
## Append a message to a SHA256 context
|
||||
|
|
|
@ -77,8 +77,13 @@ func dumpRawInt*[T: byte|char](
|
|||
for i in 0'u ..< L:
|
||||
dst[cursor+i] = toByte(src shr ((L-i-1) * 8))
|
||||
|
||||
func toBytesBE*(num: SomeUnsignedInt): array[sizeof(num), byte] {.inline.}=
|
||||
## Convert an integer to an array of bytes
|
||||
func asBytesBE*[N: static int](
|
||||
r: var array[N, byte],
|
||||
num: SomeUnsignedInt,
|
||||
pos: static int) {.inline.}=
|
||||
## Store an integer into an array of bytes
|
||||
## in big endian representation
|
||||
const L = sizeof(num)
|
||||
static: doAssert N - (pos + L) >= 0
|
||||
for i in 0 ..< L:
|
||||
result[i] = toByte(num shr ((L-1-i) * 8))
|
||||
r[i] = toByte(num shr ((L-1-i) * 8))
|
||||
|
|
|
@ -10,7 +10,10 @@ import
|
|||
# Internals
|
||||
../primitives, ../arithmetic, ../towers,
|
||||
../curves/zoo_hash_to_curve,
|
||||
../elliptic/ec_shortweierstrass_projective
|
||||
../elliptic/[
|
||||
ec_shortweierstrass_projective,
|
||||
ec_shortweierstrass_jacobian,
|
||||
]
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
|
@ -78,6 +81,51 @@ func poly_eval_horner_scaled[F; D, N: static int](
|
|||
# Missing scaling factor
|
||||
r *= xd_pow[isodegree - poly_degree - 1]
|
||||
|
||||
func h2c_isogeny_map[F](
|
||||
rxn, rxd, ryn, ryd: var F,
|
||||
xn, xd, yn: F, isodegree: static int) =
|
||||
## Given G2, the target prime order subgroup of E2,
|
||||
## this function maps an element of
|
||||
## E'2 a curve isogenous to E2
|
||||
## to E2.
|
||||
##
|
||||
## The E'2 input is represented as
|
||||
## (x', y') with x' = xn/xd and y' = yn/yd
|
||||
##
|
||||
## yd is assumed to be 1 hence y' == yn
|
||||
##
|
||||
## The E2 output is represented as
|
||||
## (rx, ry) with rx = rxn/rxd and ry = ryn/ryd
|
||||
|
||||
# xd^e with e in [1, N], for example [xd, xd², xd³]
|
||||
static: doAssert isodegree >= 2
|
||||
var xd_pow{.noInit.}: array[isodegree, F]
|
||||
xd_pow[0] = xd
|
||||
xd_pow[1].square(xd_pow[0])
|
||||
for i in 2 ..< xd_pow.len:
|
||||
xd_pow[i].prod(xd_pow[i-1], xd_pow[0])
|
||||
|
||||
rxn.poly_eval_horner_scaled(
|
||||
xn, xd_pow,
|
||||
h2cIsomapPoly(F.C, G2, isodegree, xnum)
|
||||
)
|
||||
rxd.poly_eval_horner_scaled(
|
||||
xn, xd_pow,
|
||||
h2cIsomapPoly(F.C, G2, isodegree, xden)
|
||||
)
|
||||
|
||||
ryn.poly_eval_horner_scaled(
|
||||
xn, xd_pow,
|
||||
h2cIsomapPoly(F.C, G2, isodegree, ynum)
|
||||
)
|
||||
ryd.poly_eval_horner_scaled(
|
||||
xn, xd_pow,
|
||||
h2cIsomapPoly(F.C, G2, isodegree, yden)
|
||||
)
|
||||
|
||||
# y coordinate is y' * poly_yNum(x)
|
||||
ryn *= yn
|
||||
|
||||
func h2c_isogeny_map*[F; Tw: static Twisted](
|
||||
r: var ECP_ShortW_Prj[F, Tw],
|
||||
xn, xd, yn: F, isodegree: static int) =
|
||||
|
@ -95,33 +143,15 @@ func h2c_isogeny_map*[F; Tw: static Twisted](
|
|||
## - https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-6.6.3
|
||||
## - https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve
|
||||
|
||||
# xd^e with e in [1, N], for example [xd, xd², xd³]
|
||||
var xd_pow: array[isodegree, F]
|
||||
xd_pow[0] = xd
|
||||
for i in 1 ..< xd_pow.len:
|
||||
xd_pow[i].prod(xd_pow[i-1], xd)
|
||||
var t{.noInit.}: F
|
||||
|
||||
r.x.poly_eval_horner_scaled(
|
||||
xn, xd_pow,
|
||||
h2cIsomapPoly(F.C, G2, isodegree, xnum)
|
||||
h2c_isogeny_map(
|
||||
rxn = r.x,
|
||||
rxd = r.z,
|
||||
ryn = r.y,
|
||||
ryd = t,
|
||||
xn, xd, yn, isodegree
|
||||
)
|
||||
r.z.poly_eval_horner_scaled(
|
||||
xn, xd_pow,
|
||||
h2cIsomapPoly(F.C, G2, isodegree, xden)
|
||||
)
|
||||
|
||||
r.y.poly_eval_horner_scaled(
|
||||
xn, xd_pow,
|
||||
h2cIsomapPoly(F.C, G2, isodegree, ynum)
|
||||
)
|
||||
var t {.noInit.}: F
|
||||
t.poly_eval_horner_scaled(
|
||||
xn, xd_pow,
|
||||
h2cIsomapPoly(F.C, G2, isodegree, yden)
|
||||
)
|
||||
|
||||
# y coordinate is y' * poly_yNum(x)
|
||||
r.y *= yn
|
||||
|
||||
# Now convert to projective coordinates
|
||||
# (x, y) => (xnum/xden, ynum/yden)
|
||||
|
@ -129,3 +159,103 @@ func h2c_isogeny_map*[F; Tw: static Twisted](
|
|||
r.y *= r.z
|
||||
r.x *= t
|
||||
r.z *= t
|
||||
|
||||
func h2c_isogeny_map*[F; Tw: static Twisted](
|
||||
r: var ECP_ShortW_Jac[F, Tw],
|
||||
xn, xd, yn: F, isodegree: static int) =
|
||||
## Given G2, the target prime order subgroup of E2,
|
||||
## this function maps an element of
|
||||
## E'2 a curve isogenous to E2
|
||||
## to E2.
|
||||
##
|
||||
## The E'2 input is represented as
|
||||
## (x', y') with x' = xn/xd and y' = yn/yd
|
||||
##
|
||||
## yd is assumed to be 1 hence y' == yn
|
||||
##
|
||||
## Reference:
|
||||
## - https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-6.6.3
|
||||
## - https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve
|
||||
|
||||
var rxn{.noInit.}, rxd{.noInit.}: F
|
||||
var ryn{.noInit.}, ryd{.noInit.}: F
|
||||
|
||||
h2c_isogeny_map(
|
||||
rxn, rxd,
|
||||
ryn, ryd,
|
||||
xn, xd, yn, isodegree
|
||||
)
|
||||
|
||||
# Now convert to jacobian coordinates
|
||||
# (x, y) => (xnum/xden, ynum/yden)
|
||||
# <=> (xZ², yZ³, Z)
|
||||
# <=> (xnum*xden*yden², ynum*yden²*xden³, xden*yden)
|
||||
r.z.prod(rxd, ryd) # Z = xn * xd
|
||||
r.x.prod(rxn, ryd) # X = xn * yd
|
||||
r.x *= r.z # X = xn * xd * yd²
|
||||
r.y.square(r.z) # Y = xd² * yd²
|
||||
r.y *= rdx # Y = yd² * xd³
|
||||
r.y *= ryn # Y = yn * yd² * xd³
|
||||
|
||||
func h2c_isogeny_map*[F; Tw: static Twisted](
|
||||
r: var ECP_ShortW_Jac[F, Tw],
|
||||
P: ECP_ShortW_Jac[F, Tw],
|
||||
isodegree: static int) =
|
||||
## Map P in isogenous curve E'2
|
||||
## to r in E2
|
||||
##
|
||||
## r and P are NOT on the same curve.
|
||||
#
|
||||
# We have in affine <=> jacobian representation
|
||||
# (x, y) <=> (xn/xd, yn/yd)
|
||||
# <=> (xZ², yZ³, Z)
|
||||
#
|
||||
# We scale the isogeny coefficients by powers
|
||||
# of Z²
|
||||
|
||||
var xn{.noInit.}, xd{.noInit.}: F
|
||||
var yn{.noInit.}, yd{.noInit.}: F
|
||||
|
||||
# Z²^e with e in [1, N], for example [Z², Z⁴, Z⁶]
|
||||
static: doAssert isodegree >= 2
|
||||
var ZZpow{.noInit.}: array[isodegree, F]
|
||||
ZZpow[0].square(P.z)
|
||||
ZZpow[1].square(ZZpow[0])
|
||||
for i in 2 ..< ZZpow.len:
|
||||
ZZpow[i].prod(ZZpow[i-1], ZZpow[0])
|
||||
|
||||
xn.poly_eval_horner_scaled(
|
||||
P.x, ZZpow,
|
||||
h2cIsomapPoly(F.C, G2, isodegree, xnum)
|
||||
)
|
||||
xd.poly_eval_horner_scaled(
|
||||
P.x, ZZpow,
|
||||
h2cIsomapPoly(F.C, G2, isodegree, xden)
|
||||
)
|
||||
|
||||
yn.poly_eval_horner_scaled(
|
||||
P.x, ZZpow,
|
||||
h2cIsomapPoly(F.C, G2, isodegree, ynum)
|
||||
)
|
||||
yd.poly_eval_horner_scaled(
|
||||
P.x, ZZpow,
|
||||
h2cIsomapPoly(F.C, G2, isodegree, yden)
|
||||
)
|
||||
|
||||
# yn = y' * poly_yNum(x) = yZ³ * poly_yNum(x)
|
||||
yn *= P.y
|
||||
|
||||
# Scale yd by Z³
|
||||
yd *= P.z
|
||||
yd *= ZZpow[0]
|
||||
|
||||
# Now convert to jacobian coordinates
|
||||
# (x, y) => (xnum/xden, ynum/yden)
|
||||
# <=> (xZ², yZ³, Z)
|
||||
# <=> (xnum*xden*yden², ynum*yden²*xden³, xden*yden)
|
||||
r.z.prod(xd, yd) # Z = xn * xd
|
||||
r.x.prod(xn, yd) # X = xn * yd
|
||||
r.x *= r.z # X = xn * xd * yd²
|
||||
r.y.square(r.z) # Y = xd² * yd²
|
||||
r.y *= xd # Y = yd² * xd³
|
||||
r.y *= yn # Y = yn * yd² * xd³
|
||||
|
|
|
@ -133,10 +133,8 @@ func sqrt_if_square_opt(a: var Fp2): SecretBool =
|
|||
t1.ccopy(t2, t1.isZero())
|
||||
t1.div2() # (a0 ± sqrt(a0² - β a1²))/2
|
||||
|
||||
# TODO: implement invsqrt alone
|
||||
# t1 being an actual sqrt will be tested in sqrt_rotate_extension
|
||||
# 1/sqrt((a0 ± sqrt(a0² - β b²))/2)
|
||||
sqrt_invsqrt(sqrt = cand.c1, invsqrt = cand.c0, t1) # we only want invsqrt = cand.c0
|
||||
cand.c0.invsqrt(t1) # 1/sqrt((a0 ± sqrt(a0² - β b²))/2)
|
||||
|
||||
cand.c1 = a.c1
|
||||
cand.c1.div2()
|
||||
|
|
|
@ -181,7 +181,7 @@ def genBLS12381G2_H2C_constants(curve_config):
|
|||
buf += field_to_nim(Z, 'Fp2', curve_name, comment_right = "-(2 + 𝑖)")
|
||||
buf += '\n'
|
||||
|
||||
buf += f'const {curve_name}_h2c_G2_minusA* = '
|
||||
buf += f'const {curve_name}_h2c_G2_minus_A* = '
|
||||
buf += field_to_nim(minus_A, 'Fp2', curve_name, comment_right = "-240𝑖")
|
||||
buf += '\n'
|
||||
|
||||
|
|
|
@ -80,7 +80,6 @@ proc exhaustiveCheck(C: static Curve, modulus: static int) =
|
|||
check:
|
||||
bool not a.isSquare()
|
||||
bool not a.sqrt_if_square()
|
||||
bool (a == a2) # a shouldn't be modified
|
||||
|
||||
template testImpl(a: untyped): untyped {.dirty.} =
|
||||
var na{.noInit.}: typeof(a)
|
||||
|
|
|
@ -61,7 +61,7 @@ proc runFrobeniusTowerTests*[N](
|
|||
) =
|
||||
# Random seed for reproducibility
|
||||
var rng: RngState
|
||||
let seed = 0 # uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32
|
||||
let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32
|
||||
rng.seed(seed)
|
||||
echo moduleName, " xoshiro512** seed: ", seed
|
||||
|
||||
|
|
Loading…
Reference in New Issue