From 1653a8815fc89c7c89b28aab405d41ba43494797 Mon Sep 17 00:00:00 2001 From: Mamy Ratsimbazafy Date: Tue, 8 May 2018 11:46:21 +0200 Subject: [PATCH 1/2] Restrict pragma: use C, C++, VCC + GCC + LLVM compatible syntax (#41) --- src/io.nim | 2 +- src/private/as_signed_words.nim | 4 ++-- src/private/as_words.nim | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/io.nim b/src/io.nim index dbc3cd3..2ee967b 100644 --- a/src/io.nim +++ b/src/io.nim @@ -262,7 +262,7 @@ func dumpHex*(x: Stint or StUint, order: static[Endianness]): string = hexChars = "0123456789abcdef" size = getSize(x.data) div 8 - {.pragma: restrict, codegenDecl: "$# __restrict__ $#".} + {.pragma: restrict, codegenDecl: "$# __restrict $#".} let bytes {.restrict.}= cast[ptr array[size, byte]](x.unsafeaddr) result = newString(2*size) diff --git a/src/private/as_signed_words.nim b/src/private/as_signed_words.nim index bad0062..750ca12 100644 --- a/src/private/as_signed_words.nim +++ b/src/private/as_signed_words.nim @@ -133,7 +133,7 @@ macro asSignedWordsZip*[T]( # if we have multiple iterations to do if system.cpuEndian == bigEndian: result = quote do: - {.pragma: restrict, codegenDecl: "$# __restrict__ $#".} + {.pragma: restrict, codegenDecl: "$# __restrict $#".} let `next_x`{.restrict.} = cast[ptr `optim_type`](`x`.unsafeaddr) `next_y`{.restrict.} = cast[ptr `optim_type`](`y`.unsafeaddr) @@ -143,7 +143,7 @@ macro asSignedWordsZip*[T]( else: # Little-Endian, iteration in reverse result = quote do: - {.pragma: restrict, codegenDecl: "$# __restrict__ $#".} + {.pragma: restrict, codegenDecl: "$# __restrict $#".} let `next_x`{.restrict.} = cast[ptr `optim_type`](`x`.unsafeaddr) `next_y`{.restrict.} = cast[ptr `optim_type`](`y`.unsafeaddr) diff --git a/src/private/as_words.nim b/src/private/as_words.nim index bd0da61..e6f5249 100644 --- a/src/private/as_words.nim +++ b/src/private/as_words.nim @@ -159,7 +159,7 @@ macro asWordsZip*(x, y: UintImpl or IntImpl, ignoreEndianness: static[bool], loo else: if ignoreEndianness or system.cpuEndian == bigEndian: result = quote do: - {.pragma: restrict, codegenDecl: "$# __restrict__ $#".} + {.pragma: restrict, codegenDecl: "$# __restrict $#".} let `inner_x`{.restrict.} = cast[ptr `optim_type`](`x`.unsafeaddr) `inner_y`{.restrict.} = cast[ptr `optim_type`](`y`.unsafeaddr) @@ -168,7 +168,7 @@ macro asWordsZip*(x, y: UintImpl or IntImpl, ignoreEndianness: static[bool], loo else: # Little-Endian, iteration in reverse result = quote do: - {.pragma: restrict, codegenDecl: "$# __restrict__ $#".} + {.pragma: restrict, codegenDecl: "$# __restrict $#".} let `inner_x`{.restrict.} = cast[ptr `optim_type`](`x`.unsafeaddr) `inner_y`{.restrict.} = cast[ptr `optim_type`](`y`.unsafeaddr) @@ -224,7 +224,7 @@ macro m_asWordsZip*[T: UintImpl or IntImpl](m: var T, x: T, else: if ignoreEndianness or system.cpuEndian == bigEndian: result = quote do: - {.pragma: restrict, codegenDecl: "$# __restrict__ $#".} + {.pragma: restrict, codegenDecl: "$# __restrict $#".} let `inner_m`{.restrict.} = cast[ptr `optim_type`](`m`.addr) `inner_x`{.restrict.} = cast[ptr `optim_type`](`x`.unsafeaddr) @@ -233,7 +233,7 @@ macro m_asWordsZip*[T: UintImpl or IntImpl](m: var T, x: T, else: # Little-Endian, iteration in reverse result = quote do: - {.pragma: restrict, codegenDecl: "$# __restrict__ $#".} + {.pragma: restrict, codegenDecl: "$# __restrict $#".} let `inner_m`{.restrict.} = cast[ptr `optim_type`](`m`.addr) `inner_x`{.restrict.} = cast[ptr `optim_type`](`x`.unsafeaddr) @@ -298,7 +298,7 @@ macro m_asWordsZip*[T: UintImpl or IntImpl](m: var T, x, y: T, else: if ignoreEndianness or system.cpuEndian == bigEndian: result = quote do: - {.pragma: restrict, codegenDecl: "$# __restrict__ $#".} + {.pragma: restrict, codegenDecl: "$# __restrict $#".} let `inner_m`{.restrict.} = cast[ptr `optim_type`](`m`.addr) `inner_x`{.restrict.} = cast[ptr `optim_type`](`x`.unsafeaddr) @@ -308,7 +308,7 @@ macro m_asWordsZip*[T: UintImpl or IntImpl](m: var T, x, y: T, else: # Little-Endian, iteration in reverse result = quote do: - {.pragma: restrict, codegenDecl: "$# __restrict__ $#".} + {.pragma: restrict, codegenDecl: "$# __restrict $#".} let `inner_m`{.restrict.} = cast[ptr `optim_type`](`m`.addr) `inner_x`{.restrict.} = cast[ptr `optim_type`](`x`.unsafeaddr) From adb75b32b87975d5bdbf7540d5d4eb719a81482d Mon Sep 17 00:00:00 2001 From: Mamy Ratsimbazafy Date: Tue, 8 May 2018 13:21:04 +0200 Subject: [PATCH 2/2] [IO]: Add readUintBE, hexToUint, toByteArrayBE * Add readUintBE and toByteArrayBE proc * Add readUintBE, hexToUint, to ByteArrayBE and reorder parse arguments + tests --- src/io.nim | 49 ++++++++++++++++++-- src/stint.nim | 4 +- tests/test_io.nim | 113 ++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 152 insertions(+), 14 deletions(-) diff --git a/src/io.nim b/src/io.nim index 2ee967b..f90bbf5 100644 --- a/src/io.nim +++ b/src/io.nim @@ -117,7 +117,7 @@ func unsafeConv[bits: static[int]](n: range[0..16], T: typedesc[Stint[bits]|Stui else: r_ptr[r_ptr[].len - 1] = n.byte -func parse*[bits: static[int]](T: typedesc[Stuint[bits]], input: string, base: static[int]): T = +func parse*[bits: static[int]](input: string, T: typedesc[Stuint[bits]], base: static[int]): T = ## Parse a string and store the result in a Stint[bits] or Stuint[bits]. static: assert (base >= 2) and base <= 16, "Only base from 2..16 are supported" @@ -138,7 +138,7 @@ func parse*[bits: static[int]](T: typedesc[Stuint[bits]], input: string, base: s result = result * radix + input[curr].readHexChar.unsafeConv(T) nextNonBlank(curr, input) -func parse*[bits: static[int]](T: typedesc[Stint[bits]], input: string, base: static[int]): T = +func parse*[bits: static[int]](input: string, T: typedesc[Stint[bits]], base: static[int]): T = ## Parse a string and store the result in a Stint[bits] or Stuint[bits]. static: assert (base >= 2) and base <= 16, "Only base from 2..16 are supported" @@ -176,12 +176,16 @@ func parse*[bits: static[int]](T: typedesc[Stint[bits]], input: string, base: st else: result = cast[Stint[bits]](no_overflow) -func parse*[bits: static[int]](T: typedesc[Stint[bits]|Stuint[bits]], input: string): T {.inline.}= +func parse*[bits: static[int]](input: string, T: typedesc[Stint[bits]|Stuint[bits]]): T {.inline.}= ## Parse a string and store the result in a Stint[bits] or Stuint[bits]. ## Input is considered a decimal string. # TODO: Have a default static argument in the previous proc. Currently we get # "Cannot evaluate at compile-time" in several places (2018-04-26). - parse(T, input, 10) + parse(input, T, 10) + +func hexToUint*[bits: static[int]](hexString: string): Stuint[bits] {.inline.} = + ## Convert an hex string to the corresponding unsigned integer + parse(hexString, type result, base = 16) func toString*[bits: static[int]](num: StUint[bits], base: static[int]): string = ## Convert a Stint or Stuint to string. @@ -281,3 +285,40 @@ func dumpHex*(x: Stint or StUint): string {.inline.}= dumpHex(x, bigEndian) # TODO: Have a default static argument in the previous proc. Currently we get # "Cannot evaluate at compile-time". + +func readUintBE*[bits: static[int]](ba: openarray[byte]): Stuint[bits] = + ## Convert a big-endian array of (bits div 8) Bytes to an UInt[bits] (in native host endianness) + ## Input: + ## - a big-endian openarray of size (bits div 8) at least + ## Returns: + ## - A unsigned integer of the same size with `bits` bits + ## + ## ⚠ If the openarray length is bigger than bits div 8, part converted is undefined behaviour. + + const N = bits div 8 + assert(ba.len == N) + + let r_ptr = cast[ptr array[N, byte]](result.addr) + for i, val in ba: + when system.cpuEndian == bigEndian: + # TODO: due to https://github.com/status-im/nim-stint/issues/38 + # We can't cast a stack byte array to stuint with a convenient proc signature. + r_ptr[i] = val + else: + r_ptr[N-1 - i] = val + +func toByteArrayBE*[bits: static[int]](n: StUint[bits]): array[bits div 8, byte] = + ## Convert a uint[bits] to to a big-endian array of bits div 8 bytes + ## Input: + ## - an unsigned integer + ## Returns: + ## - a big-endian array of the same size + + const N = bits div 8 + + when system.cpuEndian == bigEndian: + result = cast[type result](n) + else: + let n_ptr = cast[ptr array[N, byte]](n.unsafeAddr) + for i in 0 ..< N: + result[N-1 - i] = n_ptr[i] diff --git a/src/stint.nim b/src/stint.nim index 0e3c55d..d201ed5 100644 --- a/src/stint.nim +++ b/src/stint.nim @@ -20,7 +20,7 @@ template make_conv(conv_name: untyped, size: int): untyped = func `convname`*(n: SomeInteger): StUint[size] {.inline.}= n.stuint(size) func `convname`*(input: string): StUint[size] {.inline.}= - parse(Stuint[size], input) + input.parse(Stuint[size]) make_conv(u128, 128) make_conv(u256, 256) @@ -29,7 +29,7 @@ template make_conv(conv_name: untyped, size: int): untyped = func `convname`*(n: SomeInteger): Stint[size] {.inline.}= n.stint(size) func `convname`*(input: string): Stint[size] {.inline.}= - parse(Stint[size], input) + input.parse(Stint[size]) make_conv(i128, 128) make_conv(i256, 256) diff --git a/tests/test_io.nim b/tests/test_io.nim index a33d050..1975c91 100644 --- a/tests/test_io.nim +++ b/tests/test_io.nim @@ -12,21 +12,21 @@ import ../src/stint, unittest, strutils suite "Testing input and output procedures": test "Creation from decimal strings": block: - let a = parse(Stint[64], "123456789") + let a = "123456789".parse(Stint[64]) let b = 123456789.stint(64) check: a == b check: 123456789'i64 == cast[int64](a) block: - let a = parse(Stuint[64], "123456789") + let a = "123456789".parse(Stuint[64]) let b = 123456789.stuint(64) check: a == b check: 123456789'u64 == cast[uint64](a) block: - let a = parse(Stint[64], "-123456789") + let a = "-123456789".parse(Stint[64]) let b = (-123456789).stint(64) check: a == b @@ -34,21 +34,24 @@ suite "Testing input and output procedures": test "Creation from hex strings": block: - let a = parse(Stint[64], "0xFF", 16) + let a = "0xFF".parse(Stint[64], base = 16) let b = 255.stint(64) check: a == b check: 255'i64 == cast[int64](a) block: - let a = parse(Stuint[64], "0xFF", 16) + let a = "0xFF".parse(Stuint[64], base = 16) let b = 255.stuint(64) check: a == b check: 255'u64 == cast[uint64](a) + let a2 = hexToUint[64]("0xFF") + check: a == a2 + block: - let a = parse(Stint[16], "0xFFFF", 16) + let a = "0xFFFF".parse(Stint[16], 16) let b = (-1'i16).stint(16) check: a == b @@ -90,10 +93,104 @@ suite "Testing input and output procedures": test "Back and forth bigint conversion consistency": block: let s = "1234567890123456789012345678901234567890123456789" - let a = parse(StInt[512], s) + let a = parse(s, StInt[512]) check: a.toString == s block: let s = "1234567890123456789012345678901234567890123456789" - let a = parse(StUInt[512], s) + let a = parse(s, StUInt[512]) check: a.toString == s + +suite "Testing conversion functions: Hex, Bytes, Endianness using secp256k1 curve": + + let + SECPK1_N_HEX = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141".toLowerAscii + SECPK1_N = "115792089237316195423570985008687907852837564279074904382605163141518161494337".u256 + SECPK1_N_BYTES = [byte(255), 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 186, 174, 220, 230, 175, 72, 160, 59, 191, 210, 94, 140, 208, 54, 65, 65] + + test "hex -> uint256": + check: SECPK1_N_HEX.parse(Stuint[256], base = 16) == SECPK1_N + + test "uint256 -> hex": + check: SECPK1_N.dumpHex == SECPK1_N_HEX + + test "hex -> big-endian array -> uint256": + check: readUintBE[256](SECPK1_N_BYTES) == SECPK1_N + + test "uint256 -> big-endian array -> hex": + check: SECPK1_N.toByteArrayBE == SECPK1_N_BYTES + + # 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*: string + pubkey*: string + raw_sig*: tuple[v: int, r, s: string] + serialized_sig*: string + + let + alice = testKeySig( + privkey: "9c0257114eb9399a2985f8e75dad7600c5d89fe3824ffa99ec1c3eb8bf3b0501", + pubkey: "5eed5fa3a67696c334762bb4823e585e2ee579aba3558d9955296d6c04541b426078dbd48d74af1fd0c72aa1a05147cf17be6b60bdbed6ba19b08ec28445b0ca", + raw_sig: ( + v: 1, + r: "B20E2EA5D3CBAA83C1E0372F110CF12535648613B479B64C1A8C1A20C5021F38", # Decimal "80536744857756143861726945576089915884233437828013729338039544043241440681784", + s: "0434D07EC5795E3F789794351658E80B7FAF47A46328F41E019D7B853745CDFD" # Decimal "1902566422691403459035240420865094128779958320521066670269403689808757640701" + ), + serialized_sig: "b20e2ea5d3cbaa83c1e0372f110cf12535648613b479b64c1a8c1a20c5021f380434d07ec5795e3f789794351658e80b7faf47a46328f41e019d7b853745cdfd01" + ) + + bob = testKeySig( + privkey: "38e47a7b719dce63662aeaf43440326f551b8a7ee198cee35cb5d517f2d296a2", + pubkey: "347746ccb908e583927285fa4bd202f08e2f82f09c920233d89c47c79e48f937d049130e3d1c14cf7b21afefc057f71da73dec8e8ff74ff47dc6a574ccd5d570", + raw_sig: ( + v: 1, + r: "5C48EA4F0F2257FA23BD25E6FCB0B75BBE2FF9BBDA0167118DAB2BB6E31BA76E", # Decimal "41741612198399299636429810387160790514780876799439767175315078161978521003886", + s: "691DBDAF2A231FC9958CD8EDD99507121F8184042E075CF10F98BA88ABFF1F36" # Decimal "47545396818609319588074484786899049290652725314938191835667190243225814114102" + ), + serialized_sig: "5c48ea4f0f2257fa23bd25e6fcb0b75bbe2ff9bbda0167118dab2bb6e31ba76e691dbdaf2a231fc9958cd8edd99507121f8184042e075cf10f98ba88abff1f3601" + ) + + eve = testKeySig( + privkey: "876be0999ed9b7fc26f1b270903ef7b0c35291f89407903270fea611c85f515c", + pubkey: "c06641f0d04f64dba13eac9e52999f2d10a1ff0ca68975716b6583dee0318d91e7c2aed363ed22edeba2215b03f6237184833fd7d4ad65f75c2c1d5ea0abecc0", + raw_sig: ( + v: 0, + r: "BABEEFC5082D3CA2E0BC80532AB38F9CFB196FB9977401B2F6A98061F15ED603", # Decimal "84467545608142925331782333363288012579669270632210954476013542647119929595395", + s: "603D0AF084BF906B2CDF6CDDE8B2E1C3E51A41AF5E9ADEC7F3643B3F1AA2AADF" # Decimal "43529886636775750164425297556346136250671451061152161143648812009114516499167" + ), + serialized_sig: "babeefc5082d3ca2e0bc80532ab38f9cfb196fb9977401b2f6a98061f15ed603603d0af084bf906b2cdf6cdde8b2e1c3e51a41af5e9adec7f3643b3f1aa2aadf00" + ) + + test "Alice signature": + check: alice.raw_sig.r.parse(Stuint[256], 16) == "80536744857756143861726945576089915884233437828013729338039544043241440681784".u256 + check: alice.raw_sig.s.parse(Stuint[256], 16) == "1902566422691403459035240420865094128779958320521066670269403689808757640701".u256 + + test "Bob signature": + check: bob.raw_sig.r.parse(Stuint[256], 16) == "41741612198399299636429810387160790514780876799439767175315078161978521003886".u256 + check: bob.raw_sig.s.parse(Stuint[256], 16) == "47545396818609319588074484786899049290652725314938191835667190243225814114102".u256 + + test "Eve signature": + check: eve.raw_sig.r.parse(Stuint[256], 16) == "84467545608142925331782333363288012579669270632210954476013542647119929595395".u256 + check: eve.raw_sig.s.parse(Stuint[256], 16) == "43529886636775750164425297556346136250671451061152161143648812009114516499167".u256