Problem report ID: 533089108
With "Share smart card readers" enabled, my Linux guest sees the shared YubiKey as USB device 203a:fffd ("PARALLELS Proxy CCID - Yubico YubiKey OTP+FIDO+CCID") on the emulated EHCI controller. The proxy answers CCID commands correctly, but a bulk IN transfer whose requested length is 20480 bytes or more does not complete when the short response arrives. It only completes when the host-side timeout expires (with the correct data). Requests of 16384 bytes or less complete in a few milliseconds.
The Linux CCID driver (libccid, used by pcsc-lite) always reads responses into a 65556-byte buffer (CCID_HEADER_SIZE 10 + CMD_BUF_SIZE 65546), so every response times out and is discarded. As a result the shared reader is unusable from PC/SC (OpenSC, ssh-keygen -D, browsers) in Linux guests, and anything that loads OpenSC (e.g. Chromium) hangs.
Environment
Guest USB topology
CCID descriptor: bcdCCID 1.10, dwFeatures 0x0002004E (short APDU level exchange), dwMaxCCIDMsgLen 512, bulk IN/OUT wMaxPacketSize 512.
Reproduce (in the guest, pcscd stopped, user given access to the device)
repro.py:
Expected: every read returns as soon as the 31-byte RDR_to_PC_DataBlock arrives, regardless of the requested length.
Actual:
The response data is always correct (... 61 11 4F 06 00 00 10 00 01 00 79 07 4F 05 A0 00 00 03 08 90 00); only completion is delayed until the timeout. With a 20 s timeout the reply arrives at exactly 20.0 s. The threshold matches the point where an EHCI transfer needs more than one qTD (one qTD covers at most 20480 bytes), so the emulated EHCI controller appears not to retire the remaining qTDs of a transfer after a short packet.
Resulting pcscd log (repeats for every APDU):
Note: libccid also does not list 203A:FFFD in its Info.plist, so pcscd does not bind the reader at all until the ID is added there manually.
Request: please fix short-packet completion for multi-qTD bulk IN transfers on the emulated EHCI controller (or attach the Proxy CCID device to the xHCI controller), and consider getting 203A:FFFD added to libccid's supported reader list.
Update: working workaround
Capping the CCID driver's bulk IN reads for this device at 16 KB makes the shared reader fully usable in the guest. I do it with a small LD_PRELOAD shim for pcscd, plus a copy of the ifd-ccid bundle (with 203A:FFFD added to its Info.plist) that pcscd and libccid find via PCSCLITE_HP_DROPDIR, so the packaged files stay untouched.
readcap.c (only affects 203A:FFFD, other readers are passed through unchanged):
Build and install (Fedora, needs gcc and libusb1-devel):
/etc/systemd/system/pcscd.service.d/parallels-proxy-ccid.conf:
Then: sudo restorecon -R /usr/local/lib64 && sudo systemctl daemon-reload && sudo systemctl restart pcscd.socket
With this, opensc-tool, pkcs11-tool and ssh-keygen -D /usr/lib64/pkcs11/opensc-pkcs11.so all work against the YubiKey through the proxy, with immediate responses. Side effect: guest access appears to reset the card on the host, so a host ssh-agent holding a PIV session has to log in with the PIN again.
With "Share smart card readers" enabled, my Linux guest sees the shared YubiKey as USB device 203a:fffd ("PARALLELS Proxy CCID - Yubico YubiKey OTP+FIDO+CCID") on the emulated EHCI controller. The proxy answers CCID commands correctly, but a bulk IN transfer whose requested length is 20480 bytes or more does not complete when the short response arrives. It only completes when the host-side timeout expires (with the correct data). Requests of 16384 bytes or less complete in a few milliseconds.
The Linux CCID driver (libccid, used by pcsc-lite) always reads responses into a 65556-byte buffer (CCID_HEADER_SIZE 10 + CMD_BUF_SIZE 65546), so every response times out and is discarded. As a result the shared reader is unusable from PC/SC (OpenSC, ssh-keygen -D, browsers) in Linux guests, and anything that loads OpenSC (e.g. Chromium) hangs.
Environment
- Host: Mac14,10 (Apple silicon), macOS 26.7.1
- Parallels Desktop 27.0.3 (58680)
- Guest: Fedora Linux 44 aarch64, kernel 7.2.12-200.fc44.aarch64, Parallels Tools 27.0.3.58680
- Guest packages: pcsc-lite 2.4.1, pcsc-lite-ccid 1.7.1, libusb1 1.0.30, python3-pyusb 1.3.1
- Card: YubiKey 5 (PIV 5.8.0)
Guest USB topology
Code:
/: Bus 001.Port 001: Dev 001, Class=root_hub, Driver=ehci-pci/15p, 480M
|__ Port 002: Dev 002, If 0, Class=Chip/SmartCard, Driver=[none], 480M
Reproduce (in the guest, pcscd stopped, user given access to the device)
Code:
sudo systemctl stop pcscd.socket pcscd.service
sudo setfacl -m u:$USER:rw /dev/bus/usb/001/002
python3 repro.py
Python:
import struct, time, usb.core, usb.util
dev = usb.core.find(idVendor=0x203A, idProduct=0xFFFD)
intf = next(i for i in dev.get_active_configuration() if i.bInterfaceClass == 0x0B)
usb.util.claim_interface(dev, intf.bInterfaceNumber)
eps = {usb.util.endpoint_direction(e.bEndpointAddress): e for e in intf
if usb.util.endpoint_type(e.bmAttributes) == usb.util.ENDPOINT_TYPE_BULK}
out, inn = eps[usb.util.ENDPOINT_OUT], eps[usb.util.ENDPOINT_IN]
apdu = bytes.fromhex("00A4040005A000000308") # SELECT PIV
for seq, size in enumerate([1024, 4096, 16384, 20480, 20481, 32768]):
out.write(struct.pack("<BIBB", 0x6F, len(apdu), 0, seq) + b"\0\0\0" + apdu)
t = time.time()
r = bytes(inn.read(size, timeout=2000))
print(f"read size={size:<6} -> {time.time() - t:.3f}s, {len(r)} bytes")
Expected: every read returns as soon as the 31-byte RDR_to_PC_DataBlock arrives, regardless of the requested length.
Actual:
Code:
read size=1024 -> 0.003s, 31 bytes
read size=4096 -> 0.003s, 31 bytes
read size=16384 -> 0.002s, 31 bytes
read size=20480 -> 2.006s, 31 bytes
read size=20481 -> 2.009s, 31 bytes
read size=32768 -> 2.005s, 31 bytes
Resulting pcscd log (repeats for every APDU):
Code:
ccid_usb.c:1131:ReadUSB() read failed (1/2): LIBUSB_ERROR_TIMEOUT
ifdwrapper.c:548:IFDTransmit() Card not transacted: 612
winscard.c:1622:SCardTransmit() Card not transacted: rv=SCARD_E_NOT_TRANSACTED
Request: please fix short-packet completion for multi-qTD bulk IN transfers on the emulated EHCI controller (or attach the Proxy CCID device to the xHCI controller), and consider getting 203A:FFFD added to libccid's supported reader list.
Update: working workaround
Capping the CCID driver's bulk IN reads for this device at 16 KB makes the shared reader fully usable in the guest. I do it with a small LD_PRELOAD shim for pcscd, plus a copy of the ifd-ccid bundle (with 203A:FFFD added to its Info.plist) that pcscd and libccid find via PCSCLITE_HP_DROPDIR, so the packaged files stay untouched.
readcap.c (only affects 203A:FFFD, other readers are passed through unchanged):
C:
#define _GNU_SOURCE
#include <dlfcn.h>
#include <libusb.h>
#define PROXY_VID 0x203A
#define PROXY_PID 0xFFFD
#define MAX_READ 16384
int libusb_bulk_transfer(libusb_device_handle *handle, unsigned char endpoint,
unsigned char *data, int length, int *transferred, unsigned int timeout)
{
static int (*real)(libusb_device_handle *, unsigned char, unsigned char *,
int, int *, unsigned int);
struct libusb_device_descriptor desc;
if (!real)
real = dlsym(RTLD_NEXT, "libusb_bulk_transfer");
if ((endpoint & LIBUSB_ENDPOINT_IN) && length > MAX_READ
&& libusb_get_device_descriptor(libusb_get_device(handle), &desc) == 0
&& desc.idVendor == PROXY_VID && desc.idProduct == PROXY_PID)
length = MAX_READ;
return real(handle, endpoint, data, length, transferred, timeout);
}
Build and install (Fedora, needs gcc and libusb1-devel):
Code:
sudo gcc -O2 -Wall -fPIC -shared -o /usr/local/lib64/libccid-readcap.so readcap.c $(pkg-config --cflags --libs libusb-1.0) -ldl
B=/usr/local/lib64/pcsc/drivers/ifd-ccid.bundle
sudo mkdir -p $B/Contents/Linux
sudo cp /usr/lib64/pcsc/drivers/ifd-ccid.bundle/Contents/Info.plist $B/Contents/
# edit $B/Contents/Info.plist: append 0x203A / 0xFFFD / Parallels Proxy CCID
# to the ifdVendorID / ifdProductID / ifdFriendlyName arrays (same index in each)
sudo ln -s /usr/lib64/pcsc/drivers/ifd-ccid.bundle/Contents/Linux/libccid.so $B/Contents/Linux/libccid.so
/etc/systemd/system/pcscd.service.d/parallels-proxy-ccid.conf:
Code:
[Service]
Environment=PCSCLITE_HP_DROPDIR=/usr/local/lib64/pcsc/drivers
Environment=LD_PRELOAD=/usr/local/lib64/libccid-readcap.so
Then: sudo restorecon -R /usr/local/lib64 && sudo systemctl daemon-reload && sudo systemctl restart pcscd.socket
With this, opensc-tool, pkcs11-tool and ssh-keygen -D /usr/lib64/pkcs11/opensc-pkcs11.so all work against the YubiKey through the proxy, with immediate responses. Side effect: guest access appears to reset the card on the host, so a host ssh-agent holding a PIV session has to log in with the PIN again.
Last edited: