On Wed Jan 3, 2024 at 5:31 PM EET, James Bottomley wrote: > On Wed, 2024-01-03 at 17:18 +0200, Jarkko Sakkinen wrote: > > On Tue Jan 2, 2024 at 7:04 PM EET, James Bottomley wrote: > > > Add session based HMAC authentication plus parameter decryption > > > and > > > response encryption using AES. The basic design is to segregate > > > all > > > the nasty crypto, hash and hmac code into tpm2-sessions.c and > > > export a > > > usable API. The API first of all starts off by gaining a session > > > with > > > tpm2_start_auth_session() which initiates a session with the TPM > > > and > > > allocates an opaque tpm2_auth structure to handle the > > > session > > > parameters. The design is that session use will be single > > > threaded > > > from start to finish under the ops lock, so the tpm2_auth structure > > > is > > > stored in struct tpm2_chip to simpify the externally visible API. > > > > > > The session can be ended with tpm2_end_auth_session() which is > > > designed only to be used in error legs. Ordinarily the further > > > session API (future patches) will end or continue the session > > > appropriately without having to call this. > > > > > > Signed-off-by: James Bottomley > > > <James.Bottomley@xxxxxxxxxxxxxxxxxxxxx> > > > Reviewed-by: Ard Biesheuvel <ard.biesheuvel@xxxxxxxxxx> # crypto > > > API parts > > > > > > --- > > > > > > v6: split into new patch, update config variable > > > --- > > > drivers/char/tpm/Kconfig | 3 + > > > drivers/char/tpm/tpm-buf.c | 1 + > > > drivers/char/tpm/tpm-chip.c | 3 + > > > drivers/char/tpm/tpm2-sessions.c | 383 > > > +++++++++++++++++++++++++++++++ > > > include/linux/tpm.h | 34 +++ > > > 5 files changed, 424 insertions(+) > > > > > > diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig > > > index e3c39a83171b..086cb8588493 100644 > > > --- a/drivers/char/tpm/Kconfig > > > +++ b/drivers/char/tpm/Kconfig > > > @@ -30,6 +30,9 @@ if TCG_TPM > > > config TCG_TPM2_HMAC > > > bool "Use encrypted and HMACd transactions on the TPM bus" > > > default y > > > + select CRYPTO_ECDH > > > + select CRYPTO_LIB_AESCFB > > > + select CRYPTO_LIB_SHA256 > > > help > > > Setting this causes us to deploy a scheme which uses > > > request > > > and response HMACs in addition to encryption for > > > diff --git a/drivers/char/tpm/tpm-buf.c b/drivers/char/tpm/tpm- > > > buf.c > > > index bb81180495d1..274130398569 100644 > > > --- a/drivers/char/tpm/tpm-buf.c > > > +++ b/drivers/char/tpm/tpm-buf.c > > > @@ -44,6 +44,7 @@ void tpm_buf_reset(struct tpm_buf *buf, u16 tag, > > > u32 ordinal) > > > head->tag = cpu_to_be16(tag); > > > head->length = cpu_to_be32(sizeof(*head)); > > > head->ordinal = cpu_to_be32(ordinal); > > > + buf->handles = 0; > > > } > > > EXPORT_SYMBOL_GPL(tpm_buf_reset); > > > > > > diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm- > > > chip.c > > > index 42b1062e33cd..d93937326b2e 100644 > > > --- a/drivers/char/tpm/tpm-chip.c > > > +++ b/drivers/char/tpm/tpm-chip.c > > > @@ -275,6 +275,9 @@ static void tpm_dev_release(struct device *dev) > > > kfree(chip->work_space.context_buf); > > > kfree(chip->work_space.session_buf); > > > kfree(chip->allocated_banks); > > > +#ifdef CONFIG_TCG_TPM2_HMAC > > > + kfree(chip->auth); > > > +#endif > > > kfree(chip); > > > } > > > > > > diff --git a/drivers/char/tpm/tpm2-sessions.c > > > b/drivers/char/tpm/tpm2-sessions.c > > > index ef66c28bb332..bca6fe3ebb10 100644 > > > --- a/drivers/char/tpm/tpm2-sessions.c > > > +++ b/drivers/char/tpm/tpm2-sessions.c > > > @@ -3,18 +3,397 @@ > > > /* > > > * Copyright (C) 2018 James.Bottomley@xxxxxxxxxxxxxxxxxxxxx > > > * > > > + * Cryptographic helper routines for handling TPM2 sessions for > > > + * authorization HMAC and request response encryption. > > > + * > > > + * The idea is to ensure that every TPM command is HMAC protected > > > by a > > > + * session, meaning in-flight tampering would be detected and in > > > + * addition all sensitive inputs and responses should be > > > encrypted. > > > + * > > > + * The basic way this works is to use a TPM feature called salted > > > + * sessions where a random secret used in session construction is > > > + * encrypted to the public part of a known TPM key. The problem > > > is we > > > + * have no known keys, so initially a primary Elliptic Curve key > > > is > > > + * derived from the NULL seed (we use EC because most TPMs > > > generate > > > + * these keys much faster than RSA ones). The curve used is > > > NIST_P256 > > > + * because that's now mandated to be present in 'TCG TPM v2.0 > > > + * Provisioning Guidance' > > > + * > > > + * Threat problems: the initial TPM2_CreatePrimary is not (and > > > cannot > > > + * be) session protected, so a clever Man in the Middle could > > > return a > > > + * public key they control to this command and from there > > > intercept > > > + * and decode all subsequent session based transactions. The > > > kernel > > > + * cannot mitigate this threat but, after boot, userspace can get > > > + * proof this has not happened by asking the TPM to certify the > > > NULL > > > + * key. This certification would chain back to the TPM > > > Endorsement > > > + * Certificate and prove the NULL seed primary had not been > > > tampered > > > + * with and thus all sessions must have been cryptographically > > > secure. > > > + * To assist with this, the initial NULL seed public key name is > > > made > > > + * available in a sysfs file. > > > + * > > > + * Use of these functions: > > > + * > > > + * The design is all the crypto, hash and hmac gunk is confined in > > > this > > > + * file and never needs to be seen even by the kernel internal > > > user. To > > > + * the user there's an init function tpm2_sessions_init() that > > > needs to > > > + * be called once per TPM which generates the NULL seed primary > > > key. > > > + * > > > + * These are the usage functions: > > > + * > > > + * tpm2_start_auth_session() which allocates the opaque auth > > > structure > > > + * and gets a session from the TPM. This must be called > > > before > > > + * any of the following functions. The session is protected > > > by a > > > + * session_key which is derived from a random salt value > > > + * encrypted to the NULL seed. > > > + * tpm2_end_auth_session() kills the session and frees the > > > resources. > > > + * Under normal operation this function is done by > > > + * tpm_buf_check_hmac_response(), so this is only to be used > > > on > > > + * error legs where the latter is not executed. > > > */ > > > > > > #include "tpm.h" > > > > > > +#include <linux/random.h> > > > +#include <linux/scatterlist.h> > > > + > > > #include <asm/unaligned.h> > > > > > > #include <crypto/aes.h> > > > +#include <crypto/kpp.h> > > > +#include <crypto/ecdh.h> > > > +#include <crypto/hash.h> > > > +#include <crypto/hmac.h> > > > > > > /* if you change to AES256, you only need change this */ > > > #define AES_KEYBYTES AES_KEYSIZE_128 > > > > > > #define AES_KEYBITS (AES_KEYBYTES*8) > > > +#define AUTH_MAX_NAMES 3 > > > + > > > +/* > > > + * This is the structure that carries all the auth information > > > (like > > > + * session handle, nonces, session key and auth) from use to use > > > it is > > > + * designed to be opaque to anything outside. > > > + */ > > > +struct tpm2_auth { > > > + u32 handle; > > > + /* > > > + * This has two meanings: before > > > tpm_buf_fill_hmac_session() > > > + * it marks the offset in the buffer of the start of the > > > + * sessions (i.e. after all the handles). Once the buffer > > > has > > > + * been filled it markes the session number of our auth > > > + * session so we can find it again in the response buffer. > > > + * > > > + * The two cases are distinguished because the first offset > > > + * must always be greater than TPM_HEADER_SIZE and the > > > second > > > + * must be less than or equal to 5. > > > + */ > > > + u32 session; > > > + /* > > > + * the size here is variable and set by the size of > > > our_nonce > > > + * which must be between 16 and the name hash length. we > > > set > > > + * the maximum sha256 size for the greatest protection > > > + */ > > > + u8 our_nonce[SHA256_DIGEST_SIZE]; > > > + u8 tpm_nonce[SHA256_DIGEST_SIZE]; > > > + /* > > > + * the salt is only used across the session > > > command/response > > > + * after that it can be used as a scratch area > > > + */ > > > + union { > > > + u8 salt[EC_PT_SZ]; > > > + /* scratch for key + IV */ > > > + u8 scratch[AES_KEYBYTES + AES_BLOCK_SIZE]; > > > + }; > > > + u8 session_key[SHA256_DIGEST_SIZE]; > > > +}; > > > > Could this contain also the fields added in the previous patch? > > > > Then obviously this data would be allocated together with chip > > but is there hard reason why this needs separate kzalloc and cannot > > be always allocated with chip blob? > > It's session specific (and highly sensitive data), so it needs to be > allocated and destroyed with each session. Our usage pattern under the > ops mutex means that every session is single threaded, so effectively > it has a 1:1 relationship with the chip, but part of the reason for all > of this is to remove visibility of the contents of this area from > anything other than the session code. Essentially it's stuff the chip > doesn't need to know because it's always constructed when the session > is created. > > I've also got a policy patch much later that requires two sessions, so > needs a push and pop mechanism which a static allocation in the chip > area won't work for. > > James Given the 1:1 relationship keeping the fields in tpm_chip has the benefit of not having to deal with allocation error. I guess having struct tpm2_auth (dunno, maybe tpm2_hmac_auth tho) does make sense because then it could be declared as static field and zeroed with memzero_explicit(). I don't see any point saving memory here at least... BR, Jarkko