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 :
87 : __attribute__((always_inline)) static inline void update_indexes() {
88 644 : if (auth.receipt.level > 0)
89 874 : auth.receipt.index[auth.receipt.level - 1]++;
90 924 : for (uint8_t i = auth.receipt.level;
91 924 : i < MIN(auth.receipt.level, RECEIPT_MAX_DEPTH) - 1;
92 0 : i++)
93 0 : auth.receipt.index[i] = 0;
94 924 : }
95 :
96 280 : static void list_start(const uint16_t size) {
97 : update_indexes();
98 :
99 280 : ++auth.receipt.level;
100 :
101 : // About to start parsing a log? => clear the flags and counters
102 280 : if (auth.receipt.index[TOP_LEVEL - 1] == LOGS_INDEX &&
103 230 : auth.receipt.level == LOG_LEVEL) {
104 92 : CLR_FLAG(auth.receipt.flags, IS_VALID_EMITTER);
105 92 : CLR_FLAG(auth.receipt.flags, IS_VALID_SIGNATURE);
106 92 : CLR_FLAG(auth.receipt.flags, IS_VALID_TXHASH);
107 : }
108 :
109 : // Count total number of bytes remaining for the entire receipt
110 280 : if (auth.receipt.level == TOP_LEVEL) {
111 50 : auth.receipt.remaining_bytes = size + rlp_list_prefix_size(size);
112 : }
113 280 : }
114 :
115 280 : static void list_end() {
116 : // Have we just finished parsing a log? =>
117 : // set the match bit only if we found a match
118 280 : if (auth.receipt.index[TOP_LEVEL - 1] == LOGS_INDEX &&
119 230 : auth.receipt.level == LOG_LEVEL) {
120 : // Validate the parsed log had the exact number of elements
121 92 : if (auth.receipt.index[LOG_LEVEL - 1] != LOG_ELEMENTS) {
122 0 : LOG("[E] Found log with %u elements\n",
123 0 : auth.receipt.index[LOG_LEVEL - 1]);
124 0 : THROW(ERR_AUTH_RECEIPT_INVALID);
125 : }
126 :
127 92 : if (HAS_FLAG(auth.receipt.flags, IS_VALID_EMITTER) &&
128 88 : HAS_FLAG(auth.receipt.flags, IS_VALID_SIGNATURE) &&
129 54 : HAS_FLAG(auth.receipt.flags, IS_VALID_TXHASH)) {
130 42 : SET_FLAG(auth.receipt.flags, IS_MATCH);
131 : }
132 : }
133 :
134 : // Validate the parsed receipt had the exact number of
135 : // top level elements
136 280 : if (auth.receipt.level == TOP_LEVEL &&
137 50 : auth.receipt.index[TOP_LEVEL - 1] != LIST_ELEMENTS) {
138 2 : LOG("[E] Receipt had %u elements\n", auth.receipt.index[TOP_LEVEL - 1]);
139 2 : THROW(ERR_AUTH_RECEIPT_INVALID);
140 : }
141 :
142 278 : --auth.receipt.level;
143 :
144 : // Reset indexes of lower levels
145 884 : for (uint8_t i = auth.receipt.level; i < RECEIPT_MAX_DEPTH; i++)
146 606 : auth.receipt.index[i] = 0;
147 278 : }
148 :
149 644 : static void str_start(const uint16_t size) {
150 : UNUSED(size);
151 :
152 : // Top level must be a list
153 644 : if (auth.receipt.level == 0) {
154 0 : LOG("[E] Receipt not a list\n");
155 0 : THROW(ERR_AUTH_RECEIPT_INVALID);
156 : }
157 :
158 : update_indexes();
159 :
160 : // Save strings only for desired fields
161 644 : if (auth.receipt.index[TOP_LEVEL - 1] == LOGS_INDEX &&
162 400 : auth.receipt.level >= LOG_LEVEL) {
163 398 : auth.receipt.aux_offset = 0;
164 : }
165 644 : }
166 :
167 906 : static void str_chunk(const uint8_t* chunk, const size_t size) {
168 : // Save strings only for desired fields
169 : // Also prevent erroring while saving
170 : // undesired fields that could be too big
171 906 : if (auth.receipt.index[TOP_LEVEL - 1] == LOGS_INDEX &&
172 524 : auth.receipt.level >= LOG_LEVEL &&
173 522 : auth.receipt.aux_offset + size <= sizeof(auth.receipt.aux)) {
174 1036 : SAFE_MEMMOVE(auth.receipt.aux,
175 : sizeof(auth.receipt.aux),
176 : auth.receipt.aux_offset,
177 : chunk,
178 : size,
179 : MEMMOVE_ZERO_OFFSET,
180 : size,
181 : THROW(ERR_AUTH_INVALID_DATA_SIZE));
182 518 : auth.receipt.aux_offset += size;
183 : }
184 906 : }
185 :
186 644 : static void str_end() {
187 : // Compare values with expected values
188 644 : if (auth.receipt.index[TOP_LEVEL - 1] == LOGS_INDEX &&
189 400 : auth.receipt.level >= LOG_LEVEL) {
190 398 : switch (auth.receipt.level) {
191 184 : case LOG_LEVEL:
192 184 : if (auth.receipt.index[LOG_LEVEL - 1] == EVENT_EMITTER_INDEX &&
193 92 : auth.receipt.aux_offset == sizeof(EVENT_EMITTER) &&
194 92 : !memcmp(auth.receipt.aux,
195 : (void*)PIC(EVENT_EMITTER),
196 : sizeof(EVENT_EMITTER)))
197 88 : SET_FLAG(auth.receipt.flags, IS_VALID_EMITTER);
198 184 : break;
199 214 : case TOPIC_LEVEL:
200 214 : if (auth.receipt.index[LOG_LEVEL - 1] == TOPICS_INDEX) {
201 214 : if (auth.receipt.index[TOPIC_LEVEL - 1] ==
202 92 : TOPIC_SIGNATURE_INDEX &&
203 92 : auth.receipt.aux_offset == sizeof(EVENT_SIGNATURE) &&
204 92 : !memcmp(auth.receipt.aux,
205 : (void*)PIC(EVENT_SIGNATURE),
206 : sizeof(EVENT_SIGNATURE)))
207 56 : SET_FLAG(auth.receipt.flags, IS_VALID_SIGNATURE);
208 158 : else if (auth.receipt.index[TOPIC_LEVEL - 1] ==
209 60 : TOPIC_TXHASH_INDEX &&
210 60 : auth.receipt.aux_offset == sizeof(auth.tx_hash) &&
211 60 : !memcmp(auth.receipt.aux,
212 : auth.tx_hash,
213 : sizeof(auth.tx_hash)))
214 46 : SET_FLAG(auth.receipt.flags, IS_VALID_TXHASH);
215 : }
216 214 : break;
217 : }
218 : }
219 644 : }
220 :
221 : static const rlp_callbacks_t callbacks = {
222 : str_start,
223 : str_chunk,
224 : str_end,
225 : list_start,
226 : list_end,
227 : };
228 :
229 : /*
230 : * Implement the RSK receipt parsing and validation portion of the signing
231 : * authorization protocol.
232 : *
233 : * @arg[in] rx number of received bytes from the host
234 : * @ret number of transmited bytes to the host
235 : */
236 338 : unsigned int auth_sign_handle_receipt(volatile unsigned int rx) {
237 338 : if (auth.state != STATE_AUTH_RECEIPT) {
238 0 : LOG("[E] Expected to be in the receipt state\n");
239 0 : THROW(ERR_AUTH_INVALID_STATE);
240 : }
241 :
242 338 : if (!HAS_FLAG(auth.receipt.flags, IS_INIT)) {
243 50 : rlp_start(&callbacks);
244 50 : hash_keccak256_init(&auth.receipt.hash_ctx);
245 50 : SET_FLAG(auth.receipt.flags, IS_INIT);
246 : }
247 :
248 338 : int res = rlp_consume(APDU_DATA_PTR, APDU_DATA_SIZE(rx));
249 336 : if (res < 0) {
250 0 : LOG("[E] RLP parser returned error %d\n", res);
251 0 : THROW(ERR_AUTH_RECEIPT_RLP);
252 : }
253 336 : auth.receipt.remaining_bytes -= APDU_DATA_SIZE(rx);
254 :
255 0 : hash_keccak256_update(
256 336 : &auth.receipt.hash_ctx, APDU_DATA_PTR, APDU_DATA_SIZE(rx));
257 :
258 336 : if (auth.receipt.remaining_bytes == 0) {
259 48 : if (HAS_FLAG(auth.receipt.flags, IS_MATCH)) {
260 : // Finalize the hash calculation
261 42 : hash_keccak256_final(&auth.receipt.hash_ctx, auth.receipt_hash);
262 :
263 : // Log hash for debugging purposes
264 42 : LOG_HEX(
265 : "Receipt hash: ", auth.receipt_hash, sizeof(auth.receipt_hash));
266 :
267 : // Request RSK transaction receipt
268 42 : SET_APDU_OP(P1_MERKLEPROOF);
269 42 : SET_APDU_TXLEN(AUTH_MAX_EXCHANGE_SIZE);
270 42 : auth.expected_bytes = APDU_TXLEN();
271 42 : auth_transition_to(STATE_AUTH_MERKLEPROOF);
272 42 : return TX_FOR_TXLEN();
273 : }
274 :
275 : // No match
276 6 : LOG("[E] No log match found in the receipt\n");
277 : // To comply with the legacy implementation
278 6 : THROW(ERR_AUTH_INVALID_DATA_SIZE);
279 : }
280 :
281 288 : SET_APDU_TXLEN(MIN(auth.receipt.remaining_bytes, AUTH_MAX_EXCHANGE_SIZE));
282 288 : auth.expected_bytes = APDU_TXLEN();
283 288 : return TX_FOR_TXLEN();
284 : }
|