2019-01-03 21:30:22

by Chun-Yi Lee

[permalink] [raw]
Subject: [PATCH 0/5 v2][RFC] Encryption and authentication for hibernate snapshot image

Hi,

This patchset is the implementation of encryption and authentication
for hibernate snapshot image. The image will be encrypted by AES and
authenticated by HMAC.

The hibernate function can be used to snapshot memory pages to an image,
then kernel restores the image to memory space in a appropriate time.
There have secrets in snapshot image and cracker may modifies it for
hacking system. Encryption and authentication of snapshot image can protect
the system.

Hibernate function requests the master key through key retention service.
The snapshot master key can be a trusted key or a user defined key. The
name of snapshot master key is fixed to "swsusp-kmk". User should loads
swsusp-kmk to kernel by keyctl tool before the hibernation resume.
e.g. The swsusp-kmk must be loaded before systemd-hibernate-resume

The TPM trusted key type is preferred to be the master key. But user
defined key can also be used for testing or when the platform doesn't
have TPM. User must be aware that the security of user key relies on
user space. If the root account be compromised, then the user key will
easy to be grabbed.

v2:
- Fixed bug of trusted_key_init's return value.
- Fixed wording in Kconfig
- Removed VLA usage
- Removed the checking of capability for writing disk_kmk.
- Fixed Kconfig, select trusted key.
- Add memory barrier before setting key initialized flag.
- Add memory barrier after cleaning key initialized flag.

Cc: "Rafael J. Wysocki" <[email protected]>
Cc: Pavel Machek <[email protected]>
Cc: Chen Yu <[email protected]>
Cc: Oliver Neukum <[email protected]>
Cc: Ryan Chen <[email protected]>
Cc: David Howells <[email protected]>
Cc: Giovanni Gherdovich <[email protected]>
Cc: Randy Dunlap <[email protected]>
Cc: Jann Horn <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Signed-off-by: "Lee, Chun-Yi" <[email protected]>

Lee, Chun-Yi (5):
PM / hibernate: Create snapshot keys handler
PM / hibernate: Generate and verify signature for snapshot image
PM / hibernate: Encrypt snapshot image
PM / hibernate: Erase the snapshot master key in snapshot pages
PM / hibernate: An option to request that snapshot image must be
authenticated

Documentation/admin-guide/kernel-parameters.txt | 6 +
include/linux/kernel.h | 3 +-
kernel/panic.c | 1 +
kernel/power/Kconfig | 25 +
kernel/power/Makefile | 1 +
kernel/power/hibernate.c | 59 ++-
kernel/power/power.h | 59 +++
kernel/power/snapshot.c | 576 +++++++++++++++++++++++-
kernel/power/snapshot_key.c | 312 +++++++++++++
kernel/power/swap.c | 6 +
kernel/power/user.c | 12 +
11 files changed, 1042 insertions(+), 18 deletions(-)
create mode 100644 kernel/power/snapshot_key.c

--
2.13.6



2019-01-03 21:12:09

by Chun-Yi Lee

[permalink] [raw]
Subject: [PATCH 4/5 v2] PM / hibernate: Erase the snapshot master key in snapshot pages

If the encryption key be guessed then the snapshot master key can
also be grabbed from snapshot image. Which means that the authentication
key can also be calculated. So kernel erases master key in snapshot
pages.

Because the master key in image kernel be erased, kernel uses the
trampoline page to forward snapshot master key to image kernel.

v2:
- Add memory barrier after cleaning key initialized flag.

Cc: "Rafael J. Wysocki" <[email protected]>
Cc: Pavel Machek <[email protected]>
Cc: Chen Yu <[email protected]>
Cc: Oliver Neukum <[email protected]>
Cc: Ryan Chen <[email protected]>
Cc: David Howells <[email protected]>
Cc: Giovanni Gherdovich <[email protected]>
Cc: Randy Dunlap <[email protected]>
Cc: Jann Horn <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Signed-off-by: "Lee, Chun-Yi" <[email protected]>
---
kernel/power/power.h | 6 ++++
kernel/power/snapshot.c | 5 ++++
kernel/power/snapshot_key.c | 67 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 78 insertions(+)

diff --git a/kernel/power/power.h b/kernel/power/power.h
index 41263fdd3a54..d2fc73b2e200 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -36,6 +36,7 @@ struct swsusp_info {
struct trampoline {
bool snapshot_key_valid;
int sig_verify_ret;
+ u8 snapshot_key[SNAPSHOT_KEY_SIZE];
} __aligned(PAGE_SIZE);

#ifdef CONFIG_HIBERNATION
@@ -55,6 +56,9 @@ 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);
+extern void snapshot_key_page_erase(unsigned long pfn, void *buff_addr);
+extern void snapshot_key_trampoline_backup(struct trampoline *t);
+extern void snapshot_key_trampoline_restore(struct trampoline *t);
#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; }
@@ -62,6 +66,8 @@ 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; }
+static inline void snapshot_key_trampoline_backup(struct trampoline *t) {}
+static inline void snapshot_key_trampoline_restore(struct trampoline *t) {}
#endif /* !CONFIG_HIBERNATION_ENC_AUTH */

#ifdef CONFIG_ARCH_HIBERNATION_HEADER
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c
index cd10ab5e4850..80ed8e7c5ed8 100644
--- a/kernel/power/snapshot.c
+++ b/kernel/power/snapshot.c
@@ -1697,6 +1697,9 @@ __copy_data_pages(struct memory_bitmap *copy_bm, struct memory_bitmap *orig_bm)
crypto_buffer = page_address(d_page);
}

+ /* Erase key data in snapshot */
+ snapshot_key_page_erase(pfn, crypto_buffer);
+
/* Encrypt hashed page */
encrypt_data_page(crypto_buffer);

@@ -2482,6 +2485,7 @@ void snapshot_init_trampoline(void)
t = (struct trampoline *)trampoline_buff;

init_sig_verify(t);
+ snapshot_key_trampoline_backup(t);

pr_info("Hibernation trampoline page prepared\n");
}
@@ -2505,6 +2509,7 @@ void snapshot_restore_trampoline(void)
t = (struct trampoline *)trampoline_virt;

handle_sig_verify(t);
+ snapshot_key_trampoline_restore(t);
snapshot_free_trampoline();
}

diff --git a/kernel/power/snapshot_key.c b/kernel/power/snapshot_key.c
index 3a569b505d8d..9d478c27d6b9 100644
--- a/kernel/power/snapshot_key.c
+++ b/kernel/power/snapshot_key.c
@@ -29,11 +29,27 @@ static struct snapshot_key {
const char *key_name;
bool initialized;
unsigned int key_len;
+ unsigned long pfn; /* pfn of keyblob */
+ unsigned long addr_offset; /* offset in page for keyblob */
u8 key[SNAPSHOT_KEY_SIZE];
+ u8 fingerprint[SHA512_DIGEST_SIZE]; /* fingerprint of keyblob */
} skey = {
.key_name = "swsusp-kmk",
};

+static void snapshot_key_clean(void)
+{
+ crypto_free_shash(hash_tfm);
+ hash_tfm = NULL;
+ skey.initialized = false;
+ barrier();
+ skey.pfn = 0;
+ skey.key_len = 0;
+ skey.addr_offset = 0;
+ memzero_explicit(skey.key, SNAPSHOT_KEY_SIZE);
+ memzero_explicit(skey.fingerprint, SHA512_DIGEST_SIZE);
+}
+
static int calc_hash(u8 *digest, const u8 *buf, unsigned int buflen,
bool may_sleep)
{
@@ -81,6 +97,53 @@ static int calc_key_hash(u8 *key, unsigned int key_len, const char *salt,
return ret;
}

+static int get_key_fingerprint(u8 *fingerprint, u8 *key, unsigned int key_len,
+ bool may_sleep)
+{
+ return calc_key_hash(key, key_len, "FINGERPRINT", fingerprint, may_sleep);
+}
+
+void snapshot_key_page_erase(unsigned long pfn, void *buff_addr)
+{
+ if (!skey.initialized || pfn != skey.pfn)
+ return;
+
+ /* erase key data from snapshot buffer page */
+ if (!memcmp(skey.key, buff_addr + skey.addr_offset, skey.key_len)) {
+ memzero_explicit(buff_addr + skey.addr_offset, skey.key_len);
+ pr_info("Erased swsusp key in snapshot pages.\n");
+ }
+}
+
+/* this function may sleeps because snapshot_key_init() */
+void snapshot_key_trampoline_backup(struct trampoline *t)
+{
+ if (!t || snapshot_key_init())
+ return;
+
+ memcpy(t->snapshot_key, skey.key, skey.key_len);
+}
+
+/* Be called after snapshot image restored success */
+void snapshot_key_trampoline_restore(struct trampoline *t)
+{
+ u8 fingerprint[SHA512_DIGEST_SIZE];
+
+ if (!skey.initialized || !t)
+ return;
+
+ /* check key fingerprint before restore */
+ get_key_fingerprint(fingerprint, t->snapshot_key, skey.key_len, true);
+ if (memcmp(skey.fingerprint, fingerprint, SHA512_DIGEST_SIZE)) {
+ pr_warn("Restored swsusp key failed, fingerprint mismatch.\n");
+ snapshot_key_clean();
+ return;
+ }
+
+ memcpy(skey.key, t->snapshot_key, skey.key_len);
+ memzero_explicit(t->snapshot_key, SNAPSHOT_KEY_SIZE);
+}
+
/* Derive authentication/encryption key */
static int get_derived_key(u8 *derived_key, const char *derived_type_str,
bool may_sleep)
@@ -230,10 +293,14 @@ int snapshot_key_init(void)
if (err)
goto key_fail;

+ skey.pfn = page_to_pfn(virt_to_page(skey.key));
+ skey.addr_offset = (unsigned long) skey.key & ~PAGE_MASK;
+ get_key_fingerprint(skey.fingerprint, skey.key, skey.key_len, true);
barrier();
skey.initialized = true;

pr_info("Snapshot key is initialled.\n");
+ pr_debug("Fingerprint %*phN\n", SHA512_DIGEST_SIZE, skey.fingerprint);

return 0;

--
2.13.6


2019-01-03 21:30:03

by Chun-Yi Lee

[permalink] [raw]
Subject: [PATCH 5/5 v2] PM / hibernate: An option to request that snapshot image must be authenticated

This kernel option is similar to the option for kernel module signature
verification. When this option is unselected, kernel will be tainted by
restored from a snapshot image without (valid) signature.

When the option is selected, kernel will refuse the system to be restored
from a unauthenticated image. The hibernation resume process will be stopped
, the snapshot image will be discarded and system just boots as normal.

The hibernation can be triggered without snapshot master key when this
option is unselected. But kernel will be tainted after hibernation resume.

v2:
- Fixed wording in Kconfig

Cc: "Rafael J. Wysocki" <[email protected]>
Cc: Pavel Machek <[email protected]>
Cc: Chen Yu <[email protected]>
Cc: Oliver Neukum <[email protected]>
Cc: Ryan Chen <[email protected]>
Cc: David Howells <[email protected]>
Cc: Giovanni Gherdovich <[email protected]>
Cc: Randy Dunlap <[email protected]>
Cc: Jann Horn <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Signed-off-by: "Lee, Chun-Yi" <[email protected]>
---
Documentation/admin-guide/kernel-parameters.txt | 6 ++++
include/linux/kernel.h | 3 +-
kernel/panic.c | 1 +
kernel/power/Kconfig | 11 +++++++
kernel/power/hibernate.c | 8 +++--
kernel/power/power.h | 5 ++++
kernel/power/snapshot.c | 40 +++++++++++++++++++++++--
kernel/power/user.c | 2 +-
8 files changed, 69 insertions(+), 7 deletions(-)

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 37e235be1d35..f103b5d4676f 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -3991,6 +3991,12 @@
protect_image Turn on image protection during restoration
(that will set all pages holding image data
during restoration read-only).
+ enforce_auth When HIBERNATION_ENC_AUTH is set, this means
+ that snapshot image without (valid) signature
+ will fail to restore.
+ Note that if HIBERNATION_ENC_AUTH_FORCE is
+ set, that is always true, so this option does
+ nothing.

retain_initrd [RAM] Keep initrd memory after extraction

diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index d6aac75b51ba..61714489cb57 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -598,7 +598,8 @@ extern enum system_states {
#define TAINT_LIVEPATCH 15
#define TAINT_AUX 16
#define TAINT_RANDSTRUCT 17
-#define TAINT_FLAGS_COUNT 18
+#define TAINT_UNSAFE_HIBERNATE 18
+#define TAINT_FLAGS_COUNT 19

struct taint_flag {
char c_true; /* character printed when tainted */
diff --git a/kernel/panic.c b/kernel/panic.c
index d10c340c43b0..7179c6d8d38f 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -335,6 +335,7 @@ const struct taint_flag taint_flags[TAINT_FLAGS_COUNT] = {
[ TAINT_LIVEPATCH ] = { 'K', ' ', true },
[ TAINT_AUX ] = { 'X', ' ', true },
[ TAINT_RANDSTRUCT ] = { 'T', ' ', true },
+ [ TAINT_UNSAFE_HIBERNATE ] = { 'H', ' ', true },
};

/**
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 506a3c5a7a0d..cf1896c32014 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -90,6 +90,17 @@ config HIBERNATION_ENC_AUTH
as the master key of hibernation. The TPM trusted key depends on TPM.
The security of user defined key relies on user space.

+config HIBERNATION_ENC_AUTH_FORCE
+ bool "Require hibernate snapshot image to be validly signed"
+ depends on HIBERNATION_ENC_AUTH
+ help
+ This option will prevent that a snapshot image without (valid)
+ signature be restored. Without this option, an unauthenticated
+ snapshot image can be restored but the restored kernel will be
+ tainted, which also means that the hibernation can be triggered
+ without snapshot key but kernel will be tainted without this
+ option.
+
config ARCH_SAVE_PAGE_KEYS
bool

diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
index 5ac2ab6f4a0e..9515bad0897e 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -272,11 +272,11 @@ static int create_image(int platform_mode)
int error;

error = snapshot_prepare_hash(false);
- if (error)
+ if (error && snapshot_is_enforce_auth())
return error;

error = snapshot_prepare_crypto(false, true);
- if (error)
+ if (error && snapshot_is_enforce_auth())
goto finish_hash;

error = dpm_suspend_end(PMSG_FREEZE);
@@ -708,7 +708,7 @@ int hibernate(void)
}

error = snapshot_key_init();
- if (error)
+ if (error && snapshot_is_enforce_auth())
return error;

error = snapshot_create_trampoline();
@@ -1248,6 +1248,8 @@ static int __init hibernate_setup(char *str)
} else if (IS_ENABLED(CONFIG_STRICT_KERNEL_RWX)
&& !strncmp(str, "protect_image", 13)) {
enable_restore_image_protection();
+ } else if (!strncmp(str, "enforce_auth", 10)) {
+ snapshot_set_enforce_auth();
}
return 1;
}
diff --git a/kernel/power/power.h b/kernel/power/power.h
index d2fc73b2e200..edb63991bcdc 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -36,6 +36,7 @@ struct swsusp_info {
struct trampoline {
bool snapshot_key_valid;
int sig_verify_ret;
+ bool enforce_auth;
u8 snapshot_key[SNAPSHOT_KEY_SIZE];
} __aligned(PAGE_SIZE);

@@ -51,6 +52,8 @@ 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);
+extern void snapshot_set_enforce_auth(void);
+extern int snapshot_is_enforce_auth(void);
/* kernel/power/snapshot_key.c */
extern int snapshot_key_init(void);
extern bool snapshot_key_initialized(void);
@@ -65,6 +68,8 @@ static inline int snapshot_prepare_crypto(bool may_sleep, bool create_iv) { retu
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 void snapshot_set_enforce_auth(void) {}
+static inline int snapshot_is_enforce_auth(void) { return 0; }
static inline int snapshot_key_init(void) { return 0; }
static inline void snapshot_key_trampoline_backup(struct trampoline *t) {}
static inline void snapshot_key_trampoline_restore(struct trampoline *t) {}
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c
index 80ed8e7c5ed8..03a76c474ab7 100644
--- a/kernel/power/snapshot.c
+++ b/kernel/power/snapshot.c
@@ -1538,6 +1538,23 @@ static int decrypt_data_page(void *encrypted_page)
return ret;
}

+/* enforce the snapshot to be signed */
+#ifdef CONFIG_HIBERNATION_ENC_AUTH_FORCE
+static bool enforce_auth = true;
+#else
+static bool enforce_auth;
+#endif
+
+void snapshot_set_enforce_auth(void)
+{
+ enforce_auth = true;
+}
+
+int snapshot_is_enforce_auth(void)
+{
+ return enforce_auth;
+}
+
/*
* Signature of snapshot image
*/
@@ -1604,6 +1621,8 @@ int snapshot_prepare_hash(bool may_sleep)
crypto_free_shash(tfm);
s4_verify_digest = NULL;
s4_verify_desc = NULL;
+ if (!enforce_auth)
+ ret = 0;
return ret;
}

@@ -1665,6 +1684,8 @@ int snapshot_image_verify_decrypt(void)
pr_warn("Signature verification failed: %d\n", ret);
error:
sig_verify_ret = ret;
+ if (!enforce_auth)
+ ret = 0;
return ret;
}

@@ -1752,6 +1773,7 @@ static void load_signature(struct swsusp_info *info)

static void init_sig_verify(struct trampoline *t)
{
+ t->enforce_auth = enforce_auth;
t->sig_verify_ret = sig_verify_ret;
t->snapshot_key_valid = snapshot_key_valid;
sig_verify_ret = 0;
@@ -1760,11 +1782,25 @@ static void init_sig_verify(struct trampoline *t)

static void handle_sig_verify(struct trampoline *t)
{
- if (t->sig_verify_ret)
+ enforce_auth = t->enforce_auth;
+ if (enforce_auth)
+ pr_info("Enforce the snapshot to be validly signed\n");
+
+ if (t->sig_verify_ret) {
pr_warn("Signature verification failed: %d\n",
t->sig_verify_ret);
- else if (t->snapshot_key_valid)
+ if (t->snapshot_key_valid)
+ pr_warn("Did not find valid snapshot key.\n");
+ /* taint kernel */
+ if (!enforce_auth) {
+ pr_warn("System resumed from unsafe snapshot - "
+ "tainting kernel\n");
+ add_taint(TAINT_UNSAFE_HIBERNATE, LOCKDEP_STILL_OK);
+ pr_info("%s\n", print_tainted());
+ }
+ } else if (t->snapshot_key_valid) {
pr_info("Signature verification passed.\n");
+ }
}
#else
static int
diff --git a/kernel/power/user.c b/kernel/power/user.c
index d5c8f777e8d8..9597f48f01d0 100644
--- a/kernel/power/user.c
+++ b/kernel/power/user.c
@@ -261,7 +261,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
break;
}
error = snapshot_key_init();
- if (error)
+ if (error && snapshot_is_enforce_auth())
return error;
error = snapshot_create_trampoline();
if (error)
--
2.13.6


2019-01-03 21:30:03

by Chun-Yi Lee

[permalink] [raw]
Subject: [PATCH 2/5] PM / hibernate: Generate and verify signature for snapshot image

When producing memory snapshot image, hibernation uses HMAC-SHA512
with snapshot key (from TPM trusted key) to calculate the hash of
all data pages in snapshot image. The hash result will be kept in the
snapshot header as the image signature. Before hibernation restores
image, kernel executes HMAC-SHA512 again and compares the result with
the signature in the header to verify the integrity of snapshot image.

If the verification failed, the resume process will be stopped. Then
the snapshot image will be discarded and system will boot as normal.

On the other hand, a trampoline page be created in snapshot image
when hibernation. This trampoline page be used to forward the state of
snapshot key and the result of snapshot image verification from boot
kernel to image kernel when resuming. The trampoline page will also be
used to forward the snapshot key in the later patch.

Cc: "Rafael J. Wysocki" <[email protected]>
Cc: Pavel Machek <[email protected]>
Cc: Chen Yu <[email protected]>
Cc: Oliver Neukum <[email protected]>
Cc: Ryan Chen <[email protected]>
Cc: David Howells <[email protected]>
Cc: Giovanni Gherdovich <[email protected]>
Cc: Randy Dunlap <[email protected]>
Cc: Jann Horn <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Signed-off-by: "Lee, Chun-Yi" <[email protected]>
---
kernel/power/hibernate.c | 18 ++-
kernel/power/power.h | 26 ++++
kernel/power/snapshot.c | 387 +++++++++++++++++++++++++++++++++++++++++++++--
kernel/power/swap.c | 6 +
kernel/power/user.c | 12 ++
5 files changed, 432 insertions(+), 17 deletions(-)

diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
index ecc31e8e40d0..0dda6a9f0af1 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -271,10 +271,14 @@ static int create_image(int platform_mode)
{
int error;

+ error = snapshot_prepare_hash(false);
+ if (error)
+ return error;
+
error = dpm_suspend_end(PMSG_FREEZE);
if (error) {
pr_err("Some devices failed to power down, aborting hibernation\n");
- return error;
+ goto finish_hash;
}

error = platform_pre_snapshot(platform_mode);
@@ -331,6 +335,9 @@ static int create_image(int platform_mode)
dpm_resume_start(in_suspend ?
(error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);

+ finish_hash:
+ snapshot_finish_hash();
+
return error;
}

@@ -694,6 +701,14 @@ int hibernate(void)
return -EPERM;
}

+ error = snapshot_key_init();
+ if (error)
+ return error;
+
+ error = snapshot_create_trampoline();
+ if (error)
+ return error;
+
lock_system_sleep();
/* The snapshot device should not be opened while we're running */
if (!atomic_add_unless(&snapshot_device_available, -1, 0)) {
@@ -750,6 +765,7 @@ int hibernate(void)
pm_restore_gfp_mask();
} else {
pm_pr_dbg("Image restored successfully.\n");
+ snapshot_restore_trampoline();
}

Free_bitmaps:
diff --git a/kernel/power/power.h b/kernel/power/power.h
index fe2dfa0d4d36..c614b0a294e3 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -11,6 +11,10 @@
#define SNAPSHOT_KEY_SIZE SHA512_DIGEST_SIZE
#define DERIVED_KEY_SIZE SHA512_DIGEST_SIZE

+/* HMAC algorithm for hibernate snapshot signature */
+#define SNAPSHOT_HMAC "hmac(sha512)"
+#define SNAPSHOT_DIGEST_SIZE SHA512_DIGEST_SIZE
+
struct swsusp_info {
struct new_utsname uts;
u32 version_code;
@@ -19,6 +23,17 @@ struct swsusp_info {
unsigned long image_pages;
unsigned long pages;
unsigned long size;
+ unsigned long trampoline_pfn;
+ u8 signature[SNAPSHOT_DIGEST_SIZE];
+} __aligned(PAGE_SIZE);
+
+/*
+ * The trampoline page is used to forward information
+ * from boot kernel to image kernel in restore stage.
+ */
+struct trampoline {
+ bool snapshot_key_valid;
+ int sig_verify_ret;
} __aligned(PAGE_SIZE);

#ifdef CONFIG_HIBERNATION
@@ -27,12 +42,19 @@ extern void __init hibernate_reserved_size_init(void);
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_hash(bool may_sleep);
+extern void snapshot_finish_hash(void);
/* 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_image_verify_decrypt(void) { return 0; }
+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; }
#endif /* !CONFIG_HIBERNATION_ENC_AUTH */

@@ -171,6 +193,10 @@ extern int snapshot_read_next(struct snapshot_handle *handle);
extern int snapshot_write_next(struct snapshot_handle *handle);
extern void snapshot_write_finalize(struct snapshot_handle *handle);
extern int snapshot_image_loaded(struct snapshot_handle *handle);
+extern int snapshot_create_trampoline(void);
+extern void snapshot_init_trampoline(void);
+extern void snapshot_restore_trampoline(void);
+extern void snapshot_free_trampoline(void);

/* If unset, the snapshot device cannot be open. */
extern atomic_t snapshot_device_available;
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c
index 640b2034edd6..e817c035f378 100644
--- a/kernel/power/snapshot.c
+++ b/kernel/power/snapshot.c
@@ -33,12 +33,16 @@
#include <linux/compiler.h>
#include <linux/ktime.h>
#include <linux/set_memory.h>
+#include <linux/vmalloc.h>

#include <linux/uaccess.h>
#include <asm/mmu_context.h>
#include <asm/pgtable.h>
#include <asm/tlbflush.h>
#include <asm/io.h>
+#ifdef CONFIG_HIBERNATION_ENC_AUTH
+#include <crypto/hash.h>
+#endif

#include "power.h"

@@ -79,6 +83,15 @@ static inline void hibernate_restore_protect_page(void *page_address) {}
static inline void hibernate_restore_unprotect_page(void *page_address) {}
#endif /* CONFIG_STRICT_KERNEL_RWX && CONFIG_ARCH_HAS_SET_MEMORY */

+/* the trampoline is used by image kernel */
+static void *trampoline_virt;
+
+/* trampoline pfn from swsusp_info in snapshot for snapshot_write_next() */
+static unsigned long trampoline_pfn;
+
+/* Keep the buffer for foward page in snapshot_write_next() */
+static void *trampoline_buff;
+
static int swsusp_page_is_free(struct page *);
static void swsusp_set_page_forbidden(struct page *);
static void swsusp_unset_page_forbidden(struct page *);
@@ -1393,8 +1406,246 @@ static inline void copy_data_page(unsigned long dst_pfn, unsigned long src_pfn)
}
#endif /* CONFIG_HIGHMEM */

-static void copy_data_pages(struct memory_bitmap *copy_bm,
- struct memory_bitmap *orig_bm)
+/* Total number of image pages */
+static unsigned int nr_copy_pages;
+
+/* Point array for collecting buffers' address in snapshot_write_next() */
+static void **h_buf;
+
+#ifdef CONFIG_HIBERNATION_ENC_AUTH
+/*
+ * Signature of snapshot image
+ */
+static u8 signature[SNAPSHOT_DIGEST_SIZE];
+
+/* Keep the signature verification result for trampoline */
+static int sig_verify_ret;
+
+/* keep the snapshot key status for trampoline */
+static bool snapshot_key_valid;
+
+static u8 *s4_verify_digest;
+static struct shash_desc *s4_verify_desc;
+
+int snapshot_prepare_hash(bool may_sleep)
+{
+ char auth_key[DERIVED_KEY_SIZE];
+ struct crypto_shash *tfm;
+ size_t digest_size, desc_size;
+ int ret;
+
+ ret = snapshot_get_auth_key(auth_key, may_sleep);
+ if (ret) {
+ pr_warn_once("auth key is invalid: %d\n", ret);
+ return -EINVAL;
+ }
+ snapshot_key_valid = true;
+
+ tfm = crypto_alloc_shash(SNAPSHOT_HMAC, 0, 0);
+ if (IS_ERR(tfm)) {
+ pr_err("Allocate HMAC failed: %ld\n", PTR_ERR(tfm));
+ return PTR_ERR(tfm);
+ }
+
+ ret = crypto_shash_setkey(tfm, auth_key, DERIVED_KEY_SIZE);
+ if (ret) {
+ pr_err("Set HMAC key failed\n");
+ goto error;
+ }
+
+ desc_size = crypto_shash_descsize(tfm) + sizeof(*s4_verify_desc);
+ digest_size = crypto_shash_digestsize(tfm);
+ s4_verify_digest = kzalloc(digest_size + desc_size,
+ may_sleep ? GFP_KERNEL : GFP_ATOMIC);
+ if (!s4_verify_digest) {
+ pr_err("Allocate digest failed\n");
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ s4_verify_desc = (void *) s4_verify_digest + digest_size;
+ s4_verify_desc->tfm = tfm;
+ if (may_sleep)
+ s4_verify_desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+ ret = crypto_shash_init(s4_verify_desc);
+ if (ret < 0)
+ goto free_shash;
+
+ return 0;
+
+ free_shash:
+ kfree(s4_verify_digest);
+ error:
+ crypto_free_shash(tfm);
+ s4_verify_digest = NULL;
+ s4_verify_desc = NULL;
+ return ret;
+}
+
+void snapshot_finish_hash(void)
+{
+ if (s4_verify_desc)
+ crypto_free_shash(s4_verify_desc->tfm);
+ kfree(s4_verify_digest);
+ s4_verify_desc = NULL;
+ s4_verify_digest = NULL;
+}
+
+int snapshot_image_verify_decrypt(void)
+{
+ int ret, i;
+
+ if (!h_buf) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ ret = snapshot_key_init();
+ if (ret)
+ goto error_prep;
+
+ ret = snapshot_prepare_hash(true);
+ if (ret || !s4_verify_desc)
+ 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;
+ }
+
+ ret = crypto_shash_final(s4_verify_desc, s4_verify_digest);
+ if (ret)
+ goto error_shash;
+
+ 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:
+ snapshot_finish_hash();
+
+ error_prep:
+ vfree(h_buf);
+ if (ret)
+ pr_warn("Signature verification failed: %d\n", ret);
+ error:
+ sig_verify_ret = ret;
+ return ret;
+}
+
+static int
+__copy_data_pages(struct memory_bitmap *copy_bm, struct memory_bitmap *orig_bm)
+{
+ unsigned long pfn, dst_pfn;
+ struct page *d_page;
+ void *crypto_buffer = NULL;
+ int ret = 0;
+
+ memory_bm_position_reset(orig_bm);
+ memory_bm_position_reset(copy_bm);
+ for (;;) {
+ pfn = memory_bm_next_pfn(orig_bm);
+ if (unlikely(pfn == BM_END_OF_MAP))
+ break;
+ dst_pfn = memory_bm_next_pfn(copy_bm);
+ copy_data_page(dst_pfn, pfn);
+
+ /* Setup buffer */
+ d_page = pfn_to_page(dst_pfn);
+ if (PageHighMem(d_page)) {
+ void *kaddr = kmap_atomic(d_page);
+
+ copy_page(buffer, kaddr);
+ kunmap_atomic(kaddr);
+ crypto_buffer = buffer;
+ } else {
+ crypto_buffer = page_address(d_page);
+ }
+
+ /* Generate digest */
+ if (!s4_verify_desc)
+ continue;
+ ret = crypto_shash_update(s4_verify_desc, crypto_buffer,
+ PAGE_SIZE);
+ if (ret)
+ return ret;
+ }
+
+ if (s4_verify_desc) {
+ ret = crypto_shash_final(s4_verify_desc, s4_verify_digest);
+ if (ret)
+ return ret;
+
+ memset(signature, 0, SNAPSHOT_DIGEST_SIZE);
+ memcpy(signature, s4_verify_digest, SNAPSHOT_DIGEST_SIZE);
+ }
+
+ return 0;
+}
+
+static void alloc_h_buf(void)
+{
+ h_buf = vmalloc(sizeof(void *) * nr_copy_pages);
+ if (!h_buf)
+ pr_err("Allocate buffer point array failed\n");
+}
+
+static void init_signature(struct swsusp_info *info)
+{
+ memcpy(info->signature, signature, SNAPSHOT_DIGEST_SIZE);
+}
+
+static void load_signature(struct swsusp_info *info)
+{
+ memset(signature, 0, SNAPSHOT_DIGEST_SIZE);
+ memcpy(signature, info->signature, SNAPSHOT_DIGEST_SIZE);
+}
+
+static void init_sig_verify(struct trampoline *t)
+{
+ t->sig_verify_ret = sig_verify_ret;
+ t->snapshot_key_valid = snapshot_key_valid;
+ sig_verify_ret = 0;
+ snapshot_key_valid = 0;
+}
+
+static void handle_sig_verify(struct trampoline *t)
+{
+ if (t->sig_verify_ret)
+ pr_warn("Signature verification failed: %d\n",
+ t->sig_verify_ret);
+ else if (t->snapshot_key_valid)
+ pr_info("Signature verification passed.\n");
+}
+#else
+static int
+__copy_data_pages(struct memory_bitmap *copy_bm, struct memory_bitmap *orig_bm)
+{
+ unsigned long pfn;
+
+ memory_bm_position_reset(orig_bm);
+ memory_bm_position_reset(copy_bm);
+ for (;;) {
+ pfn = memory_bm_next_pfn(orig_bm);
+ if (unlikely(pfn == BM_END_OF_MAP))
+ break;
+ copy_data_page(memory_bm_next_pfn(copy_bm), pfn);
+ }
+
+ return 0;
+}
+
+static inline void alloc_h_buf(void) {}
+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) {}
+static inline void handle_sig_verify(struct trampoline *t) {}
+#endif /* !CONFIG_HIBERNATION_ENC_AUTH */
+
+static int copy_data_pages(struct memory_bitmap *copy_bm,
+ struct memory_bitmap *orig_bm)
{
struct zone *zone;
unsigned long pfn;
@@ -1408,18 +1659,9 @@ static void copy_data_pages(struct memory_bitmap *copy_bm,
if (page_is_saveable(zone, pfn))
memory_bm_set_bit(orig_bm, pfn);
}
- memory_bm_position_reset(orig_bm);
- memory_bm_position_reset(copy_bm);
- for(;;) {
- pfn = memory_bm_next_pfn(orig_bm);
- if (unlikely(pfn == BM_END_OF_MAP))
- break;
- copy_data_page(memory_bm_next_pfn(copy_bm), pfn);
- }
+ return __copy_data_pages(copy_bm, orig_bm);
}

-/* Total number of image pages */
-static unsigned int nr_copy_pages;
/* Number of pages needed for saving the original pfns of the image pages */
static unsigned int nr_meta_pages;
/*
@@ -1961,6 +2203,7 @@ static int swsusp_alloc(struct memory_bitmap *copy_bm,
asmlinkage __visible int swsusp_save(void)
{
unsigned int nr_pages, nr_highmem;
+ int ret;

pr_info("Creating hibernation image:\n");

@@ -1984,7 +2227,11 @@ asmlinkage __visible int swsusp_save(void)
* Kill them.
*/
drain_local_pages(NULL);
- copy_data_pages(&copy_bm, &orig_bm);
+ ret = copy_data_pages(&copy_bm, &orig_bm);
+ if (ret) {
+ pr_err("Copy data pages failed\n");
+ return ret;
+ }

/*
* End of critical section. From now on, we can write to memory,
@@ -2038,10 +2285,98 @@ static int init_header(struct swsusp_info *info)
info->pages = snapshot_get_image_size();
info->size = info->pages;
info->size <<= PAGE_SHIFT;
+ info->trampoline_pfn = page_to_pfn(virt_to_page(trampoline_virt));
+ init_signature(info);
return init_header_complete(info);
}

/**
+ * create trampoline - Create a trampoline page before snapshot be created
+ * In hibernation process, this routine will be called by kernel before
+ * the snapshot image be created. It can be used in resuming process.
+ */
+int snapshot_create_trampoline(void)
+{
+ if (trampoline_virt) {
+ pr_warn("Tried to create trampoline again\n");
+ return 0;
+ }
+
+ trampoline_virt = (void *)get_zeroed_page(GFP_KERNEL);
+ if (!trampoline_virt) {
+ pr_err("Allocate trampoline page failed\n");
+ return -ENOMEM;
+ }
+ trampoline_pfn = 0;
+ trampoline_buff = NULL;
+
+ return 0;
+}
+
+/**
+ * initial trampoline - Put data to trampoline buffer for target kernel
+ *
+ * In resuming process, this routine will be called by boot kernel before
+ * the target kernel be restored. The boot kernel uses trampoline buffer
+ * to transfer information to target kernel.
+ */
+void snapshot_init_trampoline(void)
+{
+ struct trampoline *t;
+
+ if (!trampoline_pfn || !trampoline_buff) {
+ pr_err("Did not find trampoline buffer, pfn: %ld\n",
+ trampoline_pfn);
+ return;
+ }
+
+ hibernate_restore_unprotect_page(trampoline_buff);
+ memset(trampoline_buff, 0, PAGE_SIZE);
+ t = (struct trampoline *)trampoline_buff;
+
+ init_sig_verify(t);
+
+ pr_info("Hibernation trampoline page prepared\n");
+}
+
+/**
+ * restore trampoline - Handle the data from boot kernel and free.
+ *
+ * In resuming process, this routine will be called by target kernel
+ * after target kernel is restored. The target kernel handles
+ * the data in trampoline that it is transferred from boot kernel.
+ */
+void snapshot_restore_trampoline(void)
+{
+ struct trampoline *t;
+
+ if (!trampoline_virt) {
+ pr_err("Doesn't have trampoline page\n");
+ return;
+ }
+
+ t = (struct trampoline *)trampoline_virt;
+
+ handle_sig_verify(t);
+ snapshot_free_trampoline();
+}
+
+void snapshot_free_trampoline(void)
+{
+ if (!trampoline_virt) {
+ pr_err("No trampoline page can be freed\n");
+ return;
+ }
+
+ trampoline_pfn = 0;
+ trampoline_buff = NULL;
+ memset(trampoline_virt, 0, PAGE_SIZE);
+ free_page((unsigned long)trampoline_virt);
+ trampoline_virt = NULL;
+ pr_info("Trampoline freed\n");
+}
+
+/**
* pack_pfns - Prepare PFNs for saving.
* @bm: Memory bitmap.
* @buf: Memory buffer to store the PFNs in.
@@ -2188,6 +2523,8 @@ static int load_header(struct swsusp_info *info)
if (!error) {
nr_copy_pages = info->image_pages;
nr_meta_pages = info->pages - info->image_pages - 1;
+ trampoline_pfn = info->trampoline_pfn;
+ load_signature(info);
}
return error;
}
@@ -2521,7 +2858,8 @@ static int prepare_image(struct memory_bitmap *new_bm, struct memory_bitmap *bm)
* Get the address that snapshot_write_next() should return to its caller to
* write to.
*/
-static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca)
+static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca,
+ unsigned long *pfn_out)
{
struct pbe *pbe;
struct page *page;
@@ -2530,6 +2868,9 @@ static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca)
if (pfn == BM_END_OF_MAP)
return ERR_PTR(-EFAULT);

+ if (pfn_out)
+ *pfn_out = pfn;
+
page = pfn_to_page(pfn);
if (PageHighMem(page))
return get_highmem_page_buffer(page, ca);
@@ -2577,6 +2918,7 @@ static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca)
int snapshot_write_next(struct snapshot_handle *handle)
{
static struct chain_allocator ca;
+ unsigned long pfn;
int error = 0;

/* Check if we have already loaded the entire image */
@@ -2601,6 +2943,12 @@ int snapshot_write_next(struct snapshot_handle *handle)

safe_pages_list = NULL;

+ /* Allocate buffer point array for generating
+ * digest to compare with signature.
+ * h_buf will freed in snapshot_image_verify_decrypt().
+ */
+ alloc_h_buf();
+
error = memory_bm_create(&copy_bm, GFP_ATOMIC, PG_ANY);
if (error)
return error;
@@ -2624,21 +2972,28 @@ int snapshot_write_next(struct snapshot_handle *handle)
chain_init(&ca, GFP_ATOMIC, PG_SAFE);
memory_bm_position_reset(&orig_bm);
restore_pblist = NULL;
- handle->buffer = get_buffer(&orig_bm, &ca);
+ handle->buffer = get_buffer(&orig_bm, &ca, &pfn);
handle->sync_read = 0;
if (IS_ERR(handle->buffer))
return PTR_ERR(handle->buffer);
+ if (h_buf)
+ *h_buf = handle->buffer;
}
} else {
copy_last_highmem_page();
/* Restore page key for data page (s390 only). */
page_key_write(handle->buffer);
hibernate_restore_protect_page(handle->buffer);
- handle->buffer = get_buffer(&orig_bm, &ca);
+ handle->buffer = get_buffer(&orig_bm, &ca, &pfn);
if (IS_ERR(handle->buffer))
return PTR_ERR(handle->buffer);
if (handle->buffer != buffer)
handle->sync_read = 0;
+ /* Capture the trampoline for transfer data */
+ if (pfn == trampoline_pfn && trampoline_pfn)
+ trampoline_buff = handle->buffer;
+ if (h_buf)
+ *(h_buf + (handle->cur - nr_meta_pages - 1)) = handle->buffer;
}
handle->cur++;
return PAGE_SIZE;
diff --git a/kernel/power/swap.c b/kernel/power/swap.c
index d7f6c1a288d3..2e669f589830 100644
--- a/kernel/power/swap.c
+++ b/kernel/power/swap.c
@@ -1095,6 +1095,9 @@ static int load_image(struct swap_map_handle *handle,
snapshot_write_finalize(snapshot);
if (!snapshot_image_loaded(snapshot))
ret = -ENODATA;
+ if (!ret)
+ ret = snapshot_image_verify_decrypt();
+ snapshot_init_trampoline();
}
swsusp_show_speed(start, stop, nr_to_read, "Read");
return ret;
@@ -1447,6 +1450,9 @@ static int load_image_lzo(struct swap_map_handle *handle,
}
}
}
+ if (!ret)
+ ret = snapshot_image_verify_decrypt();
+ snapshot_init_trampoline();
}
swsusp_show_speed(start, stop, nr_to_read, "Read");
out_clean:
diff --git a/kernel/power/user.c b/kernel/power/user.c
index 2d8b60a3c86b..d5c8f777e8d8 100644
--- a/kernel/power/user.c
+++ b/kernel/power/user.c
@@ -248,6 +248,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
if (!data->frozen || data->ready)
break;
pm_restore_gfp_mask();
+ snapshot_restore_trampoline();
free_basic_memory_bitmaps();
data->free_bitmaps = false;
thaw_processes();
@@ -259,6 +260,12 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
error = -EPERM;
break;
}
+ error = snapshot_key_init();
+ if (error)
+ return error;
+ error = snapshot_create_trampoline();
+ if (error)
+ return error;
pm_restore_gfp_mask();
error = hibernation_snapshot(data->platform_support);
if (!error) {
@@ -275,6 +282,11 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
error = -EPERM;
break;
}
+ if (snapshot_image_verify_decrypt()) {
+ error = -EPERM;
+ break;
+ }
+ snapshot_init_trampoline();
error = hibernation_restore(data->platform_support);
break;

--
2.13.6


2019-01-03 21:30:22

by Chun-Yi Lee

[permalink] [raw]
Subject: [PATCH 3/5] PM / hibernate: Encrypt snapshot image

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" <[email protected]>
Cc: Pavel Machek <[email protected]>
Cc: Chen Yu <[email protected]>
Cc: Oliver Neukum <[email protected]>
Cc: Ryan Chen <[email protected]>
Cc: David Howells <[email protected]>
Cc: Giovanni Gherdovich <[email protected]>
Cc: Randy Dunlap <[email protected]>
Cc: Jann Horn <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Signed-off-by: "Lee, Chun-Yi" <[email protected]>
---
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];
+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);
+ 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);
+
+ 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));
+ 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)
+{
+ 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;
--
2.13.6


2019-01-03 21:30:22

by Chun-Yi Lee

[permalink] [raw]
Subject: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

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.

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.

v2:
- Fixed bug of trusted_key_init's return value.
- Fixed wording in Kconfig
- Removed VLA usage
- Removed the checking of capability for writing disk_kmk.
- Fixed Kconfig, select trusted key.
- Add memory barrier before setting key initialized flag.

Cc: "Rafael J. Wysocki" <[email protected]>
Cc: Pavel Machek <[email protected]>
Cc: Chen Yu <[email protected]>
Cc: Oliver Neukum <[email protected]>
Cc: Ryan Chen <[email protected]>
Cc: David Howells <[email protected]>
Cc: Giovanni Gherdovich <[email protected]>
Cc: Randy Dunlap <[email protected]>
Cc: Jann Horn <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Signed-off-by: "Lee, Chun-Yi" <[email protected]>
---
kernel/power/Kconfig | 14 +++
kernel/power/Makefile | 1 +
kernel/power/hibernate.c | 33 ++++++
kernel/power/power.h | 16 +++
kernel/power/snapshot_key.c | 245 ++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 309 insertions(+)
create mode 100644 kernel/power/snapshot_key.c

diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index f8fe57d1022e..506a3c5a7a0d 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -76,6 +76,20 @@ config HIBERNATION

For more information take a look at <file:Documentation/power/swsusp.txt>.

+config HIBERNATION_ENC_AUTH
+ bool "Hibernation encryption and authentication"
+ depends on HIBERNATION
+ select 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 arbitrarily
+ modified. A user can use the TPM's 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 e7e47d9be1e5..d949adbaf580 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..ecc31e8e40d0 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -1034,6 +1034,36 @@ 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)
+{
+ int error = 0;
+ char *p;
+ int len;
+
+ p = memchr(buf, '\n', n);
+ len = p ? p - buf : n;
+ if (strncmp(buf, "1", len))
+ return -EINVAL;
+
+ 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 +1168,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 <linux/utsname.h>
#include <linux/freezer.h>
#include <linux/compiler.h>
+#include <crypto/sha.h>
+
+/* 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..3a569b505d8d
--- /dev/null
+++ b/kernel/power/snapshot_key.c
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* snapshot keys handler
+ *
+ * Copyright (C) 2018 Lee, Chun-Yi <[email protected]>
+ *
+ * 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 <linux/cred.h>
+#include <linux/key-type.h>
+#include <linux/slab.h>
+#include <crypto/hash.h>
+#include <crypto/sha.h>
+#include <keys/trusted-type.h>
+#include <keys/user-type.h>
+
+#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)
+{
+ struct shash_desc *desc;
+ int err;
+
+ desc = kzalloc(sizeof(struct shash_desc) +
+ crypto_shash_descsize(hash_tfm),
+ may_sleep ? GFP_KERNEL : GFP_ATOMIC);
+ if (!desc)
+ return -ENOMEM;
+
+ desc->tfm = hash_tfm;
+ desc->flags = may_sleep ? CRYPTO_TFM_REQ_MAY_SLEEP : 0;
+ err = crypto_shash_digest(desc, buf, buflen, digest);
+ shash_desc_zero(desc);
+ kzfree(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 = 0;
+
+ 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;
+
+ barrier();
+ 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


2019-01-06 08:12:18

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 2/5] PM / hibernate: Generate and verify signature for snapshot image

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

Hi Chun,

> +int snapshot_image_verify_decrypt(void)
> +{
> + int ret, i;
> +
> + if (!h_buf) {
> + ret = -ENOMEM;
> + goto error;
> + }
> +
> + ret = snapshot_key_init();
> + if (ret)
> + goto error_prep;
> +
> + ret = snapshot_prepare_hash(true);
> + if (ret || !s4_verify_desc)
> + 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;
> + }
> +
> + ret = crypto_shash_final(s4_verify_desc, s4_verify_digest);
> + if (ret)
> + goto error_shash;
> +
> + 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:
> + snapshot_finish_hash();
> +
> + error_prep:
> + vfree(h_buf);
> + if (ret)
> + pr_warn("Signature verification failed: %d\n", ret);
> + error:
> + sig_verify_ret = ret;
> + return ret;
> +}

May I ask why the authentication part is done manually here? Why not using an
AEAD cipher like the authenc() ciphers, or CCM (I would not recommend GCM
though)? In this case, the encryption/decryption operation would automatically
perform the creation of the hash and the verification of the hash. I.e.
decryption can return EBADMSG which indicates an authentication failure.

> +
> +static int
> +__copy_data_pages(struct memory_bitmap *copy_bm, struct memory_bitmap
> *orig_bm) +{
> + unsigned long pfn, dst_pfn;
> + struct page *d_page;
> + void *crypto_buffer = NULL;
> + int ret = 0;
> +
> + memory_bm_position_reset(orig_bm);
> + memory_bm_position_reset(copy_bm);
> + for (;;) {
> + pfn = memory_bm_next_pfn(orig_bm);
> + if (unlikely(pfn == BM_END_OF_MAP))
> + break;
> + dst_pfn = memory_bm_next_pfn(copy_bm);
> + copy_data_page(dst_pfn, pfn);
> +
> + /* Setup buffer */
> + d_page = pfn_to_page(dst_pfn);
> + if (PageHighMem(d_page)) {
> + void *kaddr = kmap_atomic(d_page);
> +
> + copy_page(buffer, kaddr);
> + kunmap_atomic(kaddr);
> + crypto_buffer = buffer;
> + } else {
> + crypto_buffer = page_address(d_page);
> + }
> +
> + /* Generate digest */
> + if (!s4_verify_desc)
> + continue;
> + ret = crypto_shash_update(s4_verify_desc, crypto_buffer,
> + PAGE_SIZE);

Same here, the creation of the hash would be implicit during the encryption.

Ciao
Stephan



2019-01-06 08:15:56

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

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

Hi Chun,

> 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.
>
> 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.
>
> v2:
> - Fixed bug of trusted_key_init's return value.
> - Fixed wording in Kconfig
> - Removed VLA usage
> - Removed the checking of capability for writing disk_kmk.
> - Fixed Kconfig, select trusted key.
> - Add memory barrier before setting key initialized flag.
>
> Cc: "Rafael J. Wysocki" <[email protected]>
> Cc: Pavel Machek <[email protected]>
> Cc: Chen Yu <[email protected]>
> Cc: Oliver Neukum <[email protected]>
> Cc: Ryan Chen <[email protected]>
> Cc: David Howells <[email protected]>
> Cc: Giovanni Gherdovich <[email protected]>
> Cc: Randy Dunlap <[email protected]>
> Cc: Jann Horn <[email protected]>
> Cc: Andy Lutomirski <[email protected]>
> Signed-off-by: "Lee, Chun-Yi" <[email protected]>
> ---
> kernel/power/Kconfig | 14 +++
> kernel/power/Makefile | 1 +
> kernel/power/hibernate.c | 33 ++++++
> kernel/power/power.h | 16 +++
> kernel/power/snapshot_key.c | 245
> ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 309
> insertions(+)
> create mode 100644 kernel/power/snapshot_key.c
>
> diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
> index f8fe57d1022e..506a3c5a7a0d 100644
> --- a/kernel/power/Kconfig
> +++ b/kernel/power/Kconfig
> @@ -76,6 +76,20 @@ config HIBERNATION
>
> For more information take a look at
> <file:Documentation/power/swsusp.txt>.
>
> +config HIBERNATION_ENC_AUTH
> + bool "Hibernation encryption and authentication"
> + depends on HIBERNATION
> + select 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 arbitrarily
> + modified. A user can use the TPM's 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 e7e47d9be1e5..d949adbaf580 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..ecc31e8e40d0 100644
> --- a/kernel/power/hibernate.c
> +++ b/kernel/power/hibernate.c
> @@ -1034,6 +1034,36 @@ 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)
> +{
> + int error = 0;
> + char *p;
> + int len;
> +
> + p = memchr(buf, '\n', n);
> + len = p ? p - buf : n;
> + if (strncmp(buf, "1", len))
> + return -EINVAL;
> +
> + 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 +1168,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 <linux/utsname.h>
> #include <linux/freezer.h>
> #include <linux/compiler.h>
> +#include <crypto/sha.h>
> +
> +/* 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..3a569b505d8d
> --- /dev/null
> +++ b/kernel/power/snapshot_key.c
> @@ -0,0 +1,245 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/* snapshot keys handler
> + *
> + * Copyright (C) 2018 Lee, Chun-Yi <[email protected]>
> + *
> + * 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 <linux/cred.h>
> +#include <linux/key-type.h>
> +#include <linux/slab.h>
> +#include <crypto/hash.h>
> +#include <crypto/sha.h>
> +#include <keys/trusted-type.h>
> +#include <keys/user-type.h>
> +
> +#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)
> +{
> + struct shash_desc *desc;
> + int err;
> +
> + desc = kzalloc(sizeof(struct shash_desc) +
> + crypto_shash_descsize(hash_tfm),
> + may_sleep ? GFP_KERNEL : GFP_ATOMIC);

Why not using SHASH_DESC_ON_STACK?

> + if (!desc)
> + return -ENOMEM;
> +
> + desc->tfm = hash_tfm;
> + desc->flags = may_sleep ? CRYPTO_TFM_REQ_MAY_SLEEP : 0;
> + err = crypto_shash_digest(desc, buf, buflen, digest);
> + shash_desc_zero(desc);
> + kzfree(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;

strlen on binary data? I guess that will not work. May I suggest to hand down
the length of salt to this function?

> + 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;
> +}

This function looks very much like a key derivation. May I strongly propose to
use an official KDF type like SP800-108 or HKDF?

You find the counter-KDF according to SP800-108 in security/keys/dh.c (search
for functions *kdf*).

Or we may start pulling in KDF support into the kernel crypto API via the
patches along the line of [1].

[1] http://www.chronox.de/kdf.html

> +
> +/* 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 = 0;
> +
> + 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)

This function and trusted_key_init look very similar - could they be collapsed
into one function?

> +{
> + 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);

Where would skey.key be destroyed again?

> + /* 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;
> +
> + barrier();
> + skey.initialized = true;
> +
> + pr_info("Snapshot key is initialled.\n");
> +
> + return 0;
> +
> +key_fail:
> + crypto_free_shash(hash_tfm);
> + hash_tfm = NULL;
> +
> + return err;
> +}



Ciao
Stephan



2019-01-06 08:26:55

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 3/5] PM / hibernate: Encrypt snapshot image

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" <[email protected]>
> Cc: Pavel Machek <[email protected]>
> Cc: Chen Yu <[email protected]>
> Cc: Oliver Neukum <[email protected]>
> Cc: Ryan Chen <[email protected]>
> Cc: David Howells <[email protected]>
> Cc: Giovanni Gherdovich <[email protected]>
> Cc: Randy Dunlap <[email protected]>
> Cc: Jann Horn <[email protected]>
> Cc: Andy Lutomirski <[email protected]>
> Signed-off-by: "Lee, Chun-Yi" <[email protected]>
> ---
> 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



2019-01-06 08:40:08

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

Am Sonntag, 6. Januar 2019, 09:01:27 CET schrieb Stephan Mueller:

Hi,

> > + memcpy(skey.key, ukp->data, ukp->datalen);
>
> Where would skey.key be destroyed again?

Now I see it - it is in patch 4. Please disregard my comment.

Ciao
Stephan



2019-01-06 18:12:51

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH 0/5 v2][RFC] Encryption and authentication for hibernate snapshot image

Hi!

> This patchset is the implementation of encryption and authentication
> for hibernate snapshot image. The image will be encrypted by AES and
> authenticated by HMAC.

Ok, so you encrypt.

> The hibernate function can be used to snapshot memory pages to an image,
> then kernel restores the image to memory space in a appropriate time.
> There have secrets in snapshot image and cracker may modifies it for
> hacking system. Encryption and authentication of snapshot image can protect
> the system.
>
> Hibernate function requests the master key through key retention service.
> The snapshot master key can be a trusted key or a user defined key. The
> name of snapshot master key is fixed to "swsusp-kmk". User should loads
> swsusp-kmk to kernel by keyctl tool before the hibernation resume.
> e.g. The swsusp-kmk must be loaded before systemd-hibernate-resume

But if userspace has a key, encryption is useless against root.

> The TPM trusted key type is preferred to be the master key. But user
> defined key can also be used for testing or when the platform doesn't
> have TPM. User must be aware that the security of user key relies on
> user space. If the root account be compromised, then the user key will
> easy to be grabbed.

In the TPM case, does userland have access to the key?

Please explain your security goals.

Pavel


> Lee, Chun-Yi (5):
> PM / hibernate: Create snapshot keys handler
> PM / hibernate: Generate and verify signature for snapshot image
> PM / hibernate: Encrypt snapshot image
> PM / hibernate: Erase the snapshot master key in snapshot pages
> PM / hibernate: An option to request that snapshot image must be
> authenticated
>
> Documentation/admin-guide/kernel-parameters.txt | 6 +
> include/linux/kernel.h | 3 +-
> kernel/panic.c | 1 +
> kernel/power/Kconfig | 25 +
> kernel/power/Makefile | 1 +
> kernel/power/hibernate.c | 59 ++-
> kernel/power/power.h | 59 +++
> kernel/power/snapshot.c | 576 +++++++++++++++++++++++-
> kernel/power/snapshot_key.c | 312 +++++++++++++
> kernel/power/swap.c | 6 +
> kernel/power/user.c | 12 +
> 11 files changed, 1042 insertions(+), 18 deletions(-)
> create mode 100644 kernel/power/snapshot_key.c
>

--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html


Attachments:
(No filename) (2.63 kB)
signature.asc (188.00 B)
Digital signature
Download all attachments

2019-01-07 16:38:45

by joeyli

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

Hi Stephan,

First, thanks for your review!

On Sun, Jan 06, 2019 at 09:01:27AM +0100, Stephan Mueller wrote:
> Am Donnerstag, 3. Januar 2019, 15:32:23 CET schrieb Lee, Chun-Yi:
>
> Hi Chun,
>
> > 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.
> >
[...snip]
> > +static int calc_hash(u8 *digest, const u8 *buf, unsigned int buflen,
> > + bool may_sleep)
> > +{
> > + struct shash_desc *desc;
> > + int err;
> > +
> > + desc = kzalloc(sizeof(struct shash_desc) +
> > + crypto_shash_descsize(hash_tfm),
> > + may_sleep ? GFP_KERNEL : GFP_ATOMIC);
>
> Why not using SHASH_DESC_ON_STACK?
>

Because security concern and bad runtime performance. Please looking at
c2cd0b08e1e patch for hibernation. And reference:

https://lore.kernel.org/lkml/CA+55aFzCG-zNmZwX4A2FQpadafLfEzK6CC=qPXydAacU1RqZWA@mail.gmail.com/T/#u
https://lwn.net/Articles/749064/

> > + if (!desc)
> > + return -ENOMEM;
> > +
> > + desc->tfm = hash_tfm;
> > + desc->flags = may_sleep ? CRYPTO_TFM_REQ_MAY_SLEEP : 0;
> > + err = crypto_shash_digest(desc, buf, buflen, digest);
> > + shash_desc_zero(desc);
> > + kzfree(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;
>
> strlen on binary data? I guess that will not work. May I suggest to hand down
> the length of salt to this function?
>

hm... The salt is actually a "salt string" that's gave from
snapshot_get_auth_key() or snapshot_get_enc_key(). So I use
strlen() here. I will change the name to salt_string to avoid
confusion.

> > + 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;
> > +}
>
> This function looks very much like a key derivation. May I strongly propose to

Actually key derivation function is modified from the get_derived_key() from
the encrypted.c file in encrypted key.

> use an official KDF type like SP800-108 or HKDF?
>
> You find the counter-KDF according to SP800-108 in security/keys/dh.c (search
> for functions *kdf*).
>
> Or we may start pulling in KDF support into the kernel crypto API via the
> patches along the line of [1].
>
> [1] http://www.chronox.de/kdf.html
>

Thanks for your suggestion. I didn't touch any key derivation standard
before. I will study it.

But I still want to use my original function currently. Because the same
logic is also used in trusted key. I will happy to move to SP800-108 or
HKDF when it's available in kernel.

> > +
> > +/* Derive authentication/encryption key */
> > +static int get_derived_key(u8 *derived_key, const char *derived_type_str,
> > + bool may_sleep)
[...snip]
> > +static int trusted_key_init(void)
> > +{
> > + struct trusted_key_payload *tkp;
> > + struct key *key;
> > + int err = 0;
> > +
> > + 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)
>
> This function and trusted_key_init look very similar - could they be collapsed
> into one function?
>

The data structure is different between trusted key with user key. I will try to
extract the duplicate part but may not collapse into one.

> > +{
> > + 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);
>
> Where would skey.key be destroyed again?
>

Yes, you saw it in later patch.

Thanks a lot!
Joey Lee

2019-01-07 16:41:03

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

Am Montag, 7. Januar 2019, 16:33:27 CET schrieb joeyli:

Hi Herbert,

>
> > use an official KDF type like SP800-108 or HKDF?
> >
> > You find the counter-KDF according to SP800-108 in security/keys/dh.c
> > (search for functions *kdf*).
> >
> > Or we may start pulling in KDF support into the kernel crypto API via the
> > patches along the line of [1].
> >
> > [1] http://www.chronox.de/kdf.html
>
> Thanks for your suggestion. I didn't touch any key derivation standard
> before. I will study it.
>
> But I still want to use my original function currently. Because the same
> logic is also used in trusted key. I will happy to move to SP800-108 or
> HKDF when it's available in kernel.

Would it make sense to polish these mentioned KDF patches and add them to the
kernel crypto API? The sprawl of key derivation logic here and there which
seemingly does not comply to any standard and thus possibly have issues should
be prevented and cleaned up.

Ciao
Stephan



2019-01-07 18:15:35

by joeyli

[permalink] [raw]
Subject: Re: [PATCH 0/5 v2][RFC] Encryption and authentication for hibernate snapshot image

Hi Pavel,

Thanks for your review!

On Sun, Jan 06, 2019 at 07:10:27PM +0100, Pavel Machek wrote:
> Hi!
>
> > This patchset is the implementation of encryption and authentication
> > for hibernate snapshot image. The image will be encrypted by AES and
> > authenticated by HMAC.
>
> Ok, so you encrypt.
>

Yes, encryption and authentication.

> > The hibernate function can be used to snapshot memory pages to an image,
> > then kernel restores the image to memory space in a appropriate time.
> > There have secrets in snapshot image and cracker may modifies it for
> > hacking system. Encryption and authentication of snapshot image can protect
> > the system.
> >
> > Hibernate function requests the master key through key retention service.
> > The snapshot master key can be a trusted key or a user defined key. The
> > name of snapshot master key is fixed to "swsusp-kmk". User should loads
> > swsusp-kmk to kernel by keyctl tool before the hibernation resume.
> > e.g. The swsusp-kmk must be loaded before systemd-hibernate-resume
>
> But if userspace has a key, encryption is useless against root.
>

Yes, but this concern is not only for hibernation encryption. This patch
set does not provide solution against this concern.

The purpose of this patch set is to encrypt and authenticate hibernate
snapshot image in kernel space. It also requests key through keyring
mechanism. Which means that we can easy to adapt to new key type from
keyring in the future.

Currently TPM trusted key or user defined key types are not against
root. Even using the TPM trusted key, it still can be unsealed by root
before the PCRs be capped (unless we capped PCRs in kernel).

My solution for keeping the secret by kernel is the EFI secure key type:
https://lkml.org/lkml/2018/8/5/31

But the EFI boot variable doesn't design for keeping secret, so Windows
and OEM/ODM do not use boot variable to keep secret. So this idea can
not be accepted. We must think other key type against root.

> > The TPM trusted key type is preferred to be the master key. But user
> > defined key can also be used for testing or when the platform doesn't
> > have TPM. User must be aware that the security of user key relies on
> > user space. If the root account be compromised, then the user key will
> > easy to be grabbed.
>
> In the TPM case, does userland have access to the key?
>

In the TPM case, userland can only touch the sealed key blob. So userland
doesn't know the real secret. But it has risk that root unseals the key
before PCRs be capped.

> Please explain your security goals.
>

My security goals:

- Encrypt and authicate hibernate snapshot image in kernel space. Userspace
can only touch an encrypted and signed snapshot image.

- The code of encryption are in kernel. They will be signed and verify with
kernel binary when secure boot enabled. It's better than using
unauthenticated userspace code at runtime.

- Using TPM trusted key, at least the security of hibernation aligns with
other subsystem. e.g. EVM.

- Using keyring as the key source of hibernation. Then we can easy to
adapt to new key type against root be compromised.

>
> > Lee, Chun-Yi (5):
> > PM / hibernate: Create snapshot keys handler
> > PM / hibernate: Generate and verify signature for snapshot image
> > PM / hibernate: Encrypt snapshot image
> > PM / hibernate: Erase the snapshot master key in snapshot pages
> > PM / hibernate: An option to request that snapshot image must be
> > authenticated
> >

Thanks a lot!
Joey Lee

2019-01-07 18:58:08

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH 0/5 v2][RFC] Encryption and authentication for hibernate snapshot image

Hi!

> Thanks for your review!
>
> > > The hibernate function can be used to snapshot memory pages to an image,
> > > then kernel restores the image to memory space in a appropriate time.
> > > There have secrets in snapshot image and cracker may modifies it for
> > > hacking system. Encryption and authentication of snapshot image can protect
> > > the system.
> > >
> > > Hibernate function requests the master key through key retention service.
> > > The snapshot master key can be a trusted key or a user defined key. The
> > > name of snapshot master key is fixed to "swsusp-kmk". User should loads
> > > swsusp-kmk to kernel by keyctl tool before the hibernation resume.
> > > e.g. The swsusp-kmk must be loaded before systemd-hibernate-resume
> >
> > But if userspace has a key, encryption is useless against root.
> >
>
> Yes, but this concern is not only for hibernation encryption. This patch
> set does not provide solution against this concern.

So, can we postpone these patches until we have a solution secure
against root users?

> My security goals:
>
> - Encrypt and authicate hibernate snapshot image in kernel space. Userspace
> can only touch an encrypted and signed snapshot image.
>
> - The code of encryption are in kernel. They will be signed and verify with
> kernel binary when secure boot enabled. It's better than using
> unauthenticated userspace code at runtime.

These are not goals. I'd like to understand why you want to put it into
kernel in the first place.
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html


Attachments:
(No filename) (1.66 kB)
signature.asc (188.00 B)
Digital signature
Download all attachments

2019-01-07 19:03:09

by Dan Carpenter

[permalink] [raw]
Subject: Re: [PATCH 2/5] PM / hibernate: Generate and verify signature for snapshot image

Hi Chun-Yi,

url: https://github.com/0day-ci/linux/commits/Lee-Chun-Yi/Encryption-and-authentication-for-hibernate-snapshot-image/20190104-062004

smatch warnings:
kernel/power/user.c:411 snapshot_ioctl() warn: inconsistent returns 'mutex:&system_transition_mutex'.
Locked on: line 265
Unlocked on: line 213

[ There are some returns missing. I don't know how/why... -dan ]

# https://github.com/0day-ci/linux/commit/1e583ff7a095d508d78bea4d35a916594c2fc23c
git remote add linux-review https://github.com/0day-ci/linux
git remote update linux-review
git checkout 1e583ff7a095d508d78bea4d35a916594c2fc23c
vim +411 kernel/power/user.c

6e1819d61 Rafael J. Wysocki 2006-03-23 203
52d11025d Alan Cox 2008-06-11 204 static long snapshot_ioctl(struct file *filp, unsigned int cmd,
52d11025d Alan Cox 2008-06-11 205 unsigned long arg)
6e1819d61 Rafael J. Wysocki 2006-03-23 206 {
6e1819d61 Rafael J. Wysocki 2006-03-23 207 int error = 0;
6e1819d61 Rafael J. Wysocki 2006-03-23 208 struct snapshot_data *data;
af508b34d Rafael J. Wysocki 2007-10-26 209 loff_t size;
3aef83e0e Rafael J. Wysocki 2006-12-06 210 sector_t offset;
6e1819d61 Rafael J. Wysocki 2006-03-23 211
6e1819d61 Rafael J. Wysocki 2006-03-23 212 if (_IOC_TYPE(cmd) != SNAPSHOT_IOC_MAGIC)
6e1819d61 Rafael J. Wysocki 2006-03-23 213 return -ENOTTY;
6e1819d61 Rafael J. Wysocki 2006-03-23 214 if (_IOC_NR(cmd) > SNAPSHOT_IOC_MAXNR)
6e1819d61 Rafael J. Wysocki 2006-03-23 215 return -ENOTTY;
6e1819d61 Rafael J. Wysocki 2006-03-23 216 if (!capable(CAP_SYS_ADMIN))
6e1819d61 Rafael J. Wysocki 2006-03-23 217 return -EPERM;
6e1819d61 Rafael J. Wysocki 2006-03-23 218
55f2503c3 Pingfan Liu 2018-07-31 219 if (!mutex_trylock(&system_transition_mutex))
25f2f3daa Rafael J. Wysocki 2008-06-11 220 return -EBUSY;
6e1819d61 Rafael J. Wysocki 2006-03-23 221
942f40155 Rafael J. Wysocki 2013-08-30 222 lock_device_hotplug();
25f2f3daa Rafael J. Wysocki 2008-06-11 223 data = filp->private_data;
52d11025d Alan Cox 2008-06-11 224
6e1819d61 Rafael J. Wysocki 2006-03-23 225 switch (cmd) {
6e1819d61 Rafael J. Wysocki 2006-03-23 226
6e1819d61 Rafael J. Wysocki 2006-03-23 227 case SNAPSHOT_FREEZE:
6e1819d61 Rafael J. Wysocki 2006-03-23 228 if (data->frozen)
6e1819d61 Rafael J. Wysocki 2006-03-23 229 break;
1bfcf1304 Rafael J. Wysocki 2008-10-15 230
232b14328 Rafael J. Wysocki 2007-10-18 231 printk("Syncing filesystems ... ");
70f68ee81 Dominik Brodowski 2018-03-14 232 ksys_sync();
232b14328 Rafael J. Wysocki 2007-10-18 233 printk("done.\n");
232b14328 Rafael J. Wysocki 2007-10-18 234
1bfcf1304 Rafael J. Wysocki 2008-10-15 235 error = freeze_processes();
8fd37a4c9 Rafael J. Wysocki 2013-08-30 236 if (error)
8fd37a4c9 Rafael J. Wysocki 2013-08-30 237 break;
8fd37a4c9 Rafael J. Wysocki 2013-08-30 238
8fd37a4c9 Rafael J. Wysocki 2013-08-30 239 error = create_basic_memory_bitmaps();
8fd37a4c9 Rafael J. Wysocki 2013-08-30 240 if (error)
8fd37a4c9 Rafael J. Wysocki 2013-08-30 241 thaw_processes();
8fd37a4c9 Rafael J. Wysocki 2013-08-30 242 else
7bc9b1cff Rafael J. Wysocki 2013-10-18 243 data->frozen = true;
8fd37a4c9 Rafael J. Wysocki 2013-08-30 244
6e1819d61 Rafael J. Wysocki 2006-03-23 245 break;
6e1819d61 Rafael J. Wysocki 2006-03-23 246
6e1819d61 Rafael J. Wysocki 2006-03-23 247 case SNAPSHOT_UNFREEZE:
2f41dddbb Rafael J. Wysocki 2007-06-16 248 if (!data->frozen || data->ready)
6e1819d61 Rafael J. Wysocki 2006-03-23 249 break;
c9e664f1f Rafael J. Wysocki 2010-12-03 250 pm_restore_gfp_mask();
1e583ff7a Lee, Chun-Yi 2019-01-03 251 snapshot_restore_trampoline();
8fd37a4c9 Rafael J. Wysocki 2013-08-30 252 free_basic_memory_bitmaps();
aab172891 Rafael J. Wysocki 2013-09-30 253 data->free_bitmaps = false;
6e1819d61 Rafael J. Wysocki 2006-03-23 254 thaw_processes();
7bc9b1cff Rafael J. Wysocki 2013-10-18 255 data->frozen = false;
6e1819d61 Rafael J. Wysocki 2006-03-23 256 break;
6e1819d61 Rafael J. Wysocki 2006-03-23 257
b694e52eb Jiri Slaby 2010-01-27 258 case SNAPSHOT_CREATE_IMAGE:
6e1819d61 Rafael J. Wysocki 2006-03-23 259 if (data->mode != O_RDONLY || !data->frozen || data->ready) {
6e1819d61 Rafael J. Wysocki 2006-03-23 260 error = -EPERM;
6e1819d61 Rafael J. Wysocki 2006-03-23 261 break;
6e1819d61 Rafael J. Wysocki 2006-03-23 262 }
1e583ff7a Lee, Chun-Yi 2019-01-03 263 error = snapshot_key_init();
1e583ff7a Lee, Chun-Yi 2019-01-03 264 if (error)
1e583ff7a Lee, Chun-Yi 2019-01-03 265 return error;
^^^^^^^^^^^^^

1e583ff7a Lee, Chun-Yi 2019-01-03 266 error = snapshot_create_trampoline();
1e583ff7a Lee, Chun-Yi 2019-01-03 267 if (error)
1e583ff7a Lee, Chun-Yi 2019-01-03 268 return error;
^^^^^^^^^^^^^
These should be break statements.

c9e664f1f Rafael J. Wysocki 2010-12-03 269 pm_restore_gfp_mask();
eb57c1cf0 Rafael J. Wysocki 2007-10-26 270 error = hibernation_snapshot(data->platform_support);
51d6ff7ac Srivatsa S. Bhat 2012-02-04 271 if (!error) {
cc5d207c8 Rafael J. Wysocki 2007-10-26 272 error = put_user(in_suspend, (int __user *)arg);
a556d5b58 Srivatsa S. Bhat 2012-02-04 273 data->ready = !freezer_test_done && !error;
97819a262 Srivatsa S. Bhat 2011-12-01 274 freezer_test_done = false;
97819a262 Srivatsa S. Bhat 2011-12-01 275 }
6e1819d61 Rafael J. Wysocki 2006-03-23 276 break;
6e1819d61 Rafael J. Wysocki 2006-03-23 277
6e1819d61 Rafael J. Wysocki 2006-03-23 278 case SNAPSHOT_ATOMIC_RESTORE:
8357376d3 Rafael J. Wysocki 2006-12-06 279 snapshot_write_finalize(&data->handle);
6e1819d61 Rafael J. Wysocki 2006-03-23 280 if (data->mode != O_WRONLY || !data->frozen ||
6e1819d61 Rafael J. Wysocki 2006-03-23 281 !snapshot_image_loaded(&data->handle)) {
6e1819d61 Rafael J. Wysocki 2006-03-23 282 error = -EPERM;
6e1819d61 Rafael J. Wysocki 2006-03-23 283 break;
6e1819d61 Rafael J. Wysocki 2006-03-23 284 }
1e583ff7a Lee, Chun-Yi 2019-01-03 285 if (snapshot_image_verify_decrypt()) {
1e583ff7a Lee, Chun-Yi 2019-01-03 286 error = -EPERM;
1e583ff7a Lee, Chun-Yi 2019-01-03 287 break;
1e583ff7a Lee, Chun-Yi 2019-01-03 288 }
1e583ff7a Lee, Chun-Yi 2019-01-03 289 snapshot_init_trampoline();
eb57c1cf0 Rafael J. Wysocki 2007-10-26 290 error = hibernation_restore(data->platform_support);
6e1819d61 Rafael J. Wysocki 2006-03-23 291 break;
6e1819d61 Rafael J. Wysocki 2006-03-23 292
6e1819d61 Rafael J. Wysocki 2006-03-23 293 case SNAPSHOT_FREE:
6e1819d61 Rafael J. Wysocki 2006-03-23 294 swsusp_free();
6e1819d61 Rafael J. Wysocki 2006-03-23 295 memset(&data->handle, 0, sizeof(struct snapshot_handle));
7bc9b1cff Rafael J. Wysocki 2013-10-18 296 data->ready = false;
181e9bdef Rafael J. Wysocki 2012-01-29 297 /*
181e9bdef Rafael J. Wysocki 2012-01-29 298 * It is necessary to thaw kernel threads here, because
181e9bdef Rafael J. Wysocki 2012-01-29 299 * SNAPSHOT_CREATE_IMAGE may be invoked directly after
181e9bdef Rafael J. Wysocki 2012-01-29 300 * SNAPSHOT_FREE. In that case, if kernel threads were not
181e9bdef Rafael J. Wysocki 2012-01-29 301 * thawed, the preallocation of memory carried out by
181e9bdef Rafael J. Wysocki 2012-01-29 302 * hibernation_snapshot() might run into problems (i.e. it
181e9bdef Rafael J. Wysocki 2012-01-29 303 * might fail or even deadlock).
181e9bdef Rafael J. Wysocki 2012-01-29 304 */
181e9bdef Rafael J. Wysocki 2012-01-29 305 thaw_kernel_threads();
6e1819d61 Rafael J. Wysocki 2006-03-23 306 break;
6e1819d61 Rafael J. Wysocki 2006-03-23 307
b694e52eb Jiri Slaby 2010-01-27 308 case SNAPSHOT_PREF_IMAGE_SIZE:
6e1819d61 Rafael J. Wysocki 2006-03-23 309 image_size = arg;
6e1819d61 Rafael J. Wysocki 2006-03-23 310 break;
6e1819d61 Rafael J. Wysocki 2006-03-23 311
af508b34d Rafael J. Wysocki 2007-10-26 312 case SNAPSHOT_GET_IMAGE_SIZE:
af508b34d Rafael J. Wysocki 2007-10-26 313 if (!data->ready) {
af508b34d Rafael J. Wysocki 2007-10-26 314 error = -ENODATA;
af508b34d Rafael J. Wysocki 2007-10-26 315 break;
af508b34d Rafael J. Wysocki 2007-10-26 316 }
af508b34d Rafael J. Wysocki 2007-10-26 317 size = snapshot_get_image_size();
af508b34d Rafael J. Wysocki 2007-10-26 318 size <<= PAGE_SHIFT;
af508b34d Rafael J. Wysocki 2007-10-26 319 error = put_user(size, (loff_t __user *)arg);
af508b34d Rafael J. Wysocki 2007-10-26 320 break;
af508b34d Rafael J. Wysocki 2007-10-26 321
b694e52eb Jiri Slaby 2010-01-27 322 case SNAPSHOT_AVAIL_SWAP_SIZE:
af508b34d Rafael J. Wysocki 2007-10-26 323 size = count_swap_pages(data->swap, 1);
af508b34d Rafael J. Wysocki 2007-10-26 324 size <<= PAGE_SHIFT;
af508b34d Rafael J. Wysocki 2007-10-26 325 error = put_user(size, (loff_t __user *)arg);
6e1819d61 Rafael J. Wysocki 2006-03-23 326 break;
6e1819d61 Rafael J. Wysocki 2006-03-23 327
b694e52eb Jiri Slaby 2010-01-27 328 case SNAPSHOT_ALLOC_SWAP_PAGE:
6e1819d61 Rafael J. Wysocki 2006-03-23 329 if (data->swap < 0 || data->swap >= MAX_SWAPFILES) {
6e1819d61 Rafael J. Wysocki 2006-03-23 330 error = -ENODEV;
6e1819d61 Rafael J. Wysocki 2006-03-23 331 break;
6e1819d61 Rafael J. Wysocki 2006-03-23 332 }
d1d241cc2 Rafael J. Wysocki 2007-05-06 333 offset = alloc_swapdev_block(data->swap);
6e1819d61 Rafael J. Wysocki 2006-03-23 334 if (offset) {
6e1819d61 Rafael J. Wysocki 2006-03-23 335 offset <<= PAGE_SHIFT;
cc5d207c8 Rafael J. Wysocki 2007-10-26 336 error = put_user(offset, (loff_t __user *)arg);
6e1819d61 Rafael J. Wysocki 2006-03-23 337 } else {
6e1819d61 Rafael J. Wysocki 2006-03-23 338 error = -ENOSPC;
6e1819d61 Rafael J. Wysocki 2006-03-23 339 }
6e1819d61 Rafael J. Wysocki 2006-03-23 340 break;
6e1819d61 Rafael J. Wysocki 2006-03-23 341
6e1819d61 Rafael J. Wysocki 2006-03-23 342 case SNAPSHOT_FREE_SWAP_PAGES:
6e1819d61 Rafael J. Wysocki 2006-03-23 343 if (data->swap < 0 || data->swap >= MAX_SWAPFILES) {
6e1819d61 Rafael J. Wysocki 2006-03-23 344 error = -ENODEV;
6e1819d61 Rafael J. Wysocki 2006-03-23 345 break;
6e1819d61 Rafael J. Wysocki 2006-03-23 346 }
d1d241cc2 Rafael J. Wysocki 2007-05-06 347 free_all_swap_pages(data->swap);
6e1819d61 Rafael J. Wysocki 2006-03-23 348 break;
6e1819d61 Rafael J. Wysocki 2006-03-23 349
9b238205b Luca Tettamanti 2006-03-23 350 case SNAPSHOT_S2RAM:
9b238205b Luca Tettamanti 2006-03-23 351 if (!data->frozen) {
9b238205b Luca Tettamanti 2006-03-23 352 error = -EPERM;
9b238205b Luca Tettamanti 2006-03-23 353 break;
9b238205b Luca Tettamanti 2006-03-23 354 }
6c961dfb7 Rafael J. Wysocki 2007-07-19 355 /*
6c961dfb7 Rafael J. Wysocki 2007-07-19 356 * Tasks are frozen and the notifiers have been called with
6c961dfb7 Rafael J. Wysocki 2007-07-19 357 * PM_HIBERNATION_PREPARE
6c961dfb7 Rafael J. Wysocki 2007-07-19 358 */
6c961dfb7 Rafael J. Wysocki 2007-07-19 359 error = suspend_devices_and_enter(PM_SUSPEND_MEM);
7bc9b1cff Rafael J. Wysocki 2013-10-18 360 data->ready = false;
9b238205b Luca Tettamanti 2006-03-23 361 break;
9b238205b Luca Tettamanti 2006-03-23 362
eb57c1cf0 Rafael J. Wysocki 2007-10-26 363 case SNAPSHOT_PLATFORM_SUPPORT:
eb57c1cf0 Rafael J. Wysocki 2007-10-26 364 data->platform_support = !!arg;
eb57c1cf0 Rafael J. Wysocki 2007-10-26 365 break;
eb57c1cf0 Rafael J. Wysocki 2007-10-26 366
eb57c1cf0 Rafael J. Wysocki 2007-10-26 367 case SNAPSHOT_POWER_OFF:
eb57c1cf0 Rafael J. Wysocki 2007-10-26 368 if (data->platform_support)
eb57c1cf0 Rafael J. Wysocki 2007-10-26 369 error = hibernation_platform_enter();
eb57c1cf0 Rafael J. Wysocki 2007-10-26 370 break;
eb57c1cf0 Rafael J. Wysocki 2007-10-26 371
37b2ba12d Rafael J. Wysocki 2006-12-06 372 case SNAPSHOT_SET_SWAP_AREA:
d1d241cc2 Rafael J. Wysocki 2007-05-06 373 if (swsusp_swap_in_use()) {
37b2ba12d Rafael J. Wysocki 2006-12-06 374 error = -EPERM;
37b2ba12d Rafael J. Wysocki 2006-12-06 375 } else {
37b2ba12d Rafael J. Wysocki 2006-12-06 376 struct resume_swap_area swap_area;
37b2ba12d Rafael J. Wysocki 2006-12-06 377 dev_t swdev;
37b2ba12d Rafael J. Wysocki 2006-12-06 378
37b2ba12d Rafael J. Wysocki 2006-12-06 379 error = copy_from_user(&swap_area, (void __user *)arg,
37b2ba12d Rafael J. Wysocki 2006-12-06 380 sizeof(struct resume_swap_area));
37b2ba12d Rafael J. Wysocki 2006-12-06 381 if (error) {
37b2ba12d Rafael J. Wysocki 2006-12-06 382 error = -EFAULT;
37b2ba12d Rafael J. Wysocki 2006-12-06 383 break;
37b2ba12d Rafael J. Wysocki 2006-12-06 384 }
37b2ba12d Rafael J. Wysocki 2006-12-06 385
37b2ba12d Rafael J. Wysocki 2006-12-06 386 /*
37b2ba12d Rafael J. Wysocki 2006-12-06 387 * User space encodes device types as two-byte values,
37b2ba12d Rafael J. Wysocki 2006-12-06 388 * so we need to recode them
37b2ba12d Rafael J. Wysocki 2006-12-06 389 */
d88d4050d Jiri Slaby 2010-04-10 390 swdev = new_decode_dev(swap_area.dev);
37b2ba12d Rafael J. Wysocki 2006-12-06 391 if (swdev) {
37b2ba12d Rafael J. Wysocki 2006-12-06 392 offset = swap_area.offset;
7bf236874 Rafael J. Wysocki 2007-01-05 393 data->swap = swap_type_of(swdev, offset, NULL);
37b2ba12d Rafael J. Wysocki 2006-12-06 394 if (data->swap < 0)
37b2ba12d Rafael J. Wysocki 2006-12-06 395 error = -ENODEV;
37b2ba12d Rafael J. Wysocki 2006-12-06 396 } else {
37b2ba12d Rafael J. Wysocki 2006-12-06 397 data->swap = -1;
37b2ba12d Rafael J. Wysocki 2006-12-06 398 error = -EINVAL;
37b2ba12d Rafael J. Wysocki 2006-12-06 399 }
37b2ba12d Rafael J. Wysocki 2006-12-06 400 }
37b2ba12d Rafael J. Wysocki 2006-12-06 401 break;
37b2ba12d Rafael J. Wysocki 2006-12-06 402
6e1819d61 Rafael J. Wysocki 2006-03-23 403 default:
6e1819d61 Rafael J. Wysocki 2006-03-23 404 error = -ENOTTY;
6e1819d61 Rafael J. Wysocki 2006-03-23 405
6e1819d61 Rafael J. Wysocki 2006-03-23 406 }
25f2f3daa Rafael J. Wysocki 2008-06-11 407
942f40155 Rafael J. Wysocki 2013-08-30 408 unlock_device_hotplug();
55f2503c3 Pingfan Liu 2018-07-31 409 mutex_unlock(&system_transition_mutex);
25f2f3daa Rafael J. Wysocki 2008-06-11 410
6e1819d61 Rafael J. Wysocki 2006-03-23 @411 return error;
6e1819d61 Rafael J. Wysocki 2006-03-23 412 }
6e1819d61 Rafael J. Wysocki 2006-03-23 413

:::::: The code at line 411 was first introduced by commit
:::::: 6e1819d615f24ce0726a7d0bd3dd0152d7b21654 [PATCH] swsusp: userland interface

:::::: TO: Rafael J. Wysocki <[email protected]>
:::::: CC: Linus Torvalds <[email protected]>

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation

2019-01-08 05:10:28

by Herbert Xu

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

On Mon, Jan 07, 2019 at 04:52:00PM +0100, Stephan Mueller wrote:
>
> Would it make sense to polish these mentioned KDF patches and add them to the
> kernel crypto API? The sprawl of key derivation logic here and there which
> seemingly does not comply to any standard and thus possibly have issues should
> be prevented and cleaned up.

Are we going to have multiple implementations for the same KDF?
If not then the crypto API is not a good fit. To consolidate
multiple implementations of the same KDF, simply provide helpers
for them.

Cheers,
--
Email: Herbert Xu <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

2019-01-08 07:12:09

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

Am Dienstag, 8. Januar 2019, 06:03:58 CET schrieb Herbert Xu:

Hi Herbert,

> Are we going to have multiple implementations for the same KDF?
> If not then the crypto API is not a good fit. To consolidate
> multiple implementations of the same KDF, simply provide helpers
> for them.

It is unlikely to have multiple implementations of a KDF. However, KDFs relate
to hashes like block chaining modes to raw block ciphers. Thus a KDF can be
applied with different hashes.

My idea was to add template support to RNGs (because KDFs are effectively a
type of RNG since they produce an arbitrary output from a fixed input). The
KDFs would be a template wrapping hashes. For example, the CTR-KDF from
SP800-108 could be instantiated like kdf-ctr(sha256).

Ciao
Stephan



2019-01-08 21:43:26

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [PATCH 0/5 v2][RFC] Encryption and authentication for hibernate snapshot image

> On Jan 7, 2019, at 9:37 AM, joeyli <[email protected]> wrote:
>
> Hi Pavel,
>
> Thanks for your review!
>
>> On Sun, Jan 06, 2019 at 07:10:27PM +0100, Pavel Machek wrote:
>> Hi!
>>
>>> This patchset is the implementation of encryption and authentication
>>> for hibernate snapshot image. The image will be encrypted by AES and
>>> authenticated by HMAC.
>>
>> Ok, so you encrypt.
>
> Yes, encryption and authentication.
>
>>> The hibernate function can be used to snapshot memory pages to an image,
>>> then kernel restores the image to memory space in a appropriate time.
>>> There have secrets in snapshot image and cracker may modifies it for
>>> hacking system. Encryption and authentication of snapshot image can protect
>>> the system.
>>>
>>> Hibernate function requests the master key through key retention service.
>>> The snapshot master key can be a trusted key or a user defined key. The
>>> name of snapshot master key is fixed to "swsusp-kmk". User should loads
>>> swsusp-kmk to kernel by keyctl tool before the hibernation resume.
>>> e.g. The swsusp-kmk must be loaded before systemd-hibernate-resume
>>
>> But if userspace has a key, encryption is useless against root.
>
> Yes, but this concern is not only for hibernation encryption. This patch
> set does not provide solution against this concern.
>
> The purpose of this patch set is to encrypt and authenticate hibernate
> snapshot image in kernel space. It also requests key through keyring
> mechanism. Which means that we can easy to adapt to new key type from
> keyring in the future.
>
> Currently TPM trusted key or user defined key types are not against
> root. Even using the TPM trusted key, it still can be unsealed by root
> before the PCRs be capped (unless we capped PCRs in kernel).
>
> My solution for keeping the secret by kernel is the EFI secure key type:
> https://lkml.org/lkml/2018/8/5/31
>
> But the EFI boot variable doesn't design for keeping secret, so Windows
> and OEM/ODM do not use boot variable to keep secret. So this idea can
> not be accepted. We must think other key type against root.
>
>>> The TPM trusted key type is preferred to be the master key. But user
>>> defined key can also be used for testing or when the platform doesn't
>>> have TPM. User must be aware that the security of user key relies on
>>> user space. If the root account be compromised, then the user key will
>>> easy to be grabbed.
>>
>> In the TPM case, does userland have access to the key?
>
> In the TPM case, userland can only touch the sealed key blob. So userland
> doesn't know the real secret. But it has risk that root unseals the key
> before PCRs be capped.
>
>> Please explain your security goals.
>
> My security goals:
>
> - Encrypt and authicate hibernate snapshot image in kernel space. Userspace
> can only touch an encrypted and signed snapshot image.

Signed?

I’m not entirely convinced that the keyring mechanism is what you
want. ISTM that there are two goals here:

a) Encryption: it should be as hard as can reasonably be arranged to
extract secrets from a hibernation image.

b) Authentication part 1: it should not be possible for someone in
possession of a turned-off machine to tamper with the hibernation
image such that the image, when booted, will leak its secrets. This
should protect against attackers who don’t know the encryption key.

c) Authentication part 2: it should be to verify, to the extent
practical, that the image came from the same machine and was really
created using hibernation. Or maybe by the same user.

For (a) and (b), using an AE mode where the key is protected in some
reasonable way. Joey, why are you using HMAC? Please tell me you’re
at least doing encrypt-then-MAC. But why not use a real AE mode like
AES-GCM?


I reviewed the code a bit. Here are some thoughts:

You have some really weird crypto. You’re using an insanely long key
(512 bits, I think, although you’ve used some bizarre indirection).
You’re explicitly checking that it’s not zero, and I don’t see why.

Why are you manually supporting three different key types? Can’t you
just somehow support all key types? And shouldn’t you be verifying
the acceptable usage of trusted keys?

You are using a non-ephemeral key and generating a fresh IV each time.
This is probably okay, but it’s needlessly fragile. Just generate an
entirely fresh key each time, please. You also seem to be doing
encrypt-and-MAC, which is not generally considered acceptable. And
you’re not authenticating everything — just the data. This seems very
weak.

Can you explain the trampoline? It looks like you are using it to
tell the resumed kernel that it was tampered with. If so, NAK to that.
Just abort.

You say “If the encryption key be guessed then the snapshot master key
can also be grabbed from snapshot image.” This makes little sense. If
the encryption key is guessed, the game is over. Just remove this
patch — it seems like pure snake oil.

As far as I can tell, there is only one reason that any of this needs
to be in the kernel: if it’s all in user code, then we lose “lockdown”
protection against compromised user code on a secure boot system. Is
that, in fact, true?

2019-01-08 23:57:51

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH 0/5 v2][RFC] Encryption and authentication for hibernate snapshot image

Hi!

> >> Please explain your security goals.
> >
> > My security goals:
> >
> > - Encrypt and authicate hibernate snapshot image in kernel space. Userspace
> > can only touch an encrypted and signed snapshot image.
>
> Signed?
>
> I’m not entirely convinced that the keyring mechanism is what you
> want. ISTM that there are two goals here:
>
> a) Encryption: it should be as hard as can reasonably be arranged to
> extract secrets from a hibernation image.
>
> b) Authentication part 1: it should not be possible for someone in
> possession of a turned-off machine to tamper with the hibernation
> image such that the image, when booted, will leak its secrets. This
> should protect against attackers who don’t know the encryption key.
>
> c) Authentication part 2: it should be to verify, to the extent
> practical, that the image came from the same machine and was really
> created using hibernation. Or maybe by the same user.

So... this looks like "security goals" I was asking in the first
place. Thanks!

Could we get something like that (with your real goals?) in the next
version of the patch?

> As far as I can tell, there is only one reason that any of this needs
> to be in the kernel: if it’s all in user code, then we lose “lockdown”
> protection against compromised user code on a secure boot system. Is
> that, in fact, true?

And this is what I'd really like answer to. Because... I'd really like
this to be in userspace if it does not provide additional security
guarantees.

Thanks,
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html


Attachments:
(No filename) (1.68 kB)
signature.asc (188.00 B)
Digital signature
Download all attachments

2019-01-08 23:59:54

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler


> On Jan 7, 2019, at 11:09 PM, Stephan Mueller <[email protected]> wrote:
>
> Am Dienstag, 8. Januar 2019, 06:03:58 CET schrieb Herbert Xu:
>
> Hi Herbert,
>
>> Are we going to have multiple implementations for the same KDF?
>> If not then the crypto API is not a good fit. To consolidate
>> multiple implementations of the same KDF, simply provide helpers
>> for them.
>
> It is unlikely to have multiple implementations of a KDF. However, KDFs relate
> to hashes like block chaining modes to raw block ciphers. Thus a KDF can be
> applied with different hashes.
>
> My idea was to add template support to RNGs (because KDFs are effectively a
> type of RNG since they produce an arbitrary output from a fixed input). The
> KDFs would be a template wrapping hashes. For example, the CTR-KDF from
> SP800-108 could be instantiated like kdf-ctr(sha256).
>
>

I think that, if the crypto API is going to grow a KDF facility, it should be done right. Have a key type or flag or whatever that says “this key may *only* be used to derive keys using such-and-such algorithm”, and have a helper to derive a key. That helper should take some useful parameters and mix them in:

- What type of key is being derived? ECDSA signing key? HMAC key? AES key?

- Can user code access the derived key?

- What is the key’s purpose? “Encrypt and authenticate a hibernation image” would be a purpose.

- Number of bytes.

All of these parameters should be mixed in to the key derivation.

Also, an AE key, even for AES+HMAC, should be just one derived key. If you need 512 bits, ask for a 512-bit key, not two 256-bit keys.

2019-01-09 00:47:05

by James Bottomley

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

On Tue, 2019-01-08 at 15:54 -0800, Andy Lutomirski wrote:
> > On Jan 7, 2019, at 11:09 PM, Stephan Mueller <[email protected]>
> > wrote:
> >
> > Am Dienstag, 8. Januar 2019, 06:03:58 CET schrieb Herbert Xu:
> >
> > Hi Herbert,
> >
> > > Are we going to have multiple implementations for the same KDF?
> > > If not then the crypto API is not a good fit. To consolidate
> > > multiple implementations of the same KDF, simply provide helpers
> > > for them.
> >
> > It is unlikely to have multiple implementations of a KDF. However,
> > KDFs relate to hashes like block chaining modes to raw block
> > ciphers. Thus a KDF can be applied with different hashes.
> >
> > My idea was to add template support to RNGs (because KDFs are
> > effectively a type of RNG since they produce an arbitrary output
> > from a fixed input). The KDFs would be a template wrapping hashes.
> > For example, the CTR-KDF from SP800-108 could be instantiated like
> > kdf-ctr(sha256).
> >
> >
>
> I think that, if the crypto API is going to grow a KDF facility, it
> should be done right. Have a key type or flag or whatever that says
> “this key may *only* be used to derive keys using such-and-such
> algorithm”, and have a helper to derive a key. That helper should
> take some useful parameters and mix them in:
>
> - What type of key is being derived? ECDSA signing key? HMAC
> key? AES key?
>
> - Can user code access the derived key?
>
> - What is the key’s purpose? “Encrypt and authenticate a hibernation
> image” would be a purpose.
>
> - Number of bytes.
>
> All of these parameters should be mixed in to the key derivation.
>
> Also, an AE key, even for AES+HMAC, should be just one derived
> key. If you need 512 bits, ask for a 512-bit key, not two 256-bit
> keys.

Actually, it would be enormously helpful if we could reuse these pieces
for the TPM as well. It has two KDFs: KDFa, which is the CTR-KDF from
SP800-108 and KDFe which is the SP800-56A KDF for elliptic curve one
pass Diffie Hellman, so if we're going to do the former, I'd really
like the latter as well.

The way the TPM parametrises input to both KDFs is

(hashAlg, key, label, contextU, contextV, bits)

Where

hashAlg = the hash algorithm used as the PRF
key = the input parameter of variable bit size or
the x co-ordinate of the shared point
label = An ASCII string representing the use
contextU = public input U
contextV = public input V
bits = number of output bits

Is that a good enough parametrisation (not the only way you distinguish
uses is with the label, which is not recoverable)? ContextU and
ContextV are simply concatenated to form the full Context of SP800-108,
but we tend to need two separate inputs (for KDFe they're the public x
co-ordinates of the points of the two inputs to ECDH for instance; in
KDFa they're usually the local and remote nonces).

The labels for TPM usage are things like "INTEGRITY" for HMAC keys or
"CFB" when generating an aes128-cfb session key. For KDFe, the tpm
seems to like the label "SECRET". Although the TPM specifies fixed
short strings for the label, nothing prevents them being longer like
the purpose you state above (essentially we could mix purpose, use and
key type into the label and the contexts).

From the point of view of accelerators, the only thing you really need
to know is the hash algorthim (PRF), because everything else above is
an input to the function, so I suppose it makes sense to name them as
kdf-X(PRF) where X would be ctr or ecdh and PRF would be a hash.

James


2019-01-09 01:46:33

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

[Adding Jarkko because this stuff relates to the TPM.]

On Tue, Jan 8, 2019 at 4:44 PM James Bottomley
<[email protected]> wrote:
>
> On Tue, 2019-01-08 at 15:54 -0800, Andy Lutomirski wrote:
> > > On Jan 7, 2019, at 11:09 PM, Stephan Mueller <[email protected]>
> > > wrote:
> > >
> > > Am Dienstag, 8. Januar 2019, 06:03:58 CET schrieb Herbert Xu:
> > >
> > > Hi Herbert,
> > >
> > > > Are we going to have multiple implementations for the same KDF?
> > > > If not then the crypto API is not a good fit. To consolidate
> > > > multiple implementations of the same KDF, simply provide helpers
> > > > for them.
> > >
> > > It is unlikely to have multiple implementations of a KDF. However,
> > > KDFs relate to hashes like block chaining modes to raw block
> > > ciphers. Thus a KDF can be applied with different hashes.
> > >
> > > My idea was to add template support to RNGs (because KDFs are
> > > effectively a type of RNG since they produce an arbitrary output
> > > from a fixed input). The KDFs would be a template wrapping hashes.
> > > For example, the CTR-KDF from SP800-108 could be instantiated like
> > > kdf-ctr(sha256).
> > >
> > >
> >
> > I think that, if the crypto API is going to grow a KDF facility, it
> > should be done right. Have a key type or flag or whatever that says
> > “this key may *only* be used to derive keys using such-and-such
> > algorithm”, and have a helper to derive a key. That helper should
> > take some useful parameters and mix them in:
> >
> > - What type of key is being derived? ECDSA signing key? HMAC
> > key? AES key?
> >
> > - Can user code access the derived key?
> >
> > - What is the key’s purpose? “Encrypt and authenticate a hibernation
> > image” would be a purpose.
> >
> > - Number of bytes.
> >
> > All of these parameters should be mixed in to the key derivation.
> >
> > Also, an AE key, even for AES+HMAC, should be just one derived
> > key. If you need 512 bits, ask for a 512-bit key, not two 256-bit
> > keys.
>
> Actually, it would be enormously helpful if we could reuse these pieces
> for the TPM as well. It has two KDFs: KDFa, which is the CTR-KDF from
> SP800-108 and KDFe which is the SP800-56A KDF for elliptic curve one
> pass Diffie Hellman, so if we're going to do the former, I'd really
> like the latter as well.
>
> The way the TPM parametrises input to both KDFs is
>
> (hashAlg, key, label, contextU, contextV, bits)
>
> Where
>
> hashAlg = the hash algorithm used as the PRF
> key = the input parameter of variable bit size or
> the x co-ordinate of the shared point
> label = An ASCII string representing the use
> contextU = public input U
> contextV = public input V
> bits = number of output bits
>
> Is that a good enough parametrisation (not the only way you distinguish
> uses is with the label, which is not recoverable)? ContextU and
> ContextV are simply concatenated to form the full Context of SP800-108,
> but we tend to need two separate inputs (for KDFe they're the public x
> co-ordinates of the points of the two inputs to ECDH for instance; in
> KDFa they're usually the local and remote nonces).
>
> The labels for TPM usage are things like "INTEGRITY" for HMAC keys or
> "CFB" when generating an aes128-cfb session key. For KDFe, the tpm
> seems to like the label "SECRET". Although the TPM specifies fixed
> short strings for the label, nothing prevents them being longer like
> the purpose you state above (essentially we could mix purpose, use and
> key type into the label and the contexts).
>

That really ought to cover anything the kernel needs.

But can you explain what's up with with KDFe? The obvious searches
end up with just warnings that the US currently has no government :(

Anyway, if we're talking about the TPM, it seems like the entire
"trusted key" mechanism in the kernel is missing the point. If I want
to encrypt something like a hibernation image on a machine with a TPM,
it makes essentially no sense to me that we would get a key with a
known raw value that is merely TPM-backed (i.e. the "trusted key") and
use that to decrypt the image. The right way to do it is to the use
the TPM as it was intended to be used: generate a single-use key that
protects the hibernation image and seal *that* directly on the TPM,
such that it can only be unsealed with appropriate PCR values. Heck,
we could even use one of the fancy NV counters such that we *can't*
decrypt the image later on. And using HMAC or any AE construction the
normal way is also wrong -- we should *hash* the image and sign the
hash directly on the TPM so that the restore code can validate the PCR
values that were in place when the hibernation image was created. [0]

In other words, I think that a kernel-based encrypted hibernation
mechanism should create an image like this:

- wrapped key
- instructions, if needed, for unwrapping
- hash of the entire image except the hash and signature fields
- signature of the hash

and the remainder is a regular hiberation image that is encrypted
against the key. No AE is needed -- just encryption. And there's no
trampoline, no weird per-page hashing, etc. Of course, this also
means that someone needs to audit the hibernation restore code to make
sure that there's no way for a malicious image to gain code execution
over the restoring kernel before the verification even runs. Or some
much more complicated hash can be used that supports incremental
verification.


(Also, do we have a sensible story of how the TPM interacts with
hibernation at all? Presumably we should at least try to replay the
PCR operations that have occurred so that we can massage the PCRs into
the same state post-hibernation. Also, do we have any way for the
kernel to sign something with the TPM along with an attestation that
the signature was requested *by the kernel*? Something like a
sub-hierarchy of keys that the kernel explicitly prevents userspace
from accessing?)

[0] If you take some data, run it through an authenticated encryption
algorithm, and sign (key, nonce, tag), I think you're operating
outside of the accepted security definitions if you expect this to
guarantee that the data wasn't tampered with. I'm reasonably
confident that there are quite a few excellent AE algorithms that
completely fail if used this like this. In fact, pretty much all of
the modern fast ones probably fail. AE is for when the key is
*secret*.

2019-01-09 06:29:38

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

Am Mittwoch, 9. Januar 2019, 00:54:22 CET schrieb Andy Lutomirski:

Hi Andy,
>
> I think that, if the crypto API is going to grow a KDF facility, it should
> be done right. Have a key type or flag or whatever that says “this key may
> *only* be used to derive keys using such-and-such algorithm”, and have a
> helper to derive a key. That helper should take some useful parameters and
> mix them in:
>
> - What type of key is being derived? ECDSA signing key? HMAC key? AES
> key?
>
> - Can user code access the derived key?
>
> - What is the key’s purpose? “Encrypt and authenticate a hibernation image”
> would be a purpose.
>
> - Number of bytes.
>
> All of these parameters should be mixed in to the key derivation.
>
> Also, an AE key, even for AES+HMAC, should be just one derived key. If you
> need 512 bits, ask for a 512-bit key, not two 256-bit keys.

I concur with your requirements. However, is the kernel crypto API the right
place to enforce such policies? To me, the kernel crypto API is a tinker-toy
set of ciphers.

The real policy enforcer would or should be the keyring facility. Thus, may I
propose to:

- implement the cryptographic primitive of the KDF in the kernel crypto API

- implement the policy system how to use the KDF in the keyring facility

Ciao
Stephan



2019-01-09 06:48:54

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

Am Mittwoch, 9. Januar 2019, 01:44:31 CET schrieb James Bottomley:

Hi James,

> Actually, it would be enormously helpful if we could reuse these pieces
> for the TPM as well.

Could you please help me understand whether the KDFs in TPM are directly
usable as a standalone cipher primitive or does it go together with additional
key generation operations?

> It has two KDFs: KDFa, which is the CTR-KDF from
> SP800-108 and KDFe which is the SP800-56A KDF for elliptic curve one
> pass Diffie Hellman, so if we're going to do the former, I'd really
> like the latter as well.
>
> The way the TPM parametrises input to both KDFs is
>
> (hashAlg, key, label, contextU, contextV, bits)
>
> Where
>
> hashAlg = the hash algorithm used as the PRF
> key = the input parameter of variable bit size or
> the x co-ordinate of the shared point
> label = An ASCII string representing the use
> contextU = public input U
> contextV = public input V
> bits = number of output bits

When implementing KDFs as an extension of the kernel crypto API's RNG facility
we currently have to handle the limitiation of the existing API. The label/
context data (and when considering RFC 5869 HKDF requring IKM, salt and
additional information as input) currently cannot directly be communicated
through the API.

The issue is that the RNG facility currently has the following prototype
defined:

int (*generate)(struct crypto_rng *tfm,
const u8 *src, unsigned int slen,
u8 *dst, unsigned int dlen);

The src pointer would need to take the label/context data.

Would it be appropriate, to implement a type cast to a structure from the u8
pointer?

E.g. for the aforementioned label/context data, we could define something like

struct crypto_kdf_ctr {
char *label;
size_t label_len;
u8 *contextU;
size_t contextU_len;
u8 *contextV;
size_t contextV_len;
};

And the implementation of the generate function for CTR KDF would then have a
type cast along the following lines:

if (slen != sizeof(struct crypto_kdf_ctr))
return -EINVAL;
const struct crypto_kdf_ctr *kdf_ctr_input = (struct crypto_kdf_ctr *)src;


For different KDFs, different structs would be needed.

Ciao
Stephan



2019-01-09 06:51:58

by James Bottomley

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

On Tue, 2019-01-08 at 17:43 -0800, Andy Lutomirski wrote:
> [Adding Jarkko because this stuff relates to the TPM.]
>
> On Tue, Jan 8, 2019 at 4:44 PM James Bottomley
> <[email protected]> wrote:
> >
> > On Tue, 2019-01-08 at 15:54 -0800, Andy Lutomirski wrote:
> > > > On Jan 7, 2019, at 11:09 PM, Stephan Mueller <smueller@chronox.
> > > > de>
> > > > wrote:
> > > >
> > > > Am Dienstag, 8. Januar 2019, 06:03:58 CET schrieb Herbert Xu:
> > > >
> > > > Hi Herbert,
> > > >
> > > > > Are we going to have multiple implementations for the same
> > > > > KDF? If not then the crypto API is not a good fit. To
> > > > > consolidate multiple implementations of the same KDF, simply
> > > > > provide helpers for them.
> > > >
> > > > It is unlikely to have multiple implementations of a KDF.
> > > > However, KDFs relate to hashes like block chaining modes to raw
> > > > block ciphers. Thus a KDF can be applied with different hashes.
> > > >
> > > > My idea was to add template support to RNGs (because KDFs are
> > > > effectively a type of RNG since they produce an arbitrary
> > > > output from a fixed input). The KDFs would be a template
> > > > wrapping hashes. For example, the CTR-KDF from SP800-108 could
> > > > be instantiated like kdf-ctr(sha256).
> > > >
> > > >
> > >
> > > I think that, if the crypto API is going to grow a KDF facility,
> > > it should be done right. Have a key type or flag or whatever that
> > > says “this key may *only* be used to derive keys using such-and-
> > > such algorithm”, and have a helper to derive a key. That helper
> > > should take some useful parameters and mix them in:
> > >
> > > - What type of key is being derived? ECDSA signing key? HMAC
> > > key? AES key?
> > >
> > > - Can user code access the derived key?
> > >
> > > - What is the key’s purpose? “Encrypt and authenticate a
> > > hibernation image” would be a purpose.
> > >
> > > - Number of bytes.
> > >
> > > All of these parameters should be mixed in to the key derivation.
> > >
> > > Also, an AE key, even for AES+HMAC, should be just one derived
> > > key. If you need 512 bits, ask for a 512-bit key, not two 256-
> > > bit keys.
> >
> > Actually, it would be enormously helpful if we could reuse these
> > pieces for the TPM as well. It has two KDFs: KDFa, which is the
> > CTR-KDF from SP800-108 and KDFe which is the SP800-56A KDF for
> > elliptic curve one pass Diffie Hellman, so if we're going to do the
> > former, I'd really like the latter as well.
> >
> > The way the TPM parametrises input to both KDFs is
> >
> > (hashAlg, key, label, contextU, contextV, bits)
> >
> > Where
> >
> > hashAlg = the hash algorithm used as the PRF
> > key = the input parameter of variable bit size or
> > the x co-ordinate of the shared point
> > label = An ASCII string representing the use
> > contextU = public input U
> > contextV = public input V
> > bits = number of output bits
> >
> > Is that a good enough parametrisation (not the only way you
> > distinguish uses is with the label, which is not
> > recoverable)? ContextU and ContextV are simply concatenated to
> > form the full Context of SP800-108, but we tend to need two
> > separate inputs (for KDFe they're the public x co-ordinates of the
> > points of the two inputs to ECDH for instance; in KDFa they're
> > usually the local and remote nonces).
> >
> > The labels for TPM usage are things like "INTEGRITY" for HMAC keys
> > or "CFB" when generating an aes128-cfb session key. For KDFe, the
> > tpm seems to like the label "SECRET". Although the TPM specifies
> > fixed short strings for the label, nothing prevents them being
> > longer like the purpose you state above (essentially we could mix
> > purpose, use and key type into the label and the contexts).
> >
>
> That really ought to cover anything the kernel needs.
>
> But can you explain what's up with with KDFe? The obvious searches
> end up with just warnings that the US currently has no government :(

You mean you can't find SP100-56A because NIST is a government entity
and it's discontinued its website because of the government shutdown?
No idea, I only live here, you'll have to ask a real American.

ACM does have a copy:

http://delivery.acm.org/10.1145/2210000/2206270/SP800-56A_Revision1_Mar08-2007.pdf?ip=50.35.68.20&id=2206270&acc=OPEN&key=4D4702B0C3E38B35%2E4D4702B0C3E38B35%2E4D4702B0C3E38B35%2E6D218144511F3437&__acm__=1546993111_ed9c8bd24b2f838c829d428aac7f5d71

> Anyway, if we're talking about the TPM, it seems like the entire
> "trusted key" mechanism in the kernel is missing the point. If I
> want to encrypt something like a hibernation image on a machine with
> a TPM, it makes essentially no sense to me that we would get a key
> with a known raw value that is merely TPM-backed (i.e. the "trusted
> key") and use that to decrypt the image. The right way to do it is
> to the use the TPM as it was intended to be used: generate a single-
> use key that protects the hibernation image and seal *that* directly
> on the TPM, such that it can only be unsealed with appropriate PCR
> values. Heck, we could even use one of the fancy NV counters such
> that we *can't* decrypt the image later on. And using HMAC or any AE
> construction the normal way is also wrong -- we should *hash* the
> image and sign the hash directly on the TPM so that the restore code
> can validate the PCR values that were in place when the hibernation
> image was created. [0]

Well, theoretically, trusted keys can be used for PCR sealed bundles,
at least in 1.2 ... I'm not sure the 2.0 one actually works because you
have to construct the policy session outside the kernel.

> In other words, I think that a kernel-based encrypted hibernation
> mechanism should create an image like this:
>
> - wrapped key
> - instructions, if needed, for unwrapping

This sounds like the format we use for TPM resident keys, except it
would protect a TPM unseal bundle:

https://git.kernel.org/pub/scm/linux/kernel/git/jejb/openssl_tpm2_engine.git/tree/tpm2-asn.h

> - hash of the entire image except the hash and signature fields
> - signature of the hash
>
> and the remainder is a regular hiberation image that is encrypted
> against the key. No AE is needed -- just encryption. And there's no
> trampoline, no weird per-page hashing, etc. Of course, this also
> means that someone needs to audit the hibernation restore code to
> make sure that there's no way for a malicious image to gain code
> execution over the restoring kernel before the verification even
> runs. Or some much more complicated hash can be used that supports
> incremental verification.
>
>
> (Also, do we have a sensible story of how the TPM interacts with
> hibernation at all?

Not really, no ... there is a TPM patch for LUKS, but trusted keys are
unused within the kernel.

> Presumably we should at least try to replay the PCR operations that
> have occurred so that we can massage the PCRs into the same state
> post-hibernation. Also, do we have any way for the kernel to sign
> something with the TPM along with an attestation that the signature
> was requested *by the kernel*? Something like a sub-hierarchy of
> keys that the kernel explicitly prevents userspace from accessing?)

We're just growing that now with the TPM asymmetric operations.
Attesting that the kernel requested the signature is harder. The TPM
can attest to log entries (as it does for the UEFI log and IMA) and it
can certify keys, but that only proves they're TPM resident not who the
requestor was. Effectively the latter is an assertion about who knows
the key authority, which is hard to prove.

> [0] If you take some data, run it through an authenticated encryption
> algorithm, and sign (key, nonce, tag), I think you're operating
> outside of the accepted security definitions if you expect this to
> guarantee that the data wasn't tampered with. I'm reasonably
> confident that there are quite a few excellent AE algorithms that
> completely fail if used this like this. In fact, pretty much all of
> the modern fast ones probably fail. AE is for when the key is
> *secret*.

Well, I think here, if we were actually trying to solve the problem of
proving the hibernated image were the same one we would need to prove
some log of the kernel operation came to a particular value *after* the
hibernated image were restored ... it's not really possible to
condition key release which must occur before the restore on that
outcome, so it strikes me we need more than a simple release bound to
PCR values.


James


2019-01-09 06:59:54

by James Bottomley

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

On Wed, 2019-01-09 at 07:45 +0100, Stephan Mueller wrote:
> Am Mittwoch, 9. Januar 2019, 01:44:31 CET schrieb James Bottomley:
>
> Hi James,
>
> > Actually, it would be enormously helpful if we could reuse these
> > pieces for the TPM as well.
>
> Could you please help me understand whether the KDFs in TPM are
> directly usable as a standalone cipher primitive or does it go
> together with additional key generation operations?

They're used as generators ... which means they deterministically
produce keys from what the TPM calls seeds so we can get crypto agility
of TPM 2.0 ... well KDFa does. KDFe is simply what NIST recommends you
do when using EC for a shared key agreement ... and really we shouldn't
be using ECDH in the kernel without it.

> > It has two KDFs: KDFa, which is the CTR-KDF from
> > SP800-108 and KDFe which is the SP800-56A KDF for elliptic curve
> > one pass Diffie Hellman, so if we're going to do the former, I'd
> > really like the latter as well.
> >
> > The way the TPM parametrises input to both KDFs is
> >
> > (hashAlg, key, label, contextU, contextV, bits)
> >
> > Where
> >
> > hashAlg = the hash algorithm used as the PRF
> > key = the input parameter of variable bit size or
> > the x co-ordinate of the shared point
> > label = An ASCII string representing the use
> > contextU = public input U
> > contextV = public input V
> > bits = number of output bits
>
> When implementing KDFs as an extension of the kernel crypto API's RNG
> facility we currently have to handle the limitiation of the existing
> API. The label/context data (and when considering RFC 5869 HKDF
> requring IKM, salt and additional information as input) currently
> cannot directly be communicated through the API.
>
> The issue is that the RNG facility currently has the following
> prototype defined:
>
> int (*generate)(struct crypto_rng *tfm,
> const u8 *src, unsigned int slen,
> u8 *dst, unsigned int dlen);
>
> The src pointer would need to take the label/context data.

That's probably good enough. For both KDFa and KDFe the label contextU
and ContextV are concatenated, so in both cases a single source is
probably good enough. However, we also need to feed in the key somehow
since it's usually used separately in the derivation functions.

> Would it be appropriate, to implement a type cast to a structure from
> the u8 pointer?
>
> E.g. for the aforementioned label/context data, we could define
> something like
>
> struct crypto_kdf_ctr {
> char *label;
> size_t label_len;
> u8 *contextU;
> size_t contextU_len;
> u8 *contextV;
> size_t contextV_len;
> };
>
> And the implementation of the generate function for CTR KDF would
> then have a type cast along the following lines:
>
> if (slen != sizeof(struct crypto_kdf_ctr))
> return -EINVAL;
> const struct crypto_kdf_ctr *kdf_ctr_input = (struct
> crypto_kdf_ctr *)src;
>
>
> For different KDFs, different structs would be needed.

Actually, we probably just need the input key (or secret material), the
concatenation and the number of output bits.

James


2019-01-09 07:08:14

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

Am Mittwoch, 9. Januar 2019, 07:58:28 CET schrieb James Bottomley:

Hi James,

> On Wed, 2019-01-09 at 07:45 +0100, Stephan Mueller wrote:
> > Am Mittwoch, 9. Januar 2019, 01:44:31 CET schrieb James Bottomley:
> >
> > Hi James,
> >
> > > Actually, it would be enormously helpful if we could reuse these
> > > pieces for the TPM as well.
> >
> > Could you please help me understand whether the KDFs in TPM are
> > directly usable as a standalone cipher primitive or does it go
> > together with additional key generation operations?
>
> They're used as generators ... which means they deterministically
> produce keys from what the TPM calls seeds so we can get crypto agility
> of TPM 2.0 ... well KDFa does. KDFe is simply what NIST recommends you
> do when using EC for a shared key agreement ... and really we shouldn't
> be using ECDH in the kernel without it.
>

Thanks for clarifying. That would mean that indeed we would have hardware-
provided KDF implementations that may be usable with the kernel crypto API.

[...]
>
> > Would it be appropriate, to implement a type cast to a structure from
> > the u8 pointer?
> >
> > E.g. for the aforementioned label/context data, we could define
> > something like
> >
> > struct crypto_kdf_ctr {
> >
> > char *label;
> > size_t label_len;
> > u8 *contextU;
> > size_t contextU_len;
> > u8 *contextV;
> > size_t contextV_len;
> >
> > };
> >
> > And the implementation of the generate function for CTR KDF would
> >
> > then have a type cast along the following lines:
> > if (slen != sizeof(struct crypto_kdf_ctr))
> >
> > return -EINVAL;
> >
> > const struct crypto_kdf_ctr *kdf_ctr_input = (struct
> >
> > crypto_kdf_ctr *)src;
> >
> >
> > For different KDFs, different structs would be needed.
>
> Actually, we probably just need the input key (or secret material), the
> concatenation and the number of output bits.

Thanks for confirming. Though, when it comes to HKDF (not that I see it being
needed or required in the kernel), there is a need to split up the src pointer
since the mentioned input is used in different ways.

In order to try to get the implementation and thus the interface right, I
would suggest to at least have a consensus on how to handle such situations.

Thus, would the proposal be acceptable for such KDFs that may need to have
different components communicated as input to the KDF?

Ciao
Stephan



2019-01-09 08:23:38

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

On Wed, Jan 09, 2019 at 08:05:21AM +0100, Stephan Mueller wrote:
> Am Mittwoch, 9. Januar 2019, 07:58:28 CET schrieb James Bottomley:
>
> Hi James,
>
> > On Wed, 2019-01-09 at 07:45 +0100, Stephan Mueller wrote:
> > > Am Mittwoch, 9. Januar 2019, 01:44:31 CET schrieb James Bottomley:
> > >
> > > Hi James,
> > >
> > > > Actually, it would be enormously helpful if we could reuse these
> > > > pieces for the TPM as well.
> > >
> > > Could you please help me understand whether the KDFs in TPM are
> > > directly usable as a standalone cipher primitive or does it go
> > > together with additional key generation operations?
> >
> > They're used as generators ... which means they deterministically
> > produce keys from what the TPM calls seeds so we can get crypto agility
> > of TPM 2.0 ... well KDFa does. KDFe is simply what NIST recommends you
> > do when using EC for a shared key agreement ... and really we shouldn't
> > be using ECDH in the kernel without it.
> >
>
> Thanks for clarifying. That would mean that indeed we would have hardware-
> provided KDF implementations that may be usable with the kernel crypto API.
>
> [...]
> >
> > > Would it be appropriate, to implement a type cast to a structure from
> > > the u8 pointer?
> > >
> > > E.g. for the aforementioned label/context data, we could define
> > > something like
> > >
> > > struct crypto_kdf_ctr {
> > >
> > > char *label;
> > > size_t label_len;
> > > u8 *contextU;
> > > size_t contextU_len;
> > > u8 *contextV;
> > > size_t contextV_len;
> > >
> > > };
> > >
> > > And the implementation of the generate function for CTR KDF would
> > >
> > > then have a type cast along the following lines:
> > > if (slen != sizeof(struct crypto_kdf_ctr))
> > >
> > > return -EINVAL;
> > >
> > > const struct crypto_kdf_ctr *kdf_ctr_input = (struct
> > >
> > > crypto_kdf_ctr *)src;
> > >
> > >
> > > For different KDFs, different structs would be needed.
> >
> > Actually, we probably just need the input key (or secret material), the
> > concatenation and the number of output bits.
>
> Thanks for confirming. Though, when it comes to HKDF (not that I see it being
> needed or required in the kernel), there is a need to split up the src pointer
> since the mentioned input is used in different ways.
>
> In order to try to get the implementation and thus the interface right, I
> would suggest to at least have a consensus on how to handle such situations.
>
> Thus, would the proposal be acceptable for such KDFs that may need to have
> different components communicated as input to the KDF?
>

FWIW, it's been very slow going since I've been working on other projects and I
also need to be very sure to get the API changes right, but I still plan to
change the KDF in fscrypt (a.k.a. ext4/f2fs/ubifs encryption) to HKDF-SHA512 as
part of a larger set of improvements to how fscrypt encryption keys are managed.
I sent the last patchset a year ago
(https://marc.info/?l=linux-fsdevel&m=150879493206257) but I'm working to revive
it. In the work-in-progress version in my git tree, this is the commit that
adds a HKDF implementation as fs/crypto/hkdf.c:
https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux.git/commit/?id=e8a78767131c9717ee838f0c4e307948d65a4427
It basically just wraps a crypto_shash for "hmac(sha512)".

I'd be fine with using a common implementation instead, provided that it gives
the same functionality, including supporting user-specified salt and
application-specific info strings, and isn't slower or more complex to use.

(This comment is solely on the tangential discussion about KDF implementations;
I've not looked at the hibernation image encryption stuff yet.)

- Eric

2019-01-09 11:49:35

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

Am Mittwoch, 9. Januar 2019, 09:21:04 CET schrieb Eric Biggers:

Hi Eric,
>
> FWIW, it's been very slow going since I've been working on other projects
> and I also need to be very sure to get the API changes right, but I still
> plan to change the KDF in fscrypt (a.k.a. ext4/f2fs/ubifs encryption) to
> HKDF-SHA512 as part of a larger set of improvements to how fscrypt
> encryption keys are managed. I sent the last patchset a year ago
> (https://marc.info/?l=linux-fsdevel&m=150879493206257) but I'm working to
> revive it. In the work-in-progress version in my git tree, this is the
> commit that adds a HKDF implementation as fs/crypto/hkdf.c:
> https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux.git/commit/?i
> d=e8a78767131c9717ee838f0c4e307948d65a4427 It basically just wraps a
> crypto_shash for "hmac(sha512)".
>
> I'd be fine with using a common implementation instead, provided that it
> gives the same functionality, including supporting user-specified salt and
> application-specific info strings, and isn't slower or more complex to use.
>
> (This comment is solely on the tangential discussion about KDF
> implementations; I've not looked at the hibernation image encryption stuff
> yet.)

Thanks for the clarification. I have started a generic HKDF implementation for
the kernel crypto API which lead to the questions above. I would then also try
to provide a HKDF proposal.

To use the (H)KDF, I currently envision 2 calls apart from alloc/free. The
following code would serve as an example.

* Example without proper error handling:
* char *keying_material = "\x00\x11\x22\x33\x44\x55\x66\x77";
* char *label_context = "\xde\xad\xbe\xef\x00\xde\xad\xbe\xef";
* kdf = crypto_alloc_rng(name, 0, 0);
* crypto_rng_reset(kdf, keying_material, 8);
* crypto_rng_generate(kdf, label_context, 9, outbuf, outbuflen);

That hopefully should be simple enough.

For HKDF, as mentioned, I would envision to use a struct instead of a char *
for the label_context to communicate IKM, Salt, and the label/info
information.

Ciao
Stephan



2019-01-09 16:47:00

by joeyli

[permalink] [raw]
Subject: Re: [PATCH 0/5 v2][RFC] Encryption and authentication for hibernate snapshot image

Hi Andy,

Thanks for your review!

On Tue, Jan 08, 2019 at 01:41:48PM -0800, Andy Lutomirski wrote:
> > On Jan 7, 2019, at 9:37 AM, joeyli <[email protected]> wrote:
> >
> > Hi Pavel,
> >
> > Thanks for your review!
> >
> >> On Sun, Jan 06, 2019 at 07:10:27PM +0100, Pavel Machek wrote:
> >> Hi!
> >>
> >>> This patchset is the implementation of encryption and authentication
> >>> for hibernate snapshot image. The image will be encrypted by AES and
> >>> authenticated by HMAC.
> >>
> >> Ok, so you encrypt.
> >
> > Yes, encryption and authentication.
> >
> >>> The hibernate function can be used to snapshot memory pages to an image,
> >>> then kernel restores the image to memory space in a appropriate time.
> >>> There have secrets in snapshot image and cracker may modifies it for
> >>> hacking system. Encryption and authentication of snapshot image can protect
> >>> the system.
> >>>
> >>> Hibernate function requests the master key through key retention service.
> >>> The snapshot master key can be a trusted key or a user defined key. The
> >>> name of snapshot master key is fixed to "swsusp-kmk". User should loads
> >>> swsusp-kmk to kernel by keyctl tool before the hibernation resume.
> >>> e.g. The swsusp-kmk must be loaded before systemd-hibernate-resume
> >>
> >> But if userspace has a key, encryption is useless against root.
> >
> > Yes, but this concern is not only for hibernation encryption. This patch
> > set does not provide solution against this concern.
> >
> > The purpose of this patch set is to encrypt and authenticate hibernate
> > snapshot image in kernel space. It also requests key through keyring
> > mechanism. Which means that we can easy to adapt to new key type from
> > keyring in the future.
> >
> > Currently TPM trusted key or user defined key types are not against
> > root. Even using the TPM trusted key, it still can be unsealed by root
> > before the PCRs be capped (unless we capped PCRs in kernel).
> >
> > My solution for keeping the secret by kernel is the EFI secure key type:
> > https://lkml.org/lkml/2018/8/5/31
> >
> > But the EFI boot variable doesn't design for keeping secret, so Windows
> > and OEM/ODM do not use boot variable to keep secret. So this idea can
> > not be accepted. We must think other key type against root.
> >
> >>> The TPM trusted key type is preferred to be the master key. But user
> >>> defined key can also be used for testing or when the platform doesn't
> >>> have TPM. User must be aware that the security of user key relies on
> >>> user space. If the root account be compromised, then the user key will
> >>> easy to be grabbed.
> >>
> >> In the TPM case, does userland have access to the key?
> >
> > In the TPM case, userland can only touch the sealed key blob. So userland
> > doesn't know the real secret. But it has risk that root unseals the key
> > before PCRs be capped.
> >
> >> Please explain your security goals.
> >
> > My security goals:
> >
> > - Encrypt and authicate hibernate snapshot image in kernel space. Userspace
> > can only touch an encrypted and signed snapshot image.
>
> Signed?
>
> I’m not entirely convinced that the keyring mechanism is what you
> want. ISTM that there are two goals here:
>
> a) Encryption: it should be as hard as can reasonably be arranged to
> extract secrets from a hibernation image.
>
> b) Authentication part 1: it should not be possible for someone in
> possession of a turned-off machine to tamper with the hibernation
> image such that the image, when booted, will leak its secrets. This
> should protect against attackers who don’t know the encryption key.
>
> c) Authentication part 2: it should be to verify, to the extent
> practical, that the image came from the same machine and was really
> created using hibernation. Or maybe by the same user.
>
> For (a) and (b), using an AE mode where the key is protected in some
> reasonable way. Joey, why are you using HMAC? Please tell me you’re
> at least doing encrypt-then-MAC. But why not use a real AE mode like
> AES-GCM?

The reason for using HMAC is the history for development. My first patch
set is only for hibernate authentication. Then I added encryption code on
top of my authentication patches in last version.

I am doing encrypt-then-MAC. My code ecrypts each page by AES then HMAC
whole snapshot image. I feed encrypted data pages one by one to
crypto_shash_update() API for calculating the hash for whole image.

Because kernel marks non-continuous free pages as a big buffer for
produce/restore hibernate snapshot image. For convenience, I hope that
the size of encrypted page will not be increased. One date page is still
one page after encrypted. It's good for saving limited free pages to
avoid the hibernation failed.

Let's why I encrypt/decrypt data pages one by one, then I copy the
encrypt/decrypt data from buffer page (only one buffer page reserved
for encrypt/decrypt) to original page. I encreypt pages one by one, but
I HMAC and verify the whole snapshot image by update mode.

My target is to create an encrypted/signed snapshot image before the
image be exposed by kernel to userspace or swap.

>
>
> I reviewed the code a bit. Here are some thoughts:
>
> You have some really weird crypto. You’re using an insanely long key
> (512 bits, I think, although you’ve used some bizarre indirection).

Is the key too long for SHA512? I keep that the key size equals to
the digest size because the HMAC spec rfc2104 suggests:

............................ We denote by B the byte-length of such
blocks (B=64 for all the above mentioned examples of hash functions),
and by L the byte-length of hash outputs (L=16 for MD5, L=20 for
SHA-1).
...
The key for HMAC can be of any length (keys longer than B bytes are
first hashed using H). However, less than L bytes is strongly
discouraged as it would decrease the security strength of the
function. Keys longer than L bytes are acceptable but the extra
length would not significantly increase the function strength. (A
longer key may be advisable if the randomness of the key is
considered weak.)

The L is the byte-length of hash output. I use SHA512 so I set the
key size to 64 bytes = 512 bits

> You’re explicitly checking that it’s not zero, and I don’t see why.
>

That's because I use trampoline page to forward the key from boot
kernel to resume kernel for next hibernation cycle. When resuming,
the empty key means that the boot kernel didn't forward valid key
to resume kernel. Then the key initial failed, hibernation can not
be triggered in next cycle.

I will explain why setting trampoline page at below.

> Why are you manually supporting three different key types? Can’t you
> just somehow support all key types? And shouldn’t you be verifying

I only supported two key typs in my patch set, user defined key and
TPM trusted key. The EFI secure boot did not accept by EFI subsystem.
So I didn't support it in this version.

Different key type has different structure. So we need different
handler for extracting the key data from key structure. It's impossible
to support all key types in one code because it's can be arbitrary
defined. For example, we can defined our own user defined key for
specific subsystem or driver, we just need a special handler to handle
it in driver.

> the acceptable usage of trusted keys?
>

Sorry for I didn't capture the meaning of "acceptable usage". The trusted
key already be unsealed by TPM when the key be enrolled by keyctl tool.
So my code just extract the unsealed key data (the random number) for
encryption.

> You are using a non-ephemeral key and generating a fresh IV each time.
> This is probably okay, but it’s needlessly fragile. Just generate an
> entirely fresh key each time, please. You also seem to be doing
> encrypt-and-MAC, which is not generally considered acceptable. And

I have thought about using one time key before. But that means I need
attach the key with snapshot image. Keyring API doesn't provide interface
for other kernel subsystem to feed an encrypted key blob, so I choice
to use non-ephemeral key that's enrolled by userland through keyctl. I
can change to one time key if keyring subsystem allows to be modified.

The encrypt-and-MAC is not acceptable? I am OK to change to other way
just I hope that I can encrypt/decrypt page one by one for the reason
that I just mentioned.

> you’re not authenticating everything — just the data. This seems very
> weak.
>

Is it too weak? Why?

There have three parts of an snapshot image:
image header :: page table pages :: data pages

The real data are in data pages. I thought that encrypt/hmac data pages
can protect sensitive data in pages and also avoid those pages be arbitrary
modified. IMHO arbitrary modifing page table pages is easy to cause
resume kernel crashed.

I keeps iv and signature to image header. They can be public. Did you see
any danger if the header and page table page not be encrypted? Maybe I
missed important things...

> Can you explain the trampoline? It looks like you are using it to
> tell the resumed kernel that it was tampered with. If so, NAK to that.
> Just abort.
>

The main job of trampoline page is using by boot kernel to forward
key to resume kernel for next hibernation cycle. The authorization result
is just an add on.

In resume process, the key will be loaded in early boot stage. Either in
EFI stub (EFI secure key type, be rejected by EFI subsystem) or initrd. On
the other hand, PCRs may capped after TPM trusted key be loaded. So resume
kernel has no chance to load or unseal key again. The only way to get the
unsealed/decrypted key is from boot kernel.

The trampoline page is an empty page that it's pre-reserved in snapshot image
when hibernation. When resuming, this trampoline page will be written by boot
kernel to fill unsealed key. Then resume kernel will check the trampoline
page to take the key. If the key is empty(zero), which means that boot
kernel didn't forward valid key because some reasons. Either the key is
broken (EFI key be erased) or userspace didn't enroll key to boot kernel.
That's why I check the zero key in code.

> You say “If the encryption key be guessed then the snapshot master key
> can also be grabbed from snapshot image.” This makes little sense. If
> the encryption key is guessed, the game is over. Just remove this
> patch — it seems like pure snake oil.
>

I agreed.

> As far as I can tell, there is only one reason that any of this needs
> to be in the kernel: if it’s all in user code, then we lose “lockdown”
> protection against compromised user code on a secure boot system. Is
> that, in fact, true?

Putting the logic to kernel has some benefit:

- If the kernel be signed and verified by boot loader in EFI secure boot
environment. Then the code can also be authenticated with kernel binary.

- Kernel can direct produce an encrypted/signed snapshot image. Either
kernel or userspace can keep the image. Userspace will not touch an
un-encrypted image.

Thanks a lot!
Joey Lee

2019-01-09 16:50:52

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 0/5 v2][RFC] Encryption and authentication for hibernate snapshot image

Am Mittwoch, 9. Januar 2019, 17:39:58 CET schrieb joeyli:

Hi joeyli,

>
> I am doing encrypt-then-MAC.

Note, this is what the authenc() AEAD cipher does.

Ciao
Stephan



2019-01-09 17:36:21

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

On Wed, Jan 09, 2019 at 11:17:45AM +0100, Stephan Mueller wrote:
> Am Mittwoch, 9. Januar 2019, 09:21:04 CET schrieb Eric Biggers:
>
> Hi Eric,
> >
> > FWIW, it's been very slow going since I've been working on other projects
> > and I also need to be very sure to get the API changes right, but I still
> > plan to change the KDF in fscrypt (a.k.a. ext4/f2fs/ubifs encryption) to
> > HKDF-SHA512 as part of a larger set of improvements to how fscrypt
> > encryption keys are managed. I sent the last patchset a year ago
> > (https://marc.info/?l=linux-fsdevel&m=150879493206257) but I'm working to
> > revive it. In the work-in-progress version in my git tree, this is the
> > commit that adds a HKDF implementation as fs/crypto/hkdf.c:
> > https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux.git/commit/?id=e8a78767131c9717ee838f0c4e307948d65a4427
> > It basically just wraps a crypto_shash for "hmac(sha512)".
> >
> > I'd be fine with using a common implementation instead, provided that it
> > gives the same functionality, including supporting user-specified salt and
> > application-specific info strings, and isn't slower or more complex to use.
> >
> > (This comment is solely on the tangential discussion about KDF
> > implementations; I've not looked at the hibernation image encryption stuff
> > yet.)
>
> Thanks for the clarification. I have started a generic HKDF implementation for
> the kernel crypto API which lead to the questions above. I would then also try
> to provide a HKDF proposal.
>
> To use the (H)KDF, I currently envision 2 calls apart from alloc/free. The
> following code would serve as an example.
>
> * Example without proper error handling:
> * char *keying_material = "\x00\x11\x22\x33\x44\x55\x66\x77";
> * char *label_context = "\xde\xad\xbe\xef\x00\xde\xad\xbe\xef";
> * kdf = crypto_alloc_rng(name, 0, 0);
> * crypto_rng_reset(kdf, keying_material, 8);
> * crypto_rng_generate(kdf, label_context, 9, outbuf, outbuflen);
>
> That hopefully should be simple enough.
>
> For HKDF, as mentioned, I would envision to use a struct instead of a char *
> for the label_context to communicate IKM, Salt, and the label/info
> information.
>
> Ciao
> Stephan
>

That would not meet my performance requirements as I want to precompute
HKDF-Extract, and then do HKDF-Expand many times. Also the HKDF-Expand part
should be thread-safe and not require allocating memory, especially not a whole
crypto_shash tfm every time.

So presumably with crypto_rng, crypto_rng_reset() would need to take the input
keyring material and salt and do HKDF-Extract (like my fscrypt_init_hkdf()), and
crypto_rng_generate() would need to take the application-specific info string
and do HKDF-Expand (like my fscrypt_hkdf_expand()).

It is ugly though. Please also consider just having simple crypto_hkdf_*()
helper functions which wrap a HMAC tfm along the lines of my patch, rather than
shoehorning it into the crypto_rng API.

- Eric

2019-01-09 18:36:31

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

>> On Jan 8, 2019, at 10:49 PM, James Bottomley <[email protected]> wrote:
>>
>> On Tue, 2019-01-08 at 17:43 -0800, Andy Lutomirski wrote:
>> [Adding Jarkko because this stuff relates to the TPM.]
>
>> Anyway, if we're talking about the TPM, it seems like the entire
>> "trusted key" mechanism in the kernel is missing the point. If I
>> want to encrypt something like a hibernation image on a machine with
>> a TPM, it makes essentially no sense to me that we would get a key
>> with a known raw value that is merely TPM-backed (i.e. the "trusted
>> key") and use that to decrypt the image. The right way to do it is
>> to the use the TPM as it was intended to be used: generate a single-
>> use key that protects the hibernation image and seal *that* directly
>> on the TPM, such that it can only be unsealed with appropriate PCR
>> values. Heck, we could even use one of the fancy NV counters such
>> that we *can't* decrypt the image later on. And using HMAC or any AE
>> construction the normal way is also wrong -- we should *hash* the
>> image and sign the hash directly on the TPM so that the restore code
>> can validate the PCR values that were in place when the hibernation
>> image was created. [0]
>
> Well, theoretically, trusted keys can be used for PCR sealed bundles,
> at least in 1.2 ... I'm not sure the 2.0 one actually works because you
> have to construct the policy session outside the kernel.

I suppose I should go read the 2.0 spec. I’ve read the 1.2 spec, but I
always assumed that 2.0 was essentially a superset of 1.2
functionality.

>> Presumably we should at least try to replay the PCR operations that
>> have occurred so that we can massage the PCRs into the same state
>> post-hibernation. Also, do we have any way for the kernel to sign
>> something with the TPM along with an attestation that the signature
>> was requested *by the kernel*? Something like a sub-hierarchy of
>> keys that the kernel explicitly prevents userspace from accessing?)
>
> We're just growing that now with the TPM asymmetric operations.
> Attesting that the kernel requested the signature is harder. The TPM
> can attest to log entries (as it does for the UEFI log and IMA) and it
> can certify keys, but that only proves they're TPM resident not who the
> requestor was. Effectively the latter is an assertion about who knows
> the key authority, which is hard to prove.

Can the kernel filter TPM 2.0 operations? If so, then a signature
that the kernel would have prevented user code from generating is de
facto an attestation that the kernel generated it (or that the kernel
was compromised, which is sort of equivalent).

>
>> [0] If you take some data, run it through an authenticated encryption
>> algorithm, and sign (key, nonce, tag), I think you're operating
>> outside of the accepted security definitions if you expect this to
>> guarantee that the data wasn't tampered with. I'm reasonably
>> confident that there are quite a few excellent AE algorithms that
>> completely fail if used this like this. In fact, pretty much all of
>> the modern fast ones probably fail. AE is for when the key is
>> *secret*.
>
> Well, I think here, if we were actually trying to solve the problem of
> proving the hibernated image were the same one we would need to prove
> some log of the kernel operation came to a particular value *after* the
> hibernated image were restored ... it's not really possible to
> condition key release which must occur before the restore on that
> outcome, so it strikes me we need more than a simple release bound to
> PCR values.

I’m not sure I follow. Here are the two properties I’d like to see:

1. If you have an encrypted hibernation image, the only thing you
should be able to do with it is to restore it. So only an actual Linux
kernel in hibernation restore mode ought to be able to restore it. We
get this if the image can only be read with appropriate PCRs and then
only by the kernel. This way, you can’t just read out secrets from
the image if you steal a laptop — you have to actually boot the thing.

2. You shouldn’t be able to create an intentionally corrupt image that
pwns you when you restore it unless you have already pwned the kernel.

Maybe the “kernel” bit in #1 can be relaxed to “root” without totally
defeating the purpose, but if some random non-root process that
happens to have access to /dev/tpm* can make a valid-looking TPM
image, then I think we fail. Limiting it to the kernel is only
dubiously better than limiting it to root until we implement lockdown,
in which case it's important.

#2 only really matters with lockdown.

I suppose that a good summary of my opinion is that there is no point
to kernel support for encrypted hibernation images until lockdown is
upstream.

--Andy

2019-01-09 20:05:52

by James Bottomley

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

On Wed, 2019-01-09 at 08:05 +0100, Stephan Mueller wrote:
> Am Mittwoch, 9. Januar 2019, 07:58:28 CET schrieb James Bottomley:
>
> Hi James,
>
> > On Wed, 2019-01-09 at 07:45 +0100, Stephan Mueller wrote:
> > > Am Mittwoch, 9. Januar 2019, 01:44:31 CET schrieb James
> > > Bottomley:
> > >
> > > Hi James,
> > >
> > > > Actually, it would be enormously helpful if we could reuse
> > > > these pieces for the TPM as well.
> > >
> > > Could you please help me understand whether the KDFs in TPM are
> > > directly usable as a standalone cipher primitive or does it go
> > > together with additional key generation operations?
> >
> > They're used as generators ... which means they deterministically
> > produce keys from what the TPM calls seeds so we can get crypto
> > agility of TPM 2.0 ... well KDFa does. KDFe is simply what NIST
> > recommends you do when using EC for a shared key agreement ... and
> > really we shouldn't be using ECDH in the kernel without it.
> >
>
> Thanks for clarifying. That would mean that indeed we would have
> hardware-provided KDF implementations that may be usable with the
> kernel crypto API.

Just on this point, the TPM doesn't actually provide any KDFa or e API,
so it can't be used for hardware acceleration (and even if it did, the
TPM is a pretty slow engine, so software would be faster anyway). We
need these algorithms in software because the TPM uses key agreements
derived from shared secrets to produce session encryption keys to
ensure confidentiality and integrity (HMAC key), so we establish the
shared secret then have to derive our key in software and the TPM
derives the same key internally and we use the shared derived key to
symmetrically encrypt and/or HMAC secret communications.

James


2019-01-09 20:13:48

by joeyli

[permalink] [raw]
Subject: Re: [PATCH 0/5 v2][RFC] Encryption and authentication for hibernate snapshot image

On Thu, Jan 10, 2019 at 12:39:58AM +0800, joeyli wrote:
> Hi Andy,
>
[...snip]
>
> Let's why I encrypt/decrypt data pages one by one, then I copy the
^^^^^^^ That's why

> encrypt/decrypt data from buffer page (only one buffer page reserved
> for encrypt/decrypt) to original page. I encreypt pages one by one, but
> I HMAC and verify the whole snapshot image by update mode.
>
[...snip]
>
> > Why are you manually supporting three different key types? Can’t you
> > just somehow support all key types? And shouldn’t you be verifying
>
> I only supported two key typs in my patch set, user defined key and
> TPM trusted key. The EFI secure boot did not accept by EFI subsystem.
^^^^^^^^^^^^^^^^^^^ EFI secure key
https://lkml.org/lkml/2018/8/5/10

Sorry for I produced too many typo when feeling sleepy...

Thanks a lot!
Joey Lee

2019-01-09 20:23:29

by joeyli

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

Hi James

On Tue, Jan 08, 2019 at 10:49:39PM -0800, James Bottomley wrote:
> On Tue, 2019-01-08 at 17:43 -0800, Andy Lutomirski wrote:
> > [Adding Jarkko because this stuff relates to the TPM.]
> >
> > On Tue, Jan 8, 2019 at 4:44 PM James Bottomley
> > <[email protected]> wrote:
> > >
> > > On Tue, 2019-01-08 at 15:54 -0800, Andy Lutomirski wrote:
> > > > > On Jan 7, 2019, at 11:09 PM, Stephan Mueller <smueller@chronox.
> > > > > de>
> > > > > wrote:
> > > > >
> > > > > Am Dienstag, 8. Januar 2019, 06:03:58 CET schrieb Herbert Xu:
> > > > >
> > > > > Hi Herbert,
> > > > >
> > > > > > Are we going to have multiple implementations for the same
> > > > > > KDF? If not then the crypto API is not a good fit. To
> > > > > > consolidate multiple implementations of the same KDF, simply
> > > > > > provide helpers for them.
> > > > >
> > > > > It is unlikely to have multiple implementations of a KDF.
> > > > > However, KDFs relate to hashes like block chaining modes to raw
> > > > > block ciphers. Thus a KDF can be applied with different hashes.
> > > > >
> > > > > My idea was to add template support to RNGs (because KDFs are
> > > > > effectively a type of RNG since they produce an arbitrary
> > > > > output from a fixed input). The KDFs would be a template
> > > > > wrapping hashes. For example, the CTR-KDF from SP800-108 could
> > > > > be instantiated like kdf-ctr(sha256).
> > > > >
> > > > >
> > > >
> > > > I think that, if the crypto API is going to grow a KDF facility,
> > > > it should be done right. Have a key type or flag or whatever that
> > > > says “this key may *only* be used to derive keys using such-and-
> > > > such algorithm”, and have a helper to derive a key. That helper
> > > > should take some useful parameters and mix them in:
> > > >
> > > > - What type of key is being derived? ECDSA signing key? HMAC
> > > > key? AES key?
> > > >
> > > > - Can user code access the derived key?
> > > >
> > > > - What is the key’s purpose? “Encrypt and authenticate a
> > > > hibernation image” would be a purpose.
> > > >
> > > > - Number of bytes.
> > > >
> > > > All of these parameters should be mixed in to the key derivation.
> > > >
> > > > Also, an AE key, even for AES+HMAC, should be just one derived
> > > > key. If you need 512 bits, ask for a 512-bit key, not two 256-
> > > > bit keys.
> > >
> > > Actually, it would be enormously helpful if we could reuse these
> > > pieces for the TPM as well. It has two KDFs: KDFa, which is the
> > > CTR-KDF from SP800-108 and KDFe which is the SP800-56A KDF for
> > > elliptic curve one pass Diffie Hellman, so if we're going to do the
> > > former, I'd really like the latter as well.
> > >
> > > The way the TPM parametrises input to both KDFs is
> > >
> > > (hashAlg, key, label, contextU, contextV, bits)
> > >
> > > Where
> > >
> > > hashAlg = the hash algorithm used as the PRF
> > > key = the input parameter of variable bit size or
> > > the x co-ordinate of the shared point
> > > label = An ASCII string representing the use
> > > contextU = public input U
> > > contextV = public input V
> > > bits = number of output bits
> > >
> > > Is that a good enough parametrisation (not the only way you
> > > distinguish uses is with the label, which is not
> > > recoverable)? ContextU and ContextV are simply concatenated to
> > > form the full Context of SP800-108, but we tend to need two
> > > separate inputs (for KDFe they're the public x co-ordinates of the
> > > points of the two inputs to ECDH for instance; in KDFa they're
> > > usually the local and remote nonces).
> > >
> > > The labels for TPM usage are things like "INTEGRITY" for HMAC keys
> > > or "CFB" when generating an aes128-cfb session key. For KDFe, the
> > > tpm seems to like the label "SECRET". Although the TPM specifies
> > > fixed short strings for the label, nothing prevents them being
> > > longer like the purpose you state above (essentially we could mix
> > > purpose, use and key type into the label and the contexts).
> > >
> >
> > That really ought to cover anything the kernel needs.
> >
> > But can you explain what's up with with KDFe? The obvious searches
> > end up with just warnings that the US currently has no government :(
>
> You mean you can't find SP100-56A because NIST is a government entity
> and it's discontinued its website because of the government shutdown?
> No idea, I only live here, you'll have to ask a real American.
>
> ACM does have a copy:
>
> http://delivery.acm.org/10.1145/2210000/2206270/SP800-56A_Revision1_Mar08-2007.pdf?ip=50.35.68.20&id=2206270&acc=OPEN&key=4D4702B0C3E38B35%2E4D4702B0C3E38B35%2E4D4702B0C3E38B35%2E6D218144511F3437&__acm__=1546993111_ed9c8bd24b2f838c829d428aac7f5d71
>
> > Anyway, if we're talking about the TPM, it seems like the entire
> > "trusted key" mechanism in the kernel is missing the point. If I
> > want to encrypt something like a hibernation image on a machine with
> > a TPM, it makes essentially no sense to me that we would get a key
> > with a known raw value that is merely TPM-backed (i.e. the "trusted
> > key") and use that to decrypt the image. The right way to do it is
> > to the use the TPM as it was intended to be used: generate a single-
> > use key that protects the hibernation image and seal *that* directly
> > on the TPM, such that it can only be unsealed with appropriate PCR
> > values. Heck, we could even use one of the fancy NV counters such
> > that we *can't* decrypt the image later on. And using HMAC or any AE
> > construction the normal way is also wrong -- we should *hash* the
> > image and sign the hash directly on the TPM so that the restore code
> > can validate the PCR values that were in place when the hibernation
> > image was created. [0]
>
> Well, theoretically, trusted keys can be used for PCR sealed bundles,
> at least in 1.2 ... I'm not sure the 2.0 one actually works because you
> have to construct the policy session outside the kernel.
>
> > In other words, I think that a kernel-based encrypted hibernation
> > mechanism should create an image like this:
> >
> > - wrapped key
> > - instructions, if needed, for unwrapping
>
> This sounds like the format we use for TPM resident keys, except it
> would protect a TPM unseal bundle:
>
> https://git.kernel.org/pub/scm/linux/kernel/git/jejb/openssl_tpm2_engine.git/tree/tpm2-asn.h
>
> > - hash of the entire image except the hash and signature fields
> > - signature of the hash
> >
> > and the remainder is a regular hiberation image that is encrypted
> > against the key. No AE is needed -- just encryption. And there's no
> > trampoline, no weird per-page hashing, etc. Of course, this also
> > means that someone needs to audit the hibernation restore code to
> > make sure that there's no way for a malicious image to gain code
> > execution over the restoring kernel before the verification even
> > runs. Or some much more complicated hash can be used that supports
> > incremental verification.
> >
> >
> > (Also, do we have a sensible story of how the TPM interacts with
> > hibernation at all?
>
> Not really, no ... there is a TPM patch for LUKS, but trusted keys are
> unused within the kernel.
>

The trusted key has be used to produce encrypted key for EVM:
https://sourceforge.net/p/linux-ima/wiki/Home/#creating-trusted-and-evm-encrypted-keys

> > Presumably we should at least try to replay the PCR operations that
> > have occurred so that we can massage the PCRs into the same state
> > post-hibernation. Also, do we have any way for the kernel to sign
> > something with the TPM along with an attestation that the signature
> > was requested *by the kernel*? Something like a sub-hierarchy of
> > keys that the kernel explicitly prevents userspace from accessing?)
>
> We're just growing that now with the TPM asymmetric operations.
> Attesting that the kernel requested the signature is harder. The TPM
> can attest to log entries (as it does for the UEFI log and IMA) and it
> can certify keys, but that only proves they're TPM resident not who the
> requestor was. Effectively the latter is an assertion about who knows
> the key authority, which is hard to prove.
>
> > [0] If you take some data, run it through an authenticated encryption
> > algorithm, and sign (key, nonce, tag), I think you're operating
> > outside of the accepted security definitions if you expect this to
> > guarantee that the data wasn't tampered with. I'm reasonably
> > confident that there are quite a few excellent AE algorithms that
> > completely fail if used this like this. In fact, pretty much all of
> > the modern fast ones probably fail. AE is for when the key is
> > *secret*.
>
> Well, I think here, if we were actually trying to solve the problem of
> proving the hibernated image were the same one we would need to prove
> some log of the kernel operation came to a particular value *after* the
> hibernated image were restored ... it's not really possible to
> condition key release which must occur before the restore on that
> outcome, so it strikes me we need more than a simple release bound to
> PCR values.
>

hm... I am studying your information. But I have a question...

If PCR is not capped and the root be compromised, is it possible that a
sealed bundle also be compromised?

Is it possible that kernel can produce a sealed key with PCR by TPM when
booting? Then kernel caps a PCR by a constant value before the root is
available for userland. Then the sealed key can be exposed to userland
or be attached on hibernate image. Even the root be compromised, the TPM
trusted key is still secure.

Thanks a lot!
Joey Lee

2019-01-09 20:28:26

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

Am Mittwoch, 9. Januar 2019, 18:34:55 CET schrieb Eric Biggers:

Hi Eric,

> That would not meet my performance requirements as I want to precompute
> HKDF-Extract, and then do HKDF-Expand many times. Also the HKDF-Expand part
> should be thread-safe and not require allocating memory, especially not a
> whole crypto_shash tfm every time.
>
> So presumably with crypto_rng, crypto_rng_reset() would need to take the
> input keyring material and salt and do HKDF-Extract (like my
> fscrypt_init_hkdf()), and crypto_rng_generate() would need to take the
> application-specific info string and do HKDF-Expand (like my
> fscrypt_hkdf_expand()).

Great, that was the idea I had in mind as well. Maybe the example was not
right to convey that. Let me work on that.
>
> It is ugly though. Please also consider just having simple crypto_hkdf_*()
> helper functions which wrap a HMAC tfm along the lines of my patch, rather
> than shoehorning it into the crypto_rng API.
>
> - Eric



Ciao
Stephan



2019-01-09 20:49:24

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [PATCH 0/5 v2][RFC] Encryption and authentication for hibernate snapshot image

On Wed, Jan 9, 2019 at 8:40 AM joeyli <[email protected]> wrote:
>
> Hi Andy,
>
> Thanks for your review!
>
> On Tue, Jan 08, 2019 at 01:41:48PM -0800, Andy Lutomirski wrote:
> > > On Jan 7, 2019, at 9:37 AM, joeyli <[email protected]> wrote:
> > >
> > > Hi Pavel,
> > >
> > > Thanks for your review!
> > >
> > >> On Sun, Jan 06, 2019 at 07:10:27PM +0100, Pavel Machek wrote:
> > >> Hi!
> > >>
> > >>> This patchset is the implementation of encryption and authentication
> > >>> for hibernate snapshot image. The image will be encrypted by AES and
> > >>> authenticated by HMAC.
> > >>
> > >> Ok, so you encrypt.
> > >
> > > Yes, encryption and authentication.
> > >
> > >>> The hibernate function can be used to snapshot memory pages to an image,
> > >>> then kernel restores the image to memory space in a appropriate time.
> > >>> There have secrets in snapshot image and cracker may modifies it for
> > >>> hacking system. Encryption and authentication of snapshot image can protect
> > >>> the system.
> > >>>
> > >>> Hibernate function requests the master key through key retention service.
> > >>> The snapshot master key can be a trusted key or a user defined key. The
> > >>> name of snapshot master key is fixed to "swsusp-kmk". User should loads
> > >>> swsusp-kmk to kernel by keyctl tool before the hibernation resume.
> > >>> e.g. The swsusp-kmk must be loaded before systemd-hibernate-resume
> > >>
> > >> But if userspace has a key, encryption is useless against root.
> > >
> > > Yes, but this concern is not only for hibernation encryption. This patch
> > > set does not provide solution against this concern.
> > >
> > > The purpose of this patch set is to encrypt and authenticate hibernate
> > > snapshot image in kernel space. It also requests key through keyring
> > > mechanism. Which means that we can easy to adapt to new key type from
> > > keyring in the future.
> > >
> > > Currently TPM trusted key or user defined key types are not against
> > > root. Even using the TPM trusted key, it still can be unsealed by root
> > > before the PCRs be capped (unless we capped PCRs in kernel).
> > >
> > > My solution for keeping the secret by kernel is the EFI secure key type:
> > > https://lkml.org/lkml/2018/8/5/31
> > >
> > > But the EFI boot variable doesn't design for keeping secret, so Windows
> > > and OEM/ODM do not use boot variable to keep secret. So this idea can
> > > not be accepted. We must think other key type against root.
> > >
> > >>> The TPM trusted key type is preferred to be the master key. But user
> > >>> defined key can also be used for testing or when the platform doesn't
> > >>> have TPM. User must be aware that the security of user key relies on
> > >>> user space. If the root account be compromised, then the user key will
> > >>> easy to be grabbed.
> > >>
> > >> In the TPM case, does userland have access to the key?
> > >
> > > In the TPM case, userland can only touch the sealed key blob. So userland
> > > doesn't know the real secret. But it has risk that root unseals the key
> > > before PCRs be capped.
> > >
> > >> Please explain your security goals.
> > >
> > > My security goals:
> > >
> > > - Encrypt and authicate hibernate snapshot image in kernel space. Userspace
> > > can only touch an encrypted and signed snapshot image.
> >
> > Signed?
> >
> > I’m not entirely convinced that the keyring mechanism is what you
> > want. ISTM that there are two goals here:
> >
> > a) Encryption: it should be as hard as can reasonably be arranged to
> > extract secrets from a hibernation image.
> >
> > b) Authentication part 1: it should not be possible for someone in
> > possession of a turned-off machine to tamper with the hibernation
> > image such that the image, when booted, will leak its secrets. This
> > should protect against attackers who don’t know the encryption key.
> >
> > c) Authentication part 2: it should be to verify, to the extent
> > practical, that the image came from the same machine and was really
> > created using hibernation. Or maybe by the same user.
> >
> > For (a) and (b), using an AE mode where the key is protected in some
> > reasonable way. Joey, why are you using HMAC? Please tell me you’re
> > at least doing encrypt-then-MAC. But why not use a real AE mode like
> > AES-GCM?
>
> The reason for using HMAC is the history for development. My first patch
> set is only for hibernate authentication. Then I added encryption code on
> top of my authentication patches in last version.
>
> I am doing encrypt-then-MAC. My code ecrypts each page by AES then HMAC
> whole snapshot image. I feed encrypted data pages one by one to
> crypto_shash_update() API for calculating the hash for whole image.

...

I think you should write down a clear description of the data format.
A general problem with crypto is that the fact that it appears to work
doesn't mean it's secure at all, and it's very hard to follow the
code. Especially in Linux using the crypto API -- code using the
crypto API tends to be mostly boilerplate.

>
> >
> >
> > I reviewed the code a bit. Here are some thoughts:
> >

>
> > You’re explicitly checking that it’s not zero, and I don’t see why.
> >
>
> That's because I use trampoline page to forward the key from boot
> kernel to resume kernel for next hibernation cycle. When resuming,
> the empty key means that the boot kernel didn't forward valid key
> to resume kernel. Then the key initial failed, hibernation can not
> be triggered in next cycle.
>

This seems like a poor design. If you need some indication of "there
is no key", then implement that. Don't use a special all-zero value
to mean something special.

>
> > the acceptable usage of trusted keys?
> >
>
> Sorry for I didn't capture the meaning of "acceptable usage". The trusted
> key already be unsealed by TPM when the key be enrolled by keyctl tool.
> So my code just extract the unsealed key data (the random number) for
> encryption.

If someone creates a trusted key that is used for asymmetric crypto or
perhaps a trusted key that is intended to be used for, say, an HMAC
key, you should not also use it to derive hibernation keys. This is
what I mean by "acceptable usage".

>
> > You are using a non-ephemeral key and generating a fresh IV each time.
> > This is probably okay, but it’s needlessly fragile. Just generate an
> > entirely fresh key each time, please. You also seem to be doing
> > encrypt-and-MAC, which is not generally considered acceptable. And
>
> I have thought about using one time key before. But that means I need
> attach the key with snapshot image. Keyring API doesn't provide interface
> for other kernel subsystem to feed an encrypted key blob, so I choice
> to use non-ephemeral key that's enrolled by userland through keyctl. I
> can change to one time key if keyring subsystem allows to be modified.

I don't see what this has to do with the keyring API. If you want a
one time key in the hibernation code, generate a one-time key in the
hibernation code and wrap the key accordingly.


>
> The encrypt-and-MAC is not acceptable? I am OK to change to other way
> just I hope that I can encrypt/decrypt page one by one for the reason
> that I just mentioned.
>
> > you’re not authenticating everything — just the data. This seems very
> > weak.
> >
>
> Is it too weak? Why?
>
> There have three parts of an snapshot image:
> image header :: page table pages :: data pages
>
> The real data are in data pages. I thought that encrypt/hmac data pages
> can protect sensitive data in pages and also avoid those pages be arbitrary
> modified. IMHO arbitrary modifing page table pages is easy to cause
> resume kernel crashed.

First you need to define *why* you're authenticating anything. But I
would guess that under any reasonable security model, a tampered-with
image should not be able to take over the system. And, if you don't
protect the page tables, then it's probably quite easy to tamper with
an image such that the HMAC is unaffected but the image takes over the
system.

I think that, if this series goes in at all, it should authenticate
the *entire* image. (But you still have to define what "authentic"
even means. "It passed an HMAC check" is not a good definition.)

>
> > Can you explain the trampoline? It looks like you are using it to
> > tell the resumed kernel that it was tampered with. If so, NAK to that.
> > Just abort.
> >
>
> The main job of trampoline page is using by boot kernel to forward
> key to resume kernel for next hibernation cycle. The authorization result
> is just an add on.
>
> In resume process, the key will be loaded in early boot stage. Either in
> EFI stub (EFI secure key type, be rejected by EFI subsystem) or initrd. On
> the other hand, PCRs may capped after TPM trusted key be loaded. So resume
> kernel has no chance to load or unseal key again. The only way to get the
> unsealed/decrypted key is from boot kernel.
>
> The trampoline page is an empty page that it's pre-reserved in snapshot image
> when hibernation. When resuming, this trampoline page will be written by boot
> kernel to fill unsealed key. Then resume kernel will check the trampoline
> page to take the key. If the key is empty(zero), which means that boot
> kernel didn't forward valid key because some reasons. Either the key is
> broken (EFI key be erased) or userspace didn't enroll key to boot kernel.
> That's why I check the zero key in code.

I don't follow this at all. After reading the code, I couldn't figure
out which stage generates the trampoline, which stage reads the
trampoline, and where there are any keys at all in the trampoline.

There are effectively three kernels:

a) The kernel that gets hibernated and generates the hibernation
image. This kernel obviously has access to all the keying
information, although some of that information may actually be
resident on the TPM and not in memory.

b) The boot kernel that reads the hibernation image.

c) The resumed kernel. This kernel has the same code as (a) but
possibly different PCRs.

If the encryption is going to protect anything at all, then kernel (b)
needs to have a way to get the key, which is presumably done using the
TPM, since it's not obvious to me that there's any other credible way
to do it. (A passphrase could also make sense, but then you'd just
put the hibernation image on a dm-crypt volume and the problem is
solved.) Kernel (c) shouldn't need to do any crypto, since, by the
time any code in kernel (c) executes at all, you've already decrypted
everything and you've effectively committed to trusting the
hibernation image.

If something goes wrong when you hibernate a second time, this means
that the first hibernation screwed up and corrupted something, e.g.
PCRs. That should be fixed independently of this series IMO.

So I still don't get what the trampoline is for.

2019-01-09 21:05:02

by James Bottomley

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

On Wed, 2019-01-09 at 10:34 -0800, Andy Lutomirski wrote:
> > > On Jan 8, 2019, at 10:49 PM, James Bottomley <James.Bottomley@han
> > > senpartnership.com> wrote:
> > >
> > > On Tue, 2019-01-08 at 17:43 -0800, Andy Lutomirski wrote:
> > > [Adding Jarkko because this stuff relates to the TPM.]
> > > Anyway, if we're talking about the TPM, it seems like the entire
> > > "trusted key" mechanism in the kernel is missing the point. If I
> > > want to encrypt something like a hibernation image on a machine
> > > with a TPM, it makes essentially no sense to me that we would get
> > > a key with a known raw value that is merely TPM-backed (i.e. the
> > > "trusted key") and use that to decrypt the image. The right way
> > > to do it is to the use the TPM as it was intended to be used:
> > > generate a single-use key that protects the hibernation image and
> > > seal *that* directly on the TPM, such that it can only be
> > > unsealed with appropriate PCR values. Heck, we could even use
> > > one of the fancy NV counters such that we *can't* decrypt the
> > > image later on. And using HMAC or any AE construction the normal
> > > way is also wrong -- we should *hash* the image and sign the hash
> > > directly on the TPM so that the restore code can validate the PCR
> > > values that were in place when the hibernation image was
> > > created. [0]
> >
> > Well, theoretically, trusted keys can be used for PCR sealed
> > bundles, at least in 1.2 ... I'm not sure the 2.0 one actually
> > works because you have to construct the policy session outside the
> > kernel.
>
> I suppose I should go read the 2.0 spec. I’ve read the 1.2 spec, but
> I always assumed that 2.0 was essentially a superset of 1.2
> functionality.

It was sold as an incremental upgrade, but in practice, adding crypto
agility and flexible policy (the main 2.0 enhancements) meant that the
API is redically different.

> > > Presumably we should at least try to replay the PCR operations
> > > that have occurred so that we can massage the PCRs into the same
> > > state post-hibernation. Also, do we have any way for the kernel
> > > to sign something with the TPM along with an attestation that the
> > > signature was requested *by the kernel*? Something like a sub-
> > > hierarchy of keys that the kernel explicitly prevents userspace
> > > from accessing?)
> >
> > We're just growing that now with the TPM asymmetric operations.
> > Attesting that the kernel requested the signature is harder. The
> > TPM can attest to log entries (as it does for the UEFI log and IMA)
> > and it can certify keys, but that only proves they're TPM resident
> > not who the requestor was. Effectively the latter is an assertion
> > about who knows the key authority, which is hard to prove.
>
> Can the kernel filter TPM 2.0 operations?

There is a proposal for this, but the proposal is on the operations by
type, not the content. Meaning we can't really police loading and
using a kernel key because we can't distinguish whether any key is a
kernel key. We can't forbid all key load operations to non-kernel
because that removes most of the TPM usefulness as a keystore.

> If so, then a signature that the kernel would have prevented user
> code from generating is de facto an attestation that the kernel
> generated it (or that the kernel was compromised, which is sort of
> equivalent).

The TPM's idea of this is it polices by authorization. Now one of the
things we can do here is add what's called locality based
authorization. we have three non-uefi localities to play with and we
could enforce walling one off for the kernel only to use, so a kernel
key could come with a policy requiring use of the kernel locality for
use of the key. That would give you an effective guarantee that only
the kernel could use this key. Note the enforcement of locality would
require a key policy, which is easy for TPM 1.2, but requires the use
of a policy session for TPM 2.0 which means we'd have to improve our
policy session handling.

> > > [0] If you take some data, run it through an authenticated
> > > encryption algorithm, and sign (key, nonce, tag), I think you're
> > > operating outside of the accepted security definitions if you
> > > expect this to guarantee that the data wasn't tampered with. I'm
> > > reasonably confident that there are quite a few excellent AE
> > > algorithms that completely fail if used this like this. In fact,
> > > pretty much all of the modern fast ones probably fail. AE is for
> > > when the key is *secret*.
> >
> > Well, I think here, if we were actually trying to solve the problem
> > of proving the hibernated image were the same one we would need to
> > prove some log of the kernel operation came to a particular value
> > *after* the hibernated image were restored ... it's not really
> > possible to condition key release which must occur before the
> > restore on that outcome, so it strikes me we need more than a
> > simple release bound to PCR values.
>
> I’m not sure I follow. Here are the two properties I’d like to see:
>
> 1. If you have an encrypted hibernation image, the only thing you
> should be able to do with it is to restore it. So only an actual
> Linux kernel in hibernation restore mode ought to be able to restore
> it. We get this if the image can only be read with appropriate PCRs
> and then only by the kernel. This way, you can’t just read out
> secrets from the image if you steal a laptop — you have to actually
> boot the thing.

Right, this we can do and if you use a TPM sealed encryption key, you
can guarantee the image will only restore on the same physical system.
You don't need PCRs for this, just the TPM and the locality
enforcement.

Note if someone has your laptop and the ability to boot their own
kernels, they could always corrupt the kernel into decrypting the image
or giving you the unsealed key, but there's no real way of preventing
that even with PCR sealing or lockdown, so the basis for the threat
model is very much my laptop in my possession running my kernel.

> 2. You shouldn’t be able to create an intentionally corrupt image
> that pwns you when you restore it unless you have already pwned the
> kernel.

So here there's a problem: the policy stated above governs key *use*
not key creation, so anyone can create a key that has a locality
restriction. The way to guarantee that the key was created by
something that has access to the locality is to have a parent key with
a locality use policy (key creation requires parent key use
authorization). Which means every system would have to create a
persistent parent key for the kernel to use (the kernel could do this
and it could be made NV resident for persistence, so it's not
impossible, just complicated).

> Maybe the “kernel” bit in #1 can be relaxed to “root” without totally
> defeating the purpose, but if some random non-root process that
> happens to have access to /dev/tpm* can make a valid-looking TPM
> image, then I think we fail. Limiting it to the kernel is only
> dubiously better than limiting it to root until we implement
> lockdown, in which case it's important.
>
> #2 only really matters with lockdown.
>
> I suppose that a good summary of my opinion is that there is no point
> to kernel support for encrypted hibernation images until lockdown is
> upstream.

I really don't think lockdown helps. If we implement locality
isolation for the kernels use of keys, a properly functioning kernel
isn't going to be tricked into releasing one of its keys to userspace.
A buggy kernel might be exploited to cause it to give one up but that
would happen irrespective of lockdown and, of course, all bets are off
if the attacker can boot their own kernel.

James


2019-01-09 21:07:13

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

On Wed, Jan 9, 2019 at 11:46 AM James Bottomley
<[email protected]> wrote:
>
> On Wed, 2019-01-09 at 10:34 -0800, Andy Lutomirski wrote:
> > > > On Jan 8, 2019, at 10:49 PM, James Bottomley <James.Bottomley@han
> > > > senpartnership.com> wrote:
> > > >

> > If so, then a signature that the kernel would have prevented user
> > code from generating is de facto an attestation that the kernel
> > generated it (or that the kernel was compromised, which is sort of
> > equivalent).
>
> The TPM's idea of this is it polices by authorization. Now one of the
> things we can do here is add what's called locality based
> authorization. we have three non-uefi localities to play with and we
> could enforce walling one off for the kernel only to use, so a kernel
> key could come with a policy requiring use of the kernel locality for
> use of the key. That would give you an effective guarantee that only
> the kernel could use this key. Note the enforcement of locality would
> require a key policy, which is easy for TPM 1.2, but requires the use
> of a policy session for TPM 2.0 which means we'd have to improve our
> policy session handling.

Hmm. On an *extremely* brief reading of what "locality" means, this
seems entirely sensible.

> >
> > I’m not sure I follow. Here are the two properties I’d like to see:
> >
> > 1. If you have an encrypted hibernation image, the only thing you
> > should be able to do with it is to restore it. So only an actual
> > Linux kernel in hibernation restore mode ought to be able to restore
> > it. We get this if the image can only be read with appropriate PCRs
> > and then only by the kernel. This way, you can’t just read out
> > secrets from the image if you steal a laptop — you have to actually
> > boot the thing.
>
> Right, this we can do and if you use a TPM sealed encryption key, you
> can guarantee the image will only restore on the same physical system.
> You don't need PCRs for this, just the TPM and the locality
> enforcement.
>
> Note if someone has your laptop and the ability to boot their own
> kernels, they could always corrupt the kernel into decrypting the image
> or giving you the unsealed key, but there's no real way of preventing
> that even with PCR sealing or lockdown, so the basis for the threat
> model is very much my laptop in my possession running my kernel.

I'm not entirely sure I agree. With a TPM-aware bootloader, it really
ought to be possible to seal to PCRs such that a corrupted kernel
can't restore the image. Obviously a *compromised* but otherwise
valid kernel will be able to restore the image.

But this is all barking up the wrong tree. If you want your laptop to
resist physical theft such that whoever stole it can boot it but can't
directly extract any data, you want to use dm-crypt (or, haha, OPAL,
but I just read a paper about some people who evaluated a bunch of
drives and had a very hard time finding one that actually implemented
OPAL in a usefully secure manner). A LUKS replacement or wrapper that
does something intelligent with the TPM would be great. This kind of
thing IMO does not belong in hibernation.

IOW, I think we do get about as much as we would want if we just seal
with a locality that only allows kernel use and ignore PCRs entirely.
This makes it so that you need the ability to run ring 0 code to
decrypt the image, which means that we get all the nice lockdown
properties.

>
> > 2. You shouldn’t be able to create an intentionally corrupt image
> > that pwns you when you restore it unless you have already pwned the
> > kernel.
>
> So here there's a problem: the policy stated above governs key *use*
> not key creation, so anyone can create a key that has a locality
> restriction. The way to guarantee that the key was created by
> something that has access to the locality is to have a parent key with
> a locality use policy (key creation requires parent key use
> authorization). Which means every system would have to create a
> persistent parent key for the kernel to use (the kernel could do this
> and it could be made NV resident for persistence, so it's not
> impossible, just complicated).

Why does anything here need to be persistent? The kernel could create
a locality-restricted key on the fly, use it to sign and/or seal the
hibernation image, and write the wrapped key blob into the hibernation
image.

> I suppose that a good summary of my opinion is that there is no point
> > to kernel support for encrypted hibernation images until lockdown is
> > upstream.
>
> I really don't think lockdown helps. If we implement locality
> isolation for the kernels use of keys, a properly functioning kernel
> isn't going to be tricked into releasing one of its keys to userspace.
> A buggy kernel might be exploited to cause it to give one up but that
> would happen irrespective of lockdown and, of course, all bets are off
> if the attacker can boot their own kernel.
>

I'm not saying that lockdown helps. I'm saying that encrypting the
hibernation image in the kernel may be pointless until the kernel
supports lockdown. If we don't support lockdown, then user code can
encrypt the hibernation image all by itself. The code will be easier
to understand, more flexible, and won't require a kernel upgrade :)

Honestly, no one should be using resume= anyway. Any distro with
hibernation support worth its salt should support having the
hibernation image on a dm-crypt volume, in which case it *must*
support userspace-driven resume. Of course, my laptop that's been
upgraded through many Fedora revisions doesn't survive a
hibernate/resume cycle, but that's not the kernel's fault.

--Andy

P.S. One thing I do want to try is encrypted *swap*. The keying for
that is trivial -- the kernel can just make a key, store it in
ordinary kernel memory, and use it to encrypt and decrypt swap pages
as needed. Getting replay protection or authentication may be tricky
due to the need for metadata, but plain old AES-XTS or
HPolyChaChaNotSpeckAnymore would be entirely straightforward and would
get 90% of the benefit. Sure, swap could live on dm-crypt too, but
that's an administration mess, and offering a strong and cheap
alternative to mlock() for crypto programs to protect their secrets
would be fantastic and encrypted swap plus some API to verify that
anonymous memory is, in fact, backed by encrypted swap would do the
job.

2019-01-09 22:52:56

by James Bottomley

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

On Wed, 2019-01-09 at 12:12 -0800, Andy Lutomirski wrote:
> On Wed, Jan 9, 2019 at 11:46 AM James Bottomley
> <[email protected]> wrote:

[...]
> > > I’m not sure I follow. Here are the two properties I’d like to
> > > see:
> > >
> > > 1. If you have an encrypted hibernation image, the only thing you
> > > should be able to do with it is to restore it. So only an actual
> > > Linux kernel in hibernation restore mode ought to be able to
> > > restore it. We get this if the image can only be read with
> > > appropriate PCRs and then only by the kernel. This way, you
> > > can’t just read out secrets from the image if you steal a laptop
> > > — you have to actually boot the thing.
> >
> > Right, this we can do and if you use a TPM sealed encryption key,
> > you can guarantee the image will only restore on the same physical
> > system. You don't need PCRs for this, just the TPM and the locality
> > enforcement.
> >
> > Note if someone has your laptop and the ability to boot their own
> > kernels, they could always corrupt the kernel into decrypting the
> > image or giving you the unsealed key, but there's no real way of
> > preventing that even with PCR sealing or lockdown, so the basis for
> > the threat model is very much my laptop in my possession running my
> > kernel.
>
> I'm not entirely sure I agree. With a TPM-aware bootloader, it
> really ought to be possible to seal to PCRs such that a corrupted
> kernel can't restore the image. Obviously a *compromised* but
> otherwise valid kernel will be able to restore the image.

It is possible to seal the key so that only the same booted kernel can
restore the image, yes. One of the measurements that goes into the
boot log is the hash of the kernel and you can seal to this value ...
obviously if you upgrade your kernel RPM (or shim or grub) this value
changes and you'd lose the ability to restore the hibernated image, but
since the image is very kernel specific, that's probably OK.

> But this is all barking up the wrong tree. If you want your laptop
> to resist physical theft such that whoever stole it can boot it but
> can't directly extract any data, you want to use dm-crypt (or, haha,
> OPAL, but I just read a paper about some people who evaluated a bunch
> of drives and had a very hard time finding one that actually
> implemented OPAL in a usefully secure manner). A LUKS replacement or
> wrapper that does something intelligent with the TPM would be
> great. This kind of thing IMO does not belong in hibernation.

Right, so the simplest way of doing this is to save the image on an
encrypted partition or filesystem which must be unlocked by a password
on boot.

> IOW, I think we do get about as much as we would want if we just seal
> with a locality that only allows kernel use and ignore PCRs entirely.
> This makes it so that you need the ability to run ring 0 code to
> decrypt the image, which means that we get all the nice lockdown
> properties.

Yes, it's a useful balance of security and ease of implementation, I
think.

> > > 2. You shouldn’t be able to create an intentionally corrupt image
> > > that pwns you when you restore it unless you have already pwned
> > > the kernel.
> >
> > So here there's a problem: the policy stated above governs key
> > *use* not key creation, so anyone can create a key that has a
> > locality restriction. The way to guarantee that the key was
> > created by something that has access to the locality is to have a
> > parent key with a locality use policy (key creation requires parent
> > key use authorization). Which means every system would have to
> > create a persistent parent key for the kernel to use (the kernel
> > could do this and it could be made NV resident for persistence, so
> > it's not impossible, just complicated).
>
> Why does anything here need to be persistent? The kernel could
> create a locality-restricted key on the fly, use it to sign and/or
> seal the hibernation image, and write the wrapped key blob into the
> hibernation image.

Yes, but so could I as a malicious user with a desire to create a bad
hibernation image. I could do it entirely in userspace. I need access
to the TPM to seal the key, but I'd get that: All you see in-kernel is
a single policy sha256 sum and you can't reverse that back to the
actual policy, so the kernel has no idea what policy is being sealed
and so can't police the policy.

In order to get the security that only the kernel created the
hibernation key, you need to have a key parent with a policy that only
allows in-kernel locality use so you know the child key was created by
the kernel and nothing else.

> > I suppose that a good summary of my opinion is that there is no
> point
> > > to kernel support for encrypted hibernation images until lockdown
> > > is upstream.
> >
> > I really don't think lockdown helps. If we implement locality
> > isolation for the kernels use of keys, a properly functioning
> > kernel isn't going to be tricked into releasing one of its keys to
> > userspace. A buggy kernel might be exploited to cause it to give
> > one up but that would happen irrespective of lockdown and, of
> > course, all bets are off if the attacker can boot their own kernel.
> >
>
> I'm not saying that lockdown helps. I'm saying that encrypting the
> hibernation image in the kernel may be pointless until the kernel
> supports lockdown. If we don't support lockdown, then user code can
> encrypt the hibernation image all by itself. The code will be easier
> to understand, more flexible, and won't require a kernel upgrade :)

Well, protection of at-rest images is one reason, but I accept that
simply writing them to dm-crypt storage is a better way of doing this
and solves the key problem nicely.

James

> Honestly, no one should be using resume= anyway. Any distro with
> hibernation support worth its salt should support having the
> hibernation image on a dm-crypt volume, in which case it *must*
> support userspace-driven resume. Of course, my laptop that's been
> upgraded through many Fedora revisions doesn't survive a
> hibernate/resume cycle, but that's not the kernel's fault.
>
> --Andy
>
> P.S. One thing I do want to try is encrypted *swap*. The keying for
> that is trivial -- the kernel can just make a key, store it in
> ordinary kernel memory, and use it to encrypt and decrypt swap pages
> as needed. Getting replay protection or authentication may be tricky
> due to the need for metadata, but plain old AES-XTS or
> HPolyChaChaNotSpeckAnymore would be entirely straightforward and
> would
> get 90% of the benefit. Sure, swap could live on dm-crypt too, but
> that's an administration mess, and offering a strong and cheap
> alternative to mlock() for crypto programs to protect their secrets
> would be fantastic and encrypted swap plus some API to verify that
> anonymous memory is, in fact, backed by encrypted swap would do the
> job.
>


2019-01-09 22:58:49

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

Hi!

> > > Note if someone has your laptop and the ability to boot their own
> > > kernels, they could always corrupt the kernel into decrypting the
> > > image or giving you the unsealed key, but there's no real way of
> > > preventing that even with PCR sealing or lockdown, so the basis for
> > > the threat model is very much my laptop in my possession running my
> > > kernel.
> >
> > I'm not entirely sure I agree. With a TPM-aware bootloader, it
> > really ought to be possible to seal to PCRs such that a corrupted
> > kernel can't restore the image. Obviously a *compromised* but
> > otherwise valid kernel will be able to restore the image.
>
> It is possible to seal the key so that only the same booted kernel can
> restore the image, yes. One of the measurements that goes into the
> boot log is the hash of the kernel and you can seal to this value ...
> obviously if you upgrade your kernel RPM (or shim or grub) this value
> changes and you'd lose the ability to restore the hibernated image, but
> since the image is very kernel specific, that's probably OK.

Non-ancient kernels actually support hibernation by one kernel and
restore by another one.

But yes, normally it is same kernel binary doing hibernation and
restore.

Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html


Attachments:
(No filename) (1.40 kB)
signature.asc (188.00 B)
Digital signature
Download all attachments

2019-01-10 15:16:20

by joeyli

[permalink] [raw]
Subject: Re: [PATCH 0/5 v2][RFC] Encryption and authentication for hibernate snapshot image

On Wed, Jan 09, 2019 at 10:47:42AM -0800, Andy Lutomirski wrote:
> On Wed, Jan 9, 2019 at 8:40 AM joeyli <[email protected]> wrote:
> >
> > Hi Andy,
> >
> > Thanks for your review!
> >
> > On Tue, Jan 08, 2019 at 01:41:48PM -0800, Andy Lutomirski wrote:
> > > > On Jan 7, 2019, at 9:37 AM, joeyli <[email protected]> wrote:
> > > >
> > > > Hi Pavel,
> > > >
> > > > Thanks for your review!
> > > >
> > > >> On Sun, Jan 06, 2019 at 07:10:27PM +0100, Pavel Machek wrote:
> > > >> Hi!
> > > >>
> > > >>> This patchset is the implementation of encryption and authentication
> > > >>> for hibernate snapshot image. The image will be encrypted by AES and
> > > >>> authenticated by HMAC.
> > > >>
> > > >> Ok, so you encrypt.
> > > >
> > > > Yes, encryption and authentication.
> > > >
> > > >>> The hibernate function can be used to snapshot memory pages to an image,
> > > >>> then kernel restores the image to memory space in a appropriate time.
> > > >>> There have secrets in snapshot image and cracker may modifies it for
> > > >>> hacking system. Encryption and authentication of snapshot image can protect
> > > >>> the system.
> > > >>>
> > > >>> Hibernate function requests the master key through key retention service.
> > > >>> The snapshot master key can be a trusted key or a user defined key. The
> > > >>> name of snapshot master key is fixed to "swsusp-kmk". User should loads
> > > >>> swsusp-kmk to kernel by keyctl tool before the hibernation resume.
> > > >>> e.g. The swsusp-kmk must be loaded before systemd-hibernate-resume
> > > >>
> > > >> But if userspace has a key, encryption is useless against root.
> > > >
> > > > Yes, but this concern is not only for hibernation encryption. This patch
> > > > set does not provide solution against this concern.
> > > >
> > > > The purpose of this patch set is to encrypt and authenticate hibernate
> > > > snapshot image in kernel space. It also requests key through keyring
> > > > mechanism. Which means that we can easy to adapt to new key type from
> > > > keyring in the future.
> > > >
> > > > Currently TPM trusted key or user defined key types are not against
> > > > root. Even using the TPM trusted key, it still can be unsealed by root
> > > > before the PCRs be capped (unless we capped PCRs in kernel).
> > > >
> > > > My solution for keeping the secret by kernel is the EFI secure key type:
> > > > https://lkml.org/lkml/2018/8/5/31
> > > >
> > > > But the EFI boot variable doesn't design for keeping secret, so Windows
> > > > and OEM/ODM do not use boot variable to keep secret. So this idea can
> > > > not be accepted. We must think other key type against root.
> > > >
> > > >>> The TPM trusted key type is preferred to be the master key. But user
> > > >>> defined key can also be used for testing or when the platform doesn't
> > > >>> have TPM. User must be aware that the security of user key relies on
> > > >>> user space. If the root account be compromised, then the user key will
> > > >>> easy to be grabbed.
> > > >>
> > > >> In the TPM case, does userland have access to the key?
> > > >
> > > > In the TPM case, userland can only touch the sealed key blob. So userland
> > > > doesn't know the real secret. But it has risk that root unseals the key
> > > > before PCRs be capped.
> > > >
> > > >> Please explain your security goals.
> > > >
> > > > My security goals:
> > > >
> > > > - Encrypt and authicate hibernate snapshot image in kernel space. Userspace
> > > > can only touch an encrypted and signed snapshot image.
> > >
> > > Signed?
> > >
> > > I’m not entirely convinced that the keyring mechanism is what you
> > > want. ISTM that there are two goals here:
> > >
> > > a) Encryption: it should be as hard as can reasonably be arranged to
> > > extract secrets from a hibernation image.
> > >
> > > b) Authentication part 1: it should not be possible for someone in
> > > possession of a turned-off machine to tamper with the hibernation
> > > image such that the image, when booted, will leak its secrets. This
> > > should protect against attackers who don’t know the encryption key.
> > >
> > > c) Authentication part 2: it should be to verify, to the extent
> > > practical, that the image came from the same machine and was really
> > > created using hibernation. Or maybe by the same user.
> > >
> > > For (a) and (b), using an AE mode where the key is protected in some
> > > reasonable way. Joey, why are you using HMAC? Please tell me you’re
> > > at least doing encrypt-then-MAC. But why not use a real AE mode like
> > > AES-GCM?
> >
> > The reason for using HMAC is the history for development. My first patch
> > set is only for hibernate authentication. Then I added encryption code on
> > top of my authentication patches in last version.
> >
> > I am doing encrypt-then-MAC. My code ecrypts each page by AES then HMAC
> > whole snapshot image. I feed encrypted data pages one by one to
> > crypto_shash_update() API for calculating the hash for whole image.
>
> ...
>
> I think you should write down a clear description of the data format.

Hibernation allocates free pages for building snapshot image. Those free
pages are scattered in memory. So kernel marks those page numbers on a
bitmap to locate them. Because this image buffer is discontinuous, so I
use update mode hashing whole image.

> A general problem with crypto is that the fact that it appears to work
> doesn't mean it's secure at all, and it's very hard to follow the
> code. Especially in Linux using the crypto API -- code using the
> crypto API tends to be mostly boilerplate.
>

hm... Do you mean that the implementation of HMAC in crypto cannot be
trusted? I hope at least that I can trust the update mode for normal
hash like SHA256 or SHA512?

> >
> > >
> > >
> > > I reviewed the code a bit. Here are some thoughts:
> > >
>
> >
> > > You’re explicitly checking that it’s not zero, and I don’t see why.
> > >
> >
> > That's because I use trampoline page to forward the key from boot
> > kernel to resume kernel for next hibernation cycle. When resuming,
> > the empty key means that the boot kernel didn't forward valid key
> > to resume kernel. Then the key initial failed, hibernation can not
> > be triggered in next cycle.
> >
>
> This seems like a poor design. If you need some indication of "there
> is no key", then implement that. Don't use a special all-zero value
> to mean something special.
>

OK, I will use other way to detect the state of key.

> >
> > > the acceptable usage of trusted keys?
> > >
> >
> > Sorry for I didn't capture the meaning of "acceptable usage". The trusted
> > key already be unsealed by TPM when the key be enrolled by keyctl tool.
> > So my code just extract the unsealed key data (the random number) for
> > encryption.
>
> If someone creates a trusted key that is used for asymmetric crypto or
> perhaps a trusted key that is intended to be used for, say, an HMAC
> key, you should not also use it to derive hibernation keys. This is
> what I mean by "acceptable usage".
>

When keyring is producing encrypted key, the trusted key be used to
derive the encrypt key and authenticate key to encrypt and hmac sign
a encrypted key. So trusted key can be used in symmetric crypto.

> >
> > > You are using a non-ephemeral key and generating a fresh IV each time.
> > > This is probably okay, but it’s needlessly fragile. Just generate an
> > > entirely fresh key each time, please. You also seem to be doing
> > > encrypt-and-MAC, which is not generally considered acceptable. And
> >
> > I have thought about using one time key before. But that means I need
> > attach the key with snapshot image. Keyring API doesn't provide interface
> > for other kernel subsystem to feed an encrypted key blob, so I choice
> > to use non-ephemeral key that's enrolled by userland through keyctl. I
> > can change to one time key if keyring subsystem allows to be modified.
>
> I don't see what this has to do with the keyring API. If you want a
> one time key in the hibernation code, generate a one-time key in the
> hibernation code and wrap the key accordingly.
>

When using trusted key, the keyring subsystem helps to deal TPM. On the
other hand, I want that hibernate can easy to support other key types. e.g.
user defined key can also be used when debugging or development.

If hibernation direct deals with TPM to seal the hibernation key. Then
yes, we don't need keyring as the abstract layer for hibernation key.

>
> >
> > The encrypt-and-MAC is not acceptable? I am OK to change to other way
> > just I hope that I can encrypt/decrypt page one by one for the reason
> > that I just mentioned.
> >
> > > you’re not authenticating everything — just the data. This seems very
> > > weak.
> > >
> >
> > Is it too weak? Why?
> >
> > There have three parts of an snapshot image:
> > image header :: page table pages :: data pages
> >
> > The real data are in data pages. I thought that encrypt/hmac data pages
> > can protect sensitive data in pages and also avoid those pages be arbitrary
> > modified. IMHO arbitrary modifing page table pages is easy to cause
> > resume kernel crashed.
>
> First you need to define *why* you're authenticating anything. But I
> would guess that under any reasonable security model, a tampered-with
> image should not be able to take over the system. And, if you don't
> protect the page tables, then it's probably quite easy to tamper with
> an image such that the HMAC is unaffected but the image takes over the
> system.
>

OK, I will protect page tables and also fields in image header.

> I think that, if this series goes in at all, it should authenticate
> the *entire* image. (But you still have to define what "authentic"
> even means. "It passed an HMAC check" is not a good definition.)
>

I thought that the authentic of image means that the image should not
be tampered. I will try to protect whole image.

> >
> > > Can you explain the trampoline? It looks like you are using it to
> > > tell the resumed kernel that it was tampered with. If so, NAK to that.
> > > Just abort.
> > >
> >
> > The main job of trampoline page is using by boot kernel to forward
> > key to resume kernel for next hibernation cycle. The authorization result
> > is just an add on.
> >
> > In resume process, the key will be loaded in early boot stage. Either in
> > EFI stub (EFI secure key type, be rejected by EFI subsystem) or initrd. On
> > the other hand, PCRs may capped after TPM trusted key be loaded. So resume
> > kernel has no chance to load or unseal key again. The only way to get the
> > unsealed/decrypted key is from boot kernel.
> >
> > The trampoline page is an empty page that it's pre-reserved in snapshot image
> > when hibernation. When resuming, this trampoline page will be written by boot
> > kernel to fill unsealed key. Then resume kernel will check the trampoline
> > page to take the key. If the key is empty(zero), which means that boot
> > kernel didn't forward valid key because some reasons. Either the key is
> > broken (EFI key be erased) or userspace didn't enroll key to boot kernel.
> > That's why I check the zero key in code.
>
> I don't follow this at all. After reading the code, I couldn't figure
> out which stage generates the trampoline, which stage reads the
> trampoline, and where there are any keys at all in the trampoline.
>

Very thinks for your review to my code...

> There are effectively three kernels:
>
> a) The kernel that gets hibernated and generates the hibernation
> image. This kernel obviously has access to all the keying
> information, although some of that information may actually be
> resident on the TPM and not in memory.
>
> b) The boot kernel that reads the hibernation image.
>
> c) The resumed kernel. This kernel has the same code as (a) but
> possibly different PCRs.
>
> If the encryption is going to protect anything at all, then kernel (b)

Sorry for I forgot mention in previous mail...

Here is the point. I developed authentication before encryption, so the
image is not encrypted in early version. In my original design, I try
to avoid that hibernate key be snapshotted to image. So I erased
hibernate key from kernel (a), which means that the kernel (b) should
forwards hibernate key to kernel (c). Otherwise next hibernate cycle
will be failed because no key.

You are right. When the snpashot image be encrypted, the key doesn't
need to be erased. Then kernel (b) doesn't need to forward key to kernel
(c).

I will remove trampoline. But, I still have a question when using PCRs
with hibernate as below...

> needs to have a way to get the key, which is presumably done using the
> TPM, since it's not obvious to me that there's any other credible way
> to do it. (A passphrase could also make sense, but then you'd just
> put the hibernation image on a dm-crypt volume and the problem is
> solved.) Kernel (c) shouldn't need to do any crypto, since, by the
> time any code in kernel (c) executes at all, you've already decrypted
> everything and you've effectively committed to trusting the
> hibernation image.
>
> If something goes wrong when you hibernate a second time, this means
> that the first hibernation screwed up and corrupted something, e.g.
> PCRs. That should be fixed independently of this series IMO.
>

For kernel lockdown situation, if PCRs are not capped, which means
that a sealed bundle can be unsealed by root. If root be compromised
then the sealed key can also be compromised.

I was thinking if kernel generates a sealed key when booting, then
kernel capped the PCR with a constant value[1] before the userspace
is available. Then the sealed key can not be unsealed by compromised
root.

In this case, kernel (b) will do the job to produce the sealed key
before PCR be capped. Then the key must be forwarded by kernel (b)
to kernel (c).

[1]
Using constant value for capping but not random number is for other
subsystem or userland can continue to use the PCR.

Thanks a lot!
Joey Lee

2019-01-11 03:16:57

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [PATCH 0/5 v2][RFC] Encryption and authentication for hibernate snapshot image

On Thu, Jan 10, 2019 at 7:13 AM joeyli <[email protected]> wrote:
>
> On Wed, Jan 09, 2019 at 10:47:42AM -0800, Andy Lutomirski wrote:
> > On Wed, Jan 9, 2019 at 8:40 AM joeyli <[email protected]> wrote:
> > >
> > > Hi Andy,
> > >
> > > Thanks for your review!
> > >
> > > On Tue, Jan 08, 2019 at 01:41:48PM -0800, Andy Lutomirski wrote:
> > > > > On Jan 7, 2019, at 9:37 AM, joeyli <[email protected]> wrote:
> > > > >
> > > > > Hi Pavel,
> > > > >
> > > > > Thanks for your review!
> > > > >
> > > > >> On Sun, Jan 06, 2019 at 07:10:27PM +0100, Pavel Machek wrote:
> > > > >> Hi!
> > > > >>
> > > > >>> This patchset is the implementation of encryption and authentication
> > > > >>> for hibernate snapshot image. The image will be encrypted by AES and
> > > > >>> authenticated by HMAC.
> > > > >>
> > > > >> Ok, so you encrypt.
> > > > >
> > > > > Yes, encryption and authentication.
> > > > >
> > > > >>> The hibernate function can be used to snapshot memory pages to an image,
> > > > >>> then kernel restores the image to memory space in a appropriate time.
> > > > >>> There have secrets in snapshot image and cracker may modifies it for
> > > > >>> hacking system. Encryption and authentication of snapshot image can protect
> > > > >>> the system.
> > > > >>>
> > > > >>> Hibernate function requests the master key through key retention service.
> > > > >>> The snapshot master key can be a trusted key or a user defined key. The
> > > > >>> name of snapshot master key is fixed to "swsusp-kmk". User should loads
> > > > >>> swsusp-kmk to kernel by keyctl tool before the hibernation resume.
> > > > >>> e.g. The swsusp-kmk must be loaded before systemd-hibernate-resume
> > > > >>
> > > > >> But if userspace has a key, encryption is useless against root.
> > > > >
> > > > > Yes, but this concern is not only for hibernation encryption. This patch
> > > > > set does not provide solution against this concern.
> > > > >
> > > > > The purpose of this patch set is to encrypt and authenticate hibernate
> > > > > snapshot image in kernel space. It also requests key through keyring
> > > > > mechanism. Which means that we can easy to adapt to new key type from
> > > > > keyring in the future.
> > > > >
> > > > > Currently TPM trusted key or user defined key types are not against
> > > > > root. Even using the TPM trusted key, it still can be unsealed by root
> > > > > before the PCRs be capped (unless we capped PCRs in kernel).
> > > > >
> > > > > My solution for keeping the secret by kernel is the EFI secure key type:
> > > > > https://lkml.org/lkml/2018/8/5/31
> > > > >
> > > > > But the EFI boot variable doesn't design for keeping secret, so Windows
> > > > > and OEM/ODM do not use boot variable to keep secret. So this idea can
> > > > > not be accepted. We must think other key type against root.
> > > > >
> > > > >>> The TPM trusted key type is preferred to be the master key. But user
> > > > >>> defined key can also be used for testing or when the platform doesn't
> > > > >>> have TPM. User must be aware that the security of user key relies on
> > > > >>> user space. If the root account be compromised, then the user key will
> > > > >>> easy to be grabbed.
> > > > >>
> > > > >> In the TPM case, does userland have access to the key?
> > > > >
> > > > > In the TPM case, userland can only touch the sealed key blob. So userland
> > > > > doesn't know the real secret. But it has risk that root unseals the key
> > > > > before PCRs be capped.
> > > > >
> > > > >> Please explain your security goals.
> > > > >
> > > > > My security goals:
> > > > >
> > > > > - Encrypt and authicate hibernate snapshot image in kernel space. Userspace
> > > > > can only touch an encrypted and signed snapshot image.
> > > >
> > > > Signed?
> > > >
> > > > I’m not entirely convinced that the keyring mechanism is what you
> > > > want. ISTM that there are two goals here:
> > > >
> > > > a) Encryption: it should be as hard as can reasonably be arranged to
> > > > extract secrets from a hibernation image.
> > > >
> > > > b) Authentication part 1: it should not be possible for someone in
> > > > possession of a turned-off machine to tamper with the hibernation
> > > > image such that the image, when booted, will leak its secrets. This
> > > > should protect against attackers who don’t know the encryption key.
> > > >
> > > > c) Authentication part 2: it should be to verify, to the extent
> > > > practical, that the image came from the same machine and was really
> > > > created using hibernation. Or maybe by the same user.
> > > >
> > > > For (a) and (b), using an AE mode where the key is protected in some
> > > > reasonable way. Joey, why are you using HMAC? Please tell me you’re
> > > > at least doing encrypt-then-MAC. But why not use a real AE mode like
> > > > AES-GCM?
> > >
> > > The reason for using HMAC is the history for development. My first patch
> > > set is only for hibernate authentication. Then I added encryption code on
> > > top of my authentication patches in last version.
> > >
> > > I am doing encrypt-then-MAC. My code ecrypts each page by AES then HMAC
> > > whole snapshot image. I feed encrypted data pages one by one to
> > > crypto_shash_update() API for calculating the hash for whole image.
> >
> > ...
> >
> > I think you should write down a clear description of the data format.
>
> Hibernation allocates free pages for building snapshot image. Those free
> pages are scattered in memory. So kernel marks those page numbers on a
> bitmap to locate them. Because this image buffer is discontinuous, so I
> use update mode hashing whole image.
>
> > A general problem with crypto is that the fact that it appears to work
> > doesn't mean it's secure at all, and it's very hard to follow the
> > code. Especially in Linux using the crypto API -- code using the
> > crypto API tends to be mostly boilerplate.
> >
>
> hm... Do you mean that the implementation of HMAC in crypto cannot be
> trusted? I hope at least that I can trust the update mode for normal
> hash like SHA256 or SHA512?

No, I fully expect the crypto code to do what it says. What I mean is
that you can easily create utterly broken things that involve crypto,
but they still work fine when you use then non-maliciously.

> > >
> > > Sorry for I didn't capture the meaning of "acceptable usage". The trusted
> > > key already be unsealed by TPM when the key be enrolled by keyctl tool.
> > > So my code just extract the unsealed key data (the random number) for
> > > encryption.
> >
> > If someone creates a trusted key that is used for asymmetric crypto or
> > perhaps a trusted key that is intended to be used for, say, an HMAC
> > key, you should not also use it to derive hibernation keys. This is
> > what I mean by "acceptable usage".
> >
>
> When keyring is producing encrypted key, the trusted key be used to
> derive the encrypt key and authenticate key to encrypt and hmac sign
> a encrypted key. So trusted key can be used in symmetric crypto.

Can it?

David, you actually understand the overall kernel keyring design. Do
keys in the keyring have usage constraints?

But I think you need to take a big step back here. We already have
kernel/power/user.c. It seems to me that you can do a better job of
what you're trying to do with less code by using it. Why do you need
new kernel code *at all* to accomplish your goals?

2019-01-11 14:22:35

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

On Tue, Jan 08, 2019 at 05:43:53PM -0800, Andy Lutomirski wrote:
> (Also, do we have a sensible story of how the TPM interacts with
> hibernation at all? Presumably we should at least try to replay the
> PCR operations that have occurred so that we can massage the PCRs into
> the same state post-hibernation. Also, do we have any way for the
> kernel to sign something with the TPM along with an attestation that
> the signature was requested *by the kernel*? Something like a
> sub-hierarchy of keys that the kernel explicitly prevents userspace
> from accessing?)

Kernel can keep it is own key hierarchy in memory as TPM2 chips allow
to offload data in encrypted form and load it to TPM when it needs to
use it.

The in-kernel resource manager that I initiated couple years ago
provides this type of functionality.

/Jarkko

2019-01-11 15:02:51

by joeyli

[permalink] [raw]
Subject: Re: [PATCH 0/5 v2][RFC] Encryption and authentication for hibernate snapshot image

On Thu, Jan 10, 2019 at 05:09:46PM -0800, Andy Lutomirski wrote:
> On Thu, Jan 10, 2019 at 7:13 AM joeyli <[email protected]> wrote:
> >
> > On Wed, Jan 09, 2019 at 10:47:42AM -0800, Andy Lutomirski wrote:
> > > On Wed, Jan 9, 2019 at 8:40 AM joeyli <[email protected]> wrote:
> > > >
> > > > Hi Andy,
> > > >
> > > > Thanks for your review!
> > > >
> > > > On Tue, Jan 08, 2019 at 01:41:48PM -0800, Andy Lutomirski wrote:
> > > > > > On Jan 7, 2019, at 9:37 AM, joeyli <[email protected]> wrote:
> > > > > >
> > > > > > Hi Pavel,
> > > > > >
> > > > > > Thanks for your review!
> > > > > >
> > > > > >> On Sun, Jan 06, 2019 at 07:10:27PM +0100, Pavel Machek wrote:
> > > > > >> Hi!
> > > > > >>
> > > > > >>> This patchset is the implementation of encryption and authentication
> > > > > >>> for hibernate snapshot image. The image will be encrypted by AES and
> > > > > >>> authenticated by HMAC.
> > > > > >>
> > > > > >> Ok, so you encrypt.
> > > > > >
> > > > > > Yes, encryption and authentication.
> > > > > >
> > > > > >>> The hibernate function can be used to snapshot memory pages to an image,
> > > > > >>> then kernel restores the image to memory space in a appropriate time.
> > > > > >>> There have secrets in snapshot image and cracker may modifies it for
> > > > > >>> hacking system. Encryption and authentication of snapshot image can protect
> > > > > >>> the system.
> > > > > >>>
> > > > > >>> Hibernate function requests the master key through key retention service.
> > > > > >>> The snapshot master key can be a trusted key or a user defined key. The
> > > > > >>> name of snapshot master key is fixed to "swsusp-kmk". User should loads
> > > > > >>> swsusp-kmk to kernel by keyctl tool before the hibernation resume.
> > > > > >>> e.g. The swsusp-kmk must be loaded before systemd-hibernate-resume
> > > > > >>
> > > > > >> But if userspace has a key, encryption is useless against root.
> > > > > >
> > > > > > Yes, but this concern is not only for hibernation encryption. This patch
> > > > > > set does not provide solution against this concern.
> > > > > >
> > > > > > The purpose of this patch set is to encrypt and authenticate hibernate
> > > > > > snapshot image in kernel space. It also requests key through keyring
> > > > > > mechanism. Which means that we can easy to adapt to new key type from
> > > > > > keyring in the future.
> > > > > >
> > > > > > Currently TPM trusted key or user defined key types are not against
> > > > > > root. Even using the TPM trusted key, it still can be unsealed by root
> > > > > > before the PCRs be capped (unless we capped PCRs in kernel).
> > > > > >
> > > > > > My solution for keeping the secret by kernel is the EFI secure key type:
> > > > > > https://lkml.org/lkml/2018/8/5/31
> > > > > >
> > > > > > But the EFI boot variable doesn't design for keeping secret, so Windows
> > > > > > and OEM/ODM do not use boot variable to keep secret. So this idea can
> > > > > > not be accepted. We must think other key type against root.
> > > > > >
> > > > > >>> The TPM trusted key type is preferred to be the master key. But user
> > > > > >>> defined key can also be used for testing or when the platform doesn't
> > > > > >>> have TPM. User must be aware that the security of user key relies on
> > > > > >>> user space. If the root account be compromised, then the user key will
> > > > > >>> easy to be grabbed.
> > > > > >>
> > > > > >> In the TPM case, does userland have access to the key?
> > > > > >
> > > > > > In the TPM case, userland can only touch the sealed key blob. So userland
> > > > > > doesn't know the real secret. But it has risk that root unseals the key
> > > > > > before PCRs be capped.
> > > > > >
> > > > > >> Please explain your security goals.
> > > > > >
> > > > > > My security goals:
> > > > > >
> > > > > > - Encrypt and authicate hibernate snapshot image in kernel space. Userspace
> > > > > > can only touch an encrypted and signed snapshot image.
> > > > >
> > > > > Signed?
> > > > >
> > > > > I’m not entirely convinced that the keyring mechanism is what you
> > > > > want. ISTM that there are two goals here:
> > > > >
> > > > > a) Encryption: it should be as hard as can reasonably be arranged to
> > > > > extract secrets from a hibernation image.
> > > > >
> > > > > b) Authentication part 1: it should not be possible for someone in
> > > > > possession of a turned-off machine to tamper with the hibernation
> > > > > image such that the image, when booted, will leak its secrets. This
> > > > > should protect against attackers who don’t know the encryption key.
> > > > >
> > > > > c) Authentication part 2: it should be to verify, to the extent
> > > > > practical, that the image came from the same machine and was really
> > > > > created using hibernation. Or maybe by the same user.
> > > > >
> > > > > For (a) and (b), using an AE mode where the key is protected in some
> > > > > reasonable way. Joey, why are you using HMAC? Please tell me you’re
> > > > > at least doing encrypt-then-MAC. But why not use a real AE mode like
> > > > > AES-GCM?
> > > >
> > > > The reason for using HMAC is the history for development. My first patch
> > > > set is only for hibernate authentication. Then I added encryption code on
> > > > top of my authentication patches in last version.
> > > >
> > > > I am doing encrypt-then-MAC. My code ecrypts each page by AES then HMAC
> > > > whole snapshot image. I feed encrypted data pages one by one to
> > > > crypto_shash_update() API for calculating the hash for whole image.
> > >
> > > ...
> > >
> > > I think you should write down a clear description of the data format.
> >
> > Hibernation allocates free pages for building snapshot image. Those free
> > pages are scattered in memory. So kernel marks those page numbers on a
> > bitmap to locate them. Because this image buffer is discontinuous, so I
> > use update mode hashing whole image.
> >
> > > A general problem with crypto is that the fact that it appears to work
> > > doesn't mean it's secure at all, and it's very hard to follow the
> > > code. Especially in Linux using the crypto API -- code using the
> > > crypto API tends to be mostly boilerplate.
> > >
> >
> > hm... Do you mean that the implementation of HMAC in crypto cannot be
> > trusted? I hope at least that I can trust the update mode for normal
> > hash like SHA256 or SHA512?
>
> No, I fully expect the crypto code to do what it says. What I mean is
> that you can easily create utterly broken things that involve crypto,
> but they still work fine when you use then non-maliciously.
>

OK, I see! I will review my code with crypto to make sure that I didn't
create broken things.

> > > >
> > > > Sorry for I didn't capture the meaning of "acceptable usage". The trusted
> > > > key already be unsealed by TPM when the key be enrolled by keyctl tool.
> > > > So my code just extract the unsealed key data (the random number) for
> > > > encryption.
> > >
> > > If someone creates a trusted key that is used for asymmetric crypto or
> > > perhaps a trusted key that is intended to be used for, say, an HMAC
> > > key, you should not also use it to derive hibernation keys. This is
> > > what I mean by "acceptable usage".
> > >
> >
> > When keyring is producing encrypted key, the trusted key be used to
> > derive the encrypt key and authenticate key to encrypt and hmac sign
> > a encrypted key. So trusted key can be used in symmetric crypto.
>
> Can it?
>
> David, you actually understand the overall kernel keyring design. Do
> keys in the keyring have usage constraints?
>
> But I think you need to take a big step back here. We already have
> kernel/power/user.c. It seems to me that you can do a better job of
> what you're trying to do with less code by using it. Why do you need
> new kernel code *at all* to accomplish your goals?

I still very think for you, Stephan, Pavel and all kernel experts'
review to my code. I will follow your suggestions to rewrite my
solution.

I thought that the userland hibernation is not enough against a
compromised root. My original target of hibernation encryption and
authentication is for kernel lockdown mode. I hope that kernel can
produce encrypted/signed snapshot image. So I developed EFI secure
key and hibernation encryption.

Becasue my original solution relates two areas, EFI and hibernation.
So I separated them and sent them to kernel upsteam for review. The
EFI seucre key is not accepted so I moved to use TPM trusted key.
I thought that the final problem is still how to deal with TPM against
a compromised root.

Thanks a lot!
Joey Lee

2019-01-11 15:05:59

by joeyli

[permalink] [raw]
Subject: Re: [PATCH 0/5 v2][RFC] Encryption and authentication for hibernate snapshot image

On Wed, Jan 09, 2019 at 05:47:53PM +0100, Stephan Mueller wrote:
> Am Mittwoch, 9. Januar 2019, 17:39:58 CET schrieb joeyli:
>
> Hi joeyli,
>
> >
> > I am doing encrypt-then-MAC.
>
> Note, this is what the authenc() AEAD cipher does.
>

OK.. Thanks for your reminding. I will look at it.

Joey Lee

2019-01-11 20:50:13

by James Bottomley

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

On Fri, 2019-01-11 at 16:02 +0200, Jarkko Sakkinen wrote:
> On Tue, Jan 08, 2019 at 05:43:53PM -0800, Andy Lutomirski wrote:
> > (Also, do we have a sensible story of how the TPM interacts with
> > hibernation at all? Presumably we should at least try to replay
> > the PCR operations that have occurred so that we can massage the
> > PCRs into the same state post-hibernation. Also, do we have any
> > way for the kernel to sign something with the TPM along with an
> > attestation that the signature was requested *by the
> > kernel*? Something like a sub-hierarchy of keys that the kernel
> > explicitly prevents userspace from accessing?)
>
> Kernel can keep it is own key hierarchy in memory as TPM2 chips allow
> to offload data in encrypted form and load it to TPM when it needs to
> use it.
>
> The in-kernel resource manager that I initiated couple years ago
> provides this type of functionality.

Actually, the resource manager only keeps volatile objects separated
when in use not when offloaded. The problem here is that the object
needs to be persisted across reboots, so either it gets written to the
NV area, bypassing the resource manager and making it globally visible
or it has to get stored in TPM form in the hibernation image, meaning
anyone with access to the TPM who can read the image can extract and
load it. Further: anyone with access to the TPM can create a bogus
sealed key and encrypt a malicious hibernation image with it. So there
are two additional problems

1. Given that the attacker may have access to the binary form of the
key, can we make sure only the kernel can get it released?
2. How do we prevent an attacker with access to the TPM from creating a
bogus sealed key?

This is why I was thinking localities.

James


2019-01-11 20:51:32

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

On Thu, Jan 10, 2019 at 02:11:55AM +0800, joeyli wrote:
> > Well, I think here, if we were actually trying to solve the problem of
> > proving the hibernated image were the same one we would need to prove
> > some log of the kernel operation came to a particular value *after* the
> > hibernated image were restored ... it's not really possible to
> > condition key release which must occur before the restore on that
> > outcome, so it strikes me we need more than a simple release bound to
> > PCR values.
> >
>
> hm... I am studying your information. But I have a question...
>
> If PCR is not capped and the root be compromised, is it possible that a
> sealed bundle also be compromised?
>
> Is it possible that kernel can produce a sealed key with PCR by TPM when
> booting? Then kernel caps a PCR by a constant value before the root is
> available for userland. Then the sealed key can be exposed to userland
> or be attached on hibernate image. Even the root be compromised, the TPM
> trusted key is still secure.

I think this even might be reasonable. Especially when we land James'
encrypted sessions patches at some point.

/Jarkko

2019-01-11 20:52:27

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

On Wed, Jan 09, 2019 at 10:34:42AM -0800, Andy Lutomirski wrote:
> I suppose I should go read the 2.0 spec. I’ve read the 1.2 spec, but I
> always assumed that 2.0 was essentially a superset of 1.2
> functionality.

They are essentially different protocols. No real compatibility.


> Can the kernel filter TPM 2.0 operations? If so, then a signature
> that the kernel would have prevented user code from generating is de
> facto an attestation that the kernel generated it (or that the kernel
> was compromised, which is sort of equivalent).

You shoud look into TPM resource manager that I implemented with great
work from James on session swapping and see how far it scales what you
have in mind. It is currently exposed only to the user space but could
be easily made an in-kernel API.

Side-topic: right now the TPM driver can be compiled as a module when
its APIs are not used by the kernel (namely IMA and trusted keys) with
some Kconfig magic. Since it looks like that there will be even more
customers, I think it would make sense to make the TPM driver core as
part of the core kernel (device drivers for different types of chips
could still be modules). I've proposed this before maybe two times, but
it has always been rejected.

/Jarkko

2019-01-11 21:46:08

by Stephan Müller

[permalink] [raw]
Subject: [PATCH 6/6] crypto: tcrypt - add KDF test invocation

Enable testing of the SP800-108 and RFC5869 KDFs.

Signed-off-by: Stephan Mueller <[email protected]>
---
crypto/tcrypt.c | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/crypto/tcrypt.c b/crypto/tcrypt.c
index e7fb87e114a5..5606e59e80ec 100644
--- a/crypto/tcrypt.c
+++ b/crypto/tcrypt.c
@@ -2054,6 +2054,14 @@ static int do_test(const char *alg, u32 type, u32 mask, int m, u32 num_mb)
ret += tcrypt_test("cbc(sm4)");
ret += tcrypt_test("ctr(sm4)");
break;
+ case 192:
+ ret += tcrypt_test("kdf_ctr(hmac(sha256))");
+ ret += tcrypt_test("kdf_dpi(hmac(sha256))");
+ ret += tcrypt_test("kdf_fb(hmac(sha256))");
+ break;
+ case 193:
+ ret += tcrypt_test("hkdf(hmac(sha256))");
+ break;
case 200:
test_cipher_speed("ecb(aes)", ENCRYPT, sec, NULL, 0,
speed_template_16_24_32);
--
2.20.1





2019-01-11 21:46:48

by Stephan Müller

[permalink] [raw]
Subject: [PATCH 5/6] crypto: hkdf - add known answer tests

Add known answer tests to the testmgr for the HKDF (RFC5869) cipher.

The known answer tests are derived from RFC 5869 appendix A.

Note, the HKDF is considered to be a FIPS 140-2 allowed (not approved)
cipher as of now. Yet, an allowed cipher is usable under FIPS 140-2
rules.

Signed-off-by: Stephan Mueller <[email protected]>
---
crypto/testmgr.c | 32 +++++++++++++
crypto/testmgr.h | 115 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 147 insertions(+)

diff --git a/crypto/testmgr.c b/crypto/testmgr.c
index ff9051bffa1f..aba7a3645293 100644
--- a/crypto/testmgr.c
+++ b/crypto/testmgr.c
@@ -3187,6 +3187,38 @@ static const struct alg_test_desc alg_test_descs[] = {
.suite = {
.hash = __VECS(ghash_tv_template)
}
+ }, {
+ .alg = "hkdf(hmac(sha1))",
+ .test = alg_test_kdf,
+ .fips_allowed = 1,
+ .suite = {
+ .kdf = {
+ .vecs = hkdf_hmac_sha1_tv_template,
+ .count = ARRAY_SIZE(hkdf_hmac_sha1_tv_template)
+ }
+ }
+ }, {
+ .alg = "hkdf(hmac(sha224))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "hkdf(hmac(sha256))",
+ .test = alg_test_kdf,
+ .fips_allowed = 1,
+ .suite = {
+ .kdf = {
+ .vecs = hkdf_hmac_sha256_tv_template,
+ .count = ARRAY_SIZE(hkdf_hmac_sha256_tv_template)
+ }
+ }
+ }, {
+ .alg = "hkdf(hmac(sha384))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "hkdf(hmac(sha512))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
}, {
.alg = "hmac(md5)",
.test = alg_test_hash,
diff --git a/crypto/testmgr.h b/crypto/testmgr.h
index a729b66f8757..7c4aa694e0f3 100644
--- a/crypto/testmgr.h
+++ b/crypto/testmgr.h
@@ -28002,6 +28002,121 @@ static struct kdf_testvec kdf_dpi_hmac_sha256_tv_template[] = {
}
};

+/* Test vectors from RFC 5869 appendix A */
+static struct kdf_testvec hkdf_hmac_sha256_tv_template[] = {
+ {
+ .K1 = (unsigned char *)
+#ifdef __LITTLE_ENDIAN
+ "\x08\x00" /* rta length */
+ "\x01\x00" /* rta type */
+ "\x0d\x00\x00\x00" /* salt length */
+#else
+ "\x00\x08" /* rta length */
+ "\x00\x01" /* rta type */
+ "\x00\x00\x00\x0d" /* salt length */
+#endif
+ "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0a\x0b\x0c" /* salt */
+ "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+ "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+ "\x0b\x0b\x0b\x0b\x0b\x0b", /* IKM */
+ .K1len = 43,
+ .context = (unsigned char *)
+ "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7"
+ "\xf8\xf9",
+ .contextlen = 10,
+ .expected = (unsigned char *)
+ "\x3c\xb2\x5f\x25\xfa\xac\xd5\x7a"
+ "\x90\x43\x4f\x64\xd0\x36\x2f\x2a"
+ "\x2d\x2d\x0a\x90\xcf\x1a\x5a\x4c"
+ "\x5d\xb0\x2d\x56\xec\xc4\xc5\xbf"
+ "\x34\x00\x72\x08\xd5\xb8\x87\x18"
+ "\x58\x65",
+ .expectedlen = 42
+ }, {
+ .K1 = (unsigned char *)
+#ifdef __LITTLE_ENDIAN
+ "\x08\x00" /* rta length */
+ "\x01\x00" /* rta type */
+#else
+ "\x00\x08" /* rta length */
+ "\x00\x01" /* rta type */
+#endif
+ "\x00\x00\x00\x00" /* salt length */
+ "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+ "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+ "\x0b\x0b\x0b\x0b\x0b\x0b", /* IKM */
+ .K1len = 30,
+ .context = NULL,
+ .contextlen = 0,
+ .expected = (unsigned char *)
+ "\x8d\xa4\xe7\x75\xa5\x63\xc1\x8f"
+ "\x71\x5f\x80\x2a\x06\x3c\x5a\x31"
+ "\xb8\xa1\x1f\x5c\x5e\xe1\x87\x9e"
+ "\xc3\x45\x4e\x5f\x3c\x73\x8d\x2d"
+ "\x9d\x20\x13\x95\xfa\xa4\xb6\x1a"
+ "\x96\xc8",
+ .expectedlen = 42
+ }
+};
+
+/* Test vectors from RFC 5869 appendix A */
+static struct kdf_testvec hkdf_hmac_sha1_tv_template[] = {
+ {
+ .K1 = (unsigned char *)
+#ifdef __LITTLE_ENDIAN
+ "\x08\x00" /* rta length */
+ "\x01\x00" /* rta type */
+ "\x0d\x00\x00\x00" /* salt length */
+#else
+ "\x00\x08" /* rta length */
+ "\x00\x01" /* rta type */
+ "\x00\x00\x00\x0d" /* salt length */
+#endif
+ "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0a\x0b\x0c" /* salt */
+ "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+ "\x0b\x0b\x0b", /* IKM */
+ .K1len = 32,
+ .context = (unsigned char *)
+ "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7"
+ "\xf8\xf9",
+ .contextlen = 10,
+ .expected = (unsigned char *)
+ "\x08\x5a\x01\xea\x1b\x10\xf3\x69"
+ "\x33\x06\x8b\x56\xef\xa5\xad\x81"
+ "\xa4\xf1\x4b\x82\x2f\x5b\x09\x15"
+ "\x68\xa9\xcd\xd4\xf1\x55\xfd\xa2"
+ "\xc2\x2e\x42\x24\x78\xd3\x05\xf3"
+ "\xf8\x96",
+ .expectedlen = 42
+ }, {
+ .K1 = (unsigned char *)
+#ifdef __LITTLE_ENDIAN
+ "\x08\x00" /* rta length */
+ "\x01\x00" /* rta type */
+#else
+ "\x00\x08" /* rta length */
+ "\x00\x01" /* rta type */
+#endif
+ "\x00\x00\x00\x00" /* salt length */
+ "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+ "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+ "\x0b\x0b\x0b\x0b\x0b\x0b", /* IKM */
+ .K1len = 30,
+ .context = NULL,
+ .contextlen = 0,
+ .expected = (unsigned char *)
+ "\x0a\xc1\xaf\x70\x02\xb3\xd7\x61"
+ "\xd1\xe5\x52\x98\xda\x9d\x05\x06"
+ "\xb9\xae\x52\x05\x72\x20\xa3\x06"
+ "\xe0\x7b\x6b\x87\xe8\xdf\x21\xd0"
+ "\xea\x00\x03\x3d\xe0\x39\x84\xd3"
+ "\x49\x18",
+ .expectedlen = 42
+ }
+};
+
/* Cast5 test vectors from RFC 2144 */
static const struct cipher_testvec cast5_tv_template[] = {
{
--
2.20.1





2019-01-11 21:47:35

by Stephan Müller

[permalink] [raw]
Subject: [PATCH 3/6] crypto: kdf - add known answer tests

Add known answer tests to the testmgr for the KDF (SP800-108) cipher.

Signed-off-by: Stephan Mueller <[email protected]>
---
crypto/testmgr.c | 226 +++++++++++++++++++++++++++++++++++++++++++++++
crypto/testmgr.h | 110 +++++++++++++++++++++++
2 files changed, 336 insertions(+)

diff --git a/crypto/testmgr.c b/crypto/testmgr.c
index 0f684a414acb..ff9051bffa1f 100644
--- a/crypto/testmgr.c
+++ b/crypto/testmgr.c
@@ -110,6 +110,11 @@ struct drbg_test_suite {
unsigned int count;
};

+struct kdf_test_suite {
+ struct kdf_testvec *vecs;
+ unsigned int count;
+};
+
struct akcipher_test_suite {
const struct akcipher_testvec *vecs;
unsigned int count;
@@ -133,6 +138,7 @@ struct alg_test_desc {
struct hash_test_suite hash;
struct cprng_test_suite cprng;
struct drbg_test_suite drbg;
+ struct kdf_test_suite kdf;
struct akcipher_test_suite akcipher;
struct kpp_test_suite kpp;
} suite;
@@ -2020,6 +2026,64 @@ static int drbg_cavs_test(const struct drbg_testvec *test, int pr,
return ret;
}

+static int kdf_cavs_test(struct kdf_testvec *test,
+ const char *driver, u32 type, u32 mask)
+{
+ int ret = -EAGAIN;
+ struct crypto_rng *drng;
+ unsigned char *buf = kzalloc(test->expectedlen, GFP_KERNEL);
+
+ if (!buf)
+ return -ENOMEM;
+
+ drng = crypto_alloc_rng(driver, type | CRYPTO_ALG_INTERNAL, mask);
+ if (IS_ERR(drng)) {
+ printk(KERN_ERR "alg: kdf: could not allocate cipher handle "
+ "for %s\n", driver);
+ kzfree(buf);
+ return -ENOMEM;
+ }
+
+ ret = crypto_rng_reset(drng, test->K1, test->K1len);
+ if (ret) {
+ printk(KERN_ERR "alg: kdf: could not set key derivation key\n");
+ goto err;
+ }
+
+ ret = crypto_rng_generate(drng, test->context, test->contextlen,
+ buf, test->expectedlen);
+ if (ret) {
+ printk(KERN_ERR "alg: kdf: could not obtain key data\n");
+ goto err;
+ }
+
+ ret = memcmp(test->expected, buf, test->expectedlen);
+
+err:
+ crypto_free_rng(drng);
+ kzfree(buf);
+ return ret;
+}
+
+static int alg_test_kdf(const struct alg_test_desc *desc, const char *driver,
+ u32 type, u32 mask)
+{
+ int err = 0;
+ unsigned int i = 0;
+ struct kdf_testvec *template = desc->suite.kdf.vecs;
+ unsigned int tcount = desc->suite.kdf.count;
+
+ for (i = 0; i < tcount; i++) {
+ err = kdf_cavs_test(&template[i], driver, type, mask);
+ if (err) {
+ printk(KERN_ERR "alg: kdf: Test %d failed for %s\n",
+ i, driver);
+ err = -EINVAL;
+ break;
+ }
+ }
+ return err;
+}

static int alg_test_drbg(const struct alg_test_desc *desc, const char *driver,
u32 type, u32 mask)
@@ -3220,6 +3284,168 @@ static const struct alg_test_desc alg_test_descs[] = {
.alg = "jitterentropy_rng",
.fips_allowed = 1,
.test = alg_test_null,
+ }, {
+ .alg = "kdf_ctr(cmac(aes))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_ctr(cmac(des3_ede))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_ctr(hmac(sha1))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_ctr(hmac(sha224))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_ctr(hmac(sha256))",
+ .test = alg_test_kdf,
+ .fips_allowed = 1,
+ .suite = {
+ .kdf = {
+ .vecs = kdf_ctr_hmac_sha256_tv_template,
+ .count = ARRAY_SIZE(kdf_ctr_hmac_sha256_tv_template)
+ }
+ }
+ }, {
+ .alg = "kdf_ctr(hmac(sha384))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_ctr(hmac(sha512))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_ctr(sha1)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_ctr(sha224)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_ctr(sha256)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_ctr(sha384)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_ctr(sha512)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_dpi(cmac(aes))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_dpi(cmac(des3_ede))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_dpi(hmac(sha1))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_dpi(hmac(sha224))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_dpi(hmac(sha256))",
+ .test = alg_test_kdf,
+ .fips_allowed = 1,
+ .suite = {
+ .kdf = {
+ .vecs = kdf_dpi_hmac_sha256_tv_template,
+ .count = ARRAY_SIZE(kdf_dpi_hmac_sha256_tv_template)
+ }
+ }
+ }, {
+ .alg = "kdf_dpi(hmac(sha384))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_dpi(hmac(sha512))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_dpi(sha1)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_dpi(sha224)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_dpi(sha256)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_dpi(sha384)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_dpi(sha512)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_fb(cmac(aes))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_fb(cmac(des3_ede))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_fb(hmac(sha1))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_fb(hmac(sha224))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_fb(hmac(sha256))",
+ .test = alg_test_kdf,
+ .fips_allowed = 1,
+ .suite = {
+ .kdf = {
+ .vecs = kdf_fb_hmac_sha256_tv_template,
+ .count = ARRAY_SIZE(kdf_fb_hmac_sha256_tv_template)
+ }
+ }
+ }, {
+ .alg = "kdf_fb(hmac(sha384))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_fb(hmac(sha512))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_fb(sha1)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_fb(sha224)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_fb(sha256)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_fb(sha384)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_fb(sha512)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
}, {
.alg = "kw(aes)",
.test = alg_test_skcipher,
diff --git a/crypto/testmgr.h b/crypto/testmgr.h
index ca8e8ebef309..a729b66f8757 100644
--- a/crypto/testmgr.h
+++ b/crypto/testmgr.h
@@ -122,6 +122,15 @@ struct drbg_testvec {
size_t expectedlen;
};

+struct kdf_testvec {
+ unsigned char *K1;
+ size_t K1len;
+ unsigned char *context;
+ size_t contextlen;
+ unsigned char *expected;
+ size_t expectedlen;
+};
+
struct akcipher_testvec {
const unsigned char *key;
const unsigned char *m;
@@ -27892,6 +27901,107 @@ static const struct drbg_testvec drbg_nopr_ctr_aes128_tv_template[] = {
},
};

+/*
+ * Test vector obtained from
+ * http://csrc.nist.gov/groups/STM/cavp/documents/KBKDF800-108/CounterMode.zip
+ */
+static struct kdf_testvec kdf_ctr_hmac_sha256_tv_template[] = {
+ {
+ .K1 = (unsigned char *)
+ "\xdd\x1d\x91\xb7\xd9\x0b\x2b\xd3"
+ "\x13\x85\x33\xce\x92\xb2\x72\xfb"
+ "\xf8\xa3\x69\x31\x6a\xef\xe2\x42"
+ "\xe6\x59\xcc\x0a\xe2\x38\xaf\xe0",
+ .K1len = 32,
+ .context = (unsigned char *)
+ "\x01\x32\x2b\x96\xb3\x0a\xcd\x19"
+ "\x79\x79\x44\x4e\x46\x8e\x1c\x5c"
+ "\x68\x59\xbf\x1b\x1c\xf9\x51\xb7"
+ "\xe7\x25\x30\x3e\x23\x7e\x46\xb8"
+ "\x64\xa1\x45\xfa\xb2\x5e\x51\x7b"
+ "\x08\xf8\x68\x3d\x03\x15\xbb\x29"
+ "\x11\xd8\x0a\x0e\x8a\xba\x17\xf3"
+ "\xb4\x13\xfa\xac",
+ .contextlen = 60,
+ .expected = (unsigned char *)
+ "\x10\x62\x13\x42\xbf\xb0\xfd\x40"
+ "\x04\x6c\x0e\x29\xf2\xcf\xdb\xf0",
+ .expectedlen = 16
+ }
+};
+
+/*
+ * Test vector obtained from
+ * http://csrc.nist.gov/groups/STM/cavp/documents/KBKDF800-108/FeedbackModeNOzeroiv.zip
+ */
+static struct kdf_testvec kdf_fb_hmac_sha256_tv_template[] = {
+ {
+ .K1 = (unsigned char *)
+ "\x93\xf6\x98\xe8\x42\xee\xd7\x53"
+ "\x94\xd6\x29\xd9\x57\xe2\xe8\x9c"
+ "\x6e\x74\x1f\x81\x0b\x62\x3c\x8b"
+ "\x90\x1e\x38\x37\x6d\x06\x8e\x7b",
+ .K1len = 32,
+ .context = (unsigned char *)
+ "\x9f\x57\x5d\x90\x59\xd3\xe0\xc0"
+ "\x80\x3f\x08\x11\x2f\x8a\x80\x6d"
+ "\xe3\xc3\x47\x19\x12\xcd\xf4\x2b"
+ "\x09\x53\x88\xb1\x4b\x33\x50\x8e"
+ "\x53\xb8\x9c\x18\x69\x0e\x20\x57"
+ "\xa1\xd1\x67\x82\x2e\x63\x6d\xe5"
+ "\x0b\xe0\x01\x85\x32\xc4\x31\xf7"
+ "\xf5\xe3\x7f\x77\x13\x92\x20\xd5"
+ "\xe0\x42\x59\x9e\xbe\x26\x6a\xf5"
+ "\x76\x7e\xe1\x8c\xd2\xc5\xc1\x9a"
+ "\x1f\x0f\x80",
+ .contextlen = 83,
+ .expected = (unsigned char *)
+ "\xbd\x14\x76\xf4\x3a\x4e\x31\x57"
+ "\x47\xcf\x59\x18\xe0\xea\x5b\xc0"
+ "\xd9\x87\x69\x45\x74\x77\xc3\xab"
+ "\x18\xb7\x42\xde\xf0\xe0\x79\xa9"
+ "\x33\xb7\x56\x36\x5a\xfb\x55\x41"
+ "\xf2\x53\xfe\xe4\x3c\x6f\xd7\x88"
+ "\xa4\x40\x41\x03\x85\x09\xe9\xee"
+ "\xb6\x8f\x7d\x65\xff\xbb\x5f\x95",
+ .expectedlen = 64
+ }
+};
+
+/*
+ * Test vector obtained from
+ * http://csrc.nist.gov/groups/STM/cavp/documents/KBKDF800-108/PipelineModewithCounter.zip
+ */
+static struct kdf_testvec kdf_dpi_hmac_sha256_tv_template[] = {
+ {
+ .K1 = (unsigned char *)
+ "\x02\xd3\x6f\xa0\x21\xc2\x0d\xdb"
+ "\xde\xe4\x69\xf0\x57\x94\x68\xba"
+ "\xe5\xcb\x13\xb5\x48\xb6\xc6\x1c"
+ "\xdf\x9d\x3e\xc4\x19\x11\x1d\xe2",
+ .K1len = 32,
+ .context = (unsigned char *)
+ "\x85\xab\xe3\x8b\xf2\x65\xfb\xdc"
+ "\x64\x45\xae\x5c\x71\x15\x9f\x15"
+ "\x48\xc7\x3b\x7d\x52\x6a\x62\x31"
+ "\x04\x90\x4a\x0f\x87\x92\x07\x0b"
+ "\x3d\xf9\x90\x2b\x96\x69\x49\x04"
+ "\x25\xa3\x85\xea\xdb\x0f\x9c\x76"
+ "\xe4\x6f\x0f",
+ .contextlen = 51,
+ .expected = (unsigned char *)
+ "\xd6\x9f\x74\xf5\x18\xc9\xf6\x4f"
+ "\x90\xa0\xbe\xeb\xab\x69\xf6\x89"
+ "\xb7\x3b\x5c\x13\xeb\x0f\x86\x0a"
+ "\x95\xca\xd7\xd9\x81\x4f\x8c\x50"
+ "\x6e\xb7\xb1\x79\xa5\xc5\xb4\x46"
+ "\x6a\x9e\xc1\x54\xc3\xbf\x1c\x13"
+ "\xef\xd6\xec\x0d\x82\xb0\x2c\x29"
+ "\xaf\x2c\x69\x02\x99\xed\xc4\x53",
+ .expectedlen = 64
+ }
+};
+
/* Cast5 test vectors from RFC 2144 */
static const struct cipher_testvec cast5_tv_template[] = {
{
--
2.20.1





2019-01-11 21:47:52

by Stephan Müller

[permalink] [raw]
Subject: [PATCH 2/6] crypto: kdf - SP800-108 Key Derivation Function

The SP800-108 compliant Key Derivation Function is implemented as a
random number generator considering that it behaves like a deterministic
RNG.

All three KDF types specified in SP800-108 are implemented.

The code comments provide details about how to invoke the different KDF
types.

Signed-off-by: Stephan Mueller <[email protected]>
---
crypto/Kconfig | 7 +
crypto/Makefile | 1 +
crypto/kdf.c | 492 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 500 insertions(+)
create mode 100644 crypto/kdf.c

diff --git a/crypto/Kconfig b/crypto/Kconfig
index 86960aa53e0f..cc80d89e0cf5 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -561,6 +561,13 @@ config CRYPTO_HMAC
HMAC: Keyed-Hashing for Message Authentication (RFC2104).
This is required for IPSec.

+config CRYPTO_KDF
+ tristate "Key Derivation Function (SP800-108)"
+ select CRYPTO_RNG
+ help
+ Support for KDF compliant to SP800-108. All three types of
+ KDF specified in SP800-108 are implemented.
+
config CRYPTO_XCBC
tristate "XCBC support"
select CRYPTO_HASH
diff --git a/crypto/Makefile b/crypto/Makefile
index 799ed5e94606..69a0bb64b0ac 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -58,6 +58,7 @@ crypto_user-y := crypto_user_base.o
crypto_user-$(CONFIG_CRYPTO_STATS) += crypto_user_stat.o
obj-$(CONFIG_CRYPTO_CMAC) += cmac.o
obj-$(CONFIG_CRYPTO_HMAC) += hmac.o
+obj-$(CONFIG_CRYPTO_KDF) += kdf.o
obj-$(CONFIG_CRYPTO_VMAC) += vmac.o
obj-$(CONFIG_CRYPTO_XCBC) += xcbc.o
obj-$(CONFIG_CRYPTO_NULL2) += crypto_null.o
diff --git a/crypto/kdf.c b/crypto/kdf.c
new file mode 100644
index 000000000000..2c6dd8676a9f
--- /dev/null
+++ b/crypto/kdf.c
@@ -0,0 +1,492 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * SP800-108 Key-derivation function
+ *
+ * Copyright (C) 2019, Stephan Mueller <[email protected]>
+ */
+
+/*
+ * For performing a KDF operation, the following input is required
+ * from the caller:
+ *
+ * * Keying material to be used to derive the new keys from
+ * (denoted as Ko in SP800-108)
+ * * Label -- a free form binary string
+ * * Context -- a free form binary string
+ *
+ * The KDF is implemented as a random number generator.
+ *
+ * The Ko keying material is to be provided with the initialization of the KDF
+ * "random number generator", i.e. with the crypto_rng_reset function.
+ *
+ * The Label and Context concatenated string is provided when obtaining random
+ * numbers, i.e. with the crypto_rng_generate function. The caller must format
+ * the free-form Label || Context input as deemed necessary for the given
+ * purpose. Note, SP800-108 mandates that the Label and Context are separated
+ * by a 0x00 byte, i.e. the caller shall provide the input as
+ * Label || 0x00 || Context when trying to be compliant to SP800-108. For
+ * the feedback KDF, an IV is required as documented below.
+ *
+ * Example without proper error handling:
+ * char *keying_material = "\x00\x11\x22\x33\x44\x55\x66\x77";
+ * char *label_context = "\xde\xad\xbe\xef\x00\xde\xad\xbe\xef";
+ * kdf = crypto_alloc_rng(name, 0, 0);
+ * crypto_rng_reset(kdf, keying_material, 8);
+ * crypto_rng_generate(kdf, label_context, 9, outbuf, outbuflen);
+ *
+ * NOTE: In-place cipher operations are not supported.
+ */
+
+#include <linux/module.h>
+#include <crypto/rng.h>
+#include <crypto/internal/rng.h>
+#include <crypto/hash.h>
+#include <crypto/internal/hash.h>
+
+struct crypto_kdf_ctx {
+ struct crypto_shash *kmd;
+};
+
+#define CRYPTO_KDF_MAX_DIGESTSIZE 64
+#define CRYPTO_KDF_MAX_ALIGNMASK 0x3f
+
+static inline void crypto_kdf_init_desc(struct shash_desc *desc,
+ struct crypto_shash *kmd)
+{
+ desc->tfm = kmd;
+ desc->flags = crypto_shash_get_flags(kmd) & CRYPTO_TFM_REQ_MAY_SLEEP;
+}
+
+/*
+ * Implementation of the KDF in double pipeline iteration mode according with
+ * counter to SP800-108 section 5.3.
+ *
+ * The caller must provide Label || 0x00 || Context in src. This src pointer
+ * may also be NULL if the caller wishes not to provide anything.
+ */
+static int crypto_kdf_dpi_random(struct crypto_rng *rng,
+ const u8 *src, unsigned int slen,
+ u8 *dst, unsigned int dlen)
+{
+ struct crypto_kdf_ctx *ctx = crypto_tfm_ctx(crypto_rng_tfm(rng));
+ struct crypto_shash *kmd = ctx->kmd;
+ SHASH_DESC_ON_STACK(desc, kmd);
+ __be32 counter = cpu_to_be32(1);
+ unsigned int h = crypto_shash_digestsize(kmd);
+ unsigned int alignmask = crypto_shash_alignmask(kmd);
+ int err = 0;
+ u8 *dst_orig = dst;
+ u8 Aiblock[CRYPTO_KDF_MAX_DIGESTSIZE + CRYPTO_KDF_MAX_ALIGNMASK];
+ u8 *Ai = PTR_ALIGN((u8 *)Aiblock, alignmask + 1);
+
+ crypto_kdf_init_desc(desc, kmd);
+
+ memset(Ai, 0, h);
+
+ while (dlen) {
+ /* Calculate A(i) */
+ if (dst == dst_orig && src && slen)
+ /* 5.3 step 4 and 5.a */
+ err = crypto_shash_digest(desc, src, slen, Ai);
+ else
+ /* 5.3 step 5.a */
+ err = crypto_shash_digest(desc, Ai, h, Ai);
+ if (err)
+ goto out;
+
+ /* Calculate K(i) -- step 5.b */
+ err = crypto_shash_init(desc);
+ if (err)
+ goto out;
+
+ err = crypto_shash_update(desc, Ai, h);
+ if (err)
+ goto out;
+
+ err = crypto_shash_update(desc, (u8 *)&counter, sizeof(__be32));
+ if (err)
+ goto out;
+ if (src && slen) {
+ err = crypto_shash_update(desc, src, slen);
+ if (err)
+ goto out;
+ }
+
+ if (dlen < h) {
+ u8 tmpbuffer[CRYPTO_KDF_MAX_DIGESTSIZE];
+
+ err = crypto_shash_final(desc, tmpbuffer);
+ if (err)
+ goto out;
+ memcpy(dst, tmpbuffer, dlen);
+ memzero_explicit(tmpbuffer, h);
+ goto out;
+ } else {
+ err = crypto_shash_final(desc, dst);
+ if (err)
+ goto out;
+ dlen -= h;
+ dst += h;
+ counter = cpu_to_be32(be32_to_cpu(counter) + 1);
+ }
+ }
+
+out:
+ if (err)
+ memzero_explicit(dst_orig, dlen);
+ shash_desc_zero(desc);
+ memzero_explicit(Ai, h);
+ return err;
+}
+
+/*
+ * Implementation of the KDF in feedback mode with a non-NULL IV and with
+ * counter according to SP800-108 section 5.2. The IV is supplied with src
+ * and must be equal to the digestsize of the used cipher.
+ *
+ * In addition, the caller must provide Label || 0x00 || Context in src. This
+ * src pointer must not be NULL as the IV is required. The ultimate format of
+ * the src pointer is IV || Label || 0x00 || Context where the length of the
+ * IV is equal to the output size of the PRF.
+ */
+static int crypto_kdf_fb_random(struct crypto_rng *rng,
+ const u8 *src, unsigned int slen,
+ u8 *dst, unsigned int dlen)
+{
+ struct crypto_kdf_ctx *ctx = crypto_tfm_ctx(crypto_rng_tfm(rng));
+ struct crypto_shash *kmd = ctx->kmd;
+ SHASH_DESC_ON_STACK(desc, kmd);
+ __be32 counter = cpu_to_be32(1);
+ unsigned int h = crypto_shash_digestsize(kmd), labellen = 0;
+ int err = 0;
+ u8 *dst_orig = dst;
+ const u8 *label;
+
+ /* require the presence of an IV */
+ if (!src || slen < h)
+ return -EINVAL;
+
+ crypto_kdf_init_desc(desc, kmd);
+
+ /* calculate the offset of the label / context data */
+ label = src + h;
+ labellen = slen - h;
+
+ while (dlen) {
+ err = crypto_shash_init(desc);
+ if (err)
+ goto out;
+
+ /*
+ * Feedback mode applies to all rounds except first which uses
+ * the IV.
+ */
+ if (dst_orig == dst)
+ err = crypto_shash_update(desc, src, h);
+ else
+ err = crypto_shash_update(desc, dst - h, h);
+ if (err)
+ goto out;
+
+ err = crypto_shash_update(desc, (u8 *)&counter, sizeof(__be32));
+ if (err)
+ goto out;
+ if (labellen) {
+ err = crypto_shash_update(desc, label, labellen);
+ if (err)
+ goto out;
+ }
+
+ if (dlen < h) {
+ u8 tmpbuffer[CRYPTO_KDF_MAX_DIGESTSIZE];
+
+ err = crypto_shash_final(desc, tmpbuffer);
+ if (err)
+ goto out;
+ memcpy(dst, tmpbuffer, dlen);
+ memzero_explicit(tmpbuffer, h);
+ goto out;
+ } else {
+ err = crypto_shash_final(desc, dst);
+ if (err)
+ goto out;
+ dlen -= h;
+ dst += h;
+ counter = cpu_to_be32(be32_to_cpu(counter) + 1);
+ }
+ }
+
+out:
+ if (err)
+ memzero_explicit(dst_orig, dlen);
+ return err;
+}
+
+/*
+ * Implementation of the KDF in counter mode according to SP800-108 section 5.1
+ * as well as SP800-56A section 5.8.1 (Single-step KDF).
+ *
+ * SP800-108:
+ * The caller must provide Label || 0x00 || Context in src. This src pointer
+ * may also be NULL if the caller wishes not to provide anything.
+ *
+ * SP800-56A:
+ * The key provided for the HMAC during the crypto_rng_reset shall NOT be the
+ * shared secret from the DH operation, but an independently generated key.
+ * The src pointer is defined as Z || other info where Z is the shared secret
+ * from DH and other info is an arbitrary string (see SP800-56A section
+ * 5.8.1.2).
+ */
+static int crypto_kdf_ctr_random(struct crypto_rng *rng,
+ const u8 *src, unsigned int slen,
+ u8 *dst, unsigned int dlen)
+{
+ struct crypto_kdf_ctx *ctx = crypto_tfm_ctx(crypto_rng_tfm(rng));
+ struct crypto_shash *kmd = ctx->kmd;
+ SHASH_DESC_ON_STACK(desc, kmd);
+ __be32 counter = cpu_to_be32(1);
+ unsigned int h = crypto_shash_digestsize(kmd);
+ int err = 0;
+ u8 *dst_orig = dst;
+
+ crypto_kdf_init_desc(desc, kmd);
+
+ while (dlen) {
+ err = crypto_shash_init(desc);
+ if (err)
+ goto out;
+
+ err = crypto_shash_update(desc, (u8 *)&counter, sizeof(__be32));
+ if (err)
+ goto out;
+
+ if (src && slen) {
+ err = crypto_shash_update(desc, src, slen);
+ if (err)
+ goto out;
+ }
+
+ if (dlen < h) {
+ u8 tmpbuffer[CRYPTO_KDF_MAX_DIGESTSIZE];
+
+ err = crypto_shash_final(desc, tmpbuffer);
+ if (err)
+ goto out;
+ memcpy(dst, tmpbuffer, dlen);
+ memzero_explicit(tmpbuffer, h);
+ return 0;
+ } else {
+ err = crypto_shash_final(desc, dst);
+ if (err)
+ goto out;
+
+ dlen -= h;
+ dst += h;
+ counter = cpu_to_be32(be32_to_cpu(counter) + 1);
+ }
+ }
+
+out:
+ if (err)
+ memzero_explicit(dst_orig, dlen);
+ shash_desc_zero(desc);
+ return err;
+}
+
+/*
+ * The seeding of the KDF allows to set a key which must be at least
+ * digestsize long.
+ */
+static int crypto_kdf_seed(struct crypto_rng *rng,
+ const u8 *seed, unsigned int slen)
+{
+ struct crypto_kdf_ctx *ctx = crypto_tfm_ctx(crypto_rng_tfm(rng));
+ unsigned int ds = crypto_shash_digestsize(ctx->kmd);
+
+ /* Check according to SP800-108 section 7.2 */
+ if (ds > slen)
+ return -EINVAL;
+
+ /*
+ * We require that we operate on a MAC -- if we do not operate on a
+ * MAC, this function returns an error.
+ */
+ return crypto_shash_setkey(ctx->kmd, seed, slen);
+}
+
+static int crypto_kdf_init_tfm(struct crypto_tfm *tfm)
+{
+ struct crypto_instance *inst = crypto_tfm_alg_instance(tfm);
+ struct crypto_shash_spawn *spawn = crypto_instance_ctx(inst);
+ struct crypto_kdf_ctx *ctx = crypto_tfm_ctx(tfm);
+ struct crypto_shash *kmd;
+ unsigned int ds;
+
+ kmd = crypto_spawn_shash(spawn);
+ if (IS_ERR(kmd))
+ return PTR_ERR(kmd);
+
+ ds = crypto_shash_digestsize(kmd);
+ /* Hashes with no digest size are not allowed for KDFs. */
+ if (!ds || ds > CRYPTO_KDF_MAX_DIGESTSIZE ||
+ CRYPTO_KDF_MAX_ALIGNMASK < crypto_shash_alignmask(kmd)) {
+ crypto_free_shash(kmd);
+ return -EOPNOTSUPP;
+ }
+
+ ctx->kmd = kmd;
+
+ return 0;
+}
+
+static void crypto_kdf_exit_tfm(struct crypto_tfm *tfm)
+{
+ struct crypto_kdf_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ crypto_free_shash(ctx->kmd);
+}
+
+static void crypto_kdf_free(struct rng_instance *inst)
+{
+ crypto_drop_spawn(rng_instance_ctx(inst));
+ kfree(inst);
+}
+
+static int crypto_kdf_alloc_common(struct crypto_template *tmpl,
+ struct rtattr **tb,
+ const u8 *name,
+ int (*generate)(struct crypto_rng *tfm,
+ const u8 *src,
+ unsigned int slen,
+ u8 *dst, unsigned int dlen))
+{
+ struct rng_instance *inst;
+ struct crypto_alg *alg;
+ struct shash_alg *salg;
+ int err;
+ unsigned int ds, ss;
+
+ err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_RNG);
+ if (err)
+ return err;
+
+ salg = shash_attr_alg(tb[1], 0, 0);
+ if (IS_ERR(salg))
+ return PTR_ERR(salg);
+
+ ds = salg->digestsize;
+ ss = salg->statesize;
+ alg = &salg->base;
+
+ inst = rng_alloc_instance(name, alg);
+ err = PTR_ERR(inst);
+ if (IS_ERR(inst))
+ goto out_put_alg;
+
+ err = crypto_init_shash_spawn(rng_instance_ctx(inst), salg,
+ rng_crypto_instance(inst));
+ if (err)
+ goto out_free_inst;
+
+ inst->alg.base.cra_priority = alg->cra_priority;
+ inst->alg.base.cra_blocksize = alg->cra_blocksize;
+ inst->alg.base.cra_alignmask = alg->cra_alignmask;
+
+ inst->alg.generate = generate;
+ inst->alg.seed = crypto_kdf_seed;
+ inst->alg.seedsize = ds;
+
+ inst->alg.base.cra_init = crypto_kdf_init_tfm;
+ inst->alg.base.cra_exit = crypto_kdf_exit_tfm;
+ inst->alg.base.cra_ctxsize = ALIGN(sizeof(struct crypto_kdf_ctx) +
+ ss * 2, crypto_tfm_ctx_alignment());
+
+ inst->free = crypto_kdf_free;
+
+ err = rng_register_instance(tmpl, inst);
+
+ if (err) {
+out_free_inst:
+ crypto_kdf_free(inst);
+ }
+
+out_put_alg:
+ crypto_mod_put(alg);
+ return err;
+}
+
+static int crypto_kdf_ctr_create(struct crypto_template *tmpl,
+ struct rtattr **tb)
+{
+ return crypto_kdf_alloc_common(tmpl, tb, "kdf_ctr",
+ crypto_kdf_ctr_random);
+}
+
+static struct crypto_template crypto_kdf_ctr_tmpl = {
+ .name = "kdf_ctr",
+ .create = crypto_kdf_ctr_create,
+ .module = THIS_MODULE,
+};
+
+static int crypto_kdf_fb_create(struct crypto_template *tmpl,
+ struct rtattr **tb) {
+ return crypto_kdf_alloc_common(tmpl, tb, "kdf_fb",
+ crypto_kdf_fb_random);
+}
+
+static struct crypto_template crypto_kdf_fb_tmpl = {
+ .name = "kdf_fb",
+ .create = crypto_kdf_fb_create,
+ .module = THIS_MODULE,
+};
+
+static int crypto_kdf_dpi_create(struct crypto_template *tmpl,
+ struct rtattr **tb) {
+ return crypto_kdf_alloc_common(tmpl, tb, "kdf_dpi",
+ crypto_kdf_dpi_random);
+}
+
+static struct crypto_template crypto_kdf_dpi_tmpl = {
+ .name = "kdf_dpi",
+ .create = crypto_kdf_dpi_create,
+ .module = THIS_MODULE,
+};
+
+static int __init crypto_kdf_init(void)
+{
+ int err = crypto_register_template(&crypto_kdf_ctr_tmpl);
+
+ if (err)
+ return err;
+
+ err = crypto_register_template(&crypto_kdf_fb_tmpl);
+ if (err) {
+ crypto_unregister_template(&crypto_kdf_ctr_tmpl);
+ return err;
+ }
+
+ err = crypto_register_template(&crypto_kdf_dpi_tmpl);
+ if (err) {
+ crypto_unregister_template(&crypto_kdf_ctr_tmpl);
+ crypto_unregister_template(&crypto_kdf_fb_tmpl);
+ }
+ return err;
+}
+
+static void __exit crypto_kdf_exit(void)
+{
+ crypto_unregister_template(&crypto_kdf_ctr_tmpl);
+ crypto_unregister_template(&crypto_kdf_fb_tmpl);
+ crypto_unregister_template(&crypto_kdf_dpi_tmpl);
+}
+
+module_init(crypto_kdf_init);
+module_exit(crypto_kdf_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Stephan Mueller <[email protected]>");
+MODULE_DESCRIPTION("Key Derivation Function according to SP800-108");
+MODULE_ALIAS_CRYPTO("kdf_ctr");
+MODULE_ALIAS_CRYPTO("kdf_fb");
+MODULE_ALIAS_CRYPTO("kdf_dpi");
--
2.20.1





2019-01-11 21:48:01

by Stephan Müller

[permalink] [raw]
Subject: [PATCH 0/6] General Key Derivation Function Support

Hi Herbert, Eric,

key derivation functions behave like a random number generator requiring
a seed and can generate arbitrarily-sized bit sequences. As KDFs wrap
ciphers, the first patch adds template support for the RNG part of the
kernel crypto API. This allows the KDFs to be implemented as templates.

The patches two through five add different KDFs. The immediate use in
the kernel are:

- SP800-108: security/keys/dh.c

- HKDF: As Eric Biggers outlined, he wants to use HKDF for Ext4 FBE

Other areas of the kernel implement KDFs which should be migrated to the
common code base offered with this patch.

The last patch adds the KDF invocation to tcrypt to allow an immediate
test invocation of the KDFs which is at least needed for FIPS 140-2
compliance where the tcrypt module is insmod'ed during boot time to
trigger the self tests.

Eric, considering your request for supporting parallel use of HKDF, I
instantiate the HMAC twice: once for the extract and once for the expand
phase. The idea is that an extract operation does not collide too much
with the expand operation (when using one instance of HMAC, the extract
phase would invoke setkey with the salt and thus affect the expand phase).
Though, the final extract phase setkey call with the PRK is non-atomic (at
least in the software HMAC implementation). Thus, the caller would need
to guarantee to invoke the extract phase while no expand phase operation
is performed. So, maintaining two HMAC instances is not really required
after all. What is your take on that?

Stephan Mueller (6):
crypto: add template handling for RNGs
crypto: kdf - SP800-108 Key Derivation Function
crypto: kdf - add known answer tests
crypto: hkdf - RFC5869 Key Derivation Function
crypto: hkdf - add known answer tests
crypto: tcrypt - add KDF test invocation

crypto/Kconfig | 13 +
crypto/Makefile | 2 +
crypto/hkdf.c | 290 ++++++++++++++++++++
crypto/kdf.c | 492 ++++++++++++++++++++++++++++++++++
crypto/rng.c | 44 +++
crypto/tcrypt.c | 8 +
crypto/testmgr.c | 258 ++++++++++++++++++
crypto/testmgr.h | 225 ++++++++++++++++
include/crypto/internal/rng.h | 26 ++
9 files changed, 1358 insertions(+)
create mode 100644 crypto/hkdf.c
create mode 100644 crypto/kdf.c

--
2.20.1





2019-01-11 21:48:19

by Stephan Müller

[permalink] [raw]
Subject: [PATCH 1/6] crypto: add template handling for RNGs

Add ability to register templates for RNGs. RNGs are
"meta" mechanisms using raw cipher primitives. Thus, RNGs can now be
implemented as templates to allow the complete flexibility the kernel
crypto API provides.

Signed-off-by: Stephan Mueller <[email protected]>
---
crypto/rng.c | 44 +++++++++++++++++++++++++++++++++++
include/crypto/internal/rng.h | 26 +++++++++++++++++++++
2 files changed, 70 insertions(+)

diff --git a/crypto/rng.c b/crypto/rng.c
index 33c38a72bff5..da4fd03c0acd 100644
--- a/crypto/rng.c
+++ b/crypto/rng.c
@@ -64,6 +64,25 @@ static int crypto_rng_init_tfm(struct crypto_tfm *tfm)
return 0;
}

+static inline struct rng_alg *__crypto_rng_alg(struct crypto_alg *alg)
+{
+ return container_of(alg, struct rng_alg, base);
+}
+
+static inline struct rng_instance *rng_instance(
+ struct crypto_instance *inst)
+{
+ return container_of(__crypto_rng_alg(&inst->alg),
+ struct rng_instance, alg);
+}
+
+static void crypto_rng_free_instance(struct crypto_instance *inst)
+{
+ struct rng_instance *rng = rng_instance(inst);
+
+ rng->free(rng);
+}
+
static unsigned int seedsize(struct crypto_alg *alg)
{
struct rng_alg *ralg = container_of(alg, struct rng_alg, base);
@@ -102,6 +121,7 @@ static void crypto_rng_show(struct seq_file *m, struct crypto_alg *alg)
static const struct crypto_type crypto_rng_type = {
.extsize = crypto_alg_extsize,
.init_tfm = crypto_rng_init_tfm,
+ .free = crypto_rng_free_instance,
#ifdef CONFIG_PROC_FS
.show = crypto_rng_show,
#endif
@@ -229,5 +249,29 @@ void crypto_unregister_rngs(struct rng_alg *algs, int count)
}
EXPORT_SYMBOL_GPL(crypto_unregister_rngs);

+static int rng_prepare_alg(struct rng_alg *alg)
+{
+ struct crypto_alg *base = &alg->base;
+
+ base->cra_type = &crypto_rng_type;
+ base->cra_flags &= ~CRYPTO_ALG_TYPE_MASK;
+ base->cra_flags |= CRYPTO_ALG_TYPE_RNG;
+
+ return 0;
+}
+
+int rng_register_instance(struct crypto_template *tmpl,
+ struct rng_instance *inst)
+{
+ int err;
+
+ err = rng_prepare_alg(&inst->alg);
+ if (err)
+ return err;
+
+ return crypto_register_instance(tmpl, rng_crypto_instance(inst));
+}
+EXPORT_SYMBOL_GPL(rng_register_instance);
+
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Random Number Generator");
diff --git a/include/crypto/internal/rng.h b/include/crypto/internal/rng.h
index a52ef3483dd7..bfe4482ad336 100644
--- a/include/crypto/internal/rng.h
+++ b/include/crypto/internal/rng.h
@@ -42,4 +42,30 @@ static inline void crypto_rng_set_entropy(struct crypto_rng *tfm,
crypto_rng_alg(tfm)->set_ent(tfm, data, len);
}

+struct rng_instance {
+ void (*free)(struct rng_instance *inst);
+ struct rng_alg alg;
+};
+
+static inline struct rng_instance *rng_alloc_instance(
+ const char *name, struct crypto_alg *alg)
+{
+ return crypto_alloc_instance(name, alg,
+ sizeof(struct rng_instance) - sizeof(*alg));
+}
+
+static inline struct crypto_instance *rng_crypto_instance(
+ struct rng_instance *inst)
+{
+ return container_of(&inst->alg.base, struct crypto_instance, alg);
+}
+
+static inline void *rng_instance_ctx(struct rng_instance *inst)
+{
+ return crypto_instance_ctx(rng_crypto_instance(inst));
+}
+
+int rng_register_instance(struct crypto_template *tmpl,
+ struct rng_instance *inst);
+
#endif
--
2.20.1





2019-01-11 21:48:47

by Stephan Müller

[permalink] [raw]
Subject: [PATCH 4/6] crypto: hkdf - RFC5869 Key Derivation Function

The RFC5869 compliant Key Derivation Function is implemented as a
random number generator considering that it behaves like a deterministic
RNG.

The extract and expand phases use different instances of the underlying
keyed message digest cipher to ensure that while the extraction phase
generates a new key for the expansion phase, the cipher for the
expansion phase can still be used. This approach is intended to aid
multi-threaded uses cases.

Signed-off-by: Stephan Mueller <[email protected]>
---
crypto/Kconfig | 6 +
crypto/Makefile | 1 +
crypto/hkdf.c | 290 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 297 insertions(+)
create mode 100644 crypto/hkdf.c

diff --git a/crypto/Kconfig b/crypto/Kconfig
index cc80d89e0cf5..0eee5e129fa3 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -568,6 +568,12 @@ config CRYPTO_KDF
Support for KDF compliant to SP800-108. All three types of
KDF specified in SP800-108 are implemented.

+config CRYPTO_HKDF
+ tristate "HMAC-based Extract-and expand Key Derivation Function"
+ select CRYPTO_RNG
+ help
+ Support for KDF compliant to RFC5869.
+
config CRYPTO_XCBC
tristate "XCBC support"
select CRYPTO_HASH
diff --git a/crypto/Makefile b/crypto/Makefile
index 69a0bb64b0ac..6bbb0a4dea13 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -59,6 +59,7 @@ crypto_user-$(CONFIG_CRYPTO_STATS) += crypto_user_stat.o
obj-$(CONFIG_CRYPTO_CMAC) += cmac.o
obj-$(CONFIG_CRYPTO_HMAC) += hmac.o
obj-$(CONFIG_CRYPTO_KDF) += kdf.o
+obj-$(CONFIG_CRYPTO_HKDF) += hkdf.o
obj-$(CONFIG_CRYPTO_VMAC) += vmac.o
obj-$(CONFIG_CRYPTO_XCBC) += xcbc.o
obj-$(CONFIG_CRYPTO_NULL2) += crypto_null.o
diff --git a/crypto/hkdf.c b/crypto/hkdf.c
new file mode 100644
index 000000000000..35a975ed64a8
--- /dev/null
+++ b/crypto/hkdf.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * RFC 5869 Key-derivation function
+ *
+ * Copyright (C) 2019, Stephan Mueller <[email protected]>
+ */
+
+/*
+ * The HKDF extract phase is applied with crypto_rng_reset().
+ * The HKDF expand phase is applied with crypto_rng_generate().
+ *
+ * NOTE: In-place cipher operations are not supported.
+ */
+
+#include <linux/module.h>
+#include <crypto/rng.h>
+#include <crypto/internal/rng.h>
+#include <crypto/hash.h>
+#include <crypto/internal/hash.h>
+#include <linux/rtnetlink.h>
+
+struct crypto_hkdf_ctx {
+ struct crypto_shash *extract_kmd;
+ struct crypto_shash *expand_kmd;
+};
+
+#define CRYPTO_HKDF_MAX_DIGESTSIZE 64
+
+/*
+ * HKDF expand phase
+ */
+static int crypto_hkdf_random(struct crypto_rng *rng,
+ const u8 *info, unsigned int infolen,
+ u8 *dst, unsigned int dlen)
+{
+ struct crypto_hkdf_ctx *ctx = crypto_tfm_ctx(crypto_rng_tfm(rng));
+ struct crypto_shash *expand_kmd = ctx->expand_kmd;
+ SHASH_DESC_ON_STACK(desc, expand_kmd);
+ unsigned int h = crypto_shash_digestsize(expand_kmd);
+ int err = 0;
+ u8 *dst_orig = dst;
+ const u8 *prev = NULL;
+ uint8_t ctr = 0x01;
+
+ if (dlen > h * 255)
+ return -EINVAL;
+
+ desc->tfm = expand_kmd;
+ desc->flags = crypto_shash_get_flags(expand_kmd) &
+ CRYPTO_TFM_REQ_MAY_SLEEP;
+
+ /* T(1) and following */
+ while (dlen) {
+ err = crypto_shash_init(desc);
+ if (err)
+ goto out;
+
+ if (prev) {
+ err = crypto_shash_update(desc, prev, h);
+ if (err)
+ goto out;
+ }
+
+ if (info) {
+ err = crypto_shash_update(desc, info, infolen);
+ if (err)
+ goto out;
+ }
+
+ if (dlen < h) {
+ u8 tmpbuffer[CRYPTO_HKDF_MAX_DIGESTSIZE];
+
+ err = crypto_shash_finup(desc, &ctr, 1, tmpbuffer);
+ if (err)
+ goto out;
+ memcpy(dst, tmpbuffer, dlen);
+ memzero_explicit(tmpbuffer, h);
+ goto out;
+ } else {
+ err = crypto_shash_finup(desc, &ctr, 1, dst);
+ if (err)
+ goto out;
+
+ prev = dst;
+ dst += h;
+ dlen -= h;
+ ctr++;
+ }
+ }
+
+out:
+ if (err)
+ memzero_explicit(dst_orig, dlen);
+ shash_desc_zero(desc);
+ return err;
+}
+
+/*
+ * HKDF extract phase.
+ *
+ * The seed is defined to be a concatenation of the salt and the IKM.
+ * The data buffer is pre-pended by an rtattr which provides an u32 value
+ * with the length of the salt. Thus, the buffer length - salt length is the
+ * IKM length.
+ */
+static int crypto_hkdf_seed(struct crypto_rng *rng,
+ const u8 *seed, unsigned int slen)
+{
+ struct crypto_hkdf_ctx *ctx = crypto_tfm_ctx(crypto_rng_tfm(rng));
+ struct crypto_shash *extract_kmd = ctx->extract_kmd;
+ struct crypto_shash *expand_kmd = ctx->expand_kmd;
+ struct rtattr *rta = (struct rtattr *)seed;
+ SHASH_DESC_ON_STACK(desc, extract_kmd);
+ u32 saltlen;
+ unsigned int h = crypto_shash_digestsize(extract_kmd);
+ int err;
+ const uint8_t null_salt[CRYPTO_HKDF_MAX_DIGESTSIZE] = { 0 };
+ u8 prk[CRYPTO_HKDF_MAX_DIGESTSIZE] = { 0 };
+
+ /* Require aligned buffer to directly read out saltlen below */
+ if (WARN_ON((unsigned long)seed & (sizeof(saltlen) - 1)))
+ return -EINVAL;
+
+ if (!RTA_OK(rta, slen))
+ return -EINVAL;
+ if (rta->rta_type != 1)
+ return -EINVAL;
+ if (RTA_PAYLOAD(rta) < sizeof(saltlen))
+ return -EINVAL;
+ saltlen = *((u32 *)RTA_DATA(rta));
+
+ seed += RTA_ALIGN(rta->rta_len);
+ slen -= RTA_ALIGN(rta->rta_len);
+
+ if (slen < saltlen)
+ return -EINVAL;
+
+ desc->tfm = extract_kmd;
+
+ /* Set the salt as HMAC key */
+ if (saltlen)
+ err = crypto_shash_setkey(extract_kmd, seed, saltlen);
+ else
+ err = crypto_shash_setkey(extract_kmd, null_salt, h);
+ if (err)
+ return err;
+
+ /* Extract the PRK */
+ err = crypto_shash_digest(desc, seed + saltlen, slen - saltlen, prk);
+ if (err)
+ goto err;
+
+ /* Set the PRK for the expand phase */
+ err = crypto_shash_setkey(expand_kmd, prk, h);
+ if (err)
+ goto err;
+
+err:
+ shash_desc_zero(desc);
+ memzero_explicit(prk, h);
+ return err;
+}
+
+static int crypto_hkdf_init_tfm(struct crypto_tfm *tfm)
+{
+ struct crypto_instance *inst = crypto_tfm_alg_instance(tfm);
+ struct crypto_shash_spawn *spawn = crypto_instance_ctx(inst);
+ struct crypto_hkdf_ctx *ctx = crypto_tfm_ctx(tfm);
+ struct crypto_shash *extract_kmd = NULL, *expand_kmd = NULL;
+ unsigned int ds;
+
+ extract_kmd = crypto_spawn_shash(spawn);
+ if (IS_ERR(extract_kmd))
+ return PTR_ERR(extract_kmd);
+
+ expand_kmd = crypto_spawn_shash(spawn);
+ if (IS_ERR(expand_kmd)) {
+ crypto_free_shash(extract_kmd);
+ return PTR_ERR(expand_kmd);
+ }
+
+ ds = crypto_shash_digestsize(extract_kmd);
+ /* Hashes with no digest size are not allowed for KDFs. */
+ if (!ds || ds > CRYPTO_HKDF_MAX_DIGESTSIZE) {
+ crypto_free_shash(extract_kmd);
+ crypto_free_shash(expand_kmd);
+ return -EOPNOTSUPP;
+ }
+
+ ctx->extract_kmd = extract_kmd;
+ ctx->expand_kmd = expand_kmd;
+
+ return 0;
+}
+
+static void crypto_hkdf_exit_tfm(struct crypto_tfm *tfm)
+{
+ struct crypto_hkdf_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ crypto_free_shash(ctx->extract_kmd);
+ crypto_free_shash(ctx->expand_kmd);
+}
+
+static void crypto_kdf_free(struct rng_instance *inst)
+{
+ crypto_drop_spawn(rng_instance_ctx(inst));
+ kfree(inst);
+}
+
+static int crypto_hkdf_create(struct crypto_template *tmpl, struct rtattr **tb)
+{
+ struct rng_instance *inst;
+ struct crypto_alg *alg;
+ struct shash_alg *salg;
+ int err;
+ unsigned int ds, ss;
+
+ err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_RNG);
+ if (err)
+ return err;
+
+ salg = shash_attr_alg(tb[1], 0, 0);
+ if (IS_ERR(salg))
+ return PTR_ERR(salg);
+
+ ds = salg->digestsize;
+ ss = salg->statesize;
+ alg = &salg->base;
+
+ inst = rng_alloc_instance("hkdf", alg);
+ err = PTR_ERR(inst);
+ if (IS_ERR(inst))
+ goto out_put_alg;
+
+ err = crypto_init_shash_spawn(rng_instance_ctx(inst), salg,
+ rng_crypto_instance(inst));
+ if (err)
+ goto out_free_inst;
+
+ inst->alg.base.cra_priority = alg->cra_priority;
+ inst->alg.base.cra_blocksize = alg->cra_blocksize;
+ inst->alg.base.cra_alignmask = alg->cra_alignmask;
+
+ inst->alg.generate = crypto_hkdf_random;
+ inst->alg.seed = crypto_hkdf_seed;
+ inst->alg.seedsize = ds;
+
+ inst->alg.base.cra_init = crypto_hkdf_init_tfm;
+ inst->alg.base.cra_exit = crypto_hkdf_exit_tfm;
+ inst->alg.base.cra_ctxsize = ALIGN(sizeof(struct crypto_hkdf_ctx) +
+ ss * 2, crypto_tfm_ctx_alignment());
+
+ inst->free = crypto_kdf_free;
+
+ err = rng_register_instance(tmpl, inst);
+
+ if (err) {
+out_free_inst:
+ crypto_kdf_free(inst);
+ }
+
+out_put_alg:
+ crypto_mod_put(alg);
+ return err;
+}
+
+static struct crypto_template crypto_hkdf_tmpl = {
+ .name = "hkdf",
+ .create = crypto_hkdf_create,
+ .module = THIS_MODULE,
+};
+
+static int __init crypto_hkdf_init(void)
+{
+ return crypto_register_template(&crypto_hkdf_tmpl);
+}
+
+static void __exit crypto_hkdf_exit(void)
+{
+ crypto_unregister_template(&crypto_hkdf_tmpl);
+}
+
+module_init(crypto_hkdf_init);
+module_exit(crypto_hkdf_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Stephan Mueller <[email protected]>");
+MODULE_DESCRIPTION("Key Derivation Function according to RFC 5869");
+MODULE_ALIAS_CRYPTO("hkdf");
--
2.20.1





2019-01-12 05:20:38

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH 4/6] crypto: hkdf - RFC5869 Key Derivation Function

Hi Stephan,

On Fri, Jan 11, 2019 at 08:10:39PM +0100, Stephan M?ller wrote:
> The RFC5869 compliant Key Derivation Function is implemented as a
> random number generator considering that it behaves like a deterministic
> RNG.
>

Thanks for the proof of concept! I guess it ended up okay. But can you explain
more the benefits of using the crypto_rng interface, as opposed to just some
crypto_hkdf_*() helper functions that are exported for modules to use?

> The extract and expand phases use different instances of the underlying
> keyed message digest cipher to ensure that while the extraction phase
> generates a new key for the expansion phase, the cipher for the
> expansion phase can still be used. This approach is intended to aid
> multi-threaded uses cases.

I think you partially misunderstood what I was asking for. One HMAC tfm is
sufficient as long as HKDF-Expand is separated from HKDF-Extract, which you've
done. So just use one HMAC tfm, and in crypto_hkdf_seed() key it with the
'salt', and then afterwards with the 'prk'.

Also everywhere in this patchset, please avoid using the word "cipher" to refer
to algorithms that are not encryption/decryption. I know a lot of the crypto
API docs do this, but I think it is a mistake and confusing. Hash algorithms
and KDFs are not "ciphers".

>
> Signed-off-by: Stephan Mueller <[email protected]>
> ---
> crypto/Kconfig | 6 +
> crypto/Makefile | 1 +
> crypto/hkdf.c | 290 ++++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 297 insertions(+)
> create mode 100644 crypto/hkdf.c
>
> diff --git a/crypto/Kconfig b/crypto/Kconfig
> index cc80d89e0cf5..0eee5e129fa3 100644
> --- a/crypto/Kconfig
> +++ b/crypto/Kconfig
> @@ -568,6 +568,12 @@ config CRYPTO_KDF
> Support for KDF compliant to SP800-108. All three types of
> KDF specified in SP800-108 are implemented.
>
> +config CRYPTO_HKDF
> + tristate "HMAC-based Extract-and expand Key Derivation Function"
> + select CRYPTO_RNG
> + help
> + Support for KDF compliant to RFC5869.
> +

Make the description
"HMAC-based Extract-and-Expand Key Derivation Function (HKDF)"?
There is a missing dash, and probably the acronym "HKDF" should be in there.

> config CRYPTO_XCBC
> tristate "XCBC support"
> select CRYPTO_HASH
> diff --git a/crypto/Makefile b/crypto/Makefile
> index 69a0bb64b0ac..6bbb0a4dea13 100644
> --- a/crypto/Makefile
> +++ b/crypto/Makefile
> @@ -59,6 +59,7 @@ crypto_user-$(CONFIG_CRYPTO_STATS) += crypto_user_stat.o
> obj-$(CONFIG_CRYPTO_CMAC) += cmac.o
> obj-$(CONFIG_CRYPTO_HMAC) += hmac.o
> obj-$(CONFIG_CRYPTO_KDF) += kdf.o
> +obj-$(CONFIG_CRYPTO_HKDF) += hkdf.o
> obj-$(CONFIG_CRYPTO_VMAC) += vmac.o
> obj-$(CONFIG_CRYPTO_XCBC) += xcbc.o
> obj-$(CONFIG_CRYPTO_NULL2) += crypto_null.o
> diff --git a/crypto/hkdf.c b/crypto/hkdf.c
> new file mode 100644
> index 000000000000..35a975ed64a8
> --- /dev/null
> +++ b/crypto/hkdf.c
> @@ -0,0 +1,290 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/*
> + * RFC 5869 Key-derivation function
> + *

People don't know what RFC xyz is. Please be more explicit than just the RFC
number, e.g.

"HKDF: HMAC-based Extract-and-Expand Key Derivation Function (RFC 5869)"

> + * Copyright (C) 2019, Stephan Mueller <[email protected]>
> + */
> +
> +/*
> + * The HKDF extract phase is applied with crypto_rng_reset().
> + * The HKDF expand phase is applied with crypto_rng_generate().
> + *
> + * NOTE: In-place cipher operations are not supported.
> + */

What does an "in-place cipher operation" mean in this context? That the 'info'
buffer must not overlap the 'dst' buffer? Maybe crypto_rng_generate() should
check that for all crypto_rngs? Or is it different for different crypto_rngs?

> +
> +#include <linux/module.h>
> +#include <crypto/rng.h>
> +#include <crypto/internal/rng.h>
> +#include <crypto/hash.h>
> +#include <crypto/internal/hash.h>
> +#include <linux/rtnetlink.h>
> +
> +struct crypto_hkdf_ctx {
> + struct crypto_shash *extract_kmd;
> + struct crypto_shash *expand_kmd;
> +};
> +
> +#define CRYPTO_HKDF_MAX_DIGESTSIZE 64
> +
> +/*
> + * HKDF expand phase
> + */
> +static int crypto_hkdf_random(struct crypto_rng *rng,
> + const u8 *info, unsigned int infolen,
> + u8 *dst, unsigned int dlen)

Why call it crypto_hkdf_random() instead of crypto_hkdf_generate()? The latter
would match rng_alg::generate.

> +{
> + struct crypto_hkdf_ctx *ctx = crypto_tfm_ctx(crypto_rng_tfm(rng));

const

> + struct crypto_shash *expand_kmd = ctx->expand_kmd;
> + SHASH_DESC_ON_STACK(desc, expand_kmd);

> + unsigned int h = crypto_shash_digestsize(expand_kmd);

const

> + int err = 0;
> + u8 *dst_orig = dst;
> + const u8 *prev = NULL;

> + uint8_t ctr = 0x01;

u8 instead of uint8_t

> +
> + if (dlen > h * 255)
> + return -EINVAL;
> +
> + desc->tfm = expand_kmd;

> + desc->flags = crypto_shash_get_flags(expand_kmd) &
> + CRYPTO_TFM_REQ_MAY_SLEEP;

This line setting desc->flags doesn't make sense. How is the user meant to
control whether crypto_rng_generate() can sleep or not? Or can it always sleep?
Either way this part is wrong since the user can't get access to the HMAC tfm to
set this flag being checked for.

> +
> + /* T(1) and following */
> + while (dlen) {
> + err = crypto_shash_init(desc);
> + if (err)
> + goto out;
> +
> + if (prev) {
> + err = crypto_shash_update(desc, prev, h);
> + if (err)
> + goto out;
> + }
> +
> + if (info) {
> + err = crypto_shash_update(desc, info, infolen);
> + if (err)
> + goto out;
> + }
> +
> + if (dlen < h) {
> + u8 tmpbuffer[CRYPTO_HKDF_MAX_DIGESTSIZE];
> +
> + err = crypto_shash_finup(desc, &ctr, 1, tmpbuffer);
> + if (err)
> + goto out;
> + memcpy(dst, tmpbuffer, dlen);
> + memzero_explicit(tmpbuffer, h);
> + goto out;
> + } else {

No need for the 'else'.

> + err = crypto_shash_finup(desc, &ctr, 1, dst);
> + if (err)
> + goto out;
> +
> + prev = dst;
> + dst += h;
> + dlen -= h;
> + ctr++;
> + }
> + }
> +
> +out:
> + if (err)
> + memzero_explicit(dst_orig, dlen);
> + shash_desc_zero(desc);
> + return err;
> +}
> +
> +/*
> + * HKDF extract phase.
> + *
> + * The seed is defined to be a concatenation of the salt and the IKM.
> + * The data buffer is pre-pended by an rtattr which provides an u32 value
> + * with the length of the salt. Thus, the buffer length - salt length is the
> + * IKM length.
> + */
> +static int crypto_hkdf_seed(struct crypto_rng *rng,
> + const u8 *seed, unsigned int slen)
> +{
> + struct crypto_hkdf_ctx *ctx = crypto_tfm_ctx(crypto_rng_tfm(rng));

const

> + struct crypto_shash *extract_kmd = ctx->extract_kmd;
> + struct crypto_shash *expand_kmd = ctx->expand_kmd;
> + struct rtattr *rta = (struct rtattr *)seed;
> + SHASH_DESC_ON_STACK(desc, extract_kmd);
> + u32 saltlen;
> + unsigned int h = crypto_shash_digestsize(extract_kmd);
> + int err;
> + const uint8_t null_salt[CRYPTO_HKDF_MAX_DIGESTSIZE] = { 0 };

static const

> + u8 prk[CRYPTO_HKDF_MAX_DIGESTSIZE] = { 0 };

No need to initialize this.

> +
> + /* Require aligned buffer to directly read out saltlen below */
> + if (WARN_ON((unsigned long)seed & (sizeof(saltlen) - 1)))
> + return -EINVAL;
> +
> + if (!RTA_OK(rta, slen))
> + return -EINVAL;
> + if (rta->rta_type != 1)
> + return -EINVAL;
> + if (RTA_PAYLOAD(rta) < sizeof(saltlen))
> + return -EINVAL;
> + saltlen = *((u32 *)RTA_DATA(rta));

I'm guessing you copied the weird "length as a rtattr payload" approach from the
authenc template. I think it's not necessary. And it's overly error-prone, as
shown by the authenc template getting the parsing wrong for years and you making
the exact same mistake again here...
(See https://patchwork.kernel.org/patch/10732803/) How about just using a u32
at the beginning without the 'rtattr' preceding it?

Also it should use get_unaligned() so there is no alignment requirement the
caller has to comply with.

> +
> + seed += RTA_ALIGN(rta->rta_len);
> + slen -= RTA_ALIGN(rta->rta_len);
> +
> + if (slen < saltlen)
> + return -EINVAL;
> +

> + desc->tfm = extract_kmd;

desc->flags needs to be set.

> +
> + /* Set the salt as HMAC key */
> + if (saltlen)
> + err = crypto_shash_setkey(extract_kmd, seed, saltlen);
> + else
> + err = crypto_shash_setkey(extract_kmd, null_salt, h);
> + if (err)
> + return err;
> +
> + /* Extract the PRK */
> + err = crypto_shash_digest(desc, seed + saltlen, slen - saltlen, prk);
> + if (err)
> + goto err;
> +
> + /* Set the PRK for the expand phase */
> + err = crypto_shash_setkey(expand_kmd, prk, h);
> + if (err)
> + goto err;
> +
> +err:
> + shash_desc_zero(desc);
> + memzero_explicit(prk, h);
> + return err;
> +}
> +
> +static int crypto_hkdf_init_tfm(struct crypto_tfm *tfm)
> +{
> + struct crypto_instance *inst = crypto_tfm_alg_instance(tfm);
> + struct crypto_shash_spawn *spawn = crypto_instance_ctx(inst);
> + struct crypto_hkdf_ctx *ctx = crypto_tfm_ctx(tfm);
> + struct crypto_shash *extract_kmd = NULL, *expand_kmd = NULL;
> + unsigned int ds;
> +
> + extract_kmd = crypto_spawn_shash(spawn);
> + if (IS_ERR(extract_kmd))
> + return PTR_ERR(extract_kmd);
> +
> + expand_kmd = crypto_spawn_shash(spawn);
> + if (IS_ERR(expand_kmd)) {
> + crypto_free_shash(extract_kmd);
> + return PTR_ERR(expand_kmd);
> + }
> +
> + ds = crypto_shash_digestsize(extract_kmd);
> + /* Hashes with no digest size are not allowed for KDFs. */
> + if (!ds || ds > CRYPTO_HKDF_MAX_DIGESTSIZE) {
> + crypto_free_shash(extract_kmd);
> + crypto_free_shash(expand_kmd);
> + return -EOPNOTSUPP;
> + }

The digest size should be validated when instantiating the template, not here.

> +
> + ctx->extract_kmd = extract_kmd;
> + ctx->expand_kmd = expand_kmd;
> +
> + return 0;
> +}
> +
> +static void crypto_hkdf_exit_tfm(struct crypto_tfm *tfm)
> +{
> + struct crypto_hkdf_ctx *ctx = crypto_tfm_ctx(tfm);
> +
> + crypto_free_shash(ctx->extract_kmd);
> + crypto_free_shash(ctx->expand_kmd);
> +}
> +
> +static void crypto_kdf_free(struct rng_instance *inst)
> +{
> + crypto_drop_spawn(rng_instance_ctx(inst));
> + kfree(inst);
> +}
> +
> +static int crypto_hkdf_create(struct crypto_template *tmpl, struct rtattr **tb)
> +{
> + struct rng_instance *inst;
> + struct crypto_alg *alg;
> + struct shash_alg *salg;
> + int err;
> + unsigned int ds, ss;
> +
> + err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_RNG);
> + if (err)
> + return err;
> +
> + salg = shash_attr_alg(tb[1], 0, 0);
> + if (IS_ERR(salg))
> + return PTR_ERR(salg);
> +
> + ds = salg->digestsize;
> + ss = salg->statesize;

I don't see what the 'statesize' is needed for.

> + alg = &salg->base;

Check here that the underlying algorithm really is "hmac(" something?

Alternatively it may be a good idea to simplify usage by making the template
just take the unkeyed hash directly, like "hkdf(sha512)". And if any users
really need to specify a specific HMAC implementation then another template
usable as "hkdf_base(hmac(sha512))" could be added later.

> +
> + inst = rng_alloc_instance("hkdf", alg);
> + err = PTR_ERR(inst);
> + if (IS_ERR(inst))
> + goto out_put_alg;
> +
> + err = crypto_init_shash_spawn(rng_instance_ctx(inst), salg,
> + rng_crypto_instance(inst));
> + if (err)
> + goto out_free_inst;

This error path calls crypto_drop_spawn() without a prior successful
crypto_init_spawn().

> +
> + inst->alg.base.cra_priority = alg->cra_priority;
> + inst->alg.base.cra_blocksize = alg->cra_blocksize;
> + inst->alg.base.cra_alignmask = alg->cra_alignmask;
> +
> + inst->alg.generate = crypto_hkdf_random;
> + inst->alg.seed = crypto_hkdf_seed;
> + inst->alg.seedsize = ds;

What does the seedsize mean here, given that crypto_hkdf_seed() actually takes a
variable length seed?

> +
> + inst->alg.base.cra_init = crypto_hkdf_init_tfm;
> + inst->alg.base.cra_exit = crypto_hkdf_exit_tfm;

> + inst->alg.base.cra_ctxsize = ALIGN(sizeof(struct crypto_hkdf_ctx) +
> + ss * 2, crypto_tfm_ctx_alignment());

Why isn't this simply sizeof(struct crypto_hkdf_ctx)?

> +
> + inst->free = crypto_kdf_free;
> +
> + err = rng_register_instance(tmpl, inst);
> +
> + if (err) {
> +out_free_inst:
> + crypto_kdf_free(inst);
> + }
> +
> +out_put_alg:
> + crypto_mod_put(alg);
> + return err;
> +}
> +
> +static struct crypto_template crypto_hkdf_tmpl = {
> + .name = "hkdf",
> + .create = crypto_hkdf_create,
> + .module = THIS_MODULE,
> +};
> +
> +static int __init crypto_hkdf_init(void)
> +{
> + return crypto_register_template(&crypto_hkdf_tmpl);
> +}
> +
> +static void __exit crypto_hkdf_exit(void)
> +{
> + crypto_unregister_template(&crypto_hkdf_tmpl);
> +}
> +
> +module_init(crypto_hkdf_init);
> +module_exit(crypto_hkdf_exit);
> +
> +MODULE_LICENSE("GPL");

MODULE_LICENSE("GPL") means GPLv2+ but the SPDX header says GPLv2 only.

> +MODULE_AUTHOR("Stephan Mueller <[email protected]>");
> +MODULE_DESCRIPTION("Key Derivation Function according to RFC 5869");

Mention "HKDF" in the module description?

> +MODULE_ALIAS_CRYPTO("hkdf");
> --
> 2.20.1

Thanks!

- Eric

2019-01-12 05:20:40

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH 5/6] crypto: hkdf - add known answer tests

On Fri, Jan 11, 2019 at 08:10:56PM +0100, Stephan M?ller wrote:
> Add known answer tests to the testmgr for the HKDF (RFC5869) cipher.
>
> The known answer tests are derived from RFC 5869 appendix A.
>
> Note, the HKDF is considered to be a FIPS 140-2 allowed (not approved)
> cipher as of now. Yet, an allowed cipher is usable under FIPS 140-2
> rules.
>
> Signed-off-by: Stephan Mueller <[email protected]>
> ---
> crypto/testmgr.c | 32 +++++++++++++
> crypto/testmgr.h | 115 +++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 147 insertions(+)
>
> diff --git a/crypto/testmgr.c b/crypto/testmgr.c
> index ff9051bffa1f..aba7a3645293 100644
> --- a/crypto/testmgr.c
> +++ b/crypto/testmgr.c
> @@ -3187,6 +3187,38 @@ static const struct alg_test_desc alg_test_descs[] = {
> .suite = {
> .hash = __VECS(ghash_tv_template)
> }
> + }, {
> + .alg = "hkdf(hmac(sha1))",
> + .test = alg_test_kdf,
> + .fips_allowed = 1,
> + .suite = {
> + .kdf = {
> + .vecs = hkdf_hmac_sha1_tv_template,
> + .count = ARRAY_SIZE(hkdf_hmac_sha1_tv_template)

Use the __VECS() macro.

> + }
> + }
> + }, {
> + .alg = "hkdf(hmac(sha224))",
> + .test = alg_test_null,
> + .fips_allowed = 1,

I think it is dumb to add algorithms to the testmgr with no tests just so the
'fips_allowed' flag can be set. And doesn't FIPS sometimes require tests
anyway? I don't think the "null test" should count as a test :-)

Perhaps just include sha256 and sha512, and have tests for them?

> + }, {
> + .alg = "hkdf(hmac(sha256))",
> + .test = alg_test_kdf,
> + .fips_allowed = 1,
> + .suite = {
> + .kdf = {
> + .vecs = hkdf_hmac_sha256_tv_template,
> + .count = ARRAY_SIZE(hkdf_hmac_sha256_tv_template)
> + }
> + }
> + }, {
> + .alg = "hkdf(hmac(sha384))",
> + .test = alg_test_null,
> + .fips_allowed = 1,
> + }, {
> + .alg = "hkdf(hmac(sha512))",
> + .test = alg_test_null,
> + .fips_allowed = 1,
> }, {
> .alg = "hmac(md5)",
> .test = alg_test_hash,
> diff --git a/crypto/testmgr.h b/crypto/testmgr.h
> index a729b66f8757..7c4aa694e0f3 100644
> --- a/crypto/testmgr.h
> +++ b/crypto/testmgr.h
> @@ -28002,6 +28002,121 @@ static struct kdf_testvec kdf_dpi_hmac_sha256_tv_template[] = {
> }
> };
>
> +/* Test vectors from RFC 5869 appendix A */
> +static struct kdf_testvec hkdf_hmac_sha256_tv_template[] = {

const

Likewise for all other kdf_testvecs.

> + {
> + .K1 = (unsigned char *)

No need for this cast if you make the pointers const in struct kdf_testvec.

Likewise for all other testvecs.

> +#ifdef __LITTLE_ENDIAN
> + "\x08\x00" /* rta length */
> + "\x01\x00" /* rta type */
> + "\x0d\x00\x00\x00" /* salt length */
> +#else
> + "\x00\x08" /* rta length */
> + "\x00\x01" /* rta type */
> + "\x00\x00\x00\x0d" /* salt length */
> +#endif
> + "\x00\x01\x02\x03\x04\x05\x06\x07"
> + "\x08\x09\x0a\x0b\x0c" /* salt */
> + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
> + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
> + "\x0b\x0b\x0b\x0b\x0b\x0b", /* IKM */
> + .K1len = 43,
> + .context = (unsigned char *)
> + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7"
> + "\xf8\xf9",
> + .contextlen = 10,
> + .expected = (unsigned char *)
> + "\x3c\xb2\x5f\x25\xfa\xac\xd5\x7a"
> + "\x90\x43\x4f\x64\xd0\x36\x2f\x2a"
> + "\x2d\x2d\x0a\x90\xcf\x1a\x5a\x4c"
> + "\x5d\xb0\x2d\x56\xec\xc4\xc5\xbf"
> + "\x34\x00\x72\x08\xd5\xb8\x87\x18"
> + "\x58\x65",
> + .expectedlen = 42
> + }, {
> + .K1 = (unsigned char *)
> +#ifdef __LITTLE_ENDIAN
> + "\x08\x00" /* rta length */
> + "\x01\x00" /* rta type */
> +#else
> + "\x00\x08" /* rta length */
> + "\x00\x01" /* rta type */
> +#endif
> + "\x00\x00\x00\x00" /* salt length */
> + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
> + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
> + "\x0b\x0b\x0b\x0b\x0b\x0b", /* IKM */
> + .K1len = 30,
> + .context = NULL,
> + .contextlen = 0,
> + .expected = (unsigned char *)
> + "\x8d\xa4\xe7\x75\xa5\x63\xc1\x8f"
> + "\x71\x5f\x80\x2a\x06\x3c\x5a\x31"
> + "\xb8\xa1\x1f\x5c\x5e\xe1\x87\x9e"
> + "\xc3\x45\x4e\x5f\x3c\x73\x8d\x2d"
> + "\x9d\x20\x13\x95\xfa\xa4\xb6\x1a"
> + "\x96\xc8",
> + .expectedlen = 42
> + }
> +};
> +
> +/* Test vectors from RFC 5869 appendix A */
> +static struct kdf_testvec hkdf_hmac_sha1_tv_template[] = {
> + {
> + .K1 = (unsigned char *)
> +#ifdef __LITTLE_ENDIAN
> + "\x08\x00" /* rta length */
> + "\x01\x00" /* rta type */
> + "\x0d\x00\x00\x00" /* salt length */
> +#else
> + "\x00\x08" /* rta length */
> + "\x00\x01" /* rta type */
> + "\x00\x00\x00\x0d" /* salt length */
> +#endif
> + "\x00\x01\x02\x03\x04\x05\x06\x07"
> + "\x08\x09\x0a\x0b\x0c" /* salt */
> + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
> + "\x0b\x0b\x0b", /* IKM */
> + .K1len = 32,
> + .context = (unsigned char *)
> + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7"
> + "\xf8\xf9",
> + .contextlen = 10,
> + .expected = (unsigned char *)
> + "\x08\x5a\x01\xea\x1b\x10\xf3\x69"
> + "\x33\x06\x8b\x56\xef\xa5\xad\x81"
> + "\xa4\xf1\x4b\x82\x2f\x5b\x09\x15"
> + "\x68\xa9\xcd\xd4\xf1\x55\xfd\xa2"
> + "\xc2\x2e\x42\x24\x78\xd3\x05\xf3"
> + "\xf8\x96",
> + .expectedlen = 42
> + }, {
> + .K1 = (unsigned char *)
> +#ifdef __LITTLE_ENDIAN
> + "\x08\x00" /* rta length */
> + "\x01\x00" /* rta type */
> +#else
> + "\x00\x08" /* rta length */
> + "\x00\x01" /* rta type */
> +#endif
> + "\x00\x00\x00\x00" /* salt length */
> + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
> + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
> + "\x0b\x0b\x0b\x0b\x0b\x0b", /* IKM */
> + .K1len = 30,
> + .context = NULL,
> + .contextlen = 0,
> + .expected = (unsigned char *)
> + "\x0a\xc1\xaf\x70\x02\xb3\xd7\x61"
> + "\xd1\xe5\x52\x98\xda\x9d\x05\x06"
> + "\xb9\xae\x52\x05\x72\x20\xa3\x06"
> + "\xe0\x7b\x6b\x87\xe8\xdf\x21\xd0"
> + "\xea\x00\x03\x3d\xe0\x39\x84\xd3"
> + "\x49\x18",
> + .expectedlen = 42
> + }
> +};
> +
> /* Cast5 test vectors from RFC 2144 */
> static const struct cipher_testvec cast5_tv_template[] = {
> {
> --
> 2.20.1
>
>
>
>

2019-01-12 05:28:16

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH 3/6] crypto: kdf - add known answer tests

On Fri, Jan 11, 2019 at 08:10:22PM +0100, Stephan M?ller wrote:
> Add known answer tests to the testmgr for the KDF (SP800-108) cipher.
>
> Signed-off-by: Stephan Mueller <[email protected]>
> ---
> crypto/testmgr.c | 226 +++++++++++++++++++++++++++++++++++++++++++++++
> crypto/testmgr.h | 110 +++++++++++++++++++++++
> 2 files changed, 336 insertions(+)
>
> diff --git a/crypto/testmgr.c b/crypto/testmgr.c
> index 0f684a414acb..ff9051bffa1f 100644
> --- a/crypto/testmgr.c
> +++ b/crypto/testmgr.c
> @@ -110,6 +110,11 @@ struct drbg_test_suite {
> unsigned int count;
> };
>
> +struct kdf_test_suite {
> + struct kdf_testvec *vecs;
> + unsigned int count;
> +};
> +
> struct akcipher_test_suite {
> const struct akcipher_testvec *vecs;
> unsigned int count;
> @@ -133,6 +138,7 @@ struct alg_test_desc {
> struct hash_test_suite hash;
> struct cprng_test_suite cprng;
> struct drbg_test_suite drbg;
> + struct kdf_test_suite kdf;
> struct akcipher_test_suite akcipher;
> struct kpp_test_suite kpp;
> } suite;
> @@ -2020,6 +2026,64 @@ static int drbg_cavs_test(const struct drbg_testvec *test, int pr,
> return ret;
> }
>
> +static int kdf_cavs_test(struct kdf_testvec *test,
> + const char *driver, u32 type, u32 mask)

Why not just "kdf_test()"?

> +{
> + int ret = -EAGAIN;
> + struct crypto_rng *drng;
> + unsigned char *buf = kzalloc(test->expectedlen, GFP_KERNEL);

s/unsigned char/u8

> +
> + if (!buf)
> + return -ENOMEM;
> +
> + drng = crypto_alloc_rng(driver, type | CRYPTO_ALG_INTERNAL, mask);
> + if (IS_ERR(drng)) {
> + printk(KERN_ERR "alg: kdf: could not allocate cipher handle "
> + "for %s\n", driver);

pr_err

> + kzfree(buf);

kfree is fine here.

> + return -ENOMEM;
> + }
> +
> + ret = crypto_rng_reset(drng, test->K1, test->K1len);
> + if (ret) {
> + printk(KERN_ERR "alg: kdf: could not set key derivation key\n");

pr_err

> + goto err;
> + }
> +
> + ret = crypto_rng_generate(drng, test->context, test->contextlen,
> + buf, test->expectedlen);
> + if (ret) {
> + printk(KERN_ERR "alg: kdf: could not obtain key data\n");

pr_err

> + goto err;
> + }
> +
> + ret = memcmp(test->expected, buf, test->expectedlen);

Elsewhere this function returns an -errno value but this is different.

> +
> +err:
> + crypto_free_rng(drng);
> + kzfree(buf);

kfree would be fine here too.

> + return ret;
> +}
> +
> +static int alg_test_kdf(const struct alg_test_desc *desc, const char *driver,
> + u32 type, u32 mask)
> +{
> + int err = 0;
> + unsigned int i = 0;
> + struct kdf_testvec *template = desc->suite.kdf.vecs;

const

- Eric

2019-01-12 05:29:28

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH 2/6] crypto: kdf - SP800-108 Key Derivation Function

On Fri, Jan 11, 2019 at 08:10:02PM +0100, Stephan M?ller wrote:
> The SP800-108 compliant Key Derivation Function is implemented as a
> random number generator considering that it behaves like a deterministic
> RNG.
>
> All three KDF types specified in SP800-108 are implemented.
>
> The code comments provide details about how to invoke the different KDF
> types.
>
> Signed-off-by: Stephan Mueller <[email protected]>
> ---
> crypto/Kconfig | 7 +
> crypto/Makefile | 1 +
> crypto/kdf.c | 492 ++++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 500 insertions(+)
> create mode 100644 crypto/kdf.c
>
> diff --git a/crypto/Kconfig b/crypto/Kconfig
> index 86960aa53e0f..cc80d89e0cf5 100644
> --- a/crypto/Kconfig
> +++ b/crypto/Kconfig
> @@ -561,6 +561,13 @@ config CRYPTO_HMAC
> HMAC: Keyed-Hashing for Message Authentication (RFC2104).
> This is required for IPSec.
>
> +config CRYPTO_KDF
> + tristate "Key Derivation Function (SP800-108)"
> + select CRYPTO_RNG
> + help
> + Support for KDF compliant to SP800-108. All three types of
> + KDF specified in SP800-108 are implemented.
> +
> config CRYPTO_XCBC
> tristate "XCBC support"
> select CRYPTO_HASH
> diff --git a/crypto/Makefile b/crypto/Makefile
> index 799ed5e94606..69a0bb64b0ac 100644
> --- a/crypto/Makefile
> +++ b/crypto/Makefile
> @@ -58,6 +58,7 @@ crypto_user-y := crypto_user_base.o
> crypto_user-$(CONFIG_CRYPTO_STATS) += crypto_user_stat.o
> obj-$(CONFIG_CRYPTO_CMAC) += cmac.o
> obj-$(CONFIG_CRYPTO_HMAC) += hmac.o
> +obj-$(CONFIG_CRYPTO_KDF) += kdf.o

This naming is too generic. CONFIG_CRYPTO_KDF and kdf.c imply that this is
related to all KDFs. But actually it is an implementation of a few specific
KDFs. Can you give it a clearer name, like KDF_SP800?

- Eric

2019-01-12 09:58:31

by Herbert Xu

[permalink] [raw]
Subject: Re: [PATCH 4/6] crypto: hkdf - RFC5869 Key Derivation Function

On Fri, Jan 11, 2019 at 09:12:54PM -0800, Eric Biggers wrote:
> Hi Stephan,
>
> On Fri, Jan 11, 2019 at 08:10:39PM +0100, Stephan M?ller wrote:
> > The RFC5869 compliant Key Derivation Function is implemented as a
> > random number generator considering that it behaves like a deterministic
> > RNG.
> >
>
> Thanks for the proof of concept! I guess it ended up okay. But can you explain
> more the benefits of using the crypto_rng interface, as opposed to just some
> crypto_hkdf_*() helper functions that are exported for modules to use?

I agree. I see no benefit in adding this through the RNG API as
opposed to just providing it as a helper. If some form of hardware
acceleration were to eventuate in the future we could always revisit
this.

Cheers,
--
Email: Herbert Xu <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

2019-01-13 07:58:37

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 4/6] crypto: hkdf - RFC5869 Key Derivation Function

Am Samstag, 12. Januar 2019, 10:55:35 CET schrieb Herbert Xu:

Hi Herbert,

> On Fri, Jan 11, 2019 at 09:12:54PM -0800, Eric Biggers wrote:
> > Hi Stephan,
> >
> > On Fri, Jan 11, 2019 at 08:10:39PM +0100, Stephan M?ller wrote:
> > > The RFC5869 compliant Key Derivation Function is implemented as a
> > > random number generator considering that it behaves like a deterministic
> > > RNG.
> >
> > Thanks for the proof of concept! I guess it ended up okay. But can you
> > explain more the benefits of using the crypto_rng interface, as opposed
> > to just some crypto_hkdf_*() helper functions that are exported for
> > modules to use?
> I agree. I see no benefit in adding this through the RNG API as
> opposed to just providing it as a helper. If some form of hardware
> acceleration were to eventuate in the future we could always revisit
> this.

The advantages for using the kernel crypto API to add KDFs as opposed to
adding helper wrappers are the following in my view:

- employment of the kernel crypto API testmgr - invocation of the testmgr is
transparent and thus already provided without any additional code to link to
it

- FIPS 140-2 compliance: To mark a KDF as FIPS 140-2 approved cipher, it must
be subject to a known-answer self test (implemented by the testmgr) as well as
to an enforcement of the integrity check verification. In FIPS 140-2 mode, the
kernel crypto API panic()s when a kernel crypto API module is loaded and its
signature does not check out. As this is only relevant for crypto modules (and
not arbitrary other kernel modules), this is implemented with the invocations
the crypto_register_alg as well as crypto_register_template functions. Thus,
when using a wrapper code to implement the KDF, they can per definition not be
FIPS 140-2 approved.

- The invoker of a KDF has one consistent API. This implies that the KDF
selection now becomes more of a "configuration" choice. For example, when you
look at the KDF use case for the keys subsystem (security/keys/dh.c),
selecting the type of KDF would only necessitate a change of a string
referencing the KDF. A lot of people somehow favor the extract-and-expand KDFs
over the traditional KDFs. Now, that the RFC5869 HKDF is also approved as per
SP800-56A rev3, I could see that folks may want to switch to HKDF for the key
management. When we have a common API, this choice could even be left to the
caller.

The question may arise why to plug the KDFs into RNGs. The answer is quite
simple: KDFs are a form of random number generator. In that they take some
input for initialization (aka seed, salt, key, personalization string). Then
they produce pseudo-random bit sequences of arbitrary length. Possibly the
generation operation can be modified by providing some additional input to be
used by the generation process (aka label, context, info string, additional
information string). Thus, the RNG interface is a natural fit for the KDFs.

Ciao
Stephan



2019-01-13 16:56:13

by James Bottomley

[permalink] [raw]
Subject: Re: [PATCH 4/6] crypto: hkdf - RFC5869 Key Derivation Function

On Sun, 2019-01-13 at 08:56 +0100, Stephan Müller wrote:
> The question may arise why to plug the KDFs into RNGs. The answer is
> quite simple: KDFs are a form of random number generator. In that
> they take some input for initialization (aka seed, salt, key,
> personalization string). Then they produce pseudo-random bit
> sequences of arbitrary length. Possibly the generation operation can
> be modified by providing some additional input to be used by the
> generation process (aka label, context, info string, additional 
> information string). Thus, the RNG interface is a natural fit for the
> KDFs.

Philosophically, that's quite wrong. KDFs are a class of pseudorandom
functions (PRFs). PRFs are designed so that the output is
indistinguishable from a random number generator to observers who don't
know the input but is deterministically useful for participants who do.
That means the're definitely not RNGs they're functions whose output
is designed to look like the output of an RNG.

I suppose the mathematical thing that distinguishes PRFs and RNGs is
entropy: PRFs have zero entropy because given the same inputs you
expect the same output. Now whether it makes sense to use the RNG API
or not I'll leave that up to the crypto people. I would have expected
any cryptographic RNG API to be mostly about entropy management (the
Linux core internal one certainly is), but it appears that the one in
crypto isn't.

James


2019-01-14 09:27:11

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 5/6] crypto: hkdf - add known answer tests

Am Samstag, 12. Januar 2019, 06:19:15 CET schrieb Eric Biggers:

Hi Eric,

[...]
>
> > + }
> > + }
> > + }, {
> > + .alg = "hkdf(hmac(sha224))",
> > + .test = alg_test_null,
> > + .fips_allowed = 1,
>
> I think it is dumb to add algorithms to the testmgr with no tests just so
> the 'fips_allowed' flag can be set.

Currently it is the only way. But I agree that it could be done better.

> And doesn't FIPS sometimes require
> tests anyway? I don't think the "null test" should count as a test :-)

Yes, it DOES count as a test (as strange as it may sound)! :-)

The FIPS requirements are as follows:

- raw ciphers must be subject to a FIPS test with one block chaining mode to
cover that cipher with all block chaining modes (e.g. you can test ecb(aes) to
cover AES with *all* existing block chaining modes).

- for compound crypto algorithm (like RSA with respect to hashes, KDF with
respect to the keyed message digest, HMAC with respect to hashes), the
wrapping crypto algorithm needs to be tested with *one* wrapped cipher at
least (but also not more. E.g. if you have a self test for, say, all SHA-1 and
SHA-2, you only need one HMAC SHA test or one KDF HMAC SHA test.

- in some circumstances, it is even permissible to test wrapping crypto
algorithms where the underlying algo is implicitly tested. E.g. if you have a
HMAC SHA-256 test, you do not need an individual SHA-256 test.


>
> Perhaps just include sha256 and sha512, and have tests for them?

Do you happen to have an official SHA-512 HKDF test vector? RFC5869 only has
SHA-1 and SHA-256 tests.
>

[...]
> >
> > +/* Test vectors from RFC 5869 appendix A */
> > +static struct kdf_testvec hkdf_hmac_sha256_tv_template[] = {
>
> const
>
> Likewise for all other kdf_testvecs.

const does not work with __VECS :-(

I leave it without const at the moment. I think the __VECS should be updated
along with all test vectors.

[...]

Ciao
Stephan



2019-01-14 09:29:03

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 3/6] crypto: kdf - add known answer tests

Am Samstag, 12. Januar 2019, 06:26:46 CET schrieb Eric Biggers:

Hi Eric,


[...]

Thanks. I integrated updates for all comments.

Ciao
Stephan



2019-01-14 09:32:47

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 4/6] crypto: hkdf - RFC5869 Key Derivation Function

Am Samstag, 12. Januar 2019, 06:12:54 CET schrieb Eric Biggers:

Hi Eric,

[...]

> > The extract and expand phases use different instances of the underlying
> > keyed message digest cipher to ensure that while the extraction phase
> > generates a new key for the expansion phase, the cipher for the
> > expansion phase can still be used. This approach is intended to aid
> > multi-threaded uses cases.
>
> I think you partially misunderstood what I was asking for. One HMAC tfm is
> sufficient as long as HKDF-Expand is separated from HKDF-Extract, which
> you've done. So just use one HMAC tfm, and in crypto_hkdf_seed() key it
> with the 'salt', and then afterwards with the 'prk'.

Ok, thanks for the clarification. I will remove the 2nd HMAC TFM then.
>
> Also everywhere in this patchset, please avoid using the word "cipher" to
> refer to algorithms that are not encryption/decryption. I know a lot of
> the crypto API docs do this, but I think it is a mistake and confusing.
> Hash algorithms and KDFs are not "ciphers".

As you wish, I will refer to specific name of the cryptographic operation.

[...]

> > + * NOTE: In-place cipher operations are not supported.
> > + */
>
> What does an "in-place cipher operation" mean in this context? That the
> 'info' buffer must not overlap the 'dst' buffer?

Correct, no overlapping.

> Maybe
> crypto_rng_generate() should check that for all crypto_rngs? Or is it
> different for different crypto_rngs?

This is the case in general for all KDFs (and even RNGs). It is no technical
or cryptographic error to have overlapping buffers. The only issue is that the
result will not match the expected value.

The issue is that the input buffer to the generate function is an input to
every round of the KDF. If the input and output buffer overlap, starting with
the 2nd iteration of the KDF, the input is the output of the 1st round. Again,
I do not think it is a cryptographic error though.

(To support my conclusion: A colleague of mine has proposed an update to the
HKDF specification where the input data changes for each KDF round. This
proposal was considered appropriate by one of the authors of HKDF.)

If the requested output is smaller or equal to the output block size of the
KDF, overlapping buffers are even harmless since the implementation will
calculate the correct output.

Due to that, I removed the statement. But I am not sure we should add a
technical block to deny overlapping input/output buffers.

[...]
> >
> > + desc->flags = crypto_shash_get_flags(expand_kmd) &
> > + CRYPTO_TFM_REQ_MAY_SLEEP;
>
> This line setting desc->flags doesn't make sense. How is the user meant to
> control whether crypto_rng_generate() can sleep or not? Or can it always
> sleep? Either way this part is wrong since the user can't get access to the
> HMAC tfm to set this flag being checked for.

Could you please help me why a user should set this flag? Isn't the
implementation specifying that flag to allow identifying whether the
implementation could or could not sleep? Thus, we simply copy the sleeping
flag from the lower level keyed message digest implementation.

At least that is also the implementation found in crypto/hmac.c.

[...]

> > + if (dlen < h) {
> > + u8 tmpbuffer[CRYPTO_HKDF_MAX_DIGESTSIZE];
> > +
> > + err = crypto_shash_finup(desc, &ctr, 1, tmpbuffer);
> > + if (err)
> > + goto out;
> > + memcpy(dst, tmpbuffer, dlen);
> > + memzero_explicit(tmpbuffer, h);
> > + goto out;
> > + } else {
>
> No need for the 'else'.

Could you please help me why that else branch is not needed? If the buffer to
be generated is equal or larger than the output block length of the keyed
message digest, I would like to directly operate on the output buffer to avoid
a memcpy.
>
> > + err = crypto_shash_finup(desc, &ctr, 1, dst);
> > + if (err)
> > + goto out;
> > +
> > + prev = dst;
> > + dst += h;
> > + dlen -= h;
> > + ctr++;
> > + }
> > + }

[...]
>
> > + struct crypto_shash *extract_kmd = ctx->extract_kmd;
> > + struct crypto_shash *expand_kmd = ctx->expand_kmd;
> > + struct rtattr *rta = (struct rtattr *)seed;
> > + SHASH_DESC_ON_STACK(desc, extract_kmd);
> > + u32 saltlen;
> > + unsigned int h = crypto_shash_digestsize(extract_kmd);
> > + int err;
> > + const uint8_t null_salt[CRYPTO_HKDF_MAX_DIGESTSIZE] = { 0 };
>
> static const
>

Why would I want to turn that buffer into a static variable? All we need it
for is in case there is no salt provided.

[...]

> > +
> > + if (!RTA_OK(rta, slen))
> > + return -EINVAL;
> > + if (rta->rta_type != 1)
> > + return -EINVAL;
> > + if (RTA_PAYLOAD(rta) < sizeof(saltlen))
> > + return -EINVAL;
> > + saltlen = *((u32 *)RTA_DATA(rta));
>
> I'm guessing you copied the weird "length as a rtattr payload" approach from
> the authenc template. I think it's not necessary. And it's overly
> error-prone, as shown by the authenc template getting the parsing wrong for
> years and you making the exact same mistake again here...
> (See https://patchwork.kernel.org/patch/10732803/) How about just using a
> u32 at the beginning without the 'rtattr' preceding it?

I was not sure whether this approach would be acceptable. I very much would
love to have a u32 pre-pended only without the RTA business.

I updated the implementation accordingly.
>
[...]

>
> > + alg = &salg->base;
>
> Check here that the underlying algorithm really is "hmac(" something?

I added a check for the presence of salg->setkey.
>
> Alternatively it may be a good idea to simplify usage by making the template
> just take the unkeyed hash directly, like "hkdf(sha512)". And if any users
> really need to specify a specific HMAC implementation then another template
> usable as "hkdf_base(hmac(sha512))" could be added later.
>

I would not suggest this, because that rounds contrary to the concept of the
kernel crypto API IMHO. The caller has to provide the wrapping cipher. It is
perfectly viable to allow a caller to invoke a specific keyed message digest.

[...]

Thank you very much for your code review.

Ciao
Stephan



2019-01-14 09:33:40

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 2/6] crypto: kdf - SP800-108 Key Derivation Function

Am Samstag, 12. Januar 2019, 06:27:59 CET schrieb Eric Biggers:

Hi Eric,

[...]
> >
> > +obj-$(CONFIG_CRYPTO_KDF) += kdf.o
>
> This naming is too generic. CONFIG_CRYPTO_KDF and kdf.c imply that this is
> related to all KDFs. But actually it is an implementation of a few specific
> KDFs. Can you give it a clearer name, like KDF_SP800?
>
I am going to use kdf_sp800108 or CRYPTO_CONFIG_KDF_SP800108. The reason is
that there are many SP800 documents.

Thanks

Ciao
Stephan



2019-01-14 17:45:45

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH 5/6] crypto: hkdf - add known answer tests

On Mon, Jan 14, 2019 at 10:25:16AM +0100, Stephan M?ller wrote:
> Am Samstag, 12. Januar 2019, 06:19:15 CET schrieb Eric Biggers:
>
> Hi Eric,
>
> [...]
> >
> > > + }
> > > + }
> > > + }, {
> > > + .alg = "hkdf(hmac(sha224))",
> > > + .test = alg_test_null,
> > > + .fips_allowed = 1,
> >
> > I think it is dumb to add algorithms to the testmgr with no tests just so
> > the 'fips_allowed' flag can be set.
>
> Currently it is the only way. But I agree that it could be done better.
>
> > And doesn't FIPS sometimes require
> > tests anyway? I don't think the "null test" should count as a test :-)
>
> Yes, it DOES count as a test (as strange as it may sound)! :-)
>
> The FIPS requirements are as follows:
>
> - raw ciphers must be subject to a FIPS test with one block chaining mode to
> cover that cipher with all block chaining modes (e.g. you can test ecb(aes) to
> cover AES with *all* existing block chaining modes).
>
> - for compound crypto algorithm (like RSA with respect to hashes, KDF with
> respect to the keyed message digest, HMAC with respect to hashes), the
> wrapping crypto algorithm needs to be tested with *one* wrapped cipher at
> least (but also not more. E.g. if you have a self test for, say, all SHA-1 and
> SHA-2, you only need one HMAC SHA test or one KDF HMAC SHA test.
>
> - in some circumstances, it is even permissible to test wrapping crypto
> algorithms where the underlying algo is implicitly tested. E.g. if you have a
> HMAC SHA-256 test, you do not need an individual SHA-256 test.
>
>
> >
> > Perhaps just include sha256 and sha512, and have tests for them?
>
> Do you happen to have an official SHA-512 HKDF test vector? RFC5869 only has
> SHA-1 and SHA-256 tests.
> >
>

No, I don't know of any official HKDF-SHA512 test vectors.

> [...]
> > >
> > > +/* Test vectors from RFC 5869 appendix A */
> > > +static struct kdf_testvec hkdf_hmac_sha256_tv_template[] = {
> >
> > const
> >
> > Likewise for all other kdf_testvecs.
>
> const does not work with __VECS :-(
>
> I leave it without const at the moment. I think the __VECS should be updated
> along with all test vectors.
>
> [...]

I don't see why. kdf_testvec just needs to be made const everywhere.

- Eric

2019-01-14 17:54:48

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH 4/6] crypto: hkdf - RFC5869 Key Derivation Function

On Mon, Jan 14, 2019 at 10:30:39AM +0100, Stephan M?ller wrote:
> Am Samstag, 12. Januar 2019, 06:12:54 CET schrieb Eric Biggers:
>
> Hi Eric,
>
> [...]
>
> > > The extract and expand phases use different instances of the underlying
> > > keyed message digest cipher to ensure that while the extraction phase
> > > generates a new key for the expansion phase, the cipher for the
> > > expansion phase can still be used. This approach is intended to aid
> > > multi-threaded uses cases.
> >
> > I think you partially misunderstood what I was asking for. One HMAC tfm is
> > sufficient as long as HKDF-Expand is separated from HKDF-Extract, which
> > you've done. So just use one HMAC tfm, and in crypto_hkdf_seed() key it
> > with the 'salt', and then afterwards with the 'prk'.
>
> Ok, thanks for the clarification. I will remove the 2nd HMAC TFM then.
> >
> > Also everywhere in this patchset, please avoid using the word "cipher" to
> > refer to algorithms that are not encryption/decryption. I know a lot of
> > the crypto API docs do this, but I think it is a mistake and confusing.
> > Hash algorithms and KDFs are not "ciphers".
>
> As you wish, I will refer to specific name of the cryptographic operation.
>
> [...]
>
> > > + * NOTE: In-place cipher operations are not supported.
> > > + */
> >
> > What does an "in-place cipher operation" mean in this context? That the
> > 'info' buffer must not overlap the 'dst' buffer?
>
> Correct, no overlapping.
>
> > Maybe
> > crypto_rng_generate() should check that for all crypto_rngs? Or is it
> > different for different crypto_rngs?
>
> This is the case in general for all KDFs (and even RNGs). It is no technical
> or cryptographic error to have overlapping buffers. The only issue is that the
> result will not match the expected value.
>
> The issue is that the input buffer to the generate function is an input to
> every round of the KDF. If the input and output buffer overlap, starting with
> the 2nd iteration of the KDF, the input is the output of the 1st round. Again,
> I do not think it is a cryptographic error though.
>
> (To support my conclusion: A colleague of mine has proposed an update to the
> HKDF specification where the input data changes for each KDF round. This
> proposal was considered appropriate by one of the authors of HKDF.)
>
> If the requested output is smaller or equal to the output block size of the
> KDF, overlapping buffers are even harmless since the implementation will
> calculate the correct output.
>
> Due to that, I removed the statement. But I am not sure we should add a
> technical block to deny overlapping input/output buffers.
>
> [...]
> > >
> > > + desc->flags = crypto_shash_get_flags(expand_kmd) &
> > > + CRYPTO_TFM_REQ_MAY_SLEEP;
> >
> > This line setting desc->flags doesn't make sense. How is the user meant to
> > control whether crypto_rng_generate() can sleep or not? Or can it always
> > sleep? Either way this part is wrong since the user can't get access to the
> > HMAC tfm to set this flag being checked for.
>
> Could you please help me why a user should set this flag? Isn't the
> implementation specifying that flag to allow identifying whether the
> implementation could or could not sleep? Thus, we simply copy the sleeping
> flag from the lower level keyed message digest implementation.
>
> At least that is also the implementation found in crypto/hmac.c.
>
> [...]

Whether the crypto_shash* stuff can sleep is controlled on a per-request basis,
not a per-implementation basis. So I don't understand what you are talking
about here.

>
> > > + if (dlen < h) {
> > > + u8 tmpbuffer[CRYPTO_HKDF_MAX_DIGESTSIZE];
> > > +
> > > + err = crypto_shash_finup(desc, &ctr, 1, tmpbuffer);
> > > + if (err)
> > > + goto out;
> > > + memcpy(dst, tmpbuffer, dlen);
> > > + memzero_explicit(tmpbuffer, h);
> > > + goto out;
> > > + } else {
> >
> > No need for the 'else'.
>
> Could you please help me why that else branch is not needed? If the buffer to
> be generated is equal or larger than the output block length of the keyed
> message digest, I would like to directly operate on the output buffer to avoid
> a memcpy.

I'm simply saying you don't need the 'else' keyword as the previous block ends
with a goto.

> >
> > > + err = crypto_shash_finup(desc, &ctr, 1, dst);
> > > + if (err)
> > > + goto out;
> > > +
> > > + prev = dst;
> > > + dst += h;
> > > + dlen -= h;
> > > + ctr++;
> > > + }
> > > + }
>
> [...]
> >
> > > + struct crypto_shash *extract_kmd = ctx->extract_kmd;
> > > + struct crypto_shash *expand_kmd = ctx->expand_kmd;
> > > + struct rtattr *rta = (struct rtattr *)seed;
> > > + SHASH_DESC_ON_STACK(desc, extract_kmd);
> > > + u32 saltlen;
> > > + unsigned int h = crypto_shash_digestsize(extract_kmd);
> > > + int err;
> > > + const uint8_t null_salt[CRYPTO_HKDF_MAX_DIGESTSIZE] = { 0 };
> >
> > static const
> >
>
> Why would I want to turn that buffer into a static variable? All we need it
> for is in case there is no salt provided.
>
> [...]
>
> > > +
> > > + if (!RTA_OK(rta, slen))
> > > + return -EINVAL;
> > > + if (rta->rta_type != 1)
> > > + return -EINVAL;
> > > + if (RTA_PAYLOAD(rta) < sizeof(saltlen))
> > > + return -EINVAL;
> > > + saltlen = *((u32 *)RTA_DATA(rta));
> >
> > I'm guessing you copied the weird "length as a rtattr payload" approach from
> > the authenc template. I think it's not necessary. And it's overly
> > error-prone, as shown by the authenc template getting the parsing wrong for
> > years and you making the exact same mistake again here...
> > (See https://patchwork.kernel.org/patch/10732803/) How about just using a
> > u32 at the beginning without the 'rtattr' preceding it?
>
> I was not sure whether this approach would be acceptable. I very much would
> love to have a u32 pre-pended only without the RTA business.
>
> I updated the implementation accordingly.
> >
> [...]
>
> >
> > > + alg = &salg->base;
> >
> > Check here that the underlying algorithm really is "hmac(" something?
>
> I added a check for the presence of salg->setkey.
> >
> > Alternatively it may be a good idea to simplify usage by making the template
> > just take the unkeyed hash directly, like "hkdf(sha512)". And if any users
> > really need to specify a specific HMAC implementation then another template
> > usable as "hkdf_base(hmac(sha512))" could be added later.
> >
>
> I would not suggest this, because that rounds contrary to the concept of the
> kernel crypto API IMHO. The caller has to provide the wrapping cipher. It is
> perfectly viable to allow a caller to invoke a specific keyed message digest.
>

Sure, but it would not conform to the HKDF specification. Are you sure it is
okay to specify an arbitrary keyed hash?

> [...]
>
> Thank you very much for your code review.
>
> Ciao
> Stephan
>
>

- Eric

2019-01-14 19:20:15

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 4/6] crypto: hkdf - RFC5869 Key Derivation Function

Am Montag, 14. Januar 2019, 18:53:16 CET schrieb Eric Biggers:

Hi Eric,

> > I would not suggest this, because that rounds contrary to the concept of
> > the kernel crypto API IMHO. The caller has to provide the wrapping
> > cipher. It is perfectly viable to allow a caller to invoke a specific
> > keyed message digest.
> Sure, but it would not conform to the HKDF specification. Are you sure it
> is okay to specify an arbitrary keyed hash?

Technically, I see no issue why this should not be possible. You see that with
the SP800-108 KDF implementations where using CMAC is perfectly legal (and
which I also test).

Though, using another keyed hash implementation like CMAC is not covered by
the HKDF spec. If a caller would use hkdf(cmac(aes)), it would produce
cryptographically strong values. Though this implementation does not conform
to any standard. I do not think we should prevent a caller to select such
combination in the kernel crypto API.

IMHO there would even be valid reasons why one would use cmac(aes) for a kdf.
For example, when you would want to use a hardware AES which somehow also
employs a hardware key that is inaccessible to software in order to tie the
KDF result to the local hardware. This could even be a valid use case for Ext4
FBE encryption where you derive a key. The KDF could be used to link the
derived key to the local hardware to prevent the encrypted data could be
copied to another system and decrypted successfully there.

Ciao
Stephan



2019-01-16 19:37:31

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v2 5/6] crypto: hkdf - add known answer tests

Add known answer tests to the testmgr for the HKDF (RFC5869) cipher.

The known answer tests are derived from RFC 5869 appendix A.

Note, the HKDF is considered to be a FIPS 140-2 allowed (not approved)
cipher as of now. Yet, an allowed cipher is usable under FIPS 140-2
rules.

Signed-off-by: Stephan Mueller <[email protected]>
---
crypto/testmgr.c | 26 ++++++++++++++
crypto/testmgr.h | 91 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 117 insertions(+)

diff --git a/crypto/testmgr.c b/crypto/testmgr.c
index 309819af55d8..0b06721a70df 100644
--- a/crypto/testmgr.c
+++ b/crypto/testmgr.c
@@ -3189,6 +3189,32 @@ static const struct alg_test_desc alg_test_descs[] = {
.suite = {
.hash = __VECS(ghash_tv_template)
}
+ }, {
+ .alg = "hkdf(hmac(sha1))",
+ .test = alg_test_kdf,
+ .fips_allowed = 1,
+ .suite = {
+ .kdf = __VECS(hkdf_hmac_sha1_tv_template)
+ }
+ }, {
+ .alg = "hkdf(hmac(sha224))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "hkdf(hmac(sha256))",
+ .test = alg_test_kdf,
+ .fips_allowed = 1,
+ .suite = {
+ .kdf = __VECS(hkdf_hmac_sha256_tv_template)
+ }
+ }, {
+ .alg = "hkdf(hmac(sha384))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "hkdf(hmac(sha512))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
}, {
.alg = "hmac(md5)",
.test = alg_test_hash,
diff --git a/crypto/testmgr.h b/crypto/testmgr.h
index 65fe3d4ef082..7ffff184fba2 100644
--- a/crypto/testmgr.h
+++ b/crypto/testmgr.h
@@ -27999,6 +27999,97 @@ static const struct kdf_testvec kdf_dpi_hmac_sha256_tv_template[] = {
}
};

+/* Test vectors from RFC 5869 appendix A */
+static const struct kdf_testvec hkdf_hmac_sha256_tv_template[] = {
+ {
+ .K1 =
+#ifdef __LITTLE_ENDIAN
+ "\x0d\x00\x00\x00" /* salt length */
+#else
+ "\x00\x00\x00\x0d" /* salt length */
+#endif
+ "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0a\x0b\x0c" /* salt */
+ "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+ "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+ "\x0b\x0b\x0b\x0b\x0b\x0b", /* IKM */
+ .K1len = 39,
+ .context =
+ "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7"
+ "\xf8\xf9",
+ .contextlen = 10,
+ .expected =
+ "\x3c\xb2\x5f\x25\xfa\xac\xd5\x7a"
+ "\x90\x43\x4f\x64\xd0\x36\x2f\x2a"
+ "\x2d\x2d\x0a\x90\xcf\x1a\x5a\x4c"
+ "\x5d\xb0\x2d\x56\xec\xc4\xc5\xbf"
+ "\x34\x00\x72\x08\xd5\xb8\x87\x18"
+ "\x58\x65",
+ .expectedlen = 42
+ }, {
+ .K1 = "\x00\x00\x00\x00" /* salt length */
+ "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+ "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+ "\x0b\x0b\x0b\x0b\x0b\x0b", /* IKM */
+ .K1len = 26,
+ .context = NULL,
+ .contextlen = 0,
+ .expected =
+ "\x8d\xa4\xe7\x75\xa5\x63\xc1\x8f"
+ "\x71\x5f\x80\x2a\x06\x3c\x5a\x31"
+ "\xb8\xa1\x1f\x5c\x5e\xe1\x87\x9e"
+ "\xc3\x45\x4e\x5f\x3c\x73\x8d\x2d"
+ "\x9d\x20\x13\x95\xfa\xa4\xb6\x1a"
+ "\x96\xc8",
+ .expectedlen = 42
+ }
+};
+
+/* Test vectors from RFC 5869 appendix A */
+static const struct kdf_testvec hkdf_hmac_sha1_tv_template[] = {
+ {
+ .K1 =
+#ifdef __LITTLE_ENDIAN
+ "\x0d\x00\x00\x00" /* salt length */
+#else
+ "\x00\x00\x00\x0d" /* salt length */
+#endif
+ "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0a\x0b\x0c" /* salt */
+ "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+ "\x0b\x0b\x0b", /* IKM */
+ .K1len = 28,
+ .context =
+ "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7"
+ "\xf8\xf9",
+ .contextlen = 10,
+ .expected =
+ "\x08\x5a\x01\xea\x1b\x10\xf3\x69"
+ "\x33\x06\x8b\x56\xef\xa5\xad\x81"
+ "\xa4\xf1\x4b\x82\x2f\x5b\x09\x15"
+ "\x68\xa9\xcd\xd4\xf1\x55\xfd\xa2"
+ "\xc2\x2e\x42\x24\x78\xd3\x05\xf3"
+ "\xf8\x96",
+ .expectedlen = 42
+ }, {
+ .K1 = "\x00\x00\x00\x00" /* salt length */
+ "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+ "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+ "\x0b\x0b\x0b\x0b\x0b\x0b", /* IKM */
+ .K1len = 26,
+ .context = NULL,
+ .contextlen = 0,
+ .expected =
+ "\x0a\xc1\xaf\x70\x02\xb3\xd7\x61"
+ "\xd1\xe5\x52\x98\xda\x9d\x05\x06"
+ "\xb9\xae\x52\x05\x72\x20\xa3\x06"
+ "\xe0\x7b\x6b\x87\xe8\xdf\x21\xd0"
+ "\xea\x00\x03\x3d\xe0\x39\x84\xd3"
+ "\x49\x18",
+ .expectedlen = 42
+ }
+};
+
/* Cast5 test vectors from RFC 2144 */
static const struct cipher_testvec cast5_tv_template[] = {
{
--
2.20.1





2019-01-16 19:37:34

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v2 3/6] crypto: kdf - add known answer tests

Add known answer tests to the testmgr for the KDF (SP800-108) cipher.

Signed-off-by: Stephan Mueller <[email protected]>
---
crypto/testmgr.c | 219 +++++++++++++++++++++++++++++++++++++++++++++++
crypto/testmgr.h | 107 +++++++++++++++++++++++
2 files changed, 326 insertions(+)

diff --git a/crypto/testmgr.c b/crypto/testmgr.c
index 0f684a414acb..309819af55d8 100644
--- a/crypto/testmgr.c
+++ b/crypto/testmgr.c
@@ -110,6 +110,11 @@ struct drbg_test_suite {
unsigned int count;
};

+struct kdf_test_suite {
+ const struct kdf_testvec *vecs;
+ unsigned int count;
+};
+
struct akcipher_test_suite {
const struct akcipher_testvec *vecs;
unsigned int count;
@@ -133,6 +138,7 @@ struct alg_test_desc {
struct hash_test_suite hash;
struct cprng_test_suite cprng;
struct drbg_test_suite drbg;
+ struct kdf_test_suite kdf;
struct akcipher_test_suite akcipher;
struct kpp_test_suite kpp;
} suite;
@@ -2020,6 +2026,66 @@ static int drbg_cavs_test(const struct drbg_testvec *test, int pr,
return ret;
}

+static int kdf_test(const struct kdf_testvec *test,
+ const char *driver, u32 type, u32 mask)
+{
+ int ret = -EAGAIN;
+ struct crypto_rng *drng;
+ u8 *buf = kzalloc(test->expectedlen, GFP_KERNEL);
+
+ if (!buf)
+ return -ENOMEM;
+
+ drng = crypto_alloc_rng(driver, type | CRYPTO_ALG_INTERNAL, mask);
+ if (IS_ERR(drng)) {
+ pr_err("alg: kdf: could not allocate cipher handle "
+ "for %s\n", driver);
+ kfree(buf);
+ return -ENOMEM;
+ }
+
+ ret = crypto_rng_reset(drng, test->K1, test->K1len);
+ if (ret) {
+ pr_err("alg: kdf: could not set key derivation key\n");
+ goto err;
+ }
+
+ ret = crypto_rng_generate(drng, test->context, test->contextlen,
+ buf, test->expectedlen);
+ if (ret) {
+ pr_err("alg: kdf: could not obtain key data\n");
+ goto err;
+ }
+
+ ret = memcmp(test->expected, buf, test->expectedlen);
+ if (ret)
+ ret = -EINVAL;
+
+err:
+ crypto_free_rng(drng);
+ kfree(buf);
+ return ret;
+}
+
+static int alg_test_kdf(const struct alg_test_desc *desc, const char *driver,
+ u32 type, u32 mask)
+{
+ int err = 0;
+ unsigned int i = 0;
+ const struct kdf_testvec *template = desc->suite.kdf.vecs;
+ unsigned int tcount = desc->suite.kdf.count;
+
+ for (i = 0; i < tcount; i++) {
+ err = kdf_test(&template[i], driver, type, mask);
+ if (err) {
+ pr_err("alg: kdf: Test %d failed for %s\n",
+ i, driver);
+ err = -EINVAL;
+ break;
+ }
+ }
+ return err;
+}

static int alg_test_drbg(const struct alg_test_desc *desc, const char *driver,
u32 type, u32 mask)
@@ -3220,6 +3286,159 @@ static const struct alg_test_desc alg_test_descs[] = {
.alg = "jitterentropy_rng",
.fips_allowed = 1,
.test = alg_test_null,
+ }, {
+ .alg = "kdf_ctr(cmac(aes))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_ctr(cmac(des3_ede))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_ctr(hmac(sha1))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_ctr(hmac(sha224))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_ctr(hmac(sha256))",
+ .test = alg_test_kdf,
+ .fips_allowed = 1,
+ .suite = {
+ .kdf = __VECS(kdf_ctr_hmac_sha256_tv_template)
+ }
+ }, {
+ .alg = "kdf_ctr(hmac(sha384))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_ctr(hmac(sha512))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_ctr(sha1)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_ctr(sha224)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_ctr(sha256)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_ctr(sha384)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_ctr(sha512)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_dpi(cmac(aes))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_dpi(cmac(des3_ede))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_dpi(hmac(sha1))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_dpi(hmac(sha224))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_dpi(hmac(sha256))",
+ .test = alg_test_kdf,
+ .fips_allowed = 1,
+ .suite = {
+ .kdf = __VECS(kdf_dpi_hmac_sha256_tv_template)
+ }
+ }, {
+ .alg = "kdf_dpi(hmac(sha384))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_dpi(hmac(sha512))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_dpi(sha1)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_dpi(sha224)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_dpi(sha256)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_dpi(sha384)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_dpi(sha512)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_fb(cmac(aes))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_fb(cmac(des3_ede))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_fb(hmac(sha1))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_fb(hmac(sha224))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_fb(hmac(sha256))",
+ .test = alg_test_kdf,
+ .fips_allowed = 1,
+ .suite = {
+ .kdf = __VECS(kdf_fb_hmac_sha256_tv_template)
+ }
+ }, {
+ .alg = "kdf_fb(hmac(sha384))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_fb(hmac(sha512))",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_fb(sha1)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_fb(sha224)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_fb(sha256)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_fb(sha384)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
+ }, {
+ .alg = "kdf_fb(sha512)",
+ .test = alg_test_null,
+ .fips_allowed = 1,
}, {
.alg = "kw(aes)",
.test = alg_test_skcipher,
diff --git a/crypto/testmgr.h b/crypto/testmgr.h
index ca8e8ebef309..65fe3d4ef082 100644
--- a/crypto/testmgr.h
+++ b/crypto/testmgr.h
@@ -122,6 +122,15 @@ struct drbg_testvec {
size_t expectedlen;
};

+struct kdf_testvec {
+ unsigned char *K1;
+ size_t K1len;
+ unsigned char *context;
+ size_t contextlen;
+ unsigned char *expected;
+ size_t expectedlen;
+};
+
struct akcipher_testvec {
const unsigned char *key;
const unsigned char *m;
@@ -27892,6 +27901,104 @@ static const struct drbg_testvec drbg_nopr_ctr_aes128_tv_template[] = {
},
};

+/*
+ * Test vector obtained from
+ * http://csrc.nist.gov/groups/STM/cavp/documents/KBKDF800-108/CounterMode.zip
+ */
+static const struct kdf_testvec kdf_ctr_hmac_sha256_tv_template[] = {
+ {
+ .K1 = "\xdd\x1d\x91\xb7\xd9\x0b\x2b\xd3"
+ "\x13\x85\x33\xce\x92\xb2\x72\xfb"
+ "\xf8\xa3\x69\x31\x6a\xef\xe2\x42"
+ "\xe6\x59\xcc\x0a\xe2\x38\xaf\xe0",
+ .K1len = 32,
+ .context =
+ "\x01\x32\x2b\x96\xb3\x0a\xcd\x19"
+ "\x79\x79\x44\x4e\x46\x8e\x1c\x5c"
+ "\x68\x59\xbf\x1b\x1c\xf9\x51\xb7"
+ "\xe7\x25\x30\x3e\x23\x7e\x46\xb8"
+ "\x64\xa1\x45\xfa\xb2\x5e\x51\x7b"
+ "\x08\xf8\x68\x3d\x03\x15\xbb\x29"
+ "\x11\xd8\x0a\x0e\x8a\xba\x17\xf3"
+ "\xb4\x13\xfa\xac",
+ .contextlen = 60,
+ .expected =
+ "\x10\x62\x13\x42\xbf\xb0\xfd\x40"
+ "\x04\x6c\x0e\x29\xf2\xcf\xdb\xf0",
+ .expectedlen = 16
+ }
+};
+
+/*
+ * Test vector obtained from
+ * http://csrc.nist.gov/groups/STM/cavp/documents/KBKDF800-108/FeedbackModeNOzeroiv.zip
+ */
+static const struct kdf_testvec kdf_fb_hmac_sha256_tv_template[] = {
+ {
+ .K1 = "\x93\xf6\x98\xe8\x42\xee\xd7\x53"
+ "\x94\xd6\x29\xd9\x57\xe2\xe8\x9c"
+ "\x6e\x74\x1f\x81\x0b\x62\x3c\x8b"
+ "\x90\x1e\x38\x37\x6d\x06\x8e\x7b",
+ .K1len = 32,
+ .context =
+ "\x9f\x57\x5d\x90\x59\xd3\xe0\xc0"
+ "\x80\x3f\x08\x11\x2f\x8a\x80\x6d"
+ "\xe3\xc3\x47\x19\x12\xcd\xf4\x2b"
+ "\x09\x53\x88\xb1\x4b\x33\x50\x8e"
+ "\x53\xb8\x9c\x18\x69\x0e\x20\x57"
+ "\xa1\xd1\x67\x82\x2e\x63\x6d\xe5"
+ "\x0b\xe0\x01\x85\x32\xc4\x31\xf7"
+ "\xf5\xe3\x7f\x77\x13\x92\x20\xd5"
+ "\xe0\x42\x59\x9e\xbe\x26\x6a\xf5"
+ "\x76\x7e\xe1\x8c\xd2\xc5\xc1\x9a"
+ "\x1f\x0f\x80",
+ .contextlen = 83,
+ .expected =
+ "\xbd\x14\x76\xf4\x3a\x4e\x31\x57"
+ "\x47\xcf\x59\x18\xe0\xea\x5b\xc0"
+ "\xd9\x87\x69\x45\x74\x77\xc3\xab"
+ "\x18\xb7\x42\xde\xf0\xe0\x79\xa9"
+ "\x33\xb7\x56\x36\x5a\xfb\x55\x41"
+ "\xf2\x53\xfe\xe4\x3c\x6f\xd7\x88"
+ "\xa4\x40\x41\x03\x85\x09\xe9\xee"
+ "\xb6\x8f\x7d\x65\xff\xbb\x5f\x95",
+ .expectedlen = 64
+ }
+};
+
+/*
+ * Test vector obtained from
+ * http://csrc.nist.gov/groups/STM/cavp/documents/KBKDF800-108/PipelineModewithCounter.zip
+ */
+static const struct kdf_testvec kdf_dpi_hmac_sha256_tv_template[] = {
+ {
+ .K1 = "\x02\xd3\x6f\xa0\x21\xc2\x0d\xdb"
+ "\xde\xe4\x69\xf0\x57\x94\x68\xba"
+ "\xe5\xcb\x13\xb5\x48\xb6\xc6\x1c"
+ "\xdf\x9d\x3e\xc4\x19\x11\x1d\xe2",
+ .K1len = 32,
+ .context =
+ "\x85\xab\xe3\x8b\xf2\x65\xfb\xdc"
+ "\x64\x45\xae\x5c\x71\x15\x9f\x15"
+ "\x48\xc7\x3b\x7d\x52\x6a\x62\x31"
+ "\x04\x90\x4a\x0f\x87\x92\x07\x0b"
+ "\x3d\xf9\x90\x2b\x96\x69\x49\x04"
+ "\x25\xa3\x85\xea\xdb\x0f\x9c\x76"
+ "\xe4\x6f\x0f",
+ .contextlen = 51,
+ .expected =
+ "\xd6\x9f\x74\xf5\x18\xc9\xf6\x4f"
+ "\x90\xa0\xbe\xeb\xab\x69\xf6\x89"
+ "\xb7\x3b\x5c\x13\xeb\x0f\x86\x0a"
+ "\x95\xca\xd7\xd9\x81\x4f\x8c\x50"
+ "\x6e\xb7\xb1\x79\xa5\xc5\xb4\x46"
+ "\x6a\x9e\xc1\x54\xc3\xbf\x1c\x13"
+ "\xef\xd6\xec\x0d\x82\xb0\x2c\x29"
+ "\xaf\x2c\x69\x02\x99\xed\xc4\x53",
+ .expectedlen = 64
+ }
+};
+
/* Cast5 test vectors from RFC 2144 */
static const struct cipher_testvec cast5_tv_template[] = {
{
--
2.20.1





2019-01-16 19:39:38

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v2 1/6] crypto: add template handling for RNGs

Adds ability to register templates for pseudo random number generators
(PRNG). PRNGs are "meta" mechanisms using raw cipher primitives. Thus,
PRNGs can now be implemented as templates to allow the complete
flexibility the kernel crypto API provides.

The RNG API provides access to the PRNG algorithms without an entropy
management.

Signed-off-by: Stephan Mueller <[email protected]>
---
crypto/rng.c | 44 +++++++++++++++++++++++++++++++++++
include/crypto/internal/rng.h | 26 +++++++++++++++++++++
2 files changed, 70 insertions(+)

diff --git a/crypto/rng.c b/crypto/rng.c
index 33c38a72bff5..da4fd03c0acd 100644
--- a/crypto/rng.c
+++ b/crypto/rng.c
@@ -64,6 +64,25 @@ static int crypto_rng_init_tfm(struct crypto_tfm *tfm)
return 0;
}

+static inline struct rng_alg *__crypto_rng_alg(struct crypto_alg *alg)
+{
+ return container_of(alg, struct rng_alg, base);
+}
+
+static inline struct rng_instance *rng_instance(
+ struct crypto_instance *inst)
+{
+ return container_of(__crypto_rng_alg(&inst->alg),
+ struct rng_instance, alg);
+}
+
+static void crypto_rng_free_instance(struct crypto_instance *inst)
+{
+ struct rng_instance *rng = rng_instance(inst);
+
+ rng->free(rng);
+}
+
static unsigned int seedsize(struct crypto_alg *alg)
{
struct rng_alg *ralg = container_of(alg, struct rng_alg, base);
@@ -102,6 +121,7 @@ static void crypto_rng_show(struct seq_file *m, struct crypto_alg *alg)
static const struct crypto_type crypto_rng_type = {
.extsize = crypto_alg_extsize,
.init_tfm = crypto_rng_init_tfm,
+ .free = crypto_rng_free_instance,
#ifdef CONFIG_PROC_FS
.show = crypto_rng_show,
#endif
@@ -229,5 +249,29 @@ void crypto_unregister_rngs(struct rng_alg *algs, int count)
}
EXPORT_SYMBOL_GPL(crypto_unregister_rngs);

+static int rng_prepare_alg(struct rng_alg *alg)
+{
+ struct crypto_alg *base = &alg->base;
+
+ base->cra_type = &crypto_rng_type;
+ base->cra_flags &= ~CRYPTO_ALG_TYPE_MASK;
+ base->cra_flags |= CRYPTO_ALG_TYPE_RNG;
+
+ return 0;
+}
+
+int rng_register_instance(struct crypto_template *tmpl,
+ struct rng_instance *inst)
+{
+ int err;
+
+ err = rng_prepare_alg(&inst->alg);
+ if (err)
+ return err;
+
+ return crypto_register_instance(tmpl, rng_crypto_instance(inst));
+}
+EXPORT_SYMBOL_GPL(rng_register_instance);
+
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Random Number Generator");
diff --git a/include/crypto/internal/rng.h b/include/crypto/internal/rng.h
index a52ef3483dd7..bfe4482ad336 100644
--- a/include/crypto/internal/rng.h
+++ b/include/crypto/internal/rng.h
@@ -42,4 +42,30 @@ static inline void crypto_rng_set_entropy(struct crypto_rng *tfm,
crypto_rng_alg(tfm)->set_ent(tfm, data, len);
}

+struct rng_instance {
+ void (*free)(struct rng_instance *inst);
+ struct rng_alg alg;
+};
+
+static inline struct rng_instance *rng_alloc_instance(
+ const char *name, struct crypto_alg *alg)
+{
+ return crypto_alloc_instance(name, alg,
+ sizeof(struct rng_instance) - sizeof(*alg));
+}
+
+static inline struct crypto_instance *rng_crypto_instance(
+ struct rng_instance *inst)
+{
+ return container_of(&inst->alg.base, struct crypto_instance, alg);
+}
+
+static inline void *rng_instance_ctx(struct rng_instance *inst)
+{
+ return crypto_instance_ctx(rng_crypto_instance(inst));
+}
+
+int rng_register_instance(struct crypto_template *tmpl,
+ struct rng_instance *inst);
+
#endif
--
2.20.1





2019-01-16 21:34:35

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v2 4/6] crypto: hkdf - HMAC-based Extract-and-Expand KDF

The HMAC-based Extract-and-Expand Key Derivation Function is conformant
to RFC5869.

The extraction phase can be invoked separately from the expansion phase.
This implies that the once a key is set and thus the extraction phase was
applied, the expansion phase can be invoked multiple times.

The HKDF implementation does not restrict the type of keyed message
digest it is instantiated with. RFC5869 specifies HMAC-SHA as the only
keyed message digest, though. From a cryptographic point of view, using
other keyed message digests would result in an equally strong KDF.

Signed-off-by: Stephan Mueller <[email protected]>
---
crypto/Kconfig | 6 ++
crypto/Makefile | 1 +
crypto/hkdf.c | 272 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 279 insertions(+)
create mode 100644 crypto/hkdf.c

diff --git a/crypto/Kconfig b/crypto/Kconfig
index 7c0336c9ba9c..c25b2033321a 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -568,6 +568,12 @@ config CRYPTO_KDF_SP800108
Support for KDF compliant to SP800-108. All three types of
KDF specified in SP800-108 are implemented.

+config CRYPTO_HKDF
+ tristate "HMAC-based Extract-and-Expand Key Derivation Function (HKDF)"
+ select CRYPTO_RNG
+ help
+ Support for KDF conformant to RFC5869.
+
config CRYPTO_XCBC
tristate "XCBC support"
select CRYPTO_HASH
diff --git a/crypto/Makefile b/crypto/Makefile
index eead7ec9fd8e..80363b8cbf6c 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -59,6 +59,7 @@ crypto_user-$(CONFIG_CRYPTO_STATS) += crypto_user_stat.o
obj-$(CONFIG_CRYPTO_CMAC) += cmac.o
obj-$(CONFIG_CRYPTO_HMAC) += hmac.o
obj-$(CONFIG_CRYPTO_KDF_SP800108) += kdf_sp800108.o
+obj-$(CONFIG_CRYPTO_HKDF) += hkdf.o
obj-$(CONFIG_CRYPTO_VMAC) += vmac.o
obj-$(CONFIG_CRYPTO_XCBC) += xcbc.o
obj-$(CONFIG_CRYPTO_NULL2) += crypto_null.o
diff --git a/crypto/hkdf.c b/crypto/hkdf.c
new file mode 100644
index 000000000000..2f392c71c85b
--- /dev/null
+++ b/crypto/hkdf.c
@@ -0,0 +1,272 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * HMAC-based Extract-and-Expand Key Derivation Function (conformant to RFC5869)
+ *
+ * Copyright (C) 2019, Stephan Mueller <[email protected]>
+ */
+
+/*
+ * The HKDF extract phase is applied with crypto_rng_reset().
+ * The HKDF expand phase is applied with crypto_rng_generate().
+ */
+
+#include <asm/unaligned.h>
+#include <linux/module.h>
+#include <crypto/rng.h>
+#include <crypto/internal/rng.h>
+#include <crypto/hash.h>
+#include <crypto/internal/hash.h>
+
+struct crypto_hkdf_ctx {
+ struct crypto_shash *kmd;
+};
+
+#define CRYPTO_HKDF_MAX_DIGESTSIZE 64
+
+static inline void crypto_kdf_init_desc(struct shash_desc *desc,
+ struct crypto_shash *kmd)
+{
+ desc->tfm = kmd;
+ desc->flags = 0;
+}
+
+/*
+ * HKDF expand phase
+ */
+static int crypto_hkdf_generate(struct crypto_rng *rng,
+ const u8 *info, unsigned int infolen,
+ u8 *dst, unsigned int dlen)
+{
+ const struct crypto_hkdf_ctx *ctx = crypto_tfm_ctx(crypto_rng_tfm(rng));
+ struct crypto_shash *kmd = ctx->kmd;
+ SHASH_DESC_ON_STACK(desc, kmd);
+ const unsigned int h = crypto_shash_digestsize(kmd);
+ int err = 0;
+ u8 *dst_orig = dst;
+ const u8 *prev = NULL;
+ u8 ctr = 0x01;
+
+ if (dlen > h * 255)
+ return -EINVAL;
+
+ crypto_kdf_init_desc(desc, kmd);
+
+ /* T(1) and following */
+ while (dlen) {
+ err = crypto_shash_init(desc);
+ if (err)
+ goto out;
+
+ if (prev) {
+ err = crypto_shash_update(desc, prev, h);
+ if (err)
+ goto out;
+ }
+
+ if (info) {
+ err = crypto_shash_update(desc, info, infolen);
+ if (err)
+ goto out;
+ }
+
+ if (dlen < h) {
+ u8 tmpbuffer[CRYPTO_HKDF_MAX_DIGESTSIZE];
+
+ err = crypto_shash_finup(desc, &ctr, 1, tmpbuffer);
+ if (err)
+ goto out;
+ memcpy(dst, tmpbuffer, dlen);
+ memzero_explicit(tmpbuffer, h);
+ goto out;
+ }
+
+ err = crypto_shash_finup(desc, &ctr, 1, dst);
+ if (err)
+ goto out;
+
+ prev = dst;
+ dst += h;
+ dlen -= h;
+ ctr++;
+ }
+
+out:
+ if (err)
+ memzero_explicit(dst_orig, dlen);
+ shash_desc_zero(desc);
+ return err;
+}
+
+/*
+ * HKDF extract phase.
+ *
+ * The seed is defined to be a concatenation of the salt and the IKM.
+ * The data buffer is pre-pended by a word which provides an u32 value
+ * with the length of the salt. Thus, the buffer length - salt length is the
+ * IKM length.
+ */
+static int crypto_hkdf_seed(struct crypto_rng *rng,
+ const u8 *seed, unsigned int slen)
+{
+ const struct crypto_hkdf_ctx *ctx = crypto_tfm_ctx(crypto_rng_tfm(rng));
+ struct crypto_shash *kmd = ctx->kmd;
+ SHASH_DESC_ON_STACK(desc, kmd);
+ u32 saltlen;
+ unsigned int h = crypto_shash_digestsize(kmd);
+ int err;
+ const u8 null_salt[CRYPTO_HKDF_MAX_DIGESTSIZE] = { 0 };
+ u8 prk[CRYPTO_HKDF_MAX_DIGESTSIZE];
+
+ if (slen < sizeof(saltlen))
+ return -EINVAL;
+
+ saltlen = get_unaligned((u32 *)seed);
+
+ seed += sizeof(saltlen);
+ slen -= sizeof(saltlen);
+
+ if (slen < saltlen)
+ return -EINVAL;
+
+ crypto_kdf_init_desc(desc, kmd);
+
+ /* Set the salt as HMAC key */
+ if (saltlen)
+ err = crypto_shash_setkey(kmd, seed, saltlen);
+ else
+ err = crypto_shash_setkey(kmd, null_salt, h);
+ if (err)
+ return err;
+
+ /* Extract the PRK */
+ err = crypto_shash_digest(desc, seed + saltlen, slen - saltlen, prk);
+ if (err)
+ goto err;
+
+ /* Set the PRK for the expand phase */
+ err = crypto_shash_setkey(kmd, prk, h);
+
+err:
+ shash_desc_zero(desc);
+ memzero_explicit(prk, h);
+ return err;
+}
+
+static int crypto_hkdf_init_tfm(struct crypto_tfm *tfm)
+{
+ struct crypto_instance *inst = crypto_tfm_alg_instance(tfm);
+ struct crypto_shash_spawn *spawn = crypto_instance_ctx(inst);
+ struct crypto_hkdf_ctx *ctx = crypto_tfm_ctx(tfm);
+ struct crypto_shash *kmd;
+
+ kmd = crypto_spawn_shash(spawn);
+ if (IS_ERR(kmd))
+ return PTR_ERR(kmd);
+
+ ctx->kmd = kmd;
+
+ return 0;
+}
+
+static void crypto_hkdf_exit_tfm(struct crypto_tfm *tfm)
+{
+ struct crypto_hkdf_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ crypto_free_shash(ctx->kmd);
+}
+
+static void crypto_kdf_free(struct rng_instance *inst)
+{
+ crypto_drop_spawn(rng_instance_ctx(inst));
+ kfree(inst);
+}
+
+static int crypto_hkdf_create(struct crypto_template *tmpl, struct rtattr **tb)
+{
+ struct rng_instance *inst;
+ struct crypto_alg *alg;
+ struct shash_alg *salg;
+ int err;
+ unsigned int ds, ss;
+
+ err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_RNG);
+ if (err)
+ return err;
+
+ salg = shash_attr_alg(tb[1], 0, 0);
+ if (IS_ERR(salg))
+ return PTR_ERR(salg);
+
+ /* Require a keyed message digest */
+ if (!salg->setkey)
+ return -EOPNOTSUPP;
+
+ ds = salg->digestsize;
+ /* Hashes with no digest size are not allowed for KDFs. */
+ if (!ds || WARN_ON(ds > CRYPTO_HKDF_MAX_DIGESTSIZE))
+ return -EOPNOTSUPP;
+
+ ss = salg->statesize;
+ alg = &salg->base;
+
+ inst = rng_alloc_instance("hkdf", alg);
+ err = PTR_ERR(inst);
+ if (IS_ERR(inst))
+ goto out_put_alg;
+
+ err = crypto_init_shash_spawn(rng_instance_ctx(inst), salg,
+ rng_crypto_instance(inst));
+ if (err)
+ goto free_inst;
+
+ inst->alg.base.cra_priority = alg->cra_priority;
+ inst->alg.base.cra_blocksize = alg->cra_blocksize;
+ inst->alg.base.cra_alignmask = alg->cra_alignmask;
+
+ inst->alg.generate = crypto_hkdf_generate;
+ inst->alg.seed = crypto_hkdf_seed;
+
+ inst->alg.base.cra_init = crypto_hkdf_init_tfm;
+ inst->alg.base.cra_exit = crypto_hkdf_exit_tfm;
+ inst->alg.base.cra_ctxsize = sizeof(struct crypto_hkdf_ctx);
+
+ inst->free = crypto_kdf_free;
+
+ err = rng_register_instance(tmpl, inst);
+
+ if (err) {
+ crypto_drop_spawn(rng_instance_ctx(inst));
+free_inst:
+ kfree(inst);
+ }
+
+
+out_put_alg:
+ crypto_mod_put(alg);
+ return err;
+}
+
+static struct crypto_template crypto_hkdf_tmpl = {
+ .name = "hkdf",
+ .create = crypto_hkdf_create,
+ .module = THIS_MODULE,
+};
+
+static int __init crypto_hkdf_init(void)
+{
+ return crypto_register_template(&crypto_hkdf_tmpl);
+}
+
+static void __exit crypto_hkdf_exit(void)
+{
+ crypto_unregister_template(&crypto_hkdf_tmpl);
+}
+
+module_init(crypto_hkdf_init);
+module_exit(crypto_hkdf_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Stephan Mueller <[email protected]>");
+MODULE_DESCRIPTION("HKDF HMAC-based Extract-and-Expand Key Derivation Function (conformant to RFC5869)");
+MODULE_ALIAS_CRYPTO("hkdf");
--
2.20.1





2019-01-16 21:35:02

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v2 2/6] crypto: kdf - SP800-108 Key Derivation Function

The SP800-108 compliant Key Derivation Function provides three KDF
types which are all implemented, including the counter-based KDF,
the double-pipeline KDF and the feedback KDF.

The code comments provide details about how to invoke the different KDF
types.

Signed-off-by: Stephan Mueller <[email protected]>
---
crypto/Kconfig | 7 +
crypto/Makefile | 1 +
crypto/kdf_sp800108.c | 491 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 499 insertions(+)
create mode 100644 crypto/kdf_sp800108.c

diff --git a/crypto/Kconfig b/crypto/Kconfig
index 86960aa53e0f..7c0336c9ba9c 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -561,6 +561,13 @@ config CRYPTO_HMAC
HMAC: Keyed-Hashing for Message Authentication (RFC2104).
This is required for IPSec.

+config CRYPTO_KDF_SP800108
+ tristate "Key Derivation Function (SP800-108)"
+ select CRYPTO_RNG
+ help
+ Support for KDF compliant to SP800-108. All three types of
+ KDF specified in SP800-108 are implemented.
+
config CRYPTO_XCBC
tristate "XCBC support"
select CRYPTO_HASH
diff --git a/crypto/Makefile b/crypto/Makefile
index 799ed5e94606..eead7ec9fd8e 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -58,6 +58,7 @@ crypto_user-y := crypto_user_base.o
crypto_user-$(CONFIG_CRYPTO_STATS) += crypto_user_stat.o
obj-$(CONFIG_CRYPTO_CMAC) += cmac.o
obj-$(CONFIG_CRYPTO_HMAC) += hmac.o
+obj-$(CONFIG_CRYPTO_KDF_SP800108) += kdf_sp800108.o
obj-$(CONFIG_CRYPTO_VMAC) += vmac.o
obj-$(CONFIG_CRYPTO_XCBC) += xcbc.o
obj-$(CONFIG_CRYPTO_NULL2) += crypto_null.o
diff --git a/crypto/kdf_sp800108.c b/crypto/kdf_sp800108.c
new file mode 100644
index 000000000000..33b26659afa7
--- /dev/null
+++ b/crypto/kdf_sp800108.c
@@ -0,0 +1,491 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * SP800-108 Key-derivation function
+ *
+ * Copyright (C) 2019, Stephan Mueller <[email protected]>
+ */
+
+/*
+ * For performing a KDF operation, the following input is required
+ * from the caller:
+ *
+ * * Keying material to be used to derive the new keys from
+ * (denoted as Ko in SP800-108)
+ * * Label -- a free form binary string
+ * * Context -- a free form binary string
+ *
+ * The KDF is implemented as a random number generator.
+ *
+ * The Ko keying material is to be provided with the initialization of the KDF
+ * "random number generator", i.e. with the crypto_rng_reset function.
+ *
+ * The Label and Context concatenated string is provided when obtaining random
+ * numbers, i.e. with the crypto_rng_generate function. The caller must format
+ * the free-form Label || Context input as deemed necessary for the given
+ * purpose. Note, SP800-108 mandates that the Label and Context are separated
+ * by a 0x00 byte, i.e. the caller shall provide the input as
+ * Label || 0x00 || Context when trying to be compliant to SP800-108. For
+ * the feedback KDF, an IV is required as documented below.
+ *
+ * Example without proper error handling:
+ * char *keying_material = "\x00\x11\x22\x33\x44\x55\x66\x77";
+ * char *label_context = "\xde\xad\xbe\xef\x00\xde\xad\xbe\xef";
+ * kdf = crypto_alloc_rng(name, 0, 0);
+ * crypto_rng_reset(kdf, keying_material, 8);
+ * crypto_rng_generate(kdf, label_context, 9, outbuf, outbuflen);
+ */
+
+#include <linux/module.h>
+#include <crypto/rng.h>
+#include <crypto/internal/rng.h>
+#include <crypto/hash.h>
+#include <crypto/internal/hash.h>
+
+struct crypto_kdf_ctx {
+ struct crypto_shash *kmd;
+};
+
+#define CRYPTO_KDF_MAX_DIGESTSIZE 64
+#define CRYPTO_KDF_MAX_ALIGNMASK 0x3f
+
+static inline void crypto_kdf_init_desc(struct shash_desc *desc,
+ struct crypto_shash *kmd)
+{
+ desc->tfm = kmd;
+ desc->flags = 0;
+}
+
+/*
+ * Implementation of the KDF in double pipeline iteration mode according with
+ * counter to SP800-108 section 5.3.
+ *
+ * The caller must provide Label || 0x00 || Context in src. This src pointer
+ * may also be NULL if the caller wishes not to provide anything.
+ */
+static int crypto_kdf_dpi_generate(struct crypto_rng *rng,
+ const u8 *src, unsigned int slen,
+ u8 *dst, unsigned int dlen)
+{
+ const struct crypto_kdf_ctx *ctx = crypto_tfm_ctx(crypto_rng_tfm(rng));
+ struct crypto_shash *kmd = ctx->kmd;
+ SHASH_DESC_ON_STACK(desc, kmd);
+ __be32 counter = cpu_to_be32(1);
+ const unsigned int h = crypto_shash_digestsize(kmd);
+ const unsigned int alignmask = crypto_shash_alignmask(kmd);
+ int err = 0;
+ u8 *dst_orig = dst;
+ u8 Aiblock[CRYPTO_KDF_MAX_DIGESTSIZE + CRYPTO_KDF_MAX_ALIGNMASK];
+ u8 *Ai = PTR_ALIGN((u8 *)Aiblock, alignmask + 1);
+
+ crypto_kdf_init_desc(desc, kmd);
+
+ memset(Ai, 0, h);
+
+ while (dlen) {
+ /* Calculate A(i) */
+ if (dst == dst_orig && src && slen)
+ /* 5.3 step 4 and 5.a */
+ err = crypto_shash_digest(desc, src, slen, Ai);
+ else
+ /* 5.3 step 5.a */
+ err = crypto_shash_digest(desc, Ai, h, Ai);
+ if (err)
+ goto out;
+
+ /* Calculate K(i) -- step 5.b */
+ err = crypto_shash_init(desc);
+ if (err)
+ goto out;
+
+ err = crypto_shash_update(desc, Ai, h);
+ if (err)
+ goto out;
+
+ err = crypto_shash_update(desc, (u8 *)&counter, sizeof(__be32));
+ if (err)
+ goto out;
+ if (src && slen) {
+ err = crypto_shash_update(desc, src, slen);
+ if (err)
+ goto out;
+ }
+
+ if (dlen < h) {
+ u8 tmpbuffer[CRYPTO_KDF_MAX_DIGESTSIZE];
+
+ err = crypto_shash_final(desc, tmpbuffer);
+ if (err)
+ goto out;
+ memcpy(dst, tmpbuffer, dlen);
+ memzero_explicit(tmpbuffer, h);
+ goto out;
+ }
+
+ err = crypto_shash_final(desc, dst);
+ if (err)
+ goto out;
+ dlen -= h;
+ dst += h;
+ counter = cpu_to_be32(be32_to_cpu(counter) + 1);
+ }
+
+out:
+ if (err)
+ memzero_explicit(dst_orig, dlen);
+ shash_desc_zero(desc);
+ memzero_explicit(Ai, h);
+ return err;
+}
+
+/*
+ * Implementation of the KDF in feedback mode with a non-NULL IV and with
+ * counter according to SP800-108 section 5.2. The IV is supplied with src
+ * and must be equal to the digestsize of the used keyed message digest.
+ *
+ * In addition, the caller must provide Label || 0x00 || Context in src. This
+ * src pointer must not be NULL as the IV is required. The ultimate format of
+ * the src pointer is IV || Label || 0x00 || Context where the length of the
+ * IV is equal to the output size of the PRF.
+ */
+static int crypto_kdf_fb_generate(struct crypto_rng *rng,
+ const u8 *src, unsigned int slen,
+ u8 *dst, unsigned int dlen)
+{
+ const struct crypto_kdf_ctx *ctx = crypto_tfm_ctx(crypto_rng_tfm(rng));
+ struct crypto_shash *kmd = ctx->kmd;
+ SHASH_DESC_ON_STACK(desc, kmd);
+ __be32 counter = cpu_to_be32(1);
+ const unsigned int h = crypto_shash_digestsize(kmd);
+ unsigned int labellen = 0;
+ int err = 0;
+ u8 *dst_orig = dst;
+ const u8 *label;
+
+ /* require the presence of an IV */
+ if (!src || slen < h)
+ return -EINVAL;
+
+ crypto_kdf_init_desc(desc, kmd);
+
+ /* calculate the offset of the label / context data */
+ label = src + h;
+ labellen = slen - h;
+
+ while (dlen) {
+ err = crypto_shash_init(desc);
+ if (err)
+ goto out;
+
+ /*
+ * Feedback mode applies to all rounds except first which uses
+ * the IV.
+ */
+ if (dst_orig == dst)
+ err = crypto_shash_update(desc, src, h);
+ else
+ err = crypto_shash_update(desc, dst - h, h);
+ if (err)
+ goto out;
+
+ err = crypto_shash_update(desc, (u8 *)&counter, sizeof(__be32));
+ if (err)
+ goto out;
+ if (labellen) {
+ err = crypto_shash_update(desc, label, labellen);
+ if (err)
+ goto out;
+ }
+
+ if (dlen < h) {
+ u8 tmpbuffer[CRYPTO_KDF_MAX_DIGESTSIZE];
+
+ err = crypto_shash_final(desc, tmpbuffer);
+ if (err)
+ goto out;
+ memcpy(dst, tmpbuffer, dlen);
+ memzero_explicit(tmpbuffer, h);
+ goto out;
+ }
+
+ err = crypto_shash_final(desc, dst);
+ if (err)
+ goto out;
+ dlen -= h;
+ dst += h;
+ counter = cpu_to_be32(be32_to_cpu(counter) + 1);
+ }
+
+out:
+ if (err)
+ memzero_explicit(dst_orig, dlen);
+ return err;
+}
+
+/*
+ * Implementation of the KDF in counter mode according to SP800-108 section 5.1
+ * as well as SP800-56A section 5.8.1 (Single-step KDF).
+ *
+ * SP800-108:
+ * The caller must provide Label || 0x00 || Context in src. This src pointer
+ * may also be NULL if the caller wishes not to provide anything.
+ *
+ * SP800-56A:
+ * The key provided for the HMAC during the crypto_rng_reset shall NOT be the
+ * shared secret from the DH operation, but an independently generated key.
+ * The src pointer is defined as Z || other info where Z is the shared secret
+ * from DH and other info is an arbitrary string (see SP800-56A section
+ * 5.8.1.2).
+ */
+static int crypto_kdf_ctr_generate(struct crypto_rng *rng,
+ const u8 *src, unsigned int slen,
+ u8 *dst, unsigned int dlen)
+{
+ const struct crypto_kdf_ctx *ctx = crypto_tfm_ctx(crypto_rng_tfm(rng));
+ struct crypto_shash *kmd = ctx->kmd;
+ SHASH_DESC_ON_STACK(desc, kmd);
+ __be32 counter = cpu_to_be32(1);
+ const unsigned int h = crypto_shash_digestsize(kmd);
+ int err = 0;
+ u8 *dst_orig = dst;
+
+ crypto_kdf_init_desc(desc, kmd);
+
+ while (dlen) {
+ err = crypto_shash_init(desc);
+ if (err)
+ goto out;
+
+ err = crypto_shash_update(desc, (u8 *)&counter, sizeof(__be32));
+ if (err)
+ goto out;
+
+ if (src && slen) {
+ err = crypto_shash_update(desc, src, slen);
+ if (err)
+ goto out;
+ }
+
+ if (dlen < h) {
+ u8 tmpbuffer[CRYPTO_KDF_MAX_DIGESTSIZE];
+
+ err = crypto_shash_final(desc, tmpbuffer);
+ if (err)
+ goto out;
+ memcpy(dst, tmpbuffer, dlen);
+ memzero_explicit(tmpbuffer, h);
+ return 0;
+ }
+
+ err = crypto_shash_final(desc, dst);
+ if (err)
+ goto out;
+
+ dlen -= h;
+ dst += h;
+ counter = cpu_to_be32(be32_to_cpu(counter) + 1);
+ }
+
+out:
+ if (err)
+ memzero_explicit(dst_orig, dlen);
+ shash_desc_zero(desc);
+ return err;
+}
+
+/*
+ * The seeding of the KDF allows to set a key which must be at least
+ * digestsize long.
+ */
+static int crypto_kdf_seed(struct crypto_rng *rng,
+ const u8 *seed, unsigned int slen)
+{
+ const struct crypto_kdf_ctx *ctx = crypto_tfm_ctx(crypto_rng_tfm(rng));
+ unsigned int ds = crypto_shash_digestsize(ctx->kmd);
+
+ /* Check according to SP800-108 section 7.2 */
+ if (ds > slen)
+ return -EINVAL;
+
+ /*
+ * We require that we operate on a MAC -- if we do not operate on a
+ * MAC, this function returns an error.
+ */
+ return crypto_shash_setkey(ctx->kmd, seed, slen);
+}
+
+static int crypto_kdf_init_tfm(struct crypto_tfm *tfm)
+{
+ struct crypto_instance *inst = crypto_tfm_alg_instance(tfm);
+ struct crypto_shash_spawn *spawn = crypto_instance_ctx(inst);
+ struct crypto_kdf_ctx *ctx = crypto_tfm_ctx(tfm);
+ struct crypto_shash *kmd;
+
+ kmd = crypto_spawn_shash(spawn);
+ if (IS_ERR(kmd))
+ return PTR_ERR(kmd);
+
+ ctx->kmd = kmd;
+
+ return 0;
+}
+
+static void crypto_kdf_exit_tfm(struct crypto_tfm *tfm)
+{
+ struct crypto_kdf_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ crypto_free_shash(ctx->kmd);
+}
+
+static void crypto_kdf_free(struct rng_instance *inst)
+{
+ crypto_drop_spawn(rng_instance_ctx(inst));
+ kfree(inst);
+}
+
+static int crypto_kdf_alloc_common(struct crypto_template *tmpl,
+ struct rtattr **tb,
+ const u8 *name,
+ int (*generate)(struct crypto_rng *tfm,
+ const u8 *src,
+ unsigned int slen,
+ u8 *dst, unsigned int dlen))
+{
+ struct rng_instance *inst;
+ struct crypto_alg *alg;
+ struct shash_alg *salg;
+ int err;
+ unsigned int ds;
+
+ err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_RNG);
+ if (err)
+ return err;
+
+ salg = shash_attr_alg(tb[1], 0, 0);
+ if (IS_ERR(salg))
+ return PTR_ERR(salg);
+
+ /* Require a keyed message digest */
+ if (!salg->setkey)
+ return -EOPNOTSUPP;
+
+ ds = salg->digestsize;
+ /* Hashes with no digest size are not allowed for KDFs. */
+ if (!ds || WARN_ON(ds > CRYPTO_KDF_MAX_DIGESTSIZE))
+ return -EOPNOTSUPP;
+
+ alg = &salg->base;
+ if (WARN_ON(CRYPTO_KDF_MAX_ALIGNMASK < alg->cra_alignmask))
+ return -EOPNOTSUPP;
+
+ inst = rng_alloc_instance(name, alg);
+ err = PTR_ERR(inst);
+ if (IS_ERR(inst))
+ goto out_put_alg;
+
+ err = crypto_init_shash_spawn(rng_instance_ctx(inst), salg,
+ rng_crypto_instance(inst));
+ if (err)
+ goto free_inst;
+
+ inst->alg.base.cra_priority = alg->cra_priority;
+ inst->alg.base.cra_blocksize = alg->cra_blocksize;
+ inst->alg.base.cra_alignmask = alg->cra_alignmask;
+
+ inst->alg.generate = generate;
+ inst->alg.seed = crypto_kdf_seed;
+ inst->alg.seedsize = ds;
+
+ inst->alg.base.cra_init = crypto_kdf_init_tfm;
+ inst->alg.base.cra_exit = crypto_kdf_exit_tfm;
+ inst->alg.base.cra_ctxsize = sizeof(struct crypto_kdf_ctx);
+
+ inst->free = crypto_kdf_free;
+
+ err = rng_register_instance(tmpl, inst);
+
+ if (err) {
+ crypto_drop_spawn(rng_instance_ctx(inst));
+free_inst:
+ kfree(inst);
+ }
+
+out_put_alg:
+ crypto_mod_put(alg);
+ return err;
+}
+
+static int crypto_kdf_ctr_create(struct crypto_template *tmpl,
+ struct rtattr **tb)
+{
+ return crypto_kdf_alloc_common(tmpl, tb, "kdf_ctr",
+ crypto_kdf_ctr_generate);
+}
+
+static struct crypto_template crypto_kdf_ctr_tmpl = {
+ .name = "kdf_ctr",
+ .create = crypto_kdf_ctr_create,
+ .module = THIS_MODULE,
+};
+
+static int crypto_kdf_fb_create(struct crypto_template *tmpl,
+ struct rtattr **tb) {
+ return crypto_kdf_alloc_common(tmpl, tb, "kdf_fb",
+ crypto_kdf_fb_generate);
+}
+
+static struct crypto_template crypto_kdf_fb_tmpl = {
+ .name = "kdf_fb",
+ .create = crypto_kdf_fb_create,
+ .module = THIS_MODULE,
+};
+
+static int crypto_kdf_dpi_create(struct crypto_template *tmpl,
+ struct rtattr **tb) {
+ return crypto_kdf_alloc_common(tmpl, tb, "kdf_dpi",
+ crypto_kdf_dpi_generate);
+}
+
+static struct crypto_template crypto_kdf_dpi_tmpl = {
+ .name = "kdf_dpi",
+ .create = crypto_kdf_dpi_create,
+ .module = THIS_MODULE,
+};
+
+static int __init crypto_kdf_init(void)
+{
+ int err = crypto_register_template(&crypto_kdf_ctr_tmpl);
+
+ if (err)
+ return err;
+
+ err = crypto_register_template(&crypto_kdf_fb_tmpl);
+ if (err) {
+ crypto_unregister_template(&crypto_kdf_ctr_tmpl);
+ return err;
+ }
+
+ err = crypto_register_template(&crypto_kdf_dpi_tmpl);
+ if (err) {
+ crypto_unregister_template(&crypto_kdf_ctr_tmpl);
+ crypto_unregister_template(&crypto_kdf_fb_tmpl);
+ }
+ return err;
+}
+
+static void __exit crypto_kdf_exit(void)
+{
+ crypto_unregister_template(&crypto_kdf_ctr_tmpl);
+ crypto_unregister_template(&crypto_kdf_fb_tmpl);
+ crypto_unregister_template(&crypto_kdf_dpi_tmpl);
+}
+
+module_init(crypto_kdf_init);
+module_exit(crypto_kdf_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Stephan Mueller <[email protected]>");
+MODULE_DESCRIPTION("Key Derivation Function conformant to SP800-108");
+MODULE_ALIAS_CRYPTO("kdf_ctr");
+MODULE_ALIAS_CRYPTO("kdf_fb");
+MODULE_ALIAS_CRYPTO("kdf_dpi");
--
2.20.1





2019-01-16 21:35:56

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v2 6/6] crypto: tcrypt - add KDF test invocation

Enable testing of the SP800-108 and RFC5869 KDFs.

Signed-off-by: Stephan Mueller <[email protected]>
---
crypto/tcrypt.c | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/crypto/tcrypt.c b/crypto/tcrypt.c
index e7fb87e114a5..5606e59e80ec 100644
--- a/crypto/tcrypt.c
+++ b/crypto/tcrypt.c
@@ -2054,6 +2054,14 @@ static int do_test(const char *alg, u32 type, u32 mask, int m, u32 num_mb)
ret += tcrypt_test("cbc(sm4)");
ret += tcrypt_test("ctr(sm4)");
break;
+ case 192:
+ ret += tcrypt_test("kdf_ctr(hmac(sha256))");
+ ret += tcrypt_test("kdf_dpi(hmac(sha256))");
+ ret += tcrypt_test("kdf_fb(hmac(sha256))");
+ break;
+ case 193:
+ ret += tcrypt_test("hkdf(hmac(sha256))");
+ break;
case 200:
test_cipher_speed("ecb(aes)", ENCRYPT, sec, NULL, 0,
speed_template_16_24_32);
--
2.20.1





2019-01-17 03:31:03

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v2 0/6] General Key Derivation Function Support

Changes v2:
* Incorporation of all comments from Eric Biggers

Stephan Mueller (6):
crypto: add template handling for RNGs
crypto: kdf - SP800-108 Key Derivation Function
crypto: kdf - add known answer tests
crypto: hkdf - HMAC-based Extract-and-Expand KDF
crypto: hkdf - add known answer tests
crypto: tcrypt - add KDF test invocation

crypto/Kconfig | 13 +
crypto/Makefile | 2 +
crypto/hkdf.c | 272 +++++++++++++++++++
crypto/kdf_sp800108.c | 491 ++++++++++++++++++++++++++++++++++
crypto/rng.c | 44 +++
crypto/tcrypt.c | 8 +
crypto/testmgr.c | 245 +++++++++++++++++
crypto/testmgr.h | 198 ++++++++++++++
include/crypto/internal/rng.h | 26 ++
9 files changed, 1299 insertions(+)
create mode 100644 crypto/hkdf.c
create mode 100644 crypto/kdf_sp800108.c

--
2.20.1





2019-01-18 14:36:49

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

On Fri, Jan 11, 2019 at 07:28:58AM -0800, James Bottomley wrote:
> On Fri, 2019-01-11 at 16:02 +0200, Jarkko Sakkinen wrote:
> > On Tue, Jan 08, 2019 at 05:43:53PM -0800, Andy Lutomirski wrote:
> > > (Also, do we have a sensible story of how the TPM interacts with
> > > hibernation at all? Presumably we should at least try to replay
> > > the PCR operations that have occurred so that we can massage the
> > > PCRs into the same state post-hibernation. Also, do we have any
> > > way for the kernel to sign something with the TPM along with an
> > > attestation that the signature was requested *by the
> > > kernel*? Something like a sub-hierarchy of keys that the kernel
> > > explicitly prevents userspace from accessing?)
> >
> > Kernel can keep it is own key hierarchy in memory as TPM2 chips allow
> > to offload data in encrypted form and load it to TPM when it needs to
> > use it.
> >
> > The in-kernel resource manager that I initiated couple years ago
> > provides this type of functionality.
>
> Actually, the resource manager only keeps volatile objects separated
> when in use not when offloaded. The problem here is that the object
> needs to be persisted across reboots, so either it gets written to the
> NV area, bypassing the resource manager and making it globally visible
> or it has to get stored in TPM form in the hibernation image, meaning
> anyone with access to the TPM who can read the image can extract and
> load it. Further: anyone with access to the TPM can create a bogus
> sealed key and encrypt a malicious hibernation image with it. So there
> are two additional problems
>
> 1. Given that the attacker may have access to the binary form of the
> key, can we make sure only the kernel can get it released?
> 2. How do we prevent an attacker with access to the TPM from creating a
> bogus sealed key?
>
> This is why I was thinking localities.

Why you would want to go for localities and not seal to PCRs?

/Jarkko

2019-01-18 21:00:49

by James Bottomley

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

On Fri, 2019-01-18 at 16:33 +0200, Jarkko Sakkinen wrote:
> On Fri, Jan 11, 2019 at 07:28:58AM -0800, James Bottomley wrote:
> > On Fri, 2019-01-11 at 16:02 +0200, Jarkko Sakkinen wrote:
> > > On Tue, Jan 08, 2019 at 05:43:53PM -0800, Andy Lutomirski wrote:
> > > > (Also, do we have a sensible story of how the TPM interacts
> > > > with hibernation at all? Presumably we should at least try to
> > > > replay the PCR operations that have occurred so that we can
> > > > massage the PCRs into the same state post-hibernation. Also,
> > > > do we have any way for the kernel to sign something with the
> > > > TPM along with an attestation that the signature was requested
> > > > *by the kernel*? Something like a sub-hierarchy of keys that
> > > > the kernel explicitly prevents userspace from accessing?)
> > >
> > > Kernel can keep it is own key hierarchy in memory as TPM2 chips
> > > allow to offload data in encrypted form and load it to TPM when
> > > it needs to use it.
> > >
> > > The in-kernel resource manager that I initiated couple years ago
> > > provides this type of functionality.
> >
> > Actually, the resource manager only keeps volatile objects
> > separated when in use not when offloaded. The problem here is that
> > the object needs to be persisted across reboots, so either it gets
> > written to the NV area, bypassing the resource manager and making
> > it globally visible or it has to get stored in TPM form in the
> > hibernation image, meaning anyone with access to the TPM who can
> > read the image can extract and load it. Further: anyone with access
> > to the TPM can create a bogus sealed key and encrypt a malicious
> > hibernation image with it. So there are two additional problems
> >
> > 1. Given that the attacker may have access to the binary form of
> > the
> > key, can we make sure only the kernel can get it released?
> > 2. How do we prevent an attacker with access to the TPM from
> > creating a
> > bogus sealed key?
> >
> > This is why I was thinking localities.
>
> Why you would want to go for localities and not seal to PCRs?

Because the requested functionality was a key that would be accessible
to the kernel and not to user space and also guaranteed created by the
kernel. The only discriminator we have to enforce that is the locality
(assuming we reserve a locality as accessible to the kernel but
inaccessible to userspace). PCRs alone can't restrict where the key is
accessed or created from.

James


2019-01-20 16:03:33

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler

On Fri, Jan 18, 2019 at 12:59:06PM -0800, James Bottomley wrote:
> On Fri, 2019-01-18 at 16:33 +0200, Jarkko Sakkinen wrote:
> > On Fri, Jan 11, 2019 at 07:28:58AM -0800, James Bottomley wrote:
> > > On Fri, 2019-01-11 at 16:02 +0200, Jarkko Sakkinen wrote:
> > > > On Tue, Jan 08, 2019 at 05:43:53PM -0800, Andy Lutomirski wrote:
> > > > > (Also, do we have a sensible story of how the TPM interacts
> > > > > with hibernation at all? Presumably we should at least try to
> > > > > replay the PCR operations that have occurred so that we can
> > > > > massage the PCRs into the same state post-hibernation. Also,
> > > > > do we have any way for the kernel to sign something with the
> > > > > TPM along with an attestation that the signature was requested
> > > > > *by the kernel*? Something like a sub-hierarchy of keys that
> > > > > the kernel explicitly prevents userspace from accessing?)
> > > >
> > > > Kernel can keep it is own key hierarchy in memory as TPM2 chips
> > > > allow to offload data in encrypted form and load it to TPM when
> > > > it needs to use it.
> > > >
> > > > The in-kernel resource manager that I initiated couple years ago
> > > > provides this type of functionality.
> > >
> > > Actually, the resource manager only keeps volatile objects
> > > separated when in use not when offloaded. The problem here is that
> > > the object needs to be persisted across reboots, so either it gets
> > > written to the NV area, bypassing the resource manager and making
> > > it globally visible or it has to get stored in TPM form in the
> > > hibernation image, meaning anyone with access to the TPM who can
> > > read the image can extract and load it. Further: anyone with access
> > > to the TPM can create a bogus sealed key and encrypt a malicious
> > > hibernation image with it. So there are two additional problems
> > >
> > > 1. Given that the attacker may have access to the binary form of
> > > the
> > > key, can we make sure only the kernel can get it released?
> > > 2. How do we prevent an attacker with access to the TPM from
> > > creating a
> > > bogus sealed key?
> > >
> > > This is why I was thinking localities.
> >
> > Why you would want to go for localities and not seal to PCRs?
>
> Because the requested functionality was a key that would be accessible
> to the kernel and not to user space and also guaranteed created by the
> kernel. The only discriminator we have to enforce that is the locality
> (assuming we reserve a locality as accessible to the kernel but
> inaccessible to userspace). PCRs alone can't restrict where the key is
> accessed or created from.

OK, locality would probably make sense, assuming that the key is stored
in nvram.

/Jarkko

/Jarkko

2019-01-28 10:09:44

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH v2 0/6] General Key Derivation Function Support

Am Mittwoch, 16. Januar 2019, 12:06:54 CET schrieb Stephan M?ller:

Hi Herbert,

> Changes v2:
> * Incorporation of all comments from Eric Biggers
>
> Stephan Mueller (6):
> crypto: add template handling for RNGs
> crypto: kdf - SP800-108 Key Derivation Function
> crypto: kdf - add known answer tests
> crypto: hkdf - HMAC-based Extract-and-Expand KDF
> crypto: hkdf - add known answer tests
> crypto: tcrypt - add KDF test invocation
>
> crypto/Kconfig | 13 +
> crypto/Makefile | 2 +
> crypto/hkdf.c | 272 +++++++++++++++++++
> crypto/kdf_sp800108.c | 491 ++++++++++++++++++++++++++++++++++
> crypto/rng.c | 44 +++
> crypto/tcrypt.c | 8 +
> crypto/testmgr.c | 245 +++++++++++++++++
> crypto/testmgr.h | 198 ++++++++++++++
> include/crypto/internal/rng.h | 26 ++
> 9 files changed, 1299 insertions(+)
> create mode 100644 crypto/hkdf.c
> create mode 100644 crypto/kdf_sp800108.c

Do you happen to have any comments on this patch set?

Ciao
Stephan



2019-01-30 10:10:29

by Herbert Xu

[permalink] [raw]
Subject: Re: [PATCH v2 0/6] General Key Derivation Function Support

On Mon, Jan 28, 2019 at 11:07:28AM +0100, Stephan Mueller wrote:
> Am Mittwoch, 16. Januar 2019, 12:06:54 CET schrieb Stephan M?ller:
>
> Hi Herbert,
>
> > Changes v2:
> > * Incorporation of all comments from Eric Biggers
> >
> > Stephan Mueller (6):
> > crypto: add template handling for RNGs
> > crypto: kdf - SP800-108 Key Derivation Function
> > crypto: kdf - add known answer tests
> > crypto: hkdf - HMAC-based Extract-and-Expand KDF
> > crypto: hkdf - add known answer tests
> > crypto: tcrypt - add KDF test invocation
> >
> > crypto/Kconfig | 13 +
> > crypto/Makefile | 2 +
> > crypto/hkdf.c | 272 +++++++++++++++++++
> > crypto/kdf_sp800108.c | 491 ++++++++++++++++++++++++++++++++++
> > crypto/rng.c | 44 +++
> > crypto/tcrypt.c | 8 +
> > crypto/testmgr.c | 245 +++++++++++++++++
> > crypto/testmgr.h | 198 ++++++++++++++
> > include/crypto/internal/rng.h | 26 ++
> > 9 files changed, 1299 insertions(+)
> > create mode 100644 crypto/hkdf.c
> > create mode 100644 crypto/kdf_sp800108.c
>
> Do you happen to have any comments on this patch set?

I'm still not convinced why this needs to go into the crypto API
instead of being hosted in a helper which should achieve pretty
much the same result.

Thanks,
--
Email: Herbert Xu <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

2019-01-30 14:40:00

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH v2 0/6] General Key Derivation Function Support

Am Mittwoch, 30. Januar 2019, 11:08:54 CET schrieb Herbert Xu:

Hi Herbert,

> I'm still not convinced why this needs to go into the crypto API
> instead of being hosted in a helper which should achieve pretty
> much the same result.

How do you propose to handle the FIPS 140-2 related requirements of enforcing
the integrity test result and the known-answer self tests?

Thanks
Stephan



2019-02-08 07:47:27

by Herbert Xu

[permalink] [raw]
Subject: Re: [PATCH v2 0/6] General Key Derivation Function Support

On Wed, Jan 30, 2019 at 03:39:10PM +0100, Stephan Mueller wrote:
> Am Mittwoch, 30. Januar 2019, 11:08:54 CET schrieb Herbert Xu:
>
> Hi Herbert,
>
> > I'm still not convinced why this needs to go into the crypto API
> > instead of being hosted in a helper which should achieve pretty
> > much the same result.
>
> How do you propose to handle the FIPS 140-2 related requirements of enforcing
> the integrity test result and the known-answer self tests?

We could easily add self-tests for the helper.

Cheers,
--
Email: Herbert Xu <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

2019-02-08 08:01:54

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH v2 0/6] General Key Derivation Function Support

Am Freitag, 8. Februar 2019, 08:45:58 CET schrieb Herbert Xu:

Hi Herbert,

> We could easily add self-tests for the helper.

Thanks for the clarification. And do you have a suggestion how we can should
ensure that the self-tests are run only once?

Also, shall we add the signature verification enforcemnt to the helper as
well?


Ciao
Stephan



2019-02-08 08:08:37

by Herbert Xu

[permalink] [raw]
Subject: Re: [PATCH v2 0/6] General Key Derivation Function Support

On Fri, Feb 08, 2019 at 09:00:59AM +0100, Stephan Mueller wrote:
>
> Thanks for the clarification. And do you have a suggestion how we can should
> ensure that the self-tests are run only once?

Making it run at kernel init time should be good enough.

> Also, shall we add the signature verification enforcemnt to the helper as
> well?

What do you mean by that?

Cheers,
--
Email: Herbert Xu <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

2019-02-08 08:19:04

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH v2 0/6] General Key Derivation Function Support

Am Freitag, 8. Februar 2019, 09:05:47 CET schrieb Herbert Xu:

Hi Herbert,
>
> > Also, shall we add the signature verification enforcemnt to the helper as
> > well?
>
> What do you mean by that?

We need to invoke the function crypto_check_module_sig when the module is
loaded. Do you have any concerns invoking it from the module init function?

Ciao
Stephan



2019-02-19 05:46:48

by Herbert Xu

[permalink] [raw]
Subject: Re: [PATCH v2 0/6] General Key Derivation Function Support

On Fri, Feb 08, 2019 at 09:17:50AM +0100, Stephan Mueller wrote:
> >
> > > Also, shall we add the signature verification enforcemnt to the helper as
> > > well?
> >
> > What do you mean by that?
>
> We need to invoke the function crypto_check_module_sig when the module is
> loaded. Do you have any concerns invoking it from the module init function?

Which module's signature are you going to check? If it's the
underlying crypto API modules then wouldn't they have been
checked already when they were registered?

Cheers,
--
Email: Herbert Xu <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt