#include <EEPROM.h> #include <string.h> #include "sha256.h" #include "uECC.h" #include "DFRobot_ID809.h" #define FPSerial Serial3 #define CID_BROADCAST 0xffffffff // Broadcast channel id #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 // 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 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 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]; #define MAX_CHANNEL 4 int button_pressed = 0; const char attestation_key[] = "\xf3\xfc\xcc\x0d\x00\xd8\x03\x19\x54\xf9" "\x08\x64\xd4\x3c\x24\x7f\x4b\xf5\xf0\x66\x5c\x6b\x50\xcc" "\x17\x74\x9a\x27\xd1\xcf\x76\x64"; const char attestation_der[] = "\x30\x82\x01\x3c\x30\x81\xe4\xa0\x03\x02" "\x01\x02\x02\x0a\x47\x90\x12\x80\x00\x11\x55\x95\x73\x52" "\x30\x0a\x06\x08\x2a\x86\x48\xce\x3d\x04\x03\x02\x30\x17" "\x31\x15\x30\x13\x06\x03\x55\x04\x03\x13\x0c\x47\x6e\x75" "\x62\x62\x79\x20\x50\x69\x6c\x6f\x74\x30\x1e\x17\x0d\x31" "\x32\x30\x38\x31\x34\x31\x38\x32\x39\x33\x32\x5a\x17\x0d" "\x31\x33\x30\x38\x31\x34\x31\x38\x32\x39\x33\x32\x5a\x30" "\x31\x31\x2f\x30\x2d\x06\x03\x55\x04\x03\x13\x26\x50\x69" "\x6c\x6f\x74\x47\x6e\x75\x62\x62\x79\x2d\x30\x2e\x34\x2e" "\x31\x2d\x34\x37\x39\x30\x31\x32\x38\x30\x30\x30\x31\x31" "\x35\x35\x39\x35\x37\x33\x35\x32\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\x8d\x61\x7e\x65\xc9\x50\x8e" "\x64\xbc\xc5\x67\x3a\xc8\x2a\x67\x99\xda\x3c\x14\x46\x68" "\x2c\x25\x8c\x46\x3f\xff\xdf\x58\xdf\xd2\xfa\x3e\x6c\x37" "\x8b\x53\xd7\x95\xc4\xa4\xdf\xfb\x41\x99\xed\xd7\x86\x2f" "\x23\xab\xaf\x02\x03\xb4\xb8\x91\x1b\xa0\x56\x99\x94\xe1" "\x01\x30\x0a\x06\x08\x2a\x86\x48\xce\x3d\x04\x03\x02\x03" "\x47\x00\x30\x44\x02\x20\x60\xcd\xb6\x06\x1e\x9c\x22\x26" "\x2d\x1a\xac\x1d\x96\xd8\xc7\x08\x29\xb2\x36\x65\x31\xdd" "\xa2\x68\x83\x2c\xb8\x36\xbc\xd3\x0d\xfa\x02\x20\x63\x1b" "\x14\x59\xf0\x9e\x63\x30\x05\x57\x22\xc8\xd8\x9b\x7f\x48" "\x88\x3b\x90\x89\xb8\x8d\x60\xd1\xd9\x79\x59\x02\xb3\x04" "\x10\xdf"; //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]; 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() { static uint8_t codeBlock = 1; static uint8_t ret = 0; /*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 */ Serial.print("codeBlock"); Serial.println(codeBlock); Serial.println("ret:"); Serial.println(ret); switch(codeBlock){ case 1: 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 */ ++codeBlock; return; case 2: if(((fingerprint.collectionFingerprint(/*timeout=*/0)) != ERR_ID809)){ ++codeBlock; return; } else return; // fingerprint.collectionFingerprint(0); /*Set fingerprint LED ring to quick blink in yellow 3 times*/ case 3: fingerprint.ctrlLED(/*LEDMode = */fingerprint.eFastBlink, /*LEDColor = */fingerprint.eLEDYellow, /*blinkCount = */3); // Serial.println("Capturing succeeds"); // Serial.println("Please release your finger"); /*Wait for finger to release Return 1 when finger is detected, otherwise return 0 */ ++codeBlock; return; case 4: while (fingerprint.detectFinger()); //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 */ ++codeBlock; return; case 5: ret = fingerprint.search(); /*Compare the captured fingerprint with a fingerprint of specific ID Return fingerprint ID(1-80) if succeed, return 0 when failed */ //ret = fingerprint.verify(/*Fingerprint ID = */1); ++codeBlock; return; case 6: if (ret != 0) { /*Set fingerprint LED ring to always ON in green */ fingerprint.ctrlLED(/*LEDMode = */fingerprint.eKeepsOn, /*LEDColor = */fingerprint.eLEDGreen, /*blinkCount = */0); Serial.print("Matching succeeds,ID="); Serial.println(ret); //delay(1000); //return 1; button_pressed = 1; } 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); //return 0; button_pressed = 0; } codeBlock = 1; break; default: codeBlock = 1 ; break; } // //Serial.println("-----------------------------"); // delay(1000); } //int readFingerPrint(){ // // // uint8_t ret = 0; // /*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 // */ // // 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"); // //// Serial.println("Please release your finger"); // /*Wait for finger to release // Return 1 when finger is detected, otherwise return 0 // */ // // while(fingerprint.detectFinger()); // //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 // */ // //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); // Serial.print("Matching succeeds,ID="); // Serial.println(ret); // //delay(1000); // return 1; // }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); // return 0; // } // }else{ // Serial.println("Capturing fails"); // /*Get error code information*/ // //desc = fingerprint.getErrorDescription(); // //Serial.println(desc); // //delay(1000); // return 0; // } //// //Serial.println("-----------------------------"); //// delay(1000); //} void setup() { uECC_set_rng(&RNG); FPSerial.begin(115200); fingerprint.begin(FPSerial); Serial.begin(9600); 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); } } 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) { int cid = *(int*)buffer; 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 { 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 RawHID.send(resp_buffer, 100); 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; 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; } #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) 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) { 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]; byte *message = buffer + 7; //todo: check CLA = 0 byte CLA = message[0]; if (CLA != 0) { respondErrorPDU(buffer, SW_CLA_NOT_SUPPORTED); return; } byte INS = message[1]; 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 (!button_pressed) { respondErrorPDU(buffer, SW_CONDITIONS_NOT_SATISFIED); //if (readFingerPrint()) if(touchRead(3)>2000) button_pressed = 1; else button_pressed = 0; // button_pressed = readFingerPrint(); Serial.println(button_pressed); 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; //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)]; } SHA256_CTX ctx; sha256_init(&ctx); large_resp_buffer[0] = 0x00; sha256_update(&ctx, large_resp_buffer, 1); sha256_update(&ctx, application_parameter, 32); sha256_update(&ctx, challenge_parameter, 32); sha256_update(&ctx, handle, 64); sha256_update(&ctx, public_k, 65); 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 *)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 = 0; } break; case U2F_INS_AUTHENTICATE: { //minimum is 64 + 1 + 64 if (reqlength != (64 + 1 + 64)) { respondErrorPDU(buffer, SW_WRONG_LENGTH); return; } 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; } if (!button_pressed) { respondErrorPDU(buffer, SW_CONDITIONS_NOT_SATISFIED); //if (readFingerPrint()) if(touchRead(3)>2000) button_pressed = 1; else button_pressed = 0; // button_pressed = readFingerPrint(); Serial.println(button_pressed); return; } memcpy(handle, client_handle, 64); for (int i = 0; i < 64; i++) { handle[i] ^= handlekey[i % (sizeof(handlekey) - 1)]; } 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 } 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); setCounter(counter + 1); } else { //return error } button_pressed = 0 ; } 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; default: { respondErrorPDU(buffer, SW_INS_NOT_SUPPORTED); } ; } } void processPacket(byte *buffer) { unsigned char cmd = buffer[4]; //cmd or continuation 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) { RawHID.send(buffer, 100); } else { //large packet //send first one 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); } } } 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) { channel_states[i].state = STATE_CHANNEL_TIMEOUT; } } } int cont_start = 0; void loop() { int n; n = RawHID.recv(recv_buffer, 0); // 0 timeout = do not wait if (n > 0) { //int cid = *(int*)recv_buffer; int cid; //handle strict-aliasing warning memcpy(&cid, recv_buffer, sizeof(cid)); 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]; //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) { 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) { 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++; return; } 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) { 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) { channel_states[i].state = STATE_CHANNEL_TIMEOUT; } } } } }