LCOV - code coverage report
Current view: top level - sgx/src/trusted - system.c (source / functions) Hit Total Coverage
Test: powHSM firmware Lines: 141 148 95.3 %
Date: 2025-07-10 13:49:13 Functions: 9 11 81.8 %

          Line data    Source code
       1             : #include <string.h>
       2             : #include <openenclave/enclave.h>
       3             : 
       4             : #include "hal/constants.h"
       5             : #include "hal/communication.h"
       6             : #include "hal/seed.h"
       7             : #include "hal/access.h"
       8             : #include "hal/endorsement.h"
       9             : #include "hal/nvmem.h"
      10             : #include "hal/exceptions.h"
      11             : #include "hal/log.h"
      12             : 
      13             : #include "secret_store.h"
      14             : #include "hsm.h"
      15             : #include "apdu.h"
      16             : #include "instructions.h"
      17             : #include "modes.h"
      18             : #include "bc_state.h"
      19             : #include "err.h"
      20             : #include "bc_err.h"
      21             : #include "upgrade.h"
      22             : #include "evidence.h"
      23             : #include "meta_bc.h"
      24             : 
      25             : /**
      26             :  * APDU buffer (host pointer and local enclave copy)
      27             :  */
      28             : #define APDU_BUFFER_SIZE (2048 + 5)
      29             : 
      30             : static unsigned char* host_apdu_buffer;
      31             : static unsigned char apdu_buffer[APDU_BUFFER_SIZE];
      32             : 
      33           2 : static void wipe_system() {
      34           2 :     seed_wipe();
      35           2 :     access_wipe();
      36           2 : }
      37             : 
      38           0 : static void on_access_wiped() {
      39           0 :     if (!seed_wipe()) {
      40             :         LOG("Error wiping seed module\n");
      41             :     }
      42             :     LOG("Seed wiped\n");
      43           0 : }
      44             : 
      45           5 : static unsigned int do_onboard(unsigned int rx) {
      46             :     // Validations
      47           5 :     if (seed_available()) {
      48           1 :         THROW(ERR_DEVICE_ONBOARDED);
      49             :     }
      50             : 
      51             :     // Require seed plus a nonblank password
      52           4 :     if (APDU_DATA_SIZE(rx) < SEED_LENGTH + 1) {
      53           1 :         THROW(ERR_INVALID_DATA_SIZE);
      54             :     }
      55             : 
      56             :     // Onboarding
      57             :     uint8_t tmp_buffer[sizeof(apdu_buffer)];
      58           3 :     memcpy(tmp_buffer, APDU_DATA_PTR, SEED_LENGTH);
      59           3 :     if (!seed_generate(tmp_buffer, SEED_LENGTH)) {
      60           1 :         wipe_system();
      61           1 :         THROW(ERR_ONBOARDING);
      62             :     }
      63             : 
      64           2 :     size_t password_length = APDU_DATA_SIZE(rx) - SEED_LENGTH;
      65           2 :     memcpy(tmp_buffer, APDU_DATA_PTR + SEED_LENGTH, password_length);
      66           2 :     if (!access_set_password((char*)tmp_buffer, password_length)) {
      67           1 :         wipe_system();
      68           1 :         THROW(ERR_ONBOARDING);
      69             :     }
      70             : 
      71           1 :     SET_APDU_OP(1);
      72           1 :     return TX_NO_DATA();
      73             : }
      74             : 
      75           3 : static unsigned int do_change_password(unsigned int rx) {
      76             :     // Require a nonblank password
      77           3 :     if (APDU_DATA_SIZE(rx) < 1) {
      78           1 :         THROW(ERR_INVALID_DATA_SIZE);
      79             :     }
      80             : 
      81             :     // Password change
      82             :     uint8_t tmp_buffer[sizeof(apdu_buffer)];
      83           2 :     size_t password_length = APDU_DATA_SIZE(rx);
      84           2 :     memcpy(tmp_buffer, APDU_DATA_PTR, password_length);
      85           2 :     if (!access_set_password((char*)tmp_buffer, password_length)) {
      86           1 :         THROW(ERR_PASSWORD_CHANGE);
      87             :     }
      88             : 
      89           1 :     SET_APDU_OP(1);
      90           1 :     return TX_NO_DATA();
      91             : }
      92             : 
      93           3 : static unsigned int do_unlock(unsigned int rx) {
      94           3 :     if (!access_is_locked()) {
      95           1 :         SET_APDU_OP(1);
      96           1 :         return TX_NO_DATA();
      97             :     }
      98             : 
      99           2 :     if (APDU_DATA_SIZE(rx) == 0) {
     100           1 :         THROW(ERR_INVALID_DATA_SIZE);
     101             :     }
     102             : 
     103           1 :     SET_APDU_OP(access_unlock((char*)APDU_DATA_PTR, APDU_DATA_SIZE(rx)) ? 1
     104             :                                                                         : 0);
     105           1 :     return TX_NO_DATA();
     106             : }
     107             : 
     108           1 : static unsigned int do_echo(unsigned int rx) {
     109           1 :     return rx;
     110             : }
     111             : 
     112             : static unsigned char curr_cmd;
     113             : 
     114          35 : static void reset_unless_cmd_is(unsigned char cmd) {
     115          35 :     if (cmd != curr_cmd) {
     116             :         // Reset all modules' contexts
     117          27 :         hsm_reset_if_starting(cmd);
     118          27 :         upgrade_reset();
     119          27 :         curr_cmd = cmd;
     120             :     }
     121          35 : }
     122             : 
     123          41 : static external_processor_result_t system_do_process_apdu(unsigned int rx) {
     124          41 :     external_processor_result_t result = {
     125             :         .handled = true,
     126             :         .tx = 0,
     127             :     };
     128             : 
     129          41 :     switch (APDU_CMD()) {
     130             :     // Reports the bootloader mode only if the device is locked
     131             :     // Otherwise command is ignored and the powHSM handler will
     132             :     // take over instead.
     133           2 :     case RSK_MODE_CMD:
     134           2 :         if (access_is_locked()) {
     135           1 :             reset_unless_cmd_is(RSK_MODE_CMD);
     136           1 :             SET_APDU_CMD(APP_MODE_BOOTLOADER);
     137           1 :             result.tx = 2;
     138           1 :             break;
     139             :         }
     140           1 :         result.handled = false;
     141           1 :         break;
     142           5 :     case SGX_ONBOARD:
     143           5 :         reset_unless_cmd_is(SGX_ONBOARD);
     144           5 :         result.tx = do_onboard(rx);
     145           1 :         break;
     146           4 :     case SGX_IS_LOCKED:
     147           4 :         REQUIRE_ONBOARDED();
     148           4 :         reset_unless_cmd_is(SGX_IS_LOCKED);
     149           4 :         SET_APDU_OP(access_is_locked() ? 1 : 0);
     150           4 :         result.tx = TX_NO_DATA();
     151           4 :         break;
     152           1 :     case SGX_RETRIES:
     153           1 :         REQUIRE_ONBOARDED();
     154           1 :         reset_unless_cmd_is(SGX_RETRIES);
     155           1 :         SET_APDU_OP(access_get_retries());
     156           1 :         result.tx = TX_NO_DATA();
     157           1 :         break;
     158           3 :     case SGX_UNLOCK:
     159           3 :         REQUIRE_ONBOARDED();
     160           3 :         reset_unless_cmd_is(SGX_UNLOCK);
     161           3 :         result.tx = do_unlock(rx);
     162           2 :         break;
     163           1 :     case SGX_ECHO:
     164           1 :         reset_unless_cmd_is(SGX_ECHO);
     165           1 :         result.tx = do_echo(rx);
     166           1 :         break;
     167           3 :     case SGX_CHANGE_PASSWORD:
     168           3 :         REQUIRE_ONBOARDED();
     169           3 :         REQUIRE_UNLOCKED();
     170           3 :         reset_unless_cmd_is(SGX_CHANGE_PASSWORD);
     171           3 :         result.tx = do_change_password(rx);
     172           1 :         break;
     173           5 :     case SGX_UPGRADE:
     174           5 :         reset_unless_cmd_is(SGX_UPGRADE);
     175           5 :         result.tx = upgrade_process_apdu(rx);
     176           5 :         break;
     177           1 :     case INS_HEARTBEAT:
     178             :         // For now, we don't support heartbeat in SGX
     179           1 :         THROW(ERR_INS_NOT_SUPPORTED);
     180             :         break;
     181             :     // Override the default hsm module handlers for these
     182             :     // commands
     183          14 :     case INS_ADVANCE:
     184             :     case INS_UPD_ANCESTOR:
     185          14 :         REQUIRE_UNLOCKED();
     186          12 :         REQUIRE_ONBOARDED();
     187          10 :         reset_unless_cmd_is(APDU_CMD());
     188          10 :         result.tx = do_meta_advupd(rx);
     189          10 :         break;
     190           2 :     default:
     191           2 :         reset_unless_cmd_is(0);
     192           2 :         result.handled = false;
     193             :     }
     194             : 
     195          29 :     return result;
     196             : }
     197             : 
     198          41 : unsigned int system_process_apdu(unsigned int rx) {
     199             :     // Copy host APDU => enclave APDU
     200          41 :     memcpy(apdu_buffer, host_apdu_buffer, sizeof(apdu_buffer));
     201          41 :     unsigned int tx = hsm_process_apdu(rx);
     202             :     // Copy enclave APDU => host APDU
     203          29 :     memcpy(host_apdu_buffer, apdu_buffer, sizeof(apdu_buffer));
     204          29 :     return tx;
     205             : }
     206             : 
     207          44 : bool system_init(unsigned char* msg_buffer, size_t msg_buffer_size) {
     208             :     // Validate that host and enclave APDU buffers have the same size
     209          44 :     if (msg_buffer_size != sizeof(apdu_buffer)) {
     210             :         LOG("Expected APDU buffer size to be %lu but got %lu\n",
     211             :             sizeof(apdu_buffer),
     212             :             msg_buffer_size);
     213           1 :         return false;
     214             :     }
     215             : 
     216             :     // Validate that the host APDU buffer is entirely outside the enclave
     217             :     // memory space
     218          43 :     if (!oe_is_outside_enclave(msg_buffer, msg_buffer_size)) {
     219             :         LOG("APDU buffer memory area not outside the enclave\n");
     220           1 :         return false;
     221             :     }
     222             : 
     223             :     // Set the pointer to the host APDU buffer
     224          42 :     host_apdu_buffer = msg_buffer;
     225             : 
     226             :     // Initialize modules
     227             :     LOG("Initializing modules...\n");
     228          42 :     if (!sest_init()) {
     229             :         LOG("Error initializing secret store module\n");
     230           1 :         return false;
     231             :     }
     232             : 
     233          41 :     if (!access_init(on_access_wiped)) {
     234             :         LOG("Error initializing access module\n");
     235           1 :         return false;
     236             :     }
     237             : 
     238          40 :     if (!seed_init()) {
     239             :         LOG("Error initializing seed module\n");
     240           1 :         return false;
     241             :     }
     242             : 
     243             :     // Make sure both access and seed are in the same state
     244          39 :     if (!seed_available() ^ access_is_wiped()) {
     245             :         LOG("Inconsistent system state detected\n");
     246          33 :         if (!access_wipe() || !seed_wipe()) {
     247             :             LOG("System wipe failed\n");
     248           2 :             return false;
     249             :         }
     250             :         LOG("System wiped\n");
     251             :     }
     252             : 
     253          37 :     if (!communication_init(apdu_buffer, sizeof(apdu_buffer))) {
     254             :         LOG("Error initializing communication module\n");
     255           1 :         return false;
     256             :     }
     257             : 
     258          36 :     if (!evidence_init()) {
     259             :         LOG("Error initializing evidence module\n");
     260           1 :         return false;
     261             :     }
     262             : 
     263          35 :     if (!endorsement_init()) {
     264             :         LOG("Error initializing endorsement module\n");
     265           1 :         return false;
     266             :     }
     267             : 
     268          34 :     nvmem_init();
     269          34 :     if (!nvmem_register_block(
     270             :             "bcstate", &N_bc_state_var, sizeof(N_bc_state_var))) {
     271             :         LOG("Error registering bcstate block\n");
     272           1 :         return false;
     273             :     }
     274          33 :     if (!nvmem_register_block("bcstate_updating",
     275             :                               &N_bc_state_updating_backup_var,
     276             :                               sizeof(N_bc_state_updating_backup_var))) {
     277             :         LOG("Error registering bcstate_updating block\n");
     278           1 :         return false;
     279             :     }
     280             : 
     281          32 :     if (!nvmem_load()) {
     282             :         LOG("Error loading nvmem\n");
     283           1 :         return false;
     284             :     }
     285             : 
     286          31 :     upgrade_init();
     287             : 
     288             :     LOG("Modules initialized\n");
     289             : 
     290             :     LOG("Initializing powHSM...\n");
     291          31 :     hsm_init();
     292          31 :     hsm_set_external_processor(system_do_process_apdu);
     293          31 :     curr_cmd = 0;
     294             :     LOG("powHSM initialized\n");
     295             : 
     296          31 :     return true;
     297             : }
     298             : 
     299           0 : void system_finalise() {
     300             :     // Finalise modules
     301           0 :     endorsement_finalise();
     302           0 :     evidence_finalise();
     303           0 : }

Generated by: LCOV version 1.16