Embedded SDK
Embedded SDK
u2f_transport.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 #ifdef HAVE_IO_U2F
20 
21 #include <stdint.h>
22 #include <string.h>
23 #include "u2f_service.h"
24 #include "u2f_transport.h"
25 #include "u2f_processing.h"
26 #include "u2f_io.h"
27 
28 #include "lcx_rng.h"
29 #include "lcx_crc.h"
30 #include "os.h"
31 #include "os_io_seproxyhal.h"
32 
33 #define U2F_MASK_COMMAND 0x80
34 #define U2F_COMMAND_HEADER_SIZE 3
35 
36 static const uint8_t BROADCAST_CHANNEL[] = {0xff, 0xff, 0xff, 0xff};
37 static const uint8_t FORBIDDEN_CHANNEL[] = {0x00, 0x00, 0x00, 0x00};
38 
39 /* TODO: take into account the INIT during SEGMENTED message correctly.
40  * Avoid erasing the first part of the apdu buffer when doing so)
41  */
42 
43 // init
44 void u2f_transport_reset(u2f_service_t *service)
45 {
46  service->transportState = U2F_IDLE;
47  service->transportOffset = 0;
48  service->transportMedia = 0;
49  service->transportPacketIndex = 0;
51  service->fakeChannelTransportOffset = 0;
53  service->sending = false;
55  // reset the receive buffer to allow for a new message to be received again (in case
56  // transmission of a CODE buffer the previous reply)
57  service->transportBuffer = service->transportReceiveBuffer;
58  cx_rng(service->channel, U2F_CHANNEL_ID_SIZE);
59 }
60 
64 void u2f_transport_init(u2f_service_t *service,
65  uint8_t *message_buffer,
66  uint16_t message_buffer_length)
67 {
68  service->next_channel = 1;
69  service->transportReceiveBuffer = message_buffer;
70  service->transportReceiveBufferLength = message_buffer_length;
71  u2f_transport_reset(service);
72 }
73 
77 static void u2f_transport_error(u2f_service_t *service, char errorCode)
78 {
79  // u2f_transport_reset(service); // warning reset first to allow for U2F_io sent call to
80  // u2f_transport_sent internally on eventless platforms
81  G_io_usb_ep_buffer[8] = errorCode;
82 
83  // ensure the state is set to error sending to allow for special treatment in case reply is not
84  // read by the receiver
86  service->transportPacketIndex = 0;
87  service->transportBuffer = G_io_usb_ep_buffer + 8;
88  service->transportOffset = 0;
89  service->transportLength = 1;
90  service->sendCmd = U2F_STATUS_ERROR;
91  // pump the first message, with the reception media
92  u2f_transport_sent(service, service->media);
93 }
94 
100 {
101  // don't process when replying to anti timeout requests
102  if (!u2f_message_repliable(service)) {
103  // previous mark packet as sent
104  service->sending = false;
105  return;
106  }
107 
108  // previous mark packet as sent
109  service->sending = false;
110 
111  // if idle (possibly after an error), then only await for a transmission
112  if (service->transportState != U2F_SENDING_RESPONSE
113  && service->transportState != U2F_SENDING_ERROR) {
114  // absorb the error, transport is erroneous but that won't hurt in the end.
115  // also absorb the fake channel user presence check reply ack
116  // THROW(INVALID_STATE);
117  return;
118  }
119  if (service->transportOffset < service->transportLength) {
120  uint16_t mtu = (media == U2F_MEDIA_USB) ? USB_SEGMENT_SIZE : BLE_SEGMENT_SIZE;
121  uint16_t channelHeader = (media == U2F_MEDIA_USB ? 4 : 0);
122  uint8_t headerSize
123  = (service->transportPacketIndex == 0 ? (channelHeader + 3) : (channelHeader + 1));
124  uint16_t blockSize
125  = ((service->transportLength - service->transportOffset) > (mtu - headerSize)
126  ? (mtu - headerSize)
127  : service->transportLength - service->transportOffset);
128  uint16_t dataSize = blockSize + headerSize;
129  uint16_t offset = 0;
130  // Fragment
131  if (media == U2F_MEDIA_USB) {
132  memcpy(G_io_usb_ep_buffer, service->channel, U2F_CHANNEL_ID_SIZE);
133  offset += 4;
134  }
135  if (service->transportPacketIndex == 0) {
136  G_io_usb_ep_buffer[offset++] = service->sendCmd;
137  G_io_usb_ep_buffer[offset++] = (service->transportLength >> 8);
138  G_io_usb_ep_buffer[offset++] = (service->transportLength & 0xff);
139  }
140  else {
141  G_io_usb_ep_buffer[offset++] = (service->transportPacketIndex - 1);
142  }
143  if (service->transportBuffer != NULL) {
144  memmove(G_io_usb_ep_buffer + headerSize,
145  service->transportBuffer + service->transportOffset,
146  blockSize);
147  }
148  service->transportOffset += blockSize;
149  service->transportPacketIndex++;
150  u2f_io_send(G_io_usb_ep_buffer, dataSize, media);
151  }
152  // last part sent
153  else if (service->transportOffset == service->transportLength) {
154  u2f_transport_reset(service);
155  // we sent the whole response (even if we haven't yet received the ack for the last sent usb
156  // in packet)
157  G_io_app.apdu_state = APDU_IDLE;
158  }
159 }
160 
161 void u2f_transport_send_usb_user_presence_required(u2f_service_t *service)
162 {
163  uint16_t offset = 0;
164  service->sending = true;
165  memcpy(G_io_usb_ep_buffer, service->channel, U2F_CHANNEL_ID_SIZE);
166  offset += 4;
167  G_io_usb_ep_buffer[offset++] = U2F_CMD_MSG;
168  G_io_usb_ep_buffer[offset++] = 0;
169  G_io_usb_ep_buffer[offset++] = 2;
170  G_io_usb_ep_buffer[offset++] = 0x69;
171  G_io_usb_ep_buffer[offset++] = 0x85;
172  u2f_io_send(G_io_usb_ep_buffer, offset, U2F_MEDIA_USB);
173 }
174 
175 void u2f_transport_send_wink(u2f_service_t *service)
176 {
177  uint16_t offset = 0;
178  service->sending = true;
179  memcpy(G_io_usb_ep_buffer, service->channel, U2F_CHANNEL_ID_SIZE);
180  offset += 4;
181  G_io_usb_ep_buffer[offset++] = U2F_CMD_WINK;
182  G_io_usb_ep_buffer[offset++] = 0;
183  G_io_usb_ep_buffer[offset++] = 0;
184  u2f_io_send(G_io_usb_ep_buffer, offset, U2F_MEDIA_USB);
185 }
186 
187 #ifdef HAVE_FIDO2
188 
190 {
191  uint16_t offset = 0;
192  service->sending = true;
193  memcpy(G_io_usb_ep_buffer, service->channel, U2F_CHANNEL_ID_SIZE);
194  offset += 4;
195  G_io_usb_ep_buffer[offset++] = CTAP2_STATUS_KEEPALIVE;
196  G_io_usb_ep_buffer[offset++] = 0;
197  G_io_usb_ep_buffer[offset++] = 1;
198  G_io_usb_ep_buffer[offset++] = reason;
199  u2f_io_send(G_io_usb_ep_buffer, offset, U2F_MEDIA_USB);
200 }
201 
202 #endif
203 
204 bool u2f_transport_receive_fakeChannel(u2f_service_t *service, uint8_t *buffer, uint16_t size)
205 {
207  return false;
208  }
209  if (memcmp(service->channel, buffer, U2F_CHANNEL_ID_SIZE) != 0) {
210  goto error;
211  }
212  if (service->fakeChannelTransportOffset == 0) {
213  uint16_t commandLength = U2BE(buffer, 4 + 1) + U2F_COMMAND_HEADER_SIZE;
214  // Some buggy implementations can send a WINK here, reply it gently
215  if (buffer[4] == U2F_CMD_WINK) {
216  u2f_transport_send_wink(service);
217  return true;
218  }
219 
220  if (commandLength != service->transportLength) {
221  goto error;
222  }
223  if (buffer[4] != U2F_CMD_MSG) {
224  goto error;
225  }
226  service->fakeChannelTransportOffset = MIN(size - 4, service->transportLength);
227  service->fakeChannelTransportPacketIndex = 0;
228  service->fakeChannelCrc
229  = cx_crc16_update(0, buffer + 4, service->fakeChannelTransportOffset);
230  }
231  else {
232  if (buffer[4] != service->fakeChannelTransportPacketIndex) {
233  goto error;
234  }
235  uint16_t xfer_len
236  = MIN(size - 5, service->transportLength - service->fakeChannelTransportOffset);
238  service->fakeChannelTransportOffset += xfer_len;
239  service->fakeChannelCrc = cx_crc16_update(service->fakeChannelCrc, buffer + 5, xfer_len);
240  }
241  if (service->fakeChannelTransportOffset >= service->transportLength) {
242  if (service->fakeChannelCrc != service->commandCrc) {
243  goto error;
244  }
246  service->fakeChannelTransportOffset = 0;
247  // reply immediately when the asynch response is not yet ready
249  u2f_transport_send_usb_user_presence_required(service);
250  // response sent
252  }
253  }
254  return true;
255 error:
257  // don't hesitate here, the user will have to exit/rerun the app otherwise.
258  THROW(EXCEPTION_IO_RESET);
259  return false;
260 }
261 
267  uint8_t *buffer,
268  uint16_t size,
269  u2f_transport_media_t media)
270 {
271  uint16_t channelHeader = (media == U2F_MEDIA_USB ? 4 : 0);
272  uint16_t xfer_len;
273  service->media = media;
274 
275  // PRINTF("recv %d %d %d %d %d\n", size, service->waitAsynchronousResponse,
276  // service->transportState, service->transportOffset, buffer[4]);
277 
278  // Handle a busy channel and avoid reentry
279  if (service->transportState == U2F_SENDING_RESPONSE) {
280  u2f_transport_error(service, ERROR_CHANNEL_BUSY);
281  goto error;
282  }
284  // TODO : this is an error for FIDO 2
285  if (!u2f_transport_receive_fakeChannel(service, buffer, size)) {
286  u2f_transport_error(service, ERROR_CHANNEL_BUSY);
287  goto error;
288  }
289  return;
290  }
291 
292  // SENDING_ERROR is accepted, and triggers a reset => means the host hasn't consumed the error.
293  if (service->transportState == U2F_SENDING_ERROR) {
294  u2f_transport_reset(service);
295  }
296 
297  if (size < (1 + channelHeader)) {
298  // Message to short, abort
299  u2f_transport_error(service, ERROR_PROP_MESSAGE_TOO_SHORT);
300  goto error;
301  }
302  if (media == U2F_MEDIA_USB) {
303  // hold the current channel value to reply to, for example, INIT commands within flow of
304  // segments.
305  memcpy(service->channel, buffer, U2F_CHANNEL_ID_SIZE);
306  }
307 
308 #ifdef HAVE_FIDO2
309 
310  // Handle a cancel request if received
311 
312  if ((buffer[channelHeader] == CTAP2_CMD_CANCEL)
313  && (((media == U2F_MEDIA_USB)
314  && (memcmp(service->transportChannel, service->channel, U2F_CHANNEL_ID_SIZE) == 0))
315  || (media != U2F_MEDIA_USB))) {
316  // Drop the cancel request if there's no command to be processed, otherwise pass it to the
317  // upper layer immediately
318  if (service->transportState != U2F_PROCESSING_COMMAND) {
319  return;
320  }
321  uint16_t commandLength = U2BE(buffer, channelHeader + 1);
322  ctap2_handle_cmd_cancel(service, buffer + channelHeader + 1 + 2, commandLength);
323  return;
324  }
325 
326 #endif
327 
328  // no previous chunk processed for the current message
329  if (service->transportOffset == 0
330  // on USB we could get an INIT within a flow of segments.
331  || (media == U2F_MEDIA_USB
332  && memcmp(service->transportChannel, service->channel, U2F_CHANNEL_ID_SIZE) != 0)
333  // CTAP2 transport test (HID-1)
334  || (buffer[channelHeader] == U2F_CMD_INIT)) {
335  if (size < (channelHeader + 3)) {
336  // Message to short, abort
337  u2f_transport_error(service, ERROR_PROP_MESSAGE_TOO_SHORT);
338  goto error;
339  }
340  // check this is a command, cannot accept continuation without previous command
341  if ((buffer[channelHeader + 0] & U2F_MASK_COMMAND) == 0) {
342  // Not a command packet, abort
343  // CTAP2 transport test : do not send back an error in this case (HID-1)
344  // u2f_transport_error(service, ERROR_INVALID_SEQ);
345  goto error;
346  }
347 
348  // If waiting for a continuation on a different channel, reply BUSY
349  // immediately
350  if (media == U2F_MEDIA_USB) {
351  if ((service->transportState == U2F_HANDLE_SEGMENTED)
352  && (memcmp(service->channel, service->transportChannel, U2F_CHANNEL_ID_SIZE) != 0)
353  && (buffer[channelHeader] != U2F_CMD_INIT)) {
354  // special error case, we reply but don't change the current state of the transport
355  // (ongoing message for example)
356  // u2f_transport_error_no_reset(service, ERROR_CHANNEL_BUSY);
357  uint16_t offset = 0;
358  // Fragment
359  if (media == U2F_MEDIA_USB) {
360  memcpy(G_io_usb_ep_buffer, service->channel, U2F_CHANNEL_ID_SIZE);
361  offset += 4;
362  }
363  G_io_usb_ep_buffer[offset++] = U2F_STATUS_ERROR;
364  G_io_usb_ep_buffer[offset++] = 0;
365  G_io_usb_ep_buffer[offset++] = 1;
366  G_io_usb_ep_buffer[offset++] = ERROR_CHANNEL_BUSY;
367  u2f_io_send(G_io_usb_ep_buffer, offset, media);
368  goto error;
369  }
370  }
371  // If a command was already sent, and we are not processing a INIT
372  // command, abort
373  if ((service->transportState == U2F_HANDLE_SEGMENTED)
374  && !((media == U2F_MEDIA_USB) && (buffer[channelHeader] == U2F_CMD_INIT))) {
375  // Unexpected continuation at this stage, abort
376  u2f_transport_error(service, ERROR_INVALID_SEQ);
377  goto error;
378  }
379  // Check the length
380  uint16_t commandLength = U2BE(buffer, channelHeader + 1);
381  if (commandLength > (service->transportReceiveBufferLength - 3)) {
382  // Overflow in message size, abort
383  u2f_transport_error(service, ERROR_INVALID_LEN);
384  goto error;
385  }
386  // Check if the command is supported
387  switch (buffer[channelHeader]) {
388  case U2F_CMD_PING:
389  case U2F_CMD_MSG:
390 #ifdef HAVE_FIDO2
391  case CTAP2_CMD_CBOR:
392  case CTAP2_CMD_CANCEL:
393 #endif
394  if (media == U2F_MEDIA_USB) {
395  if (u2f_is_channel_broadcast(service->channel)
396  || u2f_is_channel_forbidden(service->channel)) {
397  u2f_transport_error(service, ERROR_INVALID_CID);
398  goto error;
399  }
400  }
401  // no channel for BLE
402  break;
403  case U2F_CMD_INIT:
404  if (media != U2F_MEDIA_USB) {
405  // Unknown command, abort
406  u2f_transport_error(service, ERROR_INVALID_CMD);
407  goto error;
408  }
409 
410  if (u2f_is_channel_forbidden(service->channel)) {
411  u2f_transport_error(service, ERROR_INVALID_CID);
412  goto error;
413  }
414 
415  break;
416  default:
417  // Unknown command, abort
418  u2f_transport_error(service, ERROR_INVALID_CMD);
419  goto error;
420  }
421 
422  // Ok, initialize the buffer
423  // if (buffer[channelHeader] != U2F_CMD_INIT)
424  {
425  xfer_len = MIN(size - (channelHeader), U2F_COMMAND_HEADER_SIZE + commandLength);
426  memmove(service->transportBuffer, buffer + channelHeader, xfer_len);
427  if (media == U2F_MEDIA_USB) {
428  service->commandCrc = cx_crc16_update(0, service->transportBuffer, xfer_len);
429  }
430  service->transportOffset = xfer_len;
431  service->transportLength = U2F_COMMAND_HEADER_SIZE + commandLength;
432  service->transportMedia = media;
433  // initialize the response
434  service->transportPacketIndex = 0;
435  memcpy(service->transportChannel, service->channel, U2F_CHANNEL_ID_SIZE);
436  }
437  }
438  else {
439  // Continuation
440  if (size < (channelHeader + 2)) {
441  // Message to short, abort
442  u2f_transport_error(service, ERROR_PROP_MESSAGE_TOO_SHORT);
443  goto error;
444  }
445  if (media != service->transportMedia) {
446  // Mixed media
447  u2f_transport_error(service, ERROR_PROP_MEDIA_MIXED);
448  goto error;
449  }
450  if (service->transportState != U2F_HANDLE_SEGMENTED) {
451  // Unexpected continuation at this stage, abort
452  // TODO : review the behavior is HID only
453  if (media == U2F_MEDIA_USB) {
454  u2f_transport_reset(service);
455  goto error;
456  }
457  else {
458  u2f_transport_error(service, ERROR_INVALID_SEQ);
459  goto error;
460  }
461  }
462  if (media == U2F_MEDIA_USB) {
463  // Check the channel
464  if (memcmp(buffer, service->channel, U2F_CHANNEL_ID_SIZE) != 0) {
465  u2f_transport_error(service, ERROR_CHANNEL_BUSY);
466  goto error;
467  }
468  }
469  // also discriminate invalid command sent instead of a continuation
470  if (buffer[channelHeader] != service->transportPacketIndex) {
471  // Bad continuation packet, abort
472  u2f_transport_error(service, ERROR_INVALID_SEQ);
473  goto error;
474  }
475  xfer_len
476  = MIN(size - (channelHeader + 1), service->transportLength - service->transportOffset);
477  memmove(service->transportBuffer + service->transportOffset,
478  buffer + channelHeader + 1,
479  xfer_len);
480  if (media == U2F_MEDIA_USB) {
481  service->commandCrc = cx_crc16_update(
482  service->commandCrc, service->transportBuffer + service->transportOffset, xfer_len);
483  }
484  service->transportOffset += xfer_len;
485  service->transportPacketIndex++;
486  }
487  // See if we can process the command
488  if ((media != U2F_MEDIA_USB)
489  && (service->transportOffset > (service->transportLength + U2F_COMMAND_HEADER_SIZE))) {
490  // Overflow, abort
491  u2f_transport_error(service, ERROR_INVALID_LEN);
492  goto error;
493  }
494  else if (service->transportOffset >= service->transportLength) {
495  // switch before the handler gets the opportunity to change it again
497  // internal notification of a complete message received
498  u2f_message_complete(service);
499  }
500  else {
501  // new segment received, reset the timeout for the current piece
502  service->seqTimeout = 0;
504  }
505 error:
506  return;
507 }
508 
509 bool u2f_is_channel_broadcast(uint8_t *channel)
510 {
511  return (memcmp(channel, BROADCAST_CHANNEL, 4) == 0);
512 }
513 
514 bool u2f_is_channel_forbidden(uint8_t *channel)
515 {
516  return (memcmp(channel, FORBIDDEN_CHANNEL, 4) == 0);
517 }
518 
523 {
524  // TODO : this only works for U2F
525 
526  if (enabled) {
527  // start replying placeholder until user presence validated
530  u2f_transport_send_usb_user_presence_required(service);
531  }
532  }
533  // don't set to REPLY_READY when it has not been enabled beforehand
534  else if (service->waitAsynchronousResponse == U2F_WAIT_ASYNCH_ON) {
536  }
537 }
538 
540 {
541  // no more asynch replies
542  // finished receiving the command
543  // and not sending a user presence required status
547  && service->sending == false);
548 }
549 
550 void u2f_message_reply(u2f_service_t *service, uint8_t cmd, uint8_t *buffer, uint16_t len)
551 {
552  // if U2F is not ready to reply, then gently avoid replying
553  if (u2f_message_repliable(service)) {
555  service->transportPacketIndex = 0;
556  service->transportBuffer = buffer;
557  service->transportOffset = 0;
558  service->transportLength = len;
559  service->sendCmd = cmd;
560  if (service->transportMedia != U2F_MEDIA_BLE) {
561  // pump the first message
562  u2f_transport_sent(service, service->transportMedia);
563  }
564  else {
565  while (G_io_app.apdu_state != APDU_IDLE) {
566  u2f_transport_sent(service, service->transportMedia);
567  }
568  }
569  }
570 }
571 
572 #endif
CRC (Cyclic Redundancy Check).
Random Number Generation.
#define MIN(x, y)
Definition: nbgl_types.h:79
uint16_t commandCrc
Definition: u2f_service.h:84
uint32_t next_channel
Definition: u2f_service.h:62
uint8_t * transportReceiveBuffer
Definition: u2f_service.h:68
uint8_t waitAsynchronousResponse
Definition: u2f_service.h:88
uint8_t * transportBuffer
Definition: u2f_service.h:76
uint8_t fakeChannelTransportPacketIndex
Definition: u2f_service.h:82
uint32_t seqTimeout
Definition: u2f_service.h:95
uint16_t transportReceiveBufferLength
Definition: u2f_service.h:70
uint8_t sendCmd
Definition: u2f_service.h:99
uint16_t fakeChannelCrc
Definition: u2f_service.h:85
uint8_t channel[U2F_CHANNEL_ID_SIZE]
Definition: u2f_service.h:64
u2f_transport_state_t transportState
Definition: u2f_service.h:77
uint16_t transportLength
Definition: u2f_service.h:74
uint8_t transportPacketIndex
Definition: u2f_service.h:75
uint16_t fakeChannelTransportOffset
Definition: u2f_service.h:81
u2f_transport_media_t media
Definition: u2f_service.h:65
uint16_t transportOffset
Definition: u2f_service.h:73
u2f_transport_media_t transportMedia
Definition: u2f_service.h:78
uint8_t transportChannel[4]
Definition: u2f_service.h:72
u2f_transport_state_t fakeChannelTransportState
Definition: u2f_service.h:83
void u2f_io_send(uint8_t *buffer, uint16_t length, u2f_transport_media_t media)
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)
bool u2f_message_repliable(u2f_service_t *service)
u2f_transport_media_t
Definition: u2f_service.h:47
@ U2F_MEDIA_BLE
Definition: u2f_service.h:51
@ U2F_MEDIA_USB
Definition: u2f_service.h:49
@ U2F_WAIT_ASYNCH_ON
Definition: u2f_service.h:56
@ U2F_WAIT_ASYNCH_REPLY_READY
Definition: u2f_service.h:57
@ U2F_WAIT_ASYNCH_IDLE
Definition: u2f_service.h:55
void ctap2_handle_cmd_cancel(u2f_service_t *service, uint8_t *buffer, uint16_t length)
@ U2F_HANDLE_SEGMENTED
Definition: u2f_service.h:39
@ U2F_FAKE_RECEIVED
Definition: u2f_service.h:44
@ U2F_IDLE
Definition: u2f_service.h:38
@ U2F_PROCESSING_COMMAND
Definition: u2f_service.h:40
@ U2F_SENDING_ERROR
Definition: u2f_service.h:42
@ U2F_INTERNAL_ERROR
Definition: u2f_service.h:43
@ U2F_SENDING_RESPONSE
Definition: u2f_service.h:41
#define U2F_CHANNEL_ID_SIZE
Definition: u2f_service.h:27
#define ERROR_PROP_MEDIA_MIXED
#define CTAP2_CMD_CANCEL
Definition: u2f_transport.h:29
#define ERROR_INVALID_LEN
Definition: u2f_transport.h:48
#define CTAP2_CMD_CBOR
Definition: u2f_transport.h:28
void u2f_transport_sent(u2f_service_t *service, u2f_transport_media_t media)
#define ERROR_INVALID_CID
Definition: u2f_transport.h:55
bool u2f_is_channel_forbidden(uint8_t *channel)
void u2f_transport_init(u2f_service_t *service, uint8_t *message_buffer, uint16_t message_buffer_length)
#define U2F_CMD_INIT
Definition: u2f_transport.h:32
#define U2F_CMD_MSG
Definition: u2f_transport.h:27
#define U2F_STATUS_ERROR
Definition: u2f_transport.h:41
#define ERROR_CHANNEL_BUSY
Definition: u2f_transport.h:53
bool u2f_is_channel_broadcast(uint8_t *channel)
#define U2F_CMD_PING
Definition: u2f_transport.h:26
#define U2F_CMD_WINK
Definition: u2f_transport.h:34
void u2f_transport_received(u2f_service_t *service, uint8_t *buffer, uint16_t size, u2f_transport_media_t media)
#define ERROR_PROP_MESSAGE_TOO_SHORT
Definition: u2f_transport.h:95
#define CTAP2_STATUS_KEEPALIVE
Definition: u2f_transport.h:42
void u2f_transport_ctap2_send_keepalive(u2f_service_t *service, uint8_t reason)
#define ERROR_INVALID_SEQ
Definition: u2f_transport.h:49
#define ERROR_INVALID_CMD
Definition: u2f_transport.h:46
unsigned short uint16_t
Definition: usbd_conf.h:54
unsigned char uint8_t
Definition: usbd_conf.h:53