2010-09-28 18:36:46

by Mimi Zohar

[permalink] [raw]
Subject: [RFC][PATCH 0/4] keys: trusted and encrypted keys

Sorry for the duplicate posting. Am cc'ing the crypto mailing
list as well.

Trusted and Encrypted Keys are two new key types added to the
existing kernel key ring service. Both of these new types are
variable length symmetic keys, and in both cases all keys are
created in the kernel, and user space sees, stores, and loads
only encrypted blobs. Trusted Keys require the availability of a
Trusted Platform Module (TPM) chip for greater security, while
Encrypted Keys can be used on any system. All user level blobs,
are displayed and loaded in hex ascii for convenience, and
are integrity verified.

Trusted Keys use a TPM both to generate and to seal the keys.
Keys are sealed under a 2048 bit RSA key in the TPM, and optionally
sealed to specified PCR (integrity measurement) values, and only
unsealed by the TPM, if PCRs and blob integrity verifications match.
A loaded Trusted Key can be updated with new (future) PCR values,
so keys are easily migrated to new pcr values, such as when the
kernel and initramfs are updated. The same key can have many
saved blobs under different PCR values, so multiple boots are
easily supported.

Trusted Keys are sealed under the SRK, which must have the default
authorization value (20 zeros). This can be set at takeownership
time with the trouser's utility "tpm_takeownership -u -z".

Usage:
keyctl add trusted name "NEW keylen [hex_pcrinfo]" ring
keyctl add trusted name "LOAD hex_blob" ring
keyctl update key "UPDATE hex_pcrinfo"
keyctl print keyid
keyctl pipe keyid > filename

The key length for new keys are always in bytes.
Trusted Keys can be 32 - 128 bytes (256 - 1024 bits), the upper
limit is to fit within the 2048 bit SRK (RSA) keylength, with
all necessary structure/padding. The keywords NEW, LOAD, and
UPDATE can be all upper or all lower case.

Encrypted keys do not depend on a TPM, and are faster, as they
use AES for encryption/decryption. New keys are created from kernel
generated random numbers, and are encrypted/decrypted using a
specified 'master' key. The 'master' key can either be a trusted-key
or user-key type. The main disadvantage of encrypted keys is that if
they are not rooted in a trusted key, they are only as secure as the
user key encrypting them. The master user key should therefore
be loaded in as secure a way as possible, preferably early in
boot.

Usage:
keyctl add encrypted name "NEW master-key-name keylen" ring
keyctl add encrypted name "LOAD master-key-name keylen hex_blob" ring
keyctl update keyid "UPDATE master-key-name"

The initial consumer of trusted keys is EVM, which at boot time
needs a high quality symmetric key for HMAC protection of file
metadata. The use of a trusted key provides strong guarantees
that the EVM key has not been compromised by a user level problem,
and when sealed to specific boot PCR values, protects against
boot and offline attacks. Other uses for trusted and encrypted
keys, such as for disk and file encryption are anticipated.

Mimi Zohar
Dave Safford

Mimi Zohar (4):
lib: hex2bin converts ascii hexadecimal string to binary
key: add tpm_send command
keys: add new trusted key-type
keys: add new key-type encrypted

drivers/char/tpm/tpm.c | 17 +
include/keys/encrypted-type.h | 30 ++
include/keys/trusted-type.h | 33 ++
include/linux/kernel.h | 1 +
include/linux/tpm.h | 3 +
lib/hexdump.c | 16 +
security/Kconfig | 33 ++
security/keys/Makefile | 2 +
security/keys/encrypted_defined.c | 781 +++++++++++++++++++++++++++++
security/keys/encrypted_defined.h | 52 ++
security/keys/trusted_defined.c | 997 +++++++++++++++++++++++++++++++++++++
security/keys/trusted_defined.h | 125 +++++
12 files changed, 2090 insertions(+), 0 deletions(-)
create mode 100644 include/keys/encrypted-type.h
create mode 100644 include/keys/trusted-type.h
create mode 100644 security/keys/encrypted_defined.c
create mode 100644 security/keys/encrypted_defined.h
create mode 100644 security/keys/trusted_defined.c
create mode 100644 security/keys/trusted_defined.h

--
1.7.2.2


2010-09-28 18:36:45

by Mimi Zohar

[permalink] [raw]
Subject: [RFC][PATCH 1/4] lib: hex2bin converts ascii hexadecimal string to binary

Similar to the kgdb_hex2mem() code, hex2bin converts a string
to binary using the hex_to_bin() library call.

Signed-off-by: Mimi Zohar <[email protected]>
---
include/linux/kernel.h | 1 +
lib/hexdump.c | 16 ++++++++++++++++
2 files changed, 17 insertions(+), 0 deletions(-)

diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 2b0a35e..69ddea8 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -396,6 +396,7 @@ static inline char *pack_hex_byte(char *buf, u8 byte)
}

extern int hex_to_bin(char ch);
+extern void hex2bin(unsigned char *mem, char *buf, int count);

#ifndef pr_fmt
#define pr_fmt(fmt) fmt
diff --git a/lib/hexdump.c b/lib/hexdump.c
index 5d7a480..66f96bb 100644
--- a/lib/hexdump.c
+++ b/lib/hexdump.c
@@ -34,6 +34,22 @@ int hex_to_bin(char ch)
EXPORT_SYMBOL(hex_to_bin);

/**
+ * hex2bin - convert an ascii hexadecimal string to its binary representation
+ * @mem: result
+ * @buf: ascii hexadecimal string
+ * @count: result length
+ */
+void hex2bin(unsigned char *mem, char *buf, int count)
+{
+ while (count--) {
+ *mem = hex_to_bin(*buf++) << 4;
+ *mem += hex_to_bin(*buf++);
+ mem++;
+ }
+}
+EXPORT_SYMBOL(hex2bin);
+
+/**
* hex_dump_to_buffer - convert a blob of data to "hex ASCII" in memory
* @buf: data blob to dump
* @len: number of bytes in the @buf
--
1.7.2.2

2010-09-28 18:36:31

by Mimi Zohar

[permalink] [raw]
Subject: [RFC][PATCH 2/4] key: add tpm_send command

Add internal kernel tpm_send() command used to seal/unseal keys.

Signed-off-by: David Safford <[email protected]>
Reviewd-by: Mimi Zohar <[email protected]>
---
drivers/char/tpm/tpm.c | 17 +++++++++++++++++
include/linux/tpm.h | 3 +++
2 files changed, 20 insertions(+), 0 deletions(-)

diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c
index 05ad4a1..37c16a8 100644
--- a/drivers/char/tpm/tpm.c
+++ b/drivers/char/tpm/tpm.c
@@ -770,6 +770,23 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
}
EXPORT_SYMBOL_GPL(tpm_pcr_extend);

+int tpm_send(u32 chip_num, char *cmd, int buflen)
+{
+ struct tpm_chip *chip;
+ int rc;
+
+ chip = tpm_chip_find_get(chip_num);
+ if (chip == NULL)
+ return -ENODEV;
+
+ rc = transmit_cmd(chip, (struct tpm_cmd_t *)cmd, buflen,
+ "attempting tpm_cmd");
+
+ module_put(chip->dev->driver->owner);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_send);
+
ssize_t tpm_show_pcrs(struct device *dev, struct device_attribute *attr,
char *buf)
{
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index ac5d1c1..a0ecaa9 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -31,6 +31,7 @@

extern int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf);
extern int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash);
+extern int tpm_send(u32 chip_num, char *cmd, int buflen);
#else
static inline int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf) {
return -ENODEV;
@@ -38,5 +39,7 @@ static inline int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf) {
static inline int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) {
return -ENODEV;
}
+static inline int tpm_send(u32 chip_num, char *cmd, int buflen) {
+ return -ENODEV;
#endif
#endif
--
1.7.2.2


2010-09-28 18:36:32

by Mimi Zohar

[permalink] [raw]
Subject: [RFC][PATCH 3/4] keys: add new trusted key-type

Defines a new kernel key-type called 'trusted'. Trusted keys are TPM
generated random numbers, RSA sealed by the TPM, and only unsealed by
the TPM, if boot PCRs and other criteria match. Trusted keys are
created/encrypted/decrypted in the kernel. Userspace ever only
sees/stores encrypted blobs.

Signed-off-by: David Safford <[email protected]>
Signed-off-by: Mimi Zohar <[email protected]>
---
include/keys/trusted-type.h | 33 ++
security/Kconfig | 16 +
security/keys/Makefile | 1 +
security/keys/trusted_defined.c | 997 +++++++++++++++++++++++++++++++++++++++
security/keys/trusted_defined.h | 125 +++++
5 files changed, 1172 insertions(+), 0 deletions(-)
create mode 100644 include/keys/trusted-type.h
create mode 100644 security/keys/trusted_defined.c
create mode 100644 security/keys/trusted_defined.h

diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h
new file mode 100644
index 0000000..be7d44e
--- /dev/null
+++ b/include/keys/trusted-type.h
@@ -0,0 +1,33 @@
+/* trusted-type.h: trusted-defined key type
+ *
+ * Copyright (C) 2010 IBM Corporation
+ * Author: David Safford <[email protected]>
+ *
+ * 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_TRUSTED_TYPE_H
+#define _KEYS_TRUSTED_TYPE_H
+
+#include <linux/key.h>
+#include <linux/rcupdate.h>
+
+#define MAX_KEY_SIZE 128
+#define MAX_BLOB_SIZE 320
+#define MAX_PCRINFO_SIZE 64
+
+struct trusted_key_payload {
+ struct rcu_head rcu; /* RCU destructor */
+ unsigned int key_len;
+ unsigned int blob_len;
+ unsigned int pcrinfo_len;
+ unsigned char key[MAX_KEY_SIZE];
+ unsigned char blob[MAX_BLOB_SIZE];
+ unsigned char pcrinfo[MAX_PCRINFO_SIZE];
+};
+
+extern struct key_type key_type_trusted;
+
+#endif /* _KEYS_TRUSTED_TYPE_H */
diff --git a/security/Kconfig b/security/Kconfig
index bd72ae6..f9681e5 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -21,6 +21,22 @@ config KEYS

If you are unsure as to whether this is required, answer N.

+config TRUSTED_KEYS
+ tristate "TRUSTED KEYS"
+ depends on KEYS && TCG_TPM
+ select CRYPTO
+ select CRYPTO_HMAC
+ select CRYPTO_SHA1
+ select LIBCRC32C
+ help
+ This option provides support for creating/sealing/unsealing keys
+ in the kernel. Trusted keys are TPM generated random numbers
+ symmetric keys, RSA sealed by the TPM, and only unsealed by the
+ TPM, if boot PCRs and other criteria match. Userspace ever only
+ sees/stores encrypted blobs.
+
+ If you are unsure as to whether this is required, answer N.
+
config KEYS_DEBUG_PROC_KEYS
bool "Enable the /proc/keys file by which keys may be viewed"
depends on KEYS
diff --git a/security/keys/Makefile b/security/keys/Makefile
index 74d5447..fcb1070 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -13,6 +13,7 @@ obj-y := \
request_key_auth.o \
user_defined.o

+obj-$(CONFIG_TRUSTED_KEYS) += trusted_defined.o
obj-$(CONFIG_KEYS_COMPAT) += compat.o
obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_SYSCTL) += sysctl.o
diff --git a/security/keys/trusted_defined.c b/security/keys/trusted_defined.c
new file mode 100644
index 0000000..aedad16
--- /dev/null
+++ b/security/keys/trusted_defined.c
@@ -0,0 +1,997 @@
+/*
+ * Copyright (C) 2010 IBM Corporation
+ *
+ * Author:
+ * David Safford <[email protected]>
+ *
+ * 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.
+ *
+ * File: trusted_defined.c
+ *
+ * Defines a new kernel key-type called 'trusted'. Trusted keys are
+ * TPM generated random numbers, RSA sealed by the TPM, and only unsealed
+ * by the TPM, if boot PCRs and other criteria match. Trusted keys are
+ * created/sealed/unsealed in the kernel. Userspace ever only sees/stores
+ * encrypted blobs.
+ *
+ * Keys are sealed under the SRK, which must have the default
+ * authorization value (20 zeros). This can be set at takeownership
+ * time with the trouser's utility "tpm_takeownership -u -z".
+ *
+ * Usage:
+ * keyctl add trusted name "NEW keylen [hex_pcrinfo]" ring
+ * keyctl add trusted name "LOAD hex_blob" ring
+ * keyctl update key "UPDATE hex_pcrinfo"
+ * keyctl print keyid
+ * keys can be 32 - 128 bytes, blob max is 1024 hex ascii characters
+ * binary pcrinfo max is 512 hex ascii characters
+ */
+
+#include <linux/uaccess.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/parser.h>
+#include <linux/string.h>
+#include <keys/user-type.h>
+#include <keys/trusted-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/tpm.h>
+
+#include "trusted_defined.h"
+
+static char hmac_alg[] = "hmac(sha1)";
+static char hash_alg[] = "sha1";
+
+static int init_sha1_desc(struct hash_desc *desc)
+{
+ int rc;
+
+ desc->tfm = crypto_alloc_hash(hash_alg, 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(desc->tfm)) {
+ rc = PTR_ERR(desc->tfm);
+ return rc;
+ }
+ desc->flags = 0;
+ rc = crypto_hash_init(desc);
+ if (rc)
+ crypto_free_hash(desc->tfm);
+ return rc;
+}
+
+static int init_hmac_desc(struct hash_desc *desc, unsigned char *key,
+ int keylen)
+{
+ int rc;
+
+ desc->tfm = crypto_alloc_hash(hmac_alg, 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(desc->tfm)) {
+ rc = PTR_ERR(desc->tfm);
+ return rc;
+ }
+ desc->flags = 0;
+ crypto_hash_setkey(desc->tfm, key, keylen);
+ rc = crypto_hash_init(desc);
+ if (rc)
+ crypto_free_hash(desc->tfm);
+ return rc;
+}
+
+static int TSS_sha1(unsigned char *data, int datalen, unsigned char *digest)
+{
+ struct hash_desc desc;
+ struct scatterlist sg[1];
+ int rc;
+
+ rc = init_sha1_desc(&desc);
+ if (rc != 0)
+ return rc;
+
+ sg_init_one(sg, data, datalen);
+ crypto_hash_update(&desc, sg, datalen);
+ crypto_hash_final(&desc, digest);
+ crypto_free_hash(desc.tfm);
+ return rc;
+}
+
+static int TSS_rawhmac(unsigned char *digest, unsigned char *key,
+ unsigned int keylen, ...)
+{
+ struct hash_desc desc;
+ struct scatterlist sg[1];
+ unsigned int dlen;
+ unsigned char *data;
+ va_list argp;
+ int error;
+
+ error = init_hmac_desc(&desc, key, keylen);
+ if (error)
+ return error;
+
+ va_start(argp, keylen);
+ for (;;) {
+ dlen = (unsigned int)va_arg(argp, unsigned int);
+ if (dlen == 0)
+ break;
+ data = (unsigned char *)va_arg(argp, unsigned char *);
+ if (data == NULL)
+ return -1;
+ sg_init_one(sg, data, dlen);
+ crypto_hash_update(&desc, sg, dlen);
+ }
+ crypto_hash_final(&desc, digest);
+ crypto_free_hash(desc.tfm);
+
+ va_end(argp);
+ return 0;
+}
+
+/*
+ * calculate authorization info fields to send to TPM
+ */
+static uint32_t TSS_authhmac(unsigned char *digest, unsigned char *key,
+ unsigned int keylen, unsigned char *h1,
+ unsigned char *h2, unsigned char h3, ...)
+{
+ unsigned char paramdigest[TPM_HASH_SIZE];
+ struct hash_desc desc;
+ struct scatterlist sg[1];
+ unsigned int dlen;
+ unsigned char *data;
+ unsigned char c;
+ int rc;
+
+ va_list argp;
+
+ rc = init_sha1_desc(&desc);
+ if (rc != 0)
+ return rc;
+ c = h3;
+ va_start(argp, h3);
+ for (;;) {
+ dlen = (unsigned int)va_arg(argp, unsigned int);
+ if (dlen == 0)
+ break;
+ data = (unsigned char *)va_arg(argp, unsigned char *);
+ sg_init_one(sg, data, dlen);
+ rc = crypto_hash_update(&desc, sg, dlen);
+ }
+ va_end(argp);
+ rc = crypto_hash_final(&desc, paramdigest);
+ crypto_free_hash(desc.tfm);
+ TSS_rawhmac(digest, key, keylen, TPM_HASH_SIZE, paramdigest,
+ TPM_NONCE_SIZE, h1, TPM_NONCE_SIZE, h2, 1, &c, 0, 0);
+ return 0;
+}
+
+/*
+ * verify the AUTH1_COMMAND (Seal) result from TPM
+ */
+static uint32_t TSS_checkhmac1(unsigned char *buffer, uint32_t command,
+ unsigned char *ononce, unsigned char *key,
+ unsigned int keylen, ...)
+{
+ uint32_t bufsize;
+ uint16_t tag;
+ uint32_t ordinal;
+ uint32_t result;
+ unsigned char *enonce;
+ unsigned char *continueflag;
+ unsigned char *authdata;
+ unsigned char testhmac[TPM_HASH_SIZE];
+ unsigned char paramdigest[TPM_HASH_SIZE];
+ struct hash_desc desc;
+ struct scatterlist sg[1];
+ unsigned int dlen;
+ unsigned int dpos;
+ va_list argp;
+ int rc;
+
+ bufsize = LOAD32(buffer, TPM_SIZE_OFFSET);
+ tag = LOAD16(buffer, 0);
+ ordinal = command;
+ result = LOAD32N(buffer, TPM_RETURN_OFFSET);
+
+ if (tag == TPM_TAG_RSP_COMMAND)
+ return 0;
+ if (tag != TPM_TAG_RSP_AUTH1_COMMAND)
+ return -1;
+ authdata = buffer + bufsize - TPM_HASH_SIZE;
+ continueflag = authdata - 1;
+ enonce = continueflag - TPM_NONCE_SIZE;
+ rc = init_sha1_desc(&desc);
+ if (rc != 0)
+ return rc;
+ sg_init_one(sg, &result, TPM_U32_SIZE);
+ crypto_hash_update(&desc, sg, TPM_U32_SIZE);
+ sg_init_one(sg, &ordinal, TPM_U32_SIZE);
+ crypto_hash_update(&desc, sg, TPM_U32_SIZE);
+ va_start(argp, keylen);
+ for (;;) {
+ dlen = (unsigned int)va_arg(argp, unsigned int);
+ if (dlen == 0)
+ break;
+ dpos = (unsigned int)va_arg(argp, unsigned int);
+ sg_init_one(sg, buffer + dpos, dlen);
+ crypto_hash_update(&desc, sg, dlen);
+ }
+ va_end(argp);
+ crypto_hash_final(&desc, paramdigest);
+ crypto_free_hash(desc.tfm);
+
+ TSS_rawhmac(testhmac, key, keylen, TPM_HASH_SIZE, paramdigest,
+ TPM_NONCE_SIZE, enonce,
+ TPM_NONCE_SIZE, ononce, 1, continueflag, 0, 0);
+ if (memcmp(testhmac, authdata, TPM_HASH_SIZE) != 0)
+ return -1;
+ return 0;
+}
+
+/*
+ * verify the AUTH2_COMMAND (unseal) result from TPM
+ */
+static uint32_t TSS_checkhmac2(unsigned char *buffer, uint32_t command,
+ unsigned char *ononce, unsigned char *key1,
+ unsigned int keylen1, unsigned char *key2,
+ unsigned int keylen2, ...)
+{
+ uint32_t bufsize;
+ uint16_t tag;
+ uint32_t ordinal;
+ uint32_t result;
+ unsigned char *enonce1;
+ unsigned char *continueflag1;
+ unsigned char *authdata1;
+ unsigned char *enonce2;
+ unsigned char *continueflag2;
+ unsigned char *authdata2;
+ unsigned char testhmac1[TPM_HASH_SIZE];
+ unsigned char testhmac2[TPM_HASH_SIZE];
+ unsigned char paramdigest[TPM_HASH_SIZE];
+ struct hash_desc desc;
+ struct scatterlist sg[1];
+ unsigned int dlen;
+ unsigned int dpos;
+ va_list argp;
+ int rc;
+
+ bufsize = LOAD32(buffer, TPM_SIZE_OFFSET);
+ tag = LOAD16(buffer, 0);
+ ordinal = command;
+ result = LOAD32N(buffer, TPM_RETURN_OFFSET);
+
+ if (tag == TPM_TAG_RSP_COMMAND)
+ return 0;
+ if (tag != TPM_TAG_RSP_AUTH2_COMMAND)
+ return -1;
+ authdata1 = buffer + bufsize - (TPM_HASH_SIZE + 1
+ + TPM_HASH_SIZE + TPM_HASH_SIZE);
+ authdata2 = buffer + bufsize - (TPM_HASH_SIZE);
+ continueflag1 = authdata1 - 1;
+ continueflag2 = authdata2 - 1;
+ enonce1 = continueflag1 - TPM_NONCE_SIZE;
+ enonce2 = continueflag2 - TPM_NONCE_SIZE;
+
+ rc = init_sha1_desc(&desc);
+ if (rc != 0)
+ return rc;
+ sg_init_one(sg, &result, TPM_U32_SIZE);
+ crypto_hash_update(&desc, sg, TPM_U32_SIZE);
+ sg_init_one(sg, &ordinal, TPM_U32_SIZE);
+ crypto_hash_update(&desc, sg, TPM_U32_SIZE);
+
+ va_start(argp, keylen2);
+ for (;;) {
+ dlen = (unsigned int)va_arg(argp, unsigned int);
+ if (dlen == 0)
+ break;
+ dpos = (unsigned int)va_arg(argp, unsigned int);
+ sg_init_one(sg, buffer + dpos, dlen);
+ crypto_hash_update(&desc, sg, dlen);
+ }
+ crypto_hash_final(&desc, paramdigest);
+ crypto_free_hash(desc.tfm);
+
+ TSS_rawhmac(testhmac1, key1, keylen1, TPM_HASH_SIZE, paramdigest,
+ TPM_NONCE_SIZE, enonce1,
+ TPM_NONCE_SIZE, ononce, 1, continueflag1, 0, 0);
+ TSS_rawhmac(testhmac2, key2, keylen2, TPM_HASH_SIZE, paramdigest,
+ TPM_NONCE_SIZE, enonce2,
+ TPM_NONCE_SIZE, ononce, 1, continueflag2, 0, 0);
+ if (memcmp(testhmac1, authdata1, TPM_HASH_SIZE) != 0)
+ return -1;
+ if (memcmp(testhmac2, authdata2, TPM_HASH_SIZE) != 0)
+ return -1;
+ return 0;
+}
+
+static int trusted_tpm_send(u32 chip_num, unsigned char *cmd, int buflen)
+{
+ int rc;
+
+ dump_tpm_buf(cmd);
+ rc = tpm_send(chip_num, cmd, buflen);
+ dump_tpm_buf(cmd);
+ if (rc > 0)
+ /* Can't return positive return codes values to keyctl */
+ rc = -EPERM;
+ return rc;
+}
+
+static int tpm_reset(struct tpm_buf *tb)
+{
+ INIT_BUF(tb);
+ store16(tb, TPM_TAG_RQU_COMMAND);
+ store32(tb, TPM_RESET_SIZE);
+ store32(tb, TPM_ORD_RESET);
+ return trusted_tpm_send(TPM_ANY_NUM, tb->data, TPM_MAX_BUF_SIZE);
+}
+
+/*
+ * use the TPM random number generator for all new keys and nonces
+ */
+static int tpm_get_random(struct tpm_buf *tb, unsigned char *buf, uint32_t len)
+{
+ int ret = 0;
+
+ INIT_BUF(tb);
+ store16(tb, TPM_TAG_RQU_COMMAND);
+ store32(tb, TPM_GETRANDOM_SIZE);
+ store32(tb, TPM_ORD_GETRANDOM);
+ store32(tb, len);
+ ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, TPM_MAX_BUF_SIZE);
+ memcpy(buf, tb->data + TPM_GETRANDOM_RETURN, len);
+ return ret;
+}
+
+/*
+ * Create an object specific authorisation protocol (OSAP) session
+ */
+static int srkosap(struct tpm_buf *tb, struct osapsess *s)
+{
+ unsigned char enonce[TPM_NONCE_SIZE];
+ unsigned char ononce[TPM_NONCE_SIZE];
+ unsigned char key[TPM_HASH_SIZE];
+ int ret;
+
+ ret = tpm_get_random(tb, ononce, TPM_NONCE_SIZE);
+ if (ret < 0)
+ return ret;
+
+ INIT_BUF(tb);
+ store16(tb, TPM_TAG_RQU_COMMAND);
+ store32(tb, TPM_OSAP_SIZE);
+ store32(tb, TPM_ORD_OSAP);
+ store16(tb, SRKKEYTYPE);
+ store32(tb, SRKHANDLE);
+ storebytes(tb, ononce, TPM_NONCE_SIZE);
+
+ ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, TPM_MAX_BUF_SIZE);
+ if (ret < 0)
+ return ret;
+
+ memset(key, 0, TPM_HASH_SIZE);
+ s->handle = LOAD32(tb->data, TPM_DATA_OFFSET);
+ memcpy(s->enonce, &(tb->data[TPM_DATA_OFFSET + TPM_U32_SIZE]),
+ TPM_NONCE_SIZE);
+ memcpy(enonce, &(tb->data[TPM_DATA_OFFSET + TPM_U32_SIZE +
+ TPM_NONCE_SIZE]), TPM_NONCE_SIZE);
+ ret = TSS_rawhmac(s->secret, key, TPM_HASH_SIZE, TPM_NONCE_SIZE,
+ enonce, TPM_NONCE_SIZE, ononce, 0, 0);
+ return ret;
+}
+
+/*
+ * Create an object independent authorisation protocol (oiap) session
+ */
+static int oiap(struct tpm_buf *tb, uint32_t * handle, unsigned char *nonce)
+{
+ int ret;
+
+ INIT_BUF(tb);
+ store16(tb, TPM_TAG_RQU_COMMAND);
+ store32(tb, TPM_OIAP_SIZE);
+ store32(tb, TPM_ORD_OIAP);
+ ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, TPM_MAX_BUF_SIZE);
+ if (ret < 0)
+ return ret;
+
+ *handle = LOAD32(tb->data, TPM_DATA_OFFSET);
+ memcpy(nonce, &tb->data[TPM_DATA_OFFSET + TPM_U32_SIZE],
+ TPM_NONCE_SIZE);
+ return ret;
+}
+
+/*
+ * Have the TPM seal(encrypt) the trusted key, possibly based on
+ * Platform Configuration Registers (PCRs).
+ */
+static int srkseal(struct tpm_buf *tb,
+ unsigned char *data, int datalen,
+ unsigned char *blob, unsigned int *bloblen,
+ unsigned char *pcrinfo, uint32_t pcrinfosize)
+{
+ struct osapsess sess;
+ unsigned char encauth[TPM_HASH_SIZE];
+ unsigned char pubauth[TPM_HASH_SIZE];
+ unsigned char xorwork[TPM_HASH_SIZE * 2];
+ unsigned char xorhash[TPM_HASH_SIZE];
+ unsigned char dummyauth[TPM_HASH_SIZE];
+ unsigned char nonceodd[TPM_NONCE_SIZE];
+ unsigned char cont;
+ uint32_t ordinal;
+ uint32_t pcrsize;
+ uint32_t datsize;
+ int sealinfosize;
+ int encdatasize;
+ int storedsize;
+ int ret;
+ int i;
+
+ memset(dummyauth, 0, sizeof dummyauth);
+
+ ret = srkosap(tb, &sess);
+ if (ret < 0)
+ return ret;
+ dump_sess(&sess);
+
+ /* calculate encrypted authorization value */
+ memcpy(xorwork, sess.secret, TPM_HASH_SIZE);
+ memcpy(xorwork + TPM_HASH_SIZE, sess.enonce, TPM_HASH_SIZE);
+ ret = TSS_sha1(xorwork, TPM_HASH_SIZE * 2, xorhash);
+ if (ret != 0)
+ return ret;
+
+ ret = tpm_get_random(tb, nonceodd, TPM_NONCE_SIZE);
+ if (ret < 0)
+ return ret;
+ ordinal = htonl(TPM_ORD_SEAL);
+ datsize = htonl(datalen);
+ pcrsize = htonl(pcrinfosize);
+ cont = 0;
+
+ /* encrypt data authorization key */
+ for (i = 0; i < TPM_HASH_SIZE; ++i)
+ encauth[i] = xorhash[i] ^ dummyauth[i];
+
+ /* calculate authorization HMAC value */
+ if (pcrinfosize == 0) {
+ /* no pcr info specified */
+ TSS_authhmac(pubauth, sess.secret, TPM_HASH_SIZE,
+ sess.enonce, nonceodd, cont, TPM_U32_SIZE,
+ &ordinal, TPM_HASH_SIZE, encauth,
+ TPM_U32_SIZE, &pcrsize, TPM_U32_SIZE,
+ &datsize, datalen, data, 0, 0);
+ } else {
+ /* pcr info specified */
+ TSS_authhmac(pubauth, sess.secret, TPM_HASH_SIZE,
+ sess.enonce, nonceodd, cont, TPM_U32_SIZE,
+ &ordinal, TPM_HASH_SIZE, encauth,
+ TPM_U32_SIZE, &pcrsize, pcrinfosize,
+ pcrinfo, TPM_U32_SIZE, &datsize, datalen,
+ data, 0, 0);
+ }
+
+ INIT_BUF(tb);
+ store16(tb, TPM_TAG_RQU_AUTH1_COMMAND);
+ store32(tb, TPM_SEAL_SIZE + pcrinfosize + datalen);
+ store32(tb, TPM_ORD_SEAL);
+ store32(tb, SRKHANDLE);
+ storebytes(tb, encauth, TPM_HASH_SIZE);
+ store32(tb, pcrinfosize);
+ storebytes(tb, pcrinfo, pcrinfosize);
+ store32(tb, datalen);
+ storebytes(tb, data, datalen);
+ store32(tb, sess.handle);
+ storebytes(tb, nonceodd, TPM_NONCE_SIZE);
+ store8(tb, cont);
+ storebytes(tb, pubauth, TPM_HASH_SIZE);
+
+ ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, TPM_MAX_BUF_SIZE);
+ if (ret < 0)
+ return ret;
+
+ /* calculate the size of the returned Blob */
+ sealinfosize = LOAD32(tb->data, TPM_DATA_OFFSET + TPM_U32_SIZE);
+ encdatasize = LOAD32(tb->data, TPM_DATA_OFFSET + TPM_U32_SIZE +
+ TPM_U32_SIZE + sealinfosize);
+ storedsize = TPM_U32_SIZE + TPM_U32_SIZE + sealinfosize +
+ TPM_U32_SIZE + encdatasize;
+
+ /* check the HMAC in the response */
+ ret = TSS_checkhmac1(tb->data, ordinal, nonceodd, sess.secret,
+ TPM_HASH_SIZE, storedsize, TPM_DATA_OFFSET, 0, 0);
+
+ /* copy the returned blob to caller */
+ memcpy(blob, tb->data + TPM_DATA_OFFSET, storedsize);
+ *bloblen = storedsize;
+ return ret;
+}
+
+/*
+ * use the AUTH2_COMMAND form of unseal, to authorize both SRK and blob
+ */
+static int srkunseal(struct tpm_buf *tb,
+ unsigned char *blob, int bloblen,
+ unsigned char *data, unsigned int *datalen)
+{
+ unsigned char nonceodd[TPM_NONCE_SIZE];
+ unsigned char dummyauth[TPM_NONCE_SIZE];
+ unsigned char enonce1[TPM_NONCE_SIZE];
+ unsigned char enonce2[TPM_NONCE_SIZE];
+ unsigned char authdata1[TPM_HASH_SIZE];
+ unsigned char authdata2[TPM_HASH_SIZE];
+ uint32_t authhandle1 = 0;
+ uint32_t authhandle2 = 0;
+ unsigned char cont = 0;
+ uint32_t ordinal;
+ uint32_t keyhndl;
+ int ret;
+
+ memset(dummyauth, 0, sizeof dummyauth);
+
+ ret = oiap(tb, &authhandle1, enonce1);
+ if (ret < 0) {
+ pr_info("trusted_key: oiap failed (%d)\n", ret);
+ return ret;
+ }
+ ret = oiap(tb, &authhandle2, enonce2);
+ if (ret < 0) {
+ pr_info("trusted_key: oiap failed (%d)\n", ret);
+ return ret;
+ }
+
+ /* network order for HMAC */
+ ordinal = htonl(TPM_ORD_UNSEAL);
+ keyhndl = htonl(SRKHANDLE);
+ ret = tpm_get_random(tb, nonceodd, TPM_NONCE_SIZE);
+ if (ret < 0) {
+ pr_info("trusted_key: tpm_get_random failed (%d)\n", ret);
+ return ret;
+ }
+
+ TSS_authhmac(authdata1, dummyauth, TPM_NONCE_SIZE,
+ enonce1, nonceodd, cont, TPM_U32_SIZE,
+ &ordinal, bloblen, blob, 0, 0);
+ TSS_authhmac(authdata2, dummyauth, TPM_NONCE_SIZE,
+ enonce2, nonceodd, cont, TPM_U32_SIZE,
+ &ordinal, bloblen, blob, 0, 0);
+
+ INIT_BUF(tb);
+ store16(tb, TPM_TAG_RQU_AUTH2_COMMAND);
+ store32(tb, TPM_UNSEAL_SIZE + bloblen);
+ store32(tb, TPM_ORD_UNSEAL);
+ store32(tb, SRKHANDLE);
+ storebytes(tb, blob, bloblen);
+ store32(tb, authhandle1);
+ storebytes(tb, nonceodd, TPM_NONCE_SIZE);
+ store8(tb, cont);
+ storebytes(tb, authdata1, TPM_HASH_SIZE);
+ store32(tb, authhandle2);
+ storebytes(tb, nonceodd, TPM_NONCE_SIZE);
+ store8(tb, cont);
+ storebytes(tb, authdata2, TPM_HASH_SIZE);
+
+ ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, TPM_MAX_BUF_SIZE);
+ if (ret < 0) {
+ pr_info("trusted_key: authhmac failed (%d)\n", ret);
+ return ret;
+ }
+
+ *datalen = LOAD32(tb->data, TPM_DATA_OFFSET);
+ ret = TSS_checkhmac2(tb->data, ordinal, nonceodd,
+ dummyauth, TPM_HASH_SIZE,
+ dummyauth, TPM_HASH_SIZE,
+ TPM_U32_SIZE, TPM_DATA_OFFSET,
+ *datalen, TPM_DATA_OFFSET + TPM_U32_SIZE, 0, 0);
+ if (ret < 0)
+ pr_info("trusted_key: TSS_checkhmac2 failed (%d)\n", ret);
+ memcpy(data, tb->data + TPM_DATA_OFFSET + TPM_U32_SIZE, *datalen);
+ return ret;
+}
+
+/*
+ * Create a new trusted key based on a TPM random number
+ */
+static int key_create(struct trusted_key_payload *p)
+{
+ struct tpm_buf *tb;
+ int ret;
+
+ tb = kzalloc(sizeof *tb, GFP_KERNEL);
+ if (!tb)
+ return -ENOMEM;
+
+ ret = tpm_get_random(tb, p->key, p->key_len);
+ kfree(tb);
+ return ret;
+}
+
+/*
+ * Have the TPM seal(encrypt) the symmetric key
+ */
+static int key_seal(struct trusted_key_payload *p)
+{
+ struct tpm_buf *tb;
+ int ret;
+
+ tb = kzalloc(sizeof *tb, GFP_KERNEL);
+ if (!tb)
+ return -ENOMEM;
+
+ ret = tpm_reset(tb);
+ if (ret < 0) {
+ pr_info("trusted_key: tpm_reset failed (%d)\n", ret);
+ goto out;
+ }
+ ret = srkseal(tb, p->key, p->key_len, p->blob,
+ &p->blob_len, p->pcrinfo, p->pcrinfo_len);
+ if (ret < 0) {
+ pr_info("trusted_key: srkseal failed (%d)\n", ret);
+ goto out;
+ }
+
+out:
+ kfree(tb);
+ return ret;
+}
+
+/*
+ * Have the TPM unseal(decrypt) the symmetric key
+ */
+static int key_unseal(struct trusted_key_payload *p)
+{
+ struct tpm_buf *tb;
+ int ret;
+
+ tb = kzalloc(sizeof *tb, GFP_KERNEL);
+ if (!tb)
+ return -ENOMEM;
+
+ ret = tpm_reset(tb);
+ if (ret < 0) {
+ pr_info("trusted_key: tpm_reset failed (%d)\n", ret);
+ goto out;
+ }
+ ret = srkunseal(tb, p->blob, p->blob_len, p->key, &p->key_len);
+ if (ret < 0)
+ pr_info("trusted_key: srkunseal failed (%d)\n", ret);
+out:
+ kfree(tb);
+ return ret;
+}
+
+enum {
+ Opt_err = -1,
+ Opt_new = 1, Opt_load, Opt_update,
+ Opt_NEW, Opt_LOAD, Opt_UPDATE
+};
+
+static match_table_t key_tokens = {
+ {Opt_new, "new"},
+ {Opt_NEW, "NEW"},
+ {Opt_load, "load"},
+ {Opt_LOAD, "LOAD"},
+ {Opt_update, "update"},
+ {Opt_UPDATE, "UPDATE"},
+ {Opt_err, NULL}
+};
+
+/*
+ * datablob_parse - parse the keyctl data
+ *
+ * datablob format:
+ * "NEW keylen [hex_pcrinfo]"
+ * "LOAD hex_blob"
+ * "UPDATE hex_pcrinfo"
+ *
+ * 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, char **keylen, char **pcrinfo,
+ char **hex_encoded_data)
+{
+ substring_t args[MAX_OPT_ARGS];
+ int ret = -EINVAL;
+ int key_cmd;
+ char *p;
+
+ p = strsep(&datablob, " \t");
+ if (!p)
+ return ret;
+ key_cmd = match_token(p, key_tokens, args);
+
+ /* All key commands require at least one argument */
+ p = strsep(&datablob, " \t");
+ if (!p)
+ return ret;
+
+ switch (key_cmd) {
+ case Opt_new:
+ case Opt_NEW:
+ *keylen = p;
+ ret = 0;
+ p = strsep(&datablob, " \t");
+ if (p)
+ *pcrinfo = p;
+ break;
+ case Opt_load:
+ case Opt_LOAD:
+ *hex_encoded_data = p;
+ ret = 0;
+ break;
+ case Opt_update:
+ case Opt_UPDATE:
+ *pcrinfo = p;
+ ret = 0;
+ break;
+ case Opt_err:
+ break;
+ }
+ return ret;
+}
+
+/*
+ * Allocate payload.
+ */
+static struct trusted_key_payload *trusted_key_alloc(struct key *key)
+{
+ struct trusted_key_payload *p = NULL;
+ int ret;
+
+ ret = key_payload_reserve(key, sizeof *p);
+ if (ret < 0)
+ return p;
+ p = kzalloc(sizeof *p, GFP_KERNEL);
+ return p;
+}
+
+/*
+ * safety check user inputs and fill in payload
+ */
+static int trusted_key_fill(struct trusted_key_payload *p, char *asc_keylen,
+ char *hex_pcrinfo, char *hex_blob)
+{
+ long key_len;
+ int ret;
+
+ if (hex_blob) {
+ p->blob_len = strlen(hex_blob) / 2;
+ if (p->blob_len > MAX_BLOB_SIZE)
+ return -EINVAL;
+ hex2bin(p->blob, hex_blob, p->blob_len);
+ }
+ if (asc_keylen) {
+ ret = strict_strtol(asc_keylen, 10, &key_len);
+ if ((ret < 0) || (key_len < MIN_KEY_SIZE) ||
+ (key_len > MAX_KEY_SIZE))
+ return -EINVAL;
+ p->key_len = key_len;
+ }
+ if (hex_pcrinfo) {
+ p->pcrinfo_len = strlen(hex_pcrinfo) / 2;
+ if (p->pcrinfo_len > MAX_PCRINFO_SIZE)
+ return -EINVAL;
+ hex2bin(p->pcrinfo, hex_pcrinfo, p->pcrinfo_len);
+ }
+ return 0;
+}
+
+/*
+ * trusted_instantiate - create a new trusted key
+ *
+ * Unseal an existing trusted blob or, for a new key, get a
+ * random key, then seal and create an encryted key-type key,
+ * adding it to the specified keyring.
+ *
+ * On success, return 0. Otherwise return errno.
+ */
+static int trusted_instantiate(struct key *key, const void *data,
+ size_t datalen)
+{
+ struct trusted_key_payload *p = NULL;
+ char *hex_blob = NULL;
+ char *asc_keylen = NULL;
+ char *datablob;
+ char *hex_pcrinfo = NULL;
+ int ret;
+
+ if (datalen <= 0 || datalen > 32767 || !data)
+ return -EINVAL;
+
+ datablob = kzalloc(datalen + 1, GFP_KERNEL);
+ if (!datablob)
+ return -ENOMEM;
+ memcpy(datablob, data, datalen);
+
+ ret = datablob_parse(datablob, &asc_keylen, &hex_pcrinfo, &hex_blob);
+ if (ret < 0)
+ goto out;
+ p = trusted_key_alloc(key);
+ if (!p) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ ret = trusted_key_fill(p, asc_keylen, hex_pcrinfo, hex_blob);
+ if (ret < 0)
+ goto out;
+
+ dump_payload(p);
+
+ if (hex_blob) {
+ ret = key_unseal(p);
+ if (ret < 0)
+ pr_info("trusted_key: key_unseal failed (%d)\n", ret);
+ goto out;
+ }
+
+ if (asc_keylen) {
+ ret = key_create(p);
+ if (ret < 0) {
+ pr_info("trusted_key: key_create failed (%d)\n", ret);
+ goto out;
+ }
+ }
+
+ ret = key_seal(p);
+ if (ret < 0)
+ pr_info("trusted_key: key_seal failed (%d)\n", ret);
+out:
+ if (!ret)
+ rcu_assign_pointer(key->payload.data, p);
+ kfree(datablob);
+ return ret;
+}
+
+static void trusted_rcu_free(struct rcu_head *rcu)
+{
+ struct trusted_key_payload *p;
+
+ p = container_of(rcu, struct trusted_key_payload, rcu);
+ memset(p->key, 0, p->key_len);
+ kfree(p);
+}
+
+/*
+ * trusted_update - reseal an existing key with new PCR values
+ */
+static int trusted_update(struct key *key, const void *data, size_t datalen)
+{
+ struct trusted_key_payload *p = key->payload.data;
+ struct trusted_key_payload *new_p;
+ char *hex_blob = NULL;
+ char *asc_keylen = NULL;
+ char *datablob;
+ char *hex_pcrinfo = NULL;
+ int ret = 0;
+
+ if (datalen <= 0 || datalen > 32767 || !data)
+ return -EINVAL;
+
+ datablob = kzalloc(datalen + 1, GFP_KERNEL);
+ if (!datablob)
+ return -ENOMEM;
+ memcpy(datablob, data, datalen);
+ ret = datablob_parse(datablob, &asc_keylen, &hex_pcrinfo, &hex_blob);
+ if (ret < 0)
+ goto out;
+
+ new_p = trusted_key_alloc(key);
+ if (!new_p) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ ret = trusted_key_fill(new_p, asc_keylen, hex_pcrinfo, hex_blob);
+ if (ret < 0) {
+ pr_info("trusted_key: key_fill failed (%d)\n", ret);
+ kfree(new_p);
+ goto out;
+ }
+
+ /* copy old key values, and reseal with new pcrs */
+ new_p->key_len = p->key_len;
+ memcpy(new_p->key, p->key, p->key_len);
+ dump_payload(p);
+ dump_payload(new_p);
+
+ ret = key_seal(new_p);
+ if (ret < 0) {
+ pr_info("trusted_key: key_seal failed (%d)\n", ret);
+ kfree(new_p);
+ goto out;
+ }
+ rcu_assign_pointer(key->payload.data, new_p);
+ call_rcu(&p->rcu, trusted_rcu_free);
+out:
+ kfree(datablob);
+ return ret;
+}
+
+/*
+ * trusted_read - copy the sealed blob data to userspace in hex.
+ * On success, return to userspace the trusted key datablob size.
+ */
+static long trusted_read(const struct key *key, char __user * buffer,
+ size_t buflen)
+{
+ struct trusted_key_payload *p;
+ char *ascii_buf;
+ char *bufp;
+ int i;
+
+ p = rcu_dereference_protected(key->payload.data,
+ rwsem_is_locked(&((struct key *)key)->sem));
+ if (!p)
+ return -EINVAL;
+
+ if (!buffer || buflen <= 0)
+ return 2 * p->blob_len;
+
+ ascii_buf = kzalloc(2 * p->blob_len, GFP_KERNEL);
+ if (!ascii_buf)
+ return -ENOMEM;
+
+ bufp = ascii_buf;
+ for (i = 0; i < p->blob_len; i++)
+ bufp = pack_hex_byte(bufp, p->blob[i]);
+
+ if ((copy_to_user(buffer, ascii_buf, 2 * p->blob_len)) != 0) {
+ kfree(ascii_buf);
+ return -EFAULT;
+ }
+ kfree(ascii_buf);
+ return 2 * p->blob_len;
+}
+
+/*
+ * trusted_destroy - before freeing the key, clear the decrypted data
+ */
+static void trusted_destroy(struct key *key)
+{
+ struct trusted_key_payload *p = key->payload.data;
+
+ if (!p)
+ return;
+ memset(p->key, 0, p->key_len);
+ kfree(key->payload.data);
+}
+
+struct key_type key_type_trusted = {
+ .name = "trusted",
+ .instantiate = trusted_instantiate,
+ .update = trusted_update,
+ .match = user_match,
+ .destroy = trusted_destroy,
+ .describe = user_describe,
+ .read = trusted_read,
+};
+EXPORT_SYMBOL_GPL(key_type_trusted);
+
+static int __init init_trusted(void)
+{
+ int ret;
+
+ ret = register_key_type(&key_type_trusted);
+ if (ret < 0)
+ return ret;
+ return ret;
+}
+
+static void __exit cleanup_trusted(void)
+{
+ unregister_key_type(&key_type_trusted);
+}
+
+module_init(init_trusted);
+module_exit(cleanup_trusted);
+
+MODULE_LICENSE("GPL");
diff --git a/security/keys/trusted_defined.h b/security/keys/trusted_defined.h
new file mode 100644
index 0000000..e2d0c11
--- /dev/null
+++ b/security/keys/trusted_defined.h
@@ -0,0 +1,125 @@
+#ifndef __TRUSTED_KEY_H
+#define __TRUSTED_KEY_H
+
+#define TPM_MAX_BUF_SIZE 512
+#define TPM_TAG_RQU_COMMAND 193
+#define TPM_TAG_RQU_AUTH1_COMMAND 194
+#define TPM_TAG_RQU_AUTH2_COMMAND 195
+#define TPM_TAG_RSP_COMMAND 196
+#define TPM_TAG_RSP_AUTH1_COMMAND 197
+#define TPM_TAG_RSP_AUTH2_COMMAND 198
+#define TPM_NONCE_SIZE 20
+#define TPM_HASH_SIZE 20
+#define TPM_SIZE_OFFSET 2
+#define TPM_RETURN_OFFSET 6
+#define TPM_DATA_OFFSET 10
+#define TPM_U32_SIZE 4
+#define TPM_GETRANDOM_SIZE 14
+#define TPM_GETRANDOM_RETURN 14
+#define TPM_ORD_GETRANDOM 70
+#define TPM_RESET_SIZE 10
+#define TPM_ORD_RESET 90
+#define TPM_OSAP_SIZE 36
+#define TPM_ORD_OSAP 11
+#define TPM_OIAP_SIZE 10
+#define TPM_ORD_OIAP 10
+#define TPM_SEAL_SIZE 87
+#define TPM_ORD_SEAL 23
+#define TPM_ORD_UNSEAL 24
+#define TPM_UNSEAL_SIZE 104
+#define SRKKEYTYPE 4
+#define SRKHANDLE 0x40000000
+#define TPM_ANY_NUM 0xFFFF
+
+#define MIN_KEY_SIZE 32
+
+#define LOAD32(buffer, offset) (ntohl(*(uint32_t *)&buffer[offset]))
+#define LOAD32N(buffer, offset) (*(uint32_t *)&buffer[offset])
+#define LOAD16(buffer, offset) (ntohs(*(uint16_t *)&buffer[offset]))
+
+struct tpm_buf {
+ int len;
+ unsigned char data[TPM_MAX_BUF_SIZE];
+};
+
+#define INIT_BUF(tb) (tb->len = 0)
+
+struct osapsess {
+ uint32_t handle;
+ unsigned char secret[TPM_HASH_SIZE];
+ unsigned char enonce[TPM_NONCE_SIZE];
+};
+
+#define TPM_DEBUG 0
+
+#if TPM_DEBUG
+static inline void dump_payload(struct trusted_key_payload *p)
+{
+ pr_info("trusted_key: key_len %d\n", p->key_len);
+ print_hex_dump(KERN_INFO, "key ", DUMP_PREFIX_NONE,
+ 16, 1, p->key, p->key_len, 0);
+ pr_info("trusted_key: pcrinfo %d\n", p->pcrinfo_len);
+ print_hex_dump(KERN_INFO, "pcrinfo ", DUMP_PREFIX_NONE,
+ 16, 1, p->pcrinfo, p->pcrinfo_len, 0);
+ pr_info("trusted_key: bloblen %d\n", p->blob_len);
+ print_hex_dump(KERN_INFO, "blob ", DUMP_PREFIX_NONE,
+ 16, 1, p->blob, p->blob_len, 0);
+}
+
+static inline void dump_sess(struct osapsess *s)
+{
+ print_hex_dump(KERN_INFO, "trusted-key: handle", DUMP_PREFIX_NONE,
+ 16, 1, &s->handle, 4, 0);
+ pr_info("trusted-key: secret:\n");
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE,
+ 16, 1, &s->secret, TPM_HASH_SIZE, 0);
+ pr_info("trusted-key: enonce:\n");
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE,
+ 16, 1, &s->enonce, TPM_HASH_SIZE, 0);
+}
+
+static inline void dump_tpm_buf(unsigned char *buf)
+{
+ int len;
+
+ pr_info("\ntrusted-key: tpm buffer\n");
+ len = LOAD32(buf, TPM_SIZE_OFFSET);
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, buf, len, 0);
+}
+#else
+static inline void dump_payload(struct trusted_key_payload *p)
+{
+}
+
+static inline void dump_sess(struct osapsess *s)
+{
+}
+
+static inline void dump_tpm_buf(unsigned char *buf)
+{
+}
+#endif
+
+static inline void store8(struct tpm_buf *buf, unsigned char value)
+{
+ buf->data[buf->len++] = value;
+}
+
+static inline void store16(struct tpm_buf *buf, uint16_t value)
+{
+ *(uint16_t *) & buf->data[buf->len] = htons(value);
+ buf->len += sizeof(value);
+}
+
+static inline void store32(struct tpm_buf *buf, uint32_t value)
+{
+ *(uint32_t *) & buf->data[buf->len] = htonl(value);
+ buf->len += sizeof(value);
+}
+
+static inline void storebytes(struct tpm_buf *buf, unsigned char *in, int len)
+{
+ memcpy(buf->data + buf->len, in, len);
+ buf->len += len;
+}
+#endif
--
1.7.2.2


2010-09-28 18:36:33

by Mimi Zohar

[permalink] [raw]
Subject: [RFC][PATCH 4/4] keys: add new key-type encrypted

Defines a new kernel key-type called 'encrypted'. Encrypted keys are
kernel generated random numbers, which are encrypted/decrypted with
a 'trusted' symmetric key. Encrypted keys are created/encrypted/decrypted
in the kernel. Userspace only ever sees/stores encrypted blobs.

Signed-off-by: Mimi Zohar <[email protected]>
Signed-off-by: David Safford <[email protected]>
---
include/keys/encrypted-type.h | 30 ++
security/Kconfig | 17 +
security/keys/Makefile | 1 +
security/keys/encrypted_defined.c | 781 +++++++++++++++++++++++++++++++++++++
security/keys/encrypted_defined.h | 52 +++
5 files changed, 881 insertions(+), 0 deletions(-)
create mode 100644 include/keys/encrypted-type.h
create mode 100644 security/keys/encrypted_defined.c
create mode 100644 security/keys/encrypted_defined.h

diff --git a/include/keys/encrypted-type.h b/include/keys/encrypted-type.h
new file mode 100644
index 0000000..e2312e0
--- /dev/null
+++ b/include/keys/encrypted-type.h
@@ -0,0 +1,30 @@
+/* encrypted-type.h: encrypted-defined key type
+ *
+ * Copyright (C) 2010 IBM Corporation
+ * Author: Mimi Zohar <[email protected]>
+ *
+ * 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_ENCRYPTED_TYPE_H
+#define _KEYS_ENCRYPTED_TYPE_H
+
+#include <linux/key.h>
+#include <linux/rcupdate.h>
+
+struct encrypted_key_payload {
+ struct rcu_head rcu; /* RCU destructor */
+ char *master_desc; /* datablob: master key name */
+ char *datalen; /* datablob: decrypted key length */
+ void *iv; /* datablob: iv */
+ void *encrypted_data; /* datablob: encrypted key */
+ unsigned short datablob_len; /* length of datablob */
+ unsigned short decrypted_datalen; /* decrypted data length */
+ char decrypted_data[0]; /* decrypted data + datablob + hmac */
+};
+
+extern struct key_type key_type_encrypted;
+
+#endif /* _KEYS_ENCRYPTED_TYPE_H */
diff --git a/security/Kconfig b/security/Kconfig
index f9681e5..b03187d 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -37,6 +37,23 @@ config TRUSTED_KEYS

If you are unsure as to whether this is required, answer N.

+config ENCRYPTED_KEYS
+ tristate "ENCRYPTED KEYS"
+ depends on KEYS && TRUSTED_KEYS
+ select LIBCRC32C
+ select CONFIG_CRYPTO_AES
+ select CRYPTO_HMAC
+ select CRYPTO_SHA256
+ select BLOCK_CBC
+ help
+ This option provides support for create/encrypting/decrypting keys
+ in the kernel. Encrypted keys are kernel generated random numbers,
+ which are encrypted/decrypted with a 'master' symmetric key. The
+ 'master' key can be either a trusted-key or user-key type.
+ Userspace only ever sees/stores encrypted blobs.
+
+ If you are unsure as to whether this is required, answer N.
+
config KEYS_DEBUG_PROC_KEYS
bool "Enable the /proc/keys file by which keys may be viewed"
depends on KEYS
diff --git a/security/keys/Makefile b/security/keys/Makefile
index fcb1070..6c94105 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -14,6 +14,7 @@ obj-y := \
user_defined.o

obj-$(CONFIG_TRUSTED_KEYS) += trusted_defined.o
+obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted_defined.o
obj-$(CONFIG_KEYS_COMPAT) += compat.o
obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_SYSCTL) += sysctl.o
diff --git a/security/keys/encrypted_defined.c b/security/keys/encrypted_defined.c
new file mode 100644
index 0000000..6b26db6
--- /dev/null
+++ b/security/keys/encrypted_defined.c
@@ -0,0 +1,781 @@
+/*
+ * Copyright (C) 2010 IBM Corporation
+ *
+ * Author:
+ * Mimi Zohar <[email protected]>
+ *
+ * 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.
+ *
+ * File: encrypted_defined.c
+ *
+ * Defines a new kernel key-type called 'encrypted'. Encrypted keys
+ * are kernel generated random numbers, which are encrypted/decrypted
+ * using a 'master' key. The 'master' key can either be a trusted-key or
+ * user-key type. Encrypted keys are created/encrypted/decrypted in the
+ * kernel. Userspace ever only sees/stores encrypted blobs.
+ *
+ * keyctl add "encrypted" "name" "NEW master-key-name keylen" ring
+ * keyctl add "encrypted" "name" "LOAD master-key-name keylen hex_blob" ring
+ * keyctl update keyid "UPDATE master-key-name"
+ */
+
+#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 <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 <crypto/sha.h>
+#include <crypto/aes.h>
+
+#include "encrypted_defined.h"
+
+static char hash_alg[] = "sha256";
+static char hmac_alg[] = "hmac(sha256)";
+static int hash_size = SHA256_DIGEST_SIZE;
+static char blkcipher_alg[] = "cbc(aes)";
+static int ivsize;
+static int blksize;
+
+static int aes_get_sizes(int *ivsize, int *blksize)
+{
+ 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;
+}
+
+enum {
+ Opt_err = -1, Opt_new = 1, Opt_load, Opt_NEW, Opt_LOAD
+};
+
+static match_table_t key_tokens = {
+ {Opt_new, "new"},
+ {Opt_NEW, "NEW"},
+ {Opt_load, "load"},
+ {Opt_LOAD, "LOAD"},
+ {Opt_err, NULL}
+};
+
+/*
+ * datablob_parse - parse the keyctl data
+ *
+ * datablob format:
+ * NEW <master-key name> <decrypted data length>
+ * LOAD <master-key name> <decrypted data length> <encrypted iv + data>
+ *
+ * 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, char **master_desc,
+ char **decrypted_datalen, char **hex_encoded_iv,
+ char **hex_encoded_data)
+{
+ substring_t args[MAX_OPT_ARGS];
+ int ret = -EINVAL;
+ int key_cmd;
+ char *p;
+
+ p = strsep(&datablob, " \t");
+ if (!p)
+ return ret;
+ key_cmd = match_token(p, key_tokens, args);
+
+ *master_desc = strsep(&datablob, " \t");
+ if (!*master_desc)
+ goto out;
+ *decrypted_datalen = strsep(&datablob, " \t");
+ if (!*decrypted_datalen)
+ goto out;
+
+ switch (key_cmd) {
+ case Opt_new:
+ case Opt_NEW:
+ ret = 0;
+ break;
+ case Opt_load:
+ case Opt_LOAD:
+ *hex_encoded_iv = strsep(&datablob, " \t");
+ if (!*hex_encoded_iv)
+ break;
+ *hex_encoded_data = *hex_encoded_iv + (2 * ivsize) + 2;
+ ret = 0;
+ break;
+ case Opt_err:
+ break;
+ }
+out:
+ return ret;
+}
+
+/* datablob_format - format as an ascii string, before copying to userspace */
+static int datablob_format(char __user *buffer,
+ struct encrypted_key_payload *epayload,
+ int asciiblob_len)
+{
+ char *ascii_buf, *bufp;
+ char *iv = (char *)epayload->iv;
+ int ret = 0;
+ int len;
+ int i;
+
+ ascii_buf = kzalloc(asciiblob_len + 1, GFP_KERNEL);
+ if (!ascii_buf)
+ return -ENOMEM;
+
+ len = sprintf(ascii_buf, "%s %s ", 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]);
+
+ if (copy_to_user(buffer, ascii_buf, asciiblob_len) != 0)
+ ret = -EFAULT;
+ kfree(ascii_buf);
+ return ret;
+}
+
+/*
+ * 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(char *trusted_desc, void **master_key,
+ unsigned int *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;
+
+ tpayload = 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(char *master_desc, void **master_key,
+ unsigned int *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;
+
+ upayload = ukey->payload.data;
+ *master_key = upayload->data;
+ *master_keylen = (unsigned int)upayload->datalen;
+error:
+ return ukey;
+}
+
+static int init_desc(struct hash_desc *desc, char *alg)
+{
+ int ret;
+
+ desc->tfm = crypto_alloc_hash(alg, 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(desc->tfm)) {
+ pr_info("encrypted_key: failed to load %s transform: %ld\n",
+ alg, PTR_ERR(desc->tfm));
+ ret = PTR_ERR(desc->tfm);
+ return ret;
+ }
+ desc->flags = 0;
+ ret = crypto_hash_init(desc);
+ if (ret)
+ crypto_free_hash(desc->tfm);
+ return ret;
+}
+
+static int calc_hmac(char *digest, char *key, int keylen,
+ char *buf, size_t buflen)
+{
+ struct hash_desc desc;
+ struct scatterlist sg[1];
+ int ret;
+
+ ret = init_desc(&desc, hmac_alg);
+ if (ret)
+ return ret;
+
+ crypto_hash_setkey(desc.tfm, key, keylen);
+ ret = crypto_hash_init(&desc);
+ if (ret)
+ goto out;
+
+ sg_init_one(sg, buf, buflen);
+ ret = crypto_hash_update(&desc, sg, buflen);
+ if (!ret)
+ ret = crypto_hash_final(&desc, digest);
+out:
+ crypto_free_hash(desc.tfm);
+ return ret;
+}
+
+static int calc_hash(char *digest, void *buf, int buflen)
+{
+ struct hash_desc desc;
+ struct scatterlist sg[1];
+ int ret;
+
+ ret = init_desc(&desc, hash_alg);
+ if (ret)
+ return ret;
+
+ sg_init_one(sg, buf, buflen);
+ ret = crypto_hash_update(&desc, sg, buflen);
+ if (!ret)
+ ret = crypto_hash_final(&desc, digest);
+ crypto_free_hash(desc.tfm);
+ return ret;
+}
+
+enum derived_key_type { ENC_KEY, AUTH_KEY };
+
+/* Derive authentication/encryption key from trusted key */
+static int get_derived_key(char *derived_key, enum derived_key_type key_type,
+ void *master_key, unsigned int master_keylen)
+{
+ char derived_buf[hash_size + 10];
+ int ret;
+
+ memset(derived_buf, 0, sizeof derived_buf);
+ 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, sizeof derived_buf);
+ return ret;
+}
+
+static int init_blkcipher_desc(struct blkcipher_desc *desc, const void *key,
+ unsigned int key_len, void *iv, 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) {
+ 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,
+ void **master_key,
+ unsigned int *master_keylen)
+{
+ struct key *mkey;
+
+ mkey = request_trusted_key(epayload->master_desc,
+ master_key, master_keylen);
+ if (IS_ERR(mkey)) {
+ mkey = request_user_key(epayload->master_desc,
+ master_key, master_keylen);
+ if (IS_ERR(mkey)) {
+ pr_info("encrypted_key: trusted/user key %s not found",
+ epayload->master_desc);
+ return mkey;
+ }
+ }
+ dump_master_key(*master_key, *master_keylen);
+ return mkey;
+}
+
+/* Before returning data to userspace, encrypt decrypted data. */
+static int derived_key_encrypt(struct encrypted_key_payload *epayload,
+ void *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)
+ 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)
+ 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,
+ void *master_key, unsigned int master_keylen)
+{
+ char derived_key[hash_size];
+ char *digest;
+ int ret;
+
+ memset(derived_key, 0, sizeof derived_key);
+ ret = get_derived_key(derived_key, AUTH_KEY, master_key, master_keylen);
+ if (ret)
+ goto out;
+
+ digest = epayload->master_desc + epayload->datablob_len;
+ ret = calc_hmac(digest, derived_key, sizeof derived_key,
+ epayload->master_desc, 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,
+ void *master_key, unsigned int master_keylen)
+{
+ char derived_key[hash_size];
+ char digest[hash_size];
+ int ret;
+
+ memset(derived_key, 0, sizeof derived_key);
+ ret = get_derived_key(derived_key, AUTH_KEY, master_key, master_keylen);
+ if (ret)
+ goto out;
+
+ memset(digest, 0, sizeof digest);
+ ret = calc_hmac(digest, derived_key, sizeof derived_key,
+ epayload->master_desc, epayload->datablob_len);
+ if (ret)
+ goto out;
+ ret = memcmp(digest, epayload->master_desc + epayload->datablob_len,
+ sizeof digest);
+ if (ret) {
+ dump_hmac("datablob",
+ epayload->master_desc + epayload->datablob_len,
+ hash_size);
+ dump_hmac("calc", digest, hash_size);
+ }
+out:
+ return ret;
+}
+
+static int derived_key_decrypt(struct encrypted_key_payload *epayload,
+ void *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)
+ 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,
+ (unsigned int)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)
+ 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 *master_desc,
+ char *datalen)
+{
+ struct encrypted_key_payload *epayload = NULL;
+ unsigned short datablob_len;
+ unsigned short decrypted_datalen;
+ size_t encrypted_datalen;
+ long dlen;
+ int ret;
+
+ ret = strict_strtol(datalen, 10, &dlen);
+ if (ret < 0 || dlen < 20 || dlen > hash_size)
+ return ERR_PTR(-EINVAL);
+
+ decrypted_datalen = (unsigned short)dlen;
+ encrypted_datalen = roundup(decrypted_datalen, blksize);
+
+ datablob_len = strlen(master_desc) + 1 + strlen(datalen) + 1
+ + ivsize + 1 + encrypted_datalen;
+
+ ret = key_payload_reserve(key, decrypted_datalen + datablob_len);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ epayload = kzalloc(sizeof(*epayload) + decrypted_datalen +
+ datablob_len + hash_size + 1, GFP_KERNEL);
+ if (!epayload)
+ return ERR_PTR(-ENOMEM);
+
+ epayload->decrypted_datalen = decrypted_datalen;
+ epayload->datablob_len = datablob_len;
+ return epayload;
+}
+
+static int encrypted_key_decrypt(struct encrypted_key_payload *epayload,
+ char *hex_encoded_iv, char *hex_encoded_data)
+{
+ char derived_key[hash_size];
+ struct key *mkey;
+ void *master_key;
+ unsigned int master_keylen;
+ size_t encrypted_datalen;
+ char *hmac;
+ int ret;
+
+ encrypted_datalen = roundup(epayload->decrypted_datalen, blksize);
+ hex2bin(epayload->iv, hex_encoded_iv, ivsize);
+ hex2bin(epayload->encrypted_data, hex_encoded_data, encrypted_datalen);
+
+ hmac = epayload->master_desc + 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, master_key, master_keylen);
+ if (ret) {
+ pr_err("encrypted_key: bad hmac (%d)\n", ret);
+ goto out;
+ }
+
+ memset(derived_key, 0, sizeof derived_key);
+ ret = get_derived_key(derived_key, ENC_KEY, master_key, master_keylen);
+ if (ret)
+ goto out;
+
+ ret = derived_key_decrypt(epayload, derived_key, sizeof derived_key);
+ if (ret)
+ pr_err("encrypted_key: failed to decrypt key (%d)\n", ret);
+out:
+ key_put(mkey);
+ return ret;
+}
+
+static void __ekey_init(struct encrypted_key_payload *epayload,
+ char *master_desc, char *datalen)
+{
+ epayload->master_desc = epayload->decrypted_data
+ + epayload->decrypted_datalen;
+ epayload->datalen = epayload->master_desc + strlen(master_desc) + 1;
+ epayload->iv = epayload->datalen + strlen(datalen) + 1;
+ epayload->encrypted_data = epayload->iv + ivsize + 1;
+
+ 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,
+ char *master_desc, char *datalen,
+ char *hex_encoded_iv, char *hex_encoded_data)
+{
+ int ret = 0;
+
+ __ekey_init(epayload, master_desc, datalen);
+ if (!hex_encoded_data) {
+ get_random_bytes(epayload->iv, ivsize);
+
+ get_random_bytes(epayload->decrypted_data,
+ epayload->decrypted_datalen);
+ } else
+ ret = encrypted_key_decrypt(epayload, hex_encoded_iv,
+ hex_encoded_data);
+ 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;
+ char *master_desc = NULL;
+ char *decrypted_datalen = NULL;
+ char *hex_encoded_iv = NULL;
+ char *hex_encoded_data = NULL;
+ int ret;
+
+ if (datalen <= 0 || datalen > 32767 || !data)
+ return -EINVAL;
+
+ datablob = kzalloc(datalen + 1, GFP_KERNEL);
+ if (!datablob)
+ return -ENOMEM;
+
+ memcpy(datablob, data, datalen);
+ ret = datablob_parse(datablob, &master_desc, &decrypted_datalen,
+ &hex_encoded_iv, &hex_encoded_data);
+ if (ret < 0)
+ goto out;
+
+ epayload = encrypted_key_alloc(key, master_desc, decrypted_datalen);
+ if (IS_ERR(epayload)) {
+ ret = PTR_ERR(epayload);
+ goto out;
+ }
+ ret = encrypted_init(epayload, master_desc, decrypted_datalen,
+ hex_encoded_iv, hex_encoded_data);
+ rcu_assign_pointer(key->payload.data, epayload);
+out:
+ kfree(datablob);
+ return ret > 0 ? -EINVAL : 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;
+ int ret = 0;
+
+ if (datalen <= 0 || datalen > 32767 || !data)
+ return -EINVAL;
+
+ buf = kzalloc(datalen + 1, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ memcpy(buf, data, datalen);
+ new_master_desc = strsep(&buf, " \t");
+ if (!*new_master_desc) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ new_epayload = encrypted_key_alloc(key, new_master_desc,
+ epayload->datalen);
+ if (IS_ERR(new_epayload)) {
+ ret = PTR_ERR(new_epayload);
+ goto out;
+ }
+
+ __ekey_init(new_epayload, new_master_desc, epayload->datalen);
+
+ memcpy(new_epayload->iv, epayload->iv, ivsize);
+ memcpy(new_epayload->decrypted_data, epayload->decrypted_data,
+ epayload->decrypted_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;
+ void *master_key;
+ unsigned int master_keylen;
+ char derived_key[hash_size];
+ int asciiblob_len;
+ int ret;
+
+ epayload = rcu_dereference_protected(key->payload.data,
+ rwsem_is_locked(&((struct key *)key)->sem));
+
+ /* 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 <= 0)
+ return asciiblob_len;
+
+ mkey = request_master_key(epayload, &master_key, &master_keylen);
+ if (IS_ERR(mkey))
+ return PTR_ERR(mkey);
+
+ memset(derived_key, 0, sizeof derived_key);
+ ret = get_derived_key(derived_key, ENC_KEY, master_key, master_keylen);
+ if (ret)
+ goto out;
+
+ ret = derived_key_encrypt(epayload, derived_key, sizeof derived_key);
+ if (ret)
+ goto out;
+
+ ret = datablob_hmac_append(epayload, master_key, master_keylen);
+ if (ret)
+ goto out;
+
+ ret = datablob_format(buffer, epayload, asciiblob_len);
+ if (ret < 0)
+ goto out;
+
+ key_put(mkey);
+ return asciiblob_len;
+out:
+ key_put(mkey);
+ return ret > 0 ? -EINVAL : ret;
+}
+
+/*
+ * encrypted_destroy - before freeing the key, clear the decrypted data
+ *
+ * Before freeing the key, clear the memory containing the descrypted
+ * 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 int __init init_encrypted(void)
+{
+ int ret;
+
+ ret = register_key_type(&key_type_encrypted);
+ if (ret < 0)
+ return ret;
+ ret = aes_get_sizes(&ivsize, &blksize);
+ return ret;
+}
+
+static void __exit cleanup_encrypted(void)
+{
+ unregister_key_type(&key_type_encrypted);
+}
+
+module_init(init_encrypted);
+module_exit(cleanup_encrypted);
+
+MODULE_LICENSE("GPL");
diff --git a/security/keys/encrypted_defined.h b/security/keys/encrypted_defined.h
new file mode 100644
index 0000000..4e0b6e5
--- /dev/null
+++ b/security/keys/encrypted_defined.h
@@ -0,0 +1,52 @@
+#ifndef __ENCRYPTED_KEY_H
+#define __ENCRYPTED_KEY_H
+
+#define ENCRYPTED_DEBUG 0
+
+#if ENCRYPTED_DEBUG
+static inline void dump_master_key(void *master_key, unsigned int master_keylen)
+{
+ print_hex_dump(KERN_ERR, "master key: ", DUMP_PREFIX_NONE, 32, 1,
+ master_key, (size_t) 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, (size_t) encrypted_datalen, 0);
+}
+
+static inline void dump_hmac(char *str, void *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,
+ (size_t) hmac_size, 0);
+}
+#else
+static inline void dump_master_key(void *master_key, unsigned int 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(char *str, void *digest, int hmac_size)
+{
+}
+#endif
+#endif
--
1.7.2.2


2010-09-29 10:00:43

by Roberto Sassu

[permalink] [raw]
Subject: Re: [RFC][PATCH 4/4] keys: add new key-type encrypted

When a new encrypted key is created through the keyctl utility, the master
key specified is not searched in the keyring and the operation is performed even
if it is missing.
The following patch moves the master key request in the encrypted_init() function,
in order to deny a new key creation if the former is not present.


Signed-off-by: Roberto Sassu <[email protected]>
---
security/keys/encrypted_defined.c | 23 +++++++++++++----------
1 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/security/keys/encrypted_defined.c b/security/keys/encrypted_defined.c
index 6b26db6..48e627e 100644
--- a/security/keys/encrypted_defined.c
+++ b/security/keys/encrypted_defined.c
@@ -491,12 +491,10 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
}

static int encrypted_key_decrypt(struct encrypted_key_payload *epayload,
- char *hex_encoded_iv, char *hex_encoded_data)
+ char *hex_encoded_iv, char *hex_encoded_data,
+ void *master_key, unsigned int master_keylen)
{
char derived_key[hash_size];
- struct key *mkey;
- void *master_key;
- unsigned int master_keylen;
size_t encrypted_datalen;
char *hmac;
int ret;
@@ -508,10 +506,6 @@ static int encrypted_key_decrypt(struct encrypted_key_payload *epayload,
hmac = epayload->master_desc + 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, master_key, master_keylen);
if (ret) {
pr_err("encrypted_key: bad hmac (%d)\n", ret);
@@ -527,7 +521,6 @@ static int encrypted_key_decrypt(struct encrypted_key_payload *epayload,
if (ret)
pr_err("encrypted_key: failed to decrypt key (%d)\n", ret);
out:
- key_put(mkey);
return ret;
}

@@ -555,8 +548,16 @@ static int encrypted_init(struct encrypted_key_payload *epayload,
char *hex_encoded_iv, char *hex_encoded_data)
{
int ret = 0;
+ struct key *mkey;
+ void *master_key;
+ unsigned int master_keylen;

__ekey_init(epayload, master_desc, datalen);
+
+ mkey = request_master_key(epayload, &master_key, &master_keylen);
+ if (IS_ERR(mkey))
+ return PTR_ERR(mkey);
+
if (!hex_encoded_data) {
get_random_bytes(epayload->iv, ivsize);

@@ -564,7 +565,9 @@ static int encrypted_init(struct encrypted_key_payload *epayload,
epayload->decrypted_datalen);
} else
ret = encrypted_key_decrypt(epayload, hex_encoded_iv,
- hex_encoded_data);
+ hex_encoded_data, master_key, master_keylen);
+
+ key_put(mkey);
return ret;
}

--
1.7.2.3


On Tuesday, September 28, 2010 08:36:33 pm Mimi Zohar wrote:
> Defines a new kernel key-type called 'encrypted'. Encrypted keys are
> kernel generated random numbers, which are encrypted/decrypted with
> a 'trusted' symmetric key. Encrypted keys are created/encrypted/decrypted
> in the kernel. Userspace only ever sees/stores encrypted blobs.
>
> Signed-off-by: Mimi Zohar <[email protected]>
> Signed-off-by: David Safford <[email protected]>
> ---
> include/keys/encrypted-type.h | 30 ++
> security/Kconfig | 17 +
> security/keys/Makefile | 1 +
> security/keys/encrypted_defined.c | 781 +++++++++++++++++++++++++++++++++++++
> security/keys/encrypted_defined.h | 52 +++
> 5 files changed, 881 insertions(+), 0 deletions(-)
> create mode 100644 include/keys/encrypted-type.h
> create mode 100644 security/keys/encrypted_defined.c
> create mode 100644 security/keys/encrypted_defined.h
>
> diff --git a/include/keys/encrypted-type.h b/include/keys/encrypted-type.h
> new file mode 100644
> index 0000000..e2312e0
> --- /dev/null
> +++ b/include/keys/encrypted-type.h
> @@ -0,0 +1,30 @@
> +/* encrypted-type.h: encrypted-defined key type
> + *
> + * Copyright (C) 2010 IBM Corporation
> + * Author: Mimi Zohar <[email protected]>
> + *
> + * 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_ENCRYPTED_TYPE_H
> +#define _KEYS_ENCRYPTED_TYPE_H
> +
> +#include <linux/key.h>
> +#include <linux/rcupdate.h>
> +
> +struct encrypted_key_payload {
> + struct rcu_head rcu; /* RCU destructor */
> + char *master_desc; /* datablob: master key name */
> + char *datalen; /* datablob: decrypted key length */
> + void *iv; /* datablob: iv */
> + void *encrypted_data; /* datablob: encrypted key */
> + unsigned short datablob_len; /* length of datablob */
> + unsigned short decrypted_datalen; /* decrypted data length */
> + char decrypted_data[0]; /* decrypted data + datablob + hmac */
> +};
> +
> +extern struct key_type key_type_encrypted;
> +
> +#endif /* _KEYS_ENCRYPTED_TYPE_H */
> diff --git a/security/Kconfig b/security/Kconfig
> index f9681e5..b03187d 100644
> --- a/security/Kconfig
> +++ b/security/Kconfig
> @@ -37,6 +37,23 @@ config TRUSTED_KEYS
>
> If you are unsure as to whether this is required, answer N.
>
> +config ENCRYPTED_KEYS
> + tristate "ENCRYPTED KEYS"
> + depends on KEYS && TRUSTED_KEYS
> + select LIBCRC32C
> + select CONFIG_CRYPTO_AES
> + select CRYPTO_HMAC
> + select CRYPTO_SHA256
> + select BLOCK_CBC
> + help
> + This option provides support for create/encrypting/decrypting keys
> + in the kernel. Encrypted keys are kernel generated random numbers,
> + which are encrypted/decrypted with a 'master' symmetric key. The
> + 'master' key can be either a trusted-key or user-key type.
> + Userspace only ever sees/stores encrypted blobs.
> +
> + If you are unsure as to whether this is required, answer N.
> +
> config KEYS_DEBUG_PROC_KEYS
> bool "Enable the /proc/keys file by which keys may be viewed"
> depends on KEYS
> diff --git a/security/keys/Makefile b/security/keys/Makefile
> index fcb1070..6c94105 100644
> --- a/security/keys/Makefile
> +++ b/security/keys/Makefile
> @@ -14,6 +14,7 @@ obj-y := \
> user_defined.o
>
> obj-$(CONFIG_TRUSTED_KEYS) += trusted_defined.o
> +obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted_defined.o
> obj-$(CONFIG_KEYS_COMPAT) += compat.o
> obj-$(CONFIG_PROC_FS) += proc.o
> obj-$(CONFIG_SYSCTL) += sysctl.o
> diff --git a/security/keys/encrypted_defined.c b/security/keys/encrypted_defined.c
> new file mode 100644
> index 0000000..6b26db6
> --- /dev/null
> +++ b/security/keys/encrypted_defined.c
> @@ -0,0 +1,781 @@
> +/*
> + * Copyright (C) 2010 IBM Corporation
> + *
> + * Author:
> + * Mimi Zohar <[email protected]>
> + *
> + * 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.
> + *
> + * File: encrypted_defined.c
> + *
> + * Defines a new kernel key-type called 'encrypted'. Encrypted keys
> + * are kernel generated random numbers, which are encrypted/decrypted
> + * using a 'master' key. The 'master' key can either be a trusted-key or
> + * user-key type. Encrypted keys are created/encrypted/decrypted in the
> + * kernel. Userspace ever only sees/stores encrypted blobs.
> + *
> + * keyctl add "encrypted" "name" "NEW master-key-name keylen" ring
> + * keyctl add "encrypted" "name" "LOAD master-key-name keylen hex_blob" ring
> + * keyctl update keyid "UPDATE master-key-name"
> + */
> +
> +#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 <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 <crypto/sha.h>
> +#include <crypto/aes.h>
> +
> +#include "encrypted_defined.h"
> +
> +static char hash_alg[] = "sha256";
> +static char hmac_alg[] = "hmac(sha256)";
> +static int hash_size = SHA256_DIGEST_SIZE;
> +static char blkcipher_alg[] = "cbc(aes)";
> +static int ivsize;
> +static int blksize;
> +
> +static int aes_get_sizes(int *ivsize, int *blksize)
> +{
> + 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;
> +}
> +
> +enum {
> + Opt_err = -1, Opt_new = 1, Opt_load, Opt_NEW, Opt_LOAD
> +};
> +
> +static match_table_t key_tokens = {
> + {Opt_new, "new"},
> + {Opt_NEW, "NEW"},
> + {Opt_load, "load"},
> + {Opt_LOAD, "LOAD"},
> + {Opt_err, NULL}
> +};
> +
> +/*
> + * datablob_parse - parse the keyctl data
> + *
> + * datablob format:
> + * NEW <master-key name> <decrypted data length>
> + * LOAD <master-key name> <decrypted data length> <encrypted iv + data>
> + *
> + * 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, char **master_desc,
> + char **decrypted_datalen, char **hex_encoded_iv,
> + char **hex_encoded_data)
> +{
> + substring_t args[MAX_OPT_ARGS];
> + int ret = -EINVAL;
> + int key_cmd;
> + char *p;
> +
> + p = strsep(&datablob, " \t");
> + if (!p)
> + return ret;
> + key_cmd = match_token(p, key_tokens, args);
> +
> + *master_desc = strsep(&datablob, " \t");
> + if (!*master_desc)
> + goto out;
> + *decrypted_datalen = strsep(&datablob, " \t");
> + if (!*decrypted_datalen)
> + goto out;
> +
> + switch (key_cmd) {
> + case Opt_new:
> + case Opt_NEW:
> + ret = 0;
> + break;
> + case Opt_load:
> + case Opt_LOAD:
> + *hex_encoded_iv = strsep(&datablob, " \t");
> + if (!*hex_encoded_iv)
> + break;
> + *hex_encoded_data = *hex_encoded_iv + (2 * ivsize) + 2;
> + ret = 0;
> + break;
> + case Opt_err:
> + break;
> + }
> +out:
> + return ret;
> +}
> +
> +/* datablob_format - format as an ascii string, before copying to userspace */
> +static int datablob_format(char __user *buffer,
> + struct encrypted_key_payload *epayload,
> + int asciiblob_len)
> +{
> + char *ascii_buf, *bufp;
> + char *iv = (char *)epayload->iv;
> + int ret = 0;
> + int len;
> + int i;
> +
> + ascii_buf = kzalloc(asciiblob_len + 1, GFP_KERNEL);
> + if (!ascii_buf)
> + return -ENOMEM;
> +
> + len = sprintf(ascii_buf, "%s %s ", 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]);
> +
> + if (copy_to_user(buffer, ascii_buf, asciiblob_len) != 0)
> + ret = -EFAULT;
> + kfree(ascii_buf);
> + return ret;
> +}
> +
> +/*
> + * 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(char *trusted_desc, void **master_key,
> + unsigned int *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;
> +
> + tpayload = 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(char *master_desc, void **master_key,
> + unsigned int *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;
> +
> + upayload = ukey->payload.data;
> + *master_key = upayload->data;
> + *master_keylen = (unsigned int)upayload->datalen;
> +error:
> + return ukey;
> +}
> +
> +static int init_desc(struct hash_desc *desc, char *alg)
> +{
> + int ret;
> +
> + desc->tfm = crypto_alloc_hash(alg, 0, CRYPTO_ALG_ASYNC);
> + if (IS_ERR(desc->tfm)) {
> + pr_info("encrypted_key: failed to load %s transform: %ld\n",
> + alg, PTR_ERR(desc->tfm));
> + ret = PTR_ERR(desc->tfm);
> + return ret;
> + }
> + desc->flags = 0;
> + ret = crypto_hash_init(desc);
> + if (ret)
> + crypto_free_hash(desc->tfm);
> + return ret;
> +}
> +
> +static int calc_hmac(char *digest, char *key, int keylen,
> + char *buf, size_t buflen)
> +{
> + struct hash_desc desc;
> + struct scatterlist sg[1];
> + int ret;
> +
> + ret = init_desc(&desc, hmac_alg);
> + if (ret)
> + return ret;
> +
> + crypto_hash_setkey(desc.tfm, key, keylen);
> + ret = crypto_hash_init(&desc);
> + if (ret)
> + goto out;
> +
> + sg_init_one(sg, buf, buflen);
> + ret = crypto_hash_update(&desc, sg, buflen);
> + if (!ret)
> + ret = crypto_hash_final(&desc, digest);
> +out:
> + crypto_free_hash(desc.tfm);
> + return ret;
> +}
> +
> +static int calc_hash(char *digest, void *buf, int buflen)
> +{
> + struct hash_desc desc;
> + struct scatterlist sg[1];
> + int ret;
> +
> + ret = init_desc(&desc, hash_alg);
> + if (ret)
> + return ret;
> +
> + sg_init_one(sg, buf, buflen);
> + ret = crypto_hash_update(&desc, sg, buflen);
> + if (!ret)
> + ret = crypto_hash_final(&desc, digest);
> + crypto_free_hash(desc.tfm);
> + return ret;
> +}
> +
> +enum derived_key_type { ENC_KEY, AUTH_KEY };
> +
> +/* Derive authentication/encryption key from trusted key */
> +static int get_derived_key(char *derived_key, enum derived_key_type key_type,
> + void *master_key, unsigned int master_keylen)
> +{
> + char derived_buf[hash_size + 10];
> + int ret;
> +
> + memset(derived_buf, 0, sizeof derived_buf);
> + 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, sizeof derived_buf);
> + return ret;
> +}
> +
> +static int init_blkcipher_desc(struct blkcipher_desc *desc, const void *key,
> + unsigned int key_len, void *iv, 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) {
> + 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,
> + void **master_key,
> + unsigned int *master_keylen)
> +{
> + struct key *mkey;
> +
> + mkey = request_trusted_key(epayload->master_desc,
> + master_key, master_keylen);
> + if (IS_ERR(mkey)) {
> + mkey = request_user_key(epayload->master_desc,
> + master_key, master_keylen);
> + if (IS_ERR(mkey)) {
> + pr_info("encrypted_key: trusted/user key %s not found",
> + epayload->master_desc);
> + return mkey;
> + }
> + }
> + dump_master_key(*master_key, *master_keylen);
> + return mkey;
> +}
> +
> +/* Before returning data to userspace, encrypt decrypted data. */
> +static int derived_key_encrypt(struct encrypted_key_payload *epayload,
> + void *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)
> + 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)
> + 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,
> + void *master_key, unsigned int master_keylen)
> +{
> + char derived_key[hash_size];
> + char *digest;
> + int ret;
> +
> + memset(derived_key, 0, sizeof derived_key);
> + ret = get_derived_key(derived_key, AUTH_KEY, master_key, master_keylen);
> + if (ret)
> + goto out;
> +
> + digest = epayload->master_desc + epayload->datablob_len;
> + ret = calc_hmac(digest, derived_key, sizeof derived_key,
> + epayload->master_desc, 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,
> + void *master_key, unsigned int master_keylen)
> +{
> + char derived_key[hash_size];
> + char digest[hash_size];
> + int ret;
> +
> + memset(derived_key, 0, sizeof derived_key);
> + ret = get_derived_key(derived_key, AUTH_KEY, master_key, master_keylen);
> + if (ret)
> + goto out;
> +
> + memset(digest, 0, sizeof digest);
> + ret = calc_hmac(digest, derived_key, sizeof derived_key,
> + epayload->master_desc, epayload->datablob_len);
> + if (ret)
> + goto out;
> + ret = memcmp(digest, epayload->master_desc + epayload->datablob_len,
> + sizeof digest);
> + if (ret) {
> + dump_hmac("datablob",
> + epayload->master_desc + epayload->datablob_len,
> + hash_size);
> + dump_hmac("calc", digest, hash_size);
> + }
> +out:
> + return ret;
> +}
> +
> +static int derived_key_decrypt(struct encrypted_key_payload *epayload,
> + void *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)
> + 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,
> + (unsigned int)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)
> + 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 *master_desc,
> + char *datalen)
> +{
> + struct encrypted_key_payload *epayload = NULL;
> + unsigned short datablob_len;
> + unsigned short decrypted_datalen;
> + size_t encrypted_datalen;
> + long dlen;
> + int ret;
> +
> + ret = strict_strtol(datalen, 10, &dlen);
> + if (ret < 0 || dlen < 20 || dlen > hash_size)
> + return ERR_PTR(-EINVAL);
> +
> + decrypted_datalen = (unsigned short)dlen;
> + encrypted_datalen = roundup(decrypted_datalen, blksize);
> +
> + datablob_len = strlen(master_desc) + 1 + strlen(datalen) + 1
> + + ivsize + 1 + encrypted_datalen;
> +
> + ret = key_payload_reserve(key, decrypted_datalen + datablob_len);
> + if (ret < 0)
> + return ERR_PTR(ret);
> +
> + epayload = kzalloc(sizeof(*epayload) + decrypted_datalen +
> + datablob_len + hash_size + 1, GFP_KERNEL);
> + if (!epayload)
> + return ERR_PTR(-ENOMEM);
> +
> + epayload->decrypted_datalen = decrypted_datalen;
> + epayload->datablob_len = datablob_len;
> + return epayload;
> +}
> +
> +static int encrypted_key_decrypt(struct encrypted_key_payload *epayload,
> + char *hex_encoded_iv, char *hex_encoded_data)
> +{
> + char derived_key[hash_size];
> + struct key *mkey;
> + void *master_key;
> + unsigned int master_keylen;
> + size_t encrypted_datalen;
> + char *hmac;
> + int ret;
> +
> + encrypted_datalen = roundup(epayload->decrypted_datalen, blksize);
> + hex2bin(epayload->iv, hex_encoded_iv, ivsize);
> + hex2bin(epayload->encrypted_data, hex_encoded_data, encrypted_datalen);
> +
> + hmac = epayload->master_desc + 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, master_key, master_keylen);
> + if (ret) {
> + pr_err("encrypted_key: bad hmac (%d)\n", ret);
> + goto out;
> + }
> +
> + memset(derived_key, 0, sizeof derived_key);
> + ret = get_derived_key(derived_key, ENC_KEY, master_key, master_keylen);
> + if (ret)
> + goto out;
> +
> + ret = derived_key_decrypt(epayload, derived_key, sizeof derived_key);
> + if (ret)
> + pr_err("encrypted_key: failed to decrypt key (%d)\n", ret);
> +out:
> + key_put(mkey);
> + return ret;
> +}
> +
> +static void __ekey_init(struct encrypted_key_payload *epayload,
> + char *master_desc, char *datalen)
> +{
> + epayload->master_desc = epayload->decrypted_data
> + + epayload->decrypted_datalen;
> + epayload->datalen = epayload->master_desc + strlen(master_desc) + 1;
> + epayload->iv = epayload->datalen + strlen(datalen) + 1;
> + epayload->encrypted_data = epayload->iv + ivsize + 1;
> +
> + 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,
> + char *master_desc, char *datalen,
> + char *hex_encoded_iv, char *hex_encoded_data)
> +{
> + int ret = 0;
> +
> + __ekey_init(epayload, master_desc, datalen);
> + if (!hex_encoded_data) {
> + get_random_bytes(epayload->iv, ivsize);
> +
> + get_random_bytes(epayload->decrypted_data,
> + epayload->decrypted_datalen);
> + } else
> + ret = encrypted_key_decrypt(epayload, hex_encoded_iv,
> + hex_encoded_data);
> + 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;
> + char *master_desc = NULL;
> + char *decrypted_datalen = NULL;
> + char *hex_encoded_iv = NULL;
> + char *hex_encoded_data = NULL;
> + int ret;
> +
> + if (datalen <= 0 || datalen > 32767 || !data)
> + return -EINVAL;
> +
> + datablob = kzalloc(datalen + 1, GFP_KERNEL);
> + if (!datablob)
> + return -ENOMEM;
> +
> + memcpy(datablob, data, datalen);
> + ret = datablob_parse(datablob, &master_desc, &decrypted_datalen,
> + &hex_encoded_iv, &hex_encoded_data);
> + if (ret < 0)
> + goto out;
> +
> + epayload = encrypted_key_alloc(key, master_desc, decrypted_datalen);
> + if (IS_ERR(epayload)) {
> + ret = PTR_ERR(epayload);
> + goto out;
> + }
> + ret = encrypted_init(epayload, master_desc, decrypted_datalen,
> + hex_encoded_iv, hex_encoded_data);
> + rcu_assign_pointer(key->payload.data, epayload);
> +out:
> + kfree(datablob);
> + return ret > 0 ? -EINVAL : 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;
> + int ret = 0;
> +
> + if (datalen <= 0 || datalen > 32767 || !data)
> + return -EINVAL;
> +
> + buf = kzalloc(datalen + 1, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
> +
> + memcpy(buf, data, datalen);
> + new_master_desc = strsep(&buf, " \t");
> + if (!*new_master_desc) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + new_epayload = encrypted_key_alloc(key, new_master_desc,
> + epayload->datalen);
> + if (IS_ERR(new_epayload)) {
> + ret = PTR_ERR(new_epayload);
> + goto out;
> + }
> +
> + __ekey_init(new_epayload, new_master_desc, epayload->datalen);
> +
> + memcpy(new_epayload->iv, epayload->iv, ivsize);
> + memcpy(new_epayload->decrypted_data, epayload->decrypted_data,
> + epayload->decrypted_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;
> + void *master_key;
> + unsigned int master_keylen;
> + char derived_key[hash_size];
> + int asciiblob_len;
> + int ret;
> +
> + epayload = rcu_dereference_protected(key->payload.data,
> + rwsem_is_locked(&((struct key *)key)->sem));
> +
> + /* 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 <= 0)
> + return asciiblob_len;
> +
> + mkey = request_master_key(epayload, &master_key, &master_keylen);
> + if (IS_ERR(mkey))
> + return PTR_ERR(mkey);
> +
> + memset(derived_key, 0, sizeof derived_key);
> + ret = get_derived_key(derived_key, ENC_KEY, master_key, master_keylen);
> + if (ret)
> + goto out;
> +
> + ret = derived_key_encrypt(epayload, derived_key, sizeof derived_key);
> + if (ret)
> + goto out;
> +
> + ret = datablob_hmac_append(epayload, master_key, master_keylen);
> + if (ret)
> + goto out;
> +
> + ret = datablob_format(buffer, epayload, asciiblob_len);
> + if (ret < 0)
> + goto out;
> +
> + key_put(mkey);
> + return asciiblob_len;
> +out:
> + key_put(mkey);
> + return ret > 0 ? -EINVAL : ret;
> +}
> +
> +/*
> + * encrypted_destroy - before freeing the key, clear the decrypted data
> + *
> + * Before freeing the key, clear the memory containing the descrypted
> + * 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 int __init init_encrypted(void)
> +{
> + int ret;
> +
> + ret = register_key_type(&key_type_encrypted);
> + if (ret < 0)
> + return ret;
> + ret = aes_get_sizes(&ivsize, &blksize);
> + return ret;
> +}
> +
> +static void __exit cleanup_encrypted(void)
> +{
> + unregister_key_type(&key_type_encrypted);
> +}
> +
> +module_init(init_encrypted);
> +module_exit(cleanup_encrypted);
> +
> +MODULE_LICENSE("GPL");
> diff --git a/security/keys/encrypted_defined.h b/security/keys/encrypted_defined.h
> new file mode 100644
> index 0000000..4e0b6e5
> --- /dev/null
> +++ b/security/keys/encrypted_defined.h
> @@ -0,0 +1,52 @@
> +#ifndef __ENCRYPTED_KEY_H
> +#define __ENCRYPTED_KEY_H
> +
> +#define ENCRYPTED_DEBUG 0
> +
> +#if ENCRYPTED_DEBUG
> +static inline void dump_master_key(void *master_key, unsigned int master_keylen)
> +{
> + print_hex_dump(KERN_ERR, "master key: ", DUMP_PREFIX_NONE, 32, 1,
> + master_key, (size_t) 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, (size_t) encrypted_datalen, 0);
> +}
> +
> +static inline void dump_hmac(char *str, void *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,
> + (size_t) hmac_size, 0);
> +}
> +#else
> +static inline void dump_master_key(void *master_key, unsigned int 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(char *str, void *digest, int hmac_size)
> +{
> +}
> +#endif
> +#endif
>


Attachments:
smime.p7s (4.60 kB)

2010-09-29 11:57:36

by Mimi Zohar

[permalink] [raw]
Subject: Re: [RFC][PATCH 4/4] keys: add new key-type encrypted

On Wed, 2010-09-29 at 12:00 +0200, Roberto Sassu wrote:
> When a new encrypted key is created through the keyctl utility, the master
> key specified is not searched in the keyring and the operation is performed even
> if it is missing.

Yes, and why is this a problem? After creating a new key, the first
thing done should be to save it. At that point, you'd find out the
master-key doesn't exist, requiring you to either load it or change the
master-key name using 'keyctl update'.

Mimi

> The following patch moves the master key request in the encrypted_init() function,
> in order to deny a new key creation if the former is not present.
>
>
> Signed-off-by: Roberto Sassu <[email protected]>
> ---
> security/keys/encrypted_defined.c | 23 +++++++++++++----------
> 1 files changed, 13 insertions(+), 10 deletions(-)
>
> diff --git a/security/keys/encrypted_defined.c b/security/keys/encrypted_defined.c
> index 6b26db6..48e627e 100644
> --- a/security/keys/encrypted_defined.c
> +++ b/security/keys/encrypted_defined.c
> @@ -491,12 +491,10 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
> }
>
> static int encrypted_key_decrypt(struct encrypted_key_payload *epayload,
> - char *hex_encoded_iv, char *hex_encoded_data)
> + char *hex_encoded_iv, char *hex_encoded_data,
> + void *master_key, unsigned int master_keylen)
> {
> char derived_key[hash_size];
> - struct key *mkey;
> - void *master_key;
> - unsigned int master_keylen;
> size_t encrypted_datalen;
> char *hmac;
> int ret;
> @@ -508,10 +506,6 @@ static int encrypted_key_decrypt(struct encrypted_key_payload *epayload,
> hmac = epayload->master_desc + 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, master_key, master_keylen);
> if (ret) {
> pr_err("encrypted_key: bad hmac (%d)\n", ret);
> @@ -527,7 +521,6 @@ static int encrypted_key_decrypt(struct encrypted_key_payload *epayload,
> if (ret)
> pr_err("encrypted_key: failed to decrypt key (%d)\n", ret);
> out:
> - key_put(mkey);
> return ret;
> }
>
> @@ -555,8 +548,16 @@ static int encrypted_init(struct encrypted_key_payload *epayload,
> char *hex_encoded_iv, char *hex_encoded_data)
> {
> int ret = 0;
> + struct key *mkey;
> + void *master_key;
> + unsigned int master_keylen;
>
> __ekey_init(epayload, master_desc, datalen);
> +
> + mkey = request_master_key(epayload, &master_key, &master_keylen);
> + if (IS_ERR(mkey))
> + return PTR_ERR(mkey);
> +
> if (!hex_encoded_data) {
> get_random_bytes(epayload->iv, ivsize);
>
> @@ -564,7 +565,9 @@ static int encrypted_init(struct encrypted_key_payload *epayload,
> epayload->decrypted_datalen);
> } else
> ret = encrypted_key_decrypt(epayload, hex_encoded_iv,
> - hex_encoded_data);
> + hex_encoded_data, master_key, master_keylen);
> +
> + key_put(mkey);
> return ret;
> }
>
> --
> 1.7.2.3
>
>
> On Tuesday, September 28, 2010 08:36:33 pm Mimi Zohar wrote:
> > Defines a new kernel key-type called 'encrypted'. Encrypted keys are
> > kernel generated random numbers, which are encrypted/decrypted with
> > a 'trusted' symmetric key. Encrypted keys are created/encrypted/decrypted
> > in the kernel. Userspace only ever sees/stores encrypted blobs.
> >
> > Signed-off-by: Mimi Zohar <[email protected]>
> > Signed-off-by: David Safford <[email protected]>
> > ---
> > include/keys/encrypted-type.h | 30 ++
> > security/Kconfig | 17 +
> > security/keys/Makefile | 1 +
> > security/keys/encrypted_defined.c | 781 +++++++++++++++++++++++++++++++++++++
> > security/keys/encrypted_defined.h | 52 +++
> > 5 files changed, 881 insertions(+), 0 deletions(-)
> > create mode 100644 include/keys/encrypted-type.h
> > create mode 100644 security/keys/encrypted_defined.c
> > create mode 100644 security/keys/encrypted_defined.h
> >
> > diff --git a/include/keys/encrypted-type.h b/include/keys/encrypted-type.h
> > new file mode 100644
> > index 0000000..e2312e0
> > --- /dev/null
> > +++ b/include/keys/encrypted-type.h
> > @@ -0,0 +1,30 @@
> > +/* encrypted-type.h: encrypted-defined key type
> > + *
> > + * Copyright (C) 2010 IBM Corporation
> > + * Author: Mimi Zohar <[email protected]>
> > + *
> > + * 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_ENCRYPTED_TYPE_H
> > +#define _KEYS_ENCRYPTED_TYPE_H
> > +
> > +#include <linux/key.h>
> > +#include <linux/rcupdate.h>
> > +
> > +struct encrypted_key_payload {
> > + struct rcu_head rcu; /* RCU destructor */
> > + char *master_desc; /* datablob: master key name */
> > + char *datalen; /* datablob: decrypted key length */
> > + void *iv; /* datablob: iv */
> > + void *encrypted_data; /* datablob: encrypted key */
> > + unsigned short datablob_len; /* length of datablob */
> > + unsigned short decrypted_datalen; /* decrypted data length */
> > + char decrypted_data[0]; /* decrypted data + datablob + hmac */
> > +};
> > +
> > +extern struct key_type key_type_encrypted;
> > +
> > +#endif /* _KEYS_ENCRYPTED_TYPE_H */
> > diff --git a/security/Kconfig b/security/Kconfig
> > index f9681e5..b03187d 100644
> > --- a/security/Kconfig
> > +++ b/security/Kconfig
> > @@ -37,6 +37,23 @@ config TRUSTED_KEYS
> >
> > If you are unsure as to whether this is required, answer N.
> >
> > +config ENCRYPTED_KEYS
> > + tristate "ENCRYPTED KEYS"
> > + depends on KEYS && TRUSTED_KEYS
> > + select LIBCRC32C
> > + select CONFIG_CRYPTO_AES
> > + select CRYPTO_HMAC
> > + select CRYPTO_SHA256
> > + select BLOCK_CBC
> > + help
> > + This option provides support for create/encrypting/decrypting keys
> > + in the kernel. Encrypted keys are kernel generated random numbers,
> > + which are encrypted/decrypted with a 'master' symmetric key. The
> > + 'master' key can be either a trusted-key or user-key type.
> > + Userspace only ever sees/stores encrypted blobs.
> > +
> > + If you are unsure as to whether this is required, answer N.
> > +
> > config KEYS_DEBUG_PROC_KEYS
> > bool "Enable the /proc/keys file by which keys may be viewed"
> > depends on KEYS
> > diff --git a/security/keys/Makefile b/security/keys/Makefile
> > index fcb1070..6c94105 100644
> > --- a/security/keys/Makefile
> > +++ b/security/keys/Makefile
> > @@ -14,6 +14,7 @@ obj-y := \
> > user_defined.o
> >
> > obj-$(CONFIG_TRUSTED_KEYS) += trusted_defined.o
> > +obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted_defined.o
> > obj-$(CONFIG_KEYS_COMPAT) += compat.o
> > obj-$(CONFIG_PROC_FS) += proc.o
> > obj-$(CONFIG_SYSCTL) += sysctl.o
> > diff --git a/security/keys/encrypted_defined.c b/security/keys/encrypted_defined.c
> > new file mode 100644
> > index 0000000..6b26db6
> > --- /dev/null
> > +++ b/security/keys/encrypted_defined.c
> > @@ -0,0 +1,781 @@
> > +/*
> > + * Copyright (C) 2010 IBM Corporation
> > + *
> > + * Author:
> > + * Mimi Zohar <[email protected]>
> > + *
> > + * 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.
> > + *
> > + * File: encrypted_defined.c
> > + *
> > + * Defines a new kernel key-type called 'encrypted'. Encrypted keys
> > + * are kernel generated random numbers, which are encrypted/decrypted
> > + * using a 'master' key. The 'master' key can either be a trusted-key or
> > + * user-key type. Encrypted keys are created/encrypted/decrypted in the
> > + * kernel. Userspace ever only sees/stores encrypted blobs.
> > + *
> > + * keyctl add "encrypted" "name" "NEW master-key-name keylen" ring
> > + * keyctl add "encrypted" "name" "LOAD master-key-name keylen hex_blob" ring
> > + * keyctl update keyid "UPDATE master-key-name"
> > + */
> > +
> > +#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 <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 <crypto/sha.h>
> > +#include <crypto/aes.h>
> > +
> > +#include "encrypted_defined.h"
> > +
> > +static char hash_alg[] = "sha256";
> > +static char hmac_alg[] = "hmac(sha256)";
> > +static int hash_size = SHA256_DIGEST_SIZE;
> > +static char blkcipher_alg[] = "cbc(aes)";
> > +static int ivsize;
> > +static int blksize;
> > +
> > +static int aes_get_sizes(int *ivsize, int *blksize)
> > +{
> > + 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;
> > +}
> > +
> > +enum {
> > + Opt_err = -1, Opt_new = 1, Opt_load, Opt_NEW, Opt_LOAD
> > +};
> > +
> > +static match_table_t key_tokens = {
> > + {Opt_new, "new"},
> > + {Opt_NEW, "NEW"},
> > + {Opt_load, "load"},
> > + {Opt_LOAD, "LOAD"},
> > + {Opt_err, NULL}
> > +};
> > +
> > +/*
> > + * datablob_parse - parse the keyctl data
> > + *
> > + * datablob format:
> > + * NEW <master-key name> <decrypted data length>
> > + * LOAD <master-key name> <decrypted data length> <encrypted iv + data>
> > + *
> > + * 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, char **master_desc,
> > + char **decrypted_datalen, char **hex_encoded_iv,
> > + char **hex_encoded_data)
> > +{
> > + substring_t args[MAX_OPT_ARGS];
> > + int ret = -EINVAL;
> > + int key_cmd;
> > + char *p;
> > +
> > + p = strsep(&datablob, " \t");
> > + if (!p)
> > + return ret;
> > + key_cmd = match_token(p, key_tokens, args);
> > +
> > + *master_desc = strsep(&datablob, " \t");
> > + if (!*master_desc)
> > + goto out;
> > + *decrypted_datalen = strsep(&datablob, " \t");
> > + if (!*decrypted_datalen)
> > + goto out;
> > +
> > + switch (key_cmd) {
> > + case Opt_new:
> > + case Opt_NEW:
> > + ret = 0;
> > + break;
> > + case Opt_load:
> > + case Opt_LOAD:
> > + *hex_encoded_iv = strsep(&datablob, " \t");
> > + if (!*hex_encoded_iv)
> > + break;
> > + *hex_encoded_data = *hex_encoded_iv + (2 * ivsize) + 2;
> > + ret = 0;
> > + break;
> > + case Opt_err:
> > + break;
> > + }
> > +out:
> > + return ret;
> > +}
> > +
> > +/* datablob_format - format as an ascii string, before copying to userspace */
> > +static int datablob_format(char __user *buffer,
> > + struct encrypted_key_payload *epayload,
> > + int asciiblob_len)
> > +{
> > + char *ascii_buf, *bufp;
> > + char *iv = (char *)epayload->iv;
> > + int ret = 0;
> > + int len;
> > + int i;
> > +
> > + ascii_buf = kzalloc(asciiblob_len + 1, GFP_KERNEL);
> > + if (!ascii_buf)
> > + return -ENOMEM;
> > +
> > + len = sprintf(ascii_buf, "%s %s ", 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]);
> > +
> > + if (copy_to_user(buffer, ascii_buf, asciiblob_len) != 0)
> > + ret = -EFAULT;
> > + kfree(ascii_buf);
> > + return ret;
> > +}
> > +
> > +/*
> > + * 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(char *trusted_desc, void **master_key,
> > + unsigned int *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;
> > +
> > + tpayload = 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(char *master_desc, void **master_key,
> > + unsigned int *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;
> > +
> > + upayload = ukey->payload.data;
> > + *master_key = upayload->data;
> > + *master_keylen = (unsigned int)upayload->datalen;
> > +error:
> > + return ukey;
> > +}
> > +
> > +static int init_desc(struct hash_desc *desc, char *alg)
> > +{
> > + int ret;
> > +
> > + desc->tfm = crypto_alloc_hash(alg, 0, CRYPTO_ALG_ASYNC);
> > + if (IS_ERR(desc->tfm)) {
> > + pr_info("encrypted_key: failed to load %s transform: %ld\n",
> > + alg, PTR_ERR(desc->tfm));
> > + ret = PTR_ERR(desc->tfm);
> > + return ret;
> > + }
> > + desc->flags = 0;
> > + ret = crypto_hash_init(desc);
> > + if (ret)
> > + crypto_free_hash(desc->tfm);
> > + return ret;
> > +}
> > +
> > +static int calc_hmac(char *digest, char *key, int keylen,
> > + char *buf, size_t buflen)
> > +{
> > + struct hash_desc desc;
> > + struct scatterlist sg[1];
> > + int ret;
> > +
> > + ret = init_desc(&desc, hmac_alg);
> > + if (ret)
> > + return ret;
> > +
> > + crypto_hash_setkey(desc.tfm, key, keylen);
> > + ret = crypto_hash_init(&desc);
> > + if (ret)
> > + goto out;
> > +
> > + sg_init_one(sg, buf, buflen);
> > + ret = crypto_hash_update(&desc, sg, buflen);
> > + if (!ret)
> > + ret = crypto_hash_final(&desc, digest);
> > +out:
> > + crypto_free_hash(desc.tfm);
> > + return ret;
> > +}
> > +
> > +static int calc_hash(char *digest, void *buf, int buflen)
> > +{
> > + struct hash_desc desc;
> > + struct scatterlist sg[1];
> > + int ret;
> > +
> > + ret = init_desc(&desc, hash_alg);
> > + if (ret)
> > + return ret;
> > +
> > + sg_init_one(sg, buf, buflen);
> > + ret = crypto_hash_update(&desc, sg, buflen);
> > + if (!ret)
> > + ret = crypto_hash_final(&desc, digest);
> > + crypto_free_hash(desc.tfm);
> > + return ret;
> > +}
> > +
> > +enum derived_key_type { ENC_KEY, AUTH_KEY };
> > +
> > +/* Derive authentication/encryption key from trusted key */
> > +static int get_derived_key(char *derived_key, enum derived_key_type key_type,
> > + void *master_key, unsigned int master_keylen)
> > +{
> > + char derived_buf[hash_size + 10];
> > + int ret;
> > +
> > + memset(derived_buf, 0, sizeof derived_buf);
> > + 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, sizeof derived_buf);
> > + return ret;
> > +}
> > +
> > +static int init_blkcipher_desc(struct blkcipher_desc *desc, const void *key,
> > + unsigned int key_len, void *iv, 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) {
> > + 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,
> > + void **master_key,
> > + unsigned int *master_keylen)
> > +{
> > + struct key *mkey;
> > +
> > + mkey = request_trusted_key(epayload->master_desc,
> > + master_key, master_keylen);
> > + if (IS_ERR(mkey)) {
> > + mkey = request_user_key(epayload->master_desc,
> > + master_key, master_keylen);
> > + if (IS_ERR(mkey)) {
> > + pr_info("encrypted_key: trusted/user key %s not found",
> > + epayload->master_desc);
> > + return mkey;
> > + }
> > + }
> > + dump_master_key(*master_key, *master_keylen);
> > + return mkey;
> > +}
> > +
> > +/* Before returning data to userspace, encrypt decrypted data. */
> > +static int derived_key_encrypt(struct encrypted_key_payload *epayload,
> > + void *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)
> > + 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)
> > + 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,
> > + void *master_key, unsigned int master_keylen)
> > +{
> > + char derived_key[hash_size];
> > + char *digest;
> > + int ret;
> > +
> > + memset(derived_key, 0, sizeof derived_key);
> > + ret = get_derived_key(derived_key, AUTH_KEY, master_key, master_keylen);
> > + if (ret)
> > + goto out;
> > +
> > + digest = epayload->master_desc + epayload->datablob_len;
> > + ret = calc_hmac(digest, derived_key, sizeof derived_key,
> > + epayload->master_desc, 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,
> > + void *master_key, unsigned int master_keylen)
> > +{
> > + char derived_key[hash_size];
> > + char digest[hash_size];
> > + int ret;
> > +
> > + memset(derived_key, 0, sizeof derived_key);
> > + ret = get_derived_key(derived_key, AUTH_KEY, master_key, master_keylen);
> > + if (ret)
> > + goto out;
> > +
> > + memset(digest, 0, sizeof digest);
> > + ret = calc_hmac(digest, derived_key, sizeof derived_key,
> > + epayload->master_desc, epayload->datablob_len);
> > + if (ret)
> > + goto out;
> > + ret = memcmp(digest, epayload->master_desc + epayload->datablob_len,
> > + sizeof digest);
> > + if (ret) {
> > + dump_hmac("datablob",
> > + epayload->master_desc + epayload->datablob_len,
> > + hash_size);
> > + dump_hmac("calc", digest, hash_size);
> > + }
> > +out:
> > + return ret;
> > +}
> > +
> > +static int derived_key_decrypt(struct encrypted_key_payload *epayload,
> > + void *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)
> > + 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,
> > + (unsigned int)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)
> > + 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 *master_desc,
> > + char *datalen)
> > +{
> > + struct encrypted_key_payload *epayload = NULL;
> > + unsigned short datablob_len;
> > + unsigned short decrypted_datalen;
> > + size_t encrypted_datalen;
> > + long dlen;
> > + int ret;
> > +
> > + ret = strict_strtol(datalen, 10, &dlen);
> > + if (ret < 0 || dlen < 20 || dlen > hash_size)
> > + return ERR_PTR(-EINVAL);
> > +
> > + decrypted_datalen = (unsigned short)dlen;
> > + encrypted_datalen = roundup(decrypted_datalen, blksize);
> > +
> > + datablob_len = strlen(master_desc) + 1 + strlen(datalen) + 1
> > + + ivsize + 1 + encrypted_datalen;
> > +
> > + ret = key_payload_reserve(key, decrypted_datalen + datablob_len);
> > + if (ret < 0)
> > + return ERR_PTR(ret);
> > +
> > + epayload = kzalloc(sizeof(*epayload) + decrypted_datalen +
> > + datablob_len + hash_size + 1, GFP_KERNEL);
> > + if (!epayload)
> > + return ERR_PTR(-ENOMEM);
> > +
> > + epayload->decrypted_datalen = decrypted_datalen;
> > + epayload->datablob_len = datablob_len;
> > + return epayload;
> > +}
> > +
> > +static int encrypted_key_decrypt(struct encrypted_key_payload *epayload,
> > + char *hex_encoded_iv, char *hex_encoded_data)
> > +{
> > + char derived_key[hash_size];
> > + struct key *mkey;
> > + void *master_key;
> > + unsigned int master_keylen;
> > + size_t encrypted_datalen;
> > + char *hmac;
> > + int ret;
> > +
> > + encrypted_datalen = roundup(epayload->decrypted_datalen, blksize);
> > + hex2bin(epayload->iv, hex_encoded_iv, ivsize);
> > + hex2bin(epayload->encrypted_data, hex_encoded_data, encrypted_datalen);
> > +
> > + hmac = epayload->master_desc + 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, master_key, master_keylen);
> > + if (ret) {
> > + pr_err("encrypted_key: bad hmac (%d)\n", ret);
> > + goto out;
> > + }
> > +
> > + memset(derived_key, 0, sizeof derived_key);
> > + ret = get_derived_key(derived_key, ENC_KEY, master_key, master_keylen);
> > + if (ret)
> > + goto out;
> > +
> > + ret = derived_key_decrypt(epayload, derived_key, sizeof derived_key);
> > + if (ret)
> > + pr_err("encrypted_key: failed to decrypt key (%d)\n", ret);
> > +out:
> > + key_put(mkey);
> > + return ret;
> > +}
> > +
> > +static void __ekey_init(struct encrypted_key_payload *epayload,
> > + char *master_desc, char *datalen)
> > +{
> > + epayload->master_desc = epayload->decrypted_data
> > + + epayload->decrypted_datalen;
> > + epayload->datalen = epayload->master_desc + strlen(master_desc) + 1;
> > + epayload->iv = epayload->datalen + strlen(datalen) + 1;
> > + epayload->encrypted_data = epayload->iv + ivsize + 1;
> > +
> > + 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,
> > + char *master_desc, char *datalen,
> > + char *hex_encoded_iv, char *hex_encoded_data)
> > +{
> > + int ret = 0;
> > +
> > + __ekey_init(epayload, master_desc, datalen);
> > + if (!hex_encoded_data) {
> > + get_random_bytes(epayload->iv, ivsize);
> > +
> > + get_random_bytes(epayload->decrypted_data,
> > + epayload->decrypted_datalen);
> > + } else
> > + ret = encrypted_key_decrypt(epayload, hex_encoded_iv,
> > + hex_encoded_data);
> > + 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;
> > + char *master_desc = NULL;
> > + char *decrypted_datalen = NULL;
> > + char *hex_encoded_iv = NULL;
> > + char *hex_encoded_data = NULL;
> > + int ret;
> > +
> > + if (datalen <= 0 || datalen > 32767 || !data)
> > + return -EINVAL;
> > +
> > + datablob = kzalloc(datalen + 1, GFP_KERNEL);
> > + if (!datablob)
> > + return -ENOMEM;
> > +
> > + memcpy(datablob, data, datalen);
> > + ret = datablob_parse(datablob, &master_desc, &decrypted_datalen,
> > + &hex_encoded_iv, &hex_encoded_data);
> > + if (ret < 0)
> > + goto out;
> > +
> > + epayload = encrypted_key_alloc(key, master_desc, decrypted_datalen);
> > + if (IS_ERR(epayload)) {
> > + ret = PTR_ERR(epayload);
> > + goto out;
> > + }
> > + ret = encrypted_init(epayload, master_desc, decrypted_datalen,
> > + hex_encoded_iv, hex_encoded_data);
> > + rcu_assign_pointer(key->payload.data, epayload);
> > +out:
> > + kfree(datablob);
> > + return ret > 0 ? -EINVAL : 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;
> > + int ret = 0;
> > +
> > + if (datalen <= 0 || datalen > 32767 || !data)
> > + return -EINVAL;
> > +
> > + buf = kzalloc(datalen + 1, GFP_KERNEL);
> > + if (!buf)
> > + return -ENOMEM;
> > +
> > + memcpy(buf, data, datalen);
> > + new_master_desc = strsep(&buf, " \t");
> > + if (!*new_master_desc) {
> > + ret = -EINVAL;
> > + goto out;
> > + }
> > +
> > + new_epayload = encrypted_key_alloc(key, new_master_desc,
> > + epayload->datalen);
> > + if (IS_ERR(new_epayload)) {
> > + ret = PTR_ERR(new_epayload);
> > + goto out;
> > + }
> > +
> > + __ekey_init(new_epayload, new_master_desc, epayload->datalen);
> > +
> > + memcpy(new_epayload->iv, epayload->iv, ivsize);
> > + memcpy(new_epayload->decrypted_data, epayload->decrypted_data,
> > + epayload->decrypted_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;
> > + void *master_key;
> > + unsigned int master_keylen;
> > + char derived_key[hash_size];
> > + int asciiblob_len;
> > + int ret;
> > +
> > + epayload = rcu_dereference_protected(key->payload.data,
> > + rwsem_is_locked(&((struct key *)key)->sem));
> > +
> > + /* 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 <= 0)
> > + return asciiblob_len;
> > +
> > + mkey = request_master_key(epayload, &master_key, &master_keylen);
> > + if (IS_ERR(mkey))
> > + return PTR_ERR(mkey);
> > +
> > + memset(derived_key, 0, sizeof derived_key);
> > + ret = get_derived_key(derived_key, ENC_KEY, master_key, master_keylen);
> > + if (ret)
> > + goto out;
> > +
> > + ret = derived_key_encrypt(epayload, derived_key, sizeof derived_key);
> > + if (ret)
> > + goto out;
> > +
> > + ret = datablob_hmac_append(epayload, master_key, master_keylen);
> > + if (ret)
> > + goto out;
> > +
> > + ret = datablob_format(buffer, epayload, asciiblob_len);
> > + if (ret < 0)
> > + goto out;
> > +
> > + key_put(mkey);
> > + return asciiblob_len;
> > +out:
> > + key_put(mkey);
> > + return ret > 0 ? -EINVAL : ret;
> > +}
> > +
> > +/*
> > + * encrypted_destroy - before freeing the key, clear the decrypted data
> > + *
> > + * Before freeing the key, clear the memory containing the descrypted
> > + * 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 int __init init_encrypted(void)
> > +{
> > + int ret;
> > +
> > + ret = register_key_type(&key_type_encrypted);
> > + if (ret < 0)
> > + return ret;
> > + ret = aes_get_sizes(&ivsize, &blksize);
> > + return ret;
> > +}
> > +
> > +static void __exit cleanup_encrypted(void)
> > +{
> > + unregister_key_type(&key_type_encrypted);
> > +}
> > +
> > +module_init(init_encrypted);
> > +module_exit(cleanup_encrypted);
> > +
> > +MODULE_LICENSE("GPL");
> > diff --git a/security/keys/encrypted_defined.h b/security/keys/encrypted_defined.h
> > new file mode 100644
> > index 0000000..4e0b6e5
> > --- /dev/null
> > +++ b/security/keys/encrypted_defined.h
> > @@ -0,0 +1,52 @@
> > +#ifndef __ENCRYPTED_KEY_H
> > +#define __ENCRYPTED_KEY_H
> > +
> > +#define ENCRYPTED_DEBUG 0
> > +
> > +#if ENCRYPTED_DEBUG
> > +static inline void dump_master_key(void *master_key, unsigned int master_keylen)
> > +{
> > + print_hex_dump(KERN_ERR, "master key: ", DUMP_PREFIX_NONE, 32, 1,
> > + master_key, (size_t) 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, (size_t) encrypted_datalen, 0);
> > +}
> > +
> > +static inline void dump_hmac(char *str, void *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,
> > + (size_t) hmac_size, 0);
> > +}
> > +#else
> > +static inline void dump_master_key(void *master_key, unsigned int 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(char *str, void *digest, int hmac_size)
> > +{
> > +}
> > +#endif
> > +#endif
> >



2010-09-29 12:11:09

by David Howells

[permalink] [raw]
Subject: Re: [RFC][PATCH 1/4] lib: hex2bin converts ascii hexadecimal string to binary

Mimi Zohar <[email protected]> wrote:

> --- a/lib/hexdump.c
> +++ b/lib/hexdump.c
> @@ -34,6 +34,22 @@ int hex_to_bin(char ch)
> EXPORT_SYMBOL(hex_to_bin);
>
> /**
> + * hex2bin - convert an ascii hexadecimal string to its binary representation
> + * @mem: result
> + * @buf: ascii hexadecimal string
> + * @count: result length
> + */
> +void hex2bin(unsigned char *mem, char *buf, int count)

Hmmm... It might be better to put this in its own file in lib, otherwise it
will drag in all the other functions in that file if used, and vice versa.

David

2010-09-29 12:40:54

by Roberto Sassu

[permalink] [raw]
Subject: Re: [RFC][PATCH 4/4] keys: add new key-type encrypted

On Wednesday, September 29, 2010 01:57:36 pm Mimi Zohar wrote:
> On Wed, 2010-09-29 at 12:00 +0200, Roberto Sassu wrote:
> > When a new encrypted key is created through the keyctl utility, the master
> > key specified is not searched in the keyring and the operation is performed even
> > if it is missing.
>
> Yes, and why is this a problem? After creating a new key, the first
> thing done should be to save it. At that point, you'd find out the
> master-key doesn't exist, requiring you to either load it or change the
> master-key name using 'keyctl update'.
>

I think the master key verification is important at least for one reason:
i suppose to have one encrypted key which depends on a trusted key unsealed
during the boot process. If the former key is present in the keyring, this does not mean
that the platform is in a good status (condition required for the unsealing), since an attacker
can generate another encryption key with the same name that may be used by users to
encrypt their personal data.
The create check is probably wrong, what i mean is that key generation is an administrative
task and trusted and encrypted keys should be usable only after decryption with the key
they depend to.


> Mimi
>
> > The following patch moves the master key request in the encrypted_init() function,
> > in order to deny a new key creation if the former is not present.
> >
> >
> > Signed-off-by: Roberto Sassu <[email protected]>
> > ---
> > security/keys/encrypted_defined.c | 23 +++++++++++++----------
> > 1 files changed, 13 insertions(+), 10 deletions(-)
> >
> > diff --git a/security/keys/encrypted_defined.c b/security/keys/encrypted_defined.c
> > index 6b26db6..48e627e 100644
> > --- a/security/keys/encrypted_defined.c
> > +++ b/security/keys/encrypted_defined.c
> > @@ -491,12 +491,10 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
> > }
> >
> > static int encrypted_key_decrypt(struct encrypted_key_payload *epayload,
> > - char *hex_encoded_iv, char *hex_encoded_data)
> > + char *hex_encoded_iv, char *hex_encoded_data,
> > + void *master_key, unsigned int master_keylen)
> > {
> > char derived_key[hash_size];
> > - struct key *mkey;
> > - void *master_key;
> > - unsigned int master_keylen;
> > size_t encrypted_datalen;
> > char *hmac;
> > int ret;
> > @@ -508,10 +506,6 @@ static int encrypted_key_decrypt(struct encrypted_key_payload *epayload,
> > hmac = epayload->master_desc + 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, master_key, master_keylen);
> > if (ret) {
> > pr_err("encrypted_key: bad hmac (%d)\n", ret);
> > @@ -527,7 +521,6 @@ static int encrypted_key_decrypt(struct encrypted_key_payload *epayload,
> > if (ret)
> > pr_err("encrypted_key: failed to decrypt key (%d)\n", ret);
> > out:
> > - key_put(mkey);
> > return ret;
> > }
> >
> > @@ -555,8 +548,16 @@ static int encrypted_init(struct encrypted_key_payload *epayload,
> > char *hex_encoded_iv, char *hex_encoded_data)
> > {
> > int ret = 0;
> > + struct key *mkey;
> > + void *master_key;
> > + unsigned int master_keylen;
> >
> > __ekey_init(epayload, master_desc, datalen);
> > +
> > + mkey = request_master_key(epayload, &master_key, &master_keylen);
> > + if (IS_ERR(mkey))
> > + return PTR_ERR(mkey);
> > +
> > if (!hex_encoded_data) {
> > get_random_bytes(epayload->iv, ivsize);
> >
> > @@ -564,7 +565,9 @@ static int encrypted_init(struct encrypted_key_payload *epayload,
> > epayload->decrypted_datalen);
> > } else
> > ret = encrypted_key_decrypt(epayload, hex_encoded_iv,
> > - hex_encoded_data);
> > + hex_encoded_data, master_key, master_keylen);
> > +
> > + key_put(mkey);
> > return ret;
> > }
> >
> > > Defines a new kernel key-type called 'encrypted'. Encrypted keys are
> > > kernel generated random numbers, which are encrypted/decrypted with
> > > a 'trusted' symmetric key. Encrypted keys are created/encrypted/decrypted
> > > in the kernel. Userspace only ever sees/stores encrypted blobs.
> > >
> > > Signed-off-by: Mimi Zohar <[email protected]us.ibm.com>
> > > Signed-off-by: David Safford <[email protected]>
> > > ---
> > > include/keys/encrypted-type.h | 30 ++
> > > security/Kconfig | 17 +
> > > security/keys/Makefile | 1 +
> > > security/keys/encrypted_defined.c | 781 +++++++++++++++++++++++++++++++++++++
> > > security/keys/encrypted_defined.h | 52 +++
> > > 5 files changed, 881 insertions(+), 0 deletions(-)
> > > create mode 100644 include/keys/encrypted-type.h
> > > create mode 100644 security/keys/encrypted_defined.c
> > > create mode 100644 security/keys/encrypted_defined.h
> > >
> > > diff --git a/include/keys/encrypted-type.h b/include/keys/encrypted-type.h
> > > new file mode 100644
> > > index 0000000..e2312e0
> > > --- /dev/null
> > > +++ b/include/keys/encrypted-type.h
> > > @@ -0,0 +1,30 @@
> > > +/* encrypted-type.h: encrypted-defined key type
> > > + *
> > > + * Copyright (C) 2010 IBM Corporation
> > > + * Author: Mimi Zohar <[email protected]>
> > > + *
> > > + * 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_ENCRYPTED_TYPE_H
> > > +#define _KEYS_ENCRYPTED_TYPE_H
> > > +
> > > +#include <linux/key.h>
> > > +#include <linux/rcupdate.h>
> > > +
> > > +struct encrypted_key_payload {
> > > + struct rcu_head rcu; /* RCU destructor */
> > > + char *master_desc; /* datablob: master key name */
> > > + char *datalen; /* datablob: decrypted key length */
> > > + void *iv; /* datablob: iv */
> > > + void *encrypted_data; /* datablob: encrypted key */
> > > + unsigned short datablob_len; /* length of datablob */
> > > + unsigned short decrypted_datalen; /* decrypted data length */
> > > + char decrypted_data[0]; /* decrypted data + datablob + hmac */
> > > +};
> > > +
> > > +extern struct key_type key_type_encrypted;
> > > +
> > > +#endif /* _KEYS_ENCRYPTED_TYPE_H */
> > > diff --git a/security/Kconfig b/security/Kconfig
> > > index f9681e5..b03187d 100644
> > > --- a/security/Kconfig
> > > +++ b/security/Kconfig
> > > @@ -37,6 +37,23 @@ config TRUSTED_KEYS
> > >
> > > If you are unsure as to whether this is required, answer N.
> > >
> > > +config ENCRYPTED_KEYS
> > > + tristate "ENCRYPTED KEYS"
> > > + depends on KEYS && TRUSTED_KEYS
> > > + select LIBCRC32C
> > > + select CONFIG_CRYPTO_AES
> > > + select CRYPTO_HMAC
> > > + select CRYPTO_SHA256
> > > + select BLOCK_CBC
> > > + help
> > > + This option provides support for create/encrypting/decrypting keys
> > > + in the kernel. Encrypted keys are kernel generated random numbers,
> > > + which are encrypted/decrypted with a 'master' symmetric key. The
> > > + 'master' key can be either a trusted-key or user-key type.
> > > + Userspace only ever sees/stores encrypted blobs.
> > > +
> > > + If you are unsure as to whether this is required, answer N.
> > > +
> > > config KEYS_DEBUG_PROC_KEYS
> > > bool "Enable the /proc/keys file by which keys may be viewed"
> > > depends on KEYS
> > > diff --git a/security/keys/Makefile b/security/keys/Makefile
> > > index fcb1070..6c94105 100644
> > > --- a/security/keys/Makefile
> > > +++ b/security/keys/Makefile
> > > @@ -14,6 +14,7 @@ obj-y := \
> > > user_defined.o
> > >
> > > obj-$(CONFIG_TRUSTED_KEYS) += trusted_defined.o
> > > +obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted_defined.o
> > > obj-$(CONFIG_KEYS_COMPAT) += compat.o
> > > obj-$(CONFIG_PROC_FS) += proc.o
> > > obj-$(CONFIG_SYSCTL) += sysctl.o
> > > diff --git a/security/keys/encrypted_defined.c b/security/keys/encrypted_defined.c
> > > new file mode 100644
> > > index 0000000..6b26db6
> > > --- /dev/null
> > > +++ b/security/keys/encrypted_defined.c
> > > @@ -0,0 +1,781 @@
> > > +/*
> > > + * Copyright (C) 2010 IBM Corporation
> > > + *
> > > + * Author:
> > > + * Mimi Zohar <[email protected]>
> > > + *
> > > + * 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.
> > > + *
> > > + * File: encrypted_defined.c
> > > + *
> > > + * Defines a new kernel key-type called 'encrypted'. Encrypted keys
> > > + * are kernel generated random numbers, which are encrypted/decrypted
> > > + * using a 'master' key. The 'master' key can either be a trusted-key or
> > > + * user-key type. Encrypted keys are created/encrypted/decrypted in the
> > > + * kernel. Userspace ever only sees/stores encrypted blobs.
> > > + *
> > > + * keyctl add "encrypted" "name" "NEW master-key-name keylen" ring
> > > + * keyctl add "encrypted" "name" "LOAD master-key-name keylen hex_blob" ring
> > > + * keyctl update keyid "UPDATE master-key-name"
> > > + */
> > > +
> > > +#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 <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 <crypto/sha.h>
> > > +#include <crypto/aes.h>
> > > +
> > > +#include "encrypted_defined.h"
> > > +
> > > +static char hash_alg[] = "sha256";
> > > +static char hmac_alg[] = "hmac(sha256)";
> > > +static int hash_size = SHA256_DIGEST_SIZE;
> > > +static char blkcipher_alg[] = "cbc(aes)";
> > > +static int ivsize;
> > > +static int blksize;
> > > +
> > > +static int aes_get_sizes(int *ivsize, int *blksize)
> > > +{
> > > + 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;
> > > +}
> > > +
> > > +enum {
> > > + Opt_err = -1, Opt_new = 1, Opt_load, Opt_NEW, Opt_LOAD
> > > +};
> > > +
> > > +static match_table_t key_tokens = {
> > > + {Opt_new, "new"},
> > > + {Opt_NEW, "NEW"},
> > > + {Opt_load, "load"},
> > > + {Opt_LOAD, "LOAD"},
> > > + {Opt_err, NULL}
> > > +};
> > > +
> > > +/*
> > > + * datablob_parse - parse the keyctl data
> > > + *
> > > + * datablob format:
> > > + * NEW <master-key name> <decrypted data length>
> > > + * LOAD <master-key name> <decrypted data length> <encrypted iv + data>
> > > + *
> > > + * 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, char **master_desc,
> > > + char **decrypted_datalen, char **hex_encoded_iv,
> > > + char **hex_encoded_data)
> > > +{
> > > + substring_t args[MAX_OPT_ARGS];
> > > + int ret = -EINVAL;
> > > + int key_cmd;
> > > + char *p;
> > > +
> > > + p = strsep(&datablob, " \t");
> > > + if (!p)
> > > + return ret;
> > > + key_cmd = match_token(p, key_tokens, args);
> > > +
> > > + *master_desc = strsep(&datablob, " \t");
> > > + if (!*master_desc)
> > > + goto out;
> > > + *decrypted_datalen = strsep(&datablob, " \t");
> > > + if (!*decrypted_datalen)
> > > + goto out;
> > > +
> > > + switch (key_cmd) {
> > > + case Opt_new:
> > > + case Opt_NEW:
> > > + ret = 0;
> > > + break;
> > > + case Opt_load:
> > > + case Opt_LOAD:
> > > + *hex_encoded_iv = strsep(&datablob, " \t");
> > > + if (!*hex_encoded_iv)
> > > + break;
> > > + *hex_encoded_data = *hex_encoded_iv + (2 * ivsize) + 2;
> > > + ret = 0;
> > > + break;
> > > + case Opt_err:
> > > + break;
> > > + }
> > > +out:
> > > + return ret;
> > > +}
> > > +
> > > +/* datablob_format - format as an ascii string, before copying to userspace */
> > > +static int datablob_format(char __user *buffer,
> > > + struct encrypted_key_payload *epayload,
> > > + int asciiblob_len)
> > > +{
> > > + char *ascii_buf, *bufp;
> > > + char *iv = (char *)epayload->iv;
> > > + int ret = 0;
> > > + int len;
> > > + int i;
> > > +
> > > + ascii_buf = kzalloc(asciiblob_len + 1, GFP_KERNEL);
> > > + if (!ascii_buf)
> > > + return -ENOMEM;
> > > +
> > > + len = sprintf(ascii_buf, "%s %s ", 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]);
> > > +
> > > + if (copy_to_user(buffer, ascii_buf, asciiblob_len) != 0)
> > > + ret = -EFAULT;
> > > + kfree(ascii_buf);
> > > + return ret;
> > > +}
> > > +
> > > +/*
> > > + * 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(char *trusted_desc, void **master_key,
> > > + unsigned int *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;
> > > +
> > > + tpayload = 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(char *master_desc, void **master_key,
> > > + unsigned int *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;
> > > +
> > > + upayload = ukey->payload.data;
> > > + *master_key = upayload->data;
> > > + *master_keylen = (unsigned int)upayload->datalen;
> > > +error:
> > > + return ukey;
> > > +}
> > > +
> > > +static int init_desc(struct hash_desc *desc, char *alg)
> > > +{
> > > + int ret;
> > > +
> > > + desc->tfm = crypto_alloc_hash(alg, 0, CRYPTO_ALG_ASYNC);
> > > + if (IS_ERR(desc->tfm)) {
> > > + pr_info("encrypted_key: failed to load %s transform: %ld\n",
> > > + alg, PTR_ERR(desc->tfm));
> > > + ret = PTR_ERR(desc->tfm);
> > > + return ret;
> > > + }
> > > + desc->flags = 0;
> > > + ret = crypto_hash_init(desc);
> > > + if (ret)
> > > + crypto_free_hash(desc->tfm);
> > > + return ret;
> > > +}
> > > +
> > > +static int calc_hmac(char *digest, char *key, int keylen,
> > > + char *buf, size_t buflen)
> > > +{
> > > + struct hash_desc desc;
> > > + struct scatterlist sg[1];
> > > + int ret;
> > > +
> > > + ret = init_desc(&desc, hmac_alg);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + crypto_hash_setkey(desc.tfm, key, keylen);
> > > + ret = crypto_hash_init(&desc);
> > > + if (ret)
> > > + goto out;
> > > +
> > > + sg_init_one(sg, buf, buflen);
> > > + ret = crypto_hash_update(&desc, sg, buflen);
> > > + if (!ret)
> > > + ret = crypto_hash_final(&desc, digest);
> > > +out:
> > > + crypto_free_hash(desc.tfm);
> > > + return ret;
> > > +}
> > > +
> > > +static int calc_hash(char *digest, void *buf, int buflen)
> > > +{
> > > + struct hash_desc desc;
> > > + struct scatterlist sg[1];
> > > + int ret;
> > > +
> > > + ret = init_desc(&desc, hash_alg);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + sg_init_one(sg, buf, buflen);
> > > + ret = crypto_hash_update(&desc, sg, buflen);
> > > + if (!ret)
> > > + ret = crypto_hash_final(&desc, digest);
> > > + crypto_free_hash(desc.tfm);
> > > + return ret;
> > > +}
> > > +
> > > +enum derived_key_type { ENC_KEY, AUTH_KEY };
> > > +
> > > +/* Derive authentication/encryption key from trusted key */
> > > +static int get_derived_key(char *derived_key, enum derived_key_type key_type,
> > > + void *master_key, unsigned int master_keylen)
> > > +{
> > > + char derived_buf[hash_size + 10];
> > > + int ret;
> > > +
> > > + memset(derived_buf, 0, sizeof derived_buf);
> > > + 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, sizeof derived_buf);
> > > + return ret;
> > > +}
> > > +
> > > +static int init_blkcipher_desc(struct blkcipher_desc *desc, const void *key,
> > > + unsigned int key_len, void *iv, 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) {
> > > + 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,
> > > + void **master_key,
> > > + unsigned int *master_keylen)
> > > +{
> > > + struct key *mkey;
> > > +
> > > + mkey = request_trusted_key(epayload->master_desc,
> > > + master_key, master_keylen);
> > > + if (IS_ERR(mkey)) {
> > > + mkey = request_user_key(epayload->master_desc,
> > > + master_key, master_keylen);
> > > + if (IS_ERR(mkey)) {
> > > + pr_info("encrypted_key: trusted/user key %s not found",
> > > + epayload->master_desc);
> > > + return mkey;
> > > + }
> > > + }
> > > + dump_master_key(*master_key, *master_keylen);
> > > + return mkey;
> > > +}
> > > +
> > > +/* Before returning data to userspace, encrypt decrypted data. */
> > > +static int derived_key_encrypt(struct encrypted_key_payload *epayload,
> > > + void *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)
> > > + 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)
> > > + 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,
> > > + void *master_key, unsigned int master_keylen)
> > > +{
> > > + char derived_key[hash_size];
> > > + char *digest;
> > > + int ret;
> > > +
> > > + memset(derived_key, 0, sizeof derived_key);
> > > + ret = get_derived_key(derived_key, AUTH_KEY, master_key, master_keylen);
> > > + if (ret)
> > > + goto out;
> > > +
> > > + digest = epayload->master_desc + epayload->datablob_len;
> > > + ret = calc_hmac(digest, derived_key, sizeof derived_key,
> > > + epayload->master_desc, 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,
> > > + void *master_key, unsigned int master_keylen)
> > > +{
> > > + char derived_key[hash_size];
> > > + char digest[hash_size];
> > > + int ret;
> > > +
> > > + memset(derived_key, 0, sizeof derived_key);
> > > + ret = get_derived_key(derived_key, AUTH_KEY, master_key, master_keylen);
> > > + if (ret)
> > > + goto out;
> > > +
> > > + memset(digest, 0, sizeof digest);
> > > + ret = calc_hmac(digest, derived_key, sizeof derived_key,
> > > + epayload->master_desc, epayload->datablob_len);
> > > + if (ret)
> > > + goto out;
> > > + ret = memcmp(digest, epayload->master_desc + epayload->datablob_len,
> > > + sizeof digest);
> > > + if (ret) {
> > > + dump_hmac("datablob",
> > > + epayload->master_desc + epayload->datablob_len,
> > > + hash_size);
> > > + dump_hmac("calc", digest, hash_size);
> > > + }
> > > +out:
> > > + return ret;
> > > +}
> > > +
> > > +static int derived_key_decrypt(struct encrypted_key_payload *epayload,
> > > + void *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)
> > > + 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,
> > > + (unsigned int)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)
> > > + 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 *master_desc,
> > > + char *datalen)
> > > +{
> > > + struct encrypted_key_payload *epayload = NULL;
> > > + unsigned short datablob_len;
> > > + unsigned short decrypted_datalen;
> > > + size_t encrypted_datalen;
> > > + long dlen;
> > > + int ret;
> > > +
> > > + ret = strict_strtol(datalen, 10, &dlen);
> > > + if (ret < 0 || dlen < 20 || dlen > hash_size)
> > > + return ERR_PTR(-EINVAL);
> > > +
> > > + decrypted_datalen = (unsigned short)dlen;
> > > + encrypted_datalen = roundup(decrypted_datalen, blksize);
> > > +
> > > + datablob_len = strlen(master_desc) + 1 + strlen(datalen) + 1
> > > + + ivsize + 1 + encrypted_datalen;
> > > +
> > > + ret = key_payload_reserve(key, decrypted_datalen + datablob_len);
> > > + if (ret < 0)
> > > + return ERR_PTR(ret);
> > > +
> > > + epayload = kzalloc(sizeof(*epayload) + decrypted_datalen +
> > > + datablob_len + hash_size + 1, GFP_KERNEL);
> > > + if (!epayload)
> > > + return ERR_PTR(-ENOMEM);
> > > +
> > > + epayload->decrypted_datalen = decrypted_datalen;
> > > + epayload->datablob_len = datablob_len;
> > > + return epayload;
> > > +}
> > > +
> > > +static int encrypted_key_decrypt(struct encrypted_key_payload *epayload,
> > > + char *hex_encoded_iv, char *hex_encoded_data)
> > > +{
> > > + char derived_key[hash_size];
> > > + struct key *mkey;
> > > + void *master_key;
> > > + unsigned int master_keylen;
> > > + size_t encrypted_datalen;
> > > + char *hmac;
> > > + int ret;
> > > +
> > > + encrypted_datalen = roundup(epayload->decrypted_datalen, blksize);
> > > + hex2bin(epayload->iv, hex_encoded_iv, ivsize);
> > > + hex2bin(epayload->encrypted_data, hex_encoded_data, encrypted_datalen);
> > > +
> > > + hmac = epayload->master_desc + 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, master_key, master_keylen);
> > > + if (ret) {
> > > + pr_err("encrypted_key: bad hmac (%d)\n", ret);
> > > + goto out;
> > > + }
> > > +
> > > + memset(derived_key, 0, sizeof derived_key);
> > > + ret = get_derived_key(derived_key, ENC_KEY, master_key, master_keylen);
> > > + if (ret)
> > > + goto out;
> > > +
> > > + ret = derived_key_decrypt(epayload, derived_key, sizeof derived_key);
> > > + if (ret)
> > > + pr_err("encrypted_key: failed to decrypt key (%d)\n", ret);
> > > +out:
> > > + key_put(mkey);
> > > + return ret;
> > > +}
> > > +
> > > +static void __ekey_init(struct encrypted_key_payload *epayload,
> > > + char *master_desc, char *datalen)
> > > +{
> > > + epayload->master_desc = epayload->decrypted_data
> > > + + epayload->decrypted_datalen;
> > > + epayload->datalen = epayload->master_desc + strlen(master_desc) + 1;
> > > + epayload->iv = epayload->datalen + strlen(datalen) + 1;
> > > + epayload->encrypted_data = epayload->iv + ivsize + 1;
> > > +
> > > + 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,
> > > + char *master_desc, char *datalen,
> > > + char *hex_encoded_iv, char *hex_encoded_data)
> > > +{
> > > + int ret = 0;
> > > +
> > > + __ekey_init(epayload, master_desc, datalen);
> > > + if (!hex_encoded_data) {
> > > + get_random_bytes(epayload->iv, ivsize);
> > > +
> > > + get_random_bytes(epayload->decrypted_data,
> > > + epayload->decrypted_datalen);
> > > + } else
> > > + ret = encrypted_key_decrypt(epayload, hex_encoded_iv,
> > > + hex_encoded_data);
> > > + 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;
> > > + char *master_desc = NULL;
> > > + char *decrypted_datalen = NULL;
> > > + char *hex_encoded_iv = NULL;
> > > + char *hex_encoded_data = NULL;
> > > + int ret;
> > > +
> > > + if (datalen <= 0 || datalen > 32767 || !data)
> > > + return -EINVAL;
> > > +
> > > + datablob = kzalloc(datalen + 1, GFP_KERNEL);
> > > + if (!datablob)
> > > + return -ENOMEM;
> > > +
> > > + memcpy(datablob, data, datalen);
> > > + ret = datablob_parse(datablob, &master_desc, &decrypted_datalen,
> > > + &hex_encoded_iv, &hex_encoded_data);
> > > + if (ret < 0)
> > > + goto out;
> > > +
> > > + epayload = encrypted_key_alloc(key, master_desc, decrypted_datalen);
> > > + if (IS_ERR(epayload)) {
> > > + ret = PTR_ERR(epayload);
> > > + goto out;
> > > + }
> > > + ret = encrypted_init(epayload, master_desc, decrypted_datalen,
> > > + hex_encoded_iv, hex_encoded_data);
> > > + rcu_assign_pointer(key->payload.data, epayload);
> > > +out:
> > > + kfree(datablob);
> > > + return ret > 0 ? -EINVAL : 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;
> > > + int ret = 0;
> > > +
> > > + if (datalen <= 0 || datalen > 32767 || !data)
> > > + return -EINVAL;
> > > +
> > > + buf = kzalloc(datalen + 1, GFP_KERNEL);
> > > + if (!buf)
> > > + return -ENOMEM;
> > > +
> > > + memcpy(buf, data, datalen);
> > > + new_master_desc = strsep(&buf, " \t");
> > > + if (!*new_master_desc) {
> > > + ret = -EINVAL;
> > > + goto out;
> > > + }
> > > +
> > > + new_epayload = encrypted_key_alloc(key, new_master_desc,
> > > + epayload->datalen);
> > > + if (IS_ERR(new_epayload)) {
> > > + ret = PTR_ERR(new_epayload);
> > > + goto out;
> > > + }
> > > +
> > > + __ekey_init(new_epayload, new_master_desc, epayload->datalen);
> > > +
> > > + memcpy(new_epayload->iv, epayload->iv, ivsize);
> > > + memcpy(new_epayload->decrypted_data, epayload->decrypted_data,
> > > + epayload->decrypted_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;
> > > + void *master_key;
> > > + unsigned int master_keylen;
> > > + char derived_key[hash_size];
> > > + int asciiblob_len;
> > > + int ret;
> > > +
> > > + epayload = rcu_dereference_protected(key->payload.data,
> > > + rwsem_is_locked(&((struct key *)key)->sem));
> > > +
> > > + /* 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 <= 0)
> > > + return asciiblob_len;
> > > +
> > > + mkey = request_master_key(epayload, &master_key, &master_keylen);
> > > + if (IS_ERR(mkey))
> > > + return PTR_ERR(mkey);
> > > +
> > > + memset(derived_key, 0, sizeof derived_key);
> > > + ret = get_derived_key(derived_key, ENC_KEY, master_key, master_keylen);
> > > + if (ret)
> > > + goto out;
> > > +
> > > + ret = derived_key_encrypt(epayload, derived_key, sizeof derived_key);
> > > + if (ret)
> > > + goto out;
> > > +
> > > + ret = datablob_hmac_append(epayload, master_key, master_keylen);
> > > + if (ret)
> > > + goto out;
> > > +
> > > + ret = datablob_format(buffer, epayload, asciiblob_len);
> > > + if (ret < 0)
> > > + goto out;
> > > +
> > > + key_put(mkey);
> > > + return asciiblob_len;
> > > +out:
> > > + key_put(mkey);
> > > + return ret > 0 ? -EINVAL : ret;
> > > +}
> > > +
> > > +/*
> > > + * encrypted_destroy - before freeing the key, clear the decrypted data
> > > + *
> > > + * Before freeing the key, clear the memory containing the descrypted
> > > + * 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 int __init init_encrypted(void)
> > > +{
> > > + int ret;
> > > +
> > > + ret = register_key_type(&key_type_encrypted);
> > > + if (ret < 0)
> > > + return ret;
> > > + ret = aes_get_sizes(&ivsize, &blksize);
> > > + return ret;
> > > +}
> > > +
> > > +static void __exit cleanup_encrypted(void)
> > > +{
> > > + unregister_key_type(&key_type_encrypted);
> > > +}
> > > +
> > > +module_init(init_encrypted);
> > > +module_exit(cleanup_encrypted);
> > > +
> > > +MODULE_LICENSE("GPL");
> > > diff --git a/security/keys/encrypted_defined.h b/security/keys/encrypted_defined.h
> > > new file mode 100644
> > > index 0000000..4e0b6e5
> > > --- /dev/null
> > > +++ b/security/keys/encrypted_defined.h
> > > @@ -0,0 +1,52 @@
> > > +#ifndef __ENCRYPTED_KEY_H
> > > +#define __ENCRYPTED_KEY_H
> > > +
> > > +#define ENCRYPTED_DEBUG 0
> > > +
> > > +#if ENCRYPTED_DEBUG
> > > +static inline void dump_master_key(void *master_key, unsigned int master_keylen)
> > > +{
> > > + print_hex_dump(KERN_ERR, "master key: ", DUMP_PREFIX_NONE, 32, 1,
> > > + master_key, (size_t) 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, (size_t) encrypted_datalen, 0);
> > > +}
> > > +
> > > +static inline void dump_hmac(char *str, void *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,
> > > + (size_t) hmac_size, 0);
> > > +}
> > > +#else
> > > +static inline void dump_master_key(void *master_key, unsigned int 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(char *str, void *digest, int hmac_size)
> > > +{
> > > +}
> > > +#endif
> > > +#endif
> > >
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>


Attachments:
smime.p7s (4.60 kB)

2010-09-29 13:43:55

by Mimi Zohar

[permalink] [raw]
Subject: Re: [RFC][PATCH 4/4] keys: add new key-type encrypted

On Wed, 2010-09-29 at 14:40 +0200, Roberto Sassu wrote:
> On Wednesday, September 29, 2010 01:57:36 pm Mimi Zohar wrote:
> > On Wed, 2010-09-29 at 12:00 +0200, Roberto Sassu wrote:
> > > When a new encrypted key is created through the keyctl utility, the master
> > > key specified is not searched in the keyring and the operation is performed even
> > > if it is missing.
> >
> > Yes, and why is this a problem? After creating a new key, the first
> > thing done should be to save it. At that point, you'd find out the
> > master-key doesn't exist, requiring you to either load it or change the
> > master-key name using 'keyctl update'.
> >
>
> I think the master key verification is important at least for one reason:
> i suppose to have one encrypted key which depends on a trusted key unsealed
> during the boot process.

Yes, this would be the normal scenario - decrypting the encrypted key
with a trusted key.

> If the former key is present in the keyring, this does not mean
> that the platform is in a good status (condition required for the unsealing),
> since an attacker can generate another encryption key with the same name that
> may be used by users to encrypt their personal data.

True, the existence of an encrypted key with the expected name does not
imply anything about the key. However, using this 'fake' key will fail
to decrypt anything already encrypted with the real key. I'm not clear
how requiring the existence of the master-key prevents creating a 'fake'
key.

> The create check is probably wrong, what i mean is that key generation is an
> administrative task and trusted and encrypted keys should be usable only after
> decryption with the key they depend to.

I understand your concern, but checking the existence of the master-key
at creation doesn't imply that it will exist later, when needed to save
the new key. So immediately after creating a new key, it's important to
save both the master-key and the encrypted key.

Mimi

2010-09-29 13:51:38

by Mimi Zohar

[permalink] [raw]
Subject: Re: [RFC][PATCH 1/4] lib: hex2bin converts ascii hexadecimal string to binary

On Wed, 2010-09-29 at 13:11 +0100, David Howells wrote:
> Mimi Zohar <[email protected]> wrote:
>
> > --- a/lib/hexdump.c
> > +++ b/lib/hexdump.c
> > @@ -34,6 +34,22 @@ int hex_to_bin(char ch)
> > EXPORT_SYMBOL(hex_to_bin);
> >
> > /**
> > + * hex2bin - convert an ascii hexadecimal string to its binary representation
> > + * @mem: result
> > + * @buf: ascii hexadecimal string
> > + * @count: result length
> > + */
> > +void hex2bin(unsigned char *mem, char *buf, int count)
>
> Hmmm... It might be better to put this in its own file in lib, otherwise it
> will drag in all the other functions in that file if used, and vice versa.
>
> David

hex2bin calls hex_to_bin() which is defined right above it in the same
file. But the reverse, as you suggested, is still true.

thanks,

Mimi