Accessing the embedded secure element in Android 4.x

After discussing credential storage and Android's disk encryption, we'll now look at another way to protect your secrets: the embedded secure element (SE) found in recent devices. In the first post of this three part series we'll give some background info about the SE and show how to use the SE communication interfaces Android 4.x offers. In the second part we'll try sending some actual commands in order to find out more about the SE execution environment. Finally we will discuss Google Wallet and how it makes use of the SE.

What is a Secure Element and why do you want one? 

A Secure Element (SE) is a tamper resistant smart card chip capable of running smart card applications (called applets or cardlets) with a certain level of security and features. A smart card is essentially a minimalistic computing environment on single chip, complete with a CPU, ROM, EEPROM, RAM and I/O port. Recent cards also come equipped with cryptographic co-processors implementing common algorithms such as DES, AES and RSA. Smart cards use various techniques to implement tamper resistance, making it quite hard to extract data by disassembling or analyzing the chip. They come pre-programmed with a  multi-application OS that takes advantage of the hardware's memory protection features to ensure that each application's data is only available to itself. Application installation and (optionally) access is controlled by requiring the use of cryptographic keys for each operation.

The SE can be integrated in mobile devices in various form factors: UICC (commonly known as a SIM card), embedded in the handset or connected to a SD card slot. If the device supports  NFC the SE is usually connected to the NFC chip, making it possible to communicate with the SE wirelessly. 

Smart cards have been around for a while and are now used in applications ranging from pre-paid phone calls and transit ticketing to credit cards and VPN credential storage. Since an SE installed in a mobile device has equivalent or superior capabilities to that of a smart card, it can theoretically be used for any application physical smart cards are currently used for. Additionally, since an SE can host multiple applications, it has the potential to replace the bunch of cards people use daily with a single device. Furthermore, because the SE can be controlled by the device's OS, access to it can be restricted by requiring additional authentication (PIN or passphrase) to enable it. 

So a SE is obviously a very useful thing to have and with a lot of potential, but why would you want to access one from your apps? Aside from the obvious payment applications, which you couldn't realistically build unless you own a bank and have a contract with Visa and friends, there is the possibility of storing other cards you already have (access cards, loyalty cards, etc.) on your phone, but that too is somewhat of a gray area and may requiring contracting the relevant issuing entities. The main application for third party apps would be implementing and running a critical part of the app, such as credential storage or license verification inside the SE to guarantee that it is impervious to reversing and cracking. Other apps that can benefit from being implemented in the SE are One Time Password (OTP) generators and, of course PKI credential (i.e., private keys) storage. While implementing those apps is possible today with standard tools and technologies, using them in practice on current commercial Android devices is not that straightforward. We'll discuss this in detail the second part of the series, but let's first explore the types of SEs available on mobile devices, and the level of support they have in Android. 

Secure Element form factors in mobile devices

As mentioned in the previous section, SEs come integrated in different flavours: as an UICC, embedded or as plug-in cards for an SD card slot. This post is obviously about the embedded SE, but let's briefly review the rest as well. 

Pretty much any mobile device nowadays has an UICC (aka SIM card, although it is technically a SIM only when used on GSM networks) of some form or another. UICCs are actually smart cards that can host applications, and as such are one form of a SE. However, since the UICC is only connected to the basedband processor, which is separate from the application processor that runs the main device OS, they cannot be accessed directly from Android. All communication needs to go through the Radio Interface Layer (RIL) which is essentially a proprietary IPC interface to the baseband. Communication to the UICC SE is carried out using special extended AT commands (AT+CCHO, AT+CCHC, AT+CGLA as defined by 3GPP TS 27.007), which the current Android telephony manager does not support. The SEEK for Android project provides patches that do implement the needed commands, allowing for communicating with the UICC via their standard SmartCard API, which is a reference implementation of the SIMalliance Open Mobile API specification. However, as most components that talk directly to the hardware in Android, the RIL consists of an open source part (rild), and a proprietary library (libXXX-ril.so). In order to support communication with the UICC secure element, support for this needs to be added to both to rild and to the underlying proprietary library, which is of course up to hardware vendors. The SEEK project does provide a patch that lets the emulator talk directly to a UICC in an external PC/SC reader, but that is only usable for experiments. While there is some talk of integrating this functionality into stock Android (there is even an empty packages/apps/SmartCardService directory in the AOSP tree), there is currently no standard way to communicate with the UICC SE through the RIL (some commercial devices with custom firmware are reported to support it though).

An alternative way to use the UICC as a SE is using the Single Wire Protocol (SWP) when the UICC is connected to a NFC controller that supports it. This is the case in the Nexus S, as well as the Galaxy Nexus, and while this functionality is supported by the NFC controller drivers, it is disabled by default. This is however a software limitation, and people have managed to patch AOSP source to get around it and successfully communicate with UICC. This has the greatest potential to become part of stock Android, however, as of the current release (4.1.1), it is still not available. 

Another form factor for an SE is an Advanced Security SD card (ASSD), which is basically an SD card with an embedded SE chip. When connected to an Android device with and SD card slot, running a SEEK-patched Android version, the SE can be accessed via the SmartCard API. However, Android devices with an SD card slot are becoming the exceptions rather than the norm, so it is unlikely that ASSD Android support will make it to the mainstream.

And finally, there is the embedded SE. As the name implies, an embedded SE is part of the device's mainboard, either as a dedicated chip or integrated with the NFC one, and is not removable. The first Android device to feature an embedded SE was the Nexus S, which also introduced NFC support to Android. Subsequent Nexus-branded devices, as well as other popular handsets have continued this trend. The device we'll use in our experiments, the Galaxy Nexus, is built with NXP's PN65N chip, which bundles a NFC radio controller and an SE (P5CN072, part of NXP's SmartMX series) in a single package (a diagram can be found here).

NFC and the Secure Element

NFC and the SE are tightly integrated in Android, and not only because they share the same silicon, so let's say a few words about NFC. NFC has three standard modes of operation: 
  • reader/writer (R/W) mode, allowing for accessing external NFC tags 
  • peer-to-peer (P2P) mode, allowing for data exchange between two NFC devices 
  • card emulation (CE) mode, which allows the device to emulate a traditional contactless smart card 
What can Android do in each of these modes? The R/W mode allows you to read NDEF tags and  contactless cards, such as some transport cards. While this is, of course, useful, it essential turns your phone into a glorified card reader. P2P mode has been the most demoed and marketed one, in the form of Android Beam. This is only cool the first couple of times though, and since the API only gives you higher-level access to the underlying P2P communication protocol, its applications are currently limited. CE was not available in the initial Gingerbread release, and was introduced later in order to support Google Wallet. This is the NFC mode with the greatest potential for real-life applications. It allows your phone to be programmed to emulate pretty much any physical contactless card, considerably slimming down your physical wallet in the process.

The embedded SE is connected to the NFC controller through a SignalIn/SignalOut Connection (S2C, standardized as NFC-WI) and has three modes of operation: off, wired and virtual mode. In off mode there is no communication with the SE. In wired mode the SE is visible to the Android OS as if it were a contactless smartcard connected to the RF reader. In virtual mode the SE is visible to external readers as if the phone were a contactless smartcard. These modes are naturally mutually exclusive, so we can communicate with the SE either via the contactless interface (e.g., from an external reader), or through the wired interface (e.g., from an Android app). This post will focus on using the wired mode to communicate with the SE from an app. Communicating via NFC is no different than reading a physical contactless card and we'll touch on it briefly in the last post of the series.

Accessing the embedded Secure Element

This is a lot of (useful?) information, but we still haven't answered the main question of this entry: how can we access the embedded SE? The bad news is that there is no public Android SDK API for this (yet). The good news is that accessing it in a standard and (somewhat) officially supported way is possible in current Android versions.

Card emulation, and consequently, internal APIs for accessing the embedded SE were introduced in Android 2.3.4, and that is the version Google Wallet launched on. Those APIs were, and remain, hidden from SDK applications. Additionally using them required system-level permissions (WRITE_SECURE_SETTINGS or NFCEE_ADMIN) in 2.3.4 and subsequent Gingerbread releases, as well as in the initial Ice Cream Sandwich release (4.0, API Level 14). What this means is that only Google (for Nexus) devices, and mobile vendors (for everything else) could distribute apps that use the SE, because they need to either be part of the core OS, or be signed with the platform keys, controlled by the respective vendor. Since the only app that made use of the SE was Google Wallet, which ran only on Nexus S (and initially on a single carrier), this was good enough. However, it made it impossible to develop and distribute an SE app without having it signed by the platform vendor. Android 4.0.4 (API Level 15) changed that by replacing the system-level permission requirement with signing certificate (aka, 'signature' in Android framework terms) whitelisting at the OS level. While this still requires modifying core OS files, and thus vendor cooperation, there is no need to sign SE applications with the vendor key, which greatly simplifies distribution. Additionally, since the whiltelist is maintained in a file, it can easily be updated using an OTA to add support for more SE applications.

In practice this is implemented by the NfceeAccessControl class and enforced by the system NfcService. NfceeAccessControl reads the whilelist from /etc/nfcee_access.xml which is an XML file that stores a list of signing certificates and package names that are allowed to access the SE. Access can be granted both to all apps signed by a particular certificate's private key (if no package is specified), or to a single package (app) only. Here's how the file looks like:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<signer android:signature="30820...90">
<package android:name="org.foo.nfc.app">
</package></signer>
</resources>

This would allow SE access to the 'org.foo.nfc.app' package, if it is signed by the specified signer. So the first step to getting our app to access the SE is adding its signing certificate and package name to the nfcee_access.xml file. This file resides on the system partition (/etc is symlinked to /system/etc), so we need root access in order to remount it read-write and modify the file. The stock file already has the Google Wallet certificate in it, so it is a good idea to start with that and add our own package, otherwise Google Wallet SE access would be disabled. The 'signature' attribute is a hex encoding of the signing certificate in DER format, which is a pity since that results in an excessively long string (a hash of the certificate would have sufficed) . We can either add a <debug/> element to the file, install it, try to access the SE and get the string we need to add from the access denied exception, or simplify the process a bit by preparing the string in advance. We can get the certificate bytes in hex format with a command like this:

$ keytool -exportcert -v -keystore my.keystore -alias my_signing_key \
-storepass password|xxd -p -|tr -d '\n'

This will print the hex string on a single line, so you might want to redirect it to a file for easier copying. Add a new <signer> element to the stock file, add your app's package name and the certificate hex string, and replace the original file in /etc/ (backups are always a good idea). You will also need to reboot the device for the changes to take effect, since file is only read when the NfcService starts.

As we said, there are no special permissions required to access the SE in ICS (4.0.3 and above) and Jelly Bean (4.1), so we only need to add the standard NFC permission to our app's manifest. However, the library that implements SE access is marked as optional, and to get it loaded for our app, we need to mark it as required in the manifest with the <uses-library> tag. The AndroidManifest.xml for the app should look something like this:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.foo.nfc.app"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="15"
android:targetSdkVersion="16" />

<uses-permission android:name="android.permission.NFC" />

<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/title_activity_main" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<uses-library
android:name="com.android.nfc_extras"
android:required="true" />
</application>
</manifest>

With the boilerplate out of the way it is finally time to actually access the SE API. Android doesn't currently implement a standard smart card communication API such as JSR 177 or the Open Mobile API, but instead offers a very basic communication interface in the NfcExecutionEnvironment (NFC-EE) class. It has only three public methods:

public class NfcExecutionEnvironment {
public void open() throws IOException {...}

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

public byte[] transceive(byte[] in) throws IOException {...}
}

This simple interface is sufficient to communicate with the SE, so now we just need to get access to an instance. This is available via a static method of the NfcAdapterExtras class which controls both card emulation route (currently only to the SE, since UICC support is not available) and NFC-EE management. So the full code to send a command to the SE becomes:

NfcAdapterExtras adapterExtras = NfcAdapterExtras.get(NfcAdapter.getDefaultAdapter(context));
NfcExecutionEnvironment nfceEe = adapterExtras.getEmbeddedExecutionEnvironment();
nfcEe.open();
byte[] response = nfcEe.transceive(command);
nfcEe.close();

As we mentioned earlier however, com.android.nfc_extras is an optional package and thus not part of the SDK. We can't import it directly, so we have to either build our app as part of the full Android source (by placing it in /packages/apps/), or resort to reflection. Since the SE interface is quite small, we opt for ease of building and testing, and will use reflection. The code to get, open and use an NFC-EE instance now degenerates to something like this:

Class nfcExtrasClazz = Class.forName("com.android.nfc_extras.NfcAdapterExtras");
Method getMethod = nfcExtrasClazz .getMethod("get", Class.forName("android.nfc.NfcAdapter"));
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(context);
Object nfcExtras = getMethod .invoke(nfcExtrasClazz, adapter);

Method getEEMethod = nfcExtras.getClass().getMethod("getEmbeddedExecutionEnvironment",
(Class[]) null);
Object ee = getEEMethod.invoke(nfcExtras , (Object[]) null);
Class eeClazz = se.getClass();
Method openMethod = eeClazz.getMethod("open", (Class[]) null);
Method transceiveMethod = ee.getClass().getMethod("transceive",
new Class[] { byte[].class });
Method closeMethod = eeClazz.getMethod("close", (Class[]) null);

openMethod.invoke(se, (Object[]) null);
Object response = transceiveMethod.invoke(se, command);
closeMethod.invoke(se, (Object[]) null);

We can of course wrap this up in a prettier package, and we will in the second part of the series. What is important to remember is to call close() when done, because wired access to the SE blocks contactless access while the NFC-EE is open. We should now have a working connection to the embedded SE and sending some bytes should produce a (error) response. Here's a first try:

D/SEConnection(27318): --> 00000000
D/SEConnection(27318): <-- 6E00


We'll explain what the response means and show how to send some actually meaningful commands in the second part of the article.

Summary

A secure element is a tamper resistant execution environment on a chip that can execute applications and store data in a secure manner. An SE is found on the UICC of every Android phone, but the platform currently doesn't allow access to it. Recent devices come with NFC support, which is often combined with an embedded secure element chip, usually in the same package. The embedded secure element can be accessed both externally via a NFC reader/writer (virtual mode) or internally via the NfcExecutionEnvironment API (wired mode). Access to the API is currently controlled by a system level whitelist of signing certificates and package names. Once an application is whitelisted, it can communicate with the SE without any other special permissions or restrictions.

Comments