Re: [PATCH v5] KEYS: encrypted: Instantiate key with user-provided decrypted data

From: Sumit Garg
Date: Mon Feb 21 2022 - 00:47:25 EST


On Tue, 15 Feb 2022 at 19:50, Yael Tzur <yaelt@xxxxxxxxxx> wrote:
>
> For availability and performance reasons master keys often need to be
> released outside of a Key Management Service (KMS) to clients. It
> would be beneficial to provide a mechanism where the
> wrapping/unwrapping of data encryption keys (DEKs) is not dependent
> on a remote call at runtime yet security is not (or only minimally)
> compromised. Master keys could be securely stored in the Kernel and
> be used to wrap/unwrap keys from Userspace.
>
> The encrypted.c class supports instantiation of encrypted keys with
> either an already-encrypted key material, or by generating new key
> material based on random numbers. This patch defines a new datablob
> format: [<format>] <master-key name> <decrypted data length>
> <decrypted data> that allows to inject and encrypt user-provided
> decrypted data. The decrypted data must be hex-ascii encoded.
>
> Reviewed-by: Mimi Zohar <zohar@xxxxxxxxxxxxx>
> Signed-off-by: Yael Tzur <yaelt@xxxxxxxxxx>
> ---
>
> Notes:
> v -> v2: fixed compilation error.
>
> v2 -> v3: modified documentation.
>
> v3 -> v4: modified commit message.
>
> v4 -> v5: added config option to enable feature, and modified input validation.
>

Reviewed-by: Sumit Garg <sumit.garg@xxxxxxxxxx>

-Sumit

> .../security/keys/trusted-encrypted.rst | 25 +++++--
> security/keys/Kconfig | 19 +++--
> security/keys/encrypted-keys/encrypted.c | 72 ++++++++++++++-----
> 3 files changed, 87 insertions(+), 29 deletions(-)
>
> diff --git a/Documentation/security/keys/trusted-encrypted.rst b/Documentation/security/keys/trusted-encrypted.rst
> index 80d5a5af62a1..f614dad7de12 100644
> --- a/Documentation/security/keys/trusted-encrypted.rst
> +++ b/Documentation/security/keys/trusted-encrypted.rst
> @@ -107,12 +107,13 @@ Encrypted Keys
> --------------
>
> Encrypted keys do not depend on a trust source, and are faster, as they use AES
> -for encryption/decryption. New keys are created from kernel-generated random
> -numbers, and are encrypted/decrypted using a specified ‘master’ key. The
> -‘master’ key can either be a trusted-key or user-key type. The main disadvantage
> -of encrypted keys is that if they are not rooted in a trusted key, they are only
> -as secure as the user key encrypting them. The master user key should therefore
> -be loaded in as secure a way as possible, preferably early in boot.
> +for encryption/decryption. New keys are created either from kernel-generated
> +random numbers or user-provided decrypted data, and are encrypted/decrypted
> +using a specified ‘master’ key. The ‘master’ key can either be a trusted-key or
> +user-key type. The main disadvantage of encrypted keys is that if they are not
> +rooted in a trusted key, they are only as secure as the user key encrypting
> +them. The master user key should therefore be loaded in as secure a way as
> +possible, preferably early in boot.
>
>
> Usage
> @@ -199,6 +200,8 @@ Usage::
>
> keyctl add encrypted name "new [format] key-type:master-key-name keylen"
> ring
> + keyctl add encrypted name "new [format] key-type:master-key-name keylen
> + decrypted-data" ring
> keyctl add encrypted name "load hex_blob" ring
> keyctl update keyid "update key-type:master-key-name"
>
> @@ -303,6 +306,16 @@ Load an encrypted key "evm" from saved blob::
> 82dbbc55be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e0
> 24717c64 5972dcb82ab2dde83376d82b2e3c09ffc
>
> +Instantiate an encrypted key "evm" using user-provided decrypted data::
> +
> + $ keyctl add encrypted evm "new default user:kmk 32 `cat evm_decrypted_data.blob`" @u
> + 794890253
> +
> + $ keyctl print 794890253
> + default user:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b382d
> + bbc55be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e0247
> + 17c64 5972dcb82ab2dde83376d82b2e3c09ffc
> +
> Other uses for trusted and encrypted keys, such as for disk and file encryption
> are anticipated. In particular the new format 'ecryptfs' has been defined
> in order to use encrypted keys to mount an eCryptfs filesystem. More details
> diff --git a/security/keys/Kconfig b/security/keys/Kconfig
> index 969122c7b92f..0e30b361e1c1 100644
> --- a/security/keys/Kconfig
> +++ b/security/keys/Kconfig
> @@ -98,10 +98,21 @@ config ENCRYPTED_KEYS
> select CRYPTO_RNG
> 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.
> + in the kernel. Encrypted keys are instantiated using kernel
> + generated random numbers or provided decrypted data, and are
> + encrypted/decrypted with a 'master' symmetric key. The 'master'
> + key can be either a trusted-key or user-key type. Only encrypted
> + blobs are ever output to Userspace.
> +
> + If you are unsure as to whether this is required, answer N.
> +
> +config USER_DECRYPTED_DATA
> + bool "Allow encrypted keys with user decrypted data"
> + depends on ENCRYPTED_KEYS
> + help
> + This option provides support for instantiating encrypted keys using
> + user-provided decrypted data. The decrypted data must be hex-ascii
> + encoded.
>
> If you are unsure as to whether this is required, answer N.
>
> diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c
> index 87432b35d771..ebfb8129fb92 100644
> --- a/security/keys/encrypted-keys/encrypted.c
> +++ b/security/keys/encrypted-keys/encrypted.c
> @@ -78,6 +78,11 @@ static const match_table_t key_tokens = {
> {Opt_err, NULL}
> };
>
> +static bool user_decrypted_data = IS_ENABLED(CONFIG_USER_DECRYPTED_DATA);
> +module_param(user_decrypted_data, bool, 0);
> +MODULE_PARM_DESC(user_decrypted_data,
> + "Allow instantiation of encrypted keys using provided decrypted data");
> +
> static int aes_get_sizes(void)
> {
> struct crypto_skcipher *tfm;
> @@ -158,7 +163,7 @@ static int valid_master_desc(const char *new_desc, const char *orig_desc)
> * datablob_parse - parse the keyctl data
> *
> * datablob format:
> - * new [<format>] <master-key name> <decrypted data length>
> + * new [<format>] <master-key name> <decrypted data length> [<decrypted data>]
> * load [<format>] <master-key name> <decrypted data length>
> * <encrypted iv + data>
> * update <new-master-key name>
> @@ -170,7 +175,7 @@ static int valid_master_desc(const char *new_desc, const char *orig_desc)
> */
> static int datablob_parse(char *datablob, const char **format,
> char **master_desc, char **decrypted_datalen,
> - char **hex_encoded_iv)
> + char **hex_encoded_iv, char **decrypted_data)
> {
> substring_t args[MAX_OPT_ARGS];
> int ret = -EINVAL;
> @@ -231,6 +236,7 @@ static int datablob_parse(char *datablob, const char **format,
> "when called from .update method\n", keyword);
> break;
> }
> + *decrypted_data = strsep(&datablob, " \t");
> ret = 0;
> break;
> case Opt_load:
> @@ -595,7 +601,8 @@ static int derived_key_decrypt(struct encrypted_key_payload *epayload,
> static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
> const char *format,
> const char *master_desc,
> - const char *datalen)
> + const char *datalen,
> + const char *decrypted_data)
> {
> struct encrypted_key_payload *epayload = NULL;
> unsigned short datablob_len;
> @@ -604,6 +611,7 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
> unsigned int encrypted_datalen;
> unsigned int format_len;
> long dlen;
> + int i;
> int ret;
>
> ret = kstrtol(datalen, 10, &dlen);
> @@ -613,6 +620,24 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
> format_len = (!format) ? strlen(key_format_default) : strlen(format);
> decrypted_datalen = dlen;
> payload_datalen = decrypted_datalen;
> +
> + if (decrypted_data) {
> + if (!user_decrypted_data) {
> + pr_err("encrypted key: instantiation of keys using provided decrypted data is disabled since CONFIG_USER_DECRYPTED_DATA is set to false\n");
> + return ERR_PTR(-EINVAL);
> + }
> + if (strlen(decrypted_data) != decrypted_datalen) {
> + pr_err("encrypted key: decrypted data provided does not match decrypted data length provided\n");
> + return ERR_PTR(-EINVAL);
> + }
> + for (i = 0; i < strlen(decrypted_data); i++) {
> + if (!isxdigit(decrypted_data[i])) {
> + pr_err("encrypted key: decrypted data provided must contain only hexadecimal characters\n");
> + return ERR_PTR(-EINVAL);
> + }
> + }
> + }
> +
> if (format) {
> if (!strcmp(format, key_format_ecryptfs)) {
> if (dlen != ECRYPTFS_MAX_KEY_BYTES) {
> @@ -740,13 +766,14 @@ static void __ekey_init(struct encrypted_key_payload *epayload,
> /*
> * 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.
> + * For a new key, use either a random number or user-provided decrypted data in
> + * case it is provided. A random number is used for the iv in both cases. For
> + * an old key, decrypt the hex encoded data.
> */
> static int encrypted_init(struct encrypted_key_payload *epayload,
> const char *key_desc, const char *format,
> const char *master_desc, const char *datalen,
> - const char *hex_encoded_iv)
> + const char *hex_encoded_iv, const char *decrypted_data)
> {
> int ret = 0;
>
> @@ -760,21 +787,26 @@ static int encrypted_init(struct encrypted_key_payload *epayload,
> }
>
> __ekey_init(epayload, format, master_desc, datalen);
> - if (!hex_encoded_iv) {
> - get_random_bytes(epayload->iv, ivsize);
> -
> - get_random_bytes(epayload->decrypted_data,
> - epayload->decrypted_datalen);
> - } else
> + if (hex_encoded_iv) {
> ret = encrypted_key_decrypt(epayload, format, hex_encoded_iv);
> + } else if (decrypted_data) {
> + get_random_bytes(epayload->iv, ivsize);
> + memcpy(epayload->decrypted_data, decrypted_data,
> + epayload->decrypted_datalen);
> + } else {
> + get_random_bytes(epayload->iv, ivsize);
> + get_random_bytes(epayload->decrypted_data, epayload->decrypted_datalen);
> + }
> 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.
> + * Instantiates the key:
> + * - by decrypting an existing encrypted datablob, or
> + * - by creating a new encrypted key based on a kernel random number, or
> + * - using provided decrypted data.
> *
> * On success, return 0. Otherwise return errno.
> */
> @@ -787,6 +819,7 @@ static int encrypted_instantiate(struct key *key,
> char *master_desc = NULL;
> char *decrypted_datalen = NULL;
> char *hex_encoded_iv = NULL;
> + char *decrypted_data = NULL;
> size_t datalen = prep->datalen;
> int ret;
>
> @@ -799,18 +832,18 @@ static int encrypted_instantiate(struct key *key,
> datablob[datalen] = 0;
> memcpy(datablob, prep->data, datalen);
> ret = datablob_parse(datablob, &format, &master_desc,
> - &decrypted_datalen, &hex_encoded_iv);
> + &decrypted_datalen, &hex_encoded_iv, &decrypted_data);
> if (ret < 0)
> goto out;
>
> epayload = encrypted_key_alloc(key, format, master_desc,
> - decrypted_datalen);
> + decrypted_datalen, decrypted_data);
> if (IS_ERR(epayload)) {
> ret = PTR_ERR(epayload);
> goto out;
> }
> ret = encrypted_init(epayload, key->description, format, master_desc,
> - decrypted_datalen, hex_encoded_iv);
> + decrypted_datalen, hex_encoded_iv, decrypted_data);
> if (ret < 0) {
> kfree_sensitive(epayload);
> goto out;
> @@ -860,7 +893,7 @@ static int encrypted_update(struct key *key, struct key_preparsed_payload *prep)
>
> buf[datalen] = 0;
> memcpy(buf, prep->data, datalen);
> - ret = datablob_parse(buf, &format, &new_master_desc, NULL, NULL);
> + ret = datablob_parse(buf, &format, &new_master_desc, NULL, NULL, NULL);
> if (ret < 0)
> goto out;
>
> @@ -869,7 +902,7 @@ static int encrypted_update(struct key *key, struct key_preparsed_payload *prep)
> goto out;
>
> new_epayload = encrypted_key_alloc(key, epayload->format,
> - new_master_desc, epayload->datalen);
> + new_master_desc, epayload->datalen, NULL);
> if (IS_ERR(new_epayload)) {
> ret = PTR_ERR(new_epayload);
> goto out;
> --
> 2.35.1.265.g69c8d7142f-goog
>