diff mbox series

[v8,13/22] tpm: Add TCG mandated Key Derivation Functions (KDFs)

Message ID 20240429202811.13643-14-James.Bottomley@HansenPartnership.com (mailing list archive)
State New
Headers show
Series add integrity and security to TPM2 transactions | expand

Commit Message

James Bottomley April 29, 2024, 8:28 p.m. UTC
The TCG mandates two Key derivation functions called KDFa and KDFe
used to derive keys from seeds and elliptic curve points respectively.
The definitions for these functions are found in the TPM 2.0 Library
Specification Part 1 - Architecture Guide

https://trustedcomputinggroup.org/resource/tpm-library-specification/

Implement a cut down version of each of these functions sufficient to
support the key derivation needs of HMAC sessions.

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>

---

v8: Add new patch containing KDFs
---
 drivers/char/tpm/Kconfig         |   1 +
 drivers/char/tpm/tpm2-sessions.c | 105 +++++++++++++++++++++++++++++++
 2 files changed, 106 insertions(+)

Comments

Jarkko Sakkinen April 29, 2024, 10:37 p.m. UTC | #1
On Mon Apr 29, 2024 at 11:28 PM EEST, James Bottomley wrote:
> The TCG mandates two Key derivation functions called KDFa and KDFe
> used to derive keys from seeds and elliptic curve points respectively.
> The definitions for these functions are found in the TPM 2.0 Library
> Specification Part 1 - Architecture Guide
>
> https://trustedcomputinggroup.org/resource/tpm-library-specification/
>
> Implement a cut down version of each of these functions sufficient to
> support the key derivation needs of HMAC sessions.
>
> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
>
> ---
>
> v8: Add new patch containing KDFs
> ---
>  drivers/char/tpm/Kconfig         |   1 +
>  drivers/char/tpm/tpm2-sessions.c | 105 +++++++++++++++++++++++++++++++
>  2 files changed, 106 insertions(+)
>
> diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
> index ab16d347579f..4873e6eae255 100644
> --- a/drivers/char/tpm/Kconfig
> +++ b/drivers/char/tpm/Kconfig
> @@ -30,6 +30,7 @@ if TCG_TPM
>  config TCG_TPM2_HMAC
>  	bool "Use HMAC and encrypted transactions on the TPM bus"
>  	default y
> +	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/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
> index fc3f032df467..8429e596f1eb 100644
> --- a/drivers/char/tpm/tpm2-sessions.c
> +++ b/drivers/char/tpm/tpm2-sessions.c
> @@ -7,6 +7,111 @@
>  
>  #include "tpm.h"
>  #include <asm/unaligned.h>
> +#include <crypto/hash.h>
> +#include <crypto/hmac.h>
> +
> +/*
> + * 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)
> +{
> +	u8 pad[SHA256_BLOCK_SIZE];
> +	int i;
> +
> +	sha256_init(sctx);
> +	for (i = 0; i < sizeof(pad); i++) {
> +		if (i < key_len)
> +			pad[i] = key[i];
> +		else
> +			pad[i] = 0;
> +		pad[i] ^= HMAC_IPAD_VALUE;
> +	}
> +	sha256_update(sctx, pad, sizeof(pad));
> +}
> +
> +static void tpm2_hmac_final(struct sha256_state *sctx, u8 *key, u32 key_len,
> +			    u8 *out)
> +{
> +	u8 pad[SHA256_BLOCK_SIZE];
> +	int i;
> +
> +	for (i = 0; i < sizeof(pad); i++) {
> +		if (i < key_len)
> +			pad[i] = key[i];
> +		else
> +			pad[i] = 0;
> +		pad[i] ^= HMAC_OPAD_VALUE;
> +	}
> +
> +	/* collect the final hash;  use out as temporary storage */
> +	sha256_final(sctx, out);
> +
> +	sha256_init(sctx);
> +	sha256_update(sctx, pad, sizeof(pad));
> +	sha256_update(sctx, out, SHA256_DIGEST_SIZE);
> +	sha256_final(sctx, 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)
> +{
> +	u32 counter = 1;
> +	const __be32 bits = cpu_to_be32(bytes * 8);
> +
> +	while (bytes > 0) {
> +		struct sha256_state sctx;
> +		__be32 c = cpu_to_be32(counter);
> +
> +		tpm2_hmac_init(&sctx, key, key_len);
> +		sha256_update(&sctx, (u8 *)&c, sizeof(c));
> +		sha256_update(&sctx, label, strlen(label)+1);
> +		sha256_update(&sctx, u, SHA256_DIGEST_SIZE);
> +		sha256_update(&sctx, v, SHA256_DIGEST_SIZE);
> +		sha256_update(&sctx, (u8 *)&bits, sizeof(bits));
> +		tpm2_hmac_final(&sctx, key, key_len, out);
> +
> +		bytes -= SHA256_DIGEST_SIZE;
> +		counter++;
> +		out += SHA256_DIGEST_SIZE;
> +	}
> +}
> +
> +/*
> + * 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)
> +{
> +	struct sha256_state sctx;
> +	/*
> +	 * this should be an iterative counter, but because we know
> +	 *  we're only taking 32 bytes for the point using a sha256
> +	 *  hash which is also 32 bytes, there's only one loop
> +	 */
> +	__be32 c = cpu_to_be32(1);
> +
> +	sha256_init(&sctx);
> +	/* counter (BE) */
> +	sha256_update(&sctx, (u8 *)&c, sizeof(c));
> +	/* secret value */
> +	sha256_update(&sctx, z, EC_PT_SZ);
> +	/* string including trailing zero */
> +	sha256_update(&sctx, str, strlen(str)+1);
> +	sha256_update(&sctx, pt_u, EC_PT_SZ);
> +	sha256_update(&sctx, pt_v, EC_PT_SZ);
> +	sha256_final(&sctx, out);
> +}
>  
>  /**
>   * tpm2_parse_create_primary() - parse the data returned from TPM_CC_CREATE_PRIMARY

Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>

BR, Jarkko
diff mbox series

Patch

diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
index ab16d347579f..4873e6eae255 100644
--- a/drivers/char/tpm/Kconfig
+++ b/drivers/char/tpm/Kconfig
@@ -30,6 +30,7 @@  if TCG_TPM
 config TCG_TPM2_HMAC
 	bool "Use HMAC and encrypted transactions on the TPM bus"
 	default y
+	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/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index fc3f032df467..8429e596f1eb 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -7,6 +7,111 @@ 
 
 #include "tpm.h"
 #include <asm/unaligned.h>
+#include <crypto/hash.h>
+#include <crypto/hmac.h>
+
+/*
+ * 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)
+{
+	u8 pad[SHA256_BLOCK_SIZE];
+	int i;
+
+	sha256_init(sctx);
+	for (i = 0; i < sizeof(pad); i++) {
+		if (i < key_len)
+			pad[i] = key[i];
+		else
+			pad[i] = 0;
+		pad[i] ^= HMAC_IPAD_VALUE;
+	}
+	sha256_update(sctx, pad, sizeof(pad));
+}
+
+static void tpm2_hmac_final(struct sha256_state *sctx, u8 *key, u32 key_len,
+			    u8 *out)
+{
+	u8 pad[SHA256_BLOCK_SIZE];
+	int i;
+
+	for (i = 0; i < sizeof(pad); i++) {
+		if (i < key_len)
+			pad[i] = key[i];
+		else
+			pad[i] = 0;
+		pad[i] ^= HMAC_OPAD_VALUE;
+	}
+
+	/* collect the final hash;  use out as temporary storage */
+	sha256_final(sctx, out);
+
+	sha256_init(sctx);
+	sha256_update(sctx, pad, sizeof(pad));
+	sha256_update(sctx, out, SHA256_DIGEST_SIZE);
+	sha256_final(sctx, 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)
+{
+	u32 counter = 1;
+	const __be32 bits = cpu_to_be32(bytes * 8);
+
+	while (bytes > 0) {
+		struct sha256_state sctx;
+		__be32 c = cpu_to_be32(counter);
+
+		tpm2_hmac_init(&sctx, key, key_len);
+		sha256_update(&sctx, (u8 *)&c, sizeof(c));
+		sha256_update(&sctx, label, strlen(label)+1);
+		sha256_update(&sctx, u, SHA256_DIGEST_SIZE);
+		sha256_update(&sctx, v, SHA256_DIGEST_SIZE);
+		sha256_update(&sctx, (u8 *)&bits, sizeof(bits));
+		tpm2_hmac_final(&sctx, key, key_len, out);
+
+		bytes -= SHA256_DIGEST_SIZE;
+		counter++;
+		out += SHA256_DIGEST_SIZE;
+	}
+}
+
+/*
+ * 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)
+{
+	struct sha256_state sctx;
+	/*
+	 * this should be an iterative counter, but because we know
+	 *  we're only taking 32 bytes for the point using a sha256
+	 *  hash which is also 32 bytes, there's only one loop
+	 */
+	__be32 c = cpu_to_be32(1);
+
+	sha256_init(&sctx);
+	/* counter (BE) */
+	sha256_update(&sctx, (u8 *)&c, sizeof(c));
+	/* secret value */
+	sha256_update(&sctx, z, EC_PT_SZ);
+	/* string including trailing zero */
+	sha256_update(&sctx, str, strlen(str)+1);
+	sha256_update(&sctx, pt_u, EC_PT_SZ);
+	sha256_update(&sctx, pt_v, EC_PT_SZ);
+	sha256_final(&sctx, out);
+}
 
 /**
  * tpm2_parse_create_primary() - parse the data returned from TPM_CC_CREATE_PRIMARY