[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20180805032119.20485-5-jlee@suse.com>
Date: Sun, 5 Aug 2018 11:21:17 +0800
From: "Lee, Chun-Yi" <joeyli.kernel@...il.com>
To: linux-kernel@...r.kernel.org
Cc: linux-efi@...r.kernel.org, x86@...nel.org,
keyrings@...r.kernel.org, linux-integrity@...r.kernel.org,
"Lee, Chun-Yi" <jlee@...e.com>, Kees Cook <keescook@...omium.org>,
Thomas Gleixner <tglx@...utronix.de>,
Ingo Molnar <mingo@...hat.com>,
"H. Peter Anvin" <hpa@...or.com>,
"Rafael J. Wysocki" <rafael.j.wysocki@...el.com>,
Pavel Machek <pavel@....cz>, Chen Yu <yu.c.chen@...el.com>,
Oliver Neukum <oneukum@...e.com>,
Ryan Chen <yu.chen.surf@...il.com>,
Ard Biesheuvel <ard.biesheuvel@...aro.org>,
David Howells <dhowells@...hat.com>,
Mimi Zohar <zohar@...ux.vnet.ibm.com>
Subject: [PATCH 4/6] key: add EFI secure key type
EFI secure key is a new key type that the key is encrypted and
authenticated by the EFI root key. The ERK (EFI root key) is
generated by EFI boot stub and be stored in EFI boot services
variable before ExitBootServices be called.
When user enabled the secure boot in firmware, the EFI root key is
secure which means that the encrypted EFI secure key is also secure.
A EFI secure key is generated by kernel and can be keep by user
space. It is also a new master key type like trusted key(TPM) or
user key. The EFI key can be used by hibernation encryption and
authentication. And it can also be a master key to generate a
encrypted key for EVM.
The layout of the efi key payload is:
-+-+-----------------------+
| | |
| | |
| | |
key_len->| | key |
| | |
| | |
-+-+-----------------------+----+-
| | key_len | |
| |(decrypted key length) | |
| | (string) | |
| | | |
| +-----------------------| |
datablob_len->| | | |
| | ERK hash | datablob
| | | |
| +-----------------------| |
| | iv | |
| |(initialization vector)| |
| +-----------------------| |
| | | |
| | | |
| | | |
| | encrypted_key | |
| | | |
| | | |
-+-+-----------------------+----+-
| hmac |
|(signature of datablob)|
+-----------------------+
The datablob can be exported to user space with hmac.
As other key type, EFI key can be created by keyctl tool:
e.g. keyctl add efi key-name "new 128" @u
EFI secure key can also be dumped to a file by keyctl:
e.g. keyctl pipe $NUMBER > ~/tmp/key-name.blob
The $NUMBER is the key serial number.
Enroll the key blob back to kernel:
e.g. keyctl add efi key-name "load `cat ~/tmp/key-name.blob`" @u
Cc: Kees Cook <keescook@...omium.org>
Cc: Thomas Gleixner <tglx@...utronix.de>
Cc: Ingo Molnar <mingo@...hat.com>
Cc: "H. Peter Anvin" <hpa@...or.com>
Cc: "Rafael J. Wysocki" <rafael.j.wysocki@...el.com>
Cc: Pavel Machek <pavel@....cz>
Cc: Chen Yu <yu.c.chen@...el.com>
Cc: Oliver Neukum <oneukum@...e.com>
Cc: Ryan Chen <yu.chen.surf@...il.com>
Cc: Ard Biesheuvel <ard.biesheuvel@...aro.org>
Cc: David Howells <dhowells@...hat.com>
Cc: Mimi Zohar <zohar@...ux.vnet.ibm.com>
Signed-off-by: "Lee, Chun-Yi" <jlee@...e.com>
---
drivers/firmware/efi/efi-secure-key.c | 636 ++++++++++++++++++++++++++++++++++
include/keys/efi-type.h | 50 +++
2 files changed, 686 insertions(+)
create mode 100644 include/keys/efi-type.h
diff --git a/drivers/firmware/efi/efi-secure-key.c b/drivers/firmware/efi/efi-secure-key.c
index e56d7d176e03..5e72a8c9e13e 100644
--- a/drivers/firmware/efi/efi-secure-key.c
+++ b/drivers/firmware/efi/efi-secure-key.c
@@ -11,6 +11,16 @@
#include <linux/efi.h>
#include <linux/memblock.h>
#include <linux/security.h>
+#include <linux/key-type.h>
+#include <linux/parser.h>
+#include <linux/random.h>
+#include <linux/scatterlist.h>
+#include <keys/efi-type.h>
+#include <keys/user-type.h>
+#include <crypto/algapi.h>
+#include <crypto/hash.h>
+#include <crypto/sha.h>
+#include <crypto/skcipher.h>
static u8 root_key[ROOT_KEY_SIZE];
static unsigned long rkey_size;
@@ -64,3 +74,629 @@ void __init parse_efi_root_key_setup(u64 phys_addr, u32 data_len)
sizeof(struct setup_data) + sizeof(struct efi_rkey_setup_data));
early_iounmap(setup_data, data_len);
}
+
+#define ERK_HASH_SIZE SHA256_DIGEST_SIZE
+#define HMAC_HASH_SIZE SHA256_DIGEST_SIZE
+#define DKEY_SIZE SHA256_DIGEST_SIZE
+
+struct key_type key_type_efi;
+
+static const char hash_alg[] = "sha256";
+static const char hmac_alg[] = "hmac(sha256)";
+static struct crypto_shash *hash_tfm;
+
+static int calc_hash(struct crypto_shash *tfm, const u8 *buf,
+ unsigned int buflen, u8 *digest)
+{
+ SHASH_DESC_ON_STACK(desc, tfm);
+ int ret;
+
+ desc->tfm = tfm;
+ desc->flags = 0;
+
+ ret = crypto_shash_digest(desc, buf, buflen, digest);
+ shash_desc_zero(desc);
+
+ return ret;
+}
+
+static int get_derived_key(const char *salt, u8 *derived_key)
+{
+ u8 *derived_buf;
+ unsigned int derived_buf_len;
+ int ret;
+
+ derived_buf_len = strlen(salt) + 1 + ROOT_KEY_SIZE;
+ if (derived_buf_len < DKEY_SIZE)
+ derived_buf_len = DKEY_SIZE;
+
+ derived_buf = kzalloc(derived_buf_len, GFP_KERNEL);
+ if (!derived_buf)
+ return -ENOMEM;
+
+ memcpy(derived_buf + strlen(derived_buf) + 1, root_key,
+ ROOT_KEY_SIZE);
+ ret = calc_hash(hash_tfm, derived_buf, derived_buf_len, derived_key);
+ memzero_explicit(derived_buf, derived_buf_len);
+
+ return ret;
+}
+
+static int calc_hmac(const u8 *buf, unsigned int buflen, u8 *digest)
+{
+ struct crypto_shash *tfm;
+ u8 *auth_key;
+ int ret;
+
+ auth_key = kzalloc(DKEY_SIZE, GFP_KERNEL);
+ if (!auth_key)
+ return -ENOMEM;
+
+ tfm = crypto_alloc_shash(hmac_alg, 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(tfm)) {
+ pr_err("can't alloc %s transform: %ld\n",
+ hmac_alg, PTR_ERR(tfm));
+ ret = PTR_ERR(tfm);
+ goto tfm_fail;
+ }
+
+ ret = get_derived_key("AUTH_KEY", auth_key);
+ if (ret)
+ goto key_fail;
+
+ ret = crypto_shash_setkey(tfm, auth_key, DKEY_SIZE);
+ if (!ret)
+ ret = calc_hash(tfm, buf, buflen, digest);
+
+key_fail:
+ crypto_free_shash(tfm);
+tfm_fail:
+ memzero_explicit(auth_key, DKEY_SIZE);
+
+ return ret;
+}
+
+static const char blkcipher_alg[] = "cbc(aes)";
+static unsigned int ivsize;
+static int blksize;
+
+static int set_aes_sizes(void)
+{
+ struct crypto_skcipher *tfm;
+
+ tfm = crypto_alloc_skcipher(blkcipher_alg, 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(tfm)) {
+ pr_err("failed to alloc_cipher (%ld)\n", PTR_ERR(tfm));
+ return PTR_ERR(tfm);
+ }
+ ivsize = crypto_skcipher_ivsize(tfm);
+ blksize = crypto_skcipher_blocksize(tfm);
+ crypto_free_skcipher(tfm);
+
+ return 0;
+}
+
+enum {
+ Opt_err = -1,
+ Opt_new, Opt_load,
+};
+
+static const match_table_t key_tokens = {
+ {Opt_new, "new"},
+ {Opt_load, "load"},
+ {Opt_err, NULL}
+};
+
+static struct efi_key_payload *efi_payload_alloc(struct key *key, char *key_len_str)
+{
+ struct efi_key_payload *ekp = NULL;
+ unsigned short encrypted_keylen;
+ unsigned short datablob_len;
+ unsigned short payload_len;
+ long key_len;
+ int ret;
+
+ ret = kstrtol(key_len_str, 10, &key_len);
+ if (ret < 0)
+ return ERR_PTR(ret);
+ encrypted_keylen = roundup(key_len, blksize);
+
+ /* efi_key_payload + key + datablob + hmac */
+ datablob_len = strlen(key_len_str) + 1 + ERK_HASH_SIZE + ivsize + encrypted_keylen;
+ payload_len = sizeof(*ekp) + key_len + datablob_len + HMAC_HASH_SIZE;
+
+ ret = key_payload_reserve(key, payload_len);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ ekp = kzalloc(payload_len, GFP_KERNEL);
+ if (!ekp)
+ return ERR_PTR(-ENOMEM);
+ ekp->key = ekp->payload_data;
+ ekp->datablob = ekp->key + key_len;
+ ekp->key_len_str = ekp->datablob;
+ ekp->erk_hash = ekp->key_len_str + strlen(key_len_str) + 1;
+ ekp->iv = ekp->erk_hash + ERK_HASH_SIZE;
+ ekp->encrypted_key = ekp->iv + ivsize;
+ ekp->hmac = ekp->encrypted_key + encrypted_keylen;
+ ekp->key_len = key_len;
+ ekp->datablob_len = datablob_len;
+
+ memcpy(ekp->key_len_str, key_len_str, strlen(key_len_str));
+
+ return ekp;
+}
+
+/*
+ * datablob_parse - parse the keyctl data and fill in the
+ * payload and options structures
+ *
+ * On success returns command number, otherwise -EINVAL.
+ */
+static int datablob_parse(char *datablob, char **key_len_str, char **hex_encoded_blob)
+{
+ substring_t args[MAX_OPT_ARGS];
+ long key_len;
+ int key_cmd;
+ int ret;
+ char *c;
+
+ /* main command */
+ c = strsep(&datablob, " \t");
+ if (!c)
+ return -EINVAL;
+ key_cmd = match_token(c, key_tokens, args);
+
+ /* first string argument is key length */
+ c = strsep(&datablob, " \t");
+ if (!c)
+ return -EINVAL;
+ *key_len_str = c;
+ ret = kstrtol(*key_len_str, 10, &key_len);
+ if (ret < 0 || key_len < MIN_KEY_SIZE || key_len > MAX_KEY_SIZE)
+ return -EINVAL;
+
+ switch (key_cmd) {
+ case Opt_new:
+ ret = Opt_new;
+ break;
+ case Opt_load:
+ *hex_encoded_blob = strsep(&datablob, " \t");
+ if (!*hex_encoded_blob) {
+ pr_info("hex blob is missing\n");
+ return -EINVAL;
+ }
+ if (strlen(*hex_encoded_blob) / 2 > MAX_BLOB_SIZE)
+ return -EINVAL;
+ ret = Opt_load;
+ break;
+ case Opt_err:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int key_encrypt(struct efi_key_payload *ekp, size_t encrypted_keylen)
+{
+ struct scatterlist src[1], dst[1];
+ struct crypto_skcipher *tfm;
+ struct skcipher_request *req;
+ u8 *encrypted_key_tmp;
+ u8 *iv_tmp, *enc_key;
+ int ret;
+
+ encrypted_key_tmp = kzalloc(encrypted_keylen, GFP_KERNEL);
+ if (!encrypted_key_tmp)
+ return -ENOMEM;
+
+ enc_key = kzalloc(DKEY_SIZE, GFP_KERNEL);
+ if (!enc_key) {
+ ret = -ENOMEM;
+ goto key_fail;
+ }
+
+ iv_tmp = kmemdup(ekp->iv, ivsize, GFP_KERNEL);
+ if (!iv_tmp) {
+ ret = -ENOMEM;
+ goto iv_fail;
+ }
+
+ tfm = crypto_alloc_skcipher(blkcipher_alg, 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(tfm)) {
+ ret = PTR_ERR(tfm);
+ pr_err("failed to allocate skcipher (%d)\n", ret);
+ goto tfm_fail;
+ }
+
+ ret = get_derived_key("ENC_KEY", enc_key);
+ if (ret) {
+ pr_err("failed to get encrypt key\n");
+ goto req_fail;
+ }
+
+ ret = crypto_skcipher_setkey(tfm, enc_key, DKEY_SIZE);
+ if (ret) {
+ pr_err("failed to setkey (%d)\n", ret);
+ goto req_fail;
+ }
+
+ req = skcipher_request_alloc(tfm, GFP_KERNEL);
+ if (!req) {
+ pr_err("failed to allocate request\n");
+ ret = -ENOMEM;
+ goto req_fail;
+ }
+
+ memcpy(iv_tmp, ekp->iv, ivsize);
+ sg_init_one(src, ekp->key, ekp->key_len);
+ sg_init_one(dst, encrypted_key_tmp, encrypted_keylen);
+ skcipher_request_set_crypt(req, src, dst, ekp->key_len, iv_tmp);
+ ret = crypto_skcipher_encrypt(req);
+ if (!ret)
+ memcpy(ekp->encrypted_key, encrypted_key_tmp, encrypted_keylen);
+
+ skcipher_request_free(req);
+req_fail:
+ crypto_free_skcipher(tfm);
+tfm_fail:
+ kzfree(iv_tmp);
+iv_fail:
+ memzero_explicit(enc_key, DKEY_SIZE);
+key_fail:
+ kzfree(encrypted_key_tmp);
+
+ return ret;
+}
+
+static int key_decrypt(struct efi_key_payload *ekp)
+{
+ struct scatterlist src[1], dst[1];
+ struct crypto_skcipher *tfm;
+ struct skcipher_request *req;
+ size_t encrypted_keylen;
+ u8 *decrypted_key_tmp;
+ u8 *enc_key, *iv_tmp;
+ int ret;
+
+ encrypted_keylen = roundup(ekp->key_len, blksize);
+
+ decrypted_key_tmp = kzalloc(ekp->key_len, GFP_KERNEL);
+ if (!decrypted_key_tmp)
+ return -ENOMEM;
+
+ enc_key = kzalloc(DKEY_SIZE, GFP_KERNEL);
+ if (!enc_key) {
+ ret = -ENOMEM;
+ goto key_fail;
+ }
+
+ iv_tmp = kmemdup(ekp->iv, ivsize, GFP_KERNEL);
+ if (!iv_tmp) {
+ ret = -ENOMEM;
+ goto iv_fail;
+ }
+
+ tfm = crypto_alloc_skcipher(blkcipher_alg, 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(tfm)) {
+ ret = PTR_ERR(tfm);
+ pr_err("failed to allocate skcipher (%d)\n", ret);
+ goto tfm_fail;
+ }
+
+ ret = get_derived_key("ENC_KEY", enc_key);
+ if (ret) {
+ pr_err("failed to get encrypt key\n");
+ goto req_fail;
+ }
+
+ ret = crypto_skcipher_setkey(tfm, enc_key, DKEY_SIZE);
+ if (ret) {
+ pr_err("failed to setkey (%d)\n", ret);
+ goto req_fail;
+ }
+
+ req = skcipher_request_alloc(tfm, GFP_KERNEL);
+ if (!req) {
+ pr_err("failed to allocate request\n");
+ ret = -ENOMEM;
+ goto req_fail;
+ }
+
+ memcpy(iv_tmp, ekp->iv, ivsize);
+ sg_init_one(src, ekp->encrypted_key, encrypted_keylen);
+ sg_init_one(dst, decrypted_key_tmp, ekp->key_len);
+ skcipher_request_set_crypt(req, src, dst, ekp->key_len, iv_tmp);
+ ret = crypto_skcipher_decrypt(req);
+ if (!ret)
+ memcpy(ekp->key, decrypted_key_tmp, ekp->key_len);
+
+ skcipher_request_free(req);
+req_fail:
+ crypto_free_skcipher(tfm);
+tfm_fail:
+ kzfree(iv_tmp);
+iv_fail:
+ memzero_explicit(enc_key, DKEY_SIZE);
+key_fail:
+ kzfree(decrypted_key_tmp);
+
+ return ret;
+}
+
+/*
+ * Convert the ascii encoded blob to binary. And checking the hmac.
+ * The input blob format is:
+ * <erk hash> <encrypted iv> <encrypted key> <hmac>
+ */
+static int verify_hmac(struct efi_key_payload *ekp, char *hex_encoded_blob)
+{
+ u8 erk_hash[ERK_HASH_SIZE];
+ int max_encoded_blob_len;
+ size_t encrypted_keylen;
+ char *bufp;
+ u8 *hmac;
+ int ret;
+
+ /* check blob size */
+ max_encoded_blob_len = ekp->datablob_len + HMAC_HASH_SIZE
+ - strlen(ekp->key_len_str) - 1;
+ if (strlen(hex_encoded_blob) / 2 > max_encoded_blob_len)
+ return -EINVAL;
+
+ bufp = hex_encoded_blob;
+ ret = hex2bin(ekp->erk_hash, bufp, ERK_HASH_SIZE);
+ if (ret < 0)
+ return -EINVAL;
+
+ /* check the hash of ERK */
+ ret = calc_hash(hash_tfm, root_key, ROOT_KEY_SIZE, erk_hash);
+ if (ret < 0 || crypto_memneq(ekp->erk_hash, erk_hash, ERK_HASH_SIZE))
+ return -EINVAL;
+
+ /* iv */
+ bufp += ERK_HASH_SIZE * 2;
+ ret = hex2bin(ekp->iv, bufp, ivsize);
+ if (ret < 0)
+ return -EINVAL;
+
+ /* encrypted key */
+ bufp += ivsize * 2;
+ encrypted_keylen = roundup(ekp->key_len, blksize);
+ ret = hex2bin(ekp->encrypted_key, bufp, encrypted_keylen);
+ if (ret < 0)
+ return -EINVAL;
+
+ /* verify hmac */
+ bufp += encrypted_keylen * 2;
+ ret = hex2bin(ekp->hmac, bufp, HMAC_HASH_SIZE);
+ if (ret < 0)
+ return -EINVAL;
+
+ hmac = kzalloc(HMAC_HASH_SIZE, GFP_KERNEL);
+ if (!hmac)
+ return -ENOMEM;
+
+ ret = calc_hmac(ekp->datablob, ekp->datablob_len, hmac);
+ if (ret)
+ goto err;
+
+ ret = crypto_memneq(ekp->hmac, hmac, HMAC_HASH_SIZE);
+ if (ret) {
+ pr_warn("hmac signature does not match\n");
+ ret = -EINVAL;
+ }
+
+err:
+ kzfree(hmac);
+ return ret;
+}
+
+/*
+ * efi_instantiate - create a new efi key
+ *
+ * Decrypt an existing efi key blob or, for a new key, get a
+ * random key, then encrypt and creatse a efi key-type key,
+ * adding it to the specified keyring.
+ *
+ * e.g.
+ * keyctl add efi kmk-efi "new 128" @u
+ * keyctl add efi kmk-efi "load `cat kmk-efi.blob`" @u
+ *
+ * On success, return 0. Otherwise return errno.
+ */
+static int efi_instantiate(struct key *key,
+ struct key_preparsed_payload *prep)
+{
+ struct efi_key_payload *ekp = NULL;
+ size_t datalen = prep->datalen;
+ char *datablob = NULL;
+ char *key_len_str = NULL;
+ char *hex_encoded_blob = NULL;
+ int key_cmd;
+ int ret = 0;
+
+ if (datalen <= 0 || datalen > 32767 || !prep->data)
+ return -EINVAL;
+
+ datablob = kzalloc(datalen + 1, GFP_KERNEL);
+ if (!datablob)
+ return -ENOMEM;
+ memcpy(datablob, prep->data, datalen);
+ datablob[datalen] = '\0';
+
+ key_cmd = datablob_parse(datablob, &key_len_str, &hex_encoded_blob);
+ if (key_cmd < 0) {
+ ret = key_cmd;
+ goto out;
+ }
+
+ ekp = efi_payload_alloc(key, key_len_str);
+ if (!ekp) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ switch (key_cmd) {
+ case Opt_load:
+ ret = verify_hmac(ekp, hex_encoded_blob);
+ if (ret)
+ break;
+ ret = key_decrypt(ekp);
+ break;
+ case Opt_new:
+ get_random_bytes(ekp->iv, ivsize);
+ get_random_bytes(ekp->key, ekp->key_len);
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+out:
+ kzfree(datablob);
+ if (!ret)
+ rcu_assign_keypointer(key, ekp);
+ else
+ kzfree(ekp);
+ return ret;
+}
+
+/*
+ * efi_read_blob - read the encrypted blob data with hex format
+ *
+ * The resulting datablob format is:
+ * <key length string> <erk hash> <encrypted iv> <encrypted key> <hmac>
+ *
+ * On success, return the efi key datablob size.
+ */
+long efi_read_blob(const struct key *key, char __user *buffer,
+ char *kbuffer, size_t buflen)
+{
+ struct efi_key_payload *ekp;
+ size_t asciiblob_len, encrypted_keylen;
+ char *ascii_buf;
+ char *bufp;
+ int i, len;
+ int ret;
+
+ if (!is_loaded)
+ return -EINVAL;
+
+ ekp = dereference_key_locked(key);
+ if (!ekp)
+ return -EINVAL;
+
+ /* datablob_len = key_len string length + 1 + ERK hash length + ivsize + encrypted_keylen
+ * double size of ERK hash, iv, encrypted key, and hmac for ascii
+ */
+ encrypted_keylen = roundup(ekp->key_len, blksize);
+ asciiblob_len = ekp->datablob_len + ERK_HASH_SIZE + ivsize + encrypted_keylen + HMAC_HASH_SIZE * 2;
+
+ if ((!buffer && !kbuffer) || buflen < asciiblob_len)
+ return asciiblob_len;
+
+ ascii_buf = kzalloc(asciiblob_len + 1, GFP_KERNEL);
+ if (!ascii_buf)
+ return -ENOMEM;
+
+ ascii_buf[asciiblob_len] = '\0';
+
+ /* copy key length string */
+ len = sprintf(ascii_buf, "%d ", ekp->key_len);
+
+ /* pack hash of ERK */
+ bufp = ascii_buf + len;
+ ret = calc_hash(hash_tfm, root_key, ROOT_KEY_SIZE, ekp->erk_hash);
+ if (ret)
+ goto err;
+ for (i = 0; i < ERK_HASH_SIZE; i++)
+ bufp = hex_byte_pack(bufp, ekp->erk_hash[i]);
+
+ /* pack iv */
+ for (i = 0; i < ivsize; i++)
+ bufp = hex_byte_pack(bufp, ekp->iv[i]);
+
+ /* encrypt and pack key */
+ ret = key_encrypt(ekp, encrypted_keylen);
+ if (ret)
+ goto err;
+ for (i = 0; i < ekp->key_len; i++)
+ bufp = hex_byte_pack(bufp, ekp->encrypted_key[i]);
+
+ /* generate and pack HMAC */
+ ret = calc_hmac(ekp->datablob, ekp->datablob_len, ekp->hmac);
+ if (ret)
+ goto err;
+ for (i = 0; i < HMAC_HASH_SIZE; i++)
+ bufp = hex_byte_pack(bufp, ekp->hmac[i]);
+
+ ret = asciiblob_len;
+ if (buffer) {
+ if (copy_to_user(buffer, ascii_buf, asciiblob_len) != 0)
+ ret = -EFAULT;
+ }
+ if (kbuffer) {
+ if (!memcpy(kbuffer, ascii_buf, asciiblob_len))
+ ret = -EFAULT;
+ }
+err:
+ kzfree(ascii_buf);
+ return ret;
+}
+EXPORT_SYMBOL(efi_read_blob);
+
+/*
+ * efi_read - format and copy the encrypted data to userspace
+ *
+ * The resulting datablob format is:
+ * <key length string> <erk hash> <encrypted iv> <encrypted key> <hmac>
+ *
+ * On success, return to userspace the encrypted key datablob size.
+ */
+static long efi_read(const struct key *key, char __user *buffer,
+ size_t buflen)
+{
+ return efi_read_blob(key, buffer, NULL, buflen);
+}
+
+/*
+ * efi_destroy - clear and free the key's payload
+ */
+static void efi_destroy(struct key *key)
+{
+ kzfree(key->payload.data[0]);
+}
+
+struct key_type key_type_efi = {
+ .name = "efi",
+ .instantiate = efi_instantiate,
+ .destroy = efi_destroy,
+ .describe = user_describe,
+ .read = efi_read,
+};
+EXPORT_SYMBOL_GPL(key_type_efi);
+
+static int __init init_efi_secure_key(void)
+{
+ int ret;
+
+ /* root_key must be loaded */
+ if (!is_loaded)
+ return 0;
+
+ hash_tfm = crypto_alloc_shash(hash_alg, 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(hash_tfm)) {
+ pr_err("can't allocate %s transform: %ld\n",
+ hash_alg, PTR_ERR(hash_tfm));
+ return PTR_ERR(hash_tfm);
+ }
+
+ /* initial EFI key type */
+ ret = set_aes_sizes();
+ if (!ret)
+ ret = register_key_type(&key_type_efi);
+
+ return ret;
+}
+
+late_initcall(init_efi_secure_key);
diff --git a/include/keys/efi-type.h b/include/keys/efi-type.h
new file mode 100644
index 000000000000..57524b22d42f
--- /dev/null
+++ b/include/keys/efi-type.h
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* efi-type.h: EFI key type
+ *
+ * Copyright (C) 2018 Lee, Chun-Yi <jlee@...e.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef _KEYS_EFI_TYPE_H
+#define _KEYS_EFI_TYPE_H
+
+#include <linux/key.h>
+#include <linux/rcupdate.h>
+
+#define MIN_KEY_SIZE 32
+#define MAX_KEY_SIZE 128
+#define MAX_BLOB_SIZE 512
+
+struct efi_key_payload {
+ struct rcu_head rcu;
+ u8 *key;
+ u8 *datablob; /* key_len(string) + ERK hash + iv + encrypted key */
+ char *key_len_str;
+ u8 *erk_hash;
+ u8 *iv;
+ u8 *encrypted_key;
+ u8 *hmac;
+ unsigned int key_len;
+ unsigned int datablob_len;
+ u8 payload_data[0]; /* key + datablob + hmac */
+};
+
+extern struct key_type key_type_efi;
+
+#if defined(CONFIG_EFI_SECURE_KEY)
+extern long efi_read_blob(const struct key *key, char __user *buffer,
+ char *kbuffer, size_t buflen);
+#else
+inline long efi_read_blob(const struct key *key, char __user *buffer,
+ char *kbuffer, size_t buflen)
+{
+ return 0;
+}
+#endif
+
+#endif /* _KEYS_EFI_TYPE_H */
--
2.13.6
Powered by blists - more mailing lists