NFC reader "SELECT (by AID)" APDU is not routed to Android device
Asked Answered
G

1

4

I have an ACR122U NFC reader/writer connected to my Windows machine with ACR122 driver installed.

I try to use javax.smartcardio API to send an SELECT (by AID) ADPU to my Android device (which should be in HCE mode).

This is my code:

TerminalFactory factory = TerminalFactory.getDefault();
List<CardTerminal> terminals = factory.terminals().list();
CardTerminal terminal = terminals.get(0);
System.out.println(terminal.getName());
Card card = terminal.connect("*");
CardChannel channel = card.getBasicChannel();
execute(channel, new byte[] { (byte) 0xFF, 0x00, 0x51, (byte) 195, 0x00}, card);
execute(channel, new byte[] { (byte)0xFF, 0x00, 0x00, 0x00, 0x04,(byte)0xD4, 0x4A, 0x01, 0x00}, card); //InListPassiveTarget
execute(channel, new byte[] { (byte)0xFF, 0x00, 0x00, 0x00, 0x04,(byte)0xD4, 0x4A, 0x01, 0x00}, card); //InListPassiveTarget
execute(channel, new byte[] {0x00, (byte) 0xA4, 0x04, 0x00, 7,
                (byte)0xF0, 0x01, 0x02, 0x03, 0x04, (byte) 0x05, 0x07, 0}, card); //select AID

...

public static void execute(CardChannel channel, byte[] command, Card...cards) throws CardException {
    ByteBuffer r = ByteBuffer.allocate(1024);
    channel.transmit(bufferFromArray(command), r);
    System.out.println(convertBinToASCII(r.array(), 0, r.position()));
}

This is the output I get:

ACS ACR122 0
3B8F8001804F0CA000000306030000000000006B
C3
D54B6300
D54B010108032004010203049000

I guess 01020304 is the UID presented by my Android device to the NFC reader. The SELECT APDU returns no response, it's 0 bytes long.

On my Android device I have this service:

public class MyHostApduService extends HostApduService {

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e("APDU", "APDU service was created.");
    }

    @Override
    public byte[] processCommandApdu(byte[] apdu, Bundle extras) {
       Log.e("APDU", "command apdu: " + Arrays.toString(apdu));
       return new byte[2];
    }
    @Override
    public void onDeactivated(int reason) {
       Log.e("APDU", "ON DEACTIVATED.");
    }
}

But processCommandAdpu is not getting called. Looking through the logs I wasn't able to find anything when the SELECT ADPU is supposedly sent to the reader, so it seems like the ADPU doesn't even get to the Android device.

This is the apduservice.xml for the Android project:

<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:description="@string/servicedesc"
    android:requireDeviceUnlock="false" >
    <aid-group
        android:category="other"
        android:description="@string/aiddescription" >
        <aid-filter android:name="F0010203040507" />
    </aid-group>
</host-apdu-service>

Besides there are several ADPUs which when transmitted make the NFC reader somewhat stuck. For example,

execute(channel, new byte[] {(byte) 0xFF, 0x00, 0x00, 0x00, 0x02, (byte) 0xd4, 0x04}, card);

which is a pseudo APDU to query current status of PN532 chip does not return any response. Can it be that this particular reader is flawed? How can I check it?


UPDATE (based on discussion in chat):

A test with a second reader (same model, same version) just worked. So it might either be some obscure settings on the first reader or the reader was just malfunctioning.

Both readers have the same version information:

  • ACR122U firmware version: 41435231323255323135 (-> ACR122U215)
  • PN532 version: D503 32010607 9000 (-> PN532 v1.6)
Groat answered 13/5, 2018 at 13:47 Comment(0)
D
2

You used InListPassiveTarget to directly instruct the PN532 NFC chip inside the ACR122U to manually poll for tags. This essentially bypasses the abstraction layers of the ACR122U that allow you to automatically poll for tags and use "standard PC/SC" to exchange APDU commands with the enumerated smartcard. Consequently, sending plain APDUs over the PC/SC interface won't work and the SELECT APDU will never arrive at the Android HCE side.

Instead, you will also need to exchange APDU commands by speaking directly with the PN532 transmission module. You can do this by wrapping your APDU command inside an InDataExchange command (or InCommunicateThru if you need control over ISO/IEC 14443-4 header fields). In your case, the wrapped SELECT (by AID) command APDU would look something like:

execute(channel, new byte[] {
    (byte)0xFF, 0x00, 0x00, 0x00,  // direct PN532 command
    16,                // Lc = command length
    (byte)0xD4, 0x40,  // InDataExchange
    0x01,              // Tag #1 (equal to the tag number from the InListPassiveTarget response)
    0x00, (byte)0xA4, 0x04, 0x00,                         // APDU: SELECT (by AID)
          7,                                              // Lc = AID length
          (byte)0xF0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x07, // AID = F0010203040507
          0x00,                                           // Le = max
}, card);

Can it be that this particular reader is flawed?

Yes, this could be the case though I rather doubt that. Note that there are many different versions of the ACR122U firmware and most of them seem to be flawed by design. Particularly the fact that some versions of the reader perform automatic tag enumeration and some don't, and that the available API changed drastically across different versions of that reader make it difficult to program for that device.

UPDATE: Some more observations ...

  • The response to the InListPassiveTarget command does not contain ATS data (after the UID field). Maybe your reader does not perform automatic RATS during tag selection. This can be enabled using the SetParameters command (before InListPassiveTarget):

    execute(channel, new byte[] {
        (byte)0xFF, 0x00, 0x00, 0x00,  // direct PN532 command
        3,                 // Lc = command length
        (byte)0xD4, 0x12,  // InDataExchange
        (1<<4),            // fAutomaticRATS = 1
    }, card);
    

    You could also try to manually send a RATS command using InCommunicateThru:

    execute(channel, new byte[] {
        (byte)0xFF, 0x00, 0x00, 0x00,  // direct PN532 command
        4,                 // Lc = command length
        (byte)0xD4, 0x42,  // InCommunicateThru
        (byte)0xE0, 0x80,  // RATS (FSD = 256, CID = 0)
    }, card);
    

    After that you could try to communicate with the card using InCommunicateThru and raw ISO/IEC 14443-4 blocks:

    execute(channel, new byte[] {
        (byte)0xFF, 0x00, 0x00, 0x00,  // direct PN532 command
        16,                // Lc = command length
        (byte)0xD4, 0x42,  // InCommunicateThru
        0x02,              // PCB (I-block, change to 0x03 for the next block)
        0x00, (byte)0xA4, 0x04, 0x00,                     // APDU: SELECT (by AID)
          7,                                              // Lc = AID length
          (byte)0xF0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x07, // AID = F0010203040507
          0x00,                                           // Le = max
    }, card);
    
  • The ATQA 0803 looks rather odd. Especially the 0x03 in the bit frame anticollision field suggests that there is more than one target in the field (since a standards compliant tag would only set a single bit in the bit frame anticollision field). Note that this is not true. The ATQA in the response to InListPassiveTarget is transmitted little endian. Consequently, the bit frame anticollision value is 0x08 (= valid/compliant) and the value in the proprietary field is 0x03.

  • It's, indeed, strange that your reader does not respond to certain PN532 commands (particularly since the firmware version 32010607 looks fine). I've tested some of the commands that fail for you with another ACR122U and they completed successfully ...

Desex answered 22/5, 2018 at 15:11 Comment(9)
Hi Michael, thanks very much for your answer. I tried what you suggested before and I tried it once more, unfortunately it doesn't seem to work, the program is just blocked when I try to transmit this APDU. I don't believe this is the problem on Android side, because this communication happens and the service is launched when I use another android device which sends select AID APDU.Groat
I observed the same behavior while using libnfc library. => 6f 09 00 00 00 00 00 00 00 00 ff 00 00 00 04 d4 4a 01 00 <= 80 0e 00 00 00 00 00 02 fe 00 d5 4b 01 01 08 03 20 04 01 02 03 04 90 00 - tag is found. => 6f 14 00 00 00 00 00 00 00 00 ff 00 00 00 0f d4 40 01 a4 04 00 07 f0 01 02 03 04 05 07 00 - select AID ADPU which seems to be exactly like what you're proposing, and then it just timeouts. No response is received.Groat
#50338613 This is the question I asked with more info about what output I get when running libnfc example.Groat
basically, I'm all out of ideas for this one. libnfc didn't seem to work. I found some pssc python wrappers and tried to transmit the same APDU (wrapped with 0xFF000000) it also didn't work, just timeout.Groat
@Groat That's odd then. Note that in the USB frames above you seem to be missing the CLA byte for the APDU (add 00 between d4 40 01 and a4 04 00 + adapt the length), but I doubt that this would cause the timeout either.Desex
Yeah, that's odd. I ordered another ACR122U unit will try it with the new one. Thanks for your time. Could you possibly suggest another usb nfc reader which has Android API (ACS provides Android library for this one) which is more reliable than this one?Groat
Unfortunately, no.Desex
@Groat See my updated answer for some more observations/debugging steps.Desex
Let us continue this discussion in chat.Groat

© 2022 - 2024 — McMap. All rights reserved.