mirror of
https://github.com/Proxmark/proxmark3.git
synced 2025-08-19 12:59:44 -07:00
FIDO U2F NFC authenticators (#697)
* `hf fido` command * detects FIDO tag * add new commands for fido u2f * added changelog * added fido2 info
This commit is contained in:
parent
8fa6838476
commit
39cc1c879e
14 changed files with 704 additions and 35 deletions
|
@ -10,7 +10,9 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
- Added `hf emv scan` - commands for scan EMV card and dump data to json file (Merlok)
|
||||||
- `hf mfp` group of commands (Merlok)
|
- `hf mfp` group of commands (Merlok)
|
||||||
|
- Added `hf fido` - FIDO U2F authenticator commands https://fidoalliance.org/ (Merlok)
|
||||||
|
|
||||||
## [v3.1.0][2018-10-10]
|
## [v3.1.0][2018-10-10]
|
||||||
|
|
||||||
|
|
|
@ -116,7 +116,7 @@ int EPA_APDU(uint8_t *apdu, size_t length, uint8_t *response)
|
||||||
switch(iso_type)
|
switch(iso_type)
|
||||||
{
|
{
|
||||||
case 'a':
|
case 'a':
|
||||||
return iso14_apdu(apdu, (uint16_t) length, response);
|
return iso14_apdu(apdu, (uint16_t) length, response, NULL);
|
||||||
break;
|
break;
|
||||||
case 'b':
|
case 'b':
|
||||||
return iso14443b_apdu(apdu, length, response);
|
return iso14443b_apdu(apdu, length, response);
|
||||||
|
|
|
@ -1935,15 +1935,21 @@ b8 b7 b6 b5 b4 b3 b2 b1
|
||||||
b5,b6 = 00 - DESELECT
|
b5,b6 = 00 - DESELECT
|
||||||
11 - WTX
|
11 - WTX
|
||||||
*/
|
*/
|
||||||
int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data) {
|
int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data, uint8_t *res) {
|
||||||
uint8_t parity[MAX_PARITY_SIZE];
|
uint8_t parity[MAX_PARITY_SIZE];
|
||||||
uint8_t real_cmd[cmd_len + 4];
|
uint8_t real_cmd[cmd_len + 4];
|
||||||
|
|
||||||
// ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02
|
if (cmd_len) {
|
||||||
real_cmd[0] = 0x02; // bnr,nad,cid,chn=0; i-block(0x00)
|
// ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02
|
||||||
// put block number into the PCB
|
real_cmd[0] = 0x02; // bnr,nad,cid,chn=0; i-block(0x00)
|
||||||
real_cmd[0] |= iso14_pcb_blocknum;
|
// put block number into the PCB
|
||||||
memcpy(real_cmd + 1, cmd, cmd_len);
|
real_cmd[0] |= iso14_pcb_blocknum;
|
||||||
|
memcpy(real_cmd + 1, cmd, cmd_len);
|
||||||
|
} else {
|
||||||
|
// R-block. ACK
|
||||||
|
real_cmd[0] = 0xA2; // r-block + ACK
|
||||||
|
real_cmd[0] |= iso14_pcb_blocknum;
|
||||||
|
}
|
||||||
AppendCrc14443a(real_cmd, cmd_len + 1);
|
AppendCrc14443a(real_cmd, cmd_len + 1);
|
||||||
|
|
||||||
ReaderTransmit(real_cmd, cmd_len + 3, NULL);
|
ReaderTransmit(real_cmd, cmd_len + 3, NULL);
|
||||||
|
@ -1982,9 +1988,13 @@ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data) {
|
||||||
{
|
{
|
||||||
iso14_pcb_blocknum ^= 1;
|
iso14_pcb_blocknum ^= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if we received I-block with chaining we need to send ACK and receive another block of data
|
||||||
|
if (res)
|
||||||
|
*res = data_bytes[0];
|
||||||
|
|
||||||
// crc check
|
// crc check
|
||||||
if (len >=3 && !CheckCrc14443(CRC_14443_A, data_bytes, len)) {
|
if (len >= 3 && !CheckCrc14443(CRC_14443_A, data_bytes, len)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2050,9 +2060,10 @@ void ReaderIso14443a(UsbCommand *c)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(param & ISO14A_APDU && !cantSELECT) {
|
if(param & ISO14A_APDU && !cantSELECT) {
|
||||||
arg0 = iso14_apdu(cmd, len, buf);
|
uint8_t res;
|
||||||
|
arg0 = iso14_apdu(cmd, len, buf, &res);
|
||||||
LED_B_ON();
|
LED_B_ON();
|
||||||
cmd_send(CMD_ACK, arg0, 0, 0, buf, sizeof(buf));
|
cmd_send(CMD_ACK, arg0, res, 0, buf, sizeof(buf));
|
||||||
LED_B_OFF();
|
LED_B_OFF();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ extern int EmSendPrecompiledCmd(tag_response_info_t *response_info);
|
||||||
extern bool prepare_allocated_tag_modulation(tag_response_info_t *response_info, uint8_t **buffer, size_t *buffer_size);
|
extern bool prepare_allocated_tag_modulation(tag_response_info_t *response_info, uint8_t **buffer, size_t *buffer_size);
|
||||||
|
|
||||||
extern void iso14443a_setup(uint8_t fpga_minor_mode);
|
extern void iso14443a_setup(uint8_t fpga_minor_mode);
|
||||||
extern int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data);
|
extern int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data, uint8_t *res);
|
||||||
extern int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *resp_data, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats);
|
extern int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *resp_data, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats);
|
||||||
extern void iso14a_set_trigger(bool enable);
|
extern void iso14a_set_trigger(bool enable);
|
||||||
extern void iso14a_set_timeout(uint32_t timeout);
|
extern void iso14a_set_timeout(uint32_t timeout);
|
||||||
|
|
|
@ -164,6 +164,7 @@ CMDSRCS = $(SRC_SMARTCARD) \
|
||||||
cmdhfmfhard.c \
|
cmdhfmfhard.c \
|
||||||
hardnested/hardnested_bruteforce.c \
|
hardnested/hardnested_bruteforce.c \
|
||||||
cmdhftopaz.c \
|
cmdhftopaz.c \
|
||||||
|
cmdhffido.c \
|
||||||
cmdhw.c \
|
cmdhw.c \
|
||||||
cmdlf.c \
|
cmdlf.c \
|
||||||
cmdlfawid.c \
|
cmdlfawid.c \
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include "protocols.h"
|
#include "protocols.h"
|
||||||
#include "emv/cmdemv.h"
|
#include "emv/cmdemv.h"
|
||||||
#include "cmdhflist.h"
|
#include "cmdhflist.h"
|
||||||
|
#include "cmdhffido.h"
|
||||||
|
|
||||||
static int CmdHelp(const char *Cmd);
|
static int CmdHelp(const char *Cmd);
|
||||||
|
|
||||||
|
@ -598,6 +599,7 @@ static command_t CommandTable[] =
|
||||||
{"mfu", CmdHFMFUltra, 1, "{ MIFARE Ultralight RFIDs... }"},
|
{"mfu", CmdHFMFUltra, 1, "{ MIFARE Ultralight RFIDs... }"},
|
||||||
{"mfp", CmdHFMFP, 1, "{ MIFARE Plus RFIDs... }"},
|
{"mfp", CmdHFMFP, 1, "{ MIFARE Plus RFIDs... }"},
|
||||||
{"topaz", CmdHFTopaz, 1, "{ TOPAZ (NFC Type 1) RFIDs... }"},
|
{"topaz", CmdHFTopaz, 1, "{ TOPAZ (NFC Type 1) RFIDs... }"},
|
||||||
|
{"fido", CmdHFFido, 1, "{ FIDO and FIDO2 authenticators... }"},
|
||||||
{"tune", CmdHFTune, 0, "Continuously measure HF antenna tuning"},
|
{"tune", CmdHFTune, 0, "Continuously measure HF antenna tuning"},
|
||||||
{"list", CmdHFList, 1, "List protocol data in trace buffer"},
|
{"list", CmdHFList, 1, "List protocol data in trace buffer"},
|
||||||
{"search", CmdHFSearch, 1, "Search for known HF tags [preliminary]"},
|
{"search", CmdHFSearch, 1, "Search for known HF tags [preliminary]"},
|
||||||
|
|
|
@ -786,20 +786,20 @@ int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leav
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) {
|
int CmdExchangeAPDU(uint8_t *datain, int datainlen, bool activateField, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, bool *chaining) {
|
||||||
uint16_t cmdc = 0;
|
uint16_t cmdc = 0;
|
||||||
|
|
||||||
|
*chaining = false;
|
||||||
|
|
||||||
if (activateField) {
|
if (activateField) {
|
||||||
cmdc |= ISO14A_CONNECT | ISO14A_CLEAR_TRACE;
|
cmdc |= ISO14A_CONNECT | ISO14A_CLEAR_TRACE;
|
||||||
}
|
}
|
||||||
if (leaveSignalON)
|
|
||||||
cmdc |= ISO14A_NO_DISCONNECT;
|
|
||||||
|
|
||||||
// "Command APDU" length should be 5+255+1, but javacard's APDU buffer might be smaller - 133 bytes
|
// "Command APDU" length should be 5+255+1, but javacard's APDU buffer might be smaller - 133 bytes
|
||||||
// https://stackoverflow.com/questions/32994936/safe-max-java-card-apdu-data-command-and-respond-size
|
// https://stackoverflow.com/questions/32994936/safe-max-java-card-apdu-data-command-and-respond-size
|
||||||
// here length USB_CMD_DATA_SIZE=512
|
// here length USB_CMD_DATA_SIZE=512
|
||||||
// timeout must be authomatically set by "get ATS"
|
// timeout must be authomatically set by "get ATS"
|
||||||
UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_APDU | cmdc, (datainlen & 0xFFFF), 0}};
|
UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_APDU | ISO14A_NO_DISCONNECT | cmdc, (datainlen & 0xFFFF), 0}};
|
||||||
memcpy(c.d.asBytes, datain, datainlen);
|
memcpy(c.d.asBytes, datain, datainlen);
|
||||||
SendCommand(&c);
|
SendCommand(&c);
|
||||||
|
|
||||||
|
@ -813,6 +813,7 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea
|
||||||
}
|
}
|
||||||
if (resp.arg[0] != 1) {
|
if (resp.arg[0] != 1) {
|
||||||
PrintAndLog("APDU ERROR: Proxmark error %d.", resp.arg[0]);
|
PrintAndLog("APDU ERROR: Proxmark error %d.", resp.arg[0]);
|
||||||
|
DropField();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -820,45 +821,76 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea
|
||||||
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
|
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
|
||||||
recv = resp.d.asBytes;
|
recv = resp.d.asBytes;
|
||||||
int iLen = resp.arg[0];
|
int iLen = resp.arg[0];
|
||||||
|
uint8_t res = resp.arg[1];
|
||||||
|
|
||||||
*dataoutlen = iLen - 2;
|
int dlen = iLen - 2;
|
||||||
if (*dataoutlen < 0)
|
if (dlen < 0)
|
||||||
*dataoutlen = 0;
|
dlen = 0;
|
||||||
|
*dataoutlen += dlen;
|
||||||
|
|
||||||
if (maxdataoutlen && *dataoutlen > maxdataoutlen) {
|
if (maxdataoutlen && *dataoutlen > maxdataoutlen) {
|
||||||
PrintAndLog("APDU ERROR: Buffer too small(%d). Needs %d bytes", *dataoutlen, maxdataoutlen);
|
PrintAndLog("APDU ERROR: Buffer too small(%d). Needs %d bytes", *dataoutlen, maxdataoutlen);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(dataout, recv, *dataoutlen);
|
|
||||||
|
|
||||||
if(!iLen) {
|
if(!iLen) {
|
||||||
PrintAndLog("APDU ERROR: No APDU response.");
|
PrintAndLog("APDU ERROR: No APDU response.");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check apdu length
|
||||||
|
if (iLen < 4 && iLen >= 0) {
|
||||||
|
PrintAndLog("APDU ERROR: Small APDU response. Len=%d", iLen);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
// check block TODO
|
// check block TODO
|
||||||
if (iLen == -2) {
|
if (iLen == -2) {
|
||||||
PrintAndLog("APDU ERROR: Block type mismatch.");
|
PrintAndLog("APDU ERROR: Block type mismatch.");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memcpy(dataout, recv, dlen);
|
||||||
|
|
||||||
|
// chaining
|
||||||
|
if ((res & 0x10) != 0) {
|
||||||
|
*chaining = true;
|
||||||
|
}
|
||||||
|
|
||||||
// CRC Check
|
// CRC Check
|
||||||
if (iLen == -1) {
|
if (iLen == -1) {
|
||||||
PrintAndLog("APDU ERROR: ISO 14443A CRC error.");
|
PrintAndLog("APDU ERROR: ISO 14443A CRC error.");
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check apdu length
|
|
||||||
if (iLen < 4) {
|
|
||||||
PrintAndLog("APDU ERROR: Small APDU response. Len=%d", iLen);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
PrintAndLog("APDU ERROR: Reply timeout.");
|
PrintAndLog("APDU ERROR: Reply timeout.");
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) {
|
||||||
|
*dataoutlen = 0;
|
||||||
|
bool chaining = false;
|
||||||
|
|
||||||
|
int res = CmdExchangeAPDU(datain, datainlen, activateField, dataout, maxdataoutlen, dataoutlen, &chaining);
|
||||||
|
|
||||||
|
while (chaining) {
|
||||||
|
// I-block with chaining
|
||||||
|
res = CmdExchangeAPDU(NULL, 0, false, &dataout[*dataoutlen], maxdataoutlen, dataoutlen, &chaining);
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
if (!leaveSignalON)
|
||||||
|
DropField();
|
||||||
|
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!leaveSignalON)
|
||||||
|
DropField();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
550
client/cmdhffido.c
Normal file
550
client/cmdhffido.c
Normal file
|
@ -0,0 +1,550 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (C) 2018 Merlok
|
||||||
|
//
|
||||||
|
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
|
||||||
|
// at your option, any later version. See the LICENSE.txt file for the text of
|
||||||
|
// the license.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// High frequency MIFARE Plus commands
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Documentation here:
|
||||||
|
//
|
||||||
|
// FIDO Alliance specifications
|
||||||
|
// https://fidoalliance.org/download/
|
||||||
|
// FIDO NFC Protocol Specification v1.0
|
||||||
|
// https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-nfc-protocol-v1.2-ps-20170411.html
|
||||||
|
// FIDO U2F Raw Message Formats
|
||||||
|
// https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
#include "cmdhffido.h"
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <jansson.h>
|
||||||
|
#include "comms.h"
|
||||||
|
#include "cmdmain.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "ui.h"
|
||||||
|
#include "proxmark3.h"
|
||||||
|
#include "cmdhf14a.h"
|
||||||
|
#include "mifare.h"
|
||||||
|
#include "emv/emvcore.h"
|
||||||
|
#include "emv/emvjson.h"
|
||||||
|
#include "emv/dump.h"
|
||||||
|
#include "cliparser/cliparser.h"
|
||||||
|
|
||||||
|
static int CmdHelp(const char *Cmd);
|
||||||
|
|
||||||
|
int FIDOSelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
|
||||||
|
uint8_t data[] = {0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01};
|
||||||
|
|
||||||
|
return EMVSelect(ActivateField, LeaveFieldON, data, sizeof(data), Result, MaxResultLen, ResultLen, sw, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FIDOExchange(sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
|
||||||
|
int res = EMVExchange(true, apdu, Result, MaxResultLen, ResultLen, sw, NULL);
|
||||||
|
if (res == 5) // apdu result (sw) not a 0x9000
|
||||||
|
res = 0;
|
||||||
|
// software chaining
|
||||||
|
while (!res && (*sw >> 8) == 0x61) {
|
||||||
|
size_t oldlen = *ResultLen;
|
||||||
|
res = EMVExchange(true, (sAPDU){0x00, 0xC0, 0x00, 0x00, 0x00, NULL}, &Result[oldlen], MaxResultLen - oldlen, ResultLen, sw, NULL);
|
||||||
|
if (res == 5) // apdu result (sw) not a 0x9000
|
||||||
|
res = 0;
|
||||||
|
|
||||||
|
*ResultLen += oldlen;
|
||||||
|
if (*ResultLen > MaxResultLen)
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FIDORegister(uint8_t *params, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
|
||||||
|
return FIDOExchange((sAPDU){0x00, 0x01, 0x03, 0x00, 64, params}, Result, MaxResultLen, ResultLen, sw);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FIDOAuthentication(uint8_t *params, uint8_t paramslen, uint8_t controlb, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
|
||||||
|
return FIDOExchange((sAPDU){0x00, 0x02, controlb, 0x00, paramslen, params}, Result, MaxResultLen, ResultLen, sw);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FIDO2GetInfo(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
|
||||||
|
uint8_t data[] = {0x04};
|
||||||
|
return FIDOExchange((sAPDU){0x80, 0x10, 0x00, 0x00, sizeof(data), data}, Result, MaxResultLen, ResultLen, sw);
|
||||||
|
}
|
||||||
|
|
||||||
|
int CmdHFFidoInfo(const char *cmd) {
|
||||||
|
|
||||||
|
if (cmd && strlen(cmd) > 0)
|
||||||
|
PrintAndLog("WARNING: command don't have any parameters.\n");
|
||||||
|
|
||||||
|
// info about 14a part
|
||||||
|
CmdHF14AInfo("");
|
||||||
|
|
||||||
|
// FIDO info
|
||||||
|
PrintAndLog("--------------------------------------------");
|
||||||
|
SetAPDULogging(false);
|
||||||
|
|
||||||
|
uint8_t buf[APDU_RES_LEN] = {0};
|
||||||
|
size_t len = 0;
|
||||||
|
uint16_t sw = 0;
|
||||||
|
int res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
DropField();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sw != 0x9000) {
|
||||||
|
if (sw)
|
||||||
|
PrintAndLog("Not a FIDO card! APDU response: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
|
||||||
|
else
|
||||||
|
PrintAndLog("APDU exchange error. Card returns 0x0000.");
|
||||||
|
|
||||||
|
DropField();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strncmp((char *)buf, "U2F_V2", 7)) {
|
||||||
|
if (!strncmp((char *)buf, "FIDO_2_0", 8)) {
|
||||||
|
PrintAndLog("FIDO2 authenricator detected. Version: %.*s", len, buf);
|
||||||
|
} else {
|
||||||
|
PrintAndLog("FIDO authenricator detected (not standard U2F).");
|
||||||
|
PrintAndLog("Non U2F authenticator version:");
|
||||||
|
dump_buffer((const unsigned char *)buf, len, NULL, 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PrintAndLog("FIDO U2F authenricator detected. Version: %.*s", len, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
res = FIDO2GetInfo(buf, sizeof(buf), &len, &sw);
|
||||||
|
DropField();
|
||||||
|
if (res) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if (sw != 0x9000) {
|
||||||
|
PrintAndLog("FIDO2 version not exists (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintAndLog("FIDO2 version: (%d)", len);
|
||||||
|
dump_buffer((const unsigned char *)buf, len, NULL, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_t *OpenJson(int paramnum, char *fname, void* argtable[], bool *err) {
|
||||||
|
json_t *root = NULL;
|
||||||
|
json_error_t error;
|
||||||
|
*err = false;
|
||||||
|
|
||||||
|
uint8_t jsonname[250] ={0};
|
||||||
|
char *cjsonname = (char *)jsonname;
|
||||||
|
int jsonnamelen = 0;
|
||||||
|
|
||||||
|
// CLIGetStrWithReturn(paramnum, jsonname, &jsonnamelen);
|
||||||
|
if (CLIParamStrToBuf(arg_get_str(paramnum), jsonname, sizeof(jsonname), &jsonnamelen)) {
|
||||||
|
CLIParserFree();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// current path + file name
|
||||||
|
if (!strstr(cjsonname, ".json"))
|
||||||
|
strcat(cjsonname, ".json");
|
||||||
|
|
||||||
|
if (jsonnamelen) {
|
||||||
|
strcpy(fname, get_my_executable_directory());
|
||||||
|
strcat(fname, cjsonname);
|
||||||
|
if (access(fname, F_OK) != -1) {
|
||||||
|
root = json_load_file(fname, 0, &error);
|
||||||
|
if (!root) {
|
||||||
|
PrintAndLog("ERROR: json error on line %d: %s", error.line, error.text);
|
||||||
|
*err = true;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!json_is_object(root)) {
|
||||||
|
PrintAndLog("ERROR: Invalid json format. root must be an object.");
|
||||||
|
json_decref(root);
|
||||||
|
*err = true;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
root = json_object();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CmdHFFidoRegister(const char *cmd) {
|
||||||
|
uint8_t data[64] = {0};
|
||||||
|
int chlen = 0;
|
||||||
|
uint8_t cdata[250] = {0};
|
||||||
|
int applen = 0;
|
||||||
|
uint8_t adata[250] = {0};
|
||||||
|
json_t *root = NULL;
|
||||||
|
|
||||||
|
CLIParserInit("hf fido reg",
|
||||||
|
"Initiate a U2F token registration. Needs two 32-byte hash number. \nchallenge parameter (32b) and application parameter (32b).",
|
||||||
|
"Usage:\n\thf fido reg -> execute command with 2 parameters, filled 0x00\n"
|
||||||
|
"\thf fido reg 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters"
|
||||||
|
"\thf fido reg -p s0 s1 -> execute command with plain parameters");
|
||||||
|
|
||||||
|
void* argtable[] = {
|
||||||
|
arg_param_begin,
|
||||||
|
arg_lit0("aA", "apdu", "show APDU reqests and responses"),
|
||||||
|
arg_lit0("vV", "verbose", "show technical data"),
|
||||||
|
arg_lit0("pP", "plain", "send plain ASCII to challenge and application parameters instead of HEX"),
|
||||||
|
arg_str0("jJ", "json", "fido.json", "JSON input / output file name for parameters."),
|
||||||
|
arg_str0(NULL, NULL, "<HEX/ASCII challenge parameter (32b HEX/1..16 chars)>", NULL),
|
||||||
|
arg_str0(NULL, NULL, "<HEX/ASCII application parameter (32b HEX/1..16 chars)>", NULL),
|
||||||
|
arg_param_end
|
||||||
|
};
|
||||||
|
CLIExecWithReturn(cmd, argtable, true);
|
||||||
|
|
||||||
|
bool APDULogging = arg_get_lit(1);
|
||||||
|
bool verbose = arg_get_lit(2);
|
||||||
|
bool paramsPlain = arg_get_lit(3);
|
||||||
|
|
||||||
|
char fname[250] = {0};
|
||||||
|
bool err;
|
||||||
|
root = OpenJson(4, fname, argtable, &err);
|
||||||
|
if(err)
|
||||||
|
return 1;
|
||||||
|
if (root) {
|
||||||
|
size_t jlen;
|
||||||
|
JsonLoadBufAsHex(root, "$.ChallengeParam", data, 32, &jlen);
|
||||||
|
JsonLoadBufAsHex(root, "$.ApplicationParam", &data[32], 32, &jlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paramsPlain) {
|
||||||
|
memset(cdata, 0x00, 32);
|
||||||
|
CLIGetStrWithReturn(5, cdata, &chlen);
|
||||||
|
if (chlen && chlen > 16) {
|
||||||
|
PrintAndLog("ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", chlen);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
CLIGetHexWithReturn(5, cdata, &chlen);
|
||||||
|
if (chlen && chlen != 32) {
|
||||||
|
PrintAndLog("ERROR: challenge parameter length must be 32 bytes only.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (chlen)
|
||||||
|
memmove(data, cdata, 32);
|
||||||
|
|
||||||
|
|
||||||
|
if (paramsPlain) {
|
||||||
|
memset(adata, 0x00, 32);
|
||||||
|
CLIGetStrWithReturn(6, adata, &applen);
|
||||||
|
if (applen && applen > 16) {
|
||||||
|
PrintAndLog("ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", applen);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
CLIGetHexWithReturn(6, adata, &applen);
|
||||||
|
if (applen && applen != 32) {
|
||||||
|
PrintAndLog("ERROR: application parameter length must be 32 bytes only.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (applen)
|
||||||
|
memmove(&data[32], adata, 32);
|
||||||
|
|
||||||
|
CLIParserFree();
|
||||||
|
|
||||||
|
SetAPDULogging(APDULogging);
|
||||||
|
|
||||||
|
// challenge parameter [32 bytes] - The challenge parameter is the SHA-256 hash of the Client Data, a stringified JSON data structure that the FIDO Client prepares
|
||||||
|
// application parameter [32 bytes] - The application parameter is the SHA-256 hash of the UTF-8 encoding of the application identity
|
||||||
|
|
||||||
|
uint8_t buf[2048] = {0};
|
||||||
|
size_t len = 0;
|
||||||
|
uint16_t sw = 0;
|
||||||
|
|
||||||
|
DropField();
|
||||||
|
int res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
PrintAndLog("Can't select authenticator. res=%x. Exit...", res);
|
||||||
|
DropField();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sw != 0x9000) {
|
||||||
|
PrintAndLog("Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
|
||||||
|
DropField();
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = FIDORegister(data, buf, sizeof(buf), &len, &sw);
|
||||||
|
DropField();
|
||||||
|
if (res) {
|
||||||
|
PrintAndLog("Can't execute register command. res=%x. Exit...", res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sw != 0x9000) {
|
||||||
|
PrintAndLog("ERROR execute register command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintAndLog("");
|
||||||
|
if (APDULogging)
|
||||||
|
PrintAndLog("---------------------------------------------------------------");
|
||||||
|
PrintAndLog("data len: %d", len);
|
||||||
|
if (verbose) {
|
||||||
|
PrintAndLog("--------------data----------------------");
|
||||||
|
dump_buffer((const unsigned char *)buf, len, NULL, 0);
|
||||||
|
PrintAndLog("--------------data----------------------");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buf[0] != 0x05) {
|
||||||
|
PrintAndLog("ERROR: First byte must be 0x05, but it %2x", buf[0]);
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
PrintAndLog("User public key: %s", sprint_hex(&buf[1], 65));
|
||||||
|
|
||||||
|
uint8_t keyHandleLen = buf[66];
|
||||||
|
PrintAndLog("Key handle[%d]: %s", keyHandleLen, sprint_hex(&buf[67], keyHandleLen));
|
||||||
|
|
||||||
|
int derp = 67 + keyHandleLen;
|
||||||
|
int derLen = (buf[derp + 2] << 8) + buf[derp + 3] + 4;
|
||||||
|
// needs to decode DER certificate
|
||||||
|
if (verbose) {
|
||||||
|
PrintAndLog("DER certificate[%d]:------------------DER-------------------", derLen);
|
||||||
|
dump_buffer_simple((const unsigned char *)&buf[67 + keyHandleLen], derLen, NULL);
|
||||||
|
PrintAndLog("\n----------------DER---------------------");
|
||||||
|
} else {
|
||||||
|
PrintAndLog("DER certificate[%d]: %s...", derLen, sprint_hex(&buf[derp], 20));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hashp = 1 + 65 + 1 + keyHandleLen + derLen;
|
||||||
|
PrintAndLog("Hash[%d]: %s", len - hashp, sprint_hex(&buf[hashp], len - hashp));
|
||||||
|
|
||||||
|
// check ANSI X9.62 format ECDSA signature (on P-256)
|
||||||
|
|
||||||
|
PrintAndLog("\nauth command: ");
|
||||||
|
printf("hf fido auth %s%s", paramsPlain?"-p ":"", sprint_hex_inrow(&buf[67], keyHandleLen));
|
||||||
|
if(chlen || applen)
|
||||||
|
printf(" %s", paramsPlain?(char *)cdata:sprint_hex_inrow(cdata, 32));
|
||||||
|
if(applen)
|
||||||
|
printf(" %s", paramsPlain?(char *)adata:sprint_hex_inrow(adata, 32));
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
if (root) {
|
||||||
|
JsonSaveBufAsHex(root, "ChallengeParam", data, 32);
|
||||||
|
JsonSaveBufAsHex(root, "ApplicationParam", &data[32], 32);
|
||||||
|
JsonSaveInt(root, "KeyHandleLen", keyHandleLen);
|
||||||
|
JsonSaveBufAsHexCompact(root, "KeyHandle", &buf[67], keyHandleLen);
|
||||||
|
JsonSaveBufAsHexCompact(root, "DER", &buf[67 + keyHandleLen], derLen);
|
||||||
|
|
||||||
|
res = json_dump_file(root, fname, JSON_INDENT(2));
|
||||||
|
if (res) {
|
||||||
|
PrintAndLog("ERROR: can't save the file: %s", fname);
|
||||||
|
return 200;
|
||||||
|
}
|
||||||
|
PrintAndLog("File `%s` saved.", fname);
|
||||||
|
|
||||||
|
// free json object
|
||||||
|
json_decref(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
int CmdHFFidoAuthenticate(const char *cmd) {
|
||||||
|
uint8_t data[512] = {0};
|
||||||
|
uint8_t hdata[250] = {0};
|
||||||
|
int hdatalen = 0;
|
||||||
|
uint8_t keyHandleLen = 0;
|
||||||
|
json_t *root = NULL;
|
||||||
|
|
||||||
|
CLIParserInit("hf fido auth",
|
||||||
|
"Initiate a U2F token authentication. Needs key handle and two 32-byte hash number. \nkey handle(var 0..255), challenge parameter (32b) and application parameter (32b).",
|
||||||
|
"Usage:\n\thf fido auth 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with 2 parameters, filled 0x00 and key handle\n"
|
||||||
|
"\thf fido auth 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f "
|
||||||
|
"000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters");
|
||||||
|
|
||||||
|
void* argtable[] = {
|
||||||
|
arg_param_begin,
|
||||||
|
arg_lit0("aA", "apdu", "show APDU reqests and responses"),
|
||||||
|
arg_lit0("vV", "verbose", "show technical data"),
|
||||||
|
arg_lit0("pP", "plain", "send plain ASCII to challenge and application parameters instead of HEX"),
|
||||||
|
arg_rem("default mode:", "dont-enforce-user-presence-and-sign"),
|
||||||
|
arg_lit0("uU", "user", "mode: enforce-user-presence-and-sign"),
|
||||||
|
arg_lit0("cC", "check", "mode: check-only"),
|
||||||
|
arg_str0("jJ", "json", "fido.json", "JSON input / output file name for parameters."),
|
||||||
|
arg_str0(NULL, NULL, "<HEX key handle (var 0..255b)>", NULL),
|
||||||
|
arg_str0(NULL, NULL, "<HEX/ASCII challenge parameter (32b HEX/1..16 chars)>", NULL),
|
||||||
|
arg_str0(NULL, NULL, "<HEX/ASCII application parameter (32b HEX/1..16 chars)>", NULL),
|
||||||
|
arg_param_end
|
||||||
|
};
|
||||||
|
CLIExecWithReturn(cmd, argtable, true);
|
||||||
|
|
||||||
|
bool APDULogging = arg_get_lit(1);
|
||||||
|
//bool verbose = arg_get_lit(2);
|
||||||
|
bool paramsPlain = arg_get_lit(3);
|
||||||
|
uint8_t controlByte = 0x08;
|
||||||
|
if (arg_get_lit(5))
|
||||||
|
controlByte = 0x03;
|
||||||
|
if (arg_get_lit(6))
|
||||||
|
controlByte = 0x07;
|
||||||
|
|
||||||
|
char fname[250] = {0};
|
||||||
|
bool err;
|
||||||
|
root = OpenJson(7, fname, argtable, &err);
|
||||||
|
if(err)
|
||||||
|
return 1;
|
||||||
|
if (root) {
|
||||||
|
size_t jlen;
|
||||||
|
JsonLoadBufAsHex(root, "$.ChallengeParam", data, 32, &jlen);
|
||||||
|
JsonLoadBufAsHex(root, "$.ApplicationParam", &data[32], 32, &jlen);
|
||||||
|
JsonLoadBufAsHex(root, "$.KeyHandle", &data[65], 512 - 67, &jlen);
|
||||||
|
keyHandleLen = jlen & 0xff;
|
||||||
|
data[64] = keyHandleLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
CLIGetHexWithReturn(8, hdata, &hdatalen);
|
||||||
|
if (hdatalen > 255) {
|
||||||
|
PrintAndLog("ERROR: application parameter length must be less than 255.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (hdatalen) {
|
||||||
|
keyHandleLen = hdatalen;
|
||||||
|
data[64] = keyHandleLen;
|
||||||
|
memmove(&data[65], hdata, keyHandleLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paramsPlain) {
|
||||||
|
memset(hdata, 0x00, 32);
|
||||||
|
CLIGetStrWithReturn(9, hdata, &hdatalen);
|
||||||
|
if (hdatalen && hdatalen > 16) {
|
||||||
|
PrintAndLog("ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
CLIGetHexWithReturn(9, hdata, &hdatalen);
|
||||||
|
if (hdatalen && hdatalen != 32) {
|
||||||
|
PrintAndLog("ERROR: challenge parameter length must be 32 bytes only.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hdatalen)
|
||||||
|
memmove(data, hdata, 32);
|
||||||
|
|
||||||
|
if (paramsPlain) {
|
||||||
|
memset(hdata, 0x00, 32);
|
||||||
|
CLIGetStrWithReturn(10, hdata, &hdatalen);
|
||||||
|
if (hdatalen && hdatalen > 16) {
|
||||||
|
PrintAndLog("ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
CLIGetHexWithReturn(10, hdata, &hdatalen);
|
||||||
|
if (hdatalen && hdatalen != 32) {
|
||||||
|
PrintAndLog("ERROR: application parameter length must be 32 bytes only.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hdatalen)
|
||||||
|
memmove(&data[32], hdata, 32);
|
||||||
|
|
||||||
|
CLIParserFree();
|
||||||
|
|
||||||
|
SetAPDULogging(APDULogging);
|
||||||
|
|
||||||
|
// (in parameter) conrtol byte 0x07 - check only, 0x03 - user presense + cign. 0x08 - sign only
|
||||||
|
// challenge parameter [32 bytes]
|
||||||
|
// application parameter [32 bytes]
|
||||||
|
// key handle length [1b] = N
|
||||||
|
// key handle [N]
|
||||||
|
|
||||||
|
uint8_t datalen = 32 + 32 + 1 + keyHandleLen;
|
||||||
|
|
||||||
|
uint8_t buf[2048] = {0};
|
||||||
|
size_t len = 0;
|
||||||
|
uint16_t sw = 0;
|
||||||
|
|
||||||
|
DropField();
|
||||||
|
int res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
PrintAndLog("Can't select authenticator. res=%x. Exit...", res);
|
||||||
|
DropField();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sw != 0x9000) {
|
||||||
|
PrintAndLog("Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
|
||||||
|
DropField();
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = FIDOAuthentication(data, datalen, controlByte, buf, sizeof(buf), &len, &sw);
|
||||||
|
DropField();
|
||||||
|
if (res) {
|
||||||
|
PrintAndLog("Can't execute authentication command. res=%x. Exit...", res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sw != 0x9000) {
|
||||||
|
PrintAndLog("ERROR execute authentication command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintAndLog("---------------------------------------------------------------");
|
||||||
|
PrintAndLog("User presence: %s", (buf[0]?"verified":"not verified"));
|
||||||
|
uint32_t cntr = (uint32_t)bytes_to_num(&buf[1], 4);
|
||||||
|
PrintAndLog("Counter: %d", cntr);
|
||||||
|
PrintAndLog("Hash[%d]: %s", len - 5, sprint_hex(&buf[5], len - 5));
|
||||||
|
|
||||||
|
if (root) {
|
||||||
|
JsonSaveBufAsHex(root, "ChallengeParam", data, 32);
|
||||||
|
JsonSaveBufAsHex(root, "ApplicationParam", &data[32], 32);
|
||||||
|
JsonSaveInt(root, "KeyHandleLen", keyHandleLen);
|
||||||
|
JsonSaveBufAsHexCompact(root, "KeyHandle", &data[65], keyHandleLen);
|
||||||
|
JsonSaveInt(root, "Counter", cntr);
|
||||||
|
|
||||||
|
res = json_dump_file(root, fname, JSON_INDENT(2));
|
||||||
|
if (res) {
|
||||||
|
PrintAndLog("ERROR: can't save the file: %s", fname);
|
||||||
|
return 200;
|
||||||
|
}
|
||||||
|
PrintAndLog("File `%s` saved.", fname);
|
||||||
|
|
||||||
|
// free json object
|
||||||
|
json_decref(root);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
static command_t CommandTable[] =
|
||||||
|
{
|
||||||
|
{"help", CmdHelp, 1, "This help."},
|
||||||
|
{"info", CmdHFFidoInfo, 0, "Info about FIDO tag."},
|
||||||
|
{"reg", CmdHFFidoRegister, 0, "FIDO U2F Registration Message."},
|
||||||
|
{"auth", CmdHFFidoAuthenticate, 0, "FIDO U2F Authentication Message."},
|
||||||
|
{NULL, NULL, 0, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
int CmdHFFido(const char *Cmd) {
|
||||||
|
(void)WaitForResponseTimeout(CMD_ACK,NULL,100);
|
||||||
|
CmdsParse(CommandTable, Cmd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CmdHelp(const char *Cmd) {
|
||||||
|
CmdsHelp(CommandTable);
|
||||||
|
return 0;
|
||||||
|
}
|
27
client/cmdhffido.h
Normal file
27
client/cmdhffido.h
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (C) 2018 Merlok
|
||||||
|
//
|
||||||
|
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
|
||||||
|
// at your option, any later version. See the LICENSE.txt file for the text of
|
||||||
|
// the license.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// High frequency FIDO U2F and FIDO2 contactless authenticators
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Documentation here:
|
||||||
|
//
|
||||||
|
// FIDO Alliance specifications
|
||||||
|
// https://fidoalliance.org/download/
|
||||||
|
// FIDO NFC Protocol Specification v1.0
|
||||||
|
// https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-nfc-protocol-v1.2-ps-20170411.html
|
||||||
|
// FIDO U2F Raw Message Formats
|
||||||
|
// https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef CMDHFFIDO_H__
|
||||||
|
#define CMDHFFIDO_H__
|
||||||
|
|
||||||
|
extern int CmdHFFido(const char *Cmd);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -266,9 +266,14 @@ int EMVExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool Includ
|
||||||
*sw = isw;
|
*sw = isw;
|
||||||
|
|
||||||
if (isw != 0x9000) {
|
if (isw != 0x9000) {
|
||||||
if (APDULogging)
|
if (APDULogging) {
|
||||||
PrintAndLog("APDU(%02x%02x) ERROR: [%4X] %s", apdu.CLA, apdu.INS, isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xff));
|
if (*sw >> 8 == 0x61) {
|
||||||
return 5;
|
PrintAndLog("APDU chaining len:%02x -->", *sw & 0xff);
|
||||||
|
} else {
|
||||||
|
PrintAndLog("APDU(%02x%02x) ERROR: [%4X] %s", apdu.CLA, apdu.INS, isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xff));
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// add to tlv tree
|
// add to tlv tree
|
||||||
|
|
|
@ -70,6 +70,10 @@ extern struct tlvdb *GetdCVVRawFromTrack2(const struct tlv *track2);
|
||||||
|
|
||||||
extern void SetAPDULogging(bool logging);
|
extern void SetAPDULogging(bool logging);
|
||||||
|
|
||||||
|
// exchange
|
||||||
|
extern int EMVExchange(bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
|
||||||
|
|
||||||
|
|
||||||
// search application
|
// search application
|
||||||
extern int EMVSearchPSE(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv);
|
extern int EMVSearchPSE(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv);
|
||||||
extern int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv);
|
extern int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv);
|
||||||
|
|
|
@ -68,24 +68,40 @@ char* GetApplicationDataName(tlv_tag_t tag) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int JsonSaveStr(json_t *root, char *path, char *value) {
|
int JsonSaveJsonObject(json_t *root, char *path, json_t *value) {
|
||||||
json_error_t error;
|
json_error_t error;
|
||||||
|
|
||||||
if (strlen(path) < 1)
|
if (strlen(path) < 1)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (path[0] == '$') {
|
if (path[0] == '$') {
|
||||||
if (json_path_set(root, path, json_string(value), 0, &error)) {
|
if (json_path_set(root, path, value, 0, &error)) {
|
||||||
PrintAndLog("ERROR: can't set json path: ", error.text);
|
PrintAndLog("ERROR: can't set json path: ", error.text);
|
||||||
return 2;
|
return 2;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return json_object_set_new(root, path, json_string(value));
|
return json_object_set_new(root, path, value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int JsonSaveInt(json_t *root, char *path, int value) {
|
||||||
|
return JsonSaveJsonObject(root, path, json_integer(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
int JsonSaveStr(json_t *root, char *path, char *value) {
|
||||||
|
return JsonSaveJsonObject(root, path, json_string(value));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int JsonSaveBufAsHexCompact(json_t *elm, char *path, uint8_t *data, size_t datalen) {
|
||||||
|
char * msg = sprint_hex_inrow(data, datalen);
|
||||||
|
if (msg && strlen(msg) && msg[strlen(msg) - 1] == ' ')
|
||||||
|
msg[strlen(msg) - 1] = '\0';
|
||||||
|
|
||||||
|
return JsonSaveStr(elm, path, msg);
|
||||||
|
}
|
||||||
|
|
||||||
int JsonSaveBufAsHex(json_t *elm, char *path, uint8_t *data, size_t datalen) {
|
int JsonSaveBufAsHex(json_t *elm, char *path, uint8_t *data, size_t datalen) {
|
||||||
char * msg = sprint_hex(data, datalen);
|
char * msg = sprint_hex(data, datalen);
|
||||||
if (msg && strlen(msg) && msg[strlen(msg) - 1] == ' ')
|
if (msg && strlen(msg) && msg[strlen(msg) - 1] == ' ')
|
||||||
|
@ -248,6 +264,20 @@ bool HexToBuffer(const char *errormsg, const char *hexvalue, uint8_t * buffer, s
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int JsonLoadBufAsHex(json_t *elm, char *path, uint8_t *data, size_t maxbufferlen, size_t *datalen) {
|
||||||
|
if (datalen)
|
||||||
|
*datalen = 0;
|
||||||
|
|
||||||
|
json_t *jelm = json_path_get((const json_t *)elm, path);
|
||||||
|
if (!jelm || !json_is_string(jelm))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (!HexToBuffer("ERROR load", json_string_value(jelm), data, maxbufferlen, datalen))
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
bool ParamLoadFromJson(struct tlvdb *tlv) {
|
bool ParamLoadFromJson(struct tlvdb *tlv) {
|
||||||
json_t *root;
|
json_t *root;
|
||||||
json_error_t error;
|
json_error_t error;
|
||||||
|
|
|
@ -20,7 +20,10 @@ typedef struct {
|
||||||
|
|
||||||
extern char* GetApplicationDataName(tlv_tag_t tag);
|
extern char* GetApplicationDataName(tlv_tag_t tag);
|
||||||
|
|
||||||
|
extern int JsonSaveJsonObject(json_t *root, char *path, json_t *value);
|
||||||
extern int JsonSaveStr(json_t *root, char *path, char *value);
|
extern int JsonSaveStr(json_t *root, char *path, char *value);
|
||||||
|
extern int JsonSaveInt(json_t *root, char *path, int value);
|
||||||
|
extern int JsonSaveBufAsHexCompact(json_t *elm, char *path, uint8_t *data, size_t datalen);
|
||||||
extern int JsonSaveBufAsHex(json_t *elm, char *path, uint8_t *data, size_t datalen);
|
extern int JsonSaveBufAsHex(json_t *elm, char *path, uint8_t *data, size_t datalen);
|
||||||
extern int JsonSaveHex(json_t *elm, char *path, uint64_t data, int datalen);
|
extern int JsonSaveHex(json_t *elm, char *path, uint64_t data, int datalen);
|
||||||
|
|
||||||
|
@ -30,6 +33,8 @@ extern int JsonSaveTLVTreeElm(json_t *elm, char *path, struct tlvdb *tlvdbelm, b
|
||||||
|
|
||||||
extern int JsonSaveTLVTree(json_t *root, json_t *elm, char *path, struct tlvdb *tlvdbelm);
|
extern int JsonSaveTLVTree(json_t *root, json_t *elm, char *path, struct tlvdb *tlvdbelm);
|
||||||
|
|
||||||
|
extern int JsonLoadBufAsHex(json_t *elm, char *path, uint8_t *data, size_t maxbufferlen, size_t *datalen);
|
||||||
|
|
||||||
extern bool ParamLoadFromJson(struct tlvdb *tlv);
|
extern bool ParamLoadFromJson(struct tlvdb *tlv);
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -139,7 +139,7 @@ void hex_to_buffer(const uint8_t *buf, const uint8_t *hex_data, const size_t hex
|
||||||
// printing and converting functions
|
// printing and converting functions
|
||||||
|
|
||||||
char *sprint_hex(const uint8_t *data, const size_t len) {
|
char *sprint_hex(const uint8_t *data, const size_t len) {
|
||||||
static char buf[1025] = {0};
|
static char buf[4097] = {0};
|
||||||
|
|
||||||
hex_to_buffer((uint8_t *)buf, data, len, sizeof(buf) - 1, 0, 1, false);
|
hex_to_buffer((uint8_t *)buf, data, len, sizeof(buf) - 1, 0, 1, false);
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ char *sprint_hex(const uint8_t *data, const size_t len) {
|
||||||
}
|
}
|
||||||
|
|
||||||
char *sprint_hex_inrow_ex(const uint8_t *data, const size_t len, const size_t min_str_len) {
|
char *sprint_hex_inrow_ex(const uint8_t *data, const size_t len, const size_t min_str_len) {
|
||||||
static char buf[1025] = {0};
|
static char buf[4097] = {0};
|
||||||
|
|
||||||
hex_to_buffer((uint8_t *)buf, data, len, sizeof(buf) - 1, min_str_len, 0, false);
|
hex_to_buffer((uint8_t *)buf, data, len, sizeof(buf) - 1, min_str_len, 0, false);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue