Implemented authentication.
Near feature complete.
This commit is contained in:
7
.gitignore
vendored
7
.gitignore
vendored
@@ -2,8 +2,11 @@ SOut.txt
|
||||
comdev.txt
|
||||
comhost.txt
|
||||
libuECC.o
|
||||
libcppb64.o
|
||||
monitor
|
||||
obj/*
|
||||
U2F_Priv_Keys.txt
|
||||
devpackets.txt
|
||||
hostpackets.txt
|
||||
devAPDU.html
|
||||
devpackets.html
|
||||
hostAPDU.html
|
||||
hostpackets.html
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +1,6 @@
|
||||
[submodule "micro-ecc"]
|
||||
path = micro-ecc
|
||||
url = https://github.com/kmackay/micro-ecc.git
|
||||
[submodule "cpp-base64"]
|
||||
path = cpp-base64
|
||||
url = https://github.com/ReneNyffenegger/cpp-base64.git
|
||||
|
||||
12
APDU.hpp
12
APDU.hpp
@@ -1,8 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
enum APDU
|
||||
enum APDU : uint8_t
|
||||
{
|
||||
U2F_REG = 0x01,
|
||||
U2F_AUTH = 0x02,
|
||||
U2F_VER = 0x03
|
||||
};
|
||||
|
||||
enum APDU_STATUS : uint16_t
|
||||
{
|
||||
SW_NO_ERROR = 0x9000,
|
||||
SW_CONDITIONS_NOT_SATISFIED = 0x6985,
|
||||
SW_WRONG_DATA = 0x6A80,
|
||||
SW_WRONG_LENGTH = 0x6700,
|
||||
SW_CLA_NOT_SUPPORTED = 0x6E00,
|
||||
SW_INS_NOT_SUPPORTED = 0x6D00
|
||||
};
|
||||
|
||||
25
Base64.tpp
25
Base64.tpp
@@ -1,9 +1,10 @@
|
||||
#include "Base64.hpp"
|
||||
#include <sstream>
|
||||
#include <b64/encode.h>
|
||||
#include <b64/decode.h>
|
||||
#include "cpp-base64/base64.h"
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include "Field.hpp"
|
||||
|
||||
template <typename InContainer, typename OutContainer>
|
||||
void b64encode(const InContainer &iContainer, OutContainer &oContainer)
|
||||
@@ -32,22 +33,18 @@ void b64decode(const InContainer &iContainer, std::array<Elem, count> &oArr)
|
||||
template <typename InContainerIter, typename OutContainerIter>
|
||||
void b64encode(const InContainerIter beginIter, const InContainerIter endIter, OutContainerIter oBeginIter)
|
||||
{
|
||||
std::stringstream oss, iss{ std::string{ beginIter, endIter } };
|
||||
base64::encoder encoder{};
|
||||
encoder.encode(iss, oss);
|
||||
std::vector<uint8_t> rawBytes{};
|
||||
|
||||
auto oStr = oss.str();
|
||||
oStr.erase(std::remove_if(oStr.begin(), oStr.end(), [](auto charac){ return charac == '\n'; }), oStr.end());
|
||||
copy(oStr.begin(), oStr.end(), oBeginIter);
|
||||
for (auto iter = beginIter; iter != endIter; iter++)
|
||||
rawBytes.insert(rawBytes.end(), FIELD(*iter));
|
||||
|
||||
auto encoded = base64_encode(rawBytes.data(), rawBytes.size());
|
||||
std::copy(encoded.begin(), encoded.end(), oBeginIter);
|
||||
}
|
||||
|
||||
template <typename InContainerIter, typename OutContainerIter>
|
||||
void b64decode(const InContainerIter beginIter, const InContainerIter endIter, OutContainerIter oBeginIter)
|
||||
{
|
||||
std::stringstream oss, iss{ std::string{ beginIter, endIter } };
|
||||
base64::decoder decoder{};
|
||||
decoder.decode(iss, oss);
|
||||
|
||||
auto oStr = oss.str();
|
||||
copy(oStr.begin(), oStr.end(), oBeginIter);
|
||||
auto decoded = base64_decode(std::string{ beginIter, endIter });
|
||||
std::copy(decoded.begin(), decoded.end(), oBeginIter);
|
||||
}
|
||||
|
||||
8
Field.cpp
Normal file
8
Field.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
#include "Field.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
vector<uint8_t> beEncode(const uint8_t* val, const size_t byteCount)
|
||||
{
|
||||
return { reverse_iterator<const uint8_t *>(val + byteCount), reverse_iterator<const uint8_t *>(val) };
|
||||
}
|
||||
10
Field.hpp
10
Field.hpp
@@ -1,3 +1,11 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
#define FIELD(name) reinterpret_cast<uint8_t*>(&name), (reinterpret_cast<uint8_t*>(&name) + sizeof(name))
|
||||
template <typename Type>
|
||||
std::vector<uint8_t> beEncode(const Type val);
|
||||
|
||||
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_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))
|
||||
|
||||
8
Field.tpp
Normal file
8
Field.tpp
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include "Field.hpp"
|
||||
|
||||
template <typename Type>
|
||||
std::vector<uint8_t> beEncode(const Type val)
|
||||
{
|
||||
return beEncode(reinterpret_cast<const uint8_t *>(&val), sizeof(val));
|
||||
}
|
||||
10
Makefile
10
Makefile
@@ -2,7 +2,7 @@
|
||||
|
||||
SRC_DIR := .
|
||||
OBJ_DIR := obj
|
||||
LDFLAGS := -lmbedcrypto -lb64
|
||||
LDFLAGS := -lmbedcrypto
|
||||
CPPFLAGS :=
|
||||
CXXFLAGS := --std=c++14
|
||||
|
||||
@@ -10,7 +10,7 @@ CXXFLAGS += -MMD -MP
|
||||
MODULES := $(wildcard $(SRC_DIR)/*.cpp)
|
||||
OBJECTS := $(MODULES:$(SRC_DIR)/%.cpp=$(OBJ_DIR)/%.o)
|
||||
|
||||
monitor: $(OBJECTS) libuECC.o
|
||||
monitor: $(OBJECTS) libuECC.o libcppb64.o
|
||||
g++ $(LDFLAGS) -o $@ $^
|
||||
|
||||
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
|
||||
@@ -22,7 +22,11 @@ clean:
|
||||
rm $(OBJ_DIR)/*
|
||||
rm monitor
|
||||
|
||||
.PHONY: libuECC
|
||||
.PHONY: libuECC.o libcppb64.so clean
|
||||
libuECC.o:
|
||||
$(MAKE) -C micro-ecc
|
||||
cp micro-ecc/libuECC.o libuECC.o
|
||||
|
||||
libcppb64.o:
|
||||
$(MAKE) -C cpp-base64
|
||||
cp cpp-base64/libcppb64.o libcppb64.o
|
||||
|
||||
113
Packet.cpp
113
Packet.cpp
@@ -19,8 +19,32 @@ shared_ptr<InitPacket> InitPacket::getPacket(const uint32_t rCID, const uint8_t
|
||||
copy(dataBytes.begin(), dataBytes.end(), p->data.begin());
|
||||
|
||||
auto hPStream = getHostPacketStream().get();
|
||||
fprintf(hPStream, "\n");
|
||||
fwrite(dataBytes.data(), 1, dataBytes.size(), hPStream);
|
||||
fprintf(hPStream, "\t\t<table>\n"
|
||||
"\t\t\t<thead>\n"
|
||||
"\t\t\t\t<tr>\n"
|
||||
"\t\t\t\t\t<th>CID</th>\n"
|
||||
"\t\t\t\t\t<th>CMD</th>\n"
|
||||
"\t\t\t\t\t<th>BCNTH</th>\n"
|
||||
"\t\t\t\t\t<th>BCNTL</th>\n"
|
||||
"\t\t\t\t\t<th class=\"data\">DATA</th>\n"
|
||||
"\t\t\t\t</tr>\n"
|
||||
"\t\t\t</thead>\n"
|
||||
"\t\t\t<tbody>\n"
|
||||
"\t\t\t\t<tr>\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 class=\"data\">", p->cid, p->cmd, p->bcnth, p->bcntl);
|
||||
|
||||
for (auto elem : dataBytes)
|
||||
fprintf(hPStream, "%3u ", elem);
|
||||
|
||||
fprintf(hPStream, "</td>\n"
|
||||
"\t\t\t\t</tr>\n"
|
||||
"\t\t\t</tbody>\n"
|
||||
"\t\t</table>"
|
||||
"\t\t<br />");
|
||||
|
||||
clog << "Fully read init packet" << endl;
|
||||
return p;
|
||||
@@ -36,9 +60,30 @@ shared_ptr<ContPacket> ContPacket::getPacket(const uint32_t rCID, const uint8_t
|
||||
copy(dataBytes.begin(), dataBytes.end(), p->data.begin());
|
||||
|
||||
auto hPStream = getHostPacketStream().get();
|
||||
fwrite(dataBytes.data(), 1, dataBytes.size(), hPStream);
|
||||
fprintf(hPStream, "\t\t<table>\n"
|
||||
"\t\t\t<thead>\n"
|
||||
"\t\t\t\t<tr>\n"
|
||||
"\t\t\t\t\t<th>CID</th>\n"
|
||||
"\t\t\t\t\t<th>SEQ</th>\n"
|
||||
"\t\t\t\t\t<th class=\"data\">DATA</th>\n"
|
||||
"\t\t\t\t</tr>\n"
|
||||
"\t\t\t</thead>\n"
|
||||
"\t\t\t<tbody>\n"
|
||||
"\t\t\t\t<tr>\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 class=\"data\">", p->cid, p->seq);
|
||||
|
||||
clog << "Fully read cont packet" << endl;
|
||||
for (auto elem : dataBytes)
|
||||
fprintf(hPStream, "%3u ", elem);
|
||||
|
||||
fprintf(hPStream, "</td>\n"
|
||||
"\t\t\t\t</tr>\n"
|
||||
"\t\t\t</tbody>\n"
|
||||
"\t\t</table>\n"
|
||||
"\t\t<br />");
|
||||
|
||||
//clog << "Fully read cont packet" << endl;
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -47,7 +92,7 @@ shared_ptr<Packet> Packet::getPacket()
|
||||
const uint32_t cid = *reinterpret_cast<uint32_t*>(readBytes(4).data());
|
||||
uint8_t b = readBytes(1)[0];
|
||||
|
||||
clog << "Packet read 2nd byte as " << static_cast<uint16_t>(b) << endl;
|
||||
//clog << "Packet read 2nd byte as " << static_cast<uint16_t>(b) << endl;
|
||||
|
||||
if (b & TYPE_MASK)
|
||||
{
|
||||
@@ -80,11 +125,36 @@ void InitPacket::writePacket()
|
||||
fwrite(this->buf, packetSize, 1, hostStream);
|
||||
fwrite(this->buf, packetSize, 1, devStream);
|
||||
|
||||
perror(nullptr);
|
||||
if (errno != 0)
|
||||
perror("perror " __FILE__ " 85");
|
||||
|
||||
auto dPStream = getDevPacketStream().get();
|
||||
fprintf(dPStream, "\n");
|
||||
fwrite(data.data(), 1, data.size(), dPStream);
|
||||
fprintf(dPStream, "\t\t<table>\n"
|
||||
"\t\t\t<thead>\n"
|
||||
"\t\t\t\t<tr>\n"
|
||||
"\t\t\t\t\t<th>CID</th>\n"
|
||||
"\t\t\t\t\t<th>CMD</th>\n"
|
||||
"\t\t\t\t\t<th>BCNTH</th>\n"
|
||||
"\t\t\t\t\t<th>BCNTL</th>\n"
|
||||
"\t\t\t\t\t<th class=\"data\">DATA</th>\n"
|
||||
"\t\t\t\t</tr>\n"
|
||||
"\t\t\t</thead>\n"
|
||||
"\t\t\t<tbody>\n"
|
||||
"\t\t\t\t<tr>\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 class=\"data\">", cid, cmd, bcnth, bcntl);
|
||||
|
||||
for (auto elem : data)
|
||||
fprintf(dPStream, "%3u ", elem);
|
||||
|
||||
fprintf(dPStream, "</td>\n"
|
||||
"\t\t\t\t</tr>\n"
|
||||
"\t\t\t</tbody>\n"
|
||||
"\t\t</table>"
|
||||
"\t\t<br />");
|
||||
|
||||
clog << "Fully wrote init packet" << endl;
|
||||
}
|
||||
@@ -100,10 +170,31 @@ void ContPacket::writePacket()
|
||||
fwrite(this->buf, packetSize, 1, hostStream);
|
||||
fwrite(this->buf, packetSize, 1, devStream);
|
||||
|
||||
perror(nullptr);
|
||||
if (errno != 0)
|
||||
perror("perror " __FILE__ " 107");
|
||||
|
||||
auto dPStream = getDevPacketStream().get();
|
||||
fwrite(data.data(), 1, data.size(), dPStream);
|
||||
|
||||
clog << "Fully wrote cont packet" << endl;
|
||||
fprintf(dPStream, "\t\t<table>\n"
|
||||
"\t\t\t<thead>\n"
|
||||
"\t\t\t\t<tr>\n"
|
||||
"\t\t\t\t\t<th>CID</th>\n"
|
||||
"\t\t\t\t\t<th>SEQ</th>\n"
|
||||
"\t\t\t\t\t<th class=\"data\">DATA</th>\n"
|
||||
"\t\t\t\t</tr>\n"
|
||||
"\t\t\t</thead>\n"
|
||||
"\t\t\t<tbody>\n"
|
||||
"\t\t\t\t<tr>\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 class=\"data\">", cid, seq);
|
||||
|
||||
for (auto elem : data)
|
||||
fprintf(dPStream, "%3u ", elem);
|
||||
|
||||
fprintf(dPStream, "</td>\n"
|
||||
"\t\t\t\t</tr>\n"
|
||||
"\t\t\t</tbody>\n"
|
||||
"\t\t</table>\n"
|
||||
"\t\t<br />");
|
||||
}
|
||||
|
||||
20
Scripts/cpp-base64-Makefile
Executable file
20
Scripts/cpp-base64-Makefile
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env make
|
||||
|
||||
SRC_DIR := .
|
||||
OBJ_DIR := .
|
||||
LDFLAGS := -r
|
||||
CPPFLAGS :=
|
||||
CXXFLAGS := --std=c++14
|
||||
|
||||
CXXFLAGS += -MMD -MP
|
||||
MODULESC := $(SRC_DIR)/base64.cpp
|
||||
OBJECTS := $(MODULESC:$(SRC_DIR)/%.cpp=$(OBJ_DIR)/%.o)
|
||||
|
||||
libcppb64.o: $(OBJECTS)
|
||||
ld $(LDFLAGS) -o $@ $^
|
||||
|
||||
$(OBJECTS): $(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
|
||||
@mkdir -p $(@D)
|
||||
@g++ $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
-include $(OBJECTS:.o=.d)
|
||||
29
Signature.cpp
Normal file
29
Signature.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "Signature.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
//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)
|
||||
{
|
||||
response.push_back(0x30); // Start of ASN.1 SEQUENCE
|
||||
response.push_back(68); //total length from (2 * (32 + 2)) to (2 * (33 + 2))
|
||||
size_t countByte = response.size() - 1;
|
||||
|
||||
// Loop twice - for R and S
|
||||
for(unsigned int i = 0; i < 2; i++)
|
||||
{
|
||||
unsigned int sigOffs = i * 32;
|
||||
auto offset = signature.begin() + sigOffs;
|
||||
response.push_back(0x02); //header: integer
|
||||
response.push_back(32); //32 byte
|
||||
if (signature[sigOffs] > 0x7f)
|
||||
{
|
||||
// Integer needs to be represented in 2's completement notion
|
||||
response.back()++;
|
||||
response.push_back(0); // add leading 0, to indicate it is a positive number
|
||||
response[countByte]++;
|
||||
}
|
||||
copy(offset, offset + 32, back_inserter(response)); //R or S value
|
||||
}
|
||||
}
|
||||
9
Signature.hpp
Normal file
9
Signature.hpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
using Digest = std::array<uint8_t, 32>;
|
||||
using Signature = std::array<uint8_t, 64>;
|
||||
|
||||
//Ripped from https://github.com/pratikd650/Teensy_U2F/blob/master/Teensy_U2F.cpp
|
||||
void appendSignatureAsDER(std::vector<uint8_t> &response, const Signature &signature);
|
||||
42
Storage.cpp
42
Storage.cpp
@@ -10,6 +10,7 @@ std::string Storage::filename{};
|
||||
std::map<Storage::KeyHandle, Storage::AppParam> Storage::appParams{};
|
||||
std::map<Storage::KeyHandle, Storage::PrivKey> Storage::privKeys{};
|
||||
std::map<Storage::KeyHandle, Storage::PubKey> Storage::pubKeys{};
|
||||
std::map<Storage::KeyHandle, Storage::KeyCount> Storage::keyCounts{};
|
||||
|
||||
void Storage::init(const string &dirPrefix)
|
||||
{
|
||||
@@ -18,30 +19,27 @@ void Storage::init(const string &dirPrefix)
|
||||
string line;
|
||||
size_t lineNumber = 0;
|
||||
|
||||
base64::decoder decoder{};
|
||||
|
||||
while (getline(file, line))
|
||||
{
|
||||
auto strLineNum = to_string(lineNumber);
|
||||
stringstream ss{ line };
|
||||
string keyHStr, appStr, privStr, pubStr;
|
||||
ss >> keyHStr >> appStr >> privStr >> pubStr;
|
||||
string keyHStr, appStr, privStr, pubStr, keyCStr;
|
||||
ss >> keyHStr >> appStr >> privStr >> pubStr >> keyCStr;
|
||||
|
||||
if (!ss)
|
||||
throw runtime_error{ string{ "Invalid syntax of line " } + strLineNum };
|
||||
|
||||
char *endP = nullptr;
|
||||
Storage::KeyHandle keyH{ strtoull(keyHStr.c_str(), &endP, 10) };
|
||||
auto keyH{ static_cast<Storage::KeyHandle>(strtoull(keyHStr.c_str(), &endP, 10)) };
|
||||
|
||||
if (!endP)
|
||||
throw runtime_error{ "Invalid keyhandle format on line " + strLineNum };
|
||||
|
||||
endP = nullptr;
|
||||
auto keyC{ static_cast<Storage::KeyCount>(strtoull(keyCStr.c_str(), &endP, 10)) };
|
||||
|
||||
//if (appStr.size() != 32)
|
||||
// throw runtime_error{ "Invalid length of app parameter on line " + strLineNum };
|
||||
//if (privStr.size() != 32)
|
||||
// throw runtime_error{ "Invalid length of private key on line " + strLineNum };
|
||||
//if (pubStr.size() != 65)
|
||||
// throw runtime_error{ "Invalid length of public key on line " + strLineNum };
|
||||
if (!endP)
|
||||
throw runtime_error{ "Invalid key count format on line " + strLineNum };
|
||||
|
||||
Storage::AppParam appParam{};
|
||||
b64decode(appStr, appParam);
|
||||
@@ -52,9 +50,17 @@ void Storage::init(const string &dirPrefix)
|
||||
Storage::PubKey pubKey{};
|
||||
b64decode(pubStr, pubKey);
|
||||
|
||||
clog << "Loaded key with pubkey: " << hex;
|
||||
|
||||
for (auto b : pubKey)
|
||||
clog << static_cast<uint32_t>(b) << ' ';
|
||||
|
||||
clog << dec << endl;
|
||||
|
||||
Storage::appParams[keyH] = appParam;
|
||||
Storage::privKeys[keyH] = privKey;
|
||||
Storage::pubKeys[keyH] = pubKey;
|
||||
Storage::keyCounts[keyH] = keyC;
|
||||
|
||||
lineNumber++;
|
||||
}
|
||||
@@ -63,14 +69,14 @@ void Storage::init(const string &dirPrefix)
|
||||
void Storage::save()
|
||||
{
|
||||
ofstream file{ Storage::filename };
|
||||
base64::encoder encoder{};
|
||||
|
||||
for (auto &keypair : Storage::appParams)
|
||||
{
|
||||
const auto keyID = keypair.first;
|
||||
const auto appParam = keypair.second;
|
||||
const auto privKey = Storage::privKeys[keypair.first];
|
||||
const auto pubKey = Storage::pubKeys[keypair.first];
|
||||
const auto& keyID = keypair.first;
|
||||
const auto& appParam = keypair.second;
|
||||
const auto& privKey = Storage::privKeys[keyID];
|
||||
const auto& pubKey = Storage::pubKeys[keyID];
|
||||
const auto& keyCount = Storage::keyCounts[keyID];
|
||||
|
||||
file << keyID;
|
||||
file << ' ';
|
||||
@@ -85,6 +91,8 @@ void Storage::save()
|
||||
|
||||
string pubKStr{};
|
||||
b64encode(pubKey, pubKStr);
|
||||
file << pubKStr << endl;
|
||||
file << pubKStr << ' ';
|
||||
|
||||
file << keyCount << endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include <array>
|
||||
#include <string>
|
||||
@@ -7,6 +8,7 @@ class Storage
|
||||
{
|
||||
public:
|
||||
using KeyHandle = uint32_t;
|
||||
using KeyCount = uint32_t;
|
||||
using AppParam = std::array<uint8_t, 32>;
|
||||
using PrivKey = std::array<uint8_t, 32>;
|
||||
using PubKey = std::array<uint8_t, 65>;
|
||||
@@ -22,4 +24,5 @@ public:
|
||||
static std::map<KeyHandle, AppParam> appParams;
|
||||
static std::map<KeyHandle, PrivKey> privKeys;
|
||||
static std::map<KeyHandle, PubKey> pubKeys;
|
||||
static std::map<KeyHandle, KeyCount> keyCounts;
|
||||
};
|
||||
|
||||
88
Streams.cpp
88
Streams.cpp
@@ -3,6 +3,9 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
FILE* initHTML(FILE *fPtr, const string &title);
|
||||
void closeHTML(FILE *fPtr);
|
||||
|
||||
shared_ptr<FILE> getHostStream()
|
||||
{
|
||||
static shared_ptr<FILE> stream{ fopen("/dev/hidg0", "ab+"), [](FILE *f){
|
||||
@@ -30,9 +33,22 @@ shared_ptr<FILE> getComHostStream()
|
||||
|
||||
shared_ptr<FILE> getHostPacketStream()
|
||||
{
|
||||
static shared_ptr<FILE> stream{ fopen("hostpackets.txt", "wb"), [](FILE *f){
|
||||
static shared_ptr<FILE> stream{ initHTML(fopen("hostpackets.html", "wb"), "Host Packets"), [](FILE *f){
|
||||
clog << "Closing hostPackets stream" << endl;
|
||||
fclose(f);
|
||||
closeHTML(f);
|
||||
} };
|
||||
|
||||
if (!stream)
|
||||
clog << "Stream is unavailable" << endl;
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
shared_ptr<FILE> getHostAPDUStream()
|
||||
{
|
||||
static shared_ptr<FILE> stream{ initHTML(fopen("hostAPDU.html", "wb"), "Host APDU"), [](FILE *f){
|
||||
clog << "Closing host APDU stream" << endl;
|
||||
closeHTML(f);
|
||||
} };
|
||||
|
||||
if (!stream)
|
||||
@@ -56,9 +72,9 @@ shared_ptr<FILE> getComDevStream()
|
||||
|
||||
shared_ptr<FILE> getDevPacketStream()
|
||||
{
|
||||
static shared_ptr<FILE> stream{ fopen("devpackets.txt", "wb"), [](FILE *f){
|
||||
static shared_ptr<FILE> stream{ initHTML(fopen("devpackets.html", "wb"), "Dev Packets"), [](FILE *f){
|
||||
clog << "Closing devPackets stream" << endl;
|
||||
fclose(f);
|
||||
closeHTML(f);
|
||||
} };
|
||||
|
||||
if (!stream)
|
||||
@@ -66,3 +82,67 @@ shared_ptr<FILE> getDevPacketStream()
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
shared_ptr<FILE> getDevAPDUStream()
|
||||
{
|
||||
static shared_ptr<FILE> stream{ initHTML(fopen("devAPDU.html", "wb"), "Dev APDU"), [](FILE *f){
|
||||
clog << "Closing dev APDU stream" << endl;
|
||||
closeHTML(f);
|
||||
} };
|
||||
|
||||
if (!stream)
|
||||
clog << "Stream is unavailable" << endl;
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
FILE* initHTML(FILE *fPtr, const string &title)
|
||||
{
|
||||
fprintf(fPtr, "<html>\n"
|
||||
"\t<head>\n"
|
||||
"\t\t<title>%s</title>\n"
|
||||
"\t\t<style>\n"
|
||||
"\t\t\ttable {\n"
|
||||
"\t\t\t\tdisplay: table;\n"
|
||||
"\t\t\t\twidth: 100%;\n"
|
||||
"\t\t\t\tborder-collapse: collapse;\n"
|
||||
"\t\t\t\tbox-sizing: border-box;\n"
|
||||
"\t\t\t}\n"
|
||||
"\n"
|
||||
"\t\t\tth.data {\n"
|
||||
"\t\t\t\ttext-align: left;\n"
|
||||
"\t\t\t}\n"
|
||||
"\n"
|
||||
"\t\t\ttd {\n"
|
||||
"\t\t\t\tfont-family: \"Courier New\", Courier, monospace;\n"
|
||||
"\t\t\t\twhite-space: pre;\n"
|
||||
"\t\t\t}\n"
|
||||
"\n"
|
||||
"\t\t\ttd.data {\n"
|
||||
"\t\t\t\toverflow: hidden;\n"
|
||||
"\t\t\t\ttext-overflow: ellipsis;\n"
|
||||
"\t\t\t\tmax-width:1px;\n"
|
||||
"\t\t\t\twidth:100%;\n"
|
||||
"\t\t\t}\n"
|
||||
"\n"
|
||||
"\t\t\ttd.data:hover {\n"
|
||||
"\t\t\t\twhite-space: pre-wrap;\n"
|
||||
"\t\t\t}\n"
|
||||
"\n"
|
||||
"\t\t\ttable, th, td {\n"
|
||||
"\t\t\t\tborder: 1px solid black;\n"
|
||||
"\t\t\t}\n"
|
||||
"\t\t</style>\n"
|
||||
"\t</head>\n"
|
||||
"\n"
|
||||
"\t<body>", title.c_str());
|
||||
|
||||
return fPtr;
|
||||
}
|
||||
|
||||
void closeHTML(FILE *fPtr)
|
||||
{
|
||||
fprintf(fPtr, "\t</body>\n"
|
||||
"</html>");
|
||||
fclose(fPtr);
|
||||
}
|
||||
|
||||
@@ -5,5 +5,7 @@
|
||||
std::shared_ptr<FILE> getHostStream();
|
||||
std::shared_ptr<FILE> getComHostStream();
|
||||
std::shared_ptr<FILE> getHostPacketStream();
|
||||
std::shared_ptr<FILE> getHostAPDUStream();
|
||||
std::shared_ptr<FILE> getComDevStream();
|
||||
std::shared_ptr<FILE> getDevPacketStream();
|
||||
std::shared_ptr<FILE> getDevAPDUStream();
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
#include "Packet.hpp"
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include "Streams.hpp"
|
||||
#include "u2f.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -34,19 +36,22 @@ U2FMessage U2FMessage::read()
|
||||
throw runtime_error{ "Packet out of sequence" };
|
||||
|
||||
const uint16_t remainingBytes = messageSize - message.data.size();
|
||||
clog << "Remaining bytes: " << remainingBytes << endl;
|
||||
const uint16_t copyBytes = min(static_cast<uint16_t>(newPack->data.size()), remainingBytes);
|
||||
message.data.insert(message.data.end(), newPack->data.begin(), newPack->data.begin() + copyBytes);
|
||||
|
||||
currSeq++;
|
||||
}
|
||||
|
||||
std::clog << "Read all of message" << std::endl;
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
void U2FMessage::write()
|
||||
{
|
||||
clog << "Flushing host stream" << endl;
|
||||
fflush(getHostStream().get());
|
||||
clog << "Flushed host stream" << endl;
|
||||
const uint16_t bytesToWrite = this->data.size();
|
||||
uint16_t bytesWritten = 0;
|
||||
|
||||
@@ -80,8 +85,38 @@ void U2FMessage::write()
|
||||
copy(data.begin() + bytesWritten, data.begin() + bytesWritten + newByteCount, p.data.begin());
|
||||
p.writePacket();
|
||||
seq++;
|
||||
bytesWritten += newByteCount;
|
||||
}
|
||||
|
||||
auto stream = getHostStream().get();
|
||||
fflush(stream);
|
||||
|
||||
if (cmd == U2FHID_MSG)
|
||||
{
|
||||
auto dAS = getDevAPDUStream().get();
|
||||
|
||||
fprintf(dAS, "<table>\n"
|
||||
"\t\t\t<thead>\n"
|
||||
"\t\t\t\t<tr>\n"
|
||||
"\t\t\t\t\t<th>DATA</th>\n"
|
||||
"\t\t\t\t\t<th>ERR</th>\n"
|
||||
"\t\t\t\t</tr>\n"
|
||||
"\t\t\t</thead>\n"
|
||||
"\t\t\t<tbody>\n"
|
||||
"\t\t\t\t<tr>\n"
|
||||
"\t\t\t\t\t<td class=\"data\">");
|
||||
|
||||
for (size_t i = 0; i < data.size() - 2; i++)
|
||||
fprintf(dAS, "%3u ", data[i]);
|
||||
|
||||
uint16_t err = data[data.size() - 2] << 8;
|
||||
err |= data.back();
|
||||
|
||||
fprintf(dAS, "</td>\n"
|
||||
"\t\t\t\t\t<td>0x%04X</td>\n"
|
||||
"\t\t\t\t</tr>\n"
|
||||
"\t\t\t</tbody>\n"
|
||||
"\t\t</table>\n"
|
||||
"\t\t<br />", err);
|
||||
}
|
||||
}
|
||||
|
||||
120
U2F_Authenticate_APDU.cpp
Normal file
120
U2F_Authenticate_APDU.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
#include "U2F_Authenticate_APDU.hpp"
|
||||
#include "Field.hpp"
|
||||
#include "U2FMessage.hpp"
|
||||
#include "u2f.hpp"
|
||||
#include "Field.tpp"
|
||||
#include "APDU.hpp"
|
||||
#include <iostream>
|
||||
#include "Signature.hpp"
|
||||
#include "micro-ecc/uECC.h"
|
||||
#include <mbedtls/sha256.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
U2F_Authenticate_APDU::U2F_Authenticate_APDU(const U2F_Msg_CMD &msg, const vector<uint8_t> &data)
|
||||
: U2F_Msg_CMD{ msg }
|
||||
{
|
||||
if (data.size() < 66)
|
||||
throw runtime_error{ "Invalid authentication request" };
|
||||
|
||||
copy(data.begin() + 0, data.begin() + 32, challengeP.begin());
|
||||
copy(data.begin() + 32, data.begin() + 64, appParam.begin());
|
||||
|
||||
uint8_t keyHLen = data[64];
|
||||
|
||||
copy(data.begin() + 65, data.begin() + 65 + keyHLen, back_inserter(keyH));
|
||||
|
||||
clog << "Got U2F_Auth request" << endl;
|
||||
}
|
||||
|
||||
void U2F_Authenticate_APDU::respond()
|
||||
{
|
||||
U2FMessage msg{};
|
||||
msg.cid = 0xF1D0F1D0;
|
||||
msg.cmd = U2FHID_MSG;
|
||||
auto statusCode = APDU_STATUS::SW_NO_ERROR;
|
||||
|
||||
auto &response = msg.data;
|
||||
|
||||
if (keyH.size() != sizeof(Storage::KeyHandle))
|
||||
{
|
||||
//Respond with error code - key handle is of wrong size
|
||||
clog << "Invalid key handle length" << endl;
|
||||
statusCode = APDU_STATUS::SW_WRONG_DATA;
|
||||
response.insert(response.end(), FIELD_BE(statusCode));
|
||||
msg.write();
|
||||
return;
|
||||
}
|
||||
|
||||
auto keyHB = *reinterpret_cast<const Storage::KeyHandle*>(keyH.data());
|
||||
|
||||
if (Storage::appParams.find(keyHB) == Storage::appParams.end())
|
||||
{
|
||||
//Respond with error code - key handle doesn't exist in storage
|
||||
clog << "Invalid key handle" << endl;
|
||||
statusCode = APDU_STATUS::SW_WRONG_DATA;
|
||||
response.insert(response.end(), FIELD_BE(statusCode));
|
||||
msg.write();
|
||||
return;
|
||||
}
|
||||
|
||||
auto appMatches = (Storage::appParams.at(keyHB) == appParam);
|
||||
|
||||
switch (p1)
|
||||
{
|
||||
case ControlCode::CheckOnly:
|
||||
if (appMatches)
|
||||
statusCode = APDU_STATUS::SW_CONDITIONS_NOT_SATISFIED;
|
||||
else
|
||||
statusCode = APDU_STATUS::SW_WRONG_DATA;
|
||||
|
||||
response.insert(response.end(), FIELD_BE(statusCode));
|
||||
msg.write();
|
||||
return;
|
||||
case ControlCode::EnforcePresenceSign:
|
||||
//Continue processing
|
||||
case ControlCode::DontEnforcePresenceSign:
|
||||
//Continue processing
|
||||
break;
|
||||
|
||||
default:
|
||||
cerr << "Unknown APDU command" << endl;
|
||||
statusCode = APDU_STATUS::SW_WRONG_DATA;
|
||||
response.insert(response.end(), FIELD_BE(statusCode));
|
||||
msg.write();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& privKey = Storage::privKeys[keyHB];
|
||||
auto& keyCount = Storage::keyCounts[keyHB];
|
||||
keyCount++;
|
||||
|
||||
response.push_back(0x01);
|
||||
response.insert(response.end(), FIELD_BE(keyCount));
|
||||
|
||||
Digest digest;
|
||||
{
|
||||
mbedtls_sha256_context shaContext;
|
||||
|
||||
mbedtls_sha256_init(&shaContext);
|
||||
mbedtls_sha256_starts(&shaContext, 0);
|
||||
|
||||
mbedtls_sha256_update(&shaContext, reinterpret_cast<const uint8_t *>(appParam.data()), sizeof(appParam));
|
||||
uint8_t userPresence{ 1u };
|
||||
mbedtls_sha256_update(&shaContext, &userPresence, 1);
|
||||
const auto beCounter = beEncode(keyCount);
|
||||
mbedtls_sha256_update(&shaContext, beCounter.data(), beCounter.size());
|
||||
mbedtls_sha256_update(&shaContext, challengeP.data(), challengeP.size());
|
||||
|
||||
mbedtls_sha256_finish(&shaContext, digest.data());
|
||||
mbedtls_sha256_free(&shaContext);
|
||||
}
|
||||
|
||||
Signature signature{};
|
||||
uECC_sign(privKey.data(), digest.data(), digest.size(), signature.data(), uECC_secp256r1());
|
||||
|
||||
appendSignatureAsDER(response, signature);
|
||||
response.insert(response.end(), FIELD_BE(statusCode));
|
||||
|
||||
msg.write();
|
||||
}
|
||||
23
U2F_Authenticate_APDU.hpp
Normal file
23
U2F_Authenticate_APDU.hpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include "U2F_Msg_CMD.hpp"
|
||||
#include "Storage.hpp"
|
||||
|
||||
struct U2F_Authenticate_APDU : U2F_Msg_CMD
|
||||
{
|
||||
uint8_t controlByte;
|
||||
std::array<uint8_t, 32> challengeP;
|
||||
Storage::AppParam appParam;
|
||||
std::vector<uint8_t> keyH;
|
||||
|
||||
public:
|
||||
U2F_Authenticate_APDU(const U2F_Msg_CMD &msg, const std::vector<uint8_t> &data);
|
||||
|
||||
void respond();
|
||||
|
||||
enum ControlCode
|
||||
{
|
||||
CheckOnly = 0x07,
|
||||
EnforcePresenceSign = 0x03,
|
||||
DontEnforcePresenceSign = 0x08
|
||||
};
|
||||
};
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <stdexcept>
|
||||
#include "U2FMessage.hpp"
|
||||
#include "u2f.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -17,5 +18,7 @@ U2F_Init_CMD U2F_Init_CMD::get()
|
||||
U2F_Init_CMD cmd;
|
||||
cmd.nonce = *reinterpret_cast<const uint64_t*>(message.data.data());
|
||||
|
||||
clog << "Fully read nonce" << endl;
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ struct U2F_Init_Response : U2F_CMD
|
||||
|
||||
void write()
|
||||
{
|
||||
std::clog << "Beginning writeout of U2F_Init_Response" << std::endl;
|
||||
U2FMessage m{};
|
||||
m.cid = CID_BROADCAST;
|
||||
m.cmd = U2FHID_INIT;
|
||||
@@ -26,6 +27,9 @@ struct U2F_Init_Response : U2F_CMD
|
||||
m.data.insert(m.data.begin() + 15, FIELD(buildDevVer));
|
||||
m.data.insert(m.data.begin() + 16, FIELD(capabilities));
|
||||
|
||||
std::clog << "Finished inserting U2F_Init_Response fields to data buffer" << std::endl;
|
||||
|
||||
m.write();
|
||||
std::clog << "Completed writeout of U2F_Init_Response" << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
#include "U2F_Msg_CMD.hpp"
|
||||
#include "APDU.hpp"
|
||||
#include "U2F_Register_APDU.hpp"
|
||||
#include "U2F_Version_APDU.hpp"
|
||||
#include "U2F_Authenticate_APDU.hpp"
|
||||
#include "U2FMessage.hpp"
|
||||
#include "u2f.hpp"
|
||||
#include "APDU.hpp"
|
||||
#include <iostream>
|
||||
#include "Streams.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -58,10 +61,9 @@ shared_ptr<U2F_Msg_CMD> U2F_Msg_CMD::get()
|
||||
vector<uint8_t> data{ dat.begin() + 4, dat.end() };
|
||||
auto startPtr = data.begin(), endPtr = data.end();
|
||||
|
||||
if (usesData.at(cmd.ins))
|
||||
if (usesData.at(cmd.ins) || data.size() > 3)
|
||||
{
|
||||
clog << "Assuming command uses data" << endl;
|
||||
clog << "First bytes are: " << static_cast<uint16_t>(data[0]) << " " << static_cast<uint16_t>(data[1]) << " " << static_cast<uint16_t>(data[2]) << endl;
|
||||
//clog << "First bytes are: " << static_cast<uint16_t>(data[0]) << " " << static_cast<uint16_t>(data[1]) << " " << static_cast<uint16_t>(data[2]) << endl;
|
||||
|
||||
if (cBCount == 0)
|
||||
throw runtime_error{ "Invalid command - should have attached data" };
|
||||
@@ -77,15 +79,12 @@ shared_ptr<U2F_Msg_CMD> U2F_Msg_CMD::get()
|
||||
startPtr += 3;
|
||||
}
|
||||
|
||||
clog << "Got command uses " << cmd.lc << " bytes of data" << endl;
|
||||
|
||||
endPtr = startPtr + cmd.lc;
|
||||
|
||||
cmd.le = getLe(data.end() - endPtr, vector<uint8_t>(endPtr, data.end()));
|
||||
}
|
||||
else
|
||||
{
|
||||
clog << "Assuming command does not use data" << endl;
|
||||
cmd.lc = 0;
|
||||
endPtr = startPtr;
|
||||
cmd.le = getLe(cBCount, data);
|
||||
@@ -93,8 +92,50 @@ shared_ptr<U2F_Msg_CMD> U2F_Msg_CMD::get()
|
||||
|
||||
const auto dBytes = vector<uint8_t>(startPtr, endPtr);
|
||||
|
||||
if (cmd.ins == APDU::U2F_REG)
|
||||
return make_shared<U2F_Register_APDU>(cmd, dBytes);
|
||||
auto hAS = getHostAPDUStream().get();
|
||||
|
||||
fprintf(hAS, "<table>\n"
|
||||
"\t\t\t<thead>\n"
|
||||
"\t\t\t\t<tr>\n"
|
||||
"\t\t\t\t\t<th>CLA</th>\n"
|
||||
"\t\t\t\t\t<th>INS</th>\n"
|
||||
"\t\t\t\t\t<th>P1</th>\n"
|
||||
"\t\t\t\t\t<th>P2</th>\n"
|
||||
"\t\t\t\t\t<th>Lc</th>\n"
|
||||
"\t\t\t\t\t<th>Data</th>\n"
|
||||
"\t\t\t\t\t<th>Le</th>\n"
|
||||
"\t\t\t\t</tr>\n"
|
||||
"\t\t\t</thead>\n"
|
||||
"\t\t\t<tbody>\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>%u</td>\n"
|
||||
"\t\t\t\t\t<td>%3u</td>\n"
|
||||
"\t\t\t\t\t<td class=\"data\">", cmd.cla, cmd.ins, cmd.p1, cmd.p2, cmd.lc);
|
||||
|
||||
for (auto b : dBytes)
|
||||
fprintf(hAS, "%3u ", b);
|
||||
|
||||
fprintf(hAS, "</td>\n"
|
||||
"\t\t\t\t\t<td>%5u</td>\n"
|
||||
"\t\t\t\t</tr>\n"
|
||||
"\t\t\t</tbody>\n"
|
||||
"\t\t</table>\n"
|
||||
"\t\t<br />", cmd.le);
|
||||
|
||||
switch (cmd.ins)
|
||||
{
|
||||
case APDU::U2F_REG:
|
||||
return make_shared<U2F_Register_APDU>(cmd, dBytes);
|
||||
case APDU::U2F_AUTH:
|
||||
return make_shared<U2F_Authenticate_APDU>(cmd, dBytes);
|
||||
case APDU::U2F_VER:
|
||||
return make_shared<U2F_Version_APDU>(cmd);
|
||||
default:
|
||||
throw runtime_error{ "Unknown APDU command issued" };
|
||||
}
|
||||
}
|
||||
|
||||
void U2F_Msg_CMD::respond(){};
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
#include "Certificates.hpp"
|
||||
#include <mbedtls/sha256.h>
|
||||
#include <iostream>
|
||||
#include "APDU.hpp"
|
||||
#include "U2FMessage.hpp"
|
||||
#include "u2f.hpp"
|
||||
#include "Signature.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -23,63 +27,51 @@ U2F_Register_APDU::U2F_Register_APDU(const U2F_Msg_CMD &msg, const vector<uint8_
|
||||
copy(data.data() + 32, data.data() + 64, appP.begin());
|
||||
|
||||
//Use crypto lib to generate keypair
|
||||
Storage::PrivKey privKey{};
|
||||
Storage::PubKey pubKey{};
|
||||
|
||||
uint8_t pub[65];
|
||||
uint8_t priv[32];
|
||||
|
||||
//Unsure if necessary
|
||||
//From github.com/pratikd650/Teensy_U2F/blob/master/Teensy_U2F.cpp
|
||||
pub[0] = 0x04;
|
||||
pubKey[0] = 0x04;
|
||||
|
||||
uECC_make_key(pub + 1, priv, uECC_secp256r1());
|
||||
uECC_make_key(pubKey.data() + 1, privKey.data(), uECC_secp256r1());
|
||||
|
||||
Storage::PrivKey privKey{};
|
||||
copy(priv, end(priv), privKey.begin());
|
||||
Storage::PubKey pubKey;
|
||||
copy(pub, end(pub), pubKey.begin());
|
||||
|
||||
this->keyH = Storage::appParams.size();
|
||||
this->keyH = Storage::appParams.size(); //To increment the key handle
|
||||
Storage::appParams[this->keyH] = appP;
|
||||
Storage::privKeys[this->keyH] = privKey;
|
||||
Storage::pubKeys[this->keyH] = pubKey;
|
||||
}
|
||||
Storage::keyCounts[this->keyH] = 0;
|
||||
|
||||
//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) {
|
||||
response.push_back(0x30); // Start of ASN.1 SEQUENCE
|
||||
response.push_back(68); //total length from (2 * (32 + 2)) to (2 * (33 + 2))
|
||||
auto &count = response.back();
|
||||
clog << "Produced pub key: " << hex;
|
||||
|
||||
// Loop twice - for R and S
|
||||
for(unsigned int i = 0; i < 2; i++) {
|
||||
unsigned int sigOffs = i * 32;
|
||||
response.push_back(0x02); //header: integer
|
||||
response.push_back(32); //32 byte
|
||||
if (signature[sigOffs] > 0x7f) { // Integer needs to be represented in 2's completement notion
|
||||
response.back()++;
|
||||
response.push_back(0); // add leading 0, to indicate it is a positive number
|
||||
count++;
|
||||
}
|
||||
copy(signature.begin() + sigOffs, signature.begin() + sigOffs + 32, back_inserter(response)); //R or S value
|
||||
}
|
||||
for (auto b : pubKey)
|
||||
clog << static_cast<uint32_t>(b) << ' ';
|
||||
|
||||
clog << endl << dec << "Got U2F_Reg request" << endl;
|
||||
}
|
||||
|
||||
void U2F_Register_APDU::respond()
|
||||
{
|
||||
U2FMessage m{};
|
||||
m.cid = 0xF1D0F1D0;
|
||||
m.cmd = U2FHID_MSG;
|
||||
|
||||
auto& response = m.data;
|
||||
const auto appParam = Storage::appParams[this->keyH];
|
||||
const auto pubKey = Storage::pubKeys[this->keyH];
|
||||
const auto privKey = Storage::privKeys[this->keyH];
|
||||
|
||||
vector<uint8_t> response;
|
||||
response.push_back(0x05);
|
||||
copy(pubKey.begin(), pubKey.end(), back_inserter(response));
|
||||
response.push_back(sizeof(this->keyH));
|
||||
|
||||
auto fakeKeyHBytes = reinterpret_cast<uint8_t *>(&this->keyH);
|
||||
copy(fakeKeyHBytes, fakeKeyHBytes + 4, back_inserter(response));
|
||||
copy(fakeKeyHBytes, fakeKeyHBytes + sizeof(this->keyH), back_inserter(response));
|
||||
|
||||
copy(attestCert, end(attestCert), back_inserter(response));
|
||||
|
||||
//Gen signature
|
||||
array<uint8_t, 32> digest;
|
||||
Digest digest;
|
||||
{
|
||||
mbedtls_sha256_context shaContext;
|
||||
|
||||
@@ -97,13 +89,22 @@ void U2F_Register_APDU::respond()
|
||||
|
||||
mbedtls_sha256_update(&shaContext, reinterpret_cast<const unsigned char*>(pubKey.data()), pubKey.size());
|
||||
|
||||
mbedtls_sha256_finish(&shaContext, reinterpret_cast<unsigned char*>(digest.data()));
|
||||
mbedtls_sha256_finish(&shaContext, digest.data());
|
||||
mbedtls_sha256_free(&shaContext);
|
||||
}
|
||||
|
||||
array<uint8_t, 64> signature;
|
||||
Signature signature;
|
||||
std::clog << "Will sign digest with priv key" << std::endl;
|
||||
uECC_sign(attestPrivKey, digest.data(), digest.size(), signature.data(), uECC_secp256r1());
|
||||
|
||||
//Append signature as DER
|
||||
std::clog << "Will append sig as DER" << std::endl;
|
||||
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) & 0xff);
|
||||
|
||||
std::clog << "Writing out " << response.size() << " bytes in response" << std::endl;
|
||||
|
||||
m.write();
|
||||
}
|
||||
|
||||
28
U2F_Version_APDU.cpp
Normal file
28
U2F_Version_APDU.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#include "U2F_Version_APDU.hpp"
|
||||
#include <iostream>
|
||||
#include "U2FMessage.hpp"
|
||||
#include "u2f.hpp"
|
||||
#include "Field.hpp"
|
||||
#include "APDU.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
U2F_Version_APDU::U2F_Version_APDU(const U2F_Msg_CMD &msg)
|
||||
{
|
||||
clog << "Got U2F_Ver APDU request" << endl;
|
||||
|
||||
//Don't actually respond yet
|
||||
}
|
||||
|
||||
void U2F_Version_APDU::respond()
|
||||
{
|
||||
char ver[]{ 'U', '2', 'F', '_', 'V', '2' };
|
||||
U2FMessage m{};
|
||||
|
||||
m.cid = 0xF1D0F1D0;
|
||||
m.cmd = U2FHID_MSG;
|
||||
m.data.insert(m.data.end(), FIELD(ver));
|
||||
auto sC = APDU_STATUS::SW_NO_ERROR;
|
||||
m.data.insert(m.data.end(), FIELD_BE(sC));
|
||||
m.write();
|
||||
}
|
||||
9
U2F_Version_APDU.hpp
Normal file
9
U2F_Version_APDU.hpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#include "U2F_Msg_CMD.hpp"
|
||||
|
||||
struct U2F_Version_APDU : U2F_Msg_CMD
|
||||
{
|
||||
public:
|
||||
U2F_Version_APDU(const U2F_Msg_CMD &msg);
|
||||
void respond();
|
||||
};
|
||||
1
cpp-base64
Submodule
1
cpp-base64
Submodule
Submodule cpp-base64 added at 6420804f7b
16
monitor.cpp
16
monitor.cpp
@@ -18,11 +18,12 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main()
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Storage::init();
|
||||
|
||||
auto initFrame = U2F_Init_CMD::get();
|
||||
|
||||
U2F_Init_Response resp{};
|
||||
|
||||
resp.cid = 0xF1D0F1D0;
|
||||
@@ -35,9 +36,18 @@ int main()
|
||||
|
||||
resp.write();
|
||||
|
||||
auto m = U2F_Msg_CMD::get();
|
||||
size_t msgCount = (argc == 2 ? stoul(argv[1]) : 3u);
|
||||
|
||||
cout << "U2F CMD ins: " << static_cast<uint32_t>(m->ins) << endl;
|
||||
for (size_t i = 0; i < msgCount; i++)
|
||||
{
|
||||
auto reg = U2F_Msg_CMD::get();
|
||||
|
||||
cout << "U2F CMD ins: " << static_cast<uint32_t>(reg->ins) << endl;
|
||||
|
||||
reg->respond();
|
||||
|
||||
clog << "Fully responded" << endl;
|
||||
}
|
||||
|
||||
/*auto m = U2FMessage::read();
|
||||
|
||||
|
||||
81
schema/APDU.html
Normal file
81
schema/APDU.html
Normal file
@@ -0,0 +1,81 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Packets</title>
|
||||
<style>
|
||||
table {
|
||||
display: table;
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
th.data {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
td {
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
td.data {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: pre;
|
||||
max-width:1px;
|
||||
width:100%;
|
||||
}
|
||||
|
||||
td.data:hover {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
table, th, td {
|
||||
border: 1px solid black;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>CLA</th>
|
||||
<th>INS</th>
|
||||
<th>P1</th>
|
||||
<th>P2</th>
|
||||
<th>Lc</th>
|
||||
<th class="data">Data</th>
|
||||
<th>Le</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>0x%X</td>
|
||||
<td>0x%X</td>
|
||||
<td>%u</td>
|
||||
<td>%u</td>
|
||||
<td>%u</td>
|
||||
<td class="data">$DATA</td>
|
||||
<td>%u</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<br />
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Data</th>
|
||||
<th>ERR</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="data">$DATA</td>
|
||||
<td>%X</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<br />
|
||||
</body>
|
||||
</html>
|
||||
78
schema/packets.html
Normal file
78
schema/packets.html
Normal file
@@ -0,0 +1,78 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Packets</title>
|
||||
<style>
|
||||
table {
|
||||
display: table;
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
th.data {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
td {
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
td.data {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width:1px;
|
||||
width:100%;
|
||||
}
|
||||
|
||||
td.data:hover {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
table, th, td {
|
||||
border: 1px solid black;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>CID</th>
|
||||
<th>CMD</th>
|
||||
<th>BCNTH</th>
|
||||
<th>BCNTL</th>
|
||||
<th class="data">DATA</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>%08X</td>
|
||||
<td>%u</td>
|
||||
<td>%u</td>
|
||||
<td>%u</td>
|
||||
<td class="data">0x$DATA</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<br />
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>CID</th>
|
||||
<th>SEQ</th>
|
||||
<th class="data">DATA</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>%08X</td>
|
||||
<td>%u</td>
|
||||
<td class="data">0x$DATA</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<br />
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user