diff --git a/conf/plugins/tpm.opt b/conf/plugins/tpm.opt index cd666dde8..58f5d8b35 100644 --- a/conf/plugins/tpm.opt +++ b/conf/plugins/tpm.opt @@ -1,2 +1,5 @@ charon.plugins.tpm.use_rng = no Whether the TPM should be used as RNG. + +charon.plugins.tpm.tcti.name = tabrmd + Name of TCTI library. Valid values: tabrmd, device, mssim. \ No newline at end of file diff --git a/configure.ac b/configure.ac index 65df426d5..a3591aedb 100644 --- a/configure.ac +++ b/configure.ac @@ -1019,18 +1019,28 @@ if test x$tss_trousers = xtrue; then fi if test x$tss_tss2 = xtrue; then + PKG_CHECK_MODULES(tss2_sys, [tss2-sys], + [tss2_sys=true; AC_DEFINE([TSS2_SYS], [], [use TSS2 v2 System API])], + [tss2_sys=false]) + PKG_CHECK_MODULES(tss2_esys, [tss2-esys], + [tss2_esys=true; AC_DEFINE([TSS2_ESYS], [], [use TSS2 v2 Extended System API])], + [tss2_esys=false]) PKG_CHECK_MODULES(tss2_tabrmd, [tcti-tabrmd], [tss2_tabrmd=true; AC_DEFINE([TSS2_TCTI_TABRMD], [], [use TCTI Access Broker and Resource Mamager])], [tss2_tabrmd=false]) PKG_CHECK_MODULES(tss2_socket, [tcti-socket], [tss2_socket=true; AC_DEFINE([TSS2_TCTI_SOCKET], [], [use TCTI Sockets])], [tss2_socket=false]) - if test x$tss2_tabrmd = xtrue -o x$tss2_socket = xtrue; then - AC_DEFINE([TSS_TSS2], [], [use TSS 2.0 libraries]) + if test x$tss2_sys = xtrue; then + AC_DEFINE([TSS_TSS2_V2], [], [use TSS 2.0 v2 libraries]) + AC_SUBST(tss2_CFLAGS, "$tss2_sys_CFLAGS") + AC_SUBST(tss2_LIBS, "$tss2_sys_LIBS") + elif test x$tss2_tabrmd = xtrue -o x$tss2_socket = xtrue; then + AC_DEFINE([TSS_TSS2_V1], [], [use TSS 2.0 v1 libraries]) AC_SUBST(tss2_CFLAGS, "$tss2_tabrmd_CFLAGS $tss2_socket_CFLAGS") AC_SUBST(tss2_LIBS, "$tss2_tabrmd_LIBS $tss2_socket_LIBS") else - AC_MSG_FAILURE([no TSS2 TCTI library detected]) + AC_MSG_FAILURE([no TSS2 TCTI or SAPI libraries detected]) fi fi diff --git a/src/libtpmtss/Makefile.am b/src/libtpmtss/Makefile.am index 1b3a9706f..d192fc126 100644 --- a/src/libtpmtss/Makefile.am +++ b/src/libtpmtss/Makefile.am @@ -24,8 +24,8 @@ libtpmtss_la_SOURCES = \ tpm_tss.h tpm_tss.c \ tpm_tss_quote_info.h tpm_tss_quote_info.c \ tpm_tss_trousers.h tpm_tss_trousers.c \ - tpm_tss_tss2.h tpm_tss_tss2.c \ - tpm_tss_tss2_names.h tpm_tss_tss2_names.c + tpm_tss_tss2.h tpm_tss_tss2_v1.c tpm_tss_tss2_v2.c \ + tpm_tss_tss2_names.h tpm_tss_tss2_names_v1.c tpm_tss_tss2_names_v2.c if MONOLITHIC SUBDIRS = diff --git a/src/libtpmtss/plugins/tpm/tpm_plugin.c b/src/libtpmtss/plugins/tpm/tpm_plugin.c index e98899852..a00f46ea2 100644 --- a/src/libtpmtss/plugins/tpm/tpm_plugin.c +++ b/src/libtpmtss/plugins/tpm/tpm_plugin.c @@ -18,6 +18,7 @@ #include "tpm_cert.h" #include "tpm_rng.h" +#include #include typedef struct private_tpm_plugin_t private_tpm_plugin_t; @@ -80,6 +81,7 @@ METHOD(plugin_t, destroy, void, private_tpm_plugin_t *this) { free(this); + libtpmtss_deinit(); } /* @@ -89,6 +91,11 @@ plugin_t *tpm_plugin_create() { private_tpm_plugin_t *this; + if (!libtpmtss_init()) + { + return NULL; + } + INIT(this, .public = { .plugin = { diff --git a/src/libtpmtss/tpm_tss.c b/src/libtpmtss/tpm_tss.c index 42a341896..72fd45b81 100644 --- a/src/libtpmtss/tpm_tss.c +++ b/src/libtpmtss/tpm_tss.c @@ -27,12 +27,20 @@ /** * Described in header. */ -void libtpmtss_init(void) +bool libtpmtss_init(void) { - /* empty */ + return tpm_tss_tss2_init(); } -typedef tpm_tss_t*(*tpm_tss_create)(); +/** + * Described in header. + */ +void libtpmtss_deinit(void) +{ + tpm_tss_tss2_deinit(); +} + +typedef tpm_tss_t*(*tpm_tss_create)(void); /** * See header. diff --git a/src/libtpmtss/tpm_tss.h b/src/libtpmtss/tpm_tss.h index bcb7ab949..c0bc26117 100644 --- a/src/libtpmtss/tpm_tss.h +++ b/src/libtpmtss/tpm_tss.h @@ -48,14 +48,14 @@ struct tpm_tss_t { /** * Get TPM version supported by TSS * - * @return TPM version + * @return TPM version */ tpm_version_t (*get_version)(tpm_tss_t *this); /** * Get TPM version info (TPM 1.2 only) * - * @return TPM version info struct + * @return TPM version info struct */ chunk_t (*get_version_info)(tpm_tss_t *this); @@ -74,8 +74,8 @@ struct tpm_tss_t { /** * Get public key from TPM using its object handle (TPM 2.0 only) * - * @param handle key object handle - * @return public key in PKCS#1 format + * @param handle key object handle + * @return public key in PKCS#1 format */ chunk_t (*get_public)(tpm_tss_t *this, uint32_t handle); @@ -169,8 +169,15 @@ struct tpm_tss_t { tpm_tss_t *tpm_tss_probe(tpm_version_t version); /** - * Dummy libtpmtss initialization function needed for integrity test + * libtpmtss initialization function + * + * @return TRUE if initialization was successful */ -void libtpmtss_init(void); +bool libtpmtss_init(void); + +/** + * libtpmtss de-initialization function + */ +void libtpmtss_deinit(void); #endif /** TPM_TSS_H_ @}*/ diff --git a/src/libtpmtss/tpm_tss_trousers.h b/src/libtpmtss/tpm_tss_trousers.h index 3afba0db2..3ff3e6685 100644 --- a/src/libtpmtss/tpm_tss_trousers.h +++ b/src/libtpmtss/tpm_tss_trousers.h @@ -47,6 +47,6 @@ struct tpm_tss_trousers_t { /** * Create a tpm_tss_trousers instance. */ -tpm_tss_t *tpm_tss_trousers_create(); +tpm_tss_t *tpm_tss_trousers_create(void); #endif /** TPM_TSS_TROUSERS_H_ @}*/ diff --git a/src/libtpmtss/tpm_tss_tss2.h b/src/libtpmtss/tpm_tss_tss2.h index f3a11e5fd..f2846c916 100644 --- a/src/libtpmtss/tpm_tss_tss2.h +++ b/src/libtpmtss/tpm_tss_tss2.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Andreas Steffen + * Copyright (C) 2016-2018 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -26,6 +26,18 @@ /** * Create a tpm_tss_tss2 instance. */ -tpm_tss_t *tpm_tss_tss2_create(); +tpm_tss_t *tpm_tss_tss2_create(void); + +/** + * Initialize the tpm_tss_tss2 library. + * + * @return TRUE if initialization was successful + */ +bool tpm_tss_tss2_init(void); + +/** + * /De-initialize the tpm_tss_tss2 library. + */ +void tpm_tss_tss2_deinit(void); #endif /** TPM_TSS_TSS2_H_ @}*/ diff --git a/src/libtpmtss/tpm_tss_tss2_names.c b/src/libtpmtss/tpm_tss_tss2_names_v1.c similarity index 93% rename from src/libtpmtss/tpm_tss_tss2_names.c rename to src/libtpmtss/tpm_tss_tss2_names_v1.c index a613ac566..d2a4b5b57 100644 --- a/src/libtpmtss/tpm_tss_tss2_names.c +++ b/src/libtpmtss/tpm_tss_tss2_names_v1.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Andreas Steffen + * Copyright (C) 2016-2018 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -15,7 +15,7 @@ #include "tpm_tss_tss2_names.h" -#ifdef TSS_TSS2 +#ifdef TSS_TSS2_V1 #include @@ -102,7 +102,9 @@ ENUM_NEXT(tpm_ecc_curve_names, TPM_ECC_SM2_P256, TPM_ECC_SM2_P256, TPM_ECC_BN_P6 ); ENUM_END(tpm_ecc_curve_names, TPM_ECC_SM2_P256); -#else /* TSS_TSS2 */ +#else /* TSS_TSS2_V1 */ + +#ifndef TSS_TSS2_V2 /** * TPM 2.0 algorithm ID names @@ -118,6 +120,8 @@ ENUM(tpm_ecc_curve_names, 0, 0, "NONE" ); -#endif /* TSS_TSS2 */ +#endif /* !TSS_TSS2_V2 */ + +#endif /* TSS_TSS2_V1 */ diff --git a/src/libtpmtss/tpm_tss_tss2_names_v2.c b/src/libtpmtss/tpm_tss_tss2_names_v2.c new file mode 100644 index 000000000..c8d29e4e6 --- /dev/null +++ b/src/libtpmtss/tpm_tss_tss2_names_v2.c @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2018 Andreas Steffen + * HSR Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifdef TSS_TSS2_V2 + +#include "tpm_tss_tss2_names.h" + +#include + +/** + * TPM 2.0 algorithm ID names + */ +ENUM_BEGIN(tpm_alg_id_names, TPM2_ALG_ERROR, TPM2_ALG_RSA, + "ERROR", + "RSA" +); +ENUM_NEXT(tpm_alg_id_names, TPM2_ALG_SHA1, TPM2_ALG_KEYEDHASH, TPM2_ALG_RSA, + "SHA1", + "HMAC", + "AES", + "MGF1", + "KEYEDHASH" +); +ENUM_NEXT(tpm_alg_id_names, TPM2_ALG_XOR, TPM2_ALG_SHA512, TPM2_ALG_KEYEDHASH, + "XOR", + "SHA256", + "SHA384", + "SHA512" +); +ENUM_NEXT(tpm_alg_id_names, TPM2_ALG_NULL, TPM2_ALG_NULL, TPM2_ALG_SHA512, + "NULL" +); +ENUM_NEXT(tpm_alg_id_names, TPM2_ALG_SM3_256, TPM2_ALG_ECMQV, TPM2_ALG_NULL, + "SM3_256", + "SM4", + "RSASSA", + "RSAES", + "RSAPSS", + "OAEP", + "ECDSA", + "ECDH", + "SM2", + "ECSCHNORR", + "ECMQV" +); +ENUM_NEXT(tpm_alg_id_names, TPM2_ALG_KDF1_SP800_56A, TPM2_ALG_ECC, TPM2_ALG_ECMQV, + "KDF1_SP800_56A", + "KDF2", + "KDF1_SP800_108", + "ECC" +); +ENUM_NEXT(tpm_alg_id_names, TPM2_ALG_SYMCIPHER, TPM2_ALG_CAMELLIA, TPM2_ALG_ECC, + "SYMCIPHER", + "CAMELLIA" +); +ENUM_NEXT(tpm_alg_id_names, TPM2_ALG_CTR, TPM2_ALG_ECB, TPM2_ALG_CAMELLIA, + "CTR", + "OFB", + "CBC", + "CFB", + "ECB" +); +ENUM_END(tpm_alg_id_names, TPM2_ALG_ECB); + +/** + * TPM 2.0 ECC curve names + */ +ENUM_BEGIN(tpm_ecc_curve_names, TPM2_ECC_NONE, TPM2_ECC_NIST_P521, + "NONE", + "NIST_P192", + "NIST_P224", + "NIST_P256", + "NIST_P384", + "NIST_P521" +); +ENUM_NEXT(tpm_ecc_curve_names, TPM2_ECC_BN_P256, TPM2_ECC_BN_P638, TPM2_ECC_NIST_P521, + "BN_P256", + "BN_P638" +); +ENUM_NEXT(tpm_ecc_curve_names, TPM2_ECC_SM2_P256, TPM2_ECC_SM2_P256, TPM2_ECC_BN_P638, + "SM2_P256" +); +ENUM_END(tpm_ecc_curve_names, TPM2_ECC_SM2_P256); + +#endif /* TSS_TSS2_V2 */ + diff --git a/src/libtpmtss/tpm_tss_tss2.c b/src/libtpmtss/tpm_tss_tss2_v1.c similarity index 98% rename from src/libtpmtss/tpm_tss_tss2.c rename to src/libtpmtss/tpm_tss_tss2_v1.c index 1b24a42d0..219c425a8 100644 --- a/src/libtpmtss/tpm_tss_tss2.c +++ b/src/libtpmtss/tpm_tss_tss2_v1.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Andreas Steffen + * Copyright (C) 2016-2018 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -16,7 +16,7 @@ #include "tpm_tss_tss2.h" #include "tpm_tss_tss2_names.h" -#ifdef TSS_TSS2 +#ifdef TSS_TSS2_V1 #include #include @@ -24,9 +24,9 @@ #include -#ifdef TSS2_TCTI_TABRMD +#ifdef TSS2_TCTI_TABRMD_V1 #include -#endif /* TSS2_TCTI_TABRMD */ +#endif /* TSS2_TCTI_TABRMD_V1 */ #ifdef TSS2_TCTI_SOCKET #include @@ -1168,7 +1168,7 @@ tpm_tss_t *tpm_tss_tss2_create() { available = initialize_sys_context(this); } - DBG1(DBG_PTS, "TPM 2.0 via TSS2 %savailable", available ? "" : "not "); + DBG1(DBG_PTS, "TPM 2.0 via TSS2 v1 %savailable", available ? "" : "not "); if (!available) { @@ -1178,13 +1178,15 @@ tpm_tss_t *tpm_tss_tss2_create() return &this->public; } -#else /* TSS_TSS2 */ +#else /* TSS_TSS2_V1 */ -tpm_tss_t *tpm_tss_tss2_create() +#ifndef TSS_TSS2_V2 +tpm_tss_t *tpm_tss_tss2_create(void) { return NULL; } +#endif /* !TSS_TSS2_V2 */ -#endif /* TSS_TSS2 */ +#endif /* TSS_TSS2_V1 */ diff --git a/src/libtpmtss/tpm_tss_tss2_v2.c b/src/libtpmtss/tpm_tss_tss2_v2.c new file mode 100644 index 000000000..88e00a029 --- /dev/null +++ b/src/libtpmtss/tpm_tss_tss2_v2.c @@ -0,0 +1,1143 @@ +/* + * Copyright (C) 2018 Andreas Steffen + * HSR Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "tpm_tss_tss2.h" +#include "tpm_tss_tss2_names.h" + +#ifdef TSS_TSS2_V2 + +#include +#include +#include + +#include +#include + +#define LABEL "TPM 2.0 -" + +#define PLATFORM_PCR 24 + +typedef struct private_tpm_tss_tss2_t private_tpm_tss_tss2_t; + +/** + * Private data of an tpm_tss_tss2_t object. + */ +struct private_tpm_tss_tss2_t { + + /** + * Public tpm_tss_tss2_t interface. + */ + tpm_tss_t public; + + /** + * TCTI context + */ + TSS2_TCTI_CONTEXT *tcti_context; + + /** + * SYS context + */ + TSS2_SYS_CONTEXT *sys_context; + + /** + * Number of supported algorithms + */ + size_t supported_algs_count; + + /** + * List of supported algorithms + */ + TPM2_ALG_ID supported_algs[TPM2_PT_ALGORITHM_SET]; +}; + +/** + * Global TCTI dynamic library handle and init function + */ +static void *tcti_handle; + +static TSS2_TCTI_INIT_FUNC tcti_init; + +/** + * Empty AUTH_COMMAND + */ +static const TPMS_AUTH_COMMAND auth_cmd_empty; + +/** + * Convert hash algorithm to TPM2_ALG_ID + */ +static TPM2_ALG_ID hash_alg_to_tpm_alg_id(hash_algorithm_t alg) +{ + switch (alg) + { + case HASH_SHA1: + return TPM2_ALG_SHA1; + case HASH_SHA256: + return TPM2_ALG_SHA256; + case HASH_SHA384: + return TPM2_ALG_SHA384; + case HASH_SHA512: + return TPM2_ALG_SHA512; + default: + return TPM2_ALG_ERROR; + } +} + +/** + * Convert TPM2_ALG_ID to hash algorithm + */ +static hash_algorithm_t hash_alg_from_tpm_alg_id(TPM2_ALG_ID alg) +{ + switch (alg) + { + case TPM2_ALG_SHA1: + return HASH_SHA1; + case TPM2_ALG_SHA256: + return HASH_SHA256; + case TPM2_ALG_SHA384: + return HASH_SHA384; + case TPM2_ALG_SHA512: + return HASH_SHA512; + default: + return HASH_UNKNOWN; + } +} + +/** + * Check if an algorithm given by its TPM2_ALG_ID is supported by the TPM + */ +static bool is_supported_alg(private_tpm_tss_tss2_t *this, TPM2_ALG_ID alg_id) +{ + int i; + + if (alg_id == TPM2_ALG_ERROR) + { + return FALSE; + } + + for (i = 0; i < this->supported_algs_count; i++) + { + if (this->supported_algs[i] == alg_id) + { + return TRUE; + } + } + + return FALSE; +} + +/** + * Get a list of supported algorithms + */ +static bool get_algs_capability(private_tpm_tss_tss2_t *this) +{ + TPMS_CAPABILITY_DATA cap_data; + TPMS_TAGGED_PROPERTY tp; + TPMI_YES_NO more_data; + TPM2_ALG_ID alg; + uint32_t rval, i, offset, revision = 0, year = 0; + size_t len = BUF_LEN; + char buf[BUF_LEN], manufacturer[5], vendor_string[17]; + char *pos = buf; + int written; + + /* get fixed properties */ + rval = Tss2_Sys_GetCapability(this->sys_context, 0, TPM2_CAP_TPM_PROPERTIES, + TPM2_PT_FIXED, TPM2_MAX_TPM_PROPERTIES, + &more_data, &cap_data, 0); + if (rval != TPM2_RC_SUCCESS) + { + DBG1(DBG_PTS, "%s GetCapability failed for TPM2_CAP_TPM_PROPERTIES: 0x%06x", + LABEL, rval); + return FALSE; + } + memset(manufacturer, '\0', sizeof(manufacturer)); + memset(vendor_string, '\0', sizeof(vendor_string)); + + /* print fixed properties */ + for (i = 0; i < cap_data.data.tpmProperties.count; i++) + { + tp = cap_data.data.tpmProperties.tpmProperty[i]; + switch (tp.property) + { + case TPM2_PT_REVISION: + revision = tp.value; + break; + case TPM2_PT_YEAR: + year = tp.value; + break; + case TPM2_PT_MANUFACTURER: + htoun32(manufacturer, tp.value); + break; + case TPM2_PT_VENDOR_STRING_1: + case TPM2_PT_VENDOR_STRING_2: + case TPM2_PT_VENDOR_STRING_3: + case TPM2_PT_VENDOR_STRING_4: + offset = 4 * (tp.property - TPM2_PT_VENDOR_STRING_1); + htoun32(vendor_string + offset, tp.value); + break; + default: + break; + } + } + DBG2(DBG_PTS, "%s manufacturer: %s (%s) rev: %05.2f %u", LABEL, manufacturer, + vendor_string, (float)revision/100, year); + + /* get supported algorithms */ + rval = Tss2_Sys_GetCapability(this->sys_context, 0, TPM2_CAP_ALGS, + 0, TPM2_PT_ALGORITHM_SET, &more_data, &cap_data, 0); + if (rval != TPM2_RC_SUCCESS) + { + DBG1(DBG_PTS, "%s GetCapability failed for TPM2_CAP_ALGS: 0x%06x", + LABEL, rval); + return FALSE; + } + + /* Number of supported algorithms */ + this->supported_algs_count = cap_data.data.algorithms.count; + + /* store and print supported algorithms */ + for (i = 0; i < this->supported_algs_count; i++) + { + alg = cap_data.data.algorithms.algProperties[i].alg; + this->supported_algs[i] = alg; + + written = snprintf(pos, len, " %N", tpm_alg_id_names, alg); + if (written < 0 || written >= len) + { + break; + } + pos += written; + len -= written; + } + DBG2(DBG_PTS, "%s algorithms:%s", LABEL, buf); + + /* get supported ECC curves */ + rval = Tss2_Sys_GetCapability(this->sys_context, 0, TPM2_CAP_ECC_CURVES, + 0, TPM2_PT_LOADED_CURVES, &more_data, &cap_data, 0); + if (rval != TPM2_RC_SUCCESS) + { + DBG1(DBG_PTS, "%s GetCapability failed for TPM2_ECC_CURVES: 0x%06x", + LABEL, rval); + return FALSE; + } + + /* reset print buffer */ + pos = buf; + len = BUF_LEN; + + /* print supported ECC curves */ + for (i = 0; i < cap_data.data.eccCurves.count; i++) + { + written = snprintf(pos, len, " %N", tpm_ecc_curve_names, + cap_data.data.eccCurves.eccCurves[i]); + if (written < 0 || written >= len) + { + break; + } + pos += written; + len -= written; + } + DBG2(DBG_PTS, "%s ECC curves:%s", LABEL, buf); + + return TRUE; +} + +/** + * Initialize TSS2 TCTI context + */ +static bool initialize_tcti_context(private_tpm_tss_tss2_t *this) +{ + size_t tcti_context_size; + uint32_t rval; + + if (!tcti_init) + { + return FALSE; + } + + /* determine size of tcti context */ + rval = tcti_init(NULL, &tcti_context_size, ""); + if (rval != TSS2_RC_SUCCESS) + { + DBG1(DBG_PTS, "%s tcti init setup failed: 0x%06x", LABEL, rval); + return FALSE; + } + + /* allocate and initialize memory for tcti context */ + this->tcti_context = (TSS2_TCTI_CONTEXT*)malloc(tcti_context_size); + memset(this->tcti_context, 0x00, tcti_context_size); + + /* initialize tcti context */ + rval = tcti_init(this->tcti_context, &tcti_context_size, ""); + if (rval != TSS2_RC_SUCCESS) + { + DBG1(DBG_PTS, "%s tcti init allocation failed: 0x%06x", LABEL,rval); + return FALSE; + } + return TRUE; +} + +/** + * Initialize TSS2 Sys context + */ +static bool initialize_sys_context(private_tpm_tss_tss2_t *this) +{ + uint32_t sys_context_size; + uint32_t rval; + + TSS2_ABI_VERSION abi_version = { + .tssCreator = 1, + .tssFamily = 2, + .tssLevel = 1, + .tssVersion = 108 + }; + + /* determine size of sys context */ + sys_context_size = Tss2_Sys_GetContextSize(0); + + /* allocate memory for sys context */ + this->sys_context = (TSS2_SYS_CONTEXT*)malloc(sys_context_size); + + /* initialize sys context */ + rval = Tss2_Sys_Initialize(this->sys_context, sys_context_size, + this->tcti_context, &abi_version); + if (rval != TSS2_RC_SUCCESS) + { + DBG1(DBG_PTS, "%s could not get sys_context: 0x%06x", + LABEL, rval); + return FALSE; + } + + /* get a list of supported algorithms and ECC curves */ + return get_algs_capability(this); +} + +/** + * Finalize TSS context + */ +static void finalize_context(private_tpm_tss_tss2_t *this) +{ + if (this->tcti_context) + { + Tss2_Tcti_Finalize(this->tcti_context); + free(this->tcti_context); + } + if (this->sys_context) + { + Tss2_Sys_Finalize(this->sys_context); + free(this->sys_context); + } +} + +METHOD(tpm_tss_t, get_version, tpm_version_t, + private_tpm_tss_tss2_t *this) +{ + return TPM_VERSION_2_0; +} + +METHOD(tpm_tss_t, get_version_info, chunk_t, + private_tpm_tss_tss2_t *this) +{ + return chunk_empty; +} + +/** + * read the public key portion of a TSS 2.0 AIK key from NVRAM + */ +bool read_public(private_tpm_tss_tss2_t *this, TPMI_DH_OBJECT handle, + TPM2B_PUBLIC *public) +{ + uint32_t rval; + + TPM2B_NAME name = { sizeof(TPM2B_NAME)-2, }; + TPM2B_NAME qualified_name = { sizeof(TPM2B_NAME)-2, }; + TSS2L_SYS_AUTH_RESPONSE auth_rsp; + + + /* read public key for a given object handle from TPM 2.0 NVRAM */ + rval = Tss2_Sys_ReadPublic(this->sys_context, handle, 0, public, &name, + &qualified_name, &auth_rsp); + if (rval != TPM2_RC_SUCCESS) + { + DBG1(DBG_PTS, "%s could not read public key from handle 0x%08x: 0x%06x", + LABEL, handle, rval); + return FALSE; + } + return TRUE; +} + +METHOD(tpm_tss_t, generate_aik, bool, + private_tpm_tss_tss2_t *this, chunk_t ca_modulus, chunk_t *aik_blob, + chunk_t *aik_pubkey, chunk_t *identity_req) +{ + return FALSE; +} + +METHOD(tpm_tss_t, get_public, chunk_t, + private_tpm_tss_tss2_t *this, uint32_t handle) +{ + TPM2B_PUBLIC public = { 0, }; + TPM2_ALG_ID sig_alg, digest_alg; + chunk_t aik_blob, aik_pubkey = chunk_empty; + + if (!read_public(this, handle, &public)) + { + return chunk_empty; + } + + aik_blob = chunk_create((u_char*)&public, sizeof(public)); + DBG3(DBG_LIB, "%s AIK public key blob: %B", LABEL, &aik_blob); + + /* convert TSS 2.0 AIK public key blot into PKCS#1 format */ + switch (public.publicArea.type) + { + case TPM2_ALG_RSA: + { + TPM2B_PUBLIC_KEY_RSA *rsa; + TPMT_RSA_SCHEME *scheme; + chunk_t aik_exponent, aik_modulus; + + scheme = &public.publicArea.parameters.rsaDetail.scheme; + sig_alg = scheme->scheme; + digest_alg = scheme->details.anySig.hashAlg; + + rsa = &public.publicArea.unique.rsa; + aik_modulus = chunk_create(rsa->buffer, rsa->size); + aik_exponent = chunk_from_chars(0x01, 0x00, 0x01); + + /* subjectPublicKeyInfo encoding of AIK RSA key */ + if (!lib->encoding->encode(lib->encoding, PUBKEY_SPKI_ASN1_DER, + NULL, &aik_pubkey, CRED_PART_RSA_MODULUS, aik_modulus, + CRED_PART_RSA_PUB_EXP, aik_exponent, CRED_PART_END)) + { + DBG1(DBG_PTS, "%s subjectPublicKeyInfo encoding of AIK key " + "failed", LABEL); + return chunk_empty; + } + break; + } + case TPM2_ALG_ECC: + { + TPMS_ECC_POINT *ecc; + TPMT_ECC_SCHEME *scheme; + chunk_t ecc_point; + uint8_t *pos; + + scheme = &public.publicArea.parameters.eccDetail.scheme; + sig_alg = scheme->scheme; + digest_alg = scheme->details.anySig.hashAlg; + + ecc = &public.publicArea.unique.ecc; + + /* allocate space for bit string */ + pos = asn1_build_object(&ecc_point, ASN1_BIT_STRING, + 2 + ecc->x.size + ecc->y.size); + /* bit string length is a multiple of octets */ + *pos++ = 0x00; + /* uncompressed ECC point format */ + *pos++ = 0x04; + /* copy x coordinate of ECC point */ + memcpy(pos, ecc->x.buffer, ecc->x.size); + pos += ecc->x.size; + /* copy y coordinate of ECC point */ + memcpy(pos, ecc->y.buffer, ecc->y.size); + /* subjectPublicKeyInfo encoding of AIK ECC key */ + aik_pubkey = asn1_wrap(ASN1_SEQUENCE, "mm", + asn1_wrap(ASN1_SEQUENCE, "mm", + asn1_build_known_oid(OID_EC_PUBLICKEY), + asn1_build_known_oid(ecc->x.size == 32 ? + OID_PRIME256V1 : OID_SECT384R1)), + ecc_point); + break; + } + default: + DBG1(DBG_PTS, "%s unsupported AIK key type", LABEL); + return chunk_empty; + } + DBG1(DBG_PTS, "AIK signature algorithm is %N with %N hash", + tpm_alg_id_names, sig_alg, tpm_alg_id_names, digest_alg); + return aik_pubkey; +} + +/** + * Configure a PCR Selection assuming a maximum of 24 registers + */ +static bool init_pcr_selection(private_tpm_tss_tss2_t *this, uint32_t pcrs, + hash_algorithm_t alg, TPML_PCR_SELECTION *pcr_sel) +{ + TPM2_ALG_ID alg_id; + uint32_t pcr; + + /* check if hash algorithm is supported by TPM */ + alg_id = hash_alg_to_tpm_alg_id(alg); + if (!is_supported_alg(this, alg_id)) + { + DBG1(DBG_PTS, "%s %N hash algorithm not supported by TPM", + LABEL, hash_algorithm_short_names, alg); + return FALSE; + } + + /* initialize the PCR Selection structure,*/ + pcr_sel->count = 1; + pcr_sel->pcrSelections[0].hash = alg_id; + pcr_sel->pcrSelections[0].sizeofSelect = 3; + pcr_sel->pcrSelections[0].pcrSelect[0] = 0; + pcr_sel->pcrSelections[0].pcrSelect[1] = 0; + pcr_sel->pcrSelections[0].pcrSelect[2] = 0; + + /* set the selected PCRs */ + for (pcr = 0; pcr < PLATFORM_PCR; pcr++) + { + if (pcrs & (1 << pcr)) + { + pcr_sel->pcrSelections[0].pcrSelect[pcr / 8] |= ( 1 << (pcr % 8) ); + } + } + return TRUE; +} + +METHOD(tpm_tss_t, read_pcr, bool, + private_tpm_tss_tss2_t *this, uint32_t pcr_num, chunk_t *pcr_value, + hash_algorithm_t alg) +{ + TPML_PCR_SELECTION pcr_selection; + TPML_DIGEST pcr_values; + + uint32_t pcr_update_counter, rval; + uint8_t *pcr_value_ptr; + size_t pcr_value_len; + + if (pcr_num >= PLATFORM_PCR) + { + DBG1(DBG_PTS, "%s maximum number of supported PCR is %d", + LABEL, PLATFORM_PCR); + return FALSE; + } + + if (!init_pcr_selection(this, (1 << pcr_num), alg, &pcr_selection)) + { + return FALSE; + } + + /* initialize the PCR Digest structure */ + memset(&pcr_values, 0, sizeof(TPML_DIGEST)); + + /* read the PCR value */ + rval = Tss2_Sys_PCR_Read(this->sys_context, 0, &pcr_selection, + &pcr_update_counter, &pcr_selection, &pcr_values, 0); + if (rval != TPM2_RC_SUCCESS) + { + DBG1(DBG_PTS, "%s PCR bank could not be read: 0x%60x", + LABEL, rval); + return FALSE; + } + pcr_value_ptr = (uint8_t *)pcr_values.digests[0].buffer; + pcr_value_len = (size_t) pcr_values.digests[0].size; + + *pcr_value = chunk_clone(chunk_create(pcr_value_ptr, pcr_value_len)); + + return TRUE; +} + +METHOD(tpm_tss_t, extend_pcr, bool, + private_tpm_tss_tss2_t *this, uint32_t pcr_num, chunk_t *pcr_value, + chunk_t data, hash_algorithm_t alg) +{ + uint32_t rval; + TPM2_ALG_ID alg_id; + TPML_DIGEST_VALUES digest_values; + TSS2L_SYS_AUTH_COMMAND auth_cmd = { 1, { auth_cmd_empty } }; + TSS2L_SYS_AUTH_RESPONSE auth_rsp; + + auth_cmd.auths[0].sessionHandle = TPM2_RS_PW; + + /* check if hash algorithm is supported by TPM */ + alg_id = hash_alg_to_tpm_alg_id(alg); + if (!is_supported_alg(this, alg_id)) + { + DBG1(DBG_PTS, "%s %N hash algorithm not supported by TPM", + LABEL, hash_algorithm_short_names, alg); + return FALSE; + } + + digest_values.count = 1; + digest_values.digests[0].hashAlg = alg_id; + + switch (alg) + { + case HASH_SHA1: + if (data.len != HASH_SIZE_SHA1) + { + return FALSE; + } + memcpy(digest_values.digests[0].digest.sha1, data.ptr, + HASH_SIZE_SHA1); + break; + case HASH_SHA256: + if (data.len != HASH_SIZE_SHA256) + { + return FALSE; + } + memcpy(digest_values.digests[0].digest.sha256, data.ptr, + HASH_SIZE_SHA256); + break; + case HASH_SHA384: + if (data.len != HASH_SIZE_SHA384) + { + return FALSE; + } + memcpy(digest_values.digests[0].digest.sha384, data.ptr, + HASH_SIZE_SHA384); + break; + case HASH_SHA512: + if (data.len != HASH_SIZE_SHA512) + { + return FALSE; + } + memcpy(digest_values.digests[0].digest.sha512, data.ptr, + HASH_SIZE_SHA512); + break; + default: + return FALSE; + } + + /* extend PCR */ + rval = Tss2_Sys_PCR_Extend(this->sys_context, pcr_num, &auth_cmd, + &digest_values, &auth_rsp); + if (rval != TPM2_RC_SUCCESS) + { + DBG1(DBG_PTS, "%s PCR %02u could not be extended: 0x%06x", + LABEL, pcr_num, rval); + return FALSE; + } + + /* get updated PCR value */ + return read_pcr(this, pcr_num, pcr_value, alg); +} + +METHOD(tpm_tss_t, quote, bool, + private_tpm_tss_tss2_t *this, uint32_t aik_handle, uint32_t pcr_sel, + hash_algorithm_t alg, chunk_t data, tpm_quote_mode_t *quote_mode, + tpm_tss_quote_info_t **quote_info, chunk_t *quote_sig) +{ + chunk_t quoted_chunk, qualified_signer, extra_data, clock_info, + firmware_version, pcr_select, pcr_digest; + hash_algorithm_t pcr_digest_alg; + bio_reader_t *reader; + uint32_t rval; + + TPM2B_DATA qualifying_data; + TPML_PCR_SELECTION pcr_selection; + TPM2B_ATTEST quoted = { sizeof(TPM2B_ATTEST)-2, }; + TPMT_SIG_SCHEME scheme; + TPMT_SIGNATURE sig; + TPMI_ALG_HASH hash_alg; + TSS2L_SYS_AUTH_COMMAND auth_cmd = { 1, { auth_cmd_empty } }; + TSS2L_SYS_AUTH_RESPONSE auth_rsp; + + auth_cmd.auths[0].sessionHandle = TPM2_RS_PW; + + qualifying_data.size = data.len; + memcpy(qualifying_data.buffer, data.ptr, data.len); + + scheme.scheme = TPM2_ALG_NULL; + memset(&sig, 0x00, sizeof(sig)); + + /* set Quote mode */ + *quote_mode = TPM_QUOTE_TPM2; + + if (!init_pcr_selection(this, pcr_sel, alg, &pcr_selection)) + { + return FALSE; + } + + rval = Tss2_Sys_Quote(this->sys_context, aik_handle, &auth_cmd, + &qualifying_data, &scheme, &pcr_selection, "ed, + &sig, &auth_rsp); + if (rval != TPM2_RC_SUCCESS) + { + DBG1(DBG_PTS,"%s Tss2_Sys_Quote failed: 0x%06x", LABEL, rval); + return FALSE; + } + quoted_chunk = chunk_create(quoted.attestationData, quoted.size); + + reader = bio_reader_create(chunk_skip(quoted_chunk, 6)); + if (!reader->read_data16(reader, &qualified_signer) || + !reader->read_data16(reader, &extra_data) || + !reader->read_data (reader, 17, &clock_info) || + !reader->read_data (reader, 8, &firmware_version) || + !reader->read_data (reader, 10, &pcr_select) || + !reader->read_data16(reader, &pcr_digest)) + { + DBG1(DBG_PTS, "%s parsing of quoted struct failed", LABEL); + reader->destroy(reader); + return FALSE; + } + reader->destroy(reader); + + DBG2(DBG_PTS, "PCR Composite digest: %B", &pcr_digest); + DBG2(DBG_PTS, "TPM Quote Info: %B", "ed_chunk); + DBG2(DBG_PTS, "qualifiedSigner: %B", &qualified_signer); + DBG2(DBG_PTS, "extraData: %B", &extra_data); + DBG2(DBG_PTS, "clockInfo: %B", &clock_info); + DBG2(DBG_PTS, "firmwareVersion: %B", &firmware_version); + DBG2(DBG_PTS, "pcrSelect: %B", &pcr_select); + + /* extract signature */ + switch (sig.sigAlg) + { + case TPM2_ALG_RSASSA: + case TPM2_ALG_RSAPSS: + *quote_sig = chunk_clone( + chunk_create( + sig.signature.rsassa.sig.buffer, + sig.signature.rsassa.sig.size)); + hash_alg = sig.signature.rsassa.hash; + break; + case TPM2_ALG_ECDSA: + case TPM2_ALG_ECDAA: + case TPM2_ALG_SM2: + case TPM2_ALG_ECSCHNORR: + *quote_sig = chunk_cat("cc", + chunk_create( + sig.signature.ecdsa.signatureR.buffer, + sig.signature.ecdsa.signatureR.size), + chunk_create( + sig.signature.ecdsa.signatureS.buffer, + sig.signature.ecdsa.signatureS.size)); + hash_alg = sig.signature.ecdsa.hash; + break; + default: + DBG1(DBG_PTS, "%s unsupported %N signature algorithm", + LABEL, tpm_alg_id_names, sig.sigAlg); + return FALSE; + }; + + DBG2(DBG_PTS, "PCR digest algorithm is %N", tpm_alg_id_names, hash_alg); + pcr_digest_alg = hash_alg_from_tpm_alg_id(hash_alg); + + DBG2(DBG_PTS, "TPM Quote Signature: %B", quote_sig); + + /* Create and initialize Quote Info object */ + *quote_info = tpm_tss_quote_info_create(*quote_mode, pcr_digest_alg, + pcr_digest); + (*quote_info)->set_tpm2_info(*quote_info, qualified_signer, clock_info, + pcr_select); + (*quote_info)->set_version_info(*quote_info, firmware_version); + + return TRUE; +} + +METHOD(tpm_tss_t, sign, bool, + private_tpm_tss_tss2_t *this, uint32_t hierarchy, uint32_t handle, + signature_scheme_t scheme, chunk_t data, chunk_t pin, chunk_t *signature) +{ + key_type_t key_type; + hash_algorithm_t hash_alg; + uint32_t rval; + + TPM2_ALG_ID alg_id; + TPM2B_MAX_BUFFER buffer; + TPM2B_DIGEST hash = { sizeof(TPM2B_DIGEST)-2, }; + TPMT_TK_HASHCHECK validation; + TPM2B_PUBLIC public = { 0, }; + TPMT_SIG_SCHEME sig_scheme; + TPMT_SIGNATURE sig; + TPMS_AUTH_COMMAND *cmd; + TSS2L_SYS_AUTH_COMMAND auth_cmd = { 1, { auth_cmd_empty } }; + TSS2L_SYS_AUTH_RESPONSE auth_rsp; + + cmd = &auth_cmd.auths[0]; + cmd->sessionHandle = TPM2_RS_PW; + + if (pin.len > 0) + { + cmd->hmac.size = min(sizeof(cmd->hmac)-2, pin.len); + memcpy(cmd->hmac.buffer, pin.ptr, cmd->hmac.size); + } + + key_type = key_type_from_signature_scheme(scheme); + hash_alg = hasher_from_signature_scheme(scheme, NULL); + + /* Check if hash algorithm is supported by TPM */ + alg_id = hash_alg_to_tpm_alg_id(hash_alg); + if (!is_supported_alg(this, alg_id)) + { + DBG1(DBG_PTS, "%s %N hash algorithm not supported by TPM", + LABEL, hash_algorithm_short_names, hash_alg); + return FALSE; + } + + /* Get public key */ + if (!read_public(this, handle, &public)) + { + return FALSE; + } + + if (key_type == KEY_RSA && public.publicArea.type == TPM2_ALG_RSA) + { + sig_scheme.scheme = TPM2_ALG_RSASSA; + sig_scheme.details.rsassa.hashAlg = alg_id; + } + else if (key_type == KEY_ECDSA && public.publicArea.type == TPM2_ALG_ECC) + { + sig_scheme.scheme = TPM2_ALG_ECDSA; + sig_scheme.details.ecdsa.hashAlg = alg_id; + + } + else + { + DBG1(DBG_PTS, "%s signature scheme %N not supported by TPM key", + LABEL, signature_scheme_names, scheme); + return FALSE; + } + + if (data.len <= TPM2_MAX_DIGEST_BUFFER) + { + memcpy(buffer.buffer, data.ptr, data.len); + buffer.size = data.len; + + rval = Tss2_Sys_Hash(this->sys_context, 0, &buffer, alg_id, hierarchy, + &hash, &validation, 0); + if (rval != TPM2_RC_SUCCESS) + { + DBG1(DBG_PTS,"%s Tss2_Sys_Hash failed: 0x%06x", LABEL, rval); + return FALSE; + } + } + else + { + TPMI_DH_OBJECT sequence_handle; + TPM2B_AUTH null_auth; + + null_auth.size = 0; + rval = Tss2_Sys_HashSequenceStart(this->sys_context, 0, &null_auth, + alg_id, &sequence_handle, 0); + if (rval != TPM2_RC_SUCCESS) + { + DBG1(DBG_PTS,"%s Tss2_Sys_HashSequenceStart failed: 0x%06x", + LABEL, rval); + return FALSE; + } + + while (data.len > 0) + { + buffer.size = min(data.len, TPM2_MAX_DIGEST_BUFFER); + memcpy(buffer.buffer, data.ptr, buffer.size); + data.ptr += buffer.size; + data.len -= buffer.size; + + rval = Tss2_Sys_SequenceUpdate(this->sys_context, sequence_handle, + &auth_cmd, &buffer, 0); + if (rval != TPM2_RC_SUCCESS) + { + DBG1(DBG_PTS,"%s Tss2_Sys_SequenceUpdate failed: 0x%06x", + LABEL, rval); + return FALSE; + } + } + buffer.size = 0; + + rval = Tss2_Sys_SequenceComplete(this->sys_context, sequence_handle, + &auth_cmd, &buffer, hierarchy, + &hash, &validation, 0); + if (rval != TPM2_RC_SUCCESS) + { + DBG1(DBG_PTS,"%s Tss2_Sys_SequenceComplete failed: 0x%06x", + LABEL, rval); + return FALSE; + } + } + + rval = Tss2_Sys_Sign(this->sys_context, handle, &auth_cmd, &hash, + &sig_scheme, &validation, &sig, &auth_rsp); + if (rval != TPM2_RC_SUCCESS) + { + DBG1(DBG_PTS,"%s Tss2_Sys_Sign failed: 0x%06x", LABEL, rval); + return FALSE; + } + + /* extract signature */ + switch (scheme) + { + case SIGN_RSA_EMSA_PKCS1_SHA1: + case SIGN_RSA_EMSA_PKCS1_SHA2_256: + case SIGN_RSA_EMSA_PKCS1_SHA2_384: + case SIGN_RSA_EMSA_PKCS1_SHA2_512: + *signature = chunk_clone( + chunk_create( + sig.signature.rsassa.sig.buffer, + sig.signature.rsassa.sig.size)); + break; + case SIGN_ECDSA_256: + case SIGN_ECDSA_384: + case SIGN_ECDSA_521: + *signature = chunk_cat("cc", + chunk_create( + sig.signature.ecdsa.signatureR.buffer, + sig.signature.ecdsa.signatureR.size), + chunk_create( + sig.signature.ecdsa.signatureS.buffer, + sig.signature.ecdsa.signatureS.size)); + break; + case SIGN_ECDSA_WITH_SHA256_DER: + case SIGN_ECDSA_WITH_SHA384_DER: + case SIGN_ECDSA_WITH_SHA512_DER: + *signature = asn1_wrap(ASN1_SEQUENCE, "mm", + asn1_integer("c", + chunk_create( + sig.signature.ecdsa.signatureR.buffer, + sig.signature.ecdsa.signatureR.size)), + asn1_integer("c", + chunk_create( + sig.signature.ecdsa.signatureS.buffer, + sig.signature.ecdsa.signatureS.size))); + break; + default: + DBG1(DBG_PTS, "%s unsupported %N signature scheme", + LABEL, signature_scheme_names, scheme); + return FALSE; + }; + + return TRUE; +} + +METHOD(tpm_tss_t, get_random, bool, + private_tpm_tss_tss2_t *this, size_t bytes, uint8_t *buffer) +{ + size_t len, random_len= sizeof(TPM2B_DIGEST)-2; + TPM2B_DIGEST random = { random_len, }; + uint8_t *pos = buffer; + uint32_t rval; + + while (bytes > 0) + { + len = min(bytes, random_len); + + rval = Tss2_Sys_GetRandom(this->sys_context, NULL, len, &random, NULL); + if (rval != TSS2_RC_SUCCESS) + { + DBG1(DBG_PTS,"%s Tss2_Sys_GetRandom failed: 0x%06x", LABEL, rval); + return FALSE; + } + memcpy(pos, random.buffer, random.size); + pos += random.size; + bytes -= random.size; + } + + return TRUE; +} + +METHOD(tpm_tss_t, get_data, bool, + private_tpm_tss_tss2_t *this, uint32_t hierarchy, uint32_t handle, + chunk_t pin, chunk_t *data) +{ + uint16_t max_data_size, nv_size, nv_offset = 0; + uint32_t rval; + + TPMS_CAPABILITY_DATA cap_data; + TPMI_YES_NO more_data; + TPM2B_NAME nv_name = { sizeof(TPM2B_NAME)-2, }; + TPM2B_NV_PUBLIC nv_public = { 0, }; + TPM2B_MAX_NV_BUFFER nv_data = { TPM2_MAX_NV_BUFFER_SIZE, }; + TPMS_AUTH_COMMAND *cmd; + TSS2L_SYS_AUTH_COMMAND auth_cmd = { 1, { auth_cmd_empty } }; + TSS2L_SYS_AUTH_RESPONSE auth_rsp; + + /* query maximum TPM data transmission size */ + rval = Tss2_Sys_GetCapability(this->sys_context, 0, TPM2_CAP_TPM_PROPERTIES, + TPM2_PT_NV_BUFFER_MAX, 1, &more_data, &cap_data, 0); + if (rval != TPM2_RC_SUCCESS) + { + DBG1(DBG_PTS,"%s Tss2_Sys_GetCapability failed for " + "TPM2_CAP_TPM_PROPERTIES: 0x%06x", LABEL, rval); + return FALSE; + } + max_data_size = min(cap_data.data.tpmProperties.tpmProperty[0].value, + TPM2_MAX_NV_BUFFER_SIZE); + + /* get size of NV object */ + rval = Tss2_Sys_NV_ReadPublic(this->sys_context, handle, 0, &nv_public, + &nv_name, 0); + if (rval != TPM2_RC_SUCCESS) + { + DBG1(DBG_PTS,"%s Tss2_Sys_NV_ReadPublic failed: 0x%06x", LABEL, rval); + return FALSE; + } + nv_size = nv_public.nvPublic.dataSize; + *data = chunk_alloc(nv_size); + + /* prepare NV read session */ + cmd = &auth_cmd.auths[0]; + cmd->sessionHandle = TPM2_RS_PW; + + if (pin.len > 0) + { + cmd->hmac.size = min(sizeof(cmd->hmac)-2, pin.len); + memcpy(cmd->hmac.buffer, pin.ptr, cmd->hmac.size); + } + + /* read NV data a maximum data size block at a time */ + while (nv_size > 0) + { + rval = Tss2_Sys_NV_Read(this->sys_context, hierarchy, handle, &auth_cmd, + min(nv_size, max_data_size), nv_offset, &nv_data, &auth_rsp); + + if (rval != TPM2_RC_SUCCESS) + { + DBG1(DBG_PTS,"%s Tss2_Sys_NV_Read failed: 0x%06x", LABEL, rval); + chunk_free(data); + return FALSE; + } + memcpy(data->ptr + nv_offset, nv_data.buffer, nv_data.size); + nv_offset += nv_data.size; + nv_size -= nv_data.size; + } + + return TRUE; +} + +METHOD(tpm_tss_t, destroy, void, + private_tpm_tss_tss2_t *this) +{ + finalize_context(this); + free(this); +} + +/** + * See header + */ +tpm_tss_t *tpm_tss_tss2_create() +{ + private_tpm_tss_tss2_t *this; + bool available; + + INIT(this, + .public = { + .get_version = _get_version, + .get_version_info = _get_version_info, + .generate_aik = _generate_aik, + .get_public = _get_public, + .read_pcr = _read_pcr, + .extend_pcr = _extend_pcr, + .quote = _quote, + .sign = _sign, + .get_random = _get_random, + .get_data = _get_data, + .destroy = _destroy, + }, + ); + + available = initialize_tcti_context(this); + if (available) + { + available = initialize_sys_context(this); + } + DBG1(DBG_PTS, "TPM 2.0 via TSS2 v2 %savailable", available ? "" : "not "); + + if (!available) + { + destroy(this); + return NULL; + } + return &this->public; +} + +/** + * See header + */ +bool tpm_tss_tss2_init(void) +{ + TSS2_TCTI_INFO_FUNC infofn; + const TSS2_TCTI_INFO *info; + char tcti_lib_format[] = "libtss2-tcti-%s.so.0"; + char tcti_lib[BUF_LEN]; + char *tcti_names[] = { "tabrmd", "device", "mssim" }; + char *tcti_name; + bool match = FALSE; + int i; + + /* select a dynamic TCTI library */ + tcti_name = lib->settings->get_str(lib->settings, + "%s.plugins.tpm.tcti.name", tcti_names[0], lib->ns); + snprintf(tcti_lib, BUF_LEN, tcti_lib_format, tcti_name); + + for (i = 0; i < countof(tcti_names); i++) + { + if (streq(tcti_name, tcti_names[i])) + { + match = TRUE; + } + } + if (!match) + { + DBG1(DBG_PTS, "%s \"%s\" is not a valid TCTI library name", + LABEL, tcti_lib); + return FALSE; + } + + /* open the selected dynamic TCTI library */ + tcti_handle = dlopen(tcti_lib, RTLD_LAZY); + if (!tcti_handle) + { + DBG1(DBG_PTS, "%s could not load \"%s\"", LABEL, tcti_lib); + return FALSE; + } + + infofn = (TSS2_TCTI_INFO_FUNC)dlsym(tcti_handle, TSS2_TCTI_INFO_SYMBOL); + if (!infofn) + { + DBG1(DBG_PTS, "%s symbol \"%s\" not found in \"%s\"", LABEL, + TSS2_TCTI_INFO_SYMBOL, tcti_lib); + tpm_tss_tss2_deinit(); + + return FALSE; + } + DBG2(DBG_PTS, "%s \"%s\" successfully loaded", LABEL, tcti_lib); + info = infofn(); + tcti_init = info->init; + + return TRUE; +} + +/** + * See header + */ +void tpm_tss_tss2_deinit(void) +{ + dlclose(tcti_handle); + tcti_handle = tcti_init = NULL; +} + +#else /* TSS_TSS2_V2 */ + +/** + * See header + */ +bool tpm_tss_tss2_init(void) +{ + return TRUE; +} + +/** + * See header + */ +void tpm_tss_tss2_deinit(void) +{ + /* empty */ +} + +#endif /* TSS_TSS2_V2 */ +