From 1511ea28a8cc647a6fc462e9c182622a43df1318 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Mon, 21 Jan 2019 19:26:54 +0100 Subject: [PATCH] Add ROCA vulnerability test (RRG repository PR 76 by @merlokk) (#762) --- client/Makefile | 5 +- client/emv/cmdemv.c | 254 +++++++++++++++++++++++++++++++++-- client/emv/cmdemv.h | 24 +--- client/emv/emv_roca.c | 193 ++++++++++++++++++++++++++ client/emv/emv_roca.h | 36 +++++ client/emv/emvcore.c | 16 ++- client/emv/emvcore.h | 6 +- client/emv/test/cryptotest.c | 4 + 8 files changed, 490 insertions(+), 48 deletions(-) create mode 100644 client/emv/emv_roca.c create mode 100644 client/emv/emv_roca.h diff --git a/client/Makefile b/client/Makefile index 9ad8efdd..81b46f23 100644 --- a/client/Makefile +++ b/client/Makefile @@ -156,6 +156,7 @@ CMDSRCS = $(SRC_SMARTCARD) \ emv/test/dda_test.c\ emv/test/cda_test.c\ emv/cmdemv.c\ + emv/emv_roca.c \ cmdhf.c \ cmdhflist.c \ cmdhf14a.c \ @@ -250,7 +251,7 @@ endif 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) @@ -295,7 +296,7 @@ lua_build: jansson_build: @echo Compiling jansson - cd ./jansson && make all + cd $(JANSSONLIBPATH) && make all mbedtls_build: @echo Compiling mbedtls diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index d9a43cb9..e7676501 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -8,14 +8,20 @@ // EMV commands //----------------------------------------------------------------------------- +#include "cmdemv.h" + #include +#include "proxmark3.h" +#include "cmdparser.h" #include "mifare.h" -#include "cmdemv.h" #include "emvjson.h" #include "emv_pki.h" +#include "emvcore.h" #include "test/cryptotest.h" #include "cliparser/cliparser.h" -#include +#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) { @@ -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"), -#ifdef WITH_SMARTCARD arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."), -#endif - arg_strx1(NULL, NULL, "", NULL), + arg_strx1(NULL, NULL, "", NULL), arg_param_end }; CLIExecWithReturn(cmd, argtable, true); @@ -544,8 +548,11 @@ int CmdEMVInternalAuthenticate(const char *cmd) { 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"); @@ -720,7 +727,8 @@ int CmdEMVExec(const char *cmd) { 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[] = { @@ -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."), -#ifdef WITH_SMARTCARD arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."), -#endif 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 + uint8_t psenum = (channel == ECC_CONTACT) ? 1 : 2; CLIParserFree(); SetAPDULogging(showAPDU); @@ -776,7 +783,7 @@ int CmdEMVExec(const char *cmd) { // 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) { @@ -1212,12 +1219,14 @@ int CmdEMVScan(const char *cmd) { char *crelfname = (char *)relfname; int relfnamelen = 0; #ifdef WITH_SMARTCARD - if (arg_get_lit(11)) + if (arg_get_lit(11)) { channel = ECC_CONTACT; + } CLIGetStrWithReturn(12, relfname, &relfnamelen); #else CLIGetStrWithReturn(11, relfname, &relfnamelen); #endif + uint8_t psenum = (channel == ECC_CONTACT) ? 1 : 2; CLIParserFree(); SetAPDULogging(showAPDU); @@ -1292,7 +1301,7 @@ int CmdEMVScan(const char *cmd) { 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) { @@ -1502,7 +1511,225 @@ int CmdEMVTest(const char *cmd) { 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); + 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."}, + {"roca", CmdEMVRoca, 0, "Extract public keys and run ROCA test"}, {NULL, NULL, 0, NULL} }; diff --git a/client/emv/cmdemv.h b/client/emv/cmdemv.h index 3dc76fa9..1a9da118 100644 --- a/client/emv/cmdemv.h +++ b/client/emv/cmdemv.h @@ -11,28 +11,6 @@ #ifndef CMDEMV_H__ #define CMDEMV_H__ -#include -#include -#include -#include -#include -#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 diff --git a/client/emv/emv_roca.c b/client/emv/emv_roca.c new file mode 100644 index 00000000..3701ddf8 --- /dev/null +++ b/client/emv/emv_roca.c @@ -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 . + */ +//----------------------------------------------------------------------------- +// EMV roca commands +//----------------------------------------------------------------------------- + +#include "emv_roca.h" + +#include +#include +#include + +#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 index 00000000..b0b9a0ea --- /dev/null +++ b/client/emv/emv_roca.h @@ -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 . + */ +//----------------------------------------------------------------------------- +// EMV roca commands +//----------------------------------------------------------------------------- + +#ifndef EMV_ROCA_H__ +#define EMV_ROCA_H__ + +#include +#include + +#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 + diff --git a/client/emv/emvcore.c b/client/emv/emvcore.c index 0af75cdc..23aa5e38 100644 --- a/client/emv/emvcore.c +++ b/client/emv/emvcore.c @@ -123,7 +123,7 @@ static bool print_cb(void *data, const struct tlv *tlv, int level, bool is_leaf) 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) { @@ -131,9 +131,11 @@ void TLVPrintFromBuffer(uint8_t *data, int datalen) { tlvdb_visit(t, print_cb, NULL, 0); tlvdb_free(t); + return true; } else { PrintAndLogEx(WARNING, "TLV ERROR: Can't parse response as TLV tree."); } + return false; } void TLVPrintFromTLVLev(struct tlvdb *tlv, int level) { @@ -335,14 +337,14 @@ int EMVSelectPSE(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldO 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 - 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; @@ -522,7 +524,7 @@ int MSCComputeCryptoChecksum(EMVCommandChannel channel, bool LeaveFieldON, uint8 } // 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); @@ -903,7 +905,7 @@ int RecoveryCertificates(struct tlvdb *tlvRoot, json_t *root) { 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], @@ -926,10 +928,10 @@ int RecoveryCertificates(struct tlvdb *tlvRoot, json_t *root) { 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; } - 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], diff --git a/client/emv/emvcore.h b/client/emv/emvcore.h index d8b6a5c7..7d53e83b 100644 --- a/client/emv/emvcore.h +++ b/client/emv/emvcore.h @@ -65,7 +65,7 @@ enum CardPSVendor { }; 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); @@ -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); - // 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); @@ -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 struct emv_pk *get_ca_pk(struct tlvdb *db); #endif diff --git a/client/emv/test/cryptotest.c b/client/emv/test/cryptotest.c index 1d5891fe..b0212a70 100644 --- a/client/emv/test/cryptotest.c +++ b/client/emv/test/cryptotest.c @@ -31,6 +31,7 @@ #include "dda_test.h" #include "cda_test.h" #include "crypto/libpcrypto.h" +#include "emv/emv_roca.h" int ExecuteCryptoTests(bool verbose) { int res; @@ -90,6 +91,9 @@ int ExecuteCryptoTests(bool verbose) { 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]."); -- 2.39.2