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 : #include "os.h"
27 : #include "cx.h"
28 : #include "attestation.h"
29 : #include "defs.h"
30 : #include "ui_err.h"
31 : #include "memutil.h"
32 : #include "ints.h"
33 : #include "runtime.h"
34 :
35 : // Utility macros to save memory
36 : #define MIN(x, y) ((x) < (y) ? (x) : (y))
37 : #define PAGESIZE (APDU_TOTAL_DATA_SIZE_OUT - 1)
38 : #define PAGECOUNT(itemcount) (((itemcount) + PAGESIZE - 1) / PAGESIZE)
39 :
40 : // Global onboarding flag
41 : extern NON_VOLATILE unsigned char* N_onboarded_ui[1];
42 :
43 : // Attestation message prefix
44 : const char att_msg_prefix[ATT_MSG_PREFIX_LENGTH] = ATT_MSG_PREFIX;
45 :
46 : // Path of the public key to derive
47 : const char key_derivation_path[PUBKEY_PATH_LENGTH] = PUBKEY_PATH;
48 :
49 : /*
50 : * Check the SM for the attestation generation
51 : * matches the expected state.
52 : *
53 : * Reset the state and throw a protocol error
54 : * otherwise.
55 : */
56 7 : static void check_state(att_t* att_ctx, att_state_t expected) {
57 7 : if (att_ctx->state != expected) {
58 3 : reset_attestation(att_ctx);
59 3 : THROW(ERR_UI_PROT_INVALID);
60 : }
61 4 : }
62 :
63 : /*
64 : * Throw an internal error unless the APDU
65 : * buffer data part is large enough to fit
66 : * the given number of bytes
67 : *
68 : * The reason this is treated as an internal
69 : * error is that all operations checking this
70 : * already know that the buffer *should* be
71 : * large enough and are just sanity checking
72 : */
73 1 : static void check_apdu_buffer_holds(size_t size) {
74 1 : if (APDU_TOTAL_DATA_SIZE_OUT < size) {
75 0 : THROW(ERR_UI_INTERNAL);
76 : }
77 1 : }
78 :
79 : /*
80 : * Given an uncompressed secp256k1 public key,
81 : * compress it to the given destination, returning
82 : * the size.
83 : *
84 : * @arg[in] pub_key public key
85 : * @arg[in] dst destination
86 : * @arg[in] dst_size destination size
87 : * @arg[in] dst_offset destination offset
88 : * @ret size of the compressed public key
89 : */
90 1 : static size_t compress_pubkey_into(cx_ecfp_public_key_t* pub_key,
91 : uint8_t* dst,
92 : size_t dst_size,
93 : size_t dst_offset) {
94 2 : SAFE_MEMMOVE(dst,
95 : dst_size,
96 : dst_offset,
97 : pub_key->W,
98 : sizeof(pub_key->W),
99 : MEMMOVE_ZERO_OFFSET,
100 : PUBKEY_CMP_LENGTH,
101 : THROW(ERR_UI_INTERNAL));
102 1 : dst[dst_offset] = pub_key->W[pub_key->W_len - 1] & 0x01 ? 0x03 : 0x02;
103 1 : return PUBKEY_CMP_LENGTH;
104 : }
105 :
106 : /*
107 : * Reset the given attestation context
108 : *
109 : * @arg[in] att_ctx attestation context
110 : */
111 12 : void reset_attestation(att_t* att_ctx) {
112 12 : explicit_bzero(att_ctx, sizeof(att_t));
113 12 : att_ctx->state = att_state_wait_ud_value;
114 12 : }
115 :
116 : // -----------------------------------------------------------------------
117 : // Protocol implementation
118 : // -----------------------------------------------------------------------
119 :
120 : /*
121 : * Implement the attestation protocol.
122 : *
123 : * @arg[in] rx number of received bytes from the Host
124 : * @arg[in] att_ctx attestation context
125 : * @ret number of transmited bytes to the host
126 : */
127 9 : unsigned int get_attestation(volatile unsigned int rx, att_t* att_ctx) {
128 : uint8_t message_size;
129 :
130 : // Verify that the device has been onboarded
131 9 : unsigned char onboarded = *((unsigned char*)PIC(N_onboarded_ui));
132 9 : if (!onboarded) {
133 1 : THROW(ATT_NO_ONBOARD);
134 : return 0;
135 : }
136 :
137 8 : switch (APDU_OP()) {
138 2 : case OP_ATT_UD_VALUE:
139 2 : check_state(att_ctx, att_state_wait_ud_value);
140 :
141 : // Should receive a user-defined value
142 1 : if (APDU_DATA_SIZE(rx) != UD_VALUE_SIZE)
143 0 : THROW(ERR_UI_PROT_INVALID);
144 :
145 1 : sigaut_signer_t* current_signer_info = get_authorized_signer_info();
146 :
147 : // Initialize message
148 1 : explicit_bzero(att_ctx->msg, sizeof(att_ctx->msg));
149 1 : att_ctx->msg_offset = 0;
150 :
151 : // Copy prefix and user defined value into the message space
152 2 : SAFE_MEMMOVE(att_ctx->msg,
153 : sizeof(att_ctx->msg),
154 : att_ctx->msg_offset,
155 : (const void*)PIC(att_msg_prefix),
156 : sizeof(att_msg_prefix),
157 : MEMMOVE_ZERO_OFFSET,
158 : sizeof(att_msg_prefix),
159 : THROW(ERR_UI_INTERNAL));
160 1 : att_ctx->msg_offset += sizeof(att_msg_prefix);
161 2 : SAFE_MEMMOVE(att_ctx->msg,
162 : sizeof(att_ctx->msg),
163 : att_ctx->msg_offset,
164 : APDU_DATA_PTR,
165 : APDU_TOTAL_DATA_SIZE,
166 : MEMMOVE_ZERO_OFFSET,
167 : UD_VALUE_SIZE,
168 : THROW(ERR_UI_INTERNAL));
169 1 : att_ctx->msg_offset += UD_VALUE_SIZE;
170 :
171 : // Derive, compress and copy the public key into the message space
172 : BEGIN_TRY {
173 1 : TRY {
174 2 : SAFE_MEMMOVE(att_ctx->path,
175 : sizeof(att_ctx->path),
176 : MEMMOVE_ZERO_OFFSET,
177 : (const void*)PIC(key_derivation_path),
178 : sizeof(key_derivation_path),
179 : MEMMOVE_ZERO_OFFSET,
180 : sizeof(key_derivation_path),
181 : THROW(ERR_UI_INTERNAL));
182 : // Derive private key
183 1 : os_perso_derive_node_bip32(CX_CURVE_256K1,
184 1 : (unsigned int*)att_ctx->path,
185 : PATH_PART_COUNT,
186 1 : att_ctx->priv_key_data,
187 : NULL);
188 1 : cx_ecdsa_init_private_key(CX_CURVE_256K1,
189 1 : att_ctx->priv_key_data,
190 : PRIVATE_KEY_LENGTH,
191 : &att_ctx->priv_key);
192 : // Cleanup private key data
193 1 : explicit_bzero(att_ctx->priv_key_data,
194 : sizeof(att_ctx->priv_key_data));
195 : // Derive public key
196 1 : cx_ecfp_generate_pair(
197 : CX_CURVE_256K1, &att_ctx->pub_key, &att_ctx->priv_key, 1);
198 : // Cleanup private key
199 1 : explicit_bzero(&att_ctx->priv_key, sizeof(att_ctx->priv_key));
200 : // Compress public key into message space
201 1 : att_ctx->msg_offset +=
202 2 : compress_pubkey_into(&att_ctx->pub_key,
203 1 : att_ctx->msg,
204 : sizeof(att_ctx->msg),
205 1 : att_ctx->msg_offset);
206 : // Cleanup public key
207 1 : explicit_bzero(&att_ctx->pub_key, sizeof(att_ctx->pub_key));
208 : }
209 1 : CATCH_OTHER(e) {
210 : // Cleanup key data and fail
211 0 : explicit_bzero(att_ctx->priv_key_data,
212 : sizeof(att_ctx->priv_key_data));
213 0 : explicit_bzero(&att_ctx->priv_key, sizeof(att_ctx->priv_key));
214 0 : explicit_bzero(&att_ctx->pub_key, sizeof(att_ctx->pub_key));
215 0 : THROW(ERR_UI_INTERNAL);
216 : }
217 1 : FINALLY {
218 : }
219 : }
220 1 : END_TRY;
221 :
222 : // Copy signer hash and iteration into the message space
223 2 : SAFE_MEMMOVE(att_ctx->msg,
224 : sizeof(att_ctx->msg),
225 : att_ctx->msg_offset,
226 : current_signer_info->hash,
227 : sizeof(current_signer_info->hash),
228 : MEMMOVE_ZERO_OFFSET,
229 : sizeof(current_signer_info->hash),
230 : THROW(ERR_UI_INTERNAL));
231 1 : att_ctx->msg_offset += sizeof(current_signer_info->hash);
232 :
233 : // Make sure iteration fits
234 1 : if (att_ctx->msg_offset + sizeof(current_signer_info->iteration) >
235 : sizeof(att_ctx->msg))
236 0 : THROW(ERR_UI_INTERNAL);
237 :
238 3 : VAR_BIGENDIAN_TO(att_ctx->msg + att_ctx->msg_offset,
239 : current_signer_info->iteration,
240 : sizeof(current_signer_info->iteration));
241 1 : att_ctx->msg_offset += sizeof(current_signer_info->iteration);
242 :
243 1 : att_ctx->state = att_state_ready;
244 :
245 1 : return TX_FOR_DATA_SIZE(0);
246 3 : case OP_ATT_GET_MSG:
247 : // Retrieve message to sign page
248 3 : check_state(att_ctx, att_state_ready);
249 :
250 : // Should receive a page index
251 2 : if (APDU_DATA_SIZE(rx) != 1)
252 0 : THROW(ERR_UI_PROT_INVALID);
253 :
254 : // Maximum page size is APDU data part size minus one
255 : // byte (first byte of the response), which is used to indicate
256 : // whether there is a next page or not.
257 :
258 : // Check page index within range (page index is zero based)
259 2 : if (APDU_DATA_PTR[0] >= PAGECOUNT(att_ctx->msg_offset)) {
260 0 : THROW(ERR_UI_PROT_INVALID);
261 : }
262 :
263 : // Copy the page into the APDU buffer (no need to check for limits since
264 : // the chunk size is based directly on the APDU size)
265 2 : message_size =
266 2 : MIN(PAGESIZE, att_ctx->msg_offset - (APDU_DATA_PTR[0] * PAGESIZE));
267 4 : SAFE_MEMMOVE(APDU_DATA_PTR,
268 : APDU_TOTAL_DATA_SIZE_OUT,
269 : 1,
270 : att_ctx->msg,
271 : sizeof(att_ctx->msg),
272 : APDU_DATA_PTR[0] * PAGESIZE,
273 : message_size,
274 : THROW(ERR_UI_INTERNAL));
275 2 : APDU_DATA_PTR[0] =
276 2 : APDU_DATA_PTR[0] < (PAGECOUNT(att_ctx->msg_offset) - 1);
277 :
278 2 : return TX_FOR_DATA_SIZE(message_size + 1);
279 2 : case OP_ATT_GET:
280 2 : check_state(att_ctx, att_state_ready);
281 :
282 : // Reset SM
283 1 : att_ctx->state = att_state_wait_ud_value;
284 :
285 : // Sign and output
286 1 : check_apdu_buffer_holds(MAX_SIGNATURE_LENGTH);
287 1 : return TX_FOR_DATA_SIZE(os_endorsement_key2_derive_sign_data(
288 : att_ctx->msg, att_ctx->msg_offset, APDU_DATA_PTR));
289 0 : case OP_ATT_APP_HASH:
290 : // This can be asked for at any time
291 0 : check_apdu_buffer_holds(HASH_LENGTH);
292 0 : return TX_FOR_DATA_SIZE(os_endorsement_get_code_hash(APDU_DATA_PTR));
293 1 : default:
294 : // Reset SM
295 1 : att_ctx->state = att_state_wait_ud_value;
296 1 : THROW(ERR_UI_PROT_INVALID);
297 : break;
298 : }
299 : }
|