From 0401e7081c99c3745c91f8d89fd7c87ee81cbe15 Mon Sep 17 00:00:00 2001 From: dorababu <dorababu@subcom.tech> Date: Fri, 2 Sep 2022 10:36:03 +0530 Subject: [PATCH] Init - Using existing teensy key firmware - to build for esp32 --- platformio.ini | 15 + src/main.cpp | 1678 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1693 insertions(+) create mode 100644 platformio.ini create mode 100644 src/main.cpp diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..bc8f138 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,15 @@ +[env:esp32_u2f] + +platform = espressif32 +framework = arduino +board = esp32dev +;upload_protocol = teensy-cli +build_flags = +; -D USB_RAWHID + +lib_deps = + Wire + mbed-team2/Arduino + dfrobot/DFRobot_DHT20@^1.0.0 + https://github.com/OperatorFoundation/Crypto.git + diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..f1796e8 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,1678 @@ +/* + * U2F key using TeensyLC board + * The reference for this project + * + * CREDIT: + * ------- + * https://github.com/yohanes/teensy-u2f + * + * MODIFICATIONS: + * -------------- + * We have added fingerprint scanner inaddition. + */ + +// Include Arduino.h if you are building with platformIO +#include "sha256.h" +#include "uECC.h" +#include <Arduino.h> +#include <EEPROM.h> +#include <string.h> +// for AES encryption +#include <AES.h> +#include <Crypto.h> + +// Library for fingerprint scanner +#include "DFRobot_ID809.h" + +// FingerPrint scanner with serial3 communication +#define FPSerial Serial1 +#define SHA256_SIZE 32 + +#define CID_BROADCAST 0xffffffff // Broadcast channel id +#define CID_VENDOR 0x80ffffff // cid received from vendor +#define TYPE_MASK 0x80 // Frame type mask +#define TYPE_INIT 0x80 // Initial frame identifier +#define TYPE_CONT 0x00 // Continuation frame identifier + +#define U2FHID_PING (TYPE_INIT | 0x01) // Echo data through local processor only +#define U2FHID_MSG (TYPE_INIT | 0x03) // Send U2F message frame +#define U2FHID_LOCK (TYPE_INIT | 0x04) // Send lock channel command +#define U2FHID_INIT (TYPE_INIT | 0x06) // Channel initialization +#define U2FHID_WINK (TYPE_INIT | 0x08) // Send device identification wink +#define U2FHID_ERROR (TYPE_INIT | 0x3f) // Error response + +#define IS_CONTINUATION_PACKET(x) ((x) < 0x80) +#define IS_NOT_CONTINUATION_PACKET(x) ((x) >= 0x80) + +#define SW_NO_ERROR 0x9000 +#define SW_CONDITIONS_NOT_SATISFIED 0x6985 +#define SW_WRONG_DATA 0x6A80 +#define SW_WRONG_LENGTH 0x6700 +#define SW_INS_NOT_SUPPORTED 0x6D00 +#define SW_CLA_NOT_SUPPORTED 0x6E00 + +#define APPEND_SW(x, v1, v2) \ + do { \ + (*x++) = v1; \ + (*x++) = v2; \ + } while (0) +#define APPEND_SW_NO_ERROR(x) \ + do { \ + (*x++) = 0x90; \ + (*x++) = 0x00; \ + } while (0) + +// 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 + +#define U2F_INS_REGISTER 0x01 +#define U2F_INS_AUTHENTICATE 0x02 +#define U2F_INS_VERSION 0x03 + +#define U2F_INS_FPHASH 0x60 +#define U2F_INS_HASHMATCH 0x61 +#define U2F_INS_PING 0x62 +#define U2F_INS_COUNT 0x63 +#define U2F_INS_KEYCLOAK_EN 0x64 +#define U2F_INS_KEYCLOAK_DIS 0x65 + +#define U2F_INS_CLR_FPS 0x50 +#define U2F_INS_REG_FPS 0x51 +#define U2F_INS_DEL_FPS 0x52 + +#define STATE_CHANNEL_AVAILABLE 0 +#define STATE_CHANNEL_WAIT_PACKET 1 +#define STATE_CHANNEL_WAIT_CONT 2 +#define STATE_CHANNEL_TIMEOUT 3 +#define STATE_LARGE_PACKET 4 + +#define MAX_TOTAL_PACKET 7609 +#define TIMEOUT_VALUE 1000 + +#define MAX_INITIAL_PACKET 57 +#define MAX_CONTINUATION_PACKET 59 +#define SET_MSG_LEN(b, v) \ + do { \ + (b)[5] = ((v) >> 8) & 0xff; \ + (b)[6] = (v)&0xff; \ + } while (0) + +#define U2FHID_IF_VERSION 2 // Current interface implementation version +#define COLLECT_NUMBER 3 + +#define DEBUG + +byte expected_next_packet; +int large_data_len; +int large_data_offset; +byte large_buffer[1024]; +byte large_resp_buffer[1024]; +byte recv_buffer[64]; +byte resp_buffer[64]; +byte handle[64]; +byte sha256_hash[32]; + +byte fp_hash[32]; + +// AES encryption parameters +AES128 aes128; +byte key[16] = { 0x73, 0x75, 0x62, 0x63, 0x6f, 0x6d, 0x6b, 0x65, 0x79, 0x23, + 0x73, 0x63, 0x31, 0x30, 0x30, 0x33 }; +String padding_character = "/"; +byte buffer[65]; +byte cipher_buffer[65]; +byte buffer_tmp[17]; + +byte hash[SHA256_SIZE]; +uint8_t temp[1008]; +uint8_t ID = 0, ret = 0; + +int LAST_MATCHED_ID = -1; + +#define MAX_CHANNEL 4 + +void authenticateKey(); +void encrypt_handle(); +void decrypt_handle(); +void saveHash(); +void readHash(); +void compute_hash(); +// EEPROM OFFSET to store hash +const uint16_t EEPROM_FP_HASH = 1696; +const uint16_t EEPROM_KEYCLOAK_EN = 1694; + +const uint8_t KEYCLOAK_EN = 0XAA; +const uint8_t KEYCLOAK_DIS = 0X00; + +// User presence check for U2f key + +bool button_pressed = false; +bool hash_matched = false; +// Privatekey +const char attestation_key[] = "\x07\x50\x1f\xa8\x85\x01\x8d\xa8\x86\xf7\x54" + "\x8e\x13\x3d\x69\x92\x7b\x1d\x6d\xb6\x8e\x04" + "\x58\x3e\x89\x91\xd8\x18\x8f\x9f\x4f\x3d"; + +const char attestation_der[] + = "\x30\x82\x02\xa3\x30\x82\x02\x49\xa0\x03\x02\x01" + "\x02\x02\x14\x4c\xcd\x5a\xfb\x82\xe1\x1f\x1d\x33" + "\x03\x2a\x57\xfe\xf1\x7e\x23\xf6\xf3\x1b\x9a\x30" + "\x0a\x06\x08\x2a\x86\x48\xce\x3d\x04\x03\x02\x30" + "\x81\xa6\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13" + "\x02\x49\x4e\x31\x12\x30\x10\x06\x03\x55\x04\x08" + "\x0c\x09\x4b\x61\x72\x6e\x61\x74\x61\x6b\x61\x31" + "\x12\x30\x10\x06\x03\x55\x04\x07\x0c\x09\x42\x61" + "\x6e\x67\x61\x6c\x6f\x72\x65\x31\x0f\x30\x0d\x06" + "\x03\x55\x04\x0a\x0c\x06\x53\x75\x62\x63\x6f\x6d" + "\x31\x0c\x30\x0a\x06\x03\x55\x04\x0b\x0c\x03\x49" + "\x6f\x54\x31\x2b\x30\x29\x06\x03\x55\x04\x03\x0c" + "\x22\x44\x41\x34\x31\x30\x30\x31\x20\x34\x42\x30" + "\x39\x30\x30\x33\x32\x20\x30\x30\x30\x46\x39\x30" + "\x31\x33\x20\x36\x38\x32\x37\x34\x45\x34\x35\x31" + "\x23\x30\x21\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01" + "\x09\x01\x16\x14\x6d\x72\x64\x6f\x72\x61\x62\x61" + "\x62\x75\x40\x67\x6d\x61\x69\x6c\x2e\x63\x6f\x6d" + "\x30\x1e\x17\x0d\x32\x32\x30\x36\x32\x31\x30\x39" + "\x35\x30\x30\x34\x5a\x17\x0d\x33\x32\x30\x36\x31" + "\x38\x30\x39\x35\x30\x30\x34\x5a\x30\x81\xa6\x31" + "\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x49\x4e" + "\x31\x12\x30\x10\x06\x03\x55\x04\x08\x0c\x09\x4b" + "\x61\x72\x6e\x61\x74\x61\x6b\x61\x31\x12\x30\x10" + "\x06\x03\x55\x04\x07\x0c\x09\x42\x61\x6e\x67\x61" + "\x6c\x6f\x72\x65\x31\x0f\x30\x0d\x06\x03\x55\x04" + "\x0a\x0c\x06\x53\x75\x62\x63\x6f\x6d\x31\x0c\x30" + "\x0a\x06\x03\x55\x04\x0b\x0c\x03\x49\x6f\x54\x31" + "\x2b\x30\x29\x06\x03\x55\x04\x03\x0c\x22\x44\x41" + "\x34\x31\x30\x30\x31\x20\x34\x42\x30\x39\x30\x30" + "\x33\x32\x20\x30\x30\x30\x46\x39\x30\x31\x33\x20" + "\x36\x38\x32\x37\x34\x45\x34\x35\x31\x23\x30\x21" + "\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01\x16" + "\x14\x6d\x72\x64\x6f\x72\x61\x62\x61\x62\x75\x40" + "\x67\x6d\x61\x69\x6c\x2e\x63\x6f\x6d\x30\x59\x30" + "\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08" + "\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04" + "\xab\xa6\x42\xa4\x08\x3c\x41\x84\x9e\xb9\x13\x0a" + "\xa7\xac\xc7\xdc\x6f\x9f\x25\x20\x99\x87\x91\xa2" + "\x9f\xf2\xf4\x52\xdd\x5e\xc0\x6f\x18\x8d\x70\xa9" + "\xb5\x37\xd9\x8d\x51\x5b\x99\x7d\xf8\x8a\x89\xdc" + "\xee\xbf\x43\x8d\x86\x02\x76\x85\x4c\x47\x04\x2c" + "\x77\x05\x0f\x91\xa3\x53\x30\x51\x30\x1d\x06\x03" + "\x55\x1d\x0e\x04\x16\x04\x14\xeb\x04\x59\x64\xac" + "\x8f\xec\xe8\x31\x03\x9e\x16\x2f\x90\x29\x59\x56" + "\xea\x40\xcb\x30\x1f\x06\x03\x55\x1d\x23\x04\x18" + "\x30\x16\x80\x14\xeb\x04\x59\x64\xac\x8f\xec\xe8" + "\x31\x03\x9e\x16\x2f\x90\x29\x59\x56\xea\x40\xcb" + "\x30\x0f\x06\x03\x55\x1d\x13\x01\x01\xff\x04\x05" + "\x30\x03\x01\x01\xff\x30\x0a\x06\x08\x2a\x86\x48" + "\xce\x3d\x04\x03\x02\x03\x48\x00\x30\x45\x02\x20" + "\x3a\x7a\xab\x72\x58\xb9\x84\x9d\xe9\x1e\x8f\x9d" + "\x2f\x7b\xd6\xbf\xbb\x55\xb5\xcb\xb6\xd7\xdc\xf6" + "\xf5\x50\xf8\xe2\x02\xfc\xe0\x77\x02\x21\x00\xb3" + "\xc3\x5f\x72\x09\x4a\x52\x1c\xac\x94\x9b\xb8\xa8" + "\x43\xc9\x83\xd9\x89\x08\xa4\x04\x0e\xfd\x6b\x7c" + "\x0f\x92\x06\xd1\x3f\xa9\xa3"; + +// key handle: (private key + app parameter) ^ this array +const char handlekey[] = "-SUBCOMS-DORACOM-COMPUTE-SUBCOMS-"; + +const struct uECC_Curve_t* curve = uECC_secp256r1(); // P-256 +uint8_t private_k[36]; // 32 +uint8_t public_k[68]; // 64 + +struct ch_state { + int cid; + byte state; + int last_millis; +}; + +ch_state channel_states[MAX_CHANNEL]; + +// Encrypt handle using AES-128 encryption +void encrypt_handle() +{ + // Encrypt 16 by 16 + for (uint8_t i = 0; i < sizeof(handle); i = i + 16) { + for (uint8_t j = 0; j < 16; j++) { + buffer_tmp[j] = handle[i + j]; + } + aes128.encryptBlock(buffer_tmp, buffer_tmp); + for (uint8_t j = 0; j < 16; j++) { + cipher_buffer[i + j] = buffer_tmp[j]; + } + } + + // Print Encrypted + Serial.print("(Hex) Cipher : "); + for (uint8_t i = 0; i < sizeof(handle); i++) { + Serial.printf("%02x", cipher_buffer[i]); + handle[i] = cipher_buffer[i]; + } +} + +// Decrypt message using AES-128 encryption. + +void decrypt_handle() +{ + + // Decrypt 16 by 16 + for (uint8_t i = 0; i < sizeof(handle); i = i + 16) { + for (uint8_t j = 0; j < 16; j++) { + buffer_tmp[j] = handle[i + j]; + } + aes128.decryptBlock(buffer_tmp, buffer_tmp); + for (uint8_t j = 0; j < 16; j++) { + buffer[i + j] = buffer_tmp[j]; + } + } + + // Print Decrypted + Serial.print("(Hex) Clear : "); + for (uint8_t i = 0; i < sizeof(handle); i++) { + Serial.printf("%02x", buffer[i]); + handle[i] = buffer[i]; + } +} + +extern "C" { + +int RNG(uint8_t* dest, unsigned size) +{ + // Use the least-significant bits from the ADC for an unconnected pin (or + // connected to a source of random noise). This can take a long time to + // generate random data if the result of analogRead(0) doesn't change very + // frequently. + while (size) { + uint8_t val = 0; + for (unsigned i = 0; i < 8; ++i) { + int init = analogRead(0); + int count = 0; + while (analogRead(0) == init) { + ++count; + } + + if (count == 0) { + val = (val << 1) | (init & 0x01); + } else { + val = (val << 1) | (count & 0x01); + } + } + *dest = val; + ++dest; + --size; + } + // NOTE: it would be a good idea to hash the resulting random data using + // SHA-256 or similar. + return 1; +} + +} // extern "C" + +typedef struct SHA256_HashContext { + uECC_HashContext uECC; + SHA256_CTX ctx; +} SHA256_HashContext; + +void init_SHA256(uECC_HashContext* base) +{ + SHA256_HashContext* context = (SHA256_HashContext*)base; + sha256_init(&context->ctx); +} +void update_SHA256( + uECC_HashContext* base, const uint8_t* message, unsigned message_size) +{ + SHA256_HashContext* context = (SHA256_HashContext*)base; + sha256_update(&context->ctx, message, message_size); +} +void finish_SHA256(uECC_HashContext* base, uint8_t* hash_result) +{ + SHA256_HashContext* context = (SHA256_HashContext*)base; + sha256_final(&context->ctx, hash_result); +} + +DFRobot_ID809 fingerprint; + +void readFingerPrint() +{ + + uint8_t ret = 0; + uint8_t fp_temp[1008]; + uint16_t memoryAddress = 0; + byte stored_hash[SHA256_SIZE]; + + /*Set fingerprint LED ring mode, color, and number of blinks + Can be set as follows: + Parameter 1:<LEDMode> + eBreathing eFastBlink eKeepsOn eNormalClose + eFadeIn eFadeOut eSlowBlink + Parameter 2:<LEDColor> + eLEDGreen eLEDRed eLEDYellow eLEDBlue + eLEDCyan eLEDMagenta eLEDWhite + Parameter 3:<number of blinks> 0 represents blinking all the time + This parameter will only be valid in mode eBreathing, eFastBlink, + eSlowBlink + */ + + fingerprint.ctrlLED(/*LEDMode = */ fingerprint.eBreathing, + /*LEDColor = */ fingerprint.eLEDBlue, + /*blinkCount = */ 0); + // Serial.println("Please press down your finger"); + /*Capture fingerprint image, Disable the collection timeout function + If succeed return 0, otherwise return ERR_ID809 + */ + // authenticateKey(); + if ((fingerprint.collectionFingerprint(/*timeout=*/0)) != ERR_ID809) { + // fingerprint.collectionFingerprint(0); + /*Set fingerprint LED ring to quick blink in yellow 3 times*/ + fingerprint.ctrlLED(/*LEDMode = */ fingerprint.eFastBlink, + /*LEDColor = */ fingerprint.eLEDYellow, + /*blinkCount = */ 3); + // Serial.println("Capturing succeeds"); + // authenticateKey(); + // Serial.println("Please release your finger"); + /*Wait for finger to release + Return 1 when finger is detected, otherwise return 0 + */ + + while (fingerprint.detectFinger()) + ; + // authenticateKey(); + // fingerprint.detectFinger(); + /*Compare the captured fingerprint with all the fingerprints in the + fingerprint library Return fingerprint ID(1-80) if succeed, return + 0 when failed + */ + ret = fingerprint.search(); + /*Compare the captured fingerprint with a fingerprint of specific ID + Return fingerprint ID(1-80) if succeed, return 0 when failed + */ + // authenticateKey(); + // ret = fingerprint.verify(/*Fingerprint ID = */1); + if (ret != 0) { + /*Set fingerprint LED ring to always ON in green */ + fingerprint.ctrlLED(/*LEDMode = */ fingerprint.eKeepsOn, + /*LEDColor = */ fingerprint.eLEDGreen, + /*blinkCount = */ 0); + LAST_MATCHED_ID = ret; +#ifdef DEBUG + Serial.print("Matching succeeds,ID="); + Serial.println(ret); +#endif + // authenticateKey(); + // delay(1000); + // button_pressed = 1; + if (ret > 10) { + +#ifdef DebUG + Serial.println("Outof EEPROM memory"); +#endif + return; + } + fingerprint.getTemplate(ret, fp_temp); + + SHA256_CTX ctx; + sha256_init(&ctx); + sha256_update(&ctx, fp_temp, sizeof(fp_temp)); + sha256_final(&ctx, fp_hash); +#ifdef Debug + Serial.println("-----------------------"); + for (uint8_t i = 0; i < SHA256_SIZE; i++) { + + if (fp_hash[i] < 0x10) { + + Serial.print('0'); + } + + Serial.print(fp_hash[i], HEX); + Serial.print(" "); + } + Serial.println("-----------------------"); +#endif + + memoryAddress = EEPROM_FP_HASH + ((ret - 1) * 32); + +#ifdef DEBUG + Serial.println(ret); + Serial.println("---------------------------"); +#endif + for (uint8_t i = 0; i < SHA256_SIZE; i++) { + + stored_hash[i] = EEPROM.read(memoryAddress); +#ifdef DEBUG + Serial.print(stored_hash[i], HEX); + Serial.print(" "); + // Serial.println(memoryAddress); +#endif + memoryAddress++; + } + Serial.println("--------------------------"); + + for (uint8_t i = 0; i < SHA256_SIZE; i++) { + + if (stored_hash[i] != fp_hash[i]) { +#ifdef DEBUG + Serial.println("Hash not matched"); +#endif + button_pressed = 0; + + return; + } + } + button_pressed = true; +#ifdef DEBUG + Serial.println("Hash matched"); +#endif + + } else { + /*Set fingerprint LED ring to always ON in red*/ + fingerprint.ctrlLED(/*LEDMode = */ fingerprint.eKeepsOn, + /*LEDColor = */ fingerprint.eLEDRed, + /*blinkCount = */ 0); + // Serial.println("Matching fails"); + // delay(1000); + // authenticateKey(); + button_pressed = false; + } + } else { + // Serial.println("Capturing fails"); + /*Get error code information*/ + // desc = fingerprint.getErrorDescription(); + // Serial.println(desc); + // delay(1000); + // authenticateKey(); + button_pressed = false; + } + // //Serial.println("-----------------------------"); + // delay(1000); + // authenticateKey(); +} + +/*---------Compute hash----------------*/ + +void compute_hash() +{ + + SHA256_CTX ctx; + sha256_init(&ctx); + sha256_update(&ctx, temp, sizeof(temp)); + sha256_final(&ctx, hash); + + for (uint8_t i = 0; i < SHA256_SIZE; i++) { + + if (hash[i] < 0x10) { + + Serial.print('0'); + } + + Serial.print(hash[i], HEX); + Serial.print(" "); + } + Serial.println("\n"); + Serial.println("-----------------"); +} + +/*-------- Save FingerPrint hash in EEPROM ------*/ + +void saveHash() +{ + uint16_t memoryAddress; + + memoryAddress = EEPROM_FP_HASH + ((ID - 1) * 32); + + if (memoryAddress > 2016) { + Serial.println("Error: not enough space in" + " EEPROM"); + return; + } + + for (uint8_t i = 0; i < SHA256_SIZE; i++) { + + EEPROM.write(memoryAddress, hash[i]); + memoryAddress++; + } +} + +char U_ID[32]; +uint8_t HASH_DATA[32] = {}; + +/*-------Read stored hash in EEPROM*/ +void readHash() +{ + + uint16_t memoryAddress; + byte eeprom_data; + + memoryAddress = EEPROM_FP_HASH + ((ID - 1) * 32); + + uint8_t idx = 0; + for (uint16_t i = memoryAddress; i < (memoryAddress + SHA256_SIZE); i++) { + + eeprom_data = EEPROM.read(i); + HASH_DATA[idx++] = eeprom_data; + Serial.print(eeprom_data, HEX); + Serial.print(" "); + } + Serial.println("\n"); + Serial.println("--------------------"); +} + +/*-----------Arduino setup function-----------*/ +void setup() +{ + uECC_set_rng(&RNG); + FPSerial.begin(115200); + fingerprint.begin(FPSerial); + Serial.begin(9600); +#ifdef DEBUG +#endif + while (fingerprint.isConnected() == false) { + // Serial.println("Communication with device failed, please check + // connection"); + /*Get error code information*/ + // desc = fingerprint.getErrorDescription(); + // Serial.println(desc); + delay(1000); + } +#ifdef DEBUG + Serial.println(fingerprint.getEnrollCount()); +#endif + + sprintf(U_ID, "%08lX %08lX %08lX %08lX", SIM_UIDH, SIM_UIDMH, SIM_UIDML, + SIM_UIDL); +} + +void cleanup_timeout() +{ + int i; + for (i = 0; i < MAX_CHANNEL; i++) { + // free channel that is inactive + ch_state& c = channel_states[i]; + int m = millis(); + if (c.state != STATE_CHANNEL_AVAILABLE) { + if ((m - c.last_millis) > TIMEOUT_VALUE) { + c.state = STATE_CHANNEL_AVAILABLE; + } + } + } +} + +int allocate_new_channel() +{ + int i; + // alloace new channel_id + int channel_id = 1; + + do { + bool found = false; + for (i = 0; i < MAX_CHANNEL; i++) { + if (channel_states[i].state != STATE_CHANNEL_AVAILABLE) { + if (channel_states[i].cid == channel_id) { + found = true; + channel_id++; + break; + } + } + } + if (!found) + break; + } while (true); + return channel_id; +} + +int allocate_channel(int channel_id) +{ + int i; + if (channel_id == 0) { + channel_id = allocate_new_channel(); + } + + bool has_free_slots = false; + for (i = 0; i < MAX_CHANNEL; i++) { + if (channel_states[i].state == STATE_CHANNEL_AVAILABLE) { + has_free_slots = true; + break; + } + } + if (!has_free_slots) + cleanup_timeout(); + + for (i = 0; i < MAX_CHANNEL; i++) { + ch_state& c = channel_states[i]; + if (c.state == STATE_CHANNEL_AVAILABLE) { + c.cid = channel_id; + c.state = STATE_CHANNEL_WAIT_PACKET; + c.last_millis = millis(); + return channel_id; + } + } + return 0; +} + +int initResponse(byte* buffer) +{ +#ifdef DEBUG + Serial.print("INIT RESPONSE"); +#endif + int cid = *(int*)buffer; +#ifdef DEBUG + Serial.print(cid, HEX); +#endif + int len = buffer[5] << 8 | buffer[6]; + int i; + memcpy(resp_buffer, buffer, 5); + SET_MSG_LEN(resp_buffer, 17); + memcpy(resp_buffer + 7, buffer + 7, len); // nonce + i = 7 + len; + if (cid == -1) { + cid = allocate_channel(0); + } else { +#ifdef DEBUG + Serial.println("using existingg CID"); +#endif + allocate_channel(cid); + } + memcpy(resp_buffer + i, &cid, 4); + i += 4; + resp_buffer[i++] = U2FHID_IF_VERSION; + resp_buffer[i++] = 1; // major + resp_buffer[i++] = 0; + resp_buffer[i++] = 1; // build + // resp_buffer[i++] = CAPABILITY_WINK; //capabilities + resp_buffer[i++] = 0; // capabilities +#ifdef DEBUG + Serial.println("SEND RESPONSE 1"); +#endif + RawHID.send(resp_buffer, 100); +#ifdef DEBUG + Serial.println(cid, HEX); +#endif + return cid; +} + +void errorResponse(byte* buffer, int code) +{ + memcpy(resp_buffer, buffer, 4); + resp_buffer[4] = U2FHID_ERROR; + SET_MSG_LEN(resp_buffer, 1); + resp_buffer[7] = code & 0xff; +#ifdef DEBUG + Serial.print("SENT RESPONSE error"); + Serial.println(code); +#endif + RawHID.send(resp_buffer, 100); +} + +// find channel index and update last access +int find_channel_index(int channel_id) +{ + int i; + + for (i = 0; i < MAX_CHANNEL; i++) { + if (channel_states[i].cid == channel_id) { + channel_states[i].last_millis = millis(); + return i; + } + } + + return -1; +} + +void respondErrorPDU(byte* buffer, int err) +{ + SET_MSG_LEN(buffer, 2); // len("") + 2 byte SW + byte* datapart = buffer + 7; + APPEND_SW(datapart, (err >> 8) & 0xff, err & 0xff); + RawHID.send(buffer, 100); +} + +void sendLargeResponse(byte* request, int len) +{ +#ifdef DEBUG + Serial.print("Sending large response "); + Serial.println(len); + for (int i = 0; i < len; i++) { + Serial.print(large_resp_buffer[i], HEX); + Serial.print(" "); + } + Serial.println("\n--\n"); +#endif + + memcpy(resp_buffer, request, 4); // copy cid + resp_buffer[4] = U2FHID_MSG; + int r = len; + if (r > MAX_INITIAL_PACKET) { + r = MAX_INITIAL_PACKET; + } + + SET_MSG_LEN(resp_buffer, len); + memcpy(resp_buffer + 7, large_resp_buffer, r); + + RawHID.send(resp_buffer, 100); + len -= r; + byte p = 0; + int offset = MAX_INITIAL_PACKET; + while (len > 0) { + // memcpy(resp_buffer, request, 4); //copy cid, doesn't need to recopy + resp_buffer[4] = p++; + memcpy(resp_buffer + 5, large_resp_buffer + offset, + MAX_CONTINUATION_PACKET); + RawHID.send(resp_buffer, 100); + len -= MAX_CONTINUATION_PACKET; + offset += MAX_CONTINUATION_PACKET; + delayMicroseconds(2500); + } +} + +int getCounter() +{ + unsigned int eeAddress = 0; // EEPROM address to start reading from + unsigned int counter; + EEPROM.get(eeAddress, counter); + return counter; +} + +void setCounter(int counter) +{ + unsigned int eeAddress = 0; // EEPROM address to start reading from + EEPROM.put(eeAddress, counter); +} + +void processMessage(byte* buffer) +{ + int len = buffer[5] << 8 | buffer[6]; +#ifdef DEBUG + Serial.println(F("Got message")); + Serial.println(len); + Serial.println(F("Data:")); +#endif + + byte* message = buffer + 7; + +#ifdef DEBUG + for (int i = 7; i < 7 + len; i++) { + Serial.print(buffer[i], HEX); + } + Serial.println(F("")); +#endif + byte CLA = message[0]; + +#ifdef DEBUG + Serial.print("CLA : "); + Serial.println(CLA, HEX); +#endif + if (CLA != 0) { + respondErrorPDU(buffer, SW_CLA_NOT_SUPPORTED); + return; + } + + byte INS = message[1]; +#ifdef DEBUG + Serial.print("INS : "); + Serial.println(INS, HEX); +#endif + byte P1 = message[2]; + // byte P2 = message[3]; + int reqlength = (message[4] << 16) | (message[5] << 8) | message[6]; + + switch (INS) { + case U2F_INS_REGISTER: { + if (reqlength != 64) { + respondErrorPDU(buffer, SW_WRONG_LENGTH); + return; + } + + if (!EEPROM.read(EEPROM_KEYCLOAK_EN)) + hash_matched = true; + + if ((!button_pressed) || (!hash_matched)) { + respondErrorPDU(buffer, SW_CONDITIONS_NOT_SATISFIED); + /*Set fingerprint LED ring to always ON in blue */ + fingerprint.ctrlLED(/*LEDMode = */ fingerprint.eKeepsOn, + /*LEDColor = */ fingerprint.eLEDBlue, + /*blinkCount = */ 0); + + return; + } + + byte* datapart = message + 7; + byte* challenge_parameter = datapart; + byte* application_parameter = datapart + 32; + + memset(public_k, 0, sizeof(public_k)); + memset(private_k, 0, sizeof(private_k)); + uECC_make_key(public_k + 1, private_k, curve); // so we ca insert 0x04 + public_k[0] = 0x04; +#ifdef DEBUG + Serial.println(F("Public K")); + for (size_t i = 0; i < sizeof(public_k); i++) { + Serial.print(public_k[i], HEX); + Serial.print(" "); + } + Serial.println(""); + Serial.println(F("Private K")); + for (size_t i = 0; i < sizeof(private_k); i++) { + Serial.print(private_k[i], HEX); + Serial.print(" "); + } + Serial.println(""); +#endif + + // construct hash + + memcpy(handle, application_parameter, 32); + memcpy(handle + 32, private_k, 32); + for (int i = 0; i < 64; i++) { + handle[i] ^= handlekey[i % (sizeof(handlekey) - 1)]; + } + // prepare key + aes128.setKey(key, aes128.keySize()); + + // encrypt handle + encrypt_handle(); + + SHA256_CTX ctx; + sha256_init(&ctx); + large_resp_buffer[0] = 0x00; + sha256_update(&ctx, large_resp_buffer, 1); +#ifdef DEBUG + Serial.println(F("App Parameter:")); + for (int i = 0; i < 32; i++) { + Serial.print(application_parameter[i], HEX); + Serial.print(" "); + } + Serial.println(""); +#endif + + sha256_update(&ctx, application_parameter, 32); +#ifdef DEBUG + Serial.println(F("Chal Parameter:")); + for (int i = 0; i < 32; i++) { + Serial.print(challenge_parameter[i], HEX); + Serial.print(" "); + } + Serial.println(""); +#endif + + sha256_update(&ctx, challenge_parameter, 32); + +#ifdef DEBUG + Serial.println(F("Handle Parameter:")); + for (int i = 0; i < 64; i++) { + Serial.print(handle[i], HEX); + Serial.print(" "); + } + Serial.println(""); +#endif + sha256_update(&ctx, handle, 64); + sha256_update(&ctx, public_k, 65); +#ifdef DEBUG + Serial.println(F("Public key:")); + for (int i = 0; i < 65; i++) { + Serial.print(public_k[i], HEX); + Serial.print(" "); + } + Serial.println(""); +#endif + + sha256_final(&ctx, sha256_hash); +#ifdef DEBUG + Serial.println(F("Hash:")); + for (int i = 0; i < 32; i++) { + Serial.print(sha256_hash[i], HEX); + Serial.print(" "); + } + Serial.println(""); +#endif + uint8_t* signature = resp_buffer; // temporary + uint8_t tmp[32 + 32 + 64]; + SHA256_HashContext ectx + = { { &init_SHA256, &update_SHA256, &finish_SHA256, 64, 32, tmp } }; + + uECC_sign_deterministic((uint8_t*)attestation_key, sha256_hash, 32, + &ectx.uECC, signature, curve); + + int len = 0; + large_resp_buffer[len++] = 0x05; + memcpy(large_resp_buffer + len, public_k, 65); + len += 65; + large_resp_buffer[len++] = 64; // length of handle + memcpy(large_resp_buffer + len, handle, 64); + len += 64; + memcpy( + large_resp_buffer + len, attestation_der, sizeof(attestation_der)); + len += sizeof(attestation_der) - 1; + // convert signature format + // http://bitcoin.stackexchange.com/questions/12554/why-the-signature-is-always-65-13232-bytes-long + large_resp_buffer[len++] = 0x30; // header: compound structure + uint8_t* total_len = &large_resp_buffer[len]; + large_resp_buffer[len++] = 0x44; // total length (32 + 32 + 2 + 2) + large_resp_buffer[len++] = 0x02; // header: integer + + if (signature[0] > 0x7f) { + large_resp_buffer[len++] = 33; // 33 byte + large_resp_buffer[len++] = 0; + (*total_len)++; // update total length + } else { + large_resp_buffer[len++] = 32; // 32 byte + } + + memcpy(large_resp_buffer + len, signature, 32); // R value + len += 32; + large_resp_buffer[len++] = 0x02; // header: integer + + if (signature[32] > 0x7f) { + large_resp_buffer[len++] = 33; // 32 byte + large_resp_buffer[len++] = 0; + (*total_len)++; // update total length + } else { + large_resp_buffer[len++] = 32; // 32 byte + } + + memcpy(large_resp_buffer + len, signature + 32, 32); // R value + len += 32; + + byte* last = large_resp_buffer + len; + APPEND_SW_NO_ERROR(last); + len += 2; + sendLargeResponse(buffer, len); + button_pressed = false; + hash_matched = false; + /*Set fingerprint LED ring to always ON in blue */ + fingerprint.ctrlLED(/*LEDMode = */ fingerprint.eKeepsOn, + /*LEDColor = */ fingerprint.eLEDBlue, + /*blinkCount = */ 0); + + } + + break; + case U2F_INS_AUTHENTICATE: { + + // minimum is 64 + 1 + 64 + if (reqlength != (64 + 1 + 64)) { + respondErrorPDU(buffer, SW_WRONG_LENGTH); + return; + } + + if (!EEPROM.read(EEPROM_KEYCLOAK_EN)) + hash_matched = true; + if ((!button_pressed) || (!hash_matched)) { + respondErrorPDU(buffer, SW_CONDITIONS_NOT_SATISFIED); + return; + /*Set fingerprint LED ring to always ON in blue */ + fingerprint.ctrlLED(/*LEDMode = */ fingerprint.eKeepsOn, + /*LEDColor = */ fingerprint.eLEDBlue, + /*blinkCount = */ 0); + } + + byte* datapart = message + 7; + byte* challenge_parameter = datapart; + byte* application_parameter = datapart + 32; + byte handle_len = datapart[64]; + byte* client_handle = datapart + 65; + + if (handle_len != 64) { + // not from this device + respondErrorPDU(buffer, SW_WRONG_DATA); + return; + } + + memcpy(handle, client_handle, 64); + + // Prepare key + aes128.setKey(key, aes128.keySize()); + // decrypt handle using AES-128 + decrypt_handle(); + + for (int i = 0; i < 64; i++) { + handle[i] ^= handlekey[i % (sizeof(handlekey) - 1)]; + } +#ifdef DEBUG + Serial.println("handle : "); + for (uint8_t i = 0; i < sizeof(handle); i++) { + Serial.print(handle[i], HEX); + } + Serial.println(""); +#endif + uint8_t* key = handle + 32; + + if (memcmp(handle, application_parameter, 32) != 0) { + // this handle is not from us + respondErrorPDU(buffer, SW_WRONG_DATA); + return; + } + + if (P1 == 0x07) { // check-only + respondErrorPDU(buffer, SW_CONDITIONS_NOT_SATISFIED); + } else if (P1 == 0x03) { // enforce-user-presence-and-sign + int counter = getCounter(); + SHA256_CTX ctx; + sha256_init(&ctx); + sha256_update(&ctx, application_parameter, 32); + large_resp_buffer[0] = 0x01; // user_presence + + int ctr = ((counter >> 24) & 0xff) | // move byte 3 to byte 0 + ((counter << 8) & 0xff0000) | // move byte 1 to byte 2 + ((counter >> 8) & 0xff00) | // move byte 2 to byte 1 + ((counter << 24) & 0xff000000); // byte 0 to byte 3 + + memcpy(large_resp_buffer + 1, &ctr, 4); + + sha256_update(&ctx, large_resp_buffer, 5); // user presence + ctr + + sha256_update(&ctx, challenge_parameter, 32); + sha256_final(&ctx, sha256_hash); + + uint8_t* signature = resp_buffer; // temporary + + uint8_t tmp[32 + 32 + 64]; + SHA256_HashContext ectx = { { &init_SHA256, &update_SHA256, + &finish_SHA256, 64, 32, tmp } }; + + uECC_sign_deterministic( + (uint8_t*)key, sha256_hash, 32, &ectx.uECC, signature, curve); + + int len = 5; + + // convert signature format + // http://bitcoin.stackexchange.com/questions/12554/why-the-signature-is-always-65-13232-bytes-long + large_resp_buffer[len++] = 0x30; // header: compound structure + uint8_t* total_len = &large_resp_buffer[len]; + large_resp_buffer[len++] = 0x44; // total length (32 + 32 + 2 + 2) + large_resp_buffer[len++] = 0x02; // header: integer + + if (signature[0] > 0x7f) { + large_resp_buffer[len++] = 33; // 33 byte + large_resp_buffer[len++] = 0; + (*total_len)++; // update total length + } else { + large_resp_buffer[len++] = 32; // 32 byte + } + + memcpy(large_resp_buffer + len, signature, 32); // R value + len += 32; + large_resp_buffer[len++] = 0x02; // header: integer + + if (signature[32] > 0x7f) { + large_resp_buffer[len++] = 33; // 32 byte + large_resp_buffer[len++] = 0; + (*total_len)++; // update total length + } else { + large_resp_buffer[len++] = 32; // 32 byte + } + + button_pressed = false; + hash_matched = false; + /*Set fingerprint LED ring to always ON in blue */ + fingerprint.ctrlLED(/*LEDMode = */ fingerprint.eKeepsOn, + /*LEDColor = */ fingerprint.eLEDBlue, + /*blinkCount = */ 0); + + memcpy(large_resp_buffer + len, signature + 32, 32); // R value + len += 32; + byte* last = large_resp_buffer + len; + APPEND_SW_NO_ERROR(last); + len += 2; +#ifdef DEBUG + Serial.print("Len to send "); + Serial.println(len); +#endif + + sendLargeResponse(buffer, len); + + setCounter(counter + 1); + } else { + // return error + } + + } break; + case U2F_INS_VERSION: { + if (reqlength != 0) { + respondErrorPDU(buffer, SW_WRONG_LENGTH); + return; + } + // reuse input buffer for sending + SET_MSG_LEN(buffer, 8); // len("U2F_V2") + 2 byte SW + byte* datapart = buffer + 7; + memcpy(datapart, "U2F_V2", 6); + datapart += 6; + APPEND_SW_NO_ERROR(datapart); + RawHID.send(buffer, 100); + } break; + + case U2F_INS_FPHASH: { + Serial.println("Getting FPHASH"); + byte buf[64] = {}; + buf[0] = 0x69; + + if (LAST_MATCHED_ID < 1) { + Serial.printf("LAST_ID: %d\n", LAST_MATCHED_ID); + RawHID.send(buf, 100); + return; + } + Serial.printf("Getting hash for ID: %d\n", LAST_MATCHED_ID - 1); + uint16_t memoryAddress = EEPROM_FP_HASH + ((LAST_MATCHED_ID - 1) * 32); + + uint8_t idx = 1; + for (uint16_t i = memoryAddress; i < (memoryAddress + SHA256_SIZE); + i++) { + buf[idx] = EEPROM.read(i); + Serial.printf("%d ", buf[idx]); + idx++; + } + Serial.printf("\n"); + RawHID.send(buf, 100); + } break; + + case U2F_INS_HASHMATCH: { + /* +byte hash_buffer[64]; +for (uint8_t i = 0; i < 64; i++) { +hash_buffer[i] = 0; +} +RawHID.send(hash_buffer, 100); + */ + hash_matched = true; + } break; + + case U2F_INS_CLR_FPS: { + + fingerprint.ctrlLED(/*LEDMode = */ fingerprint.eBreathing, + /*LEDColor = */ fingerprint.eLEDBlue, + /*blinkCount = */ 0); + fingerprint.delFingerprint(DELALL); // delete all fingerprint + Serial.println("All fingerprints cleared"); + Serial.println("-----------------------------"); + button_pressed = false; + hash_matched = false; + delay(1000); + } break; + case U2F_INS_DEL_FPS: { + while (true) { + uint8_t ret = 0; + Serial.println( + "Press your finger on the sensor to delete the fingerprint"); + + fingerprint.ctrlLED(/*LEDMode = */ fingerprint.eBreathing, + /*LEDColor = */ fingerprint.eLEDBlue, /*blinkCount = */ 0); + + uint8_t buf[64] = { 0 }; + buf[0] = 0x69; + if ((fingerprint.collectionFingerprint(/*timeout=*/10)) + != ERR_ID809) { + + ret = fingerprint.search(); + if (ret != 0) { + /*Delete the fingerprint of this ID*/ + fingerprint.delFingerprint(/*Fingerprint ID = */ ret); + // fingerprint.delFingerprint(DELALL); //Delete all + // fingerprints + Serial.print("delete succeeds,ID="); + Serial.println(ret); + /*Set fingerprint LED ring to always ON in green */ + fingerprint.ctrlLED(/*LEDMode = */ fingerprint.eKeepsOn, + /*LEDColor = */ fingerprint.eLEDGreen, + /*blinkCount = */ 0); + buf[1] = 0xac; + RawHID.send(buf, 100); + button_pressed = false; + hash_matched = false; + return; + } else { + Serial.println("Fingerprint is unregistered"); + /*Set fingerprint LED ring to always ON in red*/ + fingerprint.ctrlLED(/*LEDMode = */ fingerprint.eKeepsOn, + /*LEDColor = */ fingerprint.eLEDRed, + /*blinkCount = */ 0); + buf[1] = 0xaf; + RawHID.send(buf, 100); + fingerprint.ctrlLED(/*LEDMode = */ fingerprint.eBreathing, + /*LEDColor = */ fingerprint.eLEDBlue, + /*blinkCount = */ 0); + button_pressed = false; + hash_matched = false; + return; + } + } else { + Serial.println("Capturing fails"); + /*Get error code information*/ + // desc = fingerprint.getErrorDescription(); + // Serial.println(desc); + /*Set fingerprint LED ring to always ON in red*/ + fingerprint.ctrlLED(/*LEDMode = */ fingerprint.eKeepsOn, + /*LEDColor = */ fingerprint.eLEDRed, /*blinkCount = */ 0); + // buf[1] = 0xaf; + // RawHID.send(buf, 100); + // return; + } + Serial.println("Please release your finger"); + /*Wait for finger to release + Return 1 when finger is detected, otherwise return 0 + */ + while (fingerprint.detectFinger()) + ; + delay(1000); + } + break; + } break; + + case U2F_INS_REG_FPS: { + + uint8_t i = 0; + /*Get an unregistered ID for saving fingerprint + Return ID when succeeded + Return ERR_ID809 if failed + */ + if ((ID = fingerprint.getEmptyID()) == ERR_ID809) { + while (1) { + /*Get error code information*/ + // desc = fingerprint.getErrorDescription(); + // Serial.println(desc); + delay(1000); + } + } + Serial.print("unresistered ID,ID="); + Serial.println(ID); + + uint8_t buf[64] = { 0 }; + buf[0] = 0x69; + buf[1] = 0xab; + + i = 0; // Clear sampling times + /*Fingerprint sampling 3 times*/ + while (i < COLLECT_NUMBER) { + /*Set fingerprint LED ring mode, color, and number of blinks + Can be set as follows: + Parameter 1:<LEDMode> + eBreathing eFastBlink eKeepsOn eNormalClose + eFadeIn eFadeOut eSlowBlink + Parameter 2:<LEDColor> + eLEDGreen eLEDRed eLEDYellow eLEDBlue + eLEDCyan eLEDMagenta eLEDWhite + Parameter 3:<Number of blinks> 0 represents blinking all the time + This parameter will only be valid in mode eBreathing, eFastBlink, + eSlowBlink + */ + fingerprint.ctrlLED(/*LEDMode = */ fingerprint.eBreathing, + /*LEDColor = */ fingerprint.eLEDBlue, + /*blinkCount = */ 0); + Serial.print("The fingerprint sampling of the"); + Serial.print(i + 1); + Serial.println("(th) is being taken"); + Serial.println("Please press down your finger"); + /*Capture fingerprint image, 10s idle timeout, if timeout=0,Disable + the collection timeout function IF succeeded, return 0, otherwise, + return ERR_ID809 + */ + if ((fingerprint.collectionFingerprint(/*timeout = */ 10)) + != ERR_ID809) { + /*Set fingerprint LED ring to quick blink in yellow 3 times */ + fingerprint.ctrlLED(/*LEDMode = */ fingerprint.eFastBlink, + /*LEDColor = */ fingerprint.eLEDYellow, + /*blinkCount = */ 3); + Serial.println("Sampling succeeds"); + i++; // Sampling times +1 + uint8_t buf[64] = { 0 }; + buf[0] = 0x69; + buf[1] = 0x4f; + RawHID.send(buf, 100); + } else { + Serial.println("Sampling failed"); + /*Get error code information*/ + // desc = fingerprint.getErrorDescription(); + // Serial.println(desc); + } + Serial.println("Please release your finger"); + /*Wait for finger to release + Return 1 when finger is detected, otherwise return 0 + */ + while (fingerprint.detectFinger()) + ; + } + + if (ID < 1) { + Serial.println("Fingerprint registration failed"); + return; + } + + /*Save fingerprint in an unregistered ID */ + if (fingerprint.storeFingerprint(/*Empty ID = */ ID) != ERR_ID809) { + Serial.print("Saving succeed,ID="); + Serial.println(ID); + Serial.println("-----------------------------"); + /*Set fingerprint LED ring to always ON in green */ + fingerprint.ctrlLED(/*LEDMode = */ fingerprint.eKeepsOn, + /*LEDColor = */ fingerprint.eLEDGreen, + /*blinkCount = */ 0); + delay(1000); + /*Turn off fingerprint LED ring */ + fingerprint.ctrlLED(/*LEDMode = */ fingerprint.eNormalClose, + /*LEDColor = */ fingerprint.eLEDBlue, + /*blinkCount = */ 0); + delay(1000); + + fingerprint.getTemplate(ID, temp); + compute_hash(); + saveHash(); + readHash(); + + uint8_t buf[64] = { 0 }; + buf[0] = 0x69; + buf[1] = 0x5f; + buf[2] = ID; + + memcpy(&buf[3], HASH_DATA, 32); + + // send the hash along with it + + RawHID.send(buf, 100); + + } else { + uint8_t buf[64] = { 0 }; + buf[0] = 0x69; + buf[1] = 0x6f; + RawHID.send(buf, 100); + + Serial.println("Saving failed"); + /*Get error code information*/ + // desc = fingerprint.getErrorDescription(); + // Serial.println(desc); + } + + byte hash_buffer[64]; + for (uint8_t i = 0; i < 64; i++) { + + hash_buffer[i] = 0; + } + RawHID.send(hash_buffer, 100); + button_pressed = false; + hash_matched = false; + + } break; + case U2F_INS_PING: { + Serial.printf("Got a ping msg"); + uint8_t buf[64] = { 0 }; + + buf[0] = 0x69; + buf[1] = 0x11; + buf[2] = fingerprint.getEnrollCount(); + memcpy(&buf[3], U_ID, 32); + RawHID.send(buf, 100); + } break; + case U2F_INS_KEYCLOAK_EN: { + Serial.printf("Enable keyCloak hash match"); + + EEPROM.write(EEPROM_KEYCLOAK_EN, KEYCLOAK_EN); + + uint8_t buf[64] = { 0 }; + RawHID.send(buf, 100); + } break; + case U2F_INS_KEYCLOAK_DIS: { + Serial.printf("Disable keyCloak hash match"); + + EEPROM.write(EEPROM_KEYCLOAK_EN, KEYCLOAK_DIS); + + uint8_t buf[64] = { 0 }; + RawHID.send(buf, 100); + } break; + + default: { + respondErrorPDU(buffer, SW_INS_NOT_SUPPORTED); + }; + } +} + +void processPacket(byte* buffer) +{ +#ifdef DEBUG + Serial.print("Process CMD "); +#endif + unsigned char cmd = buffer[4]; // cmd or continuation +#ifdef DEBUG + Serial.println((int)cmd, HEX); +#endif + int len = buffer[5] << 8 | buffer[6]; + if (cmd > U2FHID_INIT || cmd == U2FHID_LOCK) { + errorResponse(recv_buffer, ERR_INVALID_CMD); + return; + } + if (cmd == U2FHID_PING) { + if (len <= MAX_INITIAL_PACKET) { +#ifdef DEBUG + Serial.println("Sending ping response"); +#endif + RawHID.send(buffer, 100); + } else { + // large packet + // send first one +#ifdef DEBUG + Serial.println("SENT RESPONSE 3"); +#endif + RawHID.send(buffer, 100); + len -= MAX_INITIAL_PACKET; + byte p = 0; + int offset = 7 + MAX_INITIAL_PACKET; + while (len > 0) { + memcpy(resp_buffer, buffer, 4); // copy cid + resp_buffer[4] = p++; + memcpy( + resp_buffer + 5, buffer + offset, MAX_CONTINUATION_PACKET); + RawHID.send(resp_buffer, 100); + len -= MAX_CONTINUATION_PACKET; + offset += MAX_CONTINUATION_PACKET; + delayMicroseconds(2500); + +#ifdef DEBUG + Serial.println("Sending large ping response"); +#endif + } + } + } + if (cmd == U2FHID_MSG) { + processMessage(buffer); + } +} + +void setOtherTimeout() +{ + // we can process the data + // but if we find another channel is waiting for continuation, we set it as + // timeout + for (int i = 0; i < MAX_CHANNEL; i++) { + if (channel_states[i].state == STATE_CHANNEL_WAIT_CONT) { +#ifdef DEBUG + Serial.println("Set other timeout"); +#endif + channel_states[i].state = STATE_CHANNEL_TIMEOUT; + } + } +} + +int cont_start = 0; + +void authenticateKey() +{ + int n; + int cid_vendor = CID_VENDOR; + + n = RawHID.recv(recv_buffer, 0); // 0 timeout = do not wait + + if (n > 0) { + +#ifdef DEBUG + Serial.print(F("\n\nReceived packet, CID: ")); +#endif + // int cid = *(int*)recv_buffer; + int cid; // handle strict-aliasing warning + memcpy(&cid, recv_buffer, sizeof(cid)); +#ifdef DEBUG + Serial.println(cid, HEX); +#endif + + if ((cid == cid_vendor) && (recv_buffer[4] != 0xFF)) { + processMessage(recv_buffer); + return; + } + if (cid == 0) { + errorResponse(recv_buffer, ERR_INVALID_CID); + return; + } + + unsigned char cmd_or_cont = recv_buffer[4]; // cmd or continuation + + int len = (recv_buffer[5]) << 8 | recv_buffer[6]; + +#ifdef DEBUG + if (IS_NOT_CONTINUATION_PACKET(cmd_or_cont)) { + Serial.print(F("LEN ")); + Serial.println((int)len); + } +#endif + // don't care about cid + if (cmd_or_cont == U2FHID_INIT) { + setOtherTimeout(); + cid = initResponse(recv_buffer); + int cidx = find_channel_index(cid); + channel_states[cidx].state = STATE_CHANNEL_WAIT_PACKET; + return; + } + + if (cid == -1) { + errorResponse(recv_buffer, ERR_INVALID_CID); + return; + } + + int cidx = find_channel_index(cid); + + if (cidx == -1) { + +#ifdef DEBUG + Serial.println("allocating new CID"); +#endif + allocate_channel(cid); + cidx = find_channel_index(cid); + if (cidx == -1) { + errorResponse(recv_buffer, ERR_INVALID_CID); + return; + } + } + + if (IS_NOT_CONTINUATION_PACKET(cmd_or_cont)) { + + if (len > MAX_TOTAL_PACKET) { + errorResponse(recv_buffer, ERR_INVALID_LEN); // invalid length + return; + } + + if (len > MAX_INITIAL_PACKET) { + // if another channel is waiting for continuation, we respond + // with busy + for (int i = 0; i < MAX_CHANNEL; i++) { + if (channel_states[i].state == STATE_CHANNEL_WAIT_CONT) { + if (i == cidx) { + errorResponse(recv_buffer, + ERR_INVALID_SEQ); // invalid sequence + channel_states[i].state = STATE_CHANNEL_WAIT_PACKET; + } else { + errorResponse(recv_buffer, ERR_CHANNEL_BUSY); + return; + } + + return; + } + } + // no other channel is waiting + channel_states[cidx].state = STATE_CHANNEL_WAIT_CONT; + cont_start = millis(); + memcpy(large_buffer, recv_buffer, 64); + large_data_len = len; + large_data_offset = MAX_INITIAL_PACKET; + expected_next_packet = 0; + return; + } + + setOtherTimeout(); + processPacket(recv_buffer); + channel_states[cidx].state = STATE_CHANNEL_WAIT_PACKET; + } else { + + if (channel_states[cidx].state != STATE_CHANNEL_WAIT_CONT) { + +#ifdef DEBUG + Serial.println("ignoring stray packet"); + Serial.println(cid, HEX); +#endif + return; + } + + // this is a continuation + if (cmd_or_cont != expected_next_packet) { + errorResponse(recv_buffer, ERR_INVALID_SEQ); // invalid sequence + channel_states[cidx].state = STATE_CHANNEL_WAIT_PACKET; + return; + } else { + + memcpy(large_buffer + large_data_offset + 7, recv_buffer + 5, + MAX_CONTINUATION_PACKET); + large_data_offset += MAX_CONTINUATION_PACKET; + + if (large_data_offset < large_data_len) { + expected_next_packet++; +#ifdef DEBUG + Serial.println("Expecting next cont"); +#endif + + return; + } +#ifdef DEBUG + Serial.println("Completed"); +#endif + channel_states[cidx].state = STATE_CHANNEL_WAIT_PACKET; + processPacket(large_buffer); + return; + } + } + } else { + + for (int i = 0; i < MAX_CHANNEL; i++) { + if (channel_states[i].state == STATE_CHANNEL_TIMEOUT) { +#ifdef DEBUG + Serial.println("send timeout"); + Serial.println(channel_states[i].cid, HEX); +#endif + + memcpy(recv_buffer, &channel_states[i].cid, 4); + errorResponse(recv_buffer, ERR_MSG_TIMEOUT); + channel_states[i].state = STATE_CHANNEL_WAIT_PACKET; + } + if (channel_states[i].state == STATE_CHANNEL_WAIT_CONT) { + + int now = millis(); + if ((now - channel_states[i].last_millis) > 500) { +#ifdef DEBUG + Serial.println("SET timeout"); +#endif + channel_states[i].state = STATE_CHANNEL_TIMEOUT; + } + } + } + } +} + +void loop() +{ + auto count = fingerprint.getEnrollCount(); + if (!button_pressed && count != 0) { + readFingerPrint(); + // authenticateKey(); + } else { + authenticateKey(); + } +} -- GitLab