linux/drivers/char/tpm/tpm2-sessions.c

// SPDX-License-Identifier: GPL-2.0

/*
 * Copyright (C) 2018 [email protected]
 *
 * 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.
 * tpm_buf_append_name() to add a handle to the buffer.  This must be
 *	used in place of the usual tpm_buf_append_u32() for adding
 *	handles because handles have to be processed specially when
 *	calculating the HMAC.  In particular, for NV, volatile and
 *	permanent objects you now need to provide the name.
 * tpm_buf_append_hmac_session() which appends the hmac session to the
 *	buf in the same way tpm_buf_append_auth does().
 * tpm_buf_fill_hmac_session() This calculates the correct hash and
 *	places it in the buffer.  It must be called after the complete
 *	command buffer is finalized so it can fill in the correct HMAC
 *	based on the parameters.
 * tpm_buf_check_hmac_response() which checks the session response in
 *	the buffer and calculates what it should be.  If there's a
 *	mismatch it will log a warning and return an error.  If
 *	tpm_buf_append_hmac_session() did not specify
 *	TPM_SA_CONTINUE_SESSION then the session will be closed (if it
 *	hasn't been consumed) and the auth structure freed.
 */

#include "tpm.h"
#include <linux/random.h>
#include <linux/scatterlist.h>
#include <linux/unaligned.h>
#include <crypto/kpp.h>
#include <crypto/ecdh.h>
#include <crypto/hash.h>
#include <crypto/hmac.h>

/* maximum number of names the TPM must remember for authorization */
#define AUTH_MAX_NAMES

#define AES_KEY_BYTES
#define AES_KEY_BITS

/*
 * 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 {};

#ifdef CONFIG_TCG_TPM2_HMAC
/*
 * Name Size based on TPM algorithm (assumes no hash bigger than 255)
 */
static u8 name_size(const u8 *name)
{}

static int tpm2_parse_read_public(char *name, struct tpm_buf *buf)
{}

static int tpm2_read_public(struct tpm_chip *chip, u32 handle, char *name)
{}
#endif /* CONFIG_TCG_TPM2_HMAC */

/**
 * tpm_buf_append_name() - add a handle area to the buffer
 * @chip: the TPM chip structure
 * @buf: The buffer to be appended
 * @handle: The handle to be appended
 * @name: The name of the handle (may be NULL)
 *
 * In order to compute session HMACs, we need to know the names of the
 * objects pointed to by the handles.  For most objects, this is simply
 * the actual 4 byte handle or an empty buf (in these cases @name
 * should be NULL) but for volatile objects, permanent objects and NV
 * areas, the name is defined as the hash (according to the name
 * algorithm which should be set to sha256) of the public area to
 * which the two byte algorithm id has been appended.  For these
 * objects, the @name pointer should point to this.  If a name is
 * required but @name is NULL, then TPM2_ReadPublic() will be called
 * on the handle to obtain the name.
 *
 * As with most tpm_buf operations, success is assumed because failure
 * will be caused by an incorrect programming model and indicated by a
 * kernel message.
 */
void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
			 u32 handle, u8 *name)
{}
EXPORT_SYMBOL_GPL();

/**
 * tpm_buf_append_hmac_session() - Append a TPM session element
 * @chip: the TPM chip structure
 * @buf: The buffer to be appended
 * @attributes: The session attributes
 * @passphrase: The session authority (NULL if none)
 * @passphrase_len: The length of the session authority (0 if none)
 *
 * This fills in a session structure in the TPM command buffer, except
 * for the HMAC which cannot be computed until the command buffer is
 * complete.  The type of session is controlled by the @attributes,
 * the main ones of which are TPM2_SA_CONTINUE_SESSION which means the
 * session won't terminate after tpm_buf_check_hmac_response(),
 * TPM2_SA_DECRYPT which means this buffers first parameter should be
 * encrypted with a session key and TPM2_SA_ENCRYPT, which means the
 * response buffer's first parameter needs to be decrypted (confusing,
 * but the defines are written from the point of view of the TPM).
 *
 * Any session appended by this command must be finalized by calling
 * tpm_buf_fill_hmac_session() otherwise the HMAC will be incorrect
 * and the TPM will reject the command.
 *
 * As with most tpm_buf operations, success is assumed because failure
 * will be caused by an incorrect programming model and indicated by a
 * kernel message.
 */
void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
				 u8 attributes, u8 *passphrase,
				 int passphrase_len)
{}
EXPORT_SYMBOL_GPL();

#ifdef CONFIG_TCG_TPM2_HMAC

static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy,
			       u32 *handle, u8 *name);

/*
 * It turns out the crypto hmac(sha256) is hard for us to consume
 * because it assumes a fixed key and the TPM seems to change the key
 * on every operation, so we weld the hmac init and final functions in
 * here to give it the same usage characteristics as a regular hash
 */
static void tpm2_hmac_init(struct sha256_state *sctx, u8 *key, u32 key_len)
{}

static void tpm2_hmac_final(struct sha256_state *sctx, u8 *key, u32 key_len,
			    u8 *out)
{}

/*
 * assume hash sha256 and nonces u, v of size SHA256_DIGEST_SIZE but
 * otherwise standard tpm2_KDFa.  Note output is in bytes not bits.
 */
static void tpm2_KDFa(u8 *key, u32 key_len, const char *label, u8 *u,
		      u8 *v, u32 bytes, u8 *out)
{}

/*
 * Somewhat of a bastardization of the real KDFe.  We're assuming
 * we're working with known point sizes for the input parameters and
 * the hash algorithm is fixed at sha256.  Because we know that the
 * point size is 32 bytes like the hash size, there's no need to loop
 * in this KDF.
 */
static void tpm2_KDFe(u8 z[EC_PT_SZ], const char *str, u8 *pt_u, u8 *pt_v,
		      u8 *out)
{}

static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip,
				struct tpm2_auth *auth)
{}

/**
 * tpm_buf_fill_hmac_session() - finalize the session HMAC
 * @chip: the TPM chip structure
 * @buf: The buffer to be appended
 *
 * This command must not be called until all of the parameters have
 * been appended to @buf otherwise the computed HMAC will be
 * incorrect.
 *
 * This function computes and fills in the session HMAC using the
 * session key and, if TPM2_SA_DECRYPT was specified, computes the
 * encryption key and encrypts the first parameter of the command
 * buffer with it.
 *
 * As with most tpm_buf operations, success is assumed because failure
 * will be caused by an incorrect programming model and indicated by a
 * kernel message.
 */
void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
{}
EXPORT_SYMBOL();

/**
 * tpm_buf_check_hmac_response() - check the TPM return HMAC for correctness
 * @chip: the TPM chip structure
 * @buf: the original command buffer (which now contains the response)
 * @rc: the return code from tpm_transmit_cmd
 *
 * If @rc is non zero, @buf may not contain an actual return, so @rc
 * is passed through as the return and the session cleaned up and
 * de-allocated if required (this is required if
 * TPM2_SA_CONTINUE_SESSION was not specified as a session flag).
 *
 * If @rc is zero, the response HMAC is computed against the returned
 * @buf and matched to the TPM one in the session area.  If there is a
 * mismatch, an error is logged and -EINVAL returned.
 *
 * The reason for this is that the command issue and HMAC check
 * sequence should look like:
 *
 *	rc = tpm_transmit_cmd(...);
 *	rc = tpm_buf_check_hmac_response(&buf, auth, rc);
 *	if (rc)
 *		...
 *
 * Which is easily layered into the current contrl flow.
 *
 * Returns: 0 on success or an error.
 */
int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf,
				int rc)
{}
EXPORT_SYMBOL();

/**
 * tpm2_end_auth_session() - kill the allocated auth session
 * @chip: the TPM chip structure
 *
 * ends the session started by tpm2_start_auth_session and frees all
 * the resources.  Under normal conditions,
 * tpm_buf_check_hmac_response() will correctly end the session if
 * required, so this function is only for use in error legs that will
 * bypass the normal invocation of tpm_buf_check_hmac_response().
 */
void tpm2_end_auth_session(struct tpm_chip *chip)
{}
EXPORT_SYMBOL();

static int tpm2_parse_start_auth_session(struct tpm2_auth *auth,
					 struct tpm_buf *buf)
{}

static int tpm2_load_null(struct tpm_chip *chip, u32 *null_key)
{}

/**
 * tpm2_start_auth_session() - create a HMAC authentication session with the TPM
 * @chip: the TPM chip structure to create the session with
 *
 * This function loads the NULL seed from its saved context and starts
 * an authentication session on the null seed, fills in the
 * @chip->auth structure to contain all the session details necessary
 * for performing the HMAC, encrypt and decrypt operations and
 * returns.  The NULL seed is flushed before this function returns.
 *
 * Return: zero on success or actual error encountered.
 */
int tpm2_start_auth_session(struct tpm_chip *chip)
{}
EXPORT_SYMBOL();

/*
 * A mask containing the object attributes for the kernel held null primary key
 * used in HMAC encryption. For more information on specific attributes look up
 * to "8.3 TPMA_OBJECT (Object Attributes)".
 */
#define TPM2_OA_NULL_KEY

/**
 * tpm2_parse_create_primary() - parse the data returned from TPM_CC_CREATE_PRIMARY
 *
 * @chip:	The TPM the primary was created under
 * @buf:	The response buffer from the chip
 * @handle:	pointer to be filled in with the return handle of the primary
 * @hierarchy:	The hierarchy the primary was created for
 * @name:	pointer to be filled in with the primary key name
 *
 * Return:
 * * 0		- OK
 * * -errno	- A system error
 * * TPM_RC	- A TPM error
 */
static int tpm2_parse_create_primary(struct tpm_chip *chip, struct tpm_buf *buf,
				     u32 *handle, u32 hierarchy, u8 *name)
{}

/**
 * tpm2_create_primary() - create a primary key using a fixed P-256 template
 *
 * @chip:      the TPM chip to create under
 * @hierarchy: The hierarchy handle to create under
 * @handle:    The returned volatile handle on success
 * @name:      The name of the returned key
 *
 * For platforms that might not have a persistent primary, this can be
 * used to create one quickly on the fly (it uses Elliptic Curve not
 * RSA, so even slow TPMs can create one fast).  The template uses the
 * TCG mandated H one for non-endorsement ECC primaries, i.e. P-256
 * elliptic curve (the only current one all TPM2s are required to
 * have) a sha256 name hash and no policy.
 *
 * Return:
 * * 0		- OK
 * * -errno	- A system error
 * * TPM_RC	- A TPM error
 */
static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy,
			       u32 *handle, u8 *name)
{}

static int tpm2_create_null_primary(struct tpm_chip *chip)
{}

/**
 * tpm2_sessions_init() - start of day initialization for the sessions code
 * @chip: TPM chip
 *
 * Derive and context save the null primary and allocate memory in the
 * struct tpm_chip for the authorizations.
 *
 * Return:
 * * 0		- OK
 * * -errno	- A system error
 * * TPM_RC	- A TPM error
 */
int tpm2_sessions_init(struct tpm_chip *chip)
{}
EXPORT_SYMBOL();
#endif /* CONFIG_TCG_TPM2_HMAC */