Re: [RFC PATCH 0/2] TPM derived keys

Ignat Korchagin
2024-05-13T17:09:57Z

On Sat, May 4, 2024 at 5:35 PM Jarkko Sakkinen <jarkko@kernel.org> wrote:
>
> On Sat May 4, 2024 at 5:51 PM EEST, Jarkko Sakkinen wrote:
> > On Sat May 4, 2024 at 4:55 PM EEST, Ben Boeckel wrote:
> > > On Sat, May 04, 2024 at 03:21:11 +0300, Jarkko Sakkinen wrote:
> > > > I have no idea for what the key created with this is even used, which
> > > > makes this impossible to review.
> > >
> > > Additionally, there is nothing in Documentation/ for how userspace might
> > > use or create them. This includes things like their description format
> > > and describing available options.
> >
> > The whole user story is plain out broken. Documenting a feature that has
> > no provable use case won't fix that part.
> >
> > So it is better to start with the cover letter. With the *existing*
> > knowledge of the *real* issue I don't think we need this tbh.
>
> As for code I'd suggest the "Describe your changes" part from
>
> https://www.kernel.org/doc/html/latest/process/submitting-patches.html
>
> and most essentially how to split them properly.
>
> My best bet could something along the lines that perhaps there is some
> issue to be sorted out but I don't honestly believe that this will ever
> be a solution for any possible problem that exist in this planet.

Sorry, I must admit I wrote the description hastingly and too
high-level (it was pre-travelling, so probably not the right focus and
in a rush). Let me restart from scratch and describe particular
use-cases we're concerned about:

Trusted and encrypted keys are a great way to manage cryptographic
keys inside the kernel, while never exposing plaintext cryptographic
material to userspace: keys can only be read to userspace as encrypted
blobs and with trusted keys - these blobs are created with the TPM, so
only the TPM can unwrap the blobs.

One of the simplest way to create a trusted key is for an application
to request the kernel to generate a new one [1], like below with the
help of keyctl utility from keyutils:
$ keyctl add trusted kmk "new 32 keyhandle=0x81000001" @u

However, after the application generates a trusted key, it is the
responsibility of the application to manage/store it. For example, if
the application wants to reuse the key after a reboot, it needs to
read the key into userspace as an encrypted blob and store it on
persistent storage. This is challenging and sometimes not possible for
stateless/immutable/ephemeral systems, so such systems are effectively
locked out from using hardware-protected cryptographic keys.

Another point: while the fact that the application can't read the
plaintext cryptographic material into userspace is a feature of
trusted keys, it can also be a disadvantage. Since keys in plaintext
exist only in kernel context, they are useful mostly for in-kernel
systems, like dm-crypt, IMA, ecryptfs. Applications cannot easily use
trusted keys for cryptographic purposes for their own workloads: for
example, generating encrypted or MACed configuration files or
encrypting in-transit data. While since commit 7984ceb134bf ("crypto:
af_alg - Support symmetric encryption via keyring keys") it is
possible to use a trusted key via Linux Crypto API userspace interface
[2], it might not always be practical/desirable:
* due to limitations in the Linux Crypto API implementation it is
not possible to process more than ~64Kb of data using AEAD ciphers [3]
* needed algorithm implementations might not be enabled in the
kernel configuration file
* compliance constraints: the utilised cryptographic implementation
must be FIPS-validated
* performance constraints: passing large blobs of data to the kernel
for encryption is slow even with Crypto API's "zero-copy" interface
[3]

TPM derived keys attempt to address the above use cases by allowing
applications to deterministically derive unique cryptographic keys for
their own purposes directly from the TPM seed in the owner hierarchy.
The idea is that when an application requests a new key, instead of
generating a random key and wrapping it with the TPM, the
implementation generates a key via KDF(hierarchy seed, application
specific info). Therefore, the resulting keys will always be
cryptographically bound to the application itself and the device they
were generated on.

The applications then may either use in-kernel facilities, like [2],
to do crypto operations inside the kernel, so the generated
cryptographic material is never exposed to userspace (similar to
trusted/encrypted keys). Or, if they are subject to
performance/compliance/other constraints mentioned above, they can
read the key material to userspace and use a userspace crypto library.
Even with the latter approach they still get the benefit of using a
key, security of which is rooted in the TPM.

TPM derived keys also address the key storage problem for
stateless/immutable/ephemeral systems: since the derivation process is
deterministic, the same application can always re-create their keys on
the same system and doesn't need to store or back up any wrapped key
blobs. One notable use case (ironically not for a stateless system)
can be setting up proper full-disk encryption (dm-crypt plain mode
without a LUKS header), for example, to provide deniable encryption or
better resiliency to damage of encrypted media [4].

Current implementation provides two options for KDF's input for
application specific info to ensure key uniqueness:

1. A key, which is unique to a filesystem path:
$ keyctl add derived test '32 path'

Above will derive a 32 byte key based on the TPM seed and the
filesystem path of the requesting application. That is /usr/bin/keyctl
and /opt/bin/keyctl would generate different keys.

2. A key, which is cryptographically bound to the code of the
requesting application:
$ keyctl add derived test '32 csum'

Above will derive a 32 byte key based on the TPM seed and the IMA
measurement of the requesting application. That is /usr/bin/keyctl and
/opt/bin/keyctl would generate the same key if and only if their code
exactly matches bit for bit. The implementation does not measure the
requesting binary itself, but rather relies on already available
measurement. This means for this mode to work IMA needs to be enabled
and configured for requesting applications. For example:
# echo 'audit func=BPRM_CHECK' > \
/sys/kernel/security/integrity/ima/policy

Open questions:
* should any other modes/derivation parameters be considered as part
of application specific info?
* apparently in checksum mode, when calling keyring syscalls from
scripts, we mix in the measurement of the interpreter, not the script
itself. Is there any way to improve this?

I would like to mention that in Cloudflare we have found large
infrastructure key management based on derived keys from per-device
unique seeds quite convenient and almost infinitely scalable and I
believe TPM derived keys can be the next evolution bringing hardware
security to the table. I understand that folks here are not required
to follow links for additional information, but if someone is
interested in more details for our approach, which has been working
well for almost 9 years, see [5].

Hope it is better this time.

Ignat

[1]: https://www.kernel.org/doc/html/latest/security/keys/trusted-encrypted.html#examples-of-trusted-and-encrypted-key-usage
[2]: https://www.kernel.org/doc/html/latest/crypto/userspace-if.html
[3]: https://blog.cloudflare.com/the-linux-crypto-api-for-user-applications
[4]: https://wiki.archlinux.org/title/Dm-crypt/Encrypting_an_entire_system#Plain_dm-crypt
[5]: https://youtu.be/2RPcIbP2xsM?si=nKbyY0gss50i04CG

> BR, Jarkko