fscrypt: add support for hardware-wrapped keys

Add support for hardware-wrapped keys to fscrypt.  Such keys are
protected from certain attacks, such as cold boot attacks.  For more
information, see the "Hardware-wrapped keys" section of
Documentation/block/inline-encryption.rst.

To support hardware-wrapped keys in fscrypt, we allow the fscrypt master
keys to be hardware-wrapped.  File contents encryption is done by
passing the wrapped key to the inline encryption hardware via
blk-crypto.  Other fscrypt operations such as filenames encryption
continue to be done by the kernel, using the "software secret" which the
hardware derives.  For more information, see the documentation which
this patch adds to Documentation/filesystems/fscrypt.rst.

Note that this feature doesn't require any filesystem-specific changes.
However it does depend on inline encryption support, and thus currently
it is only applicable to ext4 and f2fs.

The version of this feature introduced by this patch is mostly
equivalent to the version that has existed downstream in the Android
Common Kernels since 2020.  However, a couple fixes are included.
First, the flags field in struct fscrypt_add_key_arg is now placed in
the proper location.  Second, key identifiers for HW-wrapped keys are
now derived using a distinct HKDF context byte; this fixes a bug where a
raw key could have the same identifier as a HW-wrapped key.  Note that
as a result of these fixes, the version of this feature introduced by
this patch is not UAPI or on-disk format compatible with the version in
the Android Common Kernels, though the divergence is limited to just
those specific fixes.  This version should be used going forwards.

This patch has been heavily rewritten from the original version by
Gaurav Kashyap <quic_gaurkash@quicinc.com> and
Barani Muthukumaran <bmuthuku@codeaurora.org>.

Tested-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org> # sm8650
Link: https://lore.kernel.org/r/20250404225859.172344-1-ebiggers@kernel.org
Signed-off-by: Eric Biggers <ebiggers@google.com>
This commit is contained in:
Eric Biggers 2025-04-04 15:58:59 -07:00
parent 0af2f6be1b
commit c07d3aede2
8 changed files with 410 additions and 105 deletions

View file

@ -70,7 +70,7 @@ Online attacks
-------------- --------------
fscrypt (and storage encryption in general) can only provide limited fscrypt (and storage encryption in general) can only provide limited
protection, if any at all, against online attacks. In detail: protection against online attacks. In detail:
Side-channel attacks Side-channel attacks
~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
@ -99,16 +99,23 @@ Therefore, any encryption-specific access control checks would merely
be enforced by kernel *code* and therefore would be largely redundant be enforced by kernel *code* and therefore would be largely redundant
with the wide variety of access control mechanisms already available.) with the wide variety of access control mechanisms already available.)
Kernel memory compromise Read-only kernel memory compromise
~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
An attacker who compromises the system enough to read from arbitrary Unless `hardware-wrapped keys`_ are used, an attacker who gains the
memory, e.g. by mounting a physical attack or by exploiting a kernel ability to read from arbitrary kernel memory, e.g. by mounting a
security vulnerability, can compromise all encryption keys that are physical attack or by exploiting a kernel security vulnerability, can
currently in use. compromise all fscrypt keys that are currently in-use. This also
extends to cold boot attacks; if the system is suddenly powered off,
keys the system was using may remain in memory for a short time.
However, fscrypt allows encryption keys to be removed from the kernel, However, if hardware-wrapped keys are used, then the fscrypt master
which may protect them from later compromise. keys and file contents encryption keys (but not other types of fscrypt
subkeys such as filenames encryption keys) are protected from
compromises of arbitrary kernel memory.
In addition, 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 In more detail, the FS_IOC_REMOVE_ENCRYPTION_KEY ioctl (or the
FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS ioctl) can wipe a master FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS ioctl) can wipe a master
@ -144,6 +151,24 @@ However, these ioctls have some limitations:
accelerator hardware (if used by the crypto API to implement any of accelerator hardware (if used by the crypto API to implement any of
the algorithms), or in other places not explicitly considered here. the algorithms), or in other places not explicitly considered here.
Full system compromise
~~~~~~~~~~~~~~~~~~~~~~
An attacker who gains "root" access and/or the ability to execute
arbitrary kernel code can freely exfiltrate data that is protected by
any in-use fscrypt keys. Thus, usually fscrypt provides no meaningful
protection in this scenario. (Data that is protected by a key that is
absent throughout the entire attack remains protected, modulo the
limitations of key removal mentioned above in the case where the key
was removed prior to the attack.)
However, if `hardware-wrapped keys`_ are used, such attackers will be
unable to exfiltrate the master keys or file contents keys in a form
that will be usable after the system is powered off. This may be
useful if the attacker is significantly time-limited and/or
bandwidth-limited, so they can only exfiltrate some data and need to
rely on a later offline attack to exfiltrate the rest of it.
Limitations of v1 policies Limitations of v1 policies
~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -170,6 +195,10 @@ policies on all new encrypted directories.
Key hierarchy Key hierarchy
============= =============
Note: this section assumes the use of raw keys rather than
hardware-wrapped keys. The use of hardware-wrapped keys modifies the
key hierarchy slightly. For details, see `Hardware-wrapped keys`_.
Master Keys Master Keys
----------- -----------
@ -832,7 +861,9 @@ a pointer to struct fscrypt_add_key_arg, defined as follows::
struct fscrypt_key_specifier key_spec; struct fscrypt_key_specifier key_spec;
__u32 raw_size; __u32 raw_size;
__u32 key_id; __u32 key_id;
__u32 __reserved[8]; #define FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED 0x00000001
__u32 flags;
__u32 __reserved[7];
__u8 raw[]; __u8 raw[];
}; };
@ -851,7 +882,7 @@ a pointer to struct fscrypt_add_key_arg, defined as follows::
struct fscrypt_provisioning_key_payload { struct fscrypt_provisioning_key_payload {
__u32 type; __u32 type;
__u32 __reserved; __u32 flags;
__u8 raw[]; __u8 raw[];
}; };
@ -879,24 +910,32 @@ as follows:
Alternatively, if ``key_id`` is nonzero, this field must be 0, since Alternatively, if ``key_id`` is nonzero, this field must be 0, since
in that case the size is implied by the specified Linux keyring key. in that case the size is implied by the specified Linux keyring key.
- ``key_id`` is 0 if the raw key is given directly in the ``raw`` - ``key_id`` is 0 if the key is given directly in the ``raw`` field.
field. Otherwise ``key_id`` is the ID of a Linux keyring key of Otherwise ``key_id`` is the ID of a Linux keyring key of type
type "fscrypt-provisioning" whose payload is "fscrypt-provisioning" whose payload is struct
struct fscrypt_provisioning_key_payload whose ``raw`` field contains fscrypt_provisioning_key_payload whose ``raw`` field contains the
the raw key and whose ``type`` field matches ``key_spec.type``. key, whose ``type`` field matches ``key_spec.type``, and whose
Since ``raw`` is variable-length, the total size of this key's ``flags`` field matches ``flags``. Since ``raw`` is
payload must be ``sizeof(struct fscrypt_provisioning_key_payload)`` variable-length, the total size of this key's payload must be
plus the raw key size. The process must have Search permission on ``sizeof(struct fscrypt_provisioning_key_payload)`` plus the number
this key. of key bytes. The process must have Search permission on this key.
Most users should leave this 0 and specify the raw key directly. Most users should leave this 0 and specify the key directly. The
The support for specifying a Linux keyring key is intended mainly to support for specifying a Linux keyring key is intended mainly to
allow re-adding keys after a filesystem is unmounted and re-mounted, allow re-adding keys after a filesystem is unmounted and re-mounted,
without having to store the raw keys in userspace memory. without having to store the keys in userspace memory.
- ``flags`` contains optional flags from ``<linux/fscrypt.h>``:
- FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED: This denotes that the key is a
hardware-wrapped key. See `Hardware-wrapped keys`_. This flag
can't be used if FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR is used.
- ``raw`` is a variable-length field which must contain the actual - ``raw`` is a variable-length field which must contain the actual
key, ``raw_size`` bytes long. Alternatively, if ``key_id`` is key, ``raw_size`` bytes long. Alternatively, if ``key_id`` is
nonzero, then this field is unused. nonzero, then this field is unused. Note that despite being named
``raw``, if FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED is specified then it
will contain a wrapped key, not a raw key.
For v2 policy keys, the kernel keeps track of which user (identified 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 by effective user ID) added the key, and only allows the key to be
@ -908,8 +947,8 @@ prevent that other user from unexpectedly removing it. Therefore,
FS_IOC_ADD_ENCRYPTION_KEY may also be used to add a v2 policy key 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, *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 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 current user, rather than actually add the key again (but the key must
must still be provided, as a proof of knowledge). still be provided, as a proof of knowledge).
FS_IOC_ADD_ENCRYPTION_KEY returns 0 if either the key or a claim to FS_IOC_ADD_ENCRYPTION_KEY returns 0 if either the key or a claim to
the key was either added or already exists. the key was either added or already exists.
@ -918,20 +957,23 @@ FS_IOC_ADD_ENCRYPTION_KEY can fail with the following errors:
- ``EACCES``: FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR was specified, but the - ``EACCES``: FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR was specified, but the
caller does not have the CAP_SYS_ADMIN capability in the initial caller does not have the CAP_SYS_ADMIN capability in the initial
user namespace; or the raw key was specified by Linux key ID but the user namespace; or the key was specified by Linux key ID but the
process lacks Search permission on the key. process lacks Search permission on the key.
- ``EBADMSG``: invalid hardware-wrapped key
- ``EDQUOT``: the key quota for this user would be exceeded by adding - ``EDQUOT``: the key quota for this user would be exceeded by adding
the key the key
- ``EINVAL``: invalid key size or key specifier type, or reserved bits - ``EINVAL``: invalid key size or key specifier type, or reserved bits
were set were set
- ``EKEYREJECTED``: the raw key was specified by Linux key ID, but the - ``EKEYREJECTED``: the key was specified by Linux key ID, but the key
key has the wrong type has the wrong type
- ``ENOKEY``: the raw key was specified by Linux key ID, but no key - ``ENOKEY``: the key was specified by Linux key ID, but no key exists
exists with that ID with that ID
- ``ENOTTY``: this type of filesystem does not implement encryption - ``ENOTTY``: this type of filesystem does not implement encryption
- ``EOPNOTSUPP``: the kernel was not configured with encryption - ``EOPNOTSUPP``: the kernel was not configured with encryption
support for this filesystem, or the filesystem superblock has not support for this filesystem, or the filesystem superblock has not
had encryption enabled on it had encryption enabled on it; or a hardware wrapped key was specified
but the filesystem does not support inline encryption or the hardware
does not support hardware-wrapped keys
Legacy method Legacy method
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
@ -994,9 +1036,8 @@ or removed by non-root users.
These ioctls don't work on keys that were added via the legacy These ioctls don't work on keys that were added via the legacy
process-subscribed keyrings mechanism. process-subscribed keyrings mechanism.
Before using these ioctls, read the `Kernel memory compromise`_ Before using these ioctls, read the `Online attacks`_ section for a
section for a discussion of the security goals and limitations of discussion of the security goals and limitations of these ioctls.
these ioctls.
FS_IOC_REMOVE_ENCRYPTION_KEY FS_IOC_REMOVE_ENCRYPTION_KEY
~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -1316,7 +1357,8 @@ inline encryption hardware doesn't have the needed crypto capabilities
(e.g. support for the needed encryption algorithm and data unit size) (e.g. support for the needed encryption algorithm and data unit size)
and where blk-crypto-fallback is unusable. (For blk-crypto-fallback and where blk-crypto-fallback is unusable. (For blk-crypto-fallback
to be usable, it must be enabled in the kernel configuration with to be usable, it must be enabled in the kernel configuration with
CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y.) CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y, and the file must be
protected by a raw key rather than a hardware-wrapped key.)
Currently fscrypt always uses the filesystem block size (which is Currently fscrypt always uses the filesystem block size (which is
usually 4096 bytes) as the data unit size. Therefore, it can only use usually 4096 bytes) as the data unit size. Therefore, it can only use
@ -1324,7 +1366,76 @@ inline encryption hardware that supports that data unit size.
Inline encryption doesn't affect the ciphertext or other aspects of Inline encryption doesn't affect the ciphertext or other aspects of
the on-disk format, so users may freely switch back and forth between the on-disk format, so users may freely switch back and forth between
using "inlinecrypt" and not using "inlinecrypt". using "inlinecrypt" and not using "inlinecrypt". An exception is that
files that are protected by a hardware-wrapped key can only be
encrypted/decrypted by the inline encryption hardware and therefore
can only be accessed when the "inlinecrypt" mount option is used. For
more information about hardware-wrapped keys, see below.
Hardware-wrapped keys
---------------------
fscrypt supports using *hardware-wrapped keys* when the inline
encryption hardware supports it. Such keys are only present in kernel
memory in wrapped (encrypted) form; they can only be unwrapped
(decrypted) by the inline encryption hardware and are temporally bound
to the current boot. This prevents the keys from being compromised if
kernel memory is leaked. This is done without limiting the number of
keys that can be used and while still allowing the execution of
cryptographic tasks that are tied to the same key but can't use inline
encryption hardware, e.g. filenames encryption.
Note that hardware-wrapped keys aren't specific to fscrypt; they are a
block layer feature (part of *blk-crypto*). For more details about
hardware-wrapped keys, see the block layer documentation at
:ref:`Documentation/block/inline-encryption.rst
<hardware_wrapped_keys>`. The rest of this section just focuses on
the details of how fscrypt can use hardware-wrapped keys.
fscrypt supports hardware-wrapped keys by allowing the fscrypt master
keys to be hardware-wrapped keys as an alternative to raw keys. To
add a hardware-wrapped key with `FS_IOC_ADD_ENCRYPTION_KEY`_,
userspace must specify FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED in the
``flags`` field of struct fscrypt_add_key_arg and also in the
``flags`` field of struct fscrypt_provisioning_key_payload when
applicable. The key must be in ephemerally-wrapped form, not
long-term wrapped form.
Some limitations apply. First, files protected by a hardware-wrapped
key are tied to the system's inline encryption hardware. Therefore
they can only be accessed when the "inlinecrypt" mount option is used,
and they can't be included in portable filesystem images. Second,
currently the hardware-wrapped key support is only compatible with
`IV_INO_LBLK_64 policies`_ and `IV_INO_LBLK_32 policies`_, as it
assumes that there is just one file contents encryption key per
fscrypt master key rather than one per file. Future work may address
this limitation by passing per-file nonces down the storage stack to
allow the hardware to derive per-file keys.
Implementation-wise, to encrypt/decrypt the contents of files that are
protected by a hardware-wrapped key, fscrypt uses blk-crypto,
attaching the hardware-wrapped key to the bio crypt contexts. As is
the case with raw keys, the block layer will program the key into a
keyslot when it isn't already in one. However, when programming a
hardware-wrapped key, the hardware doesn't program the given key
directly into a keyslot but rather unwraps it (using the hardware's
ephemeral wrapping key) and derives the inline encryption key from it.
The inline encryption key is the key that actually gets programmed
into a keyslot, and it is never exposed to software.
However, fscrypt doesn't just do file contents encryption; it also
uses its master keys to derive filenames encryption keys, key
identifiers, and sometimes some more obscure types of subkeys such as
dirhash keys. So even with file contents encryption out of the
picture, fscrypt still needs a raw key to work with. To get such a
key from a hardware-wrapped key, fscrypt asks the inline encryption
hardware to derive a cryptographically isolated "software secret" from
the hardware-wrapped key. fscrypt uses this "software secret" to key
its KDF to derive all subkeys other than file contents keys.
Note that this implies that the hardware-wrapped key feature only
protects the file contents encryption keys. It doesn't protect other
fscrypt subkeys such as filenames encryption keys.
Direct I/O support Direct I/O support
================== ==================

View file

@ -12,6 +12,7 @@
#define _FSCRYPT_PRIVATE_H #define _FSCRYPT_PRIVATE_H
#include <linux/fscrypt.h> #include <linux/fscrypt.h>
#include <linux/minmax.h>
#include <linux/siphash.h> #include <linux/siphash.h>
#include <crypto/hash.h> #include <crypto/hash.h>
#include <linux/blk-crypto.h> #include <linux/blk-crypto.h>
@ -27,6 +28,23 @@
*/ */
#define FSCRYPT_MIN_KEY_SIZE 16 #define FSCRYPT_MIN_KEY_SIZE 16
/* Maximum size of a raw fscrypt master key */
#define FSCRYPT_MAX_RAW_KEY_SIZE 64
/* Maximum size of a hardware-wrapped fscrypt master key */
#define FSCRYPT_MAX_HW_WRAPPED_KEY_SIZE BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE
/* Maximum size of an fscrypt master key across both key types */
#define FSCRYPT_MAX_ANY_KEY_SIZE \
MAX(FSCRYPT_MAX_RAW_KEY_SIZE, FSCRYPT_MAX_HW_WRAPPED_KEY_SIZE)
/*
* FSCRYPT_MAX_KEY_SIZE is defined in the UAPI header, but the addition of
* hardware-wrapped keys has made it misleading as it's only for raw keys.
* Don't use it in kernel code; use one of the above constants instead.
*/
#undef FSCRYPT_MAX_KEY_SIZE
#define FSCRYPT_CONTEXT_V1 1 #define FSCRYPT_CONTEXT_V1 1
#define FSCRYPT_CONTEXT_V2 2 #define FSCRYPT_CONTEXT_V2 2
@ -360,13 +378,15 @@ int fscrypt_init_hkdf(struct fscrypt_hkdf *hkdf, const u8 *master_key,
* outputs are unique and cryptographically isolated, i.e. knowledge of one * outputs are unique and cryptographically isolated, i.e. knowledge of one
* output doesn't reveal another. * output doesn't reveal another.
*/ */
#define HKDF_CONTEXT_KEY_IDENTIFIER 1 /* info=<empty> */ #define HKDF_CONTEXT_KEY_IDENTIFIER_FOR_RAW_KEY 1 /* info=<empty> */
#define HKDF_CONTEXT_PER_FILE_ENC_KEY 2 /* info=file_nonce */ #define HKDF_CONTEXT_PER_FILE_ENC_KEY 2 /* info=file_nonce */
#define HKDF_CONTEXT_DIRECT_KEY 3 /* info=mode_num */ #define HKDF_CONTEXT_DIRECT_KEY 3 /* info=mode_num */
#define HKDF_CONTEXT_IV_INO_LBLK_64_KEY 4 /* info=mode_num||fs_uuid */ #define HKDF_CONTEXT_IV_INO_LBLK_64_KEY 4 /* info=mode_num||fs_uuid */
#define HKDF_CONTEXT_DIRHASH_KEY 5 /* info=file_nonce */ #define HKDF_CONTEXT_DIRHASH_KEY 5 /* info=file_nonce */
#define HKDF_CONTEXT_IV_INO_LBLK_32_KEY 6 /* info=mode_num||fs_uuid */ #define HKDF_CONTEXT_IV_INO_LBLK_32_KEY 6 /* info=mode_num||fs_uuid */
#define HKDF_CONTEXT_INODE_HASH_KEY 7 /* info=<empty> */ #define HKDF_CONTEXT_INODE_HASH_KEY 7 /* info=<empty> */
#define HKDF_CONTEXT_KEY_IDENTIFIER_FOR_HW_WRAPPED_KEY \
8 /* info=<empty> */
int fscrypt_hkdf_expand(const struct fscrypt_hkdf *hkdf, u8 context, int fscrypt_hkdf_expand(const struct fscrypt_hkdf *hkdf, u8 context,
const u8 *info, unsigned int infolen, const u8 *info, unsigned int infolen,
@ -376,7 +396,8 @@ void fscrypt_destroy_hkdf(struct fscrypt_hkdf *hkdf);
/* inline_crypt.c */ /* inline_crypt.c */
#ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci); int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci,
bool is_hw_wrapped_key);
static inline bool static inline bool
fscrypt_using_inline_encryption(const struct fscrypt_inode_info *ci) fscrypt_using_inline_encryption(const struct fscrypt_inode_info *ci)
@ -385,12 +406,17 @@ fscrypt_using_inline_encryption(const struct fscrypt_inode_info *ci)
} }
int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
const u8 *raw_key, const u8 *key_bytes, size_t key_size,
bool is_hw_wrapped,
const struct fscrypt_inode_info *ci); const struct fscrypt_inode_info *ci);
void fscrypt_destroy_inline_crypt_key(struct super_block *sb, void fscrypt_destroy_inline_crypt_key(struct super_block *sb,
struct fscrypt_prepared_key *prep_key); struct fscrypt_prepared_key *prep_key);
int fscrypt_derive_sw_secret(struct super_block *sb,
const u8 *wrapped_key, size_t wrapped_key_size,
u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]);
/* /*
* Check whether the crypto transform or blk-crypto key has been allocated in * Check whether the crypto transform or blk-crypto key has been allocated in
* @prep_key, depending on which encryption implementation the file will use. * @prep_key, depending on which encryption implementation the file will use.
@ -414,7 +440,8 @@ fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key,
#else /* CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ #else /* CONFIG_FS_ENCRYPTION_INLINE_CRYPT */
static inline int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci) static inline int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci,
bool is_hw_wrapped_key)
{ {
return 0; return 0;
} }
@ -427,7 +454,8 @@ fscrypt_using_inline_encryption(const struct fscrypt_inode_info *ci)
static inline int static inline int
fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
const u8 *raw_key, const u8 *key_bytes, size_t key_size,
bool is_hw_wrapped,
const struct fscrypt_inode_info *ci) const struct fscrypt_inode_info *ci)
{ {
WARN_ON_ONCE(1); WARN_ON_ONCE(1);
@ -440,6 +468,15 @@ fscrypt_destroy_inline_crypt_key(struct super_block *sb,
{ {
} }
static inline int
fscrypt_derive_sw_secret(struct super_block *sb,
const u8 *wrapped_key, size_t wrapped_key_size,
u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE])
{
fscrypt_warn(NULL, "kernel doesn't support hardware-wrapped keys");
return -EOPNOTSUPP;
}
static inline bool static inline bool
fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key, fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key,
const struct fscrypt_inode_info *ci) const struct fscrypt_inode_info *ci)
@ -456,20 +493,38 @@ fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key,
struct fscrypt_master_key_secret { struct fscrypt_master_key_secret {
/* /*
* For v2 policy keys: HKDF context keyed by this master key. * The KDF with which subkeys of this key can be derived.
* For v1 policy keys: not set (hkdf.hmac_tfm == NULL). *
* For v1 policy keys, this isn't applicable and won't be set.
* Otherwise, this KDF will be keyed by this master key if
* ->is_hw_wrapped=false, or by the "software secret" that hardware
* derived from this master key if ->is_hw_wrapped=true.
*/ */
struct fscrypt_hkdf hkdf; struct fscrypt_hkdf hkdf;
/* /*
* Size of the raw key in bytes. This remains set even if ->raw was * True if this key is a hardware-wrapped key; false if this key is a
* raw key (i.e. a "software key"). For v1 policy keys this will always
* be false, as v1 policy support is a legacy feature which doesn't
* support newer functionality such as hardware-wrapped keys.
*/
bool is_hw_wrapped;
/*
* Size of the key in bytes. This remains set even if ->bytes was
* zeroized due to no longer being needed. I.e. we still remember the * zeroized due to no longer being needed. I.e. we still remember the
* size of the key even if we don't need to remember the key itself. * size of the key even if we don't need to remember the key itself.
*/ */
u32 size; u32 size;
/* For v1 policy keys: the raw key. Wiped for v2 policy keys. */ /*
u8 raw[FSCRYPT_MAX_KEY_SIZE]; * The bytes of the key, when still needed. This can be either a raw
* key or a hardware-wrapped key, as indicated by ->is_hw_wrapped. In
* the case of a raw, v2 policy key, there is no need to remember the
* actual key separately from ->hkdf so this field will be zeroized as
* soon as ->hkdf is initialized.
*/
u8 bytes[FSCRYPT_MAX_ANY_KEY_SIZE];
} __randomize_layout; } __randomize_layout;

View file

@ -1,6 +1,8 @@
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
/* /*
* This is used to derive keys from the fscrypt master keys. * This is used to derive keys from the fscrypt master keys (or from the
* "software secrets" which hardware derives from the fscrypt master keys, in
* the case that the fscrypt master keys are hardware-wrapped keys).
* *
* Copyright 2019 Google LLC * Copyright 2019 Google LLC
*/ */

View file

@ -89,7 +89,8 @@ static void fscrypt_log_blk_crypto_impl(struct fscrypt_mode *mode,
} }
/* Enable inline encryption for this file if supported. */ /* Enable inline encryption for this file if supported. */
int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci) int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci,
bool is_hw_wrapped_key)
{ {
const struct inode *inode = ci->ci_inode; const struct inode *inode = ci->ci_inode;
struct super_block *sb = inode->i_sb; struct super_block *sb = inode->i_sb;
@ -130,7 +131,8 @@ int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci)
crypto_cfg.crypto_mode = ci->ci_mode->blk_crypto_mode; crypto_cfg.crypto_mode = ci->ci_mode->blk_crypto_mode;
crypto_cfg.data_unit_size = 1U << ci->ci_data_unit_bits; crypto_cfg.data_unit_size = 1U << ci->ci_data_unit_bits;
crypto_cfg.dun_bytes = fscrypt_get_dun_bytes(ci); crypto_cfg.dun_bytes = fscrypt_get_dun_bytes(ci);
crypto_cfg.key_type = BLK_CRYPTO_KEY_TYPE_RAW; crypto_cfg.key_type = is_hw_wrapped_key ?
BLK_CRYPTO_KEY_TYPE_HW_WRAPPED : BLK_CRYPTO_KEY_TYPE_RAW;
devs = fscrypt_get_devices(sb, &num_devs); devs = fscrypt_get_devices(sb, &num_devs);
if (IS_ERR(devs)) if (IS_ERR(devs))
@ -151,12 +153,15 @@ out_free_devs:
} }
int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
const u8 *raw_key, const u8 *key_bytes, size_t key_size,
bool is_hw_wrapped,
const struct fscrypt_inode_info *ci) const struct fscrypt_inode_info *ci)
{ {
const struct inode *inode = ci->ci_inode; const struct inode *inode = ci->ci_inode;
struct super_block *sb = inode->i_sb; struct super_block *sb = inode->i_sb;
enum blk_crypto_mode_num crypto_mode = ci->ci_mode->blk_crypto_mode; enum blk_crypto_mode_num crypto_mode = ci->ci_mode->blk_crypto_mode;
enum blk_crypto_key_type key_type = is_hw_wrapped ?
BLK_CRYPTO_KEY_TYPE_HW_WRAPPED : BLK_CRYPTO_KEY_TYPE_RAW;
struct blk_crypto_key *blk_key; struct blk_crypto_key *blk_key;
struct block_device **devs; struct block_device **devs;
unsigned int num_devs; unsigned int num_devs;
@ -167,9 +172,8 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
if (!blk_key) if (!blk_key)
return -ENOMEM; return -ENOMEM;
err = blk_crypto_init_key(blk_key, raw_key, ci->ci_mode->keysize, err = blk_crypto_init_key(blk_key, key_bytes, key_size, key_type,
BLK_CRYPTO_KEY_TYPE_RAW, crypto_mode, crypto_mode, fscrypt_get_dun_bytes(ci),
fscrypt_get_dun_bytes(ci),
1U << ci->ci_data_unit_bits); 1U << ci->ci_data_unit_bits);
if (err) { if (err) {
fscrypt_err(inode, "error %d initializing blk-crypto key", err); fscrypt_err(inode, "error %d initializing blk-crypto key", err);
@ -228,6 +232,34 @@ void fscrypt_destroy_inline_crypt_key(struct super_block *sb,
kfree_sensitive(blk_key); kfree_sensitive(blk_key);
} }
/*
* Ask the inline encryption hardware to derive the software secret from a
* hardware-wrapped key. Returns -EOPNOTSUPP if hardware-wrapped keys aren't
* supported on this filesystem or hardware.
*/
int fscrypt_derive_sw_secret(struct super_block *sb,
const u8 *wrapped_key, size_t wrapped_key_size,
u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE])
{
int err;
/* The filesystem must be mounted with -o inlinecrypt. */
if (!(sb->s_flags & SB_INLINECRYPT)) {
fscrypt_warn(NULL,
"%s: filesystem not mounted with inlinecrypt\n",
sb->s_id);
return -EOPNOTSUPP;
}
err = blk_crypto_derive_sw_secret(sb->s_bdev, wrapped_key,
wrapped_key_size, sw_secret);
if (err == -EOPNOTSUPP)
fscrypt_warn(NULL,
"%s: block device doesn't support hardware-wrapped keys\n",
sb->s_id);
return err;
}
bool __fscrypt_inode_uses_inline_crypto(const struct inode *inode) bool __fscrypt_inode_uses_inline_crypto(const struct inode *inode)
{ {
return inode->i_crypt_info->ci_inlinecrypt; return inode->i_crypt_info->ci_inlinecrypt;

View file

@ -149,11 +149,11 @@ static int fscrypt_user_key_instantiate(struct key *key,
struct key_preparsed_payload *prep) struct key_preparsed_payload *prep)
{ {
/* /*
* We just charge FSCRYPT_MAX_KEY_SIZE bytes to the user's key quota for * We just charge FSCRYPT_MAX_RAW_KEY_SIZE bytes to the user's key quota
* each key, regardless of the exact key size. The amount of memory * for each key, regardless of the exact key size. The amount of memory
* actually used is greater than the size of the raw key anyway. * actually used is greater than the size of the raw key anyway.
*/ */
return key_payload_reserve(key, FSCRYPT_MAX_KEY_SIZE); return key_payload_reserve(key, FSCRYPT_MAX_RAW_KEY_SIZE);
} }
static void fscrypt_user_key_describe(const struct key *key, struct seq_file *m) static void fscrypt_user_key_describe(const struct key *key, struct seq_file *m)
@ -558,20 +558,45 @@ static int add_master_key(struct super_block *sb,
int err; int err;
if (key_spec->type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) { if (key_spec->type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) {
err = fscrypt_init_hkdf(&secret->hkdf, secret->raw, u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE];
secret->size); u8 *kdf_key = secret->bytes;
unsigned int kdf_key_size = secret->size;
u8 keyid_kdf_ctx = HKDF_CONTEXT_KEY_IDENTIFIER_FOR_RAW_KEY;
/*
* For raw keys, the fscrypt master key is used directly as the
* fscrypt KDF key. For hardware-wrapped keys, we have to pass
* the master key to the hardware to derive the KDF key, which
* is then only used to derive non-file-contents subkeys.
*/
if (secret->is_hw_wrapped) {
err = fscrypt_derive_sw_secret(sb, secret->bytes,
secret->size, sw_secret);
if (err)
return err;
kdf_key = sw_secret;
kdf_key_size = sizeof(sw_secret);
/*
* To avoid weird behavior if someone manages to
* determine sw_secret and add it as a raw key, ensure
* that hardware-wrapped keys and raw keys will have
* different key identifiers by deriving their key
* identifiers using different KDF contexts.
*/
keyid_kdf_ctx =
HKDF_CONTEXT_KEY_IDENTIFIER_FOR_HW_WRAPPED_KEY;
}
err = fscrypt_init_hkdf(&secret->hkdf, kdf_key, kdf_key_size);
/*
* Now that the KDF context is initialized, the raw KDF key is
* no longer needed.
*/
memzero_explicit(kdf_key, kdf_key_size);
if (err) if (err)
return err; return err;
/*
* Now that the HKDF context is initialized, the raw key is no
* longer needed.
*/
memzero_explicit(secret->raw, secret->size);
/* Calculate the key identifier */ /* Calculate the key identifier */
err = fscrypt_hkdf_expand(&secret->hkdf, err = fscrypt_hkdf_expand(&secret->hkdf, keyid_kdf_ctx, NULL, 0,
HKDF_CONTEXT_KEY_IDENTIFIER, NULL, 0,
key_spec->u.identifier, key_spec->u.identifier,
FSCRYPT_KEY_IDENTIFIER_SIZE); FSCRYPT_KEY_IDENTIFIER_SIZE);
if (err) if (err)
@ -580,19 +605,36 @@ static int add_master_key(struct super_block *sb,
return do_add_master_key(sb, secret, key_spec); return do_add_master_key(sb, secret, key_spec);
} }
/*
* Validate the size of an fscrypt master key being added. Note that this is
* just an initial check, as we don't know which ciphers will be used yet.
* There is a stricter size check later when the key is actually used by a file.
*/
static inline bool fscrypt_valid_key_size(size_t size, u32 add_key_flags)
{
u32 max_size = (add_key_flags & FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED) ?
FSCRYPT_MAX_HW_WRAPPED_KEY_SIZE :
FSCRYPT_MAX_RAW_KEY_SIZE;
return size >= FSCRYPT_MIN_KEY_SIZE && size <= max_size;
}
static int fscrypt_provisioning_key_preparse(struct key_preparsed_payload *prep) static int fscrypt_provisioning_key_preparse(struct key_preparsed_payload *prep)
{ {
const struct fscrypt_provisioning_key_payload *payload = prep->data; const struct fscrypt_provisioning_key_payload *payload = prep->data;
if (prep->datalen < sizeof(*payload) + FSCRYPT_MIN_KEY_SIZE || if (prep->datalen < sizeof(*payload))
prep->datalen > sizeof(*payload) + FSCRYPT_MAX_KEY_SIZE) return -EINVAL;
if (!fscrypt_valid_key_size(prep->datalen - sizeof(*payload),
payload->flags))
return -EINVAL; return -EINVAL;
if (payload->type != FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR && if (payload->type != FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR &&
payload->type != FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) payload->type != FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER)
return -EINVAL; return -EINVAL;
if (payload->__reserved) if (payload->flags & ~FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED)
return -EINVAL; return -EINVAL;
prep->payload.data[0] = kmemdup(payload, prep->datalen, GFP_KERNEL); prep->payload.data[0] = kmemdup(payload, prep->datalen, GFP_KERNEL);
@ -636,21 +678,21 @@ static struct key_type key_type_fscrypt_provisioning = {
}; };
/* /*
* Retrieve the raw key from the Linux keyring key specified by 'key_id', and * Retrieve the key from the Linux keyring key specified by 'key_id', and store
* store it into 'secret'. * it into 'secret'.
* *
* The key must be of type "fscrypt-provisioning" and must have the field * The key must be of type "fscrypt-provisioning" and must have the 'type' and
* fscrypt_provisioning_key_payload::type set to 'type', indicating that it's * 'flags' field of the payload set to the given values, indicating that the key
* only usable with fscrypt with the particular KDF version identified by * is intended for use for the specified purpose. We don't use the "logon" key
* 'type'. We don't use the "logon" key type because there's no way to * type because there's no way to completely restrict the use of such keys; they
* completely restrict the use of such keys; they can be used by any kernel API * can be used by any kernel API that accepts "logon" keys and doesn't require a
* that accepts "logon" keys and doesn't require a specific service prefix. * specific service prefix.
* *
* The ability to specify the key via Linux keyring key is intended for cases * The ability to specify the key via Linux keyring key is intended for cases
* where userspace needs to re-add keys after the filesystem is unmounted and * where userspace needs to re-add keys after the filesystem is unmounted and
* re-mounted. Most users should just provide the raw key directly instead. * re-mounted. Most users should just provide the key directly instead.
*/ */
static int get_keyring_key(u32 key_id, u32 type, static int get_keyring_key(u32 key_id, u32 type, u32 flags,
struct fscrypt_master_key_secret *secret) struct fscrypt_master_key_secret *secret)
{ {
key_ref_t ref; key_ref_t ref;
@ -667,12 +709,16 @@ static int get_keyring_key(u32 key_id, u32 type,
goto bad_key; goto bad_key;
payload = key->payload.data[0]; payload = key->payload.data[0];
/* Don't allow fscrypt v1 keys to be used as v2 keys and vice versa. */ /*
if (payload->type != type) * Don't allow fscrypt v1 keys to be used as v2 keys and vice versa.
* Similarly, don't allow hardware-wrapped keys to be used as
* non-hardware-wrapped keys and vice versa.
*/
if (payload->type != type || payload->flags != flags)
goto bad_key; goto bad_key;
secret->size = key->datalen - sizeof(*payload); secret->size = key->datalen - sizeof(*payload);
memcpy(secret->raw, payload->raw, secret->size); memcpy(secret->bytes, payload->raw, secret->size);
err = 0; err = 0;
goto out_put; goto out_put;
@ -734,19 +780,28 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg)
return -EACCES; return -EACCES;
memset(&secret, 0, sizeof(secret)); memset(&secret, 0, sizeof(secret));
if (arg.flags) {
if (arg.flags & ~FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED)
return -EINVAL;
if (arg.key_spec.type != FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER)
return -EINVAL;
secret.is_hw_wrapped = true;
}
if (arg.key_id) { if (arg.key_id) {
if (arg.raw_size != 0) if (arg.raw_size != 0)
return -EINVAL; return -EINVAL;
err = get_keyring_key(arg.key_id, arg.key_spec.type, &secret); err = get_keyring_key(arg.key_id, arg.key_spec.type, arg.flags,
&secret);
if (err) if (err)
goto out_wipe_secret; goto out_wipe_secret;
} else { } else {
if (arg.raw_size < FSCRYPT_MIN_KEY_SIZE || if (!fscrypt_valid_key_size(arg.raw_size, arg.flags))
arg.raw_size > FSCRYPT_MAX_KEY_SIZE)
return -EINVAL; return -EINVAL;
secret.size = arg.raw_size; secret.size = arg.raw_size;
err = -EFAULT; err = -EFAULT;
if (copy_from_user(secret.raw, uarg->raw, secret.size)) if (copy_from_user(secret.bytes, uarg->raw, secret.size))
goto out_wipe_secret; goto out_wipe_secret;
} }
@ -770,13 +825,13 @@ EXPORT_SYMBOL_GPL(fscrypt_ioctl_add_key);
static void static void
fscrypt_get_test_dummy_secret(struct fscrypt_master_key_secret *secret) fscrypt_get_test_dummy_secret(struct fscrypt_master_key_secret *secret)
{ {
static u8 test_key[FSCRYPT_MAX_KEY_SIZE]; static u8 test_key[FSCRYPT_MAX_RAW_KEY_SIZE];
get_random_once(test_key, FSCRYPT_MAX_KEY_SIZE); get_random_once(test_key, sizeof(test_key));
memset(secret, 0, sizeof(*secret)); memset(secret, 0, sizeof(*secret));
secret->size = FSCRYPT_MAX_KEY_SIZE; secret->size = sizeof(test_key);
memcpy(secret->raw, test_key, FSCRYPT_MAX_KEY_SIZE); memcpy(secret->bytes, test_key, sizeof(test_key));
} }
int fscrypt_get_test_dummy_key_identifier( int fscrypt_get_test_dummy_key_identifier(
@ -787,10 +842,11 @@ int fscrypt_get_test_dummy_key_identifier(
fscrypt_get_test_dummy_secret(&secret); fscrypt_get_test_dummy_secret(&secret);
err = fscrypt_init_hkdf(&secret.hkdf, secret.raw, secret.size); err = fscrypt_init_hkdf(&secret.hkdf, secret.bytes, secret.size);
if (err) if (err)
goto out; goto out;
err = fscrypt_hkdf_expand(&secret.hkdf, HKDF_CONTEXT_KEY_IDENTIFIER, err = fscrypt_hkdf_expand(&secret.hkdf,
HKDF_CONTEXT_KEY_IDENTIFIER_FOR_RAW_KEY,
NULL, 0, key_identifier, NULL, 0, key_identifier,
FSCRYPT_KEY_IDENTIFIER_SIZE); FSCRYPT_KEY_IDENTIFIER_SIZE);
out: out:

View file

@ -153,7 +153,9 @@ int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key,
struct crypto_skcipher *tfm; struct crypto_skcipher *tfm;
if (fscrypt_using_inline_encryption(ci)) if (fscrypt_using_inline_encryption(ci))
return fscrypt_prepare_inline_crypt_key(prep_key, raw_key, ci); return fscrypt_prepare_inline_crypt_key(prep_key, raw_key,
ci->ci_mode->keysize,
false, ci);
tfm = fscrypt_allocate_skcipher(ci->ci_mode, raw_key, ci->ci_inode); tfm = fscrypt_allocate_skcipher(ci->ci_mode, raw_key, ci->ci_inode);
if (IS_ERR(tfm)) if (IS_ERR(tfm))
@ -195,14 +197,29 @@ static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci,
struct fscrypt_mode *mode = ci->ci_mode; struct fscrypt_mode *mode = ci->ci_mode;
const u8 mode_num = mode - fscrypt_modes; const u8 mode_num = mode - fscrypt_modes;
struct fscrypt_prepared_key *prep_key; struct fscrypt_prepared_key *prep_key;
u8 mode_key[FSCRYPT_MAX_KEY_SIZE]; u8 mode_key[FSCRYPT_MAX_RAW_KEY_SIZE];
u8 hkdf_info[sizeof(mode_num) + sizeof(sb->s_uuid)]; u8 hkdf_info[sizeof(mode_num) + sizeof(sb->s_uuid)];
unsigned int hkdf_infolen = 0; unsigned int hkdf_infolen = 0;
bool use_hw_wrapped_key = false;
int err; int err;
if (WARN_ON_ONCE(mode_num > FSCRYPT_MODE_MAX)) if (WARN_ON_ONCE(mode_num > FSCRYPT_MODE_MAX))
return -EINVAL; return -EINVAL;
if (mk->mk_secret.is_hw_wrapped && S_ISREG(inode->i_mode)) {
/* Using a hardware-wrapped key for file contents encryption */
if (!fscrypt_using_inline_encryption(ci)) {
if (sb->s_flags & SB_INLINECRYPT)
fscrypt_warn(ci->ci_inode,
"Hardware-wrapped key required, but no suitable inline encryption capabilities are available");
else
fscrypt_warn(ci->ci_inode,
"Hardware-wrapped keys require inline encryption (-o inlinecrypt)");
return -EINVAL;
}
use_hw_wrapped_key = true;
}
prep_key = &keys[mode_num]; prep_key = &keys[mode_num];
if (fscrypt_is_key_prepared(prep_key, ci)) { if (fscrypt_is_key_prepared(prep_key, ci)) {
ci->ci_enc_key = *prep_key; ci->ci_enc_key = *prep_key;
@ -214,6 +231,16 @@ static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci,
if (fscrypt_is_key_prepared(prep_key, ci)) if (fscrypt_is_key_prepared(prep_key, ci))
goto done_unlock; goto done_unlock;
if (use_hw_wrapped_key) {
err = fscrypt_prepare_inline_crypt_key(prep_key,
mk->mk_secret.bytes,
mk->mk_secret.size, true,
ci);
if (err)
goto out_unlock;
goto done_unlock;
}
BUILD_BUG_ON(sizeof(mode_num) != 1); BUILD_BUG_ON(sizeof(mode_num) != 1);
BUILD_BUG_ON(sizeof(sb->s_uuid) != 16); BUILD_BUG_ON(sizeof(sb->s_uuid) != 16);
BUILD_BUG_ON(sizeof(hkdf_info) != 17); BUILD_BUG_ON(sizeof(hkdf_info) != 17);
@ -336,6 +363,14 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_inode_info *ci,
{ {
int err; int err;
if (mk->mk_secret.is_hw_wrapped &&
!(ci->ci_policy.v2.flags & (FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 |
FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32))) {
fscrypt_warn(ci->ci_inode,
"Hardware-wrapped keys are only supported with IV_INO_LBLK policies");
return -EINVAL;
}
if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) { if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) {
/* /*
* DIRECT_KEY: instead of deriving per-file encryption keys, the * DIRECT_KEY: instead of deriving per-file encryption keys, the
@ -362,7 +397,7 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_inode_info *ci,
FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) { FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) {
err = fscrypt_setup_iv_ino_lblk_32_key(ci, mk); err = fscrypt_setup_iv_ino_lblk_32_key(ci, mk);
} else { } else {
u8 derived_key[FSCRYPT_MAX_KEY_SIZE]; u8 derived_key[FSCRYPT_MAX_RAW_KEY_SIZE];
err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf, err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf,
HKDF_CONTEXT_PER_FILE_ENC_KEY, HKDF_CONTEXT_PER_FILE_ENC_KEY,
@ -445,10 +480,6 @@ static int setup_file_encryption_key(struct fscrypt_inode_info *ci,
struct fscrypt_master_key *mk; struct fscrypt_master_key *mk;
int err; int err;
err = fscrypt_select_encryption_impl(ci);
if (err)
return err;
err = fscrypt_policy_to_key_spec(&ci->ci_policy, &mk_spec); err = fscrypt_policy_to_key_spec(&ci->ci_policy, &mk_spec);
if (err) if (err)
return err; return err;
@ -476,6 +507,10 @@ static int setup_file_encryption_key(struct fscrypt_inode_info *ci,
if (ci->ci_policy.version != FSCRYPT_POLICY_V1) if (ci->ci_policy.version != FSCRYPT_POLICY_V1)
return -ENOKEY; return -ENOKEY;
err = fscrypt_select_encryption_impl(ci, false);
if (err)
return err;
/* /*
* As a legacy fallback for v1 policies, search for the key in * As a legacy fallback for v1 policies, search for the key in
* the current task's subscribed keyrings too. Don't move this * the current task's subscribed keyrings too. Don't move this
@ -497,9 +532,21 @@ static int setup_file_encryption_key(struct fscrypt_inode_info *ci,
goto out_release_key; goto out_release_key;
} }
err = fscrypt_select_encryption_impl(ci, mk->mk_secret.is_hw_wrapped);
if (err)
goto out_release_key;
switch (ci->ci_policy.version) { switch (ci->ci_policy.version) {
case FSCRYPT_POLICY_V1: case FSCRYPT_POLICY_V1:
err = fscrypt_setup_v1_file_key(ci, mk->mk_secret.raw); if (WARN_ON_ONCE(mk->mk_secret.is_hw_wrapped)) {
/*
* This should never happen, as adding a v1 policy key
* that is hardware-wrapped isn't allowed.
*/
err = -EINVAL;
goto out_release_key;
}
err = fscrypt_setup_v1_file_key(ci, mk->mk_secret.bytes);
break; break;
case FSCRYPT_POLICY_V2: case FSCRYPT_POLICY_V2:
err = fscrypt_setup_v2_file_key(ci, mk, need_dirhash_key); err = fscrypt_setup_v2_file_key(ci, mk, need_dirhash_key);

View file

@ -118,7 +118,7 @@ find_and_lock_process_key(const char *prefix,
payload = (const struct fscrypt_key *)ukp->data; payload = (const struct fscrypt_key *)ukp->data;
if (ukp->datalen != sizeof(struct fscrypt_key) || if (ukp->datalen != sizeof(struct fscrypt_key) ||
payload->size < 1 || payload->size > FSCRYPT_MAX_KEY_SIZE) { payload->size < 1 || payload->size > sizeof(payload->raw)) {
fscrypt_warn(NULL, fscrypt_warn(NULL,
"key with description '%s' has invalid payload", "key with description '%s' has invalid payload",
key->description); key->description);
@ -149,7 +149,7 @@ struct fscrypt_direct_key {
const struct fscrypt_mode *dk_mode; const struct fscrypt_mode *dk_mode;
struct fscrypt_prepared_key dk_key; struct fscrypt_prepared_key dk_key;
u8 dk_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; u8 dk_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE];
u8 dk_raw[FSCRYPT_MAX_KEY_SIZE]; u8 dk_raw[FSCRYPT_MAX_RAW_KEY_SIZE];
}; };
static void free_direct_key(struct fscrypt_direct_key *dk) static void free_direct_key(struct fscrypt_direct_key *dk)

View file

@ -119,7 +119,7 @@ struct fscrypt_key_specifier {
*/ */
struct fscrypt_provisioning_key_payload { struct fscrypt_provisioning_key_payload {
__u32 type; __u32 type;
__u32 __reserved; __u32 flags;
__u8 raw[]; __u8 raw[];
}; };
@ -128,7 +128,9 @@ struct fscrypt_add_key_arg {
struct fscrypt_key_specifier key_spec; struct fscrypt_key_specifier key_spec;
__u32 raw_size; __u32 raw_size;
__u32 key_id; __u32 key_id;
__u32 __reserved[8]; #define FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED 0x00000001
__u32 flags;
__u32 __reserved[7];
__u8 raw[]; __u8 raw[];
}; };