LCOV - code coverage report
Current view: top level - ledger/ui/src - ui_heartbeat.c (source / functions) Hit Total Coverage
Test: powHSM firmware Lines: 79 85 92.9 %
Date: 2025-07-10 13:49:13 Functions: 5 6 83.3 %

          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 <string.h>
      26             : 
      27             : #include "runtime.h"
      28             : #include "ui_heartbeat.h"
      29             : #include "apdu.h"
      30             : #include "ui_instructions.h"
      31             : #include "ints.h"
      32             : #include "ui_err.h"
      33             : #include "ui_comm.h"
      34             : #include "memutil.h"
      35             : #include "compiletime.h"
      36             : #include "signer_authorization_status.h"
      37             : 
      38             : // Heartbeat init alias
      39             : #define reset_ui_heartbeat(ctx) ui_heartbeat_init(ctx)
      40             : 
      41             : /**
      42             :  * Initialize the heartbeat context
      43             :  *
      44             :  * @arg[in] ui_heartbeat_ctx the UI heartbeat context
      45             :  */
      46          20 : void ui_heartbeat_init(ui_heartbeat_t *ui_heartbeat_ctx) {
      47             :     COMPILE_TIME_ASSERT(MAX_UI_HEARTBEAT_MESSAGE_SIZE <=
      48             :                         APDU_TOTAL_DATA_SIZE_OUT);
      49             : 
      50          20 :     explicit_bzero(ui_heartbeat_ctx, sizeof(ui_heartbeat_t));
      51          20 :     ui_heartbeat_ctx->state = STATE_UI_HEARTBEAT_WAIT_UD_VALUE;
      52          20 : }
      53             : 
      54             : static ui_heartbeat_t *current_context;
      55             : 
      56           0 : static void reset_state() {
      57           0 :     reset_ui_heartbeat(current_context);
      58           0 : }
      59             : 
      60             : /*
      61             :  * Check that the SM for the heartbeat generation
      62             :  * matches the expected state.
      63             :  *
      64             :  * Reset the state and throw a protocol error
      65             :  * otherwise.
      66             :  *
      67             :  * @arg[in] ui_heartbeat_ctx the UI heartbeat context
      68             :  */
      69           4 : static void check_state(ui_heartbeat_t *ui_heartbeat_ctx,
      70             :                         state_ui_heartbeat_t expected) {
      71           4 :     if (ui_heartbeat_ctx->state != expected) {
      72           2 :         reset_ui_heartbeat(ui_heartbeat_ctx);
      73           2 :         THROW(ERR_UI_HBT_PROT_INVALID);
      74             :     }
      75           2 : }
      76             : 
      77             : // -----------------------------------------------------------------------
      78             : // Protocol implementation
      79             : // -----------------------------------------------------------------------
      80             : 
      81             : /**
      82             :  * Implement the heartbeat protocol.
      83             :  *
      84             :  * @arg[in] rx               number of received bytes from the Host
      85             :  * @arg[in] ui_heartbeat_ctx the UI heartbeat context
      86             :  * @ret                      number of transmited bytes to the host
      87             :  */
      88           9 : unsigned int get_ui_heartbeat(ui_heartbeat_t *ui_heartbeat_ctx,
      89             :                               volatile unsigned int rx) {
      90           9 :     switch (APDU_OP()) {
      91           2 :     case OP_UI_HBT_UD_VALUE:
      92             :         // Should receive a user-defined value
      93           2 :         if (APDU_DATA_SIZE(rx) != UD_VALUE_SIZE) {
      94           1 :             reset_ui_heartbeat(ui_heartbeat_ctx);
      95           1 :             THROW(ERR_UI_HBT_PROT_INVALID);
      96             :         }
      97             : 
      98           1 :         sigaut_signer_t *current_signer_info = get_authorized_signer_info();
      99             : 
     100             :         // Initialize message
     101           1 :         explicit_bzero(ui_heartbeat_ctx->msg, sizeof(ui_heartbeat_ctx->msg));
     102           1 :         ui_heartbeat_ctx->msg_offset = 0;
     103             : 
     104             :         // Copy the message prefix
     105           2 :         SAFE_MEMMOVE(ui_heartbeat_ctx->msg,
     106             :                      sizeof(ui_heartbeat_ctx->msg),
     107             :                      ui_heartbeat_ctx->msg_offset,
     108             :                      (void *)PIC(UI_HEARTBEAT_MSG_PREFIX),
     109             :                      UI_HEARTBEAT_MSG_PREFIX_LENGTH,
     110             :                      MEMMOVE_ZERO_OFFSET,
     111             :                      UI_HEARTBEAT_MSG_PREFIX_LENGTH,
     112             :                      THROW(ERR_UI_INTERNAL));
     113           1 :         ui_heartbeat_ctx->msg_offset += UI_HEARTBEAT_MSG_PREFIX_LENGTH;
     114             : 
     115             :         // Copy the UD value from the APDU
     116           2 :         SAFE_MEMMOVE(ui_heartbeat_ctx->msg,
     117             :                      sizeof(ui_heartbeat_ctx->msg),
     118             :                      ui_heartbeat_ctx->msg_offset,
     119             :                      APDU_DATA_PTR,
     120             :                      APDU_TOTAL_DATA_SIZE,
     121             :                      MEMMOVE_ZERO_OFFSET,
     122             :                      UD_VALUE_SIZE,
     123             :                      THROW(ERR_UI_INTERNAL));
     124           1 :         ui_heartbeat_ctx->msg_offset += UD_VALUE_SIZE;
     125             : 
     126             :         // Copy signer hash and iteration into the message space
     127           2 :         SAFE_MEMMOVE(ui_heartbeat_ctx->msg,
     128             :                      sizeof(ui_heartbeat_ctx->msg),
     129             :                      ui_heartbeat_ctx->msg_offset,
     130             :                      current_signer_info->hash,
     131             :                      sizeof(current_signer_info->hash),
     132             :                      MEMMOVE_ZERO_OFFSET,
     133             :                      sizeof(current_signer_info->hash),
     134             :                      THROW(ERR_UI_INTERNAL));
     135           1 :         ui_heartbeat_ctx->msg_offset += sizeof(current_signer_info->hash);
     136             : 
     137             :         // Make sure iteration fits
     138           1 :         if (ui_heartbeat_ctx->msg_offset +
     139             :                 sizeof(current_signer_info->iteration) >
     140             :             sizeof(ui_heartbeat_ctx->msg))
     141           0 :             THROW(ERR_UI_INTERNAL);
     142             : 
     143           3 :         VAR_BIGENDIAN_TO(ui_heartbeat_ctx->msg + ui_heartbeat_ctx->msg_offset,
     144             :                          current_signer_info->iteration,
     145             :                          sizeof(current_signer_info->iteration));
     146           1 :         ui_heartbeat_ctx->msg_offset += sizeof(current_signer_info->iteration);
     147             : 
     148           1 :         ui_heartbeat_ctx->state = STATE_UI_HEARTBEAT_READY;
     149             : 
     150           1 :         return TX_FOR_DATA_SIZE(0);
     151           2 :     case OP_UI_HBT_GET:
     152           2 :         check_state(ui_heartbeat_ctx, STATE_UI_HEARTBEAT_READY);
     153             : 
     154             :         // Sign message
     155           2 :         int endorsement_size = os_endorsement_key2_derive_sign_data(
     156           1 :             ui_heartbeat_ctx->msg, ui_heartbeat_ctx->msg_offset, APDU_DATA_PTR);
     157             : 
     158           1 :         return TX_FOR_DATA_SIZE(endorsement_size);
     159           2 :     case OP_UI_HBT_GET_MESSAGE:
     160           2 :         check_state(ui_heartbeat_ctx, STATE_UI_HEARTBEAT_READY);
     161             : 
     162           2 :         SAFE_MEMMOVE(APDU_DATA_PTR,
     163             :                      APDU_TOTAL_DATA_SIZE,
     164             :                      MEMMOVE_ZERO_OFFSET,
     165             :                      ui_heartbeat_ctx->msg,
     166             :                      sizeof(ui_heartbeat_ctx->msg),
     167             :                      MEMMOVE_ZERO_OFFSET,
     168             :                      ui_heartbeat_ctx->msg_offset,
     169             :                      THROW(ERR_UI_INTERNAL));
     170             : 
     171           1 :         return TX_FOR_DATA_SIZE(ui_heartbeat_ctx->msg_offset);
     172           1 :     case OP_UI_HBT_APP_HASH:
     173           1 :         return TX_FOR_DATA_SIZE(os_endorsement_get_code_hash(APDU_DATA_PTR));
     174           1 :     case OP_UI_HBT_PUBKEY:
     175           1 :         return TX_FOR_DATA_SIZE(os_endorsement_get_public_key(
     176             :             ENDORSEMENT_SCHEME_INDEX, APDU_DATA_PTR));
     177           1 :     default:
     178           1 :         reset_ui_heartbeat(ui_heartbeat_ctx);
     179           1 :         THROW(ERR_UI_HBT_PROT_INVALID);
     180             :         break;
     181             :     }
     182             : }
     183             : 
     184             : /**
     185             :  * Process an APDU message
     186             :  *
     187             :  * @arg[in] ui_heartbeat_ctx the UI heartbeat context
     188             :  * @arg[in] rx number of received bytes from the host
     189             :  * @ret        number of transmited bytes to the host
     190             :  */
     191          14 : unsigned int ui_heartbeat_process_apdu(ui_heartbeat_t *ui_heartbeat_ctx,
     192             :                                        volatile unsigned int rx) {
     193          14 :     unsigned int tx = 0;
     194             : 
     195             :     // no apdu received, or bigger-than-apdu-buffer bytes received =>
     196             :     // well, reset the session, and reset the heartbeat configuration
     197          14 :     if (rx == 0 || rx > APDU_TOTAL_SIZE) {
     198           2 :         THROW(ERR_INVALID_BUFFER);
     199             :     }
     200             : 
     201          12 :     if (APDU_CLA() != CLA) {
     202           1 :         THROW(ERR_UI_INVALID_CLA);
     203             :     }
     204             : 
     205             :     // unauthenticated instruction
     206          11 :     switch (APDU_CMD()) {
     207           1 :     case RSK_MODE_CMD:
     208           1 :         tx = get_mode_heartbeat();
     209           1 :         break;
     210           9 :     case INS_UI_HEARTBEAT:
     211           9 :         tx = get_ui_heartbeat(ui_heartbeat_ctx, rx);
     212           5 :         break;
     213           1 :     case RSK_END_CMD: // return to app
     214           1 :         THROW(EX_BOOTLOADER_RSK_END);
     215           0 :     default:
     216           0 :         THROW(ERR_INS_NOT_SUPPORTED);
     217             :         break;
     218             :     }
     219             : 
     220           6 :     return tx;
     221             : }
     222             : 
     223             : /**
     224             :  * Main function for the heartbeat frontend.
     225             :  * It basically only allows for gathering the heartbeat
     226             :  * and re-executing the authorized app
     227             :  * (i.e., the signer).
     228             :  *
     229             :  * @arg[in] ui_heartbeat_ctx the UI heartbeat context
     230             :  */
     231          14 : void ui_heartbeat_main(ui_heartbeat_t *ui_heartbeat_ctx) {
     232          14 :     volatile unsigned int rx = 0;
     233          14 :     volatile unsigned int tx = 0;
     234             : 
     235             :     // DESIGN NOTE: the bootloader ignores the way APDU are fetched. The only
     236             :     // goal is to retrieve APDU.
     237             :     // When APDU are to be fetched from multiple IOs, like NFC+USB+BLE, make
     238             :     // sure the io_event is called with a
     239             :     // switch event, before the apdu is replied to the bootloader. This avoid
     240             :     // APDU injection faults.
     241             :     for (;;) {
     242          13 :         BEGIN_TRY {
     243          27 :             TRY {
     244          27 :                 rx = tx;
     245          27 :                 tx = 0; // ensure no race in catch_other if io_exchange throws
     246             :                         // an error
     247          27 :                 rx = io_exchange(CHANNEL_APDU, rx);
     248             : 
     249          14 :                 tx = ui_heartbeat_process_apdu(ui_heartbeat_ctx, rx);
     250           6 :                 THROW(APDU_OK);
     251             :             }
     252          27 :             CATCH(EX_BOOTLOADER_RSK_END) {
     253          14 :                 break;
     254             :             }
     255          13 :             CATCH_OTHER(e) {
     256          13 :                 current_context = ui_heartbeat_ctx;
     257          13 :                 tx = ui_process_exception(e, tx, &reset_state);
     258             :             }
     259          13 :             FINALLY {
     260             :             }
     261             :         }
     262          13 :         END_TRY;
     263             :     }
     264          14 : }

Generated by: LCOV version 1.16