From 03898b22926d3b86ff8227ee63c43df533dea4e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mamy=20Andr=C3=A9-Ratsimbazafy?= Date: Sat, 21 Mar 2020 01:59:23 +0100 Subject: [PATCH] =?UTF-8?q?Implement=20squaring=20for=20=F0=9D=94=BDp6=20?= =?UTF-8?q?=3D=20=F0=9D=94=BDp2[=E2=88=9B(1+=F0=9D=91=96)]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- constantine/tower_field_extensions/README.md | 4 + .../tower_field_extensions/abelian_groups.nim | 134 ++++++++++-------- .../tower_field_extensions/fp2_complex.nim | 2 +- .../tower_field_extensions/fp6_1_plus_i.nim | 82 +++++++++++ tests/test_fp6.nim | 133 +++++++++++++++++ 5 files changed, 293 insertions(+), 62 deletions(-) create mode 100644 constantine/tower_field_extensions/fp6_1_plus_i.nim create mode 100644 tests/test_fp6.nim diff --git a/constantine/tower_field_extensions/README.md b/constantine/tower_field_extensions/README.md index d20ad3f..9ae3b58 100644 --- a/constantine/tower_field_extensions/README.md +++ b/constantine/tower_field_extensions/README.md @@ -63,6 +63,10 @@ From Ben Edgington, https://hackmd.io/@benjaminion/bls12-381 Daniel V. Bailey and Christof Paar, 1998\ https://www.emsec.ruhr-uni-bochum.de/media/crypto/veroeffentlichungen/2015/03/26/crypto98rc9.pdf +- Asymmetric Squaring Formulae\ + Jaewook Chung and M. Anwar Hasan\ + http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf + - Multiplication and Squaring on Pairing-Friendly Fields\ Augusto Jun Devegili and Colm Ó hÉigeartaigh and Michael Scott and Ricardo Dahab, 2006\ https://eprint.iacr.org/2006/471 diff --git a/constantine/tower_field_extensions/abelian_groups.nim b/constantine/tower_field_extensions/abelian_groups.nim index 07b7ca0..c3d5bfe 100644 --- a/constantine/tower_field_extensions/abelian_groups.nim +++ b/constantine/tower_field_extensions/abelian_groups.nim @@ -29,67 +29,6 @@ import # sum(mR, a, b) # diff(mR, a, b) -# ############################################################ -# -# Quadratic Extension fields -# -# ############################################################ - -type - QuadExtAddGroup* = concept x - ## Quadratic extension fields - Abelian Additive Group concept - type BaseField = auto - x.c0 is BaseField - x.c1 is BaseField - -func `==`*(a, b: QuadExtAddGroup): CTBool[Word] = - ## Constant-time equality check - (a.c0 == b.c0) and (a.c1 == b.c1) - -func setZero*(a: var QuadExtAddGroup) = - ## Set ``a`` to zero in the extension field - ## Coordinates 0 + 0 𝛼 - ## with 𝛼 the solution of f(x) = x² - µ = 0 - a.c0.setZero() - a.c1.setZero() - -func setOne*(a: var QuadExtAddGroup) = - ## Set ``a`` to one in the extension field - ## Coordinates 1 + 0 𝛼 - ## with 𝛼 the solution of f(x) = x² - µ = 0 - a.c0.setOne() - a.c1.setZero() - -func `+=`*(a: var QuadExtAddGroup, b: QuadExtAddGroup) = - ## Addition in the extension field - a.c0 += b.c0 - a.c1 += b.c1 - -func `-=`*(a: var QuadExtAddGroup, b: QuadExtAddGroup) = - ## Substraction in the extension field - a.c0 -= b.c0 - a.c1 -= b.c1 - -func double*(r: var QuadExtAddGroup, a: QuadExtAddGroup) = - ## Double in the extension field - r.c0.double(a.c0) - r.c1.double(a.c1) - -func sum*(r: var QuadExtAddGroup, a, b: QuadExtAddGroup) = - ## Sum ``a`` and ``b`` into r - r.c0.sum(a.c0, b.c0) - r.c1.sum(a.c1, b.c1) - -func diff*(r: var QuadExtAddGroup, a, b: QuadExtAddGroup) = - ## Difference of ``a`` by `b`` into r - r.c0.diff(a.c0, b.c0) - r.c1.diff(a.c1, b.c1) - -func neg*(r: var QuadExtAddGroup, a: QuadExtAddGroup) = - ## Negate ``a`` into ``r`` - r.c0.neg(a.c0) - r.c1.neg(a.c1) - # ############################################################ # # Cubic Extension fields @@ -142,6 +81,12 @@ func double*(r: var CubicExtAddGroup, a: CubicExtAddGroup) = r.c1.double(a.c1) r.c2.double(a.c2) +func double*(a: var CubicExtAddGroup) = + ## Double in the extension field + double(a.c0) + double(a.c1) + double(a.c2) + func sum*(r: var CubicExtAddGroup, a, b: CubicExtAddGroup) = ## Sum ``a`` and ``b`` into r r.c0.sum(a.c0, b.c0) @@ -153,3 +98,70 @@ func diff*(r: var CubicExtAddGroup, a, b: CubicExtAddGroup) = r.c0.diff(a.c0, b.c0) r.c1.diff(a.c1, b.c1) r.c2.diff(a.c2, b.c2) + +# ############################################################ +# +# Quadratic Extension fields +# +# ############################################################ + +type + QuadExtAddGroup* = concept x + ## Quadratic extension fields - Abelian Additive Group concept + not(x is CubicExtAddGroup) + type BaseField = auto + x.c0 is BaseField + x.c1 is BaseField + +func `==`*(a, b: QuadExtAddGroup): CTBool[Word] = + ## Constant-time equality check + (a.c0 == b.c0) and (a.c1 == b.c1) + +func setZero*(a: var QuadExtAddGroup) = + ## Set ``a`` to zero in the extension field + ## Coordinates 0 + 0 𝛼 + ## with 𝛼 the solution of f(x) = x² - µ = 0 + a.c0.setZero() + a.c1.setZero() + +func setOne*(a: var QuadExtAddGroup) = + ## Set ``a`` to one in the extension field + ## Coordinates 1 + 0 𝛼 + ## with 𝛼 the solution of f(x) = x² - µ = 0 + a.c0.setOne() + a.c1.setZero() + +func `+=`*(a: var QuadExtAddGroup, b: QuadExtAddGroup) = + ## Addition in the extension field + a.c0 += b.c0 + a.c1 += b.c1 + +func `-=`*(a: var QuadExtAddGroup, b: QuadExtAddGroup) = + ## Substraction in the extension field + a.c0 -= b.c0 + a.c1 -= b.c1 + +func double*(r: var QuadExtAddGroup, a: QuadExtAddGroup) = + ## Double in the extension field + r.c0.double(a.c0) + r.c1.double(a.c1) + +func double*(a: var QuadExtAddGroup) = + ## Double in the extension field + double(a.c0) + double(a.c1) + +func sum*(r: var QuadExtAddGroup, a, b: QuadExtAddGroup) = + ## Sum ``a`` and ``b`` into r + r.c0.sum(a.c0, b.c0) + r.c1.sum(a.c1, b.c1) + +func diff*(r: var QuadExtAddGroup, a, b: QuadExtAddGroup) = + ## Difference of ``a`` by `b`` into r + r.c0.diff(a.c0, b.c0) + r.c1.diff(a.c1, b.c1) + +func neg*(r: var QuadExtAddGroup, a: QuadExtAddGroup) = + ## Negate ``a`` into ``r`` + r.c0.neg(a.c0) + r.c1.neg(a.c1) diff --git a/constantine/tower_field_extensions/fp2_complex.nim b/constantine/tower_field_extensions/fp2_complex.nim index 5f74040..25ea7fc 100644 --- a/constantine/tower_field_extensions/fp2_complex.nim +++ b/constantine/tower_field_extensions/fp2_complex.nim @@ -61,7 +61,7 @@ type c0*, c1*: Fp[C] func square*(r: var Fp2, a: Fp2) = - ## Return a^2 in 𝔽p2 = 𝔽p[𝑖] in ``r`` + ## Return a² in 𝔽p2 = 𝔽p[𝑖] in ``r`` ## ``r`` is initialized/overwritten # (c0, c1)² => (c0 + c1𝑖)² # => c0² + 2 c0 c1𝑖 + (c1𝑖)² diff --git a/constantine/tower_field_extensions/fp6_1_plus_i.nim b/constantine/tower_field_extensions/fp6_1_plus_i.nim new file mode 100644 index 0000000..a223c62 --- /dev/null +++ b/constantine/tower_field_extensions/fp6_1_plus_i.nim @@ -0,0 +1,82 @@ +# 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. + +# ############################################################ +# +# Cubic Extension field over base field 𝔽p2 +# 𝔽p6 = 𝔽p2[∛(1 + 𝑖)] +# +# ############################################################ + +# This implements a quadratic extension field over 𝔽p2 = 𝔽p[𝑖] +# the base field 𝔽p: +# 𝔽p6 = 𝔽p2[∛(1 + 𝑖)] +# with element A of coordinates (a0, a1) represented +# by a0 + a1 ξ + a2 ξ² +# +# The irreducible polynomial chosen is +# x³ - ξ with ξ = 𝑖+1 +# +# +# Consequently, for this file Fp2 to be valid +# 𝑖+1 MUST not be a square in 𝔽p2 + +import + ../arithmetic, + ../config/curves, + ./abelian_groups, + ./fp2_complex + +type + Fp6*[C: static Curve] = object + ## Element of the extension field + ## 𝔽p6 = 𝔽p2[∛(1 + 𝑖)] + ## + ## with coordinates (c0, c1, c2) such as + ## c0 + c1 ξ + c2 ξ² + ## + ## This requires 1 + 𝑖 to not be a cube in 𝔽p2 + c0*, c1*, c2*: Fp2[C] + + Xi = object + ## ξ (Xi) the cubic non-residue + +func `*`(_: typedesc[Xi], a: Fp2): Fp2 = + ## Multiply an element of 𝔽p2 by 𝔽p6 cubic non-residue 1 + 𝑖 + ## (c0 + c1 𝑖) (1 + 𝑖) => c0 + (c0 + c1)𝑖 + c1 𝑖² + ## => c0 - c1 + (c0 + c1) 𝑖 + result.c0 = a.c0 - a.c1 + result.c1 = a.c0 + a.c1 + +template `*`(a: Fp2, _: typedesc[Xi]): Fp2 = + Xi * a + +func square*[C](r: var Fp6[C], a: Fp6[C]) = + ## Return a² + ## + # Algorithm is Chung-Hasan Squaring SQR3 + # http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf + var v2{.noInit.}, v3{.noInit.}, v4{.noInit.}, v5{.noInit.}: Fp2[C] + + v4.prod(a.c0, a.c1) + v4.double() + v5.square(a.c2) + r.c1 = Xi * v5 + r.c1 += v4 + v2.diff(v4, v5) + v3.square(a.c0) + v4.diff(a.c0, a.c1) + v4 += a.c2 + v5.prod(a.c1, a.c2) + v5.double() + v4.square(v4) + r.c0 = Xi * v5 + r.c0 += v3 + r.c2.sum(v2, v4) + r.c2 += v5 + r.c2 -= v3 diff --git a/tests/test_fp6.nim b/tests/test_fp6.nim new file mode 100644 index 0000000..fbd8aec --- /dev/null +++ b/tests/test_fp6.nim @@ -0,0 +1,133 @@ +# 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. + +import + # Standard library + unittest, times, random, + # Internals + ../constantine/tower_field_extensions/[abelian_groups, fp6_1_plus_i], + ../constantine/config/[common, curves], + ../constantine/arithmetic, + # Test utilities + ../helpers/prng + +const Iters = 128 + +var rng: RngState +let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32 +rng.seed(seed) +echo "test_fp6 xoshiro512** seed: ", seed + +# Import: wrap in field element tests in small procedures +# otherwise they will become globals, +# and will create binary size issues. +# Also due to Nim stack scanning, +# having too many elements on the stack (a couple kB) +# will significantly slow down testing (100x is possible) + +suite "𝔽p6 = 𝔽p2[∛(1+𝑖)] (irreducible polynomial x³ - (1+𝑖))": + test "Squaring 1 returns 1": + template test(C: static Curve) = + block: + proc testInstance() = + let One = block: + var O{.noInit.}: Fp6[C] + O.setOne() + O + block: + var r{.noinit.}: Fp6[C] + r.square(One) + check: bool(r == One) + # block: + # var r{.noinit.}: Fp6[C] + # r.prod(One, One) + # check: bool(r == One) + + testInstance() + + test(BN254) + test(BLS12_381) + + test "Squaring 2 returns 4": + template test(C: static Curve) = + block: + proc testInstance() = + let One = block: + var O{.noInit.}: Fp6[C] + O.setOne() + O + + var Two: Fp6[C] + Two.double(One) + + var Four: Fp6[C] + Four.double(Two) + + var r: Fp6[C] + r.square(Two) + + check: bool(r == Four) + + testInstance() + + test(BN254) + test(BLS12_381) + + test "Squaring 3 returns 9": + template test(C: static Curve) = + block: + proc testInstance() = + let One = block: + var O{.noInit.}: Fp6[C] + O.setOne() + O + + var Three: Fp6[C] + for _ in 0 ..< 3: + Three += One + + var Nine: Fp6[C] + for _ in 0 ..< 9: + Nine += One + + var u: Fp6[C] + u.square(Three) + + check: bool(u == Nine) + + testInstance() + + test(BN254) + test(BLS12_381) + + test "Squaring -3 returns 9": + template test(C: static Curve) = + block: + proc testInstance() = + let One = block: + var O{.noInit.}: Fp6[C] + O.setOne() + O + + var MinusThree: Fp6[C] + for _ in 0 ..< 3: + MinusThree -= One + + var Nine: Fp6[C] + for _ in 0 ..< 9: + Nine += One + + var u: Fp6[C] + u.square(MinusThree) + + check: bool(u == Nine) + + testInstance() + + test(BN254) + test(BLS12_381)