Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DESfire emulation support: Updated support for PM3 and better compatibility with external USB readers #323

Merged
merged 12 commits into from
Aug 15, 2022
Merged
2 changes: 1 addition & 1 deletion Doc/BuilingFirmwareBinariesFromSource.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ The per-build configuration lists are currently as follows:

Latest builds supporting ISO14443, ISO1593 and DESFire (non development) are generated automatically on the
main Chameleon Mini firmware repository
(see [this listing](https://github.com/emsec/ChameleonMini/actions)).
(see [this listing](https://github.com/emsec/ChameleonMini/actions) and the [active latest firmware builds here](https://github.com/emsec/ChameleonMini/releases)).

### More customized builds

Expand Down
229 changes: 139 additions & 90 deletions Doc/DESFireSupportReadme.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
254 changes: 148 additions & 106 deletions Firmware/Chameleon-Mini/Application/DESFire/DESFireISO14443Support.c

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -40,15 +41,41 @@ 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 0xA
#define ISO14443A_NAK 0x0
#define ISO14443A_NAK_PARITY_ERROR 0x1

/* 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,12 @@ This notice must be retained at the top of all source files where indicated.
#include <inttypes.h>
#include <stdbool.h>

#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)
Expand Down Expand Up @@ -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.
Expand Down
28 changes: 23 additions & 5 deletions Firmware/Chameleon-Mini/Application/DESFire/DESFirePICCControl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
}
}
Expand All @@ -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();
}
}
Expand All @@ -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();
}

Expand Down Expand Up @@ -255,13 +258,17 @@ 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);
BYTE uidData[DESFIRE_UID_SIZE];
RandomGetBuffer(uidData, DESFIRE_UID_SIZE);
memcpy(&Picc.Uid[0], uidData, DESFIRE_UID_SIZE);
/* 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;
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);
Expand Down Expand Up @@ -354,12 +361,23 @@ 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);
if (!DesfireATQAReset && 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 = true;
} else if (!DesfireATQAReset && Uid[0] == ISO14443A_UID0_RANDOM) {
uint16_t ATQAValue = DESFIRE_ATQA_RANDOM_UID;
Picc.ATQA[0] = (uint8_t)((ATQAValue >> 8) & 0x00FF);
Picc.ATQA[1] = (uint8_t)(ATQAValue & 0x00FF);
}
SynchronizePICCInfo();
MemoryStoreDesfireHeaderBytes();
}

#endif /* CONFIG_MF_DESFIRE_SUPPORT */
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

Expand Down Expand Up @@ -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;
Expand Down
33 changes: 14 additions & 19 deletions Firmware/Chameleon-Mini/Application/ISO14443-3A.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,19 @@
#include "DESFire/DESFireISO14443Support.h"

bool ISO14443ASelectDesfire(void *Buffer, uint16_t *BitCount, uint8_t *UidCL, uint8_t UidByteCount, uint8_t SAKValue) {
if (BitCount == NULL || ASBYTES(*BitCount) < 2) {
*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.
Expand All @@ -21,7 +32,7 @@ bool ISO14443ASelectDesfire(void *Buffer, uint16_t *BitCount, uint8_t *UidCL, ui
memcpy(&DataPtr[0], &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.
Expand All @@ -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;
Expand Down
3 changes: 1 addition & 2 deletions Firmware/Chameleon-Mini/Application/ISO14443-3A.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down