blob: 2eeaf3e3e2cc9aa8594b0c979a4c329a353c4182 [file] [log] [blame] [view] [edit]
# Cryptohome Architecture
**Authors**: kerrnel@chromium.org, apronin@chromium.org
Cryptohome is one of Chrome OS’s largest and oldest code bases. It is
a gateway to user data on the system, authenticating user credentials,
deriving those credentials into file encryption keys, and mounting the
encrypted filesystem. This document describes the high level architectural
principles of cryptohome.
## Secret Wrapping on Disk
Users’ encryption secrets, such as filesystem encryption keys, are
wrapped by a combination of user input (for example, a password) and a
hardware security element (TPM, or GSC). These wrapped secrets must be
persisted to disk, and there are two formats which do this (both of which
are stored as protobufs).
### SerializedVaultKeyset
SerializedVaultKeysets are the soon to be deprecated, as of March
2021, format used to hold file encryption keys and other necessary
data (IVs, timestamps, etc). Some secrets stored there are repeated
in each SerializedVaultKeyset file. The primary reason to deprecate
SerializedVaultKeysets and replace them with UserSecretStash (defined
below) is seamless management of multiple credentials. Currently there is
a separate SerializedVaultKeyset file per credential, which has scaling
issues and makes it impossible for a user to update shared data duplicated
across all VaultKeyset files without knowing all credentials at once.
### UserSecretStash
The UserSecretStash (USS) is a future container of the user’s file system
keys. There is one USS per user, and one main key which encrypts the
USS. Each credential wraps the main key, as a series of intermediate keys,
which allows all credentials to modify the same filesystem keys and data.
## D-Bus API Concepts
Cryptohome exposes a D-Bus interface which callers, mainly Chrome, use
to interact with cryptohome. UserDataAuth is the name of the entire dbus
interface that cryptohome exposes, with AuthSessions and AuthFactors being
object oriented concepts implemented by the interface.
### UserDataAuth
UserDataAuth (UDA) is the dbus interface exposed by cryptohome. UDA was
rolled out in 2020 as a replacement of the old glib-dbus interface. UDA
allows cryptohome to use modern dbus bindings and event loops, and
uses protobufs for the message and reply data structures to simplify
versioning. The dbus messages consume and output byte arrays, which are
the serialized protobufs. In the past, changing the data fields passed
in messages and responses was time consuming and brittle as it had to be
changed in cryptohome daemon and all its clients, which have independent
releases. Now the dbus method always stays the same, and the protobuf
passed may change.
### AuthSessions
AuthSession is an API exposed by Cryptohome via UserDataAuth. The session
serves as a place for users to provide their credentials and request
operations such as mounting, which require the credentials. The session will
be established before any action that requires credential validation such
as mount, updating keys, adding keys, etc. The AuthSession is controlled by
states, and it starts by default in kAuthStatusFurtherFactorRequired. Each
AuthSession is identified with a base::UnguessableToken. Authsession works
in two steps to validate a set of credentials. These are:
#### StartAuthSession
StartAuthSession starts a new AuthSession with a given username. It
also starts a timer, after which the AuthSession becomes invalid.The
timer ensures that the credentials do not stay in the memory indefinitely.
### AuthFactors
AuthFactors abstract credentials to represent the many credentials used
for Chrome OS authentication. Historically passwords were used to sign
in, but in the future users may use: security keys, smart cards, PINs,
trusted phones, remotely stored escrow keys, etc.
AuthFactor is a purely virtual interface in C++. Each supported
authentication form will have a concrete implementation of the AuthFactor
class.
The default mapping from an authFactor to authBlock is determined by
cryptohome and may differ from OS revision to OS revision or from board
to board. E.g. a password authFactor can match to cryptohome_key-based
authBlock for TPM1.2 devices and old OS revisions, but to Pinweaver
authBlock for TPM2.0 devices and old OS revisions.
Once set, though, the authBlock used for a particular authFactor doesn't
change, even if the default mapping for the authFactors of that type
changes. Once set to a cryptohome_key authBlock, authMethod will continue
verifying user credentials that way, even if the default mapping for
"password" changes to Pinweaver in future OS versions. A migration to
Pinweaver authBlock would require a specific request from upper layers -
likely, creating a new authFactor and then deleting the old one.
## Internal Architecture
Cryptohome organzies code internally into object oriented interfaces to
support the myriad of code paths in a scalable manner. Two key interfaces
are AuthMethods and AuthBlocks.
### AuthMethods
AuthMethod is a specific combination of authFactors that may authenticate
a given user account.
As an example, based on enterprise policies or personal preferences,
a user account may be set up to allow authentication through either of
the following methods: (a) entering a password (1FA authMethod with
password-based authFactor); (b) inserting a smart card and verifying
the fingerprint (2FA authMethod with smartcard and FP authFactors); (c)
entering a PIN and verifying the fingerprint (2FA authMethod with PIN and
FP authFactors, where FP authFactor is the same as for method (b)).
When authMethod is created, specific authFactor(s) are selected for
it. I.e. even if multiple "password" authFactors exist for a user, an
authMethod will allow only one specific password from this known list. And
when an authMethod is included in a set (see below), only that specific
password will be allowed for authentication for the purposes covered by
the set. To avoid requiring the user to authenticate all factors when
credentials are created, the AuthFactor secrets must be stored in the USS.
#### Sets of authMethods
There are two sets of authMethods associated with a user account:
LoginMethodsSet (methods accepted during user login), and UnlockMethodsSet
(methods accepted for screen unlock for an already signed-in user). An
account may allow the same sets of authMethods for login and unlock, or have
different sets for them - e.g. requiring 2FA including password/PIN and FP
for login, but allowing any of the factors as a single factor for unlock:
LoginMethodsSet = {Password+FP, PIN+FP}, UnlockMethodsSet = {Password,
PIN, FP}. In practice, all authMethods are registered in UnlockMethodsSet.
### AuthBlocks
AuthBlock is a pure virtual C++ interface found in the file
auth_block.h. Each authentication method (password, security key, 2FA with
password and security key), has a concrete AuthBlock implementation. The
AuthBlock has two methods: Create() and Derive().
#### AuthBlockState
AuthBlockState contains per auth block metadata which is used to derive the
keys produced by the AuthBlock. An example of this metadata is the IVs. The
metadata is stored to disk in plaintext, output by the AuthBlock’s Create()
method, and loaded by the Derive() method.
#### Create()
Create() consumes an AuthFactor, which will reveal its underlying secret
to the AuthBlock. For example, SecurityKeyAuthFactor will talk to the
security key over DBUS, and ultimately present the AuthBlock with a high
entropy secret that can only be reconstructed with the physical security
key present. In a simpler case, PasswordAuthFactor will reveal the hashed
password sent from Chrome.
Create() then derives a high entropy key from the AuthFactor's secret,
and uses the derived key to wrap either the USS main key, or the
VaultKeyset. Create() outputs the high entropy keys in the KeyBlobs object,
which is secret and should never be persisted. Create() also outputs the
public metadata to re-create those keys in the AuthBlockState. AuthBlockState
is serialized to disk as a protobuf.
#### Derive()
Derive consumes an AuthBlockState instance, and generates the keys previously
created in Create(). For example, derive will run the user’s password
through scrypt, and then give it to the TPM to unseal the USS main
wrapping key.
## Secret Storage in Memory
Cryptohome should store user specific secrets, such as key material derived
from the password, in memory only when the user authenticates. As soon as
possible key material should be overwritten in memory and deallocated.
### brillo::SecureBlob
To support this property, cryptohome uses brillo::SecureBlob instead of
std::string or std::vector. SecureBlob uses an allocator that pins the pages to
physical memory, and overwrites the memory on deallocation, among other
properties.
### Note on Serialization Methods
Cryptohome must serialize objects to a byte array before encrypting them and
persisting to storage. Many serialization libraries, such as protobuf or
JSON parsers, will make their own heap allocations, copying class members
to intermediate objects. This will cancel any benefit of SecureBlob, so
cryptohome must use a serialization library that allows a custom allocator
to be specified. The current plan is to use Flatbuffers for future serialization
format, such as UserSecretStash and AuthBlockState. protobuf is currently used
for SerializedVaultKeyset and will remain in cryptohome until
SerializedVaultKeyset is removed.