From: Roberto Sassu Subject: Re: [RFC][PATCH 4/4] keys: add new key-type encrypted Date: Wed, 29 Sep 2010 12:00:43 +0200 Message-ID: <201009291200.47199.roberto.sassu@polito.it> References: <1285698993-16927-1-git-send-email-zohar@linux.vnet.ibm.com> <1285698993-16927-5-git-send-email-zohar@linux.vnet.ibm.com> Mime-Version: 1.0 Content-Type: multipart/signed; boundary="nextPart12608251.U9EoicsUbZ"; protocol="application/pkcs7-signature"; micalg=sha1 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: Mimi Zohar Return-path: In-Reply-To: <1285698993-16927-5-git-send-email-zohar@linux.vnet.ibm.com> Sender: linux-security-module-owner@vger.kernel.org List-Id: linux-crypto.vger.kernel.org --nextPart12608251.U9EoicsUbZ Content-Type: Text/Plain; charset="iso-8859-15" Content-Transfer-Encoding: quoted-printable 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=20 if it is missing. The following patch moves the master key request in the encrypted_init() fu= nction,=20 in order to deny a new key creation if the former is not present. Signed-off-by: Roberto Sassu =2D-- 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_de= fined.c index 6b26db6..48e627e 100644 =2D-- a/security/keys/encrypted_defined.c +++ b/security/keys/encrypted_defined.c @@ -491,12 +491,10 @@ static struct encrypted_key_payload *encrypted_key_al= loc(struct key *key, } =20 static int encrypted_key_decrypt(struct encrypted_key_payload *epayload, =2D 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]; =2D struct key *mkey; =2D void *master_key; =2D 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 =3D epayload->master_desc + epayload->datablob_len; hex2bin(hmac, hex_encoded_data + (encrypted_datalen * 2), hash_size); =20 =2D mkey =3D request_master_key(epayload, &master_key, &master_keylen); =2D if (IS_ERR(mkey)) =2D return PTR_ERR(mkey); =2D ret =3D 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_p= ayload *epayload, if (ret) pr_err("encrypted_key: failed to decrypt key (%d)\n", ret); out: =2D key_put(mkey); return ret; } =20 @@ -555,8 +548,16 @@ static int encrypted_init(struct encrypted_key_payload= *epayload, char *hex_encoded_iv, char *hex_encoded_data) { int ret =3D 0; + struct key *mkey; + void *master_key; + unsigned int master_keylen; =20 __ekey_init(epayload, master_desc, datalen); + + mkey =3D request_master_key(epayload, &master_key, &master_keylen); + if (IS_ERR(mkey)) + return PTR_ERR(mkey); +=09 if (!hex_encoded_data) { get_random_bytes(epayload->iv, ivsize); =20 @@ -564,7 +565,9 @@ static int encrypted_init(struct encrypted_key_payload = *epayload, epayload->decrypted_datalen); } else ret =3D encrypted_key_decrypt(epayload, hex_encoded_iv, =2D hex_encoded_data); + hex_encoded_data, master_key, master_keylen); + + key_put(mkey); return ret; } =20 =2D-=20 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. >=20 > 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 >=20 > 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 > =20 > If you are unsure as to whether this is required, answer N. > =20 > +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 :=3D \ > user_defined.o > =20 > obj-$(CONFIG_TRUSTED_KEYS) +=3D trusted_defined.o > +obj-$(CONFIG_ENCRYPTED_KEYS) +=3D encrypted_defined.o > obj-$(CONFIG_KEYS_COMPAT) +=3D compat.o > obj-$(CONFIG_PROC_FS) +=3D proc.o > obj-$(CONFIG_SYSCTL) +=3D 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[] =3D "sha256"; > +static char hmac_alg[] =3D "hmac(sha256)"; > +static int hash_size =3D SHA256_DIGEST_SIZE; > +static char blkcipher_alg[] =3D "cbc(aes)"; > +static int ivsize; > +static int blksize; > + > +static int aes_get_sizes(int *ivsize, int *blksize) > +{ > + struct crypto_blkcipher *tfm; > + > + tfm =3D 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 =3D crypto_blkcipher_ivsize(tfm); > + *blksize =3D crypto_blkcipher_blocksize(tfm); > + crypto_free_blkcipher(tfm); > + return 0; > +} > + > +enum { > + Opt_err =3D -1, Opt_new =3D 1, Opt_load, Opt_NEW, Opt_LOAD > +}; > + > +static match_table_t key_tokens =3D { > + {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 toke= n, > + * 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 =3D -EINVAL; > + int key_cmd; > + char *p; > + > + p =3D strsep(&datablob, " \t"); > + if (!p) > + return ret; > + key_cmd =3D match_token(p, key_tokens, args); > + > + *master_desc =3D strsep(&datablob, " \t"); > + if (!*master_desc) > + goto out; > + *decrypted_datalen =3D strsep(&datablob, " \t"); > + if (!*decrypted_datalen) > + goto out; > + > + switch (key_cmd) { > + case Opt_new: > + case Opt_NEW: > + ret =3D 0; > + break; > + case Opt_load: > + case Opt_LOAD: > + *hex_encoded_iv =3D strsep(&datablob, " \t"); > + if (!*hex_encoded_iv) > + break; > + *hex_encoded_data =3D *hex_encoded_iv + (2 * ivsize) + 2; > + ret =3D 0; > + break; > + case Opt_err: > + break; > + } > +out: > + return ret; > +} > + > +/* datablob_format - format as an ascii string, before copying to usersp= ace */ > +static int datablob_format(char __user *buffer, > + struct encrypted_key_payload *epayload, > + int asciiblob_len) > +{ > + char *ascii_buf, *bufp; > + char *iv =3D (char *)epayload->iv; > + int ret =3D 0; > + int len; > + int i; > + > + ascii_buf =3D kzalloc(asciiblob_len + 1, GFP_KERNEL); > + if (!ascii_buf) > + return -ENOMEM; > + > + len =3D sprintf(ascii_buf, "%s %s ", epayload->master_desc, > + epayload->datalen); > + > + /* convert the hex encoded iv, encrypted-data and HMAC to ascii */ > + bufp =3D &ascii_buf[len]; > + for (i =3D 0; i < (asciiblob_len - len) / 2; i++) > + bufp =3D pack_hex_byte(bufp, iv[i]); > + > + if (copy_to_user(buffer, ascii_buf, asciiblob_len) !=3D 0) > + ret =3D -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 =3D request_key(&key_type_trusted, trusted_desc, NULL); > + if (IS_ERR(tkey)) > + goto error; > + > + tpayload =3D tkey->payload.data; > + *master_key =3D tpayload->key; > + *master_keylen =3D 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 =3D request_key(&key_type_user, master_desc, NULL); > + if (IS_ERR(ukey)) > + goto error; > + > + upayload =3D ukey->payload.data; > + *master_key =3D upayload->data; > + *master_keylen =3D (unsigned int)upayload->datalen; > +error: > + return ukey; > +} > + > +static int init_desc(struct hash_desc *desc, char *alg) > +{ > + int ret; > + > + desc->tfm =3D 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 =3D PTR_ERR(desc->tfm); > + return ret; > + } > + desc->flags =3D 0; > + ret =3D 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 =3D init_desc(&desc, hmac_alg); > + if (ret) > + return ret; > + > + crypto_hash_setkey(desc.tfm, key, keylen); > + ret =3D crypto_hash_init(&desc); > + if (ret) > + goto out; > + > + sg_init_one(sg, buf, buflen); > + ret =3D crypto_hash_update(&desc, sg, buflen); > + if (!ret) > + ret =3D 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 =3D init_desc(&desc, hash_alg); > + if (ret) > + return ret; > + > + sg_init_one(sg, buf, buflen); > + ret =3D crypto_hash_update(&desc, sg, buflen); > + if (!ret) > + ret =3D 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 =3D 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 =3D 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 =3D 0; > + > + ret =3D 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 *epay= load, > + void **master_key, > + unsigned int *master_keylen) > +{ > + struct key *mkey; > + > + mkey =3D request_trusted_key(epayload->master_desc, > + master_key, master_keylen); > + if (IS_ERR(mkey)) { > + mkey =3D 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 =3D roundup(epayload->decrypted_datalen, blksize); > + padlen =3D encrypted_datalen - epayload->decrypted_datalen; > + > + ret =3D 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 =3D crypto_blkcipher_encrypt(&desc, sg_out, sg_in, encrypted_datale= n); > + 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 =3D get_derived_key(derived_key, AUTH_KEY, master_key, master_keyle= n); > + if (ret) > + goto out; > + > + digest =3D epayload->master_desc + epayload->datablob_len; > + ret =3D 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 =3D get_derived_key(derived_key, AUTH_KEY, master_key, master_keyle= n); > + if (ret) > + goto out; > + > + memset(digest, 0, sizeof digest); > + ret =3D calc_hmac(digest, derived_key, sizeof derived_key, > + epayload->master_desc, epayload->datablob_len); > + if (ret) > + goto out; > + ret =3D 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 =3D roundup(epayload->decrypted_datalen, blksize); > + ret =3D 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 =3D crypto_blkcipher_decrypt(&desc, sg_out, sg_in, encrypted_datale= n); > + 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 =3D NULL; > + unsigned short datablob_len; > + unsigned short decrypted_datalen; > + size_t encrypted_datalen; > + long dlen; > + int ret; > + > + ret =3D strict_strtol(datalen, 10, &dlen); > + if (ret < 0 || dlen < 20 || dlen > hash_size) > + return ERR_PTR(-EINVAL); > + > + decrypted_datalen =3D (unsigned short)dlen; > + encrypted_datalen =3D roundup(decrypted_datalen, blksize); > + > + datablob_len =3D strlen(master_desc) + 1 + strlen(datalen) + 1 > + + ivsize + 1 + encrypted_datalen; > + > + ret =3D key_payload_reserve(key, decrypted_datalen + datablob_len); > + if (ret < 0) > + return ERR_PTR(ret); > + > + epayload =3D kzalloc(sizeof(*epayload) + decrypted_datalen + > + datablob_len + hash_size + 1, GFP_KERNEL); > + if (!epayload) > + return ERR_PTR(-ENOMEM); > + > + epayload->decrypted_datalen =3D decrypted_datalen; > + epayload->datablob_len =3D 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 =3D roundup(epayload->decrypted_datalen, blksize); > + hex2bin(epayload->iv, hex_encoded_iv, ivsize); > + hex2bin(epayload->encrypted_data, hex_encoded_data, encrypted_datalen); > + > + hmac =3D epayload->master_desc + epayload->datablob_len; > + hex2bin(hmac, hex_encoded_data + (encrypted_datalen * 2), hash_size); > + > + mkey =3D request_master_key(epayload, &master_key, &master_keylen); > + if (IS_ERR(mkey)) > + return PTR_ERR(mkey); > + > + ret =3D 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 =3D get_derived_key(derived_key, ENC_KEY, master_key, master_keylen= ); > + if (ret) > + goto out; > + > + ret =3D 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 =3D epayload->decrypted_data > + + epayload->decrypted_datalen; > + epayload->datalen =3D epayload->master_desc + strlen(master_desc) + 1; > + epayload->iv =3D epayload->datalen + strlen(datalen) + 1; > + epayload->encrypted_data =3D 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 =3D 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 =3D 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 =3D NULL; > + char *datablob =3D NULL; > + char *master_desc =3D NULL; > + char *decrypted_datalen =3D NULL; > + char *hex_encoded_iv =3D NULL; > + char *hex_encoded_data =3D NULL; > + int ret; > + > + if (datalen <=3D 0 || datalen > 32767 || !data) > + return -EINVAL; > + > + datablob =3D kzalloc(datalen + 1, GFP_KERNEL); > + if (!datablob) > + return -ENOMEM; > + > + memcpy(datablob, data, datalen); > + ret =3D datablob_parse(datablob, &master_desc, &decrypted_datalen, > + &hex_encoded_iv, &hex_encoded_data); > + if (ret < 0) > + goto out; > + > + epayload =3D encrypted_key_alloc(key, master_desc, decrypted_datalen); > + if (IS_ERR(epayload)) { > + ret =3D PTR_ERR(epayload); > + goto out; > + } > + ret =3D 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 =3D 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 da= talen) > +{ > + struct encrypted_key_payload *epayload =3D key->payload.data; > + struct encrypted_key_payload *new_epayload; > + char *buf; > + char *new_master_desc =3D NULL; > + int ret =3D 0; > + > + if (datalen <=3D 0 || datalen > 32767 || !data) > + return -EINVAL; > + > + buf =3D kzalloc(datalen + 1, GFP_KERNEL); > + if (!buf) > + return -ENOMEM; > + > + memcpy(buf, data, datalen); > + new_master_desc =3D strsep(&buf, " \t"); > + if (!*new_master_desc) { > + ret =3D -EINVAL; > + goto out; > + } > + > + new_epayload =3D encrypted_key_alloc(key, new_master_desc, > + epayload->datalen); > + if (IS_ERR(new_epayload)) { > + ret =3D 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 =3D 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 =3D epayload->datablob_len + ivsize + 1 > + + roundup(epayload->decrypted_datalen, blksize) > + + (hash_size * 2); > + > + if (!buffer || buflen <=3D 0) > + return asciiblob_len; > + > + mkey =3D request_master_key(epayload, &master_key, &master_keylen); > + if (IS_ERR(mkey)) > + return PTR_ERR(mkey); > + > + memset(derived_key, 0, sizeof derived_key); > + ret =3D get_derived_key(derived_key, ENC_KEY, master_key, master_keylen= ); > + if (ret) > + goto out; > + > + ret =3D derived_key_encrypt(epayload, derived_key, sizeof derived_key); > + if (ret) > + goto out; > + > + ret =3D datablob_hmac_append(epayload, master_key, master_keylen); > + if (ret) > + goto out; > + > + ret =3D 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 =3D key->payload.data; > + > + if (!epayload) > + return; > + > + memset(epayload->decrypted_data, 0, epayload->decrypted_datalen); > + kfree(key->payload.data); > +} > + > +struct key_type key_type_encrypted =3D { > + .name =3D "encrypted", > + .instantiate =3D encrypted_instantiate, > + .update =3D encrypted_update, > + .match =3D user_match, > + .destroy =3D encrypted_destroy, > + .describe =3D user_describe, > + .read =3D encrypted_read, > +}; > +EXPORT_SYMBOL_GPL(key_type_encrypted); > + > +static int __init init_encrypted(void) > +{ > + int ret; > + > + ret =3D register_key_type(&key_type_encrypted); > + if (ret < 0) > + return ret; > + ret =3D 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 *epa= yload) > +{ > + 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 *epa= yload, > + 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 *epa= yload) > +{ > +} > + > +static inline void dump_encrypted_data(struct encrypted_key_payload *epa= yload, > + unsigned int encrypted_datalen) > +{ > +} > + > +static inline void dump_hmac(char *str, void *digest, int hmac_size) > +{ > +} > +#endif > +#endif >=20 --nextPart12608251.U9EoicsUbZ Content-Type: application/pkcs7-signature; name="smime.p7s" Content-Disposition: attachment; filename="smime.p7s" Content-Transfer-Encoding: base64 MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAQAAoIIQCTCCBLMw ggOboAMCAQICARQwDQYJKoZIhvcNAQEFBQAwQTEQMA4GA1UEChMHRXVyb1BLSTEtMCsGA1UEAxMk RXVyb1BLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTAyNDEzMzEzM1oXDTEw MTIzMTEyNTk1OVowUTELMAkGA1UEBhMCSVQxEDAOBgNVBAoTB0V1cm9QS0kxMDAuBgNVBAMTJ0V1 cm9QS0kgSXRhbGlhbiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQAD ggEPADCCAQoCggEBAPbJ3y5bE0iL2iFW59R4KGHS3iTc22mq2GlDaOZLE3Xz1wTvB/yBl5e4ntMZ eEeWTW9JInNRTtfC3mcLk0gnEdK1rQZbe4lQwBVHdkG8LvRQDWHIkZNuccoUpQXMv+JbZjI6m7rH fU2CN/YCvFY9QL08xXsfDxvX2Ee4S7EwJiSEEJSiy+R00uTfvfn27d8a6LCqYLzlInRlz4C2CUbk +U2UAwjYEPTP2ziqFxztysbxC3fjqH8e6P5wIPXOS5cSYnVaEyp21VXhAOamVJEGlQCdVoTHs+pP BYqCBuNhBfienUPaEDHHUUYHZ2nS4eNzd9XOu+5hFi1/8hVZxN4sbdECAwEAAaOCAaQwggGgMEwG CWCGSAGG+EIBDQQ/Fj1Jc3N1ZWQgdW5kZXIgcG9saWN5OgogaHR0cDovL3d3dy5ldXJvcGtpLm9y Zy9jYS9yb290L2Nwcy8xLjEvMGUGCCsGAQUFBwEBBFkwVzAoBggrBgEFBQcwAYYcaHR0cDovL29j c3AuZXVyb3BraS5vcmc6ODAyNjArBggrBgEFBQcwAoYfaHR0cDovL3d3dy5ldXJvcGtpLm9yZy9j YS9yb290LzA7BgNVHR8ENDAyMDCgLqAshipodHRwOi8vd3d3LmV1cm9wa2kub3JnL2NhL3Jvb3Qv Y3JsL2NybC5kZXIwDAYDVR0TBAUwAwEB/zBOBgNVHSAERzBFMEMGCisGAQQBqQcBAQEwNTAzBggr BgEFBQcCARYnaHR0cDovL3d3dy5ldXJvcGtpLm9yZy9jYS9yb290L2Nwcy8xLjEvMA4GA1UdDwEB /wQEAwIB9jAdBgNVHQ4EFgQUjl4HtzG0Gbu5BJU0rMX9016KQsAwHwYDVR0jBBgwFoAUjNyLsaVK kOdOiHMYPJ3VXn7kss0wDQYJKoZIhvcNAQEFBQADggEBAFjlAWMiL7uhEAVcrPeMXsbQU+bQNbP1 TBqyBoMCMJ4RakM7/67AsmqpKGIYwhjVkSNHhscRl5BuSiyviHOLbWUJ9tAAKt6hzMDr1J4OvWSH Sn8y9fSlyQU8+fQE8FHxXT3Aa7aLYVqsqr0ppmxRDL/9b/mdUVcMXiKFAdopFzhXIigfZFh6MW/o 7/GEiDMGsovCiwKJ5ZL46zGRDaS+O0aRYQv2PHZaWrC8i0OPwghlTeB2DfUBzm0urPijBATdjzP8 yO3YGymFZOIG08Roe4bwzrANGzX5luBk3lNmtXTPHoUtRLn3ADa+IUGwK6VYWHYJe1+uUHRNziQu /Zsb858wggU/MIIEJ6ADAgECAgIJzzANBgkqhkiG9w0BAQUFADBRMQswCQYDVQQGEwJJVDEQMA4G A1UEChMHRXVyb1BLSTEwMC4GA1UEAxMnRXVyb1BLSSBJdGFsaWFuIENlcnRpZmljYXRpb24gQXV0 aG9yaXR5MB4XDTA2MTEwNjE3MTU1OVoXDTEwMTIzMTEyNTk1OVowZTELMAkGA1UEBhMCSVQxHjAc BgNVBAoTFVBvbGl0ZWNuaWNvIGRpIFRvcmlubzE2MDQGA1UEAxMtUG9saXRlY25pY28gZGkgVG9y aW5vIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC AQEA/s1vopfmSp2eqKG/jW4H5YyfrZc/jWEnctMMm7TlVT+ryxJ2ifYV/7VyuYVIGdnCBnmJiav6 uhePqpHyuejEZ+IdCchk04BSBbgzkSbWakw2ekv1SKm8WfvhxHbiSo/t//LQEANMJ+XeeqGl1TRR 8gdMC6erWFGdl5FcuOpMoaqW6DszVGlOAf7Uo/DExZ0/9cw77GTRoDTA3vi1QJEYGaAAvI2/flaU 8gn4QoktjowchDXBylVsyqs+ux3v7m3H0cgC3q62t2om2badk40F9XH1w2WPLQB6rSl+TPDxkl2k kR4goQprXR0b0OujS5ADq5Ds2w7YPj5h4J6ebsbIUQIDAQABo4ICCzCCAgcwdQYJYIZIAYb4QgEN BGgWZklzc3VlZCB1bmRlciBwb2xpY2llczoKIGh0dHA6Ly93d3cuZXVyb3BraS5vcmcvY2Evcm9v dC9jcHMvMS4xLwogaHR0cDovL3d3dy5ldXJvcGtpLm9yZy9jYS9pdC9jcHMvMS4xLzBcBggrBgEF BQcBAQRQME4wKAYIKwYBBQUHMAGGHGh0dHA6Ly9vY3NwLmV1cm9wa2kub3JnOjgwMjYwIgYIKwYB BQUHMAKGFmh0dHA6Ly93d3cuZXVyb3BraS5vcmcwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL3d3 dy5ldXJvcGtpLm9yZy9jYS9pdDIvY3JsMDMvY3JsLmRlcjAPBgNVHRMBAf8EBTADAQH/MIGTBgNV HSAEgYswgYgwQwYKKwYBBAGpBwEBATA1MDMGCCsGAQUFBwIBFidodHRwOi8vd3d3LmV1cm9wa2ku b3JnL2NhL3Jvb3QvY3BzLzEuMS8wQQYKKwYBBAGpBwIBATAzMDEGCCsGAQUFBwIBFiVodHRwOi8v d3d3LmV1cm9wa2kub3JnL2NhL2l0L2Nwcy8xLjEvMAsGA1UdDwQEAwIB9jAdBgNVHQ4EFgQUCf0l GW/jaHyCazVzvCgAviw8OjgwHwYDVR0jBBgwFoAUjl4HtzG0Gbu5BJU0rMX9016KQsAwDQYJKoZI hvcNAQEFBQADggEBANfGdD6B98NUwBOVYIiuo+SNfw8Afjr2oFHLYEHYhKdBED1WBckhK610v2zJ ctramnrqdd+xfyzmkyNm48OtkiVetYzXbgYeyMGxhO8HrJ1Ztf1yEN0lL8HgdK3lmJeLYNYH/cwZ 5SlKwvTR/VENHLYlWsgWk47ut7W8+Zd/ESwzNdJUCBUMoQnGZ3giyhAXKAgIOckwL0g8KmnceY7J Y4L1G++1OuOoUcgg2jRPxcVmHVEe/cM0WHfUcigDXSBTIxAiDwlQ7ni97yX1RqBV/6IVdRDYKyjg YkdYcwU9ZuOKUP4Pl5KKCegLOHRoo5kX6V1o7b2IZGBFoVmkag4m4kkwggYLMIIE86ADAgECAgID wTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJJVDEeMBwGA1UEChMVUG9saXRlY25pY28gZGkg VG9yaW5vMTYwNAYDVQQDEy1Qb2xpdGVjbmljbyBkaSBUb3Jpbm8gQ2VydGlmaWNhdGlvbiBBdXRo b3JpdHkwHhcNMDgxMjA1MTYwMDAwWhcNMTAxMjMwMTIwMDAwWjB5MQswCQYDVQQGEwJJVDEeMBwG A1UEChMVUG9saXRlY25pY28gZGkgVG9yaW5vMTEwLwYDVQQLEyhEaXBhcnRpbWVudG8gZGkgQXV0 b21hdGljYSBlIEluZm9ybWF0aWNhMRcwFQYDVQQDEw5Sb2JlcnRvICBTYXNzdTCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAOS/leMNzG5v7FG73ythWtNPOdq8MEFpzg7mjy4M7UsS9+sc b7QE8TOX365q/+mBwxZ7qW+OhNKfgv14A3y/quju5P7mjTmTNvKWDpsUBjC+tCs+WpWsgIPc2xxW PrR7br04U/SYYNsxynh1TtO2gsQrHIj9S/wNiNRP8GGPCAiuYRlaL2CeSVn4JEKcT0zOeLIIqfDp 3Ad3YeBhj+cR3TrmO9TyGTPCAb1oyq9wuA1Tx044rqSQNbp0e0RvOrwAIpD/2mNPP3ReTeB53aSg q0pCHoH1s2AcY/jWJ1joYFC9hh/2DqZRvMNIsM7Uce5Iiz2sgl1I61IUD4658HYzCmMCAwEAAaOC Aq8wggKrMIGVBglghkgBhvhCAQ0EgYcWgYRJc3N1ZWQgdW5kZXIgcG9saWNpZXM6CiBodHRwOi8v d3d3LmV1cm9wa2kub3JnL2NhL3Jvb3QvY3BzLzEuMS8KIGh0dHA6Ly93d3cuZXVyb3BraS5vcmcv Y2EvaXQvY3BzLzEuMS8KIGh0dHA6Ly9jYS5wb2xpdG8uaXQvY3BzLzIuMS8wEQYJYIZIAYb4QgEB BAQDAgCwMGMGCCsGAQUFBwEBBFcwVTAoBggrBgEFBQcwAYYcaHR0cDovL29jc3AuZXVyb3BraS5v cmc6ODAyNjApBggrBgEFBQcwAoYdaHR0cDovL3d3dy5ldXJvcGtpLm9yZy9jYS9pdC8wMgYDVR0f BCswKTAnoCWgI4YhaHR0cDovL2NhLnBvbGl0by5pdC9jcmwwMy9jcmwuZGVyMAwGA1UdEwEB/wQC MAAwOgYDVR0RBDMwMYEXcm9iZXJ0by5zYXNzdUBwb2xpdG8uaXSgFgYKKwYBBAGVYgIBAaAIFgYw MjEzMDUwgc0GA1UdIASBxTCBwjBDBgorBgEEAakHAQEBMDUwMwYIKwYBBQUHAgEWJ2h0dHA6Ly93 d3cuZXVyb3BraS5vcmcvY2Evcm9vdC9jcHMvMS4xLzBBBgorBgEEAakHAgEBMDMwMQYIKwYBBQUH AgEWJWh0dHA6Ly93d3cuZXVyb3BraS5vcmcvY2EvaXQvY3BzLzEuMS8wOAYKKwYBBAGVYgECATAq MCgGCCsGAQUFBwIBFhxodHRwOi8vY2EucG9saXRvLml0L2Nwcy8yLjEvMAsGA1UdDwQEAwIE8DAd BgNVHQ4EFgQUP+37DpRL6r5BNjgsgFjI8MHvGTcwHwYDVR0jBBgwFoAUCf0lGW/jaHyCazVzvCgA viw8OjgwDQYJKoZIhvcNAQEFBQADggEBADRj0OWMglzLXTPuKND9gMTHd1iRPzRNI1TiYFN/WIyu a3mNev65wKn35VxbbMV6k7zuxxdC7o+iYRLepVOnpw90Af/SiJRc9QI4rk8tzSr5xII7vI0fPvcl rjSMymXCvwKrbYwrKe0FhBnSZ8AoewXoHDQ8f64heLIU8mLWuwo1+brq0pJUH5Q+69WAGD03ln+l Fhk2KqvVv8yAD21PydrS7Uk2ZLE7l7/+BTQBY8ZjpNRZ44C6+o7lou2zN6lkWtVHBwUNVdfu3wkS TdtCm/yxR8XUlFyZkLHfhY19VW4luxb7KZ4GiPKDERdQwBb0kja4eqSOBkAPWZ9Y8zpv4bAxggIc MIICGAIBATBrMGUxCzAJBgNVBAYTAklUMR4wHAYDVQQKExVQb2xpdGVjbmljbyBkaSBUb3Jpbm8x NjA0BgNVBAMTLVBvbGl0ZWNuaWNvIGRpIFRvcmlubyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQIC A8EwCQYFKw4DAhoFAKCBhzAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEP Fw0xMDA5MjkxMDAwNDNaMCMGCSqGSIb3DQEJBDEWBBQZsXJlkto3bCD4QqCEGmovoLi6ODAoBgkq hkiG9w0BCQ8xGzAZMAsGCWCGSAFlAwQBAjAKBggqhkiG9w0DBzANBgkqhkiG9w0BAQEFAASCAQCT JFO+9N2AEcCwo3vW1vtgIBhmGUy55YNo5MX+ugmL8bcho4KnLy8EjpQX7X+s1N0cIiFaVeZWGU8k quzed/86v4xIhlguBW6LWJt2WPMKCv4HNMi4WQ+5VPMUV8mqBRLRDVqMvkLFFxoM32jcTNN5g0CN cOPsH7OaFWnpyF+zgWKCB628OAXW/5meDQTfUlj9vPV9Iw9tTPGuRgTKTeJ9Y8L+70GCCaUqUzwf Dt7xR1dtiRJ+a8J5GYdOvTQNnRtDt5vjXH6pO+TaLwa9BpFwTun8FhopHB0/O9zq5vO8GyVRi0AF 1kKy4+AEDpHBN3o02aY9LTc2IlEjkOS9Snc+AAAAAAAA --nextPart12608251.U9EoicsUbZ--