| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| From: Eric Biggers <ebiggers@google.com> |
| Date: Thu, 21 Oct 2021 11:54:27 -0700 |
| Subject: ANDROID: fscrypt: add support for hardware-wrapped keys |
| |
| Add support for hardware-wrapped keys to fscrypt. Hardware-wrapped keys |
| are inline encryption keys which are only present in kernel memory in |
| ephemerally-wrapped form, and which can only be unwrapped by dedicated |
| hardware. 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, and we allow encryption policies to be |
| flagged as needing a hardware-wrapped key. 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. |
| |
| 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, not to ubifs or CephFS. |
| |
| This is a reworked version of a patch which was temporily reverted by |
| https://android-review.googlesource.com/c/kernel/common/+/1867364, and |
| which originated from |
| https://android-review.googlesource.com/c/kernel/common/+/1200864. |
| This is based on a version of this patch that I've proposed upstream |
| (https://lore.kernel.org/r/20211021181608.54127-4-ebiggers@kernel.org), |
| but by necessity it preserves the existing UAPI and on-disk format which |
| Android expects. I also dropped the changes to the documentation file. |
| |
| Bug: 160883801 |
| Change-Id: If4bb83f1188a5863184717c04cb8a064dc4ea168 |
| Signed-off-by: Eric Biggers <ebiggers@google.com> |
| --- |
| fs/crypto/fscrypt_private.h | 73 +++++++++++++++++++++++++++++---- |
| fs/crypto/hkdf.c | 4 +- |
| fs/crypto/inline_crypt.c | 65 ++++++++++++++++++++++++++++-- |
| fs/crypto/keyring.c | 69 ++++++++++++++++++++++--------- |
| fs/crypto/keysetup.c | 78 +++++++++++++++++++++++++++++++----- |
| fs/crypto/keysetup_v1.c | 5 ++- |
| include/uapi/linux/fscrypt.h | 5 ++- |
| 7 files changed, 254 insertions(+), 45 deletions(-) |
| |
| diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h |
| --- a/fs/crypto/fscrypt_private.h |
| +++ b/fs/crypto/fscrypt_private.h |
| @@ -27,6 +27,27 @@ |
| */ |
| #define FSCRYPT_MIN_KEY_SIZE 16 |
| |
| +/* Maximum size of a standard fscrypt master key */ |
| +#define FSCRYPT_MAX_STANDARD_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. |
| + * This should just use max(), but max() doesn't work in a struct definition. |
| + */ |
| +#define FSCRYPT_MAX_ANY_KEY_SIZE \ |
| + (FSCRYPT_MAX_HW_WRAPPED_KEY_SIZE > FSCRYPT_MAX_STANDARD_KEY_SIZE ? \ |
| + FSCRYPT_MAX_HW_WRAPPED_KEY_SIZE : FSCRYPT_MAX_STANDARD_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 standard 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_V2 2 |
| |
| @@ -335,7 +356,8 @@ void fscrypt_destroy_hkdf(struct fscrypt_hkdf *hkdf); |
| |
| /* inline_crypt.c */ |
| #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT |
| -int fscrypt_select_encryption_impl(struct fscrypt_info *ci); |
| +int fscrypt_select_encryption_impl(struct fscrypt_info *ci, |
| + bool is_hw_wrapped_key); |
| |
| static inline bool |
| fscrypt_using_inline_encryption(const struct fscrypt_info *ci) |
| @@ -345,10 +367,16 @@ fscrypt_using_inline_encryption(const struct fscrypt_info *ci) |
| |
| int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, |
| const u8 *raw_key, |
| + unsigned int raw_key_size, |
| + bool is_hw_wrapped, |
| const struct fscrypt_info *ci); |
| |
| void fscrypt_destroy_inline_crypt_key(struct fscrypt_prepared_key *prep_key); |
| |
| +int fscrypt_derive_sw_secret(struct super_block *sb, const u8 *wrapped_key, |
| + unsigned int wrapped_key_size, |
| + u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]); |
| + |
| /* |
| * Check whether the crypto transform or blk-crypto key has been allocated in |
| * @prep_key, depending on which encryption implementation the file will use. |
| @@ -359,7 +387,7 @@ fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key, |
| { |
| /* |
| * The two smp_load_acquire()'s here pair with the smp_store_release()'s |
| - * in fscrypt_prepare_inline_crypt_key() and fscrypt_prepare_key(). |
| + * in fscrypt_prepare_inline_crypt_key() and __fscrypt_prepare_key(). |
| * I.e., in some cases (namely, if this prep_key is a per-mode |
| * encryption key) another task can publish blk_key or tfm concurrently, |
| * executing a RELEASE barrier. We need to use smp_load_acquire() here |
| @@ -372,7 +400,8 @@ fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key, |
| |
| #else /* CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ |
| |
| -static inline int fscrypt_select_encryption_impl(struct fscrypt_info *ci) |
| +static inline int fscrypt_select_encryption_impl(struct fscrypt_info *ci, |
| + bool is_hw_wrapped_key) |
| { |
| return 0; |
| } |
| @@ -385,7 +414,8 @@ fscrypt_using_inline_encryption(const struct fscrypt_info *ci) |
| |
| static inline int |
| fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, |
| - const u8 *raw_key, |
| + const u8 *raw_key, unsigned int raw_key_size, |
| + bool is_hw_wrapped, |
| const struct fscrypt_info *ci) |
| { |
| WARN_ON(1); |
| @@ -397,6 +427,15 @@ fscrypt_destroy_inline_crypt_key(struct fscrypt_prepared_key *prep_key) |
| { |
| } |
| |
| +static inline int |
| +fscrypt_derive_sw_secret(struct super_block *sb, const u8 *wrapped_key, |
| + unsigned int 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 |
| fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key, |
| const struct fscrypt_info *ci) |
| @@ -413,11 +452,23 @@ fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key, |
| struct fscrypt_master_key_secret { |
| |
| /* |
| - * For v2 policy keys: HKDF context keyed by this master key. |
| - * For v1 policy keys: not set (hkdf.hmac_tfm == NULL). |
| + * The KDF with which subkeys of this key can be derived. |
| + * |
| + * 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; |
| |
| + /* |
| + * True if this key is a hardware-wrapped key; false if this key is a |
| + * standard 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 raw key in bytes. This remains set even if ->raw was |
| * zeroized due to no longer being needed. I.e. we still remember the |
| @@ -425,8 +476,14 @@ struct fscrypt_master_key_secret { |
| */ |
| u32 size; |
| |
| - /* For v1 policy keys: the raw key. Wiped for v2 policy keys. */ |
| - u8 raw[FSCRYPT_MAX_KEY_SIZE]; |
| + /* |
| + * The raw key which userspace provided, when still needed. This can be |
| + * either a standard key or a hardware-wrapped key, as indicated by |
| + * ->is_hw_wrapped. In the case of a standard, v2 policy key, there is |
| + * no need to remember the raw key separately from ->hkdf so this field |
| + * will be zeroized as soon as ->hkdf is initialized. |
| + */ |
| + u8 raw[FSCRYPT_MAX_ANY_KEY_SIZE]; |
| |
| } __randomize_layout; |
| |
| diff --git a/fs/crypto/hkdf.c b/fs/crypto/hkdf.c |
| --- a/fs/crypto/hkdf.c |
| +++ b/fs/crypto/hkdf.c |
| @@ -4,7 +4,9 @@ |
| * Function"), aka RFC 5869. See also the original paper (Krawczyk 2010): |
| * "Cryptographic Extraction and Key Derivation: The HKDF Scheme". |
| * |
| - * 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 |
| */ |
| diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c |
| --- a/fs/crypto/inline_crypt.c |
| +++ b/fs/crypto/inline_crypt.c |
| @@ -13,6 +13,7 @@ |
| */ |
| |
| #include <linux/blk-crypto.h> |
| +#include <linux/blk-crypto-profile.h> |
| #include <linux/blkdev.h> |
| #include <linux/buffer_head.h> |
| #include <linux/sched/mm.h> |
| @@ -64,7 +65,8 @@ static unsigned int fscrypt_get_dun_bytes(const struct fscrypt_info *ci) |
| } |
| |
| /* Enable inline encryption for this file if supported. */ |
| -int fscrypt_select_encryption_impl(struct fscrypt_info *ci) |
| +int fscrypt_select_encryption_impl(struct fscrypt_info *ci, |
| + bool is_hw_wrapped_key) |
| { |
| const struct inode *inode = ci->ci_inode; |
| struct super_block *sb = inode->i_sb; |
| @@ -105,7 +107,9 @@ int fscrypt_select_encryption_impl(struct fscrypt_info *ci) |
| crypto_cfg.crypto_mode = ci->ci_mode->blk_crypto_mode; |
| crypto_cfg.data_unit_size = sb->s_blocksize; |
| crypto_cfg.dun_bytes = fscrypt_get_dun_bytes(ci); |
| - crypto_cfg.key_type = BLK_CRYPTO_KEY_TYPE_STANDARD; |
| + crypto_cfg.key_type = |
| + is_hw_wrapped_key ? BLK_CRYPTO_KEY_TYPE_HW_WRAPPED : |
| + BLK_CRYPTO_KEY_TYPE_STANDARD; |
| num_devs = fscrypt_get_num_devices(sb); |
| devs = kmalloc_array(num_devs, sizeof(*devs), GFP_KERNEL); |
| if (!devs) |
| @@ -126,11 +130,15 @@ int fscrypt_select_encryption_impl(struct fscrypt_info *ci) |
| |
| int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, |
| const u8 *raw_key, |
| + unsigned int raw_key_size, |
| + bool is_hw_wrapped, |
| const struct fscrypt_info *ci) |
| { |
| const struct inode *inode = ci->ci_inode; |
| struct super_block *sb = inode->i_sb; |
| 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_STANDARD; |
| int num_devs = fscrypt_get_num_devices(sb); |
| int queue_refs = 0; |
| struct fscrypt_blk_crypto_key *blk_key; |
| @@ -144,8 +152,8 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, |
| blk_key->num_devs = num_devs; |
| fscrypt_get_devices(sb, num_devs, blk_key->devs); |
| |
| - err = blk_crypto_init_key(&blk_key->base, raw_key, ci->ci_mode->keysize, |
| - BLK_CRYPTO_KEY_TYPE_STANDARD, crypto_mode, |
| + err = blk_crypto_init_key(&blk_key->base, raw_key, raw_key_size, |
| + key_type, crypto_mode, |
| fscrypt_get_dun_bytes(ci), sb->s_blocksize); |
| if (err) { |
| fscrypt_err(inode, "error %d initializing blk-crypto key", err); |
| @@ -205,6 +213,55 @@ void fscrypt_destroy_inline_crypt_key(struct fscrypt_prepared_key *prep_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, |
| + unsigned int wrapped_key_size, |
| + u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]) |
| +{ |
| + struct blk_crypto_profile *profile; |
| + int num_devs; |
| + |
| + /* The filesystem must be mounted with -o inlinecrypt */ |
| + if (!(sb->s_flags & SB_INLINECRYPT)) |
| + return -EOPNOTSUPP; |
| + |
| + /* |
| + * Hardware-wrapped keys might be specific to a particular storage |
| + * device, so for now we don't allow them to be used if the filesystem |
| + * uses block devices with different crypto profiles. This way, there |
| + * is no ambiguity about which ->derive_sw_secret method to call. |
| + */ |
| + profile = bdev_get_queue(sb->s_bdev)->crypto_profile; |
| + num_devs = fscrypt_get_num_devices(sb); |
| + if (num_devs > 1) { |
| + struct request_queue **devs = |
| + kmalloc_array(num_devs, sizeof(*devs), GFP_KERNEL); |
| + int i; |
| + |
| + if (!devs) |
| + return -ENOMEM; |
| + |
| + fscrypt_get_devices(sb, num_devs, devs); |
| + |
| + for (i = 0; i < num_devs; i++) { |
| + if (devs[i]->crypto_profile != profile) { |
| + fscrypt_warn(NULL, |
| + "unsupported multi-device configuration for hardware-wrapped keys"); |
| + kfree(devs); |
| + return -EOPNOTSUPP; |
| + } |
| + } |
| + kfree(devs); |
| + } |
| + |
| + return blk_crypto_derive_sw_secret(profile, wrapped_key, |
| + wrapped_key_size, sw_secret); |
| +} |
| + |
| bool __fscrypt_inode_uses_inline_crypto(const struct inode *inode) |
| { |
| return inode->i_crypt_info->ci_inlinecrypt; |
| diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c |
| --- a/fs/crypto/keyring.c |
| +++ b/fs/crypto/keyring.c |
| @@ -103,11 +103,11 @@ static int fscrypt_user_key_instantiate(struct key *key, |
| struct key_preparsed_payload *prep) |
| { |
| /* |
| - * We just charge FSCRYPT_MAX_KEY_SIZE bytes to the user's key quota 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. |
| + * We just charge FSCRYPT_MAX_STANDARD_KEY_SIZE bytes to the user's key |
| + * quota 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. |
| */ |
| - return key_payload_reserve(key, FSCRYPT_MAX_KEY_SIZE); |
| + return key_payload_reserve(key, FSCRYPT_MAX_STANDARD_KEY_SIZE); |
| } |
| |
| static void fscrypt_user_key_describe(const struct key *key, struct seq_file *m) |
| @@ -479,20 +479,36 @@ static int add_master_key(struct super_block *sb, |
| int err; |
| |
| if (key_spec->type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) { |
| - err = fscrypt_init_hkdf(&secret->hkdf, secret->raw, |
| - secret->size); |
| - if (err) |
| - return err; |
| + u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]; |
| + u8 *kdf_key = secret->raw; |
| + unsigned int kdf_key_size = secret->size; |
| + u8 keyid_kdf_ctx = HKDF_CONTEXT_KEY_IDENTIFIER; |
| |
| /* |
| - * Now that the HKDF context is initialized, the raw key is no |
| - * longer needed. |
| + * For standard 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. |
| */ |
| - memzero_explicit(secret->raw, secret->size); |
| + if (secret->is_hw_wrapped) { |
| + err = fscrypt_derive_sw_secret(sb, secret->raw, |
| + secret->size, sw_secret); |
| + if (err) |
| + return err; |
| + kdf_key = sw_secret; |
| + kdf_key_size = sizeof(sw_secret); |
| + } |
| + 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) |
| + return err; |
| |
| /* Calculate the key identifier */ |
| - err = fscrypt_hkdf_expand(&secret->hkdf, |
| - HKDF_CONTEXT_KEY_IDENTIFIER, NULL, 0, |
| + err = fscrypt_hkdf_expand(&secret->hkdf, keyid_kdf_ctx, NULL, 0, |
| key_spec->u.identifier, |
| FSCRYPT_KEY_IDENTIFIER_SIZE); |
| if (err) |
| @@ -506,7 +522,7 @@ static int fscrypt_provisioning_key_preparse(struct key_preparsed_payload *prep) |
| const struct fscrypt_provisioning_key_payload *payload = prep->data; |
| |
| if (prep->datalen < sizeof(*payload) + FSCRYPT_MIN_KEY_SIZE || |
| - prep->datalen > sizeof(*payload) + FSCRYPT_MAX_KEY_SIZE) |
| + prep->datalen > sizeof(*payload) + FSCRYPT_MAX_ANY_KEY_SIZE) |
| return -EINVAL; |
| |
| if (payload->type != FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR && |
| @@ -655,15 +671,30 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg) |
| return -EACCES; |
| |
| 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.raw_size != 0) |
| return -EINVAL; |
| err = get_keyring_key(arg.key_id, arg.key_spec.type, &secret); |
| if (err) |
| goto out_wipe_secret; |
| + err = -EINVAL; |
| + if (secret.size > FSCRYPT_MAX_STANDARD_KEY_SIZE && |
| + !secret.is_hw_wrapped) |
| + goto out_wipe_secret; |
| } else { |
| if (arg.raw_size < FSCRYPT_MIN_KEY_SIZE || |
| - arg.raw_size > FSCRYPT_MAX_KEY_SIZE) |
| + arg.raw_size > (secret.is_hw_wrapped ? |
| + FSCRYPT_MAX_HW_WRAPPED_KEY_SIZE : |
| + FSCRYPT_MAX_STANDARD_KEY_SIZE)) |
| return -EINVAL; |
| secret.size = arg.raw_size; |
| err = -EFAULT; |
| @@ -696,15 +727,15 @@ EXPORT_SYMBOL_GPL(fscrypt_ioctl_add_key); |
| int fscrypt_add_test_dummy_key(struct super_block *sb, |
| struct fscrypt_key_specifier *key_spec) |
| { |
| - static u8 test_key[FSCRYPT_MAX_KEY_SIZE]; |
| + static u8 test_key[FSCRYPT_MAX_STANDARD_KEY_SIZE]; |
| struct fscrypt_master_key_secret secret; |
| int err; |
| |
| - get_random_once(test_key, FSCRYPT_MAX_KEY_SIZE); |
| + get_random_once(test_key, FSCRYPT_MAX_STANDARD_KEY_SIZE); |
| |
| memset(&secret, 0, sizeof(secret)); |
| - secret.size = FSCRYPT_MAX_KEY_SIZE; |
| - memcpy(secret.raw, test_key, FSCRYPT_MAX_KEY_SIZE); |
| + secret.size = FSCRYPT_MAX_STANDARD_KEY_SIZE; |
| + memcpy(secret.raw, test_key, FSCRYPT_MAX_STANDARD_KEY_SIZE); |
| |
| err = add_master_key(sb, &secret, key_spec); |
| wipe_master_key_secret(&secret); |
| diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c |
| --- a/fs/crypto/keysetup.c |
| +++ b/fs/crypto/keysetup.c |
| @@ -124,15 +124,23 @@ fscrypt_allocate_skcipher(struct fscrypt_mode *mode, const u8 *raw_key, |
| * Prepare the crypto transform object or blk-crypto key in @prep_key, given the |
| * raw key, encryption mode (@ci->ci_mode), flag indicating which encryption |
| * implementation (fs-layer or blk-crypto) will be used (@ci->ci_inlinecrypt), |
| - * and IV generation method (@ci->ci_policy.flags). |
| + * and IV generation method (@ci->ci_policy.flags). The raw key can be either a |
| + * standard key or a hardware-wrapped key, as indicated by @is_hw_wrapped; it |
| + * can only be a hardware-wrapped key if blk-crypto will be used. |
| */ |
| -int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key, |
| - const u8 *raw_key, const struct fscrypt_info *ci) |
| +static int __fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key, |
| + const u8 *raw_key, unsigned int raw_key_size, |
| + bool is_hw_wrapped, |
| + const struct fscrypt_info *ci) |
| { |
| struct crypto_skcipher *tfm; |
| |
| 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, raw_key_size, is_hw_wrapped, ci); |
| + |
| + if (WARN_ON(is_hw_wrapped || raw_key_size != ci->ci_mode->keysize)) |
| + return -EINVAL; |
| |
| tfm = fscrypt_allocate_skcipher(ci->ci_mode, raw_key, ci->ci_inode); |
| if (IS_ERR(tfm)) |
| @@ -147,6 +155,13 @@ int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key, |
| return 0; |
| } |
| |
| +int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key, |
| + const u8 *raw_key, const struct fscrypt_info *ci) |
| +{ |
| + return __fscrypt_prepare_key(prep_key, raw_key, ci->ci_mode->keysize, |
| + false, ci); |
| +} |
| + |
| /* Destroy a crypto transform object and/or blk-crypto key. */ |
| void fscrypt_destroy_prepared_key(struct fscrypt_prepared_key *prep_key) |
| { |
| @@ -171,14 +186,29 @@ static int setup_per_mode_enc_key(struct fscrypt_info *ci, |
| struct fscrypt_mode *mode = ci->ci_mode; |
| const u8 mode_num = mode - fscrypt_modes; |
| struct fscrypt_prepared_key *prep_key; |
| - u8 mode_key[FSCRYPT_MAX_KEY_SIZE]; |
| + u8 mode_key[FSCRYPT_MAX_STANDARD_KEY_SIZE]; |
| u8 hkdf_info[sizeof(mode_num) + sizeof(sb->s_uuid)]; |
| unsigned int hkdf_infolen = 0; |
| + bool use_hw_wrapped_key = false; |
| int err; |
| |
| if (WARN_ON(mode_num > FSCRYPT_MODE_MAX)) |
| 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 hardware is 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]; |
| if (fscrypt_is_key_prepared(prep_key, ci)) { |
| ci->ci_enc_key = *prep_key; |
| @@ -190,6 +220,14 @@ static int setup_per_mode_enc_key(struct fscrypt_info *ci, |
| if (fscrypt_is_key_prepared(prep_key, ci)) |
| goto done_unlock; |
| |
| + if (use_hw_wrapped_key) { |
| + err = __fscrypt_prepare_key(prep_key, mk->mk_secret.raw, |
| + 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(sb->s_uuid) != 16); |
| BUILD_BUG_ON(sizeof(hkdf_info) != 17); |
| @@ -312,6 +350,14 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci, |
| { |
| 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) { |
| /* |
| * DIRECT_KEY: instead of deriving per-file encryption keys, the |
| @@ -338,7 +384,7 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci, |
| FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) { |
| err = fscrypt_setup_iv_ino_lblk_32_key(ci, mk); |
| } else { |
| - u8 derived_key[FSCRYPT_MAX_KEY_SIZE]; |
| + u8 derived_key[FSCRYPT_MAX_STANDARD_KEY_SIZE]; |
| |
| err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf, |
| HKDF_CONTEXT_PER_FILE_ENC_KEY, |
| @@ -421,10 +467,6 @@ static int setup_file_encryption_key(struct fscrypt_info *ci, |
| struct fscrypt_key_specifier mk_spec; |
| int err; |
| |
| - err = fscrypt_select_encryption_impl(ci); |
| - if (err) |
| - return err; |
| - |
| switch (ci->ci_policy.version) { |
| case FSCRYPT_POLICY_V1: |
| mk_spec.type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR; |
| @@ -449,6 +491,10 @@ static int setup_file_encryption_key(struct fscrypt_info *ci, |
| ci->ci_policy.version != FSCRYPT_POLICY_V1) |
| return PTR_ERR(key); |
| |
| + err = fscrypt_select_encryption_impl(ci, false); |
| + if (err) |
| + return err; |
| + |
| /* |
| * As a legacy fallback for v1 policies, search for the key in |
| * the current task's subscribed keyrings too. Don't move this |
| @@ -472,8 +518,20 @@ static int setup_file_encryption_key(struct fscrypt_info *ci, |
| 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) { |
| case FSCRYPT_POLICY_V1: |
| + if (WARN_ON(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.raw); |
| break; |
| case FSCRYPT_POLICY_V2: |
| diff --git a/fs/crypto/keysetup_v1.c b/fs/crypto/keysetup_v1.c |
| --- a/fs/crypto/keysetup_v1.c |
| +++ b/fs/crypto/keysetup_v1.c |
| @@ -118,7 +118,8 @@ find_and_lock_process_key(const char *prefix, |
| payload = (const struct fscrypt_key *)ukp->data; |
| |
| if (ukp->datalen != sizeof(struct fscrypt_key) || |
| - payload->size < 1 || payload->size > FSCRYPT_MAX_KEY_SIZE) { |
| + payload->size < 1 || |
| + payload->size > FSCRYPT_MAX_STANDARD_KEY_SIZE) { |
| fscrypt_warn(NULL, |
| "key with description '%s' has invalid payload", |
| key->description); |
| @@ -148,7 +149,7 @@ struct fscrypt_direct_key { |
| const struct fscrypt_mode *dk_mode; |
| struct fscrypt_prepared_key dk_key; |
| u8 dk_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; |
| - u8 dk_raw[FSCRYPT_MAX_KEY_SIZE]; |
| + u8 dk_raw[FSCRYPT_MAX_STANDARD_KEY_SIZE]; |
| }; |
| |
| static void free_direct_key(struct fscrypt_direct_key *dk) |
| diff --git a/include/uapi/linux/fscrypt.h b/include/uapi/linux/fscrypt.h |
| --- a/include/uapi/linux/fscrypt.h |
| +++ b/include/uapi/linux/fscrypt.h |
| @@ -124,7 +124,10 @@ struct fscrypt_add_key_arg { |
| struct fscrypt_key_specifier key_spec; |
| __u32 raw_size; |
| __u32 key_id; |
| - __u32 __reserved[8]; |
| + __u32 __reserved[7]; |
| + /* N.B.: "temporary" flag, not reserved upstream */ |
| +#define __FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED 0x00000001 |
| + __u32 __flags; |
| __u8 raw[]; |
| }; |
| |