Merge branch 'master' of github.com:Proxmark/proxmark3 into travis
authormerlokk <olegmsn@gmail.com>
Fri, 5 Jan 2018 09:57:11 +0000 (11:57 +0200)
committermerlokk <olegmsn@gmail.com>
Fri, 5 Jan 2018 09:57:11 +0000 (11:57 +0200)
71 files changed:
CHANGELOG.md
appveyor.yml
armsrc/iso14443a.c
armsrc/lfsampling.c
armsrc/mifarecmd.c
client/Makefile
client/cmdcrc.c
client/cmdhf14a.c
client/cmdhficlass.c
client/cmdhficlass.h
client/cmdhfmf.c
client/cmdlft55xx.c
client/cmdmain.c
client/cmdscript.c
client/emv/capk.txt [new file with mode: 0644]
client/emv/cmdemv.c
client/emv/cmdemv.h
client/emv/crypto.c [new file with mode: 0644]
client/emv/crypto.h [new file with mode: 0644]
client/emv/crypto_backend.h [new file with mode: 0644]
client/emv/crypto_polarssl.c [new file with mode: 0644]
client/emv/emv_pk.c [new file with mode: 0644]
client/emv/emv_pk.h [new file with mode: 0644]
client/emv/emv_pki.c [new file with mode: 0644]
client/emv/emv_pki.h [new file with mode: 0644]
client/emv/emv_pki_priv.c [new file with mode: 0644]
client/emv/emv_pki_priv.h [new file with mode: 0644]
client/emv/emv_tags.c
client/emv/emv_tags.h
client/emv/emvcore.c
client/emv/emvcore.h
client/emv/test/cda_test.c [new file with mode: 0644]
client/emv/test/cda_test.h [new file with mode: 0644]
client/emv/test/crypto_test.c [new file with mode: 0644]
client/emv/test/crypto_test.h [new file with mode: 0644]
client/emv/test/cryptotest.c [new file with mode: 0644]
client/emv/test/cryptotest.h [new file with mode: 0644]
client/emv/test/dda_test.c [new file with mode: 0644]
client/emv/test/dda_test.h [new file with mode: 0644]
client/emv/test/sda_test.c [new file with mode: 0644]
client/emv/test/sda_test.h [new file with mode: 0644]
client/loclass/cipher.c
client/loclass/cipher.h
client/loclass/cipherutils.c
client/loclass/cipherutils.h
client/loclass/elite_crack.c
client/loclass/elite_crack.h
client/loclass/fileutils.c
client/loclass/fileutils.h
client/loclass/ikeys.c
client/loclass/ikeys.h
client/loclass/main.c
client/obj/emv/test/.dummy [new file with mode: 0644]
client/pm3_binlib.c
client/reveng/model.c
client/scripting.c
client/ui.c
client/util.c
client/util.h
common/polarssl/aes.h
common/polarssl/bignum.c [new file with mode: 0644]
common/polarssl/bignum.h [new file with mode: 0644]
common/polarssl/bn_mul.h [new file with mode: 0644]
common/polarssl/des.c
common/polarssl/polarssl_config.h
common/polarssl/rsa.c [new file with mode: 0644]
common/polarssl/rsa.h [new file with mode: 0644]
common/polarssl/sha1.c [new file with mode: 0644]
common/polarssl/sha1.h [new file with mode: 0644]
common/sha1.c [deleted file]
common/sha1.h [deleted file]

index fabd04b0d4b1cad8c7bb01f774006de729f0f20d..4162c638d30e5d196700dda0c3f1dfbeee5effba 100644 (file)
@@ -42,6 +42,10 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
 - Added `hf emv` group of commands (Merlok)
 - Added `hf emv search` `hf emv pse` - commands for selection of EMV application (Merlok)
 - Added `hf emv select` - command for select EMV application (Merlok)
+- Added `hf emv exec` - command for execute EMV transaction (Merlok)
+- Added to `hf emv exec` MSD path for VISA and Mastercard and some other compatible EMV cards (Merlok)
+- Added to `hf emv exec` SDA, DDA, fast DDA, CDA calculations for VISA and Mastercard and some other compatible EMV cards (Merlok)
+- Added `hf emv test` - crypto tests for DES, AES, SHA, RSA, SDA, DDA, CDA and some other crypto functions (Merlok)
 
 ## [3.0.1][2017-06-08]
 
index eb22114e7afd28ce45e9aebc357b9b1fe648268a..f5ae2c70a0390f3918c76f96a62bc6c664f31c74 100644 (file)
@@ -232,8 +232,8 @@ test_script:
     #--- end Job
 
       [bool]$res=$false
-      # Wait 40 sec timeout for Job
-      if(Wait-Job $Job -Timeout 40){
+      # Wait 120 sec timeout for Job
+      if(Wait-Job $Job -Timeout 120){
         $Results = $Job | Receive-Job
         if($Results -like "true"){
           $res=$true
@@ -278,6 +278,11 @@ test_script:
     ExecTest "hf mf hardnested" "hf mf hardnested" {bash -lc "cd ~/client;proxmark3 comx -c 'hf mf hardnested t 1 000000000000'"} "found:"
 
 
+    #proxmark crypto tests
+
+    ExecTest "hf emv test" "hf emv test" {bash -lc "cd ~/client;proxmark3 comx -c 'hf emv test'"} "Tests ?OK"
+
+
     if ($global:TestsPassed) {
       Write-Host "Tests [ OK ]" -ForegroundColor Green
     } else {
index a0fe232671e86418eda924b0c52857e58a03bb7e..94ca52f542e242de6816f04e4970dd7fdda5e986 100644 (file)
@@ -189,13 +189,14 @@ void iso14a_set_trigger(bool enable) {
 
 
 void iso14a_set_timeout(uint32_t timeout) {
-       iso14a_timeout = timeout - (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER)/(16*8);
+       // adjust timeout by FPGA delays and 2 additional ssp_frames to detect SOF
+       iso14a_timeout = timeout + (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER)/(16*8) + 2;
        if(MF_DBGLEVEL >= 3) Dbprintf("ISO14443A Timeout set to %ld (%dms)", timeout, timeout / 106);
 }
 
 
 uint32_t iso14a_get_timeout(void) {
-       return iso14a_timeout + (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER)/(16*8);
+       return iso14a_timeout - (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER)/(16*8) - 2;
 }
 
 //-----------------------------------------------------------------------------
@@ -1955,9 +1956,9 @@ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data) {
        } else{
                // S-Block WTX 
                while((data_bytes[0] & 0xF2) == 0xF2) {
-                       uint32_t save_iso14a_timeout = iso14a_timeout;
+                       uint32_t save_iso14a_timeout = iso14a_get_timeout();
                        // temporarily increase timeout
-                       iso14a_timeout = MAX((data_bytes[1] & 0x3f) * iso14a_timeout, MAX_ISO14A_TIMEOUT);
+                       iso14a_set_timeout(MAX((data_bytes[1] & 0x3f) * save_iso14a_timeout, MAX_ISO14A_TIMEOUT));
                        // Transmit WTX back 
                        // byte1 - WTXM [1..59]. command FWT=FWT*WTXM
                        data_bytes[1] = data_bytes[1] & 0x3f; // 2 high bits mandatory set to 0b
@@ -1969,7 +1970,7 @@ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data) {
                        len = ReaderReceive(data, parity);
                        data_bytes = data;
                        // restore timeout
-                       iso14a_timeout = save_iso14a_timeout;
+                       iso14a_set_timeout(save_iso14a_timeout);
                }
 
                // if we received an I- or R(ACK)-Block with a block number equal to the
index 999f56bb0ed7458abfd86a0ad3b843b924525bf7..084201a5a52c25825615896b1d8cd84ca29eecbf 100644 (file)
@@ -153,8 +153,10 @@ uint32_t DoAcquisition(uint8_t decimation, uint32_t bits_per_sample, bool averag
                        LED_D_OFF();
                        // threshold either high or low values 128 = center 0.  if trigger = 178 
                        if ((trigger_threshold > 0) && (sample < (trigger_threshold+128)) && (sample > (128-trigger_threshold))) { // 
-                               if (cancel_after > 0) cancel_counter++;
-                               if (cancel_after == cancel_counter) break;
+                               if (cancel_after > 0) {
+                                       cancel_counter++;
+                                       if (cancel_after == cancel_counter) break;
+                               }
                                continue;
                        }
                        trigger_threshold = 0;
index 00fd638c4daf7743a0be32669426e77acec81923..fcfd7e8fddb80f9c1bac092def66cd5fbb1d23c0 100644 (file)
@@ -694,10 +694,9 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags,
                        continue;\r
                }\r
 \r
-               // send a dummy byte as reader response in order to trigger the cards authentication timeout\r
-               uint8_t dummy_answer = 0;\r
-               ReaderTransmit(&dummy_answer, 1, NULL);\r
-               timeout = GetCountSspClk() + AUTHENTICATION_TIMEOUT;\r
+               // send a dummy response in order to trigger the cards authentication failure timeout\r
+               uint8_t dummy_answer[8] = {0};\r
+               ReaderTransmit(dummy_answer, 8, NULL);\r
 \r
                num_nonces++;\r
                if (num_nonces % 2) {\r
@@ -710,9 +709,6 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags,
                        i += 9;\r
                }\r
 \r
-               // wait for the card to become ready again\r
-               while(GetCountSspClk() < timeout);\r
-\r
        }\r
 \r
        LED_C_OFF();\r
index 5df99de775ad6da7cfb8cf41eeea4ab58553a09c..52a106bd454d9ebe55f08e9d5939ae53cd97f2a1 100644 (file)
@@ -20,7 +20,7 @@ OBJDIR = obj
 LDLIBS = -L/opt/local/lib -L/usr/local/lib -lreadline -lpthread -lm
 LUALIB = ../liblua/liblua.a
 LDFLAGS = $(ENV_LDFLAGS)
-CFLAGS = $(ENV_CFLAGS) -std=c99 -D_ISOC99_SOURCE -I. -I../include -I../common -I../zlib -I../uart -I/opt/local/include -I../liblua -Wall -g -O3
+CFLAGS = $(ENV_CFLAGS) -std=c99 -D_ISOC99_SOURCE -I. -I../include -I../common -I../common/polarssl -I../zlib -I../uart -I/opt/local/include -I../liblua -Wall -g -O3
 CXXFLAGS = -I../include -Wall -O3
 
 LUAPLATFORM = generic
@@ -88,6 +88,9 @@ CMDSRCS =     crapto1/crapto1.c\
                        crapto1/crypto1.c\
                        polarssl/des.c \
                        polarssl/aes.c\
+                       polarssl/bignum.c\
+                       polarssl/rsa.c\
+                       polarssl/sha1.c\
                        mfkey.c\
                        loclass/cipher.c \
                        loclass/cipherutils.c \
@@ -107,12 +110,22 @@ CMDSRCS =         crapto1/crapto1.c\
                        ui.c \
                        cmddata.c \
                        lfdemod.c \
+                       emv/crypto_polarssl.c\
+                       emv/crypto.c\
+                       emv/emv_pk.c\
+                       emv/emv_pki.c\
+                       emv/emv_pki_priv.c\
+                       emv/test/cryptotest.c\
                        emv/apduinfo.c\
                        emv/dump.c\
                        emv/tlv.c\
                        emv/emv_tags.c\
                        emv/dol.c\
                        emv/emvcore.c\
+                       emv/test/crypto_test.c\
+                       emv/test/sda_test.c\
+                       emv/test/dda_test.c\
+                       emv/test/cda_test.c\
                        emv/cmdemv.c\
                        cmdhf.c \
                        cmdhf14a.c \
@@ -157,7 +170,6 @@ CMDSRCS =   crapto1/crapto1.c\
                        pm3_binlib.c\
                        pm3_bitlib.c\
                        protocols.c\
-                       sha1.c\
                        cmdcrc.c\
                        reveng/reveng.c\
                        reveng/cli.c\
@@ -192,14 +204,7 @@ MULTIARCHOBJS = $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_NOSIMD.o) \
                        $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_AVX.o) \
                        $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_AVX2.o)
 
-GCC_VERSION := $(shell gcc --version | awk '/gcc/{print $$NF;}' | sed -e 's/\.\([0-9][0-9]\)/\1/g' -e 's/\.\([0-9]\)/0\1/g' -e 's/^[0-9]\{3,4\}$$/&00/')
-CLANG_VERSION := $(shell gcc --version | awk '/Apple LLVM version/{print $$4;}' | sed -e 's/\.\([0-9][0-9]\)/\1/g' -e 's/\.\([0-9]\)/0\1/g' -e 's/^[0-9]\{3,4\}$$/&00/')
-ifneq ($(CLANG_VERSION), )
-       SUPPORTS_AVX512 :=  $(shell [ $(CLANG_VERSION) -ge 80000 ] && echo "True" )
-endif
-ifneq ($(GCC_VERSION), )
-       SUPPORTS_AVX512 :=  $(shell [ $(GCC_VERSION) -ge 40900 ] && echo "True" )
-endif
+SUPPORTS_AVX512 :=  $(shell echo | gcc -E -mavx512f - > /dev/null 2>&1 && echo "True" )
 HARD_SWITCH_NOSIMD = -mno-mmx -mno-sse2 -mno-avx -mno-avx2
 HARD_SWITCH_MMX = -mmmx -mno-sse2 -mno-avx -mno-avx2
 HARD_SWITCH_SSE2 = -mmmx -msse2 -mno-avx -mno-avx2
index 27d081b911de0c459206092d40473020d4002b17..0ca2b8b1e00d816223d991f49d23978dc8f10739 100644 (file)
@@ -40,14 +40,14 @@ int split(char *str, char *arr[MAX_ARGS]){
        int wordCnt = 0;
 
        while(1){
-               while(isspace(str[beginIndex])){
+               while(isspace((unsigned char)str[beginIndex])){
                        ++beginIndex;
                }
                if(str[beginIndex] == '\0') {
                        break;
                }
                endIndex = beginIndex;
-               while (str[endIndex] && !isspace(str[endIndex])){
+               while (str[endIndex] && !isspace((unsigned char)str[endIndex])){
                        ++endIndex;
                }
                int len = endIndex - beginIndex;
index 8064724cc3185a66b2e86f0c38c4bb8baaf9646c..e4b245c36e0a8e0c1802063268ca8481fd44bb77 100644 (file)
@@ -16,6 +16,7 @@
 #include <inttypes.h>
 #include <string.h>
 #include <unistd.h>
+#include <ctype.h>
 #include "util.h"
 #include "util_posix.h"
 #include "iso14443crc.h"
@@ -764,7 +765,7 @@ int CmdHF14AAPDU(const char *cmd) {
                                        return 1;
                        }
                        
-               if (isxdigit(c)) {
+               if (isxdigit((unsigned char)c)) {
                        // len = data + PCB(1b) + CRC(2b)
                        switch(param_gethex_to_eol(cmd, cmdp, data, sizeof(data) - 1 - 2, &datalen)) {
                        case 1:
index 60713a01fad681a0129367a2bda4ff45d07ae17a..d42f7eef6badf6d3d53dd21de7d81ec1f50e2e3b 100644 (file)
@@ -14,6 +14,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
+#include <ctype.h>
 #include "iso14443crc.h" // Can also be used for iClass, using 0xE012 as CRC-type
 #include "data.h"
 #include "proxmark3.h"
@@ -32,6 +33,7 @@
 #include "protocols.h"
 #include "usb_cmd.h"
 #include "cmdhfmfu.h"
+#include "util_posix.h"
 
 static int CmdHelp(const char *Cmd);
 
@@ -51,6 +53,20 @@ typedef struct iclass_block {
     uint8_t d[8];
 } iclass_block_t;
 
+int usage_hf_iclass_chk(void) {
+       PrintAndLog("Checkkeys loads a dictionary text file with 8byte hex keys to test authenticating against a iClass tag");  
+       PrintAndLog("Usage: hf iclass chk [h|e|r] <f  (*.dic)>");
+       PrintAndLog("Options:");
+       PrintAndLog("h             Show this help");
+       PrintAndLog("f <filename>  Dictionary file with default iclass keys");
+       PrintAndLog("      e             target Elite / High security key scheme");
+       PrintAndLog("      r             interpret dictionary file as raw (diversified keys)");
+       PrintAndLog("Samples:");
+       PrintAndLog("            hf iclass chk f default_iclass_keys.dic");     
+       PrintAndLog("            hf iclass chk f default_iclass_keys.dic e");   
+       return 0;
+}
+
 int xorbits_8(uint8_t val) {
        uint8_t res = val ^ (val >> 1); //1st pass
        res = res ^ (res >> 1);                 // 2nd pass
@@ -531,7 +547,7 @@ static bool select_and_auth(uint8_t *KEY, uint8_t *MAC, uint8_t *div_key, bool u
                memcpy(div_key, KEY, 8);
        else
                HFiClassCalcDivKey(CSN, KEY, div_key, elite);
-       PrintAndLog("Authing with %s: %02x%02x%02x%02x%02x%02x%02x%02x", rawkey ? "raw key" : "diversified key", div_key[0],div_key[1],div_key[2],div_key[3],div_key[4],div_key[5],div_key[6],div_key[7]);
+        if (verbose) PrintAndLog("Authing with %s: %02x%02x%02x%02x%02x%02x%02x%02x", rawkey ? "raw key" : "diversified key", div_key[0],div_key[1],div_key[2],div_key[3],div_key[4],div_key[5],div_key[6],div_key[7]);
 
        doMAC(CCNR, div_key, MAC);
        UsbCommand resp;
@@ -541,12 +557,12 @@ static bool select_and_auth(uint8_t *KEY, uint8_t *MAC, uint8_t *div_key, bool u
        SendCommand(&d);
        if (!WaitForResponseTimeout(CMD_ACK,&resp,4500))
        {
-               PrintAndLog("Auth Command execute timeout");
+               if (verbose) PrintAndLog("Auth Command execute timeout");
                return false;
        }
        uint8_t isOK = resp.arg[0] & 0xff;
        if (!isOK) {
-               PrintAndLog("Authentication error");
+               if (verbose) PrintAndLog("Authentication error");
                return false;
        }
        return true;
@@ -1699,10 +1715,152 @@ int CmdHFiClassManageKeys(const char *Cmd) {
        return 0;
 }
 
+int CmdHFiClassCheckKeys(const char *Cmd) {
+
+       uint8_t mac[4] = {0x00,0x00,0x00,0x00};
+       uint8_t key[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+       uint8_t div_key[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+
+       // elite key,  raw key, standard key
+       bool use_elite = false;
+       bool use_raw = false;   
+       bool found_debit = false;
+       bool found_credit = false;      
+       bool errors = false;
+       uint8_t cmdp = 0x00;
+       FILE * f;
+       char filename[FILE_PATH_SIZE] = {0};
+       uint8_t fileNameLen = 0;
+       char buf[17];
+       uint8_t *keyBlock = NULL, *p;
+       int keyitems = 0, keycnt = 0;
+
+       while (param_getchar(Cmd, cmdp) != 0x00 && !errors) {
+               switch (param_getchar(Cmd, cmdp)) {
+               case 'h':
+               case 'H':
+                       return usage_hf_iclass_chk();
+               case 'f':
+               case 'F':
+                       fileNameLen = param_getstr(Cmd, cmdp+1, filename, sizeof(filename)); 
+                       if (fileNameLen < 1) {
+                               PrintAndLog("No filename found after f");
+                               errors = true;
+                       }
+                       cmdp += 2;
+                       break;
+               case 'e':
+               case 'E':
+                       use_elite = true;
+                       cmdp++;
+                       break;
+               case 'r':
+               case 'R':
+                       use_raw = true;
+                       cmdp++;
+                       break;
+               default:
+                       PrintAndLog("Unknown parameter '%c'\n", param_getchar(Cmd, cmdp));
+                       errors = true;
+                       break;
+               }
+       }
+       if (errors) return usage_hf_iclass_chk();       
+                       
+       if ( !(f = fopen( filename , "r")) ) {
+               PrintAndLog("File: %s: not found or locked.", filename);
+               return 1;
+       }
+
+       while( fgets(buf, sizeof(buf), f) ){
+               if (strlen(buf) < 16 || buf[15] == '\n')
+                       continue;
+       
+               while (fgetc(f) != '\n' && !feof(f)) ;  //goto next line
+               
+               if( buf[0]=='#' ) continue;     //The line start with # is comment, skip
+
+               if (!isxdigit(buf[0])){
+                       PrintAndLog("File content error. '%s' must include 16 HEX symbols",buf);
+                       continue;
+               }
+               
+               buf[16] = 0;
+
+               p = realloc(keyBlock, 8 * (keyitems += 64));
+               if (!p) {
+                       PrintAndLog("Cannot allocate memory for default keys");
+                       free(keyBlock);
+                       fclose(f);
+                       return 2;
+               }
+               keyBlock = p;
+
+               memset(keyBlock + 8 * keycnt, 0, 8);
+               num_to_bytes(strtoull(buf, NULL, 16), 8, keyBlock + 8 * keycnt);
+
+               //PrintAndLog("check key[%2d] %016" PRIx64, keycnt, bytes_to_num(keyBlock + 8*keycnt, 8));
+               keycnt++;
+               memset(buf, 0, sizeof(buf));
+       }
+       fclose(f);
+       PrintAndLog("Loaded %2d keys from %s", keycnt, filename);
+       
+       // time
+       uint64_t t1 = msclock();
+                               
+       for (uint32_t c = 0; c < keycnt; c += 1) {
+                       printf("."); fflush(stdout);                    
+                       if (ukbhit()) {
+                               int gc = getchar(); (void)gc;
+                               printf("\naborted via keyboard!\n");
+                               break;
+                       }
+                       
+                       memcpy(key, keyBlock + 8 * c , 8); 
+
+                       // debit key. try twice
+                       for (int foo = 0; foo < 2 && !found_debit; foo++) {
+                               if (!select_and_auth(key, mac, div_key, false, use_elite, use_raw, false))
+                                       continue;
+
+                               // key found.
+                               PrintAndLog("\n--------------------------------------------------------");
+                       PrintAndLog("   Found AA1 debit key\t\t[%s]", sprint_hex(key, 8));
+                               found_debit = true;
+                       }
+                       
+                       // credit key. try twice
+                       for (int foo = 0; foo < 2 && !found_credit; foo++) {
+                               if (!select_and_auth(key, mac, div_key, true, use_elite, use_raw, false))
+                                       continue;
+                               
+                               // key found
+                               PrintAndLog("\n--------------------------------------------------------");
+                               PrintAndLog("   Found AA2 credit key\t\t[%s]", sprint_hex(key, 8));
+                               found_credit = true;
+                       }
+                       
+                       // both keys found.
+                       if ( found_debit && found_credit )
+                               break;
+       }
+
+       t1 = msclock() - t1;
+
+       PrintAndLog("\nTime in iclass checkkeys: %.0f seconds\n", (float)t1/1000.0);
+       
+       DropField();
+       free(keyBlock);
+       PrintAndLog("");
+       return 0;
+}
+
 static command_t CommandTable[] = 
 {
        {"help",        CmdHelp,                        1,      "This help"},
        {"calcnewkey",  CmdHFiClassCalcNewKey,          1,      "[options..] Calc Diversified keys (blocks 3 & 4) to write new keys"},
+       {"chk",         CmdHFiClassCheckKeys,           0,      "            Check keys"},      
        {"clone",       CmdHFiClassCloneTag,            0,      "[options..] Authenticate and Clone from iClass bin file"},
        {"decrypt",     CmdHFiClassDecrypt,             1,      "[f <fname>] Decrypt tagdump" },
        {"dump",        CmdHFiClassReader_Dump,         0,      "[options..] Authenticate and Dump iClass tag's AA1"},
@@ -1723,6 +1881,7 @@ static command_t CommandTable[] =
 
 int CmdHFiClass(const char *Cmd)
 {
+       clearCommandBuffer();   
        CmdsParse(CommandTable, Cmd);
        return 0;
 }
index 9a3adf504b93ef59d322d62bb1cd9a6c3c13f7f7..456bb1c3803b2d3e7451812bb5fb61451e77e57c 100644 (file)
@@ -34,6 +34,7 @@ int CmdHFiClassSnoop(const char *Cmd);
 int CmdHFiClassSim(const char *Cmd);
 int CmdHFiClassWriteKeyFile(const char *Cmd);
 int CmdHFiClass_WriteBlock(const char *Cmd);
+int CmdHFiClassCheckKeys(const char *Cmd);
 void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t endblock, size_t filesize);
 void HFiClassCalcDivKey(uint8_t        *CSN, uint8_t   *KEY, uint8_t *div_key, bool elite);
 #endif
index eed6b34c64af26e3d027e19564bec7d0757683a2..c471fdf73186f5b50d54193a45ff4f9e4b0175a3 100644 (file)
@@ -987,6 +987,7 @@ int CmdHF14AMfChk(const char *Cmd)
        int i, res;\r
        int     keycnt = 0;\r
        char ctmp       = 0x00;\r
+       int clen = 0;\r
        char ctmp3[3]   = {0x00};\r
        uint8_t blockNo = 0;\r
        uint8_t SectorsCnt = 0;\r
@@ -1015,32 +1016,36 @@ int CmdHF14AMfChk(const char *Cmd)
                blockNo = param_get8(Cmd, 0);\r
 \r
        ctmp = param_getchar(Cmd, 1);\r
-       switch (ctmp) {\r
-       case 'a': case 'A':\r
-               keyType = 0;\r
-               break;\r
-       case 'b': case 'B':\r
-               keyType = 1;\r
-               break;\r
-       case '?':\r
-               keyType = 2;\r
-               break;\r
-       default:\r
-               PrintAndLog("Key type must be A , B or ?");\r
-               free(keyBlock);\r
-               return 1;\r
-       };\r
+       clen = param_getlength(Cmd, 1);\r
+       if (clen == 1) {\r
+               switch (ctmp) {\r
+               case 'a': case 'A':\r
+                       keyType = 0;\r
+                       break;\r
+               case 'b': case 'B':\r
+                       keyType = 1;\r
+                       break;\r
+               case '?':\r
+                       keyType = 2;\r
+                       break;\r
+               default:\r
+                       PrintAndLog("Key type must be A , B or ?");\r
+                       free(keyBlock);\r
+                       return 1;\r
+               };\r
+       }\r
 \r
        // transfer to emulator & create dump file\r
        ctmp = param_getchar(Cmd, 2);\r
-       if (ctmp == 't' || ctmp == 'T') transferToEml = 1;\r
-       if (ctmp == 'd' || ctmp == 'D') createDumpFile = 1;\r
+       clen = param_getlength(Cmd, 2);\r
+       if (clen == 1 && (ctmp == 't' || ctmp == 'T')) transferToEml = 1;\r
+       if (clen == 1 && (ctmp == 'd' || ctmp == 'D')) createDumpFile = 1;\r
        \r
        param3InUse = transferToEml | createDumpFile;\r
        \r
        timeout14a = 500; // fast by default\r
        // double parameters - ts, ds\r
-       int clen = param_getlength(Cmd, 2);\r
+       clen = param_getlength(Cmd, 2);\r
        if (clen == 2 || clen == 3){\r
                param_getstr(Cmd, 2, ctmp3, sizeof(ctmp3));\r
                ctmp = ctmp3[1];\r
@@ -1089,7 +1094,7 @@ int CmdHF14AMfChk(const char *Cmd)
 \r
                                        if( buf[0]=='#' ) continue;     //The line start with # is comment, skip\r
 \r
-                                       if (!isxdigit(buf[0])){\r
+                                       if (!isxdigit((unsigned char)buf[0])){\r
                                                PrintAndLog("File content error. '%s' must include 12 HEX symbols",buf);\r
                                                continue;\r
                                        }\r
@@ -1985,8 +1990,8 @@ int CmdHF14AMfCWipe(const char *Cmd)
        bool fillCard = false;\r
        \r
        if (strlen(Cmd) < 1 || param_getchar(Cmd, 0) == 'h') {\r
-               PrintAndLog("Usage:  hf mf cwipe [card size] [w] [p]");\r
-               PrintAndLog("sample:  hf mf cwipe 1 w s");\r
+               PrintAndLog("Usage:  hf mf cwipe [card size] [w] [f]");\r
+               PrintAndLog("sample:  hf mf cwipe 1 w f");\r
                PrintAndLog("[card size]: 0 = 320 bytes (Mifare Mini), 1 = 1K (default), 2 = 2K, 4 = 4K");\r
                PrintAndLog("w - Wipe magic Chinese card (only works with gen:1a cards)");\r
                PrintAndLog("f - Fill the card with default data and keys (works with gen:1a and gen:1b cards only)");\r
index 92a00bce5820f7e63dd4675c12ac1ec118f2567a..c5a6dd3f64623ed304c2487b50ae0238dedb7602 100644 (file)
@@ -1433,7 +1433,7 @@ int CmdT55xxBruteForce(const char *Cmd) {
                        //The line start with # is comment, skip\r
                        if( buf[0]=='#' ) continue;\r
 \r
-                       if (!isxdigit(buf[0])) {\r
+                       if (!isxdigit((unsigned char)buf[0])) {\r
                                PrintAndLog("File content error. '%s' must include 8 HEX symbols", buf);\r
                                continue;\r
                        }\r
index 739d68e1cbc0beb609f0d858dae78a3ad1438b95..ae059ea281de276c3df004bb1f270cca703a4293 100644 (file)
@@ -172,7 +172,7 @@ bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeo
                if (msclock() - start_time > 2000 && show_warning) {
                        PrintAndLog("Waiting for a response from the proxmark...");
                        PrintAndLog("Don't forget to cancel its operation first by pressing on the button");
-                       break;
+                       show_warning = false;
                }
        }
        return false;
index 23163aa98f128918b153c80a8ef26930519ca98c..0d19f49697a4f47304eb4edc338a31be93ed7e9a 100644 (file)
@@ -76,8 +76,11 @@ int CmdList(const char *Cmd)
 {
     DIR *dp;
     struct dirent *ep;
-       char script_directory_path[strlen(get_my_executable_directory()) + strlen(LUA_SCRIPTS_DIRECTORY) + 1];
-       strcpy(script_directory_path, get_my_executable_directory());
+       char const * exedir = get_my_executable_directory();
+       if (exedir == NULL)
+           return 0;
+       char script_directory_path[strlen(exedir) + strlen(LUA_SCRIPTS_DIRECTORY) + 1];
+       strcpy(script_directory_path, exedir);
        strcat(script_directory_path, LUA_SCRIPTS_DIRECTORY);
     dp = opendir(script_directory_path);
 
diff --git a/client/emv/capk.txt b/client/emv/capk.txt
new file mode 100644 (file)
index 0000000..0925fba
--- /dev/null
@@ -0,0 +1,32 @@
+a0:00:00:00:03 01 091231 rsa 03 c6:96:03:42:13:d7:d8:54:69:84:57:9d:1d:0f:0e:a5:19:cf:f8:de:ff:c4:29:35:4c:f3:a8:71:a6:f7:18:3f:12:28:da:5c:74:70:c0:55:38:71:00:cb:93:5a:71:2c:4e:28:64:df:5d:64:ba:93:fe:7e:63:e7:1f:25:b1:e5:f5:29:85:75:eb:e1:c6:3a:a6:17:70:69:17:91:1d:c2:a7:5a:c2:8b:25:1c:7e:f4:0f:23:65:91:24:90:b9:39:bc:a2:12:4a:30:a2:8f:54:40:2c:34:ae:ca:33:1a:b6:7e:1e:79:b2:85:dd:57:71:b5:d9:ff:79:ea:63:0b:75 sha1 d3:4a:6a:77:60:11:c7:e7:ce:3a:ec:5f:03:ad:2f:8c:fc:55:03:cc
+a0:00:00:00:03 07 171231 rsa 03 a8:9f:25:a5:6f:a6:da:25:8c:8c:a8:b4:04:27:d9:27:b4:a1:eb:4d:7e:a3:26:bb:b1:2f:97:de:d7:0a:e5:e4:48:0f:c9:c5:e8:a9:72:17:71:10:a1:cc:31:8d:06:d2:f8:f5:c4:84:4a:c5:fa:79:a4:dc:47:0b:b1:1e:d6:35:69:9c:17:08:1b:90:f1:b9:84:f1:2e:92:c1:c5:29:27:6d:8a:f8:ec:7f:28:49:20:97:d8:cd:5b:ec:ea:16:fe:40:88:f6:cf:ab:4a:1b:42:32:8a:1b:99:6f:92:78:b0:b7:e3:31:1c:a5:ef:85:6c:2f:88:84:74:b8:36:12:a8:2e:4e:00:d0:cd:40:69:a6:78:31:40:43:3d:50:72:5f sha1 b4:bc:56:cc:4e:88:32:49:32:cb:c6:43:d6:89:8f:6f:e5:93:b1:72
+a0:00:00:00:03 08 221231 rsa 03 d9:fd:6e:d7:5d:51:d0:e3:06:64:bd:15:70:23:ea:a1:ff:a8:71:e4:da:65:67:2b:86:3d:25:5e:81:e1:37:a5:1d:e4:f7:2b:cc:9e:44:ac:e1:21:27:f8:7e:26:3d:3a:f9:dd:9c:f3:5c:a4:a7:b0:1e:90:70:00:ba:85:d2:49:54:c2:fc:a3:07:48:25:dd:d4:c0:c8:f1:86:cb:02:0f:68:3e:02:f2:de:ad:39:69:13:3f:06:f7:84:51:66:ac:eb:57:ca:0f:c2:60:34:45:46:98:11:d2:93:bf:ef:ba:fa:b5:76:31:b3:dd:91:e7:96:bf:85:0a:25:01:2f:1a:e3:8f:05:aa:5c:4d:6d:03:b1:dc:2e:56:86:12:78:59:38:bb:c9:b3:cd:3a:91:0c:1d:a5:5a:5a:92:18:ac:e0:f7:a2:12:87:75:26:82:f1:58:32:a6:78:d6:e1:ed:0b sha1 20:d2:13:12:69:55:de:20:5a:dc:2f:d2:82:2b:d2:2d:e2:1c:f9:a8
+a0:00:00:00:03 09 221231 rsa 03 9d:91:22:48:de:0a:4e:39:c1:a7:dd:e3:f6:d2:58:89:92:c1:a4:09:5a:fb:d1:82:4d:1b:a7:48:47:f2:bc:49:26:d2:ef:d9:04:b4:b5:49:54:cd:18:9a:54:c5:d1:17:96:54:f8:f9:b0:d2:ab:5f:03:57:eb:64:2f:ed:a9:5d:39:12:c6:57:69:45:fa:b8:97:e7:06:2c:aa:44:a4:aa:06:b8:fe:6e:3d:ba:18:af:6a:e3:73:8e:30:42:9e:e9:be:03:42:7c:9d:64:f6:95:fa:8c:ab:4b:fe:37:68:53:ea:34:ad:1d:76:bf:ca:d1:59:08:c0:77:ff:e6:dc:55:21:ec:ef:5d:27:8a:96:e2:6f:57:35:9f:fa:ed:a1:94:34:b9:37:f1:ad:99:9d:c5:c4:1e:b1:19:35:b4:4c:18:10:0e:85:7f:43:1a:4a:5a:6b:b6:51:14:f1:74:c2:d7:b5:9f:df:23:7d:6b:b1:dd:09:16:e6:44:d7:09:de:d5:64:81:47:7c:75:d9:5c:dd:68:25:46:15:f7:74:0e:c0:7f:33:0a:c5:d6:7b:cd:75:bf:23:d2:8a:14:08:26:c0:26:db:de:97:1a:37:cd:3e:f9:b8:df:64:4a:c3:85:01:05:01:ef:c6:50:9d:7a:41 sha1 1f:f8:0a:40:17:3f:52:d7:d2:7e:0f:26:a1:46:a1:c8:cc:b2:90:46
+a0:00:00:00:03 95 000000 rsa 03 be:9e:1f:a5:e9:a8:03:85:29:99:c4:ab:43:2d:b2:86:00:dc:d9:da:b7:6d:fa:aa:47:35:5a:0f:e3:7b:15:08:ac:6b:f3:88:60:d3:c6:c2:e5:b1:2a:3c:aa:f2:a7:00:5a:72:41:eb:aa:77:71:11:2c:74:cf:9a:06:34:65:2f:bc:a0:e5:98:0c:54:a6:47:61:ea:10:1a:11:4e:0f:0b:55:72:ad:d5:7d:01:0b:7c:9c:88:7e:10:4c:a4:ee:12:72:da:66:d9:97:b9:a9:0b:5a:6d:62:4a:b6:c5:7e:73:c8:f9:19:00:0e:b5:f6:84:89:8e:f8:c3:db:ef:b3:30:c6:26:60:be:d8:8e:a7:8e:90:9a:ff:05:f6:da:62:7b sha1 ee:15:11:ce:c7:10:20:a9:b9:04:43:b3:7b:1d:5f:6e:70:30:30:f6
+a0:00:00:00:03 92 000000 rsa 03 99:6a:f5:6f:56:91:87:d0:92:93:c1:48:10:45:0e:d8:ee:33:57:39:7b:18:a2:45:8e:fa:a9:2d:a3:b6:df:65:14:ec:06:01:95:31:8f:d4:3b:e9:b8:f0:cc:66:9e:3f:84:40:57:cb:dd:f8:bd:a1:91:bb:64:47:3b:c8:dc:9a:73:0d:b8:f6:b4:ed:e3:92:41:86:ff:d9:b8:c7:73:57:89:c2:3a:36:ba:0b:8a:f6:53:72:eb:57:ea:5d:89:e7:d1:4e:9c:7b:6b:55:74:60:f1:08:85:da:16:ac:92:3f:15:af:37:58:f0:f0:3e:bd:3c:5c:2c:94:9c:ba:30:6d:b4:4e:6a:2c:07:6c:5f:67:e2:81:d7:ef:56:78:5d:c4:d7:59:45:e4:91:f0:19:18:80:0a:9e:2d:c6:6f:60:08:05:66:ce:0d:af:8d:17:ea:d4:6a:d8:e3:0a:24:7c:9f sha1 42:9c:95:4a:38:59:ce:f9:12:95:f6:63:c9:63:e5:82:ed:6e:b2:53
+a0:00:00:00:03 94 000000 rsa 03 ac:d2:b1:23:02:ee:64:4f:3f:83:5a:bd:1f:c7:a6:f6:2c:ce:48:ff:ec:62:2a:a8:ef:06:2b:ef:6f:b8:ba:8b:c6:8b:bf:6a:b5:87:0e:ed:57:9b:c3:97:3e:12:13:03:d3:48:41:a7:96:d6:dc:bc:41:db:f9:e5:2c:46:09:79:5c:0c:cf:7e:e8:6f:a1:d5:cb:04:10:71:ed:2c:51:d2:20:2f:63:f1:15:6c:58:a9:2d:38:bc:60:bd:f4:24:e1:77:6e:2b:c9:64:80:78:a0:3b:36:fb:55:43:75:fc:53:d5:7c:73:f5:16:0e:a5:9f:3a:fc:53:98:ec:7b:67:75:8d:65:c9:bf:f7:82:8b:6b:82:d4:be:12:4a:41:6a:b7:30:19:14:31:1e:a4:62:c1:9f:77:1f:31:b3:b5:73:36:00:0d:ff:73:2d:3b:83:de:07:05:2d:73:03:54:d2:97:be:c7:28:71:dc:cf:0e:19:3f:17:1a:ba:27:ee:46:4c:6a:97:69:09:43:d5:9b:da:bb:2a:27:eb:71:ce:eb:da:fa:11:76:04:64:78:fd:62:fe:c4:52:d5:ca:39:32:96:53:0a:a3:f4:19:27:ad:fe:43:4a:2d:f2:ae:30:54:f8:84:06:57:a2:6e:0f:c6:17 sha1 c4:a3:c4:3c:cf:87:32:7d:13:6b:80:41:60:e4:7d:43:b6:0e:6e:0f
+a0:00:00:00:04 03 000000 rsa 03 c2:49:07:47:fe:17:eb:05:84:c8:8d:47:b1:60:27:04:15:0a:dc:88:c5:b9:98:bd:59:ce:04:3e:de:bf:0f:fe:e3:09:3a:c7:95:6a:d3:b6:ad:45:54:c6:de:19:a1:78:d6:da:29:5b:e1:5d:52:20:64:5e:3c:81:31:66:6f:a4:be:5b:84:fe:13:1e:a4:4b:03:93:07:63:8b:9e:74:a8:c4:25:64:f8:92:a6:4d:f1:cb:15:71:2b:73:6e:33:74:f1:bb:b6:81:93:71:60:2d:89:70:e9:7b:90:07:93:c7:c2:a8:9a:4a:16:49:a5:9b:e6:80:57:4d:d0:b6:01:45 sha1 5a:dd:f2:1d:09:27:86:61:14:11:79:cb:ef:f2:72:ea:38:4b:13:bb
+a0:00:00:00:04 04 000000 rsa 03 a6:da:42:83:87:a5:02:d7:dd:fb:7a:74:d3:f4:12:be:76:26:27:19:7b:25:43:5b:7a:81:71:6a:70:01:57:dd:d0:6f:7c:c9:9d:6c:a2:8c:24:70:52:7e:2c:03:61:6b:9c:59:21:73:57:c2:67:4f:58:3b:3b:a5:c7:dc:f2:83:86:92:d0:23:e3:56:24:20:b4:61:5c:43:9c:a9:7c:44:dc:9a:24:9c:fc:e7:b3:bf:b2:2f:68:22:8c:3a:f1:33:29:aa:4a:61:3c:f8:dd:85:35:02:37:3d:62:e4:9a:b2:56:d2:bc:17:12:0e:54:ae:dc:ed:6d:96:a4:28:7a:cc:5c:04:67:7d:4a:5a:32:0d:b8:be:e2:f7:75:e5:fe:c5 sha1 38:1a:03:5d:a5:8b:48:2e:e2:af:75:f4:c3:f2:ca:46:9b:a4:aa:6c
+a0:00:00:00:04 05 000000 rsa 03 b8:04:8a:bc:30:c9:0d:97:63:36:54:3e:3f:d7:09:1c:8f:e4:80:0d:f8:20:ed:55:e7:e9:48:13:ed:00:55:5b:57:3f:ec:a3:d8:4a:f6:13:1a:65:1d:66:cf:f4:28:4f:b1:3b:63:5e:dd:0e:e4:01:76:d8:bf:04:b7:fd:1c:7b:ac:f9:ac:73:27:df:aa:8a:a7:2d:10:db:3b:8e:70:b2:dd:d8:11:cb:41:96:52:5e:a3:86:ac:c3:3c:0d:9d:45:75:91:64:69:c4:e4:f5:3e:8e:1c:91:2c:c6:18:cb:22:dd:e7:c3:56:8e:90:02:2e:6b:ba:77:02:02:e4:52:2a:2d:d6:23:d1:80:e2:15:bd:1d:15:07:fe:3d:c9:0c:a3:10:d2:7b:3e:fc:cd:8f:83:de:30:52:ca:d1:e4:89:38:c6:8d:09:5a:ac:91:b5:f3:7e:28:bb:49:ec:7e:d5:97 sha1 eb:fa:0d:5d:06:d8:ce:70:2d:a3:ea:e8:90:70:1d:45:e2:74:c8:45
+a0:00:00:00:04 06 000000 rsa 03 cb:26:fc:83:0b:43:78:5b:2b:ce:37:c8:1e:d3:34:62:2f:96:22:f4:c8:9a:ae:64:10:46:b2:35:34:33:88:3f:30:7f:b7:c9:74:16:2d:a7:2f:7a:4e:c7:5d:9d:65:73:36:86:5b:8d:30:23:d3:d6:45:66:76:25:c9:a0:7a:6b:7a:13:7c:f0:c6:41:98:ae:38:fc:23:80:06:fb:26:03:f4:1f:4f:3b:b9:da:13:47:27:0f:2f:5d:8c:60:6e:42:09:58:c5:f7:d5:0a:71:de:30:14:2f:70:de:46:88:89:b5:e3:a0:86:95:b9:38:a5:0f:c9:80:39:3a:9c:bc:e4:4a:d2:d6:4f:63:0b:b3:3a:d3:f5:f5:fd:49:5d:31:f3:78:18:c1:d9:40:71:34:2e:07:f1:be:c2:19:4f:60:35:ba:5d:ed:39:36:50:0e:b8:2d:fd:a6:e8:af:b6:55:b1:ef:3d:0d:7e:bf:86:b6:6d:d9:f2:9f:6b:1d:32:4f:e8:b2:6c:e3:8a:b2:01:3d:d1:3f:61:1e:7a:59:4d:67:5c:44:32:35:0e:a2:44:cc:34:f3:87:3c:ba:06:59:29:87:a1:d7:e8:52:ad:c2:2e:f5:a2:ee:28:13:20:31:e4:8f:74:03:7e:3b:34:ab:74:7f sha1 f9:10:a1:50:4d:5f:fb:79:3d:94:f3:b5:00:76:5e:1a:bc:ad:72:d9
+a0:00:00:00:04 00 000000 rsa 03 9c:6b:e5:ad:b1:0b:4b:e3:dc:e2:09:9b:4b:21:06:72:b8:96:56:eb:a0:91:20:4f:61:3e:cc:62:3b:ed:c9:c6:d7:7b:66:0e:8b:ae:ea:7f:7c:e3:0f:1b:15:38:79:a4:e3:64:59:34:3d:1f:e4:7a:cd:bd:41:fc:d7:10:03:0c:2b:a1:d9:46:15:97:98:2c:6e:1b:dd:08:55:4b:72:6f:5e:ff:79:13:ce:59:e7:9e:35:72:95:c3:21:e2:6d:0b:8b:e2:70:a9:44:23:45:c7:53:e2:aa:2a:cf:c9:d3:08:50:60:2f:e6:ca:c0:0c:6d:df:6b:8d:9d:9b:48:79:b2:82:6b:04:2a:07:f0:e5:ae:52:6a:3d:3c:4d:22:c7:2b:9e:aa:52:ee:d8:89:38:66:f8:66:38:7a:c0:5a:13:99 sha1 ec:0a:59:d3:5d:19:f0:31:e9:e8:cb:ec:56:db:80:e2:2b:1d:e1:30
+a0:00:00:00:04 02 000000 rsa 03 a9:9a:6d:3e:07:18:89:ed:9e:3a:0c:39:1c:69:b0:b8:04:fc:16:0b:2b:4b:dd:57:0c:92:dd:5a:0f:45:f5:3e:86:21:f7:c9:6c:40:22:42:66:73:5e:1e:e1:b3:c0:62:38:ae:35:04:63:20:fd:8e:81:f8:ce:b3:f8:b4:c9:7b:94:09:30:a3:ac:5e:79:00:86:da:d4:1a:6a:4f:51:17:ba:1c:e2:43:8a:51:ac:05:3e:b0:02:ae:d8:66:d2:c4:58:fd:73:35:90:21:a1:20:29:a0:c0:43:04:5c:11:66:4f:e0:21:9e:c6:3c:10:bf:21:55:bb:27:84:60:9a:10:64:21:d4:51:63:79:97:38:c1:c3:09:09:bb:6c:6f:e5:2b:bb:76:39:7b:97:40:ce:06:4a:61:3f:f8:41:11:85:f0:88:42:a4:23:ea:d2:0e:df:fb:ff:1c:d6:c3:fe:0c:98:21:47:91:99:c2:6d:85:72:cc:8a:ff:f0:87:a9:c3 sha1 33:40:8b:96:c8:14:74:2a:d7:35:36:c7:2f:09:26:e4:47:1e:8e:47
+a0:00:00:00:04 05 000000 rsa 03 a1:f5:e1:c9:bd:86:50:bd:43:ab:6e:e5:6b:89:1e:f7:45:9c:0a:24:fa:84:f9:12:7d:1a:6c:79:d4:93:0f:6d:b1:85:2e:25:10:f1:8b:61:cd:35:4d:b8:3a:35:6b:d1:90:b8:8a:b8:df:04:28:4d:02:a4:20:4a:7b:6c:b7:c5:55:19:77:a9:b3:63:79:ca:3d:e1:a0:8e:69:f3:01:c9:5c:c1:c2:05:06:95:92:75:f4:17:23:dd:5d:29:25:29:05:79:e5:a9:5b:0d:f6:32:3f:c8:e9:27:3d:6f:84:91:98:c4:99:62:09:16:6d:9b:fc:97:3c:36:1c:c8:26:e1 sha1 53:d0:49:03:b4:96:f5:95:44:a8:43:09:af:16:92:51:f2:89:68:74
+a0:00:00:00:04 ef 000000 rsa 03 a1:91:cb:87:47:3f:29:34:9b:5d:60:a8:8b:3e:ae:e0:97:3a:a6:f1:a0:82:f3:58:d8:49:fd:df:f9:c0:91:f8:99:ed:a9:79:2c:af:09:ef:28:f5:d2:24:04:b8:8a:22:93:ee:bb:c1:94:9c:43:be:a4:d6:0c:fd:87:9a:15:39:54:4e:09:e0:f0:9f:60:f0:65:b2:bf:2a:13:ec:c7:05:f3:d4:68:b9:d3:3a:e7:7a:d9:d3:f1:9c:a4:0f:23:dc:f5:eb:7c:04:dc:8f:69:eb:a5:65:b1:eb:cb:46:86:cd:27:47:85:53:0f:f6:f6:e9:ee:43:aa:43:fd:b0:2c:e0:0d:ae:c1:5c:7b:8f:d6:a9:b3:94:ba:ba:41:9d:3f:6d:c8:5e:16:56:9b:e8:e7:69:89:68:8e:fe:a2:df:22:ff:7d:35:c0:43:33:8d:ea:a9:82:a0:2b:86:6d:e5:32:85:19:eb:bc:d6:f0:3c:dd:68:66:73:84:7f:84:db:65:1a:b8:6c:28:cf:14:62:56:2c:57:7b:85:35:64:a2:90:c8:55:6d:81:85:31:26:8d:25:cc:98:a4:cc:6a:0b:df:ff:da:2d:cc:a3:a9:4c:99:85:59:e3:07:fd:df:91:50:06:d9:a9:87:b0:7d:da:eb:3b sha1 21:76:6e:bb:0e:e1:22:af:b6:5d:78:45:b7:3d:b4:6b:ab:65:42:7a
+a0:00:00:00:04 f1 000000 rsa 03 a0:dc:f4:bd:e1:9c:35:46:b4:b6:f0:41:4d:17:4d:de:29:4a:ab:bb:82:8c:5a:83:4d:73:aa:e2:7c:99:b0:b0:53:a9:02:78:00:72:39:b6:45:9f:f0:bb:cd:7b:4b:9c:6c:50:ac:02:ce:91:36:8d:a1:bd:21:aa:ea:db:c6:53:47:33:7d:89:b6:8f:5c:99:a0:9d:05:be:02:dd:1f:8c:5b:a2:0e:2f:13:fb:2a:27:c4:1d:3f:85:ca:d5:cf:66:68:e7:58:51:ec:66:ed:bf:98:85:1f:d4:e4:2c:44:c1:d5:9f:59:84:70:3b:27:d5:b9:f2:1b:8f:a0:d9:32:79:fb:bf:69:e0:90:64:29:09:c9:ea:27:f8:98:95:95:41:aa:67:57:f5:f6:24:10:4f:6e:1d:3a:95:32:f2:a6:e5:15:15:ae:ad:1b:43:b3:d7:83:50:88:a2:fa:fa:7b:e7 sha1 d8:e6:8d:a1:67:ab:5a:85:d8:c3:d5:5e:cb:9b:05:17:a1:a5:b4:bb
+a0:00:00:00:04 f3 000000 rsa 03 98:f0:c7:70:f2:38:64:c2:e7:66:df:02:d1:e8:33:df:f4:ff:e9:2d:69:6e:16:42:f0:a8:8c:56:94:c6:47:9d:16:db:15:37:bf:e2:9e:4f:dc:6e:6e:8a:fd:1b:0e:b7:ea:01:24:72:3c:33:31:79:bf:19:e9:3f:10:65:8b:2f:77:6e:82:9e:87:da:ed:a9:c9:4a:8b:33:82:19:9a:35:0c:07:79:77:c9:7a:ff:08:fd:11:31:0a:c9:50:a7:2c:3c:a5:00:2e:f5:13:fc:cc:28:6e:64:6e:3c:53:87:53:5d:50:95:14:b3:b3:26:e1:23:4f:9c:b4:8c:36:dd:d4:4b:41:6d:23:65:40:34:a6:6f:40:3b:a5:11:c5:ef:a3 sha1 a6:9a:c7:60:3d:af:56:6e:97:2d:ed:c2:cb:43:3e:07:e8:b0:1a:9a
+a0:00:00:00:04 f5 000000 rsa 01:00:01 a6:e6:fb:72:17:95:06:f8:60:cc:ca:8c:27:f9:9c:ec:d9:4c:7d:4f:31:91:d3:03:bb:ee:37:48:1c:7a:a1:5f:23:3b:a7:55:e9:e4:37:63:45:a9:a6:7e:79:94:bd:c1:c6:80:bb:35:22:d8:c9:3e:b0:cc:c9:1a:d3:1a:d4:50:da:30:d3:37:66:2d:19:ac:03:e2:b4:ef:5f:6e:c1:82:82:d4:91:e1:97:67:d7:b2:45:42:df:de:ff:6f:62:18:55:03:53:20:69:bb:b3:69:e3:bb:9f:b1:9a:c6:f1:c3:0b:97:d2:49:ee:e7:64:e0:ba:c9:7f:25:c8:73:d9:73:95:3e:51:53:a4:20:64:bb:fa:bf:d0:6a:4b:b4:86:86:0b:f6:63:74:06:c9:fc:36:81:3a:4a:75:f7:5c:31:cc:a9:f6:9f:8d:e5:9a:de:ce:f6:bd:e7:e0:78:00:fc:be:03:5d:31:76:af:84:73:e2:3e:9a:a3:df:ee:22:11:96:d1:14:83:02:67:7c:72:0c:fe:25:44:a0:3d:b5:53:e7:f1:b8:42:7b:a1:cc:72:b0:f2:9b:12:df:ef:4c:08:1d:07:6d:35:3e:71:88:0a:ad:ff:38:63:52:af:0a:b7:b2:8e:d4:9e:1e:67:2d:11:f9 sha1 c2:23:98:04:c8:09:81:70:be:52:d6:d5:d4:15:9e:81:ce:84:66:bf
+a0:00:00:00:04 f6 000000 rsa 03 a2:5a:6b:d7:83:a5:ef:6b:8f:b6:f8:30:55:c2:60:f5:f9:9e:a1:66:78:f3:b9:05:3e:0f:64:98:e8:2c:3f:5d:1e:8c:38:f1:35:88:01:7e:2b:12:b3:d8:ff:6f:50:16:7f:46:44:29:10:72:9e:9e:4d:1b:37:39:e5:06:7c:0a:c7:a1:f4:48:7e:35:f6:75:bc:16:e2:33:31:51:65:cb:14:2b:fd:b2:5e:30:1a:63:2a:54:a3:37:1e:ba:b6:57:2d:ee:ba:f3:70:f3:37:f0:57:ee:73:b4:ae:46:d1:a8:bc:4d:a8:53:ec:3c:c1:2c:8c:bc:2d:a1:83:22:d6:85:30:c7:0b:22:bd:ac:35:1d:d3:60:68:ae:32:1e:11:ab:f2:64:f4:d3:56:9b:b7:12:14:54:50:05:55:8d:e2:60:83:c7:35:db:77:63:68:17:2f:e8:c2:f5:c8:5e:8b:5b:89:0c:c6:82:91:1d:2d:e7:1f:a6:26:b8:81:7f:cc:c0:89:22:b7:03:86:9f:3b:ae:ac:14:59:d7:7c:d8:53:76:bc:36:18:2f:42:38:31:4d:6c:42:12:fb:dd:7f:23:d3 sha1 50:29:09:ed:54:5e:3c:8d:bd:00:ea:58:2d:06:17:fe:e9:f6:f6:84
+a0:00:00:00:04 f7 000000 rsa 01:00:01 94:ea:62:f6:d5:83:20:e3:54:c0:22:ad:dc:f0:55:9d:8c:f2:06:cd:92:e8:69:56:49:05:ce:21:d7:20:f9:71:b7:ae:a3:74:83:0e:be:17:57:11:5a:85:e0:88:d4:1c:6b:77:cf:5e:c8:21:f3:0b:1d:89:04:17:bf:2f:a3:1e:59:08:de:d5:fa:67:7f:8c:7b:18:4a:d0:90:28:fd:de:96:b6:a6:10:98:50:aa:80:01:75:ea:bc:db:bb:68:4a:96:c2:eb:63:79:df:ea:08:d3:2f:e2:33:1f:e1:03:23:3a:d5:8d:cd:b1:e6:e0:77:cb:9f:24:ea:ec:5c:25:af sha1 ee:b0:dd:9b:24:77:be:e3:20:9a:91:4c:db:a9:4c:1c:4a:9b:de:d9
+a0:00:00:00:04 f8 000000 rsa 03 a1:f5:e1:c9:bd:86:50:bd:43:ab:6e:e5:6b:89:1e:f7:45:9c:0a:24:fa:84:f9:12:7d:1a:6c:79:d4:93:0f:6d:b1:85:2e:25:10:f1:8b:61:cd:35:4d:b8:3a:35:6b:d1:90:b8:8a:b8:df:04:28:4d:02:a4:20:4a:7b:6c:b7:c5:55:19:77:a9:b3:63:79:ca:3d:e1:a0:8e:69:f3:01:c9:5c:c1:c2:05:06:95:92:75:f4:17:23:dd:5d:29:25:29:05:79:e5:a9:5b:0d:f6:32:3f:c8:e9:27:3d:6f:84:91:98:c4:99:62:09:16:6d:9b:fc:97:3c:36:1c:c8:26:e1 sha1 f0:6e:cc:6d:2a:ae:bf:25:9b:7e:75:5a:38:d9:a9:b2:4e:2f:f3:dd
+a0:00:00:00:04 f9 000000 rsa 03 a9:9a:6d:3e:07:18:89:ed:9e:3a:0c:39:1c:69:b0:b8:04:fc:16:0b:2b:4b:dd:57:0c:92:dd:5a:0f:45:f5:3e:86:21:f7:c9:6c:40:22:42:66:73:5e:1e:e1:b3:c0:62:38:ae:35:04:63:20:fd:8e:81:f8:ce:b3:f8:b4:c9:7b:94:09:30:a3:ac:5e:79:00:86:da:d4:1a:6a:4f:51:17:ba:1c:e2:43:8a:51:ac:05:3e:b0:02:ae:d8:66:d2:c4:58:fd:73:35:90:21:a1:20:29:a0:c0:43:04:5c:11:66:4f:e0:21:9e:c6:3c:10:bf:21:55:bb:27:84:60:9a:10:64:21:d4:51:63:79:97:38:c1:c3:09:09:bb:6c:6f:e5:2b:bb:76:39:7b:97:40:ce:06:4a:61:3f:f8:41:11:85:f0:88:42:a4:23:ea:d2:0e:df:fb:ff:1c:d6:c3:fe:0c:98:21:47:91:99:c2:6d:85:72:cc:8a:ff:f0:87:a9:c3 sha1 33:67:12:dc:c2:85:54:80:9c:6a:a9:b0:23:58:de:6f:75:51:64:db
+a0:00:00:00:04 fa 000000 rsa 03 a9:0f:cd:55:aa:2d:5d:99:63:e3:5e:d0:f4:40:17:76:99:83:2f:49:c6:ba:b1:5c:da:e5:79:4b:e9:3f:93:4d:44:62:d5:d1:27:62:e4:8c:38:ba:83:d8:44:5d:ea:a7:41:95:a3:01:a1:02:b2:f1:14:ea:da:0d:18:0e:e5:e7:a5:c7:3e:0c:4e:11:f6:7a:43:dd:ab:5d:55:68:3b:14:74:cc:06:27:f4:4b:8d:30:88:a4:92:ff:aa:da:d4:f4:24:22:d0:e7:01:35:36:c3:c4:9a:d3:d0:fa:e9:64:59:b0:f6:b1:b6:05:65:38:a3:d6:d4:46:40:f9:44:67:b1:08:86:7d:ec:40:fa:ae:cd:74:0c:00:e2:b7:a8:85:2d sha1 5b:ed:40:68:d9:6e:a1:6d:2d:77:e0:3d:60:36:fc:7a:16:0e:a9:9c
+b0:12:34:56:78 00 000000 rsa 03 9c:6b:e5:ad:b1:0b:4b:e3:dc:e2:09:9b:4b:21:06:72:b8:96:56:eb:a0:91:20:4f:61:3e:cc:62:3b:ed:c9:c6:d7:7b:66:0e:8b:ae:ea:7f:7c:e3:0f:1b:15:38:79:a4:e3:64:59:34:3d:1f:e4:7a:cd:bd:41:fc:d7:10:03:0c:2b:a1:d9:46:15:97:98:2c:6e:1b:dd:08:55:4b:72:6f:5e:ff:79:13:ce:59:e7:9e:35:72:95:c3:21:e2:6d:0b:8b:e2:70:a9:44:23:45:c7:53:e2:aa:2a:cf:c9:d3:08:50:60:2f:e6:ca:c0:0c:6d:df:6b:8d:9d:9b:48:79:b2:82:6b:04:2a:07:f0:e5:ae:52:6a:3d:3c:4d:22:c7:2b:9e:aa:52:ee:d8:89:38:66:f8:66:38:7a:c0:5a:13:99 sha1 5d:29:70:e6:46:75:72:7e:60:46:07:65:a8:db:75:34:2a:e1:47:83
+b0:12:34:56:78 02 000000 rsa 03 a9:9a:6d:3e:07:18:89:ed:9e:3a:0c:39:1c:69:b0:b8:04:fc:16:0b:2b:4b:dd:57:0c:92:dd:5a:0f:45:f5:3e:86:21:f7:c9:6c:40:22:42:66:73:5e:1e:e1:b3:c0:62:38:ae:35:04:63:20:fd:8e:81:f8:ce:b3:f8:b4:c9:7b:94:09:30:a3:ac:5e:79:00:86:da:d4:1a:6a:4f:51:17:ba:1c:e2:43:8a:51:ac:05:3e:b0:02:ae:d8:66:d2:c4:58:fd:73:35:90:21:a1:20:29:a0:c0:43:04:5c:11:66:4f:e0:21:9e:c6:3c:10:bf:21:55:bb:27:84:60:9a:10:64:21:d4:51:63:79:97:38:c1:c3:09:09:bb:6c:6f:e5:2b:bb:76:39:7b:97:40:ce:06:4a:61:3f:f8:41:11:85:f0:88:42:a4:23:ea:d2:0e:df:fb:ff:1c:d6:c3:fe:0c:98:21:47:91:99:c2:6d:85:72:cc:8a:ff:f0:87:a9:c3 sha1 29:4b:e2:02:39:ab:15:24:5a:63:be:a4:6c:c6:c1:75:a2:55:62:d1
+b0:12:34:56:78 05 000000 rsa 03 a1:f5:e1:c9:bd:86:50:bd:43:ab:6e:e5:6b:89:1e:f7:45:9c:0a:24:fa:84:f9:12:7d:1a:6c:79:d4:93:0f:6d:b1:85:2e:25:10:f1:8b:61:cd:35:4d:b8:3a:35:6b:d1:90:b8:8a:b8:df:04:28:4d:02:a4:20:4a:7b:6c:b7:c5:55:19:77:a9:b3:63:79:ca:3d:e1:a0:8e:69:f3:01:c9:5c:c1:c2:05:06:95:92:75:f4:17:23:dd:5d:29:25:29:05:79:e5:a9:5b:0d:f6:32:3f:c8:e9:27:3d:6f:84:91:98:c4:99:62:09:16:6d:9b:fc:97:3c:36:1c:c8:26:e1 sha1 b9:a1:d6:5c:af:e0:6b:05:4e:dd:7e:a8:25:97:ab:85:f1:30:e6:63
+b0:12:34:56:78 f3 000000 rsa 01:00:01 94:ea:62:f6:d5:83:20:e3:54:c0:22:ad:dc:f0:55:9d:8c:f2:06:cd:92:e8:69:56:49:05:ce:21:d7:20:f9:71:b7:ae:a3:74:83:0e:be:17:57:11:5a:85:e0:88:d4:1c:6b:77:cf:5e:c8:21:f3:0b:1d:89:04:17:bf:2f:a3:1e:59:08:de:d5:fa:67:7f:8c:7b:18:4a:d0:90:28:fd:de:96:b6:a6:10:98:50:aa:80:01:75:ea:bc:db:bb:68:4a:96:c2:eb:63:79:df:ea:08:d3:2f:e2:33:1f:e1:03:23:3a:d5:8d:cd:b1:e6:e0:77:cb:9f:24:ea:ec:5c:25:af sha1 56:94:b0:d2:78:48:18:14:a0:5e:12:b5:58:ce:c1:23:48:65:aa:5d
+b0:12:34:56:78 f5 000000 rsa 03 a2:5a:6b:d7:83:a5:ef:6b:8f:b6:f8:30:55:c2:60:f5:f9:9e:a1:66:78:f3:b9:05:3e:0f:64:98:e8:2c:3f:5d:1e:8c:38:f1:35:88:01:7e:2b:12:b3:d8:ff:6f:50:16:7f:46:44:29:10:72:9e:9e:4d:1b:37:39:e5:06:7c:0a:c7:a1:f4:48:7e:35:f6:75:bc:16:e2:33:31:51:65:cb:14:2b:fd:b2:5e:30:1a:63:2a:54:a3:37:1e:ba:b6:57:2d:ee:ba:f3:70:f3:37:f0:57:ee:73:b4:ae:46:d1:a8:bc:4d:a8:53:ec:3c:c1:2c:8c:bc:2d:a1:83:22:d6:85:30:c7:0b:22:bd:ac:35:1d:d3:60:68:ae:32:1e:11:ab:f2:64:f4:d3:56:9b:b7:12:14:54:50:05:55:8d:e2:60:83:c7:35:db:77:63:68:17:2f:e8:c2:f5:c8:5e:8b:5b:89:0c:c6:82:91:1d:2d:e7:1f:a6:26:b8:81:7f:cc:c0:89:22:b7:03:86:9f:3b:ae:ac:14:59:d7:7c:d8:53:76:bc:36:18:2f:42:38:31:4d:6c:42:12:fb:dd:7f:23:d3 sha1 f7:5e:88:02:85:5c:9b:14:02:7e:51:73:45:71:7e:5c:36:35:b9:1b
+b0:12:34:56:78 f6 000000 rsa 03 a1:f5:e1:c9:bd:86:50:bd:43:ab:6e:e5:6b:89:1e:f7:45:9c:0a:24:fa:84:f9:12:7d:1a:6c:79:d4:93:0f:6d:b1:85:2e:25:10:f1:8b:61:cd:35:4d:b8:3a:35:6b:d1:90:b8:8a:b8:df:04:28:4d:02:a4:20:4a:7b:6c:b7:c5:55:19:77:a9:b3:63:79:ca:3d:e1:a0:8e:69:f3:01:c9:5c:c1:c2:05:06:95:92:75:f4:17:23:dd:5d:29:25:29:05:79:e5:a9:5b:0d:f6:32:3f:c8:e9:27:3d:6f:84:91:98:c4:99:62:09:16:6d:9b:fc:97:3c:36:1c:c8:26:e1 sha1 e9:40:6b:65:10:c1:43:ab:1e:9b:9d:79:a3:c1:df:f8:90:9a:34:7c
+b0:12:34:56:78 f7 000000 rsa 03 98:f0:c7:70:f2:38:64:c2:e7:66:df:02:d1:e8:33:df:f4:ff:e9:2d:69:6e:16:42:f0:a8:8c:56:94:c6:47:9d:16:db:15:37:bf:e2:9e:4f:dc:6e:6e:8a:fd:1b:0e:b7:ea:01:24:72:3c:33:31:79:bf:19:e9:3f:10:65:8b:2f:77:6e:82:9e:87:da:ed:a9:c9:4a:8b:33:82:19:9a:35:0c:07:79:77:c9:7a:ff:08:fd:11:31:0a:c9:50:a7:2c:3c:a5:00:2e:f5:13:fc:cc:28:6e:64:6e:3c:53:87:53:5d:50:95:14:b3:b3:26:e1:23:4f:9c:b4:8c:36:dd:d4:4b:41:6d:23:65:40:34:a6:6f:40:3b:a5:11:c5:ef:a3 sha1 f7:81:13:e8:60:f0:30:a8:72:92:3f:ce:93:e3:38:1c:77:a4:2a:30
+b0:12:34:56:78 f8 000000 rsa 03 a9:9a:6d:3e:07:18:89:ed:9e:3a:0c:39:1c:69:b0:b8:04:fc:16:0b:2b:4b:dd:57:0c:92:dd:5a:0f:45:f5:3e:86:21:f7:c9:6c:40:22:42:66:73:5e:1e:e1:b3:c0:62:38:ae:35:04:63:20:fd:8e:81:f8:ce:b3:f8:b4:c9:7b:94:09:30:a3:ac:5e:79:00:86:da:d4:1a:6a:4f:51:17:ba:1c:e2:43:8a:51:ac:05:3e:b0:02:ae:d8:66:d2:c4:58:fd:73:35:90:21:a1:20:29:a0:c0:43:04:5c:11:66:4f:e0:21:9e:c6:3c:10:bf:21:55:bb:27:84:60:9a:10:64:21:d4:51:63:79:97:38:c1:c3:09:09:bb:6c:6f:e5:2b:bb:76:39:7b:97:40:ce:06:4a:61:3f:f8:41:11:85:f0:88:42:a4:23:ea:d2:0e:df:fb:ff:1c:d6:c3:fe:0c:98:21:47:91:99:c2:6d:85:72:cc:8a:ff:f0:87:a9:c3 sha1 66:46:9c:88:e7:dc:11:15:29:c7:d3:79:d7:93:8c:8d:f3:e4:c2:5e
+b0:12:34:56:78 f9 000000 rsa 01:00:01 a6:e6:fb:72:17:95:06:f8:60:cc:ca:8c:27:f9:9c:ec:d9:4c:7d:4f:31:91:d3:03:bb:ee:37:48:1c:7a:a1:5f:23:3b:a7:55:e9:e4:37:63:45:a9:a6:7e:79:94:bd:c1:c6:80:bb:35:22:d8:c9:3e:b0:cc:c9:1a:d3:1a:d4:50:da:30:d3:37:66:2d:19:ac:03:e2:b4:ef:5f:6e:c1:82:82:d4:91:e1:97:67:d7:b2:45:42:df:de:ff:6f:62:18:55:03:53:20:69:bb:b3:69:e3:bb:9f:b1:9a:c6:f1:c3:0b:97:d2:49:ee:e7:64:e0:ba:c9:7f:25:c8:73:d9:73:95:3e:51:53:a4:20:64:bb:fa:bf:d0:6a:4b:b4:86:86:0b:f6:63:74:06:c9:fc:36:81:3a:4a:75:f7:5c:31:cc:a9:f6:9f:8d:e5:9a:de:ce:f6:bd:e7:e0:78:00:fc:be:03:5d:31:76:af:84:73:e2:3e:9a:a3:df:ee:22:11:96:d1:14:83:02:67:7c:72:0c:fe:25:44:a0:3d:b5:53:e7:f1:b8:42:7b:a1:cc:72:b0:f2:9b:12:df:ef:4c:08:1d:07:6d:35:3e:71:88:0a:ad:ff:38:63:52:af:0a:b7:b2:8e:d4:9e:1e:67:2d:11:f9 sha1 ae:ac:a4:54:80:c8:83:4c:b0:be:bd:cc:57:0b:7b:2b:74:bb:4b:79
index 3c3c1f1129b86509c89c877398b22e737e9c451a..c53b02af1a1dd1e4e58225a0f2dc905332e312ae 100644 (file)
@@ -8,7 +8,9 @@
 // EMV commands
 //-----------------------------------------------------------------------------
 
+#include <ctype.h>
 #include "cmdemv.h"
+#include "test/cryptotest.h"
 
 int UsageCmdHFEMVSelect(void) {
        PrintAndLog("HELP :  Executes select applet command:\n");
@@ -68,7 +70,7 @@ int CmdHFEMVSelect(const char *cmd) {
                                        return 1;
                }
 
-               if (isxdigit(c)) {
+               if (isxdigit((unsigned char)c)) {
                        switch(param_gethex_to_eol(cmd, cmdp, data, sizeof(data), &datalen)) {
                        case 1:
                                PrintAndLog("Invalid HEX value.");
@@ -277,34 +279,48 @@ int CmdHFEMVPPSE(const char *cmd) {
 
 int UsageCmdHFEMVExec(void) {
        PrintAndLog("HELP :  Executes EMV contactless transaction:\n");
-       PrintAndLog("Usage:  hf emv exec [-s][-a][-t]\n");
+       PrintAndLog("Usage:  hf emv exec [-s][-a][-t][-f][-v][-c][-x][-g]\n");
        PrintAndLog("  Options:");
        PrintAndLog("  -s       : select card");
        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("  -g       : VISA. generate AC from GPO\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");
+       PrintAndLog(" hf emv exec -s -a -t -> execute MSD transaction");
+       PrintAndLog(" hf emv exec -s -a -t -c -> execute CDA transaction");
        return 0;
 }
 
 #define TLV_ADD(tag, value)( tlvdb_add(tlvRoot, tlvdb_fixed(tag, sizeof(value) - 1, (const unsigned char *)value)) )
+#define dreturn(n) {free(pdol_data_tlv);tlvdb_free(tlvSelect);tlvdb_free(tlvRoot);DropField();return n;}
 
 int CmdHFEMVExec(const char *cmd) {
        bool activateField = false;
        bool showAPDU = false;
        bool decodeTLV = false;
        bool forceSearch = false;
+       enum TransactionType TrType = TT_MSD;
+       bool GenACGPO = false;
 
        uint8_t buf[APDU_RES_LEN] = {0};
        size_t len = 0;
        uint16_t sw = 0;
        uint8_t AID[APDU_AID_LEN] = {0};
        size_t AIDlen = 0;
+       uint8_t ODAiList[4096];
+       size_t ODAiListLen = 0;
        
        int res;
        
+       struct tlvdb *tlvSelect = NULL;
+       struct tlvdb *tlvRoot = NULL;
+       struct tlv *pdol_data_tlv = NULL;
+
        if (strlen(cmd) < 1) {
                UsageCmdHFEMVExec();
                return 0;
@@ -317,7 +333,7 @@ int CmdHFEMVExec(const char *cmd) {
                        switch (param_getchar_indx(cmd, 1, cmdp)) {
                                case 'h':
                                case 'H':
-                                       UsageCmdHFEMVPPSE();
+                                       UsageCmdHFEMVExec();
                                        return 0;
                                case 's':
                                case 'S':
@@ -335,6 +351,22 @@ 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;
+                               case 'g':
+                               case 'G':
+                                       GenACGPO = true;
+                                       break;
                                default:
                                        PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp));
                                        return 1;
@@ -344,7 +376,6 @@ int CmdHFEMVExec(const char *cmd) {
 
        
        // init applets list tree
-       struct tlvdb *tlvSelect = NULL;
        const char *al = "Applets list";
        tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al);
 
@@ -368,8 +399,7 @@ int CmdHFEMVExec(const char *cmd) {
                PrintAndLog("\n* Search AID in list.");
                SetAPDULogging(false);
                if (EMVSearch(activateField, true, decodeTLV, tlvSelect)) {
-                       tlvdb_free(tlvSelect);
-                       return 2;
+                       dreturn(2);
                }
 
                // check search and select application id
@@ -378,14 +408,13 @@ int CmdHFEMVExec(const char *cmd) {
        }
        
        // Init TLV tree
-       struct tlvdb *tlvRoot = NULL;
        const char *alr = "Root terminal TLV tree";
        tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr);
        
        // check if we found EMV application on card
        if (!AIDlen) {
                PrintAndLog("Can't select AID. EMV AID not found");
-               return 2;
+               dreturn(2);
        }
        
        // Select
@@ -395,20 +424,40 @@ int CmdHFEMVExec(const char *cmd) {
        
        if (res) {      
                PrintAndLog("Can't select AID (%d). Exit...", res);
-               return 3;
+               dreturn(3);
        }
        
        if (decodeTLV)
                TLVPrintFromBuffer(buf, len);
        PrintAndLog("* Selected.");
        
-PrintAndLog("-----BREAK.");
-return 0;
        PrintAndLog("\n* Init transaction parameters.");
 
     //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4
-       TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // E6
-    //9F02:(Amount, Authorised (Numeric)) len:6
+       char *qVSDC = "\x26\x00\x00\x00";
+       if (GenACGPO) {
+               qVSDC = "\x26\x80\x00\x00";
+       }
+       switch(TrType) {
+               case TT_MSD:
+                       TLV_ADD(0x9F66, "\x86\x00\x00\x00"); // MSD
+                       break;
+               // not standard for contactless. just for test.
+               case TT_VSDC:  
+                       TLV_ADD(0x9F66, "\x46\x00\x00\x00"); // VSDC
+                       break;
+               case TT_QVSDCMCHIP:
+                       TLV_ADD(0x9F66, qVSDC); // qVSDC
+                       break;
+               case TT_CDA:
+                       TLV_ADD(0x9F66, qVSDC); // qVSDC (VISA CDA not enabled)
+                       break;
+               default:
+                       TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC
+                       break;
+       }
+       
+    //9F02:(Amount, authorized (Numeric)) len:6
        TLV_ADD(0x9F02, "\x00\x00\x00\x00\x01\x00");
     //9F1A:(Terminal Country Code) len:2
        TLV_ADD(0x9F1A, "ru");
@@ -421,53 +470,82 @@ return 0;
        TLV_ADD(0x9C,   "\x00");
        // 9F37 Unpredictable Number len:4
        TLV_ADD(0x9F37, "\x01\x02\x03\x04");
+       // 9F6A Unpredictable Number (MSD for UDOL) len:4
+       TLV_ADD(0x9F6A, "\x01\x02\x03\x04");
 
-       TLVPrintFromTLV(tlvRoot);
+       TLVPrintFromTLV(tlvRoot); // TODO delete!!!
        
        PrintAndLog("\n* Calc PDOL.");
-       struct tlv *pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83);
+       pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83);
        if (!pdol_data_tlv){
                PrintAndLog("ERROR: can't create PDOL TLV.");
-               return 4;
+               dreturn(4);
        }
        
        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) {
                PrintAndLog("ERROR: can't create PDOL data.");
-               return 4;
+               dreturn(4);
        }
        PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len));
 
-//PrintAndLog("-----BREAK.");
-//return 0;
        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);
+       free(pdol_data_tlv_data);
+       //free(pdol_data_tlv); --- free on exit.
        
        if (res) {      
                PrintAndLog("GPO error(%d): %4x. Exit...", res, sw);
-               return 5;
+               dreturn(5);
        }
 
        // process response template format 1 [id:80  2b AIP + x4b AFL] and format 2 [id:77 TLV]
        if (buf[0] == 0x80) {
-               
-               
-               
                if (decodeTLV){
                        PrintAndLog("GPO response format1:");
                        TLVPrintFromBuffer(buf, len);
                }
-       } else {
-               
-               
                
+               if (len < 4 || (len - 4) % 4) {
+                       PrintAndLog("ERROR: GPO response format1 parsing error. length=%d", len);
+               } else {
+                       // AIP
+                       struct tlvdb * f1AIP = tlvdb_fixed(0x82, 2, buf + 2);
+                       tlvdb_add(tlvRoot, f1AIP);
+                       if (decodeTLV){
+                               PrintAndLog("\n* * Decode response format 1 (0x80) AIP and AFL:");
+                               TLVPrintFromTLV(f1AIP);
+                       }
+
+                       // AFL
+                       struct tlvdb * f1AFL = tlvdb_fixed(0x94, len - 4, buf + 2 + 2);
+                       tlvdb_add(tlvRoot, f1AFL);
+                       if (decodeTLV)
+                               TLVPrintFromTLV(f1AFL);
+               }               
+       } else {
                if (decodeTLV)
                        TLVPrintFromBuffer(buf, len);
        }
        
+       // extract PAN from track2
+       {
+               const struct tlv *track2 = tlvdb_get(tlvRoot, 0x57, NULL);
+               if (!tlvdb_get(tlvRoot, 0x5a, NULL) && track2 && track2->len >= 8) {
+                       struct tlvdb *pan = GetPANFromTrack2(track2);
+                       if (pan) {
+                               tlvdb_add(tlvRoot, pan); 
+                               
+                               const struct tlv *pantlv = tlvdb_get(tlvRoot, 0x5a, NULL);      
+                               PrintAndLog("\n* * Extracted PAN from track2: %s", sprint_hex(pantlv->value, pantlv->len));
+                       } else {
+                               PrintAndLog("\n* * WARNING: Can't extract PAN from track2.");
+                       }
+               }
+       }
+       
        PrintAndLog("\n* Read records from AFL.");
        const struct tlv *AFL = tlvdb_get(tlvRoot, 0x94, NULL);
        if (!AFL || !AFL->len) {
@@ -506,9 +584,23 @@ return 0;
                                        PrintAndLog("");
                                }
                                
+                               // Build Input list for Offline Data Authentication
+                               // EMV 4.3 book3 10.3, page 96
                                if (SFIoffline) {
-                                       // here will be offline records storing...
-                                       // dont foget: if (sfi < 11)
+                                       if (SFI < 11) {
+                                               const unsigned char *abuf = buf;
+                                               size_t elmlen = len;
+                                               struct tlv e;
+                                               if (tlv_parse_tl(&abuf, &elmlen, &e)) {
+                                                       memcpy(&ODAiList[ODAiListLen], &buf[len - elmlen], elmlen);
+                                                       ODAiListLen += elmlen;
+                                               } else {
+                                                       PrintAndLog("ERROR SFI[%02x]. Creating input list for Offline Data Authentication error.", SFI);
+                                               }
+                                       } else {
+                                               memcpy(&ODAiList[ODAiListLen], buf, len);
+                                               ODAiListLen += len;
+                                       }
                                }
                        }
                }
@@ -516,13 +608,218 @@ return 0;
                break;
        }       
        
-       // additional contacless EMV commands (fDDA, CDA, external authenticate)
+       // copy Input list for Offline Data Authentication
+       if (ODAiListLen) {
+               struct tlvdb *oda = tlvdb_fixed(0x21, ODAiListLen, ODAiList); // not a standard tag
+               tlvdb_add(tlvRoot, oda); 
+               PrintAndLog("* Input list for Offline Data Authentication added to TLV. len=%d \n", ODAiListLen);
+       }
+       
+       // get AIP
+       const struct tlv *AIPtlv = tlvdb_get(tlvRoot, 0x82, NULL);      
+       uint16_t AIP = AIPtlv->value[0] + AIPtlv->value[1] * 0x100;
+       PrintAndLog("* * AIP=%04x", AIP);
+
+       // SDA
+       if (AIP & 0x0040) {
+               PrintAndLog("\n* SDA");
+               trSDA(tlvRoot);
+       }
+
+       // DDA
+       if (AIP & 0x0020) {
+               PrintAndLog("\n* DDA");
+               trDDA(decodeTLV, tlvRoot);
+       }       
        
+       // transaction check
+       
+       // qVSDC
+       if (TrType == TT_QVSDCMCHIP|| TrType == TT_CDA){
+               // 9F26: Application Cryptogram
+               const struct tlv *AC = tlvdb_get(tlvRoot, 0x9F26, NULL);
+               if (AC) {
+                       PrintAndLog("\n--> qVSDC transaction.");
+                       PrintAndLog("* AC path");
+                       
+                       // 9F36: Application Transaction Counter (ATC)
+                       const struct tlv *ATC = tlvdb_get(tlvRoot, 0x9F36, NULL);
+                       if (ATC) {
+                       
+                               // 9F10: Issuer Application Data - optional
+                               const struct tlv *IAD = tlvdb_get(tlvRoot, 0x9F10, NULL);
+
+                               // print AC data
+                               PrintAndLog("ATC: %s", sprint_hex(ATC->value, ATC->len));
+                               PrintAndLog("AC: %s", sprint_hex(AC->value, AC->len));
+                               if (IAD){
+                                       PrintAndLog("IAD: %s", sprint_hex(IAD->value, IAD->len));
+                                       
+                                       if (IAD->len >= IAD->value[0] + 1) {
+                                               PrintAndLog("\tKey index:  0x%02x", IAD->value[1]);
+                                               PrintAndLog("\tCrypto ver: 0x%02x(%03d)", IAD->value[2], IAD->value[2]);
+                                               PrintAndLog("\tCVR:", sprint_hex(&IAD->value[3], IAD->value[0] - 2));
+                                               struct tlvdb * cvr = tlvdb_fixed(0x20, IAD->value[0] - 2, &IAD->value[3]);
+                                               TLVPrintFromTLVLev(cvr, 1);
+                                       }
+                               } else {
+                                       PrintAndLog("WARNING: IAD not found.");
+                               }
+                               
+                       } else {
+                               PrintAndLog("ERROR AC: Application Transaction Counter (ATC) not found.");
+                       }
+               }
+       }
        
+       // 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);
+                               dreturn(6);
+                       }
+                       if (len < 4) {
+                               PrintAndLog("ERROR GetChallenge. Wrong challenge length %d", len);
+                               dreturn(6);
+                       }
+                       
+                       // 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.");
+                               dreturn(6);
+                       }
+                       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);
+                       
+                       if (res) {      
+                               PrintAndLog("AC1 error(%d): %4x. Exit...", res, sw);
+                               dreturn(7);
+                       }
+                       
+                       if (decodeTLV)
+                               TLVPrintFromBuffer(buf, len);
+                       
+                       // CDA
+                       PrintAndLog("\n* CDA:");
+                       struct tlvdb *ac_tlv = tlvdb_parse_multi(buf, len);
+                       res = trCDA(tlvRoot, ac_tlv, pdol_data_tlv, cdol_data_tlv);
+                       if (res) {      
+                               PrintAndLog("CDA error (%d)", res);
+                       }
+                       free(ac_tlv);
+                       free(cdol_data_tlv);
+                       
+                       PrintAndLog("\n* 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 && TrType == TT_MSD) { 
+               PrintAndLog("\n--> MSD transaction.");
+               
+               PrintAndLog("* MSD dCVV path. Check dCVV");
+
+               const struct tlv *track2 = tlvdb_get(tlvRoot, 0x57, NULL);
+               if (track2) {
+                       PrintAndLog("Track2: %s", sprint_hex(track2->value, track2->len));
+
+                       struct tlvdb *dCVV = GetdCVVRawFromTrack2(track2);
+                       PrintAndLog("dCVV raw data:");
+                       TLVPrintFromTLV(dCVV);
+                       
+                       if (GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD) {
+                               PrintAndLog("\n* Mastercard calculate UDOL");
+
+                               // UDOL (9F69)
+                               const struct tlv *UDOL = tlvdb_get(tlvRoot, 0x9F69, NULL);
+                               // UDOL(9F69) default: 9F6A (Unpredictable number) 4 bytes
+                               const struct tlv defUDOL = {
+                                       .tag = 0x01,
+                                       .len = 3,
+                                       .value = (uint8_t *)"\x9f\x6a\x04",
+                               };
+                               if (!UDOL)
+                                       PrintAndLog("Use default UDOL.");
+
+                               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.");
+                                       dreturn(8);
+                               }
+
+                               PrintAndLog("UDOL data[%d]: %s", udol_data_tlv->len, sprint_hex(udol_data_tlv->value, udol_data_tlv->len));
+                               
+                               PrintAndLog("\n* Mastercard compute cryptographic checksum(UDOL)");
+                               
+                               res = MSCComputeCryptoChecksum(true, (uint8_t *)udol_data_tlv->value, udol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot);
+                               if (res) {
+                                       PrintAndLog("ERROR Compute Crypto Checksum. APDU error %4x", sw);
+                                       free(udol_data_tlv);
+                                       dreturn(9);
+                               }
+                               
+                               if (decodeTLV) {
+                                       TLVPrintFromBuffer(buf, len);
+                                       PrintAndLog("");
+                               }
+                               free(udol_data_tlv);
+
+                       }
+               } else {
+                       PrintAndLog("ERROR MSD: Track2 data not found.");
+               }
+       }
+
        // DropField
        DropField();
        
        // Destroy TLV's
+       free(pdol_data_tlv);
        tlvdb_free(tlvSelect);
        tlvdb_free(tlvRoot);
 
@@ -531,6 +828,10 @@ return 0;
        return 0;
 }
 
+int CmdHFEMVTest(const char *cmd) {
+       return ExecuteCryptoTests(true);
+}
+
 int CmdHelp(const char *Cmd);
 static command_t CommandTable[] =  {
        {"help",        CmdHelp,                1,      "This help"},
@@ -538,6 +839,7 @@ static command_t CommandTable[] =  {
        {"pse",         CmdHFEMVPPSE,   0,      "Execute PPSE. It selects 2PAY.SYS.DDF01 or 1PAY.SYS.DDF01 directory."},
        {"search",      CmdHFEMVSearch, 0,      "Try to select all applets from applets list and print installed applets."},
        {"select",      CmdHFEMVSelect, 0,      "Select applet."},
+       {"test",        CmdHFEMVTest,   0,      "Crypto logic test."},
        {NULL, NULL, 0, NULL}
 };
 
index 78796efab65d4ba5b231a022316675a6636e2f98..b3f76508a85e9ef1ea8c7dc3ab324a8550f1276c 100644 (file)
@@ -15,6 +15,7 @@
 #include <stdlib.h>
 #include <inttypes.h>
 #include <string.h>
+#include <ctype.h>
 #include "proxmark3.h"
 #include "ui.h"
 #include "cmdparser.h"
diff --git a/client/emv/crypto.c b/client/emv/crypto.c
new file mode 100644 (file)
index 0000000..c8ced6d
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * libopenemv - a library to work with EMV family of smart cards
+ * Copyright (C) 2015 Dmitry Eremin-Solenikov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "crypto.h"
+#include "crypto_backend.h"
+
+#include <string.h>
+
+static struct crypto_backend *crypto_backend;
+
+static bool crypto_init(void)
+{
+       if (crypto_backend)
+               return true;
+
+       crypto_backend = crypto_polarssl_init(); 
+
+       if (!crypto_backend)
+               return false;
+
+       return true;
+}
+
+struct crypto_hash *crypto_hash_open(enum crypto_algo_hash hash)
+{
+       struct crypto_hash *ch;
+
+       if (!crypto_init())
+               return NULL;
+
+       ch = crypto_backend->hash_open(hash);
+       if (ch)
+               ch->algo = hash;
+
+       return ch;
+}
+
+void crypto_hash_close(struct crypto_hash *ch)
+{
+       ch->close(ch);
+}
+
+void crypto_hash_write(struct crypto_hash *ch, const unsigned char *buf, size_t len)
+{
+       ch->write(ch, buf, len);
+}
+
+unsigned char *crypto_hash_read(struct crypto_hash *ch)
+{
+       return ch->read(ch);
+}
+
+size_t crypto_hash_get_size(const struct crypto_hash *ch)
+{
+       return ch->get_size(ch);
+}
+
+struct crypto_pk *crypto_pk_open(enum crypto_algo_pk pk, ...)
+{
+       struct crypto_pk *cp;
+       va_list vl;
+
+       if (!crypto_init())
+               return NULL;
+
+       va_start(vl, pk);
+       cp = crypto_backend->pk_open(pk, vl);
+       va_end(vl);
+
+       if (cp)
+               cp->algo = pk;
+
+       return cp;
+}
+
+struct crypto_pk *crypto_pk_open_priv(enum crypto_algo_pk pk, ...)
+{
+       struct crypto_pk *cp;
+       va_list vl;
+
+       if (!crypto_init())
+               return NULL;
+
+       if (!crypto_backend->pk_open_priv)
+               return NULL;
+
+       va_start(vl, pk);
+       cp = crypto_backend->pk_open_priv(pk, vl);
+       va_end(vl);
+
+       if (cp)
+               cp->algo = pk;
+
+       return cp;
+}
+
+struct crypto_pk *crypto_pk_genkey(enum crypto_algo_pk pk, ...)
+{
+       struct crypto_pk *cp;
+       va_list vl;
+
+       if (!crypto_init())
+               return NULL;
+
+       if (!crypto_backend->pk_genkey)
+               return NULL;
+
+       va_start(vl, pk);
+       cp = crypto_backend->pk_genkey(pk, vl);
+       va_end(vl);
+
+       if (cp)
+               cp->algo = pk;
+
+       return cp;
+}
+
+void crypto_pk_close(struct crypto_pk *cp)
+{
+       cp->close(cp);
+}
+
+unsigned char *crypto_pk_encrypt(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen)
+{
+       return cp->encrypt(cp, buf, len, clen);
+}
+
+unsigned char *crypto_pk_decrypt(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen)
+{
+       if (!cp->decrypt) {
+               *clen = 0;
+
+               return NULL;
+       }
+
+       return cp->decrypt(cp, buf, len, clen);
+}
+
+enum crypto_algo_pk crypto_pk_get_algo(const struct crypto_pk *cp)
+{
+       if (!cp)
+               return PK_INVALID;
+
+       return cp->algo;
+}
+
+size_t crypto_pk_get_nbits(const struct crypto_pk *cp)
+{
+       if (!cp->get_nbits)
+               return 0;
+
+       return cp->get_nbits(cp);
+}
+
+unsigned char *crypto_pk_get_parameter(const struct crypto_pk *cp, unsigned param, size_t *plen)
+{
+       *plen = 0;
+
+       if (!cp->get_parameter)
+               return NULL;
+
+       return cp->get_parameter(cp, param, plen);
+}
diff --git a/client/emv/crypto.h b/client/emv/crypto.h
new file mode 100644 (file)
index 0000000..940e8b2
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * libopenemv - a library to work with EMV family of smart cards
+ * Copyright (C) 2015 Dmitry Eremin-Solenikov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ */
+
+#ifndef CRYPTO_H
+#define CRYPTO_H
+
+#include <stdbool.h>
+#include <stddef.h>
+
+enum crypto_algo_hash {
+       HASH_INVALID,
+       HASH_SHA_1,
+};
+
+struct crypto_hash *crypto_hash_open(enum crypto_algo_hash hash);
+void crypto_hash_close(struct crypto_hash *ch);
+void crypto_hash_write(struct crypto_hash *ch, const unsigned char *buf, size_t len);
+unsigned char *crypto_hash_read(struct crypto_hash *ch);
+size_t crypto_hash_get_size(const struct crypto_hash *ch);
+
+enum crypto_algo_pk {
+       PK_INVALID,
+       PK_RSA,
+};
+
+struct crypto_pk *crypto_pk_open(enum crypto_algo_pk pk, ...);
+struct crypto_pk *crypto_pk_open_priv(enum crypto_algo_pk pk, ...);
+struct crypto_pk *crypto_pk_genkey(enum crypto_algo_pk pk, ...);
+void crypto_pk_close(struct crypto_pk *cp);
+unsigned char *crypto_pk_encrypt(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen);
+unsigned char *crypto_pk_decrypt(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen);
+enum crypto_algo_pk crypto_pk_get_algo(const struct crypto_pk *cp);
+size_t crypto_pk_get_nbits(const struct crypto_pk *cp);
+unsigned char *crypto_pk_get_parameter(const struct crypto_pk *cp, unsigned param, size_t *plen);
+
+#endif
diff --git a/client/emv/crypto_backend.h b/client/emv/crypto_backend.h
new file mode 100644 (file)
index 0000000..a815ae1
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * libopenemv - a library to work with EMV family of smart cards
+ * Copyright (C) 2015 Dmitry Eremin-Solenikov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ */
+
+#ifndef CRYPTO_BACKEND_H
+#define CRYPTO_BACKEND_H
+
+#include "crypto.h"
+
+#include <stddef.h>
+#include <stdarg.h>
+
+struct crypto_hash {
+       enum crypto_algo_hash algo;
+       void (*write)(struct crypto_hash *ch, const unsigned char *buf, size_t len);
+       unsigned char *(*read)(struct crypto_hash *ch);
+       void (*close)(struct crypto_hash *ch);
+       size_t (*get_size)(const struct crypto_hash *ch);
+};
+
+struct crypto_pk {
+       enum crypto_algo_pk algo;
+       unsigned char *(*encrypt)(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen);
+       unsigned char *(*decrypt)(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen);
+       unsigned char *(*get_parameter)(const struct crypto_pk *cp, unsigned param, size_t *plen);
+       size_t (*get_nbits)(const struct crypto_pk *cp);
+       void (*close)(struct crypto_pk *cp);
+};
+
+struct crypto_backend {
+       struct crypto_hash *(*hash_open)(enum crypto_algo_hash hash);
+       struct crypto_pk *(*pk_open)(enum crypto_algo_pk pk, va_list vl);
+       struct crypto_pk *(*pk_open_priv)(enum crypto_algo_pk pk, va_list vl);
+       struct crypto_pk *(*pk_genkey)(enum crypto_algo_pk pk, va_list vl);
+};
+
+struct crypto_backend *crypto_polarssl_init(void);
+
+#endif
diff --git a/client/emv/crypto_polarssl.c b/client/emv/crypto_polarssl.c
new file mode 100644 (file)
index 0000000..760395c
--- /dev/null
@@ -0,0 +1,351 @@
+/*
+ * libopenemv - a library to work with EMV family of smart cards
+ * Copyright (C) 2015 Dmitry Eremin-Solenikov
+ * Copyright (C) 2017 Merlok
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "crypto.h"
+#include "crypto_backend.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "rsa.h"
+#include "sha1.h"
+
+struct crypto_hash_polarssl {
+       struct crypto_hash ch;
+       sha1_context ctx;
+};
+
+static void crypto_hash_polarssl_close(struct crypto_hash *_ch)
+{
+       struct crypto_hash_polarssl *ch = (struct crypto_hash_polarssl *)_ch;
+
+       free(ch);
+}
+
+static void crypto_hash_polarssl_write(struct crypto_hash *_ch, const unsigned char *buf, size_t len)
+{
+       struct crypto_hash_polarssl *ch = (struct crypto_hash_polarssl *)_ch;
+
+       sha1_update(&(ch->ctx), buf, len);
+}
+
+static unsigned char *crypto_hash_polarssl_read(struct crypto_hash *_ch)
+{
+       struct crypto_hash_polarssl *ch = (struct crypto_hash_polarssl *)_ch;
+
+       static unsigned char sha1sum[20];
+       sha1_finish(&(ch->ctx), sha1sum);
+       return sha1sum;
+}
+
+static size_t crypto_hash_polarssl_get_size(const struct crypto_hash *ch)
+{
+       if (ch->algo == HASH_SHA_1)
+               return 20;
+       else
+               return 0;
+}
+
+static struct crypto_hash *crypto_hash_polarssl_open(enum crypto_algo_hash hash)
+{
+       if (hash != HASH_SHA_1)
+               return NULL;
+
+       struct crypto_hash_polarssl *ch = malloc(sizeof(*ch));
+
+       sha1_starts(&(ch->ctx));
+
+       ch->ch.write = crypto_hash_polarssl_write;
+       ch->ch.read = crypto_hash_polarssl_read;
+       ch->ch.close = crypto_hash_polarssl_close;
+       ch->ch.get_size = crypto_hash_polarssl_get_size;
+
+       return &ch->ch;
+}
+
+struct crypto_pk_polarssl {
+       struct crypto_pk cp;
+       rsa_context ctx;
+};
+
+static struct crypto_pk *crypto_pk_polarssl_open_rsa(va_list vl)
+{
+       struct crypto_pk_polarssl *cp = malloc(sizeof(*cp));
+       memset(cp, 0x00, sizeof(*cp));
+
+       char *mod = va_arg(vl, char *);  // N
+       int modlen = va_arg(vl, size_t);
+       char *exp = va_arg(vl, char *);  // E
+       int explen = va_arg(vl, size_t);
+
+       rsa_init(&cp->ctx, RSA_PKCS_V15, 0);
+       
+       cp->ctx.len = modlen; // size(N) in bytes
+       mpi_read_binary(&cp->ctx.N, (const unsigned char *)mod, modlen);
+       mpi_read_binary(&cp->ctx.E, (const unsigned char *)exp, explen);
+       
+       int res = rsa_check_pubkey(&cp->ctx);
+       if(res != 0) {
+               fprintf(stderr, "PolarSSL public key error res=%x exp=%d mod=%d.\n", res * -1, explen, modlen);
+
+               return NULL;
+       }
+
+       return &cp->cp;
+}
+
+static struct crypto_pk *crypto_pk_polarssl_open_priv_rsa(va_list vl)
+{
+       struct crypto_pk_polarssl *cp = malloc(sizeof(*cp));
+       memset(cp, 0x00, sizeof(*cp));
+       char *mod = va_arg(vl, char *);
+       int modlen = va_arg(vl, size_t);
+       char *exp = va_arg(vl, char *);
+       int explen = va_arg(vl, size_t);
+       char *d = va_arg(vl, char *);
+       int dlen = va_arg(vl, size_t);
+       char *p = va_arg(vl, char *);
+       int plen = va_arg(vl, size_t);
+       char *q = va_arg(vl, char *);
+       int qlen = va_arg(vl, size_t);
+       char *dp = va_arg(vl, char *);
+       int dplen = va_arg(vl, size_t);
+       char *dq = va_arg(vl, char *);
+       int dqlen = va_arg(vl, size_t);
+       // calc QP via Q and P
+//     char *inv = va_arg(vl, char *);
+//     int invlen = va_arg(vl, size_t);        
+       
+       rsa_init(&cp->ctx, RSA_PKCS_V15, 0);
+       
+       cp->ctx.len = modlen; // size(N) in bytes
+       mpi_read_binary(&cp->ctx.N,  (const unsigned char *)mod, modlen);
+       mpi_read_binary(&cp->ctx.E,  (const unsigned char *)exp, explen);
+
+       mpi_read_binary(&cp->ctx.D,  (const unsigned char *)d, dlen);
+       mpi_read_binary(&cp->ctx.P,  (const unsigned char *)p, plen);
+       mpi_read_binary(&cp->ctx.Q,  (const unsigned char *)q, qlen);
+       mpi_read_binary(&cp->ctx.DP, (const unsigned char *)dp, dplen);
+       mpi_read_binary(&cp->ctx.DQ, (const unsigned char *)dq, dqlen);
+       mpi_inv_mod(&cp->ctx.QP, &cp->ctx.Q, &cp->ctx.P);
+       
+       int res = rsa_check_privkey(&cp->ctx);
+       if(res != 0) {
+               fprintf(stderr, "PolarSSL private key error res=%x exp=%d mod=%d.\n", res * -1, explen, modlen);
+               return NULL;
+       }
+
+       return &cp->cp;
+}
+
+static int myrand(void *rng_state, unsigned char *output, size_t len) {
+    size_t i;
+
+    if(rng_state != NULL)
+        rng_state = NULL;
+
+    for( i = 0; i < len; ++i )
+        output[i] = rand();
+    
+    return 0;
+}
+
+
+static struct crypto_pk *crypto_pk_polarssl_genkey_rsa(va_list vl)
+{
+       struct crypto_pk_polarssl *cp = malloc(sizeof(*cp));
+       memset(cp, 0x00, sizeof(*cp));
+       
+       int transient = va_arg(vl, int);
+       unsigned int nbits = va_arg(vl, unsigned int);
+       unsigned int exp = va_arg(vl, unsigned int);
+
+       if (transient) {
+       }
+       
+       int res = rsa_gen_key(&cp->ctx, &myrand, NULL, nbits, exp);
+       if (res) {
+               fprintf(stderr, "PolarSSL private key generation error res=%x exp=%d nbits=%d.\n", res * -1, exp, nbits);
+               return NULL;
+       }
+       
+       return &cp->cp;
+}
+
+static void crypto_pk_polarssl_close(struct crypto_pk *_cp)
+{
+       struct crypto_pk_polarssl *cp = (struct crypto_pk_polarssl *)_cp;
+
+       rsa_free(&cp->ctx);
+       free(cp);
+}
+
+static unsigned char *crypto_pk_polarssl_encrypt(const struct crypto_pk *_cp, const unsigned char *buf, size_t len, size_t *clen)
+{
+       struct crypto_pk_polarssl *cp = (struct crypto_pk_polarssl *)_cp;
+       int res;
+       unsigned char *result;
+       
+       *clen = 0;
+       size_t keylen = mpi_size(&cp->ctx.N);
+
+       result = malloc(keylen);
+       if (!result) {
+               printf("RSA encrypt failed. Can't allocate result memory.\n");
+               return NULL;
+       }
+
+       res = rsa_public(&cp->ctx, buf, result);
+       if(res) {
+               printf("RSA encrypt failed. Error: %x data len: %d key len: %d\n", res * -1, len, keylen);
+               return NULL;
+       }
+       
+       *clen = keylen;
+       
+       return result;
+}
+
+static unsigned char *crypto_pk_polarssl_decrypt(const struct crypto_pk *_cp, const unsigned char *buf, size_t len, size_t *clen)
+{
+       struct crypto_pk_polarssl *cp = (struct crypto_pk_polarssl *)_cp;
+       int res;
+       unsigned char *result;
+       
+       *clen = 0;
+       size_t keylen = mpi_size(&cp->ctx.N);
+
+       result = malloc(keylen);
+       if (!result) {
+               printf("RSA encrypt failed. Can't allocate result memory.\n");
+               return NULL;
+       }
+
+       res = rsa_private(&cp->ctx, buf, result); // CHECK???
+       if(res) {
+               printf("RSA decrypt failed. Error: %x data len: %d key len: %d\n", res * -1, len, keylen);
+               return NULL;
+       }
+       
+       *clen = keylen;
+       
+       return result;
+}
+
+static size_t crypto_pk_polarssl_get_nbits(const struct crypto_pk *_cp)
+{
+       struct crypto_pk_polarssl *cp = (struct crypto_pk_polarssl *)_cp;
+
+       return cp->ctx.len * 8;
+return 0;
+}
+
+static unsigned char *crypto_pk_polarssl_get_parameter(const struct crypto_pk *_cp, unsigned param, size_t *plen)
+{
+       struct crypto_pk_polarssl *cp = (struct crypto_pk_polarssl *)_cp;
+       unsigned char *result = NULL;
+       switch(param){
+               // mod
+               case 0:
+                       *plen = mpi_size(&cp->ctx.N);
+                       result = malloc(*plen);
+                       memset(result, 0x00, *plen);
+                       mpi_write_binary(&cp->ctx.N, result, *plen);
+                       break;
+               // exp
+               case 1:
+                       *plen = mpi_size(&cp->ctx.E);
+                       result = malloc(*plen);
+                       memset(result, 0x00, *plen);
+                       mpi_write_binary(&cp->ctx.E, result, *plen);
+                       break;
+               default:
+                       printf("Error get parameter. Param=%d", param);
+                       break;
+       }
+       
+       return result;
+}
+
+static struct crypto_pk *crypto_pk_polarssl_open(enum crypto_algo_pk pk, va_list vl)
+{
+       struct crypto_pk *cp;
+
+       if (pk == PK_RSA)
+               cp = crypto_pk_polarssl_open_rsa(vl);
+       else
+               return NULL;
+
+       cp->close = crypto_pk_polarssl_close;
+       cp->encrypt = crypto_pk_polarssl_encrypt;
+       cp->get_parameter = crypto_pk_polarssl_get_parameter;
+       cp->get_nbits = crypto_pk_polarssl_get_nbits;
+
+       return cp;
+}
+
+static struct crypto_pk *crypto_pk_polarssl_open_priv(enum crypto_algo_pk pk, va_list vl)
+{
+       struct crypto_pk *cp;
+
+       if (pk == PK_RSA)
+               cp = crypto_pk_polarssl_open_priv_rsa(vl);
+       else
+               return NULL;
+
+       cp->close = crypto_pk_polarssl_close;
+       cp->encrypt = crypto_pk_polarssl_encrypt;
+       cp->decrypt = crypto_pk_polarssl_decrypt;
+       cp->get_parameter = crypto_pk_polarssl_get_parameter;
+       cp->get_nbits = crypto_pk_polarssl_get_nbits;
+
+       return cp;
+}
+
+static struct crypto_pk *crypto_pk_polarssl_genkey(enum crypto_algo_pk pk, va_list vl)
+{
+       struct crypto_pk *cp;
+
+       if (pk == PK_RSA)
+               cp = crypto_pk_polarssl_genkey_rsa(vl);
+       else
+               return NULL;
+
+       cp->close = crypto_pk_polarssl_close;
+       cp->encrypt = crypto_pk_polarssl_encrypt;
+       cp->decrypt = crypto_pk_polarssl_decrypt;
+       cp->get_parameter = crypto_pk_polarssl_get_parameter;
+       cp->get_nbits = crypto_pk_polarssl_get_nbits;
+
+       return cp;
+}
+
+static struct crypto_backend crypto_polarssl_backend = {
+       .hash_open = crypto_hash_polarssl_open,
+       .pk_open = crypto_pk_polarssl_open,
+       .pk_open_priv = crypto_pk_polarssl_open_priv,
+       .pk_genkey = crypto_pk_polarssl_genkey,
+};
+
+struct crypto_backend *crypto_polarssl_init(void)
+{
+       return &crypto_polarssl_backend;
+}
diff --git a/client/emv/emv_pk.c b/client/emv/emv_pk.c
new file mode 100644 (file)
index 0000000..b577855
--- /dev/null
@@ -0,0 +1,524 @@
+/*
+ * libopenemv - a library to work with EMV family of smart cards
+ * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* For asprintf */
+#define _GNU_SOURCE
+#include <stdio.h>
+
+#include "emv_pk.h"
+#include "crypto.h"
+#include "proxmark3.h"
+
+#include <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#define BCD(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : \
+               -1)
+
+#define HEX(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : \
+               ((c) >= 'A' && (c) <= 'F') ? ((c) - 'A' + 10) : \
+               ((c) >= 'a' && (c) <= 'f') ? ((c) - 'a' + 10) : \
+               -1)
+
+#define TOHEX(v) ((v) < 10 ? (v) + '0' : (v) - 10 + 'a')
+
+static ssize_t emv_pk_read_bin(char *buf, unsigned char *bin, size_t size, size_t *read)
+{
+       size_t left = size;
+       char *p = buf;
+       while (*p && *p == ' ')
+               p++;
+
+       while (left > 0) {
+               int c1, c2;
+               c1 = HEX(*p);
+               if (c1 == -1)
+                       return -(p - buf);
+               p++;
+               c2 = HEX(*p);
+               if (c2 == -1)
+                       return -(p - buf);
+               p++;
+               *bin = (c1 * 16 + c2);
+               bin ++;
+               left --;
+               if (*p == ':')
+                       p++;
+               else if (read) {
+                       *read = (size - left);
+                       break;
+               } else if (left == 0)
+                       break;
+               else
+                       return -(p - buf);
+       }
+
+       while (*p && *p == ' ')
+               p++;
+
+       p--;
+
+       return (p - buf);
+}
+
+static ssize_t emv_pk_read_ymv(char *buf, unsigned *ymv)
+{
+       int i;
+       unsigned char temp[3];
+       char *p = buf;
+
+       *ymv = 0;
+
+       while (*p && *p == ' ')
+               p++;
+
+       for (i = 0; i < 3; i++) {
+               int c1, c2;
+               c1 = BCD(*p);
+               if (c1 == -1)
+                       return -(p - buf);
+               p++;
+               c2 = BCD(*p);
+               if (c2 == -1)
+                       return -(p - buf);
+               p++;
+               temp[i] = (c1 * 16 + c2);
+       }
+
+       while (*p && *p == ' ')
+               p++;
+
+       p--;
+
+       if (temp[1] > 0x12 || temp[2] > 0x31)
+               return -(p - buf);
+
+       *ymv = (temp[0] * 0x10000 + temp[1] * 0x100 + temp[2]);
+
+       return (p - buf);
+}
+
+static ssize_t emv_pk_read_string(char *buf, char *str, size_t size)
+{
+       char *p = buf;
+       while (*p && *p == ' ')
+               p++;
+
+       while (size > 1) {
+               if (*p == ' ')
+                       break;
+               else if (*p < 0x20 || *p >= 0x7f)
+                       return -(p - buf);
+               *str = *p;
+               p++;
+               str ++;
+               size --;
+       }
+
+       *str = 0;
+
+       while (*p && *p == ' ')
+               p++;
+
+       p--;
+
+       return (p - buf);
+}
+
+
+struct emv_pk *emv_pk_parse_pk(char *buf)
+{
+       struct emv_pk *r = calloc(1, sizeof(*r));
+       ssize_t l;
+       char temp[10];
+
+       l = emv_pk_read_bin(buf, r->rid, 5, NULL);
+       if (l <= 0)
+               goto out;
+       buf += l;
+
+       l = emv_pk_read_bin(buf, &r->index, 1, NULL);
+       if (l <= 0)
+               goto out;
+       buf += l;
+
+       l = emv_pk_read_ymv(buf, &r->expire);
+       if (l <= 0)
+               goto out;
+       buf += l;
+
+       l = emv_pk_read_string(buf, temp, sizeof(temp));
+       if (l <= 0)
+               goto out;
+       buf += l;
+
+       if (!strcmp(temp, "rsa"))
+               r->pk_algo = PK_RSA;
+       else
+               goto out;
+
+       l = emv_pk_read_bin(buf, r->exp, sizeof(r->exp), &r->elen);
+       if (l <= 0)
+               goto out;
+       buf += l;
+
+       r->modulus = malloc(2048/8);
+       l = emv_pk_read_bin(buf, r->modulus, 2048/8, &r->mlen);
+       if (l <= 0)
+               goto out2;
+       buf += l;
+
+       l = emv_pk_read_string(buf, temp, sizeof(temp));
+       if (l <= 0)
+               goto out2;
+       buf += l;
+
+       if (!strcmp(temp, "sha1"))
+               r->hash_algo = HASH_SHA_1;
+       else
+               goto out2;
+
+       l = emv_pk_read_bin(buf, r->hash, 20, NULL);
+       if (l <= 0)
+               goto out2;
+
+       return r;
+
+out2:
+       free(r->modulus);
+out:
+       free(r);
+       return NULL;
+}
+
+static size_t emv_pk_write_bin(char *out, size_t outlen, const unsigned char *buf, size_t len)
+{
+       int i;
+       size_t pos = 0;
+
+       if (len == 0)
+               return 0;
+       if (outlen < len * 3)
+               return 0;
+
+       out[pos++] = TOHEX(buf[0] >> 4);
+       out[pos++] = TOHEX(buf[0] & 0xf);
+       for (i = 1; i < len; i++) {
+               out[pos++] = ':';
+               out[pos++] = TOHEX(buf[i] >> 4);
+               out[pos++] = TOHEX(buf[i] & 0xf);
+       }
+       out[pos++] = ' ';
+
+       return pos;
+}
+
+static size_t emv_pk_write_str(char *out, size_t outlen, const char *str)
+{
+       size_t len = strlen(str);
+
+       if (len == 0)
+               return 0;
+       if (outlen < len)
+               return 0;
+
+       memcpy(out, str, len);
+
+       return len;
+}
+
+char *emv_pk_dump_pk(const struct emv_pk *pk)
+{
+       size_t outsize = 1024; /* should be enough */
+       char *out = malloc(outsize); /* should be enough */
+       size_t outpos = 0;
+       size_t rc;
+
+       if (!out)
+               return NULL;
+
+       rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->rid, 5);
+       if (rc == 0)
+               goto err;
+       outpos += rc;
+
+       rc = emv_pk_write_bin(out + outpos, outsize - outpos, &pk->index, 1);
+       if (rc == 0)
+               goto err;
+       outpos += rc;
+
+       if (outpos + 7 > outsize)
+               goto err;
+       out[outpos++] = TOHEX((pk->expire >> 20) & 0xf);
+       out[outpos++] = TOHEX((pk->expire >> 16) & 0xf);
+       out[outpos++] = TOHEX((pk->expire >> 12) & 0xf);
+       out[outpos++] = TOHEX((pk->expire >> 8 ) & 0xf);
+       out[outpos++] = TOHEX((pk->expire >> 4 ) & 0xf);
+       out[outpos++] = TOHEX((pk->expire >> 0 ) & 0xf);
+       out[outpos++] = ' ';
+
+       if (pk->pk_algo == PK_RSA) {
+               rc = emv_pk_write_str(out + outpos, outsize - outpos, "rsa");
+               if (rc == 0)
+                       goto err;
+               outpos += rc;
+               out[outpos++] = ' ';
+       } else {
+               if (outpos + 4 > outsize)
+                       goto err;
+               out[outpos++] = '?';
+               out[outpos++] = '?';
+               out[outpos++] = TOHEX(pk->pk_algo >> 4);
+               out[outpos++] = TOHEX(pk->pk_algo & 0xf);
+       }
+
+       rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->exp, pk->elen);
+       if (rc == 0)
+               goto err;
+       outpos += rc;
+
+       rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->modulus, pk->mlen);
+       if (rc == 0)
+               goto err;
+       outpos += rc;
+
+       if (pk->hash_algo == HASH_SHA_1) {
+               rc = emv_pk_write_str(out + outpos, outsize - outpos, "sha1");
+               if (rc == 0)
+                       goto err;
+               outpos += rc;
+               out[outpos++] = ' ';
+       } else {
+               if (outpos + 4 > outsize)
+                       goto err;
+               out[outpos++] = '?';
+               out[outpos++] = '?';
+               out[outpos++] = TOHEX(pk->pk_algo >> 4);
+               out[outpos++] = TOHEX(pk->pk_algo & 0xf);
+       }
+
+
+       rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->hash, 20);
+       if (rc == 0)
+               goto err;
+       outpos += rc;
+
+       out[outpos-1] = '\0';
+
+       return out;
+
+err:
+       free(out);
+       return NULL;
+}
+
+bool emv_pk_verify(const struct emv_pk *pk)
+{
+       struct crypto_hash *ch = crypto_hash_open(pk->hash_algo);
+       if (!ch)
+               return false;
+
+       crypto_hash_write(ch, pk->rid, sizeof(pk->rid));
+       crypto_hash_write(ch, &pk->index, 1);
+       crypto_hash_write(ch, pk->modulus, pk->mlen);
+       crypto_hash_write(ch, pk->exp, pk->elen);
+
+       unsigned char *h = crypto_hash_read(ch);
+       if (!h) {
+               crypto_hash_close(ch);
+               return false;
+       }
+
+       size_t hsize = crypto_hash_get_size(ch);
+       bool r = hsize && !memcmp(h, pk->hash, hsize) ? true : false;
+
+       crypto_hash_close(ch);
+
+       return r;
+}
+
+struct emv_pk *emv_pk_new(size_t modlen, size_t explen)
+{
+       struct emv_pk *pk;
+
+       /* Not supported ATM */
+       if (explen > 3)
+               return NULL;
+
+       pk = calloc(1, sizeof(*pk));
+       if (!pk)
+               return NULL;
+
+       pk->mlen = modlen;
+       pk->elen = explen;
+
+       pk->modulus = calloc(modlen, 1);
+       if (!pk->modulus) {
+               free(pk);
+               pk = NULL;
+       }
+
+       return pk;
+}
+
+void emv_pk_free(struct emv_pk *pk)
+{
+       if (!pk)
+               return;
+
+       free(pk->modulus);
+       free(pk);
+}
+
+static struct emv_pk *emv_pk_get_ca_pk_from_file(const char *fname,
+               const unsigned char *rid,
+               unsigned char idx)
+{
+       if  (!fname)
+               return NULL;
+
+       FILE *f = fopen(fname, "r");
+       if (!f) {
+               perror("fopen");
+               return NULL;
+       }
+
+       while (!feof(f)) {
+               char buf[2048];
+               if (fgets(buf, sizeof(buf), f) == NULL)
+                       break;
+
+               struct emv_pk *pk = emv_pk_parse_pk(buf);
+               if (!pk)
+                       continue;
+               if (memcmp(pk->rid, rid, 5) || pk->index != idx) {
+                       emv_pk_free(pk);
+                       continue;
+               }
+
+               fclose(f);
+
+               return pk;
+       }
+
+       fclose(f);
+
+       return NULL;
+}
+
+char *emv_pk_get_ca_pk_file(const char *dirname, const unsigned char *rid, unsigned char idx)
+{
+       if (!dirname)
+               dirname = ".";//openemv_config_get_str("capk.dir", NULL);
+
+       if (!dirname)
+               return NULL;
+
+       char *filename;
+       int ret = asprintf(&filename, "%s/%02hhx%02hhx%02hhx%02hhx%02hhx_%02hhx.0",
+                       dirname,
+                       rid[0],
+                       rid[1],
+                       rid[2],
+                       rid[3],
+                       rid[4],
+                       idx);
+
+       if (ret <= 0)
+               return NULL;
+
+       return filename;
+}
+
+char *emv_pk_get_ca_pk_rid_file(const char *dirname, const unsigned char *rid)
+{
+       if (!dirname)
+               dirname = "."; //openemv_config_get_str("capk.dir", NULL);
+
+       if (!dirname)
+               return NULL;
+
+       char *filename;
+       int ret = asprintf(&filename, "%s/%02hhx%02hhx%02hhx%02hhx%02hhx.pks",
+                       dirname,
+                       rid[0],
+                       rid[1],
+                       rid[2],
+                       rid[3],
+                       rid[4]);
+
+       if (ret <= 0)
+               return NULL;
+
+       return filename;
+}
+
+struct emv_pk *emv_pk_get_ca_pk(const unsigned char *rid, unsigned char idx)
+{
+       struct emv_pk *pk = NULL;
+
+/*     if (!pk) {
+               char *fname = emv_pk_get_ca_pk_file(NULL, rid, idx);
+               if (fname) {
+                       pk = emv_pk_get_ca_pk_from_file(fname, rid, idx);
+                       free(fname);
+               }
+       }
+
+       if (!pk) {
+               char *fname = emv_pk_get_ca_pk_rid_file(NULL, rid);
+               if (fname) {
+                       pk = emv_pk_get_ca_pk_from_file(fname, rid, idx);
+                       free(fname);
+               }
+       }
+*/
+       if (!pk) {
+               const char *relfname = "emv/capk.txt"; 
+
+               char fname[strlen(get_my_executable_directory()) + strlen(relfname) + 1];
+               strcpy(fname, get_my_executable_directory());
+               strcat(fname, relfname);
+
+               pk = emv_pk_get_ca_pk_from_file(fname, rid, idx);
+       }
+       if (!pk)
+               return NULL;
+
+       printf("Verifying CA PK for %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx %zd bits...",
+                               pk->rid[0],
+                               pk->rid[1],
+                               pk->rid[2],
+                               pk->rid[3],
+                               pk->rid[4],
+                               pk->index,
+                               pk->mlen * 8);
+       if (emv_pk_verify(pk)) {
+               printf("OK\n");
+
+               return pk;
+       }
+
+       printf("Failed!\n");
+       emv_pk_free(pk);
+
+       return NULL;
+}
diff --git a/client/emv/emv_pk.h b/client/emv/emv_pk.h
new file mode 100644 (file)
index 0000000..e291a06
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * libopenemv - a library to work with EMV family of smart cards
+ * Copyright (C) 2015 Dmitry Eremin-Solenikov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ */
+
+#ifndef EMV_PK_H
+#define EMV_PK_H
+
+#include <stdbool.h>
+#include <stddef.h>
+
+struct emv_pk {
+       unsigned char rid[5];
+       unsigned char index;
+       unsigned char serial[3];
+       unsigned char pan[10];
+       unsigned char hash_algo;
+       unsigned char pk_algo;
+       unsigned char hash[20];
+       unsigned char exp[3];
+       size_t elen;
+       size_t mlen;
+       unsigned char *modulus;
+       unsigned int expire;
+};
+
+#define EXPIRE(yy, mm, dd)     0x ## yy ## mm ## dd
+
+struct emv_pk *emv_pk_parse_pk(char *buf);
+struct emv_pk *emv_pk_new(size_t modlen, size_t explen);
+void emv_pk_free(struct emv_pk *pk);
+char *emv_pk_dump_pk(const struct emv_pk *pk);
+bool emv_pk_verify(const struct emv_pk *pk);
+
+char *emv_pk_get_ca_pk_file(const char *dirname, const unsigned char *rid, unsigned char idx);
+char *emv_pk_get_ca_pk_rid_file(const char *dirname, const unsigned char *rid);
+struct emv_pk *emv_pk_get_ca_pk(const unsigned char *rid, unsigned char idx);
+#endif
diff --git a/client/emv/emv_pki.c b/client/emv/emv_pki.c
new file mode 100644 (file)
index 0000000..7803060
--- /dev/null
@@ -0,0 +1,511 @@
+/*
+ * libopenemv - a library to work with EMV family of smart cards
+ * Copyright (C) 2015 Dmitry Eremin-Solenikov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "emv_pki.h"
+#include "crypto.h"
+#include "dump.h"
+#include "util.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+static const unsigned char empty_tlv_value[] = {};
+static const struct tlv empty_tlv = {.tag = 0x0, .len = 0, .value = empty_tlv_value};
+
+static size_t emv_pki_hash_psn[256] = { 0, 0, 11, 2, 17, 2, };
+
+static unsigned char *emv_pki_decode_message(const struct emv_pk *enc_pk,
+               uint8_t msgtype,
+               size_t *len,
+               const struct tlv *cert_tlv,
+               ... /* A list of tlv pointers, end with NULL */
+               )
+{
+       struct crypto_pk *kcp;
+       unsigned char *data;
+       size_t data_len;
+       va_list vl;
+
+       if (!enc_pk)
+               return NULL;
+
+       if (!cert_tlv) {
+               printf("ERROR: Can't find certificate\n");
+               return NULL;
+       }
+
+       if (cert_tlv->len != enc_pk->mlen) {
+               printf("ERROR: Certificate length (%d) not equal key length (%d)\n", cert_tlv->len, enc_pk->mlen);
+               return NULL;
+       }
+       kcp = crypto_pk_open(enc_pk->pk_algo,
+                       enc_pk->modulus, enc_pk->mlen,
+                       enc_pk->exp, enc_pk->elen);
+       if (!kcp)
+               return NULL;
+
+       data = crypto_pk_encrypt(kcp, cert_tlv->value, cert_tlv->len, &data_len);
+       crypto_pk_close(kcp);
+
+/*     if (true){
+               printf("Recovered data:\n");
+               dump_buffer(data, data_len, stdout, 0);
+       }*/
+       
+       if (data[data_len-1] != 0xbc || data[0] != 0x6a || data[1] != msgtype) {
+               printf("ERROR: Certificate format\n");
+               free(data);
+               return NULL;
+       }
+
+       size_t hash_pos = emv_pki_hash_psn[msgtype];
+       if (hash_pos == 0 || hash_pos > data_len){
+               printf("ERROR: Cant get hash position in the certificate\n");
+               free(data);
+               return NULL;
+       }
+
+       struct crypto_hash *ch;
+       ch = crypto_hash_open(data[hash_pos]);
+       if (!ch) {
+               printf("ERROR: Cant do hash\n");
+               free(data);
+               return NULL;
+       }
+
+       size_t hash_len = crypto_hash_get_size(ch);
+       crypto_hash_write(ch, data + 1, data_len - 2 - hash_len);
+
+       va_start(vl, cert_tlv);
+       while (true) {
+               const struct tlv *add_tlv = va_arg(vl, const struct tlv *);
+               if (!add_tlv)
+                       break;
+
+               crypto_hash_write(ch, add_tlv->value, add_tlv->len);
+       }
+       va_end(vl);
+
+       if (memcmp(data + data_len - 1 - hash_len, crypto_hash_read(ch), hash_len)) {
+               printf("ERROR: Calculated wrong hash\n");
+               printf("decoded:    %s\n",sprint_hex(data + data_len - 1 - hash_len, hash_len));
+               printf("calculated: %s\n",sprint_hex(crypto_hash_read(ch), hash_len));
+               crypto_hash_close(ch);
+               free(data);
+               return NULL;
+       }
+
+       crypto_hash_close(ch);
+
+       *len = data_len - hash_len - 1;
+
+       return data;
+}
+
+static unsigned emv_cn_length(const struct tlv *tlv)
+{
+       int i;
+
+       for (i = 0; i < tlv->len; i++) {
+               unsigned char c = tlv->value[i];
+
+               if (c >> 4 == 0xf)
+                       return 2 * i;
+
+               if ((c & 0xf) == 0xf)
+                       return 2 * i + 1;
+       }
+
+       return 2 * tlv->len;
+}
+
+static unsigned char emv_cn_get(const struct tlv *tlv, unsigned pos)
+{
+       if (pos > tlv->len * 2)
+               return 0xf;
+
+       unsigned char c = tlv->value[pos / 2];
+
+       if (pos % 2)
+               return c & 0xf;
+       else
+               return c >> 4;
+}
+
+static struct emv_pk *emv_pki_decode_key_ex(const struct emv_pk *enc_pk,
+               unsigned char msgtype,
+               const struct tlv *pan_tlv,
+               const struct tlv *cert_tlv,
+               const struct tlv *exp_tlv,
+               const struct tlv *rem_tlv,
+               const struct tlv *add_tlv,
+               bool showData
+               )
+{
+       size_t pan_length;
+       unsigned char *data;
+       size_t data_len;
+       size_t pk_len;
+
+       if (!cert_tlv || !exp_tlv || !pan_tlv)
+               return NULL;
+
+       if (!rem_tlv)
+               rem_tlv = &empty_tlv;
+
+       if (msgtype == 2)
+               pan_length = 4;
+       else if (msgtype == 4)
+               pan_length = 10;
+       else {
+               printf("ERROR: Message type must be 2 or 4\n");
+               return NULL;
+       }
+
+       data = emv_pki_decode_message(enc_pk, msgtype, &data_len,
+                       cert_tlv,
+                       rem_tlv,
+                       exp_tlv,
+                       add_tlv,
+                       NULL);
+       if (!data || data_len < 11 + pan_length) {
+               printf("ERROR: Can't decode message\n");
+               return NULL;
+       }
+
+       if (showData){ 
+               printf("Recovered data:\n");
+               dump_buffer(data, data_len, stdout, 0);
+       }
+
+       /* Perform the rest of checks here */
+
+       struct tlv pan2_tlv = {
+               .tag = 0x5a,
+               .len = pan_length,
+               .value = &data[2],
+       };
+       unsigned pan_len = emv_cn_length(pan_tlv);
+       unsigned pan2_len = emv_cn_length(&pan2_tlv);
+
+       if (((msgtype == 2) && (pan2_len < 4 || pan2_len > pan_len)) ||
+           ((msgtype == 4) && (pan2_len != pan_len))) {
+               printf("ERROR: Invalid PAN lengths\n");
+               free(data);
+
+               return NULL;
+       }
+
+       unsigned i;
+       for (i = 0; i < pan2_len; i++)
+               if (emv_cn_get(pan_tlv, i) != emv_cn_get(&pan2_tlv, i)) {
+                       printf("ERROR: PAN data mismatch\n");
+                       printf("tlv  pan=%s\n", sprint_hex(pan_tlv->value, pan_tlv->len));
+                       printf("cert pan=%s\n", sprint_hex(pan2_tlv.value, pan2_tlv.len));
+                       free(data);
+
+                       return NULL;
+               }
+
+       pk_len = data[9 + pan_length];
+       if (pk_len > data_len - 11 - pan_length + rem_tlv->len) {
+               printf("ERROR: Invalid pk length\n");
+               free(data);
+               return NULL;
+       }
+
+       if (exp_tlv->len != data[10 + pan_length]) {
+               free(data);
+               return NULL;
+       }
+
+       struct emv_pk *pk = emv_pk_new(pk_len, exp_tlv->len);
+
+       memcpy(pk->rid, enc_pk->rid, 5);
+       pk->index = enc_pk->index;
+
+       pk->hash_algo = data[7 + pan_length];
+       pk->pk_algo = data[8 + pan_length];
+       pk->expire = (data[3 + pan_length] << 16) | (data[2 + pan_length] << 8) | 0x31;
+       memcpy(pk->serial, data + 4 + pan_length, 3);
+       memcpy(pk->pan, data + 2, pan_length);
+       memset(pk->pan + pan_length, 0xff, 10 - pan_length);
+
+       memcpy(pk->modulus, data + 11 + pan_length,
+                       pk_len < data_len - (11 + pan_length) ?
+                       pk_len :
+                       data_len - (11 + pan_length));
+       memcpy(pk->modulus + data_len - (11 + pan_length), rem_tlv->value, rem_tlv->len);
+       memcpy(pk->exp, exp_tlv->value, exp_tlv->len);
+
+       free(data);
+
+       return pk;
+}
+
+static struct emv_pk *emv_pki_decode_key(const struct emv_pk *enc_pk,
+               unsigned char msgtype,
+               const struct tlv *pan_tlv,
+               const struct tlv *cert_tlv,
+               const struct tlv *exp_tlv,
+               const struct tlv *rem_tlv,
+               const struct tlv *add_tlv
+               ) {
+       return emv_pki_decode_key_ex(enc_pk, msgtype, pan_tlv, cert_tlv, exp_tlv, rem_tlv, add_tlv, false);
+}
+
+struct emv_pk *emv_pki_recover_issuer_cert(const struct emv_pk *pk, struct tlvdb *db)
+{
+       return emv_pki_decode_key(pk, 2,
+                       tlvdb_get(db, 0x5a, NULL),
+                       tlvdb_get(db, 0x90, NULL),
+                       tlvdb_get(db, 0x9f32, NULL),
+                       tlvdb_get(db, 0x92, NULL),
+                       NULL);
+}
+
+struct emv_pk *emv_pki_recover_icc_cert(const struct emv_pk *pk, struct tlvdb *db, const struct tlv *sda_tlv)
+{
+       return emv_pki_decode_key(pk, 4,
+                       tlvdb_get(db, 0x5a, NULL),
+                       tlvdb_get(db, 0x9f46, NULL),
+                       tlvdb_get(db, 0x9f47, NULL),
+                       tlvdb_get(db, 0x9f48, NULL),
+                       sda_tlv);
+}
+
+struct emv_pk *emv_pki_recover_icc_pe_cert(const struct emv_pk *pk, struct tlvdb *db)
+{
+       return emv_pki_decode_key(pk, 4,
+                       tlvdb_get(db, 0x5a, NULL),
+                       tlvdb_get(db, 0x9f2d, NULL),
+                       tlvdb_get(db, 0x9f2e, NULL),
+                       tlvdb_get(db, 0x9f2f, NULL),
+                       NULL);
+}
+
+struct tlvdb *emv_pki_recover_dac_ex(const struct emv_pk *enc_pk, const struct tlvdb *db, const struct tlv *sda_tlv, bool showData)
+{
+       size_t data_len;
+       unsigned char *data = emv_pki_decode_message(enc_pk, 3, &data_len,
+                       tlvdb_get(db, 0x93, NULL),
+                       sda_tlv,
+                       NULL);
+
+       if (!data || data_len < 5)
+               return NULL;
+
+       if (showData){
+               printf("Recovered data:\n");
+               dump_buffer(data, data_len, stdout, 0);
+       }
+
+       struct tlvdb *dac_db = tlvdb_fixed(0x9f45, 2, data+3);
+
+       free(data);
+
+       return dac_db;
+}
+struct tlvdb *emv_pki_recover_dac(const struct emv_pk *enc_pk, const struct tlvdb *db, const struct tlv *sda_tlv) {
+       return emv_pki_recover_dac_ex(enc_pk, db, sda_tlv, false);
+}
+
+struct tlvdb *emv_pki_recover_idn(const struct emv_pk *enc_pk, const struct tlvdb *db, const struct tlv *dyn_tlv) {
+       return emv_pki_recover_idn_ex(enc_pk, db, dyn_tlv, false);
+}
+
+struct tlvdb *emv_pki_recover_idn_ex(const struct emv_pk *enc_pk, const struct tlvdb *db, const struct tlv *dyn_tlv, bool showData)
+{
+       size_t data_len;
+       unsigned char *data = emv_pki_decode_message(enc_pk, 5, &data_len,
+                       tlvdb_get(db, 0x9f4b, NULL),
+                       dyn_tlv,
+                       NULL);
+
+       if (!data || data_len < 3)
+               return NULL;
+
+       if (data[3] < 2 || data[3] > data_len - 3) {
+               free(data);
+               return NULL;
+       }
+
+       if (showData){
+               printf("Recovered data:\n");
+               dump_buffer(data, data_len, stdout, 0);
+       }
+
+       size_t idn_len = data[4];
+       if (idn_len > data[3] - 1) {
+               free(data);
+               return NULL;
+       }
+
+       // 9f4c ICC Dynamic Number
+       struct tlvdb *idn_db = tlvdb_fixed(0x9f4c, idn_len, data + 5);
+       
+       free(data);
+
+       return idn_db;
+}
+
+struct tlvdb *emv_pki_recover_atc_ex(const struct emv_pk *enc_pk, const struct tlvdb *db, bool showData)
+{
+       size_t data_len;
+       unsigned char *data = emv_pki_decode_message(enc_pk, 5, &data_len,
+                       tlvdb_get(db, 0x9f4b, NULL),
+                       tlvdb_get(db, 0x9f37, NULL),
+                       tlvdb_get(db, 0x9f02, NULL),
+                       tlvdb_get(db, 0x5f2a, NULL),
+                       tlvdb_get(db, 0x9f69, NULL),
+                       NULL);
+
+       if (!data || data_len < 3)
+               return NULL;
+
+       if (data[3] < 2 || data[3] > data_len - 3) {
+               free(data);
+               return NULL;
+       }
+
+       if (showData){
+               printf("Recovered data:\n");
+               dump_buffer(data, data_len, stdout, 0);
+       }
+
+       size_t idn_len = data[4];
+       if (idn_len > data[3] - 1) {
+               free(data);
+               return NULL;
+       }
+
+       // 9f36 Application Transaction Counter (ATC)
+       struct tlvdb *atc_db = tlvdb_fixed(0x9f36, idn_len, data + 5);
+       
+       free(data);
+
+       return atc_db;
+}
+
+static bool tlv_hash(void *data, const struct tlv *tlv, int level, bool is_leaf)
+{
+       struct crypto_hash *ch = data;
+       size_t tag_len;
+       unsigned char *tag;
+
+       if (tlv_is_constructed(tlv))
+               return true;
+
+       if (tlv->tag == 0x9f4b)
+               return true;
+
+       tag = tlv_encode(tlv, &tag_len);
+       crypto_hash_write(ch, tag, tag_len);
+       free(tag);
+
+       return true;
+}
+
+struct tlvdb *emv_pki_perform_cda(const struct emv_pk *enc_pk, const struct tlvdb *db,
+               const struct tlvdb *this_db,
+               const struct tlv *pdol_data_tlv,
+               const struct tlv *crm1_tlv,
+               const struct tlv *crm2_tlv)
+{
+       return emv_pki_perform_cda_ex(enc_pk, db, this_db, pdol_data_tlv, crm1_tlv, crm2_tlv, false);
+}
+struct tlvdb *emv_pki_perform_cda_ex(const struct emv_pk *enc_pk, const struct tlvdb *db,
+               const struct tlvdb *this_db,     // AC TLV result
+               const struct tlv *pdol_data_tlv, // PDOL
+               const struct tlv *crm1_tlv,      // CDOL1
+               const struct tlv *crm2_tlv,      // CDOL2
+               bool showData)
+{
+       const struct tlv *un_tlv = tlvdb_get(db, 0x9f37, NULL);
+       const struct tlv *cid_tlv = tlvdb_get(this_db, 0x9f27, NULL);
+
+       if (!un_tlv || !cid_tlv)
+               return NULL;
+
+       size_t data_len = 0;
+       unsigned char *data = emv_pki_decode_message(enc_pk, 5, &data_len,
+                       tlvdb_get(this_db, 0x9f4b, NULL),
+                       un_tlv,
+                       NULL);
+       if (!data || data_len < 3) {
+               printf("ERROR: can't decode message. len %d\n", data_len);
+               return NULL;
+       }
+
+       if (showData){
+               printf("Recovered data:\n");
+               dump_buffer(data, data_len, stdout, 0);
+       }
+
+       if (data[3] < 30 || data[3] > data_len - 4) {
+               printf("ERROR: Invalid data length\n");
+               free(data);
+               return NULL;
+       }
+
+       if (!cid_tlv || cid_tlv->len != 1 || cid_tlv->value[0] != data[5 + data[4]]) {
+               printf("ERROR: CID mismatch\n");
+               free(data);
+               return NULL;
+       }
+
+       struct crypto_hash *ch;
+       ch = crypto_hash_open(enc_pk->hash_algo);
+       if (!ch) {
+               printf("ERROR: can't create hash\n");
+               free(data);
+               return NULL;
+       }
+
+       if (pdol_data_tlv)
+               crypto_hash_write(ch, pdol_data_tlv->value, pdol_data_tlv->len);
+       if (crm1_tlv)
+               crypto_hash_write(ch, crm1_tlv->value, crm1_tlv->len);
+       if (crm2_tlv)
+               crypto_hash_write(ch, crm2_tlv->value, crm2_tlv->len);
+
+       tlvdb_visit(this_db, tlv_hash, ch, 0);
+
+       if (memcmp(data + 5 + data[4] + 1 + 8, crypto_hash_read(ch), 20)) {
+               printf("ERROR: calculated hash error\n");
+               crypto_hash_close(ch);
+               free(data);
+               return NULL;
+       }
+       crypto_hash_close(ch);
+
+       size_t idn_len = data[4];
+       if (idn_len > data[3] - 1) {
+               printf("ERROR: Invalid IDN length\n");
+               free(data);
+               return NULL;
+       }
+
+       struct tlvdb *idn_db = tlvdb_fixed(0x9f4c, idn_len, data + 5);
+       free(data);
+
+       return idn_db;
+}
diff --git a/client/emv/emv_pki.h b/client/emv/emv_pki.h
new file mode 100644 (file)
index 0000000..e37e3c7
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * libopenemv - a library to work with EMV family of smart cards
+ * Copyright (C) 2015 Dmitry Eremin-Solenikov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ */
+
+#ifndef EMV_PKI_H
+#define EMV_PKI_H
+
+#include "emv_pk.h"
+#include "tlv.h"
+
+#include <stddef.h>
+
+struct emv_pk *emv_pki_recover_issuer_cert(const struct emv_pk *pk, struct tlvdb *db);
+struct emv_pk *emv_pki_recover_icc_cert(const struct emv_pk *pk, struct tlvdb *db, const struct tlv *sda_tlv);
+struct emv_pk *emv_pki_recover_icc_pe_cert(const struct emv_pk *pk, struct tlvdb *db);
+
+struct tlvdb *emv_pki_recover_dac(const struct emv_pk *pk, const struct tlvdb *db, const struct tlv *sda_tlv);
+struct tlvdb *emv_pki_recover_dac_ex(const struct emv_pk *pk, const struct tlvdb *db, const struct tlv *sda_tlv, bool showData);
+struct tlvdb *emv_pki_recover_idn(const struct emv_pk *pk, const struct tlvdb *db, const struct tlv *dyn_tlv);
+struct tlvdb *emv_pki_recover_idn_ex(const struct emv_pk *pk, const struct tlvdb *db, const struct tlv *dyn_tlv, bool showData);
+struct tlvdb *emv_pki_recover_atc_ex(const struct emv_pk *enc_pk, const struct tlvdb *db, bool showData);
+struct tlvdb *emv_pki_perform_cda(const struct emv_pk *enc_pk, const struct tlvdb *db,
+               const struct tlvdb *this_db,
+               const struct tlv *pdol_data_tlv,
+               const struct tlv *crm1_tlv,
+               const struct tlv *crm2_tlv);
+struct tlvdb *emv_pki_perform_cda_ex(const struct emv_pk *enc_pk, const struct tlvdb *db,
+               const struct tlvdb *this_db,
+               const struct tlv *pdol_data_tlv,
+               const struct tlv *crm1_tlv,
+               const struct tlv *crm2_tlv,
+               bool showData);
+
+#endif
diff --git a/client/emv/emv_pki_priv.c b/client/emv/emv_pki_priv.c
new file mode 100644 (file)
index 0000000..d67a870
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ * libopenemv - a library to work with EMV family of smart cards
+ * Copyright (C) 2015 Dmitry Eremin-Solenikov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "emv_pki_priv.h"
+#include "crypto.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+struct emv_pk *emv_pki_make_ca(const struct crypto_pk *cp,
+               const unsigned char *rid, unsigned char index,
+               unsigned int expire, enum crypto_algo_hash hash_algo)
+{
+       size_t modlen, explen;
+       unsigned char *mod, *exp;
+
+       if (!rid)
+               return NULL;
+
+       mod = crypto_pk_get_parameter(cp, 0, &modlen);
+       exp = crypto_pk_get_parameter(cp, 1, &explen);
+
+       if (!mod || !modlen || !exp || !explen) {
+               free(mod);
+               free(exp);
+
+               return NULL;
+       }
+
+       struct emv_pk *pk = emv_pk_new(modlen, explen);
+       memcpy(pk->rid, rid, 5);
+       pk->index = index;
+       pk->expire = expire;
+       pk->pk_algo = crypto_pk_get_algo(cp);
+       pk->hash_algo = hash_algo;
+       memcpy(pk->modulus, mod, modlen);
+       memcpy(pk->exp, exp, explen);
+
+       free(mod);
+       free(exp);
+
+       struct crypto_hash *ch = crypto_hash_open(pk->hash_algo);
+       if (!ch) {
+               emv_pk_free(pk);
+               return false;
+       }
+
+       crypto_hash_write(ch, pk->rid, sizeof(pk->rid));
+       crypto_hash_write(ch, &pk->index, 1);
+       crypto_hash_write(ch, pk->modulus, pk->mlen);
+       crypto_hash_write(ch, pk->exp, pk->elen);
+
+       unsigned char *h = crypto_hash_read(ch);
+       if (!h) {
+               crypto_hash_close(ch);
+               emv_pk_free(pk);
+
+               return NULL;
+       }
+
+       memcpy(pk->hash, h, crypto_hash_get_size(ch));
+       crypto_hash_close(ch);
+
+       return pk;
+}
+
+static struct tlvdb *emv_pki_sign_message(const struct crypto_pk *cp,
+               tlv_tag_t cert_tag, tlv_tag_t rem_tag,
+               const unsigned char *msg, size_t msg_len,
+               ... /* A list of tlv pointers, end with NULL */
+               )
+{
+       size_t tmp_len = (crypto_pk_get_nbits(cp) + 7) / 8;
+       unsigned char *tmp = malloc(tmp_len);
+       if (!tmp)
+               return NULL;
+
+       // XXX
+       struct crypto_hash *ch = crypto_hash_open(HASH_SHA_1);
+       if (!ch) {
+               free(tmp);
+
+               return NULL;
+       }
+
+       tmp[0] = 0x6a;
+       tmp[tmp_len - 1] = 0xbc;
+
+       const unsigned char *rem;
+       size_t rem_len;
+       size_t hash_len = crypto_hash_get_size(ch);
+       size_t part_len = tmp_len - 2 - hash_len;
+       if (part_len < msg_len) {
+               memcpy(tmp + 1, msg, part_len);
+               rem = msg + part_len;
+               rem_len = msg_len - part_len;
+       } else {
+               memcpy(tmp + 1, msg, msg_len);
+               memset(tmp + 1 + msg_len, 0xbb, part_len - msg_len);
+               rem = NULL;
+               rem_len = 0;
+       }
+       crypto_hash_write(ch, tmp + 1, part_len);
+       crypto_hash_write(ch, rem, rem_len);
+
+       va_list vl;
+       va_start(vl, msg_len);
+       while (true) {
+               const struct tlv *add_tlv = va_arg(vl, const struct tlv *);
+               if (!add_tlv)
+                       break;
+
+               crypto_hash_write(ch, add_tlv->value, add_tlv->len);
+       }
+       va_end(vl);
+
+       unsigned char *h = crypto_hash_read(ch);
+       if (!h) {
+               crypto_hash_close(ch);
+               free(tmp);
+
+               return NULL;
+       }
+
+       memcpy(tmp + 1 + part_len, h, hash_len);
+       crypto_hash_close(ch);
+
+       size_t cert_len;
+       unsigned char *cert = crypto_pk_decrypt(cp, tmp, tmp_len, &cert_len);
+       free(tmp);
+
+       if (!cert)
+               return NULL;
+
+       struct tlvdb *db = tlvdb_fixed(cert_tag, cert_len, cert);
+       free(cert);
+       if (!db)
+               return NULL;
+
+       if (rem) {
+               struct tlvdb *rdb = tlvdb_fixed(rem_tag, rem_len, rem);
+               if (!rdb) {
+                       tlvdb_free(db);
+
+                       return NULL;
+               }
+               tlvdb_add(db, rdb);
+       }
+
+       return db;
+}
+
+static struct tlvdb *emv_pki_sign_key(const struct crypto_pk *cp,
+               struct emv_pk *ipk,
+               unsigned char msgtype,
+               size_t pan_len,
+               tlv_tag_t cert_tag,
+               tlv_tag_t exp_tag,
+               tlv_tag_t rem_tag,
+               const struct tlv *add_tlv
+               )
+{
+       unsigned pos = 0;
+       unsigned char *msg = malloc(1 + pan_len + 2 + 3 + 1 + 1 + 1 + 1 + ipk->mlen);
+
+       if (!msg)
+               return NULL;
+
+       msg[pos++] = msgtype;
+       memcpy(msg + pos, ipk->pan, pan_len); pos += pan_len;
+       msg[pos++] = (ipk->expire >> 8) & 0xff;
+       msg[pos++] = (ipk->expire >> 16) & 0xff;
+       memcpy(msg + pos, ipk->serial, 3); pos += 3;
+       msg[pos++] = ipk->hash_algo;
+       msg[pos++] = ipk->pk_algo;
+       msg[pos++] = ipk->mlen;
+       msg[pos++] = ipk->elen;
+       memcpy(msg + pos, ipk->modulus, ipk->mlen);
+       pos += ipk->mlen;
+
+       struct tlvdb *exp_db = tlvdb_fixed(exp_tag, ipk->elen, ipk->exp);
+       if (!exp_db) {
+               free(msg);
+
+               return NULL;
+       }
+
+       struct tlvdb *db = emv_pki_sign_message(cp,
+                       cert_tag, rem_tag,
+                       msg, pos,
+                       tlvdb_get(exp_db, exp_tag, NULL),
+                       add_tlv,
+                       NULL);
+       free(msg);
+       if (!db)
+               return NULL;
+
+       tlvdb_add(db, exp_db);
+
+       return db;
+}
+
+struct tlvdb *emv_pki_sign_issuer_cert(const struct crypto_pk *cp, struct emv_pk *issuer_pk)
+{
+       return emv_pki_sign_key(cp, issuer_pk, 2, 4, 0x90, 0x9f32, 0x92, NULL);
+}
+
+struct tlvdb *emv_pki_sign_icc_cert(const struct crypto_pk *cp, struct emv_pk *icc_pk, const struct tlv *sda_tlv)
+{
+       return emv_pki_sign_key(cp, icc_pk, 4, 10, 0x9f46, 0x9f47, 0x9f48, sda_tlv);
+}
+
+struct tlvdb *emv_pki_sign_icc_pe_cert(const struct crypto_pk *cp, struct emv_pk *icc_pe_pk)
+{
+       return emv_pki_sign_key(cp, icc_pe_pk, 4, 10, 0x9f2d, 0x9f2e, 0x9f2f, NULL);
+}
+
+struct tlvdb *emv_pki_sign_dac(const struct crypto_pk *cp, const struct tlv *dac_tlv, const struct tlv *sda_tlv)
+{
+       unsigned pos = 0;
+       unsigned char *msg = malloc(1+1+dac_tlv->len);
+
+       if (!msg)
+               return NULL;
+
+       msg[pos++] = 3;
+       msg[pos++] = HASH_SHA_1;
+       memcpy(msg+pos, dac_tlv->value, dac_tlv->len);
+       pos += dac_tlv->len;
+
+       struct tlvdb *db = emv_pki_sign_message(cp,
+                       0x93, 0,
+                       msg, pos,
+                       sda_tlv,
+                       NULL);
+
+       free(msg);
+
+       return db;
+}
+
+struct tlvdb *emv_pki_sign_idn(const struct crypto_pk *cp, const struct tlv *idn_tlv, const struct tlv *dyn_tlv)
+{
+       unsigned pos = 0;
+       unsigned char *msg = malloc(1+1+1+1+idn_tlv->len);
+
+       if (!msg)
+               return NULL;
+
+       msg[pos++] = 5;
+       msg[pos++] = HASH_SHA_1;
+       msg[pos++] = idn_tlv->len + 1;
+       msg[pos++] = idn_tlv->len;
+       memcpy(msg+pos, idn_tlv->value, idn_tlv->len);
+       pos += idn_tlv->len;
+
+       struct tlvdb *db = emv_pki_sign_message(cp,
+                       0x9f4b, 0,
+                       msg, pos,
+                       dyn_tlv,
+                       NULL);
+
+       free(msg);
+
+       return db;
+}
diff --git a/client/emv/emv_pki_priv.h b/client/emv/emv_pki_priv.h
new file mode 100644 (file)
index 0000000..4f1639d
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * libopenemv - a library to work with EMV family of smart cards
+ * Copyright (C) 2015 Dmitry Eremin-Solenikov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ */
+
+#ifndef EMV_PKI_PRIV_H
+#define EMV_PKI_PRIV_H
+
+#include "crypto.h"
+#include "emv_pk.h"
+#include "tlv.h"
+
+#include <stddef.h>
+
+struct emv_pk *emv_pki_make_ca(const struct crypto_pk *cp,
+               const unsigned char *rid, unsigned char index,
+               unsigned int expire, enum crypto_algo_hash hash_algo);
+struct tlvdb *emv_pki_sign_issuer_cert(const struct crypto_pk *cp, struct emv_pk *issuer_pk);
+struct tlvdb *emv_pki_sign_icc_cert(const struct crypto_pk *cp, struct emv_pk *icc_pk, const struct tlv *sda_tlv);
+struct tlvdb *emv_pki_sign_icc_pe_cert(const struct crypto_pk *cp, struct emv_pk *icc_pe_pk);
+
+struct tlvdb *emv_pki_sign_dac(const struct crypto_pk *cp, const struct tlv *dac_tlv, const struct tlv *sda_tlv);
+struct tlvdb *emv_pki_sign_idn(const struct crypto_pk *cp, const struct tlv *idn_tlv, const struct tlv *dyn_tlv);
+
+#endif
index a7c11c3e768d3d98a35d23f04063882a3f8400b4..cdd57cb519486a7fba431794dc449e9b9a298e28 100644 (file)
@@ -21,6 +21,7 @@
 #include "emv_tags.h"
 
 #include <stdlib.h>
+#include <string.h>
 
 #define PRINT_INDENT(level)    {for (int i = 0; i < (level); i++) fprintf(f, "\t");}
 
@@ -33,6 +34,8 @@ enum emv_tag_t {
        EMV_TAG_STRING,
        EMV_TAG_NUMERIC,
        EMV_TAG_YYMMDD,
+       EMV_TAG_CVR,
+       EMV_TAG_CID,
 };
 
 struct emv_tag {
@@ -57,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" },
@@ -144,12 +147,39 @@ static const struct emv_tag_bit EMV_TTQ[] = {
        EMV_BIT_FINISH,
 };
 
+static const struct emv_tag_bit EMV_CVR[] = {
+       // mask 0F 0F F0 0F
+       { EMV_BIT(1, 4), "CDA Performed" },
+       { EMV_BIT(1, 3), "Offline DDA Performed" },
+       { EMV_BIT(1, 2), "Issuer Authentication Not Performed" },
+       { EMV_BIT(1, 1), "Issuer Authentication performed and Failed" },
+       { EMV_BIT(2, 4), "Offline PIN Verification Performed" },
+       { EMV_BIT(2, 3), "Offline PIN Verification Performed and PIN Not Successfully Verified" },
+       { EMV_BIT(2, 2), "PIN Try Limit Exceeded" },
+       { EMV_BIT(2, 1), "Last Online Transaction Not Completed" },
+       { EMV_BIT(3, 8), "Lower Offline Transaction Count Limit Exceeded" },
+       { EMV_BIT(3, 7), "Upper Offline Transaction Count Limit Exceeded" },
+       { EMV_BIT(3, 6), "Lower Cumulative Offline Amount Limit Exceeded" },
+       { EMV_BIT(3, 5), "Upper Cumulative Offline Amount Limit Exceeded" },
+       { EMV_BIT(4, 4), "Issuer script processing failed on last transaction" },
+       { EMV_BIT(4, 3), "Offline data authentication failed on previous transaction and transaction declined offline" },
+       { EMV_BIT(4, 2), "Go Online on Next Transaction Was Set" },
+       { EMV_BIT(4, 1), "Unable to go Online" },
+       EMV_BIT_FINISH,
+};
+
 // All Data Elements by Tags used in TLV structure (according to the EMV 4.2 Standard )
 // https://www.eftlab.co.uk/index.php/site-map/knowledge-base/145-emv-nfc-tags
 // http://dexterous-programmer.blogspot.in/2012/05/emv-tags.html
 static const struct emv_tag emv_tags[] = {
+       // internal
        { 0x00  , "Unknown ???" },
        { 0x01  , "", EMV_TAG_STRING }, // string for headers
+       { 0x02  , "Raw data", }, // data
+       { 0x20  , "Cardholder Verification Results (CVR)", EMV_TAG_CVR }, // not standard!
+       { 0x21  , "Input list for Offline Data Authentication" }, // not standard! data for "Offline Data Authentication" come from "read records" command. (EMV book3 10.3)
+
+       // EMV
        { 0x41  , "Country code and national data" },
        { 0x42  , "Issuer Identification Number (IIN)" },
        { 0x4f  , "Application Dedicated File (ADF) Name" },
@@ -193,6 +223,7 @@ static const struct emv_tag emv_tags[] = {
        { 0x9f06, "Application Identifier (AID), Terminal. ISO 7816-5" },
        { 0x9f07, "Application Usage Control", EMV_TAG_BITMASK, &EMV_AUC },
        { 0x9f08, "Application Version Number" },
+       { 0x9f0a, "Application Selection Registered Proprietary Data" }, // https://blog.ul-ts.com/posts/electronic-card-identifier-one-more-step-for-mif-compliance/
        { 0x9f0d, "Issuer Action Code - Default", EMV_TAG_BITMASK, &EMV_TVR },
        { 0x9f0e, "Issuer Action Code - Denial", EMV_TAG_BITMASK, &EMV_TVR },
        { 0x9f0f, "Issuer Action Code - Online", EMV_TAG_BITMASK, &EMV_TVR },
@@ -205,7 +236,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" },
@@ -228,18 +259,22 @@ static const struct emv_tag emv_tags[] = {
        { 0x9f4c, "ICC Dynamic Number" },
        { 0x9f4d, "Log Entry" },
        { 0x9f4f, "Log Format", EMV_TAG_DOL },
+       { 0x9f60, "CVC3 (Track1)" },
+       { 0x9f61, "CVC3 (Track2)" },
        { 0x9f62, "PCVC3(Track1)" },
        { 0x9f63, "PUNATC(Track1)" },
        { 0x9f64, "NATC(Track1)" },
        { 0x9f65, "PCVC3(Track2)" },
        { 0x9f66, "PUNATC(Track2) / Terminal Transaction Qualifiers (TTQ)", EMV_TAG_BITMASK, &EMV_TTQ },
        { 0x9f67, "NATC(Track2) / MSD Offset" },
+       { 0x9f68, "Cardholder verification method list (PayPass)" },
        { 0x9f69, "Card Authentication Related Data" },
        { 0x9f6a, "Unpredictable Number", EMV_TAG_NUMERIC },
        { 0x9f6b, "Track 2 Data" },
        { 0x9f6c, "Card Transaction Qualifiers (CTQ)", EMV_TAG_BITMASK, &EMV_CTQ },
        { 0xa5  , "File Control Information (FCI) Proprietary Template" },
        { 0xbf0c, "File Control Information (FCI) Issuer Discretionary Data" },
+       { 0xdf20, "Issuer Proprietary Bitmap (IPB)" },
 };
 
 static int emv_sort_tag(tlv_tag_t tag)
@@ -375,6 +410,116 @@ static uint32_t emv_get_binary(const unsigned char *S)
        return (S[0] << 24) | (S[1] << 16) | (S[2] << 8) | (S[3] << 0);
 }
 
+// https://github.com/binaryfoo/emv-bertlv/blob/master/src/main/resources/fields/visa-cvr.txt
+static void emv_tag_dump_cvr(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;
+       }
+       
+       if (tlv->len != tlv->value[0] + 1) {
+               PRINT_INDENT(level);
+               fprintf(f, "\tINVALID length!\n");
+               return;
+       }
+       
+       if (tlv->len >= 2) {
+               // AC1
+               PRINT_INDENT(level);
+               if ((tlv->value[1] & 0xC0) == 0x00)     fprintf(f, "\tAC1: AAC (Transaction declined)\n");
+               if ((tlv->value[1] & 0xC0) == 0x40)     fprintf(f, "\tAC1: TC (Transaction approved)\n");
+               if ((tlv->value[1] & 0xC0) == 0x80)     fprintf(f, "\tAC1: ARQC (Online authorisation requested)\n");
+               if ((tlv->value[1] & 0xC0) == 0xC0)     fprintf(f, "\tAC1: RFU\n");
+               // AC2
+               PRINT_INDENT(level);
+               if ((tlv->value[1] & 0x30) == 0x00)     fprintf(f, "\tAC2: AAC (Transaction declined)\n");
+               if ((tlv->value[1] & 0x30) == 0x10)     fprintf(f, "\tAC2: TC (Transaction approved)\n");
+               if ((tlv->value[1] & 0x30) == 0x20)     fprintf(f, "\tAC2: not requested (ARQC)\n");
+               if ((tlv->value[1] & 0x30) == 0x30)     fprintf(f, "\tAC2: RFU\n");
+       }
+       if (tlv->len >= 3 && (tlv->value[2] >> 4)) {
+               PRINT_INDENT(level);
+               fprintf(f, "\tPIN try: %x\n", tlv->value[2] >> 4);
+       }
+       if (tlv->len >= 4 && (tlv->value[3] & 0x0F)) {
+               PRINT_INDENT(level);
+               fprintf(f, "\tIssuer discretionary bits: %x\n", tlv->value[3] & 0x0F);
+       }
+       if (tlv->len >= 5 && (tlv->value[4] >> 4)) {
+               PRINT_INDENT(level);
+               fprintf(f, "\tSuccessfully processed issuer script commands: %x\n", tlv->value[4] >> 4);
+       }
+       
+       // mask 0F 0F F0 0F
+       uint8_t data[20] = {0};
+       memcpy(data, &tlv->value[1], tlv->len - 1);
+       data[0] &= 0x0F;
+       data[1] &= 0x0F;
+       data[2] &= 0xF0;
+       data[3] &= 0x0F;
+       const struct tlv bit_tlv = {
+               .tag = tlv->tag,
+               .len = tlv->len - 1,
+               .value = data,
+       };
+       const struct emv_tag bit_tag = {
+               .tag = tag->tag,
+               .name = tag->name,
+               .type = EMV_TAG_BITMASK,
+               .data = EMV_CVR,
+       };
+       
+       if (data[0] || data[1] || data[2] || data[3])
+               emv_tag_dump_bitmask(&bit_tlv, &bit_tag, f, level);
+       
+       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] & EMVAC_AC_MASK) == EMVAC_AAC)               fprintf(f, "\tAC1: AAC (Transaction declined)\n");
+       if ((tlv->value[0] & EMVAC_AC_MASK) == EMVAC_TC)                fprintf(f, "\tAC1: TC (Transaction approved)\n");
+       if ((tlv->value[0] & EMVAC_AC_MASK) == EMVAC_ARQC)              fprintf(f, "\tAC1: ARQC (Online authorisation requested)\n");
+       if ((tlv->value[0] & EMVAC_AC_MASK) == EMVAC_AC_MASK)   fprintf(f, "\tAC1: RFU\n");
+
+       if (tlv->value[0] & EMVCID_ADVICE) {
+               PRINT_INDENT(level);
+               fprintf(f, "\tAdvice required!\n");
+       }
+
+       if (tlv->value[0] & EMVCID_REASON_MASK) {
+               PRINT_INDENT(level);
+               fprintf(f, "\tReason/advice/referral code: ");
+               switch((tlv->value[0] & EMVCID_REASON_MASK)) {
+                       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] & EMVCID_REASON_MASK));
+                               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;
@@ -528,6 +673,14 @@ bool emv_tag_dump(const struct tlv *tlv, FILE *f, int level)
        case EMV_TAG_YYMMDD:
                emv_tag_dump_yymmdd(tlv, tag, f, level);
                break;
+       case EMV_TAG_CVR:
+               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 8dbd9e00a92cb29e1266260dde1ca8835f8466a6..f6d44f0150c989c5b71b1650a026997b5df0fa4b 100644 (file)
 #include "tlv.h"
 #include <stdio.h>
 
+// AC
+# define EMVAC_AC_MASK   0xC0
+# define EMVAC_AAC       0x00
+# define EMVAC_TC        0x40
+# define EMVAC_ARQC      0x80
+# define EMVAC_CDAREQ    0x10
+
+// CID
+# define EMVCID_ADVICE       0x08
+# define EMVCID_REASON_MASK  0x07
+
 bool emv_tag_dump(const struct tlv *tlv, FILE *f, int level);
 
 #endif
index 5c737b056a0543d6a05b95eb01ade1caa6c60319..08c6e3dbb53f5d57421897a35475ea9e0bb2a60f 100644 (file)
 
 // Got from here. Thanks)
 // https://eftlab.co.uk/index.php/site-map/knowledge-base/211-emv-aid-rid-pix
-const char *PSElist [] = { 
+static const char *PSElist [] = { 
        "325041592E5359532E4444463031", // 2PAY.SYS.DDF01 - Visa Proximity Payment System Environment - PPSE
        "315041592E5359532E4444463031"  // 1PAY.SYS.DDF01 - Visa Payment System Environment - PSE
 };
-const size_t PSElistLen = sizeof(PSElist)/sizeof(char*);
+//static const size_t PSElistLen = sizeof(PSElist)/sizeof(char*);
 
-const char *AIDlist [] = { 
+typedef struct {
+       enum CardPSVendor vendor;
+       const char* aid;
+} TAIDList;
+
+static const TAIDList AIDlist [] = { 
        // Visa International
-       "A00000000305076010",   // VISA ELO Credit      
-       "A0000000031010",               // VISA Debit/Credit (Classic)  
-       "A0000000031010",               // ddddddddddddddddddddddddddddddddddddddddddddddddddddddddd    
-       "A000000003101001",             // VISA Credit  
-       "A000000003101002",             // VISA Debit   
-       "A0000000032010",               // VISA Electron
-       "A0000000032020",               // VISA 
-       "A0000000033010",               // VISA Interlink       
-       "A0000000034010",               // VISA Specific        
-       "A0000000035010",               // VISA Specific        
-       "A0000000036010",               // Domestic Visa Cash Stored Value      
-       "A0000000036020",               // International Visa Cash Stored Value 
-       "A0000000038002",               // VISA Auth, VisaRemAuthen EMV-CAP (DPA)       
-       "A0000000038010",               // VISA Plus    
-       "A0000000039010",               // VISA Loyalty 
-       "A000000003999910",             // VISA Proprietary ATM 
+       { CV_VISA,      "A00000000305076010"},                  // VISA ELO Credit      
+       { CV_VISA,      "A0000000031010" },                             // VISA Debit/Credit (Classic)  
+       { CV_VISA,      "A000000003101001" },                   // VISA Credit  
+       { CV_VISA,      "A000000003101002" },                   // VISA Debit   
+       { CV_VISA,      "A0000000032010" },                             // VISA Electron
+       { CV_VISA,      "A0000000032020" },                             // VISA 
+       { CV_VISA,      "A0000000033010" },                             // VISA Interlink       
+       { CV_VISA,      "A0000000034010" },                             // VISA Specific        
+       { CV_VISA,      "A0000000035010" },                             // VISA Specific        
+       { CV_VISA,      "A0000000036010" },                             // Domestic Visa Cash Stored Value      
+       { CV_VISA,      "A0000000036020" },                             // International Visa Cash Stored Value 
+       { CV_VISA,      "A0000000038002" },                             // VISA Auth, VisaRemAuthen EMV-CAP (DPA)       
+       { CV_VISA,      "A0000000038010" },                             // VISA Plus    
+       { CV_VISA,      "A0000000039010" },                             // VISA Loyalty 
+       { CV_VISA,      "A000000003999910" },                   // VISA Proprietary ATM 
        // Visa USA
-       "A000000098",                   // Debit Card
-       "A0000000980848",               // Debit Card
+       { CV_VISA,      "A000000098" },                                 // Debit Card
+       { CV_VISA,      "A0000000980848" },                             // Debit Card
        // Mastercard International
-       "A00000000401",                 // MasterCard PayPass   
-       "A0000000041010",               // MasterCard Credit
-       "A00000000410101213",   // MasterCard Credit
-       "A00000000410101215",   // MasterCard Credit
-       "A0000000042010",               // MasterCard Specific
-       "A0000000043010",               // MasterCard Specific
-       "A0000000043060",               // Maestro (Debit)
-       "A000000004306001",             // Maestro (Debit)
-       "A0000000044010",               // MasterCard Specific
-       "A0000000045010",               // MasterCard Specific
-       "A0000000046000",               // Cirrus
-       "A0000000048002",               // SecureCode Auth EMV-CAP
-       "A0000000049999",               // MasterCard PayPass   
+       { CV_MASTERCARD, "A00000000401" },                      // MasterCard PayPass   
+       { CV_MASTERCARD, "A0000000041010" },            // MasterCard Credit
+       { CV_MASTERCARD, "A00000000410101213" },        // MasterCard Credit
+       { CV_MASTERCARD, "A00000000410101215" },        // MasterCard Credit
+       { CV_MASTERCARD, "A0000000042010" },            // MasterCard Specific
+       { CV_MASTERCARD, "A0000000043010" },            // MasterCard Specific
+       { CV_MASTERCARD, "A0000000043060" },            // Maestro (Debit)
+       { CV_MASTERCARD, "A000000004306001" },          // Maestro (Debit)
+       { CV_MASTERCARD, "A0000000044010" },            // MasterCard Specific
+       { CV_MASTERCARD, "A0000000045010" },            // MasterCard Specific
+       { CV_MASTERCARD, "A0000000046000" },            // Cirrus
+       { CV_MASTERCARD, "A0000000048002" },            // SecureCode Auth EMV-CAP
+       { CV_MASTERCARD, "A0000000049999" },            // MasterCard PayPass   
        // American Express
-       "A000000025",
-       "A0000000250000",
-       "A00000002501",
-       "A000000025010402",
-       "A000000025010701",
-       "A000000025010801",
+       { CV_AMERICANEXPRESS, "A000000025" },
+       { CV_AMERICANEXPRESS, "A0000000250000" },
+       { CV_AMERICANEXPRESS, "A00000002501" },
+       { CV_AMERICANEXPRESS, "A000000025010402" },
+       { CV_AMERICANEXPRESS, "A000000025010701" },
+       { CV_AMERICANEXPRESS, "A000000025010801" },
        // Groupement des Cartes Bancaires "CB"
-       "A0000000421010",               // Cartes Bancaire EMV Card     
-       "A0000000422010",               
-       "A0000000423010",               
-       "A0000000424010",               
-       "A0000000425010",               
+       { CV_CB, "A0000000421010" },                            // Cartes Bancaire EMV Card     
+       { CV_CB, "A0000000422010" },            
+       { CV_CB, "A0000000423010" },            
+       { CV_CB, "A0000000424010" },            
+       { CV_CB, "A0000000425010" },            
        // JCB CO., LTD.
-       "A00000006510",                 // JCB  
-       "A0000000651010",               // JCB J Smart Credit   
-       "A0000001544442",               // Banricompras Debito - Banrisul - Banco do Estado do Rio Grande do SUL - S.A.
-       "F0000000030001",               // BRADESCO
-       "A0000005241010",               // RuPay - RuPay
-       "D5780000021010"                // Bankaxept - Bankaxept        
+       { CV_JCB, "A00000006510" },                                     // JCB  
+       { CV_JCB, "A0000000651010" },                           // JCB J Smart Credit   
+       // Other
+       { CV_OTHER, "A0000001544442" },                         // Banricompras Debito - Banrisul - Banco do Estado do Rio Grande do SUL - S.A.
+       { CV_OTHER, "F0000000030001" },                         // BRADESCO
+       { CV_OTHER, "A0000005241010" },                         // RuPay - RuPay
+       { CV_OTHER, "D5780000021010" }                          // Bankaxept - Bankaxept
 };
-const size_t AIDlistLen = sizeof(AIDlist)/sizeof(char*);
+static const size_t AIDlistLen = sizeof(AIDlist)/sizeof(TAIDList);
 
 static bool APDULogging = false;
 void SetAPDULogging(bool logging) {
        APDULogging = logging;
 }
 
+enum CardPSVendor GetCardPSVendor(uint8_t * AID, size_t AIDlen) {
+       char buf[100] = {0};
+       if (AIDlen < 1)
+               return CV_NA;
+
+       hex_to_buffer((uint8_t *)buf, AID, AIDlen, sizeof(buf) - 1, 0, 0, true);
+
+       for(int i = 0; i < AIDlistLen; i ++) {
+               if (strncmp(AIDlist[i].aid, buf, strlen(AIDlist[i].aid)) == 0){
+                       return AIDlist[i].vendor;
+               }
+       }       
+       
+       return CV_NA;
+}
+
 static bool print_cb(void *data, const struct tlv *tlv, int level, bool is_leaf) {
        emv_tag_dump(tlv, stdout, level);
        if (is_leaf) {
@@ -103,11 +124,15 @@ void TLVPrintFromBuffer(uint8_t *data, int datalen) {
        }
 }
 
-void TLVPrintFromTLV(struct tlvdb *tlv) {
+void TLVPrintFromTLVLev(struct tlvdb *tlv, int level) {
        if (!tlv) 
                return;
        
-       tlvdb_visit(tlv, print_cb, NULL, 0);
+       tlvdb_visit(tlv, print_cb, NULL, level);
+}
+
+void TLVPrintFromTLV(struct tlvdb *tlv) {
+       TLVPrintFromTLVLev(tlv, 0);
 }
 
 void TLVPrintAIDlistFromSelectTLV(struct tlvdb *tlv) {
@@ -136,41 +161,94 @@ void TLVPrintAIDlistFromSelectTLV(struct tlvdb *tlv) {
        PrintAndLog("|------------------|--------|-------------------------|");
 }
 
+struct tlvdb *GetPANFromTrack2(const struct tlv *track2) {
+       char track2Hex[200] = {0};
+       uint8_t PAN[100] = {0};
+       int PANlen = 0;
+       char *tmp = track2Hex;
 
-int EMVSelect(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) {
+       if (!track2)
+               return NULL;
+
+       for (int i = 0; i < track2->len; ++i, tmp += 2)
+               sprintf(tmp, "%02x", (unsigned int)track2->value[i]);
+       
+       int posD = strchr(track2Hex, 'd') - track2Hex;
+       if (posD < 1)
+               return NULL;
+       
+       track2Hex[posD] = 0;
+       if (strlen(track2Hex) % 2) {
+               track2Hex[posD] = 'F';
+               track2Hex[posD + 1] = '\0';
+       }
+       
+       param_gethex_to_eol(track2Hex, 0, PAN, sizeof(PAN), &PANlen);
+       
+  return tlvdb_fixed(0x5a, PANlen, PAN);
+}
+
+struct tlvdb *GetdCVVRawFromTrack2(const struct tlv *track2) {
+       char track2Hex[200] = {0};
+       char dCVVHex[100] = {0};
+       uint8_t dCVV[100] = {0};
+       int dCVVlen = 0;
+       const int PINlen = 5; // must calculated from 9F67 MSD Offset but i have not seen this tag)
+       char *tmp = track2Hex;
+       
+       if (!track2)
+               return NULL;
+       
+       for (int i = 0; i < track2->len; ++i, tmp += 2)
+               sprintf(tmp, "%02x", (unsigned int)track2->value[i]);
+       
+       int posD = strchr(track2Hex, 'd') - track2Hex;
+       if (posD < 1)
+               return NULL;
+
+       memset(dCVVHex, '0', 32);
+       // ATC
+       memcpy(dCVVHex + 0, track2Hex + posD + PINlen + 11, 4);
+       // PAN 5 hex
+       memcpy(dCVVHex + 4, track2Hex, 5);
+       // expire date
+       memcpy(dCVVHex + 9, track2Hex + posD + 1, 4);
+       // service code
+       memcpy(dCVVHex + 13, track2Hex + posD + 5, 3);
+       
+       param_gethex_to_eol(dCVVHex, 0, dCVV, sizeof(dCVV), &dCVVlen);
+       
+  return tlvdb_fixed(0x02, dCVVlen, dCVV);
+}
+
+int EMVExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
        uint8_t data[APDU_RES_LEN] = {0};
+
        *ResultLen = 0;
        if (sw) *sw = 0;
        uint16_t isw = 0;
        
-       // select APDU
-       data[0] = 0x00;
-       data[1] = 0xA4;
-       data[2] = 0x04;
-       data[3] = 0x00;
-       data[4] = AIDLen;
-       memcpy(&data[5], AID, AIDLen);
-       
        if (ActivateField)
                DropField();
        
-       if (APDULogging)
-               PrintAndLog(">>>> %s", sprint_hex(data, AIDLen + 6));
-
-       int res = ExchangeAPDU14a(data, AIDLen + 6, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen);
+       // COMPUTE APDU
+       memcpy(data, &apdu, 5);
+       if (apdu.data)
+               memcpy(&data[5], apdu.data, apdu.Lc);
        
        if (APDULogging)
-               PrintAndLog("<<<< %s", sprint_hex(Result, *ResultLen));
+               PrintAndLog(">>>> %s", sprint_hex(data, 6 + apdu.Lc));
+
+       // 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 (res) {
                return res;
        }
-       
-       if (*ResultLen < 2) {
-               PrintAndLog("SELECT ERROR: returned %d bytes", *ResultLen);
-               return 5;
-       }
-       
+
+       if (APDULogging)
+               PrintAndLog("<<<< %s", sprint_hex(Result, *ResultLen));
+
        *ResultLen -= 2;
        isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1];
        if (sw)
@@ -178,7 +256,7 @@ int EMVSelect(bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen
 
        if (isw != 0x9000) {
                if (APDULogging)
-                       PrintAndLog("SELECT ERROR: [%4X] %s", isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xff));
+                       PrintAndLog("APDU(%02x%02x) ERROR: [%4X] %s", apdu.CLA, apdu.INS, isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xff));
                return 5;
        }
 
@@ -191,6 +269,14 @@ int EMVSelect(bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen
        return 0;
 }
 
+int EMVExchange(bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
+       return EMVExchangeEx(false, LeaveFieldON, apdu, Result, MaxResultLen, ResultLen, sw, tlv);
+}
+
+int EMVSelect(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) {
+       return EMVExchangeEx(ActivateField, LeaveFieldON, (sAPDU){0x00, 0xa4, 0x04, 0x00, AIDLen, AID}, Result, MaxResultLen, ResultLen, sw, tlv);
+}
+
 int EMVSelectPSE(bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
        uint8_t buf[APDU_AID_LEN] = {0};
        *ResultLen = 0;
@@ -291,7 +377,7 @@ int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvd
        int res = 0;
        int retrycnt = 0;
        for(int i = 0; i < AIDlistLen; i ++) {
-               param_gethex_to_eol(AIDlist[i], 0, aidbuf, sizeof(aidbuf), &aidlen);
+               param_gethex_to_eol(AIDlist[i].aid, 0, aidbuf, sizeof(aidbuf), &aidlen);
                res = EMVSelect((i == 0) ? ActivateField : false, (i == AIDlistLen - 1) ? LeaveFieldON : true, aidbuf, aidlen, data, sizeof(data), &datalen, &sw, tlv);
                // retry if error and not returned sw error
                if (res && res != 5) {
@@ -305,7 +391,7 @@ int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvd
                                }
                                
                                retrycnt = 0;
-                               PrintAndLog("Retry failed [%s]. Skiped...", AIDlist[i]);
+                               PrintAndLog("Retry failed [%s]. Skiped...", AIDlist[i].aid);
                        }
                        continue;
                }
@@ -315,7 +401,7 @@ int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvd
                        continue;
                
                if (decodeTLV){
-                       PrintAndLog("%s:", AIDlist[i]);
+                       PrintAndLog("%s:", AIDlist[i].aid);
                        TLVPrintFromBuffer(data, datalen);
                }
        }
@@ -324,7 +410,7 @@ int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvd
 }
 
 int EMVSelectApplication(struct tlvdb *tlv, uint8_t *AID, size_t *AIDlen) {
-       // needs to check priority. 0x00 - highest
+       // check priority. 0x00 - highest
        int prio = 0xffff;
        
        *AIDlen = 0;
@@ -363,101 +449,393 @@ int EMVSelectApplication(struct tlvdb *tlv, uint8_t *AID, size_t *AIDlen) {
 }
 
 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) {
-       uint8_t data[APDU_RES_LEN] = {0};
-       *ResultLen = 0;
-       if (sw) *sw = 0;
-       uint16_t isw = 0;
-       
-       // GPO APDU
-       data[0] = 0x80;
-       data[1] = 0xA8;
-       data[2] = 0x00;
-       data[3] = 0x00;
-       data[4] = PDOLLen;
-       if (PDOL)
-               memcpy(&data[5], PDOL, PDOLLen);
-       
-       
-       if (APDULogging)
-               PrintAndLog(">>>> %s", sprint_hex(data, PDOLLen + 5));
+       return EMVExchange(LeaveFieldON, (sAPDU){0x80, 0xa8, 0x00, 0x00, PDOLLen, PDOL}, Result, MaxResultLen, ResultLen, sw, tlv);
+}
 
-       int res = ExchangeAPDU14a(data, PDOLLen + 5, false, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen);
-       
-       if (APDULogging)
-               PrintAndLog("<<<< %s", sprint_hex(Result, *ResultLen));
-       
-       if (res) {
-               return res;
+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) {
+       return EMVExchange(LeaveFieldON, (sAPDU){0x00, 0xb2, SFIrec, (SFI << 3) | 0x04, 0, NULL}, Result, MaxResultLen, ResultLen, sw, tlv);
+}
+
+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) {
+       return EMVExchange(LeaveFieldON, (sAPDU){0x80, 0xae, RefControl, 0x00, CDOLLen, CDOL}, Result, MaxResultLen, ResultLen, sw, tlv);
+}
+
+int EMVGenerateChallenge(bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
+       return EMVExchange(LeaveFieldON, (sAPDU){0x00, 0x84, 0x00, 0x00, 0x00, NULL}, Result, MaxResultLen, ResultLen, sw, tlv);
+}
+
+int EMVInternalAuthenticate(bool LeaveFieldON, uint8_t *DDOL, size_t DDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
+       return EMVExchange(LeaveFieldON, (sAPDU){0x00, 0x88, 0x00, 0x00, DDOLLen, DDOL}, Result, MaxResultLen, ResultLen, sw, tlv);
+}
+
+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 
+static 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;
+
+       PrintAndLog("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) {
+               PrintAndLog("ERROR: Key not found. Exit.");
+               return 2;
        }
        
-       if (*ResultLen < 2) {
-               PrintAndLog("GPO ERROR: returned %d bytes", *ResultLen);
-               return 5;
+       struct emv_pk *issuer_pk = emv_pki_recover_issuer_cert(pk, tlv);
+       if (!issuer_pk) {
+               emv_pk_free(pk);
+               PrintAndLog("ERROR: Issuer certificate not found. Exit.");
+               return 2;
        }
-       
-       *ResultLen -= 2;
-       isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1];
-       if (sw)
-               *sw = isw;
 
-       if (isw != 0x9000) {
-               if (APDULogging)
-                       PrintAndLog("GPO ERROR: [%4X] %s", isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xff));
-               return 5;
+       PrintAndLog("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);
+               PrintAndLog("ERROR: Can't find input list for Offline Data Authentication. Exit.");
+               return 3;
        }
-
-       // add to tlv tree
-       if (tlv) {
-               struct tlvdb *t = tlvdb_parse_multi(Result, *ResultLen);
-               tlvdb_add(tlv, t);
+       
+       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);
+               PrintAndLog("SDA verified OK. (%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);
+               PrintAndLog("ERROR: SSAD verify error");
+               return 4;
        }
        
+       emv_pk_free(issuer_pk);
+       emv_pk_free(pk);
        return 0;
 }
 
-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) {
-       uint8_t data[10] = {0};
-       *ResultLen = 0;
-       if (sw) *sw = 0;
-       uint16_t isw = 0;
-       
-       // read record APDU
-       data[0] = 0x00;
-       data[1] = 0xb2;
-       data[2] = SFIrec;
-       data[3] = (SFI << 3) | 0x04;
-       data[4] = 0;
-       
-       if (APDULogging)
-               PrintAndLog(">>>> %s", sprint_hex(data, 5));
+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 res = ExchangeAPDU14a(data, 5, false, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen);
-       
-       if (APDULogging)
-               PrintAndLog("<<<< %s", sprint_hex(Result, *ResultLen));
-       
-       if (res) {
-               return res;
+int trDDA(bool decodeTLV, struct tlvdb *tlv) {
+       uint8_t buf[APDU_RES_LEN] = {0};
+       size_t len = 0;
+       uint16_t sw = 0;
+
+       struct emv_pk *pk = get_ca_pk(tlv);
+       if (!pk) {
+               PrintAndLog("ERROR: Key not found. Exit.");
+               return 2;
        }
 
-       *ResultLen -= 2;
-       isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1];
-       if (sw)
-               *sw = isw;
+       const struct tlv *sda_tlv = tlvdb_get(tlv, 0x21, NULL);
+       if (!sda_tlv || sda_tlv->len < 1) {
+               emv_pk_free(pk);
+               PrintAndLog("ERROR: Can't find input list for Offline Data Authentication. Exit.");
+               return 3;
+       }
 
-       if (isw != 0x9000) {
-               if (APDULogging)
-                       PrintAndLog("Read record ERROR: [%4X] %s", isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xff));
-               return 5;
+       struct emv_pk *issuer_pk = emv_pki_recover_issuer_cert(pk, tlv);
+       if (!issuer_pk) {
+               emv_pk_free(pk);
+               PrintAndLog("ERROR: Issuer certificate not found. Exit.");
+               return 2;
+       }
+       printf("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);
+               PrintAndLog("ERROR: ICC setrificate not found. Exit.");
+               return 2;
+       }
+       printf("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 emv_pk *icc_pe_pk = emv_pki_recover_icc_pe_cert(issuer_pk, tlv);
+       if (!icc_pe_pk) {
+               PrintAndLog("WARNING: ICC PE PK recover error. ");
+       } else {
+               printf("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]
+                               );
        }
 
-       // add to tlv tree
-       if (tlv) {
-               struct tlvdb *t = tlvdb_parse_multi(Result, *ResultLen);
-               tlvdb_add(tlv, t);
+       // 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) {
+               PrintAndLog("\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) {
+                       PrintAndLog("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) {
+                       PrintAndLog("\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)) {
+                               PrintAndLog("ATC check OK.");
+                               PrintAndLog("fDDA (fast DDA) verified OK.");
+                       } else {
+                               PrintAndLog("ERROR: fDDA verified, but ATC in the certificate and ATC in the record not the same.");
+                       }
+               } else {
+                       PrintAndLog("\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);
+                       printf("SDA verified OK. (%02hhx:%02hhx)\n", dac_tlv->value[0], dac_tlv->value[1]);
+                       tlvdb_add(tlv, dac_db);
+               } else {
+                       PrintAndLog("ERROR: SSAD verify error");
+                       emv_pk_free(pk);
+                       emv_pk_free(issuer_pk);
+                       emv_pk_free(icc_pk);
+                       return 4;
+               }
+               
+               PrintAndLog("\n* Calc DDOL");
+               const struct tlv *ddol_tlv = tlvdb_get(tlv, 0x9f49, NULL);
+               if (!ddol_tlv) {
+                       ddol_tlv = &default_ddol_tlv;
+                       PrintAndLog("DDOL [9f49] not found. Using default DDOL");
+               }
+
+               struct tlv *ddol_data_tlv = dol_process(ddol_tlv, tlv, 0);
+               if (!ddol_data_tlv) {
+                       PrintAndLog("ERROR: Can't create DDOL TLV");
+                       emv_pk_free(pk);
+                       emv_pk_free(issuer_pk);
+                       emv_pk_free(icc_pk);
+                       return 5;
+               }
+
+               PrintAndLog("DDOL data[%d]: %s", ddol_data_tlv->len, sprint_hex(ddol_data_tlv->value, ddol_data_tlv->len));
+
+               PrintAndLog("\n* Internal Authenticate");
+               int res = EMVInternalAuthenticate(true, (uint8_t *)ddol_data_tlv->value, ddol_data_tlv->len, buf, sizeof(buf), &len, &sw, NULL);
+               if (res) {      
+                       PrintAndLog("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 ) {
+                               PrintAndLog("ERROR: Internal Authenticate format1 parsing error. length=%d", len);
+                       } else {
+                               // 9f4b Signed Dynamic Application Data
+                               dda_db = tlvdb_fixed(0x9f4b, len - 2, buf + 2);
+                               tlvdb_add(tlv, dda_db);
+                               if (decodeTLV){
+                                       PrintAndLog("* * Decode response format 1:");
+                                       TLVPrintFromTLV(dda_db);
+                               }
+                       }
+               } else {
+                       dda_db = tlvdb_parse_multi(buf, len);
+                       if(!dda_db) {
+                               PrintAndLog("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) {
+                       PrintAndLog("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) {
+                       PrintAndLog("\nIDN (ICC Dynamic Number) [%zu] %s", idn_tlv->len, sprint_hex_inrow(idn_tlv->value, idn_tlv->len));
+                       PrintAndLog("DDA verified OK.");
+                       tlvdb_add(tlv, idn_db);
+                       tlvdb_free(idn_db);
+               } else {
+                       PrintAndLog("\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) {
+               PrintAndLog("ERROR: Key not found. Exit.");
+               return 2;
+       }
+
+       const struct tlv *sda_tlv = tlvdb_get(tlv, 0x21, NULL);
+       if (!sda_tlv || sda_tlv->len < 1) {
+               PrintAndLog("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) {
+               PrintAndLog("ERROR: Issuer certificate not found. Exit.");
+               emv_pk_free(pk);
+               return 2;
+       }
+       printf("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) {
+               PrintAndLog("ERROR: ICC setrificate not found. Exit.");
+               emv_pk_free(pk);
+               emv_pk_free(issuer_pk);
+               return 2;
+       }
+       printf("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);
+               PrintAndLog("SSAD verified OK. (%02hhx:%02hhx)", dac_tlv->value[0], dac_tlv->value[1]);
+               tlvdb_add(tlv, dac_db);
+       } else {
+               PrintAndLog("ERROR: SSAD verify error");
+               emv_pk_free(pk);
+               emv_pk_free(issuer_pk);
+               emv_pk_free(icc_pk);
+               return 4;
+       }
+       
+       PrintAndLog("\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);
+               PrintAndLog("\nIDN (ICC Dynamic Number) [%zu] %s", idn_tlv->len, sprint_hex_inrow(idn_tlv->value, idn_tlv->len));
+               PrintAndLog("CDA verified OK.");
+               tlvdb_add(tlv, idn_db);
+       } else {
+               PrintAndLog("\nERROR: CDA verify error");
+       }
+
+       emv_pk_free(pk);
+       emv_pk_free(issuer_pk);
+       emv_pk_free(icc_pk);
+       return 0;
+}
index fdd3ab868762ae9148d524a4053921376cf6a6c2..94be4fcf36b8949fc1bd641448f4ff3e689594b8 100644 (file)
 #include "common.h"
 #include "ui.h"
 #include "cmdhf14a.h"
-#include "emv/apduinfo.h"
-#include "emv/tlv.h"
-#include "emv/dol.h"
-#include "emv/dump.h"
-#include "emv/emv_tags.h"
+#include "apduinfo.h"
+#include "tlv.h"
+#include "dol.h"
+#include "dump.h"
+#include "emv_tags.h"
+#include "emv_pk.h"
+#include "emv_pki.h"
 
 #define APDU_RES_LEN 260
 #define APDU_AID_LEN 50
 
+enum TransactionType {
+       TT_MSD,
+       TT_VSDC,        // not standart for contactless!!!!
+       TT_QVSDCMCHIP,
+       TT_CDA,
+};
+
+typedef struct {
+       uint8_t CLA;
+       uint8_t INS;
+       uint8_t P1;
+       uint8_t P2;
+       uint8_t Lc;
+       uint8_t *data;
+} sAPDU;
+
+enum CardPSVendor {
+       CV_NA,
+       CV_VISA,
+       CV_MASTERCARD,
+       CV_AMERICANEXPRESS,
+       CV_JCB,
+       CV_CB,
+       CV_OTHER,
+};
+extern enum CardPSVendor GetCardPSVendor(uint8_t * AID, size_t AIDlen);
+
 extern void 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 struct tlvdb *GetPANFromTrack2(const struct tlv *track2);
+extern struct tlvdb *GetdCVVRawFromTrack2(const struct tlv *track2);
+
 extern void SetAPDULogging(bool logging);
 
 // search application
@@ -45,6 +78,17 @@ 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);
+// DDA
+extern int EMVInternalAuthenticate(bool LeaveFieldON, uint8_t *DDOL, size_t DDOLLen, 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(struct tlvdb *tlv);
+extern int trDDA(bool decodeTLV, struct tlvdb *tlv);
+extern int trCDA(struct tlvdb *tlv, struct tlvdb *ac_tlv, struct tlv *pdol_data_tlv, struct tlv *ac_data_tlv);
 
 #endif
 
diff --git a/client/emv/test/cda_test.c b/client/emv/test/cda_test.c
new file mode 100644 (file)
index 0000000..536a586
--- /dev/null
@@ -0,0 +1,442 @@
+/*
+ * emv-tools - a set of tools to work with EMV family of smart cards
+ * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "../emv_pk.h"
+#include "../crypto.h"
+#include "../dump.h"
+#include "../tlv.h"
+#include "../emv_pki.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+struct emv_pk c_mchip_05 = {
+       .rid = { 0xa0, 0x00, 0x00, 0x00, 0x04, },
+       .index = 5,
+       .hash_algo = HASH_SHA_1,
+       .pk_algo = PK_RSA,
+       .hash = {
+               0xeb, 0xfa, 0x0d, 0x5d,
+               0x06, 0xd8, 0xce, 0x70,
+               0x2d, 0xa3, 0xea, 0xe8,
+               0x90, 0x70, 0x1d, 0x45,
+               0xe2, 0x74, 0xc8, 0x45, },
+       .exp = { 0x03, },
+       .elen = 1,
+       .mlen = 1408 / 8,
+       .modulus = (unsigned char[]){
+               0xb8, 0x04, 0x8a, 0xbc, 0x30, 0xc9, 0x0d, 0x97, 0x63, 0x36, 0x54, 0x3e, 0x3f, 0xd7, 0x09, 0x1c,
+               0x8f, 0xe4, 0x80, 0x0d, 0xf8, 0x20, 0xed, 0x55, 0xe7, 0xe9, 0x48, 0x13, 0xed, 0x00, 0x55, 0x5b,
+               0x57, 0x3f, 0xec, 0xa3, 0xd8, 0x4a, 0xf6, 0x13, 0x1a, 0x65, 0x1d, 0x66, 0xcf, 0xf4, 0x28, 0x4f,
+               0xb1, 0x3b, 0x63, 0x5e, 0xdd, 0x0e, 0xe4, 0x01, 0x76, 0xd8, 0xbf, 0x04, 0xb7, 0xfd, 0x1c, 0x7b,
+               0xac, 0xf9, 0xac, 0x73, 0x27, 0xdf, 0xaa, 0x8a, 0xa7, 0x2d, 0x10, 0xdb, 0x3b, 0x8e, 0x70, 0xb2,
+               0xdd, 0xd8, 0x11, 0xcb, 0x41, 0x96, 0x52, 0x5e, 0xa3, 0x86, 0xac, 0xc3, 0x3c, 0x0d, 0x9d, 0x45,
+               0x75, 0x91, 0x64, 0x69, 0xc4, 0xe4, 0xf5, 0x3e, 0x8e, 0x1c, 0x91, 0x2c, 0xc6, 0x18, 0xcb, 0x22,
+               0xdd, 0xe7, 0xc3, 0x56, 0x8e, 0x90, 0x02, 0x2e, 0x6b, 0xba, 0x77, 0x02, 0x02, 0xe4, 0x52, 0x2a,
+               0x2d, 0xd6, 0x23, 0xd1, 0x80, 0xe2, 0x15, 0xbd, 0x1d, 0x15, 0x07, 0xfe, 0x3d, 0xc9, 0x0c, 0xa3,
+               0x10, 0xd2, 0x7b, 0x3e, 0xfc, 0xcd, 0x8f, 0x83, 0xde, 0x30, 0x52, 0xca, 0xd1, 0xe4, 0x89, 0x38,
+               0xc6, 0x8d, 0x09, 0x5a, 0xac, 0x91, 0xb5, 0xf3, 0x7e, 0x28, 0xbb, 0x49, 0xec, 0x7e, 0xd5, 0x97,
+       },
+};
+
+const unsigned char c_issuer_cert[] = {
+               0x17, 0x14, 0x28, 0x4f, 0x76, 0x3b, 0x85, 0x86, 0xee, 0x6d, 0x31, 0x99, 0x51, 0xf7, 0xe6, 0x3f,
+               0xa2, 0x50, 0x76, 0xe5, 0x0d, 0xc9, 0xd3, 0x20, 0x0b, 0xa9, 0x98, 0xd3, 0xa0, 0x52, 0xad, 0xba,
+               0x9a, 0xb6, 0x9a, 0xc6, 0xad, 0x6a, 0xdd, 0x3c, 0xe0, 0x9f, 0x02, 0x78, 0xf4, 0x07, 0x4e, 0xc4,
+               0xee, 0x9b, 0x1d, 0x22, 0x68, 0xa3, 0xe9, 0x53, 0x57, 0x5e, 0x45, 0x4e, 0x50, 0xcd, 0x86, 0x0b,
+               0xf4, 0x24, 0xc5, 0x1c, 0x59, 0x77, 0x12, 0xd2, 0xaa, 0x05, 0x70, 0x89, 0xdd, 0x86, 0x73, 0xe5,
+               0x1b, 0x1e, 0x1d, 0x71, 0x88, 0x03, 0x48, 0x92, 0x07, 0x7a, 0xc1, 0x8a, 0x6a, 0xe2, 0x34, 0x88,
+               0xbe, 0xa9, 0xdf, 0x3b, 0x1a, 0x83, 0xf2, 0xc0, 0x80, 0x0c, 0xd7, 0xc5, 0xcd, 0xf2, 0xfd, 0xe0,
+               0x49, 0x6f, 0x7b, 0xc3, 0x9f, 0xb4, 0xbf, 0x36, 0x32, 0x99, 0xbf, 0xa6, 0x37, 0xb2, 0xec, 0x33,
+               0xc5, 0x07, 0xe3, 0x68, 0x21, 0xee, 0xc2, 0x07, 0x5f, 0x0e, 0x42, 0x0d, 0x38, 0xa1, 0xc9, 0xf3,
+               0x12, 0x72, 0x61, 0xba, 0x31, 0x6c, 0x98, 0x76, 0x74, 0xfa, 0xdb, 0x20, 0xea, 0x7f, 0xeb, 0x75,
+               0xee, 0x45, 0x5d, 0x12, 0x14, 0x6e, 0xa6, 0xf0, 0x2e, 0x8b, 0x01, 0xec, 0x2f, 0xa7, 0xa1, 0x15,
+};
+
+const unsigned char c_issuer_rem[] = {
+               0x6e, 0x63, 0xb7, 0xbc, 0x70, 0xab, 0xdd, 0x09, 0x34, 0x1b, 0x34, 0xc0, 0x32, 0x86, 0xba, 0x9b,
+               0xd8, 0x3b, 0xa7, 0x93, 0x6c, 0x5b, 0x77, 0x98, 0xfb, 0x22, 0xc5, 0xe5, 0x3f, 0xf2, 0x40, 0xa2,
+               0x6d, 0xbd, 0x64, 0x15,
+};
+
+const unsigned char c_issuer_exp[] = {
+               0x03,
+};
+
+const unsigned char c_icc_cert[] = {
+               0xa4, 0x2f, 0xbe, 0xb1, 0x56, 0xb9, 0x8d, 0xcb, 0x05, 0x54, 0xda, 0x06, 0x2a, 0xdc, 0xa5, 0x30,
+               0x9a, 0x91, 0xf0, 0x4f, 0xa2, 0xc7, 0xbd, 0x71, 0x02, 0xa8, 0xd7, 0x3f, 0x16, 0xa3, 0xcf, 0xad,
+               0xe8, 0xaa, 0xdf, 0x4f, 0x3f, 0xe2, 0xa2, 0x12, 0x5c, 0xcd, 0xd7, 0x7c, 0x6b, 0x9f, 0x78, 0xb5,
+               0xb4, 0x37, 0x1c, 0xe0, 0x80, 0x57, 0x25, 0xb0, 0xf9, 0xc0, 0x27, 0xaf, 0x14, 0x7d, 0x91, 0xe1,
+               0xff, 0xdb, 0x20, 0x1e, 0x9c, 0x17, 0x0c, 0xe7, 0x77, 0x05, 0x3a, 0x17, 0x2a, 0xd5, 0x26, 0xdc,
+               0xaf, 0xd3, 0x38, 0x95, 0xe1, 0xa9, 0x47, 0x30, 0x5c, 0x5b, 0x16, 0x7f, 0x2e, 0x7c, 0x6f, 0x99,
+               0x15, 0x81, 0xa6, 0x52, 0xee, 0x47, 0x31, 0x54, 0x76, 0x0c, 0x2e, 0xd7, 0x74, 0x21, 0x4e, 0x50,
+               0xdf, 0xec, 0xdd, 0x4c, 0xf2, 0x94, 0xc9, 0x74, 0xb8, 0x9e, 0xbc, 0xa2, 0x5b, 0x5a, 0xb3, 0xc0,
+               0xbe, 0xb5, 0x0d, 0xfa, 0xf7, 0x82, 0xaf, 0xde, 0x14, 0x33, 0xd9, 0x0c, 0xa2, 0xa8, 0x9d, 0x65,
+               0x1e, 0x75, 0xd6, 0x7e, 0xbc, 0x7c, 0x3e, 0x36, 0xf5, 0xa1, 0x65, 0xee, 0x61, 0x32, 0x61, 0x29,
+               0x39, 0xc1, 0xec, 0xd3, 0x99, 0xe4, 0x60, 0x74, 0xb9, 0x96, 0xd9, 0x3a, 0x88, 0xe0, 0x1e, 0x0a,
+};
+
+const unsigned char c_icc_exp[] = {
+               0x03,
+};
+
+const unsigned char c_sdad_cr[] = {
+       0x1c, 0x00, 0x9f, 0xc4, 0x86, 0x79, 0x15, 0x7d, 0xbf, 0xf4, 0x5f, 0x65, 0xd3, 0x3f, 0xf7, 0x8d,
+       0x4f, 0xcb, 0xf0, 0xcf, 0x5e, 0xa4, 0x20, 0x8d, 0x10, 0x7a, 0xe9, 0x5a, 0xa3, 0x8c, 0x54, 0x6d,
+       0x0e, 0x5a, 0x18, 0xb8, 0x74, 0x03, 0xa1, 0x2b, 0xd4, 0x47, 0xa8, 0xbb, 0xfc, 0x1e, 0x49, 0xce,
+       0x0b, 0x2e, 0x25, 0x13, 0x89, 0x20, 0x57, 0x03, 0xc9, 0xbb, 0x1a, 0x88, 0xcc, 0x79, 0xf1, 0xdd,
+       0xc2, 0xf9, 0x84, 0x1e, 0xad, 0xf0, 0x7c, 0xe0, 0x7b, 0x62, 0x51, 0x1d, 0xdc, 0x93, 0xdf, 0x59,
+       0xf2, 0x8f, 0x0e, 0x91, 0xf9, 0x23, 0x32, 0xd2, 0x9c, 0xde, 0xf2, 0xbc, 0xcb, 0x10, 0x08, 0x85,
+       0x05, 0x00, 0xef, 0x3e, 0x47, 0x0a, 0x4c, 0xb1, 0x8c, 0xd9, 0x1a, 0xa5, 0xc1, 0xa1, 0x08, 0xf3,
+
+};
+
+const unsigned char c_ssd1[] = {
+       0x5f, 0x25, 0x03, 0x14, 0x05, 0x01, 0x5f, 0x24, 0x03, 0x15, 0x06, 0x30, 0x5a, 0x08, 0x52, 0x85,
+       0x88, 0x12, 0x54, 0x34, 0x56, 0x53, 0x5f, 0x34, 0x01, 0x01, 0x8e, 0x0c, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x1e, 0x03, 0x1f, 0x03, 0x9f, 0x07, 0x02, 0xff, 0x00, 0x9f, 0x0d, 0x05,
+       0xbc, 0x50, 0xbc, 0x00, 0x00, 0x9f, 0x0e, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9f, 0x0f, 0x05,
+       0xbc, 0x70, 0xbc, 0x98, 0x00, 0x9f, 0x4a, 0x01, 0x82, 0x5f, 0x28, 0x02, 0x06, 0x43, 0x8c, 0x21,
+       0x9f, 0x02, 0x06, 0x9f, 0x03, 0x06, 0x9f, 0x1a, 0x02, 0x95, 0x05, 0x5f, 0x2a, 0x02, 0x9a, 0x03,
+       0x9c, 0x01, 0x9f, 0x37, 0x04, 0x9f, 0x35, 0x01, 0x9f, 0x45, 0x02, 0x9f, 0x4c, 0x08, 0x9f, 0x34,
+       0x03, 0x8d, 0x0c, 0x91, 0x0a, 0x8a, 0x02, 0x95, 0x05, 0x9f, 0x37, 0x04, 0x9f, 0x4c, 0x08,
+       0x39, 0x00,
+};
+static const struct tlv ssd1_tlv = {
+       .len = sizeof(c_ssd1),
+       .value = c_ssd1,
+};
+
+const unsigned char c_pan[] = {
+       0x52, 0x85, 0x88, 0x12, 0x54, 0x34, 0x56, 0x53,
+};
+
+const unsigned char c_dd1[] = {
+       0x12, 0x34, 0x57, 0x79,
+};
+
+const unsigned char c_dd2[] = {
+       0x9f, 0x27, 0x01, 0x40, 0x9f, 0x36, 0x02, 0x00, 0x10, 0x9f, 0x10, 0x12, 0x00, 0x10, 0x90, 0x40,
+       0x01, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+};
+
+const unsigned char c_crm1[] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x43, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x06, 0x43, 0x14, 0x09, 0x25, 0x50, 0x12, 0x34, 0x57, 0x79, 0x23, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x03, 0x00,
+};
+static const struct tlv crm1_tlv = {
+       .len = sizeof(c_crm1),
+       .value = c_crm1,
+};
+
+static int cda_test_raw(bool verbose)
+{
+       const struct emv_pk *pk = &c_mchip_05;
+
+       struct crypto_pk *kcp = crypto_pk_open(PK_RSA,
+                       pk->modulus, pk->mlen,
+                       pk->exp, pk->elen);
+       if (!kcp)
+               return 1;
+
+       unsigned char *ipk_data;
+       size_t ipk_data_len;
+       ipk_data = crypto_pk_encrypt(kcp, c_issuer_cert, sizeof(c_issuer_cert), &ipk_data_len);
+       crypto_pk_close(kcp);
+
+       if (!ipk_data)
+               return 1;
+
+       if (verbose) {
+               printf("issuer cert:\n");
+               dump_buffer(ipk_data, ipk_data_len, stdout, 0);
+       }
+
+       size_t ipk_pk_len = ipk_data[13];
+       unsigned char *ipk_pk = malloc(ipk_pk_len);
+       memcpy(ipk_pk, ipk_data + 15, ipk_data_len - 36);
+       memcpy(ipk_pk + ipk_data_len - 36, c_issuer_rem, sizeof(c_issuer_rem));
+
+       struct crypto_hash *ch;
+       ch = crypto_hash_open(HASH_SHA_1);
+       if (!ch) {
+               free(ipk_pk);
+               free(ipk_data);
+               return 1;
+       }
+
+       crypto_hash_write(ch, ipk_data + 1, 14);
+       crypto_hash_write(ch, ipk_pk, ipk_pk_len);
+       crypto_hash_write(ch, c_issuer_exp, sizeof(c_issuer_exp));
+
+       unsigned char *h = crypto_hash_read(ch);
+       if (!h) {
+               crypto_hash_close(ch);
+               free(ipk_pk);
+               free(ipk_data);
+               return 1;
+       }
+
+       if (verbose) {
+               printf("crypto hash:\n");
+               dump_buffer(h, 20, stdout, 0);
+       }
+
+       if (memcmp(ipk_data + ipk_data_len - 21, h, 20)) {
+               crypto_hash_close(ch);
+               free(ipk_pk);
+               free(ipk_data);
+               return 1;
+       }
+
+       crypto_hash_close(ch);
+       free(ipk_data);
+
+       struct crypto_pk *ikcp = crypto_pk_open(PK_RSA, ipk_pk, (int) ipk_pk_len,
+                       c_issuer_exp, (int) sizeof(c_issuer_exp));
+       free(ipk_pk);
+       if (!ikcp)
+               return 1;
+
+       unsigned char *iccpk_data;
+       size_t iccpk_data_len;
+       iccpk_data = crypto_pk_encrypt(ikcp, c_icc_cert, sizeof(c_icc_cert), &iccpk_data_len);
+       crypto_pk_close(ikcp);
+
+       if (!iccpk_data)
+               return 1;
+
+       if (verbose) {
+               printf("icc cert:\n");
+               dump_buffer(iccpk_data, iccpk_data_len, stdout, 0);
+       }
+
+       size_t iccpk_pk_len = iccpk_data[19];
+       unsigned char *iccpk_pk = malloc(iccpk_pk_len);
+       memcpy(iccpk_pk, iccpk_data + 21, /*iccpk_data_len - 36*/iccpk_pk_len);
+       /*memcpy(iccpk_pk + iccpk_data_len - 36, icc_rem, sizeof(icc_rem));*/
+
+       ch = crypto_hash_open(HASH_SHA_1);
+       if (!ch) {
+               free(iccpk_pk);
+               free(iccpk_data);
+               return 1;
+       }
+
+       crypto_hash_write(ch, iccpk_data + 1, iccpk_data_len - 22);
+       crypto_hash_write(ch, c_icc_exp, sizeof(c_icc_exp));
+       crypto_hash_write(ch, c_ssd1, sizeof(c_ssd1));
+
+       h = crypto_hash_read(ch);
+       if (!h) {
+               crypto_hash_close(ch);
+               free(iccpk_pk);
+               free(iccpk_data);
+               return 1;
+       }
+
+       if (verbose) {
+               printf("crypto hash1.1:\n");
+               dump_buffer(h, 20, stdout, 0);
+       }
+
+       if (memcmp(iccpk_data + iccpk_data_len - 21, h, 20)) {
+               crypto_hash_close(ch);
+               free(iccpk_pk);
+               free(iccpk_data);
+               return 1;
+       }
+
+       crypto_hash_close(ch);
+       free(iccpk_data);
+
+       struct crypto_pk *icckcp = crypto_pk_open(PK_RSA, iccpk_pk, (int) iccpk_pk_len,
+                       c_issuer_exp, (int) sizeof(c_issuer_exp));
+       free(iccpk_pk);
+       if (!icckcp)
+               return 1;
+
+       size_t sdad_len;
+       unsigned char *sdad = crypto_pk_encrypt(icckcp, c_sdad_cr, sizeof(c_sdad_cr), &sdad_len);
+       crypto_pk_close(icckcp);
+       if (!sdad)
+               return 1;
+
+       if (verbose) {
+               printf("SDAD:\n");
+               dump_buffer(sdad, sdad_len, stdout, 0);
+       }
+
+       ch = crypto_hash_open(HASH_SHA_1);
+       if (!ch) {
+               free(sdad);
+               return 1;
+       }
+
+       crypto_hash_write(ch, sdad + 1, sdad_len - 22);
+       crypto_hash_write(ch, c_dd1, sizeof(c_dd1));
+
+       unsigned char *h2 = crypto_hash_read(ch);
+       if (!h2) {
+               crypto_hash_close(ch);
+               free(sdad);
+               return 1;
+       }
+
+       if (verbose) {
+               printf("crypto hash2:\n");
+               dump_buffer(h2, 20, stdout, 0);
+       }
+
+       crypto_hash_close(ch);
+
+       ch =  crypto_hash_open(HASH_SHA_1);
+       if (!ch) {
+               free(sdad);
+               return 1;
+       }
+
+       crypto_hash_write(ch, c_crm1, sizeof(c_crm1));
+       crypto_hash_write(ch, c_dd2, sizeof(c_dd2));
+
+       h = crypto_hash_read(ch);
+       if (!h) {
+               crypto_hash_close(ch);
+               free(sdad);
+               return 1;
+       }
+
+       if (verbose) {
+               printf("crypto hash2.1:\n");
+               dump_buffer(h, 20, stdout, 0);
+       }
+
+       if (memcmp(sdad + 5 + 8 + 1 + 8, h, 20)) {
+               crypto_hash_close(ch);
+               free(sdad);
+               return 1;
+       }
+
+       crypto_hash_close(ch);
+
+       free(sdad);
+
+       return 0;
+}
+
+static int cda_test_pk(bool verbose)
+{
+       const struct emv_pk *pk = &c_mchip_05;
+       struct tlvdb *db;
+
+       db = tlvdb_external(0x90, sizeof(c_issuer_cert), c_issuer_cert);
+       tlvdb_add(db, tlvdb_external(0x9f32, sizeof(c_issuer_exp), c_issuer_exp));
+       tlvdb_add(db, tlvdb_external(0x92, sizeof(c_issuer_rem), c_issuer_rem));
+       tlvdb_add(db, tlvdb_external(0x5a, sizeof(c_pan), c_pan));
+
+       struct emv_pk *ipk = emv_pki_recover_issuer_cert(pk, db);
+       if (!ipk) {
+               fprintf(stderr, "Could not recover Issuer certificate!\n");
+               tlvdb_free(db);
+               return 2;
+       }
+
+       tlvdb_add(db, tlvdb_external(0x9f46, sizeof(c_icc_cert), c_icc_cert));
+       tlvdb_add(db, tlvdb_external(0x9f47, sizeof(c_icc_exp), c_icc_exp));
+       /*tlvdb_add(db, tlvdb_external(0x9f48, sizeof(issuer_rem), issuer_rem));*/
+
+       struct emv_pk *iccpk = emv_pki_recover_icc_cert(ipk, db, &ssd1_tlv);
+       if (!iccpk) {
+               fprintf(stderr, "Could not recover ICC certificate!\n");
+               emv_pk_free(ipk);
+               tlvdb_free(db);
+               return 2;
+       }
+
+       tlvdb_add(db, tlvdb_fixed(0x9f37, sizeof(c_dd1), c_dd1));
+
+       struct tlvdb *cda_db;
+       cda_db = tlvdb_fixed(0x9f27, 1, (unsigned char[]){ 0x40 });
+       tlvdb_add(cda_db, tlvdb_fixed(0x9f36, 2, (unsigned char[]) { 0x00, 0x10 }));
+       tlvdb_add(cda_db, tlvdb_external(0x9f4b, sizeof(c_sdad_cr), c_sdad_cr));
+       tlvdb_add(cda_db, tlvdb_fixed(0x9f10, 0x12,
+                       (unsigned char[]) { 0x00, 0x10, 0x90, 0x40, 0x01, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}));
+
+       struct tlvdb *idndb = emv_pki_perform_cda(iccpk, db, cda_db,
+                       NULL,
+                       &crm1_tlv,
+                       NULL);
+       if (!idndb) {
+               fprintf(stderr, "Could not recover IDN!\n");
+               tlvdb_free(cda_db);
+               emv_pk_free(iccpk);
+               emv_pk_free(ipk);
+               tlvdb_free(db);
+               return 2;
+       }
+
+       const struct tlv *idn = tlvdb_get(idndb, 0x9f4c, NULL);
+       if (!idn) {
+               fprintf(stderr, "IDN not found!\n");
+               tlvdb_free(idndb);
+               tlvdb_free(cda_db);
+               emv_pk_free(iccpk);
+               emv_pk_free(ipk);
+               tlvdb_free(db);
+               return 2;
+       }
+
+       if (verbose) {
+               printf("IDN:\n");
+               dump_buffer(idn->value, idn->len, stdout, 0);
+       }
+
+       tlvdb_free(idndb);
+       tlvdb_free(cda_db);
+       emv_pk_free(iccpk);
+       emv_pk_free(ipk);
+       tlvdb_free(db);
+
+       return 0;
+}
+
+int exec_cda_test(bool verbose)
+{
+       int ret;
+       fprintf(stdout, "\n");
+       
+       ret = cda_test_raw(verbose);
+       if (ret) {
+               fprintf(stderr, "CDA raw test: failed\n");
+               return ret;
+       }
+       fprintf(stdout, "CDA raw test: passed\n");
+
+       ret = cda_test_pk(verbose);
+       if (ret) {
+               fprintf(stderr, "CDA test pk: failed\n");
+               return ret;
+       }
+       fprintf(stdout, "CDA test pk: passed\n");
+
+       return 0;
+}
diff --git a/client/emv/test/cda_test.h b/client/emv/test/cda_test.h
new file mode 100644 (file)
index 0000000..1d321a2
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * emv-tools - a set of tools to work with EMV family of smart cards
+ * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ */
+
+#include <stdbool.h> 
+extern int exec_cda_test(bool verbose);
diff --git a/client/emv/test/crypto_test.c b/client/emv/test/crypto_test.c
new file mode 100644 (file)
index 0000000..ff18b9d
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * emv-tools - a set of tools to work with EMV family of smart cards
+ * Copyright (C) 2015 Dmitry Eremin-Solenikov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "../crypto.h"
+#include "../dump.h"
+#include "util_posix.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+static int test_genkey(unsigned int keylength, unsigned char *msg, size_t msg_len, bool verbose)
+{
+       int ret = 1;
+       size_t tmp_len, tmp2_len;
+       unsigned char *tmp, *tmp2;
+       struct crypto_pk *pk;
+
+       printf("Testing key length %u ", keylength);
+       uint64_t ms = msclock();
+
+       pk = crypto_pk_genkey(PK_RSA, 1, keylength, 3);
+       if (!pk) {
+               fprintf(stderr, "ERROR: key generation error.\n");
+               goto out;
+       }
+
+       tmp_len = crypto_pk_get_nbits(pk);
+       if (tmp_len != keylength) {
+               fprintf(stderr, "ERROR: crypto_pk_get_nbits.\n");
+               goto close;
+       }
+
+       tmp = crypto_pk_decrypt(pk, msg, msg_len, &tmp_len);
+       if (!tmp) {
+               fprintf(stderr, "ERROR: crypto_pk_decrypt.\n");
+               goto close;
+       }
+
+       tmp2 = crypto_pk_encrypt(pk, tmp, tmp_len, &tmp2_len);
+       if (!tmp2) {
+               fprintf(stderr, "ERROR: crypto_pk_encrypt.\n");
+               goto free_tmp;
+       }
+
+       if (tmp2_len == msg_len && !memcmp(tmp2, msg, tmp2_len)) {
+               ret = 0;
+       } else {
+               fprintf(stderr, "ERROR: encrypt-decrypt sequence length or data error.\n");
+       }
+
+       free(tmp2);
+       printf("passed. (%"PRIu64" ms) \n", msclock() - ms);
+free_tmp:
+       free(tmp);
+close:
+       crypto_pk_close(pk);
+
+out:
+       return ret;
+}
+
+static unsigned char message[4096 / 8] =
+       "aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb"
+       "ccccccccccccccccdddddddddddddddd"
+       "eeeeeeeeeeeeeeeeffffffffffffffff"
+       "gggggggggggggggghhhhhhhhhhhhhhhh"
+       "iiiiiiiiiiiiiiiijjjjjjjjjjjjjjjj"
+       "kkkkkkkkkkkkkkkkllllllllllllllll"
+       "mmmmmmmmmmmmmmmmnnnnnnnnnnnnnnnn"
+       "oooooooooooooooopppppppppppppppp"
+       "qqqqqqqqqqqqqqqqrrrrrrrrrrrrrrrr"
+       "sssssssssssssssstttttttttttttttt"
+       "uuuuuuuuuuuuuuuuvvvvvvvvvvvvvvvv"
+       "wwwwwwwwwwwwwwwwxxxxxxxxxxxxxxxx"
+       "yyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzz"
+       "aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb"
+       "ccccccccccccccccdddddddddddddddd"
+       "eeeeeeeeeeeeeeeeffffffffffffffff"
+       ;
+
+static unsigned char pk_N[] = {
+       0xdb, 0x12, 0xe4, 0xf1, 0x8d, 0x43, 0x74, 0xf0, 0xec, 0x38, 0xdc, 0xfb, 0xf9, 0x20, 0x75, 0x6d,
+       0x05, 0xf4, 0x36, 0xc2, 0x21, 0xac, 0x34, 0x0d, 0x16, 0xc5, 0x23, 0xcb, 0xfc, 0x9a, 0x8a, 0xd1,
+       0xe0, 0xbd, 0xda, 0xe5, 0x77, 0xd5, 0xaf, 0x65, 0x8d, 0x6b, 0x62, 0x5c, 0xcd, 0x89, 0x06, 0x8d,
+       0x11, 0x19, 0x6b, 0x0e, 0x3e, 0xe2, 0x80, 0x45, 0xf6, 0x44, 0x55, 0x21, 0x9c, 0x86, 0x90, 0x00,
+       0xa8, 0xaf, 0x8c, 0x94, 0xde, 0x3f, 0xe8, 0x56, 0x52, 0xfe, 0xee, 0xa5, 0x36, 0x72, 0x07, 0xf2,
+       0xcf, 0x8e, 0xf0, 0xbd, 0xff, 0x36, 0xd5, 0xf2, 0xad, 0x74, 0x1d, 0x17, 0xd0, 0xb7, 0x93, 0xe2,
+       0x2c, 0x8d, 0x3f, 0xb6, 0x7c, 0x65, 0x19, 0x9f, 0xa7, 0x80, 0x1f, 0x9f, 0xe5, 0x2f, 0x2d, 0x75,
+       0xc9, 0xc2, 0xe9, 0x70, 0xfa, 0x1e, 0x5a, 0xc6, 0xa3, 0x82, 0xd1, 0x29, 0x5a, 0x60, 0xce, 0x1f,
+       0x40, 0x2e, 0xfc, 0x2a, 0x5e, 0xde, 0xc9, 0x67, 0xfc, 0x45, 0x18, 0xce, 0xf2, 0x83, 0x94, 0x53,
+       0xd6, 0x4f, 0x2e, 0xc5, 0x2d, 0xa1, 0xa5, 0x7a, 0x63, 0x26, 0x70, 0xcb, 0x76, 0xfc, 0xb5, 0x8d,
+       0x0f, 0x88, 0x4c, 0x07, 0xba, 0xfa, 0x8b, 0xbc, 0xa0, 0xea, 0xea, 0x0a, 0xe6, 0xa5, 0x44, 0xa5,
+       0x0d, 0x12, 0x66, 0x2b, 0xf7, 0xc4, 0x76, 0xa3, 0x82, 0xa6, 0x2b, 0xb2, 0x5a, 0x27, 0xcd, 0x11,
+       0xd2, 0x9d, 0x42, 0x86, 0x8c, 0x82, 0xc8, 0xe1, 0xff, 0x7d, 0xf1, 0xd9, 0xd9, 0xa1, 0xf3, 0x3d,
+       0xc3, 0x12, 0x4e, 0x47, 0xc8, 0xa2, 0xcd, 0x72, 0x5a, 0x18, 0xea, 0x89, 0x5c, 0x73, 0x28, 0x52,
+       0xf8, 0xdb, 0x70, 0xdc, 0x92, 0xc9, 0xb7, 0x98, 0x10, 0x94, 0x79, 0xdc, 0x9e, 0x12, 0x6c, 0x14,
+       0x78, 0xf9, 0x5a, 0xad, 0x00, 0x98, 0xc8, 0x17, 0x79, 0x8a, 0xed, 0xe7, 0xc3, 0xd3, 0xa7, 0x8b,
+};
+
+static unsigned char pk_E[] = {
+       0x01, 0x00, 0x01,
+};
+
+static unsigned char pk_D[] = {
+       0x01, 0x17, 0xd4, 0x0a, 0x9c, 0x80, 0xd4, 0xa9, 0x8b, 0x14, 0x31, 0x8e, 0x14, 0x4d, 0x24, 0x28,
+       0xda, 0x19, 0xc0, 0xd8, 0x31, 0x20, 0xd1, 0xd5, 0xaa, 0xe2, 0x6a, 0xee, 0x4e, 0xa1, 0x5a, 0xc5,
+       0xf7, 0x50, 0x1b, 0x32, 0x7f, 0xe9, 0x92, 0x09, 0x78, 0xae, 0x2b, 0x7c, 0x79, 0x0e, 0x10, 0xf9,
+       0x4d, 0x37, 0x8a, 0x40, 0x34, 0xf2, 0x1e, 0x5f, 0xba, 0xfd, 0xd6, 0x4a, 0xe7, 0xa4, 0x08, 0x3d,
+       0xe8, 0x99, 0x8f, 0xa3, 0x02, 0x84, 0xe1, 0x1c, 0xe5, 0x27, 0x1e, 0x7b, 0xb6, 0x8c, 0xd5, 0x1b,
+       0x52, 0x0b, 0xcd, 0x89, 0xb5, 0x27, 0x49, 0xe3, 0xff, 0x17, 0x90, 0x39, 0x99, 0x32, 0x01, 0x4b,
+       0xe4, 0x9b, 0x03, 0xd1, 0x5e, 0x47, 0x86, 0xdc, 0x34, 0x12, 0xc0, 0x95, 0xa4, 0xa8, 0x1a, 0x9a,
+       0xf6, 0xd9, 0xc1, 0x1e, 0x6e, 0x31, 0x0e, 0x94, 0xe5, 0x25, 0xf6, 0xf3, 0x34, 0xdf, 0x3c, 0xc8,
+       0x0a, 0xc5, 0x8c, 0x00, 0x5c, 0x59, 0x55, 0x06, 0xd1, 0x39, 0x84, 0x35, 0x96, 0x40, 0xe8, 0xb2,
+       0xf7, 0x13, 0x83, 0x37, 0xe1, 0xe2, 0x79, 0x41, 0x90, 0x2a, 0xc3, 0x71, 0xc5, 0xcf, 0xf0, 0xaa,
+       0x01, 0x2f, 0x48, 0x9c, 0x3f, 0x29, 0x7b, 0xb7, 0x5c, 0xef, 0x25, 0xde, 0x34, 0x23, 0x81, 0x7a,
+       0x4c, 0x3a, 0x9b, 0xe4, 0xa7, 0x44, 0x73, 0xbf, 0xf7, 0x39, 0x43, 0xa4, 0x39, 0xa0, 0x1b, 0xf7,
+       0x4f, 0x5f, 0x14, 0x49, 0x32, 0x0e, 0x66, 0xd0, 0x29, 0xb5, 0x80, 0xe0, 0xba, 0x3b, 0x88, 0x2b,
+       0x14, 0xa4, 0x26, 0x00, 0x2f, 0x50, 0x20, 0x4e, 0xfa, 0xc2, 0x44, 0x72, 0x72, 0x6c, 0x2a, 0x77,
+       0x85, 0x20, 0xe0, 0x1d, 0x95, 0x6a, 0x66, 0xe7, 0xb8, 0xca, 0x5b, 0xc9, 0xc3, 0xf3, 0x39, 0xef,
+       0xd7, 0xd5, 0x45, 0xb6, 0x3e, 0x19, 0xea, 0x7c, 0x56, 0x20, 0x1b, 0x95, 0x86, 0x2e, 0xc7, 0x51,
+};
+
+static unsigned char pk_P[] = {
+       0xf5, 0x93, 0x0f, 0x76, 0x00, 0xab, 0x37, 0x01, 0xb9, 0x52, 0xb6, 0x82, 0xf9, 0xf5, 0xae, 0x29,
+       0x8f, 0xd5, 0x08, 0xbc, 0xf7, 0x9f, 0x84, 0xb6, 0x4c, 0x94, 0xb5, 0xfc, 0xfe, 0xe1, 0xcd, 0x6a,
+       0xf4, 0x9c, 0xa7, 0x33, 0xdb, 0xd8, 0xc8, 0xc1, 0xc0, 0x8d, 0x65, 0xed, 0x29, 0x99, 0x6c, 0x5c,
+       0xbe, 0x08, 0xac, 0x04, 0xe4, 0x3a, 0x18, 0xe2, 0x0f, 0x70, 0x26, 0x70, 0x9b, 0x71, 0xfc, 0x9f,
+       0x22, 0xea, 0x90, 0x3b, 0xc2, 0xa5, 0x16, 0x7a, 0xcd, 0x04, 0x3e, 0xa6, 0x37, 0x49, 0xa7, 0xee,
+       0xaa, 0xe4, 0x9d, 0xaa, 0x9b, 0xb0, 0xe2, 0x6a, 0x9d, 0x1e, 0xcd, 0x83, 0x4e, 0xd8, 0x59, 0x6d,
+       0x03, 0xd5, 0x4c, 0x5e, 0xc5, 0x22, 0x10, 0xb7, 0xcc, 0x0c, 0x90, 0x76, 0x05, 0x21, 0xe7, 0x77,
+       0x5c, 0x88, 0x5f, 0xd0, 0x5f, 0x9e, 0x2e, 0x49, 0x56, 0xf4, 0x2b, 0xa9, 0x99, 0x57, 0x74, 0x19,
+};
+
+static unsigned char pk_Q[] = {
+       0xe4, 0x5f, 0xd2, 0x28, 0xbd, 0xf3, 0xdd, 0x70, 0x3d, 0xfd, 0x01, 0x23, 0xae, 0x93, 0x6a, 0x91,
+       0xca, 0x68, 0xb1, 0xdb, 0x81, 0xab, 0x1e, 0x63, 0x76, 0x9b, 0x6d, 0xaa, 0x41, 0x87, 0x5a, 0x79,
+       0xe7, 0xce, 0xd6, 0x84, 0x32, 0x53, 0xf5, 0xfc, 0xb7, 0x41, 0x7c, 0xcb, 0x88, 0x09, 0xcb, 0xe9,
+       0x07, 0x16, 0x28, 0x55, 0x23, 0xe5, 0xf2, 0xf5, 0x23, 0xf5, 0xee, 0x2b, 0x9d, 0x91, 0x56, 0xc6,
+       0x30, 0x91, 0x4d, 0x16, 0x11, 0x6c, 0x48, 0x45, 0xe8, 0x5d, 0x0e, 0x9e, 0x04, 0xc8, 0xb6, 0xdd,
+       0xba, 0x0d, 0xdf, 0x53, 0x56, 0xfa, 0x0b, 0x0b, 0x99, 0x8d, 0xea, 0x5c, 0x45, 0x7d, 0xed, 0xad,
+       0x1f, 0xc5, 0xc1, 0x7d, 0x63, 0x31, 0xf8, 0x32, 0xb5, 0x33, 0xb0, 0xef, 0xce, 0x2e, 0x74, 0x1e,
+       0x77, 0x2a, 0x18, 0x35, 0x3d, 0x6e, 0x01, 0xba, 0xde, 0x21, 0x8e, 0x14, 0x12, 0xc3, 0x0d, 0x43,
+};
+
+static unsigned char pk_dP[] = {
+       0x5a, 0xc8, 0xf7, 0x1a, 0x44, 0xbd, 0x07, 0x24, 0xd8, 0x02, 0x3f, 0xfe, 0xc3, 0xb1, 0x93, 0xa5,
+       0x41, 0xcb, 0x1b, 0xe3, 0xe0, 0x17, 0x54, 0xd4, 0xa0, 0x13, 0x0a, 0x04, 0x71, 0xa5, 0xc0, 0x6f,
+       0x1d, 0xe7, 0x1b, 0xd9, 0x0c, 0x19, 0x64, 0x7e, 0x5c, 0x54, 0xe9, 0xad, 0x77, 0x87, 0x84, 0x8b,
+       0xf4, 0xa4, 0xf8, 0x13, 0x06, 0xdc, 0x83, 0x7e, 0x6e, 0xfe, 0xa2, 0xf7, 0x56, 0x40, 0x19, 0x88,
+       0x2b, 0x3c, 0x53, 0xfe, 0x03, 0xc3, 0x4c, 0x40, 0x31, 0xb2, 0xb4, 0x06, 0x76, 0xc2, 0x00, 0x17,
+       0x37, 0x8e, 0x34, 0xcb, 0x71, 0xab, 0x3e, 0xc8, 0xf3, 0x35, 0x03, 0xfc, 0xdb, 0x15, 0x18, 0x5a,
+       0x38, 0xe4, 0x8d, 0xcb, 0x2b, 0x4d, 0xa0, 0xa8, 0x92, 0x02, 0xc3, 0x15, 0x1e, 0x68, 0x9e, 0x4d,
+       0x7e, 0x23, 0xdc, 0x68, 0x08, 0x31, 0x4e, 0x23, 0x46, 0xc6, 0x15, 0xae, 0x29, 0x46, 0x2f, 0x61,
+};
+
+static unsigned char pk_dQ[] = {
+       0x33, 0x61, 0x9f, 0xae, 0x0c, 0xf6, 0xc6, 0x16, 0x8f, 0xcb, 0xd1, 0xaa, 0xce, 0x87, 0x5a, 0x4d,
+       0xcc, 0xe5, 0x7b, 0x46, 0xb0, 0xc8, 0xe8, 0x40, 0x66, 0x9a, 0x17, 0xb5, 0x5b, 0xa2, 0xf1, 0x67,
+       0x46, 0x11, 0x52, 0x50, 0x51, 0xe6, 0x74, 0x0c, 0xd4, 0xca, 0x46, 0x22, 0xa0, 0xcb, 0xdb, 0x75,
+       0xe5, 0x63, 0x45, 0xd5, 0xca, 0x0a, 0xdd, 0x7b, 0xec, 0x08, 0x53, 0xfa, 0xba, 0x2b, 0xce, 0x03,
+       0x2f, 0x40, 0x31, 0xc0, 0xca, 0x50, 0xbb, 0x7e, 0x07, 0x06, 0x90, 0xd8, 0x5a, 0xa9, 0x32, 0x03,
+       0x76, 0xed, 0xd2, 0x16, 0x35, 0x16, 0x72, 0xcf, 0xbc, 0x4f, 0xa2, 0xaf, 0xf9, 0xee, 0x98, 0x40,
+       0x00, 0x4b, 0x04, 0xfa, 0x8a, 0x0b, 0xdf, 0x14, 0xc1, 0x92, 0x0c, 0xb8, 0x17, 0x82, 0x7a, 0x1b,
+       0xb4, 0xa1, 0xe2, 0xea, 0x6f, 0x94, 0xc5, 0x8c, 0xde, 0x97, 0x5c, 0x19, 0x06, 0x13, 0x9e, 0x73,
+};
+
+static unsigned char pk_I[] = {
+       0x75, 0x40, 0xc6, 0x02, 0x7e, 0x4f, 0xad, 0xdb, 0x95, 0xac, 0x07, 0x8d, 0x80, 0xb6, 0x80, 0x02,
+       0x06, 0xdd, 0xb8, 0x5d, 0x92, 0x65, 0x69, 0x26, 0x86, 0x61, 0x6c, 0x87, 0x4e, 0xe5, 0x03, 0x68,
+       0xc6, 0x10, 0x15, 0x8c, 0x43, 0x3a, 0x45, 0x63, 0x48, 0xb7, 0x8a, 0x8c, 0xa2, 0x2b, 0x34, 0xb6,
+       0x83, 0xfe, 0xa8, 0x10, 0xa5, 0x74, 0xa5, 0xa9, 0x52, 0x42, 0x1f, 0xa0, 0x80, 0x6a, 0xc5, 0x35,
+       0xe8, 0xb8, 0xc2, 0xa0, 0x3f, 0x49, 0x18, 0xcf, 0x0e, 0x54, 0x3c, 0x70, 0x11, 0x11, 0xd3, 0x85,
+       0x8c, 0xb2, 0xe5, 0x74, 0xdf, 0x98, 0xea, 0x6c, 0xc0, 0x5f, 0x7f, 0xff, 0x69, 0xbf, 0x08, 0x8d,
+       0x1b, 0xc4, 0x90, 0xcc, 0xa4, 0xcd, 0xcc, 0x34, 0x58, 0xe5, 0x91, 0x53, 0x3a, 0xd5, 0x39, 0xf4,
+       0xd4, 0x42, 0xc9, 0x17, 0xb2, 0x2c, 0x92, 0x12, 0x37, 0x1b, 0xd3, 0xc5, 0x79, 0xd2, 0x65, 0x61,
+};
+
+static int test_pk(bool verbose)
+{
+       int ret = 1;
+       size_t tmp_len, tmp2_len;
+       unsigned char *tmp, *tmp2;
+       struct crypto_pk *pubk, *privk;
+       unsigned char *msg = message;
+       size_t msg_len = sizeof(pk_N);
+
+       printf("Testing public keys interfaces\n");
+
+       pubk = crypto_pk_open(PK_RSA,
+                       pk_N, sizeof(pk_N),
+                       pk_E, sizeof(pk_E));
+       if (!pubk) {
+               fprintf(stderr, "ERROR: open public key.\n");
+               return 1;
+       }
+
+       tmp_len = crypto_pk_get_nbits(pubk);
+       if (tmp_len != sizeof(pk_N) * 8) {
+               fprintf(stderr, "ERROR: crypto_pk_get_nbits mismatch.\n");
+               goto close_pub;
+       }
+
+       tmp = crypto_pk_get_parameter(pubk, 0, &tmp_len);
+       if (tmp_len != sizeof(pk_N) || memcmp(tmp, pk_N, tmp_len)) {
+               fprintf(stderr, "ERROR: crypto_pk_get_parameter(0) Modulus. param len %d len %d\n", tmp_len, sizeof(pk_N));
+               free(tmp);
+               goto close_pub;
+       }
+       free(tmp);
+
+       tmp = crypto_pk_get_parameter(pubk, 1, &tmp_len);
+       if (tmp_len != sizeof(pk_E) || memcmp(tmp, pk_E, tmp_len)) {
+               fprintf(stderr, "ERROR: crypto_pk_get_parameter(1) Exponent.\n");
+               free(tmp);
+               goto close_pub;
+       }
+       free(tmp);
+
+       privk = crypto_pk_open_priv(PK_RSA,
+                       pk_N, sizeof(pk_N),
+                       pk_E, sizeof(pk_E),
+                       pk_D, sizeof(pk_D),
+                       pk_P, sizeof(pk_P),
+                       pk_Q, sizeof(pk_Q),
+                       pk_dP, sizeof(pk_dP),
+                       pk_dQ, sizeof(pk_dQ),
+                       pk_I, sizeof(pk_I));
+       if (!privk) {
+               fprintf(stderr, "ERROR: open private key.\n");
+               goto close_pub;
+       }
+
+
+       tmp_len = crypto_pk_get_nbits(privk);
+       if (tmp_len != sizeof(pk_N) * 8) {
+               fprintf(stderr, "ERROR: crypto_pk_get_nbits mismatch.\n");
+               goto close_pub;
+       }
+
+       tmp = crypto_pk_get_parameter(privk, 0, &tmp_len);
+       if (tmp_len != sizeof(pk_N) || memcmp(tmp, pk_N, tmp_len)) {
+               fprintf(stderr, "ERROR: crypto_pk_get_parameter(0) Modulus. param len %d len %d\n", tmp_len, sizeof(pk_N));
+               free(tmp);
+               goto close;
+       }
+       free(tmp);
+
+       tmp = crypto_pk_get_parameter(privk, 1, &tmp_len);
+       if (tmp_len != sizeof(pk_E) || memcmp(tmp, pk_E, tmp_len)) {
+               fprintf(stderr, "ERROR: crypto_pk_get_parameter(1) Exponent.\n");
+               free(tmp);
+               goto close;
+       }
+       free(tmp);
+
+       tmp = crypto_pk_decrypt(privk, msg, msg_len, &tmp_len);
+       if (!tmp) {
+               fprintf(stderr, "ERROR: crypto_pk_decrypt.\n");
+               goto close;
+       }
+
+       tmp2 = crypto_pk_encrypt(pubk, tmp, tmp_len, &tmp2_len);
+       if (!tmp2) {
+               fprintf(stderr, "ERROR: crypto_pk_encrypt.\n");
+               goto free_tmp;
+       }
+
+       if (tmp2_len == msg_len && !memcmp(tmp2, msg, tmp2_len)) {
+               ret = 0;
+       } else {
+               fprintf(stderr, "ERROR: encrypt-decrypt sequence length or data error.\n");
+       }
+
+       free(tmp2);
+free_tmp:
+       free(tmp);
+
+close:
+       crypto_pk_close(privk);
+close_pub:
+       crypto_pk_close(pubk);
+
+       return ret;
+}
+
+int exec_crypto_test(bool verbose)
+{
+       unsigned int keylengths[] = {1024, 1152, 1408, 1984, 2048, 3072, 4096};
+       int i;
+       int ret;
+       fprintf(stdout, "\n");
+
+       ret = test_pk(verbose);
+       if (ret) {
+               fprintf(stderr, "Crypto raw test: failed\n");
+               return ret;
+       }
+       fprintf(stdout, "Crypto raw test: passed\n\n");
+
+       for (i = 0; i < sizeof(keylengths) / sizeof(keylengths[0]); i++) {
+               unsigned int kl = keylengths[i];
+               ret = test_genkey(kl, message, kl / 8, verbose);
+               if (ret) {
+                       fprintf(stderr, "Crypto generate key[%d] test: failed\n", kl);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
diff --git a/client/emv/test/crypto_test.h b/client/emv/test/crypto_test.h
new file mode 100644 (file)
index 0000000..084bc99
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * emv-tools - a set of tools to work with EMV family of smart cards
+ * Copyright (C) 2015 Dmitry Eremin-Solenikov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ */
+
+ #include <stdbool.h>
+
+extern int exec_crypto_test(bool verbose);
diff --git a/client/emv/test/cryptotest.c b/client/emv/test/cryptotest.c
new file mode 100644 (file)
index 0000000..a6d06e6
--- /dev/null
@@ -0,0 +1,65 @@
+//-----------------------------------------------------------------------------
+// Copyright (C) 2017 Merlok
+//
+// This code is licensed to you under the terms of the GNU GPL, version 2 or,
+// at your option, any later version. See the LICENSE.txt file for the text of
+// the license.
+//-----------------------------------------------------------------------------
+// Crypto algorithms testing
+//-----------------------------------------------------------------------------
+
+#include "cryptotest.h"
+#include "util.h"
+#include "ui.h"
+
+#include "bignum.h"
+#include "aes.h"
+#include "des.h"
+#include "rsa.h"
+#include "sha1.h"
+
+#include "crypto_test.h"
+#include "sda_test.h"
+#include "dda_test.h"
+#include "cda_test.h"
+
+int ExecuteCryptoTests(bool verbose) {
+       int res;
+       bool TestFail = false;
+       
+       res = mpi_self_test(verbose);
+       if (res) TestFail = true;
+       
+       res = aes_self_test(verbose);
+       if (res) TestFail = true;
+       
+       res = des_self_test(verbose);
+       if (res) TestFail = true;
+       
+       res = sha1_self_test(verbose);
+       if (res) TestFail = true;
+       
+       res = rsa_self_test(verbose);
+       if (res) TestFail = true;
+       
+       res = exec_sda_test(verbose);
+       if (res) TestFail = true;
+
+       res = exec_dda_test(verbose);
+       if (res) TestFail = true;
+       
+       res = exec_cda_test(verbose);
+       if (res) TestFail = true;
+
+       res = exec_crypto_test(verbose);
+       if (res) TestFail = true;
+
+       PrintAndLog("\n--------------------------");
+       if (TestFail)
+               PrintAndLog("Test(s) [ERROR].");
+       else
+               PrintAndLog("Tests [OK].");
+       
+       return TestFail;
+}
+
diff --git a/client/emv/test/cryptotest.h b/client/emv/test/cryptotest.h
new file mode 100644 (file)
index 0000000..5c6aaae
--- /dev/null
@@ -0,0 +1,13 @@
+//-----------------------------------------------------------------------------
+// Copyright (C) 2017 Merlok
+//
+// This code is licensed to you under the terms of the GNU GPL, version 2 or,
+// at your option, any later version. See the LICENSE.txt file for the text of
+// the license.
+//-----------------------------------------------------------------------------
+// Crypto algorithms testing
+//-----------------------------------------------------------------------------
+
+#include <stdbool.h>
+
+extern int ExecuteCryptoTests(bool verbose);
diff --git a/client/emv/test/dda_test.c b/client/emv/test/dda_test.c
new file mode 100644 (file)
index 0000000..afdd4e2
--- /dev/null
@@ -0,0 +1,390 @@
+/*
+ * emv-tools - a set of tools to work with EMV family of smart cards
+ * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "dda_test.h"
+
+#include "../emv_pk.h"
+#include "../crypto.h"
+#include "../dump.h"
+#include "../tlv.h"
+#include "../emv_pki.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+struct emv_pk mchip_05 = {
+       .rid = { 0xa0, 0x00, 0x00, 0x00, 0x04, },
+       .index = 5,
+       .hash_algo = HASH_SHA_1,
+       .pk_algo = PK_RSA,
+       .hash = {
+               0xeb, 0xfa, 0x0d, 0x5d,
+               0x06, 0xd8, 0xce, 0x70,
+               0x2d, 0xa3, 0xea, 0xe8,
+               0x90, 0x70, 0x1d, 0x45,
+               0xe2, 0x74, 0xc8, 0x45, },
+       .exp = { 0x03, },
+       .elen = 1,
+       .mlen = 1408 / 8,
+       .modulus = (unsigned char[]){
+               0xb8, 0x04, 0x8a, 0xbc, 0x30, 0xc9, 0x0d, 0x97, 0x63, 0x36, 0x54, 0x3e, 0x3f, 0xd7, 0x09, 0x1c,
+               0x8f, 0xe4, 0x80, 0x0d, 0xf8, 0x20, 0xed, 0x55, 0xe7, 0xe9, 0x48, 0x13, 0xed, 0x00, 0x55, 0x5b,
+               0x57, 0x3f, 0xec, 0xa3, 0xd8, 0x4a, 0xf6, 0x13, 0x1a, 0x65, 0x1d, 0x66, 0xcf, 0xf4, 0x28, 0x4f,
+               0xb1, 0x3b, 0x63, 0x5e, 0xdd, 0x0e, 0xe4, 0x01, 0x76, 0xd8, 0xbf, 0x04, 0xb7, 0xfd, 0x1c, 0x7b,
+               0xac, 0xf9, 0xac, 0x73, 0x27, 0xdf, 0xaa, 0x8a, 0xa7, 0x2d, 0x10, 0xdb, 0x3b, 0x8e, 0x70, 0xb2,
+               0xdd, 0xd8, 0x11, 0xcb, 0x41, 0x96, 0x52, 0x5e, 0xa3, 0x86, 0xac, 0xc3, 0x3c, 0x0d, 0x9d, 0x45,
+               0x75, 0x91, 0x64, 0x69, 0xc4, 0xe4, 0xf5, 0x3e, 0x8e, 0x1c, 0x91, 0x2c, 0xc6, 0x18, 0xcb, 0x22,
+               0xdd, 0xe7, 0xc3, 0x56, 0x8e, 0x90, 0x02, 0x2e, 0x6b, 0xba, 0x77, 0x02, 0x02, 0xe4, 0x52, 0x2a,
+               0x2d, 0xd6, 0x23, 0xd1, 0x80, 0xe2, 0x15, 0xbd, 0x1d, 0x15, 0x07, 0xfe, 0x3d, 0xc9, 0x0c, 0xa3,
+               0x10, 0xd2, 0x7b, 0x3e, 0xfc, 0xcd, 0x8f, 0x83, 0xde, 0x30, 0x52, 0xca, 0xd1, 0xe4, 0x89, 0x38,
+               0xc6, 0x8d, 0x09, 0x5a, 0xac, 0x91, 0xb5, 0xf3, 0x7e, 0x28, 0xbb, 0x49, 0xec, 0x7e, 0xd5, 0x97,
+       },
+};
+
+const unsigned char d_issuer_cert[] = {
+               0x17, 0x14, 0x28, 0x4f, 0x76, 0x3b, 0x85, 0x86, 0xee, 0x6d, 0x31, 0x99, 0x51, 0xf7, 0xe6, 0x3f,
+               0xa2, 0x50, 0x76, 0xe5, 0x0d, 0xc9, 0xd3, 0x20, 0x0b, 0xa9, 0x98, 0xd3, 0xa0, 0x52, 0xad, 0xba,
+               0x9a, 0xb6, 0x9a, 0xc6, 0xad, 0x6a, 0xdd, 0x3c, 0xe0, 0x9f, 0x02, 0x78, 0xf4, 0x07, 0x4e, 0xc4,
+               0xee, 0x9b, 0x1d, 0x22, 0x68, 0xa3, 0xe9, 0x53, 0x57, 0x5e, 0x45, 0x4e, 0x50, 0xcd, 0x86, 0x0b,
+               0xf4, 0x24, 0xc5, 0x1c, 0x59, 0x77, 0x12, 0xd2, 0xaa, 0x05, 0x70, 0x89, 0xdd, 0x86, 0x73, 0xe5,
+               0x1b, 0x1e, 0x1d, 0x71, 0x88, 0x03, 0x48, 0x92, 0x07, 0x7a, 0xc1, 0x8a, 0x6a, 0xe2, 0x34, 0x88,
+               0xbe, 0xa9, 0xdf, 0x3b, 0x1a, 0x83, 0xf2, 0xc0, 0x80, 0x0c, 0xd7, 0xc5, 0xcd, 0xf2, 0xfd, 0xe0,
+               0x49, 0x6f, 0x7b, 0xc3, 0x9f, 0xb4, 0xbf, 0x36, 0x32, 0x99, 0xbf, 0xa6, 0x37, 0xb2, 0xec, 0x33,
+               0xc5, 0x07, 0xe3, 0x68, 0x21, 0xee, 0xc2, 0x07, 0x5f, 0x0e, 0x42, 0x0d, 0x38, 0xa1, 0xc9, 0xf3,
+               0x12, 0x72, 0x61, 0xba, 0x31, 0x6c, 0x98, 0x76, 0x74, 0xfa, 0xdb, 0x20, 0xea, 0x7f, 0xeb, 0x75,
+               0xee, 0x45, 0x5d, 0x12, 0x14, 0x6e, 0xa6, 0xf0, 0x2e, 0x8b, 0x01, 0xec, 0x2f, 0xa7, 0xa1, 0x15,
+};
+
+const unsigned char d_issuer_rem[] = {
+               0x6e, 0x63, 0xb7, 0xbc, 0x70, 0xab, 0xdd, 0x09, 0x34, 0x1b, 0x34, 0xc0, 0x32, 0x86, 0xba, 0x9b,
+               0xd8, 0x3b, 0xa7, 0x93, 0x6c, 0x5b, 0x77, 0x98, 0xfb, 0x22, 0xc5, 0xe5, 0x3f, 0xf2, 0x40, 0xa2,
+               0x6d, 0xbd, 0x64, 0x15,
+};
+
+const unsigned char d_issuer_exp[] = {
+               0x03,
+};
+
+const unsigned char d_icc_cert[] = {
+               0xa4, 0x2f, 0xbe, 0xb1, 0x56, 0xb9, 0x8d, 0xcb, 0x05, 0x54, 0xda, 0x06, 0x2a, 0xdc, 0xa5, 0x30,
+               0x9a, 0x91, 0xf0, 0x4f, 0xa2, 0xc7, 0xbd, 0x71, 0x02, 0xa8, 0xd7, 0x3f, 0x16, 0xa3, 0xcf, 0xad,
+               0xe8, 0xaa, 0xdf, 0x4f, 0x3f, 0xe2, 0xa2, 0x12, 0x5c, 0xcd, 0xd7, 0x7c, 0x6b, 0x9f, 0x78, 0xb5,
+               0xb4, 0x37, 0x1c, 0xe0, 0x80, 0x57, 0x25, 0xb0, 0xf9, 0xc0, 0x27, 0xaf, 0x14, 0x7d, 0x91, 0xe1,
+               0xff, 0xdb, 0x20, 0x1e, 0x9c, 0x17, 0x0c, 0xe7, 0x77, 0x05, 0x3a, 0x17, 0x2a, 0xd5, 0x26, 0xdc,
+               0xaf, 0xd3, 0x38, 0x95, 0xe1, 0xa9, 0x47, 0x30, 0x5c, 0x5b, 0x16, 0x7f, 0x2e, 0x7c, 0x6f, 0x99,
+               0x15, 0x81, 0xa6, 0x52, 0xee, 0x47, 0x31, 0x54, 0x76, 0x0c, 0x2e, 0xd7, 0x74, 0x21, 0x4e, 0x50,
+               0xdf, 0xec, 0xdd, 0x4c, 0xf2, 0x94, 0xc9, 0x74, 0xb8, 0x9e, 0xbc, 0xa2, 0x5b, 0x5a, 0xb3, 0xc0,
+               0xbe, 0xb5, 0x0d, 0xfa, 0xf7, 0x82, 0xaf, 0xde, 0x14, 0x33, 0xd9, 0x0c, 0xa2, 0xa8, 0x9d, 0x65,
+               0x1e, 0x75, 0xd6, 0x7e, 0xbc, 0x7c, 0x3e, 0x36, 0xf5, 0xa1, 0x65, 0xee, 0x61, 0x32, 0x61, 0x29,
+               0x39, 0xc1, 0xec, 0xd3, 0x99, 0xe4, 0x60, 0x74, 0xb9, 0x96, 0xd9, 0x3a, 0x88, 0xe0, 0x1e, 0x0a,
+};
+
+const unsigned char d_icc_exp[] = {
+               0x03,
+};
+
+const unsigned char d_sdad_cr[] = {
+       0x3d, 0x87, 0xf3, 0x10, 0x56, 0x10, 0x2d, 0x25, 0x12, 0xcf, 0xde, 0x30, 0x90, 0x06, 0x27, 0xc1,
+       0x26, 0x3a, 0x76, 0xd1, 0xda, 0xa8, 0x21, 0xf5, 0x08, 0x31, 0xe6, 0x06, 0xc5, 0x45, 0x44, 0xc2,
+       0x58, 0x13, 0x1e, 0xae, 0xbe, 0x87, 0x4d, 0xcb, 0x1a, 0x28, 0xcf, 0x82, 0xd3, 0xff, 0x91, 0x11,
+       0x82, 0x60, 0xbc, 0x91, 0x11, 0x37, 0x11, 0xd3, 0xb2, 0x89, 0xfa, 0x41, 0xbe, 0x69, 0xc7, 0xa7,
+       0xb5, 0xc7, 0x83, 0xe6, 0xf8, 0xf9, 0x7f, 0xce, 0x13, 0xf0, 0x8b, 0x13, 0xfa, 0x44, 0x18, 0x3e,
+       0x37, 0x18, 0xce, 0xbf, 0x0c, 0x41, 0x47, 0x3d, 0x2b, 0x0f, 0xf4, 0xde, 0x44, 0xb6, 0xa0, 0x2d,
+       0x75, 0xad, 0xb6, 0xd4, 0x96, 0x23, 0x93, 0xff, 0xdf, 0x4e, 0x69, 0x02, 0x6c, 0xdf, 0x38, 0xff,
+};
+
+const unsigned char d_ssd1[] = {
+       0x5f, 0x25, 0x03, 0x14, 0x05, 0x01, 0x5f, 0x24, 0x03, 0x15, 0x06, 0x30, 0x5a, 0x08, 0x52, 0x85,
+       0x88, 0x12, 0x54, 0x34, 0x56, 0x53, 0x5f, 0x34, 0x01, 0x01, 0x8e, 0x0c, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x1e, 0x03, 0x1f, 0x03, 0x9f, 0x07, 0x02, 0xff, 0x00, 0x9f, 0x0d, 0x05,
+       0xbc, 0x50, 0xbc, 0x00, 0x00, 0x9f, 0x0e, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9f, 0x0f, 0x05,
+       0xbc, 0x70, 0xbc, 0x98, 0x00, 0x9f, 0x4a, 0x01, 0x82, 0x5f, 0x28, 0x02, 0x06, 0x43, 0x8c, 0x21,
+       0x9f, 0x02, 0x06, 0x9f, 0x03, 0x06, 0x9f, 0x1a, 0x02, 0x95, 0x05, 0x5f, 0x2a, 0x02, 0x9a, 0x03,
+       0x9c, 0x01, 0x9f, 0x37, 0x04, 0x9f, 0x35, 0x01, 0x9f, 0x45, 0x02, 0x9f, 0x4c, 0x08, 0x9f, 0x34,
+       0x03, 0x8d, 0x0c, 0x91, 0x0a, 0x8a, 0x02, 0x95, 0x05, 0x9f, 0x37, 0x04, 0x9f, 0x4c, 0x08,
+       0x39, 0x00,
+};
+static const struct tlv ssd1_tlv = {
+       .len = sizeof(d_ssd1),
+       .value = d_ssd1,
+};
+
+const unsigned char d_pan[] = {
+       0x52, 0x85, 0x88, 0x12, 0x54, 0x34, 0x56, 0x53,
+};
+
+const unsigned char d_dd1[] = {
+       0x00, 0x00, 0x00, 0x00,
+};
+static const struct tlv dd1_tlv = {
+       .len = sizeof(d_dd1),
+       .value = d_dd1,
+};
+
+static int dda_test_raw(bool verbose)
+{
+       const struct emv_pk *pk = &mchip_05;
+
+       struct crypto_pk *kcp = crypto_pk_open(PK_RSA,
+                       pk->modulus, pk->mlen,
+                       pk->exp, pk->elen);
+       if (!kcp)
+               return 1;
+
+       unsigned char *ipk_data;
+       size_t ipk_data_len;
+       ipk_data = crypto_pk_encrypt(kcp, d_issuer_cert, sizeof(d_issuer_cert), &ipk_data_len);
+       crypto_pk_close(kcp);
+
+       if (!ipk_data)
+               return 1;
+
+       if (verbose) {
+               printf("issuer cert:\n");
+               dump_buffer(ipk_data, ipk_data_len, stdout, 0);
+       }
+
+       size_t ipk_pk_len = ipk_data[13];
+       unsigned char *ipk_pk = malloc(ipk_pk_len);
+       memcpy(ipk_pk, ipk_data + 15, ipk_data_len - 36);
+       memcpy(ipk_pk + ipk_data_len - 36, d_issuer_rem, sizeof(d_issuer_rem));
+
+       struct crypto_hash *ch;
+       ch = crypto_hash_open(HASH_SHA_1);
+       if (!ch) {
+               free(ipk_pk);
+               free(ipk_data);
+               return 1;
+       }
+
+       crypto_hash_write(ch, ipk_data + 1, 14);
+       crypto_hash_write(ch, ipk_pk, ipk_pk_len);
+       crypto_hash_write(ch, d_issuer_exp, sizeof(d_issuer_exp));
+
+       unsigned char *h = crypto_hash_read(ch);
+       if (!h) {
+               crypto_hash_close(ch);
+               free(ipk_pk);
+               free(ipk_data);
+               return 1;
+       }
+
+       if (verbose) {
+               printf("crypto hash:\n");
+               dump_buffer(h, 20, stdout, 0);
+       }
+
+       if (memcmp(ipk_data + ipk_data_len - 21, h, 20)) {
+               crypto_hash_close(ch);
+               free(ipk_pk);
+               free(ipk_data);
+               return 1;
+       }
+
+       crypto_hash_close(ch);
+       free(ipk_data);
+
+       struct crypto_pk *ikcp = crypto_pk_open(PK_RSA, ipk_pk, (int) ipk_pk_len,
+                       d_issuer_exp, (int) sizeof(d_issuer_exp));
+       free(ipk_pk);
+       if (!ikcp)
+               return 1;
+
+       unsigned char *iccpk_data;
+       size_t iccpk_data_len;
+       iccpk_data = crypto_pk_encrypt(ikcp, d_icc_cert, sizeof(d_icc_cert), &iccpk_data_len);
+       crypto_pk_close(ikcp);
+
+       if (!iccpk_data)
+               return 1;
+
+       if (verbose) {
+               printf("icc cert:\n");
+               dump_buffer(iccpk_data, iccpk_data_len, stdout, 0);
+       }
+
+       size_t iccpk_pk_len = iccpk_data[19];
+       unsigned char *iccpk_pk = malloc(iccpk_pk_len);
+       memcpy(iccpk_pk, iccpk_data + 21, /*iccpk_data_len - 36*/iccpk_pk_len);
+       /*memcpy(iccpk_pk + iccpk_data_len - 36, icc_rem, sizeof(icc_rem));*/
+
+       ch = crypto_hash_open(HASH_SHA_1);
+       if (!ch) {
+               free(iccpk_pk);
+               free(iccpk_data);
+               return 1;
+       }
+
+       crypto_hash_write(ch, iccpk_data + 1, iccpk_data_len - 22);
+       crypto_hash_write(ch, d_icc_exp, sizeof(d_icc_exp));
+       crypto_hash_write(ch, d_ssd1, sizeof(d_ssd1));
+
+       h = crypto_hash_read(ch);
+       if (!h) {
+               crypto_hash_close(ch);
+               free(iccpk_pk);
+               free(iccpk_data);
+               return 1;
+       }
+
+       if (verbose) {
+               printf("crypto hash1.1:\n");
+               dump_buffer(h, 20, stdout, 0);
+       }
+
+       if (memcmp(iccpk_data + iccpk_data_len - 21, h, 20)) {
+               crypto_hash_close(ch);
+               free(iccpk_pk);
+               free(iccpk_data);
+               return 1;
+       }
+
+       crypto_hash_close(ch);
+       free(iccpk_data);
+
+       struct crypto_pk *icckcp = crypto_pk_open(PK_RSA, iccpk_pk, (int) iccpk_pk_len,
+                       d_issuer_exp, (int) sizeof(d_issuer_exp));
+       free(iccpk_pk);
+       if (!icckcp)
+               return 1;
+
+       size_t sdad_len;
+       unsigned char *sdad = crypto_pk_encrypt(icckcp, d_sdad_cr, sizeof(d_sdad_cr), &sdad_len);
+       crypto_pk_close(icckcp);
+       if (!sdad)
+               return 1;
+
+       if (verbose) {
+               printf("sdad:\n");
+               dump_buffer(sdad, sdad_len, stdout, 0);
+       }
+
+       ch = crypto_hash_open(HASH_SHA_1);
+       if (!ch) {
+               free(sdad);
+               return 1;
+       }
+
+       crypto_hash_write(ch, sdad + 1, sdad_len - 22);
+       crypto_hash_write(ch, d_dd1, sizeof(d_dd1));
+
+       unsigned char *h2 = crypto_hash_read(ch);
+       if (!h2) {
+               crypto_hash_close(ch);
+               free(sdad);
+               return 1;
+       }
+
+       if (verbose) {
+               printf("crypto hash2:\n");
+               dump_buffer(h2, 20, stdout, 0);
+       }
+
+       crypto_hash_close(ch);
+
+       free(sdad);
+
+       return 0;
+}
+
+static int dda_test_pk(bool verbose)
+{
+       const struct emv_pk *pk = &mchip_05;
+       struct tlvdb *db;
+
+       db = tlvdb_external(0x90, sizeof(d_issuer_cert), d_issuer_cert);
+       tlvdb_add(db, tlvdb_external(0x9f32, sizeof(d_issuer_exp), d_issuer_exp));
+       tlvdb_add(db, tlvdb_external(0x92, sizeof(d_issuer_rem), d_issuer_rem));
+       tlvdb_add(db, tlvdb_external(0x5a, sizeof(d_pan), d_pan));
+
+       struct emv_pk *ipk = emv_pki_recover_issuer_cert(pk, db);
+       if (!ipk) {
+               fprintf(stderr, "Could not recover Issuer certificate!\n");
+               tlvdb_free(db);
+               return 2;
+       }
+
+       tlvdb_add(db, tlvdb_external(0x9f46, sizeof(d_icc_cert), d_icc_cert));
+       tlvdb_add(db, tlvdb_external(0x9f47, sizeof(d_icc_exp), d_icc_exp));
+       /*tlvdb_add(db, tlvdb_external(0x9f48, sizeof(d_issuer_rem), d_issuer_rem));*/
+
+       struct emv_pk *iccpk = emv_pki_recover_icc_cert(ipk, db, &ssd1_tlv);
+       if (!iccpk) {
+               fprintf(stderr, "Could not recover ICC certificate!\n");
+               emv_pk_free(ipk);
+               tlvdb_free(db);
+               return 2;
+       }
+
+       tlvdb_add(db, tlvdb_external(0x9f4b, sizeof(d_sdad_cr), d_sdad_cr));
+
+       struct tlvdb *idndb = emv_pki_recover_idn(iccpk, db, &dd1_tlv);
+       if (!idndb) {
+               fprintf(stderr, "Could not recover IDN!\n");
+               emv_pk_free(iccpk);
+               emv_pk_free(ipk);
+               tlvdb_free(db);
+               return 2;
+       }
+
+       const struct tlv *idn = tlvdb_get(idndb, 0x9f4c, NULL);
+       if (!idn) {
+               fprintf(stderr, "IDN not found!\n");
+               tlvdb_free(idndb);
+               emv_pk_free(iccpk);
+               emv_pk_free(ipk);
+               tlvdb_free(db);
+               return 2;
+       }
+
+       if (verbose) {
+               printf("IDN:\n");
+               dump_buffer(idn->value, idn->len, stdout, 0);
+       }
+
+       tlvdb_free(idndb);
+       emv_pk_free(iccpk);
+       emv_pk_free(ipk);
+       tlvdb_free(db);
+
+       return 0;
+}
+
+int exec_dda_test(bool verbose)
+{
+       int ret;
+       fprintf(stdout, "\n");
+
+       ret = dda_test_raw(verbose);
+       if (ret) {
+               fprintf(stderr, "DDA raw test: failed\n");
+               return ret;
+       }
+       fprintf(stdout, "DDA raw test: passed\n");
+
+       ret = dda_test_pk(verbose);
+       if (ret) {
+               fprintf(stderr, "DDA test pk: failed\n");
+               return ret;
+       }
+       fprintf(stdout, "DDA test pk: passed\n");
+
+       return 0;
+}
diff --git a/client/emv/test/dda_test.h b/client/emv/test/dda_test.h
new file mode 100644 (file)
index 0000000..354cd7e
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * emv-tools - a set of tools to work with EMV family of smart cards
+ * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ */
+
+#include <stdbool.h> 
+extern int exec_dda_test(bool verbose);
diff --git a/client/emv/test/sda_test.c b/client/emv/test/sda_test.c
new file mode 100644 (file)
index 0000000..2aafcf1
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ * emv-tools - a set of tools to work with EMV family of smart cards
+ * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "../emv_pk.h"
+#include "../crypto.h"
+#include "../dump.h"
+#include "../tlv.h"
+#include "../emv_pki.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+struct emv_pk vsdc_01 = {
+       .rid = { 0xa0, 0x00, 0x00, 0x00, 0x03, },
+       .index = 1,
+       .hash_algo = HASH_SHA_1,
+       .pk_algo = PK_RSA,
+       .hash = {
+               0xd3, 0x4a, 0x6a, 0x77,
+               0x60, 0x11, 0xc7, 0xe7,
+               0xce, 0x3a, 0xec, 0x5f,
+               0x03, 0xad, 0x2f, 0x8c,
+               0xfc, 0x55, 0x03, 0xcc, },
+       .exp = { 0x03, },
+       .elen = 1,
+       .mlen = 1024 / 8,
+       .modulus = (unsigned char[]){
+               0xc6, 0x96, 0x03, 0x42, 0x13, 0xd7, 0xd8, 0x54, 0x69, 0x84, 0x57, 0x9d, 0x1d, 0x0f, 0x0e, 0xa5,
+               0x19, 0xcf, 0xf8, 0xde, 0xff, 0xc4, 0x29, 0x35, 0x4c, 0xf3, 0xa8, 0x71, 0xa6, 0xf7, 0x18, 0x3f,
+               0x12, 0x28, 0xda, 0x5c, 0x74, 0x70, 0xc0, 0x55, 0x38, 0x71, 0x00, 0xcb, 0x93, 0x5a, 0x71, 0x2c,
+               0x4e, 0x28, 0x64, 0xdf, 0x5d, 0x64, 0xba, 0x93, 0xfe, 0x7e, 0x63, 0xe7, 0x1f, 0x25, 0xb1, 0xe5,
+               0xf5, 0x29, 0x85, 0x75, 0xeb, 0xe1, 0xc6, 0x3a, 0xa6, 0x17, 0x70, 0x69, 0x17, 0x91, 0x1d, 0xc2,
+               0xa7, 0x5a, 0xc2, 0x8b, 0x25, 0x1c, 0x7e, 0xf4, 0x0f, 0x23, 0x65, 0x91, 0x24, 0x90, 0xb9, 0x39,
+               0xbc, 0xa2, 0x12, 0x4a, 0x30, 0xa2, 0x8f, 0x54, 0x40, 0x2c, 0x34, 0xae, 0xca, 0x33, 0x1a, 0xb6,
+               0x7e, 0x1e, 0x79, 0xb2, 0x85, 0xdd, 0x57, 0x71, 0xb5, 0xd9, 0xff, 0x79, 0xea, 0x63, 0x0b, 0x75,
+       },
+};
+
+const unsigned char issuer_cert[] = {
+               0x3c, 0x5f, 0xea, 0xd4, 0xdd, 0x7b, 0xca, 0x44, 0xf9, 0x3e, 0x90, 0xc4, 0x4f, 0x76, 0xed, 0xe5,
+               0x4a, 0x32, 0x88, 0xec, 0xdc, 0x78, 0x46, 0x9f, 0xcb, 0x12, 0x25, 0xc0, 0x3b, 0x2c, 0x04, 0xf2,
+               0xc2, 0xf4, 0x12, 0x28, 0x1a, 0x08, 0x22, 0xdf, 0x14, 0x64, 0x92, 0x30, 0x98, 0x9f, 0xb1, 0x49,
+               0x40, 0x70, 0xda, 0xf8, 0xc9, 0x53, 0x4a, 0x78, 0x81, 0x96, 0x01, 0x48, 0x61, 0x6a, 0xce, 0x58,
+               0x17, 0x88, 0x12, 0x0d, 0x35, 0x06, 0xac, 0xe4, 0xce, 0xe5, 0x64, 0xfb, 0x27, 0xee, 0x53, 0x34,
+               0x1c, 0x22, 0xf0, 0xb4, 0x5b, 0x31, 0x87, 0x3d, 0x05, 0xde, 0x54, 0x5e, 0xfe, 0x33, 0xbc, 0xd2,
+               0x9b, 0x21, 0x85, 0xd0, 0x35, 0xa8, 0x06, 0xad, 0x08, 0xc6, 0x97, 0x6f, 0x35, 0x05, 0xa1, 0x99,
+               0x99, 0x93, 0x0c, 0xa8, 0xa0, 0x3e, 0xfa, 0x32, 0x1c, 0x48, 0x60, 0x61, 0xf7, 0xdc, 0xec, 0x9f,
+};
+
+const unsigned char issuer_rem[] = {
+               0x1e, 0xbc, 0xa3, 0x0f, 0x00, 0xce, 0x59, 0x62, 0xa8, 0xc6, 0xe1, 0x30, 0x54, 0x4b, 0x82, 0x89,
+               0x1b, 0x23, 0x6c, 0x65, 0xde, 0x29, 0x31, 0x7f, 0x36, 0x47, 0x35, 0xde, 0xe6, 0x3f, 0x65, 0x98,
+               0x97, 0x58, 0x35, 0xd5
+};
+
+const unsigned char issuer_exp[] = {
+               0x03,
+};
+
+const unsigned char ssad_cr[] = {
+               0x99, 0xa5, 0x58, 0xb6, 0x2b, 0x67, 0x4a, 0xa5, 0xe7, 0xd2, 0xa5, 0x7e, 0x5e, 0xf6, 0xa6, 0xf2,
+               0x25, 0x8e, 0x5d, 0xa0, 0x52, 0xd0, 0x5b, 0x54, 0xe5, 0xc1, 0x15, 0xff, 0x1c, 0xec, 0xf9, 0x4a,
+               0xa2, 0xdf, 0x8f, 0x39, 0xa0, 0x1d, 0x71, 0xc6, 0x19, 0xeb, 0x81, 0x9d, 0xa5, 0x2e, 0xf3, 0x81,
+               0xe8, 0x49, 0x79, 0x58, 0x6a, 0xea, 0x78, 0x55, 0xff, 0xbe, 0xf4, 0x0a, 0xa3, 0xa7, 0x1c, 0xd3,
+               0xb0, 0x4c, 0xfd, 0xf2, 0x70, 0xae, 0xc8, 0x15, 0x8a, 0x27, 0x97, 0xf2, 0x4f, 0xd6, 0x13, 0xb7,
+               0x48, 0x13, 0x46, 0x61, 0x13, 0x5c, 0xd2, 0x90, 0xe4, 0x5b, 0x04, 0xa8, 0xe0, 0xcc, 0xc7, 0x11,
+               0xae, 0x04, 0x2f, 0x15, 0x9e, 0x73, 0xc8, 0x9c, 0x2a, 0x7e, 0x65, 0xa4, 0xc2, 0xfd, 0x1d, 0x61,
+               0x06, 0x02, 0x4a, 0xa2, 0x71, 0x30, 0xb0, 0xec, 0xec, 0x02, 0x38, 0xf9, 0x16, 0x59, 0xde, 0x96,
+};
+
+const unsigned char ssd1[] = {
+       0x5f, 0x24, 0x03, 0x08, 0x12, 0x31, 0x5a, 0x08, 0x42, 0x76, 0x55, 0x00, 0x13, 0x23, 0x45, 0x99, 0x5f, 0x34, 0x01, 0x01, 0x9f, 0x07, 0x02, 0xff, 0x00, 0x9f, 0x0d, 0x05, 0xd0, 0x40, 0xac, 0xa8, 0x00, 0x9f, 0x0e, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x9f, 0x0f, 0x05, 0xd0, 0x68, 0xbc, 0xf8, 0x00,
+       0x5c, 0x00,
+};
+static const struct tlv ssd1_tlv = {
+       .len = sizeof(ssd1),
+       .value = ssd1,
+};
+
+const unsigned char pan[] = {
+       0x42, 0x76, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static int sda_test_raw(bool verbose)
+{
+       const struct emv_pk *pk = &vsdc_01;
+
+       struct crypto_pk *kcp = crypto_pk_open(PK_RSA,
+                       pk->modulus, pk->mlen,
+                       pk->exp, pk->elen);
+       if (!kcp)
+               return 1;
+
+       unsigned char *ipk_data;
+       size_t ipk_data_len;
+       ipk_data = crypto_pk_encrypt(kcp, issuer_cert, sizeof(issuer_cert), &ipk_data_len);
+       crypto_pk_close(kcp);
+
+       if (!ipk_data)
+               return 1;
+
+       if (verbose) {
+               printf("issuer cert:\n");
+               dump_buffer(ipk_data, ipk_data_len, stdout, 0);
+       }
+
+       size_t ipk_pk_len = ipk_data[13];
+       unsigned char *ipk_pk = malloc(ipk_pk_len);
+       memcpy(ipk_pk, ipk_data + 15, ipk_data_len - 36);
+       memcpy(ipk_pk + ipk_data_len - 36, issuer_rem, sizeof(issuer_rem));
+
+       struct crypto_hash *ch;
+       ch = crypto_hash_open(HASH_SHA_1);
+       if (!ch) {
+               free(ipk_pk);
+               free(ipk_data);
+               return 1;
+       }
+
+       crypto_hash_write(ch, ipk_data + 1, 14);
+       crypto_hash_write(ch, ipk_pk, ipk_pk_len);
+       crypto_hash_write(ch, issuer_exp, sizeof(issuer_exp));
+
+       unsigned char *h = crypto_hash_read(ch);
+       if (!h) {
+               crypto_hash_close(ch);
+               free(ipk_pk);
+               free(ipk_data);
+               return 1;
+       }
+
+       if (verbose) {
+               printf("crypto hash:\n");
+               dump_buffer(h, 20, stdout, 0);
+       }
+
+       if (memcmp(ipk_data + ipk_data_len - 21, h, 20)) {
+               crypto_hash_close(ch);
+               free(ipk_pk);
+               free(ipk_data);
+               return 1;
+       }
+
+       crypto_hash_close(ch);
+       free(ipk_data);
+
+       struct crypto_pk *ikcp = crypto_pk_open(PK_RSA, ipk_pk, (int) ipk_pk_len,
+                       issuer_exp, (int) sizeof(issuer_exp));
+       free(ipk_pk);
+       if (!ikcp)
+               return 1;
+
+       size_t ssad_len;
+       unsigned char *ssad = crypto_pk_encrypt(ikcp, ssad_cr, sizeof(ssad_cr), &ssad_len);
+       crypto_pk_close(ikcp);
+       if (!ssad)
+               return 1;
+
+       if (verbose) {
+               printf("ssad:\n");
+               dump_buffer(ssad, ssad_len, stdout, 0);
+       }
+
+       ch = crypto_hash_open(HASH_SHA_1);
+       if (!ch) {
+               free(ssad);
+               return 1;
+       }
+
+       crypto_hash_write(ch, ssad + 1, ssad_len - 22);
+       crypto_hash_write(ch, ssd1, sizeof(ssd1));
+
+       unsigned char *h2 = crypto_hash_read(ch);
+       if (!h2) {
+               crypto_hash_close(ch);
+               free(ssad);
+               return 1;
+       }
+
+       if (verbose) {
+               printf("crypto hash2:\n");
+               dump_buffer(h2, 20, stdout, 0);
+       }
+
+       crypto_hash_close(ch);
+
+       free(ssad);
+
+       return 0;
+}
+
+static int sda_test_pk(bool verbose)
+{
+       const struct emv_pk *pk = &vsdc_01;
+       struct tlvdb *db;
+
+       db = tlvdb_external(0x90, sizeof(issuer_cert), issuer_cert);
+       tlvdb_add(db, tlvdb_external(0x9f32, sizeof(issuer_exp), issuer_exp));
+       tlvdb_add(db, tlvdb_external(0x92, sizeof(issuer_rem), issuer_rem));
+       tlvdb_add(db, tlvdb_external(0x5a, sizeof(pan), pan));
+
+       struct emv_pk *ipk = emv_pki_recover_issuer_cert(pk, db);
+       if (!ipk) {
+               fprintf(stderr, "Could not recover Issuer certificate!\n");
+               tlvdb_free(db);
+               return 2;
+       }
+
+       tlvdb_add(db, tlvdb_external(0x93, sizeof(ssad_cr), ssad_cr));
+
+       struct tlvdb *dacdb = emv_pki_recover_dac(ipk, db, &ssd1_tlv);
+       if (!dacdb) {
+               fprintf(stderr, "Could not recover DAC!\n");
+               emv_pk_free(ipk);
+               tlvdb_free(db);
+               return 2;
+       }
+
+       const struct tlv *dac = tlvdb_get(dacdb, 0x9f45, NULL);
+       if (!dac) {
+               fprintf(stderr, "DAC not found!\n");
+               tlvdb_free(dacdb);
+               emv_pk_free(ipk);
+               tlvdb_free(db);
+               return 2;
+       }
+
+       if (verbose) {
+               printf("dac:\n");
+               dump_buffer(dac->value, dac->len, stdout, 0);
+       }
+
+       tlvdb_free(dacdb);
+       emv_pk_free(ipk);
+       tlvdb_free(db);
+
+       return 0;
+}
+
+int exec_sda_test(bool verbose)
+{
+       int ret;
+       fprintf(stdout, "\n");
+
+       ret = sda_test_raw(verbose);
+       if (ret) {
+               fprintf(stderr, "SDA raw test: failed\n");
+               return ret;
+       }
+       fprintf(stdout, "SDA raw test: passed\n");
+
+       ret = sda_test_pk(verbose);
+       if (ret) {
+               fprintf(stderr, "SDA test pk: failed\n");
+               return ret;
+       }
+       fprintf(stdout, "SDA test pk: passed\n");
+
+       return 0;
+}
diff --git a/client/emv/test/sda_test.h b/client/emv/test/sda_test.h
new file mode 100644 (file)
index 0000000..43a4c94
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * emv-tools - a set of tools to work with EMV family of smart cards
+ * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ */
+extern int exec_sda_test(bool verbose);
index 57bc831fe1f97dda82c3c8fa9a940f77721ec0ea..3b146b109b520fc05eec44c4d06aaffdfdee2205 100644 (file)
@@ -22,7 +22,7 @@
  *
  * This is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
+ * by the Free Software Foundation, or, at your option, any later version. 
  *
  * This file is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -33,7 +33,6 @@
  * along with loclass.  If not, see <http://www.gnu.org/licenses/>.
  * 
  * 
- * 
  ****************************************************************************/
 
 
index 7afdb524cc4393273b5bd3e1af1c84c05f246bee..632910a98f55594aafcb289a81e8334bcfdcb551 100644 (file)
@@ -22,7 +22,7 @@
  *
  * This is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
+ * by the Free Software Foundation, or, at your option, any later version. 
  *
  * This file is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -33,7 +33,6 @@
  * along with loclass.  If not, see <http://www.gnu.org/licenses/>.
  * 
  * 
- * 
  ****************************************************************************/
 
 
index 9a8256bb38a700990fe6bddf4701116cdbaeac40..b5e365bf45780c34e1911f185080ba85d6b08739 100644 (file)
@@ -22,7 +22,7 @@
  *
  * This is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
+ * by the Free Software Foundation, or, at your option, any later version. 
  *
  * This file is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -33,7 +33,6 @@
  * along with loclass.  If not, see <http://www.gnu.org/licenses/>.
  * 
  * 
- * 
  ****************************************************************************/
 
 #include <stdint.h>
index 3cdbf5fa71305e4c7ebdb363ed01de1a637e8c47..568671791c9fff3eadc1560cdf6b95a4830c42e0 100644 (file)
@@ -22,7 +22,7 @@
  *
  * This is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
+ * by the Free Software Foundation, or, at your option, any later version. 
  *
  * This file is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -33,7 +33,6 @@
  * along with loclass.  If not, see <http://www.gnu.org/licenses/>.
  * 
  * 
- * 
  ****************************************************************************/
 
 
index 777efe08b95839a953675164c1ea89caf750550a..fe9bf7d1342ef0fb453a99c8548172e7a1dcdb66 100644 (file)
@@ -22,7 +22,7 @@
  *
  * This is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
+ * by the Free Software Foundation, or, at your option, any later version. 
  *
  * This file is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -33,7 +33,6 @@
  * along with loclass.  If not, see <http://www.gnu.org/licenses/>.
  * 
  * 
- * 
  ****************************************************************************/
 
 #include <stdint.h>
index fb27355fd7a1f5dbe6e4cf63e825ca3590f64469..46791fb5d302ecfb1a39ca49117e499a2ce6f746 100644 (file)
@@ -22,7 +22,7 @@
  *
  * This is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
+ * by the Free Software Foundation, or, at your option, any later version. 
  *
  * This file is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -33,7 +33,6 @@
  * along with loclass.  If not, see <http://www.gnu.org/licenses/>.
  * 
  * 
- * 
  ****************************************************************************/
 
 
index e5e5c5b0569270c422c53e83078d738a64f9e106..90857e7aaf96de7776a7f3ad7bd5c67b26696776 100644 (file)
@@ -22,7 +22,7 @@
  *
  * This is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
+ * by the Free Software Foundation, or, at your option, any later version. 
  *
  * This file is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -33,7 +33,6 @@
  * along with loclass.  If not, see <http://www.gnu.org/licenses/>.
  * 
  * 
- * 
  ****************************************************************************/
 #ifndef ON_DEVICE
 
index 10720f76d1e50b903d1acc0469a5d3af14f9bb0e..79dbe5660568f13a61a5b0f3f4d3b7114da46955 100644 (file)
@@ -22,7 +22,7 @@
  *
  * This is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
+ * by the Free Software Foundation, or, at your option, any later version. 
  *
  * This file is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -33,7 +33,6 @@
  * along with loclass.  If not, see <http://www.gnu.org/licenses/>.
  * 
  * 
- * 
  ****************************************************************************/
 
 #ifndef FILEUTILS_H
index 8892b4a35296d7b2aa2000a921eca5bd04a64e2d..2a6a00109297c72afe47de08426814f37a510f7e 100644 (file)
@@ -22,7 +22,7 @@
  *
  * This is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
+ * by the Free Software Foundation, or, at your option, any later version. 
  *
  * This file is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -33,7 +33,6 @@
  * along with loclass.  If not, see <http://www.gnu.org/licenses/>.
  * 
  * 
- * 
  ****************************************************************************/
 
 /**
@@ -740,18 +739,16 @@ int readKeyFile(uint8_t key[8])
        FILE *f;
        int retval = 1;
        f = fopen("iclass_key.bin", "rb");
-       if (f)
-       {
-               if(fread(key, sizeof(uint8_t), 8, f) == 1) 
-               {
-                       retval = 0;     
-               }
-               fclose(f);
+       if (!f)
+               return retval;
+       
+       if (fread(key, sizeof(uint8_t), 8, f) == 8) {
+               retval = 0;
        }
+       fclose(f);      
        return retval;
 }
 
-
 int doKeyTests(uint8_t debuglevel)
 {
        debug_print = debuglevel;
index 13096194d46b6bbbbc7e06108d434f0aea7da691..5b44d1796a7cd6f044bc074731360edd6053351e 100644 (file)
@@ -22,7 +22,7 @@
  *
  * This is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
+ * by the Free Software Foundation, or, at your option, any later version. 
  *
  * This file is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -33,7 +33,6 @@
  * along with loclass.  If not, see <http://www.gnu.org/licenses/>.
  * 
  * 
- * 
  ****************************************************************************/
 
 #ifndef IKEYS_H
index 95112f7decafe7a79f05c2651d7dd9b2aa848216..fa9886ee9e9251bd685bba25439db60d0d2346e9 100644 (file)
@@ -22,7 +22,7 @@
  *
  * This is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
+ * by the Free Software Foundation, or, at your option, any later version. 
  *
  * This file is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -33,7 +33,6 @@
  * along with loclass.  If not, see <http://www.gnu.org/licenses/>.
  * 
  * 
- * 
  ****************************************************************************/
 
 
diff --git a/client/obj/emv/test/.dummy b/client/obj/emv/test/.dummy
new file mode 100644 (file)
index 0000000..e69de29
index ed46c8e805d76debb2c247a700c9d381878a0d13..cffbca6cd3995f18dae85b5ec1be5236bd8086a2 100644 (file)
@@ -306,7 +306,7 @@ static int l_pack(lua_State *L)             /** pack(f,...) */
         sbyte = 0;
         odd = 0;
       }
-    } else if (isspace(a[ii])) {
+    } else if (isspace((unsigned char)a[ii])) {
       /* ignore */
     } else {
       /* err ... ignore too*/
index 2d45b2fe1f9b29ebaf779bd1a532f504df4a628b..5a9b6580449dd20a7343f1e1e1c3e52ee4e28996 100644 (file)
@@ -596,7 +596,7 @@ mbynam(model_t *dest, const char *key) {
                uerror("cannot allocate memory for comparison string");
        akey.name = uptr = ukey;
        do
-               *uptr++ = toupper(*key);
+               *uptr++ = toupper((unsigned char)*key);
        while(*key++);
 
        aptr = bsearch(&akey, aliases, NALIASES, sizeof(struct malias), (int (*)(const void *, const void *)) &acmp);
index 13cb8cdfcb1c998fdb09a773c8904a8262e4f74a..3859fd4863ce35fbc4942c5e326568a651ce403c 100644 (file)
@@ -23,8 +23,8 @@
 #include "iso14443crc.h"
 #include "../common/crc16.h"
 #include "../common/crc64.h"
-#include "../common/sha1.h"
-#include "polarssl/aes.h"
+#include "../common/polarssl/sha1.h"
+#include "../common/polarssl/aes.h"
 #include "cmdcrc.h"
 /**
  * The following params expected:
index df2c3ce3c6a6909cb99317f34770853c40992f13..8faed6e86cae50f34849dcd4b53b8337cfb03528 100644 (file)
@@ -9,12 +9,14 @@
 // UI utilities
 //-----------------------------------------------------------------------------
 
-#include <stdarg.h>
+#include <stdbool.h>
+#ifndef EXTERNAL_PRINTANDLOG
 #include <stdlib.h>
 #include <stdio.h>
-#include <stdbool.h>
+#include <stdarg.h>
 #include <readline/readline.h>
 #include <pthread.h>
+#endif
 
 #include "ui.h"
 
@@ -26,10 +28,12 @@ int GridOffset = 0;
 bool GridLocked = false;
 bool showDemod = true;
 
-extern pthread_mutex_t print_lock;
-
 static char *logfilename = "proxmark3.log";
 
+#ifndef EXTERNAL_PRINTANDLOG
+// Declared in proxmark3.c
+extern pthread_mutex_t print_lock;
+
 void PrintAndLog(char *fmt, ...)
 {
        char *saved_line;
@@ -94,7 +98,7 @@ void PrintAndLog(char *fmt, ...)
        //release lock
        pthread_mutex_unlock(&print_lock);  
 }
-
+#endif
 
 void SetLogFilename(char *fn)
 {
index 92b0e7a6ab22dadd8160252f6364734b8c7a870d..7e6b40742c1a7fa2ecb596481e30dae8f9e290ac 100644 (file)
@@ -111,6 +111,31 @@ void FillFileNameByUID(char *fileName, uint8_t * uid, char *ext, int byteCount)
        sprintf(fnameptr, "%s", ext); 
 }
 
+void hex_to_buffer(const uint8_t *buf, const uint8_t *hex_data, const size_t hex_len, const size_t hex_max_len, 
+       const size_t min_str_len, const size_t spaces_between, bool uppercase) {
+               
+       char *tmp = (char *)buf;
+       size_t i;
+
+       int maxLen = ( hex_len > hex_max_len) ? hex_max_len : hex_len;
+
+       for (i = 0; i < maxLen; ++i, tmp += 2 + spaces_between) {
+               sprintf(tmp, (uppercase) ? "%02X" : "%02x", (unsigned int) hex_data[i]); 
+               
+               for (int j = 0; j < spaces_between; j++)
+                       sprintf(tmp + 2 + j, " ");
+       }
+       
+       i *= (2 + spaces_between);
+       int minStrLen = min_str_len > i ? min_str_len : 0;
+       if (minStrLen > hex_max_len)
+               minStrLen = hex_max_len;
+       for(; i < minStrLen; i++, tmp += 1) 
+               sprintf(tmp, " ");
+
+       return;
+}
+
 // printing and converting functions
 
 void print_hex(const uint8_t * data, const size_t len)
@@ -141,33 +166,17 @@ void print_hex_break(const uint8_t *data, const size_t len, uint8_t breaks) {
 }
 
 char *sprint_hex(const uint8_t *data, const size_t len) {
+       static char buf[1025] = {0};
        
-       int maxLen = ( len > 1024/3) ? 1024/3 : len;
-       static char buf[1024];
-       memset(buf, 0x00, 1024);
-       char *tmp = buf;
-       size_t i;
-
-       for (i=0; i < maxLen; ++i, tmp += 3)
-               sprintf(tmp, "%02x ", (unsigned int) data[i]);
+       hex_to_buffer((uint8_t *)buf, data, len, sizeof(buf) - 1, 0, 1, false);
 
        return buf;
 }
 
 char *sprint_hex_inrow_ex(const uint8_t *data, const size_t len, const size_t min_str_len) {
-       
-       int maxLen = ( len > 1024/2) ? 1024/2 : len;
-       static char buf[1024] = {0};
-       char *tmp = buf;
-       size_t i;
+       static char buf[1025] = {0};
 
-       for (i = 0; i < maxLen; ++i, tmp += 2)
-               sprintf(tmp, "%02x", (unsigned int) data[i]);
-       
-       i *= 2;
-       int minStrLen = min_str_len > i ? min_str_len : 0;
-       for(; i < minStrLen; i++, tmp += 1) 
-               sprintf(tmp, " ");
+       hex_to_buffer((uint8_t *)buf, data, len, sizeof(buf) - 1, min_str_len, 0, false);
 
        return buf;
 }
@@ -487,7 +496,7 @@ int param_gethex(const char *line, int paramnum, uint8_t * data, int hexcnt)
                return 1;
 
        for(i = 0; i < hexcnt; i += 2) {
-               if (!(isxdigit(line[bg + i]) && isxdigit(line[bg + i + 1])) )   return 1;
+               if (!(isxdigit((unsigned char)line[bg + i]) && isxdigit((unsigned char)line[bg + i + 1])) )     return 1;
                
                sscanf((char[]){line[bg + i], line[bg + i + 1], 0}, "%X", &temp);
                data[i / 2] = temp & 0xff;
@@ -509,7 +518,7 @@ int param_gethex_ex(const char *line, int paramnum, uint8_t * data, int *hexcnt)
                return 1;
 
        for(i = 0; i < *hexcnt; i += 2) {
-               if (!(isxdigit(line[bg + i]) && isxdigit(line[bg + i + 1])) )   return 1;
+               if (!(isxdigit((unsigned char)line[bg + i]) && isxdigit((unsigned char)line[bg + i + 1])) )     return 1;
                
                sscanf((char[]){line[bg + i], line[bg + i + 1], 0}, "%X", &temp);
                data[i / 2] = temp & 0xff;
@@ -534,7 +543,7 @@ int param_gethex_to_eol(const char *line, int paramnum, uint8_t * data, int maxd
                        continue;
                }
                
-               if (isxdigit(line[indx])) {
+               if (isxdigit((unsigned char)line[indx])) {
                        buf[strlen(buf) + 1] = 0x00;
                        buf[strlen(buf)] = line[indx];
                } else {
@@ -611,7 +620,7 @@ int hextobinarray(char *target, char *source)
         else if (x >= 'A' && x <= 'F')
             x -= 'A' - 10;
         else {
-               printf("Discovered unknown character %c %d at idx %d of %s\n", x, x, source - start, start);
+               printf("Discovered unknown character %c %d at idx %tu of %s\n", x, x, source - start, start);
             return 0;
         }
         // output
index e9c48b0353bbc27e3c7f0cccaf38fd81f7d9228d..fd7ceafffb6de7685328405fa479cc2163810c16 100644 (file)
@@ -12,6 +12,7 @@
 #define UTIL_H__
 
 #include <stdint.h>
+#include <stdbool.h>
 #include <stddef.h>
 
 #ifndef ROTR
@@ -35,6 +36,9 @@ extern void AddLogUint64(char *fileName, char *extData, const uint64_t data);
 extern void AddLogCurrentDT(char *fileName);
 extern void FillFileNameByUID(char *fileName, uint8_t * uid, char *ext, int byteCount);
 
+extern void hex_to_buffer(const uint8_t *buf, const uint8_t *hex_data, const size_t hex_len, 
+       const size_t hex_max_len, const size_t min_str_len, const size_t spaces_between, bool uppercase);
+
 extern void print_hex(const uint8_t * data, const size_t len);
 extern char *sprint_hex(const uint8_t * data, const size_t len);
 extern char *sprint_hex_inrow(const uint8_t *data, const size_t len);
index 946bd87d92c0fb43e0290540892a5354c79fcaea..299cb4cdfe3d02b89641eceea2404e78e22e8585 100644 (file)
@@ -125,7 +125,6 @@ int aes_crypt_ecb( aes_context *ctx,
                     int mode,
                     const unsigned char input[16],
                     unsigned char output[16] );
-
 #if defined(POLARSSL_CIPHER_MODE_CBC)
 /**
  * \brief          AES-CBC buffer encryption/decryption
diff --git a/common/polarssl/bignum.c b/common/polarssl/bignum.c
new file mode 100644 (file)
index 0000000..d22dd5c
--- /dev/null
@@ -0,0 +1,2143 @@
+/*
+ *  Multi-precision integer library
+ *
+ *  Copyright (C) 2006-2010, Brainspark B.V.
+ *
+ *  This file is part of PolarSSL (http://www.polarssl.org)
+ *  Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org>
+ *
+ *  All rights reserved.
+ *
+ *  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, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+/*
+ *  This MPI implementation is based on:
+ *
+ *  http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf
+ *  http://www.stillhq.com/extracted/gnupg-api/mpi/
+ *  http://math.libtomcrypt.com/files/tommath.pdf
+ */
+
+#include "polarssl_config.h"
+
+#if defined(POLARSSL_BIGNUM_C)
+
+#include "bignum.h"
+#include "bn_mul.h"
+
+#include <stdlib.h>
+
+#define ciL    (sizeof(t_uint))         /* chars in limb  */
+#define biL    (ciL << 3)               /* bits  in limb  */
+#define biH    (ciL << 2)               /* half limb size */
+
+/*
+ * Convert between bits/chars and number of limbs
+ */
+#define BITS_TO_LIMBS(i)  (((i) + biL - 1) / biL)
+#define CHARS_TO_LIMBS(i) (((i) + ciL - 1) / ciL)
+
+/*
+ * Initialize one MPI
+ */
+void mpi_init( mpi *X )
+{
+    if( X == NULL )
+        return;
+
+    X->s = 1;
+    X->n = 0;
+    X->p = NULL;
+}
+
+/*
+ * Unallocate one MPI
+ */
+void mpi_free( mpi *X )
+{
+    if( X == NULL )
+        return;
+
+    if( X->p != NULL )
+    {
+        memset( X->p, 0, X->n * ciL );
+        free( X->p );
+    }
+
+    X->s = 1;
+    X->n = 0;
+    X->p = NULL;
+}
+
+/*
+ * Enlarge to the specified number of limbs
+ */
+int mpi_grow( mpi *X, size_t nblimbs )
+{
+    t_uint *p;
+
+    if( nblimbs > POLARSSL_MPI_MAX_LIMBS )
+        return( POLARSSL_ERR_MPI_MALLOC_FAILED );
+
+    if( X->n < nblimbs )
+    {
+        if( ( p = (t_uint *) malloc( nblimbs * ciL ) ) == NULL )
+            return( POLARSSL_ERR_MPI_MALLOC_FAILED );
+
+        memset( p, 0, nblimbs * ciL );
+
+        if( X->p != NULL )
+        {
+            memcpy( p, X->p, X->n * ciL );
+            memset( X->p, 0, X->n * ciL );
+            free( X->p );
+        }
+
+        X->n = nblimbs;
+        X->p = p;
+    }
+
+    return( 0 );
+}
+
+/*
+ * Copy the contents of Y into X
+ */
+int mpi_copy( mpi *X, const mpi *Y )
+{
+    int ret;
+    size_t i;
+
+    if( X == Y )
+        return( 0 );
+
+    for( i = Y->n - 1; i > 0; i-- )
+        if( Y->p[i] != 0 )
+            break;
+    i++;
+
+    X->s = Y->s;
+
+    MPI_CHK( mpi_grow( X, i ) );
+
+    memset( X->p, 0, X->n * ciL );
+    memcpy( X->p, Y->p, i * ciL );
+
+cleanup:
+
+    return( ret );
+}
+
+/*
+ * Swap the contents of X and Y
+ */
+void mpi_swap( mpi *X, mpi *Y )
+{
+    mpi T;
+
+    memcpy( &T,  X, sizeof( mpi ) );
+    memcpy(  X,  Y, sizeof( mpi ) );
+    memcpy(  Y, &T, sizeof( mpi ) );
+}
+
+/*
+ * Set value from integer
+ */
+int mpi_lset( mpi *X, t_sint z )
+{
+    int ret;
+
+    MPI_CHK( mpi_grow( X, 1 ) );
+    memset( X->p, 0, X->n * ciL );
+
+    X->p[0] = ( z < 0 ) ? -z : z;
+    X->s    = ( z < 0 ) ? -1 : 1;
+
+cleanup:
+
+    return( ret );
+}
+
+/*
+ * Get a specific bit
+ */
+int mpi_get_bit( const mpi *X, size_t pos )
+{
+    if( X->n * biL <= pos )
+        return( 0 );
+
+    return ( X->p[pos / biL] >> ( pos % biL ) ) & 0x01;
+}
+
+/*
+ * Set a bit to a specific value of 0 or 1
+ */
+int mpi_set_bit( mpi *X, size_t pos, unsigned char val )
+{
+    int ret = 0;
+    size_t off = pos / biL;
+    size_t idx = pos % biL;
+
+    if( val != 0 && val != 1 )
+        return POLARSSL_ERR_MPI_BAD_INPUT_DATA;
+        
+    if( X->n * biL <= pos )
+    {
+        if( val == 0 )
+            return ( 0 );
+
+        MPI_CHK( mpi_grow( X, off + 1 ) );
+    }
+
+    X->p[off] = ( X->p[off] & ~( 0x01 << idx ) ) | ( val << idx );
+
+cleanup:
+    
+    return( ret );
+}
+
+/*
+ * Return the number of least significant bits
+ */
+size_t mpi_lsb( const mpi *X )
+{
+    size_t i, j, count = 0;
+
+    for( i = 0; i < X->n; i++ )
+        for( j = 0; j < biL; j++, count++ )
+            if( ( ( X->p[i] >> j ) & 1 ) != 0 )
+                return( count );
+
+    return( 0 );
+}
+
+/*
+ * Return the number of most significant bits
+ */
+size_t mpi_msb( const mpi *X )
+{
+    size_t i, j;
+
+    for( i = X->n - 1; i > 0; i-- )
+        if( X->p[i] != 0 )
+            break;
+
+    for( j = biL; j > 0; j-- )
+        if( ( ( X->p[i] >> ( j - 1 ) ) & 1 ) != 0 )
+            break;
+
+    return( ( i * biL ) + j );
+}
+
+/*
+ * Return the total size in bytes
+ */
+size_t mpi_size( const mpi *X )
+{
+    return( ( mpi_msb( X ) + 7 ) >> 3 );
+}
+
+/*
+ * Convert an ASCII character to digit value
+ */
+static int mpi_get_digit( t_uint *d, int radix, char c )
+{
+    *d = 255;
+
+    if( c >= 0x30 && c <= 0x39 ) *d = c - 0x30;
+    if( c >= 0x41 && c <= 0x46 ) *d = c - 0x37;
+    if( c >= 0x61 && c <= 0x66 ) *d = c - 0x57;
+
+    if( *d >= (t_uint) radix )
+        return( POLARSSL_ERR_MPI_INVALID_CHARACTER );
+
+    return( 0 );
+}
+
+/*
+ * Import from an ASCII string
+ */
+int mpi_read_string( mpi *X, int radix, const char *s )
+{
+    int ret;
+    size_t i, j, slen, n;
+    t_uint d;
+    mpi T;
+
+    if( radix < 2 || radix > 16 )
+        return( POLARSSL_ERR_MPI_BAD_INPUT_DATA );
+
+    mpi_init( &T );
+
+    slen = strlen( s );
+
+    if( radix == 16 )
+    {
+        n = BITS_TO_LIMBS( slen << 2 );
+
+        MPI_CHK( mpi_grow( X, n ) );
+        MPI_CHK( mpi_lset( X, 0 ) );
+
+        for( i = slen, j = 0; i > 0; i--, j++ )
+        {
+            if( i == 1 && s[i - 1] == '-' )
+            {
+                X->s = -1;
+                break;
+            }
+
+            MPI_CHK( mpi_get_digit( &d, radix, s[i - 1] ) );
+            X->p[j / (2 * ciL)] |= d << ( (j % (2 * ciL)) << 2 );
+        }
+    }
+    else
+    {
+        MPI_CHK( mpi_lset( X, 0 ) );
+
+        for( i = 0; i < slen; i++ )
+        {
+            if( i == 0 && s[i] == '-' )
+            {
+                X->s = -1;
+                continue;
+            }
+
+            MPI_CHK( mpi_get_digit( &d, radix, s[i] ) );
+            MPI_CHK( mpi_mul_int( &T, X, radix ) );
+
+            if( X->s == 1 )
+            {
+                MPI_CHK( mpi_add_int( X, &T, d ) );
+            }
+            else
+            {
+                MPI_CHK( mpi_sub_int( X, &T, d ) );
+            }
+        }
+    }
+
+cleanup:
+
+    mpi_free( &T );
+
+    return( ret );
+}
+
+/*
+ * Helper to write the digits high-order first
+ */
+static int mpi_write_hlp( mpi *X, int radix, char **p )
+{
+    int ret;
+    t_uint r;
+
+    if( radix < 2 || radix > 16 )
+        return( POLARSSL_ERR_MPI_BAD_INPUT_DATA );
+
+    MPI_CHK( mpi_mod_int( &r, X, radix ) );
+    MPI_CHK( mpi_div_int( X, NULL, X, radix ) );
+
+    if( mpi_cmp_int( X, 0 ) != 0 )
+        MPI_CHK( mpi_write_hlp( X, radix, p ) );
+
+    if( r < 10 )
+        *(*p)++ = (char)( r + 0x30 );
+    else
+        *(*p)++ = (char)( r + 0x37 );
+
+cleanup:
+
+    return( ret );
+}
+
+/*
+ * Export into an ASCII string
+ */
+int mpi_write_string( const mpi *X, int radix, char *s, size_t *slen )
+{
+    int ret = 0;
+    size_t n;
+    char *p;
+    mpi T;
+
+    if( radix < 2 || radix > 16 )
+        return( POLARSSL_ERR_MPI_BAD_INPUT_DATA );
+
+    n = mpi_msb( X );
+    if( radix >=  4 ) n >>= 1;
+    if( radix >= 16 ) n >>= 1;
+    n += 3;
+
+    if( *slen < n )
+    {
+        *slen = n;
+        return( POLARSSL_ERR_MPI_BUFFER_TOO_SMALL );
+    }
+
+    p = s;
+    mpi_init( &T );
+
+    if( X->s == -1 )
+        *p++ = '-';
+
+    if( radix == 16 )
+    {
+        int c;
+        size_t i, j, k;
+
+        for( i = X->n, k = 0; i > 0; i-- )
+        {
+            for( j = ciL; j > 0; j-- )
+            {
+                c = ( X->p[i - 1] >> ( ( j - 1 ) << 3) ) & 0xFF;
+
+                if( c == 0 && k == 0 && ( i + j + 3 ) != 0 )
+                    continue;
+
+                *(p++) = "0123456789ABCDEF" [c / 16];
+                *(p++) = "0123456789ABCDEF" [c % 16];
+                k = 1;
+            }
+        }
+    }
+    else
+    {
+        MPI_CHK( mpi_copy( &T, X ) );
+
+        if( T.s == -1 )
+            T.s = 1;
+
+        MPI_CHK( mpi_write_hlp( &T, radix, &p ) );
+    }
+
+    *p++ = '\0';
+    *slen = p - s;
+
+cleanup:
+
+    mpi_free( &T );
+
+    return( ret );
+}
+
+#if defined(POLARSSL_FS_IO)
+/*
+ * Read X from an opened file
+ */
+int mpi_read_file( mpi *X, int radix, FILE *fin )
+{
+    t_uint d;
+    size_t slen;
+    char *p;
+    /*
+     * Buffer should have space for (short) label and decimal formatted MPI,
+     * newline characters and '\0'
+     */
+    char s[ POLARSSL_MPI_RW_BUFFER_SIZE ];
+
+    memset( s, 0, sizeof( s ) );
+    if( fgets( s, sizeof( s ) - 1, fin ) == NULL )
+        return( POLARSSL_ERR_MPI_FILE_IO_ERROR );
+
+    slen = strlen( s );
+    if( slen == sizeof( s ) - 2 )
+        return( POLARSSL_ERR_MPI_BUFFER_TOO_SMALL );
+
+    if( s[slen - 1] == '\n' ) { slen--; s[slen] = '\0'; }
+    if( s[slen - 1] == '\r' ) { slen--; s[slen] = '\0'; }
+
+    p = s + slen;
+    while( --p >= s )
+        if( mpi_get_digit( &d, radix, *p ) != 0 )
+            break;
+
+    return( mpi_read_string( X, radix, p + 1 ) );
+}
+
+/*
+ * Write X into an opened file (or stdout if fout == NULL)
+ */
+int mpi_write_file( const char *p, const mpi *X, int radix, FILE *fout )
+{
+    int ret;
+    size_t n, slen, plen;
+    /*
+     * Buffer should have space for (short) label and decimal formatted MPI,
+     * newline characters and '\0'
+     */
+    char s[ POLARSSL_MPI_RW_BUFFER_SIZE ];
+
+    n = sizeof( s );
+    memset( s, 0, n );
+    n -= 2;
+
+    MPI_CHK( mpi_write_string( X, radix, s, (size_t *) &n ) );
+
+    if( p == NULL ) p = "";
+
+    plen = strlen( p );
+    slen = strlen( s );
+    s[slen++] = '\r';
+    s[slen++] = '\n';
+
+    if( fout != NULL )
+    {
+        if( fwrite( p, 1, plen, fout ) != plen ||
+            fwrite( s, 1, slen, fout ) != slen )
+            return( POLARSSL_ERR_MPI_FILE_IO_ERROR );
+    }
+    else
+        printf( "%s%s", p, s );
+
+cleanup:
+
+    return( ret );
+}
+#endif /* POLARSSL_FS_IO */
+
+/*
+ * Import X from unsigned binary data, big endian
+ */
+int mpi_read_binary( mpi *X, const unsigned char *buf, size_t buflen )
+{
+    int ret;
+    size_t i, j, n;
+
+    for( n = 0; n < buflen; n++ )
+        if( buf[n] != 0 )
+            break;
+
+    MPI_CHK( mpi_grow( X, CHARS_TO_LIMBS( buflen - n ) ) );
+    MPI_CHK( mpi_lset( X, 0 ) );
+
+    for( i = buflen, j = 0; i > n; i--, j++ )
+        X->p[j / ciL] |= ((t_uint) buf[i - 1]) << ((j % ciL) << 3);
+
+cleanup:
+
+    return( ret );
+}
+
+/*
+ * Export X into unsigned binary data, big endian
+ */
+int mpi_write_binary( const mpi *X, unsigned char *buf, size_t buflen )
+{
+    size_t i, j, n;
+
+    n = mpi_size( X );
+
+    if( buflen < n )
+        return( POLARSSL_ERR_MPI_BUFFER_TOO_SMALL );
+
+    memset( buf, 0, buflen );
+
+    for( i = buflen - 1, j = 0; n > 0; i--, j++, n-- )
+        buf[i] = (unsigned char)( X->p[j / ciL] >> ((j % ciL) << 3) );
+
+    return( 0 );
+}
+
+/*
+ * Left-shift: X <<= count
+ */
+int mpi_shift_l( mpi *X, size_t count )
+{
+    int ret;
+    size_t i, v0, t1;
+    t_uint r0 = 0, r1;
+
+    v0 = count / (biL    );
+    t1 = count & (biL - 1);
+
+    i = mpi_msb( X ) + count;
+
+    if( X->n * biL < i )
+        MPI_CHK( mpi_grow( X, BITS_TO_LIMBS( i ) ) );
+
+    ret = 0;
+
+    /*
+     * shift by count / limb_size
+     */
+    if( v0 > 0 )
+    {
+        for( i = X->n; i > v0; i-- )
+            X->p[i - 1] = X->p[i - v0 - 1];
+
+        for( ; i > 0; i-- )
+            X->p[i - 1] = 0;
+    }
+
+    /*
+     * shift by count % limb_size
+     */
+    if( t1 > 0 )
+    {
+        for( i = v0; i < X->n; i++ )
+        {
+            r1 = X->p[i] >> (biL - t1);
+            X->p[i] <<= t1;
+            X->p[i] |= r0;
+            r0 = r1;
+        }
+    }
+
+cleanup:
+
+    return( ret );
+}
+
+/*
+ * Right-shift: X >>= count
+ */
+int mpi_shift_r( mpi *X, size_t count )
+{
+    size_t i, v0, v1;
+    t_uint r0 = 0, r1;
+
+    v0 = count /  biL;
+    v1 = count & (biL - 1);
+
+    if( v0 > X->n || ( v0 == X->n && v1 > 0 ) )
+        return mpi_lset( X, 0 );
+
+    /*
+     * shift by count / limb_size
+     */
+    if( v0 > 0 )
+    {
+        for( i = 0; i < X->n - v0; i++ )
+            X->p[i] = X->p[i + v0];
+
+        for( ; i < X->n; i++ )
+            X->p[i] = 0;
+    }
+
+    /*
+     * shift by count % limb_size
+     */
+    if( v1 > 0 )
+    {
+        for( i = X->n; i > 0; i-- )
+        {
+            r1 = X->p[i - 1] << (biL - v1);
+            X->p[i - 1] >>= v1;
+            X->p[i - 1] |= r0;
+            r0 = r1;
+        }
+    }
+
+    return( 0 );
+}
+
+/*
+ * Compare unsigned values
+ */
+int mpi_cmp_abs( const mpi *X, const mpi *Y )
+{
+    size_t i, j;
+
+    for( i = X->n; i > 0; i-- )
+        if( X->p[i - 1] != 0 )
+            break;
+
+    for( j = Y->n; j > 0; j-- )
+        if( Y->p[j - 1] != 0 )
+            break;
+
+    if( i == 0 && j == 0 )
+        return( 0 );
+
+    if( i > j ) return(  1 );
+    if( j > i ) return( -1 );
+
+    for( ; i > 0; i-- )
+    {
+        if( X->p[i - 1] > Y->p[i - 1] ) return(  1 );
+        if( X->p[i - 1] < Y->p[i - 1] ) return( -1 );
+    }
+
+    return( 0 );
+}
+
+/*
+ * Compare signed values
+ */
+int mpi_cmp_mpi( const mpi *X, const mpi *Y )
+{
+    size_t i, j;
+
+    for( i = X->n; i > 0; i-- )
+        if( X->p[i - 1] != 0 )
+            break;
+
+    for( j = Y->n; j > 0; j-- )
+        if( Y->p[j - 1] != 0 )
+            break;
+
+    if( i == 0 && j == 0 )
+        return( 0 );
+
+    if( i > j ) return(  X->s );
+    if( j > i ) return( -Y->s );
+
+    if( X->s > 0 && Y->s < 0 ) return(  1 );
+    if( Y->s > 0 && X->s < 0 ) return( -1 );
+
+    for( ; i > 0; i-- )
+    {
+        if( X->p[i - 1] > Y->p[i - 1] ) return(  X->s );
+        if( X->p[i - 1] < Y->p[i - 1] ) return( -X->s );
+    }
+
+    return( 0 );
+}
+
+/*
+ * Compare signed values
+ */
+int mpi_cmp_int( const mpi *X, t_sint z )
+{
+    mpi Y;
+    t_uint p[1];
+
+    *p  = ( z < 0 ) ? -z : z;
+    Y.s = ( z < 0 ) ? -1 : 1;
+    Y.n = 1;
+    Y.p = p;
+
+    return( mpi_cmp_mpi( X, &Y ) );
+}
+
+/*
+ * Unsigned addition: X = |A| + |B|  (HAC 14.7)
+ */
+int mpi_add_abs( mpi *X, const mpi *A, const mpi *B )
+{
+    int ret;
+    size_t i, j;
+    t_uint *o, *p, c;
+
+    if( X == B )
+    {
+        const mpi *T = A; A = X; B = T;
+    }
+
+    if( X != A )
+        MPI_CHK( mpi_copy( X, A ) );
+   
+    /*
+     * X should always be positive as a result of unsigned additions.
+     */
+    X->s = 1;
+
+    for( j = B->n; j > 0; j-- )
+        if( B->p[j - 1] != 0 )
+            break;
+
+    MPI_CHK( mpi_grow( X, j ) );
+
+    o = B->p; p = X->p; c = 0;
+
+    for( i = 0; i < j; i++, o++, p++ )
+    {
+        *p +=  c; c  = ( *p <  c );
+        *p += *o; c += ( *p < *o );
+    }
+
+    while( c != 0 )
+    {
+        if( i >= X->n )
+        {
+            MPI_CHK( mpi_grow( X, i + 1 ) );
+            p = X->p + i;
+        }
+
+        *p += c; c = ( *p < c ); i++; p++;
+    }
+
+cleanup:
+
+    return( ret );
+}
+
+/*
+ * Helper for mpi substraction
+ */
+static void mpi_sub_hlp( size_t n, t_uint *s, t_uint *d )
+{
+    size_t i;
+    t_uint c, z;
+
+    for( i = c = 0; i < n; i++, s++, d++ )
+    {
+        z = ( *d <  c );     *d -=  c;
+        c = ( *d < *s ) + z; *d -= *s;
+    }
+
+    while( c != 0 )
+    {
+        z = ( *d < c ); *d -= c;
+        c = z; i++; d++;
+    }
+}
+
+/*
+ * Unsigned substraction: X = |A| - |B|  (HAC 14.9)
+ */
+int mpi_sub_abs( mpi *X, const mpi *A, const mpi *B )
+{
+    mpi TB;
+    int ret;
+    size_t n;
+
+    if( mpi_cmp_abs( A, B ) < 0 )
+        return( POLARSSL_ERR_MPI_NEGATIVE_VALUE );
+
+    mpi_init( &TB );
+
+    if( X == B )
+    {
+        MPI_CHK( mpi_copy( &TB, B ) );
+        B = &TB;
+    }
+
+    if( X != A )
+        MPI_CHK( mpi_copy( X, A ) );
+
+    /*
+     * X should always be positive as a result of unsigned substractions.
+     */
+    X->s = 1;
+
+    ret = 0;
+
+    for( n = B->n; n > 0; n-- )
+        if( B->p[n - 1] != 0 )
+            break;
+
+    mpi_sub_hlp( n, B->p, X->p );
+
+cleanup:
+
+    mpi_free( &TB );
+
+    return( ret );
+}
+
+/*
+ * Signed addition: X = A + B
+ */
+int mpi_add_mpi( mpi *X, const mpi *A, const mpi *B )
+{
+    int ret, s = A->s;
+
+    if( A->s * B->s < 0 )
+    {
+        if( mpi_cmp_abs( A, B ) >= 0 )
+        {
+            MPI_CHK( mpi_sub_abs( X, A, B ) );
+            X->s =  s;
+        }
+        else
+        {
+            MPI_CHK( mpi_sub_abs( X, B, A ) );
+            X->s = -s;
+        }
+    }