+ if(!iLen) {
+ PrintAndLog("APDU ERROR: No APDU response.");
+ return 1;
+ }
+
+ // check apdu length
+ if (iLen < 2 && iLen >= 0) {
+ PrintAndLog("APDU ERROR: Small APDU response. Len=%d", iLen);
+ return 2;
+ }
+
+ // check block TODO
+ if (iLen == -2) {
+ PrintAndLog("APDU ERROR: Block type mismatch.");
+ return 2;
+ }
+
+ memcpy(dataout, recv, dlen);
+
+ // chaining
+ if ((res & 0x10) != 0) {
+ *chainingout = true;
+ }
+
+ // CRC Check
+ if (iLen == -1) {
+ PrintAndLog("APDU ERROR: ISO 14443A CRC error.");
+ return 3;
+ }
+ } else {
+ PrintAndLog("APDU ERROR: Reply timeout.");
+ return 4;
+ }
+
+ return 0;
+}
+
+
+int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) {
+ *dataoutlen = 0;
+ bool chaining = false;
+ int res;
+
+ // 3 byte here - 1b framing header, 2b crc16
+ if ( (frameLength && (datainlen > frameLength - 3)) || (datainlen > USB_CMD_DATA_SIZE - 3) ) {
+ int clen = 0;
+
+ bool vActivateField = activateField;
+
+ do {
+ int vlen = MIN(frameLength - 3, datainlen - clen);
+ bool chainBlockNotLast = ((clen + vlen) < datainlen);
+
+ *dataoutlen = 0;
+ res = ExchangeAPDU(chainBlockNotLast, &datain[clen], vlen, vActivateField, dataout, maxdataoutlen, dataoutlen, &chaining);
+ if (res) {
+ if (!leaveSignalON)
+ DropField();
+
+ return 200;
+ }
+
+ // check R-block ACK
+ if ((*dataoutlen == 0) && (*dataoutlen != 0 || chaining != chainBlockNotLast)) {
+ if (!leaveSignalON)
+ DropField();
+
+ return 201;
+ }
+
+ clen += vlen;
+ vActivateField = false;
+ if (*dataoutlen) {
+ if (clen != datainlen)
+ PrintAndLogEx(WARNING, "APDU: I-block/R-block sequence error. Data len=%d, Sent=%d, Last packet len=%d", datainlen, clen, *dataoutlen);
+ break;
+ }
+ } while (clen < datainlen);
+ } else {
+ res = ExchangeAPDU(false, datain, datainlen, activateField, dataout, maxdataoutlen, dataoutlen, &chaining);
+ if (res) {
+ if (!leaveSignalON)
+ DropField();
+
+ return res;
+ }
+ }
+
+ while (chaining) {
+ // I-block with chaining
+ res = ExchangeAPDU(false, NULL, 0, false, &dataout[*dataoutlen], maxdataoutlen, dataoutlen, &chaining);
+
+ if (res) {
+ if (!leaveSignalON)
+ DropField();
+
+ return 100;
+ }
+ }
+
+ if (!leaveSignalON)
+ DropField();
+
+ 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;
+ uint8_t header[5];
+ int headerlen = 0;
+ bool activateField = false;
+ bool leaveSignalON = false;
+ bool decodeTLV = false;
+ bool decodeAPDU = false;
+ bool makeAPDU = false;
+ bool extendedAPDU = false;
+ int le = 0;
+ int res = 0;
+
+ CLIParserInit("hf 14a apdu",
+ "Sends an ISO 7816-4 APDU via ISO 14443-4 block transmission protocol (T=CL). Works with all APDU types from ISO 7816-4:2013",
+ "Examples:\n\thf 14a apdu -st 00A404000E325041592E5359532E444446303100\n"
+ "\thf 14a apdu -sd 00A404000E325041592E5359532E444446303100 - decode APDU\n"
+ "\thf 14a apdu -sm 00A40400 325041592E5359532E4444463031 -l 256 - encode standard APDU\n"
+ "\thf 14a apdu -sm 00A40400 325041592E5359532E4444463031 -el 65536 - encode extended APDU\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_lit0("dD", "decapdu", "decode APDU request if it possible"),
+ arg_str0("mM", "make", "<head (CLA INS P1 P2) hex>", "make APDU with head from this field and data from data field. Must be 4 bytes length: <CLA INS P1 P2>"),
+ arg_lit0("eE", "extended", "make extended length APDU (requires `-m`)"),
+ arg_int0("lL", "le", "<Le (int)>", "Le APDU parameter (requires `-m`)"),
+ arg_strx1(NULL, NULL, "<APDU (hex) | data (hex)>", "APDU (without `-m`), or data (with `-m`)"),
+ arg_param_end
+ };
+ CLIExecWithReturn(cmd, argtable, false);
+
+ activateField = arg_get_lit(1);
+ leaveSignalON = arg_get_lit(2);
+ decodeTLV = arg_get_lit(3);
+ decodeAPDU = arg_get_lit(4);
+
+ res = CLIParamHexToBuf(arg_get_str(5), header, sizeof(header), &headerlen);
+ makeAPDU = headerlen > 0;
+ if (res || (makeAPDU && headerlen != 4)) {
+ PrintAndLogEx(ERR, "header length must be exactly 4 bytes");
+ CLIParserFree();
+ return 1;
+ }
+ extendedAPDU = arg_get_lit(6);
+ le = arg_get_int_def(7, 0);
+
+ if (makeAPDU) {
+ uint8_t apdudata[USB_CMD_DATA_SIZE] = {0};
+ int apdudatalen = 0;
+
+ CLIGetHexBLessWithReturn(8, apdudata, &apdudatalen, 1 + 2);
+
+ APDUStruct apdu;
+ apdu.cla = header[0];
+ apdu.ins = header[1];
+ apdu.p1 = header[2];
+ apdu.p2 = header[3];
+
+ apdu.lc = apdudatalen;
+ apdu.data = apdudata;
+
+ apdu.extended_apdu = extendedAPDU;
+ apdu.le = le;
+
+ if (APDUEncode(&apdu, data, &datalen)) {
+ PrintAndLogEx(ERR, "can't make apdu with provided parameters.");
+ CLIParserFree();
+ return 2;
+ }
+ } else {
+ if (extendedAPDU) {
+ PrintAndLogEx(ERR, "`-e` without `-m`.");
+ CLIParserFree();
+ return 3;
+ }
+ if (le > 0) {
+ PrintAndLogEx(ERR, "`-l` without `-m`.");
+ CLIParserFree();
+ return 3;
+ }
+
+ // len = data + PCB(1b) + CRC(2b)
+ CLIGetHexBLessWithReturn(8, data, &datalen, 1 + 2);
+ }
+
+ CLIParserFree();
+// PrintAndLog("---str [%d] %s", arg_get_str(4)->count, arg_get_str(4)->sval[0]);
+ PrintAndLogEx(NORMAL, ">>>>[%s%s%s] %s", activateField ? "sel ": "", leaveSignalON ? "keep ": "", decodeTLV ? "TLV": "", sprint_hex(data, datalen));
+
+ if (decodeAPDU) {
+ APDUStruct apdu;
+
+ if (APDUDecode(data, datalen, &apdu) == 0)
+ APDUPrint(apdu);
+ else
+ PrintAndLogEx(WARNING, "can't decode APDU.");
+ }
+
+ res = ExchangeAPDU14a(data, datalen, activateField, leaveSignalON, data, USB_CMD_DATA_SIZE, &datalen);
+
+ if (res)
+ return res;
+
+ PrintAndLog("<<<< %s", sprint_hex(data, datalen));
+
+ PrintAndLog("APDU response: %02x %02x - %s", data[datalen - 2], data[datalen - 1], GetAPDUCodeDescription(data[datalen - 2], data[datalen - 1]));
+
+ // TLV decoder
+ if (decodeTLV && datalen > 4) {
+ TLVPrintFromBuffer(data, datalen - 2);
+ }
+
+ return 0;
+}
+
+int CmdHF14ACmdRaw(const char *cmd) {
+ UsbCommand c = {CMD_READER_ISO_14443a, {0, 0, 0}};
+ bool reply=1;
+ bool crc = false;
+ bool power = false;
+ bool active = false;
+ bool active_select = false;
+ bool no_rats = false;
+ uint16_t numbits = 0;
+ bool bTimeout = false;
+ uint32_t timeout = 0;
+ bool topazmode = false;
+ uint8_t data[USB_CMD_DATA_SIZE];
+ int datalen = 0;
+
+ // extract parameters
+ CLIParserInit("hf 14a raw", "Send raw hex data to tag",
+ "Sample:\n"\
+ "\thf 14a raw -pa -b7 -t1000 52 -- execute WUPA\n"\
+ "\thf 14a raw -p 9320 -- anticollision\n"\
+ "\thf 14a raw -psc 60 00 -- select and mifare AUTH\n");
+ void* argtable[] = {
+ arg_param_begin,
+ arg_lit0("rR", "nreply", "do not read response"),
+ arg_lit0("cC", "crc", "calculate and append CRC"),
+ arg_lit0("pP", "power", "leave the signal field ON after receive"),
+ arg_lit0("aA", "active", "active signal field ON without select"),
+ arg_lit0("sS", "actives", "active signal field ON with select"),
+ arg_int0("bB", "bits", NULL, "number of bits to send. Useful for send partial byte"),
+ arg_int0("t", "timeout", NULL, "timeout in ms"),
+ arg_lit0("T", "topaz", "use Topaz protocol to send command"),
+ arg_lit0("3", NULL, "ISO14443-3 select only (skip RATS)"),
+ arg_strx1(NULL, NULL, "<data (hex)>", NULL),
+ arg_param_end
+ };
+ // defaults
+ arg_get_int(6) = 0;
+ arg_get_int(7) = 0;
+
+ if (CLIParserParseString(cmd, argtable, arg_getsize(argtable), false)){
+ CLIParserFree();
+ return 0;
+ }
+
+ reply = !arg_get_lit(1);
+ crc = arg_get_lit(2);
+ power = arg_get_lit(3);
+ active = arg_get_lit(4);
+ active_select = arg_get_lit(5);
+ numbits = arg_get_int(6) & 0xFFFF;
+ timeout = arg_get_int(7);
+ bTimeout = (timeout > 0);
+ topazmode = arg_get_lit(8);
+ no_rats = arg_get_lit(9);
+ // len = data + CRC(2b)
+ if (CLIParamHexToBuf(arg_get_str(10), data, sizeof(data) -2, &datalen)) {
+ CLIParserFree();
+ return 1;
+ }
+
+ CLIParserFree();
+
+ // logic
+ if(crc && datalen>0 && datalen<sizeof(data)-2)
+ {
+ uint8_t first, second;
+ if (topazmode) {
+ ComputeCrc14443(CRC_14443_B, data, datalen, &first, &second);
+ } else {
+ ComputeCrc14443(CRC_14443_A, data, datalen, &first, &second);
+ }
+ data[datalen++] = first;
+ data[datalen++] = second;
+ }
+
+ if(active || active_select)
+ {
+ c.arg[0] |= ISO14A_CONNECT | ISO14A_CLEAR_TRACE;
+ if(active)
+ c.arg[0] |= ISO14A_NO_SELECT;
+ }
+
+ if(bTimeout){
+ #define MAX_TIMEOUT 40542464 // = (2^32-1) * (8*16) / 13560000Hz * 1000ms/s
+ c.arg[0] |= ISO14A_SET_TIMEOUT;
+ if(timeout > MAX_TIMEOUT) {
+ timeout = MAX_TIMEOUT;
+ PrintAndLog("Set timeout to 40542 seconds (11.26 hours). The max we can wait for response");
+ }
+ c.arg[2] = 13560000 / 1000 / (8*16) * timeout; // timeout in ETUs (time to transfer 1 bit, approx. 9.4 us)
+ }
+
+ if(power) {
+ c.arg[0] |= ISO14A_NO_DISCONNECT;
+ }
+
+ if(datalen > 0) {
+ c.arg[0] |= ISO14A_RAW;
+ }
+
+ if(topazmode) {
+ c.arg[0] |= ISO14A_TOPAZMODE;
+ }
+
+ if(no_rats) {
+ c.arg[0] |= ISO14A_NO_RATS;
+ }
+
+ // Max buffer is USB_CMD_DATA_SIZE (512)
+ c.arg[1] = (datalen & 0xFFFF) | ((uint32_t)numbits << 16);
+ memcpy(c.d.asBytes,data,datalen);
+
+ SendCommand(&c);
+
+ if (reply) {
+ int res = 0;
+ if (active_select)
+ res = waitCmd(1);
+ if (!res && datalen > 0)
+ waitCmd(0);
+ } // if reply
+ return 0;
+}
+
+
+static int waitCmd(uint8_t iSelect) {
+ uint8_t *recv;
+ UsbCommand resp;
+ char *hexout;
+
+ if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) {
+ recv = resp.d.asBytes;
+ uint8_t iLen = resp.arg[0];
+ if (iSelect){
+ iLen = resp.arg[1];
+ if (iLen){
+ PrintAndLog("Card selected. UID[%i]:", iLen);
+ } else {
+ PrintAndLog("Can't select card.");
+ }
+ } else {
+ PrintAndLog("received %i bytes:", iLen);
+ }
+ if(!iLen)
+ return 1;
+ hexout = (char *)malloc(iLen * 3 + 1);
+ if (hexout != NULL) {
+ for (int i = 0; i < iLen; i++) { // data in hex
+ sprintf(&hexout[i * 3], "%02X ", recv[i]);
+ }
+ PrintAndLog("%s", hexout);
+ free(hexout);
+ } else {
+ PrintAndLog("malloc failed your client has low memory?");
+ return 2;
+ }
+ } else {
+ PrintAndLog("timeout while waiting for reply.");
+ return 3;
+ }
+ return 0;
+}
+
+static command_t CommandTable[] =