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