miguelbalboa / rfid

Arduino RFID Library for MFRC522
The Unlicense
2.76k stars 1.43k forks source link

Send APDU and get challenge from ISO 14443-A Biometric Passport #492

Closed tetrahydra closed 4 years ago

tetrahydra commented 5 years ago

Step 1: Describe your environment

Step 2: Describe the problem

I would like to read DG1 from biometric passport.

I understood that in order to do that i must:

  1. Get Challenge using APDU
  2. Authenticate
  3. Read Binary (protected by secure messaging) - read the DG1 file which contains basic passport holder info

However, I failed to do the first part.

Observed Results:

Scan PICC to see UID and type... PCD_TransceiveData() failed: Timeout in communication.

Relevant Code:

void loop() {

  // Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle.
  if ( ! mfrc522.PICC_IsNewCardPresent()) {
    delay(50);
    return;
  }

  // Select one of the cards
  if ( ! mfrc522.PICC_ReadCardSerial()) {
    delay(50);
    return;
  }

  byte sendData[] = {0x00, 0x84, 0x00, 0x00, 0x08};
  byte backLen = 10;
  byte sendLen = sizeof(sendData);
  byte backData[backLen];
  MFRC522::StatusCode status = mfrc522.PCD_TransceiveData(sendData, sizeof(sendData), backData, &backLen);

  if ( status != MFRC522::STATUS_OK) {
    Serial.print(F("PCD_TransceiveData() failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return;
  } else {
    Serial.println(F("PICC_TransceiveData() success "));
  }

}
tolgasaglam commented 4 years ago

any help?

Rotzbua commented 4 years ago

If you have found a solution, you are welcome to publish it here.

a-v-s commented 2 years ago

I may have found the solution. Prefix the ADPU with a alternating 0x02 and 0x03 This is a ISO 14443-4 header called "Protocol Control Byte" (PCB) The answer will also be prefixed with this 0x02/0x03 value And it will be suffixed with a status code

mhaberler commented 9 months ago

@a-v-s I can confirm this works for me as well!

where did you find that solution?

I'm trying to read a Type 4 tag and doing the sequence "Select the NDEF Application", "Select CC File", "Read CC File", "Select NDEF File", "Read Length of NDEF File", "Read Data from NDEF File" as per "Exhibit E: Example of Mapping Version 2.0 Command Flow", p.65 of Type 4 Tag Technical Specification Version 1.2 2022-08-16

I had the symptom that the "Select the NDEF Application" step succeeded with 90 00 status, and the following "Select CC File" step would time out:

// Select the NDEF Application: ---> 02 00 A4 04 00 07 D2 76 00 00 85 01 01 00 35 C0 // reply status OK - 90 00: <--- 02 90 00 F1 09 CRC is not taken care of by MFRC522: 0 // unsure why, but not harmful // "Select CC File": ---> 02 00 A4 00 0C 02 E1 03 6D 2E // no reply - timeout : NDEF_SelectCapabilityContainer STATUS NOT OK: 3

If I apply your 2/3 recipe, all goes fine:

---> 02 00 A4 04 00 07 D2 76 00 00 85 01 01 00 35 C0 <--- 02 90 00 F1 09 CRC is not taken care of by MFRC522: 0 ---> 03 00 A4 00 0C 02 E1 03 D2 AF <--- 03 90 00 2D 53 CRC is not taken care of by MFRC522: 0

the low bit of an I-block PCB is the "Block number", its semantics is not touched upon in ISO/IEC 14443-4:2001 but the 2007 amendment ballot section 7.1.1.1 Protocol control byte field p16ff has some references

this looks like the alternating bit protocol for basic duplicate detection and retransmit to me

let me see if I can complete the T4T reading based on this clue..

edit: found a way to set the PCB block number by using this code as a start: https://github.com/Obsttube/MFRC522_NTAG424DNA/blob/main/src/MFRC522_NTAG424DNA.h#L108

mhaberler commented 9 months ago

hm, actually the blockNumber toggling is in place for TCL_Transceive() and TCL_TransceiveRBlock(), see
https://github.com/miguelbalboa/rfid/blob/master/src/MFRC522Extended.cpp#L797-L800 ff

    // Set the block number
    if (tag->blockNumber) {
        out.prologue.pcb |= 0x01;
    }
....
        // Swap block number on success
    tag->blockNumber = !tag->blockNumber;
....

edit: turns out there are two TCL_Transceive() members with different signatures:

        StatusCode TCL_Transceive(PcbBlock *send, PcbBlock *back);
    StatusCode TCL_Transceive(TagInfo * tag, byte *sendData, byte sendLen, byte *backData = NULL, byte *backLen = NULL);
    StatusCode TCL_TransceiveRBlock(TagInfo *tag, bool ack, byte *backData = NULL, byte *backLen = NULL);
    StatusCode TCL_Deselect(TagInfo *tag);

the first one takes a fixed block number via the PcbBlock *send argument and does not try to toggle the BN the other TCL_* methods take a Taginfo parameter, and that struct tracks the BN which gets toggled as needed

my code used the first TCL_Transceive() which doesnt do the BN toggling

edit: it helps to have a look at RFID-DESFire which is closely related/was used for/is derived from the code in MFRC522Extended.*

the methods talking to the card for business all use a custom tag structure which tracks the BN

mhaberler commented 9 months ago

@Rotzbua would you be open to re-adopt MFRC522Extended.* into Arduino_MFRC522v2 ?

that would enable ISO 7816 style APDU handling

I have an uncleaned first stab here and here

if yes, I'd clean it up and add the RuuviTag reading example once I get it to work

edit: RuuviTag reading progressing without surprises, I should have something to show next week

mhaberler commented 9 months ago

here is a complete working example for reading a Type4 tag with MFRC522v2, based on the above suggestion:

https://github.com/mhaberler/MFRC522v2_t4tag/tree/master

a-v-s commented 9 months ago

It has been a while since I wrote code for this. I was working on a C port of this library, that then evolved into something that supports multiple PCDs.

The answer is somewhere in the ISO 14443-4 specifications. Paragraph 7.5.3.1 states the block number should toggle. I might have been testing with an app on my phone for debugging, possibly using an nRF52 with nfc so see what the phone was sending. Not sure if I found out by documentation or reverse engineering.