mirror of
https://github.com/status-im/nim-eth-keys.git
synced 2025-02-17 11:46:40 +00:00
Merge pull request #10 from status-im/Private-fields-remove-ttmath-for-secp256k1-backend
[WIP] Private fields + remove ttmath for secp256k1 backend
This commit is contained in:
commit
34732e8440
@ -9,7 +9,7 @@ srcDir = "src"
|
||||
|
||||
requires "nim >= 0.18.0", "keccak_tiny >= 0.1.0", "ttmath >= 0.1.0", "nimSHA2", "secp256k1"
|
||||
|
||||
proc test(name: string, lang: string = "cpp") =
|
||||
proc test(name: string, lang: string = "c") =
|
||||
if not dirExists "build":
|
||||
mkDir "build"
|
||||
if not dirExists "nimcache":
|
||||
@ -19,9 +19,17 @@ proc test(name: string, lang: string = "cpp") =
|
||||
switch("out", ("./build/" & name))
|
||||
setCommand lang, "tests/" & name & ".nim"
|
||||
|
||||
task test, "Run all tests - libsecp256k1 backend":
|
||||
task test_c, "Run all tests - C only & libsecp256k1 backend":
|
||||
test "all_tests"
|
||||
|
||||
task test_cpp, "Run all tests - C++ only & libsecp256k1 backend":
|
||||
test "all_tests", "cpp"
|
||||
|
||||
task test, "Run all tests - C and C++ & libsecp256k1 backend":
|
||||
exec "nimble test_c"
|
||||
exec "rm ./nimcache/*"
|
||||
exec "nimble test_cpp"
|
||||
|
||||
task test_backend_native, "Run all tests - pure Nim backend":
|
||||
switch("define", "backend_native")
|
||||
test "all_tests"
|
||||
test "all_tests", "cpp"
|
||||
|
@ -7,7 +7,7 @@
|
||||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ../datatypes
|
||||
import ../datatypes, ../private/conversion_bytes
|
||||
import secp256k1, keccak_tiny
|
||||
|
||||
const SECP256K1_CONTEXT_ALL = SECP256K1_CONTEXT_VERIFY or SECP256K1_CONTEXT_SIGN
|
||||
@ -20,13 +20,13 @@ proc `=destroy`(ctx: ptr secp256k1_context) =
|
||||
ctx.secp256k1_context_destroy
|
||||
|
||||
type
|
||||
Serialized_PubKey = ByteArrayBE[65]
|
||||
Serialized_PubKey = array[65, byte]
|
||||
|
||||
proc asPtrPubKey(key: PublicKey): ptr secp256k1_pubkey =
|
||||
cast[ptr secp256k1_pubkey](unsafeAddr key.raw_key)
|
||||
cast[ptr secp256k1_pubkey](unsafeAddr key)
|
||||
|
||||
proc asPtrCuchar(key: PrivateKey): ptr cuchar =
|
||||
cast[ptr cuchar](unsafeAddr key.raw_key)
|
||||
cast[ptr cuchar](unsafeAddr key)
|
||||
|
||||
proc asPtrCuchar(key: Serialized_PubKey): ptr cuchar =
|
||||
cast[ptr cuchar](unsafeAddr key)
|
||||
|
105
src/datatypes.md
Normal file
105
src/datatypes.md
Normal file
@ -0,0 +1,105 @@
|
||||
|
||||
# Secp256k1 Implementation details (MIT License)
|
||||
|
||||
Details on the secp256k1 implementation.
|
||||
The Nim datatype is:
|
||||
|
||||
```Nim
|
||||
type
|
||||
Scalar256 = distinct array[32, byte]
|
||||
# Secp256k1 makes the signature an opaque "implementation dependant". See details in datatypes.md
|
||||
# We hide the information too as the native backend might choose a different r,s representation.
|
||||
|
||||
Signature* {.packed.}= object
|
||||
Fr*: Scalar256
|
||||
Fs*: Scalar256
|
||||
Fv*: range[0.byte .. 1.byte] # This should be 27..28 as per Ethereum but it's 0..1 in eth-keys ...
|
||||
```
|
||||
|
||||
## Conversion to and from uint256 <-> byte array
|
||||
https://github.com/bitcoin-core/secp256k1/blob/0b7024185045a49a1a6a4c5615bf31c94f63d9c4/src/scalar_low_impl.h#L47-L61
|
||||
|
||||
```C
|
||||
static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) {
|
||||
const int base = 0x100 % EXHAUSTIVE_TEST_ORDER;
|
||||
int i;
|
||||
*r = 0;
|
||||
for (i = 0; i < 32; i++) {
|
||||
*r = ((*r * base) + b32[i]) % EXHAUSTIVE_TEST_ORDER;
|
||||
}
|
||||
/* just deny overflow, it basically always happens */
|
||||
if (overflow) *overflow = 0;
|
||||
}
|
||||
|
||||
static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a) {
|
||||
memset(bin, 0, 32);
|
||||
bin[28] = *a >> 24; bin[29] = *a >> 16; bin[30] = *a >> 8; bin[31] = *a;
|
||||
}
|
||||
```
|
||||
|
||||
## Loading, saving, parsing, serializing the signature
|
||||
https://github.com/bitcoin-core/secp256k1/blob/0b7024185045a49a1a6a4c5615bf31c94f63d9c4/src/modules/recovery/main_impl.h#L12-L72
|
||||
|
||||
```C
|
||||
static void secp256k1_ecdsa_recoverable_signature_load(const secp256k1_context* ctx, secp256k1_scalar* r, secp256k1_scalar* s, int* recid, const secp256k1_ecdsa_recoverable_signature* sig) {
|
||||
(void)ctx;
|
||||
if (sizeof(secp256k1_scalar) == 32) {
|
||||
/* When the secp256k1_scalar type is exactly 32 byte, use its
|
||||
* representation inside secp256k1_ecdsa_signature, as conversion is very fast.
|
||||
* Note that secp256k1_ecdsa_signature_save must use the same representation. */
|
||||
memcpy(r, &sig->data[0], 32);
|
||||
memcpy(s, &sig->data[32], 32);
|
||||
} else {
|
||||
secp256k1_scalar_set_b32(r, &sig->data[0], NULL);
|
||||
secp256k1_scalar_set_b32(s, &sig->data[32], NULL);
|
||||
}
|
||||
*recid = sig->data[64];
|
||||
}
|
||||
|
||||
static void secp256k1_ecdsa_recoverable_signature_save(secp256k1_ecdsa_recoverable_signature* sig, const secp256k1_scalar* r, const secp256k1_scalar* s, int recid) {
|
||||
if (sizeof(secp256k1_scalar) == 32) {
|
||||
memcpy(&sig->data[0], r, 32);
|
||||
memcpy(&sig->data[32], s, 32);
|
||||
} else {
|
||||
secp256k1_scalar_get_b32(&sig->data[0], r);
|
||||
secp256k1_scalar_get_b32(&sig->data[32], s);
|
||||
}
|
||||
sig->data[64] = recid;
|
||||
}
|
||||
|
||||
int secp256k1_ecdsa_recoverable_signature_parse_compact(const secp256k1_context* ctx, secp256k1_ecdsa_recoverable_signature* sig, const unsigned char *input64, int recid) {
|
||||
secp256k1_scalar r, s;
|
||||
int ret = 1;
|
||||
int overflow = 0;
|
||||
|
||||
(void)ctx;
|
||||
ARG_CHECK(sig != NULL);
|
||||
ARG_CHECK(input64 != NULL);
|
||||
ARG_CHECK(recid >= 0 && recid <= 3);
|
||||
|
||||
secp256k1_scalar_set_b32(&r, &input64[0], &overflow);
|
||||
ret &= !overflow;
|
||||
secp256k1_scalar_set_b32(&s, &input64[32], &overflow);
|
||||
ret &= !overflow;
|
||||
if (ret) {
|
||||
secp256k1_ecdsa_recoverable_signature_save(sig, &r, &s, recid);
|
||||
} else {
|
||||
memset(sig, 0, sizeof(*sig));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int secp256k1_ecdsa_recoverable_signature_serialize_compact(const secp256k1_context* ctx, unsigned char *output64, int *recid, const secp256k1_ecdsa_recoverable_signature* sig) {
|
||||
secp256k1_scalar r, s;
|
||||
|
||||
(void)ctx;
|
||||
ARG_CHECK(output64 != NULL);
|
||||
ARG_CHECK(sig != NULL);
|
||||
ARG_CHECK(recid != NULL);
|
||||
|
||||
secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, recid, sig);
|
||||
secp256k1_scalar_get_b32(&output64[0], &r);
|
||||
secp256k1_scalar_get_b32(&output64[32], &s);
|
||||
return 1;
|
||||
}
|
||||
```
|
@ -7,22 +7,58 @@
|
||||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ./private/lowlevel_types
|
||||
import ttmath
|
||||
|
||||
export lowlevel_types, ttmath
|
||||
import ./private/conversion_bytes
|
||||
|
||||
# Note: Fields F should be private, it is intentionally ugly to directly access them
|
||||
# See private field access issue: https://github.com/nim-lang/Nim/issues/7390
|
||||
type
|
||||
PublicKey* = object
|
||||
raw_key*: ByteArrayBE[64]
|
||||
Fraw_key: array[64, byte]
|
||||
|
||||
PrivateKey* = object
|
||||
raw_key*: ByteArrayBE[32]
|
||||
public_key*: PublicKey
|
||||
Fraw_key: array[32, byte]
|
||||
Fpublic_key: PublicKey # This is exported publicly through public_key
|
||||
|
||||
BaseKey* = PrivateKey|PublicKey
|
||||
type
|
||||
Scalar256 = distinct array[32, byte]
|
||||
# Secp256k1 makes the signature an opaque "implementation dependent".
|
||||
#
|
||||
# Scalar256 is opaque/distinct too as in practice it's uint256
|
||||
# and by default we don't load any uint256 library.
|
||||
# See implementation details in datatypes.md.
|
||||
|
||||
Signature* {.packed.}= object
|
||||
r*: UInt256
|
||||
s*: UInt256
|
||||
v*: range[0.byte .. 1.byte]
|
||||
Fr: Scalar256
|
||||
Fs: Scalar256
|
||||
Fv: range[0.byte .. 1.byte] # This should be 27..28 as per Ethereum but it's 0..1 in eth-keys ...
|
||||
|
||||
# Hide the private fields and generate accessors.
|
||||
# This is needed to:
|
||||
# - Be able to not store the public_key in PrivateKey in the future and replace it by
|
||||
# an on-the-fly computation
|
||||
# - Signature: have different data representation
|
||||
|
||||
template genAccessors(name: untyped, fieldType, objType: typedesc): untyped =
|
||||
# Access
|
||||
proc name*(obj: objType): fieldType {.noSideEffect, inline, noInit.} =
|
||||
obj.`F name`
|
||||
|
||||
# Assignement
|
||||
proc `name=`*(obj: var objType, value: fieldType) {.noSideEffect, inline.} =
|
||||
obj.`F name` = value
|
||||
|
||||
# Mutable
|
||||
proc `name`*(obj: var objType): var fieldType {.noSideEffect, inline.} =
|
||||
obj.`F name`
|
||||
|
||||
genAccessors(raw_key, array[64, byte], PublicKey)
|
||||
genAccessors(raw_key, array[32, byte], PrivateKey)
|
||||
genAccessors(public_key, PublicKey, PrivateKey)
|
||||
|
||||
|
||||
## If we hide the fields we need to provide a custom `==` proc
|
||||
## Because Nim `==` template will not be able to access the fields
|
||||
|
||||
proc `==`*(x, y: Scalar256): bool {.noSideEffect, inline, borrow.}
|
||||
proc `==`*(x, y: PublicKey or PrivateKey or Signature): bool {.noSideEffect, inline.} =
|
||||
system.`==`(x, y)
|
||||
|
@ -13,7 +13,7 @@
|
||||
# Note: for now only a native pure Nim backend is supported
|
||||
# In the future alternative, proven crypto backend will be added like libsecpk1
|
||||
|
||||
import ./datatypes
|
||||
import ./datatypes, ./private/conversion_bytes
|
||||
|
||||
import keccak_tiny
|
||||
|
||||
|
@ -7,13 +7,12 @@
|
||||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ./datatypes,
|
||||
./datatypes_interface
|
||||
import ./datatypes
|
||||
export PublicKey, PrivateKey, Signature, public_key, `==`
|
||||
|
||||
import ./datatypes_interface
|
||||
export datatypes_interface
|
||||
|
||||
export datatypes,
|
||||
datatypes_interface
|
||||
|
||||
|
||||
import ttmath
|
||||
export ttmath
|
||||
when defined(backend_native):
|
||||
import ttmath
|
||||
export ttmath
|
||||
|
@ -7,7 +7,7 @@
|
||||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ttmath, strutils
|
||||
import strutils
|
||||
|
||||
# Note on endianness:
|
||||
# - UInt256 uses host endianness
|
||||
@ -19,24 +19,7 @@ import ttmath, 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]] = array[N, byte]
|
||||
## A byte array that stores bytes in big-endian order
|
||||
|
||||
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, 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.}=
|
||||
proc readHexChar*(c: char): byte {.noSideEffect.}=
|
||||
## Converts an hex char to a byte
|
||||
case c
|
||||
of '0'..'9': result = byte(ord(c) - ord('0'))
|
||||
@ -45,7 +28,7 @@ proc readHexChar(c: char): byte {.noSideEffect.}=
|
||||
else:
|
||||
raise newException(ValueError, $c & "is not a hexademical character")
|
||||
|
||||
proc skip0xPrefix(hexStr: string): int {.inline.} =
|
||||
proc skip0xPrefix*(hexStr: string): int {.inline.} =
|
||||
## Returns the index of the first meaningful char in `hexStr` by skipping
|
||||
## "0x" prefix
|
||||
if hexStr[0] == '0' and hexStr[1] in {'x', 'X'}:
|
||||
@ -68,7 +51,7 @@ proc hexToByteArrayBE*(hexStr: string, output: var openArray[byte]) {.inline.} =
|
||||
## Read a hex string and store it in a Byte Array `output` in Big-Endian order
|
||||
hexToByteArrayBE(hexStr, output, 0, output.high)
|
||||
|
||||
proc hexToByteArrayBE*[N: static[int]](hexStr: string): ByteArrayBE[N] {.noSideEffect, noInit, inline.}=
|
||||
proc hexToByteArrayBE*[N: static[int]](hexStr: string): array[N, byte] {.noSideEffect, noInit, inline.}=
|
||||
## Read an hex string and store it in a Byte Array in Big-Endian order
|
||||
hexToByteArrayBE(hexStr, result)
|
||||
|
||||
@ -83,33 +66,6 @@ proc hexToSeqByteBE*(hexStr: string): seq[byte] {.noSideEffect.}=
|
||||
result[i] = hexStr[2*i].readHexChar shl 4 or hexStr[2*i+1].readHexChar
|
||||
inc(i)
|
||||
|
||||
proc hexToUInt256*(hexStr: string): UInt256 {.noSideEffect.}=
|
||||
## Read an hex string and store it in a UInt256
|
||||
const N = 32
|
||||
|
||||
var i = skip0xPrefix(hexStr)
|
||||
|
||||
assert hexStr.len - i == 2*N
|
||||
|
||||
while i < 2*N:
|
||||
result = result shl 4 or hexStr[i].readHexChar.uint.u256
|
||||
inc(i)
|
||||
|
||||
proc toHex*(n: UInt256): string {.noSideEffect.}=
|
||||
## Convert uint256 to its hex representation
|
||||
## Output is in lowercase
|
||||
|
||||
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
|
||||
|
||||
proc toHexAux(ba: openarray[byte]): string {.noSideEffect.} =
|
||||
## Convert a byte-array to its hex representation
|
||||
## Output is in lowercase
|
||||
@ -132,7 +88,7 @@ proc toHex*(ba: openarray[byte]): string {.noSideEffect, inline.} =
|
||||
## - It is resistant against timing attack
|
||||
toHexAux(ba)
|
||||
|
||||
proc toHex*(ba: ByteArrayBE): string {.noSideEffect, inline.} =
|
||||
proc toHex*[N: static[int]](ba: array[N, byte]): string {.noSideEffect, inline.} =
|
||||
## Convert a byte-array to its hex representation
|
||||
## Output is in lowercase
|
||||
##
|
63
src/private/conversion_ttmath.nim
Normal file
63
src/private/conversion_ttmath.nim
Normal file
@ -0,0 +1,63 @@
|
||||
# Nim Eth-keys
|
||||
# Copyright (c) 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ttmath, strutils,
|
||||
conversion_bytes
|
||||
|
||||
|
||||
# Note on endianness:
|
||||
# - UInt256 uses host endianness
|
||||
# - Libsecp256k1, Ethereum EVM expect Big Endian
|
||||
# https://github.com/ethereum/evmjit/issues/91
|
||||
# - Keccak expects least-significant byte first: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf
|
||||
# Appendix B.1 p37 and outputs a hash with the same endianness as input
|
||||
# http://www.dianacoman.com/2018/02/08/eucrypt-chapter-9-byte-order-and-bit-disorder-in-keccak/
|
||||
# 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.
|
||||
|
||||
proc toByteArrayBE*(num: UInt256): array[32, byte] {.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 readUint256BE*(ba: array[32, byte]): 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 hexToUInt256*(hexStr: string): UInt256 {.noSideEffect.}=
|
||||
## Read an hex string and store it in a UInt256
|
||||
const N = 32
|
||||
|
||||
var i = skip0xPrefix(hexStr)
|
||||
|
||||
assert hexStr.len - i == 2*N
|
||||
|
||||
while i < 2*N:
|
||||
result = result shl 4 or hexStr[i].readHexChar.uint.u256
|
||||
inc(i)
|
||||
|
||||
proc toHex*(n: UInt256): string {.noSideEffect.}=
|
||||
## Convert uint256 to its hex representation
|
||||
## Output is in lowercase
|
||||
|
||||
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
|
@ -9,9 +9,15 @@
|
||||
|
||||
when defined(backend_native):
|
||||
echo "\nBackend tested: native\n"
|
||||
when defined(c):
|
||||
{.fatal: "The native backend require C++ compilation for ttmath.}".}
|
||||
else:
|
||||
echo "\nBackend tested: libsecp256k1\n"
|
||||
when not defined(cpp):
|
||||
echo "C backend chosen. Skipping ttmath_hex_bytes_conversion test"
|
||||
|
||||
import ./test_hex_bytes_conversion,
|
||||
./test_private_public_key_consistency,
|
||||
when defined(cpp):
|
||||
import ./test_ttmath_hex_bytes_conversion
|
||||
|
||||
import ./test_private_public_key_consistency,
|
||||
./test_key_and_signature_datastructures
|
||||
|
@ -42,14 +42,16 @@ let
|
||||
MSG* = "message"
|
||||
MSGHASH* = keccak256(MSG)
|
||||
|
||||
# Conversion done through https://www.mobilefish.com/services/big_number/big_number.php
|
||||
|
||||
let
|
||||
alice* = testKeySig(
|
||||
privkey: "9c0257114eb9399a2985f8e75dad7600c5d89fe3824ffa99ec1c3eb8bf3b0501",
|
||||
pubkey: "5eed5fa3a67696c334762bb4823e585e2ee579aba3558d9955296d6c04541b426078dbd48d74af1fd0c72aa1a05147cf17be6b60bdbed6ba19b08ec28445b0ca",
|
||||
raw_sig: (
|
||||
v: 1,
|
||||
r: "80536744857756143861726945576089915884233437828013729338039544043241440681784",
|
||||
s: "1902566422691403459035240420865094128779958320521066670269403689808757640701"
|
||||
r: "B20E2EA5D3CBAA83C1E0372F110CF12535648613B479B64C1A8C1A20C5021F38", # Decimal "80536744857756143861726945576089915884233437828013729338039544043241440681784",
|
||||
s: "0434D07EC5795E3F789794351658E80B7FAF47A46328F41E019D7B853745CDFD" # Decimal "1902566422691403459035240420865094128779958320521066670269403689808757640701"
|
||||
),
|
||||
serialized_sig: "b20e2ea5d3cbaa83c1e0372f110cf12535648613b479b64c1a8c1a20c5021f380434d07ec5795e3f789794351658e80b7faf47a46328f41e019d7b853745cdfd01"
|
||||
)
|
||||
@ -59,8 +61,8 @@ let
|
||||
pubkey: "347746ccb908e583927285fa4bd202f08e2f82f09c920233d89c47c79e48f937d049130e3d1c14cf7b21afefc057f71da73dec8e8ff74ff47dc6a574ccd5d570",
|
||||
raw_sig: (
|
||||
v: 1,
|
||||
r: "41741612198399299636429810387160790514780876799439767175315078161978521003886",
|
||||
s: "47545396818609319588074484786899049290652725314938191835667190243225814114102"
|
||||
r: "5C48EA4F0F2257FA23BD25E6FCB0B75BBE2FF9BBDA0167118DAB2BB6E31BA76E", # Decimal "41741612198399299636429810387160790514780876799439767175315078161978521003886",
|
||||
s: "691DBDAF2A231FC9958CD8EDD99507121F8184042E075CF10F98BA88ABFF1F36" # Decimal "47545396818609319588074484786899049290652725314938191835667190243225814114102"
|
||||
),
|
||||
serialized_sig: "5c48ea4f0f2257fa23bd25e6fcb0b75bbe2ff9bbda0167118dab2bb6e31ba76e691dbdaf2a231fc9958cd8edd99507121f8184042e075cf10f98ba88abff1f3601"
|
||||
)
|
||||
@ -70,8 +72,8 @@ let
|
||||
pubkey: "c06641f0d04f64dba13eac9e52999f2d10a1ff0ca68975716b6583dee0318d91e7c2aed363ed22edeba2215b03f6237184833fd7d4ad65f75c2c1d5ea0abecc0",
|
||||
raw_sig: (
|
||||
v: 0,
|
||||
r: "84467545608142925331782333363288012579669270632210954476013542647119929595395",
|
||||
s: "43529886636775750164425297556346136250671451061152161143648812009114516499167"
|
||||
r: "BABEEFC5082D3CA2E0BC80532AB38F9CFB196FB9977401B2F6A98061F15ED603", # Decimal "84467545608142925331782333363288012579669270632210954476013542647119929595395",
|
||||
s: "603D0AF084BF906B2CDF6CDDE8B2E1C3E51A41AF5E9ADEC7F3643B3F1AA2AADF" # Decimal "43529886636775750164425297556346136250671451061152161143648812009114516499167"
|
||||
),
|
||||
serialized_sig: "babeefc5082d3ca2e0bc80532ab38f9cfb196fb9977401b2f6a98061f15ed603603d0af084bf906b2cdf6cdde8b2e1c3e51a41af5e9adec7f3643b3f1aa2aadf00"
|
||||
)
|
||||
|
@ -1,29 +0,0 @@
|
||||
# Nim Eth-keys
|
||||
# Copyright (c) 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ../src/private/lowlevel_types
|
||||
import unittest, ttmath, strutils
|
||||
|
||||
|
||||
suite "Testing conversion functions: Hex, Bytes, Endianness":
|
||||
let
|
||||
SECPK1_N_HEX = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141".toLowerAscii
|
||||
SECPK1_N = "115792089237316195423570985008687907852837564279074904382605163141518161494337".u256
|
||||
|
||||
test "hex -> uint256":
|
||||
check: SECPK1_N_HEX.hexToUInt256 == SECPK1_N
|
||||
|
||||
test "uint256 -> hex":
|
||||
check: SECPK1_N.toHex == SECPK1_N_HEX
|
||||
|
||||
test "hex -> big-endian array -> uint256":
|
||||
check: hexToByteArrayBE[32](SECPK1_N_HEX).readUint256BE == SECPK1_N
|
||||
|
||||
test "uint256 -> big-endian array -> hex":
|
||||
check: SECPK1_N.toByteArrayBE.toHex == SECPK1_N_HEX
|
@ -7,22 +7,12 @@
|
||||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ../src/eth_keys,
|
||||
import ../src/eth_keys, ../src/private/conversion_bytes,
|
||||
./config
|
||||
|
||||
import unittest
|
||||
|
||||
suite "Test key and signature data structure":
|
||||
test "Signing from private key object":
|
||||
for person in [alice, bob, eve]:
|
||||
let
|
||||
pk = initPrivateKey(person.privkey)
|
||||
signature = pk.sign_msg(MSG)
|
||||
|
||||
check:
|
||||
signature.v == person.raw_sig.v
|
||||
signature.r == person.raw_sig.r.u256
|
||||
signature.s == person.raw_sig.s.u256
|
||||
|
||||
test "Signing from private key object (ported from official eth-keys)":
|
||||
for person in [alice, bob, eve]:
|
||||
@ -32,17 +22,6 @@ suite "Test key and signature data structure":
|
||||
|
||||
check: verify_msg(pk.public_key, MSG, signature)
|
||||
|
||||
test "Hash signing from private key object":
|
||||
for person in [alice, bob, eve]:
|
||||
let
|
||||
pk = initPrivateKey(person.privkey)
|
||||
signature = pk.sign_msg(MSG)
|
||||
|
||||
check:
|
||||
signature.v == person.raw_sig.v
|
||||
signature.r == person.raw_sig.r.u256
|
||||
signature.s == person.raw_sig.s.u256
|
||||
|
||||
test "Hash signing from private key object (ported from official eth-keys)":
|
||||
for person in [alice, bob, eve]:
|
||||
let
|
||||
|
@ -19,4 +19,4 @@ suite "Testing private -> public key conversion":
|
||||
|
||||
let computed_pubkey = $privkey.public_key
|
||||
|
||||
check: computed_pubkey == person.pubkey
|
||||
check: computed_pubkey == person.pubkey
|
||||
|
46
tests/test_ttmath_hex_bytes_conversion.nim
Normal file
46
tests/test_ttmath_hex_bytes_conversion.nim
Normal file
@ -0,0 +1,46 @@
|
||||
# Nim Eth-keys
|
||||
# Copyright (c) 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ../src/private/[conversion_bytes, conversion_ttmath]
|
||||
import unittest, ttmath, strutils # TODO remove ttmath needs if backend libsecp256k1
|
||||
|
||||
import ./config
|
||||
|
||||
suite "Testing conversion functions: Hex, Bytes, Endianness":
|
||||
let
|
||||
SECPK1_N_HEX = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141".toLowerAscii
|
||||
SECPK1_N = "115792089237316195423570985008687907852837564279074904382605163141518161494337".u256
|
||||
|
||||
test "hex -> uint256":
|
||||
check: SECPK1_N_HEX.hexToUInt256 == SECPK1_N
|
||||
|
||||
test "uint256 -> hex":
|
||||
check: SECPK1_N.toHex == SECPK1_N_HEX
|
||||
|
||||
test "hex -> big-endian array -> uint256":
|
||||
check: hexToByteArrayBE[32](SECPK1_N_HEX).readUint256BE == SECPK1_N
|
||||
|
||||
test "uint256 -> big-endian array -> hex":
|
||||
check: SECPK1_N.toByteArrayBE.toHex == SECPK1_N_HEX
|
||||
|
||||
suite "Confirming consistency: hex vs decimal conversion":
|
||||
# Conversion done through https://www.mobilefish.com/services/big_number/big_number.php
|
||||
|
||||
test "Alice signature":
|
||||
check: alice.raw_sig.r.hexToUInt256 == "80536744857756143861726945576089915884233437828013729338039544043241440681784".u256
|
||||
check: alice.raw_sig.s.hexToUInt256 == "1902566422691403459035240420865094128779958320521066670269403689808757640701".u256
|
||||
|
||||
test "Bob signature":
|
||||
check: bob.raw_sig.r.hexToUInt256 == "41741612198399299636429810387160790514780876799439767175315078161978521003886".u256
|
||||
check: bob.raw_sig.s.hexToUInt256 == "47545396818609319588074484786899049290652725314938191835667190243225814114102".u256
|
||||
|
||||
test "Eve signature":
|
||||
check: eve.raw_sig.r.hexToUInt256 == "84467545608142925331782333363288012579669270632210954476013542647119929595395".u256
|
||||
check: eve.raw_sig.s.hexToUInt256 == "43529886636775750164425297556346136250671451061152161143648812009114516499167".u256
|
||||
|
Loading…
x
Reference in New Issue
Block a user