Kotlin Multiplatform bindings for the PC/SC API (winscard), which targets:
- JNA on JVM (builds a single library for anything JNA supports)
- Native for Kotlin/Native apps
This was developed to support the PC version of Metrodroid (a public transit card reader).
Note
Due to functional limitations in Kotlin/Native, cross-compiling Native targets is not supported, except from a macOS host to a macOS target.
| Platform | PC/SC Implementation | JVM (JNA) | Native |
|---|---|---|---|
Linux x86_64 |
pcsclite | ✅ | ✅ |
macOS 15.7 aarch64 |
PCSC.framework |
✅ | ✅ |
macOS 15.7 x86_64 |
PCSC.framework |
✅ | ✅ |
Windows 11 aarch64 |
WinSCard.dll | ✅ | ❌ |
Windows 10 x86_64 |
WinSCard.dll | ✅ | ✅ |
API documentation can be viewed online, or built locally with:
./gradlew dokkaHtmlThis library mostly follows the PC/SC API, but takes some liberties to make it easier to use in Kotlin, such as using object orientation, providing helper methods for common patterns, parsing bitfields into properties, and abstracting away some small platform-specific API differences.
The result is that the same "common" API can be used on all platforms: see
the sample directory for an example.
All targets, even native ones, require JDK 17 or later to be installed (for Gradle).
To run the tests, you need:
- a working PC/SC-compatible smart card reader (IFD)
- a card inserted into the reader (ICC)
If you don't, the tests will fail with AssertionError or
IndexOutOfBoundsException (when there is no reader), or PCSCError (when
there is no card inserted).
To build the library to run on a regular JVM, and run tests:
./gradlew :jvmMainClasses :jvmTestThis builds for all platforms, as the prebuilt net.java.dev.jna package already includes
platform-specific JNI helpers. You don't need any cross-compiling or special machine for that.
Note
Only x86_64 Linux targets are currently supported for native builds.
- Build dependencies:
libpcsclite1 libpcsclite-dev - Run-time dependencies:
libpcsclite1
To build the native library and run tests:
./gradlew :linuxX64MainKlibrary :linuxX64Test- Build dependencies: Xcode 11 or later
To build the native library and run tests:
# For Apple Silicon (aarch64):
./gradlew :macosArm64MainKlibrary :macosArm64Test
# For Intel (x86_64):
./gradlew :macosX64MainKlibrary :macosX64TestNote
Only x86_64 Windows hosts and targets are supported for native builds.
Kotlin/Native
does not support building on Windows aarch64 hosts
(even when targeting x86_64) and
does not support building for mingwArm64 targets.
It is possible to build the library for Windows x86_64 on a Windows x86_64
host, and run it on a Windows 11 ARM system through emulation.
To build the native library and run tests:
.\gradlew :mingwX64MainKlibrary :mingwX64TestInstall libpcsclite1 and pcscd packages.
If you're using a reader with NXP PN53x series chipset (eg: ACS ARC122U), you
need to disable the pn533 and pn533_usb kernel modules:
# First, unplug the card reader.
# On Linux 3.1 - 4.6:
echo "blacklist pn533" | sudo tee -a /etc/modprobe.d/blacklist.conf
sudo rmmod pn533
# On Linux 4.7 and later:
echo "blacklist pn533_usb" | sudo tee -a /etc/modprobe.d/blacklist.conf
sudo rmmod pn533_usb
# Finally, plug the card reader in again.The pn533/pn533_usb module is a driver for a new Linux-kernel-specific NFC
subsystem, which is incompatible with all existing software, including
libacsccid1 (its PC/SC IFD handler).
JSR 268 defines a standard API for smart cards for Java applications. It will only work on JVM targets.
The API design is entirely different, and is only available out-of-the-box on Java 8 and earlier.
jnasmartcardio provides a JNA-based implementation of these APIs with
numerous bug fixes. This inspired kotlin-pcsc's JVM implementation.
Kotlin/Native on macOS has a disabled and partially-implemented
platform.PCSC / PCSC.def.
This isn't actually usable, but because it's disabled, it doesn't cause any troubles.
Kotlin/Native on Windows includes bindings for the entire Win32 API
via platform.windows / windows.def, including winscard.h
(via windows.h).
However, these bindings don't actually work:
windows.defdoesn't link binaries toWinSCard.dllwindows.defdoesn't handle multi-string types correctly (needed forSCardListReaders).
To make matters worse, windows.def doesn't use headerFilter, which makes it
harder to provide working bindings on Windows.
intarsys smartcard-io is a Java/JVM library which provides a
Java-friendly PC/SC API (and a javax.smartcardio wrapper).
While it can be used with Kotlin, it only targets the JVM (not Native).
Yes! See the sample directory of this repository.
This supports building on all target platforms, and includes a shadowJar task,
which pulls in all dependencies to a single JAR file.
This is explicitly not in scope for this project.
Most mobile devices do not offer a PC/SC-compatible API. The few devices that do run a regular enough Linux userland that you should be able to build using that.
You'll need to provide your own implementation of those protocols.
PC/SC only provides a very low level interface. Your application will be sending
a ByteArray to the ICC and getting a ByteArray back.
It doesn't even parse the APDU fields (CLA, INS, etc.) for you...