Skip to content
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

Oxen test fixes and improvements #17

Merged
merged 17 commits into from
May 24, 2023
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
37 changes: 37 additions & 0 deletions .github/workflows/ci-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,40 @@ jobs:
with:
name: scan-build
path: scan-build

job_test:
name: Test
needs: job_nanoS_build_debug
runs-on: ubuntu-latest

container:
image: ghcr.io/ledgerhq/speculos:latest
ports:
- 1234:1234
- 9999:9999
- 40000:40000
- 41000:41000
- 42000:42000
- 43000:43000
options: --entrypoint /bin/bash

steps:
- name: Clone
uses: actions/checkout@v2

- name: Download app binary
uses: actions/download-artifact@v2
with:
name: oxen-app-nanoS-debug
path: bin

- name: Run test
run: |
nohup bash -c "python /speculos/speculos.py bin/app.elf --seed \"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about\" --apdu-port 9999 --api-port 5000 --display headless" > speculos.log 2<&1 &
pip install pytest
pytest
- name: Upload Speculos log
uses: actions/upload-artifact@v2
with:
name: speculos-log
path: speculos.log
17 changes: 1 addition & 16 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,7 @@ endif

#Oxen /44'/240'
APP_LOAD_PARAMS = --path "44'/240'" --curve secp256k1
# OR MAYBE ? APP_LOAD_PARAMS= --path "44'" --curve secp256k1 # based on boilerplate
ifeq ($(TARGET_NAME), TARGET_NANOX)
APP_LOAD_PARAMS+=--appFlags 0x200 # APPLICATION_FLAG_BOLOS_SETTINGS
else
APP_LOAD_PARAMS+=--appFlags 0x40
endif
APP_LOAD_PARAMS += --appFlags 0x240 # APPLICATION_FLAG_BOLOS_SETTINGS & global pin
APP_LOAD_PARAMS += $(COMMON_LOAD_PARAMS)

APPNAME = "Oxen"
Expand Down Expand Up @@ -163,13 +158,3 @@ dep/%.d: %.c Makefile

listvariants:
@echo VARIANTS COIN oxen


# We probably don't need this as the nanox default is 1500 and nanos default is 1024
# ifeq ($(TARGET_NAME),TARGET_NANOS)
# Rewrite the bolos sdk script to increase the stack size slightly

# script.ld: $(BOLOS_SDK)/script.ld Makefile
# sed -e 's/^STACK_SIZE\s*=\s*[0-9]\+;/STACK_SIZE = 712;/' $< >$@
# endif

32 changes: 28 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,20 @@ sudo docker build -t ledger-app-builder:latest .

Compile the app in the container with
```
sudo docker run --rm -ti -v "$(realpath .):/app" ledger-app-builder:latest
sudo docker run --rm -ti -v "$(realpath .):/app" --privileged ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder:latest

make CLANGPATH=/usr/lib/llvm-12/bin/ DEBUG=1 clean nanos
make CLANGPATH=/usr/lib/llvm-12/bin/ DEBUG=1 clean nanox
BOLOS_SDK=$NANOS_SDK make

```

Building for tests also requires DEBUG
```
BOLOS_SDK=$NANOS_SDK make DEBUG=1
```

Then the binary will be available in the host system under `nanos/bin` or `nanox/bin`
Specify the SDK desired to change what device its built for

Then the binary will be available in the host system under `bin`

## Loading the app onto your Ledger Nano S

Expand All @@ -149,6 +156,23 @@ To remove the app from your Ledger:

make delete_nanos

## Tests

Tests are run using pytest. Install with `pip install -U pytest`

Tests require speculos running the app and it receives messages from the test suite. Assumes speculos is saved at `~/speculos`

```
nohup bash -c "python3 ~/speculos/speculos.py bin/app.elf --seed \"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about\" --apdu-port 9999 --api-port 5000 --display headless" > speculos.log 2<&1 &
pytest
```

to run speculos in a view you can see and in a separate terminal instead run

```
python3 ~/speculos/speculos.py bin/app.elf --seed "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" --apdu-port 9999 --api-port 5000
```

## Useful links

* Oxen client CLI - https://github.com/oxen-io/oxen-core/releases
Expand Down
1 change: 1 addition & 0 deletions src/oxen_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ int monero_dispatch(void);
void clear_protocol(void);

int monero_apdu_get_network(void);
int monero_apdu_reset_network(void); // Only allowed in a debug build
int monero_apdu_put_key(void);
int monero_apdu_get_key(void);
int monero_apdu_display_address(void);
Expand Down
19 changes: 11 additions & 8 deletions src/oxen_dispatch.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,7 @@ int check_protocol(void) {
break;
}
// unknown protocol or hot protocol switch is not allowed
// FALL THROUGH

__attribute__((fallthrough));
default:
return SW_PROTOCOL_NOT_SUPPORTED;
}
Expand All @@ -74,6 +73,7 @@ int check_ins_access(void) {
case INS_LOCK_DISPLAY:
case INS_RESET:
case INS_GET_NETWORK:
case INS_RESET_NETWORK:
case INS_PUT_KEY:
case INS_GET_KEY:
case INS_DISPLAY_ADDRESS:
Expand All @@ -86,7 +86,7 @@ int check_ins_access(void) {
case INS_GEN_KEY_IMAGE:
case INS_GEN_KEY_IMAGE_SIGNATURE:
case INS_GEN_UNLOCK_SIGNATURE:
case INS_GEN_LNS_SIGNATURE:
case INS_GEN_ONS_SIGNATURE:
case INS_SECRET_KEY_TO_PUBLIC_KEY:
case INS_SECRET_KEY_ADD:
case INS_GENERATE_KEYPAIR:
Expand Down Expand Up @@ -150,6 +150,9 @@ int monero_dispatch(void) {
case INS_GET_NETWORK:
sw = monero_apdu_get_network();
break;
case INS_RESET_NETWORK:
sw = monero_apdu_reset_network();
break;
/* --- KEYS --- */
case INS_PUT_KEY:
sw = monero_apdu_put_key();
Expand Down Expand Up @@ -241,7 +244,7 @@ int monero_dispatch(void) {
case INS_OPEN_TX:
// state machine check
if (!(G_oxen_state.tx_state_ins == 0 ||
G_oxen_state.tx_state_ins == INS_GEN_LNS_SIGNATURE)) {
G_oxen_state.tx_state_ins == INS_GEN_ONS_SIGNATURE)) {
THROW(SW_COMMAND_NOT_ALLOWED);
}
// 2. command process
Expand Down Expand Up @@ -454,20 +457,20 @@ int monero_dispatch(void) {
update_protocol();
break;

/* --- LNS --- */
case INS_GEN_LNS_SIGNATURE:
/* --- ONS --- */
case INS_GEN_ONS_SIGNATURE:
if (G_oxen_state.tx_in_progress) THROW(SW_COMMAND_NOT_ALLOWED);
// Initialization: must not be in the middle of something else
if (OXEN_IO_P_EQUALS(0, 0) && G_oxen_state.tx_state_ins == 0)
sw = oxen_apdu_generate_lns_hash();
// [0,0]->[1,x] or [1,x]->[1,y] -- we receive data to hash in multiple parts
else if (G_oxen_state.tx_state_ins == INS_GEN_LNS_SIGNATURE &&
else if (G_oxen_state.tx_state_ins == INS_GEN_ONS_SIGNATURE &&
(OXEN_TX_STATE_P_EQUALS(0, 0) || G_oxen_state.tx_state_p1 == 1) &&
G_oxen_state.io_p1 == 1)
sw = oxen_apdu_generate_lns_hash();
// [1,0] -> [2,0] gives the account indices and uses the hash built in the [1,x] steps
// to make the signature
else if (OXEN_TX_STATE_INS_P_EQUALS(INS_GEN_LNS_SIGNATURE, 1, 0) &&
else if (OXEN_TX_STATE_INS_P_EQUALS(INS_GEN_ONS_SIGNATURE, 1, 0) &&
OXEN_IO_P_EQUALS(2, 0))
sw = oxen_apdu_generate_lns_signature();
else
Expand Down
13 changes: 7 additions & 6 deletions src/oxen_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -149,23 +149,23 @@ void oxen_install(unsigned char netId) {
unsigned char c;

// full reset data
nvm_write(N_oxen_state, NULL, sizeof(oxen_nv_state_t));
nvm_write((void*) N_oxen_state, NULL, sizeof(oxen_nv_state_t));

// set mode key
c = KEY_MODE_SEED;
nvm_write(&N_oxen_state->key_mode, &c, 1);
nvm_write((void*) &N_oxen_state->key_mode, &c, 1);

// set net id
nvm_write(&N_oxen_state->network_id, &netId, 1);
nvm_write((void*) &N_oxen_state->network_id, &netId, 1);

// write magic
nvm_write(N_oxen_state->magic, (void*) C_MAGIC, sizeof(C_MAGIC));
nvm_write((void*) N_oxen_state->magic, (void*) C_MAGIC, sizeof(C_MAGIC));

#if DEBUG_HWDEVICE
#ifdef DEBUG_HWDEVICE
// Default into always-export-view-key mode when doing a debug build because it's annoying to
// have to confirm the view key export every time when doing dev/debugging work.
unsigned char always_export = VIEWKEY_EXPORT_ALWAYS_ALLOW;
nvm_write(&N_oxen_state->viewkey_export_mode, &always_export, 1);
nvm_write((void*) &N_oxen_state->viewkey_export_mode, &always_export, 1);
#endif
}

Expand All @@ -176,6 +176,7 @@ const char* const oxen_supported_client[] = {
"8.",
"9.",
"10.",
"11.",
};
#define OXEN_SUPPORTED_CLIENT_SIZE (sizeof(oxen_supported_client) / sizeof(char*))

Expand Down
38 changes: 34 additions & 4 deletions src/oxen_key.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,18 @@ int monero_apdu_put_key(void) {
unsigned char raw[32];
unsigned char pub[32];
unsigned char sec[32];
int address_size = 95;
switch (N_oxen_state->network_id) {
case TESTNET:
case DEVNET:
address_size = 97;
break;
default:
break;
}

// option + priv/pub view key + priv/pub spend key + base58 address
if (G_oxen_state.io_length != (1 + 32 * 2 + 32 * 2 + 95)) {
if (G_oxen_state.io_length != (1 + 32 * 2 + 32 * 2 + address_size)) {
THROW(SW_WRONG_LENGTH);
return SW_WRONG_LENGTH;
}
Expand Down Expand Up @@ -157,6 +166,27 @@ int monero_apdu_get_network(void) {
return SW_OK;
}

int monero_apdu_reset_network(void) {
#ifdef DEBUG_HWDEVICE
monero_io_discard(1);
// On (and only on) a debug build we allow this command to reset the wallet to a requested
// network (without any confirmation). This is used for testing purposes (and is the only way
// to put the ledger into FAKECHAIN mode).
uint8_t nettype = G_oxen_state.io_p1;
if (
#ifndef OXEN_ALPHA
nettype == MAINNET ||
#endif
nettype == TESTNET || nettype == DEVNET || nettype == FAKECHAIN) {
oxen_install(nettype);
monero_init();
return SW_OK;
}
#endif
// On a non-debug build this command always fails.
THROW(SW_COMMAND_NOT_ALLOWED);
}

/* ----------------------------------------------------------------------- */
/* --- --- */
/* ----------------------------------------------------------------------- */
Expand Down Expand Up @@ -188,7 +218,7 @@ int monero_apdu_get_key(void) {
}
break;

#if DEBUG_HWDEVICE
#ifdef DEBUG_HWDEVICE
// get info
case 3: {
unsigned int path[5];
Expand Down Expand Up @@ -549,10 +579,10 @@ int oxen_apdu_generate_unlock_signature(void) {
return SW_OK;
}

// Generates an LNS hash
// Generates an ONS hash
int oxen_apdu_generate_lns_hash(void) {
if (G_oxen_state.io_p1 == 0) {
// Confirm the LNS initialization with the user
// Confirm the ONS initialization with the user
monero_io_discard(1);
ui_menu_lns_validation_display();
return 0;
Expand Down
30 changes: 5 additions & 25 deletions src/oxen_monero.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,31 +109,10 @@ const unsigned char encoded_block_sizes[] = {0, 2, 3, 5, 6, 7, 9, 10, 11};

static uint64_t uint_8be_to_64(const unsigned char* data, size_t size) {
uint64_t res = 0;
switch (9 - size) {
case 1:
res |= *data++;
case 2:
res <<= 8;
res |= *data++;
case 3:
res <<= 8;
res |= *data++;
case 4:
res <<= 8;
res |= *data++;
case 5:
res <<= 8;
res |= *data++;
case 6:
res <<= 8;
res |= *data++;
case 7:
res <<= 8;
res |= *data++;
case 8:
res <<= 8;
res |= *data;
break;
for (size_t i = 0; i < 8; i++) {
if (i >= size) break;
res <<= 8;
res |= data[i];
}

return res;
Expand Down Expand Up @@ -175,6 +154,7 @@ unsigned char oxen_wallet_address(char* str_b58,
break;
#ifndef OXEN_ALPHA
case MAINNET:
case FAKECHAIN:
if (paymentID)
prefix = MAINNET_CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
else if (is_subbadress)
Expand Down
2 changes: 1 addition & 1 deletion src/oxen_open_tx.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ int monero_apdu_open_tx(void) {

if (txversion != 4) THROW(SW_WRONG_DATA_RANGE);
if (!(txtype == TXTYPE_STANDARD || txtype == TXTYPE_UNLOCK || txtype == TXTYPE_STAKE ||
txtype == TXTYPE_LNS))
txtype == TXTYPE_ONS))
THROW(SW_WRONG_DATA_RANGE);

monero_io_discard(1);
Expand Down
12 changes: 6 additions & 6 deletions src/oxen_prehash.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ int monero_apdu_clsag_prehash_init(void) {
cx_keccak_init(&G_oxen_state.keccak_alt, 256);
}
}
// We always confirm fees for LNS because often this is the *only* confirmation for an LNS tx
// We always confirm fees for ONS because often this is the *only* confirmation for an ONS tx
unsigned char confirm_fee_mode =
G_oxen_state.tx_type == TXTYPE_LNS ? CONFIRM_FEE_ALWAYS : N_oxen_state->confirm_fee_mode;
G_oxen_state.tx_type == TXTYPE_ONS ? CONFIRM_FEE_ALWAYS : N_oxen_state->confirm_fee_mode;

oxen_hash_update(&G_oxen_state.keccak_alt,
G_oxen_state.io_buffer + G_oxen_state.io_offset,
Expand Down Expand Up @@ -71,7 +71,7 @@ int monero_apdu_clsag_prehash_init(void) {
if (amount > 0) {
// ask user
oxen_currency_str(amount, G_oxen_state.ux_amount);
if (G_oxen_state.tx_type == TXTYPE_LNS)
if (G_oxen_state.tx_type == TXTYPE_ONS)
ui_menu_lns_fee_validation_display();
else
ui_menu_fee_validation_display();
Expand Down Expand Up @@ -123,7 +123,7 @@ int monero_apdu_clsag_prehash_update(void) {

if (G_oxen_state.tx_sig_mode == TRANSACTION_CREATE_REAL) {
if (!is_change && G_oxen_state.tx_type != TXTYPE_STAKE &&
G_oxen_state.tx_type != TXTYPE_LNS) {
G_oxen_state.tx_type != TXTYPE_ONS) {
// encode dest adress
unsigned char pos =
oxen_wallet_address(G_oxen_state.ux_address, Aout, Bout, is_subaddress, NULL);
Expand Down Expand Up @@ -180,8 +180,8 @@ int monero_apdu_clsag_prehash_update(void) {
if (amount) {
oxen_currency_str(amount, G_oxen_state.ux_amount);
if (!is_change) {
if (G_oxen_state.tx_type == TXTYPE_STAKE || G_oxen_state.tx_type == TXTYPE_LNS) {
// If this is a stake or LNS tx then the non-change recipient must be ourself.
if (G_oxen_state.tx_type == TXTYPE_STAKE || G_oxen_state.tx_type == TXTYPE_ONS) {
// If this is a stake or ONS tx then the non-change recipient must be ourself.
if (memcmp(Aout, G_oxen_state.view_pub, 32) ||
memcmp(Bout, G_oxen_state.spend_pub, 32))
monero_lock_and_throw(SW_SECURITY_INTERNAL);
Expand Down
Loading
Loading