Reformatting.

This commit is contained in:
2019-08-23 13:27:30 +01:00
parent 03dba21ad4
commit 01dad2ee0a
42 changed files with 859 additions and 987 deletions

View File

@@ -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,
}; };

View File

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

View File

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

View File

@@ -18,73 +18,57 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Certificates.hpp" #include "Certificates.hpp"
//You may not actually want to use these values - // You may not actually want to use these values -
// having been shared publicly, these may be vulnerable to exploits. // having been shared publicly, these may be vulnerable to exploits.
// However, generating your own attestation certificate makes your device // However, generating your own attestation certificate makes your device
// uniquely identifiable across platforms / services, etc. // uniquely identifiable across platforms / services, etc.
// You can generate your own using the method detailed in the README. // You can generate your own using the method detailed in the README.
uint8_t attestCert[] = { uint8_t attestCert[] = {
0x30, 0x82, 0x02, 0x29, 0x30, 0x82, 0x01, 0xd0, 0xa0, 0x03, 0x02, 0x01, 0x30, 0x82, 0x02, 0x29, 0x30, 0x82, 0x01, 0xd0, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00,
0x02, 0x02, 0x09, 0x00, 0x8a, 0xe2, 0x21, 0x3f, 0x2f, 0x8b, 0x72, 0x52, 0x8a, 0xe2, 0x21, 0x3f, 0x2f, 0x8b, 0x72, 0x52, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce,
0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x70, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
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,
0x02, 0x55, 0x4b, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04,
0x0c, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x0a, 0x0c, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67,
0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x49, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x31, 0x29, 0x30, 0x27, 0x06,
0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x20, 0x55, 0x32, 0x46, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x74, 0x50,
0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x31, 0x46, 0x53, 0x71, 0x54, 0x71, 0x6f, 0x5a, 0x6d, 0x62, 0x37, 0x38, 0x61, 0x6a, 0x6f, 0x2f, 0x75,
0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x20, 0x55, 0x32, 0x58, 0x50, 0x73, 0x51, 0x3d, 0x3d, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x36, 0x33, 0x30,
0x46, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x74, 0x50, 0x46, 0x53, 0x71, 0x54, 0x31, 0x39, 0x30, 0x37, 0x35, 0x31, 0x5a, 0x17, 0x0d, 0x32, 0x38, 0x30, 0x36, 0x32, 0x37, 0x31,
0x71, 0x6f, 0x5a, 0x6d, 0x62, 0x37, 0x38, 0x61, 0x6a, 0x6f, 0x2f, 0x75, 0x39, 0x30, 0x37, 0x35, 0x31, 0x5a, 0x30, 0x70, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
0x58, 0x50, 0x73, 0x51, 0x3d, 0x3d, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x38, 0x06, 0x13, 0x02, 0x55, 0x4b, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a,
0x30, 0x36, 0x33, 0x30, 0x31, 0x39, 0x30, 0x37, 0x35, 0x31, 0x5a, 0x17, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03,
0x0d, 0x32, 0x38, 0x30, 0x36, 0x32, 0x37, 0x31, 0x39, 0x30, 0x37, 0x35, 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69,
0x31, 0x5a, 0x30, 0x70, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x31, 0x29, 0x30,
0x06, 0x13, 0x02, 0x55, 0x4b, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x20, 0x55, 0x32, 0x46, 0x20, 0x4b, 0x65, 0x79, 0x20,
0x04, 0x08, 0x0c, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x50, 0x46, 0x53, 0x71, 0x54, 0x71, 0x6f, 0x5a, 0x6d, 0x62, 0x37, 0x38, 0x61, 0x6a, 0x6f,
0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x2f, 0x75, 0x58, 0x50, 0x73, 0x51, 0x3d, 0x3d, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x42, 0x00, 0x04, 0x32, 0x41, 0xc3, 0xb8, 0x96, 0x97, 0xd8, 0x90, 0x66, 0x41, 0x88, 0x96, 0xd4,
0x64, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x20, 0x73, 0xb6, 0x37, 0xf7, 0x85, 0x29, 0xaf, 0x3b, 0x15, 0x0f, 0x83, 0x61, 0x67, 0xea, 0xc9, 0xb2,
0x55, 0x32, 0x46, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x74, 0x50, 0x46, 0x53, 0xdb, 0x82, 0xb3, 0x2c, 0x99, 0x60, 0x8a, 0x98, 0x7c, 0xd4, 0x04, 0xa0, 0x92, 0x22, 0x05, 0xaa,
0x71, 0x54, 0x71, 0x6f, 0x5a, 0x6d, 0x62, 0x37, 0x38, 0x61, 0x6a, 0x6f, 0xf7, 0x7a, 0x91, 0x02, 0x03, 0xdd, 0x15, 0x88, 0x87, 0x6a, 0x26, 0xe9, 0xee, 0xcf, 0x99, 0xb1,
0x2f, 0x75, 0x58, 0x50, 0x73, 0x51, 0x3d, 0x3d, 0x30, 0x59, 0x30, 0x13, 0x66, 0xc0, 0x01, 0xa3, 0x53, 0x30, 0x51, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x04, 0x14, 0xcf, 0x7f, 0xfa, 0x7d, 0xc4, 0x8d, 0xba, 0x60, 0x52, 0x4c, 0xb6, 0x16, 0x2e, 0x88,
0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x32, 0x62, 0xc7, 0x8c, 0xfc, 0xe0, 0x63, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30,
0x41, 0xc3, 0xb8, 0x96, 0x97, 0xd8, 0x90, 0x66, 0x41, 0x88, 0x96, 0xd4, 0x16, 0x80, 0x14, 0xcf, 0x7f, 0xfa, 0x7d, 0xc4, 0x8d, 0xba, 0x60, 0x52, 0x4c, 0xb6, 0x16, 0x2e,
0x73, 0xb6, 0x37, 0xf7, 0x85, 0x29, 0xaf, 0x3b, 0x15, 0x0f, 0x83, 0x61, 0x88, 0x62, 0xc7, 0x8c, 0xfc, 0xe0, 0x63, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
0x67, 0xea, 0xc9, 0xb2, 0xdb, 0x82, 0xb3, 0x2c, 0x99, 0x60, 0x8a, 0x98, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce,
0x7c, 0xd4, 0x04, 0xa0, 0x92, 0x22, 0x05, 0xaa, 0xf7, 0x7a, 0x91, 0x02, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x47, 0x00, 0x30, 0x44, 0x02, 0x20, 0x72, 0x25, 0x89, 0xc1, 0x32,
0x03, 0xdd, 0x15, 0x88, 0x87, 0x6a, 0x26, 0xe9, 0xee, 0xcf, 0x99, 0xb1, 0x54, 0x66, 0xf8, 0x0e, 0x58, 0x77, 0xe3, 0xb5, 0x62, 0x47, 0x33, 0x18, 0x5a, 0xdc, 0x28, 0x6a,
0x66, 0xc0, 0x01, 0xa3, 0x53, 0x30, 0x51, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x4a, 0x56, 0xcb, 0x58, 0x63, 0xe3, 0xa1, 0x02, 0x6a, 0xf0, 0xd8, 0x02, 0x20, 0x65, 0x26, 0x84,
0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xcf, 0x7f, 0xfa, 0x7d, 0xc4, 0x8d, 0x7c, 0xc3, 0x3b, 0x7d, 0x6a, 0x22, 0x0c, 0x22, 0x3d, 0xc8, 0x43, 0xb7, 0x84, 0x8b, 0x7b, 0x48,
0xba, 0x60, 0x52, 0x4c, 0xb6, 0x16, 0x2e, 0x88, 0x62, 0xc7, 0x8c, 0xfc, 0x23, 0xb0, 0x1e, 0x13, 0x35, 0x1d, 0x1a, 0x90, 0x44, 0x62, 0x6c, 0xab, 0x9b
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[] = { uint8_t attestPrivKey[] = { 0x7e, 0xbd, 0x91, 0x05, 0x5a, 0x80, 0x9f, 0x36, 0xe5, 0x2f, 0xe0,
0x7e, 0xbd, 0x91, 0x05, 0x5a, 0x80, 0x9f, 0x36, 0xe5, 0x2f, 0xe0, 0xd0, 0xd0, 0xa9, 0x63, 0x0c, 0x86, 0x04, 0xb1, 0x04, 0xe3, 0xd1, 0xfb,
0xa9, 0x63, 0x0c, 0x86, 0x04, 0xb1, 0x04, 0xe3, 0xd1, 0xfb, 0xd0, 0x83, 0xd0, 0x83, 0xc7, 0x2e, 0x2f, 0x34, 0xb6, 0xd6, 0xa4, 0xb2 };
0xc7, 0x2e, 0x2f, 0x34, 0xb6, 0xd6, 0xa4, 0xb2
};
uint8_t attestPubKey[] = { uint8_t attestPubKey[] = { 0x04, 0x32, 0x41, 0xc3, 0xb8, 0x96, 0x97, 0xd8, 0x90, 0x66, 0x41,
0x04, 0x32, 0x41, 0xc3, 0xb8, 0x96, 0x97, 0xd8, 0x90, 0x66, 0x41, 0x88, 0x88, 0x96, 0xd4, 0x73, 0xb6, 0x37, 0xf7, 0x85, 0x29, 0xaf, 0x3b,
0x96, 0xd4, 0x73, 0xb6, 0x37, 0xf7, 0x85, 0x29, 0xaf, 0x3b, 0x15, 0x0f, 0x15, 0x0f, 0x83, 0x61, 0x67, 0xea, 0xc9, 0xb2, 0xdb, 0x82, 0xb3,
0x83, 0x61, 0x67, 0xea, 0xc9, 0xb2, 0xdb, 0x82, 0xb3, 0x2c, 0x99, 0x60, 0x2c, 0x99, 0x60, 0x8a, 0x98, 0x7c, 0xd4, 0x04, 0xa0, 0x92, 0x22,
0x8a, 0x98, 0x7c, 0xd4, 0x04, 0xa0, 0x92, 0x22, 0x05, 0xaa, 0xf7, 0x7a, 0x05, 0xaa, 0xf7, 0x7a, 0x91, 0x02, 0x03, 0xdd, 0x15, 0x88, 0x87,
0x91, 0x02, 0x03, 0xdd, 0x15, 0x88, 0x87, 0x6a, 0x26, 0xe9, 0xee, 0xcf, 0x6a, 0x26, 0xe9, 0xee, 0xcf, 0x99, 0xb1, 0x66, 0xc0, 0x01 };
0x99, 0xb1, 0x66, 0xc0, 0x01
};

View File

@@ -17,24 +17,23 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "Channel.hpp" #include "Channel.hpp"
#include <stdexcept>
#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 U2FMessage& uMsg) void Channel::handle(const 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)
@@ -51,12 +50,10 @@ void Channel::handle(const U2FMessage& uMsg)
return cmd->respond(this->cid); return cmd->respond(this->cid);
} }
void Channel::init(const ChannelInitState newInitState) void Channel::init(const ChannelInitState newInitState) {
{
this->initState = newInitState; this->initState = newInitState;
} }
void Channel::lock(const ChannelLockedState newLockedState) void Channel::lock(const ChannelLockedState newLockedState) {
{
this->lockedState = newLockedState; this->lockedState = newLockedState;
} }

View File

@@ -17,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 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 U2FMessage& uMsg);
uint32_t getCID() const;
void init(const ChannelInitState newInitState);
void lock(const ChannelLockedState newLockedState);
}; };

View File

@@ -17,19 +17,16 @@ 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() {
{
auto msg = U2FMessage::readNonBlock(); auto msg = U2FMessage::readNonBlock();
if (!msg) if (!msg)
@@ -38,33 +35,28 @@ void Controller::handleTransaction()
handleTransaction(*msg); handleTransaction(*msg);
} }
void Controller::handleTransaction(const U2FMessage& msg) void Controller::handleTransaction(const U2FMessage& msg) {
{ 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)
{}
lastMessage = chrono::system_clock::now(); lastMessage = chrono::system_clock::now();
auto opChannel = msg.cid; auto opChannel = msg.cid;
if (msg.cmd == U2FHID_INIT) if (msg.cmd == U2FHID_INIT) {
{
opChannel = nextChannel(); opChannel = nextChannel();
auto channel = Channel{ opChannel }; auto channel = Channel{ opChannel };
try try {
{ channels.emplace(opChannel, channel); // In case of wrap-around replace existing one
channels.emplace(opChannel, channel); //In case of wrap-around replace existing one } catch (...) {
}
catch (...)
{
channels.insert(make_pair(opChannel, channel)); channels.insert(make_pair(opChannel, channel));
} }
} }
@@ -77,8 +69,7 @@ void Controller::handleTransaction(const U2FMessage& msg)
channels.at(opChannel).handle(msg); channels.at(opChannel).handle(msg);
} }
uint32_t Controller::nextChannel() uint32_t Controller::nextChannel() {
{
do do
currChannel++; currChannel++;
while (currChannel == 0xFFFFFFFF || currChannel == 0); while (currChannel == 0xFFFFFFFF || currChannel == 0);

View File

@@ -17,21 +17,20 @@ 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();
void handleTransaction(const U2FMessage& msg); void handleTransaction(const U2FMessage& msg);
uint32_t nextChannel(); uint32_t nextChannel();
}; };

View File

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

View File

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

74
IO.cpp
View File

@@ -17,24 +17,24 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "IO.hpp" #include "IO.hpp"
#include "Streams.hpp"
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctime>
#include <chrono>
#include <ratio>
#include <array>
#include <string>
#include <stdexcept>
#include <memory>
#include <cstdio>
#include <android/log.h>
#include "u2f.hpp"
#include "Macro.hpp" #include "Macro.hpp"
#include "Streams.hpp"
#include "U2FDevice.hpp" #include "U2FDevice.hpp"
#include "u2f.hpp"
#include <android/log.h>
#include <array>
#include <chrono>
#include <cstdio>
#include <ctime>
#include <fcntl.h>
#include <iostream>
#include <memory>
#include <ratio>
#include <stdexcept>
#include <string>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
using namespace std; using namespace std;
@@ -45,15 +45,14 @@ string binaryDirectory{};
string cacheDirectory{ DEBUG_STREAMS }; string cacheDirectory{ DEBUG_STREAMS };
// Thanks to https://stackoverflow.com/a/478960 // Thanks to https://stackoverflow.com/a/478960
vector<uint8_t> execOutput(const string &cmd); vector<uint8_t> execOutput(const string& cmd);
void execInput(const string &cmd, const vector<uint8_t> &stdinData); void execInput(const string& cmd, const vector<uint8_t>& stdinData);
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);
@@ -63,20 +62,17 @@ vector<uint8_t> readNonBlock(const size_t count)
return bytes; return bytes;
} }
void write(const vector<uint8_t> &bytes) void write(const vector<uint8_t>& bytes) {
{ __android_log_print(ANDROID_LOG_DEBUG, "U2FAndroid", "Writing %zu bytes", bytes.size());
__android_log_print(ANDROID_LOG_DEBUG, "U2FAndroid", "Writing %zu bytes", bytes.size());
execInput("su -c \"" + binaryDirectory + "/U2FAndroid_Write\"", bytes); execInput("su -c \"" + binaryDirectory + "/U2FAndroid_Write\"", bytes);
} }
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, 10000000 }; const timespec iterDelay{ 0, 10000000 };
chrono::duration<double, milli> delay{ 0 }; chrono::duration<double, milli> delay{ 0 };
while (delay.count() < U2FHID_TRANS_TIMEOUT) while (delay.count() < U2FHID_TRANS_TIMEOUT) {
{
if (getBuffer().size() >= count) { if (getBuffer().size() >= count) {
#ifdef DEBUG_MSGS #ifdef DEBUG_MSGS
clog << "Requested " << count << " bytes" << endl; clog << "Requested " << count << " bytes" << endl;
@@ -84,7 +80,7 @@ bool bytesAvailable(const size_t count)
return true; return true;
} }
nanosleep(&iterDelay, nullptr); nanosleep(&iterDelay, nullptr);
delay = chrono::high_resolution_clock::now() - startTime; delay = chrono::high_resolution_clock::now() - startTime;
} }
#ifdef DEBUG_MSGS #ifdef DEBUG_MSGS
@@ -94,20 +90,18 @@ 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();
vector<uint8_t> bytes = execOutput("su -c \"" + binaryDirectory + "/U2FAndroid_Read\""); vector<uint8_t> bytes = execOutput("su -c \"" + binaryDirectory + "/U2FAndroid_Read\"");
if (!bytes.empty()) if (!bytes.empty()) {
{ __android_log_print(ANDROID_LOG_DEBUG, "U2FAndroid", "Reading bytes: got %zu",
__android_log_print(ANDROID_LOG_DEBUG, "U2FAndroid", "Reading bytes: got %zu", bytes.size()); bytes.size());
buff.insert(buff.end(), bytes.begin(), bytes.end()); buff.insert(buff.end(), bytes.begin(), bytes.end());
#ifdef DEBUG_STREAMS #ifdef DEBUG_STREAMS
@@ -118,8 +112,7 @@ vector<uint8_t>& getBuffer()
return buff; return buff;
} }
vector<uint8_t> execOutput(const string &cmd) vector<uint8_t> execOutput(const string& cmd) {
{
// NOLINT(hicpp-member-init) // NOLINT(hicpp-member-init)
array<char, HID_RPT_SIZE> buffer; array<char, HID_RPT_SIZE> buffer;
vector<uint8_t> result{}; vector<uint8_t> result{};
@@ -133,8 +126,7 @@ vector<uint8_t> execOutput(const string &cmd)
return result; return result;
} }
void execInput(const string &cmd, const vector<uint8_t> &stdinData) void execInput(const string& cmd, const vector<uint8_t>& stdinData) {
{
assert(stdinData.size() % HID_RPT_SIZE == 0); assert(stdinData.size() % HID_RPT_SIZE == 0);
size_t writtenBytes = 0; size_t writtenBytes = 0;

6
IO.hpp
View File

@@ -24,9 +24,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
extern std::string binaryDirectory; extern std::string binaryDirectory;
extern std::string cacheDirectory; extern std::string cacheDirectory;
//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 std::vector<uint8_t>& data); void write(const std::vector<uint8_t>& data);

15
LED.cpp
View File

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

View File

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

View File

@@ -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(vector<uint8_t>{ this->buf, this->buf + sizeof(this->buf) }); write(vector<uint8_t>{ this->buf, 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(vector<uint8_t>{this->buf, this->buf + HID_RPT_SIZE }); write(vector<uint8_t>{ this->buf, 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
} }

View File

@@ -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;
}; };

View File

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

View File

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

View File

@@ -17,50 +17,49 @@ 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 };
init(file); init(file);
} }
void Storage::init(std::istream &inputStream) void Storage::init(std::istream& inputStream) {
{
string line; string line;
size_t lineNumber = 0; size_t lineNumber = 0;
while (getline(inputStream, line)) while (getline(inputStream, 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 };
@@ -68,35 +67,32 @@ void Storage::init(std::istream &inputStream)
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 };
Storage::save(file); Storage::save(file);
} }
void Storage::save(ostream &outputStream) void Storage::save(ostream& outputStream) {
{ for (auto& keypair : Storage::appParams) {
for (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];
outputStream << keyID; outputStream << keyID;

View File

@@ -17,32 +17,31 @@ 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 init(std::istream &inputStream); static void init(std::istream& inputStream);
static void save(); static void save();
static void save(std::ostream &outputStream); static void save(std::ostream& outputStream);
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;
}; };

View File

@@ -18,27 +18,26 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Streams.hpp" #include "Streams.hpp"
#include "IO.hpp" #include "IO.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{}; static shared_ptr<int> descriptor{};
descriptor.reset(new int{ open(HID_DEV, O_RDWR | O_NONBLOCK | O_APPEND) }, [](int* fd){ descriptor.reset(new int{ open(HID_DEV, O_RDWR | O_NONBLOCK | O_APPEND) }, [](int* fd) {
close(*fd); close(*fd);
delete fd; delete fd;
}); });
if (*descriptor == -1) if (*descriptor == -1)
@@ -48,12 +47,12 @@ shared_ptr<int> getHostDescriptor()
} }
#ifdef DEBUG_STREAMS #ifdef DEBUG_STREAMS
shared_ptr<FILE> getComHostStream() shared_ptr<FILE> getComHostStream() {
{ static shared_ptr<FILE> stream{ fopen((cacheDirectory + "comhost.txt").c_str(), "wb"),
static shared_ptr<FILE> stream{ fopen((cacheDirectory + "comhost.txt").c_str(), "wb"), [](FILE *f){ [](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;
@@ -61,12 +60,14 @@ shared_ptr<FILE> getComHostStream()
return stream; return stream;
} }
shared_ptr<FILE> getHostPacketStream() shared_ptr<FILE> getHostPacketStream() {
{ static shared_ptr<FILE> stream{
static shared_ptr<FILE> stream{ initHTML(fopen((cacheDirectory + "hostpackets.html").c_str(), "wb"), "Host Packets"), [](FILE *f){ initHTML(fopen((cacheDirectory + "hostpackets.html").c_str(), "wb"), "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;
@@ -74,12 +75,14 @@ shared_ptr<FILE> getHostPacketStream()
return stream; return stream;
} }
shared_ptr<FILE> getHostAPDUStream() shared_ptr<FILE> getHostAPDUStream() {
{ static shared_ptr<FILE> stream{
static shared_ptr<FILE> stream{ initHTML(fopen((cacheDirectory + "hostAPDU.html").c_str(), "wb"), "Host APDU"), [](FILE *f){ initHTML(fopen((cacheDirectory + "hostAPDU.html").c_str(), "wb"), "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;
@@ -87,12 +90,12 @@ shared_ptr<FILE> getHostAPDUStream()
return stream; return stream;
} }
shared_ptr<FILE> getComDevStream() shared_ptr<FILE> getComDevStream() {
{ static shared_ptr<FILE> stream{ fopen((cacheDirectory + "comdev.txt").c_str(), "wb"),
static shared_ptr<FILE> stream{ fopen((cacheDirectory + "comdev.txt").c_str(), "wb"), [](FILE *f){ [](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;
@@ -100,12 +103,14 @@ shared_ptr<FILE> getComDevStream()
return stream; return stream;
} }
shared_ptr<FILE> getDevPacketStream() shared_ptr<FILE> getDevPacketStream() {
{ static shared_ptr<FILE> stream{
static shared_ptr<FILE> stream{ initHTML(fopen((cacheDirectory + "devpackets.html").c_str(), "wb"), "Dev Packets"), [](FILE *f){ initHTML(fopen((cacheDirectory + "devpackets.html").c_str(), "wb"), "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;
@@ -113,12 +118,13 @@ shared_ptr<FILE> getDevPacketStream()
return stream; return stream;
} }
shared_ptr<FILE> getDevAPDUStream() shared_ptr<FILE> getDevAPDUStream() {
{ static shared_ptr<FILE> stream{ initHTML(fopen((cacheDirectory + "devAPDU.html").c_str(), "wb"),
static shared_ptr<FILE> stream{ initHTML(fopen((cacheDirectory + "devAPDU.html").c_str(), "wb"), "Dev APDU"), [](FILE *f){ "Dev APDU"),
clog << "Closing dev APDU stream" << endl; [](FILE* f) {
closeHTML(f); clog << "Closing dev APDU stream" << endl;
} }; closeHTML(f);
} };
if (!stream) if (!stream)
clog << "Stream is unavailable" << endl; clog << "Stream is unavailable" << endl;
@@ -126,54 +132,54 @@ 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); fclose(fPtr);
} }
#endif #endif

View File

@@ -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();

View File

@@ -17,11 +17,11 @@ 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 <csignal> #include <csignal>
#include <iostream>
#include <unistd.h> #include <unistd.h>
using namespace std; using namespace std;
@@ -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;
return false; return false;
@@ -46,20 +43,15 @@ 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);
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;
raise(SIGINT); raise(SIGINT);
@@ -73,13 +65,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;
return false; return false;
@@ -88,8 +77,7 @@ bool deinitialiseLights(const string& prog) {
return true; return true;
} }
void signalCallback([[maybe_unused]] int signum) void signalCallback([[maybe_unused]] int signum) {
{
contProc = false; contProc = false;
clog << "\nClosing" << endl; clog << "\nClosing" << endl;
} }

View File

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

View File

@@ -17,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;

View File

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

View File

@@ -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,9 +57,8 @@ 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, SW_WRONG_DATA);
return; return;
@@ -77,11 +70,10 @@ void U2F_Authenticate_APDU::respond(const uint32_t channelID) const
msg.cid = channelID; msg.cid = channelID;
msg.cmd = U2FHID_MSG; msg.cmd = U2FHID_MSG;
auto &response = msg.data; auto& response = msg.data;
APDU_STATUS statusCode = APDU_STATUS::SW_NO_ERROR; APDU_STATUS statusCode = APDU_STATUS::SW_NO_ERROR;
switch (p1) switch (p1) {
{
case ControlCode::CheckOnly: case ControlCode::CheckOnly:
if (appMatches) if (appMatches)
statusCode = APDU_STATUS::SW_CONDITIONS_NOT_SATISFIED; statusCode = APDU_STATUS::SW_CONDITIONS_NOT_SATISFIED;
@@ -92,9 +84,9 @@ void U2F_Authenticate_APDU::respond(const uint32_t channelID) const
msg.write(); 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:
@@ -104,7 +96,7 @@ void U2F_Authenticate_APDU::respond(const uint32_t channelID) const
} }
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 +109,8 @@ void U2F_Authenticate_APDU::respond(const uint32_t channelID) const
mbedtls_sha256_init(&shaContext); mbedtls_sha256_init(&shaContext);
mbedtls_sha256_starts(&shaContext, 0); mbedtls_sha256_starts(&shaContext, 0);
mbedtls_sha256_update(&shaContext, reinterpret_cast<const uint8_t *>(appParam.data()), sizeof(appParam)); mbedtls_sha256_update(&shaContext, reinterpret_cast<const uint8_t*>(appParam.data()),
sizeof(appParam));
uint8_t userPresence{ 1u }; uint8_t userPresence{ 1u };
mbedtls_sha256_update(&shaContext, &userPresence, 1); mbedtls_sha256_update(&shaContext, &userPresence, 1);
const auto beCounter = beEncode(keyCount); const auto beCounter = beEncode(keyCount);

View File

@@ -17,23 +17,21 @@ 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 struct U2F_Authenticate_APDU : U2F_Msg_CMD {
{
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 enum ControlCode {
{
CheckOnly = 0x07, CheckOnly = 0x07,
EnforcePresenceSign = 0x03, EnforcePresenceSign = 0x03,
DontEnforcePresenceSign = 0x08 DontEnforcePresenceSign = 0x08

View File

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

View File

@@ -17,16 +17,15 @@ 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 struct 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 U2FMessage& uMsg); static std::shared_ptr<U2F_CMD> get(const U2FMessage& uMsg);
virtual void respond(const uint32_t channelID) const = 0; virtual void respond(const uint32_t channelID) const = 0;
}; //For polymorphic type casting }; // For polymorphic type casting

View File

@@ -17,23 +17,19 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "U2F_Init_CMD.hpp" #include "U2F_Init_CMD.hpp"
#include <stdexcept>
#include "u2f.hpp"
#include "Field.hpp" #include "Field.hpp"
#include "u2f.hpp"
#include <stdexcept>
using namespace std; using namespace std;
U2F_Init_CMD::U2F_Init_CMD(const U2FMessage& uMsg) U2F_Init_CMD::U2F_Init_CMD(const 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,18 @@ U2F_Init_CMD::U2F_Init_CMD(const 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();
} }

View File

@@ -17,16 +17,15 @@ 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 struct U2F_Init_CMD : U2F_CMD {
{
uint64_t nonce; uint64_t nonce;
public: public:
U2F_Init_CMD(const U2FMessage& uMsg); U2F_Init_CMD(const U2FMessage& uMsg);
virtual void respond(const uint32_t channelID) const override; virtual void respond(const uint32_t channelID) const override;
}; };

View File

@@ -18,119 +18,99 @@ 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 U2FMessage& uMsg) shared_ptr<U2F_Msg_CMD> U2F_Msg_CMD::generate(const 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) if (usesData.at(cmd.ins) || data.size() > 3) {
{ if (cBCount == 0) {
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 +121,44 @@ shared_ptr<U2F_Msg_CMD> U2F_Msg_CMD::generate(const 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 +169,14 @@ shared_ptr<U2F_Msg_CMD> U2F_Msg_CMD::generate(const 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 +185,10 @@ 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));
} }

View File

@@ -20,27 +20,25 @@ 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 : 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 U2FMessage& uMsg); static std::shared_ptr<U2F_Msg_CMD> generate(const 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;
}; };

View File

@@ -21,15 +21,12 @@ 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 U2FMessage& uMsg) U2F_Ping_CMD::U2F_Ping_CMD(const 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;

View File

@@ -17,16 +17,15 @@ 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 struct U2F_Ping_CMD : U2F_CMD {
{
std::vector<uint8_t> nonce; std::vector<uint8_t> nonce;
public: public:
U2F_Ping_CMD(const U2FMessage& uMsg); U2F_Ping_CMD(const U2FMessage& uMsg);
virtual void respond(const uint32_t channelID) const override; virtual void respond(const uint32_t channelID) const override;
}; };

View File

@@ -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);

View File

@@ -17,18 +17,16 @@ 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 struct U2F_Register_APDU : U2F_Msg_CMD {
{
std::array<uint8_t, 32> challengeP; std::array<uint8_t, 32> challengeP;
Storage::AppParam appP; Storage::AppParam appP;
Storage::KeyHandle keyH; Storage::KeyHandle keyH;
public: public:
U2F_Register_APDU(const U2F_Msg_CMD &msg, const std::vector<uint8_t> &data); U2F_Register_APDU(const U2F_Msg_CMD& msg, const std::vector<uint8_t>& data);
void respond(const uint32_t channelID) const override; void respond(const uint32_t channelID) const override;
}; };

View File

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

View File

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

View File

@@ -1,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(terminate_handler);
#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
View File

@@ -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__