LCOV - code coverage report
Current view: top level - powhsm/src - hsm.c (source / functions) Hit Total Coverage
Test: powHSM firmware Lines: 118 154 76.6 %
Date: 2025-07-10 13:49:13 Functions: 7 9 77.8 %

          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         270 : static void reset_shared_state() {
      67         270 :     explicit_bzero(&mem, sizeof(mem));
      68         270 : }
      69             : 
      70             : /*
      71             :  * Reset all reseteable operations, only if the given operation is starting.
      72             :  *
      73             :  * @arg[in] cmd operation code
      74             :  */
      75        7236 : 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        7236 :     if (cmd != curr_cmd) {
      79         270 :         reset_shared_state();
      80         270 :         bc_init_advance();
      81         270 :         bc_init_upd_ancestor();
      82         270 :         curr_cmd = cmd;
      83             :     }
      84        7236 : }
      85             : 
      86             : // Exit the application
      87           0 : static void app_exit(void) {
      88           0 :     platform_request_exit();
      89           0 :     _hsm_exit_requested = true;
      90           0 : }
      91             : 
      92        7178 : static unsigned int hsm_process_command(volatile unsigned int rx) {
      93        7178 :     unsigned int tx = 0;
      94             :     uint8_t pubkey_length;
      95             : 
      96             :     // No apdu received, or bigger-than-apdu-buffer bytes received
      97        7178 :     if (rx == 0 || rx > APDU_TOTAL_SIZE) {
      98           2 :         THROW(ERR_INVALID_BUFFER);
      99             :     }
     100             : 
     101             :     // Zero out commonly read APDU buffer offsets,
     102             :     // to avoid reading uninitialized memory
     103        7176 :     if (rx < MIN_APDU_BYTES) {
     104          79 :         explicit_bzero(communication_get_msg_buffer() + rx,
     105          79 :                        MIN_APDU_BYTES - rx);
     106             :     }
     107             : 
     108             :     // Invalid CLA
     109        7176 :     if (APDU_CLA() != CLA) {
     110           0 :         THROW(ERR_INVALID_CLA);
     111             :     }
     112             : 
     113        7176 :     if (external_processor) {
     114           0 :         external_processor_result_t epr = external_processor(rx);
     115           0 :         if (epr.handled) {
     116           0 :             return epr.tx;
     117             :         }
     118             :     }
     119             : 
     120        7176 :     switch (APDU_CMD()) {
     121             :     // Reports the current mode (i.e., always reports signer mode)
     122           0 :     case RSK_MODE_CMD:
     123           0 :         hsm_reset_if_starting(RSK_MODE_CMD);
     124           0 :         SET_APDU_CMD(APP_MODE_SIGNER);
     125           0 :         tx = 2;
     126           0 :         break;
     127             : 
     128             :     // Reports wheter the device is onboarded and the current signer version
     129           1 :     case RSK_IS_ONBOARD:
     130           1 :         hsm_reset_if_starting(RSK_IS_ONBOARD);
     131           1 :         uint8_t output_index = CMDPOS;
     132           1 :         SET_APDU_AT(output_index++, seed_available() ? 1 : 0);
     133           1 :         SET_APDU_AT(output_index++, VERSION_MAJOR);
     134           1 :         SET_APDU_AT(output_index++, VERSION_MINOR);
     135           1 :         SET_APDU_AT(output_index++, VERSION_PATCH);
     136           1 :         tx = 5;
     137           1 :         break;
     138             : 
     139             :     // Derives and returns the corresponding public key for the given path
     140          84 :     case INS_GET_PUBLIC_KEY:
     141          84 :         REQUIRE_UNLOCKED();
     142          84 :         REQUIRE_ONBOARDED();
     143             : 
     144          78 :         hsm_reset_if_starting(INS_GET_PUBLIC_KEY);
     145             : 
     146             :         // Check the received data size
     147          78 :         if (rx != DATA + sizeof(uint32_t) * BIP32_PATH_NUMPARTS)
     148           6 :             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          86 :         if (!(pathRequireAuth(APDU_DATA_PTR - 1) ||
     156          14 :               pathDontRequireAuth(APDU_DATA_PTR - 1))) {
     157             :             // If no path match, then bail out
     158           6 :             THROW(ERR_INVALID_PATH); // Invalid Key Path
     159             :         }
     160             : 
     161             :         // Derive the public key
     162         132 :         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          66 :         pubkey_length = (uint8_t)MIN(communication_get_msg_buffer_size(), 0xFF);
     172          66 :         if (!seed_derive_pubkey(auth.path,
     173             :                                 sizeof(auth.path) / sizeof(auth.path[0]),
     174          66 :                                 communication_get_msg_buffer(),
     175             :                                 &pubkey_length)) {
     176           0 :             THROW(ERR_INTERNAL);
     177             :         }
     178             : 
     179          66 :         tx = pubkey_length;
     180             : 
     181          66 :         break;
     182             : 
     183        2688 :     case INS_SIGN:
     184        2688 :         REQUIRE_UNLOCKED();
     185        2688 :         REQUIRE_ONBOARDED();
     186             : 
     187        2682 :         hsm_reset_if_starting(INS_SIGN);
     188        2682 :         tx = auth_sign(rx);
     189        2644 :         break;
     190             : 
     191           0 :     case INS_ATTESTATION:
     192           0 :         REQUIRE_UNLOCKED();
     193           0 :         REQUIRE_ONBOARDED();
     194             : 
     195           0 :         hsm_reset_if_starting(INS_ATTESTATION);
     196           0 :         tx = get_attestation(rx, &attestation);
     197           0 :         break;
     198             : 
     199          20 :     case INS_HEARTBEAT:
     200          20 :         REQUIRE_UNLOCKED();
     201          20 :         REQUIRE_ONBOARDED();
     202             : 
     203          20 :         hsm_reset_if_starting(INS_HEARTBEAT);
     204          20 :         tx = get_heartbeat(rx, &heartbeat);
     205          20 :         break;
     206             : 
     207             :     // Get blockchain state
     208         199 :     case INS_GET_STATE:
     209         199 :         REQUIRE_UNLOCKED();
     210         199 :         REQUIRE_ONBOARDED();
     211             : 
     212             :         // Get blockchain state is considered part of the
     213             :         // advance blockchain operation
     214         198 :         hsm_reset_if_starting(INS_ADVANCE);
     215         198 :         tx = bc_get_state(rx);
     216         198 :         break;
     217             : 
     218             :     // Reset blockchain state
     219          17 :     case INS_RESET_STATE:
     220          17 :         REQUIRE_UNLOCKED();
     221          17 :         REQUIRE_ONBOARDED();
     222             : 
     223          17 :         hsm_reset_if_starting(INS_RESET_STATE);
     224          17 :         tx = bc_reset_state(rx);
     225          17 :         break;
     226             : 
     227             :     // Advance blockchain
     228        3325 :     case INS_ADVANCE:
     229        3325 :         REQUIRE_UNLOCKED();
     230        3325 :         REQUIRE_ONBOARDED();
     231             : 
     232        3324 :         hsm_reset_if_starting(INS_ADVANCE);
     233        3324 :         tx = bc_advance(rx);
     234        3316 :         break;
     235             : 
     236             :     // Advance blockchain precompiled parameters
     237           1 :     case INS_ADVANCE_PARAMS:
     238           1 :         REQUIRE_UNLOCKED();
     239           1 :         REQUIRE_ONBOARDED();
     240             : 
     241           1 :         hsm_reset_if_starting(INS_ADVANCE_PARAMS);
     242           1 :         tx = bc_advance_get_params();
     243           1 :         break;
     244             : 
     245             :     // Update ancestor
     246         841 :     case INS_UPD_ANCESTOR:
     247         841 :         REQUIRE_UNLOCKED();
     248         841 :         REQUIRE_ONBOARDED();
     249             : 
     250         840 :         hsm_reset_if_starting(INS_UPD_ANCESTOR);
     251         840 :         tx = bc_upd_ancestor(rx);
     252         840 :         break;
     253             : 
     254           0 :     case INS_EXIT:
     255           0 :         REQUIRE_UNLOCKED();
     256             :         
     257           0 :         bc_backup_partial_state();
     258           0 :         app_exit();
     259           0 :         tx = TX_FOR_DATA_SIZE(0);
     260           0 :         break;
     261             : 
     262           0 :     default: // Unknown command
     263           0 :         THROW(ERR_INS_NOT_SUPPORTED);
     264             :         break;
     265             :     }
     266             : 
     267        7103 :     return tx;
     268             : }
     269             : 
     270        7178 : static unsigned int hsm_process_exception(unsigned short code,
     271             :                                           unsigned int tx) {
     272        7178 :     unsigned short sw = 0;
     273             : 
     274             :     // Always reset the full state when an error occurs
     275        7178 :     if (code != APDU_OK) {
     276          75 :         RESET_BC_STATE();
     277          75 :         hsm_reset_if_starting(0);
     278             :     }
     279             : 
     280             :     // Apply code transformations
     281        7178 :     switch (code & 0xF000) {
     282        7178 :     case 0x6000:
     283             :     case 0x9000:
     284        7178 :         sw = code;
     285        7178 :         break;
     286           0 :     default:
     287           0 :         sw = 0x6800 | (code & 0x7FF);
     288           0 :         break;
     289             :     }
     290             : 
     291             :     // Append resulting code to APDU
     292             :     // (check for a potential overflow first)
     293        7178 :     if (tx + 2 > communication_get_msg_buffer_size()) {
     294           0 :         tx = 0;
     295           0 :         sw = 0x6983;
     296             :     }
     297        7178 :     SET_APDU_AT(tx++, sw >> 8);
     298        7178 :     SET_APDU_AT(tx++, sw);
     299             : 
     300        7178 :     return tx;
     301             : }
     302             : 
     303           2 : void hsm_init() {
     304             :     // Initialize current operation
     305             :     // (0 = no operation being executed)
     306           2 :     curr_cmd = 0;
     307             : 
     308             :     // No exit requested
     309           2 :     _hsm_exit_requested = false;
     310             : 
     311             :     // No external processor
     312           2 :     external_processor = NULL;
     313             : 
     314             :     // Blockchain state initialization
     315           2 :     bc_init_state();
     316           2 : }
     317             : 
     318        7178 : unsigned int hsm_process_apdu(unsigned int rx) {
     319        7178 :     unsigned int tx = 0;
     320             : 
     321             :     BEGIN_TRY {
     322        7178 :         TRY {
     323        7178 :             tx = hsm_process_command(rx);
     324        7103 :             THROW(0x9000);
     325             :         }
     326        7178 :         CATCH_OTHER(e) {
     327        7178 :             tx = hsm_process_exception(e, tx);
     328             :         }
     329        7178 :         FINALLY {
     330             :         }
     331             :     }
     332        7178 :     END_TRY;
     333             : 
     334        7178 :     return tx;
     335             : }
     336             : 
     337        7179 : bool hsm_exit_requested() {
     338        7179 :     return _hsm_exit_requested;
     339             : }
     340             : 
     341           0 : void hsm_set_external_processor(external_processor_t _external_processor) {
     342           0 :     external_processor = _external_processor;
     343           0 : }

Generated by: LCOV version 1.16