diff mbox series

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

Message ID 20220215141953.1557009-1-yaelt@google.com (mailing list archive)
State New, archived
Headers show
Series [v5] KEYS: encrypted: Instantiate key with user-provided decrypted data | expand

Commit Message

Yael Tzur Feb. 15, 2022, 2:19 p.m. UTC
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@linux.ibm.com>
Signed-off-by: Yael Tzur <yaelt@google.com>
---

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.

 .../security/keys/trusted-encrypted.rst       | 25 +++++--
 security/keys/Kconfig                         | 19 +++--
 security/keys/encrypted-keys/encrypted.c      | 72 ++++++++++++++-----
 3 files changed, 87 insertions(+), 29 deletions(-)

Comments

Mimi Zohar Feb. 21, 2022, 5:11 a.m. UTC | #1
On Tue, 2022-02-15 at 09:19 -0500, Yael Tzur 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@linux.ibm.com>
> Signed-off-by: Yael Tzur <yaelt@google.com>

Thanks,  Yael.

This patch is now queued in the #next-integrity-testing branch.
Sumit Garg Feb. 21, 2022, 5:47 a.m. UTC | #2
On Tue, 15 Feb 2022 at 19:50, Yael Tzur <yaelt@google.com> 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@linux.ibm.com>
> Signed-off-by: Yael Tzur <yaelt@google.com>
> ---
>
> 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@linaro.org>

-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
>
Jarkko Sakkinen Feb. 21, 2022, 8:19 p.m. UTC | #3
On Mon, Feb 21, 2022 at 12:11:22AM -0500, Mimi Zohar wrote:
> On Tue, 2022-02-15 at 09:19 -0500, Yael Tzur 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@linux.ibm.com>
> > Signed-off-by: Yael Tzur <yaelt@google.com>
> 
> Thanks,  Yael.
> 
> This patch is now queued in the #next-integrity-testing branch.
> 
> -- 
> thanks,
> 
> Mimi
> 

Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>

BR, Jarkko
diff mbox series

Patch

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;