From 06fd1cd58fa8eee376cedaabef90c9b5fb632feb Mon Sep 17 00:00:00 2001 From: Mamy Ratsimbazafy Date: Fri, 16 Feb 2024 22:42:05 +0100 Subject: [PATCH 1/2] sqrt-via-dlog: cleanup and 20% accel for Verkle Trees --- .../finite_fields_square_root_precomp.nim | 140 +++--------------- .../math/constants/bandersnatch_sqrt.nim | 8 +- 2 files changed, 27 insertions(+), 121 deletions(-) diff --git a/constantine/math/arithmetic/finite_fields_square_root_precomp.nim b/constantine/math/arithmetic/finite_fields_square_root_precomp.nim index 20da9e74a..d786b4dee 100644 --- a/constantine/math/arithmetic/finite_fields_square_root_precomp.nim +++ b/constantine/math/arithmetic/finite_fields_square_root_precomp.nim @@ -31,115 +31,15 @@ func sqrtAlg_NegDlogInSmallDyadicSubgroup_vartime(x: Fp): int {.tags:[VarTime], let key = cast[int](x.mres.limbs[0] and SecretWord 0xFFFF) return Fp.C.sqrtDlog(dlogLUT).getOrDefault(key, 0) - # sqrtAlg_GetPrecomputedRootOfUnity sets target to g^(multiplier << (order * sqrtParam_BlockSize)), where g is the fixed primitive 2^32th root of unity. # # We assume that order 0 <= order*sqrtParam_BlockSize <= 32 and that multiplier is in [0, 1 < Date: Fri, 16 Feb 2024 23:27:31 +0100 Subject: [PATCH 2/2] sqrt-vartime: Benches + comment improvement --- benchmarks/bench_fields_template.nim | 27 +++++++++ benchmarks/bench_fp.nim | 3 + .../arithmetic/finite_fields_square_root.nim | 56 +++++++++++++++---- 3 files changed, 75 insertions(+), 11 deletions(-) diff --git a/benchmarks/bench_fields_template.nim b/benchmarks/bench_fields_template.nim index 40d4e1e1d..23133a88b 100644 --- a/benchmarks/bench_fields_template.nim +++ b/benchmarks/bench_fields_template.nim @@ -199,6 +199,33 @@ proc sqrtRatioBench*(T: typedesc, iters: int) = bench("Fused SquareRoot+Division+isSquare sqrt(u/v)", T, iters): let isSquare = r.sqrt_ratio_if_square(u, v) +proc sqrtVartimeBench*(T: typedesc, iters: int) = + let x = rng.random_unsafe(T) + + const algoType = block: + when T.C.has_P_3mod4_primeModulus(): + "p ≡ 3 (mod 4)" + elif T.C.has_P_5mod8_primeModulus(): + "p ≡ 5 (mod 8)" + else: + "Tonelli-Shanks" + const addchain = block: + when T.C.hasSqrtAddchain() or T.C.hasTonelliShanksAddchain(): + "with addition chain" + else: + "without addition chain" + const desc = "Square Root (vartime " & algoType & " " & addchain & ")" + bench(desc, T, iters): + var r = x + discard r.sqrt_if_square_vartime() + +proc sqrtRatioVartimeBench*(T: typedesc, iters: int) = + var r: T + let u = rng.random_unsafe(T) + let v = rng.random_unsafe(T) + bench("Fused SquareRoot+Division+isSquare sqrt_vartime(u/v)", T, iters): + let isSquare = r.sqrt_ratio_if_square_vartime(u, v) + proc powBench*(T: typedesc, iters: int) = let x = rng.random_unsafe(T) let exponent = rng.random_unsafe(BigInt[T.C.getCurveOrderBitwidth()]) diff --git a/benchmarks/bench_fp.nim b/benchmarks/bench_fp.nim index 230d61c0e..4289c7590 100644 --- a/benchmarks/bench_fp.nim +++ b/benchmarks/bench_fp.nim @@ -65,6 +65,9 @@ proc main() = isSquareBench(Fp[curve], ExponentIters) sqrtBench(Fp[curve], ExponentIters) sqrtRatioBench(Fp[curve], ExponentIters) + when curve == Bandersnatch: + sqrtVartimeBench(Fp[curve], ExponentIters) + sqrtRatioVartimeBench(Fp[curve], ExponentIters) # Exponentiation by a "secret" of size ~the curve order powBench(Fp[curve], ExponentIters) powUnsafeBench(Fp[curve], ExponentIters) diff --git a/constantine/math/arithmetic/finite_fields_square_root.nim b/constantine/math/arithmetic/finite_fields_square_root.nim index 6826db953..8450e0279 100644 --- a/constantine/math/arithmetic/finite_fields_square_root.nim +++ b/constantine/math/arithmetic/finite_fields_square_root.nim @@ -224,6 +224,7 @@ func invsqrt_vartime*[C](r: 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 + ## ## This procedure is NOT constant-time when C.has_P_3mod4_primeModulus(): r.invsqrt_p3mod4(a) @@ -250,9 +251,13 @@ func sqrt*[C](a: var Fp[C]) = a *= t func sqrt_vartime*[C](a: var Fp[C]) = - ## This is a vartime version of sqrt - ## It is not constant-time - ## This has the precomp optimisation + ## Compute the square root of ``a`` + ## + ## This requires ``a`` to be a square + ## + ## The result is undefined otherwise + ## + ## This is NOT constant-time var t {.noInit.}: Fp[C] t.invsqrt_vartime(a) a *= t @@ -271,8 +276,13 @@ func sqrt_invsqrt*[C](sqrt, invsqrt: var Fp[C], a: Fp[C]) = sqrt.prod(invsqrt, a) func sqrt_invsqrt_vartime*[C](sqrt, invsqrt: var Fp[C], a: Fp[C]) = - ## It is not constant-time - ## This has the precomp optimisation + ## Compute the square root of ``a`` and inverse square root of ``a`` + ## + ## This requires ``a`` to be a square + ## + ## The result is undefined otherwise + ## + ## This is NOT constant-time invsqrt.invsqrt_vartime(a) sqrt.prod(invsqrt, a) @@ -292,8 +302,13 @@ func sqrt_invsqrt_if_square*[C](sqrt, invsqrt: var Fp[C], a: Fp[C]): SecretBool result = test == a func sqrt_invsqrt_if_square_vartime*[C](sqrt, invsqrt: var Fp[C], a: Fp[C]): SecretBool = - ## It is not constant-time - ## This has the precomp optimisation + ## 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 + ## + ## This is NOT constant-time sqrt_invsqrt_vartime(sqrt, invsqrt, a) var test {.noInit.}: Fp[C] test.square(sqrt) @@ -311,6 +326,19 @@ func sqrt_if_square*[C](a: var Fp[C]): SecretBool = result = sqrt_invsqrt_if_square(sqrt, invsqrt, a) a = sqrt +func sqrt_if_square_vartime*[C](a: var Fp[C]): SecretBool = + ## If ``a`` is a square, compute the 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 is NOT constant-time + var sqrt{.noInit.}, invsqrt{.noInit.}: Fp[C] + result = sqrt_invsqrt_if_square_vartime(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. @@ -323,8 +351,10 @@ func invsqrt_if_square*[C](r: var Fp[C], a: Fp[C]): SecretBool = result = sqrt_invsqrt_if_square(sqrt, r, a) func invsqrt_if_square_vartime*[C](r: var Fp[C], a: Fp[C]): SecretBool = - ## It is not constant-time - ## This has the precomp optimisation + ## If ``a`` is a square, compute the inverse square root of ``a`` + ## if not, ``a`` is undefined. + ## + ## This procedure is NOT constant-time var sqrt{.noInit.}: Fp[C] result = sqrt_invsqrt_if_square_vartime(sqrt, r, a) @@ -365,8 +395,12 @@ func sqrt_ratio_if_square*(r: var Fp, u, v: Fp): SecretBool {.inline.} = r *= u # √u/√v func sqrt_ratio_if_square_vartime*(r: var Fp, u, v: Fp): SecretBool {.inline.} = - ## It is not constant-time - ## This has the precomp optimisation + ## If u/v is a square, compute √(u/v) + ## if not, the result is undefined + ## + ## r must not alias u or v + ## + ## This is NOT constant-time var uv{.noInit.}: Fp uv.prod(u, v) # uv result = r.invsqrt_if_square_vartime(uv) # 1/√uv