skip to Main Content

Trying to send GPO with a ACR39U card reader fails with an error code of 87 (0x57), INVALID_PARAMETERS, works with other card readers. It’s also working with the SELECT and READ RECORD.

Here’s my APDU log.

00 A4 04 00 0E 31 50 41 59 2E 53 59 53 2E 44 44 46 30 31

00 B2 01 0C 32

00 A4 04 00 08 A0 00 00 03 33 01 01 01

80 A8 00 00 0A 83 08 00 00 00 00 10 00 02 12 00

With other card readers it returns the correct response but with ACR39U it always returns 87. Here’s the source code isolating this problem. It’s in C#, .NET8. I’m using ANY CPU in Visual Studio. Windows 11. The card is UnionPay.

using System;
using System.Runtime.InteropServices;

public class SmartCardCommunication
{
    [DllImport("winscard.dll")]
    private static extern int SCardEstablishContext(uint dwScope, IntPtr pvReserved1, IntPtr pvReserved2, ref IntPtr phContext);

    [DllImport("winscard.dll")]
    private static extern int SCardReleaseContext(IntPtr hContext);

    [DllImport("winscard.dll")]
    private static extern int SCardListReaders(IntPtr hContext, string mszGroups, byte[] mszReaders, ref int pcchReaders);

    [DllImport("winscard.dll")]
    private static extern int SCardConnect(IntPtr hContext, string szReader, uint dwShareMode, uint dwPreferredProtocols, ref IntPtr phCard, ref uint pdwActiveProtocol);

    [DllImport("winscard.dll")]
    private static extern int SCardDisconnect(IntPtr hCard, int dwDisposition);

    [DllImport("winscard.dll")]
    private static extern int SCardTransmit(IntPtr hCard, ref SCARD_IO_REQUEST pioSendPci, byte[] pbSendBuffer, int cbSendLength, ref SCARD_IO_REQUEST pioRecvPci, byte[] pbRecvBuffer, ref int pcbRecvLength);

    private const uint SCARD_SCOPE_USER = 0;
    private const uint SCARD_SHARE_SHARED = 2;
    private const uint SCARD_PROTOCOL_T0 = 1;
    private const uint SCARD_PROTOCOL_T1 = 2;

    [StructLayout(LayoutKind.Sequential)]
    private struct SCARD_IO_REQUEST
    {
        public uint dwProtocol;
        public uint cbPciLength;
    }

    public static void Main()
    {
        IntPtr hContext = IntPtr.Zero;
        IntPtr hCard = IntPtr.Zero;
        uint activeProtocol = 0;

        try
        {
            int result = SCardEstablishContext(SCARD_SCOPE_USER, IntPtr.Zero, IntPtr.Zero, ref hContext);
            if (result != 0) throw new Exception("Failed to establish context.");

            int readerSize = 0;
            result = SCardListReaders(hContext, null, null, ref readerSize);
            if (result != 0) throw new Exception("Failed to list readers.");

            byte[] readers = new byte[readerSize];
            result = SCardListReaders(hContext, null, readers, ref readerSize);
            if (result != 0) throw new Exception("Failed to list readers.");

            string readerName = System.Text.Encoding.ASCII.GetString(readers).Split('')[0];

            result = SCardConnect(hContext, readerName, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0, ref hCard, ref activeProtocol);
            if (result != 0) throw new Exception("Failed to connect to the card.");

            apdu(hCard, activeProtocol, "00 A4 04 00 0E 31 50 41 59 2E 53 59 53 2E 44 44 46 30 31 ");
            apdu(hCard, activeProtocol, "00 B2 01 0C 32 ");
            apdu(hCard, activeProtocol, "00 A4 04 00 08 A0 00 00 03 33 01 01 01 ");
            apdu(hCard, activeProtocol, "80 A8 00 00 0A 83 08 00 00 00 00 10 00 02 12 00 ");
        }
        finally
        {
            // Clean up
            if (hCard != IntPtr.Zero) SCardDisconnect(hCard, 0);
            if (hContext != IntPtr.Zero) SCardReleaseContext(hContext);
        }
    }

    static void apdu(IntPtr hCard, uint activeProtocol, string hex)
    {
        hex = hex.Replace(" ", "");
        byte[] apduCommand = StringToByteArrayFastest(hex); // Example: SELECT command
        byte[] recvBuffer = new byte[256];
        int recvLength = recvBuffer.Length;

        SCARD_IO_REQUEST ioRequest = new SCARD_IO_REQUEST { dwProtocol = activeProtocol, cbPciLength = (uint)Marshal.SizeOf(typeof(SCARD_IO_REQUEST)) };
        SCARD_IO_REQUEST ioRequest2 = new SCARD_IO_REQUEST { dwProtocol = activeProtocol, cbPciLength = (uint)Marshal.SizeOf(typeof(SCARD_IO_REQUEST)) };

        int result = SCardTransmit(hCard, ref ioRequest, apduCommand, apduCommand.Length, ref ioRequest2, recvBuffer, ref recvLength);
        if (result != 0) throw new Exception("Failed to transmit APDU command.");

        byte[] response = new byte[recvLength];
        Array.Copy(recvBuffer, response, recvLength);
        Console.WriteLine("Response: " + BitConverter.ToString(response));
    }

    public static byte[] StringToByteArrayFastest(string hex)
    {
        if (hex.Length % 2 == 1)
            throw new Exception("The binary key cannot have an odd number of digits");

        byte[] arr = new byte[hex.Length >> 1];

        for (int i = 0; i < hex.Length >> 1; ++i)
        {
            arr[i] = (byte)((GetHexVal(hex[i << 1]) << 4) + (GetHexVal(hex[(i << 1) + 1])));
        }

        return arr;
    }

    public static int GetHexVal(char hex)
    {
        int val = (int)hex;
        return val - (val < 58 ? 48 : (val < 97 ? 55 : 87));
    }
}

What could be causing this?

2

Answers


  1. Chosen as BEST ANSWER

    Figured out the problem, it was the Lc at the end, the 00 in 02 12 00. Once I removed it it works fine.


  2. It seems that you are using the T=0 protocol (given SCARD_PROTOCOL_T0 in SCardConnect call).

    GET PROCESSING OPTIONS is a "case 4" command (see ISO 7816 for details) that needs to be transmitted according to the T=0 logic (see here).

    Your communication should look like this:

    >> 80 A8 00 00 0A 83 08 00 00 00 00 10 00 02 12 (GET PROCESSING OPTIONS)
    << 61 XX (command successful, XX response data available)
    >> 00 C0 00 00 XX (GET RESPONSE)
    << [response data] 90 00
    

    You may also find the chapter "9.3.1 Transport of APDUs by T=0" in the EMV Book 3 interesting.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search