0

So, I have been tasked with writing code to work with smart cards. The environment is C#.Net, Xamarin. The smart card reader (contact, not NFC) is attached to a Honeywell handheld device. So, with the sparse pieces out there, and I can't just use some third-party library for a variety of reasons, I am tasked with it.

Using Android.Hardware.Usb, I can get to the following.

UsbManager - created to context of application.
Scan connected USB devices to the handheld
Properly detecting the specific device.
Getting the configuration object.
Then to the interface
and finally finding the 3 endpoints.. Isochronous, Inbound and Outbound.
Request permission to use the specific device.
and finally Open the device for a UsbConnection handle.

So, now that the door is open to communicate, I need to obviously send a request outbound and get an answer back from the respective out and in endpoints.

I can build the BulkTransfer of an APDU Command and get a response that 2 bytes are available to be received. I then try to do another BulkTransfer on the inbound port with an APDU Response, but it always fails with -1.

I have seen other posts that discuss setting communication protocols, baud rates, etc but not sure if that is the issue or not. I am totally new to this granular level of serial/usb communications. Unsure of next steps to get the back/forth communication to the device.

Here is an abbreviated summation of what I am doing to get connected to the device. As for specific equipment, it is a Honeywell CN80 handheld scanner device with an attached CAC reader (also Honeywell). As for what I am trying to do, just trying to call a verify card exists (or not). I am not trying to actually send data to the device, just a command to inquire against the device and get a proper answer back.

public void attemptingSmartCommunications()
{
   _myUsbManager = (UsbManager)_myMainActivity.GetSystemService(Context.UsbService);
   foreach (var d in _myUsbManager.DeviceList)
   {  // method checks the device for proper VID/PID
      var dev = d.Value;
      if (isExpectedDevice(dev))
      {
         for (var dc = 0; dc < dev.ConfigurationCount; dc++)
         {
            // SHOULD only be 1 configuration from what I have encountered.
            _myUsbConfiguration = dev.GetConfiguration(dc);

            for (var di = 0; di < _myUsbConfiguration.InterfaceCount; di++)
            {
               _myUsbInterface = _myUsbConfiguration.GetInterface(di);
               // Add on context of each endpoint
               for (var ep = 0; ep < _myUsbInterface.EndpointCount; ep++)
               {
                  var intEndpoint = _myUsbInterface.GetEndpoint(ep);

                  // which one do we need, store into their own respecive properties
                  // for in/out/isochronous
                  switch (intEndpoint.Address.ToString())
                  {
                     case "XferIsochronous":
                        // Control pipe for communication
                        _myIsochronousEndpoint = intEndpoint;
                        break;

                     case "130":
                        // Read IN FROM the USB Device with response
                        _myInEndpoint = intEndpoint;
                        break;

                     case "131":
                        // Write OUT TO the USB Device with command/request
                        _myOutEndpoint = intEndpoint;
                        break;

                  }
               }

               // now, I have the endpoints to request send and read back, make sure permission
               if( _myUsbManager.HasPermission(_myUsbDevice))
               {
                  myConnection = _myUsbManager.OpenDevice(_myUsbDevice);
                  var apdu = MyStaticSmartCardAPDUCommand;
                  // prepares the 4-byte request of CLA, INS, P1, P2 (no data in request)
                  var byteBuffer = apdu.CommandAsBytes(); 
                  var bufferLen = byteBuffer.Length;
                  var resultBytes = _myUsbDeviceConnection.BulkTransfer(_myOutEndpoint, byteBuffer, bufferLen, 2000);
                  // I get indication of 2 byte response
                  if( resultBytes > 0 )
                  {
                     // default buffer prepared with 4 bytes.  SW1, SW2 + 2 expected bytes
                     var apduResp = new APDUResponse(resultBytes);
                     byteBuffer = apduResp.GetResponseBuffer();
                     var responseStatus = _myUsbDeviceConnection.BulkTransfer(_myInEndpoint, byteBuffer, byteBuffer.Length, 3000);

                     // HERE is where it always fails.  
                     // Am I on the right track?  Completely out of it?
                  }
               }
            }
         }
      }
   }
}
DRapp
  • 47,638
  • 12
  • 72
  • 142
  • 1
    You've described what you're doing but haven't posted any code to actually **show us** what you're doing, and you haven't actually asked a question. You also haven't specified what kind of hardware you're working with or what type of data you are trying to read/write to the smartcard. – Jason Oct 02 '22 at 00:04
  • @Jason, revised and posted a somewhat abbreviated (APDU command and response), but the rest is pretty complete otherwise. – DRapp Oct 02 '22 at 00:58
  • Have a look at the [CCID protocol](https://en.wikipedia.org/wiki/CCID_(protocol)) and it's open source implementation -- [libccid](https://github.com/LudovicRousseau/CCID). You can use Wireshark to analyze USB communication when the official driver talks to the reader -- see [here](https://wiki.wireshark.org/CaptureSetup/USB). – vlp Oct 03 '22 at 07:48
  • @vlp, appreciate the direction of libccid, but that is all C/C++ I am specifically dealing with xamarin and android (forgot to include that tag). I dont think I am too far off though with C# implementation and need to get over the hump. – DRapp Oct 03 '22 at 12:55
  • Cards have different address and data modes (8,16,32,64 bit). You are probably having issues with size being wrong. Best thing to do is read card with in windows before using c# app. You can use a File Explorer to find file and then open with notepad or equivalent. If you get an exception with File Explorer you didn't install the right driver and need to install driver. Once you open card with explorer you can determine the address and data size and then modify your c# accordingly. – jdweng Oct 03 '22 at 13:05

1 Answers1

2

(Wanted to write a comment, but it got quite long...)

Technically there are two possibilities, without using the drivers from the manufacturer:

  1. The reader uses standard USB CCID protocol. Then you need to implement that protocol in your software. The protocol is described here. There is a open source implementation libccid that you could use as a reference.

  2. Reader uses some proprietary protocol. Then you need to get the protocol description from the vendor and implement it.

To differentiate between the two, refer to the reader specifications and/or check it's USB descriptors. You may also want to have a look here and here.


Some additional (random) notes:

  • Readers using a proprietary protocol usually emulate a USB serial port. If that is the case then it is meaningless to communicate with the reader over raw USB -- use this serial port instead.

  • I suppose that reader vendor provides some Android drivers for this reader. I do not know your reasons for not using them but you should. Java code should be callable from Xamarin using a bindings library. For native libraries, use P/Invoke (and Xamarin specific documentation).

  • If some system-level smartcard API is available (e.g. winscard) then use it (using e.g. pcsc-sharp if possible).

  • What you do in your code is that you send a raw command-APDU to the USB endpoint and expect a response-APDU as a response. You will definitely need to wrap the command APDU to a protocol specific format (which one depends on the reader).


Disclaimer: I am no Xamarin expert so please do validate my thoughts.

Good luck with your project!


EDIT> You may also find those posts interesting: 1, 2


EDIT2> There is a PoC javascript CCID implementation that you might find easier to follow.

It is also very revealing to see how an official driver communicates with the reader using Wireshark that has a CCID dissector.

vlp
  • 7,811
  • 2
  • 23
  • 51
  • Sincerely appreciate your extensive comments. Looked into the libccid code of C/C++ but my mind cant wrap that mentally at this time but will try to gather more. from all you have provided. – DRapp Oct 04 '22 at 12:20
  • @DRapp Please do validate first that the reader is actually using CCID. I edited the answer with a javascript CCID demo that might be interesting for you as well. Seeing actual communication with wireshark is also very helpful (if you can connect the reader to a device capable of USB sniffing). – vlp Oct 04 '22 at 14:40
  • @DRapp And be warned that if you need to communicate with T=1 card (check its ATR to see) and your reader supports only "TPDU level exchange" (see field "dwFeatures" in descriptor) than you will need to implement the T=1 protocol as well (as defined in ISO7816-3). – vlp Oct 04 '22 at 14:47
  • Although not complete, I think you have given me enough to chew on. One of the links specifically clarified to power on / off the device. Again, not knowing the underlying requirements to communicate were not evident. – DRapp Oct 04 '22 at 16:40