2024-03-29 02:12:16

by Chang S. Bae

[permalink] [raw]
Subject: [PATCH v9 07/14] x86/cpu/keylocker: Load a wrapping key at boot time

The wrapping key is an entity to encode a clear text key into a key
handle. This key is a pivot in protecting user keys. So the value has
to be randomized before being loaded in the software-invisible CPU
state.

The wrapping key needs to be established before the first user. Given
that the only proposed Linux use case for Key Locker is dm-crypt, the
feature could be lazily enabled before the first dm-crypt user arrives.

But there is no precedent for late enabling of CPU features and it
adds maintenance burden without demonstrative benefit outside of
minimizing the visibility of Key Locker to userspace.

Therefore, generate random bytes and load them at boot time, involving
clobbering XMM registers. Perform this process under arch_initcall(),
ensuring that it occurs after FPU initialization. Finally, flush out
random bytes after loading.

Given that the Linux Key Locker support is only intended for bare
metal dm-crypt use, and that switching wrapping key per virtual machine
is impractical, explicitly skip this setup in the X86_FEATURE_HYPERVISOR
case.

Signed-off-by: Chang S. Bae <[email protected]>
Cc: Eric Biggers <[email protected]>
Cc: Dave Hansen <[email protected]>
Cc: "Elliott, Robert (Servers)" <[email protected]>
Cc: Dan Williams <[email protected]>
---
Changes from v8:
* Invoke the setup code via arch_initcall(). The move was due to the
upstream changes. Commit b81fac906a8f ("x86/fpu: Move FPU
initialization into arch_cpu_finalize_init()") delays the FPU setup.
* Tweak code comments and the changelog.
* Revoke the review tag as the code change is significant.

Changes from v6:
* Switch to use 'static inline' for the empty functions, instead of
macro that disallows type checks. (Eric Biggers and Dave Hansen)
* Use memzero_explicit() to wipe out the key data instead of writing
the poison value over there. (Robert Elliott)
* Massage the changelog for the better readability.

Changes from v5:
* Call out the disabling when the feature is available on a virtual
machine. Then, it will turn off the feature flag

Changes from RFC v2:
* Make bare metal only.
* Clean up the code (e.g. dynamically allocate the key cache).
(Dan Williams)
* Massage the changelog.
* Move out the LOADIWKEY wrapper and the Key Locker CPUID defines.
---
arch/x86/kernel/Makefile | 1 +
arch/x86/kernel/keylocker.c | 77 +++++++++++++++++++++++++++++++++++++
2 files changed, 78 insertions(+)
create mode 100644 arch/x86/kernel/keylocker.c

diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 74077694da7d..d105e5785b90 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -137,6 +137,7 @@ obj-$(CONFIG_PERF_EVENTS) += perf_regs.o
obj-$(CONFIG_TRACING) += tracepoint.o
obj-$(CONFIG_SCHED_MC_PRIO) += itmt.o
obj-$(CONFIG_X86_UMIP) += umip.o
+obj-$(CONFIG_X86_KEYLOCKER) += keylocker.o

obj-$(CONFIG_UNWINDER_ORC) += unwind_orc.o
obj-$(CONFIG_UNWINDER_FRAME_POINTER) += unwind_frame.o
diff --git a/arch/x86/kernel/keylocker.c b/arch/x86/kernel/keylocker.c
new file mode 100644
index 000000000000..0d6b715baf1e
--- /dev/null
+++ b/arch/x86/kernel/keylocker.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Setup Key Locker feature and support the wrapping key management.
+ */
+
+#include <linux/random.h>
+#include <linux/string.h>
+
+#include <asm/fpu/api.h>
+#include <asm/keylocker.h>
+#include <asm/processor.h>
+
+static struct iwkey wrapping_key __initdata;
+
+static void __init generate_keylocker_data(void)
+{
+ get_random_bytes(&wrapping_key.integrity_key, sizeof(wrapping_key.integrity_key));
+ get_random_bytes(&wrapping_key.encryption_key, sizeof(wrapping_key.encryption_key));
+}
+
+static void __init destroy_keylocker_data(void)
+{
+ memzero_explicit(&wrapping_key, sizeof(wrapping_key));
+}
+
+/*
+ * For loading the wrapping key into each CPU, the feature bit is set
+ * in the control register and FPU context management is performed.
+ */
+static void __init load_keylocker(struct work_struct *unused)
+{
+ cr4_set_bits(X86_CR4_KEYLOCKER);
+
+ kernel_fpu_begin();
+ load_xmm_iwkey(&wrapping_key);
+ kernel_fpu_end();
+}
+
+static int __init init_keylocker(void)
+{
+ u32 eax, ebx, ecx, edx;
+
+ if (!cpu_feature_enabled(X86_FEATURE_KEYLOCKER))
+ goto disable;
+
+ if (cpu_feature_enabled(X86_FEATURE_HYPERVISOR)) {
+ pr_debug("x86/keylocker: Not compatible with a hypervisor.\n");
+ goto clear_cap;
+ }
+
+ cr4_set_bits(X86_CR4_KEYLOCKER);
+
+ /* AESKLE depends on CR4.KEYLOCKER */
+ cpuid_count(KEYLOCKER_CPUID, 0, &eax, &ebx, &ecx, &edx);
+ if (!(ebx & KEYLOCKER_CPUID_EBX_AESKLE) ||
+ !(eax & KEYLOCKER_CPUID_EAX_SUPERVISOR)) {
+ pr_debug("x86/keylocker: Not fully supported.\n");
+ goto clear_cap;
+ }
+
+ generate_keylocker_data();
+ schedule_on_each_cpu(load_keylocker);
+ destroy_keylocker_data();
+
+ pr_info_once("x86/keylocker: Enabled.\n");
+ return 0;
+
+clear_cap:
+ setup_clear_cpu_cap(X86_FEATURE_KEYLOCKER);
+ pr_info_once("x86/keylocker: Disabled.\n");
+disable:
+ cr4_clear_bits(X86_CR4_KEYLOCKER);
+ return -ENODEV;
+}
+
+arch_initcall(init_keylocker);
--
2.34.1



2024-04-07 23:21:00

by Chang S. Bae

[permalink] [raw]
Subject: [PATCH v9a 07/14] x86/cpu/keylocker: Load a wrapping key at boot time

The wrapping key is an entity to encode a clear text key into a key
handle. This key is a pivot in protecting user keys. So the value has
to be randomized before being loaded in the software-invisible CPU
state.

The wrapping key needs to be established before the first user. Given
that the only proposed Linux use case for Key Locker is dm-crypt, the
feature could be lazily enabled before the first dm-crypt user arrives.

But there is no precedent for late enabling of CPU features and it
adds maintenance burden without demonstrative benefit outside of
minimizing the visibility of Key Locker to userspace.

Therefore, generate random bytes and load them at boot time, involving
clobbering XMM registers. Perform this process under arch_initcall(),
ensuring that it occurs after FPU initialization. Finally, flush out
random bytes after loading.

Given that the Linux Key Locker support is only intended for bare
metal dm-crypt use, and that switching wrapping key per virtual machine
is impractical, explicitly skip this setup in the X86_FEATURE_HYPERVISOR
case.

Signed-off-by: Chang S. Bae <[email protected]>
Cc: Eric Biggers <[email protected]>
Cc: Dave Hansen <[email protected]>
Cc: "Elliott, Robert (Servers)" <[email protected]>
Cc: Dan Williams <[email protected]>
---
Changes from v9:
* Include 'tlbflush.h' back, which was once removed by mistake.
---
arch/x86/kernel/Makefile | 1 +
arch/x86/kernel/keylocker.c | 78 +++++++++++++++++++++++++++++++++++++
2 files changed, 79 insertions(+)
create mode 100644 arch/x86/kernel/keylocker.c

diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 74077694da7d..d105e5785b90 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -137,6 +137,7 @@ obj-$(CONFIG_PERF_EVENTS) += perf_regs.o
obj-$(CONFIG_TRACING) += tracepoint.o
obj-$(CONFIG_SCHED_MC_PRIO) += itmt.o
obj-$(CONFIG_X86_UMIP) += umip.o
+obj-$(CONFIG_X86_KEYLOCKER) += keylocker.o

obj-$(CONFIG_UNWINDER_ORC) += unwind_orc.o
obj-$(CONFIG_UNWINDER_FRAME_POINTER) += unwind_frame.o
diff --git a/arch/x86/kernel/keylocker.c b/arch/x86/kernel/keylocker.c
new file mode 100644
index 000000000000..8569b92971da
--- /dev/null
+++ b/arch/x86/kernel/keylocker.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Setup Key Locker feature and support the wrapping key management.
+ */
+
+#include <linux/random.h>
+#include <linux/string.h>
+
+#include <asm/fpu/api.h>
+#include <asm/keylocker.h>
+#include <asm/processor.h>
+#include <asm/tlbflush.h>
+
+static struct iwkey wrapping_key __initdata;
+
+static void __init generate_keylocker_data(void)
+{
+ get_random_bytes(&wrapping_key.integrity_key, sizeof(wrapping_key.integrity_key));
+ get_random_bytes(&wrapping_key.encryption_key, sizeof(wrapping_key.encryption_key));
+}
+
+static void __init destroy_keylocker_data(void)
+{
+ memzero_explicit(&wrapping_key, sizeof(wrapping_key));
+}
+
+/*
+ * For loading the wrapping key into each CPU, the feature bit is set
+ * in the control register and FPU context management is performed.
+ */
+static void __init load_keylocker(struct work_struct *unused)
+{
+ cr4_set_bits(X86_CR4_KEYLOCKER);
+
+ kernel_fpu_begin();
+ load_xmm_iwkey(&wrapping_key);
+ kernel_fpu_end();
+}
+
+static int __init init_keylocker(void)
+{
+ u32 eax, ebx, ecx, edx;
+
+ if (!cpu_feature_enabled(X86_FEATURE_KEYLOCKER))
+ goto disable;
+
+ if (cpu_feature_enabled(X86_FEATURE_HYPERVISOR)) {
+ pr_debug("x86/keylocker: Not compatible with a hypervisor.\n");
+ goto clear_cap;
+ }
+
+ cr4_set_bits(X86_CR4_KEYLOCKER);
+
+ /* AESKLE depends on CR4.KEYLOCKER */
+ cpuid_count(KEYLOCKER_CPUID, 0, &eax, &ebx, &ecx, &edx);
+ if (!(ebx & KEYLOCKER_CPUID_EBX_AESKLE) ||
+ !(eax & KEYLOCKER_CPUID_EAX_SUPERVISOR)) {
+ pr_debug("x86/keylocker: Not fully supported.\n");
+ goto clear_cap;
+ }
+
+ generate_keylocker_data();
+ schedule_on_each_cpu(load_keylocker);
+ destroy_keylocker_data();
+
+ pr_info_once("x86/keylocker: Enabled.\n");
+ return 0;
+
+clear_cap:
+ setup_clear_cpu_cap(X86_FEATURE_KEYLOCKER);
+ pr_info_once("x86/keylocker: Disabled.\n");
+disable:
+ cr4_clear_bits(X86_CR4_KEYLOCKER);
+ return -ENODEV;
+}
+
+arch_initcall(init_keylocker);
--
2.40.1