From 0791da5ca7d519cd95334d0b553c41bcd7937ff0 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Fri, 1 Apr 2022 11:20:45 +0200 Subject: [PATCH 01/13] use more inplace procs --- dagger/storageproofs/bls.nim | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/dagger/storageproofs/bls.nim b/dagger/storageproofs/bls.nim index c82dc96a..b0f1d370 100644 --- a/dagger/storageproofs/bls.nim +++ b/dagger/storageproofs/bls.nim @@ -140,10 +140,9 @@ proc getSector(f: File, blockid: int64, sectorid: int64, spb: int64): ZChar = f.setFilePos((blockid * spb + sectorid) * sizeof(result)) let r = f.readBytes(result, 0, sizeof(result)) -proc rndScalar(): blst_scalar = +proc rndScalar(scalar: var blst_scalar): void = ## Generate random scalar within the subroup order r var scal{.noInit.}: array[32, byte] - var scalar{.noInit.}: blst_scalar while true: for val in scal.mitems: @@ -152,27 +151,20 @@ proc rndScalar(): blst_scalar = if blst_scalar_fr_check(scalar).bool: break - return scalar - -proc rndP2(): (blst_p2, blst_scalar) = +proc rndP2(x: var blst_p2, scalar: var blst_scalar): void = ## Generate random point on G2 - var x{.noInit.}: blst_p2 x.blst_p2_from_affine(BLS12_381_G2) # init from generator - let scalar = rndScalar() + scalar.rndScalar() x.blst_p2_mult(x, scalar, 255) - return (x, scalar) -proc rndP1(): (blst_p1, blst_scalar) = +proc rndP1(x: var blst_p1, scalar: var blst_scalar): void = ## Generate random point on G1 - var x{.noInit.}: blst_p1 x.blst_p1_from_affine(BLS12_381_G1) # init from generator - let scalar = rndScalar() + scalar.rndScalar() x.blst_p1_mult(x, scalar, 255) - return (x, scalar) -proc posKeygen(): (blst_p2, blst_scalar) = +let posKeygen = rndP2 ## Generate POS key pair - rndP2() proc keygen*(): (PublicKey, SecretKey) = ## Generate key pair for signing metadata and for POS tags @@ -184,7 +176,7 @@ proc keygen*(): (PublicKey, SecretKey) = b = byte Rng.instance.rand(0xFF) doAssert ikm.keyGen(pk.signkey, sk.signkey) - (pk.key, sk.key) = posKeygen() + posKeygen(pk.key, sk.key) return (pk, sk) proc split(f: File, s: int64): int64 = @@ -274,7 +266,10 @@ proc setup*(ssk: SecretKey, s:int64, filename: string): (Tau, seq[blst_p1]) = # generate the coefficient vector for combining sectors of a block: U var ubase: seq[blst_scalar] for i in 0 ..< s : - let (u, ub) = rndP1() + var + u: blst_p1 + ub: blst_scalar + rndP1(u, ub) t.u.add(u) ubase.add(ub) @@ -298,7 +293,7 @@ proc generateQuery*(tau: Tau, spk: PublicKey, l: int): seq[QElement] = for i in 0 ..< l : var q: QElement q.I = Rng.instance.rand(n-1) #TODO: dedup - q.V = rndScalar() #TODO: fix range + q.V.rndScalar() #TODO: fix range result.add(q) proc generateProof*(q: openArray[QElement], authenticators: openArray[blst_p1], spk: PublicKey, s: int64, filename: string): (seq[blst_scalar], blst_p1) = From 42879714803e8b6e8a1863e68cd8a1a66b7f3ed2 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Fri, 1 Apr 2022 11:21:20 +0200 Subject: [PATCH 02/13] storegeproofs: update README Signed-off-by: Csaba Kiraly --- dagger/storageproofs/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dagger/storageproofs/README.md b/dagger/storageproofs/README.md index 1ee54b5e..258606b2 100644 --- a/dagger/storageproofs/README.md +++ b/dagger/storageproofs/README.md @@ -1 +1,9 @@ Nim implementation of Proof of Storage related schemes + +Implementation of the BLS-based public PoS scheme from +Shacham H., Waters B., "Compact Proofs of Retrievability" +using pairing over BLS12-381 ECC. + +For a detailed description of the implementation, see `bls.nim` + +For a usage example, see `testbls.nim` From 01d59992e684dd06658bcae1ccc46e85d2379711 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Sat, 2 Apr 2022 21:43:50 +0200 Subject: [PATCH 03/13] por: separating BLST backend WIP: func and proc names might be improved later. --- .../storageproofs/backends/backend_blst.nim | 111 +++++++++ dagger/storageproofs/bls.nim | 221 +++++++----------- 2 files changed, 200 insertions(+), 132 deletions(-) create mode 100644 dagger/storageproofs/backends/backend_blst.nim diff --git a/dagger/storageproofs/backends/backend_blst.nim b/dagger/storageproofs/backends/backend_blst.nim new file mode 100644 index 00000000..1c27109a --- /dev/null +++ b/dagger/storageproofs/backends/backend_blst.nim @@ -0,0 +1,111 @@ +## Nim-POS +## Copyright (c) 2022 Status Research & Development GmbH +## Licensed under either of +## * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) +## * MIT license ([LICENSE-MIT](LICENSE-MIT)) +## at your option. +## This file may not be copied, modified, or distributed except according to +## those terms. + +# Implementation of the BLS-based public PoS scheme from +# Shacham H., Waters B., "Compact Proofs of Retrievability" +# using pairing over BLS12-381 ECC + +import blscurve +import blscurve/blst/blst_abi + +type + ec_SecretKey* = blscurve.SecretKey + ec_PublicKey* = blscurve.PublicKey + ec_p1* = blst_p1 + ec_p2* = blst_p2 + ec_scalar* = blst_scalar + ec_fr* = blst_fr + ec_signature* = Signature + +# these need to be template as a workaround for const +template EC_G1* : blst_p1_affine = BLS12_381_G1 +template EC_G2* : blst_p2_affine = BLS12_381_G2 + +let + ec_p1_from_affine* = blst_p1_from_affine + ec_scalar_from_bendian* = blst_scalar_from_bendian + ec_scalar_fr_check* = blst_scalar_fr_check + ec_p2_from_affine* = blst_p2_from_affine + ec_p2_mult* = blst_p2_mult + ec_p1_mult* = blst_p1_mult + ec_p1_add_or_double* = blst_p1_add_or_double + ec_fr_from_scalar* = blst_fr_from_scalar + ec_fr_mul* = blst_fr_mul + ec_scalar_from_fr* = blst_scalar_from_fr + ec_fr_add* = blst_fr_add + ec_p1_on_curve* = blst_p1_on_curve + ec_keygen* = blscurve.keyGen + +func ec_export_raw*(signature: Signature): array[96, byte] {.inline, noinit.} = + blscurve.exportRaw(signature) + +proc ec_sign*[T: byte|char](secretKey: SecretKey, message: openarray[T]): Signature = + blscurve.sign(secretKey, message) + +proc ec_hash_to_g1*[T,U,V: byte|char](dst: var blst_p1; + msg: openArray[T]; + domainSepTag: openArray[U]; + aug: openArray[V]) = + blst_hash_to_g1(dst, msg, domainSepTag, aug) + +proc pairing(a: ec_p1, b: ec_p2): blst_fp12 = + ## Calculate pairing G_1,G_2 -> G_T + var aa: blst_p1_affine + var bb: blst_p2_affine + blst_p1_to_affine(aa, a) + blst_p2_to_affine(bb, b) + var l: blst_fp12 + blst_miller_loop(l, bb, aa) + blst_final_exp(result, l) + +proc verifyPairingsNaive(a1: blst_p1, a2: blst_p2, b1: blst_p1, b2: blst_p2) : bool = + let e1 = pairing(a1, a2) + let e2 = pairing(b1, b2) + return e1 == e2 + +proc verifyPairingsNeg(a1: ec_p1, a2: ec_p2, b1: ec_p1, b2: ec_p2) : bool = + ## Faster pairing verification using 2 miller loops but ony one final exponentiation + ## based on https://github.com/benjaminion/c-kzg/blob/main/src/bls12_381.c + var + loop0, loop1, gt_point: blst_fp12 + aa1, bb1: blst_p1_affine + aa2, bb2: blst_p2_affine + + var a1neg = a1 + blst_p1_cneg(a1neg, 1) + + blst_p1_to_affine(aa1, a1neg) + blst_p1_to_affine(bb1, b1) + blst_p2_to_affine(aa2, a2) + blst_p2_to_affine(bb2, b2) + + blst_miller_loop(loop0, aa2, aa1) + blst_miller_loop(loop1, bb2, bb1) + + blst_fp12_mul(gt_point, loop0, loop1) + blst_final_exp(gt_point, gt_point) + + return blst_fp12_is_one(gt_point).bool + +proc verifyPairings*(a1: ec_p1, a2: ec_p2, b1: ec_p1, b2: ec_p2) : bool = + ## Wrapper to select verify pairings implementation + verifyPairingsNaive(a1, a2, b1, b2) + #verifyPairingsNeg(a1, a2, b1, b2) + +func ec_from_bytes*( + obj: var (Signature|ProofOfPossession), + raw: array[96, byte] or array[192, byte] + ): bool {.inline.} = + fromBytes(obj, raw) + +func ec_verify*[T: byte|char]( + publicKey: PublicKey, + message: openarray[T], + signature: Signature) : bool = + verify(publicKey, message, signature) \ No newline at end of file diff --git a/dagger/storageproofs/bls.nim b/dagger/storageproofs/bls.nim index b0f1d370..8e4b894a 100644 --- a/dagger/storageproofs/bls.nim +++ b/dagger/storageproofs/bls.nim @@ -78,8 +78,8 @@ # The size of the proof is instead # s * 32 + 48 bytes -import blscurve -import blscurve/blst/blst_abi +import ./backends/backend_blst + import ../rng import endians @@ -90,25 +90,26 @@ const bytespersector = 31 # length in bytes of the unique (random) name const namelen = 512 + type # a single sector ZChar = array[bytespersector, byte] # secret key combining the metadata signing key and the POR generation key SecretKey = object - signkey: blscurve.SecretKey - key: blst_scalar + signkey: ec_SecretKey + key: ec_scalar # public key combining the metadata signing key and the POR validation key PublicKey = object - signkey: blscurve.PublicKey - key: blst_p2 + signkey: ec_PublicKey + key: ec_p2 # POR metadata (called "file tag t_0" in the original paper) TauZero = object name: array[namelen, byte] n: int64 - u: seq[blst_p1] + u: seq[ec_p1] # signed POR metadata (called "signed file tag t" in the original paper) Tau = object @@ -118,50 +119,50 @@ type # PoR query element QElement = object I: int64 - V: blst_scalar + V: ec_scalar -proc fromBytesBE(a: array[32, byte]): blst_scalar = +proc fromBytesBE(a: array[32, byte]): ec_scalar = ## Convert data to blst native form - blst_scalar_from_bendian(result, a) - doAssert(blst_scalar_fr_check(result).bool) + ec_scalar_from_bendian(result, a) + doAssert(ec_scalar_fr_check(result).bool) -proc fromBytesBE(a: openArray[byte]): blst_scalar = +proc fromBytesBE(a: openArray[byte]): ec_scalar = ## Convert data to blst native form var b: array[32, byte] doAssert(a.len <= b.len) let d = b.len - a.len for i in 0 ..< a.len: b[i+d] = a[i] - blst_scalar_from_bendian(result, b) - doAssert(blst_scalar_fr_check(result).bool) + ec_scalar_from_bendian(result, b) + doAssert(ec_scalar_fr_check(result).bool) proc getSector(f: File, blockid: int64, sectorid: int64, spb: int64): ZChar = ## Read file sector at given postion f.setFilePos((blockid * spb + sectorid) * sizeof(result)) let r = f.readBytes(result, 0, sizeof(result)) -proc rndScalar(scalar: var blst_scalar): void = +proc rndScalar(scalar: var ec_scalar): void = ## Generate random scalar within the subroup order r var scal{.noInit.}: array[32, byte] while true: for val in scal.mitems: val = byte Rng.instance.rand(0xFF) - scalar.blst_scalar_from_bendian(scal) - if blst_scalar_fr_check(scalar).bool: + scalar.ec_scalar_from_bendian(scal) + if ec_scalar_fr_check(scalar).bool: break -proc rndP2(x: var blst_p2, scalar: var blst_scalar): void = +proc rndP2(x: var ec_p2, scalar: var ec_scalar): void = ## Generate random point on G2 - x.blst_p2_from_affine(BLS12_381_G2) # init from generator + x.ec_p2_from_affine(EC_G2) # init from generator scalar.rndScalar() - x.blst_p2_mult(x, scalar, 255) + x.ec_p2_mult(x, scalar, 255) -proc rndP1(x: var blst_p1, scalar: var blst_scalar): void = +proc rndP1(x: var ec_p1, scalar: var ec_scalar): void = ## Generate random point on G1 - x.blst_p1_from_affine(BLS12_381_G1) # init from generator + x.ec_p1_from_affine(EC_G1) # init from generator scalar.rndScalar() - x.blst_p1_mult(x, scalar, 255) + x.ec_p1_mult(x, scalar, 255) let posKeygen = rndP2 ## Generate POS key pair @@ -174,7 +175,7 @@ proc keygen*(): (PublicKey, SecretKey) = for b in ikm.mitems: b = byte Rng.instance.rand(0xFF) - doAssert ikm.keyGen(pk.signkey, sk.signkey) + doAssert ikm.ec_keygen(pk.signkey, sk.signkey) posKeygen(pk.key, sk.key) return (pk, sk) @@ -190,12 +191,12 @@ proc split(f: File, s: int64): int64 = return n -proc hashToG1[T: byte|char](msg: openArray[T]): blst_p1 = +proc hashToG1[T: byte|char](msg: openArray[T]): ec_p1 = ## Hash to curve with Dagger specific domain separation const dst = "DAGGER-PROOF-OF-CONCEPT" - result.blst_hash_to_g1(msg, dst, aug = "") + result.ec_hash_to_g1(msg, dst, aug = "") -proc hashNameI(name: array[namelen, byte], i: int64): blst_p1 = +proc hashNameI(name: array[namelen, byte], i: int64): ec_p1 = ## Calculate unique filname and block index based hash # # naive implementation, hashing a long string representation @@ -208,20 +209,20 @@ proc hashNameI(name: array[namelen, byte], i: int64): blst_p1 = bigEndian64(addr(namei[sizeof(name)]), unsafeAddr(i)) return hashToG1(namei) -proc generateAuthenticatorNaive(i: int64, s: int64, t: TauZero, f: File, ssk: SecretKey): blst_p1 = +proc generateAuthenticatorNaive(i: int64, s: int64, t: TauZero, f: File, ssk: SecretKey): ec_p1 = ## Naive implementation of authenticator as in the S&W paper. ## With the paper's multiplicative notation: ## \sigmai=\(H(file||i)\cdot\prod{j=0}^{s-1}{uj^{m[i][j]}})^{\alpha} - var sum: blst_p1 + var sum: ec_p1 for j in 0 ..< s: - var prod: blst_p1 - prod.blst_p1_mult(t.u[j], fromBytesBE(getSector(f, i, j, s)), 255) - sum.blst_p1_add_or_double(sum, prod) + var prod: ec_p1 + prod.ec_p1_mult(t.u[j], fromBytesBE(getSector(f, i, j, s)), 255) + sum.ec_p1_add_or_double(sum, prod) - blst_p1_add_or_double(result, hashNameI(t.name, i), sum) - result.blst_p1_mult(result, ssk.key, 255) + ec_p1_add_or_double(result, hashNameI(t.name, i), sum) + result.ec_p1_mult(result, ssk.key, 255) -proc generateAuthenticatorOpt(i: int64, s: int64, t: TauZero, ubase: openArray[blst_scalar], f: File, ssk: SecretKey): blst_p1 = +proc generateAuthenticatorOpt(i: int64, s: int64, t: TauZero, ubase: openArray[ec_scalar], f: File, ssk: SecretKey): ec_p1 = ## Optimized implementation of authenticator generation ## This implementation is reduces the number of scalar multiplications ## from s+1 to 1+1 , using knowledge about the scalars (r_j) @@ -229,31 +230,31 @@ proc generateAuthenticatorOpt(i: int64, s: int64, t: TauZero, ubase: openArray[b ## ## With the paper's multiplicative notation, we use: ## (H(file||i)\cdot g^{\sum{j=0}^{s-1}{r_j \cdot m[i][j]}})^{\alpha} - var sum: blst_fr - var sums: blst_scalar + var sum: ec_fr + var sums: ec_scalar for j in 0 ..< s: - var a, b, x: blst_fr - a.blst_fr_from_scalar(ubase[j]) - b.blst_fr_from_scalar(fromBytesBE(getSector(f, i, j, s))) - x.blst_fr_mul(a, b) - sum.blst_fr_add(sum, x) - sums.blst_scalar_from_fr(sum) + var a, b, x: ec_fr + a.ec_fr_from_scalar(ubase[j]) + b.ec_fr_from_scalar(fromBytesBE(getSector(f, i, j, s))) + x.ec_fr_mul(a, b) + sum.ec_fr_add(sum, x) + sums.ec_scalar_from_fr(sum) - result.blst_p1_from_affine(BLS12_381_G1) - result.blst_p1_mult(result, sums, 255) + result.ec_p1_from_affine(EC_G1) + result.ec_p1_mult(result, sums, 255) - result.blst_p1_add_or_double(result, hashNameI(t.name, i)) - result.blst_p1_mult(result, ssk.key, 255) + result.ec_p1_add_or_double(result, hashNameI(t.name, i)) + result.ec_p1_mult(result, ssk.key, 255) -proc generateAuthenticator(i: int64, s: int64, t: TauZero, ubase: openArray[blst_scalar], f: File, ssk: SecretKey): blst_p1 = +proc generateAuthenticator(i: int64, s: int64, t: TauZero, ubase: openArray[ec_scalar], f: File, ssk: SecretKey): ec_p1 = ## Wrapper to select tag generator implementation # let a = generateAuthenticatorNaive(i, s, t, f, ssk) let b = generateAuthenticatorOpt(i, s, t, ubase, f, ssk) - # doAssert(a.blst_p1_is_equal(b).bool) + # doAssert(a.ec_p1_is_equal(b).bool) return b -proc setup*(ssk: SecretKey, s:int64, filename: string): (Tau, seq[blst_p1]) = +proc setup*(ssk: SecretKey, s:int64, filename: string): (Tau, seq[ec_p1]) = ## Set up the POR scheme by generating tags and metadata let file = open(filename) let n = split(file, s) @@ -264,22 +265,22 @@ proc setup*(ssk: SecretKey, s:int64, filename: string): (Tau, seq[blst_p1]) = t.name[i] = byte Rng.instance.rand(0xFF) # generate the coefficient vector for combining sectors of a block: U - var ubase: seq[blst_scalar] + var ubase: seq[ec_scalar] for i in 0 ..< s : var - u: blst_p1 - ub: blst_scalar + u: ec_p1 + ub: ec_scalar rndP1(u, ub) t.u.add(u) ubase.add(ub) #TODO: a better bytearray conversion of TauZero for the signature might be needed # the current conversion using $t might be architecture dependent and not unique - let signature = sign(ssk.signkey, $t) - let tau = Tau(t: t, signature: signature.exportRaw()) + let signature = ec_sign(ssk.signkey, $t) + let tau = Tau(t: t, signature: signature.ec_export_raw()) #generate sigmas - var sigmas: seq[blst_p1] + var sigmas: seq[ec_p1] for i in 0 ..< n : sigmas.add(generateAuthenticator(i, s, t, ubase, file, ssk)) @@ -296,106 +297,62 @@ proc generateQuery*(tau: Tau, spk: PublicKey, l: int): seq[QElement] = q.V.rndScalar() #TODO: fix range result.add(q) -proc generateProof*(q: openArray[QElement], authenticators: openArray[blst_p1], spk: PublicKey, s: int64, filename: string): (seq[blst_scalar], blst_p1) = +proc generateProof*(q: openArray[QElement], authenticators: openArray[ec_p1], spk: PublicKey, s: int64, filename: string): (seq[ec_scalar], ec_p1) = ## Generata BLS proofs for a given query let file = open(filename) - var mu: seq[blst_scalar] + var mu: seq[ec_scalar] for j in 0 ..< s : - var muj: blst_fr + var muj: ec_fr for qelem in q : - var x, v, sector: blst_fr + var x, v, sector: ec_fr let sect = fromBytesBE(getSector(file, qelem.I, j, s)) - sector.blst_fr_from_scalar(sect) - v.blst_fr_from_scalar(qelem.V) - x.blst_fr_mul(v, sector) - muj.blst_fr_add(muj, x) - var mujs: blst_scalar - mujs.blst_scalar_from_fr(muj) + sector.ec_fr_from_scalar(sect) + v.ec_fr_from_scalar(qelem.V) + x.ec_fr_mul(v, sector) + muj.ec_fr_add(muj, x) + var mujs: ec_scalar + mujs.ec_scalar_from_fr(muj) mu.add(mujs) - var sigma: blst_p1 + var sigma: ec_p1 for qelem in q: - var prod: blst_p1 - prod.blst_p1_mult(authenticators[qelem.I], qelem.V, 255) - sigma.blst_p1_add_or_double(sigma, prod) + var prod: ec_p1 + prod.ec_p1_mult(authenticators[qelem.I], qelem.V, 255) + sigma.ec_p1_add_or_double(sigma, prod) file.close() return (mu, sigma) -proc pairing(a: blst_p1, b: blst_p2): blst_fp12 = - ## Calculate pairing G_1,G_2 -> G_T - var aa: blst_p1_affine - var bb: blst_p2_affine - blst_p1_to_affine(aa, a) - blst_p2_to_affine(bb, b) - var l: blst_fp12 - blst_miller_loop(l, bb, aa) - blst_final_exp(result, l) - -proc verifyPairingsNaive(a1: blst_p1, a2: blst_p2, b1: blst_p1, b2: blst_p2) : bool = - let e1 = pairing(a1, a2) - let e2 = pairing(b1, b2) - return e1 == e2 - -proc verifyPairingsNeg(a1: blst_p1, a2: blst_p2, b1: blst_p1, b2: blst_p2) : bool = - ## Faster pairing verification using 2 miller loops but ony one final exponentiation - ## based on https://github.com/benjaminion/c-kzg/blob/main/src/bls12_381.c - var - loop0, loop1, gt_point: blst_fp12 - aa1, bb1: blst_p1_affine - aa2, bb2: blst_p2_affine - - var a1neg = a1 - blst_p1_cneg(a1neg, 1) - - blst_p1_to_affine(aa1, a1neg) - blst_p1_to_affine(bb1, b1) - blst_p2_to_affine(aa2, a2) - blst_p2_to_affine(bb2, b2) - - blst_miller_loop(loop0, aa2, aa1) - blst_miller_loop(loop1, bb2, bb1) - - blst_fp12_mul(gt_point, loop0, loop1) - blst_final_exp(gt_point, gt_point) - - return blst_fp12_is_one(gt_point).bool - -proc verifyPairings(a1: blst_p1, a2: blst_p2, b1: blst_p1, b2: blst_p2) : bool = - ## Wrapper to select verify pairings implementation - verifyPairingsNaive(a1, a2, b1, b2) - #verifyPairingsNeg(a1, a2, b1, b2) - -proc verifyProof*(tau: Tau, q: openArray[QElement], mus: openArray[blst_scalar], sigma: blst_p1, spk: PublicKey): bool = +proc verifyProof*(tau: Tau, q: openArray[QElement], mus: openArray[ec_scalar], sigma: ec_p1, spk: PublicKey): bool = ## Verify a BLS proof given a query # verify signature on Tau - var signature: Signature - if not signature.fromBytes(tau.signature): + var signature: ec_signature + if not signature.ec_from_bytes(tau.signature): return false - if not verify(spk.signkey, $tau.t, signature): + if not ec_verify(spk.signkey, $tau.t, signature): return false - var first: blst_p1 + var first: ec_p1 for qelem in q : - var prod: blst_p1 - prod.blst_p1_mult(hashNameI(tau.t.name, qelem.I), qelem.V, 255) - first.blst_p1_add_or_double(first, prod) - doAssert(blst_p1_on_curve(first).bool) + var prod: ec_p1 + prod.ec_p1_mult(hashNameI(tau.t.name, qelem.I), qelem.V, 255) + first.ec_p1_add_or_double(first, prod) + doAssert(ec_p1_on_curve(first).bool) let us = tau.t.u - var second: blst_p1 + var second: ec_p1 for j in 0 ..< len(us) : - var prod: blst_p1 - prod.blst_p1_mult(us[j], mus[j], 255) - second.blst_p1_add_or_double(second, prod) - doAssert(blst_p1_on_curve(second).bool) + var prod: ec_p1 + prod.ec_p1_mult(us[j], mus[j], 255) + second.ec_p1_add_or_double(second, prod) + doAssert(ec_p1_on_curve(second).bool) - var sum: blst_p1 - sum.blst_p1_add_or_double(first, second) + var sum: ec_p1 + sum.ec_p1_add_or_double(first, second) - var g{.noInit.}: blst_p2 - g.blst_p2_from_affine(BLS12_381_G2) + var g{.noInit.}: ec_p2 + g.ec_p2_from_affine(EC_G2) return verifyPairings(sum, spk.key, sigma, g) From 177472ee12df5f59cfc6dd3a9ae7b4ad3975f1ad Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Wed, 6 Apr 2022 15:05:53 +0200 Subject: [PATCH 04/13] WIP: adding constantine backend This is WIP, not compiling yet test with cd dagger/storageproofs ../../env.sh nim c -r testbls.nim --- .gitmodules | 3 + .../backends/backend_constantine.nim | 161 ++++++++++++++++++ dagger/storageproofs/bls.nim | 4 +- vendor/constantine | 1 + 4 files changed, 167 insertions(+), 2 deletions(-) create mode 100644 dagger/storageproofs/backends/backend_constantine.nim create mode 160000 vendor/constantine diff --git a/.gitmodules b/.gitmodules index a4d16568..0450d9af 100644 --- a/.gitmodules +++ b/.gitmodules @@ -178,3 +178,6 @@ [submodule "vendor/nim-leopard"] path = vendor/nim-leopard url = https://github.com/status-im/nim-leopard.git +[submodule "vendor/constantine"] + path = vendor/constantine + url = https://github.com/mratsim/constantine diff --git a/dagger/storageproofs/backends/backend_constantine.nim b/dagger/storageproofs/backends/backend_constantine.nim new file mode 100644 index 00000000..23768688 --- /dev/null +++ b/dagger/storageproofs/backends/backend_constantine.nim @@ -0,0 +1,161 @@ +## Nim-POS +## Copyright (c) 2022 Status Research & Development GmbH +## Licensed under either of +## * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) +## * MIT license ([LICENSE-MIT](LICENSE-MIT)) +## at your option. +## This file may not be copied, modified, or distributed except according to +## those terms. + +# Implementation of the BLS-based public PoS scheme from +# Shacham H., Waters B., "Compact Proofs of Retrievability" +# using pairing over BLS12-381 ECC + +import + constantine, + # constantine/platforms/abstractions, + constantine/math/arithmetic, + # constantine/math/extension_fields, + # constantine/math/config/curves, + constantine/math/elliptic/[ec_shortweierstrass_affine, ec_shortweierstrass_projective], + constantine/math/curves/[zoo_subgroups, zoo_pairings, zoo_generators], + # constantine/math/pairing/cyclotomic_subgroup, + # constantine/math/io/io_extfields, + constantine/math/io/io_bigints, + # constantine/math/config/[curves_declaration, type_ff], + constantine/math/config/type_ff, + constantine/blssig_pop_on_bls12381_g2, + constantine/hash_to_curve/hash_to_curve, + constantine/hashes, + constantine/math/pairings + +export hashes +export matchingBigInt +export getNonResidueFp + +when defined(debugConstantine): + export `$` + +const C = BLS12_381 + +type + ec_SecretKey* = SecretKey + ec_PublicKey* = PublicKey + ec_p1* = ECP_ShortW_Jac[Fp2[C], G2] + ec_p1_affine = ECP_ShortW_Aff[Fp2[C], G2] + ec_p2* = ECP_ShortW_Jac[Fp[C], G1] + ec_p2_affine = ECP_ShortW_Aff[Fp[C], G1] + ec_scalar* = matchingOrderBigInt(C) + ec_fr* = Fr[C] + ec_signature* = Signature + +let + EC_G1* = C.getGenerator($G2) + EC_G2* = C.getGenerator($G1) + +func ec_p1_from_affine*(dst: var ec_p1, a: ec_p1_affine) = + dst.fromAffine(a) + +#let ec_scalar_from_bendian* = unmarshalBE # not exposed +func ec_scalar_from_bendian*( + s: var ec_scalar, + b: openArray[byte]) = + s.unmarshal(b, bigEndian) + +#let ec_scalar_fr_check* = blst_scalar_fr_check +func ec_scalar_fr_check*( + s: ec_scalar) : bool = + bool(s < C.getCurveOrder()) + +func ec_p2_from_affine*(dst: var ec_p2, a: ec_p2_affine) = + dst.fromAffine(a) + +func ec_p2_mult*( + dst: var ec_p2, + p: ec_p2, + scalar: ec_scalar, + nbits: uint) = + dst = p + dst.scalarMul(scalar) + +func ec_p1_mult*( + dst: var ec_p1, + p: ec_p1, + scalar: ec_scalar, + nbits: uint) = + dst = p + dst.scalarMul(scalar) + +func ec_p1_add_or_double*(dst: var ec_p1, a: ec_p1, b: ec_p1) = + dst.sum(a,b) + +# Workaround: using ec_fr makes bindConstant fail, hence Fr[C] type below +func ec_fr_from_scalar*(res: var Fr[C], scalar: ec_scalar) = + res.fromBig(scalar) + +# Workaround: using ec_fr makes bindConstant fail, hence Fr[C] type below +func ec_scalar_from_fr*(res: var ec_scalar, fr: Fr[C]) = + res = toBig(fr) + +func ec_fr_add*(res: var Fr[C], a, b: Fr[C]) = + sum(res, a, b) + +func ec_fr_mul*(res: var Fr[C], a, b: Fr[C]) = + prod(res, a, b) + +func ec_p1_on_curve*(p: ec_p1) : bool = + var aff : ec_p1_affine + aff.affine(p) + (bool) isOnCurve(aff.x, aff.y, G1) + +func ec_keygen*(ikm: array[32, byte], pk: var PublicKey, sk: var SecretKey) : bool = + # TODO: HKDF key generation as in spec (https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature#section-2.3) + var ikm2 = ikm + ikm2[0] = 0 # TODO: this is a hack, not secure + let ok = sk.deserialize_secret_key(ikm2) + doAssert ok == cttBLS_Success + let ok2 = pk.derive_public_key(sk) + doAssert ok2 == cttBLS_Success + (ok == cttBLS_Success) and (ok2 == cttBLS_Success) + +func ec_export_raw*(signature: Signature): array[96, byte] {.inline, noinit.} = + let ok = result.serialize_signature_compressed(signature) + doAssert ok == cttBLS_Success + +proc ec_sign*[T: byte|char](secretKey: SecretKey, message: openarray[T]): Signature = + let ok = result.sign(secretKey, message) + doAssert ok == cttBLS_Success + +proc ec_hash_to_g1*(dst: var ec_p1, + msg: openArray[byte], + domainSepTag: openArray[char], + aug: openArray[char]) = + sha256.hashToCurve(128, dst, aug, msg, domainSepTag) #TODO: fix k + +proc verifyPairings*(a1: ec_p1, a2: ec_p2, b1: ec_p1, b2: ec_p2) : bool = + when C.getEmbeddingDegree() == 12: + var gt {.noInit.}: Fp12[C] + else: + {.error: "Not implemented: signature on k=" & $C.getEmbeddingDegree() & " for curve " & $$C.} + + var a1aff, b1aff {.noInit.}: ec_p1_affine + var a2aff, b2aff {.noInit.}: ec_p2_affine + a1aff.affine(a1) + b1aff.affine(b1) + a2aff.affine(a2) + b2aff.affine(b2) + gt.pairing([a2aff, b2aff], [a1aff, b1aff]) + return gt.isOne().bool() + +func ec_from_bytes*( + dst: var Signature, + raw: array[96, byte] or array[192, byte] + ): bool {.inline.} = + let ok = dst.deserialize_signature_compressed(raw) + doAssert ok == cttBLS_Success + +func ec_verify*( + publicKey: PublicKey, + message: openarray[char], + signature: Signature) : bool = + publicKey.verify(message, signature) == cttBLS_Success diff --git a/dagger/storageproofs/bls.nim b/dagger/storageproofs/bls.nim index 8e4b894a..11a53dce 100644 --- a/dagger/storageproofs/bls.nim +++ b/dagger/storageproofs/bls.nim @@ -78,7 +78,7 @@ # The size of the proof is instead # s * 32 + 48 bytes -import ./backends/backend_blst +import ./backends/backend_constantine import ../rng import endians @@ -191,7 +191,7 @@ proc split(f: File, s: int64): int64 = return n -proc hashToG1[T: byte|char](msg: openArray[T]): ec_p1 = +proc hashToG1(msg: openArray[byte]): ec_p1 = ## Hash to curve with Dagger specific domain separation const dst = "DAGGER-PROOF-OF-CONCEPT" result.ec_hash_to_g1(msg, dst, aug = "") diff --git a/vendor/constantine b/vendor/constantine new file mode 160000 index 00000000..742cecce --- /dev/null +++ b/vendor/constantine @@ -0,0 +1 @@ +Subproject commit 742cecce0898c71564f9709003996604f7af34d9 From 434b6a1c0ddb20df978b5bdde6a716d1776c4896 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Tue, 12 Apr 2022 14:17:30 +0200 Subject: [PATCH 05/13] fix verifyPairings implementation Signed-off-by: Csaba Kiraly --- dagger/storageproofs/backends/backend_constantine.nim | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dagger/storageproofs/backends/backend_constantine.nim b/dagger/storageproofs/backends/backend_constantine.nim index 23768688..96dcfba0 100644 --- a/dagger/storageproofs/backends/backend_constantine.nim +++ b/dagger/storageproofs/backends/backend_constantine.nim @@ -134,7 +134,7 @@ proc ec_hash_to_g1*(dst: var ec_p1, proc verifyPairings*(a1: ec_p1, a2: ec_p2, b1: ec_p1, b2: ec_p2) : bool = when C.getEmbeddingDegree() == 12: - var gt {.noInit.}: Fp12[C] + var gt1, gt2 {.noInit.}: Fp12[C] else: {.error: "Not implemented: signature on k=" & $C.getEmbeddingDegree() & " for curve " & $$C.} @@ -144,8 +144,9 @@ proc verifyPairings*(a1: ec_p1, a2: ec_p2, b1: ec_p1, b2: ec_p2) : bool = b1aff.affine(b1) a2aff.affine(a2) b2aff.affine(b2) - gt.pairing([a2aff, b2aff], [a1aff, b1aff]) - return gt.isOne().bool() + gt1.pairing(a2aff, a1aff) + gt2.pairing(b2aff, b1aff) + return bool(gt1 == gt2) func ec_from_bytes*( dst: var Signature, From 62cd099f4ffe309fe2f02276d21229e54a513388 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Tue, 12 Apr 2022 14:27:21 +0200 Subject: [PATCH 06/13] faster verifyPairings Signed-off-by: Csaba Kiraly --- .../storageproofs/backends/backend_constantine.nim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dagger/storageproofs/backends/backend_constantine.nim b/dagger/storageproofs/backends/backend_constantine.nim index 96dcfba0..ae9220c0 100644 --- a/dagger/storageproofs/backends/backend_constantine.nim +++ b/dagger/storageproofs/backends/backend_constantine.nim @@ -134,19 +134,19 @@ proc ec_hash_to_g1*(dst: var ec_p1, proc verifyPairings*(a1: ec_p1, a2: ec_p2, b1: ec_p1, b2: ec_p2) : bool = when C.getEmbeddingDegree() == 12: - var gt1, gt2 {.noInit.}: Fp12[C] + var gt {.noInit.}: Fp12[C] else: {.error: "Not implemented: signature on k=" & $C.getEmbeddingDegree() & " for curve " & $$C.} var a1aff, b1aff {.noInit.}: ec_p1_affine - var a2aff, b2aff {.noInit.}: ec_p2_affine + var a2aff, negb2aff {.noInit.}: ec_p2_affine a1aff.affine(a1) b1aff.affine(b1) a2aff.affine(a2) - b2aff.affine(b2) - gt1.pairing(a2aff, a1aff) - gt2.pairing(b2aff, b1aff) - return bool(gt1 == gt2) + negb2aff.affine(b2) + negb2aff.neg(negb2aff) + gt.pairing([a2aff, negb2aff], [a1aff, b1aff]) + return gt.isOne().bool() func ec_from_bytes*( dst: var Signature, From 4463278c43a8f9c29c0162707191c9b7af9c9914 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Tue, 12 Apr 2022 14:30:17 +0200 Subject: [PATCH 07/13] remove unused generics to ease compilation Signed-off-by: Csaba Kiraly --- dagger/storageproofs/backends/backend_constantine.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dagger/storageproofs/backends/backend_constantine.nim b/dagger/storageproofs/backends/backend_constantine.nim index ae9220c0..5eb693ba 100644 --- a/dagger/storageproofs/backends/backend_constantine.nim +++ b/dagger/storageproofs/backends/backend_constantine.nim @@ -122,7 +122,7 @@ func ec_export_raw*(signature: Signature): array[96, byte] {.inline, noinit.} = let ok = result.serialize_signature_compressed(signature) doAssert ok == cttBLS_Success -proc ec_sign*[T: byte|char](secretKey: SecretKey, message: openarray[T]): Signature = +proc ec_sign*(secretKey: SecretKey, message: openarray[char]): Signature = let ok = result.sign(secretKey, message) doAssert ok == cttBLS_Success From 923b86a67b7d56462242c7f45c01839c84c390fb Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Tue, 12 Apr 2022 14:35:37 +0200 Subject: [PATCH 08/13] fix isOnCurve check this needs to be changed as well when switching between G1 and G2 Signed-off-by: Csaba Kiraly --- dagger/storageproofs/backends/backend_constantine.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dagger/storageproofs/backends/backend_constantine.nim b/dagger/storageproofs/backends/backend_constantine.nim index 5eb693ba..e868b56b 100644 --- a/dagger/storageproofs/backends/backend_constantine.nim +++ b/dagger/storageproofs/backends/backend_constantine.nim @@ -106,7 +106,7 @@ func ec_fr_mul*(res: var Fr[C], a, b: Fr[C]) = func ec_p1_on_curve*(p: ec_p1) : bool = var aff : ec_p1_affine aff.affine(p) - (bool) isOnCurve(aff.x, aff.y, G1) + (bool) isOnCurve(aff.x, aff.y, G2) func ec_keygen*(ikm: array[32, byte], pk: var PublicKey, sk: var SecretKey) : bool = # TODO: HKDF key generation as in spec (https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature#section-2.3) From 182fb209fcf1382405a9bed7eb43f36eb7a219e4 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Tue, 12 Apr 2022 14:42:08 +0200 Subject: [PATCH 09/13] bumping Constantine verison to include Hash-to-Curve BLS12-381 G1 Signed-off-by: Csaba Kiraly --- vendor/constantine | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/constantine b/vendor/constantine index 742cecce..65eedd1c 160000 --- a/vendor/constantine +++ b/vendor/constantine @@ -1 +1 @@ -Subproject commit 742cecce0898c71564f9709003996604f7af34d9 +Subproject commit 65eedd1cf70389d188db01fb4f3b6c8e76cc3594 From e3b4dddaa1a6de47135f95e664058e501c0ae56c Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Tue, 12 Apr 2022 14:45:58 +0200 Subject: [PATCH 10/13] fix debug printing of Constantine BigInt values --- dagger/storageproofs/backends/backend_constantine.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/dagger/storageproofs/backends/backend_constantine.nim b/dagger/storageproofs/backends/backend_constantine.nim index e868b56b..19453f32 100644 --- a/dagger/storageproofs/backends/backend_constantine.nim +++ b/dagger/storageproofs/backends/backend_constantine.nim @@ -34,6 +34,7 @@ export matchingBigInt export getNonResidueFp when defined(debugConstantine): + import constantine/math/config/type_bigint export `$` const C = BLS12_381 From 6c4734912556fe287470f2eaa6d171f01677fc54 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Tue, 12 Apr 2022 15:43:02 +0200 Subject: [PATCH 11/13] fixup: bad return value for signature deserialize Signed-off-by: Csaba Kiraly --- dagger/storageproofs/backends/backend_constantine.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dagger/storageproofs/backends/backend_constantine.nim b/dagger/storageproofs/backends/backend_constantine.nim index 19453f32..d2486008 100644 --- a/dagger/storageproofs/backends/backend_constantine.nim +++ b/dagger/storageproofs/backends/backend_constantine.nim @@ -154,7 +154,7 @@ func ec_from_bytes*( raw: array[96, byte] or array[192, byte] ): bool {.inline.} = let ok = dst.deserialize_signature_compressed(raw) - doAssert ok == cttBLS_Success + ok == cttBLS_Success func ec_verify*( publicKey: PublicKey, From ecb62af2538bc41d9aee0f04a6421dab1a96bf28 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Tue, 12 Apr 2022 16:02:17 +0200 Subject: [PATCH 12/13] switching back role of G1/G2 newly implemented Constantine feature allows us to use G1 for authenticators and G2 for the PoS public key. Signed-off-by: Csaba Kiraly --- .../backends/backend_constantine.nim | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dagger/storageproofs/backends/backend_constantine.nim b/dagger/storageproofs/backends/backend_constantine.nim index d2486008..ad474a81 100644 --- a/dagger/storageproofs/backends/backend_constantine.nim +++ b/dagger/storageproofs/backends/backend_constantine.nim @@ -42,17 +42,17 @@ const C = BLS12_381 type ec_SecretKey* = SecretKey ec_PublicKey* = PublicKey - ec_p1* = ECP_ShortW_Jac[Fp2[C], G2] - ec_p1_affine = ECP_ShortW_Aff[Fp2[C], G2] - ec_p2* = ECP_ShortW_Jac[Fp[C], G1] - ec_p2_affine = ECP_ShortW_Aff[Fp[C], G1] + ec_p1* = ECP_ShortW_Jac[Fp[C], G1] + ec_p1_affine = ECP_ShortW_Aff[Fp[C], G1] + ec_p2* = ECP_ShortW_Jac[Fp2[C], G2] + ec_p2_affine = ECP_ShortW_Aff[Fp2[C], G2] ec_scalar* = matchingOrderBigInt(C) ec_fr* = Fr[C] ec_signature* = Signature let - EC_G1* = C.getGenerator($G2) - EC_G2* = C.getGenerator($G1) + EC_G1* = C.getGenerator($G1) + EC_G2* = C.getGenerator($G2) func ec_p1_from_affine*(dst: var ec_p1, a: ec_p1_affine) = dst.fromAffine(a) @@ -107,7 +107,7 @@ func ec_fr_mul*(res: var Fr[C], a, b: Fr[C]) = func ec_p1_on_curve*(p: ec_p1) : bool = var aff : ec_p1_affine aff.affine(p) - (bool) isOnCurve(aff.x, aff.y, G2) + (bool) isOnCurve(aff.x, aff.y, G1) func ec_keygen*(ikm: array[32, byte], pk: var PublicKey, sk: var SecretKey) : bool = # TODO: HKDF key generation as in spec (https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature#section-2.3) @@ -146,7 +146,7 @@ proc verifyPairings*(a1: ec_p1, a2: ec_p2, b1: ec_p1, b2: ec_p2) : bool = a2aff.affine(a2) negb2aff.affine(b2) negb2aff.neg(negb2aff) - gt.pairing([a2aff, negb2aff], [a1aff, b1aff]) + gt.pairing([a1aff, b1aff], [a2aff, negb2aff]) return gt.isOne().bool() func ec_from_bytes*( From 95c543f6e0caeca6cb0c310b243072c2e18ffe22 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Wed, 4 May 2022 16:11:58 +0200 Subject: [PATCH 13/13] enabling PoR over BN254 curves To enable the use of BN254, change the value of `C` in backend_constantine.nim to `BN254_Snarks` This also requires https://github.com/mratsim/constantine/pull/193 Signed-off-by: Csaba Kiraly --- .../backends/backend_constantine.nim | 32 +++++++++++++++++-- vendor/constantine | 2 +- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/dagger/storageproofs/backends/backend_constantine.nim b/dagger/storageproofs/backends/backend_constantine.nim index ad474a81..2218c79a 100644 --- a/dagger/storageproofs/backends/backend_constantine.nim +++ b/dagger/storageproofs/backends/backend_constantine.nim @@ -37,6 +37,8 @@ when defined(debugConstantine): import constantine/math/config/type_bigint export `$` +#set up curve and G1/G2 +#const C = BN254_Snarks const C = BLS12_381 type @@ -133,14 +135,30 @@ proc ec_hash_to_g1*(dst: var ec_p1, aug: openArray[char]) = sha256.hashToCurve(128, dst, aug, msg, domainSepTag) #TODO: fix k -proc verifyPairings*(a1: ec_p1, a2: ec_p2, b1: ec_p1, b2: ec_p2) : bool = +proc verifyPairingsNaive[C](a1: ECP_ShortW_Jac[Fp[C], G1], a2: ec_p2, b1: ec_p1, b2: ec_p2) : bool = + # first parameter is in extended form to allow inference of C + var + e1, e2: Fp12[C] + a1aff, b1aff {.noInit.}: ec_p1_affine + a2aff, b2aff {.noInit.}: ec_p2_affine + a1aff.affine(a1) + b1aff.affine(b1) + a2aff.affine(a2) + b2aff.affine(b2) + pairing(e1, a1aff, a2aff) + pairing(e2, b1aff, b2aff) + return (e1 == e2).bool() + +proc verifyPairingsNeg[C](a1: ECP_ShortW_Jac[Fp[C], G1], a2: ec_p2, b1: ec_p1, b2: ec_p2) : bool = + # first parameter is in extended form to allow inference of C when C.getEmbeddingDegree() == 12: var gt {.noInit.}: Fp12[C] else: {.error: "Not implemented: signature on k=" & $C.getEmbeddingDegree() & " for curve " & $$C.} - var a1aff, b1aff {.noInit.}: ec_p1_affine - var a2aff, negb2aff {.noInit.}: ec_p2_affine + var + a1aff, b1aff {.noInit.}: ec_p1_affine + a2aff, negb2aff {.noInit.}: ec_p2_affine a1aff.affine(a1) b1aff.affine(b1) a2aff.affine(a2) @@ -149,6 +167,14 @@ proc verifyPairings*(a1: ec_p1, a2: ec_p2, b1: ec_p1, b2: ec_p2) : bool = gt.pairing([a1aff, b1aff], [a2aff, negb2aff]) return gt.isOne().bool() +proc verifyPairings*(a1: ec_p1, a2: ec_p2, b1: ec_p1, b2: ec_p2) : bool = + ## Wrapper to select verify pairings implementation + when C == BLS12_381: + verifyPairingsNeg(a1, a2, b1, b2) + else: + verifyPairingsNaive(a1, a2, b1, b2) + + func ec_from_bytes*( dst: var Signature, raw: array[96, byte] or array[192, byte] diff --git a/vendor/constantine b/vendor/constantine index 65eedd1c..4d3de57b 160000 --- a/vendor/constantine +++ b/vendor/constantine @@ -1 +1 @@ -Subproject commit 65eedd1cf70389d188db01fb4f3b6c8e76cc3594 +Subproject commit 4d3de57bbfaf065b7b41d73a9acabc73507bd039