Emulating a PKI smart card with CyanogenMod 9.1

We discussed the embedded secure element available in recent Android devices, it's execution environment and how Google Wallet makes use if it in the last series of articles. We also saw that unless you have a contract with Google and have them (or the TSM they use) distribute your applets to supported devices, there is currently no way to install anything on the embedded secure element. We briefly mentioned that CyanogenMod 9.1 supports software card emulation and it is a more practical way to create your own NFC-enabled applications. We'll now see how software card emulation works and show how you can use it to create a simple PKI 'applet' that can be accessed via NFC from any machine with a contactless card reader.

Software card emulation

We already know that if the embedded secure element is put in virtual mode it is visible to external readers as a contactless smartcard. Software card emulation (sometimes referred to as Host Card Emulation or HCE) does something very similar, but instead of routing commands received by the NFC controller to the SE, it delivers them to the application processor, and they can be processed by regular applications. Responses are then sent via NFC to the reader, and thus your app takes the role of a virtual contactless 'smartcard' (refer to this paper for a more thorough discussion).  Software card emulation is currently available on BlackBerry phones, which offer standard APIs for apps to register with the OS and process card commands received over NFC. Besides a BlackBerry device, you can use some contactless  readers in emulation mode to emulate NFC tags or a full-featured smart card. Stock Android doesn't (yet) support software card emulation, even though the NFC controllers in most current phones have this capability. Fortunately, recent version of CyanogenMod integrate a set of patches that unlock this functionality of the PN544 NFC controller found in recent Nexus (and other) devices. Let's see how it works in a bit more detail.

CyanogenMod implementation

Android doesn't provide a direct interface to its NFC subsystem to user-level apps. Instead, it leverages the OS's intent and intent filter infrastructure to let apps register for a particular NFC event (ACTION_NDEF_DISCOVERED, ACTION_TAG_DISCOVERED and ACTION_TECH_DISCOVERED) and specify additional filters based on tag type or features. When a matching NFC tag is found, interested applications are notified and one of them is selected to handle the event, either by the user or automatically if it is in the foreground and has registered for foreground dispatch. The app can then access a generic Tag object representing the target NFC device and use it to retrieve a concrete tag technology interface such as MifareClassic or IsoDep that lets it communicate with the device and use its native features. Card emulation support in CyanogenMod doesn't attempt to change or amend Android's NFC architecture, but integrates with it by adding support for two new tag technologies: IsoPcdA and IsoPcdB. 'ISO' here is the International Organization for Standardization, which among other things, is responsible for defining NFC communication standards. 'PCD' stands for Proximity Coupling Device, which is simply ISO-speak for a contactless reader. The two classes cover the two main NFC flavours in use today (outside of Japan, at least) -- Type A (based on NXP technology) and Type B (based on Motorolla technology). As you might have guessed by now, the patch reverses the usual roles in the Android NFC API: the external contactless reader is presented as a 'tag', and 'commands' you send from the phone are actually replies to the reader-initiated communication. If you have Google Wallet installed the embedded secure element is activated as well, so touching the phone to a reader would produce a potential conflict: should it route commands to the embedded SE or to applications than can handle IsoPcdA/B tags? The CyanogenMod patch handles this by using Android's native foreground dispatch mechanism: software card emulation is only enabled for apps that register for foreground dispatch of the relevant tag technologies. So unless you have an emulation app in the foreground, all communication would be routed to Google Wallet (i.e., the embedded SE). In practice though, starting up Google Wallet on ROMs with the current version of the patch might block software card emulation, so it works best if Google Wallet is not installed. A fix is available, but not yet merged in CyanogenMod master (Updated: now merged, should roll out with CM10 nightlies) .

Both of the newly introduced tag technologies extend BasicTagTechnology and offer methods to open, check and close the connection to the reader. They add a public transceive() method that acts as the main communication interface: it receives reader commands and sends the responses generated by your app to the PCD. Here's a summary of the interface:

abstract class BasicTagTechnology implements TagTechnology {
public boolean isConnected() {...}

public void connect() throws IOException {...}

public void reconnect() throws IOException {...}

public void close() throws IOException {...}

byte[] transceive(byte[] data, boolean raw) throws IOException {...}
}

Now that we know (basically) how it works, let's try to use software card emulation in practice.

Emulating a contactless card

As discussed in the previous section, to be able to respond to reader commands we need to register our app for one of the PCD tag technologies and enable foreground dispatch. This is no different than handling stock-supported  NFC technologies. We need to add an intent filter and a reference to a technology filter file to the app's manifest:

<activity android:label="@string/app_name" 
android:launchmode="singleTop"
android:name=".MainActivity"
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED" />
</intent-filter>

<meta-data android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="@xml/filter_nfc" />
</activity>

We register the IsoPcdA tag technology in filter_nfc.xml:

<resources>
<tech-list>
<tech>android.nfc.tech.IsoPcdA</tech>
</tech-list>
</resources>

And then use the same technology list to register for foreground dispatch in our activity:

public class MainActivity extends Activity {

public void onCreate(Bundle savedInstanceState) {
pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,
getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
filters = new IntentFilter[] { new IntentFilter(
NfcAdapter.ACTION_TECH_DISCOVERED) };
techLists = new String[][] { { "android.nfc.tech.IsoPcdA" } };
}

public void onResume() {
super.onResume();
if (adapter != null) {
adapter.enableForegroundDispatch(this, pendingIntent, filters,
techLists);
}
}

public void onPause() {
super.onPause();
if (adapter != null) {
adapter.disableForegroundDispatch(this);
}
}

}

With this in place, each time the phone is touched to an active reader, we will get notified via the activity's onNewIntent() method. We can get a reference to the Tag object using the intent's extras as usual. However, since neither IsoPcdA nor its superclass are part of the public SDK, we need to either build the app as part of CyanogenMod's source, or, as usual, resort to reflection. We choose to create a simple wrapper class that calls IsoPcdA methods via reflection, after getting an instance using the static get() method like this:

Class cls = Class.forName("android.nfc.tech.IsoPcdA");
Method get = cls.getMethod("get", Tag.class);
// this returns an IsoPcdA instance
tagTech = get.invoke(null, tag);

Now after we connect() we can use the transceive() method to reply to reader commands. Note that since the API is not event-driven, you won't get notified with the reader command automatically. You need to send a dummy payload to retrieve the first reader command APDU. This can be a bit awkward at first, but you just have to keep in mind that each time you call transceive() the next reader command comes in via the return value. Unfortunately this means that after you send your last response, the thread will block on I/O waiting for transceive() to return, which only happens after the reader sends its next command, which might be never. The thread will only stop if an exception is thrown, such as when communication is lost after separating the phone from the reader. Needless to say, this makes writing robust code a bit tricky. Here's how to start off the communication:

// send dummy data to get first command APDU
// at least two bytes to keep smartcardio happy
byte[] cmd = transceive(new byte[] { (byte) 0x90, 0x00 });

Writing a virtual PKI applet

Software card emulation in CyanogneMod is limited to ISO 14443-4 (used mostly for APDU-based communication), which means that you cannot emulate cards that operate on a lower-level protocol such as MIFARE Classic. This leaves out opening door locks that rely on the card UID with your phone (the UID of the emulated card is random) or getting a free ride on the subway (you cannot clone a traffic card with software alone), but allows for emulating payment (EMV) cards which use an APDU-based protocol. In fact, the first commercial application (company started by patch author Doug Yeagerthat makes use of Android software card emulation, Tapp, emulates a contactless Visa card and does all necessary processing 'in the cloud', i.e., on a remote server. Payment applications are the ones most likely to be developed using software card emulation because of the potentially higher revenue: at least one other company has announced that it is building a cloud-based NFC secure element. We, however, will look at a different use case: PKI.

PKI has been getting a lot of bad rep due to major CAs getting compromised every other month, and it has been stated multiple times that it doesn't really work on the Internet. It is however still a valid means of authentication in a corporate environment where personal certificates are used for anything from desktop login to remote VPN access. Certificates and associated private keys are often distributed on smart cards, sometimes contactless or dual-interface. Since Android now has standard credential storage which can be protected by hardware on supported devices, we could use an Android phone with software card emulation in place of a PKI card. Let's try to write a simple PKI 'applet' and an associated host-side client application to see if this is indeed feasible.

A PKI JavaCard applet can offers various features, but the essential ones are:
  • generating or importing keys
  • importing a public key certificate
  • user authentication (PIN verification)
  • signing and/or encryption with card keys
Since we will be using Android's credential storage to save keys and certificates, we already have the first two features covered. All we need to implement is PIN verification and signing (which is actually sufficient for most applications, including desktop login and SSL client authentication). If we were building a real solution, we would implement a well known applet protocol, such as one of a major vendor or an open one, such as the MUSCLE card protocol, so that we can take advantage of desktop tools and cryptographic libraries (Windows CSPs and PKCS#11 modules, such as OpenSC). But since this is a proof-of-concept exercise, we can get away by defining our own mini-protocol and only implement the bare minimum. We define the applet AID (quite arbitrary, and may be in already in use by someone else, but there is really no way to check) and two commands: VERIFY PIN and SIGN DATA. The protocol is summarized in the table below:

Virtual PKI applet protocol
CommandCLAINSP1P2LcDataResponse
SELECT00A4040006AID: A00000000101109000/6985/6A82/6F00
VERIFY PIN8001XXXXPIN length (bytes)PIN characters (ASCII)9000/6982/6985/6F00
SIGN DATA8002XXXXSigned data length (bytes)Signed data9000+signature bytes/6982/6985/6F00

The applet behaviour is rather simple: it returns a generic error if you try to send any commands before selecting it, and then requires you to authenticate by verifying the PIN before signing data. To implement the applet, we first handle new connections from a reader in the main activity's onNewIntent() method, where we receive an Intent containing a reference to the IsoPcdA object we use to communicate with the PCD. We verify that the request comes from a card reader, create a wrapper for the Tag object, connect() to the reader and finally pass control to the PkiApplet by calling it's start() method.

Tag tag = (Tag) intent.getExtras().get(NfcAdapter.EXTRA_TAG);
List techList = Arrays.asList(tag.getTechList());
if (!techList.contains("android.nfc.tech.IsoPcdA")) {
return;
}

TagWrapper tw = new TagWrapper(tag, "android.nfc.tech.IsoPcdA");
if (!tw.isConnected()) {
tw.connect();
}

pkiApplet.start(tw);

The applet in turn starts a background thread that reads commands until available and exits if communication with the reader is lost. The implementation is not terribly robust, but is works well enough for our POC:

Runnable r = new Runnable() {
public void run() {
try {
// send dummy data to get first command APDU
byte[] cmd = transceive(new byte[] { (byte) 0x90, 0x00 });
do {
// process commands
} while (cmd != null && !Thread.interrupted());
} catch (IOException e) {
// connection with reader lost
return;
}
}
};

appletThread = new Thread(r);
appletThread.start();


Before the applet can be used it needs to be 'personalized'. In our case this means importing the private key the applet will use for signing and setting a PIN. To initialize the private key we import a PKCS#12 file using the KeyChain API and store the private key alias in shared preferences. The PIN is protected using 5000 iterations of PBKDF2 with a 64-bit salt. We store the resulting PIN hash and the salt in shared preferences as well and repeat the calculation against the PIN we receive from applet clients to check if it matches. This avoids storing the PIN in clear text, but keep in mind that a short numeric-only PIN can be brute-forced in minutes (the app doesn't restrict PIN size, it can be up to 255 characters (bytes), the maximum size of APDU data). Here's how our 'personalization' UI looks like:


To make things simple, applet clients send the PIN in clear text, so it could theoretically be sniffed if NFC traffic is intercepted. This can be avoided by using some sort of a challenge-response mechanism, similar to what 'real' (e.g., EMV) cards do. Once the PIN is verified, clients can send the data to be signed and receive the signature bytes in the response. Since the size of APDU data is limited to 255 bytes (due to the single byte length field) and the applet doesn't support any sort of chaining, we are limited to using RSA keys up to 1024 bits long (a 2048-bit key needs 256 bytes). The actual applet implementation is quite straightforward: it does some minimal checks on received APDU commands, gets the PIN or signed data and uses it to execute the corresponding operation. It then selects a status code based on operation success or failure and returns it along with the result data in the response APDU. See the source code for details.

Writing a host-side applet client

Now that we have an applet, we need a host-side client to actually make use of it. As we mentioned above, for a real-world implementation this would be a standard PKCS#11 or CSP module for the host operating system that plugs into PKI-enabled applications such as browsers or email and VPN clients. We'll however create our own test Java client using the Smart Card I/O API (JSR 268). This API comes with Sun/Oracle Java SDKs since version 1.6 (Java 6), but is not officially a part of the SDK, because it is apparently not 'of sufficiently wide interest' according to the JSR expert group (committee BS at its best!). Eclipse goes as far as to flag it as a 'forbidden reference API', so you'll need to change error handling preferences to compile in Eclipse. In practice though, JSR 268 is a standard API that works fine on Windows, Solaris, Linux an Mac OS X (you may have to set the sun.security.smartcardio.library system property to point to your system's PC/SC library), so we'll use it for our POC application. The API comes with classes representing card readers, the communication channel and command and response APDUs. After we get a reference to a reader and then a card, we can create a channel and exchange APDUs with the card. Our PKI applet client is a basic command line program that waits for card availability and then simply sends the SELECT, VERIFY PIN and SIGN DATA commands in sequence, bailing out on any error (card response with status different from 0x9000). The PIN is specified in the first command line parameter and if you pass a certificate file path as the second one, it will use it to verify the signature it gets from the applet. See full code for details, but here's how to connect to a card and send a command:

TerminalFactory factory = TerminalFactory.getDefault();
CardTerminals terminals = factory.terminals();

Card card = waitForCard(terminals);
CardChannel channel = card.getBasicChannel();
CommandAPDU cmd = new CommandAPDU(CMD);
ResponseAPDU response = channel.transmit(cmd);

Card waitForCard(CardTerminals terminals)
throws CardException {
while (true) {
for (CardTerminal ct : terminals
.list(CardTerminals.State.CARD_INSERTION)) {
return ct.connect("*");
}
terminals.waitForChange();
}
}

And to prove that this all works, here's the output from a test run of the client application:

$ ./run.sh 1234 mycert.crt 
Place phone/card on reader to start
--> 00A4040006A0000000010101
<-- 9000
--> 800100000431323334
<-- 9000
--> 80020000087369676E206D6521
<-- 11C44A5448... 9000 (128)

Got signature from card: 11C44A5448...
Will use certificate from 'mycert.crt' to verify signature
Issuer: CN=test-CA, ST=Tokyo, C=JP
Subject: CN=test, ST=Tokyo, C=JP
Not Before: Wed Nov 30 00:04:31 JST 2011
Not After: Thu Nov 29 00:04:31 JST 2012

Signature is valid: true

This software implementation comes, of course, with the disadvantage that while the actual private key might be protected by Android's system key store, PIN verification and other operations not directly protected by the OS will be executed in a regular app. An Android app, unlike a dedicated smart card, could be compromised by other (malicious) apps with sufficient privileges. However, since recent Android devices do have (some) support for a Trusted Execution Environment (TEE), the sensitive parts of our virtual applet can be implemented as Trusted Application (TA) running within the TEE. The user-level app would then communicate with the TA using the controlled TEE interface, and the security level of the system could come very close to running an actual applet in a dedicated SE.

Summary

Android already supports NFC card emulation using an embedded SE (stock Android) or the UICC (various vendor firmwares). However, both of those are tightly controlled by their owning entities (Google or MNOs), and there is currently no way for third party developers to install applets and create card emulation apps. An alternative to SE-based card emulation is software card emulation, where an user-level app processes reader commands and returns responses via the NFC controller. This is supported by commonly deployed NFC controller chips, but is not implemented in the stock Andorid NFC subsystem. Recent versions of CyanogneMod however do enable it by adding support for two more tag technologies (IsoPcdA and IsoPcdB) that represent contactless readers instead of actual tags. This allows Android applications to emulate pretty much any ISO 14443-4 compliant contactless card application: from EMV payment applications to any custom JavaCard applet. We presented a sample app that emulates a PKI card, allowing you to store PKI credentials on your phone and potentially use it for desktop login or VPN access on any machine equipped with a contacltess reader. Hopefully software card emulation will become a part of stock Android in the future, making this and other card emulation NFC applications mainstream.