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