mbox series

[v4,00/13] x86: Support Key Locker

Message ID 20211214005212.20588-1-chang.seok.bae@intel.com
Headers show
Series x86: Support Key Locker | expand

Message

Chang S. Bae Dec. 14, 2021, 12:51 a.m. UTC
Recall that AES-NI [1] is a set of CPU instructions to perform encryption
operations. Like all other software encryption implementations it relies on
OS memory protections to keep clear-text key material from being
exfiltrated across security domains. However, there are demonstrated
methods for exfiltrating data across security domains.

Key Locker [2] is a CPU feature to reduce key exfiltration opportunities
while maintaining a programming interface similar to AES-NI. It converts
the AES key into an encoded form, called the 'key handle' [3]. The key
handle is a wrapped version of the clear-text key where the wrapping key
has limited exposure. Once converted, all subsequent data encryption using
new AES instructions (AES-KL) uses this key handle, reducing the exposure
of private key material in memory.

As mentioned, Key Locker introduces a CPU-internal wrapping key. This key
is loaded in a CPU state that software can not access, and then it is used
to convert the AES keys. At every boot, a new randomized key is
generated and loaded. Thus, the key handle is revoked upon system reboot
(including kexec-reboot).

== Threat Model and Mitigation Description ==

A targeted attack is with brief physical access [4] to the victim device in
unlock state or with the screen locked, but with keys in memory. For
example, the attacker might use a malicious USB peripheral to exploit a
kernel bug or perform a cold boot attack [4], and then extract the disk
encryption master key. Using this master key, the attacker will be able to
extract the contents of the disk. This includes data yet-to-be-written when
acquiring the device from the victim user at a later point in time and
performing forensic analysis.

Key Locker reduces opportunities for long-lived keys to be exfiltrated from
system RAM. Once the key is converted to a key handle, the clear text key
is no longer available to a class of data exfiltration techniques.

== Disk Encryption Use Case ==

Disk encryption uses Key Locker to mitigate key exfiltration as follows:

1. Configuration for Key Locker: AES-KL shows up in /proc/crypto as a
   distinct cipher option. From there, tools like cryptsetup [5] can select
   AES-KL vs AES-NI. For example,

   $ cryptsetup luksFormat --cipher="capi:xts-aes-aeskl-plain" <device>

Note: AES-KL has a performance tradeoff. See details in 'Performance'
below.

2. Disk encryption flow with key protection:

* The cryptsetup utility is responsible for loading the volume key into the
  kernel's keyring and passing a reference of the key. Once dm-crypt [6]
  has set up the volume, user space is responsible for invalidating the key
  material so that only the key handle remains in memory. Cryptsetup does
  this, e.g. via crypt_free_volume_key() and crypt_safe_free().

* The AES-KL code in the kernel's crypto library uses the key handle
  instead of the actual clear text key.

== Non Use Cases ==

Bare metal disk encryption is the only use case intended by these patches.
Userspace usage is not supported because there is no ABI provided to
communicate and coordinate wrapping-key restore failures to userspace.
For now, the key restore failures are only coordinated with kernel users.
For this reason a "keylocker" indicator is not published in /proc/cpuinfo.
At the same time, the kernel can not prevent userspace from using the
AES-KL instructions when Key Locker support has been enabled, so the lack
of userspace support is only documented, not actively enforced. Key Locker
support is not enumerated to VM guests.

== Performance ==

This feature comes with some performance penalty vs AESNI. The cryptsetup
utility [5] has been used to measure the Key Locker performance. Below
results have been measured [8] on an Intel 11th-gen Core Processor, code
named Tigerlake mobile part [9].

Below is the average encryption and decryption rates with key size of 256b.

Commands:
cryptsetup version – 2.3.4
$ cryptsetup benchmark-c aes-cbc -s 256
$ cryptsetup benchmark-c aes-xts -s 256

Tests are approximate using memory only (no storage IO).

+-----------+---------------+---------------+
| Cipher    |   Encryption  | Decryption    |
| (AES-NI)  |    (MiB/s)    | (MiB/s)       |
+-----------+---------------+---------------+
| AES-CBC   |     1242.6    |   4446.5      |
| AES-XTS   |     4233.3    |   4359.7      |
+-----------+-------------------------------+

+-----------+---------------+---------------+
| Cipher    |   Encryption  | Decryption    |
| (AES-KL)  |    (MiB/s)    | (MiB/s)       |
+-----------+---------------+---------------+
| AES-CBC   |     505.3     |   2097.8      |
| AES-XTS   |     1130      |   696.4       |
+-----------+-------------------------------+

The cryptsetup benchmark indicates Key Locker raw throughput can be  ~5x
slower than AES-NI. For disk encryption, storage bandwidth may be the
bottleneck before encryption bandwidth, but the potential performance
difference is why AES-KL is advertised as a distinct cipher in /proc/crypto
rather than the kernel transparently replacing AES-NI usage with AES-KL.

== Patch Series ==

The series touches two areas -- the x86 core and the x86 crypto library:

* PATCH01-09: Implement Key Locker support in the x86 core.  A new internal
  wrapping key is loaded at boot time and then it is restored from deep
  sleep states. The implication is that, e.g., a dm-crypt user needs to
  re-enter the private key at every power-on, per typical expectations, but
  it does not expect the user to re-enter the key over suspend events.
  The AES-KL code in the kernel's crypto library depends on this key
  support. Build up this support via helpers in the feature-dedicated .c
  file. Include documentation.

* PATCH10-13: For the x86 crypto library, it first prepares the AES-NI code
  to accommodate the new AES implementation. Then incrementally add base
  functions and CBC and XTS modes support. The code was found to pass the
  crypto test.

This series is based on 5.16-rc5.

Changes from v3 [7]:
* Drop ECB and CTR mode support (PATCH10/11). (Eric Biggers)
* Fix build warning and errors (PATCH8/11). (Eric Biggers)
* Trim the AES-KL objects by using symbols exported from the AES-NI (PATCH10).
  (Eric Biggers)
* Simplify the assembler dependency check (PATCH11). (Peter Zijlstra)
* Trim the Kconfig help text (PATCH11). (Dan Williams)

Thanks to Dan Williams, Charishma Gairuboyina, and Kumar Dwarakanath for
help with the cover letter.

== Reference ==

[1] Intel Advanced Encryption Standard Instructions (AES-NI):
    https://www.intel.com/content/www/us/en/developer/articles/technical/advanced-encryption-standard-instructions-aes-ni.html
[2] Intel Key Locker Specification:
    https://software.intel.com/content/dam/develop/external/us/en/documents/343965-intel-key-locker-specification.pdf
[3] This encoded form contains ciphertext of AES key, Additional
    Authentication Data, and integrity tag information. Section 1.4 Handle
    Format [2] describes the format.
[4] Key Locker cannot protect the user data in the event of a full system
    compromise, or against the scenarios where the attack can observe the
    creation of the key handle from the original key.
[5] cryptsetup: https://gitlab.com/cryptsetup/cryptsetup
[6] DM-crypt:
    https://www.kernel.org/doc/html/latest/admin-guide/device-mapper/dm-crypt.html
[7] V3: https://lore.kernel.org/lkml/20211124200700.15888-1-chang.seok.bae@intel.com/
[8] Intel publishes information about product performance at
    https://www.Intel.com/PerformanceIndex.
[9] Tigerlake:
    https://www.intel.com/content/www/us/en/products/docs/processors/embedded/11th-gen-product-brief.html

Chang S. Bae (13):
  Documentation/x86: Document Key Locker
  x86/cpufeature: Enumerate Key Locker feature
  x86/insn: Add Key Locker instructions to the opcode map
  x86/asm: Add a wrapper function for the LOADIWKEY instruction
  x86/msr-index: Add MSRs for Key Locker internal wrapping key
  x86/keylocker: Define Key Locker CPUID leaf
  x86/cpu/keylocker: Load an internal wrapping key at boot-time
  x86/power/keylocker: Restore internal wrapping key from the ACPI S3/4
    sleep states
  x86/cpu: Add a configuration and command line option for Key Locker
  crypto: x86/aes - Prepare for a new AES implementation
  crypto: x86/aes-kl - Support AES algorithm using Key Locker
    instructions
  crypto: x86/aes-kl - Support CBC mode
  crypto: x86/aes-kl - Support XTS mode

 .../admin-guide/kernel-parameters.txt         |    2 +
 Documentation/x86/index.rst                   |    1 +
 Documentation/x86/keylocker.rst               |   98 ++
 arch/x86/Kconfig                              |    3 +
 arch/x86/crypto/Makefile                      |    5 +-
 arch/x86/crypto/aes-intel_asm.S               |   26 +
 arch/x86/crypto/aes-intel_glue.c              |  153 +++
 arch/x86/crypto/aes-intel_glue.h              |   48 +
 arch/x86/crypto/aeskl-intel_asm.S             | 1014 +++++++++++++++++
 arch/x86/crypto/aeskl-intel_glue.c            |  296 +++++
 arch/x86/crypto/aesni-intel_asm.S             |   74 +-
 arch/x86/crypto/aesni-intel_glue.c            |  301 ++---
 arch/x86/crypto/aesni-intel_glue.h            |   22 +
 arch/x86/include/asm/cpufeatures.h            |    1 +
 arch/x86/include/asm/disabled-features.h      |    8 +-
 arch/x86/include/asm/keylocker.h              |   45 +
 arch/x86/include/asm/msr-index.h              |    6 +
 arch/x86/include/asm/special_insns.h          |   32 +
 arch/x86/include/uapi/asm/processor-flags.h   |    2 +
 arch/x86/kernel/Makefile                      |    1 +
 arch/x86/kernel/cpu/common.c                  |   21 +-
 arch/x86/kernel/cpu/cpuid-deps.c              |    1 +
 arch/x86/kernel/keylocker.c                   |  199 ++++
 arch/x86/kernel/smpboot.c                     |    2 +
 arch/x86/lib/x86-opcode-map.txt               |   11 +-
 arch/x86/power/cpu.c                          |    2 +
 crypto/Kconfig                                |   36 +
 tools/arch/x86/lib/x86-opcode-map.txt         |   11 +-
 28 files changed, 2157 insertions(+), 264 deletions(-)
 create mode 100644 Documentation/x86/keylocker.rst
 create mode 100644 arch/x86/crypto/aes-intel_asm.S
 create mode 100644 arch/x86/crypto/aes-intel_glue.c
 create mode 100644 arch/x86/crypto/aes-intel_glue.h
 create mode 100644 arch/x86/crypto/aeskl-intel_asm.S
 create mode 100644 arch/x86/crypto/aeskl-intel_glue.c
 create mode 100644 arch/x86/crypto/aesni-intel_glue.h
 create mode 100644 arch/x86/include/asm/keylocker.h
 create mode 100644 arch/x86/kernel/keylocker.c


base-commit: 2585cf9dfaaddf00b069673f27bb3f8530e2039c
--
2.17.1

Comments

Eric Biggers Dec. 16, 2021, 1:09 a.m. UTC | #1
On Mon, Dec 13, 2021 at 04:51:59PM -0800, Chang S. Bae wrote:
> == Disk Encryption Use Case ==
> 
> Disk encryption uses Key Locker to mitigate key exfiltration as follows:
> 
> 1. Configuration for Key Locker: AES-KL shows up in /proc/crypto as a
>    distinct cipher option. From there, tools like cryptsetup [5] can select
>    AES-KL vs AES-NI. For example,
> 
>    $ cryptsetup luksFormat --cipher="capi:xts-aes-aeskl-plain" <device>

plain64 is supposed to be used these days, not plain.

> Note: AES-KL has a performance tradeoff. See details in 'Performance'
> below.
> 
> 2. Disk encryption flow with key protection:
> 
> * The cryptsetup utility is responsible for loading the volume key into the
>   kernel's keyring and passing a reference of the key. Once dm-crypt [6]
>   has set up the volume, user space is responsible for invalidating the key
>   material so that only the key handle remains in memory. Cryptsetup does
>   this, e.g. via crypt_free_volume_key() and crypt_safe_free().
> 
> * The AES-KL code in the kernel's crypto library uses the key handle
>   instead of the actual clear text key.
> 
> == Non Use Cases ==
> 
> Bare metal disk encryption is the only use case intended by these patches.

Since dm-crypt is the use case for these patches, you probably should CC this
patchset to dm-devel@redhat.com so that the dm-crypt developers are aware of it.

> +-----------+---------------+---------------+
> | Cipher    |   Encryption  | Decryption    |
> | (AES-KL)  |    (MiB/s)    | (MiB/s)       |
> +-----------+---------------+---------------+
> | AES-CBC   |     505.3     |   2097.8      |
> | AES-XTS   |     1130      |   696.4       |
> +-----------+-------------------------------+

Why is AES-XTS decryption so much slower than AES-XTS encryption?  They should
be about the same.

Also, is the AES-CBC support really useful, given that for disk encryption,
AES-XTS is recommended over AES-CBC these days?

- Eric
Andy Lutomirski Dec. 24, 2021, 5:42 p.m. UTC | #2
On 12/13/21 16:52, Chang S. Bae wrote:
> Key Locker is a CPU feature to reduce key exfiltration opportunities while
> maintaining a programming interface similar to AES-NI. It converts the AES
> key into an encoded form, called the 'key handle'.
> 
> The key handle is a wrapped version of the clear-text key where the
> wrapping key has limited exposure. Once converted via setkey(), all
> subsequent data encryption using new AES instructions ('AES-KL') uses this
> key handle, reducing the exposure of private key material in memory.
> 
> AES-KL is analogous to that of AES-NI. Most assembly code is translated
> from the AES-NI code. They are operational in both 32-bit and 64-bit modes
> like AES-NI. However, users need to be aware of the following differences:
> 
> == Key Handle ==
> 
> AES-KL may fail with an invalid key handle. It could be corrupted or fail
> with handle restriction. A key handle may be encoded with some
> restrictions. The implementation restricts every handle only available
> in kernel mode via setkey().
> 

I find it a bit bizarre that this tries to be a drop-in replacement for 
normal AES.  Is this actually what we want, or do we want users to opt 
in to the KL implementation?

It seems like it might make more sense for tools like cryptsetup (or 
dm-crypt -- the actual layer is subject to some degree of debate) to 
explicitly create a key handle and then ask the kernel to use that key 
handle, not for the kernel to do this by magic.

What happens when someone applies your patches and runs dmsetup table 
--showkeys?

Why should the use of keylocker be part of the luksFormat operation? 
Surely a non-KL machine should still be able to decrypt a nominally 
KL-using volume in a pinch, for recovery purposes if nothing else.

--Andy
Chang S. Bae Jan. 5, 2022, 9:55 p.m. UTC | #3
On Dec 15, 2021, at 17:09, Eric Biggers <ebiggers@kernel.org> wrote:
> On Mon, Dec 13, 2021 at 04:51:59PM -0800, Chang S. Bae wrote:
>> == Disk Encryption Use Case ==
<snip>
>>   $ cryptsetup luksFormat --cipher="capi:xts-aes-aeskl-plain" <device>
> 
> plain64 is supposed to be used these days, not plain.

I see.

>> == Non Use Cases ==
>> 
>> Bare metal disk encryption is the only use case intended by these patches.
> 
> Since dm-crypt is the use case for these patches, you probably should CC this
> patchset to dm-devel@redhat.com so that the dm-crypt developers are aware of it.

Oh, I should have included them. I was not aware of this mailing address.

Hi DM-crypt folks,

Here is the patch series:
    https://lore.kernel.org/lkml/20211214005212.20588-1-chang.seok.bae@intel.com/t/

I would appreciate if you give any feedback on this feature’s use case with yours.

>> +-----------+---------------+---------------+
>> | Cipher    |   Encryption  | Decryption    |
>> | (AES-KL)  |    (MiB/s)    | (MiB/s)       |
>> +-----------+---------------+---------------+
>> | AES-CBC   |     505.3     |   2097.8      |
>> | AES-XTS   |     1130      |   696.4       |
>> +-----------+-------------------------------+
> 
> Why is AES-XTS decryption so much slower than AES-XTS encryption?  They should
> be about the same.

Analyzing and understanding this with specific hardware implementation takes
time for us. Will come back and update you when we have anything to share here.

> Also, is the AES-CBC support really useful, given that for disk encryption,
> AES-XTS is recommended over AES-CBC these days?

Yes, we understand that AES-XTS is the primary option for disk encryption.

But it seems that AES-CBC had been used for disk encryption, [1]:

    Comparing XTS to CBC for hard disk encryption
        If a storage device vendor is seeking FIPS 140-2 certification today,
        they will typically use CBC encryption, or even ECB. CBC is a good
        mode, ...

As long as it is factual that the mode was once popular, it can help somebody
who wants to use Key Locker for an old disk image I think.

Thanks,
Chang

[1] https://csrc.nist.gov/CSRC/media/Projects/Block-Cipher-Techniques/documents/BCM/Comments/XTS/XTS_comments-Ball.pdf
Chang S. Bae Jan. 7, 2022, 6:06 p.m. UTC | #4
On Dec 24, 2021, at 09:42, Andy Lutomirski <luto@kernel.org> wrote:
> 
> I find it a bit bizarre that this tries to be a drop-in replacement for normal
> AES.  Is this actually what we want, or do we want users to opt in to the KL
> implementation?
> 
> It seems like it might make more sense for tools like cryptsetup (or dm-crypt
> -- the actual layer is subject to some degree of debate) to explicitly create
> a key handle and then ask the kernel to use that key handle, not for the
> kernel to do this by magic.

Yeah, it is ideal to encode a key early but it was considered less significant
in terms of key protection versus doing it via setkey().

This opt-in looks to be the --cipher option specific and cryptsetup even
allows users to directly choose kernel’s crypto API via “capi:”.

I suspect in case a user wants to pick a specific crypto implementation via
“capi:”, one should have studied it.

But indeed, such question deserves more discussion here.

> What happens when someone applies your patches and runs dmsetup table
> --showkeys?

"dmsetup table --showkeys" [1] shows UUID that could be found when dumping the
master key by giving the passphrase, like [2]. The volume key in LUKS header
is under the user key.

> Why should the use of keylocker be part of the luksFormat operation? Surely a
> non-KL machine should still be able to decrypt a nominally KL-using volume in
> a pinch, for recovery purposes if nothing else.

I was trying to directly update cipher information in LUKS. But it was not
that smooth. But if it is possible in an acceptable way, decoding KL-volume
with a generic AES (or vice versa) should work. 

They both are AES data transformations so compatible with each other. This was
also tested by setting the default AES cipher in the LUKS then switching each
other by tweaking the priority.

On the bottom, the "reencrypt" option [3] allows updating a cipher in the LUKS
format. But maybe a better option like the above is possible.

Thanks,
Chang

[1]
$ sudo dmsetup table --showkeys /dev/mapper/volume
0 491520 crypt capi:xts-aes-aeskl-plain64 :32:logon:cryptsetup:8c12bfda-3570-406a-b4fe-5ecf1e91eecd-d0 0 7:17 32768

[2] 
$ sudo cryptsetup luksDump  --dump-master-key ./test

WARNING!
========
Header dump with volume key is sensitive information
which allows access to encrypted partition without passphrase.
This dump should be always stored encrypted on safe place.

Are you sure? (Type uppercase yes): YES
Enter passphrase for ./test:
LUKS header information for ./test
Cipher name:    capi:xts
Cipher mode:    aes-aeskl-plain64
Payload offset: 32768
UUID:           8c12bfda-3570-406a-b4fe-5ecf1e91eecd
MK bits:        256
MK dump:        3a 51 37 60 37 d2 58 9f 48 a7 ce 44 f7 ff de 34
               4d b9 6f eb f2 e7 d6 bc e0 c9 76 b6 3d b1 e6 24

[3] https://man7.org/linux/man-pages/man8/cryptsetup-reencrypt.8.html