1# zxcrypt
2
3__<font color=red>ALERT: zxcrypt is not secure until [ZX-1130][zx1130] is resolved!</font>__
4
5## Overview
6zxcrypt is a block device filter driver that transparently encrypts data being written to and
7decrypts being read from data a block device.  The underlying block device that a zxcrypt device
8uses may be almost any block device, including raw disks, ramdisks, GPT partitions, FVM partitions
9or even other zxcrypt devices.  The only restriction is that the block size be page-aligned.  Once
10bound, the zxcrypt device will publish another block device in the device tree that consumers can
11interact with normally.
12
13## Usage
14zxcrypt contains both a [driver](../system/dev/block/zxcrypt) and [library](../system/ulib/zxcrypt)
15Provided by libzxcrypt.so are four functions for managing zxcrypt devices.  Each takes one or more
16`zxcrypt_key_t` keys, which associates the key data, length, and slot in the case of multiple keys.
17* The __zxcrypt_format__ function takes an open block device, and writes the necessary encrypted
18  metadata to make it a zxcrypt device.  The zxcrypt key provided does not protect the data on the
19  device directly, but is used to protect the data key material.
20```c++
21zx_status_t zxcrypt_format(int fd, const zxcrypt_key_t* key);
22```
23* The __zxcrypt_bind__ function instructs the driver to read the encrypted metadata and extract the
24  data key material to use in transparently transforming I/O data.
25```c++
26zx_status_t zxcrypt_bind(int fd, const zxcrypt_key_t *key);
27```
28* The __zxcrypt_rekey__ function uses the old key to first read the encrypted metadata, and the new
29  key to write it back.
30```c++
31zx_status_t zxcrypt_rekey(int fd, const zxcrypt_key_t* old_key, const zxcrypt_key_t* new_key);
32```
33* The __zxcrypt_shred__ function first verifies that the caller can access the data by using the key
34  provided to read the encrypted metadata.  If this succeeded, it then destroys the encrypted
35  metadata containing the data key material.  This prevents any future access to the data.
36```c++
37zx_status_t zxcrypt_shred(int fd, const zxcrypt_key_t* key);
38```
39
40## Technical Details
41### DDKTL Driver
42zxcrypt is written as a DDKTL device driver.  [ulib/ddktl](../system/ulib/ddktl) is a C++ framework
43for writing drivers in Fuchsia.  It allows authors to automatically supply the
44[ulib/ddk](../system/ulib/ddk) function pointers and callbacks by using templatized mix-ins.  In the
45case of zxcrypt, the [device](../system/dev/block/zxcrypt/device.h) is "Ioctlable",
46"IotxnQueueable", "GetSizable", "Unbindable", and implements the methods listed in DDKTL's
47[BlockProtocol](../system/ulib/ddktl/include/ddktl/protocol/block.h).
48
49There are two small pieces of functionality which cannot be written in DDKTL and C++:
50* The driver binding logic, written using the C preprocessor macros of DDK's
51  [binding.h](../system/public/zircon/driver/binding.h).
52* The completion routines of [ulib/sync](../system/ulib/sync), which are used for synchronous I/O
53  and are incompatible with C++ atomics.
54
55### Worker Threads
56The device starts [worker threads](../system/dev/block/zxcrypt/worker.h) that run for the duration
57of the device and create a pipeline for all I/O requests.  Each has a type of I/O it operates on, a
58queue of incoming requests I/O that it will wait on, and a data cipher.  When a request is received,
59if the opcode matches the one it is looking for, it will use its cipher to transform the data in the
60request before passing it along.
61
62The overall pipeline is as shown:
63```
64DdkIotxnQueue -+
65                \       Worker 1:        Underlying      Worker 2:        Original
66    BlockRead ---+--->  Encrypter   --->   Block   --->  Decrypter  ---> Completion
67                /     Acts on writes       Device      Acts on reads      Callback
68   BlockWrite -+
69```
70
71The "encrypter" worker encrypts the data in every I/O write request before sending it to the
72underlying block device, and the "decrypter" worker decrypts the data in every I/O read response
73coming from the underlying block device.  The
74[cipher](../system/ulib/crypto/include/crypto/cipher.h) must have a key length of at least 16 bytes,
75be semantically secure ([IND-CCA2][ind-cca2]) and incorporate the block offset as a
76"[tweak][tweak]".  Currently, [AES256-XTS][aes-xts] is in use.
77
78### Rings and Txns
79In order to keep the encryption and decryption of data transparent to original I/O requester, the
80workers must copy the data when transforming it.  The I/O request sent through the pipeline is not
81actually the original request, but instead a "shadow" request that encapsulates the original
82request.  These shadows requests are managed by the [Txn](../system/dev/block/zxcrypt/txn.h) class,
83and allocated from the [Ring](../system/dev/block/zxcrypt/ring.h) class. The Ring is an enormous,
84yet very sparse, [VMO](concepts.md#shared-memory-virtual-memory-objects-vmos-).
85
86As shadow requests are needed, they are allocated backed sequentially by pages in the VMO.  When the
87worker needs to transform the data it either encrypts data from the original, encapsulated write
88request into the shadow request, or decrypts data from the shadow request into the original,
89encapsulated read request.  As soon as the original request can be handed back to the original
90requester, the shadow request is deallocated and its page [decommitted](syscalls/vmo_op_range.md).
91This ensures no more memory is used than is needed for outstanding I/O requests.
92
93### Superblock Format
94The key material for encrypting and decrypting the data is referred to as the data key, and is
95stored in a reserved portion of the device called the
96[superblock](../system/ulib/zxcrypt/include/zxcrypt/superblock.h).  The presence of this superblock
97is critical; without it, it is impossible to recreate the data key and recover the data on the
98device.  As a result, the superblock is copied to multiple locations on the device for redundancy.
99These locations are not visible to zxcrypt block device consumers.  Whenever the zxcrypt driver
100successfully reads and validates a superblock from one location, it will copy this to all other
101superblock locations to help "self-heal" any corrupted superblock locations.
102
103The superblock format is as follows, with each field described in turn:
104```
105+----------------+----------------+----+-----...-----+----...----+------...------+
106| Type GUID      | Instance GUID  |Vers| Sealed Key  | Reserved  | HMAC          |
107| 16 bytes       | 16 bytes       | 4B | Key size    |    ...    | Digest length |
108+----------------+----------------+----+-----...-----+----...----+------...------+
109```
110* _Type [GUID][guid]_: Identifies this as a zxcrypt device. Compatible with
111  [GPT](../system/ulib/gpt/include/gpt/gpt.h).
112* _Instance GUID_: Per-device identifier, used as the KDF salt as explained below.
113* _Version_: Used to indicate which cryptographic algorithms to use.
114* _Sealed Key_: The data key, encrypted by the wrap key as describer below.
115* _Reserved_: Unused data to align the superblock with the block boundary.
116* [_HMAC_][hmac]: A keyed digest of the superblock up to this point (including the Reserved field).
117
118The wrap key, wrap [IV][iv], and HMAC key are all derived from a
119[KDF](../system/ulib/crypto/include/crypto/kdf.h).  This KDF is an [RFC 5869 HKDF][hkdf], which
120combines the key provided, the "salt" of the instance GUID and a per-use label such as "wrap" or
121"hmac".  The KDF does __NOT__ try to do any rate-limiting.  The KDF mitigates the risk of key reuse,
122as a new random instance salt will lead to new derived keys.  The
123[HMAC](../system/ulib/crypto/include/crypto/hmac.h) prevents accidental or malicious modification to
124go undetected, without leaking any useful information about the zxcrypt key.
125
126_NOTE: The KDF does __NOT__ do any [key stretching][stretch].  It is assumed that an attacker can
127remove a device and attempt the key derivations on their own, bypassing the HMAC check and any
128possible rate limits.  To prevent this, zxcrypt consumers should include properly rate-limited
129device keys, e.g. those from a [TPM][tpm], in deriving their zxcrypt key._
130
131## Future Work
132There are a number of areas where further work could, should, or must be done:
133* __Properly bind with keys__ ([bug][zx1130])
134
135  Currently, there is __NO__ way to inject a key at binding.  This forces zxcrypt to currently use a
136  __static key__, which catastrophically undermines its security.
137
138* __Unbind on-demand__ ([bug][zx1138])
139
140  Currently, there is no way to ask a zxcrypt driver to unbind on demand.  The only way currently is
141  to force the underlying device to unbind it, for example by issuing an `IOCTL_BLOCK_RR_PART`
142  command to a device that supports it.
143
144* __Surface hidden bind failures__
145
146  Currently, `zxcrypt_bind` may indicate success even though the device fails to initialize.
147  zxcrypt is __NOT__ synchronously adding the device to the device tree when the binding logic is
148  run.  It must do I/O and cannot block the call to `device_bind` from returning, so it spawn an
149  initializer thread and adds the device when complete.
150
151  As of 10/2017, this is an active area of DDK development and the policy is changing to requiring
152  the device to be added before return, with an additional call to publish that may come later.
153  With this it may be desirable to have the call to `zxcrypt_bind` block synchronously for callers
154  until the device is ready or has unambiguously failed to bind.
155
156* __Use AEAD instead of AES-XTS__
157
158  It is widely recognized that [AEADs][aead] provide superior cryptographic protection by validating
159  the integrity of their data before decrypting it.  This is desirable, but requires additional
160  per-block overhead.  This means either that consumers will need to consume non-page-aligned blocks
161  (once the in-line overhead is removed), or zxcrypt will need to store the overhead out-of-line and
162  handle [non-atomic write failures][atomic].
163
164* __Support multiple keys__
165
166  To facilitate [key escrow and/or recovery][escrow], it is straightforward to modify the superblock
167  format to have a series of cryptographic envelopes.  In anticipation of this, the libzxcrypt API
168  takes a variable number of keys, although the only length currently supported is 1, and the only
169  valid slot is 0.
170
171* __Adjust number of workers__
172
173  Currently there is one encrypter and one decrypter.  These are designed to work with an arbitrary
174  number of threads, so performance tuning may be need to find the optimal number of workers that
175  balances I/O bandwidth with [scheduler churn][thrash].
176
177* __Remove internal checks__
178
179  Currently, the zxcrypt code checks for many errors conditions at internal boundaries and returns
180  informative errors if those conditions aren't met.  For performance, those that arise from
181  programmer error only and not data from either the requester or underlying device could be
182  converted to "debug" assertions that are skipped in release mode.
183
184[ind-cca2]: https://en.wikipedia.org/wiki/Ciphertext_indistinguishability
185[tweak]: https://en.wikipedia.org/wiki/Block_cipher#Tweakable_block_ciphers
186[aes-xts]: https://en.wikipedia.org/wiki/Disk_encryption_theory#XEX-based_tweaked-codebook_mode_with_ciphertext_stealing_.28XTS.29
187[guid]: https://en.wikipedia.org/wiki/Universally_unique_identifier
188[iv]: https://en.wikipedia.org/wiki/Initialization_vector
189[hkdf]: https://tools.ietf.org/html/rfc5869
190[hmac]: https://www.ietf.org/rfc/rfc2104.txt
191[stretch]: https://en.wikipedia.org/wiki/Key_stretching
192[tpm]: https://trustedcomputinggroup.org/work-groups/trusted-platform-module/
193[zx1130]: https://fuchsia.atlassian.net/browse/ZX-1130
194[zx1138]: https://fuchsia.atlassian.net/browse/ZX-1138
195[aead]: https://tools.ietf.org/html/rfc5116
196[atomic]: https://en.wikipedia.org/wiki/Atomic_commit
197[escrow]: https://en.wikipedia.org/wiki/Key_escrow
198[thrash]: https://en.wikipedia.org/wiki/Thrashing_(computer_science)#Other_uses
199