diff --git a/benchmarks/bench_hash_to_curve.nim b/benchmarks/bench_hash_to_curve.nim new file mode 100644 index 0000000..bd67256 --- /dev/null +++ b/benchmarks/bench_hash_to_curve.nim @@ -0,0 +1,78 @@ +# 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 + # Internals + ../constantine/config/[common, curves, type_bigint, type_ff], + ../constantine/[towers, hashes], + ../constantine/io/[io_bigints, io_ec], + ../constantine/elliptic/[ + ec_shortweierstrass_affine, + ec_shortweierstrass_projective], + ../constantine/hash_to_curve/hash_to_curve, + # Helpers + ../helpers/prng_unsafe, + ./bench_blueprint + +proc separator*() = separator(132) + +proc report(op, curve: string, startTime, stopTime: MonoTime, startClk, stopClk: int64, iters: int) = + let ns = inNanoseconds((stopTime-startTime) div iters) + let throughput = 1e9 / float64(ns) + when SupportsGetTicks: + echo &"{op:<40} {curve:<15} {throughput:>15.3f} ops/s {ns:>9} ns/op {(stopClk - startClk) div iters:>9} CPU cycles (approx)" + else: + echo &"{op:<40} {curve:<15} {throughput:>15.3f} ops/s {ns:>9} ns/op" + +template bench(op: string, C: static Curve, iters: int, body: untyped): untyped = + measure(iters, startTime, stopTime, startClk, stopClk, body) + report(op, $C, startTime, stopTime, startClk, stopClk, iters) + +proc bench_BLS12_381_hash_to_G2(iters: int) = + const dst = "BLS_SIG_BLS12381G2-SHA256-SSWU-RO_POP_" + let msg = "Mr F was here" + + var P: ECP_ShortW_Prj[Fp2[BLS12_381], OnTwist] + + bench("Hash to G2 (Draft #11)", BLS12_381, iters): + sha256.hashToCurve( + k = 128, + output = P, + augmentation = "", + message = msg, + domainSepTag = dst + ) + +proc bench_BLS12_381_proj_aff_conversion(iters: int) = + const dst = "BLS_SIG_BLS12381G2-SHA256-SSWU-RO_POP_" + let msg = "Mr F was here" + + var P: ECP_ShortW_Prj[Fp2[BLS12_381], OnTwist] + var Paff: ECP_ShortW_Aff[Fp2[BLS12_381], OnTwist] + + sha256.hashToCurve( + k = 128, + output = P, + augmentation = "", + message = msg, + domainSepTag = dst + ) + + bench("Proj->Affine conversion (for pairing)", BLS12_381, iters): + Paff.affineFromProjective(P) + +const Iters = 1000 + +proc main() = + separator() + bench_BLS12_381_hash_to_G2(Iters) + bench_BLS12_381_proj_aff_conversion(Iters) + separator() + +main() +notes() diff --git a/benchmarks/bench_sha256.nim b/benchmarks/bench_sha256.nim index c49e856..b10d6da 100644 --- a/benchmarks/bench_sha256.nim +++ b/benchmarks/bench_sha256.nim @@ -1,6 +1,6 @@ import # Internals - ../constantine/hashes/h_sha256, + ../constantine/hashes, # Helpers ../helpers/prng_unsafe, ./bench_blueprint diff --git a/benchmarks/bench_summary_bls12_381.nim b/benchmarks/bench_summary_bls12_381.nim index bf5ac3a..a200b90 100644 --- a/benchmarks/bench_summary_bls12_381.nim +++ b/benchmarks/bench_summary_bls12_381.nim @@ -77,6 +77,8 @@ proc main() = finalExpBLS12Bench(curve, Iters) pairingBLS12Bench(curve, Iters) separator() + hashToCurveBLS12381G2Bench(Iters) + separator() main() notes() diff --git a/benchmarks/bench_summary_template.nim b/benchmarks/bench_summary_template.nim index 139ddc5..d33a72a 100644 --- a/benchmarks/bench_summary_template.nim +++ b/benchmarks/bench_summary_template.nim @@ -15,14 +15,13 @@ import # Internals ../constantine/config/[curves, common], - ../constantine/arithmetic, - ../constantine/towers, + ../constantine/[arithmetic, hashes, towers], ../constantine/elliptic/[ ec_shortweierstrass_affine, ec_shortweierstrass_projective, ec_shortweierstrass_jacobian, ec_scalar_mul, ec_endomorphism_accel], - ../constantine/hash_to_curve/cofactors, + ../constantine/hash_to_curve/[cofactors, hash_to_curve], ../constantine/pairing/[ cyclotomic_fp12, pairing_bls12, @@ -212,3 +211,20 @@ proc pairingBNBench*(C: static Curve, iters: int) = var f: Fp12[C] bench("Pairing BN", C, iters): f.pairing_bn(P, Q) + +proc hashToCurveBLS12_381G2Bench*(iters: int) = + # Hardcode BLS12_381 + # otherwise concept symbol + # 'CryptoHash' resolution issue + const dst = "BLS_SIG_BLS12381G2-SHA256-SSWU-RO_POP_" + let msg = "Mr F was here" + var P: ECP_ShortW_Prj[Fp2[BLS12_381], OnTwist] + + bench("Hash to G2 (Draft #11)", BLS12_381, iters): + sha256.hashToCurve( + k = 128, + output = P, + augmentation = "", + message = msg, + domainSepTag = dst + ) diff --git a/constantine.nimble b/constantine.nimble index 5c671ac..6b5d7c9 100644 --- a/constantine.nimble +++ b/constantine.nimble @@ -153,6 +153,11 @@ const testDesc: seq[tuple[path: string, useGMP: bool]] = @[ # ---------------------------------------------------------- ("tests/t_hash_sha256_vs_openssl.nim", true), # skip OpenSSL tests on Windows + # Hashing to elliptic curves + # ---------------------------------------------------------- + ("tests/t_hash_to_field.nim", false), + ("tests/t_hash_to_curve.nim", false), + # Prime order fields # ---------------------------------------------------------- ("tests/t_fr.nim", false), @@ -172,6 +177,8 @@ const skipSanitizers = [ "tests/t_ec_sage_bn254_snarks.nim", "tests/t_ec_sage_bls12_377.nim", "tests/t_ec_sage_bls12_381.nim", + "tests/t_hash_to_field.nim", + "tests/t_hash_to_curve.nim" ] when defined(windows): @@ -275,6 +282,7 @@ proc buildAllBenches() = buildBench("bench_summary_bn254_nogami") buildBench("bench_summary_bn254_snarks") buildBench("bench_sha256") + buildBench("bench_hash_to_curve") echo "All benchmarks compile successfully." # Tasks @@ -677,3 +685,20 @@ task bench_summary_bn254_snarks_clang_noasm, "Run summary benchmarks for BN254-S task bench_sha256, "Run SHA256 benchmarks": runBench("bench_sha256") + +# Hash-to-curve +# ------------------------------------------ +task bench_hash_to_curve, "Run Hash-to-Curve benchmarks": + runBench("bench_hash_to_curve") + +task bench_hash_to_curve_gcc, "Run Hash-to-Curve benchmarks": + runBench("bench_hash_to_curve", "gcc") + +task bench_hash_to_curve_clang, "Run Hash-to-Curve benchmarks": + runBench("bench_hash_to_curve", "clang") + +task bench_hash_to_curve_gcc_noasm, "Run Hash-to-Curve benchmarks": + runBench("bench_hash_to_curve", "gcc", useAsm = false) + +task bench_hash_to_curve_clang_noasm, "Run Hash-to-Curve benchmarks": + runBench("bench_hash_to_curve", "clang", useAsm = false) diff --git a/constantine/arithmetic/finite_fields.nim b/constantine/arithmetic/finite_fields.nim index a219051..ce1efd0 100644 --- a/constantine/arithmetic/finite_fields.nim +++ b/constantine/arithmetic/finite_fields.nim @@ -122,6 +122,11 @@ func isMinusOne*(a: FF): SecretBool = ## Constant-time check if -1 (mod p) a.mres == FF.getMontyPrimeMinus1() +func isOdd*(a: FF): SecretBool {. + error: "Do you need the actual value to be odd\n" & + "or what it represents (so once converted out of the Montgomery internal representation)?" + .} + func setZero*(a: var FF) = ## Set ``a`` to zero a.mres.setZero() diff --git a/constantine/config/curves_derived.nim b/constantine/config/curves_derived.nim index 21ffa39..b4cb651 100644 --- a/constantine/config/curves_derived.nim +++ b/constantine/config/curves_derived.nim @@ -66,6 +66,13 @@ macro genDerivedConstants*(mode: static DerivedConstantMode): untyped = M ) ) + # const MyCurve_R4modP = r4mod(MyCurve_Modulus) + result.add newConstStmt( + used(curve & ff & "_R3modP"), newCall( + bindSym"r3mod", + M + ) + ) # const MyCurve_NegInvModWord = negInvModWord(MyCurve_Modulus) result.add newConstStmt( diff --git a/constantine/config/curves_prop_derived.nim b/constantine/config/curves_prop_derived.nim index f5237a8..9bc99bd 100644 --- a/constantine/config/curves_prop_derived.nim +++ b/constantine/config/curves_prop_derived.nim @@ -11,7 +11,7 @@ import std/macros, # Internal ./type_bigint, ./type_ff, ./common, - ./curves_declaration, ./curves_derived + ./curves_declaration, ./curves_prop_core, ./curves_derived # ############################################################ # @@ -57,9 +57,9 @@ proc bindConstant(ff: NimNode, property: string): NimNode = template fieldMod*(Field: type FF): auto = when Field is Fp: - Field.C.Mod + Mod(Field.C) else: - Field.C.getCurveOrder() + getCurveOrder(Field.C) macro getSpareBits*(ff: type FF): untyped = ## Returns the number of extra bits @@ -78,6 +78,10 @@ macro getR2modP*(ff: type FF): untyped = ## Get the Montgomery "R^2 mod P" constant associated to a curve field modulus result = bindConstant(ff, "R2modP") +macro getR3modP*(ff: type FF): untyped = + ## Get the Montgomery "R^3 mod P" constant associated to a curve field modulus + result = bindConstant(ff, "R3modP") + macro getNegInvModWord*(ff: type FF): untyped = ## Get the Montgomery "-1/P[0] mod 2^Wordbitwidth" constant associated to a curve field modulus result = bindConstant(ff, "NegInvModWord") diff --git a/constantine/config/precompute.nim b/constantine/config/precompute.nim index 7a478bc..d78dc68 100644 --- a/constantine/config/precompute.nim +++ b/constantine/config/precompute.nim @@ -345,6 +345,17 @@ func r2mod*(M: BigInt): BigInt = ## R² ≡ ((2^63)^5)^2 (mod M) = 2^630 (mod M) r_powmod(2, M) +func r3mod*(M: BigInt): BigInt = + ## Returns + ## + ## R³ ≡ R³ (mod M) with R = (2^WordBitWidth)^numWords + ## + ## This is used in hash-to-curve to + ## reduce a double-sized bigint mod M + ## and map it to the Montgomery domain + ## with just redc2x + montyMul + r_powmod(3, M) + func montyOne*(M: BigInt): BigInt = ## Returns "1 (mod M)" in the Montgomery domain. ## This is equivalent to R (mod M) in the natural domain diff --git a/constantine/config/type_ff.nim b/constantine/config/type_ff.nim index 26f7b87..3e3424e 100644 --- a/constantine/config/type_ff.nim +++ b/constantine/config/type_ff.nim @@ -8,7 +8,10 @@ import ./common, - ./curves_declaration + ./curves_declaration, + ./curves_prop_core + +export matchingBigInt type Fp*[C: static Curve] = object diff --git a/constantine/curves/bls12_381_g2_hash_to_curve.nim b/constantine/curves/bls12_381_g2_hash_to_curve.nim new file mode 100644 index 0000000..61e4345 --- /dev/null +++ b/constantine/curves/bls12_381_g2_hash_to_curve.nim @@ -0,0 +1,152 @@ +# 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, + ../io/[io_fields, io_towers] + +# Hash-to-Curve map to isogenous BLS12-381 E'2 constants +# ----------------------------------------------------------------- +# +# y² = x³ + A'*x + B' with p² = q ≡ 9 (mod 16), p the BLS12-381 characteristic (base modulus) +# +# Hardcoding from spec: +# - https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-8.8.2 +# - https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/blob/f7dd3761/poc/sswu_opt_9mod16.sage#L142-L148 + +const BLS12_381_h2c_G2_Aprime_E2* = Fp2[BLS12_381].fromHex( # 240𝑖 + "0x0", + "0xf0" +) +const BLS12_381_h2c_G2_Bprime_E2* = Fp2[BLS12_381].fromHex( # 1012 * (1 + 𝑖) + "0x3f4", + "0x3f4" +) +const BLS12_381_h2c_G2_Z* = Fp2[BLS12_381].fromHex( # -(2 + 𝑖) + "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaa9", + "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa" +) +const BLS12_381_h2c_G2_minusA* = Fp2[BLS12_381].fromHex( # -240𝑖 + "0x0", + "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa9bb" +) +const BLS12_381_h2c_G2_ZmulA* = Fp2[BLS12_381].fromHex( # Z*A = 240-480𝑖 + "0xf0", + "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa8cb" +) +const BLS12_381_h2c_G2_inv_Z3* = Fp2[BLS12_381].fromHex( # 1/Z³ + "0xec5373b4fc387140dfd46af348f55e2ca7901ef5b371b085d6da6bdbb39819171ad78d43fdbbe76a0f1189374bc3a07", + "0x9c70eed928c402db9efc63a4a7484928607fdacdea2acefe74fcc1fd9af14de7a1fe76c0d6d687295ce78d4fdf39630" +) +const BLS12_381_h2c_G2_squared_norm_inv_Z3* = Fp[BLS12_381].fromHex( # ||1/Z³||² + "0x59ded5774de2fc31e8f3083875e2b7a4cff24cacc26fbdb84e195f19dbbba49567f439538bc20c48c86f3b645a1b852") +const BLS12_381_h2c_G2_inv_norm_inv_Z3* = Fp[BLS12_381].fromHex( # 1/||1/Z³|| + "0x810e5a23cbb86fd12ded1af502287a397ed25c1d6fe0444e38c48e9c7ddb3c27cfebdd464e90f201fda0eb6983f2533") + + +# Hash-to-Curve 3-isogeny map BLS12-381 E'2 constants +# ----------------------------------------------------------------- +# +# The polynomials map a point (x', y') on the isogenous curve E'2 +# to (x, y) on E2, represented as (xnum/xden, y' * ynum/yden) + +const BLS12_381_h2c_G2_3_isogeny_map_xnum* = [ + # Polynomial k₀ + k₁ x + k₂ x² + k₃ x³ + ... + kₙ xⁿ + # The polynomial is stored as an array of coefficients ordered from k₀ to kₙ + + # 1 + Fp2[BLS12_381].fromHex( + "0x5c759507e8e333ebb5b7a9a47d7ed8532c52d39fd3a042a88b58423c50ae15d5c2638e343d9c71c6238aaaaaaaa97d6", + "0x5c759507e8e333ebb5b7a9a47d7ed8532c52d39fd3a042a88b58423c50ae15d5c2638e343d9c71c6238aaaaaaaa97d6" + ), + # x + Fp2[BLS12_381].fromHex( + "0x0", + "0x11560bf17baa99bc32126fced787c88f984f87adf7ae0c7f9a208c6b4f20a4181472aaa9cb8d555526a9ffffffffc71a" + ), + # x^2 + Fp2[BLS12_381].fromHex( + "0x11560bf17baa99bc32126fced787c88f984f87adf7ae0c7f9a208c6b4f20a4181472aaa9cb8d555526a9ffffffffc71e", + "0x8ab05f8bdd54cde190937e76bc3e447cc27c3d6fbd7063fcd104635a790520c0a395554e5c6aaaa9354ffffffffe38d" + ), + # x^3 + Fp2[BLS12_381].fromHex( + "0x171d6541fa38ccfaed6dea691f5fb614cb14b4e7f4e810aa22d6108f142b85757098e38d0f671c7188e2aaaaaaaa5ed1", + "0x0" + ) +] +const BLS12_381_h2c_G2_3_isogeny_map_xden* = [ + # Polynomial k₀ + k₁ x + k₂ x² + k₃ x³ + ... + kₙ xⁿ + # The polynomial is stored as an array of coefficients ordered from k₀ to kₙ + + # 1 + Fp2[BLS12_381].fromHex( + "0x0", + "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa63" + ), + # x + Fp2[BLS12_381].fromHex( + "0xc", + "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa9f" + ), + # x^2 + Fp2[BLS12_381].fromHex( + "0x1", + "0x0" + ) +] +const BLS12_381_h2c_G2_3_isogeny_map_ynum* = [ + # Polynomial k₀ + k₁ x + k₂ x² + k₃ x³ + ... + kₙ xⁿ + # The polynomial is stored as an array of coefficients ordered from k₀ to kₙ + + # y + Fp2[BLS12_381].fromHex( + "0x1530477c7ab4113b59a4c18b076d11930f7da5d4a07f649bf54439d87d27e500fc8c25ebf8c92f6812cfc71c71c6d706", + "0x1530477c7ab4113b59a4c18b076d11930f7da5d4a07f649bf54439d87d27e500fc8c25ebf8c92f6812cfc71c71c6d706" + ), + # x*y + Fp2[BLS12_381].fromHex( + "0x0", + "0x5c759507e8e333ebb5b7a9a47d7ed8532c52d39fd3a042a88b58423c50ae15d5c2638e343d9c71c6238aaaaaaaa97be" + ), + # x^2*y + Fp2[BLS12_381].fromHex( + "0x11560bf17baa99bc32126fced787c88f984f87adf7ae0c7f9a208c6b4f20a4181472aaa9cb8d555526a9ffffffffc71c", + "0x8ab05f8bdd54cde190937e76bc3e447cc27c3d6fbd7063fcd104635a790520c0a395554e5c6aaaa9354ffffffffe38f" + ), + # x^3*y + Fp2[BLS12_381].fromHex( + "0x124c9ad43b6cf79bfbf7043de3811ad0761b0f37a1e26286b0e977c69aa274524e79097a56dc4bd9e1b371c71c718b10", + "0x0" + ) +] +const BLS12_381_h2c_G2_3_isogeny_map_yden* = [ + # Polynomial k₀ + k₁ x + k₂ x² + k₃ x³ + ... + kₙ xⁿ + # The polynomial is stored as an array of coefficients ordered from k₀ to kₙ + + # 1 + Fp2[BLS12_381].fromHex( + "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa8fb", + "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa8fb" + ), + # x + Fp2[BLS12_381].fromHex( + "0x0", + "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa9d3" + ), + # x^2 + Fp2[BLS12_381].fromHex( + "0x12", + "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa99" + ), + # x^3 + Fp2[BLS12_381].fromHex( + "0x1", + "0x0" + ) +] \ No newline at end of file diff --git a/constantine/curves/zoo_hash_to_curve.nim b/constantine/curves/zoo_hash_to_curve.nim new file mode 100644 index 0000000..1dd8a81 --- /dev/null +++ b/constantine/curves/zoo_hash_to_curve.nim @@ -0,0 +1,29 @@ +# 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/macros, + ../config/curves, + ./bls12_381_g2_hash_to_curve + +{.experimental: "dynamicBindSym".} + +macro h2cConst*(C: static Curve, group, value: untyped): untyped = + ## Get a Hash-to-Curve constant + ## for mapping to a elliptic curve group (G1 or G2) + return bindSym($C & "_h2c_" & $group & "_" & $value) + +macro h2cIsomapPoly*(C: static Curve, + group: untyped, + isodegree: static int, + value: untyped): untyped = + ## Get an isogeny map polynomial + ## for mapping to a elliptic curve group (G1 or G2) + return bindSym($C & "_h2c_" & + $group & "_" & $isodegree & + "_isogeny_map_" & $value) diff --git a/constantine/hash_to_curve/h2c_hash_to_field.nim b/constantine/hash_to_curve/h2c_hash_to_field.nim new file mode 100644 index 0000000..03ef25c --- /dev/null +++ b/constantine/hash_to_curve/h2c_hash_to_field.nim @@ -0,0 +1,242 @@ +# 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 + # Internals + ../hashes, + ../io/[endians, io_bigints, io_fields], + ../config/[common, curves, type_bigint, type_ff], + ../arithmetic/limbs_montgomery, + ../tower_field_extensions/extension_fields + +# ############################################################ +# +# Hash to Finite Fields +# +# ############################################################ + +# No exceptions allowed in core cryptographic operations +{.push raises: [].} + +# Helpers +# ---------------------------------------------------------------- +func ceilDiv(a, b: uint): uint = + ## ceil division + ## ceil(a / b) + (a + b - 1) div b + +proc copyFrom[N](output: var openarray[byte], bi: array[N, byte], cur: var uint) = + var b_index = 0'u + while b_index < bi.len.uint and cur < output.len.uint: + output[cur] = bi[b_index] + inc cur + inc b_index + +template strxor(b_i: var array, b0: array): untyped = + for i in 0 ..< b_i.len: + b_i[i] = b_i[i] xor b0[i] +# ---------------------------------------------------------------- + +func shortDomainSepTag[DigestSize: static int, B: byte|char]( + H: type CryptoHash, + output: var array[DigestSize, byte], + oversizedDST: openarray[B]) = + ## Compute a short Domain Separation Tag + ## from a domain separation tag larger than 255 bits + ## + ## https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-09#section-5.4.3 + static: doAssert DigestSize == H.type.digestSize + var ctx {.noInit.}: H + ctx.init() + ctx.update"H2C-OVERSIZE-DST-" + ctx.update oversizedDST + ctx.finish(output) + +func expandMessageXMD*[B1, B2, B3: byte|char]( + H: type CryptoHash, + output: var openarray[byte], + augmentation: openarray[B1], + message: openarray[B2], + domainSepTag: openarray[B3] + ) = + ## The expand_message_xmd function produces a uniformly random byte + ## string using a cryptographic hash function H that outputs "b" bits, + ## with b >= 2*k and k the target security level (for example 128-bit) + ## + ## https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-09#section-5.4.1 + ## + ## Arguments: + ## - `Hash` a cryptographic hash function. + ## - `Hash` MAY be a Merkle-Damgaard hash function like SHA-2 + ## - `Hash` MAY be a sponge-based hash function like SHA-3 or BLAKE2 + ## - Otherwise, H MUST be a hash function that has been proved + ## indifferentiable from a random oracle [MRH04] under a reasonable + ## cryptographic assumption. + ## - `output`, a buffer dimensioned the requested length. + ## it will be filled with bits indifferentiable from a random oracle. + ## - `augmentation`, an optional augmentation to the message. This will be prepended, + ## prior to hashing. + ## This is used for building the "message augmentation" variant of BLS signatures + ## https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04#section-3.2 + ## which requires `CoreSign(SK, PK || message)` + ## and `CoreVerify(PK, PK || message, signature)` + ## - `message` is the message to hash + ## - `domainSepTag` is the protocol domain separation tag (DST). + ## If a domainSepTag larger than 255-bit is required, + ## it is recommended to cache the reduced DST + + # TODO oversized DST support + + # Steps: + # 1. ell = ceil(len_in_bytes / b_in_bytes) + # 2. ABORT if ell > 255 + # 3. DST_prime = DST || I2OSP(len(DST), 1) + # 4. Z_pad = I2OSP(0, r_in_bytes) + # 5. l_i_b_str = I2OSP(len_in_bytes, 2) + # 6. msg_prime = Z_pad || msg || l_i_b_str || I2OSP(0, 1) || DST_prime + # 7. b_0 = H(msg_prime) + # 8. b_1 = H(b_0 || I2OSP(1, 1) || DST_prime) + # 9. for i in (2, ..., ell): + # 10. b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime) + # 11. uniform_bytes = b_1 || ... || b_ell + # 12. return substr(uniform_bytes, 0, len_in_bytes) + mixin digestSize + type Hash = H # Otherwise the VM says "cannot evaluate at compiletime H" + const DigestSize = Hash.digestSize() + const BlockSize = Hash.internalBlockSize() + + assert output.len mod 8 == 0 + + let ell = ceilDiv(output.len.uint, DigestSize.uint) + const zPad = default(array[BlockSize, byte]) + let l_i_b_str = output.len.uint16.toBytesBE() + + var b0 {.noinit, align: DigestSize.}: array[DigestSize, byte] + func ctZpad(): Hash = + # Compile-time precompute + # TODO upstream: `toOpenArray` throws "cannot generate code for: mSlice" + result.init() + result.update zPad + var ctx = ctZpad() # static(ctZpad()) + ctx.update augmentation + ctx.update message + ctx.update l_i_b_str + ctx.update [byte 0] + ctx.update domainSepTag + ctx.update [byte domainSepTag.len] # DST_prime + ctx.finish(b0) + + var cur = 0'u + var bi {.noinit, align: DigestSize.}: array[DigestSize, byte] + # b1 + ctx.init() + ctx.update(b0) + ctx.update([byte 1]) + ctx.update domainSepTag + ctx.update [byte domainSepTag.len] # DST_prime + ctx.finish(bi) + output.copyFrom(bi, cur) + + for i in 2 .. ell: + ctx.init() + strxor(bi, b0) + ctx.update(bi) + ctx.update([byte i]) + ctx.update domainSepTag + ctx.update [byte domainSepTag.len] # DST_prime + ctx.finish(bi) + output.copyFrom(bi, cur) + if cur == output.len.uint: + return + +func redc2x[FF](r: var FF, big2x: BigInt) {.inline.} = + r.mres.limbs.montyRedc2x( + big2x.limbs, + FF.fieldMod().limbs, + FF.getNegInvModWord(), + FF.getSpareBits() + ) + +func montyMul(r: var BigInt, a, b: BigInt, FF: type) {.inline.} = + r.limbs.montyMul( + a.limbs, b.limbs, + FF.fieldMod().limbs, + FF.getNegInvModWord(), + FF.getSpareBits() + ) + +func hashToField*[Field; B1, B2, B3: byte|char, count: static int]( + H: type CryptoHash, + k: static int, + output: var array[count, Field], + augmentation: openarray[B1], + message: openarray[B2], + domainSepTag: openarray[B3] + ) = + ## Hash to a field or an extension field + ## https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3 + ## + ## Arguments: + ## - `Hash` a cryptographic hash function. + ## - `Hash` MAY be a Merkle-Damgaard hash function like SHA-2 + ## - `Hash` MAY be a sponge-based hash function like SHA-3 or BLAKE2 + ## - Otherwise, H MUST be a hash function that has been proved + ## indifferentiable from a random oracle [MRH04] under a reasonable + ## cryptographic assumption. + ## - k the security parameter of the suite in bits (for example 128) + ## - `output`, an array of fields or extension fields. + ## - `augmentation`, an optional augmentation to the message. This will be prepended, + ## prior to hashing. + ## This is used for building the "message augmentation" variant of BLS signatures + ## https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04#section-3.2 + ## which requires `CoreSign(SK, PK || message)` + ## and `CoreVerify(PK, PK || message, signature)` + ## - `message` is the message to hash + ## - `domainSepTag` is the protocol domain separation tag (DST). + ## If a domainSepTag larger than 255-bit is required, + ## it is recommended to cache the reduced DST. + + const + L = int ceilDiv(Field.C.getCurveBitwidth() + k, 8) + m = block: + when Field is Fp: 1 + elif Field is Fp2: 2 + else: {.error: "Unconfigured".} + + len_in_bytes = count * m * L + + var uniform_bytes{.noInit.}: array[len_in_bytes, byte] + sha256.expandMessageXMD( + uniform_bytes, + augmentation = augmentation, + message = message, + domainSepTag = domainSepTag + ) + + for i in 0 ..< count: + for j in 0 ..< m: + let elm_offset = L * (j + i * m) + template tv: untyped = uniform_bytes.toOpenArray(elm_offset, elm_offset + L-1) + + var big2x {.noInit.}: BigInt[2 * getCurveBitwidth(Field.C)] + big2x.fromRawUint(tv, bigEndian) + + # Reduces modulo p and output in Montgomery domain + when m == 1: + output[i].redc2x(big2x) + output[i].mres.montyMul( + output[i].mres, + Fp[Field.C].getR3ModP(), + Fp[Field.C]) + + else: + output[i].coords[j].redc2x(big2x) + output[i].coords[j].mres.montyMul( + output[i].coords[j].mres, + Fp[Field.C].getR3ModP(), + Fp[Field.C]) diff --git a/constantine/hash_to_curve/h2c_map_to_isocurve_swu.nim b/constantine/hash_to_curve/h2c_map_to_isocurve_swu.nim new file mode 100644 index 0000000..159c166 --- /dev/null +++ b/constantine/hash_to_curve/h2c_map_to_isocurve_swu.nim @@ -0,0 +1,230 @@ +# 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 + # Internals + ../config/[common, curves], + ../primitives, ../arithmetic, ../towers, + ../curves/zoo_hash_to_curve + +# ############################################################ +# +# Mapping to isogenous curve E' +# using: +# - Shallue-van de Woestijne method (SWU) +# - Simplified Shallue-van de Woestijne (SSWU) +# - Simplified Shallue-van de Woestijne +# with curve equation parameters a == 0 or b == 0 +# (SSWU0) +# +# ############################################################ + +# No exceptions allowed +{.push raises: [].} + +# Normative references +# - SWU: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-6.6.1 +# - SSWU: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-6.6.2 +# - SSWU0: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-6.6.3 +# Optimizations +# - SSWU of GF(q) with q ≡ 9 (mod 16): +# - https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-G.2.3 +# - https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/blob/f7dd3761/poc/sswu_opt_9mod16.sage +# Test vector generator +# - https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/blob/f7dd3761/poc/sswu_generic.sage + +func sgn0(x: Fp): SecretBool = + ## Returns a conventional "sign" for a field element. + ## Even numbers are considered positive by convention + ## and odd negative. + ## + ## https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-4.1 + # + # In Montgomery representation + # each number a is represented as aR (mod M) + # with R a Montgomery constant + # hence the LSB of the Montgomery representation + # cannot be used for this use-case. + # + # Another angle is that if M is odd, + # a+M and a have different parity even though they are + # the same modulo M. + let canonical = x.toBig() + result = canonical.isOdd() + +func sgn0(x: Fp2): SecretBool = + # https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-4.1 + # sgn0_m_eq_2(x) + # + # Input: x, an element of GF(p^2). + # Output: 0 or 1. + # + # Steps: + # 1. sign_0 = x_0 mod 2 + # 2. zero_0 = x_0 == 0 + # 3. sign_1 = x_1 mod 2 + # 4. return sign_0 OR (zero_0 AND sign_1) # Avoid short-circuit logic ops + + result = x.c0.sgn0() + let z0 = x.c0.isZero() + let s1 = x.c1.sgn0() + result = result or (z0 and s1) + +func invsqrt_if_square[C: static Curve]( + r: var Fp2[C], a: Fp2[C]): SecretBool = + ## If ``a`` is a square, compute the inverse square root of ``a`` + ## and store it in r + ## if not, ``r`` is unmodified. + ## + ## The square root, if it exist is multivalued, + ## i.e. both x² == (-x)² + ## This procedure returns a deterministic result + ## + ## This is an optimized version which is + ## requires the sqrt of sqrt of the quadratic non-residue + ## to be in Fp2. + ## + ## See `sqrt_if_square_opt` in square_root_fp2.nim + # + # Implementation via the complex method + # Gora Adj, Francisco Rodríguez-Henríquez, 2012, https://eprint.iacr.org/2012/685 + # Made constant-time and optimized to fuse sqrt and inverse sqrt + # and avoid unfused isSquare tests. + # See discussion and optimization with Andy Polyakov + # https://github.com/supranational/blst/issues/2#issuecomment-686656784 + + var t1{.noInit.}, t2{.noInit.}, t3{.noInit.}: Fp[C] + var inp{.noInit.}: Fp2[C] + + t1.square(a.c0) # a0² + t2.square(a.c1) # - β a1² with β = 𝑖² in a complex extension field + when a.fromComplexExtension(): + t1 += t2 # a0² - (-1) a1² + else: + t2 *= NonResidue + t1 -= t2 + + # TODO: implement invsqrt alone + result = sqrt_invsqrt_if_square(sqrt = r.c1, invsqrt = t3, t1) # 1/sqrt(a0² - β a1²) + + # If input is not a square in Fp2, multiply by 1/Z³ + inp.prod(a, h2cConst(C, G2, inv_Z3)) # inp = a / Z³ + block: # Adjust t1 and t3 accordingly + var t0{.noInit.}: Fp[C] + t0.prod(t1, h2cConst(C, G2, squared_norm_inv_Z3)) # (a0² - β a1²) * ||1/Z³||² + t1.ccopy(t0, not result) + + t0.prod(t3, h2cConst(C, G2, inv_norm_inv_Z3)) # 1/sqrt(a0² - β a1²) * 1/||1/Z³|| + t3.ccopy(t0, not result) + + inp.ccopy(a, result) + + t1 *= t3 # sqrt(a0² - β a1²) + + t2.diff(inp.c0, t1) + t1 += inp.c0 + t1.ccopy(t2, t1.isZero()) + t1.div2() # (a0 ± sqrt(a0² - βa1²))/2 + + # TODO: implement invsqrt alone + sqrt_invsqrt(sqrt = r.c1, invsqrt = r.c0, t1) + + r.c1 = inp.c1 + r.c1.div2() + r.c1 *= r.c0 # a1/(2*sqrt( (a0 ± sqrt(a0²+a1²))/2 )) + r.c0 *= t1 # sqrt((a0 ± sqrt(a0²+a1²))/2) + + # Now rotate in extension field coordinate (a+βb) + # to find the quadrant of the square root. + discard sqrt_rotate_extension(r, r, inp) + + # Inverse the result + r.c0 *= t3 + r.c1 *= t3 + r.c1.neg() + + # return result + +func mapToIsoCurve_sswuG2_opt9mod16*[C: static Curve]( + xn, xd, yn: var Fp2[C], + u: Fp2[C]) = + ## Given G2, the target prime order subgroup of E2 we want to hash to, + ## this function maps any field element of Fp2 to E'2 + ## a curve isogenous to E2 using the Simplified Shallue-van de Woestijne method. + ## + ## This requires p² ≡ 9 (mod 16). + # + # Input: + # - u, an Fp2 element + # Output: + # - (xn, xd, yn, yd) such that (x', y') = (xn/xd, yn/yd) + # is a point of E'2 + # - yd is implied to be 1 + # + # Paper: https://eprint.iacr.org/2019/403 + # Spec: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-G.2.3 + # Sage: https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/blob/f7dd3761/poc/sswu_opt_9mod16.sage#L36-L118 + # BLST: https://github.com/supranational/blst/blob/v0.3.4/src/map_to_g2.c#L229-L273 + # Formal verification: https://github.com/GaloisInc/BLST-Verification/blob/8e2efde4/spec/implementation/HashToG2.cry + + var + uu {.noInit.}, tv2 {.noInit.}: Fp2[C] + tv4 {.noInit.}, x2n {.noInit.}, gx1 {.noInit.}: Fp2[C] + gxd {.noInit.}: Fp2[C] + y2 {.noInit.}: Fp2[C] + e1, e2: SecretBool + + # Aliases + template y: untyped = yn + template x1n: untyped = xn + template y1: untyped = yn + template Zuu: untyped = x2n + + # x numerators + uu.square(u) # uu = u² + Zuu.prod(uu, h2cConst(C, G2, Z)) # Zuu = Z * uu + tv2.square(Zuu) # tv2 = Zuu² + tv2 += Zuu # tv2 = tv2 + Zuu + x1n.setOne() + x1n += tv2 # x1n = tv2 + 1 + x1n *= h2cConst(C, G2, Bprime_E2) # x1n = x1n * B' + x2n.prod(Zuu, x1n) # x2n = Zuu * x1n + + # x denumerator + xd.prod(tv2, h2cConst(C, G2, minus_A)) # xd = -A * tv2 + e1 = xd.isZero() # e1 = xd == 0 + xd.ccopy(h2cConst(C, G2, ZmulA), e1) # If xd == 0, set xd = Z*A + + # y numerators + tv2.square(xd) + gxd.prod(xd, tv2) # gxd = xd³ + tv2 *= h2CConst(C, G2, Aprime_E2) + gx1.square(x1n) + gx1 += tv2 # x1n² + A * xd² + gx1 *= x1n # x1n³ + A * x1n * xd² + tv2.prod(gxd, h2cConst(C, G2, Bprime_E2)) + gx1 += tv2 # gx1 = x1n³ + A * x1n * xd² + B * xd³ + tv4.square(gxd) # tv4 = gxd² + tv2.prod(gx1, gxd) # tv2 = gx1 * gxd + tv4 *= tv2 # tv4 = gx1 * gxd³ + + # Start searching for sqrt(gx1) + e2 = y1.invsqrt_if_square(tv4) # y1 = tv4^c1 = (gx1 * gxd³)^((p²-9)/16) + y1 *= tv2 # y1 *= gx1*gxd + y2.prod(y1, uu) + y2 *= u + + # Choose numerators + xn.ccopy(x2n, not e2) # xn = e2 ? x1n : x2n + yn.ccopy(y2, not e2) # yn = e2 ? y1 : y2 + + e1 = sgn0(u) + e2 = sgn0(y) + y.cneg(e1 xor e2) + + # yd.setOne() diff --git a/constantine/hash_to_curve/hash_to_curve.nim b/constantine/hash_to_curve/hash_to_curve.nim new file mode 100644 index 0000000..76a967b --- /dev/null +++ b/constantine/hash_to_curve/hash_to_curve.nim @@ -0,0 +1,110 @@ +# 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 + # Internals + ../config/[common, curves], + ../primitives, ../arithmetic, ../towers, + ../curves/zoo_hash_to_curve, + ../elliptic/ec_shortweierstrass_projective, + ./h2c_hash_to_field, + ./h2c_map_to_isocurve_swu, + ./cofactors, + ../isogeny/h2c_isogeny_maps, + ../hashes + +# ############################################################ +# +# Hashing to Elliptic Curve +# +# ############################################################ + +# Normative references +# - https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11 +# - https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve + +# No exceptions allowed in core cryptographic operations +{.push raises: [].} + +# Map to curve +# ---------------------------------------------------------------- + +func mapToCurve[F; Tw: static Twisted]( + r: var ECP_ShortW_Prj[F, Tw], u: F) = + ## Map an element of the + ## finite or extension field F + ## to an elliptic curve E + + when F.C == BLS12_381 and F is Fp2: + # https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-6.6.3 + # Simplified Shallue-van de Woestijne-Ulas method for AB == 0 + + # 1. Map to E'2 isogenous to E2 + var + xn{.noInit.}, xd{.noInit.}: F + yn{.noInit.}: F + + mapToIsoCurve_sswuG2_opt9mod16( + xn, xd, + yn, + u + ) + + # 2. Map from E'2 to E2 + r.h2c_isogeny_map( + xn, xd, + yn, + isodegree = 3 + ) + else: + {.error: "Not implemented".} + +# Hash to curve +# ---------------------------------------------------------------- + +func hashToCurve*[ + F; Tw: static Twisted; + B1, B2, B3: byte|char]( + H: type CryptoHash, + k: static int, + output: var ECP_ShortW_Prj[F, Tw], + augmentation: openarray[B1], + message: openarray[B2], + domainSepTag: openarray[B3] + ) = + ## Hash a message to an elliptic curve + ## + ## Arguments: + ## - `Hash` a cryptographic hash function. + ## - `Hash` MAY be a Merkle-Damgaard hash function like SHA-2 + ## - `Hash` MAY be a sponge-based hash function like SHA-3 or BLAKE2 + ## - Otherwise, H MUST be a hash function that has been proved + ## indifferentiable from a random oracle [MRH04] under a reasonable + ## cryptographic assumption. + ## - k the security parameter of the suite in bits (for example 128) + ## - `output`, an elliptic curve point that will be overwritten. + ## - `augmentation`, an optional augmentation to the message. This will be prepended, + ## prior to hashing. + ## This is used for building the "message augmentation" variant of BLS signatures + ## https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04#section-3.2 + ## which requires `CoreSign(SK, PK || message)` + ## and `CoreVerify(PK, PK || message, signature)` + ## - `message` is the message to hash + ## - `domainSepTag` is the protocol domain separation tag (DST). + ## If a domainSepTag larger than 255-bit is required, + ## it is recommended to cache the reduced DST. + + var u{.noInit.}: array[2, F] + H.hashToField(k, u, augmentation, message, domainSepTag) + + var P{.noInit.}: array[2, ECP_ShortW_Prj[F, Tw]] + P[0].mapToCurve(u[0]) + P[1].mapToCurve(u[1]) + + output.sum(P[0], P[1]) + output.clearCofactorReference() # TODO - fast cofactor clear diff --git a/constantine/hashes.nim b/constantine/hashes.nim new file mode 100644 index 0000000..f82908d --- /dev/null +++ b/constantine/hashes.nim @@ -0,0 +1,71 @@ +# 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. + +# ############################################################ +# +# Hash Function concept +# +# ############################################################ + +type + CryptoHash* = concept h, var ctx, type H + ## Interface of a cryptographic hash function + ## + ## - digestSizeInBytes is the hash output size in bytes + ## - internalBlockSize, in bits: + ## hash functions are supposed to ingest fixed block size + ## that are padded if necessary + ## - SHA256 block size is 64 bits + ## - SHA512 block size is 128 bits + ## - SHA3-512 block size is 72 bits + + # should we avoid int to avoid exception? But they are compile-time + H.digestSize is int + H.internalBlockSize is int + + # Context + # ------------------------------------------- + # update/finish are not matching properly + + # type B = char or byte + ctx.init() + # ctx.update(openarray[B]) + # ctx.finish(var array[H.digestSize, byte]) + ctx.clear() + +func hash*[DigestSize: static int, T: char|byte]( + HashKind: type CryptoHash, + digest: var array[DigestSize, byte], + message: openarray[T], + clearMem = false) = + ## Produce a digest from a message + static: doAssert DigestSize == HashKind.type.digestSize + + mixin update, finish + var ctx {.noInit.}: HashKind + ctx.init() + ctx.update(message) + ctx.finish(digest) + + if clearMem: + ctx.clear() + +func hash*[T: char|byte]( + HashKind: type CryptoHash, + message: openarray[T], + clearmem = false): array[HashKind.sizeInBytes, byte] = + ## Produce a digest from a message + HashKind.hash(result, message, clearMem) + +# Exports +# ----------------------------------------------------------------------- + +import ./hashes/h_sha256 +export h_sha256 + +static: doAssert sha256 is CryptoHash diff --git a/constantine/hashes/h_sha256.nim b/constantine/hashes/h_sha256.nim index 966a1fb..63c6250 100644 --- a/constantine/hashes/h_sha256.nim +++ b/constantine/hashes/h_sha256.nim @@ -300,6 +300,14 @@ func hashBuffer(ctx: var Sha256Context) = # Public API # ---------------------------------------------------------------- +func digestSize*(H: type sha256): int = + ## Returns the output size in bytes + 32 + +func internalBlockSize*(H: type sha256): int = + ## Returns the byte size of the hash function ingested blocks + 64 + func init*(ctx: var Sha256Context) = ## Initialize or reinitialize a Sha256 context @@ -335,6 +343,9 @@ func update*[T: char|byte](ctx: var Sha256Context, message: openarray[T]) = for i in ctx.bufIdx ..< ctx.buf.len: doAssert ctx.buf[i] == 0 + if message.len == 0: + return + var # Message processing state machine cur = 0'u bytesLeft = message.len.uint @@ -414,24 +425,3 @@ func clear*(ctx: var Sha256Context) = ## use a Key Derivation Function instead (KDF) # TODO: ensure compiler cannot optimize the code away ctx.buf.setZero() - -func hash*[T: char|byte]( - HashKind: type sha256, - digest: var array[32, byte], - message: openarray[T], - clearMem = false) = - ## Produce a SHA256 digest from a message - var ctx {.noInit.}: HashKind - ctx.init() - ctx.update(message) - ctx.finish(digest) - - if clearMem: - ctx.clear() - -func hash*[T: char|byte]( - HashKind: type sha256, - message: openarray[T], - clearmem = false): array[32, byte] = - ## Produce a SHA256 digest from a message - HashKind.hash(result, message, clearMem) diff --git a/constantine/io/endians.nim b/constantine/io/endians.nim index bee18bd..44003cc 100644 --- a/constantine/io/endians.nim +++ b/constantine/io/endians.nim @@ -76,3 +76,9 @@ func dumpRawInt*[T: byte|char]( else: for i in 0'u ..< L: dst[cursor+i] = toByte(src shr ((L-i-1) * 8)) + +func toBytesBE*(num: SomeUnsignedInt): array[sizeof(num), byte] {.inline.}= + ## Convert an integer to an array of bytes + const L = sizeof(num) + for i in 0 ..< L: + result[i] = toByte(num shr ((L-1-i) * 8)) diff --git a/constantine/io/io_bigints.nim b/constantine/io/io_bigints.nim index 9a6cf6a..5643700 100644 --- a/constantine/io/io_bigints.nim +++ b/constantine/io/io_bigints.nim @@ -70,6 +70,9 @@ func fromRawUintLE( if dst_idx < dst.limbs.len: dst.limbs[dst_idx] = acc + for i in dst_idx + 1 ..< dst.limbs.len: + dst.limbs[i] = Zero + func fromRawUintBE( dst: var BigInt, src: openarray[byte]) = @@ -107,6 +110,9 @@ func fromRawUintBE( if dst_idx < dst.limbs.len: dst.limbs[dst_idx] = acc + for i in dst_idx + 1 ..< dst.limbs.len: + dst.limbs[i] = Zero + func fromRawUint*( dst: var BigInt, src: openarray[byte], diff --git a/constantine/io/io_fields.nim b/constantine/io/io_fields.nim index 5144d2a..1fdad06 100644 --- a/constantine/io/io_fields.nim +++ b/constantine/io/io_fields.nim @@ -12,6 +12,8 @@ import ../arithmetic/finite_fields, ../primitives +export Fp + # No exceptions allowed {.push raises: [].} {.push inline.} diff --git a/constantine/isogeny/h2c_isogeny_maps.nim b/constantine/isogeny/h2c_isogeny_maps.nim new file mode 100644 index 0000000..ec5b3b7 --- /dev/null +++ b/constantine/isogeny/h2c_isogeny_maps.nim @@ -0,0 +1,131 @@ +# 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 + # Internals + ../primitives, ../arithmetic, ../towers, + ../curves/zoo_hash_to_curve, + ../elliptic/ec_shortweierstrass_projective + +# ############################################################ +# +# Mapping from isogenous curve E' to E +# +# ############################################################ + +# No exceptions allowed +{.push raises: [].} + +func poly_eval_horner[F](r: var F, x: F, poly: openarray[F]) = + ## Fast polynomial evaluation using Horner's rule + ## The polynomial k₀ + k₁ x + k₂ x² + k₃ x³ + ... + kₙ xⁿ + ## MUST be stored in order + ## [k₀, k₁, k₂, k₃, ..., kₙ] + ## + ## Assuming a degree n = 3 polynomial + ## Horner's rule rewrites its evaluation as + ## ((k₃ x + k₂)x + k₁) x + k₀ + ## which is n additions and n multiplications, + ## the lowest complexity of general polynomial evaluation algorithm. + r = poly[^1] # TODO: optim when poly[^1] == 1 + for i in countdown(poly.len-2, 0): + r *= x + r += poly[i] + +func poly_eval_horner_scaled[F; D, N: static int]( + r: var F, xn: F, + xd_pow: array[D, F], poly: array[N, F]) = + ## Fast polynomial evaluation using Horner's rule + ## Result is scaled by xd^N with N the polynomial degree + ## to avoid finite field division + ## + ## The array of xd powers xd^e has e in [1, N], + ## for example [xd, xd², xd³] + ## + ## The polynomial k₀ + k₁ xn/xd + k₂ (xn/xd)² + k₃ (xn/xd)³ + ... + kₙ (xn/xd)ⁿ + ## MUST be stored in order + ## [k₀, k₁, k₂, k₃, ..., kₙ] + ## + ## Assuming a degree n = 3 polynomial + ## Horner's rule rewrites its evaluation as + ## ((k₃ (xn/xd) + k₂)(xn/xd) + k₁) (xn/xd) + k₀ + ## which is n additions and n multiplications, + ## the lowest complexity of general polynomial evaluation algorithm. + ## + ## By scaling by xd³ + ## we get + ## ((k₃ xn + k₂ xd) xn + k₁ xd²) xn + k₀ xd³ + ## + ## avoiding expensive divisions + r = poly[^1] # TODO: optim when poly[^1] == 1 + for i in countdown(N-2, 0): + var t: F + r *= xn + t.prod(poly[i], xd_pow[N-2-i]) + r += t + + const + poly_degree = N-1 # [1, x, x², x³] of length 4 + isodegree = D # Isogeny degree + + static: doAssert isodegree - poly_degree >= 0 + when isodegree - poly_degree > 0: + # Missing scaling factor + r *= xd_pow[isodegree - poly_degree - 1] + +func h2c_isogeny_map*[F; Tw: static Twisted]( + r: var ECP_ShortW_Prj[F, Tw], + xn, xd, yn: F, isodegree: static int) = + ## Given G2, the target prime order subgroup of E2, + ## this function maps an element of + ## E'2 a curve isogenous to E2 + ## to E2. + ## + ## The E'2 input is represented as + ## (x', y') with x' = xn/xd and y' = yn/yd + ## + ## yd is assumed to be 1 hence y' == yn + ## + ## Reference: + ## - https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-6.6.3 + ## - https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve + + # xd^e with e in [1, N], for example [xd, xd², xd³] + var xd_pow: array[isodegree, F] + xd_pow[0] = xd + for i in 1 ..< xd_pow.len: + xd_pow[i].prod(xd_pow[i-1], xd) + + r.x.poly_eval_horner_scaled( + xn, xd_pow, + h2cIsomapPoly(F.C, G2, isodegree, xnum) + ) + r.z.poly_eval_horner_scaled( + xn, xd_pow, + h2cIsomapPoly(F.C, G2, isodegree, xden) + ) + + r.y.poly_eval_horner_scaled( + xn, xd_pow, + h2cIsomapPoly(F.C, G2, isodegree, ynum) + ) + var t {.noInit.}: F + t.poly_eval_horner_scaled( + xn, xd_pow, + h2cIsomapPoly(F.C, G2, isodegree, yden) + ) + + # y coordinate is y' * poly_yNum(x) + r.y *= yn + + # Now convert to projective coordinates + # (x, y) => (xnum/xden, ynum/yden) + # <=> (xnum*yden, ynum*xden, xden*yden) + r.y *= r.z + r.x *= t + r.z *= t diff --git a/constantine/tower_field_extensions/extension_fields.nim b/constantine/tower_field_extensions/extension_fields.nim index 4c64198..cced6e2 100644 --- a/constantine/tower_field_extensions/extension_fields.nim +++ b/constantine/tower_field_extensions/extension_fields.nim @@ -76,7 +76,7 @@ template C*(E: type ExtensionField): Curve = E.F.C template fieldMod*(E: type ExtensionField): auto = - Fp[E.F.C].fieldMod() + Mod(E.F.C) # Initialization # ------------------------------------------------------------------- diff --git a/constantine/tower_field_extensions/square_root_fp2.nim b/constantine/tower_field_extensions/square_root_fp2.nim index 54f03c7..4bb1767 100644 --- a/constantine/tower_field_extensions/square_root_fp2.nim +++ b/constantine/tower_field_extensions/square_root_fp2.nim @@ -42,7 +42,7 @@ func isSquare*(a: Fp2): SecretBool = result = tv1.isSquare() -func sqrt_rotate_extension( +func sqrt_rotate_extension*( out_sqrt: var Fp2, candidate_sqrt: Fp2, a: Fp2 @@ -120,7 +120,7 @@ func sqrt_if_square_opt(a: var Fp2): SecretBool = t1.square(a.c0) # a0² t2.square(a.c1) # - β a1² with β = 𝑖² in a complex extension field when a.fromComplexExtension(): - t1 += t2 # a0 - (-1) a1² + t1 += t2 # a0² - (-1) a1² else: t2 *= NonResidue t1 -= t2 @@ -133,6 +133,7 @@ func sqrt_if_square_opt(a: var Fp2): SecretBool = t1.ccopy(t2, t1.isZero()) t1.div2() # (a0 ± sqrt(a0² - β a1²))/2 + # TODO: implement invsqrt alone # t1 being an actual sqrt will be tested in sqrt_rotate_extension # 1/sqrt((a0 ± sqrt(a0² - β b²))/2) sqrt_invsqrt(sqrt = cand.c1, invsqrt = cand.c0, t1) # we only want invsqrt = cand.c0 diff --git a/sage/derive_hash_to_curve.sage b/sage/derive_hash_to_curve.sage new file mode 100644 index 0000000..b3589fd --- /dev/null +++ b/sage/derive_hash_to_curve.sage @@ -0,0 +1,329 @@ +#!/usr/bin/sage +# vim: syntax=python +# vim: set ts=2 sw=2 et: + +# 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. + +# ############################################################ +# +# Frobenius constants +# +# ############################################################ + +# Imports +# --------------------------------------------------------- + +import os +import inspect, textwrap + +# Working directory +# --------------------------------------------------------- + +os.chdir(os.path.dirname(__file__)) + +# Sage imports +# --------------------------------------------------------- +# Accelerate arithmetic by accepting probabilistic proofs +from sage.structure.proof.all import arithmetic +arithmetic(False) + +load('curves.sage') + +# Utilities +# --------------------------------------------------------- + +def fp2_to_hex(a): + v = vector(a) + return '0x' + Integer(v[0]).hex() + ' + β * ' + '0x' + Integer(v[1]).hex() + +def field_to_nim(value, field, curve, prefix = "", comment_above = "", comment_right = ""): + result = '# ' + comment_above + '\n' if comment_above else '' + comment_right = ' # ' + comment_right if comment_right else '' + + if field == 'Fp2': + v = vector(value) + + result += inspect.cleandoc(f""" + {prefix}Fp2[{curve}].fromHex( {comment_right} + "0x{Integer(v[0]).hex()}", + "0x{Integer(v[1]).hex()}" + )""") + elif field == 'Fp': + result += inspect.cleandoc(f""" + {prefix}Fp[{curve}].fromHex( {comment_right} + "0x{Integer(value).hex()}") + """) + else: + raise NotImplementedError() + + return result + +def dump_poly(name, poly, field, curve): + result = f'const {name}* = [\n' + result += ' # Polynomial k₀ + k₁ x + k₂ x² + k₃ x³ + ... + kₙ xⁿ\n' + result += ' # The polynomial is stored as an array of coefficients ordered from k₀ to kₙ\n' + result += '\n' + + poly = list(poly) + lastRow = len(poly) - 1 + + for rowID, val in enumerate(reversed(poly)): + (coef, power) = val + result += textwrap.indent( + field_to_nim( + coef, field, curve, + comment_above = str(power) + ), + ' ') + result += ',\n' if rowID != lastRow else '\n' + + result += ']' + return result + +# Unused +# --------------------------------------------------------- + +def find_z_sswu(F, A, B): + """ + https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#ref-SAGE + Arguments: + - F, a field object, e.g., F = GF(2^521 - 1) + - A and B, the coefficients of the curve equation y² = x³ + A * x + B + """ + R. = F[] # Polynomial ring over F + g = xx^3 + F(A) * xx + F(B) # y² = g(x) = x³ + A * x + B + ctr = F.gen() + while True: + for Z_cand in (F(ctr), F(-ctr)): + if Z_cand.is_square(): + # Criterion 1: Z is non-square in F. + continue + if Z_cand == F(-1): + # Criterion 2: Z != -1 in F. + continue + if not (g - Z_cand).is_irreducible(): + # Criterion 3: g(x) - Z is irreducible over F. + continue + if g(B / (Z_cand * A)).is_square(): + # Criterion 4: g(B / (Z * A)) is square in F. + return Z_cand + ctr += 1 + +# BLS12-381 G2 +# --------------------------------------------------------- +# Hardcoding from spec: +# - https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-8.8.2 +# - https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/blob/f7dd3761/poc/sswu_opt_9mod16.sage#L142-L148 + +def genBLS12381G2_H2C_constants(curve_config): + curve_name = 'BLS12_381' + + # ------------------------------------------ + embdeg = curve_config[curve_name]['tower']['embedding_degree'] + twdeg = curve_config[curve_name]['tower']['twist_degree'] + g2field = f'Fp{embdeg//twdeg}' if (embdeg//twdeg) > 1 else 'Fp' + + p = curve_config[curve_name]['field']['modulus'] + Fp = GF(p) + K. = PolynomialRing(Fp) + if g2field == 'Fp2': + QNR_Fp = curve_config[curve_name]['tower']['QNR_Fp'] + Fp2. = Fp.extension(u^2 - QNR_Fp) + else: + SNR_Fp = curve_config[curve_name]['tower']['SNR_Fp'] + Fp2. = Fp.extension(u^2 - SNR_Fp) + # ------------------------------------------ + + # Hash to curve isogenous curve parameters + # y² = x³ + A'*x + B' + + print('\n----> Hash-to-Curve map to isogenous BLS12-381 E\'2 <----\n') + buf = inspect.cleandoc(f""" + # Hash-to-Curve map to isogenous BLS12-381 E'2 constants + # ----------------------------------------------------------------- + # + # y² = x³ + A'*x + B' with p² = q ≡ 9 (mod 16), p the BLS12-381 characteristic (base modulus) + # + # Hardcoding from spec: + # - https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-8.8.2 + # - https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/blob/f7dd3761/poc/sswu_opt_9mod16.sage#L142-L148 + """) + buf += '\n\n' + + # Base constants + Aprime_E2 = Fp2([0, 240]) + Bprime_E2 = Fp2([1012, 1012]) + Z = Fp2([-2, -1]) + # Extra + minus_A = -Aprime_E2 + ZmulA = Z * Aprime_E2 + inv_Z3 = (Z^3)^-1 # modular inverse of Z³ + (a, b) = vector(inv_Z3) + squared_norm_inv_Z3 = a^2 + b^2 # ||1/Z³||² + # x^((p-3)/4)) ≡ 1/√x (mod p) if p ≡ 3 (mod 4) + inv_norm_inv_Z3 = squared_norm_inv_Z3^((p-3)/4) # 1/||1/Z³|| + + buf += f'const {curve_name}_h2c_G2_Aprime_E2* = ' + buf += field_to_nim(Aprime_E2, 'Fp2', curve_name, comment_right = "240𝑖") + buf += '\n' + + buf += f'const {curve_name}_h2c_G2_Bprime_E2* = ' + buf += field_to_nim(Bprime_E2, 'Fp2', curve_name, comment_right = "1012 * (1 + 𝑖)") + buf += '\n' + + buf += f'const {curve_name}_h2c_G2_Z* = ' + buf += field_to_nim(Z, 'Fp2', curve_name, comment_right = "-(2 + 𝑖)") + buf += '\n' + + buf += f'const {curve_name}_h2c_G2_minusA* = ' + buf += field_to_nim(minus_A, 'Fp2', curve_name, comment_right = "-240𝑖") + buf += '\n' + + buf += f'const {curve_name}_h2c_G2_ZmulA* = ' + buf += field_to_nim(ZmulA, 'Fp2', curve_name, comment_right = "Z*A = 240-480𝑖") + buf += '\n' + + buf += f'const {curve_name}_h2c_G2_inv_Z3* = ' + buf += field_to_nim(inv_Z3, 'Fp2', curve_name, comment_right = "1/Z³") + buf += '\n' + + buf += f'const {curve_name}_h2c_G2_squared_norm_inv_Z3* = ' + buf += field_to_nim(squared_norm_inv_Z3, 'Fp', curve_name, comment_right = "||1/Z³||²") + buf += '\n' + + buf += f'const {curve_name}_h2c_G2_inv_norm_inv_Z3* = ' + buf += field_to_nim(inv_norm_inv_Z3, 'Fp', curve_name, comment_right = "1/||1/Z³||") + buf += '\n' + + return buf + +def genBLS12381G2_H2C_isogeny_map(curve_config): + curve_name = 'BLS12_381' + + # ------------------------------------------ + p = curve_config[curve_name]['field']['modulus'] + # This extension field construction + # does not work with isogenies :/ + # + # embdeg = curve_config[curve_name]['tower']['embedding_degree'] + # twdeg = curve_config[curve_name]['tower']['twist_degree'] + # g2field = f'Fp{embdeg//twdeg}' if (embdeg//twdeg) > 1 else 'Fp' + # + # Fp = GF(p) + # K. = PolynomialRing(Fp) + # if g2field == 'Fp2': + # QNR_Fp = curve_config[curve_name]['tower']['QNR_Fp'] + # Fp2. = Fp.extension(u^2 - QNR_Fp) + # else: + # SNR_Fp = curve_config[curve_name]['tower']['SNR_Fp'] + # Fp2. = Fp.extension(u^2 - SNR_Fp) + # ------------------------------------------ + + QNR_Fp = curve_config[curve_name]['tower']['QNR_Fp'] + Fp2. = GF(p^2, modulus=(x^2-QNR_Fp)) + + # Hash to curve isogenous curve parameters + # y² = x³ + A'*x + B' + + print('\n----> Hash-to-Curve 3-isogeny map BLS12-381 E\'2 constants <----\n') + buf = inspect.cleandoc(f""" + # Hash-to-Curve 3-isogeny map BLS12-381 E'2 constants + # ----------------------------------------------------------------- + # + # The polynomials map a point (x', y') on the isogenous curve E'2 + # to (x, y) on E2, represented as (xnum/xden, y' * ynum/yden) + + """) + buf += '\n\n' + + # Base constants - E2 + A = curve_config[curve_name]['curve']['a'] + B = curve_config[curve_name]['curve']['b'] + twist = curve_config[curve_name]['tower']['twist'] + SNR_Fp2 = curve_config[curve_name]['tower']['SNR_Fp2'] + + if twist == 'M_twist': + Btwist = B * Fp2(SNR_Fp2) + else: + Btwist = B / Fp2(SNR_Fp2) + + E2 = EllipticCurve(Fp2, [A, B * Fp2(SNR_Fp2)]) + + # Base constants - Isogenous curve E'2, degree 3 + Aprime_E2 = Fp2([0, 240]) + Bprime_E2 = Fp2([1012, 1012]) + Eprime2 = EllipticCurve(Fp2, [Aprime_E2, Bprime_E2]) + + iso_kernel = [6 * (1 - beta), 1] + iso = EllipticCurveIsogeny(E=Eprime2, kernel=iso_kernel, codomain=E2, degree=3) + if (- iso.rational_maps()[1])(1, 1) > iso.rational_maps()[1](1, 1): + iso.switch_sign() + + (xm, ym) = iso.rational_maps() + maps = (xm.numerator(), xm.denominator(), ym.numerator(), ym.denominator()) + + buf += dump_poly( + 'BLS12_381_h2c_G2_3_isogeny_map_xnum', + xm.numerator(), 'Fp2', curve_name) + buf += '\n' + buf += dump_poly( + 'BLS12_381_h2c_G2_3_isogeny_map_xden', + xm.denominator(), 'Fp2', curve_name) + buf += '\n' + buf += dump_poly( + 'BLS12_381_h2c_G2_3_isogeny_map_ynum', + ym.numerator(), 'Fp2', curve_name) + buf += '\n' + buf += dump_poly( + 'BLS12_381_h2c_G2_3_isogeny_map_yden', + ym.denominator(), 'Fp2', curve_name) + + return buf + +# CLI +# --------------------------------------------------------- + +if __name__ == "__main__": + # Usage + # BLS12-381 + # sage sage/derive_hash_to_curve.sage BLS12_381 G2 + + from argparse import ArgumentParser + + parser = ArgumentParser() + parser.add_argument("curve",nargs="+") + args = parser.parse_args() + + curve = args.curve[0] + group = args.curve[1] + + if curve == 'BLS12_381' and group == 'G2': + h2c = genBLS12381G2_H2C_constants(Curves) + h2c += '\n\n' + h2c += genBLS12381G2_H2C_isogeny_map(Curves) + + with open(f'{curve.lower()}_g2_hash_to_curve.nim', 'w') as f: + f.write(copyright()) + f.write('\n\n') + + f.write(inspect.cleandoc(""" + import + ../config/curves, + ../io/[io_fields, io_towers] + """)) + + f.write('\n\n') + f.write(h2c) + + print(f'Successfully created {curve.lower()}_g2_hash_to_curve.nim') + else: + raise ValueError( + curve + group + + ' is not configured ' + ) diff --git a/sage/derive_square_root.sage b/sage/derive_square_root.sage index 0589df0..4c33f08 100644 --- a/sage/derive_square_root.sage +++ b/sage/derive_square_root.sage @@ -136,9 +136,6 @@ if __name__ == "__main__": f.write(copyright()) f.write('\n\n') - embdeg = Curves[curve]['tower']['embedding_degree'] - twdeg = Curves[curve]['tower']['twist_degree'] - f.write(inspect.cleandoc(""" import ../config/curves, diff --git a/tests/t_hash_sha256_vs_openssl.nim b/tests/t_hash_sha256_vs_openssl.nim index 339b683..def82c3 100644 --- a/tests/t_hash_sha256_vs_openssl.nim +++ b/tests/t_hash_sha256_vs_openssl.nim @@ -1,6 +1,6 @@ import # Internals - ../constantine/hashes/h_sha256, + ../constantine/hashes, # Helpers ../helpers/prng_unsafe, # Third-party diff --git a/tests/t_hash_to_curve.nim b/tests/t_hash_to_curve.nim new file mode 100644 index 0000000..148543a --- /dev/null +++ b/tests/t_hash_to_curve.nim @@ -0,0 +1,156 @@ +# 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 + std/[unittest, times, os, strutils, macros], + # 3rd party + jsony, + # Internals + ../constantine/config/[common, curves, type_bigint, type_ff], + ../constantine/[towers, hashes], + ../constantine/io/[io_bigints, io_ec], + ../constantine/elliptic/[ + ec_shortweierstrass_affine, + ec_shortweierstrass_projective], + ../constantine/hash_to_curve/hash_to_curve + +# Serialization +# -------------------------------------------------------------------------- + +type + FieldDesc = object + m: string + p: string + + MapDesc = object + name: string + + HashToCurveTest[EC: ECP_ShortW_Aff] = object + L: string + Z: string + ciphersuite: string + curve: string + dst: string + expand: string + field: FieldDesc + hash: string + k: string + map: MapDesc + randomOracle: bool + vectors: seq[TestVector[EC]] + + TestVector*[EC: ECP_ShortW_Aff] = object + P: EC + Q0, Q1: EC + msg: string + u: seq[string] + + EC_G1_hex = object + x: string + y: string + + Fp2_hex = string + + EC_G2_hex = object + x: Fp2_hex + y: Fp2_hex + +const + TestVectorsDir* = + currentSourcePath.rsplit(DirSep, 1)[0] / "vectors" + +proc parseHook*(src: string, pos: var int, value: var ECP_ShortW_Aff) = + # Note when nim-serialization was used: + # When ECP_ShortW_Aff[Fp[Foo], NotOnTwist] + # and ECP_ShortW_Aff[Fp[Foo], OnTwist] + # are generated in the same file (i.e. twists and base curve are both on Fp) + # this creates bad codegen, in the C code, the `value`parameter gets the wrong type + # TODO: upstream + when ECP_ShortW_Aff.F is Fp: + var P: EC_G1_hex + parseHook(src, pos, P) + let ok = value.fromHex(P.x, P.y) + doAssert ok, "\nDeserialization error on G1 for\n" & + " P.x: " & P.x & "\n" & + " P.y: " & P.x & "\n" + elif ECP_ShortW_Aff.F is Fp2: + var P: EC_G2_hex + parseHook(src, pos, P) + let Px = P.x.split(',') + let Py = P.y.split(',') + + let ok = value.fromHex(Px[0], Px[1], Py[0], Py[1]) + doAssert ok, "\nDeserialization error on G2 for\n" & + " P.x0: " & Px[0] & "\n" & + " P.x1: " & Px[1] & "\n" & + " P.y0: " & Py[0] & "\n" & + " P.y1: " & Py[1] & "\n" + else: + {.error: "Not Implemented".} + +proc loadVectors(TestType: typedesc, filename: string): TestType = + let content = readFile(TestVectorsDir/filename) + result = content.fromJson(TestType) + +# Testing +# ------------------------------------------------------------------------ + +proc run_hash_to_curve_test( + EC: typedesc, + spec_version: string, + filename: string + ) = + + when EC.Tw == NotOnTwist: + const G1_or_G2 = "G1" + else: + const G1_or_G2 = "G2" + let vec = loadVectors(HashToCurveTest[ECP_ShortW_Aff[EC.F, EC.Tw]], filename) + + let testSuiteDesc = "Hash to Curve " & $EC.F.C & " " & G1_or_G2 & " - official specs " & spec_version & " test vectors" + + suite testSuiteDesc & " [" & $WordBitwidth & "-bit mode]": + + doAssert vec.hash == "sha256" + doAssert vec.k == "0x80" # 128 + + for i in 0 ..< vec.vectors.len: + test "test " & $i & " - msg: \'" & vec.vectors[i].msg & "\'": + var P{.noInit.}: EC + sha256.hashToCurve( + k = 128, + output = P, + augmentation = "", + message = vec.vectors[i].msg, + domainSepTag = vec.dst + ) + + var P_ref: EC + P_ref.projectiveFromAffine(vec.vectors[i].P) + + doAssert: bool(P == P_ref) + +echo "\n------------------------------------------------------\n" +echo "Hash-to-curve" & '\n' + +# Hash-to-curve v8 to latest +# https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/blob/draft-irtf-cfrg-hash-to-curve-10/poc/vectors/BLS12381G2_XMD:SHA-256_SSWU_RO_.json +run_hash_to_curve_test( + ECP_ShortW_Prj[Fp2[BLS12_381], OnTwist], + "v8", + "tv_h2c_v8_BLS12_381_hash_to_G2_SHA256_SSWU_RO.json" +) + +# Hash-to-curve v7 (different domain separation tag) +# https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/blob/draft-irtf-cfrg-hash-to-curve-07/poc/vectors/BLS12381G2_XMD:SHA-256_SSWU_RO_.json +run_hash_to_curve_test( + ECP_ShortW_Prj[Fp2[BLS12_381], OnTwist], + "v7", + "tv_h2c_v7_BLS12_381_hash_to_G2_SHA256_SSWU_RO.json" +) diff --git a/tests/t_hash_to_field.nim b/tests/t_hash_to_field.nim new file mode 100644 index 0000000..5f2e6c6 --- /dev/null +++ b/tests/t_hash_to_field.nim @@ -0,0 +1,184 @@ +# 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 + ../constantine/hashes, + ../constantine/hash_to_curve/h2c_hash_to_field, + ../constantine/config/[curves_declaration, type_ff], + ../constantine/tower_field_extensions/extension_fields, + + ../constantine/io/[io_fields, io_towers], + + # Third-party + stew/byteutils + +# Test vectors for expandMessageXMD +# ---------------------------------------------------------------------- + +template testExpandMessageXMD(id, constants: untyped) = + # Section "Expand test vectors {#expand-testvectors}" + # https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-09#appendix-I.1 + proc `testExpandMessageXMD_sha256 _ id`() = + # We create a proc to avoid allocating to much globals/ + constants + + var uniform_bytes: array[len_in_bytes, byte] + sha256.expandMessageXMD( + uniform_bytes, + augmentation = "", + msg, + "QUUX-V01-CS02-with-expander" + ) + + doAssert uniform_bytes == expectedBytes, ( "\n" & + "Expected " & toHex(expectedBytes) & "\n" & + "Computed " & toHex(uniform_bytes) + ) + + echo "Success sha256.expandMessageXMD ", astToStr(id) + + `testExpandMessageXMD_sha256 _ id`() + +testExpandMessageXMD(1): + let msg = "" + const expected = "f659819a6473c1835b25ea59e3d38914c98b374f0970b7e4c92181df928fca88" + const len_in_bytes = expected.len div 2 + const expectedBytes = hexToByteArray[len_in_bytes](expected) + +testExpandMessageXMD(2): + let msg = "abc" + const expected = "1c38f7c211ef233367b2420d04798fa4698080a8901021a795a1151775fe4da7" + const len_in_bytes = expected.len div 2 + const expectedBytes = hexToByteArray[len_in_bytes](expected) + +testExpandMessageXMD(3): + let msg = "abcdef0123456789" + const expected = "8f7e7b66791f0da0dbb5ec7c22ec637f79758c0a48170bfb7c4611bd304ece89" + const len_in_bytes = expected.len div 2 + const expectedBytes = hexToByteArray[len_in_bytes](expected) + +testExpandMessageXMD(4): + let msg = "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq" & + "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq" & + "qqqqqqqqqqqqqqqqqqqqqqqqq" + const expected = "72d5aa5ec810370d1f0013c0df2f1d65699494ee2a39f72e1716b1b964e1c642" + const len_in_bytes = expected.len div 2 + const expectedBytes = hexToByteArray[len_in_bytes](expected) + +testExpandMessageXMD(5): + let msg = "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + const expected = "3b8e704fc48336aca4c2a12195b720882f2162a4b7b13a9c350db46f429b771b" + const len_in_bytes = expected.len div 2 + const expectedBytes = hexToByteArray[len_in_bytes](expected) + +testExpandMessageXMD(6): + let msg = "" + const expected = "8bcffd1a3cae24cf9cd7ab85628fd111bb17e3739d3b53f8" & + "9580d217aa79526f1708354a76a402d3569d6a9d19ef3de4d0b991" & + "e4f54b9f20dcde9b95a66824cbdf6c1a963a1913d43fd7ac443a02" & + "fc5d9d8d77e2071b86ab114a9f34150954a7531da568a1ea8c7608" & + "61c0cde2005afc2c114042ee7b5848f5303f0611cf297f" + const len_in_bytes = expected.len div 2 + const expectedBytes = hexToByteArray[len_in_bytes](expected) + +testExpandMessageXMD(7): + let msg = "abc" + const expected = "fe994ec51bdaa821598047b3121c149b364b178606d5e72b" & + "fbb713933acc29c186f316baecf7ea22212f2496ef3f785a27e84a" & + "40d8b299cec56032763eceeff4c61bd1fe65ed81decafff4a31d01" & + "98619c0aa0c6c51fca15520789925e813dcfd318b542f879944127" & + "1f4db9ee3b8092a7a2e8d5b75b73e28fb1ab6b4573c192" + const len_in_bytes = expected.len div 2 + const expectedBytes = hexToByteArray[len_in_bytes](expected) + +testExpandMessageXMD(8): + let msg = "abcdef0123456789" + const expected = "c9ec7941811b1e19ce98e21db28d22259354d4d0643e3011" & + "75e2f474e030d32694e9dd5520dde93f3600d8edad94e5c3649030" & + "88a7228cc9eff685d7eaac50d5a5a8229d083b51de4ccc3733917f" & + "4b9535a819b445814890b7029b5de805bf62b33a4dc7e24acdf2c9" & + "24e9fe50d55a6b832c8c84c7f82474b34e48c6d43867be" + const len_in_bytes = expected.len div 2 + const expectedBytes = hexToByteArray[len_in_bytes](expected) + +testExpandMessageXMD(9): + let msg = "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq" & + "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq" & + "qqqqqqqqqqqqqqqqqqqqqqqqq" + const expected = "48e256ddba722053ba462b2b93351fc966026e6d6db49318" & + "9798181c5f3feea377b5a6f1d8368d7453faef715f9aecb078cd40" & + "2cbd548c0e179c4ed1e4c7e5b048e0a39d31817b5b24f50db58bb3" & + "720fe96ba53db947842120a068816ac05c159bb5266c63658b4f00" & + "0cbf87b1209a225def8ef1dca917bcda79a1e42acd8069" + const len_in_bytes = expected.len div 2 + const expectedBytes = hexToByteArray[len_in_bytes](expected) + +testExpandMessageXMD(10): + let msg = "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + const expected = "396962db47f749ec3b5042ce2452b619607f27fd3939ece2" & + "746a7614fb83a1d097f554df3927b084e55de92c7871430d6b95c2" & + "a13896d8a33bc48587b1f66d21b128a1a8240d5b0c26dfe795a1a8" & + "42a0807bb148b77c2ef82ed4b6c9f7fcb732e7f94466c8b51e52bf" & + "378fba044a31f5cb44583a892f5969dcd73b3fa128816e" + const len_in_bytes = expected.len div 2 + const expectedBytes = hexToByteArray[len_in_bytes](expected) + +template testHashToField(id, constants: untyped) = + # Section "Expand test vectors {#expand-testvectors}" + # https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-09#appendix-I.1 + proc `testHashToField_sha256 _ id`() = + # We create a proc to avoid allocating to much globals/ + constants + + var output: array[2, Fp2[BLS12_381]] + sha256.hashToField( + k = 128, + output, + augmentation = "", + msg, + "QUUX-V01-CS02-with-BLS12381G2_XMD:SHA-256_SSWU_RO_" + ) + + doAssert output[0].c0.toHex() == "0x" & u0_re + doAssert output[0].c1.toHex() == "0x" & u0_im + doAssert output[1].c0.toHex() == "0x" & u1_re + doAssert output[1].c1.toHex() == "0x" & u1_im + + echo "Success hashToField BLS12381G2_XMD:SHA-256_SSWU_RO - ", astToStr(id) + + `testHashToField_sha256 _ id`() + +testHashToField(1): + let + msg = "" + u0_re = "03dbc2cce174e91ba93cbb08f26b917f98194a2ea08d1cce75b2b9" & + "cc9f21689d80bd79b594a613d0a68eb807dfdc1cf8" + u0_im = "05a2acec64114845711a54199ea339abd125ba38253b70a92c876d" & + "f10598bd1986b739cad67961eb94f7076511b3b39a" + u1_re = "02f99798e8a5acdeed60d7e18e9120521ba1f47ec090984662846b" & + "c825de191b5b7641148c0dbc237726a334473eee94" + u1_im = "145a81e418d4010cc027a68f14391b30074e89e60ee7a22f87217b" & + "2f6eb0c4b94c9115b436e6fa4607e95a98de30a435" diff --git a/tests/vectors/tv_h2c_v7_BLS12_381_hash_to_G2_SHA256_SSWU_RO.json b/tests/vectors/tv_h2c_v7_BLS12_381_hash_to_G2_SHA256_SSWU_RO.json new file mode 100644 index 0000000..69a8c29 --- /dev/null +++ b/tests/vectors/tv_h2c_v7_BLS12_381_hash_to_G2_SHA256_SSWU_RO.json @@ -0,0 +1,96 @@ +{ + "L": "0x40", + "Z": "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaa9,0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa", + "ciphersuite": "BLS12381G2_XMD:SHA-256_SSWU_RO_", + "curve": "BLS12381G2", + "dst": "BLS12381G2_XMD:SHA-256_SSWU_RO_TESTGEN", + "expand": "XMD", + "field": { + "m": "0x2", + "p": "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab" + }, + "hash": "sha256", + "k": "0x80", + "map": { + "name": "SSWU" + }, + "randomOracle": true, + "vectors": [ + { + "P": { + "x": "0x0a650bd36ae7455cb3fe5d8bb1310594551456f5c6593aec9ee0c03d2f6cb693bd2c5e99d4e23cbaec767609314f51d3,0x0fbdae26f9f9586a46d4b0b70390d09064ef2afe5c99348438a3c7d9756471e015cb534204c1b6824617a85024c772dc", + "y": "0x0d8d49e7737d8f9fc5cef7c4b8817633103faf2613016cb86a1f3fc29968fe2413e232d9208d2d74a89bf7a48ac36f83,0x02e5cf8f9b7348428cc9e66b9a9b36fe45ba0b0a146290c3a68d92895b1af0e1f2d9f889fb412670ae8478d8abd4c5aa" + }, + "Q0": { + "x": "0x090f7997311a1d4ec54520f81046063f4e9e7a64570133dc41c3600ade2a4d21aae59714cf290f95f90a98b658f5b64a,0x08427a6a0dc88a36698823d07ab25d11f95a9508cb5bb1ad2bd57bc02b5efb8c7b1da66ed02b0f915002446e24fd5d38", + "y": "0x10e03a54fd5ff7a0a69543aeeef42e22cb589e0b33455943cf84f0c5b28e93fe17c0bbba2fafb10aea29b28705eec303,0x053b939496e87877fb1569c911bf618056396fac2458757da71cd83fa152239d605c6a4e4e847295080ea3874f84a832" + }, + "Q1": { + "x": "0x0df5643a19f8de7e8e45575551cfb8909f4a75722ec8fbc43cb8df284cdde9e2c61ea0c6116bdd86d84063c96fc7dc7f,0x1241a410598f1d57907850699a694720712feddb916f343db08f2c18481df46cbdf7afe8eaf214127e427736ea281c5b", + "y": "0x0ad66ed30cb6f55a83feed4b12c141bd41f593292403127b07e1bc6dabacd8ea53f8a322b5d4080e4393184c713865fa,0x0c4e6fb11ad2fe3a081a399df36094465aafb232f7564f4d35abb0092ef9ee855bcfdac2e6775cd7d383241f13ed856a" + }, + "msg": "", + "u": [ + "0x0ae8ca9aed945924c3a12f3b6f419cac381bae8f16044ab6c66b41999e4bd0ea169b44f2fce3634a0ddea05b9186c6b2,0x1134506e471554affe377f908c29fc7cd7d247b3a14f9e092b9f4c5b02577939ce01bd6b43d9d59d9a994e9fb5fb5096", + "0x0b28b14113885b1d8ad08f5da9111add00d8c496fb3d5d7b5d3b6558a058e9e62cd02dafa7a95f968cb3063f09fc0e21,0x03378e456f437ce445b6bc95121566d85b1b3b8ca057064fe7a8a1aad7e8a6e9f886cfb1704ad712e9042f4f002f4bd1" + ] + }, + { + "P": { + "x": "0x1953ce6d4267939c7360756d9cca8eb34aac4633ef35369a7dc249445069888e7d1b3f9d2e75fbd468fbcbba7110ea02,0x03578447618463deb106b60e609c6f7cc446dc6035f84a72801ba17c94cd800583b493b948eff0033f09086fdd7f6175", + "y": "0x0882ab045b8fe4d7d557ebb59a63a35ac9f3d312581b509af0f8eaa2960cbc5e1e36bb969b6e22980b5cbdd0787fcf4e,0x0184d26779ae9d4670aca9b267dbd4d3b30443ad05b8546d36a195686e1ccc3a59194aea05ed5bce7c3144a29ec047c4" + }, + "Q0": { + "x": "0x0c292ac371849207564e7b8f4edf47dc4b4d7a618dbacf6a322dc732f014cc2a22049eb69de11657c301cb4202b98541,0x0f37118e477c16005cae8f639e54119ff796eafe80461bf39ecce5c0192b93075febc80d4f73f9e0893adafa17b13b45", + "y": "0x15853304d7fd9f47df2ef6c4bd1fb0b3500386b23d1acc530be0c14e027f15b0aa83856d82edb723f3d857358ecffb80,0x0626fcfc6b3d8460df7ed2aeca6449cf6701dc7ff51c143ed20054ecf18732f4c5985455864c79a4065b13e26ecccf9f" + }, + "Q1": { + "x": "0x0bce3e2dd15f6acf55cce0e3a4cde190a6d45434a8b0ba7cf79ff37f737ed90dbfd2988a257db65e10e684e5876b50db,0x19c1ad3eb0abb3590087d706eb155a4cd166484e82cdccb2465ce193b15a27d919aaa37d1824a9a9d87f31fefca1baee", + "y": "0x110c9643a8dfd00123bb9e6a956426f26bedb0d430130026ce49b862431e80f5e306850239c857474f564915fc9a4ba6,0x1748ca13032a2c262295863897a15cd9a7e0baf003336bec6fc6e40b982d866fe3250619fdd2ceadb49fab8055f47e65" + }, + "msg": "abc", + "u": [ + "0x0a7d239c9bdb41ed2ad810820a8b4f0703f60cf5833440cd684e386e235b0f092da91adbaa69562b911ebd3f820655f2,0x16302b56f5a9f538c7168cd5194957903b82be6749171f8de112c8bd3360ca24847d0567d6e42eae0c43a7fd8530b378", + "0x0a1cb4196dec71b1f704f3533cdf27f247e3ea175ddcc1ca6df0f45c587eb77efc6c493848f4df98e24a32753dfcf96b,0x07aac42db7f3dfbc5146c70ca0ac6157893abf4e2162e303510e0cefb8d024c24080b9c2a9896f6c03ffe680fc18b788" + ] + }, + { + "P": { + "x": "0x17b461fc3b96a30c2408958cbfa5f5927b6063a8ad199d5ebf2d7cdeffa9c20c85487204804fab53f950b2f87db365aa,0x195fad48982e186ce3c5c82133aefc9b26d55979b6f530992a8849d4263ec5d57f7a181553c8799bcc83da44847bdc8d", + "y": "0x174a3473a3af2d0302b9065e895ca4adba4ece6ce0b41148ba597001abb152f852dd9a96fb45c9de0a43d944746f833e,0x005cdf3d984e3391e7e969276fb4bc02323c5924a4449af167030d855acc2600cf3d4fab025432c6d868c79571a95bef" + }, + "Q0": { + "x": "0x1552566a422494f9edd07e21ee59067ecf031f333b3961b710fac1245fd003552c294ac47ef982432f0f1e1e9d07c4b6,0x115a9de418d20ce3105eaa2db025d183cc679327c6d6a229960d536b9fce33d3242f9819680a9200265ec2dd02b44b19", + "y": "0x0cef664ee9270354c3bc06d1e0570e4d6663cc528711afca10118955990126f87917c87f7b9c4cf73aaf05c1b5875c6f,0x0b136f41d233ea420bc3658c4156f717fb190775d3690d139c0923c231e44af54d780119b8edf16038208b63feb1f3ee" + }, + "Q1": { + "x": "0x0332d5027c68f38ca78c6c63c013178fb58b31283a6135f6bf5629d18c76144accfd96905f51a49284f4ef622dfec003,0x04865f680c5f2203de00f95dd6652c9b3dc0d36361ee0df16a39a86d5f7cfc8df3674f3c3fddde88fb027353eac1a3dc", + "y": "0x1651e6cc8af2241989a9006dd59a9cd41fc1bbc3a7f9e32875889ae54913b8398dfa106aff43ff1cfa9019141d9ad565,0x09324bdbfedfb886899a7961f7827702743ef550f548bb89ab15d4b24c7c086196891fc300e3e39c21aec0257543a3fd" + }, + "msg": "abcdef0123456789", + "u": [ + "0x0e17df0242a3dd0e7454a4b580cafdc956650736b45181b329ca89ee2348570a1d7a221554c7122b91e6e3c3525d396d,0x0298e9fa0ff37440cd2862e91c0a27fed05087247acf79232f1a4eb7cf8f65997a92319a8cbd00f7b73ee9e82241eade", + "0x1200056764f11beacdb6009acaf823e100da27b4bfe45e94097a52c1fed615b32dbc5503f964ab5277a7c30d9a2bf0de,0x0d1d7feb418f29dbf4d4459c839dd33f904d4292d016f701b35e4a7611798c83de1b7deb1c6c1521e9142cc36a7d0579" + ] + }, + { + "P": { + "x": "0x0a162306f3b0f2bb326f0c4fb0e1fea020019c3af796dcd1d7264f50ddae94cacf3cade74603834d44b9ab3d5d0a6c98,0x123b6bd9feeba26dd4ad00f8bfda2718c9700dc093ea5287d7711844644eb981848316d3f3f57d5d3a652c6cdc816aca", + "y": "0x15c1d4f1a685bb63ee67ca1fd96155e3d091e852a684b78d085fd34f6091e5249ddddbdcf2e7ec82ce6c04c63647eeb7,0x05483f3b96d9252dd4fc0868344dfaf3c9d145e3387db23fa8e449304fab6a7b6ec9c15f05c0a1ea66ff0efcc03e001a" + }, + "Q0": { + "x": "0x089b04f318946ce75b5b8c98607041488005ed412a4a99e7106b340427d35682036cecc076827e700e47c17f65ee3f09,0x03bef411c75f97147673952b19ee293e28df019be2fdecf5db09afb7caad4a5e984750b19c2007b50ae0b26f83088e8b", + "y": "0x18b1ef96738c5df727e1fa2098178fe371751c0c169af30bdb95be22a0ecbf0a75c0e6c63e4a32f241250f877859c086,0x0d04c624db798ca46a352637fa76516c83a5d98e147a25f629fb1e02a9a453970e42d835ba765bd7d94a4a3f9f50e4a1" + }, + "Q1": { + "x": "0x121b1257fbd3dda5f478b5de6aee2ca88780248c59afad1a9c9c9db5d03752792270cecc7cc676a1b91ee898b7f76977,0x17eadb5c134a1cc0305ad5d99f6e2a1cd906a2fdac318d4356527c70fc94242ddb664486c814ebd5959a2cf4225a783a", + "y": "0x00f0793bcfaf12e5d23fdd4173f7539e3cf182a0f5a1c98b488f59daca5ecf7b694912a93f6b81498a5c2282c09ee63f,0x081adf3c45b42c35fdb678c8bdec1d8c12f9d5a30b22cf52c1afc967d6ddc82fdae0673f76a5186a84f3602c7a22f6b8" + }, + "msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "u": [ + "0x0ca92554c8c45581eac2eed7ec2db1fe757af0a2803dc8e63180600eed2516f64b1c0d850c72a75c417f58723815795b,0x12ef692f69b1d61854b80e071c7fd751b19da2c194ba0fbee9e68454073dd3693e2c56852938aa1b090991018ff15a94", + "0x11043d352059287fe7424285da213d4cc414df4d5592ee2507503088b3f89220697753ea8cd47fa13c9a15dbfb0ef20c,0x110efeacfff2801024c019cee7adbc3d8144c3b73c548ad8f0759c4976e0b3070293056f884dc0a1b3728546dddc6bcb" + ] + } + ] +} diff --git a/tests/vectors/tv_h2c_v8_BLS12_381_hash_to_G2_SHA256_SSWU_RO.json b/tests/vectors/tv_h2c_v8_BLS12_381_hash_to_G2_SHA256_SSWU_RO.json new file mode 100644 index 0000000..5807ee6 --- /dev/null +++ b/tests/vectors/tv_h2c_v8_BLS12_381_hash_to_G2_SHA256_SSWU_RO.json @@ -0,0 +1,115 @@ +{ + "L": "0x40", + "Z": "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaa9,0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa", + "ciphersuite": "BLS12381G2_XMD:SHA-256_SSWU_RO_", + "curve": "BLS12-381 G2", + "dst": "QUUX-V01-CS02-with-BLS12381G2_XMD:SHA-256_SSWU_RO_", + "expand": "XMD", + "field": { + "m": "0x2", + "p": "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab" + }, + "hash": "sha256", + "k": "0x80", + "map": { + "name": "SSWU" + }, + "randomOracle": true, + "vectors": [ + { + "P": { + "x": "0x0141ebfbdca40eb85b87142e130ab689c673cf60f1a3e98d69335266f30d9b8d4ac44c1038e9dcdd5393faf5c41fb78a,0x05cb8437535e20ecffaef7752baddf98034139c38452458baeefab379ba13dff5bf5dd71b72418717047f5b0f37da03d", + "y": "0x0503921d7f6a12805e72940b963c0cf3471c7b2a524950ca195d11062ee75ec076daf2d4bc358c4b190c0c98064fdd92,0x12424ac32561493f3fe3c260708a12b7c620e7be00099a974e259ddc7d1f6395c3c811cdd19f1e8dbf3e9ecfdcbab8d6" + }, + "Q0": { + "x": "0x019ad3fc9c72425a998d7ab1ea0e646a1f6093444fc6965f1cad5a3195a7b1e099c050d57f45e3fa191cc6d75ed7458c,0x171c88b0b0efb5eb2b88913a9e74fe111a4f68867b59db252ce5868af4d1254bfab77ebde5d61cd1a86fb2fe4a5a1c1d", + "y": "0x0ba10604e62bdd9eeeb4156652066167b72c8d743b050fb4c1016c31b505129374f76e03fa127d6a156213576910fef3,0x0eb22c7a543d3d376e9716a49b72e79a89c9bfe9feee8533ed931cbb5373dde1fbcd7411d8052e02693654f71e15410a" + }, + "Q1": { + "x": "0x113d2b9cd4bd98aee53470b27abc658d91b47a78a51584f3d4b950677cfb8a3e99c24222c406128c91296ef6b45608be,0x13855912321c5cb793e9d1e88f6f8d342d49c0b0dbac613ee9e17e3c0b3c97dfbb5a49cc3fb45102fdbaf65e0efe2632", + "y": "0x0fd3def0b7574a1d801be44fde617162aa2e89da47f464317d9bb5abc3a7071763ce74180883ad7ad9a723a9afafcdca,0x056f617902b3c0d0f78a9a8cbda43a26b65f602f8786540b9469b060db7b38417915b413ca65f875c130bebfaa59790c" + }, + "msg": "", + "u": [ + "0x03dbc2cce174e91ba93cbb08f26b917f98194a2ea08d1cce75b2b9cc9f21689d80bd79b594a613d0a68eb807dfdc1cf8,0x05a2acec64114845711a54199ea339abd125ba38253b70a92c876df10598bd1986b739cad67961eb94f7076511b3b39a", + "0x02f99798e8a5acdeed60d7e18e9120521ba1f47ec090984662846bc825de191b5b7641148c0dbc237726a334473eee94,0x145a81e418d4010cc027a68f14391b30074e89e60ee7a22f87217b2f6eb0c4b94c9115b436e6fa4607e95a98de30a435" + ] + }, + { + "P": { + "x": "0x02c2d18e033b960562aae3cab37a27ce00d80ccd5ba4b7fe0e7a210245129dbec7780ccc7954725f4168aff2787776e6,0x139cddbccdc5e91b9623efd38c49f81a6f83f175e80b06fc374de9eb4b41dfe4ca3a230ed250fbe3a2acf73a41177fd8", + "y": "0x1787327b68159716a37440985269cf584bcb1e621d3a7202be6ea05c4cfe244aeb197642555a0645fb87bf7466b2ba48,0x00aa65dae3c8d732d10ecd2c50f8a1baf3001578f71c694e03866e9f3d49ac1e1ce70dd94a733534f106d4cec0eddd16" + }, + "Q0": { + "x": "0x12b2e525281b5f4d2276954e84ac4f42cf4e13b6ac4228624e17760faf94ce5706d53f0ca1952f1c5ef75239aeed55ad,0x05d8a724db78e570e34100c0bc4a5fa84ad5839359b40398151f37cff5a51de945c563463c9efbdda569850ee5a53e77", + "y": "0x02eacdc556d0bdb5d18d22f23dcb086dd106cad713777c7e6407943edbe0b3d1efe391eedf11e977fac55f9b94f2489c,0x04bbe48bfd5814648d0b9e30f0717b34015d45a861425fabc1ee06fdfce36384ae2c808185e693ae97dcde118f34de41" + }, + "Q1": { + "x": "0x19f18cc5ec0c2f055e47c802acc3b0e40c337256a208001dde14b25afced146f37ea3d3ce16834c78175b3ed61f3c537,0x15b0dadc256a258b4c68ea43605dffa6d312eef215c19e6474b3e101d33b661dfee43b51abbf96fee68fc6043ac56a58", + "y": "0x05e47c1781286e61c7ade887512bd9c2cb9f640d3be9cf87ea0bad24bd0ebfe946497b48a581ab6c7d4ca74b5147287f,0x19f98db2f4a1fcdf56a9ced7b320ea9deecf57c8e59236b0dc21f6ee7229aa9705ce9ac7fe7a31c72edca0d92370c096" + }, + "msg": "abc", + "u": [ + "0x15f7c0aa8f6b296ab5ff9c2c7581ade64f4ee6f1bf18f55179ff44a2cf355fa53dd2a2158c5ecb17d7c52f63e7195771,0x01c8067bf4c0ba709aa8b9abc3d1cef589a4758e09ef53732d670fd8739a7274e111ba2fcaa71b3d33df2a3a0c8529dd", + "0x187111d5e088b6b9acfdfad078c4dacf72dcd17ca17c82be35e79f8c372a693f60a033b461d81b025864a0ad051a06e4,0x08b852331c96ed983e497ebc6dee9b75e373d923b729194af8e72a051ea586f3538a6ebb1e80881a082fa2b24df9f566" + ] + }, + { + "P": { + "x": "0x121982811d2491fde9ba7ed31ef9ca474f0e1501297f68c298e9f4c0028add35aea8bb83d53c08cfc007c1e005723cd0,0x190d119345b94fbd15497bcba94ecf7db2cbfd1e1fe7da034d26cbba169fb3968288b3fafb265f9ebd380512a71c3f2c", + "y": "0x05571a0f8d3c08d094576981f4a3b8eda0a8e771fcdcc8ecceaf1356a6acf17574518acb506e435b639353c2e14827c8,0x0bb5e7572275c567462d91807de765611490205a941a5a6af3b1691bfe596c31225d3aabdf15faff860cb4ef17c7c3be" + }, + "Q0": { + "x": "0x0f48f1ea1318ddb713697708f7327781fb39718971d72a9245b9731faaca4dbaa7cca433d6c434a820c28b18e20ea208,0x06051467c8f85da5ba2540974758f7a1e0239a5981de441fdd87680a995649c211054869c50edbac1f3a86c561ba3162", + "y": "0x168b3d6df80069dbbedb714d41b32961ad064c227355e1ce5fac8e105de5e49d77f0c64867f3834848f152497eb76333,0x134e0e8331cee8cb12f9c2d0742714ed9eee78a84d634c9a95f6a7391b37125ed48bfc6e90bf3546e99930ff67cc97bc" + }, + "Q1": { + "x": "0x004fd03968cd1c99a0dd84551f44c206c84dcbdb78076c5bfee24e89a92c8508b52b88b68a92258403cbe1ea2da3495f,0x1674338ea298281b636b2eb0fe593008d03171195fd6dcd4531e8a1ed1f02a72da238a17a635de307d7d24aa2d969a47", + "y": "0x0dc7fa13fff6b12558419e0a1e94bfc3cfaf67238009991c5f24ee94b632c3d09e27eca329989aee348a67b50d5e236c,0x169585e164c131103d85324f2d7747b23b91d66ae5d947c449c8194a347969fc6bbd967729768da485ba71868df8aed2" + }, + "msg": "abcdef0123456789", + "u": [ + "0x0313d9325081b415bfd4e5364efaef392ecf69b087496973b229303e1816d2080971470f7da112c4eb43053130b785e1,0x062f84cb21ed89406890c051a0e8b9cf6c575cf6e8e18ecf63ba86826b0ae02548d83b483b79e48512b82a6c0686df8f", + "0x1739123845406baa7be5c5dc74492051b6d42504de008c635f3535bb831d478a341420e67dcc7b46b2e8cba5379cca97,0x01897665d9cb5db16a27657760bbea7951f67ad68f8d55f7113f24ba6ddd82caef240a9bfa627972279974894701d975" + ] + }, + { + "P": { + "x": "0x19a84dd7248a1066f737cc34502ee5555bd3c19f2ecdb3c7d9e24dc65d4e25e50d83f0f77105e955d78f4762d33c17da,0x0934aba516a52d8ae479939a91998299c76d39cc0c035cd18813bec433f587e2d7a4fef038260eef0cef4d02aae3eb91", + "y": "0x14f81cd421617428bc3b9fe25afbb751d934a00493524bc4e065635b0555084dd54679df1536101b2c979c0152d09192,0x09bcccfa036b4847c9950780733633f13619994394c23ff0b32fa6b795844f4a0673e20282d07bc69641cee04f5e5662" + }, + "Q0": { + "x": "0x09eccbc53df677f0e5814e3f86e41e146422834854a224bf5a83a50e4cc0a77bfc56718e8166ad180f53526ea9194b57,0x0c3633943f91daee715277bd644fba585168a72f96ded64fc5a384cce4ec884a4c3c30f08e09cd2129335dc8f67840ec", + "y": "0x0eb6186a0457d5b12d132902d4468bfeb7315d83320b6c32f1c875f344efcba979952b4aa418589cb01af712f98cc555,0x119e3cf167e69eb16c1c7830e8df88856d48be12e3ff0a40791a5cd2f7221311d4bf13b1847f371f467357b3f3c0b4c7" + }, + "Q1": { + "x": "0x0eb3aabc1ddfce17ff18455fcc7167d15ce6b60ddc9eb9b59f8d40ab49420d35558686293d046fc1e42f864b7f60e381,0x198bdfb19d7441ebcca61e8ff774b29d17da16547d2c10c273227a635cacea3f16826322ae85717630f0867539b5ed8b", + "y": "0x0aaf1dee3adf3ed4c80e481c09b57ea4c705e1b8d25b897f0ceeec3990748716575f92abff22a1c8f4582aff7b872d52,0x0d058d9061ed27d4259848a06c96c5ca68921a5d269b078650c882cb3c2bd424a8702b7a6ee4e0ead9982baf6843e924" + }, + "msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", + "u": [ + "0x025820cefc7d06fd38de7d8e370e0da8a52498be9b53cba9927b2ef5c6de1e12e12f188bbc7bc923864883c57e49e253,0x034147b77ce337a52e5948f66db0bab47a8d038e712123bb381899b6ab5ad20f02805601e6104c29df18c254b8618c7b", + "0x0930315cae1f9a6017c3f0c8f2314baa130e1cf13f6532bff0a8a1790cd70af918088c3db94bda214e896e1543629795,0x10c4df2cacf67ea3cb3108b00d4cbd0b3968031ebc8eac4b1ebcefe84d6b715fde66bef0219951ece29d1facc8a520ef" + ] + }, + { + "P": { + "x": "0x01a6ba2f9a11fa5598b2d8ace0fbe0a0eacb65deceb476fbbcb64fd24557c2f4b18ecfc5663e54ae16a84f5ab7f62534,0x11fca2ff525572795a801eed17eb12785887c7b63fb77a42be46ce4a34131d71f7a73e95fee3f812aea3de78b4d01569", + "y": "0x0b6798718c8aed24bc19cb27f866f1c9effcdbf92397ad6448b5c9db90d2b9da6cbabf48adc1adf59a1a28344e79d57e,0x03a47f8e6d1763ba0cad63d6114c0accbef65707825a511b251a660a9b3994249ae4e63fac38b23da0c398689ee2ab52" + }, + "Q0": { + "x": "0x17cadf8d04a1a170f8347d42856526a24cc466cb2ddfd506cff01191666b7f944e31244d662c904de5440516a2b09004,0x0d13ba91f2a8b0051cf3279ea0ee63a9f19bc9cb8bfcc7d78b3cbd8cc4fc43ba726774b28038213acf2b0095391c523e", + "y": "0x17ef19497d6d9246fa94d35575c0f8d06ee02f21a284dbeaa78768cb1e25abd564e3381de87bda26acd04f41181610c5,0x12c3c913ba4ed03c24f0721a81a6be7430f2971ffca8fd1729aafe496bb725807531b44b34b59b3ae5495e5a2dcbd5c8" + }, + "Q1": { + "x": "0x16ec57b7fe04c71dfe34fb5ad84dbce5a2dbbd6ee085f1d8cd17f45e8868976fc3c51ad9eeda682c7869024d24579bfd,0x13103f7aace1ae1420d208a537f7d3a9679c287208026e4e3439ab8cd534c12856284d95e27f5e1f33eec2ce656533b0", + "y": "0x0958b2c4c2c10fcef5a6c59b9e92c4a67b0fae3e2e0f1b6b5edad9c940b8f3524ba9ebbc3f2ceb3cfe377655b3163bd7,0x0ccb594ed8bd14ca64ed9cb4e0aba221be540f25dd0d6ba15a4a4be5d67bcf35df7853b2d8dad3ba245f1ea3697f66aa" + }, + "msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "u": [ + "0x190b513da3e66fc9a3587b78c76d1d132b1152174d0b83e3c1114066392579a45824c5fa17649ab89299ddd4bda54935,0x12ab625b0fe0ebd1367fe9fac57bb1168891846039b4216b9d94007b674de2d79126870e88aeef54b2ec717a887dcf39", + "0x0e6a42010cf435fb5bacc156a585e1ea3294cc81d0ceb81924d95040298380b164f702275892cedd81b62de3aba3f6b5,0x117d9a0defc57a33ed208428cb84e54c85a6840e7648480ae428838989d25d97a0af8e3255be62b25c2a85630d2dddd8" + ] + } + ] +}