From 53f8338eaaac27768c62d94c4ccd9465ee3c826d Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Wed, 4 Oct 2023 18:23:34 +0200 Subject: [PATCH 01/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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