frankmorgner / vsmartcard

umbrella project for emulation of smart card readers or smart cards
http://frankmorgner.github.io/vsmartcard/
688 stars 197 forks source link

Unsupported card with CCID Emulator #211

Closed polhenarejos closed 2 years ago

polhenarejos commented 2 years ago

I have a RPi0 configured as a GadgetFS to provide SC functionalities. I am using virt_cacard to emualte the smart card. The scheme is the following:

virt_cacard <--> libcacard <--> vpcd <--> pcsc <--> opensc <--> CCID emulator <--> USB

The USB is connected to a PC, where OpenSC is also installed.

After some modifications to work in the RPI0, the ccid emulator starts up and the USB is detected by the PC. However, something is broken in the middle because OpenSC tools always show "Unsupported card":

pol@myPC:~$ opensc-tool -an
Using reader with a card: KOBIL EMV CAP - SecOVID Reader III [1123344] (123456) 00 00
3b:89:00:56:43:41:52:44:5f:4e:53:53
Unsupported card

The strange part is that OpenSC tools are able to work when I run them directly in the RPI0, without the CCID Emulator middle layer:

pi@rpi0:~ $ opensc-tool -an
Using reader with a card: Virtual PCD 00 00
3b:89:00:56:43:41:52:44:5f:4e:53:53
Common Access Card (CAC)

Thus, I guess is something related with CCID Emulator, the small portion I modified or USB stuff.

Commands I use:

pi@rpi0:~/virt_cacard $ SOFTHSM2_CONF=./softhsm2.conf ./virt_cacard &
pi@rpi0:~ $ ccid-emulator -vvv --serial 123456 --interface 1123344

I also tried with vicc but I cannot manage to work even in the RPI0. It always shows "Unsupported card" in the PC and in the RPI0, with or without CCID Emulator.

Expected behaviour

What should happen? opensc-tool -an should display the card when using CCID Emulator, as it shows when it is executed by the host.

Actual behaviour

What happens instead? opensc-tool -an (and others) shows "Unsupported card" when CCID Emulator is in the middle.

Steps to reproduce

  1. Install virt_cacard and libcacard.
  2. Add the following code in the autoconfig() function in usb.c to be recognized by the RPI0:

else if (stat (DEVNAME = "20980000.usb", &statb) == 0) { HIGHSPEED = 1; device_desc.bcdDevice = __constant_cpu_to_le16 (0x0100);

    fs_source_desc.bEndpointAddress
        = hs_source_desc.bEndpointAddress
        = USB_DIR_IN | 1;
    EP_IN_NAME = "ep1in";
    fs_sink_desc.bEndpointAddress
        = hs_sink_desc.bEndpointAddress
        = USB_DIR_OUT | 2;
    EP_OUT_NAME = "ep2out";

    source_sink_intf.bNumEndpoints = 3;

    fs_status_desc.bEndpointAddress
        = hs_status_desc.bEndpointAddress
        = USB_DIR_IN | 3;
    EP_STATUS_NAME = "ep3in";

} 
3. Execute `ccid-emulator -vvv --serial 123456 --interface 1123344`

Similar errors happen when `pkcs11-tool -O` or `pkcs11-tool --list-slots`

### Logs

I attach the logs of `ccid-emulator` and `libcacard` when I `open-sctools -an`.

https://gist.github.com/polhenarejos/05ddad5514f4f6442886ae4ff2a581ae

### Versions:
* Linux kernel: 5.10
* OpenSC: 0.22.0
* CCID-emulator: `commit 9284766`

### Other outputs
In the RPI0:

pi@rpi0:~ $ pkcs11-tool --list-slots Available slots: Slot 0 (0x0): Virtual PCD 00 00 token label : CAC II token manufacturer : Common Access Card token model : PKCS#15 emulated token flags : login required, rng, token initialized, PIN initialized hardware version : 0.0 firmware version : 0.0 serial num : 000058bd002c19b5 pin min/max : 4/8 Slot 1 (0x4): Virtual PCD 00 01 (empty)

pi@rpi0:~ $ pkcs11-tool --list-interfaces Interface 'PKCS 11' version: 3.0 funcs=0xb63ec394 flags=0x0 Interface 'PKCS 11' version: 2.20 funcs=0xb63ec508 flags=0x0 Using slot 0 with a present token (0x0)

pi@rpi0:~ $ opensc-tool -l

Detected readers (pcsc)

Nr. Card Features Name 0 Yes Virtual PCD 00 00


In the PC:

pol@myPC:~$ pkcs11-tool --list-slots Available slots: Slot 0 (0x0): KOBIL EMV CAP - SecOVID Reader III [1123344] (123456) 00 00 (token not recognized)

pol@myPC:~$ pkcs11-tool --list-interfaces Interface 'PKCS 11' version: 3.0 funcs=0x76401394 flags=0x0 Interface 'PKCS 11' version: 2.20 funcs=0x76401508 flags=0x0 Using slot 0 with a present token (0x0)

pol@myPC:~$ opensc-tool -l

Detected readers (pcsc)

Nr. Card Features Name 0 Yes PIN pad KOBIL EMV CAP - SecOVID Reader III [1123344] (123456) 00 00

pol@myPC:~ $ lsusb -vvv Bus 001 Device 009: ID 0d46:3010 Kobil Systems GmbH Couldn't open device, some information will be missing Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 255 Vendor Specific Class bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize0 64 idVendor 0x0d46 Kobil Systems GmbH idProduct 0x3010 bcdDevice 1.00 iManufacturer 1 iProduct 2 iSerial 3 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 0x0056 bNumInterfaces 1 bConfigurationValue 3 iConfiguration 4 bmAttributes 0xc0 Self Powered MaxPower 2mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 11 Chip/SmartCard bInterfaceSubClass 0 bInterfaceProtocol 0 iInterface 5 ChipCard Interface Descriptor: bLength 54 bDescriptorType 33 bcdCCID 1.10 (Warning: Only accurate for version 1.0) nMaxSlotIndex 0 bVoltageSupport 1 5.0V dwProtocols 3 T=0 T=1 dwDefaultClock 3580 dwMaxiumumClock 3580 bNumClockSupported 1 dwDataRate 9600 bps dwMaxDataRate 9600 bps bNumDataRatesSupp. 1 dwMaxIFSD 255 dwSyncProtocols 00000000 dwMechanical 00000000 dwFeatures 000404FA Auto configuration based on ATR Auto voltage selection Auto clock change Auto baud rate change Auto parameter negotiation made by CCID Auto IFSD exchange Short and extended APDU level exchange dwMaxCCIDMsgLen 65545 bClassGetResponse echo bClassEnvelope echo wlcdLayout 255 cols 255 lines bPINSupport 51 verification modification bMaxCCIDBusySlots 1 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0200 1x 512 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x02 EP 2 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0200 1x 512 bytes bInterval 1

frankmorgner commented 2 years ago

First, I'd try comparing (diffing) the two use cases:

virt_cacard <--> libcacard <--> vpcd <--> pcsc <--> opensc <--> CCID emulator <--> USB <--> opensc-tool -an -vvvvvvvvvvvvvvv 2>&1 > broken.txt

vs

virt_cacard <--> libcacard <--> vpcd <--> opensc-tool -an -vvvvvvvvvvvvvvv 2>&1 > working.txt

Now remove timestamps, PID/TID and check the difference between working.txt and broken.txt...

polhenarejos commented 2 years ago

Done.

I uploaded the diff log to https://gist.github.com/polhenarejos/8585a41018ff09a92872923b09ba5b25

I also uploaded the working and broken in case are needed in a separate gist

https://gist.github.com/polhenarejos/f1d94d399760d06aaf2bed139a9e067f

frankmorgner commented 2 years ago

If you want me to look at this, you need to give me the two raw logs...

polhenarejos commented 2 years ago

Do you refer to broken and working? I've uploaded both at https://gist.github.com/polhenarejos/f1d94d399760d06aaf2bed139a9e067f

frankmorgner commented 2 years ago

by removing the timestamp, you've also deleted some of the APDUs. Just upload the complete logs of both cases...

polhenarejos commented 2 years ago

Right, my fault.

I uploaded the corrected clean logs to https://gist.github.com/polhenarejos/f1d94d399760d06aaf2bed139a9e067f

frankmorgner commented 2 years ago

The versions of OpenSC that you've used for both tests differ...

here's one difference:

Outgoing APDU (12 bytes):
00 A4 04 0C 07 A0 00 00 01 16 DB 00 ............

[opensc-tool] reader-pcsc.c:244:pcsc_internal_transmit: called
[opensc-tool] reader-pcsc.c:335:pcsc_transmit: 
Incoming APDU (2 bytes):
90 00 ..

vs

Outgoing APDU (12 bytes):
00 A4 04 0C 07 A0 00 00 01 16 DB 00 ............

[opensc-tool] reader-pcsc.c:244:pcsc_internal_transmit: called
[opensc-tool] reader-pcsc.c:335:pcsc_transmit: 
Incoming APDU (2 bytes):
61 0D a.

ccid-emulator uses opensc to communicate to the card. I think this passthrough conflicts with the result that's expected by the actual application. try using the default driver, to avoid this:

diff --git a/ccid/src/ccid.c b/ccid/src/ccid.c
index 6a03767..7742b3f 100644
--- a/ccid/src/ccid.c
+++ b/ccid/src/ccid.c
@@ -455,6 +455,7 @@ perform_PC_to_RDR_IccPowerOn(const __u8 *in, size_t inlen, __u8 **out, size_t *o
         sc_result = SC_SUCCESS;
     } else {
         sc_sm_stop(card);
+        sc_set_card_driver(ctx, "default");
         sc_result = sc_connect_card(reader, &card);
         card->caps |= SC_CARD_CAP_APDU_EXT;
     }
polhenarejos commented 2 years ago
  1. I upgraded the three OpenSC (rpi, PC and ccid/src/OpenSC) to OpenSC-0.22.0-rc1-74-gc902e199, rev: c902e199, commit-time: 2021-08-10 11:09:03 +0200 via git checkout tags/0.22.0
  2. I modified ccid.c to load "default" driver, as the suggested patch.

I still get the same issues and the APDU are still the same (90 00 vs. 61 0D). Is there another place where I could load the driver?

I've updated both logs at https://gist.github.com/polhenarejos/f1d94d399760d06aaf2bed139a9e067f

frankmorgner commented 2 years ago

in case of using the ccid-emulator, could you also create and compare a log on the RPI with the log on opensc-tool -n running on the PC?

polhenarejos commented 2 years ago

Sure! I updated the logs at https://gist.github.com/polhenarejos/f1d94d399760d06aaf2bed139a9e067f

What I see is that everything starts to diverge at line 773, with different APDU. What I do not know is why both opensc-tool send different APDU if both are in the same version 0.22.0.

What these APDU mean?

frankmorgner commented 2 years ago

the difference in the ATR doesn't cause the card to respond differently, but a different card state does.

your https://gist.github.com/polhenarejos/f1d94d399760d06aaf2bed139a9e067f still only contains working-clean.txt and broken-clean.txt. both are ok, but in the broken case, you could try to debug the ccid-emulator as well. To hardcode this, you could try the following:

diff --git a/ccid/src/ccid.c b/ccid/src/ccid.c
index 6a03767..f073cf6 100644
--- a/ccid/src/ccid.c
+++ b/ccid/src/ccid.c
@@ -200,6 +200,9 @@ int ccid_initialize(int reader_id, int verbose)
     i = initialize(reader_id, verbose, &ctx, &reader);
     if (i < 0)
         return i;
+    if (verbose > 0) {
+        sc_ctx_log_to_file(ctx, "stderr");
+    }

     return SC_SUCCESS;
 }
@@ -455,6 +458,7 @@ perform_PC_to_RDR_IccPowerOn(const __u8 *in, size_t inlen, __u8 **out, size_t *o
         sc_result = SC_SUCCESS;
     } else {
         sc_sm_stop(card);
+        sc_set_card_driver(ctx, "default");
         sc_result = sc_connect_card(reader, &card);
         card->caps |= SC_CARD_CAP_APDU_EXT;
     }
polhenarejos commented 2 years ago

Sure! I've uploaded ccid logs at https://gist.github.com/polhenarejos/74cc428cc594b16408bfe5ae98af5ab9 I ran and stopped ccid-emulator for each opensc-tool -n command in both sides to obtain separate logs. The command I used:

ccid-emulator --serial 123456 --interface 1123344 -vvvvvvvvvvvvvvv > ccid-stdout-working.txt 2> ccid-stderr-working.txt

changing the file for each case.

Edit: I realized that ccid-working logs are useless, since opensc-tool -n in the RPI does not make use of CCID Emulator. Sorry for adding noise.

polhenarejos commented 2 years ago

Comparing broken and ccid-broken logs, what I do not understand is that when, for instance, when the end client in the PC sends the APDU 00 A4 04 0C 07 A0 00 00 01 16 DB 00, the CCID Emulator receives back the APDU 61 0D (exactly as with working case). But the end client in the PC, receives 90 00. It seems that in between there is a translation from 61 0D to 90 00, which I cannot find.

polhenarejos commented 2 years ago

I ran pcscd -a -f -d in the PC to log what is received by the usb. I put the log at https://gist.github.com/polhenarejos/74cc428cc594b16408bfe5ae98af5ab9

Lines 388-390 are the above APDU and, in this case, is 90 00. Narrowing the actions, it seems that this happens in between usb and ccid.

polhenarejos commented 2 years ago

I also uploaded cleaned logs of pcscd for broken and working cases.

https://gist.github.com/polhenarejos/4c55d119f71b2663d86ed4679a4dc9ad

frankmorgner commented 2 years ago

Please have a look at the following lines of debug output from ccid-emulator: https://gist.github.com/polhenarejos/74cc428cc594b16408bfe5ae98af5ab9#file-ccid-stderr-broken-clean-txt-L910-L939

The card responds with 61 0D, but apdu->sw1/apdu->sw2 are somehow set to 90/00. The problem is somewhere between the following lines: https://github.com/frankmorgner/vsmartcard/blob/92847665325ad7b46e17f7161f943f7984eb4d54/ccid/src/ccid.c#L240-L265

You need to find out where/why 61 0D is misinterpreted as 90 00...

polhenarejos commented 2 years ago

Digging into libopensc, I found the place when it changes:

https://github.com/OpenSC/OpenSC/blob/master/src/libopensc/apdu.c#L420-L435

called from https://github.com/OpenSC/OpenSC/blob/master/src/libopensc/apdu.c#L534-L538

Now, I do not know why apdu->le == 0 is true.

polhenarejos commented 2 years ago

I commented out the APDU bytes change as:

diff --git a/src/libopensc/apdu.c b/src/libopensc/apdu.c
index b52183ea..5cafed80 100644
--- a/src/libopensc/apdu.c
+++ b/src/libopensc/apdu.c
@@ -428,8 +428,8 @@ sc_get_response(struct sc_card *card, struct sc_apdu *apdu, size_t olen)
        LOG_FUNC_CALLED(ctx);
        if (apdu->le == 0) {
                /* no data is requested => change return value to 0x9000 and ignore the remaining data */
-               apdu->sw1 = 0x90;
-               apdu->sw2 = 0x00;
+               //apdu->sw1 = 0x90;
+               //apdu->sw2 = 0x00;
                return SC_SUCCESS;
        }

And now all pkcs11-tool, opensc-tool... commands work properly. I can list object, mechanisms, as a local reader. However, I do not know whether I am breaking something deeper and what are the long-term implications of changing/not changing the return value to 0x9000. Honestly, I do not know what I am doing commenting this out.

Since OpenSC is statically linked to ccid-emulator, the modification is straightforward without breaking anything in the system.

frankmorgner commented 2 years ago

I've pushed a change, that hopefully fixes your issue.

polhenarejos commented 2 years ago

Thanks @frankmorgner for the efforts and quick response. Glad to see it works.

Finally, I could make it work based on your change:

diff --git a/ccid/src/ccid.c b/ccid/src/ccid.c
index bab81cf..7411b75 100644
--- a/ccid/src/ccid.c
+++ b/ccid/src/ccid.c
@@ -641,14 +641,15 @@ perform_PC_to_RDR_XfrBlock(const u8 *in, size_t inlen, __u8** out, size_t *outle
        }

     sc_result = sc_bytes2apdu(ctx, abDataIn, apdulen, &apdu);
-    if (sc_result >= 0)
+    if (sc_result >= 0) {
+        /* don't magically get additional data in OpenSC */
+        apdu.flags |= SC_APDU_FLAGS_NO_GET_RESP;
         sc_result = get_rapdu(&apdu, &abDataOut,
                 &abDataOutLen);
+    }
     else
         bin_log(ctx, SC_LOG_DEBUG_VERBOSE, "Invalid APDU", abDataIn,
                 __le32_to_cpu(request->dwLength));
-    /* don't magically get additional data in OpenSC */
-    apdu->flags |= SC_APDU_FLAGS_NO_GET_RESP;

     sc_result = get_RDR_to_PC_DataBlock(request->bSeq, sc_result,
             out, outlen, abDataOut, abDataOutLen);
frankmorgner commented 2 years ago

my fault, thanks for checking