1
0
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:
Pavol Rusnak 2019-09-12 11:46:35 +02:00
parent 75c539ec55
commit ca4581ce74
No known key found for this signature in database
GPG Key ID: 91F3B339B9A02A3D
24 changed files with 4658 additions and 0 deletions

View File

@ -0,0 +1,4 @@
*.o
list
HIDTest
U2FTest

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

View 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.

View 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

View 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.

View 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*>(&regReq),
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(&regRsp, 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, &regRsp.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;
}

View File

@ -0,0 +1 @@
https://github.com/google/u2f-ref-code/tree/master/u2f-tests/HID

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

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

View 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_ */

View 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_

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

View 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

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

View 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_

File diff suppressed because it is too large Load Diff

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

View 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_

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

View 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_

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

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

View 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
}

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