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:
Barani Muthukumaran 2020-01-15 18:41:54 -08:00 committed by Todd Kjos
parent a076eebee0
commit 23b81578bf
6 changed files with 155 additions and 37 deletions

View file

@ -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);

View file

@ -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

View file

@ -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,

View file

@ -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

View 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,

View file

@ -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[];
};