LCOV - code coverage report
Current view: top level - powhsm/src - auth_tx.c (source / functions) Hit Total Coverage
Test: powHSM firmware Lines: 153 172 89.0 %
Date: 2025-07-10 13:49:13 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /**
       2             :  * The MIT License (MIT)
       3             :  *
       4             :  * Copyright (c) 2021 RSK Labs Ltd
       5             :  *
       6             :  * Permission is hereby granted, free of charge, to any person obtaining a copy
       7             :  * of this software and associated documentation files (the "Software"), to
       8             :  * deal in the Software without restriction, including without limitation the
       9             :  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
      10             :  * sell copies of the Software, and to permit persons to whom the Software is
      11             :  * furnished to do so, subject to the following conditions:
      12             :  *
      13             :  * The above copyright notice and this permission notice shall be included in
      14             :  * all copies or substantial portions of the Software.
      15             :  *
      16             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      17             :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      18             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      19             :  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      20             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      21             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
      22             :  * IN THE SOFTWARE.
      23             :  */
      24             : 
      25             : #include "hal/hash.h"
      26             : #include "hal/platform.h"
      27             : #include "hal/exceptions.h"
      28             : 
      29             : #include "auth.h"
      30             : #include "svarint.h"
      31             : #include "mem.h"
      32             : #include "memutil.h"
      33             : 
      34             : #include "hal/log.h"
      35             : 
      36             : // IMPORTANT: This callback only executes for the scriptSig at the desired input
      37             : // (the one that is requested to sign)
      38             : // In all the other cases the parser is not even initialized or called,
      39             : // so it is not possible for this callback to execute.
      40        8804 : static void btcscript_cb(const btcscript_cb_event_t event) {
      41             :     uint8_t redeemscript_length_size;
      42             :     uint8_t redeemscript_length[MAX_SVARINT_ENCODING_SIZE];
      43             : 
      44        8804 :     if (event == BTCSCRIPT_EV_OPCODE && auth.tx.script_ctx.operand_size > 0 &&
      45          58 :         auth.tx.script_ctx.operand_size == auth.tx.script_ctx.bytes_remaining) {
      46             :         // This is a push of exactly the remaining script bytes => this is the
      47             :         // last push of the script => this is the redeemScript
      48          40 :         auth.tx.redeemscript_found = 1;
      49             :         // Hash the script size using the size of the operand (redeemScript)
      50             :         redeemscript_length_size =
      51          40 :             svarint_encode(auth.tx.script_ctx.operand_size,
      52             :                            redeemscript_length,
      53             :                            sizeof(redeemscript_length));
      54          40 :         hash_sha256_update(&auth.tx.sig_hash_ctx,
      55             :                            redeemscript_length,
      56             :                            redeemscript_length_size);
      57        8764 :     } else if (event == BTCSCRIPT_EV_OPERAND && auth.tx.redeemscript_found) {
      58        6862 :         hash_sha256_update(&auth.tx.sig_hash_ctx,
      59             :                            &auth.tx.script_ctx.operand_byte,
      60             :                            sizeof(auth.tx.script_ctx.operand_byte));
      61             :     }
      62        8804 : }
      63             : 
      64       88518 : static void btctx_cb(const btctx_cb_event_t event) {
      65             :     // Update txhash
      66       88518 :     hash_sha256_update(
      67       88518 :         &auth.tx.tx_hash_ctx, auth.tx.ctx.raw, auth.tx.ctx.raw_size);
      68             : 
      69             :     // The bridge currently only generates pegout transactions with
      70             :     // versions 1 or 2. Validate that.
      71       88518 :     if (event == BTCTX_EV_VERSION && auth.tx.ctx.parsed.version != 1 &&
      72          16 :         auth.tx.ctx.parsed.version != 2) {
      73           2 :         LOG("[E] Unsupported TX Version: %u\n", auth.tx.ctx.parsed.version);
      74           2 :         THROW(ERR_AUTH_INVALID_TX_VERSION);
      75             :     }
      76             : 
      77             :     // Validate that the input index to sign is valid
      78       88516 :     if (event == BTCTX_EV_VIN_COUNT &&
      79          44 :         auth.input_index_to_sign >= auth.tx.ctx.parsed.varint.value) {
      80           0 :         LOG("[E] Input index to sign > number of inputs.\n");
      81           0 :         THROW(ERR_AUTH_INVALID_TX_INPUT_INDEX);
      82             :     }
      83             : 
      84             :     // Update sighash
      85       88516 :     if (event == BTCTX_EV_VIN_SLENGTH) {
      86         554 :         if (auth.tx.ctx.inout_current == auth.input_index_to_sign) {
      87             :             // Parse this scriptSig
      88          44 :             auth.tx.redeemscript_found = 0;
      89          44 :             btcscript_init(&auth.tx.script_ctx,
      90             :                            &btcscript_cb,
      91             :                            auth.tx.ctx.parsed.varint.value);
      92             :         } else {
      93             :             // All other scriptSigs get replaced by an empty scriptSig
      94             :             // when calculating the sigHash
      95         510 :             hash_sha256_update(&auth.tx.sig_hash_ctx, (uint8_t[]){0x00}, 1);
      96             :         }
      97       87962 :     } else if (event == BTCTX_EV_VIN_SCRIPT_DATA &&
      98       65716 :                auth.tx.ctx.inout_current == auth.input_index_to_sign) {
      99        8804 :         if (btcscript_consume(auth.tx.ctx.raw, auth.tx.ctx.raw_size) !=
     100        8804 :             auth.tx.ctx.raw_size) {
     101           0 :             LOG("[E] Expected to consume %u bytes from the script but didn't",
     102           0 :                 auth.tx.ctx.raw_size);
     103           0 :             THROW(ERR_AUTH_TX_HASH_MISMATCH);
     104             :         }
     105             : 
     106        8804 :         if (btcscript_result() < 0) {
     107           2 :             LOG("[E] Error %u parsing the scriptSig", btcscript_result());
     108           2 :             THROW(ERR_AUTH_TX_HASH_MISMATCH);
     109             :         }
     110             : 
     111        8802 :         if (auth.tx.ctx.script_remaining == 0) {
     112          40 :             if (btcscript_result() != BTCSCRIPT_ST_DONE) {
     113           0 :                 LOG("[E] No more scriptSig bytes to parse but "
     114             :                     "the script parser isn't finished");
     115           0 :                 THROW(ERR_AUTH_TX_HASH_MISMATCH);
     116             :             }
     117          40 :             if (!auth.tx.redeemscript_found) {
     118           0 :                 LOG("[E] Finished parsing the scriptSig "
     119             :                     "but the redeemScript was not found");
     120           0 :                 THROW(ERR_AUTH_TX_HASH_MISMATCH);
     121             :             }
     122             :         }
     123       79158 :     } else if (event != BTCTX_EV_VIN_SCRIPT_DATA) {
     124       22246 :         hash_sha256_update(
     125       22246 :             &auth.tx.sig_hash_ctx, auth.tx.ctx.raw, auth.tx.ctx.raw_size);
     126             :     }
     127       88514 : }
     128             : 
     129       76204 : static void btctx_cb_segwit(const btctx_cb_event_t event) {
     130             :     // Update txhash
     131       76204 :     hash_sha256_update(
     132       76204 :         &auth.tx.tx_hash_ctx, auth.tx.ctx.raw, auth.tx.ctx.raw_size);
     133             : 
     134       76204 :     if (event == BTCTX_EV_VERSION) {
     135          12 :         hash_sha256_update(
     136          12 :             &auth.tx.sig_hash_ctx, auth.tx.ctx.raw, auth.tx.ctx.raw_size);
     137             :     }
     138             : 
     139       76204 :     if (event == BTCTX_EV_VIN_COUNT) {
     140          12 :         hash_sha256_init(&auth.tx.prevouts_hash_ctx);
     141          12 :         hash_sha256_init(&auth.tx.sequence_hash_ctx);
     142          12 :         auth.tx.aux_offset = 0;
     143             :     }
     144             : 
     145       76204 :     if (event == BTCTX_EV_VIN_TXH_DATA || event == BTCTX_EV_VIN_TXIX) {
     146       17160 :         hash_sha256_update(
     147       17160 :             &auth.tx.prevouts_hash_ctx, auth.tx.ctx.raw, auth.tx.ctx.raw_size);
     148             : 
     149       17160 :         if (auth.tx.ctx.inout_current == auth.input_index_to_sign) {
     150         792 :             SAFE_MEMMOVE(auth.tx.ip_prevout,
     151             :                          sizeof(auth.tx.ip_prevout),
     152             :                          auth.tx.aux_offset,
     153             :                          auth.tx.ctx.raw,
     154             :                          sizeof(auth.tx.ctx.raw),
     155             :                          MEMMOVE_ZERO_OFFSET,
     156             :                          auth.tx.ctx.raw_size,
     157             :                          THROW(ERR_AUTH_INVALID_DATA_SIZE));
     158         396 :             auth.tx.aux_offset += auth.tx.ctx.raw_size;
     159             :         }
     160             :     }
     161             : 
     162       76204 :     if (event == BTCTX_EV_VIN_SEQNO) {
     163         520 :         hash_sha256_update(
     164         520 :             &auth.tx.sequence_hash_ctx, auth.tx.ctx.raw, auth.tx.ctx.raw_size);
     165             : 
     166         520 :         if (auth.tx.ctx.inout_current == auth.input_index_to_sign) {
     167          24 :             SAFE_MEMMOVE(auth.tx.ip_seqno,
     168             :                          sizeof(auth.tx.ip_seqno),
     169             :                          MEMMOVE_ZERO_OFFSET,
     170             :                          auth.tx.ctx.raw,
     171             :                          sizeof(auth.tx.ctx.raw),
     172             :                          MEMMOVE_ZERO_OFFSET,
     173             :                          auth.tx.ctx.raw_size,
     174             :                          THROW(ERR_AUTH_INVALID_DATA_SIZE));
     175             :         }
     176             :     }
     177             : 
     178       76204 :     if (event == BTCTX_EV_VOUT_COUNT) {
     179          12 :         hash_sha256_final(&auth.tx.prevouts_hash_ctx, auth.tx.aux_hash);
     180          12 :         hash_sha256_init(&auth.tx.prevouts_hash_ctx);
     181          12 :         hash_sha256_update(&auth.tx.prevouts_hash_ctx,
     182             :                            auth.tx.aux_hash,
     183             :                            sizeof(auth.tx.aux_hash));
     184          12 :         hash_sha256_final(&auth.tx.prevouts_hash_ctx, auth.tx.aux_hash);
     185          12 :         hash_sha256_update(
     186             :             &auth.tx.sig_hash_ctx, auth.tx.aux_hash, sizeof(auth.tx.aux_hash));
     187             : 
     188          12 :         hash_sha256_final(&auth.tx.sequence_hash_ctx, auth.tx.aux_hash);
     189          12 :         hash_sha256_init(&auth.tx.sequence_hash_ctx);
     190          12 :         hash_sha256_update(&auth.tx.sequence_hash_ctx,
     191             :                            auth.tx.aux_hash,
     192             :                            sizeof(auth.tx.aux_hash));
     193          12 :         hash_sha256_final(&auth.tx.sequence_hash_ctx, auth.tx.aux_hash);
     194          12 :         hash_sha256_update(
     195             :             &auth.tx.sig_hash_ctx, auth.tx.aux_hash, sizeof(auth.tx.aux_hash));
     196             : 
     197             :         // Previously saved outpoint of input to sign
     198          12 :         hash_sha256_update(&auth.tx.sig_hash_ctx,
     199             :                            auth.tx.ip_prevout,
     200             :                            sizeof(auth.tx.ip_prevout));
     201             : 
     202          12 :         hash_sha256_init(&auth.tx.outputs_hash_ctx);
     203             :     }
     204             : 
     205       76204 :     if (event == BTCTX_EV_VOUT_VALUE || event == BTCTX_EV_VOUT_SLENGTH ||
     206             :         event == BTCTX_EV_VOUT_SCRIPT_DATA) {
     207         640 :         hash_sha256_update(
     208         640 :             &auth.tx.outputs_hash_ctx, auth.tx.ctx.raw, auth.tx.ctx.raw_size);
     209             :     }
     210             : 
     211       76204 :     if (event == BTCTX_EV_LOCKTIME) {
     212          12 :         hash_sha256_final(&auth.tx.outputs_hash_ctx, auth.tx.outputs_hash);
     213          12 :         hash_sha256_init(&auth.tx.outputs_hash_ctx);
     214          12 :         hash_sha256_update(&auth.tx.outputs_hash_ctx,
     215             :                            auth.tx.outputs_hash,
     216             :                            sizeof(auth.tx.outputs_hash));
     217          12 :         hash_sha256_final(&auth.tx.outputs_hash_ctx, auth.tx.outputs_hash);
     218             : 
     219          24 :         SAFE_MEMMOVE(auth.tx.lock_time,
     220             :                      sizeof(auth.tx.lock_time),
     221             :                      MEMMOVE_ZERO_OFFSET,
     222             :                      auth.tx.ctx.raw,
     223             :                      sizeof(auth.tx.ctx.raw),
     224             :                      MEMMOVE_ZERO_OFFSET,
     225             :                      auth.tx.ctx.raw_size,
     226             :                      THROW(ERR_AUTH_INVALID_DATA_SIZE));
     227             :     }
     228       76204 : }
     229             : 
     230             : /*
     231             :  * Implement the BTC tx parsing and calculations portion
     232             :  * of the signing authorization protocol.
     233             :  *
     234             :  * @arg[in] rx      number of received bytes from the host
     235             :  * @ret             number of transmited bytes to the host
     236             :  */
     237        2184 : unsigned int auth_sign_handle_btctx(volatile unsigned int rx) {
     238        2184 :     uint8_t apdu_offset = 0;
     239             : 
     240        2184 :     if (auth.state != STATE_AUTH_BTCTX) {
     241           0 :         LOG("[E] Expected to be in the BTC tx state\n");
     242           0 :         THROW(ERR_AUTH_INVALID_STATE);
     243             :     }
     244             : 
     245        2184 :     if (!auth.tx.segwit_processing_extradata) {
     246             :         // Read little endian TX length
     247             :         // (part of the legacy protocol, includes this length)
     248        2146 :         if (auth.tx.remaining_bytes == 0) {
     249         290 :             for (uint8_t i = 0; i < BTCTX_LENGTH_SIZE; i++) {
     250         232 :                 auth.tx.remaining_bytes += APDU_DATA_PTR[i] << (8 * i);
     251             :             }
     252             :             // BTC tx length includes the length of the length
     253             :             // and the length of the sighash computation mode and
     254             :             // extradata length
     255          58 :             auth.tx.remaining_bytes -=
     256             :                 BTCTX_LENGTH_SIZE + SIGHASH_COMP_MODE_SIZE + EXTRADATA_SIZE;
     257             :             // Init both hash operations
     258          58 :             hash_sha256_init(&auth.tx.tx_hash_ctx);
     259          58 :             hash_sha256_init(&auth.tx.sig_hash_ctx);
     260          58 :             apdu_offset = BTCTX_LENGTH_SIZE;
     261             :             // Following three bytes indicate the sighash computation
     262             :             // mode (1 byte) and extradata size (2 bytes LE, for segwit)
     263          58 :             auth.tx.sighash_computation_mode = APDU_DATA_PTR[apdu_offset++];
     264          58 :             auth.tx.segwit_processing_extradata = false;
     265          58 :             auth.tx.segwit_extradata_size = 0;
     266          58 :             auth.tx.segwit_extradata_size += APDU_DATA_PTR[apdu_offset++];
     267          58 :             auth.tx.segwit_extradata_size += APDU_DATA_PTR[apdu_offset++] << 8;
     268             :             // Validate computation mode and init tx parsing context
     269          58 :             switch (auth.tx.sighash_computation_mode) {
     270          46 :             case SIGHASH_COMPUTE_MODE_LEGACY:
     271          46 :                 btctx_init(&auth.tx.ctx, &btctx_cb);
     272          46 :                 break;
     273          12 :             case SIGHASH_COMPUTE_MODE_SEGWIT:
     274          12 :                 btctx_init(&auth.tx.ctx, &btctx_cb_segwit);
     275          12 :                 if (!auth.tx.segwit_extradata_size) {
     276           0 :                     LOG("[E] Invalid extradata size for segwit");
     277           0 :                     THROW(ERR_AUTH_INVALID_EXTRADATA_SIZE);
     278             :                 }
     279          12 :                 break;
     280           0 :             default:
     281           0 :                 LOG("[E] Invalid sighash computation mode\n");
     282           0 :                 THROW(ERR_AUTH_INVALID_SIGHASH_COMPUTATION_MODE);
     283             :             }
     284             :         }
     285             : 
     286        6434 :         auth.tx.remaining_bytes -= btctx_consume(
     287        2146 :             APDU_DATA_PTR + apdu_offset, APDU_DATA_SIZE(rx) - apdu_offset);
     288             : 
     289        2142 :         if (btctx_result() < 0) {
     290           0 :             LOG("[E] Error parsing BTC tx: %d\n", btctx_result());
     291             :             // To comply with the legacy implementation
     292           0 :             THROW(ERR_AUTH_TX_HASH_MISMATCH);
     293             :         }
     294             : 
     295        2142 :         if (btctx_result() == BTCTX_ST_DONE) {
     296          52 :             if (auth.tx.remaining_bytes > 0) {
     297           2 :                 LOG("[E] Error parsing BTC tx: more bytes reported "
     298             :                     "than actual tx bytes\n");
     299             :                 // To comply with the legacy implementation
     300           2 :                 THROW(ERR_AUTH_INVALID_DATA_SIZE);
     301             :             }
     302             : 
     303             :             // Finalize TX hash computation
     304          50 :             hash_sha256_final(&auth.tx.tx_hash_ctx, auth.tx_hash);
     305          50 :             hash_sha256_init(&auth.tx.tx_hash_ctx);
     306          50 :             hash_sha256_update(&auth.tx.tx_hash_ctx, auth.tx_hash, 32);
     307          50 :             hash_sha256_final(&auth.tx.tx_hash_ctx, auth.tx_hash);
     308         850 :             for (int j = 0; j < 16; j++) {
     309         800 :                 uint8_t aux = auth.tx_hash[j];
     310         800 :                 auth.tx_hash[j] = auth.tx_hash[31 - j];
     311         800 :                 auth.tx_hash[31 - j] = aux;
     312             :             }
     313             : 
     314             :             // Segwit?
     315          50 :             if (auth.tx.sighash_computation_mode ==
     316             :                 SIGHASH_COMPUTE_MODE_SEGWIT) {
     317          12 :                 auth.tx.segwit_processing_extradata = true;
     318          12 :                 auth.tx.remaining_bytes =
     319          12 :                     (uint32_t)auth.tx.segwit_extradata_size;
     320             :             } else {
     321          38 :                 auth.tx.finalise = true;
     322             :             }
     323             :         }
     324             :     } else {
     325             :         // Hash extradata
     326           0 :         hash_sha256_update(
     327          38 :             &auth.tx.sig_hash_ctx, APDU_DATA_PTR, APDU_DATA_SIZE(rx));
     328          38 :         auth.tx.remaining_bytes -= APDU_DATA_SIZE(rx);
     329          38 :         if (auth.tx.remaining_bytes == 0) {
     330          12 :             auth.tx.finalise = true;
     331             :         }
     332             :     }
     333             : 
     334        2178 :     if (auth.tx.finalise) {
     335          50 :         if (auth.tx.sighash_computation_mode == SIGHASH_COMPUTE_MODE_SEGWIT) {
     336             :             // Remaining tx items to hash for segwit
     337          12 :             hash_sha256_update(&auth.tx.sig_hash_ctx,
     338             :                                auth.tx.ip_seqno,
     339             :                                sizeof(auth.tx.ip_seqno));
     340          12 :             hash_sha256_update(&auth.tx.sig_hash_ctx,
     341             :                                auth.tx.outputs_hash,
     342             :                                sizeof(auth.tx.outputs_hash));
     343          12 :             hash_sha256_update(&auth.tx.sig_hash_ctx,
     344             :                                auth.tx.lock_time,
     345             :                                sizeof(auth.tx.lock_time));
     346             :         }
     347             : 
     348             :         // Add SIGHASH_ALL hash type at the end
     349          50 :         hash_sha256_update(&auth.tx.sig_hash_ctx,
     350          50 :                            (uint8_t[])SIGHASH_ALL_BYTES,
     351             :                            sizeof(SIGHASH_ALL_SIZE));
     352          50 :         hash_sha256_final(&auth.tx.sig_hash_ctx, auth.sig_hash);
     353             : 
     354          50 :         hash_sha256_init(&auth.tx.sig_hash_ctx);
     355          50 :         hash_sha256_update(&auth.tx.sig_hash_ctx, auth.sig_hash, 32);
     356          50 :         hash_sha256_final(&auth.tx.sig_hash_ctx, auth.sig_hash);
     357             : 
     358             :         // Log hashes for debugging purposes
     359          50 :         LOG_HEX("TX hash:     ", auth.tx_hash, sizeof(auth.tx_hash));
     360          50 :         LOG_HEX("TX sig hash: ", auth.sig_hash, sizeof(auth.sig_hash));
     361             : 
     362             :         // Request RSK transaction receipt
     363          50 :         SET_APDU_OP(P1_RECEIPT);
     364          50 :         SET_APDU_TXLEN(AUTH_MAX_EXCHANGE_SIZE);
     365          50 :         auth.expected_bytes = APDU_TXLEN();
     366          50 :         auth_transition_to(STATE_AUTH_RECEIPT);
     367          50 :         return TX_FOR_TXLEN();
     368             :     }
     369             : 
     370        2128 :     if (auth.tx.remaining_bytes == 0) {
     371           2 :         LOG("[E] Error parsing BTC tx: no more bytes should "
     372             :             "remain but haven't finished parsing\n");
     373             :         // To comply with the legacy implementation
     374           2 :         THROW(ERR_AUTH_TX_HASH_MISMATCH);
     375             :     }
     376             : 
     377        2126 :     SET_APDU_TXLEN(MIN(auth.tx.remaining_bytes, AUTH_MAX_EXCHANGE_SIZE));
     378        2126 :     auth.expected_bytes = APDU_TXLEN();
     379        2126 :     return TX_FOR_TXLEN();
     380             : }

Generated by: LCOV version 1.16