Initial commit.

This commit is contained in:
cheatfate 2018-09-27 13:35:59 +03:00
parent 7b55d96b2f
commit 4b8f6db181
19 changed files with 33407 additions and 2 deletions

86
.appveyor.yml Normal file
View File

@ -0,0 +1,86 @@
version: '{build}'
cache:
- x86_64-4.9.2-release-win32-seh-rt_v4-rev4.7z -> .appveyor.yml
- i686-4.9.2-release-win32-dwarf-rt_v4-rev4.7z -> .appveyor.yml
- Nim -> .appveyor.yml
matrix:
# We always want 32 and 64-bit compilation
fast_finish: false
platform:
- x86
- x64
install:
- setlocal EnableExtensions EnableDelayedExpansion
- IF "%PLATFORM%" == "x86" (
SET "MINGW_ARCHIVE=i686-4.9.2-release-win32-dwarf-rt_v4-rev4.7z" &
SET "MINGW_URL=https://sourceforge.net/projects/mingw-w64/files/Toolchains%%20targetting%%20Win32/Personal%%20Builds/mingw-builds/4.9.2/threads-win32/dwarf/i686-4.9.2-release-win32-dwarf-rt_v4-rev4.7z" &
SET "MINGW_DIR=mingw32"
) ELSE (
IF "%PLATFORM%" == "x64" (
SET "MINGW_ARCHIVE=x86_64-4.9.2-release-win32-seh-rt_v4-rev4.7z" &
SET "MINGW_URL=https://sourceforge.net/projects/mingw-w64/files/Toolchains%%20targetting%%20Win64/Personal%%20Builds/mingw-builds/4.9.2/threads-win32/seh/x86_64-4.9.2-release-win32-seh-rt_v4-rev4.7z" &
SET "MINGW_DIR=mingw64"
) else (
echo "Unknown platform"
)
)
- SET PATH=%CD%\%MINGW_DIR%\bin;%CD%\Nim\bin;%PATH%
# Unpack mingw
- IF NOT EXIST "%MINGW_ARCHIVE%" appveyor DownloadFile "%MINGW_URL%" -FileName "%MINGW_ARCHIVE%"
- 7z x -y "%MINGW_ARCHIVE%" > nul
# build nim from our own branch - this to avoid the day-to-day churn and
# regressions of the fast-paced Nim development while maintaining the
# flexibility to apply patches
- SET "NEED_REBUILD="
- IF NOT EXIST "Nim\\.git\\" (
git clone https://github.com/status-im/Nim.git
) ELSE (
( cd Nim ) &
( git pull ) &
( cd .. )
)
# Rebuild Nim if HEAD has moved or if we don't yet have a cached version
- IF NOT EXIST "Nim\\ver.txt" (
SET NEED_REBUILD=1
) ELSE (
( CD Nim ) &
( git rev-parse HEAD > ..\\cur_ver.txt ) &
( fc ver.txt ..\\cur_ver.txt || SET NEED_REBUILD=1 ) &
( cd .. )
)
- IF NOT EXIST "Nim\\bin\\nim.exe" SET NEED_REBUILD=1
- IF NOT EXIST "Nim\\bin\\nimble.exe" SET NEED_REBUILD=1
# after building nim, wipe csources to save on cache space
- IF DEFINED NEED_REBUILD (
cd Nim &
( IF EXIST "csources" rmdir /s /q csources ) &
git clone --depth 1 https://github.com/nim-lang/csources &
cd csources &
( IF "%PLATFORM%" == "x64" ( build64.bat ) else ( build.bat ) ) &
cd .. &
bin\nim c koch &
koch boot -d:release &
koch nimble &
git rev-parse HEAD > ver.txt &
rmdir /s /q csources
)
build_script:
- cd C:\projects\%APPVEYOR_PROJECT_SLUG%
- nimble install -y
test_script:
- nimble test
deploy: off

39
.travis.yml Normal file
View File

@ -0,0 +1,39 @@
language: c # or other C/C++ variants
sudo: false
# https://docs.travis-ci.com/user/caching/
#
# Caching the whole nim folder is better than relying on ccache - this way, we
# skip the expensive bootstrap process and linking
cache:
directories:
- nim
os:
- linux
- osx
install:
# build nim from our own branch - this to avoid the day-to-day churn and
# regressions of the fast-paced Nim development while maintaining the
# flexibility to apply patches
#
# check version of remote branch
- "export NIMVER=$(git ls-remote https://github.com/status-im/nim.git HEAD | cut -f 1)"
# after building nim, wipe csources to save on cache space
- "{ [ -f nim/$NIMVER/bin/nim ] && [ -f nim/$NIMVER/bin/nimble ] ; } ||
{ rm -rf nim ;
mkdir -p nim ;
git clone --depth=1 https://github.com/status-im/nim.git nim/$NIMVER ;
cd nim/$NIMVER ;
sh build_all.sh ;
rm -rf csources ;
cd ../.. ;
}"
- "export PATH=$PWD/nim/$NIMVER/bin:$PATH"
script:
- nimble install -y
- nimble test

201
LICENSE-APACHEv2 Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2018 Status Research & Development GmbH
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,6 +1,6 @@
MIT License
The MIT License (MIT)
Copyright (c) 2018 Status
Copyright (c) 2018 Status Research & Development GmbH
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

47
README.md Normal file
View File

@ -0,0 +1,47 @@
## BNCurve
[![Build Status](https://travis-ci.org/status-im/nim-bncurve.svg?branch=master)](https://travis-ci.org/status-im/nim-bncurve)
[![Build status](https://ci.appveyor.com/api/projects/status/hvv14l9v31mksam6/branch/master?svg=true)](https://ci.appveyor.com/project/nimbus/nim-bncurve/branch/master)
[![License: Apache](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
![Stability: experimental](https://img.shields.io/badge/stability-experimental-orange.svg)
## Introduction
This pure Nim implementation of Barreto-Naehrig pairing-friendly elliptic curve.
This is a [pairing cryptography](https://en.wikipedia.org/wiki/Pairing-based_cryptography) library written in pure Nim. It makes use of the Barreto-Naehrig (BN) curve construction from [[BCTV2015]](https://eprint.iacr.org/2013/879.pdf) to provide two cyclic groups **G<sub>1</sub>** and **G<sub>2</sub>**, with an efficient bilinear pairing:
*e: G<sub>1</sub> × G<sub>2</sub> → G<sub>T</sub>*
This code is adaptation of (bn)[https://github.com/zcash-hackworks/bn.git] library.
## Security warnings
This library, like other pairing cryptography libraries implementing this construction, is not resistant to side-channel attacks.
## Installation
Add to your `.nimble` file:
```
requires "https://github.com/status-im/nim-bncurve"
```
or install it via
```
nimble install https://github.com/status-im/nim-bncurve
```
## Build and test
```
nimble install https://github.com/status-im/nim-bncurve
nimble test
```
## License
Licensed and distributed under either of
* MIT license: [LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or 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.

10
bncurve.nim Normal file
View File

@ -0,0 +1,10 @@
# Nim Barreto-Naehrig pairing-friendly elliptic curve implementation
# Copyright (c) 2018 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.
import bncurve/[fields, groups]
export fields, groups

26
bncurve.nimble Normal file
View File

@ -0,0 +1,26 @@
packageName = "bncurve"
version = "1.0.0"
author = "Status Research & Development GmbH"
description = "Barreto-Naehrig pairing-friendly elliptic curve implementation"
license = "Apache License 2.0 or MIT"
skipDirs = @["tests", "Nim", "nim"]
### Dependencies
requires "nim > 0.18.0"
task test, "Run all tests":
exec "nim c -r tests/tarith"
exec "nim c -r -d:release tests/tarith"
exec "nim c -r tests/tfields"
exec "nim c -r -d:release tests/tfields"
exec "nim c -r tests/tgroups"
exec "nim c -r -d:release tests/tgroups"
exec "nim c -r tests/tpairing"
exec "nim c -r -d:release tests/tpairing"
exec "nim c -r tests/tvectors"
exec "nim c -r -d:release tests/tvectors"

423
bncurve/arith.nim Normal file
View File

@ -0,0 +1,423 @@
# Nim Barreto-Naehrig pairing-friendly elliptic curve implementation
# Copyright (c) 2018 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.
import options, endians
import nimcrypto/[utils, sysrand]
export options
type
BNU256* = array[4, uint64]
BNU512* = array[8, uint64]
proc setRandom*(a: var BNU512) {.inline, noinit.} =
## Set value of integer ``a`` to random value.
let ret = randomBytes(a)
doAssert(ret == 8)
proc random*(t: typedesc[BNU512]): BNU512 {.inline, noinit.} =
## Return random 512bit integer.
setRandom(result)
proc setZero*(a: var BNU256) {.inline, noinit.} =
## Set value of integer ``a`` to zero.
a[0] = 0'u64
a[1] = 0'u64
a[2] = 0'u64
a[3] = 0'u64
proc setOne*(a: var BNU256) {.inline, noinit.} =
## Set value of integer ``a`` to one.
a[0] = 1'u64
a[1] = 0'u64
a[2] = 0'u64
a[3] = 0'u64
proc zero*(t: typedesc[BNU256]): BNU256 {.inline, noinit.} =
## Return zero 256bit integer.
setZero(result)
proc one*(t: typedesc[BNU256]): BNU256 {.inline, noinit.} =
## Return one 256bit integer.
setOne(result)
proc isZero*(a: BNU256): bool {.inline, noinit.} =
## Check if integer ``a`` is zero.
(a[0] == 0'u64) and (a[1] == 0'u64) and (a[2] == 0'u64) and (a[3] == 0'u64)
proc setBit*(a: var openarray[uint64], n: int,
to: bool): bool {.inline, noinit.} =
## Set bit of integer ``a`` at position ``n`` to value ``to``.
if n >= 256:
return
let part = n shr 6
let bit = n - (part shl 6)
if to:
a[part] = a[part] or (1'u64 shl bit)
else:
a[part] = a[part] and not(1'u64 shl bit)
result = true
proc getBit*(a: openarray[uint64], n: int): bool {.inline, noinit.} =
## Get value of bit at position ``n`` in integer ``a``.
let part = n shr 6
let bit = n - (part shl 6)
result = ((a[part] and (1'u64 shl bit)) != 0)
template splitU64(n: uint64, hi, lo: untyped) =
## Split 64bit unsigned integer to 32bit parts
hi = n shr 32
lo = n and 0xFFFF_FFFF'u64
template combineU64(hi, lo: untyped): uint64 =
## Combine 64bit unsigned integer from 32bit parts
(hi shl 32) or lo
proc div2*(a: var BNU256) {.inline, noinit.} =
## Divide integer ``a`` in place by ``2``.
var t = a[3] shl 63
a[3] = a[3] shr 1
let b = a[2] shl 63
a[2] = a[2] shr 1
a[2] = a[2] or t
t = a[1] shl 63
a[1] = a[1] shr 1
a[1] = a[1] or b
a[0] = a[0] shr 1
a[0] = a[0] or t
proc mul2*(a: var BNU256) {.inline, noinit.} =
## Multiply integer ``a`` in place by ``2``.
var last = 0'u64
for i in a.mitems():
let tmp = i shr 63
i = i shl 1
i = i or last
last = tmp
proc adc(a, b: uint64, carry: var uint64): uint64 {.inline, noinit.} =
## Calculate ``a + b`` and return result, set ``carry`` to addition
## operation carry.
var a0, a1, b0, b1, c, r0, r1: uint64
splitU64(a, a1, a0)
splitU64(b, b1, b0)
let tmp0 = a0 + b0 + carry
splitU64(tmp0, c, r0)
let tmp1 = a1 + b1 + c
splitU64(tmp1, c, r1)
carry = c
result = combineU64(r1, r0)
proc addNoCarry*(a: var BNU256, b: BNU256) {.inline, noinit.} =
## Calculate integer addition ``a = a + b``.
var carry = 0'u64
a[0] = adc(a[0], b[0], carry)
a[1] = adc(a[1], b[1], carry)
a[2] = adc(a[2], b[2], carry)
a[3] = adc(a[3], b[3], carry)
assert(carry == 0)
proc subNoBorrow*(a: var BNU256, b: BNU256) {.inline, noinit.} =
## Calculate integer substraction ``a = a - b``.
proc sbb(a: uint64, b: uint64,
borrow: var uint64): uint64 {.inline, noinit.}=
var a0, a1, b0, b1, t0, r0, r1: uint64
splitU64(a, a1, a0)
splitU64(b, b1, b0)
let tmp0 = (1'u64 shl 32) + a0 - b0 - borrow
splitU64(tmp0, t0, r0)
let tmp1 = (1'u64 shl 32) + a1 - b1 - uint64(t0 == 0'u64)
splitU64(tmp1, t0, r1)
borrow = uint64(t0 == 0)
result = combineU64(r1, r0)
var borrow = 0'u64
a[0] = sbb(a[0], b[0], borrow)
a[1] = sbb(a[1], b[1], borrow)
a[2] = sbb(a[2], b[2], borrow)
a[3] = sbb(a[3], b[3], borrow)
assert(borrow == 0)
proc macDigit(acc: var openarray[uint64], pos: int, b: openarray[uint64],
c: uint64) {.noinit.} =
proc macWithCarry(a, b, c: uint64, carry: var uint64): uint64 {.noinit.} =
var
bhi, blo, chi, clo, ahi, alo, carryhi, carrylo: uint64
xhi, xlo, yhi, ylo, zhi, zlo, rhi, rlo: uint64
splitU64(b, bhi, blo)
splitU64(c, chi, clo)
splitU64(a, ahi, alo)
splitU64(carry, carryhi, carrylo)
splitU64(blo * clo + alo + carrylo, xhi, xlo)
splitU64(blo * chi, yhi, ylo)
splitU64(bhi * clo, zhi, zlo)
splitU64(x_hi + y_lo + z_lo + a_hi + carry_hi, rhi, rlo)
carry = (bhi * chi) + rhi + yhi + zhi
result = combineU64(rlo, xlo)
if c == 0'u64:
return
var carry = 0'u64
for i in pos..<len(acc):
if (i - pos) < len(b):
acc[i] = macWithCarry(acc[i], b[i - pos], c, carry)
elif carry != 0:
acc[i] = macWithCarry(acc[i], 0'u64, c, carry)
else:
break
assert(carry == 0)
proc mulReduce(a: var BNU256, by: BNU256, modulus: BNU256,
inv: uint64) {.noinit.} =
var res: array[4 * 2, uint64]
var k: uint64
for i in 0..<8:
res[i] = 0'u64
macDigit(res, 0, by, a[0])
macDigit(res, 1, by, a[1])
macDigit(res, 2, by, a[2])
macDigit(res, 3, by, a[3])
for i in 0..<4:
k = inv * res[i]
macDigit(res, i, modulus, k)
a[0] = res[4]
a[1] = res[5]
a[2] = res[6]
a[3] = res[7]
proc compare*(a: BNU256, b: BNU256): int {.noinit, inline.}=
## Compare integers ``a`` and ``b``.
## Returns ``-1`` if ``a < b``, ``1`` if ``a > b``, ``0`` if ``a == b``.
for i in countdown(3, 0):
if a[i] < b[i]:
return -1
elif a[i] > b[i]:
return 1
return 0
proc `<`*(a: BNU256, b: BNU256): bool {.noinit, inline.} =
## Return true if `a < b`.
result = (compare(a, b) == -1)
proc `<=`*(a: BNU256, b: BNU256): bool {.noinit, inline.} =
## Return true if `a <= b`.
result = (compare(a, b) <= 0)
proc `==`*(a: BNU256, b: BNU256): bool {.noinit, inline.} =
## Return true if `a == b`.
result = (compare(a, b) == 0)
proc mul*(a: var BNU256, b: BNU256, modulo: BNU256,
inv: uint64) {.inline, noinit.} =
## Multiply integer ``a`` by ``b`` (mod ``modulo``) via the Montgomery
## multiplication method.
mulReduce(a, b, modulo, inv)
if a >= modulo:
subNoBorrow(a, modulo)
proc add*(a: var BNU256, b: BNU256, modulo: BNU256) {.inline, noinit.} =
## Add integer ``b`` from integer ``a`` (mod ``modulo``).
addNoCarry(a, b)
if a >= modulo:
subNoBorrow(a, modulo)
proc sub*(a: var BNU256, b: BNU256, modulo: BNU256) {.inline, noinit.} =
## Subtract integer ``b`` from integer ``a`` (mod ``modulo``).
if a < b:
addNoCarry(a, modulo)
subNoBorrow(a, b)
proc neg*(a: var BNU256, modulo: BNU256) {.inline.} =
## Turn integer ``a`` into its additive inverse (mod ``modulo``).
if a > BNU256.zero():
var tmp = modulo
subNoBorrow(tmp, a)
a = tmp
proc isEven*(a: BNU256): bool {.inline, noinit.} =
## Check if ``a`` is even.
((a[0] and 1'u64) == 0'u64)
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
reminder.setZero()
result = some[BNU256](q)
for i in countdown(511, 0):
mul2(reminder)
let ret = reminder.setBit(0, a.getBit(i))
assert ret
if reminder >= modulo:
subNoBorrow(reminder, modulo)
if result.isSome():
if not q.setBit(i, true):
result = none[BNU256]()
else:
result = some[BNU256](q)
if result.isSome() and result.get() >= modulo:
result = none[BNU256]()
proc into*(t: typedesc[BNU512], c1: BNU256,
c0: BNU256, modulo: BNU256): BNU512 =
## Return 512bit integer of value ``c1 * modulo + c0``.
macDigit(result, 0, modulo, c1[0])
macDigit(result, 1, modulo, c1[1])
macDigit(result, 2, modulo, c1[2])
macDigit(result, 3, modulo, c1[3])
var carry = 0'u64
for i in 0..<len(result):
if len(c0) > i:
result[i] = adc(result[i], c0[i], carry)
elif carry != 0'u64:
result[i] = adc(result[i], 0'u64, carry)
else:
break
assert(carry == 0'u64)
proc fromBytes*(dst: var BNU256, src: openarray[byte]): bool =
## Create 256bit integer from big-endian bytes representation ``src``.
## Returns ``true`` if ``dst`` was successfully initialized, ``false``
## otherwise.
var buffer: array[32, byte]
if len(src) == 0:
return false
let length = if len(src) > 32: 32 else: len(src)
copyMem(addr buffer[0], unsafeAddr src[0], length)
bigEndian64(addr dst[0], addr buffer[3 * sizeof(uint64)])
bigEndian64(addr dst[1], addr buffer[2 * sizeof(uint64)])
bigEndian64(addr dst[2], addr buffer[1 * sizeof(uint64)])
bigEndian64(addr dst[3], addr buffer[0 * sizeof(uint64)])
result = true
proc fromBytes*(dst: var BNU512, src: openarray[byte]): bool {.noinit.} =
## Create 512bit integer form big-endian bytes representation ``src``.
## Returns ``true`` if ``dst`` was successfully initialized, ``false``
## otherwise.
var buffer: array[64, byte]
if len(src) == 0:
return false
let length = if len(src) > 64: 64 else: len(src)
copyMem(addr buffer[0], unsafeAddr src[0], length)
bigEndian64(addr dst[0], addr buffer[7 * sizeof(uint64)])
bigEndian64(addr dst[1], addr buffer[6 * sizeof(uint64)])
bigEndian64(addr dst[2], addr buffer[5 * sizeof(uint64)])
bigEndian64(addr dst[3], addr buffer[4 * sizeof(uint64)])
bigEndian64(addr dst[4], addr buffer[3 * sizeof(uint64)])
bigEndian64(addr dst[5], addr buffer[2 * sizeof(uint64)])
bigEndian64(addr dst[6], addr buffer[1 * sizeof(uint64)])
bigEndian64(addr dst[7], addr buffer[0 * sizeof(uint64)])
result = true
proc fromHexString*(dst: var BNU256, src: string): bool {.inline, noinit.} =
## Create 256bit integer from big-endian hexadecimal string
## representation ``src``.
## Returns ``true`` if ``dst`` was successfully initialized, ``false``
## otherwise.
result = dst.fromBytes(fromHex(src))
proc toBytes*(src: BNU256, dst: var openarray[byte]): bool {.noinit.} =
## Convert 256bit integer ``src`` to big-endian bytes representation.
## Return ``true`` if ``dst`` was successfully set, ``false`` otherwise.
if len(dst) < 4 * sizeof(uint64):
return false
bigEndian64(addr dst[0 * sizeof(uint64)], unsafeAddr src[3])
bigEndian64(addr dst[1 * sizeof(uint64)], unsafeAddr src[2])
bigEndian64(addr dst[2 * sizeof(uint64)], unsafeAddr src[1])
bigEndian64(addr dst[3 * sizeof(uint64)], unsafeAddr src[0])
result = true
proc toBytes*(src: BNU512, dst: var openarray[byte]): bool {.noinit.} =
## Convert 512bit integer ``src`` to big-endian bytes representation.
## Return ``true`` if ``dst`` was successfully set, ``false`` otherwise.
if len(dst) < 8 * sizeof(uint64):
return false
bigEndian64(addr dst[0 * sizeof(uint64)], unsafeAddr src[7])
bigEndian64(addr dst[1 * sizeof(uint64)], unsafeAddr src[6])
bigEndian64(addr dst[2 * sizeof(uint64)], unsafeAddr src[5])
bigEndian64(addr dst[3 * sizeof(uint64)], unsafeAddr src[4])
bigEndian64(addr dst[4 * sizeof(uint64)], unsafeAddr src[3])
bigEndian64(addr dst[5 * sizeof(uint64)], unsafeAddr src[2])
bigEndian64(addr dst[6 * sizeof(uint64)], unsafeAddr src[1])
bigEndian64(addr dst[7 * sizeof(uint64)], unsafeAddr src[0])
result = true
proc toString*(src: BNU256, lowercase = true): string =
## Convert 256bit integer ``src`` to big-endian hexadecimal representation.
var a: array[4 * sizeof(uint64), byte]
discard src.toBytes(a)
result = a.toHex(lowercase)
proc toString*(src: BNU512, lowercase = true): string =
## Convert 512bit integer ``src`` to big-endian hexadecimal representation.
var a: array[8 * sizeof(uint64), byte]
discard src.toBytes(a)
result = a.toHex(lowercase)
proc `$`*(src: BNU256): string =
## Return hexadecimal string representation of integer ``src``.
result = toString(src, false)
proc `$`*(src: BNU512): string =
## Return hexadecimal string representation of integer ``src``.
result = toString(src, false)
proc setRandom*(a: var BNU256, modulo: BNU256) {.noinit, inline.} =
## Set value of integer ``a`` to random value (mod ``modulo``).
var r = BNU512.random()
discard divrem(r, modulo, a)
proc random*(t: typedesc[BNU256], modulo: BNU256): BNU256 {.noinit, inline.} =
## Return random 256bit integer (mod ``modulo``).
result.setRandom(modulo)
proc invert*(a: var BNU256, modulo: BNU256) =
## Turn integer ``a`` into its multiplicative inverse (mod ``modulo``).
var u = a
var v = modulo
var b = BNU256.one()
var c = BNU256.zero()
while u != BNU256.one() and v != BNU256.one():
while u.isEven():
u.div2()
if b.isEven():
b.div2()
else:
b.addNoCarry(modulo)
b.div2()
while v.isEven():
v.div2()
if c.isEven():
c.div2()
else:
c.addNoCarry(modulo)
c.div2()
if u >= v:
u.subNoBorrow(v)
b.sub(c, modulo)
else:
v.subNoBorrow(u)
c.sub(b, modulo)
if u == BNU256.one():
a = b
else:
a = c
iterator bits*(a: BNU256): bool =
## Iterate over bits of integer ``a``.
for i in countdown(255, 0):
yield a.getBit(i)
iterator pairs*(a: BNU256): tuple[key: int, value: bool] =
## Iterate over index and bit value of integer ``a``.
var k = 0
for i in countdown(255, 0):
yield (k, a.getBit(i))
inc(k)

10
bncurve/fields.nim Normal file
View File

@ -0,0 +1,10 @@
# Nim Barreto-Naehrig pairing-friendly elliptic curve implementation
# Copyright (c) 2018 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.
import options, arith, fp, fq2, fq6, fq12
export options, arith, fp, fq2, fq6, fq12

193
bncurve/fp.nim Normal file
View File

@ -0,0 +1,193 @@
# Nim Barreto-Naehrig pairing-friendly elliptic curve implementation
# Copyright (c) 2018 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.
import arith, options
template fieldImplementation(finame, fimodulus, firsquared, fircubed,
fionep, fiinv: untyped): untyped {.dirty.} =
type finame* = distinct BNU256
proc setZero*(dst: var finame) {.noinit, inline.} =
## Set ``zero`` representation in Fp to ``dst``.
dst = finame([0'u64, 0'u64, 0'u64, 0'u64])
proc isZero*(src: finame): bool {.noinit, inline.} =
## Check if ``src`` is ``zero``.
result = BNU256(src).isZero()
proc setOne*(dst: var finame) {.noinit, inline.}=
## Set ``one`` representation in Fp to ``dst``.
dst = finame(fionep)
proc zero*(t: typedesc[finame]): finame {.noinit, inline.} =
## Return ``zero`` representation in Fp.
result.setZero()
proc one*(t: typedesc[finame]): finame {.noinit, inline.} =
## Return ``one`` representation in Fp.
result.setOne()
proc modulus*(t: typedesc[finame]): BNU256 {.noinit, inline} =
## Return ``Fp`` modulus.
result = fimodulus
proc setRandom*(dst: var finame) {.noinit, inline.} =
## Set ``dst`` to random value
var a = BNU256.random(fimodulus)
dst = finame(a)
proc random*(t: typedesc[finame]): finame {.noinit, inline.} =
## Return random ``Fp``.
result.setRandom()
proc `+`*(x, y: finame): finame {.noinit, inline.} =
## Return result of ``x + y``.
result = x
add(BNU256(result), BNU256(y), fimodulus)
proc `+=`*(x: var finame, y: finame) {.noinit, inline.} =
## Perform inplace addition ``x = x + y``.
add(BNU256(x), BNU256(y), fimodulus)
proc `-`*(x, y: finame): finame {.noinit, inline.} =
## Return result of ``x - y``.
result = x
sub(BNU256(result), BNU256(y), fimodulus)
proc `-=`*(x: var finame, y: finame) {.noinit, inline.} =
## Perform inplace substraction ``x = x - y``.
sub(BNU256(x), BNU256(y), fimodulus)
proc `*`*(x, y: finame): finame {.noinit, inline.} =
## Return result of ``x * y``.
result = x
mul(BNU256(result), BNU256(y), fimodulus, fiinv)
proc `*=`*(x: var finame, y: finame) {.noinit, inline.} =
## Perform inplace multiplication ``x = x * y``.
mul(BNU256(x), BNU256(y), fimodulus, fiinv)
proc `-`*(x: finame): finame {.noinit, inline.} =
## Negotiation of ``x``.
result = x
neg(BNU256(result), fimodulus)
proc fromString*(t: typedesc[finame],
number: string): finame {.noinit, inline.} =
## Convert decimal string representation to ``Fp``.
var numis = newSeq[finame](11)
var acc = finame.zero()
for number in numis.mitems():
number = acc
acc += finame.one()
result.setZero()
for ch in number:
assert(ch in {'0'..'9'})
let idx = ord(ch) - ord('0')
result *= numis[10]
result += numis[idx]
proc into*(t: typedesc[BNU256], num: finame): BNU256 =
## Convert Fp ``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``.
if num >= BNU256(fimodulus):
result = none[finame]()
else:
var res: finame
res = finame(num)
mul(BNU256(res), BNU256(firsquared), BNU256(fimodulus), fiinv)
result = some[finame](res)
proc fromBytes*(dst: var finame, src: openarray[byte]): bool {.noinit.} =
## Create integer FP/FQ from big-endian bytes representation ``src``.
## Returns ``true`` if ``dst`` was successfully initialized, ``false``
## otherwise.
result = false
var bn: BNU256
if bn.fromBytes(src):
var optr = finame.init(bn)
if isSome(optr):
dst = optr.get()
result = true
proc toBytes*(src: finame,
dst: var openarray[byte]): bool {.noinit, inline.} =
## Encode integer FP/FQ to big-endian bytes representation ``dst``.
## Returns ``true`` if integer was successfully serialized, ``false``
## otherwise.
result = BNU256.into(src).toBytes(dst)
proc fromHexString*(dst: var finame,
src: string): bool {.noinit, inline.} =
## Create integer FP/FQ from hexadecimal string representation ``src``.
## Returns ``true`` if ``dst`` was successfully initialized, ``false``
## otherwise.
result = false
var bn: BNU256
if bn.fromHexString(src):
var optr = finame.init(bn)
if isSome(optr):
dst = optr.get()
result = true
proc inverse*(num: finame): Option[finame] =
## Perform inversion of ``Fp``.
if num.isZero():
result = none[finame]()
else:
var res: BNU256
res = BNU256(num)
invert(res, BNU256(fimodulus))
mul(res, BNU256(fircubed), BNU256(fimodulus), fiinv)
result = some[finame](finame(res))
proc `==`*(a: finame, b: finame): bool {.inline, noinit.} =
## Return ``true`` if ``a == b``.
result = (BNU256(a) == BNU256(b))
proc squared*(a: finame): finame {.inline, noinit.} =
## Return ``a * a``.
result = a * a
proc pow*(a: finame, by: BNU256): finame {.inline, noinit.} =
## Return ``a^by``.
result = finame.one()
for i in by.bits():
result = result.squared()
if i:
result *= a
fieldImplementation(
FR,
[0x43e1f593f0000001'u64, 0x2833e84879b97091'u64,
0xb85045b68181585d'u64, 0x30644e72e131a029'u64],
[0x1bb8e645ae216da7'u64, 0x53fe3ab1e35c59e3'u64,
0x8c49833d53bb8085'u64, 0x0216d0b17f4e44a5'u64],
[0x5e94d8e1b4bf0040'u64, 0x2a489cbe1cfbb6b8'u64,
0x893cc664a19fcfed'u64, 0x0cf8594b7fcc657c'u64],
[0xac96341c4ffffffb'u64, 0x36fc76959f60cd29'u64,
0x666ea36f7879462e'u64, 0xe0a77c19a07df2f'u64],
0xc2e1f593efffffff'u64
)
fieldImplementation(
FQ,
[0x3c208c16d87cfd47'u64, 0x97816a916871ca8d'u64,
0xb85045b68181585d'u64, 0x30644e72e131a029'u64],
[0xf32cfc5b538afa89'u64, 0xb5e71911d44501fb'u64,
0x47ab1eff0a417ff6'u64, 0x06d89f71cab8351f'u64],
[0xb1cd6dafda1530df'u64, 0x62f210e6a7283db6'u64,
0xef7f0b0c0ada0afb'u64, 0x20fd6e902d592544'u64],
[0xd35d438dc58f0d9d'u64, 0xa78eb28f5c70b3d'u64,
0x666ea36f7879462c'u64, 0xe0a77c19a07df2f'u64],
0x87d20782e4866389'u64
)

309
bncurve/fq12.nim Normal file
View File

@ -0,0 +1,309 @@
# Nim Barreto-Naehrig pairing-friendly elliptic curve implementation
# Copyright (c) 2018 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.
import options
import fq6, fq2, fp, arith
const frobeniusCoeffsC1: array[4, FQ2] = [
FQ2.one(),
FQ2(
c0: Fq([12653890742059813127'u64, 14585784200204367754'u64,
1278438861261381767'u64, 212598772761311868'u64]),
c1: Fq([11683091849979440498'u64, 14992204589386555739'u64,
15866167890766973222'u64, 1200023580730561873'u64])
),
FQ2(
c0: Fq([14595462726357228530'u64, 17349508522658994025'u64,
1017833795229664280'u64, 299787779797702374'u64]),
c1: Fq.zero()
),
FQ2(
c0: Fq([3914496794763385213'u64, 790120733010914719'u64,
7322192392869644725'u64, 581366264293887267'u64]),
c1: Fq([12817045492518885689'u64, 4440270538777280383'u64,
11178533038884588256'u64, 2767537931541304486'u64])
)
]
type
FQ12* = object
c0*: FQ6
c1*: FQ6
proc init*(c0, c1: FQ6): FQ12 {.inline, noinit.} =
result.c0 = c0
result.c1 = c1
proc zero*(t: typedesc[FQ12]): FQ12 {.inline, noinit.} =
result.c0 = FQ6.zero()
result.c1 = FQ6.zero()
proc one*(t: typedesc[FQ12]): FQ12 {.inline, noinit.} =
result.c0 = FQ6.one()
result.c1 = FQ6.zero()
proc random*(t: typedesc[FQ12]): FQ12 {.inline, noinit.} =
result.c0 = FQ6.random()
result.c1 = FQ6.random()
proc isZero*(x: FQ12): bool {.inline, noinit.} =
result = (x.c0.isZero() and x.c1.isZero())
proc squared*(x: FQ12): FQ12 {.inline, noinit.} =
let ab = x.c0 * x.c1
result.c0 = (x.c1.mulByNonresidue() + x.c0) * (x.c0 + x.c1) - ab -
ab.mulByNonresidue()
result.c1 = ab + ab
proc inverse*(x: FQ12): Option[FQ12] {.inline, noinit.} =
let opt = (x.c0.squared() - (x.c1.squared().mulByNonresidue())).inverse()
if isSome(opt):
let tmp = opt.get()
result = some[FQ12](FQ12(c0: x.c0 * tmp, c1: -(x.c1 * tmp)))
else:
result = none[FQ12]()
proc `+`*(x, y: FQ12): FQ12 {.noinit, inline.} =
## Return result of ``x + y``.
result.c0 = x.c0 + y.c0
result.c1 = x.c1 + y.c1
proc `+=`*(x: var FQ12, y: FQ12) {.noinit, inline.} =
## Perform inplace addition ``x = x + y``.
x.c0 += y.c0
x.c1 += y.c1
proc `-`*(x, y: FQ12): FQ12 {.noinit, inline.} =
## Return result of ``x - y``.
result.c0 = x.c0 - y.c0
result.c1 = x.c1 - y.c1
proc `-=`*(x: var FQ12, y: FQ12) {.noinit, inline.} =
## Perform inplace substraction ``x = x - y``.
x.c0 -= y.c0
x.c1 -= y.c1
proc `*`*(x, y: FQ12): FQ12 {.noinit, inline.} =
## Return result of ``x * y``.
let aa = x.c0 * y.c0
let bb = x.c1 * y.c1
result.c0 = bb.mulByNonresidue() + aa
result.c1 = (x.c0 + x.c1) * (y.c0 + y.c1) - aa - bb
proc `*=`*(x: var FQ12, y: FQ12) {.noinit, inline.} =
## Perform inplace multiplication ``x = x * y``.
let aa = x.c0 * y.c0
let bb = x.c1 * y.c1
let cc = x.c0 + x.c1
x.c0 = bb.mulByNonresidue() + aa
x.c1 = cc * (y.c0 + y.c1) - aa - bb
proc `-`*(x: FQ12): FQ12 {.noinit, inline.} =
## Negotiation of ``x``.
result.c0 = -x.c0
result.c1 = -x.c1
proc pow*(x: FQ12, by: BNU256): FQ12 {.noinit.} =
result = FQ12.one()
for i in by.bits():
result = result.squared()
if i:
result *= x
proc pow*(x: FQ12, by: FR): FQ12 {.inline, noinit.} =
result = pow(x, BNU256.into(by))
proc frobeniusMap*(x: FQ12, power: uint64): FQ12 =
result.c0 = x.c0.frobeniusMap(power)
result.c1 = x.c1.frobeniusMap(power).scale(frobeniusCoeffsC1[power mod 12])
proc unitaryInverse*(x: FQ12): FQ12 =
result.c0 = x.c0
result.c1 = -x.c1
proc cyclotomicSquared*(x: FQ12): FQ12 =
var z0 = x.c0.c0
var z4 = x.c0.c1
var z3 = x.c0.c2
var z2 = x.c1.c0
var z1 = x.c1.c1
var z5 = x.c1.c2
var tmp = z0 * z1
let t0 = (z0 + z1) * (z1.mulByNonresidue() + z0) - tmp - tmp.mulByNonresidue()
let t1 = tmp + tmp
tmp = z2 * z3;
let t2 = (z2 + z3) * (z3.mulByNonresidue() + z2) - tmp - tmp.mulByNonresidue()
let t3 = tmp + tmp
tmp = z4 * z5;
let t4 = (z4 + z5) * (z5.mulByNonresidue() + z4) - tmp - tmp.mulByNonresidue()
let t5 = tmp + tmp
z0 = t0 - z0
z0 = z0 + z0
z0 = z0 + t0
z1 = t1 + z1
z1 = z1 + z1
z1 = z1 + t1
tmp = t5.mulByNonresidue()
z2 = tmp + z2
z2 = z2 + z2
z2 = z2 + tmp
z3 = t4 - z3
z3 = z3 + z3
z3 = z3 + t4
z4 = t2 - z4
z4 = z4 + z4
z4 = z4 + t2
z5 = t3 + z5
z5 = z5 + z5
z5 = z5 + t3
result.c0 = init(z0, z4, z3)
result.c1 = init(z2, z1, z5)
proc cyclotomicPow*(x: FQ12, by: BNU256): FQ12 =
result = FQ12.one()
var foundOne = false
for i in by.bits():
if foundOne:
result = result.cyclotomicSquared()
if i:
foundOne = true
result = x * result
proc expByNegZ*(x: FQ12): FQ12 =
let uconst = BNU256([4965661367192848881'u64, 0'u64, 0'u64, 0'u64])
result = x.cyclotomicPow(uconst).unitaryInverse()
proc finalExpFirstChunk*(x: FQ12): Option[FQ12] =
let opt = x.inverse()
if isSome(opt):
let b = opt.get()
let a = x.unitaryInverse()
let c = a * b
let d = c.frobeniusMap(2)
result = some[FQ12](d * c)
else:
result = none[FQ12]()
proc finalExpLastChunk*(x: FQ12): FQ12 =
let a = x.expByNegZ()
let b = a.cyclotomicSquared()
let c = b.cyclotomicSquared()
let d = c * b
let e = d.expByNegZ()
let f = e.cyclotomicSquared()
let g = f.expByNegZ()
let h = d.unitaryInverse()
let i = g.unitaryInverse()
let j = i * e
let k = j * h
let ll = k * b
let m = k * e
let n = x * m
let o = ll.frobeniusMap(1)
let p = o * n
let q = k.frobeniusMap(2)
let r = q * p
let s = x.unitaryInverse()
let t = s * ll
let u = t.frobeniusMap(3)
let v = u * r
result = v
proc finalExponentiation*(x: FQ12): Option[FQ12] =
let opt = x.finalExpFirstChunk()
if opt.isSome():
result = some[FQ12](opt.get().finalExpLastChunk())
else:
result = none[FQ12]()
proc mulBy024*(x: FQ12, ell0, ellvw, ellvv: FQ2): FQ12 =
var
z0, z1, z2, z3, z4, z5: FQ2
x0, x2, x4, d0, d2, d4: FQ2
s0, s1, t0, t1, t2, t3, t4: FQ2
z0 = x.c0.c0
z1 = x.c0.c1
z2 = x.c0.c2
z3 = x.c1.c0
z4 = x.c1.c1
z5 = x.c1.c2
x0 = ell0
x2 = ellvv
x4 = ellvw
d0 = z0 * x0
d2 = z2 * x2
d4 = z4 * x4
t2 = z0 + z4
t1 = z0 + z2
s0 = z1 + z3 + z5
s1 = z1 * x2
t3 = s1 + d4
t4 = t3.mulByNonresidue() + d0
z0 = t4
t3 = z5 * x4
s1 = s1 + t3
t3 = t3 + d2
t4 = t3.mulByNonresidue()
t3 = z1 * x0
s1 = s1 + t3
t4 = t4 + t3
z1 = t4
t0 = x0 + x2
t3 = t1 * t0 - d0 - d2
t4 = z3 * x4
s1 = s1 + t4
t3 = t3 + t4
t0 = z2 + z4
z2 = t3
t1 = x2 + x4
t3 = t0 * t1 - d2 - d4
t4 = t3.mulByNonresidue()
t3 = z3 * x0
s1 = s1 + t3
t4 = t4 + t3
z3 = t4
t3 = z5 * x2
s1 = s1 + t3
t4 = t3.mulByNonresidue()
t0 = x0 + x4
t3 = t2 * t0 - d0 - d4
t4 = t4 + t3
z4 = t4
t0 = x0 + x2 + x4
t3 = s0 * t0 - s1
z5 = t3
result.c0 = init(z0, z1, z2)
result.c1 = init(z3, z4, z5)

149
bncurve/fq2.nim Normal file
View File

@ -0,0 +1,149 @@
# Nim Barreto-Naehrig pairing-friendly elliptic curve implementation
# Copyright (c) 2018 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.
import options
import fp, arith
type
FQ2* = object
c0*: FQ
c1*: FQ
const
FQNonResidue = FQ([
0x68c3488912edefaa'u64, 0x8d087f6872aabf4f'u64,
0x51e1a24709081231'u64, 0x2259d6b14729c0fa'u64
])
FQ2NonResidue* = FQ2(
c0: FQ([
0xf60647ce410d7ff7'u64, 0x2f3d6f4dd31bd011'u64,
0x2943337e3940c6d1'u64, 0x1d9598e8a7e39857'u64
]),
c1: FQ([
0xd35d438dc58f0d9d'u64, 0x0a78eb28f5c70b3d'u64,
0x666ea36f7879462c'u64, 0x0e0a77c19a07df2f'u64
])
)
proc init*(c0, c1: FQ): FQ2 {.inline, noinit.} =
result.c0 = c0
result.c1 = c1
proc zero*(t: typedesc[FQ2]): FQ2 {.inline, noinit.} =
result.c0 = FQ.zero()
result.c1 = FQ.zero()
proc one*(t: typedesc[FQ2]): FQ2 {.inline, noinit.} =
result.c0 = FQ.one()
result.c1 = FQ.zero()
proc random*(t: typedesc[FQ2]): FQ2 {.inline, noinit.} =
result.c0 = FQ.random()
result.c1 = FQ.random()
proc isZero*(x: FQ2): bool {.inline, noinit.} =
result = (x.c0.isZero() and x.c1.isZero())
proc scale*(x: FQ2, by: FQ): FQ2 {.inline, noinit.} =
result.c0 = x.c0 * by
result.c1 = x.c1 * by
proc squared*(x: FQ2): FQ2 {.inline, noinit.} =
let ab = x.c0 * x.c1
result.c0 = (x.c1 * FQNonResidue + x.c0) * (x.c0 + x.c1) -
ab - ab * FQNonResidue
result.c1 = ab + ab
proc inverse*(x: FQ2): Option[FQ2] {.inline, noinit.} =
let opt = (x.c0.squared() - (x.c1.squared() * FQNonResidue)).inverse()
if isSome(opt):
let tmp = opt.get()
result = some[FQ2](FQ2(c0: x.c0 * tmp, c1: -(x.c1 * tmp)))
else:
result = none[FQ2]()
proc `+`*(x, y: FQ2): FQ2 {.noinit, inline.} =
## Return result of ``x + y``.
result.c0 = x.c0 + y.c0
result.c1 = x.c1 + y.c1
proc `+=`*(x: var FQ2, y: FQ2) {.noinit, inline.} =
## Perform inplace addition ``x = x + y``.
x.c0 += y.c0
x.c1 += y.c1
proc `-`*(x, y: FQ2): FQ2 {.noinit, inline.} =
## Return result of ``x - y``.
result.c0 = x.c0 - y.c0
result.c1 = x.c1 - y.c1
proc `-=`*(x: var FQ2, y: FQ2) {.noinit, inline.} =
## Perform inplace substraction ``x = x - y``.
x.c0 -= y.c0
x.c1 -= y.c1
proc `*`*(x, y: FQ2): FQ2 {.noinit, inline.} =
## Return result of ``x * y``.
let aa = x.c0 * y.c0
let bb = x.c1 * y.c1
result.c0 = bb * FQNonResidue + aa
result.c1 = (x.c0 + x.c1) * (y.c0 + y.c1) - aa - bb
proc `*=`*(x: var FQ2, y: FQ2) {.noinit, inline.} =
## Perform inplace multiplication ``x = x * y``.
let aa = x.c0 * y.c0
let bb = x.c1 * y.c1
let cc = x.c1 + x.c1
x.c0 = bb * FQNonResidue + aa
x.c1 = cc * (y.c0 + y.c1) - aa - bb
proc `-`*(x: FQ2): FQ2 {.noinit, inline.} =
## Negotiation of ``x``.
result.c0 = -x.c0
result.c1 = -x.c1
proc frobeniusMap*(x: FQ2, power: uint64): FQ2 =
if power mod 2 == 0:
result = x
else:
result.c0 = x.c0
result.c1 = x.c1 * FQNonResidue
proc `==`*(x: FQ2, y: FQ2): bool =
## Return ``true`` if ``a == b``.
result = (x.c0 == y.c0) and (x.c1 == y.c1)
proc mulByNonresidue*(x: FQ2): FQ2 =
result = x * FQ2NonResidue
proc fromBytes*(dst: var FQ2, src: openarray[byte]): bool {.noinit.} =
## Create 512bit integer FQ2 from big-endian bytes representation ``src``.
## Returns ``true`` if ``dst`` was successfully initialized, ``false``
## otherwise.
result = false
var value: BNU512
if fromBytes(value, src):
var b0: BNU256
var b1o = value.divrem(FQ.modulus(), b0)
if isSome(b1o):
var c0o = FQ.init(b0)
var c1o = FQ.init(b1o.get())
if isSome(c0o) and isSome(c1o):
dst = init(c0o.get(), c1o.get())
result = true
proc toBytes*(src: FQ2,
dst: var openarray[byte]): bool {.noinit, inline.} =
## Encode 512bit integer FQ2 to big-endian bytes representation ``dst``.
## Returns ``true`` if integer was successfully serialized, ``false``
## otherwise.
var c0, c1: BNU256
c0 = BNU256.into(src.c0)
c1 = BNU256.into(src.c1)
result = BNU512.into(c1, c0, FQ.modulus()).toBytes(dst)

178
bncurve/fq6.nim Normal file
View File

@ -0,0 +1,178 @@
# Nim Barreto-Naehrig pairing-friendly elliptic curve implementation
# Copyright (c) 2018 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.
import options
import fq2, fp, arith
const frobeniusCoeffsC1: array[4, FQ2] = [
FQ2.one(),
FQ2(
c0: Fq([13075984984163199792'u64, 3782902503040509012'u64,
8791150885551868305'u64, 1825854335138010348'u64]),
c1: Fq([7963664994991228759'u64, 12257807996192067905'u64,
13179524609921305146'u64, 2767831111890561987'u64])
),
FQ2(
c0: Fq([3697675806616062876'u64, 9065277094688085689'u64,
6918009208039626314'u64, 2775033306905974752'u64]),
c1: Fq.zero()
),
FQ2(
c0: Fq([14532872967180610477'u64, 12903226530429559474'u64,
1868623743233345524'u64, 2316889217940299650'u64]),
c1: Fq([12447993766991532972'u64, 4121872836076202828'u64,
7630813605053367399'u64, 740282956577754197'u64])
)
]
const frobeniusCoeffsC2: array[4, FQ2] = [
FQ2.one(),
FQ2(
c0: Fq([8314163329781907090'u64, 11942187022798819835'u64,
11282677263046157209'u64, 1576150870752482284'u64]),
c1: Fq([6763840483288992073'u64, 7118829427391486816'u64,
4016233444936635065'u64, 2630958277570195709'u64])
),
FQ2(
c0: Fq([8183898218631979349'u64, 12014359695528440611'u64,
12263358156045030468'u64, 3187210487005268291'u64]),
c1: Fq.zero()
),
FQ2(
c0: Fq([4938922280314430175'u64, 13823286637238282975'u64,
15589480384090068090'u64, 481952561930628184'u64]),
c1: Fq([3105754162722846417'u64, 11647802298615474591'u64,
13057042392041828081'u64, 1660844386505564338'u64])
)
]
type
FQ6* = object
c0*: FQ2
c1*: FQ2
c2*: FQ2
proc init*(c0, c1, c2: FQ2): FQ6 {.inline, noinit.} =
result.c0 = c0
result.c1 = c1
result.c2 = c2
proc zero*(t: typedesc[FQ6]): FQ6 {.inline, noinit.} =
result.c0 = FQ2.zero()
result.c1 = FQ2.zero()
result.c2 = FQ2.zero()
proc one*(t: typedesc[FQ6]): FQ6 {.inline, noinit.} =
result.c0 = FQ2.one()
result.c1 = FQ2.zero()
result.c2 = FQ2.zero()
proc random*(t: typedesc[FQ6]): FQ6 {.inline, noinit.} =
result.c0 = FQ2.random()
result.c1 = FQ2.random()
result.c2 = FQ2.random()
proc isZero*(x: FQ6): bool {.inline, noinit.} =
result = (x.c0.isZero() and x.c1.isZero() and x.c2.isZero())
proc scale*(x: FQ6, by: FQ2): FQ6 {.inline, noinit.} =
result.c0 = x.c0 * by
result.c1 = x.c1 * by
result.c2 = x.c2 * by
proc squared*(x: FQ6): FQ6 {.inline, noinit.} =
let s0 = x.c0.squared()
let ab = x.c0 * x.c1
let s1 = ab + ab
let s2 = (x.c0 - x.c1 + x.c2).squared()
let bc = x.c1 * x.c2
let s3 = bc + bc
let s4 = x.c2.squared()
result.c0 = s0 + s3.mulByNonresidue()
result.c1 = s1 + s4.mulByNonresidue()
result.c2 = s1 + s2 + s3 - s0 - s4
proc inverse*(x: FQ6): Option[FQ6] {.inline, noinit.} =
let c0 = x.c0.squared() - (x.c1 * x.c2.mulByNonresidue())
let c1 = x.c2.squared().mulByNonresidue() - (x.c0 * x.c1)
let c2 = x.c1.squared() - (x.c0 * x.c2)
let opt = ((x.c2 * c1 + x.c1 * c2).mulByNonresidue() +
x.c0 * c0).inverse()
if isSome(opt):
let tmp = opt.get()
result = some[FQ6](FQ6(c0: tmp * c0, c1: tmp * c1, c2: tmp * c2))
else:
result = none[FQ6]()
proc `+`*(x, y: FQ6): FQ6 {.noinit, inline.} =
## Return result of ``x + y``.
result.c0 = x.c0 + y.c0
result.c1 = x.c1 + y.c1
result.c2 = x.c2 + y.c2
proc `+=`*(x: var FQ6, y: FQ6) {.noinit, inline.} =
## Perform inplace addition ``x = x + y``.
x.c0 += y.c0
x.c1 += y.c1
x.c2 += y.c2
proc `-`*(x, y: FQ6): FQ6 {.noinit, inline.} =
## Return result of ``x - y``.
result.c0 = x.c0 - y.c0
result.c1 = x.c1 - y.c1
result.c2 = x.c2 - y.c2
proc `-=`*(x: var FQ6, y: FQ6) {.noinit, inline.} =
## Perform inplace substraction ``x = x - y``.
x.c0 -= y.c0
x.c1 -= y.c1
x.c2 -= y.c2
proc `*`*(x, y: FQ6): FQ6 {.noinit, inline.} =
## Return result of ``x * y``.
let aa = x.c0 * y.c0
let bb = x.c1 * y.c1
let cc = x.c2 * y.c2
result.c0 = ((x.c1 + x.c2) * (y.c1 + y.c2) - bb - cc).mulByNonresidue() +
aa
result.c1 = (x.c0 + x.c1) * (y.c0 + y.c1) - aa - bb + cc.mulByNonresidue()
result.c2 = (x.c0 + x.c2) * (y.c0 + y.c2) - aa + bb - cc
proc `*=`*(x: var FQ6, y: FQ6) {.noinit, inline.} =
## Perform inplace multiplication ``x = x * y``.
let aa = x.c0 * y.c0
let bb = x.c1 * y.c1
let cc = x.c2 * y.c2
let dd = x.c1 + x.c2
let ee = x.c0 + x.c1
let ff = x.c0 + x.c2
x.c0 = (dd * (y.c1 + y.c2) - bb - cc).mulByNonresidue() + aa
x.c1 = ee * (y.c0 + y.c1) - aa - bb + cc.mulByNonresidue()
x.c2 = ff * (y.c0 + y.c2) - aa - bb - cc
proc `-`*(x: FQ6): FQ6 {.noinit, inline.} =
## Negotiation of ``x``.
result.c0 = -x.c0
result.c1 = -x.c1
result.c2 = -x.c2
proc frobeniusMap*(x: FQ6, power: uint64): FQ6 =
result.c0 = x.c0.frobeniusMap(power)
result.c1 = x.c1.frobeniusMap(power) * frobeniusCoeffsC1[power mod 6]
result.c2 = x.c2.frobeniusMap(power) * frobeniusCoeffsC2[power mod 6]
proc `==`*(x: FQ6, y: FQ6): bool =
## Return ``true`` if ``a == b``.
result = (x.c0 == y.c0) and (x.c1 == y.c1) and (x.c2 == y.c2)
proc mulByNonresidue*(x: FQ6): FQ6 =
result.c0 = x.c2.mulByNonresidue()
result.c1 = x.c0
result.c2 = x.c1

515
bncurve/groups.nim Normal file
View File

@ -0,0 +1,515 @@
# Nim Barreto-Naehrig pairing-friendly elliptic curve implementation
# Copyright (c) 2018 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.
import fields, arith, options
export fields, arith, options
import nimcrypto/utils
type
G1* = object
G2* = object
Point*[T: G1|G2] = object
when T is G1:
x*, y*, z*: FQ
else:
x*, y*, z*: FQ2
AffinePoint*[T: G1|G2] = object
when T is G1:
x*, y*: FQ
else:
x*, y*: FQ2
Coeff*[T: G1|G2] = object
when T is G1:
b*: FQ
else:
b*: FQ2
EllCoeffs* = object
ell_0*: FQ2
ell_vw*: FQ2
ell_vv*: FQ2
G2Precomp* = object
q*: AffinePoint[G2]
coeffs*: seq[EllCoeffs]
const
G1One = Point[G1](
x: FQ.one(),
y: FQ([0xa6ba871b8b1e1b3a'u64, 0x14f1d651eb8e167b'u64,
0xccdd46def0f28c58'u64, 0x1c14ef83340fbe5e'u64]),
z: FQ.one()
)
G1B = FQ([0x7a17caa950ad28d7'u64, 0x1f6ac17ae15521b9'u64,
0x334bea4e696bd284'u64, 0x2a1f6744ce179d8e'u64])
G2One = Point[G2](
x: FQ2(
c0: FQ([0x8e83b5d102bc2026'u64, 0xdceb1935497b0172'u64,
0xfbb8264797811adf'u64, 0x19573841af96503b'u64]),
c1: FQ([0xafb4737da84c6140'u64, 0x6043dd5a5802d8c4'u64,
0x09e950fc52a02f86'u64, 0x14fef0833aea7b6b'u64])
),
y: FQ2(
c0: FQ([0x619dfa9d886be9f6'u64, 0xfe7fd297f59e9b78'u64,
0xff9e1a62231b7dfe'u64, 0x28fd7eebae9e4206'u64]),
c1: FQ([0x64095b56c71856ee'u64, 0xdc57f922327d3cbb'u64,
0x55f935be33351076'u64, 0x0da4a0e693fd6482'u64])
),
z: FQ2.one()
)
G2B = FQ2(
c0: FQ([0x3bf938e377b802a8'u64, 0x020b1b273633535d'u64,
0x26b7edf049755260'u64, 0x2514c6324384a86d'u64]),
c1: FQ([0x38e7ecccd1dcff67'u64, 0x65f0b37d93ce0d3e'u64,
0xd749d0dd22ac00aa'u64, 0x0141b9ce4a688d4d'u64])
)
AteLoopCount = BNU256([
0x9d797039be763ba8'u64, 0x0000000000000001'u64,
0x0000000000000000'u64, 0x0000000000000000'u64
])
TwoInv = FQ([
9781510331150239090'u64, 15059239858463337189'u64,
10331104244869713732'u64, 2249375503248834476'u64
])
Twist = FQ2NonResidue
TwistMulByQx = FQ2(
c0: FQ([
13075984984163199792'u64, 3782902503040509012'u64,
8791150885551868305'u64, 1825854335138010348'u64
]),
c1: FQ([
7963664994991228759'u64, 12257807996192067905'u64,
13179524609921305146'u64, 2767831111890561987'u64
])
)
TwistMulByQy = FQ2(
c0: FQ([
16482010305593259561'u64, 13488546290961988299'u64,
3578621962720924518'u64, 2681173117283399901'u64
]),
c1: FQ([
11661927080404088775'u64, 553939530661941723'u64,
7860678177968807019'u64, 3208568454732775116'u64
])
)
proc one*[T: G1|G2](t: typedesc[T]): Point[T] {.inline, noinit.} =
when T is G1:
result = G1One
else:
result = G2One
# proc one*(t: typedesc[Gt]): Gt {.inline, noinit.} =
# result = FQ12.one()
proc name*[T: G1|G2](t: typedesc[T]): string {.inline, noinit.} =
when T is G1:
result = "G1"
else:
result = "G2"
proc coeff*[T: G1|G2](t: typedesc[T]): Coeff[T] {.inline, noinit.} =
when T is G1:
result = G1B
else:
result = G2B
proc zero*[T: G1|G2](t: typedesc[T]): Point[T] {.inline, noinit.} =
when T is G1:
result.x = FQ.zero()
result.y = FQ.one()
result.z = FQ.zero()
else:
result.x = FQ2.zero()
result.y = FQ2.one()
result.z = FQ2.zero()
proc isZero*[T: G1|G2](p: Point[T]): bool {.inline, noinit.} =
result = p.z.isZero()
proc double*[T: G1|G2](p: Point[T]): Point[T] {.noinit.} =
let a = p.x.squared()
let b = p.y.squared()
let c = b.squared()
var d = (p.x + b).squared() - a - c
d = d + d
let e = a + a + a
let f = e.squared()
let x3 = f - (d + d)
var eightc = c + c
eightc = eightc + eightc
eightc = eightc + eightc
let y1z1 = p.y * p.z
result.x = x3
result.y = e * (d - x3) - eightc
result.z = y1z1 + y1z1
proc `*`*[T: G1|G2](p: Point[T], by: FR): Point[T] =
result = T.zero()
var foundOne = false
for i in BNU256.into(by).bits():
if foundOne:
result = result.double()
if i:
foundOne = true
result = result + p
proc random*[T: G1|G2](t: typedesc[T]): Point[T] {.inline, noinit.} =
result = t.one() * FR.random()
proc `+`*[T: G1|G2](p1, p2: Point[T]): Point[T] {.noinit.} =
if p1.isZero():
return p2
if p2.isZero():
return p1
let z1squared = p1.z.squared()
let z2squared = p2.z.squared()
let u1 = p1.x * z2squared
let u2 = p2.x * z1squared
let z1cubed = p1.z * z1squared
let z2cubed = p2.z * z2squared
let s1 = p1.y * z2cubed
let s2 = p2.y * z1cubed
if u1 == u2 and s1 == s2:
result = p1.double()
else:
let h = u2 - u1
let s2minuss1 = s2 - s1
let i = (h + h).squared()
let j = h * i
let r = s2minuss1 + s2minuss1
let v = u1 * i
let s1j = s1 * j
let x3 = r.squared() - j - (v + v)
result.x = x3
result.y = r * (v - x3) - (s1j + s1j)
result.z = ((p1.z + p2.z).squared() - z1squared - z2squared) * h
proc `-`*[T: G1|G2](p: Point[T]): Point[T] {.inline, noinit.} =
if p.isZero():
return p
else:
result.x = p.x
result.y = -p.y
result.z = p.z
proc `-`*[T: G1|G2](p: AffinePoint[T]): AffinePoint[T] {.inline, noinit.} =
result.x = p.x
result.y = -p.y
proc `-`*[T: G1|G2](p1, p2: Point[T]): Point[T] {.inline, noinit.} =
result = p1 + (-p2)
proc `==`*[T: G1|G2](p1, p2: Point[T]): bool =
if p1.isZero():
return p2.isZero()
if p2.isZero():
return false
let z1squared = p1.z.squared()
let z2squared = p2.z.squared()
if (p1.x * z2squared) != (p2.x * z1squared):
return false
let z1cubed = p1.z * z1squared
let z2cubed = p2.z * z2squared
if (p1.y * z2cubed) != (p2.y * z1cubed):
return false
return true
proc toJacobian*[T: G1|G2](p: AffinePoint[T]): Point[T] {.inline, noinit.} =
## Convert affine coordinates' point ``p`` to point.
result.x = p.x
result.y = p.y
when T is G1:
result.z = FQ.one()
else:
result.z = FQ2.one()
proc toAffine*[T: G1|G2](p: Point[T]): Option[AffinePoint[T]] =
## Attempt to convert point ``p`` to affine coordinates.
when T is G1:
var fone = FQ.one()
else:
var fone = FQ2.one()
if p.z.isZero():
result = none[AffinePoint[T]]()
elif p.z == fone:
result = some[AffinePoint[T]](AffinePoint[T](x: p.x, y: p.y))
else:
let ozinv = p.z.inverse()
if isSome(ozinv):
let zinv = ozinv.get()
var zinvsquared = zinv.squared()
result = some[AffinePoint[T]](
AffinePoint[T](
x: p.x * zinvsquared,
y: p.y * (zinvsquared * zinv)
)
)
else:
result = none[AffinePoint[T]]()
proc normalize*(p: var Point[G2]) {.inline, noinit.} =
let aopt = p.toAffine()
if isSome(aopt):
p = aopt.get().toJacobian()
else:
return
proc isOnCurve*[T: G1|G2](p: AffinePoint[T]): bool =
when T is G1:
result = (p.y.squared() == (p.x.squared() * p.x) + G1B)
else:
result = (p.y.squared() == (p.x.squared() * p.x) + G2B)
proc mulByQ(p: AffinePoint[G2]): AffinePoint[G2] =
result.x = TwistMulByQx * p.x.frobeniusMap(1)
result.y = TwistMulByQy * p.y.frobeniusMap(1)
proc mixedAdditionStepForFlippedML(p: var Point[G2],
base: AffinePoint[G2]): EllCoeffs =
let d = p.x - p.z * base.x
let e = p.y - p.z * base.y
let f = d.squared()
let g = e.squared()
let h = d * f
let i = p.x * f
let j = p.z * g + h - (i + i)
p.x = d * j
p.y = e * (i - j) - h * p.y
p.z = p.z * h
result.ell_0 = Twist * (e * base.x - d * base.y)
result.ell_vv = -e
result.ell_vw = d
proc doublingStepForFlippedML(p: var Point[G2]): EllCoeffs =
let a = (p.x * p.y).scale(TwoInv)
let b = p.y.squared()
let c = p.z.squared()
let d = c + c + c
let e = G2B * d
let f = e + e + e
let g = (b + f).scale(TwoInv)
let h = (p.y + p.z).squared() - (b + c)
let i = e - b
let j = p.x.squared()
let e_sq = e.squared()
p.x = a * (b - f)
p.y = g.squared() - (e_sq + e_sq + e_sq)
p.z = b * h
result.ell_0 = Twist * i
result.ell_vw = -h
result.ell_vv = j + j + j
proc precompute*(p: AffinePoint[G2]): G2Precomp =
var r = p.toJacobian()
result.coeffs = newSeqOfCap[EllCoeffs](102)
var foundOne = false
for i in AteLoopCount.bits():
if not foundOne:
foundOne = i
continue
result.coeffs.add(r.doublingStepForFlippedML())
if i:
result.coeffs.add(r.mixedAdditionStepForFlippedML(p))
let q1 = p.mulByQ()
let q2 = -(q1.mulByQ())
result.coeffs.add(r.mixedAdditionStepForFlippedML(q1))
result.coeffs.add(r.mixedAdditionStepForFlippedML(q2))
result.q = p
proc millerLoop*(pc: G2Precomp, g1: AffinePoint[G1]): FQ12 =
result = FQ12.one()
var idx = 0
var foundOne = false
var c: EllCoeffs
for i in AteLoopCount.bits():
if not foundOne:
foundOne = i
continue
c = pc.coeffs[idx]
inc(idx)
result = result.squared().mulBy024(c.ell_0, c.ell_vw.scale(g1.y),
c.ell_vv.scale(g1.x))
if i:
c = pc.coeffs[idx]
idx += 1
result = result.mulBy024(c.ell_0, c.ell_vw.scale(g1.y),
c.ell_vv.scale(g1.x))
c = pc.coeffs[idx]
idx += 1
result = result.mulBy024(c.ell_0, c.ell_vw.scale(g1.y), c.ell_vv.scale(g1.x))
c = pc.coeffs[idx]
result = result.mulBy024(c.ell_0, c.ell_vw.scale(g1.y), c.ell_vv.scale(g1.x))
proc pairing*(p: Point[G1], q: Point[G2]): FQ12 {.noinit, inline.} =
result = FQ12.one()
var optp = p.toAffine()
var optq = q.toAffine()
if optp.isSome() and optq.isSome():
let pc = optq.get().precompute()
let ores = finalExponentiation(pc.millerLoop(optp.get()))
if ores.isSome():
result = ores.get()
proc toBytes*[T: G1|G2](p: AffinePoint[T], dst: var openarray[byte]): bool =
## Encode affine point coordinates (x, y) to big-endian bytes representation
## ``dst``.
## Returns ``true`` if coordinates was successfully serialized, ``false``
## otherwise.
when T is G1:
if len(dst) >= 64:
if p.x.toBytes(toOpenArray(dst, 0, 31)):
if p.y.toBytes(toOpenArray(dst, 32, 63)):
result = true
else:
if len(dst) >= 128:
if p.x.toBytes(toOpenArray(dst, 0, 63)):
if p.y.toBytes(toOpenArray(dst, 64, 127)):
result = true
proc fromBytes*[T: G1|G2](p: var AffinePoint[T], src: openarray[byte]): bool =
## Decode affine point coordinates (x, y) from big endian bytes representation
## ``src``.
## Returns ``true`` if coordinates was successfully serialized, ``false``
## otherwise.
when T is G1:
const
nextOffset = 32
coeff = G1B
var
x, y: FQ
point: Point[G1]
else:
const
nextOffset = 64
coeff = G2B
var
x, y: FQ2
point: Point[G2]
if len(src) >= nextOffset * 2:
if x.fromBytes(src):
if y.fromBytes(toOpenArray(src, nextOffset, len(src) - 1)):
if y.squared() == (x.squared() * x) + coeff:
## Check if point on curve.
point.x = x
point.y = y
when T is G1:
point.z = FQ.one()
else:
point.z = FQ2.one()
if (point * (-FR.one())) + point == T.zero():
p.x = x
p.y = y
result = true
else:
## Point is not in the subgroup
discard
proc fromHexString*[T: G1|G2](p: var AffinePoint[T],
src: string): bool {.inline.} =
## Decode affine point coordinates (x, y) from hexadecimal string
## representation ``src``.
## Returns ``true`` if coordinates was successfully serialized, ``false``
## otherwise.
## ``Note:`` Can raise exception on malformed hexadecimal string.
result = fromBytes(p, fromHex(src))
proc toHexString*[T: G1|G2](p: AffinePoint[T],
lowercase = false): string {.inline.} =
## Encode affine point coordinates (x, y) and return hexadecimal string
## representation.
when T is G1:
var buffer: array[64, byte]
else:
var buffer: array[128, byte]
if toBytes(p, buffer):
result = toHex(buffer, lowercase)
proc toBytes*[T: G1|G2](p: Point[T],
dst: var openarray[byte]): bool {.inline.} =
## Encode point coordinates (x, y, z) to big-endian bytes representation
## ``dst``.
## Returns ``true`` if coordinates was successfully serialized, ``false``
## otherwise.
when T is G1:
const outputSize = 64
else:
const outputSize = 128
if p.isZero():
if len(dst) >= 1:
dst[0] = 0x00'u8
result = true
else:
result = false
else:
if len(dst) >= 1 + outputSize:
var apo = p.toAffine()
if isSome(apo):
dst[0] = 0x04'u8
result = apo.get().toBytes(toOpenArray(dst, 1, outputSize))
proc fromBytes*[T: G1|G2](p: var Point[T],
src: openarray[byte]): bool {.inline.} =
## Decode affine point coordinates (x, y, z) from big endian bytes
## representation ``src``.
## Returns ``true`` if coordinates was successfully serialized, ``false``
## otherwise.
when T is G1:
const inputSize = 64
else:
const inputSize = 128
if len(src) > 0:
if src[0] == 0x00'u8:
p = T.zero()
result = true
elif src[0] == 0x04'u8:
if len(src) >= inputSize + 1:
var ap: AffinePoint[T]
if ap.fromBytes(toOpenArray(src, 1, inputSize)):
p = toJacobian(ap)
result = true
proc fromHexString*[T: G1|G2](p: var Point[T], src: string): bool {.inline.} =
## Decode point coordinates (x, y, z) from hexadecimal string
## representation ``src``.
## Returns ``true`` if coordinates was successfully serialized, ``false``
## otherwise.
## ``Note:`` Can raise exception on malformed hexadecimal string.
result = fromBytes(p, fromHex(src))
proc toHexString*[T: G1|G2](p: Point[T], lowercase = false): string {.inline.} =
## Encode affine point coordinates (x, y, z) and return hexadecimal string
## representation.
when T is G1:
var buffer: array[64 + 1, byte]
else:
var buffer: array[128 + 1, byte]
if toBytes(p, buffer):
result = toHex(buffer, lowercase)

186
tests/tarith.nim Normal file
View File

@ -0,0 +1,186 @@
import unittest
import ../bncurve/arith
when isMainModule:
let modulo = [
0x3c208c16d87cfd47'u64, 0x97816a916871ca8d'u64,
0xb85045b68181585d'u64, 0x30644e72e131a029'u64
]
suite "Modular arithmetic test suite":
test "[256] Serialize/Deserialize tests":
for i in 0..<100:
var c0, c1, c2: BNU256
var c0b: array[4 * sizeof(uint64), byte]
c0 = BNU256.random(modulo)
var c0s = c0.toString()
check:
c0.toBytes(c0b) == true
c1.fromBytes(c0b) == true
c2.fromHexString(c0s) == true
c0 == c1
c0 == c2
test "[512] Serialize/Deserialize tests":
for i in 0..<100:
var cb: BNU512
var cbs: array[8 * sizeof(uint64), byte]
var e0 = BNU256.random(modulo)
var e1 = BNU256.random(modulo)
var bb12 = BNU512.into(e1, e0, modulo)
check:
bb12.toBytes(cbs) == true
cb.fromBytes(cbs) == true
var c0: BNU256
var c1opt = cb.divrem(modulo, c0)
check:
isSome(c1opt) == true
c1opt.get() == e1
c0 == e0
test "Setting bits":
var moduloS = [
0xfffffffffffffffff'u64, 0xfffffffffffffffff'u64,
0xfffffffffffffffff'u64, 0xfffffffffffffffff'u64
]
var a = BNU256.random(moduloS)
var e = BNU256.zero()
for i, b in a.pairs():
let ret = e.setBit(255 - i, b)
doAssert ret
check e == a
test "fromValue & divrem on random numbers":
for i in 0..<100:
var nc0: BNU256
var nc1: Option[BNU256]
var c0 = BNU256.random(modulo)
var c1 = BNU256.random(modulo)
var c1q = BNU512.into(c1, c0, modulo)
nc1 = c1q.divrem(modulo, nc0)
check:
nc1.get() == c1
nc0 == c0
test "Modulus should become 1*q + 0":
var a = [
0x3c208c16d87cfd47'u64, 0x97816a916871ca8d'u64,
0xb85045b68181585d'u64, 0x30644e72e131a029'u64,
0'u64, 0'u64, 0'u64, 0'u64
]
var c0, c2: BNU256
var c1: Option[BNU256]
c1 = a.divrem(modulo, c0)
c2 = c1.get()
check:
c2 == BNU256.one()
c0 == BNU256.zero()
c2 == BNU256.one()
c0 == BNU256.zero()
test "Modulus squared minus 1 should be (q-1) q + q-1":
let a = [
0x3b5458a2275d69b0'u64, 0xa602072d09eac101'u64,
0x4a50189c6d96cadc'u64, 0x04689e957a1242c8'u64,
0x26edfa5c34c6b38d'u64, 0xb00b855116375606'u64,
0x599a6f7c0348d21c'u64, 0x0925c4b8763cbf9c'u64
]
let expect = [
0x3c208c16d87cfd46'u64, 0x97816a916871ca8d'u64,
0xb85045b68181585d'u64, 0x30644e72e131a029'u64
]
var c0, c2: BNU256
var c1: Option[BNU256]
c1 = a.divrem(modulo, c0)
c2 = c1.get()
check:
c0 == expect
c2 == expect
test "Modulus squared minus 2 should be (q-1) q + q-2":
let a = [
0x3b5458a2275d69af'u64, 0xa602072d09eac101'u64,
0x4a50189c6d96cadc'u64, 0x04689e957a1242c8'u64,
0x26edfa5c34c6b38d'u64, 0xb00b855116375606'u64,
0x599a6f7c0348d21c'u64, 0x0925c4b8763cbf9c'u64
]
let expectc1 = [
0x3c208c16d87cfd46'u64, 0x97816a916871ca8d'u64,
0xb85045b68181585d'u64, 0x30644e72e131a029'u64
]
let expectc0 = [
0x3c208c16d87cfd45'u64, 0x97816a916871ca8d'u64,
0xb85045b68181585d'u64, 0x30644e72e131a029'u64
]
var c0, c2: BNU256
var c1: Option[BNU256]
c1 = a.divrem(modulo, c0)
c2 = c1.get()
check:
c0 == expectc0
c2 == expectc1
test "Ridiculously large number should fail":
let a = [
0xfffffffffffffffff'u64, 0xfffffffffffffffff'u64,
0xfffffffffffffffff'u64, 0xfffffffffffffffff'u64,
0xfffffffffffffffff'u64, 0xfffffffffffffffff'u64,
0xfffffffffffffffff'u64, 0xfffffffffffffffff'u64
]
let expectc0 = [
0xf32cfc5b538afa88'u64, 0xb5e71911d44501fb'u64,
0x47ab1eff0a417ff6'u64, 0x06d89f71cab8351f'u64
]
var c0: BNU256
var c1: Option[BNU256]
c1 = a.divrem(modulo, c0)
check:
c1.isNone() == true
c0 == expectc0
test "Modulus squared should fail":
let a = [
0x3b5458a2275d69b1'u64, 0xa602072d09eac101'u64,
0x4a50189c6d96cadc'u64, 0x04689e957a1242c8'u64,
0x26edfa5c34c6b38d'u64, 0xb00b855116375606'u64,
0x599a6f7c0348d21c'u64, 0x0925c4b8763cbf9c'u64
]
var c0: BNU256
var c1: Option[BNU256]
c1 = a.divrem(modulo, c0)
check:
c1.isNone() == true
c0.isZero() == true
test "Modulus squared plus one should fail":
let a = [
0x3b5458a2275d69b2'u64, 0xa602072d09eac101'u64,
0x4a50189c6d96cadc'u64, 0x04689e957a1242c8'u64,
0x26edfa5c34c6b38d'u64, 0xb00b855116375606'u64,
0x599a6f7c0348d21c'u64, 0x0925c4b8763cbf9c'u64
]
var c0: BNU256
var c1: Option[BNU256]
c1 = a.divrem(modulo, c0)
check:
c1.isNone() == true
c0 == BNU256.one()
test "Fr modulus masked off is valid":
let a = [
0xffffffffffffffff'u64, 0xffffffffffffffff'u64,
0xffffffffffffffff'u64, 0xffffffffffffffff'u64,
0xffffffffffffffff'u64, 0xffffffffffffffff'u64,
0xffffffffffffffff'u64, 0x07ffffffffffffff'u64
]
let moduloFr = [
0x43e1f593f0000001'u64, 0x2833e84879b97091'u64,
0xb85045b68181585d'u64, 0x30644e72e131a029'u64
]
var c0, c2: BNU256
var c1: Option[BNU256]
c1 = a.divrem(moduloFr, c0)
check:
c1.get() < moduloFr
c0 < moduloFr

562
tests/tfields.nim Normal file
View File

@ -0,0 +1,562 @@
import unittest
import nimcrypto/utils
import ../bncurve/fields
proc randomSquaring*[T](): bool =
for i in 0..100:
var a = T.random()
if a * a != a.squared():
return false
var cur = T.zero()
for i in 0..100:
if cur.squared() != cur * cur:
return false
cur = cur + T.one()
return true
proc zeroTest*[T](): bool =
if -T.zero() != T.zero():
return false
if (-T.one() + T.one()) != T.zero():
return false
if (T.zero() - T.zero()) != T.zero():
return false
return true
proc canInvert*[T](): bool =
var a = T.one()
for i in 0..100:
if (a * a.inverse().get()) != T.one():
return false
a = a + T.one()
a = -T.one()
for i in 0..100:
if (a * a.inverse().get()) != T.one():
return false
a = a - T.one()
return true
proc randomElementInverse*[T](): bool =
for i in 0..100:
var a = T.random()
if a.inverse().get() * a != T.one():
return false
var b = T.random()
if a * b * a.inverse().get() != b:
return false
return true
proc randomElementMultiplication*[T](): bool =
for i in 0..250:
var a = T.random()
var b = T.random()
var c = T.random()
result = ((a * b) * c == a * (b * c))
proc randomElementEval*[T](): bool =
for i in 0..100:
var a = T.random()
var b = T.random()
var c = T.random()
var d = T.random()
var lhs = (a + b) * (c + d)
var rhs = (a * c) + (b * c) + (a * d) + (b * d)
if lhs != rhs:
return false
return true
proc randomElementASN*[T](): bool =
for i in 0..100:
var a = T.random()
if a + (-a) != T.zero():
return false
for i in 0..10:
var a = T.random()
var r = T.random()
var b = a + r
var c = T.random()
var d = c + r
for m in 0..10:
let r0 = T.random()
a += r0
b += r0
c = c + r0
d = d + r0
let r1 = T.random()
a -= r1
b -= r1
c = c - r1
d = d - r1
let r2 = T.random()
a += (-(-r2))
b += (-(-r2))
c = c + (-(-r2))
d = d + (-(-r2))
let r3 = T.random()
a -= r3
b += -r3
c = c - r3
d = d + (-r3)
let r4 = T.random()
a += -r4
b -= r4
c = c + (-r4)
d = d - r4
b -= r
d = d - r
if a != b or c != d:
return false
return true
proc testCyclotomicExp(): bool =
var orig = FQ12(
c0: FQ6(
c0: FQ2(
c0: FQ.fromString("2259924035228092997691937637688451143058635253053054071159756458902878894295"),
c1: FQ.fromString("13145690032701362144460254305183927872683620413225364127064863863535255135244")
),
c1: FQ2(
c0: FQ.fromString("9910063591662383599552477067956819406417086889312288278252482503717089428441"),
c1: FQ.fromString("537414042055419261990282459138081732565514913399498746664966841152381183961")
),
c2: FQ2(
c0: FQ.fromString("15311812409497308894370893420777496684951030254049554818293571309705780605004"),
c1: FQ.fromString("13657107176064455789881282546557276003626320193974643644160350907227082365810")
)
),
c1: FQ6(
c0: FQ2(
c0: FQ.fromString("4913017949003742946864670837361832856526234260447029873580022776602534856819"),
c1: FQ.fromString("7834351480852267338070670220119081676575418514182895774094743209915633114041")
),
c1: FQ2(
c0: FQ.fromString("12837298223308203788092748646758194441270207338661891973231184407371206766993"),
c1: FQ.fromString("12756474445699147370503225379431475413909971718057034061593007812727141391799")
),
c2: FQ2(
c0: FQ.fromString("9473802207170192255373153510655867502408045964296373712891954747252332944018"),
c1: FQ.fromString("4583089109360519374075173304035813179013579459429335467869926761027310749713")
)
)
)
var expected = FQ12(
c0: FQ6(
c0: FQ2(
c0: FQ.fromString("14722956046055152398903846391223329501345567382234608299399030576415080188350"),
c1: FQ.fromString("14280703280777926697010730619606819467080027543707671882210769811674790473417")
),
c1: FQ2(
c0: FQ.fromString("19969875076083990244184003223190771301761436396530543002586073549972410735411"),
c1: FQ.fromString("10717335566913889643303549252432531178405520196706173198634734518494041323243")
),
c2: FQ2(
c0: FQ.fromString("6063612626166484870786832843320782567259894784043383626084549455432890717937"),
c1: FQ.fromString("17089783040131779205038789608891431427943860868115199598200376195935079808729")
)
),
c1: FQ6(
c0: FQ2(
c0: FQ.fromString("10029863438921507421569931792104023129735006154272482043027653425575205672906"),
c1: FQ.fromString("6406252222753462799887280578845937185621081001436094637606245493619821542775")
),
c1: FQ2(
c0: FQ.fromString("1048245462913506652602966692378792381004227332967846949234978073448561848050"),
c1: FQ.fromString("1444281375189053827455518242624554285012408033699861764136810522738182087554")
),
c2: FQ2(
c0: FQ.fromString("8839610992666735109106629514135300820412539620261852250193684883379364789120"),
c1: FQ.fromString("11347360242067273846784836674906058940820632082713814508736182487171407730718")
)
)
)
let e = orig.expByNegZ()
result = (e == expected)
proc fq12TestVector(): bool =
let start = FQ12(
c0: FQ6(
c0: FQ2(
c0: FQ.fromString("19797905000333868150253315089095386158892526856493194078073564469188852136946"),
c1: FQ.fromString("10509658143212501778222314067134547632307419253211327938344904628569123178733")
),
c1: FQ2(
c0: FQ.fromString("208316612133170645758860571704540129781090973693601051684061348604461399206"),
c1: FQ.fromString("12617661120538088237397060591907161689901553895660355849494983891299803248390")
),
c2: FQ2(
c0: FQ.fromString("2897490589776053688661991433341220818937967872052418196321943489809183508515"),
c1: FQ.fromString("2730506433347642574983433139433778984782882168213690554721050571242082865799")
)
),
c1: FQ6(
c0: FQ2(
c0: FQ.fromString("17870056122431653936196746815433147921488990391314067765563891966783088591110"),
c1: FQ.fromString("14314041658607615069703576372547568077123863812415914883625850585470406221594")
),
c1: FQ2(
c0: FQ.fromString("10123533891707846623287020000407963680629966110211808794181173248765209982878"),
c1: FQ.fromString("5062091880848845693514855272640141851746424235009114332841857306926659567101")
),
c2: FQ2(
c0: FQ.fromString("9839781502639936537333620974973645053542086898304697594692219798017709586567"),
c1: FQ.fromString("1583892292110602864638265389721494775152090720173641072176370350017825640703")
)
)
)
let expect = FQ12(
c0: FQ6(
c0: FQ2(
c0: FQ.fromString("18388750939593263065521177085001223024106699964957029146547831509155008229833"),
c1: FQ.fromString("18370529854582635460997127698388761779167953912610241447912705473964014492243")
),
c1: FQ2(
c0: FQ.fromString("3691824277096717481466579496401243638295254271265821828017111951446539785268"),
c1: FQ.fromString("20513494218085713799072115076991457239411567892860153903443302793553884247235")
),
c2: FQ2(
c0: FQ.fromString("12214155472433286415803224222551966441740960297013786627326456052558698216399"),
c1: FQ.fromString("10987494248070743195602580056085773610850106455323751205990078881956262496575")
)
),
c1: FQ6(
c0: FQ2(
c0: FQ.fromString("5134522153456102954632718911439874984161223687865160221119284322136466794876"),
c1: FQ.fromString("20119236909927036376726859192821071338930785378711977469360149362002019539920")
),
c1: FQ2(
c0: FQ.fromString("8839766648621210419302228913265679710586991805716981851373026244791934012854"),
c1: FQ.fromString("9103032146464138788288547957401673544458789595252696070370942789051858719203")
),
c2: FQ2(
c0: FQ.fromString("10378379548636866240502412547812481928323945124508039853766409196375806029865"),
c1: FQ.fromString("9021627154807648093720460686924074684389554332435186899318369174351765754041")
)
)
)
var next = start
for i in 0..<100:
next = next * start
var cpy = next
for i in 0..<10:
next = next.squared()
for i in 0..<10:
next = next + start
next = next - cpy
next = -next
next = next.squared()
result = (expect == next)
proc fpSerializeTests[T](): bool =
when (T is FQ) or (T is FR):
var buffer: array[32, byte]
elif (T is FQ2):
var buffer: array[64, byte]
else:
{.fatal.}
for i in 0..<1000:
var e = T.random()
zeroMem(addr buffer[0], sizeof(buffer))
if not e.toBytes(buffer):
return false
var a: T
if not a.fromBytes(buffer):
return false
if a != e:
return false
return true
proc fq2SerializeTestVectors(): bool =
const vectors = [
FQ2(
c0: FQ([12685471316754074400'u64, 5151117139186389981'u64,
1811926512010801501'u64, 2926027770199945729'u64]),
c1: FQ([13288357145490715372'u64, 8465179270531902744'u64,
2331932027798174928'u64, 1169568334929779847'u64])
),
FQ2(
c0: FQ([6571363706651148129'u64, 12259671536166748744'u64,
13297153216522874336'u64, 3368736813872212066'u64]),
c1: FQ([7356918428694088001'u64, 13325610168162790738'u64,
11761401944674591087'u64, 2142266911265180485'u64])
),
FQ2(
c0: FQ([12770271250542491457'u64, 5841829129088508933'u64,
5021659154182959822'u64, 765728708107386899'u64]),
c1: FQ([9814770014224857768'u64, 169926129335489937'u64,
4476430648250845846'u64, 575721800450622933'u64])
),
FQ2(
c0: FQ([10535443743532733005'u64, 18354663162560926093'u64,
3005889269269496788'u64, 892863378917010121'u64]),
c1: FQ([9912639056721134596'u64, 6115953886839683024'u64,
4097812286267812943'u64, 1337629367136352970'u64])
),
FQ2(
c0: FQ([7658679475413450244'u64, 11440992707440007515'u64,
16146061400040738154'u64, 991671862947387812'u64]),
c1: FQ([2385857951922426638'u64, 6278331068203224119'u64,
8247542493832618243'u64, 2945883060694238627'u64])
)
]
const expects = [
"06b812bee59693d4f9f18dc46c55afe42fc5c18965669316117850ca22f55ffa44dda4f58baf6cdf629ecacae4a810098fc7d68a6bfcd200ca59322e37a4be3c",
"00a41dd99c355e6984dedfc9c6752cd22b6d4d70283a128e4399734fbaa715724e0494d2d0cc7b0c71bda29d304a60cf6b3a69e366a3d50d80bfe441192d778d",
"08f943db03ed61e8f2633740bdc071b76c27547891fe90d56776f9ef2a16de98dfd7d0a481fd5efb55374ea3762d879d226ac9bf7c0b347bae142e27f97d03ed",
"068744cde0af982bff29d66a0e5799e78b350216ce53da6d828ca64d94bcd482af8816b7cad0dea041604d5b3ee5ddf2c5b65fc394e1752f6fa52133547a44bc",
"09042d4acda2f2ff75073700783010461c5250f10724a0c27ecd295b2bda961245c9a740d3d8de3dbf6ed4fe142ee5480bd96a70d9a4442385718c4995b04b8b"
]
var buffer: array[64, byte]
for i in 0..<len(vectors):
zeroMem(addr buffer[0], sizeof(buffer))
if not vectors[i].toBytes(buffer):
return false
var expect = fromHex(expects[i])
if not equalMem(addr expect[0], addr buffer[0], sizeof(buffer)):
return false
var c: FQ2
if c.fromBytes(buffer) != true:
return false
if c != vectors[i]:
return false
return true
proc frSerializeTestVectors(): bool =
const vectors = [
FR([17421400499845239983'u64, 6997355861326767820'u64,
8120099025258387513'u64, 3183707257070674626'u64]),
FR([2146638237245267213'u64, 5090456461755122866'u64,
6235019329087538353'u64, 387017393791451532'u64]),
FR([6344330946110213979'u64, 4581536139297704744'u64,
16303670869942326496'u64, 2666106878953846273'u64]),
FR([8748641423870480081'u64, 5579982342510408473'u64,
8096786847344710301'u64, 194588591521887053'u64]),
FR([13962585249620634127'u64, 12179982421793366287'u64,
16590787540748934681'u64, 1292188281347940190'u64])
]
const expects = [
"11177bed2bc11734f04d9c5ef4724e062835e514dc7142d7ea9c41758d16f8a0",
"2619ea8400189f81213d2f1c29c4372ab5a91be78df5b1f2649827138a1d8401",
"24f582ad30f718ecccbe241b94d826adb75f3aa9103deed3c7e183b06979bf1f",
"22efbafeb86bd66aa38389b5ff513384bae6de53269fafd42a8a614b3d275e56",
"03a880d889b5b4cf45376207b13d6c007c6efe0338f9490ab83938f20bd444b2"
]
var buffer: array[32, byte]
for i in 0..<len(vectors):
zeroMem(addr buffer[0], sizeof(buffer))
if not vectors[i].toBytes(buffer):
return false
var expect = fromHex(expects[i])
if not equalMem(addr expect[0], addr buffer[0], sizeof(buffer)):
return false
var c: FR
if c.fromBytes(buffer) != true:
return false
if c != vectors[i]:
return false
return true
proc fqSerializeTestVectors(): bool =
const vectors = [
FQ([12123287589963276695'u64, 17077283578393155025'u64,
18124373372772101378'u64, 846421381194958693'u64]),
FQ([17853748202411400943'u64, 2656002062984499858'u64,
10048626202887305070'u64, 694231139270692630'u64]),
FQ([14471564705950005567'u64, 18111991644968140513'u64,
17814103556911721998'u64, 92110366780417983'u64]),
FQ([2989648722487476748'u64, 6723225646291704123'u64,
11622908385009293013'u64, 2374314218300473764'u64]),
FQ([4494471629382615894'u64, 1770606299211341443'u64,
11311559966274242210'u64, 3399355515865771034'u64])
]
const expects = [
"0b812cdf8aafd6cbe1d6959968066172ebf606339547f18d735cf8364ebb6008",
"23094811623a66136c3a3f86dcf8ac67bf7a2654e1e880a42243cf0f2b652847",
"25b9bf5f353b1d29c62b954257ac188330e1335d557a11c04fcd10fc6ef07834",
"111341c47c8c55345493a0ee73fa91d5e9f853451a11c982ff6ae824d42d8017",
"15e851e5eb9e990b33d9e2056749dbb8ca6f0726c750b6665052090ff6249a63"
]
var buffer: array[32, byte]
for i in 0..<len(vectors):
zeroMem(addr buffer[0], sizeof(buffer))
if not vectors[i].toBytes(buffer):
return false
var expect = fromHex(expects[i])
if not equalMem(addr expect[0], addr buffer[0], sizeof(buffer)):
return false
var c: FQ
if c.fromBytes(buffer) != true:
return false
if c != vectors[i]:
return false
return true
when isMainModule:
suite "Field elements test suite":
test "[FR] rsquared() test":
for i in 0..<1000:
var a = FR.random()
var b = BNU256.into(a)
var c = FR.init(b)
check a == c.get()
test "[FR] String conversion test":
var a = FR.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495616")
check a == -FR.one()
test "[FR] Random multiplication test":
check randomElementMultiplication[FR]() == true
test "[FR] Random addition/substraction/negation test":
check randomElementASN[FR]() == true
test "[FR] Inversion test":
check canInvert[FR]() == true
test "[FR] Zero test":
check zeroTest[FR]() == true
test "[FR] Random element squaring test":
check randomSquaring[FR]() == true
test "[FR] Random element inversion test":
check randomElementInverse[FR]() == true
test "[FR] Random element evaluation test":
check randomElementEval[FR]() == true
test "[FR] Serialize/Deserialize tests":
check fpSerializeTests[FR]() == true
test "[FR] Serialize test vectors":
check frSerializeTestVectors() == true
test "[FQ] rsquared() tests":
for i in 0..1000:
var a = FQ.random()
var b = BNU256.into(a)
var c = FQ.init(b)
check a == c.get()
test "[FQ] String conversion test":
var b = FQ.fromString("21888242871839275222246405745257275088696311157297823662689037894645226208582")
check b == -FQ.one()
test "[FQ] Random multiplication test":
check randomElementMultiplication[FQ]() == true
test "[FQ] Random addition/substraction/negation test":
check randomElementASN[FQ]() == true
test "[FQ] Inversion test":
check canInvert[FQ]() == true
test "[FQ] Zero test":
check zeroTest[FQ]() == true
test "[FQ] Random element squaring test":
check randomSquaring[FQ]() == true
test "[FQ] Random element inversion test":
check randomElementInverse[FQ]() == true
test "[FQ] Random element evaluation test":
check randomElementEval[FQ]() == true
test "[FQ] Serialize/Deserialize tests":
check fpSerializeTests[FQ]() == true
test "[FQ] Serialize test vectors":
check fqSerializeTestVectors() == true
test "[FQ2] Random multiplication test":
check randomElementMultiplication[FQ2]() == true
test "[FQ2] Random addition/substraction/negation test":
check randomElementASN[FQ2]() == true
test "[FQ2] Inversion test":
check canInvert[FQ2]() == true
test "[FQ2] Zero test":
check zeroTest[FQ2]() == true
test "[FQ2] Random element squaring test":
check randomSquaring[FQ2]() == true
test "[FQ2] Random element inversion test":
check randomElementInverse[FQ2]() == true
test "[FQ2] Random element evaluation test":
check randomElementEval[FQ2]() == true
test "[FQ2] Serialize/Deserialize tests":
check fpSerializeTests[FQ2]() == true
test "[FQ2] Serialize test vectors":
check fq2SerializeTestVectors() == true
test "[FQ6] Random multiplication test":
check randomElementMultiplication[FQ6]() == true
test "[FQ6] Random addition/substraction/negation test":
check randomElementASN[FQ6]() == true
test "[FQ6] Inversion test":
check canInvert[FQ6]() == true
test "[FQ6] Zero test":
check zeroTest[FQ6]() == true
test "[FQ6] Random element squaring test":
check randomSquaring[FQ6]() == true
test "[FQ6] Random element inversion test":
check randomElementInverse[FQ6]() == true
test "[FQ6] Random element evaluation test":
check randomElementEval[FQ6]() == true
test "[FQ12] Random multiplication test":
check randomElementMultiplication[FQ12]() == true
test "[FQ12] Random addition/substraction/negation test":
check randomElementASN[FQ12]() == true
test "[FQ12] Inversion test":
check canInvert[FQ12]() == true
test "[FQ12] Zero test":
check zeroTest[FQ12]() == true
test "[FQ12] Random element squaring test":
check randomSquaring[FQ12]() == true
test "[FQ12] Random element inversion test":
check randomElementInverse[FQ12]() == true
test "[FQ12] Random element evaluation test":
check randomElementEval[FQ12]() == true
test "[FQ12] Cyclotomic exponent test":
check testCyclotomicExp() == true
test "[FQ12] Test vector test":
check fq12TestVector() == true

138
tests/tgroups.nim Normal file
View File

@ -0,0 +1,138 @@
import unittest
import ../bncurve/groups
proc randomAdd*(G: typedesc): bool =
for i in 0..<10:
let r1 = G.random()
let r2 = G.random()
let r3 = G.random()
if ((r1 + r2) + r3) != (r1 + (r2 + r3)):
return false
let rc = (r1 + r2 + r3) - r2 - r3 - r1
if not rc.isZero():
return false
return true
proc randomMul*(G: typedesc): bool =
for i in 0..<10:
let r1 = G.random()
let r2 = G.random()
let ti = FR.fromString("2").inverse().get()
if (r1 + r2) + r1 != (r1.double() + r2):
return false
if r1 != r1.double() * ti:
return false
return true
proc zeroTest*(G: typedesc): bool =
if not G.zero().isZero():
return false
if not (G.zero() - G.zero()).isZero():
return false
if not (G.one() - G.one()).isZero():
return false
if (G.one() + G.one()) != (G.one() * FR.fromString("2")):
return false
if not G.zero().double().isZero():
return false
if not ((G.one() * (-FR.one())) + G.one()).isZero():
return false
return true
proc randomDH*(G: typedesc): bool =
for i in 0..<10:
let alice_sk = FR.random()
let bob_sk = FR.random()
let alice_pk = G.one() * alice_sk
let bob_pk = G.one() * bob_sk
let alice_shared = bob_pk * alice_sk
let bob_shared = alice_pk * bob_sk
if alice_shared != bob_shared:
return false
result = true
proc randomEquality*(G: typedesc): bool =
let ti = FR.fromString("2").inverse().get()
for i in 0..<10:
let begin = G.random()
var acc = begin
let a = FR.random()
let b = G.random()
let c = FR.random()
let d = G.random()
for k in 0..<10:
acc = acc * a
acc = -acc
acc = acc + b
acc = acc * c
acc = -acc
acc = acc - d
acc = acc.double()
let ai = a.inverse().get()
let ci = c.inverse().get()
for k in 0..<10:
acc = acc * ti
acc = acc + d
acc = -acc
acc = acc * ci
acc = acc - b
acc = -acc
acc = acc * ai
if begin != acc:
return false
result = true
proc affineJacobianConversion(G: typedesc): bool =
if not G.zero().toAffine().isNone():
return false
if not G.zero().toAffine().isNone():
return false
for i in 0..<100:
var a = G.one() * FR.random()
let b = a.toAffine().get()
let c = b.toJacobian()
if a != c:
return false
return true
when isMainModule:
suite "Group elements test suite:":
test "[G1] Zero/One test":
check G1.zeroTest() == true
test "[G1] Random addition test":
check G1.randomAdd() == true
test "[G1] Random doubling test":
check G1.randomMul() == true
test "[G1] Random Diffie-Hellman test":
check G1.randomDH() == true
test "[G1] Random equality test":
check G1.randomEquality() == true
test "[G1] Random Affine to Jacobian conversion test":
check G1.affineJacobianConversion() == true
test "[G1] Y at point at Infinity test":
check:
(G1.zero()).y == FQ.one()
(-G1.zero()).y == FQ.one()
test "[G2] Zero/One test":
check G2.zeroTest() == true
test "[G2] Random addition test":
check G1.randomAdd() == true
test "[G2] Random doubling test":
check G2.randomMul() == true
test "[G2] Random Diffie-Hellman test":
check G2.randomDH() == true
test "[G2] Random equality test":
check G2.randomEquality() == true
test "[G2] Random Affine to Jacobian conversion test":
check G2.affineJacobianConversion() == true
test "[G2] Y at point at Infinity test":
check:
(G2.zero()).y == FQ2.one()
(-G2.zero()).y == FQ2.one()

240
tests/tpairing.nim Normal file
View File

@ -0,0 +1,240 @@
import unittest
import ../bncurve/groups
proc testPreparedG2(): bool =
var expect = G2Precomp(
q: AffinePoint[G2](x: FQ2(c0: FQ.fromString("13936578204263895229092967414825041569724079982537616431212348844388899776640"), c1: FQ.fromString("6372636725635053773371996212293600406870925440022386078671828127711809436031")), y: FQ2(c0: FQ.fromString("19293035970010898452454381709939058714495082898872914526540420247178075881697"), c1: FQ.fromString("13822349107533275437553410197128434338071760794202849712402800746887549494974"))),
coeffs: @[
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("2627043964130257285960335798481049684473505235282434252362906013168360965985"), c1: FQ.fromString("14787221188041042838526170263203159226721816513593198738527261794343476005673")), ell_vw: FQ2(c0: FQ.fromString("5190413803656753539584048070636432748402456516849818272297235294934300653772"), c1: FQ.fromString("16131787528611999569385991096257681501249100726189947900572474295515353427218")), ell_vv: FQ2(c0: FQ.fromString("11284217811624345285836951206193951701052480344824103057273798441033858118424"), c1: FQ.fromString("6392116536365389562188363539617434898082427085812013656312597845585909267447"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("827617134098165717451808940080463277390770457691666780560712143809003953598"), c1: FQ.fromString("6776229088211374446530917353066321938640163548858035832637439231634790575465")), ell_vw: FQ2(c0: FQ.fromString("987776078024262725561041258416387561158070255475504730561661362421251696401"), c1: FQ.fromString("15312963471998242334683179861466148222641884112991952428739813077336923189144")), ell_vv: FQ2(c0: FQ.fromString("2813988028633040066320201189843971639620433430176492766961373503539074898364"), c1: FQ.fromString("17055167212030988864288747645634552775658830115224987200613220012554982651578"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("17093477194591041266397380404112224367919571835678040889250664706433487493043"), c1: FQ.fromString("8643160753646550179100165428401120938543691942215161396502118430923535820500")), ell_vw: FQ2(c0: FQ.fromString("10933962898922943120024964690690003920835888964592980369975615298196656183772"), c1: FQ.fromString("10054435552662455933652209211749007190981947542684285471884164039871580864235")), ell_vv: FQ2(c0: FQ.fromString("1475406195060586931258578851599932495282912871465827155842007043242102046659"), c1: FQ.fromString("5049830924161187876168845800328902567850325970867534538120475909389784841990"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("10434276065336457961840942984114655645641355797054067910262735814196303889171"), c1: FQ.fromString("13878009844584745291193244072670264656592436467816383615606044714978763054890")), ell_vw: FQ2(c0: FQ.fromString("20993505060568459085534388708128029516059470166174503494622391683433030357130"), c1: FQ.fromString("10192769806017258272908841309051731389378033999625803563624881649401237928876")), ell_vv: FQ2(c0: FQ.fromString("17377091829483421118284926147077357677507183290871250621761583281560333718550"), c1: FQ.fromString("18297780639039540893260206902113850213293978821982237672423968999898103045256"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("5064242330961655837810472366173802140673816516016739528934082190598759216129"), c1: FQ.fromString("20172028727708469864987113767379594545870343866584998839699240724281670882460")), ell_vw: FQ2(c0: FQ.fromString("2996734698689069564052128450078797792056313730363946953355442668966438200951"), c1: FQ.fromString("9910941594900797404370917094355311738210439203708671856236954583027355115302")), ell_vv: FQ2(c0: FQ.fromString("6899792008443933570863502403567742315007264221354931185809349393350618040985"), c1: FQ.fromString("18692424201235977456836009406318755884773471215777102117879530727096807383696"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("9406196439119227017197200697795682187217614676635965071601916278141238949664"), c1: FQ.fromString("1156397052245190909533108891675012415349316096091404012699655652677336827483")), ell_vw: FQ2(c0: FQ.fromString("9249275523859165912800910211783530541095037634552893633043938333737241478198"), c1: FQ.fromString("13674086724537885208439774394455021050268654286498757861381516198880504360984")), ell_vv: FQ2(c0: FQ.fromString("5760486636714317173624828119078349011897522431614835257025421978626292083787"), c1: FQ.fromString("19195594701877562770296730558597966521343920474874926443609709880727520577918"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("12166638768862632131967787101186130559865354321458713212316891357299196109517"), c1: FQ.fromString("1517854601414954363921968422436855204960697222979994177630673862982254831193")), ell_vw: FQ2(c0: FQ.fromString("13366104107979832799705928625277388583673352228059815944738106504080223007902"), c1: FQ.fromString("21648874073566751720910466086303831459661520354253552089758049591613143727506")), ell_vv: FQ2(c0: FQ.fromString("3488776451414334656597670851583995239177248056342375163461521452312663358741"), c1: FQ.fromString("20333414955874213710027365371143864644434939642550352414403887754960702108637"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("20468104499950674906991599591024893438007901046236752159815194628487603192867"), c1: FQ.fromString("19069685413250623899891139234721987960230148622397590130148098858949057649761")), ell_vw: FQ2(c0: FQ.fromString("2458162095258922865086779772179745380154704066745856173311580123496018896409"), c1: FQ.fromString("14594824564551859025266692529845827840804005950672166057231487026855067744741")), ell_vv: FQ2(c0: FQ.fromString("9689655654286496198845571066967567466951511863880400774464590553022163229348"), c1: FQ.fromString("10870127206120874488753221889350215535903993427218589071224774335669836789433"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("15334990572515495832170458963151379997993357347608954164027751164770129172212"), c1: FQ.fromString("13576640273125978757652414940234881692674718901710647917847939367156260249620")), ell_vw: FQ2(c0: FQ.fromString("17441399177222030197667035179458353890777336233312342937564354849059149975908"), c1: FQ.fromString("17007488955907409465543686050546106251553784366237996525801373957978511005278")), ell_vv: FQ2(c0: FQ.fromString("11907792503811346855438657769533223907267837220830333730410940125854558637383"), c1: FQ.fromString("19490060942462197243379937108210110931787757078893580540054690416131017931738"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("17315779415820661602591970083599829709759601467527578787441772726289774228451"), c1: FQ.fromString("12080802446892182171725856453369856169039939341293608060423916935382017714476")), ell_vw: FQ2(c0: FQ.fromString("12912774602739952889552025090054804346758888115608888766725054474682313950190"), c1: FQ.fromString("18204955971588024842905918028829132053489100674596042021111813303048407250440")), ell_vv: FQ2(c0: FQ.fromString("15721987682182104464109212111163313043868345920774975073102123159159010623181"), c1: FQ.fromString("12041652399509652544977783565303273845883245418094457529808044120827920548972"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("19999070717670362479390656255660288732744476951677182489244240784338456791622"), c1: FQ.fromString("3906002755238658676658974095984902840159437088441472633765497396881255783772")), ell_vw: FQ2(c0: FQ.fromString("4998301920940599524538129746790252820575786197710804708434384224972978627969"), c1: FQ.fromString("13740819604543965458992763357705315276969968429480411530007322182403523242226")), ell_vv: FQ2(c0: FQ.fromString("12357083524385039711813704948190809675818592911528835736620903716727226601953"), c1: FQ.fromString("13949012942996636079549854439740332990496218971788334041233648192605019948979"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("19805154539184543843735550309592886829271314363908537964547876413114433734624"), c1: FQ.fromString("16481416862400234230001935040590314552205484054708993091993487440981092053599")), ell_vw: FQ2(c0: FQ.fromString("2850933690997557614680452825756662353227291804228932023648526608593113870536"), c1: FQ.fromString("4786423507519240726599235257249783193682886531662683554713943115802063984530")), ell_vv: FQ2(c0: FQ.fromString("1023533030117941985730558522296044345509721849960856436490999820443312747347"), c1: FQ.fromString("16431548648882316869128893343209930754508425074299553782883149795599907406302"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("17203075478724975791441470425150871365957382183648220405350599958349478966969"), c1: FQ.fromString("19566853320706390536799238411444563555185156853468371794865496424286288424312")), ell_vw: FQ2(c0: FQ.fromString("15310908404682220539815850064952575682898427316407655213706791187971849703724"), c1: FQ.fromString("16518878079785062271298378680376701241016697782308466806532677867880353948389")), ell_vv: FQ2(c0: FQ.fromString("14461573034674366293217641735721285869692106578982161360427147953320122006893"), c1: FQ.fromString("6682535969691272372531048932989583075363217464304983455197465867679730319705"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("20895553497690200905762157235991552162755238828234592755315911339400881373107"), c1: FQ.fromString("11947919061415103173593352950044808416347417810515010536678086292686107222044")), ell_vw: FQ2(c0: FQ.fromString("6277981470671734560229149673222400771590408919719217743512900463425397921908"), c1: FQ.fromString("857212248193599195410711267373529614085739023676782457331058533755337294689")), ell_vv: FQ2(c0: FQ.fromString("13885916052404740835214284721162700704805447448922857617672445286139626987191"), c1: FQ.fromString("19585389042100086858633036225209396447924441966778662250950999864988284098182"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("12748401205319976481200663793287165427761341490879316890008250070038707706903"), c1: FQ.fromString("9347025175062646190962067344106887221752685406563421042329129538495481178145")), ell_vw: FQ2(c0: FQ.fromString("12819473645128401077507234830331609583133696659610406468927174080626676619197"), c1: FQ.fromString("9039389340404634934394726812078086316924048399159604642340339967187032834011")), ell_vv: FQ2(c0: FQ.fromString("3576972655652865483567741810151575278761470911419381093571973994483543095285"), c1: FQ.fromString("15017468236028111406921228193688973260631093975412858847563400987354453569177"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("15407570760857043664675198351546637734249070940748070513828892036078049928562"), c1: FQ.fromString("16864837887933576789187120028615006826049916557295792205508706545038103706967")), ell_vw: FQ2(c0: FQ.fromString("10721360465919602144653520112889689786555051462198759164582568961994647563952"), c1: FQ.fromString("15294101887395462362997873520785213441488786364828649600395509678143054744021")), ell_vv: FQ2(c0: FQ.fromString("338877711237691732068166553384070140091720428235937017002925264628733553275"), c1: FQ.fromString("8565770517833678396194963042166078986348581926120249815567974171840711179000"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("17302401852599488824262121640328139426135883695023154134865582322858078206108"), c1: FQ.fromString("17762524466294762819341377108361075499043287383142581974755577861093917129883")), ell_vw: FQ2(c0: FQ.fromString("13015933066581331586057031655765394156438563571733887223640331819027233831808"), c1: FQ.fromString("758435331870656231667722761984400968773108978847112431665229252181053186864")), ell_vv: FQ2(c0: FQ.fromString("1969703955244816291771524536928710541897907643982046151796341732075930805350"), c1: FQ.fromString("7031491051442716548063495812915693048061021502544985435130728834982309403017"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("3256431897334799395000264533777543923057110731812322608781781329228902421476"), c1: FQ.fromString("12181683857561584905762347955319259913388176042023651776593530168946356566719")), ell_vw: FQ2(c0: FQ.fromString("796109596241493969908761241268545233387446802589790513588935996990189190052"), c1: FQ.fromString("1881011435205668659920588004752042238985141500261993259857887774967416952583")), ell_vv: FQ2(c0: FQ.fromString("8939978626202472531630769965940628317376245215184468794030552730424109408120"), c1: FQ.fromString("6876296539554825957959953448623382464100292373640362636397165101276891176372"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("9977776755404508431321449793657366203354342168643747704006143818738023991417"), c1: FQ.fromString("17287266532802977377832339048078093713694407321084566908200498605546793435820")), ell_vw: FQ2(c0: FQ.fromString("13828304001931491210449888619926027655755205497065268858009259221646932328015"), c1: FQ.fromString("15160507616608365341639459841002920786281000303114113782821091199951204875277")), ell_vv: FQ2(c0: FQ.fromString("11161349646944933007552237093278324942110971730799870343491465916610232191523"), c1: FQ.fromString("1406353163795290818414111649956135144227974028417320470897284179379367096127"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("7051926909684750402992796647032741235565177570654429988875094870973533133596"), c1: FQ.fromString("20900584116079647608136401104271956963096697793208792824067775068966626057321")), ell_vw: FQ2(c0: FQ.fromString("16845876804097000415961425779514720605919619149076212452284059758659650969766"), c1: FQ.fromString("20806461795944565303534619813753388058736049320841472653130287521152836019826")), ell_vv: FQ2(c0: FQ.fromString("14700937697004489523945953560825193972493636932341330040072362595384517591325"), c1: FQ.fromString("17137955424118909932502168320776219042080643724609838664247554484115994974522"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("11997142079850902731018491830701934107296522353910410046841604519639199995406"), c1: FQ.fromString("7846127392296015970710836702134527490160588060582987110380879354952036676012")), ell_vw: FQ2(c0: FQ.fromString("17663414534578185845724108244425572237371794484114570058559740097895281553299"), c1: FQ.fromString("5029140669755495687983399191536061323171057684732073778423303267496666431971")), ell_vv: FQ2(c0: FQ.fromString("18428442704115060890708987869503078260482829714024061977805185634781850796860"), c1: FQ.fromString("10997089106320530533400287751108615584976629751702164406696900094998369084403"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("13049677819639682137107272322562642308503411465986079606334189062279441432203"), c1: FQ.fromString("8874624972677935217588093490885486721673594121072678233714072789659128555367")), ell_vw: FQ2(c0: FQ.fromString("17057994029556488440657870420250293132874472170733557198308184169150330844407"), c1: FQ.fromString("21633949388482190843211557537195287403106536528012825991981214057356654097223")), ell_vv: FQ2(c0: FQ.fromString("6897044784874980042628318562068623805084522888972873438474139199226581764756"), c1: FQ.fromString("2692625602037608101594849144393949134766066123857076012132350588113519149572"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("9195356099870032305655798667367006686897880523242566329014149856866611060003"), c1: FQ.fromString("8052880750949660720559492348565167987253311417692073778040482170195175946428")), ell_vw: FQ2(c0: FQ.fromString("14126308928695836758114496610102001680995565158909643521688860213965125059872"), c1: FQ.fromString("671075569038912473223544948981506373517307914345286644404839238190008163502")), ell_vv: FQ2(c0: FQ.fromString("16828103633974871724641544770525140436938333764966095252502003672344759889143"), c1: FQ.fromString("11294529501746593948507624028584472676389760897716457892342249916318954501723"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("21124500310722447577044753724306337899524073055454053171030128408285122310058"), c1: FQ.fromString("6266274689105996173186441194758121417834460925587732781640173398761521329055")), ell_vw: FQ2(c0: FQ.fromString("21576331134605070996263178912402854747994495442070419140713471256922277734125"), c1: FQ.fromString("10074747339717657657921727946300324710951646905879034242434327125816600264379")), ell_vv: FQ2(c0: FQ.fromString("21765519811276677250031796638148457542841530645055777846258335421409598000534"), c1: FQ.fromString("21113224637771003677145667454854210585751245642107143223999187554888833454243"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("3972300323627467772646468544189830113523911494046612234673787196935654523692"), c1: FQ.fromString("4678843490808368914310764558657240771944434980659329233409943752329617161210")), ell_vw: FQ2(c0: FQ.fromString("17273599953138772488805612764323870175561796764345770950040891840763740358904"), c1: FQ.fromString("20415406312940209525918367703821373463952436739154646433333601347381723237136")), ell_vv: FQ2(c0: FQ.fromString("9160987523541118769404859219229455872777126347530590315596894278463599930633"), c1: FQ.fromString("9054614112960826755739076858442558227570393423663838494067070216861352843202"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("9994954547022379603539679290217274617475455164007187108524364097751858323148"), c1: FQ.fromString("20207771379440433457553782484784849880846158790602128386495229840158065235194")), ell_vw: FQ2(c0: FQ.fromString("4256518944443059194982602310083745017699415852751800911480635583088649250060"), c1: FQ.fromString("15878038811235111024146051545935959211923223574364333740058604258589674044428")), ell_vv: FQ2(c0: FQ.fromString("15953919860849853924955813198899405295748673040941543212644580663271517228860"), c1: FQ.fromString("19594437991878043589671466710739298733201896072505590020656735731744884281871"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("1333543700232406379832215757617068657339748039338978081735192495239856176346"), c1: FQ.fromString("13583045269132775344737980756385257860562307101313068058661941588822340692712")), ell_vw: FQ2(c0: FQ.fromString("11316045827391404343818336116749364562466461792138476673985290066961360847098"), c1: FQ.fromString("6683447190424452097068562814518005185732181765401062334661636064120276671717")), ell_vv: FQ2(c0: FQ.fromString("21483741541431414142042872578822487244163622589552506298253453995156009765340"), c1: FQ.fromString("16502358172728062550923345759488126380665850742066670591562640787191536290296"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("4727679401549029925466046980042210063407620223804057104870816459356196798234"), c1: FQ.fromString("4245208253099058296636688301996648265313738760450861302514451065904449375848")), ell_vw: FQ2(c0: FQ.fromString("19589426516750929424306774984752060788246605243046082989256140076730177865962"), c1: FQ.fromString("19202599366770569221216501935740579180019961851730854119509268985533547631061")), ell_vv: FQ2(c0: FQ.fromString("18329650333197074546830734977650617140322812110454751288122555715911145072440"), c1: FQ.fromString("18685828888894718887886440900247948352873037333999376257561751598045361934940"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("15348453159719894937627693446890053385899104841918429189733580400902437025934"), c1: FQ.fromString("7501190780205720975723852235835733848687299193764526538831345783610137314719")), ell_vw: FQ2(c0: FQ.fromString("12341649147852680664345690534373468938440543264264387581538764313149640739302"), c1: FQ.fromString("140543540021318882234026314588789476198722270107997413913717074968501481488")), ell_vv: FQ2(c0: FQ.fromString("5960374673747504135132782820167542238733653290730217772559545641742563125282"), c1: FQ.fromString("14507139191276441762644240084427079430815677404266073216544260783868466836469"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("1463974716527100203803811366059855506152810619047460592789012516365557382809"), c1: FQ.fromString("12894560712946121716686238503640546420854178898471895472462918225441749701869")), ell_vw: FQ2(c0: FQ.fromString("10508120321944855490271402587060090544503473558782513226881398929302598320386"), c1: FQ.fromString("20659878091446285231719184399846880718538938504006216923931157918871132219202")), ell_vv: FQ2(c0: FQ.fromString("9314687360620406591930278900314522432964075856747694070384017245464130048974"), c1: FQ.fromString("17694675150016443475335083504221747995503842086249559620223686305992007745569"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("14196539467892272085884829545587228956041818801561947743113049357924737022135"), c1: FQ.fromString("11319357657686312684765696405805456576475636756706867678717962076341276658626")), ell_vw: FQ2(c0: FQ.fromString("2758949814978246411081695103954624266457693267107844100147125044169545866221"), c1: FQ.fromString("9492100316900274072027501035053797502489796059345864111677781413985743864383")), ell_vv: FQ2(c0: FQ.fromString("4704107896591496617393915690357581449785399000813194972371050438639491862314"), c1: FQ.fromString("11531365345870313813093010071065540963321572589131340821665512078366854738990"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("18802465664727774773666122120775907428094487475952383150269507875840089541246"), c1: FQ.fromString("1731637922302665944958348222164323910730404298281499149800917211427254836819")), ell_vw: FQ2(c0: FQ.fromString("9297609456558817283944780415839637083190742770917810313181849518385379162642"), c1: FQ.fromString("16377246183633200781091121197835476043913744319877154056563766308564474143489")), ell_vv: FQ2(c0: FQ.fromString("9542461764772800142773586971354345045722377861760780709590923740133551871717"), c1: FQ.fromString("21640615613954317564353959148225302612886621120325752435425437529806262143694"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("19906049876176293236988182778475214525096978055900789654464603831282713330781"), c1: FQ.fromString("20362580925091945825523087376754169675844247448742703441518744743887060798158")), ell_vw: FQ2(c0: FQ.fromString("12267599301269062964822189844562856172284191535014283588478875529832316401461"), c1: FQ.fromString("16669825137620567269982790548542800570592942517936126877686519256685036586668")), ell_vv: FQ2(c0: FQ.fromString("8883573260261802165780406246211290131940260376316892224137508921505842308805"), c1: FQ.fromString("19231108274886583687672953546761020040720197580241947146286750565108059707992"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("634182853515641884871095612114627494589971356340786857463490962898713112220"), c1: FQ.fromString("11430884471074068345749191262196070058384500365559620896062382995797164195815")), ell_vw: FQ2(c0: FQ.fromString("10550902705393315932525976596381459439846414130661652807183357995272197708172"), c1: FQ.fromString("3961587743443277010160905820636488892023891053433292002931434257476626412826")), ell_vv: FQ2(c0: FQ.fromString("12451440882697904111698352784988882971851697245540249016187908252126835129000"), c1: FQ.fromString("11782592623486603129029758483031965320434451423371375539247662743178943477576"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("1404156345033137762434737426689373534725204733157905834242027185649572912742"), c1: FQ.fromString("19570890322851668915358612375948367153948206621537633922343603465980161569078")), ell_vw: FQ2(c0: FQ.fromString("8686503710040621382075118769097735385052600877432244428197116763766844918162"), c1: FQ.fromString("17223447941612107204326938316335236704702567123017447936901253775530936851086")), ell_vv: FQ2(c0: FQ.fromString("18767158861904777443587157807982315566545548385817262926280782765269843879565"), c1: FQ.fromString("21334543677892438295079840326280352051766440588072368601899791201007143326750"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("20453014818098756686381445228623803935714316636974320420742351477862354368007"), c1: FQ.fromString("11004429848286161246832723709453157691786114066215975460914111961981461575367")), ell_vw: FQ2(c0: FQ.fromString("14444837779919119651023367195508370508399423510380962187645910198528103345317"), c1: FQ.fromString("4021193867366929691652413553892868612329317391162172266658368471326882495507")), ell_vv: FQ2(c0: FQ.fromString("21098047915839588616030399663527845846589272989005835932893550785408588982098"), c1: FQ.fromString("16796487305049888740077005894512139332211008298712590271618863352993144873711"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("8799932768264091269394526725962837485495458036153083882004277055769655900574"), c1: FQ.fromString("18596145973176446599121174893654309094121045316907408604913712044917019537350")), ell_vw: FQ2(c0: FQ.fromString("5727901458562943136998396728651681550262281371313580881804977990767435431080"), c1: FQ.fromString("16665276274186006945492338822028666786935953718794303569570538737692828117635")), ell_vv: FQ2(c0: FQ.fromString("10910280368195212990489805322100030141483989387720003214110076576431959738476"), c1: FQ.fromString("9606982070265587786427675480691869167035030735018706315315080889814741903522"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("14453637744147393731588213406970287317078500183808667567110703622821377404467"), c1: FQ.fromString("2399421543794348043991999124897133983828094348779941685913680103324762089779")), ell_vw: FQ2(c0: FQ.fromString("9949473626857777965864312230577133154733097780222892008806269288857107115742"), c1: FQ.fromString("3174442934451895428948522429276679446276043250911620596846605340399481680631")), ell_vv: FQ2(c0: FQ.fromString("10773554875563236900680111254025179423521083612506388720894431016952344645908"), c1: FQ.fromString("1791500946161395076321848651097476041018873800532919907790655022966446958329"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("13228018442560564090007964181563674306630092611719086476342149061864561185280"), c1: FQ.fromString("12610285327624889222715450538150491139050620893563766277510163987972586901233")), ell_vw: FQ2(c0: FQ.fromString("17479662858773093405375538474745376030990506794065246603293683266124925174043"), c1: FQ.fromString("2084404960859519365733972471606438409123459865402210971655148862692485946826")), ell_vv: FQ2(c0: FQ.fromString("9675635536108496330861750800277390856149950003475979013330722219857375990089"), c1: FQ.fromString("17889926389980731198337394126857435112726851886206769936779651682292365620935"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("19205463777127243508919644186723680978776945615422050305472500178064412123473"), c1: FQ.fromString("10530028762478985738084339660443655112791893212694172883230610760959423970096")), ell_vw: FQ2(c0: FQ.fromString("16515559472874148379011472911124765449097459867226728232468301906190158644000"), c1: FQ.fromString("17379548905015313648731271429222704537289418711815257252434537403381161039222")), ell_vv: FQ2(c0: FQ.fromString("13018298656134311744614296809909600459918460239498208291306800551968894517759"), c1: FQ.fromString("16285303269049929546880902543736204779249639088136468614535789316688521663976"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("9506592875949693201584225006409064359870098448027454395748349563972724759341"), c1: FQ.fromString("1756212124305090096775291444252901778765176938185769777272403090820692916832")), ell_vw: FQ2(c0: FQ.fromString("8608069305722185875126845710591577689851258364137915830961192516727536204958"), c1: FQ.fromString("7879742654857051185278955345176850926952262018841407228150061698899939295057")), ell_vv: FQ2(c0: FQ.fromString("2311752966405194522531977544398652994349075269275918521122768201037004674541"), c1: FQ.fromString("8056192790171072186146581305753657840809572079825584316343698126369167589177"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("16894171323623324294071017663897158494527746239981369238071300334653959886935"), c1: FQ.fromString("21839557351524624846710837291698003965911117746801809883425361529677608757736")), ell_vw: FQ2(c0: FQ.fromString("7921995242367245298209685641602469741817826911044923939882608549077724733892"), c1: FQ.fromString("16195366803093832645900011617380218833645406154510352140817411408898365560241")), ell_vv: FQ2(c0: FQ.fromString("12718951609280728995721445896408716663776093135974310357265585518411722037823"), c1: FQ.fromString("14775730569800772809191320148533632897226925930462946258120395650683306784222"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("18324740767049924394294219212760164185884801967765967078427354810675386933254"), c1: FQ.fromString("18510525798971302215942990229348890627301667188256739950256532108744423958467")), ell_vw: FQ2(c0: FQ.fromString("392130232730851920199052091589353916248087256056205523658797958171832234380"), c1: FQ.fromString("21095230708713942590602820178155041621291598326436725983218983799781802054278")), ell_vv: FQ2(c0: FQ.fromString("3496296009938486548567151348321691013571513425434397040346440937975425900960"), c1: FQ.fromString("5982055369526470853931810147684497423036063399292908540001447206525852512531"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("19247021831371751344058078378442480867649167715937305884736113857856820190448"), c1: FQ.fromString("3844115161163747856814522390569661892955450798273343821401275758096863392646")), ell_vw: FQ2(c0: FQ.fromString("6827864046826742026186179073427918229064686953470940699125130948982461542396"), c1: FQ.fromString("17615043663229174389408922909891358248158892597678816385681324344031892930590")), ell_vv: FQ2(c0: FQ.fromString("17143020152984715373298117189870583206488981021188881359521310100992509008220"), c1: FQ.fromString("2174519777022093979454116656397980458948553322768446892932246705483799164016"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("7787149350876246869417695770873628344850130125888658086308716316049428077539"), c1: FQ.fromString("3645376584593179034263104864050174094110419584940335745722492256564582048708")), ell_vw: FQ2(c0: FQ.fromString("11834206166934740935662681278907472701573638397628522018255718023777042308993"), c1: FQ.fromString("11564024956967266505944728035150285591786947709753433733529805379472238406883")), ell_vv: FQ2(c0: FQ.fromString("15009525058621581543328262019401183048318643177731888421709672368574056014500"), c1: FQ.fromString("13573139489477783981960389988307434036015536834494019264597795654229345703051"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("16059660214342726778635842789601639915164060169646706775268999439826160969051"), c1: FQ.fromString("3592864998692836817549974752747753891215991801035643051485066473571282986623")), ell_vw: FQ2(c0: FQ.fromString("16014086539360826224382965134738311010906671679277894939278016943520015391650"), c1: FQ.fromString("8080101227643664909861133876167034094240488311720250596061359317310944652491")), ell_vv: FQ2(c0: FQ.fromString("3252331663236937425057834000082561720577180169369353108561319513563871033743"), c1: FQ.fromString("11096433584385810520286571872297862049422429286608206525566845219548956753724"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("19106172305007554707393707438702629275907204152148571359255256006833275609105"), c1: FQ.fromString("19930525972818199875851038877132322547334829658949554806801108404941414101103")), ell_vw: FQ2(c0: FQ.fromString("6679449617056144648556013688092503718528081567224358397706944806960707450866"), c1: FQ.fromString("11834339054115289581728384446510154030614495257639549098914576992681792644317")), ell_vv: FQ2(c0: FQ.fromString("1103971970080878735020200145931068396548255816344141900100578642050167835756"), c1: FQ.fromString("17442014287618549059909689032626628911431121434337187918457502534017974292771"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("13076805492928497748141329405065621544173515733010216253264862326898999186011"), c1: FQ.fromString("19346998537378535293491878821381642632408077348855336790184043880601230192418")), ell_vw: FQ2(c0: FQ.fromString("2545953185107430710067715026283426197979102699516436299879612082830465064387"), c1: FQ.fromString("658480144944520773976742499043453183928200527625254756411898212059863027220")), ell_vv: FQ2(c0: FQ.fromString("10599092041977168661053673645861514974472253862403262653806335823439984547520"), c1: FQ.fromString("17162893574285047796735108280435654463748265136801600105571927738662909784323"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("7085319511141515608176313666085289847826695130590757892136226409211984372940"), c1: FQ.fromString("4640142023499829801003091193578633557246618475866500623404375352964089302137")), ell_vw: FQ2(c0: FQ.fromString("1378711095367589966539356394028311641404402942969200900226036867512926984525"), c1: FQ.fromString("20227188036459114276006891788262806649925222994898646338447622388573680807347")), ell_vv: FQ2(c0: FQ.fromString("10909351851886459521617902618958119972478357132140936115187854789408351897716"), c1: FQ.fromString("4270359828288113001419972416654771076387260764523167288282363565700959902034"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("10664974041382313497332907041293813168318012018754207087217051246266695094381"), c1: FQ.fromString("7224602128989495563505314962160716046811485526332201590284671097356858321790")), ell_vw: FQ2(c0: FQ.fromString("3934655983500232748821065857705540333423053242147172231346384664554964864345"), c1: FQ.fromString("11454752219285395886386371667205283572988335397450041936477253847740701967591")), ell_vv: FQ2(c0: FQ.fromString("8613140983903038954690786727135433848146709090100659195246608245554768285578"), c1: FQ.fromString("11683437859485859733100235618517710813205908191940301821880216475492534630964"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("9260311336006363265451970156091146121161647548618044834929651955256342089015"), c1: FQ.fromString("13937163080647677520680042908210664749449075029135461146729576039103333691760")), ell_vw: FQ2(c0: FQ.fromString("17902331142943185999552054453100857674678810044830315483268693319046882556841"), c1: FQ.fromString("1544882902322119030227919062381439448920928851067359863906156836436873153173")), ell_vv: FQ2(c0: FQ.fromString("16995331248700240765237826468302734126993772776070338276810983166039248498016"), c1: FQ.fromString("14969634758504522458179971123440531767135906355134596040598769037804654587702"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("9691457346326988270440356562380914538881384898220972529069554878570918299675"), c1: FQ.fromString("4212749851190062070411861546581519573149862920541429109749681953529770836138")), ell_vw: FQ2(c0: FQ.fromString("10890917529063563080488636605724946896609903254351684402546841801646043334166"), c1: FQ.fromString("17064796604657821037034221187126605208939910043822292844975397354283757360178")), ell_vv: FQ2(c0: FQ.fromString("10053764157025690527213061522668163518874554311426109722786780729745637570153"), c1: FQ.fromString("12784239716790558610229592219566905741360808124013978959238298462172061891010"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("18178610037180803777372696468852749823139836169202607246443680867419301660001"), c1: FQ.fromString("2203553150397509140176760042861998991151797164185565920046676990283238338489")), ell_vw: FQ2(c0: FQ.fromString("20994248737185978263276750124680792452522185817020429107744966692073225126536"), c1: FQ.fromString("6349838002511478763817816496894821255234760390494115897123421446389963162622")), ell_vv: FQ2(c0: FQ.fromString("1263928993764270656816573911502202456032289420152723320064785370753332332564"), c1: FQ.fromString("3046827108505433561924898780283267608115953987844810798949064271834105292424"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("8956928906602747002792065663685826131559810003765184492377190892700326559543"), c1: FQ.fromString("9256558483224710959389211180674778886236784676421453067453985103303008910100")), ell_vw: FQ2(c0: FQ.fromString("19258483215045500292709619645410473292886118206783068420253408398264757067719"), c1: FQ.fromString("4451865829956913287893327659699467549695832858717473490066802599791690802339")), ell_vv: FQ2(c0: FQ.fromString("2764660330371796211645912465865514165647712230266940655139955982219479135487"), c1: FQ.fromString("13240152986607724183470740198192310673584785226612774164092563912390438227662"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("681767682327814473892054649470454096768283412403007527891528077929769397010"), c1: FQ.fromString("7668193584311505399758378551993462119934308304509766677185196010163712481402")), ell_vw: FQ2(c0: FQ.fromString("5655243760535315520783349710797795360471266639432050916814563868068635911845"), c1: FQ.fromString("6563255051651012450772847952915771842363624186837705815170827122750163703821")), ell_vv: FQ2(c0: FQ.fromString("649940975249127008407571724305690292046357719924151085455652491670338045916"), c1: FQ.fromString("21579311737676937819033214124612551530127167774002946532496743344036322675381"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("21175778737847401044104466077056088822729568143966863788586142950892805137666"), c1: FQ.fromString("5595589116740977138584720715075423123216685509908153451278598090500673116809")), ell_vw: FQ2(c0: FQ.fromString("10584200644032922607821101570277578933623023426596682657073390742969073652895"), c1: FQ.fromString("12837953697025504547863231066503572653998301486985427806275987315834659812986")), ell_vv: FQ2(c0: FQ.fromString("8117107548208718171682922744254403704391859020172151233388364110731129041045"), c1: FQ.fromString("13049999463955485268550904857465746325902043660001708636685369259450564610454"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("8364961604780202742303921161081604160922838515374423608219736048018999860853"), c1: FQ.fromString("2351019119583633406807532653396626109486234484023597064733508979159842183382")), ell_vw: FQ2(c0: FQ.fromString("21028432880575926523752559333294402827459370060933557257525967464997077946986"), c1: FQ.fromString("8046872296156140750811104526560806223677002170113323621491280498583760192616")), ell_vv: FQ2(c0: FQ.fromString("12898024833491487058567514428449740829256150305937499595589780150928565842102"), c1: FQ.fromString("4285065448422006185829060466560686578745712460867295748911655362509144300948"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("8072232359889306936722303478835558056241116613774315547062190240728474923834"), c1: FQ.fromString("11177008054227634237663439894104913366867843529726469624208391026893438331544")), ell_vw: FQ2(c0: FQ.fromString("6809080759521318546573252139200749353665236974149238674422052122591837770337"), c1: FQ.fromString("17038522274461048605370004543286094433565235245947661777349160189041706387246")), ell_vv: FQ2(c0: FQ.fromString("14024834693814357899802394991122512208020612837103453479716792821268632856005"), c1: FQ.fromString("12162253519201182595066522680875457472054933146636012038079782236991342232964"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("7517621744472550238729147112582763290619696603888340562320661653468259785459"), c1: FQ.fromString("5775958628431902009928696648706206677051589697801230225207961079701351805991")), ell_vw: FQ2(c0: FQ.fromString("17101537508347637393046612945060881733341630882242907674489426818282232192765"), c1: FQ.fromString("11439924192045730134857855444951867487673481853061712676505751821713592417848")), ell_vv: FQ2(c0: FQ.fromString("16524831954193049226680237913906512849861747525312574297009556588583016003485"), c1: FQ.fromString("17961833510100001957206410577764797351002903290115563676683242640020206248673"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("16912575460065719860154998920476780598787708817030774617128864437198545819929"), c1: FQ.fromString("18523390726102623209642393197047309169880798198922216024709291880134883505164")), ell_vw: FQ2(c0: FQ.fromString("8711744315121016096444521009902336544777137803294883395587631628756743226403"), c1: FQ.fromString("384665796631718061484470820770858149414181395445222225443039170963661807931")), ell_vv: FQ2(c0: FQ.fromString("15774538322747462731385931838340979144243491164741595437453112329195248618714"), c1: FQ.fromString("11632275451601229685994821619837954138410931366643826111939350416539675376582"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("4193777370819241345875098285464776579390947200963349948619027946434227330979"), c1: FQ.fromString("509866391215626792725431227507051906680902316754753085884521978793646270092")), ell_vw: FQ2(c0: FQ.fromString("6037175194865804429042789680410737585033925490367655507316663717551980954540"), c1: FQ.fromString("18041663784750649636833746105782119188697038394822653258671508993118228507622")), ell_vv: FQ2(c0: FQ.fromString("5823434994217288412340362524966578492063846889148854293277163714066988854109"), c1: FQ.fromString("5672396008289604057966364197930061596003079754530820851713923211431895221182"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("4038166397773785982077422827894979131906966567246129821324959462204170431412"), c1: FQ.fromString("18437550501813119699199412531646289403507307693226141098858673836744295275291")), ell_vw: FQ2(c0: FQ.fromString("14284923653983275338299458156785711701885868498245540450770121796221847779083"), c1: FQ.fromString("12602897944901625024952695280664364105484904418233840369524699062991946110320")), ell_vv: FQ2(c0: FQ.fromString("5486498814295375008109094800710678785628460899485721363575459728835468025031"), c1: FQ.fromString("16200691130190012476008796365701719097160136918178762563260333528187553865070"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("12314546787967340174315157919495563500551846522430770700093005979883034971431"), c1: FQ.fromString("3526449836226747514409509674529965207620312182683788709671366453353950282439")), ell_vw: FQ2(c0: FQ.fromString("5093273668274685698542824313074411944254604209522407110762874543698544425100"), c1: FQ.fromString("14413934995969972893982204347979972725062293812449032149868616703831967954701")), ell_vv: FQ2(c0: FQ.fromString("5926816361020227072352913556957354039762675747717706660663155084741002986207"), c1: FQ.fromString("11598493987133778453993847174276604684683441101397418085992950965674684594056"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("14605016886275487293555440005295724730930746064070181829857167303747503806723"), c1: FQ.fromString("125488028578519699988648864181559879827394253209756997049515479109363959656")), ell_vw: FQ2(c0: FQ.fromString("13618798589781916917754240943924749450694915714095829562214241999969133303075"), c1: FQ.fromString("7919521676249934186275057369006254600339974917319129664598969307290994883444")), ell_vv: FQ2(c0: FQ.fromString("13618646464444173552701873510284352084626546810748938598145778229202447557390"), c1: FQ.fromString("9476574581262975213029808591741428214940842553330900266594833123385626035464"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("20330569420634759863618802200434379756882301920599312733161738334139846403423"), c1: FQ.fromString("16941424680188140264426604507849243567044731815188670894126312258606055646954")), ell_vw: FQ2(c0: FQ.fromString("4276245652046353558826181153633997046897244969830610112857888938076187872093"), c1: FQ.fromString("5218134511825218024578456539758678791071455837562669069391138178014247652283")), ell_vv: FQ2(c0: FQ.fromString("5074559277335429630740829563679793620243538875458585401599810469948320707914"), c1: FQ.fromString("8399181532479234777915440768158248538072911084393374644710065479068982458679"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("228456190514225889641604150077307010131630607092226273786181288134202785827"), c1: FQ.fromString("6829691501727142726895142554058769478742020003540473851444113314854839779896")), ell_vw: FQ2(c0: FQ.fromString("7239124420778199901449006013253700083679198792863456824897986348225520969318"), c1: FQ.fromString("9612815502366829000381428453387330590336692138445456863004983078118510731327")), ell_vv: FQ2(c0: FQ.fromString("3032095333965417803301146592958869325973325547652138997619203744855910423626"), c1: FQ.fromString("9140900126707960555227810341717821370276432039323462635381934661102939064717"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("15183121839690585327918066578070528787848569635478652970262274682157344344126"), c1: FQ.fromString("17032257654469593612895387885450704824985120918227803531218149228038247116993")), ell_vw: FQ2(c0: FQ.fromString("4449946720064992614521464039848704811573349967863291193814384877976473528180"), c1: FQ.fromString("16213473115750238158840972181958630393893069174331658126086552358850995045944")), ell_vv: FQ2(c0: FQ.fromString("14820364816206160787218030695278797219740217739100995460398841348849807343630"), c1: FQ.fromString("13391831978082769659226335015618034703517552702532093599836056107584855920091"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("19376350172596821938106071755601309991977917109030961898807003698880067029751"), c1: FQ.fromString("8432677549750770318577385075581043130355856390772751886981628666318547719067")), ell_vw: FQ2(c0: FQ.fromString("19907801542387995008974808935818119635245812040205527769417146511798917761987"), c1: FQ.fromString("5161102563798536355551540455475021097419898845343559944869423371507274303876")), ell_vv: FQ2(c0: FQ.fromString("8934093116311670296805867189709376457342708648536176030347455800639124491789"), c1: FQ.fromString("2703243001408501823489300103176986467031566088207792046257025791153593204110"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("14246576411085900561418149065736187542613160942023368670593924207716148262147"), c1: FQ.fromString("3894319734417282749436553474309402307762162253367446669496582090013706055610")), ell_vw: FQ2(c0: FQ.fromString("8375748526029577467798965722696242475050467009610481042928314348040697238273"), c1: FQ.fromString("5150547143250446746041060583106242553976637956855972603954741388536316657102")), ell_vv: FQ2(c0: FQ.fromString("15907790590424017252473639606980913201030067954178224590289142061847439584806"), c1: FQ.fromString("11159987404206640511954116561264499629862056981304725791275082386203982274572"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("8534750176510034607784585561677263909151653570221826711790070724427621935471"), c1: FQ.fromString("7817152482056867378682253092041139659293041868731996264204246731388352069129")), ell_vw: FQ2(c0: FQ.fromString("16961113237531514134058762308816541864350643273034658785830240248900850618232"), c1: FQ.fromString("1179078000730113993104369721112852911420394064162245727296751799866128074554")), ell_vv: FQ2(c0: FQ.fromString("20457374714339105220680185601375259716132143552962630535477097129353077443649"), c1: FQ.fromString("4422525926709336878773825319073188719230418589270084994878618648788354576429"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("7627847586963893527076371866753343482026765205726665650559945926181618692545"), c1: FQ.fromString("11854660093187915385325641424610609952992715573834872602540776698350931163771")), ell_vw: FQ2(c0: FQ.fromString("12061802008303461437760636908585485318885385024493098952040511326354153801540"), c1: FQ.fromString("2483328707802038203960641185873859652602098605879879702928670691083351376828")), ell_vv: FQ2(c0: FQ.fromString("3179938981834043637934998748622377818001614400201120097155888429286598162788"), c1: FQ.fromString("3387712903817602592588061982323804658595985412237324988504760476608661908669"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("18935668687475417752670528265758371887227072908926123795139093572891409166177"), c1: FQ.fromString("7137948322161355685471108734802366001522057971184080351365620151904917529667")), ell_vw: FQ2(c0: FQ.fromString("4339156032094542567838794153972725732752104151129467898887880650450609600794"), c1: FQ.fromString("12048671216064877807276853910375839173096391682288251978695349073893797560105")), ell_vv: FQ2(c0: FQ.fromString("1950772803812480571427275248471639178971170644950730072787717123474197730954"), c1: FQ.fromString("9181624035531055165333621510604231877029437698520448252367489746796255738472"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("11063654615400954717295218153285806614049542407631184476400061600226642090639"), c1: FQ.fromString("10322234663627758865726091502227262577963762851829427143908452375568424940506")), ell_vw: FQ2(c0: FQ.fromString("5088512375045637070831200431021199523774202712519349524231646029565850243137"), c1: FQ.fromString("13830164350727008423053294968318643994288249825064419288902919319598527803018")), ell_vv: FQ2(c0: FQ.fromString("5664114633031170805308124108402506179037402200165762737765348606356467981435"), c1: FQ.fromString("6420500289322096738785425211380588437700105870048213919996474830908364662080"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("12089017351045163205876906516764262596252750496306969979802152087250767442655"), c1: FQ.fromString("10210558808216547366164047615754858102665922901025755697993978799086515582216")), ell_vw: FQ2(c0: FQ.fromString("5805411494371431706302702029555168082350433630263841912576207114214821637903"), c1: FQ.fromString("13417649473832328299363309530328161775713579734460687495589323668315481789186")), ell_vv: FQ2(c0: FQ.fromString("7100161023424716303226449735068569399024504614273633218656928626719841360262"), c1: FQ.fromString("4608780226874321006375565633722017610093139802788639970961143211665122970425"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("7402656885283645637844399867437832414708002274719716809314550750025200002816"), c1: FQ.fromString("3359755418859898733774484791668766343777859472186478368952701397946858170923")), ell_vw: FQ2(c0: FQ.fromString("13972633490402551449646647488711159435500678729477306484155919447807727623768"), c1: FQ.fromString("7493072689132118410451346570771015894599566093753023472904593233187191189290")), ell_vv: FQ2(c0: FQ.fromString("10553632499535499938294059057022861371325444554911689769145218759723376859522"), c1: FQ.fromString("15031724096959569889887883166930429112400023614133986918505200177949730145621"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("19614666780105423133718014776333000423285968913902974939878172716992608453809"), c1: FQ.fromString("11603995082765755677014543451017101337084691386341657702599446172204728703168")), ell_vw: FQ2(c0: FQ.fromString("3640285626277218751703647194759635465446306209869384174139659856814138047153"), c1: FQ.fromString("19852827152161121673942769427296662914445783006570890136096053338872958018957")), ell_vv: FQ2(c0: FQ.fromString("15133277841235179795557173122463348349264603847086033764926472096701308210280"), c1: FQ.fromString("11477545358800195084620539652687203979589770655971679975137907269158992047904"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("14110112013826130627308137057440352762673176006101512326319632538666268095190"), c1: FQ.fromString("11939502323738533476551128890683182628994629048369580067812328846012456410750")), ell_vw: FQ2(c0: FQ.fromString("4903570904021379863440412383519324112841733022665060696302943089410552325193"), c1: FQ.fromString("14266803156893138980425811506240396635009593456307449490727186191181005152878")), ell_vv: FQ2(c0: FQ.fromString("14881072700587137091207448172180518390500891251946414292329369965686915854422"), c1: FQ.fromString("15926908436628594399583920130944357569706863129497064862365363758969852348774"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("12558706151877569296451844961986237406953575964353502214928234165049347203902"), c1: FQ.fromString("20986351119648698768747274174278231882270962658108929786748672594878002377020")), ell_vw: FQ2(c0: FQ.fromString("19049669232824485247328064933680299726722169816787419902393190195363675139966"), c1: FQ.fromString("9038064204016446065702766858179381867420605337773795708435102200352238225734")), ell_vv: FQ2(c0: FQ.fromString("20935836360010341957040867041308346409399314465762274744792154001186771274330"), c1: FQ.fromString("10203406114258970224185400536984459120400171034941205452745624846762632193745"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("5273485321155899320017031787320374433252287213043632779466667239152835002841"), c1: FQ.fromString("3053056741460414900892697870729818263336633719763669407853703091660209931803")), ell_vw: FQ2(c0: FQ.fromString("14340825980720147789753139378133767339849529468405152905403690972933175947523"), c1: FQ.fromString("10774533501875920615470512566140848784460524907884011188458749902565676606519")), ell_vv: FQ2(c0: FQ.fromString("3964278893337662256819152335242286127722672026565541866237058714197501521533"), c1: FQ.fromString("18022244127367262334687067361628890804799640514175862613915842388966614841200"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("16332502873726000727943290608233946425803707374189230473690022986938005644703"), c1: FQ.fromString("1567962947294031055122165158764344785171646063465144808831306664719326769256")), ell_vw: FQ2(c0: FQ.fromString("20252166217271346908492216031557669988560255907738941815783926955730505937199"), c1: FQ.fromString("17117796187826532609679851805915085724672631310980423533710032156677468384570")), ell_vv: FQ2(c0: FQ.fromString("17447467886258986370787137061379217598255780655216428459333948972673151012677"), c1: FQ.fromString("5126879980313476901131854669269919146638602687291477011061931179530480305128"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("6088970897125028663727223015788851329277811231049906821835210021445230531470"), c1: FQ.fromString("8269042454443245208032034843063473106607747629991910601236499943382389383909")), ell_vw: FQ2(c0: FQ.fromString("9390988514296458582044517134794855627538985266829573142624063333360969055582"), c1: FQ.fromString("14809192622791971819542419564616657251564340618078775692251491022692953050806")), ell_vv: FQ2(c0: FQ.fromString("12392403118986217155446218801253632414719473065843423820454574775060624572223"), c1: FQ.fromString("17315648550786131512187415565477299679361071890287921321719420341945848525344"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("8509704393616883918389461491307625357351608849562781376448108143686881397874"), c1: FQ.fromString("20273788844812669168701278525056990767412251703786041383351127813548056060175")), ell_vw: FQ2(c0: FQ.fromString("13587733380957601917144020513037833512715927912961534242274129611676082179670"), c1: FQ.fromString("18219007782547538891755131684432302965729501555658190323551992733355994409648")), ell_vv: FQ2(c0: FQ.fromString("15212235563035911012497734239429958536448576200220918241945992532007455729638"), c1: FQ.fromString("15868967159924843704138451124239001351258676891119605450618238262537266256000"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("566642944339341838872659990670775497018813579200118662738575067585620262683"), c1: FQ.fromString("80633561422247315402489148783136836267866433112423246780090434639448081501")), ell_vw: FQ2(c0: FQ.fromString("16776314135335488452712466347201660236874408701359052824185479604688604443386"), c1: FQ.fromString("9397286871750513195379912820117368990110423949006393550333044521067916303673")), ell_vv: FQ2(c0: FQ.fromString("11213241389292792759942014406027965770431479614859139543821417488303149467800"), c1: FQ.fromString("13968940593285273772185768016764839518710968380183853737025067101492115101555"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("6582524148524166281651049223972241418275829952930531957133089376081226599310"), c1: FQ.fromString("12912796945237135113112612780707994091197804899944634993937602107108817617069")), ell_vw: FQ2(c0: FQ.fromString("5915143763477343907746278070838860419057837978194254349309930636588762054019"), c1: FQ.fromString("16829580672314341143130263740133292836202318062773617917928807522786048686905")), ell_vv: FQ2(c0: FQ.fromString("6048262046277135430490078693604752857941808479983722633615831914037376701617"), c1: FQ.fromString("11339265196645696685132629437938382050130007247287625382019491127986659567508"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("16221040390720130701106818492787718054167551387591190581820867019772199748452"), c1: FQ.fromString("13816650124538024294513769170531808824213976236314395631188393480750356347832")), ell_vw: FQ2(c0: FQ.fromString("17884725815671017389514271919761437310258619463085199936932965564030171526767"), c1: FQ.fromString("18248973796984942533601080062907385035195180110558182226420098600644022965345")), ell_vv: FQ2(c0: FQ.fromString("3137416660392142651716799414483848222789075294982695366740419822188364577896"), c1: FQ.fromString("9694367271685190541749430052152010428005565838702388077250686840743397580564"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("6357610272973630149840225505274539351850867930627803566656124705077309138236"), c1: FQ.fromString("6532064867969562545750502093085673423036025281166230602025667552530979558092")), ell_vw: FQ2(c0: FQ.fromString("16786670094068652274677750111730144379312178408732829954083928254919389983983"), c1: FQ.fromString("6341529030893580474995202784328098673009525246309131907462803539077246776781")), ell_vv: FQ2(c0: FQ.fromString("1974498926273096258802796702119259182018579009158122203612017565775578915815"), c1: FQ.fromString("16202371700345289299248140495735793174324620209466889736695203996551421837181"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("7645596703863049949671625140189098533219782764649960681401839437305310089346"), c1: FQ.fromString("19662686877039093771787921401379726144714150453645437909640334762978238819386")), ell_vw: FQ2(c0: FQ.fromString("19675517233991845002128733409028273554556593365253876728982552859262257722391"), c1: FQ.fromString("17810553502824017140555114404262914434390762266054795883352268646885364159410")), ell_vv: FQ2(c0: FQ.fromString("18665072545928024509881282829034535118797458683193169885406548536617293330990"), c1: FQ.fromString("19411595053923298782146703680748498557982136082135126508584153240357549362078"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("14470514611309746071605255274946119202484366658141275541087020842175954067559"), c1: FQ.fromString("2594897518684998941907190849745518961756699273476562463311001408776339658780")), ell_vw: FQ2(c0: FQ.fromString("935873152179726234311073616163680460286310013909076125956427156452613025181"), c1: FQ.fromString("1833293684395168387765260233178732135932817355166697645828433012629591012612")), ell_vv: FQ2(c0: FQ.fromString("15506344864201849985196152671178663605696906693236430756412539843011256851197"), c1: FQ.fromString("21249626787247514663459670711353542343321937134901366527096486234949436840608"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("14844298589371811580114643149994926627935566419885127313767249060440840193460"), c1: FQ.fromString("21865492910219121705480174226763323783866193080747061232287801827306146286946")), ell_vw: FQ2(c0: FQ.fromString("17151100973274235243640792655189642956800303990602164052684336481398298289411"), c1: FQ.fromString("14392190221562986523826107157981466944764379735735544220135185056395768909930")), ell_vv: FQ2(c0: FQ.fromString("4350255565215767538405433120041486620343663937357318680211203103336875636557"), c1: FQ.fromString("9926081950735179732148156026738993553156731124330466866635273595752973758855"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("13218761129163938072615510267349114476398845757988353548581904005855607494827"), c1: FQ.fromString("5599121822325202341068442750237639791575579072233857524815548719976323285948")), ell_vw: FQ2(c0: FQ.fromString("15971120376538832228790474763995956330325241100841265267806125625593561200292"), c1: FQ.fromString("12523288625433720594713119420870235433458343202664663107083051275205911332632")), ell_vv: FQ2(c0: FQ.fromString("16334648365696506264242655102949242813244449705193389318653840069223698505218"), c1: FQ.fromString("5656148484258838154513173637616383061988381726003070282394323376702835298685"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("17813738829828434290335491956960173922453516626523775773406434062574864455163"), c1: FQ.fromString("20602369742562270094694203910247000789225384961205618501257582838246099644397")), ell_vw: FQ2(c0: FQ.fromString("19878549521928480892919729627745688905048802531306612605469432942519616386562"), c1: FQ.fromString("5017457857497268504225948371697060380418757800650743056471872023476645133890")), ell_vv: FQ2(c0: FQ.fromString("11253669115821132651967277438747600032147012598164512191026558281212405907059"), c1: FQ.fromString("17527101633359473938082679669763197242520773041737623688955597341205608093817"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("4215545173967763533303120784928042872427929334572670490629936371429427871071"), c1: FQ.fromString("12294795716682648009432653500746413304750644076589949908249345884220200424705")), ell_vw: FQ2(c0: FQ.fromString("18825720171660950734491494575282684507875213824417353005009465489701558195915"), c1: FQ.fromString("16705960611772713893529695679517111622754789317070946155202186944060021844706")), ell_vv: FQ2(c0: FQ.fromString("727717707174551758531261221179545916153780087179478433501944451338651779512"), c1: FQ.fromString("8112271980906996458859937804680221894935591766524365848766530295751121904748"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("10233024162783159141952008177866660607852287510058437429941412641351833243474"), c1: FQ.fromString("9594838646894429694778293318641489517645344438331800828882574864439372872574")), ell_vw: FQ2(c0: FQ.fromString("16645682756797429615195184700295338945089747965333104334845226609564841400023"), c1: FQ.fromString("4162352035394629024812623559649906911351409190415492302470361657064711946691")), ell_vv: FQ2(c0: FQ.fromString("6032119528092518381867266123093895943802103316999621961287699634448441322294"), c1: FQ.fromString("16978590002913514123461500015923670605519520926487110857783653291799516315656"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("8218196524386055233961282665657821183883422738571989211183480610420112668380"), c1: FQ.fromString("10415506866745570162620028320970394286106067432626512901854031549920339603997")), ell_vw: FQ2(c0: FQ.fromString("5210044150482547744429104832217210823770891140961861953610847155896342519957"), c1: FQ.fromString("7249576834325689075837879557249227252903732278842727723303356548006299693332")), ell_vv: FQ2(c0: FQ.fromString("10566250213397026997176223676662431109660830193211672199901829008963029461821"), c1: FQ.fromString("10109726446308488117042758961222375921166426648339199001670753736584698407831"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("14958658301977599538003236672511296104101995894564376511283101560012949683335"), c1: FQ.fromString("3048608469033125363311089547916169290720115268350540694596412955569118898494")), ell_vw: FQ2(c0: FQ.fromString("574489666015527527356241753990038643397861257676315289742492198353125347063"), c1: FQ.fromString("20135743140323312729475654806927894483883158119600963784497647679670289292384")), ell_vv: FQ2(c0: FQ.fromString("429037282750459454100296565403648201904295120071497517313998879109362521423"), c1: FQ.fromString("15853233349247701874380259741343021520320591334343875702627292292088300425145"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("18700030680590279184945769912899822403041358764323477965514340815228877402331"), c1: FQ.fromString("10683998632701796443517414309938660369292269662497518738662465524924019738910")), ell_vw: FQ2(c0: FQ.fromString("1310694906110452150099267122613851606459600617516350757492763828252192371857"), c1: FQ.fromString("4783076385012654125132398886450190041207227400254467276271580769333335151361")), ell_vv: FQ2(c0: FQ.fromString("19588709046778297839342456362815786766378666302331732601204786295010565114246"), c1: FQ.fromString("15768094978512846661820502043664035262466161785979259300748269497441797675546"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("13060364876687824737412369705619641318084803694180161837383771909738942399322"), c1: FQ.fromString("18217552047682533290011237332328856250732338018560605204821714348687071938690")), ell_vw: FQ2(c0: FQ.fromString("12869661643727171099080271622741099816758098286424339832028838960253051261267"), c1: FQ.fromString("9771005613420764951919925720405408601257471427085946188171574196917272408972")), ell_vv: FQ2(c0: FQ.fromString("50909581144623642750375885935872654509592339539711209410079054797052409354"), c1: FQ.fromString("18926528403466649263993967111065644144239906211315737390219036362821972145669"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("20549804669307980165961565026567668900175984309510667407934326603524068980568"), c1: FQ.fromString("2860891967505015044048360806986650576604923893849408592665407858213623973073")), ell_vw: FQ2(c0: FQ.fromString("15425380359872501526721873883293811604657689327716748473720120299487795555723"), c1: FQ.fromString("19476618728903347368164774764554105623361814261939990815870207909384053172994")), ell_vv: FQ2(c0: FQ.fromString("1391900673866549134877070137078952075347059393013450066057338968238992587955"), c1: FQ.fromString("4196316861965319041187484774757427170509633759722370813890904092023663955821"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("17275387403896977778123657295201685204905269368879700703438840824610915049467"), c1: FQ.fromString("19208898840130004899976179132541534722147305748950927170013798354273078356439")), ell_vw: FQ2(c0: FQ.fromString("2806476769978795611970161315323438770892084130100332531802098991493644141407"), c1: FQ.fromString("12027685137795878129532114449809592645364590672303716160923491816560315933093")), ell_vv: FQ2(c0: FQ.fromString("8236588507133996535585623995380369531792969451558421380293164596516832310024"), c1: FQ.fromString("7194816002224802847462171990762514812770222507636791853795854276216118633532"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("8988630595018387496849477785798469553390006328427704582362113137927154840355"), c1: FQ.fromString("2295777524525303845671711839319946449143831320846022233249545363172788331149")), ell_vw: FQ2(c0: FQ.fromString("19856063720990800344827304970869772811632532243020577718537161398914360273978"), c1: FQ.fromString("19822454791015240925652351664190735476954892251509376470361602440577337517739")), ell_vv: FQ2(c0: FQ.fromString("16437024240432855113321845721989731630224984534156413407578941758236945886728"), c1: FQ.fromString("10915573623321985107029933965796058231484519510670830600808943167010577314334"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("21286975582879196598650077668567283554136994229408922053641846042051746388886"), c1: FQ.fromString("5421529658065143080970166226046014500038127295988164061860891929414040927615")), ell_vw: FQ2(c0: FQ.fromString("5555485154537896512520569992616892769357525491868135294451583184259218193278"), c1: FQ.fromString("4831809465565304340310880868620257258305484211486308136368207144444325564275")), ell_vv: FQ2(c0: FQ.fromString("2583089702666220608189326901976548985583885001543445628170288810575076369034"), c1: FQ.fromString("7097788099664798415333498763272677734135137637990913349021964143797057593727"))),
EllCoeffs(ell_0: FQ2(c0: FQ.fromString("21356455346146359274071336488740257199607341582407258426849457339859865454145"), c1: FQ.fromString("19512280220246743343068375192508909480135195681362473260894831078332945004959")), ell_vw: FQ2(c0: FQ.fromString("15435677241338788169971902812985017500898533325271813422525238374746874853456"), c1: FQ.fromString("6382067580057986766020919077216175344977116334133202456947134195629074647989")), ell_vv: FQ2(c0: FQ.fromString("1150360546643207487285835972411275438312694180812115547021545502055712764110"), c1: FQ.fromString("2194960903427600986795419731825236689333014397679688012527722471213485607323")))
]
)
var g2 = G2.one() * FR.fromString("20390255904278144451778773028944684152769293537511418234311120800877067946")
var g2p = g2.toAffine().get().precompute()
if g2p.q != expect.q:
return false
if g2p.coeffs != expect.coeffs:
return false
return true
proc testMillerLoop(): bool =
var expect = FQ12(
c0: FQ6(
c0: FQ2(c0: FQ.fromString("14551901853310307118181117653102171756020286507151693083446930124375536995872"), c1: FQ.fromString("9312135802322424742640599513015426415694425842442244572104764725304978020017")),
c1: FQ2(c0: FQ.fromString("2008578374540014049115224515107136454624926345291695498760935593377832328658"), c1: FQ.fromString("19401931167387470703307774451905975977586101231060812348184567722817888018105")),
c2: FQ2(c0: FQ.fromString("15835061253582829097893482726334173316772697321004871665993836763948321578465"), c1: FQ.fromString("2434436628082562384254182545550914004674636606111293955202388712261962820365"))
),
c1: FQ6(
c0: FQ2(c0: FQ.fromString("2874440054453559166574356420729655370224872280550180463983603224123901706537"), c1: FQ.fromString("21199736323249863378180814900160978651989782296293186487853700340281870105680")),
c1: FQ2(c0: FQ.fromString("19165582755854282767090326095669835261356341739532443976394958023142879015770"), c1: FQ.fromString("1381947898997178910398427566832118260186305708991760706544743699683050330259")),
c2: FQ2(c0: FQ.fromString("282285618133171001983721596014922591835675934808772882476123488581876545578"), c1: FQ.fromString("9533292755262567365755835323107174518472361243562718718917822947506880920117"))
)
)
let g1 = G1.one() * FR.fromString("18097487326282793650237947474982649264364522469319914492172746413872781676")
let g2 = G2.one() * FR.fromString("20390255904278144451778773028944684152769293537511418234311120800877067946")
let g1pre = g1.toAffine().get()
let g2pre = g2.toAffine().get().precompute()
let gt = g2pre.millerLoop(g1pre)
if gt == expect:
return true
proc testReducedPairing(): bool =
let g1 = G1.one() * FR.fromString("18097487326282793650237947474982649264364522469319914492172746413872781676")
let g2 = G2.one() * FR.fromString("20390255904278144451778773028944684152769293537511418234311120800877067946")
let gt = pairing(g1, g2)
let expected = FQ12(
c0: FQ6(
c0: FQ2(c0: FQ.fromString("7520311483001723614143802378045727372643587653754534704390832890681688842501"), c1: FQ.fromString("20265650864814324826731498061022229653175757397078253377158157137251452249882")),
c1: FQ2(c0: FQ.fromString("11942254371042183455193243679791334797733902728447312943687767053513298221130"), c1: FQ.fromString("759657045325139626991751731924144629256296901790485373000297868065176843620")),
c2: FQ2(c0: FQ.fromString("16045761475400271697821392803010234478356356448940805056528536884493606035236"), c1: FQ.fromString("4715626119252431692316067698189337228571577552724976915822652894333558784086"))
),
c1: FQ6(
c0: FQ2(c0: FQ.fromString("14901948363362882981706797068611719724999331551064314004234728272909570402962"), c1: FQ.fromString("11093203747077241090565767003969726435272313921345853819385060670210834379103")),
c1: FQ2(c0: FQ.fromString("17897835398184801202802503586172351707502775171934235751219763553166796820753"), c1: FQ.fromString("1344517825169318161285758374052722008806261739116142912817807653057880346554")),
c2: FQ2(c0: FQ.fromString("11123896897251094532909582772961906225000817992624500900708432321664085800838"), c1: FQ.fromString("17453370448280081813275586256976217762629631160552329276585874071364454854650"))
)
)
result = (gt == expected)
proc testBinlinearity(): bool =
for i in 0..<10:
let p = G1.random()
let q = G2.random()
let s = FR.random()
let sp = p * s
let sq = q * s
let a = pairing(p, q).pow(s)
let b = pairing(sp, q)
let c = pairing(p, sq)
if a != b:
return false
if b != c:
return false
let t = -(FR.one())
if a == FQ12.one():
return false
if a.pow(t) * a != FQ12.one():
return false
return true
proc testDH(): bool =
# Generate private keys
var alice_sk = FR.random()
var bob_sk = FR.random()
var carol_sk = FR.random()
# Construct public keys
let alice_pk = G1.one() * alice_sk
let bob_pk = G1.one() * bob_sk
let carol_pk = G1.one() * carol_sk
# Round one
let alice_dh_1 = bob_pk * carol_sk
let bob_dh_1 = carol_pk * alice_sk
let carol_dh_1 = alice_pk * bob_sk
# Round two
let alice_dh_2 = alice_dh_1 * alice_sk
let bob_dh_2 = bob_dh_1 * bob_sk
let carol_dh_2 = carol_dh_1 * carol_sk
result = (alice_dh_2 == bob_dh_2 and bob_dh_2 == carol_dh_2)
proc testJoux(): bool =
# Generate private keys
var alice_sk = FR.random()
var bob_sk = FR.random()
var carol_sk = FR.random()
# Generate public keys in G1 and G2
let alice_pk1 = G1.one() * alice_sk
let alice_pk2 = G2.one() * alice_sk
let bob_pk1 = G1.one() * bob_sk
let bob_pk2 = G2.one() * bob_sk
let carol_pk1 = G1.one() * carol_sk
let carol_pk2 = G2.one() * carol_sk
# Each party computes the shared secret
let alice_ss = pairing(bob_pk1, carol_pk2).pow(alice_sk)
let bob_ss = pairing(carol_pk1, alice_pk2).pow(bob_sk)
let carol_ss = pairing(alice_pk1, bob_pk2).pow(carol_sk)
result = (alice_ss == bob_ss and bob_ss == carol_ss)
when isMainModule:
suite "Pairing test suite":
test "Prepared G2 test":
check testPreparedG2() == true
test "Miller loop test":
check testMillerLoop() == true
test "Reduced pairing test":
check testReducedPairing() == true
test "Binlinearity test":
check testBinlinearity() == true
test "Diffie-Hellman test":
check testDH() == true
test "Joux test":
check testJoux() == true

30093
tests/tvectors.nim Normal file

File diff suppressed because it is too large Load Diff