diff --git a/constantine/elliptic/ec_shortweierstrass_affine.nim b/constantine/elliptic/ec_shortweierstrass_affine.nim index 45d86f8..8379b59 100644 --- a/constantine/elliptic/ec_shortweierstrass_affine.nim +++ b/constantine/elliptic/ec_shortweierstrass_affine.nim @@ -124,6 +124,7 @@ func trySetFromCoordX*[F, Tw]( ## will be provided, this is intended for testing purposes. P.y.curve_eq_rhs(x, Tw) result = sqrt_if_square(P.y) + P.x = x func neg*(P: var ECP_ShortW_Aff, Q: ECP_ShortW_Aff) = ## Negate ``P`` diff --git a/constantine/io/io_towers.nim b/constantine/io/io_towers.nim index dcc375c..e08a202 100644 --- a/constantine/io/io_towers.nim +++ b/constantine/io/io_towers.nim @@ -12,6 +12,7 @@ import std/typetraits, # Internal ./io_bigints, ./io_fields, + ../primitives, ../arithmetic/finite_fields, ../tower_field_extensions/tower_instantiation @@ -30,11 +31,11 @@ export tower_instantiation func appendHex*(accum: var string, f: Fp2 or Fp4 or Fp6 or Fp12, order: static Endianness = bigEndian) = ## Hex accumulator accum.add static($f.typeof.genericHead() & '(') - for fieldName, fieldValue in fieldPairs(f): - when fieldName != "c0": + staticFor i, 0, f.coords.len: + when i != 0: accum.add ", " - accum.add fieldName & ": " - accum.appendHex(fieldValue, order) + accum.add "c" & $i & ": " + accum.appendHex(f.coords[i], order) accum.add ")" func toHex*(f: Fp2 or Fp4 or Fp6 or Fp12, order: static Endianness = bigEndian): string = @@ -109,18 +110,14 @@ func fromHex*(T: typedesc[Fp12], func fromUint*(a: var ExtensionField, src: SomeUnsignedInt) = ## Set ``a`` to the bigint value int eh extension field - for fieldName, fA in fieldPairs(a): - when fieldName == "c0": - fA.fromUint(src) - else: - fA.setZero() + a.coords[0].fromUint(src) + staticFor i, 1, a.coords.len: + a.coords[i].setZero() func fromInt*(a: var ExtensionField, src: SomeInteger) = ## Parse a regular signed integer ## and store it into a Fp^n ## A negative integer will be instantiated as a negated number (mod p^n) - for fieldName, fA in fieldPairs(a): - when fieldName == "c0": - fA.fromInt(src) - else: - fA.setZero() + a.coords[0].fromInt(src) + staticFor i, 1, a.coords.len: + a.coords[i].setZero() diff --git a/constantine/isogeny/frobenius.nim b/constantine/isogeny/frobenius.nim index f596097..e7f32a7 100644 --- a/constantine/isogeny/frobenius.nim +++ b/constantine/isogeny/frobenius.nim @@ -10,6 +10,7 @@ import std/macros, ../arithmetic, ../towers, + ../primitives, ../curves/zoo_frobenius # Frobenius Map @@ -79,9 +80,9 @@ func frobenius_map*[C](r: var Fp12[C], a: Fp12[C], k: static int = 1) {.inline.} ## Computes a^(p^k) ## The p-power frobenius automorphism on 𝔽p12 static: doAssert r.c0 is Fp4 - for r_fp4, a_fp4 in fields(r, a): - for r_fp2, a_fp2 in fields(r_fp4, a_fp4): - r_fp2.frobenius_map(a_fp2, k) + staticFor i, 0, r.coords.len: + staticFor j, 0, r.coords[0].coords.len: + r.coords[i].coords[j].frobenius_map(a.coords[i].coords[j], k) r.c0.c0.mulCheckSparse frobMapConst(C, 0, k) r.c0.c1.mulCheckSparse frobMapConst(C, 3, k) diff --git a/constantine/pairing/lines_projective.nim b/constantine/pairing/lines_projective.nim index 80f4783..1ba2646 100644 --- a/constantine/pairing/lines_projective.nim +++ b/constantine/pairing/lines_projective.nim @@ -158,18 +158,18 @@ func line_eval_add[F]( when F.C.getSexticTwist() == M_Twist: A *= SexticNonResidue # A = ξ (X₁ - Z₁X₂) -func line_eval_fused_double[F]( - line: var Line[F], - T: var ECP_ShortW_Prj[F, OnTwist]) = +func line_eval_fused_double[Field]( + line: var Line[Field], + T: var ECP_ShortW_Prj[Field, OnTwist]) = ## Fused line evaluation and elliptic point doubling # Grewal et al, 2012 adapted to Scott 2019 line notation - var A {.noInit.}, B {.noInit.}, C {.noInit.}: F - var E {.noInit.}, F {.noInit.}, G {.noInit.}: F + var A {.noInit.}, B {.noInit.}, C {.noInit.}: Field + var E {.noInit.}, F {.noInit.}, G {.noInit.}: Field template H: untyped = line.x - const b3 = 3*F.C.getCoefB() + const b3 = 3*Field.C.getCoefB() var snrY = T.y - when F.C.getSexticTwist() == D_Twist: + when Field.C.getSexticTwist() == D_Twist: snrY *= SexticNonResidue A.prod(T.x, snrY) @@ -178,11 +178,11 @@ func line_eval_fused_double[F]( C.square(T.z) # C = Z² var snrB = B - when F.C.getSexticTwist() == D_Twist: + when Field.C.getSexticTwist() == D_Twist: snrB *= SexticNonResidue E.prod(C, b3) - when F.C.getSexticTwist() == M_Twist: + when Field.C.getSexticTwist() == M_Twist: E *= SexticNonResidue # E = 3b'Z² = 3bξ Z² F.prod(E, 3) # F = 3E = 9bZ² @@ -195,7 +195,7 @@ func line_eval_fused_double[F]( line.z.square(T.x) line.z *= 3 # lz = 3X² - when F.C.getSexticTwist() == D_Twist: + when Field.C.getSexticTwist() == D_Twist: line.z *= SexticNonResidue line.y.diff(E, snrB) # ly = E-B = 3b'Z² - Y² @@ -213,7 +213,7 @@ func line_eval_fused_double[F]( # M-twist: (Y²+9bξZ²)²/4 - 3*(3bξZ²)² # D-Twist: (ξY²+9bZ²)²/4 - 3*(3bZ²)² - when F.C.getSexticTwist() == D_Twist: + when Field.C.getSexticTwist() == D_Twist: H *= SexticNonResidue T.z.prod(snrB, H) # Z₃ = BH = Y²((Y+Z)² - (Y²+Z²)) = 2Y³Z # M-twist: 2Y³Z @@ -221,26 +221,26 @@ func line_eval_fused_double[F]( # Correction for Fp4 towering H.neg() # lx = -H - when F.C.getSexticTwist() == M_Twist: + when Field.C.getSexticTwist() == M_Twist: H *= SexticNonResidue # else: the SNR is already integrated in H -func line_eval_fused_add[F]( - line: var Line[F], - T: var ECP_ShortW_Prj[F, OnTwist], - Q: ECP_ShortW_Aff[F, OnTwist]) = +func line_eval_fused_add[Field]( + line: var Line[Field], + T: var ECP_ShortW_Prj[Field, OnTwist], + Q: ECP_ShortW_Aff[Field, OnTwist]) = ## Fused line evaluation and elliptic point addition # Grewal et al, 2012 adapted to Scott 2019 line notation var - A {.noInit.}: F - B {.noInit.}: F - C {.noInit.}: F - D {.noInit.}: F - E {.noInit.}: F - F {.noInit.}: F - G {.noInit.}: F - H {.noInit.}: F - I {.noInit.}: F + A {.noInit.}: Field + B {.noInit.}: Field + C {.noInit.}: Field + D {.noInit.}: Field + E {.noInit.}: Field + F {.noInit.}: Field + G {.noInit.}: Field + H {.noInit.}: Field + I {.noInit.}: Field template lambda: untyped = line.x template theta: untyped = line.z @@ -275,7 +275,7 @@ func line_eval_fused_add[F]( # Line evaluation theta.neg() - when F.C.getSexticTwist() == M_Twist: + when Field.C.getSexticTwist() == M_Twist: lambda *= SexticNonResidue # A = ξ (X₁ - Z₁X₂) # Public proc diff --git a/constantine/tower_field_extensions/extension_fields.nim b/constantine/tower_field_extensions/extension_fields.nim index 257f46e..edd2acd 100644 --- a/constantine/tower_field_extensions/extension_fields.nim +++ b/constantine/tower_field_extensions/extension_fields.nim @@ -7,7 +7,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - ../config/common, + ../config/[common, curves], ../primitives, ../arithmetic @@ -30,46 +30,55 @@ type ## ## Placeholder for the appropriate quadratic, cubic or sectic non-residue - CubicExt* = concept x - ## Cubic Extension field concept - type BaseField = auto - x.c0 is BaseField - x.c1 is BaseField - x.c2 is BaseField + QuadraticExt*[F] = object + ## Quadratic Extension field + coords*: array[2, F] - QuadraticExt* = concept x - ## Quadratic Extension field concept - not(x is CubicExt) + CubicExt*[F] = object + ## Cubic Extension field + coords*: array[3, F] - type BaseField = auto - x.c0 is BaseField - x.c1 is BaseField + ExtensionField*[F] = QuadraticExt[F] or CubicExt[F] - ExtensionField* = QuadraticExt or CubicExt +template c0*(a: ExtensionField): auto = + a.coords[0] +template c1*(a: ExtensionField): auto = + a.coords[1] +template c2*(a: CubicExt): auto = + a.coords[2] + +template `c0=`*(a: ExtensionField, v: auto) = + a.coords[0] = v +template `c1=`*(a: ExtensionField, v: auto) = + a.coords[1] = v +template `c2=`*(a: CubicExt, v: auto) = + a.coords[2] = v + +template C*(E: type ExtensionField): Curve = + E.F.C + +template fieldMod*(E: type ExtensionField): auto = + Fp[E.F.C].fieldMod() # Initialization # ------------------------------------------------------------------- func setZero*(a: var ExtensionField) = ## Set ``a`` to 0 in the extension field - for field in fields(a): - field.setZero() + staticFor i, 0, a.coords.len: + a.coords[i].setZero() func setOne*(a: var ExtensionField) = ## Set ``a`` to 1 in the extension field - for fieldName, fA in fieldPairs(a): - when fieldName == "c0": - fA.setOne() - else: - fA.setZero() + a.coords[0].setOne() + staticFor i, 1, a.coords.len: + a.coords[i].setZero() func fromBig*(a: var ExtensionField, src: BigInt) = ## Set ``a`` to the bigint value in the extension field - for fieldName, fA in fieldPairs(a): - when fieldName == "c0": - fA.fromBig(src) - else: - fA.setZero() + a.coords[0].fromBig(src) + staticFor i, 1, a.coords.len: + a.coords[i].setZero() # Comparison # ------------------------------------------------------------------- @@ -77,32 +86,28 @@ func fromBig*(a: var ExtensionField, src: BigInt) = func `==`*(a, b: ExtensionField): SecretBool = ## Constant-time equality check result = CtTrue - for fA, fB in fields(a, b): - result = result and (fA == fB) + staticFor i, 0, a.coords.len: + result = result and (a.coords[i] == b.coords[i]) func isZero*(a: ExtensionField): SecretBool = ## Constant-time check if zero result = CtTrue - for fA in fields(a): - result = result and fA.isZero() + staticFor i, 0, a.coords.len: + result = result and a.coords[i].isZero() func isOne*(a: ExtensionField): SecretBool = ## Constant-time check if one result = CtTrue - for fieldName, fA in fieldPairs(a): - when fieldName == "c0": - result = result and fA.isOne() - else: - result = result and fA.isZero() + result = result and a.coords[0].isOne() + staticFor i, 1, a.coords.len: + result = result and a.coords[i].isZero() func isMinusOne*(a: ExtensionField): SecretBool = ## Constant-time check if -1 result = CtTrue - for fieldName, fA in fieldPairs(a): - when fieldName == "c0": - result = result and fA.isMinusOne() - else: - result = result and fA.isZero() + result = result and a.coords[0].isMinusOne() + staticFor i, 1, a.coords.len: + result = result and a.coords[i].isZero() # Copies # ------------------------------------------------------------------- @@ -112,68 +117,56 @@ func ccopy*(a: var ExtensionField, b: ExtensionField, ctl: SecretBool) = ## If ctl is true: b is copied into a ## if ctl is false: b is not copied and a is unmodified ## Time and memory accesses are the same whether a copy occurs or not - for fA, fB in fields(a, b): - ccopy(fA, fB, ctl) + staticFor i, 0, a.coords.len: + a.coords[i].ccopy(b.coords[i], ctl) # Abelian group # ------------------------------------------------------------------- func neg*(r: var ExtensionField, a: ExtensionField) = ## Field out-of-place negation - for fR, fA in fields(r, a): - fR.neg(fA) + staticFor i, 0, a.coords.len: + r.coords[i].neg(a.coords[i]) func neg*(a: var ExtensionField) = ## Field in-place negation - for fA in fields(a): - fA.neg() + staticFor i, 0, a.coords.len: + a.coords[i].neg() func `+=`*(a: var ExtensionField, b: ExtensionField) = ## Addition in the extension field - for fA, fB in fields(a, b): - fA += fB + staticFor i, 0, a.coords.len: + a.coords[i] += b.coords[i] func `-=`*(a: var ExtensionField, b: ExtensionField) = ## Substraction in the extension field - for fA, fB in fields(a, b): - fA -= fB + staticFor i, 0, a.coords.len: + a.coords[i] -= b.coords[i] func double*(r: var ExtensionField, a: ExtensionField) = ## Field out-of-place doubling - for fR, fA in fields(r, a): - fR.double(fA) + staticFor i, 0, a.coords.len: + r.coords[i].double(a.coords[i]) func double*(a: var ExtensionField) = ## Field in-place doubling - for fA in fields(a): - fA.double() + staticFor i, 0, a.coords.len: + a.coords[i].double() func div2*(a: var ExtensionField) = ## Field in-place division by 2 - for fA in fields(a): - fA.div2() + staticFor i, 0, a.coords.len: + a.coords[i].div2() -func sum*(r: var QuadraticExt, a, b: QuadraticExt) = +func sum*(r: var ExtensionField, a, b: ExtensionField) = ## Sum ``a`` and ``b`` into ``r`` - r.c0.sum(a.c0, b.c0) - r.c1.sum(a.c1, b.c1) + staticFor i, 0, a.coords.len: + r.coords[i].sum(a.coords[i], b.coords[i]) -func sum*(r: var CubicExt, a, b: CubicExt) = - ## Sum ``a`` and ``b`` into ``r`` - r.c0.sum(a.c0, b.c0) - r.c1.sum(a.c1, b.c1) - r.c2.sum(a.c2, b.c2) - -func diff*(r: var QuadraticExt, a, b: QuadraticExt) = +func diff*(r: var ExtensionField, a, b: ExtensionField) = ## Diff ``a`` and ``b`` into ``r`` - r.c0.diff(a.c0, b.c0) - r.c1.diff(a.c1, b.c1) - -func diff*(r: var CubicExt, a, b: CubicExt) = - ## Diff ``a`` and ``b`` into ``r`` - r.c0.diff(a.c0, b.c0) - r.c1.diff(a.c1, b.c1) - r.c2.diff(a.c2, b.c2) + staticFor i, 0, a.coords.len: + r.coords[i].diff(a.coords[i], b.coords[i]) func conj*(a: var QuadraticExt) = ## Computes the conjugate in-place @@ -211,18 +204,18 @@ func conj*(r: var CubicExt, a: CubicExt) = func cneg*(a: var ExtensionField, ctl: SecretBool) = ## Constant-time in-place conditional negation ## Only negate if ctl is true - for fA in fields(a): - fA.cneg(ctl) + staticFor i, 0, a.coords.len: + a.coords[i].cneg(ctl) func cadd*(a: var ExtensionField, b: ExtensionField, ctl: SecretBool) = ## Constant-time in-place conditional addition - for fA, fB in fields(a, b): - fA.cadd(fB, ctl) + staticFor i, 0, a.coords.len: + a.coords[i].cadd(b.coords[i], ctl) func csub*(a: var ExtensionField, b: ExtensionField, ctl: SecretBool) = ## Constant-time in-place conditional substraction - for fA, fB in fields(a, b): - fA.csub(fB, ctl) + staticFor i, 0, a.coords.len: + a.coords[i].csub(b.coords[i], ctl) # Multiplication by a small integer known at compile-time # ------------------------------------------------------------------- @@ -603,10 +596,9 @@ func mul_sparse_generic_by_0y( var t{.noInit.}: typeof(a.c0) t.prod(a.c1, b) - t *= NonResidue r.c1.prod(a.c0, b) # aliasing: a unneeded now - r.c0 = t + r.c0.prod(t, NonResidue) func mul_sparse_generic_by_0y( r: var QuadraticExt, a: QuadraticExt, @@ -627,10 +619,9 @@ func mul_sparse_generic_by_0y( var t{.noInit.}: typeof(a.c0) t.prod(a.c1, b) - t *= NonResidue r.c1.prod(a.c0, b) # aliasing: a unneeded now - r.c0 = t + r.c0.prod(t, NonResidue) func invImpl(r: var QuadraticExt, a: QuadraticExt) = ## Compute the multiplicative inverse of ``a`` diff --git a/constantine/tower_field_extensions/tower_instantiation.nim b/constantine/tower_field_extensions/tower_instantiation.nim index f654d04..edcf9c6 100644 --- a/constantine/tower_field_extensions/tower_instantiation.nim +++ b/constantine/tower_field_extensions/tower_instantiation.nim @@ -43,8 +43,8 @@ func prod*(r: var Fp, a: Fp, _: type NonResidue){.inline.} = # ---------------------------------------------------------------- type - Fp2*[C: static Curve] = object - c0*, c1*: Fp[C] + Fp2*[C: static Curve] = + QuadraticExt[Fp[C]] template fromComplexExtension*[F](elem: F): static bool = ## Returns true if the input is a complex extension @@ -190,11 +190,11 @@ func `/=`*(a: var Fp2, _: type NonResidue) {.inline.} = # ---------------------------------------------------------------- type - Fp4*[C: static Curve] = object - c0*, c1*: Fp2[C] + Fp4*[C: static Curve] = + QuadraticExt[Fp2[C]] - Fp6*[C: static Curve] = object - c0*, c1*, c2*: Fp2[C] + Fp6*[C: static Curve] = + CubicExt[Fp2[C]] func prod*(r: var Fp4, a: Fp4, _: type NonResidue) = ## Multiply an element of 𝔽p4 by the non-residue @@ -253,9 +253,8 @@ func `*=`*(a: var Fp6, _: type NonResidue) {.inline.} = # ---------------------------------------------------------------- type - Fp12*[C: static Curve] = object - c0*, c1*, c2*: Fp4[C] - # c0*, c1*: Fp6[C] + Fp12*[C: static Curve] = + CubicExt[Fp4[C]] # Sparse functions # ---------------------------------------------------------------- diff --git a/helpers/prng_unsafe.nim b/helpers/prng_unsafe.nim index e43a9e5..38740ac 100644 --- a/helpers/prng_unsafe.nim +++ b/helpers/prng_unsafe.nim @@ -151,8 +151,8 @@ func random_unsafe(rng: var RngState, a: var FF) = func random_unsafe(rng: var RngState, a: var ExtensionField) = ## Recursively initialize an extension Field element ## Unsafe: for testing and benchmarking purposes only - for field in fields(a): - rng.random_unsafe(field) + for i in 0 ..< a.coords.len: + rng.random_unsafe(a.coords[i]) func random_word_highHammingWeight(rng: var RngState): BaseType = let numZeros = rng.random_unsafe(WordBitWidth div 3) # Average Hamming Weight is 1-0.33/2 = 0.83 @@ -182,8 +182,8 @@ func random_highHammingWeight(rng: var RngState, a: var FF) = func random_highHammingWeight(rng: var RngState, a: var ExtensionField) = ## Recursively initialize an extension Field element ## Unsafe: for testing and benchmarking purposes only - for field in fields(a): - rng.random_highHammingWeight(field) + for i in 0 ..< a.coords.len: + rng.random_highHammingWeight(a.coords[i]) func random_long01Seq(rng: var RngState, a: var openArray[byte]) = ## Initialize a bytearray @@ -227,8 +227,8 @@ func random_long01Seq(rng: var RngState, a: var FF) = func random_long01Seq(rng: var RngState, a: var ExtensionField) = ## Recursively initialize an extension Field element ## Unsafe: for testing and benchmarking purposes only - for field in fields(a): - rng.random_long01Seq(field) + for i in 0 ..< a.coords.len: + rng.random_long01Seq(a.coords[i]) # Elliptic curves # ------------------------------------------------------------ diff --git a/tests/t_fp_tower_frobenius_template.nim b/tests/t_fp_tower_frobenius_template.nim index 6c9d272..5e9f4ba 100644 --- a/tests/t_fp_tower_frobenius_template.nim +++ b/tests/t_fp_tower_frobenius_template.nim @@ -73,7 +73,7 @@ proc runFrobeniusTowerTests*[N]( var fa {.noInit.}: typeof(a) fa.frobenius_map(a, k = 1) - a.powUnsafeExponent(Field.C.Mod, window = 3) + a.powUnsafeExponent(Field.fieldMod(), window = 3) check: bool(a == fa) staticFor(curve, TestCurves): @@ -89,8 +89,8 @@ proc runFrobeniusTowerTests*[N]( var fa {.noInit.}: typeof(a) fa.frobenius_map(a, k = 2) - a.powUnsafeExponent(Field.C.Mod, window = 3) - a.powUnsafeExponent(Field.C.Mod, window = 3) + a.powUnsafeExponent(Field.fieldMod(), window = 3) + a.powUnsafeExponent(Field.fieldMod(), window = 3) check: bool(a == fa) @@ -108,9 +108,9 @@ proc runFrobeniusTowerTests*[N]( var fa {.noInit.}: typeof(a) fa.frobenius_map(a, k = 3) - a.powUnsafeExponent(Field.C.Mod, window = 3) - a.powUnsafeExponent(Field.C.Mod, window = 3) - a.powUnsafeExponent(Field.C.Mod, window = 3) + a.powUnsafeExponent(Field.fieldMod(), window = 3) + a.powUnsafeExponent(Field.fieldMod(), window = 3) + a.powUnsafeExponent(Field.fieldMod(), window = 3) check: bool(a == fa) staticFor(curve, TestCurves):