LCOV - code coverage report
Current view: top level - signer/src - srlp.c (source / functions) Hit Total Coverage
Test: powHSM firmware Lines: 49 64 76.6 %
Date: 2024-04-05 20:46:34 Functions: 2 4 50.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 <stddef.h>
      26             : #include <stdint.h>
      27             : #include <string.h>
      28             : 
      29             : #include "srlp.h"
      30             : 
      31             : #include "os.h"
      32             : 
      33             : #include "dbg.h"
      34             : 
      35             : // This code tinkers with function pointers referenced from
      36             : // const data structures, so we need to use the PIC macro.
      37             : // See:
      38             : // https://ledger.readthedocs.io/en/latest/userspace/memory.html?highlight=PIC#pic-and-model-implications
      39             : 
      40             : // Parser state is modeled as a stack of context items of the form:
      41             : //
      42             : //  { state, size, cursor }, where:
      43             : //
      44             : //  - state: signals what the parser is currently parsing
      45             : //  - size: size in bytes of the structure being parsed
      46             : //  - cursor: number of bytes consumed for the structure being parsed
      47             : //
      48             : // At the bottom of the stack there's always a mark used to prevent a
      49             : // stack underflow. The stack size is limited to MAX_RLP_CTX_DEPTH
      50             : // items (see srlp.h).
      51             : 
      52             : // Context item stack and stack pointer
      53             : static uint8_t rlp_ctx_ptr;
      54             : static rlp_ctx_t rlp_ctx[MAX_RLP_CTX_DEPTH + 1];
      55             : 
      56             : // Pointer to the beginning of the next chunk to report
      57             : static uint8_t* rlp_frame_start;
      58             : 
      59             : // User callbacks
      60             : static const rlp_callbacks_t* rlp_callbacks;
      61             : 
      62             : /*
      63             :  * Initialize the parser.
      64             :  *
      65             :  * @arg[in] cbs struct holding callbacks to be called by the parser
      66             :  */
      67          15 : void rlp_start(const rlp_callbacks_t* cbs) {
      68          15 :     rlp_callbacks = cbs;
      69          15 :     rlp_ctx_ptr = 0;
      70          15 :     explicit_bzero(rlp_ctx, sizeof(rlp_ctx));
      71          15 :     rlp_ctx[rlp_ctx_ptr].state = RLP_BOTTOM;
      72          15 : }
      73             : 
      74             : // Notify the user about a trivial bytearray (length = 0 or 1).
      75             : // Defined as a macro to save stack space.
      76             : #define TRIVIAL_BYTEARRAY(bytearray, len)                                      \
      77             :     {                                                                          \
      78             :         ((rlp_start_cb_t)PIC(rlp_callbacks->bytearray_start))(len);            \
      79             :         ((rlp_chunk_cb_t)PIC(rlp_callbacks->bytearray_chunk))(bytearray, len); \
      80             :         ((rlp_end_cb_t)PIC(rlp_callbacks->bytearray_end))();                   \
      81             :     }
      82             : 
      83             : // Push a context item to the stack. Defined as a macro to save stack.
      84             : // NOTE: Returns if stack overflow.
      85             : //
      86             : //   @arg st state of context item to push
      87             : //   @arg sz size in bytes of the item top parse
      88             : //
      89             : #define RLP_PUSH_CTX(st, sz)                    \
      90             :     {                                           \
      91             :         if (rlp_ctx_ptr == MAX_RLP_CTX_DEPTH) { \
      92             :             return RLP_STACK_OVERFLOW;          \
      93             :         }                                       \
      94             :         ++rlp_ctx_ptr;                          \
      95             :         rlp_ctx[rlp_ctx_ptr].state = (st);      \
      96             :         rlp_ctx[rlp_ctx_ptr].size = (sz);       \
      97             :         rlp_ctx[rlp_ctx_ptr].cursor = 0;        \
      98             :     }
      99             : 
     100             : // Pop current context. Defined as a macro to save stack space.
     101             : // NOTE: Returns if stack underflow.
     102             : //
     103             : #define RLP_POP_CTX()                                            \
     104             :     {                                                            \
     105             :         if (rlp_ctx_ptr == 0) {                                  \
     106             :             return RLP_STACK_UNDERFLOW;                          \
     107             :         }                                                        \
     108             :         rlp_state_t state = rlp_ctx[rlp_ctx_ptr].state;          \
     109             :         if (state == RLP_LIST) {                                 \
     110             :             ((rlp_end_cb_t)PIC(rlp_callbacks->list_end))();      \
     111             :         } else if (state == RLP_STR) {                           \
     112             :             ((rlp_end_cb_t)PIC(rlp_callbacks->bytearray_end))(); \
     113             :         }                                                        \
     114             :         --rlp_ctx_ptr;                                           \
     115             :     }
     116             : 
     117             : // Called for every consumed byte of the input buffer. Traverse stack from
     118             : // top to bottom, incrementing the number of consumed bytes for each list
     119             : // context.
     120             : // If consumed bytes reaches list size on the top context, pop it.
     121             : // If consumed bytes reaches list size on a non-top context, fail due to
     122             : // inconsistency.
     123             : //
     124             : // Defined as a macro to save stack.
     125             : // NOTE: Returns if stack underflow
     126             : //
     127             : #define RLP_UPDATE_LISTS()                                    \
     128             :     {                                                         \
     129             :         int __ix;                                             \
     130             :         for (__ix = rlp_ctx_ptr; __ix >= 0; --__ix) {         \
     131             :             if (rlp_ctx[__ix].state != RLP_LIST) {            \
     132             :                 continue;                                     \
     133             :             }                                                 \
     134             :             ++rlp_ctx[__ix].cursor;                           \
     135             :             if (rlp_ctx[__ix].cursor == rlp_ctx[__ix].size) { \
     136             :                 if (__ix == rlp_ctx_ptr) {                    \
     137             :                     RLP_POP_CTX();                            \
     138             :                 } else {                                      \
     139             :                     return RLP_MALFORMED;                     \
     140             :                 }                                             \
     141             :             }                                                 \
     142             :         }                                                     \
     143             :     }
     144             : 
     145             : // Handle beginning of list. Defined as  a macro to save stack space.
     146             : #define HANDLE_RLP_LIST(b)                                                    \
     147             :     {                                                                         \
     148             :         if (*b <= 0x7F) {                                                     \
     149             :             TRIVIAL_BYTEARRAY(rlp_frame_start, 1);                            \
     150             :         } else if (*b == 0x80) {                                              \
     151             :             TRIVIAL_BYTEARRAY(rlp_frame_start, 0);                            \
     152             :         } else if (*b <= 0xB7) {                                              \
     153             :             RLP_PUSH_CTX(RLP_STR, *b - 0x80);                                 \
     154             :             ((rlp_start_cb_t)PIC(rlp_callbacks->bytearray_start))(*b - 0x80); \
     155             :         } else if (*b <= 0xBF) {                                              \
     156             :             if (*b - 0xB7 > sizeof(uint16_t)) {                               \
     157             :                 return RLP_TOO_LONG;                                          \
     158             :             }                                                                 \
     159             :             RLP_PUSH_CTX(RLP_STR_LEN, *b - 0xB7);                             \
     160             :         } else if (*b <= 0xF7) {                                              \
     161             :             RLP_PUSH_CTX(RLP_LIST, *b - 0xC0 + 1);                            \
     162             :             ((rlp_start_cb_t)PIC(rlp_callbacks->list_start))(*b - 0xC0);      \
     163             :         } else {                                                              \
     164             :             if (*b - 0xF7 > sizeof(uint16_t)) {                               \
     165             :                 return RLP_TOO_LONG;                                          \
     166             :             }                                                                 \
     167             :             RLP_PUSH_CTX(RLP_LIST_LEN, *b - 0xF7);                            \
     168             :         }                                                                     \
     169             :         rlp_frame_start = b + 1;                                              \
     170             :     }
     171             : 
     172             : // Handle beginning of byte array. Defined as a macero to save space.
     173             : #define HANDLE_RLP_STR(b)                                               \
     174             :     {                                                                   \
     175             :         ++rlp_ctx[rlp_ctx_ptr].cursor;                                  \
     176             :         if (rlp_ctx[rlp_ctx_ptr].size == rlp_ctx[rlp_ctx_ptr].cursor) { \
     177             :             ((rlp_chunk_cb_t)PIC(rlp_callbacks->bytearray_chunk))(      \
     178             :                 rlp_frame_start, b - rlp_frame_start + 1);              \
     179             :             RLP_POP_CTX();                                              \
     180             :             rlp_frame_start = b + 1;                                    \
     181             :         }                                                               \
     182             :     }
     183             : 
     184             : /*
     185             :  * Consume a chunk of bytes.
     186             :  *
     187             :  * @arg[in] buf: buffer holdoing bytes to be consumed
     188             :  * @arg[in] len: number of bytes to consume in buffer
     189             :  *
     190             :  * @return
     191             :  *    RLP_OK if bytes were consumed successfully
     192             :  *    RLP_TOO_LONG if len greater than RLP_BUFFER_SIZE
     193             :  *    RLP_STACK_OVERFLOW if list nesting level is greater than MAX_RLP_CTX_DEPTH
     194             :  *    RLP_STACK_UNDERFLOW if RLP to parse is ill-formed (e.g., [[a])
     195             :  *    RLP_MALFORMED if RLP to parse is ill-formed wrt reported sizes
     196             :  */
     197         735 : int rlp_consume(uint8_t* buf, const uint8_t len) {
     198         735 :     if (len > RLP_BUFFER_SIZE) {
     199           0 :         return RLP_TOO_LONG;
     200             :     }
     201             : 
     202         735 :     rlp_frame_start = buf;
     203        7995 :     for (uint8_t i = 0; i < len; ++i) {
     204        7261 :         uint8_t* b = buf + i;
     205             : 
     206             :         LOG_SRLP_CTX(*b, rlp_ctx, rlp_ctx_ptr);
     207             : 
     208        7261 :         switch (rlp_ctx[rlp_ctx_ptr].state) {
     209         159 :         case RLP_BOTTOM:
     210             :         case RLP_LIST:
     211         159 :             HANDLE_RLP_LIST(b);
     212         159 :             break;
     213          25 :         case RLP_LIST_LEN:
     214          25 :             if (rlp_ctx[rlp_ctx_ptr].size == 0) {
     215           9 :                 rlp_ctx[rlp_ctx_ptr].state = RLP_LIST;
     216           9 :                 rlp_ctx[rlp_ctx_ptr].size = rlp_ctx[rlp_ctx_ptr].cursor;
     217           9 :                 rlp_ctx[rlp_ctx_ptr].cursor = 0;
     218           9 :                 rlp_frame_start = b;
     219           9 :                 ((rlp_start_cb_t)PIC(rlp_callbacks->list_start))(
     220           9 :                     rlp_ctx[rlp_ctx_ptr].size);
     221           9 :                 HANDLE_RLP_LIST(b);
     222             :             } else {
     223          16 :                 rlp_ctx[rlp_ctx_ptr].size -= 1;
     224          16 :                 rlp_ctx[rlp_ctx_ptr].cursor =
     225          16 :                     (rlp_ctx[rlp_ctx_ptr].cursor << 8) | *b;
     226             :             }
     227          25 :             break;
     228        7006 :         case RLP_STR:
     229        7006 :             HANDLE_RLP_STR(b);
     230        7006 :             break;
     231          71 :         case RLP_STR_LEN:
     232          71 :             if (rlp_ctx[rlp_ctx_ptr].size == 0) {
     233          29 :                 rlp_ctx[rlp_ctx_ptr].state = RLP_STR;
     234          29 :                 rlp_ctx[rlp_ctx_ptr].size = rlp_ctx[rlp_ctx_ptr].cursor;
     235          29 :                 rlp_ctx[rlp_ctx_ptr].cursor = 0;
     236          29 :                 rlp_frame_start = b;
     237          29 :                 ((rlp_start_cb_t)PIC(rlp_callbacks->bytearray_start))(
     238          29 :                     rlp_ctx[rlp_ctx_ptr].size);
     239          29 :                 HANDLE_RLP_STR(b);
     240             :             } else {
     241          42 :                 rlp_ctx[rlp_ctx_ptr].size -= 1;
     242          42 :                 rlp_ctx[rlp_ctx_ptr].cursor =
     243          42 :                     (rlp_ctx[rlp_ctx_ptr].cursor << 8) | *b;
     244             :             }
     245          71 :             break;
     246             :         }
     247             : 
     248             :         // Increment consumed bytes for all lists in the context
     249       28790 :         RLP_UPDATE_LISTS();
     250             :     }
     251             : 
     252             :     // If the item being parsed is a byte array, notify about the seen chunk.
     253         734 :     if (rlp_ctx[rlp_ctx_ptr].state == RLP_STR) {
     254         692 :         ((rlp_chunk_cb_t)PIC(rlp_callbacks->bytearray_chunk))(
     255         692 :             rlp_frame_start, buf + len - rlp_frame_start);
     256             :     }
     257             : 
     258         734 :     return RLP_OK;
     259             : }
     260             : 
     261             : /*
     262             :  * Guess the length in bytes of the RLP prefix for str of the given size.
     263             :  *
     264             :  * NOTE: This guessing because for single byte strings we need the str
     265             :  * value to determine accurately. For single byte strings, this method
     266             :  * always return one. It's up to the caller to take this into account.
     267             :  *
     268             :  * @arg[in] str_size string size
     269             :  */
     270           0 : uint8_t guess_rlp_str_prefix_size(uint16_t str_size) {
     271           0 :     if (str_size == 0)
     272           0 :         return 1;
     273           0 :     else if (str_size == 1)
     274             :         // Guessing happens here: we should return zero if
     275             :         // the string's single byte is less or equals 0x7F.
     276           0 :         return 1;
     277           0 :     else if (str_size <= 55)
     278           0 :         return 1;
     279             :     else {
     280             :         uint8_t n;
     281           0 :         for (n = 0; str_size != 0; str_size >>= 8, ++n)
     282             :             ;
     283           0 :         return n + 1;
     284             :     }
     285             : }
     286             : 
     287             : /*
     288             :  * Get the length in bytes of the (minimal) RLP prefix for a list of the
     289             :  * given size (max size for any given list is 2^16-1 in this
     290             :  * implementation)
     291             :  *
     292             :  * @arg[in] list_size list size
     293             :  */
     294           0 : uint8_t rlp_list_prefix_size(uint16_t list_size) {
     295           0 :     if (list_size <= 55)
     296           0 :         return 1;
     297             : 
     298             :     uint8_t n;
     299           0 :     for (n = 0; list_size != 0; list_size >>= 8, ++n)
     300             :         ;
     301           0 :     return n + 1;
     302             : }

Generated by: LCOV version 1.14