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 : }
|