Commit 646b7d4f authored by Eric Biggers's avatar Eric Biggers Committed by Theodore Ts'o

fscrypt: only derive the needed portion of the key

Currently the key derivation function in fscrypt uses the master key
length as the amount of output key material to derive.  This works, but
it means we can waste time deriving more key material than is actually
used, e.g. most commonly, deriving 64 bytes for directories which only
take a 32-byte AES-256-CTS-CBC key.  It also forces us to validate that
the master key length is a multiple of AES_BLOCK_SIZE, which wouldn't
otherwise be necessary.

Fix it to only derive the needed length key.
Signed-off-by: default avatarEric Biggers <ebiggers@google.com>
Signed-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
parent 590f497d
...@@ -19,17 +19,16 @@ ...@@ -19,17 +19,16 @@
static struct crypto_shash *essiv_hash_tfm; static struct crypto_shash *essiv_hash_tfm;
/** /*
* derive_key_aes() - Derive a key using AES-128-ECB * Key derivation function. This generates the derived key by encrypting the
* @deriving_key: Encryption key used for derivation. * master key with AES-128-ECB using the inode's nonce as the AES key.
* @source_key: Source key to which to apply derivation.
* @derived_raw_key: Derived raw key.
* *
* Return: Zero on success; non-zero otherwise. * The master key must be at least as long as the derived key. If the master
* key is longer, then only the first 'derived_keysize' bytes are used.
*/ */
static int derive_key_aes(const u8 deriving_key[FS_KEY_DERIVATION_NONCE_SIZE], static int derive_key_aes(const u8 *master_key,
const struct fscrypt_key *source_key, const struct fscrypt_context *ctx,
u8 derived_raw_key[FS_MAX_KEY_SIZE]) u8 *derived_key, unsigned int derived_keysize)
{ {
int res = 0; int res = 0;
struct skcipher_request *req = NULL; struct skcipher_request *req = NULL;
...@@ -51,14 +50,13 @@ static int derive_key_aes(const u8 deriving_key[FS_KEY_DERIVATION_NONCE_SIZE], ...@@ -51,14 +50,13 @@ static int derive_key_aes(const u8 deriving_key[FS_KEY_DERIVATION_NONCE_SIZE],
skcipher_request_set_callback(req, skcipher_request_set_callback(req,
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
crypto_req_done, &wait); crypto_req_done, &wait);
res = crypto_skcipher_setkey(tfm, deriving_key, res = crypto_skcipher_setkey(tfm, ctx->nonce, sizeof(ctx->nonce));
FS_KEY_DERIVATION_NONCE_SIZE);
if (res < 0) if (res < 0)
goto out; goto out;
sg_init_one(&src_sg, source_key->raw, source_key->size); sg_init_one(&src_sg, master_key, derived_keysize);
sg_init_one(&dst_sg, derived_raw_key, source_key->size); sg_init_one(&dst_sg, derived_key, derived_keysize);
skcipher_request_set_crypt(req, &src_sg, &dst_sg, source_key->size, skcipher_request_set_crypt(req, &src_sg, &dst_sg, derived_keysize,
NULL); NULL);
res = crypto_wait_req(crypto_skcipher_encrypt(req), &wait); res = crypto_wait_req(crypto_skcipher_encrypt(req), &wait);
out: out:
...@@ -109,10 +107,9 @@ find_and_lock_process_key(const char *prefix, ...@@ -109,10 +107,9 @@ find_and_lock_process_key(const char *prefix,
goto invalid; goto invalid;
} }
if (payload->size < min_keysize || if (payload->size < min_keysize) {
payload->size % AES_BLOCK_SIZE != 0) {
fscrypt_warn(NULL, fscrypt_warn(NULL,
"key with description '%s' is too short or is misaligned (got %u bytes, need %u+ bytes)", "key with description '%s' is too short (got %u bytes, need %u+ bytes)",
key->description, payload->size, min_keysize); key->description, payload->size, min_keysize);
goto invalid; goto invalid;
} }
...@@ -145,7 +142,7 @@ static int find_and_derive_key(const struct inode *inode, ...@@ -145,7 +142,7 @@ static int find_and_derive_key(const struct inode *inode,
} }
if (IS_ERR(key)) if (IS_ERR(key))
return PTR_ERR(key); return PTR_ERR(key);
err = derive_key_aes(ctx->nonce, payload, derived_key); err = derive_key_aes(payload->raw, ctx, derived_key, derived_keysize);
up_read(&key->sem); up_read(&key->sem);
key_put(key); key_put(key);
return err; return err;
...@@ -325,7 +322,7 @@ int fscrypt_get_encryption_info(struct inode *inode) ...@@ -325,7 +322,7 @@ int fscrypt_get_encryption_info(struct inode *inode)
* crypto API as part of key derivation. * crypto API as part of key derivation.
*/ */
res = -ENOMEM; res = -ENOMEM;
raw_key = kmalloc(FS_MAX_KEY_SIZE, GFP_NOFS); raw_key = kmalloc(keysize, GFP_NOFS);
if (!raw_key) if (!raw_key)
goto out; goto out;
...@@ -343,10 +340,6 @@ int fscrypt_get_encryption_info(struct inode *inode) ...@@ -343,10 +340,6 @@ int fscrypt_get_encryption_info(struct inode *inode)
} }
crypt_info->ci_ctfm = ctfm; crypt_info->ci_ctfm = ctfm;
crypto_skcipher_set_flags(ctfm, CRYPTO_TFM_REQ_WEAK_KEY); crypto_skcipher_set_flags(ctfm, CRYPTO_TFM_REQ_WEAK_KEY);
/*
* if the provided key is longer than keysize, we use the first
* keysize bytes of the derived key only
*/
res = crypto_skcipher_setkey(ctfm, raw_key, keysize); res = crypto_skcipher_setkey(ctfm, raw_key, keysize);
if (res) if (res)
goto out; goto out;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment