diff --git a/Cargo.lock b/Cargo.lock index 313979f1..22fdfa76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,11 +15,14 @@ dependencies = [ name = "app-boilerplate-rust" version = "1.0.0" dependencies = [ + "hex", "include_gif", "ledger_device_sdk", "ledger_device_ui_sdk", "ledger_secure_sdk_sys", "numtoa", + "serde", + "serde-json-core", ] [[package]] @@ -63,6 +66,12 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "cc" version = "1.0.83" @@ -136,6 +145,34 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + [[package]] name = "home" version = "0.5.5" @@ -349,9 +386,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.38.24" +version = "0.38.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ad981d6c340a49cdc40a1028d9c6084ec7e9fa33fcb839cab656a267071e234" +checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" dependencies = [ "bitflags 2.4.1", "errno", @@ -360,12 +397,54 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "serde" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-json-core" +version = "0.5.1" +source = "git+https://github.com/rust-embedded-community/serde-json-core#e695aa6be4761acada3ba30d99be46add277ec7f" +dependencies = [ + "heapless", + "ryu", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "shlex" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "syn" version = "1.0.109" @@ -502,6 +581,6 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "zeroize" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12a3946ecfc929b583800f4629b6c25b88ac6e92a40ea5670f77112a85d40a8b" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/Cargo.toml b/Cargo.toml index b7966e15..04e1d5f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,9 @@ ledger_device_sdk = "1.0.1" ledger_device_ui_sdk = "1.1.1" ledger_secure_sdk_sys = "1.0.1" include_gif = "1.0.0" +serde = {version="1.0.192", default_features = false, features = ["derive"]} +serde-json-core = { git = "https://github.com/rust-embedded-community/serde-json-core"} +hex = { version = "0.4.3", default-features = false, features = ["serde"] } numtoa = "0.2.4" [profile.release] diff --git a/src/app_ui/address.rs b/src/app_ui/address.rs index 28b3b207..6d3272d7 100644 --- a/src/app_ui/address.rs +++ b/src/app_ui/address.rs @@ -15,9 +15,8 @@ * limitations under the License. *****************************************************************************/ -use crate::utils::{concatenate, to_hex_all_caps}; use crate::AppSW; -use core::str::from_utf8; +use core::str::from_utf8_mut; use ledger_device_ui_sdk::bitmaps::{CROSSMARK, EYE, VALIDATE_14}; use ledger_device_ui_sdk::ui::{Field, MultiFieldReview}; @@ -25,19 +24,19 @@ use ledger_device_ui_sdk::ui::{Field, MultiFieldReview}; const DISPLAY_ADDR_BYTES_LEN: usize = 20; pub fn ui_display_pk(addr: &[u8]) -> Result { - let addr_hex_str_buf = to_hex_all_caps(&addr[addr.len() - DISPLAY_ADDR_BYTES_LEN..]) - .map_err(|_| AppSW::AddrDisplayFail)?; - let addr_hex_str = from_utf8(&addr_hex_str_buf[..DISPLAY_ADDR_BYTES_LEN * 2]) - .map_err(|_| AppSW::AddrDisplayFail)?; - - let mut addr_hex_str_with_prefix_buf = [0u8; DISPLAY_ADDR_BYTES_LEN * 2 + 2]; - concatenate(&["0x", addr_hex_str], &mut addr_hex_str_with_prefix_buf); - let addr_hex_str_with_prefix = - from_utf8(&addr_hex_str_with_prefix_buf).map_err(|_| AppSW::AddrDisplayFail)?; + let mut addr_hex = [0u8; DISPLAY_ADDR_BYTES_LEN * 2 + 2]; + addr_hex[..2].copy_from_slice("0x".as_bytes()); + hex::encode_to_slice( + &addr[addr.len() - DISPLAY_ADDR_BYTES_LEN..], + &mut addr_hex[2..], + ) + .unwrap(); + let addr_hex = from_utf8_mut(&mut addr_hex).unwrap(); + addr_hex[2..].make_ascii_uppercase(); let my_field = [Field { name: "Address", - value: addr_hex_str_with_prefix, + value: addr_hex, }]; let my_review = MultiFieldReview::new( diff --git a/src/app_ui/menu.rs b/src/app_ui/menu.rs index 87c95a8a..6f3096ae 100644 --- a/src/app_ui/menu.rs +++ b/src/app_ui/menu.rs @@ -28,11 +28,8 @@ fn ui_about_menu(comm: &mut Comm) -> Event { loop { match MultiPageMenu::new(comm, &pages).show() { EventOrPageIndex::Event(e) => return e, - i => { - if let EventOrPageIndex::Index(1) = i { - return ui_menu_main(comm); - } - } + EventOrPageIndex::Index(1) => return ui_menu_main(comm), + EventOrPageIndex::Index(_) => (), } } } @@ -50,11 +47,9 @@ pub fn ui_menu_main(comm: &mut Comm) -> Event { loop { match MultiPageMenu::new(comm, &pages).show() { EventOrPageIndex::Event(e) => return e, - i => match i { - EventOrPageIndex::Index(2) => return ui_about_menu(comm), - EventOrPageIndex::Index(3) => ledger_device_sdk::exit_app(0), - _ => (), - }, + EventOrPageIndex::Index(2) => return ui_about_menu(comm), + EventOrPageIndex::Index(3) => ledger_device_sdk::exit_app(0), + EventOrPageIndex::Index(_) => (), } } } diff --git a/src/app_ui/sign.rs b/src/app_ui/sign.rs index d1978155..f236378d 100644 --- a/src/app_ui/sign.rs +++ b/src/app_ui/sign.rs @@ -14,51 +14,52 @@ * See the License for the specific language governing permissions and * limitations under the License. *****************************************************************************/ - use crate::handlers::sign_tx::Tx; -use crate::utils::{concatenate, to_hex_all_caps}; +use crate::utils::concatenate; use crate::AppSW; -use core::str::from_utf8; use ledger_device_ui_sdk::bitmaps::{CROSSMARK, EYE, VALIDATE_14}; use ledger_device_ui_sdk::ui::{Field, MultiFieldReview}; use numtoa::NumToA; +const MAX_COIN_LENGTH: usize = 10; + +/// Displays a transaction and returns true if user approved it. +/// +/// This method can return [`AppSW::TxDisplayFail`] error if the coin name length is too long. +/// +/// # Arguments +/// +/// * `tx` - Transaction to be displayed for validation pub fn ui_display_tx(tx: &Tx) -> Result { - // Format amount value - let mut amount_buf = [0u8; 20]; - let mut amount_with_denom_buf = [0u8; 25]; - concatenate( - &["CRAB", " ", tx.value.numtoa_str(10, &mut amount_buf)], - &mut amount_with_denom_buf, - ); - let amount_str_with_denom = from_utf8(&amount_with_denom_buf) - .map_err(|_| AppSW::TxDisplayFail)? - .trim_matches(char::from(0)); + // Generate string for amount + let mut numtoa_buf = [0u8; 20]; + let mut value_buf = [0u8; 20 + MAX_COIN_LENGTH + 1]; - // Format destination address - let hex_addr_buf = to_hex_all_caps(tx.to).map_err(|_| AppSW::TxDisplayFail)?; - let hex_addr_str = from_utf8(&hex_addr_buf).map_err(|_| AppSW::TxDisplayFail)?; - let mut addr_with_prefix_buf = [0u8; 42]; - concatenate(&["0x", hex_addr_str], &mut addr_with_prefix_buf); - let hex_addr_str_with_prefix = - from_utf8(&addr_with_prefix_buf).map_err(|_| AppSW::TxDisplayFail)?; + let value_str = concatenate( + &[tx.coin, " ", tx.value.numtoa_str(10, &mut numtoa_buf)], + &mut value_buf, + ) + .map_err(|_| AppSW::TxDisplayFail)?; // Fails if value_buf is too small - // Format memo - let memo_str = from_utf8(&tx.memo[..tx.memo_len]).map_err(|_| AppSW::TxDisplayFail)?; + // Generate destination address string in hexadecimal format. + let mut to_str = [0u8; 42]; + to_str[..2].copy_from_slice("0x".as_bytes()); + hex::encode_to_slice(tx.to, &mut to_str[2..]).unwrap(); + to_str[2..].make_ascii_uppercase(); // Define transaction review fields let my_fields = [ Field { name: "Amount", - value: amount_str_with_denom, + value: value_str, }, Field { name: "Destination", - value: hex_addr_str_with_prefix, + value: core::str::from_utf8(&to_str).unwrap(), }, Field { name: "Memo", - value: memo_str, + value: tx.memo, }, ]; diff --git a/src/handlers/sign_tx.rs b/src/handlers/sign_tx.rs index f3668c5a..b83ebaa1 100644 --- a/src/handlers/sign_tx.rs +++ b/src/handlers/sign_tx.rs @@ -15,7 +15,7 @@ * limitations under the License. *****************************************************************************/ use crate::app_ui::sign::ui_display_tx; -use crate::utils::{read_bip32_path, slice_or_err, varint_read, MAX_ALLOWED_PATH_LEN}; +use crate::utils::{read_bip32_path, MAX_ALLOWED_PATH_LEN}; use crate::AppSW; use ledger_device_sdk::ecc::{Secp256k1, SeedDerive}; use ledger_device_sdk::io::Comm; @@ -23,54 +23,22 @@ use ledger_secure_sdk_sys::{ cx_hash_no_throw, cx_hash_t, cx_keccak_init_no_throw, cx_sha3_t, CX_LAST, CX_OK, }; +use serde::Deserialize; +use serde_json_core::from_slice; + const MAX_TRANSACTION_LEN: usize = 510; +#[derive(Deserialize)] pub struct Tx<'a> { #[allow(dead_code)] nonce: u64, + pub coin: &'a str, pub value: u64, - pub to: &'a [u8], - pub memo: &'a [u8], - pub memo_len: usize, -} - -// Implement deserialize for Tx from a u8 array -impl<'a> TryFrom<&'a [u8]> for Tx<'a> { - type Error = (); - fn try_from(raw_tx: &'a [u8]) -> Result { - if raw_tx.len() > MAX_TRANSACTION_LEN { - return Err(()); - } - - // Try to parse the transaction fields : - // Nonce - let nonce = u64::from_be_bytes(slice_or_err(raw_tx, 0, 8)?.try_into().map_err(|_| ())?); - // Destination address - let to = slice_or_err(raw_tx, 8, 20)?; - // Amount value - let value = u64::from_be_bytes(slice_or_err(raw_tx, 28, 8)?.try_into().map_err(|_| ())?); - // Memo length - let (memo_len_u64, memo_len_size) = varint_read(&raw_tx[36..])?; - let memo_len = memo_len_u64 as usize; - // Memo - let memo = slice_or_err(raw_tx, 36 + memo_len_size, memo_len)?; - - // Check memo ASCII encoding - if !memo[..memo_len].iter().all(|&byte| byte.is_ascii()) { - return Err(()); - } - - Ok(Tx { - nonce, - value, - to, - memo, - memo_len, - }) - } + #[serde(with = "hex::serde")] // Allows JSON deserialization from hex string + pub to: [u8; 20], + pub memo: &'a str, } -// #[derive(Copy, Clone)] pub struct TxContext { raw_tx: [u8; MAX_TRANSACTION_LEN], // raw transaction serialized raw_tx_len: usize, // length of raw transaction @@ -130,10 +98,9 @@ pub fn handler_sign_tx( return Ok(()); // Otherwise, try to parse the transaction } else { - let tx = match Tx::try_from(&ctx.raw_tx[..ctx.raw_tx_len]) { - Ok(tx) => tx, - Err(_) => return Err(AppSW::TxParsingFail), - }; + // Try to deserialize the transaction + let (tx, _): (Tx, usize) = + from_slice(&ctx.raw_tx[..ctx.raw_tx_len]).map_err(|_| AppSW::TxParsingFail)?; // Display transaction. If user approves // the transaction, sign it. Otherwise, // return a "deny" status word. diff --git a/src/main.rs b/src/main.rs index b18bda2d..876cb72d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -141,10 +141,14 @@ extern "C" fn sample_main() { } fn handle_apdu(comm: &mut Comm, ins: Ins, ctx: &mut TxContext) -> Result<(), AppSW> { - if comm.rx == 0 { + // Reject any APDU that does not have a minimum length of 4 bytes + // The APDU must have at least 4 bytes: CLA, INS, P1, P2 + if comm.rx < 4 { return Err(AppSW::WrongDataLength); } + let data = comm.get_data().map_err(|_| AppSW::WrongDataLength)?; + let apdu_metadata = comm.get_apdu_metadata(); if apdu_metadata.cla != CLA { @@ -168,14 +172,8 @@ fn handle_apdu(comm: &mut Comm, ins: Ins, ctx: &mut TxContext) -> Result<(), App if apdu_metadata.p1 > 1 || apdu_metadata.p2 != 0 { return Err(AppSW::WrongP1P2); } - - match comm.get_data() { - Ok(data) => { - if data.is_empty() { - return Err(AppSW::WrongDataLength); - } - } - Err(_) => return Err(AppSW::WrongDataLength), + if data.is_empty() { + return Err(AppSW::WrongDataLength); } return handler_get_public_key(comm, apdu_metadata.p1 == 1); @@ -187,14 +185,8 @@ fn handle_apdu(comm: &mut Comm, ins: Ins, ctx: &mut TxContext) -> Result<(), App { return Err(AppSW::WrongP1P2); } - - match comm.get_data() { - Ok(data) => { - if data.is_empty() { - return Err(AppSW::WrongDataLength); - } - } - Err(_) => return Err(AppSW::WrongDataLength), + if data.is_empty() { + return Err(AppSW::WrongDataLength); } return handler_sign_tx( diff --git a/src/utils.rs b/src/utils.rs index 59a72c28..7c87231e 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,41 +1,7 @@ use crate::AppSW; -use core::char; +use core::str::from_utf8; pub const MAX_ALLOWED_PATH_LEN: usize = 10; -const MAX_HEX_LEN: usize = 64; - -/// Convert to hex. Returns a static buffer of 64 bytes -#[inline] -pub fn to_hex(m: &[u8]) -> Result<[u8; MAX_HEX_LEN], ()> { - if 2 * m.len() > MAX_HEX_LEN { - return Err(()); - } - let mut hex = [0u8; MAX_HEX_LEN]; - let mut i = 0; - for c in m { - let c0 = char::from_digit((c >> 4).into(), 16).unwrap(); - let c1 = char::from_digit((c & 0xf).into(), 16).unwrap(); - hex[i] = c0 as u8; - hex[i + 1] = c1 as u8; - i += 2; - } - Ok(hex) -} - -/// Convert to an all capitalized string. Returns a static buffer of 255 bytes -#[inline] -pub fn to_hex_all_caps(m: &[u8]) -> Result<[u8; MAX_HEX_LEN], ()> { - match to_hex(m) { - Ok(hex) => { - let mut hex_all_caps = hex; - hex_all_caps - .iter_mut() - .for_each(|x| *x = x.to_ascii_uppercase()); - Ok(hex_all_caps) - } - Err(_) => Err(()), - } -} /// Convert serialized derivation path to u32 array elements pub fn read_bip32_path(data: &[u8], path: &mut [u32]) -> Result { @@ -64,65 +30,19 @@ pub fn read_bip32_path(data: &[u8], path: &mut [u32]) -> Result { Ok(idx) } -/// Concatenate multiple strings into a fixed-size array -pub fn concatenate(strings: &[&str], output: &mut [u8]) { +/// Returns concatenated strings, or an error if the concatenation buffer is too small. +pub fn concatenate<'a>(strings: &[&str], output: &'a mut [u8]) -> Result<&'a str, ()> { let mut offset = 0; for s in strings { let s_len = s.len(); - let copy_len = core::cmp::min(s_len, output.len() - offset); - - if copy_len > 0 { - output[offset..offset + copy_len].copy_from_slice(&s.as_bytes()[..copy_len]); - offset += copy_len; - } else { - // If the output buffer is full, stop concatenating. - break; - } - } -} - -/// Get a subslice of a slice or return an error -#[inline] -pub fn slice_or_err(slice: &[u8], start: usize, len: usize) -> Result<&[u8], ()> { - match slice.get(start..start + len) { - Some(s) => Ok(s), - None => Err(()), - } -} - -/// Read a varint from a slice -pub fn varint_read(input: &[u8]) -> Result<(u64, usize), ()> { - let mut bytes = [0u8; 8]; - let int_length: usize; - - if input.is_empty() { - return Err(()); - } - - let prefix = input[0]; - - if prefix == 0xFD { - if input.len() < 3 { - return Err(()); - } - int_length = 2; - } else if prefix == 0xFE { - if input.len() < 5 { + if offset + s_len > output.len() { return Err(()); } - int_length = 4; - } else if prefix == 0xFF { - if input.len() < 9 { - return Err(()); - } - int_length = 8; - } else { - return Ok((u64::from(prefix), 1)); + + output[offset..offset + s_len].copy_from_slice(s.as_bytes()); + offset += s_len; } - let buf = slice_or_err(input, 1, int_length)?; - bytes[..int_length].copy_from_slice(buf); - let result = u64::from_le_bytes(bytes); - Ok((result, int_length + 1)) + Ok(from_utf8(&output[..offset]).unwrap()) } diff --git a/tests/application_client/boilerplate_transaction.py b/tests/application_client/boilerplate_transaction.py index 02bd01f2..3b13034a 100644 --- a/tests/application_client/boilerplate_transaction.py +++ b/tests/application_client/boilerplate_transaction.py @@ -1,52 +1,30 @@ -from io import BytesIO -from typing import Union - -from .boilerplate_utils import read, read_uint, read_varint, write_varint, UINT64_MAX - +import json +from dataclasses import dataclass +from .boilerplate_utils import UINT64_MAX class TransactionError(Exception): pass - +@dataclass class Transaction: - def __init__(self, - nonce: int, - to: Union[str, bytes], - value: int, - memo: str, - do_check: bool = True) -> None: - self.nonce: int = nonce - self.to: bytes = bytes.fromhex(to[2:]) if isinstance(to, str) else to - self.value: int = value - self.memo: bytes = memo.encode("ascii") - - if do_check: - if not 0 <= self.nonce <= UINT64_MAX: - raise TransactionError(f"Bad nonce: '{self.nonce}'!") - - if not 0 <= self.value <= UINT64_MAX: - raise TransactionError(f"Bad value: '{self.value}'!") - - if len(self.to) != 20: - raise TransactionError(f"Bad address: '{self.to.hex()}'!") + nonce: int + coin: str + value: str + to: str + memo: str def serialize(self) -> bytes: - return b"".join([ - self.nonce.to_bytes(8, byteorder="big"), - self.to, - self.value.to_bytes(8, byteorder="big"), - write_varint(len(self.memo)), - self.memo - ]) - - @classmethod - def from_bytes(cls, hexa: Union[bytes, BytesIO]): - buf: BytesIO = BytesIO(hexa) if isinstance(hexa, bytes) else hexa - - nonce: int = read_uint(buf, 64, byteorder="big") - to: bytes = read(buf, 20) - value: int = read_uint(buf, 64, byteorder="big") - memo_len: int = read_varint(buf) - memo: str = read(buf, memo_len).decode("ascii") - - return cls(nonce=nonce, to=to, value=value, memo=memo) + if not 0 <= self.nonce <= UINT64_MAX: + raise TransactionError(f"Bad nonce: '{self.nonce}'!") + + if len(self.to) != 40: + raise TransactionError(f"Bad address: '{self.to}'!") + + # Serialize the transaction data to a JSON-formatted string + return json.dumps({ + "nonce": self.nonce, + "coin": self.coin, + "value": self.value, + "to": self.to, + "memo": self.memo + }).encode('utf-8') diff --git a/tests/snapshots/nanos/test_sign_tx_short_tx/00001.png b/tests/snapshots/nanos/test_sign_tx_short_tx/00001.png index 5472cd14..a66cb299 100644 Binary files a/tests/snapshots/nanos/test_sign_tx_short_tx/00001.png and b/tests/snapshots/nanos/test_sign_tx_short_tx/00001.png differ diff --git a/tests/snapshots/nanosp/test_sign_tx_short_tx/00001.png b/tests/snapshots/nanosp/test_sign_tx_short_tx/00001.png index 5e82af59..e4649001 100644 Binary files a/tests/snapshots/nanosp/test_sign_tx_short_tx/00001.png and b/tests/snapshots/nanosp/test_sign_tx_short_tx/00001.png differ diff --git a/tests/snapshots/nanox/test_sign_tx_short_tx/00001.png b/tests/snapshots/nanox/test_sign_tx_short_tx/00001.png index 5e82af59..e4649001 100644 Binary files a/tests/snapshots/nanox/test_sign_tx_short_tx/00001.png and b/tests/snapshots/nanox/test_sign_tx_short_tx/00001.png differ diff --git a/tests/test_error_cmd.py b/tests/test_error_cmd.py index 277f2f8a..2e45e9dd 100644 --- a/tests/test_error_cmd.py +++ b/tests/test_error_cmd.py @@ -34,15 +34,15 @@ def test_wrong_p1p2(backend): assert e.value.status == Errors.SW_WRONG_P1P2 # Ensure the app returns an error when a bad data length is used -# def test_wrong_data_length(backend): -# # APDUs must be at least 5 bytes: CLA, INS, P1, P2, Lc. -# with pytest.raises(ExceptionRAPDU) as e: -# backend.exchange_raw(b"E0030000") -# assert e.value.status == Errors.SW_WRONG_DATA_LENGTH -# # APDUs advertises a too long length -# with pytest.raises(ExceptionRAPDU) as e: -# backend.exchange_raw(b"E003000005") -# assert e.value.status == Errors.SW_WRONG_DATA_LENGTH +def test_wrong_data_length(backend): + # APDUs must be at least 4 bytes: CLA, INS, P1, P2. + with pytest.raises(ExceptionRAPDU) as e: + backend.exchange_raw(bytes.fromhex("E00300")) + assert e.value.status == Errors.SW_WRONG_DATA_LENGTH + # APDUs advertises a too long length + with pytest.raises(ExceptionRAPDU) as e: + backend.exchange_raw(bytes.fromhex("E003000005")) + assert e.value.status == Errors.SW_WRONG_DATA_LENGTH # Ensure there is no state confusion when trying wrong APDU sequences diff --git a/tests/test_sign_cmd.py b/tests/test_sign_cmd.py index 8e64813f..92b00ef5 100644 --- a/tests/test_sign_cmd.py +++ b/tests/test_sign_cmd.py @@ -26,8 +26,9 @@ def test_sign_tx_short_tx(firmware, backend, navigator, test_name): # Create the transaction that will be sent to the device for signing transaction = Transaction( nonce=1, - to="0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae", - value=666, + coin="CRAB", + value=777, + to="de0b295669a9fd93d5f28d9ec85e40f4cb697bae", memo="For u EthDev" ).serialize() @@ -70,8 +71,9 @@ def test_sign_tx_long_tx(firmware, backend, navigator, test_name): transaction = Transaction( nonce=1, - to="0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae", + coin="CRAB", value=666, + to="de0b295669a9fd93d5f28d9ec85e40f4cb697bae", memo=("This is a very long memo. " "It will force the app client to send the serialized transaction to be sent in chunk. " "As the maximum chunk size is 255 bytes we will make this memo greater than 255 characters. " @@ -109,8 +111,9 @@ def test_sign_tx_refused(firmware, backend, navigator, test_name): transaction = Transaction( nonce=1, - to="0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae", + coin="CRAB", value=666, + to="de0b295669a9fd93d5f28d9ec85e40f4cb697bae", memo="This transaction will be refused by the user" ).serialize()