X-Git-Url: http://cvs.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/blobdiff_plain/39cc1c879e3d75b3cafd79e4e139a7f6673dd349..0ab9002f36f8297c640cf9920c06d106ccae2d70:/client/cmdhffido.c?ds=inline diff --git a/client/cmdhffido.c b/client/cmdhffido.c index fd97ace5..25862445 100644 --- a/client/cmdhffido.c +++ b/client/cmdhffido.c @@ -28,57 +28,28 @@ #include #include #include +#include +#include +#include #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 "emv/apduinfo.h" #include "cliparser/cliparser.h" +#include "crypto/asn1utils.h" +#include "crypto/libpcrypto.h" +#include "fido/cbortools.h" +#include "fido/fidocore.h" +#include "fido/cose.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) @@ -91,7 +62,7 @@ int CmdHFFidoInfo(const char *cmd) { PrintAndLog("--------------------------------------------"); SetAPDULogging(false); - uint8_t buf[APDU_RES_LEN] = {0}; + uint8_t buf[APDU_RESPONSE_LEN] = {0}; size_t len = 0; uint16_t sw = 0; int res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw); @@ -133,9 +104,23 @@ int CmdHFFidoInfo(const char *cmd) { return 0; } + + if(buf[0]) { + PrintAndLog("FIDO2 ger version error: %d - %s", buf[0], fido2GetCmdErrorDescription(buf[0])); + return 0; + } - PrintAndLog("FIDO2 version: (%d)", len); - dump_buffer((const unsigned char *)buf, len, NULL, 0); + if (len > 1) { +// if (false) { +// PrintAndLog("FIDO2 version: (len=%d)", len); +// dump_buffer((const unsigned char *)buf, len, NULL, 0); +// } + + PrintAndLog("FIDO2 version CBOR decoded:"); + TinyCborPrintFIDOPackage(fido2CmdGetInfo, true, &buf[1], len - 1); + } else { + PrintAndLog("FIDO2 version length error"); + } return 0; } @@ -201,8 +186,9 @@ int CmdHFFidoRegister(const char *cmd) { void* argtable[] = { arg_param_begin, arg_lit0("aA", "apdu", "show APDU reqests and responses"), - arg_lit0("vV", "verbose", "show technical data"), + arg_litn("vV", "verbose", 0, 2, "show technical data. vv - show full certificates data"), arg_lit0("pP", "plain", "send plain ASCII to challenge and application parameters instead of HEX"), + arg_lit0("tT", "tlv", "Show DER certificate contents in TLV representation"), arg_str0("jJ", "json", "fido.json", "JSON input / output file name for parameters."), arg_str0(NULL, NULL, "", NULL), arg_str0(NULL, NULL, "", NULL), @@ -212,11 +198,13 @@ int CmdHFFidoRegister(const char *cmd) { bool APDULogging = arg_get_lit(1); bool verbose = arg_get_lit(2); + bool verbose2 = arg_get_lit(2) > 1; bool paramsPlain = arg_get_lit(3); + bool showDERTLV = arg_get_lit(4); char fname[250] = {0}; bool err; - root = OpenJson(4, fname, argtable, &err); + root = OpenJson(5, fname, argtable, &err); if(err) return 1; if (root) { @@ -227,13 +215,13 @@ int CmdHFFidoRegister(const char *cmd) { if (paramsPlain) { memset(cdata, 0x00, 32); - CLIGetStrWithReturn(5, cdata, &chlen); + CLIGetStrWithReturn(6, 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); + CLIGetHexWithReturn(6, cdata, &chlen); if (chlen && chlen != 32) { PrintAndLog("ERROR: challenge parameter length must be 32 bytes only."); return 1; @@ -245,13 +233,13 @@ int CmdHFFidoRegister(const char *cmd) { if (paramsPlain) { memset(adata, 0x00, 32); - CLIGetStrWithReturn(6, adata, &applen); + CLIGetStrWithReturn(7, 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); + CLIGetHexWithReturn(7, adata, &applen); if (applen && applen != 32) { PrintAndLog("ERROR: application parameter length must be 32 bytes only."); return 1; @@ -302,7 +290,7 @@ int CmdHFFidoRegister(const char *cmd) { if (APDULogging) PrintAndLog("---------------------------------------------------------------"); PrintAndLog("data len: %d", len); - if (verbose) { + if (verbose2) { PrintAndLog("--------------data----------------------"); dump_buffer((const unsigned char *)buf, len, NULL, 0); PrintAndLog("--------------data----------------------"); @@ -319,20 +307,66 @@ int CmdHFFidoRegister(const char *cmd) { 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); + if (verbose2) { + PrintAndLog("DER certificate[%d]:\n------------------DER-------------------", derLen); + dump_buffer_simple((const unsigned char *)&buf[derp], derLen, NULL); PrintAndLog("\n----------------DER---------------------"); } else { + if (verbose) + PrintAndLog("------------------DER-------------------"); PrintAndLog("DER certificate[%d]: %s...", derLen, sprint_hex(&buf[derp], 20)); } + // check and print DER certificate + uint8_t public_key[65] = {0}; + + // print DER certificate in TLV view + if (showDERTLV) { + PrintAndLog("----------------DER TLV-----------------"); + asn1_print(&buf[derp], derLen, " "); + PrintAndLog("----------------DER TLV-----------------"); + } + + FIDOCheckDERAndGetKey(&buf[derp], derLen, verbose, public_key, sizeof(public_key)); + // get hash 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) + uint8_t rval[300] = {0}; + uint8_t sval[300] = {0}; + res = ecdsa_asn1_get_signature(&buf[hashp], len - hashp, rval, sval); + if (!res) { + if (verbose) { + PrintAndLog(" r: %s", sprint_hex(rval, 32)); + PrintAndLog(" s: %s", sprint_hex(sval, 32)); + } + + uint8_t xbuf[4096] = {0}; + size_t xbuflen = 0; + res = FillBuffer(xbuf, sizeof(xbuf), &xbuflen, + "\x00", 1, + &data[32], 32, // application parameter + &data[0], 32, // challenge parameter + &buf[67], keyHandleLen, // keyHandle + &buf[1], 65, // user public key + NULL, 0); + //PrintAndLog("--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen)); + res = ecdsa_signature_verify(MBEDTLS_ECP_DP_SECP256R1, public_key, xbuf, xbuflen, &buf[hashp], len - hashp, true); + if (res) { + if (res == MBEDTLS_ERR_ECP_VERIFY_FAILED) { + PrintAndLog("Signature is NOT VALID."); + } else { + PrintAndLog("Other signature check error: %x %s", (res<0)?-res:res, ecdsa_get_error(res)); + } + } else { + PrintAndLog("Signature is OK."); + } + + } else { + PrintAndLog("Invalid signature. res=%d.", res); + } PrintAndLog("\nauth command: "); printf("hf fido auth %s%s", paramsPlain?"-p ":"", sprint_hex_inrow(&buf[67], keyHandleLen)); @@ -345,6 +379,7 @@ int CmdHFFidoRegister(const char *cmd) { if (root) { JsonSaveBufAsHex(root, "ChallengeParam", data, 32); JsonSaveBufAsHex(root, "ApplicationParam", &data[32], 32); + JsonSaveBufAsHexCompact(root, "PublicKey", &buf[1], 65); JsonSaveInt(root, "KeyHandleLen", keyHandleLen); JsonSaveBufAsHexCompact(root, "KeyHandle", &buf[67], keyHandleLen); JsonSaveBufAsHexCompact(root, "DER", &buf[67 + keyHandleLen], derLen); @@ -366,6 +401,8 @@ int CmdHFFidoRegister(const char *cmd) { int CmdHFFidoAuthenticate(const char *cmd) { uint8_t data[512] = {0}; uint8_t hdata[250] = {0}; + bool public_key_loaded = false; + uint8_t public_key[65] = {0}; int hdatalen = 0; uint8_t keyHandleLen = 0; json_t *root = NULL; @@ -385,6 +422,7 @@ int CmdHFFidoAuthenticate(const char *cmd) { 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("kK", "key", "public key to verify signature", NULL), arg_str0(NULL, NULL, "", NULL), arg_str0(NULL, NULL, "", NULL), arg_str0(NULL, NULL, "", NULL), @@ -393,7 +431,7 @@ int CmdHFFidoAuthenticate(const char *cmd) { CLIExecWithReturn(cmd, argtable, true); bool APDULogging = arg_get_lit(1); - //bool verbose = arg_get_lit(2); + bool verbose = arg_get_lit(2); bool paramsPlain = arg_get_lit(3); uint8_t controlByte = 0x08; if (arg_get_lit(5)) @@ -413,9 +451,22 @@ int CmdHFFidoAuthenticate(const char *cmd) { JsonLoadBufAsHex(root, "$.KeyHandle", &data[65], 512 - 67, &jlen); keyHandleLen = jlen & 0xff; data[64] = keyHandleLen; + JsonLoadBufAsHex(root, "$.PublicKey", public_key, 65, &jlen); + public_key_loaded = (jlen > 0); } + // public key CLIGetHexWithReturn(8, hdata, &hdatalen); + if (hdatalen && hdatalen != 65) { + PrintAndLog("ERROR: public key length must be 65 bytes only."); + return 1; + } + if (hdatalen) { + memmove(public_key, hdata, hdatalen); + public_key_loaded = true; + } + + CLIGetHexWithReturn(9, hdata, &hdatalen); if (hdatalen > 255) { PrintAndLog("ERROR: application parameter length must be less than 255."); return 1; @@ -434,7 +485,7 @@ int CmdHFFidoAuthenticate(const char *cmd) { return 1; } } else { - CLIGetHexWithReturn(9, hdata, &hdatalen); + CLIGetHexWithReturn(10, hdata, &hdatalen); if (hdatalen && hdatalen != 32) { PrintAndLog("ERROR: challenge parameter length must be 32 bytes only."); return 1; @@ -445,7 +496,7 @@ int CmdHFFidoAuthenticate(const char *cmd) { if (paramsPlain) { memset(hdata, 0x00, 32); - CLIGetStrWithReturn(10, hdata, &hdatalen); + CLIGetStrWithReturn(11, 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; @@ -509,6 +560,42 @@ int CmdHFFidoAuthenticate(const char *cmd) { PrintAndLog("Counter: %d", cntr); PrintAndLog("Hash[%d]: %s", len - 5, sprint_hex(&buf[5], len - 5)); + // check ANSI X9.62 format ECDSA signature (on P-256) + uint8_t rval[300] = {0}; + uint8_t sval[300] = {0}; + res = ecdsa_asn1_get_signature(&buf[5], len - 5, rval, sval); + if (!res) { + if (verbose) { + PrintAndLog(" r: %s", sprint_hex(rval, 32)); + PrintAndLog(" s: %s", sprint_hex(sval, 32)); + } + if (public_key_loaded) { + uint8_t xbuf[4096] = {0}; + size_t xbuflen = 0; + res = FillBuffer(xbuf, sizeof(xbuf), &xbuflen, + &data[32], 32, // application parameter + &buf[0], 1, // user presence + &buf[1], 4, // counter + data, 32, // challenge parameter + NULL, 0); + //PrintAndLog("--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen)); + res = ecdsa_signature_verify(MBEDTLS_ECP_DP_SECP256R1, public_key, xbuf, xbuflen, &buf[5], len - 5, true); + if (res) { + if (res == MBEDTLS_ERR_ECP_VERIFY_FAILED) { + PrintAndLog("Signature is NOT VALID."); + } else { + PrintAndLog("Other signature check error: %x %s", (res<0)?-res:res, ecdsa_get_error(res)); + } + } else { + PrintAndLog("Signature is OK."); + } + } else { + PrintAndLog("No public key provided. can't check signature."); + } + } else { + PrintAndLog("Invalid signature. res=%d.", res); + } + if (root) { JsonSaveBufAsHex(root, "ChallengeParam", data, 32); JsonSaveBufAsHex(root, "ApplicationParam", &data[32], 32); @@ -529,12 +616,301 @@ int CmdHFFidoAuthenticate(const char *cmd) { return 0; }; +void CheckSlash(char *fileName) { + if ((fileName[strlen(fileName) - 1] != '/') && + (fileName[strlen(fileName) - 1] != '\\')) + strcat(fileName, "/"); +} + +int GetExistsFileNameJson(char *prefixDir, char *reqestedFileName, char *fileName) { + fileName[0] = 0x00; + strcpy(fileName, get_my_executable_directory()); + CheckSlash(fileName); + + strcat(fileName, prefixDir); + CheckSlash(fileName); + + strcat(fileName, reqestedFileName); + if (!strstr(fileName, ".json")) + strcat(fileName, ".json"); + + if (access(fileName, F_OK) < 0) { + strcpy(fileName, get_my_executable_directory()); + CheckSlash(fileName); + + strcat(fileName, reqestedFileName); + if (!strstr(fileName, ".json")) + strcat(fileName, ".json"); + + if (access(fileName, F_OK) < 0) { + return 1; // file not found + } + } + return 0; +} + +int CmdHFFido2MakeCredential(const char *cmd) { + json_error_t error; + json_t *root = NULL; + char fname[300] = {0}; + + CLIParserInit("hf fido make", + "Execute a FIDO2 Make Credentional command. Needs json file with parameters. Sample file `fido2.json`. File can be placed in proxmark directory or in `proxmark/fido` directory.", + "Usage:\n\thf fido make -> execute command default parameters file `fido2.json`\n" + "\thf fido make test.json -> execute command with parameters file `text.json`"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_litn("vV", "verbose", 0, 2, "show technical data. vv - show full certificates data"), + arg_lit0("tT", "tlv", "Show DER certificate contents in TLV representation"), + arg_lit0("cC", "cbor", "show CBOR decoded data"), + arg_str0(NULL, NULL, "", "JSON input / output file name for parameters. Default `fido2.json`"), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool APDULogging = arg_get_lit(1); + bool verbose = arg_get_lit(2); + bool verbose2 = arg_get_lit(2) > 1; + bool showDERTLV = arg_get_lit(3); + bool showCBOR = arg_get_lit(4); + + uint8_t jsonname[250] ={0}; + char *cjsonname = (char *)jsonname; + int jsonnamelen = 0; + CLIGetStrWithReturn(5, jsonname, &jsonnamelen); + + if (!jsonnamelen) { + strcat(cjsonname, "fido2"); + jsonnamelen = strlen(cjsonname); + } + + CLIParserFree(); + + SetAPDULogging(APDULogging); + + int res = GetExistsFileNameJson("fido", cjsonname, fname); + if(res) { + PrintAndLog("ERROR: Can't found the json file."); + return res; + } + PrintAndLog("fname: %s\n", fname); + root = json_load_file(fname, 0, &error); + if (!root) { + PrintAndLog("ERROR: json error on line %d: %s", error.line, error.text); + return 1; + } + + uint8_t data[2048] = {0}; + size_t datalen = 0; + uint8_t buf[2048] = {0}; + size_t len = 0; + uint16_t sw = 0; + + DropField(); + 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 = FIDO2CreateMakeCredentionalReq(root, data, sizeof(data), &datalen); + if (res) + return res; + + if (showCBOR) { + PrintAndLog("CBOR make credentional request:"); + PrintAndLog("---------------- CBOR ------------------"); + TinyCborPrintFIDOPackage(fido2CmdMakeCredential, false, data, datalen); + PrintAndLog("---------------- CBOR ------------------"); + } + + res = FIDO2MakeCredential(data, datalen, buf, sizeof(buf), &len, &sw); + DropField(); + if (res) { + PrintAndLog("Can't execute make credential command. res=%x. Exit...", res); + return res; + } + + if (sw != 0x9000) { + PrintAndLog("ERROR execute make credential command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + return 3; + } + + if(buf[0]) { + PrintAndLog("FIDO2 make credential error: %d - %s", buf[0], fido2GetCmdErrorDescription(buf[0])); + return 0; + } + + PrintAndLog("MakeCredential result (%d b) OK.", len); + if (showCBOR) { + PrintAndLog("CBOR make credentional response:"); + PrintAndLog("---------------- CBOR ------------------"); + TinyCborPrintFIDOPackage(fido2CmdMakeCredential, true, &buf[1], len - 1); + PrintAndLog("---------------- CBOR ------------------"); + } + + // parse returned cbor + FIDO2MakeCredentionalParseRes(root, &buf[1], len - 1, verbose, verbose2, showCBOR, showDERTLV); + + if (root) { + 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); + } + + json_decref(root); + + return 0; +}; + +int CmdHFFido2GetAssertion(const char *cmd) { + json_error_t error; + json_t *root = NULL; + char fname[300] = {0}; + + CLIParserInit("hf fido assert", + "Execute a FIDO2 Get Assertion command. Needs json file with parameters. Sample file `fido2.json`. File can be placed in proxmark directory or in `proxmark/fido` directory.", + "Usage:\n\thf fido assert -> execute command default parameters file `fido2.json`\n" + "\thf fido assert test.json -l -> execute command with parameters file `text.json` and add to request CredentialId"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_litn("vV", "verbose", 0, 2, "show technical data. vv - show full certificates data"), + arg_lit0("cC", "cbor", "show CBOR decoded data"), + arg_lit0("lL", "list", "add CredentialId from json to allowList. Needs if `rk` option is `false` (authenticator don't store credential to its memory)"), + arg_str0(NULL, NULL, "", "JSON input / output file name for parameters. Default `fido2.json`"), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool APDULogging = arg_get_lit(1); + bool verbose = arg_get_lit(2); + bool verbose2 = arg_get_lit(2) > 1; + bool showCBOR = arg_get_lit(3); + bool createAllowList = arg_get_lit(4); + + uint8_t jsonname[250] ={0}; + char *cjsonname = (char *)jsonname; + int jsonnamelen = 0; + CLIGetStrWithReturn(5, jsonname, &jsonnamelen); + + if (!jsonnamelen) { + strcat(cjsonname, "fido2"); + jsonnamelen = strlen(cjsonname); + } + + CLIParserFree(); + + SetAPDULogging(APDULogging); + + int res = GetExistsFileNameJson("fido", "fido2", fname); + if(res) { + PrintAndLog("ERROR: Can't found the json file."); + return res; + } + PrintAndLog("fname: %s\n", fname); + root = json_load_file(fname, 0, &error); + if (!root) { + PrintAndLog("ERROR: json error on line %d: %s", error.line, error.text); + return 1; + } + + uint8_t data[2048] = {0}; + size_t datalen = 0; + uint8_t buf[2048] = {0}; + size_t len = 0; + uint16_t sw = 0; + + DropField(); + 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 = FIDO2CreateGetAssertionReq(root, data, sizeof(data), &datalen, createAllowList); + if (res) + return res; + + if (showCBOR) { + PrintAndLog("CBOR get assertion request:"); + PrintAndLog("---------------- CBOR ------------------"); + TinyCborPrintFIDOPackage(fido2CmdGetAssertion, false, data, datalen); + PrintAndLog("---------------- CBOR ------------------"); + } + + res = FIDO2GetAssertion(data, datalen, buf, sizeof(buf), &len, &sw); + DropField(); + if (res) { + PrintAndLog("Can't execute get assertion command. res=%x. Exit...", res); + return res; + } + + if (sw != 0x9000) { + PrintAndLog("ERROR execute get assertion command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + return 3; + } + + if(buf[0]) { + PrintAndLog("FIDO2 get assertion error: %d - %s", buf[0], fido2GetCmdErrorDescription(buf[0])); + return 0; + } + + PrintAndLog("GetAssertion result (%d b) OK.", len); + if (showCBOR) { + PrintAndLog("CBOR get assertion response:"); + PrintAndLog("---------------- CBOR ------------------"); + TinyCborPrintFIDOPackage(fido2CmdGetAssertion, true, &buf[1], len - 1); + PrintAndLog("---------------- CBOR ------------------"); + } + + // parse returned cbor + FIDO2GetAssertionParseRes(root, &buf[1], len - 1, verbose, verbose2, showCBOR); + + if (root) { + 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); + } + + 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."}, + {"make", CmdHFFido2MakeCredential, 0, "FIDO2 MakeCredential command."}, + {"assert", CmdHFFido2GetAssertion, 0, "FIDO2 GetAssertion command."}, {NULL, NULL, 0, NULL} };