Skip to content

Commit 1546d3f

Browse files
author
Eric Biggers
committed
fscrypt: use AES library for v1 key derivation
Convert the implementation of the v1 (original / deprecated) fscrypt per-file key derivation algorithm to use the AES library instead of an "ecb(aes)" crypto_skcipher. This is much simpler. While the AES library doesn't support AES-ECB directly yet, we can still simply call aes_encrypt() in a loop. While that doesn't explicitly parallelize the AES encryptions, it doesn't really matter in this case, where a new key is used each time and only 16 to 64 bytes are encrypted. In fact, a quick benchmark (AMD Ryzen 9 9950X) shows that this commit actually greatly improves performance, from ~7000 cycles per key derived to ~1500. The times don't differ much between 32 bytes and 64 bytes either, so clearly the bottleneck is API stuff and key expansion. Granted, performance of the v1 key derivation is no longer very relevant: most users have moved onto v2 encryption policies. The v2 key derivation uses HKDF-SHA512 (which is ~3500 cycles on the same CPU). Still, it's nice that the simpler solution is much faster as well. Compatibility verified with xfstests generic/548. Link: https://lore.kernel.org/r/20260321075338.99809-1-ebiggers@kernel.org Signed-off-by: Eric Biggers <ebiggers@kernel.org>
1 parent 4377a22 commit 1546d3f

2 files changed

Lines changed: 29 additions & 60 deletions

File tree

fs/crypto/Kconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ config FS_ENCRYPTION
33
bool "FS Encryption (Per-file encryption)"
44
select CRYPTO
55
select CRYPTO_SKCIPHER
6+
select CRYPTO_LIB_AES
67
select CRYPTO_LIB_SHA256
78
select CRYPTO_LIB_SHA512
89
select KEYS
@@ -30,7 +31,6 @@ config FS_ENCRYPTION_ALGS
3031
select CRYPTO_AES
3132
select CRYPTO_CBC
3233
select CRYPTO_CTS
33-
select CRYPTO_ECB
3434
select CRYPTO_XTS
3535

3636
config FS_ENCRYPTION_INLINE_CRYPT

fs/crypto/keysetup_v1.c

Lines changed: 28 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -20,60 +20,17 @@
2020
* managed alongside the master keys in the filesystem-level keyring)
2121
*/
2222

23-
#include <crypto/skcipher.h>
23+
#include <crypto/aes.h>
2424
#include <crypto/utils.h>
2525
#include <keys/user-type.h>
2626
#include <linux/hashtable.h>
27-
#include <linux/scatterlist.h>
2827

2928
#include "fscrypt_private.h"
3029

3130
/* Table of keys referenced by DIRECT_KEY policies */
3231
static DEFINE_HASHTABLE(fscrypt_direct_keys, 6); /* 6 bits = 64 buckets */
3332
static DEFINE_SPINLOCK(fscrypt_direct_keys_lock);
3433

35-
/*
36-
* v1 key derivation function. This generates the derived key by encrypting the
37-
* master key with AES-128-ECB using the nonce as the AES key. This provides a
38-
* unique derived key with sufficient entropy for each inode. However, it's
39-
* nonstandard, non-extensible, doesn't evenly distribute the entropy from the
40-
* master key, and is trivially reversible: an attacker who compromises a
41-
* derived key can "decrypt" it to get back to the master key, then derive any
42-
* other key. For all new code, use HKDF instead.
43-
*
44-
* The master key must be at least as long as the derived key. If the master
45-
* key is longer, then only the first 'derived_keysize' bytes are used.
46-
*/
47-
static int derive_key_aes(const u8 *master_key,
48-
const u8 nonce[FSCRYPT_FILE_NONCE_SIZE],
49-
u8 *derived_key, unsigned int derived_keysize)
50-
{
51-
struct crypto_sync_skcipher *tfm;
52-
int err;
53-
54-
tfm = crypto_alloc_sync_skcipher("ecb(aes)", 0, FSCRYPT_CRYPTOAPI_MASK);
55-
if (IS_ERR(tfm))
56-
return PTR_ERR(tfm);
57-
58-
err = crypto_sync_skcipher_setkey(tfm, nonce, FSCRYPT_FILE_NONCE_SIZE);
59-
if (err == 0) {
60-
SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm);
61-
struct scatterlist src_sg, dst_sg;
62-
63-
skcipher_request_set_callback(req,
64-
CRYPTO_TFM_REQ_MAY_BACKLOG |
65-
CRYPTO_TFM_REQ_MAY_SLEEP,
66-
NULL, NULL);
67-
sg_init_one(&src_sg, master_key, derived_keysize);
68-
sg_init_one(&dst_sg, derived_key, derived_keysize);
69-
skcipher_request_set_crypt(req, &src_sg, &dst_sg,
70-
derived_keysize, NULL);
71-
err = crypto_skcipher_encrypt(req);
72-
}
73-
crypto_free_sync_skcipher(tfm);
74-
return err;
75-
}
76-
7734
/*
7835
* Search the current task's subscribed keyrings for a "logon" key with
7936
* description prefix:descriptor, and if found acquire a read lock on it and
@@ -255,29 +212,41 @@ static int setup_v1_file_key_direct(struct fscrypt_inode_info *ci,
255212
return 0;
256213
}
257214

258-
/* v1 policy, !DIRECT_KEY: derive the file's encryption key */
215+
/*
216+
* v1 policy, !DIRECT_KEY: derive the file's encryption key.
217+
*
218+
* The v1 key derivation function generates the derived key by encrypting the
219+
* master key with AES-128-ECB using the file's nonce as the AES key. This
220+
* provides a unique derived key with sufficient entropy for each inode.
221+
* However, it's nonstandard, non-extensible, doesn't evenly distribute the
222+
* entropy from the master key, and is trivially reversible: an attacker who
223+
* compromises a derived key can "decrypt" it to get back to the master key,
224+
* then derive any other key. For all new code, use HKDF instead.
225+
*
226+
* The master key must be at least as long as the derived key. If the master
227+
* key is longer, then only the first ci->ci_mode->keysize bytes are used.
228+
*/
259229
static int setup_v1_file_key_derived(struct fscrypt_inode_info *ci,
260230
const u8 *raw_master_key)
261231
{
262-
u8 *derived_key;
232+
const unsigned int derived_keysize = ci->ci_mode->keysize;
233+
u8 derived_key[FSCRYPT_MAX_RAW_KEY_SIZE];
234+
struct aes_enckey aes;
263235
int err;
264236

265-
/*
266-
* This cannot be a stack buffer because it will be passed to the
267-
* scatterlist crypto API during derive_key_aes().
268-
*/
269-
derived_key = kmalloc(ci->ci_mode->keysize, GFP_KERNEL);
270-
if (!derived_key)
271-
return -ENOMEM;
237+
if (WARN_ON_ONCE(derived_keysize > FSCRYPT_MAX_RAW_KEY_SIZE ||
238+
derived_keysize % AES_BLOCK_SIZE != 0))
239+
return -EINVAL;
272240

273-
err = derive_key_aes(raw_master_key, ci->ci_nonce,
274-
derived_key, ci->ci_mode->keysize);
275-
if (err)
276-
goto out;
241+
static_assert(FSCRYPT_FILE_NONCE_SIZE == AES_KEYSIZE_128);
242+
aes_prepareenckey(&aes, ci->ci_nonce, FSCRYPT_FILE_NONCE_SIZE);
243+
for (unsigned int i = 0; i < derived_keysize; i += AES_BLOCK_SIZE)
244+
aes_encrypt(&aes, &derived_key[i], &raw_master_key[i]);
277245

278246
err = fscrypt_set_per_file_enc_key(ci, derived_key);
279-
out:
280-
kfree_sensitive(derived_key);
247+
248+
memzero_explicit(derived_key, derived_keysize);
249+
/* No need to zeroize 'aes', as its key is not secret. */
281250
return err;
282251
}
283252

0 commit comments

Comments
 (0)