Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1093294b61 | |||
| ed1add2c60 | |||
| c8061d373f | |||
| 784f3803f9 | |||
| 7ce0738338 | |||
| 701127496c | |||
| 528662a081 | |||
| 80b552012d | |||
|
|
aeb9c89be8 | ||
| ad250d14e3 | |||
| f7dea03132 | |||
| 55d1fd738f | |||
| 0ce5793414 | |||
| 4ce52d4da3 | |||
| a426a70dcf | |||
| c0ca093e83 | |||
| fc96017efd | |||
| b942713cc9 |
24
.clang-format
Normal file
24
.clang-format
Normal 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
5
.gitignore
vendored
@@ -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/*
|
||||||
|
|||||||
20
APDU.hpp
20
APDU.hpp
@@ -18,19 +18,13 @@ 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,
|
||||||
SW_WRONG_DATA = 0x6A80,
|
SW_WRONG_DATA = 0x6A80,
|
||||||
SW_INS_NOT_SUPPORTED = 0x6D00,
|
SW_INS_NOT_SUPPORTED = 0x6D00,
|
||||||
SW_COMMAND_NOT_ALLOWED = 0x6E00,
|
SW_COMMAND_NOT_ALLOWED = 0x6E00,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
14
Base64.hpp
14
Base64.hpp
@@ -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);
|
||||||
|
|||||||
@@ -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
37
Certificates.cpp.template
Normal 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
|
||||||
|
};
|
||||||
@@ -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 */];
|
||||||
30
Channel.cpp
30
Channel.cpp
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
45
Channel.hpp
45
Channel.hpp
@@ -17,34 +17,25 @@ 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,
|
enum class ChannelLockedState { Locked, Unlocked };
|
||||||
Initialised
|
|
||||||
};
|
class Channel {
|
||||||
|
protected:
|
||||||
enum class ChannelLockedState
|
uint32_t cid;
|
||||||
{
|
ChannelInitState initState;
|
||||||
Locked,
|
ChannelLockedState lockedState;
|
||||||
Unlocked
|
|
||||||
};
|
public:
|
||||||
|
Channel(const uint32_t channelID);
|
||||||
class Channel
|
void handle(const std::shared_ptr<U2FMessage> uMsg);
|
||||||
{
|
|
||||||
protected:
|
uint32_t getCID() const;
|
||||||
uint32_t cid;
|
void init(const ChannelInitState newInitState);
|
||||||
ChannelInitState initState;
|
void lock(const ChannelLockedState newLockedState);
|
||||||
ChannelLockedState lockedState;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Channel(const uint32_t channelID);
|
|
||||||
void handle(const std::shared_ptr<U2FMessage> uMsg);
|
|
||||||
|
|
||||||
uint32_t getCID() const;
|
|
||||||
void init(const ChannelInitState newInitState);
|
|
||||||
void lock(const ChannelLockedState newLockedState);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -17,20 +17,19 @@ 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();
|
||||||
uint32_t nextChannel();
|
uint32_t nextChannel();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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) };
|
||||||
}
|
}
|
||||||
|
|||||||
10
Field.hpp
10
Field.hpp
@@ -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
31
GenCertificates.sh
Executable 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
|
||||||
76
IO.cpp
76
IO.cpp
@@ -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
6
IO.hpp
@@ -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.
@@ -1,5 +0,0 @@
|
|||||||
-----BEGIN EC PRIVATE KEY-----
|
|
||||||
MHcCAQEEIH69kQVagJ825S/g0KljDIYEsQTj0fvQg8cuLzS21qSyoAoGCCqGSM49
|
|
||||||
AwEHoUQDQgAEMkHDuJaX2JBmQYiW1HO2N/eFKa87FQ+DYWfqybLbgrMsmWCKmHzU
|
|
||||||
BKCSIgWq93qRAgPdFYiHaibp7s+ZsWbAAQ==
|
|
||||||
-----END EC PRIVATE KEY-----
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
-----BEGIN PUBLIC KEY-----
|
|
||||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMkHDuJaX2JBmQYiW1HO2N/eFKa87
|
|
||||||
FQ+DYWfqybLbgrMsmWCKmHzUBKCSIgWq93qRAgPdFYiHaibp7s+ZsWbAAQ==
|
|
||||||
-----END PUBLIC KEY-----
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
~<7E><>Z<><5A>6<EFBFBD>/<2F>Щc<0C><04><04><><EFBFBD>Ѓ<EFBFBD>./4<>֤<EFBFBD>
|
|
||||||
@@ -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>
|
|
||||||
@@ -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
15
LED.cpp
@@ -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());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
12
Makefile
12
Makefile
@@ -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
|
||||||
|
|||||||
233
Packet.cpp
233
Packet.cpp
@@ -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]];
|
||||||
|
|
||||||
@@ -74,56 +73,56 @@ shared_ptr<InitPacket> InitPacket::getPacket(const uint32_t rCID, const uint8_t
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto p = make_shared<InitPacket>();
|
auto p = make_shared<InitPacket>();
|
||||||
p->cid = rCID;
|
p->cid = rCID;
|
||||||
p->cmd = rCMD;
|
p->cmd = rCMD;
|
||||||
p->bcnth = bcnth;
|
p->bcnth = bcnth;
|
||||||
p->bcntl = bcntl;
|
p->bcntl = bcntl;
|
||||||
p->data = dataBytes;
|
p->data = dataBytes;
|
||||||
|
|
||||||
#ifdef DEBUG_STREAMS
|
#ifdef DEBUG_STREAMS
|
||||||
auto hPStream = getHostPacketStream().get();
|
auto hPStream = getHostPacketStream().get();
|
||||||
fprintf(hPStream, "\t\t<table>\n"
|
fprintf(hPStream,
|
||||||
"\t\t\t<thead>\n"
|
"\t\t<table>\n"
|
||||||
"\t\t\t\t<tr>\n"
|
"\t\t\t<thead>\n"
|
||||||
"\t\t\t\t\t<th>CID</th>\n"
|
"\t\t\t\t<tr>\n"
|
||||||
"\t\t\t\t\t<th>CMD</th>\n"
|
"\t\t\t\t\t<th>CID</th>\n"
|
||||||
"\t\t\t\t\t<th>BCNTH</th>\n"
|
"\t\t\t\t\t<th>CMD</th>\n"
|
||||||
"\t\t\t\t\t<th>BCNTL</th>\n"
|
"\t\t\t\t\t<th>BCNTH</th>\n"
|
||||||
"\t\t\t\t\t<th class=\"data\">DATA</th>\n"
|
"\t\t\t\t\t<th>BCNTL</th>\n"
|
||||||
"\t\t\t\t</tr>\n"
|
"\t\t\t\t\t<th class=\"data\">DATA</th>\n"
|
||||||
"\t\t\t</thead>\n"
|
"\t\t\t\t</tr>\n"
|
||||||
"\t\t\t<tbody>\n"
|
"\t\t\t</thead>\n"
|
||||||
"\t\t\t\t<tr>\n"
|
"\t\t\t<tbody>\n"
|
||||||
"\t\t\t\t\t<td>0x%08X</td>\n"
|
"\t\t\t\t<tr>\n"
|
||||||
"\t\t\t\t\t<td>%u</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>%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>%u</td>\n"
|
||||||
|
"\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);
|
||||||
|
|
||||||
fprintf(hPStream, "</td>\n"
|
fprintf(hPStream, "</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>"
|
"\t\t</table>"
|
||||||
"\t\t<br />");
|
"\t\t<br />");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bytesRead = 0;
|
bytesRead = 0;
|
||||||
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,45 +139,45 @@ 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\t<thead>\n"
|
"\t\t<table>\n"
|
||||||
"\t\t\t\t<tr>\n"
|
"\t\t\t<thead>\n"
|
||||||
"\t\t\t\t\t<th>CID</th>\n"
|
"\t\t\t\t<tr>\n"
|
||||||
"\t\t\t\t\t<th>SEQ</th>\n"
|
"\t\t\t\t\t<th>CID</th>\n"
|
||||||
"\t\t\t\t\t<th class=\"data\">DATA</th>\n"
|
"\t\t\t\t\t<th>SEQ</th>\n"
|
||||||
"\t\t\t\t</tr>\n"
|
"\t\t\t\t\t<th class=\"data\">DATA</th>\n"
|
||||||
"\t\t\t</thead>\n"
|
"\t\t\t\t</tr>\n"
|
||||||
"\t\t\t<tbody>\n"
|
"\t\t\t</thead>\n"
|
||||||
"\t\t\t\t<tr>\n"
|
"\t\t\t<tbody>\n"
|
||||||
"\t\t\t\t\t<td>0x%08X</td>\n"
|
"\t\t\t\t<tr>\n"
|
||||||
"\t\t\t\t\t<td>%u</td>\n"
|
"\t\t\t\t\t<td>0x%08X</td>\n"
|
||||||
"\t\t\t\t\t<td class=\"data\">", p->cid, p->seq);
|
"\t\t\t\t\t<td>%u</td>\n"
|
||||||
|
"\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);
|
||||||
|
|
||||||
fprintf(hPStream, "</td>\n"
|
fprintf(hPStream, "</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 />");
|
"\t\t<br />");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
readBytes = 0;
|
readBytes = 0;
|
||||||
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{};
|
||||||
|
|
||||||
static uint32_t cid;
|
static uint32_t cid;
|
||||||
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,97 +221,98 @@ 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
|
||||||
auto devStream = getComDevStream().get();
|
auto devStream = getComDevStream().get();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
memcpy(this->buf + 4, &cmd, 1);
|
memcpy(this->buf + 4, &cmd, 1);
|
||||||
memcpy(this->buf + 5, &bcnth, 1);
|
memcpy(this->buf + 5, &bcnth, 1);
|
||||||
memcpy(this->buf + 6, &bcntl, 1);
|
memcpy(this->buf + 6, &bcntl, 1);
|
||||||
memcpy(this->buf + 7, data.data(), data.size());
|
memcpy(this->buf + 7, data.data(), data.size());
|
||||||
write(this->buf, sizeof(this->buf));
|
write(this->buf, sizeof(this->buf));
|
||||||
|
|
||||||
#ifdef DEBUG_STREAMS
|
#ifdef DEBUG_STREAMS
|
||||||
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\t<thead>\n"
|
"\t\t<table>\n"
|
||||||
"\t\t\t\t<tr>\n"
|
"\t\t\t<thead>\n"
|
||||||
"\t\t\t\t\t<th>CID</th>\n"
|
"\t\t\t\t<tr>\n"
|
||||||
"\t\t\t\t\t<th>CMD</th>\n"
|
"\t\t\t\t\t<th>CID</th>\n"
|
||||||
"\t\t\t\t\t<th>BCNTH</th>\n"
|
"\t\t\t\t\t<th>CMD</th>\n"
|
||||||
"\t\t\t\t\t<th>BCNTL</th>\n"
|
"\t\t\t\t\t<th>BCNTH</th>\n"
|
||||||
"\t\t\t\t\t<th class=\"data\">DATA</th>\n"
|
"\t\t\t\t\t<th>BCNTL</th>\n"
|
||||||
"\t\t\t\t</tr>\n"
|
"\t\t\t\t\t<th class=\"data\">DATA</th>\n"
|
||||||
"\t\t\t</thead>\n"
|
"\t\t\t\t</tr>\n"
|
||||||
"\t\t\t<tbody>\n"
|
"\t\t\t</thead>\n"
|
||||||
"\t\t\t\t<tr>\n"
|
"\t\t\t<tbody>\n"
|
||||||
"\t\t\t\t\t<td>0x%08X</td>\n"
|
"\t\t\t\t<tr>\n"
|
||||||
"\t\t\t\t\t<td>%u</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>%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>%u</td>\n"
|
||||||
|
"\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);
|
||||||
|
|
||||||
fprintf(dPStream, "</td>\n"
|
fprintf(dPStream, "</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>"
|
"\t\t</table>"
|
||||||
"\t\t<br />");
|
"\t\t<br />");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContPacket::writePacket()
|
void ContPacket::writePacket() {
|
||||||
{
|
|
||||||
Packet::writePacket();
|
Packet::writePacket();
|
||||||
|
|
||||||
#ifdef DEBUG_STREAMS
|
#ifdef DEBUG_STREAMS
|
||||||
auto devStream = getComDevStream().get();
|
auto devStream = getComDevStream().get();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
memcpy(this->buf + 4, &seq, 1);
|
memcpy(this->buf + 4, &seq, 1);
|
||||||
memcpy(this->buf + 5, data.data(), data.size());
|
memcpy(this->buf + 5, data.data(), data.size());
|
||||||
write(this->buf, HID_RPT_SIZE);
|
write(this->buf, HID_RPT_SIZE);
|
||||||
|
|
||||||
#ifdef DEBUG_STREAMS
|
#ifdef DEBUG_STREAMS
|
||||||
fwrite(this->buf, HID_RPT_SIZE, 1, devStream);
|
fwrite(this->buf, HID_RPT_SIZE, 1, devStream);
|
||||||
|
|
||||||
auto dPStream = getDevPacketStream().get();
|
auto dPStream = getDevPacketStream().get();
|
||||||
|
|
||||||
fprintf(dPStream, "\t\t<table>\n"
|
fprintf(dPStream,
|
||||||
"\t\t\t<thead>\n"
|
"\t\t<table>\n"
|
||||||
"\t\t\t\t<tr>\n"
|
"\t\t\t<thead>\n"
|
||||||
"\t\t\t\t\t<th>CID</th>\n"
|
"\t\t\t\t<tr>\n"
|
||||||
"\t\t\t\t\t<th>SEQ</th>\n"
|
"\t\t\t\t\t<th>CID</th>\n"
|
||||||
"\t\t\t\t\t<th class=\"data\">DATA</th>\n"
|
"\t\t\t\t\t<th>SEQ</th>\n"
|
||||||
"\t\t\t\t</tr>\n"
|
"\t\t\t\t\t<th class=\"data\">DATA</th>\n"
|
||||||
"\t\t\t</thead>\n"
|
"\t\t\t\t</tr>\n"
|
||||||
"\t\t\t<tbody>\n"
|
"\t\t\t</thead>\n"
|
||||||
"\t\t\t\t<tr>\n"
|
"\t\t\t<tbody>\n"
|
||||||
"\t\t\t\t\t<td>0x%08X</td>\n"
|
"\t\t\t\t<tr>\n"
|
||||||
"\t\t\t\t\t<td>%u</td>\n"
|
"\t\t\t\t\t<td>0x%08X</td>\n"
|
||||||
"\t\t\t\t\t<td class=\"data\">", cid, seq);
|
"\t\t\t\t\t<td>%u</td>\n"
|
||||||
|
"\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);
|
||||||
|
|
||||||
fprintf(dPStream, "</td>\n"
|
fprintf(dPStream, "</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 />");
|
"\t\t<br />");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
63
Packet.hpp
63
Packet.hpp
@@ -17,48 +17,45 @@ 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;
|
||||||
};
|
};
|
||||||
|
|||||||
67
Readme.AttestationCertificateGeneration.md
Normal file
67
Readme.AttestationCertificateGeneration.md
Normal 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.
|
||||||
@@ -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.
|
|
||||||
20
Readme.md
20
Readme.md
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
46
Storage.cpp
46
Storage.cpp
@@ -17,44 +17,44 @@ 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;
|
||||||
|
|
||||||
std::string Storage::filename{};
|
std::string Storage::filename{};
|
||||||
std::map<Storage::KeyHandle, Storage::AppParam> Storage::appParams{};
|
std::map<Storage::KeyHandle, Storage::AppParam> Storage::appParams{};
|
||||||
std::map<Storage::KeyHandle, Storage::PrivKey> Storage::privKeys{};
|
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;
|
||||||
ss >> keyHStr >> appStr >> privStr >> pubStr >> keyCStr;
|
ss >> keyHStr >> appStr >> privStr >> pubStr >> keyCStr;
|
||||||
|
|
||||||
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 };
|
||||||
@@ -62,31 +62,29 @@ void Storage::init(const string &dirPrefix)
|
|||||||
Storage::AppParam appParam{};
|
Storage::AppParam appParam{};
|
||||||
b64decode(appStr, appParam);
|
b64decode(appStr, appParam);
|
||||||
|
|
||||||
Storage::PrivKey privKey{};
|
Storage::PrivKey privKey{};
|
||||||
b64decode(privStr, privKey);
|
b64decode(privStr, privKey);
|
||||||
|
|
||||||
Storage::PubKey pubKey{};
|
Storage::PubKey pubKey{};
|
||||||
b64decode(pubStr, pubKey);
|
b64decode(pubStr, pubKey);
|
||||||
|
|
||||||
Storage::appParams[keyH] = appParam;
|
Storage::appParams[keyH] = appParam;
|
||||||
Storage::privKeys[keyH] = privKey;
|
Storage::privKeys[keyH] = privKey;
|
||||||
Storage::pubKeys[keyH] = pubKey;
|
Storage::pubKeys[keyH] = pubKey;
|
||||||
Storage::keyCounts[keyH] = keyC;
|
Storage::keyCounts[keyH] = keyC;
|
||||||
|
|
||||||
lineNumber++;
|
lineNumber++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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];
|
||||||
const auto& pubKey = Storage::pubKeys[keyID];
|
const auto& pubKey = Storage::pubKeys[keyID];
|
||||||
const auto& keyCount = Storage::keyCounts[keyID];
|
const auto& keyCount = Storage::keyCounts[keyID];
|
||||||
|
|
||||||
file << keyID;
|
file << keyID;
|
||||||
|
|||||||
35
Storage.hpp
35
Storage.hpp
@@ -17,30 +17,29 @@ 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;
|
||||||
using AppParam = std::array<uint8_t, 32>;
|
using AppParam = std::array<uint8_t, 32>;
|
||||||
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;
|
||||||
static std::map<KeyHandle, PubKey> pubKeys;
|
static std::map<KeyHandle, PubKey> pubKeys;
|
||||||
static std::map<KeyHandle, KeyCount> keyCounts;
|
static std::map<KeyHandle, KeyCount> keyCounts;
|
||||||
};
|
};
|
||||||
|
|||||||
190
Streams.cpp
190
Streams.cpp
@@ -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) {
|
||||||
|
close(*fd);
|
||||||
descriptor.reset(new int{ open(HID_DEV, O_RDWR | O_NONBLOCK | O_APPEND) }, [](int* fd){
|
delete fd;
|
||||||
close(*fd);
|
} };
|
||||||
delete fd;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (*descriptor == -1)
|
if (*descriptor == -1)
|
||||||
throw runtime_error{ "Descriptor is unavailable" };
|
throw runtime_error{ "Descriptor is unavailable" };
|
||||||
@@ -47,12 +45,11 @@ 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);
|
} };
|
||||||
} };
|
|
||||||
|
|
||||||
if (!stream)
|
if (!stream)
|
||||||
clog << "Stream is unavailable" << endl;
|
clog << "Stream is unavailable" << endl;
|
||||||
@@ -60,12 +57,13 @@ 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"),
|
||||||
clog << "Closing hostPackets stream" << endl;
|
[](FILE* f) {
|
||||||
closeHTML(f);
|
clog << "Closing hostPackets stream" << endl;
|
||||||
} };
|
closeHTML(f);
|
||||||
|
} };
|
||||||
|
|
||||||
if (!stream)
|
if (!stream)
|
||||||
clog << "Stream is unavailable" << endl;
|
clog << "Stream is unavailable" << endl;
|
||||||
@@ -73,12 +71,13 @@ 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"),
|
||||||
clog << "Closing host APDU stream" << endl;
|
[](FILE* f) {
|
||||||
closeHTML(f);
|
clog << "Closing host APDU stream" << endl;
|
||||||
} };
|
closeHTML(f);
|
||||||
|
} };
|
||||||
|
|
||||||
if (!stream)
|
if (!stream)
|
||||||
clog << "Stream is unavailable" << endl;
|
clog << "Stream is unavailable" << endl;
|
||||||
@@ -86,12 +85,11 @@ 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);
|
} };
|
||||||
} };
|
|
||||||
|
|
||||||
if (!stream)
|
if (!stream)
|
||||||
clog << "Stream is unavailable" << endl;
|
clog << "Stream is unavailable" << endl;
|
||||||
@@ -99,12 +97,13 @@ 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"),
|
||||||
clog << "Closing devPackets stream" << endl;
|
[](FILE* f) {
|
||||||
closeHTML(f);
|
clog << "Closing devPackets stream" << endl;
|
||||||
} };
|
closeHTML(f);
|
||||||
|
} };
|
||||||
|
|
||||||
if (!stream)
|
if (!stream)
|
||||||
clog << "Stream is unavailable" << endl;
|
clog << "Stream is unavailable" << endl;
|
||||||
@@ -112,12 +111,12 @@ 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);
|
||||||
} };
|
} };
|
||||||
|
|
||||||
if (!stream)
|
if (!stream)
|
||||||
clog << "Stream is unavailable" << endl;
|
clog << "Stream is unavailable" << endl;
|
||||||
@@ -125,54 +124,57 @@ 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"
|
||||||
"\t\t\ttable {\n"
|
"\t\t\ttable {\n"
|
||||||
"\t\t\t\tdisplay: table;\n"
|
"\t\t\t\tdisplay: table;\n"
|
||||||
"\t\t\t\twidth: 100%%;\n"
|
"\t\t\t\twidth: 100%%;\n"
|
||||||
"\t\t\t\tborder-collapse: collapse;\n"
|
"\t\t\t\tborder-collapse: collapse;\n"
|
||||||
"\t\t\t\tbox-sizing: border-box;\n"
|
"\t\t\t\tbox-sizing: border-box;\n"
|
||||||
"\t\t\t}\n"
|
"\t\t\t}\n"
|
||||||
"\n"
|
"\n"
|
||||||
"\t\t\tth.data {\n"
|
"\t\t\tth.data {\n"
|
||||||
"\t\t\t\ttext-align: left;\n"
|
"\t\t\t\ttext-align: left;\n"
|
||||||
"\t\t\t}\n"
|
"\t\t\t}\n"
|
||||||
"\n"
|
"\n"
|
||||||
"\t\t\ttd {\n"
|
"\t\t\ttd {\n"
|
||||||
"\t\t\t\tfont-family: \"Courier New\", Courier, monospace;\n"
|
"\t\t\t\tfont-family: \"Courier New\", Courier, monospace;\n"
|
||||||
"\t\t\t\twhite-space: pre;\n"
|
"\t\t\t\twhite-space: pre;\n"
|
||||||
"\t\t\t}\n"
|
"\t\t\t}\n"
|
||||||
"\n"
|
"\n"
|
||||||
"\t\t\ttd.data {\n"
|
"\t\t\ttd.data {\n"
|
||||||
"\t\t\t\toverflow: hidden;\n"
|
"\t\t\t\toverflow: hidden;\n"
|
||||||
"\t\t\t\ttext-overflow: ellipsis;\n"
|
"\t\t\t\ttext-overflow: ellipsis;\n"
|
||||||
"\t\t\t\tmax-width:1px;\n"
|
"\t\t\t\tmax-width:1px;\n"
|
||||||
"\t\t\t\twidth:100%%;\n"
|
"\t\t\t\twidth:100%%;\n"
|
||||||
"\t\t\t}\n"
|
"\t\t\t}\n"
|
||||||
"\n"
|
"\n"
|
||||||
"\t\t\ttd.data:hover {\n"
|
"\t\t\ttd.data:hover {\n"
|
||||||
"\t\t\t\twhite-space: pre-wrap;\n"
|
"\t\t\t\twhite-space: pre-wrap;\n"
|
||||||
"\t\t\t}\n"
|
"\t\t\t}\n"
|
||||||
"\n"
|
"\n"
|
||||||
"\t\t\ttable, th, td {\n"
|
"\t\t\ttable, th, td {\n"
|
||||||
"\t\t\t\tborder: 1px solid black;\n"
|
"\t\t\t\tborder: 1px solid black;\n"
|
||||||
"\t\t\t}\n"
|
"\t\t\t}\n"
|
||||||
"\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
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ 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();
|
||||||
|
|
||||||
#ifdef DEBUG_STREAMS
|
#ifdef DEBUG_STREAMS
|
||||||
std::shared_ptr<FILE> getComHostStream();
|
std::shared_ptr<FILE> getComHostStream();
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
116
U2FMessage.cpp
116
U2FMessage.cpp
@@ -17,37 +17,34 @@ 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;
|
static uint32_t cid;
|
||||||
static uint32_t cid;
|
static uint8_t cmd;
|
||||||
static uint8_t cmd;
|
|
||||||
static vector<uint8_t> dataBytes;
|
static vector<uint8_t> dataBytes;
|
||||||
|
|
||||||
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,33 +154,33 @@ 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();
|
||||||
|
|
||||||
fprintf(dAS, "<table>\n"
|
fprintf(dAS, "<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>DATA</th>\n"
|
"\t\t\t\t\t<th>DATA</th>\n"
|
||||||
"\t\t\t\t\t<th>ERR</th>\n"
|
"\t\t\t\t\t<th>ERR</th>\n"
|
||||||
"\t\t\t\t</tr>\n"
|
"\t\t\t\t</tr>\n"
|
||||||
"\t\t\t</thead>\n"
|
"\t\t\t</thead>\n"
|
||||||
"\t\t\t<tbody>\n"
|
"\t\t\t<tbody>\n"
|
||||||
"\t\t\t\t<tr>\n"
|
"\t\t\t\t<tr>\n"
|
||||||
"\t\t\t\t\t<td class=\"data\">");
|
"\t\t\t\t\t<td class=\"data\">");
|
||||||
|
|
||||||
for (size_t i = 0; i < data.size() - 2; i++)
|
for (size_t i = 0; i < data.size() - 2; i++)
|
||||||
fprintf(dAS, "%3u ", data[i]);
|
fprintf(dAS, "%3u ", data[i]);
|
||||||
@@ -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,
|
||||||
"\t\t\t\t\t<td>0x%04X</td>\n"
|
"</td>\n"
|
||||||
"\t\t\t\t</tr>\n"
|
"\t\t\t\t\t<td>0x%04X</td>\n"
|
||||||
"\t\t\t</tbody>\n"
|
"\t\t\t\t</tr>\n"
|
||||||
"\t\t</table>\n"
|
"\t\t\t</tbody>\n"
|
||||||
"\t\t<br />", err);
|
"\t\t</table>\n"
|
||||||
|
"\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;
|
||||||
|
|||||||
@@ -18,21 +18,18 @@ 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 {
|
||||||
{
|
uint32_t cid;
|
||||||
public:
|
uint8_t cmd;
|
||||||
uint32_t cid;
|
std::vector<uint8_t> data;
|
||||||
uint8_t cmd;
|
|
||||||
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();
|
||||||
void write();
|
void write();
|
||||||
static void error(const uint32_t tCID, const uint8_t tErr);
|
static void error(const uint32_t tCID, const uint8_t tErr);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,33 +17,29 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
copy(data.begin() + 0, data.begin() + 32, challengeP.begin());
|
copy(data.begin() + 0, data.begin() + 32, challengeP.begin());
|
||||||
copy(data.begin() + 32, data.begin() + 64, appParam.begin());
|
copy(data.begin() + 32, data.begin() + 64, appParam.begin());
|
||||||
|
|
||||||
uint8_t keyHLen = data[64];
|
uint8_t keyHLen = data[64];
|
||||||
@@ -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,8 +87,15 @@ 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++;
|
||||||
|
|
||||||
response.push_back(0x01);
|
response.push_back(0x01);
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
17
U2F_CMD.cpp
17
U2F_CMD.cpp
@@ -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 {};
|
||||||
}
|
}
|
||||||
|
|||||||
20
U2F_CMD.hpp
20
U2F_CMD.hpp
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
174
U2F_Msg_CMD.cpp
174
U2F_Msg_CMD.cpp
@@ -18,119 +18,104 @@ 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" };
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.ins = dat[1];
|
cmd.ins = dat[1];
|
||||||
cmd.p1 = dat[2];
|
cmd.p1 = dat[2];
|
||||||
cmd.p2 = dat[3];
|
cmd.p2 = dat[3];
|
||||||
|
|
||||||
vector<uint8_t> data{ dat.begin() + 4, dat.end() };
|
vector<uint8_t> data{ dat.begin() + 4, dat.end() };
|
||||||
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,42 +126,44 @@ 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,
|
||||||
"\t\t\t<thead>\n"
|
"<table>\n"
|
||||||
"\t\t\t\t<tr>\n"
|
"\t\t\t<thead>\n"
|
||||||
"\t\t\t\t\t<th>CLA</th>\n"
|
"\t\t\t\t<tr>\n"
|
||||||
"\t\t\t\t\t<th>INS</th>\n"
|
"\t\t\t\t\t<th>CLA</th>\n"
|
||||||
"\t\t\t\t\t<th>P1</th>\n"
|
"\t\t\t\t\t<th>INS</th>\n"
|
||||||
"\t\t\t\t\t<th>P2</th>\n"
|
"\t\t\t\t\t<th>P1</th>\n"
|
||||||
"\t\t\t\t\t<th>Lc</th>\n"
|
"\t\t\t\t\t<th>P2</th>\n"
|
||||||
"\t\t\t\t\t<th>Data</th>\n"
|
"\t\t\t\t\t<th>Lc</th>\n"
|
||||||
"\t\t\t\t\t<th>Le</th>\n"
|
"\t\t\t\t\t<th>Data</th>\n"
|
||||||
"\t\t\t\t</tr>\n"
|
"\t\t\t\t\t<th>Le</th>\n"
|
||||||
"\t\t\t</thead>\n"
|
"\t\t\t\t</tr>\n"
|
||||||
"\t\t\t<tbody>\n"
|
"\t\t\t</thead>\n"
|
||||||
"\t\t\t\t<tr>\n"
|
"\t\t\t<tbody>\n"
|
||||||
"\t\t\t\t\t<td>0x%02X</td>\n"
|
"\t\t\t\t<tr>\n"
|
||||||
"\t\t\t\t\t<td>0x%02X</td>\n"
|
"\t\t\t\t\t<td>0x%02X</td>\n"
|
||||||
"\t\t\t\t\t<td>%u</td>\n"
|
"\t\t\t\t\t<td>0x%02X</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>%u</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>%3u</td>\n"
|
||||||
|
"\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,
|
||||||
"\t\t\t\t\t<td>%5u</td>\n"
|
"</td>\n"
|
||||||
"\t\t\t\t</tr>\n"
|
"\t\t\t\t\t<td>%5u</td>\n"
|
||||||
"\t\t\t</tbody>\n"
|
"\t\t\t\t</tr>\n"
|
||||||
"\t\t</table>\n"
|
"\t\t\t</tbody>\n"
|
||||||
"\t\t<br />", cmd.le);
|
"\t\t</table>\n"
|
||||||
|
"\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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,27 +20,26 @@ 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;
|
uint8_t p2;
|
||||||
uint8_t p2;
|
|
||||||
uint32_t lc;
|
uint32_t lc;
|
||||||
uint32_t le;
|
uint32_t le;
|
||||||
|
|
||||||
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,73 +17,69 @@ 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;
|
||||||
|
|
||||||
auto& response = m.data;
|
auto& response = m.data;
|
||||||
const auto appParam = Storage::appParams[this->keyH];
|
const auto appParam = Storage::appParams[this->keyH];
|
||||||
const auto pubKey = Storage::pubKeys[this->keyH];
|
const auto pubKey = Storage::pubKeys[this->keyH];
|
||||||
|
|
||||||
response.push_back(0x05);
|
response.push_back(0x05);
|
||||||
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);
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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{};
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
|
||||||
};
|
};
|
||||||
|
|||||||
35
main.cpp
35
main.cpp
@@ -1,38 +1,33 @@
|
|||||||
#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);
|
|
||||||
|
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
#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;
|
||||||
}
|
}
|
||||||
|
|||||||
131
u2f.hpp
131
u2f.hpp
@@ -5,13 +5,13 @@
|
|||||||
#ifndef __U2FHID_H_INCLUDED__
|
#ifndef __U2FHID_H_INCLUDED__
|
||||||
#define __U2FHID_H_INCLUDED__
|
#define __U2FHID_H_INCLUDED__
|
||||||
|
|
||||||
#ifdef _MSC_VER // Windows
|
#ifdef _MSC_VER // Windows
|
||||||
typedef unsigned char uint8_t;
|
typedef unsigned char uint8_t;
|
||||||
typedef unsigned short uint16_t;
|
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
|
||||||
@@ -20,108 +20,107 @@ extern "C" {
|
|||||||
|
|
||||||
// Size of HID reports
|
// Size of HID reports
|
||||||
|
|
||||||
#define HID_RPT_SIZE 64 // Default size of raw HID report
|
#define HID_RPT_SIZE 64 // Default size of raw HID report
|
||||||
|
|
||||||
// Frame layout - command- and continuation frames
|
// Frame layout - command- and continuation frames
|
||||||
|
|
||||||
#define CID_BROADCAST 0xffffffff // Broadcast channel id
|
#define CID_BROADCAST 0xffffffff // Broadcast channel id
|
||||||
|
|
||||||
#define TYPE_MASK 0x80 // Frame type mask
|
#define TYPE_MASK 0x80 // Frame type mask
|
||||||
#define TYPE_INIT 0x80 // Initial frame identifier
|
#define TYPE_INIT 0x80 // Initial frame identifier
|
||||||
#define TYPE_CONT 0x00 // Continuation frame identifier
|
#define TYPE_CONT 0x00 // Continuation frame identifier
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t cid; // Channel identifier
|
uint32_t cid; // Channel identifier
|
||||||
union {
|
union {
|
||||||
uint8_t type; // Frame type - b7 defines type
|
uint8_t type; // Frame type - b7 defines type
|
||||||
struct {
|
struct {
|
||||||
uint8_t cmd; // Command - b7 set
|
uint8_t cmd; // Command - b7 set
|
||||||
uint8_t bcnth; // Message byte count - high part
|
uint8_t bcnth; // Message byte count - high part
|
||||||
uint8_t bcntl; // Message byte count - low part
|
uint8_t bcntl; // Message byte count - low part
|
||||||
uint8_t data[HID_RPT_SIZE - 7]; // Data payload
|
uint8_t data[HID_RPT_SIZE - 7]; // Data payload
|
||||||
} init;
|
} init;
|
||||||
struct {
|
struct {
|
||||||
uint8_t seq; // Sequence number - b7 cleared
|
uint8_t seq; // Sequence number - b7 cleared
|
||||||
uint8_t data[HID_RPT_SIZE - 5]; // Data payload
|
uint8_t data[HID_RPT_SIZE - 5]; // Data payload
|
||||||
} cont;
|
} cont;
|
||||||
};
|
};
|
||||||
} U2FHID_FRAME;
|
} U2FHID_FRAME;
|
||||||
|
|
||||||
#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
|
||||||
|
|
||||||
#define FIDO_USAGE_PAGE 0xf1d0 // FIDO alliance HID usage page
|
#define FIDO_USAGE_PAGE 0xf1d0 // FIDO alliance HID usage page
|
||||||
#define FIDO_USAGE_U2FHID 0x01 // U2FHID usage for top-level collection
|
#define FIDO_USAGE_U2FHID 0x01 // U2FHID usage for top-level collection
|
||||||
#define FIDO_USAGE_DATA_IN 0x20 // Raw IN data report
|
#define FIDO_USAGE_DATA_IN 0x20 // Raw IN data report
|
||||||
#define FIDO_USAGE_DATA_OUT 0x21 // Raw OUT data report
|
#define FIDO_USAGE_DATA_OUT 0x21 // Raw OUT data report
|
||||||
|
|
||||||
// General constants
|
// General constants
|
||||||
|
|
||||||
#define U2FHID_IF_VERSION 2 // Current interface implementation version
|
#define U2FHID_IF_VERSION 2 // Current interface implementation version
|
||||||
#define U2FHID_TRANS_TIMEOUT 3000 // Default message timeout in ms
|
#define U2FHID_TRANS_TIMEOUT 3000 // Default message timeout in ms
|
||||||
|
|
||||||
// U2FHID native commands
|
// U2FHID native commands
|
||||||
|
|
||||||
#define U2FHID_PING (TYPE_INIT | 0x01) // Echo data through local processor only
|
#define U2FHID_PING (TYPE_INIT | 0x01) // Echo data through local processor only
|
||||||
#define U2FHID_MSG (TYPE_INIT | 0x03) // Send U2F message frame
|
#define U2FHID_MSG (TYPE_INIT | 0x03) // Send U2F message frame
|
||||||
#define U2FHID_LOCK (TYPE_INIT | 0x04) // Send lock channel command
|
#define U2FHID_LOCK (TYPE_INIT | 0x04) // Send lock channel command
|
||||||
#define U2FHID_INIT (TYPE_INIT | 0x06) // Channel initialization
|
#define U2FHID_INIT (TYPE_INIT | 0x06) // Channel initialization
|
||||||
#define U2FHID_WINK (TYPE_INIT | 0x08) // Send device identification wink
|
#define U2FHID_WINK (TYPE_INIT | 0x08) // Send device identification wink
|
||||||
#define U2FHID_SYNC (TYPE_INIT | 0x3c) // Protocol resync command
|
#define U2FHID_SYNC (TYPE_INIT | 0x3c) // Protocol resync command
|
||||||
#define U2FHID_ERROR (TYPE_INIT | 0x3f) // Error response
|
#define U2FHID_ERROR (TYPE_INIT | 0x3f) // Error response
|
||||||
|
|
||||||
#define U2FHID_VENDOR_FIRST (TYPE_INIT | 0x40) // First vendor defined command
|
#define U2FHID_VENDOR_FIRST (TYPE_INIT | 0x40) // First vendor defined command
|
||||||
#define U2FHID_VENDOR_LAST (TYPE_INIT | 0x7f) // Last vendor defined command
|
#define U2FHID_VENDOR_LAST (TYPE_INIT | 0x7f) // Last vendor defined command
|
||||||
|
|
||||||
// U2FHID_INIT command defines
|
// U2FHID_INIT command defines
|
||||||
|
|
||||||
#define INIT_NONCE_SIZE 8 // Size of channel initialization challenge
|
#define INIT_NONCE_SIZE 8 // Size of channel initialization challenge
|
||||||
#define CAPFLAG_WINK 0x01 // Device supports WINK command
|
#define CAPFLAG_WINK 0x01 // Device supports WINK command
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t nonce[INIT_NONCE_SIZE]; // Client application nonce
|
uint8_t nonce[INIT_NONCE_SIZE]; // Client application nonce
|
||||||
} U2FHID_INIT_REQ;
|
} U2FHID_INIT_REQ;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t nonce[INIT_NONCE_SIZE]; // Client application nonce
|
uint8_t nonce[INIT_NONCE_SIZE]; // Client application nonce
|
||||||
uint32_t cid; // Channel identifier
|
uint32_t cid; // Channel identifier
|
||||||
uint8_t versionInterface; // Interface version
|
uint8_t versionInterface; // Interface version
|
||||||
uint8_t versionMajor; // Major version number
|
uint8_t versionMajor; // Major version number
|
||||||
uint8_t versionMinor; // Minor version number
|
uint8_t versionMinor; // Minor version number
|
||||||
uint8_t versionBuild; // Build version number
|
uint8_t versionBuild; // Build version number
|
||||||
uint8_t capFlags; // Capabilities flags
|
uint8_t capFlags; // Capabilities flags
|
||||||
} U2FHID_INIT_RESP;
|
} U2FHID_INIT_RESP;
|
||||||
|
|
||||||
// U2FHID_SYNC command defines
|
// U2FHID_SYNC command defines
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t nonce; // Client application nonce
|
uint8_t nonce; // Client application nonce
|
||||||
} U2FHID_SYNC_REQ;
|
} U2FHID_SYNC_REQ;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t nonce; // Client application nonce
|
uint8_t nonce; // Client application nonce
|
||||||
} U2FHID_SYNC_RESP;
|
} U2FHID_SYNC_RESP;
|
||||||
|
|
||||||
// Low-level error codes. Return as negatives.
|
// Low-level error codes. Return as negatives.
|
||||||
|
|
||||||
#define ERR_NONE 0x00 // No error
|
#define ERR_NONE 0x00 // No error
|
||||||
#define ERR_INVALID_CMD 0x01 // Invalid command
|
#define ERR_INVALID_CMD 0x01 // Invalid command
|
||||||
#define ERR_INVALID_PAR 0x02 // Invalid parameter
|
#define ERR_INVALID_PAR 0x02 // Invalid parameter
|
||||||
#define ERR_INVALID_LEN 0x03 // Invalid message length
|
#define ERR_INVALID_LEN 0x03 // Invalid message length
|
||||||
#define ERR_INVALID_SEQ 0x04 // Invalid message sequencing
|
#define ERR_INVALID_SEQ 0x04 // Invalid message sequencing
|
||||||
#define ERR_MSG_TIMEOUT 0x05 // Message has timed out
|
#define ERR_MSG_TIMEOUT 0x05 // Message has timed out
|
||||||
#define ERR_CHANNEL_BUSY 0x06 // Channel busy
|
#define ERR_CHANNEL_BUSY 0x06 // Channel busy
|
||||||
#define ERR_LOCK_REQUIRED 0x0a // Command requires channel lock
|
#define ERR_LOCK_REQUIRED 0x0a // Command requires channel lock
|
||||||
#define ERR_SYNC_FAIL 0x0b // SYNC command failed
|
#define ERR_SYNC_FAIL 0x0b // SYNC command failed
|
||||||
#define ERR_OTHER 0x7f // Other unspecified error
|
#define ERR_OTHER 0x7f // Other unspecified error
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // __U2FHID_H_INCLUDED__
|
#endif // __U2FHID_H_INCLUDED__
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user