Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
u2f_impl.c
Go to the documentation of this file.
1
2/*******************************************************************************
3 * Ledger Nano S - Secure firmware
4 * (c) 2022 Ledger
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 ********************************************************************************/
18
19#include <stdint.h>
20#include <string.h>
21
22#ifdef HAVE_IO_U2F
23
24#include "os.h"
25#include "os_io_seproxyhal.h"
26#include "u2f_service.h"
27#include "u2f_transport.h"
28#include "u2f_processing.h"
29
30#define INIT_U2F_VERSION 0x02
31#define INIT_DEVICE_VERSION_MAJOR 0
32#define INIT_DEVICE_VERSION_MINOR 1
33#define INIT_BUILD_VERSION 0
34
35#ifdef HAVE_FIDO2
36#define INIT_CAPABILITIES 0x04
37#else
38#define INIT_CAPABILITIES 0x00
39#endif
40
41#define OFFSET_CLA 0
42#define OFFSET_INS 1
43#define OFFSET_P1 2
44#define OFFSET_P2 3
45#define OFFSET_DATA 7
46
47#define APDU_MIN_HEADER 4
48#define LC_FIRST_BYTE_OFFSET 4
49#define LONG_ENC_LC_SIZE 3
50#define LONG_ENC_LE_SIZE 2 // considering only scenarios where Lc is present
51
52#define FIDO_CLA 0x00
53#define FIDO_INS_ENROLL 0x01
54#define FIDO_INS_SIGN 0x02
55#define U2F_HANDLE_SIGN_HEADER_SIZE (32 + 32 + 1)
56#define FIDO_INS_GET_VERSION 0x03
57
58#define FIDO_INS_PROP_GET_COUNTER 0xC0 // U2F_VENDOR_FIRST
59#define FIDO_INS_PROP_GET_INFO 0xC1 // grab the max message buffer size on 1 byte
60
61#define P1_SIGN_CHECK_ONLY 0x07
62#define P1_SIGN_SIGN 0x03
63
64#define SIGN_USER_PRESENCE_MASK 0x01
65
66#ifndef U2F_PROXY_MAGIC
67
68static const uint8_t SW_WRONG_LENGTH[] = {0x67, 0x00};
69
70#else // U2F_PROXY_MAGIC
71
72static const uint8_t SW_BUSY[] = {0x90, 0x01};
73static const uint8_t SW_PROOF_OF_PRESENCE_REQUIRED[] = {0x69, 0x85};
74static const uint8_t SW_BAD_KEY_HANDLE[] = {0x6A, 0x80};
75
76static const uint8_t SW_UNKNOWN_INSTRUCTION[] = {0x6d, 0x00};
77static const uint8_t SW_UNKNOWN_CLASS[] = {0x6e, 0x00};
78static const uint8_t SW_WRONG_LENGTH[] = {0x67, 0x00};
79static const uint8_t SW_INTERNAL[] = {0x6F, 0x00};
80
81static const uint8_t U2F_VERSION[] = {'U', '2', 'F', '_', 'V', '2', 0x90, 0x00};
82
83// take into account max header (u2f usb)
84static const uint8_t INFO[] = {1 /*info format 1*/,
85 (char) (IO_APDU_BUFFER_SIZE - U2F_HANDLE_SIGN_HEADER_SIZE - 3 - 4),
86 0x90,
87 0x00};
88
89// proxy mode enroll issue an error
90void u2f_apdu_enroll(u2f_service_t *service,
91 uint8_t p1,
92 uint8_t p2,
93 uint8_t *buffer,
94 uint16_t length)
95{
96 UNUSED(p1);
97 UNUSED(p2);
98 UNUSED(buffer);
99 UNUSED(length);
100
101 u2f_message_reply(service, U2F_CMD_MSG, (uint8_t *) SW_INTERNAL, sizeof(SW_INTERNAL));
102}
103
104void u2f_apdu_sign(u2f_service_t *service, uint8_t p1, uint8_t p2, uint8_t *buffer, uint16_t length)
105{
106 UNUSED(p2);
107 uint8_t keyHandleLength;
108 uint8_t i;
109
110 // can't process the apdu if another one is already scheduled in
111 if (G_io_app.apdu_state != APDU_IDLE) {
112 u2f_message_reply(service, U2F_CMD_MSG, (uint8_t *) SW_BUSY, sizeof(SW_BUSY));
113 return;
114 }
115
116 if (length < U2F_HANDLE_SIGN_HEADER_SIZE + 5 /*at least an apdu header*/) {
118 service, U2F_CMD_MSG, (uint8_t *) SW_WRONG_LENGTH, sizeof(SW_WRONG_LENGTH));
119 return;
120 }
121
122 // Confirm immediately if it's just a validation call
123 if (p1 == P1_SIGN_CHECK_ONLY) {
124 u2f_message_reply(service,
126 (uint8_t *) SW_PROOF_OF_PRESENCE_REQUIRED,
127 sizeof(SW_PROOF_OF_PRESENCE_REQUIRED));
128 return;
129 }
130
131 // Unwrap magic
132 keyHandleLength = buffer[U2F_HANDLE_SIGN_HEADER_SIZE - 1];
133 if (U2F_HANDLE_SIGN_HEADER_SIZE + keyHandleLength != length) {
135 service, U2F_CMD_MSG, (uint8_t *) SW_WRONG_LENGTH, sizeof(SW_WRONG_LENGTH));
136 return;
137 }
138
139 // reply to the "get magic" question of the host
140 if (keyHandleLength == 5) {
141 // GET U2F PROXY PARAMETERS
142 // this apdu is not subject to proxy magic masking
143 // APDU is F1 D0 00 00 00 to get the magic proxy
144 // RAPDU: <>
145 if (memcmp(buffer + U2F_HANDLE_SIGN_HEADER_SIZE, "\xF1\xD0\x00\x00\x00", 5) == 0) {
146 // U2F_PROXY_MAGIC is given as a 0 terminated string
147 G_io_apdu_buffer[0] = sizeof(U2F_PROXY_MAGIC) - 1;
148 memcpy(G_io_apdu_buffer + 1, U2F_PROXY_MAGIC, sizeof(U2F_PROXY_MAGIC) - 1);
149 memcpy(G_io_apdu_buffer + 1 + sizeof(U2F_PROXY_MAGIC) - 1, "\x90\x00\x90\x00", 4);
150 u2f_message_reply(service,
152 (uint8_t *) G_io_apdu_buffer,
153 G_io_apdu_buffer[0] + 1 + 2 + 2);
154 // processing finished. don't go further in the u2f msg processing
155 return;
156 }
157 }
158
159 for (i = 0; i < keyHandleLength; i++) {
160 buffer[U2F_HANDLE_SIGN_HEADER_SIZE + i]
161 ^= U2F_PROXY_MAGIC[i % (sizeof(U2F_PROXY_MAGIC) - 1)];
162 }
163 // Check that it looks like an APDU
164 if (length != U2F_HANDLE_SIGN_HEADER_SIZE + 5 + buffer[U2F_HANDLE_SIGN_HEADER_SIZE + 4]) {
166 service, U2F_CMD_MSG, (uint8_t *) SW_BAD_KEY_HANDLE, sizeof(SW_BAD_KEY_HANDLE));
167 return;
168 }
169
170 // make the apdu available to higher layers
171 memmove(G_io_apdu_buffer, buffer + U2F_HANDLE_SIGN_HEADER_SIZE, keyHandleLength);
172 G_io_app.apdu_length = keyHandleLength;
173 G_io_app.apdu_media = IO_APDU_MEDIA_U2F; // the effective transport is managed by the U2F layer
174 G_io_app.apdu_state = APDU_U2F;
175
176 // prepare for asynch reply
178
179 // don't reset the u2f processing command state, as we still await for the io_exchange caller to
180 // make the response call
181 /*
182 app_dispatch();
183 if ((btchip_context_D.io_flags & IO_ASYNCH_REPLY) == 0) {
184 u2f_proxy_response(service, btchip_context_D.outLength);
185 }
186 */
187}
188
189void u2f_apdu_get_version(u2f_service_t *service,
190 uint8_t p1,
191 uint8_t p2,
192 uint8_t *buffer,
193 uint16_t length)
194{
195 // screen_printf("U2F version\n");
196 UNUSED(p1);
197 UNUSED(p2);
198 UNUSED(buffer);
199 UNUSED(length);
200 u2f_message_reply(service, U2F_CMD_MSG, (uint8_t *) U2F_VERSION, sizeof(U2F_VERSION));
201}
202
203// Special command that returns the proxy
204void u2f_apdu_get_info(u2f_service_t *service,
205 uint8_t p1,
206 uint8_t p2,
207 uint8_t *buffer,
208 uint16_t length)
209{
210 UNUSED(p1);
211 UNUSED(p2);
212 UNUSED(buffer);
213 UNUSED(length);
214 u2f_message_reply(service, U2F_CMD_MSG, (uint8_t *) INFO, sizeof(INFO));
215}
216
217#endif // U2F_PROXY_MAGIC
218
219void u2f_handle_cmd_init(u2f_service_t *service,
220 uint8_t *buffer,
221 uint16_t length,
222 uint8_t *channelInit)
223{
224 // screen_printf("U2F init\n");
225 uint8_t channel[4];
226 (void) length;
227 if (u2f_is_channel_broadcast(channelInit)) {
228 // cx_rng_no_throw(channel, 4); // not available within the IO task, just do without
229 service->next_channel += 1;
230 U4BE_ENCODE(channel, 0, service->next_channel);
231 }
232 else {
233 memcpy(channel, channelInit, 4);
234 }
235 memmove(G_io_apdu_buffer, buffer, 8);
236 memcpy(G_io_apdu_buffer + 8, channel, 4);
237 G_io_apdu_buffer[12] = INIT_U2F_VERSION;
238 G_io_apdu_buffer[13] = INIT_DEVICE_VERSION_MAJOR;
239 G_io_apdu_buffer[14] = INIT_DEVICE_VERSION_MINOR;
240 G_io_apdu_buffer[15] = INIT_BUILD_VERSION;
241 G_io_apdu_buffer[16] = INIT_CAPABILITIES;
242
243 if (u2f_is_channel_broadcast(channelInit)) {
244 memset(service->channel, 0xff, 4);
245 }
246 else {
247 memcpy(service->channel, channel, 4);
248 }
249 u2f_message_reply(service, U2F_CMD_INIT, G_io_apdu_buffer, 17);
250}
251
252void u2f_handle_cmd_ping(u2f_service_t *service, uint8_t *buffer, uint16_t length)
253{
254 // screen_printf("U2F ping\n");
255 u2f_message_reply(service, U2F_CMD_PING, buffer, length);
256}
257
258int u2f_get_cmd_msg_data_length(const uint8_t *buffer, uint16_t length)
259{
260 /* Parse buffer to retrieve the data length.
261 Only Extended encoding is supported */
262
263 if (length < APDU_MIN_HEADER) {
264 return -1;
265 }
266
267 if (length == APDU_MIN_HEADER) {
268 // Either short or extended encoding with Lc and Le omitted
269 return 0;
270 }
271
272 if (length == APDU_MIN_HEADER + 1) {
273 // Short encoding, with next byte either Le or Lc with the other one omitted
274 // There is no way to tell so no way to check the value
275 // but anyway the data length is 0
276
277 // Support this particular short encoding APDU as Fido Conformance Tool v1.7.0
278 // is using it even though spec requires that short encoding should not be used
279 // over HID.
280 return 0;
281 }
282
283 if (length < APDU_MIN_HEADER + 3) {
284 // Short encoding or bad length
285 // We don't support short encoding
286 return -1;
287 }
288
289 if (length == APDU_MIN_HEADER + 3) {
290 if (buffer[4] != 0) {
291 // Short encoding or bad length
292 // We don't support short encoding
293 return -1;
294 }
295 // Can't be short encoding as Lc = 0x00 would lead to invalid length
296 // so extended encoding and either:
297 // - Lc = 0x00 0x00 0x00 and Le is omitted
298 // - Lc omitted and Le = 0x00 0xyy 0xzz
299 // so no way to check the value
300 // but anyway the data length is 0
301 return 0;
302 }
303
304 if (buffer[LC_FIRST_BYTE_OFFSET] != 0) {
305 // Short encoding or bad length
306 // We don't support short encoding
307 return -1;
308 }
309
310 // Can't be short encoding as Lc = 0 would lead to invalid length
311 // so extended encoding with Lc field present, optionally Le (2B) is present too
312 uint32_t dataLength
313 = (buffer[LC_FIRST_BYTE_OFFSET + 1] << 8) | (buffer[LC_FIRST_BYTE_OFFSET + 2]);
314
315 // Ensure that Lc value is consistent
316 if ((APDU_MIN_HEADER + LONG_ENC_LC_SIZE + dataLength != length)
317 && (APDU_MIN_HEADER + LONG_ENC_LC_SIZE + dataLength + LONG_ENC_LE_SIZE != length)) {
318 return -1;
319 }
320
321 return dataLength;
322}
323
324void u2f_handle_cmd_msg(u2f_service_t *service, uint8_t *buffer, uint16_t length)
325{
326 // screen_printf("U2F msg\n");
327
328#ifdef U2F_PROXY_MAGIC
329 uint8_t cla = buffer[OFFSET_CLA];
330 uint8_t ins = buffer[OFFSET_INS];
331 uint8_t p1 = buffer[OFFSET_P1];
332 uint8_t p2 = buffer[OFFSET_P2];
333#endif // U2F_PROXY_MAGIC
334
335 int dataLength = u2f_get_cmd_msg_data_length(buffer, length);
336 if (dataLength < 0) {
337 // invalid size
339 service, U2F_CMD_MSG, (uint8_t *) SW_WRONG_LENGTH, sizeof(SW_WRONG_LENGTH));
340 return;
341 }
342
343#ifndef U2F_PROXY_MAGIC
344
345 // No proxy mode, just pass the APDU as it is to the upper layer
346 memmove(G_io_apdu_buffer, buffer, length);
347 G_io_app.apdu_length = length;
348 G_io_app.apdu_media = IO_APDU_MEDIA_U2F; // the effective transport is managed by the U2F layer
349 G_io_app.apdu_state = APDU_U2F;
350
351#else // U2F_PROXY_MAGIC
352
353 if (cla != FIDO_CLA) {
355 service, U2F_CMD_MSG, (uint8_t *) SW_UNKNOWN_CLASS, sizeof(SW_UNKNOWN_CLASS));
356 return;
357 }
358 switch (ins) {
359 case FIDO_INS_ENROLL:
360 // screen_printf("enroll\n");
361 u2f_apdu_enroll(service, p1, p2, buffer + OFFSET_DATA, dataLength);
362 break;
363 case FIDO_INS_SIGN:
364 // screen_printf("sign\n");
365 u2f_apdu_sign(service, p1, p2, buffer + OFFSET_DATA, dataLength);
366 break;
367 case FIDO_INS_GET_VERSION:
368 // screen_printf("version\n");
369 u2f_apdu_get_version(service, p1, p2, buffer + OFFSET_DATA, dataLength);
370 break;
371
372 // only support by
373 case FIDO_INS_PROP_GET_INFO:
374 u2f_apdu_get_info(service, p1, p2, buffer + OFFSET_DATA, dataLength);
375 break;
376
377 default:
378 // screen_printf("unsupported\n");
379 u2f_message_reply(service,
381 (uint8_t *) SW_UNKNOWN_INSTRUCTION,
382 sizeof(SW_UNKNOWN_INSTRUCTION));
383 return;
384 }
385
386#endif // U2F_PROXY_MAGIC
387}
388
390{
391 uint8_t cmd = service->transportBuffer[0];
392 uint16_t length = (service->transportBuffer[1] << 8) | (service->transportBuffer[2]);
393 switch (cmd) {
394 case U2F_CMD_INIT:
395 u2f_handle_cmd_init(service, service->transportBuffer + 3, length, service->channel);
396 break;
397 case U2F_CMD_PING:
398 u2f_handle_cmd_ping(service, service->transportBuffer + 3, length);
399 break;
400 case U2F_CMD_MSG:
401 u2f_handle_cmd_msg(service, service->transportBuffer + 3, length);
402 break;
403#ifdef HAVE_FIDO2
404 case CTAP2_CMD_CBOR:
405 ctap2_handle_cmd_cbor(service, service->transportBuffer + 3, length);
406 break;
407 case CTAP2_CMD_CANCEL:
408 ctap2_handle_cmd_cancel(service, service->transportBuffer + 3, length);
409 break;
410#endif
411 }
412}
413
414#endif
#define OFFSET_P1
Definition offsets.h:14
#define OFFSET_P2
Definition offsets.h:18
#define OFFSET_INS
Definition offsets.h:10
#define OFFSET_CLA
Definition offsets.h:6
uint32_t next_channel
Definition u2f_service.h:62
uint8_t * transportBuffer
Definition u2f_service.h:76
uint8_t channel[U2F_CHANNEL_ID_SIZE]
Definition u2f_service.h:64
int u2f_get_cmd_msg_data_length(const uint8_t *buffer, uint16_t length)
void u2f_message_set_autoreply_wait_user_presence(u2f_service_t *service, bool enabled)
void u2f_message_complete(u2f_service_t *service)
void u2f_message_reply(u2f_service_t *service, uint8_t cmd, uint8_t *buffer, uint16_t length)
void ctap2_handle_cmd_cancel(u2f_service_t *service, uint8_t *buffer, uint16_t length)
void ctap2_handle_cmd_cbor(u2f_service_t *service, uint8_t *buffer, uint16_t length)
#define CTAP2_CMD_CANCEL
#define CTAP2_CMD_CBOR
#define U2F_CMD_INIT
#define U2F_CMD_MSG
bool u2f_is_channel_broadcast(uint8_t *channel)
#define U2F_CMD_PING
unsigned short uint16_t
Definition usbd_conf.h:54
unsigned char uint8_t
Definition usbd_conf.h:53