From 92add7843765314d850c8b8a94aa3e02953acb4f Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Tue, 30 Jan 2024 11:39:19 +0100 Subject: [PATCH] feature: message signing (CIP-8) --- CHANGELOG.md | 14 ++ Makefile | 4 +- fuzzing/CMakeLists.txt | 9 +- fuzzing/README.md | 1 + fuzzing/src/signMsg_harness.c | 52 +++++ src/bufView.h | 13 ++ src/cardano.h | 9 + src/handlers.c | 2 + src/messageSigning.c | 11 +- src/messageSigning.h | 4 + src/runTests.h | 8 +- src/securityPolicy.c | 43 ++++ src/securityPolicy.h | 6 + src/signCVote.c | 4 +- src/signCVote_ui.c | 8 +- src/signCVote_ui.h | 5 +- src/signMsg.c | 375 ++++++++++++++++++++++++++++++++++ src/signMsg.h | 56 +++++ src/signMsg_ui.c | 336 ++++++++++++++++++++++++++++++ src/signMsg_ui.h | 43 ++++ src/signOpCert.c | 2 + src/signOpCert.h | 3 +- src/signTx.h | 3 +- src/signTxCVoteRegistration.c | 2 +- src/signTxOutput.c | 13 +- src/signTxPoolRegistration.c | 2 +- src/state.h | 2 + src/textUtils.c | 3 +- src/textUtils.h | 2 +- src/tokens_test.c | 2 +- src/uiScreens_bagl.c | 4 +- 31 files changed, 1008 insertions(+), 33 deletions(-) create mode 100644 fuzzing/src/signMsg_harness.c create mode 100644 src/signMsg.c create mode 100644 src/signMsg.h create mode 100644 src/signMsg_ui.c create mode 100644 src/signMsg_ui.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 151eff69..4ca4ef16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,20 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [7.1.0](TBD) - [TBD] + +Message signing (CIP-8) + +### Added + +- support for basic message signing (CIP-8, CIP-30) + +### Changed + +- usage of chunks of maximum allowed size is now enforced (datums and reference scripts in outputs) +- TODO updated list of native tokens recognized by the app with correct decimal places + + ## [7.0.2](TBD) - [TBD] Conway era diff --git a/Makefile b/Makefile index 73a40b71..9f20aeac 100644 --- a/Makefile +++ b/Makefile @@ -18,8 +18,8 @@ APPNAME = "Cardano ADA" APPVERSION_M = 7 -APPVERSION_N = 0 -APPVERSION_P = 2 +APPVERSION_N = 1 +APPVERSION_P = 0 APPVERSION = "$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)" ifeq ($(BOLOS_SDK),) diff --git a/fuzzing/CMakeLists.txt b/fuzzing/CMakeLists.txt index 0d324582..6cda3a91 100644 --- a/fuzzing/CMakeLists.txt +++ b/fuzzing/CMakeLists.txt @@ -77,6 +77,8 @@ set(CARDANO_SOURCE ${CARDANO_PATH}/src/securityPolicy.c ${CARDANO_PATH}/src/signCVote.c ${CARDANO_PATH}/src/signCVote_ui.c + ${CARDANO_PATH}/src/signMsg.c + ${CARDANO_PATH}/src/signMsg_ui.c ${CARDANO_PATH}/src/signOpCert.c ${CARDANO_PATH}/src/signTx.c ${CARDANO_PATH}/src/signTxCVoteRegistration.c @@ -153,7 +155,7 @@ add_compile_definitions( APP_FEATURE_TOKEN_MINTING ) -set(SOURCE +set(SOURCE ${UX_SOURCE} ${CARDANO_SOURCE} ./src/os_mocks.c @@ -169,13 +171,14 @@ set(harnesses deriveNativeScriptHash_harness getPublicKeys_harness signCVote_harness + signMsg_harness signOpCert_harness signTx_harness ) foreach(harness IN LISTS harnesses) - add_executable(${harness} + add_executable(${harness} ./src/${harness}.c - ) + ) target_link_libraries(${harness} PUBLIC cardano) endforeach() \ No newline at end of file diff --git a/fuzzing/README.md b/fuzzing/README.md index a4688c84..fb18670a 100644 --- a/fuzzing/README.md +++ b/fuzzing/README.md @@ -19,6 +19,7 @@ deriveAddress_harness deriveNativeScriptHash_harness getPublicKeys_harness signCVote_harness +signMsg_harness signOpCert_harness signTx_harness ``` diff --git a/fuzzing/src/signMsg_harness.c b/fuzzing/src/signMsg_harness.c new file mode 100644 index 00000000..258a0b9c --- /dev/null +++ b/fuzzing/src/signMsg_harness.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include +#include + +uint8_t G_io_apdu_buffer[IO_APDU_BUFFER_SIZE]; + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + + UX_INIT(); + + uint8_t *input = NULL; + bool is_first = true; + + while (size > 5) { + io_state = IO_EXPECT_NONE; + uint8_t ins = data[0]; + uint8_t p1 = data[1]; + uint8_t p2 = data[2]; + uint8_t lc = data[3]; + + data += sizeof(uint8_t) * 4; + size -= sizeof(uint8_t) * 4; + + if (size < lc) { + return 0; + } + + uint8_t *input = malloc(lc); + if (input == NULL) { + return 0; + } + + memcpy(input, data, lc); + + data += lc; + size -= lc; + + BEGIN_TRY { + TRY { signMsg_handleAPDU(p1, p2, input, lc, is_first); } + CATCH_ALL {} + FINALLY {} + } + END_TRY; + + is_first = false; + free(input); + } + return 0; +} \ No newline at end of file diff --git a/src/bufView.h b/src/bufView.h index 53c1d9ef..00735a41 100644 --- a/src/bufView.h +++ b/src/bufView.h @@ -142,5 +142,18 @@ static inline int64_t parse_int64be(read_view_t* view) return (int64_t) parse_u8be(view); }; +static inline bool parse_bool(read_view_t* view) +{ + uint8_t value = parse_u1be(view); + + switch (value) { + case 0: + return false; + case 1: + return true; + default: + THROW(ERR_INVALID_DATA); + } +} #endif // H_CARDANO_APP_BUF_VIEW diff --git a/src/cardano.h b/src/cardano.h index cc99ac2c..390b9f8a 100644 --- a/src/cardano.h +++ b/src/cardano.h @@ -13,6 +13,8 @@ STATIC_ASSERT(LOVELACE_MAX_SUPPLY < LOVELACE_INVALID, "bad LOVELACE_INVALID"); +#define ED25519_SIGNATURE_LENGTH 64 + #define ADDRESS_KEY_HASH_LENGTH 28 #define POOL_KEY_HASH_LENGTH 28 #define VRF_KEY_HASH_LENGTH 32 @@ -180,4 +182,11 @@ typedef enum { #endif // APP_FEATURE_NATIVE_SCRIPT_HASH +// ============================== CIP8 MESSAGE SIGNING ============================== + +typedef enum { + CIP8_ADDRESS_FIELD_ADDRESS = 1, + CIP8_ADDRESS_FIELD_KEYHASH = 2, +} cip8_address_field_type_t; + #endif // H_CARDANO_APP_CARDANO diff --git a/src/handlers.c b/src/handlers.c index db9792f4..dd90fdb5 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -12,6 +12,7 @@ #include "deriveAddress.h" #include "deriveNativeScriptHash.h" #include "signTx.h" +#include "signMsg.h" #include "signOpCert.h" #include "signCVote.h" @@ -39,6 +40,7 @@ handler_fn_t* lookupHandler(uint8_t ins) CASE(0x22, signOpCert_handleAPDU); #endif // APP_FEATURE_OPCERT CASE(0x23, signCVote_handleAPDU); + CASE(0x24, signMsg_handleAPDU); #ifdef DEVEL // 0xF* - debug_mode related diff --git a/src/messageSigning.c b/src/messageSigning.c index 4d495ff1..4c2d4fd6 100644 --- a/src/messageSigning.c +++ b/src/messageSigning.c @@ -6,9 +6,9 @@ #include "securityPolicy.h" #include "crypto.h" -static void signRawMessageWithPath(bip44_path_t* pathSpec, - const uint8_t* messageBuffer, size_t messageSize, - uint8_t* outBuffer, size_t outSize) +void signRawMessageWithPath(bip44_path_t* pathSpec, + const uint8_t* messageBuffer, size_t messageSize, + uint8_t* outBuffer, size_t outSize) { size_t sigLen = outSize; @@ -23,6 +23,10 @@ static void signRawMessageWithPath(bip44_path_t* pathSpec, #ifndef FUZZING { + TRACE("signing with path:"); + BIP44_PRINTF(pathSpec); + PRINTF("\n"); + cx_err_t error = crypto_eddsa_sign(pathSpec->path, pathSpec->length, messageBuffer, @@ -37,7 +41,6 @@ static void signRawMessageWithPath(bip44_path_t* pathSpec, #endif ASSERT(sigLen == ED25519_SIGNATURE_LENGTH); - } // sign the given hash by the private key derived according to the given path diff --git a/src/messageSigning.h b/src/messageSigning.h index e2db31f8..4984df72 100644 --- a/src/messageSigning.h +++ b/src/messageSigning.h @@ -3,6 +3,10 @@ #include "bip44.h" +void signRawMessageWithPath(bip44_path_t* pathSpec, + const uint8_t* messageBuffer, size_t messageSize, + uint8_t* outBuffer, size_t outSize); + void getWitness(bip44_path_t* pathSpec, const uint8_t* txHashBuffer, size_t txHashSize, uint8_t* outBuffer, size_t outSize); diff --git a/src/runTests.h b/src/runTests.h index 4dec40ed..3c49170e 100644 --- a/src/runTests.h +++ b/src/runTests.h @@ -1,12 +1,12 @@ -#ifdef DEVEL - #ifndef H_CARDANO_APP_RUN_TESTS #define H_CARDANO_APP_RUN_TESTS +#ifdef DEVEL + #include "handlers.h" handler_fn_t handleRunTests; -#endif // H_CARDANO_APP_RUN_TESTS - #endif // DEVEL + +#endif // H_CARDANO_APP_RUN_TESTS diff --git a/src/securityPolicy.c b/src/securityPolicy.c index 7e5e1451..94ff7ef1 100644 --- a/src/securityPolicy.c +++ b/src/securityPolicy.c @@ -2183,3 +2183,46 @@ security_policy_t policyForSignCVoteWitness(bip44_path_t* path) break; } } + +security_policy_t policyForSignMsg( + const bip44_path_t* witnessPath, + cip8_address_field_type_t addressFieldType, + const addressParams_t* addressParams +) +{ + switch (bip44_classifyPath(witnessPath)) { + case PATH_ORDINARY_SPENDING_KEY: + case PATH_ORDINARY_STAKING_KEY: + case PATH_MULTISIG_SPENDING_KEY: + case PATH_MULTISIG_STAKING_KEY: + case PATH_MINT_KEY: + case PATH_DREP_KEY: + case PATH_COMMITTEE_COLD_KEY: + case PATH_COMMITTEE_HOT_KEY: + case PATH_POOL_COLD_KEY: + // OK + break; + default: + DENY(); + break; + } + + if (addressFieldType == CIP8_ADDRESS_FIELD_ADDRESS) { + DENY_UNLESS(isValidAddressParams(addressParams)); + + switch (addressParams->type) { + case BASE_PAYMENT_KEY_STAKE_KEY: + case BASE_PAYMENT_KEY_STAKE_SCRIPT: + case REWARD_KEY: + case ENTERPRISE_KEY: + // OK + break; + + default: + DENY(); + break; + } + } + + PROMPT(); +} diff --git a/src/securityPolicy.h b/src/securityPolicy.h index b185ac2c..7bcbd776 100644 --- a/src/securityPolicy.h +++ b/src/securityPolicy.h @@ -242,4 +242,10 @@ security_policy_t policyForSignCVoteInit(); security_policy_t policyForSignCVoteConfirm(); security_policy_t policyForSignCVoteWitness(bip44_path_t* path); +security_policy_t policyForSignMsg( + const bip44_path_t* witnessPath, + cip8_address_field_type_t addressFieldType, + const addressParams_t* addressParams +); + #endif // H_CARDANO_APP_SECURITY_POLICY diff --git a/src/signCVote.c b/src/signCVote.c index c588b7e8..6f275e4f 100644 --- a/src/signCVote.c +++ b/src/signCVote.c @@ -115,7 +115,7 @@ void signCVote_handleInitAPDU( default: THROW(ERR_NOT_IMPLEMENTED); } - handleInit_ui_runStep(); + signCVote_handleInit_ui_runStep(); } // ============================== VOTECAST CHUNK ============================== @@ -190,7 +190,7 @@ void signCVote_handleConfirmAPDU( } } - handleConfirm_ui_runStep(); + signCVote_handleConfirm_ui_runStep(); } // ============================== WITNESS ============================== diff --git a/src/signCVote_ui.c b/src/signCVote_ui.c index be0fe563..d1b3c48e 100644 --- a/src/signCVote_ui.c +++ b/src/signCVote_ui.c @@ -17,9 +17,9 @@ static ins_sign_cvote_context_t* ctx = &(instructionState.signCVoteContext); // ============================== INIT ============================== -void handleInit_ui_runStep() +void signCVote_handleInit_ui_runStep() { - ui_callback_fn_t* this_fn = handleInit_ui_runStep; + ui_callback_fn_t* this_fn = signCVote_handleInit_ui_runStep; UI_STEP_BEGIN(ctx->ui_step, this_fn); @@ -92,11 +92,11 @@ void handleInit_ui_runStep() // ============================== CONFIRM ============================== -void handleConfirm_ui_runStep() +void signCVote_handleConfirm_ui_runStep() { TRACE("UI step %d", ctx->ui_step); TRACE_STACK_USAGE(); - ui_callback_fn_t* this_fn = handleConfirm_ui_runStep; + ui_callback_fn_t* this_fn = signCVote_handleConfirm_ui_runStep; UI_STEP_BEGIN(ctx->ui_step, this_fn); diff --git a/src/signCVote_ui.h b/src/signCVote_ui.h index d18a1fb0..ca1edc3d 100644 --- a/src/signCVote_ui.h +++ b/src/signCVote_ui.h @@ -2,6 +2,7 @@ #define H_CARDANO_APP_SIGN_CVOTE_UI #include "uiHelpers.h" + // ============================== INIT ============================== enum { @@ -13,7 +14,7 @@ enum { HANDLE_INIT_INVALID, }; -void handleInit_ui_runStep(); +void signCVote_handleInit_ui_runStep(); // ============================== CONFIRM ============================== @@ -23,7 +24,7 @@ enum { HANDLE_CONFIRM_STEP_INVALID, }; -void handleConfirm_ui_runStep(); +void signCVote_handleConfirm_ui_runStep(); // ============================== WITNESS ============================== diff --git a/src/signMsg.c b/src/signMsg.c new file mode 100644 index 00000000..3fbf0d43 --- /dev/null +++ b/src/signMsg.c @@ -0,0 +1,375 @@ +#include "common.h" + +#include "signMsg.h" +#include "signMsg_ui.h" +#include "keyDerivation.h" +#include "endian.h" +#include "state.h" +#include "uiHelpers.h" +#include "securityPolicy.h" +#include "messageSigning.h" +#include "textUtils.h" +#include "signTxUtils.h" + +#ifdef HAVE_BAGL +#include "uiScreens_bagl.h" +#elif defined(HAVE_NBGL) +#include "uiScreens_nbgl.h" +#endif + +static ins_sign_msg_context_t* ctx = &(instructionState.signMsgContext); + +void signMsg_handleInitAPDU( + const uint8_t* wireDataBuffer, + size_t wireDataSize +) +{ + { + TRACE_BUFFER(wireDataBuffer, wireDataSize); + read_view_t view = make_read_view(wireDataBuffer, wireDataBuffer + wireDataSize); + + ctx->msgLength = parse_u4be(&view); + TRACE("Msg length: %d", ctx->msgLength); + ctx->remainingBytes = ctx->msgLength; + + view_skipBytes(&view, bip44_parseFromWire(&ctx->signingPath, VIEW_REMAINING_TO_TUPLE_BUF_SIZE(&view))); + TRACE("Signing path:"); + BIP44_PRINTF(&ctx->signingPath); + PRINTF("\n"); + + ctx->hashPayload = parse_bool(&view); + TRACE("Hash payload: %d", ctx->hashPayload); + + ctx->isAscii = parse_bool(&view); + TRACE("Is ascii: %d", ctx->isAscii); + + ctx->addressFieldType = parse_u1be(&view); + TRACE("Address field type: %d", ctx->addressFieldType); + switch (ctx->addressFieldType) { + case CIP8_ADDRESS_FIELD_ADDRESS: + view_parseAddressParams(&view, &ctx->addressParams); + break; + case CIP8_ADDRESS_FIELD_KEYHASH: + // no address field data to parse + break; + default: + THROW(ERR_INVALID_DATA); + } + + VALIDATE(view_remainingSize(&view) == 0, ERR_INVALID_DATA); + } + + // Check security policy + security_policy_t policy = policyForSignMsg( + &ctx->signingPath, + ctx->addressFieldType, + &ctx->addressParams + ); + ENSURE_NOT_DENIED(policy); + + // always compute message hash + blake2b_224_init(&ctx->msgHashCtx); + + { + // key is sent back at the end and possibly needed when displaying address field + extendedPublicKey_t extPubKey; + deriveExtendedPublicKey( + &ctx->signingPath, + &extPubKey + ); + STATIC_ASSERT(SIZEOF(extPubKey.pubKey) == SIZEOF(ctx->witnessKey), "wrong witness key size"); + memmove(ctx->witnessKey, extPubKey.pubKey, SIZEOF(extPubKey.pubKey)); + } + + // this must always be shown + ASSERT(policy == POLICY_PROMPT_BEFORE_RESPONSE); + ctx->ui_step = HANDLE_INIT_HASH_PAYLOAD; + signMsg_handleInit_ui_runStep(); +} + +static void signMsg_handleMsgChunkAPDU(const uint8_t* wireDataBuffer, size_t wireDataSize) +{ + { + ASSERT(ctx->stage == SIGN_MSG_STAGE_CHUNKS); + if (!ctx->hashPayload) { + // only a single chunk is to be received + ASSERT(ctx->receivedChunks == 0); + } + } + { + TRACE_BUFFER(wireDataBuffer, wireDataSize); + + ctx->receivedChunks += 1; + + read_view_t view = make_read_view(wireDataBuffer, wireDataBuffer + wireDataSize); + + const size_t chunkSize = parse_u4be(&view); + TRACE("chunkSize = %u", chunkSize); + + VALIDATE(chunkSize <= ctx->remainingBytes, ERR_INVALID_DATA); + + // the current chunk should have maximum allowed size; + // there is no point in allowing unnecessarily small chunks + // and it is a security risk if the first chunk (possibly the only one displayed) + // is artificially small + if (ctx->receivedChunks == 1) { + // the first chunk must be displayable + // the check below works for empty message (with special UI) too + if (ctx->isAscii) { + VALIDATE( + chunkSize == MIN(ctx->msgLength, MAX_CIP8_MSG_FIRST_CHUNK_ASCII_SIZE), + ERR_INVALID_DATA + ); + } else { + VALIDATE( + chunkSize == MIN(ctx->msgLength, MAX_CIP8_MSG_FIRST_CHUNK_HEX_SIZE), + ERR_INVALID_DATA + ); + } + } else { + // ctx->receivedChunks >= 2 + VALIDATE( + chunkSize == MIN(ctx->remainingBytes, MAX_CIP8_MSG_HIDDEN_CHUNK_SIZE), + ERR_INVALID_DATA + ); + } + + ASSERT(chunkSize <= ctx->remainingBytes); + ctx->remainingBytes -= chunkSize; + ctx->chunkSize = chunkSize; + + ASSERT(chunkSize <= SIZEOF(ctx->chunk)); + view_parseBuffer(ctx->chunk, &view, chunkSize); + if (ctx->isAscii) { + VALIDATE(str_isUnambiguousAscii(ctx->chunk, ctx->chunkSize), ERR_INVALID_DATA); + } + + VALIDATE(view_remainingSize(&view) == 0, ERR_INVALID_DATA); + } + { + TRACE("Adding msg chunk to msg hash"); + blake2b_224_append(&ctx->msgHashCtx, ctx->chunk, ctx->chunkSize); + } + + if (ctx->receivedChunks == 1) { + if (!ctx->hashPayload) { + // for non-hashed payload, we expect only a single chunk + VALIDATE(ctx->remainingBytes == 0, ERR_INVALID_DATA); + } + ctx->ui_step = HANDLE_CHUNK_STEP_INTRO; + signMsg_handleChunk_ui_runStep(); + } else { + // for non-hashed payload, we expect only a single chunk, + // so the state should be SIGN_MSG_STAGE_CONFIRM already + ASSERT(ctx->hashPayload); + + // the chunk has been added to msg hash, nothing more to do, and no UI + respondSuccessEmptyMsg(); + + if (ctx->remainingBytes == 0) { + ctx->stage = SIGN_MSG_STAGE_CONFIRM; + } + } +} + +static void _prepareAddressField() +{ + switch (ctx->addressFieldType) { + + case CIP8_ADDRESS_FIELD_ADDRESS: { + ctx->addressFieldSize = deriveAddress(&ctx->addressParams, ctx->addressField, SIZEOF(ctx->addressField)); + break; + } + + case CIP8_ADDRESS_FIELD_KEYHASH: { + STATIC_ASSERT(SIZEOF(ctx->addressField) >= ADDRESS_KEY_HASH_LENGTH, "wrong address field size"); + bip44_pathToKeyHash(&ctx->signingPath, ctx->addressField, ADDRESS_KEY_HASH_LENGTH); + ctx->addressFieldSize = ADDRESS_KEY_HASH_LENGTH; + break; + } + + default: + ASSERT(false); + } +} + +static size_t _createProtectedHeader(uint8_t* protectedHeaderBuffer, size_t maxSize) +{ + // protectedHeader = { + // 1 : -8, // set algorithm to EdDSA + // “address” : address_bytes // raw address given by the user, or key hash + // } + BEGIN_TRY { + TRY { + uint8_t* p = protectedHeaderBuffer; + uint8_t* end = protectedHeaderBuffer + maxSize; + + { + size_t len = cbor_writeToken(CBOR_TYPE_MAP, 2, p, end - p); + p += len; + } + { + size_t len = cbor_writeToken(CBOR_TYPE_UNSIGNED, 1, p, end - p); + p += len; + } + { + size_t len = cbor_writeToken(CBOR_TYPE_NEGATIVE, -8, p, end - p); + p += len; + } + { + size_t len = cbor_writeToken(CBOR_TYPE_TEXT, 7, p, end - p); + p += len; + } + + { + const char* text = "address"; + const size_t len = strlen(text); + ASSERT(p + len < end); + memmove(p, text, len); + p += len; + } + _prepareAddressField(); + { + size_t len = cbor_writeToken(CBOR_TYPE_BYTES, ctx->addressFieldSize, p, end - p); + p += len; + } + { + ASSERT(p + ctx->addressFieldSize < end); + memmove(p, ctx->addressField, ctx->addressFieldSize); + p += ctx->addressFieldSize; + } + + const size_t protectedHeaderSize = p - protectedHeaderBuffer; + ASSERT(protectedHeaderSize > 0); + ASSERT(protectedHeaderSize < maxSize); + + return protectedHeaderSize; + } CATCH(ERR_DATA_TOO_LARGE) { + ASSERT(false); + } FINALLY { + } + } END_TRY; + + return -1; +} + +static void signMsg_handleConfirmAPDU(const uint8_t* wireDataBuffer MARK_UNUSED, size_t wireDataSize) +{ + VALIDATE(wireDataSize == 0, ERR_INVALID_DATA); + + // it seems Ledger can sign 400 B, more is not needed since non-hashed msg is capped at 200 B + uint8_t sigStructure[400] = {0}; + explicit_bzero(sigStructure, SIZEOF(sigStructure)); + size_t written = 0; + const size_t maxWritten = SIZEOF(sigStructure); + + // Sig_structure = [ + // context : “Signature1”, + // body_protected : CBOR_encode(protectedHeader), + // external_aad : bstr, // empty buffer here + // payload : bstr // message or its hash as bytes + // ] + + written += cbor_writeToken(CBOR_TYPE_ARRAY, 4, sigStructure + written, maxWritten - written); + ASSERT(written < maxWritten); + + { + const char* firstElement = "Signature1"; + const size_t len = strlen(firstElement); + written += cbor_writeToken(CBOR_TYPE_TEXT, len, sigStructure + written, maxWritten - written); + ASSERT(written + len < maxWritten); + memmove(sigStructure + written, firstElement, len); + written += len; + ASSERT(written < maxWritten); + } + { + uint8_t protectedHeaderBuffer[100]; // address of max 57 bytes plus a couple of small items + const size_t len = _createProtectedHeader(protectedHeaderBuffer, SIZEOF(protectedHeaderBuffer)); + written += cbor_writeToken(CBOR_TYPE_BYTES, len, sigStructure + written, maxWritten - written); + ASSERT(written + len < maxWritten); + memmove(sigStructure + written, protectedHeaderBuffer, len); + written += len; + ASSERT(written < maxWritten); + } + { + written += cbor_writeToken(CBOR_TYPE_BYTES, 0, sigStructure + written, maxWritten - written); + ASSERT(written < maxWritten); + } + STATIC_ASSERT(SIZEOF(ctx->msgHash) * 8 == 224, "inconsistent message hash size"); + blake2b_224_finalize(&ctx->msgHashCtx, ctx->msgHash, SIZEOF(ctx->msgHash)); + { + if (ctx->hashPayload) { + written += cbor_writeToken(CBOR_TYPE_BYTES, SIZEOF(ctx->msgHash), sigStructure + written, maxWritten - written); + ASSERT(written < maxWritten); + + ASSERT(SIZEOF(ctx->msgHash) < maxWritten - written); + memmove(sigStructure + written, ctx->msgHash, SIZEOF(ctx->msgHash)); + written += SIZEOF(ctx->msgHash); + ASSERT(written < maxWritten); + } else { + // for non-hashed payload, the chunk from the previous APDU is used + ASSERT(ctx->receivedChunks == 1); + + written += cbor_writeToken(CBOR_TYPE_BYTES, ctx->chunkSize, sigStructure + written, maxWritten - written); + ASSERT(written < maxWritten); + + ASSERT(ctx->chunkSize < maxWritten - written); + memmove(sigStructure + written, ctx->chunk, ctx->chunkSize); + written += ctx->chunkSize; + ASSERT(written < maxWritten); + } + } + + const size_t sigStructureSize = written; + TRACE_BUFFER(sigStructure, sigStructureSize); + + // we do not sign anything that could be a transaction hash + // Note: in the v7.1 implementation Sig_structure has more than 40 bytes + ASSERT(sigStructureSize != TX_HASH_LENGTH); + + signRawMessageWithPath(&ctx->signingPath, sigStructure, sigStructureSize, ctx->signature, SIZEOF(ctx->signature)); + + ctx->ui_step = HANDLE_CONFIRM_STEP_MSG_HASH; + signMsg_handleConfirm_ui_runStep(); +} + +// ============================== MAIN HANDLER ============================== + +typedef void subhandler_fn_t(const uint8_t* dataBuffer, size_t dataSize); + +static subhandler_fn_t* lookup_subhandler(uint8_t p1) +{ + switch (p1) { +#define CASE(P1, HANDLER) case P1: return HANDLER; +#define DEFAULT(HANDLER) default: return HANDLER; + CASE(0x01, signMsg_handleInitAPDU); + CASE(0x02, signMsg_handleMsgChunkAPDU); + CASE(0x03, signMsg_handleConfirmAPDU); + DEFAULT(NULL) +#undef CASE +#undef DEFAULT + } +} + +void signMsg_handleAPDU( + uint8_t p1, + uint8_t p2, + const uint8_t* wireDataBuffer, + size_t wireDataSize, + bool isNewCall +) +{ + TRACE("P1 = 0x%x, P2 = 0x%x, isNewCall = %d", p1, p2, isNewCall); + ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); + + VALIDATE(p2 == P2_UNUSED, ERR_INVALID_REQUEST_PARAMETERS); + + if (isNewCall) { + explicit_bzero(ctx, SIZEOF(*ctx)); + ctx->stage = SIGN_MSG_STAGE_INIT; + } + + subhandler_fn_t* subhandler = lookup_subhandler(p1); + VALIDATE(subhandler != NULL, ERR_INVALID_REQUEST_PARAMETERS); + subhandler(wireDataBuffer, wireDataSize); +} diff --git a/src/signMsg.h b/src/signMsg.h new file mode 100644 index 00000000..2da3d2ea --- /dev/null +++ b/src/signMsg.h @@ -0,0 +1,56 @@ +#ifndef H_CARDANO_APP_SIGN_MSG +#define H_CARDANO_APP_SIGN_MSG + +#include "addressUtilsShelley.h" +#include "cardano.h" +#include "common.h" +#include "handlers.h" +#include "hash.h" +#include "bip44.h" +#include "keyDerivation.h" + +handler_fn_t signMsg_handleAPDU; + +#define CIP8_MSG_HASH_LENGTH 28 + +// Note: this cannot be increased, there is a limit of 200 chars in the UI +#define MAX_CIP8_MSG_FIRST_CHUNK_ASCII_SIZE 198 +#define MAX_CIP8_MSG_FIRST_CHUNK_HEX_SIZE 99 +#define MAX_CIP8_MSG_HIDDEN_CHUNK_SIZE 250 + +typedef enum { + SIGN_MSG_STAGE_NONE = 0, + SIGN_MSG_STAGE_INIT = 43, + SIGN_MSG_STAGE_CHUNKS = 44, + SIGN_MSG_STAGE_CONFIRM = 45, +} sign_msg_stage_t; + +typedef struct { + bip44_path_t signingPath; + cip8_address_field_type_t addressFieldType; + addressParams_t addressParams; + + bool isAscii; + bool hashPayload; + + size_t msgLength; + size_t remainingBytes; + size_t receivedChunks; + + uint8_t chunk[MAX_CIP8_MSG_HIDDEN_CHUNK_SIZE]; + size_t chunkSize; + + blake2b_224_context_t msgHashCtx; + uint8_t msgHash[CIP8_MSG_HASH_LENGTH]; + uint8_t signature[ED25519_SIGNATURE_LENGTH]; + uint8_t witnessKey[PUBLIC_KEY_SIZE]; + uint8_t addressField[MAX_ADDRESS_SIZE]; + size_t addressFieldSize; + + sign_msg_stage_t stage; + int ui_step; +} ins_sign_msg_context_t; + +handler_fn_t signMsg_handleAPDU; + +#endif // H_CARDANO_APP_SIGN_MSG \ No newline at end of file diff --git a/src/signMsg_ui.c b/src/signMsg_ui.c new file mode 100644 index 00000000..11b5e480 --- /dev/null +++ b/src/signMsg_ui.c @@ -0,0 +1,336 @@ +#include "hexUtils.h" +#include "messageSigning.h" +#include "securityPolicy.h" +#include "signMsg.h" +#include "signTxUtils.h" +#include "state.h" +#include "signMsg_ui.h" + +#ifdef HAVE_BAGL +#include "uiScreens_bagl.h" +#elif defined(HAVE_NBGL) +#include "uiScreens_nbgl.h" +#include "nbgl_use_case.h" +#endif + + +static ins_sign_msg_context_t* ctx = &(instructionState.signMsgContext); + +// ============================== INIT ============================== + +void signMsg_handleInit_ui_runStep() +{ + TRACE("UI step %d", ctx->ui_step); + TRACE_STACK_USAGE(); + ui_callback_fn_t* this_fn = signMsg_handleInit_ui_runStep; + + UI_STEP_BEGIN(ctx->ui_step, this_fn); + + UI_STEP(HANDLE_INIT_HASH_PAYLOAD) { + #ifdef HAVE_BAGL + const char* firstLine = (ctx->hashPayload) ? "Sign hashed" : "Sign non-hashed"; + ui_displayPrompt( + firstLine, + "message? (CIP-8)", + this_fn, + respond_with_user_reject + ); + #elif defined(HAVE_NBGL) + set_light_confirmation(true); + const char* text = (ctx->hashPayload) ? "Sign hashed\nmessage? (CIP-8)" : "Sign non-hashed\nmessage? (CIP-8)"; + display_prompt(text, "", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_INIT_WITNESS_PATH) { + #ifdef HAVE_BAGL + ui_displayPathScreen("Signing path", &ctx->signingPath, this_fn); + #elif defined(HAVE_NBGL) + char pathStr[BIP44_PATH_STRING_SIZE_MAX + 1] = {0}; + ui_getPathScreen(pathStr, SIZEOF(pathStr), &ctx->signingPath); + fill_and_display_if_required("Signing path", pathStr, this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_INIT_ADDRESS_FIELD_DISPLAY_KEY_HASH) { + if (ctx->addressFieldType == CIP8_ADDRESS_FIELD_ADDRESS) { + UI_STEP_JUMP(HANDLE_INIT_ADDRESS_FIELD_DISPLAY_ADDRESS); + } + + uint8_t hash[28]; + blake2b_224_hash( + ctx->witnessKey, SIZEOF(ctx->witnessKey), + hash, SIZEOF(hash) + ); + + #ifdef HAVE_BAGL + ui_displayHexBufferScreen( + "Address field", + hash, + SIZEOF(hash), + this_fn + ); + #elif defined(HAVE_NBGL) + char bufferHex[2 * SCRIPT_HASH_LENGTH + 1] = {0}; + ui_getHexBufferScreen(bufferHex, SIZEOF(bufferHex), hash, SIZEOF(hash)); + fill_and_display_if_required("Address field", bufferHex, this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_INIT_ADDRESS_FIELD_DISPLAY_ADDRESS) { + if (ctx->addressFieldType != CIP8_ADDRESS_FIELD_ADDRESS) { + UI_STEP_JUMP(HANDLE_INIT_RESPOND); + } + + uint8_t addressBuffer[MAX_ADDRESS_SIZE] = {0}; + size_t addressSize = deriveAddress(&ctx->addressParams, addressBuffer, SIZEOF(addressBuffer)); + ASSERT(addressSize > 0); + ASSERT(addressSize <= MAX_ADDRESS_SIZE); + + #ifdef HAVE_BAGL + ui_displayAddressScreen( + "Address field", + addressBuffer, addressSize, + this_fn + ); + #elif defined(HAVE_NBGL) + char humanAddress[MAX_HUMAN_ADDRESS_SIZE] = {0}; + ui_getAddressScreen( + humanAddress, + SIZEOF(humanAddress), + addressBuffer, + addressSize + ); + fill_and_display_if_required("Address field", humanAddress, this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_INIT_ADDRESS_FIELD_DISPLAY_SPENDING_PATH) { + #ifdef HAVE_BAGL + ui_displaySpendingInfoScreen(&ctx->addressParams, this_fn); + #elif defined(HAVE_NBGL) +#define SPENDING_INFO_SIZE MAX(BECH32_STRING_SIZE_MAX, BIP44_PATH_STRING_SIZE_MAX) + char line1[30]; + char spendingInfo[SPENDING_INFO_SIZE] = {0}; + ui_getSpendingInfoScreen(line1, SIZEOF(line1), spendingInfo, SIZEOF(spendingInfo), &ctx->addressParams); + fill_and_display_if_required(line1, spendingInfo, this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_INIT_ADDRESS_FIELD_DISPLAY_STAKING_INFO) { + #ifdef HAVE_BAGL + ui_displayStakingInfoScreen(&ctx->addressParams, this_fn); + #elif defined(HAVE_NBGL) + char line1[30] = {0}; + char stakingInfo[120] = {0}; + ui_getStakingInfoScreen(line1, SIZEOF(line1), stakingInfo, SIZEOF(stakingInfo), &ctx->addressParams); + fill_and_display_if_required(line1, stakingInfo, this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_INIT_RESPOND) { + respondSuccessEmptyMsg(); + ctx->stage = SIGN_MSG_STAGE_CHUNKS; + } + UI_STEP_END(HANDLE_INIT_INVALID); +} + +// ============================== CHUNK ============================== + +void _displayMsgEmpty(ui_callback_fn_t* callback) +{ + #ifdef HAVE_BAGL + ui_displayPaginatedText( + "Empty", + "message", + callback + ); + #elif defined(HAVE_NBGL) + fill_and_display_if_required("Empty", "message", callback, respond_with_user_reject); + #endif // HAVE_BAGL +} + +void _displayMsgIntro(ui_callback_fn_t* callback) +{ + char l1[30] = {0}; + if (ctx->isAscii) { + snprintf(l1, SIZEOF(l1), "Message (ASCII)"); + } else { + snprintf(l1, SIZEOF(l1), "Message (hex)"); + } + ASSERT(strlen(l1) + 1 < SIZEOF(l1)); + + char l2[30] = {0}; + snprintf(l2, SIZEOF(l2), "%u bytes", ctx->msgLength); + ASSERT(strlen(l2) + 1 < SIZEOF(l2)); + + #ifdef HAVE_BAGL + ui_displayPaginatedText( + l1, + l2, + callback + ); + #elif defined(HAVE_NBGL) + fill_and_display_if_required(l1, l2, callback, respond_with_user_reject); + #endif // HAVE_BAGL +} + +__noinline_due_to_stack__ +void _displayMsgFull(ui_callback_fn_t* callback) +{ + char l1[30]; + if (ctx->isAscii) { + snprintf(l1, SIZEOF(l1), "Message (ASCII)"); + } else { + snprintf(l1, SIZEOF(l1), "Message (hex)"); + } + ASSERT(strlen(l1) + 1 < SIZEOF(l1)); + + char l2[200]; + if (ctx->isAscii) { + ASSERT(ctx->chunkSize + 1 < SIZEOF(l2)); + memmove(l2, ctx->chunk, ctx->chunkSize); + l2[ctx->chunkSize] = '\0'; + } else { + encode_hex(ctx->chunk, ctx->chunkSize, l2, SIZEOF(l2)); + } + ASSERT(strlen(l2) + 1 < SIZEOF(l2)); + + #ifdef HAVE_BAGL + ui_displayPaginatedText( + l1, + l2, + callback + ); + #elif defined(HAVE_NBGL) + fill_and_display_if_required(l1, l2, callback, respond_with_user_reject); + #endif // HAVE_BAGL +} + +__noinline_due_to_stack__ +void _displayMsgChunk(ui_callback_fn_t* callback) +{ + const char* l1 = "Message starts with"; + + char l2[200]; + if (ctx->isAscii) { + ASSERT(ctx->chunkSize + 1 < SIZEOF(l2)); + memmove(l2, ctx->chunk, ctx->chunkSize); + l2[ctx->chunkSize] = '\0'; + } else { + encode_hex(ctx->chunk, ctx->chunkSize, l2, SIZEOF(l2)); + } + ASSERT(strlen(l2) + 1 < SIZEOF(l2)); + + #ifdef HAVE_BAGL + ui_displayPaginatedText( + l1, + l2, + callback + ); + #elif defined(HAVE_NBGL) + fill_and_display_if_required(l1, l2, callback, respond_with_user_reject); + #endif // HAVE_BAGL +} + +void signMsg_handleChunk_ui_runStep() +{ + TRACE("UI step %d", ctx->ui_step); + TRACE_STACK_USAGE(); + ui_callback_fn_t* this_fn = signMsg_handleChunk_ui_runStep; + + ASSERT(ctx->receivedChunks == 1); + + UI_STEP_BEGIN(ctx->ui_step, this_fn); + + UI_STEP(HANDLE_CHUNK_STEP_INTRO) { + if (ctx->msgLength == 0) { + _displayMsgEmpty(this_fn); + } else { + _displayMsgIntro(this_fn); + } + } + UI_STEP(HANDLE_CHUNK_STEP_DISPLAY) { + if (ctx->msgLength == 0) { + UI_STEP_JUMP(HANDLE_CHUNK_STEP_RESPOND); + } + if (ctx->remainingBytes == 0) { + _displayMsgFull(this_fn); + } else { + _displayMsgChunk(this_fn); + } + } + UI_STEP(HANDLE_CHUNK_STEP_RESPOND) { + respondSuccessEmptyMsg(); + + if (ctx->remainingBytes == 0) { + ctx->stage = SIGN_MSG_STAGE_CONFIRM; + } + } + UI_STEP_END(HANDLE_CHUNK_STEP_INVALID); +} + +// ============================== CONFIRM ============================== + +void signMsg_handleConfirm_ui_runStep() +{ + TRACE("UI step %d", ctx->ui_step); + TRACE_STACK_USAGE(); + ui_callback_fn_t* this_fn = signMsg_handleConfirm_ui_runStep; + + UI_STEP_BEGIN(ctx->ui_step, this_fn); + + UI_STEP(HANDLE_CONFIRM_STEP_MSG_HASH) { + #ifdef HAVE_BAGL + ui_displayHexBufferScreen( + "Message hash", + ctx->msgHash, + SIZEOF(ctx->msgHash), + this_fn + ); + #elif defined(HAVE_NBGL) + char bufferHex[2 * CIP8_MSG_HASH_LENGTH + 1] = {0}; + ui_getHexBufferScreen(bufferHex, SIZEOF(bufferHex), ctx->msgHash, SIZEOF(ctx->msgHash)); + fill_and_display_if_required("Message hash", bufferHex, this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_CONFIRM_STEP_FINAL_CONFIRM) { + #ifdef HAVE_BAGL + ui_displayPrompt( + "Sign", + "message?", + this_fn, + respond_with_user_reject + ); + #elif defined(HAVE_NBGL) + display_confirmation("Sign\n message?", "", "MESSAGE\nSIGNED", "Message\nrejected", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_CONFIRM_STEP_RESPOND) { + struct { + uint8_t signature[ED25519_SIGNATURE_LENGTH]; + uint8_t witnessKey[PUBLIC_KEY_SIZE]; + uint32_t addressFieldSize; + uint8_t addressField[MAX_ADDRESS_SIZE]; + } wireResponse = {0}; + STATIC_ASSERT(SIZEOF(wireResponse) <= 255, "too large msg signing wire response"); + + STATIC_ASSERT(SIZEOF(ctx->signature) == ED25519_SIGNATURE_LENGTH, "wrong signature buffer size"); + memmove(wireResponse.signature, ctx->signature, ED25519_SIGNATURE_LENGTH); + + STATIC_ASSERT(SIZEOF(ctx->witnessKey) == PUBLIC_KEY_SIZE, "wrong key buffer size"); + memmove(wireResponse.witnessKey, ctx->witnessKey, PUBLIC_KEY_SIZE); + + #ifndef FUZZING + STATIC_ASSERT(sizeof(wireResponse.addressFieldSize) == 4, "wrong address field size type"); + STATIC_ASSERT(sizeof(ctx->addressFieldSize) == 4, "wrong address field size type"); + u4be_write((uint8_t*) &wireResponse.addressFieldSize, ctx->addressFieldSize); + #endif + + STATIC_ASSERT(SIZEOF(ctx->addressField) == SIZEOF(wireResponse.addressField), "wrong address field size"); + memmove(wireResponse.addressField, ctx->addressField, ctx->addressFieldSize); + + io_send_buf(SUCCESS, (uint8_t*) &wireResponse, SIZEOF(wireResponse)); + #ifdef HAVE_BAGL + ui_displayBusy(); // displays dots, called only after I/O to avoid freezing + #endif // HAVE_BAGL + + ctx->stage = SIGN_MSG_STAGE_NONE; + ui_idle(); + } + UI_STEP_END(HANDLE_CONFIRM_STEP_INVALID); +} diff --git a/src/signMsg_ui.h b/src/signMsg_ui.h new file mode 100644 index 00000000..defc317c --- /dev/null +++ b/src/signMsg_ui.h @@ -0,0 +1,43 @@ +#ifndef H_CARDANO_APP_SIGN_CVOTE_UI +#define H_CARDANO_APP_SIGN_CVOTE_UI + +#include "uiHelpers.h" + +// ============================== INIT ============================== + +enum { + HANDLE_INIT_HASH_PAYLOAD = 100, + HANDLE_INIT_WITNESS_PATH, + HANDLE_INIT_ADDRESS_FIELD_DISPLAY_KEY_HASH, + HANDLE_INIT_ADDRESS_FIELD_DISPLAY_ADDRESS, + HANDLE_INIT_ADDRESS_FIELD_DISPLAY_SPENDING_PATH, + HANDLE_INIT_ADDRESS_FIELD_DISPLAY_STAKING_INFO, + HANDLE_INIT_RESPOND, + HANDLE_INIT_INVALID, +}; + +void signMsg_handleInit_ui_runStep(); + +// ============================== CHUNK ============================== + +enum { + HANDLE_CHUNK_STEP_INTRO = 200, + HANDLE_CHUNK_STEP_DISPLAY, + HANDLE_CHUNK_STEP_RESPOND, + HANDLE_CHUNK_STEP_INVALID, +}; + +void signMsg_handleChunk_ui_runStep(); + +// ============================== CONFIRM ============================== + +enum { + HANDLE_CONFIRM_STEP_MSG_HASH = 300, + HANDLE_CONFIRM_STEP_FINAL_CONFIRM, + HANDLE_CONFIRM_STEP_RESPOND, + HANDLE_CONFIRM_STEP_INVALID, +}; + +void signMsg_handleConfirm_ui_runStep(); + +#endif // H_CARDANO_APP_SIGN_CVOTE_UI diff --git a/src/signOpCert.c b/src/signOpCert.c index b28e820c..2d42dfde 100644 --- a/src/signOpCert.c +++ b/src/signOpCert.c @@ -132,6 +132,8 @@ void signOpCert_handleAPDU( static void signOpCert_ui_runStep() { + TRACE("UI step %d", ctx->ui_step); + TRACE_STACK_USAGE(); ui_callback_fn_t* this_fn = signOpCert_ui_runStep; UI_STEP_BEGIN(ctx->ui_step, this_fn); diff --git a/src/signOpCert.h b/src/signOpCert.h index e88eec68..29eb9d1f 100644 --- a/src/signOpCert.h +++ b/src/signOpCert.h @@ -3,6 +3,7 @@ #ifdef APP_FEATURE_OPCERT +#include "cardano.h" #include "common.h" #include "handlers.h" #include "bip44.h" @@ -18,7 +19,7 @@ typedef struct { uint64_t kesPeriod; uint64_t issueCounter; bip44_path_t poolColdKeyPathSpec; - uint8_t signature[64]; + uint8_t signature[ED25519_SIGNATURE_LENGTH]; int ui_step; } ins_sign_op_cert_context_t; diff --git a/src/signTx.h b/src/signTx.h index 1fbabcd7..8ff04c32 100644 --- a/src/signTx.h +++ b/src/signTx.h @@ -1,6 +1,7 @@ #ifndef H_CARDANO_APP_SIGN_TX #define H_CARDANO_APP_SIGN_TX +#include "cardano.h" #include "common.h" #include "hash.h" #include "handlers.h" @@ -156,7 +157,7 @@ typedef struct { typedef struct { bip44_path_t path; - uint8_t signature[64]; + uint8_t signature[ED25519_SIGNATURE_LENGTH]; } sign_tx_witness_data_t; typedef struct { diff --git a/src/signTxCVoteRegistration.c b/src/signTxCVoteRegistration.c index 12112433..5f080431 100644 --- a/src/signTxCVoteRegistration.c +++ b/src/signTxCVoteRegistration.c @@ -447,7 +447,7 @@ size_t _destinationToAddress( case DESTINATION_THIRD_PARTY: addressSize = destination->address.size; ASSERT(addressSize <= addressBufferSize); - memcpy( + memmove( addressBuffer, destination->address.buffer, addressSize diff --git a/src/signTxOutput.c b/src/signTxOutput.c index a412c65d..579ec09b 100644 --- a/src/signTxOutput.c +++ b/src/signTxOutput.c @@ -743,6 +743,10 @@ static void handleDatumInline(read_view_t* view) VALIDATE(chunkSize > 0, ERR_INVALID_DATA); VALIDATE(chunkSize <= MAX_CHUNK_SIZE, ERR_INVALID_DATA); VALIDATE(chunkSize <= subctx->stateData.datumRemainingBytes, ERR_INVALID_DATA); + if (subctx->stateData.datumRemainingBytes >= MAX_CHUNK_SIZE) { + // forces to use chunks of maximum allowed size + VALIDATE(chunkSize == MAX_CHUNK_SIZE, ERR_INVALID_DATA); + } view_parseBuffer(subctx->stateData.datumChunk, view, chunkSize); VALIDATE(view_remainingSize(view) == 0, ERR_INVALID_DATA); @@ -871,7 +875,7 @@ static void handleRefScriptAPDU(const uint8_t* wireDataBuffer, size_t wireDataSi // parse data subctx->stateData.refScriptRemainingBytes = parse_u4be(&view); - TRACE("refScriptRemainingBytes = %u", subctx->stateData.datumRemainingBytes); + TRACE("refScriptRemainingBytes = %u", subctx->stateData.refScriptRemainingBytes); VALIDATE(subctx->stateData.refScriptRemainingBytes > 0, ERR_INVALID_DATA); size_t chunkSize = parse_u4be(&view); @@ -936,7 +940,10 @@ static void handleRefScriptChunkAPDU(const uint8_t* wireDataBuffer, size_t wireD TRACE("chunkSize = %u", chunkSize); VALIDATE(chunkSize > 0, ERR_INVALID_DATA); VALIDATE(chunkSize <= MAX_CHUNK_SIZE, ERR_INVALID_DATA); - + if (subctx->stateData.refScriptRemainingBytes >= MAX_CHUNK_SIZE) { + // forces to use chunks of maximum allowed size + VALIDATE(chunkSize == MAX_CHUNK_SIZE, ERR_INVALID_DATA); + } VALIDATE(chunkSize <= subctx->stateData.refScriptRemainingBytes, ERR_INVALID_DATA); subctx->stateData.refScriptRemainingBytes -= chunkSize; @@ -947,7 +954,7 @@ static void handleRefScriptChunkAPDU(const uint8_t* wireDataBuffer, size_t wireD } { // add to tx - TRACE("Adding inline datum chunk to tx hash"); + TRACE("Adding reference script chunk to tx hash"); txHashBuilder_addOutput_referenceScript_dataChunk( &BODY_CTX->txHashBuilder, subctx->stateData.scriptChunk, subctx->stateData.refScriptChunkSize diff --git a/src/signTxPoolRegistration.c b/src/signTxPoolRegistration.c index aed6c5d0..d91863e8 100644 --- a/src/signTxPoolRegistration.c +++ b/src/signTxPoolRegistration.c @@ -600,7 +600,7 @@ static void _parseDnsName(pool_relay_t* relay, read_view_t* view) { relay->dnsNameSize = view_remainingSize(view); VALIDATE(relay->dnsNameSize <= DNS_NAME_SIZE_MAX, ERR_INVALID_DATA); - VALIDATE(str_isAllowedDnsName(VIEW_REMAINING_TO_TUPLE_BUF_SIZE(view)), ERR_INVALID_DATA); + VALIDATE(str_isUnambiguousAscii(VIEW_REMAINING_TO_TUPLE_BUF_SIZE(view)), ERR_INVALID_DATA); STATIC_ASSERT(SIZEOF(relay->dnsName) == DNS_NAME_SIZE_MAX, "wrong dns name buffer size"); view_parseBuffer(relay->dnsName, view, relay->dnsNameSize); diff --git a/src/state.h b/src/state.h index f95bcb53..59ff02a3 100644 --- a/src/state.h +++ b/src/state.h @@ -5,6 +5,7 @@ #include "getPublicKeys.h" #include "deriveAddress.h" #include "deriveNativeScriptHash.h" +#include "signMsg.h" #include "signTx.h" #include "signOpCert.h" #include "signCVote.h" @@ -22,6 +23,7 @@ typedef union { ins_sign_op_cert_context_t signOpCertContext; #endif // APP_FEATURE_OPCERT ins_sign_cvote_context_t signCVoteContext; + ins_sign_msg_context_t signMsgContext; } instructionState_t; // Note(instructions are uint8_t but we have a special INS_NONE value diff --git a/src/textUtils.c b/src/textUtils.c index 892fafef..f2c987de 100644 --- a/src/textUtils.c +++ b/src/textUtils.c @@ -263,7 +263,8 @@ bool str_isPrintableAsciiWithSpaces(const uint8_t* buffer, size_t bufferSize) return true; } -bool str_isAllowedDnsName(const uint8_t* buffer, size_t bufferSize) +// check if the string can be unambiguously displayed to the user +bool str_isUnambiguousAscii(const uint8_t* buffer, size_t bufferSize) { ASSERT(bufferSize < BUFFER_SIZE_PARANOIA); diff --git a/src/textUtils.h b/src/textUtils.h index 1ce68ba2..69b5ec56 100644 --- a/src/textUtils.h +++ b/src/textUtils.h @@ -46,7 +46,7 @@ size_t str_formatValidityBoundary(uint64_t slotNumber, char* out, size_t outSize bool str_isPrintableAsciiWithoutSpaces(const uint8_t* buffer, size_t bufferSize); bool str_isPrintableAsciiWithSpaces(const uint8_t* buffer, size_t bufferSize); -bool str_isAllowedDnsName(const uint8_t* buffer, size_t bufferSize); +bool str_isUnambiguousAscii(const uint8_t* buffer, size_t bufferSize); #ifdef DEVEL diff --git a/src/tokens_test.c b/src/tokens_test.c index 4abd3860..71ee56c4 100644 --- a/src/tokens_test.c +++ b/src/tokens_test.c @@ -104,7 +104,7 @@ void test_decimalPlaces() for (size_t i = 0; i < ARRAY_LEN(tokenTestCases); i++) { char tokenAmountStr[60]; token_group_t group; - memcpy(group.policyId, tokenTestCases[i].policyId, MINTING_POLICY_ID_SIZE); + memmove(group.policyId, tokenTestCases[i].policyId, MINTING_POLICY_ID_SIZE); str_formatTokenAmountOutput( &group, diff --git a/src/uiScreens_bagl.c b/src/uiScreens_bagl.c index cea937ae..ab0d85d1 100644 --- a/src/uiScreens_bagl.c +++ b/src/uiScreens_bagl.c @@ -373,7 +373,7 @@ void ui_displaySpendingInfoScreen( case SPENDING_PATH: { ui_displayPathScreen( - "Derivation path", + "Spending key path", &addressParams->spendingKeyPath, callback ); @@ -398,7 +398,7 @@ void ui_displaySpendingInfoScreen( } } -static const char STAKING_HEADING_PATH[] = "Staking path"; +static const char STAKING_HEADING_PATH[] = "Stake key path"; static const char STAKING_HEADING_KEY_HASH[] = "Stake key hash"; static const char STAKING_HEADING_SCRIPT_HASH[] = "Stake script hash"; static const char STAKING_HEADING_POINTER[] = "Stake key pointer";