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