]> cvs.zerfleddert.de Git - proxmark3-svn/blobdiff - client/cmdhffido.c
Bugfix: Output typing on print of HID card formats (#895)
[proxmark3-svn] / client / cmdhffido.c
index fd97ace5419a5788065649ab14f0dbb03bacd2e0..25862445ef715dd2b909950f3eca9fc27050d94f 100644 (file)
 #include <ctype.h>
 #include <unistd.h>
 #include <jansson.h>
+#include <mbedtls/x509_crt.h>
+#include <mbedtls/x509.h>
+#include <mbedtls/pk.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 "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,       "<HEX/ASCII challenge parameter (32b HEX/1..16 chars)>", NULL),
                arg_str0(NULL,  NULL,       "<HEX/ASCII application parameter (32b HEX/1..16 chars)>", 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,       "<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),
@@ -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 file name>", "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 file name>", "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}
 };
 
Impressum, Datenschutz