mirror of
https://github.com/logos-storage/constantine.git
synced 2026-01-08 16:13:14 +00:00
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()
|
discard r.sqrt_if_square()
|
||||||
|
|
||||||
proc sqrtP3mod4Bench*(T: typedesc, iters: int) =
|
proc sqrtP3mod4Bench*(T: typedesc, iters: int) =
|
||||||
|
var r: T
|
||||||
let x = rng.random_unsafe(T)
|
let x = rng.random_unsafe(T)
|
||||||
bench("SquareRoot + isSquare (p ≡ 3 (mod 4) exponentiation)", T, iters):
|
bench("SquareRoot (p ≡ 3 (mod 4) exponentiation)", T, iters):
|
||||||
var r = x
|
r.invsqrt_p3mod4(x)
|
||||||
discard r.sqrt_if_square_p3mod4()
|
r *= x
|
||||||
|
|
||||||
proc sqrtAddChainBench*(T: typedesc, iters: int) =
|
proc sqrtAddChainBench*(T: typedesc, iters: int) =
|
||||||
|
var r: T
|
||||||
let x = rng.random_unsafe(T)
|
let x = rng.random_unsafe(T)
|
||||||
bench("SquareRoot + isSquare (addition chain)", T, iters):
|
bench("SquareRoot (addition chain)", T, iters):
|
||||||
var r = x
|
r.invsqrt_addchain(x)
|
||||||
discard r.sqrt_if_square_addchain()
|
r *= x
|
||||||
|
|
||||||
proc sqrtTonelliBench*(T: typedesc, iters: int) =
|
proc sqrtTonelliBench*(T: typedesc, iters: int) =
|
||||||
|
var r: T
|
||||||
let x = rng.random_unsafe(T)
|
let x = rng.random_unsafe(T)
|
||||||
bench("SquareRoot + isSquare (constant-time Tonelli-Shanks exponentiation)", T, iters):
|
bench("SquareRoot (constant-time Tonelli-Shanks exponentiation)", T, iters):
|
||||||
var r = x
|
r.invsqrt_tonelli_shanks(x, useAddChain = false)
|
||||||
discard r.sqrt_if_square_tonelli_shanks(useAddChain = false)
|
r *= x
|
||||||
|
|
||||||
proc sqrtTonelliAddChainBench*(T: typedesc, iters: int) =
|
proc sqrtTonelliAddChainBench*(T: typedesc, iters: int) =
|
||||||
|
var r: T
|
||||||
let x = rng.random_unsafe(T)
|
let x = rng.random_unsafe(T)
|
||||||
bench("SquareRoot + isSquare (constant-time Tonelli-Shanks addchain)", T, iters):
|
bench("SquareRoot (constant-time Tonelli-Shanks addchain)", T, iters):
|
||||||
var r = x
|
r.invsqrt_tonelli_shanks(x, useAddChain = true)
|
||||||
discard r.sqrt_if_square_tonelli_shanks(useAddChain = true)
|
r *= x
|
||||||
|
|
||||||
proc powBench*(T: typedesc, iters: int) =
|
proc powBench*(T: typedesc, iters: int) =
|
||||||
let x = rng.random_unsafe(T)
|
let x = rng.random_unsafe(T)
|
||||||
|
|||||||
@ -263,26 +263,26 @@ proc runTests(requireGMP: bool, dumpCmdFile = false, test32bit = false, testASM
|
|||||||
flags &= sanitizers
|
flags &= sanitizers
|
||||||
test flags, td.path, dumpCmdFile
|
test flags, td.path, dumpCmdFile
|
||||||
|
|
||||||
proc buildAllBenches() =
|
proc buildAllBenches(useAsm = true) =
|
||||||
echo "\n\n------------------------------------------------------\n"
|
echo "\n\n------------------------------------------------------\n"
|
||||||
echo "Building benchmarks to ensure they stay relevant ..."
|
echo "Building benchmarks to ensure they stay relevant ..."
|
||||||
buildBench("bench_fp")
|
buildBench("bench_fp", useAsm = useAsm)
|
||||||
buildBench("bench_fp_double_precision")
|
buildBench("bench_fp_double_precision", useAsm = useAsm)
|
||||||
buildBench("bench_fp2")
|
buildBench("bench_fp2", useAsm = useAsm)
|
||||||
buildBench("bench_fp6")
|
buildBench("bench_fp6", useAsm = useAsm)
|
||||||
buildBench("bench_fp12")
|
buildBench("bench_fp12", useAsm = useAsm)
|
||||||
buildBench("bench_ec_g1")
|
buildBench("bench_ec_g1", useAsm = useAsm)
|
||||||
buildBench("bench_ec_g2")
|
buildBench("bench_ec_g2", useAsm = useAsm)
|
||||||
buildBench("bench_pairing_bls12_377")
|
buildBench("bench_pairing_bls12_377", useAsm = useAsm)
|
||||||
buildBench("bench_pairing_bls12_381")
|
buildBench("bench_pairing_bls12_381", useAsm = useAsm)
|
||||||
buildBench("bench_pairing_bn254_nogami")
|
buildBench("bench_pairing_bn254_nogami", useAsm = useAsm)
|
||||||
buildBench("bench_pairing_bn254_snarks")
|
buildBench("bench_pairing_bn254_snarks", useAsm = useAsm)
|
||||||
buildBench("bench_summary_bls12_377")
|
buildBench("bench_summary_bls12_377", useAsm = useAsm)
|
||||||
buildBench("bench_summary_bls12_381")
|
buildBench("bench_summary_bls12_381", useAsm = useAsm)
|
||||||
buildBench("bench_summary_bn254_nogami")
|
buildBench("bench_summary_bn254_nogami", useAsm = useAsm)
|
||||||
buildBench("bench_summary_bn254_snarks")
|
buildBench("bench_summary_bn254_snarks", useAsm = useAsm)
|
||||||
buildBench("bench_sha256")
|
buildBench("bench_sha256", useAsm = useAsm)
|
||||||
buildBench("bench_hash_to_curve")
|
buildBench("bench_hash_to_curve", useAsm = useAsm)
|
||||||
echo "All benchmarks compile successfully."
|
echo "All benchmarks compile successfully."
|
||||||
|
|
||||||
# Tasks
|
# Tasks
|
||||||
@ -308,7 +308,7 @@ task test_no_assembler, "Run all tests":
|
|||||||
|
|
||||||
# Ensure benchmarks stay relevant. Ignore Windows 32-bit at 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"):
|
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":
|
task test_no_gmp, "Run tests that don't require GMP":
|
||||||
# -d:testingCurves is configured in a *.nim.cfg for convenience
|
# -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
|
# ignore Windows 32-bit for the moment
|
||||||
# Ensure benchmarks stay relevant. Ignore Windows 32-bit at 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"):
|
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)":
|
task test_parallel_no_gmp, "Run all tests in parallel (via GNU parallel)":
|
||||||
# -d:testingCurves is configured in a *.nim.cfg for convenience
|
# -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
|
# ignore Windows 32-bit for the moment
|
||||||
# Ensure benchmarks stay relevant. Ignore Windows 32-bit at 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"):
|
if not defined(windows) or not (existsEnv"UCPU" or getEnv"UCPU" == "i686"):
|
||||||
buildAllBenches()
|
buildAllBenches(useASM = false)
|
||||||
|
|
||||||
# Finite field 𝔽p
|
# Finite field 𝔽p
|
||||||
# ------------------------------------------
|
# ------------------------------------------
|
||||||
|
|||||||
@ -54,8 +54,8 @@ func hasP3mod4_primeModulus(C: static Curve): static bool =
|
|||||||
## Returns true iff p ≡ 3 (mod 4)
|
## Returns true iff p ≡ 3 (mod 4)
|
||||||
(BaseType(C.Mod.limbs[0]) and 3) == 3
|
(BaseType(C.Mod.limbs[0]) and 3) == 3
|
||||||
|
|
||||||
func sqrt_p3mod4(a: var Fp) =
|
func invsqrt_p3mod4*(r: var Fp, a: Fp) =
|
||||||
## Compute the square root of ``a``
|
## Compute the inverse square root of ``a``
|
||||||
##
|
##
|
||||||
## This requires ``a`` to be a square
|
## This requires ``a`` to be a square
|
||||||
## and the prime field modulus ``p``: p ≡ 3 (mod 4)
|
## 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,
|
## The square root, if it exist is multivalued,
|
||||||
## i.e. both x² == (-x)²
|
## i.e. both x² == (-x)²
|
||||||
## This procedure returns a deterministic result
|
## 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
|
# TODO: deterministic sign
|
||||||
#
|
#
|
||||||
# Algorithm
|
# 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)/2)) ≡ 1/a (mod p)
|
||||||
# a^((p-3)/4)) ≡ 1/√a (mod p) # Requires p ≡ 3 (mod 4)
|
# a^((p-3)/4)) ≡ 1/√a (mod p) # Requires p ≡ 3 (mod 4)
|
||||||
static: doAssert BaseType(Fp.C.Mod.limbs[0]) mod 4 == 3
|
static: doAssert BaseType(Fp.C.Mod.limbs[0]) mod 4 == 3
|
||||||
|
r = a
|
||||||
invsqrt = a
|
r.powUnsafeExponent(Fp.getPrimeMinus3div4_BE())
|
||||||
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)
|
|
||||||
|
|
||||||
# Specialized routines for addchain-based square roots
|
# 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
|
{.pop.} # inline
|
||||||
|
|
||||||
# Tonelli Shanks for any prime
|
# Tonelli Shanks for any prime
|
||||||
@ -174,12 +96,15 @@ func precompute_tonelli_shanks(
|
|||||||
a_pre_exp.powUnsafeExponent(Fp.C.tonelliShanks(exponent))
|
a_pre_exp.powUnsafeExponent(Fp.C.tonelliShanks(exponent))
|
||||||
|
|
||||||
func isSquare_tonelli_shanks(
|
func isSquare_tonelli_shanks(
|
||||||
a, a_pre_exp: Fp): SecretBool =
|
a, a_pre_exp: Fp): SecretBool {.used.} =
|
||||||
## Returns if `a` is a quadratic residue
|
## Returns if `a` is a quadratic residue
|
||||||
## This uses common precomputation for
|
## This uses common precomputation for
|
||||||
## Tonelli-Shanks based square root and inverse square root
|
## Tonelli-Shanks based square root and inverse square root
|
||||||
##
|
##
|
||||||
## a^((p-1-2^e)/(2*2^e))
|
## 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)
|
const e = Fp.C.tonelliShanks(twoAdicity)
|
||||||
var r {.noInit.}: Fp
|
var r {.noInit.}: Fp
|
||||||
r.square(a_pre_exp) # a^(2(q-1-2^e)/(2*2^e)) = a^((q-1)/2^e - 1)
|
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()
|
r.isMinusOne()
|
||||||
)
|
)
|
||||||
|
|
||||||
func sqrt_invsqrt_tonelli_shanks_pre(
|
func invsqrt_tonelli_shanks_pre(
|
||||||
sqrt, invsqrt: var Fp,
|
invsqrt: var Fp,
|
||||||
a, a_pre_exp: 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
|
## of `a` via constant-time Tonelli-Shanks
|
||||||
##
|
##
|
||||||
## a_pre_exp is a precomputation a^((p-1-2^e)/(2*2^e))
|
## 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)
|
t.ccopy(buf, bNotOne)
|
||||||
b = t
|
b = t
|
||||||
|
|
||||||
sqrt.prod(invsqrt, a)
|
func invsqrt_tonelli_shanks*(r: var Fp, a: Fp, useAddChain: static bool) =
|
||||||
|
## Compute the inverse square root of ``a``
|
||||||
# ----------------------------------------------
|
|
||||||
|
|
||||||
func sqrt_tonelli_shanks(a: var Fp, useAddChain: static bool) =
|
|
||||||
## Compute the square root of ``a``
|
|
||||||
##
|
##
|
||||||
## This requires ``a`` to be a square
|
## 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)²
|
## i.e. both x² == (-x)²
|
||||||
## This procedure returns a deterministic result
|
## This procedure returns a deterministic result
|
||||||
## This procedure is constant-time
|
## 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
|
var a_pre_exp{.noInit.}: Fp
|
||||||
a_pre_exp.precompute_tonelli_shanks(a, useAddChain)
|
a_pre_exp.precompute_tonelli_shanks(a, useAddChain)
|
||||||
sqrt_invsqrt_tonelli_shanks_pre(sqrt, invsqrt, a, a_pre_exp)
|
invsqrt_tonelli_shanks_pre(r, 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
|
|
||||||
|
|
||||||
# Public routines
|
# Public routines
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
@ -301,6 +177,24 @@ func sqrt_if_square_tonelli_shanks*(a: var Fp, useAddChain: static bool): Secret
|
|||||||
|
|
||||||
{.push inline.}
|
{.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]) =
|
func sqrt*[C](a: var Fp[C]) =
|
||||||
## Compute the square root of ``a``
|
## Compute the square root of ``a``
|
||||||
##
|
##
|
||||||
@ -312,12 +206,9 @@ func sqrt*[C](a: var Fp[C]) =
|
|||||||
## i.e. both x² == (-x)²
|
## i.e. both x² == (-x)²
|
||||||
## This procedure returns a deterministic result
|
## This procedure returns a deterministic result
|
||||||
## This procedure is constant-time
|
## This procedure is constant-time
|
||||||
when C.hasSqrtAddchain():
|
var t {.noInit.}: Fp[C]
|
||||||
sqrt_addchain(a)
|
t.invsqrt(a)
|
||||||
elif C.hasP3mod4_primeModulus():
|
a *= t
|
||||||
sqrt_p3mod4(a)
|
|
||||||
else:
|
|
||||||
sqrt_tonelli_shanks(a, useAddChain = C.hasTonelliShanksAddchain())
|
|
||||||
|
|
||||||
func sqrt_invsqrt*[C](sqrt, invsqrt: var Fp[C], a: Fp[C]) =
|
func sqrt_invsqrt*[C](sqrt, invsqrt: var Fp[C], a: Fp[C]) =
|
||||||
## Compute the square root and inverse square root of ``a``
|
## 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,
|
## The square root, if it exist is multivalued,
|
||||||
## i.e. both x² == (-x)²
|
## i.e. both x² == (-x)²
|
||||||
## This procedure returns a deterministic result
|
## This procedure returns a deterministic result
|
||||||
when C.hasSqrtAddchain():
|
invsqrt.invsqrt(a)
|
||||||
sqrt_invsqrt_addchain(sqrt, invsqrt, a)
|
sqrt.prod(invsqrt, a)
|
||||||
elif C.hasP3mod4_primeModulus():
|
|
||||||
sqrt_invsqrt_p3mod4(sqrt, invsqrt, a)
|
|
||||||
else:
|
|
||||||
sqrt_invsqrt_tonelli_shanks(sqrt, invsqrt, a, useAddChain = C.hasTonelliShanksAddchain())
|
|
||||||
|
|
||||||
func sqrt_invsqrt_if_square*[C](sqrt, invsqrt: var Fp[C], a: Fp[C]): SecretBool =
|
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``
|
## 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,
|
## The square root, if it exist is multivalued,
|
||||||
## i.e. both x² == (-x)²
|
## i.e. both x² == (-x)²
|
||||||
## This procedure returns a deterministic result
|
## This procedure returns a deterministic result
|
||||||
when C.hasSqrtAddchain():
|
sqrt_invsqrt(sqrt, invsqrt, a)
|
||||||
result = sqrt_invsqrt_if_square_addchain(sqrt, invsqrt, a)
|
var test {.noInit.}: Fp[C]
|
||||||
elif C.hasP3mod4_primeModulus():
|
test.square(sqrt)
|
||||||
result = sqrt_invsqrt_if_square_p3mod4(sqrt, invsqrt, a)
|
result = test == a
|
||||||
else:
|
|
||||||
result = sqrt_invsqrt_if_square_tonelli_shanks(sqrt, invsqrt, a, useAddChain = C.hasTonelliShanksAddchain())
|
|
||||||
|
|
||||||
func sqrt_if_square*[C](a: var Fp[C]): SecretBool =
|
func sqrt_if_square*[C](a: var Fp[C]): SecretBool =
|
||||||
## If ``a`` is a square, compute the square root of ``a``
|
## 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,
|
## The square root, if it exist is multivalued,
|
||||||
## i.e. both x² == (-x)²
|
## i.e. both x² == (-x)²
|
||||||
## This procedure returns a deterministic result
|
## This procedure returns a deterministic result
|
||||||
## This procedure is constant-time
|
## This procedure is constant-time
|
||||||
when C.hasSqrtAddchain():
|
var sqrt{.noInit.}, invsqrt{.noInit.}: Fp[C]
|
||||||
result = sqrt_if_square_addchain(a)
|
result = sqrt_invsqrt_if_square(sqrt, invsqrt, a)
|
||||||
elif C.hasP3mod4_primeModulus():
|
a = sqrt
|
||||||
result = sqrt_if_square_p3mod4(a)
|
|
||||||
else:
|
func invsqrt_if_square*[C](r: var Fp[C], a: Fp[C]): SecretBool =
|
||||||
result = sqrt_if_square_tonelli_shanks(a, useAddChain = C.hasTonelliShanksAddchain())
|
## 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.} # inline
|
||||||
{.pop.} # raises no exceptions
|
{.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)
|
montSquare_CIOS_asm_adx_bmi2(r, a, M, m0ninv, spareBits >= 1)
|
||||||
else:
|
else:
|
||||||
montSquare_CIOS_asm(r, a, M, m0ninv, spareBits >= 1)
|
montSquare_CIOS_asm(r, a, M, m0ninv, spareBits >= 1)
|
||||||
else:
|
elif UseASM_X86_64:
|
||||||
var r2x {.noInit.}: Limbs[2*N]
|
var r2x {.noInit.}: Limbs[2*N]
|
||||||
r2x.square(a)
|
r2x.square(a)
|
||||||
r.montyRedc2x(r2x, M, m0ninv, spareBits)
|
r.montyRedc2x(r2x, M, m0ninv, spareBits)
|
||||||
|
else:
|
||||||
|
montyMul(r, a, a, M, m0ninv, spareBits)
|
||||||
|
|
||||||
func redc*(r: var Limbs, a, one, M: Limbs,
|
func redc*(r: var Limbs, a, one, M: Limbs,
|
||||||
m0ninv: static BaseType, spareBits: static int) =
|
m0ninv: static BaseType, spareBits: static int) =
|
||||||
|
|||||||
@ -31,7 +31,7 @@ const BLS12_381_h2c_G2_Z* = Fp2[BLS12_381].fromHex( # -(2 + 𝑖)
|
|||||||
"0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaa9",
|
"0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaa9",
|
||||||
"0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa"
|
"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",
|
"0x0",
|
||||||
"0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa9bb"
|
"0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa9bb"
|
||||||
)
|
)
|
||||||
|
|||||||
31
constantine/ec_shortweierstrass.nim
Normal file
31
constantine/ec_shortweierstrass.nim
Normal file
@ -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
|
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 =
|
func isInf*(P: ECP_ShortW_Aff): SecretBool =
|
||||||
## Returns true if P is an infinity point
|
## Returns true if P is an infinity point
|
||||||
## and false otherwise
|
## and false otherwise
|
||||||
|
|||||||
@ -134,11 +134,13 @@ func cneg*(P: var ECP_ShortW_Jac, ctl: CTBool) {.inline.} =
|
|||||||
## Negate if ``ctl`` is true
|
## Negate if ``ctl`` is true
|
||||||
P.y.cneg(ctl)
|
P.y.cneg(ctl)
|
||||||
|
|
||||||
func sum*[F; Tw: static Twisted](
|
template sumImpl[F; Tw: static Twisted](
|
||||||
r: var ECP_ShortW_Jac[F, Tw],
|
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
|
## 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
|
## R = P + Q
|
||||||
##
|
##
|
||||||
@ -148,6 +150,8 @@ func sum*[F; Tw: static Twisted](
|
|||||||
## y² = x³ + a x + b
|
## y² = x³ + a x + b
|
||||||
##
|
##
|
||||||
## ``r`` is initialized/overwritten with the sum
|
## ``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
|
## Implementation is constant-time, in particular it will not expose
|
||||||
## that P == Q or P == -Q or P or Q are the infinity points
|
## 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)
|
V_or_S *= HH_or_YY # V = U₁*HH (add) or S = X₁*YY (dbl)
|
||||||
|
|
||||||
block: # Compute M for doubling
|
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 a = H
|
||||||
var b = HH_or_YY
|
var b = HH_or_YY
|
||||||
a.ccopy(P.x, isDbl) # H or X₁
|
a.ccopy(P.x, isDbl) # H or X₁
|
||||||
@ -230,7 +243,7 @@ func sum*[F; Tw: static Twisted](
|
|||||||
M += HHH_or_Mpre # 3X₁²/2
|
M += HHH_or_Mpre # 3X₁²/2
|
||||||
R_or_M.ccopy(M, isDbl)
|
R_or_M.ccopy(M, isDbl)
|
||||||
|
|
||||||
elif F.C.getCoefA() == -3:
|
elif CoefA_eq_minus3:
|
||||||
var a{.noInit.}, b{.noInit.}: F
|
var a{.noInit.}, b{.noInit.}: F
|
||||||
a.sum(P.x, Z1Z1)
|
a.sum(P.x, Z1Z1)
|
||||||
b.diff(P.z, Z1Z1)
|
b.diff(P.z, Z1Z1)
|
||||||
@ -247,7 +260,7 @@ func sum*[F; Tw: static Twisted](
|
|||||||
# TODO: Costly `a` coefficients can be computed
|
# TODO: Costly `a` coefficients can be computed
|
||||||
# by merging their computation with Z₃ = Z₁*Z₂*H (add) or Z₃ = Y₁*Z₁ (dbl)
|
# by merging their computation with Z₃ = Z₁*Z₂*H (add) or Z₃ = Y₁*Z₁ (dbl)
|
||||||
var a = H
|
var a = H
|
||||||
var b = HH
|
var b = HH_or_YY
|
||||||
a.ccopy(P.x, isDbl)
|
a.ccopy(P.x, isDbl)
|
||||||
b.ccopy(P.x, isDbl)
|
b.ccopy(P.x, isDbl)
|
||||||
HHH_or_Mpre.prod(a, b) # HHH or X₁²
|
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.square(HHH_or_Mpre)
|
||||||
a *= HHH_or_Mpre # a = 3X₁²
|
a *= HHH_or_Mpre # a = 3X₁²
|
||||||
b.square(Z1Z1)
|
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 += b
|
||||||
a.div2()
|
a.div2()
|
||||||
@ -292,6 +306,52 @@ func sum*[F; Tw: static Twisted](
|
|||||||
r.ccopy(Q, P.isInf())
|
r.ccopy(Q, P.isInf())
|
||||||
r.ccopy(P, Q.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](
|
func madd*[F; Tw: static Twisted](
|
||||||
r: var ECP_ShortW_Jac[F, Tw],
|
r: var ECP_ShortW_Jac[F, Tw],
|
||||||
P: ECP_ShortW_Jac[F, Tw],
|
P: ECP_ShortW_Jac[F, Tw],
|
||||||
|
|||||||
@ -14,7 +14,8 @@ import
|
|||||||
../towers,
|
../towers,
|
||||||
../config/curves,
|
../config/curves,
|
||||||
../io/io_bigints,
|
../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
|
## Clear the cofactor of BW6_761 G2
|
||||||
# Endomorphism acceleration cannot be used if cofactor is not cleared
|
# Endomorphism acceleration cannot be used if cofactor is not cleared
|
||||||
P.scalarMulGeneric(Cofactor_Eff_BW6_761_G2)
|
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)
|
## ceil(a / b)
|
||||||
(a + b - 1) div b
|
(a + b - 1) div b
|
||||||
|
|
||||||
proc copyFrom[N](output: var openarray[byte], bi: array[N, byte], cur: var uint) =
|
proc copyFrom[M, N: static int](output: var array[M, byte], bi: array[N, byte], cur: var uint) =
|
||||||
var b_index = 0'u
|
static: doAssert M mod N == 0
|
||||||
while b_index < bi.len.uint and cur < output.len.uint:
|
for i in 0'u ..< N:
|
||||||
output[cur] = bi[b_index]
|
output[cur+i] = bi[i]
|
||||||
inc cur
|
cur += N.uint
|
||||||
inc b_index
|
|
||||||
|
|
||||||
template strxor(b_i: var array, b0: array): untyped =
|
template strxor(b_i: var array, b0: array): untyped =
|
||||||
for i in 0 ..< b_i.len:
|
for i in 0 ..< b_i.len:
|
||||||
@ -57,9 +56,9 @@ func shortDomainSepTag[DigestSize: static int, B: byte|char](
|
|||||||
ctx.update oversizedDST
|
ctx.update oversizedDST
|
||||||
ctx.finish(output)
|
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,
|
H: type CryptoHash,
|
||||||
output: var openarray[byte],
|
output: var array[len_in_bytes, byte],
|
||||||
augmentation: openarray[B1],
|
augmentation: openarray[B1],
|
||||||
message: openarray[B2],
|
message: openarray[B2],
|
||||||
domainSepTag: openarray[B3]
|
domainSepTag: openarray[B3]
|
||||||
@ -110,23 +109,23 @@ func expandMessageXMD*[B1, B2, B3: byte|char](
|
|||||||
const DigestSize = Hash.digestSize()
|
const DigestSize = Hash.digestSize()
|
||||||
const BlockSize = Hash.internalBlockSize()
|
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)
|
let ell = ceilDiv(output.len.uint, DigestSize.uint)
|
||||||
const zPad = default(array[BlockSize, byte])
|
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]
|
var b0 {.noinit, align: DigestSize.}: array[DigestSize, byte]
|
||||||
func ctZpad(): Hash =
|
var ctx {.noInit.}: Hash
|
||||||
# Compile-time precompute
|
ctx.initZeroPadded()
|
||||||
# TODO upstream: `toOpenArray` throws "cannot generate code for: mSlice"
|
|
||||||
result.init()
|
|
||||||
result.update zPad
|
|
||||||
var ctx = ctZpad() # static(ctZpad())
|
|
||||||
ctx.update augmentation
|
ctx.update augmentation
|
||||||
ctx.update message
|
ctx.update message
|
||||||
ctx.update l_i_b_str
|
ctx.update l_i_b_str0
|
||||||
ctx.update [byte 0]
|
# ctx.update [byte 0] # already appended to l_i_b_str
|
||||||
ctx.update domainSepTag
|
ctx.update domainSepTag
|
||||||
ctx.update [byte domainSepTag.len] # DST_prime
|
ctx.update [byte domainSepTag.len] # DST_prime
|
||||||
ctx.finish(b0)
|
ctx.finish(b0)
|
||||||
|
|||||||
@ -109,8 +109,7 @@ func invsqrt_if_square[C: static Curve](
|
|||||||
t2 *= NonResidue
|
t2 *= NonResidue
|
||||||
t1 -= t2
|
t1 -= t2
|
||||||
|
|
||||||
# TODO: implement invsqrt alone
|
result = t3.invsqrt_if_square(t1) # 1/sqrt(a0² - β a1²)
|
||||||
result = sqrt_invsqrt_if_square(sqrt = r.c1, invsqrt = t3, t1) # 1/sqrt(a0² - β a1²)
|
|
||||||
|
|
||||||
# If input is not a square in Fp2, multiply by 1/Z³
|
# If input is not a square in Fp2, multiply by 1/Z³
|
||||||
inp.prod(a, h2cConst(C, G2, inv_Z3)) # inp = a / 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.ccopy(t2, t1.isZero())
|
||||||
t1.div2() # (a0 ± sqrt(a0² - βa1²))/2
|
t1.div2() # (a0 ± sqrt(a0² - βa1²))/2
|
||||||
|
|
||||||
# TODO: implement invsqrt alone
|
r.c0.invsqrt(t1)
|
||||||
sqrt_invsqrt(sqrt = r.c1, invsqrt = r.c0, t1)
|
|
||||||
|
|
||||||
r.c1 = inp.c1
|
r.c1 = inp.c1
|
||||||
r.c1.div2()
|
r.c1.div2()
|
||||||
@ -152,19 +150,22 @@ func invsqrt_if_square[C: static Curve](
|
|||||||
|
|
||||||
func mapToIsoCurve_sswuG2_opt9mod16*[C: static Curve](
|
func mapToIsoCurve_sswuG2_opt9mod16*[C: static Curve](
|
||||||
xn, xd, yn: var Fp2[C],
|
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,
|
## 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
|
## this function maps any field element of Fp2 to E'2
|
||||||
## a curve isogenous to E2 using the Simplified Shallue-van de Woestijne method.
|
## a curve isogenous to E2 using the Simplified Shallue-van de Woestijne method.
|
||||||
##
|
##
|
||||||
## This requires p² ≡ 9 (mod 16).
|
## This requires p² ≡ 9 (mod 16).
|
||||||
#
|
##
|
||||||
# Input:
|
## Input:
|
||||||
# - u, an Fp2 element
|
## - u, an Fp2 element
|
||||||
# Output:
|
## Output:
|
||||||
# - (xn, xd, yn, yd) such that (x', y') = (xn/xd, yn/yd)
|
## - (xn, xd, yn, yd) such that (x', y') = (xn/xd, yn/yd)
|
||||||
# is a point of E'2
|
## is a point of E'2
|
||||||
# - yd is implied to be 1
|
## - 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
|
# 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
|
# 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
|
var
|
||||||
uu {.noInit.}, tv2 {.noInit.}: Fp2[C]
|
uu {.noInit.}, tv2 {.noInit.}: Fp2[C]
|
||||||
tv4 {.noInit.}, x2n {.noInit.}, gx1 {.noInit.}: Fp2[C]
|
tv4 {.noInit.}, x2n {.noInit.}, gx1 {.noInit.}: Fp2[C]
|
||||||
gxd {.noInit.}: Fp2[C]
|
|
||||||
y2 {.noInit.}: Fp2[C]
|
y2 {.noInit.}: Fp2[C]
|
||||||
e1, e2: SecretBool
|
e1, e2: SecretBool
|
||||||
|
|
||||||
@ -184,6 +184,7 @@ func mapToIsoCurve_sswuG2_opt9mod16*[C: static Curve](
|
|||||||
template x1n: untyped = xn
|
template x1n: untyped = xn
|
||||||
template y1: untyped = yn
|
template y1: untyped = yn
|
||||||
template Zuu: untyped = x2n
|
template Zuu: untyped = x2n
|
||||||
|
template gxd: untyped = xd3
|
||||||
|
|
||||||
# x numerators
|
# x numerators
|
||||||
uu.square(u) # uu = u²
|
uu.square(u) # uu = u²
|
||||||
@ -203,7 +204,7 @@ func mapToIsoCurve_sswuG2_opt9mod16*[C: static Curve](
|
|||||||
# y numerators
|
# y numerators
|
||||||
tv2.square(xd)
|
tv2.square(xd)
|
||||||
gxd.prod(xd, tv2) # gxd = xd³
|
gxd.prod(xd, tv2) # gxd = xd³
|
||||||
tv2 *= h2CConst(C, G2, Aprime_E2)
|
tv2.mulCheckSparse(h2CConst(C, G2, Aprime_E2))
|
||||||
gx1.square(x1n)
|
gx1.square(x1n)
|
||||||
gx1 += tv2 # x1n² + A * xd²
|
gx1 += tv2 # x1n² + A * xd²
|
||||||
gx1 *= x1n # x1n³ + A * x1n * xd²
|
gx1 *= x1n # x1n³ + A * x1n * xd²
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import
|
|||||||
../config/[common, curves],
|
../config/[common, curves],
|
||||||
../primitives, ../arithmetic, ../towers,
|
../primitives, ../arithmetic, ../towers,
|
||||||
../curves/zoo_hash_to_curve,
|
../curves/zoo_hash_to_curve,
|
||||||
../elliptic/ec_shortweierstrass_projective,
|
../ec_shortweierstrass,
|
||||||
./h2c_hash_to_field,
|
./h2c_hash_to_field,
|
||||||
./h2c_map_to_isocurve_swu,
|
./h2c_map_to_isocurve_swu,
|
||||||
./cofactors,
|
./cofactors,
|
||||||
@ -34,8 +34,28 @@ import
|
|||||||
# Map to curve
|
# 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](
|
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
|
## Map an element of the
|
||||||
## finite or extension field F
|
## finite or extension field F
|
||||||
## to an elliptic curve E
|
## to an elliptic curve E
|
||||||
@ -48,11 +68,12 @@ func mapToCurve[F; Tw: static Twisted](
|
|||||||
var
|
var
|
||||||
xn{.noInit.}, xd{.noInit.}: F
|
xn{.noInit.}, xd{.noInit.}: F
|
||||||
yn{.noInit.}: F
|
yn{.noInit.}: F
|
||||||
|
xd3{.noInit.}: F
|
||||||
|
|
||||||
mapToIsoCurve_sswuG2_opt9mod16(
|
mapToIsoCurve_sswuG2_opt9mod16(
|
||||||
xn, xd,
|
xn, xd,
|
||||||
yn,
|
yn,
|
||||||
u
|
u, xd3
|
||||||
)
|
)
|
||||||
|
|
||||||
# 2. Map from E'2 to E2
|
# 2. Map from E'2 to E2
|
||||||
@ -64,6 +85,39 @@ func mapToCurve[F; Tw: static Twisted](
|
|||||||
else:
|
else:
|
||||||
{.error: "Not implemented".}
|
{.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
|
# Hash to curve
|
||||||
# ----------------------------------------------------------------
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
@ -102,9 +156,14 @@ func hashToCurve*[
|
|||||||
var u{.noInit.}: array[2, F]
|
var u{.noInit.}: array[2, F]
|
||||||
H.hashToField(k, u, augmentation, message, domainSepTag)
|
H.hashToField(k, u, augmentation, message, domainSepTag)
|
||||||
|
|
||||||
var P{.noInit.}: array[2, ECP_ShortW_Prj[F, Tw]]
|
when false:
|
||||||
P[0].mapToCurve(u[0])
|
var P{.noInit.}: array[2, ECP_ShortW_Prj[F, Tw]]
|
||||||
P[1].mapToCurve(u[1])
|
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.clearCofactorFast()
|
||||||
output.clearCofactorReference() # TODO - fast cofactor clear
|
|
||||||
|
|||||||
@ -315,14 +315,37 @@ func init*(ctx: var Sha256Context) =
|
|||||||
ctx.buf.setZero()
|
ctx.buf.setZero()
|
||||||
ctx.bufIdx = 0
|
ctx.bufIdx = 0
|
||||||
|
|
||||||
ctx.H[0] = 0x6a09e667'u32;
|
ctx.H[0] = 0x6a09e667'u32
|
||||||
ctx.H[1] = 0xbb67ae85'u32;
|
ctx.H[1] = 0xbb67ae85'u32
|
||||||
ctx.H[2] = 0x3c6ef372'u32;
|
ctx.H[2] = 0x3c6ef372'u32
|
||||||
ctx.H[3] = 0xa54ff53a'u32;
|
ctx.H[3] = 0xa54ff53a'u32
|
||||||
ctx.H[4] = 0x510e527f'u32;
|
ctx.H[4] = 0x510e527f'u32
|
||||||
ctx.H[5] = 0x9b05688c'u32;
|
ctx.H[5] = 0x9b05688c'u32
|
||||||
ctx.H[6] = 0x1f83d9ab'u32;
|
ctx.H[6] = 0x1f83d9ab'u32
|
||||||
ctx.H[7] = 0x5be0cd19'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]) =
|
func update*[T: char|byte](ctx: var Sha256Context, message: openarray[T]) =
|
||||||
## Append a message to a SHA256 context
|
## Append a message to a SHA256 context
|
||||||
|
|||||||
@ -77,8 +77,13 @@ func dumpRawInt*[T: byte|char](
|
|||||||
for i in 0'u ..< L:
|
for i in 0'u ..< L:
|
||||||
dst[cursor+i] = toByte(src shr ((L-i-1) * 8))
|
dst[cursor+i] = toByte(src shr ((L-i-1) * 8))
|
||||||
|
|
||||||
func toBytesBE*(num: SomeUnsignedInt): array[sizeof(num), byte] {.inline.}=
|
func asBytesBE*[N: static int](
|
||||||
## Convert an integer to an array of bytes
|
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)
|
const L = sizeof(num)
|
||||||
|
static: doAssert N - (pos + L) >= 0
|
||||||
for i in 0 ..< L:
|
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
|
# Internals
|
||||||
../primitives, ../arithmetic, ../towers,
|
../primitives, ../arithmetic, ../towers,
|
||||||
../curves/zoo_hash_to_curve,
|
../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
|
# Missing scaling factor
|
||||||
r *= xd_pow[isodegree - poly_degree - 1]
|
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](
|
func h2c_isogeny_map*[F; Tw: static Twisted](
|
||||||
r: var ECP_ShortW_Prj[F, Tw],
|
r: var ECP_ShortW_Prj[F, Tw],
|
||||||
xn, xd, yn: F, isodegree: static int) =
|
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://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
|
## - https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve
|
||||||
|
|
||||||
# xd^e with e in [1, N], for example [xd, xd², xd³]
|
var t{.noInit.}: F
|
||||||
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)
|
|
||||||
|
|
||||||
r.x.poly_eval_horner_scaled(
|
h2c_isogeny_map(
|
||||||
xn, xd_pow,
|
rxn = r.x,
|
||||||
h2cIsomapPoly(F.C, G2, isodegree, xnum)
|
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
|
# Now convert to projective coordinates
|
||||||
# (x, y) => (xnum/xden, ynum/yden)
|
# (x, y) => (xnum/xden, ynum/yden)
|
||||||
@ -129,3 +159,103 @@ func h2c_isogeny_map*[F; Tw: static Twisted](
|
|||||||
r.y *= r.z
|
r.y *= r.z
|
||||||
r.x *= t
|
r.x *= t
|
||||||
r.z *= 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.ccopy(t2, t1.isZero())
|
||||||
t1.div2() # (a0 ± sqrt(a0² - β a1²))/2
|
t1.div2() # (a0 ± sqrt(a0² - β a1²))/2
|
||||||
|
|
||||||
# TODO: implement invsqrt alone
|
|
||||||
# t1 being an actual sqrt will be tested in sqrt_rotate_extension
|
# t1 being an actual sqrt will be tested in sqrt_rotate_extension
|
||||||
# 1/sqrt((a0 ± sqrt(a0² - β b²))/2)
|
cand.c0.invsqrt(t1) # 1/sqrt((a0 ± sqrt(a0² - β b²))/2)
|
||||||
sqrt_invsqrt(sqrt = cand.c1, invsqrt = cand.c0, t1) # we only want invsqrt = cand.c0
|
|
||||||
|
|
||||||
cand.c1 = a.c1
|
cand.c1 = a.c1
|
||||||
cand.c1.div2()
|
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 += field_to_nim(Z, 'Fp2', curve_name, comment_right = "-(2 + 𝑖)")
|
||||||
buf += '\n'
|
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 += field_to_nim(minus_A, 'Fp2', curve_name, comment_right = "-240𝑖")
|
||||||
buf += '\n'
|
buf += '\n'
|
||||||
|
|
||||||
|
|||||||
@ -80,7 +80,6 @@ proc exhaustiveCheck(C: static Curve, modulus: static int) =
|
|||||||
check:
|
check:
|
||||||
bool not a.isSquare()
|
bool not a.isSquare()
|
||||||
bool not a.sqrt_if_square()
|
bool not a.sqrt_if_square()
|
||||||
bool (a == a2) # a shouldn't be modified
|
|
||||||
|
|
||||||
template testImpl(a: untyped): untyped {.dirty.} =
|
template testImpl(a: untyped): untyped {.dirty.} =
|
||||||
var na{.noInit.}: typeof(a)
|
var na{.noInit.}: typeof(a)
|
||||||
|
|||||||
@ -61,7 +61,7 @@ proc runFrobeniusTowerTests*[N](
|
|||||||
) =
|
) =
|
||||||
# Random seed for reproducibility
|
# Random seed for reproducibility
|
||||||
var rng: RngState
|
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)
|
rng.seed(seed)
|
||||||
echo moduleName, " xoshiro512** seed: ", seed
|
echo moduleName, " xoshiro512** seed: ", seed
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user