Compare commits

18 Commits

Author SHA1 Message Date
1093294b61 Fix visability issues. 2019-09-14 09:24:03 +01:00
ed1add2c60 Fix #6. Fix relying party verification.
Fix to throw error when relying party uses the wrong application
parameter when requesting a signature.
2019-09-11 15:49:50 +01:00
c8061d373f Updated READMEs with new certificate details. 2019-09-10 10:52:02 +01:00
784f3803f9 Fixed typo in Certificates.hpp.
Updated GenCErtificates.sh script to accomodate change.
2019-09-10 10:51:32 +01:00
7ce0738338 Implemented automated certificate generation.
Removed existing certificates as private key was shared.
Updated makefile to require certificate generation.
2019-09-09 17:06:47 +01:00
701127496c Additional error handling for rarer states.
Ensure std::map::at is not blindly applied without checking that the key
actually exists.

Fixes crashes where u2f client on PC/browser expects channel open, but
service has been restarted.
2019-09-08 20:27:47 +01:00
528662a081 Fix missing signal handling.
Plug signal handler into additional signal. Fixes no cleanup (corruption)
on stopping.

May impact #6.
2019-09-08 20:25:36 +01:00
80b552012d Fixed constant re-opening kernel pipe. 2019-09-07 17:23:41 +01:00
Michael Kuc
aeb9c89be8 Save immediately on persistent state modification.
Modifications for #6.
2019-08-29 16:20:42 +01:00
ad250d14e3 Fixed U2FDevice attempting to update every microsecond. 2019-08-23 13:37:51 +01:00
f7dea03132 Reformat 2019-08-23 13:32:41 +01:00
55d1fd738f Update gitignore.
Added clang-formatting specification.
2019-08-23 13:32:03 +01:00
0ce5793414 Hopefully improved attestation certificate creation README.
Fixes #3.
2019-08-05 13:00:50 +01:00
4ce52d4da3 Replaced incorrect #if with #ifdef.
Fixes #2.
2019-08-05 12:49:59 +01:00
a426a70dcf Fixed terminateHandler typo.
Fixes #1.
2019-08-05 12:31:06 +01:00
c0ca093e83 Fixed branch path. 2019-06-26 16:56:02 +01:00
fc96017efd Refer to android branch on main readme. 2019-06-26 16:53:29 +01:00
b942713cc9 Correct architecture. 2019-06-26 13:09:20 +01:00
57 changed files with 1062 additions and 1119 deletions

24
.clang-format Normal file
View File

@@ -0,0 +1,24 @@
---
BasedOnStyle: llvm
IndentWidth: 4
---
AccessModifierOffset: -4
AllowShortFunctionsOnASingleLine: Empty
AlwaysBreakTemplateDeclarations: Yes
BreakBeforeBraces: Attach
BreakConstructorInitializers: BeforeColon
Cpp11BracedListStyle: false
ColumnLimit: 100
DerivePointerAlignment: false
FixNamespaceComments: true
IndentCaseLabels: true
IndentPPDirectives: AfterHash
Language: Cpp
PointerAlignment: Left
SortIncludes: true
SortUsingDeclarations: true
Standard: Cpp11
TabWidth: 4
UseTab: ForIndentation
...

5
.gitignore vendored
View File

@@ -3,3 +3,8 @@ libcppb64.a
U2FDevice U2FDevice
obj/* obj/*
U2F_Priv_Keys.txt U2F_Priv_Keys.txt
.ccls-cache
compile_commands.json
Certificates.hpp
Certificates.cpp
Keys/*

View File

@@ -18,15 +18,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once #pragma once
enum APDU : uint8_t enum APDU : uint8_t { U2F_REG = 0x01, U2F_AUTH = 0x02, U2F_VER = 0x03 };
{
U2F_REG = 0x01,
U2F_AUTH = 0x02,
U2F_VER = 0x03
};
enum APDU_STATUS : uint16_t enum APDU_STATUS : uint16_t {
{
SW_NO_ERROR = 0x9000, SW_NO_ERROR = 0x9000,
SW_WRONG_LENGTH = 0x6700, SW_WRONG_LENGTH = 0x6700,
SW_CONDITIONS_NOT_SATISFIED = 0x6985, SW_CONDITIONS_NOT_SATISFIED = 0x6985,

View File

@@ -20,18 +20,18 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#define ARCH_RASPBERRY_PI 1 #define ARCH_RASPBERRY_PI 1
#define ARCH_ANDROID 2 #define ARCH_ANDROID 2
#define ARCHITECTURE ARCH_ANDROID #define ARCHITECTURE ARCH_RASPBERRY_PI
#if ARCHITECTURE == ARCH_RASPBERRY_PI #if ARCHITECTURE == ARCH_RASPBERRY_PI
#define STORAGE_PREFIX "/usr/share/" # define STORAGE_PREFIX "/usr/share/"
#define HID_DEV "/dev/hidg0" # define HID_DEV "/dev/hidg0"
#define DEBUG_STREAMS "/tmp/" # define DEBUG_STREAMS "/tmp/"
// #define DEBUG_MSGS // #define DEBUG_MSGS
#define LEDS # define LEDS
#elif ARCHITECTURE == ARCH_ANDROID #elif ARCHITECTURE == ARCH_ANDROID
#define STORAGE_PREFIX "/sdcard/U2F/" # define STORAGE_PREFIX "/sdcard/U2F/"
#define HID_DEV "/dev/hidg2" # define HID_DEV "/dev/hidg2"
#define DEBUG_STREAMS "/data/local/tmp/" # define DEBUG_STREAMS "/data/local/tmp/"
// #define DEBUG_MSGS // #define DEBUG_MSGS
#endif #endif

View File

@@ -19,19 +19,21 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once #pragma once
template <typename InContainer, typename OutContainer> template <typename InContainer, typename OutContainer>
void b64encode(const InContainer &iContainer, OutContainer &oContainer); void b64encode(const InContainer& iContainer, OutContainer& oContainer);
template <typename InContainer, typename OutContainer> template <typename InContainer, typename OutContainer>
void b64decode(const InContainer &container, OutContainer &oContainer); void b64decode(const InContainer& container, OutContainer& oContainer);
template <typename InContainer, typename Elem, size_t count> template <typename InContainer, typename Elem, size_t count>
void b64encode(const InContainer &iContainer, std::array<Elem, count> &oArr); void b64encode(const InContainer& iContainer, std::array<Elem, count>& oArr);
template <typename InContainer, typename Elem, size_t count> template <typename InContainer, typename Elem, size_t count>
void b64decode(const InContainer &iContainer, std::array<Elem, count> &oArr); void b64decode(const InContainer& iContainer, std::array<Elem, count>& oArr);
template <typename InContainerIter, typename OutContainerIter> template <typename InContainerIter, typename OutContainerIter>
void b64encode(const InContainerIter beginIter, const InContainerIter endIter, OutContainerIter oBeginIter); void b64encode(const InContainerIter beginIter, const InContainerIter endIter,
OutContainerIter oBeginIter);
template <typename InContainerIter, typename OutContainerIter> template <typename InContainerIter, typename OutContainerIter>
void b64decode(const InContainerIter beginIter, const InContainerIter endIter, OutContainerIter oBeginIter); void b64decode(const InContainerIter beginIter, const InContainerIter endIter,
OutContainerIter oBeginIter);

View File

@@ -1,90 +0,0 @@
/*
U2FDevice - A program to allow Raspberry Pi Zeros to act as U2F tokens
Copyright (C) 2018 Michael Kuc
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "Certificates.hpp"
//You may not actually want to use these values -
// having been shared publicly, these may be vulnerable to exploits.
// However, generating your own attestation certificate makes your device
// uniquely identifiable across platforms / services, etc.
// You can generate your own using the method detailed in the README.
uint8_t attestCert[] = {
0x30, 0x82, 0x02, 0x29, 0x30, 0x82, 0x01, 0xd0, 0xa0, 0x03, 0x02, 0x01,
0x02, 0x02, 0x09, 0x00, 0x8a, 0xe2, 0x21, 0x3f, 0x2f, 0x8b, 0x72, 0x52,
0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02,
0x30, 0x70, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
0x02, 0x55, 0x4b, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08,
0x0c, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65,
0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x49,
0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67,
0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x31,
0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x20, 0x55, 0x32,
0x46, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x74, 0x50, 0x46, 0x53, 0x71, 0x54,
0x71, 0x6f, 0x5a, 0x6d, 0x62, 0x37, 0x38, 0x61, 0x6a, 0x6f, 0x2f, 0x75,
0x58, 0x50, 0x73, 0x51, 0x3d, 0x3d, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x38,
0x30, 0x36, 0x33, 0x30, 0x31, 0x39, 0x30, 0x37, 0x35, 0x31, 0x5a, 0x17,
0x0d, 0x32, 0x38, 0x30, 0x36, 0x32, 0x37, 0x31, 0x39, 0x30, 0x37, 0x35,
0x31, 0x5a, 0x30, 0x70, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
0x06, 0x13, 0x02, 0x55, 0x4b, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
0x04, 0x08, 0x0c, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61,
0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c,
0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69,
0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74,
0x64, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x20,
0x55, 0x32, 0x46, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x74, 0x50, 0x46, 0x53,
0x71, 0x54, 0x71, 0x6f, 0x5a, 0x6d, 0x62, 0x37, 0x38, 0x61, 0x6a, 0x6f,
0x2f, 0x75, 0x58, 0x50, 0x73, 0x51, 0x3d, 0x3d, 0x30, 0x59, 0x30, 0x13,
0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a,
0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x32,
0x41, 0xc3, 0xb8, 0x96, 0x97, 0xd8, 0x90, 0x66, 0x41, 0x88, 0x96, 0xd4,
0x73, 0xb6, 0x37, 0xf7, 0x85, 0x29, 0xaf, 0x3b, 0x15, 0x0f, 0x83, 0x61,
0x67, 0xea, 0xc9, 0xb2, 0xdb, 0x82, 0xb3, 0x2c, 0x99, 0x60, 0x8a, 0x98,
0x7c, 0xd4, 0x04, 0xa0, 0x92, 0x22, 0x05, 0xaa, 0xf7, 0x7a, 0x91, 0x02,
0x03, 0xdd, 0x15, 0x88, 0x87, 0x6a, 0x26, 0xe9, 0xee, 0xcf, 0x99, 0xb1,
0x66, 0xc0, 0x01, 0xa3, 0x53, 0x30, 0x51, 0x30, 0x1d, 0x06, 0x03, 0x55,
0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xcf, 0x7f, 0xfa, 0x7d, 0xc4, 0x8d,
0xba, 0x60, 0x52, 0x4c, 0xb6, 0x16, 0x2e, 0x88, 0x62, 0xc7, 0x8c, 0xfc,
0xe0, 0x63, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30,
0x16, 0x80, 0x14, 0xcf, 0x7f, 0xfa, 0x7d, 0xc4, 0x8d, 0xba, 0x60, 0x52,
0x4c, 0xb6, 0x16, 0x2e, 0x88, 0x62, 0xc7, 0x8c, 0xfc, 0xe0, 0x63, 0x30,
0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30,
0x03, 0x01, 0x01, 0xff, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce,
0x3d, 0x04, 0x03, 0x02, 0x03, 0x47, 0x00, 0x30, 0x44, 0x02, 0x20, 0x72,
0x25, 0x89, 0xc1, 0x32, 0x54, 0x66, 0xf8, 0x0e, 0x58, 0x77, 0xe3, 0xb5,
0x62, 0x47, 0x33, 0x18, 0x5a, 0xdc, 0x28, 0x6a, 0x4a, 0x56, 0xcb, 0x58,
0x63, 0xe3, 0xa1, 0x02, 0x6a, 0xf0, 0xd8, 0x02, 0x20, 0x65, 0x26, 0x84,
0x7c, 0xc3, 0x3b, 0x7d, 0x6a, 0x22, 0x0c, 0x22, 0x3d, 0xc8, 0x43, 0xb7,
0x84, 0x8b, 0x7b, 0x48, 0x23, 0xb0, 0x1e, 0x13, 0x35, 0x1d, 0x1a, 0x90,
0x44, 0x62, 0x6c, 0xab, 0x9b
};
uint8_t attestPrivKey[] = {
0x7e, 0xbd, 0x91, 0x05, 0x5a, 0x80, 0x9f, 0x36, 0xe5, 0x2f, 0xe0, 0xd0,
0xa9, 0x63, 0x0c, 0x86, 0x04, 0xb1, 0x04, 0xe3, 0xd1, 0xfb, 0xd0, 0x83,
0xc7, 0x2e, 0x2f, 0x34, 0xb6, 0xd6, 0xa4, 0xb2
};
uint8_t attestPubKey[] = {
0x04, 0x32, 0x41, 0xc3, 0xb8, 0x96, 0x97, 0xd8, 0x90, 0x66, 0x41, 0x88,
0x96, 0xd4, 0x73, 0xb6, 0x37, 0xf7, 0x85, 0x29, 0xaf, 0x3b, 0x15, 0x0f,
0x83, 0x61, 0x67, 0xea, 0xc9, 0xb2, 0xdb, 0x82, 0xb3, 0x2c, 0x99, 0x60,
0x8a, 0x98, 0x7c, 0xd4, 0x04, 0xa0, 0x92, 0x22, 0x05, 0xaa, 0xf7, 0x7a,
0x91, 0x02, 0x03, 0xdd, 0x15, 0x88, 0x87, 0x6a, 0x26, 0xe9, 0xee, 0xcf,
0x99, 0xb1, 0x66, 0xc0, 0x01
};

37
Certificates.cpp.template Normal file
View File

@@ -0,0 +1,37 @@
/*
U2FDevice - A program to allow Raspberry Pi Zeros to act as U2F tokens
Copyright (C) 2018 Michael Kuc
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "Certificates.hpp"
// You may not actually want to use these values -
// having been shared publicly, these may be vulnerable to exploits.
// However, generating your own attestation certificate makes your device
// uniquely identifiable across platforms / services, etc.
// You can generate your own using the method detailed in the README.
uint8_t attestCert[] = {
// Generate attestation certificate here
};
uint8_t attestPrivKey[] = {
// Generate private key here
};
uint8_t attestPubKey[] = {
// Generate public key here
};

View File

@@ -19,6 +19,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once #pragma once
#include <cstdint> #include <cstdint>
extern uint8_t attestCert[557]; extern uint8_t attestCert[/* attestation certificate size */];
extern uint8_t attestPrivKey[32]; extern uint8_t attestPrivKey[/* attestation private key size */];
extern uint8_t attestPubKey[65]; extern uint8_t attestPubKey[/* attestation public key size */];

View File

@@ -17,24 +17,24 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "Channel.hpp" #include "Channel.hpp"
#include <stdexcept> #include "Storage.hpp"
#include "u2f.hpp"
#include "U2F_CMD.hpp" #include "U2F_CMD.hpp"
#include "u2f.hpp"
#include <iostream> #include <iostream>
#include <stdexcept>
using namespace std; using namespace std;
Channel::Channel(const uint32_t channelID) Channel::Channel(const uint32_t channelID)
: cid{ channelID }, initState{ ChannelInitState::Unitialised }, lockedState{ ChannelLockedState::Unlocked } : cid{ channelID }, initState{ ChannelInitState::Unitialised }, lockedState{
{} ChannelLockedState::Unlocked
} {}
uint32_t Channel::getCID() const uint32_t Channel::getCID() const {
{
return cid; return cid;
} }
void Channel::handle(const shared_ptr<U2FMessage> uMsg) void Channel::handle(const shared_ptr<U2FMessage> uMsg) {
{
if (uMsg->cmd == U2FHID_INIT) if (uMsg->cmd == U2FHID_INIT)
this->initState = ChannelInitState::Initialised; this->initState = ChannelInitState::Initialised;
else if (uMsg->cid != this->cid) else if (uMsg->cid != this->cid)
@@ -47,16 +47,18 @@ void Channel::handle(const shared_ptr<U2FMessage> uMsg)
auto cmd = U2F_CMD::get(uMsg); auto cmd = U2F_CMD::get(uMsg);
if (cmd) if (cmd) {
return cmd->respond(this->cid); cmd->respond(this->cid);
if (cmd->modifiesPersistentState())
Storage::save();
}
} }
void Channel::init(const ChannelInitState newInitState) void Channel::init(const ChannelInitState newInitState) {
{
this->initState = newInitState; this->initState = newInitState;
} }
void Channel::lock(const ChannelLockedState newLockedState) void Channel::lock(const ChannelLockedState newLockedState) {
{
this->lockedState = newLockedState; this->lockedState = newLockedState;
} }

View File

@@ -17,30 +17,21 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "U2FMessage.hpp"
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
#include "U2FMessage.hpp"
enum class ChannelInitState enum class ChannelInitState { Unitialised, Initialised };
{
Unitialised,
Initialised
};
enum class ChannelLockedState enum class ChannelLockedState { Locked, Unlocked };
{
Locked,
Unlocked
};
class Channel class Channel {
{ protected:
protected:
uint32_t cid; uint32_t cid;
ChannelInitState initState; ChannelInitState initState;
ChannelLockedState lockedState; ChannelLockedState lockedState;
public: public:
Channel(const uint32_t channelID); Channel(const uint32_t channelID);
void handle(const std::shared_ptr<U2FMessage> uMsg); void handle(const std::shared_ptr<U2FMessage> uMsg);

View File

@@ -17,28 +17,25 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "Controller.hpp" #include "Controller.hpp"
#include "u2f.hpp"
#include <iostream>
#include "IO.hpp" #include "IO.hpp"
#include "LED.hpp" #include "LED.hpp"
#include "u2f.hpp"
#include <iostream>
using namespace std; using namespace std;
Controller::Controller(const uint32_t startChannel) Controller::Controller(const uint32_t startChannel) : channels{}, currChannel{ startChannel } {}
: channels{}, currChannel{ startChannel }
{}
void Controller::handleTransaction() void Controller::handleTransaction() {
{ try {
try if (channels.size() != 0 &&
{ chrono::duration_cast<chrono::seconds>(chrono::system_clock::now() - lastMessage) <
if (channels.size() != 0 && chrono::duration_cast<chrono::seconds>(chrono::system_clock::now() - lastMessage) < chrono::seconds(5)) chrono::seconds(5))
toggleACTLED(); toggleACTLED();
else else
enableACTLED(false); enableACTLED(false);
} catch (runtime_error& ignored) {
} }
catch (runtime_error& ignored)
{}
auto msg = U2FMessage::readNonBlock(); auto msg = U2FMessage::readNonBlock();
@@ -47,21 +44,20 @@ void Controller::handleTransaction()
lastMessage = chrono::system_clock::now(); lastMessage = chrono::system_clock::now();
auto opChannel = msg->cid; auto opChannelID = msg->cid;
if (msg->cmd == U2FHID_INIT) if (msg->cmd == U2FHID_INIT) {
{ opChannelID = nextChannel();
opChannel = nextChannel(); auto channel = Channel{ opChannelID };
auto channel = Channel{ opChannel };
try try {
{ channels.emplace(opChannelID, channel); // In case of wrap-around replace existing one
channels.emplace(opChannel, channel); //In case of wrap-around replace existing one } catch (...) {
} channels.insert(make_pair(opChannelID, channel));
catch (...)
{
channels.insert(make_pair(opChannel, channel));
} }
} else if (channels.find(opChannelID) == channels.end()) {
U2FMessage::error(opChannelID, ERR_CHANNEL_BUSY);
return;
} }
#ifdef DEBUG_MSGS #ifdef DEBUG_MSGS
@@ -69,11 +65,10 @@ void Controller::handleTransaction()
clog << "cid: " << msg->cid << ", cmd: " << static_cast<unsigned int>(msg->cmd) << endl; clog << "cid: " << msg->cid << ", cmd: " << static_cast<unsigned int>(msg->cmd) << endl;
#endif #endif
channels.at(opChannel).handle(msg); channels.at(opChannelID).handle(msg);
} }
uint32_t Controller::nextChannel() uint32_t Controller::nextChannel() {
{
do do
currChannel++; currChannel++;
while (currChannel == 0xFFFFFFFF || currChannel == 0); while (currChannel == 0xFFFFFFFF || currChannel == 0);

View File

@@ -17,18 +17,17 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <map>
#include <chrono>
#include "Channel.hpp" #include "Channel.hpp"
#include <chrono>
#include <map>
class Controller class Controller {
{ protected:
protected:
std::map<uint32_t, Channel> channels; std::map<uint32_t, Channel> channels;
uint32_t currChannel; uint32_t currChannel;
std::chrono::system_clock::time_point lastMessage; std::chrono::system_clock::time_point lastMessage;
public: public:
Controller(const uint32_t startChannel = 1); Controller(const uint32_t startChannel = 1);
void handleTransaction(); void handleTransaction();

View File

@@ -20,7 +20,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
using namespace std; using namespace std;
vector<uint8_t> beEncode(const uint8_t* val, const size_t byteCount) vector<uint8_t> beEncode(const uint8_t* val, const size_t byteCount) {
{ return { reverse_iterator<const uint8_t*>(val + byteCount),
return { reverse_iterator<const uint8_t *>(val + byteCount), reverse_iterator<const uint8_t *>(val) }; reverse_iterator<const uint8_t*>(val) };
} }

View File

@@ -17,13 +17,17 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <vector>
#include <cstdint> #include <cstdint>
#include <vector>
template <typename Type> template <typename Type>
std::vector<uint8_t> beEncode(const Type val); std::vector<uint8_t> beEncode(const Type val);
std::vector<uint8_t> beEncode(const uint8_t* val, const std::size_t byteCount); std::vector<uint8_t> beEncode(const uint8_t* val, const std::size_t byteCount);
#define FIELD(name) reinterpret_cast<const uint8_t*>(&name), (reinterpret_cast<const uint8_t*>(&name) + sizeof(name)) #define FIELD(name) \
#define FIELD_BE(name) reverse_iterator<const uint8_t*>(reinterpret_cast<const uint8_t*>(&name) + sizeof(name)), reverse_iterator<const uint8_t*>(reinterpret_cast<const uint8_t*>(&name)) reinterpret_cast<const uint8_t*>(&name), \
(reinterpret_cast<const uint8_t*>(&name) + sizeof(name))
#define FIELD_BE(name) \
reverse_iterator<const uint8_t*>(reinterpret_cast<const uint8_t*>(&name) + sizeof(name)), \
reverse_iterator<const uint8_t*>(reinterpret_cast<const uint8_t*>(&name))

31
GenCertificates.sh Executable file
View File

@@ -0,0 +1,31 @@
#!/usr/bin/env sh
sed --version | grep "GNU" 2>&1 1>/dev/null
if [ $? -ne 0 ]; then
printf "Requires the GNU version of SED.\n"
return -1
fi
[ -d Keys ] || mkdir Keys
openssl ecparam -name prime256v1 -genkey -noout -out Keys/ecprivkey.pem
openssl ec -in Keys/ecprivkey.pem -pubout -out Keys/ecpubkey.pem 2>/dev/null
openssl req -new -x509 -key Keys/ecprivkey.pem -out Keys/certificate.pem -days 3650
openssl x509 -outform der -in Keys/certificate.pem -out Keys/certificate.der 2>/dev/null
certificate="$(xxd --include Keys/certificate.der | sed -e '0,/{/d;/};/,$d' -e 's/^\s\+/\t/g')"
certificateSize="$(printf "%s" "${certificate}" | wc -w)"
printf "%s" "${certificate}" | sed -e '/\/\/ Generate attestation certificate here/{r/dev/stdin
d }' Certificates.cpp.template > Keys/Certificates.cpp.template.1
privkey="$(openssl ec -in Keys/ecprivkey.pem -pubout -text -noout 2>/dev/null | sed -e '0,/priv:/d;/pub:/,$d' -e 's/\s//g;s/:/, /g;' -e 's/^/\t/g;s/\s\+$//g' -e 's/\(\s\)/\10x/g')"
privSize="$(printf "%s" "${privkey}" | wc -w)"
printf "%s\n" "${privkey}" | sed -e '/\/\/ Generate private key here/{r/dev/stdin
d }' Keys/Certificates.cpp.template.1 > Keys/Certificates.cpp.template.2
pubkey="$(openssl ec -in Keys/ecprivkey.pem -pubout -text -noout 2>/dev/null | sed -e '0,/pub:/d;/ASN1/,$d' -e 's/\s//g;s/:/, /g;' -e 's/^/\t/g;s/\s\+$//g' -e 's/\(\s\)/\10x/g')"
pubSize="$(printf "%s" "${pubkey}" | wc -w)"
printf "%s\n" "${pubkey}" | sed -e '/\/\/ Generate public key here/{r/dev/stdin
d }' Keys/Certificates.cpp.template.2 > Certificates.cpp
sed -e "s/\\/\\* attestation certificate size \\*\\//${certificateSize}/; s/\\/\\* attestation private key size \\*\\//${privSize}/; s/\\/\\* attestation public key size \\*\\//${pubSize}/" Certificates.hpp.template > Certificates.hpp

74
IO.cpp
View File

@@ -21,30 +21,28 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <iostream> #include <iostream>
#include <unistd.h> #include <unistd.h>
//#include <sys/ioctl.h> //#include <sys/ioctl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <chrono>
#include <ratio>
#include "u2f.hpp"
#include "Macro.hpp" #include "Macro.hpp"
#include "U2FDevice.hpp" #include "U2FDevice.hpp"
#include "u2f.hpp"
#include <chrono>
#include <fcntl.h>
#include <ratio>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
using namespace std; using namespace std;
bool bytesAvailable(const size_t count); bool bytesAvailable(const size_t count);
vector<uint8_t>& getBuffer(); vector<uint8_t>& getBuffer();
vector<uint8_t> readNonBlock(const size_t count) vector<uint8_t> readNonBlock(const size_t count) {
{ if (!bytesAvailable(count)) {
if (!bytesAvailable(count))
{
return vector<uint8_t>{}; return vector<uint8_t>{};
} }
auto &buffer = getBuffer(); auto& buffer = getBuffer();
auto buffStart = buffer.begin(), buffEnd = buffer.begin() + count; auto buffStart = buffer.begin(), buffEnd = buffer.begin() + count;
vector<uint8_t> bytes{ buffStart, buffEnd }; vector<uint8_t> bytes{ buffStart, buffEnd };
buffer.erase(buffStart, buffEnd); buffer.erase(buffStart, buffEnd);
@@ -54,32 +52,29 @@ vector<uint8_t> readNonBlock(const size_t count)
return bytes; return bytes;
} }
void write(const uint8_t* bytes, const size_t count) void write(const uint8_t* bytes, const size_t count) {
{
size_t totalBytes = 0; size_t totalBytes = 0;
auto hostDescriptor = *getHostDescriptor(); auto hostDescriptor = *getHostDescriptor();
while (totalBytes < count) while (totalBytes < count) {
{
auto writtenBytes = write(hostDescriptor, bytes + totalBytes, count - totalBytes); auto writtenBytes = write(hostDescriptor, bytes + totalBytes, count - totalBytes);
if (writtenBytes > 0) if (writtenBytes > 0)
totalBytes += writtenBytes; totalBytes += writtenBytes;
else if (errno != 0 && errno != EAGAIN && errno != EWOULDBLOCK) //Expect file blocking behaviour else if (errno != 0 && errno != EAGAIN &&
errno != EWOULDBLOCK) // Expect file blocking behaviour
ERR(); ERR();
} }
errno = 0; errno = 0;
} }
bool bytesAvailable(const size_t count) bool bytesAvailable(const size_t count) {
{
auto startTime = std::chrono::high_resolution_clock::now(); auto startTime = std::chrono::high_resolution_clock::now();
const timespec iterDelay{ 0, 1000 }; const timespec iterDelay{ 0, 10000000 };
chrono::duration<double, milli> delay{ 0 }; chrono::duration<double, milli> delay{ 0 };
while (delay.count() < U2FHID_TRANS_TIMEOUT && contProc) while (delay.count() < U2FHID_TRANS_TIMEOUT && contProc) {
{
delay = chrono::high_resolution_clock::now() - startTime; delay = chrono::high_resolution_clock::now() - startTime;
if (getBuffer().size() >= count) { if (getBuffer().size() >= count) {
#ifdef DEBUG_MSGS #ifdef DEBUG_MSGS
@@ -97,51 +92,44 @@ bool bytesAvailable(const size_t count)
return false; return false;
} }
vector<uint8_t>& bufferVar() vector<uint8_t>& bufferVar() {
{
static vector<uint8_t> buffer{}; static vector<uint8_t> buffer{};
return buffer; return buffer;
} }
vector<uint8_t>& getBuffer() vector<uint8_t>& getBuffer() {
{ auto& buff = bufferVar();
auto &buff = bufferVar();
array<uint8_t, HID_RPT_SIZE> bytes{}; array<uint8_t, HID_RPT_SIZE> bytes{};
auto hostDescriptor = *getHostDescriptor(); auto hostDescriptor = *getHostDescriptor();
while (true) while (true) {
{
auto readByteCount = read(hostDescriptor, bytes.data(), HID_RPT_SIZE); auto readByteCount = read(hostDescriptor, bytes.data(), HID_RPT_SIZE);
if (readByteCount > 0 && readByteCount != HID_RPT_SIZE) if (readByteCount > 0 && readByteCount != HID_RPT_SIZE) {
{ // Failed to copy an entire packet in, so log this packet
//Failed to copy an entire packet in, so log this packet
#ifdef DEBUG_MSGS #ifdef DEBUG_MSGS
cerr << "Only retrieved " << readByteCount << " bytes from expected full packet." << endl; cerr << "Only retrieved " << readByteCount << " bytes from expected full packet."
<< endl;
#endif #endif
} }
if (readByteCount > 0) if (readByteCount > 0) {
{
copy(bytes.begin(), bytes.begin() + readByteCount, back_inserter(buff)); copy(bytes.begin(), bytes.begin() + readByteCount, back_inserter(buff));
#ifdef DEBUG_STREAMS #ifdef DEBUG_STREAMS
fwrite(bytes.data(), 1, readByteCount, getComHostStream().get()); fwrite(bytes.data(), 1, readByteCount, getComHostStream().get());
#endif #endif
} } else if (errno != EAGAIN && errno != EWOULDBLOCK) // Expect read would block
else if (errno != EAGAIN && errno != EWOULDBLOCK) //Expect read would block
{ {
ERR(); ERR();
#if DEBUG_MSGS #ifdef DEBUG_MSGS
cerr << "Unknown stream error: " << errno << endl; cerr << "Unknown stream error: " << errno << endl;
#endif #endif
break; break;
} } else {
else
{
errno = 0; errno = 0;
break; //Escape loop if blocking would occur break; // Escape loop if blocking would occur
} }
} }

6
IO.hpp
View File

@@ -21,9 +21,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <cstdint> #include <cstdint>
#include <vector> #include <vector>
//Returns either the number of bytes specified, // Returns either the number of bytes specified,
//or returns empty vector without discarding bytes from HID stream // or returns empty vector without discarding bytes from HID stream
std::vector<uint8_t> readNonBlock(const size_t count); std::vector<uint8_t> readNonBlock(const size_t count);
//Blocking write to HID stream - shouldn't block for too long // Blocking write to HID stream - shouldn't block for too long
void write(const uint8_t* bytes, const size_t count); void write(const uint8_t* bytes, const size_t count);

Binary file not shown.

View File

@@ -1,5 +0,0 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIH69kQVagJ825S/g0KljDIYEsQTj0fvQg8cuLzS21qSyoAoGCCqGSM49
AwEHoUQDQgAEMkHDuJaX2JBmQYiW1HO2N/eFKa87FQ+DYWfqybLbgrMsmWCKmHzU
BKCSIgWq93qRAgPdFYiHaibp7s+ZsWbAAQ==
-----END EC PRIVATE KEY-----

View File

@@ -1,4 +0,0 @@
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMkHDuJaX2JBmQYiW1HO2N/eFKa87
FQ+DYWfqybLbgrMsmWCKmHzUBKCSIgWq93qRAgPdFYiHaibp7s+ZsWbAAQ==
-----END PUBLIC KEY-----

View File

@@ -1 +0,0 @@
~<7E><>Z<><5A>6<EFBFBD>/<2F>Щc <0C><04><04><><EFBFBD>Ѓ<EFBFBD>./4<>֤<EFBFBD>

View File

@@ -1 +0,0 @@
2Aø<41><C3B8>ؐfA<66><41><EFBFBD>s<EFBFBD>7<EFBFBD><37>)<29>;<0F>ag<61>ɲۂ<C9B2>,<2C>`<60><>|<7C><04><>"<05><>z<EFBFBD><03><15><>j&<26><>ϙ<EFBFBD>f<EFBFBD>

View File

@@ -1,14 +0,0 @@
-----BEGIN CERTIFICATE-----
MIICKTCCAdCgAwIBAgIJAIriIT8vi3JSMAoGCCqGSM49BAMCMHAxCzAJBgNVBAYT
AlVLMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRn
aXRzIFB0eSBMdGQxKTAnBgNVBAMMIFUyRiBLZXkgdFBGU3FUcW9abWI3OGFqby91
WFBzUT09MB4XDTE4MDYzMDE5MDc1MVoXDTI4MDYyNzE5MDc1MVowcDELMAkGA1UE
BhMCVUsxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdp
ZGdpdHMgUHR5IEx0ZDEpMCcGA1UEAwwgVTJGIEtleSB0UEZTcVRxb1ptYjc4YWpv
L3VYUHNRPT0wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQyQcO4lpfYkGZBiJbU
c7Y394UprzsVD4NhZ+rJstuCsyyZYIqYfNQEoJIiBar3epECA90ViIdqJunuz5mx
ZsABo1MwUTAdBgNVHQ4EFgQUz3/6fcSNumBSTLYWLohix4z84GMwHwYDVR0jBBgw
FoAUz3/6fcSNumBSTLYWLohix4z84GMwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjO
PQQDAgNHADBEAiByJYnBMlRm+A5Yd+O1YkczGFrcKGpKVstYY+OhAmrw2AIgZSaE
fMM7fWoiDCI9yEO3hIt7SCOwHhM1HRqQRGJsq5s=
-----END CERTIFICATE-----

15
LED.cpp
View File

@@ -24,19 +24,16 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
using namespace std; using namespace std;
bool& ledState() bool& ledState() {
{
static bool state = true; static bool state = true;
return state; return state;
} }
bool getLEDState() bool getLEDState() {
{
return ledState(); return ledState();
} }
void disableACTTrigger([[maybe_unused]] bool nowDisabled) void disableACTTrigger([[maybe_unused]] bool nowDisabled) {
{
#ifdef LEDS #ifdef LEDS
ofstream trigFile{ "/sys/class/leds/led0/trigger", ofstream::out | ofstream::trunc }; ofstream trigFile{ "/sys/class/leds/led0/trigger", ofstream::out | ofstream::trunc };
@@ -48,8 +45,7 @@ void disableACTTrigger([[maybe_unused]] bool nowDisabled)
#endif #endif
} }
void enableACTLED([[maybe_unused]] bool nowOn) void enableACTLED([[maybe_unused]] bool nowOn) {
{
#ifdef LEDS #ifdef LEDS
if (nowOn == getLEDState()) if (nowOn == getLEDState())
return; return;
@@ -66,7 +62,6 @@ void enableACTLED([[maybe_unused]] bool nowOn)
#endif #endif
} }
void toggleACTLED() void toggleACTLED() {
{
enableACTLED(!getLEDState()); enableACTLED(!getLEDState());
} }

View File

@@ -17,7 +17,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <unistd.h>
#include <string> #include <string>
#include <unistd.h>
#define ERR() if (errno != 0) perror((string{ "(" } + __FILE__ + ":" + to_string(__LINE__) + ")" + " " + __PRETTY_FUNCTION__).c_str()), errno = 0 #define ERR() \
if (errno != 0) \
perror( \
(string{ "(" } + __FILE__ + ":" + to_string(__LINE__) + ")" + " " + __PRETTY_FUNCTION__) \
.c_str()), \
errno = 0

View File

@@ -1,6 +1,7 @@
#!/usr/bin/env make #!/usr/bin/env make
SRC_DIR := . SRC_DIR := .
KEY_DIR := Keys
OBJ_DIR := obj OBJ_DIR := obj
CXXFLAGS := -std=c++11 -MMD -MP -Wall -Wfatal-errors -Wextra -fPIE CXXFLAGS := -std=c++11 -MMD -MP -Wall -Wfatal-errors -Wextra -fPIE
LDFLAGS := -fPIE LDFLAGS := -fPIE
@@ -31,7 +32,7 @@ install: U2FDevice
install -m775 -t /etc/systemd/system Services/U2FDevice.service install -m775 -t /etc/systemd/system Services/U2FDevice.service
install -d /usr/share/U2FDevice/ install -d /usr/share/U2FDevice/
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp | $(OBJ_DIR) $(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp | $(OBJ_DIR) $(SRC_DIR)/Certificates.hpp
$(CXX) $(STATIC) $(CXXFLAGS) -c -o $@ $< $(CXX) $(STATIC) $(CXXFLAGS) -c -o $@ $<
$(OBJ_DIR): $(OBJ_DIR):
@@ -45,7 +46,14 @@ clean:
$(MAKE) -C micro-ecc clean $(MAKE) -C micro-ecc clean
$(MAKE) -C cpp-base64 clean $(MAKE) -C cpp-base64 clean
.PHONY: clean install clean-certificates:
rm -f $(KEY_DIR)/*
rm -f Certificates.cpp Certificates.hpp
$(SRC_DIR)/Certificates.hpp: $(SRC_DIR)/Certificates.hpp.template
$(error "Please run the GenCertificates.sh script to generate certificate before building\n")
.PHONY: clean clean-certificates install
libuECC.a: libuECC.a:
$(MAKE) -C micro-ecc $(MAKE) -C micro-ecc

View File

@@ -18,24 +18,22 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Packet.hpp" #include "Packet.hpp"
#include "IO.hpp" #include "IO.hpp"
#include "Streams.hpp"
#include "u2f.hpp" #include "u2f.hpp"
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include <unistd.h> #include <unistd.h>
#include "Streams.hpp"
using namespace std; using namespace std;
shared_ptr<InitPacket> InitPacket::getPacket(const uint32_t rCID, const uint8_t rCMD) shared_ptr<InitPacket> InitPacket::getPacket(const uint32_t rCID, const uint8_t rCMD) {
{
static size_t bytesRead = 0; static size_t bytesRead = 0;
static uint8_t bcnth; static uint8_t bcnth;
static uint8_t bcntl; static uint8_t bcntl;
static decltype(InitPacket::data) dataBytes; static decltype(InitPacket::data) dataBytes;
vector<uint8_t> bytes{}; vector<uint8_t> bytes{};
switch (bytesRead) switch (bytesRead) {
{
case 0: case 0:
bytes = readNonBlock(1); bytes = readNonBlock(1);
@@ -62,7 +60,8 @@ shared_ptr<InitPacket> InitPacket::getPacket(const uint32_t rCID, const uint8_t
if (bytes.size() == 0) if (bytes.size() == 0)
return {}; return {};
copy(bytes.begin(), bytes.end(), dataBytes.begin());; copy(bytes.begin(), bytes.end(), dataBytes.begin());
;
bytesRead += bytes.size(); bytesRead += bytes.size();
[[fallthrough]]; [[fallthrough]];
@@ -82,7 +81,8 @@ shared_ptr<InitPacket> InitPacket::getPacket(const uint32_t rCID, const uint8_t
#ifdef DEBUG_STREAMS #ifdef DEBUG_STREAMS
auto hPStream = getHostPacketStream().get(); auto hPStream = getHostPacketStream().get();
fprintf(hPStream, "\t\t<table>\n" fprintf(hPStream,
"\t\t<table>\n"
"\t\t\t<thead>\n" "\t\t\t<thead>\n"
"\t\t\t\t<tr>\n" "\t\t\t\t<tr>\n"
"\t\t\t\t\t<th>CID</th>\n" "\t\t\t\t\t<th>CID</th>\n"
@@ -98,7 +98,8 @@ shared_ptr<InitPacket> InitPacket::getPacket(const uint32_t rCID, const uint8_t
"\t\t\t\t\t<td>%u</td>\n" "\t\t\t\t\t<td>%u</td>\n"
"\t\t\t\t\t<td>%u</td>\n" "\t\t\t\t\t<td>%u</td>\n"
"\t\t\t\t\t<td>%u</td>\n" "\t\t\t\t\t<td>%u</td>\n"
"\t\t\t\t\t<td class=\"data\">", p->cid, p->cmd, p->bcnth, p->bcntl); "\t\t\t\t\t<td class=\"data\">",
p->cid, p->cmd, p->bcnth, p->bcntl);
for (auto elem : dataBytes) for (auto elem : dataBytes)
fprintf(hPStream, "%3u ", elem); fprintf(hPStream, "%3u ", elem);
@@ -114,16 +115,14 @@ shared_ptr<InitPacket> InitPacket::getPacket(const uint32_t rCID, const uint8_t
return p; return p;
} }
shared_ptr<ContPacket> ContPacket::getPacket(const uint32_t rCID, const uint8_t rSeq) shared_ptr<ContPacket> ContPacket::getPacket(const uint32_t rCID, const uint8_t rSeq) {
{
static size_t readBytes = 0; static size_t readBytes = 0;
static decltype(ContPacket::data) dataBytes; static decltype(ContPacket::data) dataBytes;
vector<uint8_t> bytes{}; vector<uint8_t> bytes{};
auto p = make_shared<ContPacket>(); auto p = make_shared<ContPacket>();
if (readBytes != dataBytes.size()) if (readBytes != dataBytes.size()) {
{
dataBytes = {}; dataBytes = {};
bytes = readNonBlock(dataBytes.size()); bytes = readNonBlock(dataBytes.size());
@@ -140,7 +139,8 @@ shared_ptr<ContPacket> ContPacket::getPacket(const uint32_t rCID, const uint8_t
#ifdef DEBUG_STREAMS #ifdef DEBUG_STREAMS
auto hPStream = getHostPacketStream().get(); auto hPStream = getHostPacketStream().get();
fprintf(hPStream, "\t\t<table>\n" fprintf(hPStream,
"\t\t<table>\n"
"\t\t\t<thead>\n" "\t\t\t<thead>\n"
"\t\t\t\t<tr>\n" "\t\t\t\t<tr>\n"
"\t\t\t\t\t<th>CID</th>\n" "\t\t\t\t\t<th>CID</th>\n"
@@ -152,7 +152,8 @@ shared_ptr<ContPacket> ContPacket::getPacket(const uint32_t rCID, const uint8_t
"\t\t\t\t<tr>\n" "\t\t\t\t<tr>\n"
"\t\t\t\t\t<td>0x%08X</td>\n" "\t\t\t\t\t<td>0x%08X</td>\n"
"\t\t\t\t\t<td>%u</td>\n" "\t\t\t\t\t<td>%u</td>\n"
"\t\t\t\t\t<td class=\"data\">", p->cid, p->seq); "\t\t\t\t\t<td class=\"data\">",
p->cid, p->seq);
for (auto elem : dataBytes) for (auto elem : dataBytes)
fprintf(hPStream, "%3u ", elem); fprintf(hPStream, "%3u ", elem);
@@ -168,8 +169,7 @@ shared_ptr<ContPacket> ContPacket::getPacket(const uint32_t rCID, const uint8_t
return p; return p;
} }
shared_ptr<Packet> Packet::getPacket() shared_ptr<Packet> Packet::getPacket() {
{
static size_t bytesRead = 0; static size_t bytesRead = 0;
vector<uint8_t> bytes{}; vector<uint8_t> bytes{};
@@ -177,8 +177,7 @@ shared_ptr<Packet> Packet::getPacket()
static uint8_t b; static uint8_t b;
shared_ptr<Packet> packet{}; shared_ptr<Packet> packet{};
switch (bytesRead) switch (bytesRead) {
{
case 0: case 0:
bytes = readNonBlock(4); bytes = readNonBlock(4);
@@ -200,19 +199,16 @@ shared_ptr<Packet> Packet::getPacket()
[[fallthrough]]; [[fallthrough]];
case 5: case 5:
if (b & TYPE_MASK) if (b & TYPE_MASK) {
{ // Init packet
//Init packet
packet = InitPacket::getPacket(cid, b); packet = InitPacket::getPacket(cid, b);
if (packet) if (packet)
bytesRead = 0; bytesRead = 0;
return packet; return packet;
} } else {
else // Cont packet
{
//Cont packet
packet = ContPacket::getPacket(cid, b); packet = ContPacket::getPacket(cid, b);
if (packet) if (packet)
@@ -225,14 +221,12 @@ shared_ptr<Packet> Packet::getPacket()
} }
} }
void Packet::writePacket() void Packet::writePacket() {
{
memset(this->buf, 0, HID_RPT_SIZE); memset(this->buf, 0, HID_RPT_SIZE);
memcpy(this->buf, &cid, 4); memcpy(this->buf, &cid, 4);
} }
void InitPacket::writePacket() void InitPacket::writePacket() {
{
Packet::writePacket(); Packet::writePacket();
#ifdef DEBUG_STREAMS #ifdef DEBUG_STREAMS
@@ -249,7 +243,8 @@ void InitPacket::writePacket()
fwrite(this->buf, 1, sizeof(this->buf), devStream); fwrite(this->buf, 1, sizeof(this->buf), devStream);
auto dPStream = getDevPacketStream().get(); auto dPStream = getDevPacketStream().get();
fprintf(dPStream, "\t\t<table>\n" fprintf(dPStream,
"\t\t<table>\n"
"\t\t\t<thead>\n" "\t\t\t<thead>\n"
"\t\t\t\t<tr>\n" "\t\t\t\t<tr>\n"
"\t\t\t\t\t<th>CID</th>\n" "\t\t\t\t\t<th>CID</th>\n"
@@ -265,7 +260,8 @@ void InitPacket::writePacket()
"\t\t\t\t\t<td>%u</td>\n" "\t\t\t\t\t<td>%u</td>\n"
"\t\t\t\t\t<td>%u</td>\n" "\t\t\t\t\t<td>%u</td>\n"
"\t\t\t\t\t<td>%u</td>\n" "\t\t\t\t\t<td>%u</td>\n"
"\t\t\t\t\t<td class=\"data\">", cid, cmd, bcnth, bcntl); "\t\t\t\t\t<td class=\"data\">",
cid, cmd, bcnth, bcntl);
for (auto elem : data) for (auto elem : data)
fprintf(dPStream, "%3u ", elem); fprintf(dPStream, "%3u ", elem);
@@ -278,8 +274,7 @@ void InitPacket::writePacket()
#endif #endif
} }
void ContPacket::writePacket() void ContPacket::writePacket() {
{
Packet::writePacket(); Packet::writePacket();
#ifdef DEBUG_STREAMS #ifdef DEBUG_STREAMS
@@ -295,7 +290,8 @@ void ContPacket::writePacket()
auto dPStream = getDevPacketStream().get(); auto dPStream = getDevPacketStream().get();
fprintf(dPStream, "\t\t<table>\n" fprintf(dPStream,
"\t\t<table>\n"
"\t\t\t<thead>\n" "\t\t\t<thead>\n"
"\t\t\t\t<tr>\n" "\t\t\t\t<tr>\n"
"\t\t\t\t\t<th>CID</th>\n" "\t\t\t\t\t<th>CID</th>\n"
@@ -307,7 +303,8 @@ void ContPacket::writePacket()
"\t\t\t\t<tr>\n" "\t\t\t\t<tr>\n"
"\t\t\t\t\t<td>0x%08X</td>\n" "\t\t\t\t\t<td>0x%08X</td>\n"
"\t\t\t\t\t<td>%u</td>\n" "\t\t\t\t\t<td>%u</td>\n"
"\t\t\t\t\t<td class=\"data\">", cid, seq); "\t\t\t\t\t<td class=\"data\">",
cid, seq);
for (auto elem : data) for (auto elem : data)
fprintf(dPStream, "%3u ", elem); fprintf(dPStream, "%3u ", elem);

View File

@@ -17,47 +17,44 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "u2f.hpp"
#include <array>
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
#include <array>
#include "u2f.hpp"
struct Packet struct Packet {
{ public:
public:
uint32_t cid; uint32_t cid;
uint8_t buf[HID_RPT_SIZE]; uint8_t buf[HID_RPT_SIZE];
protected: protected:
Packet() = default; Packet() = default;
virtual void writePacket(); virtual void writePacket();
public: public:
static std::shared_ptr<Packet> getPacket(); static std::shared_ptr<Packet> getPacket();
virtual ~Packet() = default; virtual ~Packet() = default;
}; };
struct InitPacket : Packet struct InitPacket : Packet {
{ public:
public:
uint8_t cmd; uint8_t cmd;
uint8_t bcnth; uint8_t bcnth;
uint8_t bcntl; uint8_t bcntl;
std::array<uint8_t, HID_RPT_SIZE - 7> data{}; std::array<uint8_t, HID_RPT_SIZE - 7> data{};
public: public:
InitPacket() = default; InitPacket() = default;
static std::shared_ptr<InitPacket> getPacket(const uint32_t rCID, const uint8_t rCMD); static std::shared_ptr<InitPacket> getPacket(const uint32_t rCID, const uint8_t rCMD);
void writePacket() override; void writePacket() override;
}; };
struct ContPacket : Packet struct ContPacket : Packet {
{ public:
public:
uint8_t seq; uint8_t seq;
std::array<uint8_t, HID_RPT_SIZE - 5> data{}; std::array<uint8_t, HID_RPT_SIZE - 5> data{};
public: public:
ContPacket() = default; ContPacket() = default;
static std::shared_ptr<ContPacket> getPacket(const uint32_t rCID, const uint8_t rSeq); static std::shared_ptr<ContPacket> getPacket(const uint32_t rCID, const uint8_t rSeq);
void writePacket() override; void writePacket() override;

View File

@@ -0,0 +1,67 @@
# Automatically generate keys
Run `./GenCertificates.sh`, answering the prompt to produce your own certificate.
# Manually generate keys
From [Teensy U2F](https://github.com/pratikd650/Teensy_U2F/blob/master/Teensy_U2F.cpp) line 292
Instructions to generate attestation certificate using open ssl
[OpenSSL wiki](https://wiki.openssl.org/index.php/Command_Line_Elliptic_Curve_Operations)
[Guy Rutenberg](https://www.guyrutenberg.com/2013/12/28/creating-self-signed-ecdsa-ssl-certificate-using-openssl/)
P-256 (also secp256r1) EC key pair is W = dG (Note secp256k1 is Koblitz curve - not P256)
d = private key is it 256 bits (32 bytes)
G = generator point - it is part of the curve definition
W = public key point - it is a (256, 256) bits - 64 bytes
1) Generate a key pair - the private key will be saved in PKCS8 format in ecprivkey.pem
`openssl ecparam -name prime256v1 -genkey -noout -out ecprivkey.pem`
2) Dump out the private key in hex format - it will be a 32 byte key
`openssl asn1parse -in ecprivkey.pem`
3) Compute the public key from the private key and the curve
`openssl ec -in ecprivkey.pem -pubout -out ecpubkey.pem`
4) Dump out the public key in hex format - it will be 66 byte - the first two bytes are 00 04,
`openssl ec -in ecprivkey.pem -pubout -text`
after that is the point W - 32 byte + 32 byte
5) Generate a self signed certificate
`openssl req -new -x509 -key ecprivkey.pem -out certificate.pem -days 3650`
For the Certificate name give a unique certificate name.
6) Display the certificate
`openssl x509 -in certificate.pem -text -noout`
7) Convert PEM certificate to DER
`openssl x509 -outform der -in certificate.pem -out certificate.der`
8) Generate a usable c-array for source code
`xxd --include certificate.der | sed -e '0,/{/d;/};/,$d'`
Copy output into appropriate array in 'Certificates.cpp', overwriting existing values
9) Find the public key
`openssl ec -in ecprivkey.pem -pubout -text -noout 2>/dev/null | sed -e '0,/priv:/d;/pub:/,$d' -e 's/\s//g;s/:/, /g' -e 's/^/\t/g;s/\s\+$//g' -e 's/\(\s\)/\10x/g'`
10) Find the private key
`openssl ec -in ecprivkey.pem -pubout -text -noout 2>/dev/null | sed -e '0,/pub:/d;/ASN1/,$d' | sed -e 's/\s//g;s/:/, /g' -e 's/^/\t/g;s/\s\+$//g' -e 's/\(\s\)/\10x/g'`
11)
Copy the arrays into the correct arrays in Certificates.cpp.
If any arrays have different lengths than shown in Certificates.hpp, update these too.

View File

@@ -1,45 +0,0 @@
From https://github.com/pratikd650/Teensy_U2F/blob/master/Teensy_U2F.cpp line 292
Instructions to generate attestation certificate using open ssl
https://wiki.openssl.org/index.php/Command_Line_Elliptic_Curve_Operations
https://www.guyrutenberg.com/2013/12/28/creating-self-signed-ecdsa-ssl-certificate-using-openssl/
P-256 (also secp256r1) EC key pair is W = dG (Note secp256k1 is Koblitz curve - not P256)
d = private key is it 256 bits (32 bytes)
G = generator point - it is part of the curve definition
W = public key point - it is a (256, 256) bits - 64 bytes
1) generate a key pair - the private key will be saved in PKCS8 format in ecprivkey.pem
openssl ecparam -name prime256v1 -genkey -noout -out ecprivkey.pem
2) dump out the private key in hex format - it will be a 32 byte key
openssl asn1parse -in ecprivkey.pem
3) compute the public key from the private key and the curve
openssl ec -in ecprivkey.pem -pubout -out ecpubkey.pem
4) dump out the public key in hex format - it will be 66 byte - the first two bytes are 00 04,
openssl ec -in ecprivkey.pem -pubout -text
after that is the point W - 32 byte + 32 byte
5) generate a self signed certificate
openssl req -new -x509 -key ecprivkey.pem -out server.pem -days 3650
For the Certificate name give a unique certificate name. There is a 128 bit unique identification number burned into every
Teensy chip - see http://cache.freescale.com/files/32bit/doc/data_sheet/K20P64M72SF1.pdf
You can print out the number from your Teensy using this simple program given below
6) Display the certificate
openssl x509 -in server.pem -text -noout
7) Convert PEM certificate to DER
openssl x509 -outform der -in server.pem -out certificate.der
8) Generate a usable c-array for source code
xxd --include certificate.pem
Copy output into appropriate array in 'Certificates.cpp', overwriting existing values
9) Repeat steps 7 & 8 for public key and private key
So:
`
openssl asn1parse -in ecprivkey.pem 2>/dev/null | grep 'HEX DUMP' | perl -pe 's/^.*\[HEX DUMP\]:(.+)$/$1/' 2>/dev/null | xxd -r -p > privkey.der && xxd --include privkey.der
openssl ec -in ecprivkey.pem -pubout -text 2>/dev/null | perl -0777 -ne 'print /pub:.+ASN1/sg' 2>/dev/null | sed -e '/pub:/d;/ASN1/d' | perl -pe 's/^\s+(.+):?$/$1/gm' 2>/dev/null | perl -pe 's/\n//' 2>/dev/null | perl -pe 's/(.{2}):?/$1/g' 2>/dev/null | xxd -r -p > pubkey.der && xxd --include pubkey.der
`
and copy the arrays into the correct arrays in Certificates.cpp
If any arrays have different lengths than shown in Certificates.hpp, update these too.

View File

@@ -1,5 +1,7 @@
# U2FDevice # U2FDevice
For the Android version of this, please see [the android branch](../../tree/android), as there is a completely different build procedure and resources.
This program uses a Raspberry Pi 0 to act as a [U2F token](https://www.yubico.com/solutions/fido-u2f/). This permits any person with a Raspberry Pi 0 to try out U2F 2FA for themselves. This program uses a Raspberry Pi 0 to act as a [U2F token](https://www.yubico.com/solutions/fido-u2f/). This permits any person with a Raspberry Pi 0 to try out U2F 2FA for themselves.
# Required materials # Required materials
@@ -209,6 +211,12 @@ Then, reload the rules using `sudo udevadm control --reload-rules `
4. Make the object file directories using `mkdir obj && mkdir cpp-base64/obj && mkdir micro-ecc/obj` 4. Make the object file directories using `mkdir obj && mkdir cpp-base64/obj && mkdir micro-ecc/obj`
5. Grab the required library using `sudo apt-get install libmbedtls-dev` 5. Grab the required library using `sudo apt-get install libmbedtls-dev`
## Generate a certificate
If you wish to do this automatically, just run `./GenCertificates.sh`, and answer the prompt with as much detail as you feel like entrusting to websites.
Alternatively, see `Readme.AttestationCertifcateGeneration.md` for a much more manual approach.
## Build the program ## Build the program
1. Run `make` 1. Run `make`
@@ -241,11 +249,15 @@ For these reasons, if you want to use this as a way to backup your other U2F dev
1. Install `rng-tools` with `sudo apt-get install rng-tools` 1. Install `rng-tools` with `sudo apt-get install rng-tools`
## To change the Attestation certificate ## Notes about a custom attestation certificate
This may be highly advisable, or inadvisable - I am currently unsure. <br />All registration requests use this private key, so likely advisable. <br/>However, you can be uniquely identified by having a unique attestation certificate. By using a custom attestation certificate, you lose the anonymity of conventional u2f keys. This is because they are produced in large batches and thus can share a single certificate, burned into some private ROM. However, since you require the private key to sign, and this repo is public, it is impossible to use a single signature for everyone who uses this repository.
See the `Readme.AttestationCertificateGeneration.txt` However, by generating your own certificate, you can be more assured about the inherent security of your certificate (no-one can leak the private key but you).
Note, however, that this key and certificate is only used for registration - not for further authentication.
See the `Readme.AttestationCertificateGeneration.md`
# Running the program # Running the program
@@ -261,7 +273,7 @@ If the program doesn't work on these - don't use as a backup device.
Once the program runs successfully, you can enable automatic startup at boot. Once the program runs successfully, you can enable automatic startup at boot.
Run `sudo systmectl enable U2FDevice.service` Run `sudo systemctl enable U2FDevice.service`
## Debug files ## Debug files

View File

@@ -21,27 +21,24 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
using namespace std; using namespace std;
//Ripped from https://github.com/pratikd650/Teensy_U2F/blob/master/Teensy_U2F.cpp // Ripped from https://github.com/pratikd650/Teensy_U2F/blob/master/Teensy_U2F.cpp
void appendSignatureAsDER(vector<uint8_t> &response, const array<uint8_t, 64> &signature) void appendSignatureAsDER(vector<uint8_t>& response, const array<uint8_t, 64>& signature) {
{
response.push_back(0x30); // Start of ASN.1 SEQUENCE response.push_back(0x30); // Start of ASN.1 SEQUENCE
response.push_back(68); //total length from (2 * (32 + 2)) to (2 * (33 + 2)) response.push_back(68); // total length from (2 * (32 + 2)) to (2 * (33 + 2))
size_t countByte = response.size() - 1; size_t countByte = response.size() - 1;
// Loop twice - for R and S // Loop twice - for R and S
for(unsigned int i = 0; i < 2; i++) for (unsigned int i = 0; i < 2; i++) {
{
unsigned int sigOffs = i * 32; unsigned int sigOffs = i * 32;
auto offset = signature.begin() + sigOffs; auto offset = signature.begin() + sigOffs;
response.push_back(0x02); //header: integer response.push_back(0x02); // header: integer
response.push_back(32); //32 byte response.push_back(32); // 32 byte
if (signature[sigOffs] > 0x7f) if (signature[sigOffs] > 0x7f) {
{
// Integer needs to be represented in 2's completement notion // Integer needs to be represented in 2's completement notion
response.back()++; response.back()++;
response.push_back(0); // add leading 0, to indicate it is a positive number response.push_back(0); // add leading 0, to indicate it is a positive number
response[countByte]++; response[countByte]++;
} }
copy(offset, offset + 32, back_inserter(response)); //R or S value copy(offset, offset + 32, back_inserter(response)); // R or S value
} }
} }

View File

@@ -17,11 +17,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <vector>
#include <array> #include <array>
#include <vector>
using Digest = std::array<uint8_t, 32>; using Digest = std::array<uint8_t, 32>;
using Signature = std::array<uint8_t, 64>; using Signature = std::array<uint8_t, 64>;
//Ripped from https://github.com/pratikd650/Teensy_U2F/blob/master/Teensy_U2F.cpp // Ripped from https://github.com/pratikd650/Teensy_U2F/blob/master/Teensy_U2F.cpp
void appendSignatureAsDER(std::vector<uint8_t> &response, const Signature &signature); void appendSignatureAsDER(std::vector<uint8_t>& response, const Signature& signature);

View File

@@ -17,10 +17,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "Storage.hpp" #include "Storage.hpp"
#include <exception>
#include <sstream>
#include "Base64.tpp" #include "Base64.tpp"
#include <exception>
#include <iostream> #include <iostream>
#include <sstream>
using namespace std; using namespace std;
@@ -30,15 +30,13 @@ std::map<Storage::KeyHandle, Storage::PrivKey> Storage::privKeys{};
std::map<Storage::KeyHandle, Storage::PubKey> Storage::pubKeys{}; std::map<Storage::KeyHandle, Storage::PubKey> Storage::pubKeys{};
std::map<Storage::KeyHandle, Storage::KeyCount> Storage::keyCounts{}; std::map<Storage::KeyHandle, Storage::KeyCount> Storage::keyCounts{};
void Storage::init(const string &dirPrefix) void Storage::init(const string& dirPrefix) {
{
Storage::filename = dirPrefix + "U2F_Priv_Keys.txt"; Storage::filename = dirPrefix + "U2F_Priv_Keys.txt";
ifstream file{ Storage::filename }; ifstream file{ Storage::filename };
string line; string line;
size_t lineNumber = 0; size_t lineNumber = 0;
while (getline(file, line)) while (getline(file, line)) {
{
auto strLineNum = to_string(lineNumber); auto strLineNum = to_string(lineNumber);
stringstream ss{ line }; stringstream ss{ line };
string keyHStr, appStr, privStr, pubStr, keyCStr; string keyHStr, appStr, privStr, pubStr, keyCStr;
@@ -47,14 +45,16 @@ void Storage::init(const string &dirPrefix)
if (!ss) if (!ss)
throw runtime_error{ string{ "Invalid syntax of line " } + strLineNum }; throw runtime_error{ string{ "Invalid syntax of line " } + strLineNum };
char *endP = nullptr; char* endP = nullptr;
Storage::KeyHandle keyH{ static_cast<Storage::KeyHandle>(strtoull(keyHStr.c_str(), &endP, 10)) }; Storage::KeyHandle keyH{ static_cast<Storage::KeyHandle>(
strtoull(keyHStr.c_str(), &endP, 10)) };
if (!endP) if (!endP)
throw runtime_error{ "Invalid keyhandle format on line " + strLineNum }; throw runtime_error{ "Invalid keyhandle format on line " + strLineNum };
endP = nullptr; endP = nullptr;
Storage::KeyCount keyC{ static_cast<Storage::KeyCount>(strtoull(keyCStr.c_str(), &endP, 10)) }; Storage::KeyCount keyC{ static_cast<Storage::KeyCount>(
strtoull(keyCStr.c_str(), &endP, 10)) };
if (!endP) if (!endP)
throw runtime_error{ "Invalid key count format on line " + strLineNum }; throw runtime_error{ "Invalid key count format on line " + strLineNum };
@@ -77,12 +77,10 @@ void Storage::init(const string &dirPrefix)
} }
} }
void Storage::save() void Storage::save() {
{
ofstream file{ Storage::filename }; ofstream file{ Storage::filename };
for (auto &keypair : Storage::appParams) for (const auto& keypair : Storage::appParams) {
{
const auto& keyID = keypair.first; const auto& keyID = keypair.first;
const auto& appParam = keypair.second; const auto& appParam = keypair.second;
const auto& privKey = Storage::privKeys[keyID]; const auto& privKey = Storage::privKeys[keyID];

View File

@@ -17,13 +17,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <map>
#include <array> #include <array>
#include <string>
#include <fstream> #include <fstream>
#include <map>
#include <string>
class Storage class Storage {
{
public: public:
using KeyHandle = uint32_t; using KeyHandle = uint32_t;
using KeyCount = uint32_t; using KeyCount = uint32_t;
@@ -31,13 +30,13 @@ public:
using PrivKey = std::array<uint8_t, 32>; using PrivKey = std::array<uint8_t, 32>;
using PubKey = std::array<uint8_t, 65>; using PubKey = std::array<uint8_t, 65>;
protected: protected:
Storage() = default; Storage() = default;
static std::string filename; static std::string filename;
public: public:
static void init(const std::string &dirPrefix = ""); static void init(const std::string& dirPrefix = "");
static void save(); static void save();
static std::map<KeyHandle, AppParam> appParams; static std::map<KeyHandle, AppParam> appParams;
static std::map<KeyHandle, PrivKey> privKeys; static std::map<KeyHandle, PrivKey> privKeys;

View File

@@ -17,28 +17,26 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "Streams.hpp" #include "Streams.hpp"
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cstdio> #include <cstdio>
#include <fcntl.h>
#include <iostream>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h> #include <unistd.h>
using namespace std; using namespace std;
#ifdef DEBUG_STREAMS #ifdef DEBUG_STREAMS
FILE* initHTML(FILE *fPtr, const string &title); FILE* initHTML(FILE* fPtr, const string& title);
void closeHTML(FILE *fPtr); void closeHTML(FILE* fPtr);
#endif #endif
shared_ptr<int> getHostDescriptor() shared_ptr<int> getHostDescriptor() {
{ static shared_ptr<int> descriptor{ new int{ open(HID_DEV, O_RDWR | O_NONBLOCK | O_APPEND) },
static shared_ptr<int> descriptor{}; [](int* fd) {
descriptor.reset(new int{ open(HID_DEV, O_RDWR | O_NONBLOCK | O_APPEND) }, [](int* fd){
close(*fd); close(*fd);
delete fd; delete fd;
}); } };
if (*descriptor == -1) if (*descriptor == -1)
throw runtime_error{ "Descriptor is unavailable" }; throw runtime_error{ "Descriptor is unavailable" };
@@ -47,9 +45,8 @@ shared_ptr<int> getHostDescriptor()
} }
#ifdef DEBUG_STREAMS #ifdef DEBUG_STREAMS
shared_ptr<FILE> getComHostStream() shared_ptr<FILE> getComHostStream() {
{ static shared_ptr<FILE> stream{ fopen(DEBUG_STREAMS "comhost.txt", "wb"), [](FILE* f) {
static shared_ptr<FILE> stream{ fopen(DEBUG_STREAMS "comhost.txt", "wb"), [](FILE *f){
clog << "Closing comhost stream" << endl; clog << "Closing comhost stream" << endl;
fclose(f); fclose(f);
} }; } };
@@ -60,9 +57,10 @@ shared_ptr<FILE> getComHostStream()
return stream; return stream;
} }
shared_ptr<FILE> getHostPacketStream() shared_ptr<FILE> getHostPacketStream() {
{ static shared_ptr<FILE> stream{ initHTML(fopen(DEBUG_STREAMS "hostpackets.html", "wb"),
static shared_ptr<FILE> stream{ initHTML(fopen(DEBUG_STREAMS "hostpackets.html", "wb"), "Host Packets"), [](FILE *f){ "Host Packets"),
[](FILE* f) {
clog << "Closing hostPackets stream" << endl; clog << "Closing hostPackets stream" << endl;
closeHTML(f); closeHTML(f);
} }; } };
@@ -73,9 +71,10 @@ shared_ptr<FILE> getHostPacketStream()
return stream; return stream;
} }
shared_ptr<FILE> getHostAPDUStream() shared_ptr<FILE> getHostAPDUStream() {
{ static shared_ptr<FILE> stream{ initHTML(fopen(DEBUG_STREAMS "hostAPDU.html", "wb"),
static shared_ptr<FILE> stream{ initHTML(fopen(DEBUG_STREAMS "hostAPDU.html", "wb"), "Host APDU"), [](FILE *f){ "Host APDU"),
[](FILE* f) {
clog << "Closing host APDU stream" << endl; clog << "Closing host APDU stream" << endl;
closeHTML(f); closeHTML(f);
} }; } };
@@ -86,9 +85,8 @@ shared_ptr<FILE> getHostAPDUStream()
return stream; return stream;
} }
shared_ptr<FILE> getComDevStream() shared_ptr<FILE> getComDevStream() {
{ static shared_ptr<FILE> stream{ fopen(DEBUG_STREAMS "comdev.txt", "wb"), [](FILE* f) {
static shared_ptr<FILE> stream{ fopen(DEBUG_STREAMS "comdev.txt", "wb"), [](FILE *f){
clog << "Closing comdev stream" << endl; clog << "Closing comdev stream" << endl;
fclose(f); fclose(f);
} }; } };
@@ -99,9 +97,10 @@ shared_ptr<FILE> getComDevStream()
return stream; return stream;
} }
shared_ptr<FILE> getDevPacketStream() shared_ptr<FILE> getDevPacketStream() {
{ static shared_ptr<FILE> stream{ initHTML(fopen(DEBUG_STREAMS "devpackets.html", "wb"),
static shared_ptr<FILE> stream{ initHTML(fopen(DEBUG_STREAMS "devpackets.html", "wb"), "Dev Packets"), [](FILE *f){ "Dev Packets"),
[](FILE* f) {
clog << "Closing devPackets stream" << endl; clog << "Closing devPackets stream" << endl;
closeHTML(f); closeHTML(f);
} }; } };
@@ -112,9 +111,9 @@ shared_ptr<FILE> getDevPacketStream()
return stream; return stream;
} }
shared_ptr<FILE> getDevAPDUStream() shared_ptr<FILE> getDevAPDUStream() {
{ static shared_ptr<FILE> stream{ initHTML(fopen(DEBUG_STREAMS "devAPDU.html", "wb"), "Dev APDU"),
static shared_ptr<FILE> stream{ initHTML(fopen(DEBUG_STREAMS "devAPDU.html", "wb"), "Dev APDU"), [](FILE *f){ [](FILE* f) {
clog << "Closing dev APDU stream" << endl; clog << "Closing dev APDU stream" << endl;
closeHTML(f); closeHTML(f);
} }; } };
@@ -125,9 +124,9 @@ shared_ptr<FILE> getDevAPDUStream()
return stream; return stream;
} }
FILE* initHTML(FILE *fPtr, const string &title) FILE* initHTML(FILE* fPtr, const string& title) {
{ fprintf(fPtr,
fprintf(fPtr, "<html>\n" "<html>\n"
"\t<head>\n" "\t<head>\n"
"\t\t<title>%s</title>\n" "\t\t<title>%s</title>\n"
"\t\t<style>\n" "\t\t<style>\n"
@@ -164,15 +163,18 @@ FILE* initHTML(FILE *fPtr, const string &title)
"\t\t</style>\n" "\t\t</style>\n"
"\t</head>\n" "\t</head>\n"
"\n" "\n"
"\t<body>", title.c_str()); "\t<body>",
title.c_str());
return fPtr; return fPtr;
} }
void closeHTML(FILE *fPtr) void closeHTML(FILE* fPtr) {
{
fprintf(fPtr, "\t</body>\n" fprintf(fPtr, "\t</body>\n"
"</html>"); "</html>");
fclose(fPtr); int successCode = fclose(fPtr);
if (successCode != 0)
cerr << "File closing error: " << errno << endl;
} }
#endif #endif

View File

@@ -17,9 +17,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "Architecture.hpp"
#include <cstdio> #include <cstdio>
#include <memory> #include <memory>
#include "Architecture.hpp"
std::shared_ptr<int> getHostDescriptor(); std::shared_ptr<int> getHostDescriptor();

View File

@@ -17,10 +17,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "Architecture.hpp" #include "Architecture.hpp"
#include <iostream>
#include "Storage.hpp"
#include "Controller.hpp" #include "Controller.hpp"
#include "LED.hpp" #include "LED.hpp"
#include "Storage.hpp"
#include <iostream>
#include <signal.h> #include <signal.h>
#include <unistd.h> #include <unistd.h>
@@ -31,13 +31,10 @@ void signalCallback(int signum);
volatile bool contProc = true; volatile bool contProc = true;
bool initialiseLights(const string& prog) { bool initialiseLights(const string& prog) {
try try {
{
disableACTTrigger(true); disableACTTrigger(true);
enableACTLED(false); enableACTLED(false);
} } catch (runtime_error& e) {
catch (runtime_error &e)
{
cerr << e.what() << endl; cerr << e.what() << endl;
if (getuid() != 0) if (getuid() != 0)
@@ -49,20 +46,16 @@ bool initialiseLights(const string& prog) {
return true; return true;
} }
int handleTransactions(const string& prog, const string& privKeyDir) int handleTransactions(const string& prog, const string& privKeyDir) {
{
signal(SIGINT, signalCallback); signal(SIGINT, signalCallback);
signal(SIGTERM, signalCallback);
Storage::init(privKeyDir); Storage::init(privKeyDir);
Controller ch{ 0xF1D00000 }; Controller ch{ 0xF1D00000 };
while (contProc) while (contProc) {
{ try {
try
{
ch.handleTransaction(); ch.handleTransaction();
} } catch (const runtime_error& e) {
catch (const runtime_error &e)
{
cerr << e.what() << endl; cerr << e.what() << endl;
if (getuid() != 0) if (getuid() != 0)
@@ -79,13 +72,10 @@ int handleTransactions(const string& prog, const string& privKeyDir)
} }
bool deinitialiseLights(const string& prog) { bool deinitialiseLights(const string& prog) {
try try {
{
disableACTTrigger(false); disableACTTrigger(false);
enableACTLED(true); enableACTLED(true);
} } catch (runtime_error& e) {
catch (runtime_error &e)
{
cerr << e.what() << endl; cerr << e.what() << endl;
if (getuid() != 0) if (getuid() != 0)
@@ -97,8 +87,7 @@ bool deinitialiseLights(const string& prog) {
return true; return true;
} }
void signalCallback([[maybe_unused]] int signum) void signalCallback([[maybe_unused]] int signum) {
{
contProc = false; contProc = false;
clog << "\nClosing" << endl; clog << "\nClosing" << endl;
} }

View File

@@ -24,4 +24,3 @@ extern volatile bool contProc;
bool initialiseLights(const std::string& prog); bool initialiseLights(const std::string& prog);
bool deinitialiseLights(const std::string& prog); bool deinitialiseLights(const std::string& prog);
int handleTransactions(const std::string& prog, const std::string& privKeyDir); int handleTransactions(const std::string& prog, const std::string& privKeyDir);

View File

@@ -17,18 +17,17 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "U2FMessage.hpp" #include "U2FMessage.hpp"
#include "IO.hpp"
#include "Packet.hpp" #include "Packet.hpp"
#include <stdexcept>
#include <iostream>
#include <iomanip>
#include "Streams.hpp" #include "Streams.hpp"
#include "u2f.hpp" #include "u2f.hpp"
#include "IO.hpp" #include <iomanip>
#include <iostream>
#include <stdexcept>
using namespace std; using namespace std;
shared_ptr<U2FMessage> U2FMessage::readNonBlock() shared_ptr<U2FMessage> U2FMessage::readNonBlock() {
{
const static size_t startSeq = (size_t)-1ull; const static size_t startSeq = (size_t)-1ull;
static size_t currSeq = startSeq; static size_t currSeq = startSeq;
static uint16_t messageSize; static uint16_t messageSize;
@@ -38,16 +37,14 @@ shared_ptr<U2FMessage> U2FMessage::readNonBlock()
shared_ptr<Packet> p{}; shared_ptr<Packet> p{};
if (currSeq == startSeq) if (currSeq == startSeq) {
{
cid = 0; cid = 0;
cmd = 0; cmd = 0;
messageSize = 0; messageSize = 0;
dataBytes = {}; dataBytes = {};
shared_ptr<InitPacket> initPack{}; shared_ptr<InitPacket> initPack{};
do do {
{
p = Packet::getPacket(); p = Packet::getPacket();
if (!p) if (!p)
@@ -59,43 +56,46 @@ shared_ptr<U2FMessage> U2FMessage::readNonBlock()
if (!initPack) if (!initPack)
cerr << "Spurious cont. packet" << endl; cerr << "Spurious cont. packet" << endl;
#endif #endif
} while (!initPack); //Spurious cont. packet - spec states ignore } while (!initPack); // Spurious cont. packet - spec states ignore
messageSize = ((static_cast<uint16_t>(initPack->bcnth) << 8u) + initPack->bcntl); messageSize = ((static_cast<uint16_t>(initPack->bcnth) << 8u) + initPack->bcntl);
const uint16_t copyByteCount = min(static_cast<uint16_t>(initPack->data.size()), messageSize); const uint16_t copyByteCount =
min(static_cast<uint16_t>(initPack->data.size()), messageSize);
cid = initPack->cid; cid = initPack->cid;
cmd = initPack->cmd; cmd = initPack->cmd;
copy(initPack->data.begin(), initPack->data.begin() + copyByteCount, back_inserter(dataBytes)); copy(initPack->data.begin(), initPack->data.begin() + copyByteCount,
back_inserter(dataBytes));
currSeq = 0; currSeq = 0;
} }
while (messageSize > dataBytes.size() && static_cast<bool>(p = Packet::getPacket())) //While there is a packet while (messageSize > dataBytes.size() &&
static_cast<bool>(p = Packet::getPacket())) // While there is a packet
{ {
auto contPack = dynamic_pointer_cast<ContPacket>(p); auto contPack = dynamic_pointer_cast<ContPacket>(p);
if (!contPack) //Spurious init. packet if (!contPack) // Spurious init. packet
{ {
#ifdef DEBUG_MSGS #ifdef DEBUG_MSGS
cerr << "Spurious init. packet" << endl; cerr << "Spurious init. packet" << endl;
#endif #endif
currSeq = startSeq; //Reset currSeq = startSeq; // Reset
return {}; return {};
} }
if (contPack->cid != cid) //Cont. packet of different CID if (contPack->cid != cid) // Cont. packet of different CID
{ {
#ifdef DEBUG_MSGS #ifdef DEBUG_MSGS
cerr << "Invalid CID: was handling channel 0x" << hex << cid << " and received packet from channel 0x" << contPack->cid << dec << endl; cerr << "Invalid CID: was handling channel 0x" << hex << cid
<< " and received packet from channel 0x" << contPack->cid << dec << endl;
#endif #endif
U2FMessage::error(contPack->cid, ERR_CHANNEL_BUSY); U2FMessage::error(contPack->cid, ERR_CHANNEL_BUSY);
currSeq = startSeq; currSeq = startSeq;
return {}; return {};
} }
if (contPack->seq != currSeq) if (contPack->seq != currSeq) {
{
#ifdef DEBUG_MSGS #ifdef DEBUG_MSGS
cerr << "Invalid packet seq. value" << endl; cerr << "Invalid packet seq. value" << endl;
#endif #endif
@@ -105,15 +105,18 @@ shared_ptr<U2FMessage> U2FMessage::readNonBlock()
} }
const uint16_t remainingBytes = messageSize - dataBytes.size(); const uint16_t remainingBytes = messageSize - dataBytes.size();
const uint16_t copyBytes = min(static_cast<uint16_t>(contPack->data.size()), remainingBytes); const uint16_t copyBytes =
min(static_cast<uint16_t>(contPack->data.size()), remainingBytes);
dataBytes.insert(dataBytes.end(), contPack->data.begin(), contPack->data.begin() + copyBytes); dataBytes.insert(dataBytes.end(), contPack->data.begin(),
contPack->data.begin() + copyBytes);
currSeq++; currSeq++;
} }
if (messageSize != dataBytes.size()) { if (messageSize != dataBytes.size()) {
#ifdef DEBUG_MSGS #ifdef DEBUG_MSGS
cerr << "Invalid message size: " << messageSize << " when received " << dataBytes.size() << endl; cerr << "Invalid message size: " << messageSize << " when received " << dataBytes.size()
<< endl;
#endif #endif
return {}; return {};
} }
@@ -125,8 +128,7 @@ shared_ptr<U2FMessage> U2FMessage::readNonBlock()
return message; return message;
} }
void U2FMessage::write() void U2FMessage::write() {
{
const uint16_t bytesToWrite = this->data.size(); const uint16_t bytesToWrite = this->data.size();
uint16_t bytesWritten = 0; uint16_t bytesWritten = 0;
@@ -141,7 +143,8 @@ void U2FMessage::write()
p.bcntl = bcntl; p.bcntl = bcntl;
{ {
uint16_t initialByteCount = min(static_cast<uint16_t>(p.data.size()), static_cast<uint16_t>(bytesToWrite - bytesWritten)); uint16_t initialByteCount = min(static_cast<uint16_t>(p.data.size()),
static_cast<uint16_t>(bytesToWrite - bytesWritten));
copy(data.begin(), data.begin() + initialByteCount, p.data.begin()); copy(data.begin(), data.begin() + initialByteCount, p.data.begin());
bytesWritten += initialByteCount; bytesWritten += initialByteCount;
} }
@@ -151,20 +154,20 @@ void U2FMessage::write()
uint8_t seq = 0; uint8_t seq = 0;
while (bytesWritten != bytesToWrite) while (bytesWritten != bytesToWrite) {
{
ContPacket p{}; ContPacket p{};
p.cid = cid; p.cid = cid;
p.seq = seq; p.seq = seq;
uint16_t newByteCount = min(static_cast<uint16_t>(p.data.size()), static_cast<uint16_t>(bytesToWrite - bytesWritten)); uint16_t newByteCount = min(static_cast<uint16_t>(p.data.size()),
copy(data.begin() + bytesWritten, data.begin() + bytesWritten + newByteCount, p.data.begin()); static_cast<uint16_t>(bytesToWrite - bytesWritten));
copy(data.begin() + bytesWritten, data.begin() + bytesWritten + newByteCount,
p.data.begin());
p.writePacket(); p.writePacket();
seq++; seq++;
bytesWritten += newByteCount; bytesWritten += newByteCount;
} }
if (cmd == U2FHID_MSG) if (cmd == U2FHID_MSG) {
{
#ifdef DEBUG_STREAMS #ifdef DEBUG_STREAMS
auto dAS = getDevAPDUStream().get(); auto dAS = getDevAPDUStream().get();
@@ -187,22 +190,21 @@ void U2FMessage::write()
err |= data.back(); err |= data.back();
#ifdef DEBUG_STREAMS #ifdef DEBUG_STREAMS
fprintf(dAS, "</td>\n" fprintf(dAS,
"</td>\n"
"\t\t\t\t\t<td>0x%04X</td>\n" "\t\t\t\t\t<td>0x%04X</td>\n"
"\t\t\t\t</tr>\n" "\t\t\t\t</tr>\n"
"\t\t\t</tbody>\n" "\t\t\t</tbody>\n"
"\t\t</table>\n" "\t\t</table>\n"
"\t\t<br />", err); "\t\t<br />",
err);
#endif #endif
} }
} }
U2FMessage::U2FMessage(const uint32_t nCID, const uint8_t nCMD) U2FMessage::U2FMessage(const uint32_t nCID, const uint8_t nCMD) : cid{ nCID }, cmd{ nCMD } {}
: cid{ nCID }, cmd{ nCMD }
{}
void U2FMessage::error(const uint32_t tCID, const uint8_t tErr) void U2FMessage::error(const uint32_t tCID, const uint8_t tErr) {
{
U2FMessage msg{}; U2FMessage msg{};
msg.cid = tCID; msg.cid = tCID;
msg.cmd = U2FHID_ERROR; msg.cmd = U2FHID_ERROR;

View File

@@ -18,18 +18,15 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <vector>
#include <memory> #include <memory>
#include <cstdint> #include <vector>
struct U2FMessage struct U2FMessage {
{
public:
uint32_t cid; uint32_t cid;
uint8_t cmd; uint8_t cmd;
std::vector<uint8_t> data; std::vector<uint8_t> data;
public: public:
U2FMessage() = default; U2FMessage() = default;
U2FMessage(const uint32_t nCID, const uint8_t nCMD); U2FMessage(const uint32_t nCID, const uint8_t nCMD);
static std::shared_ptr<U2FMessage> readNonBlock(); static std::shared_ptr<U2FMessage> readNonBlock();

View File

@@ -17,29 +17,25 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "U2F_Authenticate_APDU.hpp" #include "U2F_Authenticate_APDU.hpp"
#include "Field.hpp"
#include "U2FMessage.hpp"
#include "u2f.hpp"
#include "Field.tpp"
#include "APDU.hpp" #include "APDU.hpp"
#include <iostream> #include "Field.hpp"
#include "Field.tpp"
#include "Signature.hpp" #include "Signature.hpp"
#include "U2FMessage.hpp"
#include "micro-ecc/uECC.h" #include "micro-ecc/uECC.h"
#include "u2f.hpp"
#include <iostream>
#include <mbedtls/sha256.h> #include <mbedtls/sha256.h>
using namespace std; using namespace std;
U2F_Authenticate_APDU::U2F_Authenticate_APDU(const U2F_Msg_CMD &msg, const vector<uint8_t> &data) U2F_Authenticate_APDU::U2F_Authenticate_APDU(const U2F_Msg_CMD& msg, const vector<uint8_t>& data)
: U2F_Msg_CMD{ msg } : U2F_Msg_CMD{ msg } {
{ if (p2 != 0) {
if (p2 != 0) // Invalid U2F (APDU) parameter detected
{
//Invalid U2F (APDU) parameter detected
throw APDU_STATUS::SW_CONDITIONS_NOT_SATISFIED; throw APDU_STATUS::SW_CONDITIONS_NOT_SATISFIED;
} } else if (data.size() < 66) {
else if (data.size() < 66) // Invalid authentication request
{
//Invalid authentication request
throw APDU_STATUS::SW_WRONG_LENGTH; throw APDU_STATUS::SW_WRONG_LENGTH;
} }
@@ -51,11 +47,9 @@ U2F_Authenticate_APDU::U2F_Authenticate_APDU(const U2F_Msg_CMD &msg, const vecto
copy(data.begin() + 65, data.begin() + 65 + keyHLen, back_inserter(keyH)); copy(data.begin() + 65, data.begin() + 65 + keyHLen, back_inserter(keyH));
} }
void U2F_Authenticate_APDU::respond(const uint32_t channelID) const void U2F_Authenticate_APDU::respond(const uint32_t channelID) const {
{ if (keyH.size() != sizeof(Storage::KeyHandle)) {
if (keyH.size() != sizeof(Storage::KeyHandle)) // Respond with error code - key handle is of wrong size
{
//Respond with error code - key handle is of wrong size
cerr << "Invalid key handle length" << endl; cerr << "Invalid key handle length" << endl;
this->error(channelID, APDU_STATUS::SW_WRONG_DATA); this->error(channelID, APDU_STATUS::SW_WRONG_DATA);
return; return;
@@ -63,38 +57,28 @@ void U2F_Authenticate_APDU::respond(const uint32_t channelID) const
auto keyHB = *reinterpret_cast<const Storage::KeyHandle*>(keyH.data()); auto keyHB = *reinterpret_cast<const Storage::KeyHandle*>(keyH.data());
if (Storage::appParams.find(keyHB) == Storage::appParams.end()) if (Storage::appParams.find(keyHB) == Storage::appParams.end()) {
{ // Respond with error code - key handle doesn't exist in storage
//Respond with error code - key handle doesn't exist in storage
cerr << "Invalid key handle" << endl; cerr << "Invalid key handle" << endl;
this->error(channelID, SW_WRONG_DATA); this->error(channelID, APDU_STATUS::SW_WRONG_DATA);
return; return;
} }
auto appMatches = (Storage::appParams.at(keyHB) == appParam); auto appMatches = (Storage::appParams.at(keyHB) == appParam);
U2FMessage msg{}; if (!appMatches) {
msg.cid = channelID; this->error(channelID, APDU_STATUS::SW_WRONG_DATA);
msg.cmd = U2FHID_MSG; return;
}
auto &response = msg.data; switch (p1) {
APDU_STATUS statusCode = APDU_STATUS::SW_NO_ERROR;
switch (p1)
{
case ControlCode::CheckOnly: case ControlCode::CheckOnly:
if (appMatches) this->error(channelID, APDU_STATUS::SW_CONDITIONS_NOT_SATISFIED);
statusCode = APDU_STATUS::SW_CONDITIONS_NOT_SATISFIED;
else
statusCode = APDU_STATUS::SW_WRONG_DATA;
response.insert(response.end(), FIELD_BE(statusCode));
msg.write();
return; return;
case ControlCode::EnforcePresenceSign: case ControlCode::EnforcePresenceSign:
//Continue processing // Continue processing
case ControlCode::DontEnforcePresenceSign: case ControlCode::DontEnforcePresenceSign:
//Continue processing // Continue processing
break; break;
default: default:
@@ -103,6 +87,13 @@ void U2F_Authenticate_APDU::respond(const uint32_t channelID) const
return; return;
} }
U2FMessage msg{};
msg.cid = channelID;
msg.cmd = U2FHID_MSG;
auto& response = msg.data;
APDU_STATUS statusCode = APDU_STATUS::SW_NO_ERROR;
const auto& privKey = Storage::privKeys[keyHB]; const auto& privKey = Storage::privKeys[keyHB];
auto& keyCount = Storage::keyCounts[keyHB]; auto& keyCount = Storage::keyCounts[keyHB];
keyCount++; keyCount++;
@@ -117,7 +108,8 @@ void U2F_Authenticate_APDU::respond(const uint32_t channelID) const
mbedtls_sha256_init(&shaContext); mbedtls_sha256_init(&shaContext);
mbedtls_sha256_starts(&shaContext, 0); mbedtls_sha256_starts(&shaContext, 0);
mbedtls_sha256_update(&shaContext, reinterpret_cast<const uint8_t *>(appParam.data()), sizeof(appParam)); mbedtls_sha256_update(&shaContext, reinterpret_cast<const uint8_t*>(appParam.data()),
sizeof(appParam));
uint8_t userPresence{ 1u }; uint8_t userPresence{ 1u };
mbedtls_sha256_update(&shaContext, &userPresence, 1); mbedtls_sha256_update(&shaContext, &userPresence, 1);
const auto beCounter = beEncode(keyCount); const auto beCounter = beEncode(keyCount);

View File

@@ -17,23 +17,23 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "U2F_Msg_CMD.hpp"
#include "Storage.hpp" #include "Storage.hpp"
#include "U2F_Msg_CMD.hpp"
struct U2F_Authenticate_APDU : U2F_Msg_CMD class U2F_Authenticate_APDU : public U2F_Msg_CMD {
{ protected:
uint8_t controlByte; uint8_t controlByte;
std::array<uint8_t, 32> challengeP; std::array<uint8_t, 32> challengeP;
Storage::AppParam appParam; Storage::AppParam appParam;
std::vector<uint8_t> keyH; std::vector<uint8_t> keyH;
public: public:
U2F_Authenticate_APDU(const U2F_Msg_CMD &msg, const std::vector<uint8_t> &data); U2F_Authenticate_APDU(const U2F_Msg_CMD& msg, const std::vector<uint8_t>& data);
virtual void respond(const uint32_t channelID) const override; virtual void respond(const uint32_t channelID) const override;
enum ControlCode protected:
{ enum ControlCode {
CheckOnly = 0x07, CheckOnly = 0x07,
EnforcePresenceSign = 0x03, EnforcePresenceSign = 0x03,
DontEnforcePresenceSign = 0x08 DontEnforcePresenceSign = 0x08

View File

@@ -17,19 +17,16 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "U2F_CMD.hpp" #include "U2F_CMD.hpp"
#include "u2f.hpp"
#include "U2F_Msg_CMD.hpp"
#include "U2F_Init_CMD.hpp" #include "U2F_Init_CMD.hpp"
#include "U2F_Msg_CMD.hpp"
#include "U2F_Ping_CMD.hpp" #include "U2F_Ping_CMD.hpp"
#include "u2f.hpp"
using namespace std; using namespace std;
shared_ptr<U2F_CMD> U2F_CMD::get(const shared_ptr<U2FMessage> uMsg) shared_ptr<U2F_CMD> U2F_CMD::get(const shared_ptr<U2FMessage> uMsg) {
{ try {
try switch (uMsg->cmd) {
{
switch (uMsg->cmd)
{
case U2FHID_PING: case U2FHID_PING:
return make_shared<U2F_Ping_CMD>(uMsg); return make_shared<U2F_Ping_CMD>(uMsg);
case U2FHID_MSG: case U2FHID_MSG:
@@ -40,9 +37,7 @@ shared_ptr<U2F_CMD> U2F_CMD::get(const shared_ptr<U2FMessage> uMsg)
U2FMessage::error(uMsg->cid, ERR_INVALID_CMD); U2FMessage::error(uMsg->cid, ERR_INVALID_CMD);
return {}; return {};
} }
} } catch (runtime_error& ignored) {
catch (runtime_error& ignored)
{
U2FMessage::error(uMsg->cid, ERR_OTHER); U2FMessage::error(uMsg->cid, ERR_OTHER);
return {}; return {};
} }

View File

@@ -17,16 +17,16 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <memory>
#include "U2FMessage.hpp" #include "U2FMessage.hpp"
#include <memory>
struct U2F_CMD class U2F_CMD {
{ protected:
protected:
U2F_CMD() = default; U2F_CMD() = default;
public: public:
virtual ~U2F_CMD() = default; virtual ~U2F_CMD() = default;
static std::shared_ptr<U2F_CMD> get(const std::shared_ptr<U2FMessage> uMsg); static std::shared_ptr<U2F_CMD> get(const std::shared_ptr<U2FMessage> uMsg);
virtual void respond(const uint32_t channelID) const = 0; virtual void respond(const uint32_t channelID) const = 0;
}; //For polymorphic type casting virtual bool modifiesPersistentState() const = 0;
}; // For polymorphic type casting

View File

@@ -17,23 +17,19 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "U2F_Init_CMD.hpp" #include "U2F_Init_CMD.hpp"
#include <stdexcept>
#include "u2f.hpp"
#include "Field.hpp" #include "Field.hpp"
#include "u2f.hpp"
#include <stdexcept>
using namespace std; using namespace std;
U2F_Init_CMD::U2F_Init_CMD(const shared_ptr<U2FMessage> uMsg) U2F_Init_CMD::U2F_Init_CMD(const shared_ptr<U2FMessage> uMsg) {
{
if (uMsg->cmd != U2FHID_INIT) if (uMsg->cmd != U2FHID_INIT)
throw runtime_error{ "Failed to get U2F Init message" }; throw runtime_error{ "Failed to get U2F Init message" };
else if (uMsg->cid != CID_BROADCAST) else if (uMsg->cid != CID_BROADCAST) {
{
U2FMessage::error(uMsg->cid, ERR_OTHER); U2FMessage::error(uMsg->cid, ERR_OTHER);
throw runtime_error{ "Invalid CID for init command" }; throw runtime_error{ "Invalid CID for init command" };
} } else if (uMsg->data.size() != INIT_NONCE_SIZE) {
else if (uMsg->data.size() != INIT_NONCE_SIZE)
{
U2FMessage::error(uMsg->cid, ERR_INVALID_LEN); U2FMessage::error(uMsg->cid, ERR_INVALID_LEN);
throw runtime_error{ "Init nonce is incorrect size" }; throw runtime_error{ "Init nonce is incorrect size" };
} }
@@ -41,19 +37,22 @@ U2F_Init_CMD::U2F_Init_CMD(const shared_ptr<U2FMessage> uMsg)
this->nonce = *reinterpret_cast<const uint64_t*>(uMsg->data.data()); this->nonce = *reinterpret_cast<const uint64_t*>(uMsg->data.data());
} }
void U2F_Init_CMD::respond(const uint32_t channelID) const void U2F_Init_CMD::respond(const uint32_t channelID) const {
{
U2FMessage msg{}; U2FMessage msg{};
msg.cid = CID_BROADCAST; msg.cid = CID_BROADCAST;
msg.cmd = U2FHID_INIT; msg.cmd = U2FHID_INIT;
msg.data.insert(msg.data.end(), FIELD(this->nonce)); msg.data.insert(msg.data.end(), FIELD(this->nonce));
msg.data.insert(msg.data.end(), FIELD(channelID)); msg.data.insert(msg.data.end(), FIELD(channelID));
msg.data.push_back(2); //Protocol version msg.data.push_back(2); // Protocol version
msg.data.push_back(1); //Major device version msg.data.push_back(1); // Major device version
msg.data.push_back(0); //Minor device version msg.data.push_back(0); // Minor device version
msg.data.push_back(1); //Build device version msg.data.push_back(1); // Build device version
msg.data.push_back(CAPFLAG_WINK); //Wink capability msg.data.push_back(CAPFLAG_WINK); // Wink capability
msg.write(); msg.write();
} }
bool U2F_Init_CMD::modifiesPersistentState() const {
return false;
}

View File

@@ -17,16 +17,17 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "U2FMessage.hpp"
#include "U2F_CMD.hpp"
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
#include "U2F_CMD.hpp"
#include "U2FMessage.hpp"
struct U2F_Init_CMD : U2F_CMD class U2F_Init_CMD : public U2F_CMD {
{ protected:
uint64_t nonce; uint64_t nonce;
public: public:
U2F_Init_CMD(const std::shared_ptr<U2FMessage> uMsg); U2F_Init_CMD(const std::shared_ptr<U2FMessage> uMsg);
virtual void respond(const uint32_t channelID) const override; virtual void respond(const uint32_t channelID) const override;
virtual bool modifiesPersistentState() const override;
}; };

View File

@@ -18,65 +18,57 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "U2F_Msg_CMD.hpp" #include "U2F_Msg_CMD.hpp"
#include "APDU.hpp" #include "APDU.hpp"
#include "Field.hpp"
#include "Streams.hpp"
#include "U2FMessage.hpp"
#include "U2F_Authenticate_APDU.hpp"
#include "U2F_Register_APDU.hpp" #include "U2F_Register_APDU.hpp"
#include "U2F_Version_APDU.hpp" #include "U2F_Version_APDU.hpp"
#include "U2F_Authenticate_APDU.hpp"
#include "U2FMessage.hpp"
#include "u2f.hpp" #include "u2f.hpp"
#include "APDU.hpp"
#include <iostream> #include <iostream>
#include "Streams.hpp"
#include "Field.hpp"
using namespace std; using namespace std;
uint32_t U2F_Msg_CMD::getLe(const uint32_t byteCount, vector<uint8_t> bytes) uint32_t U2F_Msg_CMD::getLe(const uint32_t byteCount, vector<uint8_t> bytes) {
{ if (byteCount != 0) {
if (byteCount != 0) // Le must be length of data in bytes
{
//Le must be length of data in bytes
switch (byteCount) switch (byteCount) {
{
case 1: case 1:
return (bytes[0] == 0 ? 256 : bytes[0]); return (bytes[0] == 0 ? 256 : bytes[0]);
case 2: case 2:
//Don't handle non-compliance with spec here // Don't handle non-compliance with spec here
//This case is only possible if extended Lc used // This case is only possible if extended Lc used
//CBA // CBA
return (bytes[0] == 0 && bytes[1] == 0 ? 65536 : (bytes[0] << 8) + bytes[1]); return (bytes[0] == 0 && bytes[1] == 0 ? 65536 : (bytes[0] << 8) + bytes[1]);
case 3: case 3:
//Don't handle non-compliance with spec here // Don't handle non-compliance with spec here
//This case is only possible if extended Lc not used // This case is only possible if extended Lc not used
//CBA // CBA
if (bytes[0] != 0) if (bytes[0] != 0)
throw runtime_error{ "First byte of 3-byte Le should be 0"}; throw runtime_error{ "First byte of 3-byte Le should be 0" };
return (bytes[1] == 0 && bytes[2] == 0 ? 65536 : (bytes[1] << 8) + bytes[2]); return (bytes[1] == 0 && bytes[2] == 0 ? 65536 : (bytes[1] << 8) + bytes[2]);
default: default:
throw runtime_error{ "Too much data for command" }; throw runtime_error{ "Too much data for command" };
} }
} } else
else
return 0; return 0;
} }
shared_ptr<U2F_Msg_CMD> U2F_Msg_CMD::generate(const shared_ptr<U2FMessage> uMsg) shared_ptr<U2F_Msg_CMD> U2F_Msg_CMD::generate(const shared_ptr<U2FMessage> uMsg) {
{
if (uMsg->cmd != U2FHID_MSG) if (uMsg->cmd != U2FHID_MSG)
throw runtime_error{ "Failed to get U2F Msg uMsg" }; throw runtime_error{ "Failed to get U2F Msg uMsg" };
else if (uMsg->data.size() < 4) else if (uMsg->data.size() < 4) {
{
U2F_Msg_CMD::error(uMsg->cid, APDU_STATUS::SW_WRONG_LENGTH); U2F_Msg_CMD::error(uMsg->cid, APDU_STATUS::SW_WRONG_LENGTH);
throw runtime_error{ "Msg data is incorrect size" }; throw runtime_error{ "Msg data is incorrect size" };
} }
U2F_Msg_CMD cmd; U2F_Msg_CMD cmd;
auto &dat = uMsg->data; auto& dat = uMsg->data;
cmd.cla = dat[0]; cmd.cla = dat[0];
if (cmd.cla != 0) if (cmd.cla != 0) {
{
U2F_Msg_CMD::error(uMsg->cid, APDU_STATUS::SW_COMMAND_NOT_ALLOWED); U2F_Msg_CMD::error(uMsg->cid, APDU_STATUS::SW_COMMAND_NOT_ALLOWED);
throw runtime_error{ "Invalid CLA value in U2F Message" }; throw runtime_error{ "Invalid CLA value in U2F Message" };
} }
@@ -89,48 +81,41 @@ shared_ptr<U2F_Msg_CMD> U2F_Msg_CMD::generate(const shared_ptr<U2FMessage> uMsg)
const uint32_t cBCount = data.size(); const uint32_t cBCount = data.size();
auto startPtr = data.begin(), endPtr = data.end(); auto startPtr = data.begin(), endPtr = data.end();
if (usesData.at(cmd.ins) || data.size() > 3) const auto cmdUsesData = usesData.find(cmd.ins);
{
if (cBCount == 0) if (cmdUsesData == usesData.end()) {
{ U2F_Msg_CMD::error(uMsg->cid, APDU_STATUS::SW_INS_NOT_SUPPORTED);
throw runtime_error{ "Unknown instruction: unsure if uses data" };
} else if (cmdUsesData->second || data.size() > 3) {
if (cBCount == 0) {
U2F_Msg_CMD::error(uMsg->cid, APDU_STATUS::SW_WRONG_LENGTH); U2F_Msg_CMD::error(uMsg->cid, APDU_STATUS::SW_WRONG_LENGTH);
throw runtime_error{ "Invalid command - should have attached data" }; throw runtime_error{ "Invalid command - should have attached data" };
} }
if (data[0] != 0) //1 byte length if (data[0] != 0) // 1 byte length
{ {
cmd.lc = data[0]; cmd.lc = data[0];
startPtr++; startPtr++;
} } else {
else
{
cmd.lc = (data[1] << 8) + data[2]; cmd.lc = (data[1] << 8) + data[2];
startPtr += 3; startPtr += 3;
} }
endPtr = startPtr + cmd.lc; endPtr = startPtr + cmd.lc;
try try {
{
cmd.le = getLe(data.end() - endPtr, vector<uint8_t>(endPtr, data.end())); cmd.le = getLe(data.end() - endPtr, vector<uint8_t>(endPtr, data.end()));
} } catch (runtime_error& ignored) {
catch (runtime_error& ignored)
{
U2F_Msg_CMD::error(uMsg->cid, APDU_STATUS::SW_WRONG_LENGTH); U2F_Msg_CMD::error(uMsg->cid, APDU_STATUS::SW_WRONG_LENGTH);
throw; throw;
} }
} } else {
else
{
cmd.lc = 0; cmd.lc = 0;
endPtr = startPtr; endPtr = startPtr;
try try {
{
cmd.le = getLe(cBCount, data); cmd.le = getLe(cBCount, data);
} } catch (runtime_error& ignored) {
catch (runtime_error& ignored)
{
U2F_Msg_CMD::error(uMsg->cid, APDU_STATUS::SW_WRONG_LENGTH); U2F_Msg_CMD::error(uMsg->cid, APDU_STATUS::SW_WRONG_LENGTH);
throw; throw;
} }
@@ -141,7 +126,8 @@ shared_ptr<U2F_Msg_CMD> U2F_Msg_CMD::generate(const shared_ptr<U2FMessage> uMsg)
#ifdef DEBUG_STREAMS #ifdef DEBUG_STREAMS
auto hAS = getHostAPDUStream().get(); auto hAS = getHostAPDUStream().get();
fprintf(hAS, "<table>\n" fprintf(hAS,
"<table>\n"
"\t\t\t<thead>\n" "\t\t\t<thead>\n"
"\t\t\t\t<tr>\n" "\t\t\t\t<tr>\n"
"\t\t\t\t\t<th>CLA</th>\n" "\t\t\t\t\t<th>CLA</th>\n"
@@ -160,23 +146,24 @@ shared_ptr<U2F_Msg_CMD> U2F_Msg_CMD::generate(const shared_ptr<U2FMessage> uMsg)
"\t\t\t\t\t<td>%u</td>\n" "\t\t\t\t\t<td>%u</td>\n"
"\t\t\t\t\t<td>%u</td>\n" "\t\t\t\t\t<td>%u</td>\n"
"\t\t\t\t\t<td>%3u</td>\n" "\t\t\t\t\t<td>%3u</td>\n"
"\t\t\t\t\t<td class=\"data\">", cmd.cla, cmd.ins, cmd.p1, cmd.p2, cmd.lc); "\t\t\t\t\t<td class=\"data\">",
cmd.cla, cmd.ins, cmd.p1, cmd.p2, cmd.lc);
for (auto b : dBytes) for (auto b : dBytes)
fprintf(hAS, "%3u ", b); fprintf(hAS, "%3u ", b);
fprintf(hAS, "</td>\n" fprintf(hAS,
"</td>\n"
"\t\t\t\t\t<td>%5u</td>\n" "\t\t\t\t\t<td>%5u</td>\n"
"\t\t\t\t</tr>\n" "\t\t\t\t</tr>\n"
"\t\t\t</tbody>\n" "\t\t\t</tbody>\n"
"\t\t</table>\n" "\t\t</table>\n"
"\t\t<br />", cmd.le); "\t\t<br />",
cmd.le);
#endif #endif
try try {
{ switch (cmd.ins) {
switch (cmd.ins)
{
case APDU::U2F_REG: case APDU::U2F_REG:
return make_shared<U2F_Register_APDU>(cmd, dBytes); return make_shared<U2F_Register_APDU>(cmd, dBytes);
case APDU::U2F_AUTH: case APDU::U2F_AUTH:
@@ -187,17 +174,14 @@ shared_ptr<U2F_Msg_CMD> U2F_Msg_CMD::generate(const shared_ptr<U2FMessage> uMsg)
cerr << "Invalid command used" << endl; cerr << "Invalid command used" << endl;
throw APDU_STATUS::SW_INS_NOT_SUPPORTED; throw APDU_STATUS::SW_INS_NOT_SUPPORTED;
} }
} } catch (const APDU_STATUS e) {
catch (const APDU_STATUS e)
{
U2F_Msg_CMD::error(uMsg->cid, e); U2F_Msg_CMD::error(uMsg->cid, e);
throw runtime_error{ "APDU construction error" }; throw runtime_error{ "APDU construction error" };
return {}; return {};
} }
} }
void U2F_Msg_CMD::error(const uint32_t channelID, const uint16_t errCode) void U2F_Msg_CMD::error(const uint32_t channelID, const uint16_t errCode) {
{
clog << "U2F_Msg_CMD::error " << errCode << endl; clog << "U2F_Msg_CMD::error " << errCode << endl;
U2FMessage msg{}; U2FMessage msg{};
msg.cid = channelID; msg.cid = channelID;
@@ -206,13 +190,17 @@ void U2F_Msg_CMD::error(const uint32_t channelID, const uint16_t errCode)
msg.write(); msg.write();
} }
const map<uint8_t, bool> U2F_Msg_CMD::usesData = { const map<uint8_t, bool> U2F_Msg_CMD::usesData = { { U2F_REG, true },
{ U2F_REG, true },
{ U2F_AUTH, true }, { U2F_AUTH, true },
{ U2F_VER, false } { U2F_VER, false } };
};
void U2F_Msg_CMD::respond(const uint32_t channelID) const void U2F_Msg_CMD::respond(const uint32_t channelID) const {
{
U2F_Msg_CMD::error(channelID, static_cast<uint16_t>(APDU_STATUS::SW_INS_NOT_SUPPORTED)); U2F_Msg_CMD::error(channelID, static_cast<uint16_t>(APDU_STATUS::SW_INS_NOT_SUPPORTED));
} }
bool U2F_Msg_CMD::modifiesPersistentState() const {
const auto usesPersistentState = usesData.find(ins);
// Be conservative for future instructions. Assume that they do modify persist state.
return (usesPersistentState == usesData.end()) || usesPersistentState->second;
}

View File

@@ -20,11 +20,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "U2F_CMD.hpp" #include "U2F_CMD.hpp"
#include <cstdint> #include <cstdint>
#include <map> #include <map>
#include <vector>
#include <memory> #include <memory>
#include <vector>
struct U2F_Msg_CMD : U2F_CMD struct U2F_Msg_CMD : public U2F_CMD {
{
uint8_t cla; uint8_t cla;
uint8_t ins; uint8_t ins;
uint8_t p1; uint8_t p1;
@@ -34,13 +33,13 @@ struct U2F_Msg_CMD : U2F_CMD
const static std::map<uint8_t, bool> usesData; const static std::map<uint8_t, bool> usesData;
protected: protected:
static uint32_t getLe(const uint32_t byteCount, std::vector<uint8_t> bytes); static uint32_t getLe(const uint32_t byteCount, std::vector<uint8_t> bytes);
U2F_Msg_CMD() = default; U2F_Msg_CMD() = default;
public: public:
static std::shared_ptr<U2F_Msg_CMD> generate(const std::shared_ptr<U2FMessage> uMsg); static std::shared_ptr<U2F_Msg_CMD> generate(const std::shared_ptr<U2FMessage> uMsg);
static void error(const uint32_t channelID, const uint16_t errCode); static void error(const uint32_t channelID, const uint16_t errCode);
void respond(const uint32_t channelID) const; void respond(const uint32_t channelID) const override;
virtual bool modifiesPersistentState() const override;
}; };

View File

@@ -21,18 +21,19 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
using namespace std; using namespace std;
U2F_Ping_CMD::U2F_Ping_CMD(const shared_ptr<U2FMessage> uMsg) U2F_Ping_CMD::U2F_Ping_CMD(const shared_ptr<U2FMessage> uMsg) : nonce{ uMsg->data } {
: nonce{ uMsg->data }
{
if (uMsg->cmd != U2FHID_PING) if (uMsg->cmd != U2FHID_PING)
throw runtime_error{ "Failed to get U2F ping message" }; throw runtime_error{ "Failed to get U2F ping message" };
} }
void U2F_Ping_CMD::respond(const uint32_t channelID) const void U2F_Ping_CMD::respond(const uint32_t channelID) const {
{
U2FMessage msg{}; U2FMessage msg{};
msg.cid = channelID; msg.cid = channelID;
msg.cmd = U2FHID_PING; msg.cmd = U2FHID_PING;
msg.data = nonce; msg.data = nonce;
msg.write(); msg.write();
} }
bool U2F_Ping_CMD::modifiesPersistentState() const {
return false;
}

View File

@@ -17,16 +17,17 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "U2FMessage.hpp"
#include "U2F_CMD.hpp"
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
#include "U2F_CMD.hpp"
#include "U2FMessage.hpp"
struct U2F_Ping_CMD : U2F_CMD class U2F_Ping_CMD : public U2F_CMD {
{ protected:
std::vector<uint8_t> nonce; std::vector<uint8_t> nonce;
public: public:
U2F_Ping_CMD(const std::shared_ptr<U2FMessage> uMsg); U2F_Ping_CMD(const std::shared_ptr<U2FMessage> uMsg);
virtual void respond(const uint32_t channelID) const override; virtual void respond(const uint32_t channelID) const override;
virtual bool modifiesPersistentState() const override;
}; };

View File

@@ -17,55 +17,51 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "U2F_Register_APDU.hpp" #include "U2F_Register_APDU.hpp"
#include <exception>
#include <cstring>
#include "micro-ecc/uECC.h"
#include "Certificates.hpp"
#include <mbedtls/sha256.h>
#include <iostream>
#include "APDU.hpp" #include "APDU.hpp"
#include "U2FMessage.hpp" #include "Certificates.hpp"
#include "u2f.hpp"
#include "Signature.hpp" #include "Signature.hpp"
#include "U2FMessage.hpp"
#include "micro-ecc/uECC.h"
#include "u2f.hpp"
#include <cstring>
#include <exception>
#include <iostream>
#include <mbedtls/sha256.h>
using namespace std; using namespace std;
U2F_Register_APDU::U2F_Register_APDU(const U2F_Msg_CMD &msg, const vector<uint8_t> &data) U2F_Register_APDU::U2F_Register_APDU(const U2F_Msg_CMD& msg, const vector<uint8_t>& data)
: U2F_Msg_CMD{ msg } : U2F_Msg_CMD{ msg } {
{ if (data.size() != 64) {
if (data.size() != 64) // Incorrect registration size
{
//Incorrect registration size
throw APDU_STATUS::SW_WRONG_LENGTH; throw APDU_STATUS::SW_WRONG_LENGTH;
} } else if ((p1 != 0x00 && p1 != 0x03) || p2 != 0x00) // According to spec, 0x03 not allowed here
else if ((p1 != 0x00 && p1 != 0x03) || p2 != 0x00) //According to spec, 0x03 not allowed here // However, browsers seem to do it, so...
//However, browsers seem to do it, so...
{ {
//Invalid U2F Message (APDU) parameters detected // Invalid U2F Message (APDU) parameters detected
throw APDU_STATUS::SW_INS_NOT_SUPPORTED; throw APDU_STATUS::SW_INS_NOT_SUPPORTED;
} }
copy(data.data() + 0, data.data() + 32, challengeP.begin()); copy(data.data() + 0, data.data() + 32, challengeP.begin());
copy(data.data() + 32, data.data() + 64, appP.begin()); copy(data.data() + 32, data.data() + 64, appP.begin());
//Use crypto lib to generate keypair // Use crypto lib to generate keypair
Storage::PrivKey privKey{}; Storage::PrivKey privKey{};
Storage::PubKey pubKey{}; Storage::PubKey pubKey{};
//First byte must be 0x04 for some reason // First byte must be 0x04 for some reason
pubKey[0] = 0x04; pubKey[0] = 0x04;
uECC_make_key(pubKey.data() + 1, privKey.data(), uECC_secp256r1()); uECC_make_key(pubKey.data() + 1, privKey.data(), uECC_secp256r1());
this->keyH = Storage::appParams.size(); //To increment the key handle this->keyH = Storage::appParams.size(); // To increment the key handle
Storage::appParams[this->keyH] = appP; Storage::appParams[this->keyH] = appP;
Storage::privKeys[this->keyH] = privKey; Storage::privKeys[this->keyH] = privKey;
Storage::pubKeys[this->keyH] = pubKey; Storage::pubKeys[this->keyH] = pubKey;
Storage::keyCounts[this->keyH] = 0; Storage::keyCounts[this->keyH] = 0;
} }
void U2F_Register_APDU::respond(const uint32_t channelID) const void U2F_Register_APDU::respond(const uint32_t channelID) const {
{
U2FMessage m{}; U2FMessage m{};
m.cid = channelID; m.cid = channelID;
m.cmd = U2FHID_MSG; m.cmd = U2FHID_MSG;
@@ -78,12 +74,12 @@ void U2F_Register_APDU::respond(const uint32_t channelID) const
copy(pubKey.begin(), pubKey.end(), back_inserter(response)); copy(pubKey.begin(), pubKey.end(), back_inserter(response));
response.push_back(sizeof(this->keyH)); response.push_back(sizeof(this->keyH));
auto fakeKeyHBytes = reinterpret_cast<const uint8_t *>(&this->keyH); auto fakeKeyHBytes = reinterpret_cast<const uint8_t*>(&this->keyH);
copy(fakeKeyHBytes, fakeKeyHBytes + sizeof(this->keyH), back_inserter(response)); copy(fakeKeyHBytes, fakeKeyHBytes + sizeof(this->keyH), back_inserter(response));
copy(attestCert, end(attestCert), back_inserter(response)); copy(attestCert, end(attestCert), back_inserter(response));
//Gen signature // Gen signature
Digest digest; Digest digest;
{ {
mbedtls_sha256_context shaContext; mbedtls_sha256_context shaContext;
@@ -94,13 +90,18 @@ void U2F_Register_APDU::respond(const uint32_t channelID) const
uint8_t byteReserved = 0; uint8_t byteReserved = 0;
mbedtls_sha256_update(&shaContext, reinterpret_cast<unsigned char*>(&byteReserved), 1); mbedtls_sha256_update(&shaContext, reinterpret_cast<unsigned char*>(&byteReserved), 1);
mbedtls_sha256_update(&shaContext, reinterpret_cast<const unsigned char*>(appParam.data()), appParam.size()); mbedtls_sha256_update(&shaContext, reinterpret_cast<const unsigned char*>(appParam.data()),
appParam.size());
mbedtls_sha256_update(&shaContext, reinterpret_cast<const unsigned char*>(challengeP.data()), challengeP.size()); mbedtls_sha256_update(&shaContext,
reinterpret_cast<const unsigned char*>(challengeP.data()),
challengeP.size());
mbedtls_sha256_update(&shaContext, reinterpret_cast<const unsigned char*>(&keyH), sizeof(keyH)); mbedtls_sha256_update(&shaContext, reinterpret_cast<const unsigned char*>(&keyH),
sizeof(keyH));
mbedtls_sha256_update(&shaContext, reinterpret_cast<const unsigned char*>(pubKey.data()), pubKey.size()); mbedtls_sha256_update(&shaContext, reinterpret_cast<const unsigned char*>(pubKey.data()),
pubKey.size());
mbedtls_sha256_finish(&shaContext, digest.data()); mbedtls_sha256_finish(&shaContext, digest.data());
mbedtls_sha256_free(&shaContext); mbedtls_sha256_free(&shaContext);
@@ -109,7 +110,7 @@ void U2F_Register_APDU::respond(const uint32_t channelID) const
Signature signature; Signature signature;
uECC_sign(attestPrivKey, digest.data(), digest.size(), signature.data(), uECC_secp256r1()); uECC_sign(attestPrivKey, digest.data(), digest.size(), signature.data(), uECC_secp256r1());
//Append signature as DER // Append signature as DER
appendSignatureAsDER(response, signature); appendSignatureAsDER(response, signature);
response.push_back(static_cast<uint16_t>(APDU_STATUS::SW_NO_ERROR) >> 8); response.push_back(static_cast<uint16_t>(APDU_STATUS::SW_NO_ERROR) >> 8);

View File

@@ -17,18 +17,17 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "U2F_Msg_CMD.hpp"
#include "Storage.hpp" #include "Storage.hpp"
#include "U2F_Msg_CMD.hpp"
struct U2F_Register_APDU : U2F_Msg_CMD class U2F_Register_APDU : public U2F_Msg_CMD {
{ protected:
std::array<uint8_t, 32> challengeP; std::array<uint8_t, 32> challengeP;
Storage::AppParam appP; Storage::AppParam appP;
Storage::KeyHandle keyH; Storage::KeyHandle keyH;
public: public:
U2F_Register_APDU(const U2F_Msg_CMD &msg, const std::vector<uint8_t> &data); U2F_Register_APDU(const U2F_Msg_CMD& msg, const std::vector<uint8_t>& data);
void respond(const uint32_t channelID) const override; void respond(const uint32_t channelID) const override;
}; };

View File

@@ -17,25 +17,23 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "U2F_Version_APDU.hpp" #include "U2F_Version_APDU.hpp"
#include <iostream> #include "APDU.hpp"
#include "Field.hpp"
#include "U2FMessage.hpp" #include "U2FMessage.hpp"
#include "u2f.hpp" #include "u2f.hpp"
#include "Field.hpp" #include <iostream>
#include "APDU.hpp"
using namespace std; using namespace std;
U2F_Version_APDU::U2F_Version_APDU(const U2F_Msg_CMD &msg, const std::vector<uint8_t> &data) U2F_Version_APDU::U2F_Version_APDU(const U2F_Msg_CMD& msg, const std::vector<uint8_t>& data) {
{ // Don't actually respond yet unless invalid
//Don't actually respond yet unless invalid
if (msg.p1 != 0 || msg.p2 != 0) if (msg.p1 != 0 || msg.p2 != 0)
throw APDU_STATUS::SW_INS_NOT_SUPPORTED; throw APDU_STATUS::SW_INS_NOT_SUPPORTED;
else if (data.size() != 0) else if (data.size() != 0)
throw APDU_STATUS::SW_WRONG_LENGTH; throw APDU_STATUS::SW_WRONG_LENGTH;
} }
void U2F_Version_APDU::respond(const uint32_t channelID) const void U2F_Version_APDU::respond(const uint32_t channelID) const {
{
char ver[]{ 'U', '2', 'F', '_', 'V', '2' }; char ver[]{ 'U', '2', 'F', '_', 'V', '2' };
U2FMessage m{}; U2FMessage m{};

View File

@@ -19,9 +19,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once #pragma once
#include "U2F_Msg_CMD.hpp" #include "U2F_Msg_CMD.hpp"
struct U2F_Version_APDU : U2F_Msg_CMD class U2F_Version_APDU : public U2F_Msg_CMD {
{ public:
public: U2F_Version_APDU(const U2F_Msg_CMD& msg, const std::vector<uint8_t>& data);
U2F_Version_APDU(const U2F_Msg_CMD &msg, const std::vector<uint8_t> &data);
void respond(const uint32_t channelID) const override; void respond(const uint32_t channelID) const override;
}; };

View File

@@ -1,17 +1,15 @@
#include "U2FDevice.hpp"
#include "Architecture.hpp" #include "Architecture.hpp"
#include "U2FDevice.hpp"
#include <execinfo.h> #include <execinfo.h>
#include <iostream> #include <iostream>
#ifdef DEBUG_MSGS #ifdef DEBUG_MSGS
//Courtesy StackOverflow answer https://stackoverflow.com/a/3356421 // Courtesy StackOverflow answer https://stackoverflow.com/a/3356421
void terminateHandler() void terminateHandler() {
{ void* trace_elems[20];
void *trace_elems[20];
int trace_elem_count(backtrace(trace_elems, 20)); int trace_elem_count(backtrace(trace_elems, 20));
char **stack_syms(backtrace_symbols(trace_elems, trace_elem_count)); char** stack_syms(backtrace_symbols(trace_elems, trace_elem_count));
for (int i = 0; i < trace_elem_count; ++i) for (int i = 0; i < trace_elem_count; ++i) {
{
std::cout << stack_syms[i] << "\n"; std::cout << stack_syms[i] << "\n";
} }
free(stack_syms); free(stack_syms);
@@ -20,19 +18,16 @@ void terminateHandler()
} }
#endif #endif
int main(int argc, char **argv) { int main(int argc, char** argv) {
#ifdef DEBUG_MSGS #ifdef DEBUG_MSGS
std::set_terminate(terminate_handler); std::set_terminate(terminateHandler);
#endif #endif
int retCode = handleTransactions(argv[0], argc == 2 ? argv[1] : STORAGE_PREFIX); int retCode = handleTransactions(argv[0], argc == 2 ? argv[1] : STORAGE_PREFIX);
try try {
{
initialiseLights(argv[0]); initialiseLights(argv[0]);
deinitialiseLights(argv[0]); deinitialiseLights(argv[0]);
} } catch (std::exception& e) {
catch (std::exception &e)
{
std::cerr << "Exception in code: " << e.what() << std::endl; std::cerr << "Exception in code: " << e.what() << std::endl;
throw; throw;
} }

View File

@@ -11,7 +11,7 @@ typedef unsigned short uint16_t;
typedef unsigned int uint32_t; typedef unsigned int uint32_t;
typedef unsigned long int uint64_t; typedef unsigned long int uint64_t;
#else #else
#include <stdint.h> # include <stdint.h>
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus
@@ -49,7 +49,7 @@ typedef struct {
#define FRAME_TYPE(f) ((f).type & TYPE_MASK) #define FRAME_TYPE(f) ((f).type & TYPE_MASK)
#define FRAME_CMD(f) ((f).init.cmd & ~TYPE_MASK) #define FRAME_CMD(f) ((f).init.cmd & ~TYPE_MASK)
#define MSG_LEN(f) ((f).init.bcnth*256 + (f).init.bcntl) #define MSG_LEN(f) ((f).init.bcnth * 256 + (f).init.bcntl)
#define FRAME_SEQ(f) ((f).cont.seq & ~TYPE_MASK) #define FRAME_SEQ(f) ((f).cont.seq & ~TYPE_MASK)
// HID usage- and usage-page definitions // HID usage- and usage-page definitions
@@ -124,4 +124,3 @@ typedef struct {
#endif #endif
#endif // __U2FHID_H_INCLUDED__ #endif // __U2FHID_H_INCLUDED__