LCOV - code coverage report
Current view: top level - ledger/ui/src - signer_authorization.c (source / functions) Hit Total Coverage
Test: powHSM firmware Lines: 98 107 91.6 %
Date: 2025-07-10 13:49:13 Functions: 7 7 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             : #include <stdlib.h>
      27             : #include "os.h"
      28             : #include "cx.h"
      29             : #include "signer_authorization.h"
      30             : #include "defs.h"
      31             : #include "ui_err.h"
      32             : #include "memutil.h"
      33             : #include "ints.h"
      34             : #include "compiletime.h"
      35             : #include "runtime.h"
      36             : 
      37             : // Initial signer hash taken from an external definition (see Makefile for
      38             : // details)
      39             : #ifdef PARAM_INITIAL_SIGNER_HASH
      40             : static const uint8_t INITIAL_SIGNER_HASH[] = PARAM_INITIAL_SIGNER_HASH;
      41             : #else
      42             : #error "Initial signer hash not defined!"
      43             : #endif
      44             : 
      45             : // Initial signer iteration taken from an external definition (see Makefile for
      46             : // details)
      47             : #ifdef PARAM_INITIAL_SIGNER_ITERATION
      48             : static const uint16_t INITIAL_SIGNER_ITERATION = PARAM_INITIAL_SIGNER_ITERATION;
      49             : #else
      50             : #error "Initial signer iteration not defined!"
      51             : #endif
      52             : 
      53             : // Authorized signers
      54             : static const uint8_t authorizers_pubkeys[][AUTHORIZED_SIGNER_PUBKEY_LENGTH] =
      55             :     AUTHORIZERS_PUBKEYS;
      56             : 
      57             : // Total number of authorizers
      58             : #define TOTAL_AUTHORIZERS \
      59             :     (sizeof(authorizers_pubkeys) / sizeof(authorizers_pubkeys[0]))
      60             : 
      61             : // Minimum number of authorizers required to authorize a signer
      62             : #define THRESHOLD_AUTHORIZERS (TOTAL_AUTHORIZERS / 2 + 1)
      63             : 
      64             : /*
      65             :  * Sanity check the status of the signer authorization
      66             :  * component
      67             :  */
      68          10 : static void sanity_check() {
      69          10 :     if (!N_current_signer_status.initialized)
      70           0 :         THROW(ERR_UI_INTERNAL);
      71          10 : }
      72             : 
      73             : /*
      74             :  * Check that the SM for the signer authorization
      75             :  * matches the expected state.
      76             :  *
      77             :  * Reset the state and throw a protocol error
      78             :  * otherwise.
      79             :  */
      80           9 : static void check_state(sigaut_t* sigaut_ctx, sigaut_state_t expected) {
      81           9 :     sanity_check();
      82             : 
      83           9 :     if (sigaut_ctx->state != expected) {
      84           0 :         reset_signer_authorization(sigaut_ctx);
      85           0 :         THROW(ERR_UI_PROT_INVALID);
      86             :     }
      87           9 : }
      88             : 
      89           1 : static void generate_message_to_sign(sigaut_t* sigaut_ctx) {
      90             :     uint8_t message_size;
      91             : 
      92           1 :     cx_keccak_init(&sigaut_ctx->auth_hash_ctx, 256);
      93             : 
      94             :     // Hash eth prefix
      95           1 :     cx_hash((cx_hash_t*)&sigaut_ctx->auth_hash_ctx,
      96             :             0,
      97             :             (unsigned char*)ETHEREUM_MSG_PREFIX,
      98             :             ETHEREUM_MSG_PREFIX_LENGTH,
      99             :             0);
     100             : 
     101             :     // Compute total message size
     102           3 :     UINT_TO_DECSTR(sigaut_ctx->buf, sigaut_ctx->signer.iteration);
     103           1 :     message_size = RSK_SIGNER_VERSION_MSG_P1_LENGTH +
     104             :                    sizeof(sigaut_ctx->signer.hash) * 2 + // Hexa
     105           1 :                    RSK_SIGNER_VERSION_MSG_P2_LENGTH +
     106           1 :                    strlen((const char*)sigaut_ctx->buf);
     107             : 
     108             :     // Hash message size
     109           5 :     UINT_TO_DECSTR(sigaut_ctx->buf, message_size);
     110           1 :     cx_hash((cx_hash_t*)&sigaut_ctx->auth_hash_ctx,
     111             :             0,
     112           1 :             sigaut_ctx->buf,
     113           1 :             strlen((const char*)sigaut_ctx->buf),
     114             :             0);
     115             : 
     116             :     // Hash message
     117           1 :     cx_hash((cx_hash_t*)&sigaut_ctx->auth_hash_ctx,
     118             :             0,
     119             :             (unsigned char*)RSK_SIGNER_VERSION_MSG_P1,
     120             :             RSK_SIGNER_VERSION_MSG_P1_LENGTH,
     121             :             0);
     122          33 :     for (unsigned int i = 0; i < sizeof(sigaut_ctx->signer.hash); i++) {
     123          32 :         BYTE_TO_HEXSTR(sigaut_ctx->buf, sigaut_ctx->signer.hash[i]);
     124          32 :         cx_hash(
     125          32 :             (cx_hash_t*)&sigaut_ctx->auth_hash_ctx, 0, sigaut_ctx->buf, 2, 0);
     126             :     }
     127           1 :     cx_hash((cx_hash_t*)&sigaut_ctx->auth_hash_ctx,
     128             :             0,
     129             :             (unsigned char*)RSK_SIGNER_VERSION_MSG_P2,
     130             :             RSK_SIGNER_VERSION_MSG_P2_LENGTH,
     131             :             0);
     132           3 :     UINT_TO_DECSTR(sigaut_ctx->buf, sigaut_ctx->signer.iteration);
     133           1 :     cx_hash((cx_hash_t*)&sigaut_ctx->auth_hash_ctx,
     134             :             CX_LAST,
     135           1 :             sigaut_ctx->buf,
     136           1 :             strlen((const char*)sigaut_ctx->buf),
     137           1 :             sigaut_ctx->auth_hash);
     138           1 : }
     139             : 
     140             : // -----------------------------------------------------------------------
     141             : // Protocol implementation
     142             : // -----------------------------------------------------------------------
     143             : 
     144             : /*
     145             :  * Initialize the signer authorization
     146             :  */
     147           1 : void init_signer_authorization() {
     148             :     // Build should fail when more authorizers than supported are provided
     149             :     COMPILE_TIME_ASSERT(TOTAL_AUTHORIZERS <= MAX_AUTHORIZERS);
     150             : 
     151           1 :     const bool t = true;
     152             :     // Make sure NVM signer status is initialized
     153           1 :     if (!N_current_signer_status.initialized) {
     154           1 :         nvm_write(N_current_signer_status.signer.hash,
     155             :                   (void*)INITIAL_SIGNER_HASH,
     156             :                   sizeof(N_current_signer_status.signer.hash));
     157           1 :         nvm_write(&N_current_signer_status.signer.iteration,
     158             :                   (void*)&INITIAL_SIGNER_ITERATION,
     159             :                   sizeof(N_current_signer_status.signer.iteration));
     160           1 :         nvm_write(&N_current_signer_status.initialized,
     161             :                   (void*)&t,
     162             :                   sizeof(N_current_signer_status.initialized));
     163             :     }
     164           1 : }
     165             : 
     166             : /*
     167             :  * Reset the given signer authorization context
     168             :  *
     169             :  * @arg[in] sigaut_ctx signer authorization context
     170             :  */
     171          12 : void reset_signer_authorization(sigaut_t* sigaut_ctx) {
     172          12 :     explicit_bzero(sigaut_ctx, sizeof(sigaut_t));
     173          12 :     sigaut_ctx->state = sigaut_state_wait_signer_version;
     174          12 : }
     175             : 
     176             : /*
     177             :  * Implement the signer authorization protocol.
     178             :  *
     179             :  * @arg[in] rx                      number of received bytes from the Host
     180             :  * @arg[in] sigaut_ctx signer    authorization context
     181             :  * @ret                             number of transmited bytes to the host
     182             :  */
     183          14 : unsigned int do_authorize_signer(volatile unsigned int rx,
     184             :                                  sigaut_t* sigaut_ctx) {
     185             :     uint8_t signature_valid, valid_count, auth_index;
     186             : 
     187          14 :     switch (APDU_OP()) {
     188           1 :     case OP_SIGAUT_GET_CURRENT:
     189           1 :         sanity_check();
     190             : 
     191           2 :         SAFE_MEMMOVE(APDU_DATA_PTR,
     192             :                      APDU_TOTAL_DATA_SIZE_OUT,
     193             :                      MEMMOVE_ZERO_OFFSET,
     194             :                      N_current_signer_status.signer.hash,
     195             :                      sizeof(N_current_signer_status.signer.hash),
     196             :                      MEMMOVE_ZERO_OFFSET,
     197             :                      sizeof(N_current_signer_status.signer.hash),
     198             :                      THROW(ERR_UI_INTERNAL));
     199             : 
     200             :         if (APDU_TOTAL_DATA_SIZE_OUT <
     201             :             sizeof(N_current_signer_status.signer.hash) +
     202             :                 sizeof(N_current_signer_status.signer.iteration))
     203             :             THROW(ERR_UI_INTERNAL);
     204             : 
     205           3 :         VAR_BIGENDIAN_TO(APDU_DATA_PTR +
     206             :                              sizeof(N_current_signer_status.signer.hash),
     207             :                          N_current_signer_status.signer.iteration,
     208             :                          sizeof(N_current_signer_status.signer.iteration));
     209             : 
     210           1 :         return TX_FOR_DATA_SIZE(
     211             :             sizeof(N_current_signer_status.signer.hash) +
     212             :             sizeof(N_current_signer_status.signer.iteration));
     213           3 :     case OP_SIGAUT_SIGVER:
     214           3 :         check_state(sigaut_ctx, sigaut_state_wait_signer_version);
     215             : 
     216             :         // Should receive a signer hash followed by a signer iteration
     217           3 :         if (APDU_DATA_SIZE(rx) != (sizeof(sigaut_ctx->signer.hash) +
     218             :                                    sizeof(sigaut_ctx->signer.iteration)))
     219           1 :             THROW(ERR_UI_PROT_INVALID);
     220             : 
     221             :         // Set the signer version
     222           4 :         SAFE_MEMMOVE(sigaut_ctx->signer.hash,
     223             :                      sizeof(sigaut_ctx->signer.hash),
     224             :                      MEMMOVE_ZERO_OFFSET,
     225             :                      APDU_DATA_PTR,
     226             :                      APDU_TOTAL_DATA_SIZE,
     227             :                      MEMMOVE_ZERO_OFFSET,
     228             :                      sizeof(sigaut_ctx->signer.hash),
     229             :                      THROW(ERR_UI_INTERNAL));
     230             : 
     231           6 :         BIGENDIAN_FROM(APDU_DATA_PTR + sizeof(sigaut_ctx->signer.hash),
     232             :                        sigaut_ctx->signer.iteration);
     233             : 
     234             :         // Make sure the intended iteration is strictly greater than the current
     235             :         // iteration
     236           2 :         if (sigaut_ctx->signer.iteration <=
     237           2 :             N_current_signer_status.signer.iteration) {
     238           1 :             reset_signer_authorization(sigaut_ctx);
     239           1 :             THROW(ERR_SIGAUT_INVALID_ITERATION);
     240             :         }
     241             : 
     242             :         // Compute the hash that should be signed for this signer version
     243             :         // to be authorized
     244           1 :         generate_message_to_sign(sigaut_ctx);
     245             : 
     246           1 :         sigaut_ctx->state = sigaut_state_wait_signature;
     247             : 
     248           1 :         return TX_FOR_DATA_SIZE(0);
     249           6 :     case OP_SIGAUT_SIGN:
     250           6 :         check_state(sigaut_ctx, sigaut_state_wait_signature);
     251             : 
     252             :         // Check to see whether we find a matching authorized signer
     253           6 :         signature_valid = 0;
     254          15 :         for (unsigned int i = 0; i < TOTAL_AUTHORIZERS && !signature_valid;
     255           9 :              i++) {
     256             :             // Clear public key memory region first just in case initialization
     257             :             // fails
     258           9 :             explicit_bzero(&sigaut_ctx->pubkey, sizeof(sigaut_ctx->pubkey));
     259             :             // Init public key
     260           9 :             cx_ecfp_init_public_key(CX_CURVE_256K1,
     261           9 :                                     (unsigned char*)authorizers_pubkeys[i],
     262             :                                     sizeof(authorizers_pubkeys[i]),
     263             :                                     &sigaut_ctx->pubkey);
     264           0 :             signature_valid = cx_ecdsa_verify(&sigaut_ctx->pubkey,
     265             :                                               0,
     266             :                                               CX_NONE,
     267           9 :                                               sigaut_ctx->auth_hash,
     268             :                                               HASH_LENGTH,
     269             :                                               APDU_DATA_PTR,
     270           9 :                                               APDU_DATA_SIZE(rx));
     271             :             // Cleanup
     272           9 :             explicit_bzero(&sigaut_ctx->pubkey, sizeof(sigaut_ctx->pubkey));
     273             : 
     274             :             // Found a valid signature?
     275           9 :             if (signature_valid) {
     276           5 :                 sigaut_ctx->authorized_signer_verified[i] = true;
     277             :             }
     278             :         }
     279             : 
     280             :         // Reached the threshold?
     281           6 :         valid_count = 0;
     282          24 :         for (unsigned int i = 0; i < TOTAL_AUTHORIZERS; i++)
     283          18 :             if (sigaut_ctx->authorized_signer_verified[i])
     284           7 :                 valid_count++;
     285             : 
     286           6 :         if (valid_count >= THRESHOLD_AUTHORIZERS) {
     287             :             // Write the new authorized signer version and iteration to NVM,
     288             :             // reset the operation and signal success
     289           1 :             nvm_write(N_current_signer_status.signer.hash,
     290           1 :                       sigaut_ctx->signer.hash,
     291             :                       sizeof(N_current_signer_status.signer.hash));
     292           1 :             nvm_write(&N_current_signer_status.signer.iteration,
     293           1 :                       &sigaut_ctx->signer.iteration,
     294             :                       sizeof(N_current_signer_status.signer.iteration));
     295           1 :             reset_signer_authorization(sigaut_ctx);
     296           1 :             APDU_DATA_PTR[0] = RES_SIGAUT_SUCCESS;
     297             :         } else {
     298           5 :             APDU_DATA_PTR[0] = RES_SIGAUT_MORE;
     299             :         }
     300           6 :         return TX_FOR_DATA_SIZE(1);
     301           1 :     case OP_SIGAUT_GET_AUTH_COUNT:
     302           1 :         APDU_DATA_PTR[0] = (unsigned char)TOTAL_AUTHORIZERS;
     303           1 :         return TX_FOR_DATA_SIZE(1);
     304           3 :     case OP_SIGAUT_GET_AUTH_AT:
     305           3 :         if (APDU_DATA_SIZE(rx) != 1)
     306           0 :             THROW(ERR_UI_PROT_INVALID);
     307             : 
     308           3 :         auth_index = APDU_DATA_PTR[0];
     309           3 :         if (auth_index >= (uint8_t)TOTAL_AUTHORIZERS)
     310           0 :             THROW(ERR_SIGAUT_INVALID_AUTH_INVALID_INDEX);
     311             : 
     312           6 :         SAFE_MEMMOVE(APDU_DATA_PTR,
     313             :                      APDU_TOTAL_DATA_SIZE_OUT,
     314             :                      MEMMOVE_ZERO_OFFSET,
     315             :                      authorizers_pubkeys[auth_index],
     316             :                      sizeof(authorizers_pubkeys[auth_index]),
     317             :                      MEMMOVE_ZERO_OFFSET,
     318             :                      sizeof(authorizers_pubkeys[auth_index]),
     319             :                      THROW(ERR_UI_INTERNAL));
     320             : 
     321           3 :         return TX_FOR_DATA_SIZE(sizeof(authorizers_pubkeys[auth_index]));
     322           0 :     default:
     323           0 :         reset_signer_authorization(sigaut_ctx);
     324           0 :         THROW(ERR_UI_PROT_INVALID);
     325             :         break;
     326             :     }
     327             : }
     328             : 
     329             : /*
     330             :  * Tell whether the given signer hash is authorized to run
     331             :  * as per the current signer authorization status.
     332             :  *
     333             :  * @arg[in] signer_hash     the signer hash
     334             :  */
     335           2 : bool is_authorized_signer(unsigned char* signer_hash) {
     336             : #ifdef DEBUG_BUILD
     337             :     return true;
     338             : #else
     339           2 :     return !memcmp(N_current_signer_status.signer.hash,
     340             :                    signer_hash,
     341             :                    sizeof(N_current_signer_status.signer.hash));
     342             : #endif
     343             : }

Generated by: LCOV version 1.16