Skip to content

Use encrypted keys in fscrypt #45

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions Documentation/security/keys-fscrypt.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
Encrypted keys for the fscrypt layer

The fscrypt has the ability to encrpyt the content of several filesystems.
Therefore a symmetric key is necessary. There are two types of keys which
can be used for this purpose:
- logon keys with fscrypt as prefix
- encrypted keys from the type fscrypt

In the following only the encrypted key type is described:

Usage:
keyctl add encrypted name "new fscrypt key-type:master-key-name keylen" ring
keyctl add encrypted name "load hex_blob" ring
keyctl update keyid "update key-type:master-key-name"

name:= 'fscrypt:<16 hexadecimal characters>'
key-type:= 'trusted' | 'user' | 'caam'
keylen:= 64

Example of encrypted key usage with the ext4 filesystem which is using the
fscrypt layer:

It is assumed that the ext4 filesystem is prepared to be encrypted.

Create an encrypted key "fscrypt:0102030405060708" of length 64 bytes with format
'fscrypt' and save it using a previously loaded caam key "masterkey":

$ keyctl add encrypted fscrypt:0102030405060708 "new fscrypt caam:masterkey 64" @u
101826206

$ keyctl print 101826206
fscrypt caam:masterkey 64 fd13b42f96f22f130fb19ebf9e13df8d00749a69055b855e7aa
43dfeac7ea422ee23d4abef1fcdc0fa191a1aa6e13e5e76148a570d9cb69b307150bb14c36
c2dd1829d207ae22a4cdd5bcb91eecbcde3d64c9b60a7e68b8cc8dc042e398dd33a16e94ea
dc32041c63dd79b9a7fc446c12ca

$ keyctl pipe 101826206 > ext4.blob

Use the created encrypted key to encrypt the '/data/secret' directory which is
located on the ext4 filesystem:

$ e4crypt set_policy 0102030405060708 /data/secret
Key with descriptor [0102030405060708] applied to /data/secret.
106 changes: 69 additions & 37 deletions fs/crypto/keyinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/

#include <keys/user-type.h>
#include <keys/encrypted-type.h>
#include <linux/scatterlist.h>
#include <linux/fscrypto.h>

Expand All @@ -32,8 +33,8 @@ static void derive_crypt_complete(struct crypto_async_request *req, int rc)
* Return: Zero on success; non-zero otherwise.
*/
static int derive_key_aes(u8 deriving_key[FS_AES_128_ECB_KEY_SIZE],
u8 source_key[FS_AES_256_XTS_KEY_SIZE],
u8 derived_key[FS_AES_256_XTS_KEY_SIZE])
u8 source_key[FS_AES_256_XTS_KEY_SIZE],
u8 derived_key[FS_AES_256_XTS_KEY_SIZE])
{
int res = 0;
struct skcipher_request *req = NULL;
Expand All @@ -53,17 +54,18 @@ static int derive_key_aes(u8 deriving_key[FS_AES_128_ECB_KEY_SIZE],
goto out;
}
skcipher_request_set_callback(req,
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
derive_crypt_complete, &ecr);
res = crypto_skcipher_setkey(tfm, deriving_key,
FS_AES_128_ECB_KEY_SIZE);
CRYPTO_TFM_REQ_MAY_BACKLOG |
CRYPTO_TFM_REQ_MAY_SLEEP,
derive_crypt_complete, &ecr);
res =
crypto_skcipher_setkey(tfm, deriving_key, FS_AES_128_ECB_KEY_SIZE);
if (res < 0)
goto out;

sg_init_one(&src_sg, source_key, FS_AES_256_XTS_KEY_SIZE);
sg_init_one(&dst_sg, derived_key, FS_AES_256_XTS_KEY_SIZE);
skcipher_request_set_crypt(req, &src_sg, &dst_sg,
FS_AES_256_XTS_KEY_SIZE, NULL);
FS_AES_256_XTS_KEY_SIZE, NULL);
res = crypto_skcipher_encrypt(req);
if (res == -EINPROGRESS || res == -EBUSY) {
wait_for_completion(&ecr.completion);
Expand All @@ -76,13 +78,14 @@ static int derive_key_aes(u8 deriving_key[FS_AES_128_ECB_KEY_SIZE],
}

static int validate_user_key(struct fscrypt_info *crypt_info,
struct fscrypt_context *ctx, u8 *raw_key,
u8 *prefix, int prefix_size)
struct fscrypt_context *ctx, u8 * raw_key,
u8 * prefix, int prefix_size)
{
u8 *full_key_descriptor;
struct key *keyring_key;
struct fscrypt_key *master_key;
const struct user_key_payload *ukp;
const struct encrypted_key_payload *ekp;
int full_key_len = prefix_size + (FS_KEY_DESCRIPTOR_SIZE * 2) + 1;
int res;

Expand All @@ -92,38 +95,65 @@ static int validate_user_key(struct fscrypt_info *crypt_info,

memcpy(full_key_descriptor, prefix, prefix_size);
sprintf(full_key_descriptor + prefix_size,
"%*phN", FS_KEY_DESCRIPTOR_SIZE,
ctx->master_key_descriptor);
"%*phN", FS_KEY_DESCRIPTOR_SIZE, ctx->master_key_descriptor);
full_key_descriptor[full_key_len - 1] = '\0';
keyring_key = request_key(&key_type_logon, full_key_descriptor, NULL);
kfree(full_key_descriptor);
if (IS_ERR(keyring_key))
return PTR_ERR(keyring_key);
down_read(&keyring_key->sem);
if (IS_ERR(keyring_key)) {
keyring_key =
request_key(&key_type_encrypted, full_key_descriptor, NULL);
kfree(full_key_descriptor);
if (IS_ERR(keyring_key)) {
return PTR_ERR(keyring_key);
}
down_read(&keyring_key->sem);

if (keyring_key->type != &key_type_logon) {
printk_once(KERN_WARNING
"%s: key type must be logon\n", __func__);
res = -ENOKEY;
goto out;
}
ukp = user_key_payload(keyring_key);
if (!ukp) {
/* key was revoked before we acquired its semaphore */
res = -EKEYREVOKED;
goto out;
}
if (ukp->datalen != sizeof(struct fscrypt_key)) {
res = -EINVAL;
goto out;
if (keyring_key->type != &key_type_encrypted) {
printk_once(KERN_WARNING
"%s: key type must be encrypted\n",
__func__);
res = -ENOKEY;
goto out;
}
ekp = encrypted_key_payload(keyring_key);
if (!ekp) {
/* key was revoked before we acquired its semaphore */
res = -EKEYREVOKED;
goto out;
}
if (ekp->payload_datalen != sizeof(struct fscrypt_key)) {
res = -EINVAL;
goto out;
}
master_key = (struct fscrypt_key *)ekp->payload_data;
} else {
kfree(full_key_descriptor);
down_read(&keyring_key->sem);

if (keyring_key->type != &key_type_logon) {
printk_once(KERN_WARNING
"%s: key type must be logon\n", __func__);
res = -ENOKEY;
goto out;
}
ukp = user_key_payload(keyring_key);
if (!ukp) {
/* key was revoked before we acquired its semaphore */
res = -EKEYREVOKED;
goto out;
}
if (ukp->datalen != sizeof(struct fscrypt_key)) {
res = -EINVAL;
goto out;
}
master_key = (struct fscrypt_key *)ukp->data;
}
master_key = (struct fscrypt_key *)ukp->data;

BUILD_BUG_ON(FS_AES_128_ECB_KEY_SIZE != FS_KEY_DERIVATION_NONCE_SIZE);

if (master_key->size != FS_AES_256_XTS_KEY_SIZE) {
printk_once(KERN_WARNING
"%s: key size incorrect: %d\n",
__func__, master_key->size);
"%s: key size incorrect: %d\n",
__func__, master_key->size);
res = -ENOKEY;
goto out;
}
Expand Down Expand Up @@ -222,7 +252,7 @@ int fscrypt_get_encryption_info(struct inode *inode)
crypt_info->ci_filename_mode = ctx.filenames_encryption_mode;
crypt_info->ci_ctfm = NULL;
memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor,
sizeof(crypt_info->ci_master_key));
sizeof(crypt_info->ci_master_key));

res = determine_cipher_type(crypt_info, inode, &cipher_str, &keysize);
if (res)
Expand All @@ -243,14 +273,14 @@ int fscrypt_get_encryption_info(struct inode *inode)
}

res = validate_user_key(crypt_info, &ctx, raw_key,
FS_KEY_DESC_PREFIX, FS_KEY_DESC_PREFIX_SIZE);
FS_KEY_DESC_PREFIX, FS_KEY_DESC_PREFIX_SIZE);
if (res && inode->i_sb->s_cop->key_prefix) {
u8 *prefix = NULL;
int prefix_size, res2;

prefix_size = inode->i_sb->s_cop->key_prefix(inode, &prefix);
res2 = validate_user_key(crypt_info, &ctx, raw_key,
prefix, prefix_size);
prefix, prefix_size);
if (res2) {
if (res2 == -ENOKEY)
res = -ENOKEY;
Expand All @@ -265,7 +295,7 @@ int fscrypt_get_encryption_info(struct inode *inode)
res = ctfm ? PTR_ERR(ctfm) : -ENOMEM;
printk(KERN_DEBUG
"%s: error %d (inode %u) allocating crypto tfm\n",
__func__, res, (unsigned) inode->i_ino);
__func__, res, (unsigned)inode->i_ino);
goto out;
}
crypt_info->ci_ctfm = ctfm;
Expand All @@ -284,6 +314,7 @@ int fscrypt_get_encryption_info(struct inode *inode)
kzfree(raw_key);
return res;
}

EXPORT_SYMBOL(fscrypt_get_encryption_info);

void fscrypt_put_encryption_info(struct inode *inode, struct fscrypt_info *ci)
Expand All @@ -301,4 +332,5 @@ void fscrypt_put_encryption_info(struct inode *inode, struct fscrypt_info *ci)

put_crypt_info(ci);
}

EXPORT_SYMBOL(fscrypt_put_encryption_info);
9 changes: 7 additions & 2 deletions include/keys/encrypted-type.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,17 @@ struct encrypted_key_payload {
u8 *encrypted_data; /* datablob: encrypted data */
unsigned short datablob_len; /* length of datablob */
unsigned short decrypted_datalen; /* decrypted data length */
unsigned short payload_datalen; /* payload data length */
unsigned short payload_datalen; /* payload data length */
unsigned short encrypted_key_format; /* encrypted key format */
u8 *decrypted_data; /* decrypted data */
u8 payload_data[0]; /* payload data + datablob + hmac */
};

extern struct key_type key_type_encrypted;

#endif /* _KEYS_ENCRYPTED_TYPE_H */
static inline const struct encrypted_key_payload *encrypted_key_payload(const struct key *key)
{
return (struct encrypted_key_payload *)rcu_dereference_key(key);
}

#endif /* _KEYS_ENCRYPTED_TYPE_H */
1 change: 1 addition & 0 deletions include/linux/fscrypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ struct fscrypt_context {

#define FS_KEY_DESC_PREFIX "fscrypt:"
#define FS_KEY_DESC_PREFIX_SIZE 8
#define FS_KEY_DESC_STR_LEN (FS_KEY_DESC_PREFIX_SIZE + (2 * FS_KEY_DESCRIPTOR_SIZE))

/* This is passed in from userspace into the kernel keyring */
struct fscrypt_key {
Expand Down
2 changes: 1 addition & 1 deletion security/keys/encrypted-keys/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys.o

encrypted-keys-y := encrypted.o ecryptfs_format.o
encrypted-keys-y := encrypted.o ecryptfs_format.o fscrypt_format.o
masterkey-$(CONFIG_TRUSTED_KEYS) := masterkey_trusted.o
masterkey-$(CONFIG_TRUSTED_KEYS)-$(CONFIG_ENCRYPTED_KEYS) := masterkey_trusted.o
encrypted-keys-y += $(masterkey-y) $(masterkey-m-m)
Loading