Skip to content

Commit

Permalink
Changes to ISO14443A-4 handlers confirmed to work with the ACS ACR-12…
Browse files Browse the repository at this point in the history
…2U external USB reader ; Updated docs and source code
  • Loading branch information
maxieds committed Jul 25, 2022
1 parent 32277f0 commit 63e74f9
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 57 deletions.
93 changes: 88 additions & 5 deletions Doc/DESFireSupportReadme.md
Original file line number Diff line number Diff line change
Expand Up @@ -434,14 +434,97 @@ DESFire configuration is used:
### Compatibility with external USB readers and LibNFC
The DESFire configurations are known to work with the anticollision and RATS handshaking utility ``nfc-anticol``
from [LibNFC](https://github.com/nfc-tools/libnfc).
The Mifare DESFire commands installed by [LibFreefare](https://github.com/nfc-tools/libfreefare)
have not been tested nor confirmed to work with the Chameleon Mini.
The developers are actively working to ensure compatibility of the Chameleon DESFire emulation with external USB readers used
running ``pcscd`` and ``pcsc_spy``. This support is not yet functional with tests using ACR-122 and HID Omnikey 5022CL readers.
from [LibNFC](https://github.com/nfc-tools/libnfc). The following is the output of this utility using the
ACS ACR-122U reader over USB (with the ``pcscd`` dameon not running):
```bash
$ sudo nfc-anticol -f
NFC reader: ACS / ACR122U PICC Interface opened

Sent bits: 26 (7 bits)
Received bits: 04 03
Sent bits: 93 20
Received bits: 88 08 c7 df 98
Sent bits: 93 70 88 08 c7 df 98 9c ae
Received bits: 24 d8 36
Sent bits: 95 20
Received bits: f1 02 fc 6c 63
Sent bits: 95 70 f1 02 fc 6c 63 3a 98
Received bits: 20 fc 70
Sent bits: e0 50 bc a5
Received bits: 06 75 00 81 02 80 66 fd
Sent bits: 50 00 57 cd
Received bits: 50 00 57 cd

Found tag with
UID: 08c7dff102fc6c
ATQA: 0304
SAK: 20
ATS: 06 75 00 81 02 80 66 fd
```
The DESFire support for the Chameleon Mini is tested with the LibNFC-based source code
[developed in this directory](https://github.com/emsec/ChameleonMini/tree/master/Software/DESFireLibNFCTesting) with
[sample dumps and output here](https://github.com/emsec/ChameleonMini/tree/master/Software/DESFireLibNFCTesting/SampleOutputDumps).
The Mifare DESFire commands installed by [LibFreefare](https://github.com/nfc-tools/libfreefare)
do not work with the Chameleon Mini.
The developers are actively working to ensure compatibility of the Chameleon DESFire emulation with external USB readers used
running ``pcscd`` and ``pcsc_spy``. This support does not work with the HID Omnikey 5022CL reader.
The ACS ACR-122U reader recognizes the Chameleon running the vanilla ``CONFIG=MF_DESFIRE`` over PCSC (driver ``pcscd``)
as shown in the output of the ``pcsc_spy -v`` command:
```bash
sudo pcsc_scan -v
Using reader plug'n play mechanism
Scanning present readers...
Waiting for the first reader...found one
Scanning present readers...
0: ACS ACR122U PICC Interface 00 00
Mon Jul 25 19:26:28 2022
Reader 0: ACS ACR122U PICC Interface 00 00
Event number: 3
Card state: Card removed,
Mon Jul 25 19:26:37 2022
Reader 0: ACS ACR122U PICC Interface 00 00
Event number: 4
Card state: Card inserted,
ATR: 3B 81 80 01 80 80
ATR: 3B 81 80 01 80 80
+ TS = 3B --> Direct Convention
+ T0 = 81, Y(1): 1000, K: 1 (historical bytes)
TD(1) = 80 --> Y(i+1) = 1000, Protocol T = 0
-----
TD(2) = 01 --> Y(i+1) = 0000, Protocol T = 1
-----
+ Historical bytes: 80
Category indicator byte: 80 (compact TLV data object)
+ TCK = 80 (correct checksum)
Possibly identified card (using /usr/share/pcsc/smartcard_list.txt):
3B 81 80 01 80 80
RFID - ISO 14443 Type A - NXP DESFire or DESFire EV1 or EV2
"Reiner LoginCard" (or "OWOK", how they name it) - they have been distributed by a german computer magazine ("Computer BILD")
https://cardlogin.reiner-sct.com/
Belgium A-kaart (Antwerp citycard)
Oyster card - Transport for London (second-gen "D")
https://en.wikipedia.org/wiki/Oyster_card
Kaba Legic Advant 4k
Sydney Opal card public transport ticket (Transport)
https://www.opal.com.au
TH Köln (University of Applied Sciences Cologne) - Student Identity Card
https://www.th-koeln.de/en/academics/multica_5893.php
German red cross blood donation service
http://www.blutspende-nordost.de/
Greater Toronto/Hamilton/Ottawa PRESTO contactless fare card
http://en.wikipedia.org/wiki/Presto_card
Electic vehicle charging card of the EMSP EnBW Energie Baden-Württemberg AG, Tarif ADAC e-Charge, Germany
Mon Jul 25 19:26:37 2022
Reader 0: ACS ACR122U PICC Interface 00 00
Event number: 5
Card state: Card removed,
```
## Credits
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ void ISO144434Reset(void) {
ISO14443ALastIncomingDataFrame[0] = 0x00;
}

static uint16_t GetACKCommandData(uint8_t *Buffer);
static uint16_t GetACKCommandData(uint8_t *Buffer) {
Buffer[0] = ISO14443A_ACK;
return ASBITS(1);
}

static uint16_t GetNAKCommandData(uint8_t *Buffer, bool ResetToHaltState);
static uint16_t GetNAKCommandData(uint8_t *Buffer, bool ResetToHaltState) {
if (ResetToHaltState) {
Expand Down Expand Up @@ -124,17 +130,17 @@ uint16_t ISO144434ProcessBlock(uint8_t *Buffer, uint16_t ByteCount, uint16_t Bit
return GetNAKCommandData(Buffer, false);
//return ISO14443A_APP_NO_RESPONSE;
}
/* Process RATS.
/* 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.
*/
Iso144434CardID = Buffer[1] & 0x0F;
Buffer[0] = 0x06;
memcpy(&Buffer[1], &Picc.ATSBytes[1], 4);
Buffer[5] = 0x80; /* T1: dummy value for historical bytes */
ByteCount = 6; /* NOT including CRC */
Buffer[5] = 0x80; /* T1: dummy value for historical bytes */
ByteCount = 6;
ISO144434SwitchState(ISO14443_4_STATE_ACTIVE);
return ASBITS(ByteCount); /* PM3 expects no CRCA bytes */
return GetAndSetBufferCRCA(Buffer, ByteCount); /* PM3 'hf mfdes list' expects CRCA bytes on the RATS data */
}
case ISO14443_4_STATE_ACTIVE: {
/* See: ISO/IEC 14443-4; 7.1 Block format */
Expand Down Expand Up @@ -341,8 +347,16 @@ uint16_t ISO144433APiccProcess(uint8_t *Buffer, uint16_t BitCount) {
} else if (ISO144433AIsHalt(Buffer, BitCount)) {
DesfireLogEntry(LOG_INFO_APP_CMD_HALT, NULL, 0);
return GetHLTACommandData(Buffer, true);
} else if (Cmd == ISO14443A_CMD_RATS) {
ISO144434SwitchState(ISO14443_4_STATE_EXPECT_RATS);
uint16_t ReturnBits = ISO144434ProcessBlock(Buffer, ASBYTES(BitCount), BitCount);
Iso144433AState = ISO14443_3A_STATE_ACTIVE;
StateRetryCount = 0;
return ReturnBits;
} else if (IsDeselectCmd(Cmd)) {
return GetHLTACommandData(Buffer, true);
ISO144433AHalt();
return GetACKCommandData(Buffer);
//return GetHLTACommandData(Buffer, true);
} else if (IsRIDCmd(Cmd)) {
Iso144433AState = ISO14443_3A_STATE_ACTIVE;
StateRetryCount = 0;
Expand All @@ -352,14 +366,11 @@ uint16_t ISO144433APiccProcess(uint8_t *Buffer, uint16_t BitCount) {
*/
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;
*/
/* ??? TODO: Do we append CRCA bytes ??? */
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 */
} else if (CheckStateRetryCount(false)) {
DEBUG_PRINT_P(PSTR("ISO14443-3: SW-RESET"));
return GetHLTACommandData(Buffer, true);
} else if (BitCount <= BITS_PER_BYTE) {
Expand All @@ -382,31 +393,30 @@ uint16_t ISO144433APiccProcess(uint8_t *Buffer, uint16_t BitCount) {
Iso144433AIdleState = Iso144433AState;
ISO144433ASwitchState(ISO14443_3A_STATE_READY_CL1);
/* The LSByte ordering of the ATQA value for ISO14443 tags is
* discussed in section 2.3 of NXP AN10833.
*/
* 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:
case ISO14443_3A_STATE_READY_CL1_NVB_END: {
if (Cmd == ISO14443A_CMD_SELECT_CL1) {
/* Load UID CL1 and perform anticollision: */
/* 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
*/
ConfigurationUidType Uid;
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
*/
/* Load UID CL1 and perform anticollision: */
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[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"));
}
Expand All @@ -415,19 +425,19 @@ uint16_t ISO144433APiccProcess(uint8_t *Buffer, uint16_t BitCount) {
}
CheckStateRetryCount(false);
return GetNAKCommandData(Buffer, false);
//return ISO14443A_APP_NO_RESPONSE;

//return ISO14443A_APP_NO_RESPONSE;
}
case ISO14443_3A_STATE_READY_CL2:
case ISO14443_3A_STATE_READY_CL2_NVB_END:
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[0]);
uint8_t cl2SAKValue = SAK_CL2_VALUE;
if (Buffer[1] == ISO14443A_NVB_AC_START && ISO14443ASelectDesfire(&Buffer[0], 0, &BitCount, &Uid[4], 4, cl2SAKValue)) {
if (Buffer[1] == ISO14443A_NVB_AC_START && ISO14443ASelectDesfire(&Buffer[0], 0, &BitCount, &Uid[3], 4, cl2SAKValue)) {
ISO144433ASwitchState(ISO14443_3A_STATE_READY_CL2_NVB_END);
return BitCount;
} else if (Buffer[1] == ISO14443A_NVB_AC_END && ISO14443ASelectDesfire(&Buffer[0], 0, &BitCount, &Uid[4], 4, cl2SAKValue)) {
} else if (Buffer[1] == ISO14443A_NVB_AC_END && ISO14443ASelectDesfire(&Buffer[0], 0, &BitCount, &Uid[3], 4, cl2SAKValue)) {
ISO144433ASwitchState(ISO14443_3A_STATE_ACTIVE);
return BitCount;

Expand All @@ -439,22 +449,19 @@ uint16_t ISO144433APiccProcess(uint8_t *Buffer, uint16_t BitCount) {
}
CheckStateRetryCount(false);
return GetNAKCommandData(Buffer, false);
//return ISO14443A_APP_NO_RESPONSE;

case ISO14443_3A_STATE_ACTIVE:
//return ISO14443A_APP_NO_RESPONSE;
}
case ISO14443_3A_STATE_ACTIVE: {
StateRetryCount = MAX_STATE_RETRY_COUNT;
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 request: */
Buffer[0] = ISO14443A_SAK_COMPLETE_NOT_COMPLIANT;
ISO14443AAppendCRCA(&Buffer[0], 1);
return ISO14443A_SAK_FRAME_SIZE;
return GetNAKCommandData(Buffer, false);
}
/* Forward to ISO/IEC 14443-4 block processing code */
uint16_t ReturnBits = ISO144434ProcessBlock(Buffer, ASBYTES(BitCount), BitCount);
return ReturnBits;

return ISO144434ProcessBlock(Buffer, ASBYTES(BitCount), BitCount);
}
default:
break;

Expand Down
29 changes: 10 additions & 19 deletions Firmware/Chameleon-Mini/Application/DESFire/DESFirePICCControl.c
Original file line number Diff line number Diff line change
Expand Up @@ -261,20 +261,11 @@ void FormatPicc(void) {
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.
/* Conform to NXP Application Note AN10927 about the first
* byte of a randomly generated UID (refer to section 2.1.1).
*/
uint16_t ATQAValue = DESFIRE_ATQA_DEFAULT;
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;
Expand Down Expand Up @@ -376,12 +367,12 @@ void GetPiccUid(ConfigurationUidType Uid) {
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;
//}
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();
}

Expand Down

0 comments on commit 63e74f9

Please sign in to comment.