From 15be871bb2339f1e6ce43023bace3f6286c5a1a5 Mon Sep 17 00:00:00 2001 From: Maxie Dion Schmidt Date: Sat, 23 Jul 2022 00:16:22 -0400 Subject: [PATCH] Restore point for changes to the CL1/CL2 exchanges in the anticollision for DF ISO14443A-4 support --- .../DESFire/DESFireChameleonTerminal.c | 5 +- .../DESFire/DESFireISO14443Support.c | 198 ++++++++++-------- .../DESFire/DESFireISO14443Support.h | 36 +++- .../DESFire/DESFireISO7816Support.c | 7 + .../DESFire/DESFireISO7816Support.h | 11 +- .../Application/DESFire/DESFirePICCControl.c | 39 +++- .../DESFire/DESFirePICCHeaderLayout.h | 11 +- .../Chameleon-Mini/Application/ISO14443-3A.c | 41 ++-- .../Chameleon-Mini/Application/ISO14443-3A.h | 5 +- 9 files changed, 214 insertions(+), 139 deletions(-) diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFireChameleonTerminal.c b/Firmware/Chameleon-Mini/Application/DESFire/DESFireChameleonTerminal.c index 2b337640..c8db3727 100644 --- a/Firmware/Chameleon-Mini/Application/DESFire/DESFireChameleonTerminal.c +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFireChameleonTerminal.c @@ -68,8 +68,9 @@ CommandStatusIdType CommandDESFireSetHeaderProperty(char *OutParam, const char * if (dataByteCount != 2) { StatusError = 1; } else { - DesfireATQAValue = ((propSpecBytes[0] << 8) & 0xFF00) | (propSpecBytes[1] & 0x00FF); - memcpy(&Picc.ATSBytes[0], propSpecBytes, dataByteCount); + Picc.ATQA[0] = propSpecBytes[0]; + Picc.ATQA[1] = propSpecBytes[1]; + DesfireATQAReset = true; } } else if (!strcasecmp_P(hdrPropSpecStr, PSTR("ManuID"))) { if (dataByteCount != 1) { diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFireISO14443Support.c b/Firmware/Chameleon-Mini/Application/DESFire/DESFireISO14443Support.c index 88c8886d..ca4fbe06 100644 --- a/Firmware/Chameleon-Mini/Application/DESFire/DESFireISO14443Support.c +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFireISO14443Support.c @@ -79,6 +79,16 @@ void ISO144434Reset(void) { ISO14443ALastIncomingDataFrame[0] = 0x00; } +static uint16_t GetNAKCommandData(uint8_t *Buffer, bool ResetToHaltState); +static uint16_t GetNAKCommandData(uint8_t *Buffer, bool ResetToHaltState) { + if (ResetToHaltState) { + ISO144433AHalt(); + StateRetryCount = 0; + } + Buffer[0] = ISO14443A_NAK; + return ASBITS(1); +} + uint16_t ISO144434ProcessBlock(uint8_t *Buffer, uint16_t ByteCount, uint16_t BitCount) { uint8_t PCB = Buffer[0]; @@ -87,19 +97,21 @@ uint16_t ISO144434ProcessBlock(uint8_t *Buffer, uint16_t ByteCount, uint16_t Bit uint8_t HaveCID = 0, HaveNAD = 0; /* Verify the block's length: at the very least PCB + CRCA */ - if (ByteCount < (1 + ISO14443A_CRCA_SIZE)) { - /* Broken frame -- Respond error by returning an empty frame */ - DEBUG_PRINT_P(PSTR("ISO14443-4: length fail")); - return ISO14443A_APP_NO_RESPONSE; + if (ByteCount < 1 + ISO14443A_CRCA_SIZE) { + /* Broken frame -- Respond with error by returning an empty frame */ + //return ISO14443A_APP_NO_RESPONSE; + } else { + ByteCount -= 2; } - ByteCount -= 2; /* Verify the checksum; fail if doesn't match */ - if (!ISO14443ACheckCRCA(Buffer, ByteCount)) { + if (ByteCount >= 1 + ISO14443A_CRCA_SIZE && !ISO14443ACheckCRCA(Buffer, ByteCount)) { DesfireLogEntry(LOG_ERR_APP_CHECKSUM_FAIL, (uint8_t *) NULL, 0); /* ISO/IEC 14443-4, clause 7.5.5. The PICC does not attempt any error recovery. */ - DEBUG_PRINT_P(PSTR("WARN: 14443-4: CRC fail")); - return ISO14443A_APP_NO_RESPONSE; + DEBUG_PRINT_P(PSTR("ISO14443-4: CRC fail")); + /* Invalid data received -- Respond with NAK */ + return GetNAKCommandData(Buffer, false); + //return ISO14443A_APP_NO_RESPONSE; } switch (Iso144434State) { @@ -108,14 +120,14 @@ uint16_t ISO144434ProcessBlock(uint8_t *Buffer, uint16_t ByteCount, uint16_t Bit /* See: ISO/IEC 14443-4, clause 5.6.1.2 */ if (Buffer[0] != ISO14443A_CMD_RATS) { /* Ignore blocks other than RATS and HLTA */ - DEBUG_PRINT_P(PSTR("ISO14443-4: NOT RATS")); - return ISO14443A_APP_NO_RESPONSE; + DEBUG_PRINT_P(PSTR("ISO14443-4: NOT-RATS")); + return GetNAKCommandData(Buffer, false); + //return ISO14443A_APP_NO_RESPONSE; } /* Process RATS. * NOTE: ATS bytes are tailored to Chameleon implementation and differ from DESFire spec. * NOTE: Some PCD implementations do a memcmp() over ATS bytes, which is completely wrong. */ - //DEBUG_PRINT_P(PSTR("ISO14443-4: SEND RATS")); Iso144434CardID = Buffer[1] & 0x0F; Buffer[0] = 0x06; memcpy(&Buffer[1], &Picc.ATSBytes[1], 4); @@ -142,13 +154,15 @@ uint16_t ISO144434ProcessBlock(uint8_t *Buffer, uint16_t ByteCount, uint16_t Bit if ((Buffer[1] & 0xF) != Iso144434CardID) { /* Different card ID -- the frame is ignored */ DEBUG_PRINT_P(PSTR("ISO14443-4: NEW CARD ID %02d"), Iso144434CardID); - return ISO14443A_APP_NO_RESPONSE; + return GetNAKCommandData(Buffer, false); + //return ISO14443A_APP_NO_RESPONSE; } } break; } case ISO14443_4_STATE_LAST: { - return ISO14443A_APP_NO_RESPONSE; + return GetNAKCommandData(Buffer, false); + //return ISO14443A_APP_NO_RESPONSE; } default: break; @@ -168,7 +182,8 @@ uint16_t ISO144434ProcessBlock(uint8_t *Buffer, uint16_t ByteCount, uint16_t Bit if (PCB & ISO14443_PCB_I_BLOCK_CHAINING_MASK) { /* Currently not supported -- the frame is ignored */ DEBUG_PRINT_P(PSTR("ISO144434ProcessBlock: ISO14443_PCB_I_BLOCK")); - return ISO14443A_APP_NO_RESPONSE; + return GetNAKCommandData(Buffer, false); + //return ISO14443A_APP_NO_RESPONSE; } /* Build the prologue for the response */ @@ -183,8 +198,8 @@ uint16_t ISO144434ProcessBlock(uint8_t *Buffer, uint16_t ByteCount, uint16_t Bit ByteCount = MifareDesfireProcessCommand(&Buffer[PrologueLength], ByteCount - PrologueLength); /* Short-circuit in case the app decides not to respond at all */ if (ByteCount == 0) { - DEBUG_PRINT_P(PSTR("ISO14443-4: APP_NO_RESP")); - return ISO14443A_APP_NO_RESPONSE; + return GetNAKCommandData(Buffer, false); + //return ISO14443A_APP_NO_RESPONSE; } ByteCount += PrologueLength; DEBUG_PRINT_P(PSTR("ISO14443-4: I-BLK")); @@ -195,7 +210,8 @@ uint16_t ISO144434ProcessBlock(uint8_t *Buffer, uint16_t ByteCount, uint16_t Bit /* 7.5.4.3, rule 11 */ if ((PCB & ISO14443_PCB_BLOCK_NUMBER_MASK) == MyBlockNumber) { DEBUG_PRINT_P(PSTR("ISO144434ProcessBlock: ISO14443_PCB_R_BLOCK")); - return ISO14443A_APP_NO_RESPONSE; + return GetNAKCommandData(Buffer, false); + //return ISO14443A_APP_NO_RESPONSE; } if (PCB & ISO14443_PCB_R_BLOCK_ACKNAK_MASK) { /* 7.5.4.3, rule 12 */ @@ -212,7 +228,8 @@ uint16_t ISO144434ProcessBlock(uint8_t *Buffer, uint16_t ByteCount, uint16_t Bit memcpy(&Buffer[0], &ISO14443ALastIncomingDataFrame[0], ASBYTES(ISO14443ALastIncomingDataFrameBits)); return ISO14443ALastIncomingDataFrameBits; } else { - return ISO14443A_APP_NO_RESPONSE; + return GetNAKCommandData(Buffer, false); + //return ISO14443A_APP_NO_RESPONSE; } } DEBUG_PRINT_P(PSTR("ISO14443-4: R-BLK")); @@ -231,8 +248,9 @@ uint16_t ISO144434ProcessBlock(uint8_t *Buffer, uint16_t ByteCount, uint16_t Bit DEBUG_PRINT_P(PSTR("ISO14443-4: S-BLK")); return GetAndSetBufferCRCA(Buffer, ByteCount); } - DEBUG_PRINT_P(PSTR("ISO14443-4: PCB_S_BLK NO_RESP")); - return ISO14443A_APP_NO_RESPONSE; + DEBUG_PRINT_P(PSTR("ISO14443-4: PCB_S_BLK NAK")); + return GetNAKCommandData(Buffer, false); + //return ISO14443A_APP_NO_RESPONSE; } default: @@ -241,7 +259,8 @@ uint16_t ISO144434ProcessBlock(uint8_t *Buffer, uint16_t ByteCount, uint16_t Bit } /* Fall through: */ - return ISO14443A_APP_NO_RESPONSE; + return GetNAKCommandData(Buffer, false); + //return ISO14443A_APP_NO_RESPONSE; } @@ -280,9 +299,8 @@ void ISO144433AReset(void) { } void ISO144433AHalt(void) { - ISO144433ASwitchState(ISO14443_3A_STATE_HALT); - Iso144433AIdleState = ISO14443_3A_STATE_HALT; ISO144433AReset(); + ISO144433ASwitchState(ISO14443_3A_STATE_HALT); } bool ISO144433AIsHalt(const uint8_t *Buffer, uint16_t BitCount) { @@ -291,6 +309,17 @@ bool ISO144433AIsHalt(const uint8_t *Buffer, uint16_t BitCount) { Buffer[1] == 0x00 && ISO14443ACheckCRCA(Buffer, ASBYTES(ISO14443A_HLTA_FRAME_SIZE)); } +static uint16_t GetHLTACommandData(uint8_t *Buffer, bool ResetToHaltState); +static uint16_t GetHLTACommandData(uint8_t *Buffer, bool ResetToHaltState) { + if (ResetToHaltState) { + ISO144433AHalt(); + StateRetryCount = 0; + } + Buffer[0] == ISO14443A_CMD_HLTA; + Buffer[1] == 0x00; + ISO14443AAppendCRCA(Buffer, ASBYTES(ISO14443A_HLTA_FRAME_SIZE)); + return ISO14443A_HLTA_FRAME_SIZE + ASBITS(ISO14443A_CRCA_SIZE); +} uint16_t ISO144433APiccProcess(uint8_t *Buffer, uint16_t BitCount) { @@ -311,18 +340,30 @@ uint16_t ISO144433APiccProcess(uint8_t *Buffer, uint16_t BitCount) { StateRetryCount = 0; } else if (ISO144433AIsHalt(Buffer, BitCount)) { DesfireLogEntry(LOG_INFO_APP_CMD_HALT, NULL, 0); - DEBUG_PRINT_P(PSTR("ISO14443-3: HALTING")); - ISO144433AHalt(); - return ISO14443A_APP_NO_RESPONSE; - } else if (CheckStateRetryCount(false)) { - /* ??? TODO: Is this the correct action ??? */ - DEBUG_PRINT_P(PSTR("ISO14443-3: RESETTING")); - ISO144433AHalt(); - Buffer[0] == ISO14443A_CMD_HLTA; - Buffer[1] == 0x00; - ISO14443AAppendCRCA(Buffer, ASBYTES(ISO14443A_HLTA_FRAME_SIZE)); - return ISO14443A_HLTA_FRAME_SIZE + ASBITS(ISO14443A_CRCA_SIZE); - //return ISO14443A_APP_NO_RESPONSE; + return GetHLTACommandData(Buffer, true); + } else if (IsDeselectCmd(Cmd)) { + return GetHLTACommandData(Buffer, true); + } else if (IsRIDCmd(Cmd)) { + Iso144433AState = ISO14443_3A_STATE_ACTIVE; + StateRetryCount = 0; + /* Response to RID command as specified in section 7.3.10 (page 139) of the + * NXP PN532 Manual (InDeselect command specification): + * https://www.nxp.com/docs/en/user-guide/141520.pdf + */ + uint16_t respDataSize = (uint16_t) sizeof(MIFARE_DESFIRE_TAG_AID); + memcpy(&Buffer[0], MIFARE_DESFIRE_TAG_AID, respDataSize); + /* ??? TODO: Do we append CRCA bytes ??? + * ISO14443AAppendCRCA(Buffer, respDataSize); + * respDataSize += 2; + */ + return ASBITS(respDataSize); + } else if (IsUnsupportedCmd(Cmd)) { + return GetNAKCommandData(Buffer, true); + } else if (CheckStateRetryCount(false)) { /* Increment the state retry count to keep track of when to timeout */ + DEBUG_PRINT_P(PSTR("ISO14443-3: SW-RESET")); + return GetHLTACommandData(Buffer, true); + } else if (BitCount <= BITS_PER_BYTE) { + return ISO144434ProcessBlock(Buffer, ASBYTES(BitCount), BitCount); } /* This implements ISO 14443-3A state machine */ @@ -331,7 +372,7 @@ uint16_t ISO144433APiccProcess(uint8_t *Buffer, uint16_t BitCount) { case ISO14443_3A_STATE_HALT: if (!ISO14443ACmdIsWUPA(Cmd)) { - DEBUG_PRINT_P(PSTR("ISO14443-4: HALT -- NOT WUPA")); + DEBUG_PRINT_P(PSTR("ISO14443-4: HLT-NOT-WUPA")); break; } else { ISO144433ASwitchState(ISO14443_3A_STATE_IDLE); @@ -340,93 +381,78 @@ uint16_t ISO144433APiccProcess(uint8_t *Buffer, uint16_t BitCount) { case ISO14443_3A_STATE_IDLE: Iso144433AIdleState = Iso144433AState; ISO144433ASwitchState(ISO14443_3A_STATE_READY_CL1); - Buffer[0] = DesfireATQAValue & 0x00FF; - Buffer[1] = (DesfireATQAValue >> 8) & 0x00FF; + /* The LSByte ordering of the ATQA value for ISO14443 tags is + * discussed in section 2.3 of NXP AN10833. + */ + Buffer[0] = Picc.ATQA[1]; + Buffer[1] = Picc.ATQA[0]; return ASBITS(ISO14443A_ATQA_FRAME_SIZE_BYTES); case ISO14443_3A_STATE_READY_CL1: case ISO14443_3A_STATE_READY_CL1_NVB_END: if (Cmd == ISO14443A_CMD_SELECT_CL1) { - /* Load UID CL1 and perform anticollisio: */ + /* Load UID CL1 and perform anticollision: */ ConfigurationUidType Uid; - ApplicationGetUid(Uid); - if (ActiveConfiguration.UidSize >= ISO14443A_UID_SIZE_DOUBLE) { - Uid[0] = ISO14443A_UID0_CT; - } - uint8_t cl1SAKValue = IS_ISO14443A_4_COMPLIANT(Buffer[1]) ? ISO14443A_SAK_INCOMPLETE : ISO14443A_SAK_INCOMPLETE_NOT_COMPLIANT; - Buffer[1] = MAKE_ISO14443A_4_COMPLIANT(Buffer[1]); - if (Buffer[1] == ISO14443A_NVB_AC_START && !ISO14443ASelectDesfire(Buffer, &BitCount, &Uid[0], 4, cl1SAKValue) && BitCount > 0) { - //DEBUG_PRINT_P(PSTR("ISO14443-4: Select CL1 NVB START -- OK")); + ApplicationGetUid(&Uid[1]); + Uid[0] = ISO14443A_UID0_CT; + /* NXP AN10927 (section 2, figure 1, page 3) shows the expected + * data flow to exchange the 7-byte UID for DESFire tags: + * http://www.nxp.com/docs/en/application-note/AN10927.pdf + */ + uint8_t cl1SAKValue = SAK_CL1_VALUE; + if (Buffer[1] == ISO14443A_NVB_AC_START && ISO14443ASelectDesfire(&Buffer[0], 0, &BitCount, &Uid[0], 4, cl1SAKValue)) { ISO144433ASwitchState(ISO14443_3A_STATE_READY_CL1_NVB_END); return BitCount; - } else if (Buffer[1] == ISO14443A_NVB_AC_END && ISO14443ASelectDesfire(Buffer, &BitCount, &Uid[0], 4, cl1SAKValue)) { - //DEBUG_PRINT_P(PSTR("ISO14443-4: Select CL1 NVB END -- OK")); + } else if (Buffer[1] == ISO14443A_NVB_AC_END && ISO14443ASelectDesfire(&Buffer[0], 0, &BitCount, &Uid[0], 4, cl1SAKValue)) { ISO144433ASwitchState(ISO14443_3A_STATE_READY_CL2); return BitCount; } else { - DEBUG_PRINT_P(PSTR("ISO14443-4: Select CL1 NOT OK")); + DEBUG_PRINT_P(PSTR("ISO14443-4: Select CL1-NOT-OK")); } } else { - DEBUG_PRINT_P(PSTR("ISO14443-4: RDY1 -- NOT SLCT CMD")); + DEBUG_PRINT_P(PSTR("ISO14443-4: RDY1 -- NOT-SLCT-CMD")); } CheckStateRetryCount(false); - return ISO14443A_APP_NO_RESPONSE; + return GetNAKCommandData(Buffer, false); + //return ISO14443A_APP_NO_RESPONSE; case ISO14443_3A_STATE_READY_CL2: case ISO14443_3A_STATE_READY_CL2_NVB_END: if (Cmd == ISO14443A_CMD_SELECT_CL2 && ActiveConfiguration.UidSize >= ISO14443A_UID_SIZE_DOUBLE) { /* Load UID CL2 and perform anticollision: */ ConfigurationUidType Uid; - ApplicationGetUid(Uid); - uint8_t cl2SAKValue = IS_ISO14443A_4_COMPLIANT(Buffer[1]) ? ISO14443A_SAK_COMPLETE_COMPLIANT : ISO14443A_SAK_COMPLETE_NOT_COMPLIANT; - Buffer[1] = MAKE_ISO14443A_4_COMPLIANT(Buffer[1]); - if (Buffer[1] == ISO14443A_NVB_AC_START && !ISO14443ASelectDesfire(Buffer, &BitCount, &Uid[4], 3, cl2SAKValue) && BitCount > 0) { - //DEBUG_PRINT_P(PSTR("ISO14443-4: Select CL2 NVB START -- OK")); + ApplicationGetUid(&Uid[0]); + uint8_t cl2SAKValue = SAK_CL2_VALUE; + if (Buffer[1] == ISO14443A_NVB_AC_START && ISO14443ASelectDesfire(&Buffer[0], 0, &BitCount, &Uid[4], 4, cl2SAKValue)) { ISO144433ASwitchState(ISO14443_3A_STATE_READY_CL2_NVB_END); return BitCount; - } else if (Buffer[1] == ISO14443A_NVB_AC_END && ISO14443ASelectDesfire(Buffer, &BitCount, &Uid[4], 3, cl2SAKValue)) { - //DEBUG_PRINT_P(PSTR("ISO14443-4: Select CL2 NVB END -- OK")); + } else if (Buffer[1] == ISO14443A_NVB_AC_END && ISO14443ASelectDesfire(&Buffer[0], 0, &BitCount, &Uid[4], 4, cl2SAKValue)) { ISO144433ASwitchState(ISO14443_3A_STATE_ACTIVE); return BitCount; } else { - DEBUG_PRINT_P(PSTR("ISO14443-4: Select CL2 NOT OK")); + DEBUG_PRINT_P(PSTR("ISO14443-4: Select CL2-NOT-OK")); } } else { - DEBUG_PRINT_P(PSTR("ISO14443-4: RDY2 -- NOT SLCT CMD")); + DEBUG_PRINT_P(PSTR("ISO14443-4: RDY2 -- NOT-SLCT-CMD")); } CheckStateRetryCount(false); - return ISO14443A_APP_NO_RESPONSE; + return GetNAKCommandData(Buffer, false); + //return ISO14443A_APP_NO_RESPONSE; case ISO14443_3A_STATE_ACTIVE: StateRetryCount = MAX_STATE_RETRY_COUNT; - if (ISO144433AIsHalt(Buffer, BitCount)) { - /* Recognise the HLTA command: */ - DesfireLogEntry(LOG_INFO_APP_CMD_HALT, NULL, 0); - ISO144433AHalt(); - return ISO14443A_APP_NO_RESPONSE; - } else if (Cmd == ISO14443A_CMD_RATS) { - //DEBUG_PRINT_P(PSTR("ISO14443-3/4: Expecting RATS")); + if (Cmd == ISO14443A_CMD_RATS) { ISO144434SwitchState(ISO14443_4_STATE_EXPECT_RATS); } else if (Cmd == ISO14443A_CMD_SELECT_CL3) { - /* DESFire UID size is of insufficient size to support this: */ + /* DESFire UID size is of insufficient size to support this request: */ Buffer[0] = ISO14443A_SAK_COMPLETE_NOT_COMPLIANT; ISO14443AAppendCRCA(&Buffer[0], 1); return ISO14443A_SAK_FRAME_SIZE; - } else if (Cmd == ISO14443A_CMD_DESELECT) { - /* This has been observed to happen at this stage when swiping the - * Chameleon running CONFIG=MF_DESFIRE on an ACR122 USB external reader. - * ??? TODO: What are we supposed to return in this case ??? - */ - DesfireLogEntry(LOG_INFO_APP_CMD_DESELECT, NULL, 0); - //return ISO14443A_APP_NO_RESPONSE; - return BitCount; } - /* Forward to ISO/IEC 14443-4 processing code */ - //DEBUG_PRINT_P(PSTR("ISO14443-4: ACTIVE RET")); - uint16_t ByteCount = ASBYTES(BitCount); - uint16_t ReturnBits = ISO144434ProcessBlock(Buffer, ByteCount, BitCount); + /* Forward to ISO/IEC 14443-4 block processing code */ + uint16_t ReturnBits = ISO144434ProcessBlock(Buffer, ASBYTES(BitCount), BitCount); return ReturnBits; default: @@ -436,12 +462,10 @@ uint16_t ISO144433APiccProcess(uint8_t *Buffer, uint16_t BitCount) { /* Fallthrough: Unknown command. Reset back to idle/halt state. */ if (!CheckStateRetryCount(false)) { - DEBUG_PRINT_P(PSTR("ISO14443-3: Fall through -- RESET TO IDLE 0x%02x"), Cmd); - return ISO14443A_APP_NO_RESPONSE; - } else { - DEBUG_PRINT_P(PSTR("ISO14443-4: UNK-CMD NO RESP")); - return ISO14443A_APP_NO_RESPONSE; + DEBUG_PRINT_P(PSTR("ISO14443-3: RST-TO-IDLE 0x%02x"), Cmd); } + return GetNAKCommandData(Buffer, false); + //return ISO14443A_APP_NO_RESPONSE; } diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFireISO14443Support.h b/Firmware/Chameleon-Mini/Application/DESFire/DESFireISO14443Support.h index 5f390910..4f926e8a 100644 --- a/Firmware/Chameleon-Mini/Application/DESFire/DESFireISO14443Support.h +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFireISO14443Support.h @@ -29,6 +29,7 @@ This notice must be retained at the top of all source files where indicated. #include "DESFireFirmwareSettings.h" #include "DESFireUtils.h" +#include "DESFireISO7816Support.h" #include "../ISO14443-3A.h" @@ -40,15 +41,40 @@ This notice must be retained at the top of all source files where indicated. * CRC-16 */ +/* Refer to Table 10 in section 9.3 (page 15) of the NXP Mifare Classic EV1 1K data sheet: + * https://www/nxp.com/docs/en/data-sheet/MF1S50YYX_V1.pdf + */ +#define ISO14443A_ACK 0xA0 +#define ISO14443A_NAK 0x00 // 0x04 + +/* See Table 13 in section 7.1 (page 67) of the NXP PN532 User Manual (error Handling / status codes): + * https://www.nxp.com/docs/en/user-guide/141520.pdf + */ +#define ISO14443A_CRCA_ERROR 0x02 +#define ISO14443A_CRC_FRAME_SIZE ASBITS(ISO14443A_CRCA_SIZE) + #define ISO14443A_CMD_RATS 0xE0 #define ISO14443A_RATS_FRAME_SIZE ASBITS(6) -#define ISO14443A_CMD_RNAK 0xB2 -#define ISO14443A_CRC_FRAME_SIZE ASBITS(ISO14443A_CRCA_SIZE) -#define ISO14443A_CMD_DESELECT 0xC2 + +#define NXP_PN532_CMD_INDESELECT 0x44 +#define IsDeselectCmd(cmdCode) (cmdCode == NXP_PN532_CMD_INDESELECT) #define ISO14443A_DESELECT_FRAME_SIZE (ISO14443A_HLTA_FRAME_SIZE + ASBITS(ISO14443A_CRCA_SIZE)) -#define ISO14443ACmdIsPM3WUPA(cmd) ((cmd & 0x54) == 0x54) -#define ISO14443ACmdIsWUPA(cmd) ((cmd == ISO14443A_CMD_WUPA) || ISO14443ACmdIsPM3WUPA(cmd)) +#define NXP_PN532_INSELECT_CMD 0x54 +#define ISO14443ACmdIsPM3WUPA(cmdCode) (cmdCode == NXP_PN532_INSELECT_CMD) +#define ISO14443ACmdIsWUPA(cmdCode) ((cmdCode == ISO14443A_CMD_WUPA) || ISO14443ACmdIsPM3WUPA(cmdCode)) + +#define IsRIDCmd(cmdCode) (cmdCode == ISO7816_RID_CMD) + +/* A quick way to catch and handle the bytes the Hid Omnikey 5022CL and ACR-122 USB readers + * will throw out to identify NFC tags in a promiscuous blanket enumeration of possibilities + * while running 'pcsc_spy -v'. The strategy is to respond with a NAK and continue to ignore + * these incoming commands. + */ +#define MIFARE_RESTORE_CMD 0xC2 +#define NFCTAG_TYPE12_TERMINATOR_TLV 0xFE +#define NXP_PN532_CMD_TGGETINITIATOR 0x88 +#define IsUnsupportedCmd(cmdCode) ((cmdCode == MIFARE_RESTORE_CMD) || (cmdCode == NFCTAG_TYPE12_TERMINATOR_TLV) || (cmdCode == NXP_PN532_CMD_TGGETINITIATOR)) #define ISO14443_PCB_BLOCK_TYPE_MASK 0xC0 #define ISO14443_PCB_I_BLOCK 0x00 diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFireISO7816Support.c b/Firmware/Chameleon-Mini/Application/DESFire/DESFireISO7816Support.c index b3a196e9..4a61ed6c 100644 --- a/Firmware/Chameleon-Mini/Application/DESFire/DESFireISO7816Support.c +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFireISO7816Support.c @@ -29,6 +29,13 @@ This notice must be retained at the top of all source files where indicated. #include "DESFireStatusCodes.h" #include "../ISO14443-3A.h" +/* Data for the NDEF Tag Application / Mifare DESFire Tag Application in the table: + * https://www.eftlab.com/knowledge-base/211-emv-aid-rid-pix/ + */ +const uint8_t MIFARE_DESFIRE_TAG_AID[9] = { + 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x00 +}; + Iso7816WrappedParams_t Iso7816P1Data = ISO7816_NO_DATA; Iso7816WrappedParams_t Iso7816P2Data = ISO7816_NO_DATA; bool Iso7816FileSelected = false; diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFireISO7816Support.h b/Firmware/Chameleon-Mini/Application/DESFire/DESFireISO7816Support.h index 9fe78979..cb62cd6e 100644 --- a/Firmware/Chameleon-Mini/Application/DESFire/DESFireISO7816Support.h +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFireISO7816Support.h @@ -27,8 +27,12 @@ This notice must be retained at the top of all source files where indicated. #include #include -#define Iso7816CLA(cmdCode) \ - (cmdCode == DESFIRE_ISO7816_CLA) +#define Iso7816CLA(cmdCode) (cmdCode == DESFIRE_ISO7816_CLA) + +#define ISO7816_RID_CMD 0x78 +#define IsRIDCmd(cmdCode) (cmdCode == ISO7816_RID_CMD) + +extern const uint8_t MIFARE_DESFIRE_TAG_AID[9]; #define ISO7816_PROLOGUE_SIZE (2) #define ISO7816_STATUS_RESPONSE_SIZE (0x02) @@ -56,8 +60,7 @@ This notice must be retained at the top of all source files where indicated. #define ISO7816_ERROR_SW2_WRONG_FSPARAMS (0x00) #define ISO7816_ERROR_SW2_EOF (0x82) -#define AppendSW12Bytes(sw1, sw2) \ - ((uint16_t) ((sw1 << 8) | (sw2 & 0xff))) +#define AppendSW12Bytes(sw1, sw2) ((uint16_t) ((sw1 << 8) | (sw2 & 0xff))) /* Some of the wrapped ISO7816 commands have extra meaning * packed into the P1-P2 bytes of the APDU byte array. diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFirePICCControl.c b/Firmware/Chameleon-Mini/Application/DESFire/DESFirePICCControl.c index 3d55dd84..fabd1ab9 100644 --- a/Firmware/Chameleon-Mini/Application/DESFire/DESFirePICCControl.c +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFirePICCControl.c @@ -59,7 +59,7 @@ SIZET DESFIRE_INITIAL_FIRST_FREE_BLOCK_ID = 0; SIZET DESFIRE_FIRST_FREE_BLOCK_ID = 0; SIZET CardCapacityBlocks = 0; -uint16_t DesfireATQAValue = DESFIRE_DEFAULT_ATQA_VALUE; +bool DesfireATQAReset = false; void InitBlockSizes(void) { DESFIRE_PICC_INFO_BLOCK_ID = 0; @@ -174,6 +174,7 @@ void InitialisePiccBackendEV0(uint8_t StorageSize, bool formatPICC) { } else { MemoryRestoreDesfireHeaderBytes(false); ReadBlockBytes(&AppDir, DESFIRE_APP_DIR_BLOCK_ID, sizeof(DESFireAppDirType)); + DesfireATQAReset = true; SelectPiccApp(); } } @@ -193,6 +194,7 @@ void InitialisePiccBackendEV1(uint8_t StorageSize, bool formatPICC) { } else { MemoryRestoreDesfireHeaderBytes(false); ReadBlockBytes(&AppDir, DESFIRE_APP_DIR_BLOCK_ID, sizeof(DESFireAppDirType)); + DesfireATQAReset = true; SelectPiccApp(); } } @@ -212,6 +214,7 @@ void InitialisePiccBackendEV2(uint8_t StorageSize, bool formatPICC) { } else { MemoryRestoreDesfireHeaderBytes(false); ReadBlockBytes(&AppDir, DESFIRE_APP_DIR_BLOCK_ID, sizeof(DESFireAppDirType)); + DesfireATQAReset = true; SelectPiccApp(); } @@ -255,13 +258,26 @@ void FormatPicc(void) { memset(&AppDir, 0x00, sizeof(DESFireAppDirType)); memset(&SelectedApp, 0x00, sizeof(SelectedAppCacheType)); /* Set a random new UID */ - BYTE uidData[DESFIRE_UID_SIZE - 1]; - RandomGetBuffer(uidData, DESFIRE_UID_SIZE - 1); - memcpy(&Picc.Uid[1], uidData, DESFIRE_UID_SIZE - 1); - /* Conform to NXP Application Note AN10927 about the first - * byte of a randomly generated UID (refer to section 2.1.1). + BYTE uidData[DESFIRE_UID_SIZE]; + RandomGetBuffer(uidData, DESFIRE_UID_SIZE); + memcpy(&Picc.Uid[0], uidData, DESFIRE_UID_SIZE); + if (Picc.Uid[0] == ISO14443A_UID0_RANDOM) { + Picc.Uid[0] != 0x30; + } + /* OLD: Conform to NXP Application Note AN10927 about the first + * byte of a randomly generated UID (refer to section 2.1.1). + */ + //Picc.Uid[0] = ISO14443A_UID0_RANDOM; + //uint16_t ATQAValue = DESFIRE_ATQA_RANDOM_UID; + /* NEW: NXP AN10927 (section 2.1.1, page 5) states that a random + * UID (RID) is always limited to 4 bytes. This limitation + * is avoided by just setting the whole buffer to a random + * value whose first byte is not 0x08. */ - Picc.Uid[0] = ISO14443A_UID0_RANDOM; + uint16_t ATQAValue = DESFIRE_ATQA_DEFAULT; + Picc.ATQA[0] = (uint8_t)((ATQAValue >> 8) & 0x00FF); + Picc.ATQA[1] = (uint8_t)(ATQAValue & 0x00FF); + DesfireATQAReset = false; /* Randomize the initial batch number data: */ BYTE batchNumberData[5]; RandomGetBuffer(batchNumberData, 5); @@ -354,11 +370,18 @@ void FactoryFormatPiccEV2(uint8_t StorageSize) { } void GetPiccUid(ConfigurationUidType Uid) { - memcpy(Uid, Picc.Uid, DESFIRE_UID_SIZE + 1); + memcpy(Uid, &Picc.Uid[0], DESFIRE_UID_SIZE); } void SetPiccUid(ConfigurationUidType Uid) { memcpy(&Picc.Uid[0], Uid, DESFIRE_UID_SIZE); + DesfireATQAReset = true; + //if (!DesfireATQAReset) { + // uint16_t ATQAValue = DESFIRE_ATQA_DEFAULT; + // Picc.ATQA[0] = (uint8_t)((ATQAValue >> 8) & 0x00FF); + // Picc.ATQA[1] = (uint8_t)(ATQAValue & 0x00FF); + // DesfireATQAReset = true; + //} SynchronizePICCInfo(); } diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFirePICCHeaderLayout.h b/Firmware/Chameleon-Mini/Application/DESFire/DESFirePICCHeaderLayout.h index 704471f8..ec324b42 100644 --- a/Firmware/Chameleon-Mini/Application/DESFire/DESFirePICCHeaderLayout.h +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFirePICCHeaderLayout.h @@ -48,16 +48,12 @@ This notice must be retained at the top of all source files where indicated. */ /* Anticollision parameters */ -#define DESFIRE_DEFAULT_ATQA_VALUE 0x0344 -extern uint16_t DesfireATQAValue; +#define DESFIRE_ATQA_DEFAULT 0x0344 +#define DESFIRE_ATQA_RANDOM_UID 0x0304 +extern bool DesfireATQAReset; -#ifndef FORCE_SAK_NOT_COMPLIANT -#define SAK_CL1_VALUE (ISO14443A_SAK_INCOMPLETE) -#define SAK_CL2_VALUE (ISO14443A_SAK_COMPLETE_COMPLIANT) -#else #define SAK_CL1_VALUE (ISO14443A_SAK_INCOMPLETE_NOT_COMPLIANT) #define SAK_CL2_VALUE (ISO14443A_SAK_COMPLETE_NOT_COMPLIANT) -#endif #define STATUS_FRAME_SIZE (1 * 8) /* Bits */ @@ -138,6 +134,7 @@ typedef struct DESFIRE_FIRMWARE_PACKING DESFIRE_FIRMWARE_ALIGNAT { uint8_t BatchNumber[5] DESFIRE_FIRMWARE_ALIGNAT; uint8_t ProductionWeek; uint8_t ProductionYear; + uint8_t ATQA[2]; uint8_t ATSBytes[5]; /* Dynamic data: changes during the PICC's lifetime */ uint16_t FirstFreeBlock; diff --git a/Firmware/Chameleon-Mini/Application/ISO14443-3A.c b/Firmware/Chameleon-Mini/Application/ISO14443-3A.c index 2df53589..b453dc94 100644 --- a/Firmware/Chameleon-Mini/Application/ISO14443-3A.c +++ b/Firmware/Chameleon-Mini/Application/ISO14443-3A.c @@ -10,25 +10,36 @@ #ifdef CONFIG_MF_DESFIRE_SUPPORT #include "DESFire/DESFireISO14443Support.h" -bool ISO14443ASelectDesfire(void *Buffer, uint16_t *BitCount, uint8_t *UidCL, uint8_t UidByteCount, uint8_t SAKValue) { +bool ISO14443ASelectDesfire(void *Buffer, uint16_t Offset, uint16_t *BitCount, uint8_t *UidCL, uint8_t UidByteCount, uint8_t SAKValue) { + if (BitCount == NULL || ASBYTES(*BitCount) < Offset + 1) { + *BitCount = 0; + return false; + } uint8_t *DataPtr = (uint8_t *) Buffer; uint8_t NVB = DataPtr[1]; + /* According to the NXP Application Note AN10833, bit 6 of the SAK + * (mask of 0x20) indicates whether the PICC is compliant with the + * ISO/IEC14443-4 standard. The Mifare DESFire tags set this bit to one. + * Reference (section 2.2, page 7): + * https://www.nxp.com/docs/en/application-note/AN10833.pdf + */ + SAKValue = MAKE_ISO14443A_4_COMPLIANT(SAKValue); switch (NVB) { case ISO14443A_NVB_AC_START: /* Start of anticollision procedure. * Send whole UID CLn + BCC */ - memcpy(&DataPtr[0], &UidCL[0], UidByteCount); + memcpy(&DataPtr[Offset], &UidCL[0], UidByteCount); DataPtr[ISO14443A_CL_BCC_OFFSET] = ISO14443A_CALC_BCC(DataPtr); *BitCount = ISO14443A_CL_FRAME_SIZE; - return false; + return true; case ISO14443A_NVB_AC_END: /* End of anticollision procedure. * Send SAK CLn if we are selected. */ if (!memcmp(&DataPtr[2], &UidCL[0], UidByteCount)) { - DataPtr[0] = SAKValue; - ISO14443AAppendCRCA(Buffer, 1); + DataPtr[Offset] = SAKValue; + ISO14443AAppendCRCA(Buffer, Offset + 1); *BitCount = ISO14443A_SAK_FRAME_SIZE; return true; } else { @@ -36,24 +47,8 @@ bool ISO14443ASelectDesfire(void *Buffer, uint16_t *BitCount, uint8_t *UidCL, ui *BitCount = 0; return false; } - default: { - uint8_t CollisionByteCount = ((NVB >> 4) & 0x0f) - 2; - uint8_t CollisionBitCount = (NVB >> 0) & 0x0f; - uint8_t mask = 0xFF >> (8 - CollisionBitCount); - // Since the UidCL does not contain the BCC, we have to distinguish here - if ( - ((CollisionByteCount == 5 || (CollisionByteCount == 4 && CollisionBitCount > 0)) && memcmp(UidCL, &DataPtr[2], 4) == 0 && (ISO14443A_CALC_BCC(UidCL) & mask) == (DataPtr[6] & mask)) - || - (CollisionByteCount == 4 && CollisionBitCount == 0 && memcmp(UidCL, &DataPtr[2], 4) == 0) - || - (CollisionByteCount < 4 && memcmp(UidCL, &DataPtr[2], CollisionByteCount) == 0 && (UidCL[CollisionByteCount] & mask) == (DataPtr[CollisionByteCount + 2] & mask)) - ) { - memcpy(&DataPtr[0], &UidCL[0], UidByteCount); - DataPtr[ISO14443A_CL_BCC_OFFSET] = ISO14443A_CALC_BCC(DataPtr); - *BitCount = ISO14443A_CL_FRAME_SIZE; - return false; - } - } + default: + break; } /* No anticollision supported */ *BitCount = 0; diff --git a/Firmware/Chameleon-Mini/Application/ISO14443-3A.h b/Firmware/Chameleon-Mini/Application/ISO14443-3A.h index dab8fd9d..261ace28 100644 --- a/Firmware/Chameleon-Mini/Application/ISO14443-3A.h +++ b/Firmware/Chameleon-Mini/Application/ISO14443-3A.h @@ -45,8 +45,7 @@ #define CRC_INIT 0x6363 #define CRC_INIT_R 0xC6C6 /* Bit reversed */ -#define ISO14443A_CALC_BCC(ByteBuffer) \ - ( ByteBuffer[0] ^ ByteBuffer[1] ^ ByteBuffer[2] ^ ByteBuffer[3] ) +#define ISO14443A_CALC_BCC(ByteBuffer) (ByteBuffer[0] ^ ByteBuffer[1] ^ ByteBuffer[2] ^ ByteBuffer[3]) uint16_t ISO14443AAppendCRCA(void *Buffer, uint16_t ByteCount); bool ISO14443ACheckCRCA(const void *Buffer, uint16_t ByteCount); @@ -121,7 +120,7 @@ bool ISO14443ASelect(void *Buffer, uint16_t *BitCount, uint8_t *UidCL, uint8_t S } #ifdef CONFIG_MF_DESFIRE_SUPPORT -bool ISO14443ASelectDesfire(void *Buffer, uint16_t *BitCount, uint8_t *UidCL, uint8_t UidByteCount, uint8_t SAKValue); +bool ISO14443ASelectDesfire(void *Buffer, uint16_t Offset, uint16_t *BitCount, uint8_t *UidCL, uint8_t UidByteCount, uint8_t SAKValue); #endif INLINE