├── LICENSE ├── README.md ├── esp32-u2f-ble.ino ├── framing.cpp ├── framing.h ├── hmac.cpp ├── hmac.h ├── u2f.cpp └── u2f.h /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Matthias Vögeler 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # esp32-u2f-ble 2 | Implementation of the FIDO Universal 2nd Factor (U2F) protocol on ESP32 over Bluetooth LE. 3 | 4 | This is a project for the Arduino IDE. 5 | 6 | # License 7 | [MIT](https://opensource.org/licenses/MIT) 8 | 9 | 10 | # Testing 11 | The project has been tested usgin Chromium on Yubico's [demo website](https://demo.yubico.com/webauthn-technical/registration). 12 | -------------------------------------------------------------------------------- /esp32-u2f-ble.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "framing.h" 12 | 13 | // Device Information Service 14 | #define DEVICE_INFORMATION_SERVICE "0000180a-0000-1000-8000-00805f9b34fb" // 180A 15 | #define MANUFACTURER_NAME_STRING "00002a29-0000-1000-8000-00805f9b34fb" // 2A29 16 | #define MODEL_NUMBER_STRING "00002a24-0000-1000-8000-00805f9b34fb" // 2A24 17 | #define FIRMWAREREVISIONSTRING "00002a26-0000-1000-8000-00805f9b34fb" // 2A26 18 | 19 | // U2F Service 20 | #define U2F_SERVICE "0000fffd-0000-1000-8000-00805f9b34fb" // FFFD 21 | #define U2F_CONTROL_POINT "f1d0fff1-deaa-ecee-b42f-c9ba7ed623bb" 22 | #define U2F_STATUS "f1d0fff2-deaa-ecee-b42f-c9ba7ed623bb" 23 | #define U2F_CONTROL_POINT_LENGTH "f1d0fff3-deaa-ecee-b42f-c9ba7ed623bb" 24 | #define U2F_SERVICE_REVISION "00002a28-0000-1000-8000-00805f9b34fb" // 2A28 25 | #define U2F_SERVICE_REVISION_BITFIELD "f1d0fff4-deaa-ecee-b42f-c9ba7ed623bb" 26 | //#define CLIENT_CHARACTERISTIC_CONFIG "00002902-0000-1000-8000-00805f9b34fb" // 2902 27 | 28 | #define U2F_1_2 0x40 29 | 30 | std::mutex mtx; 31 | std::condition_variable cond_var; 32 | std::mutex mtx_ka; 33 | std::condition_variable cond_var_ka; 34 | 35 | std::string response; 36 | enum processingStatus status = Idle; 37 | 38 | static uint32_t PIN = 111111; 39 | static uint8_t U2fControlPointLength[] = { 0x00, 0x14 }; // 20 bytes 40 | static uint8_t U2fServiceRevision[] = {U2F_1_2}; // U2F Version 1.2 41 | 42 | static bool deviceConnected = false; 43 | uint8_t buffer[100]; 44 | static BLECharacteristic *pU2fStatus; 45 | 46 | class MyServerCallbacks: public BLEServerCallbacks { 47 | void onConnect(BLEServer* pServer) { 48 | deviceConnected = true; 49 | }; 50 | 51 | void onDisconnect(BLEServer* pServer) { 52 | deviceConnected = false; 53 | } 54 | }; 55 | 56 | class U2fControlPointCallback: public BLECharacteristicCallbacks { 57 | 58 | void onWrite(BLECharacteristic *pCharacteristic) { 59 | log_d("*** U2F Control Point ***"); 60 | std::string value = pCharacteristic->getValue(); 61 | if ( (status == Idle) && (value.length() > 0) ) { 62 | log_d("*********"); 63 | log_d("New value: %02x", value[0]); 64 | log_d("*********"); 65 | switch (update(value)) { 66 | case UPDATE_FRAMING: 67 | // status = Idle; 68 | break; 69 | case UPDATE_SUCCESS: 70 | case UPDATE_ERROR: 71 | std::unique_lock lock(mtx); 72 | status = CmdReady; 73 | cond_var.notify_one(); 74 | break; 75 | } 76 | } 77 | } 78 | }; 79 | 80 | class U2fServiceRevisionCallback: public BLECharacteristicCallbacks { 81 | void onWrite(BLECharacteristic *pCharacteristic) { 82 | log_d("*** U2F Service Revision ***"); 83 | std::string value = pCharacteristic->getValue(); 84 | if (value.length() > 0) { 85 | //U2fServiceRevision[0] = value[0]; 86 | log_d("*********"); 87 | log_d("New value: %.2x", value[0]); 88 | log_d("*********"); 89 | } 90 | } 91 | }; 92 | 93 | void setup() { 94 | Serial.begin(115200); 95 | Serial.println("Starting BLE-U2F!"); 96 | 97 | log_v("Verbose"); 98 | log_d("Debug"); 99 | log_i("Info"); 100 | log_w("Warning"); 101 | log_e("Error"); 102 | 103 | BLEDevice::init("ESPU2F"); 104 | /* 105 | * Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation 106 | */ 107 | BLEServer *pServer = BLEDevice::createServer(); 108 | pServer->setCallbacks(new MyServerCallbacks()); 109 | // Device Information Service 110 | BLEService *pDevInfoService = pServer->createService(DEVICE_INFORMATION_SERVICE); 111 | // Characteristic Manufacturer Name String 112 | BLECharacteristic *pCharacteristic = pDevInfoService->createCharacteristic( 113 | MANUFACTURER_NAME_STRING, 114 | BLECharacteristic::PROPERTY_READ); 115 | pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ); 116 | pCharacteristic->setValue("ESP32 U2f Demo"); 117 | // Characteristic Model Number String 118 | pCharacteristic = pDevInfoService->createCharacteristic( 119 | MODEL_NUMBER_STRING, 120 | BLECharacteristic::PROPERTY_READ); 121 | pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ); 122 | pCharacteristic->setValue("ESP32"); 123 | // Characteristic Firmware Revision String 124 | pCharacteristic = pDevInfoService->createCharacteristic( 125 | FIRMWAREREVISIONSTRING, 126 | BLECharacteristic::PROPERTY_READ); 127 | pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ); 128 | pCharacteristic->setValue("0.1"); 129 | 130 | pDevInfoService->start(); 131 | 132 | // U2F Service 133 | BLEService *pU2fService = pServer->createService(U2F_SERVICE); 134 | // Characteristic U2F Control Point 135 | BLECharacteristic *pU2fControlPoint = pU2fService->createCharacteristic( 136 | U2F_CONTROL_POINT, 137 | BLECharacteristic::PROPERTY_WRITE); 138 | pU2fControlPoint->setAccessPermissions(ESP_GATT_PERM_WRITE_ENCRYPTED); 139 | pU2fControlPoint->setCallbacks(new U2fControlPointCallback()); 140 | // Characteristic U2F Status 141 | pU2fStatus = pU2fService->createCharacteristic( 142 | U2F_STATUS, 143 | BLECharacteristic::PROPERTY_READ | 144 | BLECharacteristic::PROPERTY_WRITE | 145 | BLECharacteristic::PROPERTY_NOTIFY); 146 | pU2fStatus->addDescriptor(new BLE2902()); 147 | pU2fStatus->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); 148 | // Characteristic U2F Control Point Length 149 | pCharacteristic = pU2fService->createCharacteristic( 150 | U2F_CONTROL_POINT_LENGTH, 151 | BLECharacteristic::PROPERTY_READ); 152 | pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED); 153 | pCharacteristic->setValue(U2fControlPointLength, sizeof(U2fControlPointLength)); 154 | // Characteristic U2F Service Revision 155 | pCharacteristic = pU2fService->createCharacteristic( 156 | U2F_SERVICE_REVISION, 157 | BLECharacteristic::PROPERTY_READ); 158 | pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED); 159 | pCharacteristic->setValue("Rev. 1.0"); 160 | // Characteristic U2F Service Revision Bitfield 161 | pCharacteristic = pU2fService->createCharacteristic( 162 | U2F_SERVICE_REVISION_BITFIELD, 163 | BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE); 164 | pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); 165 | pCharacteristic->setValue(U2fServiceRevision, sizeof(U2fServiceRevision)); 166 | pCharacteristic->setCallbacks(new U2fServiceRevisionCallback()); 167 | 168 | pU2fService->start(); 169 | 170 | BLEAdvertising *pAdvertising = pServer->getAdvertising(); 171 | pAdvertising->start(); 172 | 173 | BLESecurity *pSecurity = new BLESecurity(); 174 | // set static PIN 175 | esp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, &PIN, sizeof(uint32_t)); 176 | pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_MITM_BOND); // bonding with peer device after authentication 177 | pSecurity->setCapability(ESP_IO_CAP_OUT); // set the IO capability to: Display Only 178 | pSecurity->setKeySize(16); 179 | uint8_t auth_option = ESP_BLE_ONLY_ACCEPT_SPECIFIED_AUTH_DISABLE; 180 | esp_ble_gap_set_security_param(ESP_BLE_SM_ONLY_ACCEPT_SPECIFIED_SEC_AUTH, &auth_option, sizeof(uint8_t)); 181 | //uint8_t oob_support = ESP_BLE_OOB_DISABLE; 182 | //esp_ble_gap_set_security_param(ESP_BLE_SM_OOB_SUPPORT, &oob_support, sizeof(uint8_t)); 183 | pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); 184 | pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); 185 | } 186 | 187 | void keepalive() { 188 | while (true) { 189 | auto const timeout= std::chrono::steady_clock::now()+std::chrono::milliseconds(100); 190 | std::unique_lock lock(mtx_ka); 191 | if ( cond_var_ka.wait_until(lock, timeout) == std::cv_status::timeout) { 192 | log_d("*** keep alive"); 193 | } 194 | else return; 195 | if (status == ResultReady) return; 196 | } 197 | } 198 | 199 | // simulates user presence 200 | bool user_presence_check() { 201 | const char TUP_NEEDED[] = {0x82, 0x00, 0x01, 0x02}; 202 | std::string ka = std::string(TUP_NEEDED, sizeof(TUP_NEEDED)); 203 | for (uint8_t i=0; i<5; i++) { 204 | log_d("*** TUP_NEEDED: %08x", *((uint32_t*)TUP_NEEDED)); 205 | pU2fStatus->setValue(ka); // TUP_NEEDED: CMD HLEN LLEN DATA 206 | pU2fStatus->notify(); 207 | delay(250); 208 | } 209 | return true; 210 | } 211 | 212 | void loop() { 213 | std::unique_lock lock(mtx); 214 | while (status != CmdReady) { // loop to avoid spurious wakeups 215 | cond_var.wait(lock); 216 | } 217 | status = IsProcessing; 218 | //std::thread thread_ka(keepalive); 219 | processCMD(); 220 | status = ResultReady; 221 | /*std::unique_lock lock_ka(mtx_ka); 222 | cond_var_ka.notify_one(); 223 | thread_ka.join(); 224 | */ 225 | log_d("*** response length: %d", response.length()); 226 | if (deviceConnected && (response.length() > 0)) { 227 | if (response.length() <= ATT_MTU) { 228 | pU2fStatus->setValue(response); 229 | pU2fStatus->notify(); 230 | } 231 | else { // framing 232 | int16_t LEN = response.length(); 233 | uint8_t framing_index = 0; 234 | int16_t start = 0; 235 | int16_t end = ATT_MTU; 236 | pU2fStatus->setValue(response.substr(start, end)); 237 | pU2fStatus->notify(); 238 | while (end < LEN) { 239 | start = end; 240 | end += ATT_MTU-1; 241 | end = min(LEN, end); 242 | std::string str_index = std::string(1, framing_index); 243 | pU2fStatus->setValue(str_index + response.substr(start, ATT_MTU-1)); 244 | pU2fStatus->notify(); 245 | framing_index += 1; 246 | } 247 | } 248 | } 249 | status = Idle; 250 | } 251 | -------------------------------------------------------------------------------- /framing.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "esp32-hal-log.h" 3 | #include 4 | 5 | #include "framing.h" 6 | #include "u2f.h" 7 | 8 | #define ERR_INVALID_CMD 0x01 // The command in the request is unknown/invalid 9 | #define ERR_INVALID_PAR 0x02 // The parameter(s) of the command is/are invalid or missing 10 | #define ERR_INVALID_LEN 0x03 // The length of the request is invalid 11 | #define ERR_INVALID_SEQ 0x04 // The sequence number is invalid 12 | #define ERR_REQ_TIMEOUT 0x05 // The request timed out 13 | #define ERR_OTHER 0x7f // Other, unspecified error 14 | 15 | #define CMD_PING 0x81 16 | #define CMD_MSG 0x83 17 | #define CMD_ERROR 0xbf 18 | 19 | #define STATUS_EMPTY 0 20 | #define STATUS_FRAMING 1 21 | 22 | 23 | std::string data; 24 | static int16_t missing_data; 25 | static uint8_t CMD; 26 | static uint8_t status = STATUS_EMPTY; 27 | static uint8_t expected_frame_sequence; 28 | 29 | 30 | void set_error(uint8_t error) { 31 | response = "01234"; 32 | response[0] = CMD_ERROR; 33 | response[1] = 0; 34 | response[2] = 1; 35 | response[3] = error; 36 | } 37 | 38 | void processCMD() { 39 | log_d("*** Process command: %02x", CMD); 40 | if (CMD == CMD_PING) { 41 | response = "012"; 42 | response[0] = CMD; 43 | response[1] = (data.length() >> 8); 44 | response[2] = data.length(); 45 | response += data; 46 | } 47 | else if (CMD == CMD_MSG) { 48 | // U2F 49 | std::string response_u2f = u2f_process(data); 50 | response = "012"; 51 | response[0] = CMD; 52 | response[1] = response_u2f.length() >> 8; 53 | response[2] = response_u2f.length(); 54 | response += response_u2f; 55 | //log_d("*** Response:"); 56 | //for (int i=0; i LEN+3) { 77 | set_error(ERR_INVALID_LEN); 78 | return UPDATE_ERROR; 79 | } 80 | else { 81 | CMD = value[0]; 82 | data = value.substr(3, value_len); 83 | missing_data = LEN - (value_len-3); 84 | if (missing_data == 0) { 85 | return UPDATE_SUCCESS; 86 | } 87 | else { 88 | status = STATUS_FRAMING; 89 | expected_frame_sequence = 0; 90 | return UPDATE_FRAMING; 91 | } 92 | } 93 | } 94 | } 95 | else if ((status == STATUS_FRAMING) && (value_len > 0)) { 96 | if (value[0] != expected_frame_sequence) { 97 | status = STATUS_EMPTY; 98 | set_error(ERR_INVALID_SEQ); 99 | return UPDATE_ERROR; 100 | } else if (missing_data < value_len-1) { 101 | status = STATUS_EMPTY; 102 | set_error(ERR_INVALID_LEN); 103 | return UPDATE_ERROR; 104 | } 105 | else { 106 | data += value.substr(1, value_len); 107 | missing_data -= value_len-1; 108 | if (missing_data == 0) { 109 | status = STATUS_EMPTY; 110 | return UPDATE_SUCCESS; 111 | } 112 | else { 113 | if (expected_frame_sequence == 0xff) { 114 | status = STATUS_EMPTY; 115 | set_error(ERR_INVALID_SEQ); 116 | return UPDATE_ERROR; 117 | } 118 | else { 119 | expected_frame_sequence += 1; 120 | return UPDATE_FRAMING; 121 | } 122 | } 123 | } 124 | } 125 | status = STATUS_EMPTY; 126 | set_error(ERR_OTHER); 127 | return UPDATE_ERROR; 128 | } 129 | -------------------------------------------------------------------------------- /framing.h: -------------------------------------------------------------------------------- 1 | #ifndef __FRAMING_H_INCLUDED__ 2 | #define __FRAMING_H_INCLUDED__ 3 | 4 | #include 5 | 6 | #define ATT_MTU 20 7 | 8 | #define UPDATE_SUCCESS 0 9 | #define UPDATE_ERROR 1 10 | #define UPDATE_FRAMING 2 11 | 12 | enum processingStatus {Idle, IsProcessing, CmdReady, ResultReady}; 13 | 14 | extern std::string response; 15 | 16 | uint8_t update(std::string value); 17 | void processCMD(); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /hmac.cpp: -------------------------------------------------------------------------------- 1 | #include "mbedtls/sha256.h" 2 | 3 | #define BLOCKSIZE 64 4 | 5 | void hmac_sha256(const unsigned char *key_5c, const unsigned char *key_36, const int n, const unsigned char *message, unsigned char *output) { 6 | unsigned char h1[32]; 7 | mbedtls_sha256_context ctx; 8 | 9 | mbedtls_sha256_init(&ctx); 10 | 11 | mbedtls_sha256_starts_ret(&ctx, 0); 12 | mbedtls_sha256_update_ret(&ctx, key_36, BLOCKSIZE); 13 | mbedtls_sha256_update_ret(&ctx, message, n); 14 | mbedtls_sha256_finish_ret(&ctx, h1); 15 | 16 | mbedtls_sha256_starts_ret(&ctx, 0); 17 | mbedtls_sha256_update_ret(&ctx, key_5c, BLOCKSIZE); 18 | mbedtls_sha256_update_ret(&ctx, h1, sizeof(h1)); 19 | mbedtls_sha256_finish_ret(&ctx, output); 20 | 21 | mbedtls_sha256_free(&ctx); 22 | } 23 | -------------------------------------------------------------------------------- /hmac.h: -------------------------------------------------------------------------------- 1 | #ifndef __HMAC_H_INCLUDED__ 2 | #define __HMAC_H_INCLUDED__ 3 | 4 | void hmac_sha256(const unsigned char *key_5c, const unsigned char *key_36, const int n, const unsigned char *message, unsigned char *output); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /u2f.cpp: -------------------------------------------------------------------------------- 1 | #include "u2f.h" 2 | #include "mbedtls/md.h" 3 | #include "mbedtls/sha256.h" 4 | #include "mbedtls/ecp.h" 5 | #include "mbedtls/ecdsa.h" 6 | #include "mbedtls/aes.h" 7 | #include 8 | #include "esp32-hal-log.h" 9 | #include "esp32-hal.h" 10 | #include "hmac.h" 11 | 12 | bool user_presence_check(); 13 | 14 | static uint8_t CERTIFICATE_DER[] = { 0x30, 0x82, 0x01, 0x6d, 0x30, 0x82, 0x01, 0x12, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0xad, 0x16, 0x25, 0x76, 0x3d, 0x31, 0xe2, 0xf0, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x11, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x06, 0x46, 0x69, 0x64, 0x6f, 0x42, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x39, 0x30, 0x33, 0x30, 0x37, 0x31, 0x34, 0x33, 0x32, 0x35, 0x36, 0x5a, 0x17, 0x0d, 0x34, 0x39, 0x30, 0x34, 0x31, 0x38, 0x31, 0x34, 0x33, 0x32, 0x35, 0x36, 0x5a, 0x30, 0x11, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x06, 0x46, 0x69, 0x64, 0x6f, 0x42, 0x74, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xcb, 0xf8, 0x47, 0xb3, 0x96, 0xe6, 0xa0, 0x2d, 0xe3, 0xbe, 0xb5, 0x47, 0xa7, 0xa3, 0xd6, 0x54, 0x02, 0x33, 0xa3, 0x96, 0x85, 0x2c, 0x01, 0x40, 0x72, 0x2a, 0x59, 0x27, 0x94, 0x34, 0x6c, 0x3d, 0xae, 0x66, 0x76, 0xa2, 0x78, 0x83, 0x35, 0x88, 0x3d, 0xcb, 0x92, 0x28, 0x0b, 0xc6, 0xbf, 0xeb, 0xd8, 0xca, 0x05, 0x5c, 0x0e, 0x23, 0x96, 0x9d, 0x2c, 0x30, 0x53, 0xd2, 0xf5, 0x1a, 0xea, 0xb4, 0xa3, 0x53, 0x30, 0x51, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xe7, 0x53, 0x25, 0x43, 0x89, 0xb4, 0x9b, 0x5c, 0x11, 0x5c, 0xb2, 0x1d, 0xc9, 0x31, 0x78, 0x40, 0x94, 0x2d, 0xe0, 0x3d, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe7, 0x53, 0x25, 0x43, 0x89, 0xb4, 0x9b, 0x5c, 0x11, 0x5c, 0xb2, 0x1d, 0xc9, 0x31, 0x78, 0x40, 0x94, 0x2d, 0xe0, 0x3d, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x49, 0x00, 0x30, 0x46, 0x02, 0x21, 0x00, 0xb1, 0x2f, 0x34, 0x09, 0xe4, 0x01, 0xe7, 0xf1, 0x54, 0xfd, 0x7e, 0x6d, 0x30, 0x97, 0xb6, 0x38, 0x56, 0x30, 0xc9, 0xe7, 0x36, 0x43, 0xbd, 0xfe, 0x4c, 0x01, 0x21, 0xbe, 0xc2, 0x5f, 0xa5, 0x5a, 0x02, 0x21, 0x00, 0x84, 0x84, 0x7d, 0xb6, 0xe9, 0x9d, 0x1a, 0x9e, 0xf9, 0x72, 0xe6, 0x29, 0x61, 0x95, 0xa1, 0x65, 0x8d, 0x76, 0x16, 0xec, 0x3c, 0x9c, 0x35, 0x4b, 0x56, 0x18, 0x85, 0xd0, 0xce, 0x6a, 0x71, 0x43 }; 15 | static uint8_t PRIVATE_KEY[] = { 0x0c, 0xd6, 0xa2, 0x6e, 0x95, 0x25, 0xd2, 0xc1, 0x8d, 0x5d, 0x3e, 0x32, 0xf1, 0xd5, 0x6e, 0xca, 0x1f, 0x30, 0xaf, 0x68, 0x7d, 0x18, 0x53, 0x42, 0xf5, 0xac, 0x4f, 0x38, 0x71, 0x2c, 0x9d, 0xe3 }; 16 | 17 | static uint8_t AES_KEY[] = { 172, 195, 189, 108, 184, 127, 239, 135, 167, 36, 71, 68, 84, 105, 247, 156 }; 18 | static uint8_t RND_IV[] = { 183, 245, 227, 170, 48, 66, 75, 19, 85, 196, 74, 48, 255, 211, 250, 216 }; 19 | static uint8_t KEY_5C[] = { 15, 162, 168, 118, 195, 97, 141, 230, 134, 145, 234, 248, 175, 72, 58, 123, 240, 136, 197, 3, 52, 46, 23, 222, 146, 98, 51, 62, 5, 53, 69, 214, 201, 51, 209, 158, 176, 73, 111, 219, 123, 131, 42, 136, 48, 230, 29, 93, 105, 87, 173, 24, 223, 203, 138, 104, 240, 113, 250, 207, 202, 113, 118, 30}; 20 | static uint8_t KEY_36[] = { 101, 200, 194, 28, 169, 11, 231, 140, 236, 251, 128, 146, 197, 34, 80, 17, 154, 226, 175, 105, 94, 68, 125, 180, 248, 8, 89, 84, 111, 95, 47, 188, 163, 89, 187, 244, 218, 35, 5, 177, 17, 233, 64, 226, 90, 140, 119, 55, 3, 61, 199, 114, 181, 161, 224, 2, 154, 27, 144, 165, 160, 27, 28, 116}; 21 | 22 | static uint32_t COUNTER = 0; 23 | 24 | #define U2F_REGISTER_INS 0x01 25 | #define U2F_AUTHENTICATE 0x02 26 | #define U2F_VERSION 0x03 27 | 28 | static uint8_t SW_NO_ERROR[] = {0x90, 0x00}; // The command completed successfully without error. 29 | static uint8_t SW_CONDITIONS_NOT_SATISFIED[] = {0x69, 0x85}; // The request was rejected due to test-of-user-presence being required. 30 | static uint8_t SW_WRONG_DATA[] = {0x6A, 0x80}; // The request was rejected due to an invalid key handle. 31 | static uint8_t SW_WRONG_LENGTH[] = {0x67, 0x00}; // The length of the request was invalid. 32 | static uint8_t SW_CLA_NOT_SUPPORTED[] = {0x6E, 0x00}; // The Class uint8_t of the request is not supported. 33 | static uint8_t SW_INS_NOT_SUPPORTED[] = {0x6D, 0x00}; // The Instruction of the request is not supported. 34 | static uint8_t SW_COMMAND_ABORTED[] = {0x6F, 0x00}; // operating system error) 35 | 36 | #define BLOCKSIZE 16 37 | #define KEYLENGTH 32 38 | #define HASHSIZE 32 39 | #define HMACSIZE 32 40 | #define KEY_HANDLE_LENGTH (KEYLENGTH+HMACSIZE) 41 | 42 | 43 | void log_buffer(const int n, const unsigned char*buffer) { 44 | char s[1024]; 45 | char hex[] = "0123456789ABCDEF"; 46 | 47 | for (int i=0; i> 4]; 49 | s[2*i+1] = hex[buffer[i] & 0x0f]; 50 | } 51 | s[2*n] = 0; 52 | log_d("%s", s); 53 | } 54 | 55 | 56 | uint8_t signature_to_asn1(const mbedtls_mpi *r, const mbedtls_mpi *s, unsigned char *signature) { 57 | uint8_t len_r, len_s; 58 | 59 | len_r = (mbedtls_mpi_bitlen(r) + 8) / 8; 60 | len_s = (mbedtls_mpi_bitlen(s) + 8) / 8; 61 | signature[0] = 0x30; 62 | signature[1] = len_r + len_s + 4; 63 | signature[2] = 0x02; 64 | signature[3] = len_r; 65 | mbedtls_mpi_write_binary(r, signature+4, len_r); 66 | signature[4+len_r] = 0x02; 67 | signature[5+len_r] = len_s; 68 | mbedtls_mpi_write_binary(s, signature+6+len_r, len_s); 69 | return 6+len_r+len_s; 70 | } 71 | 72 | 73 | int f_rng(void *ptr, unsigned char *buffer, size_t n) { 74 | size_t i; 75 | uint32_t r; 76 | uint8_t *rp = (uint8_t*)&r; 77 | for (i=0; i> 24); 281 | counter_big[1] = (uint8_t)(COUNTER >> 16); 282 | counter_big[2] = (uint8_t)(COUNTER >> 8); 283 | counter_big[3] = (uint8_t)(COUNTER); 284 | 285 | // Bit 0 indicates whether user presence was verified. If Bit 0 is is to 1, then user presence was verified. 286 | 287 | // Signature generation 288 | // 1. hash values 289 | uint8_t hash[HASHSIZE]; 290 | mbedtls_sha256_context ctx; 291 | mbedtls_sha256_init(&ctx); 292 | mbedtls_sha256_starts_ret(&ctx, 0); 293 | mbedtls_sha256_update_ret(&ctx, application, HASHSIZE); 294 | mbedtls_sha256_update_ret(&ctx, &user_presence, 1); 295 | mbedtls_sha256_update_ret(&ctx, counter_big, 4); 296 | mbedtls_sha256_update_ret(&ctx, challenge, HASHSIZE); 297 | mbedtls_sha256_finish_ret(&ctx, hash); 298 | mbedtls_sha256_free(&ctx); 299 | 300 | // 2. compute ECDSA 301 | mbedtls_ecp_group grp; 302 | mbedtls_mpi d; 303 | 304 | mbedtls_ecp_group_init( &grp ); 305 | mbedtls_ecp_group_load( &grp, MBEDTLS_ECP_DP_SECP256R1 ); 306 | mbedtls_mpi_init( &d ); 307 | mbedtls_mpi_read_binary(&d, output, KEYLENGTH); 308 | 309 | mbedtls_mpi r, s; 310 | mbedtls_mpi_init( &r ); 311 | mbedtls_mpi_init( &s ); 312 | mbedtls_ecdsa_sign( &grp, &r, &s, &d, hash, sizeof(hash), &f_rng, 0); 313 | mbedtls_ecp_group_free( &grp ); 314 | 315 | uint8_t signature[72]; 316 | uint8_t len_signature; 317 | len_signature = signature_to_asn1(&r, &s, signature); 318 | 319 | mbedtls_mpi_free(&d); 320 | mbedtls_mpi_free(&r); 321 | mbedtls_mpi_free(&s); 322 | 323 | // write response 324 | uint16_t i, offset; 325 | std::string response = std::string(1+4+len_signature+sizeof(SW_NO_ERROR), 0); 326 | response[0] = user_presence; 327 | offset = 1; 328 | for (i=0; i<4; i++, offset++) 329 | response[offset] = ((uint8_t *)(&COUNTER))[3-i]; // ESP32 is little endian 330 | for (i=0; i 5 | 6 | std::string u2f_process(std::string data); 7 | 8 | #endif 9 | --------------------------------------------------------------------------------