Using the SIM card as a secure element in Android

Our last post introduced one of Android 4.3's more notable security features -- improved credential storage, and while there are a few other enhancements worth discussing, this post will slightly change direction. As mentioned previously, mobile devices can include some form of a Secure Element (SE), but a smart card based UICC (usually called just 'SIM card') is almost universally present. Virtually all SIM cards in use today are programmable and thus can be used as a SE. Continuing the topic of hardware-backed security, we will now look into how SIMs can be programmed and used to enhance the security of Android applications.

SIM cards

First, a few words about terminology: while the correct term for modern mobile devices is UICC (Universal Integrated Circuit Card), since the goal of this post is not to discuss the differences between mobile networks, we will usually call it a 'SIM card' and only make the distinction when necessary. 

So what is a SIM card? 'SIM' stands for Subscriber Identity Module and refers to a smart card that securely stores the subscriber identifier and the associated key used to identify and authenticate to a mobile network. It was originally used on GSM networks and standards were later extended to support 3G and LTE. Since SIMs are smart cards, they conform to ISO-7816 standards regarding physical characteristics and electrical interface. Originally they were the same size as 'regular' smart cards (Full-size, FF), but by far the most popular sizes nowadays are Mini-SIM (2FF) and Micro-SIM (3FF), with Nano-SIM (4FF) introduced in 2012. 

Of course, not every smart that fits in the SIM slot can be used in a mobile device, so the next question is: what makes a smart card a SIM card? Technically, it's conformance to mobile communication standards such 3GPP TS 11.11 and certification by the SIMalliance. In practice it is the ability to run an application that allows it to communicate with the phone (referred to as 'Mobile Equipment', ME, or 'Mobile Station', MS in related standards) and connect to a mobile network. While the original GSM standard did not make a  distinction between the physical smart card and the software required to connect to the mobile network, with the introduction of 3G standards, a clear distinction has been made. The physical smart card is referred to as Universal Integrated Circuit Card (UICC) and different mobile network applications than run on it have been defined: GSM, CSIM, USIM, ISIM, etc. A UICC can host and run more than one network application (hence 'universal'), and thus can be used to connect to different networks. While network application functionality depends on the specific mobile network, their core features are quite similar: store network parameters securely and identify to the network, as well as authenticate the user (optionally) and store user data. 

SIM card applications

Let's take GSM/3G as an example and briefly review how a network application works. For GSM the main network parameters are network identity (International Mobile Subscriber Identity, IMSI; tied to the SIM), phone number  (MSISDN, used for routing calls and changeable) and a shared network authentication key Ki. To connect to the network the MS needs to authenticate itself and negotiate a session key. Both authentication and session key derivation make use of Ki, which is also known to the network and looked up by IMSI. The MS sends a connection request and includes its IMSI, which the network uses to find the corresponding Ki. The network then uses the Ki to generate a challenge (RAND), expected challenge response (SRES) and session key Kc and sends RAND to the MS. Here's where the GSM application running on the SIM card comes into play: the MS passes the RAND to the SIM card, which in turn generates its own SRES and Kc. The SRES is sent to the network and if it matches the expected value, encrypted communication is established using the session key Kc. As you can see, the security of this protocol hinges solely on the secrecy of the Ki. Since all operations involving the Ki are implemented inside the SIM and it never comes with direct contact with neither the MS or the network, the scheme is kept reasonably secure. Of course, security depends on the encryption algorithms used as well, and major weaknesses that allow intercepted GSM calls to be decrypted using off-the shelf hardware were found in the original versions of the A3/A5 algorithms (which were initially secret). Jumping back to Android for a moment, all of this is implemented by the baseband software (more on this later) and network authentication is never directly visible to the main OS.

We've shown that SIM cards need to run applications, let's now say a few words about how those applications are implemented and installed. Initial smart cards were based on a file system model, where files (elementary files, EF) and directories (dedicated files, DF) were named with a two-byte identifier. Thus developing 'an application' consisted mostly of selecting an ID for the DF that hosts its files (called ADF), and specifying the formats and names of EFs that store data. For example, the GSM application is under the '7F20' ADF, and the USIM ADF hosts the EF_imsi, EF_keys, EF_sms, etc. files. Practically all SIMs used today are based on Java Card technology and implement GlobalPlatform card specifications. Thus all network applications are implemented as Java Card applets and emulate the legacy file-based structure for backward compatibility. Applets are installed according to GlobalPlatform specifications by authenticating to the Issuer Security Domain (Card Manager) and issuing LOAD and INSTALL commands.

One application management feature specific to SIM cards is support for OTA (Over-The-Air) updates via binary SMS. This functionality is not used by all carries, but it allows them to remotely install applets on SIM cards they have issued. OTA is implemented by wrapping card commands (APDUs) in SMS T-PDUs, which the ME forwards to the SIM (ETSI TS 102 226). In most SIMs this is actually the only way to load applets on the card, even during initial personalization. That is why most of the common GlobalPlatform-compliant tools cannot be used as is for managing SIMs. One needs to either use a tool that supports SIM OTA, such as the SIMalliance Loader, or implement APDU wrapping/unwrapping, including any necessary encryption and integrity algorithms (ETSI TS 102 225). Incidentally, problems with the implementation of those secured packets on some SIMs that use DES as the encryption and integrity algorithm have been used to crack OTA update keys. The major use of the OTA functionality is to install and maintain SIM Toolkit (STK) applications which can interact with the handset via standard 'proactive' (in reality implemented via polling) commands and display menus or even open Web pages and send SMS. While STK applications are almost unheard of in the US and Asia, they are still heavily used in some parts of Europe and Africa for anything from mobile banking to citizen authentication. Android also supports STK with a dedicated STK system app, which is automatically disabled if the SIM card has not STK applets installed.

Accessing the SIM card

As mentioned above, network related functionality is implemented by the baseband software and what can be done from Android is entirely dependent on what features the baseband exposes. Android supports STK applications, so it does have internal support for communicating to the SIM, but the OS security overview explicitly states that 'low level access to the SIM card is not available to third-party apps'. So how can we use it as an SE then? Some Android builds from major vendors, most notably Samsung, provide an implementation of the SIMalliance Open Mobile API on some handsets and an open source implementation (for compatible devices) is available from the SEEK for Android project. The Open Mobile API aims to provide a unified interface for accessing SEs on Android, including the SIM. To understand how the Open Mobile API works and the cause of its limitations, let's first review how access to the SIM card is implemented in Android.

On Android devices all mobile network functionality (dialing, sending SMS, etc.) is provided by the baseband processor (also referred to as 'modem' or 'radio'). Android applications and system services communicate to the baseband only indirectly via the Radio Interface Layer (RIL) daemon (rild). It in turn talks to the actual hardware by using a manufacturer-provided RIL HAL library, which wraps the proprietary interface the baseband provides. The SIM card is typically connected only to baseband processor (sometimes also to the NFC controller via SWP), and thus all communication needs to go through the RIL. While the proprietary RIL implementation can always access the SIM in order to perform network identification and authentication, as well as read/write contacts and access STK applications, support for transparent APDU exchange is not always available. The standard way to provide this feature is to use extended AT commands such AT+CSIM (Generic SIM access) and AT+CGLA (Generic UICC Logical Channel Access), as defined in 3GPP TS 27.007, but some vendors implement it using proprietary extensions, so support for the necessary AT commands does not automatically provide SIM access.

SEEK for Android provides patches that implement a resource manager service (SmartCardService) that can connect to any supported SE (embedded SE, ASSD or UICC) and extensions to the Android telephony framework that allow for transparent APDU exchange with the SIM. As mentioned above, access through the RIL is hardware and proprietary RIL library dependent, so you need both a compatible device and a build that includes the SmartCardService and related framework extensions. Thanks to some work by they u'smile project, UICC access on most variants of the popular Galaxy S2 and S3 handsets is available using a patched CyanogenMod build, so you can make use of the latest SEEK version. Even if you don't own one of those devices, you can use the SEEK emulator extension which lets you use a standard PC/SC smart card reader to connect a SIM to the Android emulator. Note that just any regular Java card won't work out of the box because the emulator will look for the GSM application and mark the card as not usable if it doesn't find one. You can modify it to skip those steps, but a simple solution is to install a dummy GSM application that always returns the expected responses.

Once you have managed to get a device or the emulator to talk to the SIM, using the OpenMobile API to send commands is quite straightforward:

// connect to the SE service, asynchronous
SEService seService = new SEService(this, this);
// list readers
Reader[] readers = seService.getReaders();
// assume the first one is SIM and open session
Session session = readers[0].openSession();
// open logical (or basic) channel
Channel channel = session.openLogicalChannel(aid);
// send APDU and get response
byte[] rapdu = channel.transmit(cmd);

You will need to request the org.simalliance.openmobileapi.SMARTCARD permission and add the org.simalliance.openmobileapi extension library to your manifest for this to work. See the official wiki for more details.

<manifest ...>

<uses-permission android:name="org.simalliance.openmobileapi.SMARTCARD" />

<application ...>
<uses-library
android:name="org.simalliance.openmobileapi"
android:required="true" />
...
</application>
</manifest>

SE-enabled Android applications

Now that we can connect to the SIM card from applications, what can we use it for? Just as regular smart cards, an SE can be used to store data and keys securely and perform cryptographic operations without keys having to leave the card. One of the usual applications of smart cards is to store RSA authentication keys and certificates that are used from anything from desktop logon to VPN or SSL authentication. This is typically implemented by providing some sort of middleware library, usually a standard cryptographic service provider (CSP) module that can plug into the system CSP or be loaded by a compatible application. As the Android security model does not allow system extensions provided by third party apps, in order to integrate with the system key management service, such middleware would need to be implemented as a keymaster module for the system credential store (keystore) and be bundled as a system library. This can be accomplished by building a custom ROM which installs our custom keymaster module, but we can also take advantage of the SE without rebuilding the whole system. The most straightforward way to do this is to implement the security critical part of an app inside the SE and have the app act as a client that only provides a user-facing GUI. One such application provided with the SEEK distribution is an SE-backed one-time password (OTP) Google Authenticator app. Since the critical part of OTP generators is the seed (usually a symmetric cryptographic key), they can easily be cloned once the seed is obtained or extracted. Thus OTP apps that store the seed in a regular file (like the official Google Authenticator app) provide little protection if the device OS is compromised. The SEEK GoogleOtpAuthenticator app both stores the seed and performs OTP generation inside the SE, making it impossible to recover the seed from the app data stored on the device.

Another type of popular application that could benefit from using an SE is a password manager. Password managers typically use a user-supplied passphrase to derive a symmetric key, which is in turn used to encrypt stored passwords. This makes it hard to recover stored passwords without knowing the passphrase, but naturally security level is totally dependent on its complexity. As usual, because typing a long string with rarely used characters on a mobile device is not a particularly pleasant experience, users tend to pick easier to type, low-entropy passphrases. If the key is stored in an SE, the passphrase can be skipped or replaced with a simpler PIN, making the password manager app both more user-friendly and secure. Let's see how such an SE-backed password manager can be implemented using a Java Card applet and the Open Mobile API.

DIY SIM password manager

Ideally, all key management and encryption logic should be implemented inside the SE and the client application would only provide input (plain text passwords) and retrieve opaque encrypted data. The SE applet should not only provide encryption, but also guarantee the integrity of encrypted data either by using an algorithm that provides authenticated encryption (which most smart card don't natively support currently) or by calculating a MAC over the encrypted data using HMAC or some similar mechanism. Smart cards typically provide some sort of encryption support, starting with DES/3DES for low-end models and going up to RSA and EC for top-of-the-line ones. Since public key cryptography is typically not needed for mobile network authentication or secure OTA (which is based on symmetric algorithms), SIM cards rarely support RSA or EC. A reasonably secure symmetric and hash algorithm should be enough to implement a simple password manager though, so in theory we should be able to use even a lower-end SIM.

As mentioned in the previous section, all recent SIM cards are based on Java Card technology, and it is possible to develop and load a custom applet, provided one has access to the Card Manager or OTA keys. Those are naturally not available for commercial MNO SIMs, so we would need to use a blank 'programmable' SIM that allows for loading applets without authentication or comes bundled with the required keys. Those are quite hard, but not impossible to come by, so let's see how such a password manager applet could be implemented. We won't discuss the basics of Java Card programming, but jump straight to the implementation. Refer to the offical documentation, or a tutorial if you need an introduction.

The Java Card API provides a subset of the JCA classes, with an interface optimized towards using pre-allocated, shared byte arrays, which is typical on a memory constrained platform such as a smart card. A basic encryption example would look something like this:

byte[] buff = apdu.getBuffer();
//..
DESKey deskey = (DESKey)KeyBuilder.buildKey(KeyBuilder.TYPE_DES_TRANSIENT_DESELECT,
KeyBuilder.LENGTH_DES3_2KEY, false);
deskey.setKey(keyBytes, (short)0);
Cipher cipher = Cipher.getInstance(Cipher.ALG_DES_CBC_PKCS5, false);
cipher.init(deskey, Cipher.MODE_ENCRYPT);
cipher.doFinal(data, (short) 0, (short) data.length,
buff, (short) 0);

As you can see, a dedicated key object, that is automatically cleared when the applet is deselected, is first created and then used to initialize a Cipher instance. Besides the unwieldy number of casts to short (necessary because 'classic' Java Card does not support int, but it is still the default integer type) the code is very similar to what you would find in a Java SE or Android application. Hashing uses the MessageDigest class and follows a similar routine. Using the system-provided Cipher and MessageDigest classes as building blocks it is fairly straightforward to implement CBC mode encryption and HMAC for data integrity. However as it happens, our low end SIM card does not provide usable implementations of those classes (even though the spec sheet claims they do), so we would need to start from scratch. Fortunately, since Java cards can execute arbitrary programs (as long as they fit in memory), it is also possible to include our own encryption algorithm implementation in the applet. Even better, a Java Card optimized AES implementation is freely available. This implementation provides only the basic pieces of AES -- key schedule generation and single block encryption, so some additional work is required to match the Java Cipher class functionality. The bigger downside is that by using an algorithm implemented in software we cannot take advantage of the specialized crypto co-processor most smart cards have. With this implementation our SIM (8-bit CPU, 6KB RAM) card takes about 2 seconds to process a single AES block with a 128-bit key. The performance can be improved slightly by reducing the number of AES round to 7 (10 are recommended for 128-bit keys), but that will both lower the security level of the system and result in an non-standard cipher, making testing more difficult. Another disadvantage is that native key objects are usually stored in a secured memory area that is better protected from side channel attacks, but by using our own cipher we are forced to store keys in regular byte arrays. With those caveats, this AES implementation should give us what we need for our demo application. Using the JavaCardAES class as a building block, our AES CBC encryption routine would look something like this:

aesCipher.RoundKeysSchedule(keyBytes, (short) 0, roundKeysBuff);
short padSize = addPadding(cipherBuff, offset, len);
short paddedLen = (short) (len + padSize);
short blocks = (short) (paddedLen / AES_BLOCK_LEN);

for (short i = 0; i < blocks; i++) {
short cipherOffset = (short) (i * AES_BLOCK_LEN);
for (short j = 0; j < AES_BLOCK_LEN; j++) {
cbcV[j] ^= cipherBuff[(short) (cipherOffset + j)];
}
aesCipher.AESEncryptBlock(cbcV, OFFSET_ZERO, roundKeysBuff);
Util.arrayCopyNonAtomic(cbcV, OFFSET_ZERO, cipherBuff,
cipherOffset, AES_BLOCK_LEN);
}

Not as concise as using the system crypto classes, but gets the job done. Finally (not shown), the IV and cipher text are copied to the APDU buffer and sent back to the caller. Decryption follows a similar pattern. One thing that is obviously missing is the MAC, but as it turns out a hash algorithm implemented in software is prohibitively slow on our SIM (mostly because it needs to access large tables stored in the slow card EEPROM). While a MAC can be also implemented using the AES primitive, we have omitted it from the sample applet. In practice tampering with the cipher text of encrypted passwords would only result in incorrect passwords, but it is still a good idea to use a MAC when implementing this on a fully functional Java Card.

Our applet can now perform encryption and decryption, but one critical piece is still missing -- a random number generator. The Java Card API has the RandomData class which is typically used to generate key material and IVs for cryptographic operations, but just as with the Cipher class it is not available on our SIM. Therefore, unfortunately, we need to apply the DIY approach again. To keep things simple and with a (somewhat) reasonable response time, we implement a simple pseudo random number generator (PRNG) based on AES in counter mode. As mentioned above, the largest integer type in classic Java Card is short, so the counter will wrap as soon as it goes over 32767. While this can be overcome fairly easily by using a persistent byte array to simulate a long (or BigInteger if you are more ambitious), the bigger problem is that there is no suitable source of entropy on the smart card that we can use to seed the PRNG. Therefore the PRNG AES key and nonce need to be specified at applet install time and be unique to each SIM. Our simplistic PRNG implementation based on the JavaCardAES class is shown below (buff is the output buffer):

Util.arrayCopyNonAtomic(prngNonce, OFFSET_ZERO, cipherBuff,
OFFSET_ZERO, (short) prngNonce.length);
Util.setShort(cipherBuff, (short) (AES_BLOCK_LEN - 2), prngCounter);

aesCipher.RoundKeysSchedule(prngKey, (short) 0, roundKeysBuff);
aeCipher.AESEncryptBlock(cipherBuff, OFFSET_ZERO, roundKeysBuff);
prngCounter++;

Util.arrayCopyNonAtomic(cipherBuff, OFFSET_ZERO, buff, offset, len);

The recent Bitcoin app problems traced to a repeatable PRNG in Android, controversy around the Dual_EC_DRBG PRNG algorithm, which is both believed to be weak by design and is used by default in popular crypto toolkits and finally the low-quality hardware RNG found in FIPS certified smart cards have highlighted the critical impact a flawed PRNG can have on any system that uses cryptography. That is why a DIY PRNG is definitely not something you would like to use in a production system. Do find a SIM that provides working crypto classes and do use RandomData.ALG_SECURE_RANDOM to initialize the PRNG (that won't help much if the card's hardware RNG is flawed, of course).

With that we have all the pieces needed to implement the password manager applet, and what is left is to define and expose a public interface. For Java Card this means defining the values of the CLA and INS bytes the applet can process. Besides the obviously required encrypt and decrypt commands, we also provide commands to get the current state, initialize and clear the applet.

static final byte CLA = (byte) 0x80;
static final byte INS_GET_STATUS = (byte) 0x1;
static final byte INS_GEN_RANDOM = (byte) 0x2;
static final byte INS_GEN_KEY = (byte) 0x03;
static final byte INS_ENCRYPT = (byte) 0x4;
static final byte INS_DECRYPT = (byte) 0x5;
static final byte INS_CLEAR = (byte) 0x6;

Once we have a working applet, implementing the Android client is fairly straightforward. We need to connect to the SEService, open a logical channel to our applet (AID: 73 69 6d 70 61 73 73 6d 61 6e 01) and send the appropriate APDUs using the protocol outlined above. For example, sending a string to be encrypted requires the following code (assuming we already have an open Session to the SE). Here 0x9000 is the standard ISO 7816-3/4 success status word (SW):

Channel channel = session.openLogicalChannel(fromHex("73 69 6d 70 61 73 73 6d 61 6e 01"));
byte[] data = "password".getBytes("ASCII");
String cmdStr = "80 04 00 00 " + String.format("%02x", data.length)
+ toHex(data) + "00";
byte[] rapdu = channel.transmit(fromHex(cmdStr));
short sw = (short) ((rapdu [rapdu.length - 2] << 8) | (0xff & rapdu [rapdu.length - 1]));
if (sw != (short)0x9000) {
// handle error
}
byte[] ciphertext = Arrays.copyOf(rapdu, rapdu.length - 2);
String encrypted= Base64.encodeToString(ciphertext, Base64.NO_WRAP);

Besides calling applet operations by sending commands to the SE, the sample Android app also has a simple database to store encrypted passwords paired with a description, and displays currently managed passwords in a list view. Long pressing on the password name will bring up a contextual action that allows you to decrypt and temporarily display the password so you can copy it and paste it into the target application. The current implementation does not require a PIN to decrypt passwords, but one can easily by provided using Java Card's OwnerPIN class, optionally disabling the applet once a number of incorrect tries is reached. While this app can hardly compete with popular password managers, it has enough functionality to both illustrate the concept of an SE-backed app and be practially useful. Passwords can be added by pressing the '+' action item and the delete item clears the encryption key and PRNG counter, but not the PRNG seed and nonce. A screenshot of the award-winning UI is shown below. Full source code for both the applet and the Android app is available on Github.


Summary

The AOSP version of Android does not provide a standard API to use the SIM card as a SE, but many vendors do, and as long as the device baseband and RIL support APDU exchange, one can be added by using the SEEK for Android patches. This allows to improve the security of Android apps by using the SIM as a secure element and both store sensitive data and implement critical functionality inside it. Commercial SIM do not allow for installing arbitrary user applications, but applets can be automatically loaded by the carrier using the SIM OTA mechanism and apps that take advantage of those applets can be distributed through regular channels, such as the Play Store.

Thanks to Michael for developing the Galaxy S2/3 RIL patch and helping with getting it to work on my somewhat exotic S2.

Comments