From ca4581ce74bde0eeea29b3c001cfcfd7c3127fcb Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Thu, 12 Sep 2019 11:46:35 +0200 Subject: [PATCH] tests: add fido-tests/u2f-tests-hid --- tests/fido_tests/u2f-tests-hid/.gitignore | 4 + tests/fido_tests/u2f-tests-hid/HIDTest.cc | 670 +++++++++ tests/fido_tests/u2f-tests-hid/LICENSE | 27 + tests/fido_tests/u2f-tests-hid/Makefile | 26 + tests/fido_tests/u2f-tests-hid/README | 41 + tests/fido_tests/u2f-tests-hid/U2FTest.cc | 385 +++++ tests/fido_tests/u2f-tests-hid/UPSTREAM | 1 + tests/fido_tests/u2f-tests-hid/dev.cc | 147 ++ tests/fido_tests/u2f-tests-hid/dsa_sig.c | 126 ++ tests/fido_tests/u2f-tests-hid/dsa_sig.h | 43 + .../fido_tests/u2f-tests-hid/hash-internal.h | 63 + tests/fido_tests/u2f-tests-hid/hidapi-udp.c | 93 ++ tests/fido_tests/u2f-tests-hid/hidapi.h | 33 + tests/fido_tests/u2f-tests-hid/p256.c | 373 +++++ tests/fido_tests/u2f-tests-hid/p256.h | 162 +++ tests/fido_tests/u2f-tests-hid/p256_ec.c | 1279 +++++++++++++++++ tests/fido_tests/u2f-tests-hid/p256_ecdsa.c | 56 + tests/fido_tests/u2f-tests-hid/p256_ecdsa.h | 53 + tests/fido_tests/u2f-tests-hid/sha256.c | 184 +++ tests/fido_tests/u2f-tests-hid/sha256.h | 52 + tests/fido_tests/u2f-tests-hid/u2f.h | 97 ++ tests/fido_tests/u2f-tests-hid/u2f_hid.h | 96 ++ tests/fido_tests/u2f-tests-hid/u2f_util.cc | 489 +++++++ tests/fido_tests/u2f-tests-hid/u2f_util.h | 158 ++ 24 files changed, 4658 insertions(+) create mode 100644 tests/fido_tests/u2f-tests-hid/.gitignore create mode 100644 tests/fido_tests/u2f-tests-hid/HIDTest.cc create mode 100644 tests/fido_tests/u2f-tests-hid/LICENSE create mode 100644 tests/fido_tests/u2f-tests-hid/Makefile create mode 100644 tests/fido_tests/u2f-tests-hid/README create mode 100644 tests/fido_tests/u2f-tests-hid/U2FTest.cc create mode 100644 tests/fido_tests/u2f-tests-hid/UPSTREAM create mode 100644 tests/fido_tests/u2f-tests-hid/dev.cc create mode 100644 tests/fido_tests/u2f-tests-hid/dsa_sig.c create mode 100644 tests/fido_tests/u2f-tests-hid/dsa_sig.h create mode 100644 tests/fido_tests/u2f-tests-hid/hash-internal.h create mode 100644 tests/fido_tests/u2f-tests-hid/hidapi-udp.c create mode 100644 tests/fido_tests/u2f-tests-hid/hidapi.h create mode 100644 tests/fido_tests/u2f-tests-hid/p256.c create mode 100644 tests/fido_tests/u2f-tests-hid/p256.h create mode 100644 tests/fido_tests/u2f-tests-hid/p256_ec.c create mode 100644 tests/fido_tests/u2f-tests-hid/p256_ecdsa.c create mode 100644 tests/fido_tests/u2f-tests-hid/p256_ecdsa.h create mode 100644 tests/fido_tests/u2f-tests-hid/sha256.c create mode 100644 tests/fido_tests/u2f-tests-hid/sha256.h create mode 100644 tests/fido_tests/u2f-tests-hid/u2f.h create mode 100644 tests/fido_tests/u2f-tests-hid/u2f_hid.h create mode 100644 tests/fido_tests/u2f-tests-hid/u2f_util.cc create mode 100644 tests/fido_tests/u2f-tests-hid/u2f_util.h diff --git a/tests/fido_tests/u2f-tests-hid/.gitignore b/tests/fido_tests/u2f-tests-hid/.gitignore new file mode 100644 index 0000000000..3ab77c851a --- /dev/null +++ b/tests/fido_tests/u2f-tests-hid/.gitignore @@ -0,0 +1,4 @@ +*.o +list +HIDTest +U2FTest diff --git a/tests/fido_tests/u2f-tests-hid/HIDTest.cc b/tests/fido_tests/u2f-tests-hid/HIDTest.cc new file mode 100644 index 0000000000..cfb926622f --- /dev/null +++ b/tests/fido_tests/u2f-tests-hid/HIDTest.cc @@ -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 +#include +#include +#include + +#include +#include + +#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] + << " [-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; +} diff --git a/tests/fido_tests/u2f-tests-hid/LICENSE b/tests/fido_tests/u2f-tests-hid/LICENSE new file mode 100644 index 0000000000..cf78a508aa --- /dev/null +++ b/tests/fido_tests/u2f-tests-hid/LICENSE @@ -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. diff --git a/tests/fido_tests/u2f-tests-hid/Makefile b/tests/fido_tests/u2f-tests-hid/Makefile new file mode 100644 index 0000000000..62f86ba18c --- /dev/null +++ b/tests/fido_tests/u2f-tests-hid/Makefile @@ -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 diff --git a/tests/fido_tests/u2f-tests-hid/README b/tests/fido_tests/u2f-tests-hid/README new file mode 100644 index 0000000000..f706920fa2 --- /dev/null +++ b/tests/fido_tests/u2f-tests-hid/README @@ -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. diff --git a/tests/fido_tests/u2f-tests-hid/U2FTest.cc b/tests/fido_tests/u2f-tests-hid/U2FTest.cc new file mode 100644 index 0000000000..0a127ee784 --- /dev/null +++ b/tests/fido_tests/u2f-tests-hid/U2FTest.cc @@ -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 +#include +#include +#include + +#include +#include + +#ifdef __OS_WIN +#include // ntohl, htonl +#else +#include // 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(®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(&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] + << " [-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; +} diff --git a/tests/fido_tests/u2f-tests-hid/UPSTREAM b/tests/fido_tests/u2f-tests-hid/UPSTREAM new file mode 100644 index 0000000000..cf426628ef --- /dev/null +++ b/tests/fido_tests/u2f-tests-hid/UPSTREAM @@ -0,0 +1 @@ +https://github.com/google/u2f-ref-code/tree/master/u2f-tests/HID diff --git a/tests/fido_tests/u2f-tests-hid/dev.cc b/tests/fido_tests/u2f-tests-hid/dev.cc new file mode 100644 index 0000000000..e02cb2e369 --- /dev/null +++ b/tests/fido_tests/u2f-tests-hid/dev.cc @@ -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 +#include +#include +#include + +#include + +#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; +} diff --git a/tests/fido_tests/u2f-tests-hid/dsa_sig.c b/tests/fido_tests/u2f-tests-hid/dsa_sig.c new file mode 100644 index 0000000000..681d943bee --- /dev/null +++ b/tests/fido_tests/u2f-tests-hid/dsa_sig.c @@ -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 + +#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; +} diff --git a/tests/fido_tests/u2f-tests-hid/dsa_sig.h b/tests/fido_tests/u2f-tests-hid/dsa_sig.h new file mode 100644 index 0000000000..a83daf91a2 --- /dev/null +++ b/tests/fido_tests/u2f-tests-hid/dsa_sig.h @@ -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_ */ diff --git a/tests/fido_tests/u2f-tests-hid/hash-internal.h b/tests/fido_tests/u2f-tests-hid/hash-internal.h new file mode 100644 index 0000000000..c813b449f9 --- /dev/null +++ b/tests/fido_tests/u2f-tests-hid/hash-internal.h @@ -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 + +#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_ diff --git a/tests/fido_tests/u2f-tests-hid/hidapi-udp.c b/tests/fido_tests/u2f-tests-hid/hidapi-udp.c new file mode 100644 index 0000000000..5b9ce92622 --- /dev/null +++ b/tests/fido_tests/u2f-tests-hid/hidapi-udp.c @@ -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 . + */ + +#include "hidapi.h" + +#include +#include +#include +#include +#include + +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; +} diff --git a/tests/fido_tests/u2f-tests-hid/hidapi.h b/tests/fido_tests/u2f-tests-hid/hidapi.h new file mode 100644 index 0000000000..51560467d9 --- /dev/null +++ b/tests/fido_tests/u2f-tests-hid/hidapi.h @@ -0,0 +1,33 @@ +#ifndef __HIDAPI_H_ + +#include +#include +#include + +#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 diff --git a/tests/fido_tests/u2f-tests-hid/p256.c b/tests/fido_tests/u2f-tests-hid/p256.c new file mode 100644 index 0000000000..e90b3a2d71 --- /dev/null +++ b/tests/fido_tests/u2f-tests-hid/p256.c @@ -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 +#include +#include +#include + +#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; + } +} diff --git a/tests/fido_tests/u2f-tests-hid/p256.h b/tests/fido_tests/u2f-tests-hid/p256.h new file mode 100644 index 0000000000..465a1b9225 --- /dev/null +++ b/tests/fido_tests/u2f-tests-hid/p256.h @@ -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 + +#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_ diff --git a/tests/fido_tests/u2f-tests-hid/p256_ec.c b/tests/fido_tests/u2f-tests-hid/p256_ec.c new file mode 100644 index 0000000000..2276d94280 --- /dev/null +++ b/tests/fido_tests/u2f-tests-hid/p256_ec.c @@ -0,0 +1,1279 @@ +/* + * 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 +#include + +#include +#include + +#include "p256.h" + +typedef uint8_t u8; +typedef uint32_t u32; +typedef int32_t s32; +typedef uint64_t u64; + +/* Our field elements are represented as nine 32-bit limbs. + * + * The value of an felem (field element) is: + * x[0] + (x[1] * 2**29) + (x[2] * 2**57) + ... + (x[8] * 2**228) + * + * That is, each limb is alternately 29 or 28-bits wide in little-endian + * order. + * + * This means that an felem hits 2**257, rather than 2**256 as we would like. A + * 28, 29, ... pattern would cause us to hit 2**256, but that causes problems + * when multiplying as terms end up one bit short of a limb which would require + * much bit-shifting to correct. + * + * Finally, the values stored in an felem are in Montgomery form. So the value + * |y| is stored as (y*R) mod p, where p is the P-256 prime and R is 2**257. + */ +typedef u32 limb; +#define NLIMBS 9 +typedef limb felem[NLIMBS]; + +static const limb kBottom28Bits = 0xfffffff; +static const limb kBottom29Bits = 0x1fffffff; + +/* kOne is the number 1 as an felem. It's 2**257 mod p split up into 29 and + * 28-bit words. */ +static const felem kOne = { + 2, 0, 0, 0xffff800, + 0x1fffffff, 0xfffffff, 0x1fbfffff, 0x1ffffff, + 0 +}; +static const felem kZero = {0}; +static const felem kP = { + 0x1fffffff, 0xfffffff, 0x1fffffff, 0x3ff, + 0, 0, 0x200000, 0xf000000, + 0xfffffff +}; +static const felem k2P = { + 0x1ffffffe, 0xfffffff, 0x1fffffff, 0x7ff, + 0, 0, 0x400000, 0xe000000, + 0x1fffffff +}; +/* kPrecomputed contains precomputed values to aid the calculation of scalar + * multiples of the base point, G. It's actually two, equal length, tables + * concatenated. + * + * The first table contains (x,y) felem pairs for 16 multiples of the base + * point, G. + * + * Index | Index (binary) | Value + * 0 | 0000 | 0G (all zeros, omitted) + * 1 | 0001 | G + * 2 | 0010 | 2**64G + * 3 | 0011 | 2**64G + G + * 4 | 0100 | 2**128G + * 5 | 0101 | 2**128G + G + * 6 | 0110 | 2**128G + 2**64G + * 7 | 0111 | 2**128G + 2**64G + G + * 8 | 1000 | 2**192G + * 9 | 1001 | 2**192G + G + * 10 | 1010 | 2**192G + 2**64G + * 11 | 1011 | 2**192G + 2**64G + G + * 12 | 1100 | 2**192G + 2**128G + * 13 | 1101 | 2**192G + 2**128G + G + * 14 | 1110 | 2**192G + 2**128G + 2**64G + * 15 | 1111 | 2**192G + 2**128G + 2**64G + G + * + * The second table follows the same style, but the terms are 2**32G, + * 2**96G, 2**160G, 2**224G. + * + * This is ~2KB of data. */ +static const limb kPrecomputed[NLIMBS * 2 * 15 * 2] = { + 0x11522878, 0xe730d41, 0xdb60179, 0x4afe2ff, 0x12883add, 0xcaddd88, 0x119e7edc, 0xd4a6eab, 0x3120bee, + 0x1d2aac15, 0xf25357c, 0x19e45cdd, 0x5c721d0, 0x1992c5a5, 0xa237487, 0x154ba21, 0x14b10bb, 0xae3fe3, + 0xd41a576, 0x922fc51, 0x234994f, 0x60b60d3, 0x164586ae, 0xce95f18, 0x1fe49073, 0x3fa36cc, 0x5ebcd2c, + 0xb402f2f, 0x15c70bf, 0x1561925c, 0x5a26704, 0xda91e90, 0xcdc1c7f, 0x1ea12446, 0xe1ade1e, 0xec91f22, + 0x26f7778, 0x566847e, 0xa0bec9e, 0x234f453, 0x1a31f21a, 0xd85e75c, 0x56c7109, 0xa267a00, 0xb57c050, + 0x98fb57, 0xaa837cc, 0x60c0792, 0xcfa5e19, 0x61bab9e, 0x589e39b, 0xa324c5, 0x7d6dee7, 0x2976e4b, + 0x1fc4124a, 0xa8c244b, 0x1ce86762, 0xcd61c7e, 0x1831c8e0, 0x75774e1, 0x1d96a5a9, 0x843a649, 0xc3ab0fa, + 0x6e2e7d5, 0x7673a2a, 0x178b65e8, 0x4003e9b, 0x1a1f11c2, 0x7816ea, 0xf643e11, 0x58c43df, 0xf423fc2, + 0x19633ffa, 0x891f2b2, 0x123c231c, 0x46add8c, 0x54700dd, 0x59e2b17, 0x172db40f, 0x83e277d, 0xb0dd609, + 0xfd1da12, 0x35c6e52, 0x19ede20c, 0xd19e0c0, 0x97d0f40, 0xb015b19, 0x449e3f5, 0xe10c9e, 0x33ab581, + 0x56a67ab, 0x577734d, 0x1dddc062, 0xc57b10d, 0x149b39d, 0x26a9e7b, 0xc35df9f, 0x48764cd, 0x76dbcca, + 0xca4b366, 0xe9303ab, 0x1a7480e7, 0x57e9e81, 0x1e13eb50, 0xf466cf3, 0x6f16b20, 0x4ba3173, 0xc168c33, + 0x15cb5439, 0x6a38e11, 0x73658bd, 0xb29564f, 0x3f6dc5b, 0x53b97e, 0x1322c4c0, 0x65dd7ff, 0x3a1e4f6, + 0x14e614aa, 0x9246317, 0x1bc83aca, 0xad97eed, 0xd38ce4a, 0xf82b006, 0x341f077, 0xa6add89, 0x4894acd, + 0x9f162d5, 0xf8410ef, 0x1b266a56, 0xd7f223, 0x3e0cb92, 0xe39b672, 0x6a2901a, 0x69a8556, 0x7e7c0, + 0x9b7d8d3, 0x309a80, 0x1ad05f7f, 0xc2fb5dd, 0xcbfd41d, 0x9ceb638, 0x1051825c, 0xda0cf5b, 0x812e881, + 0x6f35669, 0x6a56f2c, 0x1df8d184, 0x345820, 0x1477d477, 0x1645db1, 0xbe80c51, 0xc22be3e, 0xe35e65a, + 0x1aeb7aa0, 0xc375315, 0xf67bc99, 0x7fdd7b9, 0x191fc1be, 0x61235d, 0x2c184e9, 0x1c5a839, 0x47a1e26, + 0xb7cb456, 0x93e225d, 0x14f3c6ed, 0xccc1ac9, 0x17fe37f3, 0x4988989, 0x1a90c502, 0x2f32042, 0xa17769b, + 0xafd8c7c, 0x8191c6e, 0x1dcdb237, 0x16200c0, 0x107b32a1, 0x66c08db, 0x10d06a02, 0x3fc93, 0x5620023, + 0x16722b27, 0x68b5c59, 0x270fcfc, 0xfad0ecc, 0xe5de1c2, 0xeab466b, 0x2fc513c, 0x407f75c, 0xbaab133, + 0x9705fe9, 0xb88b8e7, 0x734c993, 0x1e1ff8f, 0x19156970, 0xabd0f00, 0x10469ea7, 0x3293ac0, 0xcdc98aa, + 0x1d843fd, 0xe14bfe8, 0x15be825f, 0x8b5212, 0xeb3fb67, 0x81cbd29, 0xbc62f16, 0x2b6fcc7, 0xf5a4e29, + 0x13560b66, 0xc0b6ac2, 0x51ae690, 0xd41e271, 0xf3e9bd4, 0x1d70aab, 0x1029f72, 0x73e1c35, 0xee70fbc, + 0xad81baf, 0x9ecc49a, 0x86c741e, 0xfe6be30, 0x176752e7, 0x23d416, 0x1f83de85, 0x27de188, 0x66f70b8, + 0x181cd51f, 0x96b6e4c, 0x188f2335, 0xa5df759, 0x17a77eb6, 0xfeb0e73, 0x154ae914, 0x2f3ec51, 0x3826b59, + 0xb91f17d, 0x1c72949, 0x1362bf0a, 0xe23fddf, 0xa5614b0, 0xf7d8f, 0x79061, 0x823d9d2, 0x8213f39, + 0x1128ae0b, 0xd095d05, 0xb85c0c2, 0x1ecb2ef, 0x24ddc84, 0xe35e901, 0x18411a4a, 0xf5ddc3d, 0x3786689, + 0x52260e8, 0x5ae3564, 0x542b10d, 0x8d93a45, 0x19952aa4, 0x996cc41, 0x1051a729, 0x4be3499, 0x52b23aa, + 0x109f307e, 0x6f5b6bb, 0x1f84e1e7, 0x77a0cfa, 0x10c4df3f, 0x25a02ea, 0xb048035, 0xe31de66, 0xc6ecaa3, + 0x28ea335, 0x2886024, 0x1372f020, 0xf55d35, 0x15e4684c, 0xf2a9e17, 0x1a4a7529, 0xcb7beb1, 0xb2a78a1, + 0x1ab21f1f, 0x6361ccf, 0x6c9179d, 0xb135627, 0x1267b974, 0x4408bad, 0x1cbff658, 0xe3d6511, 0xc7d76f, + 0x1cc7a69, 0xe7ee31b, 0x54fab4f, 0x2b914f, 0x1ad27a30, 0xcd3579e, 0xc50124c, 0x50daa90, 0xb13f72, + 0xb06aa75, 0x70f5cc6, 0x1649e5aa, 0x84a5312, 0x329043c, 0x41c4011, 0x13d32411, 0xb04a838, 0xd760d2d, + 0x1713b532, 0xbaa0c03, 0x84022ab, 0x6bcf5c1, 0x2f45379, 0x18ae070, 0x18c9e11e, 0x20bca9a, 0x66f496b, + 0x3eef294, 0x67500d2, 0xd7f613c, 0x2dbbeb, 0xb741038, 0xe04133f, 0x1582968d, 0xbe985f7, 0x1acbc1a, + 0x1a6a939f, 0x33e50f6, 0xd665ed4, 0xb4b7bd6, 0x1e5a3799, 0x6b33847, 0x17fa56ff, 0x65ef930, 0x21dc4a, + 0x2b37659, 0x450fe17, 0xb357b65, 0xdf5efac, 0x15397bef, 0x9d35a7f, 0x112ac15f, 0x624e62e, 0xa90ae2f, + 0x107eecd2, 0x1f69bbe, 0x77d6bce, 0x5741394, 0x13c684fc, 0x950c910, 0x725522b, 0xdc78583, 0x40eeabb, + 0x1fde328a, 0xbd61d96, 0xd28c387, 0x9e77d89, 0x12550c40, 0x759cb7d, 0x367ef34, 0xae2a960, 0x91b8bdc, + 0x93462a9, 0xf469ef, 0xb2e9aef, 0xd2ca771, 0x54e1f42, 0x7aaa49, 0x6316abb, 0x2413c8e, 0x5425bf9, + 0x1bed3e3a, 0xf272274, 0x1f5e7326, 0x6416517, 0xea27072, 0x9cedea7, 0x6e7633, 0x7c91952, 0xd806dce, + 0x8e2a7e1, 0xe421e1a, 0x418c9e1, 0x1dbc890, 0x1b395c36, 0xa1dc175, 0x1dc4ef73, 0x8956f34, 0xe4b5cf2, + 0x1b0d3a18, 0x3194a36, 0x6c2641f, 0xe44124c, 0xa2f4eaa, 0xa8c25ba, 0xf927ed7, 0x627b614, 0x7371cca, + 0xba16694, 0x417bc03, 0x7c0a7e3, 0x9c35c19, 0x1168a205, 0x8b6b00d, 0x10e3edc9, 0x9c19bf2, 0x5882229, + 0x1b2b4162, 0xa5cef1a, 0x1543622b, 0x9bd433e, 0x364e04d, 0x7480792, 0x5c9b5b3, 0xe85ff25, 0x408ef57, + 0x1814cfa4, 0x121b41b, 0xd248a0f, 0x3b05222, 0x39bb16a, 0xc75966d, 0xa038113, 0xa4a1769, 0x11fbc6c, + 0x917e50e, 0xeec3da8, 0x169d6eac, 0x10c1699, 0xa416153, 0xf724912, 0x15cd60b7, 0x4acbad9, 0x5efc5fa, + 0xf150ed7, 0x122b51, 0x1104b40a, 0xcb7f442, 0xfbb28ff, 0x6ac53ca, 0x196142cc, 0x7bf0fa9, 0x957651, + 0x4e0f215, 0xed439f8, 0x3f46bd5, 0x5ace82f, 0x110916b6, 0x6db078, 0xffd7d57, 0xf2ecaac, 0xca86dec, + 0x15d6b2da, 0x965ecc9, 0x1c92b4c2, 0x1f3811, 0x1cb080f5, 0x2d8b804, 0x19d1c12d, 0xf20bd46, 0x1951fa7, + 0xa3656c3, 0x523a425, 0xfcd0692, 0xd44ddc8, 0x131f0f5b, 0xaf80e4a, 0xcd9fc74, 0x99bb618, 0x2db944c, + 0xa673090, 0x1c210e1, 0x178c8d23, 0x1474383, 0x10b8743d, 0x985a55b, 0x2e74779, 0x576138, 0x9587927, + 0x133130fa, 0xbe05516, 0x9f4d619, 0xbb62570, 0x99ec591, 0xd9468fe, 0x1d07782d, 0xfc72e0b, 0x701b298, + 0x1863863b, 0x85954b8, 0x121a0c36, 0x9e7fedf, 0xf64b429, 0x9b9d71e, 0x14e2f5d8, 0xf858d3a, 0x942eea8, + 0xda5b765, 0x6edafff, 0xa9d18cc, 0xc65e4ba, 0x1c747e86, 0xe4ea915, 0x1981d7a1, 0x8395659, 0x52ed4e2, + 0x87d43b7, 0x37ab11b, 0x19d292ce, 0xf8d4692, 0x18c3053f, 0x8863e13, 0x4c146c0, 0x6bdf55a, 0x4e4457d, + 0x16152289, 0xac78ec2, 0x1a59c5a2, 0x2028b97, 0x71c2d01, 0x295851f, 0x404747b, 0x878558d, 0x7d29aa4, + 0x13d8341f, 0x8daefd7, 0x139c972d, 0x6b7ea75, 0xd4a9dde, 0xff163d8, 0x81d55d7, 0xa5bef68, 0xb7b30d8, + 0xbe73d6f, 0xaa88141, 0xd976c81, 0x7e7a9cc, 0x18beb771, 0xd773cbd, 0x13f51951, 0x9d0c177, 0x1c49a78, +}; + + +/* Field element operations: */ + +/* NON_ZERO_TO_ALL_ONES returns: + * 0xffffffff for 0 < x <= 2**31 + * 0 for x == 0 or x > 2**31. + * + * x must be a u32 or an equivalent type such as limb. */ +#define NON_ZERO_TO_ALL_ONES(x) ((((u32)(x) - 1) >> 31) - 1) + +/* felem_reduce_carry adds a multiple of p in order to cancel |carry|, + * which is a term at 2**257. + * + * On entry: carry < 2**3, inout[0,2,...] < 2**29, inout[1,3,...] < 2**28. + * On exit: inout[0,2,..] < 2**30, inout[1,3,...] < 2**29. */ +static void felem_reduce_carry(felem inout, limb carry) { + const u32 carry_mask = NON_ZERO_TO_ALL_ONES(carry); + + inout[0] += carry << 1; + inout[3] += 0x10000000 & carry_mask; + /* carry < 2**3 thus (carry << 11) < 2**14 and we added 2**28 in the + * previous line therefore this doesn't underflow. */ + inout[3] -= carry << 11; + inout[4] += (0x20000000 - 1) & carry_mask; + inout[5] += (0x10000000 - 1) & carry_mask; + inout[6] += (0x20000000 - 1) & carry_mask; + inout[6] -= carry << 22; + /* This may underflow if carry is non-zero but, if so, we'll fix it in the + * next line. */ + inout[7] -= 1 & carry_mask; + inout[7] += carry << 25; +} + +/* felem_sum sets out = in+in2. + * + * On entry, in[i]+in2[i] must not overflow a 32-bit word. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29 */ +static void felem_sum(felem out, const felem in, const felem in2) { + limb carry = 0; + unsigned i; + + for (i = 0;; i++) { + out[i] = in[i] + in2[i]; + out[i] += carry; + carry = out[i] >> 29; + out[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + out[i] = in[i] + in2[i]; + out[i] += carry; + carry = out[i] >> 28; + out[i] &= kBottom28Bits; + } + + felem_reduce_carry(out, carry); +} + +#define two31m3 (((limb)1) << 31) - (((limb)1) << 3) +#define two30m2 (((limb)1) << 30) - (((limb)1) << 2) +#define two30p13m2 (((limb)1) << 30) + (((limb)1) << 13) - (((limb)1) << 2) +#define two31m2 (((limb)1) << 31) - (((limb)1) << 2) +#define two31p24m2 (((limb)1) << 31) + (((limb)1) << 24) - (((limb)1) << 2) +#define two30m27m2 (((limb)1) << 30) - (((limb)1) << 27) - (((limb)1) << 2) + +/* zero31 is 0 mod p. */ +static const felem zero31 = { two31m3, two30m2, two31m2, two30p13m2, two31m2, two30m2, two31p24m2, two30m27m2, two31m2 }; + +/* felem_diff sets out = in-in2. + * + * On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29 and + * in2[0,2,...] < 2**30, in2[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */ +static void felem_diff(felem out, const felem in, const felem in2) { + limb carry = 0; + unsigned i; + + for (i = 0;; i++) { + out[i] = in[i] - in2[i]; + out[i] += zero31[i]; + out[i] += carry; + carry = out[i] >> 29; + out[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + out[i] = in[i] - in2[i]; + out[i] += zero31[i]; + out[i] += carry; + carry = out[i] >> 28; + out[i] &= kBottom28Bits; + } + + felem_reduce_carry(out, carry); +} + +/* felem_reduce_degree sets out = tmp/R mod p where tmp contains 64-bit words + * with the same 29,28,... bit positions as an felem. + * + * The values in felems are in Montgomery form: x*R mod p where R = 2**257. + * Since we just multiplied two Montgomery values together, the result is + * x*y*R*R mod p. We wish to divide by R in order for the result also to be + * in Montgomery form. + * + * On entry: tmp[i] < 2**64 + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29 */ +static void felem_reduce_degree(felem out, u64 tmp[17]) { + /* The following table may be helpful when reading this code: + * + * Limb number: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10... + * Width (bits): 29| 28| 29| 28| 29| 28| 29| 28| 29| 28| 29 + * Start bit: 0 | 29| 57| 86|114|143|171|200|228|257|285 + * (odd phase): 0 | 28| 57| 85|114|142|171|199|228|256|285 */ + limb tmp2[18], carry, x, xMask; + unsigned i; + + /* tmp contains 64-bit words with the same 29,28,29-bit positions as an + * felem. So the top of an element of tmp might overlap with another + * element two positions down. The following loop eliminates this + * overlap. */ + tmp2[0] = (limb)(tmp[0] & kBottom29Bits); + + /* In the following we use "(limb) tmp[x]" and "(limb) (tmp[x]>>32)" to try + * and hint to the compiler that it can do a single-word shift by selecting + * the right register rather than doing a double-word shift and truncating + * afterwards. */ + tmp2[1] = ((limb) tmp[0]) >> 29; + tmp2[1] |= (((limb)(tmp[0] >> 32)) << 3) & kBottom28Bits; + tmp2[1] += ((limb) tmp[1]) & kBottom28Bits; + carry = tmp2[1] >> 28; + tmp2[1] &= kBottom28Bits; + + for (i = 2; i < 17; i++) { + tmp2[i] = ((limb)(tmp[i - 2] >> 32)) >> 25; + tmp2[i] += ((limb)(tmp[i - 1])) >> 28; + tmp2[i] += (((limb)(tmp[i - 1] >> 32)) << 4) & kBottom29Bits; + tmp2[i] += ((limb) tmp[i]) & kBottom29Bits; + tmp2[i] += carry; + carry = tmp2[i] >> 29; + tmp2[i] &= kBottom29Bits; + + i++; + if (i == 17) + break; + tmp2[i] = ((limb)(tmp[i - 2] >> 32)) >> 25; + tmp2[i] += ((limb)(tmp[i - 1])) >> 29; + tmp2[i] += (((limb)(tmp[i - 1] >> 32)) << 3) & kBottom28Bits; + tmp2[i] += ((limb) tmp[i]) & kBottom28Bits; + tmp2[i] += carry; + carry = tmp2[i] >> 28; + tmp2[i] &= kBottom28Bits; + } + + tmp2[17] = ((limb)(tmp[15] >> 32)) >> 25; + tmp2[17] += ((limb)(tmp[16])) >> 29; + tmp2[17] += (((limb)(tmp[16] >> 32)) << 3); + tmp2[17] += carry; + + /* Montgomery elimination of terms. + * + * Since R is 2**257, we can divide by R with a bitwise shift if we can + * ensure that the right-most 257 bits are all zero. We can make that true by + * adding multiplies of p without affecting the value. + * + * So we eliminate limbs from right to left. Since the bottom 29 bits of p + * are all ones, then by adding tmp2[0]*p to tmp2 we'll make tmp2[0] == 0. + * We can do that for 8 further limbs and then right shift to eliminate the + * extra factor of R. */ + for (i = 0;; i += 2) { + tmp2[i + 1] += tmp2[i] >> 29; + x = tmp2[i] & kBottom29Bits; + xMask = NON_ZERO_TO_ALL_ONES(x); + tmp2[i] = 0; + + /* The bounds calculations for this loop are tricky. Each iteration of + * the loop eliminates two words by adding values to words to their + * right. + * + * The following table contains the amounts added to each word (as an + * offset from the value of i at the top of the loop). The amounts are + * accounted for from the first and second half of the loop separately + * and are written as, for example, 28 to mean a value <2**28. + * + * Word: 3 4 5 6 7 8 9 10 + * Added in top half: 28 11 29 21 29 28 + * 28 29 + * 29 + * Added in bottom half: 29 10 28 21 28 28 + * 29 + * + * The value that is currently offset 7 will be offset 5 for the next + * iteration and then offset 3 for the iteration after that. Therefore + * the total value added will be the values added at 7, 5 and 3. + * + * The following table accumulates these values. The sums at the bottom + * are written as, for example, 29+28, to mean a value < 2**29+2**28. + * + * Word: 3 4 5 6 7 8 9 10 11 12 13 + * 28 11 10 29 21 29 28 28 28 28 28 + * 29 28 11 28 29 28 29 28 29 28 + * 29 28 21 21 29 21 29 21 + * 10 29 28 21 28 21 28 + * 28 29 28 29 28 29 28 + * 11 10 29 10 29 10 + * 29 28 11 28 11 + * 29 29 + * -------------------------------------------- + * 30+ 31+ 30+ 31+ 30+ + * 28+ 29+ 28+ 29+ 21+ + * 21+ 28+ 21+ 28+ 10 + * 10 21+ 10 21+ + * 11 11 + * + * So the greatest amount is added to tmp2[10] and tmp2[12]. If + * tmp2[10/12] has an initial value of <2**29, then the maximum value + * will be < 2**31 + 2**30 + 2**28 + 2**21 + 2**11, which is < 2**32, + * as required. */ + tmp2[i + 3] += (x << 10) & kBottom28Bits; + tmp2[i + 4] += (x >> 18); + + tmp2[i + 6] += (x << 21) & kBottom29Bits; + tmp2[i + 7] += x >> 8; + + /* At position 200, which is the starting bit position for word 7, we + * have a factor of 0xf000000 = 2**28 - 2**24. */ + tmp2[i + 7] += 0x10000000 & xMask; + /* Word 7 is 28 bits wide, so the 2**28 term exactly hits word 8. */ + tmp2[i + 8] += (x - 1) & xMask; + tmp2[i + 7] -= (x << 24) & kBottom28Bits; + tmp2[i + 8] -= x >> 4; + + tmp2[i + 8] += 0x20000000 & xMask; + tmp2[i + 8] -= x; + tmp2[i + 8] += (x << 28) & kBottom29Bits; + tmp2[i + 9] += ((x >> 1) - 1) & xMask; + + if (i+1 == NLIMBS) + break; + tmp2[i + 2] += tmp2[i + 1] >> 28; + x = tmp2[i + 1] & kBottom28Bits; + xMask = NON_ZERO_TO_ALL_ONES(x); + tmp2[i + 1] = 0; + + tmp2[i + 4] += (x << 11) & kBottom29Bits; + tmp2[i + 5] += (x >> 18); + + tmp2[i + 7] += (x << 21) & kBottom28Bits; + tmp2[i + 8] += x >> 7; + + /* At position 199, which is the starting bit of the 8th word when + * dealing with a context starting on an odd word, we have a factor of + * 0x1e000000 = 2**29 - 2**25. Since we have not updated i, the 8th + * word from i+1 is i+8. */ + tmp2[i + 8] += 0x20000000 & xMask; + tmp2[i + 9] += (x - 1) & xMask; + tmp2[i + 8] -= (x << 25) & kBottom29Bits; + tmp2[i + 9] -= x >> 4; + + tmp2[i + 9] += 0x10000000 & xMask; + tmp2[i + 9] -= x; + tmp2[i + 10] += (x - 1) & xMask; + } + + /* We merge the right shift with a carry chain. The words above 2**257 have + * widths of 28,29,... which we need to correct when copying them down. */ + carry = 0; + for (i = 0; i < 8; i++) { + /* The maximum value of tmp2[i + 9] occurs on the first iteration and + * is < 2**30+2**29+2**28. Adding 2**29 (from tmp2[i + 10]) is + * therefore safe. */ + out[i] = tmp2[i + 9]; + out[i] += carry; + out[i] += (tmp2[i + 10] << 28) & kBottom29Bits; + carry = out[i] >> 29; + out[i] &= kBottom29Bits; + + i++; + out[i] = tmp2[i + 9] >> 1; + out[i] += carry; + carry = out[i] >> 28; + out[i] &= kBottom28Bits; + } + + out[8] = tmp2[17]; + out[8] += carry; + carry = out[8] >> 29; + out[8] &= kBottom29Bits; + + felem_reduce_carry(out, carry); +} + +/* felem_square sets out=in*in. + * + * On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */ +static void felem_square(felem out, const felem in) { + u64 tmp[17]; + + tmp[0] = ((u64) in[0]) * in[0]; + tmp[1] = ((u64) in[0]) * (in[1] << 1); + tmp[2] = ((u64) in[0]) * (in[2] << 1) + + ((u64) in[1]) * (in[1] << 1); + tmp[3] = ((u64) in[0]) * (in[3] << 1) + + ((u64) in[1]) * (in[2] << 1); + tmp[4] = ((u64) in[0]) * (in[4] << 1) + + ((u64) in[1]) * (in[3] << 2) + ((u64) in[2]) * in[2]; + tmp[5] = ((u64) in[0]) * (in[5] << 1) + ((u64) in[1]) * + (in[4] << 1) + ((u64) in[2]) * (in[3] << 1); + tmp[6] = ((u64) in[0]) * (in[6] << 1) + ((u64) in[1]) * + (in[5] << 2) + ((u64) in[2]) * (in[4] << 1) + + ((u64) in[3]) * (in[3] << 1); + tmp[7] = ((u64) in[0]) * (in[7] << 1) + ((u64) in[1]) * + (in[6] << 1) + ((u64) in[2]) * (in[5] << 1) + + ((u64) in[3]) * (in[4] << 1); + /* tmp[8] has the greatest value of 2**61 + 2**60 + 2**61 + 2**60 + 2**60, + * which is < 2**64 as required. */ + tmp[8] = ((u64) in[0]) * (in[8] << 1) + ((u64) in[1]) * + (in[7] << 2) + ((u64) in[2]) * (in[6] << 1) + + ((u64) in[3]) * (in[5] << 2) + ((u64) in[4]) * in[4]; + tmp[9] = ((u64) in[1]) * (in[8] << 1) + ((u64) in[2]) * + (in[7] << 1) + ((u64) in[3]) * (in[6] << 1) + + ((u64) in[4]) * (in[5] << 1); + tmp[10] = ((u64) in[2]) * (in[8] << 1) + ((u64) in[3]) * + (in[7] << 2) + ((u64) in[4]) * (in[6] << 1) + + ((u64) in[5]) * (in[5] << 1); + tmp[11] = ((u64) in[3]) * (in[8] << 1) + ((u64) in[4]) * + (in[7] << 1) + ((u64) in[5]) * (in[6] << 1); + tmp[12] = ((u64) in[4]) * (in[8] << 1) + + ((u64) in[5]) * (in[7] << 2) + ((u64) in[6]) * in[6]; + tmp[13] = ((u64) in[5]) * (in[8] << 1) + + ((u64) in[6]) * (in[7] << 1); + tmp[14] = ((u64) in[6]) * (in[8] << 1) + + ((u64) in[7]) * (in[7] << 1); + tmp[15] = ((u64) in[7]) * (in[8] << 1); + tmp[16] = ((u64) in[8]) * in[8]; + + felem_reduce_degree(out, tmp); +} + +/* felem_mul sets out=in*in2. + * + * On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29 and + * in2[0,2,...] < 2**30, in2[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */ +static void felem_mul(felem out, const felem in, const felem in2) { + u64 tmp[17]; + + tmp[0] = ((u64) in[0]) * in2[0]; + tmp[1] = ((u64) in[0]) * (in2[1] << 0) + + ((u64) in[1]) * (in2[0] << 0); + tmp[2] = ((u64) in[0]) * (in2[2] << 0) + ((u64) in[1]) * + (in2[1] << 1) + ((u64) in[2]) * (in2[0] << 0); + tmp[3] = ((u64) in[0]) * (in2[3] << 0) + ((u64) in[1]) * + (in2[2] << 0) + ((u64) in[2]) * (in2[1] << 0) + + ((u64) in[3]) * (in2[0] << 0); + tmp[4] = ((u64) in[0]) * (in2[4] << 0) + ((u64) in[1]) * + (in2[3] << 1) + ((u64) in[2]) * (in2[2] << 0) + + ((u64) in[3]) * (in2[1] << 1) + + ((u64) in[4]) * (in2[0] << 0); + tmp[5] = ((u64) in[0]) * (in2[5] << 0) + ((u64) in[1]) * + (in2[4] << 0) + ((u64) in[2]) * (in2[3] << 0) + + ((u64) in[3]) * (in2[2] << 0) + ((u64) in[4]) * + (in2[1] << 0) + ((u64) in[5]) * (in2[0] << 0); + tmp[6] = ((u64) in[0]) * (in2[6] << 0) + ((u64) in[1]) * + (in2[5] << 1) + ((u64) in[2]) * (in2[4] << 0) + + ((u64) in[3]) * (in2[3] << 1) + ((u64) in[4]) * + (in2[2] << 0) + ((u64) in[5]) * (in2[1] << 1) + + ((u64) in[6]) * (in2[0] << 0); + tmp[7] = ((u64) in[0]) * (in2[7] << 0) + ((u64) in[1]) * + (in2[6] << 0) + ((u64) in[2]) * (in2[5] << 0) + + ((u64) in[3]) * (in2[4] << 0) + ((u64) in[4]) * + (in2[3] << 0) + ((u64) in[5]) * (in2[2] << 0) + + ((u64) in[6]) * (in2[1] << 0) + + ((u64) in[7]) * (in2[0] << 0); + /* tmp[8] has the greatest value but doesn't overflow. See logic in + * felem_square. */ + tmp[8] = ((u64) in[0]) * (in2[8] << 0) + ((u64) in[1]) * + (in2[7] << 1) + ((u64) in[2]) * (in2[6] << 0) + + ((u64) in[3]) * (in2[5] << 1) + ((u64) in[4]) * + (in2[4] << 0) + ((u64) in[5]) * (in2[3] << 1) + + ((u64) in[6]) * (in2[2] << 0) + ((u64) in[7]) * + (in2[1] << 1) + ((u64) in[8]) * (in2[0] << 0); + tmp[9] = ((u64) in[1]) * (in2[8] << 0) + ((u64) in[2]) * + (in2[7] << 0) + ((u64) in[3]) * (in2[6] << 0) + + ((u64) in[4]) * (in2[5] << 0) + ((u64) in[5]) * + (in2[4] << 0) + ((u64) in[6]) * (in2[3] << 0) + + ((u64) in[7]) * (in2[2] << 0) + + ((u64) in[8]) * (in2[1] << 0); + tmp[10] = ((u64) in[2]) * (in2[8] << 0) + ((u64) in[3]) * + (in2[7] << 1) + ((u64) in[4]) * (in2[6] << 0) + + ((u64) in[5]) * (in2[5] << 1) + ((u64) in[6]) * + (in2[4] << 0) + ((u64) in[7]) * (in2[3] << 1) + + ((u64) in[8]) * (in2[2] << 0); + tmp[11] = ((u64) in[3]) * (in2[8] << 0) + ((u64) in[4]) * + (in2[7] << 0) + ((u64) in[5]) * (in2[6] << 0) + + ((u64) in[6]) * (in2[5] << 0) + ((u64) in[7]) * + (in2[4] << 0) + ((u64) in[8]) * (in2[3] << 0); + tmp[12] = ((u64) in[4]) * (in2[8] << 0) + ((u64) in[5]) * + (in2[7] << 1) + ((u64) in[6]) * (in2[6] << 0) + + ((u64) in[7]) * (in2[5] << 1) + + ((u64) in[8]) * (in2[4] << 0); + tmp[13] = ((u64) in[5]) * (in2[8] << 0) + ((u64) in[6]) * + (in2[7] << 0) + ((u64) in[7]) * (in2[6] << 0) + + ((u64) in[8]) * (in2[5] << 0); + tmp[14] = ((u64) in[6]) * (in2[8] << 0) + ((u64) in[7]) * + (in2[7] << 1) + ((u64) in[8]) * (in2[6] << 0); + tmp[15] = ((u64) in[7]) * (in2[8] << 0) + + ((u64) in[8]) * (in2[7] << 0); + tmp[16] = ((u64) in[8]) * (in2[8] << 0); + + felem_reduce_degree(out, tmp); +} + +static void felem_assign(felem out, const felem in) { + memcpy(out, in, sizeof(felem)); +} + +/* felem_inv calculates |out| = |in|^{-1} + * + * Based on Fermat's Little Theorem: + * a^p = a (mod p) + * a^{p-1} = 1 (mod p) + * a^{p-2} = a^{-1} (mod p) + */ +static void felem_inv(felem out, const felem in) { + felem ftmp, ftmp2; + /* each e_I will hold |in|^{2^I - 1} */ + felem e2, e4, e8, e16, e32, e64; + unsigned i; + + felem_square(ftmp, in); /* 2^1 */ + felem_mul(ftmp, in, ftmp); /* 2^2 - 2^0 */ + felem_assign(e2, ftmp); + felem_square(ftmp, ftmp); /* 2^3 - 2^1 */ + felem_square(ftmp, ftmp); /* 2^4 - 2^2 */ + felem_mul(ftmp, ftmp, e2); /* 2^4 - 2^0 */ + felem_assign(e4, ftmp); + felem_square(ftmp, ftmp); /* 2^5 - 2^1 */ + felem_square(ftmp, ftmp); /* 2^6 - 2^2 */ + felem_square(ftmp, ftmp); /* 2^7 - 2^3 */ + felem_square(ftmp, ftmp); /* 2^8 - 2^4 */ + felem_mul(ftmp, ftmp, e4); /* 2^8 - 2^0 */ + felem_assign(e8, ftmp); + for (i = 0; i < 8; i++) { + felem_square(ftmp, ftmp); + } /* 2^16 - 2^8 */ + felem_mul(ftmp, ftmp, e8); /* 2^16 - 2^0 */ + felem_assign(e16, ftmp); + for (i = 0; i < 16; i++) { + felem_square(ftmp, ftmp); + } /* 2^32 - 2^16 */ + felem_mul(ftmp, ftmp, e16); /* 2^32 - 2^0 */ + felem_assign(e32, ftmp); + for (i = 0; i < 32; i++) { + felem_square(ftmp, ftmp); + } /* 2^64 - 2^32 */ + felem_assign(e64, ftmp); + felem_mul(ftmp, ftmp, in); /* 2^64 - 2^32 + 2^0 */ + for (i = 0; i < 192; i++) { + felem_square(ftmp, ftmp); + } /* 2^256 - 2^224 + 2^192 */ + + felem_mul(ftmp2, e64, e32); /* 2^64 - 2^0 */ + for (i = 0; i < 16; i++) { + felem_square(ftmp2, ftmp2); + } /* 2^80 - 2^16 */ + felem_mul(ftmp2, ftmp2, e16); /* 2^80 - 2^0 */ + for (i = 0; i < 8; i++) { + felem_square(ftmp2, ftmp2); + } /* 2^88 - 2^8 */ + felem_mul(ftmp2, ftmp2, e8); /* 2^88 - 2^0 */ + for (i = 0; i < 4; i++) { + felem_square(ftmp2, ftmp2); + } /* 2^92 - 2^4 */ + felem_mul(ftmp2, ftmp2, e4); /* 2^92 - 2^0 */ + felem_square(ftmp2, ftmp2); /* 2^93 - 2^1 */ + felem_square(ftmp2, ftmp2); /* 2^94 - 2^2 */ + felem_mul(ftmp2, ftmp2, e2); /* 2^94 - 2^0 */ + felem_square(ftmp2, ftmp2); /* 2^95 - 2^1 */ + felem_square(ftmp2, ftmp2); /* 2^96 - 2^2 */ + felem_mul(ftmp2, ftmp2, in); /* 2^96 - 3 */ + + felem_mul(out, ftmp2, ftmp); /* 2^256 - 2^224 + 2^192 + 2^96 - 3 */ +} + +/* felem_scalar_3 sets out=3*out. + * + * On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */ +static void felem_scalar_3(felem out) { + limb carry = 0; + unsigned i; + + for (i = 0;; i++) { + out[i] *= 3; + out[i] += carry; + carry = out[i] >> 29; + out[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + out[i] *= 3; + out[i] += carry; + carry = out[i] >> 28; + out[i] &= kBottom28Bits; + } + + felem_reduce_carry(out, carry); +} + +/* felem_scalar_4 sets out=4*out. + * + * On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */ +static void felem_scalar_4(felem out) { + limb carry = 0, next_carry; + unsigned i; + + for (i = 0;; i++) { + next_carry = out[i] >> 27; + out[i] <<= 2; + out[i] &= kBottom29Bits; + out[i] += carry; + carry = next_carry + (out[i] >> 29); + out[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + next_carry = out[i] >> 26; + out[i] <<= 2; + out[i] &= kBottom28Bits; + out[i] += carry; + carry = next_carry + (out[i] >> 28); + out[i] &= kBottom28Bits; + } + + felem_reduce_carry(out, carry); +} + +/* felem_scalar_8 sets out=8*out. + * + * On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */ +static void felem_scalar_8(felem out) { + limb carry = 0, next_carry; + unsigned i; + + for (i = 0;; i++) { + next_carry = out[i] >> 26; + out[i] <<= 3; + out[i] &= kBottom29Bits; + out[i] += carry; + carry = next_carry + (out[i] >> 29); + out[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + next_carry = out[i] >> 25; + out[i] <<= 3; + out[i] &= kBottom28Bits; + out[i] += carry; + carry = next_carry + (out[i] >> 28); + out[i] &= kBottom28Bits; + } + + felem_reduce_carry(out, carry); +} + +/* felem_is_zero_vartime returns 1 iff |in| == 0. It takes a variable amount of + * time depending on the value of |in|. */ +static char felem_is_zero_vartime(const felem in) { + limb carry; + int i; + limb tmp[NLIMBS]; + + felem_assign(tmp, in); + + /* First, reduce tmp to a minimal form. */ + do { + carry = 0; + for (i = 0;; i++) { + tmp[i] += carry; + carry = tmp[i] >> 29; + tmp[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + tmp[i] += carry; + carry = tmp[i] >> 28; + tmp[i] &= kBottom28Bits; + } + + felem_reduce_carry(tmp, carry); + } while (carry); + + /* tmp < 2**257, so the only possible zero values are 0, p and 2p. */ + return memcmp(tmp, kZero, sizeof(tmp)) == 0 || + memcmp(tmp, kP, sizeof(tmp)) == 0 || + memcmp(tmp, k2P, sizeof(tmp)) == 0; +} + + +/* Group operations: + * + * Elements of the elliptic curve group are represented in Jacobian + * coordinates: (x, y, z). An affine point (x', y') is x'=x/z**2, y'=y/z**3 in + * Jacobian form. */ + +/* point_double sets {x_out,y_out,z_out} = 2*{x,y,z}. + * + * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l */ +static void point_double(felem x_out, felem y_out, felem z_out, const felem x, + const felem y, const felem z) { + felem delta, gamma, alpha, beta, tmp, tmp2; + + felem_square(delta, z); + felem_square(gamma, y); + felem_mul(beta, x, gamma); + + felem_sum(tmp, x, delta); + felem_diff(tmp2, x, delta); + felem_mul(alpha, tmp, tmp2); + felem_scalar_3(alpha); + + felem_sum(tmp, y, z); + felem_square(tmp, tmp); + felem_diff(tmp, tmp, gamma); + felem_diff(z_out, tmp, delta); + + felem_scalar_4(beta); + felem_square(x_out, alpha); + felem_diff(x_out, x_out, beta); + felem_diff(x_out, x_out, beta); + + felem_diff(tmp, beta, x_out); + felem_mul(tmp, alpha, tmp); + felem_square(tmp2, gamma); + felem_scalar_8(tmp2); + felem_diff(y_out, tmp, tmp2); +} + +/* point_add_mixed sets {x_out,y_out,z_out} = {x1,y1,z1} + {x2,y2,1}. + * (i.e. the second point is affine.) + * + * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + * + * Note that this function does not handle P+P, infinity+P nor P+infinity + * correctly. */ +static void point_add_mixed(felem x_out, felem y_out, felem z_out, + const felem x1, const felem y1, const felem z1, + const felem x2, const felem y2) { + felem z1z1, z1z1z1, s2, u2, h, i, j, r, rr, v, tmp; + + felem_square(z1z1, z1); + felem_sum(tmp, z1, z1); + + felem_mul(u2, x2, z1z1); + felem_mul(z1z1z1, z1, z1z1); + felem_mul(s2, y2, z1z1z1); + felem_diff(h, u2, x1); + felem_sum(i, h, h); + felem_square(i, i); + felem_mul(j, h, i); + felem_diff(r, s2, y1); + felem_sum(r, r, r); + felem_mul(v, x1, i); + + felem_mul(z_out, tmp, h); + felem_square(rr, r); + felem_diff(x_out, rr, j); + felem_diff(x_out, x_out, v); + felem_diff(x_out, x_out, v); + + felem_diff(tmp, v, x_out); + felem_mul(y_out, tmp, r); + felem_mul(tmp, y1, j); + felem_diff(y_out, y_out, tmp); + felem_diff(y_out, y_out, tmp); +} + +/* point_add sets {x_out,y_out,z_out} = {x1,y1,z1} + {x2,y2,z2}. + * + * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + * + * Note that this function does not handle P+P, infinity+P nor P+infinity + * correctly. */ +static void point_add(felem x_out, felem y_out, felem z_out, const felem x1, + const felem y1, const felem z1, const felem x2, + const felem y2, const felem z2) { + felem z1z1, z1z1z1, z2z2, z2z2z2, s1, s2, u1, u2, h, i, j, r, rr, v, tmp; + + felem_square(z1z1, z1); + felem_square(z2z2, z2); + felem_mul(u1, x1, z2z2); + + felem_sum(tmp, z1, z2); + felem_square(tmp, tmp); + felem_diff(tmp, tmp, z1z1); + felem_diff(tmp, tmp, z2z2); + + felem_mul(z2z2z2, z2, z2z2); + felem_mul(s1, y1, z2z2z2); + + felem_mul(u2, x2, z1z1); + felem_mul(z1z1z1, z1, z1z1); + felem_mul(s2, y2, z1z1z1); + felem_diff(h, u2, u1); + felem_sum(i, h, h); + felem_square(i, i); + felem_mul(j, h, i); + felem_diff(r, s2, s1); + felem_sum(r, r, r); + felem_mul(v, u1, i); + + felem_mul(z_out, tmp, h); + felem_square(rr, r); + felem_diff(x_out, rr, j); + felem_diff(x_out, x_out, v); + felem_diff(x_out, x_out, v); + + felem_diff(tmp, v, x_out); + felem_mul(y_out, tmp, r); + felem_mul(tmp, s1, j); + felem_diff(y_out, y_out, tmp); + felem_diff(y_out, y_out, tmp); +} + +/* point_add_or_double_vartime sets {x_out,y_out,z_out} = {x1,y1,z1} + + * {x2,y2,z2}. + * + * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + * + * This function handles the case where {x1,y1,z1}={x2,y2,z2}. */ +static void point_add_or_double_vartime( + felem x_out, felem y_out, felem z_out, const felem x1, const felem y1, + const felem z1, const felem x2, const felem y2, const felem z2) { + felem z1z1, z1z1z1, z2z2, z2z2z2, s1, s2, u1, u2, h, i, j, r, rr, v, tmp; + char x_equal, y_equal; + + felem_square(z1z1, z1); + felem_square(z2z2, z2); + felem_mul(u1, x1, z2z2); + + felem_sum(tmp, z1, z2); + felem_square(tmp, tmp); + felem_diff(tmp, tmp, z1z1); + felem_diff(tmp, tmp, z2z2); + + felem_mul(z2z2z2, z2, z2z2); + felem_mul(s1, y1, z2z2z2); + + felem_mul(u2, x2, z1z1); + felem_mul(z1z1z1, z1, z1z1); + felem_mul(s2, y2, z1z1z1); + felem_diff(h, u2, u1); + x_equal = felem_is_zero_vartime(h); + felem_sum(i, h, h); + felem_square(i, i); + felem_mul(j, h, i); + felem_diff(r, s2, s1); + y_equal = felem_is_zero_vartime(r); + if (x_equal && y_equal) { + point_double(x_out, y_out, z_out, x1, y1, z1); + return; + } + felem_sum(r, r, r); + felem_mul(v, u1, i); + + felem_mul(z_out, tmp, h); + felem_square(rr, r); + felem_diff(x_out, rr, j); + felem_diff(x_out, x_out, v); + felem_diff(x_out, x_out, v); + + felem_diff(tmp, v, x_out); + felem_mul(y_out, tmp, r); + felem_mul(tmp, s1, j); + felem_diff(y_out, y_out, tmp); + felem_diff(y_out, y_out, tmp); +} + +/* copy_conditional sets out=in if mask = 0xffffffff in constant time. + * + * On entry: mask is either 0 or 0xffffffff. */ +static void copy_conditional(felem out, const felem in, limb mask) { + int i; + + for (i = 0; i < NLIMBS; i++) { + const limb tmp = mask & (in[i] ^ out[i]); + out[i] ^= tmp; + } +} + +/* select_affine_point sets {out_x,out_y} to the index'th entry of table. + * On entry: index < 16, table[0] must be zero. */ +static void select_affine_point(felem out_x, felem out_y, const limb* table, + limb index) { + limb i, j; + + memset(out_x, 0, sizeof(felem)); + memset(out_y, 0, sizeof(felem)); + + for (i = 1; i < 16; i++) { + limb mask = i ^ index; + mask |= mask >> 2; + mask |= mask >> 1; + mask &= 1; + mask--; + for (j = 0; j < NLIMBS; j++, table++) { + out_x[j] |= *table & mask; + } + for (j = 0; j < NLIMBS; j++, table++) { + out_y[j] |= *table & mask; + } + } +} + +/* select_jacobian_point sets {out_x,out_y,out_z} to the index'th entry of + * table. On entry: index < 16, table[0] must be zero. */ +static void select_jacobian_point(felem out_x, felem out_y, felem out_z, + const limb* table, limb index) { + limb i, j; + + memset(out_x, 0, sizeof(felem)); + memset(out_y, 0, sizeof(felem)); + memset(out_z, 0, sizeof(felem)); + + /* The implicit value at index 0 is all zero. We don't need to perform that + * iteration of the loop because we already set out_* to zero. */ + table += 3 * NLIMBS; + + // Hit all entries to obscure cache profiling. + for (i = 1; i < 16; i++) { + limb mask = i ^ index; + mask |= mask >> 2; + mask |= mask >> 1; + mask &= 1; + mask--; + for (j = 0; j < NLIMBS; j++, table++) { + out_x[j] |= *table & mask; + } + for (j = 0; j < NLIMBS; j++, table++) { + out_y[j] |= *table & mask; + } + for (j = 0; j < NLIMBS; j++, table++) { + out_z[j] |= *table & mask; + } + } +} + +/* scalar_base_mult sets {nx,ny,nz} = scalar*G where scalar is a little-endian + * number. Note that the value of scalar must be less than the order of the + * group. */ +static void scalar_base_mult(felem nx, felem ny, felem nz, + const p256_int* scalar) { + int i, j; + limb n_is_infinity_mask = -1, p_is_noninfinite_mask, mask; + u32 table_offset; + + felem px, py; + felem tx, ty, tz; + + memset(nx, 0, sizeof(felem)); + memset(ny, 0, sizeof(felem)); + memset(nz, 0, sizeof(felem)); + + /* The loop adds bits at positions 0, 64, 128 and 192, followed by + * positions 32,96,160 and 224 and does this 32 times. */ + for (i = 0; i < 32; i++) { + if (i) { + point_double(nx, ny, nz, nx, ny, nz); + } + table_offset = 0; + for (j = 0; j <= 32; j += 32) { + char bit0 = p256_get_bit(scalar, 31 - i + j); + char bit1 = p256_get_bit(scalar, 95 - i + j); + char bit2 = p256_get_bit(scalar, 159 - i + j); + char bit3 = p256_get_bit(scalar, 223 - i + j); + limb index = bit0 | (bit1 << 1) | (bit2 << 2) | (bit3 << 3); + + select_affine_point(px, py, kPrecomputed + table_offset, index); + table_offset += 30 * NLIMBS; + + /* Since scalar is less than the order of the group, we know that + * {nx,ny,nz} != {px,py,1}, unless both are zero, which we handle + * below. */ + point_add_mixed(tx, ty, tz, nx, ny, nz, px, py); + /* The result of point_add_mixed is incorrect if {nx,ny,nz} is zero + * (a.k.a. the point at infinity). We handle that situation by + * copying the point from the table. */ + copy_conditional(nx, px, n_is_infinity_mask); + copy_conditional(ny, py, n_is_infinity_mask); + copy_conditional(nz, kOne, n_is_infinity_mask); + + /* Equally, the result is also wrong if the point from the table is + * zero, which happens when the index is zero. We handle that by + * only copying from {tx,ty,tz} to {nx,ny,nz} if index != 0. */ + p_is_noninfinite_mask = NON_ZERO_TO_ALL_ONES(index); + mask = p_is_noninfinite_mask & ~n_is_infinity_mask; + copy_conditional(nx, tx, mask); + copy_conditional(ny, ty, mask); + copy_conditional(nz, tz, mask); + /* If p was not zero, then n is now non-zero. */ + n_is_infinity_mask &= ~p_is_noninfinite_mask; + } + } +} + +/* point_to_affine converts a Jacobian point to an affine point. If the input + * is the point at infinity then it returns (0, 0) in constant time. */ +static void point_to_affine(felem x_out, felem y_out, const felem nx, + const felem ny, const felem nz) { + felem z_inv, z_inv_sq; + felem_inv(z_inv, nz); + felem_square(z_inv_sq, z_inv); + felem_mul(x_out, nx, z_inv_sq); + felem_mul(z_inv, z_inv, z_inv_sq); + felem_mul(y_out, ny, z_inv); +} + +/* scalar_base_mult sets {nx,ny,nz} = scalar*{x,y}. */ +static void scalar_mult(felem nx, felem ny, felem nz, const felem x, + const felem y, const p256_int* scalar) { + int i; + felem px, py, pz, tx, ty, tz; + felem precomp[16][3]; + limb n_is_infinity_mask, index, p_is_noninfinite_mask, mask; + + /* We precompute 0,1,2,... times {x,y}. */ + memset(precomp, 0, sizeof(felem) * 3); + memcpy(&precomp[1][0], x, sizeof(felem)); + memcpy(&precomp[1][1], y, sizeof(felem)); + memcpy(&precomp[1][2], kOne, sizeof(felem)); + + for (i = 2; i < 16; i += 2) { + point_double(precomp[i][0], precomp[i][1], precomp[i][2], + precomp[i / 2][0], precomp[i / 2][1], precomp[i / 2][2]); + + point_add_mixed(precomp[i + 1][0], precomp[i + 1][1], precomp[i + 1][2], + precomp[i][0], precomp[i][1], precomp[i][2], x, y); + } + + memset(nx, 0, sizeof(felem)); + memset(ny, 0, sizeof(felem)); + memset(nz, 0, sizeof(felem)); + n_is_infinity_mask = -1; + + /* We add in a window of four bits each iteration and do this 64 times. */ + for (i = 0; i < 256; i += 4) { + if (i) { + point_double(nx, ny, nz, nx, ny, nz); + point_double(nx, ny, nz, nx, ny, nz); + point_double(nx, ny, nz, nx, ny, nz); + point_double(nx, ny, nz, nx, ny, nz); + } + + index = (p256_get_bit(scalar, 255 - i - 0) << 3) | + (p256_get_bit(scalar, 255 - i - 1) << 2) | + (p256_get_bit(scalar, 255 - i - 2) << 1) | + p256_get_bit(scalar, 255 - i - 3); + + /* See the comments in scalar_base_mult about handling infinities. */ + select_jacobian_point(px, py, pz, precomp[0][0], index); + point_add(tx, ty, tz, nx, ny, nz, px, py, pz); + copy_conditional(nx, px, n_is_infinity_mask); + copy_conditional(ny, py, n_is_infinity_mask); + copy_conditional(nz, pz, n_is_infinity_mask); + + p_is_noninfinite_mask = NON_ZERO_TO_ALL_ONES(index); + mask = p_is_noninfinite_mask & ~n_is_infinity_mask; + + copy_conditional(nx, tx, mask); + copy_conditional(ny, ty, mask); + copy_conditional(nz, tz, mask); + n_is_infinity_mask &= ~p_is_noninfinite_mask; + } +} + +#define kRDigits {2, 0, 0, 0xfffffffe, 0xffffffff, 0xffffffff, 0xfffffffd, 1} // 2^257 mod p256.p + +#define kRInvDigits {0x80000000, 1, 0xffffffff, 0, 0x80000001, 0xfffffffe, 1, 0x7fffffff} // 1 / 2^257 mod p256.p + +static const p256_int kR = { kRDigits }; +static const p256_int kRInv = { kRInvDigits }; + +/* to_montgomery sets out = R*in. */ +static void to_montgomery(felem out, const p256_int* in) { + p256_int in_shifted; + int i; + + p256_init(&in_shifted); + p256_modmul(&SECP256r1_p, in, 0, &kR, &in_shifted); + + for (i = 0; i < NLIMBS; i++) { + if ((i & 1) == 0) { + out[i] = P256_DIGIT(&in_shifted, 0) & kBottom29Bits; + p256_shr(&in_shifted, 29, &in_shifted); + } else { + out[i] = P256_DIGIT(&in_shifted, 0) & kBottom28Bits; + p256_shr(&in_shifted, 28, &in_shifted); + } + } + + p256_clear(&in_shifted); +} + +/* from_montgomery sets out=in/R. */ +static void from_montgomery(p256_int* out, const felem in) { + p256_int result, tmp; + int i, top; + + p256_init(&result); + p256_init(&tmp); + + p256_add_d(&tmp, in[NLIMBS - 1], &result); + for (i = NLIMBS - 2; i >= 0; i--) { + if ((i & 1) == 0) { + top = p256_shl(&result, 29, &tmp); + } else { + top = p256_shl(&result, 28, &tmp); + } + top |= p256_add_d(&tmp, in[i], &result); + } + + p256_modmul(&SECP256r1_p, &kRInv, top, &result, out); + + p256_clear(&result); + p256_clear(&tmp); +} + +/* p256_base_point_mul sets {out_x,out_y} = nG, where n is < the + * order of the group. */ +void p256_base_point_mul(const p256_int* n, p256_int* out_x, p256_int* out_y) { + felem x, y, z; + + scalar_base_mult(x, y, z, n); + + { + felem x_affine, y_affine; + + point_to_affine(x_affine, y_affine, x, y, z); + from_montgomery(out_x, x_affine); + from_montgomery(out_y, y_affine); + } +} + +/* p256_points_mul_vartime sets {out_x,out_y} = n1*G + n2*{in_x,in_y}, where + * n1 and n2 are < the order of the group. + * + * As indicated by the name, this function operates in variable time. This + * is safe because it's used for signature validation which doesn't deal + * with secrets. */ +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) { + felem x1, y1, z1, x2, y2, z2, px, py; + + /* If both scalars are zero, then the result is the point at infinity. */ + if (p256_is_zero(n1) != 0 && p256_is_zero(n2) != 0) { + p256_clear(out_x); + p256_clear(out_y); + return; + } + + to_montgomery(px, in_x); + to_montgomery(py, in_y); + scalar_base_mult(x1, y1, z1, n1); + scalar_mult(x2, y2, z2, px, py, n2); + + if (p256_is_zero(n2) != 0) { + /* If n2 == 0, then {x2,y2,z2} is zero and the result is just + * {x1,y1,z1}. */ + } else if (p256_is_zero(n1) != 0) { + /* If n1 == 0, then {x1,y1,z1} is zero and the result is just + * {x2,y2,z2}. */ + memcpy(x1, x2, sizeof(x2)); + memcpy(y1, y2, sizeof(y2)); + memcpy(z1, z2, sizeof(z2)); + } else { + /* This function handles the case where {x1,y1,z1} == {x2,y2,z2}. */ + point_add_or_double_vartime(x1, y1, z1, x1, y1, z1, x2, y2, z2); + } + + point_to_affine(px, py, x1, y1, z1); + from_montgomery(out_x, px); + from_montgomery(out_y, py); +} diff --git a/tests/fido_tests/u2f-tests-hid/p256_ecdsa.c b/tests/fido_tests/u2f-tests-hid/p256_ecdsa.c new file mode 100644 index 0000000000..a9883d952a --- /dev/null +++ b/tests/fido_tests/u2f-tests-hid/p256_ecdsa.c @@ -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 + +#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; +} + diff --git a/tests/fido_tests/u2f-tests-hid/p256_ecdsa.h b/tests/fido_tests/u2f-tests-hid/p256_ecdsa.h new file mode 100644 index 0000000000..da339fa38f --- /dev/null +++ b/tests/fido_tests/u2f-tests-hid/p256_ecdsa.h @@ -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_ diff --git a/tests/fido_tests/u2f-tests-hid/sha256.c b/tests/fido_tests/u2f-tests-hid/sha256.c new file mode 100644 index 0000000000..883d7e6773 --- /dev/null +++ b/tests/fido_tests/u2f-tests-hid/sha256.c @@ -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 +#include +#include + +#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; +} diff --git a/tests/fido_tests/u2f-tests-hid/sha256.h b/tests/fido_tests/u2f-tests-hid/sha256.h new file mode 100644 index 0000000000..3a87c31787 --- /dev/null +++ b/tests/fido_tests/u2f-tests-hid/sha256.h @@ -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 +#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_ diff --git a/tests/fido_tests/u2f-tests-hid/u2f.h b/tests/fido_tests/u2f-tests-hid/u2f.h new file mode 100644 index 0000000000..741c752234 --- /dev/null +++ b/tests/fido_tests/u2f-tests-hid/u2f.h @@ -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 + +#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__ diff --git a/tests/fido_tests/u2f-tests-hid/u2f_hid.h b/tests/fido_tests/u2f-tests-hid/u2f_hid.h new file mode 100644 index 0000000000..1e79041ef5 --- /dev/null +++ b/tests/fido_tests/u2f-tests-hid/u2f_hid.h @@ -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 + +#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__ diff --git a/tests/fido_tests/u2f-tests-hid/u2f_util.cc b/tests/fido_tests/u2f-tests-hid/u2f_util.cc new file mode 100644 index 0000000000..85deb815ca --- /dev/null +++ b/tests/fido_tests/u2f-tests-hid/u2f_util.cc @@ -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 +#include + +#ifdef __OS_WIN +#include // ntohl, htonl +#else +#include // ntohl, htonl +#endif + +#include + +#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 + +#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(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(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(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(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 +} diff --git a/tests/fido_tests/u2f-tests-hid/u2f_util.h b/tests/fido_tests/u2f-tests-hid/u2f_util.h new file mode 100644 index 0000000000..00dcc1f1f7 --- /dev/null +++ b/tests/fido_tests/u2f-tests-hid/u2f_util.h @@ -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 +#include +#include +#include + +#include +#include + +#include "u2f.h" +#include "u2f_hid.h" + +#include "hidapi.h" + +#ifdef _MSC_VER +#include +#define usleep(x) Sleep((x + 999) / 1000) +#else +#include +#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__