-
Notifications
You must be signed in to change notification settings - Fork 100
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
simulator: implement a simulator for bitbox02 device #1167
Conversation
@@ -0,0 +1,163 @@ | |||
// Copyright 2020 Shift Crypto AG |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wrong year
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
//! Stubs for testing. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
elaborate that this is for the C unit tests and the simulator in particular.
src/rust/bitbox02/src/ui.rs
Outdated
@@ -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")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename to sth clearer like ui_stub_c_unit_tests.rs
@@ -23,6 +23,8 @@ | |||
#include <string.h> | |||
#include <wally_crypto.h> | |||
|
|||
#define COUNTER_MAX_VALUE ((uint32_t)2097151) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where is this value coming from? you can just return any arbitrary value in securechip_monotonic_increments_remaining()
test/unit-test/test_simulator.c
Outdated
@@ -0,0 +1,368 @@ | |||
// Copyright 2023 Shift Crypto AG |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2023-2024
@@ -240,6 +240,8 @@ set(TEST_LIST | |||
"-Wl,--wrap=screen_process" | |||
ugui | |||
"" | |||
simulator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as it is in the the list of tests, the cmocka lib will be linked to it, but is cmocka used? ideally it should not be needed for the simulator. can you try linking the simulator without cmocka?
test/unit-test/test_simulator.c
Outdated
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, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
= random_32_bytes_mcu
and #include<random.h>
at the top, which uses C's rand()
to get pseudorandom numbers, no need for the fixtures.
.random_32_bytes = _memory_setup_rand_mock, | ||
}; | ||
memory_success = memory_setup(&ifs); | ||
printf("Memory setup %s\n", memory_success ? "success" : "failed"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
properly abort/fail on errors
5bb774a
to
96e1d59
Compare
|
||
use sha2::{Digest, Sha256}; | ||
cfg_if::cfg_if! { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think with that many changes, it's better to add a new file mnemonic_stub_c_unit_tests.rs, so the real firmware code does not become obfuscated by all this
df29892
to
49f512c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looking nice, just a few nits!
use alloc::string::ToString; | ||
|
||
pub async fn show_and_confirm_mnemonic(words: &[&str]) -> Result<(), CancelError> { | ||
let _ = confirm::confirm(&confirm::Params { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure what the purpose is of the contents of this function - it could just be empty right?
test/unit-test/test_simulator.c
Outdated
#include <sys/socket.h> | ||
|
||
#define BUFFER_SIZE 1024 | ||
#define SOCKET |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unused
test/unit-test/test_simulator.c
Outdated
int (*get_message)(char*, uint8_t*); | ||
void (*send_message)(void); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not needed? can just use get_usb_message_socket
and send_usb_message_socket
directly
test/unit-test/test_simulator.c
Outdated
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]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's the point of this var? why nt just data
in the next line in write()
?
test/unit-test/test_simulator.c
Outdated
} | ||
} | ||
|
||
void simulate_firmware_execution(uint8_t* input) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const uint8_t*
test/unit-test/test_simulator.c
Outdated
|
||
void simulate_firmware_execution(uint8_t* input) | ||
{ | ||
memcpy(_out_report, input, sizeof(_out_report)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no need for _out_report and this copy? can just use input
on the next line
test/unit-test/test_simulator.c
Outdated
bool memory_success, sd_success; | ||
int temp_len; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no need to declare the vars at the top, code is imo nicer when the vars are just declared on first use, since we don't care much about stack use. bool memory_success = memory_setup(...)
etc.
test/unit-test/test_simulator.c
Outdated
int portno; | ||
struct sockaddr_in serv_addr; | ||
struct hostent* server; | ||
portno = 15423; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same, one line is better than two lines ;) int portno = 15423
. could also prefix with const
since it's not changing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tACK - very nice work!
#define BUFFER_SIZE 1024 | ||
|
||
int data_len; | ||
int sockfd; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make sockfd*
an argument to the get_usb_message_socket function, then you don't need a global sockfd variable and it can be local.
looks like we can't do much about data_len being global for now, that seems it would require some refactoring of the usb processing functions, which we don't need to go into now 😄
int temp_len; | ||
while (1) { | ||
if (!get_usb_message_socket(input)) break; | ||
simulate_firmware_execution(input); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some comments here explaining how long packets are read and send would be helpful for the future, it's not very obvious because the usb stack is so convoluted.
HWI is thinking of updating its support policy such that supported wallets must implement a simulator/emulator. See bitcoin-core/HWI#685. That's why a simulator is implemented for bitbox02, supporting functionalities of its API. This first version of the simulator is capable of nearly every functionality of a normal Bitbox02 device, without promising any security or production use. Its main aim is to be able to run unit tests for features and test the API. In addition, it will be configured to run automated tests in CI, which helps both us and HWI integration. Right now, the simulator has 3 different ways to communicate with a client: giving inputs/getting output from CLI, using pipes or opening sockets. Socket is the most convenient and reliable choice in this version. It expects the clients to open a socket on port 15432, which is selected intentionally to avoid possible conflicts. The simulator resides with C unit-tests since it uses same mocks, therefore it can be built by `make unit-test`. Lastly, Python client implemented in `py/send_message.py` is updated to support communicating with simulator with the socket configuration mentioned above. Client can be started up with `./py/send_message.py --simulator` command. To run the simulator, `build-build/bin/test_simulator` command is sufficient. Signed-off-by: asi345 <inanata15@gmail.com>
HWI is thinking of updating its support policy such that supported wallets must implement a simulator/emulator. See bitcoin-core/HWI#685. That's why a simulator is implemented for bitbox02, supporting functionalities of its API.
This first version of the simulator is capable of nearly every functionality of a normal Bitbox02 device, without promising any security or production use. Its main aim is to be able to run unit tests for features and test the API.
In addition, it will be configured to run automated tests in CI, which helps both us and HWI integration.
Right now, the simulator has 3 different ways to communicate with a client: giving inputs/getting output from CLI, using pipes or opening sockets. Socket is the most convenient and reliable choice in this version. It expects the clients to open a socket on port 15432, which is selected intentionally to avoid possible conflicts.
The simulator resides with C unit-tests since it uses same mocks, therefore it can be built by
make unit-test
.Lastly, Python client implemented in
py/send_message.py
is updated to support communicating with simulator with the socket configuration mentioned above. Client can be started up with./py/send_message.py --simulator
command. To run the simulator,build-build/bin/test_simulator
command is sufficient.