Skip to content

Commit aa408f8

Browse files
drosen-googleebiggers
authored andcommitted
fscrypt: derive dirhash key for casefolded directories
When we allow indexed directories to use both encryption and casefolding, for the dirhash we can't just hash the ciphertext filenames that are stored on-disk (as is done currently) because the dirhash must be case insensitive, but the stored names are case-preserving. Nor can we hash the plaintext names with an unkeyed hash (or a hash keyed with a value stored on-disk like ext4's s_hash_seed), since that would leak information about the names that encryption is meant to protect. Instead, if we can accept a dirhash that's only computable when the fscrypt key is available, we can hash the plaintext names with a keyed hash using a secret key derived from the directory's fscrypt master key. We'll use SipHash-2-4 for this purpose. Prepare for this by deriving a SipHash key for each casefolded encrypted directory. Make sure to handle deriving the key not only when setting up the directory's fscrypt_info, but also in the case where the casefold flag is enabled after the fscrypt_info was already set up. (We could just always derive the key regardless of casefolding, but that would introduce unnecessary overhead for people not using casefolding.) Signed-off-by: Daniel Rosenberg <[email protected]> [EB: improved commit message, updated fscrypt.rst, squashed with change that avoids unnecessarily deriving the key, and many other cleanups] Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Eric Biggers <[email protected]>
1 parent 6e1918c commit aa408f8

File tree

6 files changed

+110
-14
lines changed

6 files changed

+110
-14
lines changed

Documentation/filesystems/fscrypt.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,16 @@ For master keys used for v2 encryption policies, a unique 16-byte "key
302302
identifier" is also derived using the KDF. This value is stored in
303303
the clear, since it is needed to reliably identify the key itself.
304304

305+
Dirhash keys
306+
------------
307+
308+
For directories that are indexed using a secret-keyed dirhash over the
309+
plaintext filenames, the KDF is also used to derive a 128-bit
310+
SipHash-2-4 key per directory in order to hash filenames. This works
311+
just like deriving a per-file encryption key, except that a different
312+
KDF context is used. Currently, only casefolded ("case-insensitive")
313+
encrypted directories use this style of hashing.
314+
305315
Encryption modes and usage
306316
==========================
307317

fs/crypto/fname.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,27 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
402402
}
403403
EXPORT_SYMBOL(fscrypt_setup_filename);
404404

405+
/**
406+
* fscrypt_fname_siphash() - calculate the SipHash of a filename
407+
* @dir: the parent directory
408+
* @name: the filename to calculate the SipHash of
409+
*
410+
* Given a plaintext filename @name and a directory @dir which uses SipHash as
411+
* its dirhash method and has had its fscrypt key set up, this function
412+
* calculates the SipHash of that name using the directory's secret dirhash key.
413+
*
414+
* Return: the SipHash of @name using the hash key of @dir
415+
*/
416+
u64 fscrypt_fname_siphash(const struct inode *dir, const struct qstr *name)
417+
{
418+
const struct fscrypt_info *ci = dir->i_crypt_info;
419+
420+
WARN_ON(!ci->ci_dirhash_key_initialized);
421+
422+
return siphash(name->name, name->len, &ci->ci_dirhash_key);
423+
}
424+
EXPORT_SYMBOL_GPL(fscrypt_fname_siphash);
425+
405426
/*
406427
* Validate dentries in encrypted directories to make sure we aren't potentially
407428
* caching stale dentries after a key has been added.

fs/crypto/fscrypt_private.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#define _FSCRYPT_PRIVATE_H
1313

1414
#include <linux/fscrypt.h>
15+
#include <linux/siphash.h>
1516
#include <crypto/hash.h>
1617

1718
#define CONST_STRLEN(str) (sizeof(str) - 1)
@@ -188,6 +189,14 @@ struct fscrypt_info {
188189
*/
189190
struct fscrypt_direct_key *ci_direct_key;
190191

192+
/*
193+
* This inode's hash key for filenames. This is a 128-bit SipHash-2-4
194+
* key. This is only set for directories that use a keyed dirhash over
195+
* the plaintext filenames -- currently just casefolded directories.
196+
*/
197+
siphash_key_t ci_dirhash_key;
198+
bool ci_dirhash_key_initialized;
199+
191200
/* The encryption policy used by this inode */
192201
union fscrypt_policy ci_policy;
193202

@@ -263,6 +272,7 @@ extern int fscrypt_init_hkdf(struct fscrypt_hkdf *hkdf, const u8 *master_key,
263272
#define HKDF_CONTEXT_PER_FILE_KEY 2
264273
#define HKDF_CONTEXT_DIRECT_KEY 3
265274
#define HKDF_CONTEXT_IV_INO_LBLK_64_KEY 4
275+
#define HKDF_CONTEXT_DIRHASH_KEY 5
266276

267277
extern int fscrypt_hkdf_expand(const struct fscrypt_hkdf *hkdf, u8 context,
268278
const u8 *info, unsigned int infolen,
@@ -434,6 +444,9 @@ fscrypt_allocate_skcipher(struct fscrypt_mode *mode, const u8 *raw_key,
434444
extern int fscrypt_set_derived_key(struct fscrypt_info *ci,
435445
const u8 *derived_key);
436446

447+
extern int fscrypt_derive_dirhash_key(struct fscrypt_info *ci,
448+
const struct fscrypt_master_key *mk);
449+
437450
/* keysetup_v1.c */
438451

439452
extern void fscrypt_put_direct_key(struct fscrypt_direct_key *dk);

fs/crypto/hooks.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
* Encryption hooks for higher-level filesystem operations.
66
*/
77

8+
#include <linux/key.h>
9+
810
#include "fscrypt_private.h"
911

1012
/**
@@ -137,15 +139,29 @@ int fscrypt_prepare_setflags(struct inode *inode,
137139
unsigned int oldflags, unsigned int flags)
138140
{
139141
struct fscrypt_info *ci;
142+
struct fscrypt_master_key *mk;
140143
int err;
141144

145+
/*
146+
* When the CASEFOLD flag is set on an encrypted directory, we must
147+
* derive the secret key needed for the dirhash. This is only possible
148+
* if the directory uses a v2 encryption policy.
149+
*/
142150
if (IS_ENCRYPTED(inode) && (flags & ~oldflags & FS_CASEFOLD_FL)) {
143151
err = fscrypt_require_key(inode);
144152
if (err)
145153
return err;
146154
ci = inode->i_crypt_info;
147155
if (ci->ci_policy.version != FSCRYPT_POLICY_V2)
148156
return -EINVAL;
157+
mk = ci->ci_master_key->payload.data[0];
158+
down_read(&mk->mk_secret_sem);
159+
if (is_master_key_secret_present(&mk->mk_secret))
160+
err = fscrypt_derive_dirhash_key(ci, mk);
161+
else
162+
err = -ENOKEY;
163+
up_read(&mk->mk_secret_sem);
164+
return err;
149165
}
150166
return 0;
151167
}

fs/crypto/keysetup.c

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -174,10 +174,24 @@ static int setup_per_mode_key(struct fscrypt_info *ci,
174174
return 0;
175175
}
176176

177+
int fscrypt_derive_dirhash_key(struct fscrypt_info *ci,
178+
const struct fscrypt_master_key *mk)
179+
{
180+
int err;
181+
182+
err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf, HKDF_CONTEXT_DIRHASH_KEY,
183+
ci->ci_nonce, FS_KEY_DERIVATION_NONCE_SIZE,
184+
(u8 *)&ci->ci_dirhash_key,
185+
sizeof(ci->ci_dirhash_key));
186+
if (err)
187+
return err;
188+
ci->ci_dirhash_key_initialized = true;
189+
return 0;
190+
}
191+
177192
static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci,
178193
struct fscrypt_master_key *mk)
179194
{
180-
u8 derived_key[FSCRYPT_MAX_KEY_SIZE];
181195
int err;
182196

183197
if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) {
@@ -189,8 +203,8 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci,
189203
* This ensures that the master key is consistently used only
190204
* for HKDF, avoiding key reuse issues.
191205
*/
192-
return setup_per_mode_key(ci, mk, mk->mk_direct_tfms,
193-
HKDF_CONTEXT_DIRECT_KEY, false);
206+
err = setup_per_mode_key(ci, mk, mk->mk_direct_tfms,
207+
HKDF_CONTEXT_DIRECT_KEY, false);
194208
} else if (ci->ci_policy.v2.flags &
195209
FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) {
196210
/*
@@ -199,21 +213,33 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci,
199213
* the IVs. This format is optimized for use with inline
200214
* encryption hardware compliant with the UFS or eMMC standards.
201215
*/
202-
return setup_per_mode_key(ci, mk, mk->mk_iv_ino_lblk_64_tfms,
203-
HKDF_CONTEXT_IV_INO_LBLK_64_KEY,
204-
true);
216+
err = setup_per_mode_key(ci, mk, mk->mk_iv_ino_lblk_64_tfms,
217+
HKDF_CONTEXT_IV_INO_LBLK_64_KEY, true);
218+
} else {
219+
u8 derived_key[FSCRYPT_MAX_KEY_SIZE];
220+
221+
err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf,
222+
HKDF_CONTEXT_PER_FILE_KEY,
223+
ci->ci_nonce,
224+
FS_KEY_DERIVATION_NONCE_SIZE,
225+
derived_key, ci->ci_mode->keysize);
226+
if (err)
227+
return err;
228+
229+
err = fscrypt_set_derived_key(ci, derived_key);
230+
memzero_explicit(derived_key, ci->ci_mode->keysize);
205231
}
206-
207-
err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf,
208-
HKDF_CONTEXT_PER_FILE_KEY,
209-
ci->ci_nonce, FS_KEY_DERIVATION_NONCE_SIZE,
210-
derived_key, ci->ci_mode->keysize);
211232
if (err)
212233
return err;
213234

214-
err = fscrypt_set_derived_key(ci, derived_key);
215-
memzero_explicit(derived_key, ci->ci_mode->keysize);
216-
return err;
235+
/* Derive a secret dirhash key for directories that need it. */
236+
if (S_ISDIR(ci->ci_inode->i_mode) && IS_CASEFOLDED(ci->ci_inode)) {
237+
err = fscrypt_derive_dirhash_key(ci, mk);
238+
if (err)
239+
return err;
240+
}
241+
242+
return 0;
217243
}
218244

219245
/*

include/linux/fscrypt.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,9 @@ static inline bool fscrypt_match_name(const struct fscrypt_name *fname,
247247
return !memcmp(de_name, fname->disk_name.name, fname->disk_name.len);
248248
}
249249

250+
extern u64 fscrypt_fname_siphash(const struct inode *dir,
251+
const struct qstr *name);
252+
250253
/* bio.c */
251254
extern void fscrypt_decrypt_bio(struct bio *);
252255
extern int fscrypt_zeroout_range(const struct inode *, pgoff_t, sector_t,
@@ -479,6 +482,13 @@ static inline bool fscrypt_match_name(const struct fscrypt_name *fname,
479482
return !memcmp(de_name, fname->disk_name.name, fname->disk_name.len);
480483
}
481484

485+
static inline u64 fscrypt_fname_siphash(const struct inode *dir,
486+
const struct qstr *name)
487+
{
488+
WARN_ON_ONCE(1);
489+
return 0;
490+
}
491+
482492
/* bio.c */
483493
static inline void fscrypt_decrypt_bio(struct bio *bio)
484494
{

0 commit comments

Comments
 (0)