From b5e5ad641447ddbdacecf628c988f08ce48511fc Mon Sep 17 00:00:00 2001 From: Mamy Ratsimbazafy Date: Thu, 27 Jun 2024 17:38:04 +0200 Subject: [PATCH 1/3] fix: initialization of buckets in MSM and equality check with zero points for Twisted Edwards --- .../math/elliptic/ec_multi_scalar_mul.nim | 6 ++-- .../elliptic/ec_multi_scalar_mul_parallel.nim | 14 ++++---- .../elliptic/ec_twistededwards_projective.nim | 33 ++++++++++--------- constantine/named/README.md | 10 ++++++ .../named/config_fields_and_curves.nim | 14 ++++---- 5 files changed, 48 insertions(+), 29 deletions(-) diff --git a/constantine/math/elliptic/ec_multi_scalar_mul.nim b/constantine/math/elliptic/ec_multi_scalar_mul.nim index d1068a31..117fc750 100644 --- a/constantine/math/elliptic/ec_multi_scalar_mul.nim +++ b/constantine/math/elliptic/ec_multi_scalar_mul.nim @@ -265,7 +265,8 @@ func multiScalarMul_vartime*[bits: static int, EC, ECaff]( const numBuckets = 1 shl (c-1) let buckets = allocHeapArray(EC, numBuckets) - zeroMem(buckets[0].addr, sizeof(EC) * numBuckets) + for i in 0 ..< numBuckets: + buckets[i].setNeutral() # Algorithm # --------- @@ -367,7 +368,8 @@ func multiScalarMulAffine_vartime[bits: static int, EC, ECaff]( when top != 0: # Prologue when excess != 0: # The top might use only a few bits, the affine scheduler would likely have significant collisions - zeroMem(sched.buckets.pt.addr, buckets.pt.sizeof()) + for i in 0 ..< numBuckets: + sched.buckets.pt[i].setNeutral() r.miniMSM(sched.buckets.pt.asUnchecked(), w, kTopWindow, c, coefs, points, N) w -= c else: diff --git a/constantine/math/elliptic/ec_multi_scalar_mul_parallel.nim b/constantine/math/elliptic/ec_multi_scalar_mul_parallel.nim index 03e6ff24..3dddf5e2 100644 --- a/constantine/math/elliptic/ec_multi_scalar_mul_parallel.nim +++ b/constantine/math/elliptic/ec_multi_scalar_mul_parallel.nim @@ -135,14 +135,15 @@ export bestBucketBitSize # Parallel MSM non-affine # ------------------------------ -proc bucketAccumReduce_zeroMem[bits: static int, EC, ECaff]( +proc bucketAccumReduce_withInit[bits: static int, EC, ECaff]( windowSum: ptr EC, buckets: ptr EC or ptr UncheckedArray[EC], bitIndex: int, miniMsmKind: static MiniMsmKind, c: static int, coefs: ptr UncheckedArray[BigInt[bits]], points: ptr UncheckedArray[ECaff], N: int) = const numBuckets = 1 shl (c-1) let buckets = cast[ptr UncheckedArray[EC]](buckets) - zeroMem(buckets, sizeof(EC) * numBuckets) + for i in 0 ..< numBuckets: + buckets[i].setNeutral() bucketAccumReduce(windowSum[], buckets, bitIndex, miniMsmKind, c, coefs, points, N) proc msm_vartime_parallel[bits: static int, EC, ECaff]( @@ -169,14 +170,14 @@ proc msm_vartime_parallel[bits: static int, EC, ECaff]( # --------- block: # 1. Bucket accumulation and reduction - miniMSMsReady[0] = tp.spawnAwaitable bucketAccumReduce_zeroMem( + miniMSMsReady[0] = tp.spawnAwaitable bucketAccumReduce_withInit( miniMSMsResults[0].addr, bucketsMatrix[0].addr, bitIndex = 0, kBottomWindow, c, coefs, points, N) for w in 1 ..< numFullWindows: - miniMSMsReady[w] = tp.spawnAwaitable bucketAccumReduce_zeroMem( + miniMSMsReady[w] = tp.spawnAwaitable bucketAccumReduce_withInit( miniMSMsResults[w].addr, bucketsMatrix[w*numBuckets].addr, bitIndex = w*c, kFullWindow, c, @@ -188,7 +189,7 @@ proc msm_vartime_parallel[bits: static int, EC, ECaff]( when top != 0: when excess != 0: - bucketAccumReduce_zeroMem( + bucketAccumReduce_withInit( r, bucketsMatrix[numFullWindows*numBuckets].addr, bitIndex = top, kTopWindow, c, @@ -375,7 +376,8 @@ proc msmAffine_vartime_parallel[bits: static int, EC, ECaff]( when top != 0: when excess != 0: let buckets = allocHeapArray(EC, numBuckets) - zeroMem(buckets[0].addr, sizeof(EC) * numBuckets) + for i in 0 ..< numBuckets: + buckets[i].setNeutral() r[].bucketAccumReduce(buckets, bitIndex = top, kTopWindow, c, coefs, points, N) buckets.freeHeap() diff --git a/constantine/math/elliptic/ec_twistededwards_projective.nim b/constantine/math/elliptic/ec_twistededwards_projective.nim index e9ab8025..500c0e5d 100644 --- a/constantine/math/elliptic/ec_twistededwards_projective.nim +++ b/constantine/math/elliptic/ec_twistededwards_projective.nim @@ -36,6 +36,22 @@ type EC_TwEdw_Prj*[F] = object template getScalarField*(EC: type EC_TwEdw_Prj): untyped = Fr[EC.F.Name] +func isNeutral*(P: EC_TwEdw_Prj): SecretBool {.inline.} = + ## Returns true if P is the neutral element / identity element + ## and false otherwise, i.e. ∀Q, P+Q == Q + ## Contrary to Short Weierstrass curve, the neutral element is on the curve + # Isogeny-based constructions to create + # prime order curves overload this generic identity check. + result = P.x.isZero() and (P.y == P.z) + +func setNeutral*(P: var EC_TwEdw_Prj) {.inline.} = + ## Set P to the neutral element / identity element + ## i.e. ∀Q, P+Q == Q. + ## Contrary to Short Weierstrass curve, the neutral element is on the curve + P.x.setZero() + P.y.setOne() + P.z.setOne() + func `==`*(P, Q: EC_TwEdw_Prj): SecretBool = ## Constant-time equality check ## This is a costly operation @@ -52,21 +68,8 @@ func `==`*(P, Q: EC_TwEdw_Prj): SecretBool = b.prod(Q.y, P.z) result = result and a == b -func isNeutral*(P: EC_TwEdw_Prj): SecretBool {.inline.} = - ## Returns true if P is the neutral element / identity element - ## and false otherwise, i.e. ∀Q, P+Q == Q - ## Contrary to Short Weierstrass curve, the neutral element is on the curve - # Isogeny-based constructions to create - # prime order curves overload this generic identity check. - result = P.x.isZero() and (P.y == P.z) - -func setNeutral*(P: var EC_TwEdw_Prj) {.inline.} = - ## Set P to the neutral element / identity element - ## i.e. ∀Q, P+Q == Q. - ## Contrary to Short Weierstrass curve, the neutral element is on the curve - P.x.setZero() - P.y.setOne() - P.z.setOne() + # Ensure a zero-init point doesn't propagate 0s and match any + result = result and not(P.isNeutral() xor Q.isNeutral()) func ccopy*(P: var EC_TwEdw_Prj, Q: EC_TwEdw_Prj, ctl: SecretBool) {.inline.} = ## Constant-time conditional copy diff --git a/constantine/named/README.md b/constantine/named/README.md index 474416b3..3d1e2148 100644 --- a/constantine/named/README.md +++ b/constantine/named/README.md @@ -1,3 +1,13 @@ # Named Algebraic Objects This folder holds named fields and named curves configuration and precomputed constants. + + +⚠️ For Twisted Edwards curves + The formula are complete only if d is not a square, otherwise + they must be modified to handle the identity/neutral element with conditional moves. + +Sage script to check with Bandersnatch constants for example + r = Integer('0x1cfb69d4ca675f520cce760202687600ff8f87007419047174fd06b52876e7e1') + d = Integer('0x6389c12633c267cbc66e3bf86be3b6d8cb66677177e54f92b369f2f5188d58e7') + GF(r)(d).nth_root(2, all=True) diff --git a/constantine/named/config_fields_and_curves.nim b/constantine/named/config_fields_and_curves.nim index 6ee2ff96..8cf7faf8 100644 --- a/constantine/named/config_fields_and_curves.nim +++ b/constantine/named/config_fields_and_curves.nim @@ -33,12 +33,14 @@ export CurveFamily, SexticTwist # Barbulescu, R. and S. Duquesne, "Updating Key Size Estimations for Pairings", # Journal of Cryptology, DOI 10.1007/s00145-018-9280-5, January 2018. -# Generates public: -# - type Curve* = enum -# - proc Mod*(curve: static Curve): auto -# which returns the field modulus of the curve -# - proc family*(curve: static Curve): CurveFamily -# which returns the curve family +# ⚠️ For Twisted Edwards curves +# The formula are complete only if d is not a square, otherwise +# they must be modified to handle the identity/neutral element with conditional moves. +# +# Sage script to check with Bandersnatch constants for example +# r = Integer('0x1cfb69d4ca675f520cce760202687600ff8f87007419047174fd06b52876e7e1') +# d = Integer('0x6389c12633c267cbc66e3bf86be3b6d8cb66677177e54f92b369f2f5188d58e7') +# GF(r)(d).nth_root(2, all=True) declareCurves: # ----------------------------------------------------------------------------- From c1da0b025933c9bcc23eb507aa3a52af456b0e9b Mon Sep 17 00:00:00 2001 From: Mamy Ratsimbazafy Date: Thu, 27 Jun 2024 17:45:38 +0200 Subject: [PATCH 2/3] fix: twistedEdwards zero affine point conversion to projective --- constantine/math/elliptic/ec_twistededwards_projective.nim | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/constantine/math/elliptic/ec_twistededwards_projective.nim b/constantine/math/elliptic/ec_twistededwards_projective.nim index 500c0e5d..f16313a2 100644 --- a/constantine/math/elliptic/ec_twistededwards_projective.nim +++ b/constantine/math/elliptic/ec_twistededwards_projective.nim @@ -461,6 +461,11 @@ func fromAffine*[F]( proj.y = aff.y proj.z.setOne() + let inf = aff.isNeutral() + proj.x.csetZero(inf) + proj.y.csetOne(inf) + proj.z.csetOne(inf) + # Vartime overloading # ------------------------------------------------------------ # For generic vartime operations on both ShortWeierstrass curves and Twisted Edwards From e29f2ef5f60c7548810c765f4fade781bdd54f10 Mon Sep 17 00:00:00 2001 From: Mamy Ratsimbazafy Date: Thu, 27 Jun 2024 22:05:20 +0200 Subject: [PATCH 3/3] enabled optimized MSM for Verkle IPA --- config.nims | 2 +- constantine/commitments/eth_verkle_ipa.nim | 4 ++-- constantine/commitments/pedersen_commitments.nim | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/config.nims b/config.nims index efde46d7..0f17934c 100644 --- a/config.nims +++ b/config.nims @@ -1,2 +1,2 @@ -# Absolute imports from porject root +# Absolute imports from project root --path:"." diff --git a/constantine/commitments/eth_verkle_ipa.nim b/constantine/commitments/eth_verkle_ipa.nim index c963efa2..018b111b 100644 --- a/constantine/commitments/eth_verkle_ipa.nim +++ b/constantine/commitments/eth_verkle_ipa.nim @@ -422,7 +422,7 @@ func ipa_verify*[N, logN: static int, EcAff, F]( # ∑ᵢ[uᵢ]Lᵢ + ∑ᵢ[uᵢ⁻¹]Rᵢ + [⟨ā,b̄⟩.w]G - [a₀]G₀ - [a₀.b₀.w]G var t {.noInit.}: EC - t.multiScalarMul_reference_vartime(fs.toOpenArray(), ecs.toOpenArray()) + t.multiScalarMul_vartime(fs.toOpenArray(), ecs.toOpenArray()) # ∑ᵢ[uᵢ]Lᵢ + ∑ᵢ[uᵢ⁻¹]Rᵢ + [⟨ā,b̄⟩.w]G - [a₀]G₀ - [a₀.b₀.w]G = -⟨ā,Ḡ⟩ var C {.noInit.}: EC @@ -949,7 +949,7 @@ func ipa_multi_verify*[N, logN: static int, EcAff, F]( # E = ∑rⁱ.Cᵢ/(t-zᵢ) var E {.noInit.}: EC var Eaff {.noInit.}: EcAff - E.multiScalarMul_reference_vartime(invTminusChallenges, comms_by_challenges, num_distinct_challenges) + E.multiScalarMul_vartime(invTminusChallenges, comms_by_challenges, num_distinct_challenges) Eaff.affine(E) transcript.absorb("E", Eaff) freeHeapAligned(invTminusChallenges) diff --git a/constantine/commitments/pedersen_commitments.nim b/constantine/commitments/pedersen_commitments.nim index 87dac8ca..84b7b9b7 100644 --- a/constantine/commitments/pedersen_commitments.nim +++ b/constantine/commitments/pedersen_commitments.nim @@ -33,7 +33,7 @@ func pedersen_commit[EC, ECaff]( ## ## Output: ## Commit(m) := ∑[mᵢ]Gᵢ - r.multiScalarMul_reference_vartime(messages, public_generators) + r.multiScalarMul_vartime(messages, public_generators) func pedersen_commit*[N: static int, EC, ECaff, F]( crs: PolynomialEval[N, EcAff], @@ -49,7 +49,7 @@ func pedersen_commit*[N: static int, EC, ECaff, F]( ## ## Output: ## Commit(m) := ∑[mᵢ]Gᵢ - r.multiScalarMul_reference_vartime(messages.evals, crs.evals) + r.multiScalarMul_vartime(messages.evals, crs.evals) func pedersen_commit*[EC, ECaff, F]( public_generators: View[ECaff],