Patchwork [U-Boot] tpm: add AUTH1 cmds for LoadKey2 and GetPubKey

login
register
mail settings
Submitter Pfau, Reinhard
Date April 11, 2013, 11:14 a.m.
Message ID <1365678849-11759-1-git-send-email-pfau@gdsys.de>
Download mbox | patch
Permalink /patch/235700/
State Superseded
Delegated to: Tom Rini
Headers show

Comments

Pfau, Reinhard - April 11, 2013, 11:14 a.m.
This patch requires Che-Liang Chious TPM library patch to be applied first.
(published 2013-02-28 on ML as "[PATCH v3] tpm: Add TPM command library")

Extend the tpm library with support for single authorized (AUTH1) commands
as specified in the TCG Main Specification 1.2. (The internally used helper
functions are implemented in a way that they could also be used for double
authorized commands if someone needs it.)

Provide enums with the return codes from the TCG Main specification.

For now only a single OIAP session is supported.

OIAP authorized version of the commands TPM_LoadKey2 and TPM_GetPubKey are
provided. Both features are available using the 'tpm' command, too.

Authorized commands are enabled with CONFIG_TPM_AUTH_SESSIONS. (Note that
this also requires CONFIG_SHA1 to be enabled.)

Signed-off-by: Reinhard Pfau <pfau@gdsys.de>
---
 README           |   14 ++
 common/cmd_tpm.c |   93 +++++++++++++++
 include/tpm.h    |  174 +++++++++++++++++++++++++++
 lib/tpm.c        |  348 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 628 insertions(+), 1 deletions(-)

Patch

diff --git a/README b/README
index 42544ce..b21c352 100644
--- a/README
+++ b/README
@@ -1199,6 +1199,20 @@  The following options need to be configured:
 			to. Contemporary x86 systems usually map it at
 			0xfed40000.
 
+		CONFIG_CMD_TPM
+		Add tpm monitor functions.
+		Requires CONFIG_TPM. If CONFIG_TPM_AUTH_SESSIONS is set, also
+		provides monitor access to authorized functions.
+
+		CONFIG_TPM
+		Define this to enable the TPM support library which provides
+		functional interfaces to some TPM commands.
+		Requires support for a TPM device.
+
+		CONFIG_TPM_AUTH_SESSIONS
+		Define this to enable authorized functions in the TPM library.
+		Requires CONFIG_TPM and CONFIG_SHA1.
+
 - USB Support:
 		At the moment only the UHCI host controller is
 		supported (PIP405, MIP405, MPC5200); define
diff --git a/common/cmd_tpm.c b/common/cmd_tpm.c
index 46fae18..c8767a4 100644
--- a/common/cmd_tpm.c
+++ b/common/cmd_tpm.c
@@ -546,6 +546,72 @@  static int do_tpm_nv_write(cmd_tbl_t *cmdtp, int flag,
 	return convert_return_code(err);
 }
 
+#ifdef CONFIG_TPM_AUTH_SESSIONS
+
+static int do_tpm_oiap(cmd_tbl_t *cmdtp, int flag,
+		int argc, char * const argv[])
+{
+	uint32_t auth_handle, err;
+
+	err = tpm_oiap(&auth_handle);
+
+	return convert_return_code(err);
+}
+
+static int do_tpm_load_key2_oiap(cmd_tbl_t *cmdtp, int flag,
+		int argc, char * const argv[])
+{
+	uint32_t parent_handle, key_len, key_handle, err;
+	uint8_t usage_auth[20];
+	void *key;
+
+	if (argc < 5)
+		return CMD_RET_USAGE;
+
+	parent_handle = simple_strtoul(argv[1], NULL, 0);
+	key = (void *)simple_strtoul(argv[2], NULL, 0);
+	key_len = simple_strtoul(argv[3], NULL, 0);
+	if (strlen(argv[4]) != 40)
+		return CMD_RET_FAILURE;
+	parse_byte_string(argv[4], usage_auth, NULL);
+
+	err = tpm_load_key2_oiap(parent_handle, key, key_len, usage_auth,
+			&key_handle);
+	if (!err)
+		printf("Key handle is 0x%x\n", key_handle);
+
+	return convert_return_code(err);
+}
+
+static int do_tpm_get_pub_key_oiap(cmd_tbl_t *cmdtp, int flag,
+		int argc, char * const argv[])
+{
+	uint32_t key_handle, err;
+	uint8_t usage_auth[20];
+	uint8_t pub_key_buffer[288];
+	size_t pub_key_len = sizeof(pub_key_buffer);
+
+	if (argc < 3)
+		return CMD_RET_USAGE;
+
+	key_handle = simple_strtoul(argv[1], NULL, 0);
+	if (strlen(argv[2]) != 40)
+		return CMD_RET_FAILURE;
+	parse_byte_string(argv[2], usage_auth, NULL);
+
+	err = tpm_get_pub_key_oiap(key_handle, usage_auth,
+			pub_key_buffer, &pub_key_len);
+	if (!err) {
+		printf("dump of received pub key structure:\n");
+		print_byte_string(pub_key_buffer, pub_key_len);
+	}
+	return convert_return_code(err);
+}
+
+TPM_COMMAND_NO_ARG(tpm_end_oiap)
+
+#endif /* CONFIG_TPM_AUTH_SESSIONS */
+
 #define MAKE_TPM_CMD_ENTRY(cmd) \
 	U_BOOT_CMD_MKENT(cmd, 0, 1, do_tpm_ ## cmd, "", "")
 
@@ -590,6 +656,16 @@  static cmd_tbl_t tpm_commands[] = {
 			do_tpm_nv_read, "", ""),
 	U_BOOT_CMD_MKENT(nv_write, 0, 1,
 			do_tpm_nv_write, "", ""),
+#ifdef CONFIG_TPM_AUTH_SESSIONS
+	U_BOOT_CMD_MKENT(oiap, 0, 1,
+			do_tpm_oiap, "", ""),
+	U_BOOT_CMD_MKENT(end_oiap, 0, 1,
+			do_tpm_end_oiap, "", ""),
+	U_BOOT_CMD_MKENT(load_key2_oiap, 0, 1,
+			do_tpm_load_key2_oiap, "", ""),
+	U_BOOT_CMD_MKENT(get_pub_key_oiap, 0, 1,
+			do_tpm_get_pub_key_oiap, "", ""),
+#endif /* CONFIG_TPM_AUTH_SESSIONS */
 };
 
 static int do_tpm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
@@ -638,6 +714,16 @@  U_BOOT_CMD(tpm, CONFIG_SYS_MAXARGS, 1, do_tpm,
 "  get_capability cap_area sub_cap addr count\n"
 "    - Read <count> bytes of TPM capability indexed by <cap_area> and\n"
 "      <sub_cap> to memory address <addr>.\n"
+#ifdef CONFIG_TPM_AUTH_SESSIONS
+"Storage functions\n"
+"  loadkey2_oiap parent_handle key_addr key_len usage_auth\n"
+"    - loads a key data from memory address <key_addr>, <key_len> bytes\n"
+"      into TPM using the parent key <parent_handle> with authorization\n"
+"      <usage_auth> (20 bytes hex string).\n"
+"  get_pub_key_oiap key_handle usage_auth\n"
+"    - get the public key portion of a loaded key <key_handle> using\n"
+"      authorization <usage auth> (20 bytes hex string)\n"
+#endif /* CONFIG_TPM_AUTH_SESSIONS */
 "Endorsement Key Handling Commands:\n"
 "  read_pubek addr count\n"
 "    - Read <count> bytes of the public endorsement key to memory\n"
@@ -648,6 +734,13 @@  U_BOOT_CMD(tpm, CONFIG_SYS_MAXARGS, 1, do_tpm,
 "      <digest_hex_string>\n"
 "  pcr_read index addr count\n"
 "    - Read <count> bytes from PCR <index> to memory address <addr>.\n"
+#ifdef CONFIG_TPM_AUTH_SESSIONS
+"Authorization Sessions\n"
+"  oiap\n"
+"    - setup an OIAP session\n"
+"  end_oiap\n"
+"    - terminates an active OIAP session\n"
+#endif /* CONFIG_TPM_AUTH_SESSIONS */
 "Non-volatile Storage Commands:\n"
 "  nv_define_space index permission size\n"
 "    - Establish a space at index <index> with <permission> of <size> bytes.\n"
diff --git a/include/tpm.h b/include/tpm.h
index 7219b73..5e9f832 100644
--- a/include/tpm.h
+++ b/include/tpm.h
@@ -1,5 +1,6 @@ 
 /*
  * Copyright (c) 2013 The Chromium OS Authors.
+ * Coypright (c) 2013 Guntermann & Drunck GmbH
  *
  * See file CREDITS for list of people who contributed to this
  * project.
@@ -54,6 +55,120 @@  enum tpm_nv_index {
 };
 
 /**
+ * TPM return codes as defined in the TCG Main specification
+ * (TPM Main Part 2 Structures; Specification version 1.2)
+ */
+enum tpm_return_code {
+	TPM_BASE	= 0x00000000,
+	TPM_NON_FATAL	= 0x00000800,
+	TPM_SUCCESS	= TPM_BASE,
+	/* TPM-defined fatal error codes */
+	TPM_AUTHFAIL			= TPM_BASE +  1,
+	TPM_BADINDEX			= TPM_BASE +  2,
+	TPM_BAD_PARAMETER		= TPM_BASE +  3,
+	TPM_AUDITFAILURE		= TPM_BASE +  4,
+	TPM_CLEAR_DISABLED		= TPM_BASE +  5,
+	TPM_DEACTIVATED			= TPM_BASE +  6,
+	TPM_DISABLED			= TPM_BASE +  7,
+	TPM_DISABLED_CMD		= TPM_BASE +  8,
+	TPM_FAIL			= TPM_BASE +  9,
+	TPM_BAD_ORDINAL			= TPM_BASE + 10,
+	TPM_INSTALL_DISABLED		= TPM_BASE + 11,
+	TPM_INVALID_KEYHANDLE		= TPM_BASE + 12,
+	TPM_KEYNOTFOUND			= TPM_BASE + 13,
+	TPM_INAPPROPRIATE_ENC		= TPM_BASE + 14,
+	TPM_MIGRATE_FAIL		= TPM_BASE + 15,
+	TPM_INVALID_PCR_INFO		= TPM_BASE + 16,
+	TPM_NOSPACE			= TPM_BASE + 17,
+	TPM_NOSRK			= TPM_BASE + 18,
+	TPM_NOTSEALED_BLOB		= TPM_BASE + 19,
+	TPM_OWNER_SET			= TPM_BASE + 20,
+	TPM_RESOURCES			= TPM_BASE + 21,
+	TPM_SHORTRANDOM			= TPM_BASE + 22,
+	TPM_SIZE			= TPM_BASE + 23,
+	TPM_WRONGPCRVAL			= TPM_BASE + 24,
+	TPM_BAD_PARAM_SIZE		= TPM_BASE + 25,
+	TPM_SHA_THREAD			= TPM_BASE + 26,
+	TPM_SHA_ERROR			= TPM_BASE + 27,
+	TPM_FAILEDSELFTEST		= TPM_BASE + 28,
+	TPM_AUTH2FAIL			= TPM_BASE + 29,
+	TPM_BADTAG			= TPM_BASE + 30,
+	TPM_IOERROR			= TPM_BASE + 31,
+	TPM_ENCRYPT_ERROR		= TPM_BASE + 32,
+	TPM_DECRYPT_ERROR		= TPM_BASE + 33,
+	TPM_INVALID_AUTHHANDLE		= TPM_BASE + 34,
+	TPM_NO_ENDORSEMENT		= TPM_BASE + 35,
+	TPM_INVALID_KEYUSAGE		= TPM_BASE + 36,
+	TPM_WRONG_ENTITYTYPE		= TPM_BASE + 37,
+	TPM_INVALID_POSTINIT		= TPM_BASE + 38,
+	TPM_INAPPROPRIATE_SIG		= TPM_BASE + 39,
+	TPM_BAD_KEY_PROPERTY		= TPM_BASE + 40,
+	TPM_BAD_MIGRATION		= TPM_BASE + 41,
+	TPM_BAD_SCHEME			= TPM_BASE + 42,
+	TPM_BAD_DATASIZE		= TPM_BASE + 43,
+	TPM_BAD_MODE			= TPM_BASE + 44,
+	TPM_BAD_PRESENCE		= TPM_BASE + 45,
+	TPM_BAD_VERSION			= TPM_BASE + 46,
+	TPM_NO_WRAP_TRANSPORT		= TPM_BASE + 47,
+	TPM_AUDITFAIL_UNSUCCESSFUL	= TPM_BASE + 48,
+	TPM_AUDITFAIL_SUCCESSFUL	= TPM_BASE + 49,
+	TPM_NOTRESETABLE		= TPM_BASE + 50,
+	TPM_NOTLOCAL			= TPM_BASE + 51,
+	TPM_BAD_TYPE			= TPM_BASE + 52,
+	TPM_INVALID_RESOURCE		= TPM_BASE + 53,
+	TPM_NOTFIPS			= TPM_BASE + 54,
+	TPM_INVALID_FAMILY		= TPM_BASE + 55,
+	TPM_NO_NV_PERMISSION		= TPM_BASE + 56,
+	TPM_REQUIRES_SIGN		= TPM_BASE + 57,
+	TPM_KEY_NOTSUPPORTED		= TPM_BASE + 58,
+	TPM_AUTH_CONFLICT		= TPM_BASE + 59,
+	TPM_AREA_LOCKED			= TPM_BASE + 60,
+	TPM_BAD_LOCALITY		= TPM_BASE + 61,
+	TPM_READ_ONLY			= TPM_BASE + 62,
+	TPM_PER_NOWRITE			= TPM_BASE + 63,
+	TPM_FAMILY_COUNT		= TPM_BASE + 64,
+	TPM_WRITE_LOCKED		= TPM_BASE + 65,
+	TPM_BAD_ATTRIBUTES		= TPM_BASE + 66,
+	TPM_INVALID_STRUCTURE		= TPM_BASE + 67,
+	TPM_KEY_OWNER_CONTROL		= TPM_BASE + 68,
+	TPM_BAD_COUNTER			= TPM_BASE + 69,
+	TPM_NOT_FULLWRITE		= TPM_BASE + 70,
+	TPM_CONTEXT_GAP			= TPM_BASE + 71,
+	TPM_MAXNVWRITES			= TPM_BASE + 72,
+	TPM_NOOPERATOR			= TPM_BASE + 73,
+	TPM_RESOURCEMISSING		= TPM_BASE + 74,
+	TPM_DELEGATE_LOCK		= TPM_BASE + 75,
+	TPM_DELEGATE_FAMILY		= TPM_BASE + 76,
+	TPM_DELEGATE_ADMIN		= TPM_BASE + 77,
+	TPM_TRANSPORT_NOTEXCLUSIVE	= TPM_BASE + 78,
+	TPM_OWNER_CONTROL		= TPM_BASE + 79,
+	TPM_DAA_RESOURCES		= TPM_BASE + 80,
+	TPM_DAA_INPUT_DATA0		= TPM_BASE + 81,
+	TPM_DAA_INPUT_DATA1		= TPM_BASE + 82,
+	TPM_DAA_ISSUER_SETTINGS		= TPM_BASE + 83,
+	TPM_DAA_TPM_SETTINGS		= TPM_BASE + 84,
+	TPM_DAA_STAGE			= TPM_BASE + 85,
+	TPM_DAA_ISSUER_VALIDITY		= TPM_BASE + 86,
+	TPM_DAA_WRONG_W			= TPM_BASE + 87,
+	TPM_BAD_HANDLE			= TPM_BASE + 88,
+	TPM_BAD_DELEGATE		= TPM_BASE + 89,
+	TPM_BADCONTEXT			= TPM_BASE + 90,
+	TPM_TOOMANYCONTEXTS		= TPM_BASE + 91,
+	TPM_MA_TICKET_SIGNATURE		= TPM_BASE + 92,
+	TPM_MA_DESTINATION		= TPM_BASE + 93,
+	TPM_MA_SOURCE			= TPM_BASE + 94,
+	TPM_MA_AUTHORITY		= TPM_BASE + 95,
+	TPM_PERMANENTEK			= TPM_BASE + 97,
+	TPM_BAD_SIGNATURE		= TPM_BASE + 98,
+	TPM_NOCONTEXTSPACE		= TPM_BASE + 99,
+	/* TPM-defined non-fatal errors */
+	TPM_RETRY		= TPM_BASE + TPM_NON_FATAL,
+	TPM_NEEDS_SELFTEST	= TPM_BASE + TPM_NON_FATAL + 1,
+	TPM_DOING_SELFTEST	= TPM_BASE + TPM_NON_FATAL + 2,
+	TPM_DEFEND_LOCK_RUNNING	= TPM_BASE + TPM_NON_FATAL + 3,
+};
+
+/**
  * Initialize TPM device.  It must be called before any TPM commands.
  *
  * @return 0 on success, non-0 on error.
@@ -201,4 +316,63 @@  uint32_t tpm_physical_set_deactivated(uint8_t state);
 uint32_t tpm_get_capability(uint32_t cap_area, uint32_t sub_cap,
 		void *cap, size_t count);
 
+/**
+ * Issue a TPM_FlushSpecific command for a AUTH ressource.
+ *
+ * @param auth_handle	handle of the auth session
+ * @return return code of the operation
+ */
+uint32_t tpm_terminate_auth_session(uint32_t auth_handle);
+
+/**
+ * Issue a TPM_OIAP command to setup an object independant authorization
+ * session.
+ * Information about the session is stored internally.
+ * If there was already an OIAP session active it is terminated and a new
+ * session is set up.
+ *
+ * @param auth_handle	pointer to the (new) auth handle or NULL.
+ * @return return code of the operation
+ */
+uint32_t tpm_oiap(uint32_t *auth_handle);
+
+/**
+ * Ends an active OIAP session.
+ *
+ * @return return code of the operation
+ */
+uint32_t tpm_end_oiap(void);
+
+/**
+ * Issue a TPM_LoadKey2 (Auth1) command using an OIAP session for authenticating
+ * the usage of the parent key.
+ *
+ * @param parent_handle	handle of the parent key.
+ * @param key		pointer to the key structure (TPM_KEY or TPM_KEY12).
+ * @param key_length	size of the key structure
+ * @param parent_key_usage_auth	usage auth for the parent key
+ * @param key_handle	pointer to the key handle
+ * @return return code of the operation
+ */
+uint32_t tpm_load_key2_oiap(uint32_t parent_handle,
+		const void *key, size_t key_length,
+		const void *parent_key_usage_auth,
+		uint32_t *key_handle);
+
+/**
+ * Issue a TPM_GetPubKey (Auth1) command using an OIAP session for
+ * authenticating the usage of the key.
+ *
+ * @param key_handle	handle of the key
+ * @param usage_auth	usage auth for the key
+ * @param pubkey	pointer to the pub key buffer; may be NULL if the pubkey
+ *			should not be stored.
+ * @param pubkey_len	pointer to the pub key buffer len. On entry: the size of
+ *			the provided pubkey buffer. On successful exit: the size
+ *			of the stored TPM_PUBKEY structure (iff pubkey != NULL).
+ * @return return code of the operation
+ */
+uint32_t tpm_get_pub_key_oiap(uint32_t key_handle, const void *usage_auth,
+		void *pubkey, size_t *pubkey_len);
+
 #endif /* __TPM_H */
diff --git a/lib/tpm.c b/lib/tpm.c
index 42c9bea..87bf188 100644
--- a/lib/tpm.c
+++ b/lib/tpm.c
@@ -1,5 +1,6 @@ 
 /*
  * Copyright (c) 2013 The Chromium OS Authors.
+ * Coypright (c) 2013 Guntermann & Drunck GmbH
  *
  * See file CREDITS for list of people who contributed to this
  * project.
@@ -22,6 +23,7 @@ 
 
 #include <common.h>
 #include <stdarg.h>
+#include <sha1.h>
 #include <tpm.h>
 #include <asm/unaligned.h>
 
@@ -35,8 +37,31 @@  enum {
 	TPM_REQUEST_HEADER_LENGTH	= 10,
 	TPM_RESPONSE_HEADER_LENGTH	= 10,
 	PCR_DIGEST_LENGTH		= 20,
+	DIGEST_LENGTH			= 20,
+	TPM_REQUEST_AUTH_LENGTH		= 45,
+	TPM_RESPONSE_AUTH_LENGTH	= 41,
+	/* some max lengths, valid for RSA keys <= 2048 bits */
+	TPM_KEY12_MAX_LENGTH		= 618,
+	TPM_PUBKEY_MAX_LENGTH		= 288,
 };
 
+#ifdef CONFIG_TPM_AUTH_SESSIONS
+
+#ifndef CONFIG_SHA1
+#error "TPM_AUTH_SESSIONS require SHA1 to be configured, too"
+#endif /* !CONFIG_SHA1 */
+
+struct session_data {
+	int		valid;
+	uint32_t	handle;
+	uint8_t		nonce_even[DIGEST_LENGTH];
+	uint8_t		nonce_odd[DIGEST_LENGTH];
+};
+
+static struct session_data oiap_session = {0, };
+
+#endif /* CONFIG_TPM_AUTH_SESSIONS */
+
 /**
  * Pack data into a byte string.  The data types are specified in
  * the format string: 'b' means unsigned byte, 'w' unsigned word,
@@ -235,7 +260,7 @@  static uint32_t tpm_sendrecv_command(const void *command,
 			response, &response_length);
 	if (err)
 		return TPM_LIB_ERROR;
-	if (response)
+	if (size_ptr)
 		*size_ptr = response_length;
 
 	return tpm_return_code(response);
@@ -579,3 +604,324 @@  uint32_t tpm_get_capability(uint32_t cap_area, uint32_t sub_cap,
 
 	return 0;
 }
+
+#ifdef CONFIG_TPM_AUTH_SESSIONS
+
+/**
+ * Fill an authentication block in a request.
+ * This func can create the first as well as the second auth block (for
+ * double authorized commands).
+ *
+ * @param request	pointer to the request (w/ uninitialised auth data)
+ * @param request_len0	length of the request without auth data
+ * @param handles_len	length of the handles area in request
+ * @param auth_session	pointer to the (valid) auth session to be used
+ * @param request_auth	pointer to the auth block of the request to be filled
+ * @param auth		authentication data (HMAC key)
+ */
+static uint32_t create_request_auth(const void *request, size_t request_len0,
+	size_t handles_len,
+	struct session_data *auth_session,
+	void *request_auth, const void *auth)
+{
+	uint8_t hmac_data[DIGEST_LENGTH * 3 + 1];
+	sha1_context hash_ctx;
+	const size_t command_code_offset = 6;
+	const size_t auth_nonce_odd_offset = 4;
+	const size_t auth_continue_offset = 24;
+	const size_t auth_auth_offset = 25;
+
+	if (!auth_session || !auth_session->valid)
+		return TPM_LIB_ERROR;
+
+	sha1_starts(&hash_ctx);
+	sha1_update(&hash_ctx, request + command_code_offset, 4);
+	if (request_len0 > TPM_REQUEST_HEADER_LENGTH + handles_len)
+		sha1_update(&hash_ctx,
+			request + TPM_REQUEST_HEADER_LENGTH + handles_len,
+			request_len0 - TPM_REQUEST_HEADER_LENGTH - handles_len);
+	sha1_finish(&hash_ctx, hmac_data);
+
+	sha1_starts(&hash_ctx);
+	sha1_update(&hash_ctx, auth_session->nonce_odd, DIGEST_LENGTH);
+	sha1_update(&hash_ctx, hmac_data, sizeof(hmac_data));
+	sha1_finish(&hash_ctx, auth_session->nonce_odd);
+
+	if (pack_byte_string(request_auth, TPM_REQUEST_AUTH_LENGTH, "dsb",
+			0, auth_session->handle,
+			auth_nonce_odd_offset, auth_session->nonce_odd,
+			DIGEST_LENGTH,
+			auth_continue_offset, 1))
+		return TPM_LIB_ERROR;
+	if (pack_byte_string(hmac_data, sizeof(hmac_data), "ss",
+				DIGEST_LENGTH,
+				auth_session->nonce_even,
+				DIGEST_LENGTH,
+				2 * DIGEST_LENGTH,
+				request_auth + auth_nonce_odd_offset,
+				DIGEST_LENGTH + 1))
+		return TPM_LIB_ERROR;
+	sha1_hmac(auth, DIGEST_LENGTH, hmac_data, sizeof(hmac_data),
+		request_auth + auth_auth_offset);
+
+	return TPM_SUCCESS;
+}
+
+/**
+ * Verify an authentication block in a response.
+ * Since this func updates the nonce_even in the session data it has to be
+ * called when receiving a succesfull AUTH response.
+ * This func can verify the first as well as the second auth block (for
+ * double authorized commands).
+ *
+ * @param command_code	command code of the request
+ * @param response	pointer to the request (w/ uninitialised auth data)
+ * @param handles_len	length of the handles area in response
+ * @param auth_session	pointer to the (valid) auth session to be used
+ * @param response_auth	pointer to the auth block of the response to be verified
+ * @param auth		authentication data (HMAC key)
+ */
+static uint32_t verify_response_auth(uint32_t command_code,
+	const void *response, size_t response_len0,
+	size_t handles_len,
+	struct session_data *auth_session,
+	const void *response_auth, const void *auth)
+{
+	uint8_t hmac_data[DIGEST_LENGTH * 3 + 1];
+	uint8_t computed_auth[DIGEST_LENGTH];
+	sha1_context hash_ctx;
+	const size_t return_code_offset = 6;
+	const size_t auth_continue_offset = 20;
+	const size_t auth_auth_offset = 21;
+	uint8_t auth_continue;
+
+	if (!auth_session || !auth_session->valid)
+		return TPM_AUTHFAIL;
+	if (pack_byte_string(hmac_data, sizeof(hmac_data), "d",
+				0, command_code))
+		return TPM_LIB_ERROR;
+	if (response_len0 < TPM_RESPONSE_HEADER_LENGTH)
+		return TPM_LIB_ERROR;
+
+	sha1_starts(&hash_ctx);
+	sha1_update(&hash_ctx, response + return_code_offset, 4);
+	sha1_update(&hash_ctx, hmac_data, 4);
+	if (response_len0 > TPM_RESPONSE_HEADER_LENGTH + handles_len)
+		sha1_update(&hash_ctx,
+			response + TPM_RESPONSE_HEADER_LENGTH + handles_len,
+			response_len0 - TPM_RESPONSE_HEADER_LENGTH
+			- handles_len);
+	sha1_finish(&hash_ctx, hmac_data);
+
+	memcpy(auth_session->nonce_even, response_auth, DIGEST_LENGTH);
+	auth_continue = ((uint8_t *)response_auth)[auth_continue_offset];
+	if (pack_byte_string(hmac_data, sizeof(hmac_data), "ssb",
+				DIGEST_LENGTH,
+				response_auth,
+				DIGEST_LENGTH,
+				2 * DIGEST_LENGTH,
+				auth_session->nonce_odd,
+				DIGEST_LENGTH,
+				3 * DIGEST_LENGTH,
+				auth_continue))
+		return TPM_LIB_ERROR;
+
+	sha1_hmac(auth, DIGEST_LENGTH, hmac_data, sizeof(hmac_data),
+		computed_auth);
+
+	if (memcmp(computed_auth, response_auth + auth_auth_offset,
+			DIGEST_LENGTH))
+		return TPM_AUTHFAIL;
+
+	return TPM_SUCCESS;
+}
+
+
+uint32_t tpm_terminate_auth_session(uint32_t auth_handle)
+{
+	const uint8_t command[18] = {
+		0x00, 0xc1,		/* TPM_TAG */
+		0x00, 0x00, 0x00, 0x00,	/* parameter size */
+		0x00, 0x00, 0x00, 0xba,	/* TPM_COMMAND_CODE */
+		0x00, 0x00, 0x00, 0x00,	/* TPM_HANDLE */
+		0x00, 0x00, 0x00, 0x02,	/* TPM_RESSOURCE_TYPE */
+	};
+	const size_t req_handle_offset = TPM_REQUEST_HEADER_LENGTH;
+	uint8_t request[COMMAND_BUFFER_SIZE];
+
+	if (pack_byte_string(request, sizeof(request), "sd",
+			0, command, sizeof(command),
+			req_handle_offset, auth_handle))
+		return TPM_LIB_ERROR;
+	if (oiap_session.valid && oiap_session.handle == auth_handle)
+		oiap_session.valid = 0;
+
+	return tpm_sendrecv_command(request, NULL, NULL);
+}
+
+uint32_t tpm_end_oiap(void)
+{
+	uint32_t err = TPM_SUCCESS;
+	if (oiap_session.valid)
+		err = tpm_terminate_auth_session(oiap_session.handle);
+	return err;
+}
+
+uint32_t tpm_oiap(uint32_t *auth_handle)
+{
+	const uint8_t command[10] = {
+		0x00, 0xc1,		/* TPM_TAG */
+		0x00, 0x00, 0x00, 0x0a,	/* parameter size */
+		0x00, 0x00, 0x00, 0x0a,	/* TPM_COMMAND_CODE */
+	};
+	const size_t res_auth_handle_offset = TPM_RESPONSE_HEADER_LENGTH;
+	const size_t res_nonce_even_offset = TPM_RESPONSE_HEADER_LENGTH + 4;
+	uint8_t response[COMMAND_BUFFER_SIZE];
+	size_t response_length = sizeof(response);
+	uint32_t err;
+
+	if (oiap_session.valid)
+		tpm_terminate_auth_session(oiap_session.handle);
+
+	err = tpm_sendrecv_command(command, response, &response_length);
+	if (err)
+		return err;
+	if (unpack_byte_string(response, response_length, "ds",
+			res_auth_handle_offset, &oiap_session.handle,
+			res_nonce_even_offset, &oiap_session.nonce_even,
+			(uint32_t)DIGEST_LENGTH))
+		return TPM_LIB_ERROR;
+	oiap_session.valid = 1;
+	if (auth_handle)
+		*auth_handle = oiap_session.handle;
+	return 0;
+}
+
+uint32_t tpm_load_key2_oiap(uint32_t parent_handle,
+		const void *key, size_t key_length,
+		const void *parent_key_usage_auth,
+		uint32_t *key_handle)
+{
+	const uint8_t command[14] = {
+		0x00, 0xc2,		/* TPM_TAG */
+		0x00, 0x00, 0x00, 0x00,	/* parameter size */
+		0x00, 0x00, 0x00, 0x41,	/* TPM_COMMAND_CODE */
+		0x00, 0x00, 0x00, 0x00,	/* parent handle */
+	};
+	const size_t req_size_offset = 2;
+	const size_t req_parent_handle_offset = TPM_REQUEST_HEADER_LENGTH;
+	const size_t req_key_offset = TPM_REQUEST_HEADER_LENGTH + 4;
+	const size_t res_handle_offset = TPM_RESPONSE_HEADER_LENGTH;
+	uint8_t request[sizeof(command) + TPM_KEY12_MAX_LENGTH
+			+ TPM_REQUEST_AUTH_LENGTH];
+	uint8_t response[COMMAND_BUFFER_SIZE];
+	size_t response_length = sizeof(response);
+	uint32_t err;
+
+	if (!oiap_session.valid) {
+		err = tpm_oiap(NULL);
+		if (err)
+			return err;
+	}
+	if (pack_byte_string(request, sizeof(request), "sdds",
+			0, command, sizeof(command),
+			req_size_offset,
+			sizeof(command) + key_length + TPM_REQUEST_AUTH_LENGTH,
+			req_parent_handle_offset, parent_handle,
+			req_key_offset, key, key_length
+		))
+		return TPM_LIB_ERROR;
+
+	err = create_request_auth(request, sizeof(command) + key_length, 4,
+			&oiap_session,
+			request + sizeof(command) + key_length,
+			parent_key_usage_auth);
+	if (err)
+		return err;
+	err = tpm_sendrecv_command(request, response, &response_length);
+	if (err) {
+		if (err == TPM_AUTHFAIL)
+			oiap_session.valid = 0;
+		return err;
+	}
+
+	err = verify_response_auth(0x00000041, response,
+			response_length - TPM_RESPONSE_AUTH_LENGTH,
+			4, &oiap_session,
+			response + response_length - TPM_RESPONSE_AUTH_LENGTH,
+			parent_key_usage_auth);
+	if (err)
+		return err;
+
+	if (key_handle) {
+		if (unpack_byte_string(response, response_length, "d",
+				res_handle_offset, key_handle))
+			return TPM_LIB_ERROR;
+	}
+
+	return 0;
+}
+
+uint32_t tpm_get_pub_key_oiap(uint32_t key_handle, const void *usage_auth,
+			void *pubkey, size_t *pubkey_len)
+{
+	const uint8_t command[14] = {
+		0x00, 0xc2,		/* TPM_TAG */
+		0x00, 0x00, 0x00, 0x00,	/* parameter size */
+		0x00, 0x00, 0x00, 0x21,	/* TPM_COMMAND_CODE */
+		0x00, 0x00, 0x00, 0x00,	/* key handle */
+	};
+	const size_t req_size_offset = 2;
+	const size_t req_key_handle_offset = TPM_REQUEST_HEADER_LENGTH;
+	const size_t res_pubkey_offset = TPM_RESPONSE_HEADER_LENGTH;
+	uint8_t request[sizeof(command) + TPM_REQUEST_AUTH_LENGTH];
+	uint8_t response[TPM_RESPONSE_HEADER_LENGTH + TPM_PUBKEY_MAX_LENGTH
+			+ TPM_RESPONSE_AUTH_LENGTH];
+	size_t response_length = sizeof(response);
+	uint32_t err;
+
+	if (!oiap_session.valid) {
+		err = tpm_oiap(NULL);
+		if (err)
+			return err;
+	}
+	if (pack_byte_string(request, sizeof(request), "sdd",
+			0, command, sizeof(command),
+			req_size_offset,
+			(uint32_t)(sizeof(command) + TPM_REQUEST_AUTH_LENGTH),
+			req_key_handle_offset, key_handle
+		))
+		return TPM_LIB_ERROR;
+	err = create_request_auth(request, sizeof(command), 4, &oiap_session,
+			request + sizeof(command), usage_auth);
+	if (err)
+		return err;
+	err = tpm_sendrecv_command(request, response, &response_length);
+	if (err) {
+		if (err == TPM_AUTHFAIL)
+			oiap_session.valid = 0;
+		return err;
+	}
+	err = verify_response_auth(0x00000021, response,
+			response_length - TPM_RESPONSE_AUTH_LENGTH,
+			0, &oiap_session,
+			response + response_length - TPM_RESPONSE_AUTH_LENGTH,
+			usage_auth);
+	if (err)
+		return err;
+
+	if (pubkey) {
+		if ((response_length - TPM_RESPONSE_HEADER_LENGTH
+			- TPM_RESPONSE_AUTH_LENGTH) > *pubkey_len)
+			return TPM_LIB_ERROR;
+		*pubkey_len = response_length - TPM_RESPONSE_HEADER_LENGTH
+			- TPM_RESPONSE_AUTH_LENGTH;
+		memcpy(pubkey, response + res_pubkey_offset,
+			response_length - TPM_RESPONSE_HEADER_LENGTH
+				- TPM_RESPONSE_AUTH_LENGTH);
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_TPM_AUTH_SESSIONS */