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