diff --git a/Doc/BuilingFirmwareBinariesFromSource.md b/Doc/BuilingFirmwareBinariesFromSource.md index 127082c3..7faedf93 100644 --- a/Doc/BuilingFirmwareBinariesFromSource.md +++ b/Doc/BuilingFirmwareBinariesFromSource.md @@ -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 diff --git a/Doc/DESFireSupportReadme.md b/Doc/DESFireSupportReadme.md index d8a561df..26fa845b 100644 --- a/Doc/DESFireSupportReadme.md +++ b/Doc/DESFireSupportReadme.md @@ -209,6 +209,24 @@ fingerprint the DESFire tag subtype in the return NTAG413DNA; return DESFIRE_UNKNOWN; ``` +Table 2 in section 2.1 of [NXP AN10833](https://www.nxp.com/docs/en/application-note/AN10833.pdf) (page 5) lists +standard Mifare tag identifications for several tags. This byte is represented by setting +``Picc.HwType`` using the Chameleon terminal command ``DF_SETHDR=HwType xx``. The default setting for the +Chameleon DESFire tags is ``0x01`` (*MIFARE DESFire*). The table in the application note is reproduced +below for reference. The NXP documentation says: "*The upper nibble [X] defines if the +device is a native MIFARE IC (``0x0``), an implementation (``0x8``), an applet on a Java Card +(``0x9``) or MIFARE 2GO (``0xA``).*" + +| Second Byte of GetVersion Response (``Picc.HwType``) | NXP Type Tag | +| :---: | :-- | +| ``0xX1`` | *MIFARE DESFire* | +| ``0xX2`` | *MIFARE Plus* | +| ``0xX3`` | *MIFARE Ultralight* | +| ``0xX4`` | *NTAG* | +| ``0xX5`` | *RFU* | +| ``0xX6`` | *RFU* | +| ``0xX7`` | *NTAG I2C* | +| ``0xX8`` | *MIFARE DESFire Light* | #### DF_COMM_MODE -- Manually sets the communication mode of the current session @@ -251,66 +269,6 @@ DF_ENCMODE=AES:CBC ## Supported functionality -### Tables of tested support for active commands - -#### Native DESFire command support (mixed EV0/EV1/EV2 instruction sets) - -| Instruction | Cmd Byte | Description | Testing Status | Implementation Notes | -| :--- | :----: | :----: | :----: | :-- | -| CMD_AUTHENTICATE | 0x0A | Authenticate legacy | :ballot_box_with_check: | | -| CMD_AUTHENTICATE_ISO | 0x1A | ISO / 3DES auth | :ballot_box_with_check: | | -| CMD_AUTHENTICATE_AES | 0xAA | Standard AES auth | :ballot_box_with_check: | | -| CMD_AUTHENTICATE_EV2_FIRST | 0x71 | Newer spec auth variant | :x: | | -| CMD_AUTHENTICATE_EV2_NONFIRST | 0x77 | Newer spec auth variant | :x: | See page 32 of AN12343.pdf | -| CMD_CHANGE_KEY_SETTINGS | 0x54 | | :ballot_box_with_check: | | -| CMD_SET_CONFIGURATION | 0x5C | | :x: | | -| CMD_CHANGE_KEY | 0xC4 | | :ballot_box_with_check: | | -| CMD_GET_KEY_VERSION | 0x64 | | :ballot_box_with_check: | | -| CMD_CREATE_APPLICATION | 0xCA | | :ballot_box_with_check: | | -| CMD_DELETE_APPLICATION | 0xDA | | :ballot_box_with_check: | | -| CMD_GET_APPLICATION_IDS | 0x6A | | :ballot_box_with_check: | | -| CMD_FREE_MEMORY | 0x6E | | :ballot_box_with_check: | | -| CMD_GET_DF_NAMES | 0x6D | | :x: | =Need docs for what this command does! | -| CMD_GET_KEY_SETTINGS | 0x45 | | :ballot_box_with_check: | | -| CMD_SELECT_APPLICATION | 0x5A | | :ballot_box_with_check: | | -| CMD_FORMAT_PICC | 0xFC | | :ballot_box_with_check: | | -| CMD_GET_VERSION | 0x60 | | :ballot_box_with_check: | | -| CMD_GET_CARD_UID | 0x51 | | :ballot_box_with_check: | | -| CMD_GET_FILE_IDS | 0x6F | | :ballot_box_with_check: | | -| CMD_GET_FILE_SETTINGS | 0xF5 | | :ballot_box_with_check: | | -| CMD_CHANGE_FILE_SETTINGS | 0x5F | | :x: | | -| CMD_CREATE_STDDATA_FILE | 0xCD | | :ballot_box_with_check: | | -| CMD_CREATE_BACKUPDATA_FILE | 0xCB | | :ballot_box_with_check: | | -| CMD_CREATE_VALUE_FILE | 0xCC | | :ballot_box_with_check: | | -| CMD_CREATE_LINEAR_RECORD_FILE | 0xC1 | | :wavy_dash: | GetFileSettings still not returning correct data | -| CMD_CREATE_CYCLIC_RECORD_FILE | 0xC0 | | :wavy_dash: | GetFileSettings still not returning correct data | -| CMD_DELETE_FILE | 0xDF | | :ballot_box_with_check: | | -| CMD_GET_ISO_FILE_IDS | 0x61 | | :x: | | -| CMD_READ_DATA | 0xBD | | :ballot_box_with_check: | The data for std/backup files is uninitialized (any bits) until the user sets the data with WriteData | -| CMD_WRITE_DATA | 0x3D | | :ballot_box_with_check: | Only supports write command operations with <= 52 bytes of data at a time. Offset parameters can be used to write lengthier files. | -| CMD_GET_VALUE | 0x6C | | :ballot_box_with_check: | | -| CMD_CREDIT | 0x0C | | :ballot_box_with_check: | | -| CMD_DEBIT | 0xDC | | :ballot_box_with_check: | | -| CMD_LIMITED_CREDIT | 0x1C | | :ballot_box_with_check: | | -| CMD_WRITE_RECORD | 0x3B | | :question: | | -| CMD_READ_RECORDS | 0xBB | | :ballot_box_with_check: :wavy_dash: | | -| CMD_CLEAR_RECORD_FILE | 0xEB | | :question: | | -| CMD_COMMIT_TRANSACTION | 0xC7 | | :ballot_box_with_check: | | -| CMD_ABORT_TRANSACTION | 0xA7 | | :ballot_box_with_check: | | | - -#### ISO7816 command support - -| Instruction | Cmd Byte | Description | Testing Status | Implementation Notes | -| :--- | :----: | :----: | :----: | :-- | -| CMD_ISO7816_SELECT | 0xa4 | A more nuanced ISO7816 version of EF/DF selection. | :wavy_dash: :question: | See the implementation notes [in this spec](https://cardwerk.com/smart-card-standard-iso7816-4-section-6-basic-interindustry-commands/#chap6_11). We only support EF selection with ``P1=00000000|000000010`` and DF(AID) with ``P1=00000100``. | -| CMD_ISO7816_GET_CHALLENGE | 0x84 | | :wavy_dash: :question: | | -| CMD_ISO7816_EXTERNAL_AUTHENTICATE | 0x82 | | :x: | | -| CMD_ISO7816_INTERNAL_AUTHENTICATE | 0x88 | | :x: | | -| CMD_ISO7816_READ_BINARY | 0xb0 | | :wavy_dash: :question: | Needs testing. | -| CMD_ISO7816_UPDATE_BINARY | 0xd6 | | :wavy_dash: :question: | Needs testing. | -| CMD_ISO7816_READ_RECORDS | 0xb2 | | :wavy_dash: :question: | Needs testing. | -| CMD_ISO7816_APPEND_RECORD | 0xe2 | | :wavy_dash: :question: | Especially needs testing for corner case checks. | - ### Proxmark3 (PM3) compatibility and support The next PM3 commands are known to work with the Chameleon DESFire tag emulation (using both the RDV4 and Easy device types). @@ -336,16 +294,16 @@ data setdebugmode -2 Start | End | Src | Data (! denotes parity error) | CRC | Annotation ------------+------------+-----+-------------------------------------------------------------------------+-----+-------------------- 0 | 992 | Rdr |52 | | WUPA - 2116 | 4484 | Tag |44 03 | | + 2116 | 4484 | Tag |04 03 | | 7040 | 9504 | Rdr |93 20 | | ANTICOLL - 10820 | 16708 | Tag |88 41 92 a0 fb | | - 19328 | 29856 | Rdr |93 70 88 41 92 a0 fb 87 d9 | ok | SELECT_UID - 30916 | 34436 | Tag |24 d8 36 | | - 35840 | 38304 | Rdr |95 20 | | ANTICOLL-2 - 39364 | 45188 | Tag |b2 59 78 41 d2 | | - 47872 | 58336 | Rdr |95 70 b2 59 78 41 d2 13 09 | ok | SELECT_UID-2 - 59844 | 63428 | Tag |20 fc 70 | | - 65152 | 69920 | Rdr |e0 80 31 73 | ok | RATS + 10580 | 16404 | Tag |88 08 e2 38 5a | | + 19072 | 29600 | Rdr |93 70 88 08 e2 38 5a 95 d5 | ok | SELECT_UID + 30660 | 34180 | Tag |24 d8 36 | | + 35584 | 38048 | Rdr |95 20 | | ANTICOLL-2 + 39124 | 44948 | Tag |f6 12 53 42 f5 | | + 47616 | 58080 | Rdr |95 70 f6 12 53 42 f5 cb 66 | ok | SELECT_UID-2 + 59220 | 62804 | Tag |20 fc 70 | | + 64512 | 69280 | Rdr |e0 80 31 73 | ok | RATS ``` #### Getting a summary of tag information @@ -356,6 +314,12 @@ The tag type reported will also vary depending on which EV0/EV1/EV2 generation o DESFire configuration is used: ```bash [usb] pm3 --> hf mfdes info +[#] BCC2 incorrect, got 0xf5, expected 0x12 +[#] Aborting +[#] Can't select card +[#] switch_off +[!] ⚠️ Can't select card +[usb] pm3 --> hf mfdes info [#] pcb_blocknum 0 == 2 [#] [WCMD <--: : 08/08] 02 90 60 00 00 00 14 98 [#] pcb_blocknum 1 == 3 @@ -367,8 +331,8 @@ DESFire configuration is used: [#] switch_off [=] ---------------------------------- Tag Information ---------------------------------- -[+] UID: B9 38 A2 A0 B5 1A B9 -[+] Batch number: B9 09 58 8B EA +[+] UID: 08 E2 38 F6 12 53 42 +[+] Batch number: BB 27 CB 35 08 [+] Production date: week 01 / 2005 [=] --- Hardware Information @@ -470,29 +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. -### Links to public datasheets and online specs - -The following links are the original online resource links are -archived here for documentation on how this firmware operates: -* [ISO/IEC 7816-4 Standard](http://www.unsads.com/specs/ISO/7816/ISO7816-4.pdf) -* [PublicDESFireEV0DatasheetSpecs -- April2004 (M075031_desfire.pdf)](https://web.archive.org/web/20170201031920/http://neteril.org/files/M075031_desfire.pdf) -* [NXP Application Note AN12343](https://www.nxp.com/docs/en/application-note/AN12343.pdf) -* [TI DESFire EV1 Tag AES Auth Specs (sloa213.pdf)](https://www.ti.com/lit/an/sloa213/sloa213.pdf) -* [NXP Application Note AN10833](https://www.nxp.com/docs/en/application-note/AN10833.pdf) -* My favorite conference submission in grad school is (by far) about this project -- even though I did not present my talk that year. - In rare form, the [presentation slides (tentative; see uploads)](https://archive.org/details/@maxiedschmidt) and the - [accepted manuscript](https://archive.org/download/ftc2021-presentation-slides-with-notes/schmidt-ftc2021-submission.pdf) (published in print form by Springer) - effectively document the scarce details of the DESFire spec and command sets gleaned while working on this project as a conference proceedings article. - Grace Hopper would have approved :) +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 @@ -536,6 +568,23 @@ repositories and code bases: * [AVRCryptoLib in C](https://github.com/cantora/avr-crypto-lib) * [LibFreefare DESFire Code (mostly as a reference and check point)](https://github.com/nfc-tools/libfreefare/tree/master/libfreefare) +### Links to public datasheets and online specs + +The following links are the original online resource links are +archived here for documentation on how this firmware operates: +* [ISO/IEC 7816-4 Standard](http://www.unsads.com/specs/ISO/7816/ISO7816-4.pdf) +* [DESFire Functional specification (MF3ICD81, November 2008)](https://web.archive.org/web/20201115030854/https://marvin.blogreen.org/~romain/nfc/MF3ICD81%20-%20MIFARE%20DESFire%20-%20Functional%20specification%20-%20Rev.%203.5%20-%2028%20November%202008.pdf) +* [DESFire EV0 Datasheet (M075031, April 2004)](https://web.archive.org/web/20170201031920/http://neteril.org/files/M075031_desfire.pdf) +* [NXP Application Note AN12343](https://www.nxp.com/docs/en/application-note/AN12343.pdf) +* [TI DESFire EV1 Tag AES Auth Specs (sloa213.pdf)](https://www.ti.com/lit/an/sloa213/sloa213.pdf) +* [NXP Application Note AN10833](https://www.nxp.com/docs/en/application-note/AN10833.pdf) +* My favorite conference submission in grad school is (by far) about this project -- even though I did not present my talk that year. + In rare form, the [presentation slides (tentative; see uploads)](https://archive.org/details/@maxiedschmidt) and the + [accepted manuscript](https://archive.org/download/ftc2021-presentation-slides-with-notes/schmidt-ftc2021-submission.pdf) + (published in print form by Springer) document the scarce details of the DESFire spec and command sets gleaned while working + on this project as a conference proceedings article. + Grace Hopper would have approved :) + ## New development sources of DESFire support for the Chameleon Mini David Oswald has added a [DESFire emulation project](https://github.com/orgs/emsec/projects?type=classic) to organize tasks in 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..1bbc701d 100644 --- a/Firmware/Chameleon-Mini/Application/DESFire/DESFireISO14443Support.c +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFireISO14443Support.c @@ -73,12 +73,40 @@ void ISO144434SwitchState(Iso144434StateType NewState) { void ISO144434Reset(void) { /* No logging here -- spams the log and slows things way down! */ + ISO144433AReset(); Iso144434State = ISO14443_4_STATE_EXPECT_RATS; Iso144434BlockNumber = 1; + Iso144434CardID = 1; ISO14443ALastIncomingDataFrameBits = 0; ISO14443ALastIncomingDataFrame[0] = 0x00; } +static uint16_t GetACKCommandData(uint8_t *Buffer); +static uint16_t GetACKCommandData(uint8_t *Buffer) { + Buffer[0] = ISO14443A_ACK; + return 4; +} + +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 4; +} + +static uint16_t GetNAKParityErrorCommandData(uint8_t *Buffer, bool ResetToHaltState); +static uint16_t GetNAKParityErrorCommandData(uint8_t *Buffer, bool ResetToHaltState) { + if (ResetToHaltState) { + ISO144433AHalt(); + StateRetryCount = 0; + } + Buffer[0] = ISO14443A_NAK_PARITY_ERROR; + return 4; +} + uint16_t ISO144434ProcessBlock(uint8_t *Buffer, uint16_t ByteCount, uint16_t BitCount) { uint8_t PCB = Buffer[0]; @@ -87,19 +115,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 GetNAKParityErrorCommandData(Buffer, false); + //return ISO14443A_APP_NO_RESPONSE; } switch (Iso144434State) { @@ -108,21 +138,21 @@ 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. + /* 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); - 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 */ @@ -139,16 +169,18 @@ uint16_t ISO144434ProcessBlock(uint8_t *Buffer, uint16_t ByteCount, uint16_t Bit if (HaveCID) { PrologueLength++; /* Verify the card ID */ - if ((Buffer[1] & 0xF) != Iso144434CardID) { + if (ByteCount > 1 && (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; + DEBUG_PRINT_P(PSTR("ISO14443-4: NEW-CARD-ID %02x != %02x"), (Buffer[1] & 0xF), Iso144434CardID); + 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; @@ -161,14 +193,15 @@ uint16_t ISO144434ProcessBlock(uint8_t *Buffer, uint16_t ByteCount, uint16_t Bit if (HaveNAD) { PrologueLength++; /* Not currently supported -- the frame is ignored */ - DEBUG_PRINT_P(PSTR("ISO144434ProcessBlock: ISO14443_PCB_I_BLOCK")); + DEBUG_PRINT_P(PSTR("ISO144434-4: ISO14443_PCB_I_BLOCK")); } /* 7.5.3.2, rule D: toggle on each I-block */ Iso144434BlockNumber = MyBlockNumber = !MyBlockNumber; 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; + DEBUG_PRINT_P(PSTR("ISO14443-4: ISO14443_PCB_I_BLOCK")); + return GetNAKCommandData(Buffer, false); + //return ISO14443A_APP_NO_RESPONSE; } /* Build the prologue for the response */ @@ -183,8 +216,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")); @@ -194,8 +227,9 @@ uint16_t ISO144434ProcessBlock(uint8_t *Buffer, uint16_t ByteCount, uint16_t Bit case ISO14443_PCB_R_BLOCK: { /* 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; + DEBUG_PRINT_P(PSTR("ISO144434-4: ISO14443_PCB_R_BLOCK")); + return GetNAKCommandData(Buffer, false); + //return ISO14443A_APP_NO_RESPONSE; } if (PCB & ISO14443_PCB_R_BLOCK_ACKNAK_MASK) { /* 7.5.4.3, rule 12 */ @@ -206,13 +240,14 @@ uint16_t ISO144434ProcessBlock(uint8_t *Buffer, uint16_t ByteCount, uint16_t Bit } else { /* This is an ACK: */ /* NOTE: Chaining is not supported yet. */ - DEBUG_PRINT_P(PSTR("ISO144434ProcessBlock: ISO14443_PCB_R_BLOCK")); + DEBUG_PRINT_P(PSTR("ISO144434-4: ISO14443_PCB_R_BLOCK")); // Resend the data from the last frame: if (ISO14443ALastIncomingDataFrameBits > 0) { 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 +266,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 +277,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 +317,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 +327,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 +358,34 @@ 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")); + 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)) { ISO144433AHalt(); - return ISO14443A_APP_NO_RESPONSE; + return GetACKCommandData(Buffer); + //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); + return GetAndSetBufferCRCA(Buffer, respDataSize); + } else if (IsUnsupportedCmd(Cmd)) { + return GetNAKCommandData(Buffer, true); } 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; + 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 +394,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,95 +403,76 @@ 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: + case ISO14443_3A_STATE_READY_CL1_NVB_END: { if (Cmd == ISO14443A_CMD_SELECT_CL1) { - /* Load UID CL1 and perform anticollisio: */ + /* 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); - 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; + /* Load UID CL1 and perform anticollision: */ + uint8_t cl1SAKValue = SAK_CL1_VALUE; + if (Buffer[1] == ISO14443A_NVB_AC_START && ISO14443ASelectDesfire(&Buffer[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], &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: + 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], &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, &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], &BitCount, &Uid[3], 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; - - case ISO14443_3A_STATE_ACTIVE: + 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: */ - 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; + /* DESFire UID size is of insufficient size to support this request: */ + return GetNAKCommandData(Buffer, false); } - /* 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); - return ReturnBits; - + /* Forward to ISO/IEC 14443-4 block processing code */ + return ISO144434ProcessBlock(Buffer, ASBYTES(BitCount), BitCount); + } default: break; @@ -436,12 +480,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..a31b49de 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,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 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..1082c229 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,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); @@ -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 */ 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..54ef7c09 100644 --- a/Firmware/Chameleon-Mini/Application/ISO14443-3A.c +++ b/Firmware/Chameleon-Mini/Application/ISO14443-3A.c @@ -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. @@ -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. @@ -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..28fe8696 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);