Chaps is a PKCS #11 implementation that provides trusted platform module (TPM) backed cryptographic services. It replaces openCryptoki as the provider of TPM-backed cryptographic services on Chrome OS. It aims to improve speed and reliability of cryptographic token operations as well as to provide a simpler and more flexible codebase for future enhancements. Chaps works with a TCG Software Stack (TSS). On Chrome OS the TrouSerS TSS implementation is used, but Chaps is not limited to working with TrouSerS. The name “Chaps” has no real significance other than its fitness as a name for a layer above TrouSerS.
The following diagram illustrates where Chaps fits in the cryptographic services stack.
The motivation for creating Chaps and phasing out openCryptoki is threefold:
This document is for engineers, system integrators, and security analysts interested in the details of Chrome OS security.
The following resources provide useful background to this document:
Chaps has two major components:
Chaps reports two slots: (1) a system slot (not currently enabled) and (2) a user slot. The system slot will always have a token available. The user slot will have a token available while a user is logged in. chapsd receives login/logout notifications from cryptohome. The login notification must provide the user password (or some password-derived value) that will be used as authorization data for TPM-protected keys. Cryptohome derives the authorization data from the user password and a user-and-system-specific salt value using the scrypt key derivation scheme.
This diagram shows the Chaps high-level architecture:
All IPC with chapsd is performed using D-Bus. Both the Chaps client and daemon use dbus-c++ to generate proxy and adaptor classes.
Object data is stored in /var/lib/chaps for the system token and in ~/.chaps for each user. The object database is implemented using LevelDB.
The TrouSers library is used to access TPM services from the TSPI layer.
Unit tests for Chaps use the gtest and gmock frameworks. End-to-end tests use the autotest framework.
In Phase 1 all TPM interaction is still controlled by openCryptoki. The introduction of chapsd does not change TPM interaction in any way.
All RSA private keys with the attribute CKA_TOKEN set to TRUE will be bound to the TPM (wrapped by the Storage Root Key) and will not be extractable. All other private data and non-RSA keys are stored encrypted by the User Encryption Key which is protected by the TPM’s SRK but is extractable. This diagram depicts the key hierarchy:
Authorization data for the User Authorization Key is derived from the user password using the scrypt scheme. Salt is stored in a root-accessible file in the user’s home directory and is unique per user account per system. This ensures that authorization data for the same user on two different systems is different. The scrypt parameters are tuned to perform the computation in about 100ms.
All other authorization data is randomly generated and stored encrypted with the User Encryption Key. This authorization data is not directly encrypted with the User Authorization Key for performance reasons. This does not reduce security since any attack on the key would require access to the TPM (and so decryption of authorization data by the TPM would be possible).
Existing TPM-protected keys cannot be migrated to this hierarchy. The existing hierarchy is supported so that existing keys can continue to be used. All other data including meta-data associated with TPM-protected keys that was previously stored in the openCryptoki object store can be migrated to the new persistent object store. This migration will take place when valid openCryptoki object data is found in a user’s home directory and migration has not previously taken place.
In the future, simultaneous multiple user logins may need to be supported, and this design must accommodate this scenario. Each slot maintains a distinct object database, a distinct User Authorization Key and User Encryption Key, and uses a distinct password. Currently, only the “system” and “user” slots exist, but adding a new user would be as simple as adding another slot.
The key hierarchy for Phase 3 is the same as for Phase 2. The difference is that in Phase 3 some RSA keys can be protected by the User Encryption Key rather than being directly TPM-protected. Keys that require higher performance and do not require the high security of direct TPM binding are candidates for this. There is no migration of existing RSA keys because they are not extractable.
Chaps is implemented in C++ and makes wide use of mockable interfaces. This structure allows for a high level of unit test coverage. In general, all classes inherit from an interface class, and when one class interacts with another, it references the interface, not the implementation class.
All marshalling is performed by dbus-c++ with the exception of template arguments (that is, CK_ATTRIBUTE lists), which are serialized using the protobuf library.
LevelDB is not relational, so a relational schema is not used. Objects are serialized and assigned a unique identifier. The keys in LevelDB are the object identifiers, and the values are serialized object blobs.
Phase 1 deployment has the following goals:
The following ordered deployment plan meets these goals:
Phase 2 deployment has the following goals:
The following deployment plan meets these goals:
The Chaps code resides in a Chromium OS git repository at http://git.chromium.org/gitweb/?p=chromiumos/platform/chaps.git;a=summary.
The Chaps library returns the following strings which may need to be translated. It is assumed that manufacturer ID strings do not require translation. If a string does not appear in the Chrome OS user interface, it may not require translation.
The following table lists some threats that are mitigated by the Chaps system and provides details on how the threat is mitigated.
Threat Mitigation Attacker gains access to a user’s cryptographic token services (for example, creates an unauthorized digital signature). chapsd only allows connections from authorized uids (chronos, root) using D-Bus policies. Attacker owns D-Bus identifier and spoofs cryptographic services. D-Bus policy only allows the “chaps” user to own the chapsd identifier. Attacker gains access to private data and keys by accessing run-time memory of the chapsd process. chapsd runs as the “chaps” user. Attacker gains access to private data and keys by accessing persistent data stored by chapsd. All private data and keys stored on disk are encrypted with the User Encryption Key and stored in the cryptohome partition. Attacker gains access to a user’s TPM-protected resources. TPM-protected resources are authorized by a secret derived from the user’s password and a root-accessible random salt value. Attacker stages Chaps persistent data in order to leverage a vulnerability in chapsd to gain control of chapsd. Chaps persistent data is modifiable only by the “chaps” user (The data needs to be stored in a root-owned directory for this policy to be effective. See crosbug.com/30211).
If a user is logged in and an attacker gains chapsd privilege, then nothing prevents this attacker from reading all private data and keys that are not TPM-protected and non-extractable or from creating digital signatures using TPM-protected, non-extractable keys. The following are TPM-protected:
All objects with CKA_PRIVATE=TRUE are encrypted on disk with a TPM-protected key, even if they are not themselves TPM-protected. The following are not themselves TPM-protected (other than the on-disk encryption):
Suppose you have a certificate with an associated private key used to negotiate 802.1x and/or VPN connections. The following PKCS #11 objects would exist in the Chaps database:
The following information would be in chapsd process memory while the token is loaded:
This section lists each way in which Chaps uses cryptography and gives details as to how it is used. In all cases where cryptography is used in software, Chaps uses the openssl crypto library to provide the underlying algorithm implementations.
The openssl cryptographically strong pseudo-random-number-generator (PRNG) is used as a source of random numbers. It is seeded using the default openssl seeding mechanism (/dev/urandom on Chrome OS) in combination with 128 random bytes from the TPM. This seeding occurs only once per chapsd process.
Chaps supports a number of mechanisms in software that are not supported by the TPM (for example, SHA, HMAC, AES). The implementation of these mechanisms pass through directly to openssl, including padding and cipher modes, with the following exceptions:
Private object blobs are encrypted using AES-256-CBC with PKCS padding. An initialization vector (IV) is randomly generated for each encryption operation; it is appended to the encrypted blob. An HMAC-SHA512 mac is appended to each encrypted blob. This mac is verified before the decryption is attempted. Verification uses a version of memcmp that reads and compares all bytes regardless of where a mismatch is found.
Once authorization data is derived from the user password or similar using scrypt, it is processed as follows:
All errors are logged using the facility provided by base/logging.h.
Chaps is tested using both unit and integration tests. All unit tests are run from the ebuild test phase. The integration tests can be run manually or with the autotest framework.
The classes generated by dbus-c++ are not suitable for stubbing / mocking for two reasons:
A C++ interface is defined (ChapsInterface) that is very close to the generated proxy and adaptor interfaces but suitable for using and mocking in a test environment.
The client-side code is unit tested by using a mock proxy and calling the PKCS #11 C interface. Because the code being tested is called by external code, all argument validation and internal state validation is tested thoroughly resulting in a high level of code coverage.
Phase 1: There are no unit tests per se for the chapsd code in Phase 1 because each layer is either tightly coupled with D-Bus or tightly coupled with openCryptoki. The chapsd code is tested by a suite that verifies basic functionality of ChapsInterface methods. This test suite is then run on both a ChapsServiceRedirect instance and on a ChapsProxyImpl instance. These tests require a configured openCryptoki and, in the ChapsProxyImpl case, a running chapsd instance. Because of this requirement, the tests must be run on a live system, and they do not run as part of the ebuild test phase.
Phase 2: The tests from Phase 1 are reused and are run on a ChapsServiceImpl instance and a ChapsProxyImpl instance. In the case of ChapsServiceImpl, a mock persistence object can be used, and the suite can run as a unit test. In the case of ChapsProxyImpl, the suite is run as an integration test and requires a running chapsd instance as in Phase 1. In addition to this, unit tests which are specific to a single class are run for the following classes:
For each of these unit tests, any references to other objects are mocks. The following mocks are available: