Search code examples
androidnfcapduhcepn532

SELECT APDU command on PN532 is not working


I have a Waveshare PN532 NFC Raspberry Pi Hat connected to my Raspberry Pi 4 using SPI and I am trying to send an APDU SELECT command to my Android HCE app. I'm using Flutter and this package to build the Android app. I followed the package's documentation and have set up the HCE app, here's the code:

import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:nfc_host_card_emulation/nfc_host_card_emulation.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const App());
}

class App extends StatefulWidget {
  const App({super.key});

  @override
  State<App> createState() => _AppState();
}

class _AppState extends State<App> {
  @override
  void initState() {
    super.initState();
    NfcHce.stream.listen((command) {
      print("Received command: $command");
    });
  }

  void test() async {
    final nfcState = await NfcHce.checkDeviceNfcState();
    print("NFC State: $nfcState");
  }

  void start() async {
    await NfcHce.init(
      aid: Uint8List.fromList([0x0F, 0x01, 0x02, 0x03, 0x04]),
      permanentApduResponses: true,
      listenOnlyConfiguredPorts: false,
    );

    NfcHce.addApduResponse(0, [17, 17]);
  }

  void stop() async {
    NfcHce.removeApduResponse(0);
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('NFC HCE Example'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              OutlinedButton(
                onPressed: test,
                child: const Text("TEST NFC"),
              ),
              OutlinedButton(
                onPressed: start,
                child: const Text("START NFC"),
              ),
              OutlinedButton(
                onPressed: stop,
                child: const Text("STOP NFC"),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

The app's AID is 0F01020304 and it also has a response set up for port 0 with the response value being 17, 17 (decimal). According to the docs, the SELECT APDU command should look like this: 0x00 0xA4 0x04 0x00 0x05 0x0F 0x01 0x02 0x03 0x04. I also tried adding 0x00 and 0x02 as a LE byte to the end.

On the PN532 I am using the provided python library by Waveshare. Here is the code for the reader:

import RPi.GPIO as GPIO
from pn532 import *


if __name__ == '__main__':    
    try:
        def send_apdu_command(apdu):
            pn532.call_function(0x8E, params=apdu)

        def get_apdu_response():
            result = pn532.call_function(0x86, 255)
            return result
        
        pn532 = PN532_SPI(debug=False, reset=20, cs=4)

        ic, ver, rev, support = pn532.get_firmware_version()
        print('Found PN532 with firmware version: {0}.{1}'.format(ver, rev))

        # Configure PN532 to communicate with MiFare cards
        pn532.SAM_configuration()

        print('Waiting for RFID/NFC card...')
        while True:
            # Check if a card is available to read
            uid = pn532.read_passive_target(timeout=0.5)
            print('.', end="")
            # Try again if no card is available.
            if uid is not None:
                break   
        
        print('Found card with UID:', [hex(i) for i in uid])
        
        command = [0x00, 0xA4, 0x04, 0x00, 0x05, 0x0F, 0x01, 0x02, 0x03, 0x04, 0x00]
        send_apdu_command(command)
        
        response = get_apdu_response()
        print([hex(i) for i in response])
            
    except Exception as e:
        print(e)
    finally:
        GPIO.cleanup()

When I run the reader program and my app on my phone (according to the app, on a Motorola Moto G84 5G the HCE functionality is not supported so I'm using a Nokia TA-1234), start the HCE app and put the phone close to the reader, this is the output of the reader program:

Found PN532 with firmware version: 1.6
Waiting for RFID/NFC card...............Found card with UID: ['0x08', '0x9a', '0xe7', '0x18']
['0x25']

With the "response" being 0x25 which should be 0x11 0x11 as defined in the app. The Android app should also log the APDU command using the stream but nothing happens. Does anyone know what could be the problem here?

Tried

  • Looking into other questions on Stackoverflow
  • Changing the AID length
  • Changing the LE byte
  • Changing the response port
  • Looking into the debug logs of the reader

Expecting The reader should return the specified value in the app (0x11 0x11)


Solution

  • I found this Github repository and I managed to get it working. It uses the nfcpy library instead of the one provided by Waveshare. The NFC hat also needs to be connected with UART (it's the only one supported by nfcpy). Although I had to change one thing, that being the device identifier in the main.py file - instead of "tty:AMA0" I used "ttyS0".