[WIP] add native ECDSA backend implementation (⚠ to be debugged)

* Add jacobian primitives

* Add ECDSA algos

* Implement ECDSA, HMAC crypto (to be cleaned up)

* [WIP] test suite

* Fix arrayOfBytes <-> UInt256 casting issue

* ecdsa_raw_sign: Fix shadowing result which lead to implicit object field construction requires a .partial object

* Fix casting + remove tests covered by ranged type

* Fix toHex conversion and add first test (failing)

* Fix modular inversion for unsigned ints

* Add modulo template

* Public key generation bug still to hunt.
This commit is contained in:
Mamy Ratsimbazafy 2018-02-13 19:20:27 +01:00 committed by GitHub
parent 326e44dd17
commit 33b9df4c83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 665 additions and 5 deletions

View File

@ -5,3 +5,9 @@
A reimplementation in pure Nim of [eth-keys](https://github.com/ethereum/eth-keys), the common API for Ethereum key operations.
# Experimental
Warning ⚠: current native backend is a proof of concept, not suitable for production use:
- Future versions will use libsecp256k1 as a cryptographic backend, a proven crypto library.
DO NOT USE for production

View File

@ -1,4 +1,4 @@
packageName = "eth-keys"
packageName = "eth_keys"
version = "0.0.1"
author = "Status Research & Development GmbH"
description = "A reimplementation in pure Nim of eth-keys, the common API for Ethereum key operations."
@ -6,14 +6,17 @@ license = "MIT"
srcDir = "src"
### Dependencies
requires "nim >= 0.17.2"
requires "nim >= 0.17.2", "keccak_tiny >= 0.1.0", "ttmath >= 0.1.0", "nimSHA2"
proc test(name: string, lang: string = "c") =
proc test(name: string, lang: string = "cpp") =
if not dirExists "build":
mkDir "bin"
mkDir "build"
if not dirExists "nimcache":
mkDir "nimcache"
--run
--nimcache: "nimcache"
switch("out", ("./build/" & name))
setCommand lang, "tests/" & name & ".nim"
task test, "Run all tests":
test "all_tests"

View File

@ -0,0 +1,6 @@
# Experimental
Warning ⚠: this is a proof of concept, not suitable for production use:
- Future versions will use libsecp256k1 as a cryptographic backend, a proven crypto library.
DO NOT USE for production

View File

@ -0,0 +1,17 @@
# Copyright (c) 2018 Status Research & Development GmbH
# Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT).
# SECPK1N
import ttmath
let
# TODO: Compile-Time Evaluation of those contants
# cf: https://en.bitcoin.it/wiki/Secp256k1
SECPK1_P* = "115792089237316195423570985008687907853269984665640564039457584007908834671663".u256
SECPK1_N* = "115792089237316195423570985008687907852837564279074904382605163141518161494337".u256
SECPK1_A* = 0.u256
SECPK1_B* = 7.u256
SECPK1_Gx* = "55066263022277343669578718895168534326250603453777594175500187360389116729240".u256
SECPK1_Gy* = "32670510020758816978083085130507043184471273380659243275938904335757337482424".u256
SECPK1_G* = [SECPK1_Gx, SECPK1_Gy]

View File

@ -0,0 +1,93 @@
# 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],
./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 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
raise newException(ValueError, "Invalid private key")
result.raw_key = fast_multiply(SECPK1_G, key.raw_key)
proc ecdsa_raw_verify*(msg_hash: Hash[256], vrs: Signature, key: PublicKey): bool =
let
w = invmod(vrs.s, SECPK1_N)
z = msg_hash.toUInt256
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)
)
result = vrs.r == xy[0] and vrs.r.isOdd and vrs.s.isOdd
proc deterministic_generate_k(msg_hash: Hash[256], key: PrivateKey): UInt256 =
const
v_0 = initArray[32, byte](0x01'u8)
k_0 = initArray[32, byte](0x00'u8)
let
# TODO: avoid heap allocation
k_1 = k_0.hmac_sha256(@v_0 & @[0x00.byte] & @(toByteArray(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))
v_2 = k_2.hmac_sha256(@v_1)
kb = k_2.hmac_sha256(@v_2)
result = kb.toUInt256
proc ecdsa_raw_sign*(msg_hash: Hash[256], key: PrivateKey): Signature =
modulo(SECPK1_N):
let
z = msg_hash.toUInt256
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)
result.v = uint8 getUint `xor`(
ry[1] mod 2.u256,
if s_raw * 2.u256 < SECPK1_N: 0.u256 else: 1.u256
)
result.s = if s_raw * 2.u256 < SECPK1_N: s_raw
else: SECPK1_N - s_raw
result.r = ry[0]
proc ecdsa_raw_recover*(msg_hash: Hash[256], vrs: Signature): PublicKey {.noInit.} =
modulo(SECPK1_P):
let
x = vrs.r
xcubedaxb = x * x * x + SECPK1_A * x + SECPK1_B
beta = pow(xcubedaxb, (SECPK1_P + 1.u256) div 4.u256)
y = if vrs.v == 0 xor beta.isEven: beta # TODO: precedence rule
else: SECPK1_P - beta
# If xcubedaxb is not a quadratic residue, then r cannot be the x coord
# for a point on the curve, and so the sig is invalid
if xcubedaxb - y * y != 0.u256 or
not (vrs.r mod SECPK1_N == 1.u256) or
not (vrs.s mod SECPK1_N == 1.u256):
raise newException(ValueError, "Bad signature")
let
z = msg_hash.toUInt256
Gz = jacobian_multiply(
[SECPK1_Gx, SECPK1_Gy,1.u256],
submod(SECPK1_N, z, SECPK1_N)
)
XY = jacobian_multiply(
[SECPK1_Gx, SECPK1_Gy,1.u256],
vrs.s
)
Qr = jacobian_add(Gz, XY)
Q = jacobian_multiply(Qr, invmod(vrs.r, SECPK1_N))
result.raw_key = from_jacobian(Q)

View File

@ -0,0 +1,40 @@
# Copyright (c) 2018 Status Research & Development GmbH
# Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT).
# Nim Implementation of HMAC
# https://tools.ietf.org/html/rfc2104.html
import ../private/array_utils
import nimsha2 # TODO: For SHA-256, use OpenSSL instead? (see https://rosettacode.org/wiki/SHA-256#Nim)
proc hmac_sha256*[N: static[int]](key: array[N, byte|char],
data: string|seq[byte|char]): SHA256Digest =
# Note: due to https://github.com/nim-lang/Nim/issues/7208
# blockSize cannot be a compile-time parameter with a default value
const
opad: byte = 0x5c
ipad: byte = 0x36
blockSize = 64
var k, k_ipad{.noInit.}, k_opad{.noInit.}: array[blockSize, byte]
when N > blockSize:
k[0 ..< 32] = key.computeSHA256
else:
k[0 ..< N] = cast[array[N, byte]](key)
for i in 0 ..< blockSize:
k_ipad[i] = k[i] xor ipad
k_opad[i] = k[i] xor opad
result = computeSHA256($k_opad & $computeSHA256($k_ipad & $data))
when isMainModule:
# From https://en.wikipedia.org/wiki/Hash-based_message_authentication_code
let
key = ['k','e','y']
data = "The quick brown fox jumps over the lazy dog"
import strutils
doAssert hmac_sha256(key, data).toHex == "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8".toUpperAscii

View File

@ -0,0 +1,77 @@
# Copyright (c) 2018 Status Research & Development GmbH
# Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT).
import ./constants, ./mod_arithmetic
import ttmath
proc to_jacobian(p: array[2, UInt256]): array[3, UInt256] {.noInit.}=
[p[0], p[1], 1.u256]
proc jacobian_double(p: array[3, UInt256]): array[3, UInt256] {.noInit.}=
if p[1] == 0.u256:
return [0.u256, 0.u256, 0.u256]
modulo(SECPK1_P):
let
ysq = p[1] ** 2.u256
S = 4.u256 * p[0] * ysq
M = 3.u256 * (p[0] ** 2.u256) + SECPK1_A * (p[2] ** 4.u256)
nx = M ** 2.u256 - 2.u256 * S
ny = M * (S - nx) - 8.u256 * (ysq ** 2.u256)
nz = 2.u256 * p[1] * p[2]
result = [nx, ny, nz]
proc jacobian_add*(p, q: array[3, UInt256]): array[3, UInt256] {.noInit.}=
if p[1] == 0.u256:
return q
if q[1] == 0.u256:
return p
modulo(SECPK1_P):
let
U1 = p[0] * (q[2] ** 2.u256)
U2 = q[0] * (p[2] ** 2.u256)
S1 = p[1] * (q[2] ** 2.u256)
S2 = q[1] * (p[2] ** 2.u256)
if U1 == U2:
if S1 == S2:
return [0.u256, 0.u256, 1.u256]
return jacobian_double(p)
modulo(SECPK1_P):
let
H = U2 - U1
R = S2 - S1
H2 = H * H
H3 = H * H2
U1H2 = U1 * H2
nx = R ** 2.u256 - H3 - 2.u256 * U1H2
ny = R * (U1H2 - nx) - S1 * H3
nz = H * p[2] * q[2]
result = [nx, ny, nz]
proc from_jacobian*(p: array[3, UInt256]): array[2, UInt256] =
let z = invmod(p[2], SECPK1_P)
modulo(SECPK1_P):
result = [p[0] * (z ** 2.u256), p[1] * (z ** 3.u256)]
proc jacobian_multiply*(a: array[3, UInt256], n: UInt256): array[3, UInt256] =
if a[1] == 0.u256 or n == 0.u256:
return [0.u256, 0.u256, 1.u256]
elif n == 1.u256:
return a
elif n >= SECPK1_N: # note n cannot be < 0 in Nim
return jacobian_multiply(a, n mod SECPK1_N)
elif n.isEven:
return jacobian_double jacobian_multiply(a, n div 2.u256)
else: # n.isOdd
return jacobian_add(jacobian_double jacobian_multiply(a, n div 2.u256), a)
proc fast_multiply*(a: array[2, UInt256], n: UInt256): array[2,UInt256] =
return from_jacobian jacobian_multiply(a.to_jacobian, n)
proc fast_add*(a, b: array[2, UInt256]): array[2, UInt256] =
return from_jacobian jacobian_add(a.to_jacobian, b.to_jacobian)

View File

@ -0,0 +1,158 @@
# Copyright (c) 2018 Status Research & Development GmbH
# Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT).
import ttmath
proc isEven*(a: UInt256): bool =
(a and 1.u256) == 0.u256
proc isOdd*(a: UInt256): bool =
(a and 1.u256) != 0.u256
proc addmod*(a, b, m: UInt256): UInt256 =
## Modular addition
let a_m = if a < m: a
else: a mod m
if b == 0.u256:
return a_m
let b_m = if b < m: b
else: b mod m
# We don't do a + b to avoid overflows
# But we know that m at least is inferior to biggest UInt256
let b_from_m = m - b_m
if a_m >= b_from_m:
return a_m - b_from_m
return m - b_from_m + a_m
proc submod*(a, b, m: UInt256): UInt256 =
## Modular substraction
let a_m = if a < m: a
else: a mod m
if b == 0.u256:
return a_m
let b_m = if b < m: b
else: b mod m
# We don't do a - b to avoid overflows
if a_m >= b_m:
return a_m - b_m
return m - b_m + a_m
proc doublemod(a, m: UInt256): UInt256 {.inline.}=
## double a modulo m. assume a < m
result = a
if a >= m - a:
result -= m
result += a
proc mulmod*(a, b, m: UInt256): UInt256 =
## Modular multiplication
var a_m = if a < m: a
else: a mod m
var b_m = if b < m: b
else: b mod m
if b_m > a_m:
swap(a_m, b_m)
while b_m > 0.u256:
if b_m.isOdd:
result = addmod(result, a_m, m)
a_m = doublemod(a_m, m)
b_m = b_m shr 1
proc expmod*(base, exponent, m: UInt256): UInt256 =
## Modular exponentiation
# Formula from applied Cryptography by Bruce Schneier
# function modular_pow(base, exponent, modulus)
# result := 1
# while exponent > 0
# if (exponent mod 2 == 1):
# result := (result * base) mod modulus
# exponent := exponent >> 1
# base = (base * base) mod modulus
# return result
result = 1.u256 # (exp 0 = 1)
var e = exponent
var b = base
while e > 0.u256:
if isOdd e:
result = mulmod(result, b, m)
e = e shr 1 # e div 2
b = mulmod(b,b,m)
proc invmod*(a, m: UInt256): UInt256 =
## Modular multiplication inverse
## Input:
## - 2 positive integers a and m
## Result:
## - An integer z that solves `az ≡ 1 mod m`
# Adapted from Knuth, The Art of Computer Programming, Vol2 p342
# and Menezes, Handbook of Applied Cryptography (HAC), p610
# to avoid requiring signed integers
# http://cacr.uwaterloo.ca/hac/about/chap14.pdf
# Starting from the binary extended GCD formula (Bezout identity),
# `ax + by = gcd(x,y)`
# with input x,y and outputs a, b, gcd
# We assume a and m are coprimes, i.e. gcd is 1, otherwise no inverse
# `ax + my = 1`
# `ax + my ≡ 1 mod m`
# `ax ≡ 1 mod m``
# Meaning we can use the Extended Euclid Algorithm
# `ax + by` with
# a = a, x = result, b = m, y = 0
var
a = a
x = 1.u256
b = m
y = 0.u256
oddIter = true # instead of requiring signed int, we keep track of even/odd iterations which would be in negative
while b != 0.u256:
let
q = a div b
r = a mod b
t = x + q * y
x = y; y = t; a = b; b = r
oddIter = not oddIter
if a != 1.u256:
# a now holds the gcd(a, m) and should equal 1
raise newException(ValueError, "No modular inverse exists")
if oddIter:
return x
return m - x
template modulo*(modulus: UInt256, body: untyped): untyped =
# `+`, `*`, `**` and pow will be replaced by their modular version
template `+`(a, b: UInt256): UInt256 =
addmod(a, b, `modulus`)
template `-`(a, b: UInt256): UInt256 =
submod(a, b, `modulus`)
template `*`(a, b: UInt256): UInt256 =
mulmod(a, b, `modulus`)
template `**`(a, b: UInt256): UInt256 =
expmod(a, b, `modulus`)
template pow(a, b: UInt256): UInt256 =
expmod(a, b, `modulus`)
body
when isMainModule:
# https://www.khanacademy.org/computing/computer-science/cryptography/modarithmetic/a/fast-modular-exponentiation
assert expmod(5.u256, 117.u256, 19.u256) == 1.u256
assert expmod(3.u256, 1993.u256, 17.u256) == 14.u256
assert invmod(42.u256, 2017.u256) == 1969.u256
assert invmod(271.u256, 383.u256) == 106.u256 # Handbook of Applied Cryptography p610

19
src/datatypes.nim Normal file
View File

@ -0,0 +1,19 @@
# Copyright (c) 2018 Status Research & Development GmbH
# Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT).
import strutils, ttmath
type
PublicKey* = object
raw_key*: array[2, UInt256]
PrivateKey* = object
raw_key*: UInt256
public_key*: PublicKey
BaseKey* = PrivateKey|PublicKey
Signature* = object
v*: range[0.uint8 .. 1.uint8]
r*: UInt256
s*: UInt256

View File

@ -0,0 +1,63 @@
# Copyright (c) 2018 Status Research & Development GmbH
# Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT).
# In Nim this must be in a separate files from datatypes to avoid recursive dependencies
# between datatypes <-> ecdsa
# Note: for now only a native pure Nim backend is supported
# In the future alternative, proven crypto backend will be added like libsecpk1
import ./private/hex, ./datatypes
import keccak_tiny, ttmath
import ./backend_native/ecdsa
# ################################
# Initialization
proc initPublicKey*(hexString: string): PublicKey =
assert hexString.len == 128
result.raw_key[0] = hexToUInt256(hexString[0..<64])
result.raw_key[1] = hexToUInt256(hexString[64..<128])
proc initPrivateKey*(hexString: string): PrivateKey =
assert hexString.len == 64
result.raw_key = hexToUInt256(hexString)
result.public_key = private_key_to_public_key(result)
# ################################
# Hex
proc toHex*(key: PrivateKey): string =
result = key.raw_key.toHex
proc toHex*(key: PublicKey): string =
result = key.raw_key[0].toHex
result.add key.raw_key[1].toHex
# ################################
# Public key interface
proc recover_pubkey_from_msg_hash*(message_hash: Hash[256], sig: Signature): PublicKey {.inline.}=
ecdsa_raw_recover(message_hash, sig)
proc recover_pubkey_from_msg*(message: string, sig: Signature): PublicKey {.inline.}=
let message_hash = keccak_256(message)
result = recover_pubkey_from_msg_hash(message_hash, sig)
proc verify_msg_hash*(key: PublicKey, message_hash: Hash[256], sig: Signature): bool {.inline.}=
key == ecdsa_raw_recover(message_hash, sig)
proc verify_msg*(key: PublicKey, message: string, sig: Signature): bool {.inline.} =
let message_hash = keccak_256(message)
key == ecdsa_raw_recover(message_hash, sig)
# ################################
# Private key interface
proc sign_msg_hash*(key: PrivateKey, message_hash: Hash[256]): Signature {.inline.}=
ecdsa_raw_sign(message_hash, key)
proc sign_msg*(key: PrivateKey, message: string): Signature {.inline.} =
let message_hash = keccak_256(message)
ecdsa_raw_sign(message_hash, key)
# ################################
# Signature interface is a duplicate of the public key interface

13
src/eth_keys.nim Normal file
View File

@ -0,0 +1,13 @@
# Copyright (c) 2018 Status Research & Development GmbH
# Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT).
import ./datatypes,
./datatypes_interface
export datatypes,
datatypes_interface
import ttmath
export ttmath

View File

@ -0,0 +1,18 @@
# Copyright (c) 2018 Status Research & Development GmbH
# Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT).
import algorithm
proc initArray*[N: static[int], T](value: T): array[N, T] {.noInit.}=
result.fill(value)
proc `$`*[N:static[int]](a: array[N, byte]): string =
$(cast[array[N, char]](a))
proc `&`*[N1, N2: static[int], T](
a: array[N1, T],
b: array[N2, T]
): array[N1 + N2, T] =
## Array concatenation
result[0 ..< N1] = a
result[N1 ..< N2] = b

12
src/private/casting.nim Normal file
View File

@ -0,0 +1,12 @@
# Copyright (c) 2018 Status Research & Development GmbH
# Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT).
import ttmath, keccak_tiny, nimsha2
# We can't use Nim cast :/ to avoid copy
proc toUint256*[T: Hash[256]|array[32, byte|char]](hash: T): UInt256 =
copyMem(addr result, unsafeAddr hash, 32)
proc toByteArray*(num: UInt256): array[32, byte] =
copyMem(addr result, unsafeAddr num, 32)

48
src/private/hex.nim Normal file
View File

@ -0,0 +1,48 @@
# Copyright (c) 2018 Status Research & Development GmbH
# Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT).
import ttmath, strutils, ./casting
proc readHexChar(c: char): int =
case c
of '0'..'9': result = ord(c) - ord('0')
of 'a'..'f': result = ord(c) - ord('a') + 10
of 'A'..'F': result = ord(c) - ord('A') + 10
else:
raise newException(ValueError, $c & "is not a hexademical character")
proc hexToByteArray*[N: static[int]](hexStr: string): array[N, byte] {.noSideEffect.}=
var i = 0
if hexStr[i] == '0' and (hexStr[i+1] == 'x' or hexStr[i+1] == 'X'):
# Ignore 0x and OX
inc(i, 2)
assert hexStr.len - i == 2*N
while i < N:
result[i] = byte(readHexChar(hexStr[2*i]) shl 4 or readHexChar(hexStr[2*i+1]))
inc(i)
proc hexToUInt256*(hexStr: string): UInt256 {.noSideEffect.}=
const N = 32
var i = 0
if hexStr[i] == '0' and (hexStr[i+1] == 'x' or hexStr[i+1] == 'X'):
# Ignore 0x and OX
inc(i, 2)
assert hexStr.len - i == 2*N
while i < 2*N:
result = result shl 4 or readHexChar(hexStr[i]).uint.u256
inc(i)
proc toHex*(n: UInt256): string =
var rem = n # reminder to encode
const
N = 32 # nb of bytes in n
hexChars = "0123456789abcdef"
result = newString(2*N)
for i in countdown(2*N - 1, 0):
result[i] = hexChars[(rem and 0xF.u256).getUInt.int]
rem = rem shr 4

4
tests/all_tests.nim Normal file
View File

@ -0,0 +1,4 @@
# Copyright (c) 2018 Status Research & Development GmbH
# Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT).
import ./test_key_and_signature_datastructures

64
tests/config.nim Normal file
View File

@ -0,0 +1,64 @@
# Copyright (c) 2018 Status Research & Development GmbH
# Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT).
import ../src/eth_keys
import ttmath
# This is a sample of signatures generated with a known-good implementation of the ECDSA
# algorithm, which we use to test our ECC backends. If necessary, it can be generated from scratch
# with the following code:
#
# """python
# from devp2p import crypto
# from eth_utils import encode_hex
# msg = b'message'
# msghash = crypto.sha3(b'message')
# for secret in ['alice', 'bob', 'eve']:
# print("'{}': dict(".format(secret))
# privkey = crypto.mk_privkey(secret)
# pubkey = crypto.privtopub(privkey)
# print(" privkey='{}',".format(encode_hex(privkey)))
# print(" pubkey='{}',".format(encode_hex(crypto.privtopub(privkey))))
# ecc = crypto.ECCx(raw_privkey=privkey)
# sig = ecc.sign(msghash)
# print(" sig='{}',".format(encode_hex(sig)))
# print(" raw_sig='{}')".format(crypto._decode_sig(sig)))
# assert crypto.ecdsa_recover(msghash, sig) == pubkey
# """
type
testKeySig* = object
privkey*: PrivateKey
pubkey*: PublicKey
raw_sig*: Signature
let
alice* = testKeySig(
privkey: initPrivateKey("9c0257114eb9399a2985f8e75dad7600c5d89fe3824ffa99ec1c3eb8bf3b0501"),
pubkey: initPublicKey("5eed5fa3a67696c334762bb4823e585e2ee579aba3558d9955296d6c04541b426078dbd48d74af1fd0c72aa1a05147cf17be6b60bdbed6ba19b08ec28445b0ca"),
raw_sig: Signature(
v: 1,
r: "80536744857756143861726945576089915884233437828013729338039544043241440681784".u256,
s: "1902566422691403459035240420865094128779958320521066670269403689808757640701".u256
)
)
bob* = testKeySig(
privkey: initPrivateKey("38e47a7b719dce63662aeaf43440326f551b8a7ee198cee35cb5d517f2d296a2"),
pubkey: initPublicKey("347746ccb908e583927285fa4bd202f08e2f82f09c920233d89c47c79e48f937d049130e3d1c14cf7b21afefc057f71da73dec8e8ff74ff47dc6a574ccd5d570"),
raw_sig: Signature(
v: 1,
r: "41741612198399299636429810387160790514780876799439767175315078161978521003886".u256,
s: "47545396818609319588074484786899049290652725314938191835667190243225814114102".u256
)
)
eve* = testKeySig(
privkey: initPrivateKey("876be0999ed9b7fc26f1b270903ef7b0c35291f89407903270fea611c85f515c"),
pubkey: initPublicKey("c06641f0d04f64dba13eac9e52999f2d10a1ff0ca68975716b6583dee0318d91e7c2aed363ed22edeba2215b03f6237184833fd7d4ad65f75c2c1d5ea0abecc0"),
raw_sig: Signature(
v: 0,
r: "84467545608142925331782333363288012579669270632210954476013542647119929595395".u256,
s: "43529886636775750164425297556346136250671451061152161143648812009114516499167".u256
)
)

View File

@ -0,0 +1,19 @@
# Copyright (c) 2018 Status Research & Development GmbH
# Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT).
import ../src/eth_keys,
./config
import unittest, keccak_tiny
let
MSG = "message"
MSGHASH = keccak256(MSG)
suite "Test key and signature datastructures":
test "Signing fromprivate key object":
for person in [alice, bob, eve]:
let signature = person.privkey.sign_msg(MSG)
check: verify_msg_hash(person.privkey.public_key, MSGHASH, signature)