Java Card is quite an interesting concept — developing native applications on smartcards is tedious : the old ones are running on exotic architectures, there are specific security rules to consider for each platform, and it requires significant expertise to have easy portable code from one to another.
What if we could write some code once, and run it on all platforms ? That’s the old promise of Java fulfilled by Java Card — a very tiny subset of Java, extremely popular on SIM cards. Java Card also defines how to isolate multiple applications on the same smartcard, making it possible to have several providers hosting their applications. And even to be used as a standard development platform for end users.
But, of course, there are issues. First the technical ones.
Lack of support for essential crypto functions
Java Card API is evolving very slowly — Elliptic Curves support was only added in recent releases, and there is no standard API to directly obtain the public key associated to a private key, which is annoying for Hierarchical Deterministic Wallets. RIPEMD 160 is nowhere to be found, which makes it complicated to display an address, and SHA 512 support is only found is the latest implementations, which just kills Hierarchical Deterministic Wallets for good. Some of those features are only available with a vendor NDA, and we’d like to try to avoid making this mandatory for the community.
Regarding the first part, there is an ECDH Key Agreement API that let you retrieve a 32 bytes secret — run it with the generator point of the curve, and you get the X coordinates of the public key. Then if you sign something, the host can perform a key recovery and send back the full public key to the card, which can verify it by signing and verifying an arbitrary message. This trick was first used by Toporin in the SatoChip applet
Then, the SHA 512 problem. There is some open source code around which is unfortunately extremely slow (more than 10 seconds per round). Let’s optimize it taking a few Java Card properties into consideration.
Pure Java SHA 512 implementation
First of all, we want to avoid function calls. Even more virtual function calls, which are resolved every time by the Virtual Machine.
Then, we want to run everything in RAM. Writing to flash is of course always slower. But we usually have less than 4kB of RAM available for the application.
Another interesting thing that comes to mind when browsing the Virtual Machine specification — accessing an array is slow, because the firewall needs to check both the size of the array and the type of the array. So we want to avoid arrays as much as possible.
And last but not least, we’re running Java, so no unsigned types. Also portable Java Card, so the largest type available is a short. You can imagine that rewriting SHA 512, designed to be implemented on a 64 bits architecture is going to be fun.
Long story short, after a lot of tears and preprocessing abuse, we did it, and now a single round runs in less than 1 second. Still slow, but workable.
The technical issues being dealt with, let’s move to the deployment.
Which hardware device to use?
Finding the right device is hard, finding the right device with open keys is even harder. Yubico used to sell their Yubikey Neo with open keys, but not anymore. There are other attempts to list suitable cards, and anything based on JCOP 2.4.2 should be fine. Don’t hesitate to point us to any interesting deal you could find.
In any case, we’ll do our best to find some partnerships to let you try your own customizations and test new secure Bitcoin use cases in the most open possible way. Many promising options are available : contactless cards, phones … all running the portable Ledger Wallet API
The source code
You can find the source code of the main applet on Github https://github.com/ledgerhq/ledger-javacard and try it with Electrum with Ledger plugin available on https://github.com/LedgerHQ/btchip-python