diff mbox series

[2/2] Add TPM2 Unseal command and supporting functions

Message ID 1603813495996-0.post@n7.nabble.com
State Rejected
Delegated to: Wolfgang Denk
Headers show
Series [1/2] Add HMAC-SHA-256 | expand

Commit Message

GlovePuppet Oct. 27, 2020, 3:44 p.m. UTC
Unseals a loaded object, identified by handle, and returns
data at a memory location or in an environment variable

Caveats

-The PolicyPCR command only supports one PCR
-The auth request code only supports one handle

Signed-off-by: GlovePuppet <touched.by.his.noodley.appendage@gmail.com>
---

 cmd/tpm-v2.c        |  60 +++++++
 drivers/tpm/Kconfig |   7 +
 include/tpm-v2.h    |  41 +++++
 lib/tpm-v2.c        | 413 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 521 insertions(+)

+
+	rc = tpm2_unseal(dev, nonce, &auth_session, &object, unsealed_data,
unsealed_data_sz);
+	if (rc)
+		return rc;
+
+	rc = tpm2_flushcontext(dev, &auth_session);
+	if (rc)
+		return rc;
+
+	return 0;
+}
+
+#endif /* CONFIG_TPM2_ENHANCEDAUTH_SESSIONS */

Comments

Wolfgang Denk Oct. 28, 2020, 7:29 a.m. UTC | #1
Dear GlovePuppet,

In message <1603813495996-0.post@n7.nabble.com> you wrote:
> Unseals a loaded object, identified by handle, and returns
> data at a memory location or in an environment variable
>
> Caveats
>
> -The PolicyPCR command only supports one PCR
> -The auth request code only supports one handle
>
> Signed-off-by: GlovePuppet <touched.by.his.noodley.appendage@gmail.com>


NAK.

The Submitting Patches rules require using your real name (sorry, no
pseudonyms or anonymous contributions.)

See [1] for reference.

[1] https://github.com/torvalds/linux/blob/master/Documentation/process/submitting-patches.rst

Best regards,

Wolfgang Denk
diff mbox series

Patch

diff --git a/cmd/tpm-v2.c b/cmd/tpm-v2.c
index e6742656f5..a94fcfabf0 100644
--- a/cmd/tpm-v2.c
+++ b/cmd/tpm-v2.c
@@ -354,6 +354,56 @@  static int do_tpm_pcr_setauthvalue(struct cmd_tbl
*cmdtp, int flag,
 							key, key_sz));
 }
 
+#if IS_ENABLED(CONFIG_TPM2_ENHANCEDAUTH_SESSIONS)
+
+static int do_tpm_unseal(struct cmd_tbl *cmdtp, int flag, int argc,
+					char *const argv[])
+{
+	struct udevice *dev;
+	u32 rc;
+	u32 handle;
+	u32 index;
+	struct tpm_chip_priv *priv;
+	u8 *data;
+	u8 buffer[129];
+	u16 data_sz;
+
+	if (argc != 4)
+		return CMD_RET_USAGE;
+
+	if (get_tpm(&dev))
+		return CMD_RET_FAILURE;
+
+	priv = dev_get_uclass_priv(dev);
+	if (!priv)
+		return CMD_RET_FAILURE;
+
+	handle = simple_strtoul(argv[1], NULL, 16);
+
+	index = simple_strtoul(argv[2], NULL, 0);
+	if (index >= priv->pcr_count)
+		return CMD_RET_USAGE;
+
+	if (*argv[3] == '*')	{
+		/* Skip the '*' */
+		data = map_sysmem(simple_strtoul(argv[3] + 1, NULL, 16), 0);
+	} else {
+		data = buffer;
+	}
+
+	rc = tpm2_do_unseal(dev, handle, index, priv->pcr_select_min, data,
&data_sz);
+
+	if (*argv[3] == '*') {
+		unmap_sysmem(data);
+	} else {
+		buffer[data_sz] = '\0';
+		env_set(argv[3], (char *)buffer);
+	}
+
+	return report_return_code(rc);
+}
+#endif /* CONFIG_TPM2_ENHANCEDAUTH_SESSIONS */
+
 static struct cmd_tbl tpm2_commands[] = {
 	U_BOOT_CMD_MKENT(device, 0, 1, do_tpm_device, "", ""),
 	U_BOOT_CMD_MKENT(info, 0, 1, do_tpm_info, "", ""),
@@ -371,6 +421,9 @@  static struct cmd_tbl tpm2_commands[] = {
 			 do_tpm_pcr_setauthpolicy, "", ""),
 	U_BOOT_CMD_MKENT(pcr_setauthvalue, 0, 1,
 			 do_tpm_pcr_setauthvalue, "", ""),
+#if IS_ENABLED(CONFIG_TPM2_ENHANCEDAUTH_SESSIONS)
+	U_BOOT_CMD_MKENT(unseal, 0, 1, do_tpm_unseal, "", ""),
+#endif /* CONFIG_TPM2_ENHANCEDAUTH_SESSIONS */
 };
 
 struct cmd_tbl *get_tpm2_commands(unsigned int *size)
@@ -442,4 +495,11 @@  U_BOOT_CMD(tpm2, CONFIG_SYS_MAXARGS, 1, do_tpm, "Issue
a TPMv2.x command",
 "    <pcr>: index of the PCR\n"
 "    <key>: secret to protect the access of PCR #<pcr>\n"
 "    <password>: optional password of the PLATFORM hierarchy\n"
+#if IS_ENABLED(CONFIG_TPM2_ENHANCEDAUTH_SESSIONS)
+"unseal <handle> <pcr> <data_addr>\n"
+"    Create a pcr policy and unseal data from the TPM, [save to env var /
*address]\n"
+"    <handle>: the handle of a loaded object\n"
+"    <pcr>: index of the pcr\n"
+"    <data_addr> env var or *address to receive the unsealed data"
+#endif /* CONFIG_TPM2_ENHANCEDAUTH_SESSIONS */
 );
diff --git a/drivers/tpm/Kconfig b/drivers/tpm/Kconfig
index 9eebab5cfd..0bdc76f181 100644
--- a/drivers/tpm/Kconfig
+++ b/drivers/tpm/Kconfig
@@ -161,6 +161,13 @@  config TPM2_FTPM_TEE
 	help
 	  This driver supports firmware TPM running in TEE.
 
+config TPM2_ENHANCEDAUTH_SESSIONS
+	bool "Enable TPM enhanced authentication session support"
+	depends on TPM_V2
+	help
+	  Enable support for enhanced authorisation commands and the TPM Unseal
+	  command.
+
 endif # TPM_V2
 
 endmenu
diff --git a/include/tpm-v2.h b/include/tpm-v2.h
index f6c045d354..ca9e11eb13 100644
--- a/include/tpm-v2.h
+++ b/include/tpm-v2.h
@@ -52,6 +52,7 @@  enum tpm2_startup_types {
  */
 enum tpm2_handles {
 	TPM2_RH_OWNER		= 0x40000001,
+	TPM2_RH_NULL        = 0x40000007,
 	TPM2_RS_PW		= 0x40000009,
 	TPM2_RH_LOCKOUT		= 0x4000000A,
 	TPM2_RH_ENDORSEMENT	= 0x4000000B,
@@ -85,9 +86,14 @@  enum tpm2_command_codes {
 	TPM2_CC_DAM_RESET	= 0x0139,
 	TPM2_CC_DAM_PARAMETERS	= 0x013A,
 	TPM2_CC_NV_READ         = 0x014E,
+	TPM2_CC_UNSEAL         = 0x015E,
+	TPM2_CC_FLUSH_CONTEXT	= 0x0165,
+	TPM2_CC_READ_PUBLIC		= 0x0173,
+	TPM2_CC_START_AUTH_SESSION = 0x0176,
 	TPM2_CC_GET_CAPABILITY	= 0x017A,
 	TPM2_CC_GET_RANDOM      = 0x017B,
 	TPM2_CC_PCR_READ	= 0x017E,
+	TPM2_CC_POLICY_PCR  = 0x017F,
 	TPM2_CC_PCR_EXTEND	= 0x0182,
 	TPM2_CC_PCR_SETAUTHVAL	= 0x0183,
 };
@@ -194,6 +200,23 @@  enum {
 	TPM_MAX_BUF_SIZE	= 1260,
 };
 
+#if IS_ENABLED(CONFIG_TPM2_ENHANCEDAUTH_SESSIONS)
+
+enum {
+	TPM_SE_HMAC		= 0,
+	TPM_SE_POLICY	= 1,
+	TPM_SE_TRIAL    = 3
+};
+
+enum {
+	TPM2_REQUEST_HEADER_LENGTH	= 10,
+	TPM2_RESPONSE_HEADER_LENGTH	= 10,
+	TPM2_REQUEST_AUTH_LENGTH	= 45,
+	TPM2_RESPONSE_AUTH_LENGTH	= 69
+};
+
+#endif /* CONFIG_TPM2_ENHANCEDAUTH_SESSIONS */
+
 /**
  * Issue a TPM2_Startup command.
  *
@@ -352,4 +375,22 @@  u32 tpm2_pcr_setauthvalue(struct udevice *dev, const
char *pw,
  */
 u32 tpm2_get_random(struct udevice *dev, void *data, u32 count);
 
+#ifdef CONFIG_TPM2_ENHANCEDAUTH_SESSIONS
+/**
+ * Perform a TPM_Unseal command sequence.
+ *
+ * @dev				 TPM device
+ * @handle			 Handle of a loaded object to be unsealed
+ * @pcr_index		 Index of the PCR
+ * @idx_min_sz		 Minimum size in bytes of the pcrSelect array
+ * @unsealed_data	 Buffer to receive the unsealed data
+ * @unsealed_data_sz Length of the unsealed data
+ *
+ * @return code of the operation
+ */
+u32 tpm2_do_unseal(struct udevice *dev, u32 handle,
+					u32 pcr_index, unsigned int idx_min_sz,
+					u8 *unsealed_data, u16 *unsealed_data_sz);
+#endif /* CONFIG_TPM2_ENHANCEDAUTH_SESSIONS */
+
 #endif /* __TPM_V2_H */
diff --git a/lib/tpm-v2.c b/lib/tpm-v2.c
index a4c352e3ef..d4b749d7aa 100644
--- a/lib/tpm-v2.c
+++ b/lib/tpm-v2.c
@@ -9,8 +9,30 @@ 
 #include <tpm-common.h>
 #include <tpm-v2.h>
 #include <linux/bitops.h>
+#include <u-boot/sha256.h>
 #include "tpm-utils.h"
 
+#if IS_ENABLED(CONFIG_TPM2_ENHANCEDAUTH_SESSIONS)
+
+#ifndef CONFIG_SHA256
+#error "TPM2_ENHANCEDAUTH_SESSIONS require SHA256 to be configured, too"
+#endif /* !CONFIG_SHA256 */
+
+struct session_data {
+	int valid;
+	u32 handle;
+	u8	newernonce[TPM2_DIGEST_LEN];
+	u8	oldernonce[TPM2_DIGEST_LEN];
+};
+
+struct tpm2_object {
+	u32 handle;
+	u16 hashalg;
+	u8 name[TPM2_DIGEST_LEN];
+};
+
+#endif /* CONFIG_TPM2_ENHANCEDAUTH_SESSIONS */
+
 u32 tpm2_startup(struct udevice *dev, enum tpm2_startup_types mode)
 {
 	const u8 command_v2[12] = {
@@ -466,3 +488,394 @@  u32 tpm2_get_random(struct udevice *dev, void *data,
u32 count)
 
 	return 0;
 }
+
+#if IS_ENABLED(CONFIG_TPM2_ENHANCEDAUTH_SESSIONS)
+
+static u32 tpm2_start_authsession(struct udevice *dev, u8 session_type,
const u8 *nonce,
+				  struct session_data *auth_session)
+{
+	u32 rc;
+	u16 tpm_nonce_sz;
+	size_t response_len = COMMAND_BUFFER_SIZE;
+	u8 response[COMMAND_BUFFER_SIZE];
+
+	u8 command_v2[COMMAND_BUFFER_SIZE] = {
+		tpm_u16(TPM2_ST_NO_SESSIONS),			/* TAG */
+		tpm_u32(0x1B + TPM2_DIGEST_LEN),		/* Length - fixed length nonce*/
+		tpm_u32(TPM2_CC_START_AUTH_SESSION),	/* Command code */
+
+		/* TPMI_DH_OBJECT+ */
+		tpm_u32(TPM2_RH_NULL),					/* tmpKey */
+		/* TPMI_DH_ENTITY+  */
+		tpm_u32(TPM2_RH_NULL),					/* bind */
+	};
+
+	size_t offset = 18;
+
+	if (!auth_session || auth_session->valid)
+		return TPM_LIB_ERROR;
+
+	if (pack_byte_string(command_v2, sizeof(command_v2), "wswbww",
+			     offset, TPM2_DIGEST_LEN,
+			       offset + 2, nonce, TPM2_DIGEST_LEN,
+			       offset + TPM2_DIGEST_LEN + 2, 0,
+				   offset + TPM2_DIGEST_LEN + 4, session_type,
+				   offset + TPM2_DIGEST_LEN + 5, TPM2_ALG_NULL,
+				   offset + TPM2_DIGEST_LEN + 7, TPM2_ALG_SHA256))
+		return TPM_LIB_ERROR;
+
+	rc = tpm_sendrecv_command(dev, command_v2, response, &response_len);
+	if (rc)
+		return rc;
+
+	offset = 10;
+
+	if (unpack_byte_string(response, response_len, "dw",
+			       offset, &auth_session->handle,
+			       offset + 4, &tpm_nonce_sz))
+		return TPM_LIB_ERROR;
+
+	if (unpack_byte_string(response, response_len, "s",
+			       offset + 6, auth_session->newernonce, TPM2_DIGEST_LEN))
+		return TPM_LIB_ERROR;
+
+	memcpy(auth_session->oldernonce, nonce, TPM2_DIGEST_LEN);
+	auth_session->valid = 1;
+	return rc;
+}
+
+static u32 tpm2_policypcr(struct udevice *dev, const u8 *pcr_digest, u32
idx,
+			  unsigned int idx_min_sz, u32 policy)
+{
+	u8 idx_array_sz = max(idx_min_sz, DIV_ROUND_UP(idx, 8));
+	u8 command_v2[COMMAND_BUFFER_SIZE] = {
+		tpm_u16(TPM2_ST_NO_SESSIONS),	/* TAG */
+		tpm_u32(0x1A + TPM2_DIGEST_LEN),/* Length */
+		tpm_u32(TPM2_CC_POLICY_PCR),	/* Command code */
+
+		/* TPMI_SH_POLICY */
+		tpm_u32(policy),
+
+		/* TPM2B_DIGEST */
+		tpm_u16(TPM2_DIGEST_LEN),
+	};
+
+	size_t response_len = COMMAND_BUFFER_SIZE;
+	u8 response[COMMAND_BUFFER_SIZE];
+	unsigned int pcr_sel_idx = idx / 8;
+	u8 pcr_sel_bit = BIT(idx % 8);
+
+	size_t offset = 16;
+
+	if (pack_byte_string(command_v2, COMMAND_BUFFER_SIZE, "sdwbb",
+			     offset, pcr_digest, TPM2_DIGEST_LEN,
+				 offset + TPM2_DIGEST_LEN,	1,
+				 offset + TPM2_DIGEST_LEN + 4, TPM2_ALG_SHA256,
+			     offset + TPM2_DIGEST_LEN + 4 + 2, idx_array_sz,
+			     offset + TPM2_DIGEST_LEN + 4 + 2 + 1 + pcr_sel_idx, pcr_sel_bit))
+		return TPM_LIB_ERROR;
+
+	return tpm_sendrecv_command(dev, command_v2, response, &response_len);
+}
+
+static u32 tpm2_readpublic(struct udevice *dev, u32 handle, struct
tpm2_object *object)
+{
+	size_t response_len = COMMAND_BUFFER_SIZE;
+	u32 ret;
+	u16 public_sz;
+	u16 name_sz;
+	u8 response[COMMAND_BUFFER_SIZE];
+
+	u8 command_v2[COMMAND_BUFFER_SIZE] = {
+		tpm_u16(TPM2_ST_NO_SESSIONS),			/* TAG */
+		tpm_u32(0x0E),/* Length */
+		tpm_u32(TPM2_CC_READ_PUBLIC),			/* Command code */
+		/* TPMI_SH_POLICY */
+		tpm_u32(handle)
+	};
+
+	ret = tpm_sendrecv_command(dev, command_v2, response, &response_len);
+	if (ret)
+		return ret;
+
+	size_t offset = 10;
+
+	/* skip over TPM2B_PUBLIC */
+	if (unpack_byte_string(response, response_len, "w",
+			       offset, &public_sz))
+		return TPM_LIB_ERROR;
+
+	if (unpack_byte_string(response, response_len, "ww",
+			       offset + 2 + public_sz, &name_sz,
+							offset + 2 + public_sz + 2, &object->hashalg))
+		return TPM_LIB_ERROR;
+
+	if (unpack_byte_string(response, response_len, "s",
+			       offset + 2 + public_sz + 2 + 2, object->name, TPM2_DIGEST_LEN))
+		return TPM_LIB_ERROR;
+
+	object->handle = handle;
+	return 0;
+}
+
+static u32 create_request_auth(const void *request,
+			       size_t request_len0,
+								struct tpm2_object *object,
+								struct session_data *auth_session,
+								void *request_auth,
+								const void *auth)
+{
+	size_t offset;
+	u8 names[TPM2_DIGEST_LEN + 2];
+
+	u8 hmac_data[TPM2_DIGEST_LEN * 3 + 1];
+	sha256_context ctx;
+
+	const size_t command_code_offset = 6;
+	const size_t auth_auth_offset = 45;
+	const size_t request_header_length = 10;
+
+	if (!auth_session || !auth_session->valid)
+		return TPM_LIB_ERROR;
+
+	offset = 0;
+	if (pack_byte_string(names, TPM2_DIGEST_LEN + 2, "ws",
+			     offset, object->hashalg,
+		offset + 2, object->name, TPM2_DIGEST_LEN))
+		return TPM_LIB_ERROR;
+
+	sha256_starts(&ctx);
+	sha256_update(&ctx, request + command_code_offset, 4);
+	sha256_update(&ctx, names, TPM2_DIGEST_LEN + 2);
+	if (request_len0 > request_header_length + 4)
+		sha256_update(&ctx, request + request_header_length + 4,
+			      request_len0 - request_header_length - 4);
+	sha256_finish(&ctx, hmac_data);
+
+	/* Populate the authorization session */
+	if (pack_byte_string(request_auth, TPM2_REQUEST_AUTH_LENGTH, "ddwsbw",
+			     offset, 0x49,
+							/* authHandle */
+							offset + 4, auth_session->handle,
+							/* nonceCallerSize */
+							offset + 8, TPM2_DIGEST_LEN,
+							/* nonceCaller */
+							offset + 8 + 2,	auth_session->newernonce, TPM2_DIGEST_LEN,
+							/* sessionAttributes (continueSession=1) */
+							offset + 8 + 2 + TPM2_DIGEST_LEN, 1,
+							/* hmacSize */
+							offset + 8 + 2 + TPM2_DIGEST_LEN + 1, TPM2_DIGEST_LEN))
+		return TPM_LIB_ERROR;
+
+	offset = TPM2_DIGEST_LEN;
+
+	if (pack_byte_string(hmac_data, sizeof(hmac_data), "ssb",
+			     offset, auth_session->newernonce, TPM2_DIGEST_LEN,
+			     offset + TPM2_DIGEST_LEN, auth_session->oldernonce,
TPM2_DIGEST_LEN,
+				 offset + (2 * TPM2_DIGEST_LEN), 1))	/* continue */
+		return TPM_LIB_ERROR;
+
+	sha256_hmac(auth,
+		    TPM2_DIGEST_LEN,
+			hmac_data,
+			TPM2_DIGEST_LEN * 3 + 1,
+			request_auth + auth_auth_offset);
+
+	return 0;
+}
+
+static u32 verify_response_auth(u32 command_code, const void *response,
+				size_t response_len0, size_t handles_len,
+									struct session_data *auth_session,
+									void *response_auth, const void *auth)
+{
+	u8 cc_data[4];
+	u8 hmac[TPM2_DIGEST_LEN];
+	u8 hmac_data[TPM2_DIGEST_LEN * 3 + 1];
+	u16 tpmnonce_sz;
+	u16 hmac_sz;
+	u8 auth_continue;
+
+	sha256_context ctx;
+	const size_t return_code_offset = 6;
+	const size_t response_header_length = 10;
+
+	if (!auth_session || !auth_session->valid)
+		return TPM_LIB_ERROR;
+
+	size_t offset = 0;
+
+	if (pack_byte_string(cc_data, sizeof(cc_data), "d",
+			     offset, command_code))
+		return TPM_LIB_ERROR;
+
+	sha256_starts(&ctx);
+	sha256_update(&ctx, response + return_code_offset, 4);
+	sha256_update(&ctx, cc_data, 4);
+	if (response_len0 > response_header_length + handles_len + 4)
+		sha256_update(&ctx,
+			      response + response_header_length + handles_len + 4,
+					response_len0 - response_header_length
+					- handles_len - 4);
+	sha256_finish(&ctx, hmac_data);
+
+	/* Nonce rotation */
+	memcpy(auth_session->oldernonce, auth_session->newernonce,
TPM2_DIGEST_LEN);
+
+	if (unpack_byte_string(response_auth, TPM2_RESPONSE_AUTH_LENGTH, "w",
+			       offset, &tpmnonce_sz))
+		return TPM_LIB_ERROR;
+
+	if (unpack_byte_string(response_auth, TPM2_RESPONSE_AUTH_LENGTH, "sbw",
+			       offset + 2, auth_session->newernonce, tpmnonce_sz,
+			   offset + 2 + tpmnonce_sz, &auth_continue,
+			   offset + 2 + tpmnonce_sz + 1, &hmac_sz))
+		return TPM_LIB_ERROR;
+
+	offset = TPM2_DIGEST_LEN;
+
+	if (pack_byte_string(hmac_data, sizeof(hmac_data), "ssb",
+			     offset, auth_session->newernonce, TPM2_DIGEST_LEN,
+				 offset + TPM2_DIGEST_LEN, auth_session->oldernonce, TPM2_DIGEST_LEN,
+				 offset + (2 * TPM2_DIGEST_LEN), auth_continue))
+		return TPM_LIB_ERROR;
+
+	sha256_hmac(auth, TPM2_DIGEST_LEN, hmac_data, TPM2_DIGEST_LEN * 3 + 1,
hmac);
+
+	/* Check for / fix timing leaks here */
+	return memcmp(response_auth + 2 + tpmnonce_sz + 1 + 2,
+					hmac, TPM2_DIGEST_LEN) == 0 ? 0 : TPM_LIB_ERROR;
+}
+
+static u32 tpm2_unseal(struct udevice *dev, u8 *nonce,
+		       struct session_data *auth_session,
+			struct tpm2_object *object,
+			void *data, u16 *data_sz)
+{
+	u16 parameter_sz;
+	size_t request_len0, response_len0, response_len = COMMAND_BUFFER_SIZE;
+	size_t offset;
+	u32 ret;
+	u8 response[COMMAND_BUFFER_SIZE];
+	u8 hmac_key[TPM2_DIGEST_LEN];
+
+	memset(hmac_key, 0, TPM2_DIGEST_LEN);
+
+	u8 command_v2[COMMAND_BUFFER_SIZE] = {
+		tpm_u16(TPM2_ST_SESSIONS),				/* TAG */
+		tpm_u32(0x5B),							/* Length */
+		tpm_u32(TPM2_CC_UNSEAL),				/* Command code */
+		tpm_u32(object->handle),
+	};
+
+	/* rotate the nonces */
+	memcpy(auth_session->oldernonce, auth_session->newernonce,
TPM2_DIGEST_LEN);
+	memcpy(auth_session->newernonce, nonce, TPM2_DIGEST_LEN);
+
+	request_len0 = 0x0E;
+
+	if (create_request_auth(command_v2,
+				request_len0,
+		object,
+		auth_session,
+		&command_v2[request_len0], hmac_key))
+		return TPM_LIB_ERROR;
+
+	ret = tpm_sendrecv_command(dev, command_v2, response, &response_len);
+	if (ret)
+		return ret;
+
+	offset = 10;
+	if (unpack_byte_string(response, response_len, "d", offset,
&parameter_sz))
+		return TPM_LIB_ERROR;
+
+	response_len0 = response_len - TPM2_RESPONSE_AUTH_LENGTH;
+	if (verify_response_auth(TPM2_CC_UNSEAL,
+				 response,
+						response_len0,
+						0,
+						auth_session,
+						&response[response_len0], hmac_key))
+		return TPM_LIB_ERROR;
+
+	if (unpack_byte_string(response, response_len, "w", offset + 4, data_sz))
+		return TPM_LIB_ERROR;
+
+	if (unpack_byte_string(response, response_len, "s", offset + 6, data,
*data_sz))
+		return TPM_LIB_ERROR;
+
+	return ret;
+}
+
+static u32 tpm2_flushcontext(struct udevice *dev, struct session_data
*auth_session)
+{
+	u32 rc;
+	size_t response_len = COMMAND_BUFFER_SIZE;
+	u8 response[COMMAND_BUFFER_SIZE];
+
+	u8 command_v2[COMMAND_BUFFER_SIZE] = {
+		tpm_u16(TPM2_ST_NO_SESSIONS),			/* TAG */
+		tpm_u32(0x0E),							/* Length */
+		tpm_u32(TPM2_CC_FLUSH_CONTEXT),		/* Command code */
+		/* TPMI_SH_POLICY */
+		tpm_u32(auth_session->handle)
+	};
+
+	if (!auth_session || !auth_session->valid)
+		return TPM_LIB_ERROR;
+
+	rc = tpm_sendrecv_command(dev, command_v2, response, &response_len);
+	if (rc)
+		return rc;
+
+	auth_session->valid = 0;
+
+	return 0;
+}
+
+u32 tpm2_do_unseal(struct udevice *dev, u32 handle, u32 pcr_index, unsigned
int idx_min_sz,
+		   u8 *unsealed_data, u16 *unsealed_data_sz)
+{
+	sha256_context ctx;
+	u32 rc;
+	u8 nonce[TPM2_DIGEST_LEN];
+	u8 pcr_data[TPM2_DIGEST_LEN];
+	u8 pcr_data_digest[TPM2_DIGEST_LEN];
+	unsigned int updates;
+	struct tpm2_object object;
+	struct session_data auth_session = {0, };
+
+	memset(nonce, 0, TPM2_DIGEST_LEN);
+
+	rc =  tpm2_start_authsession(dev, TPM_SE_POLICY, nonce, &auth_session);
+	if (rc)
+		return rc;
+
+	rc = tpm2_pcr_read(dev, pcr_index, idx_min_sz, pcr_data, &updates);
+	if (rc)
+		return rc;
+
+	sha256_starts(&ctx);
+	sha256_update(&ctx, pcr_data, TPM2_DIGEST_LEN);
+	sha256_finish(&ctx, pcr_data_digest);
+
+	rc = tpm2_policypcr(dev, pcr_data_digest, pcr_index, idx_min_sz,
auth_session.handle);
+	if (rc)
+		return rc;
+
+	rc = tpm2_readpublic(dev, handle, &object);
+	if (rc)
+		return rc;