diff mbox series

[RFC,07/13] fscrypt: store full fscrypt_contexts for each extent

Message ID e0263ab5998ddf723b78ed56a545490e482c29b9.1693630890.git.sweettea-kernel@dorminy.me (mailing list archive)
State Superseded
Headers show
Series fscrypt: add extent encryption | expand

Commit Message

Sweet Tea Dorminy Sept. 2, 2023, 5:54 a.m. UTC
For contrast purposes, this patch contains the entirety of the changes
necessary to switch between lightweight and heavyweight extents. This
patch could be dropped, or rolled into the former change, without
changing anything else.

Lightweight extents relying on their parent inode's context for
key and policy information do take up less disk space. Additionally,
they guarantee that if inode open succeeds, then all extents will be
readable and writeable, matching the current inode-based fscrypt
behavior.

However, heavyweight extents permit greater flexibility for future
extensions:

- Any form of changing the key for a non-empty directory's
  future writes requires that extents have some sort of policy in
  addition to the nonce, which is essentially the contents of the full
  fscrypt_context.
  - This could be approximated using overlayfs writing to a new
    encrypted directory, but this would waste space used by overwritten
    data and makes it very difficult to have nested subvolumes each with
    their own key, so it's very preferable to support this natively in
    btrfs.

- Scrub (verifying checksums) currently iterates over extents,
without interacting with inodes; in an authenticated encryption world,
scrub verifying authentication tags would need to iterate over inodes (a
large departure from the present) or need heavyweight extents storing
the necessary key information.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/crypto/keysetup.c | 20 ++++++++++----------
 fs/crypto/policy.c   |  5 ++---
 2 files changed, 12 insertions(+), 13 deletions(-)

Comments

Neal Gompa Sept. 5, 2023, 10:10 p.m. UTC | #1
On Sat, Sep 2, 2023 at 1:56 AM Sweet Tea Dorminy
<sweettea-kernel@dorminy.me> wrote:
>
> For contrast purposes, this patch contains the entirety of the changes
> necessary to switch between lightweight and heavyweight extents. This
> patch could be dropped, or rolled into the former change, without
> changing anything else.
>
> Lightweight extents relying on their parent inode's context for
> key and policy information do take up less disk space. Additionally,
> they guarantee that if inode open succeeds, then all extents will be
> readable and writeable, matching the current inode-based fscrypt
> behavior.
>
> However, heavyweight extents permit greater flexibility for future
> extensions:
>
> - Any form of changing the key for a non-empty directory's
>   future writes requires that extents have some sort of policy in
>   addition to the nonce, which is essentially the contents of the full
>   fscrypt_context.
>   - This could be approximated using overlayfs writing to a new
>     encrypted directory, but this would waste space used by overwritten
>     data and makes it very difficult to have nested subvolumes each with
>     their own key, so it's very preferable to support this natively in
>     btrfs.
>
> - Scrub (verifying checksums) currently iterates over extents,
> without interacting with inodes; in an authenticated encryption world,
> scrub verifying authentication tags would need to iterate over inodes (a
> large departure from the present) or need heavyweight extents storing
> the necessary key information.
>

I've been thinking about this patch set a bit since it was posted, and
I've got some thoughts specifically about this.

A use-case that is extremely important to me is enabling background
encryption of parts or the whole filesystem, but also enabling
rekeying the encrypted data too. This can be necessary for a variety
of reasons. This is supported in LUKS/dm-crypt and I would like to
have this supported in Btrfs native encryption through fscrypt.

My understanding of this (after following all the discussions and
patch sets) is that heavyweight extents make this a fundamentally
cheaper and safer operation because there's no need to traverse
through the inodes and change them. This would be fairly expensive
and create a situation where inode sharing becomes much more limited
as rekeying occurs on active data while not occurring on old
snapshots, for example.

It's certainly a trade-off that leads to more complexity, but I think
it's worth it because key properties that people rely on with Btrfs
can be preserved even with encrypted data.



--
真実はいつも一つ!/ Always, there's only one truth!
diff mbox series

Patch

diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index 90143377cc61..4146b1380cb5 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -1061,25 +1061,25 @@  int fscrypt_load_extent_info(struct inode *inode, void *buf, size_t len,
 {
 	int res;
 	union fscrypt_context ctx;
-	const union fscrypt_policy *policy;
+	union fscrypt_policy policy;
 
 	if (!fscrypt_has_encryption_key(inode))
 		return -EINVAL;
 
-	if (len != FSCRYPT_FILE_NONCE_SIZE) {
+	memcpy(&ctx, buf, len);
+
+	res = fscrypt_policy_from_context(&policy, &ctx, len);
+	if (res) {
 		fscrypt_warn(inode,
 			     "Unrecognized or corrupt encryption context");
-		return -EINVAL;
+		return res;
 	}
 
-	policy = fscrypt_policy_to_inherit(inode);
-	if (policy == NULL)
-		return 0;
-	if (IS_ERR(policy))
-		return PTR_ERR(policy);
+	if (!fscrypt_supported_policy(&policy, inode))
+		return -EINVAL;
 
-	return fscrypt_setup_extent_info(inode, policy, buf,
-					 info_ptr);
+	return fscrypt_setup_extent_info(inode, &policy,
+					 fscrypt_context_nonce(&ctx), info_ptr);
 }
 EXPORT_SYMBOL_GPL(fscrypt_load_extent_info);
 
diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c
index cfbe83aee847..314bb6e97cec 100644
--- a/fs/crypto/policy.c
+++ b/fs/crypto/policy.c
@@ -778,10 +778,9 @@  EXPORT_SYMBOL_GPL(fscrypt_set_context);
 int fscrypt_set_extent_context(struct fscrypt_extent_info *ci, void *ctx,
 			       size_t len)
 {
-	if (len < FSCRYPT_EXTENT_CONTEXT_MAX_SIZE)
+	if (len < FSCRYPT_SET_CONTEXT_MAX_SIZE)
 		return -EINVAL;
-	memcpy(ctx, ci->info.ci_nonce, FSCRYPT_FILE_NONCE_SIZE);
-	return FSCRYPT_FILE_NONCE_SIZE;
+	return fscrypt_new_context(ctx, &ci->info.ci_policy, ci->info.ci_nonce);
 }
 EXPORT_SYMBOL_GPL(fscrypt_set_extent_context);