+ JsonSaveBufAsHex(root, "$.Application.AID", AID, AIDlen);
+
+ // Init TLV tree
+ const char *alr = "Root terminal TLV tree";
+ struct tlvdb *tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr);
+
+ // EMV SELECT applet
+
+ PrintAndLogEx(NORMAL, "\n-->Selecting AID:%s.", sprint_hex_inrow(AID, AIDlen));
+ SetAPDULogging(showAPDU);
+ res = EMVSelect(channel, false, true, AID, AIDlen, buf, sizeof(buf), &len, &sw, tlvRoot);
+
+ if (res) {
+ PrintAndLogEx(ERR, "Can't select AID (%d). Exit...", res);
+ tlvdb_free(tlvRoot);
+ DropField();
+ return 5;
+ }
+
+ if (decodeTLV)
+ TLVPrintFromBuffer(buf, len);
+
+ // save mode
+ if (tlvdb_get(tlvRoot, 0x9f38, NULL)) {
+ JsonSaveStr(root, "$.Application.Mode", TransactionTypeStr[TrType]);
+ }
+
+ struct tlvdb *fci = tlvdb_parse_multi(buf, len);
+ if (extractTLVElements)
+ JsonSaveTLVTree(root, root, "$.Application.FCITemplate", fci);
+ else
+ JsonSaveTLVTreeElm(root, "$.Application.FCITemplate", fci, true, true, false);
+ tlvdb_free(fci);
+
+ // create transaction parameters
+ PrintAndLogEx(NORMAL, "-->Init transaction parameters.");
+ InitTransactionParameters(tlvRoot, paramLoadJSON, TrType, GenACGPO);
+
+ PrintAndLogEx(NORMAL, "-->Calc PDOL.");
+ struct tlv *pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83);
+ if (!pdol_data_tlv){
+ PrintAndLogEx(ERR, "Can't create PDOL TLV.");
+ tlvdb_free(tlvRoot);
+ DropField();
+ return 6;
+ }
+
+ size_t pdol_data_tlv_data_len;
+ unsigned char *pdol_data_tlv_data = tlv_encode(pdol_data_tlv, &pdol_data_tlv_data_len);
+ if (!pdol_data_tlv_data) {
+ PrintAndLogEx(ERR, "Can't create PDOL data.");
+ tlvdb_free(tlvRoot);
+ DropField();
+ return 6;
+ }
+ PrintAndLogEx(INFO, "PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len));
+
+ PrintAndLogEx(INFO, "-->GPO.");
+ res = EMVGPO(channel, true, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot);
+
+ free(pdol_data_tlv_data);
+ free(pdol_data_tlv);
+
+ if (res) {
+ PrintAndLogEx(ERR, "GPO error(%d): %4x. Exit...", res, sw);
+ tlvdb_free(tlvRoot);
+ DropField();
+ return 7;
+ }
+ ProcessGPOResponseFormat1(tlvRoot, buf, len, decodeTLV);
+
+ struct tlvdb *gpofci = tlvdb_parse_multi(buf, len);
+ if (extractTLVElements)
+ JsonSaveTLVTree(root, root, "$.Application.GPO", gpofci);
+ else
+ JsonSaveTLVTreeElm(root, "$.Application.GPO", gpofci, true, true, false);
+
+ JsonSaveTLVValue(root, "$.ApplicationData.AIP", tlvdb_find_full(gpofci, 0x82));
+ JsonSaveTLVValue(root, "$.ApplicationData.AFL", tlvdb_find_full(gpofci, 0x94));
+
+ tlvdb_free(gpofci);
+
+ PrintAndLogEx(INFO, "-->Read records from AFL.");
+ const struct tlv *AFL = tlvdb_get(tlvRoot, 0x94, NULL);
+
+ while(AFL && AFL->len) {
+ if (AFL->len % 4) {
+ PrintAndLogEx(ERR, "Wrong AFL length: %d", AFL->len);
+ break;
+ }
+
+ json_t *sfijson = json_path_get(root, "$.Application.Records");
+ if (!sfijson) {
+ json_t *app = json_path_get(root, "$.Application");
+ json_object_set_new(app, "Records", json_array());
+
+ sfijson = json_path_get(root, "$.Application.Records");
+ }
+ if (!json_is_array(sfijson)) {
+ PrintAndLogEx(ERR, "Internal logic error. `$.Application.Records` is not an array.");
+ break;
+ }
+ for (int i = 0; i < AFL->len / 4; i++) {
+ uint8_t SFI = AFL->value[i * 4 + 0] >> 3;
+ uint8_t SFIstart = AFL->value[i * 4 + 1];
+ uint8_t SFIend = AFL->value[i * 4 + 2];
+ uint8_t SFIoffline = AFL->value[i * 4 + 3];
+
+ PrintAndLogEx(INFO, "--->SFI[%02x] start:%02x end:%02x offline:%02x", SFI, SFIstart, SFIend, SFIoffline);
+ if (SFI == 0 || SFI == 31 || SFIstart == 0 || SFIstart > SFIend) {
+ PrintAndLogEx(ERR, "SFI ERROR! Skipped...");
+ continue;
+ }
+
+ for(int n = SFIstart; n <= SFIend; n++) {
+ PrintAndLogEx(INFO, "---->SFI[%02x] %d", SFI, n);
+
+ res = EMVReadRecord(channel, true, SFI, n, buf, sizeof(buf), &len, &sw, tlvRoot);
+ if (res) {
+ PrintAndLogEx(ERR, "SFI[%02x]. APDU error %4x", SFI, sw);
+ continue;
+ }
+
+ if (decodeTLV) {
+ TLVPrintFromBuffer(buf, len);
+ PrintAndLogEx(NORMAL, "");
+ }
+
+ json_t *jsonelm = json_object();
+ json_array_append_new(sfijson, jsonelm);
+
+ JsonSaveHex(jsonelm, "SFI", SFI, 1);
+ JsonSaveHex(jsonelm, "RecordNum", n, 1);
+ JsonSaveHex(jsonelm, "Offline", SFIoffline, 1);
+
+ struct tlvdb *rsfi = tlvdb_parse_multi(buf, len);
+ if (extractTLVElements)
+ JsonSaveTLVTree(root, jsonelm, "$.Data", rsfi);
+ else
+ JsonSaveTLVTreeElm(jsonelm, "$.Data", rsfi, true, true, false);
+ tlvdb_free(rsfi);
+ }
+ }
+
+ break;
+ }
+
+ // getting certificates
+ if (tlvdb_get(tlvRoot, 0x90, NULL)) {
+ PrintAndLogEx(INFO, "-->Recovering certificates.");
+ PKISetStrictExecution(false);
+ RecoveryCertificates(tlvRoot, root);
+ PKISetStrictExecution(true);
+ }
+
+ // free tlv object
+ tlvdb_free(tlvRoot);
+
+ // DropField
+ DropField();
+
+ res = json_dump_file(root, fname, JSON_INDENT(2));
+ if (res) {
+ PrintAndLogEx(ERR, "Can't save the file: %s", fname);
+ return 200;
+ }
+ PrintAndLogEx(SUCCESS, "File `%s` saved.", fname);
+
+ // free json object
+ json_decref(root);