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> (cherry picked from commit 2fd53f809834904a364bd65a51c1a606185958dc)
This commit is contained in:
@@ -27,6 +27,27 @@
|
|||||||
*/
|
*/
|
||||||
#define FSCRYPT_MIN_KEY_SIZE 16
|
#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_V1 1
|
||||||
#define FSCRYPT_CONTEXT_V2 2
|
#define FSCRYPT_CONTEXT_V2 2
|
||||||
|
|
||||||
@@ -335,7 +356,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_info *ci);
|
int fscrypt_select_encryption_impl(struct fscrypt_info *ci,
|
||||||
|
bool is_hw_wrapped_key);
|
||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
fscrypt_using_inline_encryption(const struct fscrypt_info *ci)
|
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,
|
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);
|
const struct fscrypt_info *ci);
|
||||||
|
|
||||||
void fscrypt_destroy_inline_crypt_key(struct fscrypt_prepared_key *prep_key);
|
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
|
* 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.
|
||||||
@@ -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
|
* 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
|
* 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,
|
* encryption key) another task can publish blk_key or tfm concurrently,
|
||||||
* executing a RELEASE barrier. We need to use smp_load_acquire() here
|
* 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 */
|
#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;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -385,7 +414,8 @@ fscrypt_using_inline_encryption(const struct fscrypt_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 *raw_key, unsigned int raw_key_size,
|
||||||
|
bool is_hw_wrapped,
|
||||||
const struct fscrypt_info *ci)
|
const struct fscrypt_info *ci)
|
||||||
{
|
{
|
||||||
WARN_ON(1);
|
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
|
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_info *ci)
|
const struct fscrypt_info *ci)
|
||||||
@@ -413,11 +452,23 @@ 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;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
* 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
|
* zeroized due to no longer being needed. I.e. we still remember the
|
||||||
@@ -425,8 +476,14 @@ struct fscrypt_master_key_secret {
|
|||||||
*/
|
*/
|
||||||
u32 size;
|
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;
|
} __randomize_layout;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,9 @@
|
|||||||
* Function"), aka RFC 5869. See also the original paper (Krawczyk 2010):
|
* Function"), aka RFC 5869. See also the original paper (Krawczyk 2010):
|
||||||
* "Cryptographic Extraction and Key Derivation: The HKDF Scheme".
|
* "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
|
* Copyright 2019 Google LLC
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/blk-crypto.h>
|
#include <linux/blk-crypto.h>
|
||||||
|
#include <linux/blk-crypto-profile.h>
|
||||||
#include <linux/blkdev.h>
|
#include <linux/blkdev.h>
|
||||||
#include <linux/buffer_head.h>
|
#include <linux/buffer_head.h>
|
||||||
#include <linux/sched/mm.h>
|
#include <linux/sched/mm.h>
|
||||||
@@ -65,7 +66,8 @@ static unsigned int fscrypt_get_dun_bytes(const struct fscrypt_info *ci)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Enable inline encryption for this file if supported. */
|
/* 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;
|
const struct inode *inode = ci->ci_inode;
|
||||||
struct super_block *sb = inode->i_sb;
|
struct super_block *sb = inode->i_sb;
|
||||||
@@ -106,7 +108,9 @@ int fscrypt_select_encryption_impl(struct fscrypt_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 = sb->s_blocksize;
|
crypto_cfg.data_unit_size = sb->s_blocksize;
|
||||||
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_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);
|
num_devs = fscrypt_get_num_devices(sb);
|
||||||
devs = kmalloc_array(num_devs, sizeof(*devs), GFP_KERNEL);
|
devs = kmalloc_array(num_devs, sizeof(*devs), GFP_KERNEL);
|
||||||
if (!devs)
|
if (!devs)
|
||||||
@@ -127,11 +131,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 *raw_key,
|
||||||
|
unsigned int raw_key_size,
|
||||||
|
bool is_hw_wrapped,
|
||||||
const struct fscrypt_info *ci)
|
const struct fscrypt_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_STANDARD;
|
||||||
int num_devs = fscrypt_get_num_devices(sb);
|
int num_devs = fscrypt_get_num_devices(sb);
|
||||||
int queue_refs = 0;
|
int queue_refs = 0;
|
||||||
struct fscrypt_blk_crypto_key *blk_key;
|
struct fscrypt_blk_crypto_key *blk_key;
|
||||||
@@ -145,8 +153,8 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
|
|||||||
blk_key->num_devs = num_devs;
|
blk_key->num_devs = num_devs;
|
||||||
fscrypt_get_devices(sb, num_devs, blk_key->devs);
|
fscrypt_get_devices(sb, num_devs, blk_key->devs);
|
||||||
|
|
||||||
err = blk_crypto_init_key(&blk_key->base, raw_key, ci->ci_mode->keysize,
|
err = blk_crypto_init_key(&blk_key->base, raw_key, raw_key_size,
|
||||||
BLK_CRYPTO_KEY_TYPE_STANDARD, crypto_mode,
|
key_type, crypto_mode,
|
||||||
fscrypt_get_dun_bytes(ci), sb->s_blocksize);
|
fscrypt_get_dun_bytes(ci), sb->s_blocksize);
|
||||||
if (err) {
|
if (err) {
|
||||||
fscrypt_err(inode, "error %d initializing blk-crypto key", err);
|
fscrypt_err(inode, "error %d initializing blk-crypto key", err);
|
||||||
@@ -206,6 +214,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)
|
bool __fscrypt_inode_uses_inline_crypto(const struct inode *inode)
|
||||||
{
|
{
|
||||||
return inode->i_crypt_info->ci_inlinecrypt;
|
return inode->i_crypt_info->ci_inlinecrypt;
|
||||||
|
|||||||
@@ -103,11 +103,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_STANDARD_KEY_SIZE bytes to the user's key
|
||||||
* each key, regardless of the exact key size. The amount of memory
|
* quota for each key, regardless of the exact key size. The amount of
|
||||||
* actually used is greater than the size of the raw key anyway.
|
* 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)
|
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;
|
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->raw;
|
||||||
|
unsigned int kdf_key_size = secret->size;
|
||||||
|
u8 keyid_kdf_ctx = HKDF_CONTEXT_KEY_IDENTIFIER;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
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)
|
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)
|
||||||
@@ -506,7 +522,7 @@ 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) + FSCRYPT_MIN_KEY_SIZE ||
|
||||||
prep->datalen > sizeof(*payload) + FSCRYPT_MAX_KEY_SIZE)
|
prep->datalen > sizeof(*payload) + FSCRYPT_MAX_ANY_KEY_SIZE)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (payload->type != FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR &&
|
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;
|
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, &secret);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_wipe_secret;
|
goto out_wipe_secret;
|
||||||
|
err = -EINVAL;
|
||||||
|
if (secret.size > FSCRYPT_MAX_STANDARD_KEY_SIZE &&
|
||||||
|
!secret.is_hw_wrapped)
|
||||||
|
goto out_wipe_secret;
|
||||||
} else {
|
} else {
|
||||||
if (arg.raw_size < FSCRYPT_MIN_KEY_SIZE ||
|
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;
|
return -EINVAL;
|
||||||
secret.size = arg.raw_size;
|
secret.size = arg.raw_size;
|
||||||
err = -EFAULT;
|
err = -EFAULT;
|
||||||
@@ -696,15 +727,15 @@ EXPORT_SYMBOL_GPL(fscrypt_ioctl_add_key);
|
|||||||
int fscrypt_add_test_dummy_key(struct super_block *sb,
|
int fscrypt_add_test_dummy_key(struct super_block *sb,
|
||||||
struct fscrypt_key_specifier *key_spec)
|
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;
|
struct fscrypt_master_key_secret secret;
|
||||||
int err;
|
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));
|
memset(&secret, 0, sizeof(secret));
|
||||||
secret.size = FSCRYPT_MAX_KEY_SIZE;
|
secret.size = FSCRYPT_MAX_STANDARD_KEY_SIZE;
|
||||||
memcpy(secret.raw, test_key, FSCRYPT_MAX_KEY_SIZE);
|
memcpy(secret.raw, test_key, FSCRYPT_MAX_STANDARD_KEY_SIZE);
|
||||||
|
|
||||||
err = add_master_key(sb, &secret, key_spec);
|
err = add_master_key(sb, &secret, key_spec);
|
||||||
wipe_master_key_secret(&secret);
|
wipe_master_key_secret(&secret);
|
||||||
|
|||||||
@@ -124,15 +124,23 @@ err_free_tfm:
|
|||||||
* Prepare the crypto transform object or blk-crypto key in @prep_key, given the
|
* 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
|
* raw key, encryption mode (@ci->ci_mode), flag indicating which encryption
|
||||||
* implementation (fs-layer or blk-crypto) will be used (@ci->ci_inlinecrypt),
|
* 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,
|
static int __fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key,
|
||||||
const u8 *raw_key, const struct fscrypt_info *ci)
|
const u8 *raw_key, unsigned int raw_key_size,
|
||||||
|
bool is_hw_wrapped,
|
||||||
|
const struct fscrypt_info *ci)
|
||||||
{
|
{
|
||||||
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, 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);
|
tfm = fscrypt_allocate_skcipher(ci->ci_mode, raw_key, ci->ci_inode);
|
||||||
if (IS_ERR(tfm))
|
if (IS_ERR(tfm))
|
||||||
@@ -147,6 +155,13 @@ int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key,
|
|||||||
return 0;
|
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. */
|
/* Destroy a crypto transform object and/or blk-crypto key. */
|
||||||
void fscrypt_destroy_prepared_key(struct fscrypt_prepared_key *prep_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;
|
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_STANDARD_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(mode_num > FSCRYPT_MODE_MAX))
|
if (WARN_ON(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 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];
|
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;
|
||||||
@@ -190,6 +220,14 @@ static int setup_per_mode_enc_key(struct fscrypt_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_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(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);
|
||||||
@@ -312,6 +350,14 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_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
|
||||||
@@ -338,7 +384,7 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_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_STANDARD_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,
|
||||||
@@ -421,10 +467,6 @@ static int setup_file_encryption_key(struct fscrypt_info *ci,
|
|||||||
struct fscrypt_key_specifier mk_spec;
|
struct fscrypt_key_specifier mk_spec;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = fscrypt_select_encryption_impl(ci);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
switch (ci->ci_policy.version) {
|
switch (ci->ci_policy.version) {
|
||||||
case FSCRYPT_POLICY_V1:
|
case FSCRYPT_POLICY_V1:
|
||||||
mk_spec.type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR;
|
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)
|
ci->ci_policy.version != FSCRYPT_POLICY_V1)
|
||||||
return PTR_ERR(key);
|
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
|
* 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
|
||||||
@@ -472,8 +518,20 @@ static int setup_file_encryption_key(struct fscrypt_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:
|
||||||
|
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);
|
err = fscrypt_setup_v1_file_key(ci, mk->mk_secret.raw);
|
||||||
break;
|
break;
|
||||||
case FSCRYPT_POLICY_V2:
|
case FSCRYPT_POLICY_V2:
|
||||||
|
|||||||
@@ -118,7 +118,8 @@ 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 > FSCRYPT_MAX_STANDARD_KEY_SIZE) {
|
||||||
fscrypt_warn(NULL,
|
fscrypt_warn(NULL,
|
||||||
"key with description '%s' has invalid payload",
|
"key with description '%s' has invalid payload",
|
||||||
key->description);
|
key->description);
|
||||||
@@ -148,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_STANDARD_KEY_SIZE];
|
||||||
};
|
};
|
||||||
|
|
||||||
static void free_direct_key(struct fscrypt_direct_key *dk)
|
static void free_direct_key(struct fscrypt_direct_key *dk)
|
||||||
|
|||||||
@@ -124,7 +124,10 @@ 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];
|
__u32 __reserved[7];
|
||||||
|
/* N.B.: "temporary" flag, not reserved upstream */
|
||||||
|
#define __FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED 0x00000001
|
||||||
|
__u32 __flags;
|
||||||
__u8 raw[];
|
__u8 raw[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user