Add ROCA vulnerability test (RRG repository PR 76 by @merlokk) (#762)
authorpwpiwi <pwpiwi@users.noreply.github.com>
Mon, 21 Jan 2019 18:26:54 +0000 (19:26 +0100)
committerGitHub <noreply@github.com>
Mon, 21 Jan 2019 18:26:54 +0000 (19:26 +0100)
client/Makefile
client/emv/cmdemv.c
client/emv/cmdemv.h
client/emv/emv_roca.c [new file with mode: 0644]
client/emv/emv_roca.h [new file with mode: 0644]
client/emv/emvcore.c
client/emv/emvcore.h
client/emv/test/cryptotest.c

index 9ad8efdd6e906bae3ab5f986b3cdc0c12761ed7c..81b46f235a7e817ec88c9bde5dba6e9fc2f4cace 100644 (file)
@@ -156,6 +156,7 @@ CMDSRCS =   $(SRC_SMARTCARD) \
                        emv/test/dda_test.c\
                        emv/test/cda_test.c\
                        emv/cmdemv.c\
                        emv/test/dda_test.c\
                        emv/test/cda_test.c\
                        emv/cmdemv.c\
+                       emv/emv_roca.c \
                        cmdhf.c \
                        cmdhflist.c \
                        cmdhf14a.c \
                        cmdhf.c \
                        cmdhflist.c \
                        cmdhf14a.c \
@@ -250,7 +251,7 @@ endif
                        
 BINS = proxmark3 flasher fpga_compress
 WINBINS = $(patsubst %, %.exe, $(BINS))
                        
 BINS = proxmark3 flasher fpga_compress
 WINBINS = $(patsubst %, %.exe, $(BINS))
-CLEAN = $(BINS) $(WINBINS) $(COREOBJS) $(CMDOBJS) $(OBJCOBJS) $(ZLIBOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(OBJDIR)/*.o *.moc.cpp ui/ui_overlays.h
+CLEAN = $(BINS) $(WINBINS) $(COREOBJS) $(CMDOBJS) $(OBJCOBJS) $(ZLIBOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(OBJDIR)/*.o *.moc.cpp ui/ui_overlays.h lualibs/usb_cmd.lua
 
 # need to assign dependancies to build these first...
 all: lua_build jansson_build mbedtls_build cbor_build $(BINS)
 
 # need to assign dependancies to build these first...
 all: lua_build jansson_build mbedtls_build cbor_build $(BINS)
@@ -295,7 +296,7 @@ lua_build:
        
 jansson_build:
        @echo Compiling jansson
        
 jansson_build:
        @echo Compiling jansson
-       cd ./jansson && make all
+       cd $(JANSSONLIBPATH) && make all
        
 mbedtls_build:
        @echo Compiling mbedtls
        
 mbedtls_build:
        @echo Compiling mbedtls
index d9a43cb9a305fa972372720601934386490fec24..e7676501edf27a90bee12c5412cb07718381aef0 100644 (file)
@@ -8,14 +8,20 @@
 // EMV commands
 //-----------------------------------------------------------------------------
 
 // EMV commands
 //-----------------------------------------------------------------------------
 
+#include "cmdemv.h"
+
 #include <ctype.h>
 #include <ctype.h>
+#include "proxmark3.h"
+#include "cmdparser.h"
 #include "mifare.h"
 #include "mifare.h"
-#include "cmdemv.h"
 #include "emvjson.h"
 #include "emv_pki.h"
 #include "emvjson.h"
 #include "emv_pki.h"
+#include "emvcore.h"
 #include "test/cryptotest.h"
 #include "cliparser/cliparser.h"
 #include "test/cryptotest.h"
 #include "cliparser/cliparser.h"
-#include <jansson.h>
+#include "jansson.h"
+#include "emv_roca.h"
+
 
 #define TLV_ADD(tag, value)( tlvdb_change_or_add_node(tlvRoot, tag, sizeof(value) - 1, (const unsigned char *)value) )
 void ParamLoadDefaults(struct tlvdb *tlvRoot) {
 
 #define TLV_ADD(tag, value)( tlvdb_change_or_add_node(tlvRoot, tag, sizeof(value) - 1, (const unsigned char *)value) )
 void ParamLoadDefaults(struct tlvdb *tlvRoot) {
@@ -323,10 +329,8 @@ int CmdEMVReadRecord(const char *cmd) {
                arg_lit0("kK",  "keep",    "keep field ON for next command"),
                arg_lit0("aA",  "apdu",    "show APDU reqests and responses"),
                arg_lit0("tT",  "tlv",     "TLV decode results of selected applets"),
                arg_lit0("kK",  "keep",    "keep field ON for next command"),
                arg_lit0("aA",  "apdu",    "show APDU reqests and responses"),
                arg_lit0("tT",  "tlv",     "TLV decode results of selected applets"),
-#ifdef WITH_SMARTCARD
                arg_lit0("wW",  "wired",   "Send data via contact (iso7816) interface. Contactless interface set by default."),
                arg_lit0("wW",  "wired",   "Send data via contact (iso7816) interface. Contactless interface set by default."),
-#endif
-               arg_strx1(NULL,  NULL,     "<SFI 1byte HEX><SFIrec 1byte HEX>", NULL),
+               arg_strx1(NULL,  NULL,     "<SFI 1byte HEX><SFIrecord 1byte HEX>", NULL),
                arg_param_end
        };
        CLIExecWithReturn(cmd, argtable, true);
                arg_param_end
        };
        CLIExecWithReturn(cmd, argtable, true);
@@ -544,8 +548,11 @@ int CmdEMVInternalAuthenticate(const char *cmd) {
        int datalen = 0;
 
        CLIParserInit("emv intauth", 
        int datalen = 0;
 
        CLIParserInit("emv intauth", 
-               "Generate Internal Authenticate command. Usually needs 4-byte random number. It returns data in TLV format .\nNeeds a EMV applet to be selected and GPO to be executed.", 
-               "Usage:\n\temv intauth -k 01020304 -> execute Internal Authenticate with 4-byte DDOLdata and keep field ON after command\n"
+               "Generate Internal Authenticate command. Usually needs 4-byte random number. It returns data in TLV format .\n"
+               "Needs a EMV applet to be selected and GPO to be executed.", 
+               
+               "Usage:\n"
+               "\temv intauth -k 01020304 -> execute Internal Authenticate with 4-byte DDOLdata and keep field ON after command\n"
                        "\temv intauth -t 01020304 -> execute Internal Authenticate with 4-byte DDOL data, show result in TLV\n"
                        "\temv intauth -pmt 9F 37 04 -> load params from file, make DDOL data from DDOL, Internal Authenticate with DDOL, show result in TLV"); 
 
                        "\temv intauth -t 01020304 -> execute Internal Authenticate with 4-byte DDOL data, show result in TLV\n"
                        "\temv intauth -pmt 9F 37 04 -> load params from file, make DDOL data from DDOL, Internal Authenticate with DDOL, show result in TLV"); 
 
@@ -720,7 +727,8 @@ int CmdEMVExec(const char *cmd) {
 
        CLIParserInit("emv exec", 
                "Executes EMV contactless transaction", 
 
        CLIParserInit("emv exec", 
                "Executes EMV contactless transaction", 
-               "Usage:\n\temv exec -sat -> select card, execute MSD transaction, show APDU and TLV\n"
+               "Usage:\n"
+                       "\temv exec -sat -> select card, execute MSD transaction, show APDU and TLV\n"
                        "\temv exec -satc -> select card, execute CDA transaction, show APDU and TLV\n");
 
        void* argtable[] = {
                        "\temv exec -satc -> select card, execute CDA transaction, show APDU and TLV\n");
 
        void* argtable[] = {
@@ -735,9 +743,7 @@ int CmdEMVExec(const char *cmd) {
                arg_lit0("cC",  "qvsdccda", "Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)."),
                arg_lit0("xX",  "vsdc",     "Transaction type - VSDC. For test only. Not a standart behavior."),
                arg_lit0("gG",  "acgpo",    "VISA. generate AC from GPO."),
                arg_lit0("cC",  "qvsdccda", "Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)."),
                arg_lit0("xX",  "vsdc",     "Transaction type - VSDC. For test only. Not a standart behavior."),
                arg_lit0("gG",  "acgpo",    "VISA. generate AC from GPO."),
-#ifdef WITH_SMARTCARD
                arg_lit0("wW",  "wired",   "Send data via contact (iso7816) interface. Contactless interface set by default."),
                arg_lit0("wW",  "wired",   "Send data via contact (iso7816) interface. Contactless interface set by default."),
-#endif
                arg_param_end
        };
        CLIExecWithReturn(cmd, argtable, true);
                arg_param_end
        };
        CLIExecWithReturn(cmd, argtable, true);
@@ -762,6 +768,7 @@ int CmdEMVExec(const char *cmd) {
        if (arg_get_lit(11))
                channel = ECC_CONTACT;
 #endif
        if (arg_get_lit(11))
                channel = ECC_CONTACT;
 #endif
+       uint8_t psenum = (channel == ECC_CONTACT) ? 1 : 2;
        CLIParserFree();
        
        SetAPDULogging(showAPDU);
        CLIParserFree();
        
        SetAPDULogging(showAPDU);
@@ -776,7 +783,7 @@ int CmdEMVExec(const char *cmd) {
                // PPSE
                PrintAndLogEx(NORMAL, "\n* PPSE.");
                SetAPDULogging(showAPDU);
                // PPSE
                PrintAndLogEx(NORMAL, "\n* PPSE.");
                SetAPDULogging(showAPDU);
-               res = EMVSearchPSE(channel, activateField, true, decodeTLV, tlvSelect);
+               res = EMVSearchPSE(channel, activateField, true, psenum, decodeTLV, tlvSelect);
 
                // check PPSE and select application id
                if (!res) { 
 
                // check PPSE and select application id
                if (!res) { 
@@ -1212,12 +1219,14 @@ int CmdEMVScan(const char *cmd) {
        char *crelfname = (char *)relfname;
        int relfnamelen = 0;
 #ifdef WITH_SMARTCARD  
        char *crelfname = (char *)relfname;
        int relfnamelen = 0;
 #ifdef WITH_SMARTCARD  
-       if (arg_get_lit(11))
+       if (arg_get_lit(11)) {
                channel = ECC_CONTACT;
                channel = ECC_CONTACT;
+       }
        CLIGetStrWithReturn(12, relfname, &relfnamelen);
 #else
        CLIGetStrWithReturn(11, relfname, &relfnamelen);
 #endif
        CLIGetStrWithReturn(12, relfname, &relfnamelen);
 #else
        CLIGetStrWithReturn(11, relfname, &relfnamelen);
 #endif
+       uint8_t psenum = (channel == ECC_CONTACT) ? 1 : 2;
        CLIParserFree();
        
        SetAPDULogging(showAPDU);
        CLIParserFree();
        
        SetAPDULogging(showAPDU);
@@ -1292,7 +1301,7 @@ int CmdEMVScan(const char *cmd) {
                tlvdb_free(fci);
        }
 
                tlvdb_free(fci);
        }
 
-       res = EMVSearchPSE(channel, false, true, decodeTLV, tlvSelect);
+       res = EMVSearchPSE(channel, false, true, psenum, decodeTLV, tlvSelect);
 
        // check PPSE and select application id
        if (!res) { 
 
        // check PPSE and select application id
        if (!res) { 
@@ -1502,7 +1511,225 @@ int CmdEMVTest(const char *cmd) {
        return ExecuteCryptoTests(true);
 }
 
        return ExecuteCryptoTests(true);
 }
 
+int CmdEMVRoca(const char *cmd) {
+       uint8_t AID[APDU_AID_LEN] = {0};
+       size_t AIDlen = 0;
+       uint8_t buf[APDU_RES_LEN] = {0};
+       size_t len = 0;
+       uint16_t sw = 0;
+       int res;
+               
+       CLIParserInit("emv roca", 
+               "Tries to extract public keys and run the ROCA test against them.\n", 
+               "Usage:\n"
+                       "\temv roca -w -> select CONTACT card and run test\n\temv roca -> select CONTACTLESS card and run test\n");
+
+       void* argtable[] = {
+               arg_param_begin,
+               arg_lit0("wW",  "wired",   "Send data via contact (iso7816) interface. Contactless interface set by default."),
+               arg_param_end
+       };
+       CLIExecWithReturn(cmd, argtable, true);
+       
+       EMVCommandChannel channel = ECC_CONTACTLESS;
+       if (arg_get_lit(1))
+               channel = ECC_CONTACT;
+
+       // select card
+       uint8_t psenum = (channel == ECC_CONTACT) ? 1 : 2;
+       
+       SetAPDULogging(false);
+       
+       // init applets list tree
+       const char *al = "Applets list";
+       struct tlvdb *tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al);
+
+       // EMV PPSE
+       PrintAndLogEx(NORMAL, "--> PPSE.");
+       res = EMVSearchPSE(channel, false, true, psenum, false, tlvSelect);
+
+       // check PPSE and select application id
+       if (!res) {     
+               TLVPrintAIDlistFromSelectTLV(tlvSelect);                
+       } else {
+               // EMV SEARCH with AID list
+               PrintAndLogEx(NORMAL, "--> AID search.");
+               if (EMVSearch(channel, false, true, false, tlvSelect)) {
+                       PrintAndLogEx(ERR, "Can't found any of EMV AID. Exit...");
+                       tlvdb_free(tlvSelect);
+                       DropField();
+                       return 3;
+               }
+
+               // check search and select application id
+               TLVPrintAIDlistFromSelectTLV(tlvSelect);
+       }
+
+       // EMV SELECT application
+       SetAPDULogging(false);
+       EMVSelectApplication(tlvSelect, AID, &AIDlen);
+
+       tlvdb_free(tlvSelect);
+
+       if (!AIDlen) {
+               PrintAndLogEx(INFO, "Can't select AID. EMV AID not found. Exit...");
+               DropField();
+               return 4;
+       }
+
+       // 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));
+       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;
+       }
+
+       PrintAndLog("\n* Init transaction parameters.");
+       InitTransactionParameters(tlvRoot, true, TT_QVSDCMCHIP, false);
+
+       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, false);
+       
+       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;
+               }
+
+               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;
+                               }
+                       }
+               }
+               
+               break;
+       }
+
+       // getting certificates
+       if (tlvdb_get(tlvRoot, 0x90, NULL)) {
+               PrintAndLogEx(INFO, "-->Recovering certificates.");
+               PKISetStrictExecution(false);
+
+               struct emv_pk *pk = get_ca_pk(tlvRoot);
+               if (!pk) {
+                       PrintAndLogEx(ERR, "ERROR: Key not found. Exit.");
+                       goto out;
+               }
+
+               struct emv_pk *issuer_pk = emv_pki_recover_issuer_cert(pk, tlvRoot);
+               if (!issuer_pk) {
+                       emv_pk_free(pk);
+                       PrintAndLogEx(WARNING, "WARNING: Issuer certificate not found. Exit.");
+                       goto out;
+               }
+       
+               PrintAndLogEx(SUCCESS, "Issuer PK recovered. RID %s IDX %02hhx CSN %s",
+                               sprint_hex(issuer_pk->rid, 5),
+                               issuer_pk->index,
+                               sprint_hex(issuer_pk->serial, 3)
+                               );
+
+
+               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.");
+                       goto out;
+               }
+               PrintAndLogEx(SUCCESS, "ICC PK recovered. RID %s IDX %02hhx CSN %s\n",
+                               sprint_hex(icc_pk->rid, 5),
+                               icc_pk->index,
+                               sprint_hex(icc_pk->serial, 3)
+                               );
+               
+               PrintAndLogEx(INFO, "ICC pk modulus: %s", sprint_hex_inrow(icc_pk->modulus, icc_pk->mlen));
+               
+               //      icc_pk->exp, icc_pk->elen
+               //      icc_pk->modulus, icc_pk->mlen
+               if (icc_pk->elen > 0 && icc_pk->mlen > 0) {
+                       if (emv_rocacheck(icc_pk->modulus, icc_pk->mlen, true)) {
+                               PrintAndLogEx(INFO, "ICC pk is a subject to ROCA vulnerability, insecure..");
+                       } else {
+                               PrintAndLogEx(INFO, "ICC pk is OK(");
+                       }
+               }               
+               
+               PKISetStrictExecution(true);
+       }
+
+out:
+       
+       // free tlv object
+       tlvdb_free(tlvRoot);
+
+       if ( channel == ECC_CONTACTLESS)
+               DropField();
+
+
+       return 0;
+}
+
 int CmdHelp(const char *Cmd);
 int CmdHelp(const char *Cmd);
+
 static command_t CommandTable[] =  {
        {"help",        CmdHelp,                    1,  "This help"},
        {"exec",        CmdEMVExec,                 0,  "Executes EMV contactless transaction."},
 static command_t CommandTable[] =  {
        {"help",        CmdHelp,                    1,  "This help"},
        {"exec",        CmdEMVExec,                 0,  "Executes EMV contactless transaction."},
@@ -1516,6 +1743,7 @@ static command_t CommandTable[] =  {
        {"intauth",     CmdEMVInternalAuthenticate, 0,  "Internal authentication."},
        {"scan",        CmdEMVScan,                 0,  "Scan EMV card and save it contents to json file for emulator."},
        {"test",        CmdEMVTest,                 0,  "Crypto logic test."},
        {"intauth",     CmdEMVInternalAuthenticate, 0,  "Internal authentication."},
        {"scan",        CmdEMVScan,                 0,  "Scan EMV card and save it contents to json file for emulator."},
        {"test",        CmdEMVTest,                 0,  "Crypto logic test."},
+       {"roca",        CmdEMVRoca,                 0,  "Extract public keys and run ROCA test"}, 
        {NULL,          NULL,                       0,  NULL}
 };
 
        {NULL,          NULL,                       0,  NULL}
 };
 
index 3dc76fa969d51d4b1cc5153db125916cbab609af..1a9da11807e5d0d8d75eb15f08b1d2683bf8ecac 100644 (file)
 #ifndef CMDEMV_H__
 #define CMDEMV_H__
 
 #ifndef CMDEMV_H__
 #define CMDEMV_H__
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <inttypes.h>
-#include <string.h>
-#include <ctype.h>
-#include "proxmark3.h"
-#include "ui.h"
-#include "cmdparser.h"
-#include "common.h"
-#include "util.h"
-#include "util_posix.h"
-#include "cmdmain.h"
-#include "emvcore.h"
-#include "apduinfo.h"
-
-int CmdEMV(const char *Cmd);
-
-extern int CmdEMVSelect(const char *cmd);
-extern int CmdEMVSearch(const char *cmd);
-extern int CmdEMVPPSE(const char *cmd);
-extern int CmdEMVExec(const char *cmd);
-extern int CmdEMVGetrng(const char *Cmd);
-extern int CmdEMVList(const char *Cmd);
+extern int CmdEMV(const char *Cmd);
 
 #endif
 
 #endif
diff --git a/client/emv/emv_roca.c b/client/emv/emv_roca.c
new file mode 100644 (file)
index 0000000..3701ddf
--- /dev/null
@@ -0,0 +1,193 @@
+/* roca.c - ROCA (CVE-2017-15361) fingerprint checker.
+ * Written by Rob Stradling (based on https://github.com/crocs-muni/roca/blob/master/roca/detect.py)
+ * Copyright (C) 2017-2018 Sectigo Limited
+ * modified 2018 iceman  (dropped openssl bignum,  now use mbedtls lib)
+ * modified 2018 merlok
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+//-----------------------------------------------------------------------------
+// EMV roca commands
+//-----------------------------------------------------------------------------
+
+#include "emv_roca.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "ui.h"
+#include "mbedtls/bignum.h"
+
+
+static uint8_t g_primes[ROCA_PRINTS_LENGTH] = {
+       11, 13, 17, 19, 37, 53, 61, 71, 73, 79, 97, 103, 107, 109, 127, 151, 157
+};
+
+mbedtls_mpi g_prints[ROCA_PRINTS_LENGTH];
+
+void rocacheck_init(void) {
+       
+       for (int i = 0; i < ROCA_PRINTS_LENGTH; i++)
+               mbedtls_mpi_init(&g_prints[i]);
+       
+       mbedtls_mpi_read_string(&g_prints[0], 10, "1026");
+       mbedtls_mpi_read_string(&g_prints[1], 10, "5658");
+       mbedtls_mpi_read_string(&g_prints[2], 10, "107286");
+       mbedtls_mpi_read_string(&g_prints[3], 10, "199410");
+       mbedtls_mpi_read_string(&g_prints[4], 10, "67109890");
+       mbedtls_mpi_read_string(&g_prints[5], 10, "5310023542746834");
+       mbedtls_mpi_read_string(&g_prints[6], 10, "1455791217086302986");
+       mbedtls_mpi_read_string(&g_prints[7], 10, "20052041432995567486");
+       mbedtls_mpi_read_string(&g_prints[8], 10, "6041388139249378920330");
+       mbedtls_mpi_read_string(&g_prints[9], 10, "207530445072488465666");
+       mbedtls_mpi_read_string(&g_prints[10], 10, "79228162521181866724264247298");
+       mbedtls_mpi_read_string(&g_prints[11], 10, "1760368345969468176824550810518");
+       mbedtls_mpi_read_string(&g_prints[12], 10, "50079290986288516948354744811034");
+       mbedtls_mpi_read_string(&g_prints[13], 10, "473022961816146413042658758988474");
+       mbedtls_mpi_read_string(&g_prints[14], 10, "144390480366845522447407333004847678774");
+       mbedtls_mpi_read_string(&g_prints[15], 10, "1800793591454480341970779146165214289059119882");
+       mbedtls_mpi_read_string(&g_prints[16], 10, "126304807362733370595828809000324029340048915994");
+}
+
+void rocacheck_cleanup(void) {
+       for (int i = 0; i < ROCA_PRINTS_LENGTH; i++)
+               mbedtls_mpi_free(&g_prints[i]);
+}
+
+int bitand_is_zero(    mbedtls_mpi* a, mbedtls_mpi* b ) {
+
+       for (int i = 0; i < mbedtls_mpi_bitlen(a); i++) {
+       
+               if (mbedtls_mpi_get_bit(a, i) && mbedtls_mpi_get_bit(b, i))
+                       return 0;
+       }
+       return 1;
+}
+
+
+mbedtls_mpi_uint mpi_get_uint(const mbedtls_mpi *X) {
+       
+       if (X->n == 1 && X->s > 0) {
+               return X->p[0];
+       }
+       printf("ZERRRRO!!!\n");
+       return 0;
+}
+
+void print_mpi(const char *msg, int radix, const mbedtls_mpi *X) {
+       
+       char Xchar[400] = {0};
+       size_t len = 0;
+       
+       mbedtls_mpi_write_string(X, radix, Xchar, sizeof(Xchar), &len); 
+       printf("%s[%zd] %s\n", msg, len, Xchar);        
+}
+
+bool emv_rocacheck(const unsigned char *buf, size_t buflen, bool verbose) {
+
+       mbedtls_mpi t_modulus;
+       mbedtls_mpi_init(&t_modulus);
+
+       bool ret = false;
+
+       rocacheck_init();
+
+       MBEDTLS_MPI_CHK( mbedtls_mpi_read_binary(&t_modulus, buf, buflen) );
+       
+       for (int i = 0; i < ROCA_PRINTS_LENGTH; i++) {
+
+               mbedtls_mpi t_temp;
+               mbedtls_mpi t_prime;
+               mbedtls_mpi g_one;
+               
+               mbedtls_mpi_init(&t_temp);
+               mbedtls_mpi_init(&t_prime);
+               mbedtls_mpi_init(&g_one);
+
+               MBEDTLS_MPI_CHK( mbedtls_mpi_read_string(&g_one, 10, "1") );
+
+               MBEDTLS_MPI_CHK( mbedtls_mpi_add_int(&t_prime, &t_prime, g_primes[i]) );
+               
+               MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi(&t_temp, &t_modulus, &t_prime) ); 
+               
+               MBEDTLS_MPI_CHK( mbedtls_mpi_shift_l(&g_one, mpi_get_uint(&t_temp)) );
+               
+               if (bitand_is_zero(&g_one, &g_prints[i])) {
+                       if (verbose)
+                               PrintAndLogEx(FAILED, "No fingerprint found.\n");
+                       goto cleanup;
+               }
+               
+               mbedtls_mpi_free(&g_one);               
+               mbedtls_mpi_free(&t_temp);
+               mbedtls_mpi_free(&t_prime);
+       }
+
+       ret = true;
+       if (verbose)
+               PrintAndLogEx(SUCCESS, "Fingerprint found!\n");
+
+cleanup:
+       mbedtls_mpi_free(&t_modulus);
+
+       rocacheck_cleanup();
+       return ret;
+}
+
+int roca_self_test( int verbose ) {
+       int ret = 0;
+
+       if( verbose != 0 )
+               printf( "\nROCA check vulnerability tests\n" );
+
+       // positive
+       uint8_t keyp[] = "\x94\x4e\x13\x20\x8a\x28\x0c\x37\xef\xc3\x1c\x31\x14\x48\x5e\x59"\
+                       "\x01\x92\xad\xbb\x8e\x11\xc8\x7c\xad\x60\xcd\xef\x00\x37\xce\x99"\
+                                       "\x27\x83\x30\xd3\xf4\x71\xa2\x53\x8f\xa6\x67\x80\x2e\xd2\xa3\xc4"\
+                                       "\x4a\x8b\x7d\xea\x82\x6e\x88\x8d\x0a\xa3\x41\xfd\x66\x4f\x7f\xa7";
+
+       if( verbose != 0 )
+               printf( "  ROCA positive test: " );
+       
+       if (emv_rocacheck(keyp, 64, false)) {
+               if( verbose != 0 )
+                       printf( "passed\n" );
+       } else {
+               ret = 1;
+               if( verbose != 0 )
+                       printf( "failed\n" );
+       }
+
+       // negative
+       uint8_t keyn[] = "\x84\x4e\x13\x20\x8a\x28\x0c\x37\xef\xc3\x1c\x31\x14\x48\x5e\x59"\
+                       "\x01\x92\xad\xbb\x8e\x11\xc8\x7c\xad\x60\xcd\xef\x00\x37\xce\x99"\
+                                       "\x27\x83\x30\xd3\xf4\x71\xa2\x53\x8f\xa6\x67\x80\x2e\xd2\xa3\xc4"\
+                                       "\x4a\x8b\x7d\xea\x82\x6e\x88\x8d\x0a\xa3\x41\xfd\x66\x4f\x7f\xa7";
+
+       if( verbose != 0 )
+               printf( "  ROCA negative test: " );
+
+       if (emv_rocacheck(keyn, 64, false)) {
+               ret = 1;
+               if( verbose != 0 )
+                       printf( "failed\n" );
+       } else {
+               if( verbose != 0 )
+                       printf( "passed\n" );
+       }
+
+       
+       return ret;
+}
diff --git a/client/emv/emv_roca.h b/client/emv/emv_roca.h
new file mode 100644 (file)
index 0000000..b0b9a0e
--- /dev/null
@@ -0,0 +1,36 @@
+/* roca.c - ROCA (CVE-2017-15361) fingerprint checker.
+ * Written by Rob Stradling (based on https://github.com/crocs-muni/roca/blob/master/roca/detect.py)
+ * Copyright (C) 2017-2018 Sectigo Limited
+ * modified 2018 iceman  (dropped openssl bignum,  now use mbedtls lib)
+ * modified 2018 merlok
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+//-----------------------------------------------------------------------------
+// EMV roca commands
+//-----------------------------------------------------------------------------
+
+#ifndef EMV_ROCA_H__
+#define EMV_ROCA_H__
+
+#include <stddef.h>
+#include <stdbool.h>
+
+#define ROCA_PRINTS_LENGTH     17
+
+extern bool emv_rocacheck( const unsigned char *buf, size_t buflen, bool verbose );
+extern int roca_self_test( int verbose );
+
+#endif
+
index 0af75cdc73b9f1c1bf547e67e137d96de01469dd..23aa5e387fd9cff23b9112b5fa8cfb802decc73d 100644 (file)
@@ -123,7 +123,7 @@ static bool print_cb(void *data, const struct tlv *tlv, int level, bool is_leaf)
        return true;
 }
 
        return true;
 }
 
-void TLVPrintFromBuffer(uint8_t *data, int datalen) {
+bool TLVPrintFromBuffer(uint8_t *data, int datalen) {
        struct tlvdb *t = NULL;
        t = tlvdb_parse_multi(data, datalen);
        if (t) {
        struct tlvdb *t = NULL;
        t = tlvdb_parse_multi(data, datalen);
        if (t) {
@@ -131,9 +131,11 @@ void TLVPrintFromBuffer(uint8_t *data, int datalen) {
 
                tlvdb_visit(t, print_cb, NULL, 0);
                tlvdb_free(t);
 
                tlvdb_visit(t, print_cb, NULL, 0);
                tlvdb_free(t);
+               return true;
        } else {
                PrintAndLogEx(WARNING, "TLV ERROR: Can't parse response as TLV tree.");
        }
        } else {
                PrintAndLogEx(WARNING, "TLV ERROR: Can't parse response as TLV tree.");
        }
+       return false;
 }
 
 void TLVPrintFromTLVLev(struct tlvdb *tlv, int level) {
 }
 
 void TLVPrintFromTLVLev(struct tlvdb *tlv, int level) {
@@ -335,14 +337,14 @@ int EMVSelectPSE(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldO
        return res;
 }
 
        return res;
 }
 
-int EMVSearchPSE(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv) {
+int EMVSearchPSE(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t PSENum, bool decodeTLV, struct tlvdb *tlv) {
        uint8_t data[APDU_RES_LEN] = {0};
        size_t datalen = 0;
        uint16_t sw = 0;
        int res;
 
        // select PPSE
        uint8_t data[APDU_RES_LEN] = {0};
        size_t datalen = 0;
        uint16_t sw = 0;
        int res;
 
        // select PPSE
-       res = EMVSelectPSE(channel, ActivateField, true, 2, data, sizeof(data), &datalen, &sw);
+       res = EMVSelectPSE(channel, ActivateField, true, PSENum, data, sizeof(data), &datalen, &sw);
 
        if (!res){
                struct tlvdb *t = NULL;
 
        if (!res){
                struct tlvdb *t = NULL;
@@ -522,7 +524,7 @@ int MSCComputeCryptoChecksum(EMVCommandChannel channel, bool LeaveFieldON, uint8
 }
 
 // Authentication
 }
 
 // Authentication
-static struct emv_pk *get_ca_pk(struct tlvdb *db) {
+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);
 
        const struct tlv *df_tlv = tlvdb_get(db, 0x84, NULL);
        const struct tlv *caidx_tlv = tlvdb_get(db, 0x8f, NULL);
 
@@ -903,7 +905,7 @@ int RecoveryCertificates(struct tlvdb *tlvRoot, json_t *root) {
                PrintAndLog("WARNING: Issuer certificate not found. Exit.");
                return 2;
        }
                PrintAndLog("WARNING: Issuer certificate not found. Exit.");
                return 2;
        }
-       PrintAndLog("Issuer PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx",
+       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[0],
                        issuer_pk->rid[1],
                        issuer_pk->rid[2],
@@ -926,10 +928,10 @@ int RecoveryCertificates(struct tlvdb *tlvRoot, json_t *root) {
        if (!icc_pk) {
                emv_pk_free(pk);
                emv_pk_free(issuer_pk);
        if (!icc_pk) {
                emv_pk_free(pk);
                emv_pk_free(issuer_pk);
-               PrintAndLog("WARNING: ICC certificate not found. Exit.");
+               PrintAndLogEx(WARNING, "WARNING: ICC certificate not found. Exit.");
                return 2;
        }
                return 2;
        }
-       printf("ICC PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx\n",
+       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[0],
                        icc_pk->rid[1],
                        icc_pk->rid[2],
index d8b6a5c77278201701c5218a0ff3de3a6ae63a0d..7d53e83bb15a8411b7f6c5e21eb75068ea6c1471 100644 (file)
@@ -65,7 +65,7 @@ enum CardPSVendor {
 };
 extern enum CardPSVendor GetCardPSVendor(uint8_t * AID, size_t AIDlen);
 
 };
 extern enum CardPSVendor GetCardPSVendor(uint8_t * AID, size_t AIDlen);
 
-extern void TLVPrintFromBuffer(uint8_t *data, int datalen);
+extern bool TLVPrintFromBuffer(uint8_t *data, int datalen);
 extern void TLVPrintFromTLV(struct tlvdb *tlv);
 extern void TLVPrintFromTLVLev(struct tlvdb *tlv, int level);
 extern void TLVPrintAIDlistFromSelectTLV(struct tlvdb *tlv);
 extern void TLVPrintFromTLV(struct tlvdb *tlv);
 extern void TLVPrintFromTLVLev(struct tlvdb *tlv, int level);
 extern void TLVPrintAIDlistFromSelectTLV(struct tlvdb *tlv);
@@ -78,9 +78,8 @@ extern void SetAPDULogging(bool logging);
 // exchange
 extern int EMVExchange(EMVCommandChannel channel, bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
 
 // exchange
 extern int EMVExchange(EMVCommandChannel channel, bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
 
-
 // search application
 // search application
-extern int EMVSearchPSE(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv);
+extern int EMVSearchPSE(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t PSENum, bool decodeTLV, struct tlvdb *tlv);
 extern int EMVSearch(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv);
 extern int EMVSelectPSE(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
 extern int EMVSelect(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
 extern int EMVSearch(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv);
 extern int EMVSelectPSE(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
 extern int EMVSelect(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
@@ -103,6 +102,7 @@ extern int trCDA(struct tlvdb *tlv, struct tlvdb *ac_tlv, struct tlv *pdol_data_
 
 extern int RecoveryCertificates(struct tlvdb *tlvRoot, json_t *root);
 
 
 extern int RecoveryCertificates(struct tlvdb *tlvRoot, json_t *root);
 
+extern struct emv_pk *get_ca_pk(struct tlvdb *db);
 #endif
 
 
 #endif
 
 
index 1d5891fe06cae649f3166b710cdd3642de17ce2c..b0212a70b453fe7d5a478c24eb834ae1c218684d 100644 (file)
@@ -31,6 +31,7 @@
 #include "dda_test.h"
 #include "cda_test.h"
 #include "crypto/libpcrypto.h"
 #include "dda_test.h"
 #include "cda_test.h"
 #include "crypto/libpcrypto.h"
+#include "emv/emv_roca.h"
 
 int ExecuteCryptoTests(bool verbose) {
        int res;
 
 int ExecuteCryptoTests(bool verbose) {
        int res;
@@ -90,6 +91,9 @@ int ExecuteCryptoTests(bool verbose) {
        res = exec_crypto_test(verbose);
        if (res) TestFail = true;
 
        res = exec_crypto_test(verbose);
        if (res) TestFail = true;
 
+       res = roca_self_test(verbose);
+       if (res) TestFail = true;
+
        PrintAndLog("\n--------------------------");
        if (TestFail)
                PrintAndLog("Test(s) [ERROR].");
        PrintAndLog("\n--------------------------");
        if (TestFail)
                PrintAndLog("Test(s) [ERROR].");
Impressum, Datenschutz