Skip to content

Commit e98ad46

Browse files
committed
fscrypt: add FS_IOC_GET_ENCRYPTION_NONCE ioctl
Add an ioctl FS_IOC_GET_ENCRYPTION_NONCE which retrieves the nonce from an encrypted file or directory. The nonce is the 16-byte random value stored in the inode's encryption xattr. It is normally used together with the master key to derive the inode's actual encryption key. The nonces are needed by automated tests that verify the correctness of the ciphertext on-disk. Except for the IV_INO_LBLK_64 case, there's no way to replicate a file's ciphertext without knowing that file's nonce. The nonces aren't secret, and the existing ciphertext verification tests in xfstests retrieve them from disk using debugfs or dump.f2fs. But in environments that lack these debugging tools, getting the nonces by manually parsing the filesystem structure would be very hard. To make this important type of testing much easier, let's just add an ioctl that retrieves the nonce. Link: https://lore.kernel.org/r/[email protected] Reviewed-by: Theodore Ts'o <[email protected]> Signed-off-by: Eric Biggers <[email protected]>
1 parent 98d54f8 commit e98ad46

File tree

6 files changed

+60
-15
lines changed

6 files changed

+60
-15
lines changed

Documentation/filesystems/fscrypt.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,17 @@ from a passphrase or other low-entropy user credential.
633633
FS_IOC_GET_ENCRYPTION_PWSALT is deprecated. Instead, prefer to
634634
generate and manage any needed salt(s) in userspace.
635635

636+
Getting a file's encryption nonce
637+
---------------------------------
638+
639+
Since Linux v5.7, the ioctl FS_IOC_GET_ENCRYPTION_NONCE is supported.
640+
On encrypted files and directories it gets the inode's 16-byte nonce.
641+
On unencrypted files and directories, it fails with ENODATA.
642+
643+
This ioctl can be useful for automated tests which verify that the
644+
encryption is being done correctly. It is not needed for normal use
645+
of fscrypt.
646+
636647
Adding keys
637648
-----------
638649

fs/crypto/fscrypt_private.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,26 @@ static inline int fscrypt_context_size(const union fscrypt_context *ctx)
7676
return 0;
7777
}
7878

79+
/* Check whether an fscrypt_context has a recognized version number and size */
80+
static inline bool fscrypt_context_is_valid(const union fscrypt_context *ctx,
81+
int ctx_size)
82+
{
83+
return ctx_size >= 1 && ctx_size == fscrypt_context_size(ctx);
84+
}
85+
86+
/* Retrieve the context's nonce, assuming the context was already validated */
87+
static inline const u8 *fscrypt_context_nonce(const union fscrypt_context *ctx)
88+
{
89+
switch (ctx->version) {
90+
case FSCRYPT_CONTEXT_V1:
91+
return ctx->v1.nonce;
92+
case FSCRYPT_CONTEXT_V2:
93+
return ctx->v2.nonce;
94+
}
95+
WARN_ON(1);
96+
return NULL;
97+
}
98+
7999
#undef fscrypt_policy
80100
union fscrypt_policy {
81101
u8 version;

fs/crypto/keysetup.c

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -425,20 +425,8 @@ int fscrypt_get_encryption_info(struct inode *inode)
425425
goto out;
426426
}
427427

428-
switch (ctx.version) {
429-
case FSCRYPT_CONTEXT_V1:
430-
memcpy(crypt_info->ci_nonce, ctx.v1.nonce,
431-
FS_KEY_DERIVATION_NONCE_SIZE);
432-
break;
433-
case FSCRYPT_CONTEXT_V2:
434-
memcpy(crypt_info->ci_nonce, ctx.v2.nonce,
435-
FS_KEY_DERIVATION_NONCE_SIZE);
436-
break;
437-
default:
438-
WARN_ON(1);
439-
res = -EINVAL;
440-
goto out;
441-
}
428+
memcpy(crypt_info->ci_nonce, fscrypt_context_nonce(&ctx),
429+
FS_KEY_DERIVATION_NONCE_SIZE);
442430

443431
if (!fscrypt_supported_policy(&crypt_info->ci_policy, inode)) {
444432
res = -EINVAL;

fs/crypto/policy.c

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ int fscrypt_policy_from_context(union fscrypt_policy *policy_u,
258258
{
259259
memset(policy_u, 0, sizeof(*policy_u));
260260

261-
if (ctx_size <= 0 || ctx_size != fscrypt_context_size(ctx_u))
261+
if (!fscrypt_context_is_valid(ctx_u, ctx_size))
262262
return -EINVAL;
263263

264264
switch (ctx_u->version) {
@@ -481,6 +481,25 @@ int fscrypt_ioctl_get_policy_ex(struct file *filp, void __user *uarg)
481481
}
482482
EXPORT_SYMBOL_GPL(fscrypt_ioctl_get_policy_ex);
483483

484+
/* FS_IOC_GET_ENCRYPTION_NONCE: retrieve file's encryption nonce for testing */
485+
int fscrypt_ioctl_get_nonce(struct file *filp, void __user *arg)
486+
{
487+
struct inode *inode = file_inode(filp);
488+
union fscrypt_context ctx;
489+
int ret;
490+
491+
ret = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
492+
if (ret < 0)
493+
return ret;
494+
if (!fscrypt_context_is_valid(&ctx, ret))
495+
return -EINVAL;
496+
if (copy_to_user(arg, fscrypt_context_nonce(&ctx),
497+
FS_KEY_DERIVATION_NONCE_SIZE))
498+
return -EFAULT;
499+
return 0;
500+
}
501+
EXPORT_SYMBOL_GPL(fscrypt_ioctl_get_nonce);
502+
484503
/**
485504
* fscrypt_has_permitted_context() - is a file's encryption policy permitted
486505
* within its directory?

include/linux/fscrypt.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ extern void fscrypt_free_bounce_page(struct page *bounce_page);
139139
extern int fscrypt_ioctl_set_policy(struct file *, const void __user *);
140140
extern int fscrypt_ioctl_get_policy(struct file *, void __user *);
141141
extern int fscrypt_ioctl_get_policy_ex(struct file *, void __user *);
142+
extern int fscrypt_ioctl_get_nonce(struct file *filp, void __user *arg);
142143
extern int fscrypt_has_permitted_context(struct inode *, struct inode *);
143144
extern int fscrypt_inherit_context(struct inode *, struct inode *,
144145
void *, bool);
@@ -300,6 +301,11 @@ static inline int fscrypt_ioctl_get_policy_ex(struct file *filp,
300301
return -EOPNOTSUPP;
301302
}
302303

304+
static inline int fscrypt_ioctl_get_nonce(struct file *filp, void __user *arg)
305+
{
306+
return -EOPNOTSUPP;
307+
}
308+
303309
static inline int fscrypt_has_permitted_context(struct inode *parent,
304310
struct inode *child)
305311
{

include/uapi/linux/fscrypt.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ struct fscrypt_get_key_status_arg {
163163
#define FS_IOC_REMOVE_ENCRYPTION_KEY _IOWR('f', 24, struct fscrypt_remove_key_arg)
164164
#define FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS _IOWR('f', 25, struct fscrypt_remove_key_arg)
165165
#define FS_IOC_GET_ENCRYPTION_KEY_STATUS _IOWR('f', 26, struct fscrypt_get_key_status_arg)
166+
#define FS_IOC_GET_ENCRYPTION_NONCE _IOR('f', 27, __u8[16])
166167

167168
/**********************************************************************/
168169

0 commit comments

Comments
 (0)