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/platform.h"
      28             : #include "hal/exceptions.h"
      29             : 
      30             : #include "auth.h"
      31             : #include "auth_constants.h"
      32             : #include "mem.h"
      33             : #include "memutil.h"
      34             : #include "srlp.h"
      35             : #include "flags.h"
      36             : #include "util.h"
      37             : 
      38             : #include "hal/log.h"
      39             : 
      40             : // -----------------------------------------------------------------------
      41             : // RLP parser callbacks
      42             : // -----------------------------------------------------------------------
      43             : 
      44             : // Valid RSK receipts have the form [field_0, ..., field_5]
      45             : // (six fields in the top-level list).
      46             : //
      47             : // In particular, field_3 (the fourth top level field) has the form
      48             : // [log_0, ..., log_n], with n >= 0. This is the log list.
      49             : //
      50             : // Each log_m with 0 <= m <= n is of the form
      51             : // [address, topics, data] (exactly three elements)
      52             : // with:
      53             : // - address being the emitter address
      54             : // - topics: [topic_0, ..., topic_p] with p >=0 the list of topics
      55             : // - data being the unindexed data associated with the event (not used)
      56             : //
      57             : // In particular, we're interested in finding a log with:
      58             : // - a specific address (hardcoded, predefined) - the bridge address
      59             : // - exactly three topics, topic_0, topic_1 and topic_2, of which:
      60             : //   - topic_0 (aka the event signature) must match a specific
      61             : //     value (harcoded, predefined)
      62             : //   - topic_2 must match the current BTC tx hash
      63             : 
      64             : // RSK receipt constants
      65             : // All indexes and levels are 1-based
      66             : 
      67             : #define LIST_ELEMENTS (6)
      68             : #define LOG_ELEMENTS (3)
      69             : 
      70             : #define TOP_LEVEL (1)
      71             : #define LOG_LEVEL (3)
      72             : #define TOPIC_LEVEL (4)
      73             : 
      74             : #define LOGS_INDEX (4)
      75             : #define EVENT_EMITTER_INDEX (1)
      76             : #define TOPICS_INDEX (2)
      77             : #define TOPIC_SIGNATURE_INDEX (1)
      78             : #define TOPIC_TXHASH_INDEX (3)
      79             : 
      80             : // Flags
      81             : #define IS_INIT (0x01)
      82             : #define IS_VALID_EMITTER (0x02)
      83             : #define IS_VALID_SIGNATURE (0x04)
      84             : #define IS_VALID_TXHASH (0x08)
      85             : #define IS_MATCH (0x10)
      86             : #define TOP_LEVEL_LIST_SEEN (0x20)
      87             : 
      88             : __attribute__((always_inline)) static inline void update_indexes() {
      89         656 :     if (auth.receipt.level > 0)
      90         886 :         auth.receipt.index[auth.receipt.level - 1]++;
      91         940 :     for (uint8_t i = auth.receipt.level;
      92         940 :          i < MIN(auth.receipt.level, RECEIPT_MAX_DEPTH) - 1;
      93           0 :          i++)
      94           0 :         auth.receipt.index[i] = 0;
      95         940 : }
      96             : 
      97         284 : static void list_start(const uint16_t size) {
      98             :     update_indexes();
      99             : 
     100         284 :     ++auth.receipt.level;
     101             : 
     102             :     // About to start parsing a log? => clear the flags and counters
     103         284 :     if (auth.receipt.index[TOP_LEVEL - 1] == LOGS_INDEX &&
     104         230 :         auth.receipt.level == LOG_LEVEL) {
     105          92 :         CLR_FLAG(auth.receipt.flags, IS_VALID_EMITTER);
     106          92 :         CLR_FLAG(auth.receipt.flags, IS_VALID_SIGNATURE);
     107          92 :         CLR_FLAG(auth.receipt.flags, IS_VALID_TXHASH);
     108             :     }
     109             : 
     110             :     // Count total number of bytes remaining for the entire receipt
     111         284 :     if (auth.receipt.level == TOP_LEVEL) {
     112             :         // All receipts MUST consist of a single top-level list. Fail otherwise
     113          54 :         if (HAS_FLAG(auth.receipt.flags, TOP_LEVEL_LIST_SEEN)) {
     114           2 :             LOG("[E] Receipt had more than one top-level list\n");
     115           2 :             THROW(ERR_AUTH_RECEIPT_INVALID);
     116             :         }
     117          52 :         SET_FLAG(auth.receipt.flags, TOP_LEVEL_LIST_SEEN);
     118          52 :         auth.receipt.remaining_bytes = size + rlp_list_prefix_size(size);
     119             :     }
     120         282 : }
     121             : 
     122         282 : static void list_end() {
     123             :     // Have we just finished parsing a log? =>
     124             :     // set the match bit only if we found a match
     125         282 :     if (auth.receipt.index[TOP_LEVEL - 1] == LOGS_INDEX &&
     126         230 :         auth.receipt.level == LOG_LEVEL) {
     127             :         // Validate the parsed log had the exact number of elements
     128          92 :         if (auth.receipt.index[LOG_LEVEL - 1] != LOG_ELEMENTS) {
     129           0 :             LOG("[E] Found log with %u elements\n",
     130           0 :                 auth.receipt.index[LOG_LEVEL - 1]);
     131           0 :             THROW(ERR_AUTH_RECEIPT_INVALID);
     132             :         }
     133             : 
     134          92 :         if (HAS_FLAG(auth.receipt.flags, IS_VALID_EMITTER) &&
     135          88 :             HAS_FLAG(auth.receipt.flags, IS_VALID_SIGNATURE) &&
     136          54 :             HAS_FLAG(auth.receipt.flags, IS_VALID_TXHASH)) {
     137          42 :             SET_FLAG(auth.receipt.flags, IS_MATCH);
     138             :         }
     139             :     }
     140             : 
     141             :     // Validate the parsed receipt had the exact number of
     142             :     // top level elements
     143         282 :     if (auth.receipt.level == TOP_LEVEL &&
     144          52 :         auth.receipt.index[TOP_LEVEL - 1] != LIST_ELEMENTS) {
     145           2 :         LOG("[E] Receipt had %u elements\n", auth.receipt.index[TOP_LEVEL - 1]);
     146           2 :         THROW(ERR_AUTH_RECEIPT_INVALID);
     147             :     }
     148             : 
     149         280 :     --auth.receipt.level;
     150             : 
     151             :     // Reset indexes of lower levels
     152         894 :     for (uint8_t i = auth.receipt.level; i < RECEIPT_MAX_DEPTH; i++)
     153         614 :         auth.receipt.index[i] = 0;
     154         280 : }
     155             : 
     156         656 : static void str_start(const uint16_t size) {
     157             :     UNUSED(size);
     158             : 
     159             :     // Top level must be a list
     160         656 :     if (auth.receipt.level == 0) {
     161           0 :         LOG("[E] Receipt not a list\n");
     162           0 :         THROW(ERR_AUTH_RECEIPT_INVALID);
     163             :     }
     164             : 
     165             :     update_indexes();
     166             : 
     167             :     // Save strings only for desired fields
     168         656 :     if (auth.receipt.index[TOP_LEVEL - 1] == LOGS_INDEX &&
     169         402 :         auth.receipt.level >= LOG_LEVEL) {
     170         398 :         auth.receipt.aux_offset = 0;
     171             :     }
     172         656 : }
     173             : 
     174         918 : static void str_chunk(const uint8_t* chunk, const size_t size) {
     175             :     // Save strings only for desired fields
     176             :     // Also prevent erroring while saving
     177             :     // undesired fields that could be too big
     178         918 :     if (auth.receipt.index[TOP_LEVEL - 1] == LOGS_INDEX &&
     179         526 :         auth.receipt.level >= LOG_LEVEL &&
     180         522 :         auth.receipt.aux_offset + size <= sizeof(auth.receipt.aux)) {
     181        1036 :         SAFE_MEMMOVE(auth.receipt.aux,
     182             :                      sizeof(auth.receipt.aux),
     183             :                      auth.receipt.aux_offset,
     184             :                      chunk,
     185             :                      size,
     186             :                      MEMMOVE_ZERO_OFFSET,
     187             :                      size,
     188             :                      THROW(ERR_AUTH_INVALID_DATA_SIZE));
     189         518 :         auth.receipt.aux_offset += size;
     190             :     }
     191         918 : }
     192             : 
     193         656 : static void str_end() {
     194             :     // Compare values with expected values
     195         656 :     if (auth.receipt.index[TOP_LEVEL - 1] == LOGS_INDEX &&
     196         402 :         auth.receipt.level >= LOG_LEVEL) {
     197         398 :         switch (auth.receipt.level) {
     198         184 :         case LOG_LEVEL:
     199         184 :             if (auth.receipt.index[LOG_LEVEL - 1] == EVENT_EMITTER_INDEX &&
     200          92 :                 auth.receipt.aux_offset == sizeof(EVENT_EMITTER) &&
     201          92 :                 !memcmp(auth.receipt.aux,
     202             :                         (void*)PIC(EVENT_EMITTER),
     203             :                         sizeof(EVENT_EMITTER)))
     204          88 :                 SET_FLAG(auth.receipt.flags, IS_VALID_EMITTER);
     205         184 :             break;
     206         214 :         case TOPIC_LEVEL:
     207         214 :             if (auth.receipt.index[LOG_LEVEL - 1] == TOPICS_INDEX) {
     208         214 :                 if (auth.receipt.index[TOPIC_LEVEL - 1] ==
     209          92 :                         TOPIC_SIGNATURE_INDEX &&
     210          92 :                     auth.receipt.aux_offset == sizeof(EVENT_SIGNATURE) &&
     211          92 :                     !memcmp(auth.receipt.aux,
     212             :                             (void*)PIC(EVENT_SIGNATURE),
     213             :                             sizeof(EVENT_SIGNATURE)))
     214          56 :                     SET_FLAG(auth.receipt.flags, IS_VALID_SIGNATURE);
     215         158 :                 else if (auth.receipt.index[TOPIC_LEVEL - 1] ==
     216          60 :                              TOPIC_TXHASH_INDEX &&
     217          60 :                          auth.receipt.aux_offset == sizeof(auth.tx_hash) &&
     218          60 :                          !memcmp(auth.receipt.aux,
     219             :                                  auth.tx_hash,
     220             :                                  sizeof(auth.tx_hash)))
     221          46 :                     SET_FLAG(auth.receipt.flags, IS_VALID_TXHASH);
     222             :             }
     223         214 :             break;
     224             :         }
     225             :     }
     226         656 : }
     227             : 
     228             : static const rlp_callbacks_t callbacks = {
     229             :     str_start,
     230             :     str_chunk,
     231             :     str_end,
     232             :     list_start,
     233             :     list_end,
     234             : };
     235             : 
     236             : /*
     237             :  * Implement the RSK receipt parsing and validation portion of the signing
     238             :  * authorization protocol.
     239             :  *
     240             :  * @arg[in] rx      number of received bytes from the host
     241             :  * @ret             number of transmited bytes to the host
     242             :  */
     243         340 : unsigned int auth_sign_handle_receipt(volatile unsigned int rx) {
     244         340 :     if (auth.state != STATE_AUTH_RECEIPT) {
     245           0 :         LOG("[E] Expected to be in the receipt state\n");
     246           0 :         THROW(ERR_AUTH_INVALID_STATE);
     247             :     }
     248             : 
     249         340 :     if (!HAS_FLAG(auth.receipt.flags, IS_INIT)) {
     250          52 :         rlp_start(&callbacks);
     251          52 :         hash_keccak256_init(&auth.receipt.hash_ctx);
     252          52 :         SET_FLAG(auth.receipt.flags, IS_INIT);
     253             :     }
     254             : 
     255         340 :     int res = rlp_consume(APDU_DATA_PTR, APDU_DATA_SIZE(rx));
     256         336 :     if (res < 0) {
     257           0 :         LOG("[E] RLP parser returned error %d\n", res);
     258           0 :         THROW(ERR_AUTH_RECEIPT_RLP);
     259             :     }
     260         336 :     auth.receipt.remaining_bytes -= APDU_DATA_SIZE(rx);
     261             : 
     262           0 :     hash_keccak256_update(
     263         336 :         &auth.receipt.hash_ctx, APDU_DATA_PTR, APDU_DATA_SIZE(rx));
     264             : 
     265         336 :     if (auth.receipt.remaining_bytes == 0) {
     266          48 :         if (HAS_FLAG(auth.receipt.flags, IS_MATCH)) {
     267             :             // Finalize the hash calculation
     268          42 :             hash_keccak256_final(&auth.receipt.hash_ctx, auth.receipt_hash);
     269             : 
     270             :             // Log hash for debugging purposes
     271          42 :             LOG_HEX(
     272             :                 "Receipt hash: ", auth.receipt_hash, sizeof(auth.receipt_hash));
     273             : 
     274             :             // Request RSK transaction receipt
     275          42 :             SET_APDU_OP(P1_MERKLEPROOF);
     276          42 :             SET_APDU_TXLEN(AUTH_MAX_EXCHANGE_SIZE);
     277          42 :             auth.expected_bytes = APDU_TXLEN();
     278          42 :             auth_transition_to(STATE_AUTH_MERKLEPROOF);
     279          42 :             return TX_FOR_TXLEN();
     280             :         }
     281             : 
     282             :         // No match
     283           6 :         LOG("[E] No log match found in the receipt\n");
     284             :         // To comply with the legacy implementation
     285           6 :         THROW(ERR_AUTH_INVALID_DATA_SIZE);
     286             :     }
     287             : 
     288         288 :     SET_APDU_TXLEN(MIN(auth.receipt.remaining_bytes, AUTH_MAX_EXCHANGE_SIZE));
     289         288 :     auth.expected_bytes = APDU_TXLEN();
     290         288 :     return TX_FOR_TXLEN();
     291             : }
       |