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
SIM card applications
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.'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.Accessing the SIM card
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.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.// 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
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.
This entry was posted on at 8:30 AM and is filed under android security. You can follow any responses to this entry through the RSS 2.0. You can