Skip to content

Commit 72c67be

Browse files
[Eth]: Fix EIP712 message hashing when fixed byte array is 0x0 (#3522)
1 parent 6846db5 commit 72c67be

File tree

4 files changed

+120
-8
lines changed

4 files changed

+120
-8
lines changed

rust/tw_encoding/src/hex.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
// file LICENSE at the root of the source code distribution tree.
66

77
pub use hex::FromHexError;
8+
use tw_memory::Data;
9+
10+
pub type FromHexResult<T> = Result<T, FromHexError>;
811

912
pub trait ToHex {
1013
fn to_hex(&self) -> String;
@@ -26,20 +29,34 @@ where
2629
}
2730

2831
pub trait DecodeHex {
29-
fn decode_hex(&self) -> Result<Vec<u8>, FromHexError>;
32+
fn decode_hex(&self) -> FromHexResult<Data>;
3033
}
3134

3235
impl<'a> DecodeHex for &'a str {
33-
fn decode_hex(&self) -> Result<Vec<u8>, FromHexError> {
36+
fn decode_hex(&self) -> FromHexResult<Data> {
3437
decode(self)
3538
}
3639
}
3740

38-
pub fn decode(data: &str) -> Result<Vec<u8>, FromHexError> {
41+
/// Decodes the given hexadecimal string.
42+
pub fn decode(data: &str) -> FromHexResult<Data> {
3943
let hex_string = data.trim_start_matches("0x");
4044
hex::decode(hex_string)
4145
}
4246

47+
/// Decodes the given hexadecimal string leniently allowing to pass odd number of chars.
48+
/// For example, `0x0` is extended to `0x00`, `0x123` is extended to `0x0123`.
49+
pub fn decode_lenient(data: &str) -> FromHexResult<Data> {
50+
let hex_string = data.trim_start_matches("0x");
51+
if hex_string.len() % 2 == 0 {
52+
hex::decode(hex_string)
53+
} else {
54+
// Insert a leading 0.
55+
let standard_hex = format!("0{hex_string}");
56+
hex::decode(standard_hex)
57+
}
58+
}
59+
4360
pub fn encode<T: AsRef<[u8]>>(data: T, prefixed: bool) -> String {
4461
let encoded = hex::encode(data.as_ref());
4562
if prefixed {

rust/tw_evm/src/message/eip712/eip712_message.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use serde::Deserialize;
1515
use serde_json::Value as Json;
1616
use std::collections::HashMap;
1717
use std::str::FromStr;
18-
use tw_encoding::hex::DecodeHex;
18+
use tw_encoding::hex::{self, DecodeHex};
1919
use tw_hash::sha3::keccak256;
2020
use tw_hash::{H160, H256};
2121
use tw_memory::Data;
@@ -158,10 +158,9 @@ fn encode_fix_bytes(value: &Json, expected_len: usize) -> MessageSigningResult<D
158158
let str = value
159159
.as_str()
160160
.ok_or(MessageSigningError::InvalidParameterValue)?;
161-
let fix_bytes = str
162-
.decode_hex()
163-
.map_err(|_| MessageSigningError::InvalidParameterValue)?;
164-
if fix_bytes.len() != expected_len {
161+
let fix_bytes =
162+
hex::decode_lenient(str).map_err(|_| MessageSigningError::InvalidParameterValue)?;
163+
if fix_bytes.len() > expected_len {
165164
return Err(MessageSigningError::TypeValueMismatch);
166165
}
167166
let checked_bytes =
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
{
2+
"types": {
3+
"EIP712Domain": [
4+
{
5+
"name": "name",
6+
"type": "string"
7+
},
8+
{
9+
"name": "version",
10+
"type": "string"
11+
},
12+
{
13+
"name": "chainId",
14+
"type": "uint256"
15+
},
16+
{
17+
"name": "verifyingContract",
18+
"type": "address"
19+
},
20+
{
21+
"name": "salt",
22+
"type": "bytes32"
23+
}
24+
],
25+
"Trade": [
26+
{
27+
"name": "nonce",
28+
"type": "bytes32"
29+
},
30+
{
31+
"name": "firstParty",
32+
"type": "address"
33+
},
34+
{
35+
"name": "askingId",
36+
"type": "uint256"
37+
},
38+
{
39+
"name": "askingQty",
40+
"type": "uint256"
41+
},
42+
{
43+
"name": "offeringId",
44+
"type": "uint256"
45+
},
46+
{
47+
"name": "offeringQty",
48+
"type": "uint256"
49+
},
50+
{
51+
"name": "maxFee",
52+
"type": "uint256"
53+
},
54+
{
55+
"name": "secondParty",
56+
"type": "address"
57+
},
58+
{
59+
"name": "count",
60+
"type": "uint8"
61+
}
62+
]
63+
},
64+
"domain": {
65+
"name": "CryptoFights Trading",
66+
"version": "1",
67+
"chainId": 1,
68+
"verifyingContract": "0xdc45529aC0FA3185f79A005e57deF64F600c4e97",
69+
"salt": "0x0"
70+
},
71+
"primaryType": "Trade",
72+
"message": {
73+
"count": 1,
74+
"offeringQty": 1,
75+
"askingQty": 2,
76+
"nonce": "0xcfe49aa546453df3f2e768a97204a3268cef7c27df53cc2f2d47385cfeaf",
77+
"firstParty": "0xC36edF48e21cf395B206352A1819DE658fD7f988",
78+
"askingId": "0x0000000000000000000000000000000000000000000000000000000000000000",
79+
"offeringId": "0x0000000000000000000000000000000000000000000000000000000000000000",
80+
"maxFee": "1000000000000000000",
81+
"secondParty": "0x0000000000000000000000000000000000000000"
82+
}
83+
}

rust/tw_evm/tests/message_signer.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const EIP712_WITH_CUSTOM_ARRAY: &str = include_str!("data/eip712_with_custom_arr
1919
const EIP712_UNEQUAL_ARRAY_LEN: &str = include_str!("data/eip712_unequal_array_lengths.json");
2020
const EIP712_WITH_CHAIN_ID_STR: &str = include_str!("data/eip712_with_chain_id_string.json");
2121
const EIP712_GREENFIELD: &str = include_str!("data/eip712_greenfield.json");
22+
const EIP712_FIXED_BYTES: &str = include_str!("data/eip712_fixed_bytes.json");
2223

2324
struct SignVerifyTestInput {
2425
private_key: &'static str,
@@ -256,3 +257,15 @@ fn test_message_signer_sign_verify_eip712_greenfield() {
256257
signature: "cb3a4684a991014a387a04a85b59227ebb79567c2025addcb296b4ca856e9f810d3b526f2a0d0fad6ad1b126b3b9516f8b3be020a7cca9c03ce3cf47f4199b6d1b",
257258
});
258259
}
260+
261+
// The test checks if `0x0` is a valid `bytes32` value.
262+
#[test]
263+
fn test_message_signer_sign_verify_eip712_fixed_bytes() {
264+
test_message_signer_sign_verify(SignVerifyTestInput {
265+
private_key: "c85ef7d79691fe79573b1a7064c19c1a9819ebdbd1faaab1a8ec92344438aaf4",
266+
msg: EIP712_FIXED_BYTES,
267+
msg_type: Proto::MessageType::MessageType_typed,
268+
chain_id: None,
269+
signature: "7ee9b54fedf355e40fa86bbe23e63b318ef797bd8fdbc5bb714edbace042d4cb60111912218234e856f2cf300b3b47c91383b98e263ecf69c6c10193fef6c9581b",
270+
});
271+
}

0 commit comments

Comments
 (0)