From: Andreas Steffen Date: Tue, 5 Dec 2017 16:08:55 +0000 (+0100) Subject: libtpmtss: Load X.509 certificates from TPM 2.0 NV RAM X-Git-Tag: 5.6.2dr1~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e850d000b84146fa23dcaf2c41ff1dafe986a077;p=thirdparty%2Fstrongswan.git libtpmtss: Load X.509 certificates from TPM 2.0 NV RAM --- diff --git a/src/libtpmtss/plugins/tpm/Makefile.am b/src/libtpmtss/plugins/tpm/Makefile.am index 2812810220..27db5cc014 100644 --- a/src/libtpmtss/plugins/tpm/Makefile.am +++ b/src/libtpmtss/plugins/tpm/Makefile.am @@ -15,6 +15,7 @@ endif libstrongswan_tpm_la_SOURCES = \ tpm_plugin.h tpm_plugin.c \ + tpm_cert.h tpm_cert.c \ tpm_private_key.h tpm_private_key.c \ tpm_rng.h tpm_rng.c diff --git a/src/libtpmtss/plugins/tpm/tpm_cert.c b/src/libtpmtss/plugins/tpm/tpm_cert.c new file mode 100644 index 0000000000..248da7e539 --- /dev/null +++ b/src/libtpmtss/plugins/tpm/tpm_cert.c @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2017 Andreas Steffen + * HSR Hochschule für 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_cert.h" + +#include + +#include + + +/** + * See header. + */ +certificate_t *tpm_cert_load(certificate_type_t type, va_list args) +{ + tpm_tss_t *tpm; + chunk_t keyid = chunk_empty, pin = chunk_empty, data = chunk_empty; + certificate_t *cert; + char handle_str[4]; + size_t len; + uint32_t hierarchy = 0x40000001; /* TPM_RH_OWNER */ + uint32_t handle; + bool success; + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_PKCS11_KEYID: + keyid = va_arg(args, chunk_t); + continue; + case BUILD_PKCS11_SLOT: + hierarchy = va_arg(args, int); + continue; + case BUILD_PKCS11_MODULE: + va_arg(args, char*); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + + /* convert keyid into 32 bit TPM key object handle */ + if (!keyid.len) + { + return NULL; + } + len = min(keyid.len, 4); + memset(handle_str, 0x00, 4); + memcpy(handle_str + 4 - len, keyid.ptr + keyid.len - len, len); + handle = untoh32(handle_str); + + /* try to find a TPM 2.0 */ + tpm = tpm_tss_probe(TPM_VERSION_2_0); + if (!tpm) + { + DBG1(DBG_LIB, "no TPM 2.0 found"); + return NULL; + } + success = tpm->get_data(tpm, hierarchy, handle, pin, &data); + tpm->destroy(tpm); + + if (!success) + { + DBG1(DBG_LIB, "loading certificate from TPM NV index 0x%08x failed", + handle); + return NULL; + } + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_BLOB_ASN1_DER, data, BUILD_END); + free(data.ptr); + + if (!cert) + { + DBG1(DBG_LIB, "parsing certificate from TPM NV index 0x%08x failed", + handle); + return NULL; + } + DBG1(DBG_LIB, "loaded certificate from TPM NV index 0x%08x", handle); + + return cert; +} diff --git a/src/libtpmtss/plugins/tpm/tpm_cert.h b/src/libtpmtss/plugins/tpm/tpm_cert.h new file mode 100644 index 0000000000..a6cb34554b --- /dev/null +++ b/src/libtpmtss/plugins/tpm/tpm_cert.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2017 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. + */ + +/** + * @defgroup tpm_cert tpm_cert + * @{ @ingroup tpm + */ + +#ifndef TPM_CERT_H_ +#define TPM_CERT_H_ + +#include + +/** + * Load a specific certificate from a TPM + * + * Requires a BUILD_PKCS11_KEYID argument, and optionally a BUILD_PKCS11_SLOT + * to designate the NV storage hierarchy. + * + * @param type certificate type, must be CERT_X509 + * @param args variable argument list, containing BUILD_PKCS11_KEYID. + * @return loaded certificate, or NULL on failure + */ +certificate_t *tpm_cert_load(certificate_type_t type, va_list args); + +#endif /** TPM_CERT_H_ @}*/ diff --git a/src/libtpmtss/plugins/tpm/tpm_plugin.c b/src/libtpmtss/plugins/tpm/tpm_plugin.c index b9a4c12a84..e98899852f 100644 --- a/src/libtpmtss/plugins/tpm/tpm_plugin.c +++ b/src/libtpmtss/plugins/tpm/tpm_plugin.c @@ -15,6 +15,7 @@ #include "tpm_plugin.h" #include "tpm_private_key.h" +#include "tpm_cert.h" #include "tpm_rng.h" #include @@ -50,13 +51,19 @@ METHOD(plugin_t, get_features, int, PLUGIN_REGISTER(PRIVKEY, tpm_private_key_connect, FALSE), PLUGIN_PROVIDE(PRIVKEY, KEY_ANY), }; - static plugin_feature_t f[countof(f_rng) + countof(f_privkey)] = {}; - + static plugin_feature_t f_cert[] = { + PLUGIN_REGISTER(CERT_DECODE, tpm_cert_load, FALSE), + PLUGIN_PROVIDE(CERT_DECODE, CERT_X509), + PLUGIN_DEPENDS(CERT_DECODE, CERT_X509), + }; + static plugin_feature_t f[countof(f_rng) + countof(f_privkey) + + countof(f_cert)] = {}; static int count = 0; if (!count) { plugin_features_add(f, f_privkey, countof(f_privkey), &count); + plugin_features_add(f, f_cert, countof(f_cert), &count); if (lib->settings->get_bool(lib->settings, "%s.plugins.tpm.use_rng", FALSE, lib->ns)) diff --git a/src/libtpmtss/tpm_tss.h b/src/libtpmtss/tpm_tss.h index f408d04408..bcb7ab949f 100644 --- a/src/libtpmtss/tpm_tss.h +++ b/src/libtpmtss/tpm_tss.h @@ -143,6 +143,18 @@ struct tpm_tss_t { */ bool (*get_random)(tpm_tss_t *this, size_t bytes, uint8_t *buffer); + /** + * Get a data blob from TPM NV store using its object handle (TPM 2.0 only) + * + * @param handle object handle of TPM key to be used for signature + * @param hierarchy hierarchy the TPM key object is attached to + * @param pin PIN code or empty chunk + * @param data returns data blob + * @return TRUE if data retrieval succeeded + */ + bool (*get_data)(tpm_tss_t *this, uint32_t hierarchy, uint32_t handle, + chunk_t pin, chunk_t *data); + /** * Destroy a tpm_tss_t. */ diff --git a/src/libtpmtss/tpm_tss_trousers.c b/src/libtpmtss/tpm_tss_trousers.c index d5bc2b84f1..6ed57af9d0 100644 --- a/src/libtpmtss/tpm_tss_trousers.c +++ b/src/libtpmtss/tpm_tss_trousers.c @@ -595,6 +595,13 @@ METHOD(tpm_tss_t, get_random, bool, return FALSE; } +METHOD(tpm_tss_t, get_data, bool, + private_tpm_tss_trousers_t *this, uint32_t hierarchy, uint32_t handle, + chunk_t pin, chunk_t *data) +{ + return FALSE; +} + METHOD(tpm_tss_t, destroy, void, private_tpm_tss_trousers_t *this) { @@ -639,6 +646,7 @@ tpm_tss_t *tpm_tss_trousers_create() .quote = _quote, .sign = _sign, .get_random = _get_random, + .get_data = _get_data, .destroy = _destroy, }, .load_aik = _load_aik, diff --git a/src/libtpmtss/tpm_tss_tss2.c b/src/libtpmtss/tpm_tss_tss2.c index 0136c27554..2e33589c1b 100644 --- a/src/libtpmtss/tpm_tss_tss2.c +++ b/src/libtpmtss/tpm_tss_tss2.c @@ -954,6 +954,78 @@ METHOD(tpm_tss_t, get_random, bool, 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 nv_size, nv_offset = 0; + uint32_t rval; + + TPM2B_NAME nv_name = { { sizeof(TPM2B_NAME)-2, } }; + TPM2B_NV_PUBLIC nv_public = { { 0, } }; + TPM2B_MAX_NV_BUFFER nv_data = { { sizeof(TPM2B_MAX_NV_BUFFER)-2, } }; + TPMS_AUTH_COMMAND session_data_cmd; + TPMS_AUTH_RESPONSE session_data_rsp; + TSS2_SYS_CMD_AUTHS sessions_data_cmd; + TSS2_SYS_RSP_AUTHS sessions_data_rsp; + TPMS_AUTH_COMMAND *session_data_cmd_array[1]; + TPMS_AUTH_RESPONSE *session_data_rsp_array[1]; + + /* get size of NV object */ + rval = Tss2_Sys_NV_ReadPublic(this->sys_context, handle, 0, &nv_public, + &nv_name, 0); + if (rval != TPM_RC_SUCCESS) + { + DBG1(DBG_PTS,"%s Tss2_Sys_NV_ReadPublic failed: 0x%06x", LABEL, rval); + return FALSE; + } + nv_size = nv_public.t.nvPublic.dataSize; + *data = chunk_alloc(nv_size); + + /*prepare NV read session */ + session_data_cmd_array[0] = &session_data_cmd; + session_data_rsp_array[0] = &session_data_rsp; + + sessions_data_cmd.cmdAuths = &session_data_cmd_array[0]; + sessions_data_rsp.rspAuths = &session_data_rsp_array[0]; + + sessions_data_cmd.cmdAuthsCount = 1; + sessions_data_rsp.rspAuthsCount = 1; + + session_data_cmd.sessionHandle = TPM_RS_PW; + session_data_cmd.nonce.t.size = 0; + session_data_cmd.hmac.t.size = 0; + + if (pin.len > 0) + { + session_data_cmd.hmac.t.size = min(sizeof(session_data_cmd.hmac.t) - 2, + pin.len); + memcpy(session_data_cmd.hmac.t.buffer, pin.ptr, + session_data_cmd.hmac.t.size); + } + *( (uint8_t *)((void *)&session_data_cmd.sessionAttributes ) ) = 0; + + /* read NV data an NV buffer block at a time */ + while (nv_size > 0) + { + rval = Tss2_Sys_NV_Read(this->sys_context, hierarchy, handle, + &sessions_data_cmd, min(nv_size, MAX_NV_BUFFER_SIZE), + nv_offset, &nv_data, &sessions_data_rsp); + + if (rval != TPM_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.t.buffer, nv_data.t.size); + nv_offset += nv_data.t.size; + nv_size -= nv_data.t.size; + } + + return TRUE; +} + METHOD(tpm_tss_t, destroy, void, private_tpm_tss_tss2_t *this) { @@ -980,6 +1052,7 @@ tpm_tss_t *tpm_tss_tss2_create() .quote = _quote, .sign = _sign, .get_random = _get_random, + .get_data = _get_data, .destroy = _destroy, }, );