Skip to content

Commit

Permalink
Merge pull request emsec#323 from maxieds/ExternalUSBReadersCompat
Browse files Browse the repository at this point in the history
DESfire emulation support: Updated support for PM3 and better compatibility with external USB readers
  • Loading branch information
fptrs authored Aug 15, 2022
2 parents f5c1347 + d5d36fe commit 62e2f71
Show file tree
Hide file tree
Showing 11 changed files with 379 additions and 241 deletions.
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

0 comments on commit 62e2f71

Please sign in to comment.