+
+ if (!LeaveFieldON)
+ DropFieldEx( channel );
+
+ return res;
+}
+
+int EMVSearch(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv) {
+ uint8_t aidbuf[APDU_DATA_LEN] = {0};
+ int aidlen = 0;
+ uint8_t data[APDU_RESPONSE_LEN] = {0};
+ size_t datalen = 0;
+ uint16_t sw = 0;
+
+ int res = 0;
+ int retrycnt = 0;
+ for(int i = 0; i < AIDlistLen; i ++) {
+ param_gethex_to_eol(AIDlist[i].aid, 0, aidbuf, sizeof(aidbuf), &aidlen);
+ res = EMVSelect(channel, (i == 0) ? ActivateField : false, true, aidbuf, aidlen, data, sizeof(data), &datalen, &sw, tlv);
+ // retry if error and not returned sw error
+ if (res && res != 5) {
+ if (++retrycnt < 3){
+ i--;
+ } else {
+ // (1) - card select error, (4) reply timeout, (200) - result length = 0
+ if (res == 1 || res == 4 || res == 200) {
+ if (!LeaveFieldON)
+ DropFieldEx(channel);
+ PrintAndLogEx(WARNING, "Exit...");
+ return 1;
+ }
+
+ retrycnt = 0;
+ PrintAndLogEx(FAILED, "Retry failed [%s]. Skipped...", AIDlist[i].aid);
+ }
+ continue;
+ }
+ retrycnt = 0;
+
+ if (res)
+ continue;
+
+ if (!datalen)
+ continue;
+
+ if (decodeTLV) {
+ PrintAndLogEx(SUCCESS, "%s", AIDlist[i].aid);
+ TLVPrintFromBuffer(data, datalen);
+ }
+ }
+
+ if (!LeaveFieldON)
+ DropFieldEx(channel);
+
+ return 0;
+}
+
+int EMVSelectApplication(struct tlvdb *tlv, uint8_t *AID, size_t *AIDlen) {
+ // check priority. 0x00 - highest
+ int prio = 0xffff;
+
+ *AIDlen = 0;
+
+ struct tlvdb *ttmp = tlvdb_find(tlv, 0x6f);
+ if (!ttmp)
+ return 1;
+
+ while (ttmp) {
+ const struct tlv *tgAID = tlvdb_get_inchild(ttmp, 0x84, NULL);
+ const struct tlv *tgPrio = tlvdb_get_inchild(ttmp, 0x87, NULL);
+
+ if (!tgAID)
+ break;
+
+ if (tgPrio) {
+ int pt = bytes_to_num((uint8_t*)tgPrio->value, (tgPrio->len < 2) ? tgPrio->len : 2);
+ if (pt < prio) {
+ prio = pt;
+
+ memcpy(AID, tgAID->value, tgAID->len);
+ *AIDlen = tgAID->len;
+ }
+ } else {
+ // takes the first application from list wo priority
+ if (!*AIDlen) {
+ memcpy(AID, tgAID->value, tgAID->len);
+ *AIDlen = tgAID->len;
+ }
+ }
+
+ ttmp = tlvdb_find_next(ttmp, 0x6f);
+ }
+
+ return 0;
+}
+
+int EMVGPO(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv)
+{
+ uint8_t GPO_APDU[APDU_COMMAND_LEN] = {0x80, ISO7816_GET_PROCESSING_OPTIONS, 0x00, 0x00, PDOLLen, 0x00};
+ memcpy(GPO_APDU + 5, PDOL, PDOLLen);
+ int apdulen = 5 + PDOLLen;
+
+ return EMVExchange(channel, LeaveFieldON, GPO_APDU, apdulen, Result, MaxResultLen, ResultLen, sw, tlv);
+}
+
+int EMVReadRecord(EMVCommandChannel channel, bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv)
+{
+ uint8_t read_APDU[5] = {0x00, ISO7816_READ_RECORDS, SFIrec, (SFI << 3) | 0x04, 0x00};
+ int res = EMVExchange(channel, LeaveFieldON, read_APDU, sizeof(read_APDU), Result, MaxResultLen, ResultLen, sw, tlv);
+ if (*sw == 0x6700) {
+ PrintAndLogEx(INFO, ">>> trying to reissue command withouth Le...");
+ res = EMVExchangeEx(channel, false, LeaveFieldON, read_APDU, sizeof(read_APDU), Result, MaxResultLen, ResultLen, sw, tlv);
+ }
+ return res;
+}
+
+int EMVAC(EMVCommandChannel channel, bool LeaveFieldON, uint8_t RefControl, uint8_t *CDOL, size_t CDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv)
+{
+ uint8_t CDOL_APDU[APDU_COMMAND_LEN] = {0x80, 0xae, RefControl, 0x00, CDOLLen, 0x00};
+ memcpy(CDOL_APDU + 5, CDOL, CDOLLen);
+ int apdulen = 5 + CDOLLen;
+
+ return EMVExchange(channel, LeaveFieldON, CDOL_APDU, apdulen, Result, MaxResultLen, ResultLen, sw, tlv);
+}
+
+int EMVGenerateChallenge(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv)
+{
+ uint8_t get_challenge_APDU[APDU_COMMAND_LEN] = {0x00, ISO7816_GET_CHALLENGE, 0x00, 0x00};
+
+ int res = EMVExchange(channel, LeaveFieldON, get_challenge_APDU, 4, Result, MaxResultLen, ResultLen, sw, tlv);
+ if (*sw == 0x6700) {
+ PrintAndLogEx(INFO, ">>> trying to reissue command withouth Le...");
+ res = EMVExchangeEx(channel, false, LeaveFieldON, get_challenge_APDU, 4, Result, MaxResultLen, ResultLen, sw, tlv);
+ }
+ return res;
+}
+
+int EMVInternalAuthenticate(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *DDOL, size_t DDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv)
+{
+ uint8_t authenticate_APDU[APDU_COMMAND_LEN] = {0x00, ISO7816_INTERNAL_AUTHENTICATE, 0x00, 0x00, DDOLLen, 0x00};
+ memcpy(authenticate_APDU + 5, DDOL, DDOLLen);
+ int apdulen = 5 + DDOLLen;
+
+ return EMVExchange(channel, LeaveFieldON, authenticate_APDU, apdulen, Result, MaxResultLen, ResultLen, sw, tlv);
+}
+
+int MSCComputeCryptoChecksum(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *UDOL, uint8_t UDOLlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv)
+{
+ uint8_t checksum_APDU[APDU_COMMAND_LEN] = {0x80, 0x2a, 0x8e, 0x80, UDOLlen, 0x00};
+ memcpy(checksum_APDU + 5, UDOL, UDOLlen);
+ int apdulen = 5 + UDOLlen;
+
+ return EMVExchange(channel, LeaveFieldON, checksum_APDU, apdulen, Result, MaxResultLen, ResultLen, sw, tlv);
+}
+
+// Authentication
+struct emv_pk *get_ca_pk(struct tlvdb *db) {
+ const struct tlv *df_tlv = tlvdb_get(db, 0x84, NULL);
+ const struct tlv *caidx_tlv = tlvdb_get(db, 0x8f, NULL);
+
+ if (!df_tlv || !caidx_tlv || df_tlv->len < 6 || caidx_tlv->len != 1)
+ return NULL;
+
+ PrintAndLogEx(NORMAL, "CA Public Key index 0x%0x", caidx_tlv->value[0]);
+ return emv_pk_get_ca_pk(df_tlv->value, caidx_tlv->value[0]);
+}
+
+int trSDA(struct tlvdb *tlv) {
+
+ struct emv_pk *pk = get_ca_pk(tlv);
+ if (!pk) {
+ PrintAndLogEx(WARNING, "Error: Key not found. Exit.");
+ return 2;
+ }
+
+ struct emv_pk *issuer_pk = emv_pki_recover_issuer_cert(pk, tlv);
+ if (!issuer_pk) {
+ emv_pk_free(pk);
+ PrintAndLogEx(WARNING, "Error: Issuer certificate not found. Exit.");
+ return 2;
+ }
+
+ PrintAndLogEx(SUCCESS, "Issuer PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx",
+ issuer_pk->rid[0],
+ issuer_pk->rid[1],
+ issuer_pk->rid[2],
+ issuer_pk->rid[3],
+ issuer_pk->rid[4],
+ issuer_pk->index,
+ issuer_pk->serial[0],
+ issuer_pk->serial[1],
+ issuer_pk->serial[2]
+ );
+
+ const struct tlv *sda_tlv = tlvdb_get(tlv, 0x21, NULL);
+ if (!sda_tlv || sda_tlv->len < 1) {
+ emv_pk_free(issuer_pk);
+ emv_pk_free(pk);
+ PrintAndLogEx(WARNING, "Can't find input list for Offline Data Authentication. Exit.");
+ return 3;
+ }
+
+ struct tlvdb *dac_db = emv_pki_recover_dac(issuer_pk, tlv, sda_tlv);
+ if (dac_db) {
+ const struct tlv *dac_tlv = tlvdb_get(dac_db, 0x9f45, NULL);
+ PrintAndLogEx(NORMAL, "SDA verified OK. (Data Authentication Code: %02hhx:%02hhx)\n", dac_tlv->value[0], dac_tlv->value[1]);
+ tlvdb_add(tlv, dac_db);
+ } else {
+ emv_pk_free(issuer_pk);
+ emv_pk_free(pk);
+ PrintAndLogEx(WARNING, "SSAD verify error");
+ return 4;
+ }
+
+ emv_pk_free(issuer_pk);
+ emv_pk_free(pk);
+ return 0;
+}
+
+static const unsigned char default_ddol_value[] = {0x9f, 0x37, 0x04};
+static struct tlv default_ddol_tlv = {.tag = 0x9f49, .len = 3, .value = default_ddol_value };
+
+int trDDA(EMVCommandChannel channel, bool decodeTLV, struct tlvdb *tlv) {
+ uint8_t buf[APDU_RESPONSE_LEN] = {0};
+ size_t len = 0;
+ uint16_t sw = 0;
+
+ struct emv_pk *pk = get_ca_pk(tlv);
+ if (!pk) {
+ PrintAndLogEx(WARNING, "Error: Key not found. Exit.");
+ return 2;
+ }
+
+ const struct tlv *sda_tlv = tlvdb_get(tlv, 0x21, NULL);
+/* if (!sda_tlv || sda_tlv->len < 1) { it may be 0!!!!
+ emv_pk_free(pk);
+ PrintAndLogEx(WARNING, "Error: Can't find input list for Offline Data Authentication. Exit.");
+ return 3;
+ }
+*/
+ struct emv_pk *issuer_pk = emv_pki_recover_issuer_cert(pk, tlv);
+ if (!issuer_pk) {
+ emv_pk_free(pk);
+ PrintAndLogEx(WARNING, "Error: Issuer certificate not found. Exit.");
+ return 2;
+ }
+ PrintAndLogEx(SUCCESS, "Issuer PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx\n",
+ issuer_pk->rid[0],
+ issuer_pk->rid[1],
+ issuer_pk->rid[2],
+ issuer_pk->rid[3],
+ issuer_pk->rid[4],
+ issuer_pk->index,
+ issuer_pk->serial[0],
+ issuer_pk->serial[1],
+ issuer_pk->serial[2]
+ );
+
+ struct emv_pk *icc_pk = emv_pki_recover_icc_cert(issuer_pk, tlv, sda_tlv);
+ if (!icc_pk) {
+ emv_pk_free(pk);
+ emv_pk_free(issuer_pk);
+ PrintAndLogEx(WARNING, "Error: ICC certificate not found. Exit.");
+ return 2;
+ }
+ PrintAndLogEx(SUCCESS, "ICC PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx\n",
+ icc_pk->rid[0],
+ icc_pk->rid[1],
+ icc_pk->rid[2],
+ icc_pk->rid[3],
+ icc_pk->rid[4],
+ icc_pk->index,
+ icc_pk->serial[0],
+ icc_pk->serial[1],
+ icc_pk->serial[2]
+ );
+
+ if (tlvdb_get(tlv, 0x9f2d, NULL)) {
+ struct emv_pk *icc_pe_pk = emv_pki_recover_icc_pe_cert(issuer_pk, tlv);
+ if (!icc_pe_pk) {
+ PrintAndLogEx(WARNING, "WARNING: ICC PE PK recover error. ");
+ } else {
+ PrintAndLogEx(SUCCESS, "ICC PE PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx\n",
+ icc_pe_pk->rid[0],
+ icc_pe_pk->rid[1],
+ icc_pe_pk->rid[2],
+ icc_pe_pk->rid[3],
+ icc_pe_pk->rid[4],
+ icc_pe_pk->index,
+ icc_pe_pk->serial[0],
+ icc_pe_pk->serial[1],
+ icc_pe_pk->serial[2]
+ );
+ }
+ } else {
+ PrintAndLogEx(INFO, "ICC PE PK (PIN Encipherment Public Key Certificate) not found.\n");
+ }
+
+ // 9F4B: Signed Dynamic Application Data
+ const struct tlv *sdad_tlv = tlvdb_get(tlv, 0x9f4b, NULL);
+ // DDA with internal authenticate OR fDDA with filled 0x9F4B tag (GPO result)
+ // EMV kernel3 v2.4, contactless book C-3, C.1., page 147
+ if (sdad_tlv) {
+ PrintAndLogEx(NORMAL, "\n* * Got Signed Dynamic Application Data (9F4B) form GPO. Maybe fDDA...");
+
+ const struct tlvdb *atc_db = emv_pki_recover_atc_ex(icc_pk, tlv, true);
+ if (!atc_db) {
+ PrintAndLogEx(WARNING, "Error: Can't recover IDN (ICC Dynamic Number)");
+ emv_pk_free(pk);
+ emv_pk_free(issuer_pk);
+ emv_pk_free(icc_pk);
+ return 8;
+ }
+
+ // 9f36 Application Transaction Counter (ATC)
+ const struct tlv *atc_tlv = tlvdb_get(atc_db, 0x9f36, NULL);
+ if(atc_tlv) {
+ PrintAndLogEx(NORMAL, "\nATC (Application Transaction Counter) [%zu] %s", atc_tlv->len, sprint_hex_inrow(atc_tlv->value, atc_tlv->len));
+
+ const struct tlv *core_atc_tlv = tlvdb_get(tlv, 0x9f36, NULL);
+ if(tlv_equal(core_atc_tlv, atc_tlv)) {
+ PrintAndLogEx(SUCCESS, "ATC check OK.");
+ PrintAndLogEx(SUCCESS, "fDDA (fast DDA) verified OK.");
+ } else {
+ PrintAndLogEx(WARNING, "Error: fDDA verified, but ATC in the certificate and ATC in the record not the same.");
+ }
+ } else {
+ PrintAndLogEx(NORMAL, "\nERROR: fDDA (fast DDA) verify error");
+ emv_pk_free(pk);
+ emv_pk_free(issuer_pk);
+ emv_pk_free(icc_pk);
+ return 9;
+ }
+ } else {
+ struct tlvdb *dac_db = emv_pki_recover_dac(issuer_pk, tlv, sda_tlv);
+ if (dac_db) {
+ const struct tlv *dac_tlv = tlvdb_get(dac_db, 0x9f45, NULL);
+ PrintAndLogEx(NORMAL, "SDAD verified OK. (Data Authentication Code: %02hhx:%02hhx)\n", dac_tlv->value[0], dac_tlv->value[1]);
+ tlvdb_add(tlv, dac_db);
+ } else {
+ PrintAndLogEx(WARNING, "Error: SSAD verify error");
+ emv_pk_free(pk);
+ emv_pk_free(issuer_pk);
+ emv_pk_free(icc_pk);
+ return 4;
+ }
+
+ PrintAndLogEx(NORMAL, "\n* Calc DDOL");
+ const struct tlv *ddol_tlv = tlvdb_get(tlv, 0x9f49, NULL);
+ if (!ddol_tlv) {
+ ddol_tlv = &default_ddol_tlv;
+ PrintAndLogEx(NORMAL, "DDOL [9f49] not found. Using default DDOL");
+ }
+
+ struct tlv *ddol_data_tlv = dol_process(ddol_tlv, tlv, 0);
+ if (!ddol_data_tlv) {
+ PrintAndLogEx(WARNING, "Error: Can't create DDOL TLV");
+ emv_pk_free(pk);
+ emv_pk_free(issuer_pk);
+ emv_pk_free(icc_pk);
+ return 5;
+ }
+
+ PrintAndLogEx(NORMAL, "DDOL data[%d]: %s", ddol_data_tlv->len, sprint_hex(ddol_data_tlv->value, ddol_data_tlv->len));
+
+ PrintAndLogEx(NORMAL, "\n* Internal Authenticate");
+ int res = EMVInternalAuthenticate(channel, true, (uint8_t *)ddol_data_tlv->value, ddol_data_tlv->len, buf, sizeof(buf), &len, &sw, NULL);
+ if (res) {
+ PrintAndLogEx(WARNING, "Internal Authenticate error(%d): %4x. Exit...", res, sw);
+ free(ddol_data_tlv);
+ emv_pk_free(pk);
+ emv_pk_free(issuer_pk);
+ emv_pk_free(icc_pk);
+ return 6;
+ }
+
+ struct tlvdb *dda_db = NULL;
+ if (buf[0] == 0x80) {
+ if (len < 3 ) {
+ PrintAndLogEx(WARNING, "Error: Internal Authenticate format1 parsing error. length=%d", len);
+ } else {
+ // parse response 0x80
+ struct tlvdb *t80 = tlvdb_parse_multi(buf, len);
+ const struct tlv * t80tlv = tlvdb_get_tlv(t80);
+
+ // 9f4b Signed Dynamic Application Data
+ dda_db = tlvdb_fixed(0x9f4b, t80tlv->len, t80tlv->value);
+ tlvdb_add(tlv, dda_db);
+
+ tlvdb_free(t80);
+
+ if (decodeTLV){
+ PrintAndLogEx(NORMAL, "* * Decode response format 1:");
+ TLVPrintFromTLV(dda_db);
+ }
+ }
+ } else {
+ dda_db = tlvdb_parse_multi(buf, len);
+ if(!dda_db) {
+ PrintAndLogEx(WARNING, "Error: Can't parse Internal Authenticate result as TLV");
+ free(ddol_data_tlv);
+ emv_pk_free(pk);
+ emv_pk_free(issuer_pk);
+ emv_pk_free(icc_pk);
+ return 7;
+ }
+ tlvdb_add(tlv, dda_db);
+
+ if (decodeTLV)
+ TLVPrintFromTLV(dda_db);
+ }
+
+ struct tlvdb *idn_db = emv_pki_recover_idn_ex(icc_pk, dda_db, ddol_data_tlv, true);
+ free(ddol_data_tlv);
+ if (!idn_db) {
+ PrintAndLogEx(WARNING, "Error: Can't recover IDN (ICC Dynamic Number)");
+ tlvdb_free(dda_db);
+ emv_pk_free(pk);
+ emv_pk_free(issuer_pk);
+ emv_pk_free(icc_pk);
+ return 8;
+ }
+ tlvdb_free(dda_db);
+
+ // 9f4c ICC Dynamic Number
+ const struct tlv *idn_tlv = tlvdb_get(idn_db, 0x9f4c, NULL);
+ if(idn_tlv) {
+ PrintAndLogEx(NORMAL, "\nIDN (ICC Dynamic Number) [%zu] %s", idn_tlv->len, sprint_hex_inrow(idn_tlv->value, idn_tlv->len));
+ PrintAndLogEx(NORMAL, "DDA verified OK.");
+ tlvdb_add(tlv, idn_db);
+ tlvdb_free(idn_db);
+ } else {
+ PrintAndLogEx(NORMAL, "\nERROR: DDA verify error");
+ tlvdb_free(idn_db);
+
+ emv_pk_free(pk);
+ emv_pk_free(issuer_pk);
+ emv_pk_free(icc_pk);
+ return 9;
+ }
+ }
+
+ emv_pk_free(pk);
+ emv_pk_free(issuer_pk);
+ emv_pk_free(icc_pk);
+ return 0;
+}
+
+int trCDA(struct tlvdb *tlv, struct tlvdb *ac_tlv, struct tlv *pdol_data_tlv, struct tlv *ac_data_tlv) {
+
+ struct emv_pk *pk = get_ca_pk(tlv);
+ if (!pk) {
+ PrintAndLogEx(WARNING, "Error: Key not found. Exit.");
+ return 2;
+ }
+
+ const struct tlv *sda_tlv = tlvdb_get(tlv, 0x21, NULL);
+ if (!sda_tlv || sda_tlv->len < 1) {
+ PrintAndLogEx(WARNING, "Error: Can't find input list for Offline Data Authentication. Exit.");
+ emv_pk_free(pk);
+ return 3;
+ }
+
+ struct emv_pk *issuer_pk = emv_pki_recover_issuer_cert(pk, tlv);
+ if (!issuer_pk) {
+ PrintAndLogEx(WARNING, "Error: Issuer certificate not found. Exit.");
+ emv_pk_free(pk);
+ return 2;
+ }
+ PrintAndLogEx(SUCCESS, "Issuer PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx\n",
+ issuer_pk->rid[0],
+ issuer_pk->rid[1],
+ issuer_pk->rid[2],
+ issuer_pk->rid[3],
+ issuer_pk->rid[4],
+ issuer_pk->index,
+ issuer_pk->serial[0],
+ issuer_pk->serial[1],
+ issuer_pk->serial[2]
+ );
+
+ struct emv_pk *icc_pk = emv_pki_recover_icc_cert(issuer_pk, tlv, sda_tlv);
+ if (!icc_pk) {
+ PrintAndLogEx(WARNING, "Error: ICC certificate not found. Exit.");
+ emv_pk_free(pk);
+ emv_pk_free(issuer_pk);
+ return 2;
+ }
+ PrintAndLogEx(SUCCESS, "ICC PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx\n",
+ icc_pk->rid[0],
+ icc_pk->rid[1],
+ icc_pk->rid[2],
+ icc_pk->rid[3],
+ icc_pk->rid[4],
+ icc_pk->index,
+ icc_pk->serial[0],
+ icc_pk->serial[1],
+ icc_pk->serial[2]
+ );
+
+ struct tlvdb *dac_db = emv_pki_recover_dac(issuer_pk, tlv, sda_tlv);
+ if (dac_db) {
+ const struct tlv *dac_tlv = tlvdb_get(dac_db, 0x9f45, NULL);
+ PrintAndLogEx(NORMAL, "SSAD verified OK. (%02hhx:%02hhx)", dac_tlv->value[0], dac_tlv->value[1]);
+ tlvdb_add(tlv, dac_db);
+ } else {
+ PrintAndLogEx(WARNING, "Error: SSAD verify error");
+ emv_pk_free(pk);
+ emv_pk_free(issuer_pk);
+ emv_pk_free(icc_pk);
+ return 4;
+ }
+
+ PrintAndLogEx(NORMAL, "\n* * Check Signed Dynamic Application Data (SDAD)");
+ struct tlvdb *idn_db = emv_pki_perform_cda_ex(icc_pk, tlv, ac_tlv,
+ pdol_data_tlv, // pdol
+ ac_data_tlv, // cdol1
+ NULL, // cdol2
+ true);
+ if (idn_db) {
+ const struct tlv *idn_tlv = tlvdb_get(idn_db, 0x9f4c, NULL);
+ PrintAndLogEx(NORMAL, "\nIDN (ICC Dynamic Number) [%zu] %s", idn_tlv->len, sprint_hex_inrow(idn_tlv->value, idn_tlv->len));
+ PrintAndLogEx(NORMAL, "CDA verified OK.");
+ tlvdb_add(tlv, idn_db);
+ } else {
+ PrintAndLogEx(NORMAL, "\nERROR: CDA verify error");
+ }
+
+ emv_pk_free(pk);
+ emv_pk_free(issuer_pk);
+ emv_pk_free(icc_pk);
+ return 0;
+}
+
+int RecoveryCertificates(struct tlvdb *tlvRoot, json_t *root) {
+
+ struct emv_pk *pk = get_ca_pk(tlvRoot);
+ if (!pk) {
+ PrintAndLog("ERROR: Key not found. Exit.");
+ return 1;
+ }
+
+ struct emv_pk *issuer_pk = emv_pki_recover_issuer_cert(pk, tlvRoot);
+ if (!issuer_pk) {
+ emv_pk_free(pk);
+ PrintAndLog("WARNING: Issuer certificate not found. Exit.");
+ return 2;
+ }
+ PrintAndLogEx(SUCCESS, "Issuer PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx",
+ issuer_pk->rid[0],
+ issuer_pk->rid[1],
+ issuer_pk->rid[2],
+ issuer_pk->rid[3],
+ issuer_pk->rid[4],
+ issuer_pk->index,
+ issuer_pk->serial[0],
+ issuer_pk->serial[1],
+ issuer_pk->serial[2]
+ );
+
+ JsonSaveBufAsHex(root, "$.ApplicationData.RID", issuer_pk->rid, 5);
+
+ char *issuer_pk_c = emv_pk_dump_pk(issuer_pk);
+ JsonSaveStr(root, "$.ApplicationData.IssuerPublicKeyDec", issuer_pk_c);
+ JsonSaveBufAsHex(root, "$.ApplicationData.IssuerPublicKeyModulus", issuer_pk->modulus, issuer_pk->mlen);
+ free(issuer_pk_c);
+
+ struct emv_pk *icc_pk = emv_pki_recover_icc_cert(issuer_pk, tlvRoot, NULL);
+ if (!icc_pk) {
+ emv_pk_free(pk);
+ emv_pk_free(issuer_pk);
+ PrintAndLogEx(WARNING, "WARNING: ICC certificate not found. Exit.");
+ return 2;
+ }
+ PrintAndLogEx(SUCCESS, "ICC PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx\n",
+ icc_pk->rid[0],
+ icc_pk->rid[1],
+ icc_pk->rid[2],
+ icc_pk->rid[3],
+ icc_pk->rid[4],
+ icc_pk->index,
+ icc_pk->serial[0],
+ icc_pk->serial[1],
+ icc_pk->serial[2]
+ );
+
+ char *icc_pk_c = emv_pk_dump_pk(icc_pk);
+ JsonSaveStr(root, "$.ApplicationData.ICCPublicKeyDec", icc_pk_c);
+ JsonSaveBufAsHex(root, "$.ApplicationData.ICCPublicKeyModulus", icc_pk->modulus, icc_pk->mlen);
+ free(issuer_pk_c);
+
+ return 0;