Add formally verified and prover generated BLS12_381 implementation
This commit is contained in:
parent
6423be0dfb
commit
fafebacd05
|
@ -0,0 +1,14 @@
|
|||
# Formal verification
|
||||
|
||||
This folder will hold code related to formal verification.
|
||||
|
||||
## References
|
||||
|
||||
- Fiat Crypto: Synthesizing Correct-by-Construction Code for Cryptographic Primitives
|
||||
https://github.com/mit-plv/fiat-crypto
|
||||
|
||||
- [Andres Erbsen, Jade Philipoom, Jason Gross, Robert Sloan, Adam Chlipala. Simple High-Level Code For Cryptographic Arithmetic -- With Proofs, Without Compromises. To Appear in Proceedings of the IEEE Symposium on Security & Privacy 2019 (S&P'19). May 2019.](http://adam.chlipala.net/papers/FiatCryptoSP19/FiatCryptoSP19.pdf). This paper describes multiple field arithmetic implementations, and an older version of the compilation pipeline (preserved [here](https://github.com/mit-plv/fiat-crypto/tree/sp2019latest)). It is somewhat space-constrained, so some details are best read about in theses below.
|
||||
- [Jade Philipoom. Correct-by-Construction Finite Field Arithmetic in Coq. MIT Master's Thesis. February 2018.](http://adam.chlipala.net/theses/jadep_meng.pdf) Chapters 3 and 4 contain a detailed walkthrough of the field arithmetic implementations (again, targeting the previous compilation pipeline).
|
||||
- [Andres Erbsen. Crafting Certified Elliptic CurveCryptography Implementations in Coq. MIT Master's Thesis. June 2017.](
|
||||
http://adam.chlipala.net/theses/andreser_meng.pdf) Section 3 contains a whirlwind introduction to synthesizing field arithmetic code using coq, without assuming Coq skills, but covering a tiny fraction of the overall library. Sections 5 and 6 contain the only write-up on the ellitpic-curve library in this repository.
|
||||
- The newest compilation pipeline does not have a separate document yet, but this README does go over it in some detail.
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,261 @@
|
|||
## Autogenerated
|
||||
## curve description: test
|
||||
## requested operations: (all)
|
||||
## m = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab (from "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab")
|
||||
## machine_wordsize = 64 (from "64")
|
||||
##
|
||||
## NOTE: In addition to the bounds specified above each function, all
|
||||
## functions synthesized for this Montgomery arithmetic require the
|
||||
## input to be strictly less than the prime modulus (m), and also
|
||||
## require the input to be in the unique saturated representation.
|
||||
## All functions also ensure that these two properties are true of
|
||||
## return values.
|
||||
|
||||
{.compile: "bls12_381_q_64.c".}
|
||||
|
||||
type
|
||||
fiat_bls12_381_q_uint1*{.importc.} = cuchar
|
||||
fiat_bls12_381_q_int1*{.importc.} = cchar
|
||||
fiat_bls12_381_q_int128*{.importc.} = object
|
||||
fiat_bls12_381_q_uint128*{.importc.} = object
|
||||
|
||||
BLSNumber = array[6, uint64]
|
||||
|
||||
func fiat_bls12_381_q_mul*(r: var BLSNumber, a, b: BLSNumber) {.importc.}
|
||||
## Mongomery Mul
|
||||
func fiat_bls12_381_q_square*(r: var BLSNumber, a: BLSNumber) {.importc.}
|
||||
## Mongomery Square
|
||||
func fiat_bls12_381_q_add*(r: var BLSNumber, a, b: BLSNumber) {.importc.}
|
||||
## Modular Add
|
||||
func fiat_bls12_381_q_sub*(r: var BLSNumber, a, b: BLSNumber) {.importc.}
|
||||
## Modular Sub
|
||||
func fiat_bls12_381_q_opp*(r: var BLSNumber, a: BLSNumber) {.importc.}
|
||||
## Modular Negate
|
||||
func fiat_bls12_381_q_from_montgomery*(r: var BLSNumber, a: BLSNumber) {.importc.}
|
||||
## Montgomery to Canonical
|
||||
func fiat_bls12_381_q_to_bytes*(r: var array[48, byte], a: BLSNumber) {.importc.}
|
||||
## Montgomery to Little-Endian
|
||||
func fiat_bls12_381_q_from_bytes*(r: var BLSNumber, a: array[48, byte]) {.importc.}
|
||||
## Little-Endian to Montgomery
|
||||
|
||||
# Hex conversion
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
func readHexChar(c: char): uint8 {.inline.}=
|
||||
## Converts an hex char to an int
|
||||
## CT: leaks position of invalid input if any.
|
||||
case c
|
||||
of '0'..'9': result = uint8 ord(c) - ord('0')
|
||||
of 'a'..'f': result = uint8 ord(c) - ord('a') + 10
|
||||
of 'A'..'F': result = uint8 ord(c) - ord('A') + 10
|
||||
else:
|
||||
raise newException(ValueError, $c & "is not a hexadecimal character")
|
||||
|
||||
func skipPrefixes(current_idx: var int, str: string, radix: static range[2..16]) {.inline.} =
|
||||
## Returns the index of the first meaningful char in `hexStr` by skipping
|
||||
## "0x" prefix
|
||||
## CT:
|
||||
## - leaks if input length < 2
|
||||
## - leaks if input start with 0x, 0o or 0b prefix
|
||||
|
||||
if str.len < 2:
|
||||
return
|
||||
|
||||
assert current_idx == 0, "skipPrefixes only works for prefixes (position 0 and 1 of the string)"
|
||||
if str[0] == '0':
|
||||
case str[1]
|
||||
of {'x', 'X'}:
|
||||
assert radix == 16, "Parsing mismatch, 0x prefix is only valid for a hexadecimal number (base 16)"
|
||||
current_idx = 2
|
||||
of {'o', 'O'}:
|
||||
assert radix == 8, "Parsing mismatch, 0o prefix is only valid for an octal number (base 8)"
|
||||
current_idx = 2
|
||||
of {'b', 'B'}:
|
||||
assert radix == 2, "Parsing mismatch, 0b prefix is only valid for a binary number (base 2)"
|
||||
current_idx = 2
|
||||
else: discard
|
||||
|
||||
func countNonBlanks(hexStr: string, startPos: int): int =
|
||||
## Count the number of non-blank characters
|
||||
## ' ' (space) and '_' (underscore) are considered blank
|
||||
##
|
||||
## CT:
|
||||
## - Leaks white-spaces and non-white spaces position
|
||||
const blanks = {' ', '_'}
|
||||
|
||||
for c in hexStr:
|
||||
if c in blanks:
|
||||
result += 1
|
||||
|
||||
func fromHex(output: var openArray[byte], hexStr: string, order: static[Endianness]) =
|
||||
## Read a hex string and store it in a byte array `output`.
|
||||
## The string may be shorter than the byte array.
|
||||
##
|
||||
## The source string must be hex big-endian.
|
||||
## The destination array can be big or little endian
|
||||
var
|
||||
skip = 0
|
||||
dstIdx: int
|
||||
shift = 4
|
||||
skipPrefixes(skip, hexStr, 16)
|
||||
|
||||
const blanks = {' ', '_'}
|
||||
let nonBlanksCount = countNonBlanks(hexStr, skip)
|
||||
|
||||
let maxStrSize = output.len * 2
|
||||
let size = hexStr.len - skip - nonBlanksCount
|
||||
|
||||
doAssert size <= maxStrSize, "size: " & $size & " (without blanks or prefix), maxSize: " & $maxStrSize
|
||||
|
||||
if size < maxStrSize:
|
||||
# include extra byte if odd length
|
||||
dstIdx = output.len - (size + 1) div 2
|
||||
# start with shl of 4 if length is even
|
||||
shift = 4 - size mod 2 * 4
|
||||
|
||||
for srcIdx in skip ..< hexStr.len:
|
||||
if hexStr[srcIdx] in blanks:
|
||||
continue
|
||||
|
||||
let nibble = hexStr[srcIdx].readHexChar shl shift
|
||||
when order == bigEndian:
|
||||
output[dstIdx] = output[dstIdx] or nibble
|
||||
else:
|
||||
output[output.high - dstIdx] = output[output.high - dstIdx] or nibble
|
||||
shift = (shift + 4) and 4
|
||||
dstIdx += shift shr 2
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
when isMainModule:
|
||||
import random, std/monotimes, times, strformat, ./timers
|
||||
|
||||
const Iters = 1_000_000
|
||||
const InvIters = 1000
|
||||
|
||||
randomize(1234)
|
||||
|
||||
# warmup
|
||||
proc warmup*() =
|
||||
# Warmup - make sure cpu is on max perf
|
||||
let start = cpuTime()
|
||||
var foo = 123
|
||||
for i in 0 ..< 300_000_000:
|
||||
foo += i*i mod 456
|
||||
foo = foo mod 789
|
||||
|
||||
# Compiler shouldn't optimize away the results as cpuTime rely on sideeffects
|
||||
let stop = cpuTime()
|
||||
echo &"\n\nWarmup: {stop - start:>4.4f} s, result {foo} (displayed to avoid compiler optimizing warmup away)\n"
|
||||
|
||||
warmup()
|
||||
|
||||
echo "\n⚠️ Measurements are approximate and use the CPU nominal clock: Turbo-Boost and overclocking will skew them."
|
||||
echo "==========================================================================================================\n"
|
||||
|
||||
proc report(op, field: string, start, stop: MonoTime, startClk, stopClk: int64, iters: int) =
|
||||
echo &"{op:<15} {field:<15} {inNanoseconds((stop-start) div iters):>9} ns {(stopClk - startClk) div iters:>9} cycles"
|
||||
|
||||
proc addBench() =
|
||||
var aBytes, bBytes: array[48, byte]
|
||||
# BN254 field modulus
|
||||
aBytes.fromHex("0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47", littleEndian)
|
||||
# BLS12-381 prime - 2
|
||||
bBytes.fromHex("0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaa9", littleEndian)
|
||||
|
||||
var r, a, b: BLSNumber
|
||||
|
||||
a.fiat_bls12_381_q_from_bytes(aBytes)
|
||||
b.fiat_bls12_381_q_from_bytes(bBytes)
|
||||
|
||||
let start = getMonotime()
|
||||
let startClk = getTicks()
|
||||
for _ in 0 ..< Iters:
|
||||
r.fiat_bls12_381_q_add(a, b)
|
||||
let stopClk = getTicks()
|
||||
let stop = getMonotime()
|
||||
report("Addition", "FiatCrypto[BLS12_381]", start, stop, startClk, stopClk, Iters)
|
||||
|
||||
addBench()
|
||||
|
||||
proc subBench() =
|
||||
var aBytes, bBytes: array[48, byte]
|
||||
# BN254 field modulus
|
||||
aBytes.fromHex("0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47", littleEndian)
|
||||
# BLS12-381 prime - 2
|
||||
bBytes.fromHex("0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaa9", littleEndian)
|
||||
|
||||
var r, a, b: BLSNumber
|
||||
|
||||
a.fiat_bls12_381_q_from_bytes(aBytes)
|
||||
b.fiat_bls12_381_q_from_bytes(bBytes)
|
||||
|
||||
let start = getMonotime()
|
||||
let startClk = getTicks()
|
||||
for _ in 0 ..< Iters:
|
||||
r.fiat_bls12_381_q_add(a, b)
|
||||
let stopClk = getTicks()
|
||||
let stop = getMonotime()
|
||||
report("Substraction", "FiatCrypto[BLS12_381]", start, stop, startClk, stopClk, Iters)
|
||||
|
||||
subBench()
|
||||
|
||||
proc negBench() =
|
||||
var aBytes: array[48, byte]
|
||||
# BN254 field modulus
|
||||
aBytes.fromHex("0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47", littleEndian)
|
||||
|
||||
var r, a: BLSNumber
|
||||
a.fiat_bls12_381_q_from_bytes(aBytes)
|
||||
|
||||
let start = getMonotime()
|
||||
let startClk = getTicks()
|
||||
for _ in 0 ..< Iters:
|
||||
r.fiat_bls12_381_q_opp(a)
|
||||
let stopClk = getTicks()
|
||||
let stop = getMonotime()
|
||||
report("Negation", "FiatCrypto[BLS12_381]", start, stop, startClk, stopClk, Iters)
|
||||
|
||||
negBench()
|
||||
|
||||
proc mulBench() =
|
||||
var aBytes, bBytes: array[48, byte]
|
||||
# BN254 field modulus
|
||||
aBytes.fromHex("0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47", littleEndian)
|
||||
# BLS12-381 prime - 2
|
||||
bBytes.fromHex("0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaa9", littleEndian)
|
||||
|
||||
var r, a, b: BLSNumber
|
||||
|
||||
a.fiat_bls12_381_q_from_bytes(aBytes)
|
||||
b.fiat_bls12_381_q_from_bytes(bBytes)
|
||||
|
||||
let start = getMonotime()
|
||||
let startClk = getTicks()
|
||||
for _ in 0 ..< Iters:
|
||||
r.fiat_bls12_381_q_mul(a, b)
|
||||
let stopClk = getTicks()
|
||||
let stop = getMonotime()
|
||||
report("Multiplication", "FiatCrypto[BLS12_381]", start, stop, startClk, stopClk, Iters)
|
||||
|
||||
mulBench()
|
||||
|
||||
proc sqrBench() =
|
||||
var aBytes: array[48, byte]
|
||||
# BN254 field modulus
|
||||
aBytes.fromHex("0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47", littleEndian)
|
||||
|
||||
var r, a: BLSNumber
|
||||
a.fiat_bls12_381_q_from_bytes(aBytes)
|
||||
|
||||
let start = getMonotime()
|
||||
let startClk = getTicks()
|
||||
for _ in 0 ..< Iters:
|
||||
r.fiat_bls12_381_q_square(a)
|
||||
let stopClk = getTicks()
|
||||
let stop = getMonotime()
|
||||
report("Squaring", "FiatCrypto[BLS12_381]", start, stop, startClk, stopClk, Iters)
|
||||
|
||||
sqrBench()
|
||||
|
||||
# TODO: No inversion bench
|
Loading…
Reference in New Issue