diff --git a/.gitignore b/.gitignore index fd916a87ba..7015d834d7 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ .ccls-cache compile_commands.json compile_flags.txt +.vscode # gnu global /src/GPATH diff --git a/py/send_message.py b/py/send_message.py index a56f66487c..47fa429ce9 100755 --- a/py/send_message.py +++ b/py/send_message.py @@ -18,6 +18,7 @@ # pylint: disable=too-many-lines import argparse +import socket import pprint import sys from typing import List, Any, Optional, Callable, Union, Tuple, Sequence @@ -41,6 +42,7 @@ FirmwareVersionOutdatedException, u2fhid, bitbox_api_protocol, + PhysicalLayer, ) import u2f @@ -1556,6 +1558,65 @@ def run(self) -> int: return 0 +def connect_to_simulator_bitbox(debug: bool) -> int: + """ + Connects and runs the main menu on host computer, + simulating a BitBox02 connected over USB. + """ + + class Simulator(PhysicalLayer): + """ + Simulator class handles the communication + with the firmware simulator + """ + + def __init__(self) -> None: + self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + port = 15423 + self.client_socket.bind(("", port)) + self.client_socket.listen(50) + print(f"Waiting for connection on port {port}") + self.connection, addr = self.client_socket.accept() + print(f"Connected to {addr}") + + def write(self, data: bytes) -> None: + self.connection.send(data[1:]) + if debug: + print(f"Written to the simulator:\n{data.hex()[2:]}") + + def read(self, size: int, timeout_ms: int) -> bytes: + res = self.connection.recv(64) + if debug: + print(f"Read from the simulator:\n{res.hex()}") + return res + + def __del__(self) -> None: + print("Simulator quit") + if self.connection: + self.connection.shutdown(socket.SHUT_RDWR) + self.connection.close() + + simulator = Simulator() + + device_info: devices.DeviceInfo = { + "serial_number": "v9.16.0", + "path": b"", + "product_string": "BitBox02BTC", + } + noise_config = bitbox_api_protocol.BitBoxNoiseConfig() + bitbox_connection = bitbox02.BitBox02( + transport=u2fhid.U2FHid(simulator), + device_info=device_info, + noise_config=noise_config, + ) + try: + bitbox_connection.check_min_version() + except FirmwareVersionOutdatedException as exc: + print("WARNING: ", exc) + + return SendMessage(bitbox_connection, debug).run() + + def connect_to_usb_bitbox(debug: bool, use_cache: bool) -> int: """ Connects and runs the main menu on a BitBox02 connected @@ -1643,6 +1704,11 @@ def main() -> int: parser = argparse.ArgumentParser(description="Tool for communicating with bitbox device") parser.add_argument("--debug", action="store_true", help="Print messages sent and received") parser.add_argument("--u2f", action="store_true", help="Use u2f menu instead") + parser.add_argument( + "--simulator", + action="store_true", + help="Connect to the BitBox02 simulator instead of a real BitBox02", + ) parser.add_argument( "--no-cache", action="store_true", help="Don't use cached or store noise keys" ) @@ -1663,6 +1729,9 @@ def main() -> int: return u2fapp.run() return 1 + if args.simulator: + return connect_to_simulator_bitbox(args.debug) + return connect_to_usb_bitbox(args.debug, not args.no_cache) diff --git a/src/rust/bitbox02-rust/src/workflow/confirm.rs b/src/rust/bitbox02-rust/src/workflow/confirm.rs index 19dc14701e..ef3ae196c5 100644 --- a/src/rust/bitbox02-rust/src/workflow/confirm.rs +++ b/src/rust/bitbox02-rust/src/workflow/confirm.rs @@ -31,5 +31,7 @@ pub async fn confirm(params: &Params<'_>) -> Result<(), UserAbort> { }; }); component.screen_stack_push(); + #[cfg(feature = "c-unit-testing")] + bitbox02::print_stdout(&format!("CONFIRM SCREEN START\nTITLE: {}\nBODY: {}\nCONFIRM SCREEN END\n", params.title, params.body)); option_no_screensaver(&result).await } diff --git a/src/rust/bitbox02-rust/src/workflow/mnemonic.rs b/src/rust/bitbox02-rust/src/workflow/mnemonic.rs index 075b28384a..396c234a22 100644 --- a/src/rust/bitbox02-rust/src/workflow/mnemonic.rs +++ b/src/rust/bitbox02-rust/src/workflow/mnemonic.rs @@ -22,6 +22,7 @@ use super::trinary_input_string; use alloc::boxed::Box; use alloc::string::String; +use alloc::string::ToString; use alloc::vec::Vec; use core::cell::RefCell; @@ -107,6 +108,7 @@ async fn confirm_word(choices: &[&str], title: &str) -> Result with_cancel("Recovery\nwords", &mut component, &result).await } +#[cfg(not(feature = "c-unit-testing"))] pub async fn show_and_confirm_mnemonic(words: &[&str]) -> Result<(), CancelError> { // Part 1) Scroll through words show_mnemonic(words).await?; @@ -140,6 +142,25 @@ pub async fn show_and_confirm_mnemonic(words: &[&str]) -> Result<(), CancelError Ok(()) } +#[cfg(feature = "c-unit-testing")] +pub async fn show_and_confirm_mnemonic(words: &[&str]) -> Result<(), CancelError> { + let _ = confirm::confirm(&confirm::Params { + title: "", + body: "Please confirm\neach word", + accept_only: true, + accept_is_nextarrow: true, + ..Default::default() + }) + .await; + + for word in words.iter() { + bitbox02::println_stdout(word); + } + bitbox02::println_stdout("Words confirmed"); + + Ok(()) +} + /// Given 11/17/23 initial words, this function returns a list of candidate words for the last word, /// such that the resulting bip39 phrase has a valid checksum. There are always exactly 8 such words /// for 24 word mnemonics, 32 words for 18 word mnemonics and 128 words for 12 word mnemonics. @@ -289,6 +310,7 @@ async fn get_12th_18th_word( } } +#[cfg(not(feature = "c-unit-testing"))] /// Retrieve a BIP39 mnemonic sentence of 12, 18 or 24 words from the user. pub async fn get() -> Result, CancelError> { let num_words: usize = match choose("How many words?", "12", "18", "24").await { @@ -403,6 +425,17 @@ pub async fn get() -> Result, CancelError> { )) } +#[cfg(feature = "c-unit-testing")] +pub async fn get() -> Result, CancelError> { + let words = "boring mistake dish oyster truth pigeon viable emerge sort crash wire portion cannon couple enact box walk height pull today solid off enable tide"; + bitbox02::println_stdout("Restored from recovery words below:"); + bitbox02::println_stdout(words); + return Ok(zeroize::Zeroizing::new( + words + .to_string() + )); +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/rust/bitbox02-rust/src/workflow/status.rs b/src/rust/bitbox02-rust/src/workflow/status.rs index d5a38ec30f..4bac5b8e91 100644 --- a/src/rust/bitbox02-rust/src/workflow/status.rs +++ b/src/rust/bitbox02-rust/src/workflow/status.rs @@ -21,5 +21,7 @@ pub async fn status(title: &str, status_success: bool) { *result.borrow_mut() = Some(()); }); component.screen_stack_push(); + #[cfg(feature = "c-unit-testing")] + bitbox02::print_stdout(&format!("STATUS SCREEN START\nTITLE: {}\nSTATUS SCREEN END\n", title)); option_no_screensaver(&result).await } diff --git a/src/rust/bitbox02-rust/src/workflow/trinary_input_string.rs b/src/rust/bitbox02-rust/src/workflow/trinary_input_string.rs index 62f8d6f259..9b300d5f89 100644 --- a/src/rust/bitbox02-rust/src/workflow/trinary_input_string.rs +++ b/src/rust/bitbox02-rust/src/workflow/trinary_input_string.rs @@ -49,6 +49,8 @@ pub async fn enter( bitbox02::ui::trinary_input_string_set_input(&mut component, preset); } component.screen_stack_push(); + #[cfg(feature = "c-unit-testing")] + bitbox02::print_stdout(&format!("ENTER SCREEN START\nTITLE: {}\nENTER SCREEN END\n", params.title)); option(&result) .await .or(Err(super::cancel::Error::Cancelled)) diff --git a/src/rust/bitbox02/src/lib.rs b/src/rust/bitbox02/src/lib.rs index e5e05280d1..5c46f21429 100644 --- a/src/rust/bitbox02/src/lib.rs +++ b/src/rust/bitbox02/src/lib.rs @@ -183,6 +183,14 @@ pub fn print_stdout(msg: &str) { } } +#[cfg(any(feature = "testing", feature = "c-unit-testing"))] +pub fn println_stdout(msg: &str) { + unsafe { + bitbox02_sys::printf(crate::util::str_to_cstr_vec(msg).unwrap().as_ptr()); + bitbox02_sys::printf(crate::util::str_to_cstr_vec("\n").unwrap().as_ptr()); + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/rust/bitbox02/src/ui.rs b/src/rust/bitbox02/src/ui.rs index ba3af064d0..b25ac139c7 100644 --- a/src/rust/bitbox02/src/ui.rs +++ b/src/rust/bitbox02/src/ui.rs @@ -16,6 +16,7 @@ mod types; #[cfg_attr(feature = "testing", path = "ui/ui_stub.rs")] +#[cfg_attr(feature = "c-unit-testing", path = "ui/ui_stub_c.rs")] // We don't actually use ui::ui anywhere, we re-export below. #[allow(clippy::module_inception)] mod ui; diff --git a/src/rust/bitbox02/src/ui/ui_stub_c.rs b/src/rust/bitbox02/src/ui/ui_stub_c.rs new file mode 100644 index 0000000000..ee3eac4c5f --- /dev/null +++ b/src/rust/bitbox02/src/ui/ui_stub_c.rs @@ -0,0 +1,163 @@ +// Copyright 2020 Shift Crypto AG +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Stubs for testing. + +pub use super::types::{ + AcceptRejectCb, ConfirmParams, ContinueCancelCb, Font, MenuParams, SelectWordCb, TrinaryChoice, + TrinaryChoiceCb, TrinaryInputStringParams, +}; + +use crate::input::SafeInputString; + +use core::marker::PhantomData; + +extern crate alloc; + +pub struct Component<'a> { + is_pushed: bool, + _p: PhantomData<&'a ()>, +} + +impl<'a> Component<'a> { + pub fn screen_stack_push(&mut self) { + if self.is_pushed { + panic!("component pushed twice"); + } + self.is_pushed = true; + } +} + +impl<'a> Drop for Component<'a> { + fn drop(&mut self) { + if !self.is_pushed { + panic!("component not pushed"); + } + } +} + +pub fn trinary_input_string_create<'a, F>( + _params: &TrinaryInputStringParams, + mut confirm_callback: F, + _cancel_callback: Option>, +) -> Component<'a> +where + F: FnMut(SafeInputString) + 'a, +{ + confirm_callback(SafeInputString::new()); + Component { + is_pushed: false, + _p: PhantomData, + } +} + +pub fn confirm_create<'a, F>(_params: &ConfirmParams, mut result_callback: F) -> Component<'a> +where + F: FnMut(bool) + 'a, +{ + result_callback(true); + Component { + is_pushed: false, + _p: PhantomData, + } +} + +pub fn screen_process() {} + +pub fn status_create<'a, F>(_text: &str, _status_success: bool, mut callback: F) -> Component<'a> +where + F: FnMut() + 'a, +{ + callback(); + Component { + is_pushed: false, + _p: PhantomData, + } +} + +pub fn sdcard_create<'a, F>(_insert: bool, mut continue_callback: F) -> Component<'a> +where + F: FnMut() + 'a, +{ + continue_callback(); + Component { + is_pushed: false, + _p: PhantomData, + } +} + +pub fn menu_create(_params: MenuParams<'_>) -> Component<'_> { + panic!("not implemented"); +} + +pub fn trinary_choice_create<'a>( + _message: &'a str, + _label_left: &'a str, + _label_middle: &'a str, + _label_right: &'a str, + _chosen_callback: TrinaryChoiceCb, +) -> Component<'a> { + panic!("not implemented") +} + +pub fn confirm_transaction_address_create<'a, 'b>( + _amount: &'a str, + _address: &'a str, + mut callback: AcceptRejectCb<'b>, +) -> Component<'b> { + callback(true); + Component { + is_pushed: false, + _p: PhantomData, + } +} + +pub fn confirm_transaction_fee_create<'a, 'b>( + _amount: &'a str, + _fee: &'a str, + _longtouch: bool, + mut callback: AcceptRejectCb<'b>, +) -> Component<'b> { + callback(true); + Component { + is_pushed: false, + _p: PhantomData, + } +} + +pub fn trinary_input_string_set_input(_component: &mut Component, _word: &str) { + panic!("not implemented") +} + +pub fn with_lock_animation(f: F) { + f() +} + +pub fn screen_stack_pop_all() {} + +pub fn progress_create<'a>(_title: &str) -> Component<'a> { + Component { + is_pushed: false, + _p: PhantomData, + } +} + +pub fn progress_set(_component: &mut Component, _progress: f32) {} + +pub fn empty_create<'a>() -> Component<'a> { + Component { + is_pushed: false, + _p: PhantomData, + } +} \ No newline at end of file diff --git a/test/.DS_Store b/test/.DS_Store new file mode 100644 index 0000000000..76c12f1b68 Binary files /dev/null and b/test/.DS_Store differ diff --git a/test/unit-test/CMakeLists.txt b/test/unit-test/CMakeLists.txt index 8f79f0516e..45f928c0d9 100644 --- a/test/unit-test/CMakeLists.txt +++ b/test/unit-test/CMakeLists.txt @@ -240,6 +240,8 @@ set(TEST_LIST "-Wl,--wrap=screen_process" ugui "" + simulator + "" ) find_package(CMocka REQUIRED) diff --git a/test/unit-test/framework/mock_securechip.c b/test/unit-test/framework/mock_securechip.c index 957c42d840..8ad23088b4 100644 --- a/test/unit-test/framework/mock_securechip.c +++ b/test/unit-test/framework/mock_securechip.c @@ -23,6 +23,8 @@ #include #include +#define COUNTER_MAX_VALUE ((uint32_t)2097151) + static uint32_t _u2f_counter; bool securechip_update_keys(void) @@ -86,10 +88,12 @@ bool securechip_attestation_sign(const uint8_t* msg, uint8_t* signature_out) bool securechip_monotonic_increments_remaining(uint32_t* remaining_out) { - return false; + *remaining_out = COUNTER_MAX_VALUE; + return true; } bool securechip_model(securechip_model_t* model_out) { - return false; + *model_out = SECURECHIP_ATECC608B; + return true; } diff --git a/test/unit-test/test_simulator.c b/test/unit-test/test_simulator.c new file mode 100644 index 0000000000..6fab217d96 --- /dev/null +++ b/test/unit-test/test_simulator.c @@ -0,0 +1,368 @@ +// Copyright 2023 Shift Crypto AG +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* +infinite loop reading stdin line by line +each line is e.g. a hex encoded string containing the USB message that is sent by the host (e.g. the +USB msgs that is sent by the bitbox-api-rs client library) process the usb message print the +response usb messages to stdout, again one message per line, hex encoded +*/ + +/* +Uninitialized menu : + Set up a new wallet : works + Restore from backup : to do + Restore from mnemonic : works, words are fixed + List device info : works + Reboot into bootloader : does not work, firmware gets stuck without an error + Check if SD card inserted : works + +Initialized menu : + List device info : works + Change device name : works + Get root fingerprint : works + Retrieve zpub of first account : works + Retrieve a BTC address : works + Retrieve a BTC Multisig address : works + Retrieve a BTC policy address : to do, Exception("Noise communication failed.") + Sign a BTC tx : to do, something polls but dont know what + Sign a BTC Message : works + List backups : works + Check backup : works + Show mnemonic : works + Create backup : works + Reboot into bootloader : to do + Check if SD card inserted : works + Insert SD card : works but effectively does nothing + Remove SD card : works but effectively does nothing + Toggle BIP39 Mnemonic Passphrase : works but it toggles for next unlock, which is not done in +the simulator + Retrieve Ethereum xpub : works + Retrieve Ethereum address : works + Retrieve ERC20 address with long token name : works + Sign Ethereum tx : works Sign Ethereum Message : works + Sign Ethereum Typed Message (EIP-712) : works + Cardano : functionality disabled(I think it is disabled for +testing environment) + Show Electrum wallet encryption key : works + Reset Device : to do + + Typos in the menu : + Backup created sucessfully + recovey words + + Problems : + In Sign Ethereum tx choice, the option 8(eip1559) fails due to decoding fail in rlp library + */ + +#include "hww.h" +#include "usb/usb_packet.c" +#include "usb/usb_processing.c" +#include "usb/usb_processing.h" +#include "workflow/idle_workflow.h" +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#define BUFFER_SIZE 1024 +// #define CLI +// #define PIPE +#define SOCKET + +static uint8_t _out_report[USB_HID_REPORT_OUT_SIZE]; +char out_buffer[2 * USB_HID_REPORT_OUT_SIZE]; +int fd_read, fd_write; +int sockfd; +int data_len; + +#ifndef SOCKET +int hex_to_uint8(char high, char low, uint8_t* num) +{ + *num = 0; + + if (high >= '0' && high <= '9') { + *num += (high - '0') << 4; + } else if (high >= 'A' && high <= 'F') { + *num += (high - 'A' + 10) << 4; + } else if (high >= 'a' && high <= 'f') { + *num += (high - 'a' + 10) << 4; + } else { + return 1; + } + + if (low >= '0' && low <= '9') { + *num += (low - '0'); + } else if (low >= 'A' && low <= 'F') { + *num += (low - 'A' + 10); + } else if (low >= 'a' && low <= 'f') { + *num += (low - 'a' + 10); + } else { + return 1; + } + + return 0; +} +#endif + +// Copied from test_memory_functional.c +void _memory_setup_rand_mock(uint8_t* buf_out) +{ + static uint8_t ctr = 0; + static uint8_t fixtures[][32] = { + // salt root + "\xbd\xb9\xca\x49\x75\xe5\x9e\x1b\x61\xd9\x14\x1c\x5e\x79\x68\x8c\xba\x7b\x39\x89\xb5\x2b" + "\x78\x2d\xe2\xe7\xe4\x9b\x07\xec\x8f\xae", + // io_protection_key + "\x28\x30\x9e\x5a\x2e\x3b\xcf\x4a\xac\x94\xc0\xe5\x90\x10\xfa\x34\x92\xe1\x08\x39\xef\xb5" + "\xb6\x61\x92\xad\x18\xf6\x6a\x80\x51\x0b", + // io_protection_key_split + "\xae\x5b\xe4\x4d\x8b\x71\xa6\x04\x1a\x7e\x97\x33\xe5\x5f\x8c\x88\xb7\x9d\xd5\x52\x10\x76" + "\x24\xe0\xa9\x16\xc1\x0d\x87\x55\xe0\x4e", + // authorization_key + "\x62\xc7\x41\xd9\xce\x78\x32\xe8\x56\xec\x06\xf6\x35\x1c\xef\xcd\x9e\x7c\x5c\xa6\x07\x93" + "\x8a\xbb\x70\x97\x70\xa5\xf2\xdb\xeb\xcb", + // authorization_key_split + "\x20\x74\x2d\x5a\x58\x2f\x1f\x25\xb6\xe9\xd1\xc1\xe8\xb1\xef\xfb\x40\xcf\xac\x85\x56\x67" + "\xea\x7f\x49\x96\x8a\xf7\xf7\xeb\x5c\x19", + // encryption_key + "\xed\x18\x37\x84\xcb\xd2\x97\xf9\xc2\xc2\x41\xd0\xdd\x7c\xd1\x6d\x62\x36\x6c\x44\xb8\x33" + "\xdd\xf2\xc0\x12\xfb\x4b\x49\xe1\xe8\xf3", + // encryption_key_split + "\x19\xf6\x0e\xe8\x25\xe7\x52\x15\x0d\x30\x88\x17\x34\x8c\x0f\xa6\xb3\xfe\x4f\x60\x4c\x85" + "\xc1\x7e\x2e\xb9\x7a\xda\x60\x4a\x47\x6f", + }; + memcpy(buf_out, fixtures[ctr], 32); + ctr++; +} + +#ifdef CLI +int get_usb_message_stdin(char* buffer, uint8_t* input) +{ + printf("Enter the USB message:\n"); + char* fes = fgets(buffer, BUFFER_SIZE, stdin); + if (fes == NULL) { + return 0; + } + buffer[strcspn(buffer, "\n\r")] = 0; + for (int i = 0; i < 2 * USB_HID_REPORT_OUT_SIZE; i += 2) { + hex_to_uint8(buffer[i], buffer[i + 1], &input[i / 2]); + } + printf("\n"); + return 1; +} + +void send_usb_message_stdout(void) +{ + struct queue* q = queue_hww_queue(); + const uint8_t* data = queue_pull(q); + printf("Write to the Python client:\n"); + if (data != NULL) { + for (int i = 0; i < USB_HID_REPORT_OUT_SIZE; i++) { + printf("%02x", data[i]); + } + } + printf("\n"); +} +#endif + +#ifdef PIPE +int get_usb_message_pipe(char* buffer, uint8_t* input) +{ + int num_read = read(fd_read, buffer, 2 * USB_HID_REPORT_OUT_SIZE); + buffer[strcspn(buffer, "\n\r")] = 0; + for (int i = 0; i < 2 * USB_HID_REPORT_OUT_SIZE; i += 2) { + hex_to_uint8(buffer[i], buffer[i + 1], &input[i / 2]); + } + return num_read; +} + +void send_usb_message_pipe(void) +{ + struct queue* q = queue_hww_queue(); + const uint8_t* data = queue_pull(q); + if (data != NULL) { + for (int i = 0; i < USB_HID_REPORT_OUT_SIZE; i++) { + sprintf(out_buffer, "%02x", data[i]); + } + } + write(fd_write, out_buffer, 2 * USB_HID_REPORT_OUT_SIZE); +} +#endif + +#ifdef SOCKET +int get_usb_message_socket(char* buffer, uint8_t* input) +{ + int num_read = read(sockfd, buffer, USB_HID_REPORT_OUT_SIZE); + for (int i = 0; i < USB_HID_REPORT_OUT_SIZE; i++) { + input[i] = (uint8_t)buffer[i]; + } + return num_read; +} + +void send_usb_message_socket(void) +{ + struct queue* q = queue_hww_queue(); + const uint8_t* data = queue_pull(q); + if (data != NULL) { + data_len = 256 * (int)data[5] + (int)data[6]; + for (int i = 0; i < USB_HID_REPORT_OUT_SIZE; i++) { + out_buffer[i] = (char)data[i]; + // printf("%02x", out_buffer[i]); + } + write(sockfd, out_buffer, USB_HID_REPORT_OUT_SIZE); + } +} +#endif + +bool simulate_firmware_execution(uint8_t* input) +{ + // usb_packet_process but it puts the packet into queue + // chain of call: usb_packet_process -> usb_processing_enqueue -> _build_packet + memcpy(_out_report, input, sizeof(_out_report)); + + // Understand the received packet and enqueue it + // Flow : usb_processing_process -> _usb_consume_incoming_packets -> + // _usb_arbitrate_packet -> _usb_execute_packet -> + usb_packet_process((const USB_FRAME*)_out_report); + // Consume and process the enqueued packet + // Sending the output packet is done in usb_processing_process, after + // _usb_consume_incoming_packets ends + + rust_workflow_spin(); + rust_async_usb_spin(); + + // FAIL : in_packet->cmd = 193 '\301' + // rust_async_usb_spin(); -> This call while processing the packet causes the error, + // specifically this line : task.as_mut().poll(context) in bb02_async.rs + usb_processing_process(usb_processing_hww()); + + // For getting the output packet, _send_next from hid_hww.c is called + // Flow : _send_next <- _sent_done <- hid_hww_setup <- _hww_endpoint_available + // <- usb_start <- _init_communication <- idle_workflow_blocking + //(These functions are all initialized in main function) +} + +int main(void) +{ + char buffer[BUFFER_SIZE]; + uint8_t input[BUFFER_SIZE]; + int (*get_message)(char*, uint8_t*); + void (*send_message)(void); + bool memory_success, sd_success; + int temp_len; + +#ifdef SOCKET + int portno; + struct sockaddr_in serv_addr; + struct hostent* server; + portno = 15423; + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) { + perror("ERROR opening socket"); + exit(1); + } + server = gethostbyname("host.docker.internal"); + if (server == NULL) { + fprintf(stderr, "ERROR, no such host\n"); + exit(0); + } + bzero((char*)&serv_addr, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + bcopy((char*)server->h_addr_list[0], (char*)&serv_addr.sin_addr.s_addr, server->h_length); + serv_addr.sin_port = htons(portno); + if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { + perror("ERROR connecting"); + exit(1); + } + get_message = get_usb_message_socket; + send_message = send_usb_message_socket; +#elif PIPE + const char* fifo_read_path = "./tmp/fifo_sim_read"; + const char* fifo_write_path = "./tmp/fifo_sim_write"; + printf("opening fifo\n"); + fd_read = open(fifo_read_path, O_RDONLY | O_NONBLOCK); + if (fd_read == -1) { + perror("open"); + return 1; + } + printf("opened read fifo\n"); + fd_write = open(fifo_write_path, O_WRONLY); + if (fd_write == -1) { + perror("open"); + return 1; + } + printf("opened write fifo\n"); + get_message = get_usb_message_pipe; + send_message = send_usb_message_pipe; +#else + get_message = get_usb_message_stdin; + send_message = send_usb_message_stdout; +#endif + + /* When device starts, before firmware_main_loop, there are many initializations done, + they can be found in firmware.c. Apparently, not calling idle_workflow_blocking() causes + wrong address reads + */ + // system_init(); + usb_processing_init(); + // For getting the output packet from the firmware + usb_processing_set_send(usb_processing_hww(), send_message); + printf("USB setup success\n"); + hww_setup(); + printf("HWW setup success\n"); + // Memory initialization + sd_success = sd_format(); + printf("Sd card setup %s\n", sd_success ? "success" : "failed"); + mock_memory_factoryreset(); + memory_interface_functions_t ifs = { + .random_32_bytes = _memory_setup_rand_mock, + }; + memory_success = memory_setup(&ifs); + printf("Memory setup %s\n", memory_success ? "success" : "failed"); + // Enable/configure SmartEEPROM, seems required for wallet setup + smarteeprom_bb02_config(); + bitbox02_smarteeprom_init(); + // mock_unlocked_using_mnemonic(); + // For the sake of ordering in firmware original setup, taken to last + idle_workflow_blocking(); + + while (1) { + if (!get_message(buffer, input)) break; + simulate_firmware_execution(input); + + temp_len = data_len - (USB_HID_REPORT_OUT_SIZE - 7); + while (temp_len > 0) { + usb_processing_process(usb_processing_hww()); + temp_len -= (USB_HID_REPORT_OUT_SIZE - 5); + } + } + +#ifdef SOCKET + close(sockfd); +#elif PIPE + close(fd_read); + close(fd_write); +#endif + return 0; +} \ No newline at end of file