Jelly Bean hardware-backed credential storage
Along with all the user facing new features everyone is talking about, the latest Android release has quite a bit of security improvements under the hood. Of those only app encryption has been properly announced, while the rest remain mostly covered up by upper level APIs. This, of course, is not fair, so let's call them up (the list is probably not exhaustive):
Galaxy Nexus
This produces key files in the
As you can see from the table above, Bouncy Castle and OpensSSL perform about the same, while the TEE takes more time to generate keys (most probably because it's using a hardware RNG, not a PRNG), but signing is about 3 times faster compared to the software implementations. Verification takes about the same time as signing, and is slower than software. It should be noted that this test is not exactly precise: calling the token TA via the
- RSA and DSA key generation and signatures are now implemented in native code for better performance
- TLS v1.2 support
- improved system key store
- new OpenSSL interface (engine) to the system key store
- new key management HAL component --
keymaster
- hardware-backed
keymaster
implementation on Galaxy Nexus and Nexus 7
The first two features are mostly self-explanatory, but the rest merit some exploration. Let's look into each one in turn.
System key store improvements
As we have already discussed, the system key store in Android is provided by a native daemon that encrypts secrets using a key derived from the device unlock password, stores them on disk and regulates key access based on UID. In ICS and previous versions, the
keystore
daemon simply stores opaque encrypted blobs and the only meatdata available (UID of owner and key name) was encoded in the file name under which blobs are stored. In Jelly Bean (JB), blobs also have a version field and a type field. The following key types are newly defined:TYPE_GENERIC
TYPE_MASTER_KEY
TYPE_KEY_PAIR
TYPE_GENERIC
is used for key blobs saved using the previous get/put interface, and TYPE_MASTER_KEY
is, of course, only used for the key store master key. The newly added TYPE_KEY_PAIR
is used for key blobs created using the new GENERATE
and IMPORT
commands. Before we go into more details, here are the keystore
commands added in Jelly Bean:GENERATE
IMPORT
SIGN
VERIFY
GET_PUBKEY
DEL_KEY
GRANT
UNGRANT
In order to use a key stored using the pre-JB implementation, we needed to first export the raw key bytes, and then use them to initialize an actual key object. Thus even though the key blob is encrypted on disk, the plain text key eventually needed to be exposed (in memory). The new commands let us generate an RSA key pair and sign or verify data without the key ever leaving the key store. There is however no way to specify key size for generated keys, it is fixed at 2048 bits. There is no restriction for importing keys though, so shorter (or longer keys) can be used as well (confirmed for 512-4096 bit keys). Importing requires that keys are encoded using the PKCS#8 format. The sign operation doesn't do any automatic padding and therefore requires input data to be equal to the RSA key size (it's essentially performs raw RSA encryption using the private key).
VERIFY
takes the key name, signed data and signature value as input, and outputs the verification result. GET_PUBKEY
works as expected -- it returns the public key in X.509 format. As mentioned above, the keystore
daemon does access control based on UID, and pre-JB a process could use only a key it had created itself. The new GRANT
/ UNGRANT
commands allow the OS to temporarily allow access to system keys to other processes. The grants are not persisted, so they are lost on restart.Key store OpenSSL engine
The next addition to Android's security system is the keystore-backed OpenSSL engine (pluggable cryptographic module). It only supports loading of and signing with RSA private keys, but that is usually enough to implement key-based authentication (such as SSL client authentication). This small engine makes it possible for native code that uses OpenSSL APIs to use private keys saved in the system key store without any code modifications. It also has a Java wrapper (
And finally, time for our feature presentation -- the OpenSSLEngine
), which is used to implement the KeyChain.getPrivateKey()
API. Thus all apps that acquire a private key reference via the KeyChain
API get the benefit of using the new native implementation.keymaster
module overview
keymaster
module and its hardware-based implementation on Galaxy Nexus (and Nexus 7, but that currently has no relevant source code in AOSP, so we will focus on the GN). Jelly Bean introduces a new libhardware
(aka HAL) module, called keymaster
. It defines structures and methods for generating keys and signing/verifying data. The keymaster
module is meant to decouple Android from the actual device security hardware, and a typical implementation would use a vendor-provided library to communicate with the crypto-enabled hardware. Jelly Bean comes with a default softkeymaster
module that does all key operations in software only (using the ubiquitous OpenSSL). It is used on the emulator and probably will be included in devices that lack dedicated cryptographic hardware. The currently defined operations are listed below. Only RSA is supported at present.generate_keypair
import_keypair
sign_data
verify_data
get_keypair_public
delete_keypair
delete_all
If those look familiar, this is because they are pretty much the same as the newly added
keystore
commands listed in the previous section. All of the asymmetric key operations exposed by the keystore
daemon are implemented by calling the system keymaster
module. Thus if the keymaster
HAL module is backed by a hardware cryptographic device, all upper level commands and APIs that use the keystore
daemon interface automatically get to use hardware crypto.Galaxy Nexus keymaster
implementation
Let's look at how this is implemented on Galaxy Nexus, starting from the lowest level, the actual hardware. Galaxy Nexus is built using the Texas Instruments OMAP4460 SoC, which integrates TI's M-Shield (not to be confused with nShield) mobile security technology. Among other things, M-Shield provides cryptographic acceleration, a secure random number generator and secure on-chip key storage. On top of that sits TI's Security Middleware Component (SMC), which is essentially a Trusted Execution Environment (TEE, Global Platform specs and white paper) implementation. The actual software is by Trusted Logic Mobility, marketed under the name Trusted Foundations. Looking at this TI white paper, it looks like secure key storage was planned for ICS (Android 4.0), but apparently, it got pushed to back to Jelly Bean (4.1). Cf. this statement from the white paper: 'Android 4.0 also introduces a new keychain API and underlying encrypted storage that are protected by M-Shield hardware security on the OMAP 4 platform.'.
With all the buzzwords and abbreviations out of the way, let's say a few words about TEE. As the name implies, TEE is defined as a logical execution environment, separate from the device's main OS, referred to as the REE (Rich Execution Environment). Its purpose is both to protect assets and execute trusted code. It is also required to be protected against certain physical attacks, although the level of protection is typically lower that that of a tamper-resistant module such as a Secure Element (SE). The TEE can host trusted applications (TAs) which utilize the TEE's services via the standardized internal APIs. Those fall under 4 categories:
- trusted storage
- cryptographic operations
- time-related
- arithmetical (for dealing with big numbers)
Applications running in the REE (the Android OS and apps) can only communicate with TAs via a low level Client API (essentially sending commands and receiving responses synchronously, where the protocol is defined by each TA). The Client API also lets the REE and TA applications share memory in a controlled manner for efficient data transfer.
Finally, let's see how all this is tied together in the GN build of Jelly Bean. A generic PKCS#11 module (
libtf_crypto_sst.so
) uses the TEE Client API to communicate with a TA that implements hashing, key generation, encryption/decryption, signing/verification and random number generation. Since there doesn't seem to a 'official' name for the TA on the Galaxy Nexus, and its commands map pretty much one-to-one to PKCS#11 interfaces, we will be calling it the 'token TA' from now on. The GN keymaster
HAL module calls the PKCS#11 module to implement RSA key pair generation and import, as well as signing and verification. This in turn is used by the keystore
daemon to implement the corresponding commands.However, it turns out that the hardware-backed
keymaster
module is not in the latest GN build (JRO03C
at the time of this writing. Update: according to this commit message, the reason for its being removed is that it has a power usage bug). Fortunately it is quite easy to build it and install it on the device (notice that the keymaster
module, for whatever reason, is actually called keystore.so
):$ make -j8 keystore.tuna $ adb push out/product/maguro/system/lib/hw/keystore.tuna.so /mnt/sdcard $ adb shell $ su # mount -o remount,rw /system # cp /mnt/sdcard/keystore.tuna.so /system/lib/hw
Then all we need to do is reboot the device to have it load the new module (otherwise it will continue to use the software-only
keystore.default.so
). If we send a few keystore
commands, we see the following output (maybe a bit too verbose for a production device), confirming that cryptographic operations are actually executed by the TEE:V/TEEKeyMaster( 299): Opening subsession 0x414f2a88 V/TEEKeyMaster( 299): public handle = 0x60011, private handle = 0x60021 V/TEEKeyMaster( 299): Closing object handle 0x60021 V/TEEKeyMaster( 299): Closing object handle 0x60011 V/TEEKeyMaster( 299): Closing subsession 0x414f2a88: 0x0 I/keystore( 299): uid: 10164 action: a -> 1 state: 1 -> 1 retry: 4 V/TEEKeyMaster( 299): tee_sign_data(0x414ea008, 0xbea018fc, 36, 0xbea1195c, 256, 0xbea018c4, 0xbea018c8) V/TEEKeyMaster( 299): Opening subsession 0x414f2ab8 V/TEEKeyMaster( 299): Found 1 object 0x60011 : class 0x2 V/TEEKeyMaster( 299): Found 1 object 0x60021 : class 0x3 V/TEEKeyMaster( 299): public handle = 0x60011, private handle = 0x60021 V/TEEKeyMaster( 299): tee_sign_data(0x414ea008, 0xbea018fc, 36, 0xbea1195c, 256, 0xbea018c4, 0xbea018c8) => 0x414f2838 size 256 V/TEEKeyMaster( 299): Closing object handle 0x60021 V/TEEKeyMaster( 299): Closing object handle 0x60011 V/TEEKeyMaster( 299): Closing subsession 0x414f2ab8: 0x0 I/keystore( 299): uid: 10164 action: n -> 1 state: 1 -> 1 retry: 4
This produces key files in the
keystore
daemon data directory, bus as you can see in the listing below, they are not large enough to store 2048 bit RSA keys. They only store a key identifier, as returned by the underlying PKCS#11 module. Keys are loaded based on this ID, and signing are verification are preformed within the token TA, without the keys being exported to the REE. # ls -l /data/misc/keystore/10164* -rw------- keystore keystore 84 2012-07-12 14:15 10164_foobar -rw------- keystore keystore 84 2012-07-12 14:15 10164_imported
So where are the actual keys? It turns out they are in the
Currently installing a PKCS#12 packaged key and certificate via the public /data/smc/user.bin
file. The format is, of course, proprietary, but it would be a safe bet that it is encrypted with a key stored on the SoC (or at least somehow protected by a hardware key). This allows to have practically an unlimited number of keys inside the TEE, without being bounded by the limited storage space on the physical chip.keymaster
usage and performance
KeyChain
API (or importing via Settings->Security->Insall from storage) will import the private key into the token TA and getting a private key object using KeyChain.getPrivateKey()
will return a reference to the stored key. Subsequent signature operations using this key object will be performed by the token TA and take advantage of the OMAP4 chip's cryptographic hardware. There are currently no public APIs or stock applications that use the generate key functionality, but if you want to generate a key protected by the token TA, you can call android.security.KeyStore.generate()
directly (via reflection or by duplicating the class in your project). This API can potentially be used for things like generating a CSR request from a browser and other types of PKI enrollment.The OMAP4 chip is advertised as having hardware accelerated cryptographic operations, so let's see how RSA key generation, signing and verification measure up against the default Android software implementations:
Crypto Provider/Operation | Key generation | Signing | Verification |
---|---|---|---|
Bouncy Castle | 2176.20 [ms] | 34.60 [ms] | 1.90 [ms] |
OpenSSL | 2467.40 [ms] | 29.80 [ms] | 1.00 [ms] |
TEE | 3487.00 [ms] | 10.90 [ms] | 10.60 [ms] |
As you can see from the table above, Bouncy Castle and OpensSSL perform about the same, while the TEE takes more time to generate keys (most probably because it's using a hardware RNG, not a PRNG), but signing is about 3 times faster compared to the software implementations. Verification takes about the same time as signing, and is slower than software. It should be noted that this test is not exactly precise: calling the token TA via the
keystore
daemon causes a lot of TEE client API sessions to be open and closed which has its overhead. Getting more accurate times will require benchmarking using the Client API directly, but the order of the results should be the same. Summary
To sum things up: Jelly Bean finally has a standard hardware key storage and cryptographic operations API in thekeymater
HAL module definition. The implementation for each device is hardware-dependent, and the currently available implementations use the TEE Client API on the Galaxy Nexus and Nexus 7 to take advantage of the TEE capabilities of the respective SoC (OMAP4 and Tegra 3). The current interface and implementation only support generating/importing of RSA keys and signing/verification, but will probably be extended in the future with more key types and operations. It is integrated with the system credential storage (managed by the keystore
daemon) and allows us to generate, import and use RSA keys protected by the devices's TEE from Android applications.
Comments
http://code.google.com/p/android/issues/detail?id=36545
and
http://stackoverflow.com/questions/11261774/using-android-4-1-keychain
For example, you say that the KeyChain.getPrivateKey() API method should automatically use the hardware-based keystores, but I'm finding (as one of the above links reports) that even calling KeyChain.getPrivateKey by itself will cause a segfault as soon as the VM garbage collects the returned key.
What is the canonical way to compute an RSA signature on 4.1 in the same way that this code used to work (but now segfaults) on 4.0:
privateKey = KeyChain.getPrivateKey(context,mAlias);
byte[] data;
Cipher rsasinger = javax.crypto.Cipher.getInstance("RSA/ECB/PKCS1PADDING");
rsasinger.init(Cipher.ENCRYPT_MODE, privkey);
byte[] signed_bytes = rsasinger.doFinal(data);
While I agree that there have been some bugs (key names with dashes not working, etc.), "RSA/ECB/PKCS1PADDING" is definitely not the 'canonical' way to compute an RSA signature. Use the Signature class, it should link to the proper lower implementation (OpenSSL or hardware keystore) correctly.
Haven't seen a segfault with stock JB, are you using some custom ROM?
The problem seems be here: KeyChain.getPrivateKey(). When I getEncoded method gives me a null value.
There is some other way to get Private Key from certificate p12?
Thanks in advance
http://developer.android.com/reference/android/security/KeyChain.html#isBoundKeyAlgorithm(java.lang.String)