Kernel keyring development
 help / color / mirror / Atom feed
From: Stephan Mueller <smueller@chronox.de>
To: "Lee, Chun-Yi" <joeyli.kernel@gmail.com>
Cc: "Rafael J . Wysocki" <rjw@rjwysocki.net>,
	Pavel Machek <pavel@ucw.cz>,
	linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org,
	keyrings@vger.kernel.org, "Lee, Chun-Yi" <jlee@suse.com>,
	"Rafael J. Wysocki" <rafael.j.wysocki@intel.com>,
	Chen Yu <yu.c.chen@intel.com>, Oliver Neukum <oneukum@suse.com>,
	Ryan Chen <yu.chen.surf@gmail.com>,
	David Howells <dhowells@redhat.com>,
	Giovanni Gherdovich <ggherdovich@suse.cz>,
	Randy Dunlap <rdunlap@infradead.org>,
	Jann Horn <jannh@google.com>, Andy Lutomirski <luto@kernel.org>
Subject: Re: [PATCH 3/5] PM / hibernate: Encrypt snapshot image
Date: Sun, 06 Jan 2019 08:23:33 +0000	[thread overview]
Message-ID: <8502734.OEeY8cfajc@tauon.chronox.de> (raw)
In-Reply-To: <20190103143227.9138-4-jlee@suse.com>

Am Donnerstag, 3. Januar 2019, 15:32:25 CET schrieb Lee, Chun-Yi:

Hi Chun,

> To protect the secret in memory snapshot image, this patch adds the
> logic to encrypt snapshot pages by AES-CTR. Using AES-CTR is because
> it's simple, fast and parallelizable. But this patch didn't implement
> parallel encryption.
> 
> The encrypt key is derived from the snapshot key. And the initialization
> vector will be kept in snapshot header for resuming.
> 
> Cc: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
> Cc: Pavel Machek <pavel@ucw.cz>
> Cc: Chen Yu <yu.c.chen@intel.com>
> Cc: Oliver Neukum <oneukum@suse.com>
> Cc: Ryan Chen <yu.chen.surf@gmail.com>
> Cc: David Howells <dhowells@redhat.com>
> Cc: Giovanni Gherdovich <ggherdovich@suse.cz>
> Cc: Randy Dunlap <rdunlap@infradead.org>
> Cc: Jann Horn <jannh@google.com>
> Cc: Andy Lutomirski <luto@kernel.org>
> Signed-off-by: "Lee, Chun-Yi" <jlee@suse.com>
> ---
>  kernel/power/hibernate.c |   8 ++-
>  kernel/power/power.h     |   6 ++
>  kernel/power/snapshot.c  | 154
> ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 164
> insertions(+), 4 deletions(-)
> 
> diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
> index 0dda6a9f0af1..5ac2ab6f4a0e 100644
> --- a/kernel/power/hibernate.c
> +++ b/kernel/power/hibernate.c
> @@ -275,10 +275,14 @@ static int create_image(int platform_mode)
>  	if (error)
>  		return error;
> 
> +	error = snapshot_prepare_crypto(false, true);
> +	if (error)
> +		goto finish_hash;
> +
>  	error = dpm_suspend_end(PMSG_FREEZE);
>  	if (error) {
>  		pr_err("Some devices failed to power down, aborting hibernation\n");
> -		goto finish_hash;
> +		goto finish_crypto;
>  	}
> 
>  	error = platform_pre_snapshot(platform_mode);
> @@ -335,6 +339,8 @@ static int create_image(int platform_mode)
>  	dpm_resume_start(in_suspend ?
>  		(error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);
> 
> + finish_crypto:
> +	snapshot_finish_crypto();
>   finish_hash:
>  	snapshot_finish_hash();
> 
> diff --git a/kernel/power/power.h b/kernel/power/power.h
> index c614b0a294e3..41263fdd3a54 100644
> --- a/kernel/power/power.h
> +++ b/kernel/power/power.h
> @@ -5,6 +5,7 @@
>  #include <linux/freezer.h>
>  #include <linux/compiler.h>
>  #include <crypto/sha.h>
> +#include <crypto/aes.h>
> 
>  /* The max size of encrypted key blob */
>  #define KEY_BLOB_BUFF_LEN 512
> @@ -24,6 +25,7 @@ struct swsusp_info {
>  	unsigned long		pages;
>  	unsigned long		size;
>  	unsigned long		trampoline_pfn;
> +	u8			iv[AES_BLOCK_SIZE];
>  	u8			signature[SNAPSHOT_DIGEST_SIZE];
>  } __aligned(PAGE_SIZE);
> 
> @@ -44,6 +46,8 @@ extern void __init hibernate_image_size_init(void);
>  #ifdef CONFIG_HIBERNATION_ENC_AUTH
>  /* kernel/power/snapshot.c */
>  extern int snapshot_image_verify_decrypt(void);
> +extern int snapshot_prepare_crypto(bool may_sleep, bool create_iv);
> +extern void snapshot_finish_crypto(void);
>  extern int snapshot_prepare_hash(bool may_sleep);
>  extern void snapshot_finish_hash(void);
>  /* kernel/power/snapshot_key.c */
> @@ -53,6 +57,8 @@ extern int snapshot_get_auth_key(u8 *auth_key, bool
> may_sleep); extern int snapshot_get_enc_key(u8 *enc_key, bool may_sleep);
>  #else
>  static inline int snapshot_image_verify_decrypt(void) { return 0; }
> +static inline int snapshot_prepare_crypto(bool may_sleep, bool create_iv) {
> return 0; } +static inline void snapshot_finish_crypto(void) {}
>  static inline int snapshot_prepare_hash(bool may_sleep) { return 0; }
>  static inline void snapshot_finish_hash(void) {}
>  static inline int snapshot_key_init(void) { return 0; }
> diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c
> index e817c035f378..cd10ab5e4850 100644
> --- a/kernel/power/snapshot.c
> +++ b/kernel/power/snapshot.c
> @@ -41,7 +41,11 @@
>  #include <asm/tlbflush.h>
>  #include <asm/io.h>
>  #ifdef CONFIG_HIBERNATION_ENC_AUTH
> +#include <linux/random.h>
> +#include <linux/scatterlist.h>
> +#include <crypto/aes.h>
>  #include <crypto/hash.h>
> +#include <crypto/skcipher.h>
>  #endif
> 
>  #include "power.h"
> @@ -1413,6 +1417,127 @@ static unsigned int nr_copy_pages;
>  static void **h_buf;
> 
>  #ifdef CONFIG_HIBERNATION_ENC_AUTH
> +static struct skcipher_request *sk_req;
> +static u8 iv[AES_BLOCK_SIZE];

May I ask for a different name here? The variable iv is used throughout the 
kernel crypto API and it is always a challenge when doing code reviews to 
trace the right variable when using common names :-)

> +static void *c_buffer;
> +
> +static void init_iv(struct swsusp_info *info)
> +{
> +	memcpy(info->iv, iv, AES_BLOCK_SIZE);
> +}
> +
> +static void load_iv(struct swsusp_info *info)
> +{
> +	memcpy(iv, info->iv, AES_BLOCK_SIZE);
> +}
> +
> +int snapshot_prepare_crypto(bool may_sleep, bool create_iv)
> +{
> +	char enc_key[DERIVED_KEY_SIZE];
> +	struct crypto_skcipher *tfm;
> +	int ret = 0;
> +
> +	ret = snapshot_get_enc_key(enc_key, may_sleep);
> +	if (ret) {
> +		pr_warn_once("enc key is invalid\n");
> +		return -EINVAL;
> +	}
> +
> +	c_buffer = (void *)get_zeroed_page(GFP_KERNEL);
> +	if (!c_buffer) {
> +		pr_err("Allocate crypto buffer page failed\n");
> +		return -ENOMEM;
> +	}
> +
> +	tfm = crypto_alloc_skcipher("ctr(aes)", 0, CRYPTO_ALG_ASYNC);

What is the reason for choosing CTR-AES to store data on disk?

> +	if (IS_ERR(tfm)) {
> +		ret = PTR_ERR(tfm);
> +		pr_err("failed to allocate skcipher (%d)\n", ret);
> +		goto alloc_fail;
> +	}
> +
> +	ret = crypto_skcipher_setkey(tfm, enc_key, AES_MAX_KEY_SIZE);
> +	if (ret) {
> +		pr_err("failed to setkey (%d)\n", ret);
> +		goto set_fail;
> +	}
> +
> +	sk_req = skcipher_request_alloc(tfm, GFP_KERNEL);
> +	if (!sk_req) {
> +		pr_err("failed to allocate request\n");
> +		ret = -ENOMEM;
> +		goto set_fail;
> +	}
> +	if (may_sleep)
> +		skcipher_request_set_callback(sk_req, CRYPTO_TFM_REQ_MAY_SLEEP,
> +						NULL, NULL);
> +	if (create_iv)
> +		get_random_bytes(iv, AES_BLOCK_SIZE);
> +
> +	return 0;
> +
> +set_fail:
> +	crypto_free_skcipher(tfm);
> +alloc_fail:
> +	__free_page(c_buffer);

May I recommend to memzero_explicit(enc_key)?

> +
> +	return ret;
> +}
> +
> +void snapshot_finish_crypto(void)
> +{
> +	struct crypto_skcipher *tfm;
> +
> +	if (!sk_req)
> +		return;
> +
> +	tfm = crypto_skcipher_reqtfm(sk_req);
> +	skcipher_request_zero(sk_req);
> +	skcipher_request_free(sk_req);
> +	crypto_free_skcipher(tfm);
> +	__free_page(c_buffer);
> +	sk_req = NULL;
> +}
> +
> +static int encrypt_data_page(void *hash_buffer)
> +{
> +	struct scatterlist src[1], dst[1];
> +	u8 iv_tmp[AES_BLOCK_SIZE];
> +	int ret = 0;
> +
> +	if (!sk_req)
> +		return 0;
> +
> +	memcpy(iv_tmp, iv, sizeof(iv));

Why do you copy the IV? If I see that right, we would have a key/counter 
collision as follows:

1. you copy the IV into a tmp variable

2. CTR AES is invoked which updates iv_tmp

3. iv_tmp is discarded

4. a repeated invocation of this function would again use the initially set IV 
to copy it into iv_tmp which means that the subsequent cipher operation uses 
yet again the same IV.

If my hunch is correct, the cryptographic strength of the cipher is defeated.

> +	sg_init_one(src, hash_buffer, PAGE_SIZE);
> +	sg_init_one(dst, c_buffer, PAGE_SIZE);
> +	skcipher_request_set_crypt(sk_req, src, dst, PAGE_SIZE, iv_tmp);
> +	ret = crypto_skcipher_encrypt(sk_req);
> +
> +	copy_page(hash_buffer, c_buffer);
> +	memset(c_buffer, 0, PAGE_SIZE);
> +
> +	return ret;
> +}
> +
> +static int decrypt_data_page(void *encrypted_page)

This function looks almost identical to encrypt_data_page - may I suggest to 
collapse it into one function?

> +{
> +	struct scatterlist src[1], dst[1];
> +	u8 iv_tmp[AES_BLOCK_SIZE];
> +	int ret = 0;
> +
> +	memcpy(iv_tmp, iv, sizeof(iv));
> +	sg_init_one(src, encrypted_page, PAGE_SIZE);
> +	sg_init_one(dst, c_buffer, PAGE_SIZE);
> +	skcipher_request_set_crypt(sk_req, src, dst, PAGE_SIZE, iv_tmp);
> +	ret = crypto_skcipher_decrypt(sk_req);
> +
> +	copy_page(encrypted_page, c_buffer);
> +	memset(c_buffer, 0, PAGE_SIZE);
> +
> +	return ret;
> +}
> +
>  /*
>   * Signature of snapshot image
>   */
> @@ -1508,22 +1633,30 @@ int snapshot_image_verify_decrypt(void)
>  	if (ret || !s4_verify_desc)
>  		goto error_prep;
> 
> +	ret = snapshot_prepare_crypto(true, false);
> +	if (ret)
> +		goto error_prep;
> +
>  	for (i = 0; i < nr_copy_pages; i++) {
>  		ret = crypto_shash_update(s4_verify_desc, *(h_buf + i), PAGE_SIZE);
>  		if (ret)
> -			goto error_shash;
> +			goto error_shash_crypto;
> +		ret = decrypt_data_page(*(h_buf + i));
> +		if (ret)
> +			goto error_shash_crypto;
>  	}
> 
>  	ret = crypto_shash_final(s4_verify_desc, s4_verify_digest);
>  	if (ret)
> -		goto error_shash;
> +		goto error_shash_crypto;
> 
>  	pr_debug("Signature %*phN\n", SNAPSHOT_DIGEST_SIZE, signature);
>  	pr_debug("Digest    %*phN\n", SNAPSHOT_DIGEST_SIZE, s4_verify_digest);
>  	if (memcmp(signature, s4_verify_digest, SNAPSHOT_DIGEST_SIZE))
>  		ret = -EKEYREJECTED;
> 
> - error_shash:
> + error_shash_crypto:
> +	snapshot_finish_crypto();
>  	snapshot_finish_hash();
> 
>   error_prep:
> @@ -1564,6 +1697,17 @@ __copy_data_pages(struct memory_bitmap *copy_bm,
> struct memory_bitmap *orig_bm) crypto_buffer = page_address(d_page);
>  		}
> 
> +		/* Encrypt hashed page */
> +		encrypt_data_page(crypto_buffer);
> +
> +		/* Copy encrypted buffer to destination page in high memory */
> +		if (PageHighMem(d_page)) {
> +			void *kaddr = kmap_atomic(d_page);
> +
> +			copy_page(kaddr, crypto_buffer);
> +			kunmap_atomic(kaddr);
> +		}
> +
>  		/* Generate digest */
>  		if (!s4_verify_desc)
>  			continue;
> @@ -1638,6 +1782,8 @@ __copy_data_pages(struct memory_bitmap *copy_bm,
> struct memory_bitmap *orig_bm) }
> 
>  static inline void alloc_h_buf(void) {}
> +static inline void init_iv(struct swsusp_info *info) {}
> +static inline void load_iv(struct swsusp_info *info) {}
>  static inline void init_signature(struct swsusp_info *info) {}
>  static inline void load_signature(struct swsusp_info *info) {}
>  static inline void init_sig_verify(struct trampoline *t) {}
> @@ -2286,6 +2432,7 @@ static int init_header(struct swsusp_info *info)
>  	info->size = info->pages;
>  	info->size <<= PAGE_SHIFT;
>  	info->trampoline_pfn = page_to_pfn(virt_to_page(trampoline_virt));
> +	init_iv(info);
>  	init_signature(info);
>  	return init_header_complete(info);
>  }
> @@ -2524,6 +2671,7 @@ static int load_header(struct swsusp_info *info)
>  		nr_copy_pages = info->image_pages;
>  		nr_meta_pages = info->pages - info->image_pages - 1;
>  		trampoline_pfn = info->trampoline_pfn;
> +		load_iv(info);
>  		load_signature(info);
>  	}
>  	return error;



Ciao
Stephan

  reply	other threads:[~2019-01-06  8:23 UTC|newest]

Thread overview: 90+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-01-03 14:32 [PATCH 0/5 v2][RFC] Encryption and authentication for hibernate snapshot image Lee, Chun-Yi
2019-01-03 14:32 ` [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler Lee, Chun-Yi
2019-01-06  8:01   ` Stephan Mueller
2019-01-06  8:25     ` Stephan Mueller
2019-01-07 15:33     ` joeyli
2019-01-07 15:52       ` Stephan Mueller
2019-01-08  5:03         ` Herbert Xu
2019-01-08  7:09           ` Stephan Mueller
2019-01-08 23:54             ` Andy Lutomirski
2019-01-09  0:44               ` James Bottomley
2019-01-09  1:43                 ` Andy Lutomirski
2019-01-09  6:49                   ` James Bottomley
2019-01-09 18:11                     ` joeyli
2019-01-11 15:53                       ` Jarkko Sakkinen
2019-01-09 18:34                     ` Andy Lutomirski
2019-01-09 19:46                       ` James Bottomley
2019-01-09 20:12                         ` Andy Lutomirski
2019-01-09 21:43                           ` James Bottomley
2019-01-09 22:19                             ` Pavel Machek
2019-01-11 16:04                       ` Jarkko Sakkinen
2019-01-11 14:02                   ` Jarkko Sakkinen
2019-01-11 15:28                     ` James Bottomley
2019-01-18 14:33                       ` Jarkko Sakkinen
2019-01-18 20:59                         ` James Bottomley
2019-01-20 16:02                           ` Jarkko Sakkinen
2019-01-09  6:45                 ` Stephan Mueller
2019-01-09  6:58                   ` James Bottomley
2019-01-09  7:05                     ` Stephan Mueller
2019-01-09  8:21                       ` Eric Biggers
2019-01-09 10:17                         ` Stephan Mueller
2019-01-09 17:34                           ` Eric Biggers
2019-01-09 18:18                             ` Stephan Mueller
2019-01-11 19:08                         ` [PATCH 0/6] General Key Derivation Function Support Stephan Müller
2019-01-11 19:09                           ` [PATCH 1/6] crypto: add template handling for RNGs Stephan Müller
2019-01-11 19:10                           ` [PATCH 2/6] crypto: kdf - SP800-108 Key Derivation Function Stephan Müller
2019-01-12  5:27                             ` Eric Biggers
2019-01-14  9:31                               ` Stephan Müller
2019-01-11 19:10                           ` [PATCH 3/6] crypto: kdf - add known answer tests Stephan Müller
2019-01-12  5:26                             ` Eric Biggers
2019-01-14  9:26                               ` Stephan Müller
2019-01-11 19:10                           ` [PATCH 4/6] crypto: hkdf - RFC5869 Key Derivation Function Stephan Müller
2019-01-12  5:12                             ` Eric Biggers
2019-01-12  9:55                               ` Herbert Xu
2019-01-13  7:56                                 ` Stephan Müller
2019-01-13 16:52                                   ` James Bottomley
2019-01-14  9:30                               ` Stephan Müller
2019-01-14 17:53                                 ` Eric Biggers
2019-01-14 18:44                                   ` Stephan Mueller
2019-01-11 19:10                           ` [PATCH 5/6] crypto: hkdf - add known answer tests Stephan Müller
2019-01-12  5:19                             ` Eric Biggers
2019-01-14  9:25                               ` Stephan Müller
2019-01-14 17:44                                 ` Eric Biggers
2019-01-11 19:11                           ` [PATCH 6/6] crypto: tcrypt - add KDF test invocation Stephan Müller
2019-01-16 11:06                           ` [PATCH v2 0/6] General Key Derivation Function Support Stephan Müller
2019-01-16 11:07                             ` [PATCH v2 1/6] crypto: add template handling for RNGs Stephan Müller
2019-01-16 11:08                             ` [PATCH v2 2/6] crypto: kdf - SP800-108 Key Derivation Function Stephan Müller
2019-01-16 11:08                             ` [PATCH v2 3/6] crypto: kdf - add known answer tests Stephan Müller
2019-01-16 11:08                             ` [PATCH v2 4/6] crypto: hkdf - HMAC-based Extract-and-Expand KDF Stephan Müller
2019-01-16 11:09                             ` [PATCH v2 5/6] crypto: hkdf - add known answer tests Stephan Müller
2019-01-16 11:09                             ` [PATCH v2 6/6] crypto: tcrypt - add KDF test invocation Stephan Müller
2019-01-28 10:07                             ` [PATCH v2 0/6] General Key Derivation Function Support Stephan Mueller
2019-01-30 10:08                               ` Herbert Xu
2019-01-30 14:39                                 ` Stephan Mueller
2019-02-08  7:45                                   ` Herbert Xu
2019-02-08  8:00                                     ` Stephan Mueller
2019-02-08  8:05                                       ` Herbert Xu
2019-02-08  8:17                                         ` Stephan Mueller
2019-02-19  5:44                                           ` Herbert Xu
2019-01-09 15:34                       ` [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler James Bottomley
2019-01-09  6:27               ` Stephan Mueller
2019-01-03 14:32 ` [PATCH 2/5] PM / hibernate: Generate and verify signature for snapshot image Lee, Chun-Yi
2019-01-06  8:09   ` Stephan Mueller
2019-01-07 18:58   ` Dan Carpenter
2019-01-03 14:32 ` [PATCH 3/5] PM / hibernate: Encrypt " Lee, Chun-Yi
2019-01-06  8:23   ` Stephan Mueller [this message]
2019-01-03 14:32 ` [PATCH 4/5 v2] PM / hibernate: Erase the snapshot master key in snapshot pages Lee, Chun-Yi
2019-01-03 14:32 ` [PATCH 5/5 v2] PM / hibernate: An option to request that snapshot image must be authenticated Lee, Chun-Yi
2019-01-06 18:10 ` [PATCH 0/5 v2][RFC] Encryption and authentication for hibernate snapshot image Pavel Machek
2019-01-07 17:37   ` joeyli
2019-01-07 18:07     ` Pavel Machek
2019-01-08 21:41     ` Andy Lutomirski
2019-01-08 23:42       ` Pavel Machek
2019-01-09 16:39       ` joeyli
2019-01-09 16:47         ` Stephan Mueller
2019-01-11 14:29           ` joeyli
2019-01-09 16:51         ` joeyli
2019-01-09 18:47         ` Andy Lutomirski
2019-01-10 15:12           ` joeyli
2019-01-11  1:09             ` Andy Lutomirski
2019-01-11 14:59               ` joeyli

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=8502734.OEeY8cfajc@tauon.chronox.de \
    --to=smueller@chronox.de \
    --cc=dhowells@redhat.com \
    --cc=ggherdovich@suse.cz \
    --cc=jannh@google.com \
    --cc=jlee@suse.com \
    --cc=joeyli.kernel@gmail.com \
    --cc=keyrings@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=luto@kernel.org \
    --cc=oneukum@suse.com \
    --cc=pavel@ucw.cz \
    --cc=rafael.j.wysocki@intel.com \
    --cc=rdunlap@infradead.org \
    --cc=rjw@rjwysocki.net \
    --cc=yu.c.chen@intel.com \
    --cc=yu.chen.surf@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox