Merge pull request #559 from LedgerHQ/fix/apa/eip712_empty_array_handling
EIP-712 empty array handling fix
@@ -5,13 +5,17 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.4.1] - 2024-04-05
|
||||
## [0.4.1] - 2024-04-15
|
||||
|
||||
### Added
|
||||
|
||||
- Add new function `send_raw`, allowing to send a raw payload APDU
|
||||
- Add new error code definition
|
||||
|
||||
### Fixed
|
||||
|
||||
- Encoding of EIP-712 bytes elements
|
||||
|
||||
## [0.4.0] - 2024-04-03
|
||||
|
||||
### Added
|
||||
|
||||
@@ -4,7 +4,8 @@ import re
|
||||
import signal
|
||||
import sys
|
||||
import copy
|
||||
from typing import Any, Callable, Optional
|
||||
from typing import Any, Callable, Optional, Union
|
||||
import struct
|
||||
|
||||
from client import keychain
|
||||
from client.client import EthAppClient, EIP712FieldType
|
||||
@@ -118,69 +119,63 @@ def send_struct_def_field(typename, keyname):
|
||||
return (typename, type_enum, typesize, array_lvls)
|
||||
|
||||
|
||||
def encode_integer(value, typesize):
|
||||
data = bytearray()
|
||||
|
||||
def encode_integer(value: Union[str | int], typesize: int) -> bytes:
|
||||
# Some are already represented as integers in the JSON, but most as strings
|
||||
if isinstance(value, str):
|
||||
base = 10
|
||||
if value.startswith("0x"):
|
||||
base = 16
|
||||
value = int(value, base)
|
||||
value = int(value, 0)
|
||||
|
||||
if value == 0:
|
||||
data.append(0)
|
||||
data = b'\x00'
|
||||
else:
|
||||
if value < 0: # negative number, send it as unsigned
|
||||
mask = 0
|
||||
for i in range(typesize): # make a mask as big as the typesize
|
||||
mask = (mask << 8) | 0xff
|
||||
value &= mask
|
||||
while value > 0:
|
||||
data.append(value & 0xff)
|
||||
value >>= 8
|
||||
data.reverse()
|
||||
# biggest uint type accepted by struct.pack
|
||||
uint64_mask = 0xffffffffffffffff
|
||||
data = struct.pack(">QQQQ",
|
||||
(value >> 192) & uint64_mask,
|
||||
(value >> 128) & uint64_mask,
|
||||
(value >> 64) & uint64_mask,
|
||||
value & uint64_mask)
|
||||
data = data[len(data) - typesize:]
|
||||
data = data.lstrip(b'\x00')
|
||||
return data
|
||||
|
||||
|
||||
def encode_int(value, typesize):
|
||||
def encode_int(value: str, typesize: int) -> bytes:
|
||||
return encode_integer(value, typesize)
|
||||
|
||||
|
||||
def encode_uint(value, typesize):
|
||||
def encode_uint(value: str, typesize: int) -> bytes:
|
||||
return encode_integer(value, typesize)
|
||||
|
||||
|
||||
def encode_hex_string(value, size):
|
||||
data = bytearray()
|
||||
value = value[2:] # skip 0x
|
||||
byte_idx = 0
|
||||
while byte_idx < size:
|
||||
data.append(int(value[(byte_idx * 2):(byte_idx * 2 + 2)], 16))
|
||||
byte_idx += 1
|
||||
return data
|
||||
def encode_hex_string(value: str, size: int) -> bytes:
|
||||
assert value.startswith("0x")
|
||||
value = value[2:]
|
||||
if len(value) < (size * 2):
|
||||
value = value.rjust(size * 2, "0")
|
||||
assert len(value) == (size * 2)
|
||||
return bytes.fromhex(value)
|
||||
|
||||
|
||||
def encode_address(value, typesize):
|
||||
def encode_address(value: str, typesize: int) -> bytes:
|
||||
return encode_hex_string(value, 20)
|
||||
|
||||
|
||||
def encode_bool(value, typesize):
|
||||
return encode_integer(value, typesize)
|
||||
def encode_bool(value: str, typesize: int) -> bytes:
|
||||
return encode_integer(value, 1)
|
||||
|
||||
|
||||
def encode_string(value, typesize):
|
||||
def encode_string(value: str, typesize: int) -> bytes:
|
||||
data = bytearray()
|
||||
for char in value:
|
||||
data.append(ord(char))
|
||||
return data
|
||||
|
||||
|
||||
def encode_bytes_fix(value, typesize):
|
||||
def encode_bytes_fix(value: str, typesize: int) -> bytes:
|
||||
return encode_hex_string(value, typesize)
|
||||
|
||||
|
||||
def encode_bytes_dyn(value, typesize):
|
||||
def encode_bytes_dyn(value: str, typesize: int) -> bytes:
|
||||
# length of the value string
|
||||
# - the length of 0x (2)
|
||||
# / by the length of one byte in a hex string (2)
|
||||
|
||||
@@ -228,7 +228,7 @@ static bool field_hash_finalize(const void *const field_ptr,
|
||||
return false;
|
||||
}
|
||||
}
|
||||
path_advance();
|
||||
path_advance(true);
|
||||
fh->state = FHS_IDLE;
|
||||
ui_712_finalize_field();
|
||||
return true;
|
||||
|
||||
@@ -255,6 +255,7 @@ static bool array_depth_list_push(uint8_t path_idx, uint8_t size) {
|
||||
arr = &path_struct->array_depths[path_struct->array_depth_count];
|
||||
arr->path_index = path_idx;
|
||||
arr->size = size;
|
||||
arr->index = 0;
|
||||
path_struct->array_depth_count += 1;
|
||||
return true;
|
||||
}
|
||||
@@ -285,11 +286,14 @@ static bool array_depth_list_pop(void) {
|
||||
* Updates the path so that it doesn't point to a struct-type field, but rather
|
||||
* only to actual fields.
|
||||
*
|
||||
* @param[in] skip_if_array skip if path is already pointing at an array
|
||||
* @param[in] stop_at_array stop at the first downstream array
|
||||
* @return whether the path update worked or not
|
||||
*/
|
||||
static bool path_update(void) {
|
||||
static bool path_update(bool skip_if_array, bool stop_at_array) {
|
||||
uint8_t fields_count;
|
||||
const void *struct_ptr;
|
||||
const void *starting_field_ptr;
|
||||
const void *field_ptr;
|
||||
const char *typename;
|
||||
uint8_t typename_len;
|
||||
@@ -298,10 +302,20 @@ static bool path_update(void) {
|
||||
if (path_struct == NULL) {
|
||||
return false;
|
||||
}
|
||||
if ((field_ptr = get_field(NULL)) == NULL) {
|
||||
if ((starting_field_ptr = get_field(NULL)) == NULL) {
|
||||
return false;
|
||||
}
|
||||
field_ptr = starting_field_ptr;
|
||||
while (struct_field_type(field_ptr) == TYPE_CUSTOM) {
|
||||
// check if we meet one of the given conditions
|
||||
if (((field_ptr == starting_field_ptr) && skip_if_array) ||
|
||||
((field_ptr != starting_field_ptr) && stop_at_array)) {
|
||||
// only if it is the first iteration of that array depth
|
||||
if ((path_struct->array_depths[path_struct->array_depth_count - 1].index == 0) &&
|
||||
struct_field_is_array(field_ptr)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
typename = get_struct_field_typename(field_ptr, &typename_len);
|
||||
if ((struct_ptr = get_structn(typename, typename_len)) == NULL) {
|
||||
return false;
|
||||
@@ -313,11 +327,16 @@ static bool path_update(void) {
|
||||
if (push_new_hash_depth(true) == false) {
|
||||
return false;
|
||||
}
|
||||
// get the struct typehash
|
||||
if (type_hash(typename, typename_len, hash) == false) {
|
||||
return false;
|
||||
|
||||
// The only times they are both at false is if we are traversing an empty array,
|
||||
// don't do a typehash in that case
|
||||
if ((skip_if_array != false) || (stop_at_array != false)) {
|
||||
// get the struct typehash
|
||||
if (type_hash(typename, typename_len, hash) == false) {
|
||||
return false;
|
||||
}
|
||||
feed_last_hash_depth(hash);
|
||||
}
|
||||
feed_last_hash_depth(hash);
|
||||
|
||||
// TODO: Find a better way to show inner structs in verbose mode when it might be
|
||||
// an empty array of structs in which case we don't want to show it but the
|
||||
@@ -381,7 +400,7 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) {
|
||||
struct_state = DEFINED;
|
||||
|
||||
// because the first field could be a struct type
|
||||
path_update();
|
||||
path_update(true, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -449,6 +468,9 @@ bool path_new_array_depth(const uint8_t *const data, uint8_t length) {
|
||||
}
|
||||
|
||||
array_size = *data;
|
||||
if (!path_update(false, array_size > 0)) {
|
||||
return false;
|
||||
}
|
||||
array_depth_count_bak = path_struct->array_depth_count;
|
||||
for (pidx = 0; pidx < path_struct->depth_count; ++pidx) {
|
||||
if ((field_ptr = get_nth_field(NULL, pidx + 1)) == NULL) {
|
||||
@@ -492,8 +514,8 @@ bool path_new_array_depth(const uint8_t *const data, uint8_t length) {
|
||||
}
|
||||
if (array_size == 0) {
|
||||
do {
|
||||
path_advance();
|
||||
} while (path_struct->array_depth_count != array_depth_count_bak);
|
||||
path_advance(false);
|
||||
} while (path_struct->array_depth_count > array_depth_count_bak);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -546,8 +568,8 @@ static bool path_advance_in_array(void) {
|
||||
|
||||
if ((path_struct->array_depth_count > 0) &&
|
||||
(arr_depth->path_index == (path_struct->depth_count - 1))) {
|
||||
if (arr_depth->size > 0) arr_depth->size -= 1;
|
||||
if (arr_depth->size == 0) {
|
||||
arr_depth->index += 1;
|
||||
if (arr_depth->index == arr_depth->size) {
|
||||
array_depth_list_pop();
|
||||
end_reached = true;
|
||||
} else {
|
||||
@@ -563,7 +585,7 @@ static bool path_advance_in_array(void) {
|
||||
*
|
||||
* @return whether the advancement was successful or not
|
||||
*/
|
||||
bool path_advance(void) {
|
||||
bool path_advance(bool array_check) {
|
||||
bool end_reached;
|
||||
|
||||
do {
|
||||
@@ -573,8 +595,7 @@ bool path_advance(void) {
|
||||
end_reached = false;
|
||||
}
|
||||
} while (end_reached);
|
||||
path_update();
|
||||
return true;
|
||||
return path_update(array_check, array_check);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
typedef struct {
|
||||
uint8_t path_index;
|
||||
uint8_t size;
|
||||
uint8_t index;
|
||||
} s_array_depth;
|
||||
|
||||
typedef enum { ROOT_DOMAIN, ROOT_MESSAGE } e_root_type;
|
||||
@@ -27,7 +28,7 @@ typedef struct {
|
||||
|
||||
bool path_set_root(const char *const struct_name, uint8_t length);
|
||||
const void *path_get_field(void);
|
||||
bool path_advance(void);
|
||||
bool path_advance(bool array_check);
|
||||
bool path_init(void);
|
||||
void path_deinit(void);
|
||||
bool path_new_array_depth(const uint8_t *const data, uint8_t length);
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
[signature]
|
||||
v = 1b
|
||||
r = 23599abd6c4b631e42770c112b5955907fe91339f1ea1e102f7682262ca178b9
|
||||
s = 29fc94518588165114b4c4acb4d73e6d028dfb051d90e517b3b4746e04eb0f5f
|
||||
@@ -1,4 +0,0 @@
|
||||
[signature]
|
||||
v = 1c
|
||||
r = 3f084a471e6158bce792287500d62d40061acc1864180ed2da7a704bf3aced0f
|
||||
s = 3b799ced9e48cda152b4b9a4b7f45e3119dc7acdf16710a73800b4e336fa1b40
|
||||
@@ -38,10 +38,6 @@
|
||||
{ "name": "chainId", "type": "uint256" },
|
||||
{ "name": "verifyingContract", "type": "address" }
|
||||
],
|
||||
"Group": [
|
||||
{ "name": "name", "type": "string" },
|
||||
{ "name": "members", "type": "Person[]" }
|
||||
],
|
||||
"Mail": [
|
||||
{ "name": "from", "type": "Person" },
|
||||
{ "name": "to", "type": "Person[]" },
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
[signature]
|
||||
v = 1b
|
||||
r = 49dd2aa96d7494e0cd9111f19f87ac50194e4bbc61ea9f4bb86d674da0ae7721
|
||||
s = 7a12ddd9083b4caaabd2fb80df6de1d5d926c0e8a73bf371a45e231d409d79d6
|
||||
@@ -33,10 +33,6 @@
|
||||
{ "name": "chainId", "type": "uint256" },
|
||||
{ "name": "verifyingContract", "type": "address" }
|
||||
],
|
||||
"Group": [
|
||||
{ "name": "name", "type": "string" },
|
||||
{ "name": "members", "type": "Person[]" }
|
||||
],
|
||||
"Mail": [
|
||||
{ "name": "from", "type": "Person" },
|
||||
{ "name": "to", "type": "Person[]" },
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
[signature]
|
||||
v = 1c
|
||||
r = b23ffac2cb350fd6e7d06ec4b981fe016d33426d753c870e7e753797cc43bb1f
|
||||
s = 37948a656fa3403e21956ef10c8d3152f7ce22cc252d958c9f9249435090f426
|
||||
@@ -33,10 +33,6 @@
|
||||
{ "name": "chainId", "type": "uint256" },
|
||||
{ "name": "verifyingContract", "type": "address" }
|
||||
],
|
||||
"Group": [
|
||||
{ "name": "name", "type": "string" },
|
||||
{ "name": "members", "type": "Person[]" }
|
||||
],
|
||||
"Mail": [
|
||||
{ "name": "from", "type": "Person" },
|
||||
{ "name": "to", "type": "Person[]" },
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
[signature]
|
||||
v = 1b
|
||||
r = db18ea1b9757773385138d0802fb2f8107c3e45882962b8e0c6789eccdbfab05
|
||||
s = 3d66d4dee47916fb7fec39a538ad8d5e94fbc92f99327410716180ab07591218
|
||||
@@ -1,4 +0,0 @@
|
||||
[signature]
|
||||
v = 1c
|
||||
r = 50fb2861367daf3b5b73cac277e698b27bf7627a462fade1acb5a2ef285ba8ae
|
||||
s = 131a62515a0a5c4b35c5cb672b46f562151c45508d8efcdf78c4608bc14c5f30
|
||||
@@ -1,4 +0,0 @@
|
||||
[signature]
|
||||
v = 1c
|
||||
r = 929681d77ed88cd1adef57185a0cd7b3a268aca5d4122b8c0acfd2ce4c0afb18
|
||||
s = 5afe8e3004c182b6b02fe7559c26f20f4133ad9b17223658ccd9061b33b021cf
|
||||
@@ -1,4 +0,0 @@
|
||||
[signature]
|
||||
v = 1b
|
||||
r = e021d88afc50079b0341b01193c4687c47b85bcd6749fe69e0b87521d65a1847
|
||||
s = 5b7670d2a67c781a11164920403db0f7707161e81d88226cdbf91298390dfeda
|
||||
@@ -1,4 +0,0 @@
|
||||
[signature]
|
||||
v = 1b
|
||||
r = 1539547ae7cf8ebcd3eabfb57cd2b1fb7775ce757c3f4a307c7425d35b7bfff7
|
||||
s = 47248cb61e554c1f90af6331d9c9e51cbb8655667514194f509abe097a032319
|
||||
@@ -1,4 +0,0 @@
|
||||
[signature]
|
||||
v = 1c
|
||||
r = 341bca1c0dfd805d4befc21500084424dbe559c7aafd78d8fb461c0c76dfea1d
|
||||
s = 33ebb7b6fe0691961cd8b263faac20ecbbdcaef3febb57eb76614cad629080ea
|
||||
@@ -1,4 +0,0 @@
|
||||
[signature]
|
||||
v = 1b
|
||||
r = d11a91bdf7836288818875d046452061d565cc6dc1bf3dd6216ab27ef9a2844f
|
||||
s = 4f6bda8ac4c39721aff7ae08989897ede9d573085a192d03ab0eb7735d2ef403
|
||||
@@ -1,4 +0,0 @@
|
||||
[signature]
|
||||
v = 1c
|
||||
r = cce2e63aaac6a5f9a74684d8fdddcbc7f3b27aa17235bfab89226821ead933b6
|
||||
s = 3f3c93977abcc3f8cc9a3dc1ecc02dbca14aca1a6ecb2fb6ca3d7c713ace1ec4
|
||||
@@ -1,4 +0,0 @@
|
||||
[signature]
|
||||
v = 1b
|
||||
r = 7be1671577753c13bfd1da8b234b6df8484daf47351c2366637fd291dd4aa4d9
|
||||
s = 1a7ffbb01dc8a64e9ee97d19b8f154e9eecbe0b1bfb9dcfa781a65e474573963
|
||||
@@ -6,19 +6,36 @@
|
||||
"version": "1"
|
||||
},
|
||||
"message": {
|
||||
"list1": [],
|
||||
"list2": [],
|
||||
"list3": [
|
||||
"test1": [],
|
||||
"test2": [
|
||||
[
|
||||
"1",
|
||||
"2"
|
||||
"one",
|
||||
"two"
|
||||
],
|
||||
[],
|
||||
[
|
||||
"3",
|
||||
"4"
|
||||
"three",
|
||||
"four"
|
||||
]
|
||||
]
|
||||
],
|
||||
"test3": [
|
||||
{
|
||||
"sub": [
|
||||
{
|
||||
"sub": [],
|
||||
"value": 3
|
||||
}
|
||||
],
|
||||
"value": 1
|
||||
}
|
||||
],
|
||||
"test4": [
|
||||
{
|
||||
"sub": [],
|
||||
"value": 2
|
||||
}
|
||||
],
|
||||
"test5": []
|
||||
},
|
||||
"primaryType": "Struct",
|
||||
"types": {
|
||||
@@ -29,9 +46,22 @@
|
||||
{ "name": "verifyingContract", "type": "address" }
|
||||
],
|
||||
"Struct": [
|
||||
{ "name": "list1", "type": "EIP712Domain[]" },
|
||||
{ "name": "list2", "type": "uint8[]" },
|
||||
{ "name": "list3", "type": "string[][]" }
|
||||
{ "name": "test1", "type": "uint8[]" },
|
||||
{ "name": "test2", "type": "string[][]" },
|
||||
{ "name": "test3", "type": "Top[]" },
|
||||
{ "name": "test4", "type": "Top[]" },
|
||||
{ "name": "test5", "type": "Top[]" }
|
||||
],
|
||||
"Bottom": [
|
||||
{ "name": "value", "type": "uint8" }
|
||||
],
|
||||
"Mid": [
|
||||
{ "name": "sub", "type": "Bottom[]" },
|
||||
{ "name": "value", "type": "uint8" }
|
||||
],
|
||||
"Top": [
|
||||
{ "name": "sub", "type": "Mid[]" },
|
||||
{ "name": "value", "type": "uint8" }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
[signature]
|
||||
v = 1b
|
||||
r = 5d0635a868602e29366da6328f8fadf2d6a9b4e69ee7a65928e85ca56fb1b515
|
||||
s = 257364d6faaf5687edf90c3984f4240b0ce7b2dee55aa1f8f39c32d0d4d8c93d
|
||||
61
tests/ragger/eip712_input_files/14-rabby_bug-data.json
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"types": {
|
||||
"BatchSignedERC721Orders": [
|
||||
{ "type": "address", "name": "maker" },
|
||||
{ "type": "uint256", "name": "listingTime" },
|
||||
{ "type": "uint256", "name": "expiryTime" },
|
||||
{ "type": "uint256", "name": "startNonce" },
|
||||
{ "type": "address", "name": "erc20Token" },
|
||||
{ "type": "address", "name": "platformFeeRecipient" },
|
||||
{ "type": "BasicCollection[]", "name": "basicCollections" },
|
||||
{ "type": "Collection[]", "name": "collections" },
|
||||
{ "type": "uint256", "name": "hashNonce" }
|
||||
],
|
||||
"BasicCollection": [
|
||||
{ "type": "address", "name": "nftAddress" },
|
||||
{ "type": "bytes32", "name": "fee" },
|
||||
{ "type": "bytes32[]", "name": "items" }
|
||||
],
|
||||
"Collection": [
|
||||
{ "type": "address", "name": "nftAddress" },
|
||||
{ "type": "bytes32", "name": "fee" },
|
||||
{ "type": "OrderItem[]", "name": "items" }
|
||||
],
|
||||
"OrderItem": [
|
||||
{ "type": "uint256", "name": "erc20TokenAmount" },
|
||||
{ "type": "uint256", "name": "nftId" }
|
||||
],
|
||||
"EIP712Domain": [
|
||||
{ "name": "name", "type": "string" },
|
||||
{ "name": "version", "type": "string" },
|
||||
{ "name": "chainId", "type": "uint256" },
|
||||
{ "name": "verifyingContract", "type": "address" }
|
||||
]
|
||||
},
|
||||
"domain": {
|
||||
"name": "ElementEx",
|
||||
"version": "1.0.0",
|
||||
"chainId": "5000",
|
||||
"verifyingContract": "0x2fa13cf695ec51ded5b8e45ad0bef838ab17e2af"
|
||||
},
|
||||
"primaryType": "BatchSignedERC721Orders",
|
||||
"message": {
|
||||
"maker": "0x6d3b90747dbf5883bf88ff7eb5fcc86f408b5409",
|
||||
"listingTime": "1706688449",
|
||||
"expiryTime": "1709280466",
|
||||
"startNonce": "7",
|
||||
"erc20Token": "0x09bc4e0d864854c6afb6eb9a9cdf58ac190d0df9",
|
||||
"platformFeeRecipient": "0x07538262ae993ca117a0e481f908209137a4626e",
|
||||
"basicCollections": [
|
||||
{
|
||||
"nftAddress": "0xaaaea1fb9f3de3f70e89f37b69ab11b47eb9ce6f",
|
||||
"fee": "0x000000000000000000c80000000000000000000000000000000000000000000",
|
||||
"items": [
|
||||
"0x000000000000000020c8558000000000000000000000000000000000000005d"
|
||||
]
|
||||
}
|
||||
],
|
||||
"collections": [],
|
||||
"hashNonce": "0"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 472 B After Width: | Height: | Size: 472 B |
|
Before Width: | Height: | Size: 414 B After Width: | Height: | Size: 414 B |
|
Before Width: | Height: | Size: 338 B After Width: | Height: | Size: 338 B |
|
Before Width: | Height: | Size: 336 B After Width: | Height: | Size: 336 B |
|
Before Width: | Height: | Size: 324 B After Width: | Height: | Size: 324 B |
|
Before Width: | Height: | Size: 364 B After Width: | Height: | Size: 364 B |
|
Before Width: | Height: | Size: 381 B After Width: | Height: | Size: 381 B |
|
Before Width: | Height: | Size: 472 B After Width: | Height: | Size: 472 B |
|
Before Width: | Height: | Size: 463 B After Width: | Height: | Size: 463 B |
|
Before Width: | Height: | Size: 371 B After Width: | Height: | Size: 371 B |
|
Before Width: | Height: | Size: 298 B After Width: | Height: | Size: 298 B |
|
Before Width: | Height: | Size: 285 B After Width: | Height: | Size: 285 B |
|
Before Width: | Height: | Size: 655 B After Width: | Height: | Size: 655 B |
|
Before Width: | Height: | Size: 433 B After Width: | Height: | Size: 433 B |
|
Before Width: | Height: | Size: 555 B After Width: | Height: | Size: 555 B |
|
Before Width: | Height: | Size: 709 B After Width: | Height: | Size: 709 B |
|
Before Width: | Height: | Size: 321 B After Width: | Height: | Size: 321 B |
|
Before Width: | Height: | Size: 774 B After Width: | Height: | Size: 774 B |
|
Before Width: | Height: | Size: 364 B After Width: | Height: | Size: 364 B |
|
Before Width: | Height: | Size: 381 B After Width: | Height: | Size: 381 B |
|
Before Width: | Height: | Size: 472 B After Width: | Height: | Size: 472 B |
|
Before Width: | Height: | Size: 414 B After Width: | Height: | Size: 414 B |
|
Before Width: | Height: | Size: 338 B After Width: | Height: | Size: 338 B |
|
Before Width: | Height: | Size: 336 B After Width: | Height: | Size: 336 B |
|
Before Width: | Height: | Size: 324 B After Width: | Height: | Size: 324 B |
|
Before Width: | Height: | Size: 364 B After Width: | Height: | Size: 364 B |
|
Before Width: | Height: | Size: 381 B After Width: | Height: | Size: 381 B |
|
Before Width: | Height: | Size: 472 B After Width: | Height: | Size: 472 B |
|
Before Width: | Height: | Size: 463 B After Width: | Height: | Size: 463 B |
|
Before Width: | Height: | Size: 371 B After Width: | Height: | Size: 371 B |
|
Before Width: | Height: | Size: 298 B After Width: | Height: | Size: 298 B |
|
Before Width: | Height: | Size: 285 B After Width: | Height: | Size: 285 B |
|
Before Width: | Height: | Size: 655 B After Width: | Height: | Size: 655 B |
|
Before Width: | Height: | Size: 433 B After Width: | Height: | Size: 433 B |
|
Before Width: | Height: | Size: 555 B After Width: | Height: | Size: 555 B |
|
Before Width: | Height: | Size: 709 B After Width: | Height: | Size: 709 B |
|
Before Width: | Height: | Size: 321 B After Width: | Height: | Size: 321 B |
|
Before Width: | Height: | Size: 774 B After Width: | Height: | Size: 774 B |
|
Before Width: | Height: | Size: 364 B After Width: | Height: | Size: 364 B |
|
Before Width: | Height: | Size: 381 B After Width: | Height: | Size: 381 B |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
@@ -1,12 +1,12 @@
|
||||
import fnmatch
|
||||
import os
|
||||
import time
|
||||
from configparser import ConfigParser
|
||||
from functools import partial
|
||||
from pathlib import Path
|
||||
import json
|
||||
from typing import Optional
|
||||
import pytest
|
||||
from eth_account.messages import encode_typed_data
|
||||
|
||||
import client.response_parser as ResponseParser
|
||||
from client.utils import recover_message
|
||||
@@ -29,7 +29,8 @@ class SnapshotsConfig:
|
||||
|
||||
|
||||
BIP32_PATH = "m/44'/60'/0'/0/0"
|
||||
snaps_config: Optional[SnapshotsConfig] = None
|
||||
SNAPS_CONFIG: Optional[SnapshotsConfig] = None
|
||||
WALLET_ADDR: Optional[bytes] = None
|
||||
|
||||
|
||||
def eip712_json_path() -> str:
|
||||
@@ -59,33 +60,44 @@ def filtering_fixture(request) -> bool:
|
||||
return request.param
|
||||
|
||||
|
||||
def get_wallet_addr(client: EthAppClient) -> bytes:
|
||||
global WALLET_ADDR
|
||||
|
||||
# don't ask again if we already have it
|
||||
if WALLET_ADDR is None:
|
||||
with client.get_public_addr(display=False):
|
||||
pass
|
||||
_, WALLET_ADDR, _ = ResponseParser.pk_addr(client.response().data)
|
||||
return WALLET_ADDR
|
||||
|
||||
|
||||
def test_eip712_legacy(firmware: Firmware,
|
||||
backend: BackendInterface,
|
||||
navigator: Navigator):
|
||||
app_client = EthAppClient(backend)
|
||||
with app_client.eip712_sign_legacy(
|
||||
BIP32_PATH,
|
||||
bytes.fromhex('6137beb405d9ff777172aa879e33edb34a1460e701802746c5ef96e741710e59'),
|
||||
bytes.fromhex('eb4221181ff3f1a83ea7313993ca9218496e424604ba9492bb4052c03d5c3df8')):
|
||||
moves = []
|
||||
if firmware.device.startswith("nano"):
|
||||
moves += [NavInsID.RIGHT_CLICK]
|
||||
if firmware.device == "nanos":
|
||||
screens_per_hash = 4
|
||||
|
||||
with open(input_files()[0]) as file:
|
||||
data = json.load(file)
|
||||
smsg = encode_typed_data(full_message=data)
|
||||
with app_client.eip712_sign_legacy(BIP32_PATH, smsg.header, smsg.body):
|
||||
moves = []
|
||||
if firmware.device.startswith("nano"):
|
||||
moves += [NavInsID.RIGHT_CLICK]
|
||||
if firmware.device == "nanos":
|
||||
screens_per_hash = 4
|
||||
else:
|
||||
screens_per_hash = 2
|
||||
moves += [NavInsID.RIGHT_CLICK] * screens_per_hash * 2
|
||||
moves += [NavInsID.BOTH_CLICK]
|
||||
else:
|
||||
screens_per_hash = 2
|
||||
moves += [NavInsID.RIGHT_CLICK] * screens_per_hash * 2
|
||||
moves += [NavInsID.BOTH_CLICK]
|
||||
else:
|
||||
moves += [NavInsID.USE_CASE_REVIEW_TAP] * 2
|
||||
moves += [NavInsID.USE_CASE_REVIEW_CONFIRM]
|
||||
navigator.navigate(moves)
|
||||
moves += [NavInsID.USE_CASE_REVIEW_TAP] * 2
|
||||
moves += [NavInsID.USE_CASE_REVIEW_CONFIRM]
|
||||
navigator.navigate(moves)
|
||||
|
||||
v, r, s = ResponseParser.signature(app_client.response().data)
|
||||
vrs = ResponseParser.signature(app_client.response().data)
|
||||
recovered_addr = recover_message(data, vrs)
|
||||
|
||||
assert v == bytes.fromhex("1c")
|
||||
assert r == bytes.fromhex("ea66f747173762715751c889fea8722acac3fc35db2c226d37a2e58815398f64")
|
||||
assert s == bytes.fromhex("52d8ba9153de9255da220ffd36762c0b027701a3b5110f0a765f94b16a9dfb55")
|
||||
assert recovered_addr == get_wallet_addr(app_client)
|
||||
|
||||
|
||||
def autonext(firmware: Firmware, navigator: Navigator, default_screenshot_path: Path):
|
||||
@@ -94,18 +106,18 @@ def autonext(firmware: Firmware, navigator: Navigator, default_screenshot_path:
|
||||
moves = [NavInsID.RIGHT_CLICK]
|
||||
else:
|
||||
moves = [NavInsID.USE_CASE_REVIEW_TAP]
|
||||
if snaps_config is not None:
|
||||
if SNAPS_CONFIG is not None:
|
||||
navigator.navigate_and_compare(default_screenshot_path,
|
||||
snaps_config.test_name,
|
||||
SNAPS_CONFIG.test_name,
|
||||
moves,
|
||||
screen_change_before_first_instruction=False,
|
||||
screen_change_after_last_instruction=False,
|
||||
snap_start_idx=snaps_config.idx)
|
||||
snaps_config.idx += 1
|
||||
snap_start_idx=SNAPS_CONFIG.idx)
|
||||
SNAPS_CONFIG.idx += 1
|
||||
else:
|
||||
navigator.navigate(moves,
|
||||
screen_change_before_first_instruction=False,
|
||||
screen_change_after_last_instruction=False)
|
||||
screen_change_before_first_instruction=False,
|
||||
screen_change_after_last_instruction=False)
|
||||
|
||||
|
||||
def eip712_new_common(firmware: Firmware,
|
||||
@@ -132,12 +144,12 @@ def eip712_new_common(firmware: Firmware,
|
||||
if not verbose and filters is None:
|
||||
moves += [NavInsID.USE_CASE_REVIEW_TAP]
|
||||
moves += [NavInsID.USE_CASE_REVIEW_CONFIRM]
|
||||
if snaps_config is not None:
|
||||
if SNAPS_CONFIG is not None:
|
||||
navigator.navigate_and_compare(default_screenshot_path,
|
||||
snaps_config.test_name,
|
||||
moves,
|
||||
snap_start_idx=snaps_config.idx)
|
||||
snaps_config.idx += 1
|
||||
SNAPS_CONFIG.test_name,
|
||||
moves,
|
||||
snap_start_idx=SNAPS_CONFIG.idx)
|
||||
SNAPS_CONFIG.idx += 1
|
||||
else:
|
||||
navigator.navigate(moves)
|
||||
return ResponseParser.signature(app_client.response().data)
|
||||
@@ -155,7 +167,6 @@ def test_eip712_new(firmware: Firmware,
|
||||
pytest.skip("Not supported on LNS")
|
||||
|
||||
test_path = f"{input_file.parent}/{'-'.join(input_file.stem.split('-')[:-1])}"
|
||||
conf_file = f"{test_path}.ini"
|
||||
|
||||
filters = None
|
||||
if filtering:
|
||||
@@ -166,51 +177,39 @@ def test_eip712_new(firmware: Firmware,
|
||||
except (IOError, json.decoder.JSONDecodeError) as e:
|
||||
pytest.skip(f"{filterfile.name}: {e.strerror}")
|
||||
|
||||
config = ConfigParser()
|
||||
config.read(conf_file)
|
||||
|
||||
# sanity check
|
||||
assert "signature" in config.sections()
|
||||
assert "v" in config["signature"]
|
||||
assert "r" in config["signature"]
|
||||
assert "s" in config["signature"]
|
||||
|
||||
if verbose:
|
||||
settings_toggle(firmware, navigator, [SettingID.VERBOSE_EIP712])
|
||||
|
||||
with open(input_file, encoding="utf-8") as file:
|
||||
v, r, s = eip712_new_common(firmware,
|
||||
navigator,
|
||||
default_screenshot_path,
|
||||
app_client,
|
||||
json.load(file),
|
||||
filters,
|
||||
verbose)
|
||||
data = json.load(file)
|
||||
vrs = eip712_new_common(firmware,
|
||||
navigator,
|
||||
default_screenshot_path,
|
||||
app_client,
|
||||
data,
|
||||
filters,
|
||||
verbose)
|
||||
|
||||
assert v == bytes.fromhex(config["signature"]["v"])
|
||||
assert r == bytes.fromhex(config["signature"]["r"])
|
||||
assert s == bytes.fromhex(config["signature"]["s"])
|
||||
recovered_addr = recover_message(data, vrs)
|
||||
|
||||
assert recovered_addr == get_wallet_addr(app_client)
|
||||
|
||||
|
||||
def test_eip712_address_substitution(firmware: Firmware,
|
||||
backend: BackendInterface,
|
||||
navigator: Navigator,
|
||||
default_screenshot_path: Path,
|
||||
test_name: str,
|
||||
verbose: bool):
|
||||
global snaps_config
|
||||
global SNAPS_CONFIG
|
||||
|
||||
app_client = EthAppClient(backend)
|
||||
if firmware.device == "nanos":
|
||||
pytest.skip("Not supported on LNS")
|
||||
|
||||
with app_client.get_public_addr(display=False):
|
||||
pass
|
||||
_, DEVICE_ADDR, _ = ResponseParser.pk_addr(app_client.response().data)
|
||||
|
||||
test_name = "eip712_address_substitution"
|
||||
if verbose:
|
||||
test_name += "_verbose"
|
||||
snaps_config = SnapshotsConfig(test_name)
|
||||
SNAPS_CONFIG = SnapshotsConfig(test_name)
|
||||
with open(f"{eip712_json_path()}/address_substitution.json", encoding="utf-8") as file:
|
||||
data = json.load(file)
|
||||
|
||||
@@ -247,4 +246,4 @@ def test_eip712_address_substitution(firmware: Firmware,
|
||||
|
||||
# verify signature
|
||||
addr = recover_message(data, vrs)
|
||||
assert addr == DEVICE_ADDR
|
||||
assert addr == get_wallet_addr(app_client)
|
||||
|
||||