nim-bncurve/bncurve/fp.nim

212 lines
7.0 KiB
Nim

# 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
{.deadCodeElim: on.}
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:
doAssert(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 FR/FQ ``num`` to 256bit integer.
result = BNU256(num)
mul(result, BNU256.one(), BNU256(fimodulus), fiinv)
proc init*(t: typedesc[finame], num: BNU256): Option[finame] =
## Initialize FR/FQ 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 init2*(t: typedesc[finame], num: BNU256): finame =
## Initalize FR/FQ from 256bit integer ``num`` regardless of modulus.
result = finame(num)
mul(BNU256(result), BNU256(firsquared), BNU256(fimodulus), fiinv)
proc fromBytes*(dst: var finame, src: openArray[byte]): bool {.noinit.} =
## Create integer FR/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 fromBytes2*(dst: var finame, src: openArray[byte]): bool {.noinit.} =
## Create integer FR/FQ from big-endian bytes representation ``src`` in
## Ethereum way (without modulo check).
## Returns ``true`` if ``dst`` was successfully initialized, ``false``
## otherwise.
result = false
var bn: BNU256
if bn.fromBytes(src):
dst = finame.init2(bn)
result = true
proc toBytes*(src: finame,
dst: var openArray[byte]): bool {.noinit, inline.} =
## Encode integer FP/FQ to big-endian bytes representation ``dst``.
## 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
)