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:
Yuriy Glukhov 2018-03-21 23:14:20 +02:00 committed by GitHub
commit 34732e8440
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 307 additions and 136 deletions

View File

@ -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"

View File

@ -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
View 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;
}
```

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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
##

View 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

View File

@ -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

View File

@ -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"
)

View File

@ -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

View File

@ -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

View File

@ -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

View 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