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 "runtime.h"
28 : #include "ui_heartbeat.h"
29 : #include "apdu.h"
30 : #include "ui_instructions.h"
31 : #include "ints.h"
32 : #include "ui_err.h"
33 : #include "ui_comm.h"
34 : #include "memutil.h"
35 : #include "compiletime.h"
36 : #include "signer_authorization_status.h"
37 :
38 : // Heartbeat init alias
39 : #define reset_ui_heartbeat(ctx) ui_heartbeat_init(ctx)
40 :
41 : /**
42 : * Initialize the heartbeat context
43 : *
44 : * @arg[in] ui_heartbeat_ctx the UI heartbeat context
45 : */
46 20 : void ui_heartbeat_init(ui_heartbeat_t *ui_heartbeat_ctx) {
47 : COMPILE_TIME_ASSERT(MAX_UI_HEARTBEAT_MESSAGE_SIZE <=
48 : APDU_TOTAL_DATA_SIZE_OUT);
49 :
50 20 : explicit_bzero(ui_heartbeat_ctx, sizeof(ui_heartbeat_t));
51 20 : ui_heartbeat_ctx->state = STATE_UI_HEARTBEAT_WAIT_UD_VALUE;
52 20 : }
53 :
54 : static ui_heartbeat_t *current_context;
55 :
56 0 : static void reset_state() {
57 0 : reset_ui_heartbeat(current_context);
58 0 : }
59 :
60 : /*
61 : * Check that the SM for the heartbeat generation
62 : * matches the expected state.
63 : *
64 : * Reset the state and throw a protocol error
65 : * otherwise.
66 : *
67 : * @arg[in] ui_heartbeat_ctx the UI heartbeat context
68 : */
69 4 : static void check_state(ui_heartbeat_t *ui_heartbeat_ctx,
70 : state_ui_heartbeat_t expected) {
71 4 : if (ui_heartbeat_ctx->state != expected) {
72 2 : reset_ui_heartbeat(ui_heartbeat_ctx);
73 2 : THROW(ERR_UI_HBT_PROT_INVALID);
74 : }
75 2 : }
76 :
77 : // -----------------------------------------------------------------------
78 : // Protocol implementation
79 : // -----------------------------------------------------------------------
80 :
81 : /**
82 : * Implement the heartbeat protocol.
83 : *
84 : * @arg[in] rx number of received bytes from the Host
85 : * @arg[in] ui_heartbeat_ctx the UI heartbeat context
86 : * @ret number of transmited bytes to the host
87 : */
88 9 : unsigned int get_ui_heartbeat(ui_heartbeat_t *ui_heartbeat_ctx,
89 : volatile unsigned int rx) {
90 9 : switch (APDU_OP()) {
91 2 : case OP_UI_HBT_UD_VALUE:
92 : // Should receive a user-defined value
93 2 : if (APDU_DATA_SIZE(rx) != UD_VALUE_SIZE) {
94 1 : reset_ui_heartbeat(ui_heartbeat_ctx);
95 1 : THROW(ERR_UI_HBT_PROT_INVALID);
96 : }
97 :
98 1 : sigaut_signer_t *current_signer_info = get_authorized_signer_info();
99 :
100 : // Initialize message
101 1 : explicit_bzero(ui_heartbeat_ctx->msg, sizeof(ui_heartbeat_ctx->msg));
102 1 : ui_heartbeat_ctx->msg_offset = 0;
103 :
104 : // Copy the message prefix
105 2 : SAFE_MEMMOVE(ui_heartbeat_ctx->msg,
106 : sizeof(ui_heartbeat_ctx->msg),
107 : ui_heartbeat_ctx->msg_offset,
108 : (void *)PIC(UI_HEARTBEAT_MSG_PREFIX),
109 : UI_HEARTBEAT_MSG_PREFIX_LENGTH,
110 : MEMMOVE_ZERO_OFFSET,
111 : UI_HEARTBEAT_MSG_PREFIX_LENGTH,
112 : THROW(ERR_UI_INTERNAL));
113 1 : ui_heartbeat_ctx->msg_offset += UI_HEARTBEAT_MSG_PREFIX_LENGTH;
114 :
115 : // Copy the UD value from the APDU
116 2 : SAFE_MEMMOVE(ui_heartbeat_ctx->msg,
117 : sizeof(ui_heartbeat_ctx->msg),
118 : ui_heartbeat_ctx->msg_offset,
119 : APDU_DATA_PTR,
120 : APDU_TOTAL_DATA_SIZE,
121 : MEMMOVE_ZERO_OFFSET,
122 : UD_VALUE_SIZE,
123 : THROW(ERR_UI_INTERNAL));
124 1 : ui_heartbeat_ctx->msg_offset += UD_VALUE_SIZE;
125 :
126 : // Copy signer hash and iteration into the message space
127 2 : SAFE_MEMMOVE(ui_heartbeat_ctx->msg,
128 : sizeof(ui_heartbeat_ctx->msg),
129 : ui_heartbeat_ctx->msg_offset,
130 : current_signer_info->hash,
131 : sizeof(current_signer_info->hash),
132 : MEMMOVE_ZERO_OFFSET,
133 : sizeof(current_signer_info->hash),
134 : THROW(ERR_UI_INTERNAL));
135 1 : ui_heartbeat_ctx->msg_offset += sizeof(current_signer_info->hash);
136 :
137 : // Make sure iteration fits
138 1 : if (ui_heartbeat_ctx->msg_offset +
139 : sizeof(current_signer_info->iteration) >
140 : sizeof(ui_heartbeat_ctx->msg))
141 0 : THROW(ERR_UI_INTERNAL);
142 :
143 3 : VAR_BIGENDIAN_TO(ui_heartbeat_ctx->msg + ui_heartbeat_ctx->msg_offset,
144 : current_signer_info->iteration,
145 : sizeof(current_signer_info->iteration));
146 1 : ui_heartbeat_ctx->msg_offset += sizeof(current_signer_info->iteration);
147 :
148 1 : ui_heartbeat_ctx->state = STATE_UI_HEARTBEAT_READY;
149 :
150 1 : return TX_FOR_DATA_SIZE(0);
151 2 : case OP_UI_HBT_GET:
152 2 : check_state(ui_heartbeat_ctx, STATE_UI_HEARTBEAT_READY);
153 :
154 : // Sign message
155 2 : int endorsement_size = os_endorsement_key2_derive_sign_data(
156 1 : ui_heartbeat_ctx->msg, ui_heartbeat_ctx->msg_offset, APDU_DATA_PTR);
157 :
158 1 : return TX_FOR_DATA_SIZE(endorsement_size);
159 2 : case OP_UI_HBT_GET_MESSAGE:
160 2 : check_state(ui_heartbeat_ctx, STATE_UI_HEARTBEAT_READY);
161 :
162 2 : SAFE_MEMMOVE(APDU_DATA_PTR,
163 : APDU_TOTAL_DATA_SIZE,
164 : MEMMOVE_ZERO_OFFSET,
165 : ui_heartbeat_ctx->msg,
166 : sizeof(ui_heartbeat_ctx->msg),
167 : MEMMOVE_ZERO_OFFSET,
168 : ui_heartbeat_ctx->msg_offset,
169 : THROW(ERR_UI_INTERNAL));
170 :
171 1 : return TX_FOR_DATA_SIZE(ui_heartbeat_ctx->msg_offset);
172 1 : case OP_UI_HBT_APP_HASH:
173 1 : return TX_FOR_DATA_SIZE(os_endorsement_get_code_hash(APDU_DATA_PTR));
174 1 : case OP_UI_HBT_PUBKEY:
175 1 : return TX_FOR_DATA_SIZE(os_endorsement_get_public_key(
176 : ENDORSEMENT_SCHEME_INDEX, APDU_DATA_PTR));
177 1 : default:
178 1 : reset_ui_heartbeat(ui_heartbeat_ctx);
179 1 : THROW(ERR_UI_HBT_PROT_INVALID);
180 : break;
181 : }
182 : }
183 :
184 : /**
185 : * Process an APDU message
186 : *
187 : * @arg[in] ui_heartbeat_ctx the UI heartbeat context
188 : * @arg[in] rx number of received bytes from the host
189 : * @ret number of transmited bytes to the host
190 : */
191 14 : unsigned int ui_heartbeat_process_apdu(ui_heartbeat_t *ui_heartbeat_ctx,
192 : volatile unsigned int rx) {
193 14 : unsigned int tx = 0;
194 :
195 : // no apdu received, or bigger-than-apdu-buffer bytes received =>
196 : // well, reset the session, and reset the heartbeat configuration
197 14 : if (rx == 0 || rx > APDU_TOTAL_SIZE) {
198 2 : THROW(ERR_INVALID_BUFFER);
199 : }
200 :
201 12 : if (APDU_CLA() != CLA) {
202 1 : THROW(ERR_UI_INVALID_CLA);
203 : }
204 :
205 : // unauthenticated instruction
206 11 : switch (APDU_CMD()) {
207 1 : case RSK_MODE_CMD:
208 1 : tx = get_mode_heartbeat();
209 1 : break;
210 9 : case INS_UI_HEARTBEAT:
211 9 : tx = get_ui_heartbeat(ui_heartbeat_ctx, rx);
212 5 : break;
213 1 : case RSK_END_CMD: // return to app
214 1 : THROW(EX_BOOTLOADER_RSK_END);
215 0 : default:
216 0 : THROW(ERR_INS_NOT_SUPPORTED);
217 : break;
218 : }
219 :
220 6 : return tx;
221 : }
222 :
223 : /**
224 : * Main function for the heartbeat frontend.
225 : * It basically only allows for gathering the heartbeat
226 : * and re-executing the authorized app
227 : * (i.e., the signer).
228 : *
229 : * @arg[in] ui_heartbeat_ctx the UI heartbeat context
230 : */
231 14 : void ui_heartbeat_main(ui_heartbeat_t *ui_heartbeat_ctx) {
232 14 : volatile unsigned int rx = 0;
233 14 : volatile unsigned int tx = 0;
234 :
235 : // DESIGN NOTE: the bootloader ignores the way APDU are fetched. The only
236 : // goal is to retrieve APDU.
237 : // When APDU are to be fetched from multiple IOs, like NFC+USB+BLE, make
238 : // sure the io_event is called with a
239 : // switch event, before the apdu is replied to the bootloader. This avoid
240 : // APDU injection faults.
241 : for (;;) {
242 13 : BEGIN_TRY {
243 27 : TRY {
244 27 : rx = tx;
245 27 : tx = 0; // ensure no race in catch_other if io_exchange throws
246 : // an error
247 27 : rx = io_exchange(CHANNEL_APDU, rx);
248 :
249 14 : tx = ui_heartbeat_process_apdu(ui_heartbeat_ctx, rx);
250 6 : THROW(APDU_OK);
251 : }
252 27 : CATCH(EX_BOOTLOADER_RSK_END) {
253 14 : break;
254 : }
255 13 : CATCH_OTHER(e) {
256 13 : current_context = ui_heartbeat_ctx;
257 13 : tx = ui_process_exception(e, tx, &reset_state);
258 : }
259 13 : FINALLY {
260 : }
261 : }
262 13 : END_TRY;
263 : }
264 14 : }
|