lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20200617175931.GB115256@google.com>
Date:   Wed, 17 Jun 2020 10:59:31 -0700
From:   Jaegeuk Kim <jaegeuk@...nel.org>
To:     Satya Tangirala <satyat@...gle.com>
Cc:     linux-fscrypt@...r.kernel.org, linux-fsdevel@...r.kernel.org,
        linux-f2fs-devel@...ts.sourceforge.net, linux-ext4@...r.kernel.org,
        Eric Biggers <ebiggers@...gle.com>
Subject: Re: [PATCH 2/4] fscrypt: add inline encryption support

On 06/17, Satya Tangirala wrote:
> Add support for inline encryption to fs/crypto/.  With "inline
> encryption", the block layer handles the decryption/encryption as part
> of the bio, instead of the filesystem doing the crypto itself via
> Linux's crypto API.  This model is needed in order to take advantage of
> the inline encryption hardware present on most modern mobile SoCs.
> 
> To use inline encryption, the filesystem needs to be mounted with
> '-o inlinecrypt'.  The contents of any encrypted files will then be
> encrypted using blk-crypto, instead of using the traditional
> filesystem-layer crypto. Fscrypt still provides the key and IV to use,
> and the actual ciphertext on-disk is still the same; therefore it's
> testable using the existing fscrypt ciphertext verification tests.
> 
> Note that since blk-crypto has a fallback to Linux's crypto API, and
> also supports all the encryption modes currently supported by fscrypt,
> this feature is usable and testable even without actual inline
> encryption hardware.
> 
> Per-filesystem changes will be needed to set encryption contexts when
> submitting bios and to implement the 'inlinecrypt' mount option.  This
> patch just adds the common code.
> 
> Co-developed-by: Eric Biggers <ebiggers@...gle.com>
> Signed-off-by: Eric Biggers <ebiggers@...gle.com>
> Signed-off-by: Satya Tangirala <satyat@...gle.com>

Reviewed-by: Jaegeuk Kim <jaegeuk@...nel.org>

> ---
>  fs/crypto/Kconfig           |   6 +
>  fs/crypto/Makefile          |   1 +
>  fs/crypto/bio.c             |  50 ++++++
>  fs/crypto/crypto.c          |   2 +-
>  fs/crypto/fname.c           |   4 +-
>  fs/crypto/fscrypt_private.h | 118 ++++++++++--
>  fs/crypto/inline_crypt.c    | 349 ++++++++++++++++++++++++++++++++++++
>  fs/crypto/keyring.c         |   6 +-
>  fs/crypto/keysetup.c        |  68 ++++---
>  fs/crypto/keysetup_v1.c     |  16 +-
>  include/linux/fscrypt.h     |  82 +++++++++
>  11 files changed, 655 insertions(+), 47 deletions(-)
>  create mode 100644 fs/crypto/inline_crypt.c
> 
> diff --git a/fs/crypto/Kconfig b/fs/crypto/Kconfig
> index 8046d7c7a3e9..f1f11a6228eb 100644
> --- a/fs/crypto/Kconfig
> +++ b/fs/crypto/Kconfig
> @@ -24,3 +24,9 @@ config FS_ENCRYPTION_ALGS
>  	select CRYPTO_SHA256
>  	select CRYPTO_SHA512
>  	select CRYPTO_XTS
> +
> +config FS_ENCRYPTION_INLINE_CRYPT
> +	bool "Enable fscrypt to use inline crypto"
> +	depends on FS_ENCRYPTION && BLK_INLINE_ENCRYPTION
> +	help
> +	  Enable fscrypt to use inline encryption hardware if available.
> diff --git a/fs/crypto/Makefile b/fs/crypto/Makefile
> index 232e2bb5a337..652c7180ec6d 100644
> --- a/fs/crypto/Makefile
> +++ b/fs/crypto/Makefile
> @@ -11,3 +11,4 @@ fscrypto-y := crypto.o \
>  	      policy.o
>  
>  fscrypto-$(CONFIG_BLOCK) += bio.o
> +fscrypto-$(CONFIG_FS_ENCRYPTION_INLINE_CRYPT) += inline_crypt.o
> diff --git a/fs/crypto/bio.c b/fs/crypto/bio.c
> index 4fa18fff9c4e..1ea9369a7688 100644
> --- a/fs/crypto/bio.c
> +++ b/fs/crypto/bio.c
> @@ -41,6 +41,52 @@ void fscrypt_decrypt_bio(struct bio *bio)
>  }
>  EXPORT_SYMBOL(fscrypt_decrypt_bio);
>  
> +static int fscrypt_zeroout_range_inline_crypt(const struct inode *inode,
> +					      pgoff_t lblk, sector_t pblk,
> +					      unsigned int len)
> +{
> +	const unsigned int blockbits = inode->i_blkbits;
> +	const unsigned int blocks_per_page = 1 << (PAGE_SHIFT - blockbits);
> +	struct bio *bio;
> +	int ret, err = 0;
> +	int num_pages = 0;
> +
> +	/* This always succeeds since __GFP_DIRECT_RECLAIM is set. */
> +	bio = bio_alloc(GFP_NOFS, BIO_MAX_PAGES);
> +
> +	while (len) {
> +		unsigned int blocks_this_page = min(len, blocks_per_page);
> +		unsigned int bytes_this_page = blocks_this_page << blockbits;
> +
> +		if (num_pages == 0) {
> +			fscrypt_set_bio_crypt_ctx(bio, inode, lblk, GFP_NOFS);
> +			bio_set_dev(bio, inode->i_sb->s_bdev);
> +			bio->bi_iter.bi_sector =
> +					pblk << (blockbits - SECTOR_SHIFT);
> +			bio_set_op_attrs(bio, REQ_OP_WRITE, 0);
> +		}
> +		ret = bio_add_page(bio, ZERO_PAGE(0), bytes_this_page, 0);
> +		if (WARN_ON(ret != bytes_this_page)) {
> +			err = -EIO;
> +			goto out;
> +		}
> +		num_pages++;
> +		len -= blocks_this_page;
> +		lblk += blocks_this_page;
> +		pblk += blocks_this_page;
> +		if (num_pages == BIO_MAX_PAGES || !len) {
> +			err = submit_bio_wait(bio);
> +			if (err)
> +				goto out;
> +			bio_reset(bio);
> +			num_pages = 0;
> +		}
> +	}
> +out:
> +	bio_put(bio);
> +	return err;
> +}
> +
>  /**
>   * fscrypt_zeroout_range() - zero out a range of blocks in an encrypted file
>   * @inode: the file's inode
> @@ -75,6 +121,10 @@ int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk,
>  	if (len == 0)
>  		return 0;
>  
> +	if (fscrypt_inode_uses_inline_crypto(inode))
> +		return fscrypt_zeroout_range_inline_crypt(inode, lblk, pblk,
> +							  len);
> +
>  	BUILD_BUG_ON(ARRAY_SIZE(pages) > BIO_MAX_PAGES);
>  	nr_pages = min_t(unsigned int, ARRAY_SIZE(pages),
>  			 (len + blocks_per_page - 1) >> blocks_per_page_bits);
> diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c
> index ed015cb66c7c..a52cf32733ab 100644
> --- a/fs/crypto/crypto.c
> +++ b/fs/crypto/crypto.c
> @@ -100,7 +100,7 @@ int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw,
>  	DECLARE_CRYPTO_WAIT(wait);
>  	struct scatterlist dst, src;
>  	struct fscrypt_info *ci = inode->i_crypt_info;
> -	struct crypto_skcipher *tfm = ci->ci_ctfm;
> +	struct crypto_skcipher *tfm = ci->ci_enc_key.tfm;
>  	int res = 0;
>  
>  	if (WARN_ON_ONCE(len <= 0))
> diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c
> index 83ca5f1e7934..d828e3df898b 100644
> --- a/fs/crypto/fname.c
> +++ b/fs/crypto/fname.c
> @@ -115,7 +115,7 @@ int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname,
>  	struct skcipher_request *req = NULL;
>  	DECLARE_CRYPTO_WAIT(wait);
>  	const struct fscrypt_info *ci = inode->i_crypt_info;
> -	struct crypto_skcipher *tfm = ci->ci_ctfm;
> +	struct crypto_skcipher *tfm = ci->ci_enc_key.tfm;
>  	union fscrypt_iv iv;
>  	struct scatterlist sg;
>  	int res;
> @@ -171,7 +171,7 @@ static int fname_decrypt(const struct inode *inode,
>  	DECLARE_CRYPTO_WAIT(wait);
>  	struct scatterlist src_sg, dst_sg;
>  	const struct fscrypt_info *ci = inode->i_crypt_info;
> -	struct crypto_skcipher *tfm = ci->ci_ctfm;
> +	struct crypto_skcipher *tfm = ci->ci_enc_key.tfm;
>  	union fscrypt_iv iv;
>  	int res;
>  
> diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
> index eb7fcd2b7fb8..1572186b0db4 100644
> --- a/fs/crypto/fscrypt_private.h
> +++ b/fs/crypto/fscrypt_private.h
> @@ -14,6 +14,7 @@
>  #include <linux/fscrypt.h>
>  #include <linux/siphash.h>
>  #include <crypto/hash.h>
> +#include <linux/blk-crypto.h>
>  
>  #define CONST_STRLEN(str)	(sizeof(str) - 1)
>  
> @@ -166,6 +167,20 @@ struct fscrypt_symlink_data {
>  	char encrypted_path[1];
>  } __packed;
>  
> +/**
> + * struct fscrypt_prepared_key - a key prepared for actual encryption/decryption
> + * @tfm: crypto API transform object
> + * @blk_key: key for blk-crypto
> + *
> + * Normally only one of the fields will be non-NULL.
> + */
> +struct fscrypt_prepared_key {
> +	struct crypto_skcipher *tfm;
> +#ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
> +	struct fscrypt_blk_crypto_key *blk_key;
> +#endif
> +};
> +
>  /*
>   * fscrypt_info - the "encryption key" for an inode
>   *
> @@ -175,12 +190,23 @@ struct fscrypt_symlink_data {
>   */
>  struct fscrypt_info {
>  
> -	/* The actual crypto transform used for encryption and decryption */
> -	struct crypto_skcipher *ci_ctfm;
> +	/* The key in a form prepared for actual encryption/decryption */
> +	struct fscrypt_prepared_key	ci_enc_key;
>  
> -	/* True if the key should be freed when this fscrypt_info is freed */
> +	/*
> +	 * True if the ci_enc_key should be freed when this fscrypt_info is
> +	 * freed
> +	 */
>  	bool ci_owns_key;
>  
> +#ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
> +	/*
> +	 * True if this inode will use inline encryption (blk-crypto) instead of
> +	 * the traditional filesystem-layer encryption.
> +	 */
> +	bool ci_inlinecrypt;
> +#endif
> +
>  	/*
>  	 * Encryption mode used for this inode.  It corresponds to either the
>  	 * contents or filenames encryption mode, depending on the inode type.
> @@ -205,7 +231,7 @@ struct fscrypt_info {
>  
>  	/*
>  	 * If non-NULL, then encryption is done using the master key directly
> -	 * and ci_ctfm will equal ci_direct_key->dk_ctfm.
> +	 * and ci_enc_key will equal ci_direct_key->dk_key.
>  	 */
>  	struct fscrypt_direct_key *ci_direct_key;
>  
> @@ -260,6 +286,7 @@ union fscrypt_iv {
>  		u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE];
>  	};
>  	u8 raw[FSCRYPT_MAX_IV_SIZE];
> +	__le64 dun[FSCRYPT_MAX_IV_SIZE / sizeof(__le64)];
>  };
>  
>  void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num,
> @@ -302,6 +329,74 @@ int fscrypt_hkdf_expand(const struct fscrypt_hkdf *hkdf, u8 context,
>  
>  void fscrypt_destroy_hkdf(struct fscrypt_hkdf *hkdf);
>  
> +/* inline_crypt.c */
> +#ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
> +void fscrypt_select_encryption_impl(struct fscrypt_info *ci);
> +
> +static inline bool
> +fscrypt_using_inline_encryption(const struct fscrypt_info *ci)
> +{
> +	return ci->ci_inlinecrypt;
> +}
> +
> +int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
> +				     const u8 *raw_key,
> +				     const struct fscrypt_info *ci);
> +
> +void fscrypt_destroy_inline_crypt_key(struct fscrypt_prepared_key *prep_key);
> +
> +/*
> + * Check whether the crypto transform or blk-crypto key has been allocated in
> + * @prep_key, depending on which encryption implementation the file will use.
> + */
> +static inline bool
> +fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key,
> +			const struct fscrypt_info *ci)
> +{
> +	/*
> +	 * The READ_ONCE() here pairs with the smp_store_release() in
> +	 * fscrypt_prepare_key().  (This only matters for the per-mode keys,
> +	 * which are shared by multiple inodes.)
> +	 */
> +	if (fscrypt_using_inline_encryption(ci))
> +		return READ_ONCE(prep_key->blk_key) != NULL;
> +	return READ_ONCE(prep_key->tfm) != NULL;
> +}
> +
> +#else /* CONFIG_FS_ENCRYPTION_INLINE_CRYPT */
> +
> +static inline void fscrypt_select_encryption_impl(struct fscrypt_info *ci)
> +{
> +}
> +
> +static inline bool fscrypt_using_inline_encryption(
> +					const struct fscrypt_info *ci)
> +{
> +	return false;
> +}
> +
> +static inline int
> +fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
> +				 const u8 *raw_key,
> +				 const struct fscrypt_info *ci)
> +{
> +	WARN_ON(1);
> +	return -EOPNOTSUPP;
> +}
> +
> +static inline void
> +fscrypt_destroy_inline_crypt_key(struct fscrypt_prepared_key *prep_key)
> +{
> +}
> +
> +static inline bool
> +fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key,
> +			const struct fscrypt_info *ci)
> +{
> +	return READ_ONCE(prep_key->tfm) != NULL;
> +}
> +#endif /* !CONFIG_FS_ENCRYPTION_INLINE_CRYPT */
> +
>  /* keyring.c */
>  
>  /*
> @@ -395,9 +490,9 @@ struct fscrypt_master_key {
>  	 * Per-mode encryption keys for the various types of encryption policies
>  	 * that use them.  Allocated and derived on-demand.
>  	 */
> -	struct crypto_skcipher *mk_direct_keys[__FSCRYPT_MODE_MAX + 1];
> -	struct crypto_skcipher *mk_iv_ino_lblk_64_keys[__FSCRYPT_MODE_MAX + 1];
> -	struct crypto_skcipher *mk_iv_ino_lblk_32_keys[__FSCRYPT_MODE_MAX + 1];
> +	struct fscrypt_prepared_key mk_direct_keys[__FSCRYPT_MODE_MAX + 1];
> +	struct fscrypt_prepared_key mk_iv_ino_lblk_64_keys[__FSCRYPT_MODE_MAX + 1];
> +	struct fscrypt_prepared_key mk_iv_ino_lblk_32_keys[__FSCRYPT_MODE_MAX + 1];
>  
>  	/* Hash key for inode numbers.  Initialized only when needed. */
>  	siphash_key_t		mk_ino_hash_key;
> @@ -461,13 +556,16 @@ struct fscrypt_mode {
>  	int keysize;
>  	int ivsize;
>  	int logged_impl_name;
> +	enum blk_crypto_mode_num blk_crypto_mode;
>  };
>  
>  extern struct fscrypt_mode fscrypt_modes[];
>  
> -struct crypto_skcipher *fscrypt_allocate_skcipher(struct fscrypt_mode *mode,
> -						  const u8 *raw_key,
> -						  const struct inode *inode);
> +int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key,
> +			const u8 *raw_key,
> +			const struct fscrypt_info *ci);
> +
> +void fscrypt_destroy_prepared_key(struct fscrypt_prepared_key *prep_key);
>  
>  int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci, const u8 *raw_key);
>  
> diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c
> new file mode 100644
> index 000000000000..7dbeb49260a6
> --- /dev/null
> +++ b/fs/crypto/inline_crypt.c
> @@ -0,0 +1,349 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Inline encryption support for fscrypt
> + *
> + * Copyright 2019 Google LLC
> + */
> +
> +/*
> + * With "inline encryption", the block layer handles the decryption/encryption
> + * as part of the bio, instead of the filesystem doing the crypto itself via
> + * crypto API.  See Documentation/block/inline-encryption.rst.  fscrypt still
> + * provides the key and IV to use.
> + */
> +
> +#include <linux/blk-crypto.h>
> +#include <linux/blkdev.h>
> +#include <linux/buffer_head.h>
> +#include <linux/sched/mm.h>
> +
> +#include "fscrypt_private.h"
> +
> +struct fscrypt_blk_crypto_key {
> +	struct blk_crypto_key base;
> +	int num_devs;
> +	struct request_queue *devs[];
> +};
> +
> +static int fscrypt_get_num_devices(struct super_block *sb)
> +{
> +	if (sb->s_cop->get_num_devices)
> +		return sb->s_cop->get_num_devices(sb);
> +	return 1;
> +}
> +
> +static void fscrypt_get_devices(struct super_block *sb, int num_devs,
> +				struct request_queue **devs)
> +{
> +	if (num_devs == 1)
> +		devs[0] = bdev_get_queue(sb->s_bdev);
> +	else
> +		sb->s_cop->get_devices(sb, devs);
> +}
> +
> +static unsigned int fscrypt_get_dun_bytes(const struct fscrypt_info *ci)
> +{
> +	struct super_block *sb = ci->ci_inode->i_sb;
> +	unsigned int flags = fscrypt_policy_flags(&ci->ci_policy);
> +	int ino_bits = 64, lblk_bits = 64;
> +
> +	if (flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY)
> +		return offsetofend(union fscrypt_iv, nonce);
> +
> +	if (flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64)
> +		return sizeof(__le64);
> +
> +	if (flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32)
> +		return sizeof(__le32);
> +
> +	/* Default case: IVs are just the file logical block number */
> +	if (sb->s_cop->get_ino_and_lblk_bits)
> +		sb->s_cop->get_ino_and_lblk_bits(sb, &ino_bits, &lblk_bits);
> +	return DIV_ROUND_UP(lblk_bits, 8);
> +}
> +
> +/* Enable inline encryption for this file if supported. */
> +void fscrypt_select_encryption_impl(struct fscrypt_info *ci)
> +{
> +	const struct inode *inode = ci->ci_inode;
> +	struct super_block *sb = inode->i_sb;
> +	struct blk_crypto_config crypto_cfg;
> +	int num_devs;
> +	struct request_queue **devs;
> +	int i;
> +
> +	/* The file must need contents encryption, not filenames encryption */
> +	if (!fscrypt_needs_contents_encryption(inode))
> +		return;
> +
> +	/* The crypto mode must be valid */
> +	if (ci->ci_mode->blk_crypto_mode == BLK_ENCRYPTION_MODE_INVALID)
> +		return;
> +
> +	/* The filesystem must be mounted with -o inlinecrypt */
> +	if (!(sb->s_flags & SB_INLINECRYPT))
> +		return;
> +
> +	/*
> +	 * blk-crypto must support the crypto configuration we'll use for the
> +	 * inode on all devices in the sb
> +	 */
> +	crypto_cfg.crypto_mode = ci->ci_mode->blk_crypto_mode;
> +	crypto_cfg.data_unit_size = sb->s_blocksize;
> +	crypto_cfg.dun_bytes = fscrypt_get_dun_bytes(ci);
> +	num_devs = fscrypt_get_num_devices(sb);
> +	devs = kmalloc_array(num_devs, sizeof(*devs), GFP_NOFS);
> +	if (!devs)
> +		return;
> +	fscrypt_get_devices(sb, num_devs, devs);
> +
> +	for (i = 0; i < num_devs; i++) {
> +		if (!blk_crypto_config_supported(devs[i], &crypto_cfg))
> +			goto out_free_devs;
> +	}
> +
> +	ci->ci_inlinecrypt = true;
> +out_free_devs:
> +	kfree(devs);
> +}
> +
> +int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
> +				     const u8 *raw_key,
> +				     const struct fscrypt_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;
> +	int num_devs = fscrypt_get_num_devices(sb);
> +	int queue_refs = 0;
> +	struct fscrypt_blk_crypto_key *blk_key;
> +	int err;
> +	int i;
> +	unsigned int flags;
> +
> +	blk_key = kzalloc(struct_size(blk_key, devs, num_devs), GFP_NOFS);
> +	if (!blk_key)
> +		return -ENOMEM;
> +
> +	blk_key->num_devs = num_devs;
> +	fscrypt_get_devices(sb, num_devs, blk_key->devs);
> +
> +	err = blk_crypto_init_key(&blk_key->base, raw_key, crypto_mode,
> +				  fscrypt_get_dun_bytes(ci), sb->s_blocksize);
> +	if (err) {
> +		fscrypt_err(inode, "error %d initializing blk-crypto key", err);
> +		goto fail;
> +	}
> +
> +	/*
> +	 * We have to start using blk-crypto on all the filesystem's devices.
> +	 * We also have to save all the request_queue's for later so that the
> +	 * key can be evicted from them.  This is needed because some keys
> +	 * aren't destroyed until after the filesystem was already unmounted
> +	 * (namely, the per-mode keys in struct fscrypt_master_key).
> +	 */
> +	for (i = 0; i < num_devs; i++) {
> +		if (!blk_get_queue(blk_key->devs[i])) {
> +			fscrypt_err(inode, "couldn't get request_queue");
> +			err = -EAGAIN;
> +			goto fail;
> +		}
> +		queue_refs++;
> +
> +		flags = memalloc_nofs_save();
> +		err = blk_crypto_start_using_key(&blk_key->base,
> +						 blk_key->devs[i]);
> +		memalloc_nofs_restore(flags);
> +		if (err) {
> +			fscrypt_err(inode,
> +				    "error %d starting to use blk-crypto", err);
> +			goto fail;
> +		}
> +	}
> +	/*
> +	 * Pairs with READ_ONCE() in fscrypt_is_key_prepared().  (Only matters
> +	 * for the per-mode keys, which are shared by multiple inodes.)
> +	 */
> +	smp_store_release(&prep_key->blk_key, blk_key);
> +	return 0;
> +
> +fail:
> +	for (i = 0; i < queue_refs; i++)
> +		blk_put_queue(blk_key->devs[i]);
> +	kzfree(blk_key);
> +	return err;
> +}
> +
> +void fscrypt_destroy_inline_crypt_key(struct fscrypt_prepared_key *prep_key)
> +{
> +	struct fscrypt_blk_crypto_key *blk_key = prep_key->blk_key;
> +	int i;
> +
> +	if (blk_key) {
> +		for (i = 0; i < blk_key->num_devs; i++) {
> +			blk_crypto_evict_key(blk_key->devs[i], &blk_key->base);
> +			blk_put_queue(blk_key->devs[i]);
> +		}
> +		kzfree(blk_key);
> +	}
> +}
> +
> +bool __fscrypt_inode_uses_inline_crypto(const struct inode *inode)
> +{
> +	return inode->i_crypt_info->ci_inlinecrypt;
> +}
> +EXPORT_SYMBOL_GPL(__fscrypt_inode_uses_inline_crypto);
> +
> +static void fscrypt_generate_dun(const struct fscrypt_info *ci, u64 lblk_num,
> +				 u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE])
> +{
> +	union fscrypt_iv iv;
> +	int i;
> +
> +	fscrypt_generate_iv(&iv, lblk_num, ci);
> +
> +	BUILD_BUG_ON(FSCRYPT_MAX_IV_SIZE > BLK_CRYPTO_MAX_IV_SIZE);
> +	memset(dun, 0, BLK_CRYPTO_MAX_IV_SIZE);
> +	for (i = 0; i < ci->ci_mode->ivsize/sizeof(dun[0]); i++)
> +		dun[i] = le64_to_cpu(iv.dun[i]);
> +}
> +
> +/**
> + * fscrypt_set_bio_crypt_ctx() - prepare a file contents bio for inline crypto
> + * @bio: a bio which will eventually be submitted to the file
> + * @inode: the file's inode
> + * @first_lblk: the first file logical block number in the I/O
> + * @gfp_mask: memory allocation flags - these must be a waiting mask so that
> + *					bio_crypt_set_ctx can't fail.
> + *
> + * If the contents of the file should be encrypted (or decrypted) with inline
> + * encryption, then assign the appropriate encryption context to the bio.
> + *
> + * Normally the bio should be newly allocated (i.e. no pages added yet), as
> + * otherwise fscrypt_mergeable_bio() won't work as intended.
> + *
> + * The encryption context will be freed automatically when the bio is freed.
> + */
> +void fscrypt_set_bio_crypt_ctx(struct bio *bio, const struct inode *inode,
> +			       u64 first_lblk, gfp_t gfp_mask)
> +{
> +	const struct fscrypt_info *ci = inode->i_crypt_info;
> +	u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE];
> +
> +	if (!fscrypt_inode_uses_inline_crypto(inode))
> +		return;
> +
> +	fscrypt_generate_dun(ci, first_lblk, dun);
> +	bio_crypt_set_ctx(bio, &ci->ci_enc_key.blk_key->base, dun, gfp_mask);
> +}
> +EXPORT_SYMBOL_GPL(fscrypt_set_bio_crypt_ctx);
> +
> +/* Extract the inode and logical block number from a buffer_head. */
> +static bool bh_get_inode_and_lblk_num(const struct buffer_head *bh,
> +				      const struct inode **inode_ret,
> +				      u64 *lblk_num_ret)
> +{
> +	struct page *page = bh->b_page;
> +	const struct address_space *mapping;
> +	const struct inode *inode;
> +
> +	/*
> +	 * The ext4 journal (jbd2) can submit a buffer_head it directly created
> +	 * for a non-pagecache page.  fscrypt doesn't care about these.
> +	 */
> +	mapping = page_mapping(page);
> +	if (!mapping)
> +		return false;
> +	inode = mapping->host;
> +
> +	*inode_ret = inode;
> +	*lblk_num_ret = ((u64)page->index << (PAGE_SHIFT - inode->i_blkbits)) +
> +			(bh_offset(bh) >> inode->i_blkbits);
> +	return true;
> +}
> +
> +/**
> + * fscrypt_set_bio_crypt_ctx_bh() - prepare a file contents bio for inline
> + *				    crypto
> + * @bio: a bio which will eventually be submitted to the file
> + * @first_bh: the first buffer_head for which I/O will be submitted
> + * @gfp_mask: memory allocation flags
> + *
> + * Same as fscrypt_set_bio_crypt_ctx(), except this takes a buffer_head instead
> + * of an inode and block number directly.
> + */
> +void fscrypt_set_bio_crypt_ctx_bh(struct bio *bio,
> +				  const struct buffer_head *first_bh,
> +				  gfp_t gfp_mask)
> +{
> +	const struct inode *inode;
> +	u64 first_lblk;
> +
> +	if (bh_get_inode_and_lblk_num(first_bh, &inode, &first_lblk))
> +		fscrypt_set_bio_crypt_ctx(bio, inode, first_lblk, gfp_mask);
> +}
> +EXPORT_SYMBOL_GPL(fscrypt_set_bio_crypt_ctx_bh);
> +
> +/**
> + * fscrypt_mergeable_bio() - test whether data can be added to a bio
> + * @bio: the bio being built up
> + * @inode: the inode for the next part of the I/O
> + * @next_lblk: the next file logical block number in the I/O
> + *
> + * When building a bio which may contain data which should undergo inline
> + * encryption (or decryption) via fscrypt, filesystems should call this function
> + * to ensure that the resulting bio contains only logically contiguous data.
> + * This will return false if the next part of the I/O cannot be merged with the
> + * bio because either the encryption key would be different or the encryption
> + * data unit numbers would be discontiguous.
> + *
> + * fscrypt_set_bio_crypt_ctx() must have already been called on the bio.
> + *
> + * Return: true iff the I/O is mergeable
> + */
> +bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode,
> +			   u64 next_lblk)
> +{
> +	const struct bio_crypt_ctx *bc = bio->bi_crypt_context;
> +	u64 next_dun[BLK_CRYPTO_DUN_ARRAY_SIZE];
> +
> +	if (!!bc != fscrypt_inode_uses_inline_crypto(inode))
> +		return false;
> +	if (!bc)
> +		return true;
> +
> +	/*
> +	 * Comparing the key pointers is good enough, as all I/O for each key
> +	 * uses the same pointer.  I.e., there's currently no need to support
> +	 * merging requests where the keys are the same but the pointers differ.
> +	 */
> +	if (bc->bc_key != &inode->i_crypt_info->ci_enc_key.blk_key->base)
> +		return false;
> +
> +	fscrypt_generate_dun(inode->i_crypt_info, next_lblk, next_dun);
> +	return bio_crypt_dun_is_contiguous(bc, bio->bi_iter.bi_size, next_dun);
> +}
> +EXPORT_SYMBOL_GPL(fscrypt_mergeable_bio);
> +
> +/**
> + * fscrypt_mergeable_bio_bh() - test whether data can be added to a bio
> + * @bio: the bio being built up
> + * @next_bh: the next buffer_head for which I/O will be submitted
> + *
> + * Same as fscrypt_mergeable_bio(), except this takes a buffer_head instead of
> + * an inode and block number directly.
> + *
> + * Return: true iff the I/O is mergeable
> + */
> +bool fscrypt_mergeable_bio_bh(struct bio *bio,
> +			      const struct buffer_head *next_bh)
> +{
> +	const struct inode *inode;
> +	u64 next_lblk;
> +
> +	if (!bh_get_inode_and_lblk_num(next_bh, &inode, &next_lblk))
> +		return !bio->bi_crypt_context;
> +
> +	return fscrypt_mergeable_bio(bio, inode, next_lblk);
> +}
> +EXPORT_SYMBOL_GPL(fscrypt_mergeable_bio_bh);
> diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c
> index e24eb48bfbe1..7f8ac61a20d6 100644
> --- a/fs/crypto/keyring.c
> +++ b/fs/crypto/keyring.c
> @@ -45,9 +45,9 @@ static void free_master_key(struct fscrypt_master_key *mk)
>  	wipe_master_key_secret(&mk->mk_secret);
>  
>  	for (i = 0; i <= __FSCRYPT_MODE_MAX; i++) {
> -		crypto_free_skcipher(mk->mk_direct_keys[i]);
> -		crypto_free_skcipher(mk->mk_iv_ino_lblk_64_keys[i]);
> -		crypto_free_skcipher(mk->mk_iv_ino_lblk_32_keys[i]);
> +		fscrypt_destroy_prepared_key(&mk->mk_direct_keys[i]);
> +		fscrypt_destroy_prepared_key(&mk->mk_iv_ino_lblk_64_keys[i]);
> +		fscrypt_destroy_prepared_key(&mk->mk_iv_ino_lblk_32_keys[i]);
>  	}
>  
>  	key_put(mk->mk_users);
> diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
> index 1129adfa097d..c789ac30af7c 100644
> --- a/fs/crypto/keysetup.c
> +++ b/fs/crypto/keysetup.c
> @@ -19,6 +19,7 @@ struct fscrypt_mode fscrypt_modes[] = {
>  		.cipher_str = "xts(aes)",
>  		.keysize = 64,
>  		.ivsize = 16,
> +		.blk_crypto_mode = BLK_ENCRYPTION_MODE_AES_256_XTS,
>  	},
>  	[FSCRYPT_MODE_AES_256_CTS] = {
>  		.friendly_name = "AES-256-CTS-CBC",
> @@ -31,6 +32,7 @@ struct fscrypt_mode fscrypt_modes[] = {
>  		.cipher_str = "essiv(cbc(aes),sha256)",
>  		.keysize = 16,
>  		.ivsize = 16,
> +		.blk_crypto_mode = BLK_ENCRYPTION_MODE_AES_128_CBC_ESSIV,
>  	},
>  	[FSCRYPT_MODE_AES_128_CTS] = {
>  		.friendly_name = "AES-128-CTS-CBC",
> @@ -43,6 +45,7 @@ struct fscrypt_mode fscrypt_modes[] = {
>  		.cipher_str = "adiantum(xchacha12,aes)",
>  		.keysize = 32,
>  		.ivsize = 32,
> +		.blk_crypto_mode = BLK_ENCRYPTION_MODE_ADIANTUM,
>  	},
>  };
>  
> @@ -64,9 +67,9 @@ select_encryption_mode(const union fscrypt_policy *policy,
>  }
>  
>  /* Create a symmetric cipher object for the given encryption mode and key */
> -struct crypto_skcipher *fscrypt_allocate_skcipher(struct fscrypt_mode *mode,
> -						  const u8 *raw_key,
> -						  const struct inode *inode)
> +static struct crypto_skcipher *
> +fscrypt_allocate_skcipher(struct fscrypt_mode *mode, const u8 *raw_key,
> +			  const struct inode *inode)
>  {
>  	struct crypto_skcipher *tfm;
>  	int err;
> @@ -109,30 +112,54 @@ struct crypto_skcipher *fscrypt_allocate_skcipher(struct fscrypt_mode *mode,
>  	return ERR_PTR(err);
>  }
>  
> -/* Given a per-file encryption key, set up the file's crypto transform object */
> -int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci, const u8 *raw_key)
> +/*
> + * Prepare the crypto transform object or blk-crypto key in @prep_key, given the
> + * raw key, encryption mode, and flag indicating which encryption implementation
> + * (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)
>  {
>  	struct crypto_skcipher *tfm;
>  
> +	if (fscrypt_using_inline_encryption(ci))
> +		return fscrypt_prepare_inline_crypt_key(prep_key, raw_key, ci);
> +
>  	tfm = fscrypt_allocate_skcipher(ci->ci_mode, raw_key, ci->ci_inode);
>  	if (IS_ERR(tfm))
>  		return PTR_ERR(tfm);
> +	/*
> +	 * Pairs with READ_ONCE() in fscrypt_is_key_prepared().  (Only matters
> +	 * for the per-mode keys, which are shared by multiple inodes.)
> +	 */
> +	smp_store_release(&prep_key->tfm, tfm);
> +	return 0;
> +}
> +
> +/* Destroy a crypto transform object and/or blk-crypto key. */
> +void fscrypt_destroy_prepared_key(struct fscrypt_prepared_key *prep_key)
> +{
> +	crypto_free_skcipher(prep_key->tfm);
> +	fscrypt_destroy_inline_crypt_key(prep_key);
> +}
>  
> -	ci->ci_ctfm = tfm;
> +/* Given a per-file encryption key, set up the file's crypto transform object */
> +int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci, const u8 *raw_key)
> +{
>  	ci->ci_owns_key = true;
> -	return 0;
> +	return fscrypt_prepare_key(&ci->ci_enc_key, raw_key, ci);
>  }
>  
>  static int setup_per_mode_enc_key(struct fscrypt_info *ci,
>  				  struct fscrypt_master_key *mk,
> -				  struct crypto_skcipher **tfms,
> +				  struct fscrypt_prepared_key *keys,
>  				  u8 hkdf_context, bool include_fs_uuid)
>  {
>  	const struct inode *inode = ci->ci_inode;
>  	const struct super_block *sb = inode->i_sb;
>  	struct fscrypt_mode *mode = ci->ci_mode;
>  	const u8 mode_num = mode - fscrypt_modes;
> -	struct crypto_skcipher *tfm;
> +	struct fscrypt_prepared_key *prep_key;
>  	u8 mode_key[FSCRYPT_MAX_KEY_SIZE];
>  	u8 hkdf_info[sizeof(mode_num) + sizeof(sb->s_uuid)];
>  	unsigned int hkdf_infolen = 0;
> @@ -141,16 +168,15 @@ static int setup_per_mode_enc_key(struct fscrypt_info *ci,
>  	if (WARN_ON(mode_num > __FSCRYPT_MODE_MAX))
>  		return -EINVAL;
>  
> -	/* pairs with smp_store_release() below */
> -	tfm = READ_ONCE(tfms[mode_num]);
> -	if (likely(tfm != NULL)) {
> -		ci->ci_ctfm = tfm;
> +	prep_key = &keys[mode_num];
> +	if (fscrypt_is_key_prepared(prep_key, ci)) {
> +		ci->ci_enc_key = *prep_key;
>  		return 0;
>  	}
>  
>  	mutex_lock(&fscrypt_mode_key_setup_mutex);
>  
> -	if (tfms[mode_num])
> +	if (fscrypt_is_key_prepared(prep_key, ci))
>  		goto done_unlock;
>  
>  	BUILD_BUG_ON(sizeof(mode_num) != 1);
> @@ -167,16 +193,12 @@ static int setup_per_mode_enc_key(struct fscrypt_info *ci,
>  				  mode_key, mode->keysize);
>  	if (err)
>  		goto out_unlock;
> -	tfm = fscrypt_allocate_skcipher(mode, mode_key, inode);
> +	err = fscrypt_prepare_key(prep_key, mode_key, ci);
>  	memzero_explicit(mode_key, mode->keysize);
> -	if (IS_ERR(tfm)) {
> -		err = PTR_ERR(tfm);
> +	if (err)
>  		goto out_unlock;
> -	}
> -	/* pairs with READ_ONCE() above */
> -	smp_store_release(&tfms[mode_num], tfm);
>  done_unlock:
> -	ci->ci_ctfm = tfm;
> +	ci->ci_enc_key = *prep_key;
>  	err = 0;
>  out_unlock:
>  	mutex_unlock(&fscrypt_mode_key_setup_mutex);
> @@ -310,6 +332,8 @@ static int setup_file_encryption_key(struct fscrypt_info *ci,
>  	struct fscrypt_key_specifier mk_spec;
>  	int err;
>  
> +	fscrypt_select_encryption_impl(ci);
> +
>  	switch (ci->ci_policy.version) {
>  	case FSCRYPT_POLICY_V1:
>  		mk_spec.type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR;
> @@ -402,7 +426,7 @@ static void put_crypt_info(struct fscrypt_info *ci)
>  	if (ci->ci_direct_key)
>  		fscrypt_put_direct_key(ci->ci_direct_key);
>  	else if (ci->ci_owns_key)
> -		crypto_free_skcipher(ci->ci_ctfm);
> +		fscrypt_destroy_prepared_key(&ci->ci_enc_key);
>  
>  	key = ci->ci_master_key;
>  	if (key) {
> diff --git a/fs/crypto/keysetup_v1.c b/fs/crypto/keysetup_v1.c
> index 801b48c0cd7f..a52686729a67 100644
> --- a/fs/crypto/keysetup_v1.c
> +++ b/fs/crypto/keysetup_v1.c
> @@ -146,7 +146,7 @@ struct fscrypt_direct_key {
>  	struct hlist_node		dk_node;
>  	refcount_t			dk_refcount;
>  	const struct fscrypt_mode	*dk_mode;
> -	struct crypto_skcipher		*dk_ctfm;
> +	struct fscrypt_prepared_key	dk_key;
>  	u8				dk_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE];
>  	u8				dk_raw[FSCRYPT_MAX_KEY_SIZE];
>  };
> @@ -154,7 +154,7 @@ struct fscrypt_direct_key {
>  static void free_direct_key(struct fscrypt_direct_key *dk)
>  {
>  	if (dk) {
> -		crypto_free_skcipher(dk->dk_ctfm);
> +		fscrypt_destroy_prepared_key(&dk->dk_key);
>  		kzfree(dk);
>  	}
>  }
> @@ -199,6 +199,8 @@ find_or_insert_direct_key(struct fscrypt_direct_key *to_insert,
>  			continue;
>  		if (ci->ci_mode != dk->dk_mode)
>  			continue;
> +		if (!fscrypt_is_key_prepared(&dk->dk_key, ci))
> +			continue;
>  		if (crypto_memneq(raw_key, dk->dk_raw, ci->ci_mode->keysize))
>  			continue;
>  		/* using existing tfm with same (descriptor, mode, raw_key) */
> @@ -231,13 +233,9 @@ 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;
> -	dk->dk_ctfm = fscrypt_allocate_skcipher(ci->ci_mode, raw_key,
> -						ci->ci_inode);
> -	if (IS_ERR(dk->dk_ctfm)) {
> -		err = PTR_ERR(dk->dk_ctfm);
> -		dk->dk_ctfm = NULL;
> +	err = fscrypt_prepare_key(&dk->dk_key, raw_key, ci);
> +	if (err)
>  		goto err_free_dk;
> -	}
>  	memcpy(dk->dk_descriptor, ci->ci_policy.v1.master_key_descriptor,
>  	       FSCRYPT_KEY_DESCRIPTOR_SIZE);
>  	memcpy(dk->dk_raw, raw_key, ci->ci_mode->keysize);
> @@ -259,7 +257,7 @@ static int setup_v1_file_key_direct(struct fscrypt_info *ci,
>  	if (IS_ERR(dk))
>  		return PTR_ERR(dk);
>  	ci->ci_direct_key = dk;
> -	ci->ci_ctfm = dk->dk_ctfm;
> +	ci->ci_enc_key = dk->dk_key;
>  	return 0;
>  }
>  
> diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
> index 2862ca5fea33..bb257411365f 100644
> --- a/include/linux/fscrypt.h
> +++ b/include/linux/fscrypt.h
> @@ -69,6 +69,9 @@ struct fscrypt_operations {
>  	bool (*has_stable_inodes)(struct super_block *sb);
>  	void (*get_ino_and_lblk_bits)(struct super_block *sb,
>  				      int *ino_bits_ret, int *lblk_bits_ret);
> +	int (*get_num_devices)(struct super_block *sb);
> +	void (*get_devices)(struct super_block *sb,
> +			    struct request_queue **devs);
>  };
>  
>  static inline bool fscrypt_has_encryption_key(const struct inode *inode)
> @@ -537,6 +540,85 @@ static inline void fscrypt_set_ops(struct super_block *sb,
>  
>  #endif	/* !CONFIG_FS_ENCRYPTION */
>  
> +/* inline_crypt.c */
> +#ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
> +
> +bool __fscrypt_inode_uses_inline_crypto(const struct inode *inode);
> +
> +void fscrypt_set_bio_crypt_ctx(struct bio *bio,
> +			       const struct inode *inode, u64 first_lblk,
> +			       gfp_t gfp_mask);
> +
> +void fscrypt_set_bio_crypt_ctx_bh(struct bio *bio,
> +				  const struct buffer_head *first_bh,
> +				  gfp_t gfp_mask);
> +
> +bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode,
> +			   u64 next_lblk);
> +
> +bool fscrypt_mergeable_bio_bh(struct bio *bio,
> +			      const struct buffer_head *next_bh);
> +
> +#else /* CONFIG_FS_ENCRYPTION_INLINE_CRYPT */
> +
> +static inline bool __fscrypt_inode_uses_inline_crypto(const struct inode *inode)
> +{
> +	return false;
> +}
> +
> +static inline void fscrypt_set_bio_crypt_ctx(struct bio *bio,
> +					     const struct inode *inode,
> +					     u64 first_lblk, gfp_t gfp_mask) { }
> +
> +static inline void fscrypt_set_bio_crypt_ctx_bh(
> +					 struct bio *bio,
> +					 const struct buffer_head *first_bh,
> +					 gfp_t gfp_mask) { }
> +
> +static inline bool fscrypt_mergeable_bio(struct bio *bio,
> +					 const struct inode *inode,
> +					 u64 next_lblk)
> +{
> +	return true;
> +}
> +
> +static inline bool fscrypt_mergeable_bio_bh(struct bio *bio,
> +					    const struct buffer_head *next_bh)
> +{
> +	return true;
> +}
> +#endif /* !CONFIG_FS_ENCRYPTION_INLINE_CRYPT */
> +
> +/**
> + * fscrypt_inode_uses_inline_crypto() - test whether an inode uses inline
> + *					encryption
> + * @inode: an inode. If encrypted, its key must be set up.
> + *
> + * Return: true if the inode requires file contents encryption and if the
> + *	   encryption should be done in the block layer via blk-crypto rather
> + *	   than in the filesystem layer.
> + */
> +static inline bool fscrypt_inode_uses_inline_crypto(const struct inode *inode)
> +{
> +	return fscrypt_needs_contents_encryption(inode) &&
> +	       __fscrypt_inode_uses_inline_crypto(inode);
> +}
> +
> +/**
> + * fscrypt_inode_uses_fs_layer_crypto() - test whether an inode uses fs-layer
> + *					  encryption
> + * @inode: an inode. If encrypted, its key must be set up.
> + *
> + * Return: true if the inode requires file contents encryption and if the
> + *	   encryption should be done in the filesystem layer rather than in the
> + *	   block layer via blk-crypto.
> + */
> +static inline bool fscrypt_inode_uses_fs_layer_crypto(const struct inode *inode)
> +{
> +	return fscrypt_needs_contents_encryption(inode) &&
> +	       !__fscrypt_inode_uses_inline_crypto(inode);
> +}
> +
>  /**
>   * fscrypt_require_key() - require an inode's encryption key
>   * @inode: the inode we need the key for
> -- 
> 2.27.0.290.gba653c62da-goog

Powered by blists - more mailing lists