Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Changelog

## [Unreleased]
- Preserve the sign of positive EIP-712 signed integers supplied as JSON numbers.
- Reset unfinished sessions when connecting to firmware v9.28.0 or newer, allowing host
reconnects while the device remains powered on.
- Remove the `wasm` feature, the public `wasm` module, and the TypeScript/WASM package. The
Expand Down
43 changes: 36 additions & 7 deletions src/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,12 +333,7 @@ fn encode_value(typ: &MemberType, value: &Value) -> Result<Vec<u8>, String> {
if (v64 as f64) != v {
Err("Number is not an int".to_string())
} else {
let mut bytes = BigInt::from(v64).to_signed_bytes_be();
// Drop leading zero. There can be at most one.
if let [0, ..] = bytes.as_slice() {
bytes.remove(0);
}
Ok(bytes)
Ok(BigInt::from(v64).to_signed_bytes_be())
}
} else {
Err("Number is not an int".to_string())
Expand Down Expand Up @@ -1164,7 +1159,7 @@ mod tests {
&2983742332.0.into(),
)
.unwrap();
assert_eq!(vec![0xb1, 0xd8, 0x4b, 0x7c], encoded);
assert_eq!(vec![0x00, 0xb1, 0xd8, 0x4b, 0x7c], encoded);

let encoded = encode_value(
&parse_type_no_err("int64", &HashMap::new()),
Expand Down Expand Up @@ -1218,6 +1213,40 @@ mod tests {
assert_eq!(b"\x00\x00\x03\xe8".to_vec(), encoded);
}

#[test]
fn test_encode_value_signed_integers() {
let typ = parse_type_no_err("int64", &HashMap::new());
for (value, expected) in [
(0i64, "00"),
(1, "01"),
(-1, "ff"),
(127, "7f"),
(128, "0080"),
(129, "0081"),
(-127, "81"),
(-128, "80"),
(-129, "ff7f"),
(255, "00ff"),
(256, "0100"),
(-255, "ff01"),
(-256, "ff00"),
(32767, "7fff"),
(32768, "008000"),
(-32768, "8000"),
(-32769, "ff7fff"),
(65535, "00ffff"),
(65536, "010000"),
] {
for input in [Value::from(value), Value::from(value.to_string())] {
assert_eq!(
hex::encode(encode_value(&typ, &input).unwrap()),
expected,
"{input:?}",
);
}
}
}

#[test]
fn test_get_value() {
let msg: Eip712Message = serde_json::from_str(EIP712_MSG).unwrap();
Expand Down
50 changes: 50 additions & 0 deletions tests/test_eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,56 @@ async fn test_eth_sign_typed_message_antiklepto_enabled() {
.await
}

#[tokio::test]
async fn test_eth_sign_typed_message_signed_integers() {
test_initialized_simulators(async |paired_bitbox| {
let msg = r#"{
"types": {
"EIP712Domain": [{"name": "name", "type": "string"}],
"SignedIntegers": [
{"name": "positive", "type": "int16"},
{"name": "negative", "type": "int8"},
{"name": "zero", "type": "int8"}
]
},
"primaryType": "SignedIntegers",
"domain": {"name": "Signed integers"},
"message": {"positive": 128, "negative": -128, "zero": 0}
}"#;
let signature = paired_bitbox
.eth_sign_typed_message(1, &"m/44'/60'/0'/0/0".try_into().unwrap(), msg, true)
.await
.unwrap();
assert_eq!(signature.len(), 65);

// Compute the EIP-712 digest independently of the wire encoder. The device
// must sign +128, -128 and zero, not reinterpret or reject their sign bytes.
let domain_hash = keccak256(
&[
keccak256(b"EIP712Domain(string name)"),
keccak256(b"Signed integers"),
]
.concat(),
);
let mut positive = [0u8; 32];
positive[31] = 0x80;
let mut negative = [0xff; 32];
negative[31] = 0x80;
let message_hash = keccak256(
&[
keccak256(b"SignedIntegers(int16 positive,int8 negative,int8 zero)"),
positive,
negative,
[0u8; 32],
]
.concat(),
);
let digest = keccak256(&[b"\x19\x01".as_slice(), &domain_hash, &message_hash].concat());
verify_eth_signature(&digest, &signature);
})
.await
}

#[tokio::test]
async fn test_eth_sign_typed_message_antiklepto_disabled() {
test_initialized_simulators(async |paired_bitbox| {
Expand Down
Loading