void transaction_offset(unsigned char value) { if ((btchip_context_D.transactionHashOption & TRANSACTION_HASH_FULL) != 0) { L_DEBUG_BUF(("Add to hash full\n", btchip_context_D.transactionBufferPointer, value)); cx_hash(&btchip_context_D.transactionHashFull.header, 0, btchip_context_D.transactionBufferPointer, value, NULL); } if ((btchip_context_D.transactionHashOption & TRANSACTION_HASH_AUTHORIZATION) != 0) { cx_hash(&btchip_context_D.transactionHashAuthorization.header, 0, btchip_context_D.transactionBufferPointer, value, NULL); } }
void transaction_offset(unsigned char value) { if ((btchip_context_D.transactionHashOption & TRANSACTION_HASH_FULL) != 0) { PRINTF("Add to hash full\n%.*H\n",value,btchip_context_D.transactionBufferPointer); if (btchip_context_D.usingOverwinter) { cx_hash(&btchip_context_D.transactionHashFull.blake2b.header, 0, btchip_context_D.transactionBufferPointer, value, NULL, 0); } else { cx_hash(&btchip_context_D.transactionHashFull.sha256.header, 0, btchip_context_D.transactionBufferPointer, value, NULL, 0); } } if ((btchip_context_D.transactionHashOption & TRANSACTION_HASH_AUTHORIZATION) != 0) { cx_hash(&btchip_context_D.transactionHashAuthorization.header, 0, btchip_context_D.transactionBufferPointer, value, NULL, 0); } }
unsigned short btchip_apdu_get_trusted_input() { unsigned char apduLength; unsigned char dataOffset = 0; unsigned char trustedInputSignature[32]; cx_sha256_t hash; apduLength = G_io_apdu_buffer[ISO_OFFSET_LC]; SB_CHECK(N_btchip.bkp.config.operationMode); switch (SB_GET(N_btchip.bkp.config.operationMode)) { case BTCHIP_MODE_WALLET: case BTCHIP_MODE_RELAXED_WALLET: case BTCHIP_MODE_SERVER: break; default: return BTCHIP_SW_CONDITIONS_OF_USE_NOT_SATISFIED; } if (G_io_apdu_buffer[ISO_OFFSET_P1] == GET_TRUSTED_INPUT_P1_FIRST) { // Initialize btchip_context_D.transactionTargetInput = btchip_read_u32(G_io_apdu_buffer + ISO_OFFSET_CDATA, 1, 0); btchip_context_D.transactionContext.transactionState = BTCHIP_TRANSACTION_NONE; btchip_context_D.trustedInputProcessed = 0; btchip_context_D.transactionContext.consumeP2SH = 0; btchip_set_check_internal_structure_integrity(1); dataOffset = 4; btchip_context_D.transactionHashOption = TRANSACTION_HASH_FULL; btchip_context_D.usingSegwit = 0; } else if (G_io_apdu_buffer[ISO_OFFSET_P1] != GET_TRUSTED_INPUT_P1_NEXT) { return BTCHIP_SW_INCORRECT_P1_P2; } if (G_io_apdu_buffer[ISO_OFFSET_P2] != 0x00) { return BTCHIP_SW_INCORRECT_P1_P2; } btchip_context_D.transactionBufferPointer = G_io_apdu_buffer + ISO_OFFSET_CDATA + dataOffset; btchip_context_D.transactionDataRemaining = apduLength - dataOffset; transaction_parse(PARSE_MODE_TRUSTED_INPUT); if (btchip_context_D.transactionContext.transactionState == BTCHIP_TRANSACTION_PARSED) { unsigned char targetHash[32]; btchip_context_D.transactionContext.transactionState = BTCHIP_TRANSACTION_NONE; btchip_set_check_internal_structure_integrity(1); if (!btchip_context_D.trustedInputProcessed) { // Output was not found return BTCHIP_SW_INCORRECT_DATA; } cx_hash(&btchip_context_D.transactionHashFull.sha256.header, CX_LAST, (unsigned char *)NULL, 0, targetHash, 32); // Otherwise prepare cx_rng(G_io_apdu_buffer, 8); G_io_apdu_buffer[0] = MAGIC_TRUSTED_INPUT; G_io_apdu_buffer[1] = 0x00; cx_sha256_init(&hash); cx_hash(&hash.header, CX_LAST, targetHash, 32, G_io_apdu_buffer + 4, 32); btchip_write_u32_le(G_io_apdu_buffer + 4 + 32, btchip_context_D.transactionTargetInput); os_memmove(G_io_apdu_buffer + 4 + 32 + 4, btchip_context_D.transactionContext.transactionAmount, 8); cx_hmac_sha256(N_btchip.bkp.trustedinput_key, sizeof(N_btchip.bkp.trustedinput_key), G_io_apdu_buffer, TRUSTEDINPUT_SIZE, trustedInputSignature, 32); os_memmove(G_io_apdu_buffer + TRUSTEDINPUT_SIZE, trustedInputSignature, 8); btchip_context_D.outLength = 0x38; } return BTCHIP_SW_OK; }
void transaction_parse(unsigned char parseMode) { unsigned char optionP2SHSkip2FA = ((N_btchip.bkp.config.options & BTCHIP_OPTION_SKIP_2FA_P2SH) != 0); btchip_set_check_internal_structure_integrity(0); BEGIN_TRY { TRY { for (;;) { switch (btchip_context_D.transactionContext.transactionState) { case BTCHIP_TRANSACTION_NONE: { PRINTF("Init transaction parser\n"); // Reset transaction state btchip_context_D.transactionContext .transactionRemainingInputsOutputs = 0; btchip_context_D.transactionContext .transactionCurrentInputOutput = 0; btchip_context_D.transactionContext.scriptRemaining = 0; os_memset( btchip_context_D.transactionContext.transactionAmount, 0, sizeof(btchip_context_D.transactionContext .transactionAmount)); // TODO : transactionControlFid // Reset hashes if (btchip_context_D.usingOverwinter) { if (btchip_context_D.segwitParsedOnce) { uint8_t parameters[16]; os_memmove(parameters, OVERWINTER_PARAM_SIGHASH, 16); btchip_write_u32_le(parameters + 12, btchip_context_D.usingOverwinter == ZCASH_USING_OVERWINTER_SAPLING ? CONSENSUS_BRANCH_ID_SAPLING : CONSENSUS_BRANCH_ID_OVERWINTER); cx_blake2b_init2(&btchip_context_D.transactionHashFull.blake2b, 256, NULL, 0, parameters, 16); } } else { cx_sha256_init(&btchip_context_D.transactionHashFull.sha256); } cx_sha256_init( &btchip_context_D.transactionHashAuthorization); if (btchip_context_D.usingSegwit) { btchip_context_D.transactionHashOption = 0; if (!btchip_context_D.segwitParsedOnce) { if (btchip_context_D.usingOverwinter) { cx_blake2b_init2(&btchip_context_D.segwit.hash.hashPrevouts.blake2b, 256, NULL, 0, OVERWINTER_PARAM_PREVOUTS, 16); cx_blake2b_init2(&btchip_context_D.transactionHashFull.blake2b, 256, NULL, 0, OVERWINTER_PARAM_SEQUENCE, 16); } else { cx_sha256_init( &btchip_context_D.segwit.hash.hashPrevouts.sha256); } } else { PRINTF("Resume SegWit hash\n"); PRINTF("SEGWIT Version\n%.*H\n",sizeof(btchip_context_D.transactionVersion),btchip_context_D.transactionVersion); PRINTF("SEGWIT HashedPrevouts\n%.*H\n",sizeof(btchip_context_D.segwit.cache.hashedPrevouts),btchip_context_D.segwit.cache.hashedPrevouts); PRINTF("SEGWIT HashedSequence\n%.*H\n",sizeof(btchip_context_D.segwit.cache.hashedSequence),btchip_context_D.segwit.cache.hashedSequence); if (btchip_context_D.usingOverwinter) { cx_hash(&btchip_context_D.transactionHashFull.blake2b.header, 0, btchip_context_D.transactionVersion, sizeof(btchip_context_D.transactionVersion), NULL, 0); cx_hash(&btchip_context_D.transactionHashFull.blake2b.header, 0, btchip_context_D.nVersionGroupId, sizeof(btchip_context_D.nVersionGroupId), NULL, 0); cx_hash(&btchip_context_D.transactionHashFull.blake2b.header, 0, btchip_context_D.segwit.cache.hashedPrevouts, sizeof(btchip_context_D.segwit.cache.hashedPrevouts), NULL, 0); cx_hash(&btchip_context_D.transactionHashFull.blake2b.header, 0, btchip_context_D.segwit.cache.hashedSequence, sizeof(btchip_context_D.segwit.cache.hashedSequence), NULL, 0); cx_hash(&btchip_context_D.transactionHashFull.blake2b.header, 0, btchip_context_D.segwit.cache.hashedOutputs, sizeof(btchip_context_D.segwit.cache.hashedOutputs), NULL, 0); cx_hash(&btchip_context_D.transactionHashFull.blake2b.header, 0, OVERWINTER_NO_JOINSPLITS, 32, NULL, 0); if (btchip_context_D.usingOverwinter == ZCASH_USING_OVERWINTER_SAPLING) { cx_hash(&btchip_context_D.transactionHashFull.blake2b.header, 0, OVERWINTER_NO_JOINSPLITS, 32, NULL, 0); // sapling hashShieldedSpends cx_hash(&btchip_context_D.transactionHashFull.blake2b.header, 0, OVERWINTER_NO_JOINSPLITS, 32, NULL, 0); // sapling hashShieldedOutputs } cx_hash(&btchip_context_D.transactionHashFull.blake2b.header, 0, btchip_context_D.nLockTime, sizeof(btchip_context_D.nLockTime), NULL, 0); cx_hash(&btchip_context_D.transactionHashFull.blake2b.header, 0, btchip_context_D.nExpiryHeight, sizeof(btchip_context_D.nExpiryHeight), NULL, 0); if (btchip_context_D.usingOverwinter == ZCASH_USING_OVERWINTER_SAPLING) { unsigned char valueBalance[8]; os_memset(valueBalance, 0, sizeof(valueBalance)); cx_hash(&btchip_context_D.transactionHashFull.blake2b.header, 0, valueBalance, sizeof(valueBalance), NULL, 0); // sapling valueBalance } cx_hash(&btchip_context_D.transactionHashFull.blake2b.header, 0, btchip_context_D.sigHashType, sizeof(btchip_context_D.sigHashType), NULL, 0); } else { cx_hash( &btchip_context_D.transactionHashFull.sha256.header, 0, btchip_context_D.transactionVersion, sizeof(btchip_context_D.transactionVersion), NULL, 0); cx_hash( &btchip_context_D.transactionHashFull.sha256.header, 0, btchip_context_D.segwit.cache.hashedPrevouts, sizeof(btchip_context_D.segwit.cache .hashedPrevouts), NULL, 0); cx_hash( &btchip_context_D.transactionHashFull.sha256.header, 0, btchip_context_D.segwit.cache.hashedSequence, sizeof(btchip_context_D.segwit.cache .hashedSequence), NULL, 0); cx_hash(&btchip_context_D .transactionHashAuthorization.header, 0, (unsigned char *)&btchip_context_D .segwit.cache, sizeof(btchip_context_D.segwit.cache), NULL, 0); } } } // Parse the beginning of the transaction // Version check_transaction_available(4); os_memmove(btchip_context_D.transactionVersion, btchip_context_D.transactionBufferPointer, 4); transaction_offset_increase(4); if (btchip_context_D.usingOverwinter) { // nVersionGroupId check_transaction_available(4); os_memmove(btchip_context_D.nVersionGroupId, btchip_context_D.transactionBufferPointer, 4); transaction_offset_increase(4); } if (G_coin_config->flags & FLAG_PEERCOIN_SUPPORT) { if (btchip_context_D.coinFamily == BTCHIP_FAMILY_PEERCOIN) { // Timestamp check_transaction_available(4); transaction_offset_increase(4); } } // Number of inputs btchip_context_D.transactionContext .transactionRemainingInputsOutputs = transaction_get_varint(); PRINTF("Number of inputs : " DEBUG_LONG "\n",btchip_context_D.transactionContext.transactionRemainingInputsOutputs); // Ready to proceed btchip_context_D.transactionContext.transactionState = BTCHIP_TRANSACTION_DEFINED_WAIT_INPUT; // no break is intentional } case BTCHIP_TRANSACTION_DEFINED_WAIT_INPUT: { unsigned char trustedInputFlag = 1; PRINTF("Process input\n"); if (btchip_context_D.transactionContext .transactionRemainingInputsOutputs == 0) { // No more inputs to hash, move forward btchip_context_D.transactionContext.transactionState = BTCHIP_TRANSACTION_INPUT_HASHING_DONE; continue; } if (btchip_context_D.transactionDataRemaining < 1) { // No more data to read, ok goto ok; } // Proceed with the next input if (parseMode == PARSE_MODE_TRUSTED_INPUT) { check_transaction_available( 36); // prevout : 32 hash + 4 index transaction_offset_increase(36); } if (parseMode == PARSE_MODE_SIGNATURE) { unsigned char trustedInputLength; unsigned char trustedInput[0x38]; unsigned char amount[8]; unsigned char *savePointer; // Expect the trusted input flag and trusted input // length check_transaction_available(2); switch (*btchip_context_D.transactionBufferPointer) { case 0: if (btchip_context_D.usingSegwit) { PRINTF("Non trusted input used in segwit mode"); goto fail; } trustedInputFlag = 0; break; case 1: if (btchip_context_D.usingSegwit) { PRINTF("Trusted input used in segwit mode"); goto fail; } trustedInputFlag = 1; break; case 2: if (!btchip_context_D.usingSegwit) { PRINTF("Segwit input not used in segwit mode"); goto fail; } break; default: PRINTF("Invalid trusted input flag\n"); goto fail; } /* trustedInputLength = *(btchip_context_D.transactionBufferPointer + 1); if (trustedInputLength > sizeof(trustedInput)) { PRINTF("Trusted input too long\n"); goto fail; } */ if (btchip_context_D.usingSegwit) { transaction_offset_increase(1); check_transaction_available( 36); // prevout : 32 hash + 4 index if (!btchip_context_D.segwitParsedOnce) { if (btchip_context_D.usingOverwinter) { cx_hash(&btchip_context_D.segwit.hash.hashPrevouts.blake2b.header, 0, btchip_context_D.transactionBufferPointer, 36, NULL, 0); } else { cx_hash( &btchip_context_D.segwit.hash.hashPrevouts .sha256.header, 0, btchip_context_D.transactionBufferPointer, 36, NULL, 0); } transaction_offset_increase(36); check_transaction_available(8); // update amount btchip_swap_bytes( amount, btchip_context_D.transactionBufferPointer, 8); if (transaction_amount_add_be( btchip_context_D.transactionContext .transactionAmount, btchip_context_D.transactionContext .transactionAmount, amount)) { PRINTF("Overflow\n"); goto fail; } PRINTF("Adding amount\n%.*H\n",8,btchip_context_D.transactionBufferPointer); PRINTF("New amount\n%.*H\n",8,btchip_context_D.transactionContext.transactionAmount); transaction_offset_increase(8); } else { btchip_context_D.transactionHashOption = TRANSACTION_HASH_FULL; transaction_offset_increase(36); btchip_context_D.transactionHashOption = 0; check_transaction_available(8); // save amount os_memmove( btchip_context_D.inputValue, btchip_context_D.transactionBufferPointer, 8); transaction_offset_increase(8); btchip_context_D.transactionHashOption = TRANSACTION_HASH_FULL; } } else if (!trustedInputFlag) { // Only authorized in relaxed wallet and server // modes SB_CHECK(N_btchip.bkp.config.operationMode); switch (SB_GET(N_btchip.bkp.config.operationMode)) { case BTCHIP_MODE_WALLET: if (!optionP2SHSkip2FA) { PRINTF("Untrusted input not authorized\n"); goto fail; } break; case BTCHIP_MODE_RELAXED_WALLET: case BTCHIP_MODE_SERVER: break; default: PRINTF("Untrusted input not authorized\n"); goto fail; } btchip_context_D.transactionBufferPointer++; btchip_context_D.transactionDataRemaining--; check_transaction_available( 36); // prevout : 32 hash + 4 index transaction_offset_increase(36); PRINTF("Marking relaxed input\n"); btchip_context_D.transactionContext.relaxed = 1; /* PRINTF("Clearing P2SH consumption\n"); btchip_context_D.transactionContext.consumeP2SH = 0; */ } else { trustedInputLength = *( btchip_context_D.transactionBufferPointer + 1); if ((trustedInputLength > sizeof(trustedInput)) || (trustedInputLength < 8)) { PRINTF("Invalid trusted input size\n"); goto fail; } check_transaction_available(2 + trustedInputLength); cx_hmac_sha256( N_btchip.bkp.trustedinput_key, sizeof(N_btchip.bkp.trustedinput_key), btchip_context_D.transactionBufferPointer + 2, trustedInputLength - 8, trustedInput, trustedInputLength); if (btchip_secure_memcmp( trustedInput, btchip_context_D.transactionBufferPointer + 2 + trustedInputLength - 8, 8) != 0) { PRINTF("Invalid signature\n"); goto fail; } os_memmove( trustedInput, btchip_context_D.transactionBufferPointer + 2, trustedInputLength - 8); if (trustedInput[0] != MAGIC_TRUSTED_INPUT) { PRINTF("Failed to verify trusted input signature\n"); goto fail; } // Update the hash with prevout data savePointer = btchip_context_D.transactionBufferPointer; /* // Check if a P2SH script is used if ((trustedInput[1] & FLAG_TRUSTED_INPUT_P2SH) == 0) { PRINTF("Clearing P2SH consumption\n"); btchip_context_D.transactionContext.consumeP2SH = 0; } */ btchip_context_D.transactionBufferPointer = trustedInput + 4; PRINTF("Trusted input hash\n%.*H\n",36,btchip_context_D.transactionBufferPointer); transaction_offset(36); btchip_context_D.transactionBufferPointer = savePointer + (2 + trustedInputLength); btchip_context_D.transactionDataRemaining -= (2 + trustedInputLength); // Update the amount btchip_swap_bytes(amount, trustedInput + 40, 8); if (transaction_amount_add_be( btchip_context_D.transactionContext .transactionAmount, btchip_context_D.transactionContext .transactionAmount, amount)) { PRINTF("Overflow\n"); goto fail; } PRINTF("Adding amount\n%.*H\n",8,(trustedInput + 40)); PRINTF("New amount\n%.*H\n",8,btchip_context_D.transactionContext.transactionAmount); } if (!btchip_context_D.usingSegwit) { // Do not include the input script length + value in // the authentication hash btchip_context_D.transactionHashOption = TRANSACTION_HASH_FULL; } } // Read the script length btchip_context_D.transactionContext.scriptRemaining = transaction_get_varint(); PRINTF("Script to read " DEBUG_LONG "\n",btchip_context_D.transactionContext.scriptRemaining); if ((parseMode == PARSE_MODE_SIGNATURE) && !trustedInputFlag && !btchip_context_D.usingSegwit) { // Only proceeds if this is not to be signed - so length // should be null if (btchip_context_D.transactionContext .scriptRemaining != 0) { PRINTF("Request to sign relaxed input\n"); if (!optionP2SHSkip2FA) { goto fail; } } } // Move on btchip_context_D.transactionContext.transactionState = BTCHIP_TRANSACTION_INPUT_HASHING_IN_PROGRESS_INPUT_SCRIPT; // no break is intentional } case BTCHIP_TRANSACTION_INPUT_HASHING_IN_PROGRESS_INPUT_SCRIPT: { unsigned char dataAvailable; PRINTF("Process input script, remaining " DEBUG_LONG "\n",btchip_context_D.transactionContext.scriptRemaining); if (btchip_context_D.transactionDataRemaining < 1) { // No more data to read, ok goto ok; } // Scan for P2SH consumption - huge shortcut, but fine // enough // Also usable in SegWit mode if (btchip_context_D.transactionContext.scriptRemaining == 1) { if (*btchip_context_D.transactionBufferPointer == OP_CHECKMULTISIG) { if (optionP2SHSkip2FA) { PRINTF("Marking P2SH consumption\n"); btchip_context_D.transactionContext .consumeP2SH = 1; } } else { // When using the P2SH shortcut, all inputs must use // P2SH PRINTF("Disabling P2SH consumption\n"); btchip_context_D.transactionContext.consumeP2SH = 0; } transaction_offset_increase(1); btchip_context_D.transactionContext.scriptRemaining--; } if (btchip_context_D.transactionContext.scriptRemaining == 0) { if (parseMode == PARSE_MODE_SIGNATURE) { if (!btchip_context_D.usingSegwit) { // Restore dual hash for signature + // authentication btchip_context_D.transactionHashOption = TRANSACTION_HASH_BOTH; } else { if (btchip_context_D.segwitParsedOnce) { // Append the saved value PRINTF("SEGWIT Add value\n%.*H\n",8,btchip_context_D.inputValue); if (btchip_context_D.usingOverwinter) { cx_hash(&btchip_context_D.transactionHashFull.blake2b.header, 0, btchip_context_D.inputValue, 8, NULL, 0); } else { cx_hash(&btchip_context_D .transactionHashFull.sha256.header, 0, btchip_context_D.inputValue, 8, NULL, 0); } } } } // Sequence check_transaction_available(4); if (btchip_context_D.usingSegwit && !btchip_context_D.segwitParsedOnce) { if (btchip_context_D.usingOverwinter) { cx_hash(&btchip_context_D.transactionHashFull.blake2b.header, 0, btchip_context_D.transactionBufferPointer, 4, NULL, 0); } else { cx_hash(&btchip_context_D.transactionHashFull .sha256.header, 0, btchip_context_D.transactionBufferPointer, 4, NULL, 0); } } transaction_offset_increase(4); // Move to next input btchip_context_D.transactionContext .transactionRemainingInputsOutputs--; btchip_context_D.transactionContext .transactionCurrentInputOutput++; btchip_context_D.transactionContext.transactionState = BTCHIP_TRANSACTION_DEFINED_WAIT_INPUT; continue; } // Save the last script byte for the P2SH check dataAvailable = (btchip_context_D.transactionDataRemaining > btchip_context_D.transactionContext .scriptRemaining - 1 ? btchip_context_D.transactionContext .scriptRemaining - 1 : btchip_context_D.transactionDataRemaining); if (dataAvailable == 0) { goto ok; } transaction_offset_increase(dataAvailable); btchip_context_D.transactionContext.scriptRemaining -= dataAvailable; break; } case BTCHIP_TRANSACTION_INPUT_HASHING_DONE: { PRINTF("Input hashing done\n"); if (parseMode == PARSE_MODE_SIGNATURE) { // inputs have been prepared, stop the parsing here if (btchip_context_D.usingSegwit && !btchip_context_D.segwitParsedOnce) { unsigned char hashedPrevouts[32]; unsigned char hashedSequence[32]; // Flush the cache if (btchip_context_D.usingOverwinter) { cx_hash(&btchip_context_D.segwit.hash.hashPrevouts.blake2b.header, CX_LAST, hashedPrevouts, 0, hashedPrevouts, 32); cx_hash(&btchip_context_D.transactionHashFull.blake2b.header, CX_LAST, hashedSequence, 0, hashedSequence, 32); } else { cx_hash(&btchip_context_D.segwit.hash.hashPrevouts .sha256.header, CX_LAST, hashedPrevouts, 0, hashedPrevouts, 32); cx_sha256_init( &btchip_context_D.segwit.hash.hashPrevouts.sha256); cx_hash(&btchip_context_D.segwit.hash.hashPrevouts .sha256.header, CX_LAST, hashedPrevouts, sizeof(hashedPrevouts), hashedPrevouts, 32); cx_hash(&btchip_context_D.transactionHashFull .sha256.header, CX_LAST, hashedSequence, 0, hashedSequence, 32); cx_sha256_init( &btchip_context_D.transactionHashFull.sha256); cx_hash(&btchip_context_D.transactionHashFull .sha256.header, CX_LAST, hashedSequence, sizeof(hashedSequence), hashedSequence, 32); } os_memmove( btchip_context_D.segwit.cache.hashedPrevouts, hashedPrevouts, sizeof(hashedPrevouts)); os_memmove( btchip_context_D.segwit.cache.hashedSequence, hashedSequence, sizeof(hashedSequence)); PRINTF("hashPrevout\n%.*H\n",32,btchip_context_D.segwit.cache.hashedPrevouts); PRINTF("hashSequence\n%.*H\n",32,btchip_context_D.segwit.cache.hashedSequence); } if (btchip_context_D.usingSegwit && btchip_context_D.segwitParsedOnce) { if (!btchip_context_D.usingOverwinter) { PRINTF("SEGWIT hashedOutputs\n%.*H\n",sizeof(btchip_context_D.segwit.cache.hashedOutputs),btchip_context_D.segwit.cache.hashedOutputs); cx_hash( &btchip_context_D.transactionHashFull.sha256.header, 0, btchip_context_D.segwit.cache.hashedOutputs, sizeof(btchip_context_D.segwit.cache .hashedOutputs), NULL, 0); } btchip_context_D.transactionContext .transactionState = BTCHIP_TRANSACTION_SIGN_READY; } else { btchip_context_D.transactionContext .transactionState = BTCHIP_TRANSACTION_PRESIGN_READY; if (btchip_context_D.usingOverwinter) { cx_blake2b_init2(&btchip_context_D.transactionHashFull.blake2b, 256, NULL, 0, OVERWINTER_PARAM_OUTPUTS, 16); } else if (btchip_context_D.usingSegwit) { cx_sha256_init(&btchip_context_D.transactionHashFull.sha256); } } continue; } if (btchip_context_D.transactionDataRemaining < 1) { // No more data to read, ok goto ok; } // Number of outputs btchip_context_D.transactionContext .transactionRemainingInputsOutputs = transaction_get_varint(); btchip_context_D.transactionContext .transactionCurrentInputOutput = 0; PRINTF("Number of outputs : " DEBUG_LONG "\n", btchip_context_D.transactionContext .transactionRemainingInputsOutputs); // Ready to proceed btchip_context_D.transactionContext.transactionState = BTCHIP_TRANSACTION_DEFINED_WAIT_OUTPUT; // no break is intentional } case BTCHIP_TRANSACTION_DEFINED_WAIT_OUTPUT: { if (btchip_context_D.transactionContext .transactionRemainingInputsOutputs == 0) { // No more outputs to hash, move forward btchip_context_D.transactionContext.transactionState = BTCHIP_TRANSACTION_OUTPUT_HASHING_DONE; continue; } if (btchip_context_D.transactionDataRemaining < 1) { // No more data to read, ok goto ok; } // Amount check_transaction_available(8); if ((parseMode == PARSE_MODE_TRUSTED_INPUT) && (btchip_context_D.transactionContext .transactionCurrentInputOutput == btchip_context_D.transactionTargetInput)) { // Save the amount os_memmove(btchip_context_D.transactionContext .transactionAmount, btchip_context_D.transactionBufferPointer, 8); btchip_context_D.trustedInputProcessed = 1; } transaction_offset_increase(8); // Read the script length btchip_context_D.transactionContext.scriptRemaining = transaction_get_varint(); PRINTF("Script to read " DEBUG_LONG "\n",btchip_context_D.transactionContext.scriptRemaining); // Move on btchip_context_D.transactionContext.transactionState = BTCHIP_TRANSACTION_OUTPUT_HASHING_IN_PROGRESS_OUTPUT_SCRIPT; // no break is intentional } case BTCHIP_TRANSACTION_OUTPUT_HASHING_IN_PROGRESS_OUTPUT_SCRIPT: { unsigned char dataAvailable; PRINTF("Process output script, remaining " DEBUG_LONG "\n",btchip_context_D.transactionContext.scriptRemaining); /* // Special check if consuming a P2SH script if (parseMode == PARSE_MODE_TRUSTED_INPUT) { // Assume the full input script is sent in a single APDU, then do the ghetto validation if ((btchip_context_D.transactionBufferPointer[0] == OP_HASH160) && (btchip_context_D.transactionBufferPointer[btchip_context_D.transactionDataRemaining - 1] == OP_EQUAL)) { PRINTF("Marking P2SH output\n"); btchip_context_D.transactionContext.consumeP2SH = 1; } } */ if (btchip_context_D.transactionDataRemaining < 1) { // No more data to read, ok goto ok; } if (btchip_context_D.transactionContext.scriptRemaining == 0) { // Move to next output btchip_context_D.transactionContext .transactionRemainingInputsOutputs--; btchip_context_D.transactionContext .transactionCurrentInputOutput++; btchip_context_D.transactionContext.transactionState = BTCHIP_TRANSACTION_DEFINED_WAIT_OUTPUT; continue; } dataAvailable = (btchip_context_D.transactionDataRemaining > btchip_context_D.transactionContext .scriptRemaining ? btchip_context_D.transactionContext .scriptRemaining : btchip_context_D.transactionDataRemaining); if (dataAvailable == 0) { goto ok; } transaction_offset_increase(dataAvailable); btchip_context_D.transactionContext.scriptRemaining -= dataAvailable; break; } case BTCHIP_TRANSACTION_OUTPUT_HASHING_DONE: { PRINTF("Output hashing done\n"); if (btchip_context_D.transactionDataRemaining < 1) { // No more data to read, ok goto ok; } // Locktime check_transaction_available(4); transaction_offset_increase(4); if (btchip_context_D.transactionDataRemaining == 0) { btchip_context_D.transactionContext.transactionState = BTCHIP_TRANSACTION_PARSED; continue; } else { btchip_context_D.transactionHashOption = 0; btchip_context_D.transactionContext.scriptRemaining = transaction_get_varint(); btchip_context_D.transactionHashOption = TRANSACTION_HASH_FULL; btchip_context_D.transactionContext.transactionState = BTCHIP_TRANSACTION_PROCESS_EXTRA; continue; } } case BTCHIP_TRANSACTION_PROCESS_EXTRA: { unsigned char dataAvailable; if (btchip_context_D.transactionContext.scriptRemaining == 0) { btchip_context_D.transactionContext.transactionState = BTCHIP_TRANSACTION_PARSED; continue; } if (btchip_context_D.transactionDataRemaining < 1) { // No more data to read, ok goto ok; } dataAvailable = (btchip_context_D.transactionDataRemaining > btchip_context_D.transactionContext .scriptRemaining ? btchip_context_D.transactionContext .scriptRemaining : btchip_context_D.transactionDataRemaining); if (dataAvailable == 0) { goto ok; } transaction_offset_increase(dataAvailable); btchip_context_D.transactionContext.scriptRemaining -= dataAvailable; break; } case BTCHIP_TRANSACTION_PARSED: { PRINTF("Transaction parsed\n"); goto ok; } case BTCHIP_TRANSACTION_PRESIGN_READY: { PRINTF("Presign ready\n"); goto ok; } case BTCHIP_TRANSACTION_SIGN_READY: { PRINTF("Sign ready\n"); goto ok; } } } fail: PRINTF("Transaction parse - fail\n"); THROW(EXCEPTION); ok : {} } CATCH_OTHER(e) { PRINTF("Transaction parse - surprise fail\n"); btchip_context_D.transactionContext.transactionState = BTCHIP_TRANSACTION_NONE; btchip_set_check_internal_structure_integrity(1); THROW(e); } // before the finally to restore the surrounding context if an exception // is raised during finally FINALLY { btchip_set_check_internal_structure_integrity(1); } } END_TRY; }