Hide the (r,s) in signature
This commit is contained in:
commit
4454750f95
|
@ -19,12 +19,22 @@ proc test(name: string, lang: string = "c") =
|
||||||
switch("out", ("./build/" & name))
|
switch("out", ("./build/" & name))
|
||||||
setCommand lang, "tests/" & name & ".nim"
|
setCommand lang, "tests/" & name & ".nim"
|
||||||
|
|
||||||
task test, "Run all tests - C & libsecp256k1 backend":
|
task test_c, "Run all tests - C only & libsecp256k1 backend":
|
||||||
test "all_tests"
|
test "all_tests"
|
||||||
|
|
||||||
task test_cpp, "Run all tests - C++ & libsecp256k1 backend":
|
task test_cpp, "Run all tests - C++ only & libsecp256k1 backend":
|
||||||
test "all_tests", "cpp"
|
test "all_tests", "cpp"
|
||||||
|
|
||||||
|
task test, "Run all tests - C and C++ & libsecp256k1 backend":
|
||||||
|
## TODO: This only runs the C++ tests ...
|
||||||
|
# block:
|
||||||
|
# test "all_tests"
|
||||||
|
# block:
|
||||||
|
# test "all_tests", "cpp"
|
||||||
|
exec "nimble test_c"
|
||||||
|
exec "rm ./nimcache/*"
|
||||||
|
exec "nimble test_cpp"
|
||||||
|
|
||||||
task test_backend_native, "Run all tests - pure Nim backend":
|
task test_backend_native, "Run all tests - pure Nim backend":
|
||||||
switch("define", "backend_native")
|
switch("define", "backend_native")
|
||||||
test "all_tests", "cpp"
|
test "all_tests", "cpp"
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
```
|
|
@ -19,8 +19,17 @@ type
|
||||||
Fraw_key*: array[32, byte]
|
Fraw_key*: array[32, byte]
|
||||||
Fpublic_key*: PublicKey
|
Fpublic_key*: PublicKey
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
Scalar256 = array[32, byte]
|
||||||
|
# Secp256k1 makes the signature an opaque "implementation dependent".
|
||||||
|
#
|
||||||
|
# Scalar256 is opaque/distinct too as in practice, they are uint256
|
||||||
|
# and by default we don't load any.
|
||||||
|
# See implementation details in datatypes.md.
|
||||||
|
|
||||||
Signature* {.packed.}= object
|
Signature* {.packed.}= object
|
||||||
Fr*: array[32, byte]
|
Fr*: Scalar256
|
||||||
Fs*: array[32, byte]
|
Fs*: Scalar256
|
||||||
Fv*: range[0.byte .. 1.byte] # This should be 27..28 as per Ethereum but it's 0..1 in eth-keys ...
|
Fv*: range[0.byte .. 1.byte] # This should be 27..28 as per Ethereum but it's 0..1 in eth-keys ...
|
||||||
|
|
||||||
|
|
|
@ -13,16 +13,20 @@ import ../src/eth_keys, ../src/private/conversion_bytes,
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
suite "Test key and signature data structure":
|
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:
|
# TODO: For now due to needing hex <-> uint256 conversion
|
||||||
signature.Fv == person.raw_sig.v
|
# Testing r, s, v direct access is disabled
|
||||||
signature.Fr == hexToByteArrayBE[32](person.raw_sig.r)
|
#
|
||||||
signature.Fs == hexToByteArrayBE[32](person.raw_sig.s)
|
# test "Signing from private key object":
|
||||||
|
# for person in [alice, bob, eve]:
|
||||||
|
# let
|
||||||
|
# pk = initPrivateKey(person.privkey)
|
||||||
|
# signature = pk.sign_msg(MSG)
|
||||||
|
|
||||||
|
# check:
|
||||||
|
# signature.Fv == person.raw_sig.v
|
||||||
|
# signature.Fr == hexToByteArrayBE[32](person.raw_sig.r)
|
||||||
|
# signature.Fs == hexToByteArrayBE[32](person.raw_sig.s)
|
||||||
|
|
||||||
test "Signing from private key object (ported from official eth-keys)":
|
test "Signing from private key object (ported from official eth-keys)":
|
||||||
for person in [alice, bob, eve]:
|
for person in [alice, bob, eve]:
|
||||||
|
@ -32,16 +36,19 @@ suite "Test key and signature data structure":
|
||||||
|
|
||||||
check: verify_msg(pk.Fpublic_key, MSG, signature)
|
check: verify_msg(pk.Fpublic_key, MSG, signature)
|
||||||
|
|
||||||
test "Hash signing from private key object":
|
# TODO: For now due to needing hex <-> uint256 conversion
|
||||||
for person in [alice, bob, eve]:
|
# Testing r, s, v direct access is disabled
|
||||||
let
|
#
|
||||||
pk = initPrivateKey(person.privkey)
|
# test "Hash signing from private key object":
|
||||||
signature = pk.sign_msg(MSG)
|
# for person in [alice, bob, eve]:
|
||||||
|
# let
|
||||||
|
# pk = initPrivateKey(person.privkey)
|
||||||
|
# signature = pk.sign_msg(MSG)
|
||||||
|
|
||||||
check:
|
# check:
|
||||||
signature.Fv == person.raw_sig.v
|
# signature.Fv == person.raw_sig.v
|
||||||
signature.Fr == hexToByteArrayBE[32](person.raw_sig.r)
|
# signature.Fr == hexToByteArrayBE[32](person.raw_sig.r)
|
||||||
signature.Fs == hexToByteArrayBE[32](person.raw_sig.s)
|
# signature.Fs == hexToByteArrayBE[32](person.raw_sig.s)
|
||||||
|
|
||||||
test "Hash signing from private key object (ported from official eth-keys)":
|
test "Hash signing from private key object (ported from official eth-keys)":
|
||||||
for person in [alice, bob, eve]:
|
for person in [alice, bob, eve]:
|
||||||
|
|
Loading…
Reference in New Issue