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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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.sassu@xxxxxxxxx>
> ---
>  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 <zohar@xxxxxxxxxx>
> > Signed-off-by: David Safford <safford@xxxxxxxxxxxxxx>
> > ---
> >  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 <zohar@xxxxxxxxxx>
> > + *
> > + * 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 <zohar@xxxxxxxxxx>
> > + *
> > + * 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 majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Kernel]     [Gnu Classpath]     [Gnu Crypto]     [DM Crypt]     [Netfilter]     [Bugtraq]

  Powered by Linux