diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c48a68..2330202 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,7 +137,6 @@ file(GLOB_RECURSE LIB_SRC ${CMAKE_CURRENT_SOURCE_DIR}/app/src/parser_impl.c ${CMAKE_CURRENT_SOURCE_DIR}/app/src/parser_impl_common.c ${CMAKE_CURRENT_SOURCE_DIR}/app/src/crypto_helper.c - ${CMAKE_CURRENT_SOURCE_DIR}/app/src/crypto_helper/chacha.c # ### ${CMAKE_CURRENT_SOURCE_DIR}/deps/blake2/ref/blake2b-ref.c @@ -150,7 +149,6 @@ target_include_directories(app_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/deps/ledger-zxlib/include ${CMAKE_CURRENT_SOURCE_DIR}/app/src ${CMAKE_CURRENT_SOURCE_DIR}/app/src/lib - ${CMAKE_CURRENT_SOURCE_DIR}/app/src/crypto_helper ${CMAKE_CURRENT_SOURCE_DIR}/app/src/common ${CMAKE_CURRENT_SOURCE_DIR}/app/rust/include ${CMAKE_CURRENT_SOURCE_DIR}/deps/blake2/ref diff --git a/README.md b/README.md index ad476c8..fc24635 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # Ledger Ironfish app + [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![GithubActions](https://github.com/Zondax/ledger-ironfish/actions/workflows/main.yml/badge.svg)](https://github.com/Zondax/ledger-ironfish/blob/main/.github/workflows/main.yaml) @@ -26,15 +27,15 @@ Please: - **Do not use a Ledger device with funds for development purposes.** - **Have a separate and marked device that is used ONLY for development and testing** - ## Download and install a prerelease -*Once the app is approved by Ledger, it will be available in their app store (Ledger Live). -You can get builds generated by Github Actions from the release tab. THESE ARE UNVETTED DEVELOPMENT RELEASES* +_Once the app is approved by Ledger, it will be available in their app store (Ledger Live). +You can get builds generated by Github Actions from the release tab. THESE ARE UNVETTED DEVELOPMENT RELEASES_ -Download a release from here (https://github.com/Zondax/ledger-ironfish/releases). You only need `installer.sh` +Download a release from here (). You only need `installer.sh` If the file is not executable, run + ```sh chmod +x ./installer.sh ``` @@ -56,9 +57,10 @@ then run: ``` - Install Docker CE - - Instructions can be found here: https://docs.docker.com/install/ + - Instructions can be found here: - We only officially support Ubuntu. Install the following packages: + ``` sudo apt update && apt-get -y install build-essential git wget cmake \ libssl-dev libgmp-dev autoconf libtool @@ -67,12 +69,12 @@ then run: - Install `node > v13.0`. We typically recommend using `n` - You will need python 3 and then run - - `make deps` + - `make deps` - This project requires Ledger firmware 2.0 - - The current repository keeps track of Ledger's SDK but it is possible to override it by changing the git submodule. + - The current repository keeps track of Ledger's SDK but it is possible to override it by changing the git submodule. -*Warning*: Some IDEs may not use the same python interpreter or virtual enviroment as the one you used when running `pip`. +_Warning_: Some IDEs may not use the same python interpreter or virtual enviroment as the one you used when running `pip`. If you see conan is not found, check that you installed the package in the same interpreter as the one that launches `cmake`. ## How to build ? @@ -83,15 +85,19 @@ If you see conan is not found, check that you installed the package in the same - Building the app itself If you installed the what is described above, just run: + ```bash - make + APP_TESTING=1 make ``` + The APP_TESTING flag enables the use of specific test values during compilation, which is essential for testing with Zemu. + ## Running tests - Running rust tests (x64) If you installed the what is described above, just run: + ```bash make rust_test ``` @@ -99,6 +105,7 @@ If you see conan is not found, check that you installed the package in the same - Running C/C++ tests (x64) If you installed the what is described above, just run: + ```bash make cpp_test ``` @@ -114,9 +121,9 @@ If you see conan is not found, check that you installed the package in the same > What is Zemu?? Great you asked!! > As part of this project, we are making public a beta version of our internal testing+emulation framework for Ledger apps. > -> Npm Package here: https://www.npmjs.com/package/@zondax/zemu +> Npm Package here: > -> Repo here: https://github.com/Zondax/zemu +> Repo here: Let's go! First install everything: > At this moment, if you change the app you will need to run `make` before running the test again. @@ -137,20 +144,21 @@ To run a single specific test: ## Using a real device -### How to prepare your DEVELOPMENT! device: +### How to prepare your DEVELOPMENT! device -> You can use an emulated device for development. This is only required if you are using a physical device +> You can use an emulated device for development. This is only required if you are using a physical device > -> **Please do not use a Ledger device with funds for development purposes.** +> **Please do not use a Ledger device with funds for development purposes.** >> -> **Have a separate and marked device that is used ONLY for development and testing** +> **Have a separate and marked device that is used ONLY for development and testing** There are a few additional steps that increase reproducibility and simplify development: **1 - Ensure your device works in your OS** + - In Linux hosts it might be necessary to adjust udev rules, etc. - Refer to Ledger documentation: https://support.ledger.com/hc/en-us/articles/115005165269-Fix-connection-issues + Refer to Ledger documentation: **2 - Set a test mnemonic** @@ -181,7 +189,6 @@ Many of our integration tests expect the device to be configured with a known te - Run `make dev_ca`. The device will receive a development certificate to avoid constant manual confirmations. - ### Loading into your development device The Makefile will build the firmware in a docker container and leave the binary in the correct directory. @@ -194,6 +201,7 @@ The Makefile will build the firmware in a docker container and leave the binary - Upload to a device The following command will upload the application to the ledger. _Warning: The application will be deleted before uploading._ + ``` make load # Builds and loads the app to the device ``` diff --git a/app/Makefile.version b/app/Makefile.version index c1c7e1f..aa329cf 100644 --- a/app/Makefile.version +++ b/app/Makefile.version @@ -3,4 +3,4 @@ APPVERSION_M=0 # This is the minor version APPVERSION_N=1 # This is the patch version -APPVERSION_P=0 +APPVERSION_P=1 diff --git a/app/rust/Cargo.lock b/app/rust/Cargo.lock index 7fee2cf..fb3d0fe 100644 --- a/app/rust/Cargo.lock +++ b/app/rust/Cargo.lock @@ -2,6 +2,16 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + [[package]] name = "arrayref" version = "0.3.9" @@ -48,12 +58,72 @@ dependencies = [ "subtle", ] +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + [[package]] name = "constant_time_eq" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "ff" version = "0.13.0" @@ -70,6 +140,16 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "group" version = "0.13.0" @@ -81,6 +161,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + [[package]] name = "jubjub" version = "0.10.0" @@ -95,12 +184,35 @@ dependencies = [ "subtle", ] +[[package]] +name = "libc" +version = "0.2.159" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + [[package]] name = "panic-halt" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812" +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "radium" version = "0.7.0" @@ -118,6 +230,7 @@ name = "rslib" version = "0.1.0" dependencies = [ "blake2b_simd", + "chacha20poly1305", "jubjub", "panic-halt", ] @@ -134,6 +247,28 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "wyz" version = "0.5.1" @@ -142,3 +277,9 @@ checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" dependencies = [ "tap", ] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/app/rust/Cargo.toml b/app/rust/Cargo.toml index 32e6a03..6b2bcc1 100644 --- a/app/rust/Cargo.toml +++ b/app/rust/Cargo.toml @@ -14,6 +14,7 @@ crate-type = ["staticlib"] [dependencies] jubjub = { version = "0.10.0", default-features = false } blake2b_simd = { version = "1.0.0", default-features = false } +chacha20poly1305 = { version = "0.10.1", default-features = false } [target.thumbv6m-none-eabi.dev-dependencies] panic-halt = "0.2.0" diff --git a/app/rust/include/rslib.h b/app/rust/include/rslib.h index 4dd71f8..790f018 100644 --- a/app/rust/include/rslib.h +++ b/app/rust/include/rslib.h @@ -15,8 +15,10 @@ parser_error_t scalar_multiplication(const uint8_t input[32], constant_key_t key parser_error_t randomizeKey(const uint8_t key[KEY_LENGTH], const uint8_t randomness[KEY_LENGTH], uint8_t output[KEY_LENGTH]); parser_error_t compute_sbar(const uint8_t s[KEY_LENGTH], const uint8_t r[KEY_LENGTH], const uint8_t rsk[KEY_LENGTH], uint8_t sbar[32]); -parser_error_t shared_secret(uint8_t secret_key[32], uint8_t other_public_key[32], const uint8_t reference_public_key[32], - uint8_t output[32]); +parser_error_t decrypt_note(const uint8_t *note, uint8_t secret_key[32], uint8_t other_public_key[32], + const uint8_t reference_public_key[32], uint8_t output[32]); +parser_error_t decrypt_note_encryption_keys(const uint8_t *ovk, const uint8_t *note, uint8_t output[ENCRYPTED_SHARED_KEY_SIZE]); + #ifdef __cplusplus } #endif diff --git a/app/rust/src/constants.rs b/app/rust/src/constants.rs index cd78861..218b5dc 100644 --- a/app/rust/src/constants.rs +++ b/app/rust/src/constants.rs @@ -74,10 +74,22 @@ pub const MEMO_SIZE: usize = 32; pub const AMOUNT_VALUE_SIZE: usize = 8; pub const ASSET_ID_LENGTH: usize = 32; pub const PUBLIC_ADDRESS_SIZE: usize = 32; - +pub const EPHEMEREAL_PUBLIC_KEY_SIZE: usize = 32; +pub const NOTE_COMMITMENT_SIZE: usize = 32; pub const ENCRYPTED_NOTE_SIZE: usize = SCALAR_SIZE + MEMO_SIZE + AMOUNT_VALUE_SIZE + ASSET_ID_LENGTH + PUBLIC_ADDRESS_SIZE; - +pub const ENCRYPTED_NOTE_OFFSET: usize = + VALUE_COMMITMENT_SIZE + NOTE_COMMITMENT_SIZE + EPHEMEREAL_PUBLIC_KEY_SIZE; pub const ENCRYPTED_SHARED_KEY_SIZE: usize = 64; pub const NOTE_ENCRYPTION_KEY_SIZE: usize = ENCRYPTED_SHARED_KEY_SIZE + MAC_SIZE; +pub const NOTE_LEN_TO_HASH: usize = + VALUE_COMMITMENT_SIZE + NOTE_COMMITMENT_SIZE + EPHEMEREAL_PUBLIC_KEY_SIZE; +pub const VALUE_COMMITMENT_SIZE: usize = 32; +pub const MERKLE_NOTE_SIZE: usize = VALUE_COMMITMENT_SIZE + + NOTE_COMMITMENT_SIZE + + EPHEMEREAL_PUBLIC_KEY_SIZE + + ENCRYPTED_NOTE_SIZE + + MAC_SIZE + + NOTE_ENCRYPTION_KEY_SIZE; +pub const ENCRYPTED_NOTE_SIZE_WITH_MAC: usize = ENCRYPTED_NOTE_SIZE + MAC_SIZE; diff --git a/app/rust/src/lib.rs b/app/rust/src/lib.rs index ed8e591..27b5ec0 100644 --- a/app/rust/src/lib.rs +++ b/app/rust/src/lib.rs @@ -19,9 +19,12 @@ use core::panic::PanicInfo; +use chacha20poly1305::{aead::generic_array::GenericArray, ChaCha20Poly1305, Key, KeyInit, Nonce}; use constants::{ - DIFFIE_HELLMAN_PERSONALIZATION, ENCRYPTED_NOTE_SIZE, ENCRYPTED_SHARED_KEY_SIZE, MAC_SIZE, - NOTE_ENCRYPTION_KEY_SIZE, SHARED_KEY_PERSONALIZATION, SPENDING_KEY_GENERATOR, + DIFFIE_HELLMAN_PERSONALIZATION, ENCRYPTED_NOTE_OFFSET, ENCRYPTED_NOTE_SIZE, + ENCRYPTED_NOTE_SIZE_WITH_MAC, ENCRYPTED_SHARED_KEY_SIZE, EPHEMEREAL_PUBLIC_KEY_SIZE, MAC_SIZE, + MERKLE_NOTE_SIZE, NOTE_COMMITMENT_SIZE, NOTE_ENCRYPTION_KEY_SIZE, NOTE_LEN_TO_HASH, + SHARED_KEY_PERSONALIZATION, SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_SIZE, }; mod constants; @@ -123,12 +126,90 @@ fn hash_shared_secret(shared_secret: &[u8; 32], reference_public_key: &AffinePoi hash_result } +#[inline(never)] +pub fn calculate_key_for_encryption_keys( + outgoing_view_key: &[u8; 32], + note: &[u8; NOTE_LEN_TO_HASH], +) -> Result<[u8; 32], ParserError> { + let mut key_input = [0u8; 128]; + + key_input[0..32].copy_from_slice(outgoing_view_key); + key_input[32..128].copy_from_slice(note); + + // Store the hash state in a variable + let hash_state = Blake2b::new() + .hash_length(32) + .personal(SHARED_KEY_PERSONALIZATION) + .hash(&key_input); + + // Get the hash result as bytes + let hash_result = hash_state.as_bytes(); + + // Attempt to convert the hash result into an array and handle the error + hash_result + .try_into() + .map_err(|_| ParserError::ParserUnexpectedError) // Return error if conversion fails +} + +fn decrypt( + key: &[u8; 32], + ciphertext: &[u8], + plaintext: &mut [u8; SIZE], +) -> ParserError { + use chacha20poly1305::AeadInPlace; + + // Check if the ciphertext length is sufficient + if ciphertext.len() < SIZE { + return ParserError::ParserUnexpectedError; // Return an error if insufficient data + } + + let decryptor = ChaCha20Poly1305::new(Key::from_slice(key)); + + plaintext.copy_from_slice(&ciphertext[..SIZE]); + + // Attempt decryption + match decryptor.decrypt_in_place_detached( + &Nonce::default(), + &[], + plaintext, + ciphertext[SIZE..].into(), + ) { + Ok(_) => ParserError::ParserOk, + Err(_) => ParserError::ParserUnexpectedError, // Handle decryption failure + } +} + #[no_mangle] -pub extern "C" fn shared_secret( +pub extern "C" fn decrypt_note_encryption_keys( + ovk: &[u8; 32], + note: &[u8; MERKLE_NOTE_SIZE], + output: &mut [u8; ENCRYPTED_SHARED_KEY_SIZE], +) -> ParserError { + let key_result = + calculate_key_for_encryption_keys(ovk, ¬e[..NOTE_LEN_TO_HASH].try_into().unwrap()); + + // Handle the result of calculate_key_for_encryption_keys + let key = match key_result { + Ok(k) => k, + Err(_) => return ParserError::ParserUnexpectedError, // Return error if key calculation fails + }; + + let ciphertext: &[u8; NOTE_ENCRYPTION_KEY_SIZE] = note + [MERKLE_NOTE_SIZE - NOTE_ENCRYPTION_KEY_SIZE..] + .try_into() + .unwrap(); + decrypt::(&key, ciphertext, output); + + ParserError::ParserOk +} + +#[no_mangle] +pub extern "C" fn decrypt_note( + note: &[u8; MERKLE_NOTE_SIZE], secret_key: &[u8; 32], other_public_key: &[u8; 32], reference_public_key: &[u8; 32], - output: &mut [u8; 32], + output: &mut [u8; ENCRYPTED_NOTE_SIZE], ) -> ParserError { let secret_key = Fr::from_bytes(secret_key); if secret_key.is_none().into() { @@ -145,9 +226,13 @@ pub extern "C" fn shared_secret( let shared_secret = other_public_key.unwrap() * secret_key.unwrap(); let affine = AffinePoint::from(&shared_secret).to_bytes(); - *output = hash_shared_secret(&affine, &reference_public_key.unwrap()); + let hash = hash_shared_secret(&affine, &reference_public_key.unwrap()); - ParserError::ParserOk + let ciphertext: &[u8; ENCRYPTED_NOTE_SIZE_WITH_MAC] = note + [ENCRYPTED_NOTE_OFFSET..ENCRYPTED_NOTE_OFFSET + ENCRYPTED_NOTE_SIZE_WITH_MAC] + .try_into() + .unwrap(); + decrypt::(&hash, ciphertext, output) } #[cfg(not(test))] diff --git a/app/src/blake2s/blake2.h b/app/src/blake2s/blake2.h index c5e8f46..bfd6289 100644 --- a/app/src/blake2s/blake2.h +++ b/app/src/blake2s/blake2.h @@ -55,16 +55,15 @@ typedef struct blake2sp_state__ { } blake2sp_state; BLAKE2_PACKED(struct blake2s_param__ { - uint8_t digest_length; /* 1 */ - uint8_t key_length; /* 2 */ - uint8_t fanout; /* 3 */ - uint8_t depth; /* 4 */ - uint32_t leaf_length; /* 8 */ - uint32_t node_offset; /* 12 */ - uint16_t xof_length; /* 14 */ - uint8_t node_depth; /* 15 */ - uint8_t inner_length; /* 16 */ - /* uint8_t reserved[0]; */ + uint8_t digest_length; /* 1 */ + uint8_t key_length; /* 2 */ + uint8_t fanout; /* 3 */ + uint8_t depth; /* 4 */ + uint32_t leaf_length; /* 8 */ + uint32_t node_offset; /* 12 */ + uint16_t xof_length; /* 14 */ + uint8_t node_depth; /* 15 */ + uint8_t inner_length; /* 16 */ uint8_t salt[BLAKE2S_SALTBYTES]; /* 24 */ uint8_t personal[BLAKE2S_PERSONALBYTES]; /* 32 */ }); @@ -81,7 +80,6 @@ typedef struct blake2xs_state__ { /* Padded structs result in a compile-time error */ enum { BLAKE2_DUMMY_1 = 1 / (int)(sizeof(blake2s_param) == BLAKE2S_OUTBYTES), - // BLAKE2_DUMMY_2 = 1/(int)(sizeof(blake2b_param) == BLAKE2B_OUTBYTES) }; /* Streaming API */ diff --git a/app/src/blake2s/blake2s-ref.c b/app/src/blake2s/blake2s-ref.c index 87c8de9..59347c9 100644 --- a/app/src/blake2s/blake2s-ref.c +++ b/app/src/blake2s/blake2s-ref.c @@ -90,7 +90,6 @@ int blake2s_init(blake2s_state *S, size_t outlen) { store16(&P->xof_length, 0); P->node_depth = 0; P->inner_length = 0; - /* memset(P->reserved, 0, sizeof(P->reserved) ); */ memset(P->salt, 0, sizeof(P->salt)); memset(P->personal, 0, sizeof(P->personal)); return blake2s_init_param(S, P); @@ -114,7 +113,6 @@ int blake2s_init_with_personalization(blake2s_state *S, size_t outlen, const uin store16(&P->xof_length, 0); P->node_depth = 0; P->inner_length = 0; - /* memset(P->reserved, 0, sizeof(P->reserved) ); */ memset(P->salt, 0, sizeof(P->salt)); memset(P->personal, 0, sizeof(P->personal)); memcpy(P->personal, personalization, personalizationlen); @@ -138,7 +136,6 @@ int blake2s_init_key(blake2s_state *S, size_t outlen, const void *key, size_t ke store16(&P->xof_length, 0); P->node_depth = 0; P->inner_length = 0; - /* memset(P->reserved, 0, sizeof(P->reserved) ); */ memset(P->salt, 0, sizeof(P->salt)); memset(P->personal, 0, sizeof(P->personal)); diff --git a/app/src/crypto_helper.c b/app/src/crypto_helper.c index eedcb3f..ed97b57 100644 --- a/app/src/crypto_helper.c +++ b/app/src/crypto_helper.c @@ -18,10 +18,10 @@ #include #include "coin.h" -#include "crypto_helper/chacha.h" #include "keys_personalizations.h" #include "rslib.h" #include "zxformat.h" +#include "parser_common.h" #if defined(LEDGER_SPECIFIC) #include "cx.h" @@ -38,7 +38,7 @@ parser_error_t convertKey(const uint8_t spendingKey[KEY_LENGTH], const uint8_t m sizeof(EXPANDED_SPEND_BLAKE2_KEY))); ASSERT_CX_OK(cx_blake2b_update(&ctx, spendingKey, KEY_LENGTH)); ASSERT_CX_OK(cx_blake2b_update(&ctx, &modifier, 1)); - cx_blake2b_final(&ctx, output); + ASSERT_CX_OK(cx_blake2b_final(&ctx, output)); #else blake2b_state state = {0}; blake2b_init_with_personalization(&state, BLAKE2B_OUTPUT_LEN, (const uint8_t *)EXPANDED_SPEND_BLAKE2_KEY, @@ -49,7 +49,7 @@ parser_error_t convertKey(const uint8_t spendingKey[KEY_LENGTH], const uint8_t m #endif if (reduceWideByte) { - from_bytes_wide(output, outputKey); + CHECK_ERROR(from_bytes_wide(output, outputKey)); } else { memcpy(outputKey, output, KEY_LENGTH); } @@ -60,7 +60,7 @@ parser_error_t generate_key(const uint8_t expandedKey[KEY_LENGTH], constant_key_ if (keyType >= PointInvalidKey) { return parser_value_out_of_range; } - scalar_multiplication(expandedKey, keyType, output); + CHECK_ERROR(scalar_multiplication(expandedKey, keyType, output)); return parser_ok; } @@ -72,9 +72,6 @@ parser_error_t computeIVK(const ak_t ak, const nk_t nk, ivk_t ivk) { blake2s_final(&state, ivk, KEY_LENGTH); ivk[31] &= 0x07; - // if ivk == [0; 32] { - // return Err(IronfishError::new(IronfishErrorKind::InvalidViewingKey)); - // } return parser_ok; } @@ -178,7 +175,7 @@ static parser_error_t h_star(bytes_t a, const uint8_t randomizedPublicKey[32], c ASSERT_CX_OK(cx_blake2b_update(&ctx, a.ptr, a.len)); ASSERT_CX_OK(cx_blake2b_update(&ctx, randomizedPublicKey, 32)); ASSERT_CX_OK(cx_blake2b_update(&ctx, transactionHash, 32)); - cx_blake2b_final(&ctx, hash); + ASSERT_CX_OK(cx_blake2b_final(&ctx, hash)); #else blake2b_state state = {0}; blake2b_init_with_personalization(&state, BLAKE2B_OUTPUT_LEN, (const uint8_t *)SIGNING_REDJUBJUB, @@ -206,15 +203,15 @@ zxerr_t crypto_signRedjubjub(const uint8_t randomizedPrivateKey[KEY_LENGTH], con // Compute r and rbar uint8_t r[32] = {0}; bytes_t a = {.ptr = rng, .len = RNG_LEN}; - h_star(a, randomizedPublicKey, transactionHash, r); - scalar_multiplication(r, SpendingKeyGenerator, rbar); + CHECK_PARSER_OK(h_star(a, randomizedPublicKey, transactionHash, r)); + CHECK_PARSER_OK(scalar_multiplication(r, SpendingKeyGenerator, rbar)); // compute s and sbar uint8_t s[32] = {0}; a.ptr = rbar; a.len = 32; - h_star(a, randomizedPublicKey, transactionHash, s); - compute_sbar(s, r, randomizedPrivateKey, sbar); + CHECK_PARSER_OK(h_star(a, randomizedPublicKey, transactionHash, s)); + CHECK_PARSER_OK(compute_sbar(s, r, randomizedPrivateKey, sbar)); MEMZERO(r, sizeof(r)); MEMZERO(s, sizeof(s)); @@ -227,91 +224,42 @@ parser_error_t crypto_get_ovk(uint8_t ovk[KEY_LENGTH]) { uint8_t buffer[4 * KEY_LENGTH] = {0}; if (crypto_generateSaplingKeys(buffer, sizeof(buffer), ViewKeys) != zxerr_ok) { + MEMZERO(buffer, sizeof(buffer)); return parser_unexpected_error; } memcpy(ovk, buffer + 3 * KEY_LENGTH, KEY_LENGTH); + MEMZERO(buffer, sizeof(buffer)); return parser_ok; } #endif -parser_error_t crypto_calculate_key_for_encryption_keys(const uint8_t *note, const uint8_t ovk[KEY_LENGTH], - uint8_t output[32]) { - if (ovk == NULL || note == NULL) { - return parser_no_data; - } - -#if defined(LEDGER_SPECIFIC) - cx_blake2b_t ctx = {0}; - ASSERT_CX_OK(cx_blake2b_init2_no_throw(&ctx, 256, NULL, 0, (uint8_t *)SHARED_KEY_PERSONALIZATION, - sizeof(SHARED_KEY_PERSONALIZATION))); - ASSERT_CX_OK(cx_blake2b_update(&ctx, ovk, 32)); - ASSERT_CX_OK(cx_blake2b_update(&ctx, note, VALUE_COMMITMENT_SIZE + NOTE_COMMITMENT_SIZE + EPHEMERAL_PUBLIC_KEY_SIZE)); - cx_blake2b_final(&ctx, output); -#else - blake2b_state state = {0}; - blake2b_init_with_personalization(&state, 32, (const uint8_t *)SHARED_KEY_PERSONALIZATION, - sizeof(SHARED_KEY_PERSONALIZATION)); - blake2b_update(&state, ovk, 32); - blake2b_update(&state, note, VALUE_COMMITMENT_SIZE + NOTE_COMMITMENT_SIZE + EPHEMERAL_PUBLIC_KEY_SIZE); - blake2b_final(&state, output, 32); -#endif - - return parser_ok; -} - parser_error_t crypto_decrypt_merkle_note(parser_tx_t *txObj, const uint8_t *m_note, const uint8_t ovk[KEY_LENGTH]) { if (ovk == NULL || m_note == NULL) { return parser_no_data; } - // Calculate the key used to encrypt the shared keys for a note - uint8_t encryption_key[32] = {0}; - CHECK_ERROR(crypto_calculate_key_for_encryption_keys(m_note, ovk, encryption_key)); - CHECK_APP_CANARY() - - // Decrypt the note encryption keys uint8_t note_encryption_key[ENCRYPTED_SHARED_KEY_SIZE] = {0}; - uint8_t cc_nonce[CHACHA_NONCE_SIZE] = {0}; - CHECK_ERROR(chacha(note_encryption_key, sizeof(note_encryption_key), m_note + NOTE_ENCRYPTION_KEYS_OFFSET, - ENCRYPTED_SHARED_KEY_SIZE, encryption_key, cc_nonce, 1)); -#if defined(LEDGER_SPECIFIC) - io_seproxyhal_io_heartbeat(); -#endif - CHECK_APP_CANARY() - - // Extract public address and secret key from the note encryption key - uint8_t public_address[PUBLIC_ADDRESS_SIZE] = {0}; - uint8_t secret_key[SECRET_KEY_SIZE] = {0}; - MEMCPY(public_address, note_encryption_key, PUBLIC_ADDRESS_SIZE); - MEMCPY(secret_key, note_encryption_key + PUBLIC_ADDRESS_SIZE, SECRET_KEY_SIZE); + if (decrypt_note_encryption_keys(ovk, m_note, note_encryption_key) != parser_ok) { + MEMZERO(note_encryption_key, sizeof(note_encryption_key)); + return parser_unexpected_error; + } - // Compute the shared key - uint8_t shared_key[32] = {0}; + uint8_t plain_text[ENCRYPTED_NOTE_SIZE] = {0}; const uint8_t *ephemeral_public_key = m_note + VALUE_COMMITMENT_SIZE + NOTE_COMMITMENT_SIZE; - CHECK_ERROR(shared_secret(secret_key, public_address, ephemeral_public_key, shared_key)); -#if defined(LEDGER_SPECIFIC) - io_seproxyhal_io_heartbeat(); -#endif - CHECK_APP_CANARY() + if (decrypt_note(m_note, note_encryption_key + PUBLIC_ADDRESS_SIZE, note_encryption_key, ephemeral_public_key, + plain_text) != parser_ok) { + MEMZERO(note_encryption_key, sizeof(note_encryption_key)); + MEMZERO(plain_text, sizeof(plain_text)); + return parser_unexpected_error; + } - // Finally decrypt the note - uint8_t plain_text[ENCRYPTED_NOTE_SIZE] = {0}; - CHECK_ERROR(chacha(plain_text, sizeof(plain_text), m_note + ENCRYPTED_NOTE_OFFSET, ENCRYPTED_NOTE_SIZE, shared_key, - cc_nonce, 1)); -#if defined(LEDGER_SPECIFIC) - io_seproxyhal_io_heartbeat(); -#endif - CHECK_APP_CANARY() - // Fill the txObj with the decrypted note txObj->outputs.decrypted_note.value = *(uint64_t *)(plain_text + SCALAR_SIZE); MEMCPY(txObj->outputs.decrypted_note.asset_id, plain_text + SCALAR_SIZE + AMOUNT_VALUE_SIZE + MEMO_SIZE, ASSET_ID_LENGTH); - MEMCPY(txObj->outputs.decrypted_note.owner, public_address, PUBLIC_ADDRESS_SIZE); + MEMCPY(txObj->outputs.decrypted_note.owner, note_encryption_key, PUBLIC_ADDRESS_SIZE); // Clear sensitive data MEMZERO(note_encryption_key, sizeof(note_encryption_key)); - MEMZERO(secret_key, sizeof(secret_key)); - MEMZERO(shared_key, sizeof(shared_key)); MEMZERO(plain_text, sizeof(plain_text)); return parser_ok; } diff --git a/app/src/crypto_helper/chacha.c b/app/src/crypto_helper/chacha.c deleted file mode 100644 index 0652632..0000000 --- a/app/src/crypto_helper/chacha.c +++ /dev/null @@ -1,139 +0,0 @@ -/* Copyright (c) 2014, Google Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -// Adapted from the public domain, estream code by D. Bernstein. -// Adapted by Zondax - -#include "chacha.h" - -#include "coin.h" -#if defined(LEDGER_SPECIFIC) -#include "cx.h" -#include "os.h" -#endif -#include "zxmacros.h" - -#define U8TO32_LITTLE(p) \ - (((uint32_t)((p)[0])) | ((uint32_t)((p)[1]) << 8) | ((uint32_t)((p)[2]) << 16) | ((uint32_t)((p)[3]) << 24)) -// sigma contains the ChaCha constants, which happen to be an ASCII string. -static const uint8_t sigma[16] = {'e', 'x', 'p', 'a', 'n', 'd', ' ', '3', '2', '-', 'b', 'y', 't', 'e', ' ', 'k'}; -#define ROTATE(v, n) (((v) << (n)) | ((v) >> (32 - (n)))) -// QUARTERROUND updates a, b, c, d with a ChaCha "quarter" round. -#define QUARTERROUND(a, b, c, d) \ - x[a] += x[b]; \ - x[d] = ROTATE(x[d] ^ x[a], 16); \ - x[c] += x[d]; \ - x[b] = ROTATE(x[b] ^ x[c], 12); \ - x[a] += x[b]; \ - x[d] = ROTATE(x[d] ^ x[a], 8); \ - x[c] += x[d]; \ - x[b] = ROTATE(x[b] ^ x[c], 7); - -void CRYPTO_hchacha20(uint8_t out[32], const uint8_t key[32], const uint8_t nonce[16]) { - uint32_t x[16]; - MEMCPY(x, sigma, sizeof(sigma)); - MEMCPY(&x[4], key, 32); - MEMCPY(&x[12], nonce, 16); - for (size_t i = 0; i < 20; i += 2) { - QUARTERROUND(0, 4, 8, 12) - QUARTERROUND(1, 5, 9, 13) - QUARTERROUND(2, 6, 10, 14) - QUARTERROUND(3, 7, 11, 15) - QUARTERROUND(0, 5, 10, 15) - QUARTERROUND(1, 6, 11, 12) - QUARTERROUND(2, 7, 8, 13) - QUARTERROUND(3, 4, 9, 14) - } - MEMCPY(out, &x[0], sizeof(uint32_t) * 4); - MEMCPY(&out[16], &x[12], sizeof(uint32_t) * 4); -} - -#define U32TO8_LITTLE(p, v) \ - { \ - (p)[0] = (v >> 0) & 0xff; \ - (p)[1] = (v >> 8) & 0xff; \ - (p)[2] = (v >> 16) & 0xff; \ - (p)[3] = (v >> 24) & 0xff; \ - } - -// chacha_core performs 20 rounds of ChaCha on the input words in -// |input| and writes the 64 output bytes to |output|. -void chacha_core(uint8_t *output, const uint32_t *input) { - uint32_t x[16]; - int i; - MEMCPY(x, input, sizeof(uint32_t) * 16); - for (i = 20; i > 0; i -= 2) { - QUARTERROUND(0, 4, 8, 12) - QUARTERROUND(1, 5, 9, 13) - QUARTERROUND(2, 6, 10, 14) - QUARTERROUND(3, 7, 11, 15) - QUARTERROUND(0, 5, 10, 15) - QUARTERROUND(1, 6, 11, 12) - QUARTERROUND(2, 7, 8, 13) - QUARTERROUND(3, 4, 9, 14) - } - for (i = 0; i < 16; ++i) { - x[i] += input[i]; - } - for (i = 0; i < 16; ++i) { - U32TO8_LITTLE(output + 4 * i, x[i]); - } -} - -parser_error_t chacha(uint8_t *out, size_t out_len, const uint8_t *in, size_t in_len, const uint8_t *key, - const uint8_t *nonce, uint32_t counter) { - if (out_len < in_len) { - return parser_value_out_of_range; - } - uint32_t input[16]; - uint8_t buf[64]; - - size_t todo, i; - input[0] = U8TO32_LITTLE(sigma + 0); - input[1] = U8TO32_LITTLE(sigma + 4); - input[2] = U8TO32_LITTLE(sigma + 8); - input[3] = U8TO32_LITTLE(sigma + 12); - input[4] = U8TO32_LITTLE(key + 0); - input[5] = U8TO32_LITTLE(key + 4); - input[6] = U8TO32_LITTLE(key + 8); - input[7] = U8TO32_LITTLE(key + 12); - input[8] = U8TO32_LITTLE(key + 16); - input[9] = U8TO32_LITTLE(key + 20); - input[10] = U8TO32_LITTLE(key + 24); - input[11] = U8TO32_LITTLE(key + 28); - input[12] = counter; - input[13] = U8TO32_LITTLE(nonce + 0); - input[14] = U8TO32_LITTLE(nonce + 4); - input[15] = U8TO32_LITTLE(nonce + 8); - while (in_len > 0) { -#if defined(LEDGER_SPECIFIC) - io_seproxyhal_io_heartbeat(); -#endif - todo = sizeof(buf); - if (in_len < todo) { - todo = in_len; - } - chacha_core(buf, input); -#if defined(LEDGER_SPECIFIC) - io_seproxyhal_io_heartbeat(); -#endif - for (i = 0; i < todo; i++) { - out[i] = in[i] ^ buf[i]; - } - out += todo; - in += todo; - in_len -= todo; - input[12]++; - } - return parser_ok; -} diff --git a/app/src/crypto_helper/chacha.h b/app/src/crypto_helper/chacha.h deleted file mode 100644 index cd35a4f..0000000 --- a/app/src/crypto_helper/chacha.h +++ /dev/null @@ -1,35 +0,0 @@ -/* Copyright (c) 2018, Google Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ - -#pragma once - -#include -#include - -#include "parser_common.h" - -#if defined(__cplusplus) -extern "C" { -#endif - -// CRYPTO_hchacha20 computes the HChaCha20 function, which should only be used -// as part of XChaCha20. -void CRYPTO_hchacha20(uint8_t out[32], const uint8_t key[32], const uint8_t nonce[16]); - -parser_error_t chacha(uint8_t *out, size_t out_len, const uint8_t *in, size_t in_len, const uint8_t *key, - const uint8_t *nonce, uint32_t counter); - -#if defined(__cplusplus) -} // extern C -#endif // OPENSSL_HEADER_CHACHA_INTERNAL diff --git a/app/src/parser.c b/app/src/parser.c index 4b73cd4..3df6f63 100644 --- a/app/src/parser.c +++ b/app/src/parser.c @@ -91,6 +91,7 @@ static parser_error_t checkSanity(uint8_t numItems, uint8_t displayIdx) { } uint8_t out_idx = 0; +uint8_t prev_decrypted_out_idx = 0; parser_error_t parser_getItem(const parser_context_t *ctx, uint8_t displayIdx, char *outKey, uint16_t outKeyLen, char *outVal, uint16_t outValLen, uint8_t pageIdx, uint8_t *pageCount) { UNUSED(pageIdx); @@ -105,53 +106,55 @@ parser_error_t parser_getItem(const parser_context_t *ctx, uint8_t displayIdx, c uint64_t total_out_elements = ctx->tx_obj->outputs.elements * ELEMENTS_PER_OUTPUT; uint8_t tmp_idx = displayIdx; + if (displayIdx == 0) { + snprintf(outKey, outKeyLen, "Tx Version"); + snprintf(outVal, outValLen, "V%d", (uint8_t)ctx->tx_obj->transactionVersion); + return parser_ok; + } + if (tmp_idx > 0 && tmp_idx <= total_out_elements) { tmp_idx = (displayIdx % ELEMENTS_PER_OUTPUT); out_idx = (displayIdx / ELEMENTS_PER_OUTPUT); - if (tmp_idx == 0) { - tmp_idx = ELEMENTS_PER_OUTPUT; - out_idx--; - if (out_idx > ctx->tx_obj->outputs.elements - 1) { - return parser_display_idx_out_of_range; - } + + if (tmp_idx == 1 || tmp_idx == 2) { + out_idx++; } - if (tmp_idx == 1) { - const uint8_t *output = ctx->tx_obj->outputs.data.ptr + (out_idx * (192 + 328)); - if (pageIdx == 0) { - CHECK_ERROR(crypto_decrypt_merkle_note(ctx->tx_obj, output + 192, ctx->tx_obj->ovk)); - } + + if (prev_decrypted_out_idx != out_idx) { + const uint8_t *output = ctx->tx_obj->outputs.data.ptr + ((out_idx - 1) * (192 + 328)); + CHECK_ERROR(crypto_decrypt_merkle_note(ctx->tx_obj, output + 192, ctx->tx_obj->ovk)); + prev_decrypted_out_idx = out_idx; } } else if (tmp_idx > total_out_elements) { - tmp_idx -= total_out_elements - ELEMENTS_PER_OUTPUT; + tmp_idx -= total_out_elements - ELEMENTS_PER_OUTPUT + 1; } char buf[70] = {0}; switch (tmp_idx) { case 0: - snprintf(outKey, outKeyLen, "Tx Version"); - snprintf(outVal, outValLen, "V%d", (uint8_t)ctx->tx_obj->transactionVersion); + snprintf(outKey, outKeyLen, "AssetID %d", out_idx); + array_to_hexstr(buf, sizeof(buf), ctx->tx_obj->outputs.decrypted_note.asset_id, 32); + pageString(outVal, outValLen, buf, pageIdx, pageCount); return parser_ok; case 1: - snprintf(outKey, outKeyLen, "Owner %d", out_idx + 1); + snprintf(outKey, outKeyLen, "Owner %d", out_idx); array_to_hexstr(buf, sizeof(buf), ctx->tx_obj->outputs.decrypted_note.owner, 32); pageString(outVal, outValLen, buf, pageIdx, pageCount); return parser_ok; case 2: - snprintf(outKey, outKeyLen, "Amount %d", out_idx + 1); - snprintf(outVal, outValLen, "%d", (uint8_t)ctx->tx_obj->outputs.decrypted_note.value); + snprintf(outKey, outKeyLen, "Amount %d", out_idx); + uint64_to_str(buf, sizeof(buf), ctx->tx_obj->outputs.decrypted_note.value); + pageString(outVal, outValLen, buf, pageIdx, pageCount); return parser_ok; case 3: - snprintf(outKey, outKeyLen, "AssetID %d", out_idx + 1); - array_to_hexstr(buf, sizeof(buf), ctx->tx_obj->outputs.decrypted_note.asset_id, 32); + snprintf(outKey, outKeyLen, "Fee"); + uint64_to_str(buf, sizeof(buf), ctx->tx_obj->fee); pageString(outVal, outValLen, buf, pageIdx, pageCount); return parser_ok; case 4: - snprintf(outKey, outKeyLen, "Fee"); - snprintf(outVal, outValLen, "%d", (uint8_t)ctx->tx_obj->fee); - return parser_ok; - case 5: snprintf(outKey, outKeyLen, "Expiration"); - snprintf(outVal, outValLen, "%d", ctx->tx_obj->expiration); + uint32_to_str(buf, sizeof(buf), ctx->tx_obj->expiration); + pageString(outVal, outValLen, buf, pageIdx, pageCount); return parser_ok; default: break; diff --git a/app/src/parser_impl.c b/app/src/parser_impl.c index 84fd1b5..a6cd003 100644 --- a/app/src/parser_impl.c +++ b/app/src/parser_impl.c @@ -81,7 +81,7 @@ static parser_error_t readMints(parser_context_t *ctx, vec_mint_description_t *m if (txnVersion == V1) { // Owner field only available for V2 CHECK_ERROR(readBytes(ctx, &tmpPtr, MINTLEN - 32 + REDJUBJUB_SIGNATURE_LEN)); - mints->data.len += MINTLEN + REDJUBJUB_SIGNATURE_LEN; + mints->data.len += MINTLEN + REDJUBJUB_SIGNATURE_LEN - 32; } else { CTX_CHECK_AVAIL(ctx, (MINTLEN + 1)); const uint8_t transferOwnershipToLen = mints->data.ptr[MINTLEN] == 1 ? 33 : 1; diff --git a/app/src/parser_impl_common.h b/app/src/parser_impl_common.h index 4132b20..ae8ebb6 100644 --- a/app/src/parser_impl_common.h +++ b/app/src/parser_impl_common.h @@ -16,9 +16,6 @@ #pragma once #include "parser_common.h" -// #include -// #include "zxtypes.h" -// #include "parser_txdef.h" #ifdef __cplusplus extern "C" { diff --git a/app/src/parser_txdef.h b/app/src/parser_txdef.h index 4a82abe..a15bd3f 100644 --- a/app/src/parser_txdef.h +++ b/app/src/parser_txdef.h @@ -62,8 +62,7 @@ extern "C" { (VALUE_COMMITMENT_SIZE + NOTE_COMMITMENT_SIZE + EPHEMERAL_PUBLIC_KEY_SIZE + ENCRYPTED_NOTE_SIZE + MAC_SIZE + \ NOTE_ENCRYPTION_KEYS_SIZE) -#define SECRET_KEY_SIZE 32 -#define CHACHA_NONCE_SIZE 12 +#define SECRET_KEY_SIZE 32 typedef enum { V1 = 1, V2 = 2, diff --git a/app/src/review_keys.c b/app/src/review_keys.c index 7845d3d..6eb80b7 100644 --- a/app/src/review_keys.c +++ b/app/src/review_keys.c @@ -40,8 +40,6 @@ zxerr_t getNumItemsPublicAddress(uint8_t *num_items) { zxerr_t getItemPublicAddress(int8_t displayIdx, char *outKey, uint16_t outKeyLen, char *outVal, uint16_t outValLen, uint8_t pageIdx, uint8_t *pageCount) { - ZEMU_LOGF(50, "[addr_getItem] %d/%d\n", displayIdx, pageIdx) - switch (displayIdx) { case 0: snprintf(outKey, outKeyLen, "Address"); @@ -74,8 +72,6 @@ zxerr_t getNumItemsProofGenerationKey(uint8_t *num_items) { zxerr_t getItemProofGenerationKey(int8_t displayIdx, char *outKey, uint16_t outKeyLen, char *outVal, uint16_t outValLen, uint8_t pageIdx, uint8_t *pageCount) { - ZEMU_LOGF(50, "[addr_getItem] %d/%d\n", displayIdx, pageIdx) - switch (displayIdx) { case 0: snprintf(outKey, outKeyLen, "AuthKey"); @@ -115,8 +111,6 @@ zxerr_t getNumItemsViewKey(uint8_t *num_items) { zxerr_t getItemViewKey(int8_t displayIdx, char *outKey, uint16_t outKeyLen, char *outVal, uint16_t outValLen, uint8_t pageIdx, uint8_t *pageCount) { - ZEMU_LOGF(50, "[addr_getItem] %d/%d\n", displayIdx, pageIdx) - switch (displayIdx) { case 0: snprintf(outKey, outKeyLen, "ViewKey"); diff --git a/deps/ledger-zxlib b/deps/ledger-zxlib index 33f5c38..0172a43 160000 --- a/deps/ledger-zxlib +++ b/deps/ledger-zxlib @@ -1 +1 @@ -Subproject commit 33f5c38d8153534cf572d1ca7a7e09be7d55058b +Subproject commit 0172a430279045bfd5103e51d3975a3fbb9dc4b4 diff --git a/tests/testcases.json b/tests/testcases.json index 3226a0e..fe45568 100644 --- a/tests/testcases.json +++ b/tests/testcases.json @@ -43,5 +43,50 @@ "10 | Fee : 1", "11 | Expiration : 0" ] + }, + { + "index": 2, + "name": "2_ore_send_biger_amount", + "blob": "0101000000000000000300000000000000010000000000000000000000000000000100000000000000550000006c636ab46d0b2151580bba045c5e555c1d5a1318fb26c75ba2d5073d5e693e130d3283a9925dbde668353c7236a19cd2024094a38e7954adfbe4c2f69653b2000d3283a9925dbde668353c7236a19cd2024094a38e7954adfbe4c2f69653b200b10cc4d0b4c4becc68715ae4aa5453a234f989455d87cc54f14bf5d71ced41fc065050c33e37b08ac425a9125e390592a62ba9feab4a1bdbcf6311ed505e960c0496a837e61b802aea21ecd08ba76b53bc409cf43536cced3c79df677285ff3c0c5379a7b4f9ede10084b5ec76ba36aeabf882f2b5ece440fa7b239f8dab164db761b7e0c7071cf52211035482de30e8aaf5a2896e7df1dfa8079e458172ebfc824ec2a5ff1b80f29fdba92fb95f1ee7ab3467f41df427d519b332519e72cf3cb2c68eca60d78761350753bfa18b9867c7ffa8c3f83468dbc761137deb8ef2a11821d13c8ff4cfef584831211bc60e80c0bdf422438027e0b660dbdc619f872d000000008fbf7ae75c398cb702b309c812e743e9600e409f4d7bf6a1a12b74e06f0631d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b7e2f9b4a725957f10230896786eae9ff51f7f0293cbc252e4310fbdb4aeb5185828c019dd63c21ee20930bfd88e7249afd35b6c0974856d55a56a151ce586f27ded9ad661b8cde859dc76eb45645f5847fd9b959e4d8504f90534245cd313ca169419823800b11311c74555d914ca887a7cef5e2ec111d11c47e11a399c66714b6951aea8a1eb4f70b55f8a026a9b3e812847925072c7b46e284dced72d275e64faec299ef4dd370ed28d3f88b4d9e72cd15004d0092b8d9104327b707a7c4b804e81e4cd1de9cc3495c0bb73d9b1040e10af9f2a20559d4883fca4cebc348a2d7f3bf6bf32482180ac24bafd9ec2732d974eef0cc0fde34d672ddb7d7067481c0874ca991d31ca5b0ade9ef3464b5223d52900a443afc8cd64dadfa82d4ed0acdb22cf43276dbcc95e587077152f90df579b73ba543d7e79519ded7a9e652704d060f7e236f1bd5804ba08e717af9b70410061c170a462ec9aa854ff9bbe36d8d332c4fbdf37192970c29c44e88dfb7c3e5950be35e44ecdbbd5389162e7e79061f2630dc7938db34d35e357162265d22d3f018d959706205ec2c90e82013227ac6c66dfc23febf056d94dbace1dbf6b6641ab97a9ac422bd5191acbd8c2ae213e0ecda9907eb70a9798c5f783b748971993f4ba6b7c8931e125c143041abc4923b48bc309d1a6f2845664c73a4846b5ca5e18aa708314d0ebc067c1b5ba82831b8fe365463933a7a129ed5c18e055a32793b86b11052ba8bf3492c477bc80e24697e0cd4f0ed8239037971806ff5fa6768cfe70e50cdeb20340675382f8de37b4e8f3929153bb5815c5a16a78cbcd5a976b34f351c9db37ea5e708c9086bd599356295cd44fbc00a8c15128808ba88b9150404aabd2bdbeb83c039106cde728eec3459ef473236f7a32b281a2de480460313ed64118498c7fc7a70a9b4bd66845d2835020ec48e718ad306e4ceab1897876d648f0c1d3edc45475e3011ae180b07884d9f0b05cb108d2724b81540d0ec4269aaec617ab6b2c3982776f5fc7b650ab5bdca56650e7f97fdd609a9f01cf7beb409457b04ebea6501667b62ed84e77c643ebbe312ad4d957425f2a163b342b719dd806a22bf0bd2c4628ceb33b3cf39e393924264d8bac5bb3da174a32f4ed263102115583e72729286937a227f1eb3723b1ff07470de98ebee829d564ee0549c1ffd8a3d9f5187ae80e0a0f5bfa60778fd484bb7efb9b821215895a3fef58585b62fcbbc7198f947edfbc794a60f3fa18e98bc747ace026f03db7fbc15c4ce1d18111303f8aff1fff5065aefe92997a4ef4beb2932383f9257b5769df33a837388a122edee64c0db7487c154dc41826715817391c6e4c7dc3a6e0b06db8974329f051edfb58a2756957c18dcf43eb22df7b3925c943060b6c1c0d0eb03f0330b6812737951609908b6e23827caee06a2a49258ae5e9a24f3dbf3759b6a062628f29c4c7812294357ac3b4d45377176c0967228ba70e10360397ab141a8c629330d6eed4b3456419757fce360388eca7b7e5ba00ae284f99194f5e3752b99d8cb57b5ad62ea37188c5486c7650b089996ffc23b7453e33916bc55fad7d023893c8f403c71dd3724cf50542541bd8a7e825a853f9b99b59bc4dce2b8623b8bb653c01c37325f5bda408bc45dc59aef2b215b72d96f17372a8f7222e44b036289d430715fe6332cc8228f224044b3b29fdb6ab30bb843755d0e70d0cd80d3a9650a0a2bef96a775790a301722332c0e32d7d00b8534430ccfdbcc5db4bd9295f1ed4124d64e5d1590746849374fb7a29409f2c861d1547e368a77f34b462dd365db4e50fcfedfd22fe876971f3ad12ff49fa9ac49795648f3e4a96586816c757ccd7fcb2347a8d1b25a143d2f2373d4219669967edc8d3cc4f15663f76aac41630bdcfcdfb8ee39f98b2078ddd0a02a4db28eec6ef779bc74b158372470cfb093fa20b81dff2637d5dc588769c9289314277825bacc3e8088de9b8fa6bb9fb1ecd5ed612e0ea08acc397ff8247f45997700b7cf1b8d1122f0f61028f912ebfa9fa568ba43bc875005282bbcef616b1bbe7d61aa9fd181fbb874dceb471ac0821542ee3bc095fd111126a886bf1af722ef7a9652e6e7f17ac01a0d0b12f089136694ada6482a66dc7172d2a5d2742705966d98d458429e3a517432d7a1131ca5afd8a4f38e9b80d3283a9925dbde668353c7236a19cd2024094a38e7954adfbe4c2f69653b200a0930bfac1d7abf2d8fb96954259700ba13d9e7ccb89f196e3e48b14e92dc5221f85cfd7e65b645ae1c64e4200300b80842749b6c0ca5e18ec211a1f3efabb9f075d8ccfe8b448ab4bb86857c7fbd9f069b6c8bcbb88124f4ef92170183b164201390c08418836a383d6a111aee2a6767e25282270c949dbfad221383ea6363957fb12c8b7e37f537c5461704a6ee8c083a8f31ca4b3a040beac7fbe9705a2454fea4e45fe1cc935bd12716aa60485cb68ab82ab775eef395a29dbf2b703397440fae059d8ee3361b7a08429867254523f937c7654ae6fe7b188c1f0da57e9cd54657374636f696e00000000000000000000000000000000000000000000000041207265616c6c7920636f6f6c20636f696e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1adf46b40dc8411eb74acb000cc7a69b8dd10aa181d42535c827292ccd1fc18b518389495998cbc06263de032d34a400de199cd7f1d7d08843999f42915909", + "output": [ + "0 | Tx Version : V1", + "1 | Owner 1 [1/2] : 40fae059d8ee3361b7a08429867254523f937c", + "1 | Owner 1 [2/2] : 7654ae6fe7b188c1f0da57e9cd", + "2 | Amount 1 : 40", + "3 | AssetID 1 [1/2] : 51f33a2f14f92735e562dc658a5639279ddca3", + "3 | AssetID 1 [2/2] : d5079a6d1242b2a588a9cbf44c", + "4 | Owner 2 [1/2] : 40fae059d8ee3361b7a08429867254523f937c", + "4 | Owner 2 [2/2] : 7654ae6fe7b188c1f0da57e9cd", + "5 | Amount 2 : 256", + "6 | AssetID 2 [1/2] : da40fc530a12327a1c1c367b8bd66ada351005", + "6 | AssetID 2 [2/2] : 01228cdb355b8fb6c40d048a64", + "7 | Owner 3 [1/2] : 40fae059d8ee3361b7a08429867254523f937c", + "7 | Owner 3 [2/2] : 7654ae6fe7b188c1f0da57e9cd", + "8 | Amount 3 : 1", + "9 | AssetID 3 [1/2] : 51f33a2f14f92735e562dc658a5639279ddca3", + "9 | AssetID 3 [2/2] : d5079a6d1242b2a588a9cbf44c", + "10 | Fee : 1", + "11 | Expiration : 85" + ], + "output_expert": [ + "0 | Tx Version : V1", + "1 | Owner 1 [1/2] : 40fae059d8ee3361b7a08429867254523f937c", + "1 | Owner 1 [2/2] : 7654ae6fe7b188c1f0da57e9cd", + "2 | Amount 1 : 40", + "3 | AssetID 1 [1/2] : 51f33a2f14f92735e562dc658a5639279ddca3", + "3 | AssetID 1 [2/2] : d5079a6d1242b2a588a9cbf44c", + "4 | Owner 2 [1/2] : 40fae059d8ee3361b7a08429867254523f937c", + "4 | Owner 2 [2/2] : 7654ae6fe7b188c1f0da57e9cd", + "5 | Amount 2 : 256", + "6 | AssetID 2 [1/2] : da40fc530a12327a1c1c367b8bd66ada351005", + "6 | AssetID 2 [2/2] : 01228cdb355b8fb6c40d048a64", + "7 | Owner 3 [1/2] : 40fae059d8ee3361b7a08429867254523f937c", + "7 | Owner 3 [2/2] : 7654ae6fe7b188c1f0da57e9cd", + "8 | Amount 3 : 1", + "9 | AssetID 3 [1/2] : 51f33a2f14f92735e562dc658a5639279ddca3", + "9 | AssetID 3 [2/2] : d5079a6d1242b2a588a9cbf44c", + "10 | Fee : 1", + "11 | Expiration : 85" + ] } ] diff --git a/tests/ui_tests.cpp b/tests/ui_tests.cpp index e11b93c..06008eb 100644 --- a/tests/ui_tests.cpp +++ b/tests/ui_tests.cpp @@ -120,7 +120,7 @@ void check_testcase(const testcase_t &tc, bool expert_mode) { } INSTANTIATE_TEST_SUITE_P - + // Testcases generated from ironfish dkg app, using BuildTx() from utils.tx in zemu_tests (JsonTestCasesCurrentTxVer, JsonTestsA, ::testing::ValuesIn(GetJsonTestCases("testcases.json")), JsonTestsA::PrintToStringParamName()); TEST_P(JsonTestsA, CheckUIOutput_CurrentTX_Expert) { diff --git a/tests_zemu/snapshots/fl-mainmenu/00001.png b/tests_zemu/snapshots/fl-mainmenu/00001.png index 26916c9..e3dda8a 100644 Binary files a/tests_zemu/snapshots/fl-mainmenu/00001.png and b/tests_zemu/snapshots/fl-mainmenu/00001.png differ diff --git a/tests_zemu/snapshots/fl-mainmenu/00002.png b/tests_zemu/snapshots/fl-mainmenu/00002.png index 998320f..f8a6e75 100644 Binary files a/tests_zemu/snapshots/fl-mainmenu/00002.png and b/tests_zemu/snapshots/fl-mainmenu/00002.png differ diff --git a/tests_zemu/snapshots/fl-mainmenu/00003.png b/tests_zemu/snapshots/fl-mainmenu/00003.png index 26916c9..e3dda8a 100644 Binary files a/tests_zemu/snapshots/fl-mainmenu/00003.png and b/tests_zemu/snapshots/fl-mainmenu/00003.png differ diff --git a/tests_zemu/snapshots/fl-mainmenu/00004.png b/tests_zemu/snapshots/fl-mainmenu/00004.png index 12b4442..1cf6393 100644 Binary files a/tests_zemu/snapshots/fl-mainmenu/00004.png and b/tests_zemu/snapshots/fl-mainmenu/00004.png differ diff --git a/tests_zemu/snapshots/fl-show_address/00000.png b/tests_zemu/snapshots/fl-show_address/00000.png index d0e156c..ba9c4d2 100644 Binary files a/tests_zemu/snapshots/fl-show_address/00000.png and b/tests_zemu/snapshots/fl-show_address/00000.png differ diff --git a/tests_zemu/snapshots/fl-show_address/00001.png b/tests_zemu/snapshots/fl-show_address/00001.png index a80f850..0d877ba 100644 Binary files a/tests_zemu/snapshots/fl-show_address/00001.png and b/tests_zemu/snapshots/fl-show_address/00001.png differ diff --git a/tests_zemu/snapshots/fl-show_address/00002.png b/tests_zemu/snapshots/fl-show_address/00002.png index c2664c9..ad4dac5 100644 Binary files a/tests_zemu/snapshots/fl-show_address/00002.png and b/tests_zemu/snapshots/fl-show_address/00002.png differ diff --git a/tests_zemu/snapshots/fl-show_viewkey/00002.png b/tests_zemu/snapshots/fl-show_viewkey/00002.png index 054d68e..6b6bf5f 100644 Binary files a/tests_zemu/snapshots/fl-show_viewkey/00002.png and b/tests_zemu/snapshots/fl-show_viewkey/00002.png differ diff --git a/tests_zemu/snapshots/fl-show_viewkey/00003.png b/tests_zemu/snapshots/fl-show_viewkey/00003.png index 89bc1f1..69c0b10 100644 Binary files a/tests_zemu/snapshots/fl-show_viewkey/00003.png and b/tests_zemu/snapshots/fl-show_viewkey/00003.png differ diff --git a/tests_zemu/snapshots/fl-show_viewkey/00004.png b/tests_zemu/snapshots/fl-show_viewkey/00004.png index 3dd268f..d46448b 100644 Binary files a/tests_zemu/snapshots/fl-show_viewkey/00004.png and b/tests_zemu/snapshots/fl-show_viewkey/00004.png differ diff --git a/tests_zemu/snapshots/fl-sign_3_out_tx/00000.png b/tests_zemu/snapshots/fl-sign_3_out_tx/00000.png index 7f81dd8..febf810 100644 Binary files a/tests_zemu/snapshots/fl-sign_3_out_tx/00000.png and b/tests_zemu/snapshots/fl-sign_3_out_tx/00000.png differ diff --git a/tests_zemu/snapshots/fl-sign_3_out_tx/00001.png b/tests_zemu/snapshots/fl-sign_3_out_tx/00001.png index 31119fc..2604847 100644 Binary files a/tests_zemu/snapshots/fl-sign_3_out_tx/00001.png and b/tests_zemu/snapshots/fl-sign_3_out_tx/00001.png differ diff --git a/tests_zemu/snapshots/fl-sign_3_out_tx/00004.png b/tests_zemu/snapshots/fl-sign_3_out_tx/00004.png index eab2477..64bf5bb 100644 Binary files a/tests_zemu/snapshots/fl-sign_3_out_tx/00004.png and b/tests_zemu/snapshots/fl-sign_3_out_tx/00004.png differ diff --git a/tests_zemu/snapshots/fl-sign_3_out_tx/00005.png b/tests_zemu/snapshots/fl-sign_3_out_tx/00005.png index e66bd7d..41dc54b 100644 Binary files a/tests_zemu/snapshots/fl-sign_3_out_tx/00005.png and b/tests_zemu/snapshots/fl-sign_3_out_tx/00005.png differ diff --git a/tests_zemu/snapshots/sp-mainmenu/00004.png b/tests_zemu/snapshots/sp-mainmenu/00004.png index ae17e04..530e2ea 100644 Binary files a/tests_zemu/snapshots/sp-mainmenu/00004.png and b/tests_zemu/snapshots/sp-mainmenu/00004.png differ diff --git a/tests_zemu/snapshots/sp-mainmenu/00010.png b/tests_zemu/snapshots/sp-mainmenu/00010.png index ae17e04..530e2ea 100644 Binary files a/tests_zemu/snapshots/sp-mainmenu/00010.png and b/tests_zemu/snapshots/sp-mainmenu/00010.png differ diff --git a/tests_zemu/snapshots/sp-show_address/00003.png b/tests_zemu/snapshots/sp-show_address/00003.png index 1e4be69..87bcc36 100644 Binary files a/tests_zemu/snapshots/sp-show_address/00003.png and b/tests_zemu/snapshots/sp-show_address/00003.png differ diff --git a/tests_zemu/snapshots/sp-show_address/00004.png b/tests_zemu/snapshots/sp-show_address/00004.png index 9b29b76..1e4be69 100644 Binary files a/tests_zemu/snapshots/sp-show_address/00004.png and b/tests_zemu/snapshots/sp-show_address/00004.png differ diff --git a/tests_zemu/snapshots/sp-show_viewkey/00008.png b/tests_zemu/snapshots/sp-show_viewkey/00008.png index 1e4be69..87bcc36 100644 Binary files a/tests_zemu/snapshots/sp-show_viewkey/00008.png and b/tests_zemu/snapshots/sp-show_viewkey/00008.png differ diff --git a/tests_zemu/snapshots/sp-show_viewkey/00009.png b/tests_zemu/snapshots/sp-show_viewkey/00009.png index 9b29b76..1e4be69 100644 Binary files a/tests_zemu/snapshots/sp-show_viewkey/00009.png and b/tests_zemu/snapshots/sp-show_viewkey/00009.png differ diff --git a/tests_zemu/snapshots/st-mainmenu/00001.png b/tests_zemu/snapshots/st-mainmenu/00001.png index 5f60b4d..7b82196 100644 Binary files a/tests_zemu/snapshots/st-mainmenu/00001.png and b/tests_zemu/snapshots/st-mainmenu/00001.png differ diff --git a/tests_zemu/snapshots/st-mainmenu/00002.png b/tests_zemu/snapshots/st-mainmenu/00002.png index 82a1ef4..d40467c 100644 Binary files a/tests_zemu/snapshots/st-mainmenu/00002.png and b/tests_zemu/snapshots/st-mainmenu/00002.png differ diff --git a/tests_zemu/snapshots/st-mainmenu/00003.png b/tests_zemu/snapshots/st-mainmenu/00003.png index 5f60b4d..7b82196 100644 Binary files a/tests_zemu/snapshots/st-mainmenu/00003.png and b/tests_zemu/snapshots/st-mainmenu/00003.png differ diff --git a/tests_zemu/snapshots/st-mainmenu/00004.png b/tests_zemu/snapshots/st-mainmenu/00004.png index f16eedd..7a29666 100644 Binary files a/tests_zemu/snapshots/st-mainmenu/00004.png and b/tests_zemu/snapshots/st-mainmenu/00004.png differ diff --git a/tests_zemu/snapshots/st-show_address/00002.png b/tests_zemu/snapshots/st-show_address/00002.png index da2c2d7..58a343e 100644 Binary files a/tests_zemu/snapshots/st-show_address/00002.png and b/tests_zemu/snapshots/st-show_address/00002.png differ diff --git a/tests_zemu/snapshots/st-show_viewkey/00000.png b/tests_zemu/snapshots/st-show_viewkey/00000.png index 328d9ce..8800ea8 100644 Binary files a/tests_zemu/snapshots/st-show_viewkey/00000.png and b/tests_zemu/snapshots/st-show_viewkey/00000.png differ diff --git a/tests_zemu/snapshots/st-show_viewkey/00001.png b/tests_zemu/snapshots/st-show_viewkey/00001.png index eb25d34..9bd3dff 100644 Binary files a/tests_zemu/snapshots/st-show_viewkey/00001.png and b/tests_zemu/snapshots/st-show_viewkey/00001.png differ diff --git a/tests_zemu/snapshots/st-show_viewkey/00002.png b/tests_zemu/snapshots/st-show_viewkey/00002.png index a02f53a..61fd80b 100644 Binary files a/tests_zemu/snapshots/st-show_viewkey/00002.png and b/tests_zemu/snapshots/st-show_viewkey/00002.png differ diff --git a/tests_zemu/snapshots/st-show_viewkey/00003.png b/tests_zemu/snapshots/st-show_viewkey/00003.png index 3f1f9fd..f732fd0 100644 Binary files a/tests_zemu/snapshots/st-show_viewkey/00003.png and b/tests_zemu/snapshots/st-show_viewkey/00003.png differ diff --git a/tests_zemu/snapshots/st-show_viewkey/00004.png b/tests_zemu/snapshots/st-show_viewkey/00004.png index 02ee8eb..d42efca 100644 Binary files a/tests_zemu/snapshots/st-show_viewkey/00004.png and b/tests_zemu/snapshots/st-show_viewkey/00004.png differ diff --git a/tests_zemu/snapshots/x-mainmenu/00004.png b/tests_zemu/snapshots/x-mainmenu/00004.png index ae17e04..530e2ea 100644 Binary files a/tests_zemu/snapshots/x-mainmenu/00004.png and b/tests_zemu/snapshots/x-mainmenu/00004.png differ diff --git a/tests_zemu/snapshots/x-mainmenu/00010.png b/tests_zemu/snapshots/x-mainmenu/00010.png index ae17e04..530e2ea 100644 Binary files a/tests_zemu/snapshots/x-mainmenu/00010.png and b/tests_zemu/snapshots/x-mainmenu/00010.png differ