ANDROID: fscrypt: add support for hardware-wrapped keys
To prevent keys from being compromised if an attacker acquires read access to kernel memory, some inline encryption hardware supports protecting the keys in hardware without software having access to or the ability to set the plaintext keys. Instead, software only sees "wrapped keys", which may differ on every boot. The keys can be initially generated either by software (in which case they need to be imported to hardware to be wrapped), or directly by the hardware. Add support for this type of hardware by allowing keys to be flagged as hardware-wrapped and encryption policies to be flagged as needing a hardware-wrapped key. When used, fscrypt will pass the wrapped key directly to the inline encryption hardware to encrypt file contents. The hardware is responsible for internally unwrapping the key and deriving the actual file contents encryption key. fscrypt also asks the inline encryption hardware to derive a cryptographically isolated software "secret", which fscrypt then uses as the master key for all other purposes besides file contents encryption, e.g. to derive filenames encryption keys and the key identifier. Bug: 147209885 Change-Id: I7f4cc5c32f130709db3eb9ebdb1ebfff5751ca95 Co-developed-by: Gaurav Kashyap <gaurkash@codeaurora.org> Signed-off-by: Gaurav Kashyap <gaurkash@codeaurora.org> Signed-off-by: Barani Muthukumaran <bmuthuku@codeaurora.org> Signed-off-by: Eric Biggers <ebiggers@google.com>
This commit is contained in:
parent
a076eebee0
commit
23b81578bf
6 changed files with 155 additions and 37 deletions
|
@ -20,6 +20,7 @@
|
|||
#define FS_KEY_DERIVATION_NONCE_SIZE 16
|
||||
|
||||
#define FSCRYPT_MIN_KEY_SIZE 16
|
||||
#define FSCRYPT_MAX_HW_WRAPPED_KEY_SIZE 128
|
||||
|
||||
#define FSCRYPT_CONTEXT_V1 1
|
||||
#define FSCRYPT_CONTEXT_V2 2
|
||||
|
@ -330,11 +331,18 @@ fscrypt_using_inline_encryption(const struct fscrypt_info *ci)
|
|||
extern int fscrypt_prepare_inline_crypt_key(
|
||||
struct fscrypt_prepared_key *prep_key,
|
||||
const u8 *raw_key,
|
||||
unsigned int raw_key_size,
|
||||
const struct fscrypt_info *ci);
|
||||
|
||||
extern void fscrypt_destroy_inline_crypt_key(
|
||||
struct fscrypt_prepared_key *prep_key);
|
||||
|
||||
extern int fscrypt_derive_raw_secret(struct super_block *sb,
|
||||
const u8 *wrapped_key,
|
||||
unsigned int wrapped_key_size,
|
||||
u8 *raw_secret,
|
||||
unsigned int raw_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.
|
||||
|
@ -367,7 +375,7 @@ static inline bool fscrypt_using_inline_encryption(
|
|||
|
||||
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,
|
||||
const struct fscrypt_info *ci)
|
||||
{
|
||||
WARN_ON(1);
|
||||
|
@ -379,6 +387,17 @@ fscrypt_destroy_inline_crypt_key(struct fscrypt_prepared_key *prep_key)
|
|||
{
|
||||
}
|
||||
|
||||
static inline int fscrypt_derive_raw_secret(struct super_block *sb,
|
||||
const u8 *wrapped_key,
|
||||
unsigned int wrapped_key_size,
|
||||
u8 *raw_secret,
|
||||
unsigned int raw_secret_size)
|
||||
{
|
||||
fscrypt_warn(NULL,
|
||||
"kernel built without support for hardware-wrapped keys");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key,
|
||||
const struct fscrypt_info *ci)
|
||||
|
@ -403,8 +422,15 @@ struct fscrypt_master_key_secret {
|
|||
/* Size of the raw key in bytes. Set even if ->raw isn't set. */
|
||||
u32 size;
|
||||
|
||||
/* For v1 policy keys: the raw key. Wiped for v2 policy keys. */
|
||||
u8 raw[FSCRYPT_MAX_KEY_SIZE];
|
||||
/* True if the key in ->raw is a hardware-wrapped key. */
|
||||
bool is_hw_wrapped;
|
||||
|
||||
/*
|
||||
* For v1 policy keys: the raw key. Wiped for v2 policy keys, unless
|
||||
* ->is_hw_wrapped is true, in which case this contains the wrapped key
|
||||
* rather than the key with which 'hkdf' was keyed.
|
||||
*/
|
||||
u8 raw[FSCRYPT_MAX_HW_WRAPPED_KEY_SIZE];
|
||||
|
||||
} __randomize_layout;
|
||||
|
||||
|
@ -549,7 +575,7 @@ fscrypt_mode_supports_direct_key(const struct fscrypt_mode *mode)
|
|||
}
|
||||
|
||||
extern int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key,
|
||||
const u8 *raw_key,
|
||||
const u8 *raw_key, unsigned int raw_key_size,
|
||||
const struct fscrypt_info *ci);
|
||||
|
||||
extern void fscrypt_destroy_prepared_key(struct fscrypt_prepared_key *prep_key);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/blk-crypto.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/keyslot-manager.h>
|
||||
|
||||
#include "fscrypt_private.h"
|
||||
|
||||
|
@ -48,6 +49,7 @@ void 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,
|
||||
const struct fscrypt_info *ci)
|
||||
{
|
||||
const struct inode *inode = ci->ci_inode;
|
||||
|
@ -74,7 +76,10 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
|
|||
else
|
||||
sb->s_cop->get_devices(sb, blk_key->devs);
|
||||
|
||||
err = blk_crypto_init_key(&blk_key->base, raw_key, ci->ci_mode->keysize,
|
||||
BUILD_BUG_ON(FSCRYPT_MAX_HW_WRAPPED_KEY_SIZE >
|
||||
BLK_CRYPTO_MAX_WRAPPED_KEY_SIZE);
|
||||
|
||||
err = blk_crypto_init_key(&blk_key->base, raw_key, raw_key_size,
|
||||
crypto_mode, sb->s_blocksize);
|
||||
if (err) {
|
||||
fscrypt_err(inode, "error %d initializing blk-crypto key", err);
|
||||
|
@ -132,6 +137,22 @@ void fscrypt_destroy_inline_crypt_key(struct fscrypt_prepared_key *prep_key)
|
|||
}
|
||||
}
|
||||
|
||||
int fscrypt_derive_raw_secret(struct super_block *sb,
|
||||
const u8 *wrapped_key,
|
||||
unsigned int wrapped_key_size,
|
||||
u8 *raw_secret, unsigned int raw_secret_size)
|
||||
{
|
||||
struct request_queue *q;
|
||||
|
||||
q = sb->s_bdev->bd_queue;
|
||||
if (!q->ksm)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return keyslot_manager_derive_raw_secret(q->ksm,
|
||||
wrapped_key, wrapped_key_size,
|
||||
raw_secret, raw_secret_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* fscrypt_inode_uses_inline_crypto - test whether an inode uses inline
|
||||
* encryption
|
||||
|
|
|
@ -465,6 +465,9 @@ static int add_master_key(struct super_block *sb,
|
|||
return err;
|
||||
}
|
||||
|
||||
/* Size of software "secret" derived from hardware-wrapped key */
|
||||
#define RAW_SECRET_SIZE 32
|
||||
|
||||
/*
|
||||
* Add a master encryption key to the filesystem, causing all files which were
|
||||
* encrypted with it to appear "unlocked" (decrypted) when accessed.
|
||||
|
@ -495,6 +498,9 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg)
|
|||
struct fscrypt_add_key_arg __user *uarg = _uarg;
|
||||
struct fscrypt_add_key_arg arg;
|
||||
struct fscrypt_master_key_secret secret;
|
||||
u8 _kdf_key[RAW_SECRET_SIZE];
|
||||
u8 *kdf_key;
|
||||
unsigned int kdf_key_size;
|
||||
int err;
|
||||
|
||||
if (copy_from_user(&arg, uarg, sizeof(arg)))
|
||||
|
@ -503,11 +509,16 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg)
|
|||
if (!valid_key_spec(&arg.key_spec))
|
||||
return -EINVAL;
|
||||
|
||||
if (arg.raw_size < FSCRYPT_MIN_KEY_SIZE ||
|
||||
arg.raw_size > FSCRYPT_MAX_KEY_SIZE)
|
||||
if (memchr_inv(arg.__reserved, 0, sizeof(arg.__reserved)))
|
||||
return -EINVAL;
|
||||
|
||||
if (memchr_inv(arg.__reserved, 0, sizeof(arg.__reserved)))
|
||||
BUILD_BUG_ON(FSCRYPT_MAX_HW_WRAPPED_KEY_SIZE <
|
||||
FSCRYPT_MAX_KEY_SIZE);
|
||||
|
||||
if (arg.raw_size < FSCRYPT_MIN_KEY_SIZE ||
|
||||
arg.raw_size >
|
||||
((arg.__flags & __FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED) ?
|
||||
FSCRYPT_MAX_HW_WRAPPED_KEY_SIZE : FSCRYPT_MAX_KEY_SIZE))
|
||||
return -EINVAL;
|
||||
|
||||
memset(&secret, 0, sizeof(secret));
|
||||
|
@ -526,18 +537,37 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg)
|
|||
err = -EACCES;
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
goto out_wipe_secret;
|
||||
|
||||
err = -EINVAL;
|
||||
if (arg.__flags)
|
||||
goto out_wipe_secret;
|
||||
break;
|
||||
case FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER:
|
||||
err = fscrypt_init_hkdf(&secret.hkdf, secret.raw, secret.size);
|
||||
err = -EINVAL;
|
||||
if (arg.__flags & ~__FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED)
|
||||
goto out_wipe_secret;
|
||||
if (arg.__flags & __FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED) {
|
||||
kdf_key = _kdf_key;
|
||||
kdf_key_size = RAW_SECRET_SIZE;
|
||||
err = fscrypt_derive_raw_secret(sb, secret.raw,
|
||||
secret.size,
|
||||
kdf_key, kdf_key_size);
|
||||
if (err)
|
||||
goto out_wipe_secret;
|
||||
secret.is_hw_wrapped = true;
|
||||
} else {
|
||||
kdf_key = secret.raw;
|
||||
kdf_key_size = secret.size;
|
||||
}
|
||||
err = fscrypt_init_hkdf(&secret.hkdf, kdf_key, kdf_key_size);
|
||||
/*
|
||||
* Now that the HKDF context is initialized, the raw HKDF
|
||||
* key is no longer needed.
|
||||
*/
|
||||
memzero_explicit(kdf_key, kdf_key_size);
|
||||
if (err)
|
||||
goto out_wipe_secret;
|
||||
|
||||
/*
|
||||
* Now that the HKDF context is initialized, the raw key is no
|
||||
* longer needed.
|
||||
*/
|
||||
memzero_explicit(secret.raw, secret.size);
|
||||
|
||||
/* Calculate the key identifier and return it to userspace. */
|
||||
err = fscrypt_hkdf_expand(&secret.hkdf,
|
||||
HKDF_CONTEXT_KEY_IDENTIFIER,
|
||||
|
|
|
@ -113,12 +113,17 @@ fscrypt_allocate_skcipher(struct fscrypt_mode *mode, const u8 *raw_key,
|
|||
* (fs-layer or blk-crypto) will be used.
|
||||
*/
|
||||
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,
|
||||
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, ci);
|
||||
|
||||
if (WARN_ON(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))
|
||||
|
@ -142,7 +147,8 @@ void fscrypt_destroy_prepared_key(struct fscrypt_prepared_key *prep_key)
|
|||
int fscrypt_set_derived_key(struct fscrypt_info *ci, const u8 *derived_key)
|
||||
{
|
||||
ci->ci_owns_key = true;
|
||||
return fscrypt_prepare_key(&ci->ci_key, derived_key, ci);
|
||||
return fscrypt_prepare_key(&ci->ci_key, derived_key,
|
||||
ci->ci_mode->keysize, ci);
|
||||
}
|
||||
|
||||
static int setup_per_mode_key(struct fscrypt_info *ci,
|
||||
|
@ -175,24 +181,48 @@ static int setup_per_mode_key(struct fscrypt_info *ci,
|
|||
if (fscrypt_is_key_prepared(prep_key, ci))
|
||||
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);
|
||||
hkdf_info[hkdf_infolen++] = mode_num;
|
||||
if (include_fs_uuid) {
|
||||
memcpy(&hkdf_info[hkdf_infolen], &sb->s_uuid,
|
||||
sizeof(sb->s_uuid));
|
||||
hkdf_infolen += sizeof(sb->s_uuid);
|
||||
if (mk->mk_secret.is_hw_wrapped && S_ISREG(inode->i_mode)) {
|
||||
int i;
|
||||
|
||||
if (!fscrypt_using_inline_encryption(ci)) {
|
||||
fscrypt_warn(ci->ci_inode,
|
||||
"Hardware-wrapped keys require inline encryption (-o inlinecrypt)");
|
||||
err = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
for (i = 0; i <= __FSCRYPT_MODE_MAX; i++) {
|
||||
if (fscrypt_is_key_prepared(&keys[i], ci)) {
|
||||
fscrypt_warn(ci->ci_inode,
|
||||
"Each hardware-wrapped key can only be used with one encryption mode");
|
||||
err = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
}
|
||||
err = fscrypt_prepare_key(prep_key, mk->mk_secret.raw,
|
||||
mk->mk_secret.size, ci);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
} else {
|
||||
BUILD_BUG_ON(sizeof(mode_num) != 1);
|
||||
BUILD_BUG_ON(sizeof(sb->s_uuid) != 16);
|
||||
BUILD_BUG_ON(sizeof(hkdf_info) != 17);
|
||||
hkdf_info[hkdf_infolen++] = mode_num;
|
||||
if (include_fs_uuid) {
|
||||
memcpy(&hkdf_info[hkdf_infolen], &sb->s_uuid,
|
||||
sizeof(sb->s_uuid));
|
||||
hkdf_infolen += sizeof(sb->s_uuid);
|
||||
}
|
||||
err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf,
|
||||
hkdf_context, hkdf_info, hkdf_infolen,
|
||||
mode_key, mode->keysize);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
err = fscrypt_prepare_key(prep_key, mode_key, mode->keysize,
|
||||
ci);
|
||||
memzero_explicit(mode_key, mode->keysize);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
}
|
||||
err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf,
|
||||
hkdf_context, hkdf_info, hkdf_infolen,
|
||||
mode_key, mode->keysize);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
err = fscrypt_prepare_key(prep_key, mode_key, ci);
|
||||
memzero_explicit(mode_key, mode->keysize);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
done_unlock:
|
||||
ci->ci_key = *prep_key;
|
||||
err = 0;
|
||||
|
@ -207,6 +237,13 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci,
|
|||
u8 derived_key[FSCRYPT_MAX_KEY_SIZE];
|
||||
int err;
|
||||
|
||||
if (mk->mk_secret.is_hw_wrapped &&
|
||||
!(ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64)) {
|
||||
fscrypt_warn(ci->ci_inode,
|
||||
"Hardware-wrapped keys are only supported with IV_INO_LBLK_64 policies");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) {
|
||||
/*
|
||||
* DIRECT_KEY: instead of deriving per-file keys, the per-file
|
||||
|
|
|
@ -233,7 +233,8 @@ fscrypt_get_direct_key(const struct fscrypt_info *ci, const u8 *raw_key)
|
|||
return ERR_PTR(-ENOMEM);
|
||||
refcount_set(&dk->dk_refcount, 1);
|
||||
dk->dk_mode = ci->ci_mode;
|
||||
err = fscrypt_prepare_key(&dk->dk_key, raw_key, ci);
|
||||
err = fscrypt_prepare_key(&dk->dk_key, raw_key, ci->ci_mode->keysize,
|
||||
ci);
|
||||
if (err)
|
||||
goto err_free_dk;
|
||||
memcpy(dk->dk_descriptor, ci->ci_policy.v1.master_key_descriptor,
|
||||
|
|
|
@ -113,7 +113,10 @@ struct fscrypt_key_specifier {
|
|||
struct fscrypt_add_key_arg {
|
||||
struct fscrypt_key_specifier key_spec;
|
||||
__u32 raw_size;
|
||||
__u32 __reserved[9];
|
||||
__u32 __reserved[8];
|
||||
/* N.B.: "temporary" flag, not reserved upstream */
|
||||
#define __FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED 0x00000001
|
||||
__u32 __flags;
|
||||
__u8 raw[];
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue