From 53f8338eaaac27768c62d94c4ccd9465ee3c826d Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Wed, 4 Oct 2023 18:23:34 +0200 Subject: [PATCH 01/43] remove irrelevant TODOs --- doc/ins_get_public_keys.md | 4 +--- src/addressUtilsByron.c | 2 +- src/io.c | 2 +- src/securityPolicy.c | 3 ++- src/signTx.c | 2 +- src/signTx.h | 2 +- src/signTxUtils.h | 2 +- src/txHashBuilder.c | 4 ---- src/txHashBuilder_test.c | 1 - src/uiHelpers_nanos.c | 2 +- 10 files changed, 9 insertions(+), 15 deletions(-) diff --git a/doc/ins_get_public_keys.md b/doc/ins_get_public_keys.md index e3035d82..fc304d72 100644 --- a/doc/ins_get_public_keys.md +++ b/doc/ins_get_public_keys.md @@ -80,8 +80,6 @@ Concatenation of `pub_key` and `chain_code` representing the extended public key - Ledger might impose more restrictions, see implementation of `policyForGetExtendedPublicKey` in [src/securityPolicy.c](../src/securityPolicy.c) for details - calculate extended public key - respond with extended public key - + **TODOs** - ❓(IOHK): Should we also support BTC app like token validation? (Note: Token validation is to prevent concurrent access to the Ledger by two different host apps which could confuse user into performing wrong actions) -- ❓(IOHK): Should we support permanent app setting where Ledger forces user to acknowledge public key retrieval before sending it to host? (Note: probably not in the first version of the app) -- ❓(IOHK): Should there be an option to show the public key on display? Is it useful in any way? (Note: probably not) diff --git a/src/addressUtilsByron.c b/src/addressUtilsByron.c index 078706e8..cf68f2b8 100644 --- a/src/addressUtilsByron.c +++ b/src/addressUtilsByron.c @@ -31,7 +31,7 @@ void addressRootFromExtPubKey( { // [0, [0, publicKey:chainCode], Map(0)] - // TODO(ppershing): what are the first two 0 constants? + // Note(ppershing): what are the first two 0 constants? view_appendToken(&cbor, CBOR_TYPE_ARRAY, 3); { view_appendToken(&cbor, CBOR_TYPE_UNSIGNED, CARDANO_ADDRESS_TYPE_PUBKEY); diff --git a/src/io.c b/src/io.c index a85bfc50..02b32e9b 100644 --- a/src/io.c +++ b/src/io.c @@ -127,7 +127,7 @@ unsigned char io_event(unsigned char channel MARK_UNUSED) case SEPROXYHAL_TAG_FINGER_EVENT: UX_FINGER_EVENT(G_io_seproxyhal_spi_buffer); break; - #endif // HAVE_NBGL + #endif // HAVE_NBGL case SEPROXYHAL_TAG_TICKER_EVENT: UX_TICKER_EVENT(G_io_seproxyhal_spi_buffer, { diff --git a/src/securityPolicy.c b/src/securityPolicy.c index a694a749..18d31fe8 100644 --- a/src/securityPolicy.c +++ b/src/securityPolicy.c @@ -1493,7 +1493,8 @@ static inline security_policy_t _poolRegistrationOperatorWitnessPolicy(const bip case PATH_POOL_COLD_KEY: // only ordinary spending key paths (because of inputs) and pool cold key path are allowed WARN_UNLESS(bip44_isPathReasonable(path)); - // TODO is there a reason to show the witnesses? + // it might be safe to hide the witnesses, but txs related to stake pools + // are rare, so it would not help much and might introduce some unknown risk SHOW(); break; diff --git a/src/signTx.c b/src/signTx.c index f26bec2d..65746842 100644 --- a/src/signTx.c +++ b/src/signTx.c @@ -1782,7 +1782,7 @@ static subhandler_fn_t* lookup_subhandler(uint8_t p1) CASE(0x0c, signTx_handleScriptDataHashAPDU); CASE(0x0d, signTx_handleCollateralInputAPDU); CASE(0x0e, signTx_handleRequiredSignerAPDU); - CASE(0x12, signTx_handleCollateralOutputAPDU); // TODO perhaps change the numbers for the newly added items? + CASE(0x12, signTx_handleCollateralOutputAPDU); CASE(0x10, signTx_handleTotalCollateralAPDU); CASE(0x11, signTx_handleReferenceInputsAPDU); CASE(0x0a, signTx_handleConfirmAPDU); diff --git a/src/signTx.h b/src/signTx.h index 32a8751b..e9573733 100644 --- a/src/signTx.h +++ b/src/signTx.h @@ -169,7 +169,7 @@ typedef struct { uint8_t scriptDataHash[SCRIPT_DATA_HASH_LENGTH]; sign_tx_required_signer_t requiredSigner; uint64_t totalCollateral; - } stageData; // TODO rename to reflect single-APDU scope + } stageData; union { pool_registration_context_t pool_registration_subctx; diff --git a/src/signTxUtils.h b/src/signTxUtils.h index 8f5baf3f..fcb63149 100644 --- a/src/signTxUtils.h +++ b/src/signTxUtils.h @@ -18,4 +18,4 @@ bool violatesSingleAccountOrStoreIt(const bip44_path_t* path); void view_parseDestination(read_view_t* view, tx_output_destination_storage_t* destination); -#endif // H_CARDANO_APP_SIGN_TX_UTILS +#endif // H_CARDANO_APP_SIGN_TX_UTILS diff --git a/src/txHashBuilder.c b/src/txHashBuilder.c index 8c50e1b5..3c0ff551 100644 --- a/src/txHashBuilder.c +++ b/src/txHashBuilder.c @@ -537,8 +537,6 @@ void txHashBuilder_addOutput_datum( ASSERT(false); } - //TODO: MAX_DATUM_SIZE?? - // the babbage output format serializes some preliminary stuff if (builder->outputData.serializationFormat == MAP_BABBAGE) { // datum_option = [ 0, $hash32 // 1, data ] @@ -603,8 +601,6 @@ void txHashBuilder_addOutput_datum_inline_chunk( void txHashBuilder_addOutput_referenceScript(tx_hash_builder_t* builder, size_t scriptSize) { - // TODO: MAX_SCRIPT_SIZE?? maybe we don't need to limit it - ASSERT(builder->outputData.includeRefScript); switch (builder->outputState) { diff --git a/src/txHashBuilder_test.c b/src/txHashBuilder_test.c index 1a0f0a8a..3dad47a3 100644 --- a/src/txHashBuilder_test.c +++ b/src/txHashBuilder_test.c @@ -262,7 +262,6 @@ static void collateralOutputTokenHandler(tx_hash_builder_t* builder, { txHashBuilder_addCollateralOutput_token(builder, assetNameBuffer, assetNameSize, (int64_t)amount); } -//TODO: more generic function to handle similar? or just merge to addCollRet? static void addMultiassetCollateralOutput(tx_hash_builder_t* builder, tx_output_serialization_format_t outputFormat) { uint8_t tmp[70] = {0}; diff --git a/src/uiHelpers_nanos.c b/src/uiHelpers_nanos.c index 2472f138..93b6bc27 100644 --- a/src/uiHelpers_nanos.c +++ b/src/uiHelpers_nanos.c @@ -132,7 +132,7 @@ static const bagl_element_t ui_paginatedText[] = { UI_ICON_LEFT(ID_ICON_GO_LEFT, BAGL_GLYPH_ICON_LEFT), UI_ICON_RIGHT(ID_ICON_GO_RIGHT, BAGL_GLYPH_ICON_RIGHT), - // TODO(ppershing): what are the following magical numbers? + // Note(ppershing): what are the following magical numbers? UI_TEXT(ID_UNSPECIFIED, 0, 12, 128, &displayState.paginatedText.header), UI_TEXT(ID_UNSPECIFIED, 0, 26, 128, &displayState.paginatedText.currentText), From 0106dac21088c65c1743d1754fdd0429b6f56e27 Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Tue, 20 Jun 2023 11:19:12 +0200 Subject: [PATCH 02/43] remove features from Nano S --- Makefile | 42 +++++++++++ doc/build.md | 2 +- doc/features.md | 11 +++ fuzzing/CMakeLists.txt | 9 +++ src/addressUtilsByron.c | 109 ++++++++++++++++------------- src/addressUtilsByron.h | 10 ++- src/addressUtilsByron_test.c | 2 +- src/addressUtilsShelley.c | 18 ++++- src/cardano.h | 4 ++ src/deriveNativeScriptHash.c | 4 ++ src/deriveNativeScriptHash.h | 4 ++ src/deriveNativeScriptHash_ui.c | 4 ++ src/deriveNativeScriptHash_ui.h | 6 ++ src/getVersion.c | 10 ++- src/handlers.c | 4 ++ src/ipUtils.c | 4 ++ src/ipUtils.h | 6 +- src/ipUtils_test.c | 5 +- src/messageSigning.c | 2 + src/messageSigning.h | 2 + src/nativeScriptHashBuilder.c | 4 ++ src/nativeScriptHashBuilder.h | 4 ++ src/nativeScriptHashBuilder_test.c | 5 +- src/runTests.c | 10 +++ src/securityPolicy.c | 24 ++++++- src/securityPolicy.h | 7 +- src/signOpCert.c | 4 ++ src/signOpCert.h | 4 ++ src/signTx.c | 69 +++++++++++++++++- src/signTx.h | 8 +++ src/signTxMint.c | 4 ++ src/signTxMint.h | 4 ++ src/signTxMint_ui.c | 4 ++ src/signTxMint_ui.h | 4 ++ src/signTxPoolRegistration.c | 4 ++ src/signTxPoolRegistration.h | 4 ++ src/signTxPoolRegistration_ui.c | 4 ++ src/signTxPoolRegistration_ui.h | 5 ++ src/signTx_ui.c | 12 +++- src/state.h | 4 ++ src/txHashBuilder.c | 12 ++++ src/txHashBuilder.h | 14 +++- src/txHashBuilder_test.c | 2 +- src/uiScreens_bagl.c | 16 +++++ src/uiScreens_bagl.h | 4 ++ src/uiScreens_nbgl.c | 4 ++ src/uiScreens_nbgl.h | 4 ++ 47 files changed, 431 insertions(+), 71 deletions(-) create mode 100644 doc/features.md diff --git a/Makefile b/Makefile index c03f1fee..218503e8 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,7 @@ #******************************************************************************* APPNAME = "Cardano ADA" + APPVERSION_M = 6 APPVERSION_N = 1 APPVERSION_P = 2 @@ -120,6 +121,31 @@ else DEFINES += PRINTF\(...\)= endif +# restricted features for Nano S +# but not in DEVEL mode where we usually want to test all features with HEADLESS +ifeq ($(TARGET_NAME), TARGET_NANOS) + ifneq ($(DEVEL), 1) + APP_XS = 1 + else + APP_XS = 0 + endif +else + APP_XS = 0 +endif + +ifeq ($(APP_XS), 1) + DEFINES += APP_XS +else + # features not included in the Nano S app + DEFINES += APP_FEATURE_OPCERT + DEFINES += APP_FEATURE_NATIVE_SCRIPT_HASH + DEFINES += APP_FEATURE_POOL_REGISTRATION + DEFINES += APP_FEATURE_POOL_RETIREMENT + DEFINES += APP_FEATURE_BYRON_ADDRESS_DERIVATION + DEFINES += APP_FEATURE_BYRON_PROTOCOL_MAGIC_CHECK +endif +# always include this, it's important for Plutus users +DEFINES += APP_FEATURE_TOKEN_MINTING ################## # Dependencies # @@ -196,5 +222,21 @@ format: size: all $(GCCPATH)arm-none-eabi-size --format=gnu bin/app.elf +############## +# Device-specific builds +############## + +nanos: clean + BOLOS_SDK=$(NANOS_SDK) make + +nanosp: clean + BOLOS_SDK=$(NANOSP_SDK) make + +nanox: clean + BOLOS_SDK=$(NANOX_SDK) make + +stax: clean + BOLOS_SDK=$(STAX_SDK) make + # import generic rules from the sdk include $(BOLOS_SDK)/Makefile.rules diff --git a/doc/build.md b/doc/build.md index 14a11485..b94de09c 100644 --- a/doc/build.md +++ b/doc/build.md @@ -5,7 +5,7 @@ - Install Docker - Pull the required containers as discussed in https://github.com/LedgerHQ/ledger-app-builder/ (lite container is sufficient for a C build): - `sudo docker pull ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-lite:latest` + `docker pull ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-lite:latest` ## Compiling the app diff --git a/doc/features.md b/doc/features.md new file mode 100644 index 00000000..ec9745f3 --- /dev/null +++ b/doc/features.md @@ -0,0 +1,11 @@ +# Features (not) available on specific Ledger devices + +Nano S has a very limited space for storing applications. It is not enough to fit all Cardano features there, so some of them are only available on Nano S+ and other more spacious Ledger devices (e.g. Nano X and Stax). + +The features not supported on Nano S, Cardano app version 7 and above: +* pool registration and retirement +* signing of operational certificates +* computation of native script hashes +* details in Byron change outputs (only the address is shown) + +Details can be found in [Makefile](../Makefile) and in the code (search for compilation flags beginning with `APP_FEATURE_`). diff --git a/fuzzing/CMakeLists.txt b/fuzzing/CMakeLists.txt index fe7e9fe6..0d324582 100644 --- a/fuzzing/CMakeLists.txt +++ b/fuzzing/CMakeLists.txt @@ -142,6 +142,15 @@ add_compile_definitions( HAVE_HASH HAVE_SHA256 HAVE_SHA3 + + # include all app features, incl. those removed from Nano S + APP_FEATURE_OPCERT + APP_FEATURE_NATIVE_SCRIPT_HASH + APP_FEATURE_POOL_REGISTRATION + APP_FEATURE_POOL_RETIREMENT + APP_FEATURE_BYRON_ADDRESS_DERIVATION + APP_FEATURE_BYRON_PROTOCOL_MAGIC_CHECK + APP_FEATURE_TOKEN_MINTING ) set(SOURCE diff --git a/src/addressUtilsByron.c b/src/addressUtilsByron.c index cf68f2b8..bb3c01a1 100644 --- a/src/addressUtilsByron.c +++ b/src/addressUtilsByron.c @@ -7,6 +7,15 @@ #include "crc32.h" #include "bufView.h" +#if defined(APP_FEATURE_BYRON_ADDRESS_DERIVATION) || defined(APP_FEATURE_BYRON_PROTOCOL_MAGIC_CHECK) + +static const size_t ADDRESS_ROOT_SIZE = 28; +static const size_t PROTOCOL_MAGIC_ADDRESS_ATTRIBUTE_KEY = 2; + +#endif + +#ifdef APP_FEATURE_BYRON_ADDRESS_DERIVATION + enum { CARDANO_ADDRESS_TYPE_PUBKEY = 0, /* @@ -15,9 +24,6 @@ enum { */ }; -static const size_t ADDRESS_ROOT_SIZE = 28; -static const size_t PROTOCOL_MAGIC_ADDRESS_ATTRIBUTE_KEY = 2; - void addressRootFromExtPubKey( const extendedPublicKey_t* extPubKey, uint8_t* outBuffer, size_t outSize @@ -136,6 +142,56 @@ size_t cborPackRawAddressWithChecksum( return view_processedSize(&output); } +size_t deriveRawAddress( + const bip44_path_t* pathSpec, uint32_t protocolMagic, + uint8_t* outBuffer, size_t outSize +) +{ + ASSERT(outSize < BUFFER_SIZE_PARANOIA); + + uint8_t addressRoot[28] = {0}; + { + extendedPublicKey_t extPubKey; + + deriveExtendedPublicKey(pathSpec, &extPubKey); + + addressRootFromExtPubKey( + &extPubKey, + addressRoot, SIZEOF(addressRoot) + ); + } + + return cborEncodePubkeyAddressInner( + addressRoot, SIZEOF(addressRoot), + protocolMagic, + outBuffer, outSize + ); +} + +size_t deriveAddress_byron( + const bip44_path_t* pathSpec, uint32_t protocolMagic, + uint8_t* outBuffer, size_t outSize +) +{ + ASSERT(outSize < BUFFER_SIZE_PARANOIA); + + uint8_t rawAddressBuffer[40] = {0}; + size_t rawAddressSize = deriveRawAddress( + pathSpec, protocolMagic, + rawAddressBuffer, SIZEOF(rawAddressBuffer) + ); + + return cborPackRawAddressWithChecksum( + rawAddressBuffer, rawAddressSize, + outBuffer, outSize + ); + +} + +#endif // APP_FEATURE_BYRON_ADDRESS_DERIVATION + +#ifdef APP_FEATURE_BYRON_PROTOCOL_MAGIC_CHECK + static uint64_t parseToken(read_view_t* view, uint8_t type) { const cbor_token_t token = view_parseToken(view); @@ -166,7 +222,6 @@ static size_t parseBytesSizeToken(read_view_t* view) return parsedSizeDowncasted; } - uint32_t extractProtocolMagic( const uint8_t* addressBuffer, size_t addressSize ) @@ -252,48 +307,4 @@ uint32_t extractProtocolMagic( return protocolMagic; } -size_t deriveRawAddress( - const bip44_path_t* pathSpec, uint32_t protocolMagic, - uint8_t* outBuffer, size_t outSize -) -{ - ASSERT(outSize < BUFFER_SIZE_PARANOIA); - - uint8_t addressRoot[28] = {0}; - { - extendedPublicKey_t extPubKey; - - deriveExtendedPublicKey(pathSpec, &extPubKey); - - addressRootFromExtPubKey( - &extPubKey, - addressRoot, SIZEOF(addressRoot) - ); - } - - return cborEncodePubkeyAddressInner( - addressRoot, SIZEOF(addressRoot), - protocolMagic, - outBuffer, outSize - ); -} - -size_t deriveAddress_byron( - const bip44_path_t* pathSpec, uint32_t protocolMagic, - uint8_t* outBuffer, size_t outSize -) -{ - ASSERT(outSize < BUFFER_SIZE_PARANOIA); - - uint8_t rawAddressBuffer[40] = {0}; - size_t rawAddressSize = deriveRawAddress( - pathSpec, protocolMagic, - rawAddressBuffer, SIZEOF(rawAddressBuffer) - ); - - return cborPackRawAddressWithChecksum( - rawAddressBuffer, rawAddressSize, - outBuffer, outSize - ); - -} +#endif // APP_FEATURE_BYRON_PROTOCOL_MAGIC_CHECK diff --git a/src/addressUtilsByron.h b/src/addressUtilsByron.h index e5d275a0..d107993c 100644 --- a/src/addressUtilsByron.h +++ b/src/addressUtilsByron.h @@ -4,19 +4,27 @@ #include "common.h" #include "bip44.h" +#ifdef APP_FEATURE_BYRON_ADDRESS_DERIVATION + size_t deriveAddress_byron( const bip44_path_t* pathSpec, uint32_t protocolMagic, uint8_t* outBuffer, size_t outSize ); +#endif // APP_FEATURE_BYRON_ADDRESS_DERIVATION + +#ifdef APP_FEATURE_BYRON_PROTOCOL_MAGIC_CHECK + // Note: validates the overall address structure at the same time uint32_t extractProtocolMagic( const uint8_t* addressBuffer, size_t addressSize ); +#endif // APP_FEATURE_BYRON_PROTOCOL_MAGIC_CHECK + -#ifdef DEVEL +#if defined(DEVEL) && !defined(APP_XS) void run_addressUtilsByron_test(); #endif // DEVEL diff --git a/src/addressUtilsByron_test.c b/src/addressUtilsByron_test.c index 945418e5..444e3bb6 100644 --- a/src/addressUtilsByron_test.c +++ b/src/addressUtilsByron_test.c @@ -1,4 +1,4 @@ -#ifdef DEVEL +#if defined(DEVEL) && !defined(APP_XS) #include "addressUtilsByron.h" #include "cardano.h" diff --git a/src/addressUtilsShelley.c b/src/addressUtilsShelley.c index 7740708e..f6db8d23 100644 --- a/src/addressUtilsShelley.c +++ b/src/addressUtilsShelley.c @@ -439,8 +439,6 @@ size_t deriveAddress(const addressParams_t* addressParams, uint8_t* outBuffer, s ASSERT(outSize < BUFFER_SIZE_PARANOIA); ASSERT(isValidAddressParams(addressParams)); - const bip44_path_t* spendingPath = &addressParams->spendingKeyPath; - // shelley switch (addressParams->type) { case BASE_PAYMENT_KEY_STAKE_KEY: @@ -458,8 +456,16 @@ size_t deriveAddress(const addressParams_t* addressParams, uint8_t* outBuffer, s case REWARD_KEY: case REWARD_SCRIPT: return deriveAddress_reward(addressParams, outBuffer, outSize); + + #ifdef APP_FEATURE_BYRON_ADDRESS_DERIVATION case BYRON: - return deriveAddress_byron(spendingPath, addressParams->protocolMagic, outBuffer, outSize); + return deriveAddress_byron( + &addressParams->spendingKeyPath, + addressParams->protocolMagic, + outBuffer, outSize + ); + #endif // APP_FEATURE_BYRON_ADDRESS_DERIVATION + default: ASSERT(false); } @@ -698,6 +704,12 @@ bool isValidAddressParams(const addressParams_t* params) #define CHECK(cond) if (!(cond)) return false if (params->type != BYRON) { CHECK(isValidNetworkId(params->networkId)); + } else { + // code for Byron address derivation not available in XS app + // thus we cannot process address params + #ifndef APP_FEATURE_BYRON_ADDRESS_DERIVATION + return false; + #endif } CHECK(isValidStakingInfo(params)); diff --git a/src/cardano.h b/src/cardano.h index f0cfe29b..03f13e01 100644 --- a/src/cardano.h +++ b/src/cardano.h @@ -160,6 +160,8 @@ typedef struct { // ============================== NATIVE SCRIPTS ============================== +#ifdef APP_FEATURE_NATIVE_SCRIPT_HASH + // depth of n means it can handle up to n-1 levels of nesting #define MAX_SCRIPT_DEPTH 11 @@ -172,4 +174,6 @@ typedef enum { NATIVE_SCRIPT_INVALID_HEREAFTER = 5, } native_script_type; +#endif // APP_FEATURE_NATIVE_SCRIPT_HASH + #endif // H_CARDANO_APP_CARDANO diff --git a/src/deriveNativeScriptHash.c b/src/deriveNativeScriptHash.c index cac061f9..de8df37c 100644 --- a/src/deriveNativeScriptHash.c +++ b/src/deriveNativeScriptHash.c @@ -1,3 +1,5 @@ +#ifdef APP_FEATURE_NATIVE_SCRIPT_HASH + #include "deriveNativeScriptHash.h" #include "deriveNativeScriptHash_ui.h" #include "state.h" @@ -304,3 +306,5 @@ void deriveNativeScriptHash_handleAPDU( } #undef TRACE_WITH_CTX + +#endif // APP_FEATURE_NATIVE_SCRIPT_HASH diff --git a/src/deriveNativeScriptHash.h b/src/deriveNativeScriptHash.h index 2044a1d8..b26e59c4 100644 --- a/src/deriveNativeScriptHash.h +++ b/src/deriveNativeScriptHash.h @@ -1,6 +1,8 @@ #ifndef H_CARDANO_APP_DERIVE_NATIVE_SCRIPT_HASH #define H_CARDANO_APP_DERIVE_NATIVE_SCRIPT_HASH +#ifdef APP_FEATURE_NATIVE_SCRIPT_HASH + #include "bip44.h" #include "cardano.h" #include "common.h" @@ -48,4 +50,6 @@ typedef struct { ui_native_script_type ui_scriptType; } ins_derive_native_script_hash_context_t; +#endif // APP_FEATURE_NATIVE_SCRIPT_HASH + #endif // H_CARDANO_APP_DERIVE_NATIVE_SCRIPT_HASH diff --git a/src/deriveNativeScriptHash_ui.c b/src/deriveNativeScriptHash_ui.c index 716d3023..99bb376d 100644 --- a/src/deriveNativeScriptHash_ui.c +++ b/src/deriveNativeScriptHash_ui.c @@ -1,3 +1,5 @@ +#ifdef APP_FEATURE_NATIVE_SCRIPT_HASH + #include "deriveNativeScriptHash.h" #include "deriveNativeScriptHash_ui.h" #include "state.h" @@ -338,3 +340,5 @@ void deriveNativeScriptHash_displayNativeScriptHash_policyId() fill_and_display_if_required("Policy ID", bufferHex, deriveNativeScriptHash_displayNativeScriptHash_finish, respond_with_user_reject); #endif // HAVE_BAGL } + +#endif // APP_FEATURE_NATIVE_SCRIPT_HASH diff --git a/src/deriveNativeScriptHash_ui.h b/src/deriveNativeScriptHash_ui.h index 95460806..b4193c34 100644 --- a/src/deriveNativeScriptHash_ui.h +++ b/src/deriveNativeScriptHash_ui.h @@ -1,5 +1,8 @@ #ifndef H_CARDANO_APP_DERIVE_NATIVE_SCRIPT_HASH_UI #define H_CARDANO_APP_DERIVE_NATIVE_SCRIPT_HASH_UI + +#ifdef APP_FEATURE_NATIVE_SCRIPT_HASH + enum { DISPLAY_UI_STEP_POSITION = 200, #ifdef HAVE_NBGL @@ -17,4 +20,7 @@ void deriveNativeScriptHash_displayNativeScriptHash_callback(); void deriveNativeScriptHash_displayNativeScriptHash_bech32(); void deriveNativeScriptHash_displayNativeScriptHash_policyId(); + +#endif // APP_FEATURE_NATIVE_SCRIPT_HASH + #endif // H_CARDANO_APP_DERIVE_NATIVE_SCRIPT_HASH_UI diff --git a/src/getVersion.c b/src/getVersion.c index 3edf206b..2c831dfc 100644 --- a/src/getVersion.c +++ b/src/getVersion.c @@ -6,8 +6,9 @@ enum { - FLAG_DEVEL = 1, - + FLAG_DEVEL = 1 << 0, +// FLAG_HEADLESS = 1 << 1, + FLAG_APP_XS = 1 << 2, }; void getVersion_handleAPDU( @@ -40,12 +41,15 @@ void getVersion_handleAPDU( .major = MAJOR_VERSION, .minor = MINOR_VERSION, .patch = PATCH_VERSION, - .flags = 0, + .flags = 0, // see below }; #ifdef DEVEL response.flags |= FLAG_DEVEL; #endif // DEVEL + #ifdef APP_XS + response.flags |= FLAG_APP_XS; + #endif // APP_XS io_send_buf(SUCCESS, (uint8_t*) &response, sizeof(response)); ui_idle(); diff --git a/src/handlers.c b/src/handlers.c index 4c6cc5a1..db9792f4 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -29,11 +29,15 @@ handler_fn_t* lookupHandler(uint8_t ins) // 0x1* - public-key/address related CASE(0x10, getPublicKeys_handleAPDU); CASE(0x11, deriveAddress_handleAPDU); + #ifdef APP_FEATURE_NATIVE_SCRIPT_HASH CASE(0x12, deriveNativeScriptHash_handleAPDU); + #endif // APP_FEATURE_NATIVE_SCRIPT_HASH // 0x2* - signing related CASE(0x21, signTx_handleAPDU); + #ifdef APP_FEATURE_OPCERT CASE(0x22, signOpCert_handleAPDU); + #endif // APP_FEATURE_OPCERT CASE(0x23, signCVote_handleAPDU); #ifdef DEVEL diff --git a/src/ipUtils.c b/src/ipUtils.c index 7b6fc89f..2cbcfc31 100644 --- a/src/ipUtils.c +++ b/src/ipUtils.c @@ -1,3 +1,5 @@ +#ifdef APP_FEATURE_POOL_REGISTRATION + /* * Taken from glibc: * https://www.gnu.org/software/libc/sources.html @@ -159,3 +161,5 @@ void inet_ntop6 (const uint8_t* src, char* dst, size_t dstSize) strncpy(dst, tmp, dstSize); } + +#endif // APP_FEATURE_POOL_REGISTRATION diff --git a/src/ipUtils.h b/src/ipUtils.h index 111fa076..333945e8 100644 --- a/src/ipUtils.h +++ b/src/ipUtils.h @@ -1,6 +1,8 @@ #ifndef H_CARDANO_APP_IP_UTILS #define H_CARDANO_APP_IP_UTILS +#ifdef APP_FEATURE_POOL_REGISTRATION + #include "os.h" #define IPV4_STR_SIZE_MAX (sizeof "255.255.255.255") @@ -13,4 +15,6 @@ void inet_ntop6 (const uint8_t* src, char* dst, size_t dstSize); void run_ipUtils_test(); #endif // DEVEL -#endif // H_CARDANO_APP_SIGN_TX_UTILS +#endif // APP_FEATURE_POOL_REGISTRATION + +#endif // H_CARDANO_APP_IP_UTILS diff --git a/src/ipUtils_test.c b/src/ipUtils_test.c index 413bef7c..b54e019f 100644 --- a/src/ipUtils_test.c +++ b/src/ipUtils_test.c @@ -1,4 +1,4 @@ -#ifdef DEVEL +#if defined(DEVEL) && defined(APP_FEATURE_POOL_REGISTRATION) #include "ipUtils.h" #include "utils.h" @@ -55,4 +55,5 @@ void run_ipUtils_test() test3(); } -#endif // DEVEL +#endif // DEVEL && APP_FEATURE_POOL_REGISTRATION + diff --git a/src/messageSigning.c b/src/messageSigning.c index 41e5c00d..4d495ff1 100644 --- a/src/messageSigning.c +++ b/src/messageSigning.c @@ -64,6 +64,7 @@ void getCVoteRegistrationSignature(bip44_path_t* pathSpec, #endif } +#ifdef APP_FEATURE_OPCERT void getOpCertSignature(bip44_path_t* pathSpec, const uint8_t* opCertBodyBuffer, size_t opCertBodySize, uint8_t* outBuffer, size_t outSize) @@ -74,3 +75,4 @@ void getOpCertSignature(bip44_path_t* pathSpec, signRawMessageWithPath(pathSpec, opCertBodyBuffer, opCertBodySize, outBuffer, outSize); } +#endif // APP_FEATURE_OPCERT diff --git a/src/messageSigning.h b/src/messageSigning.h index fbf18f89..e2db31f8 100644 --- a/src/messageSigning.h +++ b/src/messageSigning.h @@ -11,8 +11,10 @@ void getCVoteRegistrationSignature(bip44_path_t* pathSpec, const uint8_t* payloadHashBuffer, size_t payloadHashSize, uint8_t* outBuffer, size_t outSize); +#ifdef APP_FEATURE_OPCERT void getOpCertSignature(bip44_path_t* pathSpec, const uint8_t* opCertBodyBuffer, size_t opCertBodySize, uint8_t* outBuffer, size_t outSize); +#endif // APP_FEATURE_OPCERT #endif // H_CARDANO_APP_MESSAGE_SIGNING diff --git a/src/nativeScriptHashBuilder.c b/src/nativeScriptHashBuilder.c index 6bea3f82..7e4a80b0 100644 --- a/src/nativeScriptHashBuilder.c +++ b/src/nativeScriptHashBuilder.c @@ -1,3 +1,5 @@ +#ifdef APP_FEATURE_NATIVE_SCRIPT_HASH + #include "cbor.h" #include "nativeScriptHashBuilder.h" @@ -227,3 +229,5 @@ void nativeScriptHashBuilder_finalize( #undef APPEND_CBOR #undef _TRACE_BUFFER #undef _TRACE + +#endif // APP_FEATURE_NATIVE_SCRIPT_HASH diff --git a/src/nativeScriptHashBuilder.h b/src/nativeScriptHashBuilder.h index de2653e4..a5508fb1 100644 --- a/src/nativeScriptHashBuilder.h +++ b/src/nativeScriptHashBuilder.h @@ -1,6 +1,8 @@ #ifndef H_CARDANO_APP_NATIVE_SCRIPT_HASH_BUILDER #define H_CARDANO_APP_NATIVE_SCRIPT_HASH_BUILDER +#ifdef APP_FEATURE_NATIVE_SCRIPT_HASH + #include "cardano.h" #include "hash.h" @@ -59,4 +61,6 @@ void nativeScriptHashBuilder_finalize( void run_nativeScriptHashBuilder_test(); /* #endif // DEVEL */ +#endif // APP_FEATURE_NATIVE_SCRIPT_HASH + #endif // H_CARDANO_APP_NATIVE_SCRIPT_HASH_BUILDER diff --git a/src/nativeScriptHashBuilder_test.c b/src/nativeScriptHashBuilder_test.c index c3f2e88c..5bcc340c 100644 --- a/src/nativeScriptHashBuilder_test.c +++ b/src/nativeScriptHashBuilder_test.c @@ -1,4 +1,4 @@ -#ifdef DEVEL +#if defined(DEVEL) && defined(APP_FEATURE_NATIVE_SCRIPT_HASH) #include "hexUtils.h" #include "nativeScriptHashBuilder.h" @@ -105,4 +105,5 @@ void run_nativeScriptHashBuilder_test() #undef FINALIZE #undef BEFORE_EACH #undef BUF_FROM_STR -#endif // DEVEL + +#endif // DEVEL && APP_FEATURE_NATIVE_SCRIPT_HASH diff --git a/src/runTests.c b/src/runTests.c index 8134edaa..959b63ca 100644 --- a/src/runTests.c +++ b/src/runTests.c @@ -32,6 +32,7 @@ void handleRunTests( // Note: Make sure to have RESET_ON_CRASH flag disabled // as it interferes with tests verifying assertions BEGIN_ASSERT_NOEXCEPT { + PRINTF("Running tests\n"); run_hex_test(); run_base58_test(); @@ -40,17 +41,26 @@ void handleRunTests( run_endian_test(); run_textUtils_test(); run_tokens_test(); + #if defined(APP_FEATURE_POOL_REGISTRATION) run_ipUtils_test(); + #endif run_hash_test(); run_cbor_test(); run_bip44_test(); run_key_derivation_test(); + #if !defined(APP_XS) run_addressUtilsByron_test(); + #endif run_addressUtilsShelley_test(); + #if !defined(APP_XS) run_txHashBuilder_test(); + #endif run_auxDataHashBuilder_test(); + #if defined(APP_FEATURE_NATIVE_SCRIPT_HASH) run_nativeScriptHashBuilder_test(); + #endif PRINTF("All tests done\n"); + } END_ASSERT_NOEXCEPT; io_send_buf(SUCCESS, NULL, 0); diff --git a/src/securityPolicy.c b/src/securityPolicy.c index 18d31fe8..a5ffd4b2 100644 --- a/src/securityPolicy.c +++ b/src/securityPolicy.c @@ -463,7 +463,7 @@ security_policy_t policyForSignTxInput(sign_tx_signingmode_t txSigningMode) static bool is_addressBytes_suitable_for_tx_output( const uint8_t* addressBuffer, size_t addressSize, - const uint8_t networkId, const uint32_t protocolMagic + const uint8_t networkId, const uint32_t protocolMagic __attribute__((unused)) ) { ASSERT(addressSize < BUFFER_SIZE_PARANOIA); @@ -482,7 +482,9 @@ static bool is_addressBytes_suitable_for_tx_output( return false; case BYRON: + #ifdef APP_FEATURE_BYRON_PROTOCOL_MAGIC_CHECK CHECK(extractProtocolMagic(addressBuffer, addressSize) == protocolMagic); + #endif // APP_FEATURE_BYRON_PROTOCOL_MAGIC_CHECK break; default: { @@ -1091,6 +1093,8 @@ security_policy_t policyForSignTxCertificateStaking( DENY(); // should not be reached } +#ifdef APP_FEATURE_POOL_RETIREMENT + security_policy_t policyForSignTxCertificateStakePoolRetirement( sign_tx_signingmode_t txSigningMode, const bip44_path_t* poolIdPath, @@ -1115,6 +1119,10 @@ security_policy_t policyForSignTxCertificateStakePoolRetirement( DENY(); // should not be reached } +#endif // APP_FEATURE_POOL_RETIREMENT + +#ifdef APP_FEATURE_POOL_REGISTRATION + security_policy_t policyForSignTxStakePoolRegistrationInit( sign_tx_signingmode_t txSigningMode, uint32_t numOwners @@ -1285,6 +1293,8 @@ security_policy_t policyForSignTxStakePoolRegistrationConfirm( ALLOW(); } +#endif // APP_FEATURE_POOL_REGISTRATION + // For each withdrawal security_policy_t policyForSignTxWithdrawal( sign_tx_signingmode_t txSigningMode, @@ -1461,6 +1471,8 @@ static inline security_policy_t _plutusWitnessPolicy(const bip44_path_t* path, b } } +#ifdef APP_FEATURE_POOL_REGISTRATION + static inline security_policy_t _poolRegistrationOwnerWitnessPolicy(const bip44_path_t* witnessPath, const bip44_path_t* poolOwnerPath) { switch (bip44_classifyPath(witnessPath)) { @@ -1504,6 +1516,8 @@ static inline security_policy_t _poolRegistrationOperatorWitnessPolicy(const bip } } +#endif // APP_FEATURE_POOL_REGISTRATION + // For each transaction witness // Note: witnesses reveal public key of an address and Ledger *does not* check // whether they correspond to previously declared inputs and certificates @@ -1511,7 +1525,7 @@ security_policy_t policyForSignTxWitness( sign_tx_signingmode_t txSigningMode, const bip44_path_t* witnessPath, bool mintPresent, - const bip44_path_t* poolOwnerPath + const bip44_path_t* poolOwnerPath __attribute__((unused)) ) { switch (txSigningMode) { @@ -1524,12 +1538,16 @@ security_policy_t policyForSignTxWitness( case SIGN_TX_SIGNINGMODE_PLUTUS_TX: return _plutusWitnessPolicy(witnessPath, mintPresent); + #ifdef APP_FEATURE_POOL_REGISTRATION + case SIGN_TX_SIGNINGMODE_POOL_REGISTRATION_OWNER: return _poolRegistrationOwnerWitnessPolicy(witnessPath, poolOwnerPath); case SIGN_TX_SIGNINGMODE_POOL_REGISTRATION_OPERATOR: return _poolRegistrationOperatorWitnessPolicy(witnessPath); + #endif // APP_FEATURE_POOL_REGISTRATION + default: ASSERT(false); } @@ -1853,6 +1871,7 @@ security_policy_t policyForCVoteRegistrationConfirm() PROMPT(); } +#ifdef APP_FEATURE_OPCERT security_policy_t policyForSignOpCert(const bip44_path_t* poolColdKeyPathSpec) { switch (bip44_classifyPath(poolColdKeyPathSpec)) { @@ -1872,6 +1891,7 @@ security_policy_t policyForSignOpCert(const bip44_path_t* poolColdKeyPathSpec) DENY(); // should not be reached } +#endif // APP_FEATURE_OPCERT security_policy_t policyForSignCVoteInit() { diff --git a/src/securityPolicy.h b/src/securityPolicy.h index 37613d7c..299c4298 100644 --- a/src/securityPolicy.h +++ b/src/securityPolicy.h @@ -113,11 +113,14 @@ security_policy_t policyForSignTxCertificateStaking( const certificate_type_t certificateType, const stake_credential_t* stakeCredential ); +#ifdef APP_FEATURE_POOL_RETIREMENT security_policy_t policyForSignTxCertificateStakePoolRetirement( sign_tx_signingmode_t txSigningMode, const bip44_path_t* stakeCredential, uint64_t epoch ); +#endif // APP_FEATURE_POOL_RETIREMENT +#ifdef APP_FEATURE_POOL_REGISTRATION security_policy_t policyForSignTxStakePoolRegistrationInit( sign_tx_signingmode_t txSigningMode, uint32_t numOwners @@ -147,7 +150,7 @@ security_policy_t policyForSignTxStakePoolRegistrationNoMetadata(); security_policy_t policyForSignTxStakePoolRegistrationConfirm( uint32_t numOwners, uint32_t numRelays ); - +#endif // APP_FEATURE_POOL_REGISTRATION security_policy_t policyForSignTxWithdrawal( sign_tx_signingmode_t txSigningMode, const stake_credential_t* stakeCredential @@ -185,7 +188,9 @@ security_policy_t policyForSignTxReferenceInput(const sign_tx_signingmode_t txSi security_policy_t policyForSignTxConfirm(); +#ifdef APP_FEATURE_OPCERT security_policy_t policyForSignOpCert(const bip44_path_t* poolColdKeyPathSpec); +#endif // APP_FEATURE_OPCERT security_policy_t policyForCVoteRegistrationVoteKey(); security_policy_t policyForCVoteRegistrationVoteKeyPath( diff --git a/src/signOpCert.c b/src/signOpCert.c index 9a3302b4..b28e820c 100644 --- a/src/signOpCert.c +++ b/src/signOpCert.c @@ -1,3 +1,5 @@ +#ifdef APP_FEATURE_OPCERT + #include "common.h" #include "signOpCert.h" @@ -243,3 +245,5 @@ static void signOpCert_ui_runStep() } UI_STEP_END(UI_STEP_INVALID); } + +#endif // APP_FEATURE_OPCERT diff --git a/src/signOpCert.h b/src/signOpCert.h index af844792..e88eec68 100644 --- a/src/signOpCert.h +++ b/src/signOpCert.h @@ -1,6 +1,8 @@ #ifndef H_CARDANO_APP_SIGN_OP_CERT #define H_CARDANO_APP_SIGN_OP_CERT +#ifdef APP_FEATURE_OPCERT + #include "common.h" #include "handlers.h" #include "bip44.h" @@ -20,4 +22,6 @@ typedef struct { int ui_step; } ins_sign_op_cert_context_t; +#endif // APP_FEATURE_OPCERT + #endif // H_CARDANO_APP_SIGN_OP_CERT \ No newline at end of file diff --git a/src/signTx.c b/src/signTx.c index 65746842..f8efc472 100644 --- a/src/signTx.c +++ b/src/signTx.c @@ -188,10 +188,14 @@ void tx_advanceStage() } ctx->stage = SIGN_STAGE_BODY_MINT; if (ctx->includeMint) { + #ifdef APP_FEATURE_TOKEN_MINTING txHashBuilder_enterMint(&BODY_CTX->txHashBuilder); signTxMint_init(); // wait for mint APDU break; + #else + ASSERT(false); + #endif // APP_FEATURE_TOKEN_MINTING } __attribute__((fallthrough)); @@ -312,7 +316,11 @@ void tx_advanceCertificatesStateIfAppropriate() break; default: + #ifdef APP_FEATURE_POOL_REGISTRATION ASSERT(ctx->stage == SIGN_STAGE_BODY_CERTIFICATES_POOL_SUBMACHINE); + #else + ASSERT(false); + #endif // APP_FEATURE_POOL_REGISTRATION } } @@ -343,6 +351,8 @@ static inline void checkForFinishedSubmachines() } break; + #ifdef APP_FEATURE_POOL_REGISTRATION + case SIGN_STAGE_BODY_CERTIFICATES_POOL_SUBMACHINE: if (signTxPoolRegistration_isFinished()) { TRACE(); @@ -353,6 +363,8 @@ static inline void checkForFinishedSubmachines() } break; + #endif // APP_FEATURE_POOL_REGISTRATION + case SIGN_STAGE_AUX_DATA_CVOTE_REGISTRATION_SUBMACHINE: if (signTxCVoteRegistration_isFinished()) { TRACE(); @@ -367,6 +379,8 @@ static inline void checkForFinishedSubmachines() } break; + #ifdef APP_FEATURE_TOKEN_MINTING + case SIGN_STAGE_BODY_MINT_SUBMACHINE: if (signTxMint_isFinished()) { TRACE(); @@ -376,6 +390,8 @@ static inline void checkForFinishedSubmachines() } break; + #endif // APP_FEATURE_TOKEN_MINTING + case SIGN_STAGE_BODY_COLLATERAL_OUTPUT_SUBMACHINE: if (isCurrentOutputFinished()) { TRACE(); @@ -480,13 +496,19 @@ static void signTx_handleInitAPDU(uint8_t p2, const uint8_t* wireDataBuffer, siz TRACE("Signing mode %d", (int) ctx->commonTxData.txSigningMode); switch (ctx->commonTxData.txSigningMode) { case SIGN_TX_SIGNINGMODE_ORDINARY_TX: - case SIGN_TX_SIGNINGMODE_POOL_REGISTRATION_OWNER: - case SIGN_TX_SIGNINGMODE_POOL_REGISTRATION_OPERATOR: case SIGN_TX_SIGNINGMODE_MULTISIG_TX: case SIGN_TX_SIGNINGMODE_PLUTUS_TX: // these signing modes are allowed break; + case SIGN_TX_SIGNINGMODE_POOL_REGISTRATION_OWNER: + case SIGN_TX_SIGNINGMODE_POOL_REGISTRATION_OPERATOR: + // these are allowed unless we have the XS app which does not have code for handling them + #ifndef APP_FEATURE_POOL_REGISTRATION + THROW(ERR_INVALID_DATA); + #endif // APP_FEATURE_POOL_REGISTRATION + break; + default: THROW(ERR_INVALID_DATA); } @@ -535,6 +557,13 @@ static void signTx_handleInitAPDU(uint8_t p2, const uint8_t* wireDataBuffer, siz ctx->shouldDisplayTxid = false; } + // minting not included in the XS app + #ifndef APP_FEATURE_TOKEN_MINTING + if (ctx->includeMint) { + THROW(ERR_INVALID_DATA); + } + #endif // APP_FEATURE_TOKEN_MINTING + security_policy_t policy = policyForSignTxInit( ctx->commonTxData.txSigningMode, ctx->commonTxData.networkId, @@ -925,16 +954,24 @@ static void _parseCertificateData(const uint8_t* wireDataBuffer, size_t wireData view_parseBuffer(certificateData->poolKeyHash, &view, POOL_KEY_HASH_LENGTH); break; + #ifdef APP_FEATURE_POOL_REGISTRATION + case CERTIFICATE_TYPE_STAKE_POOL_REGISTRATION: // nothing more to parse, certificate data will be provided // in additional APDUs processed by a submachine return; + #endif // APP_FEATURE_POOL_REGISTRATION + + #ifdef APP_FEATURE_POOL_RETIREMENT + case CERTIFICATE_TYPE_STAKE_POOL_RETIREMENT: _parsePathSpec(&view, &certificateData->poolIdPath); certificateData->epoch = parse_u8be(&view); break; + #endif // APP_FEATURE_POOL_RETIREMENT + default: THROW(ERR_INVALID_DATA); } @@ -1016,6 +1053,8 @@ static void _addCertificateDataToTx( break; } + #ifdef APP_FEATURE_POOL_RETIREMENT + case CERTIFICATE_TYPE_STAKE_POOL_RETIREMENT: { _fillHashFromPath(&BODY_CTX->stageData.certificate.poolIdPath, certificateData->poolKeyHash, SIZEOF(certificateData->poolKeyHash)); txHashBuilder_addCertificate_poolRetirement( @@ -1026,6 +1065,8 @@ static void _addCertificateDataToTx( break; } + #endif // APP_FEATURE_POOL_RETIREMENT + default: ASSERT(false); } @@ -1038,6 +1079,7 @@ static void signTx_handleCertificateAPDU(uint8_t p2, const uint8_t* wireDataBuff ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); ASSERT(BODY_CTX->currentCertificate < ctx->numCertificates); + #ifdef APP_FEATURE_POOL_REGISTRATION // delegate to state sub-machine for stake pool registration certificate data if (signTxPoolRegistration_isValidInstruction(p2)) { TRACE(); @@ -1048,6 +1090,7 @@ static void signTx_handleCertificateAPDU(uint8_t p2, const uint8_t* wireDataBuff signTxPoolRegistration_handleAPDU(p2, wireDataBuffer, wireDataSize); return; } + #endif // APP_FEATURE_POOL_REGISTRATION VALIDATE(p2 == P2_UNUSED, ERR_INVALID_REQUEST_PARAMETERS); CHECK_STAGE(SIGN_STAGE_BODY_CERTIFICATES); @@ -1097,6 +1140,8 @@ static void signTx_handleCertificateAPDU(uint8_t p2, const uint8_t* wireDataBuff return; } + #ifdef APP_FEATURE_POOL_REGISTRATION + case CERTIFICATE_TYPE_STAKE_POOL_REGISTRATION: { // pool registration certificates have a separate sub-machine for handling APDU and UI // nothing more to be done with them here, we just init the sub-machine @@ -1107,6 +1152,10 @@ static void signTx_handleCertificateAPDU(uint8_t p2, const uint8_t* wireDataBuff return; } + #endif // APP_FEATURE_POOL_REGISTRATION + + #ifdef APP_FEATURE_POOL_RETIREMENT + case CERTIFICATE_TYPE_STAKE_POOL_RETIREMENT: { security_policy_t policy = policyForSignTxCertificateStakePoolRetirement( ctx->commonTxData.txSigningMode, @@ -1130,6 +1179,8 @@ static void signTx_handleCertificateAPDU(uint8_t p2, const uint8_t* wireDataBuff return; } + #endif // APP_FEATURE_POOL_RETIREMENT + default: ASSERT(false); } @@ -1305,6 +1356,8 @@ static void signTx_handleValidityIntervalStartAPDU(uint8_t p2, const uint8_t* wi // ============================== MINT ============================== +#ifdef APP_FEATURE_TOKEN_MINTING + static void signTx_handleMintAPDU(uint8_t p2, const uint8_t* wireDataBuffer, size_t wireDataSize) { { @@ -1324,6 +1377,8 @@ static void signTx_handleMintAPDU(uint8_t p2, const uint8_t* wireDataBuffer, siz signTxMint_handleAPDU(p2, wireDataBuffer, wireDataSize); } +#endif // APP_FEATURE_TOKEN_MINTING + // ========================= SCRIPT DATA HASH ========================== static void signTx_handleScriptDataHashAPDU(uint8_t p2, const uint8_t* wireDataBuffer, size_t wireDataSize) @@ -1778,7 +1833,9 @@ static subhandler_fn_t* lookup_subhandler(uint8_t p1) CASE(0x06, signTx_handleCertificateAPDU); CASE(0x07, signTx_handleWithdrawalAPDU); CASE(0x09, signTx_handleValidityIntervalStartAPDU); + #ifdef APP_FEATURE_TOKEN_MINTING CASE(0x0b, signTx_handleMintAPDU); + #endif // APP_FEATURE_TOKEN_MINTING CASE(0x0c, signTx_handleScriptDataHashAPDU); CASE(0x0d, signTx_handleCollateralInputAPDU); CASE(0x0e, signTx_handleRequiredSignerAPDU); @@ -1819,11 +1876,15 @@ void signTx_handleAPDU( case SIGN_STAGE_BODY_FEE: case SIGN_STAGE_BODY_TTL: case SIGN_STAGE_BODY_CERTIFICATES: + #ifdef APP_FEATURE_POOL_REGISTRATION case SIGN_STAGE_BODY_CERTIFICATES_POOL_SUBMACHINE: + #endif // APP_FEATURE_POOL_REGISTRATION case SIGN_STAGE_BODY_WITHDRAWALS: case SIGN_STAGE_BODY_VALIDITY_INTERVAL: case SIGN_STAGE_BODY_MINT: + #ifdef APP_FEATURE_TOKEN_MINTING case SIGN_STAGE_BODY_MINT_SUBMACHINE: + #endif // APP_FEATURE_TOKEN_MINTING case SIGN_STAGE_BODY_SCRIPT_DATA_HASH: case SIGN_STAGE_BODY_COLLATERAL_INPUTS: case SIGN_STAGE_BODY_REQUIRED_SIGNERS: @@ -1869,11 +1930,15 @@ ins_sign_tx_body_context_t* accessBodyContext() case SIGN_STAGE_BODY_FEE: case SIGN_STAGE_BODY_TTL: case SIGN_STAGE_BODY_CERTIFICATES: + #ifdef APP_FEATURE_POOL_REGISTRATION case SIGN_STAGE_BODY_CERTIFICATES_POOL_SUBMACHINE: + #endif // APP_FEATURE_POOL_REGISTRATION case SIGN_STAGE_BODY_WITHDRAWALS: case SIGN_STAGE_BODY_VALIDITY_INTERVAL: case SIGN_STAGE_BODY_MINT: + #ifdef APP_FEATURE_TOKEN_MINTING case SIGN_STAGE_BODY_MINT_SUBMACHINE: + #endif // APP_FEATURE_TOKEN_MINTING case SIGN_STAGE_BODY_SCRIPT_DATA_HASH: case SIGN_STAGE_BODY_COLLATERAL_INPUTS: case SIGN_STAGE_BODY_REQUIRED_SIGNERS: diff --git a/src/signTx.h b/src/signTx.h index e9573733..aa3d8555 100644 --- a/src/signTx.h +++ b/src/signTx.h @@ -33,11 +33,15 @@ typedef enum { SIGN_STAGE_BODY_FEE = 29, SIGN_STAGE_BODY_TTL = 30, SIGN_STAGE_BODY_CERTIFICATES = 31, + #ifdef APP_FEATURE_POOL_REGISTRATION SIGN_STAGE_BODY_CERTIFICATES_POOL_SUBMACHINE = 32, // pool registration certificate sub-machine + #endif // APP_FEATURE_POOL_REGISTRATION SIGN_STAGE_BODY_WITHDRAWALS = 33, SIGN_STAGE_BODY_VALIDITY_INTERVAL = 34, SIGN_STAGE_BODY_MINT = 35, + #ifdef APP_FEATURE_TOKEN_MINTING SIGN_STAGE_BODY_MINT_SUBMACHINE = 36, + #endif // APP_FEATURE_TOKEN_MINTING SIGN_STAGE_BODY_SCRIPT_DATA_HASH = 37, SIGN_STAGE_BODY_COLLATERAL_INPUTS = 38, SIGN_STAGE_BODY_REQUIRED_SIGNERS = 39, @@ -172,9 +176,13 @@ typedef struct { } stageData; union { + #ifdef APP_FEATURE_POOL_REGISTRATION pool_registration_context_t pool_registration_subctx; + #endif // APP_FEATURE_POOL_REGISTRATION output_context_t output_subctx; + #ifdef APP_FEATURE_TOKEN_MINTING mint_context_t mint_subctx; + #endif // APP_FEATURE_TOKEN_MINTING } stageContext; } ins_sign_tx_body_context_t; diff --git a/src/signTxMint.c b/src/signTxMint.c index 589b499b..88ca8bc5 100644 --- a/src/signTxMint.c +++ b/src/signTxMint.c @@ -1,3 +1,5 @@ +#ifdef APP_FEATURE_TOKEN_MINTING + #include "signTxMint.h" #include "signTxMint_ui.h" #include "signTxUtils.h" @@ -290,3 +292,5 @@ bool signTxMint_isFinished() ASSERT(false); } } + +#endif // APP_FEATURE_TOKEN_MINTING diff --git a/src/signTxMint.h b/src/signTxMint.h index 97958082..51389107 100644 --- a/src/signTxMint.h +++ b/src/signTxMint.h @@ -1,6 +1,8 @@ #ifndef H_CARDANO_APP_SIGN_TX_MINT #define H_CARDANO_APP_SIGN_TX_MINT +#ifdef APP_FEATURE_TOKEN_MINTING + #include "common.h" #include "cardano.h" #include "addressUtilsShelley.h" @@ -55,4 +57,6 @@ void signTxMint_handleAPDU(uint8_t p2, const uint8_t* wireDataBuffer, size_t wir bool signTxMint_isFinished(); +#endif // APP_FEATURE_TOKEN_MINTING + #endif // H_CARDANO_APP_SIGN_TX_MINT diff --git a/src/signTxMint_ui.c b/src/signTxMint_ui.c index 5638b5e8..456871ec 100644 --- a/src/signTxMint_ui.c +++ b/src/signTxMint_ui.c @@ -1,3 +1,5 @@ +#ifdef APP_FEATURE_TOKEN_MINTING + #include "signTxMint.h" #include "signTxMint_ui.h" #include "signTxUtils.h" @@ -203,3 +205,5 @@ void signTxMint_handleConfirm_ui_runStep() } UI_STEP_END(HANDLE_CONFIRM_STEP_INVALID); } + +#endif // APP_FEATURE_TOKEN_MINTING \ No newline at end of file diff --git a/src/signTxMint_ui.h b/src/signTxMint_ui.h index be87f434..58f5aeaf 100644 --- a/src/signTxMint_ui.h +++ b/src/signTxMint_ui.h @@ -1,6 +1,8 @@ #ifndef H_CARDANO_APP_SIGN_TX_MINT_UI #define H_CARDANO_APP_SIGN_TX_MINT_UI +#ifdef APP_FEATURE_TOKEN_MINTING + enum { HANDLE_MINT_TOP_LEVEL_DATA_DISPLAY = 9200, HANDLE_MINT_TOP_LEVEL_DATA_RESPOND, @@ -34,4 +36,6 @@ enum { void signTxMint_handleConfirm_ui_runStep(); +#endif // APP_FEATURE_TOKEN_MINTING + #endif // H_CARDANO_APP_SIGN_TX_MINT_UI diff --git a/src/signTxPoolRegistration.c b/src/signTxPoolRegistration.c index 80fc4935..d66f6f81 100644 --- a/src/signTxPoolRegistration.c +++ b/src/signTxPoolRegistration.c @@ -1,3 +1,5 @@ +#ifdef APP_FEATURE_POOL_REGISTRATION + #include "signTx.h" #include "signTxPoolRegistration_ui.h" #include "state.h" @@ -969,3 +971,5 @@ void signTxPoolRegistration_handleAPDU(uint8_t p2, const uint8_t* wireDataBuffer ASSERT(false); } } + +#endif // APP_FEATURE_POOL_REGISTRATION diff --git a/src/signTxPoolRegistration.h b/src/signTxPoolRegistration.h index 7363147b..85504acb 100644 --- a/src/signTxPoolRegistration.h +++ b/src/signTxPoolRegistration.h @@ -1,6 +1,8 @@ #ifndef H_CARDANO_APP_SIGN_TX_POOL_REGISTRATION #define H_CARDANO_APP_SIGN_TX_POOL_REGISTRATION +#ifdef APP_FEATURE_POOL_REGISTRATION + #include "common.h" #include "cardano.h" #include "txHashBuilder.h" @@ -83,4 +85,6 @@ void signTxPoolRegistration_handleAPDU(uint8_t p2, const uint8_t* wireDataBuffer bool signTxPoolRegistration_isFinished(); +#endif // APP_FEATURE_POOL_REGISTRATION + #endif // H_CARDANO_APP_SIGN_TX_POOL_REGISTRATION diff --git a/src/signTxPoolRegistration_ui.c b/src/signTxPoolRegistration_ui.c index 04ece815..855d840b 100644 --- a/src/signTxPoolRegistration_ui.c +++ b/src/signTxPoolRegistration_ui.c @@ -1,3 +1,5 @@ +#ifdef APP_FEATURE_POOL_REGISTRATION + #include "signTx.h" #include "signTxPoolRegistration_ui.h" #include "state.h" @@ -792,3 +794,5 @@ void signTxPoolRegistration_handleConfirm_ui_runStep() } UI_STEP_END(HANDLE_CONFIRM_STEP_INVALID); } + +#endif // APP_FEATURE_POOL_REGISTRATION diff --git a/src/signTxPoolRegistration_ui.h b/src/signTxPoolRegistration_ui.h index b155805f..b9b60ea2 100644 --- a/src/signTxPoolRegistration_ui.h +++ b/src/signTxPoolRegistration_ui.h @@ -1,6 +1,8 @@ #ifndef H_CARDANO_APP_SIGN_TX_POOL_REGISATRATION_UI #define H_CARDANO_APP_SIGN_TX_POOL_REGISATRATION_UI +#ifdef APP_FEATURE_POOL_REGISTRATION + // ============================== INIT ============================== enum { @@ -122,4 +124,7 @@ enum { }; void signTxPoolRegistration_handleConfirm_ui_runStep(); + +#endif // APP_FEATURE_POOL_REGISTRATION + #endif // H_CARDANO_APP_SIGN_TX_POOL_REGISATRATION_UI diff --git a/src/signTx_ui.c b/src/signTx_ui.c index 97b71c73..91c00b91 100644 --- a/src/signTx_ui.c +++ b/src/signTx_ui.c @@ -74,12 +74,17 @@ static const char* _newTxLine1(sign_tx_signingmode_t txSigningMode) #ifdef HAVE_NBGL static void signTx_handleInit_ui_runStep_cb(void) { + // if the protocol magic check is not enabled, + // displaying the protocol magic might be misleading, + // so we must not show it + #ifdef APP_FEATURE_BYRON_PROTOCOL_MAGIC_CHECK char networkParams[100] = {0}; ui_getNetworkParamsScreen_2( networkParams, SIZEOF(networkParams), ctx->commonTxData.protocolMagic); fill_and_display_if_required("Protocol magic", networkParams, signTx_handleInit_ui_runStep, respond_with_user_reject); + #endif } #endif // HAVE_NBGL @@ -118,7 +123,8 @@ void signTx_handleInit_ui_runStep() #ifdef HAVE_BAGL ui_displayNetworkParamsScreen( "Network details", - ctx->commonTxData.networkId, ctx->commonTxData.protocolMagic, + ctx->commonTxData.networkId, + ctx->commonTxData.protocolMagic, this_fn ); #elif defined(HAVE_NBGL) @@ -518,6 +524,8 @@ void signTx_handleCertificate_ui_runStep() UI_STEP_END(HANDLE_CERTIFICATE_STEP_INVALID); } +#ifdef APP_FEATURE_POOL_RETIREMENT + void signTx_handleCertificatePoolRetirement_ui_runStep() { TRACE("UI step %d", ctx->ui_step); @@ -579,6 +587,8 @@ void signTx_handleCertificatePoolRetirement_ui_runStep() UI_STEP_END(HANDLE_CERTIFICATE_POOL_RETIREMENT_STEP_INVALID); } +#endif // APP_FEATURE_POOL_RETIREMENT + // ============================== WITHDRAWALS ============================== void signTx_handleWithdrawal_ui_runStep() diff --git a/src/state.h b/src/state.h index 8ca7f18a..f95bcb53 100644 --- a/src/state.h +++ b/src/state.h @@ -14,9 +14,13 @@ typedef union { // Here should go states of all instructions ins_get_keys_context_t getKeysContext; ins_derive_address_context_t deriveAddressContext; + #ifdef APP_FEATURE_NATIVE_SCRIPT_HASH ins_derive_native_script_hash_context_t deriveNativeScriptHashContext; + #endif // APP_FEATURE_NATIVE_SCRIPT_HASH ins_sign_tx_context_t signTxContext; + #ifdef APP_FEATURE_OPCERT ins_sign_op_cert_context_t signOpCertContext; + #endif // APP_FEATURE_OPCERT ins_sign_cvote_context_t signCVoteContext; } instructionState_t; diff --git a/src/txHashBuilder.c b/src/txHashBuilder.c index 3c0ff551..43efdff3 100644 --- a/src/txHashBuilder.c +++ b/src/txHashBuilder.c @@ -850,6 +850,8 @@ void txHashBuilder_addCertificate_delegation( } } +#ifdef APP_FEATURE_POOL_RETIREMENT + void txHashBuilder_addCertificate_poolRetirement( tx_hash_builder_t* builder, const uint8_t* poolKeyHash, size_t poolKeyHashSize, @@ -882,6 +884,10 @@ void txHashBuilder_addCertificate_poolRetirement( } } +#endif // APP_FEATURE_POOL_RETIREMENT + +#ifdef APP_FEATURE_POOL_REGISTRATION + void txHashBuilder_poolRegistrationCertificate_enter( tx_hash_builder_t* builder, uint16_t numOwners, uint16_t numRelays @@ -1276,6 +1282,8 @@ void txHashBuilder_addPoolRegistrationCertificate_addPoolMetadata_null( builder->state = TX_HASH_BUILDER_IN_CERTIFICATES; } +#endif // APP_FEATURE_POOL_REGISTRATION + static void txHashBuilder_assertCanLeaveCertificates(tx_hash_builder_t* builder) { _TRACE("state = %d, remainingCertificates = %u", builder->state, builder->remainingCertificates); @@ -1430,6 +1438,8 @@ static void txHashBuilder_assertCanLeaveValidityIntervalStart(tx_hash_builder_t* // ============================== MINT ============================== +#ifdef APP_FEATURE_TOKEN_MINTING + void txHashBuilder_enterMint(tx_hash_builder_t* builder) { _TRACE("state = %d", builder->state); @@ -1486,6 +1496,8 @@ void txHashBuilder_addMint_token( amount < 0 ? CBOR_TYPE_NEGATIVE : CBOR_TYPE_UNSIGNED); } +#endif // APP_FEATURE_TOKEN_MINTING + static void txHashBuilder_assertCanLeaveMint(tx_hash_builder_t* builder) { _TRACE("state = %u, remainingMintAssetGroups = %u, remainingMintTokens = %u", diff --git a/src/txHashBuilder.h b/src/txHashBuilder.h index e633bca6..34e0b675 100644 --- a/src/txHashBuilder.h +++ b/src/txHashBuilder.h @@ -264,12 +264,18 @@ void txHashBuilder_addCertificate_delegation( const uint8_t* poolKeyHash, size_t poolKeyHashSize ); +#ifdef APP_FEATURE_POOL_RETIREMENT + void txHashBuilder_addCertificate_poolRetirement( tx_hash_builder_t* builder, const uint8_t* poolKeyHash, size_t poolKeyHashSize, uint64_t epoch ); +#endif // APP_FEATURE_POOL_RETIREMENT + +#ifdef APP_FEATURE_POOL_REGISTRATION + void txHashBuilder_poolRegistrationCertificate_enter( tx_hash_builder_t* builder, uint16_t numOwners, uint16_t numRelays @@ -320,6 +326,8 @@ void txHashBuilder_addPoolRegistrationCertificate_addPoolMetadata_null( tx_hash_builder_t* builder ); +#endif // APP_FEATURE_POOL_REGISTRATION + void txHashBuilder_enterWithdrawals(tx_hash_builder_t* builder); void txHashBuilder_addWithdrawal( @@ -338,6 +346,8 @@ void txHashBuilder_addValidityIntervalStart( uint64_t validityIntervalStart ); +#ifdef APP_FEATURE_TOKEN_MINTING + void txHashBuilder_enterMint(tx_hash_builder_t* builder); void txHashBuilder_addMint_topLevelData( @@ -356,6 +366,8 @@ void txHashBuilder_addMint_token( int64_t amount ); +#endif // APP_FEATURE_TOKEN_MINTING + void txHashBuilder_addScriptDataHash( tx_hash_builder_t* builder, const uint8_t* scriptHashData, size_t scriptHashDataSize @@ -406,7 +418,7 @@ void txHashBuilder_finalize( ); -#ifdef DEVEL +#if defined(DEVEL) && !defined(APP_XS) void run_txHashBuilder_test(); #endif // DEVEL diff --git a/src/txHashBuilder_test.c b/src/txHashBuilder_test.c index 3dad47a3..52b6d4a4 100644 --- a/src/txHashBuilder_test.c +++ b/src/txHashBuilder_test.c @@ -1,4 +1,4 @@ -#ifdef DEVEL +#if defined(DEVEL) && !defined(APP_XS) #include "txHashBuilder.h" #include "cardano.h" diff --git a/src/uiScreens_bagl.c b/src/uiScreens_bagl.c index 1e158a0c..cea937ae 100644 --- a/src/uiScreens_bagl.c +++ b/src/uiScreens_bagl.c @@ -653,11 +653,23 @@ void ui_displayNetworkParamsScreen( STATIC_ASSERT(!IS_SIGNED(networkId), "signed type for %u"); STATIC_ASSERT(sizeof(protocolMagic) <= sizeof(unsigned), "oversized type for %u"); STATIC_ASSERT(!IS_SIGNED(protocolMagic), "signed type for %u"); + + #ifdef APP_FEATURE_BYRON_PROTOCOL_MAGIC_CHECK snprintf( networkParams, SIZEOF(networkParams), "network id %u / protocol magic %u", networkId, protocolMagic ); + #else + // if the protocol magic check is not enabled, + // displaying the protocol magic might be misleading, + // so we must not show it + snprintf( + networkParams, SIZEOF(networkParams), + "network id %u", + networkId + ); + #endif // APP_FEATURE_BYRON_PROTOCOL_MAGIC_CHECK ASSERT(strlen(networkParams) + 1 < SIZEOF(networkParams)); ui_displayPaginatedText( @@ -702,6 +714,8 @@ void ui_displayPoolMarginScreen( ); } +#ifdef APP_FEATURE_POOL_REGISTRATION + void ui_displayPoolOwnerScreen( const pool_owner_t* owner, uint32_t ownerIndex, @@ -859,6 +873,8 @@ void ui_displayIpPortScreen( ); } +#endif // APP_FEATURE_POOL_REGISTRATION + void ui_displayInputScreen( const sign_tx_transaction_input_t* input, ui_callback_fn_t callback) diff --git a/src/uiScreens_bagl.h b/src/uiScreens_bagl.h index f4073130..abb162bb 100644 --- a/src/uiScreens_bagl.h +++ b/src/uiScreens_bagl.h @@ -134,6 +134,8 @@ void ui_displayPoolMarginScreen( ui_callback_fn_t callback ); +#ifdef APP_FEATURE_POOL_REGISTRATION + __noinline_due_to_stack__ void ui_displayPoolOwnerScreen( const pool_owner_t* owner, @@ -167,6 +169,8 @@ void ui_displayIpPortScreen( ui_callback_fn_t callback ); +#endif // APP_FEATURE_POOL_REGISTRATION + __noinline_due_to_stack__ void ui_displayInputScreen( const sign_tx_transaction_input_t* input, diff --git a/src/uiScreens_nbgl.c b/src/uiScreens_nbgl.c index db1b7bf9..1dfca96a 100644 --- a/src/uiScreens_nbgl.c +++ b/src/uiScreens_nbgl.c @@ -620,6 +620,8 @@ void ui_getPoolMarginScreen( TRACE("%s", line1); } +#ifdef APP_FEATURE_POOL_REGISTRATION + void ui_getPoolOwnerScreen( char* firstLine, const size_t firstLineSize, @@ -748,6 +750,8 @@ void ui_getIpPortScreen( ASSERT(strlen(portStr) + 1 < portStrSize); } +#endif // APP_FEATURE_POOL_REGISTRATION + void ui_getInputScreen( char* line, const size_t lineSize, diff --git a/src/uiScreens_nbgl.h b/src/uiScreens_nbgl.h index b7424145..e5120cfa 100644 --- a/src/uiScreens_nbgl.h +++ b/src/uiScreens_nbgl.h @@ -165,6 +165,8 @@ void ui_getPoolMarginScreen( uint64_t marginNumerator, uint64_t marginDenominator ); +#ifdef APP_FEATURE_POOL_REGISTRATION + __noinline_due_to_stack__ void ui_getPoolOwnerScreen( char* firstLine, @@ -200,6 +202,8 @@ void ui_getIpPortScreen( const ipport_t* port ); +#endif // APP_FEATURE_POOL_REGISTRATION + __noinline_due_to_stack__ void ui_getInputScreen( char* line, From 58a34d5ed3f4ae2f4fbf6715fef81cdc8b80e99e Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Tue, 12 Dec 2023 12:19:32 +0100 Subject: [PATCH 03/43] add ledger_app.toml --- ledger_app.toml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 ledger_app.toml diff --git a/ledger_app.toml b/ledger_app.toml new file mode 100644 index 00000000..8832868a --- /dev/null +++ b/ledger_app.toml @@ -0,0 +1,5 @@ +[app] +build_directory = "./" +sdk = "C" +devices = ["nanos", "nanox", "nanos+", "stax"] + From 98cf06e9504dd0ec014eee54cee0decbadbab7ef Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Fri, 6 Oct 2023 17:51:04 +0200 Subject: [PATCH 04/43] refactor: rename credentials --- src/cardano.h | 8 ++++---- src/securityPolicy.c | 16 ++++++++-------- src/securityPolicy.h | 4 ++-- src/signTx.c | 22 +++++++++++----------- src/signTx.h | 8 ++++---- src/signTx_ui.c | 12 ++++++------ src/txHashBuilder.c | 18 +++++++++--------- src/txHashBuilder.h | 4 ++-- src/txHashBuilder_test.c | 6 +++--- 9 files changed, 49 insertions(+), 49 deletions(-) diff --git a/src/cardano.h b/src/cardano.h index 03f13e01..2915c2ba 100644 --- a/src/cardano.h +++ b/src/cardano.h @@ -120,10 +120,10 @@ typedef enum { typedef enum { // enum values are affected by backwards-compatibility - STAKE_CREDENTIAL_KEY_PATH = 0, - STAKE_CREDENTIAL_KEY_HASH = 2, - STAKE_CREDENTIAL_SCRIPT_HASH = 1, -} stake_credential_type_t; + CREDENTIAL_KEY_PATH = 0, + CREDENTIAL_KEY_HASH = 2, + CREDENTIAL_SCRIPT_HASH = 1, +} credential_type_t; typedef enum { RELAY_SINGLE_HOST_IP = 0, diff --git a/src/securityPolicy.c b/src/securityPolicy.c index a5ffd4b2..984cf60f 100644 --- a/src/securityPolicy.c +++ b/src/securityPolicy.c @@ -1012,7 +1012,7 @@ security_policy_t policyForSignTxCertificate( security_policy_t policyForSignTxCertificateStaking( sign_tx_signingmode_t txSigningMode, const certificate_type_t certificateType, - const stake_credential_t* stakeCredential + const credential_t* stakeCredential ) { switch (certificateType) { @@ -1026,7 +1026,7 @@ security_policy_t policyForSignTxCertificateStaking( } switch (stakeCredential->type) { - case STAKE_CREDENTIAL_KEY_PATH: + case CREDENTIAL_KEY_PATH: DENY_UNLESS(bip44_isOrdinaryStakingKeyPath(&stakeCredential->keyPath)); DENY_IF(violatesSingleAccountOrStoreIt(&stakeCredential->keyPath)); switch (txSigningMode) { @@ -1047,7 +1047,7 @@ security_policy_t policyForSignTxCertificateStaking( } break; - case STAKE_CREDENTIAL_KEY_HASH: + case CREDENTIAL_KEY_HASH: switch (txSigningMode) { case SIGN_TX_SIGNINGMODE_PLUTUS_TX: PROMPT(); @@ -1066,7 +1066,7 @@ security_policy_t policyForSignTxCertificateStaking( } break; - case STAKE_CREDENTIAL_SCRIPT_HASH: + case CREDENTIAL_SCRIPT_HASH: switch (txSigningMode) { case SIGN_TX_SIGNINGMODE_MULTISIG_TX: case SIGN_TX_SIGNINGMODE_PLUTUS_TX: @@ -1298,11 +1298,11 @@ security_policy_t policyForSignTxStakePoolRegistrationConfirm( // For each withdrawal security_policy_t policyForSignTxWithdrawal( sign_tx_signingmode_t txSigningMode, - const stake_credential_t* stakeCredential + const credential_t* stakeCredential ) { switch (stakeCredential->type) { - case STAKE_CREDENTIAL_KEY_PATH: + case CREDENTIAL_KEY_PATH: DENY_UNLESS(bip44_isOrdinaryStakingKeyPath(&stakeCredential->keyPath)); DENY_IF(violatesSingleAccountOrStoreIt(&stakeCredential->keyPath)); switch (txSigningMode) { @@ -1325,7 +1325,7 @@ security_policy_t policyForSignTxWithdrawal( } break; - case STAKE_CREDENTIAL_KEY_HASH: + case CREDENTIAL_KEY_HASH: switch (txSigningMode) { case SIGN_TX_SIGNINGMODE_PLUTUS_TX: SHOW_IF(app_mode_expert()); @@ -1353,7 +1353,7 @@ security_policy_t policyForSignTxWithdrawal( } break; - case STAKE_CREDENTIAL_SCRIPT_HASH: + case CREDENTIAL_SCRIPT_HASH: switch (txSigningMode) { case SIGN_TX_SIGNINGMODE_MULTISIG_TX: case SIGN_TX_SIGNINGMODE_PLUTUS_TX: diff --git a/src/securityPolicy.h b/src/securityPolicy.h index 299c4298..e9784c7c 100644 --- a/src/securityPolicy.h +++ b/src/securityPolicy.h @@ -111,7 +111,7 @@ security_policy_t policyForSignTxCertificate( security_policy_t policyForSignTxCertificateStaking( sign_tx_signingmode_t txSigningMode, const certificate_type_t certificateType, - const stake_credential_t* stakeCredential + const credential_t* stakeCredential ); #ifdef APP_FEATURE_POOL_RETIREMENT security_policy_t policyForSignTxCertificateStakePoolRetirement( @@ -153,7 +153,7 @@ security_policy_t policyForSignTxStakePoolRegistrationConfirm( #endif // APP_FEATURE_POOL_REGISTRATION security_policy_t policyForSignTxWithdrawal( sign_tx_signingmode_t txSigningMode, - const stake_credential_t* stakeCredential + const credential_t* stakeCredential ); security_policy_t policyForSignTxAuxData(aux_data_type_t auxDataType); diff --git a/src/signTx.c b/src/signTx.c index f8efc472..e03e759c 100644 --- a/src/signTx.c +++ b/src/signTx.c @@ -907,19 +907,19 @@ static void _parsePathSpec(read_view_t* view, bip44_path_t* pathSpec) PRINTF("\n"); } -static void _parseStakeCredential(read_view_t* view, stake_credential_t* stakeCredential) +static void _parseStakeCredential(read_view_t* view, credential_t* stakeCredential) { stakeCredential->type = parse_u1be(view); switch (stakeCredential->type) { - case STAKE_CREDENTIAL_KEY_PATH: + case CREDENTIAL_KEY_PATH: _parsePathSpec(view, &stakeCredential->keyPath); break; - case STAKE_CREDENTIAL_KEY_HASH: { + case CREDENTIAL_KEY_HASH: { STATIC_ASSERT(SIZEOF(stakeCredential->keyHash) == ADDRESS_KEY_HASH_LENGTH, "bad key hash container size"); view_parseBuffer(stakeCredential->keyHash, view, SIZEOF(stakeCredential->keyHash)); break; } - case STAKE_CREDENTIAL_SCRIPT_HASH: { + case CREDENTIAL_SCRIPT_HASH: { STATIC_ASSERT(SIZEOF(stakeCredential->scriptHash) == SCRIPT_HASH_LENGTH, "bad script hash container size"); view_parseBuffer(stakeCredential->scriptHash, view, SIZEOF(stakeCredential->scriptHash)); break; @@ -991,21 +991,21 @@ static void _fillHashFromPath(const bip44_path_t* path, ); } -static void _fillHashFromStakeCredential(const stake_credential_t* stakeCredential, +static void _fillHashFromStakeCredential(const credential_t* stakeCredential, uint8_t* hash, size_t hashSize) { ASSERT(hashSize < BUFFER_SIZE_PARANOIA); switch (stakeCredential->type) { - case STAKE_CREDENTIAL_KEY_PATH: + case CREDENTIAL_KEY_PATH: _fillHashFromPath(&stakeCredential->keyPath, hash, hashSize); break; - case STAKE_CREDENTIAL_KEY_HASH: + case CREDENTIAL_KEY_HASH: ASSERT(ADDRESS_KEY_HASH_LENGTH <= hashSize); STATIC_ASSERT(SIZEOF(stakeCredential->keyHash) == ADDRESS_KEY_HASH_LENGTH, "bad key hash container size"); memmove(hash, stakeCredential->keyHash, SIZEOF(stakeCredential->keyHash)); break; - case STAKE_CREDENTIAL_SCRIPT_HASH: + case CREDENTIAL_SCRIPT_HASH: ASSERT(SCRIPT_HASH_LENGTH <= hashSize); STATIC_ASSERT(SIZEOF(stakeCredential->scriptHash) == SCRIPT_HASH_LENGTH, "bad script hash container size"); memmove(hash, stakeCredential->scriptHash, SIZEOF(stakeCredential->scriptHash)); @@ -1194,7 +1194,7 @@ static void _addWithdrawalToTxHash(bool validateCanonicalOrdering) uint8_t rewardAddress[REWARD_ACCOUNT_SIZE] = {0}; switch (BODY_CTX->stageData.withdrawal.stakeCredential.type) { - case STAKE_CREDENTIAL_KEY_PATH: + case CREDENTIAL_KEY_PATH: constructRewardAddressFromKeyPath( &BODY_CTX->stageData.withdrawal.stakeCredential.keyPath, ctx->commonTxData.networkId, @@ -1202,7 +1202,7 @@ static void _addWithdrawalToTxHash(bool validateCanonicalOrdering) SIZEOF(rewardAddress) ); break; - case STAKE_CREDENTIAL_KEY_HASH: + case CREDENTIAL_KEY_HASH: constructRewardAddressFromHash( ctx->commonTxData.networkId, REWARD_HASH_SOURCE_KEY, @@ -1212,7 +1212,7 @@ static void _addWithdrawalToTxHash(bool validateCanonicalOrdering) SIZEOF(rewardAddress) ); break; - case STAKE_CREDENTIAL_SCRIPT_HASH: + case CREDENTIAL_SCRIPT_HASH: constructRewardAddressFromHash( ctx->commonTxData.networkId, REWARD_HASH_SOURCE_SCRIPT, diff --git a/src/signTx.h b/src/signTx.h index aa3d8555..9a7e55ec 100644 --- a/src/signTx.h +++ b/src/signTx.h @@ -83,20 +83,20 @@ typedef struct { } common_tx_data_t; typedef struct { - stake_credential_type_t type; + credential_type_t type; union { bip44_path_t keyPath; uint8_t keyHash[ADDRESS_KEY_HASH_LENGTH]; uint8_t scriptHash[SCRIPT_HASH_LENGTH]; }; -} stake_credential_t; +} credential_t; typedef struct { certificate_type_t type; union { - stake_credential_t stakeCredential; bip44_path_t poolIdPath; + credential_t stakeCredential; }; uint64_t epoch; uint8_t poolKeyHash[POOL_KEY_HASH_LENGTH]; @@ -114,7 +114,7 @@ typedef struct { } sign_tx_witness_data_t; typedef struct { - stake_credential_t stakeCredential; + credential_t stakeCredential; uint64_t amount; uint8_t previousRewardAccount[REWARD_ACCOUNT_SIZE]; } sign_tx_withdrawal_data_t; diff --git a/src/signTx_ui.c b/src/signTx_ui.c index 91c00b91..163ded14 100644 --- a/src/signTx_ui.c +++ b/src/signTx_ui.c @@ -417,7 +417,7 @@ void signTx_handleCertificate_ui_runStep() } UI_STEP(HANDLE_CERTIFICATE_STEP_DISPLAY_STAKING_KEY) { switch (BODY_CTX->stageData.certificate.stakeCredential.type) { - case STAKE_CREDENTIAL_KEY_PATH: + case CREDENTIAL_KEY_PATH: #ifdef HAVE_BAGL ui_displayPathScreen( "Stake key", @@ -432,7 +432,7 @@ void signTx_handleCertificate_ui_runStep() } #endif // HAVE_BAGL break; - case STAKE_CREDENTIAL_KEY_HASH: + case CREDENTIAL_KEY_HASH: #ifdef HAVE_BAGL ui_displayBech32Screen( "Stake key hash", @@ -449,7 +449,7 @@ void signTx_handleCertificate_ui_runStep() } #endif // HAVE_BAGL break; - case STAKE_CREDENTIAL_SCRIPT_HASH: + case CREDENTIAL_SCRIPT_HASH: #ifdef HAVE_BAGL ui_displayBech32Screen( "Stake script hash", @@ -611,12 +611,12 @@ void signTx_handleWithdrawal_ui_runStep() UI_STEP(HANDLE_WITHDRAWAL_STEP_DISPLAY_PATH) { reward_account_t rewardAccount; switch (BODY_CTX->stageData.withdrawal.stakeCredential.type) { - case STAKE_CREDENTIAL_KEY_PATH: { + case CREDENTIAL_KEY_PATH: { rewardAccount.keyReferenceType = KEY_REFERENCE_PATH; rewardAccount.path = BODY_CTX->stageData.withdrawal.stakeCredential.keyPath; break; } - case STAKE_CREDENTIAL_KEY_HASH: { + case CREDENTIAL_KEY_HASH: { rewardAccount.keyReferenceType = KEY_REFERENCE_HASH; constructRewardAddressFromHash( ctx->commonTxData.networkId, @@ -628,7 +628,7 @@ void signTx_handleWithdrawal_ui_runStep() ); break; } - case STAKE_CREDENTIAL_SCRIPT_HASH: { + case CREDENTIAL_SCRIPT_HASH: { rewardAccount.keyReferenceType = KEY_REFERENCE_HASH; constructRewardAddressFromHash( ctx->commonTxData.networkId, diff --git a/src/txHashBuilder.c b/src/txHashBuilder.c index 43efdff3..3d20af3f 100644 --- a/src/txHashBuilder.c +++ b/src/txHashBuilder.c @@ -742,17 +742,17 @@ void txHashBuilder_enterCertificates(tx_hash_builder_t* builder) builder->state = TX_HASH_BUILDER_IN_CERTIFICATES; } -static uint32_t getStakeCredentialSource(const stake_credential_type_t stakeCredentialType) +static uint32_t getStakeCredentialSource(const credential_type_t credentialType) { enum { KEY = 0, SCRIPT = 1 }; - switch (stakeCredentialType) { - case STAKE_CREDENTIAL_KEY_PATH: - case STAKE_CREDENTIAL_KEY_HASH: + switch (credentialType) { + case CREDENTIAL_KEY_PATH: + case CREDENTIAL_KEY_HASH: return KEY; - case STAKE_CREDENTIAL_SCRIPT_HASH: + case CREDENTIAL_SCRIPT_HASH: return SCRIPT; default: ASSERT(false); @@ -764,7 +764,7 @@ static uint32_t getStakeCredentialSource(const stake_credential_type_t stakeCred void txHashBuilder_addCertificate_stakingHash( tx_hash_builder_t* builder, const certificate_type_t certificateType, - const stake_credential_type_t stakeCredentialType, + const credential_type_t credentialType, const uint8_t* stakingHash, size_t stakingHashSize ) { @@ -794,7 +794,7 @@ void txHashBuilder_addCertificate_stakingHash( { BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 2); { - BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, getStakeCredentialSource(stakeCredentialType)); + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, getStakeCredentialSource(credentialType)); } { BUILDER_APPEND_CBOR(CBOR_TYPE_BYTES, stakingHashSize); @@ -806,7 +806,7 @@ void txHashBuilder_addCertificate_stakingHash( void txHashBuilder_addCertificate_delegation( tx_hash_builder_t* builder, - const stake_credential_type_t stakeCredentialType, + const credential_type_t credentialType, const uint8_t* stakingKeyHash, size_t stakingKeyHashSize, const uint8_t* poolKeyHash, size_t poolKeyHashSize ) @@ -836,7 +836,7 @@ void txHashBuilder_addCertificate_delegation( { BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 2); { - BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, getStakeCredentialSource(stakeCredentialType)); + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, getStakeCredentialSource(credentialType)); } { BUILDER_APPEND_CBOR(CBOR_TYPE_BYTES, stakingKeyHashSize); diff --git a/src/txHashBuilder.h b/src/txHashBuilder.h index 34e0b675..64c063c8 100644 --- a/src/txHashBuilder.h +++ b/src/txHashBuilder.h @@ -253,13 +253,13 @@ void txHashBuilder_enterCertificates(tx_hash_builder_t* builder); void txHashBuilder_addCertificate_stakingHash( tx_hash_builder_t* builder, const certificate_type_t certificateType, - const stake_credential_type_t stakeCredentialType, + const credential_type_t credentialType, const uint8_t* stakingHash, size_t stakingHashSize ); void txHashBuilder_addCertificate_delegation( tx_hash_builder_t* builder, - const stake_credential_type_t stakeCredentialType, + const credential_type_t credentialType, const uint8_t* stakingKeyHash, size_t stakingKeyHashSize, const uint8_t* poolKeyHash, size_t poolKeyHashSize ); diff --git a/src/txHashBuilder_test.c b/src/txHashBuilder_test.c index 52b6d4a4..12dcfeee 100644 --- a/src/txHashBuilder_test.c +++ b/src/txHashBuilder_test.c @@ -402,7 +402,7 @@ static void addCertificates(tx_hash_builder_t* builder) txHashBuilder_addCertificate_stakingHash( builder, CERTIFICATE_TYPE_STAKE_REGISTRATION, - STAKE_CREDENTIAL_KEY_PATH, + CREDENTIAL_KEY_PATH, tmp, tmpSize ); } @@ -413,7 +413,7 @@ static void addCertificates(tx_hash_builder_t* builder) txHashBuilder_addCertificate_stakingHash( builder, CERTIFICATE_TYPE_STAKE_DEREGISTRATION, - STAKE_CREDENTIAL_KEY_PATH, + CREDENTIAL_KEY_PATH, tmp, tmpSize ); } @@ -431,7 +431,7 @@ static void addCertificates(tx_hash_builder_t* builder) uint8_t tmp_pool[70] = {0}; size_t tmpSize_pool = decode_hex(PTR_PIC(it->poolKeyHash), tmp_pool, SIZEOF(tmp_pool)); txHashBuilder_addCertificate_delegation( - builder, STAKE_CREDENTIAL_KEY_PATH, + builder, CREDENTIAL_KEY_PATH, tmp_credential, tmpSize_credential, tmp_pool, tmpSize_pool ); From e4aaf1a648f0cd3e80fe1baaccb24991a109961f Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Sat, 7 Oct 2023 10:30:42 +0200 Subject: [PATCH 05/43] refactor: rename a local variable --- src/signTx.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/signTx.c b/src/signTx.c index e03e759c..cbd02e45 100644 --- a/src/signTx.c +++ b/src/signTx.c @@ -1029,25 +1029,25 @@ static void _addCertificateDataToTx( TRACE("Adding certificate (type %d) to tx hash", certificateData->type); STATIC_ASSERT(ADDRESS_KEY_HASH_LENGTH == SCRIPT_HASH_LENGTH, "incompatible hash sizes"); - uint8_t stakingHash[ADDRESS_KEY_HASH_LENGTH] = {0}; + uint8_t hash[ADDRESS_KEY_HASH_LENGTH] = {0}; switch (BODY_CTX->stageData.certificate.type) { case CERTIFICATE_TYPE_STAKE_REGISTRATION: case CERTIFICATE_TYPE_STAKE_DEREGISTRATION: { - _fillHashFromStakeCredential(&BODY_CTX->stageData.certificate.stakeCredential, stakingHash, SIZEOF(stakingHash)); + _fillHashFromStakeCredential(&BODY_CTX->stageData.certificate.stakeCredential, hash, SIZEOF(hash)); txHashBuilder_addCertificate_stakingHash( txHashBuilder, certificateData->type, certificateData->stakeCredential.type, - stakingHash, SIZEOF(stakingHash) + hash, SIZEOF(hash) ); break; } case CERTIFICATE_TYPE_STAKE_DELEGATION: { - _fillHashFromStakeCredential(&BODY_CTX->stageData.certificate.stakeCredential, stakingHash, SIZEOF(stakingHash)); + _fillHashFromStakeCredential(&BODY_CTX->stageData.certificate.stakeCredential, hash, SIZEOF(hash)); txHashBuilder_addCertificate_delegation( txHashBuilder, certificateData->stakeCredential.type, - stakingHash, SIZEOF(stakingHash), + hash, SIZEOF(hash), certificateData->poolKeyHash, SIZEOF(certificateData->poolKeyHash) ); break; @@ -1056,10 +1056,10 @@ static void _addCertificateDataToTx( #ifdef APP_FEATURE_POOL_RETIREMENT case CERTIFICATE_TYPE_STAKE_POOL_RETIREMENT: { - _fillHashFromPath(&BODY_CTX->stageData.certificate.poolIdPath, certificateData->poolKeyHash, SIZEOF(certificateData->poolKeyHash)); + _fillHashFromPath(&BODY_CTX->stageData.certificate.poolIdPath, hash, SIZEOF(hash)); txHashBuilder_addCertificate_poolRetirement( txHashBuilder, - certificateData->poolKeyHash, SIZEOF(certificateData->poolKeyHash), + hash, SIZEOF(hash), certificateData->epoch ); break; From 3b863de01ccfdee251c45f0b0b91bae00f1ad5c7 Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Sat, 7 Oct 2023 10:50:51 +0200 Subject: [PATCH 06/43] refactor: remove poolIdPath from ctx --- src/securityPolicy.c | 5 +++-- src/securityPolicy.h | 2 +- src/signTx.c | 32 +++++++++++++++++++------------- src/signTx.h | 7 ++++++- 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/securityPolicy.c b/src/securityPolicy.c index 984cf60f..abba9725 100644 --- a/src/securityPolicy.c +++ b/src/securityPolicy.c @@ -1097,7 +1097,7 @@ security_policy_t policyForSignTxCertificateStaking( security_policy_t policyForSignTxCertificateStakePoolRetirement( sign_tx_signingmode_t txSigningMode, - const bip44_path_t* poolIdPath, + const credential_t* poolCredential, uint64_t epoch MARK_UNUSED ) { @@ -1106,7 +1106,8 @@ security_policy_t policyForSignTxCertificateStakePoolRetirement( case SIGN_TX_SIGNINGMODE_ORDINARY_TX: // pool retirement may only be present in ORDINARY_TX signing mode // the path hash should be a valid pool cold key path - DENY_UNLESS(bip44_isPoolColdKeyPath(poolIdPath)); + DENY_UNLESS(poolCredential->type == CREDENTIAL_KEY_PATH); + DENY_UNLESS(bip44_isPoolColdKeyPath(&poolCredential->keyPath)); PROMPT(); break; diff --git a/src/securityPolicy.h b/src/securityPolicy.h index e9784c7c..ec6f4b59 100644 --- a/src/securityPolicy.h +++ b/src/securityPolicy.h @@ -116,7 +116,7 @@ security_policy_t policyForSignTxCertificateStaking( #ifdef APP_FEATURE_POOL_RETIREMENT security_policy_t policyForSignTxCertificateStakePoolRetirement( sign_tx_signingmode_t txSigningMode, - const bip44_path_t* stakeCredential, + const credential_t* poolCredential, uint64_t epoch ); #endif // APP_FEATURE_POOL_RETIREMENT diff --git a/src/signTx.c b/src/signTx.c index cbd02e45..0d4bfe6c 100644 --- a/src/signTx.c +++ b/src/signTx.c @@ -966,7 +966,9 @@ static void _parseCertificateData(const uint8_t* wireDataBuffer, size_t wireData #ifdef APP_FEATURE_POOL_RETIREMENT case CERTIFICATE_TYPE_STAKE_POOL_RETIREMENT: - _parsePathSpec(&view, &certificateData->poolIdPath); + // TODO refactor APDU serialization to parse credential + certificateData->poolCredential.type = CREDENTIAL_KEY_PATH; + _parsePathSpec(&view, &certificateData->poolCredential.keyPath); certificateData->epoch = parse_u8be(&view); break; @@ -991,24 +993,26 @@ static void _fillHashFromPath(const bip44_path_t* path, ); } -static void _fillHashFromStakeCredential(const credential_t* stakeCredential, - uint8_t* hash, size_t hashSize) +static void _fillHashFromCredential( + const credential_t* credential, + uint8_t* hash, size_t hashSize +) { ASSERT(hashSize < BUFFER_SIZE_PARANOIA); - switch (stakeCredential->type) { + switch (credential->type) { case CREDENTIAL_KEY_PATH: - _fillHashFromPath(&stakeCredential->keyPath, hash, hashSize); + _fillHashFromPath(&credential->keyPath, hash, hashSize); break; case CREDENTIAL_KEY_HASH: ASSERT(ADDRESS_KEY_HASH_LENGTH <= hashSize); - STATIC_ASSERT(SIZEOF(stakeCredential->keyHash) == ADDRESS_KEY_HASH_LENGTH, "bad key hash container size"); - memmove(hash, stakeCredential->keyHash, SIZEOF(stakeCredential->keyHash)); + STATIC_ASSERT(SIZEOF(credential->keyHash) == ADDRESS_KEY_HASH_LENGTH, "bad key hash container size"); + memmove(hash, credential->keyHash, SIZEOF(credential->keyHash)); break; case CREDENTIAL_SCRIPT_HASH: ASSERT(SCRIPT_HASH_LENGTH <= hashSize); - STATIC_ASSERT(SIZEOF(stakeCredential->scriptHash) == SCRIPT_HASH_LENGTH, "bad script hash container size"); - memmove(hash, stakeCredential->scriptHash, SIZEOF(stakeCredential->scriptHash)); + STATIC_ASSERT(SIZEOF(credential->scriptHash) == SCRIPT_HASH_LENGTH, "bad script hash container size"); + memmove(hash, credential->scriptHash, SIZEOF(credential->scriptHash)); break; default: ASSERT(false); @@ -1035,7 +1039,7 @@ static void _addCertificateDataToTx( case CERTIFICATE_TYPE_STAKE_REGISTRATION: case CERTIFICATE_TYPE_STAKE_DEREGISTRATION: { - _fillHashFromStakeCredential(&BODY_CTX->stageData.certificate.stakeCredential, hash, SIZEOF(hash)); + _fillHashFromCredential(&BODY_CTX->stageData.certificate.stakeCredential, hash, SIZEOF(hash)); txHashBuilder_addCertificate_stakingHash( txHashBuilder, certificateData->type, certificateData->stakeCredential.type, hash, SIZEOF(hash) @@ -1044,7 +1048,7 @@ static void _addCertificateDataToTx( } case CERTIFICATE_TYPE_STAKE_DELEGATION: { - _fillHashFromStakeCredential(&BODY_CTX->stageData.certificate.stakeCredential, hash, SIZEOF(hash)); + _fillHashFromCredential(&BODY_CTX->stageData.certificate.stakeCredential, hash, SIZEOF(hash)); txHashBuilder_addCertificate_delegation( txHashBuilder, certificateData->stakeCredential.type, hash, SIZEOF(hash), @@ -1056,7 +1060,8 @@ static void _addCertificateDataToTx( #ifdef APP_FEATURE_POOL_RETIREMENT case CERTIFICATE_TYPE_STAKE_POOL_RETIREMENT: { - _fillHashFromPath(&BODY_CTX->stageData.certificate.poolIdPath, hash, SIZEOF(hash)); + // TODO rename to _fillHashFromCredential + _fillHashFromCredential(&BODY_CTX->stageData.certificate.poolCredential, hash, SIZEOF(hash)); txHashBuilder_addCertificate_poolRetirement( txHashBuilder, hash, SIZEOF(hash), @@ -1157,9 +1162,10 @@ static void signTx_handleCertificateAPDU(uint8_t p2, const uint8_t* wireDataBuff #ifdef APP_FEATURE_POOL_RETIREMENT case CERTIFICATE_TYPE_STAKE_POOL_RETIREMENT: { + // TODO refactor to use credential instead of path directly security_policy_t policy = policyForSignTxCertificateStakePoolRetirement( ctx->commonTxData.txSigningMode, - &BODY_CTX->stageData.certificate.poolIdPath, + &BODY_CTX->stageData.certificate.poolCredential, BODY_CTX->stageData.certificate.epoch ); TRACE("Policy: %d", (int) policy); diff --git a/src/signTx.h b/src/signTx.h index 9a7e55ec..8055ca8b 100644 --- a/src/signTx.h +++ b/src/signTx.h @@ -95,8 +95,13 @@ typedef struct { certificate_type_t type; union { - bip44_path_t poolIdPath; credential_t stakeCredential; + // TODO credential_t committeeColdCredential; + }; + union { + credential_t poolCredential; + // TODO credential_t drepCredential; + // TODO credential_t committeeHotCredential; }; uint64_t epoch; uint8_t poolKeyHash[POOL_KEY_HASH_LENGTH]; From 93e9e61ff3671f1c396dc6bae797b3245945aa0a Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Sat, 7 Oct 2023 11:22:46 +0200 Subject: [PATCH 07/43] refactor: remove poolKeyHash from ctx --- src/signTx.c | 8 +++++--- src/signTx.h | 2 -- src/signTx_ui.c | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/signTx.c b/src/signTx.c index 0d4bfe6c..6e5f5d6d 100644 --- a/src/signTx.c +++ b/src/signTx.c @@ -950,8 +950,9 @@ static void _parseCertificateData(const uint8_t* wireDataBuffer, size_t wireData case CERTIFICATE_TYPE_STAKE_DELEGATION: _parseStakeCredential(&view, &certificateData->stakeCredential); - STATIC_ASSERT(SIZEOF(certificateData->poolKeyHash) == POOL_KEY_HASH_LENGTH, "wrong poolKeyHash size"); - view_parseBuffer(certificateData->poolKeyHash, &view, POOL_KEY_HASH_LENGTH); + // TODO change APDU to parse credential + STATIC_ASSERT(SIZEOF(certificateData->poolCredential.keyHash) == POOL_KEY_HASH_LENGTH, "wrong poolKeyHash size"); + view_parseBuffer(certificateData->poolCredential.keyHash, &view, POOL_KEY_HASH_LENGTH); break; #ifdef APP_FEATURE_POOL_REGISTRATION @@ -1052,7 +1053,8 @@ static void _addCertificateDataToTx( txHashBuilder_addCertificate_delegation( txHashBuilder, certificateData->stakeCredential.type, hash, SIZEOF(hash), - certificateData->poolKeyHash, SIZEOF(certificateData->poolKeyHash) + // TODO make sure credential is key hash + certificateData->poolCredential.keyHash, SIZEOF(certificateData->poolCredential.keyHash) ); break; } diff --git a/src/signTx.h b/src/signTx.h index 8055ca8b..20fa9d57 100644 --- a/src/signTx.h +++ b/src/signTx.h @@ -104,8 +104,6 @@ typedef struct { // TODO credential_t committeeHotCredential; }; uint64_t epoch; - uint8_t poolKeyHash[POOL_KEY_HASH_LENGTH]; - } sign_tx_certificate_data_t; typedef struct { diff --git a/src/signTx_ui.c b/src/signTx_ui.c index 163ded14..2f4d2b5a 100644 --- a/src/signTx_ui.c +++ b/src/signTx_ui.c @@ -354,7 +354,7 @@ void signTx_handleTtl_ui_runStep() static void signTx_handleCertificate_ui_delegation_cb(void) { char encodedStr[BECH32_STRING_SIZE_MAX] = {0}; - ui_getBech32Screen(encodedStr, SIZEOF(encodedStr), "pool", BODY_CTX->stageData.certificate.poolKeyHash, SIZEOF(BODY_CTX->stageData.certificate.poolKeyHash)); + ui_getBech32Screen(encodedStr, SIZEOF(encodedStr), "pool", BODY_CTX->stageData.certificate.poolCredential.keyHash, SIZEOF(BODY_CTX->stageData.certificate.poolCredential.keyHash)); fill_and_display_if_required("Pool", encodedStr, signTx_handleCertificate_ui_runStep, respond_with_user_reject); } #endif @@ -399,7 +399,7 @@ void signTx_handleCertificate_ui_runStep() ui_displayBech32Screen( "Delegate stake", "to pool", - BODY_CTX->stageData.certificate.poolKeyHash, SIZEOF(BODY_CTX->stageData.certificate.poolKeyHash), + BODY_CTX->stageData.certificate.poolCredential.keyHash, SIZEOF(BODY_CTX->stageData.certificate.poolCredential.keyHash), this_fn ); #elif defined(HAVE_NBGL) @@ -540,13 +540,13 @@ void signTx_handleCertificatePoolRetirement_ui_runStep() ui_displayBech32Screen( "Retire stake pool", "pool", - BODY_CTX->stageData.certificate.poolKeyHash, SIZEOF(BODY_CTX->stageData.certificate.poolKeyHash), + BODY_CTX->stageData.certificate.poolCredential.keyHash, SIZEOF(BODY_CTX->stageData.certificate.poolCredential.keyHash), this_fn ); #elif defined(HAVE_NBGL) set_light_confirmation(true); char encodedStr[BECH32_STRING_SIZE_MAX] = {0}; - ui_getBech32Screen(encodedStr, SIZEOF(encodedStr), "pool", BODY_CTX->stageData.certificate.poolKeyHash, SIZEOF(BODY_CTX->stageData.certificate.poolKeyHash)); + ui_getBech32Screen(encodedStr, SIZEOF(encodedStr), "pool", BODY_CTX->stageData.certificate.poolCredential.keyHash, SIZEOF(BODY_CTX->stageData.certificate.poolCredential.keyHash)); fill_and_display_if_required("Retire stake pool", encodedStr, this_fn, respond_with_user_reject); #endif // HAVE_BAGL } From 06013221a556d3bbb2d19133ce89751508ae1e17 Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Sat, 7 Oct 2023 11:25:05 +0200 Subject: [PATCH 08/43] refactor: rename local function --- src/signTx.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/signTx.c b/src/signTx.c index 6e5f5d6d..121bf4d8 100644 --- a/src/signTx.c +++ b/src/signTx.c @@ -907,21 +907,21 @@ static void _parsePathSpec(read_view_t* view, bip44_path_t* pathSpec) PRINTF("\n"); } -static void _parseStakeCredential(read_view_t* view, credential_t* stakeCredential) +static void _parseCredential(read_view_t* view, credential_t* credential) { - stakeCredential->type = parse_u1be(view); - switch (stakeCredential->type) { + credential->type = parse_u1be(view); + switch (credential->type) { case CREDENTIAL_KEY_PATH: - _parsePathSpec(view, &stakeCredential->keyPath); + _parsePathSpec(view, &credential->keyPath); break; case CREDENTIAL_KEY_HASH: { - STATIC_ASSERT(SIZEOF(stakeCredential->keyHash) == ADDRESS_KEY_HASH_LENGTH, "bad key hash container size"); - view_parseBuffer(stakeCredential->keyHash, view, SIZEOF(stakeCredential->keyHash)); + STATIC_ASSERT(SIZEOF(credential->keyHash) == ADDRESS_KEY_HASH_LENGTH, "bad key hash container size"); + view_parseBuffer(credential->keyHash, view, SIZEOF(credential->keyHash)); break; } case CREDENTIAL_SCRIPT_HASH: { - STATIC_ASSERT(SIZEOF(stakeCredential->scriptHash) == SCRIPT_HASH_LENGTH, "bad script hash container size"); - view_parseBuffer(stakeCredential->scriptHash, view, SIZEOF(stakeCredential->scriptHash)); + STATIC_ASSERT(SIZEOF(credential->scriptHash) == SCRIPT_HASH_LENGTH, "bad script hash container size"); + view_parseBuffer(credential->scriptHash, view, SIZEOF(credential->scriptHash)); break; } default: @@ -941,15 +941,15 @@ static void _parseCertificateData(const uint8_t* wireDataBuffer, size_t wireData switch (certificateData->type) { case CERTIFICATE_TYPE_STAKE_REGISTRATION: - _parseStakeCredential(&view, &certificateData->stakeCredential); + _parseCredential(&view, &certificateData->stakeCredential); break; case CERTIFICATE_TYPE_STAKE_DEREGISTRATION: - _parseStakeCredential(&view, &certificateData->stakeCredential); + _parseCredential(&view, &certificateData->stakeCredential); break; case CERTIFICATE_TYPE_STAKE_DELEGATION: - _parseStakeCredential(&view, &certificateData->stakeCredential); + _parseCredential(&view, &certificateData->stakeCredential); // TODO change APDU to parse credential STATIC_ASSERT(SIZEOF(certificateData->poolCredential.keyHash) == POOL_KEY_HASH_LENGTH, "wrong poolKeyHash size"); view_parseBuffer(certificateData->poolCredential.keyHash, &view, POOL_KEY_HASH_LENGTH); @@ -1281,7 +1281,7 @@ static void signTx_handleWithdrawalAPDU(uint8_t p2, const uint8_t* wireDataBuffe read_view_t view = make_read_view(wireDataBuffer, wireDataBuffer + wireDataSize); BODY_CTX->stageData.withdrawal.amount = parse_u8be(&view); - _parseStakeCredential(&view, &BODY_CTX->stageData.withdrawal.stakeCredential); + _parseCredential(&view, &BODY_CTX->stageData.withdrawal.stakeCredential); VALIDATE(view_remainingSize(&view) == 0, ERR_INVALID_DATA); From 9d8024c84c762c309551b19a1b4ee2dee5bfb2ff Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Sat, 7 Oct 2023 15:55:00 +0200 Subject: [PATCH 09/43] refactor: certificates in tx hash builder --- src/txHashBuilder.c | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/src/txHashBuilder.c b/src/txHashBuilder.c index 3d20af3f..6659aa97 100644 --- a/src/txHashBuilder.c +++ b/src/txHashBuilder.c @@ -760,6 +760,15 @@ static uint32_t getStakeCredentialSource(const credential_type_t credentialType) } } +static void _initNewCertificate(tx_hash_builder_t* builder) +{ + _TRACE("state = %d, remainingCertificates = %u", builder->state, builder->remainingCertificates); + + ASSERT(builder->state == TX_HASH_BUILDER_IN_CERTIFICATES); + ASSERT(builder->remainingCertificates > 0); + builder->remainingCertificates--; +} + // stake key certificate registration or deregistration void txHashBuilder_addCertificate_stakingHash( tx_hash_builder_t* builder, @@ -768,17 +777,14 @@ void txHashBuilder_addCertificate_stakingHash( const uint8_t* stakingHash, size_t stakingHashSize ) { - _TRACE("state = %d, remainingCertificates = %u", builder->state, builder->remainingCertificates); - - ASSERT(builder->state == TX_HASH_BUILDER_IN_CERTIFICATES); - ASSERT(builder->remainingCertificates > 0); - builder->remainingCertificates--; + _initNewCertificate(builder); ASSERT((certificateType == CERTIFICATE_TYPE_STAKE_REGISTRATION) || (certificateType == CERTIFICATE_TYPE_STAKE_DEREGISTRATION)); ASSERT(stakingHashSize == ADDRESS_KEY_HASH_LENGTH); + // Array(2)[ // Unsigned[certificateType] // Array(2)[ @@ -806,16 +812,12 @@ void txHashBuilder_addCertificate_stakingHash( void txHashBuilder_addCertificate_delegation( tx_hash_builder_t* builder, - const credential_type_t credentialType, + const ext_credential_type_t credentialType, const uint8_t* stakingKeyHash, size_t stakingKeyHashSize, const uint8_t* poolKeyHash, size_t poolKeyHashSize ) { - _TRACE("state = %d, remainingCertificates = %u", builder->state, builder->remainingCertificates); - - ASSERT(builder->state == TX_HASH_BUILDER_IN_CERTIFICATES); - ASSERT(builder->remainingCertificates > 0); - builder->remainingCertificates--; + _initNewCertificate(builder); ASSERT(stakingKeyHashSize == ADDRESS_KEY_HASH_LENGTH); ASSERT(poolKeyHashSize == POOL_KEY_HASH_LENGTH); @@ -858,11 +860,7 @@ void txHashBuilder_addCertificate_poolRetirement( uint64_t epoch ) { - _TRACE("state = %d", builder->state); - - ASSERT(builder->state == TX_HASH_BUILDER_IN_CERTIFICATES); - ASSERT(builder->remainingCertificates > 0); - builder->remainingCertificates--; + _initNewCertificate(builder); ASSERT(poolKeyHashSize == POOL_KEY_HASH_LENGTH); @@ -893,11 +891,7 @@ void txHashBuilder_poolRegistrationCertificate_enter( uint16_t numOwners, uint16_t numRelays ) { - _TRACE("state = %d, remainingCertificates = %u", builder->state, builder->remainingCertificates); - - ASSERT(builder->state == TX_HASH_BUILDER_IN_CERTIFICATES); - ASSERT(builder->remainingCertificates > 0); - builder->remainingCertificates--; + _initNewCertificate(builder); ASSERT(builder->poolCertificateData.remainingOwners == 0); builder->poolCertificateData.remainingOwners = numOwners; From 61114c9a59c2ee3b1d9db17020ea00f35078e4b0 Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Sat, 7 Oct 2023 16:24:52 +0200 Subject: [PATCH 10/43] refactor: separate extended credential types --- src/cardano.h | 7 ---- src/securityPolicy.c | 20 +++++------ src/securityPolicy.h | 6 ++-- src/signTx.c | 74 ++++++++++++++++++++++------------------ src/signTx.h | 24 ++++++++----- src/signTx_ui.c | 12 +++---- src/txHashBuilder.c | 64 +++++++++++++++++----------------- src/txHashBuilder.h | 19 ++++++++--- src/txHashBuilder_test.c | 31 +++++++++-------- 9 files changed, 139 insertions(+), 118 deletions(-) diff --git a/src/cardano.h b/src/cardano.h index 2915c2ba..33bf1b8c 100644 --- a/src/cardano.h +++ b/src/cardano.h @@ -118,13 +118,6 @@ typedef enum { CERTIFICATE_TYPE_STAKE_POOL_RETIREMENT = 4, } certificate_type_t; -typedef enum { - // enum values are affected by backwards-compatibility - CREDENTIAL_KEY_PATH = 0, - CREDENTIAL_KEY_HASH = 2, - CREDENTIAL_SCRIPT_HASH = 1, -} credential_type_t; - typedef enum { RELAY_SINGLE_HOST_IP = 0, RELAY_SINGLE_HOST_NAME = 1, diff --git a/src/securityPolicy.c b/src/securityPolicy.c index abba9725..ce55724c 100644 --- a/src/securityPolicy.c +++ b/src/securityPolicy.c @@ -1012,7 +1012,7 @@ security_policy_t policyForSignTxCertificate( security_policy_t policyForSignTxCertificateStaking( sign_tx_signingmode_t txSigningMode, const certificate_type_t certificateType, - const credential_t* stakeCredential + const ext_credential_t* stakeCredential ) { switch (certificateType) { @@ -1026,7 +1026,7 @@ security_policy_t policyForSignTxCertificateStaking( } switch (stakeCredential->type) { - case CREDENTIAL_KEY_PATH: + case EXT_CREDENTIAL_KEY_PATH: DENY_UNLESS(bip44_isOrdinaryStakingKeyPath(&stakeCredential->keyPath)); DENY_IF(violatesSingleAccountOrStoreIt(&stakeCredential->keyPath)); switch (txSigningMode) { @@ -1047,7 +1047,7 @@ security_policy_t policyForSignTxCertificateStaking( } break; - case CREDENTIAL_KEY_HASH: + case EXT_CREDENTIAL_KEY_HASH: switch (txSigningMode) { case SIGN_TX_SIGNINGMODE_PLUTUS_TX: PROMPT(); @@ -1066,7 +1066,7 @@ security_policy_t policyForSignTxCertificateStaking( } break; - case CREDENTIAL_SCRIPT_HASH: + case EXT_CREDENTIAL_SCRIPT_HASH: switch (txSigningMode) { case SIGN_TX_SIGNINGMODE_MULTISIG_TX: case SIGN_TX_SIGNINGMODE_PLUTUS_TX: @@ -1097,7 +1097,7 @@ security_policy_t policyForSignTxCertificateStaking( security_policy_t policyForSignTxCertificateStakePoolRetirement( sign_tx_signingmode_t txSigningMode, - const credential_t* poolCredential, + const ext_credential_t* poolCredential, uint64_t epoch MARK_UNUSED ) { @@ -1106,7 +1106,7 @@ security_policy_t policyForSignTxCertificateStakePoolRetirement( case SIGN_TX_SIGNINGMODE_ORDINARY_TX: // pool retirement may only be present in ORDINARY_TX signing mode // the path hash should be a valid pool cold key path - DENY_UNLESS(poolCredential->type == CREDENTIAL_KEY_PATH); + DENY_UNLESS(poolCredential->type == EXT_CREDENTIAL_KEY_PATH); DENY_UNLESS(bip44_isPoolColdKeyPath(&poolCredential->keyPath)); PROMPT(); break; @@ -1299,11 +1299,11 @@ security_policy_t policyForSignTxStakePoolRegistrationConfirm( // For each withdrawal security_policy_t policyForSignTxWithdrawal( sign_tx_signingmode_t txSigningMode, - const credential_t* stakeCredential + const ext_credential_t* stakeCredential ) { switch (stakeCredential->type) { - case CREDENTIAL_KEY_PATH: + case EXT_CREDENTIAL_KEY_PATH: DENY_UNLESS(bip44_isOrdinaryStakingKeyPath(&stakeCredential->keyPath)); DENY_IF(violatesSingleAccountOrStoreIt(&stakeCredential->keyPath)); switch (txSigningMode) { @@ -1326,7 +1326,7 @@ security_policy_t policyForSignTxWithdrawal( } break; - case CREDENTIAL_KEY_HASH: + case EXT_CREDENTIAL_KEY_HASH: switch (txSigningMode) { case SIGN_TX_SIGNINGMODE_PLUTUS_TX: SHOW_IF(app_mode_expert()); @@ -1354,7 +1354,7 @@ security_policy_t policyForSignTxWithdrawal( } break; - case CREDENTIAL_SCRIPT_HASH: + case EXT_CREDENTIAL_SCRIPT_HASH: switch (txSigningMode) { case SIGN_TX_SIGNINGMODE_MULTISIG_TX: case SIGN_TX_SIGNINGMODE_PLUTUS_TX: diff --git a/src/securityPolicy.h b/src/securityPolicy.h index ec6f4b59..f3669f52 100644 --- a/src/securityPolicy.h +++ b/src/securityPolicy.h @@ -111,12 +111,12 @@ security_policy_t policyForSignTxCertificate( security_policy_t policyForSignTxCertificateStaking( sign_tx_signingmode_t txSigningMode, const certificate_type_t certificateType, - const credential_t* stakeCredential + const ext_credential_t* stakeCredential ); #ifdef APP_FEATURE_POOL_RETIREMENT security_policy_t policyForSignTxCertificateStakePoolRetirement( sign_tx_signingmode_t txSigningMode, - const credential_t* poolCredential, + const ext_credential_t* poolCredential, uint64_t epoch ); #endif // APP_FEATURE_POOL_RETIREMENT @@ -153,7 +153,7 @@ security_policy_t policyForSignTxStakePoolRegistrationConfirm( #endif // APP_FEATURE_POOL_REGISTRATION security_policy_t policyForSignTxWithdrawal( sign_tx_signingmode_t txSigningMode, - const credential_t* stakeCredential + const ext_credential_t* stakeCredential ); security_policy_t policyForSignTxAuxData(aux_data_type_t auxDataType); diff --git a/src/signTx.c b/src/signTx.c index 121bf4d8..d53ef80a 100644 --- a/src/signTx.c +++ b/src/signTx.c @@ -907,19 +907,19 @@ static void _parsePathSpec(read_view_t* view, bip44_path_t* pathSpec) PRINTF("\n"); } -static void _parseCredential(read_view_t* view, credential_t* credential) +static void _parseCredential(read_view_t* view, ext_credential_t* credential) { credential->type = parse_u1be(view); switch (credential->type) { - case CREDENTIAL_KEY_PATH: + case EXT_CREDENTIAL_KEY_PATH: _parsePathSpec(view, &credential->keyPath); break; - case CREDENTIAL_KEY_HASH: { + case EXT_CREDENTIAL_KEY_HASH: { STATIC_ASSERT(SIZEOF(credential->keyHash) == ADDRESS_KEY_HASH_LENGTH, "bad key hash container size"); view_parseBuffer(credential->keyHash, view, SIZEOF(credential->keyHash)); break; } - case CREDENTIAL_SCRIPT_HASH: { + case EXT_CREDENTIAL_SCRIPT_HASH: { STATIC_ASSERT(SIZEOF(credential->scriptHash) == SCRIPT_HASH_LENGTH, "bad script hash container size"); view_parseBuffer(credential->scriptHash, view, SIZEOF(credential->scriptHash)); break; @@ -951,6 +951,7 @@ static void _parseCertificateData(const uint8_t* wireDataBuffer, size_t wireData case CERTIFICATE_TYPE_STAKE_DELEGATION: _parseCredential(&view, &certificateData->stakeCredential); // TODO change APDU to parse credential + certificateData->poolCredential.type = EXT_CREDENTIAL_KEY_HASH; STATIC_ASSERT(SIZEOF(certificateData->poolCredential.keyHash) == POOL_KEY_HASH_LENGTH, "wrong poolKeyHash size"); view_parseBuffer(certificateData->poolCredential.keyHash, &view, POOL_KEY_HASH_LENGTH); break; @@ -968,7 +969,7 @@ static void _parseCertificateData(const uint8_t* wireDataBuffer, size_t wireData case CERTIFICATE_TYPE_STAKE_POOL_RETIREMENT: // TODO refactor APDU serialization to parse credential - certificateData->poolCredential.type = CREDENTIAL_KEY_PATH; + certificateData->poolCredential.type = EXT_CREDENTIAL_KEY_PATH; _parsePathSpec(&view, &certificateData->poolCredential.keyPath); certificateData->epoch = parse_u8be(&view); break; @@ -994,34 +995,36 @@ static void _fillHashFromPath(const bip44_path_t* path, ); } -static void _fillHashFromCredential( - const credential_t* credential, - uint8_t* hash, size_t hashSize +static void _setCredential( + credential_t* credential, + const ext_credential_t* extCredential ) { - ASSERT(hashSize < BUFFER_SIZE_PARANOIA); + switch (extCredential->type) { - switch (credential->type) { - case CREDENTIAL_KEY_PATH: - _fillHashFromPath(&credential->keyPath, hash, hashSize); + case EXT_CREDENTIAL_KEY_PATH: + credential->type = CREDENTIAL_KEY_HASH; + _fillHashFromPath(&extCredential->keyPath, credential->keyHash, SIZEOF(credential->keyHash)); break; - case CREDENTIAL_KEY_HASH: - ASSERT(ADDRESS_KEY_HASH_LENGTH <= hashSize); - STATIC_ASSERT(SIZEOF(credential->keyHash) == ADDRESS_KEY_HASH_LENGTH, "bad key hash container size"); - memmove(hash, credential->keyHash, SIZEOF(credential->keyHash)); + + case EXT_CREDENTIAL_KEY_HASH: + credential->type = CREDENTIAL_KEY_HASH; + STATIC_ASSERT(SIZEOF(credential->keyHash) == SIZEOF(extCredential->keyHash), "bad script hash container size"); + memmove(credential->keyHash, extCredential->keyHash, SIZEOF(extCredential->keyHash)); break; - case CREDENTIAL_SCRIPT_HASH: - ASSERT(SCRIPT_HASH_LENGTH <= hashSize); - STATIC_ASSERT(SIZEOF(credential->scriptHash) == SCRIPT_HASH_LENGTH, "bad script hash container size"); - memmove(hash, credential->scriptHash, SIZEOF(credential->scriptHash)); + + case EXT_CREDENTIAL_SCRIPT_HASH: + credential->type = CREDENTIAL_SCRIPT_HASH; + STATIC_ASSERT(SIZEOF(credential->scriptHash) == SIZEOF(extCredential->scriptHash), "bad script hash container size"); + memmove(credential->scriptHash, extCredential->scriptHash, SIZEOF(extCredential->scriptHash)); break; + default: ASSERT(false); break; } } - __noinline_due_to_stack__ static void _addCertificateDataToTx( sign_tx_certificate_data_t* certificateData, @@ -1034,26 +1037,27 @@ static void _addCertificateDataToTx( TRACE("Adding certificate (type %d) to tx hash", certificateData->type); STATIC_ASSERT(ADDRESS_KEY_HASH_LENGTH == SCRIPT_HASH_LENGTH, "incompatible hash sizes"); - uint8_t hash[ADDRESS_KEY_HASH_LENGTH] = {0}; + credential_t stakeCredential; switch (BODY_CTX->stageData.certificate.type) { case CERTIFICATE_TYPE_STAKE_REGISTRATION: case CERTIFICATE_TYPE_STAKE_DEREGISTRATION: { - _fillHashFromCredential(&BODY_CTX->stageData.certificate.stakeCredential, hash, SIZEOF(hash)); + _setCredential(&stakeCredential, &certificateData->stakeCredential); txHashBuilder_addCertificate_stakingHash( - txHashBuilder, certificateData->type, certificateData->stakeCredential.type, - hash, SIZEOF(hash) + txHashBuilder, + certificateData->type, + &stakeCredential ); break; } case CERTIFICATE_TYPE_STAKE_DELEGATION: { - _fillHashFromCredential(&BODY_CTX->stageData.certificate.stakeCredential, hash, SIZEOF(hash)); + _setCredential(&stakeCredential, &certificateData->stakeCredential); + ASSERT(certificateData->poolCredential.type == EXT_CREDENTIAL_KEY_HASH); txHashBuilder_addCertificate_delegation( - txHashBuilder, certificateData->stakeCredential.type, - hash, SIZEOF(hash), - // TODO make sure credential is key hash + txHashBuilder, + &stakeCredential, certificateData->poolCredential.keyHash, SIZEOF(certificateData->poolCredential.keyHash) ); break; @@ -1062,8 +1066,10 @@ static void _addCertificateDataToTx( #ifdef APP_FEATURE_POOL_RETIREMENT case CERTIFICATE_TYPE_STAKE_POOL_RETIREMENT: { - // TODO rename to _fillHashFromCredential - _fillHashFromCredential(&BODY_CTX->stageData.certificate.poolCredential, hash, SIZEOF(hash)); + uint8_t hash[ADDRESS_KEY_HASH_LENGTH] = {0}; + ext_credential_t* credential = &BODY_CTX->stageData.certificate.poolCredential; + ASSERT(credential->type == EXT_CREDENTIAL_KEY_PATH); + _fillHashFromPath(&credential->keyPath, hash, SIZEOF(hash)); txHashBuilder_addCertificate_poolRetirement( txHashBuilder, hash, SIZEOF(hash), @@ -1202,7 +1208,7 @@ static void _addWithdrawalToTxHash(bool validateCanonicalOrdering) uint8_t rewardAddress[REWARD_ACCOUNT_SIZE] = {0}; switch (BODY_CTX->stageData.withdrawal.stakeCredential.type) { - case CREDENTIAL_KEY_PATH: + case EXT_CREDENTIAL_KEY_PATH: constructRewardAddressFromKeyPath( &BODY_CTX->stageData.withdrawal.stakeCredential.keyPath, ctx->commonTxData.networkId, @@ -1210,7 +1216,7 @@ static void _addWithdrawalToTxHash(bool validateCanonicalOrdering) SIZEOF(rewardAddress) ); break; - case CREDENTIAL_KEY_HASH: + case EXT_CREDENTIAL_KEY_HASH: constructRewardAddressFromHash( ctx->commonTxData.networkId, REWARD_HASH_SOURCE_KEY, @@ -1220,7 +1226,7 @@ static void _addWithdrawalToTxHash(bool validateCanonicalOrdering) SIZEOF(rewardAddress) ); break; - case CREDENTIAL_SCRIPT_HASH: + case EXT_CREDENTIAL_SCRIPT_HASH: constructRewardAddressFromHash( ctx->commonTxData.networkId, REWARD_HASH_SOURCE_SCRIPT, diff --git a/src/signTx.h b/src/signTx.h index 20fa9d57..9bdb9929 100644 --- a/src/signTx.h +++ b/src/signTx.h @@ -82,26 +82,34 @@ typedef struct { single_account_data_t singleAccountData; } common_tx_data_t; +// credentials are extended to allow key derivation paths +typedef enum { + // enum values are affected by backwards-compatibility + EXT_CREDENTIAL_KEY_PATH = 0, + EXT_CREDENTIAL_KEY_HASH = 2, + EXT_CREDENTIAL_SCRIPT_HASH = 1, +} ext_credential_type_t; + typedef struct { - credential_type_t type; + ext_credential_type_t type; union { bip44_path_t keyPath; uint8_t keyHash[ADDRESS_KEY_HASH_LENGTH]; uint8_t scriptHash[SCRIPT_HASH_LENGTH]; }; -} credential_t; +} ext_credential_t; typedef struct { certificate_type_t type; union { - credential_t stakeCredential; - // TODO credential_t committeeColdCredential; + ext_credential_t stakeCredential; + // TODO ext_credential_t committeeColdCredential; }; union { - credential_t poolCredential; - // TODO credential_t drepCredential; - // TODO credential_t committeeHotCredential; + ext_credential_t poolCredential; + // TODO ext_credential_t drepCredential; + // TODO ext_credential_t committeeHotCredential; }; uint64_t epoch; } sign_tx_certificate_data_t; @@ -117,7 +125,7 @@ typedef struct { } sign_tx_witness_data_t; typedef struct { - credential_t stakeCredential; + ext_credential_t stakeCredential; uint64_t amount; uint8_t previousRewardAccount[REWARD_ACCOUNT_SIZE]; } sign_tx_withdrawal_data_t; diff --git a/src/signTx_ui.c b/src/signTx_ui.c index 2f4d2b5a..74be17dd 100644 --- a/src/signTx_ui.c +++ b/src/signTx_ui.c @@ -417,7 +417,7 @@ void signTx_handleCertificate_ui_runStep() } UI_STEP(HANDLE_CERTIFICATE_STEP_DISPLAY_STAKING_KEY) { switch (BODY_CTX->stageData.certificate.stakeCredential.type) { - case CREDENTIAL_KEY_PATH: + case EXT_CREDENTIAL_KEY_PATH: #ifdef HAVE_BAGL ui_displayPathScreen( "Stake key", @@ -432,7 +432,7 @@ void signTx_handleCertificate_ui_runStep() } #endif // HAVE_BAGL break; - case CREDENTIAL_KEY_HASH: + case EXT_CREDENTIAL_KEY_HASH: #ifdef HAVE_BAGL ui_displayBech32Screen( "Stake key hash", @@ -449,7 +449,7 @@ void signTx_handleCertificate_ui_runStep() } #endif // HAVE_BAGL break; - case CREDENTIAL_SCRIPT_HASH: + case EXT_CREDENTIAL_SCRIPT_HASH: #ifdef HAVE_BAGL ui_displayBech32Screen( "Stake script hash", @@ -611,12 +611,12 @@ void signTx_handleWithdrawal_ui_runStep() UI_STEP(HANDLE_WITHDRAWAL_STEP_DISPLAY_PATH) { reward_account_t rewardAccount; switch (BODY_CTX->stageData.withdrawal.stakeCredential.type) { - case CREDENTIAL_KEY_PATH: { + case EXT_CREDENTIAL_KEY_PATH: { rewardAccount.keyReferenceType = KEY_REFERENCE_PATH; rewardAccount.path = BODY_CTX->stageData.withdrawal.stakeCredential.keyPath; break; } - case CREDENTIAL_KEY_HASH: { + case EXT_CREDENTIAL_KEY_HASH: { rewardAccount.keyReferenceType = KEY_REFERENCE_HASH; constructRewardAddressFromHash( ctx->commonTxData.networkId, @@ -628,7 +628,7 @@ void signTx_handleWithdrawal_ui_runStep() ); break; } - case CREDENTIAL_SCRIPT_HASH: { + case EXT_CREDENTIAL_SCRIPT_HASH: { rewardAccount.keyReferenceType = KEY_REFERENCE_HASH; constructRewardAddressFromHash( ctx->commonTxData.networkId, diff --git a/src/txHashBuilder.c b/src/txHashBuilder.c index 6659aa97..9b2e3001 100644 --- a/src/txHashBuilder.c +++ b/src/txHashBuilder.c @@ -742,39 +742,44 @@ void txHashBuilder_enterCertificates(tx_hash_builder_t* builder) builder->state = TX_HASH_BUILDER_IN_CERTIFICATES; } -static uint32_t getStakeCredentialSource(const credential_type_t credentialType) -{ - enum { - KEY = 0, - SCRIPT = 1 - }; - switch (credentialType) { - case CREDENTIAL_KEY_PATH: +static void _initNewCertificate(tx_hash_builder_t* builder) +{ + _TRACE("state = %d, remainingCertificates = %u", builder->state, builder->remainingCertificates); + + ASSERT(builder->state == TX_HASH_BUILDER_IN_CERTIFICATES); + ASSERT(builder->remainingCertificates > 0); + builder->remainingCertificates--; +} + +static const uint8_t* getCredentialHashBuffer(const credential_t* credential) +{ + switch (credential->type) { case CREDENTIAL_KEY_HASH: - return KEY; + return credential->keyHash; case CREDENTIAL_SCRIPT_HASH: - return SCRIPT; + return credential->scriptHash; default: ASSERT(false); - break; } } -static void _initNewCertificate(tx_hash_builder_t* builder) +static size_t getCredentialHashSize(const credential_t* credential) { - _TRACE("state = %d, remainingCertificates = %u", builder->state, builder->remainingCertificates); - - ASSERT(builder->state == TX_HASH_BUILDER_IN_CERTIFICATES); - ASSERT(builder->remainingCertificates > 0); - builder->remainingCertificates--; + switch (credential->type) { + case CREDENTIAL_KEY_HASH: + return SIZEOF(credential->keyHash); + case CREDENTIAL_SCRIPT_HASH: + return SIZEOF(credential->scriptHash); + default: + ASSERT(false); + } } // stake key certificate registration or deregistration void txHashBuilder_addCertificate_stakingHash( tx_hash_builder_t* builder, const certificate_type_t certificateType, - const credential_type_t credentialType, - const uint8_t* stakingHash, size_t stakingHashSize + const credential_t* stakeCredential ) { _initNewCertificate(builder); @@ -782,9 +787,6 @@ void txHashBuilder_addCertificate_stakingHash( ASSERT((certificateType == CERTIFICATE_TYPE_STAKE_REGISTRATION) || (certificateType == CERTIFICATE_TYPE_STAKE_DEREGISTRATION)); - ASSERT(stakingHashSize == ADDRESS_KEY_HASH_LENGTH); - - // Array(2)[ // Unsigned[certificateType] // Array(2)[ @@ -800,11 +802,12 @@ void txHashBuilder_addCertificate_stakingHash( { BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 2); { - BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, getStakeCredentialSource(credentialType)); + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, stakeCredential->type); } { - BUILDER_APPEND_CBOR(CBOR_TYPE_BYTES, stakingHashSize); - BUILDER_APPEND_DATA(stakingHash, stakingHashSize); + const size_t size = getCredentialHashSize(stakeCredential); + BUILDER_APPEND_CBOR(CBOR_TYPE_BYTES, size); + BUILDER_APPEND_DATA(getCredentialHashBuffer(stakeCredential), size); } } } @@ -812,14 +815,12 @@ void txHashBuilder_addCertificate_stakingHash( void txHashBuilder_addCertificate_delegation( tx_hash_builder_t* builder, - const ext_credential_type_t credentialType, - const uint8_t* stakingKeyHash, size_t stakingKeyHashSize, + const credential_t* stakeCredential, const uint8_t* poolKeyHash, size_t poolKeyHashSize ) { _initNewCertificate(builder); - ASSERT(stakingKeyHashSize == ADDRESS_KEY_HASH_LENGTH); ASSERT(poolKeyHashSize == POOL_KEY_HASH_LENGTH); // Array(3)[ @@ -838,11 +839,12 @@ void txHashBuilder_addCertificate_delegation( { BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 2); { - BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, getStakeCredentialSource(credentialType)); + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, stakeCredential->type); } { - BUILDER_APPEND_CBOR(CBOR_TYPE_BYTES, stakingKeyHashSize); - BUILDER_APPEND_DATA(stakingKeyHash, stakingKeyHashSize); + const size_t size = getCredentialHashSize(stakeCredential); + BUILDER_APPEND_CBOR(CBOR_TYPE_BYTES, size); + BUILDER_APPEND_DATA(getCredentialHashBuffer(stakeCredential), size); } } { diff --git a/src/txHashBuilder.h b/src/txHashBuilder.h index 64c063c8..075ae583 100644 --- a/src/txHashBuilder.h +++ b/src/txHashBuilder.h @@ -5,6 +5,19 @@ #include "hash.h" #include "addressUtilsShelley.h" +typedef enum { + CREDENTIAL_KEY_HASH = 0, + CREDENTIAL_SCRIPT_HASH = 1, +} credential_type_t; + +typedef struct { + credential_type_t type; + union { + uint8_t keyHash[ADDRESS_KEY_HASH_LENGTH]; + uint8_t scriptHash[SCRIPT_HASH_LENGTH]; + }; +} credential_t; + typedef enum { ARRAY_LEGACY = 0, // legacy_transaction_output MAP_BABBAGE = 1 // post_alonzo_transaction_output @@ -253,14 +266,12 @@ void txHashBuilder_enterCertificates(tx_hash_builder_t* builder); void txHashBuilder_addCertificate_stakingHash( tx_hash_builder_t* builder, const certificate_type_t certificateType, - const credential_type_t credentialType, - const uint8_t* stakingHash, size_t stakingHashSize + const credential_t* stakingCredential ); void txHashBuilder_addCertificate_delegation( tx_hash_builder_t* builder, - const credential_type_t credentialType, - const uint8_t* stakingKeyHash, size_t stakingKeyHashSize, + const credential_t* stakeCredential, const uint8_t* poolKeyHash, size_t poolKeyHashSize ); diff --git a/src/txHashBuilder_test.c b/src/txHashBuilder_test.c index 12dcfeee..9fbb25df 100644 --- a/src/txHashBuilder_test.c +++ b/src/txHashBuilder_test.c @@ -397,24 +397,26 @@ static void addCertificates(tx_hash_builder_t* builder) txHashBuilder_enterCertificates(builder); ITERATE(it, registrationCertificates) { - uint8_t tmp[70] = {0}; - size_t tmpSize = decode_hex(PTR_PIC(it->stakingKeyHash), tmp, SIZEOF(tmp)); + credential_t credential = { + .type = CREDENTIAL_KEY_HASH + }; + decode_hex(PTR_PIC(it->stakingKeyHash), credential.keyHash, SIZEOF(credential.keyHash)); txHashBuilder_addCertificate_stakingHash( builder, CERTIFICATE_TYPE_STAKE_REGISTRATION, - CREDENTIAL_KEY_PATH, - tmp, tmpSize + &credential ); } ITERATE(it, deregistrationCertificates) { - uint8_t tmp[70] = {0}; - size_t tmpSize = decode_hex(PTR_PIC(it->stakingKeyHash), tmp, SIZEOF(tmp)); + credential_t credential = { + .type = CREDENTIAL_KEY_HASH + }; + decode_hex(PTR_PIC(it->stakingKeyHash), credential.keyHash, SIZEOF(credential.keyHash)); txHashBuilder_addCertificate_stakingHash( builder, CERTIFICATE_TYPE_STAKE_DEREGISTRATION, - CREDENTIAL_KEY_PATH, - tmp, tmpSize + &credential ); } @@ -423,16 +425,15 @@ static void addCertificates(tx_hash_builder_t* builder) addPoolRetirementCertificate(builder); ITERATE(it, delegationCertificates) { - uint8_t tmp_credential[70] = {0}; - size_t tmpSize_credential = decode_hex( - PTR_PIC(it->stakingKeyHash), - tmp_credential, SIZEOF(tmp_credential) - ); + credential_t credential = { + .type = CREDENTIAL_KEY_HASH + }; + decode_hex(PTR_PIC(it->stakingKeyHash), credential.keyHash, SIZEOF(credential.keyHash)); uint8_t tmp_pool[70] = {0}; size_t tmpSize_pool = decode_hex(PTR_PIC(it->poolKeyHash), tmp_pool, SIZEOF(tmp_pool)); txHashBuilder_addCertificate_delegation( - builder, CREDENTIAL_KEY_PATH, - tmp_credential, tmpSize_credential, + builder, + &credential, tmp_pool, tmpSize_pool ); } From 5ee6fac8423be26ff32f787264d78c2f571efd7b Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Wed, 11 Oct 2023 11:11:02 +0200 Subject: [PATCH 11/43] refactor: certificates in signTx --- src/signTx.c | 190 ++++++++++++++++++++++++++++----------------------- 1 file changed, 106 insertions(+), 84 deletions(-) diff --git a/src/signTx.c b/src/signTx.c index d53ef80a..a07dc653 100644 --- a/src/signTx.c +++ b/src/signTx.c @@ -983,18 +983,6 @@ static void _parseCertificateData(const uint8_t* wireDataBuffer, size_t wireData VALIDATE(view_remainingSize(&view) == 0, ERR_INVALID_DATA); } -static void _fillHashFromPath(const bip44_path_t* path, - uint8_t* hash, size_t hashSize) -{ - ASSERT(ADDRESS_KEY_HASH_LENGTH <= hashSize); - ASSERT(hashSize < BUFFER_SIZE_PARANOIA); - - bip44_pathToKeyHash( - path, - hash, hashSize - ); -} - static void _setCredential( credential_t* credential, const ext_credential_t* extCredential @@ -1004,7 +992,10 @@ static void _setCredential( case EXT_CREDENTIAL_KEY_PATH: credential->type = CREDENTIAL_KEY_HASH; - _fillHashFromPath(&extCredential->keyPath, credential->keyHash, SIZEOF(credential->keyHash)); + bip44_pathToKeyHash( + &extCredential->keyPath, + credential->keyHash, SIZEOF(credential->keyHash) + ); break; case EXT_CREDENTIAL_KEY_HASH: @@ -1031,12 +1022,8 @@ static void _addCertificateDataToTx( tx_hash_builder_t* txHashBuilder ) { - // data only added in the sub-machine, see signTxPoolRegistration.c - ASSERT(BODY_CTX->stageData.certificate.type != CERTIFICATE_TYPE_STAKE_POOL_REGISTRATION); - TRACE("Adding certificate (type %d) to tx hash", certificateData->type); - STATIC_ASSERT(ADDRESS_KEY_HASH_LENGTH == SCRIPT_HASH_LENGTH, "incompatible hash sizes"); credential_t stakeCredential; switch (BODY_CTX->stageData.certificate.type) { @@ -1067,9 +1054,12 @@ static void _addCertificateDataToTx( case CERTIFICATE_TYPE_STAKE_POOL_RETIREMENT: { uint8_t hash[ADDRESS_KEY_HASH_LENGTH] = {0}; - ext_credential_t* credential = &BODY_CTX->stageData.certificate.poolCredential; - ASSERT(credential->type == EXT_CREDENTIAL_KEY_PATH); - _fillHashFromPath(&credential->keyPath, hash, SIZEOF(hash)); + ext_credential_t* extCredential = &BODY_CTX->stageData.certificate.poolCredential; + ASSERT(extCredential->type == EXT_CREDENTIAL_KEY_PATH); + bip44_pathToKeyHash( + &extCredential->keyPath, + hash, SIZEOF(hash) + ); txHashBuilder_addCertificate_poolRetirement( txHashBuilder, hash, SIZEOF(hash), @@ -1081,18 +1071,16 @@ static void _addCertificateDataToTx( #endif // APP_FEATURE_POOL_RETIREMENT default: + // stake pool registration data only added in the sub-machine, not here + // see signTxPoolRegistration.c ASSERT(false); } } -__noinline_due_to_stack__ -static void signTx_handleCertificateAPDU(uint8_t p2, const uint8_t* wireDataBuffer, size_t wireDataSize) -{ - TRACE_STACK_USAGE(); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); - ASSERT(BODY_CTX->currentCertificate < ctx->numCertificates); +#ifdef APP_FEATURE_POOL_REGISTRATION - #ifdef APP_FEATURE_POOL_REGISTRATION +static bool _handlePoolRegistrationIfNeeded(uint8_t p2, const uint8_t* wireDataBuffer, size_t wireDataSize) +{ // delegate to state sub-machine for stake pool registration certificate data if (signTxPoolRegistration_isValidInstruction(p2)) { TRACE(); @@ -1101,20 +1089,104 @@ static void signTx_handleCertificateAPDU(uint8_t p2, const uint8_t* wireDataBuff TRACE_STACK_USAGE(); signTxPoolRegistration_handleAPDU(p2, wireDataBuffer, wireDataSize); + return true; + } + + return false; +} + +#endif // APP_FEATURE_POOL_REGISTRATION + +static void _handleCertificateStaking() +{ + security_policy_t policy = policyForSignTxCertificateStaking( + ctx->commonTxData.txSigningMode, + BODY_CTX->stageData.certificate.type, + &BODY_CTX->stageData.certificate.stakeCredential + ); + TRACE("Policy: %d", (int) policy); + ENSURE_NOT_DENIED(policy); + + _addCertificateDataToTx(&BODY_CTX->stageData.certificate, &BODY_CTX->txHashBuilder); + + switch (policy) { +#define CASE(POLICY, UI_STEP) case POLICY: {ctx->ui_step=UI_STEP; break;} + CASE(POLICY_PROMPT_BEFORE_RESPONSE, HANDLE_CERTIFICATE_STEP_DISPLAY_OPERATION); + CASE(POLICY_ALLOW_WITHOUT_PROMPT, HANDLE_CERTIFICATE_STEP_RESPOND); +#undef CASE + default: + THROW(ERR_NOT_IMPLEMENTED); + } + + signTx_handleCertificate_ui_runStep(); +} + +#ifdef APP_FEATURE_POOL_REGISTRATION + +static void _handleCertificatePoolRegistration() +{ + // pool registration certificates have a separate sub-machine for handling APDU and UI + // nothing more to be done with them here, we just init the sub-machine + ctx->stage = SIGN_STAGE_BODY_CERTIFICATES_POOL_SUBMACHINE; + signTxPoolRegistration_init(); + + respondSuccessEmptyMsg(); +} + +#endif // APP_FEATURE_POOL_REGISTRATION + + +#ifdef APP_FEATURE_POOL_RETIREMENT + +static void _handleCertificatePoolRetirement() +{ + security_policy_t policy = policyForSignTxCertificateStakePoolRetirement( + ctx->commonTxData.txSigningMode, + &BODY_CTX->stageData.certificate.poolCredential, + BODY_CTX->stageData.certificate.epoch + ); + TRACE("Policy: %d", (int) policy); + ENSURE_NOT_DENIED(policy); + + _addCertificateDataToTx(&BODY_CTX->stageData.certificate, &BODY_CTX->txHashBuilder); + + switch (policy) { +#define CASE(POLICY, UI_STEP) case POLICY: {ctx->ui_step=UI_STEP; break;} + CASE(POLICY_PROMPT_BEFORE_RESPONSE, HANDLE_CERTIFICATE_POOL_RETIREMENT_STEP_DISPLAY_OPERATION); + CASE(POLICY_ALLOW_WITHOUT_PROMPT, HANDLE_CERTIFICATE_POOL_RETIREMENT_STEP_RESPOND); +#undef CASE + default: + THROW(ERR_NOT_IMPLEMENTED); + } + signTx_handleCertificatePoolRetirement_ui_runStep(); +} + +#endif // APP_FEATURE_POOL_RETIREMENT + +// Note(JM): it is possible to treat every certificate separately, +// which makes the code somewhat more readable if read per certificate, +// but it increases code size and that creates problems for Nano S +__noinline_due_to_stack__ +static void signTx_handleCertificateAPDU(uint8_t p2, const uint8_t* wireDataBuffer, size_t wireDataSize) +{ + TRACE_STACK_USAGE(); + ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); + ASSERT(BODY_CTX->currentCertificate < ctx->numCertificates); + + #ifdef APP_FEATURE_POOL_REGISTRATION + // usage of P2 determines if we are in the pool registration submachine + if (_handlePoolRegistrationIfNeeded(p2, wireDataBuffer, wireDataSize)) { return; } #endif // APP_FEATURE_POOL_REGISTRATION - VALIDATE(p2 == P2_UNUSED, ERR_INVALID_REQUEST_PARAMETERS); CHECK_STAGE(SIGN_STAGE_BODY_CERTIFICATES); // a new certificate arrived explicit_bzero(&BODY_CTX->stageData.certificate, SIZEOF(BODY_CTX->stageData.certificate)); - _parseCertificateData(wireDataBuffer, wireDataSize, &BODY_CTX->stageData.certificate); - { - // basic policy that just decides if the certificate is allowed + // basic policy that just decides if the certificate type is allowed security_policy_t policy = policyForSignTxCertificate( ctx->commonTxData.txSigningMode, BODY_CTX->stageData.certificate.type @@ -1123,76 +1195,26 @@ static void signTx_handleCertificateAPDU(uint8_t p2, const uint8_t* wireDataBuff ENSURE_NOT_DENIED(policy); } - // TODO refactor --- does it make sense to process different certificate types entirely separately? - // or perhaps group registration with deregistration? - // notice that _parseCertificateData and _addCertificateDataToTx already do a big switch on cert type switch (BODY_CTX->stageData.certificate.type) { case CERTIFICATE_TYPE_STAKE_REGISTRATION: case CERTIFICATE_TYPE_STAKE_DEREGISTRATION: case CERTIFICATE_TYPE_STAKE_DELEGATION: { - security_policy_t policy = policyForSignTxCertificateStaking( - ctx->commonTxData.txSigningMode, - BODY_CTX->stageData.certificate.type, - &BODY_CTX->stageData.certificate.stakeCredential - ); - TRACE("Policy: %d", (int) policy); - ENSURE_NOT_DENIED(policy); - - _addCertificateDataToTx(&BODY_CTX->stageData.certificate, &BODY_CTX->txHashBuilder); - - switch (policy) { -#define CASE(POLICY, UI_STEP) case POLICY: {ctx->ui_step=UI_STEP; break;} - CASE(POLICY_PROMPT_BEFORE_RESPONSE, HANDLE_CERTIFICATE_STEP_DISPLAY_OPERATION); - CASE(POLICY_ALLOW_WITHOUT_PROMPT, HANDLE_CERTIFICATE_STEP_RESPOND); -#undef CASE - default: - THROW(ERR_NOT_IMPLEMENTED); - } - - signTx_handleCertificate_ui_runStep(); + _handleCertificateStaking(); return; } #ifdef APP_FEATURE_POOL_REGISTRATION - case CERTIFICATE_TYPE_STAKE_POOL_REGISTRATION: { - // pool registration certificates have a separate sub-machine for handling APDU and UI - // nothing more to be done with them here, we just init the sub-machine - ctx->stage = SIGN_STAGE_BODY_CERTIFICATES_POOL_SUBMACHINE; - signTxPoolRegistration_init(); - - respondSuccessEmptyMsg(); + _handleCertificatePoolRegistration(); return; } - #endif // APP_FEATURE_POOL_REGISTRATION #ifdef APP_FEATURE_POOL_RETIREMENT - case CERTIFICATE_TYPE_STAKE_POOL_RETIREMENT: { - // TODO refactor to use credential instead of path directly - security_policy_t policy = policyForSignTxCertificateStakePoolRetirement( - ctx->commonTxData.txSigningMode, - &BODY_CTX->stageData.certificate.poolCredential, - BODY_CTX->stageData.certificate.epoch - ); - TRACE("Policy: %d", (int) policy); - ENSURE_NOT_DENIED(policy); - - _addCertificateDataToTx(&BODY_CTX->stageData.certificate, &BODY_CTX->txHashBuilder); - - switch (policy) { -#define CASE(POLICY, UI_STEP) case POLICY: {ctx->ui_step=UI_STEP; break;} - CASE(POLICY_PROMPT_BEFORE_RESPONSE, HANDLE_CERTIFICATE_POOL_RETIREMENT_STEP_DISPLAY_OPERATION); - CASE(POLICY_ALLOW_WITHOUT_PROMPT, HANDLE_CERTIFICATE_POOL_RETIREMENT_STEP_RESPOND); -#undef CASE - default: - THROW(ERR_NOT_IMPLEMENTED); - } - signTx_handleCertificatePoolRetirement_ui_runStep(); + _handleCertificatePoolRetirement(); return; } - #endif // APP_FEATURE_POOL_RETIREMENT default: From fa22922668036efb56c330d2ca5bcbed48af076e Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Fri, 6 Oct 2023 00:56:20 +0200 Subject: [PATCH 12/43] conway: new key derivation schemas --- src/bip44.c | 68 ++++++++++++++++++++++++++++++++++++++++++-- src/bip44.h | 16 +++++++++++ src/securityPolicy.c | 38 +++++++++++++++++++++++-- 3 files changed, 116 insertions(+), 6 deletions(-) diff --git a/src/bip44.c b/src/bip44.c index 883a98e9..90ffb775 100644 --- a/src/bip44.c +++ b/src/bip44.c @@ -4,9 +4,12 @@ #include "hash.h" #include "keyDerivation.h" -#define CARDANO_CHAIN_EXTERNAL 0 -#define CARDANO_CHAIN_INTERNAL 1 -#define CARDANO_CHAIN_STAKING_KEY 2 +static const uint32_t CARDANO_CHAIN_EXTERNAL = 0; +static const uint32_t CARDANO_CHAIN_INTERNAL = 1; +static const uint32_t CARDANO_CHAIN_STAKING_KEY = 2; +static const uint32_t CARDANO_CHAIN_DREP_KEY = 3; +static const uint32_t CARDANO_CHAIN_COMMITTEE_COLD_KEY = 4; +static const uint32_t CARDANO_CHAIN_COMMITTEE_HOT_KEY = 5; static const uint32_t MAX_REASONABLE_ACCOUNT = 100; static const uint32_t MAX_REASONABLE_ADDRESS = 1000000; @@ -252,6 +255,45 @@ bool bip44_isMultidelegationStakingKeyPath(const bip44_path_t* pathSpec) && (bip44_getAddressValue(pathSpec) > 0); } +bool bip44_isDRepKeyPath(const bip44_path_t* pathSpec) +{ +#define CHECK(cond) if (!(cond)) return false + CHECK(bip44_containsAddress(pathSpec)); + CHECK(!bip44_containsMoreThanAddress(pathSpec)); + CHECK(bip44_hasShelleyPrefix(pathSpec)); + CHECK(isHardened(bip44_getAccount(pathSpec))); + CHECK(bip44_getChainTypeValue(pathSpec) == CARDANO_CHAIN_DREP_KEY); + CHECK(bip44_getAddressValue(pathSpec) == 0); // TODO allow other values and check for hardened only? + return true; +#undef CHECK +} + +bool bip44_isCommitteeColdKeyPath(const bip44_path_t* pathSpec) +{ +#define CHECK(cond) if (!(cond)) return false + CHECK(bip44_containsAddress(pathSpec)); + CHECK(!bip44_containsMoreThanAddress(pathSpec)); + CHECK(bip44_hasShelleyPrefix(pathSpec)); + CHECK(isHardened(bip44_getAccount(pathSpec))); + CHECK(bip44_getChainTypeValue(pathSpec) == CARDANO_CHAIN_COMMITTEE_COLD_KEY); + CHECK(bip44_getAddressValue(pathSpec) == 0); // TODO allow other values and check for hardened only? + return true; +#undef CHECK +} + +bool bip44_isCommitteeHotKeyPath(const bip44_path_t* pathSpec) +{ +#define CHECK(cond) if (!(cond)) return false + CHECK(bip44_containsAddress(pathSpec)); + CHECK(!bip44_containsMoreThanAddress(pathSpec)); + CHECK(bip44_hasShelleyPrefix(pathSpec)); + CHECK(isHardened(bip44_getAccount(pathSpec))); + CHECK(bip44_getChainTypeValue(pathSpec) == CARDANO_CHAIN_COMMITTEE_HOT_KEY); + CHECK(bip44_getAddressValue(pathSpec) == 0); // TODO allow other values and check for hardened only? + return true; +#undef CHECK +} + bool bip44_isMintKeyPath(const bip44_path_t* pathSpec) { #define CHECK(cond) if (!(cond)) return false @@ -367,6 +409,21 @@ static bip44_path_type_t bip44_classifyOrdinaryWalletPath(const bip44_path_t* pa PATH_ORDINARY_STAKING_KEY : PATH_INVALID; + case CARDANO_CHAIN_DREP_KEY: + return bip44_isDRepKeyPath(pathSpec) ? + PATH_DREP_KEY : + PATH_INVALID; + + case CARDANO_CHAIN_COMMITTEE_COLD_KEY: + return bip44_isCommitteeColdKeyPath(pathSpec) ? + PATH_COMMITTEE_COLD_KEY : + PATH_INVALID; + + case CARDANO_CHAIN_COMMITTEE_HOT_KEY: + return bip44_isCommitteeHotKeyPath(pathSpec) ? + PATH_COMMITTEE_HOT_KEY : + PATH_INVALID; + default: return PATH_INVALID; } @@ -492,6 +549,11 @@ bool bip44_isPathReasonable(const bip44_path_t* pathSpec) case PATH_MULTISIG_STAKING_KEY: return bip44_hasReasonableAccount(pathSpec) && bip44_hasReasonableAddress(pathSpec); + case PATH_DREP_KEY: + case PATH_COMMITTEE_COLD_KEY: + case PATH_COMMITTEE_HOT_KEY: + return bip44_hasReasonableAccount(pathSpec) && bip44_hasReasonableAddress(pathSpec); + case PATH_MINT_KEY: return bip44_hasReasonableMintPolicy(pathSpec); diff --git a/src/bip44.h b/src/bip44.h index f8629356..6047e7db 100644 --- a/src/bip44.h +++ b/src/bip44.h @@ -79,12 +79,17 @@ bool bip44_isOrdinaryStakingKeyPath(const bip44_path_t* pathSpec); bool bip44_isMultisigStakingKeyPath(const bip44_path_t* pathSpec); bool bip44_isMultidelegationStakingKeyPath(const bip44_path_t* pathSpec); +bool bip44_isDRepKeyPath(const bip44_path_t* pathSpec); +bool bip44_isCommitteeColdKeyPath(const bip44_path_t* pathSpec); +bool bip44_isCommitteeHotKeyPath(const bip44_path_t* pathSpec); + bool bip44_isMintKeyPath(const bip44_path_t* pathSpec); bool bip44_isPoolColdKeyPath(const bip44_path_t* pathSpec); bool bip44_isCVoteKeyPath(const bip44_path_t* pathSpec); + size_t bip44_printToStr(const bip44_path_t*, char* out, size_t outSize); @@ -101,6 +106,17 @@ typedef enum { PATH_ORDINARY_STAKING_KEY, PATH_MULTISIG_STAKING_KEY, + // DRep key + // m / 1852' / 1815' / account' / 3 / address_index + PATH_DREP_KEY, + + // constitutional committee hot key TODO not approved yet https://github.com/Ryun1/CIPs/blob/conway-keys/CIP-conway-keys/README.md + // m / 1852' / 1815' / account' / 4 / address_index + PATH_COMMITTEE_COLD_KEY, + // constitutional committee cold key TODO not approved yet + // m / 1852' / 1815' / account' / 5 / address_index + PATH_COMMITTEE_HOT_KEY, + // native token minting/burning PATH_MINT_KEY, diff --git a/src/securityPolicy.c b/src/securityPolicy.c index ce55724c..1333a9f8 100644 --- a/src/securityPolicy.c +++ b/src/securityPolicy.c @@ -85,6 +85,10 @@ security_policy_t policyForDerivePrivateKey(const bip44_path_t* path) case PATH_MULTISIG_SPENDING_KEY: case PATH_MULTISIG_STAKING_KEY: + case PATH_DREP_KEY: + case PATH_COMMITTEE_COLD_KEY: + case PATH_COMMITTEE_HOT_KEY: + case PATH_MINT_KEY: case PATH_POOL_COLD_KEY: @@ -162,6 +166,9 @@ security_policy_t policyForGetExtendedPublicKey(const bip44_path_t* pathSpec) case PATH_ORDINARY_STAKING_KEY: case PATH_MULTISIG_SPENDING_KEY: case PATH_MULTISIG_STAKING_KEY: + case PATH_DREP_KEY: + case PATH_COMMITTEE_COLD_KEY: + case PATH_COMMITTEE_HOT_KEY: case PATH_CVOTE_KEY: WARN_UNLESS(bip44_isPathReasonable(pathSpec)); // ask for permission (it is unusual if client asks this instead of the account key) @@ -187,6 +194,9 @@ security_policy_t policyForGetExtendedPublicKeyBulkExport(const bip44_path_t* pa case PATH_MULTISIG_ACCOUNT: case PATH_MULTISIG_SPENDING_KEY: case PATH_MULTISIG_STAKING_KEY: + case PATH_DREP_KEY: + case PATH_COMMITTEE_COLD_KEY: + case PATH_COMMITTEE_HOT_KEY: case PATH_MINT_KEY: case PATH_CVOTE_ACCOUNT: case PATH_CVOTE_KEY: @@ -197,7 +207,7 @@ security_policy_t policyForGetExtendedPublicKeyBulkExport(const bip44_path_t* pa case PATH_POOL_COLD_KEY: WARN_UNLESS(bip44_isPathReasonable(pathSpec)); - // but ask for permission when pool cold key is requested + // but ask for permission PROMPT(); break; @@ -1391,15 +1401,30 @@ static inline security_policy_t _ordinaryWitnessPolicy(const bip44_path_t* path, switch (bip44_classifyPath(path)) { case PATH_ORDINARY_SPENDING_KEY: case PATH_ORDINARY_STAKING_KEY: + // ordinary key paths can be hidden if they are not unusual + // (the user saw all outputs not belonging to him, withdrawals and certificates, + // those belong to him in an ORDINARY txs thanks to + // keys being displayed by paths instead of hashes) DENY_IF(violatesSingleAccountOrStoreIt(path)); WARN_UNLESS(bip44_isPathReasonable(path)); SHOW_IF(app_mode_expert()); ALLOW(); break; + case PATH_DREP_KEY: + case PATH_COMMITTEE_COLD_KEY: + case PATH_COMMITTEE_HOT_KEY: + // these have to be shown because the tx might contain + // an action proposal that cannot be fully shown on the device + // TODO what about violation of single account policy? + DENY_IF(violatesSingleAccountOrStoreIt(path)); + WARN_UNLESS(bip44_isPathReasonable(path)); + SHOW(); + break; + case PATH_POOL_COLD_KEY: - // ordinary key paths and pool cold key paths can be hidden if they are not unusual - // (the user saw all outputs, withdrawals and pool certificates and they all belong to him) + // could be hidden perhaps, but it's safer to let the user to know + // the SW wallet wants to sign with the stake pool key WARN_UNLESS(bip44_isPathReasonable(path)); SHOW(); break; @@ -1439,6 +1464,7 @@ static inline security_policy_t _multisigWitnessPolicy(const bip44_path_t* path, default: // ordinary and pool cold keys forbidden + // DRep and committee keys forbidden DENY(); break; } @@ -1709,6 +1735,12 @@ static bool is_required_signer_allowed(bip44_path_t* path) case PATH_MULTISIG_STAKING_KEY: return true; + case PATH_DREP_KEY: + case PATH_COMMITTEE_COLD_KEY: + case PATH_COMMITTEE_HOT_KEY: + // no known use case, but also no reason to deny + return true; + case PATH_MINT_KEY: return true; From 15e901fd4c941fe1fcb0ca9c328c38ba592a6086 Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Wed, 11 Oct 2023 12:27:18 +0200 Subject: [PATCH 13/43] conway: add tx body items --- doc/ins_sign_tx.md | 42 +- src/bip44.c | 27 +- src/bip44.h | 4 +- src/cardano.h | 21 +- src/getPublicKeys.c | 4 - src/runTests.c | 3 - src/securityPolicy.c | 365 ++++++++++--- src/securityPolicy.h | 32 +- src/signCVote.c | 10 +- src/signTx.c | 781 +++++++++++++++++++++++++--- src/signTx.h | 82 ++- src/signTxCVoteRegistration.c | 15 +- src/signTxMint.c | 9 +- src/signTxOutput.c | 11 - src/signTxPoolRegistration.c | 18 - src/signTxPoolRegistration.h | 2 +- src/signTxPoolRegistration_ui.c | 2 +- src/signTx_ui.c | 883 +++++++++++++++++++++++++++++--- src/signTx_ui.h | 100 +++- src/textUtils.c | 10 +- src/textUtils.h | 2 - src/txHashBuilder.c | 577 +++++++++++++++++++-- src/txHashBuilder.h | 140 ++++- src/txHashBuilder_test.c | 565 -------------------- src/utils.h | 1 - 25 files changed, 2770 insertions(+), 936 deletions(-) delete mode 100644 src/txHashBuilder_test.c diff --git a/doc/ins_sign_tx.md b/doc/ins_sign_tx.md index e8e6aeda..38e60123 100644 --- a/doc/ins_sign_tx.md +++ b/doc/ins_sign_tx.md @@ -1,8 +1,10 @@ # Sign Transaction +Note: this is somewhat incomplete (Babbage and Conway era elements are not described in detail) and some parts might be outdated. We strongly recommend to use [ledgerjs for Cardano](https://github.com/vacuumlabs/ledgerjs-cardano-shelley) for signing transactions. Check its latest API to find out what is supported. + **Description** -Given transaction inputs and transaction outputs, fee, ttl, staking certificates, reward withdrawals, metadata hash, validity interval start, and mint, construct and sign a transaction. +Given transaction inputs and transaction outputs, fee, ttl, staking certificates, reward withdrawals, metadata hash, validity interval start, mint, Plutus (Babbage) additional transaction body elements, and Conway additional elements, construct and sign a transaction. Due to Ledger constraints and potential security implications (parsing errors), Cardano Ledger app uses a custom format for streaming the transaction to be signed. The main rationale behind not streaming directly the (CBOR-encoded) cardano raw transaction to Ledger is the following: 1) The app needs to support BIP44 change address outputs (Ledger should not display user's own change addresses to the user as this degrades UX). @@ -233,7 +235,23 @@ Optional. ### Certificate -We support 4 types of certificates in ordinary transactions (signing mode `SIGN_TX_SIGNINGMODE_ORDINARY_TX` in the initial APDU message): stake key registration, stake key deregistration, stake delegation, and stake pool retirement. We support 3 types in multisig transactions (signing mode `SIGN_TX_SIGNINGMODE_MULTISIG_TX` in the initial APDU message): stake key registration, stake key deregistration, and stake delegation. +We support the following certificate types in ordinary transactions (signing mode `SIGN_TX_SIGNINGMODE_ORDINARY_TX` in the initial APDU message): +* CERTIFICATE_STAKE_REGISTRATION = 0, +* CERTIFICATE_STAKE_DEREGISTRATION = 1, +* CERTIFICATE_STAKE_DELEGATION = 2, +* CERTIFICATE_STAKE_POOL_RETIREMENT = 4, +* CERTIFICATE_STAKE_REGISTRATION_CONWAY = 7, +* CERTIFICATE_STAKE_DEREGISTRATION_CONWAY = 8, +* CERTIFICATE_VOTE_DELEGATION = 9, +* CERTIFICATE_AUTHORIZE_COMMITTEE_HOT = 14, +* CERTIFICATE_RESIGN_COMMITTEE_COLD = 15, +* CERTIFICATE_DREP_REGISTRATION = 16, +* CERTIFICATE_DREP_DEREGISTRATION = 17, +* CERTIFICATE_DREP_UPDATE = 18, + +For signing mode `SIGN_TX_SIGNINGMODE_MULTISIG_TX`, everything from the above list except `CERTIFICATE_STAKE_POOL_RETIREMENT` is allowed. + +For signing mode `SIGN_TX_SIGNINGMODE_PLUTUS_TX`, everything from the above list is allowed. In addition, a transaction using `SIGN_TX_SIGNINGMODE_POOL_REGISTRATION_OPERATOR` or `SIGN_TX_SIGNINGMODE_POOL_REGISTRATION_OWNER` as the signing mode contains a single certificate for stake pool registration which must not be accompanied by other certificates or by withdrawals (due to security concerns about cross-witnessing data between them). This certificate is processed by a state sub-machine. Instructions for this sub-machine are given in P2; see [Stake Pool Registration](ins_sign_stake_pool_registration.md) for the details on accepted P2 values and additional APDU messages needed. @@ -242,41 +260,41 @@ In addition, a transaction using `SIGN_TX_SIGNINGMODE_POOL_REGISTRATION_OPERATOR | P1 | `0x06` | | P2 | (unused / see [Stake Pool Registration](ins_sign_stake_pool_registration.md)) | -**Data for CERTIFICATE_TYPE_STAKE_REGISTRATION** +**Data for CERTIFICATE_STAKE_REGISTRATION** |Field| Length | Comments| |-----|--------|---------| -|Output type| 1 | `CERTIFICATE_TYPE_STAKE_REGISTRATION=0x00`| +|Output type| 1 | `CERTIFICATE_STAKE_REGISTRATION=0x00`| |Stake credential| variable | See stake credential explained above| -**Data for CERTIFICATE_TYPE_STAKE_DEREGISTRATION** +**Data for CERTIFICATE_STAKE_DEREGISTRATION** |Field| Length | Comments| |-----|--------|---------| -|Output type| 1 | `CERTIFICATE_TYPE_STAKE_DEREGISTRATION=0x01`| +|Output type| 1 | `CERTIFICATE_STAKE_DEREGISTRATION=0x01`| |Stake credential| variable | See stake credential explained above| -**Data for CERTIFICATE_TYPE_STAKE_DELEGATION** +**Data for CERTIFICATE_STAKE_DELEGATION** |Field| Length | Comments| |-----|--------|---------| -|Output type| 1 | `CERTIFICATE_TYPE_STAKE_DELEGATION=0x02`| +|Output type| 1 | `CERTIFICATE_STAKE_DELEGATION=0x02`| |Stake credential| variable | See stake credential explained above| |Pool key hash| 28 | Hash of staking pool public key| -**Data for CERTIFICATE_TYPE_STAKE_POOL_REGISTRATION** +**Data for CERTIFICATE_STAKE_POOL_REGISTRATION** |Field| Length | Comments| |-----|--------|---------| -|Output type| 1 | `CERTIFICATE_TYPE_STAKE_POOL_REGISTRATION=0x03`| +|Output type| 1 | `CERTIFICATE_STAKE_POOL_REGISTRATION=0x03`| This only describes the initial certificate message. All the data for this certificate are obtained via a series of additional APDU messages; see [Stake Pool Registration](ins_sign_stake_pool_registration.md) for the details. -**Data for CERTIFICATE_TYPE_STAKE_POOL_RETIREMENT** +**Data for CERTIFICATE_STAKE_POOL_RETIREMENT** |Field| Length | Comments| |-----|--------|---------| -|Output type| 1 | `CERTIFICATE_TYPE_STAKE_POOL_RETIREMENT=0x04`| +|Output type| 1 | `CERTIFICATE_STAKE_POOL_RETIREMENT=0x04`| |Stake key path| variable | BIP44 path. See [GetExtPubKey call](ins_get_public_keys.md) for a format example | |Pool key hash| 28 | Hash of staking pool public key| diff --git a/src/bip44.c b/src/bip44.c index 90ffb775..f569c3f7 100644 --- a/src/bip44.c +++ b/src/bip44.c @@ -216,6 +216,20 @@ static bool bip44_hasReasonableAddress(const bip44_path_t* pathSpec) return (address <= MAX_REASONABLE_ADDRESS); } +static bool bip44_isConwayPathRecommended(const bip44_path_t* pathSpec) +{ + switch (bip44_classifyPath(pathSpec)) { + case PATH_DREP_KEY: + case PATH_COMMITTEE_COLD_KEY: + case PATH_COMMITTEE_HOT_KEY: + // strongly recommended in CIP-0105 to only use 0 as address + return (bip44_getAddressValue(pathSpec) == 0); + default: + ASSERT(false); + return false; + } +} + static bool bip44_containsMoreThanAddress(const bip44_path_t* pathSpec) { return (pathSpec->length > BIP44_I_ADDRESS + 1); @@ -263,7 +277,8 @@ bool bip44_isDRepKeyPath(const bip44_path_t* pathSpec) CHECK(bip44_hasShelleyPrefix(pathSpec)); CHECK(isHardened(bip44_getAccount(pathSpec))); CHECK(bip44_getChainTypeValue(pathSpec) == CARDANO_CHAIN_DREP_KEY); - CHECK(bip44_getAddressValue(pathSpec) == 0); // TODO allow other values and check for hardened only? + // is it strongly recommended (but not forbidden) to only use 0 as address + CHECK(!isHardened(bip44_getAddressValue(pathSpec))); return true; #undef CHECK } @@ -276,7 +291,8 @@ bool bip44_isCommitteeColdKeyPath(const bip44_path_t* pathSpec) CHECK(bip44_hasShelleyPrefix(pathSpec)); CHECK(isHardened(bip44_getAccount(pathSpec))); CHECK(bip44_getChainTypeValue(pathSpec) == CARDANO_CHAIN_COMMITTEE_COLD_KEY); - CHECK(bip44_getAddressValue(pathSpec) == 0); // TODO allow other values and check for hardened only? + // is it strongly recommended (but not forbidden) to only use 0 as address + CHECK(!isHardened(bip44_getAddressValue(pathSpec))); return true; #undef CHECK } @@ -289,7 +305,8 @@ bool bip44_isCommitteeHotKeyPath(const bip44_path_t* pathSpec) CHECK(bip44_hasShelleyPrefix(pathSpec)); CHECK(isHardened(bip44_getAccount(pathSpec))); CHECK(bip44_getChainTypeValue(pathSpec) == CARDANO_CHAIN_COMMITTEE_HOT_KEY); - CHECK(bip44_getAddressValue(pathSpec) == 0); // TODO allow other values and check for hardened only? + // is it strongly recommended (but not forbidden) to only use 0 as address + CHECK(!isHardened(bip44_getAddressValue(pathSpec))); return true; #undef CHECK } @@ -552,7 +569,9 @@ bool bip44_isPathReasonable(const bip44_path_t* pathSpec) case PATH_DREP_KEY: case PATH_COMMITTEE_COLD_KEY: case PATH_COMMITTEE_HOT_KEY: - return bip44_hasReasonableAccount(pathSpec) && bip44_hasReasonableAddress(pathSpec); + return bip44_hasReasonableAccount(pathSpec) + && bip44_hasReasonableAddress(pathSpec) + && bip44_isConwayPathRecommended(pathSpec); case PATH_MINT_KEY: return bip44_hasReasonableMintPolicy(pathSpec); diff --git a/src/bip44.h b/src/bip44.h index 6047e7db..038a0176 100644 --- a/src/bip44.h +++ b/src/bip44.h @@ -110,10 +110,10 @@ typedef enum { // m / 1852' / 1815' / account' / 3 / address_index PATH_DREP_KEY, - // constitutional committee hot key TODO not approved yet https://github.com/Ryun1/CIPs/blob/conway-keys/CIP-conway-keys/README.md + // constitutional committee hot key // m / 1852' / 1815' / account' / 4 / address_index PATH_COMMITTEE_COLD_KEY, - // constitutional committee cold key TODO not approved yet + // constitutional committee cold key // m / 1852' / 1815' / account' / 5 / address_index PATH_COMMITTEE_HOT_KEY, diff --git a/src/cardano.h b/src/cardano.h index 33bf1b8c..95249ac1 100644 --- a/src/cardano.h +++ b/src/cardano.h @@ -24,6 +24,7 @@ STATIC_ASSERT(LOVELACE_MAX_SUPPLY < LOVELACE_INVALID, "bad LOVELACE_INVALID"); #define SCRIPT_HASH_LENGTH 28 #define SCRIPT_DATA_HASH_LENGTH 32 #define OUTPUT_DATUM_HASH_LENGTH 32 +#define ANCHOR_HASH_LENGTH 32 #define MINTING_POLICY_ID_SIZE (SCRIPT_HASH_LENGTH) #define ASSET_NAME_SIZE_MAX 32 @@ -100,6 +101,8 @@ typedef struct { // ============================== CERTIFICATES ============================== +#define ANCHOR_URL_LENGTH_MAX 64 + #define POOL_METADATA_URL_LENGTH_MAX 64 #define DNS_NAME_SIZE_MAX 64 @@ -111,11 +114,19 @@ typedef struct { // there may be other types we do not support typedef enum { - CERTIFICATE_TYPE_STAKE_REGISTRATION = 0, - CERTIFICATE_TYPE_STAKE_DEREGISTRATION = 1, - CERTIFICATE_TYPE_STAKE_DELEGATION = 2, - CERTIFICATE_TYPE_STAKE_POOL_REGISTRATION = 3, - CERTIFICATE_TYPE_STAKE_POOL_RETIREMENT = 4, + CERTIFICATE_STAKE_REGISTRATION = 0, + CERTIFICATE_STAKE_DEREGISTRATION = 1, + CERTIFICATE_STAKE_DELEGATION = 2, + CERTIFICATE_STAKE_POOL_REGISTRATION = 3, + CERTIFICATE_STAKE_POOL_RETIREMENT = 4, + CERTIFICATE_STAKE_REGISTRATION_CONWAY = 7, + CERTIFICATE_STAKE_DEREGISTRATION_CONWAY = 8, + CERTIFICATE_VOTE_DELEGATION = 9, + CERTIFICATE_AUTHORIZE_COMMITTEE_HOT = 14, + CERTIFICATE_RESIGN_COMMITTEE_COLD = 15, + CERTIFICATE_DREP_REGISTRATION = 16, + CERTIFICATE_DREP_DEREGISTRATION = 17, + CERTIFICATE_DREP_UPDATE = 18, } certificate_type_t; typedef enum { diff --git a/src/getPublicKeys.c b/src/getPublicKeys.c index dc2cee7f..182898c0 100644 --- a/src/getPublicKeys.c +++ b/src/getPublicKeys.c @@ -109,8 +109,6 @@ static void getPublicKeys_handleInitAPDU(const uint8_t* wireDataBuffer, size_t w { { CHECK_STAGE(GET_KEYS_STAGE_INIT); - - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } { // parse data @@ -186,8 +184,6 @@ void getPublicKeys_handleGetNextKeyAPDU( { CHECK_STAGE(GET_KEYS_STAGE_GET_KEYS); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); - VALIDATE(ctx->currentPath < ctx->numPaths, ERR_INVALID_STATE); read_view_t view = make_read_view(wireDataBuffer, wireDataBuffer + wireDataSize); diff --git a/src/runTests.c b/src/runTests.c index 959b63ca..29ab61c2 100644 --- a/src/runTests.c +++ b/src/runTests.c @@ -52,9 +52,6 @@ void handleRunTests( run_addressUtilsByron_test(); #endif run_addressUtilsShelley_test(); - #if !defined(APP_XS) - run_txHashBuilder_test(); - #endif run_auxDataHashBuilder_test(); #if defined(APP_FEATURE_NATIVE_SCRIPT_HASH) run_nativeScriptHashBuilder_test(); diff --git a/src/securityPolicy.c b/src/securityPolicy.c index 1333a9f8..7e5e1451 100644 --- a/src/securityPolicy.c +++ b/src/securityPolicy.c @@ -367,7 +367,10 @@ security_policy_t policyForSignTxInit( bool includeNetworkId, bool includeCollateralOutput, bool includeTotalCollateral, - uint16_t numReferenceInputs + uint16_t numReferenceInputs, + uint16_t numVotingProcedures, + bool includeTreasury, + bool includeDonation ) { DENY_UNLESS(isValidNetworkId(networkId)); @@ -399,6 +402,12 @@ security_policy_t policyForSignTxInit( DENY_IF(includeCollateralOutput); DENY_IF(includeTotalCollateral); DENY_IF(numReferenceInputs > 0); + + // no voting, treasuries, donations for pool registrations + // we don't need them and we want to avoid overlap in witnesses + DENY_IF(numVotingProcedures > 0); + DENY_IF(includeTreasury); + DENY_IF(includeDonation); break; case SIGN_TX_SIGNINGMODE_ORDINARY_TX: @@ -883,6 +892,8 @@ security_policy_t policyForSignTxCollateralOutputAdaAmount( bool isTotalCollateralPresent ) { + // WARNING: policies for collateral inputs, collateral return output and total collateral are interdependent + if (outputPolicy == POLICY_ALLOW_WITHOUT_PROMPT) { // output not shown, so none of its elements should be shown ALLOW(); @@ -899,6 +910,8 @@ security_policy_t policyForSignTxCollateralOutputTokens( const tx_output_description_t* output ) { + // WARNING: policies for collateral inputs, collateral return output and total collateral are interdependent + if (outputPolicy == POLICY_ALLOW_WITHOUT_PROMPT) { // output not shown, so none of its elements should be shown ALLOW(); @@ -992,22 +1005,22 @@ security_policy_t policyForSignTxCertificate( case SIGN_TX_SIGNINGMODE_PLUTUS_TX: case SIGN_TX_SIGNINGMODE_ORDINARY_TX: // pool registration is allowed only in POOL_REGISTRATION signing modes - DENY_IF(certificateType == CERTIFICATE_TYPE_STAKE_POOL_REGISTRATION); + DENY_IF(certificateType == CERTIFICATE_STAKE_POOL_REGISTRATION); ALLOW(); break; case SIGN_TX_SIGNINGMODE_MULTISIG_TX: // pool registration is allowed only in POOL_REGISTRATION signing modes - DENY_IF(certificateType == CERTIFICATE_TYPE_STAKE_POOL_REGISTRATION); + DENY_IF(certificateType == CERTIFICATE_STAKE_POOL_REGISTRATION); // pool retirement is impossible with multisig keys - DENY_IF(certificateType == CERTIFICATE_TYPE_STAKE_POOL_RETIREMENT); + DENY_IF(certificateType == CERTIFICATE_STAKE_POOL_RETIREMENT); ALLOW(); break; case SIGN_TX_SIGNINGMODE_POOL_REGISTRATION_OPERATOR: case SIGN_TX_SIGNINGMODE_POOL_REGISTRATION_OWNER: // only pool registration is allowed - DENY_UNLESS(certificateType == CERTIFICATE_TYPE_STAKE_POOL_REGISTRATION); + DENY_UNLESS(certificateType == CERTIFICATE_STAKE_POOL_REGISTRATION); ALLOW(); break; @@ -1018,6 +1031,80 @@ security_policy_t policyForSignTxCertificate( DENY(); // should not be reached } +// applicable to credentials that are witnessed in this tx +static bool _forbiddenCredential( + sign_tx_signingmode_t txSigningMode, + const ext_credential_t* credential +) +{ + // certain combinations of tx signing mode and credential type are not allowed + // either because they don't make sense or are dangerous + switch (txSigningMode) { + case SIGN_TX_SIGNINGMODE_MULTISIG_TX: + switch (credential->type) { + case EXT_CREDENTIAL_KEY_PATH: + case EXT_CREDENTIAL_KEY_HASH: + // everything is expected to be governed by native scripts + return true; + case EXT_CREDENTIAL_SCRIPT_HASH: + break; + default: + ASSERT(false); + } + break; + + case SIGN_TX_SIGNINGMODE_PLUTUS_TX: + // everything allowed, txs are too complex for a HW wallet to understand + // and there might be third-party key hashes in the tx + break; + + case SIGN_TX_SIGNINGMODE_ORDINARY_TX: + switch (credential->type) { + case EXT_CREDENTIAL_KEY_HASH: + case EXT_CREDENTIAL_SCRIPT_HASH: + // keys must be given by path, otherwise the user does not know + // if the hash corresponds to some of his keys, + // and might inadvertently sign several certificates with a single witness + return true; + case EXT_CREDENTIAL_KEY_PATH: + break; + default: + ASSERT(false); + } + break; + + default: + // this should not be called in POOL_REGISTRATION signing modes + ASSERT(false); + } + + return false; +} + +security_policy_t _policyForSignTxCertificateStakeCredential( + sign_tx_signingmode_t txSigningMode, + const ext_credential_t* stakeCredential +) +{ + DENY_IF(_forbiddenCredential(txSigningMode, stakeCredential)); + + switch (stakeCredential->type) { + case EXT_CREDENTIAL_KEY_PATH: + DENY_UNLESS(bip44_isOrdinaryStakingKeyPath(&stakeCredential->keyPath)); + DENY_IF(violatesSingleAccountOrStoreIt(&stakeCredential->keyPath)); + break; + case EXT_CREDENTIAL_KEY_HASH: + case EXT_CREDENTIAL_SCRIPT_HASH: + // the rest is OK, forbidden credentials have been dealt with above + break; + + default: + ASSERT(false); + } + + PROMPT(); +} + // for certificates concerning stake keys and stake delegation security_policy_t policyForSignTxCertificateStaking( sign_tx_signingmode_t txSigningMode, @@ -1026,81 +1113,135 @@ security_policy_t policyForSignTxCertificateStaking( ) { switch (certificateType) { - case CERTIFICATE_TYPE_STAKE_REGISTRATION: - case CERTIFICATE_TYPE_STAKE_DEREGISTRATION: - case CERTIFICATE_TYPE_STAKE_DELEGATION: + case CERTIFICATE_STAKE_REGISTRATION: + case CERTIFICATE_STAKE_REGISTRATION_CONWAY: + case CERTIFICATE_STAKE_DEREGISTRATION: + case CERTIFICATE_STAKE_DEREGISTRATION_CONWAY: + case CERTIFICATE_STAKE_DELEGATION: break; // these are allowed default: ASSERT(false); } - switch (stakeCredential->type) { - case EXT_CREDENTIAL_KEY_PATH: - DENY_UNLESS(bip44_isOrdinaryStakingKeyPath(&stakeCredential->keyPath)); - DENY_IF(violatesSingleAccountOrStoreIt(&stakeCredential->keyPath)); - switch (txSigningMode) { - case SIGN_TX_SIGNINGMODE_ORDINARY_TX: - case SIGN_TX_SIGNINGMODE_PLUTUS_TX: - PROMPT(); - break; + return _policyForSignTxCertificateStakeCredential(txSigningMode, stakeCredential); +} - case SIGN_TX_SIGNINGMODE_MULTISIG_TX: - DENY(); - break; +security_policy_t policyForSignTxCertificateVoteDelegation( + sign_tx_signingmode_t txSigningMode, + const ext_credential_t* stakeCredential, + const ext_drep_t* drep +) +{ + switch (drep->type) { + case EXT_DREP_KEY_PATH: + // DRep can be anything, but if given by key path, it should be a valid path + DENY_UNLESS(bip44_isDRepKeyPath(&drep->keyPath)); + break; - default: - // in POOL_REGISTRATION signing modes, this certificate should have already been - // reported as invalid (only pool registration certificate is allowed) - ASSERT(false); - break; - } + case EXT_DREP_KEY_HASH: + case EXT_DREP_SCRIPT_HASH: + case EXT_DREP_ABSTAIN: + case EXT_DREP_NO_CONFIDENCE: + // nothing to deny break; - case EXT_CREDENTIAL_KEY_HASH: - switch (txSigningMode) { - case SIGN_TX_SIGNINGMODE_PLUTUS_TX: - PROMPT(); - break; + default: + ASSERT(false); + } - case SIGN_TX_SIGNINGMODE_ORDINARY_TX: - case SIGN_TX_SIGNINGMODE_MULTISIG_TX: - DENY(); - break; + return _policyForSignTxCertificateStakeCredential(txSigningMode, stakeCredential); +} - default: - // in POOL_REGISTRATION signing modes, this certificate should have already been - // reported as invalid (only pool registration certificate is allowed) - ASSERT(false); - break; - } +security_policy_t policyForSignTxCertificateCommitteeAuth( + sign_tx_signingmode_t txSigningMode, + const ext_credential_t* coldCredential, + const ext_credential_t* hotCredential +) +{ + DENY_IF(_forbiddenCredential(txSigningMode, coldCredential)); + + switch (coldCredential->type) { + case EXT_CREDENTIAL_KEY_PATH: + DENY_UNLESS(bip44_isCommitteeColdKeyPath(&coldCredential->keyPath)); + DENY_IF(violatesSingleAccountOrStoreIt(&coldCredential->keyPath)); break; + case EXT_CREDENTIAL_KEY_HASH: case EXT_CREDENTIAL_SCRIPT_HASH: - switch (txSigningMode) { - case SIGN_TX_SIGNINGMODE_MULTISIG_TX: - case SIGN_TX_SIGNINGMODE_PLUTUS_TX: - PROMPT(); - break; + // the rest is OK, forbidden credentials have been dealt with above + break; - case SIGN_TX_SIGNINGMODE_ORDINARY_TX: - DENY(); - break; + default: + ASSERT(false); + } - default: - // in POOL_REGISTRATION signing modes, this certificate should have already been - // reported as invalid (only pool registration certificate is allowed) - ASSERT(false); - break; - } + switch (hotCredential->type) { + + case EXT_CREDENTIAL_SCRIPT_HASH: + case EXT_CREDENTIAL_KEY_HASH: + // keys might be governed outside of this device + break; + + case EXT_CREDENTIAL_KEY_PATH: + DENY_UNLESS(bip44_isCommitteeHotKeyPath(&hotCredential->keyPath)); + break; + + default: + ASSERT(false); + } + + PROMPT(); +} + +security_policy_t policyForSignTxCertificateCommitteeResign( + sign_tx_signingmode_t txSigningMode, + const ext_credential_t* coldCredential +) +{ + DENY_IF(_forbiddenCredential(txSigningMode, coldCredential)); + + switch (coldCredential->type) { + case EXT_CREDENTIAL_KEY_PATH: + DENY_UNLESS(bip44_isCommitteeColdKeyPath(&coldCredential->keyPath)); + DENY_IF(violatesSingleAccountOrStoreIt(&coldCredential->keyPath)); + break; + + case EXT_CREDENTIAL_KEY_HASH: + case EXT_CREDENTIAL_SCRIPT_HASH: + // the rest is OK, forbidden credentials have been dealt with above break; default: ASSERT(false); + } + + PROMPT(); +} + +security_policy_t policyForSignTxCertificateDRep( + sign_tx_signingmode_t txSigningMode, + const ext_credential_t* dRepCredential +) +{ + DENY_IF(_forbiddenCredential(txSigningMode, dRepCredential)); + + switch (dRepCredential->type) { + case EXT_CREDENTIAL_KEY_PATH: + DENY_UNLESS(bip44_isDRepKeyPath(&dRepCredential->keyPath)); + DENY_IF(violatesSingleAccountOrStoreIt(&dRepCredential->keyPath)); break; + + case EXT_CREDENTIAL_KEY_HASH: + case EXT_CREDENTIAL_SCRIPT_HASH: + // the rest is OK, forbidden credentials have been dealt with above + break; + + default: + ASSERT(false); } - DENY(); // should not be reached + PROMPT(); } #ifdef APP_FEATURE_POOL_RETIREMENT @@ -1158,7 +1299,6 @@ security_policy_t policyForSignTxStakePoolRegistrationInit( default: ASSERT(false); - break; } DENY(); // should not be reached @@ -1332,7 +1472,6 @@ security_policy_t policyForSignTxWithdrawal( // in POOL_REGISTRATION signing modes, this certificate should have already been // reported as invalid (only pool registration certificate is allowed) ASSERT(false); - break; } break; @@ -1360,7 +1499,6 @@ security_policy_t policyForSignTxWithdrawal( // in POOL_REGISTRATION signing modes, this certificate should have already been // reported as invalid (only pool registration certificate is allowed) ASSERT(false); - break; } break; @@ -1381,7 +1519,6 @@ security_policy_t policyForSignTxWithdrawal( // in POOL_REGISTRATION signing modes, this certificate should have already been // reported as invalid (only pool registration certificate is allowed) ASSERT(false); - break; } break; @@ -1389,7 +1526,6 @@ security_policy_t policyForSignTxWithdrawal( // in POOL_REGISTRATION signing modes, non-zero number of withdrawals // should have already been reported as invalid ASSERT(false); - break; } DENY(); // should not be reached @@ -1414,9 +1550,10 @@ static inline security_policy_t _ordinaryWitnessPolicy(const bip44_path_t* path, case PATH_DREP_KEY: case PATH_COMMITTEE_COLD_KEY: case PATH_COMMITTEE_HOT_KEY: - // these have to be shown because the tx might contain - // an action proposal that cannot be fully shown on the device - // TODO what about violation of single account policy? + // used to sign certificates and voting procedures + // these won't occur often, so little benefit from hiding them + // better to show them at least while they are new + // in the future, we might want to hide some of them in non-expert mode DENY_IF(violatesSingleAccountOrStoreIt(path)); WARN_UNLESS(bip44_isPathReasonable(path)); SHOW(); @@ -1478,6 +1615,9 @@ static inline security_policy_t _plutusWitnessPolicy(const bip44_path_t* path, b case PATH_ORDINARY_STAKING_KEY: case PATH_MULTISIG_SPENDING_KEY: case PATH_MULTISIG_STAKING_KEY: + case PATH_DREP_KEY: + case PATH_COMMITTEE_COLD_KEY: + case PATH_COMMITTEE_HOT_KEY: WARN_UNLESS(bip44_isPathReasonable(path)); SHOW(); break; @@ -1640,7 +1780,7 @@ security_policy_t policyForSignTxMintConfirm(security_policy_t mintInitPolicy) break; case POLICY_SHOW_BEFORE_RESPONSE: - // all minted coins were shown, show a final confirmation prompt as well + // all minted tokens were shown, show a final confirmation prompt as well PROMPT(); break; @@ -1807,6 +1947,100 @@ security_policy_t policyForSignTxReferenceInput(const sign_tx_signingmode_t txSi DENY(); } +// For voting procedures +security_policy_t policyForSignTxVotingProcedure( + sign_tx_signingmode_t txSigningMode, + ext_voter_t* voter +) +{ + // gov action id and vote can be arbitrary + // we only restrict voter because that determines witnesses + // certain combinations of tx signing mode and credential type are not allowed + // either because they don't make sense or are dangerous + switch (txSigningMode) { + + case SIGN_TX_SIGNINGMODE_ORDINARY_TX: + switch (voter->type) { + case EXT_VOTER_COMMITTEE_HOT_KEY_HASH: + case EXT_VOTER_COMMITTEE_HOT_SCRIPT_HASH: + case EXT_VOTER_DREP_KEY_HASH: + case EXT_VOTER_DREP_SCRIPT_HASH: + case EXT_VOTER_STAKE_POOL_KEY_HASH: + // keys must be given by path, otherwise the user does not know + // if the hash corresponds to one of his keys + DENY(); + break; + + case EXT_VOTER_COMMITTEE_HOT_KEY_PATH: + DENY_UNLESS(bip44_isCommitteeHotKeyPath(&voter->keyPath)); + break; + + case EXT_VOTER_DREP_KEY_PATH: + DENY_UNLESS(bip44_isDRepKeyPath(&voter->keyPath)); + break; + + case EXT_VOTER_STAKE_POOL_KEY_PATH: + DENY_UNLESS(bip44_isPoolColdKeyPath(&voter->keyPath)); + break; + + default: + ASSERT(false); + } + break; + + case SIGN_TX_SIGNINGMODE_MULTISIG_TX: + switch (voter->type) { + case EXT_VOTER_COMMITTEE_HOT_KEY_PATH: + case EXT_VOTER_COMMITTEE_HOT_KEY_HASH: + case EXT_VOTER_DREP_KEY_PATH: + case EXT_VOTER_DREP_KEY_HASH: + case EXT_VOTER_STAKE_POOL_KEY_PATH: + case EXT_VOTER_STAKE_POOL_KEY_HASH: + // everything is expected to be governed by native scripts + DENY(); + break; + + case EXT_VOTER_COMMITTEE_HOT_SCRIPT_HASH: + case EXT_VOTER_DREP_SCRIPT_HASH: + // scripts are OK + break; + + default: + ASSERT(false); + } + break; + + case SIGN_TX_SIGNINGMODE_PLUTUS_TX: + // everything allowed, txs are too complex for a HW wallet to understand + // and there might be third-party key hashes in the tx + break; + + default: + // this should not be called in POOL_REGISTRATION signing modes + ASSERT(false); + } + + SHOW(); +} + +// For treasury +security_policy_t policyForSignTxTreasury( + sign_tx_signingmode_t txSigningMode MARK_UNUSED, + uint64_t treasury MARK_UNUSED +) +{ + SHOW(); +} + +// For donation +security_policy_t policyForSignTxDonation( + sign_tx_signingmode_t txSigningMode MARK_UNUSED, + uint64_t donation MARK_UNUSED +) +{ + SHOW(); +} + security_policy_t policyForSignTxConfirm() { PROMPT(); @@ -1880,7 +2114,6 @@ security_policy_t policyForCVoteRegistrationPaymentDestination( default: ASSERT(false); - break; } DENY(); // should not be reached diff --git a/src/securityPolicy.h b/src/securityPolicy.h index f3669f52..b185ac2c 100644 --- a/src/securityPolicy.h +++ b/src/securityPolicy.h @@ -44,7 +44,10 @@ security_policy_t policyForSignTxInit( bool includeNetworkId, bool includeCollateralOutput, bool includeTotalCollateral, - uint16_t numReferenceInputs + uint16_t numReferenceInputs, + uint16_t numVotingProcedures, + bool includeTreasury, + bool includeDonation ); security_policy_t policyForSignTxInput(sign_tx_signingmode_t txSigningMode); @@ -113,6 +116,24 @@ security_policy_t policyForSignTxCertificateStaking( const certificate_type_t certificateType, const ext_credential_t* stakeCredential ); +security_policy_t policyForSignTxCertificateVoteDelegation( + sign_tx_signingmode_t txSigningMode, + const ext_credential_t* stakeCredential, + const ext_drep_t* drep +); +security_policy_t policyForSignTxCertificateCommitteeAuth( + sign_tx_signingmode_t txSigningMode, + const ext_credential_t* coldCredential, + const ext_credential_t* hotCredential +); +security_policy_t policyForSignTxCertificateCommitteeResign( + sign_tx_signingmode_t txSigningMode, + const ext_credential_t* coldCredential +); +security_policy_t policyForSignTxCertificateDRep( + sign_tx_signingmode_t txSigningMode, + const ext_credential_t* dRepCredential +); #ifdef APP_FEATURE_POOL_RETIREMENT security_policy_t policyForSignTxCertificateStakePoolRetirement( sign_tx_signingmode_t txSigningMode, @@ -186,6 +207,15 @@ security_policy_t policyForSignTxTotalCollateral(); security_policy_t policyForSignTxReferenceInput(const sign_tx_signingmode_t txSigningMode); +security_policy_t policyForSignTxVotingProcedure( + sign_tx_signingmode_t txSigningMode, + ext_voter_t* voter +); + +security_policy_t policyForSignTxTreasury(sign_tx_signingmode_t txSigningMode, uint64_t treasury); + +security_policy_t policyForSignTxDonation(sign_tx_signingmode_t txSigningMode, uint64_t donation); + security_policy_t policyForSignTxConfirm(); #ifdef APP_FEATURE_OPCERT diff --git a/src/signCVote.c b/src/signCVote.c index 5cdb7b70..c588b7e8 100644 --- a/src/signCVote.c +++ b/src/signCVote.c @@ -60,9 +60,8 @@ void signCVote_handleInitAPDU( ) { { - //sanity checks + // sanity checks CHECK_STAGE(VOTECAST_STAGE_INIT); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } { TRACE_BUFFER(wireDataBuffer, wireDataSize); @@ -127,9 +126,8 @@ void signCVote_handleVotecastChunkAPDU( ) { { - //sanity checks + // sanity checks CHECK_STAGE(VOTECAST_STAGE_CHUNK); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } { read_view_t view = make_read_view(wireDataBuffer, wireDataBuffer + wireDataSize); @@ -159,9 +157,8 @@ void signCVote_handleConfirmAPDU( { TRACE_STACK_USAGE(); { - //sanity checks + // sanity checks CHECK_STAGE(VOTECAST_STAGE_CONFIRM); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } { // no data to receive @@ -207,7 +204,6 @@ void signCVote_handleWitnessAPDU( { // sanity checks CHECK_STAGE(VOTECAST_STAGE_WITNESS); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } { diff --git a/src/signTx.c b/src/signTx.c index a07dc653..d50ff0cf 100644 --- a/src/signTx.c +++ b/src/signTx.c @@ -30,6 +30,8 @@ static inline void initTxBodyCtx() BODY_CTX->currentWithdrawal = 0; BODY_CTX->currentCollateral = 0; BODY_CTX->currentRequiredSigner = 0; + BODY_CTX->currentReferenceInput = 0; + BODY_CTX->currentVotingProcedure = 0; BODY_CTX->feeReceived = false; BODY_CTX->ttlReceived = false; BODY_CTX->validityIntervalStartReceived = false; @@ -37,6 +39,8 @@ static inline void initTxBodyCtx() BODY_CTX->scriptDataHashReceived = false; BODY_CTX->collateralOutputReceived = false; BODY_CTX->totalCollateralReceived = false; + BODY_CTX->treasuryReceived = false; + BODY_CTX->donationReceived = false; } } @@ -101,7 +105,10 @@ void tx_advanceStage() ctx->includeNetworkId, ctx->includeCollateralOutput, ctx->includeTotalCollateral, - ctx->numReferenceInputs + ctx->numReferenceInputs, + ctx->numVotingProcedures, + ctx->includeTreasury, + ctx->includeDonation ); txHashBuilder_enterInputs(&BODY_CTX->txHashBuilder); } @@ -112,6 +119,7 @@ void tx_advanceStage() ASSERT(BODY_CTX->currentInput == ctx->numInputs); txHashBuilder_enterOutputs(&BODY_CTX->txHashBuilder); initializeOutputSubmachine(); + ctx->stage = SIGN_STAGE_BODY_OUTPUTS; if (ctx->numOutputs > 0) { @@ -123,6 +131,7 @@ void tx_advanceStage() case SIGN_STAGE_BODY_OUTPUTS: // we should have received all outputs ASSERT(BODY_CTX->currentOutput == ctx->numOutputs); + ctx->stage = SIGN_STAGE_BODY_FEE; break; @@ -176,6 +185,7 @@ void tx_advanceStage() } ctx->stage = SIGN_STAGE_BODY_VALIDITY_INTERVAL; + if (ctx->includeValidityIntervalStart) { // wait for Validity interval start APDU break; @@ -186,7 +196,9 @@ void tx_advanceStage() if (ctx->includeValidityIntervalStart) { ASSERT(BODY_CTX->validityIntervalStartReceived); } + ctx->stage = SIGN_STAGE_BODY_MINT; + if (ctx->includeMint) { #ifdef APP_FEATURE_TOKEN_MINTING txHashBuilder_enterMint(&BODY_CTX->txHashBuilder); @@ -203,7 +215,9 @@ void tx_advanceStage() if (ctx->includeMint) { ASSERT(BODY_CTX->mintReceived); } + ctx->stage = SIGN_STAGE_BODY_SCRIPT_DATA_HASH; + if (ctx->includeScriptDataHash) { break; } @@ -213,7 +227,9 @@ void tx_advanceStage() if (ctx->includeScriptDataHash) { ASSERT(BODY_CTX->scriptDataHashReceived); } + ctx->stage = SIGN_STAGE_BODY_COLLATERAL_INPUTS; + if (ctx->numCollateralInputs > 0) { txHashBuilder_enterCollateralInputs(&BODY_CTX->txHashBuilder); break; @@ -222,7 +238,9 @@ void tx_advanceStage() __attribute__((fallthrough)); case SIGN_STAGE_BODY_COLLATERAL_INPUTS: ASSERT(BODY_CTX->currentCollateral == ctx->numCollateralInputs); + ctx->stage = SIGN_STAGE_BODY_REQUIRED_SIGNERS; + if (ctx->numRequiredSigners > 0) { txHashBuilder_enterRequiredSigners(&BODY_CTX->txHashBuilder); break; @@ -235,7 +253,9 @@ void tx_advanceStage() // we are not waiting for any APDU here, network id is already known from the init APDU txHashBuilder_addNetworkId(&BODY_CTX->txHashBuilder, ctx->commonTxData.networkId); } + ctx->stage = SIGN_STAGE_BODY_COLLATERAL_OUTPUT; + if (ctx->includeCollateralOutput) { break; } @@ -245,7 +265,9 @@ void tx_advanceStage() if (ctx->includeCollateralOutput) { ASSERT(BODY_CTX->collateralOutputReceived); } + ctx->stage = SIGN_STAGE_BODY_TOTAL_COLLATERAL; + if (ctx->includeTotalCollateral) { break; } @@ -255,7 +277,9 @@ void tx_advanceStage() if (ctx->includeTotalCollateral) { ASSERT(BODY_CTX->totalCollateralReceived); } + ctx->stage = SIGN_STAGE_BODY_REFERENCE_INPUTS; + if (ctx->numReferenceInputs > 0) { txHashBuilder_enterReferenceInputs(&BODY_CTX->txHashBuilder); break; @@ -264,6 +288,42 @@ void tx_advanceStage() __attribute__((fallthrough)); case SIGN_STAGE_BODY_REFERENCE_INPUTS: ASSERT(BODY_CTX->currentReferenceInput == ctx->numReferenceInputs); + + ctx->stage = SIGN_STAGE_BODY_VOTING_PROCEDURES; + + if (ctx->numVotingProcedures > 0) { + txHashBuilder_enterVotingProcedures(&BODY_CTX->txHashBuilder); + break; + } + + __attribute__((fallthrough)); + case SIGN_STAGE_BODY_VOTING_PROCEDURES: + ASSERT(BODY_CTX->currentVotingProcedure == ctx->numVotingProcedures); + + ctx->stage = SIGN_STAGE_BODY_TREASURY; + + if (ctx->includeTreasury) { + break; + } + + __attribute__((fallthrough)); + case SIGN_STAGE_BODY_TREASURY: + if (ctx->includeTreasury) { + ASSERT(BODY_CTX->treasuryReceived); + } + + ctx->stage = SIGN_STAGE_BODY_DONATION; + + if (ctx->includeDonation) { + break; + } + + __attribute__((fallthrough)); + case SIGN_STAGE_BODY_DONATION: + if (ctx->includeDonation) { + ASSERT(BODY_CTX->donationReceived); + } + ctx->stage = SIGN_STAGE_CONFIRM; break; @@ -424,7 +484,6 @@ static void signTx_handleInitAPDU(uint8_t p2, const uint8_t* wireDataBuffer, siz CHECK_STAGE(SIGN_STAGE_INIT); VALIDATE(p2 == P2_UNUSED, ERR_INVALID_REQUEST_PARAMETERS); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } { @@ -444,6 +503,8 @@ static void signTx_handleInitAPDU(uint8_t p2, const uint8_t* wireDataBuffer, siz uint8_t includeNetworkId; uint8_t includeCollateralOutput; uint8_t includeTotalCollateral; + uint8_t includeTreasury; + uint8_t includeDonation; uint8_t txSigningMode; uint8_t numInputs[4]; @@ -453,6 +514,7 @@ static void signTx_handleInitAPDU(uint8_t p2, const uint8_t* wireDataBuffer, siz uint8_t numCollateralInputs[4]; uint8_t numRequiredSigners[4]; uint8_t numReferenceInputs[4]; + uint8_t numVotingProcedures[4]; uint8_t numWitnesses[4]; }* wireHeader = (void*) wireDataBuffer; @@ -492,6 +554,12 @@ static void signTx_handleInitAPDU(uint8_t p2, const uint8_t* wireDataBuffer, siz ctx->includeTotalCollateral = signTx_parseIncluded(wireHeader->includeTotalCollateral); TRACE("Include total collateral %d", ctx->includeTotalCollateral); + ctx->includeTreasury = signTx_parseIncluded(wireHeader->includeTreasury); + TRACE("Include treasury %d", ctx->includeTreasury); + + ctx->includeDonation = signTx_parseIncluded(wireHeader->includeDonation); + TRACE("Include donation %d", ctx->includeDonation); + ctx->commonTxData.txSigningMode = wireHeader->txSigningMode; TRACE("Signing mode %d", (int) ctx->commonTxData.txSigningMode); switch (ctx->commonTxData.txSigningMode) { @@ -520,21 +588,24 @@ static void signTx_handleInitAPDU(uint8_t p2, const uint8_t* wireDataBuffer, siz ASSERT_TYPE(ctx->numCollateralInputs, uint16_t); ASSERT_TYPE(ctx->numRequiredSigners, uint16_t); ASSERT_TYPE(ctx->numReferenceInputs, uint16_t); + ASSERT_TYPE(ctx->numVotingProcedures, uint16_t); ASSERT_TYPE(ctx->numWitnesses, uint16_t); ctx->numInputs = (uint16_t) u4be_read(wireHeader->numInputs); ctx->numOutputs = (uint16_t) u4be_read(wireHeader->numOutputs); ctx->numCertificates = (uint16_t) u4be_read(wireHeader->numCertificates); ctx->numWithdrawals = (uint16_t) u4be_read(wireHeader->numWithdrawals); - ctx->numCollateralInputs = (uint16_t) u4be_read(wireHeader->numCollateralInputs); + ctx->numCollateralInputs = (uint16_t) u4be_read(wireHeader->numCollateralInputs); ctx->numRequiredSigners = (uint16_t) u4be_read(wireHeader->numRequiredSigners); ctx->numReferenceInputs = (uint16_t) u4be_read(wireHeader->numReferenceInputs); + ctx->numVotingProcedures = (uint16_t) u4be_read(wireHeader->numVotingProcedures); ctx->numWitnesses = (uint16_t) u4be_read(wireHeader->numWitnesses); TRACE( - "num inputs, outputs, certificates, withdrawals, collateral inputs, required signers, reference inputs, witnesses: %d %d %d %d %d %d %d %d", + "num inputs, outputs, certificates, withdrawals, collateral inputs, required signers, reference inputs, voting procedures, witnesses: %d %d %d %d %d %d %d %d %d", ctx->numInputs, ctx->numOutputs, ctx->numCertificates, ctx->numWithdrawals, - ctx->numCollateralInputs, ctx->numRequiredSigners, ctx->numReferenceInputs, ctx->numWitnesses + ctx->numCollateralInputs, ctx->numRequiredSigners, ctx->numReferenceInputs, ctx->numVotingProcedures, + ctx->numWitnesses ); VALIDATE(ctx->numInputs <= SIGN_MAX_INPUTS, ERR_INVALID_DATA); VALIDATE(ctx->numOutputs <= SIGN_MAX_OUTPUTS, ERR_INVALID_DATA); @@ -543,6 +614,7 @@ static void signTx_handleInitAPDU(uint8_t p2, const uint8_t* wireDataBuffer, siz VALIDATE(ctx->numCollateralInputs <= SIGN_MAX_COLLATERAL_INPUTS, ERR_INVALID_DATA); VALIDATE(ctx->numRequiredSigners <= SIGN_MAX_REQUIRED_SIGNERS, ERR_INVALID_DATA); VALIDATE(ctx->numReferenceInputs <= SIGN_MAX_REFERENCE_INPUTS, ERR_INVALID_DATA); + VALIDATE(ctx->numVotingProcedures <= SIGN_MAX_VOTING_PROCEDURES, ERR_INVALID_DATA); // Current code design assumes at least one input. // If this is to be relaxed, stage switching logic needs to be re-visited. @@ -578,7 +650,10 @@ static void signTx_handleInitAPDU(uint8_t p2, const uint8_t* wireDataBuffer, siz ctx->includeNetworkId, ctx->includeCollateralOutput, ctx->includeTotalCollateral, - ctx->numReferenceInputs + ctx->numReferenceInputs, + ctx->numVotingProcedures, + ctx->includeTreasury, + ctx->includeDonation ); TRACE("Policy: %d", (int) policy); ENSURE_NOT_DENIED(policy); @@ -605,7 +680,6 @@ static void signTx_handleAuxDataAPDU(uint8_t p2, const uint8_t* wireDataBuffer, { { TRACE_STACK_USAGE(); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); ASSERT(ctx->includeAuxData == true); // delegate to state sub-machine for CIP-36 voting registration data @@ -749,7 +823,6 @@ static void signTx_handleInputAPDU(uint8_t p2, const uint8_t* wireDataBuffer, si ASSERT(BODY_CTX->currentInput < ctx->numInputs); VALIDATE(p2 == P2_UNUSED, ERR_INVALID_REQUEST_PARAMETERS); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } parseInput(wireDataBuffer, wireDataSize); @@ -783,7 +856,6 @@ static void signTx_handleOutputAPDU(uint8_t p2, const uint8_t* wireDataBuffer, s { { TRACE("p2 = %d", p2); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); TRACE_BUFFER(wireDataBuffer, wireDataSize); } @@ -812,7 +884,6 @@ static void signTx_handleFeeAPDU(uint8_t p2, const uint8_t* wireDataBuffer, size CHECK_STAGE(SIGN_STAGE_BODY_FEE); VALIDATE(p2 == P2_UNUSED, ERR_INVALID_REQUEST_PARAMETERS); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } { // parse data @@ -860,7 +931,6 @@ static void signTx_handleTtlAPDU(uint8_t p2, const uint8_t* wireDataBuffer, size ASSERT(ctx->includeTtl == true); VALIDATE(p2 == P2_UNUSED, ERR_INVALID_REQUEST_PARAMETERS); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } { // parse data @@ -929,9 +999,63 @@ static void _parseCredential(read_view_t* view, ext_credential_t* credential) } } +static void _parseDRep(read_view_t* view, ext_drep_t* drep) +{ + drep->type = parse_u1be(view); + switch (drep->type) { + case EXT_DREP_KEY_PATH: + _parsePathSpec(view, &drep->keyPath); + break; + case EXT_DREP_KEY_HASH: { + STATIC_ASSERT(SIZEOF(drep->keyHash) == ADDRESS_KEY_HASH_LENGTH, "bad key hash container size"); + view_parseBuffer(drep->keyHash, view, SIZEOF(drep->keyHash)); + break; + } + case EXT_DREP_SCRIPT_HASH: { + STATIC_ASSERT(SIZEOF(drep->scriptHash) == SCRIPT_HASH_LENGTH, "bad script hash container size"); + view_parseBuffer(drep->scriptHash, view, SIZEOF(drep->scriptHash)); + break; + } + case EXT_DREP_ABSTAIN: + case EXT_DREP_NO_CONFIDENCE: { + // nothing more to parse + break; + } + default: + THROW(ERR_INVALID_DATA); + } +} + +static void _parseAnchor(read_view_t* view, anchor_t* anchor) +{ + { + uint8_t includeAnchorByte = parse_u1be(view); + anchor->isIncluded = signTx_parseIncluded(includeAnchorByte); + + if (!anchor->isIncluded) { + VALIDATE(view_remainingSize(view) == 0, ERR_INVALID_DATA); + return; + } + } + { + STATIC_ASSERT(SIZEOF(anchor->hash) == ANCHOR_HASH_LENGTH, "wrong anchor buffer size"); + view_parseBuffer(anchor->hash, view, ANCHOR_HASH_LENGTH); + } + { + anchor->urlLength = view_remainingSize(view); + VALIDATE(anchor->urlLength <= ANCHOR_URL_LENGTH_MAX, ERR_INVALID_DATA); + STATIC_ASSERT(SIZEOF(anchor->url) >= ANCHOR_URL_LENGTH_MAX, "wrong anchor url length"); + view_parseBuffer(anchor->url, view, anchor->urlLength); + + // whitespace not allowed + VALIDATE(str_isPrintableAsciiWithoutSpaces(anchor->url, anchor->urlLength), ERR_INVALID_DATA); + } + + VALIDATE(view_remainingSize(view) == 0, ERR_INVALID_DATA); +} + static void _parseCertificateData(const uint8_t* wireDataBuffer, size_t wireDataSize, sign_tx_certificate_data_t* certificateData) { - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); TRACE_BUFFER(wireDataBuffer, wireDataSize); read_view_t view = make_read_view(wireDataBuffer, wireDataBuffer + wireDataSize); @@ -940,25 +1064,58 @@ static void _parseCertificateData(const uint8_t* wireDataBuffer, size_t wireData TRACE("Certificate type: %d", certificateData->type); switch (certificateData->type) { - case CERTIFICATE_TYPE_STAKE_REGISTRATION: + case CERTIFICATE_STAKE_REGISTRATION: + case CERTIFICATE_STAKE_DEREGISTRATION: _parseCredential(&view, &certificateData->stakeCredential); break; - case CERTIFICATE_TYPE_STAKE_DEREGISTRATION: + case CERTIFICATE_STAKE_REGISTRATION_CONWAY: + case CERTIFICATE_STAKE_DEREGISTRATION_CONWAY: _parseCredential(&view, &certificateData->stakeCredential); + certificateData->deposit = parse_u8be(&view); break; - case CERTIFICATE_TYPE_STAKE_DELEGATION: + case CERTIFICATE_STAKE_DELEGATION: _parseCredential(&view, &certificateData->stakeCredential); - // TODO change APDU to parse credential certificateData->poolCredential.type = EXT_CREDENTIAL_KEY_HASH; STATIC_ASSERT(SIZEOF(certificateData->poolCredential.keyHash) == POOL_KEY_HASH_LENGTH, "wrong poolKeyHash size"); view_parseBuffer(certificateData->poolCredential.keyHash, &view, POOL_KEY_HASH_LENGTH); break; + case CERTIFICATE_VOTE_DELEGATION: + _parseCredential(&view, &certificateData->stakeCredential); + _parseDRep(&view, &certificateData->drep); + break; + + case CERTIFICATE_AUTHORIZE_COMMITTEE_HOT: + _parseCredential(&view, &certificateData->committeeColdCredential); + _parseCredential(&view, &certificateData->committeeHotCredential); + break; + + case CERTIFICATE_RESIGN_COMMITTEE_COLD: + _parseCredential(&view, &certificateData->committeeColdCredential); + _parseAnchor(&view, &certificateData->anchor); + break; + + case CERTIFICATE_DREP_REGISTRATION: + _parseCredential(&view, &certificateData->dRepCredential); + certificateData->deposit = parse_u8be(&view); + _parseAnchor(&view, &certificateData->anchor); + break; + + case CERTIFICATE_DREP_DEREGISTRATION: + _parseCredential(&view, &certificateData->dRepCredential); + certificateData->deposit = parse_u8be(&view); + break; + + case CERTIFICATE_DREP_UPDATE: + _parseCredential(&view, &certificateData->dRepCredential); + _parseAnchor(&view, &certificateData->anchor); + break; + #ifdef APP_FEATURE_POOL_REGISTRATION - case CERTIFICATE_TYPE_STAKE_POOL_REGISTRATION: + case CERTIFICATE_STAKE_POOL_REGISTRATION: // nothing more to parse, certificate data will be provided // in additional APDUs processed by a submachine return; @@ -967,8 +1124,7 @@ static void _parseCertificateData(const uint8_t* wireDataBuffer, size_t wireData #ifdef APP_FEATURE_POOL_RETIREMENT - case CERTIFICATE_TYPE_STAKE_POOL_RETIREMENT: - // TODO refactor APDU serialization to parse credential + case CERTIFICATE_STAKE_POOL_RETIREMENT: certificateData->poolCredential.type = EXT_CREDENTIAL_KEY_PATH; _parsePathSpec(&view, &certificateData->poolCredential.keyPath); certificateData->epoch = parse_u8be(&view); @@ -1016,6 +1172,47 @@ static void _setCredential( } } +static void _setDRep( + drep_t* drep, + const ext_drep_t* extDRep +) +{ + switch (extDRep->type) { + + case EXT_DREP_KEY_PATH: + drep->type = DREP_KEY_HASH; + bip44_pathToKeyHash( + &extDRep->keyPath, + drep->keyHash, SIZEOF(drep->keyHash) + ); + break; + + case EXT_DREP_KEY_HASH: + drep->type = DREP_KEY_HASH; + STATIC_ASSERT(SIZEOF(drep->keyHash) == SIZEOF(extDRep->keyHash), "bad script hash container size"); + memmove(drep->keyHash, extDRep->keyHash, SIZEOF(extDRep->keyHash)); + break; + + case EXT_DREP_SCRIPT_HASH: + drep->type = DREP_SCRIPT_HASH; + STATIC_ASSERT(SIZEOF(drep->scriptHash) == SIZEOF(extDRep->scriptHash), "bad script hash container size"); + memmove(drep->scriptHash, extDRep->scriptHash, SIZEOF(extDRep->scriptHash)); + break; + + case EXT_DREP_ABSTAIN: + drep->type = DREP_ALWAYS_ABSTAIN; + break; + + case EXT_DREP_NO_CONFIDENCE: + drep->type = DREP_ALWAYS_NO_CONFIDENCE; + break; + + default: + ASSERT(false); + break; + } +} + __noinline_due_to_stack__ static void _addCertificateDataToTx( sign_tx_certificate_data_t* certificateData, @@ -1024,35 +1221,113 @@ static void _addCertificateDataToTx( { TRACE("Adding certificate (type %d) to tx hash", certificateData->type); - credential_t stakeCredential; + // declared here to save the stack space compiler allocates for this function + credential_t tmpCredential; switch (BODY_CTX->stageData.certificate.type) { - case CERTIFICATE_TYPE_STAKE_REGISTRATION: - case CERTIFICATE_TYPE_STAKE_DEREGISTRATION: { - _setCredential(&stakeCredential, &certificateData->stakeCredential); - txHashBuilder_addCertificate_stakingHash( + case CERTIFICATE_STAKE_REGISTRATION: + case CERTIFICATE_STAKE_DEREGISTRATION: { + _setCredential(&tmpCredential, &certificateData->stakeCredential); + txHashBuilder_addCertificate_stakingOld( + txHashBuilder, + certificateData->type, + &tmpCredential + ); + break; + } + + case CERTIFICATE_STAKE_REGISTRATION_CONWAY: + case CERTIFICATE_STAKE_DEREGISTRATION_CONWAY: { + _setCredential(&tmpCredential, &certificateData->stakeCredential); + txHashBuilder_addCertificate_staking( txHashBuilder, certificateData->type, - &stakeCredential + &tmpCredential, + certificateData->deposit ); break; } - case CERTIFICATE_TYPE_STAKE_DELEGATION: { - _setCredential(&stakeCredential, &certificateData->stakeCredential); + case CERTIFICATE_STAKE_DELEGATION: { + _setCredential(&tmpCredential, &certificateData->stakeCredential); ASSERT(certificateData->poolCredential.type == EXT_CREDENTIAL_KEY_HASH); - txHashBuilder_addCertificate_delegation( + txHashBuilder_addCertificate_stakeDelegation( txHashBuilder, - &stakeCredential, + &tmpCredential, certificateData->poolCredential.keyHash, SIZEOF(certificateData->poolCredential.keyHash) ); break; } + case CERTIFICATE_VOTE_DELEGATION: { + drep_t drep; + _setCredential(&tmpCredential, &certificateData->stakeCredential); + _setDRep(&drep, &certificateData->drep); + txHashBuilder_addCertificate_voteDelegation( + txHashBuilder, + &tmpCredential, + &drep + ); + break; + } + + case CERTIFICATE_AUTHORIZE_COMMITTEE_HOT: { + credential_t hotCredential; + _setCredential(&tmpCredential, &certificateData->committeeColdCredential); + _setCredential(&hotCredential, &certificateData->committeeHotCredential); + txHashBuilder_addCertificate_committeeAuthHot( + txHashBuilder, + &tmpCredential, + &hotCredential + ); + break; + } + + case CERTIFICATE_RESIGN_COMMITTEE_COLD: { + _setCredential(&tmpCredential, &certificateData->committeeColdCredential); + txHashBuilder_addCertificate_committeeResign( + txHashBuilder, + &tmpCredential, + &certificateData->anchor + ); + break; + } + + case CERTIFICATE_DREP_REGISTRATION: { + _setCredential(&tmpCredential, &certificateData->dRepCredential); + txHashBuilder_addCertificate_dRepRegistration( + txHashBuilder, + &tmpCredential, + certificateData->deposit, + &certificateData->anchor + ); + break; + } + + case CERTIFICATE_DREP_DEREGISTRATION: { + _setCredential(&tmpCredential, &certificateData->dRepCredential); + txHashBuilder_addCertificate_dRepDeregistration( + txHashBuilder, + &tmpCredential, + certificateData->deposit + ); + break; + } + + case CERTIFICATE_DREP_UPDATE: { + _setCredential(&tmpCredential, &certificateData->dRepCredential); + txHashBuilder_addCertificate_dRepUpdate( + txHashBuilder, + &tmpCredential, + &certificateData->anchor + ); + break; + } + #ifdef APP_FEATURE_POOL_RETIREMENT - case CERTIFICATE_TYPE_STAKE_POOL_RETIREMENT: { + case CERTIFICATE_STAKE_POOL_RETIREMENT: { uint8_t hash[ADDRESS_KEY_HASH_LENGTH] = {0}; ext_credential_t* extCredential = &BODY_CTX->stageData.certificate.poolCredential; ASSERT(extCredential->type == EXT_CREDENTIAL_KEY_PATH); @@ -1111,14 +1386,108 @@ static void _handleCertificateStaking() switch (policy) { #define CASE(POLICY, UI_STEP) case POLICY: {ctx->ui_step=UI_STEP; break;} - CASE(POLICY_PROMPT_BEFORE_RESPONSE, HANDLE_CERTIFICATE_STEP_DISPLAY_OPERATION); - CASE(POLICY_ALLOW_WITHOUT_PROMPT, HANDLE_CERTIFICATE_STEP_RESPOND); + CASE(POLICY_PROMPT_BEFORE_RESPONSE, HANDLE_CERTIFICATE_STAKING_STEP_DISPLAY_OPERATION); + CASE(POLICY_ALLOW_WITHOUT_PROMPT, HANDLE_CERTIFICATE_STAKING_STEP_RESPOND); #undef CASE default: THROW(ERR_NOT_IMPLEMENTED); } - signTx_handleCertificate_ui_runStep(); + signTx_handleCertificateStaking_ui_runStep(); +} + +static void _handleCertificateVoteDeleg() +{ + security_policy_t policy = policyForSignTxCertificateVoteDelegation( + ctx->commonTxData.txSigningMode, + &BODY_CTX->stageData.certificate.stakeCredential, + &BODY_CTX->stageData.certificate.drep + ); + TRACE("Policy: %d", (int) policy); + ENSURE_NOT_DENIED(policy); + + _addCertificateDataToTx(&BODY_CTX->stageData.certificate, &BODY_CTX->txHashBuilder); + + switch (policy) { +#define CASE(POLICY, UI_STEP) case POLICY: {ctx->ui_step=UI_STEP; break;} + CASE(POLICY_PROMPT_BEFORE_RESPONSE, HANDLE_CERTIFICATE_VOTE_DELEGATION_STEP_DISPLAY_OPERATION); + CASE(POLICY_ALLOW_WITHOUT_PROMPT, HANDLE_CERTIFICATE_VOTE_DELEGATION_STEP_RESPOND); +#undef CASE + default: + THROW(ERR_NOT_IMPLEMENTED); + } + + signTx_handleCertificateVoteDeleg_ui_runStep(); +} + +static void _handleCertificateCommitteeAuth() +{ + security_policy_t policy = policyForSignTxCertificateCommitteeAuth( + ctx->commonTxData.txSigningMode, + &BODY_CTX->stageData.certificate.committeeColdCredential, + &BODY_CTX->stageData.certificate.committeeHotCredential + ); + TRACE("Policy: %d", (int) policy); + ENSURE_NOT_DENIED(policy); + + _addCertificateDataToTx(&BODY_CTX->stageData.certificate, &BODY_CTX->txHashBuilder); + + switch (policy) { +#define CASE(POLICY, UI_STEP) case POLICY: {ctx->ui_step=UI_STEP; break;} + CASE(POLICY_PROMPT_BEFORE_RESPONSE, HANDLE_CERTIFICATE_COMM_AUTH_STEP_DISPLAY_OPERATION); + CASE(POLICY_ALLOW_WITHOUT_PROMPT, HANDLE_CERTIFICATE_COMM_AUTH_STEP_RESPOND); +#undef CASE + default: + THROW(ERR_NOT_IMPLEMENTED); + } + + signTx_handleCertificateCommitteeAuth_ui_runStep(); +} + +static void _handleCertificateCommitteeResign() +{ + security_policy_t policy = policyForSignTxCertificateCommitteeResign( + ctx->commonTxData.txSigningMode, + &BODY_CTX->stageData.certificate.committeeColdCredential + ); + TRACE("Policy: %d", (int) policy); + ENSURE_NOT_DENIED(policy); + + _addCertificateDataToTx(&BODY_CTX->stageData.certificate, &BODY_CTX->txHashBuilder); + + switch (policy) { +#define CASE(POLICY, UI_STEP) case POLICY: {ctx->ui_step=UI_STEP; break;} + CASE(POLICY_PROMPT_BEFORE_RESPONSE, HANDLE_CERTIFICATE_COMM_RESIGN_STEP_DISPLAY_OPERATION); + CASE(POLICY_ALLOW_WITHOUT_PROMPT, HANDLE_CERTIFICATE_COMM_RESIGN_STEP_RESPOND); +#undef CASE + default: + THROW(ERR_NOT_IMPLEMENTED); + } + + signTx_handleCertificateCommitteeResign_ui_runStep(); +} + +static void _handleCertificateDRep() +{ + security_policy_t policy = policyForSignTxCertificateDRep( + ctx->commonTxData.txSigningMode, + &BODY_CTX->stageData.certificate.dRepCredential + ); + TRACE("Policy: %d", (int) policy); + ENSURE_NOT_DENIED(policy); + + _addCertificateDataToTx(&BODY_CTX->stageData.certificate, &BODY_CTX->txHashBuilder); + + switch (policy) { +#define CASE(POLICY, UI_STEP) case POLICY: {ctx->ui_step=UI_STEP; break;} + CASE(POLICY_PROMPT_BEFORE_RESPONSE, HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_OPERATION); + CASE(POLICY_ALLOW_WITHOUT_PROMPT, HANDLE_CERTIFICATE_DREP_STEP_RESPOND); +#undef CASE + default: + THROW(ERR_NOT_IMPLEMENTED); + } + + signTx_handleCertificateDRep_ui_runStep(); } #ifdef APP_FEATURE_POOL_REGISTRATION @@ -1170,7 +1539,6 @@ __noinline_due_to_stack__ static void signTx_handleCertificateAPDU(uint8_t p2, const uint8_t* wireDataBuffer, size_t wireDataSize) { TRACE_STACK_USAGE(); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); ASSERT(BODY_CTX->currentCertificate < ctx->numCertificates); #ifdef APP_FEATURE_POOL_REGISTRATION @@ -1196,22 +1564,46 @@ static void signTx_handleCertificateAPDU(uint8_t p2, const uint8_t* wireDataBuff } switch (BODY_CTX->stageData.certificate.type) { - case CERTIFICATE_TYPE_STAKE_REGISTRATION: - case CERTIFICATE_TYPE_STAKE_DEREGISTRATION: - case CERTIFICATE_TYPE_STAKE_DELEGATION: { + case CERTIFICATE_STAKE_REGISTRATION: + case CERTIFICATE_STAKE_DEREGISTRATION: + case CERTIFICATE_STAKE_REGISTRATION_CONWAY: + case CERTIFICATE_STAKE_DEREGISTRATION_CONWAY: + case CERTIFICATE_STAKE_DELEGATION: { _handleCertificateStaking(); return; } + case CERTIFICATE_VOTE_DELEGATION: { + _handleCertificateVoteDeleg(); + return; + } + + case CERTIFICATE_AUTHORIZE_COMMITTEE_HOT: { + _handleCertificateCommitteeAuth(); + return; + } + + case CERTIFICATE_RESIGN_COMMITTEE_COLD: { + _handleCertificateCommitteeResign(); + return; + } + + case CERTIFICATE_DREP_REGISTRATION: + case CERTIFICATE_DREP_DEREGISTRATION: + case CERTIFICATE_DREP_UPDATE: { + _handleCertificateDRep(); + return; + } + #ifdef APP_FEATURE_POOL_REGISTRATION - case CERTIFICATE_TYPE_STAKE_POOL_REGISTRATION: { + case CERTIFICATE_STAKE_POOL_REGISTRATION: { _handleCertificatePoolRegistration(); return; } #endif // APP_FEATURE_POOL_REGISTRATION #ifdef APP_FEATURE_POOL_RETIREMENT - case CERTIFICATE_TYPE_STAKE_POOL_RETIREMENT: { + case CERTIFICATE_STAKE_POOL_RETIREMENT: { _handleCertificatePoolRetirement(); return; } @@ -1297,7 +1689,6 @@ static void signTx_handleWithdrawalAPDU(uint8_t p2, const uint8_t* wireDataBuffe ASSERT(BODY_CTX->currentWithdrawal < ctx->numWithdrawals); VALIDATE(p2 == P2_UNUSED, ERR_INVALID_REQUEST_PARAMETERS); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } explicit_bzero(&BODY_CTX->stageData.withdrawal, SIZEOF(BODY_CTX->stageData.withdrawal)); @@ -1350,7 +1741,6 @@ static void signTx_handleValidityIntervalStartAPDU(uint8_t p2, const uint8_t* wi ASSERT(ctx->includeValidityIntervalStart == true); VALIDATE(p2 == P2_UNUSED, ERR_INVALID_REQUEST_PARAMETERS); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } { @@ -1398,7 +1788,6 @@ static void signTx_handleMintAPDU(uint8_t p2, const uint8_t* wireDataBuffer, siz { { TRACE("p2 = %d", p2); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); TRACE_BUFFER(wireDataBuffer, wireDataSize); } @@ -1422,9 +1811,9 @@ static void signTx_handleScriptDataHashAPDU(uint8_t p2, const uint8_t* wireDataB { // sanity checks CHECK_STAGE(SIGN_STAGE_BODY_SCRIPT_DATA_HASH); + ASSERT(ctx->includeScriptDataHash == true); VALIDATE(p2 == P2_UNUSED, ERR_INVALID_REQUEST_PARAMETERS); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } { // parse data @@ -1486,7 +1875,6 @@ static void signTx_handleCollateralInputAPDU(uint8_t p2, const uint8_t* wireData ASSERT(BODY_CTX->currentCollateral < ctx->numCollateralInputs); VALIDATE(p2 == P2_UNUSED, ERR_INVALID_REQUEST_PARAMETERS); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } parseInput(wireDataBuffer, wireDataSize); @@ -1528,7 +1916,6 @@ static void signTx_handleRequiredSignerAPDU(uint8_t p2, const uint8_t* wireDataB ASSERT(BODY_CTX->currentRequiredSigner < ctx->numRequiredSigners); VALIDATE(p2 == P2_UNUSED, ERR_INVALID_REQUEST_PARAMETERS); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } { @@ -1586,13 +1973,13 @@ static void signTx_handleRequiredSignerAPDU(uint8_t p2, const uint8_t* wireDataB } signTx_handleRequiredSigner_ui_runStep(); } + // ========================= COLLATERAL RETURN OUTPUT =========================== static void signTx_handleCollateralOutputAPDU(uint8_t p2, const uint8_t* wireDataBuffer, size_t wireDataSize) { { TRACE("p2 = %d", p2); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); TRACE_BUFFER(wireDataBuffer, wireDataSize); } @@ -1617,9 +2004,9 @@ static void signTx_handleTotalCollateralAPDU(uint8_t p2, const uint8_t* wireData { // sanity checks CHECK_STAGE(SIGN_STAGE_BODY_TOTAL_COLLATERAL); + ASSERT(ctx->includeTotalCollateral == true); VALIDATE(p2 == P2_UNUSED, ERR_INVALID_REQUEST_PARAMETERS); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } { // parse data @@ -1647,8 +2034,8 @@ static void signTx_handleTotalCollateralAPDU(uint8_t p2, const uint8_t* wireData // select UI steps switch (policy) { #define CASE(POLICY, UI_STEP) case POLICY: {ctx->ui_step=UI_STEP; break;} - CASE(POLICY_SHOW_BEFORE_RESPONSE, HANDLE_FEE_STEP_DISPLAY); - CASE(POLICY_ALLOW_WITHOUT_PROMPT, HANDLE_FEE_STEP_RESPOND); + CASE(POLICY_SHOW_BEFORE_RESPONSE, HANDLE_TOTAL_COLLATERAL_STEP_DISPLAY); + CASE(POLICY_ALLOW_WITHOUT_PROMPT, HANDLE_TOTAL_COLLATERAL_STEP_RESPOND); #undef CASE default: THROW(ERR_NOT_IMPLEMENTED); @@ -1672,7 +2059,7 @@ static void ui_advanceState_ReferenceInput() } __noinline_due_to_stack__ -static void signTx_handleReferenceInputsAPDU(uint8_t p2, const uint8_t* wireDataBuffer, size_t wireDataSize) +static void signTx_handleReferenceInputAPDU(uint8_t p2, const uint8_t* wireDataBuffer, size_t wireDataSize) { TRACE_STACK_USAGE(); { @@ -1681,7 +2068,6 @@ static void signTx_handleReferenceInputsAPDU(uint8_t p2, const uint8_t* wireData ASSERT(BODY_CTX->currentReferenceInput < ctx->numReferenceInputs); VALIDATE(p2 == P2_UNUSED, ERR_INVALID_REQUEST_PARAMETERS); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } // Parsed in same way as the inputs parseInput(wireDataBuffer, wireDataSize); @@ -1708,6 +2094,276 @@ static void signTx_handleReferenceInputsAPDU(uint8_t p2, const uint8_t* wireData } } + +// ========================= VOTING PROCEDURES =========================== + +static void _setVoter( + voter_t* voter, + const ext_voter_t* extVoter +) +{ + switch (extVoter->type) { + + case EXT_VOTER_COMMITTEE_HOT_KEY_PATH: + voter->type = VOTER_COMMITTEE_HOT_KEY_HASH; + bip44_pathToKeyHash( + &extVoter->keyPath, + voter->keyHash, SIZEOF(voter->keyHash) + ); + break; + + case EXT_VOTER_DREP_KEY_PATH: + voter->type = VOTER_DREP_KEY_HASH; + bip44_pathToKeyHash( + &extVoter->keyPath, + voter->keyHash, SIZEOF(voter->keyHash) + ); + break; + + case EXT_VOTER_STAKE_POOL_KEY_PATH: + voter->type = VOTER_STAKE_POOL_KEY_HASH; + bip44_pathToKeyHash( + &extVoter->keyPath, + voter->keyHash, SIZEOF(voter->keyHash) + ); + break; + + case EXT_VOTER_COMMITTEE_HOT_KEY_HASH: + voter->type = VOTER_COMMITTEE_HOT_KEY_HASH; + STATIC_ASSERT(SIZEOF(voter->keyHash) == SIZEOF(extVoter->keyHash), "bad script hash container size"); + memmove(voter->keyHash, extVoter->keyHash, SIZEOF(extVoter->keyHash)); + break; + + case EXT_VOTER_DREP_KEY_HASH: + voter->type = VOTER_DREP_KEY_HASH; + STATIC_ASSERT(SIZEOF(voter->keyHash) == SIZEOF(extVoter->keyHash), "bad script hash container size"); + memmove(voter->keyHash, extVoter->keyHash, SIZEOF(extVoter->keyHash)); + break; + + case EXT_VOTER_STAKE_POOL_KEY_HASH: + voter->type = VOTER_STAKE_POOL_KEY_HASH; + STATIC_ASSERT(SIZEOF(voter->keyHash) == SIZEOF(extVoter->keyHash), "bad script hash container size"); + memmove(voter->keyHash, extVoter->keyHash, SIZEOF(extVoter->keyHash)); + break; + + case EXT_VOTER_COMMITTEE_HOT_SCRIPT_HASH: + voter->type = VOTER_COMMITTEE_HOT_SCRIPT_HASH; + STATIC_ASSERT(SIZEOF(voter->scriptHash) == SIZEOF(extVoter->scriptHash), "bad script hash container size"); + memmove(voter->scriptHash, extVoter->scriptHash, SIZEOF(extVoter->scriptHash)); + break; + + case EXT_VOTER_DREP_SCRIPT_HASH: + voter->type = VOTER_DREP_SCRIPT_HASH; + STATIC_ASSERT(SIZEOF(voter->scriptHash) == SIZEOF(extVoter->scriptHash), "bad script hash container size"); + memmove(voter->scriptHash, extVoter->scriptHash, SIZEOF(extVoter->scriptHash)); + break; + + default: + ASSERT(false); + break; + } +} + +__noinline_due_to_stack__ +static void signTx_handleVotingProcedureAPDU(uint8_t p2, const uint8_t* wireDataBuffer, size_t wireDataSize) +{ + TRACE_STACK_USAGE(); + { + // sanity checks + CHECK_STAGE(SIGN_STAGE_BODY_VOTING_PROCEDURES); + ASSERT(BODY_CTX->currentVotingProcedure < ctx->numVotingProcedures); + + VALIDATE(p2 == P2_UNUSED, ERR_INVALID_REQUEST_PARAMETERS); + } + + { + TRACE_BUFFER(wireDataBuffer, wireDataSize); + + read_view_t view = make_read_view(wireDataBuffer, wireDataBuffer + wireDataSize); + { + // voter + ext_voter_t* voter = &BODY_CTX->stageData.votingProcedure.voter; + voter->type = parse_u1be(&view); + switch (voter->type) { + case EXT_VOTER_COMMITTEE_HOT_KEY_PATH: + case EXT_VOTER_DREP_KEY_PATH: + case EXT_VOTER_STAKE_POOL_KEY_PATH: { + _parsePathSpec(&view, &voter->keyPath); + break; + } + case EXT_VOTER_COMMITTEE_HOT_KEY_HASH: + case EXT_VOTER_DREP_KEY_HASH: + case EXT_VOTER_STAKE_POOL_KEY_HASH: { + STATIC_ASSERT(SIZEOF(voter->keyHash) == ADDRESS_KEY_HASH_LENGTH, "bad key hash container size"); + view_parseBuffer(voter->keyHash, &view, SIZEOF(voter->keyHash)); + break; + } + case EXT_VOTER_COMMITTEE_HOT_SCRIPT_HASH: + case EXT_VOTER_DREP_SCRIPT_HASH: { + STATIC_ASSERT(SIZEOF(voter->scriptHash) == SCRIPT_HASH_LENGTH, "bad script hash container size"); + view_parseBuffer(voter->scriptHash, &view, SIZEOF(voter->scriptHash)); + break; + } + default: + THROW(ERR_INVALID_DATA); + } + } + { + // gov action id + gov_action_id_t* actionId = &BODY_CTX->stageData.votingProcedure.govActionId; + view_parseBuffer(actionId->txHashBuffer, &view, TX_HASH_LENGTH); + actionId->govActionIndex = parse_u4be(&view); + } + { + // voting procedure + voting_procedure_t* procedure = &BODY_CTX->stageData.votingProcedure.votingProcedure; + procedure->vote = parse_u1be(&view); + switch (procedure->vote) { + case VOTE_NO: + case VOTE_YES: + case VOTE_ABSTAIN: + // OK + break; + default: + THROW(ERR_INVALID_DATA); + } + _parseAnchor(&view, &procedure->anchor); + } + VALIDATE(view_remainingSize(&view) == 0, ERR_INVALID_DATA); + } + + security_policy_t policy = policyForSignTxVotingProcedure( + ctx->commonTxData.txSigningMode, + &BODY_CTX->stageData.votingProcedure.voter + ); + TRACE("Policy: %d", (int) policy); + ENSURE_NOT_DENIED(policy); + + // Note: if more than one voter is ever allowed, we need to check canonical ordering + // of voters and possibly canonical ordering of governance actions in the subordinated map + { + // add to tx + TRACE("Adding voting procedure to tx hash"); + voter_t voter; + _setVoter(&voter, &BODY_CTX->stageData.votingProcedure.voter); + txHashBuilder_addVotingProcedure( + &BODY_CTX->txHashBuilder, + &voter, + &BODY_CTX->stageData.votingProcedure.govActionId, + &BODY_CTX->stageData.votingProcedure.votingProcedure + ); + } + + { + // select UI steps + switch (policy) { +# define CASE(POLICY, UI_STEP) case POLICY: {ctx->ui_step=UI_STEP; break;} + CASE(POLICY_SHOW_BEFORE_RESPONSE, HANDLE_VOTING_PROCEDURE_STEP_INTRO); + CASE(POLICY_ALLOW_WITHOUT_PROMPT, HANDLE_VOTING_PROCEDURE_STEP_RESPOND); +# undef CASE + default: + THROW(ERR_NOT_IMPLEMENTED); + } + } + signTx_handleVotingProcedure_ui_runStep(); +} + + +// ============================== TREASURY ============================== + +__noinline_due_to_stack__ +static void signTx_handleTreasuryAPDU(uint8_t p2, const uint8_t* wireDataBuffer, size_t wireDataSize) +{ + { + // sanity checks + CHECK_STAGE(SIGN_STAGE_BODY_TREASURY); + ASSERT(ctx->includeTreasury == true); + + VALIDATE(p2 == P2_UNUSED, ERR_INVALID_REQUEST_PARAMETERS); + } + { + // parse data + TRACE_BUFFER(wireDataBuffer, wireDataSize); + + VALIDATE(wireDataSize == 8, ERR_INVALID_DATA); + BODY_CTX->stageData.treasury = u8be_read(wireDataBuffer); + BODY_CTX->treasuryReceived = true; + } + + security_policy_t policy = policyForSignTxTreasury(ctx->commonTxData.txSigningMode, BODY_CTX->stageData.treasury); + TRACE("Policy: %d", (int) policy); + ENSURE_NOT_DENIED(policy); + + { + // add to tx + TRACE("Adding treasury to tx hash"); + txHashBuilder_addTreasury(&BODY_CTX->txHashBuilder, BODY_CTX->stageData.treasury); + } + + { + // select UI steps + switch (policy) { +#define CASE(POLICY, UI_STEP) case POLICY: {ctx->ui_step=UI_STEP; break;} + CASE(POLICY_SHOW_BEFORE_RESPONSE, HANDLE_TREASURY_STEP_DISPLAY); + CASE(POLICY_ALLOW_WITHOUT_PROMPT, HANDLE_TREASURY_STEP_RESPOND); +#undef CASE + default: + THROW(ERR_NOT_IMPLEMENTED); + } + } + + signTx_handleTreasury_ui_runStep(); +} + + +// ============================== DONATION ============================== + +__noinline_due_to_stack__ +static void signTx_handleDonationAPDU(uint8_t p2, const uint8_t* wireDataBuffer, size_t wireDataSize) +{ + { + // sanity checks + CHECK_STAGE(SIGN_STAGE_BODY_DONATION); + ASSERT(ctx->includeDonation == true); + + VALIDATE(p2 == P2_UNUSED, ERR_INVALID_REQUEST_PARAMETERS); + } + { + // parse data + TRACE_BUFFER(wireDataBuffer, wireDataSize); + + VALIDATE(wireDataSize == 8, ERR_INVALID_DATA); + BODY_CTX->stageData.donation = u8be_read(wireDataBuffer); + VALIDATE(BODY_CTX->stageData.donation > 0, ERR_INVALID_DATA); + BODY_CTX->donationReceived = true; + } + + security_policy_t policy = policyForSignTxDonation(ctx->commonTxData.txSigningMode, BODY_CTX->stageData.donation); + TRACE("Policy: %d", (int) policy); + ENSURE_NOT_DENIED(policy); + + { + // add to tx + TRACE("Adding donation to tx hash"); + txHashBuilder_addDonation(&BODY_CTX->txHashBuilder, BODY_CTX->stageData.donation); + } + + { + // select UI steps + switch (policy) { +#define CASE(POLICY, UI_STEP) case POLICY: {ctx->ui_step=UI_STEP; break;} + CASE(POLICY_SHOW_BEFORE_RESPONSE, HANDLE_DONATION_STEP_DISPLAY); + CASE(POLICY_ALLOW_WITHOUT_PROMPT, HANDLE_DONATION_STEP_RESPOND); +#undef CASE + default: + THROW(ERR_NOT_IMPLEMENTED); + } + } + + signTx_handleDonation_ui_runStep(); +} + + // ============================== CONFIRM ============================== static bool _shouldDisplayTxId(sign_tx_signingmode_t signingMode) @@ -1737,7 +2393,6 @@ static void signTx_handleConfirmAPDU(uint8_t p2, const uint8_t* wireDataBuffer M CHECK_STAGE(SIGN_STAGE_CONFIRM); VALIDATE(p2 == P2_UNUSED, ERR_INVALID_REQUEST_PARAMETERS); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } { @@ -1785,11 +2440,10 @@ static void signTx_handleWitnessAPDU(uint8_t p2, const uint8_t* wireDataBuffer, { // sanity checks CHECK_STAGE(SIGN_STAGE_WITNESSES); - VALIDATE(p2 == P2_UNUSED, ERR_INVALID_REQUEST_PARAMETERS); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); - TRACE("Witness no. %d out of %d", WITNESS_CTX->currentWitness + 1, ctx->numWitnesses); ASSERT(WITNESS_CTX->currentWitness < ctx->numWitnesses); + + VALIDATE(p2 == P2_UNUSED, ERR_INVALID_REQUEST_PARAMETERS); } explicit_bzero(&WITNESS_CTX->stageData.witness, SIZEOF(WITNESS_CTX->stageData.witness)); @@ -1877,7 +2531,10 @@ static subhandler_fn_t* lookup_subhandler(uint8_t p1) CASE(0x0e, signTx_handleRequiredSignerAPDU); CASE(0x12, signTx_handleCollateralOutputAPDU); CASE(0x10, signTx_handleTotalCollateralAPDU); - CASE(0x11, signTx_handleReferenceInputsAPDU); + CASE(0x11, signTx_handleReferenceInputAPDU); + CASE(0x13, signTx_handleVotingProcedureAPDU); + CASE(0x15, signTx_handleTreasuryAPDU); + CASE(0x16, signTx_handleDonationAPDU); CASE(0x0a, signTx_handleConfirmAPDU); CASE(0x0f, signTx_handleWitnessAPDU); DEFAULT(NULL) @@ -1949,9 +2606,8 @@ ins_sign_tx_aux_data_context_t* accessAuxDataContext() return &(ctx->txPartCtx.aux_data_ctx); default: - #ifndef DEVEL + PRINTF("accessAuxDataContext() bug\n"); ASSERT(false); - #endif THROW(ERR_ASSERT); } } @@ -1982,13 +2638,15 @@ ins_sign_tx_body_context_t* accessBodyContext() case SIGN_STAGE_BODY_COLLATERAL_OUTPUT_SUBMACHINE: case SIGN_STAGE_BODY_TOTAL_COLLATERAL: case SIGN_STAGE_BODY_REFERENCE_INPUTS: + case SIGN_STAGE_BODY_VOTING_PROCEDURES: + case SIGN_STAGE_BODY_TREASURY: + case SIGN_STAGE_BODY_DONATION: case SIGN_STAGE_CONFIRM: return &(ctx->txPartCtx.body_ctx); default: - #ifndef DEVEL + PRINTF("accessBodyContext() bug\n"); ASSERT(false); - #endif THROW(ERR_ASSERT); } } @@ -2001,9 +2659,8 @@ ins_sign_tx_witness_context_t* accessWitnessContext() return &(ctx->txPartCtx.witnesses_ctx); default: - #ifndef DEVEL + PRINTF("accessWitnessContext() bug\n"); ASSERT(false); - #endif THROW(ERR_ASSERT); } } diff --git a/src/signTx.h b/src/signTx.h index 9bdb9929..43fb07ac 100644 --- a/src/signTx.h +++ b/src/signTx.h @@ -49,8 +49,11 @@ typedef enum { SIGN_STAGE_BODY_COLLATERAL_OUTPUT_SUBMACHINE = 41, SIGN_STAGE_BODY_TOTAL_COLLATERAL = 42, SIGN_STAGE_BODY_REFERENCE_INPUTS = 43, - SIGN_STAGE_CONFIRM = 44, - SIGN_STAGE_WITNESSES = 45, + SIGN_STAGE_BODY_VOTING_PROCEDURES = 44, + SIGN_STAGE_BODY_TREASURY = 45, + SIGN_STAGE_BODY_DONATION = 46, + SIGN_STAGE_CONFIRM = 47, + SIGN_STAGE_WITNESSES = 48, } sign_tx_stage_t; enum { @@ -61,7 +64,7 @@ enum { SIGN_MAX_COLLATERAL_INPUTS = UINT16_MAX, SIGN_MAX_REQUIRED_SIGNERS = UINT16_MAX, SIGN_MAX_REFERENCE_INPUTS = UINT16_MAX, - SIGN_MAX_WITNESSES = SIGN_MAX_INPUTS + SIGN_MAX_OUTPUTS + SIGN_MAX_CERTIFICATES + SIGN_MAX_REWARD_WITHDRAWALS, + SIGN_MAX_VOTING_PROCEDURES = 1, // we only support a single vote per tx }; #define UI_INPUT_LABEL_SIZE 20 @@ -99,19 +102,42 @@ typedef struct { }; } ext_credential_t; +// DReps are extended to allow key derivation paths +typedef enum { + EXT_DREP_KEY_HASH = 0, + EXT_DREP_KEY_PATH = 0 + 100, + EXT_DREP_SCRIPT_HASH = 1, + EXT_DREP_ABSTAIN = 2, + EXT_DREP_NO_CONFIDENCE = 3, +} ext_drep_type_t; + +typedef struct { + ext_drep_type_t type; + union { + bip44_path_t keyPath; + uint8_t keyHash[ADDRESS_KEY_HASH_LENGTH]; + uint8_t scriptHash[SCRIPT_HASH_LENGTH]; + }; +} ext_drep_t; + typedef struct { certificate_type_t type; union { ext_credential_t stakeCredential; - // TODO ext_credential_t committeeColdCredential; + ext_credential_t committeeColdCredential; + ext_credential_t dRepCredential; }; union { ext_credential_t poolCredential; - // TODO ext_credential_t drepCredential; - // TODO ext_credential_t committeeHotCredential; + ext_credential_t committeeHotCredential; + ext_drep_t drep; + anchor_t anchor; + }; + union { + uint64_t epoch; // in pool retirement + uint64_t deposit; // not in pool retirement }; - uint64_t epoch; } sign_tx_certificate_data_t; typedef struct { @@ -153,7 +179,37 @@ typedef struct { }; } sign_tx_required_signer_t; +// voters are extended to allow key derivation paths +typedef enum { + EXT_VOTER_COMMITTEE_HOT_KEY_HASH = 0, + EXT_VOTER_COMMITTEE_HOT_KEY_PATH = 0 + 100, + EXT_VOTER_COMMITTEE_HOT_SCRIPT_HASH = 1, + EXT_VOTER_DREP_KEY_HASH = 2, + EXT_VOTER_DREP_KEY_PATH = 2 + 100, + EXT_VOTER_DREP_SCRIPT_HASH = 3, + EXT_VOTER_STAKE_POOL_KEY_HASH = 4, + EXT_VOTER_STAKE_POOL_KEY_PATH = 4 + 100, +} ext_voter_type_t; + typedef struct { + ext_voter_type_t type; + union { + bip44_path_t keyPath; + uint8_t keyHash[ADDRESS_KEY_HASH_LENGTH]; + uint8_t scriptHash[SCRIPT_HASH_LENGTH]; + }; +} ext_voter_t; + +typedef struct { + ext_voter_t voter; + gov_action_id_t govActionId; + voting_procedure_t votingProcedure; +} sign_tx_voting_procedure_t; + + +typedef struct { + tx_hash_builder_t txHashBuilder; + uint16_t currentInput; uint16_t currentOutput; uint16_t currentCertificate; @@ -161,6 +217,7 @@ typedef struct { uint16_t currentCollateral; uint16_t currentRequiredSigner; uint16_t currentReferenceInput; + uint16_t currentVotingProcedure; bool feeReceived; bool ttlReceived; @@ -169,9 +226,8 @@ typedef struct { bool scriptDataHashReceived; bool collateralOutputReceived; bool totalCollateralReceived; - - // TODO move these to commonTxData? - tx_hash_builder_t txHashBuilder; + bool treasuryReceived; + bool donationReceived; // this holds data valid only through the processing of a single APDU union { @@ -184,6 +240,9 @@ typedef struct { uint8_t scriptDataHash[SCRIPT_DATA_HASH_LENGTH]; sign_tx_required_signer_t requiredSigner; uint64_t totalCollateral; + sign_tx_voting_procedure_t votingProcedure; + uint64_t treasury; + uint64_t donation; } stageData; union { @@ -225,6 +284,9 @@ typedef struct { bool includeTotalCollateral; uint64_t totalCollateral; uint16_t numReferenceInputs; + uint16_t numVotingProcedures; + bool includeTreasury; + bool includeDonation; uint16_t numWitnesses; diff --git a/src/signTxCVoteRegistration.c b/src/signTxCVoteRegistration.c index cb06ffb5..12112433 100644 --- a/src/signTxCVoteRegistration.c +++ b/src/signTxCVoteRegistration.c @@ -118,8 +118,6 @@ static void signTxCVoteRegistration_handleInitAPDU(const uint8_t* wireDataBuffer { { CHECK_STATE(STATE_CVOTE_REGISTRATION_INIT); - - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } cvote_registration_context_t* subctx = accessSubContext(); { @@ -229,8 +227,6 @@ static void signTxCVoteRegistration_handleVoteKeyAPDU(const uint8_t* wireDataBuf { { CHECK_STATE(STATE_CVOTE_REGISTRATION_VOTE_KEY); - - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } cvote_registration_context_t* subctx = accessSubContext(); { @@ -379,7 +375,6 @@ static void signTxCVoteRegistration_handleStakingKeyAPDU(const uint8_t* wireData { // sanity checks CHECK_STATE(STATE_CVOTE_REGISTRATION_STAKING_KEY); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } cvote_registration_context_t* subctx = accessSubContext(); { @@ -472,8 +467,6 @@ static void signTxCVoteRegistration_handlePaymentAddressAPDU(const uint8_t* wire { // safety checks CHECK_STATE(STATE_CVOTE_REGISTRATION_PAYMENT_ADDRESS); - - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } cvote_registration_context_t* subctx = accessSubContext(); { @@ -535,8 +528,6 @@ static void signTxCVoteRegistration_handleNonceAPDU(const uint8_t* wireDataBuffe { // sanity checks CHECK_STATE(STATE_CVOTE_REGISTRATION_NONCE); - - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } cvote_registration_context_t* subctx = accessSubContext(); { @@ -583,8 +574,6 @@ static void signTxCVoteRegistration_handleVotingPurposeAPDU(const uint8_t* wireD { { CHECK_STATE(STATE_CVOTE_REGISTRATION_VOTING_PURPOSE); - - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } cvote_registration_context_t* subctx = accessSubContext(); { @@ -652,10 +641,8 @@ __noinline_due_to_stack__ static void signTxCVoteRegistration_handleConfirmAPDU(const uint8_t* wireDataBuffer MARK_UNUSED, size_t wireDataSize) { { - //sanity checks + // sanity checks CHECK_STATE(STATE_CVOTE_REGISTRATION_CONFIRM); - - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } cvote_registration_context_t* subctx = accessSubContext(); { diff --git a/src/signTxMint.c b/src/signTxMint.c index 88ca8bc5..5d6ebfe5 100644 --- a/src/signTxMint.c +++ b/src/signTxMint.c @@ -35,7 +35,6 @@ static void signTxMint_handleTopLevelDataAPDU(const uint8_t* wireDataBuffer, siz { // safety checks CHECK_STATE(STATE_MINT_TOP_LEVEL_DATA); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } TRACE_BUFFER(wireDataBuffer, wireDataSize); mint_context_t* subctx = accessSubcontext(); @@ -67,8 +66,6 @@ static void signTxMint_handleAssetGroupAPDU(const uint8_t* wireDataBuffer, size_ { // sanity checks CHECK_STATE(STATE_MINT_ASSET_GROUP); - - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } mint_context_t* subctx = accessSubcontext(); { @@ -122,8 +119,6 @@ static void signTxMint_handleTokenAPDU(const uint8_t* wireDataBuffer, size_t wir { // sanity checks CHECK_STATE(STATE_MINT_TOKEN); - - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } mint_context_t* subctx = accessSubcontext(); { @@ -185,10 +180,8 @@ static void signTxMint_handleTokenAPDU(const uint8_t* wireDataBuffer, size_t wir static void signTxMint_handleConfirmAPDU(const uint8_t* wireDataBuffer MARK_UNUSED, size_t wireDataSize) { { - //sanity checks + // sanity checks CHECK_STATE(STATE_MINT_CONFIRM); - - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } { diff --git a/src/signTxOutput.c b/src/signTxOutput.c index 57954284..a412c65d 100644 --- a/src/signTxOutput.c +++ b/src/signTxOutput.c @@ -307,8 +307,6 @@ static void parseTopLevelData(const uint8_t* wireDataBuffer, size_t wireDataSize { // safety checks CHECK_STATE(STATE_OUTPUT_TOP_LEVEL_DATA); - - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } output_context_t* subctx = accessSubcontext(); @@ -398,7 +396,6 @@ static void handleCollateralOutput_addressBytes() .includeRefScript = subctx->includeRefScript, }; - // TODO maybe restrict to specific address types? we don't support datum in coll ret outputs security_policy_t policy = policyForSignTxCollateralOutputAddressBytes( &output, commonTxData->txSigningMode, @@ -545,8 +542,6 @@ static void handleAssetGroupAPDU(const uint8_t* wireDataBuffer, size_t wireDataS { // sanity checks CHECK_STATE(STATE_OUTPUT_ASSET_GROUP); - - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } output_context_t* subctx = accessSubcontext(); { @@ -621,8 +616,6 @@ static void handleTokenAPDU(const uint8_t* wireDataBuffer, size_t wireDataSize) { // sanity checks CHECK_STATE(STATE_OUTPUT_TOKEN); - - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } output_context_t* subctx = accessSubcontext(); { @@ -797,7 +790,6 @@ static void handleDatumAPDU(const uint8_t* wireDataBuffer, size_t wireDataSize) { // sanity checks CHECK_STATE(STATE_OUTPUT_DATUM); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } output_context_t* subctx = accessSubcontext(); { @@ -829,7 +821,6 @@ static void handleDatumChunkAPDU(const uint8_t* wireDataBuffer, size_t wireDataS { // sanity checks CHECK_STATE(STATE_OUTPUT_DATUM_INLINE_CHUNKS); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } output_context_t* subctx = accessSubcontext(); { @@ -871,7 +862,6 @@ static void handleRefScriptAPDU(const uint8_t* wireDataBuffer, size_t wireDataSi { // sanity checks CHECK_STATE(STATE_OUTPUT_REFERENCE_SCRIPT); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } output_context_t* subctx = accessSubcontext(); { @@ -935,7 +925,6 @@ static void handleRefScriptChunkAPDU(const uint8_t* wireDataBuffer, size_t wireD { // sanity checks CHECK_STATE(STATE_OUTPUT_REFERENCE_SCRIPT_CHUNKS); - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } output_context_t* subctx = accessSubcontext(); { diff --git a/src/signTxPoolRegistration.c b/src/signTxPoolRegistration.c index d66f6f81..aed6c5d0 100644 --- a/src/signTxPoolRegistration.c +++ b/src/signTxPoolRegistration.c @@ -75,8 +75,6 @@ static void signTxPoolRegistration_handleInitAPDU(const uint8_t* wireDataBuffer, { // sanity checks CHECK_STATE(STAKE_POOL_REGISTRATION_INIT); - - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } pool_registration_context_t* subctx = accessSubcontext(); { @@ -185,8 +183,6 @@ static void signTxPoolRegistration_handlePoolKeyAPDU(const uint8_t* wireDataBuff { // sanity checks CHECK_STATE(STAKE_POOL_REGISTRATION_POOL_KEY); - - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } { // parse data @@ -258,8 +254,6 @@ static void signTxPoolRegistration_handleVrfKeyAPDU(const uint8_t* wireDataBuffe { // sanity checks CHECK_STATE(STAKE_POOL_REGISTRATION_VRF_KEY); - - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } pool_registration_context_t* subctx = accessSubcontext(); { @@ -313,8 +307,6 @@ static void signTxPoolRegistration_handlePoolFinancialsAPDU(const uint8_t* wireD { // sanity checks CHECK_STATE(STAKE_POOL_REGISTRATION_FINANCIALS); - - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } pool_registration_context_t* subctx = accessSubcontext(); { @@ -407,8 +399,6 @@ static void signTxPoolRegistration_handleRewardAccountAPDU(const uint8_t* wireDa { // sanity checks CHECK_STATE(STAKE_POOL_REGISTRATION_REWARD_ACCOUNT); - - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } { // parse data @@ -494,8 +484,6 @@ static void signTxPoolRegistration_handleOwnerAPDU(const uint8_t* wireDataBuffer { // sanity checks CHECK_STATE(STAKE_POOL_REGISTRATION_OWNERS); - - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } pool_registration_context_t* subctx = accessSubcontext(); @@ -638,8 +626,6 @@ static void signTxPoolRegistration_handleRelayAPDU(const uint8_t* wireDataBuffer { // sanity checks CHECK_STATE(STAKE_POOL_REGISTRATION_RELAYS); - - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } pool_relay_t* relay = &accessSubcontext()->stateData.relay; @@ -776,8 +762,6 @@ static void signTxPoolRegistration_handlePoolMetadataAPDU(const uint8_t* wireDat { // sanity checks CHECK_STATE(STAKE_POOL_REGISTRATION_METADATA); - - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } pool_registration_context_t* subctx = accessSubcontext(); @@ -857,8 +841,6 @@ static void signTxPoolRegistration_handleConfirmAPDU(const uint8_t* wireDataBuff { //sanity checks CHECK_STATE(STAKE_POOL_REGISTRATION_CONFIRM); - - ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); } { diff --git a/src/signTxPoolRegistration.h b/src/signTxPoolRegistration.h index 85504acb..24417ade 100644 --- a/src/signTxPoolRegistration.h +++ b/src/signTxPoolRegistration.h @@ -11,7 +11,7 @@ #define POOL_MAX_RELAYS 1000 // SIGN_STAGE_BODY_CERTIFICATES = 28 -// CERTIFICATE_TYPE_STAKE_POOL_REGISTRATION = 3 +// CERTIFICATE_STAKE_POOL_REGISTRATION = 3 typedef enum { STAKE_POOL_REGISTRATION_INIT = 2830, STAKE_POOL_REGISTRATION_POOL_KEY = 2831, diff --git a/src/signTxPoolRegistration_ui.c b/src/signTxPoolRegistration_ui.c index 855d840b..433555d5 100644 --- a/src/signTxPoolRegistration_ui.c +++ b/src/signTxPoolRegistration_ui.c @@ -686,7 +686,7 @@ void handleMetadata_ui_runStep() UI_STEP(HANDLE_METADATA_STEP_DISPLAY_HASH) { char metadataHashHex[1 + 2 * POOL_METADATA_HASH_LENGTH] = {0}; explicit_bzero(metadataHashHex, SIZEOF(metadataHashHex)); - size_t len = str_formatMetadata( + size_t len = encode_hex( md->hash, SIZEOF(md->hash), metadataHashHex, SIZEOF(metadataHashHex) ); diff --git a/src/signTx_ui.c b/src/signTx_ui.c index 74be17dd..c475ec92 100644 --- a/src/signTx_ui.c +++ b/src/signTx_ui.c @@ -353,22 +353,213 @@ void signTx_handleTtl_ui_runStep() #ifdef HAVE_NBGL static void signTx_handleCertificate_ui_delegation_cb(void) { + sign_tx_certificate_data_t* cert = &BODY_CTX->stageData.certificate; + char encodedStr[BECH32_STRING_SIZE_MAX] = {0}; - ui_getBech32Screen(encodedStr, SIZEOF(encodedStr), "pool", BODY_CTX->stageData.certificate.poolCredential.keyHash, SIZEOF(BODY_CTX->stageData.certificate.poolCredential.keyHash)); - fill_and_display_if_required("Pool", encodedStr, signTx_handleCertificate_ui_runStep, respond_with_user_reject); + ASSERT(cert->poolCredential.type == EXT_CREDENTIAL_KEY_HASH); + ui_getBech32Screen( + encodedStr, SIZEOF(encodedStr), + "pool", + cert->poolCredential.keyHash, SIZEOF(cert->poolCredential.keyHash) + ); + fill_and_display_if_required("Pool", encodedStr, signTx_handleCertificateStaking_ui_runStep, respond_with_user_reject); } #endif -void signTx_handleCertificate_ui_runStep() +static void _displayKeyPath( + ui_callback_fn_t* callback, + bip44_path_t* path, + const char* label +) +{ + #ifdef HAVE_BAGL + ui_displayPathScreen( + label, + path, + callback + ); + #elif defined(HAVE_NBGL) + { + char pathStr[BIP44_PATH_STRING_SIZE_MAX + 1] = {0}; + ui_getPathScreen(pathStr, SIZEOF(pathStr), path); + fill_and_display_if_required(label, pathStr, callback, respond_with_user_reject); + } + #endif // HAVE_BAGL +} + +static void _displayKeyHash( + ui_callback_fn_t* callback, + uint8_t keyHash[static ADDRESS_KEY_HASH_LENGTH], + const char* label, + const char* bech32Prefix +) +{ + #ifdef HAVE_BAGL + ui_displayBech32Screen( + label, + bech32Prefix, + keyHash, + ADDRESS_KEY_HASH_LENGTH, + callback + ); + #elif defined(HAVE_NBGL) + { + char encodedStr[BECH32_STRING_SIZE_MAX] = {0}; + ui_getBech32Screen(encodedStr, SIZEOF(encodedStr), bech32Prefix, keyHash, ADDRESS_KEY_HASH_LENGTH); + fill_and_display_if_required(label, encodedStr, callback, respond_with_user_reject); + } + #endif // HAVE_BAGL +} + +static void _displayScriptHash( + ui_callback_fn_t* callback, + uint8_t scriptHash[static SCRIPT_HASH_LENGTH], + const char* label, + const char* bech32Prefix +) +{ + #ifdef HAVE_BAGL + ui_displayBech32Screen( + label, + bech32Prefix, + scriptHash, + SCRIPT_HASH_LENGTH, + callback + ); + #elif defined(HAVE_NBGL) + { + char encodedStr[BECH32_STRING_SIZE_MAX] = {0}; + ui_getBech32Screen(encodedStr, SIZEOF(encodedStr), bech32Prefix, scriptHash, SCRIPT_HASH_LENGTH); + fill_and_display_if_required(label, encodedStr, callback, respond_with_user_reject); + } + #endif // HAVE_BAGL +} + +static void _displayCredential( + ui_callback_fn_t* callback, + ext_credential_t* credential, + const char* keyPathLabel, + const char* keyHashLabel, + const char* keyHashPrefix, + const char* scriptHashLabel, + const char* scriptHashPrefix +) +{ + switch (credential->type) { + case EXT_CREDENTIAL_KEY_PATH: + _displayKeyPath(callback, &credential->keyPath, keyPathLabel); + break; + case EXT_CREDENTIAL_KEY_HASH: + _displayKeyHash(callback, credential->keyHash, keyHashLabel, keyHashPrefix); + break; + case EXT_CREDENTIAL_SCRIPT_HASH: + _displayScriptHash(callback, credential->scriptHash, scriptHashLabel, scriptHashPrefix); + break; + default: + ASSERT(false); + break; + } +} + +static void _displayDeposit( + ui_callback_fn_t* callback, + uint64_t deposit +) +{ + #ifdef HAVE_BAGL + ui_displayAdaAmountScreen( + "Deposit", + deposit, + callback + ); + #elif defined(HAVE_NBGL) + char adaAmountStr[50] = {0}; + ui_getAdaAmountScreen(adaAmountStr, SIZEOF(adaAmountStr), deposit); + fill_and_display_if_required("Deposit", adaAmountStr, callback, respond_with_user_reject); + #endif // HAVE_BAGL +} + +static void _displayAnchorNull(ui_callback_fn_t* callback) +{ + #ifdef HAVE_BAGL + ui_displayPaginatedText( + "Anchor", + "null", + callback + ); + #elif defined(HAVE_NBGL) + fill_and_display_if_required( + "Anchor", + "null", + callback, + respond_with_user_reject + ); + #endif // HAVE_BAGL +} + +static void _displayAnchorUrl(ui_callback_fn_t* callback, anchor_t* anchor) +{ + char urlStr[1 + ANCHOR_URL_LENGTH_MAX] = {0}; + explicit_bzero(urlStr, SIZEOF(urlStr)); + ASSERT(anchor->urlLength <= ANCHOR_URL_LENGTH_MAX); + memmove(urlStr, anchor->url, anchor->urlLength); + urlStr[anchor->urlLength] = '\0'; + ASSERT(strlen(urlStr) == anchor->urlLength); + + #ifdef HAVE_BAGL + ui_displayPaginatedText( + "Anchor url", + urlStr, + callback + ); + #elif defined(HAVE_NBGL) + fill_and_display_if_required( + "Anchor url", + urlStr, + callback, + respond_with_user_reject + ); + #endif // HAVE_BAGL +} + +static void _displayAnchorHash(ui_callback_fn_t* callback, anchor_t* anchor) +{ + char hex[1 + 2 * ANCHOR_HASH_LENGTH] = {0}; + explicit_bzero(hex, SIZEOF(hex)); + size_t len = encode_hex( + anchor->hash, SIZEOF(anchor->hash), + hex, SIZEOF(hex) + ); + ASSERT(len + 1 == SIZEOF(hex)); + + #ifdef HAVE_BAGL + ui_displayPaginatedText( + "Anchor data hash", + hex, + callback + ); + #elif defined(HAVE_NBGL) + fill_and_display_if_required( + "Anchor data hash", + hex, + callback, + respond_with_user_reject + ); + #endif // HAVE_BAGL +} + +void signTx_handleCertificateStaking_ui_runStep() { TRACE("UI step %d", ctx->ui_step); - ui_callback_fn_t* this_fn = signTx_handleCertificate_ui_runStep; + ui_callback_fn_t* this_fn = signTx_handleCertificateStaking_ui_runStep; + sign_tx_certificate_data_t* cert = &BODY_CTX->stageData.certificate; UI_STEP_BEGIN(ctx->ui_step, this_fn); - UI_STEP(HANDLE_CERTIFICATE_STEP_DISPLAY_OPERATION) { - switch (BODY_CTX->stageData.certificate.type) { - case CERTIFICATE_TYPE_STAKE_REGISTRATION: + UI_STEP(HANDLE_CERTIFICATE_STAKING_STEP_DISPLAY_OPERATION) { + switch (cert->type) { + case CERTIFICATE_STAKE_REGISTRATION: + case CERTIFICATE_STAKE_REGISTRATION_CONWAY: #ifdef HAVE_BAGL ui_displayPaginatedText( "Register", @@ -381,7 +572,8 @@ void signTx_handleCertificate_ui_runStep() #endif // HAVE_BAGL break; - case CERTIFICATE_TYPE_STAKE_DEREGISTRATION: + case CERTIFICATE_STAKE_DEREGISTRATION: + case CERTIFICATE_STAKE_DEREGISTRATION_CONWAY: #ifdef HAVE_BAGL ui_displayPaginatedText( "Deregister", @@ -394,12 +586,13 @@ void signTx_handleCertificate_ui_runStep() #endif // HAVE_BAGL break; - case CERTIFICATE_TYPE_STAKE_DELEGATION: + case CERTIFICATE_STAKE_DELEGATION: #ifdef HAVE_BAGL + ASSERT(cert->poolCredential.type == EXT_CREDENTIAL_KEY_HASH); ui_displayBech32Screen( "Delegate stake", "to pool", - BODY_CTX->stageData.certificate.poolCredential.keyHash, SIZEOF(BODY_CTX->stageData.certificate.poolCredential.keyHash), + cert->poolCredential.keyHash, SIZEOF(cert->poolCredential.keyHash), this_fn ); #elif defined(HAVE_NBGL) @@ -409,74 +602,421 @@ void signTx_handleCertificate_ui_runStep() break; default: - // includes CERTIFICATE_TYPE_STAKE_POOL_REGISTRATION - // and CERTIFICATE_TYPE_STAKE_POOL_RETIREMENT + // includes CERTIFICATE_STAKE_POOL_REGISTRATION + // and CERTIFICATE_STAKE_POOL_RETIREMENT // which have separate UI; this handler must not be used ASSERT(false); } } - UI_STEP(HANDLE_CERTIFICATE_STEP_DISPLAY_STAKING_KEY) { - switch (BODY_CTX->stageData.certificate.stakeCredential.type) { - case EXT_CREDENTIAL_KEY_PATH: + UI_STEP(HANDLE_CERTIFICATE_STAKING_STEP_DISPLAY_STAKE_CRED) { + _displayCredential( + this_fn, + &cert->stakeCredential, + "Stake key", + "Stake key hash", + "stake_vkh", + "Stake script hash", + "script" + ); + } + UI_STEP(HANDLE_CERTIFICATE_STAKING_STEP_DISPLAY_DEPOSIT) { + switch (cert->type) { + case CERTIFICATE_STAKE_REGISTRATION: + case CERTIFICATE_STAKE_DEREGISTRATION: + case CERTIFICATE_STAKE_DELEGATION: + // no deposit in these + UI_STEP_JUMP(HANDLE_CERTIFICATE_STAKING_STEP_CONFIRM); + break; + + case CERTIFICATE_STAKE_REGISTRATION_CONWAY: + case CERTIFICATE_STAKE_DEREGISTRATION_CONWAY: + _displayDeposit(this_fn, cert->deposit); + break; + + default: + ASSERT(false); + } + } + UI_STEP(HANDLE_CERTIFICATE_STAKING_STEP_CONFIRM) { + char description[50] = {0}; + explicit_bzero(description, SIZEOF(description)); + + switch (cert->type) { + case CERTIFICATE_STAKE_REGISTRATION: + case CERTIFICATE_STAKE_REGISTRATION_CONWAY: + #ifdef HAVE_BAGL + snprintf(description, SIZEOF(description), "registration?"); + #elif defined(HAVE_NBGL) + display_confirmation("Confirm\nregistration", "", "REGISTRATION\nACCEPTED", "Registration\nrejected", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + break; + + case CERTIFICATE_STAKE_DEREGISTRATION: + case CERTIFICATE_STAKE_DEREGISTRATION_CONWAY: + #ifdef HAVE_BAGL + snprintf(description, SIZEOF(description), "deregistration?"); + #elif defined(HAVE_NBGL) + display_confirmation("Confirm\nderegistration", "", "DEREGISTRATION\nACCEPTED", "Deregistration\nrejected", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + break; + + case CERTIFICATE_STAKE_DELEGATION: #ifdef HAVE_BAGL - ui_displayPathScreen( - "Stake key", - &BODY_CTX->stageData.certificate.stakeCredential.keyPath, + snprintf(description, SIZEOF(description), "delegation?"); + #elif defined(HAVE_NBGL) + display_confirmation("Confirm\ndelegation", "", "DELEGATION\nACCEPTED", "Delegation\nrejected", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + break; + + default: + ASSERT(false); + } + // make sure all the information is displayed to the user + ASSERT(strlen(description) + 1 < SIZEOF(description)); + + #ifdef HAVE_BAGL + ui_displayPrompt( + "Confirm", + description, + this_fn, + respond_with_user_reject + ); + #elif defined(HAVE_NBGL) + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_CERTIFICATE_STAKING_STEP_RESPOND) { + respondSuccessEmptyMsg(); + + tx_advanceCertificatesStateIfAppropriate(); + } + UI_STEP_END(HANDLE_CERTIFICATE_STAKING_STEP_INVALID); +} + +void signTx_handleCertificateVoteDeleg_ui_runStep() +{ + TRACE("UI step %d", ctx->ui_step); + ui_callback_fn_t* this_fn = signTx_handleCertificateVoteDeleg_ui_runStep; + sign_tx_certificate_data_t* cert = &BODY_CTX->stageData.certificate; + + UI_STEP_BEGIN(ctx->ui_step, this_fn); + + UI_STEP(HANDLE_CERTIFICATE_VOTE_DELEGATION_STEP_DISPLAY_OPERATION) { + #ifdef HAVE_BAGL + ui_displayPaginatedText( + "Delegate", + "vote", + this_fn + ); + #elif defined(HAVE_NBGL) + set_light_confirmation(true); + display_prompt("Delegate\nvote", "", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_CERTIFICATE_VOTE_DELEGATION_STEP_DISPLAY_STAKE_CRED) { + _displayCredential( + this_fn, + &cert->stakeCredential, + "Stake key", + "Stake key hash", + "stake_vkh", + "Stake script hash", + "script" + ); + } + UI_STEP(HANDLE_CERTIFICATE_VOTE_DELEGATION_STEP_DISPLAY_DREP) { + switch (cert->drep.type) { + case EXT_DREP_KEY_PATH: + _displayKeyPath(this_fn, &cert->drep.keyPath, "DRep key"); + break; + case EXT_DREP_KEY_HASH: + _displayKeyHash(this_fn, cert->drep.keyHash, "DRep key hash", "drep"); + break; + case EXT_DREP_SCRIPT_HASH: + _displayScriptHash(this_fn, cert->drep.scriptHash, "DRep script hash", "drep"); + break; + case DREP_ALWAYS_ABSTAIN: + #ifdef HAVE_BAGL + ui_displayPaginatedText( + "Always", + "abstain", this_fn ); #elif defined(HAVE_NBGL) - { - char pathStr[BIP44_PATH_STRING_SIZE_MAX + 1] = {0}; - ui_getPathScreen(pathStr, SIZEOF(pathStr), &BODY_CTX->stageData.certificate.stakeCredential.keyPath); - fill_and_display_if_required("Stake key", pathStr, this_fn, respond_with_user_reject); - } + set_light_confirmation(true); + display_prompt("Always\nabstain", "", this_fn, respond_with_user_reject); #endif // HAVE_BAGL break; - case EXT_CREDENTIAL_KEY_HASH: + case DREP_ALWAYS_NO_CONFIDENCE: #ifdef HAVE_BAGL - ui_displayBech32Screen( - "Stake key hash", - "stake_vkh", - BODY_CTX->stageData.certificate.stakeCredential.keyHash, - SIZEOF(BODY_CTX->stageData.certificate.stakeCredential.keyHash), + ui_displayPaginatedText( + "Always", + "no confidence", this_fn ); #elif defined(HAVE_NBGL) - { - char encodedStr[BECH32_STRING_SIZE_MAX] = {0}; - ui_getBech32Screen(encodedStr, SIZEOF(encodedStr), "stake_vkh", BODY_CTX->stageData.certificate.stakeCredential.keyHash, SIZEOF(BODY_CTX->stageData.certificate.stakeCredential.keyHash)); - fill_and_display_if_required("Stake key hash", encodedStr, this_fn, respond_with_user_reject); - } + set_light_confirmation(true); + display_prompt("Always\nno confidence", "", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + break; + default: + ASSERT(false); + break; + } + } + UI_STEP(HANDLE_CERTIFICATE_VOTE_DELEGATION_STEP_CONFIRM) { + #ifdef HAVE_BAGL + ui_displayPrompt( + "Confirm vote", + "delegation", + this_fn, + respond_with_user_reject + ); + #elif defined(HAVE_NBGL) + display_confirmation("Confirm vote\ndelegation", "", "DELEGATION\nACCEPTED", "Delegation\nrejected", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_CERTIFICATE_VOTE_DELEGATION_STEP_RESPOND) { + respondSuccessEmptyMsg(); + + tx_advanceCertificatesStateIfAppropriate(); + } + UI_STEP_END(HANDLE_CERTIFICATE_VOTE_DELEGATION_STEP_INVALID); +} + +void signTx_handleCertificateCommitteeAuth_ui_runStep() +{ + TRACE("UI step %d", ctx->ui_step); + ui_callback_fn_t* this_fn = signTx_handleCertificateCommitteeAuth_ui_runStep; + sign_tx_certificate_data_t* cert = &BODY_CTX->stageData.certificate; + + UI_STEP_BEGIN(ctx->ui_step, this_fn); + + UI_STEP(HANDLE_CERTIFICATE_COMM_AUTH_STEP_DISPLAY_OPERATION) { + #ifdef HAVE_BAGL + ui_displayPaginatedText( + "Authorize", + "committee", + this_fn + ); + #elif defined(HAVE_NBGL) + set_light_confirmation(true); + display_prompt("Authorize committee", "", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_CERTIFICATE_COMM_AUTH_STEP_DISPLAY_COLD_CRED) { + _displayCredential( + this_fn, + &cert->committeeColdCredential, + "Cmte. cold key", + "Cmte. cold key hash", + "cc_cold", + "Cmte. cold script", + "cc_cold" + ); + } + UI_STEP(HANDLE_CERTIFICATE_COMM_AUTH_STEP_DISPLAY_HOT_CRED) { + _displayCredential( + this_fn, + &cert->committeeHotCredential, + "Cmte. hot key", + "Cmte. hot key hash", + "cc_hot", + "Cmte. hot script", + "cc_hot" + ); + } + UI_STEP(HANDLE_CERTIFICATE_COMM_AUTH_STEP_CONFIRM) { + #ifdef HAVE_BAGL + ui_displayPrompt( + "Confirm", + "authorization?", + this_fn, + respond_with_user_reject + ); + #elif defined(HAVE_NBGL) + display_confirmation("Confirm\nauthorization", "", "AUTHORIZATION\nACCEPTED", "Authorization\nrejected", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_CERTIFICATE_COMM_AUTH_STEP_RESPOND) { + respondSuccessEmptyMsg(); + + tx_advanceCertificatesStateIfAppropriate(); + } + UI_STEP_END(HANDLE_CERTIFICATE_COMM_AUTH_STEP_INVALID); +} + +void signTx_handleCertificateCommitteeResign_ui_runStep() +{ + TRACE("UI step %d", ctx->ui_step); + ui_callback_fn_t* this_fn = signTx_handleCertificateCommitteeResign_ui_runStep; + sign_tx_certificate_data_t* cert = &BODY_CTX->stageData.certificate; + + UI_STEP_BEGIN(ctx->ui_step, this_fn); + + UI_STEP(HANDLE_CERTIFICATE_COMM_RESIGN_STEP_DISPLAY_OPERATION) { + #ifdef HAVE_BAGL + ui_displayPaginatedText( + "Resign from", + "committee", + this_fn + ); + #elif defined(HAVE_NBGL) + set_light_confirmation(true); + display_prompt("Resign from\ncommittee", "", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_CERTIFICATE_COMM_RESIGN_STEP_DISPLAY_COLD_CRED) { + _displayCredential( + this_fn, + &cert->committeeColdCredential, + "Cmte. cold key", + "Cmte. cold key hash", + "cc_cold", + "Cmte. cold script", + "cc_cold" + ); + } + UI_STEP(HANDLE_CERTIFICATE_COMM_RESIGN_STEP_DISPLAY_ANCHOR_NULL) { + if (cert->anchor.isIncluded) { + UI_STEP_JUMP(HANDLE_CERTIFICATE_COMM_RESIGN_STEP_DISPLAY_ANCHOR_URL); + } + _displayAnchorNull(this_fn); + } + UI_STEP(HANDLE_CERTIFICATE_COMM_RESIGN_STEP_DISPLAY_ANCHOR_URL) { + if (!cert->anchor.isIncluded) { + UI_STEP_JUMP(HANDLE_CERTIFICATE_COMM_RESIGN_STEP_CONFIRM); + } + _displayAnchorUrl(this_fn, &cert->anchor); + } + UI_STEP(HANDLE_CERTIFICATE_COMM_RESIGN_STEP_DISPLAY_ANCHOR_HASH) { + _displayAnchorHash(this_fn, &cert->anchor); + } + UI_STEP(HANDLE_CERTIFICATE_COMM_RESIGN_STEP_CONFIRM) { + #ifdef HAVE_BAGL + ui_displayPrompt( + "Confirm", + "resignation", + this_fn, + respond_with_user_reject + ); + #elif defined(HAVE_NBGL) + display_confirmation("Confirm\nresignation", "", "RESIGNATION\nACCEPTED", "Resignation\nrejected", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_CERTIFICATE_COMM_RESIGN_STEP_RESPOND) { + respondSuccessEmptyMsg(); + + tx_advanceCertificatesStateIfAppropriate(); + } + UI_STEP_END(HANDLE_CERTIFICATE_COMM_RESIGN_STEP_INVALID); +} + +void signTx_handleCertificateDRep_ui_runStep() +{ + TRACE("UI step %d", ctx->ui_step); + ui_callback_fn_t* this_fn = signTx_handleCertificateDRep_ui_runStep; + sign_tx_certificate_data_t* cert = &BODY_CTX->stageData.certificate; + + UI_STEP_BEGIN(ctx->ui_step, this_fn); + + UI_STEP(HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_OPERATION) { + switch (cert->type) { + case CERTIFICATE_DREP_REGISTRATION: + #ifdef HAVE_BAGL + ui_displayPaginatedText( + "Register", + "DRep", + this_fn + ); + #elif defined(HAVE_NBGL) + set_light_confirmation(true); + display_prompt("Register\nDRep", "", this_fn, respond_with_user_reject); #endif // HAVE_BAGL break; - case EXT_CREDENTIAL_SCRIPT_HASH: + + case CERTIFICATE_DREP_DEREGISTRATION: + case CERTIFICATE_STAKE_DEREGISTRATION_CONWAY: #ifdef HAVE_BAGL - ui_displayBech32Screen( - "Stake script hash", - "script", - BODY_CTX->stageData.certificate.stakeCredential.scriptHash, - SIZEOF(BODY_CTX->stageData.certificate.stakeCredential.scriptHash), + ui_displayPaginatedText( + "Deregister", + "DRep", this_fn ); #elif defined(HAVE_NBGL) - { - char encodedStr[BECH32_STRING_SIZE_MAX] = {0}; - ui_getBech32Screen(encodedStr, SIZEOF(encodedStr), "script", BODY_CTX->stageData.certificate.stakeCredential.scriptHash, SIZEOF(BODY_CTX->stageData.certificate.stakeCredential.scriptHash)); - fill_and_display_if_required("Stake script hash", encodedStr, this_fn, respond_with_user_reject); - } + set_light_confirmation(true); + display_prompt("Deregister\nDRep", "", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + break; + + case CERTIFICATE_DREP_UPDATE: + #ifdef HAVE_BAGL + ui_displayPaginatedText( + "Update", + "DRep", + this_fn + ); + #elif defined(HAVE_NBGL) + set_light_confirmation(true); + display_prompt("Update\nDRep", "", this_fn, respond_with_user_reject); #endif // HAVE_BAGL break; + default: ASSERT(false); + } + } + UI_STEP(HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_CREDENTIAL) { + _displayCredential( + this_fn, + &cert->dRepCredential, + "DRep key", + "DRep key hash", + "drep", + "DRep script hash", + "drep" + ); + } + UI_STEP(HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_DEPOSIT) { + switch (cert->type) { + case CERTIFICATE_DREP_UPDATE: + // no deposit in these + UI_STEP_JUMP(HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_ANCHOR_NULL); + break; + + case CERTIFICATE_DREP_REGISTRATION: + case CERTIFICATE_DREP_DEREGISTRATION: + _displayDeposit(this_fn, cert->deposit); break; + + default: + ASSERT(false); } } - UI_STEP(HANDLE_CERTIFICATE_STEP_CONFIRM) { + UI_STEP(HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_ANCHOR_NULL) { + if (cert->type == CERTIFICATE_DREP_DEREGISTRATION) { + // no anchor for this type + UI_STEP_JUMP(HANDLE_CERTIFICATE_DREP_STEP_CONFIRM); + } + if (cert->anchor.isIncluded) { + UI_STEP_JUMP(HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_ANCHOR_URL); + } + _displayAnchorNull(this_fn); + } + UI_STEP(HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_ANCHOR_URL) { + if (!cert->anchor.isIncluded) { + UI_STEP_JUMP(HANDLE_CERTIFICATE_DREP_STEP_CONFIRM); + } + _displayAnchorUrl(this_fn, &cert->anchor); + } + UI_STEP(HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_ANCHOR_HASH) { + _displayAnchorHash(this_fn, &cert->anchor); + } + UI_STEP(HANDLE_CERTIFICATE_DREP_STEP_CONFIRM) { char description[50] = {0}; explicit_bzero(description, SIZEOF(description)); - switch (BODY_CTX->stageData.certificate.type) { - case CERTIFICATE_TYPE_STAKE_REGISTRATION: + switch (cert->type) { + case CERTIFICATE_DREP_REGISTRATION: #ifdef HAVE_BAGL snprintf(description, SIZEOF(description), "registration?"); #elif defined(HAVE_NBGL) @@ -484,7 +1024,7 @@ void signTx_handleCertificate_ui_runStep() #endif // HAVE_BAGL break; - case CERTIFICATE_TYPE_STAKE_DEREGISTRATION: + case CERTIFICATE_DREP_DEREGISTRATION: #ifdef HAVE_BAGL snprintf(description, SIZEOF(description), "deregistration?"); #elif defined(HAVE_NBGL) @@ -492,11 +1032,11 @@ void signTx_handleCertificate_ui_runStep() #endif // HAVE_BAGL break; - case CERTIFICATE_TYPE_STAKE_DELEGATION: + case CERTIFICATE_DREP_UPDATE: #ifdef HAVE_BAGL - snprintf(description, SIZEOF(description), "delegation?"); + snprintf(description, SIZEOF(description), "update?"); #elif defined(HAVE_NBGL) - display_confirmation("Confirm\ndelegation", "", "DELEGATION\nACCEPTED", "Delegation\nrejected", this_fn, respond_with_user_reject); + display_confirmation("Confirm\nupdate", "", "UPDATE\nACCEPTED", "Update\nrejected", this_fn, respond_with_user_reject); #endif // HAVE_BAGL break; @@ -516,12 +1056,12 @@ void signTx_handleCertificate_ui_runStep() #elif defined(HAVE_NBGL) #endif // HAVE_BAGL } - UI_STEP(HANDLE_CERTIFICATE_STEP_RESPOND) { + UI_STEP(HANDLE_CERTIFICATE_DREP_STEP_RESPOND) { respondSuccessEmptyMsg(); tx_advanceCertificatesStateIfAppropriate(); } - UI_STEP_END(HANDLE_CERTIFICATE_STEP_INVALID); + UI_STEP_END(HANDLE_CERTIFICATE_DREP_STEP_INVALID); } #ifdef APP_FEATURE_POOL_RETIREMENT @@ -529,9 +1069,10 @@ void signTx_handleCertificate_ui_runStep() void signTx_handleCertificatePoolRetirement_ui_runStep() { TRACE("UI step %d", ctx->ui_step); - ASSERT(BODY_CTX->stageData.certificate.type == CERTIFICATE_TYPE_STAKE_POOL_RETIREMENT); ui_callback_fn_t* this_fn = signTx_handleCertificatePoolRetirement_ui_runStep; + sign_tx_certificate_data_t* cert = &BODY_CTX->stageData.certificate; + ASSERT(cert->type == CERTIFICATE_STAKE_POOL_RETIREMENT); UI_STEP_BEGIN(ctx->ui_step, this_fn); @@ -540,13 +1081,13 @@ void signTx_handleCertificatePoolRetirement_ui_runStep() ui_displayBech32Screen( "Retire stake pool", "pool", - BODY_CTX->stageData.certificate.poolCredential.keyHash, SIZEOF(BODY_CTX->stageData.certificate.poolCredential.keyHash), + cert->poolCredential.keyHash, SIZEOF(cert->poolCredential.keyHash), this_fn ); #elif defined(HAVE_NBGL) set_light_confirmation(true); char encodedStr[BECH32_STRING_SIZE_MAX] = {0}; - ui_getBech32Screen(encodedStr, SIZEOF(encodedStr), "pool", BODY_CTX->stageData.certificate.poolCredential.keyHash, SIZEOF(BODY_CTX->stageData.certificate.poolCredential.keyHash)); + ui_getBech32Screen(encodedStr, SIZEOF(encodedStr), "pool", cert->poolCredential.keyHash, SIZEOF(cert->poolCredential.keyHash)); fill_and_display_if_required("Retire stake pool", encodedStr, this_fn, respond_with_user_reject); #endif // HAVE_BAGL } @@ -554,7 +1095,7 @@ void signTx_handleCertificatePoolRetirement_ui_runStep() #ifdef HAVE_BAGL ui_displayUint64Screen( "at the start of epoch", - BODY_CTX->stageData.certificate.epoch, + cert->epoch, this_fn ); #elif defined(HAVE_NBGL) @@ -562,7 +1103,7 @@ void signTx_handleCertificatePoolRetirement_ui_runStep() ui_getUint64Screen( line, SIZEOF(line), - BODY_CTX->stageData.certificate.epoch + cert->epoch ); fill_and_display_if_required("Start of epoch", line, this_fn, respond_with_user_reject); #endif // HAVE_BAGL @@ -774,7 +1315,7 @@ void signTx_handleRequiredSigner_ui_runStep() UI_STEP(HANDLE_REQUIRED_SIGNERS_STEP_RESPOND) { respondSuccessEmptyMsg(); - // Advance stage to the next input + // Advance stage to the next required signer ASSERT(BODY_CTX->currentRequiredSigner < ctx->numRequiredSigners); BODY_CTX->currentRequiredSigner++; @@ -812,6 +1353,228 @@ void signTx_handleTotalCollateral_ui_runStep() UI_STEP_END(HANDLE_TOTAL_COLLATERAL_STEP_INVALID); } +// ========================= VOTING PROCEDURES =========================== + +void signTx_handleVotingProcedure_ui_runStep() +{ + TRACE("UI step %d", ctx->ui_step); + ui_callback_fn_t* this_fn = signTx_handleVotingProcedure_ui_runStep; + sign_tx_voting_procedure_t* vp = &BODY_CTX->stageData.votingProcedure; + + UI_STEP_BEGIN(ctx->ui_step, this_fn); + + UI_STEP(HANDLE_VOTING_PROCEDURE_STEP_INTRO) { + #ifdef HAVE_BAGL + ui_displayPaginatedText( + "Vote for", + "governance action", + this_fn + ); + #elif defined(HAVE_NBGL) + set_light_confirmation(true); + display_prompt("Vote for\ngovernance action", "", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_VOTING_PROCEDURE_STEP_VOTER) { + switch (vp->voter.type) { + case EXT_VOTER_DREP_KEY_PATH: + case EXT_VOTER_COMMITTEE_HOT_KEY_PATH: + case EXT_VOTER_STAKE_POOL_KEY_PATH: + _displayKeyPath(this_fn, &vp->voter.keyPath, "Voter key"); + break; + case EXT_VOTER_DREP_KEY_HASH: + _displayKeyHash(this_fn, vp->voter.keyHash, "Voter key hash", "drep"); + break; + case EXT_VOTER_COMMITTEE_HOT_KEY_HASH: + _displayKeyHash(this_fn, vp->voter.keyHash, "Voter key hash", "cc_hot"); + break; + case EXT_VOTER_STAKE_POOL_KEY_HASH: + _displayKeyHash(this_fn, vp->voter.keyHash, "Voter key hash", "pool"); + break; + case EXT_VOTER_COMMITTEE_HOT_SCRIPT_HASH: + _displayScriptHash(this_fn, vp->voter.scriptHash, "Voter script hash", "cc_hot"); + break; + case EXT_VOTER_DREP_SCRIPT_HASH: + _displayScriptHash(this_fn, vp->voter.scriptHash, "Voter script hash", "drep"); + break; + default: + ASSERT(false); + break; + } + } + UI_STEP(HANDLE_VOTING_PROCEDURE_STEP_GOV_ACTION_ID_TXHASH) { + char txHashHex[1 + 2 * TX_HASH_LENGTH] = {0}; + explicit_bzero(txHashHex, SIZEOF(txHashHex)); + size_t len = encode_hex( + vp->govActionId.txHashBuffer, SIZEOF(vp->govActionId.txHashBuffer), + txHashHex, SIZEOF(txHashHex) + ); + ASSERT(len + 1 == SIZEOF(txHashHex)); + + #ifdef HAVE_BAGL + ui_displayPaginatedText( + "Action tx hash", + txHashHex, + this_fn + ); + #elif defined(HAVE_NBGL) + fill_and_display_if_required( + "Action tx hash", + txHashHex, + this_fn, + respond_with_user_reject + ); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_VOTING_PROCEDURE_STEP_GOV_ACTION_ID_INDEX) { + char indexStr[30] = {0}; + explicit_bzero(indexStr, SIZEOF(indexStr)); + snprintf(indexStr, SIZEOF(indexStr), "%d", vp->govActionId.govActionIndex); + ASSERT(indexStr[SIZEOF(indexStr) - 1] == '\0'); + + #ifdef HAVE_BAGL + ui_displayPaginatedText( + "Action tx index", + indexStr, + this_fn + ); + #elif defined(HAVE_NBGL) + fill_and_display_if_required( + "Action tx index", + indexStr, + this_fn, + respond_with_user_reject + ); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_VOTING_PROCEDURE_STEP_VOTE) { + char voteStr[30] = {0}; + explicit_bzero(voteStr, SIZEOF(voteStr)); + switch (vp->votingProcedure.vote) { + case VOTE_NO: + snprintf(voteStr, SIZEOF(voteStr), "NO"); + break; + case VOTE_YES: + snprintf(voteStr, SIZEOF(voteStr), "YES"); + break; + case VOTE_ABSTAIN: + snprintf(voteStr, SIZEOF(voteStr), "ABSTAIN"); + break; + default: + ASSERT(false); + } + ASSERT(voteStr[SIZEOF(voteStr) - 1] == '\0'); + + #ifdef HAVE_BAGL + ui_displayPaginatedText( + "Vote", + voteStr, + this_fn + ); + #elif defined(HAVE_NBGL) + fill_and_display_if_required( + "Vote", + voteStr, + this_fn, + respond_with_user_reject + ); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_VOTING_PROCEDURE_STEP_ANCHOR_NULL) { + if (vp->votingProcedure.anchor.isIncluded) { + UI_STEP_JUMP(HANDLE_VOTING_PROCEDURE_STEP_ANCHOR_URL); + } + _displayAnchorNull(this_fn); + } + UI_STEP(HANDLE_VOTING_PROCEDURE_STEP_ANCHOR_URL) { + if (!vp->votingProcedure.anchor.isIncluded) { + UI_STEP_JUMP(HANDLE_VOTING_PROCEDURE_STEP_CONFIRM); + } + _displayAnchorUrl(this_fn, &vp->votingProcedure.anchor); + } + UI_STEP(HANDLE_VOTING_PROCEDURE_STEP_ANCHOR_HASH) { + _displayAnchorHash(this_fn, &vp->votingProcedure.anchor); + } + UI_STEP(HANDLE_VOTING_PROCEDURE_STEP_CONFIRM) { + #ifdef HAVE_BAGL + ui_displayPrompt( + "Confirm", + "vote?", + this_fn, + respond_with_user_reject + ); + #elif defined(HAVE_NBGL) + display_confirmation("Confirm\nvote?", "", "VOTE\nACCEPTED", "Vote\nrejected", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_VOTING_PROCEDURE_STEP_RESPOND) { + respondSuccessEmptyMsg(); + + // Advance stage to the next vote + ASSERT(BODY_CTX->currentVotingProcedure < ctx->numVotingProcedures); + BODY_CTX->currentVotingProcedure++; + + if (BODY_CTX->currentVotingProcedure == ctx->numVotingProcedures) { + tx_advanceStage(); + } + } + UI_STEP_END(HANDLE_VOTING_PROCEDURE_STEP_INVALID); +} + +// ============================== TREASURY ============================== + +void signTx_handleTreasury_ui_runStep() +{ + TRACE("UI step %d", ctx->ui_step); + ui_callback_fn_t* this_fn = signTx_handleTreasury_ui_runStep; + + TRACE_ADA_AMOUNT("treasury ", BODY_CTX->stageData.treasury); + + UI_STEP_BEGIN(ctx->ui_step, this_fn); + + UI_STEP(HANDLE_TREASURY_STEP_DISPLAY) { + #ifdef HAVE_BAGL + ui_displayAdaAmountScreen("Treasury amount", BODY_CTX->stageData.treasury, this_fn); + #elif defined(HAVE_NBGL) + char adaAmountStr[50] = {0}; + ui_getAdaAmountScreen(adaAmountStr, SIZEOF(adaAmountStr), BODY_CTX->stageData.treasury); + fill_and_display_if_required("Treasury amount", adaAmountStr, this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_TREASURY_STEP_RESPOND) { + respondSuccessEmptyMsg(); + tx_advanceStage(); + } + UI_STEP_END(HANDLE_TREASURY_STEP_INVALID); +} + +// ============================== DONATION ============================== + +void signTx_handleDonation_ui_runStep() +{ + TRACE("UI step %d", ctx->ui_step); + ui_callback_fn_t* this_fn = signTx_handleDonation_ui_runStep; + + TRACE_ADA_AMOUNT("donation ", BODY_CTX->stageData.donation); + + UI_STEP_BEGIN(ctx->ui_step, this_fn); + + UI_STEP(HANDLE_DONATION_STEP_DISPLAY) { + #ifdef HAVE_BAGL + ui_displayAdaAmountScreen("Donation", BODY_CTX->stageData.donation, this_fn); + #elif defined(HAVE_NBGL) + char adaAmountStr[50] = {0}; + ui_getAdaAmountScreen(adaAmountStr, SIZEOF(adaAmountStr), BODY_CTX->stageData.donation); + fill_and_display_if_required("Donation", adaAmountStr, this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_DONATION_STEP_RESPOND) { + respondSuccessEmptyMsg(); + tx_advanceStage(); + } + UI_STEP_END(HANDLE_DONATION_STEP_INVALID); +} + // ============================== CONFIRM ============================== void signTx_handleConfirm_ui_runStep() diff --git a/src/signTx_ui.h b/src/signTx_ui.h index 775517d0..3d87867f 100644 --- a/src/signTx_ui.h +++ b/src/signTx_ui.h @@ -67,23 +67,67 @@ void signTx_handleTtl_ui_runStep(); // ============================== CERTIFICATES ============================== enum { - HANDLE_CERTIFICATE_STEP_DISPLAY_OPERATION = 600, - HANDLE_CERTIFICATE_STEP_DISPLAY_STAKING_KEY, - HANDLE_CERTIFICATE_STEP_CONFIRM, - HANDLE_CERTIFICATE_STEP_RESPOND, - HANDLE_CERTIFICATE_STEP_INVALID, + HANDLE_CERTIFICATE_STAKING_STEP_DISPLAY_OPERATION = 600, + HANDLE_CERTIFICATE_STAKING_STEP_DISPLAY_STAKE_CRED, + HANDLE_CERTIFICATE_STAKING_STEP_DISPLAY_DEPOSIT, + HANDLE_CERTIFICATE_STAKING_STEP_CONFIRM, + HANDLE_CERTIFICATE_STAKING_STEP_RESPOND, + HANDLE_CERTIFICATE_STAKING_STEP_INVALID, }; +void signTx_handleCertificateStaking_ui_runStep(); -void signTx_handleCertificate_ui_runStep(); +enum { + HANDLE_CERTIFICATE_VOTE_DELEGATION_STEP_DISPLAY_OPERATION = 610, + HANDLE_CERTIFICATE_VOTE_DELEGATION_STEP_DISPLAY_STAKE_CRED, + HANDLE_CERTIFICATE_VOTE_DELEGATION_STEP_DISPLAY_DREP, + HANDLE_CERTIFICATE_VOTE_DELEGATION_STEP_CONFIRM, + HANDLE_CERTIFICATE_VOTE_DELEGATION_STEP_RESPOND, + HANDLE_CERTIFICATE_VOTE_DELEGATION_STEP_INVALID, +}; +void signTx_handleCertificateVoteDeleg_ui_runStep(); + +enum { + HANDLE_CERTIFICATE_COMM_AUTH_STEP_DISPLAY_OPERATION = 620, + HANDLE_CERTIFICATE_COMM_AUTH_STEP_DISPLAY_COLD_CRED, + HANDLE_CERTIFICATE_COMM_AUTH_STEP_DISPLAY_HOT_CRED, + HANDLE_CERTIFICATE_COMM_AUTH_STEP_CONFIRM, + HANDLE_CERTIFICATE_COMM_AUTH_STEP_RESPOND, + HANDLE_CERTIFICATE_COMM_AUTH_STEP_INVALID, +}; +void signTx_handleCertificateCommitteeAuth_ui_runStep(); + +enum { + HANDLE_CERTIFICATE_COMM_RESIGN_STEP_DISPLAY_OPERATION = 640, + HANDLE_CERTIFICATE_COMM_RESIGN_STEP_DISPLAY_COLD_CRED, + HANDLE_CERTIFICATE_COMM_RESIGN_STEP_DISPLAY_ANCHOR_NULL, + HANDLE_CERTIFICATE_COMM_RESIGN_STEP_DISPLAY_ANCHOR_URL, + HANDLE_CERTIFICATE_COMM_RESIGN_STEP_DISPLAY_ANCHOR_HASH, + HANDLE_CERTIFICATE_COMM_RESIGN_STEP_CONFIRM, + HANDLE_CERTIFICATE_COMM_RESIGN_STEP_RESPOND, + HANDLE_CERTIFICATE_COMM_RESIGN_STEP_INVALID, +}; +void signTx_handleCertificateCommitteeResign_ui_runStep(); + +enum { + HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_OPERATION = 660, + HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_CREDENTIAL, + HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_DEPOSIT, + HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_ANCHOR_NULL, + HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_ANCHOR_URL, + HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_ANCHOR_HASH, + HANDLE_CERTIFICATE_DREP_STEP_CONFIRM, + HANDLE_CERTIFICATE_DREP_STEP_RESPOND, + HANDLE_CERTIFICATE_DREP_STEP_INVALID, +}; +void signTx_handleCertificateDRep_ui_runStep(); enum { - HANDLE_CERTIFICATE_POOL_RETIREMENT_STEP_DISPLAY_OPERATION = 650, + HANDLE_CERTIFICATE_POOL_RETIREMENT_STEP_DISPLAY_OPERATION = 680, HANDLE_CERTIFICATE_POOL_RETIREMENT_STEP_DISPLAY_EPOCH, HANDLE_CERTIFICATE_POOL_RETIREMENT_STEP_CONFIRM, HANDLE_CERTIFICATE_POOL_RETIREMENT_STEP_RESPOND, HANDLE_CERTIFICATE_POOL_RETIREMENT_STEP_INVALID, }; - void signTx_handleCertificatePoolRetirement_ui_runStep(); // ============================== WITHDRAWALS ============================== @@ -131,13 +175,51 @@ void signTx_handleRequiredSigner_ui_runStep(); // ========================= TOTAL COLLATERAL =========================== enum { - HANDLE_TOTAL_COLLATERAL_STEP_DISPLAY = 400, + HANDLE_TOTAL_COLLATERAL_STEP_DISPLAY = 1700, HANDLE_TOTAL_COLLATERAL_STEP_RESPOND, HANDLE_TOTAL_COLLATERAL_STEP_INVALID, }; void signTx_handleTotalCollateral_ui_runStep(); +// ========================= VOTING PROCEDURES =========================== + +enum { + HANDLE_VOTING_PROCEDURE_STEP_INTRO = 1900, + HANDLE_VOTING_PROCEDURE_STEP_VOTER, + HANDLE_VOTING_PROCEDURE_STEP_GOV_ACTION_ID_TXHASH, + HANDLE_VOTING_PROCEDURE_STEP_GOV_ACTION_ID_INDEX, + HANDLE_VOTING_PROCEDURE_STEP_VOTE, + HANDLE_VOTING_PROCEDURE_STEP_ANCHOR_NULL, + HANDLE_VOTING_PROCEDURE_STEP_ANCHOR_URL, + HANDLE_VOTING_PROCEDURE_STEP_ANCHOR_HASH, + HANDLE_VOTING_PROCEDURE_STEP_CONFIRM, + HANDLE_VOTING_PROCEDURE_STEP_RESPOND, + HANDLE_VOTING_PROCEDURE_STEP_INVALID, +}; + +void signTx_handleVotingProcedure_ui_runStep(); + +// ============================== TREASURY ============================== + +enum { + HANDLE_TREASURY_STEP_DISPLAY = 2100, + HANDLE_TREASURY_STEP_RESPOND, + HANDLE_TREASURY_STEP_INVALID, +}; + +void signTx_handleTreasury_ui_runStep(); + +// ============================== DONATION ============================== + +enum { + HANDLE_DONATION_STEP_DISPLAY = 2200, + HANDLE_DONATION_STEP_RESPOND, + HANDLE_DONATION_STEP_INVALID, +}; + +void signTx_handleDonation_ui_runStep(); + // ============================== CONFIRM ============================== enum { diff --git a/src/textUtils.c b/src/textUtils.c index 1eac4c93..892fafef 100644 --- a/src/textUtils.c +++ b/src/textUtils.c @@ -190,7 +190,7 @@ void str_traceInt64(int64_t number) #endif // DEVEL -// TODO: This is valid only for mainnet +// Note: This is valid only for mainnet static struct { uint64_t startSlotNumber; uint64_t startEpoch; @@ -237,14 +237,6 @@ size_t str_formatValidityBoundary(uint64_t slotNumber, char* out, size_t outSize return strlen(out); } -// returns length of the resulting string -size_t str_formatMetadata(const uint8_t* metadataHash, size_t metadataHashSize, char* out, size_t outSize) -{ - ASSERT(outSize < BUFFER_SIZE_PARANOIA); - - return encode_hex(metadataHash, metadataHashSize, out, outSize); -} - // check if a non-null-terminated buffer contains printable ASCII between 33 and 126 (inclusive) bool str_isPrintableAsciiWithoutSpaces(const uint8_t* buffer, size_t bufferSize) { diff --git a/src/textUtils.h b/src/textUtils.h index 971f4807..1ce68ba2 100644 --- a/src/textUtils.h +++ b/src/textUtils.h @@ -43,8 +43,6 @@ void str_traceInt64(int64_t number); size_t str_formatValidityBoundary(uint64_t slotNumber, char* out, size_t outSize); -size_t str_formatMetadata(const uint8_t* metadataHash, size_t metadataHashSize, char* out, size_t outSize); - bool str_isPrintableAsciiWithoutSpaces(const uint8_t* buffer, size_t bufferSize); bool str_isPrintableAsciiWithSpaces(const uint8_t* buffer, size_t bufferSize); diff --git a/src/txHashBuilder.c b/src/txHashBuilder.c index 9b2e3001..ec6d514b 100644 --- a/src/txHashBuilder.c +++ b/src/txHashBuilder.c @@ -176,7 +176,7 @@ static void processOutputTopLevel(tx_hash_builder_t* builder, const tx_output_de static void assertCanLeaveCurrentOutput(tx_hash_builder_t* builder) { - switch (builder->outputState) { + switch (builder->outputData.outputState) { case TX_OUTPUT_INIT: case TX_OUTPUT_TOP_LEVEL_DATA: // no tokens @@ -235,7 +235,10 @@ void txHashBuilder_init( bool includeNetworkId, bool includeCollateralOutput, bool includeTotalCollateral, - uint16_t numReferenceInputs + uint16_t numReferenceInputs, + uint16_t numVotingProcedures, + bool includeTreasury, + bool includeDonation ) { TRACE("numInputs = %u", numInputs); @@ -253,6 +256,9 @@ void txHashBuilder_init( TRACE("includeCollateralOutput = %u", includeCollateralOutput); TRACE("includeTotalCollateral = %u", includeTotalCollateral); TRACE("numReferenceInputs = %u", numReferenceInputs); + TRACE("numVotingProcedures = %u", numReferenceInputs); + TRACE("includeTreasury = %u", includeTreasury); + TRACE("includeDonation = %u", includeDonation); blake2b_256_init(&builder->txHash); @@ -307,7 +313,16 @@ void txHashBuilder_init( builder->remainingReferenceInputs = numReferenceInputs; if (numReferenceInputs > 0) numItems++; - ASSERT((3 <= numItems) && (numItems <= 16)); + builder->remainingVotingProcedures = numVotingProcedures; + if (numVotingProcedures > 0) numItems++; + + builder->includeTreasury = includeTreasury; + if (includeTreasury) numItems++; + + builder->includeDonation = includeDonation; + if (includeDonation) numItems++; + + ASSERT((3 <= numItems) && (numItems <= 19)); _TRACE("Serializing tx body with %u items", numItems); BUILDER_APPEND_CBOR(CBOR_TYPE_MAP, numItems); @@ -371,7 +386,7 @@ void txHashBuilder_enterOutputs(tx_hash_builder_t* builder) BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, builder->remainingOutputs); } builder->state = TX_HASH_BUILDER_IN_OUTPUTS; - builder->outputState = TX_OUTPUT_INIT; + builder->outputData.outputState = TX_OUTPUT_INIT; } void txHashBuilder_addOutput_topLevelData( @@ -381,7 +396,7 @@ void txHashBuilder_addOutput_topLevelData( { _TRACE( "state = %d, outputState = %d, remainingOutputs = %u", - builder->state, builder->outputState, builder->remainingOutputs + builder->state, builder->outputData.outputState, builder->remainingOutputs ); ASSERT(builder->state == TX_HASH_BUILDER_IN_OUTPUTS); @@ -392,7 +407,7 @@ void txHashBuilder_addOutput_topLevelData( processOutputTopLevel(builder, output); - builder->outputState = TX_OUTPUT_TOP_LEVEL_DATA; + builder->outputData.outputState = TX_OUTPUT_TOP_LEVEL_DATA; } __noinline_due_to_stack__ @@ -404,10 +419,10 @@ static void addTokenGroup( { _TRACE( "state = %d, outputState = %d, remainingAssetGroups = %u", - builder->state, builder->outputState, builder->outputData.multiassetData.remainingAssetGroups + builder->state, builder->outputData.outputState, builder->outputData.multiassetData.remainingAssetGroups ); - switch (builder->outputState) { + switch (builder->outputData.outputState) { case TX_OUTPUT_ASSET_GROUP: // we have been adding tokens into the previous asset group ASSERT(builder->outputData.multiassetData.remainingTokens == 0); @@ -443,7 +458,7 @@ static void addTokenGroup( } } - builder->outputState = TX_OUTPUT_ASSET_GROUP; + builder->outputData.outputState = TX_OUTPUT_ASSET_GROUP; } __noinline_due_to_stack__ @@ -456,10 +471,10 @@ static void addToken( { _TRACE( "state = %d, outputState = %d, remainingTokens = %u", - builder->state, builder->outputState, builder->outputData.multiassetData.remainingTokens + builder->state, builder->outputData.outputState, builder->outputData.multiassetData.remainingTokens ); - switch (builder->outputState) { + switch (builder->outputData.outputState) { case TX_OUTPUT_ASSET_GROUP: // we have been adding tokens into an asset group break; @@ -485,7 +500,7 @@ static void addToken( } } - builder->outputState = TX_OUTPUT_ASSET_GROUP; + builder->outputData.outputState = TX_OUTPUT_ASSET_GROUP; } void txHashBuilder_addOutput_tokenGroup( @@ -518,9 +533,9 @@ void txHashBuilder_addOutput_datum( { ASSERT(builder->outputData.includeDatum); - TRACE("%d", builder->outputState); + TRACE("%d", builder->outputData.outputState); - switch (builder->outputState) { + switch (builder->outputData.outputState) { case TX_OUTPUT_TOP_LEVEL_DATA: // top level data has been added instantaneously // so we only check there are no asset groups left out @@ -564,7 +579,7 @@ void txHashBuilder_addOutput_datum( BUILDER_APPEND_DATA(buffer, bufferSize); } // Hash is transmitted in one chunk, and datumType stage is finished - builder->outputState = TX_OUTPUT_DATUM_HASH; + builder->outputData.outputState = TX_OUTPUT_DATUM_HASH; break; case DATUM_INLINE: @@ -578,7 +593,7 @@ void txHashBuilder_addOutput_datum( BUILDER_APPEND_CBOR(CBOR_TYPE_BYTES, bufferSize); // byte chunks will be added later } - builder->outputState = TX_OUTPUT_DATUM_INLINE; + builder->outputData.outputState = TX_OUTPUT_DATUM_INLINE; break; default: @@ -591,7 +606,7 @@ void txHashBuilder_addOutput_datum_inline_chunk( const uint8_t* buffer, size_t bufferSize ) { - ASSERT(builder->outputState == TX_OUTPUT_DATUM_INLINE); + ASSERT(builder->outputData.outputState == TX_OUTPUT_DATUM_INLINE); ASSERT(bufferSize <= builder->outputData.datumData.remainingBytes); builder->outputData.datumData.remainingBytes -= bufferSize; { @@ -603,7 +618,7 @@ void txHashBuilder_addOutput_referenceScript(tx_hash_builder_t* builder, size_t { ASSERT(builder->outputData.includeRefScript); - switch (builder->outputState) { + switch (builder->outputData.outputState) { case TX_OUTPUT_TOP_LEVEL_DATA: // top level data has been added instantaneously // so we only check there are no asset groups left out @@ -639,7 +654,7 @@ void txHashBuilder_addOutput_referenceScript(tx_hash_builder_t* builder, size_t // byte chunks will be added later } builder->outputData.referenceScriptData.remainingBytes = scriptSize; - builder->outputState = TX_OUTPUT_SCRIPT_REFERENCE_CHUNKS; + builder->outputData.outputState = TX_OUTPUT_SCRIPT_REFERENCE_CHUNKS; } void txHashBuilder_addOutput_referenceScript_dataChunk( @@ -647,7 +662,7 @@ void txHashBuilder_addOutput_referenceScript_dataChunk( const uint8_t* buffer, size_t bufferSize ) { - ASSERT(builder->outputState == TX_OUTPUT_SCRIPT_REFERENCE_CHUNKS); + ASSERT(builder->outputData.outputState == TX_OUTPUT_SCRIPT_REFERENCE_CHUNKS); { BUILDER_APPEND_DATA(buffer, bufferSize); } @@ -751,7 +766,7 @@ static void _initNewCertificate(tx_hash_builder_t* builder) builder->remainingCertificates--; } -static const uint8_t* getCredentialHashBuffer(const credential_t* credential) +static const uint8_t* _getCredentialHashBuffer(const credential_t* credential) { switch (credential->type) { case CREDENTIAL_KEY_HASH: @@ -763,7 +778,7 @@ static const uint8_t* getCredentialHashBuffer(const credential_t* credential) } } -static size_t getCredentialHashSize(const credential_t* credential) +static size_t _getCredentialHashSize(const credential_t* credential) { switch (credential->type) { case CREDENTIAL_KEY_HASH: @@ -775,8 +790,25 @@ static size_t getCredentialHashSize(const credential_t* credential) } } +static void _appendCredential( + tx_hash_builder_t* builder, + const credential_t* credential +) +{ + BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 2); + { + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, credential->type); + } + { + const size_t size = _getCredentialHashSize(credential); + BUILDER_APPEND_CBOR(CBOR_TYPE_BYTES, size); + BUILDER_APPEND_DATA(_getCredentialHashBuffer(credential), size); + } +} + // stake key certificate registration or deregistration -void txHashBuilder_addCertificate_stakingHash( +// will be deprecated after Conway +void txHashBuilder_addCertificate_stakingOld( tx_hash_builder_t* builder, const certificate_type_t certificateType, const credential_t* stakeCredential @@ -784,8 +816,8 @@ void txHashBuilder_addCertificate_stakingHash( { _initNewCertificate(builder); - ASSERT((certificateType == CERTIFICATE_TYPE_STAKE_REGISTRATION) - || (certificateType == CERTIFICATE_TYPE_STAKE_DEREGISTRATION)); + ASSERT((certificateType == CERTIFICATE_STAKE_REGISTRATION) + || (certificateType == CERTIFICATE_STAKE_DEREGISTRATION)); // Array(2)[ // Unsigned[certificateType] @@ -800,20 +832,48 @@ void txHashBuilder_addCertificate_stakingHash( BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, certificateType); } { - BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 2); - { - BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, stakeCredential->type); - } - { - const size_t size = getCredentialHashSize(stakeCredential); - BUILDER_APPEND_CBOR(CBOR_TYPE_BYTES, size); - BUILDER_APPEND_DATA(getCredentialHashBuffer(stakeCredential), size); - } + _appendCredential(builder, stakeCredential); } } } -void txHashBuilder_addCertificate_delegation( +// stake key certificate registration or deregistration +// exists since Conway +void txHashBuilder_addCertificate_staking( + tx_hash_builder_t* builder, + const certificate_type_t certificateType, + const credential_t* stakeCredential, + uint64_t deposit +) +{ + _initNewCertificate(builder); + + ASSERT((certificateType == CERTIFICATE_STAKE_REGISTRATION_CONWAY) + || (certificateType == CERTIFICATE_STAKE_DEREGISTRATION_CONWAY)); + + // Array(3)[ + // Unsigned[certificateType] + // Array(2)[ + // Unsigned[0] + // Bytes[stakingKeyHash] + // ] + // Unsigned[deposit] + // ] + { + BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 3); + { + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, certificateType); + } + { + _appendCredential(builder, stakeCredential); + } + { + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, deposit); + } + } +} + +void txHashBuilder_addCertificate_stakeDelegation( tx_hash_builder_t* builder, const credential_t* stakeCredential, const uint8_t* poolKeyHash, size_t poolKeyHashSize @@ -834,18 +894,10 @@ void txHashBuilder_addCertificate_delegation( { BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 3); { - BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, 2); + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, CERTIFICATE_STAKE_DELEGATION); } { - BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 2); - { - BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, stakeCredential->type); - } - { - const size_t size = getCredentialHashSize(stakeCredential); - BUILDER_APPEND_CBOR(CBOR_TYPE_BYTES, size); - BUILDER_APPEND_DATA(getCredentialHashBuffer(stakeCredential), size); - } + _appendCredential(builder, stakeCredential); } { BUILDER_APPEND_CBOR(CBOR_TYPE_BYTES, poolKeyHashSize); @@ -854,6 +906,246 @@ void txHashBuilder_addCertificate_delegation( } } +void txHashBuilder_addCertificate_voteDelegation( + tx_hash_builder_t* builder, + const credential_t* stakeCredential, + const drep_t* drep +) +{ + _initNewCertificate(builder); + + // Array(3)[ + // Unsigned[9] + // Array(2)[ + // Unsigned[0] + // Bytes[stakingKeyHash] + // ] + // Array(1 or 2)[ + // Unsigned[drep_type] + // ?Bytes[key/script hash] // optional + // ] + // ] + { + BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 3); + { + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, CERTIFICATE_VOTE_DELEGATION); + } + { + _appendCredential(builder, stakeCredential); + } + { + // DRep + switch (drep->type) { + case DREP_KEY_HASH: { + BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 2); + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, DREP_KEY_HASH); + BUILDER_APPEND_CBOR(CBOR_TYPE_BYTES, SIZEOF(drep->keyHash)); + BUILDER_APPEND_DATA(drep->keyHash, SIZEOF(drep->keyHash)); + break; + } + case DREP_SCRIPT_HASH: { + BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 2); + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, DREP_SCRIPT_HASH); + BUILDER_APPEND_CBOR(CBOR_TYPE_BYTES, SIZEOF(drep->scriptHash)); + BUILDER_APPEND_DATA(drep->scriptHash, SIZEOF(drep->scriptHash)); + break; + } + case DREP_ALWAYS_ABSTAIN: + case DREP_ALWAYS_NO_CONFIDENCE: { + BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 1); + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, drep->type); + break; + } + default: + ASSERT(false); + } + } + } +} + +void txHashBuilder_addCertificate_committeeAuthHot( + tx_hash_builder_t* builder, + const credential_t* coldCredential, + const credential_t* hotCredential +) +{ + _initNewCertificate(builder); + + // Array(3)[ + // Unsigned[14] + // Array(2)[ + // Unsigned[0 or 1] + // Bytes[stakingKeyHash] + // ] + // Array(2)[ + // Unsigned[0 or 1] + // Bytes[stakingKeyHash] + // ] + // ] + { + BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 3); + { + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, CERTIFICATE_AUTHORIZE_COMMITTEE_HOT); + } + { + _appendCredential(builder, coldCredential); + } + { + _appendCredential(builder, hotCredential); + } + } +} + +static void _appendAnchor( + tx_hash_builder_t* builder, + const anchor_t* anchor +) +{ + if (anchor->isIncluded) { + // Array(2)[ + // Tstr[url] + // Bytes[32] + // ] + BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 2); + { + BUILDER_APPEND_CBOR(CBOR_TYPE_TEXT, anchor->urlLength); + BUILDER_APPEND_DATA(anchor->url, anchor->urlLength); + } + { + BUILDER_APPEND_CBOR(CBOR_TYPE_BYTES, SIZEOF(anchor->hash)); + BUILDER_APPEND_DATA(anchor->hash, SIZEOF(anchor->hash)); + } + } else { + // Null + BUILDER_APPEND_CBOR(CBOR_TYPE_NULL, 0); + } +} + +void txHashBuilder_addCertificate_committeeResign( + tx_hash_builder_t* builder, + const credential_t* coldCredential, + const anchor_t* anchor +) +{ + _initNewCertificate(builder); + + // Array(3)[ + // Unsigned[15] + // Array(2)[ + // Unsigned[0 or 1] + // Bytes[stakingKeyHash] + // ] + // Null / ...anchor + // ] + { + BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 3); + { + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, CERTIFICATE_RESIGN_COMMITTEE_COLD); + } + { + _appendCredential(builder, coldCredential); + } + { + _appendAnchor(builder, anchor); + } + } +} + +void txHashBuilder_addCertificate_dRepRegistration( + tx_hash_builder_t* builder, + const credential_t* dRepCredential, + uint64_t deposit, + const anchor_t* anchor +) +{ + _initNewCertificate(builder); + + // Array(4)[ + // Unsigned[16] + // Array(2)[ + // Unsigned[0/1] + // Bytes[key/script hash] + // ] + // Unsigned[deposit] + // Null / ...anchor + // ] + { + BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 4); + { + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, CERTIFICATE_DREP_REGISTRATION); + } + { + _appendCredential(builder, dRepCredential); + } + { + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, deposit); + } + { + _appendAnchor(builder, anchor); + } + } +} + +void txHashBuilder_addCertificate_dRepDeregistration( + tx_hash_builder_t* builder, + const credential_t* dRepCredential, + uint64_t deposit +) +{ + _initNewCertificate(builder); + + // Array(3)[ + // Unsigned[17] + // Array(2)[ + // Unsigned[0/1] + // Bytes[key/script hash] + // ] + // Unsigned[deposit] + // ] + { + BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 3); + { + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, CERTIFICATE_DREP_DEREGISTRATION); + } + { + _appendCredential(builder, dRepCredential); + } + { + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, deposit); + } + } +} + +void txHashBuilder_addCertificate_dRepUpdate( + tx_hash_builder_t* builder, + const credential_t* dRepCredential, + const anchor_t* anchor +) +{ + _initNewCertificate(builder); + + // Array(3)[ + // Unsigned[18] + // Array(2)[ + // Unsigned[0/1] + // Bytes[key/script hash] + // ] + // Null / ...anchor + // ] + { + BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 3); + { + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, CERTIFICATE_DREP_UPDATE); + } + { + _appendCredential(builder, dRepCredential); + } + { + _appendAnchor(builder, anchor); + } + } +} + #ifdef APP_FEATURE_POOL_RETIREMENT void txHashBuilder_addCertificate_poolRetirement( @@ -874,7 +1166,7 @@ void txHashBuilder_addCertificate_poolRetirement( { BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 3); { - BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, 4); + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, CERTIFICATE_STAKE_POOL_RETIREMENT); } { BUILDER_APPEND_CBOR(CBOR_TYPE_BYTES, poolKeyHashSize); @@ -906,7 +1198,7 @@ void txHashBuilder_poolRegistrationCertificate_enter( { BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 10); { - BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, 3); + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, CERTIFICATE_STAKE_POOL_REGISTRATION); } } @@ -1466,7 +1758,7 @@ void txHashBuilder_addMint_topLevelData( // ] BUILDER_APPEND_CBOR(CBOR_TYPE_MAP, numAssetGroups); - builder->outputState = TX_OUTPUT_TOP_LEVEL_DATA; + builder->outputData.outputState = TX_OUTPUT_TOP_LEVEL_DATA; } void txHashBuilder_addMint_tokenGroup( @@ -1501,7 +1793,7 @@ static void txHashBuilder_assertCanLeaveMint(tx_hash_builder_t* builder) switch (builder->state) { case TX_HASH_BUILDER_IN_MINT: - ASSERT(builder->outputState == TX_OUTPUT_ASSET_GROUP); + ASSERT(builder->outputData.outputState == TX_OUTPUT_ASSET_GROUP); ASSERT(builder->outputData.multiassetData.remainingAssetGroups == 0); ASSERT(builder->outputData.multiassetData.remainingTokens == 0); break; @@ -1717,7 +2009,7 @@ void txHashBuilder_addCollateralOutput( } processOutputTopLevel(builder, output); - builder->outputState = TX_OUTPUT_TOP_LEVEL_DATA; + builder->outputData.outputState = TX_OUTPUT_TOP_LEVEL_DATA; builder->state = TX_HASH_BUILDER_IN_COLLATERAL_OUTPUT; } @@ -1851,11 +2143,194 @@ static void txHashBuilder_assertCanLeaveReferenceInputs(tx_hash_builder_t* build } } +// ========================= VOTING PROCEDURES ========================== + +void txHashBuilder_enterVotingProcedures(tx_hash_builder_t* builder) +{ + _TRACE("state = %d", builder->state); + + txHashBuilder_assertCanLeaveReferenceInputs(builder); + // we don't allow an empty map for an optional item + ASSERT(builder->remainingVotingProcedures > 0); + + { + // Enter voting procedures + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, TX_BODY_KEY_VOTING_PROCEDURES); + BUILDER_APPEND_CBOR(CBOR_TYPE_MAP, builder->remainingVotingProcedures); + } + builder->state = TX_HASH_BUILDER_IN_VOTING_PROCEDURES; +} + +// assumes a single voting procedure for the voter +void txHashBuilder_addVotingProcedure( + tx_hash_builder_t* builder, + voter_t* voter, + gov_action_id_t* govActionId, + voting_procedure_t* votingProcedure +) +{ + _TRACE("state = %d, remainingVotingProcedures = %u", builder->state, builder->remainingVotingProcedures); + + ASSERT(builder->state == TX_HASH_BUILDER_IN_VOTING_PROCEDURES); + ASSERT(builder->remainingVotingProcedures > 0); + builder->remainingVotingProcedures--; + + { + // voter + // Array(2)[ + // Unsigned[voter type] + // Bytes[key or script hash], + // ] + BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 2); + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, voter->type); + + switch (voter->type) { + case VOTER_COMMITTEE_HOT_KEY_HASH: + case VOTER_DREP_KEY_HASH: + case VOTER_STAKE_POOL_KEY_HASH: { + BUILDER_APPEND_CBOR(CBOR_TYPE_BYTES, SIZEOF(voter->keyHash)); + BUILDER_APPEND_DATA(voter->keyHash, SIZEOF(voter->keyHash)); + break; + } + case VOTER_COMMITTEE_HOT_SCRIPT_HASH: + case VOTER_DREP_SCRIPT_HASH: { + BUILDER_APPEND_CBOR(CBOR_TYPE_BYTES, SIZEOF(voter->scriptHash)); + BUILDER_APPEND_DATA(voter->scriptHash, SIZEOF(voter->scriptHash)); + break; + } + default: + ASSERT(false); + } + } + { + // only 1 vote for the voter is supported + BUILDER_APPEND_CBOR(CBOR_TYPE_MAP, 1); + { + // governance action id + // Array(2)[ + // Bytes[hash], + // Unsigned[index] + // ] + BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 2); + { + size_t size = SIZEOF(govActionId->txHashBuffer); + ASSERT(size == TX_HASH_LENGTH); + BUILDER_APPEND_CBOR(CBOR_TYPE_BYTES, size); + BUILDER_APPEND_DATA(govActionId->txHashBuffer, size); + } + { + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, govActionId->govActionIndex); + } + } + { + // voting procedure + // Array(2)[ + // Unsigned[vote] + // Null / ...anchor + // ] + BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 2); + { + // vote + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, votingProcedure->vote); + } + { + _appendAnchor(builder, &votingProcedure->anchor); + } + } + } +} + + +static void txHashBuilder_assertCanLeaveVotingProcedures(tx_hash_builder_t* builder) +{ + _TRACE("state = %d", builder->state); + + switch (builder->state) { + case TX_HASH_BUILDER_IN_VOTING_PROCEDURES: + // make sure there are no more voting procedures to process + ASSERT(builder->remainingVotingProcedures == 0); + break; + + default: + // make sure no voting procedures are expected + ASSERT(builder->remainingVotingProcedures == 0); + // assert we can leave the previous state + txHashBuilder_assertCanLeaveReferenceInputs(builder); + break; + } +} + +// ============================== TREASURY ============================== + +void txHashBuilder_addTreasury(tx_hash_builder_t* builder, uint64_t treasury) +{ + _TRACE("state = %d", builder->state); + + txHashBuilder_assertCanLeaveVotingProcedures(builder); + + // add treasury item into the main tx body map + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, TX_BODY_KEY_TREASURY); + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, treasury); + + builder->state = TX_HASH_BUILDER_IN_TREASURY; +} + +static void txHashBuilder_assertCanLeaveTreasury(tx_hash_builder_t* builder) +{ + _TRACE("state = %d", builder->state); + + switch (builder->state) { + case TX_HASH_BUILDER_IN_TREASURY: + // treasury item was added, we can move on + break; + + default: + // make sure treasury was not expected + ASSERT(!builder->includeTreasury); + // assert we can leave the previous state + txHashBuilder_assertCanLeaveVotingProcedures(builder); + break; + } +} + +// ============================== DONATION ============================== + +void txHashBuilder_addDonation(tx_hash_builder_t* builder, uint64_t donation) +{ + _TRACE("state = %d", builder->state); + + txHashBuilder_assertCanLeaveTreasury(builder); + + // add donation item into the main tx body map + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, TX_BODY_KEY_DONATION); + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, donation); + + builder->state = TX_HASH_BUILDER_IN_DONATION; +} + +static void txHashBuilder_assertCanLeaveDonation(tx_hash_builder_t* builder) +{ + _TRACE("state = %d", builder->state); + + switch (builder->state) { + case TX_HASH_BUILDER_IN_DONATION: + // donation item was added, we can move on + break; + + default: + // make sure donation was not expected + ASSERT(!builder->includeDonation); + // assert we can leave the previous state + txHashBuilder_assertCanLeaveTreasury(builder); + break; + } +} + // ========================= FINALIZE ========================== void txHashBuilder_finalize(tx_hash_builder_t* builder, uint8_t* outBuffer, size_t outSize) { - txHashBuilder_assertCanLeaveReferenceInputs(builder); + txHashBuilder_assertCanLeaveDonation(builder); ASSERT(outSize == TX_HASH_LENGTH); { diff --git a/src/txHashBuilder.h b/src/txHashBuilder.h index 075ae583..53e77017 100644 --- a/src/txHashBuilder.h +++ b/src/txHashBuilder.h @@ -18,6 +18,28 @@ typedef struct { }; } credential_t; +typedef enum { + DREP_KEY_HASH = 0, + DREP_SCRIPT_HASH = 1, + DREP_ALWAYS_ABSTAIN = 2, + DREP_ALWAYS_NO_CONFIDENCE = 3, +} drep_type_t; + +typedef struct { + drep_type_t type; + union { + uint8_t keyHash[ADDRESS_KEY_HASH_LENGTH]; + uint8_t scriptHash[SCRIPT_HASH_LENGTH]; + }; +} drep_t; + +typedef struct { + bool isIncluded; + uint8_t url[ANCHOR_URL_LENGTH_MAX]; + size_t urlLength; + uint8_t hash[ANCHOR_HASH_LENGTH]; +} anchor_t; + typedef enum { ARRAY_LEGACY = 0, // legacy_transaction_output MAP_BABBAGE = 1 // post_alonzo_transaction_output @@ -58,6 +80,39 @@ typedef struct { }; } tx_output_destination_t; +typedef enum { + VOTER_COMMITTEE_HOT_KEY_HASH = 0, + VOTER_COMMITTEE_HOT_SCRIPT_HASH = 1, + VOTER_DREP_KEY_HASH = 2, + VOTER_DREP_SCRIPT_HASH = 3, + VOTER_STAKE_POOL_KEY_HASH = 4, +} voter_type_t; + +typedef struct { + voter_type_t type; + union { + uint8_t keyHash[ADDRESS_KEY_HASH_LENGTH]; + uint8_t scriptHash[SCRIPT_HASH_LENGTH]; + }; +} voter_t; + +typedef struct { + uint8_t txHashBuffer[TX_HASH_LENGTH]; + uint32_t govActionIndex; +} gov_action_id_t; + +typedef enum { + VOTE_NO = 0, + VOTE_YES = 1, + VOTE_ABSTAIN = 2, +} vote_t; + +typedef struct { + vote_t vote; + anchor_t anchor; +} voting_procedure_t; + + enum { TX_BODY_KEY_INPUTS = 0, TX_BODY_KEY_OUTPUTS = 1, @@ -76,6 +131,10 @@ enum { TX_BODY_KEY_COLLATERAL_OUTPUT = 16, TX_BODY_KEY_TOTAL_COLLATERAL = 17, TX_BODY_KEY_REFERENCE_INPUTS = 18, + TX_BODY_KEY_VOTING_PROCEDURES = 19, + // TX_BODY_KEY_PROPOSAL_PROCEDURES = 20, // not used + TX_BODY_KEY_TREASURY = 21, + TX_BODY_KEY_DONATION = 22, }; enum { @@ -121,7 +180,10 @@ typedef enum { TX_HASH_BUILDER_IN_COLLATERAL_OUTPUT = 1500, TX_HASH_BUILDER_IN_TOTAL_COLLATERAL = 1600, TX_HASH_BUILDER_IN_REFERENCE_INPUTS = 1700, - TX_HASH_BUILDER_FINISHED = 1800, + TX_HASH_BUILDER_IN_VOTING_PROCEDURES = 1800, + TX_HASH_BUILDER_IN_TREASURY = 1900, + TX_HASH_BUILDER_IN_DONATION = 2000, + TX_HASH_BUILDER_FINISHED = 2100, } tx_hash_builder_state_t; typedef enum { @@ -141,6 +203,7 @@ typedef struct { uint16_t remainingCollateralInputs; uint16_t remainingRequiredSigners; uint16_t remainingReferenceInputs; + uint16_t remainingVotingProcedures; bool includeTtl; bool includeAuxData; bool includeValidityIntervalStart; @@ -149,6 +212,8 @@ typedef struct { bool includeNetworkId; bool includeCollateralOutput; bool includeTotalCollateral; + bool includeTreasury; + bool includeDonation; union { struct { @@ -157,6 +222,7 @@ typedef struct { } poolCertificateData; struct { + tx_hash_builder_output_state_t outputState; tx_output_serialization_format_t serializationFormat; bool includeDatum; bool includeRefScript; @@ -177,9 +243,8 @@ typedef struct { size_t remainingBytes; } referenceScriptData; }; - } outputData; // TODO rename to output? + } outputData; }; - tx_hash_builder_output_state_t outputState; // TODO move to outputData above tx_hash_builder_state_t state; @@ -213,7 +278,10 @@ void txHashBuilder_init( bool includeNetworkId, bool includeCollateralOutput, bool includeTotalCollateral, - uint16_t numReferenceInputs + uint16_t numReferenceInputs, + uint16_t numVotingProcedures, + bool includeTreasury, + bool includeDonation ); void txHashBuilder_enterInputs(tx_hash_builder_t* builder); @@ -263,18 +331,61 @@ void txHashBuilder_addTtl(tx_hash_builder_t* builder, uint64_t ttl); void txHashBuilder_enterCertificates(tx_hash_builder_t* builder); -void txHashBuilder_addCertificate_stakingHash( +void txHashBuilder_addCertificate_stakingOld( tx_hash_builder_t* builder, const certificate_type_t certificateType, const credential_t* stakingCredential ); +void txHashBuilder_addCertificate_staking( + tx_hash_builder_t* builder, + const certificate_type_t certificateType, + const credential_t* stakeCredential, + uint64_t deposit +); -void txHashBuilder_addCertificate_delegation( +void txHashBuilder_addCertificate_stakeDelegation( tx_hash_builder_t* builder, const credential_t* stakeCredential, const uint8_t* poolKeyHash, size_t poolKeyHashSize ); +void txHashBuilder_addCertificate_voteDelegation( + tx_hash_builder_t* builder, + const credential_t* stakeCredential, + const drep_t* drep +); + +void txHashBuilder_addCertificate_committeeAuthHot( + tx_hash_builder_t* builder, + const credential_t* coldCredential, + const credential_t* hotCredential +); + +void txHashBuilder_addCertificate_committeeResign( + tx_hash_builder_t* builder, + const credential_t* coldCredential, + const anchor_t* anchor +); + +void txHashBuilder_addCertificate_dRepRegistration( + tx_hash_builder_t* builder, + const credential_t* dRepCredential, + uint64_t deposit, + const anchor_t* anchor +); + +void txHashBuilder_addCertificate_dRepDeregistration( + tx_hash_builder_t* builder, + const credential_t* dRepCredential, + uint64_t deposit +); + +void txHashBuilder_addCertificate_dRepUpdate( + tx_hash_builder_t* builder, + const credential_t* dRepCredential, + const anchor_t* anchor +); + #ifdef APP_FEATURE_POOL_RETIREMENT void txHashBuilder_addCertificate_poolRetirement( @@ -423,14 +534,23 @@ void txHashBuilder_addReferenceInput( const tx_input_t* refInput ); +void txHashBuilder_enterVotingProcedures(tx_hash_builder_t* builder); + +void txHashBuilder_addVotingProcedure( + tx_hash_builder_t* builder, + voter_t* voter, + gov_action_id_t* govActionId, + voting_procedure_t* votingProcedure +); + +void txHashBuilder_addTreasury(tx_hash_builder_t* builder, uint64_t treasury); + +void txHashBuilder_addDonation(tx_hash_builder_t* builder, uint64_t donation); + void txHashBuilder_finalize( tx_hash_builder_t* builder, uint8_t* outBuffer, size_t outSize ); -#if defined(DEVEL) && !defined(APP_XS) -void run_txHashBuilder_test(); -#endif // DEVEL - #endif // H_CARDANO_APP_TX_HASH_BUILDER diff --git a/src/txHashBuilder_test.c b/src/txHashBuilder_test.c deleted file mode 100644 index 9fbb25df..00000000 --- a/src/txHashBuilder_test.c +++ /dev/null @@ -1,565 +0,0 @@ -#if defined(DEVEL) && !defined(APP_XS) - -#include "txHashBuilder.h" -#include "cardano.h" -#include "hexUtils.h" -#include "textUtils.h" -#include "testUtils.h" - - -static const struct { - const char* txHashHex; - int index; -} inputs[] = { - { - "0B40265111D8BB3C3C608D95B3A0BF83461ACE32D79336579A1939B3AAD1C0B7", - 0 - }, - { - "1B40265111D8BB3C3C608D95B3A0BF83461ACE32D79336579A1939B3AAD1C0B7", - 1 - }, - { - "2B40265111D8BB3C3C608D95B3A0BF83461ACE32D79336579A1939B3AAD1C0B7", - 2 - }, - { - "3B40265111D8BB3C3C608D95B3A0BF83461ACE32D79336579A1939B3AAD1C0B7", - 3 - }, -}; - -static const struct { - const char* rawAddressHex; - uint64_t amount; -} outputs[] = { - { - "82D818582183581C6EE5BB111C8771CE03278E624056A12C9CFB353EB112E8ABF21FA4FEA0001A74EEE408", - 100 - }, - { - "009493315CD92EB5D8C4304E67B7E16AE36D61D34502694657811A2C8E32C728D3861E164CAB28CB8F006448139C8F1740FFB8E7AA9E5232DC", - 200 - }, - { - "409493315CD92EB5D8C4304E67B7E16AE36D61D34502694657811A2C8E87688F509738", - 300 - }, - { - "609493315CD92EB5D8C4304E67B7E16AE36D61D34502694657811A2C8E", - 400 - }, - { - "609493315CD92EB5D8C4304E67B7E16AE36D61D34502694657811A2C8E", - 500 - }, -}; - -static struct { - const char* stakingKeyHash; -} registrationCertificates[] = { - { - "32C728D3861E164CAB28CB8F006448139C8F1740FFB8E7AA9E5232DC" - }, -}; - -static struct { - const char* stakingKeyHash; -} deregistrationCertificates[] = { - { - "32C728D3861E164CAB28CB8F006448139C8F1740FFB8E7AA9E5232DC" - }, - { - "337B62CFFF6403A06A3ACBC34F8C46003C69FE79A3628CEFA9C47251" - }, -}; - -static struct { - const char* stakingKeyHash; - const char* poolKeyHash; -} delegationCertificates[] = { - { - "32C728D3861E164CAB28CB8F006448139C8F1740FFB8E7AA9E5232DC", - "0D13015CDBCDBD0889CE276192A1601F2D4D20B8392D4EF4F9A754E2" - }, - { - "32C728D3861E164CAB28CB8F006448139C8F1740FFB8E7AA9E5232DC", - "1D13015CDBCDBD0889CE276192A1601F2D4D20B8392D4EF4F9A754E2" - }, - { - "32C728D3861E164CAB28CB8F006448139C8F1740FFB8E7AA9E5232DC", - "2D13015CDBCDBD0889CE276192A1601F2D4D20B8392D4EF4F9A754E2" - }, -}; - -static struct { - const char* rewardAddress; - uint64_t amount; -} withdrawals[] = { - { - "E032C728D3861E164CAB28CB8F006448139C8F1740FFB8E7AA9E5232DC", - 666 - } -}; - -static const char* expectedHex = "7d772be6f4bebee00b469ca2793b3636594a9b16267cf23ae40236065387b3f0"; -static const char* scriptDataHash = "853cbe68f7fccdeeeb0fd7b711ea147912190c35ac52d9d94080ae82809b2f84"; - -typedef void(*addTokenGroupFun)(tx_hash_builder_t* builder, - const uint8_t* policyIdBuffer, size_t policyIdSize, - uint16_t numTokens); -typedef void(*addTokenFun)(tx_hash_builder_t* builder, - const uint8_t* assetNameBuffer, size_t assetNameSize, - uint64_t amount); - -static void addTwoMultiassetTokenGroups(tx_hash_builder_t* builder, - addTokenGroupFun tokenGroupAdder, addTokenFun tokenAdder) -{ - // we reuse the buffers to avoid wasting stack - uint8_t policy[MINTING_POLICY_ID_SIZE] = {0}; - explicit_bzero(policy, SIZEOF(policy)); - - uint8_t assetNameBuffer[ASSET_NAME_SIZE_MAX] = {0}; - explicit_bzero(assetNameBuffer, SIZEOF(assetNameBuffer)); - - policy[0] = 1; - tokenGroupAdder(builder, policy, SIZEOF(policy), 2); - - assetNameBuffer[0] = 11; - tokenAdder(builder, assetNameBuffer, SIZEOF(assetNameBuffer), 110); - assetNameBuffer[0] = 12; - tokenAdder(builder, assetNameBuffer, SIZEOF(assetNameBuffer), 120); - - policy[0] = 2; - tokenGroupAdder(builder, policy, SIZEOF(policy), 2); - - assetNameBuffer[0] = 21; - tokenAdder(builder, assetNameBuffer, SIZEOF(assetNameBuffer), 210); - assetNameBuffer[0] = 22; - // use a short buffer on purpose - tokenAdder(builder, assetNameBuffer, 1, 220); -} - -static void mintTokenHandler(tx_hash_builder_t* builder, - const uint8_t* assetNameBuffer, size_t assetNameSize, - uint64_t amount) -{ - txHashBuilder_addMint_token(builder, assetNameBuffer, assetNameSize, (int64_t)amount); -} - -static void addMultiassetMint(tx_hash_builder_t* builder) -{ - txHashBuilder_addMint_topLevelData(builder, 2); - addTwoMultiassetTokenGroups(builder, &txHashBuilder_addMint_tokenGroup, &mintTokenHandler); -} - -static void addMint(tx_hash_builder_t* builder) -{ - txHashBuilder_enterMint(builder); - - addMultiassetMint(builder); -} - -static void outputTokenHandler( - tx_hash_builder_t* builder, - const uint8_t* assetNameBuffer, size_t assetNameSize, - uint64_t amount -) -{ - txHashBuilder_addOutput_token(builder, assetNameBuffer, assetNameSize, amount); -} - - -static void addMultiassetOutput(tx_hash_builder_t* builder, tx_output_serialization_format_t const* outputFormat) -{ - uint8_t tmp[70] = {0}; - size_t tmpSize = decode_hex(PTR_PIC(outputs[1].rawAddressHex), tmp, SIZEOF(tmp)); - tx_output_description_t output = { - .format = (*outputFormat), - .destination = { - .type = DESTINATION_THIRD_PARTY, - .address = { - .buffer = tmp, - .size = tmpSize, - }, - }, - .amount = outputs[1].amount, - .numAssetGroups = 2, - .includeDatum = false, - .includeRefScript = false - }; - txHashBuilder_addOutput_topLevelData(builder, &output); - - addTwoMultiassetTokenGroups(builder, &txHashBuilder_addOutput_tokenGroup, &outputTokenHandler); -} - -static void addOutputs(tx_hash_builder_t* builder) -{ - txHashBuilder_enterOutputs(builder); - - tx_output_serialization_format_t outputFormat = ARRAY_LEGACY; - addMultiassetOutput(builder, &outputFormat); - - ITERATE(it, outputs) { - uint8_t tmp[70] = {0}; - size_t tmpSize = decode_hex(PTR_PIC(it->rawAddressHex), tmp, SIZEOF(tmp)); - tx_output_description_t output = { - .format = outputFormat, - .destination = { - .type = DESTINATION_THIRD_PARTY, - .address = { - .size = tmpSize, - .buffer = tmp, - } - }, - .amount = it->amount, - .numAssetGroups = 0, - .includeDatum = false, - .includeRefScript = false - }; - txHashBuilder_addOutput_topLevelData( - builder, - &output - ); - } - - // added for the second time to more thoroughly check the state machine - addMultiassetOutput(builder, &outputFormat); - - //New output format - outputFormat = MAP_BABBAGE; - - addMultiassetOutput(builder, &outputFormat); - - ITERATE(it, outputs) { - uint8_t tmp[70] = {0}; - size_t tmpSize = decode_hex(PTR_PIC(it->rawAddressHex), tmp, SIZEOF(tmp)); - - tx_output_description_t output = { - .format = outputFormat, - .destination = { - .type = DESTINATION_THIRD_PARTY, - .address = { - .buffer = tmp, - .size = tmpSize, - }, - }, - .amount = it->amount, - .numAssetGroups = 0, - .includeDatum = false, - .includeRefScript = false - }; - txHashBuilder_addOutput_topLevelData(builder, &output); - } - - // added for the second time to more thoroughly check the state machine - addMultiassetOutput(builder, &outputFormat); -} - -static void collateralOutputTokenHandler(tx_hash_builder_t* builder, - const uint8_t* assetNameBuffer, size_t assetNameSize, - uint64_t amount) -{ - txHashBuilder_addCollateralOutput_token(builder, assetNameBuffer, assetNameSize, (int64_t)amount); -} -static void addMultiassetCollateralOutput(tx_hash_builder_t* builder, tx_output_serialization_format_t outputFormat) -{ - uint8_t tmp[70] = {0}; - size_t tmpSize = decode_hex(PTR_PIC(outputs[1].rawAddressHex), tmp, SIZEOF(tmp)); - tx_output_description_t output = { - .format = outputFormat, - .destination = { - .type = DESTINATION_THIRD_PARTY, - .address = { - .buffer = tmp, - .size = tmpSize, - }, - }, - .amount = outputs[1].amount, - .numAssetGroups = 2, - .includeDatum = false, - .includeRefScript = false - }; - txHashBuilder_addCollateralOutput(builder, &output); - - addTwoMultiassetTokenGroups(builder, &txHashBuilder_addCollateralOutput_tokenGroup, &collateralOutputTokenHandler); -} - -static void addCollateralOutput(tx_hash_builder_t* builder) -{ - addMultiassetCollateralOutput(builder, MAP_BABBAGE); -} - -static void addPoolRegistrationCertificate(tx_hash_builder_t* builder) -{ - uint8_t poolKeyHash[POOL_KEY_HASH_LENGTH] = {0}; - uint8_t vrfKeyHash[VRF_KEY_HASH_LENGTH] = {0}; - uint64_t pledge = 500000000; - uint64_t cost = 340000000; - uint64_t marginNumerator = 1; - uint64_t marginDenominator = 1; - uint8_t rewardAccount[REWARD_ACCOUNT_SIZE] = {0}; - - size_t poolKeyHashSize = decode_hex( - "5631EDE662CFB10FD5FD69B4667101DD289568E12BCF5F64D1C406FC", - poolKeyHash, SIZEOF(poolKeyHash) - ); - ASSERT(poolKeyHashSize == SIZEOF(poolKeyHash)); - - size_t vrfKeyHashSize = decode_hex( - "198890AD6C92E80FBDAB554DDA02DA9FB49D001BBD96181F3E07F7A6AB0D0640", - vrfKeyHash, SIZEOF(vrfKeyHash) - ); - ASSERT(vrfKeyHashSize == SIZEOF(vrfKeyHash)); - - size_t rewardAccountSize = decode_hex( - "E03A7F09D3DF4CF66A7399C2B05BFA234D5A29560C311FC5DB4C490711", - rewardAccount, SIZEOF(rewardAccount) - ); - ASSERT(rewardAccountSize == SIZEOF(rewardAccount)); - - txHashBuilder_poolRegistrationCertificate_enter(builder, 1, 3); - txHashBuilder_poolRegistrationCertificate_poolKeyHash(builder, poolKeyHash, SIZEOF(poolKeyHash)); - txHashBuilder_poolRegistrationCertificate_vrfKeyHash(builder, vrfKeyHash, SIZEOF(vrfKeyHash)); - txHashBuilder_poolRegistrationCertificate_financials(builder, pledge, cost, marginNumerator, marginDenominator); - txHashBuilder_poolRegistrationCertificate_rewardAccount(builder, rewardAccount, SIZEOF(rewardAccount)); - - txHashBuilder_addPoolRegistrationCertificate_enterOwners(builder); - - uint8_t owner1[28] = {0}; - size_t owner1Size = decode_hex("3A7F09D3DF4CF66A7399C2B05BFA234D5A29560C311FC5DB4C490711", owner1, SIZEOF(owner1)); - ASSERT(owner1Size == SIZEOF(owner1)); - - txHashBuilder_addPoolRegistrationCertificate_addOwner(builder, owner1, owner1Size); - - txHashBuilder_addPoolRegistrationCertificate_enterRelays(builder); - - { - pool_relay_t relay0; - relay0.format = 0; - relay0.port.isNull = false; - relay0.port.number = 1234; - relay0.ipv4.isNull = false; - decode_hex("08080808", relay0.ipv4.ip, IPV4_SIZE); - relay0.ipv6.isNull = true; - txHashBuilder_addPoolRegistrationCertificate_addRelay(builder, &relay0); - } - { - pool_relay_t relay1; - relay1.format = 1; - relay1.port.isNull = true; - // a valid DNS AAAA record, since dnsName actually is supposed to be an A or AAAA record - const char* dnsName = "AAAA 2400:cb00:2049:1::a29f:1804"; - relay1.dnsNameSize = str_textToBuffer(dnsName, relay1.dnsName, SIZEOF(relay1.dnsName)); - txHashBuilder_addPoolRegistrationCertificate_addRelay(builder, &relay1); - } - { - pool_relay_t relay2; - relay2.format = 2; - // dnsName is not a valid DNS SRV record, but we don't validate it - const char* dnsName = "AAAA 2400:cb00:2049:1::a29f:1804"; - relay2.dnsNameSize = str_textToBuffer(dnsName, relay2.dnsName, SIZEOF(relay2.dnsName)); - txHashBuilder_addPoolRegistrationCertificate_addRelay(builder, &relay2); - } - - uint8_t metadataHash[32] = {0}; - size_t metadataHashSize = decode_hex("914C57C1F12BBF4A82B12D977D4F274674856A11ED4B9B95BD70F5D41C5064A6", metadataHash, SIZEOF(metadataHash)); - ASSERT(metadataHashSize == SIZEOF(metadataHash)); - - const char* metadataUrl = "https://teststakepool.com"; - uint8_t urlBuffer[DNS_NAME_SIZE_MAX] = {0}; - size_t urlSize = str_textToBuffer(metadataUrl, urlBuffer, SIZEOF(urlBuffer)); - ASSERT(urlSize <= DNS_NAME_SIZE_MAX); - - txHashBuilder_addPoolRegistrationCertificate_addPoolMetadata(builder, urlBuffer, urlSize, metadataHash, metadataHashSize); -} - -static void addPoolRetirementCertificate(tx_hash_builder_t* builder) -{ - uint8_t poolKeyHash[POOL_KEY_HASH_LENGTH] = {0}; - uint64_t epoch = 1000; - - size_t poolKeyHashSize = decode_hex( - "5631EDE662CFB10FD5FD69B4667101DD289568E12BCF5F64D1C406FC", - poolKeyHash, SIZEOF(poolKeyHash) - ); - ASSERT(poolKeyHashSize == SIZEOF(poolKeyHash)); - - txHashBuilder_addCertificate_poolRetirement( - builder, - poolKeyHash, SIZEOF(poolKeyHash), - epoch - ); -} - -static void addCertificates(tx_hash_builder_t* builder) -{ - txHashBuilder_enterCertificates(builder); - - ITERATE(it, registrationCertificates) { - credential_t credential = { - .type = CREDENTIAL_KEY_HASH - }; - decode_hex(PTR_PIC(it->stakingKeyHash), credential.keyHash, SIZEOF(credential.keyHash)); - txHashBuilder_addCertificate_stakingHash( - builder, - CERTIFICATE_TYPE_STAKE_REGISTRATION, - &credential - ); - } - - ITERATE(it, deregistrationCertificates) { - credential_t credential = { - .type = CREDENTIAL_KEY_HASH - }; - decode_hex(PTR_PIC(it->stakingKeyHash), credential.keyHash, SIZEOF(credential.keyHash)); - txHashBuilder_addCertificate_stakingHash( - builder, - CERTIFICATE_TYPE_STAKE_DEREGISTRATION, - &credential - ); - } - - addPoolRegistrationCertificate(builder); - - addPoolRetirementCertificate(builder); - - ITERATE(it, delegationCertificates) { - credential_t credential = { - .type = CREDENTIAL_KEY_HASH - }; - decode_hex(PTR_PIC(it->stakingKeyHash), credential.keyHash, SIZEOF(credential.keyHash)); - uint8_t tmp_pool[70] = {0}; - size_t tmpSize_pool = decode_hex(PTR_PIC(it->poolKeyHash), tmp_pool, SIZEOF(tmp_pool)); - txHashBuilder_addCertificate_delegation( - builder, - &credential, - tmp_pool, tmpSize_pool - ); - } -} - -void run_txHashBuilder_test() -{ - PRINTF("txHashBuilder test\n"); - tx_hash_builder_t builder; - - const size_t numCertificates = ARRAY_LEN(registrationCertificates) + - ARRAY_LEN(deregistrationCertificates) + - ARRAY_LEN(delegationCertificates) + - 1 + // stake pool retirement certificate - 1; // stake pool registration certificate - - txHashBuilder_init(&builder, - ARRAY_LEN(inputs), - (ARRAY_LEN(outputs) + 2) * 2, // +2 for multiasset outputs *2 for new format - true, // ttl - numCertificates, ARRAY_LEN(withdrawals), - true, // metadata - true, // validity interval start - true, // mint - true, // script hash data - 1, // collateral inputs - 1, // required - true, // network id - true, // collateral return output, - true, // total collateral, - ARRAY_LEN(inputs) // reference inputs - ); - - // 0 : set ; inputs - txHashBuilder_enterInputs(&builder); - ITERATE(it, inputs) { - uint8_t tmp[TX_HASH_LENGTH] = {0}; - size_t tmpSize = decode_hex(PTR_PIC(it->txHashHex), tmp, SIZEOF(tmp)); - tx_input_t input; - memmove(input.txHashBuffer, tmp, tmpSize); - input.index = it->index; - txHashBuilder_addInput(&builder, &input); - } - // 1 : [* transaction_output] - addOutputs(&builder); - // 2 : coin ; fee - txHashBuilder_addFee(&builder, 42); - // ? 3 : uint ; time to live - txHashBuilder_addTtl(&builder, 235000); - // ? 4 : [* certificate] - addCertificates(&builder); - // ? 5 : withdrawals - txHashBuilder_enterWithdrawals(&builder); - - ITERATE(it, withdrawals) { - uint8_t tmp[70] = {0}; - size_t tmpSize = decode_hex(PTR_PIC(it->rewardAddress), tmp, SIZEOF(tmp)); - txHashBuilder_addWithdrawal( - &builder, - tmp, tmpSize, - it->amount - ); - } - // ? 7 : auxiliary_data_hash - { - /* cspell:disable-next-line */ - const char auxDataHashHex[] = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; - uint8_t tmp[AUX_DATA_HASH_LENGTH] = {0}; - size_t tmpSize = decode_hex(auxDataHashHex, tmp, SIZEOF(tmp)); - ASSERT(tmpSize == AUX_DATA_HASH_LENGTH); - txHashBuilder_addAuxData(&builder, tmp, tmpSize); - } - // ? 8 : uint ; validity interval start - txHashBuilder_addValidityIntervalStart(&builder, 33); - // ? 9 : mint - addMint(&builder); - // ? 11 : script_data_hash - { - uint8_t scriptHashData[SCRIPT_DATA_HASH_LENGTH] = {0}; - size_t hashSize = decode_hex(scriptDataHash, scriptHashData, SIZEOF(scriptHashData)); - txHashBuilder_addScriptDataHash(&builder, scriptHashData, hashSize); - } - // ? 13 : set ; collateral inputs - { - txHashBuilder_enterCollateralInputs(&builder); - uint8_t tmp[TX_HASH_LENGTH] = {0}; - size_t tmpSize = decode_hex(PTR_PIC(inputs[0].txHashHex), tmp, SIZEOF(tmp)); - tx_input_t input; - memmove(input.txHashBuffer, tmp, tmpSize); - input.index = inputs[0].index; - txHashBuilder_addCollateralInput(&builder, &input); - } - // ? 14 : required_signers - { - uint8_t keyHash[ADDRESS_KEY_HASH_LENGTH] = {0}; - txHashBuilder_enterRequiredSigners(&builder); - txHashBuilder_addRequiredSigner(&builder, keyHash, SIZEOF(keyHash)); - } - // ? 15 : network_id - txHashBuilder_addNetworkId(&builder, 0); - // ? 16 : transaction_output ; collateral return - addCollateralOutput(&builder); - // ? 17 : coin ; total collateral - txHashBuilder_addTotalCollateral(&builder, 10); - // ? 18 : set ; reference inputs - txHashBuilder_enterReferenceInputs(&builder); - - ITERATE(it, inputs) { - uint8_t tmp[TX_HASH_LENGTH] = {0}; - size_t tmpSize = decode_hex(PTR_PIC(it->txHashHex), tmp, SIZEOF(tmp)); - tx_input_t input; - memmove(input.txHashBuffer, tmp, tmpSize); - input.index = it->index; - txHashBuilder_addReferenceInput(&builder, &input); - } - - uint8_t result[TX_HASH_LENGTH] = {0}; - txHashBuilder_finalize(&builder, result, SIZEOF(result)); - - uint8_t expected[TX_HASH_LENGTH] = {0}; - decode_hex(expectedHex, expected, SIZEOF(expected)); - - PRINTF("result\n"); - PRINTF("%.*h\n", 32, result); - - EXPECT_EQ_BYTES(result, expected, 32); -} - -#endif // DEVEL diff --git a/src/utils.h b/src/utils.h index 7d54ec09..beb5f1f9 100644 --- a/src/utils.h +++ b/src/utils.h @@ -48,7 +48,6 @@ } while(0) // Helper functions for ranges -// TODO(ppershing): make more type safe? #define BEGIN(buf) buf // Note: SIZEOF would not work if buf is not uin8_t* #define END(buf) (buf + ARRAY_LEN(buf)) From 219fbf9c87a0406836c2e84b5349e77c3acad24d Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Sat, 16 Dec 2023 20:43:56 +0100 Subject: [PATCH 14/43] update version --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 218503e8..9b2ca0a6 100644 --- a/Makefile +++ b/Makefile @@ -17,9 +17,9 @@ APPNAME = "Cardano ADA" -APPVERSION_M = 6 -APPVERSION_N = 1 -APPVERSION_P = 2 +APPVERSION_M = 7 +APPVERSION_N = 0 +APPVERSION_P = 0 APPVERSION = "$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)" ifeq ($(BOLOS_SDK),) From e086ad50817faf0590a77c3195cf9a90c5f7c3c0 Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Sat, 6 Jan 2024 15:03:10 +0100 Subject: [PATCH 15/43] feature: add cbor set tag 258 --- src/cbor.h | 2 ++ src/signTx.c | 16 ++++++++++++++++ src/signTx.h | 9 +++++++++ src/txHashBuilder.c | 18 +++++++++++++++++- src/txHashBuilder.h | 3 +++ 5 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/cbor.h b/src/cbor.h index 2614c882..1c7066eb 100644 --- a/src/cbor.h +++ b/src/cbor.h @@ -40,6 +40,8 @@ typedef enum { enum { CBOR_TAG_EMBEDDED_CBOR_BYTE_STRING = 24, + CBOR_TAG_UNIT_INTERVAL = 30, + CBOR_TAG_SET = 258, }; typedef struct { diff --git a/src/signTx.c b/src/signTx.c index d50ff0cf..ded7f094 100644 --- a/src/signTx.c +++ b/src/signTx.c @@ -91,6 +91,7 @@ void tx_advanceStage() // Note: make sure that everything in ctx is initialized properly txHashBuilder_init( &BODY_CTX->txHashBuilder, + ctx->commonTxData.tagCborSets, ctx->numInputs, ctx->numOutputs, ctx->includeTtl, @@ -475,6 +476,16 @@ static inline void CHECK_STAGE(sign_tx_stage_t expected) // ============================== INIT ============================== +static void _parseTxOptions(uint64_t options) +{ + ctx->commonTxData.tagCborSets = options & TX_OPTIONS_TAG_CBOR_SETS; + options &= ~TX_OPTIONS_TAG_CBOR_SETS; + TRACE("tagCborSets = %d", ctx->commonTxData.tagCborSets); + + // we only accept known flags + VALIDATE(options == 0, ERR_INVALID_DATA); +} + __noinline_due_to_stack__ static void signTx_handleInitAPDU(uint8_t p2, const uint8_t* wireDataBuffer, size_t wireDataSize) { @@ -492,6 +503,8 @@ static void signTx_handleInitAPDU(uint8_t p2, const uint8_t* wireDataBuffer, siz TRACE_BUFFER(wireDataBuffer, wireDataSize); struct { + uint8_t txOptions[8]; + uint8_t networkId; uint8_t protocolMagic[4]; @@ -521,6 +534,9 @@ static void signTx_handleInitAPDU(uint8_t p2, const uint8_t* wireDataBuffer, siz VALIDATE(SIZEOF(*wireHeader) == wireDataSize, ERR_INVALID_DATA); + uint64_t txOptions = u8be_read(wireHeader->txOptions); + _parseTxOptions(txOptions); + ASSERT_TYPE(ctx->commonTxData.networkId, uint8_t); ctx->commonTxData.networkId = wireHeader->networkId; TRACE("network id %d", ctx->commonTxData.networkId); diff --git a/src/signTx.h b/src/signTx.h index 43fb07ac..6890efae 100644 --- a/src/signTx.h +++ b/src/signTx.h @@ -75,6 +75,10 @@ typedef struct { uint32_t accountNumber; } single_account_data_t; +enum { + TX_OPTIONS_TAG_CBOR_SETS = 1, +}; + typedef struct { // significantly affects restrictions on the tx sign_tx_signingmode_t txSigningMode; @@ -83,6 +87,11 @@ typedef struct { uint32_t protocolMagic; // part of Byron address single_account_data_t singleAccountData; + + // there is only one flag and no more flags planned for the future + // but if there were many, it might be necessary to keep them + // packed in a single uint variable + bool tagCborSets; } common_tx_data_t; // credentials are extended to allow key derivation paths diff --git a/src/txHashBuilder.c b/src/txHashBuilder.c index ec6d514b..72f9d0b3 100644 --- a/src/txHashBuilder.c +++ b/src/txHashBuilder.c @@ -51,6 +51,12 @@ static void blake2b_256_append_cbor_tx_body( blake2b_256_append(hashCtx, buffer, size); } +#define BUILDER_TAG_CBOR_SET() \ + if (builder->tagCborSets) { \ + TRACE("appending set tag 258"); \ + BUILDER_APPEND_CBOR(CBOR_TYPE_TAG, CBOR_TAG_SET); \ + } + /* End of hash computation utilities. */ static void cbor_append_txInput( @@ -221,6 +227,7 @@ static void assertCanLeaveCurrentOutput(tx_hash_builder_t* builder) void txHashBuilder_init( tx_hash_builder_t* builder, + bool tagCborSets, uint16_t numInputs, uint16_t numOutputs, bool includeTtl, @@ -241,6 +248,7 @@ void txHashBuilder_init( bool includeDonation ) { + TRACE("tagCborSets = %u", tagCborSets); TRACE("numInputs = %u", numInputs); TRACE("numOutputs = %u", numOutputs); TRACE("includeTtl = %u", includeTtl); @@ -260,6 +268,8 @@ void txHashBuilder_init( TRACE("includeTreasury = %u", includeTreasury); TRACE("includeDonation = %u", includeDonation); + builder->tagCborSets = tagCborSets; + blake2b_256_init(&builder->txHash); { @@ -347,6 +357,7 @@ void txHashBuilder_enterInputs(tx_hash_builder_t* builder) { // Enter inputs BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, TX_BODY_KEY_INPUTS); + BUILDER_TAG_CBOR_SET(); BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, builder->remainingInputs); } builder->state = TX_HASH_BUILDER_IN_INPUTS; @@ -748,6 +759,7 @@ void txHashBuilder_enterCertificates(tx_hash_builder_t* builder) { // Enter certificates BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, TX_BODY_KEY_CERTIFICATES); + BUILDER_TAG_CBOR_SET(); BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, builder->remainingCertificates); } @@ -1269,7 +1281,7 @@ void txHashBuilder_poolRegistrationCertificate_financials( BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, cost); } { - BUILDER_APPEND_CBOR(CBOR_TYPE_TAG, 30); + BUILDER_APPEND_CBOR(CBOR_TYPE_TAG, CBOR_TAG_UNIT_INTERVAL); BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 2); { BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, marginNumerator); @@ -1310,6 +1322,7 @@ void txHashBuilder_addPoolRegistrationCertificate_enterOwners(tx_hash_builder_t* ASSERT(builder->state == TX_HASH_BUILDER_IN_CERTIFICATES_POOL_REWARD_ACCOUNT); { + BUILDER_TAG_CBOR_SET(); BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, builder->poolCertificateData.remainingOwners); } @@ -1859,6 +1872,7 @@ void txHashBuilder_enterCollateralInputs(tx_hash_builder_t* builder) { // Enter collateral inputs BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, TX_BODY_KEY_COLLATERAL_INPUTS); + BUILDER_TAG_CBOR_SET(); BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, builder->remainingCollateralInputs); } builder->state = TX_HASH_BUILDER_IN_COLLATERAL_INPUTS; @@ -1909,6 +1923,7 @@ void txHashBuilder_enterRequiredSigners(tx_hash_builder_t* builder) { // Enter required signers BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, TX_BODY_KEY_REQUIRED_SIGNERS); + BUILDER_TAG_CBOR_SET(); BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, builder->remainingRequiredSigners); } builder->state = TX_HASH_BUILDER_IN_REQUIRED_SIGNERS; @@ -2101,6 +2116,7 @@ void txHashBuilder_enterReferenceInputs(tx_hash_builder_t* builder) { // Enter reference inputs BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, TX_BODY_KEY_REFERENCE_INPUTS); + BUILDER_TAG_CBOR_SET(); BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, builder->remainingReferenceInputs); } builder->state = TX_HASH_BUILDER_IN_REFERENCE_INPUTS; diff --git a/src/txHashBuilder.h b/src/txHashBuilder.h index 53e77017..197ec9e1 100644 --- a/src/txHashBuilder.h +++ b/src/txHashBuilder.h @@ -196,6 +196,8 @@ typedef enum { } tx_hash_builder_output_state_t; typedef struct { + bool tagCborSets; + uint16_t remainingInputs; uint16_t remainingOutputs; uint16_t remainingWithdrawals; @@ -264,6 +266,7 @@ typedef struct { void txHashBuilder_init( tx_hash_builder_t* builder, + bool tagCborSets, uint16_t numInputs, uint16_t numOutputs, bool includeTtl, From 62a72e4948c40b1294bb01762915e95237ee5a2a Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Sat, 6 Jan 2024 15:56:25 +0100 Subject: [PATCH 16/43] increase max URL length --- Makefile | 2 +- doc/ins_sign_stake_pool_registration.md | 6 +++--- src/cardano.h | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 9b2ca0a6..d51676b9 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ APPNAME = "Cardano ADA" APPVERSION_M = 7 APPVERSION_N = 0 -APPVERSION_P = 0 +APPVERSION_P = 1 APPVERSION = "$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)" ifeq ($(BOLOS_SDK),) diff --git a/doc/ins_sign_stake_pool_registration.md b/doc/ins_sign_stake_pool_registration.md index 70106a6e..f8ee324c 100644 --- a/doc/ins_sign_stake_pool_registration.md +++ b/doc/ins_sign_stake_pool_registration.md @@ -157,12 +157,12 @@ P2 = `0x36` |relay format | 1 | `RELAY_SINGLE_HOST_NAME=0x01` | |isPortGiven | 1 | `ITEM_INCLUDED_NO=0x01` or `ITEM_INCLUDED_YES=0x02` | |port | 2 | Big endian; included if and only if isPortGiven is `ITEM_INCLUDED_YES` -|dns name | variable | byte buffer, max size 64 +|dns name | variable | byte buffer, max size 128 |Field| Length | Comments| |-----|--------|---------| |relay format | 1 | `RELAY_MULTIPLE_HOST_NAME=0x02` | -|dns name | variable | byte buffer, max size 64 +|dns name | variable | byte buffer, max size 128 --- @@ -175,7 +175,7 @@ P2 = `0x37` |-----|--------|---------| |includeMetadata | 1 | `ITEM_INCLUDED_NO=0x01` or `ITEM_INCLUDED_YES=0x02` | |metadata hash | 32 | byte buffer; only if includeMetadata is `ITEM_INCLUDED_YES` -|metadata url | variable | byte buffer, max size 64; only if includeMetadata is `ITEM_INCLUDED_YES` +|metadata url | variable | byte buffer, max size 128; only if includeMetadata is `ITEM_INCLUDED_YES` --- diff --git a/src/cardano.h b/src/cardano.h index 95249ac1..cc99ac2c 100644 --- a/src/cardano.h +++ b/src/cardano.h @@ -101,10 +101,10 @@ typedef struct { // ============================== CERTIFICATES ============================== -#define ANCHOR_URL_LENGTH_MAX 64 +#define ANCHOR_URL_LENGTH_MAX 128 -#define POOL_METADATA_URL_LENGTH_MAX 64 -#define DNS_NAME_SIZE_MAX 64 +#define POOL_METADATA_URL_LENGTH_MAX 128 +#define DNS_NAME_SIZE_MAX 128 #define IPV4_SIZE 4 #define IPV6_SIZE 16 From 89d1ed02e04b80da26689fff705639d2d496ba59 Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Wed, 17 Jan 2024 00:22:55 +0100 Subject: [PATCH 17/43] fix: withdrawal canonical ordering --- src/signTx.c | 11 +++++++++-- src/signTx.h | 1 - 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/signTx.c b/src/signTx.c index ded7f094..873d7851 100644 --- a/src/signTx.c +++ b/src/signTx.c @@ -1707,7 +1707,10 @@ static void signTx_handleWithdrawalAPDU(uint8_t p2, const uint8_t* wireDataBuffe VALIDATE(p2 == P2_UNUSED, ERR_INVALID_REQUEST_PARAMETERS); } - explicit_bzero(&BODY_CTX->stageData.withdrawal, SIZEOF(BODY_CTX->stageData.withdrawal)); + // we can't bzero the whole stageData.withdrawal since + // we need to compare it with the previous one (canonical ordering check) + BODY_CTX->stageData.withdrawal.amount = 0; + explicit_bzero(&BODY_CTX->stageData.withdrawal.stakeCredential, SIZEOF(BODY_CTX->stageData.withdrawal.stakeCredential)); { // parse input @@ -2588,7 +2591,6 @@ void signTx_handleAPDU( #ifdef APP_FEATURE_POOL_REGISTRATION case SIGN_STAGE_BODY_CERTIFICATES_POOL_SUBMACHINE: #endif // APP_FEATURE_POOL_REGISTRATION - case SIGN_STAGE_BODY_WITHDRAWALS: case SIGN_STAGE_BODY_VALIDITY_INTERVAL: case SIGN_STAGE_BODY_MINT: #ifdef APP_FEATURE_TOKEN_MINTING @@ -2604,6 +2606,11 @@ void signTx_handleAPDU( explicit_bzero(&BODY_CTX->stageData, SIZEOF(BODY_CTX->stageData)); break; } + + case SIGN_STAGE_BODY_WITHDRAWALS: + // we need to keep previous data for checking canonical ordering + break; + default: break; } diff --git a/src/signTx.h b/src/signTx.h index 6890efae..1fbabcd7 100644 --- a/src/signTx.h +++ b/src/signTx.h @@ -238,7 +238,6 @@ typedef struct { bool treasuryReceived; bool donationReceived; - // this holds data valid only through the processing of a single APDU union { sign_tx_transaction_input_t input; uint64_t fee; From f0c41926a33d42bb0968550e098f3cb45c143256 Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Wed, 17 Jan 2024 00:26:14 +0100 Subject: [PATCH 18/43] update bech32 prefixes --- src/signTx_ui.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/signTx_ui.c b/src/signTx_ui.c index c475ec92..b3dfa1fc 100644 --- a/src/signTx_ui.c +++ b/src/signTx_ui.c @@ -732,7 +732,7 @@ void signTx_handleCertificateVoteDeleg_ui_runStep() _displayKeyHash(this_fn, cert->drep.keyHash, "DRep key hash", "drep"); break; case EXT_DREP_SCRIPT_HASH: - _displayScriptHash(this_fn, cert->drep.scriptHash, "DRep script hash", "drep"); + _displayScriptHash(this_fn, cert->drep.scriptHash, "DRep script hash", "drep_script"); break; case DREP_ALWAYS_ABSTAIN: #ifdef HAVE_BAGL @@ -811,7 +811,7 @@ void signTx_handleCertificateCommitteeAuth_ui_runStep() "Cmte. cold key hash", "cc_cold", "Cmte. cold script", - "cc_cold" + "cc_cold_script" ); } UI_STEP(HANDLE_CERTIFICATE_COMM_AUTH_STEP_DISPLAY_HOT_CRED) { @@ -822,7 +822,7 @@ void signTx_handleCertificateCommitteeAuth_ui_runStep() "Cmte. hot key hash", "cc_hot", "Cmte. hot script", - "cc_hot" + "cc_hot_script" ); } UI_STEP(HANDLE_CERTIFICATE_COMM_AUTH_STEP_CONFIRM) { From 44f1c32eeea02783036de16a6c48a50c31b2f4f0 Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Fri, 10 Nov 2023 11:41:27 +0100 Subject: [PATCH 19/43] update changelog --- CHANGELOG.md | 25 ++++++++++++++++++++++++- Makefile | 2 +- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ec353f7..4f1f11e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,28 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## [6.0.3](TBD) - [TBD] +## [7.0.2](TBD) - [TBD] + +Conway era + +### Added + +- export of Conway-era keys (DReps, Constitutional Committee Hot and Cold keys) +- Conway era transaction body items (new certificates, voting procedures, treasury, donation) +- optional CBOR tag 258 in CDDL sets +- reduced features on Nano S (since Ledger app v7, due to memory limits) + +### Changed + +- updated list of native tokens recognized by the app with correct decimal places +- increased max. URL and DNS name length to 128 + +### Fixed + +- bug in checking canonical ordering of withdrawals + + +## [6.1.2](TBD) - [TBD] Support for CIP-36 voting @@ -15,11 +36,13 @@ Support for CIP-36 voting - export of vote keys (1694'/1815'/...) - support for CIP-36 voting (signing of vote-cast fragments with 1694 keys) - support for CIP-36 registrations (in transaction auxiliary data) +- support for the Stax device ### Changed - API for Catalyst voting registration (it is still possible to use CIP-15 in auxiliary data) - updated list of native tokens recognized by the app with correct decimal places +- multidelegation allowed (as used by Lace, i.e. stake keys do not need to end with 0 as address_index) ## [5.0.0](https://github.com/LedgerHQ/app-cardano/compare/4.1.2...LedgerHQ:nanos_2.1.0_5.0.0) - [October 11th 2022] diff --git a/Makefile b/Makefile index d51676b9..73a40b71 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ APPNAME = "Cardano ADA" APPVERSION_M = 7 APPVERSION_N = 0 -APPVERSION_P = 1 +APPVERSION_P = 2 APPVERSION = "$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)" ifeq ($(BOLOS_SDK),) From 8b0896f504acc85617bac13d008d695cf312229e Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Fri, 2 Feb 2024 15:38:09 +0100 Subject: [PATCH 20/43] audit fixes --- CHANGELOG.md | 2 +- src/signTxOutput_ui.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f1f11e4..151eff69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,7 @@ Conway era - bug in checking canonical ordering of withdrawals -## [6.1.2](TBD) - [TBD] +## [6.1.2](https://github.com/LedgerHQ/app-cardano/compare/v5.0.0...LedgerHQ:nanos_2.1.0_6.1.2_sdk_2.1.0-12) - [October 25th 2023] Support for CIP-36 voting diff --git a/src/signTxOutput_ui.h b/src/signTxOutput_ui.h index e61755a5..45994512 100644 --- a/src/signTxOutput_ui.h +++ b/src/signTxOutput_ui.h @@ -1,6 +1,5 @@ #ifndef H_CARDANO_APP_SIGN_TX_OUTPUT_UI #define H_CARDANO_APP_SIGN_TX_OUTPUT_UI -#endif // H_CARDANO_APP_SIGN_TX_OUTPUT_UI #ifdef HAVE_BAGL #include "uiScreens_bagl.h" @@ -91,3 +90,5 @@ enum { }; void signTxOutput_handleConfirm_ui_runStep(); + +#endif // H_CARDANO_APP_SIGN_TX_OUTPUT_UI From dd4cc8d969d55bb4f284f8e69cfaeb732ec9e2e4 Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Tue, 30 Jan 2024 11:39:19 +0100 Subject: [PATCH 21/43] feature: message signing (CIP-8) --- CHANGELOG.md | 14 ++ Makefile | 4 +- fuzzing/CMakeLists.txt | 9 +- fuzzing/README.md | 1 + fuzzing/src/signMsg_harness.c | 52 +++++ src/bufView.h | 13 ++ src/cardano.h | 9 + src/handlers.c | 2 + src/messageSigning.c | 11 +- src/messageSigning.h | 4 + src/runTests.h | 8 +- src/securityPolicy.c | 43 ++++ src/securityPolicy.h | 6 + src/signCVote.c | 4 +- src/signCVote_ui.c | 8 +- src/signCVote_ui.h | 5 +- src/signMsg.c | 375 ++++++++++++++++++++++++++++++++++ src/signMsg.h | 56 +++++ src/signMsg_ui.c | 319 +++++++++++++++++++++++++++++ src/signMsg_ui.h | 43 ++++ src/signOpCert.c | 2 + src/signOpCert.h | 3 +- src/signTx.h | 3 +- src/signTxCVoteRegistration.c | 2 +- src/signTxOutput.c | 13 +- src/signTxPoolRegistration.c | 2 +- src/state.h | 2 + src/textUtils.c | 3 +- src/textUtils.h | 2 +- src/tokens_test.c | 2 +- src/uiScreens_bagl.c | 4 +- src/ui_nbgl.c | 3 + 32 files changed, 994 insertions(+), 33 deletions(-) create mode 100644 fuzzing/src/signMsg_harness.c create mode 100644 src/signMsg.c create mode 100644 src/signMsg.h create mode 100644 src/signMsg_ui.c create mode 100644 src/signMsg_ui.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 151eff69..4ca4ef16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,20 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [7.1.0](TBD) - [TBD] + +Message signing (CIP-8) + +### Added + +- support for basic message signing (CIP-8, CIP-30) + +### Changed + +- usage of chunks of maximum allowed size is now enforced (datums and reference scripts in outputs) +- TODO updated list of native tokens recognized by the app with correct decimal places + + ## [7.0.2](TBD) - [TBD] Conway era diff --git a/Makefile b/Makefile index 73a40b71..9f20aeac 100644 --- a/Makefile +++ b/Makefile @@ -18,8 +18,8 @@ APPNAME = "Cardano ADA" APPVERSION_M = 7 -APPVERSION_N = 0 -APPVERSION_P = 2 +APPVERSION_N = 1 +APPVERSION_P = 0 APPVERSION = "$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)" ifeq ($(BOLOS_SDK),) diff --git a/fuzzing/CMakeLists.txt b/fuzzing/CMakeLists.txt index 0d324582..6cda3a91 100644 --- a/fuzzing/CMakeLists.txt +++ b/fuzzing/CMakeLists.txt @@ -77,6 +77,8 @@ set(CARDANO_SOURCE ${CARDANO_PATH}/src/securityPolicy.c ${CARDANO_PATH}/src/signCVote.c ${CARDANO_PATH}/src/signCVote_ui.c + ${CARDANO_PATH}/src/signMsg.c + ${CARDANO_PATH}/src/signMsg_ui.c ${CARDANO_PATH}/src/signOpCert.c ${CARDANO_PATH}/src/signTx.c ${CARDANO_PATH}/src/signTxCVoteRegistration.c @@ -153,7 +155,7 @@ add_compile_definitions( APP_FEATURE_TOKEN_MINTING ) -set(SOURCE +set(SOURCE ${UX_SOURCE} ${CARDANO_SOURCE} ./src/os_mocks.c @@ -169,13 +171,14 @@ set(harnesses deriveNativeScriptHash_harness getPublicKeys_harness signCVote_harness + signMsg_harness signOpCert_harness signTx_harness ) foreach(harness IN LISTS harnesses) - add_executable(${harness} + add_executable(${harness} ./src/${harness}.c - ) + ) target_link_libraries(${harness} PUBLIC cardano) endforeach() \ No newline at end of file diff --git a/fuzzing/README.md b/fuzzing/README.md index a4688c84..fb18670a 100644 --- a/fuzzing/README.md +++ b/fuzzing/README.md @@ -19,6 +19,7 @@ deriveAddress_harness deriveNativeScriptHash_harness getPublicKeys_harness signCVote_harness +signMsg_harness signOpCert_harness signTx_harness ``` diff --git a/fuzzing/src/signMsg_harness.c b/fuzzing/src/signMsg_harness.c new file mode 100644 index 00000000..258a0b9c --- /dev/null +++ b/fuzzing/src/signMsg_harness.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include +#include + +uint8_t G_io_apdu_buffer[IO_APDU_BUFFER_SIZE]; + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + + UX_INIT(); + + uint8_t *input = NULL; + bool is_first = true; + + while (size > 5) { + io_state = IO_EXPECT_NONE; + uint8_t ins = data[0]; + uint8_t p1 = data[1]; + uint8_t p2 = data[2]; + uint8_t lc = data[3]; + + data += sizeof(uint8_t) * 4; + size -= sizeof(uint8_t) * 4; + + if (size < lc) { + return 0; + } + + uint8_t *input = malloc(lc); + if (input == NULL) { + return 0; + } + + memcpy(input, data, lc); + + data += lc; + size -= lc; + + BEGIN_TRY { + TRY { signMsg_handleAPDU(p1, p2, input, lc, is_first); } + CATCH_ALL {} + FINALLY {} + } + END_TRY; + + is_first = false; + free(input); + } + return 0; +} \ No newline at end of file diff --git a/src/bufView.h b/src/bufView.h index 53c1d9ef..00735a41 100644 --- a/src/bufView.h +++ b/src/bufView.h @@ -142,5 +142,18 @@ static inline int64_t parse_int64be(read_view_t* view) return (int64_t) parse_u8be(view); }; +static inline bool parse_bool(read_view_t* view) +{ + uint8_t value = parse_u1be(view); + + switch (value) { + case 0: + return false; + case 1: + return true; + default: + THROW(ERR_INVALID_DATA); + } +} #endif // H_CARDANO_APP_BUF_VIEW diff --git a/src/cardano.h b/src/cardano.h index cc99ac2c..390b9f8a 100644 --- a/src/cardano.h +++ b/src/cardano.h @@ -13,6 +13,8 @@ STATIC_ASSERT(LOVELACE_MAX_SUPPLY < LOVELACE_INVALID, "bad LOVELACE_INVALID"); +#define ED25519_SIGNATURE_LENGTH 64 + #define ADDRESS_KEY_HASH_LENGTH 28 #define POOL_KEY_HASH_LENGTH 28 #define VRF_KEY_HASH_LENGTH 32 @@ -180,4 +182,11 @@ typedef enum { #endif // APP_FEATURE_NATIVE_SCRIPT_HASH +// ============================== CIP8 MESSAGE SIGNING ============================== + +typedef enum { + CIP8_ADDRESS_FIELD_ADDRESS = 1, + CIP8_ADDRESS_FIELD_KEYHASH = 2, +} cip8_address_field_type_t; + #endif // H_CARDANO_APP_CARDANO diff --git a/src/handlers.c b/src/handlers.c index db9792f4..dd90fdb5 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -12,6 +12,7 @@ #include "deriveAddress.h" #include "deriveNativeScriptHash.h" #include "signTx.h" +#include "signMsg.h" #include "signOpCert.h" #include "signCVote.h" @@ -39,6 +40,7 @@ handler_fn_t* lookupHandler(uint8_t ins) CASE(0x22, signOpCert_handleAPDU); #endif // APP_FEATURE_OPCERT CASE(0x23, signCVote_handleAPDU); + CASE(0x24, signMsg_handleAPDU); #ifdef DEVEL // 0xF* - debug_mode related diff --git a/src/messageSigning.c b/src/messageSigning.c index 4d495ff1..4c2d4fd6 100644 --- a/src/messageSigning.c +++ b/src/messageSigning.c @@ -6,9 +6,9 @@ #include "securityPolicy.h" #include "crypto.h" -static void signRawMessageWithPath(bip44_path_t* pathSpec, - const uint8_t* messageBuffer, size_t messageSize, - uint8_t* outBuffer, size_t outSize) +void signRawMessageWithPath(bip44_path_t* pathSpec, + const uint8_t* messageBuffer, size_t messageSize, + uint8_t* outBuffer, size_t outSize) { size_t sigLen = outSize; @@ -23,6 +23,10 @@ static void signRawMessageWithPath(bip44_path_t* pathSpec, #ifndef FUZZING { + TRACE("signing with path:"); + BIP44_PRINTF(pathSpec); + PRINTF("\n"); + cx_err_t error = crypto_eddsa_sign(pathSpec->path, pathSpec->length, messageBuffer, @@ -37,7 +41,6 @@ static void signRawMessageWithPath(bip44_path_t* pathSpec, #endif ASSERT(sigLen == ED25519_SIGNATURE_LENGTH); - } // sign the given hash by the private key derived according to the given path diff --git a/src/messageSigning.h b/src/messageSigning.h index e2db31f8..4984df72 100644 --- a/src/messageSigning.h +++ b/src/messageSigning.h @@ -3,6 +3,10 @@ #include "bip44.h" +void signRawMessageWithPath(bip44_path_t* pathSpec, + const uint8_t* messageBuffer, size_t messageSize, + uint8_t* outBuffer, size_t outSize); + void getWitness(bip44_path_t* pathSpec, const uint8_t* txHashBuffer, size_t txHashSize, uint8_t* outBuffer, size_t outSize); diff --git a/src/runTests.h b/src/runTests.h index 4dec40ed..3c49170e 100644 --- a/src/runTests.h +++ b/src/runTests.h @@ -1,12 +1,12 @@ -#ifdef DEVEL - #ifndef H_CARDANO_APP_RUN_TESTS #define H_CARDANO_APP_RUN_TESTS +#ifdef DEVEL + #include "handlers.h" handler_fn_t handleRunTests; -#endif // H_CARDANO_APP_RUN_TESTS - #endif // DEVEL + +#endif // H_CARDANO_APP_RUN_TESTS diff --git a/src/securityPolicy.c b/src/securityPolicy.c index 7e5e1451..94ff7ef1 100644 --- a/src/securityPolicy.c +++ b/src/securityPolicy.c @@ -2183,3 +2183,46 @@ security_policy_t policyForSignCVoteWitness(bip44_path_t* path) break; } } + +security_policy_t policyForSignMsg( + const bip44_path_t* witnessPath, + cip8_address_field_type_t addressFieldType, + const addressParams_t* addressParams +) +{ + switch (bip44_classifyPath(witnessPath)) { + case PATH_ORDINARY_SPENDING_KEY: + case PATH_ORDINARY_STAKING_KEY: + case PATH_MULTISIG_SPENDING_KEY: + case PATH_MULTISIG_STAKING_KEY: + case PATH_MINT_KEY: + case PATH_DREP_KEY: + case PATH_COMMITTEE_COLD_KEY: + case PATH_COMMITTEE_HOT_KEY: + case PATH_POOL_COLD_KEY: + // OK + break; + default: + DENY(); + break; + } + + if (addressFieldType == CIP8_ADDRESS_FIELD_ADDRESS) { + DENY_UNLESS(isValidAddressParams(addressParams)); + + switch (addressParams->type) { + case BASE_PAYMENT_KEY_STAKE_KEY: + case BASE_PAYMENT_KEY_STAKE_SCRIPT: + case REWARD_KEY: + case ENTERPRISE_KEY: + // OK + break; + + default: + DENY(); + break; + } + } + + PROMPT(); +} diff --git a/src/securityPolicy.h b/src/securityPolicy.h index b185ac2c..7bcbd776 100644 --- a/src/securityPolicy.h +++ b/src/securityPolicy.h @@ -242,4 +242,10 @@ security_policy_t policyForSignCVoteInit(); security_policy_t policyForSignCVoteConfirm(); security_policy_t policyForSignCVoteWitness(bip44_path_t* path); +security_policy_t policyForSignMsg( + const bip44_path_t* witnessPath, + cip8_address_field_type_t addressFieldType, + const addressParams_t* addressParams +); + #endif // H_CARDANO_APP_SECURITY_POLICY diff --git a/src/signCVote.c b/src/signCVote.c index c588b7e8..6f275e4f 100644 --- a/src/signCVote.c +++ b/src/signCVote.c @@ -115,7 +115,7 @@ void signCVote_handleInitAPDU( default: THROW(ERR_NOT_IMPLEMENTED); } - handleInit_ui_runStep(); + signCVote_handleInit_ui_runStep(); } // ============================== VOTECAST CHUNK ============================== @@ -190,7 +190,7 @@ void signCVote_handleConfirmAPDU( } } - handleConfirm_ui_runStep(); + signCVote_handleConfirm_ui_runStep(); } // ============================== WITNESS ============================== diff --git a/src/signCVote_ui.c b/src/signCVote_ui.c index be0fe563..d1b3c48e 100644 --- a/src/signCVote_ui.c +++ b/src/signCVote_ui.c @@ -17,9 +17,9 @@ static ins_sign_cvote_context_t* ctx = &(instructionState.signCVoteContext); // ============================== INIT ============================== -void handleInit_ui_runStep() +void signCVote_handleInit_ui_runStep() { - ui_callback_fn_t* this_fn = handleInit_ui_runStep; + ui_callback_fn_t* this_fn = signCVote_handleInit_ui_runStep; UI_STEP_BEGIN(ctx->ui_step, this_fn); @@ -92,11 +92,11 @@ void handleInit_ui_runStep() // ============================== CONFIRM ============================== -void handleConfirm_ui_runStep() +void signCVote_handleConfirm_ui_runStep() { TRACE("UI step %d", ctx->ui_step); TRACE_STACK_USAGE(); - ui_callback_fn_t* this_fn = handleConfirm_ui_runStep; + ui_callback_fn_t* this_fn = signCVote_handleConfirm_ui_runStep; UI_STEP_BEGIN(ctx->ui_step, this_fn); diff --git a/src/signCVote_ui.h b/src/signCVote_ui.h index d18a1fb0..ca1edc3d 100644 --- a/src/signCVote_ui.h +++ b/src/signCVote_ui.h @@ -2,6 +2,7 @@ #define H_CARDANO_APP_SIGN_CVOTE_UI #include "uiHelpers.h" + // ============================== INIT ============================== enum { @@ -13,7 +14,7 @@ enum { HANDLE_INIT_INVALID, }; -void handleInit_ui_runStep(); +void signCVote_handleInit_ui_runStep(); // ============================== CONFIRM ============================== @@ -23,7 +24,7 @@ enum { HANDLE_CONFIRM_STEP_INVALID, }; -void handleConfirm_ui_runStep(); +void signCVote_handleConfirm_ui_runStep(); // ============================== WITNESS ============================== diff --git a/src/signMsg.c b/src/signMsg.c new file mode 100644 index 00000000..3fbf0d43 --- /dev/null +++ b/src/signMsg.c @@ -0,0 +1,375 @@ +#include "common.h" + +#include "signMsg.h" +#include "signMsg_ui.h" +#include "keyDerivation.h" +#include "endian.h" +#include "state.h" +#include "uiHelpers.h" +#include "securityPolicy.h" +#include "messageSigning.h" +#include "textUtils.h" +#include "signTxUtils.h" + +#ifdef HAVE_BAGL +#include "uiScreens_bagl.h" +#elif defined(HAVE_NBGL) +#include "uiScreens_nbgl.h" +#endif + +static ins_sign_msg_context_t* ctx = &(instructionState.signMsgContext); + +void signMsg_handleInitAPDU( + const uint8_t* wireDataBuffer, + size_t wireDataSize +) +{ + { + TRACE_BUFFER(wireDataBuffer, wireDataSize); + read_view_t view = make_read_view(wireDataBuffer, wireDataBuffer + wireDataSize); + + ctx->msgLength = parse_u4be(&view); + TRACE("Msg length: %d", ctx->msgLength); + ctx->remainingBytes = ctx->msgLength; + + view_skipBytes(&view, bip44_parseFromWire(&ctx->signingPath, VIEW_REMAINING_TO_TUPLE_BUF_SIZE(&view))); + TRACE("Signing path:"); + BIP44_PRINTF(&ctx->signingPath); + PRINTF("\n"); + + ctx->hashPayload = parse_bool(&view); + TRACE("Hash payload: %d", ctx->hashPayload); + + ctx->isAscii = parse_bool(&view); + TRACE("Is ascii: %d", ctx->isAscii); + + ctx->addressFieldType = parse_u1be(&view); + TRACE("Address field type: %d", ctx->addressFieldType); + switch (ctx->addressFieldType) { + case CIP8_ADDRESS_FIELD_ADDRESS: + view_parseAddressParams(&view, &ctx->addressParams); + break; + case CIP8_ADDRESS_FIELD_KEYHASH: + // no address field data to parse + break; + default: + THROW(ERR_INVALID_DATA); + } + + VALIDATE(view_remainingSize(&view) == 0, ERR_INVALID_DATA); + } + + // Check security policy + security_policy_t policy = policyForSignMsg( + &ctx->signingPath, + ctx->addressFieldType, + &ctx->addressParams + ); + ENSURE_NOT_DENIED(policy); + + // always compute message hash + blake2b_224_init(&ctx->msgHashCtx); + + { + // key is sent back at the end and possibly needed when displaying address field + extendedPublicKey_t extPubKey; + deriveExtendedPublicKey( + &ctx->signingPath, + &extPubKey + ); + STATIC_ASSERT(SIZEOF(extPubKey.pubKey) == SIZEOF(ctx->witnessKey), "wrong witness key size"); + memmove(ctx->witnessKey, extPubKey.pubKey, SIZEOF(extPubKey.pubKey)); + } + + // this must always be shown + ASSERT(policy == POLICY_PROMPT_BEFORE_RESPONSE); + ctx->ui_step = HANDLE_INIT_HASH_PAYLOAD; + signMsg_handleInit_ui_runStep(); +} + +static void signMsg_handleMsgChunkAPDU(const uint8_t* wireDataBuffer, size_t wireDataSize) +{ + { + ASSERT(ctx->stage == SIGN_MSG_STAGE_CHUNKS); + if (!ctx->hashPayload) { + // only a single chunk is to be received + ASSERT(ctx->receivedChunks == 0); + } + } + { + TRACE_BUFFER(wireDataBuffer, wireDataSize); + + ctx->receivedChunks += 1; + + read_view_t view = make_read_view(wireDataBuffer, wireDataBuffer + wireDataSize); + + const size_t chunkSize = parse_u4be(&view); + TRACE("chunkSize = %u", chunkSize); + + VALIDATE(chunkSize <= ctx->remainingBytes, ERR_INVALID_DATA); + + // the current chunk should have maximum allowed size; + // there is no point in allowing unnecessarily small chunks + // and it is a security risk if the first chunk (possibly the only one displayed) + // is artificially small + if (ctx->receivedChunks == 1) { + // the first chunk must be displayable + // the check below works for empty message (with special UI) too + if (ctx->isAscii) { + VALIDATE( + chunkSize == MIN(ctx->msgLength, MAX_CIP8_MSG_FIRST_CHUNK_ASCII_SIZE), + ERR_INVALID_DATA + ); + } else { + VALIDATE( + chunkSize == MIN(ctx->msgLength, MAX_CIP8_MSG_FIRST_CHUNK_HEX_SIZE), + ERR_INVALID_DATA + ); + } + } else { + // ctx->receivedChunks >= 2 + VALIDATE( + chunkSize == MIN(ctx->remainingBytes, MAX_CIP8_MSG_HIDDEN_CHUNK_SIZE), + ERR_INVALID_DATA + ); + } + + ASSERT(chunkSize <= ctx->remainingBytes); + ctx->remainingBytes -= chunkSize; + ctx->chunkSize = chunkSize; + + ASSERT(chunkSize <= SIZEOF(ctx->chunk)); + view_parseBuffer(ctx->chunk, &view, chunkSize); + if (ctx->isAscii) { + VALIDATE(str_isUnambiguousAscii(ctx->chunk, ctx->chunkSize), ERR_INVALID_DATA); + } + + VALIDATE(view_remainingSize(&view) == 0, ERR_INVALID_DATA); + } + { + TRACE("Adding msg chunk to msg hash"); + blake2b_224_append(&ctx->msgHashCtx, ctx->chunk, ctx->chunkSize); + } + + if (ctx->receivedChunks == 1) { + if (!ctx->hashPayload) { + // for non-hashed payload, we expect only a single chunk + VALIDATE(ctx->remainingBytes == 0, ERR_INVALID_DATA); + } + ctx->ui_step = HANDLE_CHUNK_STEP_INTRO; + signMsg_handleChunk_ui_runStep(); + } else { + // for non-hashed payload, we expect only a single chunk, + // so the state should be SIGN_MSG_STAGE_CONFIRM already + ASSERT(ctx->hashPayload); + + // the chunk has been added to msg hash, nothing more to do, and no UI + respondSuccessEmptyMsg(); + + if (ctx->remainingBytes == 0) { + ctx->stage = SIGN_MSG_STAGE_CONFIRM; + } + } +} + +static void _prepareAddressField() +{ + switch (ctx->addressFieldType) { + + case CIP8_ADDRESS_FIELD_ADDRESS: { + ctx->addressFieldSize = deriveAddress(&ctx->addressParams, ctx->addressField, SIZEOF(ctx->addressField)); + break; + } + + case CIP8_ADDRESS_FIELD_KEYHASH: { + STATIC_ASSERT(SIZEOF(ctx->addressField) >= ADDRESS_KEY_HASH_LENGTH, "wrong address field size"); + bip44_pathToKeyHash(&ctx->signingPath, ctx->addressField, ADDRESS_KEY_HASH_LENGTH); + ctx->addressFieldSize = ADDRESS_KEY_HASH_LENGTH; + break; + } + + default: + ASSERT(false); + } +} + +static size_t _createProtectedHeader(uint8_t* protectedHeaderBuffer, size_t maxSize) +{ + // protectedHeader = { + // 1 : -8, // set algorithm to EdDSA + // “address” : address_bytes // raw address given by the user, or key hash + // } + BEGIN_TRY { + TRY { + uint8_t* p = protectedHeaderBuffer; + uint8_t* end = protectedHeaderBuffer + maxSize; + + { + size_t len = cbor_writeToken(CBOR_TYPE_MAP, 2, p, end - p); + p += len; + } + { + size_t len = cbor_writeToken(CBOR_TYPE_UNSIGNED, 1, p, end - p); + p += len; + } + { + size_t len = cbor_writeToken(CBOR_TYPE_NEGATIVE, -8, p, end - p); + p += len; + } + { + size_t len = cbor_writeToken(CBOR_TYPE_TEXT, 7, p, end - p); + p += len; + } + + { + const char* text = "address"; + const size_t len = strlen(text); + ASSERT(p + len < end); + memmove(p, text, len); + p += len; + } + _prepareAddressField(); + { + size_t len = cbor_writeToken(CBOR_TYPE_BYTES, ctx->addressFieldSize, p, end - p); + p += len; + } + { + ASSERT(p + ctx->addressFieldSize < end); + memmove(p, ctx->addressField, ctx->addressFieldSize); + p += ctx->addressFieldSize; + } + + const size_t protectedHeaderSize = p - protectedHeaderBuffer; + ASSERT(protectedHeaderSize > 0); + ASSERT(protectedHeaderSize < maxSize); + + return protectedHeaderSize; + } CATCH(ERR_DATA_TOO_LARGE) { + ASSERT(false); + } FINALLY { + } + } END_TRY; + + return -1; +} + +static void signMsg_handleConfirmAPDU(const uint8_t* wireDataBuffer MARK_UNUSED, size_t wireDataSize) +{ + VALIDATE(wireDataSize == 0, ERR_INVALID_DATA); + + // it seems Ledger can sign 400 B, more is not needed since non-hashed msg is capped at 200 B + uint8_t sigStructure[400] = {0}; + explicit_bzero(sigStructure, SIZEOF(sigStructure)); + size_t written = 0; + const size_t maxWritten = SIZEOF(sigStructure); + + // Sig_structure = [ + // context : “Signature1”, + // body_protected : CBOR_encode(protectedHeader), + // external_aad : bstr, // empty buffer here + // payload : bstr // message or its hash as bytes + // ] + + written += cbor_writeToken(CBOR_TYPE_ARRAY, 4, sigStructure + written, maxWritten - written); + ASSERT(written < maxWritten); + + { + const char* firstElement = "Signature1"; + const size_t len = strlen(firstElement); + written += cbor_writeToken(CBOR_TYPE_TEXT, len, sigStructure + written, maxWritten - written); + ASSERT(written + len < maxWritten); + memmove(sigStructure + written, firstElement, len); + written += len; + ASSERT(written < maxWritten); + } + { + uint8_t protectedHeaderBuffer[100]; // address of max 57 bytes plus a couple of small items + const size_t len = _createProtectedHeader(protectedHeaderBuffer, SIZEOF(protectedHeaderBuffer)); + written += cbor_writeToken(CBOR_TYPE_BYTES, len, sigStructure + written, maxWritten - written); + ASSERT(written + len < maxWritten); + memmove(sigStructure + written, protectedHeaderBuffer, len); + written += len; + ASSERT(written < maxWritten); + } + { + written += cbor_writeToken(CBOR_TYPE_BYTES, 0, sigStructure + written, maxWritten - written); + ASSERT(written < maxWritten); + } + STATIC_ASSERT(SIZEOF(ctx->msgHash) * 8 == 224, "inconsistent message hash size"); + blake2b_224_finalize(&ctx->msgHashCtx, ctx->msgHash, SIZEOF(ctx->msgHash)); + { + if (ctx->hashPayload) { + written += cbor_writeToken(CBOR_TYPE_BYTES, SIZEOF(ctx->msgHash), sigStructure + written, maxWritten - written); + ASSERT(written < maxWritten); + + ASSERT(SIZEOF(ctx->msgHash) < maxWritten - written); + memmove(sigStructure + written, ctx->msgHash, SIZEOF(ctx->msgHash)); + written += SIZEOF(ctx->msgHash); + ASSERT(written < maxWritten); + } else { + // for non-hashed payload, the chunk from the previous APDU is used + ASSERT(ctx->receivedChunks == 1); + + written += cbor_writeToken(CBOR_TYPE_BYTES, ctx->chunkSize, sigStructure + written, maxWritten - written); + ASSERT(written < maxWritten); + + ASSERT(ctx->chunkSize < maxWritten - written); + memmove(sigStructure + written, ctx->chunk, ctx->chunkSize); + written += ctx->chunkSize; + ASSERT(written < maxWritten); + } + } + + const size_t sigStructureSize = written; + TRACE_BUFFER(sigStructure, sigStructureSize); + + // we do not sign anything that could be a transaction hash + // Note: in the v7.1 implementation Sig_structure has more than 40 bytes + ASSERT(sigStructureSize != TX_HASH_LENGTH); + + signRawMessageWithPath(&ctx->signingPath, sigStructure, sigStructureSize, ctx->signature, SIZEOF(ctx->signature)); + + ctx->ui_step = HANDLE_CONFIRM_STEP_MSG_HASH; + signMsg_handleConfirm_ui_runStep(); +} + +// ============================== MAIN HANDLER ============================== + +typedef void subhandler_fn_t(const uint8_t* dataBuffer, size_t dataSize); + +static subhandler_fn_t* lookup_subhandler(uint8_t p1) +{ + switch (p1) { +#define CASE(P1, HANDLER) case P1: return HANDLER; +#define DEFAULT(HANDLER) default: return HANDLER; + CASE(0x01, signMsg_handleInitAPDU); + CASE(0x02, signMsg_handleMsgChunkAPDU); + CASE(0x03, signMsg_handleConfirmAPDU); + DEFAULT(NULL) +#undef CASE +#undef DEFAULT + } +} + +void signMsg_handleAPDU( + uint8_t p1, + uint8_t p2, + const uint8_t* wireDataBuffer, + size_t wireDataSize, + bool isNewCall +) +{ + TRACE("P1 = 0x%x, P2 = 0x%x, isNewCall = %d", p1, p2, isNewCall); + ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); + + VALIDATE(p2 == P2_UNUSED, ERR_INVALID_REQUEST_PARAMETERS); + + if (isNewCall) { + explicit_bzero(ctx, SIZEOF(*ctx)); + ctx->stage = SIGN_MSG_STAGE_INIT; + } + + subhandler_fn_t* subhandler = lookup_subhandler(p1); + VALIDATE(subhandler != NULL, ERR_INVALID_REQUEST_PARAMETERS); + subhandler(wireDataBuffer, wireDataSize); +} diff --git a/src/signMsg.h b/src/signMsg.h new file mode 100644 index 00000000..2da3d2ea --- /dev/null +++ b/src/signMsg.h @@ -0,0 +1,56 @@ +#ifndef H_CARDANO_APP_SIGN_MSG +#define H_CARDANO_APP_SIGN_MSG + +#include "addressUtilsShelley.h" +#include "cardano.h" +#include "common.h" +#include "handlers.h" +#include "hash.h" +#include "bip44.h" +#include "keyDerivation.h" + +handler_fn_t signMsg_handleAPDU; + +#define CIP8_MSG_HASH_LENGTH 28 + +// Note: this cannot be increased, there is a limit of 200 chars in the UI +#define MAX_CIP8_MSG_FIRST_CHUNK_ASCII_SIZE 198 +#define MAX_CIP8_MSG_FIRST_CHUNK_HEX_SIZE 99 +#define MAX_CIP8_MSG_HIDDEN_CHUNK_SIZE 250 + +typedef enum { + SIGN_MSG_STAGE_NONE = 0, + SIGN_MSG_STAGE_INIT = 43, + SIGN_MSG_STAGE_CHUNKS = 44, + SIGN_MSG_STAGE_CONFIRM = 45, +} sign_msg_stage_t; + +typedef struct { + bip44_path_t signingPath; + cip8_address_field_type_t addressFieldType; + addressParams_t addressParams; + + bool isAscii; + bool hashPayload; + + size_t msgLength; + size_t remainingBytes; + size_t receivedChunks; + + uint8_t chunk[MAX_CIP8_MSG_HIDDEN_CHUNK_SIZE]; + size_t chunkSize; + + blake2b_224_context_t msgHashCtx; + uint8_t msgHash[CIP8_MSG_HASH_LENGTH]; + uint8_t signature[ED25519_SIGNATURE_LENGTH]; + uint8_t witnessKey[PUBLIC_KEY_SIZE]; + uint8_t addressField[MAX_ADDRESS_SIZE]; + size_t addressFieldSize; + + sign_msg_stage_t stage; + int ui_step; +} ins_sign_msg_context_t; + +handler_fn_t signMsg_handleAPDU; + +#endif // H_CARDANO_APP_SIGN_MSG \ No newline at end of file diff --git a/src/signMsg_ui.c b/src/signMsg_ui.c new file mode 100644 index 00000000..cbe4ba69 --- /dev/null +++ b/src/signMsg_ui.c @@ -0,0 +1,319 @@ +#include "hexUtils.h" +#include "messageSigning.h" +#include "securityPolicy.h" +#include "signMsg.h" +#include "signTxUtils.h" +#include "state.h" +#include "signMsg_ui.h" + +#ifdef HAVE_BAGL +#include "uiScreens_bagl.h" +#elif defined(HAVE_NBGL) +#include "uiScreens_nbgl.h" +#include "nbgl_use_case.h" +#endif + + +static ins_sign_msg_context_t* ctx = &(instructionState.signMsgContext); + +// ============================== INIT ============================== + +void signMsg_handleInit_ui_runStep() +{ + TRACE("UI step %d", ctx->ui_step); + TRACE_STACK_USAGE(); + ui_callback_fn_t* this_fn = signMsg_handleInit_ui_runStep; + + UI_STEP_BEGIN(ctx->ui_step, this_fn); + + UI_STEP(HANDLE_INIT_HASH_PAYLOAD) { + #ifdef HAVE_BAGL + const char* firstLine = (ctx->hashPayload) ? "Sign hashed" : "Sign non-hashed"; + ui_displayPrompt( + firstLine, + "message? (CIP-8)", + this_fn, + respond_with_user_reject + ); + #elif defined(HAVE_NBGL) + set_light_confirmation(true); + const char* text = (ctx->hashPayload) ? "Sign hashed\nmessage? (CIP-8)" : "Sign non-hashed\nmessage? (CIP-8)"; + display_prompt(text, "", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_INIT_WITNESS_PATH) { + #ifdef HAVE_BAGL + ui_displayPathScreen("Signing path", &ctx->signingPath, this_fn); + #elif defined(HAVE_NBGL) + char pathStr[BIP44_PATH_STRING_SIZE_MAX + 1] = {0}; + ui_getPathScreen(pathStr, SIZEOF(pathStr), &ctx->signingPath); + fill_and_display_if_required("Signing path", pathStr, this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_INIT_ADDRESS_FIELD_DISPLAY_KEY_HASH) { + if (ctx->addressFieldType == CIP8_ADDRESS_FIELD_ADDRESS) { + UI_STEP_JUMP(HANDLE_INIT_ADDRESS_FIELD_DISPLAY_ADDRESS); + } + + uint8_t hash[28]; + blake2b_224_hash( + ctx->witnessKey, SIZEOF(ctx->witnessKey), + hash, SIZEOF(hash) + ); + + #ifdef HAVE_BAGL + ui_displayHexBufferScreen( + "Address field", + hash, + SIZEOF(hash), + this_fn + ); + #elif defined(HAVE_NBGL) + char bufferHex[2 * SCRIPT_HASH_LENGTH + 1] = {0}; + ui_getHexBufferScreen(bufferHex, SIZEOF(bufferHex), hash, SIZEOF(hash)); + fill_and_display_if_required("Address field", bufferHex, this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_INIT_ADDRESS_FIELD_DISPLAY_ADDRESS) { + if (ctx->addressFieldType != CIP8_ADDRESS_FIELD_ADDRESS) { + UI_STEP_JUMP(HANDLE_INIT_RESPOND); + } + + uint8_t addressBuffer[MAX_ADDRESS_SIZE] = {0}; + size_t addressSize = deriveAddress(&ctx->addressParams, addressBuffer, SIZEOF(addressBuffer)); + ASSERT(addressSize > 0); + ASSERT(addressSize <= MAX_ADDRESS_SIZE); + + #ifdef HAVE_BAGL + ui_displayAddressScreen( + "Address field", + addressBuffer, addressSize, + this_fn + ); + #elif defined(HAVE_NBGL) + char humanAddress[MAX_HUMAN_ADDRESS_SIZE] = {0}; + ui_getAddressScreen( + humanAddress, + SIZEOF(humanAddress), + addressBuffer, + addressSize + ); + fill_and_display_if_required("Address field", humanAddress, this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_INIT_ADDRESS_FIELD_DISPLAY_SPENDING_PATH) { + #ifdef HAVE_BAGL + ui_displaySpendingInfoScreen(&ctx->addressParams, this_fn); + #elif defined(HAVE_NBGL) +#define SPENDING_INFO_SIZE MAX(BECH32_STRING_SIZE_MAX, BIP44_PATH_STRING_SIZE_MAX) + char line1[30]; + char spendingInfo[SPENDING_INFO_SIZE] = {0}; + ui_getSpendingInfoScreen(line1, SIZEOF(line1), spendingInfo, SIZEOF(spendingInfo), &ctx->addressParams); + fill_and_display_if_required(line1, spendingInfo, this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_INIT_ADDRESS_FIELD_DISPLAY_STAKING_INFO) { + #ifdef HAVE_BAGL + ui_displayStakingInfoScreen(&ctx->addressParams, this_fn); + #elif defined(HAVE_NBGL) + char line1[30] = {0}; + char stakingInfo[120] = {0}; + ui_getStakingInfoScreen(line1, SIZEOF(line1), stakingInfo, SIZEOF(stakingInfo), &ctx->addressParams); + fill_and_display_if_required(line1, stakingInfo, this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_INIT_RESPOND) { + respondSuccessEmptyMsg(); + ctx->stage = SIGN_MSG_STAGE_CHUNKS; + } + UI_STEP_END(HANDLE_INIT_INVALID); +} + +// ============================== CHUNK ============================== + +void _displayMsgIntro(ui_callback_fn_t* callback) +{ + char l1[30] = {0}; + if (ctx->isAscii) { + snprintf(l1, SIZEOF(l1), "Message (ASCII)"); + } else { + snprintf(l1, SIZEOF(l1), "Message (hex)"); + } + ASSERT(strlen(l1) + 1 < SIZEOF(l1)); + + char l2[30] = {0}; + snprintf(l2, SIZEOF(l2), "%u bytes", ctx->msgLength); + ASSERT(strlen(l2) + 1 < SIZEOF(l2)); + + #ifdef HAVE_BAGL + ui_displayPaginatedText( + l1, + l2, + callback + ); + #elif defined(HAVE_NBGL) + fill_and_display_if_required(l1, l2, callback, respond_with_user_reject); + #endif // HAVE_BAGL +} + +__noinline_due_to_stack__ +void _displayMsgFull(ui_callback_fn_t* callback) +{ + char l1[30]; + if (ctx->isAscii) { + snprintf(l1, SIZEOF(l1), "Message (ASCII)"); + } else { + snprintf(l1, SIZEOF(l1), "Message (hex)"); + } + ASSERT(strlen(l1) + 1 < SIZEOF(l1)); + + char l2[200]; + if (ctx->isAscii) { + ASSERT(ctx->chunkSize + 1 < SIZEOF(l2)); + memmove(l2, ctx->chunk, ctx->chunkSize); + l2[ctx->chunkSize] = '\0'; + } else { + encode_hex(ctx->chunk, ctx->chunkSize, l2, SIZEOF(l2)); + } + ASSERT(strlen(l2) + 1 < SIZEOF(l2)); + + #ifdef HAVE_BAGL + ui_displayPaginatedText( + l1, + l2, + callback + ); + #elif defined(HAVE_NBGL) + fill_and_display_if_required(l1, l2, callback, respond_with_user_reject); + #endif // HAVE_BAGL +} + +__noinline_due_to_stack__ +void _displayMsgChunk(ui_callback_fn_t* callback) +{ + const char* l1 = "Message starts with"; + + char l2[200]; + if (ctx->isAscii) { + ASSERT(ctx->chunkSize + 1 < SIZEOF(l2)); + memmove(l2, ctx->chunk, ctx->chunkSize); + l2[ctx->chunkSize] = '\0'; + } else { + encode_hex(ctx->chunk, ctx->chunkSize, l2, SIZEOF(l2)); + } + ASSERT(strlen(l2) + 1 < SIZEOF(l2)); + + #ifdef HAVE_BAGL + ui_displayPaginatedText( + l1, + l2, + callback + ); + #elif defined(HAVE_NBGL) + fill_and_display_if_required(l1, l2, callback, respond_with_user_reject); + #endif // HAVE_BAGL +} + +void signMsg_handleChunk_ui_runStep() +{ + TRACE("UI step %d", ctx->ui_step); + TRACE_STACK_USAGE(); + ui_callback_fn_t* this_fn = signMsg_handleChunk_ui_runStep; + + ASSERT(ctx->receivedChunks == 1); + + UI_STEP_BEGIN(ctx->ui_step, this_fn); + + UI_STEP(HANDLE_CHUNK_STEP_INTRO) { + _displayMsgIntro(this_fn); + } + UI_STEP(HANDLE_CHUNK_STEP_DISPLAY) { + if (ctx->msgLength == 0) { + UI_STEP_JUMP(HANDLE_CHUNK_STEP_RESPOND); + } + if (ctx->remainingBytes == 0) { + _displayMsgFull(this_fn); + } else { + _displayMsgChunk(this_fn); + } + } + UI_STEP(HANDLE_CHUNK_STEP_RESPOND) { + respondSuccessEmptyMsg(); + + if (ctx->remainingBytes == 0) { + ctx->stage = SIGN_MSG_STAGE_CONFIRM; + } + } + UI_STEP_END(HANDLE_CHUNK_STEP_INVALID); +} + +// ============================== CONFIRM ============================== + +void signMsg_handleConfirm_ui_runStep() +{ + TRACE("UI step %d", ctx->ui_step); + TRACE_STACK_USAGE(); + ui_callback_fn_t* this_fn = signMsg_handleConfirm_ui_runStep; + + UI_STEP_BEGIN(ctx->ui_step, this_fn); + + UI_STEP(HANDLE_CONFIRM_STEP_MSG_HASH) { + #ifdef HAVE_BAGL + ui_displayHexBufferScreen( + "Message hash", + ctx->msgHash, + SIZEOF(ctx->msgHash), + this_fn + ); + #elif defined(HAVE_NBGL) + char bufferHex[2 * CIP8_MSG_HASH_LENGTH + 1] = {0}; + ui_getHexBufferScreen(bufferHex, SIZEOF(bufferHex), ctx->msgHash, SIZEOF(ctx->msgHash)); + fill_and_display_if_required("Message hash", bufferHex, this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_CONFIRM_STEP_FINAL_CONFIRM) { + #ifdef HAVE_BAGL + ui_displayPrompt( + "Sign", + "message?", + this_fn, + respond_with_user_reject + ); + #elif defined(HAVE_NBGL) + display_confirmation("Sign\n message?", "", "MESSAGE\nSIGNED", "Message\nrejected", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_CONFIRM_STEP_RESPOND) { + struct { + uint8_t signature[ED25519_SIGNATURE_LENGTH]; + uint8_t witnessKey[PUBLIC_KEY_SIZE]; + uint32_t addressFieldSize; + uint8_t addressField[MAX_ADDRESS_SIZE]; + } wireResponse = {0}; + STATIC_ASSERT(SIZEOF(wireResponse) <= 255, "too large msg signing wire response"); + + STATIC_ASSERT(SIZEOF(ctx->signature) == ED25519_SIGNATURE_LENGTH, "wrong signature buffer size"); + memmove(wireResponse.signature, ctx->signature, ED25519_SIGNATURE_LENGTH); + + STATIC_ASSERT(SIZEOF(ctx->witnessKey) == PUBLIC_KEY_SIZE, "wrong key buffer size"); + memmove(wireResponse.witnessKey, ctx->witnessKey, PUBLIC_KEY_SIZE); + + #ifndef FUZZING + STATIC_ASSERT(sizeof(wireResponse.addressFieldSize) == 4, "wrong address field size type"); + STATIC_ASSERT(sizeof(ctx->addressFieldSize) == 4, "wrong address field size type"); + u4be_write((uint8_t*) &wireResponse.addressFieldSize, ctx->addressFieldSize); + #endif + + STATIC_ASSERT(SIZEOF(ctx->addressField) == SIZEOF(wireResponse.addressField), "wrong address field size"); + memmove(wireResponse.addressField, ctx->addressField, ctx->addressFieldSize); + + io_send_buf(SUCCESS, (uint8_t*) &wireResponse, SIZEOF(wireResponse)); + #ifdef HAVE_BAGL + ui_displayBusy(); // displays dots, called only after I/O to avoid freezing + #endif // HAVE_BAGL + + ctx->stage = SIGN_MSG_STAGE_NONE; + ui_idle(); + } + UI_STEP_END(HANDLE_CONFIRM_STEP_INVALID); +} diff --git a/src/signMsg_ui.h b/src/signMsg_ui.h new file mode 100644 index 00000000..defc317c --- /dev/null +++ b/src/signMsg_ui.h @@ -0,0 +1,43 @@ +#ifndef H_CARDANO_APP_SIGN_CVOTE_UI +#define H_CARDANO_APP_SIGN_CVOTE_UI + +#include "uiHelpers.h" + +// ============================== INIT ============================== + +enum { + HANDLE_INIT_HASH_PAYLOAD = 100, + HANDLE_INIT_WITNESS_PATH, + HANDLE_INIT_ADDRESS_FIELD_DISPLAY_KEY_HASH, + HANDLE_INIT_ADDRESS_FIELD_DISPLAY_ADDRESS, + HANDLE_INIT_ADDRESS_FIELD_DISPLAY_SPENDING_PATH, + HANDLE_INIT_ADDRESS_FIELD_DISPLAY_STAKING_INFO, + HANDLE_INIT_RESPOND, + HANDLE_INIT_INVALID, +}; + +void signMsg_handleInit_ui_runStep(); + +// ============================== CHUNK ============================== + +enum { + HANDLE_CHUNK_STEP_INTRO = 200, + HANDLE_CHUNK_STEP_DISPLAY, + HANDLE_CHUNK_STEP_RESPOND, + HANDLE_CHUNK_STEP_INVALID, +}; + +void signMsg_handleChunk_ui_runStep(); + +// ============================== CONFIRM ============================== + +enum { + HANDLE_CONFIRM_STEP_MSG_HASH = 300, + HANDLE_CONFIRM_STEP_FINAL_CONFIRM, + HANDLE_CONFIRM_STEP_RESPOND, + HANDLE_CONFIRM_STEP_INVALID, +}; + +void signMsg_handleConfirm_ui_runStep(); + +#endif // H_CARDANO_APP_SIGN_CVOTE_UI diff --git a/src/signOpCert.c b/src/signOpCert.c index b28e820c..2d42dfde 100644 --- a/src/signOpCert.c +++ b/src/signOpCert.c @@ -132,6 +132,8 @@ void signOpCert_handleAPDU( static void signOpCert_ui_runStep() { + TRACE("UI step %d", ctx->ui_step); + TRACE_STACK_USAGE(); ui_callback_fn_t* this_fn = signOpCert_ui_runStep; UI_STEP_BEGIN(ctx->ui_step, this_fn); diff --git a/src/signOpCert.h b/src/signOpCert.h index e88eec68..29eb9d1f 100644 --- a/src/signOpCert.h +++ b/src/signOpCert.h @@ -3,6 +3,7 @@ #ifdef APP_FEATURE_OPCERT +#include "cardano.h" #include "common.h" #include "handlers.h" #include "bip44.h" @@ -18,7 +19,7 @@ typedef struct { uint64_t kesPeriod; uint64_t issueCounter; bip44_path_t poolColdKeyPathSpec; - uint8_t signature[64]; + uint8_t signature[ED25519_SIGNATURE_LENGTH]; int ui_step; } ins_sign_op_cert_context_t; diff --git a/src/signTx.h b/src/signTx.h index 1fbabcd7..8ff04c32 100644 --- a/src/signTx.h +++ b/src/signTx.h @@ -1,6 +1,7 @@ #ifndef H_CARDANO_APP_SIGN_TX #define H_CARDANO_APP_SIGN_TX +#include "cardano.h" #include "common.h" #include "hash.h" #include "handlers.h" @@ -156,7 +157,7 @@ typedef struct { typedef struct { bip44_path_t path; - uint8_t signature[64]; + uint8_t signature[ED25519_SIGNATURE_LENGTH]; } sign_tx_witness_data_t; typedef struct { diff --git a/src/signTxCVoteRegistration.c b/src/signTxCVoteRegistration.c index 12112433..5f080431 100644 --- a/src/signTxCVoteRegistration.c +++ b/src/signTxCVoteRegistration.c @@ -447,7 +447,7 @@ size_t _destinationToAddress( case DESTINATION_THIRD_PARTY: addressSize = destination->address.size; ASSERT(addressSize <= addressBufferSize); - memcpy( + memmove( addressBuffer, destination->address.buffer, addressSize diff --git a/src/signTxOutput.c b/src/signTxOutput.c index a412c65d..579ec09b 100644 --- a/src/signTxOutput.c +++ b/src/signTxOutput.c @@ -743,6 +743,10 @@ static void handleDatumInline(read_view_t* view) VALIDATE(chunkSize > 0, ERR_INVALID_DATA); VALIDATE(chunkSize <= MAX_CHUNK_SIZE, ERR_INVALID_DATA); VALIDATE(chunkSize <= subctx->stateData.datumRemainingBytes, ERR_INVALID_DATA); + if (subctx->stateData.datumRemainingBytes >= MAX_CHUNK_SIZE) { + // forces to use chunks of maximum allowed size + VALIDATE(chunkSize == MAX_CHUNK_SIZE, ERR_INVALID_DATA); + } view_parseBuffer(subctx->stateData.datumChunk, view, chunkSize); VALIDATE(view_remainingSize(view) == 0, ERR_INVALID_DATA); @@ -871,7 +875,7 @@ static void handleRefScriptAPDU(const uint8_t* wireDataBuffer, size_t wireDataSi // parse data subctx->stateData.refScriptRemainingBytes = parse_u4be(&view); - TRACE("refScriptRemainingBytes = %u", subctx->stateData.datumRemainingBytes); + TRACE("refScriptRemainingBytes = %u", subctx->stateData.refScriptRemainingBytes); VALIDATE(subctx->stateData.refScriptRemainingBytes > 0, ERR_INVALID_DATA); size_t chunkSize = parse_u4be(&view); @@ -936,7 +940,10 @@ static void handleRefScriptChunkAPDU(const uint8_t* wireDataBuffer, size_t wireD TRACE("chunkSize = %u", chunkSize); VALIDATE(chunkSize > 0, ERR_INVALID_DATA); VALIDATE(chunkSize <= MAX_CHUNK_SIZE, ERR_INVALID_DATA); - + if (subctx->stateData.refScriptRemainingBytes >= MAX_CHUNK_SIZE) { + // forces to use chunks of maximum allowed size + VALIDATE(chunkSize == MAX_CHUNK_SIZE, ERR_INVALID_DATA); + } VALIDATE(chunkSize <= subctx->stateData.refScriptRemainingBytes, ERR_INVALID_DATA); subctx->stateData.refScriptRemainingBytes -= chunkSize; @@ -947,7 +954,7 @@ static void handleRefScriptChunkAPDU(const uint8_t* wireDataBuffer, size_t wireD } { // add to tx - TRACE("Adding inline datum chunk to tx hash"); + TRACE("Adding reference script chunk to tx hash"); txHashBuilder_addOutput_referenceScript_dataChunk( &BODY_CTX->txHashBuilder, subctx->stateData.scriptChunk, subctx->stateData.refScriptChunkSize diff --git a/src/signTxPoolRegistration.c b/src/signTxPoolRegistration.c index aed6c5d0..d91863e8 100644 --- a/src/signTxPoolRegistration.c +++ b/src/signTxPoolRegistration.c @@ -600,7 +600,7 @@ static void _parseDnsName(pool_relay_t* relay, read_view_t* view) { relay->dnsNameSize = view_remainingSize(view); VALIDATE(relay->dnsNameSize <= DNS_NAME_SIZE_MAX, ERR_INVALID_DATA); - VALIDATE(str_isAllowedDnsName(VIEW_REMAINING_TO_TUPLE_BUF_SIZE(view)), ERR_INVALID_DATA); + VALIDATE(str_isUnambiguousAscii(VIEW_REMAINING_TO_TUPLE_BUF_SIZE(view)), ERR_INVALID_DATA); STATIC_ASSERT(SIZEOF(relay->dnsName) == DNS_NAME_SIZE_MAX, "wrong dns name buffer size"); view_parseBuffer(relay->dnsName, view, relay->dnsNameSize); diff --git a/src/state.h b/src/state.h index f95bcb53..59ff02a3 100644 --- a/src/state.h +++ b/src/state.h @@ -5,6 +5,7 @@ #include "getPublicKeys.h" #include "deriveAddress.h" #include "deriveNativeScriptHash.h" +#include "signMsg.h" #include "signTx.h" #include "signOpCert.h" #include "signCVote.h" @@ -22,6 +23,7 @@ typedef union { ins_sign_op_cert_context_t signOpCertContext; #endif // APP_FEATURE_OPCERT ins_sign_cvote_context_t signCVoteContext; + ins_sign_msg_context_t signMsgContext; } instructionState_t; // Note(instructions are uint8_t but we have a special INS_NONE value diff --git a/src/textUtils.c b/src/textUtils.c index 892fafef..f2c987de 100644 --- a/src/textUtils.c +++ b/src/textUtils.c @@ -263,7 +263,8 @@ bool str_isPrintableAsciiWithSpaces(const uint8_t* buffer, size_t bufferSize) return true; } -bool str_isAllowedDnsName(const uint8_t* buffer, size_t bufferSize) +// check if the string can be unambiguously displayed to the user +bool str_isUnambiguousAscii(const uint8_t* buffer, size_t bufferSize) { ASSERT(bufferSize < BUFFER_SIZE_PARANOIA); diff --git a/src/textUtils.h b/src/textUtils.h index 1ce68ba2..69b5ec56 100644 --- a/src/textUtils.h +++ b/src/textUtils.h @@ -46,7 +46,7 @@ size_t str_formatValidityBoundary(uint64_t slotNumber, char* out, size_t outSize bool str_isPrintableAsciiWithoutSpaces(const uint8_t* buffer, size_t bufferSize); bool str_isPrintableAsciiWithSpaces(const uint8_t* buffer, size_t bufferSize); -bool str_isAllowedDnsName(const uint8_t* buffer, size_t bufferSize); +bool str_isUnambiguousAscii(const uint8_t* buffer, size_t bufferSize); #ifdef DEVEL diff --git a/src/tokens_test.c b/src/tokens_test.c index 4abd3860..71ee56c4 100644 --- a/src/tokens_test.c +++ b/src/tokens_test.c @@ -104,7 +104,7 @@ void test_decimalPlaces() for (size_t i = 0; i < ARRAY_LEN(tokenTestCases); i++) { char tokenAmountStr[60]; token_group_t group; - memcpy(group.policyId, tokenTestCases[i].policyId, MINTING_POLICY_ID_SIZE); + memmove(group.policyId, tokenTestCases[i].policyId, MINTING_POLICY_ID_SIZE); str_formatTokenAmountOutput( &group, diff --git a/src/uiScreens_bagl.c b/src/uiScreens_bagl.c index cea937ae..ab0d85d1 100644 --- a/src/uiScreens_bagl.c +++ b/src/uiScreens_bagl.c @@ -373,7 +373,7 @@ void ui_displaySpendingInfoScreen( case SPENDING_PATH: { ui_displayPathScreen( - "Derivation path", + "Spending key path", &addressParams->spendingKeyPath, callback ); @@ -398,7 +398,7 @@ void ui_displaySpendingInfoScreen( } } -static const char STAKING_HEADING_PATH[] = "Staking path"; +static const char STAKING_HEADING_PATH[] = "Stake key path"; static const char STAKING_HEADING_KEY_HASH[] = "Stake key hash"; static const char STAKING_HEADING_SCRIPT_HASH[] = "Stake script hash"; static const char STAKING_HEADING_POINTER[] = "Stake key pointer"; diff --git a/src/ui_nbgl.c b/src/ui_nbgl.c index 08adf0cc..bcb20563 100644 --- a/src/ui_nbgl.c +++ b/src/ui_nbgl.c @@ -214,6 +214,9 @@ static void _display_light_confirmation(void) { TRACE("_light_confirmation"); + // TODO "reject transaction" is used as a constant string in several places + // and then applied also for signing messages and operational certificates. + // We should change this as part of the upcoming larger UX rewrite. nbgl_useCaseChoice(&C_cardano_64, uiContext.pageText[0], "", "Confirm", "Reject Transaction", light_confirm_callback); From bdf24b03463c948ac724e0b7f71a895ab5beb121 Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Mon, 19 Feb 2024 10:29:02 +0100 Subject: [PATCH 22/43] refactor: fix naming for payment part --- doc/ins_get_public_keys.md | 2 +- doc/ins_sign_tx.md | 2 +- src/addressUtilsShelley.c | 56 ++++++++++++++++---------------- src/addressUtilsShelley.h | 14 ++++---- src/addressUtilsShelley_test.c | 20 ++++++------ src/bip44.c | 8 ++--- src/bip44.h | 4 +-- src/deriveAddress.c | 42 ++++++++++++------------ src/securityPolicy.c | 58 +++++++++++++++++----------------- src/signMsg_ui.c | 12 +++---- src/signMsg_ui.h | 2 +- src/signTxOutput_ui.c | 14 ++++---- src/signTxOutput_ui.h | 2 +- src/uiScreens_bagl.c | 20 ++++++------ src/uiScreens_bagl.h | 2 +- src/uiScreens_nbgl.c | 20 ++++++------ src/uiScreens_nbgl.h | 2 +- 17 files changed, 140 insertions(+), 140 deletions(-) diff --git a/doc/ins_get_public_keys.md b/doc/ins_get_public_keys.md index fc304d72..3219a81f 100644 --- a/doc/ins_get_public_keys.md +++ b/doc/ins_get_public_keys.md @@ -6,7 +6,7 @@ Get an extended public key (i.e., public key + chain code) for a given BIP32 pat It is also possible to ask for a confirmation for exporting several keys (if the paths describing the keys are not suspicious, they won't be shown to the user and no further confirmation is required). -The allowed derivation paths correspond to wallet keys (accounts, spending paths, staking paths) and pool cold keys, as described in +The allowed derivation paths correspond to wallet keys (accounts, payment paths, staking paths) and pool cold keys, as described in - [CIP 1852 - HD Wallets for Cardano](https://cips.cardano.org/cips/cip1852/); - [CIP 1853 - HD Stake Pool Cold Keys for Cardano](https://cips.cardano.org/cips/cip1853/). diff --git a/doc/ins_sign_tx.md b/doc/ins_sign_tx.md index 38e60123..bc9c8f1a 100644 --- a/doc/ins_sign_tx.md +++ b/doc/ins_sign_tx.md @@ -15,7 +15,7 @@ Due to Ledger constraints and potential security implications (parsing errors), **SignTx Limitations** - Output address size is limited to 128 bytes (single APDU). (Note: IOHK is fine with address size limit of 100 bytes) -- Addresses that are not shown to the user are base addresses with spending path `m/1852'/1815'/account'/{0,1}/changeIndex` and the standard stake key `m/1852'/1815'/account'/2/0`, where values of `account` and `changeIndex` are limited (for now, `0 <= account <= 100` and `0 <= changeIndex <= 1 000 000`). This makes it feasible to brute-force all change addresses in case an attacker manages to modify change address(es). (As the user does not confirm change addresses, it is relatively easy to perform MITM attack). +- Addresses that are not shown to the user are base addresses with payment key path `m/1852'/1815'/account'/{0,1}/changeIndex` and the standard stake key `m/1852'/1815'/account'/2/0`, where values of `account` and `changeIndex` are limited (for now, `0 <= account <= 100` and `0 <= changeIndex <= 1 000 000`). This makes it feasible to brute-force all change addresses in case an attacker manages to modify change address(es). (As the user does not confirm change addresses, it is relatively easy to perform MITM attack). - Only transactions with at least one input will be signed (this provides protection against certificate replays and transaction replays on different networks). **Communication protocol non-goals:** diff --git a/src/addressUtilsShelley.c b/src/addressUtilsShelley.c index f6db8d23..36ece7a4 100644 --- a/src/addressUtilsShelley.c +++ b/src/addressUtilsShelley.c @@ -200,17 +200,17 @@ static size_t deriveAddress_base(const addressParams_t* addressParams, uint8_t* view_appendBuffer(&out, &header, 1); ++size; } - STATIC_ASSERT(SIZEOF(addressParams->spendingScriptHash) == SCRIPT_HASH_LENGTH, "bad spending script hash size"); + STATIC_ASSERT(SIZEOF(addressParams->paymentScriptHash) == SCRIPT_HASH_LENGTH, "bad payment script hash size"); switch (addressParams->type) { case BASE_PAYMENT_KEY_STAKE_KEY: case BASE_PAYMENT_KEY_STAKE_SCRIPT: { - view_appendAddressPublicKeyHash(&out, &addressParams->spendingKeyPath); + view_appendAddressPublicKeyHash(&out, &addressParams->paymentKeyPath); size += ADDRESS_KEY_HASH_LENGTH; } break; case BASE_PAYMENT_SCRIPT_STAKE_KEY: case BASE_PAYMENT_SCRIPT_STAKE_SCRIPT: { - view_appendBuffer(&out, addressParams->spendingScriptHash, SCRIPT_HASH_LENGTH); + view_appendBuffer(&out, addressParams->paymentScriptHash, SCRIPT_HASH_LENGTH); size += SCRIPT_HASH_LENGTH; } break; @@ -296,9 +296,9 @@ static size_t deriveAddress_pointer( } { if (addressType == POINTER_KEY) { - view_appendAddressPublicKeyHash(&out, &addressParams->spendingKeyPath); + view_appendAddressPublicKeyHash(&out, &addressParams->paymentKeyPath); } else { - view_appendBuffer(&out, addressParams->spendingScriptHash, SCRIPT_HASH_LENGTH); + view_appendBuffer(&out, addressParams->paymentScriptHash, SCRIPT_HASH_LENGTH); } STATIC_ASSERT(SCRIPT_HASH_LENGTH == ADDRESS_KEY_HASH_LENGTH, "incompatible hash lengths"); @@ -332,9 +332,9 @@ static size_t deriveAddress_enterprise( } { if (addressType == ENTERPRISE_KEY) { - view_appendAddressPublicKeyHash(&out, &addressParams->spendingKeyPath); + view_appendAddressPublicKeyHash(&out, &addressParams->paymentKeyPath); } else { - view_appendBuffer(&out, addressParams->spendingScriptHash, SCRIPT_HASH_LENGTH); + view_appendBuffer(&out, addressParams->paymentScriptHash, SCRIPT_HASH_LENGTH); } } { @@ -365,7 +365,7 @@ static size_t deriveAddress_reward( view_appendBuffer(&out, &addressHeader, 1); } { - // no spending data + // no payment data } { if (addressType == REWARD_KEY) { @@ -460,7 +460,7 @@ size_t deriveAddress(const addressParams_t* addressParams, uint8_t* outBuffer, s #ifdef APP_FEATURE_BYRON_ADDRESS_DERIVATION case BYRON: return deriveAddress_byron( - &addressParams->spendingKeyPath, + &addressParams->paymentKeyPath, addressParams->protocolMagic, outBuffer, outSize ); @@ -541,7 +541,7 @@ size_t humanReadableAddress(const uint8_t* address, size_t addressSize, char* ou * protocol magic 4B * else * network id 1B - * spending public key derivation path (1B for length + [0-10] x 4B) + * payment public key derivation path (1B for length + [0-10] x 4B) * staking choice 1B * if NO_STAKING: * nothing more @@ -570,15 +570,15 @@ void view_parseAddressParams(read_view_t* view, addressParams_t* params) TRACE("Network id: 0x%x", params->networkId); VALIDATE(isValidNetworkId(params->networkId), ERR_INVALID_DATA); } - // spending part + // payment part switch (params->type) { case BASE_PAYMENT_KEY_STAKE_KEY: case BASE_PAYMENT_KEY_STAKE_SCRIPT: case POINTER_KEY: case ENTERPRISE_KEY: case BYRON: - view_skipBytes(view, bip44_parseFromWire(¶ms->spendingKeyPath, VIEW_REMAINING_TO_TUPLE_BUF_SIZE(view))); - BIP44_PRINTF(¶ms->spendingKeyPath); + view_skipBytes(view, bip44_parseFromWire(¶ms->paymentKeyPath, VIEW_REMAINING_TO_TUPLE_BUF_SIZE(view))); + BIP44_PRINTF(¶ms->paymentKeyPath); PRINTF("\n"); break; @@ -586,16 +586,16 @@ void view_parseAddressParams(read_view_t* view, addressParams_t* params) case BASE_PAYMENT_SCRIPT_STAKE_SCRIPT: case POINTER_SCRIPT: case ENTERPRISE_SCRIPT: { - STATIC_ASSERT(SIZEOF(params->spendingScriptHash) == SCRIPT_HASH_LENGTH, "Wrong address key hash length"); - view_parseBuffer(params->spendingScriptHash, view, SCRIPT_HASH_LENGTH); - TRACE("Spending script hash: "); - TRACE_BUFFER(params->spendingScriptHash, SIZEOF(params->spendingScriptHash)); + STATIC_ASSERT(SIZEOF(params->paymentScriptHash) == SCRIPT_HASH_LENGTH, "Wrong address key hash length"); + view_parseBuffer(params->paymentScriptHash, view, SCRIPT_HASH_LENGTH); + TRACE("Payment script hash: "); + TRACE_BUFFER(params->paymentScriptHash, SIZEOF(params->paymentScriptHash)); break; } case REWARD_KEY: case REWARD_SCRIPT: - // no spending info for reward address types + // no payment info for reward address types break; default: @@ -664,21 +664,21 @@ static inline bool isValidStakingInfo(const addressParams_t* params) #undef CHECK } -static inline bool isValidSpendingInfo(const addressParams_t* params) +static inline bool isValidPaymentInfo(const addressParams_t* params) { #define CHECK(cond) if (!(cond)) return false switch (params->type) { case BYRON: - CHECK(bip44_classifyPath(¶ms->spendingKeyPath) == PATH_ORDINARY_SPENDING_KEY); - CHECK(bip44_hasByronPrefix(¶ms->spendingKeyPath)); + CHECK(bip44_classifyPath(¶ms->paymentKeyPath) == PATH_ORDINARY_PAYMENT_KEY); + CHECK(bip44_hasByronPrefix(¶ms->paymentKeyPath)); break; case BASE_PAYMENT_KEY_STAKE_KEY: case BASE_PAYMENT_KEY_STAKE_SCRIPT: case POINTER_KEY: case ENTERPRISE_KEY: - CHECK(bip44_classifyPath(¶ms->spendingKeyPath) == PATH_ORDINARY_SPENDING_KEY); - CHECK(bip44_hasShelleyPrefix(¶ms->spendingKeyPath)); + CHECK(bip44_classifyPath(¶ms->paymentKeyPath) == PATH_ORDINARY_PAYMENT_KEY); + CHECK(bip44_hasShelleyPrefix(¶ms->paymentKeyPath)); break; @@ -713,13 +713,13 @@ bool isValidAddressParams(const addressParams_t* params) } CHECK(isValidStakingInfo(params)); - CHECK(isValidSpendingInfo(params)); + CHECK(isValidPaymentInfo(params)); return true; #undef CHECK } -spending_choice_t determineSpendingChoice(address_type_t addressType) +payment_choice_t determinePaymentChoice(address_type_t addressType) { switch (addressType) { @@ -728,19 +728,19 @@ spending_choice_t determineSpendingChoice(address_type_t addressType) case POINTER_KEY: case ENTERPRISE_KEY: case BYRON: - return SPENDING_PATH; + return PAYMENT_PATH; case BASE_PAYMENT_SCRIPT_STAKE_KEY: case BASE_PAYMENT_SCRIPT_STAKE_SCRIPT: case POINTER_SCRIPT: case ENTERPRISE_SCRIPT: - return SPENDING_SCRIPT_HASH; + return PAYMENT_SCRIPT_HASH; default: ASSERT(false); __attribute__((fallthrough)); case REWARD_KEY: case REWARD_SCRIPT: - return SPENDING_NONE; + return PAYMENT_NONE; } } diff --git a/src/addressUtilsShelley.h b/src/addressUtilsShelley.h index d1723a29..44be6f95 100644 --- a/src/addressUtilsShelley.h +++ b/src/addressUtilsShelley.h @@ -54,10 +54,10 @@ typedef enum { bool isValidStakingChoice(staking_data_source_t stakingDataSource); typedef enum { - SPENDING_PATH, - SPENDING_SCRIPT_HASH, - SPENDING_NONE, -} spending_choice_t; + PAYMENT_PATH, + PAYMENT_SCRIPT_HASH, + PAYMENT_NONE, +} payment_choice_t; typedef uint32_t blockchainIndex_t; // must be unsigned @@ -74,8 +74,8 @@ typedef struct { uint8_t networkId; // all the other types (i.e. Shelley) }; union { - bip44_path_t spendingKeyPath; - uint8_t spendingScriptHash[SCRIPT_HASH_LENGTH]; + bip44_path_t paymentKeyPath; + uint8_t paymentScriptHash[SCRIPT_HASH_LENGTH]; }; staking_data_source_t stakingDataSource; union { @@ -116,7 +116,7 @@ size_t humanReadableAddress(const uint8_t* address, size_t addressSize, char* ou void view_parseAddressParams(read_view_t* view, addressParams_t* params); bool isValidAddressParams(const addressParams_t* addressParams); -spending_choice_t determineSpendingChoice(address_type_t addressType); +payment_choice_t determinePaymentChoice(address_type_t addressType); #ifdef DEVEL void run_addressUtilsShelley_test(); diff --git a/src/addressUtilsShelley_test.c b/src/addressUtilsShelley_test.c index 9b2b91ba..1b4f54c5 100644 --- a/src/addressUtilsShelley_test.c +++ b/src/addressUtilsShelley_test.c @@ -17,7 +17,7 @@ static void pathSpec_init(bip44_path_t* pathSpec, const uint32_t* pathArray, uin // networkIdOrProtocolMagic is used as networkId for Shelley addresses and as protocol magic for Byron addresses static void testcase_deriveAddressShelley( - uint8_t type, uint32_t networkIdOrProtocolMagic, const uint32_t* spendingPathArray, size_t spendingPathLen, + uint8_t type, uint32_t networkIdOrProtocolMagic, const uint32_t* paymentPathArray, size_t paymentPathLen, uint8_t stakingDataSource, const uint32_t* stakingPathArray, size_t stakingPathLen, const char* stakingKeyHashHex, const blockchainPointer_t* stakingKeyBlockchainPointer, const char* expectedHex) @@ -56,7 +56,7 @@ static void testcase_deriveAddressShelley( }; } // the rest of params is initialized to zero - pathSpec_init(¶ms.spendingKeyPath, spendingPathArray, spendingPathLen); + pathSpec_init(¶ms.paymentKeyPath, paymentPathArray, paymentPathLen); if (stakingPathLen > 0) pathSpec_init(¶ms.stakingKeyPath, stakingPathArray, stakingPathLen); if (stakingKeyHashHex != NULL) { @@ -73,7 +73,7 @@ static void testcase_deriveAddressShelley( PRINTF("testcase_deriveAddressShelley 0x%02x ", constructShelleyAddressHeader(type, (uint8_t) networkIdOrProtocolMagic)); } - BIP44_PRINTF(¶ms.spendingKeyPath); + BIP44_PRINTF(¶ms.paymentKeyPath); PRINTF("\n"); if (stakingDataSource == STAKING_KEY_PATH) { @@ -104,16 +104,16 @@ static void testcase_deriveAddressShelley( } // test addresses for Shelley are generated by our Trezor implementation -// (from public keys derived by Ledger from the given spending and staking paths) +// (from public keys derived by Ledger from the given payment and staking paths) static void testAddressDerivation() { #define NO_STAKING_KEY_PATH () #define NO_STAKING_KEY_HASH NULL -#define TESTCASE(type_, networkIdOrProtocolMagic_, spendingPath_, stakingChoice_, stakingPath_, stakingKeyHashHex_, expected_) \ +#define TESTCASE(type_, networkIdOrProtocolMagic_, paymentPath_, stakingChoice_, stakingPath_, stakingKeyHashHex_, expected_) \ { \ - uint32_t spendingPath[] = { UNWRAP spendingPath_ }; \ + uint32_t paymentPath[] = { UNWRAP paymentPath_ }; \ uint32_t stakingPath[] = { UNWRAP stakingPath_ }; \ - testcase_deriveAddressShelley(type_, networkIdOrProtocolMagic_, spendingPath, ARRAY_LEN(spendingPath), stakingChoice_, stakingPath, ARRAY_LEN(stakingPath), stakingKeyHashHex_, NULL, expected_); \ + testcase_deriveAddressShelley(type_, networkIdOrProtocolMagic_, paymentPath, ARRAY_LEN(paymentPath), stakingChoice_, stakingPath, ARRAY_LEN(stakingPath), stakingKeyHashHex_, NULL, expected_); \ } TESTCASE( @@ -168,11 +168,11 @@ static void testAddressDerivation() #undef NO_STAKING_KEY_PATH #undef NO_STAKING_KEY_HASH -#define TESTCASE_POINTER(type_, networkId_, spendingPath_, stakingKeyBlockchainPointer_, expected_) \ +#define TESTCASE_POINTER(type_, networkId_, paymentPath_, stakingKeyBlockchainPointer_, expected_) \ { \ - uint32_t spendingPath[] = { UNWRAP spendingPath_ }; \ + uint32_t paymentPath[] = { UNWRAP paymentPath_ }; \ blockchainPointer_t stakingKeyBlockchainPointer = { UNWRAP stakingKeyBlockchainPointer_ }; \ - testcase_deriveAddressShelley(type_, networkId_, spendingPath, ARRAY_LEN(spendingPath), BLOCKCHAIN_POINTER, NULL, 0, NULL, &stakingKeyBlockchainPointer, expected_); \ + testcase_deriveAddressShelley(type_, networkId_, paymentPath, ARRAY_LEN(paymentPath), BLOCKCHAIN_POINTER, NULL, 0, NULL, &stakingKeyBlockchainPointer, expected_); \ } TESTCASE_POINTER( diff --git a/src/bip44.c b/src/bip44.c index f569c3f7..e59a597f 100644 --- a/src/bip44.c +++ b/src/bip44.c @@ -419,7 +419,7 @@ static bip44_path_type_t bip44_classifyOrdinaryWalletPath(const bip44_path_t* pa // so we don't want to make users' funds on such addresses unavailable. // But such addresses are given a warning // and are never hidden from users (see bip44_isPathReasonable). - return PATH_ORDINARY_SPENDING_KEY; + return PATH_ORDINARY_PAYMENT_KEY; case CARDANO_CHAIN_STAKING_KEY: return bip44_isOrdinaryStakingKeyPath(pathSpec) ? @@ -475,7 +475,7 @@ static bip44_path_type_t bip44_classifyMultisigWalletPath(const bip44_path_t* pa // address index must not be hardened (CIP 1854) return PATH_INVALID; } - return PATH_MULTISIG_SPENDING_KEY; + return PATH_MULTISIG_PAYMENT_KEY; case CARDANO_CHAIN_STAKING_KEY: return bip44_isMultisigStakingKeyPath(pathSpec) ? @@ -558,8 +558,8 @@ bool bip44_isPathReasonable(const bip44_path_t* pathSpec) case PATH_MULTISIG_ACCOUNT: return bip44_hasReasonableAccount(pathSpec); - case PATH_ORDINARY_SPENDING_KEY: - case PATH_MULTISIG_SPENDING_KEY: + case PATH_ORDINARY_PAYMENT_KEY: + case PATH_MULTISIG_PAYMENT_KEY: return bip44_hasReasonableAccount(pathSpec) && bip44_hasReasonableAddress(pathSpec); case PATH_ORDINARY_STAKING_KEY: diff --git a/src/bip44.h b/src/bip44.h index 038a0176..800763ca 100644 --- a/src/bip44.h +++ b/src/bip44.h @@ -99,8 +99,8 @@ typedef enum { PATH_MULTISIG_ACCOUNT, // hd wallet address (payment part in shelley) - PATH_ORDINARY_SPENDING_KEY, - PATH_MULTISIG_SPENDING_KEY, + PATH_ORDINARY_PAYMENT_KEY, + PATH_MULTISIG_PAYMENT_KEY, // hd wallet reward address, withdrawal witness, pool owner PATH_ORDINARY_STAKING_KEY, diff --git a/src/deriveAddress.c b/src/deriveAddress.c index 193f9df9..93c3cf4f 100644 --- a/src/deriveAddress.c +++ b/src/deriveAddress.c @@ -58,16 +58,16 @@ static void _displayExportAddress(ui_callback_fn_t* this_fn) #endif // HAVE_BAGL } -static void _displaySpendingInfo_returnAddr(ui_callback_fn_t* this_fn) +static void _displayPaymentInfo_returnAddr(ui_callback_fn_t* this_fn) { #ifdef HAVE_BAGL - ui_displaySpendingInfoScreen(&ctx->addressParams, this_fn); + ui_displayPaymentInfoScreen(&ctx->addressParams, this_fn); #elif defined(HAVE_NBGL) -#define SPENDING_INFO_SIZE MAX(BECH32_STRING_SIZE_MAX, BIP44_PATH_STRING_SIZE_MAX) +#define PAYMENT_INFO_SIZE MAX(BECH32_STRING_SIZE_MAX, BIP44_PATH_STRING_SIZE_MAX) char line1[30] = {0}; - char spendingInfo[SPENDING_INFO_SIZE] = {0}; - ui_getSpendingInfoScreen(line1, SIZEOF(line1), spendingInfo, SIZEOF(spendingInfo), &ctx->addressParams); - fill_and_display_if_required(line1, spendingInfo, this_fn, respond_with_user_reject); + char paymentInfo[PAYMENT_INFO_SIZE] = {0}; + ui_getPaymentInfoScreen(line1, SIZEOF(line1), paymentInfo, SIZEOF(paymentInfo), &ctx->addressParams); + fill_and_display_if_required(line1, paymentInfo, this_fn, respond_with_user_reject); #endif // HAVE_BAGL } @@ -108,7 +108,7 @@ static void deriveAddress_return_ui_runStep(); enum { RETURN_UI_STEP_WARNING = 100, RETURN_UI_STEP_BEGIN, - RETURN_UI_STEP_SPENDING_PATH, + RETURN_UI_STEP_PAYMENT_PATH, RETURN_UI_STEP_STAKING_INFO, RETURN_UI_STEP_CONFIRM, RETURN_UI_STEP_RESPOND, @@ -150,12 +150,12 @@ static void deriveAddress_return_ui_runStep() UI_STEP(RETURN_UI_STEP_BEGIN) { _displayExportAddress(this_fn); } - UI_STEP(RETURN_UI_STEP_SPENDING_PATH) { - if (determineSpendingChoice(ctx->addressParams.type) == SPENDING_NONE) { + UI_STEP(RETURN_UI_STEP_PAYMENT_PATH) { + if (determinePaymentChoice(ctx->addressParams.type) == PAYMENT_NONE) { // reward address UI_STEP_JUMP(RETURN_UI_STEP_STAKING_INFO); } - _displaySpendingInfo_returnAddr(this_fn); + _displayPaymentInfo_returnAddr(this_fn); } UI_STEP(RETURN_UI_STEP_STAKING_INFO) { _displayStakingInfo_returnAddr(this_fn); @@ -175,16 +175,16 @@ static void deriveAddress_return_ui_runStep() /* ========================== DISPLAY ADDRESS ========================== */ -static void _displaySpendingInfo_displayAddr(ui_callback_fn_t* this_fn) +static void _displaypaymentInfo_displayAddr(ui_callback_fn_t* this_fn) { #ifdef HAVE_BAGL - ui_displaySpendingInfoScreen(&ctx->addressParams, this_fn); + ui_displayPaymentInfoScreen(&ctx->addressParams, this_fn); #elif defined(HAVE_NBGL) -#define SPENDING_INFO_SIZE MAX(BECH32_STRING_SIZE_MAX, BIP44_PATH_STRING_SIZE_MAX) +#define PAYMENT_INFO_SIZE MAX(BECH32_STRING_SIZE_MAX, BIP44_PATH_STRING_SIZE_MAX) char line1[30] = {0}; - char spendingInfo[SPENDING_INFO_SIZE] = {0}; - ui_getSpendingInfoScreen(line1, SIZEOF(line1), spendingInfo, SIZEOF(spendingInfo), &ctx->addressParams); - fill_address_data(line1, spendingInfo, this_fn); + char paymentInfo[PAYMENT_INFO_SIZE] = {0}; + ui_getPaymentInfoScreen(line1, SIZEOF(line1), paymentInfo, SIZEOF(paymentInfo), &ctx->addressParams); + fill_address_data(line1, paymentInfo, this_fn); #endif // HAVE_BAGL } @@ -241,7 +241,7 @@ static void _displayConfirmAddressPrompt(ui_callback_fn_t* this_fn) static void deriveAddress_display_ui_runStep(); enum { DISPLAY_UI_STEP_WARNING = 200, - DISPLAY_UI_STEP_SPENDING_INFO, + DISPLAY_UI_STEP_PAYMENT_INFO, DISPLAY_UI_STEP_STAKING_INFO, DISPLAY_UI_STEP_ADDRESS, DISPLAY_UI_STEP_CONFIRM, @@ -261,7 +261,7 @@ static void deriveAddress_handleDisplay() switch (policy) { #define CASE(policy, step) case policy: {ctx->ui_step=step; break;} CASE(POLICY_PROMPT_WARN_UNUSUAL, DISPLAY_UI_STEP_WARNING); - CASE(POLICY_SHOW_BEFORE_RESPONSE, DISPLAY_UI_STEP_SPENDING_INFO); + CASE(POLICY_SHOW_BEFORE_RESPONSE, DISPLAY_UI_STEP_PAYMENT_INFO); #undef CASE default: THROW(ERR_NOT_IMPLEMENTED); @@ -279,12 +279,12 @@ static void deriveAddress_display_ui_runStep() UI_STEP(DISPLAY_UI_STEP_WARNING) { ui_displayUnusualWarning(this_fn); } - UI_STEP(DISPLAY_UI_STEP_SPENDING_INFO) { - if (determineSpendingChoice(ctx->addressParams.type) == SPENDING_NONE) { + UI_STEP(DISPLAY_UI_STEP_PAYMENT_INFO) { + if (determinePaymentChoice(ctx->addressParams.type) == PAYMENT_NONE) { // reward address UI_STEP_JUMP(DISPLAY_UI_STEP_STAKING_INFO); } - _displaySpendingInfo_displayAddr(this_fn); + _displaypaymentInfo_displayAddr(this_fn); } UI_STEP(DISPLAY_UI_STEP_STAKING_INFO) { _displayStakingInfo_displayAddr(this_fn); diff --git a/src/securityPolicy.c b/src/securityPolicy.c index 94ff7ef1..332f2ef2 100644 --- a/src/securityPolicy.c +++ b/src/securityPolicy.c @@ -9,7 +9,7 @@ // helper functions -// stake key path has the same account as the spending key path +// stake key path has the same account as the payment key path static inline bool is_standard_base_address(const addressParams_t* addressParams) { ASSERT(isValidAddressParams(addressParams)); @@ -18,8 +18,8 @@ static inline bool is_standard_base_address(const addressParams_t* addressParams CHECK(addressParams->type == BASE_PAYMENT_KEY_STAKE_KEY); CHECK(addressParams->stakingDataSource == STAKING_KEY_PATH); - CHECK(bip44_classifyPath(&addressParams->spendingKeyPath) == PATH_ORDINARY_SPENDING_KEY); - CHECK(bip44_isPathReasonable(&addressParams->spendingKeyPath)); + CHECK(bip44_classifyPath(&addressParams->paymentKeyPath) == PATH_ORDINARY_PAYMENT_KEY); + CHECK(bip44_isPathReasonable(&addressParams->paymentKeyPath)); CHECK(bip44_classifyPath(&addressParams->stakingKeyPath) == PATH_ORDINARY_STAKING_KEY); CHECK(bip44_isPathReasonable(&addressParams->stakingKeyPath)); @@ -29,7 +29,7 @@ static inline bool is_standard_base_address(const addressParams_t* addressParams CHECK( bip44_getAccount(&addressParams->stakingKeyPath) == - bip44_getAccount(&addressParams->spendingKeyPath) + bip44_getAccount(&addressParams->paymentKeyPath) ); return true; @@ -78,11 +78,11 @@ security_policy_t policyForDerivePrivateKey(const bip44_path_t* path) switch (bip44_classifyPath(path)) { case PATH_ORDINARY_ACCOUNT: - case PATH_ORDINARY_SPENDING_KEY: + case PATH_ORDINARY_PAYMENT_KEY: case PATH_ORDINARY_STAKING_KEY: case PATH_MULTISIG_ACCOUNT: - case PATH_MULTISIG_SPENDING_KEY: + case PATH_MULTISIG_PAYMENT_KEY: case PATH_MULTISIG_STAKING_KEY: case PATH_DREP_KEY: @@ -162,9 +162,9 @@ security_policy_t policyForGetExtendedPublicKey(const bip44_path_t* pathSpec) ALLOW(); break; - case PATH_ORDINARY_SPENDING_KEY: + case PATH_ORDINARY_PAYMENT_KEY: case PATH_ORDINARY_STAKING_KEY: - case PATH_MULTISIG_SPENDING_KEY: + case PATH_MULTISIG_PAYMENT_KEY: case PATH_MULTISIG_STAKING_KEY: case PATH_DREP_KEY: case PATH_COMMITTEE_COLD_KEY: @@ -189,10 +189,10 @@ security_policy_t policyForGetExtendedPublicKeyBulkExport(const bip44_path_t* pa switch (bip44_classifyPath(pathSpec)) { case PATH_ORDINARY_ACCOUNT: - case PATH_ORDINARY_SPENDING_KEY: + case PATH_ORDINARY_PAYMENT_KEY: case PATH_ORDINARY_STAKING_KEY: case PATH_MULTISIG_ACCOUNT: - case PATH_MULTISIG_SPENDING_KEY: + case PATH_MULTISIG_PAYMENT_KEY: case PATH_MULTISIG_STAKING_KEY: case PATH_DREP_KEY: case PATH_COMMITTEE_COLD_KEY: @@ -229,7 +229,7 @@ static security_policy_t _policyForDeriveAddress(const addressParams_t* addressP case BASE_PAYMENT_KEY_STAKE_KEY: // unusual path - WARN_UNLESS(bip44_isPathReasonable(&addressParams->spendingKeyPath)); + WARN_UNLESS(bip44_isPathReasonable(&addressParams->paymentKeyPath)); WARN_IF( addressParams->stakingDataSource == STAKING_KEY_PATH && !bip44_isPathReasonable(&addressParams->stakingKeyPath) @@ -241,7 +241,7 @@ static security_policy_t _policyForDeriveAddress(const addressParams_t* addressP case ENTERPRISE_KEY: case BYRON: // unusual path - WARN_UNLESS(bip44_isPathReasonable(&addressParams->spendingKeyPath)); + WARN_UNLESS(bip44_isPathReasonable(&addressParams->paymentKeyPath)); break; case BASE_PAYMENT_SCRIPT_STAKE_KEY: @@ -570,7 +570,7 @@ static bool contains_forbidden_plutus_elements( bool needsMissingDatumWarning(const tx_output_destination_t* destination, bool includeDatum) { - const bool mightRequireDatum = determineSpendingChoice(getDestinationAddressType(destination)) == SPENDING_SCRIPT_HASH; + const bool mightRequireDatum = determinePaymentChoice(getDestinationAddressType(destination)) == PAYMENT_SCRIPT_HASH; return mightRequireDatum && !includeDatum; } @@ -647,12 +647,12 @@ static bool is_addressParams_suitable_for_tx_output( // this captures the essence of a change output: money stays // on an address where payment is fully controlled by this device - CHECK(determineSpendingChoice(params->type) == SPENDING_PATH); - // Note: if we allowed script hash in spending part, we must add a warning + CHECK(determinePaymentChoice(params->type) == PAYMENT_PATH); + // Note: if we allowed script hash in payment part, we must add a warning // for missing datum (see policyForSignTxOutputAddressBytes) - ASSERT(determineSpendingChoice(params->type) == SPENDING_PATH); - CHECK(!violatesSingleAccountOrStoreIt(¶ms->spendingKeyPath)); + ASSERT(determinePaymentChoice(params->type) == PAYMENT_PATH); + CHECK(!violatesSingleAccountOrStoreIt(¶ms->paymentKeyPath)); } return true; @@ -677,7 +677,7 @@ security_policy_t policyForSignTxOutputAddressParams( case SIGN_TX_SIGNINGMODE_POOL_REGISTRATION_OPERATOR: case SIGN_TX_SIGNINGMODE_ORDINARY_TX: { - // unusual paths or spending and staking path mismatch + // unusual paths or payment and staking path mismatch SHOW_UNLESS(is_standard_base_address(params)); // outputs (eUTXOs) with datum or ref script are not interchangeable @@ -694,7 +694,7 @@ security_policy_t policyForSignTxOutputAddressParams( case SIGN_TX_SIGNINGMODE_MULTISIG_TX: { // for simplicity, all outputs should be given as external addresses; // generally, more than one party is needed to sign - // spending from a multisig address, so we do not expect + // payment from a multisig address, so we do not expect // there will be 1852 outputs (that would be considered change) DENY(); break; @@ -1535,7 +1535,7 @@ security_policy_t policyForSignTxWithdrawal( static inline security_policy_t _ordinaryWitnessPolicy(const bip44_path_t* path, bool mintPresent) { switch (bip44_classifyPath(path)) { - case PATH_ORDINARY_SPENDING_KEY: + case PATH_ORDINARY_PAYMENT_KEY: case PATH_ORDINARY_STAKING_KEY: // ordinary key paths can be hidden if they are not unusual // (the user saw all outputs not belonging to him, withdrawals and certificates, @@ -1583,7 +1583,7 @@ static inline security_policy_t _ordinaryWitnessPolicy(const bip44_path_t* path, static inline security_policy_t _multisigWitnessPolicy(const bip44_path_t* path, bool mintPresent) { switch (bip44_classifyPath(path)) { - case PATH_MULTISIG_SPENDING_KEY: + case PATH_MULTISIG_PAYMENT_KEY: case PATH_MULTISIG_STAKING_KEY: // multisig key paths are allowed, but hiding them would make impossible for the user to // distinguish what funds are being spent (multisig UTXOs sharing a signer are not @@ -1611,9 +1611,9 @@ static inline security_policy_t _plutusWitnessPolicy(const bip44_path_t* path, b { switch (bip44_classifyPath(path)) { // in PLUTUS_TX, we allow signing with any path, but it must be shown - case PATH_ORDINARY_SPENDING_KEY: + case PATH_ORDINARY_PAYMENT_KEY: case PATH_ORDINARY_STAKING_KEY: - case PATH_MULTISIG_SPENDING_KEY: + case PATH_MULTISIG_PAYMENT_KEY: case PATH_MULTISIG_STAKING_KEY: case PATH_DREP_KEY: case PATH_COMMITTEE_COLD_KEY: @@ -1668,9 +1668,9 @@ static inline security_policy_t _poolRegistrationOperatorWitnessPolicy(const bip { switch (bip44_classifyPath(path)) { - case PATH_ORDINARY_SPENDING_KEY: + case PATH_ORDINARY_PAYMENT_KEY: case PATH_POOL_COLD_KEY: - // only ordinary spending key paths (because of inputs) and pool cold key path are allowed + // only ordinary payment key paths (because of inputs) and pool cold key path are allowed WARN_UNLESS(bip44_isPathReasonable(path)); // it might be safe to hide the witnesses, but txs related to stake pools // are rare, so it would not help much and might introduce some unknown risk @@ -1866,12 +1866,12 @@ static bool is_required_signer_allowed(bip44_path_t* path) { switch (bip44_classifyPath(path)) { case PATH_ORDINARY_ACCOUNT: - case PATH_ORDINARY_SPENDING_KEY: + case PATH_ORDINARY_PAYMENT_KEY: case PATH_ORDINARY_STAKING_KEY: return bip44_hasShelleyPrefix(path); case PATH_MULTISIG_ACCOUNT: - case PATH_MULTISIG_SPENDING_KEY: + case PATH_MULTISIG_PAYMENT_KEY: case PATH_MULTISIG_STAKING_KEY: return true; @@ -2191,9 +2191,9 @@ security_policy_t policyForSignMsg( ) { switch (bip44_classifyPath(witnessPath)) { - case PATH_ORDINARY_SPENDING_KEY: + case PATH_ORDINARY_PAYMENT_KEY: case PATH_ORDINARY_STAKING_KEY: - case PATH_MULTISIG_SPENDING_KEY: + case PATH_MULTISIG_PAYMENT_KEY: case PATH_MULTISIG_STAKING_KEY: case PATH_MINT_KEY: case PATH_DREP_KEY: diff --git a/src/signMsg_ui.c b/src/signMsg_ui.c index cbe4ba69..6406f0ed 100644 --- a/src/signMsg_ui.c +++ b/src/signMsg_ui.c @@ -101,15 +101,15 @@ void signMsg_handleInit_ui_runStep() fill_and_display_if_required("Address field", humanAddress, this_fn, respond_with_user_reject); #endif // HAVE_BAGL } - UI_STEP(HANDLE_INIT_ADDRESS_FIELD_DISPLAY_SPENDING_PATH) { + UI_STEP(HANDLE_INIT_ADDRESS_FIELD_DISPLAY_PAYMENT_PATH) { #ifdef HAVE_BAGL - ui_displaySpendingInfoScreen(&ctx->addressParams, this_fn); + ui_displayPaymentInfoScreen(&ctx->addressParams, this_fn); #elif defined(HAVE_NBGL) -#define SPENDING_INFO_SIZE MAX(BECH32_STRING_SIZE_MAX, BIP44_PATH_STRING_SIZE_MAX) +#define PAYMENT_INFO_SIZE MAX(BECH32_STRING_SIZE_MAX, BIP44_PATH_STRING_SIZE_MAX) char line1[30]; - char spendingInfo[SPENDING_INFO_SIZE] = {0}; - ui_getSpendingInfoScreen(line1, SIZEOF(line1), spendingInfo, SIZEOF(spendingInfo), &ctx->addressParams); - fill_and_display_if_required(line1, spendingInfo, this_fn, respond_with_user_reject); + char paymentInfo[PAYMENT_INFO_SIZE] = {0}; + ui_getPaymentInfoScreen(line1, SIZEOF(line1), paymentInfo, SIZEOF(paymentInfo), &ctx->addressParams); + fill_and_display_if_required(line1, paymentInfo, this_fn, respond_with_user_reject); #endif // HAVE_BAGL } UI_STEP(HANDLE_INIT_ADDRESS_FIELD_DISPLAY_STAKING_INFO) { diff --git a/src/signMsg_ui.h b/src/signMsg_ui.h index defc317c..d6b7b87d 100644 --- a/src/signMsg_ui.h +++ b/src/signMsg_ui.h @@ -10,7 +10,7 @@ enum { HANDLE_INIT_WITNESS_PATH, HANDLE_INIT_ADDRESS_FIELD_DISPLAY_KEY_HASH, HANDLE_INIT_ADDRESS_FIELD_DISPLAY_ADDRESS, - HANDLE_INIT_ADDRESS_FIELD_DISPLAY_SPENDING_PATH, + HANDLE_INIT_ADDRESS_FIELD_DISPLAY_PAYMENT_PATH, HANDLE_INIT_ADDRESS_FIELD_DISPLAY_STAKING_INFO, HANDLE_INIT_RESPOND, HANDLE_INIT_INVALID, diff --git a/src/signTxOutput_ui.c b/src/signTxOutput_ui.c index de75d286..72ba1442 100644 --- a/src/signTxOutput_ui.c +++ b/src/signTxOutput_ui.c @@ -66,7 +66,7 @@ void signTx_handleOutput_address_bytes_ui_runStep() #endif // HAVE_BAGL } UI_STEP(HANDLE_OUTPUT_ADDRESS_BYTES_STEP_WARNING_DATUM) { - // this warning does not apply to address given by params where we only allow key hash spending part + // this warning does not apply to address given by params where we only allow key hash payment part // in which case datum is just optional and rarely used if (_needsMissingDatumWarning()) { #ifdef HAVE_BAGL @@ -124,15 +124,15 @@ void signTx_handleOutput_addressParams_ui_runStep() display_prompt(msg, "", this_fn, respond_with_user_reject); #endif // HAVE_BAGL } - UI_STEP(HANDLE_OUTPUT_ADDRESS_PARAMS_STEP_DISPLAY_SPENDING_PATH) { + UI_STEP(HANDLE_OUTPUT_ADDRESS_PARAMS_STEP_DISPLAY_PAYMENT_PATH) { #ifdef HAVE_BAGL - ui_displaySpendingInfoScreen(&subctx->stateData.destination.params, this_fn); + ui_displayPaymentInfoScreen(&subctx->stateData.destination.params, this_fn); #elif defined(HAVE_NBGL) -#define SPENDING_INFO_SIZE MAX(BECH32_STRING_SIZE_MAX, BIP44_PATH_STRING_SIZE_MAX) +#define PAYMENT_INFO_SIZE MAX(BECH32_STRING_SIZE_MAX, BIP44_PATH_STRING_SIZE_MAX) char line1[30]; - char spendingInfo[SPENDING_INFO_SIZE] = {0}; - ui_getSpendingInfoScreen(line1, SIZEOF(line1), spendingInfo, SIZEOF(spendingInfo), &subctx->stateData.destination.params); - fill_and_display_if_required(line1, spendingInfo, this_fn, respond_with_user_reject); + char paymentInfoInfo[PAYMENT_INFO_SIZE] = {0}; + ui_getPaymentInfoScreen(line1, SIZEOF(line1), paymentInfoInfo, SIZEOF(paymentInfoInfo), &subctx->stateData.destination.params); + fill_and_display_if_required(line1, paymentInfoInfo, this_fn, respond_with_user_reject); #endif // HAVE_BAGL } UI_STEP(HANDLE_OUTPUT_ADDRESS_PARAMS_STEP_DISPLAY_STAKING_INFO) { diff --git a/src/signTxOutput_ui.h b/src/signTxOutput_ui.h index 45994512..d9494d06 100644 --- a/src/signTxOutput_ui.h +++ b/src/signTxOutput_ui.h @@ -22,7 +22,7 @@ void signTx_handleOutput_address_bytes_ui_runStep(); enum { HANDLE_OUTPUT_ADDRESS_PARAMS_STEP_DISPLAY_BEGIN = 3200, HANDLE_OUTPUT_ADDRESS_PARAMS_STEP_WARNING_DATUM, - HANDLE_OUTPUT_ADDRESS_PARAMS_STEP_DISPLAY_SPENDING_PATH, + HANDLE_OUTPUT_ADDRESS_PARAMS_STEP_DISPLAY_PAYMENT_PATH, HANDLE_OUTPUT_ADDRESS_PARAMS_STEP_DISPLAY_STAKING_INFO, HANDLE_OUTPUT_ADDRESS_PARAMS_STEP_DISPLAY_ADDRESS, HANDLE_OUTPUT_ADDRESS_PARAMS_STEP_DISPLAY_AMOUNT, diff --git a/src/uiScreens_bagl.c b/src/uiScreens_bagl.c index ab0d85d1..83bfb3ee 100644 --- a/src/uiScreens_bagl.c +++ b/src/uiScreens_bagl.c @@ -364,35 +364,35 @@ void ui_displayRewardAccountScreen( ); } -void ui_displaySpendingInfoScreen( +void ui_displayPaymentInfoScreen( const addressParams_t* addressParams, ui_callback_fn_t callback ) { - switch (determineSpendingChoice(addressParams->type)) { + switch (determinePaymentChoice(addressParams->type)) { - case SPENDING_PATH: { + case PAYMENT_PATH: { ui_displayPathScreen( - "Spending key path", - &addressParams->spendingKeyPath, + "Payment key path", + &addressParams->paymentKeyPath, callback ); return; } - case SPENDING_SCRIPT_HASH: { + case PAYMENT_SCRIPT_HASH: { ui_displayBech32Screen( - "Spending script hash", + "Payment script hash", "script", - addressParams->spendingScriptHash, - SIZEOF(addressParams->spendingScriptHash), + addressParams->paymentScriptHash, + SIZEOF(addressParams->paymentScriptHash), callback ); return; } default: { - // includes SPENDING_NONE + // includes PAYMENT_NONE ASSERT(false); } } diff --git a/src/uiScreens_bagl.h b/src/uiScreens_bagl.h index abb162bb..f0d78339 100644 --- a/src/uiScreens_bagl.h +++ b/src/uiScreens_bagl.h @@ -57,7 +57,7 @@ void ui_displayRewardAccountScreen( ); __noinline_due_to_stack__ -void ui_displaySpendingInfoScreen( +void ui_displayPaymentInfoScreen( const addressParams_t* addressParams, ui_callback_fn_t callback ); diff --git a/src/uiScreens_nbgl.c b/src/uiScreens_nbgl.c index 1dfca96a..86a1034a 100644 --- a/src/uiScreens_nbgl.c +++ b/src/uiScreens_nbgl.c @@ -333,7 +333,7 @@ void ui_getRewardAccountScreen( ); } -void ui_getSpendingInfoScreen( +void ui_getPaymentInfoScreen( char* line1, const size_t line1Size, char* line2, @@ -341,32 +341,32 @@ void ui_getSpendingInfoScreen( const addressParams_t* addressParams ) { - switch (determineSpendingChoice(addressParams->type)) { + switch (determinePaymentChoice(addressParams->type)) { - case SPENDING_PATH: { - snprintf(line1, line1Size, "Derivation path"); + case PAYMENT_PATH: { + snprintf(line1, line1Size, "Payment key path"); ui_getPathScreen( line2, line2Size, - &addressParams->spendingKeyPath + &addressParams->paymentKeyPath ); return; } - case SPENDING_SCRIPT_HASH: { - snprintf(line1, line1Size, "Spending script hash"); + case PAYMENT_SCRIPT_HASH: { + snprintf(line1, line1Size, "Payment script hash"); ui_getBech32Screen( line2, line2Size, "script", - addressParams->spendingScriptHash, - SIZEOF(addressParams->spendingScriptHash) + addressParams->paymentScriptHash, + SIZEOF(addressParams->paymentScriptHash) ); return; } default: { - // includes SPENDING_NONE + // includes PAYMENT_NONE ASSERT(false); } } diff --git a/src/uiScreens_nbgl.h b/src/uiScreens_nbgl.h index e5120cfa..664d83fb 100644 --- a/src/uiScreens_nbgl.h +++ b/src/uiScreens_nbgl.h @@ -74,7 +74,7 @@ void ui_getRewardAccountScreen( ); __noinline_due_to_stack__ -void ui_getSpendingInfoScreen( +void ui_getPaymentInfoScreen( char* line1, const size_t line1Size, char* line2, From 0281c1feddfdbb3306011c0c5d834d892127f92a Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Sun, 25 Feb 2024 10:52:40 +0100 Subject: [PATCH 23/43] update: token list with decimals --- tokenRegistry/tokenList.json | 218 ++++++++++++++++++++++++++++++++--- tokenRegistry/token_data.c | 38 +++++- 2 files changed, 233 insertions(+), 23 deletions(-) diff --git a/tokenRegistry/tokenList.json b/tokenRegistry/tokenList.json index 1fd16b5a..332f5d8b 100644 --- a/tokenRegistry/tokenList.json +++ b/tokenRegistry/tokenList.json @@ -97,7 +97,7 @@ }, { "assetSubject": "7f376e3d1cf52e6c4350a1a91c8f8d0f0b63baedd443999ebe8fe57a424f52475a", - "name": "BORGZ", + "name": "Battle Borgz", "ticker": "BORGZ", "decimals": 0 }, @@ -275,12 +275,6 @@ "ticker": "RAT", "decimals": 6 }, - { - "assetSubject": "8fef2d34078659493ce161a6c7fba4b56afefa8535296a5743f6958741414441", - "name": "Aada DAO Token", - "ticker": "AADA", - "decimals": 6 - }, { "assetSubject": "0c78f619e54a5d00e143f66181a2c500d0c394b38a10e86cd1a23c5f41444158", "name": "ADAX", @@ -395,12 +389,6 @@ "ticker": "DOEX", "decimals": 0 }, - { - "assetSubject": "25f0fc240e91bd95dcdaebd2ba7713fc5168ac77234a3d79449fc20c534f4349455459", - "name": "SOCIETY", - "ticker": "SOCIETY", - "decimals": 6 - }, { "assetSubject": "c9f955eeffa84e42363b4992281d32dd2f9239153d6c66420a9acc154c4f47", "name": "Life Log Token", @@ -503,12 +491,6 @@ "ticker": "DGADA", "decimals": 0 }, - { - "assetSubject": "133fac9e153194428eb0919be39837b42b9e977fc7298f3ff1b76ef95055444759", - "name": "$PUDGY Token", - "ticker": "PUDGY", - "decimals": 0 - }, { "assetSubject": "f555c46bad0731d080c9381d7fff6f82839946a66bd070d185e1ea2f42444f4745", "name": "BlackDoge", @@ -610,5 +592,203 @@ "name": "iUSD", "ticker": "iUSD", "decimals": 6 + }, + { + "assetSubject": "279c909f348e533da5808898f87f9a14bb2c3dfbbacccd631d927a3f534e454b", + "name": "Snek", + "ticker": "SNEK", + "decimals": 0 + }, + { + "assetSubject": "95a427e384527065f2f8946f5e86320d0117839a5e98ea2c0b55fb0048554e54", + "name": "HUNT", + "ticker": "HUNT", + "decimals": 6 + }, + { + "assetSubject": "5d16cc1a177b5d9ba9cfa9793b07e60f1fb70fea1f8aef064415d114494147", + "name": "IAGON", + "ticker": "IAG", + "decimals": 6 + }, + { + "assetSubject": "749d101a169dbd15bcd488d3fca942665b7bc6f69512235bd301c5864652454e", + "name": "FREN", + "ticker": "FREN", + "decimals": 0 + }, + { + "assetSubject": "8fef2d34078659493ce161a6c7fba4b56afefa8535296a5743f6958741414441", + "name": "Lenfi DAO token", + "ticker": "LENFI", + "decimals": 6 + }, + { + "assetSubject": "f66d78b4a3cb3d37afa0ec36461e51ecbde00f26c8f0a68f94b6988069425443", + "name": "iBTC", + "ticker": "iBTC", + "decimals": 6 + }, + { + "assetSubject": "51a5e236c4de3af2b8020442e2a26f454fda3b04cb621c1294a0ef34424f4f4b", + "name": "BOOK", + "ticker": "BOOK", + "decimals": 6 + }, + { + "assetSubject": "edfd7a1d77bcb8b884c474bdc92a16002d1fb720e454fa6e993444794e5458", + "name": "NuNet Utility Token", + "ticker": "NTX", + "decimals": 6 + }, + { + "assetSubject": "a3931691f5c4e65d01c429e473d0dd24c51afdb6daf88e632a6c1e516f7263666178746f6b656e", + "name": "Orcfax Token", + "ticker": "FACT", + "decimals": 6 + }, + { + "assetSubject": "7914fae20eb2903ed6fd5021a415c1bd2626b64a2d86a304cb40ff5e4c494649", + "name": "LinkageFinanceToken", + "ticker": "LIFI", + "decimals": 6 + }, + { + "assetSubject": "e52964af4fffdb54504859875b1827b60ba679074996156461143dc14f5054494d", + "name": "OPTIM", + "ticker": "OPTIM", + "decimals": 6 + }, + { + "assetSubject": "1ddcb9c9de95361565392c5bdff64767492d61a96166cb16094e54be4f5054", + "name": "OptionFlowToken", + "ticker": "OPT", + "decimals": 6 + }, + { + "assetSubject": "2d587111358801114f04df83dc0015de0a740b462b75cce5170fc935434749", + "name": "CardanoGPT", + "ticker": "CGI", + "decimals": 6 + }, + { + "assetSubject": "8db269c3ec630e06ae29f74bc39edd1f87c819f1056206e879a1cd615368656e4d6963726f555344", + "name": "Shen USD", + "ticker": "SHEN", + "decimals": 6 + }, + { + "assetSubject": "cc8d1b026353022abbfcc2e1e71159f9e308d9c6e905ac1db24c7fb650617269627573", + "name": "Paribus", + "ticker": "PBX", + "decimals": 6 + }, + { + "assetSubject": "f66d78b4a3cb3d37afa0ec36461e51ecbde00f26c8f0a68f94b6988069455448", + "name": "iETH", + "ticker": "iETH", + "decimals": 6 + }, + { + "assetSubject": "b166a1047a8cd275bf0a50201ece3d4f0b4da300094ffcc668a6f4084b49545550", + "name": "KITUP", + "ticker": "KITUP", + "decimals": 0 + }, + { + "assetSubject": "dda5fdb1002f7389b33e036b6afee82a8189becb6cba852e8b79b4fb0014df1047454e53", + "name": "Genius Yield Token", + "ticker": "GENS", + "decimals": 6 + }, + { + "assetSubject": "b201928d6bdb21c2e39205a92e226653d6002b949eaaacde3d986c2f524f4e", + "name": "Ron", + "ticker": "RON", + "decimals": 0 + }, + { + "assetSubject": "ac015c38917f306a84748c2d646bed90bdd64421c592163e60702d735453555255", + "name": "TOOL", + "ticker": "TOOL", + "decimals": 0 + }, + { + "assetSubject": "681b5d0383ac3b457e1bcc453223c90ccef26b234328f45fa10fd2764a5047", + "name": "JPG TOKEN", + "ticker": "JPG", + "decimals": 6 + }, + { + "assetSubject": "25f0fc240e91bd95dcdaebd2ba7713fc5168ac77234a3d79449fc20c534f4349455459", + "name": "Society", + "ticker": "SOC", + "decimals": 6 + }, + { + "assetSubject": "caff93803e51c7b97bf79146790bfa3feb0d0b856ef16113b391b9975649504552", + "name": "VIPER", + "ticker": "VIPER", + "decimals": 0 + }, + { + "assetSubject": "9abf0afd2f236a19f2842d502d0450cbcd9c79f123a9708f96fd9b96454e4353", + "name": "ENCOINS utility token", + "ticker": "ENCS", + "decimals": 6 + }, + { + "assetSubject": "2b28c81dbba6d67e4b5a997c6be1212cba9d60d33f82444ab8b1f21842414e4b", + "name": "Bankercoin", + "ticker": "BANK", + "decimals": 0 + }, + { + "assetSubject": "a1c94e56e4b24945338f2779e33b02e52fdd30d5f279c6131cc43c7c42555a5a", + "name": "Buzz The Bellboy", + "ticker": "BUZZ", + "decimals": 2 + }, + { + "assetSubject": "04b95368393c821f180deee8229fbd941baaf9bd748ebcdbf7adbb147273455247", + "name": "rsERG", + "ticker": "rsERG", + "decimals": 9 + }, + { + "assetSubject": "c27600f3aff3d94043464a33786429b78e6ab9df5e1d23b774acb34c434e4354", + "name": "Coinecta", + "ticker": "CNCT", + "decimals": 4 + }, + { + "assetSubject": "420000029ad9527271b1b1e3c27ee065c18df70a4a4cfc3093a41a4441584f", + "name": "AXO", + "ticker": "AXO", + "decimals": 9 + }, + { + "assetSubject": "09f2d4e4a5c3662f4c1e6a7d9600e9605279dbdcedb22d4507cb6e75535046", + "name": "SPF", + "ticker": "SPF", + "decimals": 6 + }, + { + "assetSubject": "86abe45be4d8fb2e8f28e8047d17d0ba5592f2a6c8c452fc88c2c14358524159", + "name": "XRAY", + "ticker": "XRAY", + "decimals": 6 + }, + { + "assetSubject": "8daefa391220bd0d8d007f3748d870f7f3c106040314c8515ccc35a5464c4143", + "name": "Flac Finance", + "ticker": "FLAC", + "decimals": 6 + }, + { + "assetSubject": "75fcc276057db5fc48eae0e11453c773c8a54604c3086bf9d95ac1b743485259", + "name": "CherryLend", + "ticker": "CHRY", + "decimals": 6 } ] diff --git a/tokenRegistry/token_data.c b/tokenRegistry/token_data.c index 34e208fe..270d09f2 100644 --- a/tokenRegistry/token_data.c +++ b/tokenRegistry/token_data.c @@ -44,7 +44,6 @@ { { 0xc1, 0xb9, 0x94, 0x86, 0xca, 0xf1, 0x1a, 0xd8, 0x93, 0xb9, 0xcc, 0x59, 0x27, 0x0d, 0x22, 0x00, 0x71, 0xce, 0xd5, 0xeb }, 0, "SWEET" }, { { 0x0a, 0xe3, 0xb4, 0x86, 0xe8, 0x5a, 0xf1, 0x50, 0xb4, 0x78, 0x1a, 0x13, 0x17, 0x1f, 0x27, 0x91, 0x4b, 0x3c, 0xb6, 0x1b }, 0, "TOKEN" }, { { 0xbc, 0xca, 0x70, 0x17, 0x1f, 0xa6, 0x6f, 0x68, 0x0a, 0xe9, 0x3d, 0x42, 0x3b, 0xac, 0xac, 0x97, 0xcd, 0xa6, 0xd7, 0x91 }, 6, "RAT" }, -{ { 0xb5, 0xed, 0x5d, 0x2d, 0xa9, 0x85, 0xa6, 0x2c, 0x96, 0xc7, 0xaf, 0x72, 0xfc, 0xe5, 0xf6, 0x51, 0x8f, 0xbc, 0x3a, 0xe2 }, 6, "AADA" }, { { 0xb4, 0x45, 0x60, 0xb8, 0xff, 0xb6, 0x9e, 0xf2, 0x80, 0x74, 0x14, 0x2e, 0x6c, 0xf3, 0x2e, 0xcb, 0xe6, 0xfc, 0x15, 0xd9 }, 0, "ADAX" }, { { 0xfc, 0x09, 0x66, 0xfe, 0x97, 0x60, 0x10, 0xfe, 0x99, 0x4e, 0xb8, 0x5e, 0x92, 0x88, 0x31, 0x06, 0x23, 0x15, 0x10, 0x97 }, 6, "BLC" }, { { 0x33, 0xc2, 0xc5, 0x94, 0xcc, 0xf0, 0x2b, 0xeb, 0x0d, 0x45, 0xa8, 0xa2, 0xcc, 0x7e, 0x44, 0xa8, 0x15, 0x16, 0xe4, 0x66 }, 0, "Pina" }, @@ -64,7 +63,6 @@ { { 0xef, 0xb2, 0xfe, 0xf8, 0xf1, 0x41, 0xba, 0x2d, 0x36, 0x03, 0xc4, 0x99, 0xa5, 0xa7, 0xbb, 0x56, 0xe6, 0x3e, 0xd3, 0x6a }, 0, "TREES" }, { { 0x33, 0x4c, 0xa4, 0xeb, 0x94, 0xa1, 0x93, 0xe2, 0x14, 0x1b, 0x96, 0x84, 0x2d, 0x0d, 0x86, 0x30, 0xb7, 0x15, 0x43, 0x81 }, 0, "LOOKZ" }, { { 0x73, 0x39, 0x1f, 0xb2, 0xdd, 0xfe, 0xdb, 0xee, 0x6d, 0xe8, 0x66, 0xe4, 0xb7, 0x6d, 0x60, 0xf8, 0x52, 0x62, 0x35, 0x75 }, 0, "DOEX" }, -{ { 0x58, 0x61, 0x97, 0x35, 0x64, 0x6d, 0x94, 0xa8, 0xc9, 0x4b, 0xb1, 0x94, 0xeb, 0xbc, 0xa0, 0x0a, 0xf8, 0x44, 0x67, 0x51 }, 6, "SOCIETY" }, { { 0x1b, 0x4e, 0x44, 0x23, 0x7b, 0x3e, 0x88, 0x3f, 0x1d, 0x74, 0x06, 0x89, 0xcc, 0xe4, 0xd1, 0x5f, 0x63, 0xa1, 0x62, 0xf9 }, 6, "LOG" }, { { 0x93, 0xe9, 0x1a, 0xbb, 0x2f, 0x08, 0x0a, 0xe9, 0x5d, 0x73, 0xce, 0xfa, 0x2c, 0x33, 0xab, 0xc5, 0x1b, 0x05, 0xd3, 0x8c }, 0, "$CLAW" }, { { 0x09, 0xf5, 0xcd, 0x55, 0x6b, 0x56, 0x25, 0xfd, 0xaf, 0xee, 0xa1, 0x0f, 0x08, 0xac, 0x54, 0xbf, 0x72, 0xf3, 0xa3, 0x2d }, 0, "PIGY" }, @@ -82,7 +80,6 @@ { { 0x0f, 0x08, 0xfb, 0x00, 0x3c, 0xe8, 0xe0, 0x40, 0x84, 0xd6, 0x0f, 0xca, 0xe4, 0x92, 0x0c, 0xc9, 0xb4, 0xaf, 0xbb, 0x6c }, 0, "HIS" }, { { 0x6d, 0x88, 0xa7, 0x0d, 0xd7, 0x0a, 0x74, 0x0f, 0x89, 0x71, 0x8c, 0x6f, 0xf2, 0xdd, 0xef, 0x6a, 0xf1, 0xbf, 0x49, 0x32 }, 0, "CDFC6" }, { { 0x24, 0x6a, 0x12, 0xb6, 0x19, 0x32, 0x95, 0x54, 0xcb, 0xce, 0xa5, 0x2d, 0x6c, 0xd0, 0x88, 0x22, 0x80, 0x1d, 0x30, 0x97 }, 0, "DGADA" }, -{ { 0x0b, 0x02, 0x47, 0x51, 0x70, 0x2b, 0x12, 0x94, 0x44, 0xe2, 0x94, 0x1d, 0xf0, 0x7d, 0x45, 0x49, 0xf7, 0x16, 0x60, 0x42 }, 0, "PUDGY" }, { { 0x07, 0x1c, 0xe8, 0x95, 0xb6, 0xe5, 0xbc, 0x82, 0x48, 0xfd, 0x3c, 0x22, 0x23, 0x68, 0x6e, 0x42, 0x22, 0xa7, 0xd1, 0x2f }, 0, "BDOGE" }, { { 0xc8, 0x08, 0xd4, 0xc6, 0x12, 0x93, 0xdf, 0x37, 0x14, 0x00, 0x04, 0xca, 0x06, 0xfa, 0x81, 0xe0, 0xe4, 0x52, 0x28, 0xf4 }, 6, "MEOW" }, { { 0x20, 0xee, 0xdc, 0xc7, 0xe1, 0x87, 0xd5, 0xa6, 0x0f, 0x6b, 0x35, 0x57, 0x24, 0x3b, 0x61, 0xfa, 0xa4, 0xe3, 0x61, 0x91 }, 0, "C4" }, @@ -99,4 +96,37 @@ { { 0x73, 0x88, 0x43, 0x9d, 0x27, 0xe1, 0x63, 0xeb, 0x8b, 0xd6, 0xe4, 0xff, 0x68, 0x43, 0xc8, 0xe5, 0x6b, 0xa0, 0xeb, 0x2d }, 8, "AGIX" }, { { 0xe1, 0xf1, 0xde, 0x48, 0x36, 0xc3, 0xed, 0xba, 0xb3, 0xee, 0x34, 0xda, 0x74, 0x96, 0x95, 0xf9, 0x83, 0x71, 0xe0, 0xff }, 6, "INDY" }, { { 0xa2, 0x62, 0x4d, 0xda, 0x5d, 0x49, 0x8d, 0x9e, 0x4d, 0x94, 0x42, 0xdb, 0xfa, 0x55, 0x14, 0x04, 0x0e, 0xb2, 0x16, 0xc7 }, 6, "DJED" }, -{ { 0x1e, 0xe2, 0x7e, 0xdf, 0xf4, 0x99, 0xcf, 0x88, 0xf0, 0x3a, 0x1e, 0xbc, 0x4f, 0x22, 0x6f, 0x4a, 0x4c, 0x07, 0x71, 0xde }, 6, "iUSD" } +{ { 0x1e, 0xe2, 0x7e, 0xdf, 0xf4, 0x99, 0xcf, 0x88, 0xf0, 0x3a, 0x1e, 0xbc, 0x4f, 0x22, 0x6f, 0x4a, 0x4c, 0x07, 0x71, 0xde }, 6, "iUSD" }, +{ { 0x79, 0xcd, 0xc7, 0xab, 0x16, 0x70, 0xd3, 0x83, 0x82, 0x05, 0x69, 0x7d, 0xb4, 0x12, 0xf5, 0xb4, 0xce, 0x86, 0xf0, 0xae }, 0, "SNEK" }, +{ { 0x63, 0xdc, 0x06, 0x02, 0xea, 0xac, 0x4c, 0x17, 0x8e, 0xc9, 0x0b, 0xd2, 0x23, 0x8d, 0x94, 0xb6, 0xa9, 0x16, 0x6a, 0x76 }, 6, "HUNT" }, +{ { 0x16, 0x94, 0xeb, 0x43, 0x8c, 0xac, 0x25, 0x6f, 0xaa, 0xb6, 0x92, 0x06, 0xa8, 0x6c, 0xeb, 0x48, 0xea, 0x17, 0xe1, 0x25 }, 6, "IAG" }, +{ { 0x61, 0x71, 0xd3, 0x3e, 0x73, 0xb4, 0xbb, 0x18, 0x7c, 0xe4, 0x5f, 0xb8, 0x93, 0x2f, 0x73, 0xb0, 0x8a, 0x30, 0xca, 0xd0 }, 0, "FREN" }, +{ { 0xb5, 0xed, 0x5d, 0x2d, 0xa9, 0x85, 0xa6, 0x2c, 0x96, 0xc7, 0xaf, 0x72, 0xfc, 0xe5, 0xf6, 0x51, 0x8f, 0xbc, 0x3a, 0xe2 }, 6, "LENFI" }, +{ { 0xb2, 0x5d, 0xa0, 0xff, 0x7c, 0x12, 0x10, 0x0f, 0x71, 0x85, 0x7d, 0xee, 0xcb, 0x12, 0x81, 0xd1, 0x89, 0x8e, 0x9a, 0x3f }, 6, "iBTC" }, +{ { 0xf8, 0x77, 0x71, 0x02, 0x31, 0x64, 0x24, 0x59, 0x06, 0x4a, 0x74, 0x3f, 0xce, 0x3f, 0x90, 0x02, 0x7a, 0x9d, 0x5a, 0xc5 }, 6, "BOOK" }, +{ { 0x29, 0x39, 0x3c, 0x8d, 0x53, 0x12, 0xbc, 0x10, 0x14, 0x32, 0x6a, 0xfb, 0x47, 0x99, 0xc9, 0x23, 0x5e, 0xd5, 0x4c, 0x64 }, 6, "NTX" }, +{ { 0xb7, 0x1c, 0x96, 0xbf, 0x30, 0x49, 0x4d, 0x83, 0x70, 0x61, 0x51, 0xaa, 0x92, 0xbb, 0x54, 0xfb, 0x05, 0x55, 0xd9, 0x2e }, 6, "FACT" }, +{ { 0x23, 0x54, 0xee, 0x7a, 0xa2, 0x5d, 0xb0, 0xc5, 0x8a, 0x74, 0x94, 0x2c, 0x3f, 0xb9, 0xe7, 0xa1, 0xa5, 0x24, 0x6a, 0xf5 }, 6, "LIFI" }, +{ { 0x4b, 0x70, 0x07, 0xc8, 0xa0, 0xe3, 0x8b, 0xd1, 0xcf, 0x42, 0x49, 0xc6, 0x21, 0x0b, 0x80, 0xbe, 0x03, 0xf7, 0x9d, 0x8d }, 6, "OPTIM" }, +{ { 0x29, 0x35, 0x01, 0xe1, 0xd2, 0xea, 0x4a, 0x6b, 0x70, 0x47, 0x8a, 0xcf, 0x76, 0xb7, 0xab, 0x2d, 0x4e, 0xe2, 0x58, 0xa3 }, 6, "OPT" }, +{ { 0x27, 0x10, 0x3e, 0x8a, 0xc1, 0xf4, 0x52, 0x17, 0x2c, 0xd9, 0x4c, 0x32, 0x41, 0xe4, 0xd4, 0x3f, 0x67, 0xed, 0x06, 0xfd }, 6, "CGI" }, +{ { 0xf3, 0x0a, 0x25, 0x41, 0x3e, 0x63, 0xe8, 0x2d, 0x6e, 0x79, 0xa8, 0x2c, 0xfb, 0x0b, 0xfb, 0xe6, 0x69, 0xd0, 0x19, 0x69 }, 6, "SHEN" }, +{ { 0x25, 0xf8, 0xef, 0xb7, 0x66, 0xe5, 0x85, 0xc2, 0x9e, 0x85, 0x4f, 0x63, 0x46, 0xaa, 0x3e, 0x55, 0x74, 0x2c, 0x01, 0x89 }, 6, "PBX" }, +{ { 0x9a, 0x56, 0x95, 0x83, 0x63, 0x32, 0xd0, 0x6e, 0xdb, 0x97, 0x81, 0xd3, 0xbb, 0x60, 0xdf, 0xd4, 0x74, 0x6e, 0x2d, 0x12 }, 6, "iETH" }, +{ { 0x98, 0xb6, 0x0a, 0x33, 0xc5, 0x54, 0xe6, 0x9b, 0x90, 0xd9, 0x7d, 0x15, 0x7b, 0x5b, 0x3f, 0x2c, 0x9b, 0x85, 0x73, 0x12 }, 0, "KITUP" }, +{ { 0x56, 0xb4, 0x05, 0x65, 0xd7, 0x47, 0x3f, 0xe9, 0x5e, 0x26, 0x00, 0x59, 0x21, 0x03, 0xa3, 0x1e, 0x42, 0x99, 0x73, 0xf9 }, 6, "GENS" }, +{ { 0x1e, 0x57, 0xbc, 0x89, 0xed, 0x20, 0xc5, 0x2f, 0x2d, 0xc1, 0x00, 0x99, 0xf1, 0x8f, 0x25, 0x76, 0xe3, 0x8b, 0xd4, 0x22 }, 0, "RON" }, +{ { 0x19, 0x1c, 0xc9, 0xfe, 0x2b, 0x45, 0xa7, 0xa9, 0x0a, 0x71, 0x1e, 0x10, 0xb8, 0x5a, 0x9d, 0x8b, 0x0c, 0x29, 0xbc, 0x82 }, 0, "TOOL" }, +{ { 0x14, 0xba, 0x77, 0x29, 0x6f, 0x62, 0x73, 0xba, 0x60, 0x15, 0xbd, 0x7e, 0x75, 0xbf, 0x2d, 0x72, 0x4d, 0xb3, 0xcb, 0xa0 }, 6, "JPG" }, +{ { 0x58, 0x61, 0x97, 0x35, 0x64, 0x6d, 0x94, 0xa8, 0xc9, 0x4b, 0xb1, 0x94, 0xeb, 0xbc, 0xa0, 0x0a, 0xf8, 0x44, 0x67, 0x51 }, 6, "SOC" }, +{ { 0x1c, 0x77, 0x87, 0x8f, 0xb4, 0xe1, 0x40, 0x2e, 0xdf, 0xf5, 0x03, 0x40, 0x84, 0x0f, 0x80, 0x89, 0x00, 0x50, 0x68, 0x79 }, 0, "VIPER" }, +{ { 0x0f, 0x76, 0xf8, 0xdd, 0x1a, 0xba, 0xcf, 0xf4, 0xa4, 0xc3, 0x65, 0xa1, 0xfa, 0x64, 0x33, 0xb7, 0xac, 0x8d, 0x49, 0xc5 }, 6, "ENCS" }, +{ { 0xc6, 0x00, 0x7e, 0x0c, 0x42, 0xac, 0x1b, 0x3c, 0xf0, 0x0c, 0xa4, 0x94, 0xce, 0xae, 0x59, 0x90, 0x8f, 0x2a, 0x2f, 0x02 }, 0, "BANK" }, +{ { 0x2b, 0x60, 0x77, 0x00, 0xc1, 0xd6, 0x08, 0x71, 0x3d, 0x8f, 0x4f, 0xc5, 0xf6, 0xeb, 0x60, 0x65, 0x35, 0xd7, 0x01, 0x02 }, 2, "BUZZ" }, +{ { 0xef, 0xb3, 0x25, 0x1f, 0x0d, 0x4b, 0xfc, 0x75, 0xcd, 0xa2, 0xc3, 0x20, 0xed, 0xd1, 0xd7, 0xb2, 0xa4, 0x30, 0xb5, 0xe2 }, 9, "rsERG" }, +{ { 0xca, 0xbf, 0xde, 0x8b, 0xc1, 0xc7, 0xc8, 0x51, 0x9e, 0xbb, 0xf9, 0xf5, 0x6e, 0x48, 0xbd, 0x61, 0x57, 0x2e, 0xef, 0xc5 }, 4, "CNCT" }, +{ { 0xd2, 0x17, 0xf9, 0x26, 0x3c, 0x12, 0x2e, 0x62, 0x6f, 0xe5, 0xc4, 0xae, 0x5e, 0x31, 0xc8, 0xa4, 0xb7, 0x81, 0x2a, 0x88 }, 9, "AXO" }, +{ { 0x6d, 0x7a, 0xe0, 0x79, 0xfd, 0x4f, 0xfe, 0xe3, 0x94, 0xe5, 0xe1, 0x4a, 0x59, 0x9d, 0xac, 0x43, 0x3f, 0x47, 0x59, 0x64 }, 6, "SPF" }, +{ { 0x13, 0xbb, 0x5c, 0x5d, 0xc5, 0x36, 0xfc, 0x67, 0x5b, 0xc8, 0xae, 0xb2, 0x92, 0xd4, 0x10, 0x92, 0x8f, 0x7d, 0xcd, 0xbe }, 6, "XRAY" }, +{ { 0x0e, 0xc9, 0x9e, 0x17, 0x89, 0xa0, 0x23, 0xa9, 0xca, 0x55, 0xef, 0xce, 0xcf, 0xeb, 0x1e, 0xcf, 0x6e, 0x65, 0xfe, 0x6c }, 6, "FLAC" }, +{ { 0xcc, 0xcf, 0x11, 0xca, 0xe6, 0x9b, 0xe3, 0xd5, 0x3b, 0xae, 0x0b, 0x68, 0xb2, 0x2c, 0x65, 0x0a, 0xc8, 0x05, 0xa5, 0xa2 }, 6, "CHRY" } From d42d012970c4255173cf08990cae5676c7fc91aa Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Sun, 17 Mar 2024 16:56:42 +0100 Subject: [PATCH 24/43] fix: audit --- README.md | 12 ++++++------ fuzzing/CMakeLists.txt | 3 ++- fuzzing/README.md | 1 + src/getPublicKeys.c | 1 + src/signCVote.c | 1 + src/signMsg.c | 20 ++++++++++++++------ src/signMsg_ui.c | 3 ++- src/signMsg_ui.h | 6 +++--- src/signTxCVoteRegistration.c | 1 + src/signTxMint.c | 1 + src/signTxOutput.c | 2 ++ src/signTxOutput_ui.c | 6 ++++-- 12 files changed, 38 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index c822fa9a..b021f825 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,7 @@ We recommend using the containerized build. See [Getting started](doc/build.md) ### Loading the app -`make load` - -Builds and loads the application into the connected device. Make sure to close the Ledger app on the device before running the command. - -Most common reason for a failed loading is the app taking too much space. Check `make size` (should be below 140K or so). +We recommend using the [Ledger VS Code plugin](https://marketplace.visualstudio.com/items?itemName=LedgerHQ.ledger-dev-tools) to load the app on a device. ### Debug version @@ -27,7 +23,7 @@ also comment out DEFINES += RESET_ON_CRASH -and then run `make clean load`. +The debug version is too big to fit on Nano S, but works on Speculos. ### Setup @@ -78,6 +74,10 @@ _Before merging a PR, one should make sure that:_ * `make clean load` runs without errors and warnings (except those reported for nanos-secure-sdk repo) for development build (see Debug version above) * `make analyze` does not report errors or warnings +## Running tests + +All the tests are initiated from the accompanying [ledgerjs package](https://github.com/vacuumlabs/ledgerjs-cardano-shelley) (see what [commands to run](https://github.com/vacuumlabs/ledgerjs-cardano-shelley?tab=readme-ov-file#tests)). You have to make sure that the version of ledgerjs correspond to the app version, otherwise some tests with fail (possibly resulting in odd errors) or test coverage will be incomplete. + ## How to get a transaction body computed by Ledger Ledger computes a rolling hash of the serialized transaction body, but the body itself is ordinarily not available. It is possible to acquire it from the development build by going through the following steps: diff --git a/fuzzing/CMakeLists.txt b/fuzzing/CMakeLists.txt index 6cda3a91..f4f281f6 100644 --- a/fuzzing/CMakeLists.txt +++ b/fuzzing/CMakeLists.txt @@ -142,6 +142,7 @@ add_compile_definitions( HAVE_ECDSA HAVE_EDDSA HAVE_HASH + HAVE_SHA224 HAVE_SHA256 HAVE_SHA3 @@ -181,4 +182,4 @@ foreach(harness IN LISTS harnesses) ./src/${harness}.c ) target_link_libraries(${harness} PUBLIC cardano) -endforeach() \ No newline at end of file +endforeach() diff --git a/fuzzing/README.md b/fuzzing/README.md index fb18670a..383c01ab 100644 --- a/fuzzing/README.md +++ b/fuzzing/README.md @@ -37,4 +37,5 @@ Since there is an already existing corpus, to start fuzzing with it simply do `. ## Notes + For more context regarding fuzzing check out the app-boilerplate fuzzing [README.md](https://github.com/LedgerHQ/app-boilerplate/blob/master/fuzzing/README.md) diff --git a/src/getPublicKeys.c b/src/getPublicKeys.c index 182898c0..e1615236 100644 --- a/src/getPublicKeys.c +++ b/src/getPublicKeys.c @@ -218,6 +218,7 @@ void getPublicKeys_handleAPDU( bool isNewCall ) { + ASSERT(wireDataBuffer != NULL); ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); if (isNewCall) { diff --git a/src/signCVote.c b/src/signCVote.c index 6f275e4f..4d51e92e 100644 --- a/src/signCVote.c +++ b/src/signCVote.c @@ -278,6 +278,7 @@ void signCVote_handleAPDU( bool isNewCall ) { + ASSERT(wireDataBuffer != NULL); ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); if (isNewCall) { diff --git a/src/signMsg.c b/src/signMsg.c index 3fbf0d43..1eb87529 100644 --- a/src/signMsg.c +++ b/src/signMsg.c @@ -193,6 +193,7 @@ static void _prepareAddressField() } } +__noinline_due_to_stack__ static size_t _createProtectedHeader(uint8_t* protectedHeaderBuffer, size_t maxSize) { // protectedHeader = { @@ -207,36 +208,41 @@ static size_t _createProtectedHeader(uint8_t* protectedHeaderBuffer, size_t maxS { size_t len = cbor_writeToken(CBOR_TYPE_MAP, 2, p, end - p); p += len; + ASSERT(p < end); } { size_t len = cbor_writeToken(CBOR_TYPE_UNSIGNED, 1, p, end - p); p += len; + ASSERT(p < end); } { size_t len = cbor_writeToken(CBOR_TYPE_NEGATIVE, -8, p, end - p); p += len; + ASSERT(p < end); } { size_t len = cbor_writeToken(CBOR_TYPE_TEXT, 7, p, end - p); p += len; + ASSERT(p < end); } - { const char* text = "address"; const size_t len = strlen(text); ASSERT(p + len < end); memmove(p, text, len); p += len; + ASSERT(p < end); } - _prepareAddressField(); { + _prepareAddressField(); + ASSERT(ctx->addressFieldSize > 0); + size_t len = cbor_writeToken(CBOR_TYPE_BYTES, ctx->addressFieldSize, p, end - p); p += len; - } - { ASSERT(p + ctx->addressFieldSize < end); memmove(p, ctx->addressField, ctx->addressFieldSize); p += ctx->addressFieldSize; + ASSERT(p < end); } const size_t protectedHeaderSize = p - protectedHeaderBuffer; @@ -250,7 +256,7 @@ static size_t _createProtectedHeader(uint8_t* protectedHeaderBuffer, size_t maxS } } END_TRY; - return -1; + return SIZE_MAX; } static void signMsg_handleConfirmAPDU(const uint8_t* wireDataBuffer MARK_UNUSED, size_t wireDataSize) @@ -283,8 +289,9 @@ static void signMsg_handleConfirmAPDU(const uint8_t* wireDataBuffer MARK_UNUSED, ASSERT(written < maxWritten); } { - uint8_t protectedHeaderBuffer[100]; // address of max 57 bytes plus a couple of small items + uint8_t protectedHeaderBuffer[100] = {0}; // address of max 57 bytes plus a couple of small items const size_t len = _createProtectedHeader(protectedHeaderBuffer, SIZEOF(protectedHeaderBuffer)); + ASSERT(len < BUFFER_SIZE_PARANOIA); written += cbor_writeToken(CBOR_TYPE_BYTES, len, sigStructure + written, maxWritten - written); ASSERT(written + len < maxWritten); memmove(sigStructure + written, protectedHeaderBuffer, len); @@ -360,6 +367,7 @@ void signMsg_handleAPDU( ) { TRACE("P1 = 0x%x, P2 = 0x%x, isNewCall = %d", p1, p2, isNewCall); + ASSERT(wireDataBuffer != NULL); ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); VALIDATE(p2 == P2_UNUSED, ERR_INVALID_REQUEST_PARAMETERS); diff --git a/src/signMsg_ui.c b/src/signMsg_ui.c index 6406f0ed..6377a7d9 100644 --- a/src/signMsg_ui.c +++ b/src/signMsg_ui.c @@ -142,7 +142,8 @@ void _displayMsgIntro(ui_callback_fn_t* callback) ASSERT(strlen(l1) + 1 < SIZEOF(l1)); char l2[30] = {0}; - snprintf(l2, SIZEOF(l2), "%u bytes", ctx->msgLength); + ASSERT(ctx->msgLength < UINT32_MAX); + snprintf(l2, SIZEOF(l2), "%u bytes", (uint32_t)ctx->msgLength); ASSERT(strlen(l2) + 1 < SIZEOF(l2)); #ifdef HAVE_BAGL diff --git a/src/signMsg_ui.h b/src/signMsg_ui.h index d6b7b87d..2f81df50 100644 --- a/src/signMsg_ui.h +++ b/src/signMsg_ui.h @@ -1,5 +1,5 @@ -#ifndef H_CARDANO_APP_SIGN_CVOTE_UI -#define H_CARDANO_APP_SIGN_CVOTE_UI +#ifndef H_CARDANO_APP_SIGN_MSG_UI +#define H_CARDANO_APP_SIGN_MSG_UI #include "uiHelpers.h" @@ -40,4 +40,4 @@ enum { void signMsg_handleConfirm_ui_runStep(); -#endif // H_CARDANO_APP_SIGN_CVOTE_UI +#endif // H_CARDANO_APP_SIGN_MSG_UI diff --git a/src/signTxCVoteRegistration.c b/src/signTxCVoteRegistration.c index 5f080431..501adc81 100644 --- a/src/signTxCVoteRegistration.c +++ b/src/signTxCVoteRegistration.c @@ -724,6 +724,7 @@ bool signTxCVoteRegistration_isValidInstruction(uint8_t p2) void signTxCVoteRegistration_handleAPDU(uint8_t p2, const uint8_t* wireDataBuffer, size_t wireDataSize) { + ASSERT(wireDataBuffer != NULL); ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); switch (p2) { diff --git a/src/signTxMint.c b/src/signTxMint.c index 5d6ebfe5..5957588b 100644 --- a/src/signTxMint.c +++ b/src/signTxMint.c @@ -241,6 +241,7 @@ void signTxMint_init() void signTxMint_handleAPDU(uint8_t p2, const uint8_t* wireDataBuffer, size_t wireDataSize) { + ASSERT(wireDataBuffer != NULL); ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); switch (p2) { diff --git a/src/signTxOutput.c b/src/signTxOutput.c index 579ec09b..54e6b559 100644 --- a/src/signTxOutput.c +++ b/src/signTxOutput.c @@ -1071,6 +1071,7 @@ bool signTxOutput_isValidInstruction(uint8_t p2) void signTxOutput_handleAPDU(uint8_t p2, const uint8_t* wireDataBuffer, size_t wireDataSize) { + ASSERT(wireDataBuffer != NULL); ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); switch (p2) { @@ -1132,6 +1133,7 @@ bool signTxCollateralOutput_isValidInstruction(uint8_t p2) void signTxCollateralOutput_handleAPDU(uint8_t p2, const uint8_t* wireDataBuffer, size_t wireDataSize) { + ASSERT(wireDataBuffer != NULL); ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); switch (p2) { diff --git a/src/signTxOutput_ui.c b/src/signTxOutput_ui.c index 72ba1442..ff955d92 100644 --- a/src/signTxOutput_ui.c +++ b/src/signTxOutput_ui.c @@ -356,7 +356,8 @@ void signTxOutput_handleDatumInline_ui_runStep() size_t datumSize = subctx->stateData.datumRemainingBytes + subctx->stateData.datumChunkSize; // datumSize with 6 digits fits on the screen, less than max tx size // if more is needed, "bytes" can be replaced by "B" for those larger numbers - snprintf(l1, SIZEOF(l1), "Datum %u bytes", datumSize); + ASSERT(datumSize < UINT32_MAX); + snprintf(l1, SIZEOF(l1), "Datum %u bytes", (uint32_t)datumSize); ASSERT(strlen(l1) + 1 < SIZEOF(l1)); char l2[20]; @@ -397,7 +398,8 @@ void handleRefScript_ui_runStep() size_t scriptSize = subctx->stateData.refScriptRemainingBytes + subctx->stateData.refScriptChunkSize; // scriptSize with 6 digits fits on the screen, less than max tx size // if more is needed, "bytes" can be replaced by "B" for those larger numbers - snprintf(l1, SIZEOF(l1), "Script %u bytes", scriptSize); + ASSERT(scriptSize < UINT32_MAX); + snprintf(l1, SIZEOF(l1), "Script %u bytes", (uint32_t)scriptSize); ASSERT(strlen(l1) + 1 < SIZEOF(l1)); char l2[20]; From 01ae3874040f9f8db50dcaa7a88c17b2cbbfd0b3 Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Sun, 17 Mar 2024 21:34:10 +0100 Subject: [PATCH 25/43] fix: fuzzing --- fuzzing/CMakeLists.txt | 1 + src/signMsg.c | 99 +++++++++++++++++++----------------------- 2 files changed, 46 insertions(+), 54 deletions(-) diff --git a/fuzzing/CMakeLists.txt b/fuzzing/CMakeLists.txt index f4f281f6..215c06c8 100644 --- a/fuzzing/CMakeLists.txt +++ b/fuzzing/CMakeLists.txt @@ -21,6 +21,7 @@ else() add_link_options($ENV{LIB_FUZZING_ENGINE}) endif() +add_compile_options(-g) set(SDK_PATH ${BOLOS_SDK}) diff --git a/src/signMsg.c b/src/signMsg.c index 1eb87529..4e5397e5 100644 --- a/src/signMsg.c +++ b/src/signMsg.c @@ -200,63 +200,54 @@ static size_t _createProtectedHeader(uint8_t* protectedHeaderBuffer, size_t maxS // 1 : -8, // set algorithm to EdDSA // “address” : address_bytes // raw address given by the user, or key hash // } - BEGIN_TRY { - TRY { - uint8_t* p = protectedHeaderBuffer; - uint8_t* end = protectedHeaderBuffer + maxSize; - - { - size_t len = cbor_writeToken(CBOR_TYPE_MAP, 2, p, end - p); - p += len; - ASSERT(p < end); - } - { - size_t len = cbor_writeToken(CBOR_TYPE_UNSIGNED, 1, p, end - p); - p += len; - ASSERT(p < end); - } - { - size_t len = cbor_writeToken(CBOR_TYPE_NEGATIVE, -8, p, end - p); - p += len; - ASSERT(p < end); - } - { - size_t len = cbor_writeToken(CBOR_TYPE_TEXT, 7, p, end - p); - p += len; - ASSERT(p < end); - } - { - const char* text = "address"; - const size_t len = strlen(text); - ASSERT(p + len < end); - memmove(p, text, len); - p += len; - ASSERT(p < end); - } - { - _prepareAddressField(); - ASSERT(ctx->addressFieldSize > 0); - - size_t len = cbor_writeToken(CBOR_TYPE_BYTES, ctx->addressFieldSize, p, end - p); - p += len; - ASSERT(p + ctx->addressFieldSize < end); - memmove(p, ctx->addressField, ctx->addressFieldSize); - p += ctx->addressFieldSize; - ASSERT(p < end); - } + uint8_t* p = protectedHeaderBuffer; + uint8_t* end = protectedHeaderBuffer + maxSize; - const size_t protectedHeaderSize = p - protectedHeaderBuffer; - ASSERT(protectedHeaderSize > 0); - ASSERT(protectedHeaderSize < maxSize); + { + size_t len = cbor_writeToken(CBOR_TYPE_MAP, 2, p, end - p); + p += len; + ASSERT(p < end); + } + { + size_t len = cbor_writeToken(CBOR_TYPE_UNSIGNED, 1, p, end - p); + p += len; + ASSERT(p < end); + } + { + size_t len = cbor_writeToken(CBOR_TYPE_NEGATIVE, -8, p, end - p); + p += len; + ASSERT(p < end); + } + { + size_t len = cbor_writeToken(CBOR_TYPE_TEXT, 7, p, end - p); + p += len; + ASSERT(p < end); + } + { + const char* text = "address"; + const size_t len = strlen(text); + ASSERT(p + len < end); + memmove(p, text, len); + p += len; + ASSERT(p < end); + } + { + _prepareAddressField(); + ASSERT(ctx->addressFieldSize > 0); + + size_t len = cbor_writeToken(CBOR_TYPE_BYTES, ctx->addressFieldSize, p, end - p); + p += len; + ASSERT(p + ctx->addressFieldSize < end); + memmove(p, ctx->addressField, ctx->addressFieldSize); + p += ctx->addressFieldSize; + ASSERT(p < end); + } - return protectedHeaderSize; - } CATCH(ERR_DATA_TOO_LARGE) { - ASSERT(false); - } FINALLY { - } - } END_TRY; + const size_t protectedHeaderSize = p - protectedHeaderBuffer; + ASSERT(protectedHeaderSize > 0); + ASSERT(protectedHeaderSize < maxSize); - return SIZE_MAX; + return protectedHeaderSize; } static void signMsg_handleConfirmAPDU(const uint8_t* wireDataBuffer MARK_UNUSED, size_t wireDataSize) From 8d4c3448f63538069e875f60bbb133c4dcf194be Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Fri, 22 Mar 2024 11:01:26 +0100 Subject: [PATCH 26/43] fix: fuzzing2 --- src/deriveNativeScriptHash.c | 8 ++++++++ src/deriveNativeScriptHash_ui.c | 3 +++ 2 files changed, 11 insertions(+) diff --git a/src/deriveNativeScriptHash.c b/src/deriveNativeScriptHash.c index de8df37c..68ffe28d 100644 --- a/src/deriveNativeScriptHash.c +++ b/src/deriveNativeScriptHash.c @@ -22,11 +22,13 @@ static inline bool areMoreScriptsExpected() // if the number of remaining scripts is not bigger than 0, then this request // is invalid in the current context, as Ledger was not expecting another // script to be parsed + ASSERT(ctx->level < MAX_SCRIPT_DEPTH); return ctx->complexScripts[ctx->level].remainingScripts > 0; } static inline bool isComplexScriptFinished() { + ASSERT(ctx->level < MAX_SCRIPT_DEPTH); return ctx->level > 0 && ctx->complexScripts[ctx->level].remainingScripts == 0; } @@ -36,6 +38,7 @@ static inline void complexScriptFinished() ASSERT(ctx->level > 0); ctx->level--; + ASSERT(ctx->level < MAX_SCRIPT_DEPTH); ASSERT(ctx->complexScripts[ctx->level].remainingScripts > 0); ctx->complexScripts[ctx->level].remainingScripts--; @@ -45,6 +48,7 @@ static inline void complexScriptFinished() static inline void simpleScriptFinished() { + ASSERT(ctx->level < MAX_SCRIPT_DEPTH); ASSERT(ctx->complexScripts[ctx->level].remainingScripts > 0); ctx->complexScripts[ctx->level].remainingScripts--; @@ -69,6 +73,7 @@ static void deriveNativeScriptHash_handleAll(read_view_t* view) TRACE_WITH_CTX(""); VALIDATE(view_remainingSize(view) == 0, ERR_INVALID_DATA); + ASSERT(ctx->level < MAX_SCRIPT_DEPTH); nativeScriptHashBuilder_startComplexScript_all(&ctx->hashBuilder, ctx->complexScripts[ctx->level].remainingScripts); UI_DISPLAY_SCRIPT(UI_SCRIPT_ALL); @@ -79,6 +84,7 @@ static void deriveNativeScriptHash_handleAny(read_view_t* view) TRACE_WITH_CTX(""); VALIDATE(view_remainingSize(view) == 0, ERR_INVALID_DATA); + ASSERT(ctx->level < MAX_SCRIPT_DEPTH); nativeScriptHashBuilder_startComplexScript_any(&ctx->hashBuilder, ctx->complexScripts[ctx->level].remainingScripts); UI_DISPLAY_SCRIPT(UI_SCRIPT_ANY); @@ -93,6 +99,7 @@ static void deriveNativeScriptHash_handleNofK(read_view_t* view) VALIDATE(view_remainingSize(view) == 0, ERR_INVALID_DATA); // validate that the received requiredScripts count makes sense + ASSERT(ctx->level < MAX_SCRIPT_DEPTH); VALIDATE(ctx->complexScripts[ctx->level].remainingScripts >= ctx->scriptContent.requiredScripts, ERR_INVALID_DATA); nativeScriptHashBuilder_startComplexScript_n_of_k(&ctx->hashBuilder, ctx->scriptContent.requiredScripts, ctx->complexScripts[ctx->level].remainingScripts); @@ -112,6 +119,7 @@ static void deriveNativeScriptHash_handleComplexScriptStart(read_view_t* view) uint8_t nativeScriptType = parse_u1be(view); TRACE("native complex script type = %u", nativeScriptType); + ASSERT(ctx->level < MAX_SCRIPT_DEPTH); ctx->complexScripts[ctx->level].remainingScripts = parse_u4be(view); ctx->complexScripts[ctx->level].totalScripts = ctx->complexScripts[ctx->level].remainingScripts; diff --git a/src/deriveNativeScriptHash_ui.c b/src/deriveNativeScriptHash_ui.c index 99bb376d..7d8dda22 100644 --- a/src/deriveNativeScriptHash_ui.c +++ b/src/deriveNativeScriptHash_ui.c @@ -90,6 +90,7 @@ static void deriveScriptHash_display_ui_runStep_cb(void) // where n and k is 2^32-1 char text[87] = {0}; explicit_bzero(text, SIZEOF(text)); + ASSERT(ctx->level < MAX_SCRIPT_DEPTH); snprintf(text, SIZEOF(text), "%u nested scripts", ctx->complexScripts[ctx->level].remainingScripts); // make sure all the information is displayed to the user ASSERT(strlen(text) + 1 < SIZEOF(text)); @@ -136,6 +137,7 @@ static void _displayScriptContent(ui_callback_fn_t* this_fn) // where n is 2^32-1 char text[37] = {0}; explicit_bzero(text, SIZEOF(text)); + ASSERT(ctx->level < MAX_SCRIPT_DEPTH); STATIC_ASSERT(sizeof(ctx->complexScripts[ctx->level].remainingScripts) <= sizeof(unsigned), "oversized type for %u"); STATIC_ASSERT(!IS_SIGNED(ctx->complexScripts[ctx->level].remainingScripts), "signed type for %u"); #ifdef HAVE_BAGL @@ -157,6 +159,7 @@ static void _displayScriptContent(ui_callback_fn_t* this_fn) break; } case UI_SCRIPT_N_OF_K: { + ASSERT(ctx->level < MAX_SCRIPT_DEPTH); STATIC_ASSERT(sizeof(ctx->scriptContent.requiredScripts) <= sizeof(unsigned), "oversized type for %u"); STATIC_ASSERT(!IS_SIGNED(ctx->scriptContent.requiredScripts), "signed type for %u"); STATIC_ASSERT(sizeof(ctx->complexScripts[ctx->level].remainingScripts) <= sizeof(unsigned), "oversized type for %u"); From 8ffc2aad0de472cae44366ff723df9f65381e1c3 Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Tue, 9 Apr 2024 15:09:46 +0200 Subject: [PATCH 27/43] fix: msg signing ui bug --- src/signMsg_ui.c | 124 +++++++++++++++++++++-------------------------- src/signMsg_ui.h | 5 +- 2 files changed, 57 insertions(+), 72 deletions(-) diff --git a/src/signMsg_ui.c b/src/signMsg_ui.c index 6377a7d9..da0c60df 100644 --- a/src/signMsg_ui.c +++ b/src/signMsg_ui.c @@ -18,43 +18,36 @@ static ins_sign_msg_context_t* ctx = &(instructionState.signMsgContext); // ============================== INIT ============================== -void signMsg_handleInit_ui_runStep() +__noinline_due_to_stack__ +static void _displayAddressField(ui_callback_fn_t* callback) { - TRACE("UI step %d", ctx->ui_step); - TRACE_STACK_USAGE(); - ui_callback_fn_t* this_fn = signMsg_handleInit_ui_runStep; - - UI_STEP_BEGIN(ctx->ui_step, this_fn); + switch (ctx->addressFieldType) { + case CIP8_ADDRESS_FIELD_ADDRESS: { + uint8_t addressBuffer[MAX_ADDRESS_SIZE] = {0}; + size_t addressSize = deriveAddress(&ctx->addressParams, addressBuffer, SIZEOF(addressBuffer)); + ASSERT(addressSize > 0); + ASSERT(addressSize <= MAX_ADDRESS_SIZE); - UI_STEP(HANDLE_INIT_HASH_PAYLOAD) { #ifdef HAVE_BAGL - const char* firstLine = (ctx->hashPayload) ? "Sign hashed" : "Sign non-hashed"; - ui_displayPrompt( - firstLine, - "message? (CIP-8)", - this_fn, - respond_with_user_reject + ui_displayAddressScreen( + "Address field", + addressBuffer, addressSize, + callback ); #elif defined(HAVE_NBGL) - set_light_confirmation(true); - const char* text = (ctx->hashPayload) ? "Sign hashed\nmessage? (CIP-8)" : "Sign non-hashed\nmessage? (CIP-8)"; - display_prompt(text, "", this_fn, respond_with_user_reject); - #endif // HAVE_BAGL - } - UI_STEP(HANDLE_INIT_WITNESS_PATH) { - #ifdef HAVE_BAGL - ui_displayPathScreen("Signing path", &ctx->signingPath, this_fn); - #elif defined(HAVE_NBGL) - char pathStr[BIP44_PATH_STRING_SIZE_MAX + 1] = {0}; - ui_getPathScreen(pathStr, SIZEOF(pathStr), &ctx->signingPath); - fill_and_display_if_required("Signing path", pathStr, this_fn, respond_with_user_reject); + char humanAddress[MAX_HUMAN_ADDRESS_SIZE] = {0}; + ui_getAddressScreen( + humanAddress, + SIZEOF(humanAddress), + addressBuffer, + addressSize + ); + fill_and_display_if_required("Address field", humanAddress, callback, respond_with_user_reject); #endif // HAVE_BAGL + return; } - UI_STEP(HANDLE_INIT_ADDRESS_FIELD_DISPLAY_KEY_HASH) { - if (ctx->addressFieldType == CIP8_ADDRESS_FIELD_ADDRESS) { - UI_STEP_JUMP(HANDLE_INIT_ADDRESS_FIELD_DISPLAY_ADDRESS); - } + case CIP8_ADDRESS_FIELD_KEYHASH: { uint8_t hash[28]; blake2b_224_hash( ctx->witnessKey, SIZEOF(ctx->witnessKey), @@ -63,64 +56,59 @@ void signMsg_handleInit_ui_runStep() #ifdef HAVE_BAGL ui_displayHexBufferScreen( - "Address field", + "Address field (hex)", hash, SIZEOF(hash), - this_fn + callback ); #elif defined(HAVE_NBGL) char bufferHex[2 * SCRIPT_HASH_LENGTH + 1] = {0}; ui_getHexBufferScreen(bufferHex, SIZEOF(bufferHex), hash, SIZEOF(hash)); - fill_and_display_if_required("Address field", bufferHex, this_fn, respond_with_user_reject); + fill_and_display_if_required("Address field (hex)", bufferHex, callback, respond_with_user_reject); #endif // HAVE_BAGL + return; } - UI_STEP(HANDLE_INIT_ADDRESS_FIELD_DISPLAY_ADDRESS) { - if (ctx->addressFieldType != CIP8_ADDRESS_FIELD_ADDRESS) { - UI_STEP_JUMP(HANDLE_INIT_RESPOND); - } - uint8_t addressBuffer[MAX_ADDRESS_SIZE] = {0}; - size_t addressSize = deriveAddress(&ctx->addressParams, addressBuffer, SIZEOF(addressBuffer)); - ASSERT(addressSize > 0); - ASSERT(addressSize <= MAX_ADDRESS_SIZE); + default: + ASSERT(false); + return; + } +} + +void signMsg_handleInit_ui_runStep() +{ + TRACE("UI step %d", ctx->ui_step); + TRACE_STACK_USAGE(); + ui_callback_fn_t* this_fn = signMsg_handleInit_ui_runStep; + + UI_STEP_BEGIN(ctx->ui_step, this_fn); + UI_STEP(HANDLE_INIT_HASH_PAYLOAD) { #ifdef HAVE_BAGL - ui_displayAddressScreen( - "Address field", - addressBuffer, addressSize, - this_fn + const char* firstLine = (ctx->hashPayload) ? "Sign hashed" : "Sign non-hashed"; + ui_displayPrompt( + firstLine, + "message? (CIP-8)", + this_fn, + respond_with_user_reject ); #elif defined(HAVE_NBGL) - char humanAddress[MAX_HUMAN_ADDRESS_SIZE] = {0}; - ui_getAddressScreen( - humanAddress, - SIZEOF(humanAddress), - addressBuffer, - addressSize - ); - fill_and_display_if_required("Address field", humanAddress, this_fn, respond_with_user_reject); + set_light_confirmation(true); + const char* text = (ctx->hashPayload) ? "Sign hashed\nmessage? (CIP-8)" : "Sign non-hashed\nmessage? (CIP-8)"; + display_prompt(text, "", this_fn, respond_with_user_reject); #endif // HAVE_BAGL } - UI_STEP(HANDLE_INIT_ADDRESS_FIELD_DISPLAY_PAYMENT_PATH) { + UI_STEP(HANDLE_INIT_WITNESS_PATH) { #ifdef HAVE_BAGL - ui_displayPaymentInfoScreen(&ctx->addressParams, this_fn); + ui_displayPathScreen("Signing path", &ctx->signingPath, this_fn); #elif defined(HAVE_NBGL) -#define PAYMENT_INFO_SIZE MAX(BECH32_STRING_SIZE_MAX, BIP44_PATH_STRING_SIZE_MAX) - char line1[30]; - char paymentInfo[PAYMENT_INFO_SIZE] = {0}; - ui_getPaymentInfoScreen(line1, SIZEOF(line1), paymentInfo, SIZEOF(paymentInfo), &ctx->addressParams); - fill_and_display_if_required(line1, paymentInfo, this_fn, respond_with_user_reject); + char pathStr[BIP44_PATH_STRING_SIZE_MAX + 1] = {0}; + ui_getPathScreen(pathStr, SIZEOF(pathStr), &ctx->signingPath); + fill_and_display_if_required("Signing path", pathStr, this_fn, respond_with_user_reject); #endif // HAVE_BAGL } - UI_STEP(HANDLE_INIT_ADDRESS_FIELD_DISPLAY_STAKING_INFO) { - #ifdef HAVE_BAGL - ui_displayStakingInfoScreen(&ctx->addressParams, this_fn); - #elif defined(HAVE_NBGL) - char line1[30] = {0}; - char stakingInfo[120] = {0}; - ui_getStakingInfoScreen(line1, SIZEOF(line1), stakingInfo, SIZEOF(stakingInfo), &ctx->addressParams); - fill_and_display_if_required(line1, stakingInfo, this_fn, respond_with_user_reject); - #endif // HAVE_BAGL + UI_STEP(HANDLE_INIT_ADDRESS_FIELD) { + _displayAddressField(this_fn); } UI_STEP(HANDLE_INIT_RESPOND) { respondSuccessEmptyMsg(); diff --git a/src/signMsg_ui.h b/src/signMsg_ui.h index 2f81df50..e09681d7 100644 --- a/src/signMsg_ui.h +++ b/src/signMsg_ui.h @@ -8,10 +8,7 @@ enum { HANDLE_INIT_HASH_PAYLOAD = 100, HANDLE_INIT_WITNESS_PATH, - HANDLE_INIT_ADDRESS_FIELD_DISPLAY_KEY_HASH, - HANDLE_INIT_ADDRESS_FIELD_DISPLAY_ADDRESS, - HANDLE_INIT_ADDRESS_FIELD_DISPLAY_PAYMENT_PATH, - HANDLE_INIT_ADDRESS_FIELD_DISPLAY_STAKING_INFO, + HANDLE_INIT_ADDRESS_FIELD, HANDLE_INIT_RESPOND, HANDLE_INIT_INVALID, }; From e1c04f8ab9a780ee30609e2388b729950ccebfe0 Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Tue, 9 Apr 2024 15:12:41 +0200 Subject: [PATCH 28/43] update: token registry --- tokenRegistry/tokenList.json | 6 ++++++ tokenRegistry/token_data.c | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/tokenRegistry/tokenList.json b/tokenRegistry/tokenList.json index 332f5d8b..0d95ba2c 100644 --- a/tokenRegistry/tokenList.json +++ b/tokenRegistry/tokenList.json @@ -790,5 +790,11 @@ "name": "CherryLend", "ticker": "CHRY", "decimals": 6 + }, + { + "assetSubject": "c48cbb3d5e57ed56e276bc45f99ab39abe94e6cd7ac39fb402da47ad0014df105553444d", + "name": "USDM", + "ticker": "USDM", + "decimals": 6 } ] diff --git a/tokenRegistry/token_data.c b/tokenRegistry/token_data.c index 270d09f2..5ca0eea9 100644 --- a/tokenRegistry/token_data.c +++ b/tokenRegistry/token_data.c @@ -129,4 +129,5 @@ { { 0x6d, 0x7a, 0xe0, 0x79, 0xfd, 0x4f, 0xfe, 0xe3, 0x94, 0xe5, 0xe1, 0x4a, 0x59, 0x9d, 0xac, 0x43, 0x3f, 0x47, 0x59, 0x64 }, 6, "SPF" }, { { 0x13, 0xbb, 0x5c, 0x5d, 0xc5, 0x36, 0xfc, 0x67, 0x5b, 0xc8, 0xae, 0xb2, 0x92, 0xd4, 0x10, 0x92, 0x8f, 0x7d, 0xcd, 0xbe }, 6, "XRAY" }, { { 0x0e, 0xc9, 0x9e, 0x17, 0x89, 0xa0, 0x23, 0xa9, 0xca, 0x55, 0xef, 0xce, 0xcf, 0xeb, 0x1e, 0xcf, 0x6e, 0x65, 0xfe, 0x6c }, 6, "FLAC" }, -{ { 0xcc, 0xcf, 0x11, 0xca, 0xe6, 0x9b, 0xe3, 0xd5, 0x3b, 0xae, 0x0b, 0x68, 0xb2, 0x2c, 0x65, 0x0a, 0xc8, 0x05, 0xa5, 0xa2 }, 6, "CHRY" } +{ { 0xcc, 0xcf, 0x11, 0xca, 0xe6, 0x9b, 0xe3, 0xd5, 0x3b, 0xae, 0x0b, 0x68, 0xb2, 0x2c, 0x65, 0x0a, 0xc8, 0x05, 0xa5, 0xa2 }, 6, "CHRY" }, +{ { 0x52, 0x52, 0xd9, 0x1e, 0xd6, 0x53, 0xaa, 0x7a, 0x40, 0x7e, 0xed, 0x33, 0x6d, 0xca, 0x4d, 0x26, 0x70, 0x23, 0x86, 0x0a }, 6, "USDM" } From a432ca70fc57c7599c7c69255657b2498924966c Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Tue, 9 Apr 2024 18:49:16 +0200 Subject: [PATCH 29/43] update version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9f20aeac..e6ec65d5 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ APPNAME = "Cardano ADA" APPVERSION_M = 7 APPVERSION_N = 1 -APPVERSION_P = 0 +APPVERSION_P = 1 APPVERSION = "$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)" ifeq ($(BOLOS_SDK),) From 6f20e60c13c26f11bc0dcf4811d0d99de894c9a8 Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Thu, 25 Apr 2024 14:58:47 +0200 Subject: [PATCH 30/43] fix: bech32 prefix for stake pool --- src/signTx_ui.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/signTx_ui.c b/src/signTx_ui.c index b3dfa1fc..a500fcba 100644 --- a/src/signTx_ui.c +++ b/src/signTx_ui.c @@ -591,7 +591,7 @@ void signTx_handleCertificateStaking_ui_runStep() ASSERT(cert->poolCredential.type == EXT_CREDENTIAL_KEY_HASH); ui_displayBech32Screen( "Delegate stake", - "to pool", + "pool", cert->poolCredential.keyHash, SIZEOF(cert->poolCredential.keyHash), this_fn ); From df2db668f8e993a603ea9ac1325bd2365275e13c Mon Sep 17 00:00:00 2001 From: Alexis Grojean Date: Fri, 19 Jul 2024 10:39:09 +0200 Subject: [PATCH 31/43] Add Flex support. --- Makefile | 207 ++++++++++++++++------------------------------ icon_ada_flex.gif | Bin 0 -> 394 bytes ledger_app.toml | 2 +- src/uiHelpers.c | 2 +- src/uiHelpers.h | 2 +- src/ui_nbgl.c | 2 +- 6 files changed, 74 insertions(+), 141 deletions(-) create mode 100644 icon_ada_flex.gif diff --git a/Makefile b/Makefile index e6ec65d5..364e3941 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ #******************************************************************************* -# Ledger Nano S -# (c) 2016 Ledger +# Ledger Cardano App +# (c) 2024 Ledger # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,112 +15,97 @@ # limitations under the License. #******************************************************************************* +ifeq ($(BOLOS_SDK),) +$(error Environment variable BOLOS_SDK is not set) +endif + +include $(BOLOS_SDK)/Makefile.defines + +######################################## +# Mandatory configuration # +######################################## +# Application name APPNAME = "Cardano ADA" +# Application version APPVERSION_M = 7 APPVERSION_N = 1 APPVERSION_P = 1 APPVERSION = "$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)" -ifeq ($(BOLOS_SDK),) -$(error Environment variable BOLOS_SDK is not set) -endif +# Application source files +APP_SOURCE_PATH += src -include $(BOLOS_SDK)/Makefile.defines +# Application icons following guidelines: +# https://developers.ledger.com/docs/embedded-app/design-requirements/#device-icon +ICON_NANOS = icon_ada_nanos.gif +ICON_NANOX = icon_ada_nanox.gif +ICON_NANOSP = icon_ada_nanox.gif +ICON_STAX = icon_ada_stax.gif +ICON_FLEX = icon_ada_flex.gif + +# Application allowed derivation curves. +CURVE_APP_LOAD_PARAMS = ed25519 + +# Application allowed derivation paths. +PATH_APP_LOAD_PARAMS += "44'/1815'" "1852'/1815'" "1853'/1815'" "1854'/1815'" "1855'/1815'" "1694'/1815'" + +# Setting to allow building variant applications +VARIANT_PARAM = COIN +VARIANT_VALUES = cardano_ada + +DEFINES += RESET_ON_CRASH +# Enabling DEBUG flag will enable PRINTF and disable optimizations +# DEVEL = 1 +# DEFINES += HEADLESS + +# Enabling debug PRINTF +ifeq ($(DEVEL), 1) + DEBUG = 1 + DEFINES += DEVEL +endif +######################################## +# Application custom permissions # +######################################## +HAVE_APPLICATION_FLAG_BOLOS_SETTINGS = 1 + +######################################## +# NBGL custom features # +######################################## +ENABLE_NBGL_QRCODE = 1 + +######################################## +# Application communication interfaces # +######################################## +ENABLE_BLUETOOTH = 1 + +######################################## +# Features disablers # +######################################## +# These advanced settings allow to disable some feature that are by +# default enabled in the SDK `Makefile.standard_app`. +DISABLE_STANDARD_APP_FILES = 1 ifeq ($(TARGET_NAME),TARGET_NANOS) - ICONNAME=icon_ada_nanos.gif -else ifeq ($(TARGET_NAME),TARGET_STAX) - ICONNAME=icon_ada_stax.gif -else - ICONNAME=icon_ada_nanox.gif +DISABLE_STANDARD_BAGL_UX_FLOW = 1 endif +SDK_SOURCE_PATH += lib_u2f + ############## # Compiler # ############## # based in part on https://interrupt.memfault.com/blog/best-and-worst-gcc-clang-compiler-flags WERROR := -Werror=return-type -Werror=parentheses -Werror=format-security - -CC := $(CLANGPATH)clang -CFLAGS += -std=gnu99 -Wall -Wextra -Wuninitialized -Wshadow -Wformat=2 -Wwrite-strings -Wundef -fno-common $(WERROR) - -AS := $(GCCPATH)arm-none-eabi-gcc -LD := $(GCCPATH)arm-none-eabi-gcc -LDFLAGS += -Wall -LDLIBS += -lm -lgcc -lc - - -############ -# Platform # -############ - -DEFINES += OS_IO_SEPROXYHAL -ifneq ($(TARGET_NAME),TARGET_STAX) -DEFINES += HAVE_BAGL -endif -DEFINES += HAVE_SPRINTF HAVE_SNPRINTF_FORMAT_U -DEFINES += APPVERSION=\"$(APPVERSION)\" -DEFINES += MAJOR_VERSION=$(APPVERSION_M) MINOR_VERSION=$(APPVERSION_N) PATCH_VERSION=$(APPVERSION_P) - -## USB HID? -DEFINES += HAVE_IO_USB HAVE_L4_USBLIB IO_USB_MAX_ENDPOINTS=4 IO_HID_EP_LENGTH=64 HAVE_USB_APDU +CFLAGS += -Wuninitialized -fno-common $(WERROR) ## USB U2F DEFINES += HAVE_U2F HAVE_IO_U2F U2F_PROXY_MAGIC=\"ADA\" USB_SEGMENT_SIZE=64 -## WEBUSB -#WEBUSB_URL = https://www.ledger.com/pages/supported-crypto-assets -#DEFINES += HAVE_WEBUSB WEBUSB_URL_SIZE_B=$(shell echo -n $(WEBUSB_URL) | wc -c) WEBUSB_URL=$(shell echo -n $(WEBUSB_URL) | sed -e "s/./\\\'\0\\\',/g") -DEFINES += HAVE_WEBUSB WEBUSB_URL_SIZE_B=0 WEBUSB_URL="" - -## BLUETOOTH -ifeq ($(TARGET_NAME),$(filter $(TARGET_NAME),TARGET_NANOX TARGET_STAX)) - DEFINES += HAVE_BLE BLE_COMMAND_TIMEOUT_MS=2000 HAVE_BLE_APDU -endif - ## Protect stack overflows DEFINES += HAVE_BOLOS_APP_STACK_CANARY -ifeq ($(TARGET_NAME),TARGET_NANOS) -DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=128 -else -DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=300 -DEFINES += HAVE_GLO096 -ifneq ($(TARGET_NAME),TARGET_STAX) -DEFINES += HAVE_BAGL BAGL_WIDTH=128 BAGL_HEIGHT=64 -DEFINES += HAVE_UX_FLOW -endif - -ifeq ($(TARGET_NAME),TARGET_STAX) -DEFINES += NBGL_QRCODE -SDK_SOURCE_PATH += qrcode -endif -DEFINES += HAVE_BAGL_ELLIPSIS # long label truncation feature -DEFINES += HAVE_BAGL_FONT_OPEN_SANS_REGULAR_11PX -DEFINES += HAVE_BAGL_FONT_OPEN_SANS_EXTRABOLD_11PX -DEFINES += HAVE_BAGL_FONT_OPEN_SANS_LIGHT_16PX -endif - -DEFINES += RESET_ON_CRASH - -## Use developer build -#DEVEL = 1 -#DEFINES += HEADLESS - -# Enabling debug PRINTF -ifeq ($(DEVEL), 1) - DEFINES += DEVEL HAVE_PRINTF - ifeq ($(TARGET_NAME),TARGET_NANOS) - DEFINES += PRINTF=screen_printf - else - DEFINES += PRINTF=mcu_usb_printf - endif -else - DEFINES += PRINTF\(...\)= -endif - # restricted features for Nano S # but not in DEVEL mode where we usually want to test all features with HEADLESS ifeq ($(TARGET_NAME), TARGET_NANOS) @@ -147,64 +132,14 @@ endif # always include this, it's important for Plutus users DEFINES += APP_FEATURE_TOKEN_MINTING -################## -# Dependencies # -################## - -# import rules to compile glyphs -include $(BOLOS_SDK)/Makefile.glyphs - -### computed variables -APP_SOURCE_PATH += src -SDK_SOURCE_PATH += lib_stusb lib_stusb_impl lib_u2f - -ifneq ($(TARGET_NAME),TARGET_STAX) -SDK_SOURCE_PATH += lib_ux -endif - -ifeq ($(TARGET_NAME),$(filter $(TARGET_NAME),TARGET_NANOX TARGET_STAX)) - SDK_SOURCE_PATH += lib_blewbxx lib_blewbxx_impl -endif - ################ -# Default rule # +# Analyze # ################ -all: default - - -############## -# Build # -############## - -listvariants: - @echo VARIANTS COIN cardano_ada - # part of CI analyze: clean scan-build --use-cc=clang -analyze-headers -enable-checker security -enable-checker unix -enable-checker valist -o scan-build --status-bugs make default -############## -# Load # -############## - -NANOS_ID = 1 -WORDS = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" -PIN = 5555 - -APP_LOAD_PARAMS =--appFlags 0x200 --curve ed25519 --path "44'/1815'" --path "1852'/1815'" --path "1853'/1815'" --path "1854'/1815'" --path "1855'/1815'" --path "1694'/1815'" -APP_LOAD_PARAMS += $(COMMON_LOAD_PARAMS) - -load: - python -m ledgerblue.loadApp $(APP_LOAD_PARAMS) - -delete: - python -m ledgerblue.deleteApp $(COMMON_DELETE_PARAMS) - -seed: - python -m ledgerblue.hostOnboard --id $(NANOS_ID) --words $(WORDS) --pin $(PIN) - - ############## # Style # ############## @@ -212,13 +147,11 @@ seed: format: astyle --options=.astylerc "src/*.h" "src/*.c" --exclude=src/glyphs.h --exclude=src/glyphs.c --ignore-exclude-errors - ############## # Size # ############## # prints app size, max is about 140K - size: all $(GCCPATH)arm-none-eabi-size --format=gnu bin/app.elf @@ -239,4 +172,4 @@ stax: clean BOLOS_SDK=$(STAX_SDK) make # import generic rules from the sdk -include $(BOLOS_SDK)/Makefile.rules +include $(BOLOS_SDK)/Makefile.standard_app diff --git a/icon_ada_flex.gif b/icon_ada_flex.gif new file mode 100644 index 0000000000000000000000000000000000000000..6cb2f87264b75fa35dce830e55f9f5f1592dacef GIT binary patch literal 394 zcmV;50d@XINk%w1VJHA70OJn;>gwu?i;J(XubrKpdU|@o!ot?p)@f;JSy@>O3=B$2 zN+>8O78VveJ39aX0RR90A^s6Va%Ew3Wn>_CX>@2HM@dak04x9i001ZeC;$Ki{_x33 ztGzhu&Ab2Lq#Pk Date: Fri, 19 Jul 2024 10:39:20 +0200 Subject: [PATCH 32/43] Improve NBGL main menu. --- src/ui_menu_nbgl.c | 76 ++++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/src/ui_menu_nbgl.c b/src/ui_menu_nbgl.c index 3d11377a..11ddc54b 100644 --- a/src/ui_menu_nbgl.c +++ b/src/ui_menu_nbgl.c @@ -35,10 +35,30 @@ enum { static nbgl_layoutSwitch_t switches[NB_SETTINGS_SWITCHES]; -static const char* const infoTypes[] = {"Version", "Developer", "Copyright"}; -static const char* const infoContents[] = {APPVERSION, "Vacuumlabs", +static const char* const infoTypes[NB_INFO_FIELDS] = {"Version", "Developer", "Copyright"}; +static const char* const infoContents[NB_INFO_FIELDS] = {APPVERSION, "Vacuumlabs", "(c) 2022 Ledger" }; + +static const nbgl_contentInfoList_t infoList = { + .nbInfos = NB_INFO_FIELDS, + .infoTypes = infoTypes, + .infoContents = infoContents, +}; + +// settings menu definition +static void settings_control_callback(int token, uint8_t index, int page); +#define SETTING_CONTENTS_NB 1 +static const nbgl_content_t contents[SETTING_CONTENTS_NB] = { + {.type = SWITCHES_LIST, + .content.switchesList.nbSwitches = NB_SETTINGS_SWITCHES, + .content.switchesList.switches = switches, + .contentActionCallback = settings_control_callback}}; + +static const nbgl_genericContents_t settingContents = {.callbackCallNeeded = false, + .contentsList = contents, + .nbContents = SETTING_CONTENTS_NB}; + static const int INS_NONE = -1; // Settings @@ -47,35 +67,13 @@ static void exit(void) os_sched_exit(-1); } -static bool settings_navigation_callback(uint8_t page, nbgl_pageContent_t* content) +static void settings_control_callback(int token, uint8_t index, int page) { - if (page == 0) { - content->type = INFOS_LIST; - content->infosList.nbInfos = NB_INFO_FIELDS; - content->infosList.infoTypes = (const char**)infoTypes; - content->infosList.infoContents = (const char**)infoContents; - } else if (page == 1) { - switches[0].text = "Expert mode"; - switches[0].subText = "Enable expert mode"; - switches[0].token = SWITCH_APP_MODE_TOKEN; - switches[0].tuneId = TUNE_TAP_CASUAL; - switches[0].initState = app_mode_expert(); - - content->type = SWITCHES_LIST; - content->switchesList.nbSwitches = NB_SETTINGS_SWITCHES; - content->switchesList.switches = (nbgl_layoutSwitch_t*)switches; - } else { - return false; - } - return true; -} - -static void settings_control_callback(int token, uint8_t index) -{ - UNUSED(index); + UNUSED(page); switch (token) { case SWITCH_APP_MODE_TOKEN: app_mode_set_expert(index); + switches[0].initState = app_mode_expert(); break; default: @@ -84,19 +82,25 @@ static void settings_control_callback(int token, uint8_t index) } } -static void ui_menu_settings(void) -{ - nbgl_useCaseSettings("Cardano settings", PAGE_START, NB_PAGE_SETTING, - IS_TOUCHABLE, ui_idle_flow, settings_navigation_callback, - settings_control_callback); -} - void ui_idle_flow(void) { // We need to make sure the ui context is reset even if the app restarts nbgl_reset_transaction_full_context(); - nbgl_useCaseHome("Cardano", &C_cardano_64, NULL, true, - ui_menu_settings, exit); + + switches[0].text = "Expert mode"; + switches[0].subText = "Enable expert mode"; + switches[0].token = SWITCH_APP_MODE_TOKEN; + switches[0].tuneId = TUNE_TAP_CASUAL; + switches[0].initState = app_mode_expert(); + + nbgl_useCaseHomeAndSettings("Cardano", + &C_cardano_64, + NULL, + INIT_HOME_PAGE, + &settingContents, + &infoList, + NULL, + exit); } void ui_idle(void) From 54964132fb82482c61c9b98a2d03724e6540342b Mon Sep 17 00:00:00 2001 From: Alexis Grojean Date: Fri, 19 Jul 2024 10:41:38 +0200 Subject: [PATCH 33/43] Bump version. --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 364e3941..2399e7e7 100644 --- a/Makefile +++ b/Makefile @@ -29,8 +29,8 @@ APPNAME = "Cardano ADA" # Application version APPVERSION_M = 7 -APPVERSION_N = 1 -APPVERSION_P = 1 +APPVERSION_N = 2 +APPVERSION_P = 0 APPVERSION = "$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)" # Application source files From f3686a4d9163fb3f65c90c0a4caba9033833473a Mon Sep 17 00:00:00 2001 From: Alexis Grojean Date: Fri, 19 Jul 2024 10:51:59 +0200 Subject: [PATCH 34/43] Format. --- src/ui_menu_nbgl.c | 65 +++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 36 deletions(-) diff --git a/src/ui_menu_nbgl.c b/src/ui_menu_nbgl.c index 11ddc54b..f34af4ff 100644 --- a/src/ui_menu_nbgl.c +++ b/src/ui_menu_nbgl.c @@ -23,10 +23,10 @@ #include "uiHelpers.h" #include "uiScreens_nbgl.h" -#define PAGE_START 0 -#define NB_PAGE_SETTING 2 -#define IS_TOUCHABLE false -#define NB_INFO_FIELDS 3 +#define PAGE_START 0 +#define NB_PAGE_SETTING 2 +#define IS_TOUCHABLE false +#define NB_INFO_FIELDS 3 #define NB_SETTINGS_SWITCHES 1 enum { @@ -37,27 +37,26 @@ static nbgl_layoutSwitch_t switches[NB_SETTINGS_SWITCHES]; static const char* const infoTypes[NB_INFO_FIELDS] = {"Version", "Developer", "Copyright"}; static const char* const infoContents[NB_INFO_FIELDS] = {APPVERSION, "Vacuumlabs", - "(c) 2022 Ledger" - }; + "(c) 2022 Ledger"}; static const nbgl_contentInfoList_t infoList = { - .nbInfos = NB_INFO_FIELDS, - .infoTypes = infoTypes, - .infoContents = infoContents, + .nbInfos = NB_INFO_FIELDS, + .infoTypes = infoTypes, + .infoContents = infoContents, }; // settings menu definition static void settings_control_callback(int token, uint8_t index, int page); #define SETTING_CONTENTS_NB 1 static const nbgl_content_t contents[SETTING_CONTENTS_NB] = { - {.type = SWITCHES_LIST, - .content.switchesList.nbSwitches = NB_SETTINGS_SWITCHES, - .content.switchesList.switches = switches, - .contentActionCallback = settings_control_callback}}; + {.type = SWITCHES_LIST, + .content.switchesList.nbSwitches = NB_SETTINGS_SWITCHES, + .content.switchesList.switches = switches, + .contentActionCallback = settings_control_callback}}; static const nbgl_genericContents_t settingContents = {.callbackCallNeeded = false, - .contentsList = contents, - .nbContents = SETTING_CONTENTS_NB}; + .contentsList = contents, + .nbContents = SETTING_CONTENTS_NB}; static const int INS_NONE = -1; @@ -71,14 +70,14 @@ static void settings_control_callback(int token, uint8_t index, int page) { UNUSED(page); switch (token) { - case SWITCH_APP_MODE_TOKEN: - app_mode_set_expert(index); - switches[0].initState = app_mode_expert(); - break; + case SWITCH_APP_MODE_TOKEN: + app_mode_set_expert(index); + switches[0].initState = app_mode_expert(); + break; - default: - PRINTF("Should not happen !"); - break; + default: + PRINTF("Should not happen !"); + break; } } @@ -87,24 +86,18 @@ void ui_idle_flow(void) // We need to make sure the ui context is reset even if the app restarts nbgl_reset_transaction_full_context(); - switches[0].text = "Expert mode"; - switches[0].subText = "Enable expert mode"; - switches[0].token = SWITCH_APP_MODE_TOKEN; - switches[0].tuneId = TUNE_TAP_CASUAL; - switches[0].initState = app_mode_expert(); + switches[0].text = "Expert mode"; + switches[0].subText = "Enable expert mode"; + switches[0].token = SWITCH_APP_MODE_TOKEN; + switches[0].tuneId = TUNE_TAP_CASUAL; + switches[0].initState = app_mode_expert(); - nbgl_useCaseHomeAndSettings("Cardano", - &C_cardano_64, - NULL, - INIT_HOME_PAGE, - &settingContents, - &infoList, - NULL, - exit); + nbgl_useCaseHomeAndSettings("Cardano", &C_cardano_64, NULL, INIT_HOME_PAGE, &settingContents, + &infoList, NULL, exit); } void ui_idle(void) { currentInstruction = INS_NONE; } -#endif // HAVE_NBGL +#endif // HAVE_NBGL From 094a17d758fdb00674d2be7893f2d60ce9d174c7 Mon Sep 17 00:00:00 2001 From: Alexis Grojean Date: Fri, 19 Jul 2024 16:54:00 +0200 Subject: [PATCH 35/43] Update CI --- .github/workflows/ci.yml | 52 ++++++++++++++++++++++++++++++++++++---- Makefile | 4 ++-- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 39d7d0ff..db287a6d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,8 +10,50 @@ on: workflow_dispatch: jobs: - build_application: - name: Build application using the reusable workflow - uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_build.yml@v1 - with: - upload_app_binaries_artifact: "compiled_app_binaries" +# build_application: +# name: Build application using the reusable workflow +# uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_build.yml@v1 +# with: +# upload_app_binaries_artifact: "compiled_app_binaries" + build: + name: Build application for NanoS, X, S+, and Stax + strategy: + fail-fast: false + matrix: + device: ["nanos", "nanox", "nanosp", "stax", "flex"] + runs-on: ubuntu-latest + container: + image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-dev-tools:latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Build application in developer + headless mode + id: "build" + shell: bash + run: | + eval "BOLOS_SDK=\$$(echo ${{ matrix.device }} | tr [:lower:] [:upper:])_SDK" && \ + echo "BOLOS_SDK value will be: ${BOLOS_SDK}" && \ + BOLOS_SDK=${BOLOS_SDK} make + # echo "binary_path=./build/*" >> $GITHUB_OUTPUT + echo "Build complete" + # - name: Upload app binary + # uses: actions/upload-artifact@v4 + # with: + # name: binaries-${{ matrix.device }} + # path: ${{ steps.build.outputs.binary_path }} + # if-no-files-found: error + - name: Install Node.js + run : apk add --update nodejs npm + - name : Install yarn + run: npm install --global yarn + - name: Install Cardano JS Library + run: git clone https://github.com/vacuumlabs/ledgerjs-cardano-shelley.git + - name : Run tests + run: | + BUILD_DEVICE_NAME="$(echo ${{ matrix.device }} | sed 's/nanosp/nanos2/')" + speculos --model ${{ matrix.device }} build/$BUILD_DEVICE_NAME/bin/app.elf --seed "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" --display headless & + apk add g++ libusb-dev libusb eudev-dev eudev && + cd ledgerjs-cardano-shelley && + yarn install && + yarn test-speculos diff --git a/Makefile b/Makefile index 2399e7e7..3d81fa56 100644 --- a/Makefile +++ b/Makefile @@ -56,8 +56,8 @@ VARIANT_VALUES = cardano_ada DEFINES += RESET_ON_CRASH # Enabling DEBUG flag will enable PRINTF and disable optimizations -# DEVEL = 1 -# DEFINES += HEADLESS +DEVEL = 1 +DEFINES += HEADLESS # Enabling debug PRINTF ifeq ($(DEVEL), 1) From 689c85ba20a84aa00caee0e1146377bc605bcb6d Mon Sep 17 00:00:00 2001 From: Alexis Grojean Date: Tue, 23 Jul 2024 14:52:01 +0200 Subject: [PATCH 36/43] Revert headless mode changes for Nano X and Nano S Plus. --- src/io.c | 8 +++----- src/io.h | 12 +++--------- src/uiHelpers.c | 12 ++---------- src/uiScreens_bagl.c | 2 +- 4 files changed, 9 insertions(+), 25 deletions(-) diff --git a/src/io.c b/src/io.c index 02b32e9b..18b7ec86 100644 --- a/src/io.c +++ b/src/io.c @@ -19,15 +19,15 @@ void reset_app_callback(void) } #endif -#if defined(TARGET_NANOS) +#ifdef HAVE_BAGL static timeout_callback_fn_t* timeout_cb; -void nanos_clear_timer() +void clear_timer() { timeout_cb = NULL; } -void nanos_set_timer(int ms, timeout_callback_fn_t* cb) +void set_timer(int ms, timeout_callback_fn_t* cb) { // if TRACE() is enabled, set_timer must be called // before ui_ methods, because it causes Ledger Nano S @@ -48,8 +48,6 @@ void nanos_set_timer(int ms, timeout_callback_fn_t* cb) callback(ux_allowed); \ } \ } while(0) -#elif defined(TARGET_NANOX) || defined(TARGET_NANOS2) -#define HANDLE_UX_TICKER_EVENT(ux_allowed) do {} while(0) #endif void CHECK_RESPONSE_SIZE(unsigned int tx) diff --git a/src/io.h b/src/io.h index 13d23f19..5c931d2c 100644 --- a/src/io.h +++ b/src/io.h @@ -51,16 +51,10 @@ void io_seproxyhal_display(const bagl_element_t* element); unsigned char io_event(unsigned char channel); bool device_is_unlocked(); -#endif -#if defined(TARGET_NANOS) + typedef void timeout_callback_fn_t(bool ux_allowed); -void nanos_set_timer(int ms, timeout_callback_fn_t* cb); -void nanos_clear_timer(); -#elif defined(TARGET_NANOX) || defined(TARGET_NANOS2) -// we had to disable set_timer for Nano X, since in the new SDK UX_STEP_CB/UX_STEP_NOCB macros -// automatically push a confirm callback to G_ux.stack[].ticker_callback with timeout zero -// which causes other callbacks (i.e. ours) to be ignored in UX_TICKER_EVENT, so set_timer -// does not actually work anymore in Nano X +void set_timer(int ms, timeout_callback_fn_t* cb); +void clear_timer(); #endif #ifdef HAVE_NBGL diff --git a/src/uiHelpers.c b/src/uiHelpers.c index ad32691b..22028706 100644 --- a/src/uiHelpers.c +++ b/src/uiHelpers.c @@ -93,11 +93,7 @@ void ui_displayPrompt_headless_cb(bool ux_allowed) void autoconfirmPrompt() { - #if defined(TARGET_NANOS) - nanos_set_timer(HEADLESS_DELAY, ui_displayPrompt_headless_cb); - #elif defined(TARGET_NANOX) || defined(TARGET_NANOS2) - UX_CALLBACK_SET_INTERVAL(HEADLESS_DELAY); - #endif + set_timer(HEADLESS_DELAY, ui_displayPrompt_headless_cb); } void ui_displayPaginatedText_headless_cb(bool ux_allowed) @@ -117,11 +113,7 @@ void ui_displayPaginatedText_headless_cb(bool ux_allowed) void autoconfirmPaginatedText() { - #if defined(TARGET_NANOS) - nanos_set_timer(HEADLESS_DELAY, ui_displayPaginatedText_headless_cb); - #elif defined(TARGET_NANOX) || defined(TARGET_NANOS2) - UX_CALLBACK_SET_INTERVAL(HEADLESS_DELAY); - #endif + set_timer(HEADLESS_DELAY, ui_displayPaginatedText_headless_cb); } #endif // HEADLESS diff --git a/src/uiScreens_bagl.c b/src/uiScreens_bagl.c index 83bfb3ee..1a5a0c52 100644 --- a/src/uiScreens_bagl.c +++ b/src/uiScreens_bagl.c @@ -19,8 +19,8 @@ void ui_idle(void) { currentInstruction = INS_NONE; + clear_timer(); #if defined(TARGET_NANOS) - nanos_clear_timer(); h_expert_update(); // The first argument is the starting index within menu_main, and the last // argument is a preprocessor. From 7a274efcc80ba5901bf632a4951b3b8d74b8c797 Mon Sep 17 00:00:00 2001 From: Alexis Grojean Date: Tue, 23 Jul 2024 14:52:37 +0200 Subject: [PATCH 37/43] Disable ledger assert and debug throw for Nano S --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 3d81fa56..f884a733 100644 --- a/Makefile +++ b/Makefile @@ -88,6 +88,8 @@ ENABLE_BLUETOOTH = 1 DISABLE_STANDARD_APP_FILES = 1 ifeq ($(TARGET_NAME),TARGET_NANOS) DISABLE_STANDARD_BAGL_UX_FLOW = 1 +DISABLE_DEBUG_LEDGER_ASSERT = 1 +DISABLE_DEBUG_THROW = 1 endif SDK_SOURCE_PATH += lib_u2f From 59e014da7c993434a0e70e16c6d88847af7e5293 Mon Sep 17 00:00:00 2001 From: Alexis Grojean Date: Tue, 23 Jul 2024 14:54:20 +0200 Subject: [PATCH 38/43] Update CI --- .github/workflows/ci.yml | 28 +++++++++++----------------- Makefile | 4 ++-- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index db287a6d..124d69e7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,13 +10,14 @@ on: workflow_dispatch: jobs: -# build_application: -# name: Build application using the reusable workflow -# uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_build.yml@v1 -# with: -# upload_app_binaries_artifact: "compiled_app_binaries" - build: - name: Build application for NanoS, X, S+, and Stax + build_application: + name: Build application using the reusable workflow + uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_build.yml@v1 + with: + upload_app_binaries_artifact: "compiled_app_binaries" + + build_and_test: + name: Build and test app for all supported devices strategy: fail-fast: false matrix: @@ -34,21 +35,14 @@ jobs: run: | eval "BOLOS_SDK=\$$(echo ${{ matrix.device }} | tr [:lower:] [:upper:])_SDK" && \ echo "BOLOS_SDK value will be: ${BOLOS_SDK}" && \ - BOLOS_SDK=${BOLOS_SDK} make - # echo "binary_path=./build/*" >> $GITHUB_OUTPUT + BOLOS_SDK=${BOLOS_SDK} DEVEL=1 DEFINES+=HEADLESS make echo "Build complete" - # - name: Upload app binary - # uses: actions/upload-artifact@v4 - # with: - # name: binaries-${{ matrix.device }} - # path: ${{ steps.build.outputs.binary_path }} - # if-no-files-found: error - name: Install Node.js run : apk add --update nodejs npm - name : Install yarn run: npm install --global yarn - - name: Install Cardano JS Library - run: git clone https://github.com/vacuumlabs/ledgerjs-cardano-shelley.git + - name: Install Cardano JS Library (Ledger fork) + run: git clone https://github.com/LedgerHQ/ledgerjs-cardano-shelley.git - name : Run tests run: | BUILD_DEVICE_NAME="$(echo ${{ matrix.device }} | sed 's/nanosp/nanos2/')" diff --git a/Makefile b/Makefile index f884a733..6bf02513 100644 --- a/Makefile +++ b/Makefile @@ -56,8 +56,8 @@ VARIANT_VALUES = cardano_ada DEFINES += RESET_ON_CRASH # Enabling DEBUG flag will enable PRINTF and disable optimizations -DEVEL = 1 -DEFINES += HEADLESS +# DEVEL = 1 +# DEFINES += HEADLESS # Enabling debug PRINTF ifeq ($(DEVEL), 1) From 4143bedd2d4082dcbf1376da5c885d40d1d5b16a Mon Sep 17 00:00:00 2001 From: Alexis Grojean Date: Tue, 23 Jul 2024 17:11:04 +0200 Subject: [PATCH 39/43] Fix fuzzing. --- src/io.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/io.h b/src/io.h index 5c931d2c..0cc2fd44 100644 --- a/src/io.h +++ b/src/io.h @@ -46,15 +46,15 @@ extern io_state_t io_state; // Everything below this point is Ledger magic #ifdef HAVE_BAGL void io_seproxyhal_display(const bagl_element_t* element); +typedef void timeout_callback_fn_t(bool ux_allowed); +void set_timer(int ms, timeout_callback_fn_t* cb); +void clear_timer(); #endif #ifndef FUZZING unsigned char io_event(unsigned char channel); bool device_is_unlocked(); -typedef void timeout_callback_fn_t(bool ux_allowed); -void set_timer(int ms, timeout_callback_fn_t* cb); -void clear_timer(); #endif #ifdef HAVE_NBGL From df6d34f1d06f8baa6ee19d2bd4a3f6864d684561 Mon Sep 17 00:00:00 2001 From: Alexis Grojean Date: Wed, 24 Jul 2024 09:01:15 +0200 Subject: [PATCH 40/43] PR review. --- CHANGELOG.md | 12 ++++++++++++ Makefile | 5 ----- README.md | 2 +- src/uiHelpers.c | 2 +- src/uiHelpers.h | 2 +- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ca4ef16..6b0445ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [7.2.0](TBD) - [TBD] + +### Added + +- Add flex support, +- Add integration tests in CI using [ledgerjs-cardano-shelley](https://github.com/LedgerHQ/ledgerjs-cardano-shelley) + +### Changed + +- Update Makefile to standard format, +- Fix headless mode on Nano X / SP for integration tests running, +- Update some NBGL calls to latest API (main menu), ## [7.1.0](TBD) - [TBD] diff --git a/Makefile b/Makefile index 6bf02513..559b95bc 100644 --- a/Makefile +++ b/Makefile @@ -97,11 +97,6 @@ SDK_SOURCE_PATH += lib_u2f ############## # Compiler # ############## - -# based in part on https://interrupt.memfault.com/blog/best-and-worst-gcc-clang-compiler-flags -WERROR := -Werror=return-type -Werror=parentheses -Werror=format-security -CFLAGS += -Wuninitialized -fno-common $(WERROR) - ## USB U2F DEFINES += HAVE_U2F HAVE_IO_U2F U2F_PROXY_MAGIC=\"ADA\" USB_SEGMENT_SIZE=64 diff --git a/README.md b/README.md index b021f825..8d52f163 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Cardano Ledger App -Cardano Ledger App for Ledger Nano S +Cardano Ledger App for Ledger devices (Nano S, Nano S Plus, Nano X, Stax, Flex) ## Building diff --git a/src/uiHelpers.c b/src/uiHelpers.c index 22028706..ce58009b 100644 --- a/src/uiHelpers.c +++ b/src/uiHelpers.c @@ -19,7 +19,7 @@ displayState_t displayState; // ux is a magic global variable implicitly referenced by the UX_ macros. Apps // should never need to reference it directly ux_state_t ux; -#elif defined(TARGET_NANOX) || defined(TARGET_NANOS2) || defined(TARGET_STAX) || defined(TARGET_FLEX) +#else ux_state_t G_ux; bolos_ux_params_t G_ux_params; #endif diff --git a/src/uiHelpers.h b/src/uiHelpers.h index e2f2e727..bd2a5c3d 100644 --- a/src/uiHelpers.h +++ b/src/uiHelpers.h @@ -10,7 +10,7 @@ typedef void ui_callback_fn_t(); -#if defined(TARGET_NANOX) || defined(TARGET_NANOS2) || defined(TARGET_STAX) || defined(TARGET_FLEX) +#if !defined(TARGET_NANOS) extern ux_state_t G_ux; extern bolos_ux_params_t G_ux_params; #endif From 45810057119394b22f6570f1a786703d069093d7 Mon Sep 17 00:00:00 2001 From: Alexis Grojean Date: Fri, 2 Aug 2024 12:49:23 +0200 Subject: [PATCH 41/43] Update address confirm UX + confirmation strings. --- src/getPublicKeys_ui.c | 2 +- src/signTxOutput_ui.c | 2 +- src/signTx_ui.c | 2 +- src/ui_nbgl.c | 93 +++++++++++++++++++++++++----------------- 4 files changed, 59 insertions(+), 40 deletions(-) diff --git a/src/getPublicKeys_ui.c b/src/getPublicKeys_ui.c index 038f11cf..be5ca08f 100644 --- a/src/getPublicKeys_ui.c +++ b/src/getPublicKeys_ui.c @@ -107,7 +107,7 @@ void getPublicKeys_respondOneKey_ui_runStep() if (ctx->currentPath == ctx->numPaths) { #ifdef HAVE_NBGL if (!ctx->silent_export) { - display_status("PUBLIC KEY\nEXPORTED"); + display_status("Public key\nexported"); } #endif // HAVE_NBGL keys_advanceStage(); diff --git a/src/signTxOutput_ui.c b/src/signTxOutput_ui.c index ff955d92..44010738 100644 --- a/src/signTxOutput_ui.c +++ b/src/signTxOutput_ui.c @@ -450,7 +450,7 @@ void signTxOutput_handleConfirm_ui_runStep() char msg[100] = {0}; snprintf(msg, SIZEOF(msg), "%s\n%s", subctx->ui_text1, subctx->ui_text2); ASSERT(strlen(msg) + 1 < SIZEOF(msg)); - display_confirmation(msg, "", "OUTPUT\nCONFIRMED", "Output\nrejected", this_fn, respond_with_user_reject); + display_confirmation(msg, "", "Output\nconfirmed", "Output\nrejected", this_fn, respond_with_user_reject); #endif // HAVE_BAGL } UI_STEP(HANDLE_CONFIRM_STEP_RESPOND) { diff --git a/src/signTx_ui.c b/src/signTx_ui.c index a500fcba..41b1c13d 100644 --- a/src/signTx_ui.c +++ b/src/signTx_ui.c @@ -1692,6 +1692,6 @@ void signTx_handleWitness_ui_runStep() void endTxStatus(void) { #ifdef HAVE_NBGL - display_status("TRANSACTION\nSIGNED"); + display_status("Transaction\nsigned"); #endif // HAVE_NBGL } diff --git a/src/ui_nbgl.c b/src/ui_nbgl.c index 70981cf8..826d41a8 100644 --- a/src/ui_nbgl.c +++ b/src/ui_nbgl.c @@ -32,8 +32,15 @@ enum { CONFIRMATION_STATUS_TOKEN, }; +typedef enum { + STATUS_TYPE_TRANSACTION, + STATUS_TYPE_ADDRESS, +} statusType_t; + typedef struct { - const char* confirmedStatus; // text displayed in confirmation page (after long press) + bool standardStatus; + statusType_t statusType; + const char* confirmedStatus; // text displayed in confirmation page (after long press) const char* rejectedStatus; // text displayed in rejection page (after reject confirmed) callback_t approvedCallback; callback_t rejectedCallback; @@ -123,6 +130,8 @@ static void reset_transaction_current_context(void) void nbgl_reset_transaction_full_context(void) { reset_transaction_current_context(); + uiContext.standardStatus = false; + uiContext.statusType = STATUS_TYPE_TRANSACTION; uiContext.pendingElement = 0; uiContext.lightConfirmation = false; uiContext.rejectedStatus = NULL; @@ -251,7 +260,14 @@ static void display_cancel_status(void) if (uiContext.rejectedStatus) { nbgl_useCaseStatus(uiContext.rejectedStatus, false, cancellation_status_callback); } else { - nbgl_useCaseStatus("Transaction rejected", false, cancellation_status_callback); + switch (uiContext.statusType) { + case STATUS_TYPE_TRANSACTION: + nbgl_useCaseReviewStatus(STATUS_TYPE_TRANSACTION_REJECTED, cancellation_status_callback); + break; + case STATUS_TYPE_ADDRESS: + nbgl_useCaseReviewStatus(STATUS_TYPE_ADDRESS_REJECTED, cancellation_status_callback); + break; + } } } @@ -347,6 +363,16 @@ static void confirmation_status_callback(void) if (uiContext.confirmedStatus) { nbgl_useCaseStatus(uiContext.confirmedStatus, true, ui_idle_flow); nbgl_reset_transaction_full_context(); + } else if(uiContext.standardStatus) { + switch (uiContext.statusType) { + case STATUS_TYPE_TRANSACTION: + nbgl_useCaseReviewStatus(STATUS_TYPE_TRANSACTION_SIGNED, ui_idle_flow); + break; + case STATUS_TYPE_ADDRESS: + nbgl_useCaseReviewStatus(STATUS_TYPE_ADDRESS_VERIFIED, ui_idle_flow); + break; + } + nbgl_reset_transaction_full_context(); } else { nbgl_useCaseSpinner("Processing"); } @@ -364,35 +390,6 @@ static void display_confirmation_status(void) } } -static void display_address_callback(void) -{ - uint8_t address_index = 0; - - // Address field is not displayed in pairList, so there is one element less. - uiContext.pairList.nbPairs = uiContext.currentElementCount - 1; - uiContext.pairList.pairs = tagValues; - - uiContext.confirmedStatus = "ADDRESS\nVERIFIED"; - uiContext.rejectedStatus = "Address verification\ncancelled"; - - for (uint8_t i = 0; i < uiContext.currentElementCount; i++) { - if (strcmp(uiContext.tagTitle[i], "Address")) { - tagValues[i].item = uiContext.tagTitle[i]; - tagValues[i].value = uiContext.tagContent[i]; - } else { - address_index = i; - } - } - - nbgl_useCaseAddressConfirmationExt(uiContext.tagContent[address_index], light_confirm_callback, &uiContext.pairList); - reset_transaction_current_context(); - - #ifdef HEADLESS - nbgl_refresh(); - trigger_callback(&display_confirmation_status); - #endif -} - static void trigger_callback(callback_t userAcceptCallback) { set_app_callback(userAcceptCallback); @@ -545,15 +542,37 @@ void display_choice(const char* text1, const char* text2, callback_t userAcceptC void display_address(callback_t userAcceptCallback, callback_t userRejectCallback) { TRACE("Displaying Address"); - uiContext.rejectedStatus = "Address verification\ncancelled"; - set_callbacks(userAcceptCallback, userRejectCallback); - nbgl_useCaseReviewStart(&C_cardano_64, "Verify Cardano\naddress", - NULL, "Cancel", display_address_callback, - display_cancel_status); + + uint8_t address_index = 0; + + // Address field is not displayed in pairList, so there is one element less. + uiContext.pairList.nbPairs = uiContext.currentElementCount - 1; + uiContext.pairList.pairs = tagValues; + + uiContext.standardStatus = true; + uiContext.statusType = STATUS_TYPE_ADDRESS; + + for (uint8_t i = 0; i < uiContext.currentElementCount; i++) { + if (strcmp(uiContext.tagTitle[i], "Address")) { + tagValues[i].item = uiContext.tagTitle[i]; + tagValues[i].value = uiContext.tagContent[i]; + } else { + address_index = i; + } + } + + nbgl_useCaseAddressReview(uiContext.tagContent[address_index], + &uiContext.pairList, + &C_cardano_64, + "Verify Cardano address", + NULL, + light_confirm_callback); + reset_transaction_current_context(); + #ifdef HEADLESS nbgl_refresh(); - trigger_callback(&display_address_callback); + trigger_callback(&display_confirmation_status); #endif } From 92bc9c93ecfc34d7b2e80c7d7faf8a766ef9426a Mon Sep 17 00:00:00 2001 From: Alexis Grojean Date: Fri, 2 Aug 2024 14:28:48 +0200 Subject: [PATCH 42/43] Update version. --- .github/workflows/ci.yml | 2 +- Makefile | 6 ++--- src/ui_nbgl.c | 50 ++++++++++++++++++++-------------------- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 124d69e7..e203aa1a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,7 +42,7 @@ jobs: - name : Install yarn run: npm install --global yarn - name: Install Cardano JS Library (Ledger fork) - run: git clone https://github.com/LedgerHQ/ledgerjs-cardano-shelley.git + run: git clone https://github.com/vacuumlabs/ledgerjs-cardano-shelley.git - name : Run tests run: | BUILD_DEVICE_NAME="$(echo ${{ matrix.device }} | sed 's/nanosp/nanos2/')" diff --git a/Makefile b/Makefile index 559b95bc..e0025416 100644 --- a/Makefile +++ b/Makefile @@ -29,8 +29,8 @@ APPNAME = "Cardano ADA" # Application version APPVERSION_M = 7 -APPVERSION_N = 2 -APPVERSION_P = 0 +APPVERSION_N = 1 +APPVERSION_P = 2 APPVERSION = "$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)" # Application source files @@ -56,7 +56,7 @@ VARIANT_VALUES = cardano_ada DEFINES += RESET_ON_CRASH # Enabling DEBUG flag will enable PRINTF and disable optimizations -# DEVEL = 1 +DEVEL = 1 # DEFINES += HEADLESS # Enabling debug PRINTF diff --git a/src/ui_nbgl.c b/src/ui_nbgl.c index 826d41a8..79d63311 100644 --- a/src/ui_nbgl.c +++ b/src/ui_nbgl.c @@ -33,15 +33,15 @@ enum { }; typedef enum { - STATUS_TYPE_TRANSACTION, - STATUS_TYPE_ADDRESS, + STATUS_TYPE_TRANSACTION, + STATUS_TYPE_ADDRESS, } statusType_t; typedef struct { bool standardStatus; - statusType_t statusType; - const char* confirmedStatus; // text displayed in confirmation page (after long press) - const char* rejectedStatus; // text displayed in rejection page (after reject confirmed) + statusType_t statusType; + const char* confirmedStatus; // text displayed in confirmation page (after long press) + const char* rejectedStatus; // text displayed in rejection page (after reject confirmed) callback_t approvedCallback; callback_t rejectedCallback; callback_t pendingDisplayPageFn; @@ -130,8 +130,8 @@ static void reset_transaction_current_context(void) void nbgl_reset_transaction_full_context(void) { reset_transaction_current_context(); - uiContext.standardStatus = false; - uiContext.statusType = STATUS_TYPE_TRANSACTION; + uiContext.standardStatus = false; + uiContext.statusType = STATUS_TYPE_TRANSACTION; uiContext.pendingElement = 0; uiContext.lightConfirmation = false; uiContext.rejectedStatus = NULL; @@ -259,15 +259,18 @@ static void display_cancel_status(void) if (uiContext.rejectedStatus) { nbgl_useCaseStatus(uiContext.rejectedStatus, false, cancellation_status_callback); - } else { - switch (uiContext.statusType) { - case STATUS_TYPE_TRANSACTION: - nbgl_useCaseReviewStatus(STATUS_TYPE_TRANSACTION_REJECTED, cancellation_status_callback); - break; - case STATUS_TYPE_ADDRESS: - nbgl_useCaseReviewStatus(STATUS_TYPE_ADDRESS_REJECTED, cancellation_status_callback); - break; - } + } + else { + switch (uiContext.statusType) { + case STATUS_TYPE_TRANSACTION: + nbgl_useCaseReviewStatus(STATUS_TYPE_TRANSACTION_REJECTED, + cancellation_status_callback); + break; + case STATUS_TYPE_ADDRESS: + nbgl_useCaseReviewStatus(STATUS_TYPE_ADDRESS_REJECTED, + cancellation_status_callback); + break; + } } } @@ -544,14 +547,14 @@ void display_address(callback_t userAcceptCallback, callback_t userRejectCallbac TRACE("Displaying Address"); set_callbacks(userAcceptCallback, userRejectCallback); - uint8_t address_index = 0; + uint8_t address_index = 0; // Address field is not displayed in pairList, so there is one element less. uiContext.pairList.nbPairs = uiContext.currentElementCount - 1; uiContext.pairList.pairs = tagValues; - uiContext.standardStatus = true; - uiContext.statusType = STATUS_TYPE_ADDRESS; + uiContext.standardStatus = true; + uiContext.statusType = STATUS_TYPE_ADDRESS; for (uint8_t i = 0; i < uiContext.currentElementCount; i++) { if (strcmp(uiContext.tagTitle[i], "Address")) { @@ -562,12 +565,9 @@ void display_address(callback_t userAcceptCallback, callback_t userRejectCallbac } } - nbgl_useCaseAddressReview(uiContext.tagContent[address_index], - &uiContext.pairList, - &C_cardano_64, - "Verify Cardano address", - NULL, - light_confirm_callback); + nbgl_useCaseAddressReview(uiContext.tagContent[address_index], &uiContext.pairList, + &C_cardano_64, "Verify Cardano address", NULL, + light_confirm_callback); reset_transaction_current_context(); #ifdef HEADLESS From b3012cc5160309ead76a7bac104c3f01281c71e9 Mon Sep 17 00:00:00 2001 From: Alexis Grojean Date: Fri, 2 Aug 2024 15:49:57 +0200 Subject: [PATCH 43/43] PR review. --- .github/workflows/ci.yml | 2 +- Makefile | 2 +- src/ui_nbgl.c | 38 ++++++++++++++++++++++++++------------ 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e203aa1a..d86d0ab1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,7 @@ jobs: run : apk add --update nodejs npm - name : Install yarn run: npm install --global yarn - - name: Install Cardano JS Library (Ledger fork) + - name: Install Cardano JS Library run: git clone https://github.com/vacuumlabs/ledgerjs-cardano-shelley.git - name : Run tests run: | diff --git a/Makefile b/Makefile index e0025416..8ba2715b 100644 --- a/Makefile +++ b/Makefile @@ -56,7 +56,7 @@ VARIANT_VALUES = cardano_ada DEFINES += RESET_ON_CRASH # Enabling DEBUG flag will enable PRINTF and disable optimizations -DEVEL = 1 +# DEVEL = 1 # DEFINES += HEADLESS # Enabling debug PRINTF diff --git a/src/ui_nbgl.c b/src/ui_nbgl.c index 79d63311..5a18fb3a 100644 --- a/src/ui_nbgl.c +++ b/src/ui_nbgl.c @@ -257,9 +257,13 @@ static void display_cancel_status(void) { ui_idle(); + // If rejectedStatus string is not NULL, then use it to display + // the status notification after the user has rejected the transaction. if (uiContext.rejectedStatus) { nbgl_useCaseStatus(uiContext.rejectedStatus, false, cancellation_status_callback); } + // Otherwise use the statusType to determine the status to display. + // The string is managed by the SDK. else { switch (uiContext.statusType) { case STATUS_TYPE_TRANSACTION: @@ -363,23 +367,31 @@ static void _display_choice(void) static void confirmation_status_callback(void) { + // If confirmedStatus string is not NULL, then use it to display + // the status notification after the user has confirmed the transaction. if (uiContext.confirmedStatus) { nbgl_useCaseStatus(uiContext.confirmedStatus, true, ui_idle_flow); nbgl_reset_transaction_full_context(); - } else if(uiContext.standardStatus) { - switch (uiContext.statusType) { - case STATUS_TYPE_TRANSACTION: - nbgl_useCaseReviewStatus(STATUS_TYPE_TRANSACTION_SIGNED, ui_idle_flow); - break; - case STATUS_TYPE_ADDRESS: - nbgl_useCaseReviewStatus(STATUS_TYPE_ADDRESS_VERIFIED, ui_idle_flow); - break; - } - nbgl_reset_transaction_full_context(); - } else { + // Otherwise, if the standardStatus flag is set, then use the statusType + // to determine the status to display (the string is managed by the SDK). + } + else if (uiContext.standardStatus) { + switch (uiContext.statusType) { + case STATUS_TYPE_TRANSACTION: + nbgl_useCaseReviewStatus(STATUS_TYPE_TRANSACTION_SIGNED, ui_idle_flow); + break; + case STATUS_TYPE_ADDRESS: + nbgl_useCaseReviewStatus(STATUS_TYPE_ADDRESS_VERIFIED, ui_idle_flow); + break; + } + nbgl_reset_transaction_full_context(); + // If neither of the above conditions are met, then the status notification + // display is delayed until the confirmation process is finished. + // A spinner is displayed in the meantime. + } + else { nbgl_useCaseSpinner("Processing"); } - } static void display_confirmation_status(void) @@ -446,6 +458,8 @@ void force_display(callback_t userAcceptCallback, callback_t userRejectCallback) } } +// Fill page content. If the content's number of lines exceeds the maximum number of lines per page, +// the page is displayed and the pending element is added. void fill_and_display_if_required(const char* line1, const char* line2, callback_t userAcceptCallback, callback_t userRejectCallback)