Hash to curve - BLS12-381 (#110)

* Hash to Curve: impl expand_message_xmd

* Try to precompute part of hash to curve at compile-time

* sha256 bench - use the new hashes module

* [WIP] smoke test hash to field

* Implement hash_to_field with expected output

* unoptimized hash-to-curve G2 for BLS12-381

* Don't run sanitizer on hash to field as it uses GC-ed strings
This commit is contained in:
Mamy Ratsimbazafy 2021-08-13 22:07:26 +02:00 committed by GitHub
parent 5404437d18
commit 499f9605b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 2034 additions and 36 deletions

View File

@ -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()

View File

@ -1,6 +1,6 @@
import
# Internals
../constantine/hashes/h_sha256,
../constantine/hashes,
# Helpers
../helpers/prng_unsafe,
./bench_blueprint

View File

@ -77,6 +77,8 @@ proc main() =
finalExpBLS12Bench(curve, Iters)
pairingBLS12Bench(curve, Iters)
separator()
hashToCurveBLS12381G2Bench(Iters)
separator()
main()
notes()

View File

@ -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
)

View File

@ -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)

View File

@ -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()

View File

@ -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(

View File

@ -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")

View File

@ -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

View File

@ -8,7 +8,10 @@
import
./common,
./curves_declaration
./curves_declaration,
./curves_prop_core
export matchingBigInt
type
Fp*[C: static Curve] = object

View File

@ -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"
)
]

View File

@ -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)

View File

@ -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])

View File

@ -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()

View File

@ -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

71
constantine/hashes.nim Normal file
View File

@ -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

View File

@ -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)

View File

@ -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))

View File

@ -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],

View File

@ -12,6 +12,8 @@ import
../arithmetic/finite_fields,
../primitives
export Fp
# No exceptions allowed
{.push raises: [].}
{.push inline.}

View File

@ -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

View File

@ -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
# -------------------------------------------------------------------

View File

@ -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

View File

@ -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 = + A * x + B
"""
R.<xx> = 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.<u> = PolynomialRing(Fp)
if g2field == 'Fp2':
QNR_Fp = curve_config[curve_name]['tower']['QNR_Fp']
Fp2.<beta> = Fp.extension(u^2 - QNR_Fp)
else:
SNR_Fp = curve_config[curve_name]['tower']['SNR_Fp']
Fp2.<beta> = 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.<u> = PolynomialRing(Fp)
# if g2field == 'Fp2':
# QNR_Fp = curve_config[curve_name]['tower']['QNR_Fp']
# Fp2.<beta> = Fp.extension(u^2 - QNR_Fp)
# else:
# SNR_Fp = curve_config[curve_name]['tower']['SNR_Fp']
# Fp2.<beta> = Fp.extension(u^2 - SNR_Fp)
# ------------------------------------------
QNR_Fp = curve_config[curve_name]['tower']['QNR_Fp']
Fp2.<beta> = 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 '
)

View File

@ -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,

View File

@ -1,6 +1,6 @@
import
# Internals
../constantine/hashes/h_sha256,
../constantine/hashes,
# Helpers
../helpers/prng_unsafe,
# Third-party

156
tests/t_hash_to_curve.nim Normal file
View File

@ -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"
)

184
tests/t_hash_to_field.nim Normal file
View File

@ -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"

View File

@ -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"
]
}
]
}

View File

@ -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"
]
}
]
}