diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 7005dbe6bfec..5d7b20fead86 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -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); diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index a1bd550f668b..e7e8e9a27e8b 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "fscrypt_private.h" @@ -49,6 +50,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; @@ -75,7 +77,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); @@ -133,6 +138,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 diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c index 9052197d8f5a..7facb5993b03 100644 --- a/fs/crypto/keyring.c +++ b/fs/crypto/keyring.c @@ -465,6 +465,9 @@ out_unlock: 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,17 +537,36 @@ 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); - if (err) + 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 key is no - * longer needed. + * Now that the HKDF context is initialized, the raw HKDF + * key is no longer needed. */ - memzero_explicit(secret.raw, secret.size); + memzero_explicit(kdf_key, kdf_key_size); + if (err) + goto out_wipe_secret; /* Calculate the key identifier and return it to userspace. */ err = fscrypt_hkdf_expand(&secret.hkdf, diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index 8c2675d0b2d0..f87daf215ac9 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -113,12 +113,17 @@ err_free_tfm: * (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 diff --git a/fs/crypto/keysetup_v1.c b/fs/crypto/keysetup_v1.c index 8cafdbf47002..47591c54dc3d 100644 --- a/fs/crypto/keysetup_v1.c +++ b/fs/crypto/keysetup_v1.c @@ -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, diff --git a/include/uapi/linux/fscrypt.h b/include/uapi/linux/fscrypt.h index 1beb174ad950..7d150d800abc 100644 --- a/include/uapi/linux/fscrypt.h +++ b/include/uapi/linux/fscrypt.h @@ -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[]; };