transactions MSD and M/Chip, qVSDC works
authormerlokk <olegmsn@gmail.com>
Sat, 2 Dec 2017 19:47:30 +0000 (21:47 +0200)
committermerlokk <olegmsn@gmail.com>
Sat, 2 Dec 2017 19:47:30 +0000 (21:47 +0200)
client/emv/cmdemv.c
client/emv/emv_tags.c
client/emv/emvcore.c
client/emv/emvcore.h

index b188555e3d0a350349d3ed9d12fb48fbf5931cad..268d9e630d61d7e3669fa4e15706081fe8b6c7ac 100644 (file)
@@ -283,6 +283,10 @@ int UsageCmdHFEMVExec(void) {
        PrintAndLog("  -a       : show APDU reqests and responses\n");
        PrintAndLog("  -t       : TLV decode results\n");
        PrintAndLog("  -f       : force search AID. Search AID instead of execute PPSE.\n");
+       PrintAndLog("  -v       : transaction type - qVSDC or M/Chip.\n");
+       PrintAndLog("  -c       : transaction type - qVSDC or M/Chip plus CDA (SDAD generation).\n");
+       PrintAndLog("  -x       : transaction type - VSDC. For test only. Not a standart behavior.\n");
+       PrintAndLog("By default : transaction type - MSD.\n");
        PrintAndLog("Samples:");
        PrintAndLog(" hf emv pse -s -> select card");
        PrintAndLog(" hf emv pse -s -t -a -> select card, show responses in TLV, show APDU");
@@ -296,6 +300,7 @@ int CmdHFEMVExec(const char *cmd) {
        bool showAPDU = false;
        bool decodeTLV = false;
        bool forceSearch = false;
+       enum TransactionType TrType = TT_MSD;
 
        uint8_t buf[APDU_RES_LEN] = {0};
        size_t len = 0;
@@ -335,6 +340,18 @@ int CmdHFEMVExec(const char *cmd) {
                                case 'F':
                                        forceSearch = true;
                                        break;
+                               case 'x':
+                               case 'X':
+                                       TrType = TT_VSDC;
+                                       break;
+                               case 'v':
+                               case 'V':
+                                       TrType = TT_QVSDCMCHIP;
+                                       break;
+                               case 'c':
+                               case 'C':
+                                       TrType = TT_CDA;
+                                       break;
                                default:
                                        PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp));
                                        return 1;
@@ -405,9 +422,24 @@ int CmdHFEMVExec(const char *cmd) {
        PrintAndLog("\n* Init transaction parameters.");
 
     //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4
-       TLV_ADD(0x9F66, "\x86\x00\x00\x00"); // MSD
-//     TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC
-//     TLV_ADD(0x9F66, "\x8e\x00\x00\x00"); // CDA
+       switch(TrType) {
+               case TT_MSD:
+                       TLV_ADD(0x9F66, "\x86\x00\x00\x00"); // MSD
+                       break;
+               // not standart for contactless. just for test.
+               case TT_VSDC:  
+                       TLV_ADD(0x9F66, "\x46\x00\x00\x00"); // VSDC
+                       break;
+               case TT_QVSDCMCHIP:
+                       TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC
+                       break;
+               case TT_CDA:
+                       TLV_ADD(0x9F66, "\x86\x80\x00\x00"); // CDA
+                       break;
+               default:
+                       TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC
+                       break;
+       }
     //9F02:(Amount, Authorised (Numeric)) len:6
        TLV_ADD(0x9F02, "\x00\x00\x00\x00\x01\x00");
     //9F1A:(Terminal Country Code) len:2
@@ -444,6 +476,7 @@ int CmdHFEMVExec(const char *cmd) {
        PrintAndLog("\n* GPO.");
        res = EMVGPO(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) {      
@@ -544,13 +577,27 @@ int CmdHFEMVExec(const char *cmd) {
                break;
        }       
        
-       // transaction check
+       // get AIP
        const struct tlv *AIPtlv = tlvdb_get(tlvRoot, 0x82, NULL);      
        uint16_t AIP = AIPtlv->value[0] + AIPtlv->value[1] * 0x100;
-       PrintAndLog("* * AIP=%x", AIP);
+       PrintAndLog("* * AIP=%04x", AIP);
 
+       // SDA
+       if (AIP & 0x0040) {
+               PrintAndLog("\n* SDA");
+               trSDA(AID, AIDlen, tlvRoot);
+       }
+
+       // DDA
+       if (AIP & 0x0020) {
+               PrintAndLog("\n* DDA");
+               
+       }       
+       
+       // transaction check
+       
        // qVSDC
-       {
+       if (TrType == TT_QVSDCMCHIP|| TrType == TT_CDA){
                // 9F26: Application Cryptogram
                const struct tlv *AC = tlvdb_get(tlvRoot, 0x9F26, NULL);
                if (AC) {
@@ -587,19 +634,88 @@ int CmdHFEMVExec(const char *cmd) {
                }
        }
        
-       // TODO: Mastercard M/CHIP
-       {
+       // Mastercard M/CHIP
+       if (GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD && (TrType == TT_QVSDCMCHIP || TrType == TT_CDA)){
                const struct tlv *CDOL1 = tlvdb_get(tlvRoot, 0x8c, NULL);
                if (CDOL1 && GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD) { // and m/chip transaction flag
+                       PrintAndLog("\n--> Mastercard M/Chip transaction.");
+
+                       PrintAndLog("* * Generate challenge");
+                       res = EMVGenerateChallenge(true, buf, sizeof(buf), &len, &sw, tlvRoot);
+                       if (res) {
+                               PrintAndLog("ERROR GetChallenge. APDU error %4x", sw);
+                               return 5;
+                       }
+                       if (len < 4) {
+                               PrintAndLog("ERROR GetChallenge. Wrong challenge length %d", len);
+                               return 5;
+                       }
+                       
+                       // ICC Dynamic Number
+                       struct tlvdb * ICCDynN = tlvdb_fixed(0x9f4c, len, buf);
+                       tlvdb_add(tlvRoot, ICCDynN);
+                       if (decodeTLV){
+                               PrintAndLog("\n* * ICC Dynamic Number:");
+                               TLVPrintFromTLV(ICCDynN);
+                       }
+                       
+                       PrintAndLog("* * Calc CDOL1");
+                       struct tlv *cdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x8c, NULL), tlvRoot, 0x01); // 0x01 - dummy tag
+                       if (!cdol_data_tlv){
+                               PrintAndLog("ERROR: can't create CDOL1 TLV.");
+                               return 4;
+                       }
+                       PrintAndLog("CDOL1 data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len));
+                       
+                       PrintAndLog("* * AC1");
+                       // EMVAC_TC + EMVAC_CDAREQ --- to get SDAD
+                       res = EMVAC(true, (TrType == TT_CDA) ? EMVAC_TC + EMVAC_CDAREQ : EMVAC_TC, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot);
+                       
+                       free(cdol_data_tlv);
+                       
+                       if (res) {      
+                               PrintAndLog("AC1 error(%d): %4x. Exit...", res, sw);
+                               return 5;
+                       }
+                       
+                       if (decodeTLV)
+                               TLVPrintFromBuffer(buf, len);
+                       
+                       PrintAndLog("* M/Chip transaction result:");
+                       // 9F27: Cryptogram Information Data (CID)
+                       const struct tlv *CID = tlvdb_get(tlvRoot, 0x9F27, NULL);
+                       if (CID) {
+                               emv_tag_dump(CID, stdout, 0);
+                               PrintAndLog("------------------------------");
+                               if (CID->len > 0) {
+                                       switch(CID->value[0] & EMVAC_AC_MASK){
+                                               case EMVAC_AAC:
+                                                       PrintAndLog("Transaction DECLINED.");
+                                                       break;
+                                               case EMVAC_TC:
+                                                       PrintAndLog("Transaction approved OFFLINE.");
+                                                       break;
+                                               case EMVAC_ARQC:
+                                                       PrintAndLog("Transaction approved ONLINE.");
+                                                       break;
+                                               default:
+                                                       PrintAndLog("ERROR: CID transaction code error %2x", CID->value[0] & EMVAC_AC_MASK);
+                                                       break;
+                                       }
+                               } else {
+                                       PrintAndLog("ERROR: Wrong CID length %d", CID->len);
+                               }
+                       } else {
+                               PrintAndLog("ERROR: CID(9F27) not found.");
+                       }
                
                }
        }
                
        // MSD
-       if (AIP & 0x8000) {
+       if (AIP & 0x8000 && TrType == TT_MSD) { 
                PrintAndLog("\n--> MSD transaction.");
                
-               
                PrintAndLog("* MSD dCVV path. Check dCVV");
 
                const struct tlv *track2 = tlvdb_get(tlvRoot, 0x57, NULL);
@@ -624,7 +740,7 @@ int CmdHFEMVExec(const char *cmd) {
                                if (!UDOL)
                                        PrintAndLog("Use default UDOL.");
 
-                               struct tlv *udol_data_tlv = dol_process(UDOL ? UDOL : &defUDOL, tlvRoot, 0x01); // 0x01 - fake tag!
+                               struct tlv *udol_data_tlv = dol_process(UDOL ? UDOL : &defUDOL, tlvRoot, 0x01); // 0x01 - dummy tag
                                if (!udol_data_tlv){
                                        PrintAndLog("ERROR: can't create UDOL TLV.");
                                        return 4;
@@ -651,9 +767,6 @@ int CmdHFEMVExec(const char *cmd) {
                }
        }
        
-       // additional contacless EMV commands (fDDA, external authenticate)
-       
-       
        // DropField
        DropField();
        
index a69d7196011d11dd8130937c692651e1c1dd8758..1c5cbb9d24f6ab657f9442b5406714d3545008e1 100644 (file)
@@ -35,6 +35,7 @@ enum emv_tag_t {
        EMV_TAG_NUMERIC,
        EMV_TAG_YYMMDD,
        EMV_TAG_CVR,
+       EMV_TAG_CID,
 };
 
 struct emv_tag {
@@ -59,7 +60,7 @@ static const struct emv_tag_bit EMV_AIP[] = {
        { EMV_BIT(1, 4), "Terminal risk management is to be performed" },
        { EMV_BIT(1, 3), "Issuer authentication is supported" },
        { EMV_BIT(1, 2), "Reserved for use by the EMV Contactless Specifications" },
-       { EMV_BIT(1, 1), "CDA supported" },
+       { EMV_BIT(1, 1), "CDA supported (Combined Dynamic Data Authentication / Application Cryptogram Generation)" },
        { EMV_BIT(2, 8), "MSD is supported (Magnetic Stripe Data)" },
        { EMV_BIT(2, 7), "Reserved for use by the EMV Contactless Specifications" },
        { EMV_BIT(2, 6), "Reserved for use by the EMV Contactless Specifications" },
@@ -233,7 +234,7 @@ static const struct emv_tag emv_tags[] = {
        { 0x9f1f, "Track 1 Discretionary Data", EMV_TAG_STRING },
        { 0x9f21, "Transaction Time" },
        { 0x9f26, "Application Cryptogram" },
-       { 0x9f27, "Cryptogram Information Data" },
+       { 0x9f27, "Cryptogram Information Data", EMV_TAG_CID },
        { 0x9f2a, "Kernel Identifier" },
        { 0x9f2d, "ICC PIN Encipherment Public Key Certificate" },
        { 0x9f2e, "ICC PIN Encipherment Public Key Exponent" },
@@ -471,6 +472,50 @@ static void emv_tag_dump_cvr(const struct tlv *tlv, const struct emv_tag *tag, F
        return;
 }
 
+// EMV Book 3
+static void emv_tag_dump_cid(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) {
+       if (!tlv || tlv->len < 1) {
+               PRINT_INDENT(level);
+               fprintf(f, "\tINVALID!\n");
+               return;
+       }
+       
+       PRINT_INDENT(level);
+       if ((tlv->value[0] & 0xC0) == 0x00)     fprintf(f, "\tAC1: AAC (Transaction declined)\n");
+       if ((tlv->value[0] & 0xC0) == 0x40)     fprintf(f, "\tAC1: TC (Transaction approved)\n");
+       if ((tlv->value[0] & 0xC0) == 0x80)     fprintf(f, "\tAC1: ARQC (Online authorisation requested)\n");
+       if ((tlv->value[0] & 0xC0) == 0xC0)     fprintf(f, "\tAC1: RFU\n");
+
+       if ((tlv->value[0] & 0x08) != 0x00) {
+               PRINT_INDENT(level);
+               fprintf(f, "\tAdvice required!\n");
+       }
+
+       if ((tlv->value[0] & 0x07) != 0x00) {
+               PRINT_INDENT(level);
+               fprintf(f, "\tReason/advice/referral code: ");
+               switch((tlv->value[0] & 0x07)) {
+                       case 0:
+                               fprintf(f, "No information given\n");
+                               break;
+                       case 1:
+                               fprintf(f, "Service not allowed\n");
+                               break;
+                       case 2:
+                               fprintf(f, "PIN Try Limit exceeded\n");
+                               break;
+                       case 3:
+                               fprintf(f, "Issuer authentication failed\n");
+                               break;
+                       default:
+                               fprintf(f, "\tRFU: %2x\n", (tlv->value[0] & 0x07));
+                               break;
+               }
+       }
+
+       return;
+}
+
 static void emv_tag_dump_cvm_list(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level)
 {
        uint32_t X, Y;
@@ -628,6 +673,10 @@ bool emv_tag_dump(const struct tlv *tlv, FILE *f, int level)
                fprintf(f, "\n");
                emv_tag_dump_cvr(tlv, tag, f, level);
                break;
+       case EMV_TAG_CID:
+               fprintf(f, "\n");
+               emv_tag_dump_cid(tlv, tag, f, level);
+               break;
        };
 
        return true;
index 825e9bf949d138efd447387ef872b5412574bcf1..36438413a25882f447e96a4c3aa55ed736253e04 100644 (file)
@@ -242,13 +242,13 @@ int EMVExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, uint8_t *Re
        // 6 byes + data = INS + CLA + P1 + P2 + Lc + <data = Nc> + Le
        int res = ExchangeAPDU14a(data, 6 + apdu.Lc, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen);
        
-       if (APDULogging)
-               PrintAndLog("<<<< %s", sprint_hex(Result, *ResultLen));
-       
        if (res) {
                return res;
        }
 
+       if (APDULogging)
+               PrintAndLog("<<<< %s", sprint_hex(Result, *ResultLen));
+
        *ResultLen -= 2;
        isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1];
        if (sw)
@@ -467,3 +467,29 @@ int EMVGenerateChallenge(bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen
 int MSCComputeCryptoChecksum(bool LeaveFieldON, uint8_t *UDOL, uint8_t UDOLlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
        return EMVExchange(LeaveFieldON, (sAPDU){0x80, 0x2a, 0x8e, 0x80, UDOLlen, UDOL}, Result, MaxResultLen, ResultLen, sw, tlv);
 }
+
+// Authentication 
+int trSDA(uint8_t *AID, size_t AIDlen, struct tlvdb *tlv) {
+       if (AIDlen < 5) 
+               return 1;
+       
+       // Get public key index (0x8F)
+       //int PubKeyIndx = 0; 
+       
+       // Get public key from key storage
+       // GetPublicKey(AID(0..5), PubKeyIndx)
+       
+       // Processing of Issuer Public Key Certificate (0x90)
+       //Certificate = 
+       
+       // check issuer public key certificate
+       
+       // Verification of Signed Static Application Data (SSAD) (0x93)
+       
+       // get 9F4A Static Data Authentication Tag List
+       
+       // set Data Auth Code (9F45) from SSAD 
+       
+       return 0;
+}
+
index 03d5c956c232fbaae1ff54e7861320e4c0c643f9..94c5d9b02ca625c345fee6e0332377ba7bfe9097 100644 (file)
 #define APDU_RES_LEN 260
 #define APDU_AID_LEN 50
 
+// AC
+# define EMVAC_AC_MASK 0xC0
+# define EMVAC_AAC     0x00
+# define EMVAC_TC      0x40
+# define EMVAC_ARQC    0x80
+# define EMVAC_CDAREQ  0x10
+
+enum TransactionType {
+       TT_MSD,
+       TT_VSDC,        // not standart for contactless!!!!
+       TT_QVSDCMCHIP,
+       TT_CDA,
+};
+
 typedef struct {
        uint8_t CLA;
        uint8_t INS;
@@ -69,8 +83,13 @@ extern int EMVSelectApplication(struct tlvdb *tlv, uint8_t *AID, size_t *AIDlen)
 // Get Processing Options
 extern int EMVGPO(bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
 extern int EMVReadRecord(bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
+// AC
+extern int EMVGenerateChallenge(bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
+extern int EMVAC(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);
 // Mastercard
 int MSCComputeCryptoChecksum(bool LeaveFieldON, uint8_t *UDOL, uint8_t UDOLlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
+// Auth
+extern int trSDA(uint8_t *AID, size_t AIDlen, struct tlvdb *tlv);
 
 #endif
 
Impressum, Datenschutz