LCOV - code coverage report
Current view: top level - powhsm/src - auth_trie.c (source / functions) Hit Total Coverage
Test: powHSM firmware Lines: 94 106 88.7 %
Date: 2025-07-10 13:49:13 Functions: 2 2 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/hash.h"
      28             : #include "hal/platform.h"
      29             : #include "hal/exceptions.h"
      30             : 
      31             : #include "auth.h"
      32             : #include "mem.h"
      33             : #include "memutil.h"
      34             : #include "bc_state.h"
      35             : 
      36             : #include "hal/log.h"
      37             : 
      38             : #define REQUEST_MORE_IF_NEED()                      \
      39             :     {                                               \
      40             :         if (apdu_offset >= APDU_DATA_SIZE(rx)) {    \
      41             :             SET_APDU_TXLEN(AUTH_MAX_EXCHANGE_SIZE); \
      42             :             return TX_FOR_TXLEN();                  \
      43             :         }                                           \
      44             :     }
      45             : 
      46             : #define FAIL_IF_LEAF()                             \
      47             :     {                                              \
      48             :         if (auth.trie.current_node == 0) {         \
      49             :             LOG("[E] Leaf node not a leaf\n");     \
      50             :             THROW(ERR_AUTH_RECEIPT_HASH_MISMATCH); \
      51             :         }                                          \
      52             :     }
      53             : 
      54             : #define IGNORE_IF_INTERNAL()            \
      55             :     {                                   \
      56             :         if (auth.trie.current_node > 0) \
      57             :             break;                      \
      58             :     }
      59             : 
      60        4690 : static void trie_cb(const trie_cb_event_t event) {
      61             :     // Update node hash
      62        4690 :     hash_keccak256_update(
      63        4690 :         &auth.trie.hash_ctx, auth.trie.ctx.raw, auth.trie.ctx.raw_size);
      64             : 
      65        4690 :     switch (event) {
      66          90 :     case TRIE_EV_FLAGS:
      67          90 :         if (TRIE_FG_VERSION(auth.trie.ctx.flags) != AUTH_TRIE_NODE_VERSION) {
      68           2 :             LOG("[E] Invalid node version: %u\n",
      69           2 :                 TRIE_FG_VERSION(auth.trie.ctx.flags));
      70           2 :             THROW(ERR_AUTH_NODE_INVALID_VERSION);
      71             :         }
      72             : 
      73          88 :         if (auth.trie.current_node == 0 &&
      74          40 :             (TRIE_FG_NODE_PRESENT_LEFT(auth.trie.ctx.flags) ||
      75          38 :              TRIE_FG_NODE_PRESENT_RIGHT(auth.trie.ctx.flags))) {
      76           2 :             LOG("[E] Leaf node not a leaf\n");
      77           2 :             THROW(ERR_AUTH_RECEIPT_HASH_MISMATCH);
      78             :         }
      79             : 
      80             :         // In a valid proof, the first node of the partial merkle proof
      81             :         // MUST be the leaf and contain the receipt as a value.
      82             :         // Any receipt is by definition more than 32 bytes
      83             :         // in size, and therefore a merkle proof node that contains it should
      84             :         // encode it as a long value (i.e., containing hash and length only)
      85             :         // Hence, getting here indicates the given proof is INVALID.
      86             :         // Fail indicating a receipt hash mismatch.
      87          86 :         if (auth.trie.current_node == 0 &&
      88          38 :             !TRIE_FG_HAS_LONG_VALUE(auth.trie.ctx.flags)) {
      89           2 :             LOG("[E] Leaf node must have a long value\n");
      90           2 :             THROW(ERR_AUTH_RECEIPT_HASH_MISMATCH);
      91             :         }
      92          84 :         break;
      93           0 :     case TRIE_EV_VALUE_START:
      94             :     case TRIE_EV_VALUE_DATA:
      95             :     case TRIE_EV_VALUE_END:
      96           0 :         IGNORE_IF_INTERNAL();
      97           0 :         LOG("[E] Leaf node must have a long value\n");
      98           0 :         THROW(ERR_AUTH_RECEIPT_HASH_MISMATCH);
      99             :         break;
     100          36 :     case TRIE_EV_VALUE_HASH_START:
     101          36 :         IGNORE_IF_INTERNAL();
     102          36 :         auth.trie.offset = 0;
     103          36 :         break;
     104        1152 :     case TRIE_EV_VALUE_HASH_DATA:
     105        1152 :         IGNORE_IF_INTERNAL();
     106        2304 :         SAFE_MEMMOVE(auth.trie.value_hash,
     107             :                      sizeof(auth.trie.value_hash),
     108             :                      auth.trie.offset,
     109             :                      auth.trie.ctx.raw,
     110             :                      sizeof(auth.trie.ctx.raw),
     111             :                      MEMMOVE_ZERO_OFFSET,
     112             :                      auth.trie.ctx.raw_size,
     113             :                      THROW(ERR_AUTH_INVALID_DATA_SIZE));
     114        1152 :         auth.trie.offset += auth.trie.ctx.raw_size;
     115        1152 :         break;
     116          36 :     case TRIE_EV_VALUE_HASH_END:
     117          36 :         IGNORE_IF_INTERNAL();
     118          36 :         if (memcmp(auth.receipt_hash,
     119             :                    auth.trie.value_hash,
     120             :                    sizeof(auth.trie.value_hash))) {
     121           2 :             LOG("[E] Receipt hash mismatch\n");
     122           2 :             THROW(ERR_AUTH_RECEIPT_HASH_MISMATCH);
     123             :         }
     124          34 :         break;
     125          16 :     case TRIE_EV_LEFT_NODE_START:
     126             :     case TRIE_EV_RIGHT_NODE_START:
     127          16 :         FAIL_IF_LEAF();
     128          16 :         auth.trie.offset = 0;
     129          16 :         break;
     130         512 :     case TRIE_EV_LEFT_NODE_DATA:
     131             :     case TRIE_EV_RIGHT_NODE_DATA:
     132         512 :         FAIL_IF_LEAF();
     133        1024 :         SAFE_MEMMOVE(auth.trie.child_hash,
     134             :                      sizeof(auth.trie.child_hash),
     135             :                      auth.trie.offset,
     136             :                      auth.trie.ctx.raw,
     137             :                      sizeof(auth.trie.ctx.raw),
     138             :                      MEMMOVE_ZERO_OFFSET,
     139             :                      auth.trie.ctx.raw_size,
     140             :                      THROW(ERR_AUTH_INVALID_DATA_SIZE));
     141         512 :         auth.trie.offset += auth.trie.ctx.raw_size;
     142         512 :         break;
     143          68 :     case TRIE_EV_LEFT_NODE_EMBEDDED_START:
     144             :     case TRIE_EV_RIGHT_NODE_EMBEDDED_START:
     145          68 :         FAIL_IF_LEAF();
     146          68 :         hash_keccak256_init(&auth.trie.aux_hash_ctx);
     147          68 :         break;
     148        2576 :     case TRIE_EV_LEFT_NODE_EMBEDDED_DATA:
     149             :     case TRIE_EV_RIGHT_NODE_EMBEDDED_DATA:
     150        2576 :         FAIL_IF_LEAF();
     151        2576 :         hash_keccak256_update(
     152        2576 :             &auth.trie.aux_hash_ctx, auth.trie.ctx.raw, auth.trie.ctx.raw_size);
     153        2576 :         break;
     154          84 :     case TRIE_EV_LEFT_NODE_END:
     155             :     case TRIE_EV_RIGHT_NODE_END:
     156             :     case TRIE_EV_LEFT_NODE_EMBEDDED_END:
     157             :     case TRIE_EV_RIGHT_NODE_EMBEDDED_END:
     158          84 :         FAIL_IF_LEAF();
     159          84 :         if (event == TRIE_EV_LEFT_NODE_EMBEDDED_END ||
     160             :             event == TRIE_EV_RIGHT_NODE_EMBEDDED_END)
     161          68 :             hash_keccak256_final(&auth.trie.aux_hash_ctx, auth.trie.child_hash);
     162          84 :         if (!memcmp(auth.trie.node_hash,
     163             :                     auth.trie.child_hash,
     164             :                     sizeof(auth.trie.node_hash)))
     165          46 :             auth.trie.num_linked++;
     166          84 :         break;
     167             :     }
     168        4682 : }
     169             : 
     170             : /*
     171             :  * Implement the partial merkle trie parsing portion
     172             :  * of the signing authorization protocol.
     173             :  *
     174             :  * @arg[in] rx      number of received bytes from the host
     175             :  * @ret             number of transmited bytes to the host
     176             :  */
     177          82 : unsigned int auth_sign_handle_merkleproof(volatile unsigned int rx) {
     178          82 :     uint8_t apdu_offset = 0;
     179             : 
     180          82 :     if (auth.state != STATE_AUTH_MERKLEPROOF) {
     181           0 :         LOG("[E] Expected to be in the MP state\n");
     182           0 :         THROW(ERR_AUTH_INVALID_STATE);
     183             :     }
     184             : 
     185             :     while (true) {
     186             :         // Read number of nodes (single byte)
     187         130 :         if (auth.trie.total_nodes == 0) {
     188          42 :             auth.trie.total_nodes = APDU_DATA_PTR[apdu_offset++];
     189          42 :             auth.trie.current_node = 0;
     190          42 :             auth.trie.state = AUTH_TRIE_STATE_NODE_LENGTH;
     191             : 
     192          42 :             REQUEST_MORE_IF_NEED();
     193             :         }
     194             : 
     195         130 :         if (auth.trie.state == AUTH_TRIE_STATE_NODE_LENGTH) {
     196             :             // Verify node is at least one byte long
     197          90 :             if (APDU_DATA_PTR[apdu_offset] == 0) {
     198           0 :                 LOG("[E] Got MP node length zero\n");
     199           0 :                 THROW(ERR_AUTH_INVALID_DATA_SIZE);
     200             :             }
     201          90 :             trie_init(&auth.trie.ctx, &trie_cb, APDU_DATA_PTR[apdu_offset++]);
     202          90 :             hash_keccak256_init(&auth.trie.hash_ctx);
     203          90 :             auth.trie.state = AUTH_TRIE_STATE_NODE;
     204          90 :             auth.trie.num_linked = 0;
     205             : 
     206          90 :             REQUEST_MORE_IF_NEED();
     207             :         }
     208             : 
     209         130 :         if (auth.trie.state == AUTH_TRIE_STATE_NODE) {
     210         130 :             apdu_offset += trie_consume(APDU_DATA_PTR + apdu_offset,
     211         130 :                                         APDU_DATA_SIZE(rx) - apdu_offset);
     212             : 
     213         122 :             if (trie_result() < 0) {
     214           0 :                 LOG("[E] Error parsing MP node: %u\n", trie_result());
     215             :                 // Reusing an existing error code due to legacy protocol
     216           0 :                 THROW(ERR_AUTH_RECEIPT_ROOT_MISMATCH);
     217         122 :             } else if (trie_result() == TRIE_ST_DONE) {
     218          82 :                 hash_keccak256_final(&auth.trie.hash_ctx, auth.trie.node_hash);
     219          82 :                 LOG("MP@%u ", auth.trie.current_node);
     220          82 :                 LOG_HEX(
     221             :                     "hash: ", auth.trie.node_hash, sizeof(auth.trie.node_hash));
     222             : 
     223          82 :                 if (auth.trie.current_node > 0) {
     224             :                     // If this is an internal node, check
     225             :                     // it was successfully linked with exactly
     226             :                     // one child
     227          48 :                     if (auth.trie.num_linked != 1) {
     228           2 :                         LOG("[E] Node chaining mismatch\n");
     229           2 :                         THROW(ERR_AUTH_NODE_CHAINING_MISMATCH);
     230             :                     }
     231             :                 }
     232             : 
     233          80 :                 auth.trie.current_node++;
     234             : 
     235             :                 // If this is the root, check it matches the current
     236             :                 // bc state's ancestor receipts root
     237          80 :                 if (auth.trie.current_node == auth.trie.total_nodes) {
     238          32 :                     if (!memcmp(N_bc_state.ancestor_receipt_root,
     239             :                                 auth.trie.node_hash,
     240             :                                 sizeof(N_bc_state.ancestor_receipt_root))) {
     241          32 :                         auth_transition_to(STATE_AUTH_SIGN);
     242          32 :                         return 0;
     243             :                     }
     244             : 
     245           0 :                     LOG("[E] Receipt root mismatch\n");
     246           0 :                     THROW(ERR_AUTH_RECEIPT_ROOT_MISMATCH);
     247             :                 }
     248             : 
     249          48 :                 auth.trie.state = AUTH_TRIE_STATE_NODE_LENGTH;
     250             :             }
     251             : 
     252          88 :             REQUEST_MORE_IF_NEED();
     253             :         }
     254             :     }
     255             : }

Generated by: LCOV version 1.16