Skip to content

Commit 63e74f9

Browse files
committed
Changes to ISO14443A-4 handlers confirmed to work with the ACS ACR-122U external USB reader ; Updated docs and source code
1 parent 32277f0 commit 63e74f9

File tree

3 files changed

+138
-57
lines changed

3 files changed

+138
-57
lines changed

Doc/DESFireSupportReadme.md

+88-5
Original file line numberDiff line numberDiff line change
@@ -434,14 +434,97 @@ DESFire configuration is used:
434434
### Compatibility with external USB readers and LibNFC
435435
436436
The DESFire configurations are known to work with the anticollision and RATS handshaking utility ``nfc-anticol``
437-
from [LibNFC](https://github.com/nfc-tools/libnfc).
438-
The Mifare DESFire commands installed by [LibFreefare](https://github.com/nfc-tools/libfreefare)
439-
have not been tested nor confirmed to work with the Chameleon Mini.
440-
The developers are actively working to ensure compatibility of the Chameleon DESFire emulation with external USB readers used
441-
running ``pcscd`` and ``pcsc_spy``. This support is not yet functional with tests using ACR-122 and HID Omnikey 5022CL readers.
437+
from [LibNFC](https://github.com/nfc-tools/libnfc). The following is the output of this utility using the
438+
ACS ACR-122U reader over USB (with the ``pcscd`` dameon not running):
439+
```bash
440+
$ sudo nfc-anticol -f
441+
NFC reader: ACS / ACR122U PICC Interface opened
442+
443+
Sent bits: 26 (7 bits)
444+
Received bits: 04 03
445+
Sent bits: 93 20
446+
Received bits: 88 08 c7 df 98
447+
Sent bits: 93 70 88 08 c7 df 98 9c ae
448+
Received bits: 24 d8 36
449+
Sent bits: 95 20
450+
Received bits: f1 02 fc 6c 63
451+
Sent bits: 95 70 f1 02 fc 6c 63 3a 98
452+
Received bits: 20 fc 70
453+
Sent bits: e0 50 bc a5
454+
Received bits: 06 75 00 81 02 80 66 fd
455+
Sent bits: 50 00 57 cd
456+
Received bits: 50 00 57 cd
457+
458+
Found tag with
459+
UID: 08c7dff102fc6c
460+
ATQA: 0304
461+
SAK: 20
462+
ATS: 06 75 00 81 02 80 66 fd
463+
```
442464
The DESFire support for the Chameleon Mini is tested with the LibNFC-based source code
443465
[developed in this directory](https://github.com/emsec/ChameleonMini/tree/master/Software/DESFireLibNFCTesting) with
444466
[sample dumps and output here](https://github.com/emsec/ChameleonMini/tree/master/Software/DESFireLibNFCTesting/SampleOutputDumps).
467+
The Mifare DESFire commands installed by [LibFreefare](https://github.com/nfc-tools/libfreefare)
468+
do not work with the Chameleon Mini.
469+
470+
The developers are actively working to ensure compatibility of the Chameleon DESFire emulation with external USB readers used
471+
running ``pcscd`` and ``pcsc_spy``. This support does not work with the HID Omnikey 5022CL reader.
472+
The ACS ACR-122U reader recognizes the Chameleon running the vanilla ``CONFIG=MF_DESFIRE`` over PCSC (driver ``pcscd``)
473+
as shown in the output of the ``pcsc_spy -v`` command:
474+
```bash
475+
sudo pcsc_scan -v
476+
Using reader plug'n play mechanism
477+
Scanning present readers...
478+
Waiting for the first reader...found one
479+
Scanning present readers...
480+
0: ACS ACR122U PICC Interface 00 00
481+
482+
Mon Jul 25 19:26:28 2022
483+
Reader 0: ACS ACR122U PICC Interface 00 00
484+
Event number: 3
485+
Card state: Card removed,
486+
487+
Mon Jul 25 19:26:37 2022
488+
Reader 0: ACS ACR122U PICC Interface 00 00
489+
Event number: 4
490+
Card state: Card inserted,
491+
ATR: 3B 81 80 01 80 80
492+
493+
ATR: 3B 81 80 01 80 80
494+
+ TS = 3B --> Direct Convention
495+
+ T0 = 81, Y(1): 1000, K: 1 (historical bytes)
496+
TD(1) = 80 --> Y(i+1) = 1000, Protocol T = 0
497+
-----
498+
TD(2) = 01 --> Y(i+1) = 0000, Protocol T = 1
499+
-----
500+
+ Historical bytes: 80
501+
Category indicator byte: 80 (compact TLV data object)
502+
+ TCK = 80 (correct checksum)
503+
504+
Possibly identified card (using /usr/share/pcsc/smartcard_list.txt):
505+
3B 81 80 01 80 80
506+
RFID - ISO 14443 Type A - NXP DESFire or DESFire EV1 or EV2
507+
"Reiner LoginCard" (or "OWOK", how they name it) - they have been distributed by a german computer magazine ("Computer BILD")
508+
https://cardlogin.reiner-sct.com/
509+
Belgium A-kaart (Antwerp citycard)
510+
Oyster card - Transport for London (second-gen "D")
511+
https://en.wikipedia.org/wiki/Oyster_card
512+
Kaba Legic Advant 4k
513+
Sydney Opal card public transport ticket (Transport)
514+
https://www.opal.com.au
515+
TH Köln (University of Applied Sciences Cologne) - Student Identity Card
516+
https://www.th-koeln.de/en/academics/multica_5893.php
517+
German red cross blood donation service
518+
http://www.blutspende-nordost.de/
519+
Greater Toronto/Hamilton/Ottawa PRESTO contactless fare card
520+
http://en.wikipedia.org/wiki/Presto_card
521+
Electic vehicle charging card of the EMSP EnBW Energie Baden-Württemberg AG, Tarif ADAC e-Charge, Germany
522+
523+
Mon Jul 25 19:26:37 2022
524+
Reader 0: ACS ACR122U PICC Interface 00 00
525+
Event number: 5
526+
Card state: Card removed,
527+
```
445528
446529
## Credits
447530

Firmware/Chameleon-Mini/Application/DESFire/DESFireISO14443Support.c

+40-33
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ void ISO144434Reset(void) {
7979
ISO14443ALastIncomingDataFrame[0] = 0x00;
8080
}
8181

82+
static uint16_t GetACKCommandData(uint8_t *Buffer);
83+
static uint16_t GetACKCommandData(uint8_t *Buffer) {
84+
Buffer[0] = ISO14443A_ACK;
85+
return ASBITS(1);
86+
}
87+
8288
static uint16_t GetNAKCommandData(uint8_t *Buffer, bool ResetToHaltState);
8389
static uint16_t GetNAKCommandData(uint8_t *Buffer, bool ResetToHaltState) {
8490
if (ResetToHaltState) {
@@ -124,17 +130,17 @@ uint16_t ISO144434ProcessBlock(uint8_t *Buffer, uint16_t ByteCount, uint16_t Bit
124130
return GetNAKCommandData(Buffer, false);
125131
//return ISO14443A_APP_NO_RESPONSE;
126132
}
127-
/* Process RATS.
133+
/* Process RATS:
128134
* NOTE: ATS bytes are tailored to Chameleon implementation and differ from DESFire spec.
129135
* NOTE: Some PCD implementations do a memcmp() over ATS bytes, which is completely wrong.
130136
*/
131137
Iso144434CardID = Buffer[1] & 0x0F;
132138
Buffer[0] = 0x06;
133139
memcpy(&Buffer[1], &Picc.ATSBytes[1], 4);
134-
Buffer[5] = 0x80; /* T1: dummy value for historical bytes */
135-
ByteCount = 6; /* NOT including CRC */
140+
Buffer[5] = 0x80; /* T1: dummy value for historical bytes */
141+
ByteCount = 6;
136142
ISO144434SwitchState(ISO14443_4_STATE_ACTIVE);
137-
return ASBITS(ByteCount); /* PM3 expects no CRCA bytes */
143+
return GetAndSetBufferCRCA(Buffer, ByteCount); /* PM3 'hf mfdes list' expects CRCA bytes on the RATS data */
138144
}
139145
case ISO14443_4_STATE_ACTIVE: {
140146
/* See: ISO/IEC 14443-4; 7.1 Block format */
@@ -341,8 +347,16 @@ uint16_t ISO144433APiccProcess(uint8_t *Buffer, uint16_t BitCount) {
341347
} else if (ISO144433AIsHalt(Buffer, BitCount)) {
342348
DesfireLogEntry(LOG_INFO_APP_CMD_HALT, NULL, 0);
343349
return GetHLTACommandData(Buffer, true);
350+
} else if (Cmd == ISO14443A_CMD_RATS) {
351+
ISO144434SwitchState(ISO14443_4_STATE_EXPECT_RATS);
352+
uint16_t ReturnBits = ISO144434ProcessBlock(Buffer, ASBYTES(BitCount), BitCount);
353+
Iso144433AState = ISO14443_3A_STATE_ACTIVE;
354+
StateRetryCount = 0;
355+
return ReturnBits;
344356
} else if (IsDeselectCmd(Cmd)) {
345-
return GetHLTACommandData(Buffer, true);
357+
ISO144433AHalt();
358+
return GetACKCommandData(Buffer);
359+
//return GetHLTACommandData(Buffer, true);
346360
} else if (IsRIDCmd(Cmd)) {
347361
Iso144433AState = ISO14443_3A_STATE_ACTIVE;
348362
StateRetryCount = 0;
@@ -352,14 +366,11 @@ uint16_t ISO144433APiccProcess(uint8_t *Buffer, uint16_t BitCount) {
352366
*/
353367
uint16_t respDataSize = (uint16_t) sizeof(MIFARE_DESFIRE_TAG_AID);
354368
memcpy(&Buffer[0], MIFARE_DESFIRE_TAG_AID, respDataSize);
355-
/* ??? TODO: Do we append CRCA bytes ???
356-
* ISO14443AAppendCRCA(Buffer, respDataSize);
357-
* respDataSize += 2;
358-
*/
369+
/* ??? TODO: Do we append CRCA bytes ??? */
359370
return ASBITS(respDataSize);
360371
} else if (IsUnsupportedCmd(Cmd)) {
361372
return GetNAKCommandData(Buffer, true);
362-
} else if (CheckStateRetryCount(false)) { /* Increment the state retry count to keep track of when to timeout */
373+
} else if (CheckStateRetryCount(false)) {
363374
DEBUG_PRINT_P(PSTR("ISO14443-3: SW-RESET"));
364375
return GetHLTACommandData(Buffer, true);
365376
} else if (BitCount <= BITS_PER_BYTE) {
@@ -382,31 +393,30 @@ uint16_t ISO144433APiccProcess(uint8_t *Buffer, uint16_t BitCount) {
382393
Iso144433AIdleState = Iso144433AState;
383394
ISO144433ASwitchState(ISO14443_3A_STATE_READY_CL1);
384395
/* The LSByte ordering of the ATQA value for ISO14443 tags is
385-
* discussed in section 2.3 of NXP AN10833.
386-
*/
396+
* discussed in section 2.3 of NXP AN10833.
397+
*/
387398
Buffer[0] = Picc.ATQA[1];
388399
Buffer[1] = Picc.ATQA[0];
389400
return ASBITS(ISO14443A_ATQA_FRAME_SIZE_BYTES);
390401

391402
case ISO14443_3A_STATE_READY_CL1:
392-
case ISO14443_3A_STATE_READY_CL1_NVB_END:
403+
case ISO14443_3A_STATE_READY_CL1_NVB_END: {
393404
if (Cmd == ISO14443A_CMD_SELECT_CL1) {
394-
/* Load UID CL1 and perform anticollision: */
405+
/* NXP AN10927 (section 2, figure 1, page 3) shows the expected
406+
* data flow to exchange the 7-byte UID for DESFire tags:
407+
* http://www.nxp.com/docs/en/application-note/AN10927.pdf
408+
*/
395409
ConfigurationUidType Uid;
396410
ApplicationGetUid(&Uid[1]);
397411
Uid[0] = ISO14443A_UID0_CT;
398-
/* NXP AN10927 (section 2, figure 1, page 3) shows the expected
399-
* data flow to exchange the 7-byte UID for DESFire tags:
400-
* http://www.nxp.com/docs/en/application-note/AN10927.pdf
401-
*/
412+
/* Load UID CL1 and perform anticollision: */
402413
uint8_t cl1SAKValue = SAK_CL1_VALUE;
403414
if (Buffer[1] == ISO14443A_NVB_AC_START && ISO14443ASelectDesfire(&Buffer[0], 0, &BitCount, &Uid[0], 4, cl1SAKValue)) {
404415
ISO144433ASwitchState(ISO14443_3A_STATE_READY_CL1_NVB_END);
405416
return BitCount;
406417
} else if (Buffer[1] == ISO14443A_NVB_AC_END && ISO14443ASelectDesfire(&Buffer[0], 0, &BitCount, &Uid[0], 4, cl1SAKValue)) {
407418
ISO144433ASwitchState(ISO14443_3A_STATE_READY_CL2);
408419
return BitCount;
409-
410420
} else {
411421
DEBUG_PRINT_P(PSTR("ISO14443-4: Select CL1-NOT-OK"));
412422
}
@@ -415,19 +425,19 @@ uint16_t ISO144433APiccProcess(uint8_t *Buffer, uint16_t BitCount) {
415425
}
416426
CheckStateRetryCount(false);
417427
return GetNAKCommandData(Buffer, false);
418-
//return ISO14443A_APP_NO_RESPONSE;
419-
428+
//return ISO14443A_APP_NO_RESPONSE;
429+
}
420430
case ISO14443_3A_STATE_READY_CL2:
421-
case ISO14443_3A_STATE_READY_CL2_NVB_END:
431+
case ISO14443_3A_STATE_READY_CL2_NVB_END: {
422432
if (Cmd == ISO14443A_CMD_SELECT_CL2 && ActiveConfiguration.UidSize >= ISO14443A_UID_SIZE_DOUBLE) {
423433
/* Load UID CL2 and perform anticollision: */
424434
ConfigurationUidType Uid;
425435
ApplicationGetUid(&Uid[0]);
426436
uint8_t cl2SAKValue = SAK_CL2_VALUE;
427-
if (Buffer[1] == ISO14443A_NVB_AC_START && ISO14443ASelectDesfire(&Buffer[0], 0, &BitCount, &Uid[4], 4, cl2SAKValue)) {
437+
if (Buffer[1] == ISO14443A_NVB_AC_START && ISO14443ASelectDesfire(&Buffer[0], 0, &BitCount, &Uid[3], 4, cl2SAKValue)) {
428438
ISO144433ASwitchState(ISO14443_3A_STATE_READY_CL2_NVB_END);
429439
return BitCount;
430-
} else if (Buffer[1] == ISO14443A_NVB_AC_END && ISO14443ASelectDesfire(&Buffer[0], 0, &BitCount, &Uid[4], 4, cl2SAKValue)) {
440+
} else if (Buffer[1] == ISO14443A_NVB_AC_END && ISO14443ASelectDesfire(&Buffer[0], 0, &BitCount, &Uid[3], 4, cl2SAKValue)) {
431441
ISO144433ASwitchState(ISO14443_3A_STATE_ACTIVE);
432442
return BitCount;
433443

@@ -439,22 +449,19 @@ uint16_t ISO144433APiccProcess(uint8_t *Buffer, uint16_t BitCount) {
439449
}
440450
CheckStateRetryCount(false);
441451
return GetNAKCommandData(Buffer, false);
442-
//return ISO14443A_APP_NO_RESPONSE;
443-
444-
case ISO14443_3A_STATE_ACTIVE:
452+
//return ISO14443A_APP_NO_RESPONSE;
453+
}
454+
case ISO14443_3A_STATE_ACTIVE: {
445455
StateRetryCount = MAX_STATE_RETRY_COUNT;
446456
if (Cmd == ISO14443A_CMD_RATS) {
447457
ISO144434SwitchState(ISO14443_4_STATE_EXPECT_RATS);
448458
} else if (Cmd == ISO14443A_CMD_SELECT_CL3) {
449459
/* DESFire UID size is of insufficient size to support this request: */
450-
Buffer[0] = ISO14443A_SAK_COMPLETE_NOT_COMPLIANT;
451-
ISO14443AAppendCRCA(&Buffer[0], 1);
452-
return ISO14443A_SAK_FRAME_SIZE;
460+
return GetNAKCommandData(Buffer, false);
453461
}
454462
/* Forward to ISO/IEC 14443-4 block processing code */
455-
uint16_t ReturnBits = ISO144434ProcessBlock(Buffer, ASBYTES(BitCount), BitCount);
456-
return ReturnBits;
457-
463+
return ISO144434ProcessBlock(Buffer, ASBYTES(BitCount), BitCount);
464+
}
458465
default:
459466
break;
460467

Firmware/Chameleon-Mini/Application/DESFire/DESFirePICCControl.c

+10-19
Original file line numberDiff line numberDiff line change
@@ -261,20 +261,11 @@ void FormatPicc(void) {
261261
BYTE uidData[DESFIRE_UID_SIZE];
262262
RandomGetBuffer(uidData, DESFIRE_UID_SIZE);
263263
memcpy(&Picc.Uid[0], uidData, DESFIRE_UID_SIZE);
264-
if (Picc.Uid[0] == ISO14443A_UID0_RANDOM) {
265-
Picc.Uid[0] != 0x30;
266-
}
267-
/* OLD: Conform to NXP Application Note AN10927 about the first
268-
* byte of a randomly generated UID (refer to section 2.1.1).
269-
*/
270-
//Picc.Uid[0] = ISO14443A_UID0_RANDOM;
271-
//uint16_t ATQAValue = DESFIRE_ATQA_RANDOM_UID;
272-
/* NEW: NXP AN10927 (section 2.1.1, page 5) states that a random
273-
* UID (RID) is always limited to 4 bytes. This limitation
274-
* is avoided by just setting the whole buffer to a random
275-
* value whose first byte is not 0x08.
264+
/* Conform to NXP Application Note AN10927 about the first
265+
* byte of a randomly generated UID (refer to section 2.1.1).
276266
*/
277-
uint16_t ATQAValue = DESFIRE_ATQA_DEFAULT;
267+
Picc.Uid[0] = ISO14443A_UID0_RANDOM;
268+
uint16_t ATQAValue = DESFIRE_ATQA_RANDOM_UID;
278269
Picc.ATQA[0] = (uint8_t)((ATQAValue >> 8) & 0x00FF);
279270
Picc.ATQA[1] = (uint8_t)(ATQAValue & 0x00FF);
280271
DesfireATQAReset = false;
@@ -376,12 +367,12 @@ void GetPiccUid(ConfigurationUidType Uid) {
376367
void SetPiccUid(ConfigurationUidType Uid) {
377368
memcpy(&Picc.Uid[0], Uid, DESFIRE_UID_SIZE);
378369
DesfireATQAReset = true;
379-
//if (!DesfireATQAReset) {
380-
// uint16_t ATQAValue = DESFIRE_ATQA_DEFAULT;
381-
// Picc.ATQA[0] = (uint8_t)((ATQAValue >> 8) & 0x00FF);
382-
// Picc.ATQA[1] = (uint8_t)(ATQAValue & 0x00FF);
383-
// DesfireATQAReset = true;
384-
//}
370+
if (!DesfireATQAReset) {
371+
uint16_t ATQAValue = DESFIRE_ATQA_DEFAULT;
372+
Picc.ATQA[0] = (uint8_t)((ATQAValue >> 8) & 0x00FF);
373+
Picc.ATQA[1] = (uint8_t)(ATQAValue & 0x00FF);
374+
DesfireATQAReset = true;
375+
}
385376
SynchronizePICCInfo();
386377
}
387378

0 commit comments

Comments
 (0)