mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-27 08:38:07 +00:00
tests: add fido-tests/u2f-tests-hid
This commit is contained in:
parent
75c539ec55
commit
ca4581ce74
4
tests/fido_tests/u2f-tests-hid/.gitignore
vendored
Normal file
4
tests/fido_tests/u2f-tests-hid/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
*.o
|
||||
list
|
||||
HIDTest
|
||||
U2FTest
|
670
tests/fido_tests/u2f-tests-hid/HIDTest.cc
Normal file
670
tests/fido_tests/u2f-tests-hid/HIDTest.cc
Normal file
@ -0,0 +1,670 @@
|
||||
// Copyright 2014 Google Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
// Basic U2F HID framing compliance test.
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
#include "u2f_util.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
int arg_Verbose = 0; // default
|
||||
bool arg_Pause = false; // default
|
||||
bool arg_Abort = true; // default
|
||||
|
||||
static
|
||||
void checkPause() {
|
||||
if (arg_Pause) {
|
||||
printf("\nPress any key to continue..");
|
||||
getchar();
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void AbortOrNot() {
|
||||
checkPause();
|
||||
if (arg_Abort) abort();
|
||||
cerr << "(continuing -a)" << endl;
|
||||
}
|
||||
|
||||
struct U2Fob* device;
|
||||
|
||||
#define SEND(f) CHECK_EQ(0, U2Fob_sendHidFrame(device, &f))
|
||||
#define RECV(f, t) CHECK_EQ(0, U2Fob_receiveHidFrame(device, &f, t))
|
||||
|
||||
// Initialize a frame with |len| random payload, or data.
|
||||
void initFrame(U2FHID_FRAME* f, uint32_t cid, uint8_t cmd,
|
||||
size_t len, const void* data = NULL) {
|
||||
memset(f, 0, sizeof(U2FHID_FRAME));
|
||||
f->cid = cid;
|
||||
f->init.cmd = cmd | TYPE_INIT;
|
||||
f->init.bcnth = (uint8_t) (len >> 8);
|
||||
f->init.bcntl = (uint8_t) len;
|
||||
for (size_t i = 0; i < min(len, sizeof(f->init.data)); ++i) {
|
||||
f->init.data[i] = data ? ((const uint8_t*)data)[i] : (rand() & 255);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize a continue frame
|
||||
void contFrame(U2FHID_FRAME* f, uint32_t cid, uint8_t seqno, uint8_t val) {
|
||||
memset(f, val, sizeof(U2FHID_FRAME));
|
||||
f->cid = cid;
|
||||
f->cont.seq = seqno & ~TYPE_INIT;
|
||||
}
|
||||
|
||||
// Return true if frame r is error frame for expected error.
|
||||
bool isError(const U2FHID_FRAME r, int error) {
|
||||
return
|
||||
r.init.cmd == U2FHID_ERROR &&
|
||||
MSG_LEN(r) == 1 &&
|
||||
r.init.data[0] == error;
|
||||
}
|
||||
|
||||
// Test basic INIT.
|
||||
// Returns basic capabilities field.
|
||||
uint8_t test_BasicInit() {
|
||||
U2FHID_FRAME f, r;
|
||||
initFrame(&f, U2Fob_getCid(device), U2FHID_INIT, INIT_NONCE_SIZE);
|
||||
|
||||
SEND(f);
|
||||
RECV(r, 1.0);
|
||||
CHECK_EQ(f.cid, r.cid);
|
||||
|
||||
CHECK_EQ(r.init.cmd, U2FHID_INIT);
|
||||
CHECK_EQ(MSG_LEN(r), sizeof(U2FHID_INIT_RESP));
|
||||
CHECK_EQ(memcmp(&f.init.data[0], &r.init.data[0], INIT_NONCE_SIZE), 0);
|
||||
CHECK_EQ(r.init.data[12], U2FHID_IF_VERSION);
|
||||
return r.init.data[16];
|
||||
}
|
||||
|
||||
// Test we have a working (single frame) echo.
|
||||
void test_Echo() {
|
||||
U2FHID_FRAME f, r;
|
||||
uint64_t t = 0; U2Fob_deltaTime(&t);
|
||||
|
||||
initFrame(&f, U2Fob_getCid(device), U2FHID_PING, 8);
|
||||
|
||||
U2Fob_deltaTime(&t);
|
||||
|
||||
SEND(f);
|
||||
RECV(r, 1.0);
|
||||
CHECK_EQ(f.cid, r.cid);
|
||||
|
||||
// Expect echo somewhat quickly.
|
||||
CHECK_LT(U2Fob_deltaTime(&t), .1);
|
||||
|
||||
// Check echoed content matches.
|
||||
CHECK_EQ(U2FHID_PING, r.init.cmd);
|
||||
CHECK_EQ(MSG_LEN(f), MSG_LEN(r));
|
||||
CHECK_EQ(0, memcmp(f.init.data, r.init.data, MSG_LEN(f)));
|
||||
}
|
||||
|
||||
// Test we can echo message larger than a single frame.
|
||||
void test_LongEcho() {
|
||||
const size_t TESTSIZE = 1024;
|
||||
uint8_t challenge[TESTSIZE];
|
||||
uint8_t response[TESTSIZE];
|
||||
uint8_t cmd = U2FHID_PING;
|
||||
|
||||
for (size_t i = 0; i < sizeof(challenge); ++i) challenge[i] = rand();
|
||||
|
||||
uint64_t t = 0; U2Fob_deltaTime(&t);
|
||||
|
||||
CHECK_EQ(0, U2Fob_send(device, cmd, challenge, sizeof(challenge)));
|
||||
|
||||
float sent = U2Fob_deltaTime(&t);
|
||||
|
||||
CHECK_EQ(sizeof(response),
|
||||
U2Fob_recv(device, &cmd, response, sizeof(response), 2.0));
|
||||
|
||||
float received = U2Fob_deltaTime(&t);
|
||||
|
||||
CHECK_EQ(cmd, U2FHID_PING);
|
||||
CHECK_EQ(0, memcmp(challenge, response, sizeof(challenge)));
|
||||
|
||||
INFO << "sent: " << sent << ", received: " << received;
|
||||
|
||||
// Expected transfer times for 2ms bInterval.
|
||||
// We do not want fobs to be too slow or too agressive.
|
||||
if (device->dev != NULL) {
|
||||
// CHECK_GE(sent, .020);
|
||||
CHECK_LE(sent, .075);
|
||||
// CHECK_GE(received, .020);
|
||||
CHECK_LE(received, .075);
|
||||
}
|
||||
}
|
||||
|
||||
// Execute WINK, if implemented.
|
||||
// Visually inspect fob for compliance.
|
||||
void test_OptionalWink() {
|
||||
U2FHID_FRAME f, r;
|
||||
uint8_t caps = test_BasicInit();
|
||||
|
||||
initFrame(&f, U2Fob_getCid(device), U2FHID_WINK, 0);
|
||||
|
||||
SEND(f);
|
||||
RECV(r, 1.0);
|
||||
CHECK_EQ(f.cid, r.cid);
|
||||
|
||||
if (caps & CAPFLAG_WINK) {
|
||||
CHECK_EQ(f.init.cmd, r.init.cmd);
|
||||
CHECK_EQ(MSG_LEN(r), 0);
|
||||
} else {
|
||||
CHECK_EQ(isError(r, ERR_INVALID_CMD), true);
|
||||
}
|
||||
}
|
||||
|
||||
// Test max data size limit enforcement.
|
||||
// We try echo 7610 bytes.
|
||||
// Device should pre-empt communications with error reply.
|
||||
void test_Limits() {
|
||||
U2FHID_FRAME f, r;
|
||||
uint64_t t = 0; U2Fob_deltaTime(&t);
|
||||
|
||||
initFrame(&f, U2Fob_getCid(device), U2FHID_PING, 7610);
|
||||
|
||||
SEND(f);
|
||||
RECV(r, 1.0);
|
||||
CHECK_EQ(f.cid, r.cid);
|
||||
|
||||
CHECK_EQ(isError(r, ERR_INVALID_LEN), true);
|
||||
}
|
||||
|
||||
// Check there are no frames pending for this cid.
|
||||
// Poll for a frame with short timeout.
|
||||
// Make sure none got received and timeout time passed.
|
||||
void test_Idle(float timeOut = .3) {
|
||||
U2FHID_FRAME r;
|
||||
uint64_t t = 0; U2Fob_deltaTime(&t);
|
||||
|
||||
U2Fob_deltaTime(&t);
|
||||
CHECK_EQ(-ERR_MSG_TIMEOUT, U2Fob_receiveHidFrame(device, &r, timeOut));
|
||||
CHECK_GE(U2Fob_deltaTime(&t), .2);
|
||||
CHECK_LE(U2Fob_deltaTime(&t), .5);
|
||||
}
|
||||
|
||||
// Check we get a timeout error frame if not sending TYPE_CONT frames
|
||||
// for a message that spans multiple frames.
|
||||
// Device should timeout at ~.5 seconds.
|
||||
void test_Timeout() {
|
||||
U2FHID_FRAME f, r;
|
||||
float measuredTimeout;
|
||||
uint64_t t = 0; U2Fob_deltaTime(&t);
|
||||
|
||||
initFrame(&f, U2Fob_getCid(device), U2FHID_PING, 99);
|
||||
|
||||
U2Fob_deltaTime(&t);
|
||||
|
||||
SEND(f);
|
||||
RECV(r, 1.0);
|
||||
CHECK_EQ(f.cid, r.cid);
|
||||
|
||||
CHECK_EQ(isError(r, ERR_MSG_TIMEOUT), true);
|
||||
|
||||
measuredTimeout = U2Fob_deltaTime(&t);
|
||||
CHECK_GE(measuredTimeout, .4); // needs to be at least 0.4 seconds
|
||||
CHECK_LE(measuredTimeout, 1.0); // but at most 1.0 seconds
|
||||
}
|
||||
|
||||
// Test LOCK functionality, if implemented.
|
||||
void test_Lock() {
|
||||
U2FHID_FRAME f, r;
|
||||
uint64_t t = 0; U2Fob_deltaTime(&t);
|
||||
uint8_t caps = test_BasicInit();
|
||||
|
||||
// Check whether lock is supported using an unlock command.
|
||||
initFrame(&f, U2Fob_getCid(device), U2FHID_LOCK, 1, "\x00");
|
||||
SEND(f);
|
||||
RECV(r, 1.0);
|
||||
CHECK_EQ(f.cid, r.cid);
|
||||
|
||||
if (!(caps & CAPFLAG_LOCK)) {
|
||||
// Make sure CAPFLAG reflects behavior.
|
||||
CHECK_EQ(isError(r, ERR_INVALID_CMD), true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Lock channel for 3 seconds.
|
||||
initFrame(&f, U2Fob_getCid(device), U2FHID_LOCK, 1, "\x03");
|
||||
|
||||
SEND(f);
|
||||
RECV(r, 1.0);
|
||||
CHECK_EQ(f.cid, r.cid);
|
||||
|
||||
CHECK_EQ(f.init.cmd, r.init.cmd);
|
||||
CHECK_EQ(0, MSG_LEN(r));
|
||||
|
||||
// Rattle lock, checking for BUSY.
|
||||
int count = 0;
|
||||
do {
|
||||
// The requested channel timeout (3 seconds) resets
|
||||
// after every message, so we only send a couple of
|
||||
// messages down the channel in this loop. Otherwise
|
||||
// the lock would never expire.
|
||||
if (++count < 2) test_Echo();
|
||||
usleep(100000);
|
||||
initFrame(&f, U2Fob_getCid(device) ^ 1, U2FHID_PING, 1);
|
||||
|
||||
SEND(f);
|
||||
RECV(r, 1.0);
|
||||
CHECK_EQ(f.cid, r.cid);
|
||||
|
||||
if (r.init.cmd == U2FHID_ERROR) {
|
||||
// We only expect BUSY here.
|
||||
CHECK_EQ(isError(r, ERR_CHANNEL_BUSY), true);
|
||||
}
|
||||
} while (r.init.cmd == U2FHID_ERROR);
|
||||
|
||||
CHECK_GE(U2Fob_deltaTime(&t), 2.5);
|
||||
}
|
||||
|
||||
// Check we get abort if we send TYPE_INIT when TYPE_CONT is expected.
|
||||
void test_NotCont() {
|
||||
U2FHID_FRAME f, r;
|
||||
uint64_t t = 0; U2Fob_deltaTime(&t);
|
||||
|
||||
initFrame(&f, U2Fob_getCid(device), U2FHID_PING, 99); // Note 99 > frame.
|
||||
|
||||
SEND(f);
|
||||
|
||||
SEND(f); // Send frame again, i.e. another TYPE_INIT frame.
|
||||
RECV(r, 1.0);
|
||||
CHECK_EQ(f.cid, r.cid);
|
||||
|
||||
CHECK_LT(U2Fob_deltaTime(&t), .1); // Expect fail reply quickly.
|
||||
CHECK_EQ(isError(r, ERR_INVALID_SEQ), true);
|
||||
|
||||
// Check there are no further messages.
|
||||
CHECK_EQ(-ERR_MSG_TIMEOUT, U2Fob_receiveHidFrame(device, &r, 0.6f));
|
||||
}
|
||||
|
||||
// Check we get a error when sending wrong sequence in continuation frame.
|
||||
void test_WrongSeq() {
|
||||
U2FHID_FRAME f, r;
|
||||
uint64_t t = 0; U2Fob_deltaTime(&t);
|
||||
|
||||
initFrame(&f, U2Fob_getCid(device), U2FHID_PING, 99);
|
||||
|
||||
SEND(f);
|
||||
|
||||
f.cont.seq = 1 | TYPE_CONT; // Send wrong SEQ, 0 is expected.
|
||||
|
||||
SEND(f);
|
||||
RECV(r, 1.0);
|
||||
CHECK_EQ(f.cid, r.cid);
|
||||
|
||||
CHECK_LT(U2Fob_deltaTime(&t), .1); // Expect fail reply quickly.
|
||||
CHECK_EQ(isError(r, ERR_INVALID_SEQ), true);
|
||||
|
||||
// Check there are no further messages.
|
||||
CHECK_EQ(-ERR_MSG_TIMEOUT, U2Fob_receiveHidFrame(device, &r, 0.6f));
|
||||
}
|
||||
|
||||
// Check we hear nothing if we send a random CONT frame.
|
||||
void test_NotFirst() {
|
||||
U2FHID_FRAME f, r;
|
||||
|
||||
initFrame(&f, U2Fob_getCid(device), U2FHID_PING, 8);
|
||||
f.cont.seq = 0 | TYPE_CONT; // Make continuation packet.
|
||||
|
||||
SEND(f);
|
||||
CHECK_EQ(-ERR_MSG_TIMEOUT, U2Fob_receiveHidFrame(device, &r, 1.0));
|
||||
}
|
||||
|
||||
// Check we get a BUSY if device is waiting for CONT on other channel.
|
||||
void test_Busy() {
|
||||
U2FHID_FRAME f, r;
|
||||
uint64_t t = 0; U2Fob_deltaTime(&t);
|
||||
|
||||
initFrame(&f, U2Fob_getCid(device), U2FHID_PING, 99);
|
||||
|
||||
SEND(f);
|
||||
|
||||
f.cid ^= 1; // Flip channel.
|
||||
|
||||
SEND(f);
|
||||
RECV(r, 1.0);
|
||||
CHECK_EQ(f.cid, r.cid);
|
||||
|
||||
CHECK_LT(U2Fob_deltaTime(&t), .1); // Expect busy reply quickly.
|
||||
CHECK_EQ(isError(r, ERR_CHANNEL_BUSY), true);
|
||||
|
||||
f.cid ^= 1; // Flip back.
|
||||
|
||||
RECV(r, 1.0);
|
||||
CHECK_EQ(f.cid, r.cid);
|
||||
|
||||
CHECK_EQ(isError(r, ERR_MSG_TIMEOUT), true);
|
||||
|
||||
CHECK_GE(U2Fob_deltaTime(&t), .45); // Expect T/O msg only after timeout.
|
||||
}
|
||||
|
||||
// Check that fob ignores CONT frame for different cid.
|
||||
void test_Interleave() {
|
||||
U2FHID_FRAME f, r;
|
||||
uint64_t t = 0; U2Fob_deltaTime(&t);
|
||||
uint32_t cid0 = U2Fob_getCid(device);
|
||||
uint32_t cid1 = U2Fob_getCid(device) ^ 1;
|
||||
uint8_t expected;
|
||||
|
||||
// Start a 2 frame request on cid 0
|
||||
initFrame(&f, cid0, U2FHID_PING, sizeof(f.cont.data) + sizeof(f.init.data));
|
||||
expected = f.init.data[0];
|
||||
SEND(f);
|
||||
|
||||
// Interleave a 2 frame request on cid 1
|
||||
initFrame(&f, cid1, U2FHID_PING, sizeof(f.cont.data) + sizeof(f.init.data));
|
||||
SEND(f);
|
||||
contFrame(&f, cid1, 0, expected ^ 1);
|
||||
SEND(f);
|
||||
|
||||
// Then send 2nd frame on cid 0
|
||||
contFrame(&f, cid0, 0, expected);
|
||||
SEND(f);
|
||||
|
||||
// Expect CHANNEL_BUSY for cid 1
|
||||
RECV(r, 1.0);
|
||||
CHECK_EQ(r.cid, cid1);
|
||||
CHECK_EQ(isError(r, ERR_CHANNEL_BUSY), true);
|
||||
|
||||
// Expect correct 2 frame reply for cid 0
|
||||
RECV(r, 1.0);
|
||||
CHECK_EQ(r.cid, cid0);
|
||||
CHECK_EQ(r.init.data[0], expected);
|
||||
RECV(r, 1.0);
|
||||
CHECK_EQ(r.cid, cid0);
|
||||
CHECK_EQ(r.cont.data[1], expected);
|
||||
|
||||
// Expect nothing left to receive
|
||||
CHECK_EQ(-ERR_MSG_TIMEOUT, U2Fob_receiveHidFrame(device, &r, .5));
|
||||
}
|
||||
|
||||
// Test INIT self aborts wait for CONT frame
|
||||
void test_InitSelfAborts() {
|
||||
U2FHID_FRAME f, r;
|
||||
|
||||
initFrame(&f, U2Fob_getCid(device), U2FHID_PING, 99);
|
||||
SEND(f);
|
||||
|
||||
initFrame(&f, U2Fob_getCid(device), U2FHID_INIT, INIT_NONCE_SIZE);
|
||||
|
||||
SEND(f);
|
||||
RECV(r, 1.0);
|
||||
CHECK_EQ(f.cid, r.cid);
|
||||
|
||||
CHECK_EQ(r.init.cmd, U2FHID_INIT);
|
||||
CHECK_GE(MSG_LEN(r), MSG_LEN(f));
|
||||
CHECK_EQ(memcmp(&f.init.data[0], &r.init.data[0], INIT_NONCE_SIZE), 0);
|
||||
|
||||
test_NotFirst();
|
||||
}
|
||||
|
||||
// Test INIT other does not abort wait for CONT.
|
||||
void test_InitOther() {
|
||||
U2FHID_FRAME f, f2, r;
|
||||
|
||||
initFrame(&f, U2Fob_getCid(device), U2FHID_PING, 99);
|
||||
SEND(f);
|
||||
|
||||
initFrame(&f2, U2Fob_getCid(device) ^ 1, U2FHID_INIT, INIT_NONCE_SIZE);
|
||||
|
||||
SEND(f2);
|
||||
RECV(r, 1.0);
|
||||
CHECK_EQ(f2.cid, r.cid);
|
||||
|
||||
// Expect sync reply for requester
|
||||
CHECK_EQ(r.init.cmd, U2FHID_INIT);
|
||||
CHECK_GE(MSG_LEN(r), MSG_LEN(f2));
|
||||
CHECK_EQ(memcmp(&f2.init.data[0], &r.init.data[0], INIT_NONCE_SIZE), 0);
|
||||
|
||||
// Expect error frame after timeout on first channel.
|
||||
RECV(r, 1.0);
|
||||
CHECK_EQ(f.cid, r.cid);
|
||||
|
||||
CHECK_EQ(isError(r, ERR_MSG_TIMEOUT), true);
|
||||
}
|
||||
|
||||
void wait_Idle() {
|
||||
U2FHID_FRAME r;
|
||||
|
||||
while (-ERR_MSG_TIMEOUT != U2Fob_receiveHidFrame(device, &r, .2f)) {
|
||||
}
|
||||
}
|
||||
|
||||
void test_LeadingZero() {
|
||||
U2FHID_FRAME f, r;
|
||||
initFrame(&f, 0x100, U2FHID_PING, 10);
|
||||
|
||||
SEND(f);
|
||||
RECV(r, 1.0);
|
||||
CHECK_EQ(r.cid, f.cid);
|
||||
|
||||
CHECK_EQ(r.init.cmd, U2FHID_PING);
|
||||
CHECK_EQ(MSG_LEN(f), MSG_LEN(r));
|
||||
}
|
||||
|
||||
void test_InitOnNonBroadcastEchoesCID() {
|
||||
U2FHID_FRAME f, r;
|
||||
size_t cs = INIT_NONCE_SIZE;
|
||||
|
||||
initFrame(&f, 0xdeadbeef, U2FHID_INIT, cs); // Use non-broadcast cid
|
||||
|
||||
SEND(f);
|
||||
RECV(r, 1.0);
|
||||
CHECK_EQ(r.cid, f.cid);
|
||||
|
||||
CHECK_EQ(r.init.cmd, U2FHID_INIT);
|
||||
CHECK_EQ(MSG_LEN(r), sizeof(U2FHID_INIT_RESP));
|
||||
CHECK_EQ(0, memcmp(f.init.data, r.init.data, cs));
|
||||
|
||||
uint32_t cid =
|
||||
(r.init.data[cs + 0] << 24) |
|
||||
(r.init.data[cs + 1] << 16) |
|
||||
(r.init.data[cs + 2] << 8) |
|
||||
(r.init.data[cs + 3] << 0);
|
||||
|
||||
CHECK_EQ(cid, 0xdeadbeef);
|
||||
}
|
||||
|
||||
uint32_t test_Init(bool check = true) {
|
||||
U2FHID_FRAME f, r;
|
||||
size_t cs = INIT_NONCE_SIZE;
|
||||
|
||||
initFrame(&f, -1, U2FHID_INIT, cs); // -1 is broadcast channel
|
||||
|
||||
SEND(f);
|
||||
RECV(r, 1.0);
|
||||
CHECK_EQ(r.cid, f.cid);
|
||||
|
||||
// expect init reply
|
||||
CHECK_EQ(r.init.cmd, U2FHID_INIT);
|
||||
|
||||
CHECK_EQ(MSG_LEN(r), sizeof(U2FHID_INIT_RESP));
|
||||
|
||||
// Check echo of challenge
|
||||
CHECK_EQ(0, memcmp(f.init.data, r.init.data, cs));
|
||||
|
||||
uint32_t cid =
|
||||
(r.init.data[cs + 0] << 0) |
|
||||
(r.init.data[cs + 1] << 8) |
|
||||
(r.init.data[cs + 2] << 16) |
|
||||
(r.init.data[cs + 3] << 24);
|
||||
|
||||
if (check) {
|
||||
// Check that another INIT yields a distinct cid.
|
||||
CHECK_NE(test_Init(false), cid);
|
||||
}
|
||||
|
||||
return cid;
|
||||
}
|
||||
|
||||
void test_InitUnderLock() {
|
||||
U2FHID_FRAME f, r;
|
||||
uint8_t caps = test_BasicInit();
|
||||
|
||||
// Check whether lock is supported, using an unlock command.
|
||||
initFrame(&f, U2Fob_getCid(device), U2FHID_LOCK, 1, "\x00"); // unlock
|
||||
|
||||
SEND(f);
|
||||
RECV(r, 1.0);
|
||||
CHECK_EQ(f.cid, r.cid);
|
||||
|
||||
if (!(caps & CAPFLAG_LOCK)) {
|
||||
// Make sure CAPFLAG reflects behavior.
|
||||
CHECK_EQ(isError(r, ERR_INVALID_CMD), true);
|
||||
return;
|
||||
}
|
||||
|
||||
initFrame(&f, U2Fob_getCid(device), U2FHID_LOCK, 1, "\x03"); // 3 seconds
|
||||
|
||||
SEND(f);
|
||||
RECV(r, 1.0);
|
||||
CHECK_EQ(f.cid, r.cid);
|
||||
|
||||
CHECK_EQ(f.init.cmd, r.init.cmd);
|
||||
CHECK_EQ(0, MSG_LEN(r));
|
||||
|
||||
// We have a lock. CMD_INIT should work whilst another holds lock.
|
||||
|
||||
test_Init(false);
|
||||
test_InitOnNonBroadcastEchoesCID();
|
||||
|
||||
// Unlock.
|
||||
initFrame(&f, U2Fob_getCid(device), U2FHID_LOCK, 1, "\x00");
|
||||
|
||||
SEND(f);
|
||||
RECV(r, 1.0);
|
||||
CHECK_EQ(f.cid, r.cid);
|
||||
|
||||
CHECK_EQ(f.init.cmd, r.init.cmd);
|
||||
CHECK_EQ(0, MSG_LEN(r));
|
||||
}
|
||||
|
||||
void test_Unknown(uint8_t cmd) {
|
||||
U2FHID_FRAME f, r;
|
||||
|
||||
initFrame(&f, U2Fob_getCid(device), cmd, 0);
|
||||
|
||||
SEND(f);
|
||||
RECV(r, 1.0);
|
||||
CHECK_EQ(f.cid, r.cid);
|
||||
|
||||
CHECK_EQ(isError(r, ERR_INVALID_CMD), true);
|
||||
}
|
||||
|
||||
void test_OnlyInitOnBroadcast() {
|
||||
U2FHID_FRAME f, r;
|
||||
|
||||
initFrame(&f, -1, U2FHID_PING, INIT_NONCE_SIZE);
|
||||
|
||||
SEND(f);
|
||||
RECV(r, 1.0);
|
||||
CHECK_EQ(f.cid, r.cid);
|
||||
|
||||
CHECK_EQ(isError(r, ERR_INVALID_CID), true);
|
||||
}
|
||||
|
||||
void test_NothingOnChannel0() {
|
||||
U2FHID_FRAME f, r;
|
||||
|
||||
initFrame(&f, 0, U2FHID_INIT, INIT_NONCE_SIZE);
|
||||
|
||||
SEND(f);
|
||||
RECV(r, 1.0);
|
||||
CHECK_EQ(f.cid, r.cid);
|
||||
|
||||
CHECK_EQ(isError(r, ERR_INVALID_CID), true);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc < 2) {
|
||||
cerr << "Usage: " << argv[0]
|
||||
<< " <device-path> [-a] [-v] [-V] [-p]" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
device = U2Fob_create();
|
||||
|
||||
char* arg_DeviceName = argv[1];
|
||||
|
||||
while (--argc > 1) {
|
||||
if (!strncmp(argv[argc], "-v", 2)) {
|
||||
// INFO only
|
||||
arg_Verbose |= 1;
|
||||
}
|
||||
if (!strncmp(argv[argc], "-V", 2)) {
|
||||
// All logging
|
||||
arg_Verbose |= 2;
|
||||
U2Fob_setLog(device, stdout, -1);
|
||||
}
|
||||
if (!strncmp(argv[argc], "-a", 2)) {
|
||||
// Don't abort, try continue;
|
||||
arg_Abort = false;
|
||||
}
|
||||
if (!strncmp(argv[argc], "-p", 2)) {
|
||||
// Pause at abort
|
||||
arg_Pause = true;
|
||||
}
|
||||
}
|
||||
|
||||
srand((unsigned int) time(NULL));
|
||||
|
||||
// Start of tests
|
||||
//
|
||||
CHECK_EQ(U2Fob_open(device, arg_DeviceName), 0);
|
||||
|
||||
PASS(test_Idle());
|
||||
|
||||
PASS(test_Init());
|
||||
|
||||
// Now that we have INIT, get a proper cid for device.
|
||||
CHECK_EQ(U2Fob_init(device), 0);
|
||||
|
||||
PASS(test_BasicInit());
|
||||
|
||||
PASS(test_Unknown(U2FHID_SYNC));
|
||||
|
||||
PASS(test_InitOnNonBroadcastEchoesCID());
|
||||
PASS(test_InitUnderLock());
|
||||
PASS(test_InitSelfAborts());
|
||||
// PASS(test_InitOther());
|
||||
|
||||
PASS(test_OptionalWink());
|
||||
|
||||
PASS(test_Lock());
|
||||
|
||||
PASS(test_Echo());
|
||||
PASS(test_LongEcho());
|
||||
|
||||
// PASS(test_Timeout());
|
||||
|
||||
PASS(test_WrongSeq());
|
||||
PASS(test_NotCont());
|
||||
PASS(test_NotFirst());
|
||||
|
||||
// PASS(test_Limits());
|
||||
|
||||
// PASS(test_Busy());
|
||||
// PASS(test_Interleave());
|
||||
PASS(test_LeadingZero());
|
||||
|
||||
PASS(test_Idle(2.0));
|
||||
|
||||
PASS(test_NothingOnChannel0());
|
||||
// PASS(test_OnlyInitOnBroadcast());
|
||||
|
||||
U2Fob_destroy(device);
|
||||
|
||||
return 0;
|
||||
}
|
27
tests/fido_tests/u2f-tests-hid/LICENSE
Normal file
27
tests/fido_tests/u2f-tests-hid/LICENSE
Normal file
@ -0,0 +1,27 @@
|
||||
Copyright 2014, Google Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
26
tests/fido_tests/u2f-tests-hid/Makefile
Normal file
26
tests/fido_tests/u2f-tests-hid/Makefile
Normal file
@ -0,0 +1,26 @@
|
||||
# Copyright 2014 Google Inc. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
all: HIDTest U2FTest
|
||||
|
||||
CC ?= gcc
|
||||
CXX ?= g++
|
||||
CFLAGS ?= -Wall
|
||||
|
||||
%.o: %.c
|
||||
$(CC) -c $(CFLAGS) $^
|
||||
|
||||
%.o: %.cc
|
||||
$(CXX) -c $(CFLAGS) $^
|
||||
|
||||
HIDTest: dev.o HIDTest.cc u2f_util.o hidapi-udp.o
|
||||
$(CXX) $(CFLAGS) -o $@ $^
|
||||
|
||||
U2FTest: dev.o U2FTest.o u2f_util.o dsa_sig.o hidapi-udp.o p256.o p256_ec.o p256_ecdsa.o sha256.o
|
||||
$(CXX) $(CFLAGS) -o $@ $^
|
||||
|
||||
clean:
|
||||
rm -f *.o U2FTest
|
41
tests/fido_tests/u2f-tests-hid/README
Normal file
41
tests/fido_tests/u2f-tests-hid/README
Normal file
@ -0,0 +1,41 @@
|
||||
WHAT:
|
||||
A couple of self-contained u2f HID device conformance tests:
|
||||
HIDTest to check compliance at the usb HID communication layer.
|
||||
U2FTest to check compliance at the application layer.
|
||||
|
||||
DEPENDENCIES:
|
||||
git clone https://github.com/signal11/hidapi
|
||||
git clone -b lollipop-release https://android.googlesource.com/platform/system/core
|
||||
|
||||
linux: sudo apt-get install libudev-dev
|
||||
|
||||
BUILD:
|
||||
linux, mac: make
|
||||
|
||||
windows: nmake -f Makefile.win
|
||||
- if you have an old vc compiler, consider adding
|
||||
http://msinttypes.googlecode.com/svn/trunk/stdint.h
|
||||
or similar to your vc include directory.
|
||||
|
||||
RUN:
|
||||
./list
|
||||
to find path of device to test (e.g. /dev/hidraw3)
|
||||
|
||||
./HIDTest $PATH [args]?
|
||||
to test low level communications with device.
|
||||
On windows?
|
||||
- Make sure to keep the quotes around the path.
|
||||
- Try https://github.com/adoxa/ansicon to get ANSI colors.
|
||||
On linux?
|
||||
- Make sure path is rw for your uid. Typically, a udev rule that adds
|
||||
rw for group plugdev goes a long way.
|
||||
|
||||
./U2FTest $PATH [args]?
|
||||
to test u2f application layer functionality of device.
|
||||
|
||||
Additional commandline arguments:
|
||||
Add -a to continue execution after an error.
|
||||
Add -p to pause after each error.
|
||||
Add -v and -V to get more verbose output, down to the usb frames with -V.
|
||||
Add -b to U2FTest in case fob under test is of the insert / remove
|
||||
class and does not have a user-presence button.
|
385
tests/fido_tests/u2f-tests-hid/U2FTest.cc
Normal file
385
tests/fido_tests/u2f-tests-hid/U2FTest.cc
Normal file
@ -0,0 +1,385 @@
|
||||
// Copyright 2014 Google Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
// U2F register / sign compliance test.
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
#ifdef __OS_WIN
|
||||
#include <winsock2.h> // ntohl, htonl
|
||||
#else
|
||||
#include <arpa/inet.h> // ntohl, htonl
|
||||
#endif
|
||||
|
||||
#include "u2f.h"
|
||||
#include "u2f_util.h"
|
||||
|
||||
#include "dsa_sig.h"
|
||||
#include "p256.h"
|
||||
#include "p256_ecdsa.h"
|
||||
#include "sha256.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
int arg_Verbose = 0; // default
|
||||
bool arg_Pause = false; // default
|
||||
bool arg_Abort = true; // default
|
||||
|
||||
static
|
||||
void pause(const string& prompt) {
|
||||
printf("\n%s", prompt.c_str());
|
||||
getchar();
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static
|
||||
void checkPause(const string& prompt) {
|
||||
if (arg_Pause) pause(prompt);
|
||||
}
|
||||
|
||||
static
|
||||
void AbortOrNot() {
|
||||
checkPause("Hit enter to continue..");
|
||||
if (arg_Abort) abort();
|
||||
cerr << "(continuing -a)" << endl;
|
||||
}
|
||||
|
||||
void WaitForUserPresence(struct U2Fob* device, bool hasButton) {
|
||||
int touched = DEV_touch(device);
|
||||
U2Fob_close(device);
|
||||
if (!touched) {
|
||||
pause(string(hasButton ? "Touch" : "Re-insert") + " device and hit enter..");
|
||||
}
|
||||
CHECK_EQ(0, U2Fob_reopen(device));
|
||||
CHECK_EQ(0, U2Fob_init(device));
|
||||
}
|
||||
|
||||
struct U2Fob* device;
|
||||
|
||||
U2F_REGISTER_REQ regReq;
|
||||
U2F_REGISTER_RESP regRsp;
|
||||
U2F_AUTHENTICATE_REQ authReq;
|
||||
|
||||
void test_Version() {
|
||||
string rsp;
|
||||
int res = U2Fob_apdu(device, 0, U2F_INS_VERSION, 0, 0, "", &rsp);
|
||||
if (res == 0x9000) {
|
||||
CHECK_EQ(rsp, "U2F_V2");
|
||||
return;
|
||||
}
|
||||
|
||||
// Non-ISO 7816-4 compliant U2F_INS_VERSION "APDU" that includes Lc value 0,
|
||||
// for compatibility with older devices.
|
||||
uint8_t buf[4 + 3 + 2];
|
||||
buf[0] = 0; // CLA
|
||||
buf[1] = U2F_INS_VERSION; // INS
|
||||
buf[2] = 0; // P1
|
||||
buf[3] = 0; // P2
|
||||
buf[4] = 0; // extended length
|
||||
buf[5] = 0; // Lc = 0 (Not ISO 7816-4 compliant)
|
||||
buf[6] = 0; // Lc = 0 (Not ISO 7816-4 compliant)
|
||||
buf[7] = 0; // Le = 0
|
||||
buf[8] = 0; // Le = 0
|
||||
CHECK_EQ(0x9000, U2Fob_exchange_apdu_buffer(device, buf, sizeof(buf), &rsp));
|
||||
CHECK_EQ(rsp, "U2F_V2");
|
||||
}
|
||||
|
||||
void test_UnknownINS() {
|
||||
string rsp;
|
||||
CHECK_EQ(0x6D00, U2Fob_apdu(device, 0, 0 /* not U2F INS */,
|
||||
0, 0, "", &rsp));
|
||||
CHECK_EQ(rsp.empty(), true);
|
||||
}
|
||||
|
||||
void test_BadCLA() {
|
||||
string rsp;
|
||||
CHECK_EQ(0x6E00, U2Fob_apdu(device, 1 /* not U2F CLA, 0x00 */,
|
||||
U2F_INS_VERSION, 0, 0, "abc", &rsp));
|
||||
CHECK_EQ(rsp.empty(), true);
|
||||
}
|
||||
|
||||
void test_WrongLength_U2F_VERSION() {
|
||||
string rsp;
|
||||
// U2F_VERSION does not take any input.
|
||||
CHECK_EQ(0x6700, U2Fob_apdu(device, 0, U2F_INS_VERSION, 0, 0, "abc", &rsp));
|
||||
CHECK_EQ(rsp.empty(), true);
|
||||
}
|
||||
|
||||
void test_WrongLength_U2F_REGISTER() {
|
||||
string rsp;
|
||||
// U2F_REGISTER does expect input.
|
||||
CHECK_EQ(0x6700, U2Fob_apdu(device, 0, U2F_INS_REGISTER, 0, 0, "abc", &rsp));
|
||||
CHECK_EQ(rsp.empty(), true);
|
||||
}
|
||||
|
||||
void test_Enroll(int expectedSW12 = 0x9000) {
|
||||
uint64_t t = 0; U2Fob_deltaTime(&t);
|
||||
|
||||
string rsp;
|
||||
CHECK_EQ(expectedSW12,
|
||||
U2Fob_apdu(device, 0, U2F_INS_REGISTER, U2F_AUTH_ENFORCE, 0,
|
||||
string(reinterpret_cast<char*>(®Req),
|
||||
sizeof(regReq)),
|
||||
&rsp));
|
||||
|
||||
if (expectedSW12 != 0x9000) {
|
||||
CHECK_EQ(true, rsp.empty());
|
||||
return;
|
||||
}
|
||||
|
||||
CHECK_NE(rsp.empty(), true);
|
||||
CHECK_LE(rsp.size(), sizeof(U2F_REGISTER_RESP));
|
||||
|
||||
memcpy(®Rsp, rsp.data(), rsp.size());
|
||||
|
||||
CHECK_EQ(regRsp.registerId, U2F_REGISTER_ID);
|
||||
CHECK_EQ(regRsp.pubKey.format, UNCOMPRESSED_POINT);
|
||||
|
||||
INFO << "Enroll: " << rsp.size() << " bytes in "
|
||||
<< U2Fob_deltaTime(&t) << "s";
|
||||
|
||||
// Check crypto of enroll response.
|
||||
string cert;
|
||||
CHECK_EQ(getCertificate(regRsp, &cert), true);
|
||||
INFO << "cert: " << b2a(cert);
|
||||
|
||||
string pk;
|
||||
CHECK_EQ(getSubjectPublicKey(cert, &pk), true);
|
||||
INFO << "pk : " << b2a(pk);
|
||||
|
||||
string sig;
|
||||
CHECK_EQ(getSignature(regRsp, &sig), true);
|
||||
INFO << "sig : " << b2a(sig);
|
||||
|
||||
// Parse signature into two integers.
|
||||
p256_int sig_r, sig_s;
|
||||
CHECK_EQ(1, dsa_sig_unpack((uint8_t*) (sig.data()), sig.size(),
|
||||
&sig_r, &sig_s));
|
||||
|
||||
// Compute hash as integer.
|
||||
p256_int h;
|
||||
SHA256_CTX sha;
|
||||
SHA256_init(&sha);
|
||||
uint8_t rfu = 0;
|
||||
SHA256_update(&sha, &rfu, sizeof(rfu)); // 0x00
|
||||
SHA256_update(&sha, regReq.appId, sizeof(regReq.appId)); // O
|
||||
SHA256_update(&sha, regReq.nonce, sizeof(regReq.nonce)); // d
|
||||
SHA256_update(&sha, regRsp.keyHandleCertSig, regRsp.keyHandleLen); // hk
|
||||
SHA256_update(&sha, ®Rsp.pubKey, sizeof(regRsp.pubKey)); // pk
|
||||
p256_from_bin(SHA256_final(&sha), &h);
|
||||
|
||||
// Parse subject public key into two integers.
|
||||
CHECK_EQ(pk.size(), P256_POINT_SIZE);
|
||||
p256_int pk_x, pk_y;
|
||||
p256_from_bin((uint8_t*) pk.data() + 1, &pk_x);
|
||||
p256_from_bin((uint8_t*) pk.data() + 1 + P256_SCALAR_SIZE, &pk_y);
|
||||
|
||||
// Verify signature.
|
||||
CHECK_EQ(1, p256_ecdsa_verify(&pk_x, &pk_y, &h, &sig_r, &sig_s));
|
||||
|
||||
#if 0
|
||||
// Check for standard U2F self-signed certificate.
|
||||
// Implementations without batch attestation should use this minimalist
|
||||
// self-signed certificate for enroll.
|
||||
// Conforming to a standard self-signed certficate format and attributes
|
||||
// bins all such fobs into a single large batch, which helps privacy.
|
||||
string selfSigned = a2b(
|
||||
"3081B3A003020102020101300A06082A8648CE3D040302300E310C300A060355040A0C035532463022180F32303030303130313030303030305A180F32303939313233313233353935395A300E310C300A060355040313035532463059301306072A8648CE3D020106082A8648CE3D030107034200") + pk;
|
||||
|
||||
SHA256_init(&sha);
|
||||
SHA256_update(&sha, selfSigned.data(), selfSigned.size());
|
||||
p256_from_bin(SHA256_final(&sha), &h);
|
||||
|
||||
string certSig;
|
||||
CHECK_EQ(getCertSignature(cert, &certSig), true);
|
||||
INFO << "certSig : " << b2a(certSig);
|
||||
|
||||
CHECK_EQ(1, dsa_sig_unpack((uint8_t*) (certSig.data()), certSig.size(),
|
||||
&sig_r, &sig_s));
|
||||
// Verify cert signature.
|
||||
CHECK_EQ(1, p256_ecdsa_verify(&pk_x, &pk_y, &h, &sig_r, &sig_s));
|
||||
#endif
|
||||
}
|
||||
|
||||
// returns ctr
|
||||
uint32_t test_Sign(int expectedSW12 = 0x9000, bool checkOnly = false) {
|
||||
memcpy(authReq.appId, regReq.appId, sizeof(authReq.appId));
|
||||
authReq.keyHandleLen = regRsp.keyHandleLen;
|
||||
memcpy(authReq.keyHandle, regRsp.keyHandleCertSig, authReq.keyHandleLen);
|
||||
|
||||
uint64_t t = 0; U2Fob_deltaTime(&t);
|
||||
|
||||
string rsp;
|
||||
CHECK_EQ(expectedSW12,
|
||||
U2Fob_apdu(device, 0, U2F_INS_AUTHENTICATE,
|
||||
checkOnly ? U2F_AUTH_CHECK_ONLY : U2F_AUTH_ENFORCE, 0,
|
||||
string(reinterpret_cast<char*>(&authReq),
|
||||
U2F_NONCE_SIZE + U2F_APPID_SIZE + 1 +
|
||||
authReq.keyHandleLen),
|
||||
&rsp));
|
||||
|
||||
if (expectedSW12 != 0x9000) {
|
||||
CHECK_EQ(true, rsp.empty());
|
||||
return 0;
|
||||
}
|
||||
|
||||
CHECK_NE(rsp.empty(), true);
|
||||
CHECK_LE(rsp.size(), sizeof(U2F_AUTHENTICATE_RESP));
|
||||
|
||||
U2F_AUTHENTICATE_RESP resp;
|
||||
memcpy(&resp, rsp.data(), rsp.size());
|
||||
|
||||
CHECK_EQ(resp.flags, 0x01);
|
||||
|
||||
INFO << "Sign: " << rsp.size() << " bytes in "
|
||||
<< U2Fob_deltaTime(&t) << "s";
|
||||
|
||||
// Parse signature from authenticate response.
|
||||
p256_int sig_r, sig_s;
|
||||
CHECK_EQ(1, dsa_sig_unpack(resp.sig,
|
||||
rsp.size() - sizeof(resp.flags) - sizeof(resp.ctr),
|
||||
&sig_r, &sig_s));
|
||||
|
||||
// Compute hash as integer.
|
||||
p256_int h;
|
||||
SHA256_CTX sha;
|
||||
SHA256_init(&sha);
|
||||
SHA256_update(&sha, regReq.appId, sizeof(regReq.appId)); // O
|
||||
SHA256_update(&sha, &resp.flags, sizeof(resp.flags)); // T
|
||||
SHA256_update(&sha, &resp.ctr, sizeof(resp.ctr)); // CTR
|
||||
SHA256_update(&sha, authReq.nonce, sizeof(authReq.nonce)); // d
|
||||
p256_from_bin(SHA256_final(&sha), &h);
|
||||
|
||||
// Parse public key from registration response.
|
||||
p256_int pk_x, pk_y;
|
||||
p256_from_bin(regRsp.pubKey.x, &pk_x);
|
||||
p256_from_bin(regRsp.pubKey.y, &pk_y);
|
||||
|
||||
// Verify signature.
|
||||
CHECK_EQ(1, p256_ecdsa_verify(&pk_x, &pk_y, &h, &sig_r, &sig_s));
|
||||
|
||||
return ntohl(resp.ctr);
|
||||
}
|
||||
|
||||
void check_Compilation() {
|
||||
// Couple of sanity checks.
|
||||
CHECK_EQ(sizeof(P256_POINT), 65);
|
||||
CHECK_EQ(sizeof(U2F_REGISTER_REQ), 64);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc < 2) {
|
||||
cerr << "Usage: " << argv[0]
|
||||
<< " <device-path> [-a] [-v] [-V] [-p] [-b]" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
device = U2Fob_create();
|
||||
|
||||
char* arg_DeviceName = argv[1];
|
||||
bool arg_hasButton = true; // fob has button
|
||||
|
||||
while (--argc > 1) {
|
||||
if (!strncmp(argv[argc], "-v", 2)) {
|
||||
// Log INFO.
|
||||
arg_Verbose |= 1;
|
||||
}
|
||||
if (!strncmp(argv[argc], "-V", 2)) {
|
||||
// Log everything.
|
||||
arg_Verbose |= 2;
|
||||
U2Fob_setLog(device, stdout, -1);
|
||||
}
|
||||
if (!strncmp(argv[argc], "-a", 2)) {
|
||||
// Don't abort, try continue;
|
||||
arg_Abort = false;
|
||||
}
|
||||
if (!strncmp(argv[argc], "-p", 2)) {
|
||||
// Pause at abort
|
||||
arg_Pause = true;
|
||||
}
|
||||
if (!strncmp(argv[argc], "-b", 2)) {
|
||||
// Fob does not have button
|
||||
arg_hasButton = false;
|
||||
}
|
||||
}
|
||||
|
||||
srand((unsigned int) time(NULL));
|
||||
|
||||
CHECK_EQ(0, U2Fob_open(device, arg_DeviceName));
|
||||
CHECK_EQ(0, U2Fob_init(device));
|
||||
|
||||
PASS(check_Compilation());
|
||||
|
||||
PASS(test_Version());
|
||||
PASS(test_UnknownINS());
|
||||
PASS(test_WrongLength_U2F_VERSION());
|
||||
PASS(test_WrongLength_U2F_REGISTER());
|
||||
PASS(test_BadCLA());
|
||||
|
||||
// pick random origin and challenge.
|
||||
for (size_t i = 0; i < sizeof(regReq.nonce); ++i)
|
||||
regReq.nonce[i] = rand();
|
||||
for (size_t i = 0; i < sizeof(regReq.appId); ++i)
|
||||
regReq.appId[i] = rand();
|
||||
|
||||
// Fob with button should need touch.
|
||||
if (arg_hasButton) PASS(test_Enroll(0x6985));
|
||||
|
||||
WaitForUserPresence(device, arg_hasButton);
|
||||
|
||||
PASS(test_Enroll(0x9000));
|
||||
|
||||
// pick random challenge and use registered appId.
|
||||
for (size_t i = 0; i < sizeof(authReq.nonce); ++i)
|
||||
authReq.nonce[i] = rand();
|
||||
|
||||
// Fob with button should have consumed touch.
|
||||
if (arg_hasButton) PASS(test_Sign(0x6985));
|
||||
|
||||
// Sign with check only should not produce signature.
|
||||
PASS(test_Sign(0x6985, true));
|
||||
|
||||
// Sign with wrong hk.
|
||||
regRsp.keyHandleCertSig[0] ^= 0x55;
|
||||
PASS(test_Sign(0x6a80));
|
||||
regRsp.keyHandleCertSig[0] ^= 0x55;
|
||||
|
||||
// Sign with wrong appid.
|
||||
regReq.appId[0] ^= 0xaa;
|
||||
PASS(test_Sign(0x6a80));
|
||||
regReq.appId[0] ^= 0xaa;
|
||||
|
||||
WaitForUserPresence(device, arg_hasButton);
|
||||
|
||||
// Sign with check only should not produce signature.
|
||||
PASS(test_Sign(0x6985, true));
|
||||
|
||||
uint32_t ctr1;
|
||||
PASS(ctr1 = test_Sign(0x9000));
|
||||
PASS(test_Sign(0x6985));
|
||||
|
||||
WaitForUserPresence(device, arg_hasButton);
|
||||
|
||||
uint32_t ctr2;
|
||||
PASS(ctr2 = test_Sign(0x9000));
|
||||
|
||||
// Ctr should have incremented by 1.
|
||||
CHECK_EQ(ctr2, ctr1 + 1);
|
||||
|
||||
regRsp.keyHandleLen -= 8; // perturb keyhandle length
|
||||
PASS(test_Sign(0x6700, false));
|
||||
|
||||
//DEV_quit(device); // stop emulator, if any
|
||||
U2Fob_destroy(device);
|
||||
return 0;
|
||||
}
|
1
tests/fido_tests/u2f-tests-hid/UPSTREAM
Normal file
1
tests/fido_tests/u2f-tests-hid/UPSTREAM
Normal file
@ -0,0 +1 @@
|
||||
https://github.com/google/u2f-ref-code/tree/master/u2f-tests/HID
|
147
tests/fido_tests/u2f-tests-hid/dev.cc
Normal file
147
tests/fido_tests/u2f-tests-hid/dev.cc
Normal file
@ -0,0 +1,147 @@
|
||||
// Copyright Google Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
// Thin shim to allow abstracting away from hid_* calls to
|
||||
// a set or read/write pipes w/ a particular interface.
|
||||
#include <assert.h>
|
||||
#include <endian.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "u2f_util.h"
|
||||
|
||||
bool DEV_opened(struct U2Fob* device) {
|
||||
return device->dev != NULL || (device->fd_in != -1 && device->fd_out != -1);
|
||||
}
|
||||
|
||||
void DEV_close(struct U2Fob* device) {
|
||||
if (device->dev) {
|
||||
hid_close(device->dev);
|
||||
device->dev = NULL;
|
||||
}
|
||||
if (device->fd_in != -1) {
|
||||
close(device->fd_in);
|
||||
device->fd_in = -1;
|
||||
}
|
||||
if (device->fd_out != -1) {
|
||||
close(device->fd_out);
|
||||
device->fd_out = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void DEV_open_path(struct U2Fob* device) {
|
||||
std::string path(device->path);
|
||||
if (path[path.size() - 1] == '-') {
|
||||
device->fd_in = open((path + "out").c_str(), O_RDONLY);
|
||||
device->fd_out = open((path + "in").c_str(), O_RDWR);
|
||||
} else {
|
||||
device->dev = hid_open_path(device->path);
|
||||
}
|
||||
}
|
||||
|
||||
void DEV_quit(struct U2Fob* device) {
|
||||
if (device->dev) return;
|
||||
|
||||
struct {
|
||||
uint32_t cmd;
|
||||
uint32_t len;
|
||||
uint32_t mode;
|
||||
} data;
|
||||
|
||||
data.cmd = htole32(2); // kReset
|
||||
data.len = htole32(4);
|
||||
data.mode = htole32(3); // kResetQuit
|
||||
assert(write(device->fd_out, (const void*)&data, sizeof(data)) == sizeof(data));
|
||||
}
|
||||
|
||||
int DEV_write(struct U2Fob* device, const uint8_t* src, size_t n) {
|
||||
assert(n == 65);
|
||||
|
||||
if (device->dev != NULL) return hid_write(device->dev, src, n);
|
||||
|
||||
struct {
|
||||
uint32_t cmd;
|
||||
uint32_t len;
|
||||
uint8_t data[64];
|
||||
} data;
|
||||
|
||||
static_assert(sizeof(data) == 64 + 8);
|
||||
|
||||
data.cmd = htole32(4); // k64ByteWrite
|
||||
data.len = htole32(64);
|
||||
memcpy(data.data, src + 1, 64);
|
||||
|
||||
int nwritten = write(device->fd_out, (const void*)&data, sizeof(data));
|
||||
assert(nwritten == sizeof(data));
|
||||
usleep(1500); // delay a bit to mimic HID transport.
|
||||
return 65;
|
||||
}
|
||||
|
||||
int DEV_read_timeout(struct U2Fob* device, uint8_t* dst, size_t n,
|
||||
int timeout) {
|
||||
assert(n == 64);
|
||||
|
||||
if (device->dev != NULL)
|
||||
return hid_read_timeout(device->dev, dst, n, timeout);
|
||||
|
||||
struct {
|
||||
uint32_t cmd;
|
||||
uint32_t len;
|
||||
} data;
|
||||
|
||||
static_assert(sizeof(data) == 8);
|
||||
|
||||
data.cmd = htole32(5); // k64ByteRead
|
||||
data.len = htole32(0);
|
||||
|
||||
uint64_t t0, t1;
|
||||
float dt;
|
||||
U2Fob_deltaTime(&t0);
|
||||
|
||||
// Poll until timeout
|
||||
do {
|
||||
assert(write(device->fd_out, (uint8_t*)&data, sizeof(data)) ==
|
||||
sizeof(data));
|
||||
assert(read(device->fd_in, (uint8_t*)&data, sizeof(data)) == sizeof(data));
|
||||
|
||||
assert(data.cmd == htole32(5));
|
||||
if (data.len == htole32(64)) break;
|
||||
|
||||
assert(data.len == 0);
|
||||
|
||||
usleep(100);
|
||||
|
||||
t1 = t0;
|
||||
dt = U2Fob_deltaTime(&t1);
|
||||
} while (dt < timeout / 1000.0);
|
||||
|
||||
if (data.len == 0) return 0;
|
||||
|
||||
assert(data.len == htole32(64));
|
||||
assert(read(device->fd_in, dst, 64) == 64);
|
||||
return 64;
|
||||
}
|
||||
|
||||
int DEV_touch(struct U2Fob* device) {
|
||||
if (device->dev != NULL) return 0;
|
||||
|
||||
struct {
|
||||
uint32_t cmd;
|
||||
uint32_t len;
|
||||
uint32_t number;
|
||||
} data;
|
||||
|
||||
static_assert(sizeof(data) == 12);
|
||||
|
||||
data.cmd = htole32(6); // kRaiseInterrupt
|
||||
data.len = htole32(sizeof(uint32_t));
|
||||
data.number = htole32(199); // touch toggle handler
|
||||
|
||||
assert(write(device->fd_out, (uint8_t*)&data, sizeof(data)) == sizeof(data));
|
||||
return 1;
|
||||
}
|
126
tests/fido_tests/u2f-tests-hid/dsa_sig.c
Normal file
126
tests/fido_tests/u2f-tests-hid/dsa_sig.c
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2013 The Android Open Source Project
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "dsa_sig.h"
|
||||
#include "p256.h"
|
||||
|
||||
/**
|
||||
* Trims off the leading zero bytes and copy it to a buffer aligning it to the end.
|
||||
*/
|
||||
static inline int trim_to_p256_bytes(unsigned char dst[P256_NBYTES], unsigned char *src,
|
||||
int src_len) {
|
||||
int dst_offset;
|
||||
while (*src == '\0' && src_len > 0) {
|
||||
src++;
|
||||
src_len--;
|
||||
}
|
||||
if (src_len > P256_NBYTES || src_len < 1) {
|
||||
return 0;
|
||||
}
|
||||
dst_offset = P256_NBYTES - src_len;
|
||||
memset(dst, 0, dst_offset);
|
||||
memcpy(dst + dst_offset, src, src_len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpacks the ASN.1 DSA signature sequence.
|
||||
*/
|
||||
int dsa_sig_unpack(unsigned char* sig, int sig_len, p256_int* r_int, p256_int* s_int) {
|
||||
/*
|
||||
* Structure is:
|
||||
* 0x30 0xNN SEQUENCE + s_length
|
||||
* 0x02 0xNN INTEGER + r_length
|
||||
* 0xAA 0xBB .. r_length bytes of "r" (offset 4)
|
||||
* 0x02 0xNN INTEGER + s_length
|
||||
* 0xMM 0xNN .. s_length bytes of "s" (offset 6 + r_len)
|
||||
*/
|
||||
int seq_len;
|
||||
unsigned char r_bytes[P256_NBYTES];
|
||||
unsigned char s_bytes[P256_NBYTES];
|
||||
int r_len;
|
||||
int s_len;
|
||||
|
||||
memset(r_bytes, 0, sizeof(r_bytes));
|
||||
memset(s_bytes, 0, sizeof(s_bytes));
|
||||
|
||||
/*
|
||||
* Must have at least:
|
||||
* 2 bytes sequence header and length
|
||||
* 2 bytes R integer header and length
|
||||
* 1 byte of R
|
||||
* 2 bytes S integer header and length
|
||||
* 1 byte of S
|
||||
*
|
||||
* 8 bytes total
|
||||
*/
|
||||
if (sig_len < 8 || sig[0] != 0x30 || sig[2] != 0x02) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
seq_len = sig[1];
|
||||
if ((seq_len <= 0) || (seq_len + 2 != sig_len)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
r_len = sig[3];
|
||||
/*
|
||||
* Must have at least:
|
||||
* 2 bytes for R header and length
|
||||
* 2 bytes S integer header and length
|
||||
* 1 byte of S
|
||||
*/
|
||||
if ((r_len < 1) || (r_len > seq_len - 5) || (sig[4 + r_len] != 0x02)) {
|
||||
return 0;
|
||||
}
|
||||
s_len = sig[5 + r_len];
|
||||
|
||||
/**
|
||||
* Must have:
|
||||
* 2 bytes for R header and length
|
||||
* r_len bytes for R
|
||||
* 2 bytes S integer header and length
|
||||
*/
|
||||
if ((s_len < 1) || (s_len != seq_len - 4 - r_len)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ASN.1 encoded integers are zero-padded for positive integers. Make sure we have
|
||||
* a correctly-sized buffer and that the resulting integer isn't too large.
|
||||
*/
|
||||
if (!trim_to_p256_bytes(r_bytes, &sig[4], r_len)
|
||||
|| !trim_to_p256_bytes(s_bytes, &sig[6 + r_len], s_len)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
p256_from_bin(r_bytes, r_int);
|
||||
p256_from_bin(s_bytes, s_int);
|
||||
|
||||
return 1;
|
||||
}
|
43
tests/fido_tests/u2f-tests-hid/dsa_sig.h
Normal file
43
tests/fido_tests/u2f-tests-hid/dsa_sig.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2013 The Android Open Source Project
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_DSA_SIG_H_
|
||||
#define SYSTEM_CORE_INCLUDE_MINCRYPT_DSA_SIG_H_
|
||||
|
||||
#include "p256.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Returns 0 if input sig is not a valid ASN.1 sequence
|
||||
int dsa_sig_unpack(unsigned char* sig, int sig_len, p256_int* r_int, p256_int* s_int);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SYSTEM_CORE_INCLUDE_MINCRYPT_DSA_SIG_H_ */
|
63
tests/fido_tests/u2f-tests-hid/hash-internal.h
Normal file
63
tests/fido_tests/u2f-tests-hid/hash-internal.h
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2007 The Android Open Source Project
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_
|
||||
#define SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
struct HASH_CTX; // forward decl
|
||||
|
||||
typedef struct HASH_VTAB {
|
||||
void (* const init)(struct HASH_CTX*);
|
||||
void (* const update)(struct HASH_CTX*, const void*, int);
|
||||
const uint8_t* (* const final)(struct HASH_CTX*);
|
||||
const uint8_t* (* const hash)(const void*, int, uint8_t*);
|
||||
int size;
|
||||
} HASH_VTAB;
|
||||
|
||||
typedef struct HASH_CTX {
|
||||
const HASH_VTAB * f;
|
||||
uint64_t count;
|
||||
uint8_t buf[64];
|
||||
uint32_t state[8]; // upto SHA2
|
||||
} HASH_CTX;
|
||||
|
||||
#define HASH_init(ctx) (ctx)->f->init(ctx)
|
||||
#define HASH_update(ctx, data, len) (ctx)->f->update(ctx, data, len)
|
||||
#define HASH_final(ctx) (ctx)->f->final(ctx)
|
||||
#define HASH_hash(data, len, digest) (ctx)->f->hash(data, len, digest)
|
||||
#define HASH_size(ctx) (ctx)->f->size
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_
|
93
tests/fido_tests/u2f-tests-hid/hidapi-udp.c
Normal file
93
tests/fido_tests/u2f-tests-hid/hidapi-udp.c
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* This file is part of the Trezor project, https://trezor.io/
|
||||
*
|
||||
* Copyright (c) SatoshiLabs
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "hidapi.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
int hid_init(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hid_exit(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
hid_device *hid_open_path(const char *path) {
|
||||
|
||||
int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Failed to create socket\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hid_device *d = malloc(sizeof(hid_device));
|
||||
d->fd = fd;
|
||||
|
||||
d->other.sin_family = AF_INET;
|
||||
d->other.sin_port = htons(atoi(path));
|
||||
d->other.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
d->slen = sizeof(d->other);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
void hid_close(hid_device *device) {
|
||||
if (device && device->fd) {
|
||||
close(device->fd);
|
||||
}
|
||||
if (device) {
|
||||
free(device);
|
||||
}
|
||||
}
|
||||
|
||||
int hid_write(hid_device *device, const unsigned char *data, size_t length) {
|
||||
if (length != 65) {
|
||||
fprintf(stderr, "Invalid packet size\n");
|
||||
return -1;
|
||||
}
|
||||
ssize_t n = sendto(device->fd, data + 1, length - 1, 0, (const struct sockaddr *)&(device->other), device->slen);
|
||||
if (n < 0 || ((size_t)n) != length - 1) {
|
||||
fprintf(stderr, "Failed to write socket\n");
|
||||
return -1;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
int hid_read_timeout(hid_device *device, unsigned char *data, size_t length, int milliseconds) {
|
||||
for (int i = 0; i < milliseconds; i++) {
|
||||
ssize_t n = recvfrom(device->fd, data, length, MSG_DONTWAIT, (struct sockaddr *)&(device->other), &(device->slen));
|
||||
if (n < 0) {
|
||||
if (errno == EAGAIN && errno == EWOULDBLOCK) { // timeout tick
|
||||
usleep(1000);
|
||||
continue;
|
||||
} else {
|
||||
fprintf(stderr, "Failed to read socket\n");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return n;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
33
tests/fido_tests/u2f-tests-hid/hidapi.h
Normal file
33
tests/fido_tests/u2f-tests-hid/hidapi.h
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef __HIDAPI_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int fd;
|
||||
struct sockaddr_in other;
|
||||
socklen_t slen;
|
||||
} hid_device;
|
||||
|
||||
int hid_init(void);
|
||||
|
||||
int hid_exit(void);
|
||||
|
||||
hid_device *hid_open_path(const char *path);
|
||||
|
||||
void hid_close(hid_device *device);
|
||||
|
||||
int hid_write(hid_device *device, const unsigned char *data, size_t length);
|
||||
|
||||
int hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
373
tests/fido_tests/u2f-tests-hid/p256.c
Normal file
373
tests/fido_tests/u2f-tests-hid/p256.c
Normal file
@ -0,0 +1,373 @@
|
||||
/*
|
||||
* Copyright 2013 The Android Open Source Project
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// This is an implementation of the P256 elliptic curve group. It's written to
|
||||
// be portable 32-bit, although it's still constant-time.
|
||||
//
|
||||
// WARNING: Implementing these functions in a constant-time manner is far from
|
||||
// obvious. Be careful when touching this code.
|
||||
//
|
||||
// See http://www.imperialviolet.org/2010/12/04/ecc.html ([1]) for background.
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "p256.h"
|
||||
|
||||
const p256_int SECP256r1_n = // curve order
|
||||
{{0xfc632551, 0xf3b9cac2, 0xa7179e84, 0xbce6faad, -1, -1, 0, -1}};
|
||||
|
||||
const p256_int SECP256r1_p = // curve field size
|
||||
{{-1, -1, -1, 0, 0, 0, 1, -1 }};
|
||||
|
||||
const p256_int SECP256r1_b = // curve b
|
||||
{{0x27d2604b, 0x3bce3c3e, 0xcc53b0f6, 0x651d06b0,
|
||||
0x769886bc, 0xb3ebbd55, 0xaa3a93e7, 0x5ac635d8}};
|
||||
|
||||
void p256_init(p256_int* a) {
|
||||
memset(a, 0, sizeof(*a));
|
||||
}
|
||||
|
||||
void p256_clear(p256_int* a) { p256_init(a); }
|
||||
|
||||
int p256_get_bit(const p256_int* scalar, int bit) {
|
||||
return (P256_DIGIT(scalar, bit / P256_BITSPERDIGIT)
|
||||
>> (bit & (P256_BITSPERDIGIT - 1))) & 1;
|
||||
}
|
||||
|
||||
int p256_is_zero(const p256_int* a) {
|
||||
int i, result = 0;
|
||||
for (i = 0; i < P256_NDIGITS; ++i) result |= P256_DIGIT(a, i);
|
||||
return !result;
|
||||
}
|
||||
|
||||
// top, c[] += a[] * b
|
||||
// Returns new top
|
||||
static p256_digit mulAdd(const p256_int* a,
|
||||
p256_digit b,
|
||||
p256_digit top,
|
||||
p256_digit* c) {
|
||||
int i;
|
||||
p256_ddigit carry = 0;
|
||||
|
||||
for (i = 0; i < P256_NDIGITS; ++i) {
|
||||
carry += *c;
|
||||
carry += (p256_ddigit)P256_DIGIT(a, i) * b;
|
||||
*c++ = (p256_digit)carry;
|
||||
carry >>= P256_BITSPERDIGIT;
|
||||
}
|
||||
return top + (p256_digit)carry;
|
||||
}
|
||||
|
||||
// top, c[] -= top_a, a[]
|
||||
static p256_digit subTop(p256_digit top_a,
|
||||
const p256_digit* a,
|
||||
p256_digit top_c,
|
||||
p256_digit* c) {
|
||||
int i;
|
||||
p256_sddigit borrow = 0;
|
||||
|
||||
for (i = 0; i < P256_NDIGITS; ++i) {
|
||||
borrow += *c;
|
||||
borrow -= *a++;
|
||||
*c++ = (p256_digit)borrow;
|
||||
borrow >>= P256_BITSPERDIGIT;
|
||||
}
|
||||
borrow += top_c;
|
||||
borrow -= top_a;
|
||||
top_c = (p256_digit)borrow;
|
||||
assert((borrow >> P256_BITSPERDIGIT) == 0);
|
||||
return top_c;
|
||||
}
|
||||
|
||||
// top, c[] -= MOD[] & mask (0 or -1)
|
||||
// returns new top.
|
||||
static p256_digit subM(const p256_int* MOD,
|
||||
p256_digit top,
|
||||
p256_digit* c,
|
||||
p256_digit mask) {
|
||||
int i;
|
||||
p256_sddigit borrow = 0;
|
||||
for (i = 0; i < P256_NDIGITS; ++i) {
|
||||
borrow += *c;
|
||||
borrow -= P256_DIGIT(MOD, i) & mask;
|
||||
*c++ = (p256_digit)borrow;
|
||||
borrow >>= P256_BITSPERDIGIT;
|
||||
}
|
||||
return top + (p256_digit)borrow;
|
||||
}
|
||||
|
||||
// top, c[] += MOD[] & mask (0 or -1)
|
||||
// returns new top.
|
||||
static p256_digit addM(const p256_int* MOD,
|
||||
p256_digit top,
|
||||
p256_digit* c,
|
||||
p256_digit mask) {
|
||||
int i;
|
||||
p256_ddigit carry = 0;
|
||||
for (i = 0; i < P256_NDIGITS; ++i) {
|
||||
carry += *c;
|
||||
carry += P256_DIGIT(MOD, i) & mask;
|
||||
*c++ = (p256_digit)carry;
|
||||
carry >>= P256_BITSPERDIGIT;
|
||||
}
|
||||
return top + (p256_digit)carry;
|
||||
}
|
||||
|
||||
// c = a * b mod MOD. c can be a and/or b.
|
||||
void p256_modmul(const p256_int* MOD,
|
||||
const p256_int* a,
|
||||
const p256_digit top_b,
|
||||
const p256_int* b,
|
||||
p256_int* c) {
|
||||
p256_digit tmp[P256_NDIGITS * 2 + 1] = { 0 };
|
||||
p256_digit top = 0;
|
||||
int i;
|
||||
|
||||
// Multiply/add into tmp.
|
||||
for (i = 0; i < P256_NDIGITS; ++i) {
|
||||
if (i) tmp[i + P256_NDIGITS - 1] = top;
|
||||
top = mulAdd(a, P256_DIGIT(b, i), 0, tmp + i);
|
||||
}
|
||||
|
||||
// Multiply/add top digit
|
||||
tmp[i + P256_NDIGITS - 1] = top;
|
||||
top = mulAdd(a, top_b, 0, tmp + i);
|
||||
|
||||
// Reduce tmp, digit by digit.
|
||||
for (; i >= 0; --i) {
|
||||
p256_digit reducer[P256_NDIGITS] = { 0 };
|
||||
p256_digit top_reducer;
|
||||
|
||||
// top can be any value at this point.
|
||||
// Guestimate reducer as top * MOD, since msw of MOD is -1.
|
||||
top_reducer = mulAdd(MOD, top, 0, reducer);
|
||||
|
||||
// Subtract reducer from top | tmp.
|
||||
top = subTop(top_reducer, reducer, top, tmp + i);
|
||||
|
||||
// top is now either 0 or 1. Make it 0, fixed-timing.
|
||||
assert(top <= 1);
|
||||
|
||||
top = subM(MOD, top, tmp + i, ~(top - 1));
|
||||
|
||||
assert(top == 0);
|
||||
|
||||
// We have now reduced the top digit off tmp. Fetch new top digit.
|
||||
top = tmp[i + P256_NDIGITS - 1];
|
||||
}
|
||||
|
||||
// tmp might still be larger than MOD, yet same bit length.
|
||||
// Make sure it is less, fixed-timing.
|
||||
addM(MOD, 0, tmp, subM(MOD, 0, tmp, -1));
|
||||
|
||||
memcpy(c, tmp, P256_NBYTES);
|
||||
}
|
||||
int p256_is_odd(const p256_int* a) { return P256_DIGIT(a, 0) & 1; }
|
||||
int p256_is_even(const p256_int* a) { return !(P256_DIGIT(a, 0) & 1); }
|
||||
|
||||
p256_digit p256_shl(const p256_int* a, int n, p256_int* b) {
|
||||
int i;
|
||||
p256_digit top = P256_DIGIT(a, P256_NDIGITS - 1);
|
||||
|
||||
n %= P256_BITSPERDIGIT;
|
||||
for (i = P256_NDIGITS - 1; i > 0; --i) {
|
||||
p256_digit accu = (P256_DIGIT(a, i) << n);
|
||||
accu |= (P256_DIGIT(a, i - 1) >> (P256_BITSPERDIGIT - n));
|
||||
P256_DIGIT(b, i) = accu;
|
||||
}
|
||||
P256_DIGIT(b, i) = (P256_DIGIT(a, i) << n);
|
||||
|
||||
top = (p256_digit)((((p256_ddigit)top) << n) >> P256_BITSPERDIGIT);
|
||||
|
||||
return top;
|
||||
}
|
||||
|
||||
void p256_shr(const p256_int* a, int n, p256_int* b) {
|
||||
int i;
|
||||
|
||||
n %= P256_BITSPERDIGIT;
|
||||
for (i = 0; i < P256_NDIGITS - 1; ++i) {
|
||||
p256_digit accu = (P256_DIGIT(a, i) >> n);
|
||||
accu |= (P256_DIGIT(a, i + 1) << (P256_BITSPERDIGIT - n));
|
||||
P256_DIGIT(b, i) = accu;
|
||||
}
|
||||
P256_DIGIT(b, i) = (P256_DIGIT(a, i) >> n);
|
||||
}
|
||||
|
||||
static void p256_shr1(const p256_int* a, int highbit, p256_int* b) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < P256_NDIGITS - 1; ++i) {
|
||||
p256_digit accu = (P256_DIGIT(a, i) >> 1);
|
||||
accu |= (P256_DIGIT(a, i + 1) << (P256_BITSPERDIGIT - 1));
|
||||
P256_DIGIT(b, i) = accu;
|
||||
}
|
||||
P256_DIGIT(b, i) = (P256_DIGIT(a, i) >> 1) |
|
||||
(highbit << (P256_BITSPERDIGIT - 1));
|
||||
}
|
||||
|
||||
// Return -1, 0, 1 for a < b, a == b or a > b respectively.
|
||||
int p256_cmp(const p256_int* a, const p256_int* b) {
|
||||
int i;
|
||||
p256_sddigit borrow = 0;
|
||||
p256_digit notzero = 0;
|
||||
|
||||
for (i = 0; i < P256_NDIGITS; ++i) {
|
||||
borrow += (p256_sddigit)P256_DIGIT(a, i) - P256_DIGIT(b, i);
|
||||
// Track whether any result digit is ever not zero.
|
||||
// Relies on !!(non-zero) evaluating to 1, e.g., !!(-1) evaluating to 1.
|
||||
notzero |= !!((p256_digit)borrow);
|
||||
borrow >>= P256_BITSPERDIGIT;
|
||||
}
|
||||
return (int)borrow | notzero;
|
||||
}
|
||||
|
||||
// c = a - b. Returns borrow: 0 or -1.
|
||||
int p256_sub(const p256_int* a, const p256_int* b, p256_int* c) {
|
||||
int i;
|
||||
p256_sddigit borrow = 0;
|
||||
|
||||
for (i = 0; i < P256_NDIGITS; ++i) {
|
||||
borrow += (p256_sddigit)P256_DIGIT(a, i) - P256_DIGIT(b, i);
|
||||
if (c) P256_DIGIT(c, i) = (p256_digit)borrow;
|
||||
borrow >>= P256_BITSPERDIGIT;
|
||||
}
|
||||
return (int)borrow;
|
||||
}
|
||||
|
||||
// c = a + b. Returns carry: 0 or 1.
|
||||
int p256_add(const p256_int* a, const p256_int* b, p256_int* c) {
|
||||
int i;
|
||||
p256_ddigit carry = 0;
|
||||
|
||||
for (i = 0; i < P256_NDIGITS; ++i) {
|
||||
carry += (p256_ddigit)P256_DIGIT(a, i) + P256_DIGIT(b, i);
|
||||
if (c) P256_DIGIT(c, i) = (p256_digit)carry;
|
||||
carry >>= P256_BITSPERDIGIT;
|
||||
}
|
||||
return (int)carry;
|
||||
}
|
||||
|
||||
// b = a + d. Returns carry, 0 or 1.
|
||||
int p256_add_d(const p256_int* a, p256_digit d, p256_int* b) {
|
||||
int i;
|
||||
p256_ddigit carry = d;
|
||||
|
||||
for (i = 0; i < P256_NDIGITS; ++i) {
|
||||
carry += (p256_ddigit)P256_DIGIT(a, i);
|
||||
if (b) P256_DIGIT(b, i) = (p256_digit)carry;
|
||||
carry >>= P256_BITSPERDIGIT;
|
||||
}
|
||||
return (int)carry;
|
||||
}
|
||||
|
||||
// b = 1/a mod MOD, binary euclid.
|
||||
void p256_modinv_vartime(const p256_int* MOD,
|
||||
const p256_int* a,
|
||||
p256_int* b) {
|
||||
p256_int R = P256_ZERO;
|
||||
p256_int S = P256_ONE;
|
||||
p256_int U = *MOD;
|
||||
p256_int V = *a;
|
||||
|
||||
for (;;) {
|
||||
if (p256_is_even(&U)) {
|
||||
p256_shr1(&U, 0, &U);
|
||||
if (p256_is_even(&R)) {
|
||||
p256_shr1(&R, 0, &R);
|
||||
} else {
|
||||
// R = (R+MOD)/2
|
||||
p256_shr1(&R, p256_add(&R, MOD, &R), &R);
|
||||
}
|
||||
} else if (p256_is_even(&V)) {
|
||||
p256_shr1(&V, 0, &V);
|
||||
if (p256_is_even(&S)) {
|
||||
p256_shr1(&S, 0, &S);
|
||||
} else {
|
||||
// S = (S+MOD)/2
|
||||
p256_shr1(&S, p256_add(&S, MOD, &S) , &S);
|
||||
}
|
||||
} else { // U,V both odd.
|
||||
if (!p256_sub(&V, &U, NULL)) {
|
||||
p256_sub(&V, &U, &V);
|
||||
if (p256_sub(&S, &R, &S)) p256_add(&S, MOD, &S);
|
||||
if (p256_is_zero(&V)) break; // done.
|
||||
} else {
|
||||
p256_sub(&U, &V, &U);
|
||||
if (p256_sub(&R, &S, &R)) p256_add(&R, MOD, &R);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p256_mod(MOD, &R, b);
|
||||
}
|
||||
|
||||
void p256_mod(const p256_int* MOD,
|
||||
const p256_int* in,
|
||||
p256_int* out) {
|
||||
if (out != in) *out = *in;
|
||||
addM(MOD, 0, P256_DIGITS(out), subM(MOD, 0, P256_DIGITS(out), -1));
|
||||
}
|
||||
|
||||
// Verify y^2 == x^3 - 3x + b mod p
|
||||
// and 0 < x < p and 0 < y < p
|
||||
int p256_is_valid_point(const p256_int* x, const p256_int* y) {
|
||||
p256_int y2, x3;
|
||||
|
||||
if (p256_cmp(&SECP256r1_p, x) <= 0 ||
|
||||
p256_cmp(&SECP256r1_p, y) <= 0 ||
|
||||
p256_is_zero(x) ||
|
||||
p256_is_zero(y)) return 0;
|
||||
|
||||
p256_modmul(&SECP256r1_p, y, 0, y, &y2); // y^2
|
||||
|
||||
p256_modmul(&SECP256r1_p, x, 0, x, &x3); // x^2
|
||||
p256_modmul(&SECP256r1_p, x, 0, &x3, &x3); // x^3
|
||||
if (p256_sub(&x3, x, &x3)) p256_add(&x3, &SECP256r1_p, &x3); // x^3 - x
|
||||
if (p256_sub(&x3, x, &x3)) p256_add(&x3, &SECP256r1_p, &x3); // x^3 - 2x
|
||||
if (p256_sub(&x3, x, &x3)) p256_add(&x3, &SECP256r1_p, &x3); // x^3 - 3x
|
||||
if (p256_add(&x3, &SECP256r1_b, &x3)) // x^3 - 3x + b
|
||||
p256_sub(&x3, &SECP256r1_p, &x3);
|
||||
|
||||
return p256_cmp(&y2, &x3) == 0;
|
||||
}
|
||||
|
||||
void p256_from_bin(const uint8_t src[P256_NBYTES], p256_int* dst) {
|
||||
int i;
|
||||
const uint8_t* p = &src[0];
|
||||
|
||||
for (i = P256_NDIGITS - 1; i >= 0; --i) {
|
||||
P256_DIGIT(dst, i) =
|
||||
(p[0] << 24) |
|
||||
(p[1] << 16) |
|
||||
(p[2] << 8) |
|
||||
p[3];
|
||||
p += 4;
|
||||
}
|
||||
}
|
162
tests/fido_tests/u2f-tests-hid/p256.h
Normal file
162
tests/fido_tests/u2f-tests-hid/p256.h
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright 2013 The Android Open Source Project
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_LITE_P256_H_
|
||||
#define SYSTEM_CORE_INCLUDE_MINCRYPT_LITE_P256_H_
|
||||
|
||||
// Collection of routines manipulating 256 bit unsigned integers.
|
||||
// Just enough to implement ecdsa-p256 and related algorithms.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define P256_BITSPERDIGIT 32
|
||||
#define P256_NDIGITS 8
|
||||
#define P256_NBYTES 32
|
||||
|
||||
typedef int p256_err;
|
||||
typedef uint32_t p256_digit;
|
||||
typedef int32_t p256_sdigit;
|
||||
typedef uint64_t p256_ddigit;
|
||||
typedef int64_t p256_sddigit;
|
||||
|
||||
// Defining p256_int as struct to leverage struct assigment.
|
||||
typedef struct {
|
||||
p256_digit a[P256_NDIGITS];
|
||||
} p256_int;
|
||||
|
||||
extern const p256_int SECP256r1_n; // Curve order
|
||||
extern const p256_int SECP256r1_p; // Curve prime
|
||||
extern const p256_int SECP256r1_b; // Curve param
|
||||
|
||||
// Initialize a p256_int to zero.
|
||||
void p256_init(p256_int* a);
|
||||
|
||||
// Clear a p256_int to zero.
|
||||
void p256_clear(p256_int* a);
|
||||
|
||||
// Return bit. Index 0 is least significant.
|
||||
int p256_get_bit(const p256_int* a, int index);
|
||||
|
||||
// b := a % MOD
|
||||
void p256_mod(
|
||||
const p256_int* MOD,
|
||||
const p256_int* a,
|
||||
p256_int* b);
|
||||
|
||||
// c := a * (top_b | b) % MOD
|
||||
void p256_modmul(
|
||||
const p256_int* MOD,
|
||||
const p256_int* a,
|
||||
const p256_digit top_b,
|
||||
const p256_int* b,
|
||||
p256_int* c);
|
||||
|
||||
// b := 1 / a % MOD
|
||||
// MOD best be SECP256r1_n
|
||||
void p256_modinv(
|
||||
const p256_int* MOD,
|
||||
const p256_int* a,
|
||||
p256_int* b);
|
||||
|
||||
// b := 1 / a % MOD
|
||||
// MOD best be SECP256r1_n
|
||||
// Faster than p256_modinv()
|
||||
void p256_modinv_vartime(
|
||||
const p256_int* MOD,
|
||||
const p256_int* a,
|
||||
p256_int* b);
|
||||
|
||||
// b := a << (n % P256_BITSPERDIGIT)
|
||||
// Returns the bits shifted out of most significant digit.
|
||||
p256_digit p256_shl(const p256_int* a, int n, p256_int* b);
|
||||
|
||||
// b := a >> (n % P256_BITSPERDIGIT)
|
||||
void p256_shr(const p256_int* a, int n, p256_int* b);
|
||||
|
||||
int p256_is_zero(const p256_int* a);
|
||||
int p256_is_odd(const p256_int* a);
|
||||
int p256_is_even(const p256_int* a);
|
||||
|
||||
// Returns -1, 0 or 1.
|
||||
int p256_cmp(const p256_int* a, const p256_int *b);
|
||||
|
||||
// c: = a - b
|
||||
// Returns -1 on borrow.
|
||||
int p256_sub(const p256_int* a, const p256_int* b, p256_int* c);
|
||||
|
||||
// c := a + b
|
||||
// Returns 1 on carry.
|
||||
int p256_add(const p256_int* a, const p256_int* b, p256_int* c);
|
||||
|
||||
// c := a + (single digit)b
|
||||
// Returns carry 1 on carry.
|
||||
int p256_add_d(const p256_int* a, p256_digit b, p256_int* c);
|
||||
|
||||
// ec routines.
|
||||
|
||||
// {out_x,out_y} := nG
|
||||
void p256_base_point_mul(const p256_int *n,
|
||||
p256_int *out_x,
|
||||
p256_int *out_y);
|
||||
|
||||
// {out_x,out_y} := n{in_x,in_y}
|
||||
void p256_point_mul(const p256_int *n,
|
||||
const p256_int *in_x,
|
||||
const p256_int *in_y,
|
||||
p256_int *out_x,
|
||||
p256_int *out_y);
|
||||
|
||||
// {out_x,out_y} := n1G + n2{in_x,in_y}
|
||||
void p256_points_mul_vartime(
|
||||
const p256_int *n1, const p256_int *n2,
|
||||
const p256_int *in_x, const p256_int *in_y,
|
||||
p256_int *out_x, p256_int *out_y);
|
||||
|
||||
// Return whether point {x,y} is on curve.
|
||||
int p256_is_valid_point(const p256_int* x, const p256_int* y);
|
||||
|
||||
// Outputs big-endian binary form. No leading zero skips.
|
||||
void p256_to_bin(const p256_int* src, uint8_t dst[P256_NBYTES]);
|
||||
|
||||
// Reads from big-endian binary form,
|
||||
// thus pre-pad with leading zeros if short.
|
||||
void p256_from_bin(const uint8_t src[P256_NBYTES], p256_int* dst);
|
||||
|
||||
#define P256_DIGITS(x) ((x)->a)
|
||||
#define P256_DIGIT(x,y) ((x)->a[y])
|
||||
|
||||
#define P256_ZERO {{0}}
|
||||
#define P256_ONE {{1}}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_LITE_P256_H_
|
1279
tests/fido_tests/u2f-tests-hid/p256_ec.c
Normal file
1279
tests/fido_tests/u2f-tests-hid/p256_ec.c
Normal file
File diff suppressed because it is too large
Load Diff
56
tests/fido_tests/u2f-tests-hid/p256_ecdsa.c
Normal file
56
tests/fido_tests/u2f-tests-hid/p256_ecdsa.c
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2013 The Android Open Source Project
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "p256_ecdsa.h"
|
||||
#include "p256.h"
|
||||
|
||||
int p256_ecdsa_verify(const p256_int* key_x, const p256_int* key_y,
|
||||
const p256_int* message,
|
||||
const p256_int* r, const p256_int* s) {
|
||||
p256_int u, v;
|
||||
|
||||
// Check public key.
|
||||
if (!p256_is_valid_point(key_x, key_y)) return 0;
|
||||
|
||||
// Check r and s are != 0 % n.
|
||||
p256_mod(&SECP256r1_n, r, &u);
|
||||
p256_mod(&SECP256r1_n, s, &v);
|
||||
if (p256_is_zero(&u) || p256_is_zero(&v)) return 0;
|
||||
|
||||
p256_modinv_vartime(&SECP256r1_n, s, &v);
|
||||
p256_modmul(&SECP256r1_n, message, 0, &v, &u); // message / s % n
|
||||
p256_modmul(&SECP256r1_n, r, 0, &v, &v); // r / s % n
|
||||
|
||||
p256_points_mul_vartime(&u, &v,
|
||||
key_x, key_y,
|
||||
&u, &v);
|
||||
|
||||
p256_mod(&SECP256r1_n, &u, &u); // (x coord % p) % n
|
||||
return p256_cmp(r, &u) == 0;
|
||||
}
|
||||
|
53
tests/fido_tests/u2f-tests-hid/p256_ecdsa.h
Normal file
53
tests/fido_tests/u2f-tests-hid/p256_ecdsa.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2013 The Android Open Source Project
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_P256_ECDSA_H_
|
||||
#define SYSTEM_CORE_INCLUDE_MINCRYPT_P256_ECDSA_H_
|
||||
|
||||
// Using current directory as relative include path here since
|
||||
// this code typically gets lifted into a variety of build systems
|
||||
// and directory structures.
|
||||
#include "p256.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Returns 0 if {r,s} is not a signature on message for
|
||||
// public key {key_x,key_y}.
|
||||
//
|
||||
// Note: message is a p256_int.
|
||||
// Convert from a binary string using p256_from_bin().
|
||||
int p256_ecdsa_verify(const p256_int* key_x,
|
||||
const p256_int* key_y,
|
||||
const p256_int* message,
|
||||
const p256_int* r, const p256_int* s);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_P256_ECDSA_H_
|
184
tests/fido_tests/u2f-tests-hid/sha256.c
Normal file
184
tests/fido_tests/u2f-tests-hid/sha256.c
Normal file
@ -0,0 +1,184 @@
|
||||
/* sha256.c
|
||||
**
|
||||
** Copyright 2013, The Android Open Source Project
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** * Neither the name of Google Inc. nor the names of its contributors may
|
||||
** be used to endorse or promote products derived from this software
|
||||
** without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// Optimized for minimal code size.
|
||||
|
||||
#include "sha256.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define ror(value, bits) (((value) >> (bits)) | ((value) << (32 - (bits))))
|
||||
#define shr(value, bits) ((value) >> (bits))
|
||||
|
||||
static const uint32_t K[64] = {
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
|
||||
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
||||
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
|
||||
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
|
||||
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
||||
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
|
||||
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
|
||||
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
||||
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 };
|
||||
|
||||
static void SHA256_Transform(SHA256_CTX* ctx) {
|
||||
uint32_t W[64];
|
||||
uint32_t A, B, C, D, E, F, G, H;
|
||||
uint8_t* p = ctx->buf;
|
||||
int t;
|
||||
|
||||
for(t = 0; t < 16; ++t) {
|
||||
uint32_t tmp = *p++ << 24;
|
||||
tmp |= *p++ << 16;
|
||||
tmp |= *p++ << 8;
|
||||
tmp |= *p++;
|
||||
W[t] = tmp;
|
||||
}
|
||||
|
||||
for(; t < 64; t++) {
|
||||
uint32_t s0 = ror(W[t-15], 7) ^ ror(W[t-15], 18) ^ shr(W[t-15], 3);
|
||||
uint32_t s1 = ror(W[t-2], 17) ^ ror(W[t-2], 19) ^ shr(W[t-2], 10);
|
||||
W[t] = W[t-16] + s0 + W[t-7] + s1;
|
||||
}
|
||||
|
||||
A = ctx->state[0];
|
||||
B = ctx->state[1];
|
||||
C = ctx->state[2];
|
||||
D = ctx->state[3];
|
||||
E = ctx->state[4];
|
||||
F = ctx->state[5];
|
||||
G = ctx->state[6];
|
||||
H = ctx->state[7];
|
||||
|
||||
for(t = 0; t < 64; t++) {
|
||||
uint32_t s0 = ror(A, 2) ^ ror(A, 13) ^ ror(A, 22);
|
||||
uint32_t maj = (A & B) ^ (A & C) ^ (B & C);
|
||||
uint32_t t2 = s0 + maj;
|
||||
uint32_t s1 = ror(E, 6) ^ ror(E, 11) ^ ror(E, 25);
|
||||
uint32_t ch = (E & F) ^ ((~E) & G);
|
||||
uint32_t t1 = H + s1 + ch + K[t] + W[t];
|
||||
|
||||
H = G;
|
||||
G = F;
|
||||
F = E;
|
||||
E = D + t1;
|
||||
D = C;
|
||||
C = B;
|
||||
B = A;
|
||||
A = t1 + t2;
|
||||
}
|
||||
|
||||
ctx->state[0] += A;
|
||||
ctx->state[1] += B;
|
||||
ctx->state[2] += C;
|
||||
ctx->state[3] += D;
|
||||
ctx->state[4] += E;
|
||||
ctx->state[5] += F;
|
||||
ctx->state[6] += G;
|
||||
ctx->state[7] += H;
|
||||
}
|
||||
|
||||
static const HASH_VTAB SHA256_VTAB = {
|
||||
SHA256_init,
|
||||
SHA256_update,
|
||||
SHA256_final,
|
||||
SHA256_hash,
|
||||
SHA256_DIGEST_SIZE
|
||||
};
|
||||
|
||||
void SHA256_init(SHA256_CTX* ctx) {
|
||||
ctx->f = &SHA256_VTAB;
|
||||
ctx->state[0] = 0x6a09e667;
|
||||
ctx->state[1] = 0xbb67ae85;
|
||||
ctx->state[2] = 0x3c6ef372;
|
||||
ctx->state[3] = 0xa54ff53a;
|
||||
ctx->state[4] = 0x510e527f;
|
||||
ctx->state[5] = 0x9b05688c;
|
||||
ctx->state[6] = 0x1f83d9ab;
|
||||
ctx->state[7] = 0x5be0cd19;
|
||||
ctx->count = 0;
|
||||
}
|
||||
|
||||
|
||||
void SHA256_update(SHA256_CTX* ctx, const void* data, int len) {
|
||||
int i = (int) (ctx->count & 63);
|
||||
const uint8_t* p = (const uint8_t*)data;
|
||||
|
||||
ctx->count += len;
|
||||
|
||||
while (len--) {
|
||||
ctx->buf[i++] = *p++;
|
||||
if (i == 64) {
|
||||
SHA256_Transform(ctx);
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const uint8_t* SHA256_final(SHA256_CTX* ctx) {
|
||||
uint8_t *p = ctx->buf;
|
||||
uint64_t cnt = ctx->count * 8;
|
||||
int i;
|
||||
|
||||
SHA256_update(ctx, (uint8_t*)"\x80", 1);
|
||||
while ((ctx->count & 63) != 56) {
|
||||
SHA256_update(ctx, (uint8_t*)"\0", 1);
|
||||
}
|
||||
for (i = 0; i < 8; ++i) {
|
||||
uint8_t tmp = (uint8_t) (cnt >> ((7 - i) * 8));
|
||||
SHA256_update(ctx, &tmp, 1);
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
uint32_t tmp = ctx->state[i];
|
||||
*p++ = tmp >> 24;
|
||||
*p++ = tmp >> 16;
|
||||
*p++ = tmp >> 8;
|
||||
*p++ = tmp >> 0;
|
||||
}
|
||||
|
||||
return ctx->buf;
|
||||
}
|
||||
|
||||
/* Convenience function */
|
||||
const uint8_t* SHA256_hash(const void* data, int len, uint8_t* digest) {
|
||||
SHA256_CTX ctx;
|
||||
SHA256_init(&ctx);
|
||||
SHA256_update(&ctx, data, len);
|
||||
memcpy(digest, SHA256_final(&ctx), SHA256_DIGEST_SIZE);
|
||||
return digest;
|
||||
}
|
52
tests/fido_tests/u2f-tests-hid/sha256.h
Normal file
52
tests/fido_tests/u2f-tests-hid/sha256.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2011 The Android Open Source Project
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_SHA256_H_
|
||||
#define SYSTEM_CORE_INCLUDE_MINCRYPT_SHA256_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "hash-internal.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
typedef HASH_CTX SHA256_CTX;
|
||||
|
||||
void SHA256_init(SHA256_CTX* ctx);
|
||||
void SHA256_update(SHA256_CTX* ctx, const void* data, int len);
|
||||
const uint8_t* SHA256_final(SHA256_CTX* ctx);
|
||||
|
||||
// Convenience method. Returns digest address.
|
||||
const uint8_t* SHA256_hash(const void* data, int len, uint8_t* digest);
|
||||
|
||||
#define SHA256_DIGEST_SIZE 32
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_SHA256_H_
|
97
tests/fido_tests/u2f-tests-hid/u2f.h
Normal file
97
tests/fido_tests/u2f-tests-hid/u2f.h
Normal file
@ -0,0 +1,97 @@
|
||||
// Copyright 2014 Google Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
#ifndef __U2F_H_INCLUDED__
|
||||
#define __U2F_H_INCLUDED__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef __NO_PRAGMA_PACK
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
|
||||
// General constants
|
||||
|
||||
#define P256_SCALAR_SIZE 32 // nistp256 in bytes
|
||||
#define P256_POINT_SIZE ((P256_SCALAR_SIZE * 2) + 1)
|
||||
|
||||
#define MAX_ECDSA_SIG_SIZE 72 // asn1 DER format
|
||||
#define MAX_KH_SIZE 128 // key handle
|
||||
#define MAX_CERT_SIZE 2048 // attestation certificate
|
||||
|
||||
#define U2F_APPID_SIZE 32
|
||||
#define U2F_NONCE_SIZE 32
|
||||
|
||||
#define UNCOMPRESSED_POINT 0x04
|
||||
|
||||
typedef struct {
|
||||
uint8_t format;
|
||||
uint8_t x[P256_SCALAR_SIZE];
|
||||
uint8_t y[P256_SCALAR_SIZE];
|
||||
} P256_POINT;
|
||||
|
||||
// U2Fv2 instructions
|
||||
|
||||
#define U2F_INS_REGISTER 0x01
|
||||
#define U2F_INS_AUTHENTICATE 0x02
|
||||
#define U2F_INS_VERSION 0x03
|
||||
|
||||
// U2F_REGISTER instruction defines
|
||||
|
||||
#define U2F_REGISTER_ID 0x05 // magic constant
|
||||
#define U2F_REGISTER_HASH_ID 0x00 // magic constant
|
||||
|
||||
typedef struct {
|
||||
uint8_t nonce[U2F_NONCE_SIZE];
|
||||
uint8_t appId[U2F_APPID_SIZE];
|
||||
} U2F_REGISTER_REQ;
|
||||
|
||||
typedef struct {
|
||||
uint8_t registerId;
|
||||
P256_POINT pubKey;
|
||||
uint8_t keyHandleLen;
|
||||
uint8_t keyHandleCertSig[
|
||||
MAX_KH_SIZE +
|
||||
MAX_CERT_SIZE +
|
||||
MAX_ECDSA_SIG_SIZE];
|
||||
} U2F_REGISTER_RESP;
|
||||
|
||||
// U2F_AUTHENTICATE instruction defines
|
||||
|
||||
// Authentication parameter byte
|
||||
#define U2F_AUTH_ENFORCE 0x03 // Require user presence
|
||||
#define U2F_AUTH_CHECK_ONLY 0x07 // Test but do not consume
|
||||
|
||||
typedef struct {
|
||||
uint8_t nonce[U2F_NONCE_SIZE];
|
||||
uint8_t appId[U2F_APPID_SIZE];
|
||||
uint8_t keyHandleLen;
|
||||
uint8_t keyHandle[MAX_KH_SIZE];
|
||||
} U2F_AUTHENTICATE_REQ;
|
||||
|
||||
// Flags values
|
||||
#define U2F_TOUCHED 0x01
|
||||
#define U2F_ALTERNATE_INTERFACE 0x02
|
||||
|
||||
typedef struct {
|
||||
uint8_t flags;
|
||||
uint32_t ctr;
|
||||
uint8_t sig[MAX_ECDSA_SIG_SIZE];
|
||||
} U2F_AUTHENTICATE_RESP;
|
||||
|
||||
#ifndef __NO_PRAGMA_PACK
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __U2F_H_INCLUDED__
|
96
tests/fido_tests/u2f-tests-hid/u2f_hid.h
Normal file
96
tests/fido_tests/u2f-tests-hid/u2f_hid.h
Normal file
@ -0,0 +1,96 @@
|
||||
// Copyright 2014 Google Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
#ifndef __U2F_HID_H_INCLUDED__
|
||||
#define __U2F_HID_H_INCLUDED__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef __NO_PRAGMA_PACK
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
|
||||
// HID frame definition
|
||||
typedef struct {
|
||||
uint32_t cid;
|
||||
union {
|
||||
uint8_t type;
|
||||
struct {
|
||||
uint8_t cmd;
|
||||
uint8_t bcnth;
|
||||
uint8_t bcntl;
|
||||
uint8_t data[64 - 7];
|
||||
} init;
|
||||
struct {
|
||||
uint8_t seq;
|
||||
uint8_t data[64 - 5];
|
||||
} cont;
|
||||
};
|
||||
} U2FHID_FRAME;
|
||||
|
||||
// HID frame accessors
|
||||
#define TYPE_MASK 0x80
|
||||
#define TYPE_INIT 0x80
|
||||
#define TYPE_CONT 0x00
|
||||
|
||||
#define FRAME_TYPE(x) ((x).type & TYPE_MASK)
|
||||
#define FRAME_SEQ(x) ((x).cont.seq & ~TYPE_MASK)
|
||||
#define FRAME_CMD(x) ((x).init.cmd & ~TYPE_MASK)
|
||||
#define MSG_LEN(x) ((x).init.bcnth * 256u + (x).init.bcntl)
|
||||
|
||||
// Commands
|
||||
#define U2FHID_PING (TYPE_INIT | 1)
|
||||
#define U2FHID_MSG (TYPE_INIT | 3)
|
||||
#define U2FHID_LOCK (TYPE_INIT | 4)
|
||||
#define U2FHID_INIT (TYPE_INIT | 6)
|
||||
#define U2FHID_WINK (TYPE_INIT | 8)
|
||||
#define U2FHID_SYNC (TYPE_INIT | 0x3c)
|
||||
#define U2FHID_ERROR (TYPE_INIT | 0x3f)
|
||||
|
||||
// Errors
|
||||
#define ERR_NONE 0
|
||||
#define ERR_INVALID_CMD 1
|
||||
#define ERR_INVALID_PAR 2
|
||||
#define ERR_INVALID_LEN 3
|
||||
#define ERR_INVALID_SEQ 4
|
||||
#define ERR_MSG_TIMEOUT 5
|
||||
#define ERR_CHANNEL_BUSY 6
|
||||
#define ERR_LOCK_REQUIRED 10
|
||||
#define ERR_INVALID_CID 11
|
||||
#define ERR_OTHER 127
|
||||
|
||||
// Init command parameters
|
||||
#define CID_BROADCAST -1
|
||||
#define INIT_NONCE_SIZE 8
|
||||
|
||||
typedef struct {
|
||||
uint8_t nonce[INIT_NONCE_SIZE];
|
||||
uint32_t cid;
|
||||
uint8_t versionInterface;
|
||||
uint8_t versionMajor;
|
||||
uint8_t versionMinor;
|
||||
uint8_t versionBuild;
|
||||
uint8_t capFlags;
|
||||
} U2FHID_INIT_RESP;
|
||||
|
||||
#define U2FHID_IF_VERSION 2
|
||||
|
||||
#define CAPFLAG_WINK 0x01
|
||||
#define CAPFLAG_LOCK 0x02
|
||||
|
||||
#ifndef __NO_PRAGMA_PACK
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __U2F_HID_H_INCLUDED__
|
489
tests/fido_tests/u2f-tests-hid/u2f_util.cc
Normal file
489
tests/fido_tests/u2f-tests-hid/u2f_util.cc
Normal file
@ -0,0 +1,489 @@
|
||||
// Copyright 2014 Google Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __OS_WIN
|
||||
#include <winsock2.h> // ntohl, htonl
|
||||
#else
|
||||
#include <arpa/inet.h> // ntohl, htonl
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "u2f_util.h"
|
||||
|
||||
// This is a "library"; do not abort.
|
||||
#define AbortOrNot() \
|
||||
std::cerr << "returning false" << std::endl; \
|
||||
return false
|
||||
|
||||
#ifdef __OS_WIN
|
||||
#define strdup _strdup
|
||||
#endif
|
||||
|
||||
#ifdef __OS_MAC
|
||||
// Implement something compatible w/ linux clock_gettime()
|
||||
|
||||
#include <mach/mach_time.h>
|
||||
|
||||
#define CLOCK_MONOTONIC 0
|
||||
|
||||
static void clock_gettime(int which, struct timespec* ts) {
|
||||
static mach_timebase_info_data_t __clock_gettime_inf;
|
||||
uint64_t now, nano;
|
||||
|
||||
now = mach_absolute_time();
|
||||
if (0 == __clock_gettime_inf.denom) mach_timebase_info(&__clock_gettime_inf);
|
||||
|
||||
nano = now * __clock_gettime_inf.numer / __clock_gettime_inf.denom;
|
||||
ts->tv_sec = nano * 1e-9;
|
||||
ts->tv_nsec = nano - (ts->tv_sec * 1e9);
|
||||
}
|
||||
#endif // __OS_MAC
|
||||
|
||||
std::string b2a(const void* ptr, size_t size) {
|
||||
const uint8_t* p = reinterpret_cast<const uint8_t*>(ptr);
|
||||
std::string result;
|
||||
|
||||
for (size_t i = 0; i < 2 * size; ++i) {
|
||||
int nib = p[i / 2];
|
||||
if ((i & 1) == 0) nib >>= 4;
|
||||
nib &= 15;
|
||||
result.push_back("0123456789ABCDEF"[nib]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string b2a(const std::string& s) { return b2a(s.data(), s.size()); }
|
||||
|
||||
std::string a2b(const std::string& s) {
|
||||
std::string result;
|
||||
int v;
|
||||
for (size_t i = 0; i < s.size(); ++i) {
|
||||
if ((i & 1) == 1)
|
||||
v <<= 4;
|
||||
else
|
||||
v = 0;
|
||||
char d = s[i];
|
||||
if (d >= '0' && d <= '9')
|
||||
v += (d - '0');
|
||||
else if (d >= 'A' && d <= 'F')
|
||||
v += (d - 'A' + 10);
|
||||
else if (d >= 'a' && d <= 'f')
|
||||
v += (d - 'a' + 10);
|
||||
if ((i & 1) == 1) result.push_back(v & 255);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
float U2Fob_deltaTime(uint64_t* state) {
|
||||
uint64_t now, delta;
|
||||
#ifdef __OS_WIN
|
||||
now = (uint64_t)GetTickCount64() * 1000000;
|
||||
#else
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
now = (uint64_t)(ts.tv_sec * 1e9 + ts.tv_nsec);
|
||||
#endif
|
||||
delta = *state ? now - *state : 0;
|
||||
*state = now;
|
||||
return (float)(delta / 1.0e9);
|
||||
}
|
||||
|
||||
struct U2Fob* U2Fob_create() {
|
||||
struct U2Fob* f = NULL;
|
||||
if (hid_init() == 0) {
|
||||
f = (struct U2Fob*)malloc(sizeof(struct U2Fob));
|
||||
memset(f, 0, sizeof(struct U2Fob));
|
||||
f->cid = f->fd_in = f->fd_out = -1;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
void U2Fob_destroy(struct U2Fob* device) {
|
||||
if (device) {
|
||||
U2Fob_close(device);
|
||||
if (device->path) {
|
||||
free(device->path);
|
||||
device->path = NULL;
|
||||
}
|
||||
free(device);
|
||||
}
|
||||
hid_exit();
|
||||
}
|
||||
|
||||
uint32_t U2Fob_getCid(struct U2Fob* device) { return device->cid; }
|
||||
|
||||
int U2Fob_open(struct U2Fob* device, const char* path) {
|
||||
U2Fob_close(device);
|
||||
if (device->path) {
|
||||
free(device->path);
|
||||
device->path = NULL;
|
||||
}
|
||||
device->path = strdup(path);
|
||||
DEV_open_path(device);
|
||||
return DEV_opened(device) ? -ERR_NONE : -ERR_OTHER;
|
||||
}
|
||||
|
||||
void U2Fob_close(struct U2Fob* device) { DEV_close(device); }
|
||||
|
||||
int U2Fob_reopen(struct U2Fob* device) {
|
||||
U2Fob_close(device);
|
||||
DEV_open_path(device);
|
||||
return DEV_opened(device) ? -ERR_NONE : -ERR_OTHER;
|
||||
}
|
||||
|
||||
void U2Fob_setLog(struct U2Fob* device, FILE* fd, int level) {
|
||||
device->logfp = fd;
|
||||
device->loglevel = level;
|
||||
device->logtime = 0;
|
||||
U2Fob_deltaTime(&device->logtime);
|
||||
}
|
||||
|
||||
static void U2Fob_logFrame(struct U2Fob* device, const char* tag,
|
||||
const U2FHID_FRAME* f) {
|
||||
if (device->logfp) {
|
||||
fprintf(device->logfp, "t+%.3f", U2Fob_deltaTime(&device->logtime));
|
||||
fprintf(device->logfp, "%s %08x:%02x", tag, f->cid, f->type);
|
||||
if (f->type & TYPE_INIT) {
|
||||
int len = f->init.bcnth * 256 + f->init.bcntl;
|
||||
fprintf(device->logfp, "[%d]:", len);
|
||||
for (size_t i = 0; i < sizeof(f->init.data); ++i)
|
||||
fprintf(device->logfp, "%02X", f->init.data[i]);
|
||||
} else {
|
||||
fprintf(device->logfp, ":");
|
||||
for (size_t i = 0; i < sizeof(f->cont.data); ++i)
|
||||
fprintf(device->logfp, "%02X", f->cont.data[i]);
|
||||
}
|
||||
fprintf(device->logfp, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
int U2Fob_sendHidFrame(struct U2Fob* device, U2FHID_FRAME* f) {
|
||||
uint8_t d[sizeof(U2FHID_FRAME) + 1];
|
||||
int res;
|
||||
|
||||
d[0] = 0; // un-numbered report
|
||||
f->cid = htonl(f->cid); // cid is in network order on the wire
|
||||
memcpy(d + 1, f, sizeof(U2FHID_FRAME));
|
||||
f->cid = ntohl(f->cid);
|
||||
|
||||
if (!DEV_opened(device)) return -ERR_OTHER;
|
||||
res = DEV_write(device, d, sizeof(d));
|
||||
|
||||
if (res == sizeof(d)) {
|
||||
U2Fob_logFrame(device, ">", f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ERR_OTHER;
|
||||
}
|
||||
|
||||
int U2Fob_receiveHidFrame(struct U2Fob* device, U2FHID_FRAME* r, float to) {
|
||||
if (to <= 0.0) return -ERR_MSG_TIMEOUT;
|
||||
|
||||
if (!DEV_opened(device)) return -ERR_OTHER;
|
||||
memset((int8_t*)r, 0xEE, sizeof(U2FHID_FRAME));
|
||||
int res = DEV_read_timeout(device, (uint8_t*)r, sizeof(U2FHID_FRAME),
|
||||
(int)(to * 1000));
|
||||
if (res == sizeof(U2FHID_FRAME)) {
|
||||
r->cid = ntohl(r->cid);
|
||||
U2Fob_logFrame(device, "<", r);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (res == -1) return -ERR_OTHER;
|
||||
|
||||
if (device->logfp) {
|
||||
fprintf(device->logfp, "t+%.3f", U2Fob_deltaTime(&device->logtime));
|
||||
fprintf(device->logfp, "< (timeout)\n");
|
||||
}
|
||||
|
||||
return -ERR_MSG_TIMEOUT;
|
||||
}
|
||||
|
||||
int U2Fob_init(struct U2Fob* device) {
|
||||
int res;
|
||||
U2FHID_FRAME challenge;
|
||||
|
||||
for (size_t i = 0; i < sizeof(device->nonce); ++i) {
|
||||
device->nonce[i] ^= (rand() >> 3);
|
||||
}
|
||||
|
||||
challenge.cid = device->cid;
|
||||
challenge.init.cmd = U2FHID_INIT | TYPE_INIT;
|
||||
challenge.init.bcnth = 0;
|
||||
challenge.init.bcntl = INIT_NONCE_SIZE;
|
||||
memcpy(challenge.init.data, device->nonce, INIT_NONCE_SIZE);
|
||||
|
||||
res = U2Fob_sendHidFrame(device, &challenge);
|
||||
if (res != 0) return res;
|
||||
|
||||
for (;;) {
|
||||
U2FHID_FRAME response;
|
||||
res = U2Fob_receiveHidFrame(device, &response, 2.0);
|
||||
|
||||
if (res == -ERR_MSG_TIMEOUT) return res;
|
||||
if (res == -ERR_OTHER) return res;
|
||||
|
||||
if (response.cid != challenge.cid) continue;
|
||||
if (response.init.cmd != challenge.init.cmd) continue;
|
||||
if (MSG_LEN(response) != sizeof(U2FHID_INIT_RESP)) continue;
|
||||
if (memcmp(response.init.data, challenge.init.data, INIT_NONCE_SIZE))
|
||||
continue;
|
||||
|
||||
device->cid = (response.init.data[8] << 24) |
|
||||
(response.init.data[9] << 16) |
|
||||
(response.init.data[10] << 8) | (response.init.data[11] << 0);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int U2Fob_send(struct U2Fob* device, uint8_t cmd, const void* data,
|
||||
size_t size) {
|
||||
U2FHID_FRAME frame;
|
||||
int res;
|
||||
size_t frameLen;
|
||||
uint8_t seq = 0;
|
||||
uint8_t* pData = (uint8_t*)data;
|
||||
|
||||
frame.cid = device->cid;
|
||||
frame.init.cmd = TYPE_INIT | cmd;
|
||||
frame.init.bcnth = (size >> 8) & 255;
|
||||
frame.init.bcntl = (size & 255);
|
||||
|
||||
frameLen = min(size, sizeof(frame.init.data));
|
||||
memset(frame.init.data, 0xEE, sizeof(frame.init.data));
|
||||
memcpy(frame.init.data, pData, frameLen);
|
||||
|
||||
do {
|
||||
res = U2Fob_sendHidFrame(device, &frame);
|
||||
if (res != 0) return res;
|
||||
|
||||
if (device->dev == NULL) usleep(10000);
|
||||
|
||||
size -= frameLen;
|
||||
pData += frameLen;
|
||||
|
||||
frame.cont.seq = seq++;
|
||||
frameLen = min(size, sizeof(frame.cont.data));
|
||||
memset(frame.cont.data, 0xEE, sizeof(frame.cont.data));
|
||||
memcpy(frame.cont.data, pData, frameLen);
|
||||
} while (size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int U2Fob_recv(struct U2Fob* device, uint8_t* cmd, void* data, size_t max,
|
||||
float timeout) {
|
||||
U2FHID_FRAME frame;
|
||||
int res, result;
|
||||
size_t totalLen, frameLen;
|
||||
uint8_t seq = 0;
|
||||
uint8_t* pData = (uint8_t*)data;
|
||||
uint64_t timeTracker = 0;
|
||||
|
||||
U2Fob_deltaTime(&timeTracker);
|
||||
|
||||
do {
|
||||
res = U2Fob_receiveHidFrame(device, &frame, timeout);
|
||||
if (res != 0) return res;
|
||||
|
||||
timeout -= U2Fob_deltaTime(&timeTracker);
|
||||
} while (frame.cid != device->cid || FRAME_TYPE(frame) != TYPE_INIT);
|
||||
|
||||
if (frame.init.cmd == U2FHID_ERROR) return -frame.init.data[0];
|
||||
|
||||
*cmd = frame.init.cmd;
|
||||
|
||||
totalLen = min(max, MSG_LEN(frame));
|
||||
frameLen = min(sizeof(frame.init.data), totalLen);
|
||||
|
||||
result = totalLen;
|
||||
|
||||
memcpy(pData, frame.init.data, frameLen);
|
||||
totalLen -= frameLen;
|
||||
pData += frameLen;
|
||||
|
||||
while (totalLen) {
|
||||
res = U2Fob_receiveHidFrame(device, &frame, timeout);
|
||||
if (res != 0) return res;
|
||||
|
||||
timeout -= U2Fob_deltaTime(&timeTracker);
|
||||
|
||||
if (frame.cid != device->cid) continue;
|
||||
if (FRAME_TYPE(frame) != TYPE_CONT) return -ERR_INVALID_SEQ;
|
||||
if (FRAME_SEQ(frame) != seq++) return -ERR_INVALID_SEQ;
|
||||
|
||||
frameLen = min(sizeof(frame.cont.data), totalLen);
|
||||
|
||||
memcpy(pData, frame.cont.data, frameLen);
|
||||
totalLen -= frameLen;
|
||||
pData += frameLen;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int U2Fob_exchange_apdu_buffer(struct U2Fob* device, void* data, size_t size,
|
||||
std::string* in) {
|
||||
uint8_t cmd = U2FHID_MSG;
|
||||
|
||||
int res = U2Fob_send(device, cmd, data, size);
|
||||
if (res != 0) return res;
|
||||
|
||||
uint8_t buf[4096];
|
||||
memset(buf, 0xEE, sizeof(buf));
|
||||
res = U2Fob_recv(device, &cmd, buf, sizeof(buf), 5.0);
|
||||
if (res < 0) return res;
|
||||
|
||||
if (cmd != U2FHID_MSG) return -ERR_OTHER;
|
||||
|
||||
uint16_t sw12;
|
||||
|
||||
if (res < 2) return -ERR_OTHER;
|
||||
sw12 = (buf[res - 2] << 8) | buf[res - 1];
|
||||
res -= 2;
|
||||
|
||||
in->assign(reinterpret_cast<char*>(buf), res);
|
||||
|
||||
return sw12;
|
||||
}
|
||||
|
||||
int U2Fob_apdu(struct U2Fob* device, uint8_t CLA, uint8_t INS, uint8_t P1,
|
||||
uint8_t P2, const std::string& out, std::string* in) {
|
||||
uint8_t buf[4096];
|
||||
size_t nc = out.size() ? (3 + out.size()) : 0;
|
||||
|
||||
// Construct outgoing message.
|
||||
memset(buf, 0xEE, sizeof(buf));
|
||||
buf[0] = CLA;
|
||||
buf[1] = INS;
|
||||
buf[2] = P1;
|
||||
buf[3] = P2;
|
||||
|
||||
uint8_t offs = 4;
|
||||
|
||||
// Encode lc.
|
||||
if (nc) {
|
||||
buf[offs++] = 0; // extended length
|
||||
buf[offs++] = (out.size() >> 8) & 255;
|
||||
buf[offs++] = (out.size() & 255);
|
||||
memcpy(buf + offs, out.data(), out.size());
|
||||
offs += out.size();
|
||||
}
|
||||
|
||||
// Encode le.
|
||||
if (!nc) {
|
||||
// When there are no data sent, an extra 0 is necessary prior to Le.
|
||||
buf[offs++] = 0;
|
||||
}
|
||||
buf[offs++] = 0;
|
||||
buf[offs++] = 0;
|
||||
|
||||
return U2Fob_exchange_apdu_buffer(device, buf, offs, in);
|
||||
}
|
||||
|
||||
bool getCertificate(const U2F_REGISTER_RESP& rsp, std::string* cert) {
|
||||
size_t hkLen = rsp.keyHandleLen;
|
||||
|
||||
CHECK_GE(hkLen, 64);
|
||||
CHECK_LT(hkLen, sizeof(rsp.keyHandleCertSig));
|
||||
|
||||
size_t certOff = hkLen;
|
||||
size_t certLen = sizeof(rsp.keyHandleCertSig) - certOff;
|
||||
const uint8_t* p = &rsp.keyHandleCertSig[certOff];
|
||||
|
||||
CHECK_GE(certLen, 4);
|
||||
CHECK_EQ(p[0], 0x30);
|
||||
|
||||
CHECK_GE(p[1], 0x81);
|
||||
CHECK_LE(p[1], 0x82);
|
||||
|
||||
size_t seqLen;
|
||||
size_t headerLen;
|
||||
if (p[1] == 0x81) {
|
||||
seqLen = p[2];
|
||||
headerLen = 3;
|
||||
} else if (p[1] == 0x82) {
|
||||
seqLen = p[2] * 256 + p[3];
|
||||
headerLen = 4;
|
||||
} else {
|
||||
// FAIL
|
||||
AbortOrNot();
|
||||
}
|
||||
|
||||
CHECK_LE(seqLen, certLen - headerLen);
|
||||
|
||||
cert->assign(reinterpret_cast<const char*>(p), seqLen + headerLen);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getSignature(const U2F_REGISTER_RESP& rsp, std::string* sig) {
|
||||
std::string cert;
|
||||
CHECK_NE(false, getCertificate(rsp, &cert));
|
||||
|
||||
size_t sigOff = rsp.keyHandleLen + cert.size();
|
||||
CHECK_LE(sigOff, sizeof(rsp.keyHandleCertSig));
|
||||
|
||||
size_t sigLen = sizeof(rsp.keyHandleCertSig) - sigOff;
|
||||
const uint8_t* p = &rsp.keyHandleCertSig[sigOff];
|
||||
|
||||
CHECK_GE(sigLen, 2);
|
||||
CHECK_EQ(p[0], 0x30);
|
||||
|
||||
size_t seqLen = p[1];
|
||||
CHECK_LE(seqLen, sigLen - 2);
|
||||
|
||||
sig->assign(reinterpret_cast<const char*>(p), seqLen + 2);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getSubjectPublicKey(const std::string& cert, std::string* pk) {
|
||||
CHECK_GE(cert.size(), P256_POINT_SIZE);
|
||||
|
||||
// Explicitly search for asn1 lead-in sequence of p256-ecdsa public key.
|
||||
const char asn1[] = "3059301306072A8648CE3D020106082A8648CE3D030107034200";
|
||||
std::string pkStart(a2b(asn1));
|
||||
|
||||
size_t off = cert.find(pkStart);
|
||||
CHECK_NE(off, std::string::npos);
|
||||
|
||||
off += pkStart.size();
|
||||
CHECK_LE(off, cert.size() - P256_POINT_SIZE);
|
||||
|
||||
pk->assign(cert, off, P256_POINT_SIZE);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getCertSignature(const std::string& cert, std::string* sig) {
|
||||
// Explicitly search asn1 lead-in sequence of p256-ecdsa signature.
|
||||
const char asn1[] = "300A06082A8648CE3D04030203";
|
||||
std::string sigStart(a2b(asn1));
|
||||
|
||||
size_t off = cert.find(sigStart);
|
||||
CHECK_NE(off, std::string::npos);
|
||||
|
||||
off += sigStart.size();
|
||||
CHECK_LE(off, cert.size() - 8);
|
||||
|
||||
size_t bitStringLen = cert[off] & 255;
|
||||
CHECK_EQ(bitStringLen, cert.size() - off - 1);
|
||||
CHECK_EQ(cert[off + 1], 0);
|
||||
|
||||
sig->assign(cert, off + 2, cert.size() - off - 2);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool verifyCertificate(const std::string& pk, const std::string& cert) {
|
||||
CHECK_EQ(true, false); // not yet implemented
|
||||
}
|
158
tests/fido_tests/u2f-tests-hid/u2f_util.h
Normal file
158
tests/fido_tests/u2f-tests-hid/u2f_util.h
Normal file
@ -0,0 +1,158 @@
|
||||
// Copyright 2014 Google Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
#ifndef __U2F_UTIL_H_INCLUDED__
|
||||
#define __U2F_UTIL_H_INCLUDED__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
#include "u2f.h"
|
||||
#include "u2f_hid.h"
|
||||
|
||||
#include "hidapi.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <windows.h>
|
||||
#define usleep(x) Sleep((x + 999) / 1000)
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#define max(a,b) \
|
||||
({ __typeof__ (a) _a = (a); \
|
||||
__typeof__ (b) _b = (b); \
|
||||
_a > _b ? _a : _b; })
|
||||
|
||||
#define min(a,b) \
|
||||
({ __typeof__ (a) _a = (a); \
|
||||
__typeof__ (b) _b = (b); \
|
||||
_a < _b ? _a : _b; })
|
||||
#endif
|
||||
|
||||
#define CHECK_INFO __FUNCTION__ << "[" << __LINE__ << "]:"
|
||||
|
||||
#define CHECK_EQ(a,b) do { if ((a)!=(b)) { std::cerr << "\x1b[31mCHECK_EQ fail at " << CHECK_INFO#a << " != "#b << ":\x1b[0m "; AbortOrNot(); }} while(0)
|
||||
#define CHECK_NE(a,b) do { if ((a)==(b)) { std::cerr << "\x1b[31mCHECK_NE fail at " << CHECK_INFO#a << " == "#b << ":\x1b[0m "; AbortOrNot(); }} while(0)
|
||||
#define CHECK_GE(a,b) do { if ((a)<(b)) { std::cerr << "\x1b[31mCHECK_GE fail at " << CHECK_INFO#a << " < "#b << ":\x1b[0m "; AbortOrNot(); }} while(0)
|
||||
#define CHECK_GT(a,b) do { if ((a)<=(b)) { std::cerr << "\x1b[31mCHECK_GT fail at " << CHECK_INFO#a << " < "#b << ":\x1b[0m "; AbortOrNot(); }} while(0)
|
||||
#define CHECK_LT(a,b) do { if ((a)>=(b)) { std::cerr << "\x1b[31mCHECK_LT fail at " << CHECK_INFO#a << " >= "#b << ":\x1b[0m "; AbortOrNot(); }} while(0)
|
||||
#define CHECK_LE(a,b) do { if ((a)>(b)) { std::cerr << "\x1b[31mCHECK_LE fail at " << CHECK_INFO#a << " > "#b << ":\x1b[0m "; AbortOrNot(); }} while(0)
|
||||
|
||||
#define PASS(x) do { (x); std::cout << "\x1b[32mPASS("#x")\x1b[0m" << std::endl; } while(0)
|
||||
|
||||
class U2F_info {
|
||||
public:
|
||||
U2F_info(const char* func, int line) {
|
||||
std::cout << func << "[" << line << "]";
|
||||
}
|
||||
~U2F_info() {
|
||||
std::cout << std::endl;
|
||||
}
|
||||
std::ostream& operator<<(const char* s) {
|
||||
std::cout << s;
|
||||
return std::cout;
|
||||
}
|
||||
};
|
||||
|
||||
extern int arg_Verbose;
|
||||
#define INFO if (arg_Verbose) U2F_info(__FUNCTION__, __LINE__) << ": "
|
||||
|
||||
std::string b2a(const void* ptr, size_t size);
|
||||
std::string b2a(const std::string& s);
|
||||
std::string a2b(const std::string& s);
|
||||
|
||||
float U2Fob_deltaTime(uint64_t* state);
|
||||
|
||||
struct U2Fob {
|
||||
hid_device* dev;
|
||||
int fd_in;
|
||||
int fd_out;
|
||||
char* path;
|
||||
uint32_t cid;
|
||||
int loglevel;
|
||||
uint8_t nonce[INIT_NONCE_SIZE];
|
||||
uint64_t logtime;
|
||||
FILE* logfp;
|
||||
char logbuf[BUFSIZ];
|
||||
};
|
||||
|
||||
struct U2Fob* U2Fob_create();
|
||||
|
||||
void U2Fob_destroy(struct U2Fob* device);
|
||||
|
||||
void U2Fob_setLog(struct U2Fob* device, FILE* fd, int logMask);
|
||||
|
||||
int U2Fob_open(struct U2Fob* device, const char* pathname);
|
||||
|
||||
bool U2Fob_opened(struct U2Fob* device);
|
||||
|
||||
void U2Fob_close(struct U2Fob* device);
|
||||
|
||||
int U2Fob_reopen(struct U2Fob* device);
|
||||
|
||||
int U2Fob_init(struct U2Fob* device);
|
||||
|
||||
uint32_t U2Fob_getCid(struct U2Fob* device);
|
||||
|
||||
int U2Fob_sendHidFrame(struct U2Fob* device, U2FHID_FRAME* out);
|
||||
|
||||
int U2Fob_receiveHidFrame(struct U2Fob* device, U2FHID_FRAME* in,
|
||||
float timeoutSeconds);
|
||||
|
||||
int U2Fob_send(struct U2Fob* device, uint8_t cmd,
|
||||
const void* data, size_t size);
|
||||
|
||||
int U2Fob_recv(struct U2Fob* device, uint8_t* cmd,
|
||||
void* data, size_t size,
|
||||
float timeoutSeconds);
|
||||
|
||||
// Exchanges a pre-formatted APDU buffer with the device.
|
||||
// returns
|
||||
// negative error
|
||||
// positive sw12, e.g. 0x9000, 0x6985 etc.
|
||||
int U2Fob_exchange_apdu_buffer(struct U2Fob* device,
|
||||
void* data,
|
||||
size_t size,
|
||||
std::string* in);
|
||||
|
||||
// Formats an APDU with the given field values, and exchanges it
|
||||
// with the device.
|
||||
// returns
|
||||
// negative error
|
||||
// positive sw12, e.g. 0x9000, 0x6985 etc.
|
||||
int U2Fob_apdu(struct U2Fob* device,
|
||||
uint8_t CLA, uint8_t INS, uint8_t P1, uint8_t P2,
|
||||
const std::string& out,
|
||||
std::string* in);
|
||||
|
||||
bool getCertificate(const U2F_REGISTER_RESP& rsp,
|
||||
std::string* cert);
|
||||
|
||||
bool getSignature(const U2F_REGISTER_RESP& rsp,
|
||||
std::string* sig);
|
||||
|
||||
bool getSubjectPublicKey(const std::string& cert,
|
||||
std::string* pk);
|
||||
|
||||
bool getCertSignature(const std::string& cert,
|
||||
std::string* sig);
|
||||
|
||||
bool verifyCertificate(const std::string& pk,
|
||||
const std::string& cert);
|
||||
|
||||
bool DEV_opened(struct U2Fob* device);
|
||||
void DEV_close(struct U2Fob* device);
|
||||
void DEV_open_path(struct U2Fob* device);
|
||||
int DEV_write(struct U2Fob* device, const uint8_t* src, size_t n);
|
||||
int DEV_read_timeout(struct U2Fob* device, uint8_t* dst, size_t n, int timeout);
|
||||
int DEV_touch(struct U2Fob* device);
|
||||
void DEV_quit(struct U2Fob* device);
|
||||
|
||||
#endif // __U2F_UTIL_H_INCLUDED__
|
Loading…
Reference in New Issue
Block a user