Home > Enterprise >  Xamarin/Android, SmartCard, BulkTransfer
Xamarin/Android, SmartCard, BulkTransfer

Time:10-05

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?
                  }
               }
            }
         }
      }
   }
}

CodePudding user response:

(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.

  • Related