xref: /rk3399_rockchip-uboot/lib/tpm.c (revision 1a4596601fd395f3afb8f82f3f840c5e00bdd57a)
18732b070SChe-liang Chiou /*
28732b070SChe-liang Chiou  * Copyright (c) 2013 The Chromium OS Authors.
3be6c1529SReinhard Pfau  * Coypright (c) 2013 Guntermann & Drunck GmbH
48732b070SChe-liang Chiou  *
5*1a459660SWolfgang Denk  * SPDX-License-Identifier:	GPL-2.0+
68732b070SChe-liang Chiou  */
78732b070SChe-liang Chiou 
88732b070SChe-liang Chiou #include <common.h>
98732b070SChe-liang Chiou #include <stdarg.h>
10be6c1529SReinhard Pfau #include <sha1.h>
118732b070SChe-liang Chiou #include <tpm.h>
128732b070SChe-liang Chiou #include <asm/unaligned.h>
138732b070SChe-liang Chiou 
148732b070SChe-liang Chiou /* Internal error of TPM command library */
158732b070SChe-liang Chiou #define TPM_LIB_ERROR	((uint32_t)~0u)
168732b070SChe-liang Chiou 
178732b070SChe-liang Chiou /* Useful constants */
188732b070SChe-liang Chiou enum {
198732b070SChe-liang Chiou 	COMMAND_BUFFER_SIZE		= 256,
208732b070SChe-liang Chiou 	TPM_PUBEK_SIZE			= 256,
218732b070SChe-liang Chiou 	TPM_REQUEST_HEADER_LENGTH	= 10,
228732b070SChe-liang Chiou 	TPM_RESPONSE_HEADER_LENGTH	= 10,
238732b070SChe-liang Chiou 	PCR_DIGEST_LENGTH		= 20,
24be6c1529SReinhard Pfau 	DIGEST_LENGTH			= 20,
25be6c1529SReinhard Pfau 	TPM_REQUEST_AUTH_LENGTH		= 45,
26be6c1529SReinhard Pfau 	TPM_RESPONSE_AUTH_LENGTH	= 41,
27be6c1529SReinhard Pfau 	/* some max lengths, valid for RSA keys <= 2048 bits */
28be6c1529SReinhard Pfau 	TPM_KEY12_MAX_LENGTH		= 618,
29be6c1529SReinhard Pfau 	TPM_PUBKEY_MAX_LENGTH		= 288,
308732b070SChe-liang Chiou };
318732b070SChe-liang Chiou 
32be6c1529SReinhard Pfau #ifdef CONFIG_TPM_AUTH_SESSIONS
33be6c1529SReinhard Pfau 
34be6c1529SReinhard Pfau #ifndef CONFIG_SHA1
35be6c1529SReinhard Pfau #error "TPM_AUTH_SESSIONS require SHA1 to be configured, too"
36be6c1529SReinhard Pfau #endif /* !CONFIG_SHA1 */
37be6c1529SReinhard Pfau 
38be6c1529SReinhard Pfau struct session_data {
39be6c1529SReinhard Pfau 	int		valid;
40be6c1529SReinhard Pfau 	uint32_t	handle;
41be6c1529SReinhard Pfau 	uint8_t		nonce_even[DIGEST_LENGTH];
42be6c1529SReinhard Pfau 	uint8_t		nonce_odd[DIGEST_LENGTH];
43be6c1529SReinhard Pfau };
44be6c1529SReinhard Pfau 
45be6c1529SReinhard Pfau static struct session_data oiap_session = {0, };
46be6c1529SReinhard Pfau 
47be6c1529SReinhard Pfau #endif /* CONFIG_TPM_AUTH_SESSIONS */
48be6c1529SReinhard Pfau 
498732b070SChe-liang Chiou /**
508732b070SChe-liang Chiou  * Pack data into a byte string.  The data types are specified in
518732b070SChe-liang Chiou  * the format string: 'b' means unsigned byte, 'w' unsigned word,
528732b070SChe-liang Chiou  * 'd' unsigned double word, and 's' byte string.  The data are a
538732b070SChe-liang Chiou  * series of offsets and values (for type byte string there are also
548732b070SChe-liang Chiou  * lengths).  The data values are packed into the byte string
558732b070SChe-liang Chiou  * sequentially, and so a latter value could over-write a former
568732b070SChe-liang Chiou  * value.
578732b070SChe-liang Chiou  *
588732b070SChe-liang Chiou  * @param str		output string
598732b070SChe-liang Chiou  * @param size		size of output string
608732b070SChe-liang Chiou  * @param format	format string
618732b070SChe-liang Chiou  * @param ...		data points
628732b070SChe-liang Chiou  * @return 0 on success, non-0 on error
638732b070SChe-liang Chiou  */
648732b070SChe-liang Chiou int pack_byte_string(uint8_t *str, size_t size, const char *format, ...)
658732b070SChe-liang Chiou {
668732b070SChe-liang Chiou 	va_list args;
678732b070SChe-liang Chiou 	size_t offset = 0, length = 0;
688732b070SChe-liang Chiou 	uint8_t *data = NULL;
698732b070SChe-liang Chiou 	uint32_t value = 0;
708732b070SChe-liang Chiou 
718732b070SChe-liang Chiou 	va_start(args, format);
728732b070SChe-liang Chiou 	for (; *format; format++) {
738732b070SChe-liang Chiou 		switch (*format) {
748732b070SChe-liang Chiou 		case 'b':
758732b070SChe-liang Chiou 			offset = va_arg(args, size_t);
768732b070SChe-liang Chiou 			value = va_arg(args, int);
778732b070SChe-liang Chiou 			length = 1;
788732b070SChe-liang Chiou 			break;
798732b070SChe-liang Chiou 		case 'w':
808732b070SChe-liang Chiou 			offset = va_arg(args, size_t);
818732b070SChe-liang Chiou 			value = va_arg(args, int);
828732b070SChe-liang Chiou 			length = 2;
838732b070SChe-liang Chiou 			break;
848732b070SChe-liang Chiou 		case 'd':
858732b070SChe-liang Chiou 			offset = va_arg(args, size_t);
868732b070SChe-liang Chiou 			value = va_arg(args, uint32_t);
878732b070SChe-liang Chiou 			length = 4;
888732b070SChe-liang Chiou 			break;
898732b070SChe-liang Chiou 		case 's':
908732b070SChe-liang Chiou 			offset = va_arg(args, size_t);
918732b070SChe-liang Chiou 			data = va_arg(args, uint8_t *);
928732b070SChe-liang Chiou 			length = va_arg(args, uint32_t);
938732b070SChe-liang Chiou 			break;
948732b070SChe-liang Chiou 		default:
958732b070SChe-liang Chiou 			debug("Couldn't recognize format string\n");
968732b070SChe-liang Chiou 			return -1;
978732b070SChe-liang Chiou 		}
988732b070SChe-liang Chiou 
998732b070SChe-liang Chiou 		if (offset + length > size)
1008732b070SChe-liang Chiou 			return -1;
1018732b070SChe-liang Chiou 
1028732b070SChe-liang Chiou 		switch (*format) {
1038732b070SChe-liang Chiou 		case 'b':
1048732b070SChe-liang Chiou 			str[offset] = value;
1058732b070SChe-liang Chiou 			break;
1068732b070SChe-liang Chiou 		case 'w':
1078732b070SChe-liang Chiou 			put_unaligned_be16(value, str + offset);
1088732b070SChe-liang Chiou 			break;
1098732b070SChe-liang Chiou 		case 'd':
1108732b070SChe-liang Chiou 			put_unaligned_be32(value, str + offset);
1118732b070SChe-liang Chiou 			break;
1128732b070SChe-liang Chiou 		case 's':
1138732b070SChe-liang Chiou 			memcpy(str + offset, data, length);
1148732b070SChe-liang Chiou 			break;
1158732b070SChe-liang Chiou 		}
1168732b070SChe-liang Chiou 	}
1178732b070SChe-liang Chiou 	va_end(args);
1188732b070SChe-liang Chiou 
1198732b070SChe-liang Chiou 	return 0;
1208732b070SChe-liang Chiou }
1218732b070SChe-liang Chiou 
1228732b070SChe-liang Chiou /**
1238732b070SChe-liang Chiou  * Unpack data from a byte string.  The data types are specified in
1248732b070SChe-liang Chiou  * the format string: 'b' means unsigned byte, 'w' unsigned word,
1258732b070SChe-liang Chiou  * 'd' unsigned double word, and 's' byte string.  The data are a
1268732b070SChe-liang Chiou  * series of offsets and pointers (for type byte string there are also
1278732b070SChe-liang Chiou  * lengths).
1288732b070SChe-liang Chiou  *
1298732b070SChe-liang Chiou  * @param str		output string
1308732b070SChe-liang Chiou  * @param size		size of output string
1318732b070SChe-liang Chiou  * @param format	format string
1328732b070SChe-liang Chiou  * @param ...		data points
1338732b070SChe-liang Chiou  * @return 0 on success, non-0 on error
1348732b070SChe-liang Chiou  */
1358732b070SChe-liang Chiou int unpack_byte_string(const uint8_t *str, size_t size, const char *format, ...)
1368732b070SChe-liang Chiou {
1378732b070SChe-liang Chiou 	va_list args;
1388732b070SChe-liang Chiou 	size_t offset = 0, length = 0;
1398732b070SChe-liang Chiou 	uint8_t *ptr8 = NULL;
1408732b070SChe-liang Chiou 	uint16_t *ptr16 = NULL;
1418732b070SChe-liang Chiou 	uint32_t *ptr32 = NULL;
1428732b070SChe-liang Chiou 
1438732b070SChe-liang Chiou 	va_start(args, format);
1448732b070SChe-liang Chiou 	for (; *format; format++) {
1458732b070SChe-liang Chiou 		switch (*format) {
1468732b070SChe-liang Chiou 		case 'b':
1478732b070SChe-liang Chiou 			offset = va_arg(args, size_t);
1488732b070SChe-liang Chiou 			ptr8 = va_arg(args, uint8_t *);
1498732b070SChe-liang Chiou 			length = 1;
1508732b070SChe-liang Chiou 			break;
1518732b070SChe-liang Chiou 		case 'w':
1528732b070SChe-liang Chiou 			offset = va_arg(args, size_t);
1538732b070SChe-liang Chiou 			ptr16 = va_arg(args, uint16_t *);
1548732b070SChe-liang Chiou 			length = 2;
1558732b070SChe-liang Chiou 			break;
1568732b070SChe-liang Chiou 		case 'd':
1578732b070SChe-liang Chiou 			offset = va_arg(args, size_t);
1588732b070SChe-liang Chiou 			ptr32 = va_arg(args, uint32_t *);
1598732b070SChe-liang Chiou 			length = 4;
1608732b070SChe-liang Chiou 			break;
1618732b070SChe-liang Chiou 		case 's':
1628732b070SChe-liang Chiou 			offset = va_arg(args, size_t);
1638732b070SChe-liang Chiou 			ptr8 = va_arg(args, uint8_t *);
1648732b070SChe-liang Chiou 			length = va_arg(args, uint32_t);
1658732b070SChe-liang Chiou 			break;
1668732b070SChe-liang Chiou 		default:
1678732b070SChe-liang Chiou 			debug("Couldn't recognize format string\n");
1688732b070SChe-liang Chiou 			return -1;
1698732b070SChe-liang Chiou 		}
1708732b070SChe-liang Chiou 
1718732b070SChe-liang Chiou 		if (offset + length > size)
1728732b070SChe-liang Chiou 			return -1;
1738732b070SChe-liang Chiou 
1748732b070SChe-liang Chiou 		switch (*format) {
1758732b070SChe-liang Chiou 		case 'b':
1768732b070SChe-liang Chiou 			*ptr8 = str[offset];
1778732b070SChe-liang Chiou 			break;
1788732b070SChe-liang Chiou 		case 'w':
1798732b070SChe-liang Chiou 			*ptr16 = get_unaligned_be16(str + offset);
1808732b070SChe-liang Chiou 			break;
1818732b070SChe-liang Chiou 		case 'd':
1828732b070SChe-liang Chiou 			*ptr32 = get_unaligned_be32(str + offset);
1838732b070SChe-liang Chiou 			break;
1848732b070SChe-liang Chiou 		case 's':
1858732b070SChe-liang Chiou 			memcpy(ptr8, str + offset, length);
1868732b070SChe-liang Chiou 			break;
1878732b070SChe-liang Chiou 		}
1888732b070SChe-liang Chiou 	}
1898732b070SChe-liang Chiou 	va_end(args);
1908732b070SChe-liang Chiou 
1918732b070SChe-liang Chiou 	return 0;
1928732b070SChe-liang Chiou }
1938732b070SChe-liang Chiou 
1948732b070SChe-liang Chiou /**
1958732b070SChe-liang Chiou  * Get TPM command size.
1968732b070SChe-liang Chiou  *
1978732b070SChe-liang Chiou  * @param command	byte string of TPM command
1988732b070SChe-liang Chiou  * @return command size of the TPM command
1998732b070SChe-liang Chiou  */
2008732b070SChe-liang Chiou static uint32_t tpm_command_size(const void *command)
2018732b070SChe-liang Chiou {
2028732b070SChe-liang Chiou 	const size_t command_size_offset = 2;
2038732b070SChe-liang Chiou 	return get_unaligned_be32(command + command_size_offset);
2048732b070SChe-liang Chiou }
2058732b070SChe-liang Chiou 
2068732b070SChe-liang Chiou /**
2078732b070SChe-liang Chiou  * Get TPM response return code, which is one of TPM_RESULT values.
2088732b070SChe-liang Chiou  *
2098732b070SChe-liang Chiou  * @param response	byte string of TPM response
2108732b070SChe-liang Chiou  * @return return code of the TPM response
2118732b070SChe-liang Chiou  */
2128732b070SChe-liang Chiou static uint32_t tpm_return_code(const void *response)
2138732b070SChe-liang Chiou {
2148732b070SChe-liang Chiou 	const size_t return_code_offset = 6;
2158732b070SChe-liang Chiou 	return get_unaligned_be32(response + return_code_offset);
2168732b070SChe-liang Chiou }
2178732b070SChe-liang Chiou 
2188732b070SChe-liang Chiou /**
2198732b070SChe-liang Chiou  * Send a TPM command and return response's return code, and optionally
2208732b070SChe-liang Chiou  * return response to caller.
2218732b070SChe-liang Chiou  *
2228732b070SChe-liang Chiou  * @param command	byte string of TPM command
2238732b070SChe-liang Chiou  * @param response	output buffer for TPM response, or NULL if the
2248732b070SChe-liang Chiou  *			caller does not care about it
2258732b070SChe-liang Chiou  * @param size_ptr	output buffer size (input parameter) and TPM
2268732b070SChe-liang Chiou  *			response length (output parameter); this parameter
2278732b070SChe-liang Chiou  *			is a bidirectional
2288732b070SChe-liang Chiou  * @return return code of the TPM response
2298732b070SChe-liang Chiou  */
2308732b070SChe-liang Chiou static uint32_t tpm_sendrecv_command(const void *command,
2318732b070SChe-liang Chiou 		void *response, size_t *size_ptr)
2328732b070SChe-liang Chiou {
2338732b070SChe-liang Chiou 	uint8_t response_buffer[COMMAND_BUFFER_SIZE];
2348732b070SChe-liang Chiou 	size_t response_length;
2358732b070SChe-liang Chiou 	uint32_t err;
2368732b070SChe-liang Chiou 
2378732b070SChe-liang Chiou 	if (response) {
2388732b070SChe-liang Chiou 		response_length = *size_ptr;
2398732b070SChe-liang Chiou 	} else {
2408732b070SChe-liang Chiou 		response = response_buffer;
2418732b070SChe-liang Chiou 		response_length = sizeof(response_buffer);
2428732b070SChe-liang Chiou 	}
2438732b070SChe-liang Chiou 	err = tis_sendrecv(command, tpm_command_size(command),
2448732b070SChe-liang Chiou 			response, &response_length);
2458732b070SChe-liang Chiou 	if (err)
2468732b070SChe-liang Chiou 		return TPM_LIB_ERROR;
247be6c1529SReinhard Pfau 	if (size_ptr)
2488732b070SChe-liang Chiou 		*size_ptr = response_length;
2498732b070SChe-liang Chiou 
2508732b070SChe-liang Chiou 	return tpm_return_code(response);
2518732b070SChe-liang Chiou }
2528732b070SChe-liang Chiou 
2538732b070SChe-liang Chiou uint32_t tpm_init(void)
2548732b070SChe-liang Chiou {
2558732b070SChe-liang Chiou 	uint32_t err;
2568732b070SChe-liang Chiou 
2578732b070SChe-liang Chiou 	err = tis_init();
2588732b070SChe-liang Chiou 	if (err)
2598732b070SChe-liang Chiou 		return err;
2608732b070SChe-liang Chiou 
2618732b070SChe-liang Chiou 	return tis_open();
2628732b070SChe-liang Chiou }
2638732b070SChe-liang Chiou 
2648732b070SChe-liang Chiou uint32_t tpm_startup(enum tpm_startup_type mode)
2658732b070SChe-liang Chiou {
2668732b070SChe-liang Chiou 	const uint8_t command[12] = {
2678732b070SChe-liang Chiou 		0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x99, 0x0, 0x0,
2688732b070SChe-liang Chiou 	};
2698732b070SChe-liang Chiou 	const size_t mode_offset = 10;
2708732b070SChe-liang Chiou 	uint8_t buf[COMMAND_BUFFER_SIZE];
2718732b070SChe-liang Chiou 
2728732b070SChe-liang Chiou 	if (pack_byte_string(buf, sizeof(buf), "sw",
2738732b070SChe-liang Chiou 				0, command, sizeof(command),
2748732b070SChe-liang Chiou 				mode_offset, mode))
2758732b070SChe-liang Chiou 		return TPM_LIB_ERROR;
2768732b070SChe-liang Chiou 
2778732b070SChe-liang Chiou 	return tpm_sendrecv_command(buf, NULL, NULL);
2788732b070SChe-liang Chiou }
2798732b070SChe-liang Chiou 
2808732b070SChe-liang Chiou uint32_t tpm_self_test_full(void)
2818732b070SChe-liang Chiou {
2828732b070SChe-liang Chiou 	const uint8_t command[10] = {
2838732b070SChe-liang Chiou 		0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x50,
2848732b070SChe-liang Chiou 	};
2858732b070SChe-liang Chiou 	return tpm_sendrecv_command(command, NULL, NULL);
2868732b070SChe-liang Chiou }
2878732b070SChe-liang Chiou 
2888732b070SChe-liang Chiou uint32_t tpm_continue_self_test(void)
2898732b070SChe-liang Chiou {
2908732b070SChe-liang Chiou 	const uint8_t command[10] = {
2918732b070SChe-liang Chiou 		0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x53,
2928732b070SChe-liang Chiou 	};
2938732b070SChe-liang Chiou 	return tpm_sendrecv_command(command, NULL, NULL);
2948732b070SChe-liang Chiou }
2958732b070SChe-liang Chiou 
2968732b070SChe-liang Chiou uint32_t tpm_nv_define_space(uint32_t index, uint32_t perm, uint32_t size)
2978732b070SChe-liang Chiou {
2988732b070SChe-liang Chiou 	const uint8_t command[101] = {
2998732b070SChe-liang Chiou 		0x0, 0xc1,		/* TPM_TAG */
3008732b070SChe-liang Chiou 		0x0, 0x0, 0x0, 0x65,	/* parameter size */
3018732b070SChe-liang Chiou 		0x0, 0x0, 0x0, 0xcc,	/* TPM_COMMAND_CODE */
3028732b070SChe-liang Chiou 		/* TPM_NV_DATA_PUBLIC->... */
3038732b070SChe-liang Chiou 		0x0, 0x18,		/* ...->TPM_STRUCTURE_TAG */
3048732b070SChe-liang Chiou 		0, 0, 0, 0,		/* ...->TPM_NV_INDEX */
3058732b070SChe-liang Chiou 		/* TPM_NV_DATA_PUBLIC->TPM_PCR_INFO_SHORT */
3068732b070SChe-liang Chiou 		0x0, 0x3,
3078732b070SChe-liang Chiou 		0, 0, 0,
3088732b070SChe-liang Chiou 		0x1f,
3098732b070SChe-liang Chiou 		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3108732b070SChe-liang Chiou 		/* TPM_NV_DATA_PUBLIC->TPM_PCR_INFO_SHORT */
3118732b070SChe-liang Chiou 		0x0, 0x3,
3128732b070SChe-liang Chiou 		0, 0, 0,
3138732b070SChe-liang Chiou 		0x1f,
3148732b070SChe-liang Chiou 		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3158732b070SChe-liang Chiou 		/* TPM_NV_ATTRIBUTES->... */
3168732b070SChe-liang Chiou 		0x0, 0x17,		/* ...->TPM_STRUCTURE_TAG */
3178732b070SChe-liang Chiou 		0, 0, 0, 0,		/* ...->attributes */
3188732b070SChe-liang Chiou 		/* End of TPM_NV_ATTRIBUTES */
3198732b070SChe-liang Chiou 		0,			/* bReadSTClear */
3208732b070SChe-liang Chiou 		0,			/* bWriteSTClear */
3218732b070SChe-liang Chiou 		0,			/* bWriteDefine */
3228732b070SChe-liang Chiou 		0, 0, 0, 0,		/* size */
3238732b070SChe-liang Chiou 	};
3248732b070SChe-liang Chiou 	const size_t index_offset = 12;
3258732b070SChe-liang Chiou 	const size_t perm_offset = 70;
3268732b070SChe-liang Chiou 	const size_t size_offset = 77;
3278732b070SChe-liang Chiou 	uint8_t buf[COMMAND_BUFFER_SIZE];
3288732b070SChe-liang Chiou 
3298732b070SChe-liang Chiou 	if (pack_byte_string(buf, sizeof(buf), "sddd",
3308732b070SChe-liang Chiou 				0, command, sizeof(command),
3318732b070SChe-liang Chiou 				index_offset, index,
3328732b070SChe-liang Chiou 				perm_offset, perm,
3338732b070SChe-liang Chiou 				size_offset, size))
3348732b070SChe-liang Chiou 		return TPM_LIB_ERROR;
3358732b070SChe-liang Chiou 
3368732b070SChe-liang Chiou 	return tpm_sendrecv_command(buf, NULL, NULL);
3378732b070SChe-liang Chiou }
3388732b070SChe-liang Chiou 
3398732b070SChe-liang Chiou uint32_t tpm_nv_read_value(uint32_t index, void *data, uint32_t count)
3408732b070SChe-liang Chiou {
3418732b070SChe-liang Chiou 	const uint8_t command[22] = {
3428732b070SChe-liang Chiou 		0x0, 0xc1, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0xcf,
3438732b070SChe-liang Chiou 	};
3448732b070SChe-liang Chiou 	const size_t index_offset = 10;
3458732b070SChe-liang Chiou 	const size_t length_offset = 18;
3468732b070SChe-liang Chiou 	const size_t data_size_offset = 10;
3478732b070SChe-liang Chiou 	const size_t data_offset = 14;
3488732b070SChe-liang Chiou 	uint8_t buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE];
3498732b070SChe-liang Chiou 	size_t response_length = sizeof(response);
3508732b070SChe-liang Chiou 	uint32_t data_size;
3518732b070SChe-liang Chiou 	uint32_t err;
3528732b070SChe-liang Chiou 
3538732b070SChe-liang Chiou 	if (pack_byte_string(buf, sizeof(buf), "sdd",
3548732b070SChe-liang Chiou 				0, command, sizeof(command),
3558732b070SChe-liang Chiou 				index_offset, index,
3568732b070SChe-liang Chiou 				length_offset, count))
3578732b070SChe-liang Chiou 		return TPM_LIB_ERROR;
3588732b070SChe-liang Chiou 	err = tpm_sendrecv_command(buf, response, &response_length);
3598732b070SChe-liang Chiou 	if (err)
3608732b070SChe-liang Chiou 		return err;
3618732b070SChe-liang Chiou 	if (unpack_byte_string(response, response_length, "d",
3628732b070SChe-liang Chiou 				data_size_offset, &data_size))
3638732b070SChe-liang Chiou 		return TPM_LIB_ERROR;
3648732b070SChe-liang Chiou 	if (data_size > count)
3658732b070SChe-liang Chiou 		return TPM_LIB_ERROR;
3668732b070SChe-liang Chiou 	if (unpack_byte_string(response, response_length, "s",
3678732b070SChe-liang Chiou 				data_offset, data, data_size))
3688732b070SChe-liang Chiou 		return TPM_LIB_ERROR;
3698732b070SChe-liang Chiou 
3708732b070SChe-liang Chiou 	return 0;
3718732b070SChe-liang Chiou }
3728732b070SChe-liang Chiou 
3738732b070SChe-liang Chiou uint32_t tpm_nv_write_value(uint32_t index, const void *data, uint32_t length)
3748732b070SChe-liang Chiou {
3758732b070SChe-liang Chiou 	const uint8_t command[256] = {
3768732b070SChe-liang Chiou 		0x0, 0xc1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcd,
3778732b070SChe-liang Chiou 	};
3788732b070SChe-liang Chiou 	const size_t command_size_offset = 2;
3798732b070SChe-liang Chiou 	const size_t index_offset = 10;
3808732b070SChe-liang Chiou 	const size_t length_offset = 18;
3818732b070SChe-liang Chiou 	const size_t data_offset = 22;
3828732b070SChe-liang Chiou 	const size_t write_info_size = 12;
3838732b070SChe-liang Chiou 	const uint32_t total_length =
3848732b070SChe-liang Chiou 		TPM_REQUEST_HEADER_LENGTH + write_info_size + length;
3858732b070SChe-liang Chiou 	uint8_t buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE];
3868732b070SChe-liang Chiou 	size_t response_length = sizeof(response);
3878732b070SChe-liang Chiou 	uint32_t err;
3888732b070SChe-liang Chiou 
3898732b070SChe-liang Chiou 	if (pack_byte_string(buf, sizeof(buf), "sddds",
3908732b070SChe-liang Chiou 				0, command, sizeof(command),
3918732b070SChe-liang Chiou 				command_size_offset, total_length,
3928732b070SChe-liang Chiou 				index_offset, index,
3938732b070SChe-liang Chiou 				length_offset, length,
3948732b070SChe-liang Chiou 				data_offset, data, length))
3958732b070SChe-liang Chiou 		return TPM_LIB_ERROR;
3968732b070SChe-liang Chiou 	err = tpm_sendrecv_command(buf, response, &response_length);
3978732b070SChe-liang Chiou 	if (err)
3988732b070SChe-liang Chiou 		return err;
3998732b070SChe-liang Chiou 
4008732b070SChe-liang Chiou 	return 0;
4018732b070SChe-liang Chiou }
4028732b070SChe-liang Chiou 
4038732b070SChe-liang Chiou uint32_t tpm_extend(uint32_t index, const void *in_digest, void *out_digest)
4048732b070SChe-liang Chiou {
4058732b070SChe-liang Chiou 	const uint8_t command[34] = {
4068732b070SChe-liang Chiou 		0x0, 0xc1, 0x0, 0x0, 0x0, 0x22, 0x0, 0x0, 0x0, 0x14,
4078732b070SChe-liang Chiou 	};
4088732b070SChe-liang Chiou 	const size_t index_offset = 10;
4098732b070SChe-liang Chiou 	const size_t in_digest_offset = 14;
4108732b070SChe-liang Chiou 	const size_t out_digest_offset = 10;
4118732b070SChe-liang Chiou 	uint8_t buf[COMMAND_BUFFER_SIZE];
4128732b070SChe-liang Chiou 	uint8_t response[TPM_RESPONSE_HEADER_LENGTH + PCR_DIGEST_LENGTH];
4138732b070SChe-liang Chiou 	size_t response_length = sizeof(response);
4148732b070SChe-liang Chiou 	uint32_t err;
4158732b070SChe-liang Chiou 
4168732b070SChe-liang Chiou 	if (pack_byte_string(buf, sizeof(buf), "sds",
4178732b070SChe-liang Chiou 				0, command, sizeof(command),
4188732b070SChe-liang Chiou 				index_offset, index,
4198732b070SChe-liang Chiou 				in_digest_offset, in_digest,
4208732b070SChe-liang Chiou 				PCR_DIGEST_LENGTH))
4218732b070SChe-liang Chiou 		return TPM_LIB_ERROR;
4228732b070SChe-liang Chiou 	err = tpm_sendrecv_command(buf, response, &response_length);
4238732b070SChe-liang Chiou 	if (err)
4248732b070SChe-liang Chiou 		return err;
4258732b070SChe-liang Chiou 
4268732b070SChe-liang Chiou 	if (unpack_byte_string(response, response_length, "s",
4278732b070SChe-liang Chiou 				out_digest_offset, out_digest,
4288732b070SChe-liang Chiou 				PCR_DIGEST_LENGTH))
4298732b070SChe-liang Chiou 		return TPM_LIB_ERROR;
4308732b070SChe-liang Chiou 
4318732b070SChe-liang Chiou 	return 0;
4328732b070SChe-liang Chiou }
4338732b070SChe-liang Chiou 
4348732b070SChe-liang Chiou uint32_t tpm_pcr_read(uint32_t index, void *data, size_t count)
4358732b070SChe-liang Chiou {
4368732b070SChe-liang Chiou 	const uint8_t command[14] = {
4378732b070SChe-liang Chiou 		0x0, 0xc1, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x0, 0x15,
4388732b070SChe-liang Chiou 	};
4398732b070SChe-liang Chiou 	const size_t index_offset = 10;
4408732b070SChe-liang Chiou 	const size_t out_digest_offset = 10;
4418732b070SChe-liang Chiou 	uint8_t buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE];
4428732b070SChe-liang Chiou 	size_t response_length = sizeof(response);
4438732b070SChe-liang Chiou 	uint32_t err;
4448732b070SChe-liang Chiou 
4458732b070SChe-liang Chiou 	if (count < PCR_DIGEST_LENGTH)
4468732b070SChe-liang Chiou 		return TPM_LIB_ERROR;
4478732b070SChe-liang Chiou 
4488732b070SChe-liang Chiou 	if (pack_byte_string(buf, sizeof(buf), "sd",
4498732b070SChe-liang Chiou 				0, command, sizeof(command),
4508732b070SChe-liang Chiou 				index_offset, index))
4518732b070SChe-liang Chiou 		return TPM_LIB_ERROR;
4528732b070SChe-liang Chiou 	err = tpm_sendrecv_command(buf, response, &response_length);
4538732b070SChe-liang Chiou 	if (err)
4548732b070SChe-liang Chiou 		return err;
4558732b070SChe-liang Chiou 	if (unpack_byte_string(response, response_length, "s",
4568732b070SChe-liang Chiou 				out_digest_offset, data, PCR_DIGEST_LENGTH))
4578732b070SChe-liang Chiou 		return TPM_LIB_ERROR;
4588732b070SChe-liang Chiou 
4598732b070SChe-liang Chiou 	return 0;
4608732b070SChe-liang Chiou }
4618732b070SChe-liang Chiou 
4628732b070SChe-liang Chiou uint32_t tpm_tsc_physical_presence(uint16_t presence)
4638732b070SChe-liang Chiou {
4648732b070SChe-liang Chiou 	const uint8_t command[12] = {
4658732b070SChe-liang Chiou 		0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x40, 0x0, 0x0, 0xa, 0x0, 0x0,
4668732b070SChe-liang Chiou 	};
4678732b070SChe-liang Chiou 	const size_t presence_offset = 10;
4688732b070SChe-liang Chiou 	uint8_t buf[COMMAND_BUFFER_SIZE];
4698732b070SChe-liang Chiou 
4708732b070SChe-liang Chiou 	if (pack_byte_string(buf, sizeof(buf), "sw",
4718732b070SChe-liang Chiou 				0, command, sizeof(command),
4728732b070SChe-liang Chiou 				presence_offset, presence))
4738732b070SChe-liang Chiou 		return TPM_LIB_ERROR;
4748732b070SChe-liang Chiou 
4758732b070SChe-liang Chiou 	return tpm_sendrecv_command(buf, NULL, NULL);
4768732b070SChe-liang Chiou }
4778732b070SChe-liang Chiou 
4788732b070SChe-liang Chiou uint32_t tpm_read_pubek(void *data, size_t count)
4798732b070SChe-liang Chiou {
4808732b070SChe-liang Chiou 	const uint8_t command[30] = {
4818732b070SChe-liang Chiou 		0x0, 0xc1, 0x0, 0x0, 0x0, 0x1e, 0x0, 0x0, 0x0, 0x7c,
4828732b070SChe-liang Chiou 	};
4838732b070SChe-liang Chiou 	const size_t response_size_offset = 2;
4848732b070SChe-liang Chiou 	const size_t data_offset = 10;
4858732b070SChe-liang Chiou 	const size_t header_and_checksum_size = TPM_RESPONSE_HEADER_LENGTH + 20;
4868732b070SChe-liang Chiou 	uint8_t response[COMMAND_BUFFER_SIZE + TPM_PUBEK_SIZE];
4878732b070SChe-liang Chiou 	size_t response_length = sizeof(response);
4888732b070SChe-liang Chiou 	uint32_t data_size;
4898732b070SChe-liang Chiou 	uint32_t err;
4908732b070SChe-liang Chiou 
4918732b070SChe-liang Chiou 	err = tpm_sendrecv_command(command, response, &response_length);
4928732b070SChe-liang Chiou 	if (err)
4938732b070SChe-liang Chiou 		return err;
4948732b070SChe-liang Chiou 	if (unpack_byte_string(response, response_length, "d",
4958732b070SChe-liang Chiou 				response_size_offset, &data_size))
4968732b070SChe-liang Chiou 		return TPM_LIB_ERROR;
4978732b070SChe-liang Chiou 	if (data_size < header_and_checksum_size)
4988732b070SChe-liang Chiou 		return TPM_LIB_ERROR;
4998732b070SChe-liang Chiou 	data_size -= header_and_checksum_size;
5008732b070SChe-liang Chiou 	if (data_size > count)
5018732b070SChe-liang Chiou 		return TPM_LIB_ERROR;
5028732b070SChe-liang Chiou 	if (unpack_byte_string(response, response_length, "s",
5038732b070SChe-liang Chiou 				data_offset, data, data_size))
5048732b070SChe-liang Chiou 		return TPM_LIB_ERROR;
5058732b070SChe-liang Chiou 
5068732b070SChe-liang Chiou 	return 0;
5078732b070SChe-liang Chiou }
5088732b070SChe-liang Chiou 
5098732b070SChe-liang Chiou uint32_t tpm_force_clear(void)
5108732b070SChe-liang Chiou {
5118732b070SChe-liang Chiou 	const uint8_t command[10] = {
5128732b070SChe-liang Chiou 		0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x5d,
5138732b070SChe-liang Chiou 	};
5148732b070SChe-liang Chiou 
5158732b070SChe-liang Chiou 	return tpm_sendrecv_command(command, NULL, NULL);
5168732b070SChe-liang Chiou }
5178732b070SChe-liang Chiou 
5188732b070SChe-liang Chiou uint32_t tpm_physical_enable(void)
5198732b070SChe-liang Chiou {
5208732b070SChe-liang Chiou 	const uint8_t command[10] = {
5218732b070SChe-liang Chiou 		0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x6f,
5228732b070SChe-liang Chiou 	};
5238732b070SChe-liang Chiou 
5248732b070SChe-liang Chiou 	return tpm_sendrecv_command(command, NULL, NULL);
5258732b070SChe-liang Chiou }
5268732b070SChe-liang Chiou 
5278732b070SChe-liang Chiou uint32_t tpm_physical_disable(void)
5288732b070SChe-liang Chiou {
5298732b070SChe-liang Chiou 	const uint8_t command[10] = {
5308732b070SChe-liang Chiou 		0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x70,
5318732b070SChe-liang Chiou 	};
5328732b070SChe-liang Chiou 
5338732b070SChe-liang Chiou 	return tpm_sendrecv_command(command, NULL, NULL);
5348732b070SChe-liang Chiou }
5358732b070SChe-liang Chiou 
5368732b070SChe-liang Chiou uint32_t tpm_physical_set_deactivated(uint8_t state)
5378732b070SChe-liang Chiou {
5388732b070SChe-liang Chiou 	const uint8_t command[11] = {
5398732b070SChe-liang Chiou 		0x0, 0xc1, 0x0, 0x0, 0x0, 0xb, 0x0, 0x0, 0x0, 0x72,
5408732b070SChe-liang Chiou 	};
5418732b070SChe-liang Chiou 	const size_t state_offset = 10;
5428732b070SChe-liang Chiou 	uint8_t buf[COMMAND_BUFFER_SIZE];
5438732b070SChe-liang Chiou 
5448732b070SChe-liang Chiou 	if (pack_byte_string(buf, sizeof(buf), "sb",
5458732b070SChe-liang Chiou 				0, command, sizeof(command),
5468732b070SChe-liang Chiou 				state_offset, state))
5478732b070SChe-liang Chiou 		return TPM_LIB_ERROR;
5488732b070SChe-liang Chiou 
5498732b070SChe-liang Chiou 	return tpm_sendrecv_command(buf, NULL, NULL);
5508732b070SChe-liang Chiou }
5518732b070SChe-liang Chiou 
5528732b070SChe-liang Chiou uint32_t tpm_get_capability(uint32_t cap_area, uint32_t sub_cap,
5538732b070SChe-liang Chiou 		void *cap, size_t count)
5548732b070SChe-liang Chiou {
5558732b070SChe-liang Chiou 	const uint8_t command[22] = {
5568732b070SChe-liang Chiou 		0x0, 0xc1,		/* TPM_TAG */
5578732b070SChe-liang Chiou 		0x0, 0x0, 0x0, 0x16,	/* parameter size */
5588732b070SChe-liang Chiou 		0x0, 0x0, 0x0, 0x65,	/* TPM_COMMAND_CODE */
5598732b070SChe-liang Chiou 		0x0, 0x0, 0x0, 0x0,	/* TPM_CAPABILITY_AREA */
5608732b070SChe-liang Chiou 		0x0, 0x0, 0x0, 0x4,	/* subcap size */
5618732b070SChe-liang Chiou 		0x0, 0x0, 0x0, 0x0,	/* subcap value */
5628732b070SChe-liang Chiou 	};
5638732b070SChe-liang Chiou 	const size_t cap_area_offset = 10;
5648732b070SChe-liang Chiou 	const size_t sub_cap_offset = 18;
5658732b070SChe-liang Chiou 	const size_t cap_offset = 14;
5668732b070SChe-liang Chiou 	const size_t cap_size_offset = 10;
5678732b070SChe-liang Chiou 	uint8_t buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE];
5688732b070SChe-liang Chiou 	size_t response_length = sizeof(response);
5698732b070SChe-liang Chiou 	uint32_t cap_size;
5708732b070SChe-liang Chiou 	uint32_t err;
5718732b070SChe-liang Chiou 
5728732b070SChe-liang Chiou 	if (pack_byte_string(buf, sizeof(buf), "sdd",
5738732b070SChe-liang Chiou 				0, command, sizeof(command),
5748732b070SChe-liang Chiou 				cap_area_offset, cap_area,
5758732b070SChe-liang Chiou 				sub_cap_offset, sub_cap))
5768732b070SChe-liang Chiou 		return TPM_LIB_ERROR;
5778732b070SChe-liang Chiou 	err = tpm_sendrecv_command(buf, response, &response_length);
5788732b070SChe-liang Chiou 	if (err)
5798732b070SChe-liang Chiou 		return err;
5808732b070SChe-liang Chiou 	if (unpack_byte_string(response, response_length, "d",
5818732b070SChe-liang Chiou 				cap_size_offset, &cap_size))
5828732b070SChe-liang Chiou 		return TPM_LIB_ERROR;
5838732b070SChe-liang Chiou 	if (cap_size > response_length || cap_size > count)
5848732b070SChe-liang Chiou 		return TPM_LIB_ERROR;
5858732b070SChe-liang Chiou 	if (unpack_byte_string(response, response_length, "s",
5868732b070SChe-liang Chiou 				cap_offset, cap, cap_size))
5878732b070SChe-liang Chiou 		return TPM_LIB_ERROR;
5888732b070SChe-liang Chiou 
5898732b070SChe-liang Chiou 	return 0;
5908732b070SChe-liang Chiou }
591be6c1529SReinhard Pfau 
592be6c1529SReinhard Pfau #ifdef CONFIG_TPM_AUTH_SESSIONS
593be6c1529SReinhard Pfau 
594be6c1529SReinhard Pfau /**
595be6c1529SReinhard Pfau  * Fill an authentication block in a request.
596be6c1529SReinhard Pfau  * This func can create the first as well as the second auth block (for
597be6c1529SReinhard Pfau  * double authorized commands).
598be6c1529SReinhard Pfau  *
599be6c1529SReinhard Pfau  * @param request	pointer to the request (w/ uninitialised auth data)
600be6c1529SReinhard Pfau  * @param request_len0	length of the request without auth data
601be6c1529SReinhard Pfau  * @param handles_len	length of the handles area in request
602be6c1529SReinhard Pfau  * @param auth_session	pointer to the (valid) auth session to be used
603be6c1529SReinhard Pfau  * @param request_auth	pointer to the auth block of the request to be filled
604be6c1529SReinhard Pfau  * @param auth		authentication data (HMAC key)
605be6c1529SReinhard Pfau  */
606be6c1529SReinhard Pfau static uint32_t create_request_auth(const void *request, size_t request_len0,
607be6c1529SReinhard Pfau 	size_t handles_len,
608be6c1529SReinhard Pfau 	struct session_data *auth_session,
609be6c1529SReinhard Pfau 	void *request_auth, const void *auth)
610be6c1529SReinhard Pfau {
611be6c1529SReinhard Pfau 	uint8_t hmac_data[DIGEST_LENGTH * 3 + 1];
612be6c1529SReinhard Pfau 	sha1_context hash_ctx;
613be6c1529SReinhard Pfau 	const size_t command_code_offset = 6;
614be6c1529SReinhard Pfau 	const size_t auth_nonce_odd_offset = 4;
615be6c1529SReinhard Pfau 	const size_t auth_continue_offset = 24;
616be6c1529SReinhard Pfau 	const size_t auth_auth_offset = 25;
617be6c1529SReinhard Pfau 
618be6c1529SReinhard Pfau 	if (!auth_session || !auth_session->valid)
619be6c1529SReinhard Pfau 		return TPM_LIB_ERROR;
620be6c1529SReinhard Pfau 
621be6c1529SReinhard Pfau 	sha1_starts(&hash_ctx);
622be6c1529SReinhard Pfau 	sha1_update(&hash_ctx, request + command_code_offset, 4);
623be6c1529SReinhard Pfau 	if (request_len0 > TPM_REQUEST_HEADER_LENGTH + handles_len)
624be6c1529SReinhard Pfau 		sha1_update(&hash_ctx,
625be6c1529SReinhard Pfau 			    request + TPM_REQUEST_HEADER_LENGTH + handles_len,
626be6c1529SReinhard Pfau 			    request_len0 - TPM_REQUEST_HEADER_LENGTH
627be6c1529SReinhard Pfau 			    - handles_len);
628be6c1529SReinhard Pfau 	sha1_finish(&hash_ctx, hmac_data);
629be6c1529SReinhard Pfau 
630be6c1529SReinhard Pfau 	sha1_starts(&hash_ctx);
631be6c1529SReinhard Pfau 	sha1_update(&hash_ctx, auth_session->nonce_odd, DIGEST_LENGTH);
632be6c1529SReinhard Pfau 	sha1_update(&hash_ctx, hmac_data, sizeof(hmac_data));
633be6c1529SReinhard Pfau 	sha1_finish(&hash_ctx, auth_session->nonce_odd);
634be6c1529SReinhard Pfau 
635be6c1529SReinhard Pfau 	if (pack_byte_string(request_auth, TPM_REQUEST_AUTH_LENGTH, "dsb",
636be6c1529SReinhard Pfau 			     0, auth_session->handle,
637be6c1529SReinhard Pfau 			     auth_nonce_odd_offset, auth_session->nonce_odd,
638be6c1529SReinhard Pfau 			     DIGEST_LENGTH,
639be6c1529SReinhard Pfau 			     auth_continue_offset, 1))
640be6c1529SReinhard Pfau 		return TPM_LIB_ERROR;
641be6c1529SReinhard Pfau 	if (pack_byte_string(hmac_data, sizeof(hmac_data), "ss",
642be6c1529SReinhard Pfau 			     DIGEST_LENGTH,
643be6c1529SReinhard Pfau 			     auth_session->nonce_even,
644be6c1529SReinhard Pfau 			     DIGEST_LENGTH,
645be6c1529SReinhard Pfau 			     2 * DIGEST_LENGTH,
646be6c1529SReinhard Pfau 			     request_auth + auth_nonce_odd_offset,
647be6c1529SReinhard Pfau 			     DIGEST_LENGTH + 1))
648be6c1529SReinhard Pfau 		return TPM_LIB_ERROR;
649be6c1529SReinhard Pfau 	sha1_hmac(auth, DIGEST_LENGTH, hmac_data, sizeof(hmac_data),
650be6c1529SReinhard Pfau 		  request_auth + auth_auth_offset);
651be6c1529SReinhard Pfau 
652be6c1529SReinhard Pfau 	return TPM_SUCCESS;
653be6c1529SReinhard Pfau }
654be6c1529SReinhard Pfau 
655be6c1529SReinhard Pfau /**
656be6c1529SReinhard Pfau  * Verify an authentication block in a response.
657be6c1529SReinhard Pfau  * Since this func updates the nonce_even in the session data it has to be
658be6c1529SReinhard Pfau  * called when receiving a succesfull AUTH response.
659be6c1529SReinhard Pfau  * This func can verify the first as well as the second auth block (for
660be6c1529SReinhard Pfau  * double authorized commands).
661be6c1529SReinhard Pfau  *
662be6c1529SReinhard Pfau  * @param command_code	command code of the request
663be6c1529SReinhard Pfau  * @param response	pointer to the request (w/ uninitialised auth data)
664be6c1529SReinhard Pfau  * @param handles_len	length of the handles area in response
665be6c1529SReinhard Pfau  * @param auth_session	pointer to the (valid) auth session to be used
666be6c1529SReinhard Pfau  * @param response_auth	pointer to the auth block of the response to be verified
667be6c1529SReinhard Pfau  * @param auth		authentication data (HMAC key)
668be6c1529SReinhard Pfau  */
669be6c1529SReinhard Pfau static uint32_t verify_response_auth(uint32_t command_code,
670be6c1529SReinhard Pfau 	const void *response, size_t response_len0,
671be6c1529SReinhard Pfau 	size_t handles_len,
672be6c1529SReinhard Pfau 	struct session_data *auth_session,
673be6c1529SReinhard Pfau 	const void *response_auth, const void *auth)
674be6c1529SReinhard Pfau {
675be6c1529SReinhard Pfau 	uint8_t hmac_data[DIGEST_LENGTH * 3 + 1];
676be6c1529SReinhard Pfau 	uint8_t computed_auth[DIGEST_LENGTH];
677be6c1529SReinhard Pfau 	sha1_context hash_ctx;
678be6c1529SReinhard Pfau 	const size_t return_code_offset = 6;
679be6c1529SReinhard Pfau 	const size_t auth_continue_offset = 20;
680be6c1529SReinhard Pfau 	const size_t auth_auth_offset = 21;
681be6c1529SReinhard Pfau 	uint8_t auth_continue;
682be6c1529SReinhard Pfau 
683be6c1529SReinhard Pfau 	if (!auth_session || !auth_session->valid)
684be6c1529SReinhard Pfau 		return TPM_AUTHFAIL;
685be6c1529SReinhard Pfau 	if (pack_byte_string(hmac_data, sizeof(hmac_data), "d",
686be6c1529SReinhard Pfau 			     0, command_code))
687be6c1529SReinhard Pfau 		return TPM_LIB_ERROR;
688be6c1529SReinhard Pfau 	if (response_len0 < TPM_RESPONSE_HEADER_LENGTH)
689be6c1529SReinhard Pfau 		return TPM_LIB_ERROR;
690be6c1529SReinhard Pfau 
691be6c1529SReinhard Pfau 	sha1_starts(&hash_ctx);
692be6c1529SReinhard Pfau 	sha1_update(&hash_ctx, response + return_code_offset, 4);
693be6c1529SReinhard Pfau 	sha1_update(&hash_ctx, hmac_data, 4);
694be6c1529SReinhard Pfau 	if (response_len0 > TPM_RESPONSE_HEADER_LENGTH + handles_len)
695be6c1529SReinhard Pfau 		sha1_update(&hash_ctx,
696be6c1529SReinhard Pfau 			    response + TPM_RESPONSE_HEADER_LENGTH + handles_len,
697be6c1529SReinhard Pfau 			    response_len0 - TPM_RESPONSE_HEADER_LENGTH
698be6c1529SReinhard Pfau 			    - handles_len);
699be6c1529SReinhard Pfau 	sha1_finish(&hash_ctx, hmac_data);
700be6c1529SReinhard Pfau 
701be6c1529SReinhard Pfau 	memcpy(auth_session->nonce_even, response_auth, DIGEST_LENGTH);
702be6c1529SReinhard Pfau 	auth_continue = ((uint8_t *)response_auth)[auth_continue_offset];
703be6c1529SReinhard Pfau 	if (pack_byte_string(hmac_data, sizeof(hmac_data), "ssb",
704be6c1529SReinhard Pfau 			     DIGEST_LENGTH,
705be6c1529SReinhard Pfau 			     response_auth,
706be6c1529SReinhard Pfau 			     DIGEST_LENGTH,
707be6c1529SReinhard Pfau 			     2 * DIGEST_LENGTH,
708be6c1529SReinhard Pfau 			     auth_session->nonce_odd,
709be6c1529SReinhard Pfau 			     DIGEST_LENGTH,
710be6c1529SReinhard Pfau 			     3 * DIGEST_LENGTH,
711be6c1529SReinhard Pfau 			     auth_continue))
712be6c1529SReinhard Pfau 		return TPM_LIB_ERROR;
713be6c1529SReinhard Pfau 
714be6c1529SReinhard Pfau 	sha1_hmac(auth, DIGEST_LENGTH, hmac_data, sizeof(hmac_data),
715be6c1529SReinhard Pfau 		  computed_auth);
716be6c1529SReinhard Pfau 
717be6c1529SReinhard Pfau 	if (memcmp(computed_auth, response_auth + auth_auth_offset,
718be6c1529SReinhard Pfau 		   DIGEST_LENGTH))
719be6c1529SReinhard Pfau 		return TPM_AUTHFAIL;
720be6c1529SReinhard Pfau 
721be6c1529SReinhard Pfau 	return TPM_SUCCESS;
722be6c1529SReinhard Pfau }
723be6c1529SReinhard Pfau 
724be6c1529SReinhard Pfau 
725be6c1529SReinhard Pfau uint32_t tpm_terminate_auth_session(uint32_t auth_handle)
726be6c1529SReinhard Pfau {
727be6c1529SReinhard Pfau 	const uint8_t command[18] = {
728be6c1529SReinhard Pfau 		0x00, 0xc1,		/* TPM_TAG */
729be6c1529SReinhard Pfau 		0x00, 0x00, 0x00, 0x00,	/* parameter size */
730be6c1529SReinhard Pfau 		0x00, 0x00, 0x00, 0xba,	/* TPM_COMMAND_CODE */
731be6c1529SReinhard Pfau 		0x00, 0x00, 0x00, 0x00,	/* TPM_HANDLE */
732be6c1529SReinhard Pfau 		0x00, 0x00, 0x00, 0x02,	/* TPM_RESSOURCE_TYPE */
733be6c1529SReinhard Pfau 	};
734be6c1529SReinhard Pfau 	const size_t req_handle_offset = TPM_REQUEST_HEADER_LENGTH;
735be6c1529SReinhard Pfau 	uint8_t request[COMMAND_BUFFER_SIZE];
736be6c1529SReinhard Pfau 
737be6c1529SReinhard Pfau 	if (pack_byte_string(request, sizeof(request), "sd",
738be6c1529SReinhard Pfau 			     0, command, sizeof(command),
739be6c1529SReinhard Pfau 			     req_handle_offset, auth_handle))
740be6c1529SReinhard Pfau 		return TPM_LIB_ERROR;
741be6c1529SReinhard Pfau 	if (oiap_session.valid && oiap_session.handle == auth_handle)
742be6c1529SReinhard Pfau 		oiap_session.valid = 0;
743be6c1529SReinhard Pfau 
744be6c1529SReinhard Pfau 	return tpm_sendrecv_command(request, NULL, NULL);
745be6c1529SReinhard Pfau }
746be6c1529SReinhard Pfau 
747be6c1529SReinhard Pfau uint32_t tpm_end_oiap(void)
748be6c1529SReinhard Pfau {
749be6c1529SReinhard Pfau 	uint32_t err = TPM_SUCCESS;
750be6c1529SReinhard Pfau 	if (oiap_session.valid)
751be6c1529SReinhard Pfau 		err = tpm_terminate_auth_session(oiap_session.handle);
752be6c1529SReinhard Pfau 	return err;
753be6c1529SReinhard Pfau }
754be6c1529SReinhard Pfau 
755be6c1529SReinhard Pfau uint32_t tpm_oiap(uint32_t *auth_handle)
756be6c1529SReinhard Pfau {
757be6c1529SReinhard Pfau 	const uint8_t command[10] = {
758be6c1529SReinhard Pfau 		0x00, 0xc1,		/* TPM_TAG */
759be6c1529SReinhard Pfau 		0x00, 0x00, 0x00, 0x0a,	/* parameter size */
760be6c1529SReinhard Pfau 		0x00, 0x00, 0x00, 0x0a,	/* TPM_COMMAND_CODE */
761be6c1529SReinhard Pfau 	};
762be6c1529SReinhard Pfau 	const size_t res_auth_handle_offset = TPM_RESPONSE_HEADER_LENGTH;
763be6c1529SReinhard Pfau 	const size_t res_nonce_even_offset = TPM_RESPONSE_HEADER_LENGTH + 4;
764be6c1529SReinhard Pfau 	uint8_t response[COMMAND_BUFFER_SIZE];
765be6c1529SReinhard Pfau 	size_t response_length = sizeof(response);
766be6c1529SReinhard Pfau 	uint32_t err;
767be6c1529SReinhard Pfau 
768be6c1529SReinhard Pfau 	if (oiap_session.valid)
769be6c1529SReinhard Pfau 		tpm_terminate_auth_session(oiap_session.handle);
770be6c1529SReinhard Pfau 
771be6c1529SReinhard Pfau 	err = tpm_sendrecv_command(command, response, &response_length);
772be6c1529SReinhard Pfau 	if (err)
773be6c1529SReinhard Pfau 		return err;
774be6c1529SReinhard Pfau 	if (unpack_byte_string(response, response_length, "ds",
775be6c1529SReinhard Pfau 			       res_auth_handle_offset, &oiap_session.handle,
776be6c1529SReinhard Pfau 			       res_nonce_even_offset, &oiap_session.nonce_even,
777be6c1529SReinhard Pfau 			       (uint32_t)DIGEST_LENGTH))
778be6c1529SReinhard Pfau 		return TPM_LIB_ERROR;
779be6c1529SReinhard Pfau 	oiap_session.valid = 1;
780be6c1529SReinhard Pfau 	if (auth_handle)
781be6c1529SReinhard Pfau 		*auth_handle = oiap_session.handle;
782be6c1529SReinhard Pfau 	return 0;
783be6c1529SReinhard Pfau }
784be6c1529SReinhard Pfau 
785be6c1529SReinhard Pfau uint32_t tpm_load_key2_oiap(uint32_t parent_handle,
786be6c1529SReinhard Pfau 		const void *key, size_t key_length,
787be6c1529SReinhard Pfau 		const void *parent_key_usage_auth,
788be6c1529SReinhard Pfau 		uint32_t *key_handle)
789be6c1529SReinhard Pfau {
790be6c1529SReinhard Pfau 	const uint8_t command[14] = {
791be6c1529SReinhard Pfau 		0x00, 0xc2,		/* TPM_TAG */
792be6c1529SReinhard Pfau 		0x00, 0x00, 0x00, 0x00,	/* parameter size */
793be6c1529SReinhard Pfau 		0x00, 0x00, 0x00, 0x41,	/* TPM_COMMAND_CODE */
794be6c1529SReinhard Pfau 		0x00, 0x00, 0x00, 0x00,	/* parent handle */
795be6c1529SReinhard Pfau 	};
796be6c1529SReinhard Pfau 	const size_t req_size_offset = 2;
797be6c1529SReinhard Pfau 	const size_t req_parent_handle_offset = TPM_REQUEST_HEADER_LENGTH;
798be6c1529SReinhard Pfau 	const size_t req_key_offset = TPM_REQUEST_HEADER_LENGTH + 4;
799be6c1529SReinhard Pfau 	const size_t res_handle_offset = TPM_RESPONSE_HEADER_LENGTH;
800be6c1529SReinhard Pfau 	uint8_t request[sizeof(command) + TPM_KEY12_MAX_LENGTH
801be6c1529SReinhard Pfau 			+ TPM_REQUEST_AUTH_LENGTH];
802be6c1529SReinhard Pfau 	uint8_t response[COMMAND_BUFFER_SIZE];
803be6c1529SReinhard Pfau 	size_t response_length = sizeof(response);
804be6c1529SReinhard Pfau 	uint32_t err;
805be6c1529SReinhard Pfau 
806be6c1529SReinhard Pfau 	if (!oiap_session.valid) {
807be6c1529SReinhard Pfau 		err = tpm_oiap(NULL);
808be6c1529SReinhard Pfau 		if (err)
809be6c1529SReinhard Pfau 			return err;
810be6c1529SReinhard Pfau 	}
811be6c1529SReinhard Pfau 	if (pack_byte_string(request, sizeof(request), "sdds",
812be6c1529SReinhard Pfau 			     0, command, sizeof(command),
813be6c1529SReinhard Pfau 			     req_size_offset,
814be6c1529SReinhard Pfau 			     sizeof(command) + key_length
815be6c1529SReinhard Pfau 			     + TPM_REQUEST_AUTH_LENGTH,
816be6c1529SReinhard Pfau 			     req_parent_handle_offset, parent_handle,
817be6c1529SReinhard Pfau 			     req_key_offset, key, key_length
818be6c1529SReinhard Pfau 		))
819be6c1529SReinhard Pfau 		return TPM_LIB_ERROR;
820be6c1529SReinhard Pfau 
821be6c1529SReinhard Pfau 	err = create_request_auth(request, sizeof(command) + key_length, 4,
822be6c1529SReinhard Pfau 				&oiap_session,
823be6c1529SReinhard Pfau 				request + sizeof(command) + key_length,
824be6c1529SReinhard Pfau 				parent_key_usage_auth);
825be6c1529SReinhard Pfau 	if (err)
826be6c1529SReinhard Pfau 		return err;
827be6c1529SReinhard Pfau 	err = tpm_sendrecv_command(request, response, &response_length);
828be6c1529SReinhard Pfau 	if (err) {
829be6c1529SReinhard Pfau 		if (err == TPM_AUTHFAIL)
830be6c1529SReinhard Pfau 			oiap_session.valid = 0;
831be6c1529SReinhard Pfau 		return err;
832be6c1529SReinhard Pfau 	}
833be6c1529SReinhard Pfau 
834be6c1529SReinhard Pfau 	err = verify_response_auth(0x00000041, response,
835be6c1529SReinhard Pfau 			response_length - TPM_RESPONSE_AUTH_LENGTH,
836be6c1529SReinhard Pfau 			4, &oiap_session,
837be6c1529SReinhard Pfau 			response + response_length - TPM_RESPONSE_AUTH_LENGTH,
838be6c1529SReinhard Pfau 			parent_key_usage_auth);
839be6c1529SReinhard Pfau 	if (err)
840be6c1529SReinhard Pfau 		return err;
841be6c1529SReinhard Pfau 
842be6c1529SReinhard Pfau 	if (key_handle) {
843be6c1529SReinhard Pfau 		if (unpack_byte_string(response, response_length, "d",
844be6c1529SReinhard Pfau 				       res_handle_offset, key_handle))
845be6c1529SReinhard Pfau 			return TPM_LIB_ERROR;
846be6c1529SReinhard Pfau 	}
847be6c1529SReinhard Pfau 
848be6c1529SReinhard Pfau 	return 0;
849be6c1529SReinhard Pfau }
850be6c1529SReinhard Pfau 
851be6c1529SReinhard Pfau uint32_t tpm_get_pub_key_oiap(uint32_t key_handle, const void *usage_auth,
852be6c1529SReinhard Pfau 			void *pubkey, size_t *pubkey_len)
853be6c1529SReinhard Pfau {
854be6c1529SReinhard Pfau 	const uint8_t command[14] = {
855be6c1529SReinhard Pfau 		0x00, 0xc2,		/* TPM_TAG */
856be6c1529SReinhard Pfau 		0x00, 0x00, 0x00, 0x00,	/* parameter size */
857be6c1529SReinhard Pfau 		0x00, 0x00, 0x00, 0x21,	/* TPM_COMMAND_CODE */
858be6c1529SReinhard Pfau 		0x00, 0x00, 0x00, 0x00,	/* key handle */
859be6c1529SReinhard Pfau 	};
860be6c1529SReinhard Pfau 	const size_t req_size_offset = 2;
861be6c1529SReinhard Pfau 	const size_t req_key_handle_offset = TPM_REQUEST_HEADER_LENGTH;
862be6c1529SReinhard Pfau 	const size_t res_pubkey_offset = TPM_RESPONSE_HEADER_LENGTH;
863be6c1529SReinhard Pfau 	uint8_t request[sizeof(command) + TPM_REQUEST_AUTH_LENGTH];
864be6c1529SReinhard Pfau 	uint8_t response[TPM_RESPONSE_HEADER_LENGTH + TPM_PUBKEY_MAX_LENGTH
865be6c1529SReinhard Pfau 			+ TPM_RESPONSE_AUTH_LENGTH];
866be6c1529SReinhard Pfau 	size_t response_length = sizeof(response);
867be6c1529SReinhard Pfau 	uint32_t err;
868be6c1529SReinhard Pfau 
869be6c1529SReinhard Pfau 	if (!oiap_session.valid) {
870be6c1529SReinhard Pfau 		err = tpm_oiap(NULL);
871be6c1529SReinhard Pfau 		if (err)
872be6c1529SReinhard Pfau 			return err;
873be6c1529SReinhard Pfau 	}
874be6c1529SReinhard Pfau 	if (pack_byte_string(request, sizeof(request), "sdd",
875be6c1529SReinhard Pfau 			     0, command, sizeof(command),
876be6c1529SReinhard Pfau 			     req_size_offset,
877be6c1529SReinhard Pfau 			     (uint32_t)(sizeof(command)
878be6c1529SReinhard Pfau 			     + TPM_REQUEST_AUTH_LENGTH),
879be6c1529SReinhard Pfau 			     req_key_handle_offset, key_handle
880be6c1529SReinhard Pfau 		))
881be6c1529SReinhard Pfau 		return TPM_LIB_ERROR;
882be6c1529SReinhard Pfau 	err = create_request_auth(request, sizeof(command), 4, &oiap_session,
883be6c1529SReinhard Pfau 			request + sizeof(command), usage_auth);
884be6c1529SReinhard Pfau 	if (err)
885be6c1529SReinhard Pfau 		return err;
886be6c1529SReinhard Pfau 	err = tpm_sendrecv_command(request, response, &response_length);
887be6c1529SReinhard Pfau 	if (err) {
888be6c1529SReinhard Pfau 		if (err == TPM_AUTHFAIL)
889be6c1529SReinhard Pfau 			oiap_session.valid = 0;
890be6c1529SReinhard Pfau 		return err;
891be6c1529SReinhard Pfau 	}
892be6c1529SReinhard Pfau 	err = verify_response_auth(0x00000021, response,
893be6c1529SReinhard Pfau 			response_length - TPM_RESPONSE_AUTH_LENGTH,
894be6c1529SReinhard Pfau 			0, &oiap_session,
895be6c1529SReinhard Pfau 			response + response_length - TPM_RESPONSE_AUTH_LENGTH,
896be6c1529SReinhard Pfau 			usage_auth);
897be6c1529SReinhard Pfau 	if (err)
898be6c1529SReinhard Pfau 		return err;
899be6c1529SReinhard Pfau 
900be6c1529SReinhard Pfau 	if (pubkey) {
901be6c1529SReinhard Pfau 		if ((response_length - TPM_RESPONSE_HEADER_LENGTH
902be6c1529SReinhard Pfau 			- TPM_RESPONSE_AUTH_LENGTH) > *pubkey_len)
903be6c1529SReinhard Pfau 			return TPM_LIB_ERROR;
904be6c1529SReinhard Pfau 		*pubkey_len = response_length - TPM_RESPONSE_HEADER_LENGTH
905be6c1529SReinhard Pfau 			- TPM_RESPONSE_AUTH_LENGTH;
906be6c1529SReinhard Pfau 		memcpy(pubkey, response + res_pubkey_offset,
907be6c1529SReinhard Pfau 		       response_length - TPM_RESPONSE_HEADER_LENGTH
908be6c1529SReinhard Pfau 		       - TPM_RESPONSE_AUTH_LENGTH);
909be6c1529SReinhard Pfau 	}
910be6c1529SReinhard Pfau 
911be6c1529SReinhard Pfau 	return 0;
912be6c1529SReinhard Pfau }
913be6c1529SReinhard Pfau 
914be6c1529SReinhard Pfau #endif /* CONFIG_TPM_AUTH_SESSIONS */
915