From faef1a093801d54f423441e0f551c8292fe1fe7d Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 16 Nov 2018 23:57:55 +0100 Subject: [PATCH 01/12] chg: lf simulation - trying the new clock for better timings. --- armsrc/lfops.c | 109 ++++++++++++++++++++++++++----------------------- 1 file changed, 57 insertions(+), 52 deletions(-) diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 021fc2411..0dac64bd1 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -575,10 +575,13 @@ void WriteTItag(uint32_t idhi, uint32_t idlo, uint16_t crc) // this may destroy the bigbuf so be sure this is called before calling SimulateTagLowFrequencyEx void SimulateTagLowFrequencyEx(int period, int gap, int ledcontrol, int numcycles) { + // start us timer + StartTicks(); + //FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_TOGGLE_MODE ); FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT); - SpinDelay(20); - + WaitMS(20); + int i = 0, x = 0; uint8_t *buf = BigBuf_get_addr(); @@ -617,7 +620,7 @@ void SimulateTagLowFrequencyEx(int period, int gap, int ledcontrol, int numcycle goto OUT; } - if(buf[i]) + if (buf[i]) OPEN_COIL(); else SHORT_COIL(); @@ -631,17 +634,18 @@ void SimulateTagLowFrequencyEx(int period, int gap, int ledcontrol, int numcycle } i++; - if(i == period) { + if (i == period) { i = 0; if (gap) { SHORT_COIL(); - SpinDelayUs(gap); + WaitUS(gap); } } if (ledcontrol) LED_D_OFF(); } OUT: + StopTicks(); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); } @@ -665,44 +669,44 @@ static void fc(int c, int *n) int idx; // for when we want an fc8 pattern every 4 logical bits - if(c==0) { - dest[((*n)++)]=1; - dest[((*n)++)]=1; - dest[((*n)++)]=1; - dest[((*n)++)]=1; - dest[((*n)++)]=0; - dest[((*n)++)]=0; - dest[((*n)++)]=0; - dest[((*n)++)]=0; + if (c == 0) { + dest[((*n)++)] = 1; + dest[((*n)++)] = 1; + dest[((*n)++)] = 1; + dest[((*n)++)] = 1; + dest[((*n)++)] = 0; + dest[((*n)++)] = 0; + dest[((*n)++)] = 0; + dest[((*n)++)] = 0; } // an fc/8 encoded bit is a bit pattern of 11110000 x6 = 48 samples - if(c==8) { - for (idx=0; idx<6; idx++) { - dest[((*n)++)]=1; - dest[((*n)++)]=1; - dest[((*n)++)]=1; - dest[((*n)++)]=1; - dest[((*n)++)]=0; - dest[((*n)++)]=0; - dest[((*n)++)]=0; - dest[((*n)++)]=0; + if (c == 8) { + for (idx=0; idx < 6; idx++) { + dest[((*n)++)] = 1; + dest[((*n)++)] = 1; + dest[((*n)++)] = 1; + dest[((*n)++)] = 1; + dest[((*n)++)] = 0; + dest[((*n)++)] = 0; + dest[((*n)++)] = 0; + dest[((*n)++)] = 0; } } // an fc/10 encoded bit is a bit pattern of 1111100000 x5 = 50 samples - if(c==10) { - for (idx=0; idx<5; idx++) { - dest[((*n)++)]=1; - dest[((*n)++)]=1; - dest[((*n)++)]=1; - dest[((*n)++)]=1; - dest[((*n)++)]=1; - dest[((*n)++)]=0; - dest[((*n)++)]=0; - dest[((*n)++)]=0; - dest[((*n)++)]=0; - dest[((*n)++)]=0; + if (c == 10) { + for (idx = 0; idx < 5; idx++) { + dest[((*n)++)] = 1; + dest[((*n)++)] = 1; + dest[((*n)++)] = 1; + dest[((*n)++)] = 1; + dest[((*n)++)] = 1; + dest[((*n)++)] = 0; + dest[((*n)++)] = 0; + dest[((*n)++)] = 0; + dest[((*n)++)] = 0; + dest[((*n)++)] = 0; } } } @@ -720,7 +724,7 @@ static void fcSTT(int *n) { static void fcAll(uint8_t fc, int *n, uint8_t clock, uint16_t *modCnt) { uint8_t *dest = BigBuf_get_addr(); - uint8_t halfFC = fc/2; + uint8_t halfFC = fc >> 1; uint8_t wavesPerClock = clock/fc; uint8_t mod = clock % fc; //modifier uint8_t modAdj = fc/mod; //how often to apply modifier @@ -729,21 +733,22 @@ static void fcAll(uint8_t fc, int *n, uint8_t clock, uint16_t *modCnt) // loop through clock - step field clock for (uint8_t idx=0; idx < wavesPerClock; idx++){ // put 1/2 FC length 1's and 1/2 0's per field clock wave (to create the wave) - memset(dest+(*n), 0, fc-halfFC); //in case of odd number use extra here - memset(dest+(*n)+(fc-halfFC), 1, halfFC); + memset(dest + (*n), 0, fc - halfFC); //in case of odd number use extra here + memset(dest + (*n) + (fc - halfFC), 1, halfFC); *n += fc; } - if (mod>0) (*modCnt)++; - if ((mod>0) && modAdjOk){ //fsk2 + if (mod > 0) (*modCnt)++; + + if ((mod > 0) && modAdjOk){ //fsk2 if ((*modCnt % modAdj) == 0){ //if 4th 8 length wave in a rf/50 add extra 8 length wave - memset(dest+(*n), 0, fc-halfFC); - memset(dest+(*n)+(fc-halfFC), 1, halfFC); + memset(dest + (*n), 0, fc - halfFC); + memset(dest + (*n) + ( fc - halfFC), 1, halfFC); *n += fc; } } - if (mod>0 && !modAdjOk){ //fsk1 - memset(dest+(*n), 0, mod-(mod/2)); - memset(dest+(*n)+(mod-(mod/2)), 1, mod/2); + if (mod > 0 && !modAdjOk){ //fsk1 + memset(dest + (*n), 0, mod - (mod >> 1)); + memset(dest + (*n) + (mod - (mod >> 1)), 1, mod >> 1); *n += mod; } } @@ -781,11 +786,11 @@ void CmdHIDsimTAGEx( uint32_t hi, uint32_t lo, int ledcontrol, int numcycles) { fcSTT(&n); // manchester encode bits 43 to 32 - for (i=11; i>=0; i--) { + for (i = 11; i >= 0; i--) { - if ((i%4)==3) fc(0, &n); + if ((i % 4) == 3) fc(0, &n); - if ((hi>>i) & 1) { + if ((hi >> i) & 1) { fc(10, &n); fc(8, &n); // low-high transition } else { fc(8, &n); fc(10, &n); // high-low transition @@ -793,11 +798,11 @@ void CmdHIDsimTAGEx( uint32_t hi, uint32_t lo, int ledcontrol, int numcycles) { } // manchester encode bits 31 to 0 - for (i=31; i>=0; i--) { + for (i = 31; i >= 0; i--) { - if ((i%4)==3) fc(0, &n); + if ((i % 4) == 3) fc(0, &n); - if ((lo>>i) & 1) { + if (( lo >> i ) & 1) { fc(10, &n); fc(8, &n); // low-high transition } else { fc(8, &n); fc(10, &n); // high-low transition From aa3b322d0f86f9b7e1f7ee8ecfefa8b84f805ab5 Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 16 Nov 2018 23:59:14 +0100 Subject: [PATCH 02/12] chg: 'analyze a' - some fpc test changes. --- armsrc/appmain.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index a3b330e84..60229868d 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1080,26 +1080,28 @@ void UsbPacketReceived(uint8_t *packet, int len) { static const char* welcome = "Proxmark3 Serial interface ready\n"; strncat(dest, welcome, sizeof(dest) - strlen(dest) - 1); - sprintf(dest + strlen(dest) - 1, "Arg0 | 0x%" PRIx64 " \n", c->arg[0]); - sprintf(dest + strlen(dest) - 1, "Arg1 | 0x%" PRIx64 " \n", c->arg[1]); - sprintf(dest + strlen(dest) - 1, "Arg2 | 0x%" PRIx64 " \n", c->arg[2]); - sprintf(dest + strlen(dest) - 1, "bytes | 0x%02x 0x%02x 0x%02x 0x%02x \n" + sprintf(dest + strlen(dest) - 1, "| Arg0 0x%" PRIx64 " \n", c->arg[0]); + sprintf(dest + strlen(dest) - 1, "| Arg1 0x%" PRIx64 " \n", c->arg[1]); + sprintf(dest + strlen(dest) - 1, "| Arg2 0x%" PRIx64 " \n", c->arg[2]); + sprintf(dest + strlen(dest) - 1, "| bytes 0x%02x 0x%02x 0x%02x 0x%02x \n" ,c->d.asBytes[0], c->d.asBytes[1], c->d.asBytes[2], c->d.asBytes[3]); -/* + UsbCommand txcmd; for (size_t i=0; i < sizeof(UsbCommand); i++) ((uint8_t*)&txcmd)[i] = 0x00; // Compose the outgoing command frame txcmd.cmd = CMD_DEBUG_PRINT_STRING; - txcmd.arg[0] = len; + txcmd.arg[0] = strlen(dest); txcmd.arg[1] = 0; txcmd.arg[2] = 0; memcpy(txcmd.d.asBytes, dest, USB_CMD_DATA_SIZE); + + usart_init(); usart_writebuffer((uint8_t*)&txcmd, sizeof(UsbCommand)); -*/ - DbpString("Starting to listen"); + + DbpString("Justs send to usart"); LED_A_ON(); /* uint8_t rx[sizeof(UsbCommand)]; @@ -1110,7 +1112,7 @@ void UsbPacketReceived(uint8_t *packet, int len) { DbpString("got 544"); } */ - cmd_send(CMD_DEBUG_PRINT_STRING, strlen(dest), 0, 0, dest, strlen(dest)); + //cmd_send(CMD_DEBUG_PRINT_STRING, strlen(dest), 0, 0, dest, strlen(dest)); //DbpString("finished"); LED_A_OFF(); cmd_send(CMD_ACK,0,0,0,0,0); @@ -1196,7 +1198,6 @@ void UsbPacketReceived(uint8_t *packet, int len) { // arg0 = startindex // arg1 = length bytes to transfer // arg2 = RFU - //Dbprintf("transfer to client parameters: %" PRIu32 " | %" PRIu32 " | %" PRIu32, startidx, numofbytes, c->arg[2]); for (size_t i = 0; i < numofbytes; i += USB_CMD_DATA_SIZE) { len = MIN((numofbytes - i), USB_CMD_DATA_SIZE); @@ -1231,7 +1232,6 @@ void UsbPacketReceived(uint8_t *packet, int len) { if (!FlashInit()) { break; } - //Flash_CheckBusy(BUSY_TIMEOUT); for(size_t i = 0; i < len; i += size) { len = MIN((len - i), size); From f8c33af1daa417875f8712dc737c00d289d0d514 Mon Sep 17 00:00:00 2001 From: Chris Date: Tue, 20 Nov 2018 10:58:32 +0100 Subject: [PATCH 03/12] CHG: FPC connector tests. Device -> Client communications works. Adjust armsrc/Makefile and client/Makefile to include the -DWITH_FPC flag to compile with FPC enabled. --- armsrc/appmain.c | 81 ++++++++++++++++++++------------ client/comms.c | 18 +++++--- common/cmd.c | 4 +- common/usart.c | 114 +++++++++++++++------------------------------- common/usart.h | 2 +- uart/uart_posix.c | 6 ++- uart/uart_win32.c | 17 ++++++- 7 files changed, 123 insertions(+), 119 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 60229868d..eed443599 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -628,7 +628,7 @@ void ListenReaderField(int limit) { void UsbPacketReceived(uint8_t *packet, int len) { UsbCommand *c = (UsbCommand *)packet; - //Dbprintf("received %d bytes, with command: 0x%04x and args: %d %d %d",len,c->cmd,c->arg[0],c->arg[1],c->arg[2]); + //Dbprintf("received %d bytes, with command: 0x%04x and args: %d %d %d", len, c->cmd, c->arg[0], c->arg[1], c->arg[2]); switch(c->cmd) { #ifdef WITH_LF @@ -1075,47 +1075,70 @@ void UsbPacketReceived(uint8_t *packet, int len) { #ifdef WITH_FPC case CMD_FPC_SEND: { + + StartTicks(); + DbpString("Mutual USB/FPC sending from device to client"); + + /* + char at[11] = {'\0'}; + static const char* s_at = "AT+BAUD8\0D\0A"; + strncat(at, s_at, sizeof(at) - strlen(at) - 1); + DbpString("Try AT baud rate setting"); + usart_init(); + int16_t res = usart_writebuffer((uint8_t*)&at, sizeof(at)); + WaitMS(1); + Dbprintf("SEND %d | %c%c%c%c%c%c%c%c%c%c%c", res, at[0], at[1], at[2], at[3], at[4], at[5], at[6], at[7], at[8], at[9], at[10]); + + uint8_t my_rx[20]; + memset(my_rx, 0, sizeof(my_rx)); + res = usart_readbuffer(my_rx, sizeof(my_rx)); + WaitMS(1); + Dbprintf("GOT %d | %c%c%c%c%c%c%c%c", res, my_rx[0], my_rx[1], my_rx[2], my_rx[3], my_rx[4], my_rx[5], my_rx[6], my_rx[7]); + */ + + char dest[USB_CMD_DATA_SIZE] = { '\0' }; - - static const char* welcome = "Proxmark3 Serial interface ready\n"; + static const char* welcome = "Proxmark3 Serial interface via FPC ready\n"; strncat(dest, welcome, sizeof(dest) - strlen(dest) - 1); - - sprintf(dest + strlen(dest) - 1, "| Arg0 0x%" PRIx64 " \n", c->arg[0]); - sprintf(dest + strlen(dest) - 1, "| Arg1 0x%" PRIx64 " \n", c->arg[1]); - sprintf(dest + strlen(dest) - 1, "| Arg2 0x%" PRIx64 " \n", c->arg[2]); sprintf(dest + strlen(dest) - 1, "| bytes 0x%02x 0x%02x 0x%02x 0x%02x \n" - ,c->d.asBytes[0], c->d.asBytes[1], c->d.asBytes[2], c->d.asBytes[3]); + , c->d.asBytes[0] + , c->d.asBytes[1] + , c->d.asBytes[2] + , c->d.asBytes[3] + ); + UsbCommand txcmd = { CMD_DEBUG_PRINT_STRING, { strlen(dest), 0, 0 } }; + memcpy(txcmd.d.asBytes, dest, sizeof(dest)); - UsbCommand txcmd; - for (size_t i=0; i < sizeof(UsbCommand); i++) - ((uint8_t*)&txcmd)[i] = 0x00; - - // Compose the outgoing command frame - txcmd.cmd = CMD_DEBUG_PRINT_STRING; - txcmd.arg[0] = strlen(dest); - txcmd.arg[1] = 0; - txcmd.arg[2] = 0; - memcpy(txcmd.d.asBytes, dest, USB_CMD_DATA_SIZE); - + LED_A_ON(); + usart_init(); usart_writebuffer((uint8_t*)&txcmd, sizeof(UsbCommand)); - - DbpString("Justs send to usart"); - LED_A_ON(); + + //usb + cmd_send(CMD_DEBUG_PRINT_STRING, strlen(dest), 0, 0, dest, strlen(dest)); + LED_A_OFF(); + + /* - uint8_t rx[sizeof(UsbCommand)]; - usart_init(); + uint8_t my_rx[sizeof(UsbCommand)]; while (!BUTTON_PRESS() && !usb_poll_validate_length()) { - WaitMS(1); - if (usart_readbuffer(rx, sizeof(rx)) ) - DbpString("got 544"); + LED_B_INV(); + if (usart_readbuffer(my_rx, sizeof(UsbCommand)) ) { + //UsbPacketReceived(my_rx, sizeof(my_rx)); + + UsbCommand *my = (UsbCommand *)my_rx; + if (mc->cmd > 0 ) { + Dbprintf("received command: 0x%04x and args: %d %d %d", my->cmd, my->arg[0], my->arg[1], my->arg[2]); + } + } } */ + //cmd_send(CMD_DEBUG_PRINT_STRING, strlen(dest), 0, 0, dest, strlen(dest)); - //DbpString("finished"); - LED_A_OFF(); + cmd_send(CMD_ACK,0,0,0,0,0); + StopTicks(); break; } #endif diff --git a/client/comms.c b/client/comms.c index 7204aa1c4..97af830df 100644 --- a/client/comms.c +++ b/client/comms.c @@ -146,7 +146,7 @@ static int getCommand(UsbCommand* response) { // that we weren't necessarily expecting, for example a debug print. //----------------------------------------------------------------------------- static void UsbCommandReceived(UsbCommand* c) { - + switch(c->cmd) { // First check if we are handling a debug message case CMD_DEBUG_PRINT_STRING: { @@ -237,7 +237,7 @@ __attribute__((force_align_arg_pointer)) #endif *uart_communication(void *targ) { communication_arg_t *conn = (communication_arg_t*)targ; - size_t rxlen; + size_t rxlen, totallen = 0; UsbCommand rx; UsbCommand *prx = ℞ @@ -247,22 +247,29 @@ __attribute__((force_align_arg_pointer)) disableAppNap("Proxmark3 polling UART"); #endif - while (conn->run) { rxlen = 0; bool ACK_received = false; + if (uart_receive(sp, (uint8_t *)prx, sizeof(UsbCommand) - (prx - &rx), &rxlen) && rxlen) { prx += rxlen; + totallen += rxlen; - if (prx - &rx < sizeof(UsbCommand)) { - PrintAndLogEx(NORMAL, "Foo %d | %d (will loop)", prx - &rx, rxlen); + if ( totallen < sizeof(UsbCommand)) { + + // iceman: this looping is no working as expected at all. The reassemble of package is nonfunctional. + // solved so far with increasing the timeouts of the serial port configuration. + PrintAndLogEx(NORMAL, "Foo %d | %d (loop)", prx - &rx, rxlen); continue; } + + totallen = 0; UsbCommandReceived(&rx); if (rx.cmd == CMD_ACK) { ACK_received = true; } } + prx = ℞ pthread_mutex_lock(&txBufferMutex); @@ -297,7 +304,6 @@ __attribute__((force_align_arg_pointer)) #if defined(__MACH__) && defined(__APPLE__) enableAppNap(); #endif - pthread_exit(NULL); return NULL; diff --git a/common/cmd.c b/common/cmd.c index 281ff0065..dec095750 100644 --- a/common/cmd.c +++ b/common/cmd.c @@ -56,8 +56,8 @@ uint8_t cmd_send(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void sendlen = usb_write( (uint8_t*)&txcmd, sizeof(UsbCommand) ); #ifdef WITH_FPC - usart_init(); - usart_writebuffer( (uint8_t*)&txcmd, sizeof(UsbCommand) ); +// usart_init(); +// usart_writebuffer( (uint8_t*)&txcmd, sizeof(UsbCommand) ); #endif return sendlen; diff --git a/common/usart.c b/common/usart.c index b58a7cbaa..32ccb5ca1 100644 --- a/common/usart.c +++ b/common/usart.c @@ -1,6 +1,6 @@ //----------------------------------------------------------------------------- // Iceman, July 2018 -// edists by - Anticat, August 2018 +// edits by - Anticat, August 2018 // // 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 @@ -14,7 +14,7 @@ #define AT91_BAUD_RATE 115200 volatile AT91PS_USART pUS1 = AT91C_BASE_US1; -volatile AT91PS_PIO pPIOA = AT91C_BASE_PIOA; +volatile AT91PS_PIO pPIO = AT91C_BASE_PIOA; volatile AT91PS_PDC pPDC = AT91C_BASE_PDC_US1; /* @@ -39,90 +39,53 @@ void usart_close(void) { } */ -static uint8_t outbuf[sizeof(UsbCommand)]; -//static uint8_t inbuf[sizeof(UsbCommand)]; - +static uint8_t us_outbuf[sizeof(UsbCommand)]; /// Reads data from an USART peripheral /// \param data Pointer to the buffer where the received data will be stored. /// \param len Size of the data buffer (in bytes). -inline int usart_readbuffer(uint8_t *data, size_t len) { +inline int16_t usart_readbuffer(uint8_t *data, size_t len) { - pUS1->US_PTSR = AT91C_PDC_TXTEN; - pUS1->US_PTCR = AT91C_PDC_TXTEN; - // Check if the first PDC bank is free if (!(pUS1->US_RCR)) { pUS1->US_RPR = (uint32_t)data; pUS1->US_RCR = len; + + pUS1->US_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTDIS; return 2; } // Check if the second PDC bank is free else if (!(pUS1->US_RNCR)) { pUS1->US_RNPR = (uint32_t)data; pUS1->US_RNCR = len; + + pUS1->US_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTDIS; return 1; } else { return 0; } - - /* - pPDC->PDC_PTSR = AT91C_PDC_RXTEN; - pPDC->PDC_PTCR = AT91C_PDC_RXTEN; - - //check if data is available - if (pPDC->PDC_RCR != 0) return -1; - - memcpy(data, inbuf, len); - - //start next transfer - pPDC->PDC_RNPR = (uint32_t)inbuf; - pPDC->PDC_RNCR = sizeof(inbuf); - - return sizeof(inbuf); - */ } -/* -int16_t usart_writebuffer(uint8_t *data, size_t len) { -// pUS1->US_PTSR = AT91C_PDC_TXTEN; - pUS1->US_PTCR = AT91C_PDC_TXTEN; - - // if buffer is sent - if (pUS1->US_TCR != 0) return -1; - - memcpy(outbuf, data, len); - //start next transfer - pUS1->US_TNPR = (uint32_t)outbuf; - pUS1->US_TNCR = sizeof(outbuf); - - return sizeof(outbuf); -} -*/ - -// works. -// transfer to client +// transfer from device to client inline int16_t usart_writebuffer(uint8_t *data, size_t len) { - pUS1->US_PTSR = AT91C_PDC_TXTEN; - pUS1->US_PTCR = AT91C_PDC_TXTEN; - // Check if the first PDC bank is free if (!(pUS1->US_TCR)) { - - memcpy(outbuf, data, len); - - pUS1->US_TPR = (uint32_t)outbuf; - pUS1->US_TCR = sizeof(outbuf); + memcpy(us_outbuf, data, len); + pUS1->US_TPR = (uint32_t)us_outbuf; + pUS1->US_TCR = sizeof(us_outbuf); + + pUS1->US_PTCR = AT91C_PDC_TXTEN | AT91C_PDC_RXTDIS; return 2; } // Check if the second PDC bank is free else if (!(pUS1->US_TNCR)) { - memcpy(outbuf, data, len); - - pUS1->US_TNPR = (uint32_t)outbuf; - pUS1->US_TNCR = sizeof(outbuf); + memcpy(us_outbuf, data, len); + pUS1->US_TNPR = (uint32_t)us_outbuf; + pUS1->US_TNCR = sizeof(us_outbuf); + + pUS1->US_PTCR = AT91C_PDC_TXTEN | AT91C_PDC_RXTDIS; return 1; } else { return 0; @@ -132,19 +95,20 @@ inline int16_t usart_writebuffer(uint8_t *data, size_t len) { void usart_init(void) { // disable & reset receiver / transmitter for configuration - pUS1->US_CR = (AT91C_US_RSTRX | AT91C_US_RSTTX); + pUS1->US_CR = (AT91C_US_RSTRX | AT91C_US_RSTTX | AT91C_US_RXDIS | AT91C_US_TXDIS); //enable the USART1 Peripheral clock AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_US1); // disable PIO control of receive / transmit pins - pPIOA->PIO_PDR |= (AT91C_PA21_RXD1 | AT91C_PA22_TXD1); + pPIO->PIO_PDR |= (AT91C_PA21_RXD1 | AT91C_PA22_TXD1); // enable peripheral mode A on receive / transmit pins - pPIOA->PIO_ASR |= (AT91C_PA21_RXD1 | AT91C_PA22_TXD1); + pPIO->PIO_ASR |= (AT91C_PA21_RXD1 | AT91C_PA22_TXD1); + pPIO->PIO_BSR = 0; // enable pull-up on receive / transmit pins (see 31.5.1 I/O Lines) - pPIOA->PIO_PPUER |= (AT91C_PA21_RXD1 | AT91C_PA22_TXD1); + pPIO->PIO_PPUER |= (AT91C_PA21_RXD1 | AT91C_PA22_TXD1); // set mode pUS1->US_MR = AT91C_US_USMODE_NORMAL | // normal mode @@ -154,8 +118,19 @@ void usart_init(void) { AT91C_US_NBSTOP_1_BIT | // 1 stop bit AT91C_US_CHMODE_NORMAL; // channel mode: normal + // all interrupts disabled + pUS1->US_IDR = 0xFFFF; + + // iceman, setting 115200 doesn't work. Only speed I got to work is 9600. + // something fishy with the AT91SAM7S512 USART.. Or I missed something + // For a nice detailed sample, interrupt driven but still relevant. + // See https://www.sparkfun.com/datasheets/DevTools/SAM7/at91sam7%20serial%20communications.pdf + // set baudrate to 115200 - pUS1->US_BRGR = (48UL*1000*1000) / (115200*16); + // 115200 * 16 == 1843200 + // + //pUS1->US_BRGR = (48UL*1000*1000) / (9600*16); + pUS1->US_BRGR = 48054841 / (9600 << 4); // Write the Timeguard Register pUS1->US_TTGR = 0; @@ -163,23 +138,6 @@ void usart_init(void) { pUS1->US_FIDI = 0; pUS1->US_IF = 0; - /* - //Empty PDC - pUS1->US_RNPR = (uint32_t)(char *)0; - pUS1->US_RNCR = 0; - pUS1->US_RPR = (uint32_t)(char *)0; - pUS1->US_RCR = 0; - - pUS1->US_TNPR = (uint32_t)(char *)0; - pUS1->US_TNCR = 0; - pUS1->US_TPR = (uint32_t)(char *)0; - pUS1->US_TCR = 0; - */ - - //pUS1->US_PTCR = (AT91C_PDC_RXTEN | AT91C_PDC_TXTEN); - //pUS1->US_PTSR = (AT91C_PDC_RXTEN | AT91C_PDC_TXTEN); - // re-enable receiver / transmitter pUS1->US_CR = (AT91C_US_RXEN | AT91C_US_TXEN); - } \ No newline at end of file diff --git a/common/usart.h b/common/usart.h index 271a24ba4..7e8d3eb7b 100644 --- a/common/usart.h +++ b/common/usart.h @@ -7,6 +7,6 @@ void usart_init(void); void usart_close(void); -int usart_readbuffer(uint8_t *data, size_t len); +int16_t usart_readbuffer(uint8_t *data, size_t len); int16_t usart_writebuffer(uint8_t *data, size_t len); #endif diff --git a/uart/uart_posix.c b/uart/uart_posix.c index 20a577ded..b587d6546 100644 --- a/uart/uart_posix.c +++ b/uart/uart_posix.c @@ -118,8 +118,12 @@ serial_port uart_open(const char* pcPortName) tcflush(sp->fd, TCIOFLUSH); #ifdef WITH_FPC - uart_set_speed(sp, 115200); + if ( uart_set_speed(sp, 115200) ) { printf("[=] UART Setting serial baudrate 115200 [FPC enabled]\n"); + } else { + uart_set_speed(sp, 9600); + printf("[=] UART Setting serial baudrate 9600 [FPC enabled]\n"); + } #else // set speed, works for UBUNTU 14.04 bool success = uart_set_speed(sp, 460800); diff --git a/uart/uart_win32.c b/uart/uart_win32.c index eef350cda..11c90f15f 100644 --- a/uart/uart_win32.c +++ b/uart/uart_win32.c @@ -86,12 +86,20 @@ serial_port uart_open(const char* pcPortName) { } // all zero's configure: no timeout for read/write used. // took settings from libnfc/buses/uart.c +#ifdef WITH_FPC + sp->ct.ReadIntervalTimeout = 1000; + sp->ct.ReadTotalTimeoutMultiplier = 0; + sp->ct.ReadTotalTimeoutConstant = 1500; + sp->ct.WriteTotalTimeoutMultiplier = 1000; + sp->ct.WriteTotalTimeoutConstant = 0; +#else sp->ct.ReadIntervalTimeout = 30; sp->ct.ReadTotalTimeoutMultiplier = 0; sp->ct.ReadTotalTimeoutConstant = 30; sp->ct.WriteTotalTimeoutMultiplier = 30; sp->ct.WriteTotalTimeoutConstant = 0; - +#endif + if (!SetCommTimeouts(sp->hPort, &sp->ct)) { uart_close(sp); printf("[!] UART error while setting comm time outs\n"); @@ -101,7 +109,12 @@ serial_port uart_open(const char* pcPortName) { PurgeComm(sp->hPort, PURGE_RXABORT | PURGE_RXCLEAR); #ifdef WITH_FPC - uart_set_speed(sp, 115200); + if ( uart_set_speed(sp, 115200) ) { + printf("[=] UART Setting serial baudrate 115200 [FPC enabled]\n"); + } else { + uart_set_speed(sp, 9600); + printf("[=] UART Setting serial baudrate 9600 [FPC enabled]\n"); + } #else bool success = uart_set_speed(sp, 460800); if (success) { From e3aebd7985dea1f9337450ccc03c3ea94fc1f08f Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Tue, 20 Nov 2018 16:25:19 +0200 Subject: [PATCH 04/12] get card data --- client/cmdhf14a.c | 40 ++++++++++++++++++++++++++++++++++++++++ client/cmdhf14a.h | 1 + 2 files changed, 41 insertions(+) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 56eadc9cc..77e9473a1 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -190,6 +190,45 @@ int CmdHF14AList(const char *Cmd) { return 0; } +int Hf14443_4aGetCardData(iso14a_card_select_t * card) { + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT, 0, 0}}; + SendCommand(&c); + + UsbCommand resp; + WaitForResponse(CMD_ACK,&resp); + + memcpy(card, (iso14a_card_select_t *)resp.d.asBytes, sizeof(iso14a_card_select_t)); + + uint64_t select_status = resp.arg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision + + if(select_status == 0) { + PrintAndLog("E->iso14443a card select failed"); + return 1; + } + + if(select_status == 2) { + PrintAndLog("E->Card doesn't support iso14443-4 mode"); + return 1; + } + + if(select_status == 3) { + PrintAndLog("E->Card doesn't support standard iso14443-3 anticollision"); + PrintAndLog("\tATQA : %02x %02x", card->atqa[1], card->atqa[0]); + return 1; + } + + PrintAndLog(" UID: %s", sprint_hex(card->uid, card->uidlen)); + PrintAndLog("ATQA: %02x %02x", card->atqa[1], card->atqa[0]); + PrintAndLog(" SAK: %02x [%" PRIu64 "]", card->sak, resp.arg[0]); + if(card->ats_len < 3) { // a valid ATS consists of at least the length byte (TL) and 2 CRC bytes + PrintAndLog("E-> Error ATS length(%d) : %s", card->ats_len, sprint_hex(card->ats, card->ats_len)); + return 1; + } + PrintAndLog(" ATS: %s", sprint_hex(card->ats, card->ats_len)); + + return 0; +} + int CmdHF14AReader(const char *Cmd) { uint32_t cm = ISO14A_CONNECT; @@ -875,6 +914,7 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea return 0; } +// ISO14443-4. 7. Half-duplex block transmission protocol int CmdHF14AAPDU(const char *cmd) { uint8_t data[USB_CMD_DATA_SIZE]; int datalen = 0; diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index 310a2f7d5..723c31100 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -50,6 +50,7 @@ extern int CmdHF14ACUIDs(const char *Cmd); extern int CmdHF14AAntiFuzz(const char *cmd); extern char* getTagInfo(uint8_t uid); +extern int Hf14443_4aGetCardData(iso14a_card_select_t * card); extern int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); extern int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); From b1091c486b803347ea2ef84879e55c00939311c2 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Tue, 20 Nov 2018 16:25:54 +0200 Subject: [PATCH 05/12] emv_pki --- client/emv/emv_pki.c | 14 +++++++++++--- client/emv/emv_pki.h | 2 ++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/client/emv/emv_pki.c b/client/emv/emv_pki.c index 81918ecb9..be4dfa0a7 100644 --- a/client/emv/emv_pki.c +++ b/client/emv/emv_pki.c @@ -27,6 +27,11 @@ #include #include +static bool strictExecution = true; +void PKISetStrictExecution(bool se) { + strictExecution = se; +} + static const unsigned char empty_tlv_value[] = {}; static const struct tlv empty_tlv = {.tag = 0x0, .len = 0, .value = empty_tlv_value}; @@ -108,9 +113,12 @@ static unsigned char *emv_pki_decode_message(const struct emv_pk *enc_pk, 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; + + if (strictExecution) { + crypto_hash_close(ch); + free(data); + return NULL; + } } crypto_hash_close(ch); diff --git a/client/emv/emv_pki.h b/client/emv/emv_pki.h index e37e3c7d2..6fa7b12e9 100644 --- a/client/emv/emv_pki.h +++ b/client/emv/emv_pki.h @@ -21,6 +21,8 @@ #include +extern void PKISetStrictExecution(bool se); + 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); From fb14d665dd90fbe1c58f909262b712c51c5111e0 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Tue, 20 Nov 2018 16:26:35 +0200 Subject: [PATCH 06/12] emvcore --- client/emv/emvcore.c | 84 +++++++++++++++++++++++++++++++++++++++++--- client/emv/emvcore.h | 4 +++ 2 files changed, 83 insertions(+), 5 deletions(-) diff --git a/client/emv/emvcore.c b/client/emv/emvcore.c index b7d7df923..f2acda25c 100644 --- a/client/emv/emvcore.c +++ b/client/emv/emvcore.c @@ -9,6 +9,8 @@ //----------------------------------------------------------------------------- #include "emvcore.h" +#include "emvjson.h" +#include "util_posix.h" // Got from here. Thanks) // https://eftlab.co.uk/index.php/site-map/knowledge-base/211-emv-aid-rid-pix @@ -18,6 +20,13 @@ static const char *PSElist [] = { }; //static const size_t PSElistLen = sizeof(PSElist)/sizeof(char*); +char *TransactionTypeStr[] = { + "MSD", + "VSDC", + "qVCDCMCHIP", + "CDA" +}; + typedef struct { enum CardPSVendor vendor; const char* aid; @@ -257,9 +266,14 @@ int EMVExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool Includ *sw = isw; if (isw != 0x9000) { - if (APDULogging) - PrintAndLogEx(WARNING, "APDU(%02x%02x) ERROR: [%4X] %s", apdu.CLA, apdu.INS, isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xff)); - return 5; + if (APDULogging) { + if (*sw >> 8 == 0x61) { + PrintAndLogEx(ERR, "APDU chaining len:%02x -->", *sw & 0xff); + } else { + PrintAndLogEx(ERR, "APDU(%02x%02x) ERROR: [%4X] %s", apdu.CLA, apdu.INS, isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xff)); + return 5; + } + } } // add to tlv tree @@ -527,7 +541,7 @@ int trSDA(struct tlvdb *tlv) { if (!sda_tlv || sda_tlv->len < 1) { emv_pk_free(issuer_pk); emv_pk_free(pk); - PrintAndLogEx(WARNING, "Error: Can't find input list for Offline Data Authentication. Exit."); + PrintAndLogEx(WARNING, "Can't find input list for Offline Data Authentication. Exit."); return 3; } @@ -539,7 +553,7 @@ int trSDA(struct tlvdb *tlv) { } else { emv_pk_free(issuer_pk); emv_pk_free(pk); - PrintAndLogEx(WARNING, "Error: SSAD verify error"); + PrintAndLogEx(WARNING, "SSAD verify error"); return 4; } @@ -851,3 +865,63 @@ int trCDA(struct tlvdb *tlv, struct tlvdb *ac_tlv, struct tlv *pdol_data_tlv, st emv_pk_free(icc_pk); return 0; } + +int RecoveryCertificates(struct tlvdb *tlvRoot, json_t *root) { + + struct emv_pk *pk = get_ca_pk(tlvRoot); + if (!pk) { + PrintAndLog("ERROR: Key not found. Exit."); + return 1; + } + + struct emv_pk *issuer_pk = emv_pki_recover_issuer_cert(pk, tlvRoot); + if (!issuer_pk) { + emv_pk_free(pk); + PrintAndLog("WARNING: Issuer certificate not found. Exit."); + return 2; + } + 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] + ); + + JsonSaveBufAsHex(root, "$.ApplicationData.RID", issuer_pk->rid, 5); + + char *issuer_pk_c = emv_pk_dump_pk(issuer_pk); + JsonSaveStr(root, "$.ApplicationData.IssuerPublicKeyDec", issuer_pk_c); + JsonSaveBufAsHex(root, "$.ApplicationData.IssuerPublicKeyModulus", issuer_pk->modulus, issuer_pk->mlen); + free(issuer_pk_c); + + struct emv_pk *icc_pk = emv_pki_recover_icc_cert(issuer_pk, tlvRoot, NULL); + if (!icc_pk) { + emv_pk_free(pk); + emv_pk_free(issuer_pk); + PrintAndLog("WARNING: ICC certificate not found. Exit."); + return 2; + } + printf("ICC PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx\n", + icc_pk->rid[0], + icc_pk->rid[1], + icc_pk->rid[2], + icc_pk->rid[3], + icc_pk->rid[4], + icc_pk->index, + icc_pk->serial[0], + icc_pk->serial[1], + icc_pk->serial[2] + ); + + char *icc_pk_c = emv_pk_dump_pk(icc_pk); + JsonSaveStr(root, "$.ApplicationData.ICCPublicKeyDec", icc_pk_c); + JsonSaveBufAsHex(root, "$.ApplicationData.ICCPublicKeyModulus", icc_pk->modulus, icc_pk->mlen); + free(issuer_pk_c); + + return 0; +} diff --git a/client/emv/emvcore.h b/client/emv/emvcore.h index 1e39f696c..5fb6f202c 100644 --- a/client/emv/emvcore.h +++ b/client/emv/emvcore.h @@ -16,6 +16,7 @@ #include #include #include +#include #include "util.h" #include "common.h" #include "ui.h" @@ -37,6 +38,7 @@ enum TransactionType { TT_QVSDCMCHIP, TT_CDA, }; +extern char *TransactionTypeStr[]; typedef struct { uint8_t CLA; @@ -93,6 +95,8 @@ 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); +extern int RecoveryCertificates(struct tlvdb *tlvRoot, json_t *root); + #endif From 6d896ca6039d102d2b2dfc4314297327d4580b19 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Tue, 20 Nov 2018 16:26:54 +0200 Subject: [PATCH 07/12] cmdemv merge --- client/emv/cmdemv.c | 1226 ++++++++++++++++++++++++++++++++----------- 1 file changed, 917 insertions(+), 309 deletions(-) diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index 5ff31770d..21691d0b7 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -8,84 +8,65 @@ // EMV commands //----------------------------------------------------------------------------- +#include +#include "mifare.h" #include "cmdemv.h" +#include "emvjson.h" +#include "emv_pki.h" #include "test/cryptotest.h" +#include "cliparser/cliparser.h" +#include static int CmdHelp(const char *Cmd); -int usage_emv_select(void) { - PrintAndLogEx(NORMAL, "Executes select applet command:\n"); - PrintAndLogEx(NORMAL, "Usage: hf emv select [-s][-k][-a][-t] \n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " -s : select card"); - PrintAndLogEx(NORMAL, " -k : keep field for next command"); - PrintAndLogEx(NORMAL, " -a : show APDU reqests and responses\n"); - PrintAndLogEx(NORMAL, " -t : TLV decode results\n"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf emv select -s a00000000101 -> select card, select applet"); - PrintAndLogEx(NORMAL, " hf emv select -s -t a00000000101 -> select card, select applet, show result in TLV"); - return 0; +#define TLV_ADD(tag, value)( tlvdb_change_or_add_node(tlvRoot, tag, sizeof(value) - 1, (const unsigned char *)value) ) +void ParamLoadDefaults(struct tlvdb *tlvRoot) { + //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"); + //5F2A:(Transaction Currency Code) len:2 + // USD 840, EUR 978, RUR 810, RUB 643, RUR 810(old), UAH 980, AZN 031, n/a 999 + TLV_ADD(0x5F2A, "\x09\x80"); + //9A:(Transaction Date) len:3 + TLV_ADD(0x9A, "\x00\x00\x00"); + //9C:(Transaction Type) len:1 | 00 => Goods and service #01 => Cash + 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"); + //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 + TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC } int CmdHFEMVSelect(const char *cmd) { uint8_t data[APDU_AID_LEN] = {0}; int datalen = 0; - bool activateField = false; - bool leaveSignalON = false; - bool decodeTLV = false; - if (strlen(cmd) < 1) - return usage_emv_select(); - - SetAPDULogging(false); - - int cmdp = 0; - while(param_getchar(cmd, cmdp) != 0x00) { - char c = param_getchar(cmd, cmdp); - if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) - switch (param_getchar_indx(cmd, 1, cmdp)) { - case 'h': - case 'H': - return usage_emv_select(); - case 's': - case 'S': - activateField = true; - break; - case 'k': - case 'K': - leaveSignalON = true; - break; - case 'a': - case 'A': - SetAPDULogging(true); - break; - case 't': - case 'T': - decodeTLV = true; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); - return 1; - } + CLIParserInit("hf emv select", + "Executes select applet command", + "Usage:\n\thf emv select -s a00000000101 -> select card, select applet\n\thf emv select -st a00000000101 -> select card, select applet, show result in TLV\n"); - if (isxdigit(c)) { - switch(param_gethex_to_eol(cmd, cmdp, data, sizeof(data), &datalen)) { - case 1: - PrintAndLogEx(WARNING, "Invalid HEX value."); - return 1; - case 2: - PrintAndLogEx(WARNING, "AID too large."); - return 1; - case 3: - PrintAndLogEx(WARNING, "Hex must have even number of digits."); - return 1; - } - - // we get all the hex to end of line with spaces - break; - } - cmdp++; - } + void* argtable[] = { + arg_param_begin, + arg_lit0("sS", "select", "activate field and select card"), + arg_lit0("kK", "keep", "keep field for next command"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results"), + arg_strx0(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool activateField = arg_get_lit(1); + bool leaveSignalON = arg_get_lit(2); + bool APDULogging = arg_get_lit(3); + bool decodeTLV = arg_get_lit(4); + CLIGetHexWithReturn(5, data, &datalen); + CLIParserFree(); + + SetAPDULogging(APDULogging); // exec uint8_t buf[APDU_RES_LEN] = {0}; @@ -94,7 +75,7 @@ int CmdHFEMVSelect(const char *cmd) { int res = EMVSelect(activateField, leaveSignalON, data, datalen, buf, sizeof(buf), &len, &sw, NULL); if (sw) - PrintAndLogEx(NORMAL, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); if (res) return res; @@ -105,62 +86,30 @@ int CmdHFEMVSelect(const char *cmd) { return 0; } -int usage_emv_search(void) { - PrintAndLogEx(NORMAL, "Tries to select all applets from applet list:\n"); - PrintAndLogEx(NORMAL, "Usage: hf emv search [-s][-k][-a][-t]\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " -s : select card"); - PrintAndLogEx(NORMAL, " -k : keep field for next command"); - PrintAndLogEx(NORMAL, " -a : show APDU reqests and responses\n"); - PrintAndLogEx(NORMAL, " -t : TLV decode results of selected applets\n"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf emv search -s -> select card and search"); - PrintAndLogEx(NORMAL, " hf emv search -s -t -> select card, search and show result in TLV"); - return 0; -} - int CmdHFEMVSearch(const char *cmd) { - bool activateField = false; - bool leaveSignalON = false; - bool decodeTLV = false; + CLIParserInit("hf emv search", + "Tries to select all applets from applet list:\n", + "Usage:\n\thf emv search -s -> select card and search\n\thf emv search -st -> select card, search and show result in TLV\n"); - if (strlen(cmd) < 1) - return usage_emv_search(); + void* argtable[] = { + arg_param_begin, + arg_lit0("sS", "select", "activate field and select card"), + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results of selected applets"), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); - SetAPDULogging(false); + bool activateField = arg_get_lit(1); + bool leaveSignalON = arg_get_lit(2); + bool APDULogging = arg_get_lit(3); + bool decodeTLV = arg_get_lit(4); + CLIParserFree(); + + SetAPDULogging(APDULogging); - int cmdp = 0; - while(param_getchar(cmd, cmdp) != 0x00) { - char c = param_getchar(cmd, cmdp); - if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) - switch (param_getchar_indx(cmd, 1, cmdp)) { - case 'h': - case 'H': - return usage_emv_search(); - case 's': - case 'S': - activateField = true; - break; - case 'k': - case 'K': - leaveSignalON = true; - break; - case 'a': - case 'A': - SetAPDULogging(true); - break; - case 't': - case 'T': - decodeTLV = true; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); - return 1; - } - cmdp++; - } - struct tlvdb *t = NULL; const char *al = "Applets list"; t = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); @@ -170,7 +119,7 @@ int CmdHFEMVSearch(const char *cmd) { return 2; } - PrintAndLogEx(NORMAL, "Search completed."); + PrintAndLog("Search completed."); // print list here if (!decodeTLV) { @@ -182,66 +131,36 @@ int CmdHFEMVSearch(const char *cmd) { return 0; } -int usage_emv_ppse(void) { - PrintAndLogEx(NORMAL, "Executes PSE/PPSE select command. It returns list of applet on the card:\n"); - PrintAndLogEx(NORMAL, "Usage: hf emv pse [-s][-k][-1][-2][-a][-t]\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " -s : select card"); - PrintAndLogEx(NORMAL, " -k : keep field for next command"); - PrintAndLogEx(NORMAL, " -1 : ppse (1PAY.SYS.DDF01)"); - PrintAndLogEx(NORMAL, " -2 : pse (2PAY.SYS.DDF01)"); - PrintAndLogEx(NORMAL, " -a : show APDU reqests and responses\n"); - PrintAndLogEx(NORMAL, " -t : TLV decode results\n"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf emv pse -s -1 -> select, get pse"); - PrintAndLogEx(NORMAL, " hf emv pse -s -k -2 -> select, get ppse, keep field"); - PrintAndLogEx(NORMAL, " hf emv pse -s -t -2 -> select, get ppse, show result in TLV"); - return 0; -} - int CmdHFEMVPPSE(const char *cmd) { - uint8_t PSENum = 2; - bool activateField = false; - bool leaveSignalON = false; - bool decodeTLV = false; + CLIParserInit("hf emv pse", + "Executes PSE/PPSE select command. It returns list of applet on the card:\n", + "Usage:\n\thf emv pse -s1 -> select, get pse\n\thf emv pse -st2 -> select, get ppse, show result in TLV\n"); - if (strlen(cmd) < 1) - return usage_emv_ppse(); - - SetAPDULogging(false); + void* argtable[] = { + arg_param_begin, + arg_lit0("sS", "select", "activate field and select card"), + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("1", "pse", "pse (1PAY.SYS.DDF01) mode"), + arg_lit0("2", "ppse", "ppse (2PAY.SYS.DDF01) mode (default mode)"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results of selected applets"), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); - int cmdp = 0; - while(param_getchar(cmd, cmdp) != 0x00) { - char c = param_getchar(cmd, cmdp); - if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) - switch (tolower(param_getchar_indx(cmd, 1, cmdp))) { - case 'h': - return usage_emv_ppse(); - case 's': - activateField = true; - break; - case 'k': - leaveSignalON = true; - break; - case 'a': - SetAPDULogging(true); - break; - case 't': - decodeTLV = true; - break; - case '1': - PSENum = 1; - break; - case '2': - PSENum = 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); - return 1; - } - cmdp++; - } + bool activateField = arg_get_lit(1); + bool leaveSignalON = arg_get_lit(2); + uint8_t PSENum = 2; + if (arg_get_lit(3)) + PSENum = 1; + if (arg_get_lit(4)) + PSENum = 2; + bool APDULogging = arg_get_lit(5); + bool decodeTLV = arg_get_lit(6); + CLIParserFree(); + + SetAPDULogging(APDULogging); // exec uint8_t buf[APDU_RES_LEN] = {0}; @@ -250,10 +169,11 @@ int CmdHFEMVPPSE(const char *cmd) { int res = EMVSelectPSE(activateField, leaveSignalON, PSENum, buf, sizeof(buf), &len, &sw); if (sw) - PrintAndLogEx(NORMAL, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); if (res) - return res; + return res; + if (decodeTLV) TLVPrintFromBuffer(buf, len); @@ -261,36 +181,457 @@ int CmdHFEMVPPSE(const char *cmd) { return 0; } -int usage_emv_exec(void) { - PrintAndLogEx(NORMAL, "Executes EMV contactless transaction:\n"); - PrintAndLogEx(NORMAL, "Usage: hf emv exec [-s][-a][-t][-f][-v][-c][-x][-g]\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " -s : select card"); - PrintAndLogEx(NORMAL, " -a : show APDU reqests and responses\n"); - PrintAndLogEx(NORMAL, " -t : TLV decode results\n"); - PrintAndLogEx(NORMAL, " -f : force search AID. Search AID instead of execute PPSE.\n"); - PrintAndLogEx(NORMAL, " -v : transaction type - qVSDC or M/Chip.\n"); - PrintAndLogEx(NORMAL, " -c : transaction type - qVSDC or M/Chip plus CDA (SDAD generation).\n"); - PrintAndLogEx(NORMAL, " -x : transaction type - VSDC. For test only. Not a standart behavior.\n"); - PrintAndLogEx(NORMAL, " -g : VISA. generate AC from GPO\n"); - PrintAndLogEx(NORMAL, "By default : transaction type - MSD.\n"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf emv exec -s -a -t -> execute MSD transaction"); - PrintAndLogEx(NORMAL, " hf emv exec -s -a -t -c -> execute CDA transaction"); +int CmdHFEMVGPO(const char *cmd) { + uint8_t data[APDU_RES_LEN] = {0}; + int datalen = 0; + + CLIParserInit("hf emv gpo", + "Executes Get Processing Options command. It returns data in TLV format (0x77 - format2) or plain format (0x80 - format1).\nNeeds a EMV applet to be selected.", + "Usage:\n\thf emv gpo -k -> execute GPO\n" + "\thf emv gpo -t 01020304 -> execute GPO with 4-byte PDOL data, show result in TLV\n" + "\thf emv gpo -pmt 9F 37 04 -> load params from file, make PDOL data from PDOL, execute GPO with PDOL, show result in TLV\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("pP", "params", "load parameters from `emv/defparams.json` file for PDOLdata making from PDOL and parameters"), + arg_lit0("mM", "make", "make PDOLdata from PDOL (tag 9F38) and parameters (by default uses default parameters)"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results of selected applets"), + arg_strx0(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool leaveSignalON = arg_get_lit(1); + bool paramsLoadFromFile = arg_get_lit(2); + bool dataMakeFromPDOL = arg_get_lit(3); + bool APDULogging = arg_get_lit(4); + bool decodeTLV = arg_get_lit(5); + CLIGetHexWithReturn(6, data, &datalen); + CLIParserFree(); + + SetAPDULogging(APDULogging); + + // Init TLV tree + const char *alr = "Root terminal TLV tree"; + struct tlvdb *tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr); + + // calc PDOL + struct tlv *pdol_data_tlv = NULL; + struct tlv data_tlv = { + .tag = 0x83, + .len = datalen, + .value = (uint8_t *)data, + }; + if (dataMakeFromPDOL) { + ParamLoadDefaults(tlvRoot); + + if (paramsLoadFromFile) { + PrintAndLog("Params loading from file..."); + ParamLoadFromJson(tlvRoot); + }; + + pdol_data_tlv = dol_process((const struct tlv *)tlvdb_external(0x9f38, datalen, data), tlvRoot, 0x83); + if (!pdol_data_tlv){ + PrintAndLog("ERROR: can't create PDOL TLV."); + tlvdb_free(tlvRoot); + return 4; + } + } else { + if (paramsLoadFromFile) { + PrintAndLog("WARNING: don't need to load parameters. Sending plain PDOL data..."); + } + pdol_data_tlv = &data_tlv; + } + + size_t pdol_data_tlv_data_len = 0; + 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."); + tlvdb_free(tlvRoot); + return 4; + } + PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); + + // exec + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + int res = EMVGPO(leaveSignalON, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); + + if (pdol_data_tlv != &data_tlv) + free(pdol_data_tlv); + tlvdb_free(tlvRoot); + + if (sw) + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + + if (res) + return res; + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + return 0; } -#define TLV_ADD(tag, value)( tlvdb_add(tlvRoot, tlvdb_fixed(tag, sizeof(value) - 1, (const unsigned char *)value)) ) +int CmdHFEMVReadRecord(const char *cmd) { + uint8_t data[APDU_RES_LEN] = {0}; + int datalen = 0; + + CLIParserInit("hf emv readrec", + "Executes Read Record command. It returns data in TLV format.\nNeeds a bank applet to be selected and sometimes needs GPO to be executed.", + "Usage:\n\thf emv readrec -k 0101 -> read file SFI=01, SFIrec=01\n\thf emv readrec -kt 0201-> read file 0201 and show result in TLV\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results of selected applets"), + arg_strx1(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool leaveSignalON = arg_get_lit(1); + bool APDULogging = arg_get_lit(2); + bool decodeTLV = arg_get_lit(3); + CLIGetHexWithReturn(4, data, &datalen); + CLIParserFree(); + + if (datalen != 2) { + PrintAndLog("ERROR: Command needs to have 2 bytes of data"); + return 1; + } + + SetAPDULogging(APDULogging); + + // exec + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + int res = EMVReadRecord(leaveSignalON, data[0], data[1], buf, sizeof(buf), &len, &sw, NULL); + + if (sw) + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + + if (res) + return res; + + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + return 0; +} + +int CmdHFEMVAC(const char *cmd) { + uint8_t data[APDU_RES_LEN] = {0}; + int datalen = 0; + + CLIParserInit("hf emv genac", + "Generate Application Cryptogram command. It returns data in TLV format .\nNeeds a EMV applet to be selected and GPO to be executed.", + "Usage:\n\thf emv genac -k 0102 -> generate AC with 2-byte CDOLdata and keep field ON after command\n" + "\thf emv genac -t 01020304 -> generate AC with 4-byte CDOL data, show result in TLV\n" + "\thf emv genac -Daac 01020304 -> generate AC with 4-byte CDOL data and terminal decision 'declined'\n" + "\thf emv genac -pmt 9F 37 04 -> load params from file, make CDOL data from CDOL, generate AC with CDOL, show result in TLV"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("cC", "cda", "executes CDA transaction. Needs to get SDAD in results."), + arg_str0("dD", "decision", "", "Terminal decision. aac - declined, tc - approved, arqc - online authorisation requested"), + arg_lit0("pP", "params", "load parameters from `emv/defparams.json` file for CDOLdata making from CDOL and parameters"), + arg_lit0("mM", "make", "make CDOLdata from CDOL (tag 8C and 8D) and parameters (by default uses default parameters)"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results of selected applets"), + arg_strx1(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, false); + + bool leaveSignalON = arg_get_lit(1); + bool trTypeCDA = arg_get_lit(2); + uint8_t termDecision = 0xff; + if (arg_get_str_len(3)) { + if (!strncmp(arg_get_str(3)->sval[0], "aac", 4)) + termDecision = EMVAC_AAC; + if (!strncmp(arg_get_str(3)->sval[0], "tc", 4)) + termDecision = EMVAC_TC; + if (!strncmp(arg_get_str(3)->sval[0], "arqc", 4)) + termDecision = EMVAC_ARQC; + + if (termDecision == 0xff) { + PrintAndLog("ERROR: can't find terminal decision '%s'", arg_get_str(3)->sval[0]); + return 1; + } + } else { + termDecision = EMVAC_TC; + } + if (trTypeCDA) + termDecision = termDecision | EMVAC_CDAREQ; + bool paramsLoadFromFile = arg_get_lit(4); + bool dataMakeFromCDOL = arg_get_lit(5); + bool APDULogging = arg_get_lit(6); + bool decodeTLV = arg_get_lit(7); + CLIGetHexWithReturn(8, data, &datalen); + CLIParserFree(); + + SetAPDULogging(APDULogging); + + // Init TLV tree + const char *alr = "Root terminal TLV tree"; + struct tlvdb *tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr); + + // calc CDOL + struct tlv *cdol_data_tlv = NULL; + struct tlv data_tlv = { + .tag = 0x01, + .len = datalen, + .value = (uint8_t *)data, + }; + + if (dataMakeFromCDOL) { + ParamLoadDefaults(tlvRoot); + + if (paramsLoadFromFile) { + PrintAndLog("Params loading from file..."); + ParamLoadFromJson(tlvRoot); + }; + + cdol_data_tlv = dol_process((const struct tlv *)tlvdb_external(0x8c, datalen, data), tlvRoot, 0x01); // 0x01 - dummy tag + if (!cdol_data_tlv){ + PrintAndLog("ERROR: can't create CDOL TLV."); + tlvdb_free(tlvRoot); + return 4; + } + } else { + if (paramsLoadFromFile) { + PrintAndLog("WARNING: don't need to load parameters. Sending plain CDOL data..."); + } + cdol_data_tlv = &data_tlv; + } + + PrintAndLog("CDOL data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len)); + + // exec + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + int res = EMVAC(leaveSignalON, termDecision, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); + + if (cdol_data_tlv != &data_tlv) + free(cdol_data_tlv); + tlvdb_free(tlvRoot); + + if (sw) + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + + if (res) + return res; + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + return 0; +} + +int CmdHFEMVGenerateChallenge(const char *cmd) { + + CLIParserInit("hf emv challenge", + "Executes Generate Challenge command. It returns 4 or 8-byte random number from card.\nNeeds a EMV applet to be selected and GPO to be executed.", + "Usage:\n\thf emv challenge -> get challenge\n\thf emv challenge -k -> get challenge, keep fileld ON\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool leaveSignalON = arg_get_lit(1); + bool APDULogging = arg_get_lit(2); + CLIParserFree(); + + SetAPDULogging(APDULogging); + + // exec + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + int res = EMVGenerateChallenge(leaveSignalON, buf, sizeof(buf), &len, &sw, NULL); + + if (sw) + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + + if (res) + return res; + + PrintAndLog("Challenge: %s", sprint_hex(buf, len)); + + if (len != 4 && len != 8) + PrintAndLog("WARNING: length of challenge must be 4 or 8, but it %d", len); + + return 0; +} + +int CmdHFEMVInternalAuthenticate(const char *cmd) { + uint8_t data[APDU_RES_LEN] = {0}; + int datalen = 0; + + CLIParserInit("hf emv intauth", + "Generate Internal Authenticate command. Usually needs 4-byte random number. It returns data in TLV format .\nNeeds a EMV applet to be selected and GPO to be executed.", + "Usage:\n\thf emv intauth -k 01020304 -> execute Internal Authenticate with 4-byte DDOLdata and keep field ON after command\n" + "\thf emv intauth -t 01020304 -> execute Internal Authenticate with 4-byte DDOL data, show result in TLV\n" + "\thf emv intauth -pmt 9F 37 04 -> load params from file, make DDOL data from DDOL, Internal Authenticate with DDOL, show result in TLV"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("pP", "params", "load parameters from `emv/defparams.json` file for DDOLdata making from DDOL and parameters"), + arg_lit0("mM", "make", "make DDOLdata from DDOL (tag 9F49) and parameters (by default uses default parameters)"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results of selected applets"), + arg_strx1(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, false); + + bool leaveSignalON = arg_get_lit(1); + bool paramsLoadFromFile = arg_get_lit(2); + bool dataMakeFromDDOL = arg_get_lit(3); + bool APDULogging = arg_get_lit(4); + bool decodeTLV = arg_get_lit(5); + CLIGetHexWithReturn(6, data, &datalen); + CLIParserFree(); + + SetAPDULogging(APDULogging); + + // Init TLV tree + const char *alr = "Root terminal TLV tree"; + struct tlvdb *tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr); + + // calc DDOL + struct tlv *ddol_data_tlv = NULL; + struct tlv data_tlv = { + .tag = 0x01, + .len = datalen, + .value = (uint8_t *)data, + }; + + if (dataMakeFromDDOL) { + ParamLoadDefaults(tlvRoot); + + if (paramsLoadFromFile) { + PrintAndLog("Params loading from file..."); + ParamLoadFromJson(tlvRoot); + }; + + ddol_data_tlv = dol_process((const struct tlv *)tlvdb_external(0x9f49, datalen, data), tlvRoot, 0x01); // 0x01 - dummy tag + if (!ddol_data_tlv){ + PrintAndLog("ERROR: can't create DDOL TLV."); + tlvdb_free(tlvRoot); + return 4; + } + } else { + if (paramsLoadFromFile) { + PrintAndLog("WARNING: don't need to load parameters. Sending plain DDOL data..."); + } + ddol_data_tlv = &data_tlv; + } + + PrintAndLog("DDOL data[%d]: %s", ddol_data_tlv->len, sprint_hex(ddol_data_tlv->value, ddol_data_tlv->len)); + + // exec + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + int res = EMVInternalAuthenticate(leaveSignalON, data, datalen, buf, sizeof(buf), &len, &sw, NULL); + + if (ddol_data_tlv != &data_tlv) + free(ddol_data_tlv); + tlvdb_free(tlvRoot); + + if (sw) + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + + if (res) + return res; + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + return 0; +} + #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; +void InitTransactionParameters(struct tlvdb *tlvRoot, bool paramLoadJSON, enum TransactionType TrType, bool GenACGPO) { + + ParamLoadDefaults(tlvRoot); + if (paramLoadJSON) { + PrintAndLog("* * Transaction parameters loading from JSON..."); + ParamLoadFromJson(tlvRoot); + } + + //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 + 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: + break; + } +} + +void ProcessGPOResponseFormat1(struct tlvdb *tlvRoot, uint8_t *buf, size_t len, bool decodeTLV) { + if (buf[0] == 0x80) { + if (decodeTLV){ + PrintAndLog("GPO response format1:"); + TLVPrintFromBuffer(buf, len); + } + + 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); + } +} + +int CmdHFEMVExec(const char *cmd) { uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; uint16_t sw = 0; @@ -304,46 +645,46 @@ int CmdHFEMVExec(const char *cmd) { struct tlvdb *tlvSelect = NULL; struct tlvdb *tlvRoot = NULL; struct tlv *pdol_data_tlv = NULL; - if (strlen(cmd) < 1) - return usage_emv_exec(); + + CLIParserInit("hf emv exec", + "Executes EMV contactless transaction", + "Usage:\n\thf emv exec -sat -> select card, execute MSD transaction, show APDU and TLV\n" + "\thf emv exec -satc -> select card, execute CDA transaction, show APDU and TLV\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("sS", "select", "activate field and select card."), + arg_lit0("aA", "apdu", "show APDU reqests and responses."), + arg_lit0("tT", "tlv", "TLV decode results."), + arg_lit0("jJ", "jload", "Load transaction parameters from `emv/defparams.json` file."), + arg_lit0("fF", "forceaid", "Force search AID. Search AID instead of execute PPSE."), + arg_rem("By default:", "Transaction type - MSD"), + arg_lit0("vV", "qvsdc", "Transaction type - qVSDC or M/Chip."), + arg_lit0("cC", "qvsdccda", "Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)."), + arg_lit0("xX", "vsdc", "Transaction type - VSDC. For test only. Not a standart behavior."), + arg_lit0("gG", "acgpo", "VISA. generate AC from GPO."), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); - int cmdp = 0; - while(param_getchar(cmd, cmdp) != 0x00) { - char c = param_getchar(cmd, cmdp); - if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) - switch (tolower(param_getchar_indx(cmd, 1, cmdp))) { - case 'h': - return usage_emv_exec(); - case 's': - activateField = true; - break; - case 'a': - showAPDU = true; - break; - case 't': - decodeTLV = true; - break; - case 'f': - forceSearch = true; - break; - case 'x': - TrType = TT_VSDC; - break; - case 'v': + bool activateField = arg_get_lit(1); + bool showAPDU = arg_get_lit(2); + bool decodeTLV = arg_get_lit(3); + bool paramLoadJSON = arg_get_lit(4); + bool forceSearch = arg_get_lit(5); + + enum TransactionType TrType = TT_MSD; + if (arg_get_lit(6)) TrType = TT_QVSDCMCHIP; - break; - case 'c': + if (arg_get_lit(7)) TrType = TT_CDA; - break; - case 'g': - GenACGPO = true; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); - return 1; - } - cmdp++; - } + if (arg_get_lit(8)) + TrType = TT_VSDC; + + bool GenACGPO = arg_get_lit(9); + CLIParserFree(); + + SetAPDULogging(showAPDU); // init applets list tree const char *al = "Applets list"; @@ -399,50 +740,10 @@ int CmdHFEMVExec(const char *cmd) { if (decodeTLV) TLVPrintFromBuffer(buf, len); + PrintAndLog("* Selected."); - PrintAndLogEx(NORMAL, "* Selected."); - PrintAndLogEx(NORMAL, "\n* Init transaction parameters."); - - //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 - 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"); - //5F2A:(Transaction Currency Code) len:2 - // USD 840, EUR 978, RUR 810, RUB 643, RUR 810(old), UAH 980, AZN 031, n/a 999 - TLV_ADD(0x5F2A, "\x09\x80"); - //9A:(Transaction Date) len:3 - TLV_ADD(0x9A, "\x00\x00\x00"); - //9C:(Transaction Type) len:1 | 00 => Goods and service #01 => Cash - 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"); - + PrintAndLog("\n* Init transaction parameters."); + InitTransactionParameters(tlvRoot, paramLoadJSON, TrType, GenACGPO); TLVPrintFromTLV(tlvRoot); // TODO delete!!! PrintAndLogEx(NORMAL, "\n* Calc PDOL."); @@ -472,33 +773,7 @@ int CmdHFEMVExec(const char *cmd) { } // process response template format 1 [id:80 2b AIP + x4b AFL] and format 2 [id:77 TLV] - if (buf[0] == 0x80) { - if (decodeTLV){ - PrintAndLogEx(NORMAL, "GPO response format1:"); - TLVPrintFromBuffer(buf, len); - } - - if (len < 4 || (len - 4) % 4) { - PrintAndLogEx(WARNING, "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){ - PrintAndLogEx(NORMAL, "\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); - } + ProcessGPOResponseFormat1(tlvRoot, buf, len, decodeTLV); // extract PAN from track2 { @@ -583,7 +858,7 @@ int CmdHFEMVExec(const char *cmd) { struct tlvdb *oda = tlvdb_fixed(0x21, ODAiListLen, ODAiList); // not a standard tag tlvdb_add(tlvRoot, oda); PrintAndLogEx(NORMAL, "* Input list for Offline Data Authentication added to TLV. len=%d \n", ODAiListLen); - } + } // get AIP const struct tlv *AIPtlv = tlvdb_get(tlvRoot, 0x82, NULL); @@ -796,6 +1071,332 @@ int CmdHFEMVExec(const char *cmd) { return 0; } +int CmdHFEMVScan(const char *cmd) { + uint8_t AID[APDU_AID_LEN] = {0}; + size_t AIDlen = 0; + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + int res; + json_t *root; + json_error_t error; + + CLIParserInit("hf emv scan", + "Scan EMV card and save it contents to a file.", + "It executes EMV contactless transaction and saves result to a file which can be used for emulation\n" + "Usage:\n\thf emv scan -at -> scan MSD transaction mode and show APDU and TLV\n" + "\thf emv scan -c -> scan CDA transaction mode\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("aA", "apdu", "show APDU reqests and responses."), + arg_lit0("tT", "tlv", "TLV decode results."), + arg_lit0("eE", "extract", "Extract TLV elements and fill Application Data"), + arg_lit0("jJ", "jload", "Load transaction parameters from `emv/defparams.json` file."), + arg_rem("By default:", "Transaction type - MSD"), + arg_lit0("vV", "qvsdc", "Transaction type - qVSDC or M/Chip."), + arg_lit0("cC", "qvsdccda", "Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)."), + arg_lit0("xX", "vsdc", "Transaction type - VSDC. For test only. Not a standart behavior."), + arg_lit0("gG", "acgpo", "VISA. generate AC from GPO."), + arg_lit0("mM", "merge", "Merge output file with card's data. (warning: the file may be corrupted!)"), + arg_str1(NULL, NULL, "output.json", "JSON output file name"), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool showAPDU = arg_get_lit(1); + bool decodeTLV = arg_get_lit(2); + bool extractTLVElements = arg_get_lit(3); + bool paramLoadJSON = arg_get_lit(4); + + enum TransactionType TrType = TT_MSD; + if (arg_get_lit(6)) + TrType = TT_QVSDCMCHIP; + if (arg_get_lit(7)) + TrType = TT_CDA; + if (arg_get_lit(8)) + TrType = TT_VSDC; + + bool GenACGPO = arg_get_lit(9); + bool MergeJSON = arg_get_lit(10); + uint8_t relfname[250] ={0}; + char *crelfname = (char *)relfname; + int relfnamelen = 0; + CLIGetStrWithReturn(11, relfname, &relfnamelen); + CLIParserFree(); + + SetAPDULogging(showAPDU); + + // current path + file name + if (!strstr(crelfname, ".json")) + strcat(crelfname, ".json"); + char fname[strlen(get_my_executable_directory()) + strlen(crelfname) + 1]; + strcpy(fname, get_my_executable_directory()); + strcat(fname, crelfname); + + if (MergeJSON) { + root = json_load_file(fname, 0, &error); + if (!root) { + PrintAndLog("ERROR: json error on line %d: %s", error.line, error.text); + return 1; + } + + if (!json_is_object(root)) { + PrintAndLog("ERROR: Invalid json format. root must be an object."); + return 1; + } + } else { + root = json_object(); + } + + // drop field at start + DropField(); + + // iso 14443 select + PrintAndLog("--> GET UID, ATS."); + + iso14a_card_select_t card; + if (Hf14443_4aGetCardData(&card)) { + return 2; + } + + JsonSaveStr(root, "$.File.Created", "proxmark3 `hf emv scan`"); + + JsonSaveStr(root, "$.Card.Communication", "iso14443-4a"); + JsonSaveBufAsHex(root, "$.Card.UID", (uint8_t *)&card.uid, card.uidlen); + JsonSaveHex(root, "$.Card.ATQA", card.atqa[0] + (card.atqa[1] << 2), 2); + JsonSaveHex(root, "$.Card.SAK", card.sak, 0); + JsonSaveBufAsHex(root, "$.Card.ATS", (uint8_t *)card.ats, card.ats_len); + + // init applets list tree + const char *al = "Applets list"; + struct tlvdb *tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); + + // EMV PPSE + PrintAndLog("--> PPSE."); + res = EMVSelectPSE(true, true, 2, buf, sizeof(buf), &len, &sw); + + if (!res && sw == 0x9000){ + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + JsonSaveBufAsHex(root, "$.PPSE.AID", (uint8_t *)"2PAY.SYS.DDF01", 14); + + struct tlvdb *fci = tlvdb_parse_multi(buf, len); + if (extractTLVElements) + JsonSaveTLVTree(root, root, "$.PPSE.FCITemplate", fci); + else + JsonSaveTLVTreeElm(root, "$.PPSE.FCITemplate", fci, true, true, false); + JsonSaveTLVValue(root, "$.Application.KernelID", tlvdb_find_full(fci, 0x9f2a)); + tlvdb_free(fci); + } + + res = EMVSearchPSE(false, true, decodeTLV, tlvSelect); + + // check PPSE and select application id + if (!res) { + TLVPrintAIDlistFromSelectTLV(tlvSelect); + } else { + // EMV SEARCH with AID list + SetAPDULogging(false); + PrintAndLog("--> AID search."); + if (EMVSearch(false, true, decodeTLV, tlvSelect)) { + PrintAndLog("E->Can't found any of EMV AID. Exit..."); + tlvdb_free(tlvSelect); + DropField(); + return 3; + } + + // check search and select application id + TLVPrintAIDlistFromSelectTLV(tlvSelect); + } + + // EMV SELECT application + SetAPDULogging(showAPDU); + EMVSelectApplication(tlvSelect, AID, &AIDlen); + + tlvdb_free(tlvSelect); + + if (!AIDlen) { + PrintAndLog("Can't select AID. EMV AID not found. Exit..."); + DropField(); + return 4; + } + + JsonSaveBufAsHex(root, "$.Application.AID", AID, AIDlen); + + // Init TLV tree + const char *alr = "Root terminal TLV tree"; + struct tlvdb *tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr); + + // EMV SELECT applet + + PrintAndLog("\n-->Selecting AID:%s.", sprint_hex_inrow(AID, AIDlen)); + SetAPDULogging(showAPDU); + res = EMVSelect(false, true, AID, AIDlen, buf, sizeof(buf), &len, &sw, tlvRoot); + + if (res) { + PrintAndLog("E->Can't select AID (%d). Exit...", res); + tlvdb_free(tlvRoot); + DropField(); + return 5; + } + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + // save mode + if (tlvdb_get(tlvRoot, 0x9f38, NULL)) { + JsonSaveStr(root, "$.Application.Mode", TransactionTypeStr[TrType]); + } + + struct tlvdb *fci = tlvdb_parse_multi(buf, len); + if (extractTLVElements) + JsonSaveTLVTree(root, root, "$.Application.FCITemplate", fci); + else + JsonSaveTLVTreeElm(root, "$.Application.FCITemplate", fci, true, true, false); + tlvdb_free(fci); + + // create transaction parameters + PrintAndLog("-->Init transaction parameters."); + InitTransactionParameters(tlvRoot, paramLoadJSON, TrType, GenACGPO); + + PrintAndLog("-->Calc PDOL."); + struct tlv *pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83); + if (!pdol_data_tlv){ + PrintAndLog("E->Can't create PDOL TLV."); + tlvdb_free(tlvRoot); + DropField(); + return 6; + } + + size_t pdol_data_tlv_data_len; + unsigned char *pdol_data_tlv_data = tlv_encode(pdol_data_tlv, &pdol_data_tlv_data_len); + if (!pdol_data_tlv_data) { + PrintAndLog("E->Can't create PDOL data."); + tlvdb_free(tlvRoot); + DropField(); + return 6; + } + PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); + + PrintAndLog("-->GPO."); + res = EMVGPO(true, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); + + free(pdol_data_tlv_data); + free(pdol_data_tlv); + + if (res) { + PrintAndLog("GPO error(%d): %4x. Exit...", res, sw); + tlvdb_free(tlvRoot); + DropField(); + return 7; + } + ProcessGPOResponseFormat1(tlvRoot, buf, len, decodeTLV); + + struct tlvdb *gpofci = tlvdb_parse_multi(buf, len); + if (extractTLVElements) + JsonSaveTLVTree(root, root, "$.Application.GPO", gpofci); + else + JsonSaveTLVTreeElm(root, "$.Application.GPO", gpofci, true, true, false); + + JsonSaveTLVValue(root, "$.ApplicationData.AIP", tlvdb_find_full(gpofci, 0x82)); + JsonSaveTLVValue(root, "$.ApplicationData.AFL", tlvdb_find_full(gpofci, 0x94)); + + tlvdb_free(gpofci); + + PrintAndLog("-->Read records from AFL."); + const struct tlv *AFL = tlvdb_get(tlvRoot, 0x94, NULL); + + while(AFL && AFL->len) { + if (AFL->len % 4) { + PrintAndLog("E->Wrong AFL length: %d", AFL->len); + break; + } + + json_t *sfijson = json_path_get(root, "$.Application.Records"); + if (!sfijson) { + json_t *app = json_path_get(root, "$.Application"); + json_object_set_new(app, "Records", json_array()); + + sfijson = json_path_get(root, "$.Application.Records"); + } + if (!json_is_array(sfijson)) { + PrintAndLog("E->Internal logic error. `$.Application.Records` is not an array."); + break; + } + for (int i = 0; i < AFL->len / 4; i++) { + uint8_t SFI = AFL->value[i * 4 + 0] >> 3; + uint8_t SFIstart = AFL->value[i * 4 + 1]; + uint8_t SFIend = AFL->value[i * 4 + 2]; + uint8_t SFIoffline = AFL->value[i * 4 + 3]; + + PrintAndLog("--->SFI[%02x] start:%02x end:%02x offline:%02x", SFI, SFIstart, SFIend, SFIoffline); + if (SFI == 0 || SFI == 31 || SFIstart == 0 || SFIstart > SFIend) { + PrintAndLog("SFI ERROR! Skipped..."); + continue; + } + + for(int n = SFIstart; n <= SFIend; n++) { + PrintAndLog("---->SFI[%02x] %d", SFI, n); + + res = EMVReadRecord(true, SFI, n, buf, sizeof(buf), &len, &sw, tlvRoot); + if (res) { + PrintAndLog("E->SFI[%02x]. APDU error %4x", SFI, sw); + continue; + } + + if (decodeTLV) { + TLVPrintFromBuffer(buf, len); + PrintAndLog(""); + } + + json_t *jsonelm = json_object(); + json_array_append_new(sfijson, jsonelm); + + JsonSaveHex(jsonelm, "SFI", SFI, 1); + JsonSaveHex(jsonelm, "RecordNum", n, 1); + JsonSaveHex(jsonelm, "Offline", SFIoffline, 1); + + struct tlvdb *rsfi = tlvdb_parse_multi(buf, len); + if (extractTLVElements) + JsonSaveTLVTree(root, jsonelm, "$.Data", rsfi); + else + JsonSaveTLVTreeElm(jsonelm, "$.Data", rsfi, true, true, false); + tlvdb_free(rsfi); + } + } + + break; + } + + // getting certificates + if (tlvdb_get(tlvRoot, 0x90, NULL)) { + PrintAndLog("-->Recovering certificates."); + PKISetStrictExecution(false); + RecoveryCertificates(tlvRoot, root); + PKISetStrictExecution(true); + } + + // free tlv object + tlvdb_free(tlvRoot); + + // DropField + DropField(); + + res = json_dump_file(root, fname, JSON_INDENT(2)); + if (res) { + PrintAndLog("ERROR: can't save the file: %s", fname); + return 200; + } + PrintAndLog("File `%s` saved.", fname); + + // free json object + json_decref(root); + + return 0; +} + int usage_emv_getrnd(void){ PrintAndLogEx(NORMAL, "retrieve the UN number from a terminal"); PrintAndLogEx(NORMAL, "Usage: hf emv getrnd [h]"); @@ -822,12 +1423,19 @@ int CmdHfEMVList(const char *Cmd) { int CmdHFEMVTest(const char *cmd) { return ExecuteCryptoTests(true); } + static command_t CommandTable[] = { {"help", CmdHelp, 1, "This help"}, {"exec", CmdHFEMVExec, 0, "Executes EMV contactless transaction."}, {"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."}, + {"gpo", CmdHFEMVGPO, 0, "Execute GetProcessingOptions."}, + {"readrec", CmdHFEMVReadRecord, 0, "Read files from card."}, + {"genac", CmdHFEMVAC, 0, "Generate ApplicationCryptogram."}, + {"challenge", CmdHFEMVGenerateChallenge, 0, "Generate challenge."}, + {"intauth", CmdHFEMVInternalAuthenticate, 0, "Internal authentication."}, + {"scan", CmdHFEMVScan, 0, "Scan EMV card and save it contents to json file for emulator."}, {"test", CmdHFEMVTest, 0, "Crypto logic test."}, /* {"getrng", CmdHfEMVGetrng, 0, "get random number from terminal"}, From d7d681ab51c74900282ae88df09f79c5ed372ca1 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Tue, 20 Nov 2018 17:28:41 +0200 Subject: [PATCH 08/12] cmdhf apdu move to argtable --- client/cmdhf14a.c | 66 +++++++++++++---------------------------------- 1 file changed, 18 insertions(+), 48 deletions(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 77e9473a1..42a941ffa 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -171,13 +171,6 @@ int usage_hf_14a_info(void){ PrintAndLogEx(NORMAL, " n test for nack bug"); return 0; } -int usage_hf_14a_apdu(void) { - PrintAndLogEx(NORMAL, "Usage: hf 14a apdu [-s] [-k] [-t] "); - PrintAndLogEx(NORMAL, " -s activate field and select card"); - PrintAndLogEx(NORMAL, " -k leave the signal field ON after receive response"); - PrintAndLogEx(NORMAL, " -t executes TLV decoder if it possible. TODO!!!!"); - return 0; -} int usage_hf_14a_antifuzz(void) { PrintAndLogEx(NORMAL, "Usage: hf 14a antifuzz [4|7|10]"); PrintAndLogEx(NORMAL, " determine which anticollision phase the command will target."); @@ -921,51 +914,28 @@ int CmdHF14AAPDU(const char *cmd) { bool activateField = false; bool leaveSignalON = false; bool decodeTLV = false; - - if (strlen(cmd) < 2) return usage_hf_14a_apdu(); - int cmdp = 0; - while(param_getchar(cmd, cmdp) != 0x00) { - char c = param_getchar(cmd, cmdp); - if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) - switch (tolower(param_getchar_indx(cmd, 1, cmdp))) { - case 'h': - return usage_hf_14a_apdu(); - case 's': - activateField = true; - break; - case 'k': - leaveSignalON = true; - break; - case 't': - decodeTLV = true; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); - return 1; - } + CLIParserInit("hf 14a apdu", + "Sends an ISO 7816-4 APDU via ISO 14443-4 block transmission protocol (T=CL)", + "Sample:\n\thf 14a apdu -st 00A404000E325041592E5359532E444446303100\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("sS", "select", "activate field and select card"), + arg_lit0("kK", "keep", "leave the signal field ON after receive response"), + arg_lit0("tT", "tlv", "executes TLV decoder if it possible"), + arg_strx1(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, false); - if (isxdigit(c)) { + activateField = arg_get_lit(1); + leaveSignalON = arg_get_lit(2); + decodeTLV = arg_get_lit(3); // len = data + PCB(1b) + CRC(2b) - switch(param_gethex_to_eol(cmd, cmdp, data, sizeof(data) - 1 - 2, &datalen)) { - case 1: - PrintAndLogEx(WARNING, "invalid HEX value."); - return 1; - case 2: - PrintAndLogEx(WARNING, "APDU too large."); - return 1; - case 3: - PrintAndLogEx(WARNING, "hex must have even number of digits."); - return 1; - } - - // we get all the hex to end of line with spaces - break; - } - - cmdp++; - } + CLIGetHexBLessWithReturn(4, data, &datalen, 1 + 2); +CLIParserFree(); PrintAndLogEx(NORMAL, ">>>>[%s%s%s] %s", activateField ? "sel ": "", leaveSignalON ? "keep ": "", decodeTLV ? "TLV": "", sprint_hex(data, datalen)); int res = ExchangeAPDU14a(data, datalen, activateField, leaveSignalON, data, USB_CMD_DATA_SIZE, &datalen); From 9df1d14ca42dbbd272e86f4d5033632e5ac38ebd Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Tue, 20 Nov 2018 17:29:17 +0200 Subject: [PATCH 09/12] printandlogex in hf emv commands --- client/emv/cmdemv.c | 102 ++++++++++++++++++++++---------------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index 21691d0b7..5a64892c4 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -75,7 +75,7 @@ int CmdHFEMVSelect(const char *cmd) { int res = EMVSelect(activateField, leaveSignalON, data, datalen, buf, sizeof(buf), &len, &sw, NULL); if (sw) - PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); if (res) return res; @@ -119,7 +119,7 @@ int CmdHFEMVSearch(const char *cmd) { return 2; } - PrintAndLog("Search completed."); + PrintAndLogEx(SUCCESS, "Search completed."); // print list here if (!decodeTLV) { @@ -169,7 +169,7 @@ int CmdHFEMVPPSE(const char *cmd) { int res = EMVSelectPSE(activateField, leaveSignalON, PSENum, buf, sizeof(buf), &len, &sw); if (sw) - PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); if (res) return res; @@ -228,19 +228,19 @@ int CmdHFEMVGPO(const char *cmd) { ParamLoadDefaults(tlvRoot); if (paramsLoadFromFile) { - PrintAndLog("Params loading from file..."); + PrintAndLogEx(INFO, "Params loading from file..."); ParamLoadFromJson(tlvRoot); }; pdol_data_tlv = dol_process((const struct tlv *)tlvdb_external(0x9f38, datalen, data), tlvRoot, 0x83); if (!pdol_data_tlv){ - PrintAndLog("ERROR: can't create PDOL TLV."); + PrintAndLogEx(ERR, "Can't create PDOL TLV."); tlvdb_free(tlvRoot); return 4; } } else { if (paramsLoadFromFile) { - PrintAndLog("WARNING: don't need to load parameters. Sending plain PDOL data..."); + PrintAndLogEx(WARNING, "Don't need to load parameters. Sending plain PDOL data..."); } pdol_data_tlv = &data_tlv; } @@ -248,11 +248,11 @@ int CmdHFEMVGPO(const char *cmd) { size_t pdol_data_tlv_data_len = 0; 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."); + PrintAndLogEx(ERR, "Can't create PDOL data."); tlvdb_free(tlvRoot); return 4; } - PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); + PrintAndLogEx(INFO, "PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); // exec uint8_t buf[APDU_RES_LEN] = {0}; @@ -265,7 +265,7 @@ int CmdHFEMVGPO(const char *cmd) { tlvdb_free(tlvRoot); if (sw) - PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); if (res) return res; @@ -301,7 +301,7 @@ int CmdHFEMVReadRecord(const char *cmd) { CLIParserFree(); if (datalen != 2) { - PrintAndLog("ERROR: Command needs to have 2 bytes of data"); + PrintAndLogEx(ERR, "Command needs to have 2 bytes of data"); return 1; } @@ -314,7 +314,7 @@ int CmdHFEMVReadRecord(const char *cmd) { int res = EMVReadRecord(leaveSignalON, data[0], data[1], buf, sizeof(buf), &len, &sw, NULL); if (sw) - PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); if (res) return res; @@ -396,24 +396,24 @@ int CmdHFEMVAC(const char *cmd) { ParamLoadDefaults(tlvRoot); if (paramsLoadFromFile) { - PrintAndLog("Params loading from file..."); + PrintAndLogEx(INFO, "Params loading from file..."); ParamLoadFromJson(tlvRoot); }; cdol_data_tlv = dol_process((const struct tlv *)tlvdb_external(0x8c, datalen, data), tlvRoot, 0x01); // 0x01 - dummy tag if (!cdol_data_tlv){ - PrintAndLog("ERROR: can't create CDOL TLV."); + PrintAndLogEx(ERR, "Can't create CDOL TLV."); tlvdb_free(tlvRoot); return 4; } } else { if (paramsLoadFromFile) { - PrintAndLog("WARNING: don't need to load parameters. Sending plain CDOL data..."); + PrintAndLogEx(WARNING, "Don't need to load parameters. Sending plain CDOL data..."); } cdol_data_tlv = &data_tlv; } - PrintAndLog("CDOL data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len)); + PrintAndLogEx(INFO, "CDOL data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len)); // exec uint8_t buf[APDU_RES_LEN] = {0}; @@ -426,7 +426,7 @@ int CmdHFEMVAC(const char *cmd) { tlvdb_free(tlvRoot); if (sw) - PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); if (res) return res; @@ -464,15 +464,15 @@ int CmdHFEMVGenerateChallenge(const char *cmd) { int res = EMVGenerateChallenge(leaveSignalON, buf, sizeof(buf), &len, &sw, NULL); if (sw) - PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); if (res) return res; - PrintAndLog("Challenge: %s", sprint_hex(buf, len)); + PrintAndLogEx(SUCCESS, "Challenge: %s", sprint_hex(buf, len)); if (len != 4 && len != 8) - PrintAndLog("WARNING: length of challenge must be 4 or 8, but it %d", len); + PrintAndLogEx(WARNING, "Length of challenge must be 4 or 8, but it %d", len); return 0; } @@ -525,24 +525,24 @@ int CmdHFEMVInternalAuthenticate(const char *cmd) { ParamLoadDefaults(tlvRoot); if (paramsLoadFromFile) { - PrintAndLog("Params loading from file..."); + PrintAndLogEx(INFO, "Params loading from file..."); ParamLoadFromJson(tlvRoot); }; ddol_data_tlv = dol_process((const struct tlv *)tlvdb_external(0x9f49, datalen, data), tlvRoot, 0x01); // 0x01 - dummy tag if (!ddol_data_tlv){ - PrintAndLog("ERROR: can't create DDOL TLV."); + PrintAndLogEx(ERR, "Can't create DDOL TLV."); tlvdb_free(tlvRoot); return 4; } } else { if (paramsLoadFromFile) { - PrintAndLog("WARNING: don't need to load parameters. Sending plain DDOL data..."); + PrintAndLogEx(WARNING, "Don't need to load parameters. Sending plain DDOL data..."); } ddol_data_tlv = &data_tlv; } - PrintAndLog("DDOL data[%d]: %s", ddol_data_tlv->len, sprint_hex(ddol_data_tlv->value, ddol_data_tlv->len)); + PrintAndLogEx(INFO, "DDOL data[%d]: %s", ddol_data_tlv->len, sprint_hex(ddol_data_tlv->value, ddol_data_tlv->len)); // exec uint8_t buf[APDU_RES_LEN] = {0}; @@ -555,7 +555,7 @@ int CmdHFEMVInternalAuthenticate(const char *cmd) { tlvdb_free(tlvRoot); if (sw) - PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); if (res) return res; @@ -609,13 +609,13 @@ void ProcessGPOResponseFormat1(struct tlvdb *tlvRoot, uint8_t *buf, size_t len, } if (len < 4 || (len - 4) % 4) { - PrintAndLog("ERROR: GPO response format1 parsing error. length=%d", len); + PrintAndLogEx(ERR, "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:"); + PrintAndLogEx(INFO, "\n* * Decode response format 1 (0x80) AIP and AFL:"); TLVPrintFromTLV(f1AIP); } @@ -1137,12 +1137,12 @@ int CmdHFEMVScan(const char *cmd) { if (MergeJSON) { root = json_load_file(fname, 0, &error); if (!root) { - PrintAndLog("ERROR: json error on line %d: %s", error.line, error.text); + PrintAndLogEx(ERR, "Json error on line %d: %s", error.line, error.text); return 1; } if (!json_is_object(root)) { - PrintAndLog("ERROR: Invalid json format. root must be an object."); + PrintAndLogEx(ERR, "Invalid json format. root must be an object."); return 1; } } else { @@ -1153,7 +1153,7 @@ int CmdHFEMVScan(const char *cmd) { DropField(); // iso 14443 select - PrintAndLog("--> GET UID, ATS."); + PrintAndLogEx(NORMAL, "--> GET UID, ATS."); iso14a_card_select_t card; if (Hf14443_4aGetCardData(&card)) { @@ -1173,7 +1173,7 @@ int CmdHFEMVScan(const char *cmd) { struct tlvdb *tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); // EMV PPSE - PrintAndLog("--> PPSE."); + PrintAndLogEx(NORMAL, "--> PPSE."); res = EMVSelectPSE(true, true, 2, buf, sizeof(buf), &len, &sw); if (!res && sw == 0x9000){ @@ -1199,9 +1199,9 @@ int CmdHFEMVScan(const char *cmd) { } else { // EMV SEARCH with AID list SetAPDULogging(false); - PrintAndLog("--> AID search."); + PrintAndLogEx(NORMAL, "--> AID search."); if (EMVSearch(false, true, decodeTLV, tlvSelect)) { - PrintAndLog("E->Can't found any of EMV AID. Exit..."); + PrintAndLogEx(ERR, "Can't found any of EMV AID. Exit..."); tlvdb_free(tlvSelect); DropField(); return 3; @@ -1218,7 +1218,7 @@ int CmdHFEMVScan(const char *cmd) { tlvdb_free(tlvSelect); if (!AIDlen) { - PrintAndLog("Can't select AID. EMV AID not found. Exit..."); + PrintAndLogEx(INFO, "Can't select AID. EMV AID not found. Exit..."); DropField(); return 4; } @@ -1231,12 +1231,12 @@ int CmdHFEMVScan(const char *cmd) { // EMV SELECT applet - PrintAndLog("\n-->Selecting AID:%s.", sprint_hex_inrow(AID, AIDlen)); + PrintAndLogEx(NORMAL, "\n-->Selecting AID:%s.", sprint_hex_inrow(AID, AIDlen)); SetAPDULogging(showAPDU); res = EMVSelect(false, true, AID, AIDlen, buf, sizeof(buf), &len, &sw, tlvRoot); if (res) { - PrintAndLog("E->Can't select AID (%d). Exit...", res); + PrintAndLogEx(ERR, "Can't select AID (%d). Exit...", res); tlvdb_free(tlvRoot); DropField(); return 5; @@ -1258,13 +1258,13 @@ int CmdHFEMVScan(const char *cmd) { tlvdb_free(fci); // create transaction parameters - PrintAndLog("-->Init transaction parameters."); + PrintAndLogEx(NORMAL, "-->Init transaction parameters."); InitTransactionParameters(tlvRoot, paramLoadJSON, TrType, GenACGPO); - PrintAndLog("-->Calc PDOL."); + PrintAndLogEx(NORMAL, "-->Calc PDOL."); struct tlv *pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83); if (!pdol_data_tlv){ - PrintAndLog("E->Can't create PDOL TLV."); + PrintAndLogEx(ERR, "Can't create PDOL TLV."); tlvdb_free(tlvRoot); DropField(); return 6; @@ -1273,21 +1273,21 @@ int CmdHFEMVScan(const char *cmd) { 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("E->Can't create PDOL data."); + PrintAndLogEx(ERR, "Can't create PDOL data."); tlvdb_free(tlvRoot); DropField(); return 6; } - PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); + PrintAndLogEx(INFO, "PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); - PrintAndLog("-->GPO."); + PrintAndLogEx(INFO, "-->GPO."); res = EMVGPO(true, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); free(pdol_data_tlv_data); free(pdol_data_tlv); if (res) { - PrintAndLog("GPO error(%d): %4x. Exit...", res, sw); + PrintAndLogEx(ERR, "GPO error(%d): %4x. Exit...", res, sw); tlvdb_free(tlvRoot); DropField(); return 7; @@ -1305,12 +1305,12 @@ int CmdHFEMVScan(const char *cmd) { tlvdb_free(gpofci); - PrintAndLog("-->Read records from AFL."); + PrintAndLogEx(INFO, "-->Read records from AFL."); const struct tlv *AFL = tlvdb_get(tlvRoot, 0x94, NULL); while(AFL && AFL->len) { if (AFL->len % 4) { - PrintAndLog("E->Wrong AFL length: %d", AFL->len); + PrintAndLogEx(ERR, "Wrong AFL length: %d", AFL->len); break; } @@ -1322,7 +1322,7 @@ int CmdHFEMVScan(const char *cmd) { sfijson = json_path_get(root, "$.Application.Records"); } if (!json_is_array(sfijson)) { - PrintAndLog("E->Internal logic error. `$.Application.Records` is not an array."); + PrintAndLogEx(ERR, "Internal logic error. `$.Application.Records` is not an array."); break; } for (int i = 0; i < AFL->len / 4; i++) { @@ -1333,16 +1333,16 @@ int CmdHFEMVScan(const char *cmd) { PrintAndLog("--->SFI[%02x] start:%02x end:%02x offline:%02x", SFI, SFIstart, SFIend, SFIoffline); if (SFI == 0 || SFI == 31 || SFIstart == 0 || SFIstart > SFIend) { - PrintAndLog("SFI ERROR! Skipped..."); + PrintAndLogEx(ERR, "SFI ERROR! Skipped..."); continue; } for(int n = SFIstart; n <= SFIend; n++) { - PrintAndLog("---->SFI[%02x] %d", SFI, n); + PrintAndLogEx(INFO, "---->SFI[%02x] %d", SFI, n); res = EMVReadRecord(true, SFI, n, buf, sizeof(buf), &len, &sw, tlvRoot); if (res) { - PrintAndLog("E->SFI[%02x]. APDU error %4x", SFI, sw); + PrintAndLogEx(ERR, "SFI[%02x]. APDU error %4x", SFI, sw); continue; } @@ -1372,7 +1372,7 @@ int CmdHFEMVScan(const char *cmd) { // getting certificates if (tlvdb_get(tlvRoot, 0x90, NULL)) { - PrintAndLog("-->Recovering certificates."); + PrintAndLogEx(INFO, "-->Recovering certificates."); PKISetStrictExecution(false); RecoveryCertificates(tlvRoot, root); PKISetStrictExecution(true); @@ -1386,10 +1386,10 @@ int CmdHFEMVScan(const char *cmd) { res = json_dump_file(root, fname, JSON_INDENT(2)); if (res) { - PrintAndLog("ERROR: can't save the file: %s", fname); + PrintAndLogEx(ERR, "Can't save the file: %s", fname); return 200; } - PrintAndLog("File `%s` saved.", fname); + PrintAndLogEx(SUCCESS, "File `%s` saved.", fname); // free json object json_decref(root); From 291321f01df8de585ebdfff8044bd9e9676b0495 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Tue, 20 Nov 2018 17:34:29 +0200 Subject: [PATCH 10/12] add changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a57924d70..193d9c2fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -83,6 +83,9 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added `hf fido` commands that work with FIDO U2F authenticators (@merlokk) - Added mbedtls instead of old polarssl (@merlokk) - Added jansson (@merlokk) + - Added `hf emv scan` - save card's data to json file (@merlokk) + - Added `hf emv` `gpo`, `readrec`, `genac`, `challenge`, `intauth` - separate commands from `hf emc exec` (@merlokk) + ### Fixed - Changed driver file proxmark3.inf to support both old and new Product/Vendor IDs (piwi) - Changed start sequence in Qt mode (fix: short commands hangs main Qt thread) (Merlok) From 7943ba7d91005147944b8e439da031572df65878 Mon Sep 17 00:00:00 2001 From: Jean-Michel Picod Date: Thu, 22 Nov 2018 23:57:30 +0100 Subject: [PATCH 11/12] Fix logic op being used instead of bitwise op --- client/cmdsmartcard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/cmdsmartcard.c b/client/cmdsmartcard.c index c9997f241..59172b9c8 100644 --- a/client/cmdsmartcard.c +++ b/client/cmdsmartcard.c @@ -344,7 +344,7 @@ int CmdSmartUpgrade(const char *Cmd) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); return 1; } - if ( (resp.arg[0] && 0xFF ) ) + if ( (resp.arg[0] & 0xFF ) ) PrintAndLogEx(SUCCESS, "Smartcard socket firmware upgraded successful"); else PrintAndLogEx(FAILED, "Smartcard socket firmware updating failed"); From 3beb14a25c51e51c1c527173ac72ff79fb31f5db Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 25 Nov 2018 14:35:47 +0100 Subject: [PATCH 12/12] chg: 'hf iclass lookup' - its not an online command... --- client/cmdhficlass.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/cmdhficlass.c b/client/cmdhficlass.c index 368aaad2e..6cc2aa191 100644 --- a/client/cmdhficlass.c +++ b/client/cmdhficlass.c @@ -2290,7 +2290,6 @@ int CmdHFiClassLookUp(const char *Cmd) { t1 = msclock() - t1; PrintAndLogEx(NORMAL, "\nTime in iclass : %.0f seconds\n", (float)t1/1000.0); - DropField(); free(prekey); free(keyBlock); PrintAndLogEx(NORMAL, ""); @@ -2527,7 +2526,7 @@ static command_t CommandTable[] = { {"encryptblk", CmdHFiClassEncryptBlk, 1, " Encrypt given block data"}, {"list", CmdHFiClassList, 0, " (Deprecated) List iClass history"}, {"loclass", CmdHFiClass_loclass, 1, "[options..] Use loclass to perform bruteforce of reader attack dump"}, - {"lookup", CmdHFiClassLookUp, 0, "[options..] Uses authentication trace to check for key in dictionary file"}, + {"lookup", CmdHFiClassLookUp, 1, "[options..] Uses authentication trace to check for key in dictionary file"}, {"managekeys", CmdHFiClassManageKeys, 1, "[options..] Manage the keys to use with iClass"}, {"permutekey", CmdHFiClassPermuteKey, 0, " Permute function from 'heart of darkness' paper"}, {"readblk", CmdHFiClass_ReadBlock, 0, "[options..] Authenticate and Read iClass block"},