All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/2] encrypted-keys: remove trusted-keys dependency
@ 2011-08-29 13:08 Mimi Zohar
  2011-08-29 13:08 ` [PATCH 2/2] evm: remove TCG_TPM dependency Mimi Zohar
  0 siblings, 1 reply; 2+ messages in thread
From: Mimi Zohar @ 2011-08-29 13:08 UTC (permalink / raw)
  To: linux-security-module
  Cc: Mimi Zohar, James Morris, LKML, Randy Dunlap, Arnaud Lacombe,
	Stephen Rothwell, David Safford, Mimi Zohar

Encrypted keys are decrypted/encrypted using either a trusted-key or,
for those systems without a TPM, a user-defined key.  This patch
removes the trusted-keys and TCG_TPM dependencies.

Signed-off-by: Mimi Zohar <zohar@us.ibm.com>
---
 security/Kconfig                                 |    4 +-
 security/keys/Makefile                           |    2 +-
 security/keys/encrypted-keys/Makefile            |    7 +
 security/keys/encrypted-keys/ecryptfs_format.c   |   81 ++
 security/keys/encrypted-keys/ecryptfs_format.h   |   30 +
 security/keys/encrypted-keys/encrypted.c         | 1030 +++++++++++++++++++++
 security/keys/encrypted-keys/encrypted.h         |   65 ++
 security/keys/encrypted-keys/masterkey_trusted.c |   44 +
 security/keys/encrypted.c                        | 1049 ----------------------
 security/keys/encrypted.h                        |   54 --
 10 files changed, 1261 insertions(+), 1105 deletions(-)
 create mode 100644 security/keys/encrypted-keys/Makefile
 create mode 100644 security/keys/encrypted-keys/ecryptfs_format.c
 create mode 100644 security/keys/encrypted-keys/ecryptfs_format.h
 create mode 100644 security/keys/encrypted-keys/encrypted.c
 create mode 100644 security/keys/encrypted-keys/encrypted.h
 create mode 100644 security/keys/encrypted-keys/masterkey_trusted.c
 delete mode 100644 security/keys/encrypted.c
 delete mode 100644 security/keys/encrypted.h

diff --git a/security/Kconfig b/security/Kconfig
index 22847a8..51bd5a0 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -38,7 +38,9 @@ config TRUSTED_KEYS
 
 config ENCRYPTED_KEYS
 	tristate "ENCRYPTED KEYS"
-	depends on KEYS && TRUSTED_KEYS
+	depends on KEYS
+	select CRYPTO
+	select CRYPTO_HMAC
 	select CRYPTO_AES
 	select CRYPTO_CBC
 	select CRYPTO_SHA256
diff --git a/security/keys/Makefile b/security/keys/Makefile
index b34cc6e..a56f1ff 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -14,7 +14,7 @@ obj-y := \
 	user_defined.o
 
 obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
-obj-$(CONFIG_ENCRYPTED_KEYS) += ecryptfs_format.o encrypted.o
+obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/
 obj-$(CONFIG_KEYS_COMPAT) += compat.o
 obj-$(CONFIG_PROC_FS) += proc.o
 obj-$(CONFIG_SYSCTL) += sysctl.o
diff --git a/security/keys/encrypted-keys/Makefile b/security/keys/encrypted-keys/Makefile
new file mode 100644
index 0000000..e5d5b7d
--- /dev/null
+++ b/security/keys/encrypted-keys/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for encrypted keys
+#
+
+obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted.o ecryptfs_format.o
+obj-$(CONFIG_TRUSTED_KEYS) += masterkey_trusted.o
+
diff --git a/security/keys/encrypted-keys/ecryptfs_format.c b/security/keys/encrypted-keys/ecryptfs_format.c
new file mode 100644
index 0000000..6daa3b6
--- /dev/null
+++ b/security/keys/encrypted-keys/ecryptfs_format.c
@@ -0,0 +1,81 @@
+/*
+ * ecryptfs_format.c: helper functions for the encrypted key type
+ *
+ * Copyright (C) 2006 International Business Machines Corp.
+ * Copyright (C) 2010 Politecnico di Torino, Italy
+ *                    TORSEC group -- http://security.polito.it
+ *
+ * Authors:
+ * Michael A. Halcrow <mahalcro@us.ibm.com>
+ * Tyler Hicks <tyhicks@ou.edu>
+ * Roberto Sassu <roberto.sassu@polito.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ */
+
+#include <linux/module.h>
+#include "ecryptfs_format.h"
+
+u8 *ecryptfs_get_auth_tok_key(struct ecryptfs_auth_tok *auth_tok)
+{
+	return auth_tok->token.password.session_key_encryption_key;
+}
+EXPORT_SYMBOL(ecryptfs_get_auth_tok_key);
+
+/*
+ * ecryptfs_get_versions()
+ *
+ * Source code taken from the software 'ecryptfs-utils' version 83.
+ *
+ */
+void ecryptfs_get_versions(int *major, int *minor, int *file_version)
+{
+	*major = ECRYPTFS_VERSION_MAJOR;
+	*minor = ECRYPTFS_VERSION_MINOR;
+	if (file_version)
+		*file_version = ECRYPTFS_SUPPORTED_FILE_VERSION;
+}
+EXPORT_SYMBOL(ecryptfs_get_versions);
+
+/*
+ * ecryptfs_fill_auth_tok - fill the ecryptfs_auth_tok structure
+ *
+ * Fill the ecryptfs_auth_tok structure with required ecryptfs data.
+ * The source code is inspired to the original function generate_payload()
+ * shipped with the software 'ecryptfs-utils' version 83.
+ *
+ */
+int ecryptfs_fill_auth_tok(struct ecryptfs_auth_tok *auth_tok,
+			   const char *key_desc)
+{
+	int major, minor;
+
+	ecryptfs_get_versions(&major, &minor, NULL);
+	auth_tok->version = (((uint16_t)(major << 8) & 0xFF00)
+			     | ((uint16_t)minor & 0x00FF));
+	auth_tok->token_type = ECRYPTFS_PASSWORD;
+	strncpy((char *)auth_tok->token.password.signature, key_desc,
+		ECRYPTFS_PASSWORD_SIG_SIZE);
+	auth_tok->token.password.session_key_encryption_key_bytes =
+		ECRYPTFS_MAX_KEY_BYTES;
+	/*
+	 * Removed auth_tok->token.password.salt and
+	 * auth_tok->token.password.session_key_encryption_key
+	 * initialization from the original code
+	 */
+	/* TODO: Make the hash parameterizable via policy */
+	auth_tok->token.password.flags |=
+		ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET;
+	/* The kernel code will encrypt the session key. */
+	auth_tok->session_key.encrypted_key[0] = 0;
+	auth_tok->session_key.encrypted_key_size = 0;
+	/* Default; subject to change by kernel eCryptfs */
+	auth_tok->token.password.hash_algo = PGP_DIGEST_ALGO_SHA512;
+	auth_tok->token.password.flags &= ~(ECRYPTFS_PERSISTENT_PASSWORD);
+	return 0;
+}
+EXPORT_SYMBOL(ecryptfs_fill_auth_tok);
+
+MODULE_LICENSE("GPL");
diff --git a/security/keys/encrypted-keys/ecryptfs_format.h b/security/keys/encrypted-keys/ecryptfs_format.h
new file mode 100644
index 0000000..40294de
--- /dev/null
+++ b/security/keys/encrypted-keys/ecryptfs_format.h
@@ -0,0 +1,30 @@
+/*
+ * ecryptfs_format.h: helper functions for the encrypted key type
+ *
+ * Copyright (C) 2006 International Business Machines Corp.
+ * Copyright (C) 2010 Politecnico di Torino, Italy
+ *                    TORSEC group -- http://security.polito.it
+ *
+ * Authors:
+ * Michael A. Halcrow <mahalcro@us.ibm.com>
+ * Tyler Hicks <tyhicks@ou.edu>
+ * Roberto Sassu <roberto.sassu@polito.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ */
+
+#ifndef __KEYS_ECRYPTFS_H
+#define __KEYS_ECRYPTFS_H
+
+#include <linux/ecryptfs.h>
+
+#define PGP_DIGEST_ALGO_SHA512   10
+
+u8 *ecryptfs_get_auth_tok_key(struct ecryptfs_auth_tok *auth_tok);
+void ecryptfs_get_versions(int *major, int *minor, int *file_version);
+int ecryptfs_fill_auth_tok(struct ecryptfs_auth_tok *auth_tok,
+			   const char *key_desc);
+
+#endif /* __KEYS_ECRYPTFS_H */
diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c
new file mode 100644
index 0000000..3f57795
--- /dev/null
+++ b/security/keys/encrypted-keys/encrypted.c
@@ -0,0 +1,1030 @@
+/*
+ * Copyright (C) 2010 IBM Corporation
+ * Copyright (C) 2010 Politecnico di Torino, Italy
+ *                    TORSEC group -- http://security.polito.it
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ * Roberto Sassu <roberto.sassu@polito.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * See Documentation/security/keys-trusted-encrypted.txt
+ */
+
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/parser.h>
+#include <linux/string.h>
+#include <linux/err.h>
+#include <keys/user-type.h>
+#include <keys/trusted-type.h>
+#include <keys/encrypted-type.h>
+#include <linux/key-type.h>
+#include <linux/random.h>
+#include <linux/rcupdate.h>
+#include <linux/scatterlist.h>
+#include <linux/crypto.h>
+#include <linux/ctype.h>
+#include <crypto/hash.h>
+#include <crypto/sha.h>
+#include <crypto/aes.h>
+
+#include "encrypted.h"
+#include "ecryptfs_format.h"
+
+static const char KEY_TRUSTED_PREFIX[] = "trusted:";
+static const char KEY_USER_PREFIX[] = "user:";
+static const char hash_alg[] = "sha256";
+static const char hmac_alg[] = "hmac(sha256)";
+static const char blkcipher_alg[] = "cbc(aes)";
+static const char key_format_default[] = "default";
+static const char key_format_ecryptfs[] = "ecryptfs";
+static unsigned int ivsize;
+static int blksize;
+
+#define KEY_TRUSTED_PREFIX_LEN (sizeof (KEY_TRUSTED_PREFIX) - 1)
+#define KEY_USER_PREFIX_LEN (sizeof (KEY_USER_PREFIX) - 1)
+#define KEY_ECRYPTFS_DESC_LEN 16
+#define HASH_SIZE SHA256_DIGEST_SIZE
+#define MAX_DATA_SIZE 4096
+#define MIN_DATA_SIZE  20
+
+struct sdesc {
+	struct shash_desc shash;
+	char ctx[];
+};
+
+static struct crypto_shash *hashalg;
+static struct crypto_shash *hmacalg;
+
+enum {
+	Opt_err = -1, Opt_new, Opt_load, Opt_update
+};
+
+enum {
+	Opt_error = -1, Opt_default, Opt_ecryptfs
+};
+
+static const match_table_t key_format_tokens = {
+	{Opt_default, "default"},
+	{Opt_ecryptfs, "ecryptfs"},
+	{Opt_error, NULL}
+};
+
+static const match_table_t key_tokens = {
+	{Opt_new, "new"},
+	{Opt_load, "load"},
+	{Opt_update, "update"},
+	{Opt_err, NULL}
+};
+
+static int aes_get_sizes(void)
+{
+	struct crypto_blkcipher *tfm;
+
+	tfm = crypto_alloc_blkcipher(blkcipher_alg, 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(tfm)) {
+		pr_err("encrypted_key: failed to alloc_cipher (%ld)\n",
+		       PTR_ERR(tfm));
+		return PTR_ERR(tfm);
+	}
+	ivsize = crypto_blkcipher_ivsize(tfm);
+	blksize = crypto_blkcipher_blocksize(tfm);
+	crypto_free_blkcipher(tfm);
+	return 0;
+}
+
+/*
+ * valid_ecryptfs_desc - verify the description of a new/loaded encrypted key
+ *
+ * The description of a encrypted key with format 'ecryptfs' must contain
+ * exactly 16 hexadecimal characters.
+ *
+ */
+static int valid_ecryptfs_desc(const char *ecryptfs_desc)
+{
+	int i;
+
+	if (strlen(ecryptfs_desc) != KEY_ECRYPTFS_DESC_LEN) {
+		pr_err("encrypted_key: key description must be %d hexadecimal "
+		       "characters long\n", KEY_ECRYPTFS_DESC_LEN);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < KEY_ECRYPTFS_DESC_LEN; i++) {
+		if (!isxdigit(ecryptfs_desc[i])) {
+			pr_err("encrypted_key: key description must contain "
+			       "only hexadecimal characters\n");
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * valid_master_desc - verify the 'key-type:desc' of a new/updated master-key
+ *
+ * key-type:= "trusted:" | "user:"
+ * desc:= master-key description
+ *
+ * Verify that 'key-type' is valid and that 'desc' exists. On key update,
+ * only the master key description is permitted to change, not the key-type.
+ * The key-type remains constant.
+ *
+ * On success returns 0, otherwise -EINVAL.
+ */
+static int valid_master_desc(const char *new_desc, const char *orig_desc)
+{
+	if (!memcmp(new_desc, KEY_TRUSTED_PREFIX, KEY_TRUSTED_PREFIX_LEN)) {
+		if (strlen(new_desc) == KEY_TRUSTED_PREFIX_LEN)
+			goto out;
+		if (orig_desc)
+			if (memcmp(new_desc, orig_desc, KEY_TRUSTED_PREFIX_LEN))
+				goto out;
+	} else if (!memcmp(new_desc, KEY_USER_PREFIX, KEY_USER_PREFIX_LEN)) {
+		if (strlen(new_desc) == KEY_USER_PREFIX_LEN)
+			goto out;
+		if (orig_desc)
+			if (memcmp(new_desc, orig_desc, KEY_USER_PREFIX_LEN))
+				goto out;
+	} else
+		goto out;
+	return 0;
+out:
+	return -EINVAL;
+}
+
+/*
+ * datablob_parse - parse the keyctl data
+ *
+ * datablob format:
+ * new [<format>] <master-key name> <decrypted data length>
+ * load [<format>] <master-key name> <decrypted data length>
+ *     <encrypted iv + data>
+ * update <new-master-key name>
+ *
+ * Tokenizes a copy of the keyctl data, returning a pointer to each token,
+ * which is null terminated.
+ *
+ * On success returns 0, otherwise -EINVAL.
+ */
+static int datablob_parse(char *datablob, const char **format,
+			  char **master_desc, char **decrypted_datalen,
+			  char **hex_encoded_iv)
+{
+	substring_t args[MAX_OPT_ARGS];
+	int ret = -EINVAL;
+	int key_cmd;
+	int key_format;
+	char *p, *keyword;
+
+	keyword = strsep(&datablob, " \t");
+	if (!keyword) {
+		pr_info("encrypted_key: insufficient parameters specified\n");
+		return ret;
+	}
+	key_cmd = match_token(keyword, key_tokens, args);
+
+	/* Get optional format: default | ecryptfs */
+	p = strsep(&datablob, " \t");
+	if (!p) {
+		pr_err("encrypted_key: insufficient parameters specified\n");
+		return ret;
+	}
+
+	key_format = match_token(p, key_format_tokens, args);
+	switch (key_format) {
+	case Opt_ecryptfs:
+	case Opt_default:
+		*format = p;
+		*master_desc = strsep(&datablob, " \t");
+		break;
+	case Opt_error:
+		*master_desc = p;
+		break;
+	}
+
+	if (!*master_desc) {
+		pr_info("encrypted_key: master key parameter is missing\n");
+		goto out;
+	}
+
+	if (valid_master_desc(*master_desc, NULL) < 0) {
+		pr_info("encrypted_key: master key parameter \'%s\' "
+			"is invalid\n", *master_desc);
+		goto out;
+	}
+
+	if (decrypted_datalen) {
+		*decrypted_datalen = strsep(&datablob, " \t");
+		if (!*decrypted_datalen) {
+			pr_info("encrypted_key: keylen parameter is missing\n");
+			goto out;
+		}
+	}
+
+	switch (key_cmd) {
+	case Opt_new:
+		if (!decrypted_datalen) {
+			pr_info("encrypted_key: keyword \'%s\' not allowed "
+				"when called from .update method\n", keyword);
+			break;
+		}
+		ret = 0;
+		break;
+	case Opt_load:
+		if (!decrypted_datalen) {
+			pr_info("encrypted_key: keyword \'%s\' not allowed "
+				"when called from .update method\n", keyword);
+			break;
+		}
+		*hex_encoded_iv = strsep(&datablob, " \t");
+		if (!*hex_encoded_iv) {
+			pr_info("encrypted_key: hex blob is missing\n");
+			break;
+		}
+		ret = 0;
+		break;
+	case Opt_update:
+		if (decrypted_datalen) {
+			pr_info("encrypted_key: keyword \'%s\' not allowed "
+				"when called from .instantiate method\n",
+				keyword);
+			break;
+		}
+		ret = 0;
+		break;
+	case Opt_err:
+		pr_info("encrypted_key: keyword \'%s\' not recognized\n",
+			keyword);
+		break;
+	}
+out:
+	return ret;
+}
+
+/*
+ * datablob_format - format as an ascii string, before copying to userspace
+ */
+static char *datablob_format(struct encrypted_key_payload *epayload,
+			     size_t asciiblob_len)
+{
+	char *ascii_buf, *bufp;
+	u8 *iv = epayload->iv;
+	int len;
+	int i;
+
+	ascii_buf = kmalloc(asciiblob_len + 1, GFP_KERNEL);
+	if (!ascii_buf)
+		goto out;
+
+	ascii_buf[asciiblob_len] = '\0';
+
+	/* copy datablob master_desc and datalen strings */
+	len = sprintf(ascii_buf, "%s %s %s ", epayload->format,
+		      epayload->master_desc, epayload->datalen);
+
+	/* convert the hex encoded iv, encrypted-data and HMAC to ascii */
+	bufp = &ascii_buf[len];
+	for (i = 0; i < (asciiblob_len - len) / 2; i++)
+		bufp = pack_hex_byte(bufp, iv[i]);
+out:
+	return ascii_buf;
+}
+
+/*
+ * request_user_key - request the user key
+ *
+ * Use a user provided key to encrypt/decrypt an encrypted-key.
+ */
+static struct key *request_user_key(const char *master_desc, u8 **master_key,
+				    size_t *master_keylen)
+{
+	struct user_key_payload *upayload;
+	struct key *ukey;
+
+	ukey = request_key(&key_type_user, master_desc, NULL);
+	if (IS_ERR(ukey))
+		goto error;
+
+	down_read(&ukey->sem);
+	upayload = rcu_dereference(ukey->payload.data);
+	*master_key = upayload->data;
+	*master_keylen = upayload->datalen;
+error:
+	return ukey;
+}
+
+static struct sdesc *alloc_sdesc(struct crypto_shash *alg)
+{
+	struct sdesc *sdesc;
+	int size;
+
+	size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
+	sdesc = kmalloc(size, GFP_KERNEL);
+	if (!sdesc)
+		return ERR_PTR(-ENOMEM);
+	sdesc->shash.tfm = alg;
+	sdesc->shash.flags = 0x0;
+	return sdesc;
+}
+
+static int calc_hmac(u8 *digest, const u8 *key, unsigned int keylen,
+		     const u8 *buf, unsigned int buflen)
+{
+	struct sdesc *sdesc;
+	int ret;
+
+	sdesc = alloc_sdesc(hmacalg);
+	if (IS_ERR(sdesc)) {
+		pr_info("encrypted_key: can't alloc %s\n", hmac_alg);
+		return PTR_ERR(sdesc);
+	}
+
+	ret = crypto_shash_setkey(hmacalg, key, keylen);
+	if (!ret)
+		ret = crypto_shash_digest(&sdesc->shash, buf, buflen, digest);
+	kfree(sdesc);
+	return ret;
+}
+
+static int calc_hash(u8 *digest, const u8 *buf, unsigned int buflen)
+{
+	struct sdesc *sdesc;
+	int ret;
+
+	sdesc = alloc_sdesc(hashalg);
+	if (IS_ERR(sdesc)) {
+		pr_info("encrypted_key: can't alloc %s\n", hash_alg);
+		return PTR_ERR(sdesc);
+	}
+
+	ret = crypto_shash_digest(&sdesc->shash, buf, buflen, digest);
+	kfree(sdesc);
+	return ret;
+}
+
+enum derived_key_type { ENC_KEY, AUTH_KEY };
+
+/* Derive authentication/encryption key from trusted key */
+static int get_derived_key(u8 *derived_key, enum derived_key_type key_type,
+			   const u8 *master_key, size_t master_keylen)
+{
+	u8 *derived_buf;
+	unsigned int derived_buf_len;
+	int ret;
+
+	derived_buf_len = strlen("AUTH_KEY") + 1 + master_keylen;
+	if (derived_buf_len < HASH_SIZE)
+		derived_buf_len = HASH_SIZE;
+
+	derived_buf = kzalloc(derived_buf_len, GFP_KERNEL);
+	if (!derived_buf) {
+		pr_err("encrypted_key: out of memory\n");
+		return -ENOMEM;
+	}
+	if (key_type)
+		strcpy(derived_buf, "AUTH_KEY");
+	else
+		strcpy(derived_buf, "ENC_KEY");
+
+	memcpy(derived_buf + strlen(derived_buf) + 1, master_key,
+	       master_keylen);
+	ret = calc_hash(derived_key, derived_buf, derived_buf_len);
+	kfree(derived_buf);
+	return ret;
+}
+
+static int init_blkcipher_desc(struct blkcipher_desc *desc, const u8 *key,
+			       unsigned int key_len, const u8 *iv,
+			       unsigned int ivsize)
+{
+	int ret;
+
+	desc->tfm = crypto_alloc_blkcipher(blkcipher_alg, 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(desc->tfm)) {
+		pr_err("encrypted_key: failed to load %s transform (%ld)\n",
+		       blkcipher_alg, PTR_ERR(desc->tfm));
+		return PTR_ERR(desc->tfm);
+	}
+	desc->flags = 0;
+
+	ret = crypto_blkcipher_setkey(desc->tfm, key, key_len);
+	if (ret < 0) {
+		pr_err("encrypted_key: failed to setkey (%d)\n", ret);
+		crypto_free_blkcipher(desc->tfm);
+		return ret;
+	}
+	crypto_blkcipher_set_iv(desc->tfm, iv, ivsize);
+	return 0;
+}
+
+static struct key *request_master_key(struct encrypted_key_payload *epayload,
+				      u8 **master_key, size_t *master_keylen)
+{
+	struct key *mkey = NULL;
+
+	if (!strncmp(epayload->master_desc, KEY_TRUSTED_PREFIX,
+		     KEY_TRUSTED_PREFIX_LEN)) {
+		mkey = request_trusted_key(epayload->master_desc +
+					   KEY_TRUSTED_PREFIX_LEN,
+					   master_key, master_keylen);
+	} else if (!strncmp(epayload->master_desc, KEY_USER_PREFIX,
+			    KEY_USER_PREFIX_LEN)) {
+		mkey = request_user_key(epayload->master_desc +
+					KEY_USER_PREFIX_LEN,
+					master_key, master_keylen);
+	} else
+		goto out;
+
+	if (IS_ERR(mkey)) {
+		int ret = PTR_ERR(epayload);
+
+		if (ret == -ENOTSUPP)
+			pr_info("encrypted_key: key %s not supported",
+				epayload->master_desc);
+		else
+			pr_info("encrypted_key: key %s not found",
+				epayload->master_desc);
+		goto out;
+	}
+
+	dump_master_key(*master_key, *master_keylen);
+out:
+	return mkey;
+}
+
+/* Before returning data to userspace, encrypt decrypted data. */
+static int derived_key_encrypt(struct encrypted_key_payload *epayload,
+			       const u8 *derived_key,
+			       unsigned int derived_keylen)
+{
+	struct scatterlist sg_in[2];
+	struct scatterlist sg_out[1];
+	struct blkcipher_desc desc;
+	unsigned int encrypted_datalen;
+	unsigned int padlen;
+	char pad[16];
+	int ret;
+
+	encrypted_datalen = roundup(epayload->decrypted_datalen, blksize);
+	padlen = encrypted_datalen - epayload->decrypted_datalen;
+
+	ret = init_blkcipher_desc(&desc, derived_key, derived_keylen,
+				  epayload->iv, ivsize);
+	if (ret < 0)
+		goto out;
+	dump_decrypted_data(epayload);
+
+	memset(pad, 0, sizeof pad);
+	sg_init_table(sg_in, 2);
+	sg_set_buf(&sg_in[0], epayload->decrypted_data,
+		   epayload->decrypted_datalen);
+	sg_set_buf(&sg_in[1], pad, padlen);
+
+	sg_init_table(sg_out, 1);
+	sg_set_buf(sg_out, epayload->encrypted_data, encrypted_datalen);
+
+	ret = crypto_blkcipher_encrypt(&desc, sg_out, sg_in, encrypted_datalen);
+	crypto_free_blkcipher(desc.tfm);
+	if (ret < 0)
+		pr_err("encrypted_key: failed to encrypt (%d)\n", ret);
+	else
+		dump_encrypted_data(epayload, encrypted_datalen);
+out:
+	return ret;
+}
+
+static int datablob_hmac_append(struct encrypted_key_payload *epayload,
+				const u8 *master_key, size_t master_keylen)
+{
+	u8 derived_key[HASH_SIZE];
+	u8 *digest;
+	int ret;
+
+	ret = get_derived_key(derived_key, AUTH_KEY, master_key, master_keylen);
+	if (ret < 0)
+		goto out;
+
+	digest = epayload->format + epayload->datablob_len;
+	ret = calc_hmac(digest, derived_key, sizeof derived_key,
+			epayload->format, epayload->datablob_len);
+	if (!ret)
+		dump_hmac(NULL, digest, HASH_SIZE);
+out:
+	return ret;
+}
+
+/* verify HMAC before decrypting encrypted key */
+static int datablob_hmac_verify(struct encrypted_key_payload *epayload,
+				const u8 *format, const u8 *master_key,
+				size_t master_keylen)
+{
+	u8 derived_key[HASH_SIZE];
+	u8 digest[HASH_SIZE];
+	int ret;
+	char *p;
+	unsigned short len;
+
+	ret = get_derived_key(derived_key, AUTH_KEY, master_key, master_keylen);
+	if (ret < 0)
+		goto out;
+
+	len = epayload->datablob_len;
+	if (!format) {
+		p = epayload->master_desc;
+		len -= strlen(epayload->format) + 1;
+	} else
+		p = epayload->format;
+
+	ret = calc_hmac(digest, derived_key, sizeof derived_key, p, len);
+	if (ret < 0)
+		goto out;
+	ret = memcmp(digest, epayload->format + epayload->datablob_len,
+		     sizeof digest);
+	if (ret) {
+		ret = -EINVAL;
+		dump_hmac("datablob",
+			  epayload->format + epayload->datablob_len,
+			  HASH_SIZE);
+		dump_hmac("calc", digest, HASH_SIZE);
+	}
+out:
+	return ret;
+}
+
+static int derived_key_decrypt(struct encrypted_key_payload *epayload,
+			       const u8 *derived_key,
+			       unsigned int derived_keylen)
+{
+	struct scatterlist sg_in[1];
+	struct scatterlist sg_out[2];
+	struct blkcipher_desc desc;
+	unsigned int encrypted_datalen;
+	char pad[16];
+	int ret;
+
+	encrypted_datalen = roundup(epayload->decrypted_datalen, blksize);
+	ret = init_blkcipher_desc(&desc, derived_key, derived_keylen,
+				  epayload->iv, ivsize);
+	if (ret < 0)
+		goto out;
+	dump_encrypted_data(epayload, encrypted_datalen);
+
+	memset(pad, 0, sizeof pad);
+	sg_init_table(sg_in, 1);
+	sg_init_table(sg_out, 2);
+	sg_set_buf(sg_in, epayload->encrypted_data, encrypted_datalen);
+	sg_set_buf(&sg_out[0], epayload->decrypted_data,
+		   epayload->decrypted_datalen);
+	sg_set_buf(&sg_out[1], pad, sizeof pad);
+
+	ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in, encrypted_datalen);
+	crypto_free_blkcipher(desc.tfm);
+	if (ret < 0)
+		goto out;
+	dump_decrypted_data(epayload);
+out:
+	return ret;
+}
+
+/* Allocate memory for decrypted key and datablob. */
+static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
+							 const char *format,
+							 const char *master_desc,
+							 const char *datalen)
+{
+	struct encrypted_key_payload *epayload = NULL;
+	unsigned short datablob_len;
+	unsigned short decrypted_datalen;
+	unsigned short payload_datalen;
+	unsigned int encrypted_datalen;
+	unsigned int format_len;
+	long dlen;
+	int ret;
+
+	ret = strict_strtol(datalen, 10, &dlen);
+	if (ret < 0 || dlen < MIN_DATA_SIZE || dlen > MAX_DATA_SIZE)
+		return ERR_PTR(-EINVAL);
+
+	format_len = (!format) ? strlen(key_format_default) : strlen(format);
+	decrypted_datalen = dlen;
+	payload_datalen = decrypted_datalen;
+	if (format && !strcmp(format, key_format_ecryptfs)) {
+		if (dlen != ECRYPTFS_MAX_KEY_BYTES) {
+			pr_err("encrypted_key: keylen for the ecryptfs format "
+			       "must be equal to %d bytes\n",
+			       ECRYPTFS_MAX_KEY_BYTES);
+			return ERR_PTR(-EINVAL);
+		}
+		decrypted_datalen = ECRYPTFS_MAX_KEY_BYTES;
+		payload_datalen = sizeof(struct ecryptfs_auth_tok);
+	}
+
+	encrypted_datalen = roundup(decrypted_datalen, blksize);
+
+	datablob_len = format_len + 1 + strlen(master_desc) + 1
+	    + strlen(datalen) + 1 + ivsize + 1 + encrypted_datalen;
+
+	ret = key_payload_reserve(key, payload_datalen + datablob_len
+				  + HASH_SIZE + 1);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	epayload = kzalloc(sizeof(*epayload) + payload_datalen +
+			   datablob_len + HASH_SIZE + 1, GFP_KERNEL);
+	if (!epayload)
+		return ERR_PTR(-ENOMEM);
+
+	epayload->payload_datalen = payload_datalen;
+	epayload->decrypted_datalen = decrypted_datalen;
+	epayload->datablob_len = datablob_len;
+	return epayload;
+}
+
+static int encrypted_key_decrypt(struct encrypted_key_payload *epayload,
+				 const char *format, const char *hex_encoded_iv)
+{
+	struct key *mkey;
+	u8 derived_key[HASH_SIZE];
+	u8 *master_key;
+	u8 *hmac;
+	const char *hex_encoded_data;
+	unsigned int encrypted_datalen;
+	size_t master_keylen;
+	size_t asciilen;
+	int ret;
+
+	encrypted_datalen = roundup(epayload->decrypted_datalen, blksize);
+	asciilen = (ivsize + 1 + encrypted_datalen + HASH_SIZE) * 2;
+	if (strlen(hex_encoded_iv) != asciilen)
+		return -EINVAL;
+
+	hex_encoded_data = hex_encoded_iv + (2 * ivsize) + 2;
+	hex2bin(epayload->iv, hex_encoded_iv, ivsize);
+	hex2bin(epayload->encrypted_data, hex_encoded_data, encrypted_datalen);
+
+	hmac = epayload->format + epayload->datablob_len;
+	hex2bin(hmac, hex_encoded_data + (encrypted_datalen * 2), HASH_SIZE);
+
+	mkey = request_master_key(epayload, &master_key, &master_keylen);
+	if (IS_ERR(mkey))
+		return PTR_ERR(mkey);
+
+	ret = datablob_hmac_verify(epayload, format, master_key, master_keylen);
+	if (ret < 0) {
+		pr_err("encrypted_key: bad hmac (%d)\n", ret);
+		goto out;
+	}
+
+	ret = get_derived_key(derived_key, ENC_KEY, master_key, master_keylen);
+	if (ret < 0)
+		goto out;
+
+	ret = derived_key_decrypt(epayload, derived_key, sizeof derived_key);
+	if (ret < 0)
+		pr_err("encrypted_key: failed to decrypt key (%d)\n", ret);
+out:
+	up_read(&mkey->sem);
+	key_put(mkey);
+	return ret;
+}
+
+static void __ekey_init(struct encrypted_key_payload *epayload,
+			const char *format, const char *master_desc,
+			const char *datalen)
+{
+	unsigned int format_len;
+
+	format_len = (!format) ? strlen(key_format_default) : strlen(format);
+	epayload->format = epayload->payload_data + epayload->payload_datalen;
+	epayload->master_desc = epayload->format + format_len + 1;
+	epayload->datalen = epayload->master_desc + strlen(master_desc) + 1;
+	epayload->iv = epayload->datalen + strlen(datalen) + 1;
+	epayload->encrypted_data = epayload->iv + ivsize + 1;
+	epayload->decrypted_data = epayload->payload_data;
+
+	if (!format)
+		memcpy(epayload->format, key_format_default, format_len);
+	else {
+		if (!strcmp(format, key_format_ecryptfs))
+			epayload->decrypted_data =
+				ecryptfs_get_auth_tok_key((struct ecryptfs_auth_tok *)epayload->payload_data);
+
+		memcpy(epayload->format, format, format_len);
+	}
+
+	memcpy(epayload->master_desc, master_desc, strlen(master_desc));
+	memcpy(epayload->datalen, datalen, strlen(datalen));
+}
+
+/*
+ * encrypted_init - initialize an encrypted key
+ *
+ * For a new key, use a random number for both the iv and data
+ * itself.  For an old key, decrypt the hex encoded data.
+ */
+static int encrypted_init(struct encrypted_key_payload *epayload,
+			  const char *key_desc, const char *format,
+			  const char *master_desc, const char *datalen,
+			  const char *hex_encoded_iv)
+{
+	int ret = 0;
+
+	if (format && !strcmp(format, key_format_ecryptfs)) {
+		ret = valid_ecryptfs_desc(key_desc);
+		if (ret < 0)
+			return ret;
+
+		ecryptfs_fill_auth_tok((struct ecryptfs_auth_tok *)epayload->payload_data,
+				       key_desc);
+	}
+
+	__ekey_init(epayload, format, master_desc, datalen);
+	if (!hex_encoded_iv) {
+		get_random_bytes(epayload->iv, ivsize);
+
+		get_random_bytes(epayload->decrypted_data,
+				 epayload->decrypted_datalen);
+	} else
+		ret = encrypted_key_decrypt(epayload, format, hex_encoded_iv);
+	return ret;
+}
+
+/*
+ * encrypted_instantiate - instantiate an encrypted key
+ *
+ * Decrypt an existing encrypted datablob or create a new encrypted key
+ * based on a kernel random number.
+ *
+ * On success, return 0. Otherwise return errno.
+ */
+static int encrypted_instantiate(struct key *key, const void *data,
+				 size_t datalen)
+{
+	struct encrypted_key_payload *epayload = NULL;
+	char *datablob = NULL;
+	const char *format = NULL;
+	char *master_desc = NULL;
+	char *decrypted_datalen = NULL;
+	char *hex_encoded_iv = NULL;
+	int ret;
+
+	if (datalen <= 0 || datalen > 32767 || !data)
+		return -EINVAL;
+
+	datablob = kmalloc(datalen + 1, GFP_KERNEL);
+	if (!datablob)
+		return -ENOMEM;
+	datablob[datalen] = 0;
+	memcpy(datablob, data, datalen);
+	ret = datablob_parse(datablob, &format, &master_desc,
+			     &decrypted_datalen, &hex_encoded_iv);
+	if (ret < 0)
+		goto out;
+
+	epayload = encrypted_key_alloc(key, format, master_desc,
+				       decrypted_datalen);
+	if (IS_ERR(epayload)) {
+		ret = PTR_ERR(epayload);
+		goto out;
+	}
+	ret = encrypted_init(epayload, key->description, format, master_desc,
+			     decrypted_datalen, hex_encoded_iv);
+	if (ret < 0) {
+		kfree(epayload);
+		goto out;
+	}
+
+	rcu_assign_pointer(key->payload.data, epayload);
+out:
+	kfree(datablob);
+	return ret;
+}
+
+static void encrypted_rcu_free(struct rcu_head *rcu)
+{
+	struct encrypted_key_payload *epayload;
+
+	epayload = container_of(rcu, struct encrypted_key_payload, rcu);
+	memset(epayload->decrypted_data, 0, epayload->decrypted_datalen);
+	kfree(epayload);
+}
+
+/*
+ * encrypted_update - update the master key description
+ *
+ * Change the master key description for an existing encrypted key.
+ * The next read will return an encrypted datablob using the new
+ * master key description.
+ *
+ * On success, return 0. Otherwise return errno.
+ */
+static int encrypted_update(struct key *key, const void *data, size_t datalen)
+{
+	struct encrypted_key_payload *epayload = key->payload.data;
+	struct encrypted_key_payload *new_epayload;
+	char *buf;
+	char *new_master_desc = NULL;
+	const char *format = NULL;
+	int ret = 0;
+
+	if (datalen <= 0 || datalen > 32767 || !data)
+		return -EINVAL;
+
+	buf = kmalloc(datalen + 1, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	buf[datalen] = 0;
+	memcpy(buf, data, datalen);
+	ret = datablob_parse(buf, &format, &new_master_desc, NULL, NULL);
+	if (ret < 0)
+		goto out;
+
+	ret = valid_master_desc(new_master_desc, epayload->master_desc);
+	if (ret < 0)
+		goto out;
+
+	new_epayload = encrypted_key_alloc(key, epayload->format,
+					   new_master_desc, epayload->datalen);
+	if (IS_ERR(new_epayload)) {
+		ret = PTR_ERR(new_epayload);
+		goto out;
+	}
+
+	__ekey_init(new_epayload, epayload->format, new_master_desc,
+		    epayload->datalen);
+
+	memcpy(new_epayload->iv, epayload->iv, ivsize);
+	memcpy(new_epayload->payload_data, epayload->payload_data,
+	       epayload->payload_datalen);
+
+	rcu_assign_pointer(key->payload.data, new_epayload);
+	call_rcu(&epayload->rcu, encrypted_rcu_free);
+out:
+	kfree(buf);
+	return ret;
+}
+
+/*
+ * encrypted_read - format and copy the encrypted data to userspace
+ *
+ * The resulting datablob format is:
+ * <master-key name> <decrypted data length> <encrypted iv> <encrypted data>
+ *
+ * On success, return to userspace the encrypted key datablob size.
+ */
+static long encrypted_read(const struct key *key, char __user *buffer,
+			   size_t buflen)
+{
+	struct encrypted_key_payload *epayload;
+	struct key *mkey;
+	u8 *master_key;
+	size_t master_keylen;
+	char derived_key[HASH_SIZE];
+	char *ascii_buf;
+	size_t asciiblob_len;
+	int ret;
+
+	epayload = rcu_dereference_key(key);
+
+	/* returns the hex encoded iv, encrypted-data, and hmac as ascii */
+	asciiblob_len = epayload->datablob_len + ivsize + 1
+	    + roundup(epayload->decrypted_datalen, blksize)
+	    + (HASH_SIZE * 2);
+
+	if (!buffer || buflen < asciiblob_len)
+		return asciiblob_len;
+
+	mkey = request_master_key(epayload, &master_key, &master_keylen);
+	if (IS_ERR(mkey))
+		return PTR_ERR(mkey);
+
+	ret = get_derived_key(derived_key, ENC_KEY, master_key, master_keylen);
+	if (ret < 0)
+		goto out;
+
+	ret = derived_key_encrypt(epayload, derived_key, sizeof derived_key);
+	if (ret < 0)
+		goto out;
+
+	ret = datablob_hmac_append(epayload, master_key, master_keylen);
+	if (ret < 0)
+		goto out;
+
+	ascii_buf = datablob_format(epayload, asciiblob_len);
+	if (!ascii_buf) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	up_read(&mkey->sem);
+	key_put(mkey);
+
+	if (copy_to_user(buffer, ascii_buf, asciiblob_len) != 0)
+		ret = -EFAULT;
+	kfree(ascii_buf);
+
+	return asciiblob_len;
+out:
+	up_read(&mkey->sem);
+	key_put(mkey);
+	return ret;
+}
+
+/*
+ * encrypted_destroy - before freeing the key, clear the decrypted data
+ *
+ * Before freeing the key, clear the memory containing the decrypted
+ * key data.
+ */
+static void encrypted_destroy(struct key *key)
+{
+	struct encrypted_key_payload *epayload = key->payload.data;
+
+	if (!epayload)
+		return;
+
+	memset(epayload->decrypted_data, 0, epayload->decrypted_datalen);
+	kfree(key->payload.data);
+}
+
+struct key_type key_type_encrypted = {
+	.name = "encrypted",
+	.instantiate = encrypted_instantiate,
+	.update = encrypted_update,
+	.match = user_match,
+	.destroy = encrypted_destroy,
+	.describe = user_describe,
+	.read = encrypted_read,
+};
+EXPORT_SYMBOL_GPL(key_type_encrypted);
+
+static void encrypted_shash_release(void)
+{
+	if (hashalg)
+		crypto_free_shash(hashalg);
+	if (hmacalg)
+		crypto_free_shash(hmacalg);
+}
+
+static int __init encrypted_shash_alloc(void)
+{
+	int ret;
+
+	hmacalg = crypto_alloc_shash(hmac_alg, 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(hmacalg)) {
+		pr_info("encrypted_key: could not allocate crypto %s\n",
+			hmac_alg);
+		return PTR_ERR(hmacalg);
+	}
+
+	hashalg = crypto_alloc_shash(hash_alg, 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(hashalg)) {
+		pr_info("encrypted_key: could not allocate crypto %s\n",
+			hash_alg);
+		ret = PTR_ERR(hashalg);
+		goto hashalg_fail;
+	}
+
+	return 0;
+
+hashalg_fail:
+	crypto_free_shash(hmacalg);
+	return ret;
+}
+
+static int __init init_encrypted(void)
+{
+	int ret;
+
+	ret = encrypted_shash_alloc();
+	if (ret < 0)
+		return ret;
+	ret = register_key_type(&key_type_encrypted);
+	if (ret < 0)
+		goto out;
+	return aes_get_sizes();
+out:
+	encrypted_shash_release();
+	return ret;
+
+}
+
+static void __exit cleanup_encrypted(void)
+{
+	encrypted_shash_release();
+	unregister_key_type(&key_type_encrypted);
+}
+
+late_initcall(init_encrypted);
+module_exit(cleanup_encrypted);
+
+MODULE_LICENSE("GPL");
diff --git a/security/keys/encrypted-keys/encrypted.h b/security/keys/encrypted-keys/encrypted.h
new file mode 100644
index 0000000..b6ade89
--- /dev/null
+++ b/security/keys/encrypted-keys/encrypted.h
@@ -0,0 +1,65 @@
+#ifndef __ENCRYPTED_KEY_H
+#define __ENCRYPTED_KEY_H
+
+#define ENCRYPTED_DEBUG 0
+#ifdef CONFIG_TRUSTED_KEYS
+extern struct key *request_trusted_key(const char *trusted_desc,
+				       u8 **master_key, size_t *master_keylen);
+#else
+static inline struct key *request_trusted_key(const char *trusted_desc,
+					      u8 **master_key,
+					      size_t *master_keylen)
+{
+	return ERR_PTR(-EOPNOTSUPP);
+}
+#endif
+
+#if ENCRYPTED_DEBUG
+static inline void dump_master_key(const u8 *master_key, size_t master_keylen)
+{
+	print_hex_dump(KERN_ERR, "master key: ", DUMP_PREFIX_NONE, 32, 1,
+		       master_key, master_keylen, 0);
+}
+
+static inline void dump_decrypted_data(struct encrypted_key_payload *epayload)
+{
+	print_hex_dump(KERN_ERR, "decrypted data: ", DUMP_PREFIX_NONE, 32, 1,
+		       epayload->decrypted_data,
+		       epayload->decrypted_datalen, 0);
+}
+
+static inline void dump_encrypted_data(struct encrypted_key_payload *epayload,
+				       unsigned int encrypted_datalen)
+{
+	print_hex_dump(KERN_ERR, "encrypted data: ", DUMP_PREFIX_NONE, 32, 1,
+		       epayload->encrypted_data, encrypted_datalen, 0);
+}
+
+static inline void dump_hmac(const char *str, const u8 *digest,
+			     unsigned int hmac_size)
+{
+	if (str)
+		pr_info("encrypted_key: %s", str);
+	print_hex_dump(KERN_ERR, "hmac: ", DUMP_PREFIX_NONE, 32, 1, digest,
+		       hmac_size, 0);
+}
+#else
+static inline void dump_master_key(const u8 *master_key, size_t master_keylen)
+{
+}
+
+static inline void dump_decrypted_data(struct encrypted_key_payload *epayload)
+{
+}
+
+static inline void dump_encrypted_data(struct encrypted_key_payload *epayload,
+				       unsigned int encrypted_datalen)
+{
+}
+
+static inline void dump_hmac(const char *str, const u8 *digest,
+			     unsigned int hmac_size)
+{
+}
+#endif
+#endif
diff --git a/security/keys/encrypted-keys/masterkey_trusted.c b/security/keys/encrypted-keys/masterkey_trusted.c
new file mode 100644
index 0000000..a5da512
--- /dev/null
+++ b/security/keys/encrypted-keys/masterkey_trusted.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2010 IBM Corporation
+ * Copyright (C) 2010 Politecnico di Torino, Italy
+ *                    TORSEC group -- http://security.polito.it
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ * Roberto Sassu <roberto.sassu@polito.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * See Documentation/security/keys-trusted-encrypted.txt
+ */
+
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <keys/trusted-type.h>
+
+/*
+ * request_trusted_key - request the trusted key
+ *
+ * Trusted keys are sealed to PCRs and other metadata. Although userspace
+ * manages both trusted/encrypted key-types, like the encrypted key type
+ * data, trusted key type data is not visible decrypted from userspace.
+ */
+struct key *request_trusted_key(const char *trusted_desc,
+				u8 **master_key, size_t *master_keylen)
+{
+	struct trusted_key_payload *tpayload;
+	struct key *tkey;
+
+	tkey = request_key(&key_type_trusted, trusted_desc, NULL);
+	if (IS_ERR(tkey))
+		goto error;
+
+	down_read(&tkey->sem);
+	tpayload = rcu_dereference(tkey->payload.data);
+	*master_key = tpayload->key;
+	*master_keylen = tpayload->key_len;
+error:
+	return tkey;
+}
diff --git a/security/keys/encrypted.c b/security/keys/encrypted.c
deleted file mode 100644
index e7eca9e..0000000
--- a/security/keys/encrypted.c
+++ /dev/null
@@ -1,1049 +0,0 @@
-/*
- * Copyright (C) 2010 IBM Corporation
- * Copyright (C) 2010 Politecnico di Torino, Italy
- *                    TORSEC group -- http://security.polito.it
- *
- * Authors:
- * Mimi Zohar <zohar@us.ibm.com>
- * Roberto Sassu <roberto.sassu@polito.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 2 of the License.
- *
- * See Documentation/security/keys-trusted-encrypted.txt
- */
-
-#include <linux/uaccess.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/parser.h>
-#include <linux/string.h>
-#include <linux/err.h>
-#include <keys/user-type.h>
-#include <keys/trusted-type.h>
-#include <keys/encrypted-type.h>
-#include <linux/key-type.h>
-#include <linux/random.h>
-#include <linux/rcupdate.h>
-#include <linux/scatterlist.h>
-#include <linux/crypto.h>
-#include <linux/ctype.h>
-#include <crypto/hash.h>
-#include <crypto/sha.h>
-#include <crypto/aes.h>
-
-#include "encrypted.h"
-#include "ecryptfs_format.h"
-
-static const char KEY_TRUSTED_PREFIX[] = "trusted:";
-static const char KEY_USER_PREFIX[] = "user:";
-static const char hash_alg[] = "sha256";
-static const char hmac_alg[] = "hmac(sha256)";
-static const char blkcipher_alg[] = "cbc(aes)";
-static const char key_format_default[] = "default";
-static const char key_format_ecryptfs[] = "ecryptfs";
-static unsigned int ivsize;
-static int blksize;
-
-#define KEY_TRUSTED_PREFIX_LEN (sizeof (KEY_TRUSTED_PREFIX) - 1)
-#define KEY_USER_PREFIX_LEN (sizeof (KEY_USER_PREFIX) - 1)
-#define KEY_ECRYPTFS_DESC_LEN 16
-#define HASH_SIZE SHA256_DIGEST_SIZE
-#define MAX_DATA_SIZE 4096
-#define MIN_DATA_SIZE  20
-
-struct sdesc {
-	struct shash_desc shash;
-	char ctx[];
-};
-
-static struct crypto_shash *hashalg;
-static struct crypto_shash *hmacalg;
-
-enum {
-	Opt_err = -1, Opt_new, Opt_load, Opt_update
-};
-
-enum {
-	Opt_error = -1, Opt_default, Opt_ecryptfs
-};
-
-static const match_table_t key_format_tokens = {
-	{Opt_default, "default"},
-	{Opt_ecryptfs, "ecryptfs"},
-	{Opt_error, NULL}
-};
-
-static const match_table_t key_tokens = {
-	{Opt_new, "new"},
-	{Opt_load, "load"},
-	{Opt_update, "update"},
-	{Opt_err, NULL}
-};
-
-static int aes_get_sizes(void)
-{
-	struct crypto_blkcipher *tfm;
-
-	tfm = crypto_alloc_blkcipher(blkcipher_alg, 0, CRYPTO_ALG_ASYNC);
-	if (IS_ERR(tfm)) {
-		pr_err("encrypted_key: failed to alloc_cipher (%ld)\n",
-		       PTR_ERR(tfm));
-		return PTR_ERR(tfm);
-	}
-	ivsize = crypto_blkcipher_ivsize(tfm);
-	blksize = crypto_blkcipher_blocksize(tfm);
-	crypto_free_blkcipher(tfm);
-	return 0;
-}
-
-/*
- * valid_ecryptfs_desc - verify the description of a new/loaded encrypted key
- *
- * The description of a encrypted key with format 'ecryptfs' must contain
- * exactly 16 hexadecimal characters.
- *
- */
-static int valid_ecryptfs_desc(const char *ecryptfs_desc)
-{
-	int i;
-
-	if (strlen(ecryptfs_desc) != KEY_ECRYPTFS_DESC_LEN) {
-		pr_err("encrypted_key: key description must be %d hexadecimal "
-		       "characters long\n", KEY_ECRYPTFS_DESC_LEN);
-		return -EINVAL;
-	}
-
-	for (i = 0; i < KEY_ECRYPTFS_DESC_LEN; i++) {
-		if (!isxdigit(ecryptfs_desc[i])) {
-			pr_err("encrypted_key: key description must contain "
-			       "only hexadecimal characters\n");
-			return -EINVAL;
-		}
-	}
-
-	return 0;
-}
-
-/*
- * valid_master_desc - verify the 'key-type:desc' of a new/updated master-key
- *
- * key-type:= "trusted:" | "user:"
- * desc:= master-key description
- *
- * Verify that 'key-type' is valid and that 'desc' exists. On key update,
- * only the master key description is permitted to change, not the key-type.
- * The key-type remains constant.
- *
- * On success returns 0, otherwise -EINVAL.
- */
-static int valid_master_desc(const char *new_desc, const char *orig_desc)
-{
-	if (!memcmp(new_desc, KEY_TRUSTED_PREFIX, KEY_TRUSTED_PREFIX_LEN)) {
-		if (strlen(new_desc) == KEY_TRUSTED_PREFIX_LEN)
-			goto out;
-		if (orig_desc)
-			if (memcmp(new_desc, orig_desc, KEY_TRUSTED_PREFIX_LEN))
-				goto out;
-	} else if (!memcmp(new_desc, KEY_USER_PREFIX, KEY_USER_PREFIX_LEN)) {
-		if (strlen(new_desc) == KEY_USER_PREFIX_LEN)
-			goto out;
-		if (orig_desc)
-			if (memcmp(new_desc, orig_desc, KEY_USER_PREFIX_LEN))
-				goto out;
-	} else
-		goto out;
-	return 0;
-out:
-	return -EINVAL;
-}
-
-/*
- * datablob_parse - parse the keyctl data
- *
- * datablob format:
- * new [<format>] <master-key name> <decrypted data length>
- * load [<format>] <master-key name> <decrypted data length>
- *     <encrypted iv + data>
- * update <new-master-key name>
- *
- * Tokenizes a copy of the keyctl data, returning a pointer to each token,
- * which is null terminated.
- *
- * On success returns 0, otherwise -EINVAL.
- */
-static int datablob_parse(char *datablob, const char **format,
-			  char **master_desc, char **decrypted_datalen,
-			  char **hex_encoded_iv)
-{
-	substring_t args[MAX_OPT_ARGS];
-	int ret = -EINVAL;
-	int key_cmd;
-	int key_format;
-	char *p, *keyword;
-
-	keyword = strsep(&datablob, " \t");
-	if (!keyword) {
-		pr_info("encrypted_key: insufficient parameters specified\n");
-		return ret;
-	}
-	key_cmd = match_token(keyword, key_tokens, args);
-
-	/* Get optional format: default | ecryptfs */
-	p = strsep(&datablob, " \t");
-	if (!p) {
-		pr_err("encrypted_key: insufficient parameters specified\n");
-		return ret;
-	}
-
-	key_format = match_token(p, key_format_tokens, args);
-	switch (key_format) {
-	case Opt_ecryptfs:
-	case Opt_default:
-		*format = p;
-		*master_desc = strsep(&datablob, " \t");
-		break;
-	case Opt_error:
-		*master_desc = p;
-		break;
-	}
-
-	if (!*master_desc) {
-		pr_info("encrypted_key: master key parameter is missing\n");
-		goto out;
-	}
-
-	if (valid_master_desc(*master_desc, NULL) < 0) {
-		pr_info("encrypted_key: master key parameter \'%s\' "
-			"is invalid\n", *master_desc);
-		goto out;
-	}
-
-	if (decrypted_datalen) {
-		*decrypted_datalen = strsep(&datablob, " \t");
-		if (!*decrypted_datalen) {
-			pr_info("encrypted_key: keylen parameter is missing\n");
-			goto out;
-		}
-	}
-
-	switch (key_cmd) {
-	case Opt_new:
-		if (!decrypted_datalen) {
-			pr_info("encrypted_key: keyword \'%s\' not allowed "
-				"when called from .update method\n", keyword);
-			break;
-		}
-		ret = 0;
-		break;
-	case Opt_load:
-		if (!decrypted_datalen) {
-			pr_info("encrypted_key: keyword \'%s\' not allowed "
-				"when called from .update method\n", keyword);
-			break;
-		}
-		*hex_encoded_iv = strsep(&datablob, " \t");
-		if (!*hex_encoded_iv) {
-			pr_info("encrypted_key: hex blob is missing\n");
-			break;
-		}
-		ret = 0;
-		break;
-	case Opt_update:
-		if (decrypted_datalen) {
-			pr_info("encrypted_key: keyword \'%s\' not allowed "
-				"when called from .instantiate method\n",
-				keyword);
-			break;
-		}
-		ret = 0;
-		break;
-	case Opt_err:
-		pr_info("encrypted_key: keyword \'%s\' not recognized\n",
-			keyword);
-		break;
-	}
-out:
-	return ret;
-}
-
-/*
- * datablob_format - format as an ascii string, before copying to userspace
- */
-static char *datablob_format(struct encrypted_key_payload *epayload,
-			     size_t asciiblob_len)
-{
-	char *ascii_buf, *bufp;
-	u8 *iv = epayload->iv;
-	int len;
-	int i;
-
-	ascii_buf = kmalloc(asciiblob_len + 1, GFP_KERNEL);
-	if (!ascii_buf)
-		goto out;
-
-	ascii_buf[asciiblob_len] = '\0';
-
-	/* copy datablob master_desc and datalen strings */
-	len = sprintf(ascii_buf, "%s %s %s ", epayload->format,
-		      epayload->master_desc, epayload->datalen);
-
-	/* convert the hex encoded iv, encrypted-data and HMAC to ascii */
-	bufp = &ascii_buf[len];
-	for (i = 0; i < (asciiblob_len - len) / 2; i++)
-		bufp = pack_hex_byte(bufp, iv[i]);
-out:
-	return ascii_buf;
-}
-
-/*
- * request_trusted_key - request the trusted key
- *
- * Trusted keys are sealed to PCRs and other metadata. Although userspace
- * manages both trusted/encrypted key-types, like the encrypted key type
- * data, trusted key type data is not visible decrypted from userspace.
- */
-static struct key *request_trusted_key(const char *trusted_desc,
-				       u8 **master_key, size_t *master_keylen)
-{
-	struct trusted_key_payload *tpayload;
-	struct key *tkey;
-
-	tkey = request_key(&key_type_trusted, trusted_desc, NULL);
-	if (IS_ERR(tkey))
-		goto error;
-
-	down_read(&tkey->sem);
-	tpayload = rcu_dereference(tkey->payload.data);
-	*master_key = tpayload->key;
-	*master_keylen = tpayload->key_len;
-error:
-	return tkey;
-}
-
-/*
- * request_user_key - request the user key
- *
- * Use a user provided key to encrypt/decrypt an encrypted-key.
- */
-static struct key *request_user_key(const char *master_desc, u8 **master_key,
-				    size_t *master_keylen)
-{
-	struct user_key_payload *upayload;
-	struct key *ukey;
-
-	ukey = request_key(&key_type_user, master_desc, NULL);
-	if (IS_ERR(ukey))
-		goto error;
-
-	down_read(&ukey->sem);
-	upayload = rcu_dereference(ukey->payload.data);
-	*master_key = upayload->data;
-	*master_keylen = upayload->datalen;
-error:
-	return ukey;
-}
-
-static struct sdesc *alloc_sdesc(struct crypto_shash *alg)
-{
-	struct sdesc *sdesc;
-	int size;
-
-	size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
-	sdesc = kmalloc(size, GFP_KERNEL);
-	if (!sdesc)
-		return ERR_PTR(-ENOMEM);
-	sdesc->shash.tfm = alg;
-	sdesc->shash.flags = 0x0;
-	return sdesc;
-}
-
-static int calc_hmac(u8 *digest, const u8 *key, unsigned int keylen,
-		     const u8 *buf, unsigned int buflen)
-{
-	struct sdesc *sdesc;
-	int ret;
-
-	sdesc = alloc_sdesc(hmacalg);
-	if (IS_ERR(sdesc)) {
-		pr_info("encrypted_key: can't alloc %s\n", hmac_alg);
-		return PTR_ERR(sdesc);
-	}
-
-	ret = crypto_shash_setkey(hmacalg, key, keylen);
-	if (!ret)
-		ret = crypto_shash_digest(&sdesc->shash, buf, buflen, digest);
-	kfree(sdesc);
-	return ret;
-}
-
-static int calc_hash(u8 *digest, const u8 *buf, unsigned int buflen)
-{
-	struct sdesc *sdesc;
-	int ret;
-
-	sdesc = alloc_sdesc(hashalg);
-	if (IS_ERR(sdesc)) {
-		pr_info("encrypted_key: can't alloc %s\n", hash_alg);
-		return PTR_ERR(sdesc);
-	}
-
-	ret = crypto_shash_digest(&sdesc->shash, buf, buflen, digest);
-	kfree(sdesc);
-	return ret;
-}
-
-enum derived_key_type { ENC_KEY, AUTH_KEY };
-
-/* Derive authentication/encryption key from trusted key */
-static int get_derived_key(u8 *derived_key, enum derived_key_type key_type,
-			   const u8 *master_key, size_t master_keylen)
-{
-	u8 *derived_buf;
-	unsigned int derived_buf_len;
-	int ret;
-
-	derived_buf_len = strlen("AUTH_KEY") + 1 + master_keylen;
-	if (derived_buf_len < HASH_SIZE)
-		derived_buf_len = HASH_SIZE;
-
-	derived_buf = kzalloc(derived_buf_len, GFP_KERNEL);
-	if (!derived_buf) {
-		pr_err("encrypted_key: out of memory\n");
-		return -ENOMEM;
-	}
-	if (key_type)
-		strcpy(derived_buf, "AUTH_KEY");
-	else
-		strcpy(derived_buf, "ENC_KEY");
-
-	memcpy(derived_buf + strlen(derived_buf) + 1, master_key,
-	       master_keylen);
-	ret = calc_hash(derived_key, derived_buf, derived_buf_len);
-	kfree(derived_buf);
-	return ret;
-}
-
-static int init_blkcipher_desc(struct blkcipher_desc *desc, const u8 *key,
-			       unsigned int key_len, const u8 *iv,
-			       unsigned int ivsize)
-{
-	int ret;
-
-	desc->tfm = crypto_alloc_blkcipher(blkcipher_alg, 0, CRYPTO_ALG_ASYNC);
-	if (IS_ERR(desc->tfm)) {
-		pr_err("encrypted_key: failed to load %s transform (%ld)\n",
-		       blkcipher_alg, PTR_ERR(desc->tfm));
-		return PTR_ERR(desc->tfm);
-	}
-	desc->flags = 0;
-
-	ret = crypto_blkcipher_setkey(desc->tfm, key, key_len);
-	if (ret < 0) {
-		pr_err("encrypted_key: failed to setkey (%d)\n", ret);
-		crypto_free_blkcipher(desc->tfm);
-		return ret;
-	}
-	crypto_blkcipher_set_iv(desc->tfm, iv, ivsize);
-	return 0;
-}
-
-static struct key *request_master_key(struct encrypted_key_payload *epayload,
-				      u8 **master_key, size_t *master_keylen)
-{
-	struct key *mkey = NULL;
-
-	if (!strncmp(epayload->master_desc, KEY_TRUSTED_PREFIX,
-		     KEY_TRUSTED_PREFIX_LEN)) {
-		mkey = request_trusted_key(epayload->master_desc +
-					   KEY_TRUSTED_PREFIX_LEN,
-					   master_key, master_keylen);
-	} else if (!strncmp(epayload->master_desc, KEY_USER_PREFIX,
-			    KEY_USER_PREFIX_LEN)) {
-		mkey = request_user_key(epayload->master_desc +
-					KEY_USER_PREFIX_LEN,
-					master_key, master_keylen);
-	} else
-		goto out;
-
-	if (IS_ERR(mkey)) {
-		pr_info("encrypted_key: key %s not found",
-			epayload->master_desc);
-		goto out;
-	}
-
-	dump_master_key(*master_key, *master_keylen);
-out:
-	return mkey;
-}
-
-/* Before returning data to userspace, encrypt decrypted data. */
-static int derived_key_encrypt(struct encrypted_key_payload *epayload,
-			       const u8 *derived_key,
-			       unsigned int derived_keylen)
-{
-	struct scatterlist sg_in[2];
-	struct scatterlist sg_out[1];
-	struct blkcipher_desc desc;
-	unsigned int encrypted_datalen;
-	unsigned int padlen;
-	char pad[16];
-	int ret;
-
-	encrypted_datalen = roundup(epayload->decrypted_datalen, blksize);
-	padlen = encrypted_datalen - epayload->decrypted_datalen;
-
-	ret = init_blkcipher_desc(&desc, derived_key, derived_keylen,
-				  epayload->iv, ivsize);
-	if (ret < 0)
-		goto out;
-	dump_decrypted_data(epayload);
-
-	memset(pad, 0, sizeof pad);
-	sg_init_table(sg_in, 2);
-	sg_set_buf(&sg_in[0], epayload->decrypted_data,
-		   epayload->decrypted_datalen);
-	sg_set_buf(&sg_in[1], pad, padlen);
-
-	sg_init_table(sg_out, 1);
-	sg_set_buf(sg_out, epayload->encrypted_data, encrypted_datalen);
-
-	ret = crypto_blkcipher_encrypt(&desc, sg_out, sg_in, encrypted_datalen);
-	crypto_free_blkcipher(desc.tfm);
-	if (ret < 0)
-		pr_err("encrypted_key: failed to encrypt (%d)\n", ret);
-	else
-		dump_encrypted_data(epayload, encrypted_datalen);
-out:
-	return ret;
-}
-
-static int datablob_hmac_append(struct encrypted_key_payload *epayload,
-				const u8 *master_key, size_t master_keylen)
-{
-	u8 derived_key[HASH_SIZE];
-	u8 *digest;
-	int ret;
-
-	ret = get_derived_key(derived_key, AUTH_KEY, master_key, master_keylen);
-	if (ret < 0)
-		goto out;
-
-	digest = epayload->format + epayload->datablob_len;
-	ret = calc_hmac(digest, derived_key, sizeof derived_key,
-			epayload->format, epayload->datablob_len);
-	if (!ret)
-		dump_hmac(NULL, digest, HASH_SIZE);
-out:
-	return ret;
-}
-
-/* verify HMAC before decrypting encrypted key */
-static int datablob_hmac_verify(struct encrypted_key_payload *epayload,
-				const u8 *format, const u8 *master_key,
-				size_t master_keylen)
-{
-	u8 derived_key[HASH_SIZE];
-	u8 digest[HASH_SIZE];
-	int ret;
-	char *p;
-	unsigned short len;
-
-	ret = get_derived_key(derived_key, AUTH_KEY, master_key, master_keylen);
-	if (ret < 0)
-		goto out;
-
-	len = epayload->datablob_len;
-	if (!format) {
-		p = epayload->master_desc;
-		len -= strlen(epayload->format) + 1;
-	} else
-		p = epayload->format;
-
-	ret = calc_hmac(digest, derived_key, sizeof derived_key, p, len);
-	if (ret < 0)
-		goto out;
-	ret = memcmp(digest, epayload->format + epayload->datablob_len,
-		     sizeof digest);
-	if (ret) {
-		ret = -EINVAL;
-		dump_hmac("datablob",
-			  epayload->format + epayload->datablob_len,
-			  HASH_SIZE);
-		dump_hmac("calc", digest, HASH_SIZE);
-	}
-out:
-	return ret;
-}
-
-static int derived_key_decrypt(struct encrypted_key_payload *epayload,
-			       const u8 *derived_key,
-			       unsigned int derived_keylen)
-{
-	struct scatterlist sg_in[1];
-	struct scatterlist sg_out[2];
-	struct blkcipher_desc desc;
-	unsigned int encrypted_datalen;
-	char pad[16];
-	int ret;
-
-	encrypted_datalen = roundup(epayload->decrypted_datalen, blksize);
-	ret = init_blkcipher_desc(&desc, derived_key, derived_keylen,
-				  epayload->iv, ivsize);
-	if (ret < 0)
-		goto out;
-	dump_encrypted_data(epayload, encrypted_datalen);
-
-	memset(pad, 0, sizeof pad);
-	sg_init_table(sg_in, 1);
-	sg_init_table(sg_out, 2);
-	sg_set_buf(sg_in, epayload->encrypted_data, encrypted_datalen);
-	sg_set_buf(&sg_out[0], epayload->decrypted_data,
-		   epayload->decrypted_datalen);
-	sg_set_buf(&sg_out[1], pad, sizeof pad);
-
-	ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in, encrypted_datalen);
-	crypto_free_blkcipher(desc.tfm);
-	if (ret < 0)
-		goto out;
-	dump_decrypted_data(epayload);
-out:
-	return ret;
-}
-
-/* Allocate memory for decrypted key and datablob. */
-static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
-							 const char *format,
-							 const char *master_desc,
-							 const char *datalen)
-{
-	struct encrypted_key_payload *epayload = NULL;
-	unsigned short datablob_len;
-	unsigned short decrypted_datalen;
-	unsigned short payload_datalen;
-	unsigned int encrypted_datalen;
-	unsigned int format_len;
-	long dlen;
-	int ret;
-
-	ret = strict_strtol(datalen, 10, &dlen);
-	if (ret < 0 || dlen < MIN_DATA_SIZE || dlen > MAX_DATA_SIZE)
-		return ERR_PTR(-EINVAL);
-
-	format_len = (!format) ? strlen(key_format_default) : strlen(format);
-	decrypted_datalen = dlen;
-	payload_datalen = decrypted_datalen;
-	if (format && !strcmp(format, key_format_ecryptfs)) {
-		if (dlen != ECRYPTFS_MAX_KEY_BYTES) {
-			pr_err("encrypted_key: keylen for the ecryptfs format "
-			       "must be equal to %d bytes\n",
-			       ECRYPTFS_MAX_KEY_BYTES);
-			return ERR_PTR(-EINVAL);
-		}
-		decrypted_datalen = ECRYPTFS_MAX_KEY_BYTES;
-		payload_datalen = sizeof(struct ecryptfs_auth_tok);
-	}
-
-	encrypted_datalen = roundup(decrypted_datalen, blksize);
-
-	datablob_len = format_len + 1 + strlen(master_desc) + 1
-	    + strlen(datalen) + 1 + ivsize + 1 + encrypted_datalen;
-
-	ret = key_payload_reserve(key, payload_datalen + datablob_len
-				  + HASH_SIZE + 1);
-	if (ret < 0)
-		return ERR_PTR(ret);
-
-	epayload = kzalloc(sizeof(*epayload) + payload_datalen +
-			   datablob_len + HASH_SIZE + 1, GFP_KERNEL);
-	if (!epayload)
-		return ERR_PTR(-ENOMEM);
-
-	epayload->payload_datalen = payload_datalen;
-	epayload->decrypted_datalen = decrypted_datalen;
-	epayload->datablob_len = datablob_len;
-	return epayload;
-}
-
-static int encrypted_key_decrypt(struct encrypted_key_payload *epayload,
-				 const char *format, const char *hex_encoded_iv)
-{
-	struct key *mkey;
-	u8 derived_key[HASH_SIZE];
-	u8 *master_key;
-	u8 *hmac;
-	const char *hex_encoded_data;
-	unsigned int encrypted_datalen;
-	size_t master_keylen;
-	size_t asciilen;
-	int ret;
-
-	encrypted_datalen = roundup(epayload->decrypted_datalen, blksize);
-	asciilen = (ivsize + 1 + encrypted_datalen + HASH_SIZE) * 2;
-	if (strlen(hex_encoded_iv) != asciilen)
-		return -EINVAL;
-
-	hex_encoded_data = hex_encoded_iv + (2 * ivsize) + 2;
-	hex2bin(epayload->iv, hex_encoded_iv, ivsize);
-	hex2bin(epayload->encrypted_data, hex_encoded_data, encrypted_datalen);
-
-	hmac = epayload->format + epayload->datablob_len;
-	hex2bin(hmac, hex_encoded_data + (encrypted_datalen * 2), HASH_SIZE);
-
-	mkey = request_master_key(epayload, &master_key, &master_keylen);
-	if (IS_ERR(mkey))
-		return PTR_ERR(mkey);
-
-	ret = datablob_hmac_verify(epayload, format, master_key, master_keylen);
-	if (ret < 0) {
-		pr_err("encrypted_key: bad hmac (%d)\n", ret);
-		goto out;
-	}
-
-	ret = get_derived_key(derived_key, ENC_KEY, master_key, master_keylen);
-	if (ret < 0)
-		goto out;
-
-	ret = derived_key_decrypt(epayload, derived_key, sizeof derived_key);
-	if (ret < 0)
-		pr_err("encrypted_key: failed to decrypt key (%d)\n", ret);
-out:
-	up_read(&mkey->sem);
-	key_put(mkey);
-	return ret;
-}
-
-static void __ekey_init(struct encrypted_key_payload *epayload,
-			const char *format, const char *master_desc,
-			const char *datalen)
-{
-	unsigned int format_len;
-
-	format_len = (!format) ? strlen(key_format_default) : strlen(format);
-	epayload->format = epayload->payload_data + epayload->payload_datalen;
-	epayload->master_desc = epayload->format + format_len + 1;
-	epayload->datalen = epayload->master_desc + strlen(master_desc) + 1;
-	epayload->iv = epayload->datalen + strlen(datalen) + 1;
-	epayload->encrypted_data = epayload->iv + ivsize + 1;
-	epayload->decrypted_data = epayload->payload_data;
-
-	if (!format)
-		memcpy(epayload->format, key_format_default, format_len);
-	else {
-		if (!strcmp(format, key_format_ecryptfs))
-			epayload->decrypted_data =
-				ecryptfs_get_auth_tok_key((struct ecryptfs_auth_tok *)epayload->payload_data);
-
-		memcpy(epayload->format, format, format_len);
-	}
-
-	memcpy(epayload->master_desc, master_desc, strlen(master_desc));
-	memcpy(epayload->datalen, datalen, strlen(datalen));
-}
-
-/*
- * encrypted_init - initialize an encrypted key
- *
- * For a new key, use a random number for both the iv and data
- * itself.  For an old key, decrypt the hex encoded data.
- */
-static int encrypted_init(struct encrypted_key_payload *epayload,
-			  const char *key_desc, const char *format,
-			  const char *master_desc, const char *datalen,
-			  const char *hex_encoded_iv)
-{
-	int ret = 0;
-
-	if (format && !strcmp(format, key_format_ecryptfs)) {
-		ret = valid_ecryptfs_desc(key_desc);
-		if (ret < 0)
-			return ret;
-
-		ecryptfs_fill_auth_tok((struct ecryptfs_auth_tok *)epayload->payload_data,
-				       key_desc);
-	}
-
-	__ekey_init(epayload, format, master_desc, datalen);
-	if (!hex_encoded_iv) {
-		get_random_bytes(epayload->iv, ivsize);
-
-		get_random_bytes(epayload->decrypted_data,
-				 epayload->decrypted_datalen);
-	} else
-		ret = encrypted_key_decrypt(epayload, format, hex_encoded_iv);
-	return ret;
-}
-
-/*
- * encrypted_instantiate - instantiate an encrypted key
- *
- * Decrypt an existing encrypted datablob or create a new encrypted key
- * based on a kernel random number.
- *
- * On success, return 0. Otherwise return errno.
- */
-static int encrypted_instantiate(struct key *key, const void *data,
-				 size_t datalen)
-{
-	struct encrypted_key_payload *epayload = NULL;
-	char *datablob = NULL;
-	const char *format = NULL;
-	char *master_desc = NULL;
-	char *decrypted_datalen = NULL;
-	char *hex_encoded_iv = NULL;
-	int ret;
-
-	if (datalen <= 0 || datalen > 32767 || !data)
-		return -EINVAL;
-
-	datablob = kmalloc(datalen + 1, GFP_KERNEL);
-	if (!datablob)
-		return -ENOMEM;
-	datablob[datalen] = 0;
-	memcpy(datablob, data, datalen);
-	ret = datablob_parse(datablob, &format, &master_desc,
-			     &decrypted_datalen, &hex_encoded_iv);
-	if (ret < 0)
-		goto out;
-
-	epayload = encrypted_key_alloc(key, format, master_desc,
-				       decrypted_datalen);
-	if (IS_ERR(epayload)) {
-		ret = PTR_ERR(epayload);
-		goto out;
-	}
-	ret = encrypted_init(epayload, key->description, format, master_desc,
-			     decrypted_datalen, hex_encoded_iv);
-	if (ret < 0) {
-		kfree(epayload);
-		goto out;
-	}
-
-	rcu_assign_pointer(key->payload.data, epayload);
-out:
-	kfree(datablob);
-	return ret;
-}
-
-static void encrypted_rcu_free(struct rcu_head *rcu)
-{
-	struct encrypted_key_payload *epayload;
-
-	epayload = container_of(rcu, struct encrypted_key_payload, rcu);
-	memset(epayload->decrypted_data, 0, epayload->decrypted_datalen);
-	kfree(epayload);
-}
-
-/*
- * encrypted_update - update the master key description
- *
- * Change the master key description for an existing encrypted key.
- * The next read will return an encrypted datablob using the new
- * master key description.
- *
- * On success, return 0. Otherwise return errno.
- */
-static int encrypted_update(struct key *key, const void *data, size_t datalen)
-{
-	struct encrypted_key_payload *epayload = key->payload.data;
-	struct encrypted_key_payload *new_epayload;
-	char *buf;
-	char *new_master_desc = NULL;
-	const char *format = NULL;
-	int ret = 0;
-
-	if (datalen <= 0 || datalen > 32767 || !data)
-		return -EINVAL;
-
-	buf = kmalloc(datalen + 1, GFP_KERNEL);
-	if (!buf)
-		return -ENOMEM;
-
-	buf[datalen] = 0;
-	memcpy(buf, data, datalen);
-	ret = datablob_parse(buf, &format, &new_master_desc, NULL, NULL);
-	if (ret < 0)
-		goto out;
-
-	ret = valid_master_desc(new_master_desc, epayload->master_desc);
-	if (ret < 0)
-		goto out;
-
-	new_epayload = encrypted_key_alloc(key, epayload->format,
-					   new_master_desc, epayload->datalen);
-	if (IS_ERR(new_epayload)) {
-		ret = PTR_ERR(new_epayload);
-		goto out;
-	}
-
-	__ekey_init(new_epayload, epayload->format, new_master_desc,
-		    epayload->datalen);
-
-	memcpy(new_epayload->iv, epayload->iv, ivsize);
-	memcpy(new_epayload->payload_data, epayload->payload_data,
-	       epayload->payload_datalen);
-
-	rcu_assign_pointer(key->payload.data, new_epayload);
-	call_rcu(&epayload->rcu, encrypted_rcu_free);
-out:
-	kfree(buf);
-	return ret;
-}
-
-/*
- * encrypted_read - format and copy the encrypted data to userspace
- *
- * The resulting datablob format is:
- * <master-key name> <decrypted data length> <encrypted iv> <encrypted data>
- *
- * On success, return to userspace the encrypted key datablob size.
- */
-static long encrypted_read(const struct key *key, char __user *buffer,
-			   size_t buflen)
-{
-	struct encrypted_key_payload *epayload;
-	struct key *mkey;
-	u8 *master_key;
-	size_t master_keylen;
-	char derived_key[HASH_SIZE];
-	char *ascii_buf;
-	size_t asciiblob_len;
-	int ret;
-
-	epayload = rcu_dereference_key(key);
-
-	/* returns the hex encoded iv, encrypted-data, and hmac as ascii */
-	asciiblob_len = epayload->datablob_len + ivsize + 1
-	    + roundup(epayload->decrypted_datalen, blksize)
-	    + (HASH_SIZE * 2);
-
-	if (!buffer || buflen < asciiblob_len)
-		return asciiblob_len;
-
-	mkey = request_master_key(epayload, &master_key, &master_keylen);
-	if (IS_ERR(mkey))
-		return PTR_ERR(mkey);
-
-	ret = get_derived_key(derived_key, ENC_KEY, master_key, master_keylen);
-	if (ret < 0)
-		goto out;
-
-	ret = derived_key_encrypt(epayload, derived_key, sizeof derived_key);
-	if (ret < 0)
-		goto out;
-
-	ret = datablob_hmac_append(epayload, master_key, master_keylen);
-	if (ret < 0)
-		goto out;
-
-	ascii_buf = datablob_format(epayload, asciiblob_len);
-	if (!ascii_buf) {
-		ret = -ENOMEM;
-		goto out;
-	}
-
-	up_read(&mkey->sem);
-	key_put(mkey);
-
-	if (copy_to_user(buffer, ascii_buf, asciiblob_len) != 0)
-		ret = -EFAULT;
-	kfree(ascii_buf);
-
-	return asciiblob_len;
-out:
-	up_read(&mkey->sem);
-	key_put(mkey);
-	return ret;
-}
-
-/*
- * encrypted_destroy - before freeing the key, clear the decrypted data
- *
- * Before freeing the key, clear the memory containing the decrypted
- * key data.
- */
-static void encrypted_destroy(struct key *key)
-{
-	struct encrypted_key_payload *epayload = key->payload.data;
-
-	if (!epayload)
-		return;
-
-	memset(epayload->decrypted_data, 0, epayload->decrypted_datalen);
-	kfree(key->payload.data);
-}
-
-struct key_type key_type_encrypted = {
-	.name = "encrypted",
-	.instantiate = encrypted_instantiate,
-	.update = encrypted_update,
-	.match = user_match,
-	.destroy = encrypted_destroy,
-	.describe = user_describe,
-	.read = encrypted_read,
-};
-EXPORT_SYMBOL_GPL(key_type_encrypted);
-
-static void encrypted_shash_release(void)
-{
-	if (hashalg)
-		crypto_free_shash(hashalg);
-	if (hmacalg)
-		crypto_free_shash(hmacalg);
-}
-
-static int __init encrypted_shash_alloc(void)
-{
-	int ret;
-
-	hmacalg = crypto_alloc_shash(hmac_alg, 0, CRYPTO_ALG_ASYNC);
-	if (IS_ERR(hmacalg)) {
-		pr_info("encrypted_key: could not allocate crypto %s\n",
-			hmac_alg);
-		return PTR_ERR(hmacalg);
-	}
-
-	hashalg = crypto_alloc_shash(hash_alg, 0, CRYPTO_ALG_ASYNC);
-	if (IS_ERR(hashalg)) {
-		pr_info("encrypted_key: could not allocate crypto %s\n",
-			hash_alg);
-		ret = PTR_ERR(hashalg);
-		goto hashalg_fail;
-	}
-
-	return 0;
-
-hashalg_fail:
-	crypto_free_shash(hmacalg);
-	return ret;
-}
-
-static int __init init_encrypted(void)
-{
-	int ret;
-
-	ret = encrypted_shash_alloc();
-	if (ret < 0)
-		return ret;
-	ret = register_key_type(&key_type_encrypted);
-	if (ret < 0)
-		goto out;
-	return aes_get_sizes();
-out:
-	encrypted_shash_release();
-	return ret;
-
-}
-
-static void __exit cleanup_encrypted(void)
-{
-	encrypted_shash_release();
-	unregister_key_type(&key_type_encrypted);
-}
-
-late_initcall(init_encrypted);
-module_exit(cleanup_encrypted);
-
-MODULE_LICENSE("GPL");
diff --git a/security/keys/encrypted.h b/security/keys/encrypted.h
deleted file mode 100644
index cef5e2f..0000000
--- a/security/keys/encrypted.h
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifndef __ENCRYPTED_KEY_H
-#define __ENCRYPTED_KEY_H
-
-#define ENCRYPTED_DEBUG 0
-
-#if ENCRYPTED_DEBUG
-static inline void dump_master_key(const u8 *master_key, size_t master_keylen)
-{
-	print_hex_dump(KERN_ERR, "master key: ", DUMP_PREFIX_NONE, 32, 1,
-		       master_key, master_keylen, 0);
-}
-
-static inline void dump_decrypted_data(struct encrypted_key_payload *epayload)
-{
-	print_hex_dump(KERN_ERR, "decrypted data: ", DUMP_PREFIX_NONE, 32, 1,
-		       epayload->decrypted_data,
-		       epayload->decrypted_datalen, 0);
-}
-
-static inline void dump_encrypted_data(struct encrypted_key_payload *epayload,
-				       unsigned int encrypted_datalen)
-{
-	print_hex_dump(KERN_ERR, "encrypted data: ", DUMP_PREFIX_NONE, 32, 1,
-		       epayload->encrypted_data, encrypted_datalen, 0);
-}
-
-static inline void dump_hmac(const char *str, const u8 *digest,
-			     unsigned int hmac_size)
-{
-	if (str)
-		pr_info("encrypted_key: %s", str);
-	print_hex_dump(KERN_ERR, "hmac: ", DUMP_PREFIX_NONE, 32, 1, digest,
-		       hmac_size, 0);
-}
-#else
-static inline void dump_master_key(const u8 *master_key, size_t master_keylen)
-{
-}
-
-static inline void dump_decrypted_data(struct encrypted_key_payload *epayload)
-{
-}
-
-static inline void dump_encrypted_data(struct encrypted_key_payload *epayload,
-				       unsigned int encrypted_datalen)
-{
-}
-
-static inline void dump_hmac(const char *str, const u8 *digest,
-			     unsigned int hmac_size)
-{
-}
-#endif
-#endif
-- 
1.7.3.4


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* [PATCH 2/2] evm: remove TCG_TPM dependency
  2011-08-29 13:08 [PATCH 1/2] encrypted-keys: remove trusted-keys dependency Mimi Zohar
@ 2011-08-29 13:08 ` Mimi Zohar
  0 siblings, 0 replies; 2+ messages in thread
From: Mimi Zohar @ 2011-08-29 13:08 UTC (permalink / raw)
  To: linux-security-module
  Cc: Mimi Zohar, James Morris, LKML, Randy Dunlap, Arnaud Lacombe,
	Stephen Rothwell, David Safford, Mimi Zohar

All tristates selected by EVM(boolean) are forced to be builtin, except
in the TCG_TPM(tristate) dependency case. Arnaud Lacombe summarizes the
Kconfig bug as, "So it would seem direct dependency state influence the
state of reverse dependencies.."  For a detailed explanation, refer to
Arnaud Lacombe's posting http://lkml.org/lkml/2011/8/23/498.

With the "encrypted-keys: remove trusted-keys dependency" patch, EVM
can now be built without a dependency on TCG_TPM.  The trusted-keys
dependency requires trusted-keys to either be builtin or not selected.
This dependency will prevent the boolean/tristate mismatch from
occuring.

Reported-by: Stephen Rothwell <sfr@canb.auug.org.au>,
             Randy Dunlap <rdunlap@xenotimenet>
Signed-off-by: Mimi Zohar <zohar@us.ibm.com>
---
 security/integrity/evm/Kconfig |    3 +--
 1 files changed, 1 insertions(+), 2 deletions(-)

diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig
index 884617d..afbb59d 100644
--- a/security/integrity/evm/Kconfig
+++ b/security/integrity/evm/Kconfig
@@ -1,11 +1,10 @@
 config EVM
 	boolean "EVM support"
-	depends on SECURITY && KEYS && TCG_TPM
+	depends on SECURITY && KEYS && (TRUSTED_KEYS=y || TRUSTED_KEYS=n)
 	select CRYPTO_HMAC
 	select CRYPTO_MD5
 	select CRYPTO_SHA1
 	select ENCRYPTED_KEYS
-	select TRUSTED_KEYS
 	default n
 	help
 	  EVM protects a file's security extended attributes against
-- 
1.7.3.4


^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2011-08-29 13:08 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-08-29 13:08 [PATCH 1/2] encrypted-keys: remove trusted-keys dependency Mimi Zohar
2011-08-29 13:08 ` [PATCH 2/2] evm: remove TCG_TPM dependency Mimi Zohar

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.