Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

+29 -60
+1 -1
fs/crypto/Kconfig
··· 3 3 bool "FS Encryption (Per-file encryption)" 4 4 select CRYPTO 5 5 select CRYPTO_SKCIPHER 6 + select CRYPTO_LIB_AES 6 7 select CRYPTO_LIB_SHA256 7 8 select CRYPTO_LIB_SHA512 8 9 select KEYS ··· 31 30 select CRYPTO_AES 32 31 select CRYPTO_CBC 33 32 select CRYPTO_CTS 34 - select CRYPTO_ECB 35 33 select CRYPTO_XTS 36 34 37 35 config FS_ENCRYPTION_INLINE_CRYPT
+28 -59
fs/crypto/keysetup_v1.c
··· 20 20 * managed alongside the master keys in the filesystem-level keyring) 21 21 */ 22 22 23 - #include <crypto/skcipher.h> 23 + #include <crypto/aes.h> 24 24 #include <crypto/utils.h> 25 25 #include <keys/user-type.h> 26 26 #include <linux/hashtable.h> 27 - #include <linux/scatterlist.h> 28 27 29 28 #include "fscrypt_private.h" 30 29 31 30 /* Table of keys referenced by DIRECT_KEY policies */ 32 31 static DEFINE_HASHTABLE(fscrypt_direct_keys, 6); /* 6 bits = 64 buckets */ 33 32 static DEFINE_SPINLOCK(fscrypt_direct_keys_lock); 34 - 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 33 77 34 /* 78 35 * Search the current task's subscribed keyrings for a "logon" key with ··· 212 255 return 0; 213 256 } 214 257 215 - /* v1 policy, !DIRECT_KEY: derive the file's encryption key */ 258 + /* 259 + * v1 policy, !DIRECT_KEY: derive the file's encryption key. 260 + * 261 + * The v1 key derivation function generates the derived key by encrypting the 262 + * master key with AES-128-ECB using the file's nonce as the AES key. This 263 + * provides a unique derived key with sufficient entropy for each inode. 264 + * However, it's nonstandard, non-extensible, doesn't evenly distribute the 265 + * entropy from the master key, and is trivially reversible: an attacker who 266 + * compromises a derived key can "decrypt" it to get back to the master key, 267 + * then derive any other key. For all new code, use HKDF instead. 268 + * 269 + * The master key must be at least as long as the derived key. If the master 270 + * key is longer, then only the first ci->ci_mode->keysize bytes are used. 271 + */ 216 272 static int setup_v1_file_key_derived(struct fscrypt_inode_info *ci, 217 273 const u8 *raw_master_key) 218 274 { 219 - u8 *derived_key; 275 + const unsigned int derived_keysize = ci->ci_mode->keysize; 276 + u8 derived_key[FSCRYPT_MAX_RAW_KEY_SIZE]; 277 + struct aes_enckey aes; 220 278 int err; 221 279 222 - /* 223 - * This cannot be a stack buffer because it will be passed to the 224 - * scatterlist crypto API during derive_key_aes(). 225 - */ 226 - derived_key = kmalloc(ci->ci_mode->keysize, GFP_KERNEL); 227 - if (!derived_key) 228 - return -ENOMEM; 280 + if (WARN_ON_ONCE(derived_keysize > FSCRYPT_MAX_RAW_KEY_SIZE || 281 + derived_keysize % AES_BLOCK_SIZE != 0)) 282 + return -EINVAL; 229 283 230 - err = derive_key_aes(raw_master_key, ci->ci_nonce, 231 - derived_key, ci->ci_mode->keysize); 232 - if (err) 233 - goto out; 284 + static_assert(FSCRYPT_FILE_NONCE_SIZE == AES_KEYSIZE_128); 285 + aes_prepareenckey(&aes, ci->ci_nonce, FSCRYPT_FILE_NONCE_SIZE); 286 + for (unsigned int i = 0; i < derived_keysize; i += AES_BLOCK_SIZE) 287 + aes_encrypt(&aes, &derived_key[i], &raw_master_key[i]); 234 288 235 289 err = fscrypt_set_per_file_enc_key(ci, derived_key); 236 - out: 237 - kfree_sensitive(derived_key); 290 + 291 + memzero_explicit(derived_key, derived_keysize); 292 + /* No need to zeroize 'aes', as its key is not secret. */ 238 293 return err; 239 294 } 240 295