Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / u-boot / lib / tpm.c
diff --git a/qemu/roms/u-boot/lib/tpm.c b/qemu/roms/u-boot/lib/tpm.c
new file mode 100644 (file)
index 0000000..967c8e6
--- /dev/null
@@ -0,0 +1,914 @@
+/*
+ * Copyright (c) 2013 The Chromium OS Authors.
+ * Coypright (c) 2013 Guntermann & Drunck GmbH
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <stdarg.h>
+#include <sha1.h>
+#include <tpm.h>
+#include <asm/unaligned.h>
+
+/* Internal error of TPM command library */
+#define TPM_LIB_ERROR  ((uint32_t)~0u)
+
+/* Useful constants */
+enum {
+       COMMAND_BUFFER_SIZE             = 256,
+       TPM_PUBEK_SIZE                  = 256,
+       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,
+ * 'd' unsigned double word, and 's' byte string.  The data are a
+ * series of offsets and values (for type byte string there are also
+ * lengths).  The data values are packed into the byte string
+ * sequentially, and so a latter value could over-write a former
+ * value.
+ *
+ * @param str          output string
+ * @param size         size of output string
+ * @param format       format string
+ * @param ...          data points
+ * @return 0 on success, non-0 on error
+ */
+int pack_byte_string(uint8_t *str, size_t size, const char *format, ...)
+{
+       va_list args;
+       size_t offset = 0, length = 0;
+       uint8_t *data = NULL;
+       uint32_t value = 0;
+
+       va_start(args, format);
+       for (; *format; format++) {
+               switch (*format) {
+               case 'b':
+                       offset = va_arg(args, size_t);
+                       value = va_arg(args, int);
+                       length = 1;
+                       break;
+               case 'w':
+                       offset = va_arg(args, size_t);
+                       value = va_arg(args, int);
+                       length = 2;
+                       break;
+               case 'd':
+                       offset = va_arg(args, size_t);
+                       value = va_arg(args, uint32_t);
+                       length = 4;
+                       break;
+               case 's':
+                       offset = va_arg(args, size_t);
+                       data = va_arg(args, uint8_t *);
+                       length = va_arg(args, uint32_t);
+                       break;
+               default:
+                       debug("Couldn't recognize format string\n");
+                       return -1;
+               }
+
+               if (offset + length > size)
+                       return -1;
+
+               switch (*format) {
+               case 'b':
+                       str[offset] = value;
+                       break;
+               case 'w':
+                       put_unaligned_be16(value, str + offset);
+                       break;
+               case 'd':
+                       put_unaligned_be32(value, str + offset);
+                       break;
+               case 's':
+                       memcpy(str + offset, data, length);
+                       break;
+               }
+       }
+       va_end(args);
+
+       return 0;
+}
+
+/**
+ * Unpack data from a byte string.  The data types are specified in
+ * the format string: 'b' means unsigned byte, 'w' unsigned word,
+ * 'd' unsigned double word, and 's' byte string.  The data are a
+ * series of offsets and pointers (for type byte string there are also
+ * lengths).
+ *
+ * @param str          output string
+ * @param size         size of output string
+ * @param format       format string
+ * @param ...          data points
+ * @return 0 on success, non-0 on error
+ */
+int unpack_byte_string(const uint8_t *str, size_t size, const char *format, ...)
+{
+       va_list args;
+       size_t offset = 0, length = 0;
+       uint8_t *ptr8 = NULL;
+       uint16_t *ptr16 = NULL;
+       uint32_t *ptr32 = NULL;
+
+       va_start(args, format);
+       for (; *format; format++) {
+               switch (*format) {
+               case 'b':
+                       offset = va_arg(args, size_t);
+                       ptr8 = va_arg(args, uint8_t *);
+                       length = 1;
+                       break;
+               case 'w':
+                       offset = va_arg(args, size_t);
+                       ptr16 = va_arg(args, uint16_t *);
+                       length = 2;
+                       break;
+               case 'd':
+                       offset = va_arg(args, size_t);
+                       ptr32 = va_arg(args, uint32_t *);
+                       length = 4;
+                       break;
+               case 's':
+                       offset = va_arg(args, size_t);
+                       ptr8 = va_arg(args, uint8_t *);
+                       length = va_arg(args, uint32_t);
+                       break;
+               default:
+                       debug("Couldn't recognize format string\n");
+                       return -1;
+               }
+
+               if (offset + length > size)
+                       return -1;
+
+               switch (*format) {
+               case 'b':
+                       *ptr8 = str[offset];
+                       break;
+               case 'w':
+                       *ptr16 = get_unaligned_be16(str + offset);
+                       break;
+               case 'd':
+                       *ptr32 = get_unaligned_be32(str + offset);
+                       break;
+               case 's':
+                       memcpy(ptr8, str + offset, length);
+                       break;
+               }
+       }
+       va_end(args);
+
+       return 0;
+}
+
+/**
+ * Get TPM command size.
+ *
+ * @param command      byte string of TPM command
+ * @return command size of the TPM command
+ */
+static uint32_t tpm_command_size(const void *command)
+{
+       const size_t command_size_offset = 2;
+       return get_unaligned_be32(command + command_size_offset);
+}
+
+/**
+ * Get TPM response return code, which is one of TPM_RESULT values.
+ *
+ * @param response     byte string of TPM response
+ * @return return code of the TPM response
+ */
+static uint32_t tpm_return_code(const void *response)
+{
+       const size_t return_code_offset = 6;
+       return get_unaligned_be32(response + return_code_offset);
+}
+
+/**
+ * Send a TPM command and return response's return code, and optionally
+ * return response to caller.
+ *
+ * @param command      byte string of TPM command
+ * @param response     output buffer for TPM response, or NULL if the
+ *                     caller does not care about it
+ * @param size_ptr     output buffer size (input parameter) and TPM
+ *                     response length (output parameter); this parameter
+ *                     is a bidirectional
+ * @return return code of the TPM response
+ */
+static uint32_t tpm_sendrecv_command(const void *command,
+               void *response, size_t *size_ptr)
+{
+       uint8_t response_buffer[COMMAND_BUFFER_SIZE];
+       size_t response_length;
+       uint32_t err;
+
+       if (response) {
+               response_length = *size_ptr;
+       } else {
+               response = response_buffer;
+               response_length = sizeof(response_buffer);
+       }
+       err = tis_sendrecv(command, tpm_command_size(command),
+                       response, &response_length);
+       if (err)
+               return TPM_LIB_ERROR;
+       if (size_ptr)
+               *size_ptr = response_length;
+
+       return tpm_return_code(response);
+}
+
+uint32_t tpm_init(void)
+{
+       uint32_t err;
+
+       err = tis_init();
+       if (err)
+               return err;
+
+       return tis_open();
+}
+
+uint32_t tpm_startup(enum tpm_startup_type mode)
+{
+       const uint8_t command[12] = {
+               0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x99, 0x0, 0x0,
+       };
+       const size_t mode_offset = 10;
+       uint8_t buf[COMMAND_BUFFER_SIZE];
+
+       if (pack_byte_string(buf, sizeof(buf), "sw",
+                               0, command, sizeof(command),
+                               mode_offset, mode))
+               return TPM_LIB_ERROR;
+
+       return tpm_sendrecv_command(buf, NULL, NULL);
+}
+
+uint32_t tpm_self_test_full(void)
+{
+       const uint8_t command[10] = {
+               0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x50,
+       };
+       return tpm_sendrecv_command(command, NULL, NULL);
+}
+
+uint32_t tpm_continue_self_test(void)
+{
+       const uint8_t command[10] = {
+               0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x53,
+       };
+       return tpm_sendrecv_command(command, NULL, NULL);
+}
+
+uint32_t tpm_nv_define_space(uint32_t index, uint32_t perm, uint32_t size)
+{
+       const uint8_t command[101] = {
+               0x0, 0xc1,              /* TPM_TAG */
+               0x0, 0x0, 0x0, 0x65,    /* parameter size */
+               0x0, 0x0, 0x0, 0xcc,    /* TPM_COMMAND_CODE */
+               /* TPM_NV_DATA_PUBLIC->... */
+               0x0, 0x18,              /* ...->TPM_STRUCTURE_TAG */
+               0, 0, 0, 0,             /* ...->TPM_NV_INDEX */
+               /* TPM_NV_DATA_PUBLIC->TPM_PCR_INFO_SHORT */
+               0x0, 0x3,
+               0, 0, 0,
+               0x1f,
+               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+               /* TPM_NV_DATA_PUBLIC->TPM_PCR_INFO_SHORT */
+               0x0, 0x3,
+               0, 0, 0,
+               0x1f,
+               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+               /* TPM_NV_ATTRIBUTES->... */
+               0x0, 0x17,              /* ...->TPM_STRUCTURE_TAG */
+               0, 0, 0, 0,             /* ...->attributes */
+               /* End of TPM_NV_ATTRIBUTES */
+               0,                      /* bReadSTClear */
+               0,                      /* bWriteSTClear */
+               0,                      /* bWriteDefine */
+               0, 0, 0, 0,             /* size */
+       };
+       const size_t index_offset = 12;
+       const size_t perm_offset = 70;
+       const size_t size_offset = 77;
+       uint8_t buf[COMMAND_BUFFER_SIZE];
+
+       if (pack_byte_string(buf, sizeof(buf), "sddd",
+                               0, command, sizeof(command),
+                               index_offset, index,
+                               perm_offset, perm,
+                               size_offset, size))
+               return TPM_LIB_ERROR;
+
+       return tpm_sendrecv_command(buf, NULL, NULL);
+}
+
+uint32_t tpm_nv_read_value(uint32_t index, void *data, uint32_t count)
+{
+       const uint8_t command[22] = {
+               0x0, 0xc1, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0xcf,
+       };
+       const size_t index_offset = 10;
+       const size_t length_offset = 18;
+       const size_t data_size_offset = 10;
+       const size_t data_offset = 14;
+       uint8_t buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE];
+       size_t response_length = sizeof(response);
+       uint32_t data_size;
+       uint32_t err;
+
+       if (pack_byte_string(buf, sizeof(buf), "sdd",
+                               0, command, sizeof(command),
+                               index_offset, index,
+                               length_offset, count))
+               return TPM_LIB_ERROR;
+       err = tpm_sendrecv_command(buf, response, &response_length);
+       if (err)
+               return err;
+       if (unpack_byte_string(response, response_length, "d",
+                               data_size_offset, &data_size))
+               return TPM_LIB_ERROR;
+       if (data_size > count)
+               return TPM_LIB_ERROR;
+       if (unpack_byte_string(response, response_length, "s",
+                               data_offset, data, data_size))
+               return TPM_LIB_ERROR;
+
+       return 0;
+}
+
+uint32_t tpm_nv_write_value(uint32_t index, const void *data, uint32_t length)
+{
+       const uint8_t command[256] = {
+               0x0, 0xc1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcd,
+       };
+       const size_t command_size_offset = 2;
+       const size_t index_offset = 10;
+       const size_t length_offset = 18;
+       const size_t data_offset = 22;
+       const size_t write_info_size = 12;
+       const uint32_t total_length =
+               TPM_REQUEST_HEADER_LENGTH + write_info_size + length;
+       uint8_t buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE];
+       size_t response_length = sizeof(response);
+       uint32_t err;
+
+       if (pack_byte_string(buf, sizeof(buf), "sddds",
+                               0, command, sizeof(command),
+                               command_size_offset, total_length,
+                               index_offset, index,
+                               length_offset, length,
+                               data_offset, data, length))
+               return TPM_LIB_ERROR;
+       err = tpm_sendrecv_command(buf, response, &response_length);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+uint32_t tpm_extend(uint32_t index, const void *in_digest, void *out_digest)
+{
+       const uint8_t command[34] = {
+               0x0, 0xc1, 0x0, 0x0, 0x0, 0x22, 0x0, 0x0, 0x0, 0x14,
+       };
+       const size_t index_offset = 10;
+       const size_t in_digest_offset = 14;
+       const size_t out_digest_offset = 10;
+       uint8_t buf[COMMAND_BUFFER_SIZE];
+       uint8_t response[TPM_RESPONSE_HEADER_LENGTH + PCR_DIGEST_LENGTH];
+       size_t response_length = sizeof(response);
+       uint32_t err;
+
+       if (pack_byte_string(buf, sizeof(buf), "sds",
+                               0, command, sizeof(command),
+                               index_offset, index,
+                               in_digest_offset, in_digest,
+                               PCR_DIGEST_LENGTH))
+               return TPM_LIB_ERROR;
+       err = tpm_sendrecv_command(buf, response, &response_length);
+       if (err)
+               return err;
+
+       if (unpack_byte_string(response, response_length, "s",
+                               out_digest_offset, out_digest,
+                               PCR_DIGEST_LENGTH))
+               return TPM_LIB_ERROR;
+
+       return 0;
+}
+
+uint32_t tpm_pcr_read(uint32_t index, void *data, size_t count)
+{
+       const uint8_t command[14] = {
+               0x0, 0xc1, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x0, 0x15,
+       };
+       const size_t index_offset = 10;
+       const size_t out_digest_offset = 10;
+       uint8_t buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE];
+       size_t response_length = sizeof(response);
+       uint32_t err;
+
+       if (count < PCR_DIGEST_LENGTH)
+               return TPM_LIB_ERROR;
+
+       if (pack_byte_string(buf, sizeof(buf), "sd",
+                               0, command, sizeof(command),
+                               index_offset, index))
+               return TPM_LIB_ERROR;
+       err = tpm_sendrecv_command(buf, response, &response_length);
+       if (err)
+               return err;
+       if (unpack_byte_string(response, response_length, "s",
+                               out_digest_offset, data, PCR_DIGEST_LENGTH))
+               return TPM_LIB_ERROR;
+
+       return 0;
+}
+
+uint32_t tpm_tsc_physical_presence(uint16_t presence)
+{
+       const uint8_t command[12] = {
+               0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x40, 0x0, 0x0, 0xa, 0x0, 0x0,
+       };
+       const size_t presence_offset = 10;
+       uint8_t buf[COMMAND_BUFFER_SIZE];
+
+       if (pack_byte_string(buf, sizeof(buf), "sw",
+                               0, command, sizeof(command),
+                               presence_offset, presence))
+               return TPM_LIB_ERROR;
+
+       return tpm_sendrecv_command(buf, NULL, NULL);
+}
+
+uint32_t tpm_read_pubek(void *data, size_t count)
+{
+       const uint8_t command[30] = {
+               0x0, 0xc1, 0x0, 0x0, 0x0, 0x1e, 0x0, 0x0, 0x0, 0x7c,
+       };
+       const size_t response_size_offset = 2;
+       const size_t data_offset = 10;
+       const size_t header_and_checksum_size = TPM_RESPONSE_HEADER_LENGTH + 20;
+       uint8_t response[COMMAND_BUFFER_SIZE + TPM_PUBEK_SIZE];
+       size_t response_length = sizeof(response);
+       uint32_t data_size;
+       uint32_t err;
+
+       err = tpm_sendrecv_command(command, response, &response_length);
+       if (err)
+               return err;
+       if (unpack_byte_string(response, response_length, "d",
+                               response_size_offset, &data_size))
+               return TPM_LIB_ERROR;
+       if (data_size < header_and_checksum_size)
+               return TPM_LIB_ERROR;
+       data_size -= header_and_checksum_size;
+       if (data_size > count)
+               return TPM_LIB_ERROR;
+       if (unpack_byte_string(response, response_length, "s",
+                               data_offset, data, data_size))
+               return TPM_LIB_ERROR;
+
+       return 0;
+}
+
+uint32_t tpm_force_clear(void)
+{
+       const uint8_t command[10] = {
+               0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x5d,
+       };
+
+       return tpm_sendrecv_command(command, NULL, NULL);
+}
+
+uint32_t tpm_physical_enable(void)
+{
+       const uint8_t command[10] = {
+               0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x6f,
+       };
+
+       return tpm_sendrecv_command(command, NULL, NULL);
+}
+
+uint32_t tpm_physical_disable(void)
+{
+       const uint8_t command[10] = {
+               0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x70,
+       };
+
+       return tpm_sendrecv_command(command, NULL, NULL);
+}
+
+uint32_t tpm_physical_set_deactivated(uint8_t state)
+{
+       const uint8_t command[11] = {
+               0x0, 0xc1, 0x0, 0x0, 0x0, 0xb, 0x0, 0x0, 0x0, 0x72,
+       };
+       const size_t state_offset = 10;
+       uint8_t buf[COMMAND_BUFFER_SIZE];
+
+       if (pack_byte_string(buf, sizeof(buf), "sb",
+                               0, command, sizeof(command),
+                               state_offset, state))
+               return TPM_LIB_ERROR;
+
+       return tpm_sendrecv_command(buf, NULL, NULL);
+}
+
+uint32_t tpm_get_capability(uint32_t cap_area, uint32_t sub_cap,
+               void *cap, size_t count)
+{
+       const uint8_t command[22] = {
+               0x0, 0xc1,              /* TPM_TAG */
+               0x0, 0x0, 0x0, 0x16,    /* parameter size */
+               0x0, 0x0, 0x0, 0x65,    /* TPM_COMMAND_CODE */
+               0x0, 0x0, 0x0, 0x0,     /* TPM_CAPABILITY_AREA */
+               0x0, 0x0, 0x0, 0x4,     /* subcap size */
+               0x0, 0x0, 0x0, 0x0,     /* subcap value */
+       };
+       const size_t cap_area_offset = 10;
+       const size_t sub_cap_offset = 18;
+       const size_t cap_offset = 14;
+       const size_t cap_size_offset = 10;
+       uint8_t buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE];
+       size_t response_length = sizeof(response);
+       uint32_t cap_size;
+       uint32_t err;
+
+       if (pack_byte_string(buf, sizeof(buf), "sdd",
+                               0, command, sizeof(command),
+                               cap_area_offset, cap_area,
+                               sub_cap_offset, sub_cap))
+               return TPM_LIB_ERROR;
+       err = tpm_sendrecv_command(buf, response, &response_length);
+       if (err)
+               return err;
+       if (unpack_byte_string(response, response_length, "d",
+                               cap_size_offset, &cap_size))
+               return TPM_LIB_ERROR;
+       if (cap_size > response_length || cap_size > count)
+               return TPM_LIB_ERROR;
+       if (unpack_byte_string(response, response_length, "s",
+                               cap_offset, cap, cap_size))
+               return TPM_LIB_ERROR;
+
+       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 */