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 348 : static void reset_shared_state() {
67 348 : explicit_bzero(&mem, sizeof(mem));
68 348 : }
69 :
70 : /*
71 : * Reset all reseteable operations, only if the given operation is starting.
72 : *
73 : * @arg[in] cmd operation code
74 : */
75 7347 : 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 7347 : if (cmd != curr_cmd) {
79 348 : reset_shared_state();
80 348 : bc_init_advance();
81 348 : bc_init_upd_ancestor();
82 348 : curr_cmd = cmd;
83 : }
84 7347 : }
85 :
86 : // Exit the application
87 1 : static void app_exit(void) {
88 1 : platform_request_exit();
89 1 : _hsm_exit_requested = true;
90 1 : }
91 :
92 7272 : static unsigned int hsm_process_command(volatile unsigned int rx) {
93 7272 : unsigned int tx = 0;
94 : uint8_t pubkey_length;
95 :
96 : // No apdu received, or bigger-than-apdu-buffer bytes received
97 7272 : if (rx == 0 || rx > APDU_TOTAL_SIZE) {
98 4 : THROW(ERR_INVALID_BUFFER);
99 : }
100 :
101 : // Zero out commonly read APDU buffer offsets,
102 : // to avoid reading uninitialized memory
103 7268 : if (rx < MIN_APDU_BYTES) {
104 134 : explicit_bzero(communication_get_msg_buffer() + rx,
105 134 : MIN_APDU_BYTES - rx);
106 : }
107 :
108 : // Invalid CLA
109 7268 : if (APDU_CLA() != CLA) {
110 2 : THROW(ERR_INVALID_CLA);
111 : }
112 :
113 7266 : if (external_processor) {
114 2 : external_processor_result_t epr = external_processor(rx);
115 2 : if (epr.handled) {
116 1 : return epr.tx;
117 : }
118 : }
119 :
120 7265 : switch (APDU_CMD()) {
121 : // Reports the current mode (i.e., always reports signer mode)
122 20 : case RSK_MODE_CMD:
123 20 : hsm_reset_if_starting(RSK_MODE_CMD);
124 20 : SET_APDU_CMD(APP_MODE_SIGNER);
125 20 : tx = 2;
126 20 : break;
127 :
128 : // Reports wheter the device is onboarded and the current signer version
129 3 : case RSK_IS_ONBOARD:
130 3 : hsm_reset_if_starting(RSK_IS_ONBOARD);
131 3 : uint8_t output_index = CMDPOS;
132 3 : SET_APDU_AT(output_index++, seed_available() ? 1 : 0);
133 3 : SET_APDU_AT(output_index++, VERSION_MAJOR);
134 3 : SET_APDU_AT(output_index++, VERSION_MINOR);
135 3 : SET_APDU_AT(output_index++, VERSION_PATCH);
136 3 : tx = 5;
137 3 : break;
138 :
139 : // Derives and returns the corresponding public key for the given path
140 92 : case INS_GET_PUBLIC_KEY:
141 92 : REQUIRE_UNLOCKED();
142 91 : REQUIRE_ONBOARDED();
143 :
144 84 : hsm_reset_if_starting(INS_GET_PUBLIC_KEY);
145 :
146 : // Check the received data size
147 84 : if (rx != DATA + sizeof(uint32_t) * BIP32_PATH_NUMPARTS)
148 7 : 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 92 : if (!(pathRequireAuth(APDU_DATA_PTR - 1) ||
156 15 : pathDontRequireAuth(APDU_DATA_PTR - 1))) {
157 : // If no path match, then bail out
158 7 : THROW(ERR_INVALID_PATH); // Invalid Key Path
159 : }
160 :
161 : // Derive the public key
162 140 : 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 70 : pubkey_length = (uint8_t)MIN(
172 : communication_get_msg_buffer_size() - APDU_RESULT_CODE_SIZE, 0xFF);
173 70 : if (!seed_derive_pubkey(auth.path,
174 : sizeof(auth.path) / sizeof(auth.path[0]),
175 70 : communication_get_msg_buffer(),
176 : &pubkey_length)) {
177 1 : THROW(ERR_INTERNAL);
178 : }
179 :
180 69 : tx = pubkey_length;
181 :
182 69 : break;
183 :
184 2714 : case INS_SIGN:
185 2714 : REQUIRE_UNLOCKED();
186 2713 : REQUIRE_ONBOARDED();
187 :
188 2706 : hsm_reset_if_starting(INS_SIGN);
189 2706 : tx = auth_sign(rx);
190 2664 : break;
191 :
192 4 : case INS_ATTESTATION:
193 4 : REQUIRE_UNLOCKED();
194 3 : REQUIRE_ONBOARDED();
195 :
196 2 : hsm_reset_if_starting(INS_ATTESTATION);
197 2 : tx = get_attestation(rx, &attestation);
198 1 : break;
199 :
200 24 : case INS_HEARTBEAT:
201 24 : REQUIRE_UNLOCKED();
202 23 : REQUIRE_ONBOARDED();
203 :
204 22 : hsm_reset_if_starting(INS_HEARTBEAT);
205 22 : tx = get_heartbeat(rx, &heartbeat);
206 21 : break;
207 :
208 : // Get blockchain state
209 203 : case INS_GET_STATE:
210 203 : REQUIRE_UNLOCKED();
211 202 : REQUIRE_ONBOARDED();
212 :
213 : // Get blockchain state is considered part of the
214 : // advance blockchain operation
215 200 : hsm_reset_if_starting(INS_ADVANCE);
216 200 : tx = bc_get_state(rx);
217 199 : break;
218 :
219 : // Reset blockchain state
220 21 : case INS_RESET_STATE:
221 21 : REQUIRE_UNLOCKED();
222 20 : REQUIRE_ONBOARDED();
223 :
224 19 : hsm_reset_if_starting(INS_RESET_STATE);
225 19 : tx = bc_reset_state(rx);
226 18 : break;
227 :
228 : // Advance blockchain
229 3329 : case INS_ADVANCE:
230 3329 : REQUIRE_UNLOCKED();
231 3328 : REQUIRE_ONBOARDED();
232 :
233 3326 : hsm_reset_if_starting(INS_ADVANCE);
234 3326 : tx = bc_advance(rx);
235 3317 : break;
236 :
237 : // Advance blockchain precompiled parameters
238 5 : case INS_ADVANCE_PARAMS:
239 5 : REQUIRE_UNLOCKED();
240 4 : REQUIRE_ONBOARDED();
241 :
242 3 : hsm_reset_if_starting(INS_ADVANCE_PARAMS);
243 3 : tx = bc_advance_get_params();
244 2 : break;
245 :
246 : // Update ancestor
247 845 : case INS_UPD_ANCESTOR:
248 845 : REQUIRE_UNLOCKED();
249 844 : REQUIRE_ONBOARDED();
250 :
251 842 : hsm_reset_if_starting(INS_UPD_ANCESTOR);
252 842 : tx = bc_upd_ancestor(rx);
253 841 : break;
254 :
255 2 : case INS_EXIT:
256 2 : REQUIRE_UNLOCKED();
257 :
258 1 : bc_backup_partial_state();
259 1 : app_exit();
260 1 : tx = TX_FOR_DATA_SIZE(0);
261 1 : break;
262 :
263 3 : default: // Unknown command
264 3 : THROW(ERR_INS_NOT_SUPPORTED);
265 : break;
266 : }
267 :
268 7156 : return tx;
269 : }
270 :
271 7272 : static unsigned int hsm_process_exception(unsigned short code,
272 : unsigned int tx) {
273 7272 : unsigned short sw = 0;
274 :
275 : // Always reset the full state when an error occurs
276 7272 : if (code != APDU_OK) {
277 115 : RESET_BC_STATE();
278 115 : hsm_reset_if_starting(0);
279 : }
280 :
281 : // Apply code transformations
282 7272 : switch (code & 0xF000) {
283 7272 : case 0x6000:
284 : case 0x9000:
285 7272 : sw = code;
286 7272 : break;
287 0 : default:
288 0 : sw = 0x6800 | (code & 0x7FF);
289 0 : break;
290 : }
291 :
292 : // Append resulting code to APDU
293 : // (check for a potential overflow first)
294 7272 : if (tx + 2 > communication_get_msg_buffer_size()) {
295 0 : tx = 0;
296 0 : sw = 0x6983;
297 : }
298 7272 : SET_APDU_AT(tx++, sw >> 8);
299 7272 : SET_APDU_AT(tx++, sw);
300 :
301 7272 : return tx;
302 : }
303 :
304 21 : void hsm_init() {
305 : // Initialize current operation
306 : // (0 = no operation being executed)
307 21 : curr_cmd = 0;
308 :
309 : // No exit requested
310 21 : _hsm_exit_requested = false;
311 :
312 : // No external processor
313 21 : external_processor = NULL;
314 :
315 : // Blockchain state initialization
316 21 : bc_init_state();
317 21 : }
318 :
319 7272 : unsigned int hsm_process_apdu(unsigned int rx) {
320 7272 : unsigned int tx = 0;
321 7272 : unsigned short ex = APDU_OK;
322 : BEGIN_TRY {
323 7272 : TRY {
324 7272 : tx = hsm_process_command(rx);
325 : }
326 7157 : CATCH_OTHER(e) {
327 115 : ex = e;
328 : }
329 115 : FINALLY {
330 7272 : tx = hsm_process_exception(ex, tx);
331 : }
332 : }
333 7272 : END_TRY;
334 :
335 7272 : return tx;
336 : }
337 :
338 7206 : bool hsm_exit_requested() {
339 7206 : return _hsm_exit_requested;
340 : }
341 :
342 2 : void hsm_set_external_processor(external_processor_t _external_processor) {
343 2 : external_processor = _external_processor;
344 2 : }
|