diff options
| -rw-r--r-- | Documentation/filesystems/fscrypt.rst | 187 | ||||
| -rw-r--r-- | fs/crypto/fscrypt_private.h | 75 | ||||
| -rw-r--r-- | fs/crypto/hkdf.c | 4 | ||||
| -rw-r--r-- | fs/crypto/inline_crypt.c | 44 | ||||
| -rw-r--r-- | fs/crypto/keyring.c | 132 | ||||
| -rw-r--r-- | fs/crypto/keysetup.c | 63 | ||||
| -rw-r--r-- | fs/crypto/keysetup_v1.c | 4 | ||||
| -rw-r--r-- | include/uapi/linux/fscrypt.h | 6 | 
8 files changed, 410 insertions, 105 deletions
| diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst index 3d22e2db732d..29e84d125e02 100644 --- a/Documentation/filesystems/fscrypt.rst +++ b/Documentation/filesystems/fscrypt.rst @@ -70,7 +70,7 @@ Online attacks  --------------  fscrypt (and storage encryption in general) can only provide limited -protection, if any at all, against online attacks.  In detail: +protection against online attacks.  In detail:  Side-channel attacks  ~~~~~~~~~~~~~~~~~~~~ @@ -99,16 +99,23 @@ Therefore, any encryption-specific access control checks would merely  be enforced by kernel *code* and therefore would be largely redundant  with the wide variety of access control mechanisms already available.) -Kernel memory compromise -~~~~~~~~~~~~~~~~~~~~~~~~ +Read-only kernel memory compromise +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Unless `hardware-wrapped keys`_ are used, an attacker who gains the +ability to read from arbitrary kernel memory, e.g. by mounting a +physical attack or by exploiting a kernel security vulnerability, can +compromise all fscrypt keys that are currently in-use.  This also +extends to cold boot attacks; if the system is suddenly powered off, +keys the system was using may remain in memory for a short time. -An attacker who compromises the system enough to read from arbitrary -memory, e.g. by mounting a physical attack or by exploiting a kernel -security vulnerability, can compromise all encryption keys that are -currently in use. +However, if hardware-wrapped keys are used, then the fscrypt master +keys and file contents encryption keys (but not other types of fscrypt +subkeys such as filenames encryption keys) are protected from +compromises of arbitrary kernel memory. -However, fscrypt allows encryption keys to be removed from the kernel, -which may protect them from later compromise. +In addition, fscrypt allows encryption keys to be removed from the +kernel, which may protect them from later compromise.  In more detail, the FS_IOC_REMOVE_ENCRYPTION_KEY ioctl (or the  FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS ioctl) can wipe a master @@ -144,6 +151,24 @@ However, these ioctls have some limitations:    accelerator hardware (if used by the crypto API to implement any of    the algorithms), or in other places not explicitly considered here. +Full system compromise +~~~~~~~~~~~~~~~~~~~~~~ + +An attacker who gains "root" access and/or the ability to execute +arbitrary kernel code can freely exfiltrate data that is protected by +any in-use fscrypt keys.  Thus, usually fscrypt provides no meaningful +protection in this scenario.  (Data that is protected by a key that is +absent throughout the entire attack remains protected, modulo the +limitations of key removal mentioned above in the case where the key +was removed prior to the attack.) + +However, if `hardware-wrapped keys`_ are used, such attackers will be +unable to exfiltrate the master keys or file contents keys in a form +that will be usable after the system is powered off.  This may be +useful if the attacker is significantly time-limited and/or +bandwidth-limited, so they can only exfiltrate some data and need to +rely on a later offline attack to exfiltrate the rest of it. +  Limitations of v1 policies  ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -170,6 +195,10 @@ policies on all new encrypted directories.  Key hierarchy  ============= +Note: this section assumes the use of raw keys rather than +hardware-wrapped keys.  The use of hardware-wrapped keys modifies the +key hierarchy slightly.  For details, see `Hardware-wrapped keys`_. +  Master Keys  ----------- @@ -832,7 +861,9 @@ a pointer to struct fscrypt_add_key_arg, defined as follows::              struct fscrypt_key_specifier key_spec;              __u32 raw_size;              __u32 key_id; -            __u32 __reserved[8]; +    #define FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED 0x00000001 +            __u32 flags; +            __u32 __reserved[7];              __u8 raw[];      }; @@ -851,7 +882,7 @@ a pointer to struct fscrypt_add_key_arg, defined as follows::      struct fscrypt_provisioning_key_payload {              __u32 type; -            __u32 __reserved; +            __u32 flags;              __u8 raw[];      }; @@ -879,24 +910,32 @@ as follows:    Alternatively, if ``key_id`` is nonzero, this field must be 0, since    in that case the size is implied by the specified Linux keyring key. -- ``key_id`` is 0 if the raw key is given directly in the ``raw`` -  field.  Otherwise ``key_id`` is the ID of a Linux keyring key of -  type "fscrypt-provisioning" whose payload is -  struct fscrypt_provisioning_key_payload whose ``raw`` field contains -  the raw key and whose ``type`` field matches ``key_spec.type``. -  Since ``raw`` is variable-length, the total size of this key's -  payload must be ``sizeof(struct fscrypt_provisioning_key_payload)`` -  plus the raw key size.  The process must have Search permission on -  this key. - -  Most users should leave this 0 and specify the raw key directly. -  The support for specifying a Linux keyring key is intended mainly to +- ``key_id`` is 0 if the key is given directly in the ``raw`` field. +  Otherwise ``key_id`` is the ID of a Linux keyring key of type +  "fscrypt-provisioning" whose payload is struct +  fscrypt_provisioning_key_payload whose ``raw`` field contains the +  key, whose ``type`` field matches ``key_spec.type``, and whose +  ``flags`` field matches ``flags``.  Since ``raw`` is +  variable-length, the total size of this key's payload must be +  ``sizeof(struct fscrypt_provisioning_key_payload)`` plus the number +  of key bytes.  The process must have Search permission on this key. + +  Most users should leave this 0 and specify the key directly.  The +  support for specifying a Linux keyring key is intended mainly to    allow re-adding keys after a filesystem is unmounted and re-mounted, -  without having to store the raw keys in userspace memory. +  without having to store the keys in userspace memory. + +- ``flags`` contains optional flags from ``<linux/fscrypt.h>``: + +  - FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED: This denotes that the key is a +    hardware-wrapped key.  See `Hardware-wrapped keys`_.  This flag +    can't be used if FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR is used.  - ``raw`` is a variable-length field which must contain the actual    key, ``raw_size`` bytes long.  Alternatively, if ``key_id`` is -  nonzero, then this field is unused. +  nonzero, then this field is unused.  Note that despite being named +  ``raw``, if FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED is specified then it +  will contain a wrapped key, not a raw key.  For v2 policy keys, the kernel keeps track of which user (identified  by effective user ID) added the key, and only allows the key to be @@ -908,8 +947,8 @@ prevent that other user from unexpectedly removing it.  Therefore,  FS_IOC_ADD_ENCRYPTION_KEY may also be used to add a v2 policy key  *again*, even if it's already added by other user(s).  In this case,  FS_IOC_ADD_ENCRYPTION_KEY will just install a claim to the key for the -current user, rather than actually add the key again (but the raw key -must still be provided, as a proof of knowledge). +current user, rather than actually add the key again (but the key must +still be provided, as a proof of knowledge).  FS_IOC_ADD_ENCRYPTION_KEY returns 0 if either the key or a claim to  the key was either added or already exists. @@ -918,20 +957,23 @@ FS_IOC_ADD_ENCRYPTION_KEY can fail with the following errors:  - ``EACCES``: FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR was specified, but the    caller does not have the CAP_SYS_ADMIN capability in the initial -  user namespace; or the raw key was specified by Linux key ID but the +  user namespace; or the key was specified by Linux key ID but the    process lacks Search permission on the key. +- ``EBADMSG``: invalid hardware-wrapped key  - ``EDQUOT``: the key quota for this user would be exceeded by adding    the key  - ``EINVAL``: invalid key size or key specifier type, or reserved bits    were set -- ``EKEYREJECTED``: the raw key was specified by Linux key ID, but the -  key has the wrong type -- ``ENOKEY``: the raw key was specified by Linux key ID, but no key -  exists with that ID +- ``EKEYREJECTED``: the key was specified by Linux key ID, but the key +  has the wrong type +- ``ENOKEY``: the key was specified by Linux key ID, but no key exists +  with that ID  - ``ENOTTY``: this type of filesystem does not implement encryption  - ``EOPNOTSUPP``: the kernel was not configured with encryption    support for this filesystem, or the filesystem superblock has not -  had encryption enabled on it +  had encryption enabled on it; or a hardware wrapped key was specified +  but the filesystem does not support inline encryption or the hardware +  does not support hardware-wrapped keys  Legacy method  ~~~~~~~~~~~~~ @@ -994,9 +1036,8 @@ or removed by non-root users.  These ioctls don't work on keys that were added via the legacy  process-subscribed keyrings mechanism. -Before using these ioctls, read the `Kernel memory compromise`_ -section for a discussion of the security goals and limitations of -these ioctls. +Before using these ioctls, read the `Online attacks`_ section for a +discussion of the security goals and limitations of these ioctls.  FS_IOC_REMOVE_ENCRYPTION_KEY  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1316,7 +1357,8 @@ inline encryption hardware doesn't have the needed crypto capabilities  (e.g. support for the needed encryption algorithm and data unit size)  and where blk-crypto-fallback is unusable.  (For blk-crypto-fallback  to be usable, it must be enabled in the kernel configuration with -CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y.) +CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y, and the file must be +protected by a raw key rather than a hardware-wrapped key.)  Currently fscrypt always uses the filesystem block size (which is  usually 4096 bytes) as the data unit size.  Therefore, it can only use @@ -1324,7 +1366,76 @@ inline encryption hardware that supports that data unit size.  Inline encryption doesn't affect the ciphertext or other aspects of  the on-disk format, so users may freely switch back and forth between -using "inlinecrypt" and not using "inlinecrypt". +using "inlinecrypt" and not using "inlinecrypt".  An exception is that +files that are protected by a hardware-wrapped key can only be +encrypted/decrypted by the inline encryption hardware and therefore +can only be accessed when the "inlinecrypt" mount option is used.  For +more information about hardware-wrapped keys, see below. + +Hardware-wrapped keys +--------------------- + +fscrypt supports using *hardware-wrapped keys* when the inline +encryption hardware supports it.  Such keys are only present in kernel +memory in wrapped (encrypted) form; they can only be unwrapped +(decrypted) by the inline encryption hardware and are temporally bound +to the current boot.  This prevents the keys from being compromised if +kernel memory is leaked.  This is done without limiting the number of +keys that can be used and while still allowing the execution of +cryptographic tasks that are tied to the same key but can't use inline +encryption hardware, e.g. filenames encryption. + +Note that hardware-wrapped keys aren't specific to fscrypt; they are a +block layer feature (part of *blk-crypto*).  For more details about +hardware-wrapped keys, see the block layer documentation at +:ref:`Documentation/block/inline-encryption.rst +<hardware_wrapped_keys>`.  The rest of this section just focuses on +the details of how fscrypt can use hardware-wrapped keys. + +fscrypt supports hardware-wrapped keys by allowing the fscrypt master +keys to be hardware-wrapped keys as an alternative to raw keys.  To +add a hardware-wrapped key with `FS_IOC_ADD_ENCRYPTION_KEY`_, +userspace must specify FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED in the +``flags`` field of struct fscrypt_add_key_arg and also in the +``flags`` field of struct fscrypt_provisioning_key_payload when +applicable.  The key must be in ephemerally-wrapped form, not +long-term wrapped form. + +Some limitations apply.  First, files protected by a hardware-wrapped +key are tied to the system's inline encryption hardware.  Therefore +they can only be accessed when the "inlinecrypt" mount option is used, +and they can't be included in portable filesystem images.  Second, +currently the hardware-wrapped key support is only compatible with +`IV_INO_LBLK_64 policies`_ and `IV_INO_LBLK_32 policies`_, as it +assumes that there is just one file contents encryption key per +fscrypt master key rather than one per file.  Future work may address +this limitation by passing per-file nonces down the storage stack to +allow the hardware to derive per-file keys. + +Implementation-wise, to encrypt/decrypt the contents of files that are +protected by a hardware-wrapped key, fscrypt uses blk-crypto, +attaching the hardware-wrapped key to the bio crypt contexts.  As is +the case with raw keys, the block layer will program the key into a +keyslot when it isn't already in one.  However, when programming a +hardware-wrapped key, the hardware doesn't program the given key +directly into a keyslot but rather unwraps it (using the hardware's +ephemeral wrapping key) and derives the inline encryption key from it. +The inline encryption key is the key that actually gets programmed +into a keyslot, and it is never exposed to software. + +However, fscrypt doesn't just do file contents encryption; it also +uses its master keys to derive filenames encryption keys, key +identifiers, and sometimes some more obscure types of subkeys such as +dirhash keys.  So even with file contents encryption out of the +picture, fscrypt still needs a raw key to work with.  To get such a +key from a hardware-wrapped key, fscrypt asks the inline encryption +hardware to derive a cryptographically isolated "software secret" from +the hardware-wrapped key.  fscrypt uses this "software secret" to key +its KDF to derive all subkeys other than file contents keys. + +Note that this implies that the hardware-wrapped key feature only +protects the file contents encryption keys.  It doesn't protect other +fscrypt subkeys such as filenames encryption keys.  Direct I/O support  ================== diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 8371e4e1f596..c1d92074b65c 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -12,6 +12,7 @@  #define _FSCRYPT_PRIVATE_H  #include <linux/fscrypt.h> +#include <linux/minmax.h>  #include <linux/siphash.h>  #include <crypto/hash.h>  #include <linux/blk-crypto.h> @@ -27,6 +28,23 @@   */  #define FSCRYPT_MIN_KEY_SIZE	16 +/* Maximum size of a raw fscrypt master key */ +#define FSCRYPT_MAX_RAW_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 */ +#define FSCRYPT_MAX_ANY_KEY_SIZE \ +	MAX(FSCRYPT_MAX_RAW_KEY_SIZE, FSCRYPT_MAX_HW_WRAPPED_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 raw 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 @@ -360,13 +378,15 @@ int fscrypt_init_hkdf(struct fscrypt_hkdf *hkdf, const u8 *master_key,   * outputs are unique and cryptographically isolated, i.e. knowledge of one   * output doesn't reveal another.   */ -#define HKDF_CONTEXT_KEY_IDENTIFIER	1 /* info=<empty>		*/ +#define HKDF_CONTEXT_KEY_IDENTIFIER_FOR_RAW_KEY	1 /* info=<empty>	*/  #define HKDF_CONTEXT_PER_FILE_ENC_KEY	2 /* info=file_nonce		*/  #define HKDF_CONTEXT_DIRECT_KEY		3 /* info=mode_num		*/  #define HKDF_CONTEXT_IV_INO_LBLK_64_KEY	4 /* info=mode_num||fs_uuid	*/  #define HKDF_CONTEXT_DIRHASH_KEY	5 /* info=file_nonce		*/  #define HKDF_CONTEXT_IV_INO_LBLK_32_KEY	6 /* info=mode_num||fs_uuid	*/  #define HKDF_CONTEXT_INODE_HASH_KEY	7 /* info=<empty>		*/ +#define HKDF_CONTEXT_KEY_IDENTIFIER_FOR_HW_WRAPPED_KEY \ +					8 /* info=<empty>		*/  int fscrypt_hkdf_expand(const struct fscrypt_hkdf *hkdf, u8 context,  			const u8 *info, unsigned int infolen, @@ -376,7 +396,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_inode_info *ci); +int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci, +				   bool is_hw_wrapped_key);  static inline bool  fscrypt_using_inline_encryption(const struct fscrypt_inode_info *ci) @@ -385,12 +406,17 @@ fscrypt_using_inline_encryption(const struct fscrypt_inode_info *ci)  }  int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, -				     const u8 *raw_key, +				     const u8 *key_bytes, size_t key_size, +				     bool is_hw_wrapped,  				     const struct fscrypt_inode_info *ci);  void fscrypt_destroy_inline_crypt_key(struct super_block *sb,  				      struct fscrypt_prepared_key *prep_key); +int fscrypt_derive_sw_secret(struct super_block *sb, +			     const u8 *wrapped_key, size_t 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. @@ -414,7 +440,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_inode_info *ci) +static inline int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci, +						 bool is_hw_wrapped_key)  {  	return 0;  } @@ -427,7 +454,8 @@ fscrypt_using_inline_encryption(const struct fscrypt_inode_info *ci)  static inline int  fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, -				 const u8 *raw_key, +				 const u8 *key_bytes, size_t key_size, +				 bool is_hw_wrapped,  				 const struct fscrypt_inode_info *ci)  {  	WARN_ON_ONCE(1); @@ -440,6 +468,15 @@ fscrypt_destroy_inline_crypt_key(struct super_block *sb,  {  } +static inline int +fscrypt_derive_sw_secret(struct super_block *sb, +			 const u8 *wrapped_key, size_t 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_inode_info *ci) @@ -456,20 +493,38 @@ 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;  	/* -	 * Size of the raw key in bytes.  This remains set even if ->raw was +	 * True if this key is a hardware-wrapped key; false if this key is a +	 * raw 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 key in bytes.  This remains set even if ->bytes was  	 * zeroized due to no longer being needed.  I.e. we still remember the  	 * size of the key even if we don't need to remember the key itself.  	 */  	u32			size; -	/* For v1 policy keys: the raw key.  Wiped for v2 policy keys. */ -	u8			raw[FSCRYPT_MAX_KEY_SIZE]; +	/* +	 * The bytes of the key, when still needed.  This can be either a raw +	 * key or a hardware-wrapped key, as indicated by ->is_hw_wrapped.  In +	 * the case of a raw, v2 policy key, there is no need to remember the +	 * actual key separately from ->hkdf so this field will be zeroized as +	 * soon as ->hkdf is initialized. +	 */ +	u8			bytes[FSCRYPT_MAX_ANY_KEY_SIZE];  } __randomize_layout; diff --git a/fs/crypto/hkdf.c b/fs/crypto/hkdf.c index 855a0f4b7318..0f3028adc9c7 100644 --- a/fs/crypto/hkdf.c +++ b/fs/crypto/hkdf.c @@ -1,6 +1,8 @@  // SPDX-License-Identifier: GPL-2.0  /* - * 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 index 7fa53d30aec3..1d008c440cb6 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -89,7 +89,8 @@ static void fscrypt_log_blk_crypto_impl(struct fscrypt_mode *mode,  }  /* Enable inline encryption for this file if supported. */ -int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci) +int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci, +				   bool is_hw_wrapped_key)  {  	const struct inode *inode = ci->ci_inode;  	struct super_block *sb = inode->i_sb; @@ -130,7 +131,8 @@ int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci)  	crypto_cfg.crypto_mode = ci->ci_mode->blk_crypto_mode;  	crypto_cfg.data_unit_size = 1U << ci->ci_data_unit_bits;  	crypto_cfg.dun_bytes = fscrypt_get_dun_bytes(ci); -	crypto_cfg.key_type = BLK_CRYPTO_KEY_TYPE_RAW; +	crypto_cfg.key_type = is_hw_wrapped_key ? +		BLK_CRYPTO_KEY_TYPE_HW_WRAPPED : BLK_CRYPTO_KEY_TYPE_RAW;  	devs = fscrypt_get_devices(sb, &num_devs);  	if (IS_ERR(devs)) @@ -151,12 +153,15 @@ out_free_devs:  }  int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, -				     const u8 *raw_key, +				     const u8 *key_bytes, size_t key_size, +				     bool is_hw_wrapped,  				     const struct fscrypt_inode_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_RAW;  	struct blk_crypto_key *blk_key;  	struct block_device **devs;  	unsigned int num_devs; @@ -167,9 +172,8 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,  	if (!blk_key)  		return -ENOMEM; -	err = blk_crypto_init_key(blk_key, raw_key, ci->ci_mode->keysize, -				  BLK_CRYPTO_KEY_TYPE_RAW, crypto_mode, -				  fscrypt_get_dun_bytes(ci), +	err = blk_crypto_init_key(blk_key, key_bytes, key_size, key_type, +				  crypto_mode, fscrypt_get_dun_bytes(ci),  				  1U << ci->ci_data_unit_bits);  	if (err) {  		fscrypt_err(inode, "error %d initializing blk-crypto key", err); @@ -228,6 +232,34 @@ void fscrypt_destroy_inline_crypt_key(struct super_block *sb,  	kfree_sensitive(blk_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, size_t wrapped_key_size, +			     u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]) +{ +	int err; + +	/* The filesystem must be mounted with -o inlinecrypt. */ +	if (!(sb->s_flags & SB_INLINECRYPT)) { +		fscrypt_warn(NULL, +			     "%s: filesystem not mounted with inlinecrypt\n", +			     sb->s_id); +		return -EOPNOTSUPP; +	} + +	err = blk_crypto_derive_sw_secret(sb->s_bdev, wrapped_key, +					  wrapped_key_size, sw_secret); +	if (err == -EOPNOTSUPP) +		fscrypt_warn(NULL, +			     "%s: block device doesn't support hardware-wrapped keys\n", +			     sb->s_id); +	return err; +} +  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 index 787e9c8938ba..ace369f13068 100644 --- a/fs/crypto/keyring.c +++ b/fs/crypto/keyring.c @@ -149,11 +149,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 +	 * We just charge FSCRYPT_MAX_RAW_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_RAW_KEY_SIZE);  }  static void fscrypt_user_key_describe(const struct key *key, struct seq_file *m) @@ -558,20 +558,45 @@ 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->bytes; +		unsigned int kdf_key_size = secret->size; +		u8 keyid_kdf_ctx = HKDF_CONTEXT_KEY_IDENTIFIER_FOR_RAW_KEY;  		/* -		 * Now that the HKDF context is initialized, the raw key is no -		 * longer needed. +		 * For raw 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->bytes, +						       secret->size, sw_secret); +			if (err) +				return err; +			kdf_key = sw_secret; +			kdf_key_size = sizeof(sw_secret); +			/* +			 * To avoid weird behavior if someone manages to +			 * determine sw_secret and add it as a raw key, ensure +			 * that hardware-wrapped keys and raw keys will have +			 * different key identifiers by deriving their key +			 * identifiers using different KDF contexts. +			 */ +			keyid_kdf_ctx = +				HKDF_CONTEXT_KEY_IDENTIFIER_FOR_HW_WRAPPED_KEY; +		} +		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(secret->raw, secret->size); +		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) @@ -580,19 +605,36 @@ static int add_master_key(struct super_block *sb,  	return do_add_master_key(sb, secret, key_spec);  } +/* + * Validate the size of an fscrypt master key being added.  Note that this is + * just an initial check, as we don't know which ciphers will be used yet. + * There is a stricter size check later when the key is actually used by a file. + */ +static inline bool fscrypt_valid_key_size(size_t size, u32 add_key_flags) +{ +	u32 max_size = (add_key_flags & FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED) ? +		       FSCRYPT_MAX_HW_WRAPPED_KEY_SIZE : +		       FSCRYPT_MAX_RAW_KEY_SIZE; + +	return size >= FSCRYPT_MIN_KEY_SIZE && size <= max_size; +} +  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) +	if (prep->datalen < sizeof(*payload)) +		return -EINVAL; + +	if (!fscrypt_valid_key_size(prep->datalen - sizeof(*payload), +				    payload->flags))  		return -EINVAL;  	if (payload->type != FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR &&  	    payload->type != FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER)  		return -EINVAL; -	if (payload->__reserved) +	if (payload->flags & ~FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED)  		return -EINVAL;  	prep->payload.data[0] = kmemdup(payload, prep->datalen, GFP_KERNEL); @@ -636,21 +678,21 @@ static struct key_type key_type_fscrypt_provisioning = {  };  /* - * Retrieve the raw key from the Linux keyring key specified by 'key_id', and - * store it into 'secret'. + * Retrieve the key from the Linux keyring key specified by 'key_id', and store + * it into 'secret'.   * - * The key must be of type "fscrypt-provisioning" and must have the field - * fscrypt_provisioning_key_payload::type set to 'type', indicating that it's - * only usable with fscrypt with the particular KDF version identified by - * 'type'.  We don't use the "logon" key type because there's no way to - * completely restrict the use of such keys; they can be used by any kernel API - * that accepts "logon" keys and doesn't require a specific service prefix. + * The key must be of type "fscrypt-provisioning" and must have the 'type' and + * 'flags' field of the payload set to the given values, indicating that the key + * is intended for use for the specified purpose.  We don't use the "logon" key + * type because there's no way to completely restrict the use of such keys; they + * can be used by any kernel API that accepts "logon" keys and doesn't require a + * specific service prefix.   *   * The ability to specify the key via Linux keyring key is intended for cases   * where userspace needs to re-add keys after the filesystem is unmounted and - * re-mounted.  Most users should just provide the raw key directly instead. + * re-mounted.  Most users should just provide the key directly instead.   */ -static int get_keyring_key(u32 key_id, u32 type, +static int get_keyring_key(u32 key_id, u32 type, u32 flags,  			   struct fscrypt_master_key_secret *secret)  {  	key_ref_t ref; @@ -667,12 +709,16 @@ static int get_keyring_key(u32 key_id, u32 type,  		goto bad_key;  	payload = key->payload.data[0]; -	/* Don't allow fscrypt v1 keys to be used as v2 keys and vice versa. */ -	if (payload->type != type) +	/* +	 * Don't allow fscrypt v1 keys to be used as v2 keys and vice versa. +	 * Similarly, don't allow hardware-wrapped keys to be used as +	 * non-hardware-wrapped keys and vice versa. +	 */ +	if (payload->type != type || payload->flags != flags)  		goto bad_key;  	secret->size = key->datalen - sizeof(*payload); -	memcpy(secret->raw, payload->raw, secret->size); +	memcpy(secret->bytes, payload->raw, secret->size);  	err = 0;  	goto out_put; @@ -734,19 +780,28 @@ 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); +		err = get_keyring_key(arg.key_id, arg.key_spec.type, arg.flags, +				      &secret);  		if (err)  			goto out_wipe_secret;  	} else { -		if (arg.raw_size < FSCRYPT_MIN_KEY_SIZE || -		    arg.raw_size > FSCRYPT_MAX_KEY_SIZE) +		if (!fscrypt_valid_key_size(arg.raw_size, arg.flags))  			return -EINVAL;  		secret.size = arg.raw_size;  		err = -EFAULT; -		if (copy_from_user(secret.raw, uarg->raw, secret.size)) +		if (copy_from_user(secret.bytes, uarg->raw, secret.size))  			goto out_wipe_secret;  	} @@ -770,13 +825,13 @@ EXPORT_SYMBOL_GPL(fscrypt_ioctl_add_key);  static void  fscrypt_get_test_dummy_secret(struct fscrypt_master_key_secret *secret)  { -	static u8 test_key[FSCRYPT_MAX_KEY_SIZE]; +	static u8 test_key[FSCRYPT_MAX_RAW_KEY_SIZE]; -	get_random_once(test_key, FSCRYPT_MAX_KEY_SIZE); +	get_random_once(test_key, sizeof(test_key));  	memset(secret, 0, sizeof(*secret)); -	secret->size = FSCRYPT_MAX_KEY_SIZE; -	memcpy(secret->raw, test_key, FSCRYPT_MAX_KEY_SIZE); +	secret->size = sizeof(test_key); +	memcpy(secret->bytes, test_key, sizeof(test_key));  }  int fscrypt_get_test_dummy_key_identifier( @@ -787,10 +842,11 @@ int fscrypt_get_test_dummy_key_identifier(  	fscrypt_get_test_dummy_secret(&secret); -	err = fscrypt_init_hkdf(&secret.hkdf, secret.raw, secret.size); +	err = fscrypt_init_hkdf(&secret.hkdf, secret.bytes, secret.size);  	if (err)  		goto out; -	err = fscrypt_hkdf_expand(&secret.hkdf, HKDF_CONTEXT_KEY_IDENTIFIER, +	err = fscrypt_hkdf_expand(&secret.hkdf, +				  HKDF_CONTEXT_KEY_IDENTIFIER_FOR_RAW_KEY,  				  NULL, 0, key_identifier,  				  FSCRYPT_KEY_IDENTIFIER_SIZE);  out: diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index b4fe01ea4bd4..0d71843af946 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -153,7 +153,9 @@ int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key,  	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, +							ci->ci_mode->keysize, +							false, ci);  	tfm = fscrypt_allocate_skcipher(ci->ci_mode, raw_key, ci->ci_inode);  	if (IS_ERR(tfm)) @@ -195,14 +197,29 @@ static int setup_per_mode_enc_key(struct fscrypt_inode_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_RAW_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_ONCE(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 capabilities are 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; @@ -214,6 +231,16 @@ static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci,  	if (fscrypt_is_key_prepared(prep_key, ci))  		goto done_unlock; +	if (use_hw_wrapped_key) { +		err = fscrypt_prepare_inline_crypt_key(prep_key, +						       mk->mk_secret.bytes, +						       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); @@ -336,6 +363,14 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_inode_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 @@ -362,7 +397,7 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_inode_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_RAW_KEY_SIZE];  		err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf,  					  HKDF_CONTEXT_PER_FILE_ENC_KEY, @@ -445,10 +480,6 @@ static int setup_file_encryption_key(struct fscrypt_inode_info *ci,  	struct fscrypt_master_key *mk;  	int err; -	err = fscrypt_select_encryption_impl(ci); -	if (err) -		return err; -  	err = fscrypt_policy_to_key_spec(&ci->ci_policy, &mk_spec);  	if (err)  		return err; @@ -476,6 +507,10 @@ static int setup_file_encryption_key(struct fscrypt_inode_info *ci,  		if (ci->ci_policy.version != FSCRYPT_POLICY_V1)  			return -ENOKEY; +		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 @@ -497,9 +532,21 @@ static int setup_file_encryption_key(struct fscrypt_inode_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: -		err = fscrypt_setup_v1_file_key(ci, mk->mk_secret.raw); +		if (WARN_ON_ONCE(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.bytes);  		break;  	case FSCRYPT_POLICY_V2:  		err = fscrypt_setup_v2_file_key(ci, mk, need_dirhash_key); diff --git a/fs/crypto/keysetup_v1.c b/fs/crypto/keysetup_v1.c index cf3b58ec32cc..b70521c55132 100644 --- a/fs/crypto/keysetup_v1.c +++ b/fs/crypto/keysetup_v1.c @@ -118,7 +118,7 @@ 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 > sizeof(payload->raw)) {  		fscrypt_warn(NULL,  			     "key with description '%s' has invalid payload",  			     key->description); @@ -149,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_RAW_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 index 7a8f4c290187..3aff99f2696a 100644 --- a/include/uapi/linux/fscrypt.h +++ b/include/uapi/linux/fscrypt.h @@ -119,7 +119,7 @@ struct fscrypt_key_specifier {   */  struct fscrypt_provisioning_key_payload {  	__u32 type; -	__u32 __reserved; +	__u32 flags;  	__u8 raw[];  }; @@ -128,7 +128,9 @@ struct fscrypt_add_key_arg {  	struct fscrypt_key_specifier key_spec;  	__u32 raw_size;  	__u32 key_id; -	__u32 __reserved[8]; +#define FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED	0x00000001 +	__u32 flags; +	__u32 __reserved[7];  	__u8 raw[];  }; | 
