diff --git a/constantine/ethereum_bls_signatures.nim b/constantine/ethereum_bls_signatures.nim index 9cb709f..0169bc9 100644 --- a/constantine/ethereum_bls_signatures.nim +++ b/constantine/ethereum_bls_signatures.nim @@ -80,6 +80,7 @@ import ], ./math/io/[io_bigints, io_fields], signatures/bls_signatures, + serialization/codecs_status_codes, serialization/codecs_bls12_381 export diff --git a/constantine/ethereum_eip4844_kzg_polynomial_commitments.nim b/constantine/ethereum_eip4844_kzg_polynomial_commitments.nim index 831f5c6..f5651b8 100644 --- a/constantine/ethereum_eip4844_kzg_polynomial_commitments.nim +++ b/constantine/ethereum_eip4844_kzg_polynomial_commitments.nim @@ -18,7 +18,7 @@ import ./commitments/kzg_polynomial_commitments, ./hashes, ./platforms/[abstractions, allocs], - ./serialization/[codecs_bls12_381, endians], + ./serialization/[codecs_status_codes, codecs_bls12_381, endians], ./trusted_setups/ethereum_kzg_srs export loadTrustedSetup, TrustedSetupStatus, EthereumKZGContext diff --git a/constantine/math/config/curves_declaration.nim b/constantine/math/config/curves_declaration.nim index 36bcf05..d4da292 100644 --- a/constantine/math/config/curves_declaration.nim +++ b/constantine/math/config/curves_declaration.nim @@ -155,6 +155,24 @@ declareCurves: coef_a: -5 coef_d: "6389c12633c267cbc66e3bf86be3b6d8cb66677177e54f92b369f2f5188d58e7" + curve Banderwagon: # Banderwagon is a prime subgroup constructed over the Bandersnatch Curve. + # https://hackmd.io/@6iQDuIePQjyYBqDChYw_jg/BJBNcv9fq + bitwidth: 255 + modulus: "0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001" + + # Weierstrass form: y² = x³ − 3763200000x − 7867596800000 + # Mongomery form: By² = x³ + Ax² + x + # B=0x300c3385d13bedb7c9e229e185c4ce8b1dd3b71366bb97c30855c0aa41d62727 + # A=0x4247698f4e32ad45a293959b4ca17afa4a2d2317e4c6ce5023e1f + # Twisted Edwards form: −5x² + y² = 1 + dx²y² + # d = 138827208126141220649022263972958607803 / 171449701953573178309673572579671231137 + order: "0x1cfb69d4ca675f520cce760202687600ff8f87007419047174fd06b52876e7e1" + orderBitwidth: 253 + cofactor: 4 + eq_form: TwistedEdwards + coef_a: -5 + coef_d: "6389c12633c267cbc66e3bf86be3b6d8cb66677177e54f92b369f2f5188d58e7" + curve Edwards25519: # Bernstein curve bitwidth: 255 modulus: "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed" diff --git a/constantine/math/config/curves_derived.nim b/constantine/math/config/curves_derived.nim index 98cd1d6..7429a1b 100644 --- a/constantine/math/config/curves_derived.nim +++ b/constantine/math/config/curves_derived.nim @@ -102,6 +102,13 @@ macro genDerivedConstants*(mode: static DerivedConstantMode): untyped = M ) ) + # const MyCurve_PrimeMinus1div2 = primeMinus1div2(MyCurve_Modulus) + result.add newConstStmt( + used(curve & ff & "_PrimeMinus1div2"), newCall( + bindSym"primeMinus1div2", + M + ) + ) # const MyCurve_PrimeMinus3div4_BE = primeMinus3div4_BE(MyCurve_Modulus) result.add newConstStmt( used(curve & ff & "_PrimeMinus3div4_BE"), newCall( diff --git a/constantine/math/config/curves_prop_field_derived.nim b/constantine/math/config/curves_prop_field_derived.nim index d99fcca..4f2992c 100644 --- a/constantine/math/config/curves_prop_field_derived.nim +++ b/constantine/math/config/curves_prop_field_derived.nim @@ -104,6 +104,11 @@ macro getPrimePlus1div2*(ff: type FF): untyped = ## Warning ⚠️: Result in canonical domain (not Montgomery) result = bindConstant(ff, "PrimePlus1div2") +macro getPrimeMinus1div2*(ff: type FF): untyped = + ## Get (P-1) / 2 for an odd prime + ## Warning ⚠️: Result in canonical domain (not Montgomery) + result = bindConstant(ff, "PrimeMinus1div2") + macro getPrimeMinus3div4_BE*(ff: type FF): untyped = ## Get (P-3) / 4 in big-endian serialized format result = bindConstant(ff, "PrimeMinus3div4_BE") diff --git a/constantine/math/config/precompute.nim b/constantine/math/config/precompute.nim index 3efda69..06e814a 100644 --- a/constantine/math/config/precompute.nim +++ b/constantine/math/config/precompute.nim @@ -392,6 +392,16 @@ func primePlus1div2*(P: BigInt): BigInt = let carry = result.add(1) doAssert not carry +func primeMinus1div2*(P: BigInt): BigInt = + ## Compute (P-1)/2 + ## For use in constant-time modular inversion + ## + ## Warning ⚠️: Result is in the canonical domain (not Montgomery) + + result = P + # discard result.sub(1) # right-shifting automatically implies "-1" for odd numbers (which all prime >2 are). + result.shiftRight(1) + func primeMinus3div4_BE*[bits: static int]( P: BigInt[bits] ): array[bits.ceilDiv_vartime(8), byte] {.noInit.} = diff --git a/constantine/math/constants/bandersnatch_generators.nim b/constantine/math/constants/bandersnatch_generators.nim new file mode 100644 index 0000000..1e8b160 --- /dev/null +++ b/constantine/math/constants/bandersnatch_generators.nim @@ -0,0 +1,23 @@ +# 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 + ../config/curves, + ../elliptic/ec_twistededwards_affine, + ../io/[io_fields, io_extfields] + +{.used.} + +# Generators +# ----------------------------------------------------------------- +# https://eprint.iacr.org/2021/1152.pdf + +const Bandersnatch_generator* = ECP_TwEdwards_Aff[Fp[Bandersnatch]]( + x: Fp[Bandersnatch].fromHex"0x29c132cc2c0b34c5743711777bbe42f32b79c022ad998465e1e71866a252ae18", + y: Fp[Bandersnatch].fromHex"0x2a6c669eda123e0f157d8b50badcd586358cad81eee464605e3167b6cc974166" +) \ No newline at end of file diff --git a/constantine/math/constants/banderwagon_generators.nim b/constantine/math/constants/banderwagon_generators.nim new file mode 100644 index 0000000..609bfbd --- /dev/null +++ b/constantine/math/constants/banderwagon_generators.nim @@ -0,0 +1,23 @@ +# 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 + ../config/curves, + ../elliptic/ec_twistededwards_affine, + ../io/[io_fields, io_extfields] + +{.used.} + +# Generators +# ----------------------------------------------------------------- +# https://eprint.iacr.org/2021/1152.pdf + +const Banderwagon_generator* = ECP_TwEdwards_Aff[Fp[Banderwagon]]( + x: Fp[Banderwagon].fromHex("29c132cc2c0b34c5743711777bbe42f32b79c022ad998465e1e71866a252ae18"), + y: Fp[Banderwagon].fromHex("2a6c669eda123e0f157d8b50badcd586358cad81eee464605e3167b6cc974166") +) \ No newline at end of file diff --git a/constantine/math/constants/banderwagon_sqrt.nim b/constantine/math/constants/banderwagon_sqrt.nim new file mode 100644 index 0000000..d7ced16 --- /dev/null +++ b/constantine/math/constants/banderwagon_sqrt.nim @@ -0,0 +1,19 @@ +# 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 + ../config/curves, + ../io/[io_bigints, io_fields], + ../arithmetic/finite_fields + +const + # with e = 2adicity + # p == s * 2^e + 1 + # root_of_unity = smallest_quadratic_nonresidue^s + # exponent = (p-1-2^e)/2^e / 2 + Banderwagon_TonelliShanks_exponent* = BigInt[222].fromHex"0x39f6d3a994cebea4199cec0404d0ec02a9ded2017fff2dff7fffffff" + Banderwagon_TonelliShanks_twoAdicity* = 32 + Banderwagon_TonelliShanks_root_of_unity* = Fp[Banderwagon].fromHex"0x212d79e5b416b6f0fd56dc8d168d6c0c4024ff270b3e0941b788f500b912f1f" diff --git a/constantine/math/constants/banderwagon_subgroups.nim b/constantine/math/constants/banderwagon_subgroups.nim new file mode 100644 index 0000000..73a2502 --- /dev/null +++ b/constantine/math/constants/banderwagon_subgroups.nim @@ -0,0 +1,42 @@ +# 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 + ../../platforms/abstractions, + ../config/curves, + ../arithmetic, + ../extension_fields, + ../elliptic/ec_twistededwards_projective + +# ############################################################ +# +# Subgroup Check +# +# ############################################################ + +func isInSubGroup*(P: ECP_TwEdwards_Prj[Fp[Banderwagon]]): SecretBool = + ## Checks if the point is in the quotient subgroup + ## The group law does not change because what we quotiented by was a subgroup. + ## These are still points on the bandersnatch curve and form a group under point addition. + ## + ## This is to be used to check if the point lies in the Banderwagon + ## while importing a point from serialized bytes + + var res{.noInit.}: typeof(P).F + var one{.noInit.}: typeof(P).F + + one.setOne() + res.setZero() + + ## Compute 1 - aX^2 and check its legendre symbol + res.prod(P.x, P.x) + res.prod(res, Banderwagon.getCoefA()) + res.neg(res) + res.sum(res, one) + + return res.isSquare() \ No newline at end of file diff --git a/constantine/math/constants/zoo_generators.nim b/constantine/math/constants/zoo_generators.nim index 951be8a..d7e58ce 100644 --- a/constantine/math/constants/zoo_generators.nim +++ b/constantine/math/constants/zoo_generators.nim @@ -10,7 +10,9 @@ import std/macros, ../config/curves, ./bls12_381_generators, - ./bn254_snarks_generators + ./bn254_snarks_generators, + ./bandersnatch_generators, + ./banderwagon_generators {.experimental: "dynamicbindsym".} diff --git a/constantine/math/constants/zoo_square_roots.nim b/constantine/math/constants/zoo_square_roots.nim index 5852ad0..1a41538 100644 --- a/constantine/math/constants/zoo_square_roots.nim +++ b/constantine/math/constants/zoo_square_roots.nim @@ -17,6 +17,7 @@ import ./curve25519_sqrt, ./jubjub_sqrt, ./bandersnatch_sqrt, + ./banderwagon_sqrt, ./pallas_sqrt, ./vesta_sqrt @@ -29,6 +30,7 @@ export curve25519_sqrt, jubjub_sqrt, bandersnatch_sqrt, + banderwagon_sqrt, pallas_sqrt, vesta_sqrt diff --git a/constantine/math/elliptic/ec_twistededwards_affine.nim b/constantine/math/elliptic/ec_twistededwards_affine.nim index 4644915..558eca3 100644 --- a/constantine/math/elliptic/ec_twistededwards_affine.nim +++ b/constantine/math/elliptic/ec_twistededwards_affine.nim @@ -39,6 +39,11 @@ func isInf*(P: ECP_TwEdwards_Aff): SecretBool = ## and false otherwise result = P.x.isZero() and P.y.isOne() +func setInf*(P: var ECP_TwEdwards_Aff) {.inline.} = + ## Set ``P`` to infinity + P.x.setZero() + P.y.setOne() + func isOnCurve*[F](x, y: F): SecretBool = ## Returns true if the (x, y) coordinates @@ -76,6 +81,51 @@ func isOnCurve*[F](x, y: F): SecretBool = t2 -= t0 return t2.isOne() +func trySetFromCoordX*[F](P: var ECP_TwEdwards_Aff[F], x: F): SecretBool = + ## Try to create a point on the elliptic curve from X co-ordinate + ## ax²+y²=1+dx²y² (affine coordinate) + ## + ## return true and update `P` if `y` leads to a valid point + ## return false otherwise, in that case `P` is undefined. + + # y² = (1 - ax²)/(1 - dx²) + var t {.noInit.}: F + var one {.noInit.}: F + one.setOne() + + # (1 - dx²) + t.square(x) + when F.C.getCoefD() is int: + when F.C.getCoefD() >= 0: + P.y.fromUint uint F.C.getCoefD() + else: + P.y.fromUint uint -F.C.getCoefD() + P.y.neg() + else: + P.y = F.C.getCoefD() + P.y *= t + P.y.neg() + P.y += one + + # (1 - ax²) + when F.C.getCoefA() is int: + when F.C.getCoefA() >= 0: + P.x.fromUint uint F.C.getCoefA() + else: + P.x.fromUint uint -F.C.getCoefA() + P.x.neg() + else: + P.x = F.C.getCoefA() + P.x *= t + P.x.neg() + P.x += one + + # √((1 - ax²)/(1 - dx²)) + result = sqrt_ratio_if_square(t, P.x, P.y) + P.y = t + P.x = x + + func trySetFromCoordY*[F](P: var ECP_TwEdwards_Aff[F], y: F): SecretBool = ## Try to create a point the elliptic curve ## ax²+y²=1+dx²y² (affine coordinate) @@ -147,3 +197,30 @@ func cneg*(P: var ECP_TwEdwards_Aff, ctl: CTBool) = ## Conditional negation. ## Negate if ``ctl`` is true P.x.cneg(ctl) + +# ############################################################ +# +# Banderwagon Specific Operations +# +# ############################################################ + +func `==`*(P, Q: ECP_TwEdwards_Aff[Fp[Banderwagon]]): SecretBool = + ## Equality check for points in the Banderwagon Group + ## The equality check is optimized for the quotient group + ## see: https://hackmd.io/@6iQDuIePQjyYBqDChYw_jg/BJBNcv9fq#Equality-check + ## + ## Check for the (0,0) point, which is possible + ## + ## This is a costly operation + + var lhs{.noInit.}, rhs{.noInit.}: typeof(P).F + + # Check for the zero points + result = not(P.x.is_zero() and P.y.is_zero()) + result = result or not(Q.x.is_zero() and Q.y.is_zero()) + + ## Check for the equality of the points + ## X1 * Y2 == X2 * Y1 + lhs.prod(P.x, Q.y) + rhs.prod(Q.x, P.y) + result = result and lhs == rhs diff --git a/constantine/math/elliptic/ec_twistededwards_projective.nim b/constantine/math/elliptic/ec_twistededwards_projective.nim index 7bb3214..a1f7aac 100644 --- a/constantine/math/elliptic/ec_twistededwards_projective.nim +++ b/constantine/math/elliptic/ec_twistededwards_projective.nim @@ -13,6 +13,7 @@ import ../extension_fields, ./ec_twistededwards_affine + # ############################################################ # # Elliptic Curve in Twisted Edwards form @@ -65,6 +66,25 @@ func ccopy*(P: var ECP_TwEdwards_Prj, Q: ECP_TwEdwards_Prj, ctl: SecretBool) {.i for fP, fQ in fields(P, Q): ccopy(fP, fQ, ctl) +func trySetFromCoordX*[F]( + P: var ECP_TwEdwards_Prj[F], + x: F): SecretBool = + ## Try to create a point on the elliptic curve from X co-ordinate + ## ax²+y²=1+dx²y² (affine coordinate) + ## + ## The `Z` coordinates is set to 1 + ## + ## return true and update `P` if `y` leads to a valid point + ## return false otherwise, in that case `P` is undefined. + + var Q{.noInit.}: ECP_TwEdwards_Aff[F] + result = Q.trySetFromCoordX(x) + + P.x = Q.x + P.y = Q.y + P.z.setOne() + + func trySetFromCoordY*[F]( P: var ECP_TwEdwards_Prj[F], y: F): SecretBool = @@ -259,11 +279,11 @@ func double*[Field]( # (B-C-D) => 2X1Y1, but With squaring and 2 substractions instead of mul + addition # In practice, squaring is not cheap enough to compasate the extra substraction cost. + E.square(P.x) r.x.prod(P.x, P.y) r.x.double() D.square(P.y) - E.square(P.x) E *= Field.C.getCoefA() r.y.sum(E, D) # Ry stores F = E+D @@ -293,6 +313,14 @@ func diff*(r: var ECP_TwEdwards_Prj, nQ.neg(Q) r.sum(P, nQ) +template affine*[F](_: type ECP_TwEdwards_Prj[F]): typedesc = + ## Returns the affine type that corresponds to the Jacobian type input + ECP_TwEdwards_Aff[F] + +template projective*[F](_: type ECP_TwEdwards_Aff[F]): typedesc = + ## Returns the projective type that corresponds to the affine type input + ECP_TwEdwards_Aff[F] + func affine*[F]( aff: var ECP_TwEdwards_Aff[F], proj: ECP_TwEdwards_Prj[F]) = @@ -302,9 +330,36 @@ func affine*[F]( aff.x.prod(proj.x, invZ) aff.y.prod(proj.y, invZ) -func projective*[F]( - proj: var ECP_TwEdwards_Aff[F], - aff: ECP_TwEdwards_Prj[F]) {.inline.} = +func fromAffine*[F]( + proj: var ECP_TwEdwards_Prj[F], + aff: ECP_TwEdwards_Aff[F]) {.inline.} = proj.x = aff.x proj.y = aff.y proj.z.setOne() + +# ############################################################ +# +# Banderwagon Specific Operations +# +# ############################################################ + +func `==`*(P, Q: ECP_TwEdwards_Prj[Fp[Banderwagon]]): SecretBool = + ## Equality check for points in the Banderwagon Group + ## The equality check is optimized for the quotient group + ## see: https://hackmd.io/@6iQDuIePQjyYBqDChYw_jg/BJBNcv9fq#Equality-check + ## + ## Check for the (0,0) point, which is possible + ## + ## This is a costly operation + + var lhs{.noInit.}, rhs{.noInit.}: typeof(P).F + + # Check for the zero points + result = not(P.x.is_zero() and P.y.is_zero()) + result = result or not(Q.x.is_zero() and Q.y.is_zero()) + + ## Check for the equality of the points + ## X1 * Y2 == X2 * Y1 + lhs.prod(P.x, Q.y) + rhs.prod(Q.x, P.y) + result = result and lhs == rhs \ No newline at end of file diff --git a/constantine/serialization/codecs_banderwagon.nim b/constantine/serialization/codecs_banderwagon.nim new file mode 100644 index 0000000..ea6c918 --- /dev/null +++ b/constantine/serialization/codecs_banderwagon.nim @@ -0,0 +1,113 @@ +# 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. + +## ############################################################ +## +## Banderwagon Serialization +## +## ############################################################ + +import + ../platforms/abstractions, + ../math/config/curves, + ../math/elliptic/[ + ec_twistededwards_affine, + ec_twistededwards_projective + ], + ../math/[ + extension_fields, + arithmetic, + constants/banderwagon_subgroups + ], + ../math/io/[io_bigints, io_fields], + ./codecs_status_codes + +type + EC_Prj* = ECP_TwEdwards_Prj[Fp[Banderwagon]] + EC_Aff* = ECP_TwEdwards_Aff[Fp[Banderwagon]] + +func serialize*(dst: var array[32, byte], P: EC_Prj): CttCodecEccStatus = + ## Serialize a Banderwagon point(x, y) in the format + ## + ## serialize = bigEndian( sign(y) * x ) + ## If y is not lexicographically largest + ## set x -> -x + ## then serialize + ## + ## Returns cttCodecEcc_Success if successful + ## Spec: https://hackmd.io/@6iQDuIePQjyYBqDChYw_jg/BJBNcv9fq#Serialisation + + # Setting all bits to 0 for the point of infinity + if P.isInf().bool(): + for i in 0 ..< dst.len: + dst[i] = byte 0 + return cttCodecEcc_Success + + # Convert the projective points into affine format before encoding + var aff {.noInit.}: EC_Aff + aff.affine(P) + + let lexicographicallyLargest = aff.y.toBig() >= Fp[Banderwagon].getPrimeMinus1div2() + + if not lexicographicallyLargest.bool(): + aff.x.neg() + + dst.marshal(aff.x, bigEndian) + return cttCodecEcc_Success + +func deserialize_unchecked*(dst: var EC_Prj, src: array[32, byte]): CttCodecEccStatus = + ## Deserialize a Banderwagon point (x, y) in format + ## + ## if y is not lexicographically largest + ## set y -> -y + ## + ## Returns cttCodecEcc_Success if successful + ## https://hackmd.io/@6iQDuIePQjyYBqDChYw_jg/BJBNcv9fq#Serialisation + # If infinity, src must be all zeros + var check: bool = true + for i in 0 ..< src.len: + if src[i] != byte 0: + check = false + break + if check: + dst.setInf() + return cttCodecEcc_PointAtInfinity + + var t{.noInit.}: matchingBigInt(Banderwagon) + t.unmarshal(src, bigEndian) + + if bool(t >= Banderwagon.Mod()): + return cttCodecEcc_CoordinateGreaterThanOrEqualModulus + + var x{.noInit.}: Fp[Banderwagon] + x.fromBig(t) + + let onCurve = dst.trySetFromCoordX(x) + if not(bool onCurve): + return cttCodecEcc_PointNotOnCurve + + let isLexicographicallyLargest = dst.y.toBig() >= Fp[Banderwagon].getPrimeMinus1div2() + dst.y.cneg(not isLexicographicallyLargest) + + return cttCodecEcc_Success + +func deserialize*(dst: var EC_Prj, src: array[32, byte]): CttCodecEccStatus = + ## Deserialize a Banderwagon point (x, y) in format + ## + ## Also checks if the point lies in the banderwagon scheme subgroup + ## + ## Returns cttCodecEcc_Success if successful + ## Returns cttCodecEcc_PointNotInSubgroup if doesn't lie in subgroup + result = deserialize_unchecked(dst, src) + if result != cttCodecEcc_Success: + return result + + if not(bool dst.isInSubgroup()): + return cttCodecEcc_PointNotInSubgroup + + return cttCodecEcc_Success \ No newline at end of file diff --git a/constantine/serialization/codecs_bls12_381.nim b/constantine/serialization/codecs_bls12_381.nim index f37d42f..c139f36 100644 --- a/constantine/serialization/codecs_bls12_381.nim +++ b/constantine/serialization/codecs_bls12_381.nim @@ -45,22 +45,11 @@ import extension_fields, arithmetic, constants/zoo_subgroups], - ../math/io/[io_bigints, io_fields] + ../math/io/[io_bigints, io_fields], + ./codecs_status_codes type - CttCodecScalarStatus* = enum - cttCodecScalar_Success - cttCodecScalar_Zero - cttCodecScalar_ScalarLargerThanCurveOrder - - CttCodecEccStatus* = enum - cttCodecEcc_Success - cttCodecEcc_InvalidEncoding - cttCodecEcc_CoordinateGreaterThanOrEqualModulus - cttCodecEcc_PointNotOnCurve - cttCodecEcc_PointNotInSubgroup - cttCodecEcc_PointAtInfinity - + Scalar* = matchingOrderBigInt(BLS12_381) G1P* = ECP_ShortW_Aff[Fp[BLS12_381], G1] G2P* = ECP_ShortW_Aff[Fp2[BLS12_381], G2] diff --git a/constantine/serialization/codecs_status_codes.nim b/constantine/serialization/codecs_status_codes.nim new file mode 100644 index 0000000..4a1fae5 --- /dev/null +++ b/constantine/serialization/codecs_status_codes.nim @@ -0,0 +1,21 @@ +# 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. + +type + CttCodecScalarStatus* = enum + cttCodecScalar_Success + cttCodecScalar_Zero + cttCodecScalar_ScalarLargerThanCurveOrder + + CttCodecEccStatus* = enum + cttCodecEcc_Success + cttCodecEcc_InvalidEncoding + cttCodecEcc_CoordinateGreaterThanOrEqualModulus + cttCodecEcc_PointNotOnCurve + cttCodecEcc_PointNotInSubgroup + cttCodecEcc_PointAtInfinity \ No newline at end of file diff --git a/tests/math_elliptic_curves/t_ec_template.nim b/tests/math_elliptic_curves/t_ec_template.nim index 7551607..792044f 100644 --- a/tests/math_elliptic_curves/t_ec_template.nim +++ b/tests/math_elliptic_curves/t_ec_template.nim @@ -95,7 +95,7 @@ proc run_EC_addition_tests*( const testSuiteDesc = "Elliptic curve in " & $ec.F.C.getEquationForm() & " form" suite testSuiteDesc & " - " & $ec & " - [" & $WordBitWidth & "-bit mode]": - test "The infinity point is the neutral element w.r.t. to EC " & $ec.G & " addition": + test "The infinity point is the neutral element w.r.t. to EC " & " addition": proc test(EC: typedesc, randZ: bool, gen: RandomGen) = var inf {.noInit.}: EC inf.setInf() @@ -182,7 +182,7 @@ proc run_EC_addition_tests*( test(ec, randZ = false, gen = Long01Sequence) test(ec, randZ = true, gen = Long01Sequence) - test "EC " & $ec.G & " add is commutative": + test "EC" & " add is commutative": proc test(EC: typedesc, randZ: bool, gen: RandomGen) = for _ in 0 ..< Iters: var r0{.noInit.}, r1{.noInit.}: EC @@ -200,7 +200,7 @@ proc run_EC_addition_tests*( test(ec, randZ = false, gen = Long01Sequence) test(ec, randZ = true, gen = Long01Sequence) - test "EC " & $ec.G & " add is associative": + test "EC" & " add is associative": proc test(EC: typedesc, randZ: bool, gen: RandomGen) = for _ in 0 ..< Iters: let a = rng.random_point(EC, randZ, gen) @@ -249,7 +249,7 @@ proc run_EC_addition_tests*( test(ec, randZ = false, gen = Long01Sequence) test(ec, randZ = true, gen = Long01Sequence) - test "EC " & $ec.G & " double and EC " & $ec.G & " add are consistent": + test "EC " & " double and EC " & " add are consistent": proc test(EC: typedesc, randZ: bool, gen: RandomGen) = for _ in 0 ..< Iters: let a = rng.random_point(EC, randZ, gen) @@ -469,7 +469,7 @@ proc run_EC_mul_sanity_tests*( const testSuiteDesc = "Elliptic curve in " & $ec.F.C.getEquationForm() & " form" suite testSuiteDesc & " - " & $ec & " - [" & $WordBitWidth & "-bit mode]": - test "EC " & $ec.G & " mul [0]P == Inf": + test "EC " & " mul [0]P == Inf": proc test(EC: typedesc, bits: static int, randZ: bool, gen: RandomGen) = for _ in 0 ..< ItersMul: let a = rng.random_point(EC, randZ, gen) @@ -506,7 +506,7 @@ proc run_EC_mul_sanity_tests*( test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false, gen = Long01Sequence) test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true, gen = Long01Sequence) - test "EC " & $ec.G & " mul [1]P == P": + test "EC " & " mul [1]P == P": proc test(EC: typedesc, bits: static int, randZ: bool, gen: RandomGen) = for _ in 0 ..< ItersMul: let a = rng.random_point(EC, randZ, gen) @@ -532,7 +532,7 @@ proc run_EC_mul_sanity_tests*( test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false, gen = Long01Sequence) test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true, gen = Long01Sequence) - test "EC " & $ec.G & " mul [2]P == P.double()": + test "EC " & " mul [2]P == P.double()": proc test(EC: typedesc, bits: static int, randZ: bool, gen: RandomGen) = for _ in 0 ..< ItersMul: let a = rng.random_point(EC, randZ, gen) @@ -575,7 +575,7 @@ proc run_EC_mul_distributive_tests*( suite testSuiteDesc & " - " & $ec & " - [" & $WordBitWidth & "-bit mode]": - test "EC " & $ec.G & " mul is distributive over EC add": + test "EC" & " mul is distributive over EC add": proc test(EC: typedesc, bits: static int, randZ: bool, gen: RandomGen) = for _ in 0 ..< ItersMul: let a = rng.random_point(EC, randZ, gen) diff --git a/tests/math_elliptic_curves/t_ec_twedwards_prj_add_double.nim b/tests/math_elliptic_curves/t_ec_twedwards_prj_add_double.nim index 4da74e1..42d3a89 100644 --- a/tests/math_elliptic_curves/t_ec_twedwards_prj_add_double.nim +++ b/tests/math_elliptic_curves/t_ec_twedwards_prj_add_double.nim @@ -32,4 +32,10 @@ run_EC_addition_tests( ec = ECP_TwEdwards_Prj[Fp[Bandersnatch]], Iters = Iters, moduleName = "test_ec_twistededwards_projective_add_double_" & $Bandersnatch + ) + +run_EC_addition_tests( + ec = ECP_TwEdwards_Prj[Fp[Banderwagon]], + Iters = Iters, + moduleName = "test_ec_twistededwards_projective_add_double_" & $Banderwagon ) \ No newline at end of file diff --git a/tests/math_elliptic_curves/t_ec_twedwards_prj_mul_distri.nim b/tests/math_elliptic_curves/t_ec_twedwards_prj_mul_distri.nim index 92161c2..173994e 100644 --- a/tests/math_elliptic_curves/t_ec_twedwards_prj_mul_distri.nim +++ b/tests/math_elliptic_curves/t_ec_twedwards_prj_mul_distri.nim @@ -34,3 +34,9 @@ run_EC_mul_distributive_tests( ItersMul = ItersMul, moduleName = "test_ec_twistededwards_projective_mul_distributive_" & $Bandersnatch ) + +run_EC_mul_distributive_tests( + ec = ECP_TwEdwards_Prj[Fp[Banderwagon]], + ItersMul = ItersMul, + moduleName = "test_ec_twistededwards_projective_mul_distributive_" & $Banderwagon + ) \ No newline at end of file diff --git a/tests/t_banderwagon.nim b/tests/t_banderwagon.nim new file mode 100644 index 0000000..9c48d07 --- /dev/null +++ b/tests/t_banderwagon.nim @@ -0,0 +1,209 @@ +# 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 + std/unittest, + ../constantine/math/config/[type_ff, curves], + ../constantine/math/elliptic/[ + ec_twistededwards_affine, + ec_twistededwards_projective + ], + ../constantine/math/io/io_fields, + ../constantine/serialization/[ + codecs_status_codes, + codecs_banderwagon, + codecs + ], + ../constantine/math/arithmetic, + ../constantine/math/constants/zoo_generators + +type + EC* = ECP_TwEdwards_Prj[Fp[Banderwagon]] + Bytes* = array[32, byte] + +# The generator point from Banderwagon +var generator = Banderwagon.getGenerator() + +# serialized points which lie on Banderwagon +const expected_bit_strings: array[16, string] = [ + "0x4a2c7486fd924882bf02c6908de395122843e3e05264d7991e18e7985dad51e9", + "0x43aa74ef706605705989e8fd38df46873b7eae5921fbed115ac9d937399ce4d5", + "0x5e5f550494159f38aa54d2ed7f11a7e93e4968617990445cc93ac8e59808c126", + "0x0e7e3748db7c5c999a7bcd93d71d671f1f40090423792266f94cb27ca43fce5c", + "0x14ddaa48820cb6523b9ae5fe9fe257cbbd1f3d598a28e670a40da5d1159d864a", + "0x6989d1c82b2d05c74b62fb0fbdf8843adae62ff720d370e209a7b84e14548a7d", + "0x26b8df6fa414bf348a3dc780ea53b70303ce49f3369212dec6fbe4b349b832bf", + "0x37e46072db18f038f2cc7d3d5b5d1374c0eb86ca46f869d6a95fc2fb092c0d35", + "0x2c1ce64f26e1c772282a6633fac7ca73067ae820637ce348bb2c8477d228dc7d", + "0x297ab0f5a8336a7a4e2657ad7a33a66e360fb6e50812d4be3326fab73d6cee07", + "0x5b285811efa7a965bd6ef5632151ebf399115fcc8f5b9b8083415ce533cc39ce", + "0x1f939fa2fd457b3effb82b25d3fe8ab965f54015f108f8c09d67e696294ab626", + "0x3088dcb4d3f4bacd706487648b239e0be3072ed2059d981fe04ce6525af6f1b8", + "0x35fbc386a16d0227ff8673bc3760ad6b11009f749bb82d4facaea67f58fc60ed", + "0x00f29b4f3255e318438f0a31e058e4c081085426adb0479f14c64985d0b956e0", + "0x3fa4384b2fa0ecc3c0582223602921daaa893a97b64bdf94dcaa504e8b7b9e5f", +] + +## These are all points which will be shown to be on the curve +## but are not in the correct subgroup +const bad_bit_string: array[16, string] = [ + "0x1b6989e2393c65bbad7567929cdbd72bbf0218521d975b0fb209fba0ee493c32", + "0x280e608d5bbbe84b16aac62aa450e8921840ea563f1c9c266e0240d89cbe6a78", + "0x31468782818807366dbbcd20b9f10f0d5b93f22e33fe49b450dfbddaf3ba6a9b", + "0x6bfc4097e4874cdddebe74e041fcd329d8455278cd42b6dd4f40b042d4fc466b", + "0x65dc0a9730cce485d82b230ce32c7c21688967c8943b4a51ba468f927e2e28ef", + "0x0fd3536157199b46617c3fba4bae1c2ffab5409dfea1de62161bc10748651671", + "0x5bdc73f43e90ae5c2956320ce2ef2b17809b11d6b9758c7861793b41f39b7c01", + "0x23a89c778ee10b9925ad3df5dc1f7ab244c1daf305669bc6b03d1aaa100037a4", + "0x67505814852867356aaa8387896efa1d1b9a72aad95549e53e69c15eb36a642c", + "0x301bc9b1129a727c2a65b96f55a5bcd642a3d37e0834196863c4430e4281dc3a", + "0x45d08715ac67ebb088bcfa3d04bcce76510edeb9e23f12ed512894ba1e6518fc", + "0x0b3b6e1f8ec72e63c6aa7ae87628071df3d82ea2bea6516d1948dac2edc12179", + "0x72430a05f507747aa5a42481b4f93522aa682b1d56e5285f089aa1b5fb09c67a", + "0x5eb4d3e5ce8107c6dd7c6398f2a903a0df75ce655939c29a3e309f43fe5bcd1f", + "0x6671109a7a15f4852ead3298318595a36010930fddbd3c8f667c6390e7ac3c66", + "0x120faa1df94d5d831bbb69fc44816e25afd27288a333299ac3c94518fd0e016f", +] + +# ############################################################ +# +# Banderwagon Serialization Tests +# +# ############################################################ +suite "Banderwagon Serialization Tests": + var points: seq[EC] + + ## Check encoding if it is as expected or not + test "Test Encoding from Fixed Vectors": + proc testSerialize(len: int) = + # First the point is set to generator P + # then with each iteration 2P, 4P, . . . doubling + var point {.noInit.}: EC + point.fromAffine(generator) + + for i in 0 ..< len: + var arr: Bytes + let stat = arr.serialize(point) + + # Check if the serialization took place and in expected way + doAssert stat == cttCodecEcc_Success, "Serialization Failed" + doAssert expected_bit_strings[i] == arr.toHex(), "bit string does not match expected" + points.add(point) + + point.double() #doubling the point + + testSerialize(expected_bit_strings.len) + + ## Check decoding if it is as expected or not + test "Decoding Each bit string": + proc testDeserialization(len: int) = + # Checks if the point serialized in the previous + # tests matches with the deserialization of expected strings + for i, bit_string in expected_bit_strings: + + # converts serialized value in hex to byte array + var arr: Bytes + arr.fromHex(bit_string) + + # deserialization from expected bits + var point{.noInit.}: EC + let stat = point.deserialize(arr) + + # Assertion check for the Deserialization Success & correctness + doAssert stat == cttCodecEcc_Success, "Deserialization Failed" + doAssert (point == points[i]).bool(), "Decoded Element is different from expected element" + + testDeserialization(expected_bit_strings.len) + + # Check if the subgroup check is working on eliminating + # points which don't lie on banderwagon, while + # deserializing from an untrusted source + test "Decoding Points Not on Curve": + proc testBadPointDeserialization(len: int) = + # Checks whether the bad bit string + # get deserialized, it should return error -> cttCodecEcc_PointNotInSubgroup + for bit_string in bad_bit_string: + + # converts serialized value in hex to byte array + var arr: Bytes + arr.fromHex(bit_string) + + # deserialization from bits + var point{.noInit.}: EC + let stat = point.deserialize(arr) + + # Assertion check for error + doAssert stat == cttCodecEcc_PointNotInSubgroup, "Bad point Deserialization Failed, in subgroup check" + + testBadPointDeserialization(bad_bit_string.len) + + +# ############################################################ +# +# Banderwagon Point Operations Tests +# +# ############################################################ +suite "Banderwagon Points Tests": + + ## Tests if the operation are consistent & correct + ## consistency of Addition with doubling + ## and correctness of the subtraction + test "Test for Addition, Subtraction, Doubling": + proc testAddSubDouble() = + var a, b, gen_point, identity {.noInit.} : EC + gen_point.fromAffine(generator) + + # Setting the identity Element + identity.x.setZero() + identity.y.setOne() + identity.z.setOne() + + a.sum(gen_point, gen_point) # a = g+g = 2g + b.double(gen_point) # b = 2g + + doAssert (not (a == gen_point).bool()), "The generator should not have order < 2" + doAssert (a == b).bool(), "Add and Double formulae do not match" # Checks is doubling and addition are consistent + + a.diff(a, b) # a <- a - b + doAssert (a == identity).bool(), "Sub formula is incorrect; any point minus itself should give the identity point" + + testAddSubDouble() + + ## Points that differ by a two torsion point + ## are equal, where the two torsion point is not the point at infinity + test "Test Two Torsion Equality": + proc testTwoTorsion() = + var two_torsion: EC + + # Setting the two torsion point + two_torsion.x.setZero() + two_torsion.y.setMinusOne() + two_torsion.z.setOne() + + var point{.noInit.}: EC + point.fromAffine(generator) + + for i in 0 ..< 1000: + var point_plus_torsion: EC + point_plus_torsion.sum(point, two_torsion) # adding generator with two torsion point + + doAssert (point == point_plus_torsion).bool(), "points that differ by an order-2 point should be equal" + + # Serializing to the point and point added with two torsion point + var point_bytes: Bytes + let stat1 = point_bytes.serialize(point) + var plus_point_bytes: Bytes + let stat2 = plus_point_bytes.serialize(point_plus_torsion) + + doAssert stat1 == cttCodecEcc_Success and stat2 == cttCodecEcc_Success, "Serialization Failed" + doAssert plus_point_bytes == point_bytes, "points that differ by an order-2 point should produce the same bit string" + + point.double() + + testTwoTorsion() +