cryptnox-sdk-esp32 1.0.0
ESP32 SDK for Cryptnox Hardware Wallet
Loading...
Searching...
No Matches
eth_rlp.cpp
Go to the documentation of this file.
1/*
2 * SPDX-License-Identifier: LGPL-3.0-or-later
3 * Copyright (c) 2026 Cryptnox SA
4 */
5
6#include "eth_rlp.h"
7#include <string.h>
8
9/* Maximum scratch buffer for the list content before prepending the header. */
10#define ITEMS_BUF_MAX 320U
11
12/******************************************************************
13 * Internal helpers
14 ******************************************************************/
15
16/*
17 * Write 'value' as a minimal big-endian byte sequence into out[0..].
18 * Returns the number of bytes written (1–8). Value 0 produces one 0x00 byte;
19 * the caller is responsible for treating the empty-integer case (value==0
20 * → RLP 0x80) before calling this function.
21 */
22static uint8_t be_minimal(uint64_t value, uint8_t out[8])
23{
24 uint8_t tmp[8];
25 uint8_t first = 0U;
26 uint8_t i;
27
28 tmp[0] = (uint8_t)((value >> 56U) & 0xFFU);
29 tmp[1] = (uint8_t)((value >> 48U) & 0xFFU);
30 tmp[2] = (uint8_t)((value >> 40U) & 0xFFU);
31 tmp[3] = (uint8_t)((value >> 32U) & 0xFFU);
32 tmp[4] = (uint8_t)((value >> 24U) & 0xFFU);
33 tmp[5] = (uint8_t)((value >> 16U) & 0xFFU);
34 tmp[6] = (uint8_t)((value >> 8U) & 0xFFU);
35 tmp[7] = (uint8_t)(value & 0xFFU);
36
37 while ((first < 7U) && (tmp[first] == 0U)) {
38 first++;
39 }
40 uint8_t count = (uint8_t)(8U - first);
41 for (i = 0U; i < count; i++) {
42 out[i] = tmp[(size_t)first + i];
43 }
44 return count;
45}
46
47/*
48 * RLP-encode a byte string of length data_len at 'data' into out.
49 * Returns bytes written.
50 */
51static size_t rlp_bytes(const uint8_t *data, size_t data_len, uint8_t *out)
52{
53 size_t written = 0U;
54
55 if (data_len == 0U) {
56 out[0] = 0x80U;
57 written = 1U;
58 } else if ((data_len == 1U) && (data[0] < 0x80U)) {
59 out[0] = data[0];
60 written = 1U;
61 } else if (data_len <= 55U) {
62 out[0] = (uint8_t)(0x80U + data_len);
63 (void)memcpy(out + 1U, data, data_len);
64 written = 1U + data_len;
65 } else {
66 /* Long string: 0xb7 + len(length) bytes, then length, then data. */
67 uint8_t len_bytes[8];
68 uint8_t num_lb = be_minimal((uint64_t)data_len, len_bytes);
69 out[0] = (uint8_t)(0xB7U + num_lb);
70 (void)memcpy(out + 1U, len_bytes, num_lb);
71 (void)memcpy(out + 1U + num_lb, data, data_len);
72 written = 1U + num_lb + data_len;
73 }
74 return written;
75}
76
77/*
78 * RLP-encode a non-negative integer (uint64_t) into out.
79 * 0 is encoded as the empty byte string (0x80).
80 * Returns bytes written.
81 */
82static size_t rlp_uint64(uint64_t value, uint8_t *out)
83{
84 if (value == 0U) {
85 out[0] = 0x80U;
86 return 1U;
87 }
88 uint8_t be[8];
89 uint8_t be_len = be_minimal(value, be);
90 return rlp_bytes(be, be_len, out);
91}
92
93/*
94 * RLP-encode a 32-byte big-endian integer (e.g. r or s from ECDSA) into out.
95 * Leading zero bytes are stripped so the encoding is minimal.
96 * Returns bytes written.
97 */
98static size_t rlp_int256(const uint8_t data[32], uint8_t *out)
99{
100 size_t first = 0U;
101 while ((first < 31U) && (data[first] == 0U)) {
102 first++;
103 }
104 if ((first == 31U) && (data[31] == 0U)) {
105 /* Value is zero */
106 out[0] = 0x80U;
107 return 1U;
108 }
109 return rlp_bytes(data + first, 32U - first, out);
110}
111
112/*
113 * Write the RLP list header for a list whose content is content_len bytes.
114 * Returns bytes written (does NOT write the content).
115 */
116static size_t rlp_list_header(size_t content_len, uint8_t *out)
117{
118 if (content_len <= 55U) {
119 out[0] = (uint8_t)(0xC0U + content_len);
120 return 1U;
121 }
122 uint8_t len_bytes[8];
123 uint8_t num_lb = be_minimal((uint64_t)content_len, len_bytes);
124 out[0] = (uint8_t)(0xF7U + num_lb);
125 (void)memcpy(out + 1U, len_bytes, num_lb);
126 return 1U + num_lb;
127}
128
129/******************************************************************
130 * Shared field encoder
131 ******************************************************************/
132
133static size_t encode_common_fields(const eth_tx_t *tx, uint8_t *items)
134{
135 size_t n = 0U;
136 n += rlp_uint64(tx->chain_id, items + n);
137 n += rlp_uint64(tx->nonce, items + n);
138 n += rlp_uint64(tx->max_priority_fee, items + n);
139 n += rlp_uint64(tx->max_fee, items + n);
140 n += rlp_uint64(tx->gas_limit, items + n);
141 n += rlp_bytes(tx->to, 20U, items + n);
142 n += rlp_uint64(tx->eth_value, items + n);
143 n += rlp_bytes(tx->calldata, tx->calldata_len, items + n);
144 items[n++] = 0xC0U; /* empty access list */
145 return n;
146}
147
148/******************************************************************
149 * Public API
150 ******************************************************************/
151
152size_t eth_rlp_encode_unsigned(const eth_tx_t *tx, uint8_t *out, size_t out_max)
153{
154 uint8_t items[ITEMS_BUF_MAX];
155 size_t items_len = encode_common_fields(tx, items);
156
157 uint8_t hdr[10];
158 size_t hdr_len = rlp_list_header(items_len, hdr);
159
160 size_t total = 1U + hdr_len + items_len; /* 0x02 prefix + list */
161 if (total > out_max) { return 0U; }
162
163 out[0] = 0x02U; /* EIP-1559 type prefix */
164 (void)memcpy(out + 1U, hdr, hdr_len);
165 (void)memcpy(out + 1U + hdr_len, items, items_len);
166 return total;
167}
168
169size_t eth_rlp_encode_signed(const eth_tx_t *tx, uint8_t v,
170 const uint8_t r[32], const uint8_t s[32],
171 uint8_t *out, size_t out_max)
172{
173 uint8_t items[ITEMS_BUF_MAX];
174 size_t items_len = encode_common_fields(tx, items);
175
176 items_len += rlp_uint64((uint64_t)v, items + items_len);
177 items_len += rlp_int256(r, items + items_len);
178 items_len += rlp_int256(s, items + items_len);
179
180 uint8_t hdr[10];
181 size_t hdr_len = rlp_list_header(items_len, hdr);
182
183 size_t total = 1U + hdr_len + items_len;
184 if (total > out_max) { return 0U; }
185
186 out[0] = 0x02U;
187 (void)memcpy(out + 1U, hdr, hdr_len);
188 (void)memcpy(out + 1U + hdr_len, items, items_len);
189 return total;
190}
size_t eth_rlp_encode_signed(const eth_tx_t *tx, uint8_t v, const uint8_t r[32], const uint8_t s[32], uint8_t *out, size_t out_max)
Definition eth_rlp.cpp:169
static uint8_t be_minimal(uint64_t value, uint8_t out[8])
Definition eth_rlp.cpp:22
static size_t rlp_bytes(const uint8_t *data, size_t data_len, uint8_t *out)
Definition eth_rlp.cpp:51
static size_t rlp_list_header(size_t content_len, uint8_t *out)
Definition eth_rlp.cpp:116
#define ITEMS_BUF_MAX
Definition eth_rlp.cpp:10
static size_t rlp_int256(const uint8_t data[32], uint8_t *out)
Definition eth_rlp.cpp:98
size_t eth_rlp_encode_unsigned(const eth_tx_t *tx, uint8_t *out, size_t out_max)
Definition eth_rlp.cpp:152
static size_t encode_common_fields(const eth_tx_t *tx, uint8_t *items)
Definition eth_rlp.cpp:133
static size_t rlp_uint64(uint64_t value, uint8_t *out)
Definition eth_rlp.cpp:82
size_t calldata_len
Definition eth_rlp.h:25
uint64_t gas_limit
Definition eth_rlp.h:21
uint64_t eth_value
Definition eth_rlp.h:23
const uint8_t * calldata
Definition eth_rlp.h:24
uint64_t max_fee
Definition eth_rlp.h:20
uint64_t max_priority_fee
Definition eth_rlp.h:19
uint64_t chain_id
Definition eth_rlp.h:17
uint8_t to[20]
Definition eth_rlp.h:22
uint64_t nonce
Definition eth_rlp.h:18