LCOV - code coverage report
Current view: top level - powhsm/src - hsm.c (source / functions) Hit Total Coverage
Test: powHSM firmware Lines: 150 155 96.8 %
Date: 2025-11-18 03:05:18 Functions: 9 9 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 <string.h>
      26             : 
      27             : #include "hal/communication.h"
      28             : #include "hal/seed.h"
      29             : #include "hal/platform.h"
      30             : #include "hal/exceptions.h"
      31             : 
      32             : #include "hsm.h"
      33             : 
      34             : #include "defs.h"
      35             : #include "instructions.h"
      36             : #include "modes.h"
      37             : #include "err.h"
      38             : #include "mem.h"
      39             : #include "memutil.h"
      40             : #include "util.h"
      41             : 
      42             : #include "pathAuth.h"
      43             : #include "auth.h"
      44             : 
      45             : #include "bc_state.h"
      46             : #include "bc_advance.h"
      47             : #include "bc_ancestor.h"
      48             : 
      49             : #include "attestation.h"
      50             : #include "heartbeat.h"
      51             : 
      52             : #include "hal/log.h"
      53             : 
      54             : // Operation being currently executed
      55             : static unsigned char curr_cmd;
      56             : 
      57             : // Whether exit has been requested
      58             : static bool _hsm_exit_requested;
      59             : 
      60             : // External processor
      61             : static external_processor_t external_processor;
      62             : 
      63             : /*
      64             :  * Reset shared memory state.
      65             :  */
      66         348 : static void reset_shared_state() {
      67         348 :     explicit_bzero(&mem, sizeof(mem));
      68         348 : }
      69             : 
      70             : /*
      71             :  * Reset all reseteable operations, only if the given operation is starting.
      72             :  *
      73             :  * @arg[in] cmd operation code
      74             :  */
      75        7347 : void hsm_reset_if_starting(unsigned char cmd) {
      76             :     // Reset only if starting new operation (cmd != curr_cmd).
      77             :     // Otherwise we already reset when curr_cmd started.
      78        7347 :     if (cmd != curr_cmd) {
      79         348 :         reset_shared_state();
      80         348 :         bc_init_advance();
      81         348 :         bc_init_upd_ancestor();
      82         348 :         curr_cmd = cmd;
      83             :     }
      84        7347 : }
      85             : 
      86             : // Exit the application
      87           1 : static void app_exit(void) {
      88           1 :     platform_request_exit();
      89           1 :     _hsm_exit_requested = true;
      90           1 : }
      91             : 
      92        7272 : static unsigned int hsm_process_command(volatile unsigned int rx) {
      93        7272 :     unsigned int tx = 0;
      94             :     uint8_t pubkey_length;
      95             : 
      96             :     // No apdu received, or bigger-than-apdu-buffer bytes received
      97        7272 :     if (rx == 0 || rx > APDU_TOTAL_SIZE) {
      98           4 :         THROW(ERR_INVALID_BUFFER);
      99             :     }
     100             : 
     101             :     // Zero out commonly read APDU buffer offsets,
     102             :     // to avoid reading uninitialized memory
     103        7268 :     if (rx < MIN_APDU_BYTES) {
     104         134 :         explicit_bzero(communication_get_msg_buffer() + rx,
     105         134 :                        MIN_APDU_BYTES - rx);
     106             :     }
     107             : 
     108             :     // Invalid CLA
     109        7268 :     if (APDU_CLA() != CLA) {
     110           2 :         THROW(ERR_INVALID_CLA);
     111             :     }
     112             : 
     113        7266 :     if (external_processor) {
     114           2 :         external_processor_result_t epr = external_processor(rx);
     115           2 :         if (epr.handled) {
     116           1 :             return epr.tx;
     117             :         }
     118             :     }
     119             : 
     120        7265 :     switch (APDU_CMD()) {
     121             :     // Reports the current mode (i.e., always reports signer mode)
     122          20 :     case RSK_MODE_CMD:
     123          20 :         hsm_reset_if_starting(RSK_MODE_CMD);
     124          20 :         SET_APDU_CMD(APP_MODE_SIGNER);
     125          20 :         tx = 2;
     126          20 :         break;
     127             : 
     128             :     // Reports wheter the device is onboarded and the current signer version
     129           3 :     case RSK_IS_ONBOARD:
     130           3 :         hsm_reset_if_starting(RSK_IS_ONBOARD);
     131           3 :         uint8_t output_index = CMDPOS;
     132           3 :         SET_APDU_AT(output_index++, seed_available() ? 1 : 0);
     133           3 :         SET_APDU_AT(output_index++, VERSION_MAJOR);
     134           3 :         SET_APDU_AT(output_index++, VERSION_MINOR);
     135           3 :         SET_APDU_AT(output_index++, VERSION_PATCH);
     136           3 :         tx = 5;
     137           3 :         break;
     138             : 
     139             :     // Derives and returns the corresponding public key for the given path
     140          92 :     case INS_GET_PUBLIC_KEY:
     141          92 :         REQUIRE_UNLOCKED();
     142          91 :         REQUIRE_ONBOARDED();
     143             : 
     144          84 :         hsm_reset_if_starting(INS_GET_PUBLIC_KEY);
     145             : 
     146             :         // Check the received data size
     147          84 :         if (rx != DATA + sizeof(uint32_t) * BIP32_PATH_NUMPARTS)
     148           7 :             THROW(ERR_INVALID_DATA_SIZE); // Wrong buffer size
     149             : 
     150             :         // Check for path validity before returning the public key
     151             :         // Actual path starts at normal data pointer, but
     152             :         // is prepended by a single byte indicating the path length
     153             :         // (all paths have the same length in practice, so this should
     154             :         // be refactored in the future)
     155          92 :         if (!(pathRequireAuth(APDU_DATA_PTR - 1) ||
     156          15 :               pathDontRequireAuth(APDU_DATA_PTR - 1))) {
     157             :             // If no path match, then bail out
     158           7 :             THROW(ERR_INVALID_PATH); // Invalid Key Path
     159             :         }
     160             : 
     161             :         // Derive the public key
     162         140 :         SAFE_MEMMOVE(auth.path,
     163             :                      sizeof(auth.path),
     164             :                      MEMMOVE_ZERO_OFFSET,
     165             :                      APDU_DATA_PTR,
     166             :                      APDU_TOTAL_DATA_SIZE_OUT,
     167             :                      MEMMOVE_ZERO_OFFSET,
     168             :                      sizeof(auth.path),
     169             :                      THROW(ERR_INVALID_PATH));
     170             : 
     171          70 :         pubkey_length = (uint8_t)MIN(
     172             :             communication_get_msg_buffer_size() - APDU_RESULT_CODE_SIZE, 0xFF);
     173          70 :         if (!seed_derive_pubkey(auth.path,
     174             :                                 sizeof(auth.path) / sizeof(auth.path[0]),
     175          70 :                                 communication_get_msg_buffer(),
     176             :                                 &pubkey_length)) {
     177           1 :             THROW(ERR_INTERNAL);
     178             :         }
     179             : 
     180          69 :         tx = pubkey_length;
     181             : 
     182          69 :         break;
     183             : 
     184        2714 :     case INS_SIGN:
     185        2714 :         REQUIRE_UNLOCKED();
     186        2713 :         REQUIRE_ONBOARDED();
     187             : 
     188        2706 :         hsm_reset_if_starting(INS_SIGN);
     189        2706 :         tx = auth_sign(rx);
     190        2664 :         break;
     191             : 
     192           4 :     case INS_ATTESTATION:
     193           4 :         REQUIRE_UNLOCKED();
     194           3 :         REQUIRE_ONBOARDED();
     195             : 
     196           2 :         hsm_reset_if_starting(INS_ATTESTATION);
     197           2 :         tx = get_attestation(rx, &attestation);
     198           1 :         break;
     199             : 
     200          24 :     case INS_HEARTBEAT:
     201          24 :         REQUIRE_UNLOCKED();
     202          23 :         REQUIRE_ONBOARDED();
     203             : 
     204          22 :         hsm_reset_if_starting(INS_HEARTBEAT);
     205          22 :         tx = get_heartbeat(rx, &heartbeat);
     206          21 :         break;
     207             : 
     208             :     // Get blockchain state
     209         203 :     case INS_GET_STATE:
     210         203 :         REQUIRE_UNLOCKED();
     211         202 :         REQUIRE_ONBOARDED();
     212             : 
     213             :         // Get blockchain state is considered part of the
     214             :         // advance blockchain operation
     215         200 :         hsm_reset_if_starting(INS_ADVANCE);
     216         200 :         tx = bc_get_state(rx);
     217         199 :         break;
     218             : 
     219             :     // Reset blockchain state
     220          21 :     case INS_RESET_STATE:
     221          21 :         REQUIRE_UNLOCKED();
     222          20 :         REQUIRE_ONBOARDED();
     223             : 
     224          19 :         hsm_reset_if_starting(INS_RESET_STATE);
     225          19 :         tx = bc_reset_state(rx);
     226          18 :         break;
     227             : 
     228             :     // Advance blockchain
     229        3329 :     case INS_ADVANCE:
     230        3329 :         REQUIRE_UNLOCKED();
     231        3328 :         REQUIRE_ONBOARDED();
     232             : 
     233        3326 :         hsm_reset_if_starting(INS_ADVANCE);
     234        3326 :         tx = bc_advance(rx);
     235        3317 :         break;
     236             : 
     237             :     // Advance blockchain precompiled parameters
     238           5 :     case INS_ADVANCE_PARAMS:
     239           5 :         REQUIRE_UNLOCKED();
     240           4 :         REQUIRE_ONBOARDED();
     241             : 
     242           3 :         hsm_reset_if_starting(INS_ADVANCE_PARAMS);
     243           3 :         tx = bc_advance_get_params();
     244           2 :         break;
     245             : 
     246             :     // Update ancestor
     247         845 :     case INS_UPD_ANCESTOR:
     248         845 :         REQUIRE_UNLOCKED();
     249         844 :         REQUIRE_ONBOARDED();
     250             : 
     251         842 :         hsm_reset_if_starting(INS_UPD_ANCESTOR);
     252         842 :         tx = bc_upd_ancestor(rx);
     253         841 :         break;
     254             : 
     255           2 :     case INS_EXIT:
     256           2 :         REQUIRE_UNLOCKED();
     257             : 
     258           1 :         bc_backup_partial_state();
     259           1 :         app_exit();
     260           1 :         tx = TX_FOR_DATA_SIZE(0);
     261           1 :         break;
     262             : 
     263           3 :     default: // Unknown command
     264           3 :         THROW(ERR_INS_NOT_SUPPORTED);
     265             :         break;
     266             :     }
     267             : 
     268        7156 :     return tx;
     269             : }
     270             : 
     271        7272 : static unsigned int hsm_process_exception(unsigned short code,
     272             :                                           unsigned int tx) {
     273        7272 :     unsigned short sw = 0;
     274             : 
     275             :     // Always reset the full state when an error occurs
     276        7272 :     if (code != APDU_OK) {
     277         115 :         RESET_BC_STATE();
     278         115 :         hsm_reset_if_starting(0);
     279             :     }
     280             : 
     281             :     // Apply code transformations
     282        7272 :     switch (code & 0xF000) {
     283        7272 :     case 0x6000:
     284             :     case 0x9000:
     285        7272 :         sw = code;
     286        7272 :         break;
     287           0 :     default:
     288           0 :         sw = 0x6800 | (code & 0x7FF);
     289           0 :         break;
     290             :     }
     291             : 
     292             :     // Append resulting code to APDU
     293             :     // (check for a potential overflow first)
     294        7272 :     if (tx + 2 > communication_get_msg_buffer_size()) {
     295           0 :         tx = 0;
     296           0 :         sw = 0x6983;
     297             :     }
     298        7272 :     SET_APDU_AT(tx++, sw >> 8);
     299        7272 :     SET_APDU_AT(tx++, sw);
     300             : 
     301        7272 :     return tx;
     302             : }
     303             : 
     304          21 : void hsm_init() {
     305             :     // Initialize current operation
     306             :     // (0 = no operation being executed)
     307          21 :     curr_cmd = 0;
     308             : 
     309             :     // No exit requested
     310          21 :     _hsm_exit_requested = false;
     311             : 
     312             :     // No external processor
     313          21 :     external_processor = NULL;
     314             : 
     315             :     // Blockchain state initialization
     316          21 :     bc_init_state();
     317          21 : }
     318             : 
     319        7272 : unsigned int hsm_process_apdu(unsigned int rx) {
     320        7272 :     unsigned int tx = 0;
     321        7272 :     unsigned short ex = APDU_OK;
     322             :     BEGIN_TRY {
     323        7272 :         TRY {
     324        7272 :             tx = hsm_process_command(rx);
     325             :         }
     326        7157 :         CATCH_OTHER(e) {
     327         115 :             ex = e;
     328             :         }
     329         115 :         FINALLY {
     330        7272 :             tx = hsm_process_exception(ex, tx);
     331             :         }
     332             :     }
     333        7272 :     END_TRY;
     334             : 
     335        7272 :     return tx;
     336             : }
     337             : 
     338        7206 : bool hsm_exit_requested() {
     339        7206 :     return _hsm_exit_requested;
     340             : }
     341             : 
     342           2 : void hsm_set_external_processor(external_processor_t _external_processor) {
     343           2 :     external_processor = _external_processor;
     344           2 : }

Generated by: LCOV version 1.16