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/communication.h"
28 : #include "hal/seed.h"
29 : #include "hal/platform.h"
30 : #include "hal/exceptions.h"
31 :
32 : #include "hsm.h"
33 :
34 : #include "defs.h"
35 : #include "instructions.h"
36 : #include "modes.h"
37 : #include "err.h"
38 : #include "mem.h"
39 : #include "memutil.h"
40 : #include "util.h"
41 :
42 : #include "pathAuth.h"
43 : #include "auth.h"
44 :
45 : #include "bc_state.h"
46 : #include "bc_advance.h"
47 : #include "bc_ancestor.h"
48 :
49 : #include "attestation.h"
50 : #include "heartbeat.h"
51 :
52 : #include "hal/log.h"
53 :
54 : // Operation being currently executed
55 : static unsigned char curr_cmd;
56 :
57 : // Whether exit has been requested
58 : static bool _hsm_exit_requested;
59 :
60 : // External processor
61 : static external_processor_t external_processor;
62 :
63 : /*
64 : * Reset shared memory state.
65 : */
66 270 : static void reset_shared_state() {
67 270 : explicit_bzero(&mem, sizeof(mem));
68 270 : }
69 :
70 : /*
71 : * Reset all reseteable operations, only if the given operation is starting.
72 : *
73 : * @arg[in] cmd operation code
74 : */
75 7236 : void hsm_reset_if_starting(unsigned char cmd) {
76 : // Reset only if starting new operation (cmd != curr_cmd).
77 : // Otherwise we already reset when curr_cmd started.
78 7236 : if (cmd != curr_cmd) {
79 270 : reset_shared_state();
80 270 : bc_init_advance();
81 270 : bc_init_upd_ancestor();
82 270 : curr_cmd = cmd;
83 : }
84 7236 : }
85 :
86 : // Exit the application
87 0 : static void app_exit(void) {
88 0 : platform_request_exit();
89 0 : _hsm_exit_requested = true;
90 0 : }
91 :
92 7178 : static unsigned int hsm_process_command(volatile unsigned int rx) {
93 7178 : unsigned int tx = 0;
94 : uint8_t pubkey_length;
95 :
96 : // No apdu received, or bigger-than-apdu-buffer bytes received
97 7178 : if (rx == 0 || rx > APDU_TOTAL_SIZE) {
98 2 : THROW(ERR_INVALID_BUFFER);
99 : }
100 :
101 : // Zero out commonly read APDU buffer offsets,
102 : // to avoid reading uninitialized memory
103 7176 : if (rx < MIN_APDU_BYTES) {
104 79 : explicit_bzero(communication_get_msg_buffer() + rx,
105 79 : MIN_APDU_BYTES - rx);
106 : }
107 :
108 : // Invalid CLA
109 7176 : if (APDU_CLA() != CLA) {
110 0 : THROW(ERR_INVALID_CLA);
111 : }
112 :
113 7176 : if (external_processor) {
114 0 : external_processor_result_t epr = external_processor(rx);
115 0 : if (epr.handled) {
116 0 : return epr.tx;
117 : }
118 : }
119 :
120 7176 : switch (APDU_CMD()) {
121 : // Reports the current mode (i.e., always reports signer mode)
122 0 : case RSK_MODE_CMD:
123 0 : hsm_reset_if_starting(RSK_MODE_CMD);
124 0 : SET_APDU_CMD(APP_MODE_SIGNER);
125 0 : tx = 2;
126 0 : break;
127 :
128 : // Reports wheter the device is onboarded and the current signer version
129 1 : case RSK_IS_ONBOARD:
130 1 : hsm_reset_if_starting(RSK_IS_ONBOARD);
131 1 : uint8_t output_index = CMDPOS;
132 1 : SET_APDU_AT(output_index++, seed_available() ? 1 : 0);
133 1 : SET_APDU_AT(output_index++, VERSION_MAJOR);
134 1 : SET_APDU_AT(output_index++, VERSION_MINOR);
135 1 : SET_APDU_AT(output_index++, VERSION_PATCH);
136 1 : tx = 5;
137 1 : break;
138 :
139 : // Derives and returns the corresponding public key for the given path
140 84 : case INS_GET_PUBLIC_KEY:
141 84 : REQUIRE_UNLOCKED();
142 84 : REQUIRE_ONBOARDED();
143 :
144 78 : hsm_reset_if_starting(INS_GET_PUBLIC_KEY);
145 :
146 : // Check the received data size
147 78 : if (rx != DATA + sizeof(uint32_t) * BIP32_PATH_NUMPARTS)
148 6 : THROW(ERR_INVALID_DATA_SIZE); // Wrong buffer size
149 :
150 : // Check for path validity before returning the public key
151 : // Actual path starts at normal data pointer, but
152 : // is prepended by a single byte indicating the path length
153 : // (all paths have the same length in practice, so this should
154 : // be refactored in the future)
155 86 : if (!(pathRequireAuth(APDU_DATA_PTR - 1) ||
156 14 : pathDontRequireAuth(APDU_DATA_PTR - 1))) {
157 : // If no path match, then bail out
158 6 : THROW(ERR_INVALID_PATH); // Invalid Key Path
159 : }
160 :
161 : // Derive the public key
162 132 : SAFE_MEMMOVE(auth.path,
163 : sizeof(auth.path),
164 : MEMMOVE_ZERO_OFFSET,
165 : APDU_DATA_PTR,
166 : APDU_TOTAL_DATA_SIZE_OUT,
167 : MEMMOVE_ZERO_OFFSET,
168 : sizeof(auth.path),
169 : THROW(ERR_INVALID_PATH));
170 :
171 66 : pubkey_length = (uint8_t)MIN(communication_get_msg_buffer_size(), 0xFF);
172 66 : if (!seed_derive_pubkey(auth.path,
173 : sizeof(auth.path) / sizeof(auth.path[0]),
174 66 : communication_get_msg_buffer(),
175 : &pubkey_length)) {
176 0 : THROW(ERR_INTERNAL);
177 : }
178 :
179 66 : tx = pubkey_length;
180 :
181 66 : break;
182 :
183 2688 : case INS_SIGN:
184 2688 : REQUIRE_UNLOCKED();
185 2688 : REQUIRE_ONBOARDED();
186 :
187 2682 : hsm_reset_if_starting(INS_SIGN);
188 2682 : tx = auth_sign(rx);
189 2644 : break;
190 :
191 0 : case INS_ATTESTATION:
192 0 : REQUIRE_UNLOCKED();
193 0 : REQUIRE_ONBOARDED();
194 :
195 0 : hsm_reset_if_starting(INS_ATTESTATION);
196 0 : tx = get_attestation(rx, &attestation);
197 0 : break;
198 :
199 20 : case INS_HEARTBEAT:
200 20 : REQUIRE_UNLOCKED();
201 20 : REQUIRE_ONBOARDED();
202 :
203 20 : hsm_reset_if_starting(INS_HEARTBEAT);
204 20 : tx = get_heartbeat(rx, &heartbeat);
205 20 : break;
206 :
207 : // Get blockchain state
208 199 : case INS_GET_STATE:
209 199 : REQUIRE_UNLOCKED();
210 199 : REQUIRE_ONBOARDED();
211 :
212 : // Get blockchain state is considered part of the
213 : // advance blockchain operation
214 198 : hsm_reset_if_starting(INS_ADVANCE);
215 198 : tx = bc_get_state(rx);
216 198 : break;
217 :
218 : // Reset blockchain state
219 17 : case INS_RESET_STATE:
220 17 : REQUIRE_UNLOCKED();
221 17 : REQUIRE_ONBOARDED();
222 :
223 17 : hsm_reset_if_starting(INS_RESET_STATE);
224 17 : tx = bc_reset_state(rx);
225 17 : break;
226 :
227 : // Advance blockchain
228 3325 : case INS_ADVANCE:
229 3325 : REQUIRE_UNLOCKED();
230 3325 : REQUIRE_ONBOARDED();
231 :
232 3324 : hsm_reset_if_starting(INS_ADVANCE);
233 3324 : tx = bc_advance(rx);
234 3316 : break;
235 :
236 : // Advance blockchain precompiled parameters
237 1 : case INS_ADVANCE_PARAMS:
238 1 : REQUIRE_UNLOCKED();
239 1 : REQUIRE_ONBOARDED();
240 :
241 1 : hsm_reset_if_starting(INS_ADVANCE_PARAMS);
242 1 : tx = bc_advance_get_params();
243 1 : break;
244 :
245 : // Update ancestor
246 841 : case INS_UPD_ANCESTOR:
247 841 : REQUIRE_UNLOCKED();
248 841 : REQUIRE_ONBOARDED();
249 :
250 840 : hsm_reset_if_starting(INS_UPD_ANCESTOR);
251 840 : tx = bc_upd_ancestor(rx);
252 840 : break;
253 :
254 0 : case INS_EXIT:
255 0 : REQUIRE_UNLOCKED();
256 :
257 0 : bc_backup_partial_state();
258 0 : app_exit();
259 0 : tx = TX_FOR_DATA_SIZE(0);
260 0 : break;
261 :
262 0 : default: // Unknown command
263 0 : THROW(ERR_INS_NOT_SUPPORTED);
264 : break;
265 : }
266 :
267 7103 : return tx;
268 : }
269 :
270 7178 : static unsigned int hsm_process_exception(unsigned short code,
271 : unsigned int tx) {
272 7178 : unsigned short sw = 0;
273 :
274 : // Always reset the full state when an error occurs
275 7178 : if (code != APDU_OK) {
276 75 : RESET_BC_STATE();
277 75 : hsm_reset_if_starting(0);
278 : }
279 :
280 : // Apply code transformations
281 7178 : switch (code & 0xF000) {
282 7178 : case 0x6000:
283 : case 0x9000:
284 7178 : sw = code;
285 7178 : break;
286 0 : default:
287 0 : sw = 0x6800 | (code & 0x7FF);
288 0 : break;
289 : }
290 :
291 : // Append resulting code to APDU
292 : // (check for a potential overflow first)
293 7178 : if (tx + 2 > communication_get_msg_buffer_size()) {
294 0 : tx = 0;
295 0 : sw = 0x6983;
296 : }
297 7178 : SET_APDU_AT(tx++, sw >> 8);
298 7178 : SET_APDU_AT(tx++, sw);
299 :
300 7178 : return tx;
301 : }
302 :
303 2 : void hsm_init() {
304 : // Initialize current operation
305 : // (0 = no operation being executed)
306 2 : curr_cmd = 0;
307 :
308 : // No exit requested
309 2 : _hsm_exit_requested = false;
310 :
311 : // No external processor
312 2 : external_processor = NULL;
313 :
314 : // Blockchain state initialization
315 2 : bc_init_state();
316 2 : }
317 :
318 7178 : unsigned int hsm_process_apdu(unsigned int rx) {
319 7178 : unsigned int tx = 0;
320 :
321 : BEGIN_TRY {
322 7178 : TRY {
323 7178 : tx = hsm_process_command(rx);
324 7103 : THROW(0x9000);
325 : }
326 7178 : CATCH_OTHER(e) {
327 7178 : tx = hsm_process_exception(e, tx);
328 : }
329 7178 : FINALLY {
330 : }
331 : }
332 7178 : END_TRY;
333 :
334 7178 : return tx;
335 : }
336 :
337 7179 : bool hsm_exit_requested() {
338 7179 : return _hsm_exit_requested;
339 : }
340 :
341 0 : void hsm_set_external_processor(external_processor_t _external_processor) {
342 0 : external_processor = _external_processor;
343 0 : }
|