From: Mimi Zohar Subject: Re: [RFC][PATCH 4/4] keys: add new key-type encrypted Date: Wed, 29 Sep 2010 07:57:36 -0400 Message-ID: <1285761456.4350.11.camel@localhost.localdomain> References: <1285698993-16927-1-git-send-email-zohar@linux.vnet.ibm.com> <1285698993-16927-5-git-send-email-zohar@linux.vnet.ibm.com> <201009291200.47199.roberto.sassu@polito.it> Mime-Version: 1.0 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: 7bit Cc: linux-security-module@vger.kernel.org, keyrings@linux-nfs.org, linux-crypto@vger.kernel.org, David Howells , David Safford , Rajiv Andrade , Mimi Zohar To: Roberto Sassu Return-path: In-Reply-To: <201009291200.47199.roberto.sassu@polito.it> Sender: linux-security-module-owner@vger.kernel.org List-Id: linux-crypto.vger.kernel.org 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 > --- > 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 > > Signed-off-by: David Safford > > --- > > 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 > > + * > > + * 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 > > +#include > > + > > +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 > > + * > > + * 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 > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +#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 > > + * LOAD > > + * > > + * 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: > > + * > > + * > > + * 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 > >