From 7709d551c8247bfd262af30d555a9da3567a66dd Mon Sep 17 00:00:00 2001 From: cheatfate Date: Tue, 16 Oct 2018 11:30:14 +0300 Subject: [PATCH] Added Ethereum specific serialization procedures. Added tests for Ethereum ecAdd, ecMul, ecPairing procedures. Bump version to 1.0.1. --- bncurve.nimble | 3 +- bncurve/arith.nim | 2 +- bncurve/fp.nim | 22 ++++- bncurve/fq2.nim | 10 +++ bncurve/groups.nim | 8 +- tests/tether.nim | 218 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 254 insertions(+), 9 deletions(-) create mode 100644 tests/tether.nim diff --git a/bncurve.nimble b/bncurve.nimble index ab5c38e..8729227 100644 --- a/bncurve.nimble +++ b/bncurve.nimble @@ -1,5 +1,5 @@ packageName = "bncurve" -version = "1.0.0" +version = "1.0.1" author = "Status Research & Development GmbH" description = "Barreto-Naehrig pairing-friendly elliptic curve implementation" license = "Apache License 2.0 or MIT" @@ -15,4 +15,5 @@ task test, "Run all tests": exec "nim c -f -r -d:release tests/tfields" exec "nim c -f -r -d:release tests/tgroups" exec "nim c -f -r -d:release tests/tpairing" + exec "nim c -f -r -d:release tests/tether" exec "nim c -f -r -d:release tests/tvectors" diff --git a/bncurve/arith.nim b/bncurve/arith.nim index 71d35d8..2bcae67 100644 --- a/bncurve/arith.nim +++ b/bncurve/arith.nim @@ -242,7 +242,7 @@ proc isEven*(a: BNU256): bool {.inline, noinit.} = proc divrem*(a: BNU512, modulo: BNU256, reminder: var BNU256): Option[BNU256] = ## Divides integer ``a`` by ``modulo``, set ``remainder`` to reminder and, if ## possible, return quotient smaller than the modulus. - var q: BNU256 + var q = BNU256.zero() reminder.setZero() result = some[BNU256](q) for i in countdown(511, 0): diff --git a/bncurve/fp.nim b/bncurve/fp.nim index 7b43726..7411478 100644 --- a/bncurve/fp.nim +++ b/bncurve/fp.nim @@ -95,12 +95,12 @@ template fieldImplementation(finame, fimodulus, firsquared, fircubed, result += numis[idx] proc into*(t: typedesc[BNU256], num: finame): BNU256 = - ## Convert Fp ``num`` to 256bit integer. + ## Convert FR/FQ ``num`` to 256bit integer. result = BNU256(num) mul(result, BNU256.one(), BNU256(fimodulus), fiinv) proc init*(t: typedesc[finame], num: BNU256): Option[finame] = - ## Initialize Fp from 256bit integer ``num``. + ## Initialize FR/FQ from 256bit integer ``num``. if num >= BNU256(fimodulus): result = none[finame]() else: @@ -109,8 +109,13 @@ template fieldImplementation(finame, fimodulus, firsquared, fircubed, mul(BNU256(res), BNU256(firsquared), BNU256(fimodulus), fiinv) result = some[finame](res) + proc init2*(t: typedesc[finame], num: BNU256): finame = + ## Initalize FR/FQ from 256bit integer ``num`` regardless of modulus. + result = finame(num) + mul(BNU256(result), BNU256(firsquared), BNU256(fimodulus), fiinv) + proc fromBytes*(dst: var finame, src: openarray[byte]): bool {.noinit.} = - ## Create integer FP/FQ from big-endian bytes representation ``src``. + ## Create integer FR/FQ from big-endian bytes representation ``src``. ## Returns ``true`` if ``dst`` was successfully initialized, ``false`` ## otherwise. result = false @@ -121,6 +126,17 @@ template fieldImplementation(finame, fimodulus, firsquared, fircubed, dst = optr.get() result = true + proc fromBytes2*(dst: var finame, src: openarray[byte]): bool {.noinit.} = + ## Create integer FR/FQ from big-endian bytes representation ``src`` in + ## Ethereum way (without modulo check). + ## Returns ``true`` if ``dst`` was successfully initialized, ``false`` + ## otherwise. + result = false + var bn: BNU256 + if bn.fromBytes(src): + dst = finame.init2(bn) + result = true + proc toBytes*(src: finame, dst: var openarray[byte]): bool {.noinit, inline.} = ## Encode integer FP/FQ to big-endian bytes representation ``dst``. diff --git a/bncurve/fq2.nim b/bncurve/fq2.nim index 4472881..a4bb124 100644 --- a/bncurve/fq2.nim +++ b/bncurve/fq2.nim @@ -140,6 +140,16 @@ proc fromBytes*(dst: var FQ2, src: openarray[byte]): bool {.noinit.} = dst = init(c0o.get(), c1o.get()) result = true +proc fromBytes2*(dst: var FQ2, src: openarray[byte]): bool {.noinit.} = + ## Create integer FQ2 from big-endian bytes representation ``src`` in + ## Ethereum way. + ## Returns ``true`` if ``dst`` was successfully initialized, ``false`` + ## otherwise. + result = false + if dst.c1.fromBytes2(src.toOpenArray(0, 31)) and + dst.c0.fromBytes2(src.toOpenArray(32, 63)): + result = true + proc toBytes*(src: FQ2, dst: var openarray[byte]): bool {.noinit, inline.} = ## Encode 512bit integer FQ2 to big-endian bytes representation ``dst``. diff --git a/bncurve/groups.nim b/bncurve/groups.nim index 879de8f..20ab886 100644 --- a/bncurve/groups.nim +++ b/bncurve/groups.nim @@ -376,8 +376,8 @@ proc pairing*(p: Point[G1], q: Point[G2]): FQ12 {.noinit, inline.} = proc init*(p: var AffinePoint[G1], x: FQ, y: FQ): bool {.inline.} = ## Initializes AffinePoint[G1] with coordinates ``x`` and ``y``. ## Returns ``true`` if (x, y) is on curve and in the subgroup. - if y.squared() == (x.squared() * x) + G1B: - var point = Point[G1](x: x, y: y, z: FQ.one()) + if y.squared() == ((x.squared() * x) + G1B): + let point = Point[G1](x: x, y: y, z: FQ.one()) if (point * (-FR.one())) + point == G1.zero(): p.x = x p.y = y @@ -386,8 +386,8 @@ proc init*(p: var AffinePoint[G1], x: FQ, y: FQ): bool {.inline.} = proc init*(p: var AffinePoint[G2], x: FQ2, y: FQ2): bool {.inline.} = ## Initializes AffinePoint[G2] with coordinates ``x`` and ``y``. ## Returns ``true`` if (x, y) is on curve and in the subgroup. - if y.squared() == (x.squared() * x) + G2B: - var point = Point[G2](x: x, y: y, z: FQ2.one()) + if y.squared() == ((x.squared() * x) + G2B): + let point = Point[G2](x: x, y: y, z: FQ2.one()) if (point * (-FR.one())) + point == G2.zero(): p.x = x p.y = y diff --git a/tests/tether.nim b/tests/tether.nim new file mode 100644 index 0000000..e5786ad --- /dev/null +++ b/tests/tether.nim @@ -0,0 +1,218 @@ +import unittest +import ../bncurve/groups +import nimcrypto/utils + +type + ValidationError = object of Exception + +proc getPoint[T: G1|G2](t: typedesc[T], data: openarray[byte]): Point[T] = + when T is G1: + const nextOffset = 32 + var px, py: FQ + else: + const nextOffset = 64 + var px, py: FQ2 + if not px.fromBytes2(data.toOpenArray(0, nextOffset - 1)): + raise newException(ValidationError, "Could not get point value") + if not py.fromBytes2(data.toOpenArray(nextOffset, nextOffset * 2 - 1)): + raise newException(ValidationError, "Could not get point value") + if px.isZero() and py.isZero(): + result = T.zero() + else: + var ap: AffinePoint[T] + if not ap.init(px, py): + raise newException(ValidationError, "Point is not on curve") + result = ap.toJacobian() + +proc getFR(data: openarray[byte]): FR = + if not result.fromBytes2(data): + raise newException(ValidationError, "Could not get FR value") + +proc bn256ecAdd*(data: openarray[byte]): array[64, byte] = + var input: array[128, byte] + # Padding data + let msglen = len(data) + let tocopy = if msglen < 128: msglen else: 128 + if tocopy > 0: + copyMem(addr input[0], unsafeAddr data[0], tocopy) + var p1 = G1.getPoint(input.toOpenArray(0, 63)) + var p2 = G1.getPoint(input.toOpenArray(64, 127)) + var apo = (p1 + p2).toAffine() + if isSome(apo): + let p = apo.get() + # we can discard here because we supply proper buffer + discard p.toBytes(result) + +proc bn256ecMul*(data: openarray[byte]): array[64, byte] = + var input: array[96, byte] + + # Padding data + let msglen = len(data) + let tocopy = if msglen < 96: msglen else: 96 + if tocopy > 0: + copyMem(addr input[0], unsafeAddr data[0], tocopy) + + var p1 = G1.getPoint(input.toOpenArray(0, 63)) + var fr = getFR(input.toOpenArray(64, 95)) + var apo = (p1 * fr).toAffine() + if isSome(apo): + let p = apo.get() + # we can discard here because we supply buffer of proper size + discard p.toBytes(result) + +proc bn256ecPairing*(data: openarray[byte]): array[32, byte] = + let msglen = len(data) + if msglen mod 192 != 0: + raise newException(ValidationError, "Invalid input length") + + if msglen == 0: + # we can discard here because we supply buffer of proper size + discard BNU256.one().toBytes(result) + else: + # Calculate number of pairing pairs + let count = msglen div 192 + # Pairing accumulator + var acc = FQ12.one() + + for i in 0..