#include "Reader14443A.h" #include "Application.h" #include "ISO14443-3A.h" #include "../Codec/Reader14443-2A.h" #include "Crypto1.h" #include "../System.h" #include "../Terminal/Terminal.h" #define CHECK_BCC(B) ((B[0] ^ B[1] ^ B[2] ^ B[3]) == B[4]) #define IS_CASCADE_BIT_SET(buf) (buf[0] & 0x04) #define IS_ISO14443A_4_COMPLIANT(buf) (buf[0] & 0x20) // TODO replace remaining magic numbers static bool Selected = false; Reader14443Command Reader14443CurrentCommand = Reader14443_Do_Nothing; static enum { STATE_IDLE, STATE_HALT, STATE_READY, STATE_ACTIVE_CL1, // must be ordered sequentially STATE_ACTIVE_CL2, STATE_ACTIVE_CL3, STATE_SAK_CL1, // must be ordered sequentially STATE_SAK_CL2, STATE_SAK_CL3, STATE_ATS, STATE_DESELECT, STATE_DESFIRE_INFO, STATE_END } ReaderState = STATE_IDLE; static struct { uint16_t ATQA; uint8_t SAK; uint8_t UID[10]; enum { UIDSize_No_UID = 0, UIDSize_Single = 4, UIDSize_Double = 7, UIDSize_Triple = 10 } UIDSize; } CardCharacteristics = {0}; typedef enum { CardType_NXP_MIFARE_Mini = 0, // do NOT assign another CardType item with a specific value since there are loops over this type CardType_NXP_MIFARE_Classic_1k, CardType_NXP_MIFARE_Classic_4k, CardType_NXP_MIFARE_Ultralight, CardType_NXP_MIFARE_DESFire, CardType_NXP_MIFARE_DESFire_EV1, CardType_IBM_JCOP31, CardType_IBM_JCOP31_v241, CardType_IBM_JCOP41_v22, CardType_IBM_JCOP41_v231, CardType_Infineon_MIFARE_Classic_1k, CardType_Gemplus_MPCOS, CardType_Innovision_Jewel, CardType_Nokia_MIFARE_Classic_4k_emulated_6212, CardType_Nokia_MIFARE_Classic_4k_emulated_6131 } CardType; typedef struct { uint16_t ATQA; bool ATQARelevant; uint8_t SAK; bool SAKRelevant; uint8_t ATS[16]; uint8_t ATSSize; bool ATSRelevant; char Manufacturer[16]; char Type[64]; } CardIdentificationType; static const CardIdentificationType PROGMEM CardIdentificationList[] = { [CardType_NXP_MIFARE_Mini] = { .ATQA=0x0004, .ATQARelevant=true, .SAK=0x09, .SAKRelevant=true, .ATSRelevant=false, .Manufacturer="NXP", .Type="MIFARE Mini" }, [CardType_NXP_MIFARE_Classic_1k] = { .ATQA=0x0004, .ATQARelevant=true, .SAK=0x08, .SAKRelevant=true, .ATSRelevant=false, .Manufacturer="NXP", .Type="MIFARE Classic 1k" }, [CardType_NXP_MIFARE_Classic_4k] = { .ATQA=0x0002, .ATQARelevant=true, .SAK=0x18, .SAKRelevant=true, .ATSRelevant=false, .Manufacturer="NXP", .Type="MIFARE Classic 4k" }, [CardType_NXP_MIFARE_Ultralight] = { .ATQA=0x0044, .ATQARelevant=true, .SAK=0x00, .SAKRelevant=true, .ATSRelevant=false, .Manufacturer="NXP", .Type="MIFARE Ultralight" }, // for the following two, setting ATSRelevant to true would cause checking the ATS value, but the NXP paper for distinguishing cards does not recommend this [CardType_NXP_MIFARE_DESFire] = { .ATQA=0x0344, .ATQARelevant=true, .SAK=0x20, .SAKRelevant=true, .ATSRelevant=false, .ATSSize= 5, .ATS={0x75, 0x77, 0x81, 0x02, 0x80}, .Manufacturer="NXP", .Type="MIFARE DESFire" }, [CardType_NXP_MIFARE_DESFire_EV1] = { .ATQA=0x0344, .ATQARelevant=true, .SAK=0x20, .SAKRelevant=true, .ATSRelevant=false, .ATSSize= 5, .ATS={0x75, 0x77, 0x81, 0x02, 0x80}, .Manufacturer="NXP", .Type="MIFARE DESFire EV1" }, [CardType_IBM_JCOP31] = { .ATQA=0x0304, .ATQARelevant=true, .SAK=0x28, .SAKRelevant=true, .ATSRelevant=true, .ATSSize= 9, .ATS={0x38, 0x77, 0xb1, 0x4a, 0x43, 0x4f, 0x50, 0x33, 0x31}, .Manufacturer="IBM", .Type="JCOP31" }, [CardType_IBM_JCOP31_v241] = { .ATQA=0x0048, .ATQARelevant=true, .SAK=0x20, .SAKRelevant=true, .ATSRelevant=true, .ATSSize=12, .ATS={0x78, 0x77, 0xb1, 0x02, 0x4a, 0x43, 0x4f, 0x50, 0x76, 0x32, 0x34, 0x31}, .Manufacturer="IBM", .Type="JCOP31 v2.4.1" }, [CardType_IBM_JCOP41_v22] = { .ATQA=0x0048, .ATQARelevant=true, .SAK=0x20, .SAKRelevant=true, .ATSRelevant=true, .ATSSize=12, .ATS={0x38, 0x33, 0xb1, 0x4a, 0x43, 0x4f, 0x50, 0x34, 0x31, 0x56, 0x32, 0x32}, .Manufacturer="IBM", .Type="JCOP41 v2.2" }, [CardType_IBM_JCOP41_v231] = { .ATQA=0x0004, .ATQARelevant=true, .SAK=0x28, .SAKRelevant=true, .ATSRelevant=true, .ATSSize=13, .ATS={0x38, 0x33, 0xb1, 0x4a, 0x43, 0x4f, 0x50, 0x34, 0x31, 0x56, 0x32, 0x33, 0x31}, .Manufacturer="IBM", .Type="JCOP41 v2.3.1" }, [CardType_Infineon_MIFARE_Classic_1k] = { .ATQA=0x0004, .ATQARelevant=true, .SAK=0x88, .SAKRelevant=true, .ATSRelevant=false, .Manufacturer="Infineon", .Type="MIFARE Classic 1k" }, [CardType_Gemplus_MPCOS] = { .ATQA=0x0002, .ATQARelevant=true, .SAK=0x98, .SAKRelevant=true, .ATSRelevant=false, .Manufacturer="Gemplus", .Type="MPCOS" }, [CardType_Innovision_Jewel] = { .ATQA=0x0C00, .ATQARelevant=true, .SAKRelevant=false, .ATSRelevant=false, .Manufacturer="Innovision R&T", .Type="Jewel" }, [CardType_Nokia_MIFARE_Classic_4k_emulated_6212] = { .ATQA=0x0002, .ATQARelevant=true, .SAK=0x38, .SAKRelevant=true, .ATSRelevant=false, .Manufacturer="Nokia", .Type="MIFARE Classic 4k - emulated (6212 Classic)" }, [CardType_Nokia_MIFARE_Classic_4k_emulated_6131] = { .ATQA=0x0008, .ATQARelevant=true, .SAK=0x38, .SAKRelevant=true, .ATSRelevant=false, .Manufacturer="Nokia", .Type="MIFARE Classic 4k - emulated (6131 NFC)" } }; static CardType CardCandidates[ARRAY_COUNT(CardIdentificationList)]; static uint8_t CardCandidatesIdx = 0; uint16_t addParityBits(uint8_t * Buffer, uint16_t BitCount) { uint8_t i = CardIdentificationList[0].ATQA; if (i == 0) return 0; if (BitCount == 7) return 7; if (BitCount % 8) return BitCount; uint8_t * currByte, * tmpByte; uint8_t * const lastByte = Buffer + BitCount/8 + BitCount/64; // starting address + number of bytes + number of parity bytes currByte = Buffer + BitCount/8 - 1; uint8_t parity; memset(currByte+1, 0, lastByte-currByte); // zeroize all bytes used for parity bits while (currByte >= Buffer) // loop over all input bytes { parity = OddParityBit(*currByte); // get parity bit tmpByte = lastByte; while (tmpByte > currByte) // loop over all bytes from the last byte to the current one -- shifts the whole byte string { *tmpByte <<= 1; // shift this byte *tmpByte |= (*(tmpByte-1) & 0x80) >> 7; // insert the last bit from the previous byte tmpByte--; // go to the previous byte } *(++tmpByte) &= 0xFE; // zeroize the bit, where we want to put the parity bit *tmpByte |= parity & 1; // add the parity bit currByte--; // go to previous input byte } return BitCount + (BitCount / 8); } uint16_t removeParityBits(uint8_t * Buffer, uint16_t BitCount) { if (BitCount == 7) return 7; uint16_t i; for (i = 0; i < (BitCount / 9); i++) { Buffer[i] = (Buffer[i + i/8] >> (i%8)); if (i%8) Buffer[i] |= (Buffer[i + i/8 + 1] << (8 - (i % 8))); } return BitCount/9*8; } uint16_t removeSOC(uint8_t * Buffer, uint16_t BitCount) { if (BitCount == 0) return 0; uint16_t i; Buffer[0] >>= 1; for (i = 1; i < (BitCount + 7) / 8; i++) { Buffer[i-1] |= Buffer[i] << 7; Buffer[i] >>= 1; } return BitCount - 1; } bool checkParityBits(uint8_t * Buffer, uint16_t BitCount) { if (BitCount == 7) return true; //if (BitCount % 9 || BitCount == 0) // return false; uint16_t i; uint8_t currentByte, parity; for (i = 0; i < (BitCount / 9); i++) { currentByte = (Buffer[i + i/8] >> (i%8)); if (i%8) currentByte |= (Buffer[i + i/8 + 1] << (8 - (i % 8))); parity = OddParityBit(currentByte); if (((Buffer[i + i/8 + 1] >> (i % 8)) ^ parity) & 1) { return false; } } return true; } void Reader14443AAppTimeout(void) { Reader14443AAppReset(); Reader14443ACodecReset(); ReaderState = STATE_IDLE; } void Reader14443AAppInit(void) { ReaderState = STATE_IDLE; } void Reader14443AAppReset(void) { ReaderState = STATE_IDLE; Reader14443CurrentCommand = Reader14443_Do_Nothing; Selected = false; } void Reader14443AAppTask(void) { } void Reader14443AAppTick(void) { } INLINE uint16_t Reader14443A_Deselect(uint8_t* Buffer) // deselects the card because of an error, so we will continue to select the card afterwards { Buffer[0] = 0xC2; uint16_t crc = ISO14443_CRCA(Buffer, 1); Buffer[1] = crc; Buffer[2] = crc >> 8; ReaderState = STATE_DESELECT; Selected = false; return addParityBits(Buffer, 24); } INLINE uint16_t Reader14443A_Select(uint8_t * Buffer, uint16_t BitCount) { if (Selected) return 0; switch (ReaderState) { case STATE_IDLE: case STATE_HALT: /* Send a REQA */ Buffer[0] = ISO14443A_CMD_WUPA; // whenever REQA works, WUPA also works, so we choose WUPA always ReaderState = STATE_READY; return 7; case STATE_READY: if (BitCount < 19) { ReaderState = STATE_IDLE; Reader14443ACodecStart(); return 0; } BitCount = removeSOC(Buffer, BitCount); if (!checkParityBits(Buffer, BitCount)) { ReaderState = STATE_IDLE; LogEntry(LOG_ERR_APP_CHECKSUM_FAIL, Buffer, (BitCount + 8) / 7); Reader14443ACodecStart(); return 0; } BitCount = removeParityBits(Buffer, BitCount); CardCharacteristics.ATQA = Buffer[1] << 8 | Buffer[0]; // save ATQA for possible later use Buffer[0] = ISO14443A_CMD_SELECT_CL1; Buffer[1] = 0x20; // NVB = 16 ReaderState = STATE_ACTIVE_CL1; return addParityBits(Buffer, 16); case STATE_ACTIVE_CL1 ... STATE_ACTIVE_CL3: BitCount = removeSOC(Buffer, BitCount); if (!checkParityBits(Buffer, BitCount) || BitCount < 8) { ReaderState = STATE_IDLE; LogEntry(LOG_ERR_APP_CHECKSUM_FAIL, Buffer, (BitCount + 8) / 7); Reader14443ACodecStart(); return 0; } BitCount = removeParityBits(Buffer, BitCount); if (!CHECK_BCC(Buffer)) { ReaderState = STATE_IDLE; Reader14443ACodecStart(); return 0; } if (Buffer[0] == ISO14443A_UID0_CT) { memcpy(CardCharacteristics.UID + (ReaderState - STATE_ACTIVE_CL1) * 3, Buffer + 1, 3); } else { memcpy(CardCharacteristics.UID + (ReaderState - STATE_ACTIVE_CL1) * 3, Buffer, 4); } // shift received UID two bytes to the right memmove(Buffer+2, Buffer, 5); Buffer[0] = (ReaderState == STATE_ACTIVE_CL1) ? ISO14443A_CMD_SELECT_CL1 : (ReaderState == STATE_ACTIVE_CL2) ? ISO14443A_CMD_SELECT_CL2 : ISO14443A_CMD_SELECT_CL3; Buffer[1] = 0x70; // NVB = 56 uint16_t crc = ISO14443_CRCA(Buffer, 7); Buffer[7] = crc & 0xFF; Buffer[8] = crc >> 8; ReaderState = ReaderState - STATE_ACTIVE_CL1 + STATE_SAK_CL1; return addParityBits(Buffer, 72); case STATE_SAK_CL1 ... STATE_SAK_CL3: if (BitCount < 9) { ReaderState = STATE_IDLE; Reader14443ACodecStart(); return 0; } BitCount = removeSOC(Buffer, BitCount); if (!checkParityBits(Buffer, BitCount)) { LogEntry(LOG_ERR_APP_CHECKSUM_FAIL, Buffer, (BitCount + 8) / 7); ReaderState = STATE_IDLE; Reader14443ACodecStart(); return 0; } BitCount = removeParityBits(Buffer, BitCount); if (ISO14443_CRCA(Buffer, 3)) { ReaderState = STATE_IDLE; Reader14443ACodecStart(); return 0; } if (IS_CASCADE_BIT_SET(Buffer) && ReaderState != STATE_SAK_CL3) { Buffer[0] = (ReaderState == STATE_SAK_CL1) ? ISO14443A_CMD_SELECT_CL2 : ISO14443A_CMD_SELECT_CL3; Buffer[1] = 0x20; // NVB = 16 bit ReaderState = ReaderState - STATE_SAK_CL1 + STATE_ACTIVE_CL1 + 1; return addParityBits(Buffer, 16); } else if (IS_CASCADE_BIT_SET(Buffer) && ReaderState == STATE_SAK_CL3) { // TODO handle this very strange hopefully not happening error } Selected = true; CardCharacteristics.UIDSize = (ReaderState - STATE_SAK_CL1) * 3 + 4; CardCharacteristics.SAK = Buffer[0]; // save last SAK for possible later use return 0; case STATE_DESELECT: if (BitCount == 0) // most likely the card already understood the deselect { ReaderState = STATE_HALT; Reader14443ACodecStart(); return 0; } BitCount = removeSOC(Buffer, BitCount); if (!checkParityBits(Buffer, BitCount)) { LogEntry(LOG_ERR_APP_CHECKSUM_FAIL, Buffer, (BitCount + 8) / 7); return Reader14443A_Deselect(Buffer); } BitCount = removeParityBits(Buffer, BitCount); if (ISO14443_CRCA(Buffer, 3)) { return Reader14443A_Deselect(Buffer); } ReaderState = STATE_HALT; Reader14443ACodecStart(); return 0; default: return 0; } } INLINE uint16_t Reader14443A_Halt(uint8_t* Buffer) { Buffer[0] = ISO14443A_CMD_HLTA; Buffer[1] = 0x00; uint16_t crc = ISO14443_CRCA(Buffer, 2); Buffer[2] = crc; Buffer[3] = crc >> 8; ReaderState = STATE_HALT; Selected = false; return addParityBits(Buffer, 32); } INLINE uint16_t Reader14443A_RATS(uint8_t* Buffer) { Buffer[0] = 0xE0; // RATS command Buffer[1] = 0x80; uint16_t crc = ISO14443_CRCA(Buffer, 2); Buffer[2] = crc; Buffer[3] = crc >> 8; ReaderState = STATE_ATS; return addParityBits(Buffer, 32); } uint16_t Reader14443AAppProcess(uint8_t* Buffer, uint16_t BitCount) { switch (Reader14443CurrentCommand) { case Reader14443_Send: { if (ReaderSendBitCount) { memcpy(Buffer, ReaderSendBuffer, (ReaderSendBitCount + 7) / 8); uint16_t tmp = addParityBits(Buffer, ReaderSendBitCount); ReaderSendBitCount = 0; return tmp; } if (BitCount == 0) { char tmpBuf[] = "NO DATA"; Reader14443CurrentCommand = Reader14443_Do_Nothing; CommandLinePendingTaskFinished(COMMAND_INFO_OK_WITH_TEXT_ID, tmpBuf); return 0; } char tmpBuf[128]; BitCount = removeSOC(Buffer, BitCount); bool parity = checkParityBits(Buffer, BitCount); BitCount = removeParityBits(Buffer, BitCount); if ((2 * (BitCount + 7) / 8 + 2 + 4) > 128) // 2 = \r\n, 4 = size of bitcount in hex { sprintf(tmpBuf, "Too many data."); Reader14443CurrentCommand = Reader14443_Do_Nothing; CommandLinePendingTaskFinished(COMMAND_INFO_OK_WITH_TEXT_ID, tmpBuf); return 0; } uint16_t charCnt = BufferToHexString(tmpBuf, 128, Buffer, (BitCount + 7) / 8); uint8_t count[2] = {(BitCount>>8)&0xFF, BitCount&0xFF}; charCnt += snprintf(tmpBuf + charCnt, 128 - charCnt, "\r\n"); charCnt += BufferToHexString(tmpBuf + charCnt, 128 - charCnt, count, 2); if (!parity) snprintf(tmpBuf + charCnt, 128 - charCnt, "\r\nPARITY ERROR"); else snprintf(tmpBuf + charCnt, 128 - charCnt, "\r\nPARITY OK"); Reader14443CurrentCommand = Reader14443_Do_Nothing; CommandLinePendingTaskFinished(COMMAND_INFO_OK_WITH_TEXT_ID, tmpBuf); return 0; } case Reader14443_Send_Raw: { if (ReaderSendBitCount) { memcpy(Buffer, ReaderSendBuffer, (ReaderSendBitCount + 7) / 8); uint16_t tmp = ReaderSendBitCount; ReaderSendBitCount = 0; return tmp; } if (BitCount == 0) { char tmpBuf[] = "NO DATA"; Reader14443CurrentCommand = Reader14443_Do_Nothing; CommandLinePendingTaskFinished(COMMAND_INFO_OK_WITH_TEXT_ID, tmpBuf); return 0; } char tmpBuf[128]; uint16_t charCnt = BufferToHexString(tmpBuf, 128, Buffer, (BitCount + 7) / 8); uint8_t count[2] = {(BitCount>>8)&0xFF, BitCount&0xFF}; charCnt += snprintf(tmpBuf + charCnt, 128 - charCnt, "\r\n"); charCnt += BufferToHexString(tmpBuf + charCnt, 128 - charCnt, count, 2); Reader14443CurrentCommand = Reader14443_Do_Nothing; CommandLinePendingTaskFinished(COMMAND_INFO_OK_WITH_TEXT_ID, tmpBuf); return 0; } case Reader14443_Get_UID: { uint16_t rVal = Reader14443A_Select(Buffer, BitCount); if (Selected) // we are done finding the UID { char tmpBuf[20]; BufferToHexString(tmpBuf, 20, CardCharacteristics.UID, CardCharacteristics.UIDSize); CommandLinePendingTaskFinished(COMMAND_INFO_OK_WITH_TEXT_ID, tmpBuf); Selected = false; Reader14443CurrentCommand = Reader14443_Do_Nothing; CodecReaderFieldStop(); return 0; } return rVal; } case Reader14443_Read_MF_Ultralight: { static uint8_t MFURead_CurrentAdress = 0; static uint8_t MFUContents[64]; uint16_t rVal = Reader14443A_Select(Buffer, BitCount); if (Selected) { if (MFURead_CurrentAdress != 0) { BitCount = removeSOC(Buffer, BitCount); if (BitCount == 0) // relaunch select protocol { MFURead_CurrentAdress = 0; // reset read address Selected = false; ReaderState = STATE_IDLE; Reader14443ACodecStart(); return 0; } bool readPageAgain = (BitCount < 162) || !checkParityBits(Buffer, BitCount); BitCount = removeParityBits(Buffer, BitCount); if (readPageAgain || ISO14443_CRCA(Buffer, 18)) // the CRC function should return 0 if everything is ok { MFURead_CurrentAdress -= 4; } else { // everything is ok for this page memcpy(MFUContents + (MFURead_CurrentAdress - 4) * 4, Buffer, 16); } } else { uint16_t RefATQA; memcpy_P(&RefATQA, &CardIdentificationList[CardType_NXP_MIFARE_Ultralight].ATQA, 2); uint8_t RefSAK = pgm_read_byte(&CardIdentificationList[CardType_NXP_MIFARE_Ultralight].SAK); if (CardCharacteristics.ATQA != RefATQA || CardCharacteristics.SAK != RefSAK) // seems to be no MiFare Ultralight card, so retry { ReaderState = STATE_IDLE; Reader14443ACodecStart(); return 0; } } if (MFURead_CurrentAdress == 16) { Selected = false; MFURead_CurrentAdress = 0; Reader14443CurrentCommand = Reader14443_Do_Nothing; char tmpBuf[135]; // 135 = 128 hex digits + 3 * \r\n + \0 BufferToHexString( tmpBuf, 135, MFUContents, 16); snprintf( tmpBuf + 32, 135 - 32, "\r\n"); BufferToHexString( tmpBuf + 32 + 2, 135 - 32 - 2, MFUContents + 16, 16); snprintf( tmpBuf + 32 + 2 + 32, 135 - 32 - 2 - 32, "\r\n"); BufferToHexString( tmpBuf + 32 + 2 + 32 + 2, 135 - 32 - 2 - 32 - 2, MFUContents + 32, 16); snprintf( tmpBuf + 32 + 2 + 32 + 2 + 32, 135 - 32 - 2 - 32 - 2 - 32, "\r\n"); BufferToHexString( tmpBuf + 32 + 2 + 32 + 2 + 32 + 2, 135 - 32 - 2 - 32 - 2 - 32 - 2, MFUContents + 48, 16); CodecReaderFieldStop(); CommandLinePendingTaskFinished(COMMAND_INFO_OK_WITH_TEXT_ID, tmpBuf); return 0; } Buffer[0] = 0x30; // MiFare Ultralight read command Buffer[1] = MFURead_CurrentAdress; uint16_t crc = ISO14443_CRCA(Buffer, 2); Buffer[2] = crc; Buffer[3] = crc >> 8; MFURead_CurrentAdress += 4; return addParityBits(Buffer, 32); } return rVal; } /************************************ * This function identifies a PICC. * ************************************/ case Reader14443_Indentify: { uint16_t rVal = Reader14443A_Select(Buffer, BitCount); if (Selected) { if (ReaderState >= STATE_SAK_CL1 && ReaderState <= STATE_SAK_CL3) { bool ISO14443_4A_compliant = IS_ISO14443A_4_COMPLIANT(Buffer); uint8_t i; for (i = 0; i < ARRAY_COUNT(CardIdentificationList); i++) { CardIdentificationType card; memcpy_P(&card, &CardIdentificationList[i], sizeof(CardIdentificationType)); if (card.ATQARelevant && card.ATQA != CardCharacteristics.ATQA) continue; if (card.SAKRelevant && card.SAK != CardCharacteristics.SAK) continue; if (card.ATSRelevant && !ISO14443_4A_compliant) continue; // for this card type candidate, the ATS is relevant, but the card does not support ISO14443-4A CardCandidates[CardCandidatesIdx++] = i; } if (ISO14443_4A_compliant) { // send RATS return Reader14443A_RATS(Buffer); } // if we don't have to send the RATS, we are finished for distinguishing with ISO 14443A } else if (ReaderState == STATE_ATS) { // we have got the ATS BitCount = removeSOC(Buffer, BitCount); if (!checkParityBits(Buffer, BitCount)) { LogEntry(LOG_ERR_APP_CHECKSUM_FAIL, Buffer, (BitCount + 8) / 7); return Reader14443A_Deselect(Buffer); } BitCount = removeParityBits(Buffer, BitCount); if (Buffer[0] != BitCount / 8 - 2 || ISO14443_CRCA(Buffer, Buffer[0] + 2)) { return Reader14443A_Deselect(Buffer); } uint8_t i; for (i = 0; i < CardCandidatesIdx; i++) { CardIdentificationType card; memcpy_P(&card, &CardIdentificationList[CardCandidates[i]], sizeof(CardIdentificationType)); if (!card.ATSRelevant || (card.ATSRelevant && card.ATSSize == Buffer[0] - 1 && memcmp(card.ATS, Buffer + 1, card.ATSSize) == 0)) /* * If for this candidate the ATS is not relevant, it remains being a candidate. * If the ATS is relevant and the size is correct and the ATS is the same as the reference value, this candidate remains a candidate. */ continue; // Else, we have to delete this candidate uint8_t j; for (j = i; j < CardCandidatesIdx - 1; j++) CardCandidates[j] = CardCandidates[j+1]; CardCandidatesIdx--; i--; } } /* * If any cards are not distinguishable with ISO14443A commands only, this is the place to run some proprietary commands. */ if ((ReaderState >= STATE_SAK_CL1 && ReaderState <= STATE_SAK_CL3) || ReaderState == STATE_ATS) { uint8_t i; for (i = 0; i < CardCandidatesIdx; i++) { switch (CardCandidates[i]) { case CardType_NXP_MIFARE_DESFire: case CardType_NXP_MIFARE_DESFire_EV1: Buffer[0] = 0x02; Buffer[1] = 0x60; uint16_t crc = ISO14443_CRCA(Buffer, 2); Buffer[2] = crc; Buffer[3] = crc >> 8; ReaderState = STATE_DESFIRE_INFO; return addParityBits(Buffer, 32); default: break; } } } else { switch (ReaderState) { case STATE_DESFIRE_INFO: if (BitCount == 0) { CardCandidatesIdx = 0; // this will return that this card is unknown to us break; } BitCount = removeSOC(Buffer, BitCount); if (!checkParityBits(Buffer, BitCount)) { LogEntry(LOG_ERR_APP_CHECKSUM_FAIL, Buffer, (BitCount + 8) / 7); CardCandidatesIdx = 0; return Reader14443A_Deselect(Buffer); } BitCount = removeParityBits(Buffer, BitCount); if (ISO14443_CRCA(Buffer, BitCount / 8)) { CardCandidatesIdx = 0; return Reader14443A_Deselect(Buffer); } switch (Buffer[3]) { case 0x00: CardCandidatesIdx = 1; CardCandidates[0] = CardType_NXP_MIFARE_DESFire; break; case 0x01: CardCandidatesIdx = 1; CardCandidates[0] = CardType_NXP_MIFARE_DESFire_EV1; break; default: CardCandidatesIdx = 0; } break; default: break; } } if (CardCandidatesIdx == 0) { CommandLinePendingTaskFinished(COMMAND_INFO_OK_WITH_TEXT_ID, "Unknown card type."); } else if (CardCandidatesIdx == 1) { char tmpType[64]; memcpy_P(tmpType, &CardIdentificationList[CardCandidates[0]].Type, 64); CommandLinePendingTaskFinished(COMMAND_INFO_OK_WITH_TEXT_ID, tmpType); } else { char tmpBuf[TERMINAL_BUFFER_SIZE]; uint16_t size = 0, tmpsize = 0; bool enoughspace = true; uint8_t i; for (i = 0; i < CardCandidatesIdx; i++) { if (size <= TERMINAL_BUFFER_SIZE) // prevents buffer overflow { char tmpType[64]; memcpy_P(tmpType, &CardIdentificationList[CardCandidates[i]].Type, 64); tmpsize = snprintf(tmpBuf + size, TERMINAL_BUFFER_SIZE - size, "%s or ", tmpType); size += tmpsize; } else { break; } } if (size > TERMINAL_BUFFER_SIZE) { size -= tmpsize; enoughspace = false; } tmpBuf[size-4] = '.'; tmpBuf[size-3] = '\0'; CommandLinePendingTaskFinished(COMMAND_INFO_OK_WITH_TEXT_ID, tmpBuf); if (!enoughspace) TerminalSendString("There is at least one more card type candidate, but there was not enough terminal buffer space.\r\n"); } // print general data TerminalSendString("ATQA:\t"); CommandLineAppendData(&CardCharacteristics.ATQA, 2); TerminalSendString("UID:\t"); CommandLineAppendData(CardCharacteristics.UID, CardCharacteristics.UIDSize); TerminalSendString("SAK:\t"); CommandLineAppendData(&CardCharacteristics.SAK, 1); Reader14443CurrentCommand = Reader14443_Do_Nothing; CardCandidatesIdx = 0; CodecReaderFieldStop(); Selected = false; return 0; } return rVal; } default: // e.g. Do_Nothing return 0; } return 0; } uint16_t ISO14443_CRCA(uint8_t * Buffer, uint8_t ByteCount) { uint8_t * DataPtr = Buffer; uint16_t crc = 0x6363; uint8_t ch; while (ByteCount--) { ch = *DataPtr++ ^ crc; ch = ch ^ (ch << 4); crc = (crc >> 8) ^ (ch << 8) ^ (ch << 3) ^ (ch >> 4); } return crc; }