+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;
+};
+