Skip to content

Commit

Permalink
U2F: separate queues
Browse files Browse the repository at this point in the history
  • Loading branch information
NickeZ committed Aug 13, 2019
1 parent 98cbdb5 commit 1f64cd3
Show file tree
Hide file tree
Showing 23 changed files with 405 additions and 200 deletions.
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ set(DBB-FIRMWARE-USB-SOURCES
${CMAKE_SOURCE_DIR}/src/usb/usb.c
${CMAKE_SOURCE_DIR}/src/usb/usb_frame.c
${CMAKE_SOURCE_DIR}/src/usb/usb_packet.c
${CMAKE_SOURCE_DIR}/src/u2f/u2f_packet.c
${CMAKE_SOURCE_DIR}/src/usb/usb_processing.c
${CMAKE_SOURCE_DIR}/src/queue.c
)
Expand Down
2 changes: 1 addition & 1 deletion src/bootloader/bootloader.c
Original file line number Diff line number Diff line change
Expand Up @@ -779,7 +779,7 @@ static void _api_setup(void)
{
const CMD_Callback cmd_callbacks[] = {{BOOTLOADER_CMD, _api_msg}};

usb_processing_register_cmds(cmd_callbacks, 1);
usb_processing_register_cmds(usb_processing_hww(), cmd_callbacks, 1);
}

#ifdef BOOTLOADER_PRODUCTION
Expand Down
2 changes: 1 addition & 1 deletion src/bootloader/startup.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ int main(void)

// If did not jump to firmware code, begin USB processing
while (1) {
usb_processing_process();
usb_processing_process(usb_processing_hww());
}

return 0;
Expand Down
3 changes: 3 additions & 0 deletions src/drivers/usb/class/hid/hww/hid_hww.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <queue.h>
#include "hid_hww.h"
#include "usb_desc.h"
#include "usb/usb_processing.h"

#define HID_HWW_VERSION 0x00000001u

Expand Down Expand Up @@ -125,6 +126,8 @@ void hid_hww_setup(void) {
// usb_report_sent is called when the outgoing usb frame is fully transmitted.
hid_hww_register_callback(HID_CB_WRITE, (FUNC_PTR) _sent_done);

usb_processing_set_send(usb_processing_hww(), _send_next);

// Wait for data
_read();
}
Expand Down
9 changes: 6 additions & 3 deletions src/drivers/usb/class/hid/u2f/hid_u2f.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@
// limitations under the License.

#include <string.h>
#include <usb/usb_packet.h>
#include <u2f/u2f_packet.h>
#include <queue.h>
#include "hid_u2f.h"
#include "usb_desc.h"
#include "usb/usb_processing.h"

#define HID_U2F_VERSION 0x00000001u

Expand Down Expand Up @@ -65,7 +66,7 @@ static int32_t _read(void)
* Sends the next data, if the USB interface is ready.
*/
static void _send_next(void) {
const uint8_t *data = queue_pull(queue_hww_queue());
const uint8_t *data = queue_pull(queue_u2f_queue());
if (data != NULL) {
hid_write(&_func_data, data, USB_HID_REPORT_OUT_SIZE);
} else {
Expand All @@ -86,7 +87,7 @@ static uint8_t _out(const uint8_t ep, const enum usb_xfer_code rc,
(void) rc;
(void) count;

bool need_more = usb_packet_process((const USB_FRAME *) _out_report, _send_next);
bool need_more = u2f_packet_process((const USB_FRAME *) _out_report, _send_next);
if (need_more) {
_read();
}
Expand Down Expand Up @@ -125,6 +126,8 @@ void hid_u2f_setup(void) {
// usb_report_sent is called when the outgoing usb frame is fully transmitted.
hid_u2f_register_callback(HID_CB_WRITE, (FUNC_PTR) _sent_done);

usb_processing_set_send(usb_processing_u2f(), _send_next);

// Wait for data
_read();
}
Expand Down
5 changes: 3 additions & 2 deletions src/factorysetup.c
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,8 @@ static void _api_msg(const Packet* in_packet, Packet* out_packet, const size_t m
static void _api_setup(void)
{
const CMD_Callback cmd_callbacks[] = {{FACTORYSETUP_CMD, _api_msg}};
usb_processing_register_cmds(cmd_callbacks, sizeof(cmd_callbacks) / sizeof(CMD_Callback));
usb_processing_register_cmds(
usb_processing_hww(), cmd_callbacks, sizeof(cmd_callbacks) / sizeof(CMD_Callback));
screen_print_debug("READY", 0);
}

Expand All @@ -197,6 +198,6 @@ int main(void)
}
usb_start(_api_setup);
while (1) {
usb_processing_process();
usb_processing_process(usb_processing_hww());
}
}
2 changes: 1 addition & 1 deletion src/hww.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,5 +110,5 @@ void hww_setup(void)
{
const CMD_Callback hww_cmd_callbacks[] = {{HWW_MSG, _msg}};
usb_processing_register_cmds(
hww_cmd_callbacks, sizeof(hww_cmd_callbacks) / sizeof(CMD_Callback));
usb_processing_hww(), hww_cmd_callbacks, sizeof(hww_cmd_callbacks) / sizeof(CMD_Callback));
}
6 changes: 3 additions & 3 deletions src/queue.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@

// `start` and `end` are indices into `items`
struct queue {
uint32_t start;
uint32_t end;
uint32_t volatile start;
uint32_t volatile end;
uint8_t items[QUEUE_NUM_REPORTS][USB_REPORT_SIZE];
};

Expand All @@ -47,7 +47,7 @@ const uint8_t* queue_pull(struct queue* ctx)
return ctx->items[p];
}

int32_t queue_push(struct queue* ctx, const uint8_t* data)
queue_error_t queue_push(struct queue* ctx, const uint8_t* data)
{
uint32_t next = (ctx->end + 1) % QUEUE_NUM_REPORTS;
if (ctx->start == next) {
Expand Down
8 changes: 5 additions & 3 deletions src/queue.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
#include <stdint.h>
#include <string.h>

#define QUEUE_ERR_NONE 0
#define QUEUE_ERR_FULL -1
typedef enum {
QUEUE_ERR_NONE = 0,
QUEUE_ERR_FULL = 1,
} queue_error_t;

struct queue;

Expand All @@ -28,7 +30,7 @@ struct queue;
* Returns QUEUE_ERR_NONE if the data was added and QUEUE_ERR_FULL if the buffer was full.
* data must be USB_REPORT_SIZE large
*/
int32_t queue_push(struct queue* ctx, const uint8_t* data);
queue_error_t queue_push(struct queue* ctx, const uint8_t* data);

/**
* Return the first data that was added to the queue.
Expand Down
2 changes: 1 addition & 1 deletion src/u2f.c
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,6 @@ void u2f_device_setup(void)
{U2FHID_MSG, _cmd_msg},
};
usb_processing_register_cmds(
u2f_cmd_callbacks, sizeof(u2f_cmd_callbacks) / sizeof(CMD_Callback));
usb_processing_u2f(), u2f_cmd_callbacks, sizeof(u2f_cmd_callbacks) / sizeof(CMD_Callback));
#endif
}
175 changes: 175 additions & 0 deletions src/u2f/u2f_packet.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
// Copyright 2019 Shift Cryptosecurity AG
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "u2f_packet.h"
#include "err_codes.h"
#include "queue.h"
#include "screen.h"
#include "usb/usb_processing.h"
#include <stdbool.h>
#include <stdlib.h>

// We can handle up to NUM_TIMEOUT_COUNTERS missing continuation frames
#define NUM_TIMEOUT_COUNTERS 3

struct frame_counter {
uint32_t cid;
uint8_t counter;
};

// cid == 0 indicates that there isn't any active timer for that slot
static volatile struct frame_counter _timeout_counters[NUM_TIMEOUT_COUNTERS];

static void _reset_timeout(uint32_t cid)
{
for (int i = 0; i < NUM_TIMEOUT_COUNTERS; ++i) {
if (_timeout_counters[i].cid == cid) {
_timeout_counters[i].counter = 0;
}
}
}

static void _timeout_disable(uint32_t cid)
{
for (int i = 0; i < NUM_TIMEOUT_COUNTERS; ++i) {
if (_timeout_counters[i].cid == cid) {
_timeout_counters[i].cid = 0;
_timeout_counters[i].counter = 0;
}
}
}

/**
* Keeps a state for the frame processing of incoming frames.
*/
static State _in_state;

/**
* Resets the current state.
*/
static void _reset_state(void)
{
queue_clear(queue_u2f_queue());
_timeout_disable(_in_state.cid);
memset(&_in_state, 0, sizeof(_in_state));
}

static queue_error_t _queue_push(const uint8_t* data)
{
return queue_push(queue_u2f_queue(), data);
}

/**
* Responds with an error.
* @param[in] err The error.
* @param[in] cid The channel identifier.
* No return value needed as long as _reset_state clears the queue.
*/
static void _queue_err(const uint8_t err, uint32_t cid)
{
usb_frame_prepare_err(err, cid, _queue_push);
}

static bool _need_more_data(void)
{
return (_in_state.buf_ptr - _in_state.data) < (signed)_in_state.len;
}

void u2f_packet_timeout_enable(uint32_t cid)
{
for (int i = 0; i < NUM_TIMEOUT_COUNTERS; ++i) {
if (_timeout_counters[i].cid == 0) {
_timeout_counters[i].cid = cid;
_timeout_counters[i].counter = 0;
return;
}
}
}

bool u2f_packet_timeout_get(uint32_t* cid)
{
for (int i = 0; i < NUM_TIMEOUT_COUNTERS; ++i) {
*cid = _timeout_counters[i].cid;
if (_timeout_counters[i].cid != 0 && _timeout_counters[i].counter >= 5) {
return true;
}
}
return false;
}

void u2f_packet_timeout_tick(void)
{
for (int i = 0; i < NUM_TIMEOUT_COUNTERS; ++i) {
if (_timeout_counters[i].cid != 0) {
_timeout_counters[i].counter += 1;
}
}
}

void u2f_packet_timeout(uint32_t cid)
{
_timeout_disable(cid);
if (cid == _in_state.cid) {
_reset_state();
}
usb_frame_prepare_err(FRAME_ERR_MSG_TIMEOUT, cid, _queue_push);
}

bool u2f_packet_process(const USB_FRAME* frame, void (*send_packet)(void))
{
struct usb_processing* ctx = usb_processing_u2f();
switch (usb_frame_process(frame, &_in_state)) {
case FRAME_ERR_IGNORE:
// Ignore this frame, i.e. no response.
break;
case FRAME_ERR_INVALID_SEQ:
// Reset the state becuase this error indicates that there is a host application bug
_reset_state();
_queue_err(FRAME_ERR_INVALID_SEQ, frame->cid);
break;
case FRAME_ERR_CHANNEL_BUSY:
// We don't reset the state because this error doesn't indicate something wrong with the
// "current" connection.
_queue_err(FRAME_ERR_CHANNEL_BUSY, frame->cid);
break;
case FRAME_ERR_INVALID_LEN:
// Reset the state becuase this error indicates that there is a host application bug
_reset_state();
_queue_err(FRAME_ERR_INVALID_LEN, frame->cid);
break;
case ERR_NONE:
_reset_timeout(frame->cid);
if (_need_more_data()) {
// Do not send a message yet
return true;
}
if (usb_processing_enqueue(ctx, &_in_state)) {
// Queue filled and will be sent during usb processing
_reset_state();
return false;
}
// Else: Currently processing a message, reset the state and forget about this packet
_timeout_disable(frame->cid);
_reset_state();
_queue_err(FRAME_ERR_CHANNEL_BUSY, frame->cid);
break;
default:
// other errors
_reset_state();
_queue_err(FRAME_ERR_OTHER, frame->cid);
break;
}
send_packet();
return false;
}
51 changes: 51 additions & 0 deletions src/u2f/u2f_packet.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright 2019 Shift Cryptosecurity AG
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef _U2F_PACKET_H_
#define _U2F_PACKET_H_

#include "usb/usb_frame.h"
#include "usb/usb_packet.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

/**
* Processes an incoming USB packet.
* @param[in] frame The frame that is to be processed.
* @param[in] send_packet The function to be called to send the response packet.
* @return true if we are waiting for more frames to complete a packet, false otherwise.
*/
bool u2f_packet_process(const USB_FRAME* frame, void (*send_packet)(void));

/**
* Checks if there has been a timeout
*/
bool u2f_packet_timeout_get(uint32_t* cid);

/**
* Queue a timeout packet for cid
*/
void u2f_packet_timeout(uint32_t cid);

/**
* Increase the timout timers with 1 step (steps in 100ms)
*/
void u2f_packet_timeout_tick(void);

/**
* Enable timer for this cid
*/
void u2f_packet_timeout_enable(uint32_t cid);
#endif
3 changes: 2 additions & 1 deletion src/ui/screen_process.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ static void _screen_process(bool (*is_done)(void), void (*on_timeout)(void), con
screen_frame_cnt++;
ui_screen_stack_cleanup();
if (is_done == NULL) {
usb_processing_process();
usb_processing_process(usb_processing_hww());
usb_processing_process(usb_processing_u2f());
}
}
}
Expand Down
Loading

0 comments on commit 1f64cd3

Please sign in to comment.