Received: by 2002:ac0:a5a6:0:0:0:0:0 with SMTP id m35-v6csp724613imm; Thu, 13 Sep 2018 06:52:30 -0700 (PDT) X-Google-Smtp-Source: ANB0VdbJFl1NgPYpv4fZG+mfd35LmkbWZKReyyqm8FR1+R86wlT7EPGdqq/d0i2j73BmkggcdGVS X-Received: by 2002:a63:d09:: with SMTP id c9-v6mr7404216pgl.314.1536846750799; Thu, 13 Sep 2018 06:52:30 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1536846750; cv=none; d=google.com; s=arc-20160816; b=y0/hbTtizPwKuuwqLtWnVVYNepgoaYrU8zuse2xTtqj054xGx2oT2kobDaSIqdLgwz nVahcyPkYsZTKEYISSbV/571HAvxK9grLVpLvJgQ8lEbR+QQJ3ghvlXFFuQ6VupkyU+V bxDtTeHizGU5lYkcIQSK2TggIcuSjM5LuAh4wtJ2X9vKZML6WD66SDT6jz4zFoLScIQ8 eK54bTrad1YVYRryUBQIka4lfmOhjLbiSL2DnoKol2s9W03a8Q4BocRwjwe9N4/K6YN6 uXKvOA5buKn8d6XclZ4DjUD5Ws6xHejZqhRQJbI7Vyg1x/S7RXiNthRKG8YYuax4ZhOC 3dSQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:user-agent:in-reply-to :content-disposition:mime-version:references:message-id:subject:cc :to:from:date; bh=7aTFq4Xe39rzzJ9iU63aUYFry7Sx8EkD45mk5s+M6GM=; b=eeHjktwQUWdBRyaRCYaxia1ObiewkontSbLO2r/F848eoR8b4AkOZM7U+6uVpt1zvT BpVSpMXR48ZhJbvDvS6nHK0UaeKnZWVXpaLiUUbveQYOv5syuCmWAkaOgPQL6YK+Eo1K mZChoF0VJeixzTLHKzH55JxP7MuXBQ07y0uzbfxmbMUX9JOieFmh2meJnteAJ6/XVqco DjtHv5vOy/U9N7/N0DCzOwuma2MrxZCkcc1Ve6ACTv0xMbsZlPukvITjE3NmCp92k5RQ HJf86MGyqvIUnkwPweza36KXD+qVddOPmwp4xzc8rsoRqVO7hs+LxPkBPj/f0PyvwcDa Carg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id z19-v6si4324950pgi.388.2018.09.13.06.52.15; Thu, 13 Sep 2018 06:52:30 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730904AbeIMTB3 (ORCPT + 99 others); Thu, 13 Sep 2018 15:01:29 -0400 Received: from mga18.intel.com ([134.134.136.126]:28719 "EHLO mga18.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730390AbeIMTB2 (ORCPT ); Thu, 13 Sep 2018 15:01:28 -0400 X-Amp-Result: UNSCANNABLE X-Amp-File-Uploaded: False Received: from fmsmga008.fm.intel.com ([10.253.24.58]) by orsmga106.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 13 Sep 2018 06:51:53 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.53,369,1531810800"; d="scan'208";a="70573476" Received: from chenyu-desktop.sh.intel.com (HELO chenyu-desktop) ([10.239.160.116]) by fmsmga008.fm.intel.com with ESMTP; 13 Sep 2018 06:51:51 -0700 Date: Thu, 13 Sep 2018 21:58:32 +0800 From: Yu Chen To: "Lee, Chun-Yi" Cc: "Rafael J . Wysocki" , Pavel Machek , linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, "Lee, Chun-Yi" , "Rafael J. Wysocki" , Oliver Neukum , Ryan Chen , David Howells , Giovanni Gherdovich Subject: Re: [PATCH 1/5] PM / hibernate: Create snapshot keys handler Message-ID: <20180913135832.GA8155@chenyu-desktop> References: <20180912142337.21955-1-jlee@suse.com> <20180912142337.21955-2-jlee@suse.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20180912142337.21955-2-jlee@suse.com> User-Agent: Mutt/1.9.4 (2018-02-28) Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Wed, Sep 12, 2018 at 10:23:33PM +0800, Lee, Chun-Yi wrote: > This patch adds a snapshot keys handler for using the key retention > service api to create keys for snapshot image encryption and > authentication. > > This handler uses TPM trusted key as the snapshot master key, and the > encryption key and authentication key are derived from the snapshot > key. The user defined key can also be used as the snapshot master key > , but user must be aware that the security of user key relies on user > space. > In case the kernel provides mechanism to generate key from passphase, the master key could also be generated in kernel space other than TPM. It seems than snapshot_key_init() is easy to add the interface for that, right? > The name of snapshot key is fixed to "swsusp-kmk". User should use > the keyctl tool to load the key blob to root's user keyring. e.g. > > # /bin/keyctl add trusted swsusp-kmk "load `cat swsusp-kmk.blob`" @u > > or create a new user key. e.g. > > # /bin/keyctl add user swsusp-kmk password @u > > Then the disk_kmk sysfs file can be used to trigger the initialization > of snapshot key: > > # echo 1 > /sys/power/disk_kmk > > After the initialization be triggered, the secret in the payload of > swsusp-key will be copied by hibernation and be erased. Then user can > use keyctl to remove swsusp-kmk key from root's keyring. > > If user does not trigger the initialization by disk_kmk file after > swsusp-kmk be loaded to kernel. Then the snapshot key will be > initialled when hibernation be triggered. > > Cc: "Rafael J. Wysocki" > Cc: Pavel Machek > Cc: Chen Yu > Cc: Oliver Neukum > Cc: Ryan Chen > Cc: David Howells > Cc: Giovanni Gherdovich > Signed-off-by: "Lee, Chun-Yi" > --- > kernel/power/Kconfig | 14 +++ > kernel/power/Makefile | 1 + > kernel/power/hibernate.c | 36 +++++++ > kernel/power/power.h | 16 +++ > kernel/power/snapshot_key.c | 237 ++++++++++++++++++++++++++++++++++++++++++++ > 5 files changed, 304 insertions(+) > create mode 100644 kernel/power/snapshot_key.c > > diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig > index 3a6c2f87699e..7c5c30149dbc 100644 > --- a/kernel/power/Kconfig > +++ b/kernel/power/Kconfig > @@ -76,6 +76,20 @@ config HIBERNATION > > For more information take a look at . > > +config HIBERNATION_ENC_AUTH > + bool "Hibernation encryption and authentication" > + depends on HIBERNATION > + depends on TRUSTED_KEYS > + select CRYPTO_AES > + select CRYPTO_HMAC > + select CRYPTO_SHA512 > + help > + This option will encrypt and authenticate the memory snapshot image > + of hibernation. It prevents that the snapshot image be arbitrary > + modified. User can use TPMs trusted key or user defined key as the > + master key of hibernation. The TPM trusted key depends on TPM. The > + security of user defined key relies on user space. > + > config ARCH_SAVE_PAGE_KEYS > bool > > diff --git a/kernel/power/Makefile b/kernel/power/Makefile > index a3f79f0eef36..bddca7b79a28 100644 > --- a/kernel/power/Makefile > +++ b/kernel/power/Makefile > @@ -11,6 +11,7 @@ obj-$(CONFIG_FREEZER) += process.o > obj-$(CONFIG_SUSPEND) += suspend.o > obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o > obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o > +obj-$(CONFIG_HIBERNATION_ENC_AUTH) += snapshot_key.o > obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o > obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o > > diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c > index abef759de7c8..18d13cbf0591 100644 > --- a/kernel/power/hibernate.c > +++ b/kernel/power/hibernate.c > @@ -1034,6 +1034,39 @@ static ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr, > > power_attr(disk); > > +#ifdef CONFIG_HIBERNATION_ENC_AUTH > +static ssize_t disk_kmk_show(struct kobject *kobj, struct kobj_attribute *attr, > + char *buf) > +{ > + if (snapshot_key_initialized()) > + return sprintf(buf, "initialized\n"); > + else > + return sprintf(buf, "uninitialized\n"); > +} > + > +static ssize_t disk_kmk_store(struct kobject *kobj, struct kobj_attribute *attr, > + const char *buf, size_t n) > +{ Does kmk mean kernel master key? It might looks unclear from first glance, how about disk_genkey_store()? > + int error = 0; > + char *p; > + int len; > + > + if (!capable(CAP_SYS_ADMIN)) > + return -EPERM; > + > + p = memchr(buf, '\n', n); > + len = p ? p - buf : n; > + if (strncmp(buf, "1", len)) > + return -EINVAL; Why user is not allowed to disable(remove) it by echo 0 ? > + > + error = snapshot_key_init(); > + > + return error ? error : n; > +} > + > +power_attr(disk_kmk); > +#endif /* !CONFIG_HIBERNATION_ENC_AUTH */ > + > static ssize_t resume_show(struct kobject *kobj, struct kobj_attribute *attr, > char *buf) > { > @@ -1138,6 +1171,9 @@ power_attr(reserved_size); > > static struct attribute * g[] = { > &disk_attr.attr, > +#ifdef CONFIG_HIBERNATION_ENC_AUTH > + &disk_kmk_attr.attr, > +#endif > &resume_offset_attr.attr, > &resume_attr.attr, > &image_size_attr.attr, > diff --git a/kernel/power/power.h b/kernel/power/power.h > index 9e58bdc8a562..fe2dfa0d4d36 100644 > --- a/kernel/power/power.h > +++ b/kernel/power/power.h > @@ -4,6 +4,12 @@ > #include > #include > #include > +#include > + > +/* The max size of encrypted key blob */ > +#define KEY_BLOB_BUFF_LEN 512 > +#define SNAPSHOT_KEY_SIZE SHA512_DIGEST_SIZE > +#define DERIVED_KEY_SIZE SHA512_DIGEST_SIZE > > struct swsusp_info { > struct new_utsname uts; > @@ -20,6 +26,16 @@ struct swsusp_info { > extern void __init hibernate_reserved_size_init(void); > extern void __init hibernate_image_size_init(void); > > +#ifdef CONFIG_HIBERNATION_ENC_AUTH > +/* kernel/power/snapshot_key.c */ > +extern int snapshot_key_init(void); > +extern bool snapshot_key_initialized(void); > +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_key_init(void) { return 0; } > +#endif /* !CONFIG_HIBERNATION_ENC_AUTH */ > + > #ifdef CONFIG_ARCH_HIBERNATION_HEADER > /* Maximum size of architecture specific data in a hibernation header */ > #define MAX_ARCH_HEADER_SIZE (sizeof(struct new_utsname) + 4) > diff --git a/kernel/power/snapshot_key.c b/kernel/power/snapshot_key.c > new file mode 100644 > index 000000000000..091f33929b47 > --- /dev/null > +++ b/kernel/power/snapshot_key.c > @@ -0,0 +1,237 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +/* snapshot keys handler > + * > + * Copyright (C) 2018 Lee, Chun-Yi > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public Licence > + * as published by the Free Software Foundation; either version > + * 2 of the Licence, or (at your option) any later version. > + */ > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "power.h" > + > +static const char hash_alg[] = "sha512"; > +static struct crypto_shash *hash_tfm; > + > +/* The master key of snapshot */ > +static struct snapshot_key { > + const char *key_name; > + bool initialized; > + unsigned int key_len; > + u8 key[SNAPSHOT_KEY_SIZE]; > +} skey = { > + .key_name = "swsusp-kmk", > +}; > + > +static int calc_hash(u8 *digest, const u8 *buf, unsigned int buflen, > + bool may_sleep) calc_hash() is used for both signature and encryption, could it be integrated in snapshot_key_init() thus the code could be re-used? > +{ > + SHASH_DESC_ON_STACK(desc, hash_tfm); Per commit c2cd0b08e1efd9ee58d09049a6c77e5efa0ef627 SHASH_DESC_ON_STACK() should not be used. > + int err; > + > + desc->tfm = hash_tfm; > + desc->flags = may_sleep ? CRYPTO_TFM_REQ_MAY_SLEEP : 0; > + > + err = crypto_shash_digest(desc, buf, buflen, digest); Check the err? > + shash_desc_zero(desc); > + return err; > +} > + > +static int calc_key_hash(u8 *key, unsigned int key_len, const char *salt, > + u8 *hash, bool may_sleep) > +{ > + unsigned int salted_buf_len; > + u8 *salted_buf; > + int ret; > + > + if (!key || !hash_tfm || !hash) > + return -EINVAL; > + > + salted_buf_len = strlen(salt) + 1 + SNAPSHOT_KEY_SIZE; > + salted_buf = kzalloc(salted_buf_len, > + may_sleep ? GFP_KERNEL : GFP_ATOMIC); > + if (!salted_buf) > + return -ENOMEM; > + > + strcpy(salted_buf, salt); > + memcpy(salted_buf + strlen(salted_buf) + 1, key, key_len); > + > + ret = calc_hash(hash, salted_buf, salted_buf_len, may_sleep); > + memzero_explicit(salted_buf, salted_buf_len); > + kzfree(salted_buf); > + > + return ret; > +} > + > +/* Derive authentication/encryption key */ > +static int get_derived_key(u8 *derived_key, const char *derived_type_str, > + bool may_sleep) > +{ > + int ret; > + > + if (!skey.initialized || !hash_tfm) > + return -EINVAL; > + > + ret = calc_key_hash(skey.key, skey.key_len, derived_type_str, > + derived_key, may_sleep); > + > + return ret; > +} > + > +int snapshot_get_auth_key(u8 *auth_key, bool may_sleep) > +{ > + return get_derived_key(auth_key, "AUTH_KEY", may_sleep); > +} > + > +int snapshot_get_enc_key(u8 *enc_key, bool may_sleep) > +{ > + return get_derived_key(enc_key, "ENC_KEY", may_sleep); > +} > + > +bool snapshot_key_initialized(void) > +{ > + return skey.initialized; > +} > + > +static bool invalid_key(u8 *key, unsigned int key_len) > +{ > + int i; > + > + if (!key || !key_len) > + return true; > + > + if (key_len > SNAPSHOT_KEY_SIZE) { > + pr_warn("Size of swsusp key more than: %d.\n", > + SNAPSHOT_KEY_SIZE); > + return true; > + } > + > + /* zero keyblob is invalid key */ > + for (i = 0; i < key_len; i++) { > + if (key[i] != 0) > + return false; > + } > + pr_warn("The swsusp key should not be zero.\n"); > + > + return true; > +} > + > +static int trusted_key_init(void) > +{ > + struct trusted_key_payload *tkp; > + struct key *key; > + int err; > + > + pr_debug("%s\n", __func__); > + > + /* find out swsusp-key */ > + key = request_key(&key_type_trusted, skey.key_name, NULL); > + if (IS_ERR(key)) { > + pr_err("Request key error: %ld\n", PTR_ERR(key)); > + err = PTR_ERR(key); > + return err; > + } > + > + down_write(&key->sem); > + tkp = key->payload.data[0]; > + if (invalid_key(tkp->key, tkp->key_len)) { > + err = -EINVAL; > + goto key_invalid; > + } > + skey.key_len = tkp->key_len; > + memcpy(skey.key, tkp->key, tkp->key_len); > + /* burn the original key contents */ > + memzero_explicit(tkp->key, tkp->key_len); > + > +key_invalid: > + up_write(&key->sem); > + key_put(key); > + > + return err; > +} > + > +static int user_key_init(void) > +{ > + struct user_key_payload *ukp; > + struct key *key; > + int err = 0; > + > + pr_debug("%s\n", __func__); > + > + /* find out swsusp-key */ > + key = request_key(&key_type_user, skey.key_name, NULL); > + if (IS_ERR(key)) { > + pr_err("Request key error: %ld\n", PTR_ERR(key)); > + err = PTR_ERR(key); > + return err; > + } > + > + down_write(&key->sem); > + ukp = user_key_payload_locked(key); > + if (!ukp) { > + /* key was revoked before we acquired its semaphore */ > + err = -EKEYREVOKED; > + goto key_invalid; > + } > + if (invalid_key(ukp->data, ukp->datalen)) { > + err = -EINVAL; > + goto key_invalid; > + } > + skey.key_len = ukp->datalen; > + memcpy(skey.key, ukp->data, ukp->datalen); > + /* burn the original key contents */ > + memzero_explicit(ukp->data, ukp->datalen); > + > +key_invalid: > + up_write(&key->sem); > + key_put(key); > + > + return err; > +} > + > +/* this function may sleeps */ > +int snapshot_key_init(void) > +{ > + int err; > + > + pr_debug("%s\n", __func__); > + > + if (skey.initialized) > + return 0; > + > + hash_tfm = crypto_alloc_shash(hash_alg, 0, CRYPTO_ALG_ASYNC); > + if (IS_ERR(hash_tfm)) { > + pr_err("Can't allocate %s transform: %ld\n", > + hash_alg, PTR_ERR(hash_tfm)); > + return PTR_ERR(hash_tfm); > + } > + > + err = trusted_key_init(); > + if (err) > + err = user_key_init(); > + if (err) > + goto key_fail; > + > + skey.initialized = true; > + > + pr_info("Snapshot key is initialled.\n"); > + > + return 0; > + > +key_fail: > + crypto_free_shash(hash_tfm); > + hash_tfm = NULL; > + > + return err; > +} > -- > 2.13.6 >