Fix native backend compilation (static types only) (#4)

* Fix types

* Fix compilation of native backends (implementation tests still fails)
This commit is contained in:
Mamy Ratsimbazafy 2018-03-01 16:33:34 +01:00 committed by GitHub
parent 18b8617d37
commit 5e192ebec9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 60 additions and 30 deletions

View File

@ -19,5 +19,9 @@ proc test(name: string, lang: string = "cpp") =
switch("out", ("./build/" & name))
setCommand lang, "tests/" & name & ".nim"
task test, "Run all tests":
task test, "Run all tests - libsecp256k1 backend":
test "all_tests"
task test_backend_native, "Run all tests - pure Nim backend":
switch("define", "backend_native")
test "all_tests"

View File

@ -1,30 +1,55 @@
# Copyright (c) 2018 Status Research & Development GmbH
# Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT).
import ../datatypes, ../private/[array_utils, casting],
import ../datatypes, ../private/[array_utils, lowlevel_types],
./jacobian, ./mod_arithmetic, ./hmac, ./constants
import ttmath, keccak_tiny, strutils,
nimsha2 # TODO: For SHA-256, use OpenSSL instead? (see https://rosettacode.org/wiki/SHA-256#Nim)
proc decode_public_key(pubKey: ByteArrayBE[64]
): array[2, UInt256] {.noInit,inline,noSideEffect.} =
# Slicing with "result[0] = readUint256BE pubKey[0 ..< 32]" would allocate an intermediary seq
# See https://github.com/nim-lang/Nim/issues/5753#issuecomment-369597564
# Workaround: pointers
var
pk1, pk2: ptr array[32, byte]
shallowCopy(pk1, cast[type pk1](pubkey[0].unsafeAddr))
shallowCopy(pk2, cast[type pk2](pubkey[32].unsafeAddr))
result[0] = readUint256BE pk1[]
result[1] = readUint256BE pk2[]
proc encode_raw_public_key(pubKeyInt: array[2, Uint256]
): ByteArrayBE[64] {.noInit,inline,noSideEffect.}=
result[0 ..< 32] = pubKeyInt[0].toByteArrayBE
result[32 ..< 64] = pubKeyInt[1].toByteArrayBE
proc private_key_to_public_key*(key: PrivateKey): PublicKey {.noInit.}=
# TODO: allow to switch implementation based on backend
if key.raw_key >= SECPK1_N: # TODO use ranged type
let keyInt = key.raw_key.readUint256BE
if keyInt >= SECPK1_N: # TODO use ranged type
raise newException(ValueError, "Invalid private key")
result.raw_key = fast_multiply(SECPK1_G, key.raw_key)
result.raw_key = encode_raw_public_key fast_multiply(SECPK1_G, keyInt)
proc ecdsa_raw_verify*(msg_hash: Hash[256], vrs: Signature, key: PublicKey): bool =
let
w = invmod(vrs.s, SECPK1_N)
z = msg_hash.toUInt256
z = readUint256BE cast[ByteArrayBE[32]](msg_hash)
u1 = mulmod(z, w, SECPK1_N)
u2 = mulmod(vrs.r, w, SECPK1_N)
xy = fast_add(
fast_multiply(SECPK1_G, u1),
fast_multiply(key.raw_key, u2)
fast_multiply(key.raw_key.decode_public_key, u2)
)
result = vrs.r == xy[0] and vrs.r.isOdd and vrs.s.isOdd
@ -35,23 +60,23 @@ proc deterministic_generate_k(msg_hash: Hash[256], key: PrivateKey): UInt256 =
let
# TODO: avoid heap allocation
k_1 = k_0.hmac_sha256(@v_0 & @[0x00.byte] & @(toByteArray(key.raw_key)) & @(msg_hash.data))
k_1 = k_0.hmac_sha256(@v_0 & @[0x00.byte] & @(key.raw_key) & @(msg_hash.data))
v_1 = cast[array[32, byte]](k_1.hmac_sha256(@v_0))
k_2 = k_1.hmac_sha256(@v_1 & @[0x01.byte] & @(toByteArray(key.raw_key)) & @(msg_hash.data))
k_2 = k_1.hmac_sha256(@v_1 & @[0x01.byte] & @(key.raw_key) & @(msg_hash.data))
v_2 = k_2.hmac_sha256(@v_1)
kb = k_2.hmac_sha256(@v_2)
result = kb.toUInt256
result = readUint256BE cast[ByteArrayBE[32]](kb)
proc ecdsa_raw_sign*(msg_hash: Hash[256], key: PrivateKey): Signature =
proc ecdsa_sign*(key: PrivateKey, msg_hash: Hash[256]): Signature {.noInit.} =
modulo(SECPK1_N):
let
z = msg_hash.toUInt256
z = readUint256BE cast[ByteArrayBE[32]](msg_hash)
k = deterministic_generate_k(msg_hash, key)
ry = fast_multiply(SECPK1_G, k)
s_raw = invmod(k, SECPK1_N) * (z + ry[0] * key.raw_key)
s_raw = invmod(k, SECPK1_N) * (z + ry[0] * key.raw_key.readUint256BE)
result.v = uint8 getUint `xor`(
ry[1] mod 2.u256,
@ -61,7 +86,7 @@ proc ecdsa_raw_sign*(msg_hash: Hash[256], key: PrivateKey): Signature =
else: SECPK1_N - s_raw
result.r = ry[0]
proc ecdsa_raw_recover*(msg_hash: Hash[256], vrs: Signature): PublicKey {.noInit.} =
proc ecdsa_recover*(msg_hash: Hash[256], vrs: Signature): PublicKey {.noInit.} =
modulo(SECPK1_P):
let
x = vrs.r
@ -78,7 +103,7 @@ proc ecdsa_raw_recover*(msg_hash: Hash[256], vrs: Signature): PublicKey {.noInit
raise newException(ValueError, "Bad signature")
let
z = msg_hash.toUInt256
z = readUint256BE cast[ByteArrayBE[32]](msg_hash)
Gz = jacobian_multiply(
[SECPK1_Gx, SECPK1_Gy,1.u256],
submod(SECPK1_N, z, SECPK1_N)
@ -90,4 +115,14 @@ proc ecdsa_raw_recover*(msg_hash: Hash[256], vrs: Signature): PublicKey {.noInit
Qr = jacobian_add(Gz, XY)
Q = jacobian_multiply(Qr, invmod(vrs.r, SECPK1_N))
result.raw_key = from_jacobian(Q)
result.raw_key = encode_raw_public_key from_jacobian(Q)
proc serialize*(key: PublicKey): string {.noSideEffect.}=
## Exports a publicKey to a hex string
result = "04"
let decoded = key.raw_key.decode_public_key
result.add decoded[0].toHex
result.add decoded[1].toHex

View File

@ -13,6 +13,7 @@ import keccak_tiny
when defined(backend_native):
import ./backend_native/ecdsa
export ecdsa.serialize
else:
import ./backend_libsecp256k1/libsecp256k1
export libsecp256k1.serialize

View File

@ -13,31 +13,21 @@ import ttmath, strutils, strutils
# https://www.reddit.com/r/crypto/comments/6287my/explanations_on_the_keccaksha3_paddingbyte/
# Note: Since Nim's Keccak-Tiny only accepts string as input, endianness does not matter.
type ByteArrayBE*[N: static[int]] = distinct array[N, byte]
type ByteArrayBE*[N: static[int]] = array[N, byte]
## A byte array that stores bytes in big-endian order
proc `[]`*[N: static[int], I: Ordinal](ba: ByteArrayBE[N], i: I): byte {.noSideEffect, inline.}=
(array[N,byte])(ba)[i]
proc `[]`*[N: static[int], I: Ordinal](ba: var ByteArrayBE[N], i: I): var byte {.noSideEffect, inline.}=
(array[N,byte])(ba)[i]
proc `[]=`*[N: static[int], I: Ordinal](ba: var ByteArrayBE[N], i: I, val: byte) {.noSideEffect, inline.}=
(array[N,byte])(ba)[i] = val
proc `==`*[N: static[int]](a, b: ByteArrayBE[N]): bool {.noSideEffect, inline.} =
(array[N, byte])(a) == (array[N, byte])(b)
proc readUint256BE*(ba: ByteArrayBE[32]): UInt256 {.noSideEffect.}=
proc readUint256BE*(ba: ByteArrayBE[32]): UInt256 {.noSideEffect, inline.}=
## Convert a big-endian array of Bytes to an UInt256 (in native host endianness)
const N = 32
for i in 0 ..< N:
{.unroll: 4.}
result = result shl 8 or ba[i].u256
proc toByteArrayBE*(num: UInt256): ByteArrayBE[32] {.noSideEffect, noInit.}=
proc toByteArrayBE*(num: UInt256): ByteArrayBE[32] {.noSideEffect, noInit, inline.}=
## Convert an UInt256 (in native host endianness) to a big-endian byte array
const N = 32
for i in 0 ..< N:
{.unroll: 4.}
result[i] = byte getUInt(num shr uint((N-1-i) * 8))
proc readHexChar(c: char): byte {.noSideEffect.}=