diff --git a/groth16.nimble b/groth16.nimble index b5510f6..8c74b38 100644 --- a/groth16.nimble +++ b/groth16.nimble @@ -8,6 +8,7 @@ binDir = "build" namedBin = {"cli/cli_main": "nim-groth16"}.toTable() installExt = @["nim"] -requires "nim >= 2.0.0" +requires "nim >= 2.2.0" requires "https://github.com/status-im/nim-taskpools >= 0.0.5" -requires "https://github.com/mratsim/constantine#bc3845aa492b52f7fef047503b1592e830d1a774" +requires "https://github.com/mratsim/constantine" +# requires "https://github.com/mratsim/constantine#bc3845aa492b52f7fef047503b1592e830d1a774" diff --git a/groth16/bn128/curves.nim b/groth16/bn128/curves.nim index 5a39813..f162875 100644 --- a/groth16/bn128/curves.nim +++ b/groth16/bn128/curves.nim @@ -1,5 +1,5 @@ # -# the `alt-bn128` elliptic curve +# the `BN254` (aka `alt-bn128`) elliptic curve # # See for example # @@ -53,8 +53,14 @@ func unsafeMkG2* ( X, Y: Fp2[BN254_Snarks] ) : G2 = #------------------------------------------------------------------------------- -const infG1* : G1 = unsafeMkG1( zeroFp , zeroFp ) -const infG2* : G2 = unsafeMkG2( zeroFp2 , zeroFp2 ) +const infG1* : G1 = unsafeMkG1( zeroFp , zeroFp ) +const infG2* : G2 = unsafeMkG2( zeroFp2 , zeroFp2 ) + +func isInfG1*(pt : G1): bool = bool(isNeutral(pt)) +func isInfG2*(pt : G2): bool = bool(isNeutral(pt)) + +func isInfProjG1*(pt : ProjG1): bool = bool(isNeutral(pt)) +func isInfProjG2*(pt : ProjG2): bool = bool(isNeutral(pt)) #------------------------------------------------------------------------------- @@ -73,6 +79,10 @@ func checkCurveEqG1*( x, y: Fp[BN254_Snarks] ) : bool = # echo("eq = ",toDecimalFp(eq)) return (bool(isZero(eq))) +# note: for BN254, the G1 is the whole curve. This is however not true for other curves like BLS12-381! +func checkSubgroupG1*( x, y: Fp[BN254_Snarks] ) : bool = + return checkCurveEqG1(x,y) + #--------------------------------------- # y^2 = x^3 + B @@ -97,20 +107,48 @@ func checkCurveEqG2*( x, y: Fp2[BN254_Snarks] ) : bool = eq -= y2 return isZeroFp2(eq) +# both just fits into 254 bits +const G2_cofactor: BigInt[254] = fromHex( BigInt[254] , "0x30644e72e131a029b85045b68181585e06ceecda572a2489345f2299c0f9fa8d" , bigEndian ) +const GroupOrder : BigInt[254] = fromHex( BigInt[254] , "0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001" , bigEndian ) + +# checks that the point is in the right subgroup +func checkSubgroupG2*( x, y: Fp2[BN254_Snarks] ) : bool = + + if not checkCurveEqG2(x,y): + # reject if not on the curve + return false + else: + # multiply by the order. Note: this is slow + # TODO: implement + let point = unsafeMkG2(x,y) + var q : ProjG2 + prj.fromAffine( q , point ) + scl.scalarMul_vartime( q , GroupOrder ) + return bool(isNeutral(q)) + # var r : G2 + # prj.affine( r, q ) + #------------------------------------------------------------------------------- func mkG1*( x, y: Fp[BN254_Snarks] ) : G1 = if isZeroFp(x) and isZeroFp(y): return infG1 else: - assert( checkCurveEqG1(x,y) , "mkG1: not a G1 curve point" ) + assert( checkCurveEqG1(x,y) , "mkG1: not a G1 group point (in case of BN254, this is the same a curve point)" ) return unsafeMkG1(x,y) +func mkCurve2*( x, y: Fp2[BN254_Snarks] ) : G2 = + if isZeroFp2(x) and isZeroFp2(y): + return infG2 + else: + assert( checkCurveEqG2(x,y) , "mkCurve2: not a curve point on the curve over the extended field" ) + return unsafeMkG2(x,y) + func mkG2*( x, y: Fp2[BN254_Snarks] ) : G2 = if isZeroFp2(x) and isZeroFp2(y): return infG2 else: - assert( checkCurveEqG2(x,y) , "mkG2: not a G2 curve point" ) + assert( checkSubgroupG2(x,y) , "mkG2: not a G2 group point" ) return unsafeMkG2(x,y) #------------------------------------------------------------------------------- @@ -132,12 +170,18 @@ const gen2* : G2 = unsafeMkG2( gen2_x, gen2_y ) #------------------------------------------------------------------------------- -func isOnCurveG1* ( p: G1 ) : bool = +func isOnCurve1* ( p: G1 ) : bool = return checkCurveEqG1( p.x, p.y ) -func isOnCurveG2* ( p: G2 ) : bool = +func isOnCurve2* ( p: G2 ) : bool = return checkCurveEqG2( p.x, p.y ) +func isInSubgroupG1* ( p: G1 ) : bool = + return checkSubgroupG1( p.x, p.y ) + +func isInSubgroupG2* ( p: G2 ) : bool = + return checkSubgroupG2( p.x, p.y ) + #=============================================================================== func addG1*(p,q: G1): G1 = @@ -229,11 +273,33 @@ func pairing* (p: G1, q: G2) : Fp12[BN254_Snarks] = #------------------------------------------------------------------------------- -proc sanityCheckGroupGen*() = - echo( "gen1 on the curve = ", checkCurveEqG1(gen1.x,gen1.y) ) - echo( "gen2 on the curve = ", checkCurveEqG2(gen2.x,gen2.y) ) - # TODO: fix compilation error with Constantine 0.2.0: - # echo( "order of gen1 is R = ", (not bool(isNeutral(gen1))) and bool(isNeutral(primeR ** gen1)) ) - # echo( "order of gen2 is R = ", (not bool(isNeutral(gen2))) and bool(isNeutral(primeR ** gen2)) ) +#[ +proc sanityCheckGroupGens*() = + echo( "gen1 on the curve = ", checkCurveEqG1(gen1.x,gen1.y) ) + echo( "gen2 on the curve = ", checkCurveEqG2(gen2.x,gen2.y) ) + echo( "gen2 is in the subgroup = ", checkSubgroupG2(gen2.x,gen2.y) ) + + let primeR : BigInt[254] = fromHex( BigInt[254], "0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001", bigEndian ) + echo( "order of gen1 is R = ", (not bool(isNeutral(gen1))) and bool(isNeutral(primeR ** gen1)) ) + echo( "order of gen2 is R = ", (not bool(isNeutral(gen2))) and bool(isNeutral(primeR ** gen2)) ) + +# +# the point (computed via Sage) +# +# (2 : 2237046587054574173616397632856518880513033439888792180868262182050662989363*u + 10894412225134874879786325788974416805327887441035008073952212076423500941133 : 1) +# +# should be on the curve but not in the subgroup +# +proc sanityCheckInSubgroupG2*() = + let pt2_x1 = fromHex(Fp[BN254_Snarks], "0x2") + let pt2_xu = fromHex(Fp[BN254_Snarks], "0x0") + let pt2_y1 = fromHex(Fp[BN254_Snarks], "0x181604d0560080401c08b557815482553e278257d98100d193a011c42782474d") + let pt2_yu = fromHex(Fp[BN254_Snarks], "0x04f21f9d99cc25f694cf22ff70dc0ac4692e7a721b725dc454a217f04bd03e33") + let pt2_x = mkFp2( pt2_x1, pt2_xu ) + let pt2_y = mkFp2( pt2_y1, pt2_yu ) + echo("pt2 is on the curve (should be true ) = " , checkCurveEqG2(pt2_x, pt2_y) ) + echo("pt2 is in the subgroup (should be false) = " , checkSubgroupG2(pt2_x, pt2_y) ) + +]# #------------------------------------------------------------------------------- diff --git a/groth16/bn128/msm.nim b/groth16/bn128/msm.nim index 4ee7c3d..b471e01 100644 --- a/groth16/bn128/msm.nim +++ b/groth16/bn128/msm.nim @@ -7,7 +7,7 @@ import system import taskpools import constantine/platforms/abstractions except Subgroup -import constantine/math/endomorphisms/frobenius except Subgroup +# import constantine/math/endomorphisms/frobenius except Subgroup import constantine/math/io/io_bigints import constantine/named/properties_fields except Subgroup diff --git a/groth16/verifier.nim b/groth16/verifier.nim index d60be47..091d34b 100644 --- a/groth16/verifier.nim +++ b/groth16/verifier.nim @@ -33,9 +33,9 @@ proc verifyProof* (vkey: VKey, prf: Proof): bool = assert( prf.curve == "bn128" ) - assert( isOnCurveG1(prf.pi_a) , "pi_a is not in G1" ) - assert( isOnCurveG2(prf.pi_b) , "pi_b is not in G2" ) - assert( isOnCurveG1(prf.pi_c) , "pi_c is not in G1" ) + assert( isInSubgroupG1(prf.pi_a) , "pi_a is not in G1" ) + assert( isInSubgroupG2(prf.pi_b) , "pi_b is not in G2" ) + assert( isInSubgroupG1(prf.pi_c) , "pi_c is not in G1" ) var pubG1 : G1 = msmG1( prf.publicIO , vkey.vpoints.pointsIC ) diff --git a/tests/groth16/testCurve.nim b/tests/groth16/testCurve.nim new file mode 100644 index 0000000..9b2f489 --- /dev/null +++ b/tests/groth16/testCurve.nim @@ -0,0 +1,60 @@ + +{.used.} + +import std/unittest + +import constantine/math/io/io_bigints +import constantine/math/arithmetic +import constantine/math/io/io_fields +import constantine/named/properties_fields +import constantine/math/extension_fields/towers + +import groth16/bn128/fields +import groth16/bn128/curves + +#------------------------------------------------------------------------------- + +# +# the point (computed via Sage) +# +# pt2 = (2 : 2237046587054574173616397632856518880513033439888792180868262182050662989363*u + 10894412225134874879786325788974416805327887441035008073952212076423500941133 : 1) +# +# should be on the curve but not in the subgroup +# + +const pt2_x1 = fromHex(Fp[BN254_Snarks], "0x2") +const pt2_xu = fromHex(Fp[BN254_Snarks], "0x0") +const pt2_y1 = fromHex(Fp[BN254_Snarks], "0x181604d0560080401c08b557815482553e278257d98100d193a011c42782474d") +const pt2_yu = fromHex(Fp[BN254_Snarks], "0x04f21f9d99cc25f694cf22ff70dc0ac4692e7a721b725dc454a217f04bd03e33") +const pt2_x = mkFp2( pt2_x1, pt2_xu ) +const pt2_y = mkFp2( pt2_y1, pt2_yu ) + +suite "curves": + + test "gen1 is on the curve": + check checkCurveEqG1(gen1.x,gen1.y) + + test "gen1 is in the subgroup G1": + check checkSubgroupG1(gen1.x,gen1.y) + + test "gen2 is on the curve over Fp2": + check checkCurveEqG2(gen2.x,gen2.y) + + test "gen2 is in the subgroup G2": + check checkSubgroupG2(gen2.x,gen2.y) + + let prime254 : BigInt[254] = fromHex( BigInt[254], "0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001", bigEndian ) + + test "order of gen1 equals to R": + check ( (not bool(isInfG1(gen1))) and bool(isInfG1(prime254 ** gen1)) ) + + test "order of gen2 equals to R": + check ( (not bool(isInfG2(gen2))) and bool(isInfG2(prime254 ** gen2)) ) + + test "pt2 is on the curve over Fp2": + check checkCurveEqG2(pt2_x, pt2_y) + + test "pt2 is NOT in the subgroup G2": + check (not checkSubgroupG2(pt2_x, pt2_y)) + +#------------------------------------------------------------------------------- diff --git a/tests/groth16/testProver.nim b/tests/groth16/testProver.nim index 3bb3eda..1be5593 100644 --- a/tests/groth16/testProver.nim +++ b/tests/groth16/testProver.nim @@ -1,4 +1,6 @@ +{.used.} + import std/unittest import std/sequtils diff --git a/tests/test.nim b/tests/test.nim index f2bf786..a4e5e02 100644 --- a/tests/test.nim +++ b/tests/test.nim @@ -1,3 +1,4 @@ +import ./groth16/testCurve import ./groth16/testProver