| ===================================== |
| Filesystem-level encryption (fscrypt) |
| ===================================== |
| |
| Introduction |
| ============ |
| |
| fscrypt is a library which filesystems can hook into to support |
| transparent encryption of files and directories. |
| |
| Note: "fscrypt" in this document refers to the kernel-level portion, |
| implemented in ``fs/crypto/``, as opposed to the userspace tool |
| `fscrypt <https://github.com/google/fscrypt>`_. This document only |
| covers the kernel-level portion. For command-line examples of how to |
| use encryption, see the documentation for the userspace tool `fscrypt |
| <https://github.com/google/fscrypt>`_. Also, it is recommended to use |
| the fscrypt userspace tool, or other existing userspace tools such as |
| `fscryptctl <https://github.com/google/fscryptctl>`_ or `Android's key |
| management system |
| <https://source.android.com/security/encryption/file-based>`_, over |
| using the kernel's API directly. Using existing tools reduces the |
| chance of introducing your own security bugs. (Nevertheless, for |
| completeness this documentation covers the kernel's API anyway.) |
| |
| Unlike dm-crypt, fscrypt operates at the filesystem level rather than |
| at the block device level. This allows it to encrypt different files |
| with different keys and to have unencrypted files on the same |
| filesystem. This is useful for multi-user systems where each user's |
| data-at-rest needs to be cryptographically isolated from the others. |
| However, except for filenames, fscrypt does not encrypt filesystem |
| metadata. |
| |
| Unlike eCryptfs, which is a stacked filesystem, fscrypt is integrated |
| directly into supported filesystems --- currently ext4, F2FS, and |
| UBIFS. This allows encrypted files to be read and written without |
| caching both the decrypted and encrypted pages in the pagecache, |
| thereby nearly halving the memory used and bringing it in line with |
| unencrypted files. Similarly, half as many dentries and inodes are |
| needed. eCryptfs also limits encrypted filenames to 143 bytes, |
| causing application compatibility issues; fscrypt allows the full 255 |
| bytes (NAME_MAX). Finally, unlike eCryptfs, the fscrypt API can be |
| used by unprivileged users, with no need to mount anything. |
| |
| fscrypt does not support encrypting files in-place. Instead, it |
| supports marking an empty directory as encrypted. Then, after |
| userspace provides the key, all regular files, directories, and |
| symbolic links created in that directory tree are transparently |
| encrypted. |
| |
| Threat model |
| ============ |
| |
| Offline attacks |
| --------------- |
| |
| Provided that userspace chooses a strong encryption key, fscrypt |
| protects the confidentiality of file contents and filenames in the |
| event of a single point-in-time permanent offline compromise of the |
| block device content. fscrypt does not protect the confidentiality of |
| non-filename metadata, e.g. file sizes, file permissions, file |
| timestamps, and extended attributes. Also, the existence and location |
| of holes (unallocated blocks which logically contain all zeroes) in |
| files is not protected. |
| |
| fscrypt is not guaranteed to protect confidentiality or authenticity |
| if an attacker is able to manipulate the filesystem offline prior to |
| an authorized user later accessing the filesystem. |
| |
| Online attacks |
| -------------- |
| |
| fscrypt (and storage encryption in general) can only provide limited |
| protection, if any at all, against online attacks. In detail: |
| |
| Side-channel attacks |
| ~~~~~~~~~~~~~~~~~~~~ |
| |
| fscrypt is only resistant to side-channel attacks, such as timing or |
| electromagnetic attacks, to the extent that the underlying Linux |
| Cryptographic API algorithms are. If a vulnerable algorithm is used, |
| such as a table-based implementation of AES, it may be possible for an |
| attacker to mount a side channel attack against the online system. |
| Side channel attacks may also be mounted against applications |
| consuming decrypted data. |
| |
| Unauthorized file access |
| ~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| After an encryption key has been added, fscrypt does not hide the |
| plaintext file contents or filenames from other users on the same |
| system. Instead, existing access control mechanisms such as file mode |
| bits, POSIX ACLs, LSMs, or namespaces should be used for this purpose. |
| |
| (For the reasoning behind this, understand that while the key is |
| added, the confidentiality of the data, from the perspective of the |
| system itself, is *not* protected by the mathematical properties of |
| encryption but rather only by the correctness of the kernel. |
| Therefore, any encryption-specific access control checks would merely |
| be enforced by kernel *code* and therefore would be largely redundant |
| with the wide variety of access control mechanisms already available.) |
| |
| Kernel memory compromise |
| ~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| An attacker who compromises the system enough to read from arbitrary |
| memory, e.g. by mounting a physical attack or by exploiting a kernel |
| security vulnerability, can compromise all encryption keys that are |
| currently in use. |
| |
| However, fscrypt allows encryption keys to be removed from the kernel, |
| which may protect them from later compromise. |
| |
| In more detail, the FS_IOC_REMOVE_ENCRYPTION_KEY ioctl (or the |
| FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS ioctl) can wipe a master |
| encryption key from kernel memory. If it does so, it will also try to |
| evict all cached inodes which had been "unlocked" using the key, |
| thereby wiping their per-file keys and making them once again appear |
| "locked", i.e. in ciphertext or encrypted form. |
| |
| However, these ioctls have some limitations: |
| |
| - Per-file keys for in-use files will *not* be removed or wiped. |
| Therefore, for maximum effect, userspace should close the relevant |
| encrypted files and directories before removing a master key, as |
| well as kill any processes whose working directory is in an affected |
| encrypted directory. |
| |
| - The kernel cannot magically wipe copies of the master key(s) that |
| userspace might have as well. Therefore, userspace must wipe all |
| copies of the master key(s) it makes as well; normally this should |
| be done immediately after FS_IOC_ADD_ENCRYPTION_KEY, without waiting |
| for FS_IOC_REMOVE_ENCRYPTION_KEY. Naturally, the same also applies |
| to all higher levels in the key hierarchy. Userspace should also |
| follow other security precautions such as mlock()ing memory |
| containing keys to prevent it from being swapped out. |
| |
| - In general, decrypted contents and filenames in the kernel VFS |
| caches are freed but not wiped. Therefore, portions thereof may be |
| recoverable from freed memory, even after the corresponding key(s) |
| were wiped. To partially solve this, you can set |
| CONFIG_PAGE_POISONING=y in your kernel config and add page_poison=1 |
| to your kernel command line. However, this has a performance cost. |
| |
| - Secret keys might still exist in CPU registers, in crypto |
| accelerator hardware (if used by the crypto API to implement any of |
| the algorithms), or in other places not explicitly considered here. |
| |
| Limitations of v1 policies |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| v1 encryption policies have some weaknesses with respect to online |
| attacks: |
| |
| - There is no verification that the provided master key is correct. |
| Therefore, a malicious user can temporarily associate the wrong key |
| with another user's encrypted files to which they have read-only |
| access. Because of filesystem caching, the wrong key will then be |
| used by the other user's accesses to those files, even if the other |
| user has the correct key in their own keyring. This violates the |
| meaning of "read-only access". |
| |
| - A compromise of a per-file key also compromises the master key from |
| which it was derived. |
| |
| - Non-root users cannot securely remove encryption keys. |
| |
| All the above problems are fixed with v2 encryption policies. For |
| this reason among others, it is recommended to use v2 encryption |
| policies on all new encrypted directories. |
| |
| Key hierarchy |
| ============= |
| |
| Master Keys |
| ----------- |
| |
| Each encrypted directory tree is protected by a *master key*. Master |
| keys can be up to 64 bytes long, and must be at least as long as the |
| greater of the key length needed by the contents and filenames |
| encryption modes being used. For example, if AES-256-XTS is used for |
| contents encryption, the master key must be 64 bytes (512 bits). Note |
| that the XTS mode is defined to require a key twice as long as that |
| required by the underlying block cipher. |
| |
| To "unlock" an encrypted directory tree, userspace must provide the |
| appropriate master key. There can be any number of master keys, each |
| of which protects any number of directory trees on any number of |
| filesystems. |
| |
| Master keys must be real cryptographic keys, i.e. indistinguishable |
| from random bytestrings of the same length. This implies that users |
| **must not** directly use a password as a master key, zero-pad a |
| shorter key, or repeat a shorter key. Security cannot be guaranteed |
| if userspace makes any such error, as the cryptographic proofs and |
| analysis would no longer apply. |
| |
| Instead, users should generate master keys either using a |
| cryptographically secure random number generator, or by using a KDF |
| (Key Derivation Function). The kernel does not do any key stretching; |
| therefore, if userspace derives the key from a low-entropy secret such |
| as a passphrase, it is critical that a KDF designed for this purpose |
| be used, such as scrypt, PBKDF2, or Argon2. |
| |
| Key derivation function |
| ----------------------- |
| |
| With one exception, fscrypt never uses the master key(s) for |
| encryption directly. Instead, they are only used as input to a KDF |
| (Key Derivation Function) to derive the actual keys. |
| |
| The KDF used for a particular master key differs depending on whether |
| the key is used for v1 encryption policies or for v2 encryption |
| policies. Users **must not** use the same key for both v1 and v2 |
| encryption policies. (No real-world attack is currently known on this |
| specific case of key reuse, but its security cannot be guaranteed |
| since the cryptographic proofs and analysis would no longer apply.) |
| |
| For v1 encryption policies, the KDF only supports deriving per-file |
| encryption keys. It works by encrypting the master key with |
| AES-128-ECB, using the file's 16-byte nonce as the AES key. The |
| resulting ciphertext is used as the derived key. If the ciphertext is |
| longer than needed, then it is truncated to the needed length. |
| |
| For v2 encryption policies, the KDF is HKDF-SHA512. The master key is |
| passed as the "input keying material", no salt is used, and a distinct |
| "application-specific information string" is used for each distinct |
| key to be derived. For example, when a per-file encryption key is |
| derived, the application-specific information string is the file's |
| nonce prefixed with "fscrypt\\0" and a context byte. Different |
| context bytes are used for other types of derived keys. |
| |
| HKDF-SHA512 is preferred to the original AES-128-ECB based KDF because |
| HKDF is more flexible, is nonreversible, and evenly distributes |
| entropy from the master key. HKDF is also standardized and widely |
| used by other software, whereas the AES-128-ECB based KDF is ad-hoc. |
| |
| Per-file keys |
| ------------- |
| |
| Since each master key can protect many files, it is necessary to |
| "tweak" the encryption of each file so that the same plaintext in two |
| files doesn't map to the same ciphertext, or vice versa. In most |
| cases, fscrypt does this by deriving per-file keys. When a new |
| encrypted inode (regular file, directory, or symlink) is created, |
| fscrypt randomly generates a 16-byte nonce and stores it in the |
| inode's encryption xattr. Then, it uses a KDF (as described in `Key |
| derivation function`_) to derive the file's key from the master key |
| and nonce. |
| |
| Key derivation was chosen over key wrapping because wrapped keys would |
| require larger xattrs which would be less likely to fit in-line in the |
| filesystem's inode table, and there didn't appear to be any |
| significant advantages to key wrapping. In particular, currently |
| there is no requirement to support unlocking a file with multiple |
| alternative master keys or to support rotating master keys. Instead, |
| the master keys may be wrapped in userspace, e.g. as is done by the |
| `fscrypt <https://github.com/google/fscrypt>`_ tool. |
| |
| DIRECT_KEY policies |
| ------------------- |
| |
| The Adiantum encryption mode (see `Encryption modes and usage`_) is |
| suitable for both contents and filenames encryption, and it accepts |
| long IVs --- long enough to hold both an 8-byte logical block number |
| and a 16-byte per-file nonce. Also, the overhead of each Adiantum key |
| is greater than that of an AES-256-XTS key. |
| |
| Therefore, to improve performance and save memory, for Adiantum a |
| "direct key" configuration is supported. When the user has enabled |
| this by setting FSCRYPT_POLICY_FLAG_DIRECT_KEY in the fscrypt policy, |
| per-file keys are not used. Instead, whenever any data (contents or |
| filenames) is encrypted, the file's 16-byte nonce is included in the |
| IV. Moreover: |
| |
| - For v1 encryption policies, the encryption is done directly with the |
| master key. Because of this, users **must not** use the same master |
| key for any other purpose, even for other v1 policies. |
| |
| - For v2 encryption policies, the encryption is done with a per-mode |
| key derived using the KDF. Users may use the same master key for |
| other v2 encryption policies. |
| |
| IV_INO_LBLK_64 policies |
| ----------------------- |
| |
| When FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 is set in the fscrypt policy, |
| the encryption keys are derived from the master key, encryption mode |
| number, and filesystem UUID. This normally results in all files |
| protected by the same master key sharing a single contents encryption |
| key and a single filenames encryption key. To still encrypt different |
| files' data differently, inode numbers are included in the IVs. |
| Consequently, shrinking the filesystem may not be allowed. |
| |
| This format is optimized for use with inline encryption hardware |
| compliant with the UFS or eMMC standards, which support only 64 IV |
| bits per I/O request and may have only a small number of keyslots. |
| |
| Key identifiers |
| --------------- |
| |
| For master keys used for v2 encryption policies, a unique 16-byte "key |
| identifier" is also derived using the KDF. This value is stored in |
| the clear, since it is needed to reliably identify the key itself. |
| |
| Encryption modes and usage |
| ========================== |
| |
| fscrypt allows one encryption mode to be specified for file contents |
| and one encryption mode to be specified for filenames. Different |
| directory trees are permitted to use different encryption modes. |
| Currently, the following pairs of encryption modes are supported: |
| |
| - AES-256-XTS for contents and AES-256-CTS-CBC for filenames |
| - AES-128-CBC for contents and AES-128-CTS-CBC for filenames |
| - Adiantum for both contents and filenames |
| |
| If unsure, you should use the (AES-256-XTS, AES-256-CTS-CBC) pair. |
| |
| AES-128-CBC was added only for low-powered embedded devices with |
| crypto accelerators such as CAAM or CESA that do not support XTS. To |
| use AES-128-CBC, CONFIG_CRYPTO_ESSIV and CONFIG_CRYPTO_SHA256 (or |
| another SHA-256 implementation) must be enabled so that ESSIV can be |
| used. |
| |
| Adiantum is a (primarily) stream cipher-based mode that is fast even |
| on CPUs without dedicated crypto instructions. It's also a true |
| wide-block mode, unlike XTS. It can also eliminate the need to derive |
| per-file keys. However, it depends on the security of two primitives, |
| XChaCha12 and AES-256, rather than just one. See the paper |
| "Adiantum: length-preserving encryption for entry-level processors" |
| (https://eprint.iacr.org/2018/720.pdf) for more details. To use |
| Adiantum, CONFIG_CRYPTO_ADIANTUM must be enabled. Also, fast |
| implementations of ChaCha and NHPoly1305 should be enabled, e.g. |
| CONFIG_CRYPTO_CHACHA20_NEON and CONFIG_CRYPTO_NHPOLY1305_NEON for ARM. |
| |
| New encryption modes can be added relatively easily, without changes |
| to individual filesystems. However, authenticated encryption (AE) |
| modes are not currently supported because of the difficulty of dealing |
| with ciphertext expansion. |
| |
| Contents encryption |
| ------------------- |
| |
| For file contents, each filesystem block is encrypted independently. |
| Starting from Linux kernel 5.5, encryption of filesystems with block |
| size less than system's page size is supported. |
| |
| Each block's IV is set to the logical block number within the file as |
| a little endian number, except that: |
| |
| - With CBC mode encryption, ESSIV is also used. Specifically, each IV |
| is encrypted with AES-256 where the AES-256 key is the SHA-256 hash |
| of the file's data encryption key. |
| |
| - With `DIRECT_KEY policies`_, the file's nonce is appended to the IV. |
| Currently this is only allowed with the Adiantum encryption mode. |
| |
| - With `IV_INO_LBLK_64 policies`_, the logical block number is limited |
| to 32 bits and is placed in bits 0-31 of the IV. The inode number |
| (which is also limited to 32 bits) is placed in bits 32-63. |
| |
| Note that because file logical block numbers are included in the IVs, |
| filesystems must enforce that blocks are never shifted around within |
| encrypted files, e.g. via "collapse range" or "insert range". |
| |
| Filenames encryption |
| -------------------- |
| |
| For filenames, each full filename is encrypted at once. Because of |
| the requirements to retain support for efficient directory lookups and |
| filenames of up to 255 bytes, the same IV is used for every filename |
| in a directory. |
| |
| However, each encrypted directory still uses a unique key, or |
| alternatively has the file's nonce (for `DIRECT_KEY policies`_) or |
| inode number (for `IV_INO_LBLK_64 policies`_) included in the IVs. |
| Thus, IV reuse is limited to within a single directory. |
| |
| With CTS-CBC, the IV reuse means that when the plaintext filenames |
| share a common prefix at least as long as the cipher block size (16 |
| bytes for AES), the corresponding encrypted filenames will also share |
| a common prefix. This is undesirable. Adiantum does not have this |
| weakness, as it is a wide-block encryption mode. |
| |
| All supported filenames encryption modes accept any plaintext length |
| >= 16 bytes; cipher block alignment is not required. However, |
| filenames shorter than 16 bytes are NUL-padded to 16 bytes before |
| being encrypted. In addition, to reduce leakage of filename lengths |
| via their ciphertexts, all filenames are NUL-padded to the next 4, 8, |
| 16, or 32-byte boundary (configurable). 32 is recommended since this |
| provides the best confidentiality, at the cost of making directory |
| entries consume slightly more space. Note that since NUL (``\0``) is |
| not otherwise a valid character in filenames, the padding will never |
| produce duplicate plaintexts. |
| |
| Symbolic link targets are considered a type of filename and are |
| encrypted in the same way as filenames in directory entries, except |
| that IV reuse is not a problem as each symlink has its own inode. |
| |
| User API |
| ======== |
| |
| Setting an encryption policy |
| ---------------------------- |
| |
| FS_IOC_SET_ENCRYPTION_POLICY |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| The FS_IOC_SET_ENCRYPTION_POLICY ioctl sets an encryption policy on an |
| empty directory or verifies that a directory or regular file already |
| has the specified encryption policy. It takes in a pointer to a |
| :c:type:`struct fscrypt_policy_v1` or a :c:type:`struct |
| fscrypt_policy_v2`, defined as follows:: |
| |
| #define FSCRYPT_POLICY_V1 0 |
| #define FSCRYPT_KEY_DESCRIPTOR_SIZE 8 |
| struct fscrypt_policy_v1 { |
| __u8 version; |
| __u8 contents_encryption_mode; |
| __u8 filenames_encryption_mode; |
| __u8 flags; |
| __u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; |
| }; |
| #define fscrypt_policy fscrypt_policy_v1 |
| |
| #define FSCRYPT_POLICY_V2 2 |
| #define FSCRYPT_KEY_IDENTIFIER_SIZE 16 |
| struct fscrypt_policy_v2 { |
| __u8 version; |
| __u8 contents_encryption_mode; |
| __u8 filenames_encryption_mode; |
| __u8 flags; |
| __u8 __reserved[4]; |
| __u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]; |
| }; |
| |
| This structure must be initialized as follows: |
| |
| - ``version`` must be FSCRYPT_POLICY_V1 (0) if the struct is |
| :c:type:`fscrypt_policy_v1` or FSCRYPT_POLICY_V2 (2) if the struct |
| is :c:type:`fscrypt_policy_v2`. (Note: we refer to the original |
| policy version as "v1", though its version code is really 0.) For |
| new encrypted directories, use v2 policies. |
| |
| - ``contents_encryption_mode`` and ``filenames_encryption_mode`` must |
| be set to constants from ``<linux/fscrypt.h>`` which identify the |
| encryption modes to use. If unsure, use FSCRYPT_MODE_AES_256_XTS |
| (1) for ``contents_encryption_mode`` and FSCRYPT_MODE_AES_256_CTS |
| (4) for ``filenames_encryption_mode``. |
| |
| - ``flags`` contains optional flags from ``<linux/fscrypt.h>``: |
| |
| - FSCRYPT_POLICY_FLAGS_PAD_*: The amount of NUL padding to use when |
| encrypting filenames. If unsure, use FSCRYPT_POLICY_FLAGS_PAD_32 |
| (0x3). |
| - FSCRYPT_POLICY_FLAG_DIRECT_KEY: See `DIRECT_KEY policies`_. |
| - FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64: See `IV_INO_LBLK_64 |
| policies`_. This is mutually exclusive with DIRECT_KEY and is not |
| supported on v1 policies. |
| |
| - For v2 encryption policies, ``__reserved`` must be zeroed. |
| |
| - For v1 encryption policies, ``master_key_descriptor`` specifies how |
| to find the master key in a keyring; see `Adding keys`_. It is up |
| to userspace to choose a unique ``master_key_descriptor`` for each |
| master key. The e4crypt and fscrypt tools use the first 8 bytes of |
| ``SHA-512(SHA-512(master_key))``, but this particular scheme is not |
| required. Also, the master key need not be in the keyring yet when |
| FS_IOC_SET_ENCRYPTION_POLICY is executed. However, it must be added |
| before any files can be created in the encrypted directory. |
| |
| For v2 encryption policies, ``master_key_descriptor`` has been |
| replaced with ``master_key_identifier``, which is longer and cannot |
| be arbitrarily chosen. Instead, the key must first be added using |
| `FS_IOC_ADD_ENCRYPTION_KEY`_. Then, the ``key_spec.u.identifier`` |
| the kernel returned in the :c:type:`struct fscrypt_add_key_arg` must |
| be used as the ``master_key_identifier`` in the :c:type:`struct |
| fscrypt_policy_v2`. |
| |
| If the file is not yet encrypted, then FS_IOC_SET_ENCRYPTION_POLICY |
| verifies that the file is an empty directory. If so, the specified |
| encryption policy is assigned to the directory, turning it into an |
| encrypted directory. After that, and after providing the |
| corresponding master key as described in `Adding keys`_, all regular |
| files, directories (recursively), and symlinks created in the |
| directory will be encrypted, inheriting the same encryption policy. |
| The filenames in the directory's entries will be encrypted as well. |
| |
| Alternatively, if the file is already encrypted, then |
| FS_IOC_SET_ENCRYPTION_POLICY validates that the specified encryption |
| policy exactly matches the actual one. If they match, then the ioctl |
| returns 0. Otherwise, it fails with EEXIST. This works on both |
| regular files and directories, including nonempty directories. |
| |
| When a v2 encryption policy is assigned to a directory, it is also |
| required that either the specified key has been added by the current |
| user or that the caller has CAP_FOWNER in the initial user namespace. |
| (This is needed to prevent a user from encrypting their data with |
| another user's key.) The key must remain added while |
| FS_IOC_SET_ENCRYPTION_POLICY is executing. However, if the new |
| encrypted directory does not need to be accessed immediately, then the |
| key can be removed right away afterwards. |
| |
| Note that the ext4 filesystem does not allow the root directory to be |
| encrypted, even if it is empty. Users who want to encrypt an entire |
| filesystem with one key should consider using dm-crypt instead. |
| |
| FS_IOC_SET_ENCRYPTION_POLICY can fail with the following errors: |
| |
| - ``EACCES``: the file is not owned by the process's uid, nor does the |
| process have the CAP_FOWNER capability in a namespace with the file |
| owner's uid mapped |
| - ``EEXIST``: the file is already encrypted with an encryption policy |
| different from the one specified |
| - ``EINVAL``: an invalid encryption policy was specified (invalid |
| version, mode(s), or flags; or reserved bits were set) |
| - ``ENOKEY``: a v2 encryption policy was specified, but the key with |
| the specified ``master_key_identifier`` has not been added, nor does |
| the process have the CAP_FOWNER capability in the initial user |
| namespace |
| - ``ENOTDIR``: the file is unencrypted and is a regular file, not a |
| directory |
| - ``ENOTEMPTY``: the file is unencrypted and is a nonempty directory |
| - ``ENOTTY``: this type of filesystem does not implement encryption |
| - ``EOPNOTSUPP``: the kernel was not configured with encryption |
| support for filesystems, or the filesystem superblock has not |
| had encryption enabled on it. (For example, to use encryption on an |
| ext4 filesystem, CONFIG_FS_ENCRYPTION must be enabled in the |
| kernel config, and the superblock must have had the "encrypt" |
| feature flag enabled using ``tune2fs -O encrypt`` or ``mkfs.ext4 -O |
| encrypt``.) |
| - ``EPERM``: this directory may not be encrypted, e.g. because it is |
| the root directory of an ext4 filesystem |
| - ``EROFS``: the filesystem is readonly |
| |
| Getting an encryption policy |
| ---------------------------- |
| |
| Two ioctls are available to get a file's encryption policy: |
| |
| - `FS_IOC_GET_ENCRYPTION_POLICY_EX`_ |
| - `FS_IOC_GET_ENCRYPTION_POLICY`_ |
| |
| The extended (_EX) version of the ioctl is more general and is |
| recommended to use when possible. However, on older kernels only the |
| original ioctl is available. Applications should try the extended |
| version, and if it fails with ENOTTY fall back to the original |
| version. |
| |
| FS_IOC_GET_ENCRYPTION_POLICY_EX |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| The FS_IOC_GET_ENCRYPTION_POLICY_EX ioctl retrieves the encryption |
| policy, if any, for a directory or regular file. No additional |
| permissions are required beyond the ability to open the file. It |
| takes in a pointer to a :c:type:`struct fscrypt_get_policy_ex_arg`, |
| defined as follows:: |
| |
| struct fscrypt_get_policy_ex_arg { |
| __u64 policy_size; /* input/output */ |
| union { |
| __u8 version; |
| struct fscrypt_policy_v1 v1; |
| struct fscrypt_policy_v2 v2; |
| } policy; /* output */ |
| }; |
| |
| The caller must initialize ``policy_size`` to the size available for |
| the policy struct, i.e. ``sizeof(arg.policy)``. |
| |
| On success, the policy struct is returned in ``policy``, and its |
| actual size is returned in ``policy_size``. ``policy.version`` should |
| be checked to determine the version of policy returned. Note that the |
| version code for the "v1" policy is actually 0 (FSCRYPT_POLICY_V1). |
| |
| FS_IOC_GET_ENCRYPTION_POLICY_EX can fail with the following errors: |
| |
| - ``EINVAL``: the file is encrypted, but it uses an unrecognized |
| encryption policy version |
| - ``ENODATA``: the file is not encrypted |
| - ``ENOTTY``: this type of filesystem does not implement encryption, |
| or this kernel is too old to support FS_IOC_GET_ENCRYPTION_POLICY_EX |
| (try FS_IOC_GET_ENCRYPTION_POLICY instead) |
| - ``EOPNOTSUPP``: the kernel was not configured with encryption |
| support for this filesystem, or the filesystem superblock has not |
| had encryption enabled on it |
| - ``EOVERFLOW``: the file is encrypted and uses a recognized |
| encryption policy version, but the policy struct does not fit into |
| the provided buffer |
| |
| Note: if you only need to know whether a file is encrypted or not, on |
| most filesystems it is also possible to use the FS_IOC_GETFLAGS ioctl |
| and check for FS_ENCRYPT_FL, or to use the statx() system call and |
| check for STATX_ATTR_ENCRYPTED in stx_attributes. |
| |
| FS_IOC_GET_ENCRYPTION_POLICY |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| The FS_IOC_GET_ENCRYPTION_POLICY ioctl can also retrieve the |
| encryption policy, if any, for a directory or regular file. However, |
| unlike `FS_IOC_GET_ENCRYPTION_POLICY_EX`_, |
| FS_IOC_GET_ENCRYPTION_POLICY only supports the original policy |
| version. It takes in a pointer directly to a :c:type:`struct |
| fscrypt_policy_v1` rather than a :c:type:`struct |
| fscrypt_get_policy_ex_arg`. |
| |
| The error codes for FS_IOC_GET_ENCRYPTION_POLICY are the same as those |
| for FS_IOC_GET_ENCRYPTION_POLICY_EX, except that |
| FS_IOC_GET_ENCRYPTION_POLICY also returns ``EINVAL`` if the file is |
| encrypted using a newer encryption policy version. |
| |
| Getting the per-filesystem salt |
| ------------------------------- |
| |
| Some filesystems, such as ext4 and F2FS, also support the deprecated |
| ioctl FS_IOC_GET_ENCRYPTION_PWSALT. This ioctl retrieves a randomly |
| generated 16-byte value stored in the filesystem superblock. This |
| value is intended to used as a salt when deriving an encryption key |
| from a passphrase or other low-entropy user credential. |
| |
| FS_IOC_GET_ENCRYPTION_PWSALT is deprecated. Instead, prefer to |
| generate and manage any needed salt(s) in userspace. |
| |
| Adding keys |
| ----------- |
| |
| FS_IOC_ADD_ENCRYPTION_KEY |
| ~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| The FS_IOC_ADD_ENCRYPTION_KEY ioctl adds a master encryption key to |
| the filesystem, making all files on the filesystem which were |
| encrypted using that key appear "unlocked", i.e. in plaintext form. |
| It can be executed on any file or directory on the target filesystem, |
| but using the filesystem's root directory is recommended. It takes in |
| a pointer to a :c:type:`struct fscrypt_add_key_arg`, defined as |
| follows:: |
| |
| struct fscrypt_add_key_arg { |
| struct fscrypt_key_specifier key_spec; |
| __u32 raw_size; |
| __u32 __reserved[9]; |
| __u8 raw[]; |
| }; |
| |
| #define FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR 1 |
| #define FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER 2 |
| |
| struct fscrypt_key_specifier { |
| __u32 type; /* one of FSCRYPT_KEY_SPEC_TYPE_* */ |
| __u32 __reserved; |
| union { |
| __u8 __reserved[32]; /* reserve some extra space */ |
| __u8 descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; |
| __u8 identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]; |
| } u; |
| }; |
| |
| :c:type:`struct fscrypt_add_key_arg` must be zeroed, then initialized |
| as follows: |
| |
| - If the key is being added for use by v1 encryption policies, then |
| ``key_spec.type`` must contain FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR, and |
| ``key_spec.u.descriptor`` must contain the descriptor of the key |
| being added, corresponding to the value in the |
| ``master_key_descriptor`` field of :c:type:`struct |
| fscrypt_policy_v1`. To add this type of key, the calling process |
| must have the CAP_SYS_ADMIN capability in the initial user |
| namespace. |
| |
| Alternatively, if the key is being added for use by v2 encryption |
| policies, then ``key_spec.type`` must contain |
| FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER, and ``key_spec.u.identifier`` is |
| an *output* field which the kernel fills in with a cryptographic |
| hash of the key. To add this type of key, the calling process does |
| not need any privileges. However, the number of keys that can be |
| added is limited by the user's quota for the keyrings service (see |
| ``Documentation/security/keys/core.rst``). |
| |
| - ``raw_size`` must be the size of the ``raw`` key provided, in bytes. |
| |
| - ``raw`` is a variable-length field which must contain the actual |
| key, ``raw_size`` bytes long. |
| |
| For v2 policy keys, the kernel keeps track of which user (identified |
| by effective user ID) added the key, and only allows the key to be |
| removed by that user --- or by "root", if they use |
| `FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS`_. |
| |
| However, if another user has added the key, it may be desirable to |
| prevent that other user from unexpectedly removing it. Therefore, |
| FS_IOC_ADD_ENCRYPTION_KEY may also be used to add a v2 policy key |
| *again*, even if it's already added by other user(s). In this case, |
| FS_IOC_ADD_ENCRYPTION_KEY will just install a claim to the key for the |
| current user, rather than actually add the key again (but the raw key |
| must still be provided, as a proof of knowledge). |
| |
| FS_IOC_ADD_ENCRYPTION_KEY returns 0 if either the key or a claim to |
| the key was either added or already exists. |
| |
| FS_IOC_ADD_ENCRYPTION_KEY can fail with the following errors: |
| |
| - ``EACCES``: FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR was specified, but the |
| caller does not have the CAP_SYS_ADMIN capability in the initial |
| user namespace |
| - ``EDQUOT``: the key quota for this user would be exceeded by adding |
| the key |
| - ``EINVAL``: invalid key size or key specifier type, or reserved bits |
| were set |
| - ``ENOTTY``: this type of filesystem does not implement encryption |
| - ``EOPNOTSUPP``: the kernel was not configured with encryption |
| support for this filesystem, or the filesystem superblock has not |
| had encryption enabled on it |
| |
| Legacy method |
| ~~~~~~~~~~~~~ |
| |
| For v1 encryption policies, a master encryption key can also be |
| provided by adding it to a process-subscribed keyring, e.g. to a |
| session keyring, or to a user keyring if the user keyring is linked |
| into the session keyring. |
| |
| This method is deprecated (and not supported for v2 encryption |
| policies) for several reasons. First, it cannot be used in |
| combination with FS_IOC_REMOVE_ENCRYPTION_KEY (see `Removing keys`_), |
| so for removing a key a workaround such as keyctl_unlink() in |
| combination with ``sync; echo 2 > /proc/sys/vm/drop_caches`` would |
| have to be used. Second, it doesn't match the fact that the |
| locked/unlocked status of encrypted files (i.e. whether they appear to |
| be in plaintext form or in ciphertext form) is global. This mismatch |
| has caused much confusion as well as real problems when processes |
| running under different UIDs, such as a ``sudo`` command, need to |
| access encrypted files. |
| |
| Nevertheless, to add a key to one of the process-subscribed keyrings, |
| the add_key() system call can be used (see: |
| ``Documentation/security/keys/core.rst``). The key type must be |
| "logon"; keys of this type are kept in kernel memory and cannot be |
| read back by userspace. The key description must be "fscrypt:" |
| followed by the 16-character lower case hex representation of the |
| ``master_key_descriptor`` that was set in the encryption policy. The |
| key payload must conform to the following structure:: |
| |
| #define FSCRYPT_MAX_KEY_SIZE 64 |
| |
| struct fscrypt_key { |
| __u32 mode; |
| __u8 raw[FSCRYPT_MAX_KEY_SIZE]; |
| __u32 size; |
| }; |
| |
| ``mode`` is ignored; just set it to 0. The actual key is provided in |
| ``raw`` with ``size`` indicating its size in bytes. That is, the |
| bytes ``raw[0..size-1]`` (inclusive) are the actual key. |
| |
| The key description prefix "fscrypt:" may alternatively be replaced |
| with a filesystem-specific prefix such as "ext4:". However, the |
| filesystem-specific prefixes are deprecated and should not be used in |
| new programs. |
| |
| Removing keys |
| ------------- |
| |
| Two ioctls are available for removing a key that was added by |
| `FS_IOC_ADD_ENCRYPTION_KEY`_: |
| |
| - `FS_IOC_REMOVE_ENCRYPTION_KEY`_ |
| - `FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS`_ |
| |
| These two ioctls differ only in cases where v2 policy keys are added |
| or removed by non-root users. |
| |
| These ioctls don't work on keys that were added via the legacy |
| process-subscribed keyrings mechanism. |
| |
| Before using these ioctls, read the `Kernel memory compromise`_ |
| section for a discussion of the security goals and limitations of |
| these ioctls. |
| |
| FS_IOC_REMOVE_ENCRYPTION_KEY |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| The FS_IOC_REMOVE_ENCRYPTION_KEY ioctl removes a claim to a master |
| encryption key from the filesystem, and possibly removes the key |
| itself. It can be executed on any file or directory on the target |
| filesystem, but using the filesystem's root directory is recommended. |
| It takes in a pointer to a :c:type:`struct fscrypt_remove_key_arg`, |
| defined as follows:: |
| |
| struct fscrypt_remove_key_arg { |
| struct fscrypt_key_specifier key_spec; |
| #define FSCRYPT_KEY_REMOVAL_STATUS_FLAG_FILES_BUSY 0x00000001 |
| #define FSCRYPT_KEY_REMOVAL_STATUS_FLAG_OTHER_USERS 0x00000002 |
| __u32 removal_status_flags; /* output */ |
| __u32 __reserved[5]; |
| }; |
| |
| This structure must be zeroed, then initialized as follows: |
| |
| - The key to remove is specified by ``key_spec``: |
| |
| - To remove a key used by v1 encryption policies, set |
| ``key_spec.type`` to FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR and fill |
| in ``key_spec.u.descriptor``. To remove this type of key, the |
| calling process must have the CAP_SYS_ADMIN capability in the |
| initial user namespace. |
| |
| - To remove a key used by v2 encryption policies, set |
| ``key_spec.type`` to FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER and fill |
| in ``key_spec.u.identifier``. |
| |
| For v2 policy keys, this ioctl is usable by non-root users. However, |
| to make this possible, it actually just removes the current user's |
| claim to the key, undoing a single call to FS_IOC_ADD_ENCRYPTION_KEY. |
| Only after all claims are removed is the key really removed. |
| |
| For example, if FS_IOC_ADD_ENCRYPTION_KEY was called with uid 1000, |
| then the key will be "claimed" by uid 1000, and |
| FS_IOC_REMOVE_ENCRYPTION_KEY will only succeed as uid 1000. Or, if |
| both uids 1000 and 2000 added the key, then for each uid |
| FS_IOC_REMOVE_ENCRYPTION_KEY will only remove their own claim. Only |
| once *both* are removed is the key really removed. (Think of it like |
| unlinking a file that may have hard links.) |
| |
| If FS_IOC_REMOVE_ENCRYPTION_KEY really removes the key, it will also |
| try to "lock" all files that had been unlocked with the key. It won't |
| lock files that are still in-use, so this ioctl is expected to be used |
| in cooperation with userspace ensuring that none of the files are |
| still open. However, if necessary, this ioctl can be executed again |
| later to retry locking any remaining files. |
| |
| FS_IOC_REMOVE_ENCRYPTION_KEY returns 0 if either the key was removed |
| (but may still have files remaining to be locked), the user's claim to |
| the key was removed, or the key was already removed but had files |
| remaining to be the locked so the ioctl retried locking them. In any |
| of these cases, ``removal_status_flags`` is filled in with the |
| following informational status flags: |
| |
| - ``FSCRYPT_KEY_REMOVAL_STATUS_FLAG_FILES_BUSY``: set if some file(s) |
| are still in-use. Not guaranteed to be set in the case where only |
| the user's claim to the key was removed. |
| - ``FSCRYPT_KEY_REMOVAL_STATUS_FLAG_OTHER_USERS``: set if only the |
| user's claim to the key was removed, not the key itself |
| |
| FS_IOC_REMOVE_ENCRYPTION_KEY can fail with the following errors: |
| |
| - ``EACCES``: The FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR key specifier type |
| was specified, but the caller does not have the CAP_SYS_ADMIN |
| capability in the initial user namespace |
| - ``EINVAL``: invalid key specifier type, or reserved bits were set |
| - ``ENOKEY``: the key object was not found at all, i.e. it was never |
| added in the first place or was already fully removed including all |
| files locked; or, the user does not have a claim to the key (but |
| someone else does). |
| - ``ENOTTY``: this type of filesystem does not implement encryption |
| - ``EOPNOTSUPP``: the kernel was not configured with encryption |
| support for this filesystem, or the filesystem superblock has not |
| had encryption enabled on it |
| |
| FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS is exactly the same as |
| `FS_IOC_REMOVE_ENCRYPTION_KEY`_, except that for v2 policy keys, the |
| ALL_USERS version of the ioctl will remove all users' claims to the |
| key, not just the current user's. I.e., the key itself will always be |
| removed, no matter how many users have added it. This difference is |
| only meaningful if non-root users are adding and removing keys. |
| |
| Because of this, FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS also requires |
| "root", namely the CAP_SYS_ADMIN capability in the initial user |
| namespace. Otherwise it will fail with EACCES. |
| |
| Getting key status |
| ------------------ |
| |
| FS_IOC_GET_ENCRYPTION_KEY_STATUS |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| The FS_IOC_GET_ENCRYPTION_KEY_STATUS ioctl retrieves the status of a |
| master encryption key. It can be executed on any file or directory on |
| the target filesystem, but using the filesystem's root directory is |
| recommended. It takes in a pointer to a :c:type:`struct |
| fscrypt_get_key_status_arg`, defined as follows:: |
| |
| struct fscrypt_get_key_status_arg { |
| /* input */ |
| struct fscrypt_key_specifier key_spec; |
| __u32 __reserved[6]; |
| |
| /* output */ |
| #define FSCRYPT_KEY_STATUS_ABSENT 1 |
| #define FSCRYPT_KEY_STATUS_PRESENT 2 |
| #define FSCRYPT_KEY_STATUS_INCOMPLETELY_REMOVED 3 |
| __u32 status; |
| #define FSCRYPT_KEY_STATUS_FLAG_ADDED_BY_SELF 0x00000001 |
| __u32 status_flags; |
| __u32 user_count; |
| __u32 __out_reserved[13]; |
| }; |
| |
| The caller must zero all input fields, then fill in ``key_spec``: |
| |
| - To get the status of a key for v1 encryption policies, set |
| ``key_spec.type`` to FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR and fill |
| in ``key_spec.u.descriptor``. |
| |
| - To get the status of a key for v2 encryption policies, set |
| ``key_spec.type`` to FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER and fill |
| in ``key_spec.u.identifier``. |
| |
| On success, 0 is returned and the kernel fills in the output fields: |
| |
| - ``status`` indicates whether the key is absent, present, or |
| incompletely removed. Incompletely removed means that the master |
| secret has been removed, but some files are still in use; i.e., |
| `FS_IOC_REMOVE_ENCRYPTION_KEY`_ returned 0 but set the informational |
| status flag FSCRYPT_KEY_REMOVAL_STATUS_FLAG_FILES_BUSY. |
| |
| - ``status_flags`` can contain the following flags: |
| |
| - ``FSCRYPT_KEY_STATUS_FLAG_ADDED_BY_SELF`` indicates that the key |
| has added by the current user. This is only set for keys |
| identified by ``identifier`` rather than by ``descriptor``. |
| |
| - ``user_count`` specifies the number of users who have added the key. |
| This is only set for keys identified by ``identifier`` rather than |
| by ``descriptor``. |
| |
| FS_IOC_GET_ENCRYPTION_KEY_STATUS can fail with the following errors: |
| |
| - ``EINVAL``: invalid key specifier type, or reserved bits were set |
| - ``ENOTTY``: this type of filesystem does not implement encryption |
| - ``EOPNOTSUPP``: the kernel was not configured with encryption |
| support for this filesystem, or the filesystem superblock has not |
| had encryption enabled on it |
| |
| Among other use cases, FS_IOC_GET_ENCRYPTION_KEY_STATUS can be useful |
| for determining whether the key for a given encrypted directory needs |
| to be added before prompting the user for the passphrase needed to |
| derive the key. |
| |
| FS_IOC_GET_ENCRYPTION_KEY_STATUS can only get the status of keys in |
| the filesystem-level keyring, i.e. the keyring managed by |
| `FS_IOC_ADD_ENCRYPTION_KEY`_ and `FS_IOC_REMOVE_ENCRYPTION_KEY`_. It |
| cannot get the status of a key that has only been added for use by v1 |
| encryption policies using the legacy mechanism involving |
| process-subscribed keyrings. |
| |
| Access semantics |
| ================ |
| |
| With the key |
| ------------ |
| |
| With the encryption key, encrypted regular files, directories, and |
| symlinks behave very similarly to their unencrypted counterparts --- |
| after all, the encryption is intended to be transparent. However, |
| astute users may notice some differences in behavior: |
| |
| - Unencrypted files, or files encrypted with a different encryption |
| policy (i.e. different key, modes, or flags), cannot be renamed or |
| linked into an encrypted directory; see `Encryption policy |
| enforcement`_. Attempts to do so will fail with EXDEV. However, |
| encrypted files can be renamed within an encrypted directory, or |
| into an unencrypted directory. |
| |
| Note: "moving" an unencrypted file into an encrypted directory, e.g. |
| with the `mv` program, is implemented in userspace by a copy |
| followed by a delete. Be aware that the original unencrypted data |
| may remain recoverable from free space on the disk; prefer to keep |
| all files encrypted from the very beginning. The `shred` program |
| may be used to overwrite the source files but isn't guaranteed to be |
| effective on all filesystems and storage devices. |
| |
| - Direct I/O is not supported on encrypted files. Attempts to use |
| direct I/O on such files will fall back to buffered I/O. |
| |
| - The fallocate operations FALLOC_FL_COLLAPSE_RANGE, |
| FALLOC_FL_INSERT_RANGE, and FALLOC_FL_ZERO_RANGE are not supported |
| on encrypted files and will fail with EOPNOTSUPP. |
| |
| - Online defragmentation of encrypted files is not supported. The |
| EXT4_IOC_MOVE_EXT and F2FS_IOC_MOVE_RANGE ioctls will fail with |
| EOPNOTSUPP. |
| |
| - The ext4 filesystem does not support data journaling with encrypted |
| regular files. It will fall back to ordered data mode instead. |
| |
| - DAX (Direct Access) is not supported on encrypted files. |
| |
| - The st_size of an encrypted symlink will not necessarily give the |
| length of the symlink target as required by POSIX. It will actually |
| give the length of the ciphertext, which will be slightly longer |
| than the plaintext due to NUL-padding and an extra 2-byte overhead. |
| |
| - The maximum length of an encrypted symlink is 2 bytes shorter than |
| the maximum length of an unencrypted symlink. For example, on an |
| EXT4 filesystem with a 4K block size, unencrypted symlinks can be up |
| to 4095 bytes long, while encrypted symlinks can only be up to 4093 |
| bytes long (both lengths excluding the terminating null). |
| |
| Note that mmap *is* supported. This is possible because the pagecache |
| for an encrypted file contains the plaintext, not the ciphertext. |
| |
| Without the key |
| --------------- |
| |
| Some filesystem operations may be performed on encrypted regular |
| files, directories, and symlinks even before their encryption key has |
| been added, or after their encryption key has been removed: |
| |
| - File metadata may be read, e.g. using stat(). |
| |
| - Directories may be listed, in which case the filenames will be |
| listed in an encoded form derived from their ciphertext. The |
| current encoding algorithm is described in `Filename hashing and |
| encoding`_. The algorithm is subject to change, but it is |
| guaranteed that the presented filenames will be no longer than |
| NAME_MAX bytes, will not contain the ``/`` or ``\0`` characters, and |
| will uniquely identify directory entries. |
| |
| The ``.`` and ``..`` directory entries are special. They are always |
| present and are not encrypted or encoded. |
| |
| - Files may be deleted. That is, nondirectory files may be deleted |
| with unlink() as usual, and empty directories may be deleted with |
| rmdir() as usual. Therefore, ``rm`` and ``rm -r`` will work as |
| expected. |
| |
| - Symlink targets may be read and followed, but they will be presented |
| in encrypted form, similar to filenames in directories. Hence, they |
| are unlikely to point to anywhere useful. |
| |
| Without the key, regular files cannot be opened or truncated. |
| Attempts to do so will fail with ENOKEY. This implies that any |
| regular file operations that require a file descriptor, such as |
| read(), write(), mmap(), fallocate(), and ioctl(), are also forbidden. |
| |
| Also without the key, files of any type (including directories) cannot |
| be created or linked into an encrypted directory, nor can a name in an |
| encrypted directory be the source or target of a rename, nor can an |
| O_TMPFILE temporary file be created in an encrypted directory. All |
| such operations will fail with ENOKEY. |
| |
| It is not currently possible to backup and restore encrypted files |
| without the encryption key. This would require special APIs which |
| have not yet been implemented. |
| |
| Encryption policy enforcement |
| ============================= |
| |
| After an encryption policy has been set on a directory, all regular |
| files, directories, and symbolic links created in that directory |
| (recursively) will inherit that encryption policy. Special files --- |
| that is, named pipes, device nodes, and UNIX domain sockets --- will |
| not be encrypted. |
| |
| Except for those special files, it is forbidden to have unencrypted |
| files, or files encrypted with a different encryption policy, in an |
| encrypted directory tree. Attempts to link or rename such a file into |
| an encrypted directory will fail with EXDEV. This is also enforced |
| during ->lookup() to provide limited protection against offline |
| attacks that try to disable or downgrade encryption in known locations |
| where applications may later write sensitive data. It is recommended |
| that systems implementing a form of "verified boot" take advantage of |
| this by validating all top-level encryption policies prior to access. |
| |
| Implementation details |
| ====================== |
| |
| Encryption context |
| ------------------ |
| |
| An encryption policy is represented on-disk by a :c:type:`struct |
| fscrypt_context_v1` or a :c:type:`struct fscrypt_context_v2`. It is |
| up to individual filesystems to decide where to store it, but normally |
| it would be stored in a hidden extended attribute. It should *not* be |
| exposed by the xattr-related system calls such as getxattr() and |
| setxattr() because of the special semantics of the encryption xattr. |
| (In particular, there would be much confusion if an encryption policy |
| were to be added to or removed from anything other than an empty |
| directory.) These structs are defined as follows:: |
| |
| #define FS_KEY_DERIVATION_NONCE_SIZE 16 |
| |
| #define FSCRYPT_KEY_DESCRIPTOR_SIZE 8 |
| struct fscrypt_context_v1 { |
| u8 version; |
| u8 contents_encryption_mode; |
| u8 filenames_encryption_mode; |
| u8 flags; |
| u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; |
| u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE]; |
| }; |
| |
| #define FSCRYPT_KEY_IDENTIFIER_SIZE 16 |
| struct fscrypt_context_v2 { |
| u8 version; |
| u8 contents_encryption_mode; |
| u8 filenames_encryption_mode; |
| u8 flags; |
| u8 __reserved[4]; |
| u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]; |
| u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE]; |
| }; |
| |
| The context structs contain the same information as the corresponding |
| policy structs (see `Setting an encryption policy`_), except that the |
| context structs also contain a nonce. The nonce is randomly generated |
| by the kernel and is used as KDF input or as a tweak to cause |
| different files to be encrypted differently; see `Per-file keys`_ and |
| `DIRECT_KEY policies`_. |
| |
| Data path changes |
| ----------------- |
| |
| For the read path (->readpage()) of regular files, filesystems can |
| read the ciphertext into the page cache and decrypt it in-place. The |
| page lock must be held until decryption has finished, to prevent the |
| page from becoming visible to userspace prematurely. |
| |
| For the write path (->writepage()) of regular files, filesystems |
| cannot encrypt data in-place in the page cache, since the cached |
| plaintext must be preserved. Instead, filesystems must encrypt into a |
| temporary buffer or "bounce page", then write out the temporary |
| buffer. Some filesystems, such as UBIFS, already use temporary |
| buffers regardless of encryption. Other filesystems, such as ext4 and |
| F2FS, have to allocate bounce pages specially for encryption. |
| |
| Filename hashing and encoding |
| ----------------------------- |
| |
| Modern filesystems accelerate directory lookups by using indexed |
| directories. An indexed directory is organized as a tree keyed by |
| filename hashes. When a ->lookup() is requested, the filesystem |
| normally hashes the filename being looked up so that it can quickly |
| find the corresponding directory entry, if any. |
| |
| With encryption, lookups must be supported and efficient both with and |
| without the encryption key. Clearly, it would not work to hash the |
| plaintext filenames, since the plaintext filenames are unavailable |
| without the key. (Hashing the plaintext filenames would also make it |
| impossible for the filesystem's fsck tool to optimize encrypted |
| directories.) Instead, filesystems hash the ciphertext filenames, |
| i.e. the bytes actually stored on-disk in the directory entries. When |
| asked to do a ->lookup() with the key, the filesystem just encrypts |
| the user-supplied name to get the ciphertext. |
| |
| Lookups without the key are more complicated. The raw ciphertext may |
| contain the ``\0`` and ``/`` characters, which are illegal in |
| filenames. Therefore, readdir() must base64-encode the ciphertext for |
| presentation. For most filenames, this works fine; on ->lookup(), the |
| filesystem just base64-decodes the user-supplied name to get back to |
| the raw ciphertext. |
| |
| However, for very long filenames, base64 encoding would cause the |
| filename length to exceed NAME_MAX. To prevent this, readdir() |
| actually presents long filenames in an abbreviated form which encodes |
| a strong "hash" of the ciphertext filename, along with the optional |
| filesystem-specific hash(es) needed for directory lookups. This |
| allows the filesystem to still, with a high degree of confidence, map |
| the filename given in ->lookup() back to a particular directory entry |
| that was previously listed by readdir(). See :c:type:`struct |
| fscrypt_digested_name` in the source for more details. |
| |
| Note that the precise way that filenames are presented to userspace |
| without the key is subject to change in the future. It is only meant |
| as a way to temporarily present valid filenames so that commands like |
| ``rm -r`` work as expected on encrypted directories. |
| |
| Tests |
| ===== |
| |
| To test fscrypt, use xfstests, which is Linux's de facto standard |
| filesystem test suite. First, run all the tests in the "encrypt" |
| group on the relevant filesystem(s). For example, to test ext4 and |
| f2fs encryption using `kvm-xfstests |
| <https://github.com/tytso/xfstests-bld/blob/master/Documentation/kvm-quickstart.md>`_:: |
| |
| kvm-xfstests -c ext4,f2fs -g encrypt |
| |
| UBIFS encryption can also be tested this way, but it should be done in |
| a separate command, and it takes some time for kvm-xfstests to set up |
| emulated UBI volumes:: |
| |
| kvm-xfstests -c ubifs -g encrypt |
| |
| No tests should fail. However, tests that use non-default encryption |
| modes (e.g. generic/549 and generic/550) will be skipped if the needed |
| algorithms were not built into the kernel's crypto API. Also, tests |
| that access the raw block device (e.g. generic/399, generic/548, |
| generic/549, generic/550) will be skipped on UBIFS. |
| |
| Besides running the "encrypt" group tests, for ext4 and f2fs it's also |
| possible to run most xfstests with the "test_dummy_encryption" mount |
| option. This option causes all new files to be automatically |
| encrypted with a dummy key, without having to make any API calls. |
| This tests the encrypted I/O paths more thoroughly. To do this with |
| kvm-xfstests, use the "encrypt" filesystem configuration:: |
| |
| kvm-xfstests -c ext4/encrypt,f2fs/encrypt -g auto |
| |
| Because this runs many more tests than "-g encrypt" does, it takes |
| much longer to run; so also consider using `gce-xfstests |
| <https://github.com/tytso/xfstests-bld/blob/master/Documentation/gce-xfstests.md>`_ |
| instead of kvm-xfstests:: |
| |
| gce-xfstests -c ext4/encrypt,f2fs/encrypt -g auto |