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/hash.h"
28 : #include "hal/platform.h"
29 : #include "hal/exceptions.h"
30 :
31 : #include "auth.h"
32 : #include "mem.h"
33 : #include "memutil.h"
34 : #include "bc_state.h"
35 :
36 : #include "hal/log.h"
37 :
38 : #define REQUEST_MORE_IF_NEED() \
39 : { \
40 : if (apdu_offset >= APDU_DATA_SIZE(rx)) { \
41 : SET_APDU_TXLEN(AUTH_MAX_EXCHANGE_SIZE); \
42 : return TX_FOR_TXLEN(); \
43 : } \
44 : }
45 :
46 : #define FAIL_IF_LEAF() \
47 : { \
48 : if (auth.trie.current_node == 0) { \
49 : LOG("[E] Leaf node not a leaf\n"); \
50 : THROW(ERR_AUTH_RECEIPT_HASH_MISMATCH); \
51 : } \
52 : }
53 :
54 : #define IGNORE_IF_INTERNAL() \
55 : { \
56 : if (auth.trie.current_node > 0) \
57 : break; \
58 : }
59 :
60 4690 : static void trie_cb(const trie_cb_event_t event) {
61 : // Update node hash
62 4690 : hash_keccak256_update(
63 4690 : &auth.trie.hash_ctx, auth.trie.ctx.raw, auth.trie.ctx.raw_size);
64 :
65 4690 : switch (event) {
66 90 : case TRIE_EV_FLAGS:
67 90 : if (TRIE_FG_VERSION(auth.trie.ctx.flags) != AUTH_TRIE_NODE_VERSION) {
68 2 : LOG("[E] Invalid node version: %u\n",
69 2 : TRIE_FG_VERSION(auth.trie.ctx.flags));
70 2 : THROW(ERR_AUTH_NODE_INVALID_VERSION);
71 : }
72 :
73 88 : if (auth.trie.current_node == 0 &&
74 40 : (TRIE_FG_NODE_PRESENT_LEFT(auth.trie.ctx.flags) ||
75 38 : TRIE_FG_NODE_PRESENT_RIGHT(auth.trie.ctx.flags))) {
76 2 : LOG("[E] Leaf node not a leaf\n");
77 2 : THROW(ERR_AUTH_RECEIPT_HASH_MISMATCH);
78 : }
79 :
80 : // In a valid proof, the first node of the partial merkle proof
81 : // MUST be the leaf and contain the receipt as a value.
82 : // Any receipt is by definition more than 32 bytes
83 : // in size, and therefore a merkle proof node that contains it should
84 : // encode it as a long value (i.e., containing hash and length only)
85 : // Hence, getting here indicates the given proof is INVALID.
86 : // Fail indicating a receipt hash mismatch.
87 86 : if (auth.trie.current_node == 0 &&
88 38 : !TRIE_FG_HAS_LONG_VALUE(auth.trie.ctx.flags)) {
89 2 : LOG("[E] Leaf node must have a long value\n");
90 2 : THROW(ERR_AUTH_RECEIPT_HASH_MISMATCH);
91 : }
92 84 : break;
93 0 : case TRIE_EV_VALUE_START:
94 : case TRIE_EV_VALUE_DATA:
95 : case TRIE_EV_VALUE_END:
96 0 : IGNORE_IF_INTERNAL();
97 0 : LOG("[E] Leaf node must have a long value\n");
98 0 : THROW(ERR_AUTH_RECEIPT_HASH_MISMATCH);
99 : break;
100 36 : case TRIE_EV_VALUE_HASH_START:
101 36 : IGNORE_IF_INTERNAL();
102 36 : auth.trie.offset = 0;
103 36 : break;
104 1152 : case TRIE_EV_VALUE_HASH_DATA:
105 1152 : IGNORE_IF_INTERNAL();
106 2304 : SAFE_MEMMOVE(auth.trie.value_hash,
107 : sizeof(auth.trie.value_hash),
108 : auth.trie.offset,
109 : auth.trie.ctx.raw,
110 : sizeof(auth.trie.ctx.raw),
111 : MEMMOVE_ZERO_OFFSET,
112 : auth.trie.ctx.raw_size,
113 : THROW(ERR_AUTH_INVALID_DATA_SIZE));
114 1152 : auth.trie.offset += auth.trie.ctx.raw_size;
115 1152 : break;
116 36 : case TRIE_EV_VALUE_HASH_END:
117 36 : IGNORE_IF_INTERNAL();
118 36 : if (memcmp(auth.receipt_hash,
119 : auth.trie.value_hash,
120 : sizeof(auth.trie.value_hash))) {
121 2 : LOG("[E] Receipt hash mismatch\n");
122 2 : THROW(ERR_AUTH_RECEIPT_HASH_MISMATCH);
123 : }
124 34 : break;
125 16 : case TRIE_EV_LEFT_NODE_START:
126 : case TRIE_EV_RIGHT_NODE_START:
127 16 : FAIL_IF_LEAF();
128 16 : auth.trie.offset = 0;
129 16 : break;
130 512 : case TRIE_EV_LEFT_NODE_DATA:
131 : case TRIE_EV_RIGHT_NODE_DATA:
132 512 : FAIL_IF_LEAF();
133 1024 : SAFE_MEMMOVE(auth.trie.child_hash,
134 : sizeof(auth.trie.child_hash),
135 : auth.trie.offset,
136 : auth.trie.ctx.raw,
137 : sizeof(auth.trie.ctx.raw),
138 : MEMMOVE_ZERO_OFFSET,
139 : auth.trie.ctx.raw_size,
140 : THROW(ERR_AUTH_INVALID_DATA_SIZE));
141 512 : auth.trie.offset += auth.trie.ctx.raw_size;
142 512 : break;
143 68 : case TRIE_EV_LEFT_NODE_EMBEDDED_START:
144 : case TRIE_EV_RIGHT_NODE_EMBEDDED_START:
145 68 : FAIL_IF_LEAF();
146 68 : hash_keccak256_init(&auth.trie.aux_hash_ctx);
147 68 : break;
148 2576 : case TRIE_EV_LEFT_NODE_EMBEDDED_DATA:
149 : case TRIE_EV_RIGHT_NODE_EMBEDDED_DATA:
150 2576 : FAIL_IF_LEAF();
151 2576 : hash_keccak256_update(
152 2576 : &auth.trie.aux_hash_ctx, auth.trie.ctx.raw, auth.trie.ctx.raw_size);
153 2576 : break;
154 84 : case TRIE_EV_LEFT_NODE_END:
155 : case TRIE_EV_RIGHT_NODE_END:
156 : case TRIE_EV_LEFT_NODE_EMBEDDED_END:
157 : case TRIE_EV_RIGHT_NODE_EMBEDDED_END:
158 84 : FAIL_IF_LEAF();
159 84 : if (event == TRIE_EV_LEFT_NODE_EMBEDDED_END ||
160 : event == TRIE_EV_RIGHT_NODE_EMBEDDED_END)
161 68 : hash_keccak256_final(&auth.trie.aux_hash_ctx, auth.trie.child_hash);
162 84 : if (!memcmp(auth.trie.node_hash,
163 : auth.trie.child_hash,
164 : sizeof(auth.trie.node_hash)))
165 46 : auth.trie.num_linked++;
166 84 : break;
167 : }
168 4682 : }
169 :
170 : /*
171 : * Implement the partial merkle trie parsing portion
172 : * of the signing authorization protocol.
173 : *
174 : * @arg[in] rx number of received bytes from the host
175 : * @ret number of transmited bytes to the host
176 : */
177 82 : unsigned int auth_sign_handle_merkleproof(volatile unsigned int rx) {
178 82 : uint8_t apdu_offset = 0;
179 :
180 82 : if (auth.state != STATE_AUTH_MERKLEPROOF) {
181 0 : LOG("[E] Expected to be in the MP state\n");
182 0 : THROW(ERR_AUTH_INVALID_STATE);
183 : }
184 :
185 : while (true) {
186 : // Read number of nodes (single byte)
187 130 : if (auth.trie.total_nodes == 0) {
188 42 : auth.trie.total_nodes = APDU_DATA_PTR[apdu_offset++];
189 42 : auth.trie.current_node = 0;
190 42 : auth.trie.state = AUTH_TRIE_STATE_NODE_LENGTH;
191 :
192 42 : REQUEST_MORE_IF_NEED();
193 : }
194 :
195 130 : if (auth.trie.state == AUTH_TRIE_STATE_NODE_LENGTH) {
196 : // Verify node is at least one byte long
197 90 : if (APDU_DATA_PTR[apdu_offset] == 0) {
198 0 : LOG("[E] Got MP node length zero\n");
199 0 : THROW(ERR_AUTH_INVALID_DATA_SIZE);
200 : }
201 90 : trie_init(&auth.trie.ctx, &trie_cb, APDU_DATA_PTR[apdu_offset++]);
202 90 : hash_keccak256_init(&auth.trie.hash_ctx);
203 90 : auth.trie.state = AUTH_TRIE_STATE_NODE;
204 90 : auth.trie.num_linked = 0;
205 :
206 90 : REQUEST_MORE_IF_NEED();
207 : }
208 :
209 130 : if (auth.trie.state == AUTH_TRIE_STATE_NODE) {
210 130 : apdu_offset += trie_consume(APDU_DATA_PTR + apdu_offset,
211 130 : APDU_DATA_SIZE(rx) - apdu_offset);
212 :
213 122 : if (trie_result() < 0) {
214 0 : LOG("[E] Error parsing MP node: %u\n", trie_result());
215 : // Reusing an existing error code due to legacy protocol
216 0 : THROW(ERR_AUTH_RECEIPT_ROOT_MISMATCH);
217 122 : } else if (trie_result() == TRIE_ST_DONE) {
218 82 : hash_keccak256_final(&auth.trie.hash_ctx, auth.trie.node_hash);
219 82 : LOG("MP@%u ", auth.trie.current_node);
220 82 : LOG_HEX(
221 : "hash: ", auth.trie.node_hash, sizeof(auth.trie.node_hash));
222 :
223 82 : if (auth.trie.current_node > 0) {
224 : // If this is an internal node, check
225 : // it was successfully linked with exactly
226 : // one child
227 48 : if (auth.trie.num_linked != 1) {
228 2 : LOG("[E] Node chaining mismatch\n");
229 2 : THROW(ERR_AUTH_NODE_CHAINING_MISMATCH);
230 : }
231 : }
232 :
233 80 : auth.trie.current_node++;
234 :
235 : // If this is the root, check it matches the current
236 : // bc state's ancestor receipts root
237 80 : if (auth.trie.current_node == auth.trie.total_nodes) {
238 32 : if (!memcmp(N_bc_state.ancestor_receipt_root,
239 : auth.trie.node_hash,
240 : sizeof(N_bc_state.ancestor_receipt_root))) {
241 32 : auth_transition_to(STATE_AUTH_SIGN);
242 32 : return 0;
243 : }
244 :
245 0 : LOG("[E] Receipt root mismatch\n");
246 0 : THROW(ERR_AUTH_RECEIPT_ROOT_MISMATCH);
247 : }
248 :
249 48 : auth.trie.state = AUTH_TRIE_STATE_NODE_LENGTH;
250 : }
251 :
252 88 : REQUEST_MORE_IF_NEED();
253 : }
254 : }
255 : }
|