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 <roberto.sa...@polito.it>
> ---
>  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 <zo...@us.ibm.com>
> > Signed-off-by: David Safford <saff...@watson.ibm.com>
> > ---
> >  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 <zo...@us.ibm.com>
> > + *
> > + * 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 <zo...@us.ibm.com>
> > + *
> > + * 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-crypto" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to