diff options
author | Andreas Steffen <andreas.steffen@strongswan.org> | 2016-06-26 18:40:01 +0200 |
---|---|---|
committer | Andreas Steffen <andreas.steffen@strongswan.org> | 2016-06-26 18:40:01 +0200 |
commit | 4b1513ed5973df0c05cd87009acd19dec78a2263 (patch) | |
tree | 1ecb249cd7d23120bf79a18b3a5d2fd8f4cd07a5 /src | |
parent | 5a09734c2cabe419419496a36445be65f8791e10 (diff) | |
parent | b03159364136de391f71046289ba7e08f687ddb7 (diff) | |
download | strongswan-4b1513ed5973df0c05cd87009acd19dec78a2263.tar.bz2 strongswan-4b1513ed5973df0c05cd87009acd19dec78a2263.tar.xz |
Merge branch 'tpm2'
The libtpmtss library supports both TPM 1.2 and TPM 2.0 Trusted
Platform Modules. Features comprise capability discovery,
listing of PCRs, AIK generation and quote signatures.
Diffstat (limited to 'src')
40 files changed, 3168 insertions, 995 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index a9df10cc6..938335e78 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -32,6 +32,10 @@ if USE_LIBPTTLS SUBDIRS += libpttls endif +if USE_LIBTPMTSS + SUBDIRS += libtpmtss +endif + if USE_IMCV SUBDIRS += libimcv endif @@ -131,3 +135,7 @@ endif if USE_AIKGEN SUBDIRS += aikgen endif + +if USE_AIKPUB2 + SUBDIRS += aikpub2 +endif diff --git a/src/aikgen/Makefile.am b/src/aikgen/Makefile.am index dc59d20cf..860a8f7a6 100644 --- a/src/aikgen/Makefile.am +++ b/src/aikgen/Makefile.am @@ -2,14 +2,13 @@ bin_PROGRAMS = aikgen aikgen_SOURCES = aikgen.c -aikgen_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la -aikgen.o : $(top_builddir)/config.status +aikgen_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la \ + $(top_builddir)/src/libtpmtss/libtpmtss.la -if USE_TROUSERS - aikgen_LDADD += -ltspi -endif +aikgen.o : $(top_builddir)/config.status AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libtpmtss \ -DIPSEC_CONFDIR=\"${sysconfdir}\" \ -DPLUGINS=\""${aikgen_plugins}\"" diff --git a/src/aikgen/aikgen.c b/src/aikgen/aikgen.c index 192636afc..22e80badb 100644 --- a/src/aikgen/aikgen.c +++ b/src/aikgen/aikgen.c @@ -1,38 +1,25 @@ /* - * Copyright (C) 2014 Andreas Steffen + * Copyright (C) 2014-2016 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * - * Copyright (c) 2008 Hal Finney + * 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 <http://www.fsf.org/copyleft/gpl.txt>. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. + * 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.h" + #include <library.h> #include <utils/debug.h> #include <utils/optionsfrom.h> #include <credentials/certificates/x509.h> #include <credentials/keys/public_key.h> -#include <asn1/oid.h> -#include <asn1/asn1.h> - -#include <trousers/tss.h> -#include <trousers/trousers.h> #include <syslog.h> #include <getopt.h> @@ -44,12 +31,9 @@ /* default name of AIK private key blob */ #define DEFAULT_FILENAME_AIKBLOB AIK_DIR "aikBlob.bin" -/* default name of AIK private key blob */ +/* default name of AIK public key */ #define DEFAULT_FILENAME_AIKPUBKEY AIK_DIR "aikPub.der" -/* size in bytes of a TSS AIK public key blob */ -#define AIK_PUBKEY_BLOB_SIZE 284 - /* logging */ static bool log_to_stderr = TRUE; static bool log_to_syslog = TRUE; @@ -64,9 +48,7 @@ public_key_t *ca_pubkey; chunk_t ca_modulus; chunk_t aik_pubkey; chunk_t aik_keyid; - -/* TPM context */ -TSS_HCONTEXT hContext; +tpm_tss_t *tpm; /** * logging function for aikgen @@ -128,12 +110,13 @@ static void init_log(const char *program) /** * @brief exit aikgen * - * @param status 0 = OK, 1 = general discomfort + * @param status 0 = OK, -1 = general discomfort */ static void exit_aikgen(err_t message, ...) { int status = 0; + DESTROY_IF(tpm); DESTROY_IF(cacert); DESTROY_IF(ca_pubkey); free(ca_modulus.ptr); @@ -141,13 +124,6 @@ static void exit_aikgen(err_t message, ...) free(aik_keyid.ptr); options->destroy(options); - /* clean up TPM context */ - if (hContext) - { - Tspi_Context_FreeMemory(hContext, NULL); - Tspi_Context_Close(hContext); - } - /* print any error message to stderr */ if (message != NULL && *message != '\0') { @@ -158,7 +134,7 @@ static void exit_aikgen(err_t message, ...) vsnprintf(m, sizeof(m), message, args); va_end(args); - fprintf(stderr, "error: %s\n", m); + fprintf(stderr, "aikgen error: %s\n", m); status = -1; } library_deinit(); @@ -178,7 +154,7 @@ static void usage(const char *message) " [--aikblob <filename>] [--aikpubkey <filename>] \n" " [--idreq <filename>] [--force]" " [--quiet] [--debug <level>]\n" - " aikgen --help\n" + " aikgen --help\n" "\n" "Options:\n" " --cacert (-c) certificate of [privacy] CA\n" @@ -216,25 +192,9 @@ int main(int argc, char *argv[]) bool force = FALSE; chunk_t identity_req; chunk_t aik_blob; - chunk_t aik_pubkey_blob; chunk_t aik_modulus; chunk_t aik_exponent; - /* TPM variables */ - TSS_RESULT result; - TSS_HTPM hTPM; - TSS_HKEY hSRK; - TSS_HKEY hPCAKey; - TSS_HPOLICY hSrkPolicy; - TSS_HPOLICY hTPMPolicy; - TSS_HKEY hIdentKey; - TSS_UUID SRK_UUID = TSS_UUID_SRK; - BYTE secret[] = TSS_WELL_KNOWN_SECRET; - BYTE *IdentityReq; - UINT32 IdentityReqLen; - BYTE *blob; - UINT32 blobLen; - atexit(library_deinit); if (!library_init(NULL, "aikgen")) { @@ -370,105 +330,29 @@ int main(int argc, char *argv[]) if (ca_pubkey->get_type(ca_pubkey) != KEY_RSA || ca_pubkey->get_keysize(ca_pubkey) != 2048) { - exit_aikgen("ca public key must be RSA 2048 but is %N %d", + exit_aikgen("CA public key must be RSA 2048 but is %N %d", key_type_names, ca_pubkey->get_type(ca_pubkey), ca_pubkey->get_keysize(ca_pubkey)); } if (!ca_pubkey->get_encoding(ca_pubkey, PUBKEY_RSA_MODULUS, &ca_modulus)) { - exit_aikgen("could not extract RSA modulus from ca public key"); - } - - /* initialize TSS context and connect to it */ - result = Tspi_Context_Create(&hContext); - if (result != TSS_SUCCESS) - { - exit_aikgen("tss 0x%x on Tspi_Context_Create", result); - } - result = Tspi_Context_Connect(hContext, NULL); - if (result != TSS_SUCCESS) - { - exit_aikgen("tss 0x%x on Tspi_Context_Connect", result); - } - - /* get SRK plus SRK policy and set SRK secret */ - result = Tspi_Context_LoadKeyByUUID(hContext, TSS_PS_TYPE_SYSTEM, - SRK_UUID, &hSRK); - if (result != TSS_SUCCESS) - { - exit_aikgen("tss 0x%x on Tspi_Context_LoadKeyByUUID for SRK", result); - } - result = Tspi_GetPolicyObject(hSRK, TSS_POLICY_USAGE, &hSrkPolicy); - if (result != TSS_SUCCESS) - { - exit_aikgen("tss 0x%x on Tspi_GetPolicyObject for SRK", result); - } - result = Tspi_Policy_SetSecret(hSrkPolicy, TSS_SECRET_MODE_SHA1, 20, secret); - if (result != TSS_SUCCESS) - { - exit_aikgen("tss 0x%x on Tspi_Policy_SetSecret for SRK", result); - } - - /* get TPM plus TPM policy and set TPM secret */ - result = Tspi_Context_GetTpmObject (hContext, &hTPM); - if (result != TSS_SUCCESS) - { - exit_aikgen("tss 0x%x on Tspi_Context_GetTpmObject", result); - } - result = Tspi_GetPolicyObject(hTPM, TSS_POLICY_USAGE, &hTPMPolicy); - if (result != TSS_SUCCESS) - { - exit_aikgen("tss 0x%x on Tspi_GetPolicyObject for TPM", result); - } - result = Tspi_Policy_SetSecret(hTPMPolicy, TSS_SECRET_MODE_SHA1, 20, secret); - if (result != TSS_SUCCESS) - { - exit_aikgen("tss 0x%x on Tspi_Policy_SetSecret for TPM", result); + exit_aikgen("could not extract RSA modulus from CA public key"); } - /* create context for a 2048 bit AIK */ - result = Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_RSAKEY, - TSS_KEY_TYPE_IDENTITY | TSS_KEY_SIZE_2048 | - TSS_KEY_VOLATILE | TSS_KEY_NOT_MIGRATABLE, &hIdentKey); - if (result != TSS_SUCCESS) + /* try to find a TPM 1.2 */ + tpm = tpm_tss_probe(TPM_VERSION_1_2); + if (!tpm) { - exit_aikgen("tss 0x%x on Tspi_Context_CreateObject for key", result); + exit_aikgen("no TPM 1.2 found"); } - /* create context for the Privacy CA public key and assign modulus */ - result = Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_RSAKEY, - TSS_KEY_TYPE_LEGACY|TSS_KEY_SIZE_2048, &hPCAKey); - if (result != TSS_SUCCESS) - { - exit_aikgen("tss 0x%x on Tspi_Context_CreateObject for PCA", result); - } - result = Tspi_SetAttribData (hPCAKey, TSS_TSPATTRIB_RSAKEY_INFO, - TSS_TSPATTRIB_KEYINFO_RSA_MODULUS, ca_modulus.len, - ca_modulus.ptr); - if (result != TSS_SUCCESS) - { - exit_aikgen("tss 0x%x on Tspi_SetAttribData for PCA modulus", result); - } - result = Tspi_SetAttribUint32(hPCAKey, TSS_TSPATTRIB_KEY_INFO, - TSS_TSPATTRIB_KEYINFO_ENCSCHEME, TSS_ES_RSAESPKCSV15); - if (result != TSS_SUCCESS) + if (!tpm->generate_aik(tpm, ca_modulus, &aik_blob, &aik_pubkey, + &identity_req)) { - exit_aikgen("tss 0x%x on Tspi_SetAttribUint32 for PCA " - "encryption scheme", result); + exit_aikgen("could not generate AIK"); } - /* generate AIK */ - DBG1(DBG_LIB, "Generating identity key..."); - result = Tspi_TPM_CollateIdentityRequest(hTPM, hSRK, hPCAKey, 0, NULL, - hIdentKey, TSS_ALG_AES, &IdentityReqLen, &IdentityReq); - if (result != TSS_SUCCESS) - { - exit_aikgen("tss 0x%x on Tspi_TPM_CollateIdentityRequest", result); - } - identity_req = chunk_create(IdentityReq, IdentityReqLen); - DBG3(DBG_LIB, "Identity Request: %B", &identity_req); - - /* optionally output identity request encrypted with ca public key */ + /* optionally output identity request encrypted with CA public key */ if (idreq_filename) { if (!chunk_write(identity_req, idreq_filename, 0022, force)) @@ -480,24 +364,7 @@ int main(int argc, char *argv[]) idreq_filename, identity_req.len); } - /* load identity key */ - result = Tspi_Key_LoadKey (hIdentKey, hSRK); - if (result != TSS_SUCCESS) - { - exit_aikgen("tss 0x%x on Tspi_Key_LoadKey for AIK\n", result); - } - - /* output AIK private key in TSS blob format */ - result = Tspi_GetAttribData (hIdentKey, TSS_TSPATTRIB_KEY_BLOB, - TSS_TSPATTRIB_KEYBLOB_BLOB, &blobLen, &blob); - if (result != TSS_SUCCESS) - { - exit_aikgen("tss 0x%x on Tspi_GetAttribData for private key blob", - result); - } - aik_blob = chunk_create(blob, blobLen); - DBG3(DBG_LIB, "AIK private key blob: %B", &aik_blob); - + /* output AIK private key blob */ if (!chunk_write(aik_blob, aikblob_filename, 0022, force)) { exit_aikgen("could not write AIK blob file '%s': %s", @@ -506,32 +373,7 @@ int main(int argc, char *argv[]) DBG1(DBG_LIB, "AIK private key blob written to '%s' (%u bytes)", aikblob_filename, aik_blob.len); - /* output AIK Public Key in TSS blob format */ - result = Tspi_GetAttribData (hIdentKey, TSS_TSPATTRIB_KEY_BLOB, - TSS_TSPATTRIB_KEYBLOB_PUBLIC_KEY, &blobLen, &blob); - if (result != TSS_SUCCESS) - { - exit_aikgen("tss 0x%x on Tspi_GetAttribData for public key blob", - result); - } - aik_pubkey_blob = chunk_create(blob, blobLen); - DBG3(DBG_LIB, "AIK public key blob: %B", &aik_pubkey_blob); - - /* create a trusted AIK public key */ - if (aik_pubkey_blob.len != AIK_PUBKEY_BLOB_SIZE) - { - exit_aikgen("AIK public key is not in TSS blob format"); - } - aik_modulus = chunk_skip(aik_pubkey_blob, AIK_PUBKEY_BLOB_SIZE - 256); - aik_exponent = chunk_from_chars(0x01, 0x00, 0x01); - - /* output subjectPublicKeyInfo encoding of AIK public 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)) - { - exit_aikgen("subjectPublicKeyInfo encoding of AIK key failed"); - } + /* output AIK public key */ if (!chunk_write(aik_pubkey, aikpubkey_filename, 0022, force)) { exit_aikgen("could not write AIK public key file '%s': %s", diff --git a/src/aikpub2/.gitignore b/src/aikpub2/.gitignore new file mode 100644 index 000000000..42b5e265b --- /dev/null +++ b/src/aikpub2/.gitignore @@ -0,0 +1 @@ +aikpub2 diff --git a/src/aikpub2/Makefile.am b/src/aikpub2/Makefile.am new file mode 100644 index 000000000..a9ab13870 --- /dev/null +++ b/src/aikpub2/Makefile.am @@ -0,0 +1,15 @@ +bin_PROGRAMS = aikpub2 + +aikpub2_SOURCES = aikpub2.c + +aikpub2_LDADD = \ + $(top_builddir)/src/libstrongswan/libstrongswan.la \ + $(top_builddir)/src/libtpmtss/libtpmtss.la + +aikpub2.o : $(top_builddir)/config.status + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libtpmtss \ + -DIPSEC_CONFDIR=\"${sysconfdir}\" \ + -DPLUGINS=\""${aikgen_plugins}\"" diff --git a/src/aikpub2/aikpub2.c b/src/aikpub2/aikpub2.c new file mode 100644 index 000000000..fea58ed27 --- /dev/null +++ b/src/aikpub2/aikpub2.c @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2016 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 <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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.h" + +#include <library.h> +#include <utils/debug.h> +#include <utils/optionsfrom.h> + +#include <syslog.h> +#include <getopt.h> +#include <errno.h> + +/* default directory where AIK keys are stored */ +#define AIK_DIR IPSEC_CONFDIR "/pts/" + +/* default name of AIK public key blob */ +#define DEFAULT_FILENAME_AIKPUBKEY AIK_DIR "aikPub.der" + +/* logging */ +static bool log_to_stderr = TRUE; +static bool log_to_syslog = TRUE; +static level_t default_loglevel = 1; + +/* options read by optionsfrom */ +options_t *options; + +chunk_t aik_pubkey; +chunk_t aik_keyid; + +/** + * logging function for aikpub2 + */ +static void aikpub2_dbg(debug_t group, level_t level, char *fmt, ...) +{ + char buffer[8192]; + char *current = buffer, *next; + va_list args; + + if (level <= default_loglevel) + { + if (log_to_stderr) + { + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, "\n"); + } + if (log_to_syslog) + { + /* write in memory buffer first */ + va_start(args, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, args); + va_end(args); + + /* do a syslog with every line */ + while (current) + { + next = strchr(current, '\n'); + if (next) + { + *(next++) = '\0'; + } + syslog(LOG_INFO, "%s\n", current); + current = next; + } + } + } +} + +/** + * Initialize logging to stderr/syslog + */ +static void init_log(const char *program) +{ + dbg = aikpub2_dbg; + + if (log_to_stderr) + { + setbuf(stderr, NULL); + } + if (log_to_syslog) + { + openlog(program, LOG_CONS | LOG_NDELAY | LOG_PID, LOG_AUTHPRIV); + } +} + +/** + * @brief exit aikgen + * + * @param status 0 = OK, -1 = general discomfort + */ +static void exit_aikpub2(err_t message, ...) +{ + int status = 0; + + free(aik_pubkey.ptr); + free(aik_keyid.ptr); + options->destroy(options); + + /* print any error message to stderr */ + if (message != NULL && *message != '\0') + { + va_list args; + char m[8192]; + + va_start(args, message); + vsnprintf(m, sizeof(m), message, args); + va_end(args); + + fprintf(stderr, "aikpub2 error: %s\n", m); + status = -1; + } + library_deinit(); + exit(status); +} + +/** + * @brief prints the usage of the program to the stderr output + * + * If message is set, program is exited with 1 (error) + * @param message message in case of an error + */ +static void usage(const char *message) +{ + fprintf(stderr, + "Usage: aikpub2 --handle <handle> --out <filename>\n" + " [--force] [--quiet] [--debug <level>]\n" + " aikpub2 --help\n" + "\n" + "Options:\n" + " --handle (-H) TSS 2.0 AIK object handle\n" + " --out (-o) AIK public key in PKCS #1 format\n" + " --force (-f) force to overwrite existing files\n" + " --help (-h) show usage and exit\n" + "\n" + "Debugging output:\n" + " --debug (-l) changes the log level (-1..4, default: 1)\n" + " --quiet (-q) do not write log output to stderr\n" + ); + exit_aikpub2(message); +} + + +/** + * @brief main of aikpub2 which extracts an Attestation Identity Key (AIK) + * + * @param argc number of arguments + * @param argv pointer to the argument values + */ +int main(int argc, char *argv[]) +{ + /* external values */ + extern char * optarg; + extern int optind; + + char *aik_out_filename = DEFAULT_FILENAME_AIKPUBKEY; + uint32_t aik_handle = 0; + bool force = FALSE; + hasher_t *hasher; + tpm_tss_t *tpm; + + atexit(library_deinit); + if (!library_init(NULL, "aikpub2")) + { + exit(SS_RC_LIBSTRONGSWAN_INTEGRITY); + } + if (lib->integrity && + !lib->integrity->check_file(lib->integrity, "aikpub2", argv[0])) + { + fprintf(stderr, "integrity check of aikpub2 failed\n"); + exit(SS_RC_DAEMON_INTEGRITY); + } + + /* initialize global variables */ + options = options_create(); + + for (;;) + { + static const struct option long_opts[] = { + /* name, has_arg, flag, val */ + { "help", no_argument, NULL, 'h' }, + { "optionsfrom", required_argument, NULL, '+' }, + { "handle", required_argument, NULL, 'H' }, + { "in", required_argument, NULL, 'i' }, + { "out", required_argument, NULL, 'o' }, + { "force", no_argument, NULL, 'f' }, + { "quiet", no_argument, NULL, 'q' }, + { "debug", required_argument, NULL, 'l' }, + { 0,0,0,0 } + }; + + /* parse next option */ + int c = getopt_long(argc, argv, "h+:H:i:o:fql:", long_opts, NULL); + + switch (c) + { + case EOF: /* end of flags */ + break; + + case 'h': /* --help */ + usage(NULL); + + case '+': /* --optionsfrom <filename> */ + if (!options->from(options, optarg, &argc, &argv, optind)) + { + exit_aikpub2("optionsfrom failed"); + } + continue; + + case 'H': /* --handle <handle> */ + aik_handle = strtoll(optarg, NULL, 16); + continue; + + case 'o': /* --out <filename> */ + aik_out_filename = optarg; + continue; + + case 'f': /* --force */ + force = TRUE; + continue; + + case 'q': /* --quiet */ + log_to_stderr = FALSE; + continue; + + case 'l': /* --debug <level> */ + default_loglevel = atoi(optarg); + continue; + + default: + usage("unknown option"); + } + /* break from loop */ + break; + } + + init_log("aikpub2"); + + if (!lib->plugins->load(lib->plugins, + lib->settings->get_str(lib->settings, "aikpub2.load", PLUGINS))) + { + exit_aikpub2("plugin loading failed"); + } + if (!aik_handle) + { + usage("--handle option is required"); + } + + /* try to find a TPM 2.0 */ + tpm = tpm_tss_probe(TPM_VERSION_2_0); + if (!tpm) + { + exit_aikpub2("no TPM 2.0 found"); + } + + /* get AIK public key from TPM */ + aik_pubkey = tpm->get_public(tpm, aik_handle); + tpm->destroy(tpm); + + /* exit if AIK public key retrieval failed */ + if (aik_pubkey.len == 0) + { + exit_aikpub2("retrieval of AIK public key failed"); + } + + /* store AIK subjectPublicKeyInfo to file */ + if (!chunk_write(aik_pubkey, aik_out_filename, 0022, force)) + { + exit_aikpub2("could not write AIK public key file '%s': %s", + aik_out_filename, strerror(errno)); + } + DBG1(DBG_LIB, "AIK public key written to '%s' (%u bytes)", + aik_out_filename, aik_pubkey.len); + + /* AIK keyid derived from subjectPublicKeyInfo encoding */ + hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); + if (!hasher) + { + exit_aikpub2("SHA1 hash algorithm not supported"); + } + if (!hasher->allocate_hash(hasher, aik_pubkey, &aik_keyid)) + { + hasher->destroy(hasher); + exit_aikpub2("computing SHA1 fingerprint failed"); + } + hasher->destroy(hasher); + + DBG1(DBG_LIB, "AIK keyid: %#B", &aik_keyid); + + exit_aikpub2(NULL); + return -1; /* should never be reached */ +} diff --git a/src/checksum/Makefile.am b/src/checksum/Makefile.am index 9cc5fb6b2..87bbf9f28 100644 --- a/src/checksum/Makefile.am +++ b/src/checksum/Makefile.am @@ -53,6 +53,11 @@ if USE_LIBPTTLS libs += $(DESTDIR)$(ipseclibdir)/libpttls.so endif +if USE_LIBTPMTSS + deps += $(top_builddir)/src/libtpmtss/libtpmtss.la + libs += $(DESTDIR)$(ipseclibdir)/libtpmtss.so +endif + if USE_LIBTNCCS deps += $(top_builddir)/src/libtnccs/libtnccs.la libs += $(DESTDIR)$(ipseclibdir)/libtnccs.so diff --git a/src/libimcv/Makefile.am b/src/libimcv/Makefile.am index 7683da3af..8cde4b7fc 100644 --- a/src/libimcv/Makefile.am +++ b/src/libimcv/Makefile.am @@ -1,6 +1,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ -I$(top_srcdir)/src/libtncif \ + -I$(top_srcdir)/src/libtpmtss \ -DIPSEC_SCRIPT=\"${ipsec_script}\" ipseclib_LTLIBRARIES = libimcv.la @@ -10,11 +11,8 @@ libimcv_la_LDFLAGS = \ libimcv_la_LIBADD = \ $(top_builddir)/src/libstrongswan/libstrongswan.la \ - $(top_builddir)/src/libtncif/libtncif.la - -if USE_TROUSERS - libimcv_la_LIBADD += -ltspi -endif + $(top_builddir)/src/libtncif/libtncif.la \ + $(top_builddir)/src/libtpmtss/libtpmtss.la if USE_WINDOWS libimcv_la_LIBADD += -lws2_32 @@ -66,7 +64,6 @@ libimcv_la_SOURCES = \ pts/pts_pcr.h pts/pts_pcr.c \ pts/pts_proto_caps.h \ pts/pts_req_func_comp_evid.h \ - pts/pts_simple_evid_final.h \ pts/pts_creds.h pts/pts_creds.c \ pts/pts_database.h pts/pts_database.c \ pts/pts_dh_group.h pts/pts_dh_group.c \ @@ -207,5 +204,6 @@ imcv_tests_CFLAGS = \ imcv_tests_LDFLAGS = @COVERAGE_LDFLAGS@ imcv_tests_LDADD = \ $(top_builddir)/src/libimcv/libimcv.la \ + $(top_builddir)/src/libtpmtss/libtpmtss.la \ $(top_builddir)/src/libstrongswan/libstrongswan.la \ $(top_builddir)/src/libstrongswan/tests/libtest.la diff --git a/src/libimcv/plugins/imc_attestation/Makefile.am b/src/libimcv/plugins/imc_attestation/Makefile.am index e7b1f1ce1..14b1646e5 100644 --- a/src/libimcv/plugins/imc_attestation/Makefile.am +++ b/src/libimcv/plugins/imc_attestation/Makefile.am @@ -1,7 +1,8 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ -I$(top_srcdir)/src/libtncif \ - -I$(top_srcdir)/src/libimcv + -I$(top_srcdir)/src/libimcv \ + -I$(top_srcdir)/src/libtpmtss AM_CFLAGS = \ $(PLUGIN_CFLAGS) diff --git a/src/libimcv/plugins/imc_attestation/imc_attestation_process.c b/src/libimcv/plugins/imc_attestation/imc_attestation_process.c index 0e1bc153b..56713bb04 100644 --- a/src/libimcv/plugins/imc_attestation/imc_attestation_process.c +++ b/src/libimcv/plugins/imc_attestation/imc_attestation_process.c @@ -1,5 +1,6 @@ /* - * Copyright (C) 2011-2012 Sansar Choinyambuu, Andreas Steffen + * Copyright (C) 2011-2012 Sansar Choinyambuu + * Copyright (C) 2011-2016 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -420,11 +421,11 @@ bool imc_attestation_process(pa_tnc_attr_t *attr, imc_msg_t *msg, } case TCG_PTS_GEN_ATTEST_EVID: { - pts_simple_evid_final_flag_t flags; - pts_meas_algorithms_t comp_hash_algorithm; pts_comp_evidence_t *evid; - chunk_t pcr_composite, quote_sig; - bool use_quote2; + tpm_quote_mode_t quote_mode; + tpm_tss_quote_info_t *quote_info; + chunk_t quote_sig; + bool use_quote2, use_version_info; /* Send cached Component Evidence entries */ while (attestation_state->next_evidence(attestation_state, &evid)) @@ -434,21 +435,23 @@ bool imc_attestation_process(pa_tnc_attr_t *attr, imc_msg_t *msg, } use_quote2 = lib->settings->get_bool(lib->settings, - "%s.plugins.imc-attestation.use_quote2", TRUE, - lib->ns); - if (!pts->quote_tpm(pts, use_quote2, &pcr_composite, "e_sig)) + "%s.plugins.imc-attestation.use_quote2", + TRUE, lib->ns); + use_version_info = lib->settings->get_bool(lib->settings, + "%s.plugins.imc-attestation.use_version_info", + FALSE, lib->ns); + quote_mode = use_quote2 ? (use_version_info ? + TPM_QUOTE2_VERSION_INFO : + TPM_QUOTE2) : + TPM_QUOTE; + + if (!pts->quote(pts, "e_mode, "e_info, "e_sig)) { DBG1(DBG_IMC, "error occurred during TPM quote operation"); return FALSE; } - /* Send Simple Evidence Final attribute */ - flags = use_quote2 ? PTS_SIMPLE_EVID_FINAL_QUOTE_INFO2 : - PTS_SIMPLE_EVID_FINAL_QUOTE_INFO; - comp_hash_algorithm = PTS_MEAS_ALGO_SHA1; - - attr = tcg_pts_attr_simple_evid_final_create(flags, - comp_hash_algorithm, pcr_composite, quote_sig); + attr = tcg_pts_attr_simple_evid_final_create(quote_info, quote_sig); msg->add_attribute(msg, attr); break; } diff --git a/src/libimcv/plugins/imv_attestation/Makefile.am b/src/libimcv/plugins/imv_attestation/Makefile.am index 6c5bf8913..f353d30fc 100644 --- a/src/libimcv/plugins/imv_attestation/Makefile.am +++ b/src/libimcv/plugins/imv_attestation/Makefile.am @@ -2,6 +2,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ -I$(top_srcdir)/src/libtncif \ -I$(top_srcdir)/src/libimcv \ + -I$(top_srcdir)/src/libtpmtss \ -DPLUGINS=\""${attest_plugins}\"" AM_CFLAGS = \ @@ -11,6 +12,7 @@ imcv_LTLIBRARIES = imv-attestation.la imv_attestation_la_LIBADD = \ $(top_builddir)/src/libimcv/libimcv.la \ + $(top_builddir)/src/libtpmtss/libtpmtss.la \ $(top_builddir)/src/libstrongswan/libstrongswan.la imv_attestation_la_SOURCES = imv_attestation.c \ @@ -27,6 +29,7 @@ attest_SOURCES = attest.c \ attest_db.h attest_db.c attest_LDADD = \ $(top_builddir)/src/libimcv/libimcv.la \ + $(top_builddir)/src/libtpmtss/libtpmtss.la \ $(top_builddir)/src/libstrongswan/libstrongswan.la attest.o : $(top_builddir)/config.status diff --git a/src/libimcv/plugins/imv_attestation/imv_attestation_agent.c b/src/libimcv/plugins/imv_attestation/imv_attestation_agent.c index 91c12f33b..89ba86930 100644 --- a/src/libimcv/plugins/imv_attestation/imv_attestation_agent.c +++ b/src/libimcv/plugins/imv_attestation/imv_attestation_agent.c @@ -217,7 +217,12 @@ static TNC_Result receive_msg(private_imv_attestation_agent_t *this, DBG1(DBG_IMV, "received TCG-PTS error '%N'", pts_error_code_names, error_code.type); DBG1(DBG_IMV, "error information: %B", &msg_info); - fatal_error = TRUE; + + /* TPM 2.0 doesn't return TPM Version Information */ + if (error_code.type != TCG_PTS_TPM_VERS_NOT_SUPPORTED) + { + fatal_error = TRUE; + } } break; } diff --git a/src/libimcv/plugins/imv_attestation/imv_attestation_process.c b/src/libimcv/plugins/imv_attestation/imv_attestation_process.c index c3e053d9b..b1ee16bf8 100644 --- a/src/libimcv/plugins/imv_attestation/imv_attestation_process.c +++ b/src/libimcv/plugins/imv_attestation/imv_attestation_process.c @@ -418,45 +418,31 @@ bool imv_attestation_process(pa_tnc_attr_t *attr, imv_msg_t *out_msg, case TCG_PTS_SIMPLE_EVID_FINAL: { tcg_pts_attr_simple_evid_final_t *attr_cast; - uint8_t flags; - pts_meas_algorithms_t comp_hash_algorithm; - chunk_t pcr_comp, tpm_quote_sig, evid_sig; - chunk_t pcr_composite, quote_info, result_buf; + tpm_tss_quote_info_t *quote_info; + chunk_t quoted = chunk_empty, quote_sig, evid_sig, result_buf; imv_workitem_t *workitem; imv_reason_string_t *reason_string; + hash_algorithm_t digest_alg; enumerator_t *enumerator; - bool use_quote2, use_ver_info; bio_writer_t *result; attr_cast = (tcg_pts_attr_simple_evid_final_t*)attr; - flags = attr_cast->get_quote_info(attr_cast, &comp_hash_algorithm, - &pcr_comp, &tpm_quote_sig); + attr_cast->get_quote_info(attr_cast, "e_info, "e_sig); - if (flags != PTS_SIMPLE_EVID_FINAL_NO) + if (quote_info->get_quote_mode(quote_info) != TPM_QUOTE_NONE) { - use_quote2 = (flags == PTS_SIMPLE_EVID_FINAL_QUOTE_INFO2 || - flags == PTS_SIMPLE_EVID_FINAL_QUOTE_INFO2_CAP_VER); - use_ver_info = (flags == PTS_SIMPLE_EVID_FINAL_QUOTE_INFO2_CAP_VER); - /* Construct PCR Composite and TPM Quote Info structures */ - if (!pts->get_quote_info(pts, use_quote2, use_ver_info, - comp_hash_algorithm, &pcr_composite, "e_info)) - { - DBG1(DBG_IMV, "unable to construct TPM Quote Info"); - return FALSE; - } - - if (!chunk_equals_const(pcr_comp, pcr_composite)) + if (!pts->get_quote(pts, quote_info, "ed)) { - DBG1(DBG_IMV, "received PCR Composite does not match " - "constructed one"); + DBG1(DBG_IMV, "unable to construct TPM Quote Info digest"); attestation_state->set_measurement_error(attestation_state, IMV_ATTESTATION_ERROR_TPM_QUOTE_FAIL); goto quote_error; } - DBG2(DBG_IMV, "received PCR Composite matches constructed one"); + digest_alg = quote_info->get_pcr_digest_alg(quote_info); - if (!pts->verify_quote_signature(pts, quote_info, tpm_quote_sig)) + if (!pts->verify_quote_signature(pts, digest_alg, quoted, + quote_sig)) { attestation_state->set_measurement_error(attestation_state, IMV_ATTESTATION_ERROR_TPM_QUOTE_FAIL); @@ -465,8 +451,7 @@ bool imv_attestation_process(pa_tnc_attr_t *attr, imv_msg_t *out_msg, DBG2(DBG_IMV, "TPM Quote Info signature verification successful"); quote_error: - free(pcr_composite.ptr); - free(quote_info.ptr); + chunk_free("ed); /** * Finalize any pending measurement registrations and check diff --git a/src/libimcv/plugins/imv_hcd/Makefile.am b/src/libimcv/plugins/imv_hcd/Makefile.am index 28926d45e..0dce300ef 100644 --- a/src/libimcv/plugins/imv_hcd/Makefile.am +++ b/src/libimcv/plugins/imv_hcd/Makefile.am @@ -1,6 +1,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ -I$(top_srcdir)/src/libtncif \ + -I$(top_srcdir)/src/libtpmtss \ -I$(top_srcdir)/src/libimcv AM_CFLAGS = \ diff --git a/src/libimcv/plugins/imv_os/Makefile.am b/src/libimcv/plugins/imv_os/Makefile.am index 3b3f793f1..f5bc9010c 100644 --- a/src/libimcv/plugins/imv_os/Makefile.am +++ b/src/libimcv/plugins/imv_os/Makefile.am @@ -1,7 +1,8 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ -I$(top_srcdir)/src/libtncif \ - -I$(top_srcdir)/src/libimcv + -I$(top_srcdir)/src/libimcv \ + -I$(top_srcdir)/src/libtpmtss AM_CFLAGS = \ $(PLUGIN_CFLAGS) diff --git a/src/libimcv/plugins/imv_scanner/Makefile.am b/src/libimcv/plugins/imv_scanner/Makefile.am index 98814437e..3b3ee818f 100644 --- a/src/libimcv/plugins/imv_scanner/Makefile.am +++ b/src/libimcv/plugins/imv_scanner/Makefile.am @@ -1,7 +1,8 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ -I$(top_srcdir)/src/libtncif \ - -I$(top_srcdir)/src/libimcv + -I$(top_srcdir)/src/libimcv \ + -I$(top_srcdir)/src/libtpmtss AM_CFLAGS = \ $(PLUGIN_CFLAGS) diff --git a/src/libimcv/plugins/imv_swid/Makefile.am b/src/libimcv/plugins/imv_swid/Makefile.am index 3a63b67d2..73da84b55 100644 --- a/src/libimcv/plugins/imv_swid/Makefile.am +++ b/src/libimcv/plugins/imv_swid/Makefile.am @@ -1,6 +1,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ -I$(top_srcdir)/src/libtncif \ + -I$(top_srcdir)/src/libtpmtss \ -I$(top_srcdir)/src/libimcv AM_CFLAGS = \ diff --git a/src/libimcv/pts/components/ita/ita_comp_tgrub.c b/src/libimcv/pts/components/ita/ita_comp_tgrub.c index 8c22e9440..a5a1a9b96 100644 --- a/src/libimcv/pts/components/ita/ita_comp_tgrub.c +++ b/src/libimcv/pts/components/ita/ita_comp_tgrub.c @@ -90,7 +90,7 @@ METHOD(pts_component_t, measure, status_t, extended_pcr = PCR_DEBUG; time(&measurement_time); - if (!pts->read_pcr(pts, extended_pcr, &pcr_after)) + if (!pts->read_pcr(pts, extended_pcr, &pcr_after, HASH_SHA1)) { DBG1(DBG_PTS, "error occurred while reading PCR: %d", extended_pcr); return FAILED; diff --git a/src/libimcv/pts/components/pts_comp_func_name.c b/src/libimcv/pts/components/pts_comp_func_name.c index 257d205ae..00494e1ad 100644 --- a/src/libimcv/pts/components/pts_comp_func_name.c +++ b/src/libimcv/pts/components/pts_comp_func_name.c @@ -117,12 +117,12 @@ METHOD(pts_comp_func_name_t, log_, void, if (names && types) { - DBG2(DBG_PTS, "%s%N functional component '%N' [%s] '%N'", + DBG3(DBG_PTS, "%s%N functional component '%N' [%s] '%N'", label, pen_names, this->vid, names, this->name, flags, types, type); } else { - DBG2(DBG_PTS, "%s0x%06x functional component 0x%08x 0x%02x", + DBG3(DBG_PTS, "%s0x%06x functional component 0x%08x 0x%02x", label, this->vid, this->name, this->qualifier); } } diff --git a/src/libimcv/pts/pts.c b/src/libimcv/pts/pts.c index 3c5359193..2ba949e40 100644 --- a/src/libimcv/pts/pts.c +++ b/src/libimcv/pts/pts.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2011-2012 Sansar Choinyambuu - * Copyright (C) 2012-2014 Andreas Steffen + * Copyright (C) 2012-2016 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -21,21 +21,8 @@ #include <bio/bio_writer.h> #include <bio/bio_reader.h> -#ifdef TSS_TROUSERS -#ifdef _BASETSD_H_ -/* MinGW defines _BASETSD_H_, but TSS checks for _BASETSD_H */ -# define _BASETSD_H -#endif -#include <trousers/tss.h> -#include <trousers/trousers.h> -#else -#ifndef TPM_TAG_QUOTE_INFO2 -#define TPM_TAG_QUOTE_INFO2 0x0036 -#endif -#ifndef TPM_LOC_ZERO -#define TPM_LOC_ZERO 0x01 -#endif -#endif +#include <tpm_tss.h> +#include <tpm_tss_trousers.h> #include <sys/types.h> #include <sys/stat.h> @@ -43,6 +30,13 @@ #include <unistd.h> #include <errno.h> +#ifndef TPM_TAG_QUOTE_INFO2 +#define TPM_TAG_QUOTE_INFO2 0x0036 +#endif +#ifndef TPM_LOC_ZERO +#define TPM_LOC_ZERO 0x01 +#endif + typedef struct private_pts_t private_pts_t; /** @@ -102,9 +96,9 @@ struct private_pts_t { bool is_imc; /** - * Do we have an activated TPM + * Active TPM */ - bool has_tpm; + tpm_tss_t *tpm; /** * Contains a TPM_CAP_VERSION_INFO struct @@ -112,14 +106,14 @@ struct private_pts_t { chunk_t tpm_version_info; /** - * Contains TSS Blob structure for AIK + * AIK object handle */ - chunk_t aik_blob; + uint32_t aik_handle; /** - * Contains a Attestation Identity Key or Certificate + * Contains an Attestation Identity Key Certificate */ - certificate_t *aik; + certificate_t *aik_cert; /** * Primary key referening AIK in database @@ -191,7 +185,6 @@ METHOD(pts_t, set_dh_hash_algorithm, void, } } - METHOD(pts_t, create_dh_nonce, bool, private_pts_t *this, pts_dh_group_t group, int nonce_len) { @@ -306,41 +299,6 @@ METHOD(pts_t, calculate_secret, bool, return TRUE; } -#ifdef TSS_TROUSERS - -/** - * Print TPM 1.2 Version Info - */ -static void print_tpm_version_info(private_pts_t *this) -{ - TPM_CAP_VERSION_INFO *info; - - info = (TPM_CAP_VERSION_INFO*)this->tpm_version_info.ptr; - - if (this->tpm_version_info.len >= - sizeof(*info) - sizeof(info->vendorSpecific)) - { - DBG2(DBG_PTS, "TPM Version Info: Chip Version: %u.%u.%u.%u, " - "Spec Level: %u, Errata Rev: %u, Vendor ID: %.4s", - info->version.major, info->version.minor, - info->version.revMajor, info->version.revMinor, - untoh16(&info->specLevel), info->errataRev, info->tpmVendorID); - } - else - { - DBG1(DBG_PTS, "could not parse tpm version info"); - } -} - -#else - -static void print_tpm_version_info(private_pts_t *this) -{ - DBG1(DBG_PTS, "unknown TPM version: no TSS implementation available"); -} - -#endif /* TSS_TROUSERS */ - METHOD(pts_t, get_platform_id, int, private_pts_t *this) { @@ -356,104 +314,135 @@ METHOD(pts_t, set_platform_id, void, METHOD(pts_t, get_tpm_version_info, bool, private_pts_t *this, chunk_t *info) { - if (!this->has_tpm) - { - return FALSE; - } - *info = this->tpm_version_info; - print_tpm_version_info(this); - return TRUE; + *info = this->tpm ? this->tpm->get_version_info(this->tpm) : + this->tpm_version_info; + return info->len > 0; } METHOD(pts_t, set_tpm_version_info, void, private_pts_t *this, chunk_t info) { this->tpm_version_info = chunk_clone(info); - print_tpm_version_info(this); -} - -/** - * Load an AIK Blob (TSS_TSPATTRIB_KEYBLOB_BLOB attribute) - */ -static void load_aik_blob(private_pts_t *this) -{ - char *path; - chunk_t *map; - - path = lib->settings->get_str(lib->settings, - "%s.plugins.imc-attestation.aik_blob", NULL, lib->ns); - if (path) - { - map = chunk_map(path, FALSE); - if (map) - { - DBG2(DBG_PTS, "loaded AIK Blob from '%s'", path); - DBG3(DBG_PTS, "AIK Blob: %B", map); - this->aik_blob = chunk_clone(*map); - chunk_unmap(map); - } - else - { - DBG1(DBG_PTS, "unable to map AIK Blob file '%s': %s", - path, strerror(errno)); - } - } - else - { - DBG1(DBG_PTS, "AIK Blob is not available"); - } + /* print_tpm_version_info(this); */ } /** - * Load an AIK certificate or public key + * Load an AIK handle and an optional AIK certificate and + * in the case of a TPM 1.2 an AIK private key blob plus matching public key, * the certificate having precedence over the public key if both are present */ static void load_aik(private_pts_t *this) { - char *cert_path, *key_path; + char *handle_str, *cert_path, *key_path, *blob_path; + chunk_t aik_pubkey = chunk_empty; + handle_str = lib->settings->get_str(lib->settings, + "%s.plugins.imc-attestation.aik_handle", NULL, lib->ns); cert_path = lib->settings->get_str(lib->settings, "%s.plugins.imc-attestation.aik_cert", NULL, lib->ns); key_path = lib->settings->get_str(lib->settings, "%s.plugins.imc-attestation.aik_pubkey", NULL, lib->ns); + blob_path = lib->settings->get_str(lib->settings, + "%s.plugins.imc-attestation.aik_blob", NULL, lib->ns); + if (handle_str) + { + this->aik_handle = strtoll(handle_str, NULL, 16); + } if (cert_path) { - this->aik = lib->creds->create(lib->creds, CRED_CERTIFICATE, + this->aik_cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, BUILD_FROM_FILE, cert_path, BUILD_END); - if (this->aik) + if (this->aik_cert) { DBG2(DBG_PTS, "loaded AIK certificate from '%s'", cert_path); - return; } } - if (key_path) + + if (this->tpm->get_version(this->tpm) == TPM_VERSION_1_2) { - this->aik = lib->creds->create(lib->creds, CRED_CERTIFICATE, - CERT_TRUSTED_PUBKEY, BUILD_FROM_FILE, - key_path, BUILD_END); - if (this->aik) + tpm_tss_trousers_t *tpm_12; + chunk_t aik_blob = chunk_empty; + chunk_t *map; + + /* get AIK private key blob */ + if (blob_path) { - DBG2(DBG_PTS, "loaded AIK public key from '%s'", key_path); - return; + map = chunk_map(blob_path, FALSE); + if (map) + { + DBG2(DBG_PTS, "loaded AIK Blob from '%s'", blob_path); + DBG3(DBG_PTS, "AIK Blob: %B", map); + aik_blob = chunk_clone(*map); + chunk_unmap(map); + } + else + { + DBG1(DBG_PTS, "unable to map AIK Blob file '%s': %s", + blob_path, strerror(errno)); + } } + else + { + DBG1(DBG_PTS, "AIK Blob is not available"); + } + + /* get AIK public key */ + if (key_path) + { + map = chunk_map(key_path, FALSE); + if (map) + { + DBG2(DBG_PTS, "loaded AIK public key from '%s'", key_path); + aik_pubkey = chunk_clone(*map); + chunk_unmap(map); + } + else + { + DBG1(DBG_PTS, "unable to map AIK public key file '%s': %s", + key_path, strerror(errno)); + } + } + else + { + DBG1(DBG_PTS, "AIK public key is not available"); + } + + /* Load AIK item into TPM 1.2 object */ + tpm_12 = (tpm_tss_trousers_t *)this->tpm; + tpm_12->load_aik(tpm_12, aik_blob, aik_pubkey, this->aik_handle); } - DBG1(DBG_PTS, "neither AIK certificate nor public key is available"); + /* if no signed X.509 AIK certificate is available use public key instead */ + if (!this->aik_cert) + { + aik_pubkey = this->tpm->get_public(this->tpm, this->aik_handle); + if (aik_pubkey.len > 0) + { + this->aik_cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, + CERT_TRUSTED_PUBKEY, BUILD_BLOB, + aik_pubkey, BUILD_END); + chunk_free(&aik_pubkey); + } + else + { + DBG1(DBG_PTS, "neither AIK certificate nor public key is available"); + } + } } METHOD(pts_t, get_aik, certificate_t*, private_pts_t *this) { - return this->aik; + return this->aik_cert; } METHOD(pts_t, set_aik, void, private_pts_t *this, certificate_t *aik, int aik_id) { - DESTROY_IF(this->aik); - this->aik = aik->get_ref(aik); + DESTROY_IF(this->aik_cert); + this->aik_cert = aik->get_ref(aik); this->aik_id = aik_id; } @@ -611,312 +600,64 @@ METHOD(pts_t, get_metadata, pts_file_meta_t*, return metadata; } - -#ifdef TSS_TROUSERS - METHOD(pts_t, read_pcr, bool, - private_pts_t *this, uint32_t pcr_num, chunk_t *pcr_value) + private_pts_t *this, uint32_t pcr_num, chunk_t *pcr_value, + hash_algorithm_t alg) { - TSS_HCONTEXT hContext; - TSS_HTPM hTPM; - TSS_RESULT result; - BYTE *buf; - UINT32 len; - - bool success = FALSE; - - result = Tspi_Context_Create(&hContext); - if (result != TSS_SUCCESS) - { - DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x", result); - return FALSE; - } - - result = Tspi_Context_Connect(hContext, NULL); - if (result != TSS_SUCCESS) - { - goto err; - } - result = Tspi_Context_GetTpmObject (hContext, &hTPM); - if (result != TSS_SUCCESS) - { - goto err; - } - result = Tspi_TPM_PcrRead(hTPM, pcr_num, &len, &buf); - if (result != TSS_SUCCESS) - { - goto err; - } - *pcr_value = chunk_clone(chunk_create(buf, len)); - DBG3(DBG_PTS, "PCR %d value:%B", pcr_num, pcr_value); - success = TRUE; - -err: - if (!success) - { - DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result); - } - Tspi_Context_FreeMemory(hContext, NULL); - Tspi_Context_Close(hContext); - - return success; + return this->tpm ? this->tpm->read_pcr(this->tpm, pcr_num, pcr_value, alg) + : FALSE; } METHOD(pts_t, extend_pcr, bool, - private_pts_t *this, uint32_t pcr_num, chunk_t input, chunk_t *output) + private_pts_t *this, uint32_t pcr_num, chunk_t *pcr_value, chunk_t data, + hash_algorithm_t alg) { - TSS_HCONTEXT hContext; - TSS_HTPM hTPM; - TSS_RESULT result; - uint32_t pcr_length; - chunk_t pcr_value = chunk_empty; - - result = Tspi_Context_Create(&hContext); - if (result != TSS_SUCCESS) + if (!this->tpm->extend_pcr(this->tpm, pcr_num, pcr_value, data, alg)) { - DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x", - result); return FALSE; } - result = Tspi_Context_Connect(hContext, NULL); - if (result != TSS_SUCCESS) - { - goto err; - } - result = Tspi_Context_GetTpmObject (hContext, &hTPM); - if (result != TSS_SUCCESS) - { - goto err; - } - - pcr_value = chunk_alloc(PTS_PCR_LEN); - result = Tspi_TPM_PcrExtend(hTPM, pcr_num, PTS_PCR_LEN, input.ptr, - NULL, &pcr_length, &pcr_value.ptr); - if (result != TSS_SUCCESS) - { - goto err; - } - - *output = pcr_value; - *output = chunk_clone(*output); - - DBG3(DBG_PTS, "PCR %d extended with: %B", pcr_num, &input); - DBG3(DBG_PTS, "PCR %d value after extend: %B", pcr_num, output); - - chunk_clear(&pcr_value); - Tspi_Context_FreeMemory(hContext, NULL); - Tspi_Context_Close(hContext); + DBG3(DBG_PTS, "PCR %d extended with: %#B", pcr_num, &data); + DBG3(DBG_PTS, "PCR %d after extension: %#B", pcr_num, pcr_value); return TRUE; - -err: - DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result); - - chunk_clear(&pcr_value); - Tspi_Context_FreeMemory(hContext, NULL); - Tspi_Context_Close(hContext); - - return FALSE; } -METHOD(pts_t, quote_tpm, bool, - private_pts_t *this, bool use_quote2, chunk_t *pcr_comp, chunk_t *quote_sig) +METHOD(pts_t, quote, bool, + private_pts_t *this, tpm_quote_mode_t *quote_mode, + tpm_tss_quote_info_t **quote_info, chunk_t *quote_sig) { - TSS_HCONTEXT hContext; - TSS_HTPM hTPM; - TSS_HKEY hAIK; - TSS_HKEY hSRK; - TSS_HPOLICY srkUsagePolicy; - TSS_UUID SRK_UUID = TSS_UUID_SRK; - BYTE secret[] = TSS_WELL_KNOWN_SECRET; - TSS_HPCRS hPcrComposite; - TSS_VALIDATION valData; - TSS_RESULT result; - chunk_t quote_info; - BYTE* versionInfo; - uint32_t versionInfoSize, pcr; + chunk_t pcr_value, pcr_computed; + uint32_t pcr, pcr_sel = 0; enumerator_t *enumerator; - bool success = FALSE; - - result = Tspi_Context_Create(&hContext); - if (result != TSS_SUCCESS) - { - DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x", - result); - return FALSE; - } - result = Tspi_Context_Connect(hContext, NULL); - if (result != TSS_SUCCESS) - { - goto err1; - } - result = Tspi_Context_GetTpmObject (hContext, &hTPM); - if (result != TSS_SUCCESS) - { - goto err1; - } - - /* Retrieve SRK from TPM and set the authentication to well known secret*/ - result = Tspi_Context_LoadKeyByUUID(hContext, TSS_PS_TYPE_SYSTEM, - SRK_UUID, &hSRK); - if (result != TSS_SUCCESS) - { - goto err1; - } - - result = Tspi_GetPolicyObject(hSRK, TSS_POLICY_USAGE, &srkUsagePolicy); - if (result != TSS_SUCCESS) - { - goto err1; - } - result = Tspi_Policy_SetSecret(srkUsagePolicy, TSS_SECRET_MODE_SHA1, - 20, secret); - if (result != TSS_SUCCESS) - { - goto err1; - } - result = Tspi_Context_LoadKeyByBlob (hContext, hSRK, this->aik_blob.len, - this->aik_blob.ptr, &hAIK); - if (result != TSS_SUCCESS) - { - goto err1; - } - - /* Create PCR composite object */ - result = use_quote2 ? - Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_PCRS, - TSS_PCRS_STRUCT_INFO_SHORT, &hPcrComposite) : - Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_PCRS, - TSS_PCRS_STRUCT_DEFAULT, &hPcrComposite); - if (result != TSS_SUCCESS) - { - goto err2; - } - - /* Select PCRs */ + /* select PCRs */ + DBG2(DBG_PTS, "PCR values hashed into PCR Composite:"); enumerator = this->pcrs->create_enumerator(this->pcrs); while (enumerator->enumerate(enumerator, &pcr)) { - result = use_quote2 ? - Tspi_PcrComposite_SelectPcrIndexEx(hPcrComposite, pcr, - TSS_PCRS_DIRECTION_RELEASE) : - Tspi_PcrComposite_SelectPcrIndex(hPcrComposite, pcr); - if (result != TSS_SUCCESS) + if (this->tpm->read_pcr(this->tpm, pcr, &pcr_value, HASH_SHA1)) { - break; - } - } - enumerator->destroy(enumerator); + pcr_computed = this->pcrs->get(this->pcrs, pcr); + DBG2(DBG_PTS, "PCR %2d %#B %s", pcr, &pcr_value, + chunk_equals(pcr_value, pcr_computed) ? "ok" : "differs"); + chunk_free(&pcr_value); + }; - if (result != TSS_SUCCESS) - { - goto err3; + /* add PCR to selection list */ + pcr_sel |= (1 << pcr); } - - /* Set the Validation Data */ - valData.ulExternalDataLength = this->secret.len; - valData.rgbExternalData = (BYTE *)this->secret.ptr; - + enumerator->destroy(enumerator); /* TPM Quote */ - result = use_quote2 ? - Tspi_TPM_Quote2(hTPM, hAIK, FALSE, hPcrComposite, &valData, - &versionInfoSize, &versionInfo): - Tspi_TPM_Quote(hTPM, hAIK, hPcrComposite, &valData); - if (result != TSS_SUCCESS) - { - goto err4; - } - - /* Set output chunks */ - *pcr_comp = chunk_alloc(HASH_SIZE_SHA1); - - if (use_quote2) - { - /* TPM_Composite_Hash is last 20 bytes of TPM_Quote_Info2 structure */ - memcpy(pcr_comp->ptr, valData.rgbData + valData.ulDataLength - HASH_SIZE_SHA1, - HASH_SIZE_SHA1); - } - else - { - /* TPM_Composite_Hash is 8-28th bytes of TPM_Quote_Info structure */ - memcpy(pcr_comp->ptr, valData.rgbData + 8, HASH_SIZE_SHA1); - } - DBG3(DBG_PTS, "Hash of PCR Composite: %#B", pcr_comp); - - quote_info = chunk_create(valData.rgbData, valData.ulDataLength); - DBG3(DBG_PTS, "TPM Quote Info: %B","e_info); - - *quote_sig = chunk_clone(chunk_create(valData.rgbValidationData, - valData.ulValidationDataLength)); - DBG3(DBG_PTS, "TPM Quote Signature: %B",quote_sig); - - success = TRUE; - - /* Cleanup */ -err4: - Tspi_Context_FreeMemory(hContext, NULL); - -err3: - Tspi_Context_CloseObject(hContext, hPcrComposite); - -err2: - Tspi_Context_CloseObject(hContext, hAIK); - -err1: - Tspi_Context_Close(hContext); - if (!success) - { - DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result); - } - return success; -} - -#else /* TSS_TROUSERS */ - -METHOD(pts_t, read_pcr, bool, - private_pts_t *this, uint32_t pcr_num, chunk_t *pcr_value) -{ - return FALSE; -} - -METHOD(pts_t, extend_pcr, bool, - private_pts_t *this, uint32_t pcr_num, chunk_t input, chunk_t *output) -{ - return FALSE; -} - -METHOD(pts_t, quote_tpm, bool, - private_pts_t *this, bool use_quote2, chunk_t *pcr_comp, chunk_t *quote_sig) -{ - return FALSE; + return this->tpm->quote(this->tpm, this->aik_handle, pcr_sel, HASH_SHA1, + this->secret, quote_mode, quote_info, quote_sig); } -#endif /* TSS_TROUSERS */ - -/** - * TPM_QUOTE_INFO structure: - * 4 bytes of version - * 4 bytes 'Q' 'U' 'O' 'T' - * 20 byte SHA1 of TCPA_PCR_COMPOSITE - * 20 byte nonce - * - * TPM_QUOTE_INFO2 structure: - * 2 bytes Tag 0x0036 TPM_Tag_Quote_info2 - * 4 bytes 'Q' 'U' 'T' '2' - * 20 bytes nonce - * 26 bytes PCR_INFO_SHORT - */ - -METHOD(pts_t, get_quote_info, bool, - private_pts_t *this, bool use_quote2, bool use_ver_info, - pts_meas_algorithms_t comp_hash_algo, - chunk_t *out_pcr_comp, chunk_t *out_quote_info) +METHOD(pts_t, get_quote, bool, + private_pts_t *this, tpm_tss_quote_info_t *quote_info, chunk_t *quoted) { - chunk_t selection, pcr_comp, hash_pcr_comp; - bio_writer_t *writer; - hasher_t *hasher; + tpm_tss_pcr_composite_t *pcr_composite; + bool success; if (!this->pcrs->get_count(this->pcrs)) { @@ -930,128 +671,93 @@ METHOD(pts_t, get_quote_info, bool, "unable to construct TPM Quote Info"); return FALSE; } - if (use_quote2 && use_ver_info && !this->tpm_version_info.ptr) - { - DBG1(DBG_PTS, "TPM Version Information unavailable, ", - "unable to construct TPM Quote Info2"); - return FALSE; - } - - pcr_comp = this->pcrs->get_composite(this->pcrs); - - - /* Output the TPM_PCR_COMPOSITE expected from IMC */ - if (comp_hash_algo) + if (quote_info->get_quote_mode(quote_info) == TPM_QUOTE2_VERSION_INFO) { - hash_algorithm_t algo; - - algo = pts_meas_algo_to_hash(comp_hash_algo); - hasher = lib->crypto->create_hasher(lib->crypto, algo); - - /* Hash the PCR Composite Structure */ - if (!hasher || !hasher->allocate_hash(hasher, pcr_comp, out_pcr_comp)) + if (!this->tpm_version_info.ptr) { - DESTROY_IF(hasher); - free(pcr_comp.ptr); + DBG1(DBG_PTS, "TPM Version Information unavailable, ", + "unable to construct TPM Quote Info2"); return FALSE; } - DBG3(DBG_PTS, "constructed PCR Composite hash: %#B", out_pcr_comp); - hasher->destroy(hasher); + quote_info->set_version_info(quote_info, this->tpm_version_info); } - else - { - *out_pcr_comp = chunk_clone(pcr_comp); - } - - /* SHA1 hash of PCR Composite to construct TPM_QUOTE_INFO */ - hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); - if (!hasher || !hasher->allocate_hash(hasher, pcr_comp, &hash_pcr_comp)) - { - DESTROY_IF(hasher); - chunk_free(out_pcr_comp); - free(pcr_comp.ptr); - return FALSE; - } - hasher->destroy(hasher); - - /* Construct TPM_QUOTE_INFO/TPM_QUOTE_INFO2 structure */ - writer = bio_writer_create(TPM_QUOTE_INFO_LEN); - - if (use_quote2) - { - /* TPM Structure Tag */ - writer->write_uint16(writer, TPM_TAG_QUOTE_INFO2); - - /* Magic QUT2 value */ - writer->write_data(writer, chunk_create("QUT2", 4)); - - /* Secret assessment value 20 bytes (nonce) */ - writer->write_data(writer, this->secret); - - /* PCR selection */ - selection.ptr = pcr_comp.ptr; - selection.len = 2 + this->pcrs->get_selection_size(this->pcrs); - writer->write_data(writer, selection); - - /* TPM Locality Selection */ - writer->write_uint8(writer, TPM_LOC_ZERO); - - /* PCR Composite Hash */ - writer->write_data(writer, hash_pcr_comp); - - if (use_ver_info) - { - /* TPM version Info */ - writer->write_data(writer, this->tpm_version_info); - } - } - else - { - /* Version number */ - writer->write_data(writer, chunk_from_chars(1, 1, 0, 0)); - - /* Magic QUOT value */ - writer->write_data(writer, chunk_create("QUOT", 4)); - - /* PCR Composite Hash */ - writer->write_data(writer, hash_pcr_comp); - - /* Secret assessment value 20 bytes (nonce) */ - writer->write_data(writer, this->secret); - } - - /* TPM Quote Info */ - *out_quote_info = writer->extract_buf(writer); - DBG3(DBG_PTS, "constructed TPM Quote Info: %B", out_quote_info); + pcr_composite = this->pcrs->get_composite(this->pcrs); - writer->destroy(writer); - free(pcr_comp.ptr); - free(hash_pcr_comp.ptr); + success = quote_info->get_quote(quote_info, this->secret, + pcr_composite, quoted); + chunk_free(&pcr_composite->pcr_select); + chunk_free(&pcr_composite->pcr_composite); + free(pcr_composite); - return TRUE; + return success; } METHOD(pts_t, verify_quote_signature, bool, - private_pts_t *this, chunk_t data, chunk_t signature) + private_pts_t *this, hash_algorithm_t digest_alg, chunk_t digest, + chunk_t signature) { - public_key_t *aik_pub_key; + public_key_t *aik_pubkey; + signature_scheme_t scheme; - aik_pub_key = this->aik->get_public_key(this->aik); - if (!aik_pub_key) + aik_pubkey = this->aik_cert->get_public_key(this->aik_cert); + if (!aik_pubkey) { DBG1(DBG_PTS, "failed to get public key from AIK certificate"); return FALSE; } - if (!aik_pub_key->verify(aik_pub_key, SIGN_RSA_EMSA_PKCS1_SHA1, - data, signature)) + /* Determine signing scheme */ + switch (aik_pubkey->get_type(aik_pubkey)) + { + case KEY_RSA: + switch (digest_alg) + { + case HASH_SHA1: + scheme = SIGN_RSA_EMSA_PKCS1_SHA1; + break; + case HASH_SHA256: + scheme = SIGN_RSA_EMSA_PKCS1_SHA256; + break; + case HASH_SHA384: + scheme = SIGN_RSA_EMSA_PKCS1_SHA384; + break; + case HASH_SHA512: + scheme = SIGN_RSA_EMSA_PKCS1_SHA512; + break; + default: + scheme = SIGN_UNKNOWN; + } + break; + case KEY_ECDSA: + switch (digest_alg) + { + case HASH_SHA256: + scheme = SIGN_ECDSA_256; + break; + case HASH_SHA384: + scheme = SIGN_ECDSA_384; + break; + case HASH_SHA512: + scheme = SIGN_ECDSA_521; + break; + default: + scheme = SIGN_UNKNOWN; + } + break; + default: + DBG1(DBG_PTS, "%N AIK key type not supported", key_type_names, + aik_pubkey->get_type(aik_pubkey)); + return FALSE; + } + + if (!aik_pubkey->verify(aik_pubkey, scheme, digest, signature)) { DBG1(DBG_PTS, "signature verification failed for TPM Quote Info"); - DESTROY_IF(aik_pub_key); + DESTROY_IF(aik_pubkey); return FALSE; } - aik_pub_key->destroy(aik_pub_key); + aik_pubkey->destroy(aik_pubkey); return TRUE; } @@ -1064,78 +770,17 @@ METHOD(pts_t, get_pcrs, pts_pcr_t*, METHOD(pts_t, destroy, void, private_pts_t *this) { + DESTROY_IF(this->tpm); DESTROY_IF(this->pcrs); - DESTROY_IF(this->aik); + DESTROY_IF(this->aik_cert); DESTROY_IF(this->dh); free(this->initiator_nonce.ptr); free(this->responder_nonce.ptr); free(this->secret.ptr); - free(this->aik_blob.ptr); free(this->tpm_version_info.ptr); free(this); } - -#ifdef TSS_TROUSERS - -/** - * Check for a TPM by querying for TPM Version Info - */ -static bool has_tpm(private_pts_t *this) -{ - TSS_HCONTEXT hContext; - TSS_HTPM hTPM; - TSS_RESULT result; - uint32_t version_info_len; - - result = Tspi_Context_Create(&hContext); - if (result != TSS_SUCCESS) - { - DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x", - result); - return FALSE; - } - result = Tspi_Context_Connect(hContext, NULL); - if (result != TSS_SUCCESS) - { - goto err; - } - result = Tspi_Context_GetTpmObject (hContext, &hTPM); - if (result != TSS_SUCCESS) - { - goto err; - } - result = Tspi_TPM_GetCapability(hTPM, TSS_TPMCAP_VERSION_VAL, 0, NULL, - &version_info_len, - &this->tpm_version_info.ptr); - this->tpm_version_info.len = version_info_len; - if (result != TSS_SUCCESS) - { - goto err; - } - this->tpm_version_info = chunk_clone(this->tpm_version_info); - - Tspi_Context_FreeMemory(hContext, NULL); - Tspi_Context_Close(hContext); - return TRUE; - - err: - DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result); - Tspi_Context_FreeMemory(hContext, NULL); - Tspi_Context_Close(hContext); - return FALSE; -} - -#else /* TSS_TROUSERS */ - -static bool has_tpm(private_pts_t *this) -{ - return FALSE; -} - -#endif /* TSS_TROUSERS */ - - /** * See header */ @@ -1174,9 +819,9 @@ pts_t *pts_create(bool is_imc) .get_metadata = _get_metadata, .read_pcr = _read_pcr, .extend_pcr = _extend_pcr, - .quote_tpm = _quote_tpm, + .quote = _quote, .get_pcrs = _get_pcrs, - .get_quote_info = _get_quote_info, + .get_quote = _get_quote, .verify_quote_signature = _verify_quote_signature, .destroy = _destroy, }, @@ -1189,12 +834,11 @@ pts_t *pts_create(bool is_imc) if (is_imc) { - if (has_tpm(this)) + this->tpm = tpm_tss_probe(TPM_VERSION_ANY); + if (this->tpm) { - this->has_tpm = TRUE; this->proto_caps |= PTS_PROTO_CAPS_T | PTS_PROTO_CAPS_D; load_aik(this); - load_aik_blob(this); } } else diff --git a/src/libimcv/pts/pts.h b/src/libimcv/pts/pts.h index 1e07c4be3..f3da659dc 100644 --- a/src/libimcv/pts/pts.h +++ b/src/libimcv/pts/pts.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2011 Sansar Choinyambuu - * Copyright (C) 2012-2014 Andreas Steffen + * Copyright (C) 2012-2016 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -32,9 +32,10 @@ typedef struct pts_t pts_t; #include "pts_dh_group.h" #include "pts_pcr.h" #include "pts_req_func_comp_evid.h" -#include "pts_simple_evid_final.h" #include "components/pts_comp_func_name.h" +#include <tpm_tss_quote_info.h> + #include <library.h> #include <collections/linked_list.h> @@ -71,11 +72,6 @@ typedef struct pts_t pts_t; #define ASSESSMENT_SECRET_LEN 20 /** - * Length of the TPM_QUOTE_INFO structure, TPM Spec 1.2 - */ -#define TPM_QUOTE_INFO_LEN 48 - -/** * Hashing algorithm used by tboot and trustedGRUB */ #define TRUSTED_HASH_ALGO PTS_MEAS_ALGO_SHA1 @@ -236,39 +232,39 @@ struct pts_t { pts_file_meta_t* (*get_metadata)(pts_t *this, char *pathname, bool is_dir); /** - * Reads given PCR value and returns it - * Expects owner secret to be WELL_KNOWN_SECRET + * Retrieve the current value of a PCR register in a given PCR bank * - * @param pcr_num Number of PCR to read - * @param pcr_value Chunk to save pcr read output - * @return NULL in case of TSS error, PCR value otherwise + * @param pcr_num PCR number + * @param pcr_value PCR value returned + * @param alg hash algorithm, selects PCR bank (TPM 2.0 only) + * @return TRUE if PCR value retrieval succeeded */ - bool (*read_pcr)(pts_t *this, uint32_t pcr_num, chunk_t *pcr_value); + bool (*read_pcr)(pts_t *this, uint32_t pcr_num, chunk_t *pcr_value, + hash_algorithm_t alg); /** - * Extends given PCR with given value - * Expects owner secret to be WELL_KNOWN_SECRET + * Extend a PCR register in a given PCR bank with a hash value * - * @param pcr_num Number of PCR to extend - * @param input Value to extend - * @param output Chunk to save PCR value after extension - * @return FALSE in case of TSS error, TRUE otherwise + * @param pcr_num PCR number + * @param pcr_value extended PCR value returned + * @param hash data to be extended into the PCR + * @param alg hash algorithm, selects PCR bank (TPM 2.0 only) + * @return TRUE if PCR extension succeeded */ - bool (*extend_pcr)(pts_t *this, uint32_t pcr_num, chunk_t input, - chunk_t *output); + bool (*extend_pcr)(pts_t *this, uint32_t pcr_num, chunk_t *pcr_value, + chunk_t data, hash_algorithm_t alg); /** * Quote over PCR's * Expects owner and SRK secret to be WELL_KNOWN_SECRET and no password set for AIK * - * @param use_quote2 Version of the Quote function to be used - * @param pcr_comp Chunk to save PCR composite structure - * @param quote_sig Chunk to save quote operation output - * without external data (anti-replay protection) - * @return FALSE in case of TSS error, TRUE otherwise + * @param quote_mode type of Quote signature + * @param quote_info returns various info covered by Quote signature + * @param quote_sig returns Quote signature + * @return FALSE in case of Quote error, TRUE otherwise */ - bool (*quote_tpm)(pts_t *this, bool use_quote2, chunk_t *pcr_comp, - chunk_t *quote_sig); + bool (*quote)(pts_t *this, tpm_quote_mode_t *quote_mode, + tpm_tss_quote_info_t **quote_info, chunk_t *quote_sig); /** * Get the shadow PCR set @@ -277,28 +273,26 @@ struct pts_t { */ pts_pcr_t* (*get_pcrs)(pts_t *this); - /** - * Constructs and returns TPM Quote Info structure expected from IMC + /** + * Computes digest of the constructed TPM Quote Info structure * - * @param use_quote2 Version of the TPM_QUOTE_INFO to be constructed - * @param use_ver_info Version info is concatenated to TPM_QUOTE_INFO2 - * @param comp_hash_algo Composite Hash Algorithm - * @param pcr_comp Output variable to store PCR Composite - * @param quote_info Output variable to store TPM Quote Info + * @param quote_info TPM Quote Info as received from IMC + * @param quoted Encoding of TPM Quote Info * @return FALSE in case of any error, TRUE otherwise */ - bool (*get_quote_info)(pts_t *this, bool use_quote2, bool ver_info_included, - pts_meas_algorithms_t comp_hash_algo, - chunk_t *pcr_comp, chunk_t *quote_info); + bool (*get_quote)(pts_t *this, tpm_tss_quote_info_t *quote_info, + chunk_t *quoted); /** * Constructs and returns PCR Quote Digest structure expected from IMC * - * @param data Calculated TPM Quote Digest + * @param digest_alg Hash algorithm used for TPM Quote Digest + * @param digest Calculated TPM Quote Digest * @param signature TPM Quote Signature received from IMC * @return FALSE if signature is not verified */ - bool (*verify_quote_signature)(pts_t *this, chunk_t data, chunk_t signature); + bool (*verify_quote_signature)(pts_t *this, hash_algorithm_t digest_alg, + chunk_t digest, chunk_t signature); /** * Destroys a pts_t object. diff --git a/src/libimcv/pts/pts_meas_algo.c b/src/libimcv/pts/pts_meas_algo.c index c06371123..246c37714 100644 --- a/src/libimcv/pts/pts_meas_algo.c +++ b/src/libimcv/pts/pts_meas_algo.c @@ -158,6 +158,24 @@ hash_algorithm_t pts_meas_algo_to_hash(pts_meas_algorithms_t algorithm) /** * Described in header. */ +pts_meas_algorithms_t pts_meas_algo_from_hash(hash_algorithm_t algorithm) +{ + switch (algorithm) + { + case HASH_SHA1: + return PTS_MEAS_ALGO_SHA1; + case HASH_SHA256: + return PTS_MEAS_ALGO_SHA256; + case HASH_SHA384: + return PTS_MEAS_ALGO_SHA384; + default: + return PTS_MEAS_ALGO_NONE; + } +} + +/** + * Described in header. + */ size_t pts_meas_algo_hash_size(pts_meas_algorithms_t algorithm) { switch (algorithm) diff --git a/src/libimcv/pts/pts_meas_algo.h b/src/libimcv/pts/pts_meas_algo.h index eec7e7981..d70310679 100644 --- a/src/libimcv/pts/pts_meas_algo.h +++ b/src/libimcv/pts/pts_meas_algo.h @@ -96,6 +96,14 @@ pts_meas_algorithms_t pts_meas_algo_select(pts_meas_algorithms_t supported_algos hash_algorithm_t pts_meas_algo_to_hash(pts_meas_algorithms_t algorithm); /** + * Convert hash_algorithm_t to pts_meas_algorithms_t + * + * @param algorithm PTS measurement algorithm type + * @return libstrongswan hash algorithm type + */ +pts_meas_algorithms_t pts_meas_algo_from_hash(hash_algorithm_t algorithm); + +/** * Return the hash size of a pts_meas_algorithm * * @param algorithm PTS measurement algorithm type diff --git a/src/libimcv/pts/pts_pcr.c b/src/libimcv/pts/pts_pcr.c index 895c273bb..d514532c5 100644 --- a/src/libimcv/pts/pts_pcr.c +++ b/src/libimcv/pts/pts_pcr.c @@ -200,10 +200,10 @@ METHOD(pts_pcr_t, extend, chunk_t, return this->pcrs[pcr]; } -METHOD(pts_pcr_t, get_composite, chunk_t, +METHOD(pts_pcr_t, get_composite, tpm_tss_pcr_composite_t*, private_pts_pcr_t *this) { - chunk_t composite; + tpm_tss_pcr_composite_t *pcr_composite; enumerator_t *enumerator; uint16_t selection_size; uint32_t pcr_field_size, pcr; @@ -212,14 +212,13 @@ METHOD(pts_pcr_t, get_composite, chunk_t, selection_size = get_selection_size(this); pcr_field_size = this->pcr_count * PTS_PCR_LEN; - composite = chunk_alloc(2 + selection_size + 4 + pcr_field_size); - pos = composite.ptr; - htoun16(pos, selection_size); - pos += 2; - memcpy(pos, this->pcr_select, selection_size); - pos += selection_size; - htoun32(pos, pcr_field_size); - pos += 4; + INIT(pcr_composite, + .pcr_select = chunk_alloc(selection_size), + .pcr_composite = chunk_alloc(pcr_field_size), + ); + + memcpy(pcr_composite->pcr_select.ptr, this->pcr_select, selection_size); + pos = pcr_composite->pcr_composite.ptr; enumerator = create_enumerator(this); while (enumerator->enumerate(enumerator, &pcr)) @@ -229,8 +228,7 @@ METHOD(pts_pcr_t, get_composite, chunk_t, } enumerator->destroy(enumerator); - DBG3(DBG_PTS, "constructed PCR Composite: %B", &composite); - return composite; + return pcr_composite; } METHOD(pts_pcr_t, destroy, void, diff --git a/src/libimcv/pts/pts_pcr.h b/src/libimcv/pts/pts_pcr.h index b6ca73edc..df84c679f 100644 --- a/src/libimcv/pts/pts_pcr.h +++ b/src/libimcv/pts/pts_pcr.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Andreas Steffen + * Copyright (C) 2012-2016 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -25,6 +25,8 @@ typedef struct pts_pcr_t pts_pcr_t; #include <library.h> +#include <tpm_tss_quote_info.h> + /** * Maximum number of PCR's of TPM, TPM Spec 1.2 */ @@ -100,7 +102,7 @@ struct pts_pcr_t { * * @return PCR Composite object (must be freed) */ - chunk_t (*get_composite)(pts_pcr_t *this); + tpm_tss_pcr_composite_t* (*get_composite)(pts_pcr_t *this); /** diff --git a/src/libimcv/pts/pts_simple_evid_final.h b/src/libimcv/pts/pts_simple_evid_final.h deleted file mode 100644 index 0c8dea0cc..000000000 --- a/src/libimcv/pts/pts_simple_evid_final.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2011 Sansar Choinyambuu - * 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 <http://www.fsf.org/copyleft/gpl.txt>. - * - * 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 pts_simple_evid_final pts_rsimple_evid_final - * @{ @ingroup pts - */ - -#ifndef PTS_SIMPLE_EVID_FINAL_H_ -#define PTS_SIMPLE_EVID_FINAL_H_ - -typedef enum pts_simple_evid_final_flag_t pts_simple_evid_final_flag_t; - -#include <library.h> - -/** - * PTS Simple Evidence Final Flags - */ -enum pts_simple_evid_final_flag_t { - /** TPM PCR Composite and TPM Quote Signature not included */ - PTS_SIMPLE_EVID_FINAL_NO = 0x00, - /** TPM PCR Composite and TPM Quote Signature included - * using TPM_QUOTE_INFO */ - PTS_SIMPLE_EVID_FINAL_QUOTE_INFO = 0x40, - /** TPM PCR Composite and TPM Quote Signature included - * using TPM_QUOTE_INFO2, TPM_CAP_VERSION_INFO not appended */ - PTS_SIMPLE_EVID_FINAL_QUOTE_INFO2 = 0x80, - /** TPM PCR Composite and TPM Quote Signature included - * using TPM_QUOTE_INFO2, TPM_CAP_VERSION_INFO appended */ - PTS_SIMPLE_EVID_FINAL_QUOTE_INFO2_CAP_VER = 0xC0, - /** Evidence Signature included */ - PTS_SIMPLE_EVID_FINAL_EVID_SIG = 0x20, -}; - -#endif /** PTS_SIMPLE_EVID_FINAL_H_ @}*/ diff --git a/src/libimcv/tcg/pts/tcg_pts_attr_simple_evid_final.c b/src/libimcv/tcg/pts/tcg_pts_attr_simple_evid_final.c index a847dcb70..267c85776 100644 --- a/src/libimcv/tcg/pts/tcg_pts_attr_simple_evid_final.c +++ b/src/libimcv/tcg/pts/tcg_pts_attr_simple_evid_final.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2011-2012 Sansar Choinyambuu - * Copyright (C) 2011-2014 Andreas Steffen + * Copyright (C) 2011-2016 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -15,7 +15,6 @@ */ #include "tcg_pts_attr_simple_evid_final.h" -#include "pts/pts_simple_evid_final.h" #include <pa_tnc/pa_tnc_msg.h> #include <bio/bio_writer.h> @@ -27,6 +26,7 @@ typedef struct private_tcg_pts_attr_simple_evid_final_t private_tcg_pts_attr_sim /** * Simple Evidence Final * see section 3.15.2 of PTS Protocol: Binding to TNC IF-M Specification + * plus non-standard extensions to cover the TPM 2.0 Quote Info format * * 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 @@ -37,17 +37,57 @@ typedef struct private_tcg_pts_attr_simple_evid_final_t private_tcg_pts_attr_sim * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * ~ Optional TPM PCR Composite (Variable Length) ~ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Opt. TPM Qual. Signer Length | Optional TPM Qualified Signer ~ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ~ Optional TPM Qualified Signer (Variable Length) ~ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Opt. TPM Clock Info Length | Optional TPM Clock Info ~ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ~ Optional TPM Clock Info (Variable Length) ~ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Opt. TPM Version Info Length | Optional TPM Version Info ~ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ~ Optional TPM Version Info (Variable Length) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Opt. TPM PCR Selection Length | Opt. TPM PCR Selection ~ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ~ Optional TPM PCR Selection (Variable Length) ~ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Optional TPM Quote Signature Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * ~ Optional TPM Quote Signature (Variable Length) ~ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * ~ Optional Evidence Signature (Variable Length) ~ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ +*/ #define PTS_SIMPLE_EVID_FINAL_SIZE 2 #define PTS_SIMPLE_EVID_FINAL_RESERVED 0x00 -#define PTS_SIMPLE_EVID_FINAL_FLAG_MASK 0xC0 + +/** + * PTS Simple Evidence Final Flags + */ +enum pts_simple_evid_final_flag_t { + /** TPM PCR Composite and TPM Quote Signature not included */ + PTS_SIMPLE_EVID_FINAL_NO = 0x00, + /** TPM Quote Info and TPM Quite Signature included + * using TPM 2.0 Quote Info format */ + PTS_SIMPLE_EVID_FINAL_EVID_QUOTE_INFO_TPM2 = 0x10, + /** Evidence Signature included */ + PTS_SIMPLE_EVID_FINAL_EVID_SIG = 0x20, + /** TPM PCR Composite and TPM Quote Signature included + * using TPM_QUOTE_INFO */ + PTS_SIMPLE_EVID_FINAL_QUOTE_INFO = 0x40, + /** TPM PCR Composite and TPM Quote Signature included + * using TPM_QUOTE_INFO2, TPM_CAP_VERSION_INFO not appended */ + PTS_SIMPLE_EVID_FINAL_QUOTE_INFO2 = 0x80, + /** TPM PCR Composite and TPM Quote Signature included + * using TPM_QUOTE_INFO2, TPM_CAP_VERSION_INFO appended */ + PTS_SIMPLE_EVID_FINAL_QUOTE_INFO2_CAP_VER = 0xC0, + /** Mask for the TPM Quote Info flags */ + PTS_SIMPLE_EVID_FINAL_QUOTE_INFO_MASK = 0xD0 +}; + /** * Private data of an tcg_pts_attr_simple_evid_final_t object. */ @@ -79,24 +119,14 @@ struct private_tcg_pts_attr_simple_evid_final_t { bool noskip_flag; /** - * Set of flags for Simple Evidence Final - */ - uint8_t flags; - - /** - * Optional Composite Hash Algorithm - */ - pts_meas_algorithms_t comp_hash_algorithm; - - /** - * Optional TPM PCR Composite + * Optional TPM Quote Info */ - chunk_t pcr_comp; + tpm_tss_quote_info_t *quote_info; /** * Optional TPM Quote Signature */ - chunk_t tpm_quote_sig; + chunk_t quote_sig; /** * Is Evidence Signature included? @@ -156,9 +186,9 @@ METHOD(pa_tnc_attr_t, destroy, void, { if (ref_put(&this->ref)) { + DESTROY_IF(this->quote_info); free(this->value.ptr); - free(this->pcr_comp.ptr); - free(this->tpm_quote_sig.ptr); + free(this->quote_sig.ptr); free(this->evid_sig.ptr); free(this); } @@ -167,6 +197,9 @@ METHOD(pa_tnc_attr_t, destroy, void, METHOD(pa_tnc_attr_t, build, void, private_tcg_pts_attr_simple_evid_final_t *this) { + chunk_t pcr_digest, pcr_select, qualified_signer, clock_info, version_info; + hash_algorithm_t pcr_digest_alg; + tpm_quote_mode_t quote_mode; bio_writer_t *writer; uint8_t flags; @@ -174,7 +207,26 @@ METHOD(pa_tnc_attr_t, build, void, { return; } - flags = this->flags & PTS_SIMPLE_EVID_FINAL_FLAG_MASK; + + quote_mode = this->quote_info->get_quote_mode(this->quote_info); + switch (quote_mode) + { + case TPM_QUOTE: + flags = PTS_SIMPLE_EVID_FINAL_QUOTE_INFO; + break; + case TPM_QUOTE2: + flags = PTS_SIMPLE_EVID_FINAL_QUOTE_INFO2; + break; + case TPM_QUOTE2_VERSION_INFO: + flags = PTS_SIMPLE_EVID_FINAL_QUOTE_INFO2_CAP_VER; + break; + case TPM_QUOTE_TPM2: + flags = PTS_SIMPLE_EVID_FINAL_EVID_QUOTE_INFO_TPM2; + break; + case TPM_QUOTE_NONE: + default: + flags = PTS_SIMPLE_EVID_FINAL_NO; + } if (this->has_evid_sig) { @@ -185,25 +237,35 @@ METHOD(pa_tnc_attr_t, build, void, writer->write_uint8 (writer, flags); writer->write_uint8 (writer, PTS_SIMPLE_EVID_FINAL_RESERVED); - /** Optional Composite Hash Algorithm field is always present - * Field has value of all zeroes if not used. - * Implemented adhering the suggestion of Paul Sangster 28.Oct.2011 - */ - writer->write_uint16(writer, this->comp_hash_algorithm); + pcr_digest_alg = this->quote_info->get_pcr_digest_alg(this->quote_info); + pcr_digest = this->quote_info->get_pcr_digest(this->quote_info); + + writer->write_uint16(writer, pts_meas_algo_from_hash(pcr_digest_alg)); /* Optional fields */ - if (this->flags != PTS_SIMPLE_EVID_FINAL_NO) + if (quote_mode != TPM_QUOTE_NONE) { - writer->write_uint32 (writer, this->pcr_comp.len); - writer->write_data (writer, this->pcr_comp); - - writer->write_uint32 (writer, this->tpm_quote_sig.len); - writer->write_data (writer, this->tpm_quote_sig); + writer->write_data32(writer, pcr_digest); } - if (this->has_evid_sig) + if (quote_mode == TPM_QUOTE_TPM2) + { + version_info = this->quote_info->get_version_info(this->quote_info); + this->quote_info->get_tpm2_info(this->quote_info, &qualified_signer, + &clock_info, &pcr_select); + writer->write_data16(writer, qualified_signer); + writer->write_data16(writer, clock_info); + writer->write_data16(writer, version_info); + writer->write_data16(writer, pcr_select); + } + + if (quote_mode != TPM_QUOTE_NONE) { - writer->write_data (writer, this->evid_sig); + writer->write_data32(writer, this->quote_sig); + if (this->has_evid_sig) + { + writer->write_data(writer, this->evid_sig); + } } this->value = writer->extract_buf(writer); @@ -214,10 +276,14 @@ METHOD(pa_tnc_attr_t, build, void, METHOD(pa_tnc_attr_t, process, status_t, private_tcg_pts_attr_simple_evid_final_t *this, uint32_t *offset) { + hash_algorithm_t pcr_digest_alg; + tpm_quote_mode_t quote_mode; bio_reader_t *reader; uint8_t flags, reserved; uint16_t algorithm; - uint32_t pcr_comp_len, tpm_quote_sig_len, evid_sig_len; + uint32_t evid_sig_len; + chunk_t pcr_digest = chunk_empty, quote_sig, evid_sig; + chunk_t qualified_signer, clock_info, version_info, pcr_select; status_t status = FAILED; *offset = 0; @@ -236,56 +302,99 @@ METHOD(pa_tnc_attr_t, process, status_t, reader->read_uint8(reader, &flags); reader->read_uint8(reader, &reserved); - this->flags = flags & PTS_SIMPLE_EVID_FINAL_FLAG_MASK; - this->has_evid_sig = (flags & PTS_SIMPLE_EVID_FINAL_EVID_SIG) != 0; + flags &= PTS_SIMPLE_EVID_FINAL_QUOTE_INFO_MASK; + + switch (flags) + { + case PTS_SIMPLE_EVID_FINAL_QUOTE_INFO: + quote_mode = TPM_QUOTE; + break; + case PTS_SIMPLE_EVID_FINAL_QUOTE_INFO2: + quote_mode = TPM_QUOTE2; + break; + case PTS_SIMPLE_EVID_FINAL_QUOTE_INFO2_CAP_VER: + quote_mode = TPM_QUOTE2_VERSION_INFO; + break; + case PTS_SIMPLE_EVID_FINAL_EVID_QUOTE_INFO_TPM2: + quote_mode = TPM_QUOTE_TPM2; + break; + case PTS_SIMPLE_EVID_FINAL_NO: + default: + quote_mode = TPM_QUOTE_NONE; + break; + } + /** Optional Composite Hash Algorithm field is always present * Field has value of all zeroes if not used. * Implemented adhering the suggestion of Paul Sangster 28.Oct.2011 */ - reader->read_uint16(reader, &algorithm); - this->comp_hash_algorithm = algorithm; + pcr_digest_alg = pts_meas_algo_to_hash(algorithm); + + /* Optional fields */ + if (quote_mode != TPM_QUOTE_NONE) + { + if (!reader->read_data32(reader, &pcr_digest)) + { + DBG1(DBG_TNC, "insufficient data for PTS Simple Evidence Final " + "PCR Composite"); + goto end; + } + } + this->quote_info = tpm_tss_quote_info_create(quote_mode, pcr_digest_alg, + pcr_digest); - /* Optional Composite Hash Algorithm and TPM PCR Composite fields */ - if (this->flags != PTS_SIMPLE_EVID_FINAL_NO) + if (quote_mode == TPM_QUOTE_TPM2) { - if (!reader->read_uint32(reader, &pcr_comp_len)) + if (!reader->read_data16(reader, &qualified_signer)) { DBG1(DBG_TNC, "insufficient data for PTS Simple Evidence Final " - "PCR Composite Length"); + "Qualified Signer"); goto end; } - if (!reader->read_data(reader, pcr_comp_len, &this->pcr_comp)) + if (!reader->read_data16(reader, &clock_info)) { DBG1(DBG_TNC, "insufficient data for PTS Simple Evidence Final " - "PCR Composite"); + "Clock Info"); goto end; } - this->pcr_comp = chunk_clone(this->pcr_comp); - - if (!reader->read_uint32(reader, &tpm_quote_sig_len)) + if (!reader->read_data16(reader, &version_info)) { DBG1(DBG_TNC, "insufficient data for PTS Simple Evidence Final " - "TPM Quote Singature Length"); + "Version Info"); goto end; } - if (!reader->read_data(reader, tpm_quote_sig_len, &this->tpm_quote_sig)) + if (!reader->read_data16(reader, &pcr_select)) + { + DBG1(DBG_TNC, "insufficient data for PTS Simple Evidence Final " + "PCR select"); + goto end; + } + this->quote_info->set_tpm2_info(this->quote_info, qualified_signer, + clock_info, pcr_select); + this->quote_info->set_version_info(this->quote_info, version_info); + } + + + if (quote_mode != TPM_QUOTE_NONE) + { + if (!reader->read_data32(reader, "e_sig)) { DBG1(DBG_TNC, "insufficient data for PTS Simple Evidence Final " "TPM Quote Singature"); goto end; } - this->tpm_quote_sig = chunk_clone(this->tpm_quote_sig); + this->quote_sig = chunk_clone(quote_sig); } /* Optional Evidence Signature field */ if (this->has_evid_sig) { evid_sig_len = reader->remaining(reader); - reader->read_data(reader, evid_sig_len, &this->evid_sig); - this->evid_sig = chunk_clone(this->evid_sig); + reader->read_data(reader, evid_sig_len, &evid_sig); + this->evid_sig = chunk_clone(evid_sig); } reader->destroy(reader); @@ -296,23 +405,18 @@ end: return status; } -METHOD(tcg_pts_attr_simple_evid_final_t, get_quote_info, uint8_t, +METHOD(tcg_pts_attr_simple_evid_final_t, get_quote_info, void, private_tcg_pts_attr_simple_evid_final_t *this, - pts_meas_algorithms_t *comp_hash_algo, chunk_t *pcr_comp, chunk_t *tpm_quote_sig) + tpm_tss_quote_info_t **quote_info, chunk_t *quote_sig) { - if (comp_hash_algo) - { - *comp_hash_algo = this->comp_hash_algorithm; - } - if (pcr_comp) + if (quote_info) { - *pcr_comp = this->pcr_comp; + *quote_info = this->quote_info; } - if (tpm_quote_sig) + if (quote_sig) { - *tpm_quote_sig = this->tpm_quote_sig; + *quote_sig = this->quote_sig; } - return this->flags; } METHOD(tcg_pts_attr_simple_evid_final_t, get_evid_sig, bool, @@ -335,9 +439,8 @@ METHOD(tcg_pts_attr_simple_evid_final_t, set_evid_sig, void, /** * Described in header. */ -pa_tnc_attr_t *tcg_pts_attr_simple_evid_final_create(uint8_t flags, - pts_meas_algorithms_t comp_hash_algorithm, - chunk_t pcr_comp, chunk_t tpm_quote_sig) +pa_tnc_attr_t *tcg_pts_attr_simple_evid_final_create( + tpm_tss_quote_info_t *quote_info, chunk_t quote_sig) { private_tcg_pts_attr_simple_evid_final_t *this; @@ -359,10 +462,8 @@ pa_tnc_attr_t *tcg_pts_attr_simple_evid_final_create(uint8_t flags, .set_evid_sig = _set_evid_sig, }, .type = { PEN_TCG, TCG_PTS_SIMPLE_EVID_FINAL }, - .flags = flags, - .comp_hash_algorithm = comp_hash_algorithm, - .pcr_comp = pcr_comp, - .tpm_quote_sig = tpm_quote_sig, + .quote_info = quote_info, + .quote_sig = quote_sig, .ref = 1, ); diff --git a/src/libimcv/tcg/pts/tcg_pts_attr_simple_evid_final.h b/src/libimcv/tcg/pts/tcg_pts_attr_simple_evid_final.h index aed4d941f..c12e520fd 100644 --- a/src/libimcv/tcg/pts/tcg_pts_attr_simple_evid_final.h +++ b/src/libimcv/tcg/pts/tcg_pts_attr_simple_evid_final.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2011 Sansar Choinyambuu - * Copyright (C) 2014 Andreas Steffen + * Copyright (C) 2014-2016 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -28,6 +28,8 @@ typedef struct tcg_pts_attr_simple_evid_final_t tcg_pts_attr_simple_evid_final_t #include "tcg_pts_attr_meas_algo.h" #include "pa_tnc/pa_tnc_attr.h" +#include <tpm_tss_quote_info.h> + /** * Class implementing the TCG PTS Simple Evidence Final attribute * @@ -40,16 +42,14 @@ struct tcg_pts_attr_simple_evid_final_t { pa_tnc_attr_t pa_tnc_attribute; /** - * Get Optional PCR Composite and TPM Quote Signature + * Get Optional TPM Quote Info and TPM Quote Signature * - * @param comp_hash_algo Optional Composite Hash Algorithm - * @param pcr_comp Optional PCR Composite - * @param tpm_quote sig Optional TPM Quote Signature - * @return PTS_SIMPLE_EVID_FINAL flags + * @param quote_info Optional TPM Quote Info + * @param quote sig Optional TPM Quote Signature */ - uint8_t (*get_quote_info)(tcg_pts_attr_simple_evid_final_t *this, - pts_meas_algorithms_t *comp_hash_algo, - chunk_t *pcr_comp, chunk_t *tpm_quote_sig); + void (*get_quote_info)(tcg_pts_attr_simple_evid_final_t *this, + tpm_tss_quote_info_t **quote_info, + chunk_t *quote_sig); /** * Get Optional Evidence Signature @@ -73,16 +73,11 @@ struct tcg_pts_attr_simple_evid_final_t { /** * Creates an tcg_pts_attr_simple_evid_final_t object * - * @param flags Set of flags - * @param comp_hash_algorithm Composite Hash Algorithm - * @param pcr_comp Optional TPM PCR Composite - * @param tpm_quote_sign Optional TPM Quote Signature + * @param quote_info Optional TPM Quote Info + * @param quote_sign Optional TPM Quote Signature */ pa_tnc_attr_t* tcg_pts_attr_simple_evid_final_create( - uint8_t flags, - pts_meas_algorithms_t comp_hash_algorithm, - chunk_t pcr_comp, - chunk_t tpm_quote_sign); + tpm_tss_quote_info_t *quote_info, chunk_t quote_sig); /** * Creates an tcg_pts_attr_simple_evid_final_t object from received data diff --git a/src/libpttls/pt_tls.c b/src/libpttls/pt_tls.c index 1b136a7f0..01493f45c 100644 --- a/src/libpttls/pt_tls.c +++ b/src/libpttls/pt_tls.c @@ -17,6 +17,7 @@ #include <utils/debug.h> #include <pen/pen.h> + /** * Described in header. */ diff --git a/src/libtpmtss/Makefile.am b/src/libtpmtss/Makefile.am new file mode 100644 index 000000000..8fcb44f6a --- /dev/null +++ b/src/libtpmtss/Makefile.am @@ -0,0 +1,25 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/libstrongswan + +AM_LDFLAGS = \ + -no-undefined + +ipseclib_LTLIBRARIES = libtpmtss.la +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 + +libtpmtss_la_LIBADD = \ + $(top_builddir)/src/libstrongswan/libstrongswan.la + +if USE_TSS2 +libtpmtss_la_LIBADD += -ltctisocket -ltss2 +endif + +if USE_TROUSERS +libtpmtss_la_LIBADD += -ltspi +endif + diff --git a/src/libtpmtss/tpm_tss.c b/src/libtpmtss/tpm_tss.c new file mode 100644 index 000000000..b7b970c8d --- /dev/null +++ b/src/libtpmtss/tpm_tss.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 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 <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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.h" +#include "tpm_tss_tss2.h" +#include "tpm_tss_trousers.h" + +/** + * Described in header. + */ +void libtpmtss_init(void) +{ + /* empty */ +} + +typedef tpm_tss_t*(*tpm_tss_create)(); + +/** + * See header. + */ +tpm_tss_t *tpm_tss_probe(tpm_version_t version) +{ + tpm_tss_create stacks[] = { + tpm_tss_tss2_create, + tpm_tss_trousers_create, + }; + tpm_tss_t *tpm; + int i; + + for (i = 0; i < countof(stacks); i++) + { + tpm = stacks[i](); + if (tpm) + { + if (version == TPM_VERSION_ANY || version == tpm->get_version(tpm)) + { + return tpm; + } + } + } + return NULL; +} diff --git a/src/libtpmtss/tpm_tss.h b/src/libtpmtss/tpm_tss.h new file mode 100644 index 000000000..9295b2969 --- /dev/null +++ b/src/libtpmtss/tpm_tss.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2016 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 <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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_tss tpm_tss + * @{ @ingroup libtpmtss + */ + +#ifndef TPM_TSS_H_ +#define TPM_TSS_H_ + +#include "tpm_tss_quote_info.h" + +#include <library.h> +#include <crypto/hashers/hasher.h> + +typedef enum tpm_version_t tpm_version_t; +typedef struct tpm_tss_t tpm_tss_t; + +/** + * TPM Versions + */ +enum tpm_version_t { + TPM_VERSION_ANY, + TPM_VERSION_1_2, + TPM_VERSION_2_0, +}; + +/** + * TPM access via TSS public interface + */ +struct tpm_tss_t { + + /** + * Get TPM version supported by TSS + * + * @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 + */ + chunk_t (*get_version_info)(tpm_tss_t *this); + + /** + * Generate AIK key pair bound to TPM (TPM 1.2 only) + * + * @param ca_modulus RSA modulus of CA public key + * @param aik_blob AIK private key blob + * @param aik_pubkey AIK public key + * @return TRUE if AIK key generation succeeded + */ + bool (*generate_aik)(tpm_tss_t *this, chunk_t ca_modulus, + chunk_t *aik_blob, chunk_t *aik_pubkey, + chunk_t *identity_req); + + /** + * 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 + */ + chunk_t (*get_public)(tpm_tss_t *this, uint32_t handle); + + /** + * Retrieve the current value of a PCR register in a given PCR bank + * + * @param pcr_num PCR number + * @param pcr_value PCR value returned + * @param alg hash algorithm, selects PCR bank (TPM 2.0 only) + * @return TRUE if PCR value retrieval succeeded + */ + bool (*read_pcr)(tpm_tss_t *this, uint32_t pcr_num, chunk_t *pcr_value, + hash_algorithm_t alg); + + /** + * Extend a PCR register in a given PCR bank with a hash value + * + * @param pcr_num PCR number + * @param pcr_value extended PCR value returned + * @param hash data to be extended into the PCR + * @param alg hash algorithm, selects PCR bank (TPM 2.0 only) + * @return TRUE if PCR extension succeeded + */ + bool (*extend_pcr)(tpm_tss_t *this, uint32_t pcr_num, chunk_t *pcr_value, + chunk_t data, hash_algorithm_t alg); + + /** + * Do a quote signature over a selection of PCR registers + * + * @param aik_handle object handle of AIK to be used for quote signature + * @param pcr_sel selection of PCR registers + * @param alg hash algorithm to be used for quote signature + * @param data additional data to be hashed into the quote + * @param quote_mode define current and legacy TPM quote modes + * @param quote_info returns various info covered by quote signature + * @param quote_sig returns quote signature + * @return TRUE if quote signature succeeded + */ + bool (*quote)(tpm_tss_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); + + /** + * Destroy a tpm_tss_t. + */ + void (*destroy)(tpm_tss_t *this); +}; + +/** + * Create a tpm_tss instance. + * + * @param version TPM version that must be supported by TSS + */ +tpm_tss_t *tpm_tss_probe(tpm_version_t version); + +/** + * Dummy libtpmtss initialization function needed for integrity test + */ +void libtpmtss_init(void); + +#endif /** TPM_TSS_H_ @}*/ diff --git a/src/libtpmtss/tpm_tss_quote_info.c b/src/libtpmtss/tpm_tss_quote_info.c new file mode 100644 index 000000000..6a58448ee --- /dev/null +++ b/src/libtpmtss/tpm_tss_quote_info.c @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2016 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 <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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_quote_info.h> + +#include <bio/bio_writer.h> + +#ifndef TPM_TAG_QUOTE_INFO2 +#define TPM_TAG_QUOTE_INFO2 0x0036 +#endif +#ifndef TPM_LOC_ZERO +#define TPM_LOC_ZERO 0x01 +#endif + +typedef struct private_tpm_tss_quote_info_t private_tpm_tss_quote_info_t; + +/** + * Private data of an tpm_tss_quote_info_t object. + */ +struct private_tpm_tss_quote_info_t { + + /** + * Public tpm_tss_quote_info_t interface. + */ + tpm_tss_quote_info_t public; + + /** + * TPM Quote Mode + */ + tpm_quote_mode_t quote_mode; + + /** + * TPM Qualified Signer + */ + chunk_t qualified_signer; + + /** + * TPM Clock Info + */ + chunk_t clock_info; + + /** + * TPM Version Info + */ + chunk_t version_info; + + /** + * TPM PCR Selection + */ + chunk_t pcr_select; + + /** + * TPM PCR Composite Hash + */ + chunk_t pcr_digest; + + /** + * TPM PCR Composite Hash algoritm + */ + hash_algorithm_t pcr_digest_alg; + + /** + * Reference count + */ + refcount_t ref; + +}; + +METHOD(tpm_tss_quote_info_t, get_quote_mode, tpm_quote_mode_t, + private_tpm_tss_quote_info_t *this) +{ + return this->quote_mode; +} + +METHOD(tpm_tss_quote_info_t, get_pcr_digest_alg, hash_algorithm_t, + private_tpm_tss_quote_info_t *this) +{ + return this->pcr_digest_alg; +} + +METHOD(tpm_tss_quote_info_t, get_pcr_digest, chunk_t, + private_tpm_tss_quote_info_t *this) +{ + return this->pcr_digest; +} + +METHOD(tpm_tss_quote_info_t, get_quote, bool, + private_tpm_tss_quote_info_t *this, chunk_t nonce, + tpm_tss_pcr_composite_t *composite, chunk_t *quoted) +{ + chunk_t pcr_composite, pcr_digest; + bio_writer_t *writer; + hasher_t *hasher; + bool equal_digests; + + /* Construct PCR Composite */ + writer = bio_writer_create(32); + + switch (this->quote_mode) + { + case TPM_QUOTE: + case TPM_QUOTE2: + case TPM_QUOTE2_VERSION_INFO: + writer->write_data16(writer, composite->pcr_select); + writer->write_data32(writer, composite->pcr_composite); + + break; + case TPM_QUOTE_TPM2: + writer->write_data(writer, composite->pcr_composite); + break; + case TPM_QUOTE_NONE: + break; + } + + pcr_composite = writer->extract_buf(writer); + writer->destroy(writer); + + DBG2(DBG_PTS, "constructed PCR Composite: %B", &pcr_composite); + + /* Compute PCR Composite Hash */ + hasher = lib->crypto->create_hasher(lib->crypto, this->pcr_digest_alg); + if (!hasher || !hasher->allocate_hash(hasher, pcr_composite, &pcr_digest)) + { + DESTROY_IF(hasher); + chunk_free(&pcr_composite); + return FALSE; + } + hasher->destroy(hasher); + chunk_free(&pcr_composite); + + DBG2(DBG_PTS, "constructed PCR Composite digest: %B", &pcr_digest); + + equal_digests = chunk_equals(pcr_digest, this->pcr_digest); + + /* Construct Quote Info */ + writer = bio_writer_create(32); + + switch (this->quote_mode) + { + case TPM_QUOTE: + /* Version number */ + writer->write_data(writer, chunk_from_chars(1, 1, 0, 0)); + + /* Magic QUOT value */ + writer->write_data(writer, chunk_from_str("QUOT")); + + /* PCR Composite Hash */ + writer->write_data(writer, pcr_digest); + + /* Secret assessment value 20 bytes (nonce) */ + writer->write_data(writer, nonce); + break; + case TPM_QUOTE2: + case TPM_QUOTE2_VERSION_INFO: + /* TPM Structure Tag */ + writer->write_uint16(writer, TPM_TAG_QUOTE_INFO2); + + /* Magic QUT2 value */ + writer->write_data(writer, chunk_from_str("QUT2")); + + /* Secret assessment value 20 bytes (nonce) */ + writer->write_data(writer, nonce); + + /* PCR selection */ + writer->write_data16(writer, composite->pcr_select); + + /* TPM Locality Selection */ + writer->write_uint8(writer, TPM_LOC_ZERO); + + /* PCR Composite Hash */ + writer->write_data(writer, pcr_digest); + + if (this->quote_mode == TPM_QUOTE2_VERSION_INFO) + { + /* TPM version Info */ + writer->write_data(writer, this->version_info); + } + break; + case TPM_QUOTE_TPM2: + /* Magic */ + writer->write_data(writer, chunk_from_chars(0xff,0x54,0x43,0x47)); + + /* Type */ + writer->write_uint16(writer, 0x8018); + + /* Qualified Signer */ + writer->write_data16(writer, this->qualified_signer); + + /* Extra Data */ + writer->write_data16(writer, nonce); + + /* Clock Info */ + writer->write_data(writer, this->clock_info); + + /* Firmware Version */ + writer->write_data(writer, this->version_info); + + /* PCR Selection */ + writer->write_data(writer, this->pcr_select); + + /* PCR Composite Hash */ + writer->write_data16(writer, pcr_digest); + break; + case TPM_QUOTE_NONE: + break; + } + chunk_free(&pcr_digest); + *quoted = writer->extract_buf(writer); + writer->destroy(writer); + + DBG2(DBG_PTS, "constructed TPM Quote Info: %B", quoted); + + if (!equal_digests) + { + DBG1(DBG_IMV, "received PCR Composite digest does not match " + "constructed one"); + chunk_free(quoted); + } + return equal_digests; +} + +METHOD(tpm_tss_quote_info_t, set_version_info, void, + private_tpm_tss_quote_info_t *this, chunk_t version_info) +{ + chunk_free(&this->version_info); + this->version_info = chunk_clone(version_info); +} + +METHOD(tpm_tss_quote_info_t, get_version_info, chunk_t, + private_tpm_tss_quote_info_t *this) +{ + return this->version_info; +} + +METHOD(tpm_tss_quote_info_t, set_tpm2_info, void, + private_tpm_tss_quote_info_t *this, chunk_t qualified_signer, + chunk_t clock_info, chunk_t pcr_select) +{ + chunk_free(&this->qualified_signer); + this->qualified_signer = chunk_clone(qualified_signer); + + chunk_free(&this->clock_info); + this->clock_info = chunk_clone(clock_info); + + chunk_free(&this->pcr_select); + this->pcr_select = chunk_clone(pcr_select); +} + +METHOD(tpm_tss_quote_info_t, get_tpm2_info, void, + private_tpm_tss_quote_info_t *this, chunk_t *qualified_signer, + chunk_t *clock_info, chunk_t *pcr_select) +{ + if (qualified_signer) + { + *qualified_signer = this->qualified_signer; + } + if (clock_info) + { + *clock_info = this->clock_info; + } + if (pcr_select) + { + *pcr_select = this->pcr_select; + } +} + +METHOD(tpm_tss_quote_info_t, get_ref, tpm_tss_quote_info_t*, + private_tpm_tss_quote_info_t *this) +{ + ref_get(&this->ref); + + return &this->public; +} + +METHOD(tpm_tss_quote_info_t, destroy, void, + private_tpm_tss_quote_info_t *this) +{ + if (ref_put(&this->ref)) + { + chunk_free(&this->qualified_signer); + chunk_free(&this->clock_info); + chunk_free(&this->version_info); + chunk_free(&this->pcr_select); + chunk_free(&this->pcr_digest); + free(this); + } +} + +/** + * See header + */ +tpm_tss_quote_info_t *tpm_tss_quote_info_create(tpm_quote_mode_t quote_mode, + hash_algorithm_t pcr_digest_alg, chunk_t pcr_digest) + +{ + private_tpm_tss_quote_info_t *this; + + INIT(this, + .public = { + .get_quote_mode = _get_quote_mode, + .get_pcr_digest_alg = _get_pcr_digest_alg, + .get_pcr_digest = _get_pcr_digest, + .get_quote = _get_quote, + .set_version_info = _set_version_info, + .get_version_info = _get_version_info, + .set_tpm2_info = _set_tpm2_info, + .get_tpm2_info = _get_tpm2_info, + .get_ref = _get_ref, + .destroy = _destroy, + }, + .quote_mode = quote_mode, + .pcr_digest_alg = pcr_digest_alg, + .pcr_digest = chunk_clone(pcr_digest), + .ref = 1, + ); + + return &this->public; +} diff --git a/src/libtpmtss/tpm_tss_quote_info.h b/src/libtpmtss/tpm_tss_quote_info.h new file mode 100644 index 000000000..5b1c45794 --- /dev/null +++ b/src/libtpmtss/tpm_tss_quote_info.h @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2016 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 <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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_tss_quote_info tpm_tss_quote_info + * @{ @ingroup libtpmtss + */ + +#ifndef TPM_TSS_QUOTE_INFO_H_ +#define TPM_TSS_QUOTE_INFO_H_ + +#include <library.h> + +#include <crypto/hashers/hasher.h> + +typedef enum tpm_quote_mode_t tpm_quote_mode_t; +typedef struct tpm_tss_quote_info_t tpm_tss_quote_info_t; +typedef struct tpm_tss_pcr_composite_t tpm_tss_pcr_composite_t; + +/** + * TPM Quote Modes + */ +enum tpm_quote_mode_t { + TPM_QUOTE_NONE, + TPM_QUOTE, + TPM_QUOTE2, + TPM_QUOTE2_VERSION_INFO, + TPM_QUOTE_TPM2 +}; + +struct tpm_tss_pcr_composite_t { + + /** + * Bit map of selected PCRs + */ + chunk_t pcr_select; + + /** + * Array of selected PCRs + */ + chunk_t pcr_composite; + +}; + +/** + * TPM Quote Information needed to verify the Quote Signature + */ +struct tpm_tss_quote_info_t { + + /** + * Get TPM Quote Mode + * + * @return TPM Quote Mode + */ + tpm_quote_mode_t (*get_quote_mode)(tpm_tss_quote_info_t *this); + + /** + * Get PCR Composite digest algorithm + * + * @return PCR Composite digest algorithm + */ + hash_algorithm_t (*get_pcr_digest_alg)(tpm_tss_quote_info_t *this); + + /** + * Get PCR Composite digest + * + * @return PCR Composite digest + */ + chunk_t (*get_pcr_digest)(tpm_tss_quote_info_t *this); + + /** + * Get TPM Quote Info digest, the basis of the TPM Quote Singature + * + * @param nonce Derived from the Diffie-Hellman exchange + * @param composite PCR Composite as computed by IMV + * @param quoted Encoded TPM Quote + * @return TRUE if TPM Quote was successfully constructed + */ + bool (*get_quote)(tpm_tss_quote_info_t *this, chunk_t nonce, + tpm_tss_pcr_composite_t *composite, + chunk_t *quoted); + + /** + * Set TPM version info (needed for TPM 1.2) + * + * @param version_info TPM 1.2 version info + */ + void (*set_version_info)(tpm_tss_quote_info_t *this, chunk_t version_info); + + /** + * Get TPM 2.0 version info (needed for TPM 2.0) + * + * @return TPM 2.0 firmwareVersioin + */ + chunk_t (*get_version_info)(tpm_tss_quote_info_t *this); + + /** + * Set TPM 2.0 info parameters (needed for TPM 2.0) + * + * @param qualified_signer TPM 2.0 qualifiedSigner + * @param clock_info TPM 2.0 clockInfo + * @param pcr_select TPM 2.0 pcrSelect + */ + void (*set_tpm2_info)(tpm_tss_quote_info_t *this, chunk_t qualified_signer, + chunk_t clock_info, chunk_t pcr_select); + + + /** + * Get TPM 2.0 info parameters (needed for TPM 2.0) + * + * @param qualified_signer TPM 2.0 qualifiedSigner + * @param clock_info TPM 2.0 clockInfo + * @param pcr_select TPM 2.0 pcrSelect + */ + void (*get_tpm2_info)(tpm_tss_quote_info_t *this, chunk_t *qualified_signer, + chunk_t *clock_info, chunk_t *pcr_select); + + /** + * Get reference to Quote Info object. + */ + tpm_tss_quote_info_t* (*get_ref)(tpm_tss_quote_info_t *this); + + /** + * Destroy a tpm_tss_quote_info_t. + */ + void (*destroy)(tpm_tss_quote_info_t *this); +}; + +/** + * Create a tpm_tss_quote_info instance. + * + * @param quote_mode TPM Quote mode + * @param pcr_digest_alg PCR Composite digest algorithm + * @param pcr_digest PCR Composite digest + */ +tpm_tss_quote_info_t *tpm_tss_quote_info_create(tpm_quote_mode_t quote_mode, + hash_algorithm_t pcr_digest_alg, chunk_t pcr_digest); + +#endif /** TPM_TSS_QUOTE_INFO_H_ @}*/ diff --git a/src/libtpmtss/tpm_tss_trousers.c b/src/libtpmtss/tpm_tss_trousers.c new file mode 100644 index 000000000..8be3ad877 --- /dev/null +++ b/src/libtpmtss/tpm_tss_trousers.c @@ -0,0 +1,655 @@ +/* + * Copyright (C) 2016 Andreas Steffen + * HSR Hochschule fuer Technik Rapperswil + * + * Copyright (c) 2008 Hal Finney + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "tpm_tss_trousers.h" + +#ifdef TSS_TROUSERS + +#ifdef _BASETSD_H_ +/* MinGW defines _BASETSD_H_, but TSS checks for _BASETSD_H */ +# define _BASETSD_H +#endif + +#include <trousers/tss.h> +#include <trousers/trousers.h> + +#define LABEL "TPM 1.2 -" + +/* size in bytes of a TSS AIK public key blob */ +#define AIK_PUBKEY_BLOB_SIZE 284 + +/* maximum number of PCR registers */ +#define PCR_NUM_MAX 24 + +typedef struct private_tpm_tss_trousers_t private_tpm_tss_trousers_t; +typedef struct aik_t aik_t; + +/** + * Private data of an tpm_tss_trousers_t object. + */ +struct private_tpm_tss_trousers_t { + + /** + * Public tpm_tss_trousers_t interface. + */ + tpm_tss_trousers_t interface; + + /** + * TSS context + */ + TSS_HCONTEXT hContext; + + /** + * TPM handle + */ + TSS_HTPM hTPM; + + /** + * TPM version info + */ + chunk_t version_info; + + /** + * List of AIKs retrievable by an object handle + */ + linked_list_t *aik_list; + +}; + +struct aik_t { + /** AIK object handle */ + uint32_t handle; + + /** AIK private key blob */ + chunk_t blob; + + /** AIK public key */ + chunk_t pubkey; +}; + +static void free_aik(aik_t *this) +{ + free(this->blob.ptr); + free(this->pubkey.ptr); + free(this); +} + +/** + * Initialize TSS context + * + * TPM 1.2 Specification, Part 2 TPM Structures, 21.6 TPM_CAP_VERSION_INFO + * + * typedef struct tdTPM_VERSION { + * TPM_VERSION_BYTE major; + * TPM_VERSION_BYTE minor; + * BYTE revMajor; + * BYTE revMinor; + * } TPM_VERSION; + * + * typedef struct tdTPM_CAP_VERSION_INFO { + * TPM_STRUCTURE_TAG tag; + * TPM_VERSION version; + * UINT16 specLevel; + * BYTE errataRev; + * BYTE tpmVendorID[4]; + * UINT16 vendorSpecificSize; + * [size_is(vendorSpecificSize)] BYTE* vendorSpecific; + * } TPM_CAP_VERSION_INFO; + */ +static bool initialize_context(private_tpm_tss_trousers_t *this) +{ + uint8_t *version_ptr; + uint32_t version_len; + + TSS_RESULT result; + TPM_CAP_VERSION_INFO *info; + + result = Tspi_Context_Create(&this->hContext); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "%s could not created context: 0x%x", + LABEL, result); + return FALSE; + } + + result = Tspi_Context_Connect(this->hContext, NULL); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "%s could not connect with context: 0x%x", + LABEL, result); + return FALSE; + } + + result = Tspi_Context_GetTpmObject (this->hContext, &this->hTPM); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "%s could not get TPM object: 0x%x", + LABEL, result); + return FALSE; + } + + result = Tspi_TPM_GetCapability(this->hTPM, TSS_TPMCAP_VERSION_VAL, 0, + NULL, &version_len, &version_ptr); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "%s Tspi_TPM_GetCapability failed: 0x%x", + LABEL, result); + return FALSE; + } + + info = (TPM_CAP_VERSION_INFO *)version_ptr; + DBG2(DBG_PTS, "TPM Version Info: Chip Version: %u.%u.%u.%u, " + "Spec Level: %u, Errata Rev: %u, Vendor ID: %.4s", + info->version.major, info->version.minor, + info->version.revMajor, info->version.revMinor, + untoh16(&info->specLevel), info->errataRev, info->tpmVendorID); + + this->version_info = chunk_clone(chunk_create(version_ptr, version_len)); + + return TRUE; +} + +/** + * Finalize TSS context + */ +static void finalize_context(private_tpm_tss_trousers_t *this) +{ + if (this->hContext) + { + Tspi_Context_FreeMemory(this->hContext, NULL); + Tspi_Context_Close(this->hContext); + } +} + +METHOD(tpm_tss_t, get_version, tpm_version_t, + private_tpm_tss_trousers_t *this) +{ + return TPM_VERSION_1_2; +} + +METHOD(tpm_tss_t, get_version_info, chunk_t, + private_tpm_tss_trousers_t *this) +{ + return this->version_info; +} + +METHOD(tpm_tss_t, generate_aik, bool, + private_tpm_tss_trousers_t *this, chunk_t ca_modulus, chunk_t *aik_blob, + chunk_t *aik_pubkey, chunk_t *identity_req) +{ + chunk_t aik_pubkey_blob; + chunk_t aik_modulus; + chunk_t aik_exponent; + + TSS_RESULT result; + TSS_HKEY hSRK; + TSS_HKEY hPCAKey; + TSS_HPOLICY hSrkPolicy; + TSS_HPOLICY hTPMPolicy; + TSS_HKEY hIdentKey; + TSS_UUID SRK_UUID = TSS_UUID_SRK; + BYTE secret[] = TSS_WELL_KNOWN_SECRET; + BYTE *IdentityReq; + UINT32 IdentityReqLen; + BYTE *blob; + UINT32 blobLen; + + /* get SRK plus SRK policy and set SRK secret */ + result = Tspi_Context_LoadKeyByUUID(this->hContext, TSS_PS_TYPE_SYSTEM, + SRK_UUID, &hSRK); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "%s Tspi_Context_LoadKeyByUUID for SRK failed: 0x%x", + LABEL, result); + return FALSE; + } + result = Tspi_GetPolicyObject(hSRK, TSS_POLICY_USAGE, &hSrkPolicy); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "%s Tspi_GetPolicyObject or SRK failed: 0x%x ", + LABEL, result); + return FALSE; + } + result = Tspi_Policy_SetSecret(hSrkPolicy, TSS_SECRET_MODE_SHA1, 20, secret); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "%s Tspi_Policy_SetSecret for SRK failed: 0x%x ", + LABEL, result); + return FALSE; + } + + /* get TPM plus TPM policy and set TPM secret */ + result = Tspi_Context_GetTpmObject (this->hContext, &this->hTPM); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "%s Tspi_Context_GetTpmObject failed: 0x%x", + LABEL, result); + return FALSE; + } + result = Tspi_GetPolicyObject(this->hTPM, TSS_POLICY_USAGE, &hTPMPolicy); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "%s Tspi_GetPolicyObject for TPM failed: 0x%x", + LABEL, result); + return FALSE; + } + result = Tspi_Policy_SetSecret(hTPMPolicy, TSS_SECRET_MODE_SHA1, 20, secret); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS,"%s Tspi_Policy_SetSecret for TPM failed: 0x%x", + LABEL, result); + return FALSE; + } + + /* create context for a 2048 bit AIK */ + result = Tspi_Context_CreateObject(this->hContext, TSS_OBJECT_TYPE_RSAKEY, + TSS_KEY_TYPE_IDENTITY | TSS_KEY_SIZE_2048 | + TSS_KEY_VOLATILE | TSS_KEY_NOT_MIGRATABLE, &hIdentKey); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "%s Tspi_Context_CreateObject for key failed: 0x%x", + LABEL, result); + return FALSE; + } + + /* create context for the Privacy CA public key and assign modulus */ + result = Tspi_Context_CreateObject(this->hContext, TSS_OBJECT_TYPE_RSAKEY, + TSS_KEY_TYPE_LEGACY|TSS_KEY_SIZE_2048, &hPCAKey); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "%s Tspi_Context_CreateObject for PCA failed: 0x%x", + LABEL, result); + return FALSE; + } + result = Tspi_SetAttribData (hPCAKey, TSS_TSPATTRIB_RSAKEY_INFO, + TSS_TSPATTRIB_KEYINFO_RSA_MODULUS, ca_modulus.len, + ca_modulus.ptr); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "%s Tspi_SetAttribData for PCA modulus failed: 0x%x", + LABEL, result); + return FALSE; + } + result = Tspi_SetAttribUint32(hPCAKey, TSS_TSPATTRIB_KEY_INFO, + TSS_TSPATTRIB_KEYINFO_ENCSCHEME, TSS_ES_RSAESPKCSV15); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS,"%s Tspi_SetAttribUint32 for PCA encryption scheme " + "failed: 0x%x", LABEL, result); + return FALSE; + } + + /* generate AIK */ + DBG1(DBG_LIB, "Generating identity key..."); + result = Tspi_TPM_CollateIdentityRequest(this->hTPM, hSRK, hPCAKey, 0, NULL, + hIdentKey, TSS_ALG_AES, &IdentityReqLen, &IdentityReq); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "%s Tspi_TPM_CollateIdentityRequest failed: 0x%x", + LABEL, result); + return FALSE; + } + *identity_req = chunk_create(IdentityReq, IdentityReqLen); + DBG3(DBG_LIB, "%s Identity Request: %B", LABEL, identity_req); + + /* load identity key */ + result = Tspi_Key_LoadKey (hIdentKey, hSRK); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "%s Tspi_Key_LoadKey for AIK failed: 0x%x", + LABEL, result); + return FALSE; + } + + /* output AIK private key in TSS blob format */ + result = Tspi_GetAttribData (hIdentKey, TSS_TSPATTRIB_KEY_BLOB, + TSS_TSPATTRIB_KEYBLOB_BLOB, &blobLen, &blob); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "%s Tspi_GetAttribData for private key blob failed: 0x%x", + LABEL, result); + return FALSE; + } + *aik_blob = chunk_create(blob, blobLen); + DBG3(DBG_LIB, "%s AIK private key blob: %B", LABEL, aik_blob); + + /* output AIK Public Key in TSS blob format */ + result = Tspi_GetAttribData (hIdentKey, TSS_TSPATTRIB_KEY_BLOB, + TSS_TSPATTRIB_KEYBLOB_PUBLIC_KEY, &blobLen, &blob); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "%s Tspi_GetAttribData for public key blob failed: 0x%x", + LABEL, result); + return FALSE; + } + aik_pubkey_blob = chunk_create(blob, blobLen); + DBG3(DBG_LIB, "%s AIK public key blob: %B", LABEL, &aik_pubkey_blob); + + /* create a trusted AIK public key */ + if (aik_pubkey_blob.len != AIK_PUBKEY_BLOB_SIZE) + { + DBG1(DBG_PTS, "%s AIK public key is not in TSS blob format", + LABEL); + return FALSE; + } + aik_modulus = chunk_skip(aik_pubkey_blob, AIK_PUBKEY_BLOB_SIZE - 256); + aik_exponent = chunk_from_chars(0x01, 0x00, 0x01); + + /* output subjectPublicKeyInfo encoding of AIK public 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 FALSE; + } + return TRUE; +} + +METHOD(tpm_tss_t, get_public, chunk_t, + private_tpm_tss_trousers_t *this, uint32_t handle) +{ + enumerator_t *enumerator; + chunk_t aik_pubkey = chunk_empty; + aik_t *aik; + + enumerator = this->aik_list->create_enumerator(this->aik_list); + while (enumerator->enumerate(enumerator, &aik)) + { + if (aik->handle == handle) + { + aik_pubkey = chunk_clone(aik->pubkey); + break; + } + } + enumerator->destroy(enumerator); + + return aik_pubkey; +} + +METHOD(tpm_tss_t, read_pcr, bool, + private_tpm_tss_trousers_t *this, uint32_t pcr_num, chunk_t *pcr_value, + hash_algorithm_t alg) +{ + TSS_RESULT result; + uint8_t *value; + uint32_t len; + + result = Tspi_TPM_PcrRead(this->hTPM, pcr_num, &len, &value); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "%s Tspi_TPM_PcrRead failed: 0x%x", LABEL, result); + return FALSE; + } + *pcr_value = chunk_clone(chunk_create(value, len)); + + return TRUE; +} + +METHOD(tpm_tss_t, extend_pcr, bool, + private_tpm_tss_trousers_t *this, uint32_t pcr_num, chunk_t *pcr_value, + chunk_t data, hash_algorithm_t alg) +{ + TSS_RESULT result; + uint32_t pcr_len; + uint8_t *pcr_ptr; + + result = Tspi_TPM_PcrExtend(this->hTPM, pcr_num, data.len, data.ptr, + NULL, &pcr_len, &pcr_ptr); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "%s Tspi_TPM_PcrExtend failed: 0x%x", LABEL, result); + return FALSE; + } + *pcr_value = chunk_clone(chunk_create(pcr_ptr, pcr_len)); + + return TRUE; +} + +METHOD(tpm_tss_t, quote, bool, + private_tpm_tss_trousers_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) +{ + TSS_HKEY hAIK; + TSS_HKEY hSRK; + TSS_HPOLICY srkUsagePolicy; + TSS_UUID SRK_UUID = TSS_UUID_SRK; + TSS_HPCRS hPcrComposite; + TSS_VALIDATION valData; + TSS_RESULT result; + uint8_t secret[] = TSS_WELL_KNOWN_SECRET; + uint8_t *version_info, *comp_hash; + uint32_t version_info_size, pcr; + aik_t *aik; + chunk_t aik_blob = chunk_empty; + chunk_t quote_chunk, pcr_digest; + enumerator_t *enumerator; + bool success = FALSE; + + /* Retrieve SRK from TPM and set the authentication to well known secret*/ + result = Tspi_Context_LoadKeyByUUID(this->hContext, TSS_PS_TYPE_SYSTEM, + SRK_UUID, &hSRK); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "%s Tspi_Context_LoadKeyByUUID for SRK failed: 0x%x", + LABEL, result); + return FALSE; + } + result = Tspi_GetPolicyObject(hSRK, TSS_POLICY_USAGE, &srkUsagePolicy); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "%s Tspi_GetPolicyObject for SRK failed: 0x%x", + LABEL, result); + return FALSE; + } + result = Tspi_Policy_SetSecret(srkUsagePolicy, TSS_SECRET_MODE_SHA1, + 20, secret); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "%s Tspi_Policy_SetSecret for SRK failed: 0x%x", + LABEL, result); + return FALSE; + } + + /* Retrieve AIK using its handle and load private key into TPM 1.2 */ + enumerator = this->aik_list->create_enumerator(this->aik_list); + while (enumerator->enumerate(enumerator, &aik)) + { + if (aik->handle == aik_handle) + { + aik_blob = aik->blob; + break; + } + } + enumerator->destroy(enumerator); + + if (aik_blob.len == 0) + { + DBG1(DBG_PTS, "%s AIK private key for handle 0x%80x not found", LABEL); + return FALSE; + } + result = Tspi_Context_LoadKeyByBlob(this->hContext, hSRK, aik_blob.len, + aik_blob.ptr, &hAIK); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "%s Tspi_Context_LoadKeyByBlob for AIK failed: 0x%x", + LABEL, result); + return FALSE; + } + + /* Create PCR composite object */ + result = Tspi_Context_CreateObject(this->hContext, TSS_OBJECT_TYPE_PCRS, + (*quote_mode == TPM_QUOTE) ? TSS_PCRS_STRUCT_INFO : + TSS_PCRS_STRUCT_INFO_SHORT, + &hPcrComposite); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "%s Tspi_Context_CreateObject for pcrComposite failed: " + "0x%x", LABEL, result); + goto err1; + } + + /* Select PCRs */ + for (pcr = 0; pcr < PCR_NUM_MAX; pcr++) + { + if (pcr_sel & (1 << pcr)) + { + result = (*quote_mode == TPM_QUOTE) ? + Tspi_PcrComposite_SelectPcrIndex(hPcrComposite, pcr) : + Tspi_PcrComposite_SelectPcrIndexEx(hPcrComposite, pcr, + TSS_PCRS_DIRECTION_RELEASE); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "%s Tspi_PcrComposite_SelectPcrIndex failed: " + "0x%x", LABEL, result); + goto err2; + } + } + } + + /* Set the Validation Data */ + valData.ulExternalDataLength = data.len; + valData.rgbExternalData = data.ptr; + + /* TPM Quote */ + result = (*quote_mode == TPM_QUOTE) ? + Tspi_TPM_Quote (this->hTPM, hAIK, hPcrComposite, &valData) : + Tspi_TPM_Quote2(this->hTPM, hAIK, + *quote_mode == TPM_QUOTE2_VERSION_INFO, + hPcrComposite, &valData, &version_info_size, + &version_info); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "%s Tspi_TPM_Quote%s failed: 0x%x", LABEL, + (*quote_mode == TPM_QUOTE) ? "" : "2", result); + goto err2; + } + + if (*quote_mode == TPM_QUOTE) + { + /* TPM_Composite_Hash starts at byte 8 of TPM_Quote_Info structure */ + comp_hash = valData.rgbData + 8; + } + else + { + /* TPM_Composite_Hash is last 20 bytes of TPM_Quote_Info2 structure */ + comp_hash = valData.rgbData + valData.ulDataLength - version_info_size - + HASH_SIZE_SHA1; + } + pcr_digest = chunk_create(comp_hash, HASH_SIZE_SHA1); + DBG2(DBG_PTS, "PCR composite digest: %B", &pcr_digest); + + quote_chunk = chunk_create(valData.rgbData, valData.ulDataLength); + DBG2(DBG_PTS, "TPM Quote Info: %B", "e_chunk); + + *quote_info = tpm_tss_quote_info_create(*quote_mode, HASH_SHA1, pcr_digest); + + *quote_sig = chunk_clone(chunk_create(valData.rgbValidationData, + valData.ulValidationDataLength)); + DBG2(DBG_PTS, "TPM Quote Signature: %B", quote_sig); + + success = TRUE; + +err2: + Tspi_Context_CloseObject(this->hContext, hPcrComposite); +err1: + Tspi_Context_CloseObject(this->hContext, hAIK); + + return success; +} + +METHOD(tpm_tss_t, destroy, void, + private_tpm_tss_trousers_t *this) +{ + finalize_context(this); + this->aik_list->destroy_function(this->aik_list, (void*)free_aik); + free(this->version_info.ptr); + free(this); +} + +METHOD(tpm_tss_trousers_t, load_aik, void, + private_tpm_tss_trousers_t *this, chunk_t blob, chunk_t pubkey, + uint32_t handle) +{ + aik_t *item; + + INIT(item, + .handle = handle, + .blob = blob, + .pubkey = pubkey, + ); + + this->aik_list->insert_last(this->aik_list, item); +} + +/** + * See header + */ +tpm_tss_t *tpm_tss_trousers_create() +{ + private_tpm_tss_trousers_t *this; + bool available; + + INIT(this, + .interface = { + .public = { + .get_version = _get_version, + .get_version_info = _get_version_info, + .generate_aik = _generate_aik, + .get_public = _get_public, + .read_pcr = _read_pcr, + .quote = _quote, + .extend_pcr = _extend_pcr, + .destroy = _destroy, + }, + .load_aik = _load_aik, + }, + .aik_list = linked_list_create(), + ); + + available = initialize_context(this); + DBG1(DBG_PTS, "TPM 1.2 via TrouSerS %savailable", available ? "" : "not "); + + if (!available) + { + destroy(this); + return NULL; + } + return &this->interface.public; +} + +#else /* TSS_TROUSERS */ + +tpm_tss_t *tpm_tss_trousers_create() +{ + return NULL; +} + +#endif /* TSS_TROUSERS */ + + + diff --git a/src/libtpmtss/tpm_tss_trousers.h b/src/libtpmtss/tpm_tss_trousers.h new file mode 100644 index 000000000..3afba0db2 --- /dev/null +++ b/src/libtpmtss/tpm_tss_trousers.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2016 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 <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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_tss_trousers tpm_tss_trousers + * @{ @ingroup libtpmtss + */ + +#ifndef TPM_TSS_TROUSERS_H_ +#define TPM_TSS_TROUSERS_H_ + +#include "tpm_tss.h" + +typedef struct tpm_tss_trousers_t tpm_tss_trousers_t; + +/** + * TPM 1.2 access via TrouSerS public interface + */ +struct tpm_tss_trousers_t { + + tpm_tss_t public; + + /** + * Load AIK public and private key pair and save it under an object handle + * + * @param blob encrypted AIK private key + * @param pubkey AIK public key + * @param handle object handle under which the AIK key is stored + */ + void (*load_aik)(tpm_tss_trousers_t *this, chunk_t blob, chunk_t pubkey, + uint32_t handle); + +}; + +/** + * Create a tpm_tss_trousers instance. + */ +tpm_tss_t *tpm_tss_trousers_create(); + +#endif /** TPM_TSS_TROUSERS_H_ @}*/ diff --git a/src/libtpmtss/tpm_tss_tss2.c b/src/libtpmtss/tpm_tss_tss2.c new file mode 100644 index 000000000..79db8eeef --- /dev/null +++ b/src/libtpmtss/tpm_tss_tss2.c @@ -0,0 +1,696 @@ +/* + * Copyright (C) 2016 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 <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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 + +#include <asn1/asn1.h> +#include <asn1/oid.h> +#include <bio/bio_reader.h> + +#include <tss2/tpm20.h> +#include <tcti/tcti_socket.h> + +#define LABEL "TPM 2.0 -" + +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 + */ + TPM_ALG_ID supported_algs[TPM_PT_ALGORITHM_SET]; +}; + +/** + * Some symbols required by libtctisocket + */ +FILE *outFp; +uint8_t simulator = 1; + +int TpmClientPrintf (uint8_t type, const char *format, ...) +{ + return 0; +} + +/** + * Convert hash algorithm to TPM_ALG_ID + */ +static TPM_ALG_ID hash_alg_to_tpm_alg_id(hash_algorithm_t alg) +{ + switch (alg) + { + case HASH_SHA1: + return TPM_ALG_SHA1; + case HASH_SHA256: + return TPM_ALG_SHA256; + case HASH_SHA384: + return TPM_ALG_SHA384; + case HASH_SHA512: + return TPM_ALG_SHA512; + default: + return TPM_ALG_ERROR; + } +} + +/** + * Convert TPM_ALG_ID to hash algorithm + */ +static hash_algorithm_t hash_alg_from_tpm_alg_id(TPM_ALG_ID alg) +{ + switch (alg) + { + case TPM_ALG_SHA1: + return HASH_SHA1; + case TPM_ALG_SHA256: + return HASH_SHA256; + case TPM_ALG_SHA384: + return HASH_SHA384; + case TPM_ALG_SHA512: + return HASH_SHA512; + default: + return HASH_UNKNOWN; + } +} + +/** + * Check if an algorithm given by its TPM_ALG_ID is supported by the TPM + */ +static bool is_supported_alg(private_tpm_tss_tss2_t *this, TPM_ALG_ID alg_id) +{ + int i; + + if (alg_id == TPM_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; + TPMI_YES_NO more_data; + TPM_ALG_ID alg; + uint32_t rval, i; + size_t len = BUF_LEN; + char buf[BUF_LEN]; + char *pos = buf; + int written; + + /* get supported algorithms */ + rval = Tss2_Sys_GetCapability(this->sys_context, 0, TPM_CAP_ALGS, + 0, TPM_PT_ALGORITHM_SET, &more_data, &cap_data, 0); + if (rval != TPM_RC_SUCCESS) + { + DBG1(DBG_PTS, "%s GetCapability failed for TPM_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, TPM_CAP_ECC_CURVES, + 0, TPM_PT_LOADED_CURVES, &more_data, &cap_data, 0); + if (rval != TPM_RC_SUCCESS) + { + DBG1(DBG_PTS, "%s GetCapability failed for TPM_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 TSS context + */ +static bool initialize_context(private_tpm_tss_tss2_t *this) +{ + size_t tcti_context_size; + uint32_t sys_context_size; + uint32_t rval; + + TCTI_SOCKET_CONF rm_if_config = { DEFAULT_HOSTNAME, + DEFAULT_RESMGR_TPM_PORT + }; + + TSS2_ABI_VERSION abi_version = { TSSWG_INTEROP, + TSS_SAPI_FIRST_FAMILY, + TSS_SAPI_FIRST_LEVEL, + TSS_SAPI_FIRST_VERSION + }; + + /* determine size of tcti context */ + rval = InitSocketTcti(NULL, &tcti_context_size, &rm_if_config, 0); + if (rval != TSS2_RC_SUCCESS) + { + DBG1(DBG_PTS, "%s could not get tcti_context size: 0x%06x", + LABEL, rval); + return FALSE; + } + + /* allocate memory for tcti context */ + this->tcti_context = (TSS2_TCTI_CONTEXT*)malloc(tcti_context_size); + + /* initialize tcti context */ + rval = InitSocketTcti(this->tcti_context, &tcti_context_size, + &rm_if_config, 0); + if (rval != TSS2_RC_SUCCESS) + { + DBG1(DBG_PTS, "%s could not get tcti_context: 0x%06x", + LABEL, rval); + return FALSE; + } + + /* determine size of sys context */ + sys_context_size = Tss2_Sys_GetContextSize(0); + + /* allocate memory for sys context */ + this->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) + { + TeardownSocketTcti(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, } }; + + TPMS_AUTH_RESPONSE session_data; + TSS2_SYS_RSP_AUTHS sessions_data; + TPMS_AUTH_RESPONSE *session_data_array[1]; + + session_data_array[0] = &session_data; + sessions_data.rspAuths = &session_data_array[0]; + sessions_data.rspAuthsCount = 1; + + /* always send simulator platform command, ignored by true RM */ + PlatformCommand(this->tcti_context ,MS_SIM_POWER_ON ); + PlatformCommand(this->tcti_context, MS_SIM_NV_ON ); + + /* 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, &sessions_data); + + PlatformCommand(this->tcti_context, MS_SIM_POWER_OFF); + + if (rval != TPM_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, } }; + TPM_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.t.publicArea.type) + { + case TPM_ALG_RSA: + { + TPM2B_PUBLIC_KEY_RSA *rsa; + TPMT_RSA_SCHEME *scheme; + chunk_t aik_exponent, aik_modulus; + + scheme = &public.t.publicArea.parameters.rsaDetail.scheme; + sig_alg = scheme->scheme; + digest_alg = scheme->details.anySig.hashAlg; + + rsa = &public.t.publicArea.unique.rsa; + aik_modulus = chunk_create(rsa->t.buffer, rsa->t.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); + } + break; + } + case TPM_ALG_ECC: + { + TPMS_ECC_POINT *ecc; + TPMT_ECC_SCHEME *scheme; + chunk_t ecc_point; + uint8_t *pos; + + scheme = &public.t.publicArea.parameters.eccDetail.scheme; + sig_alg = scheme->scheme; + digest_alg = scheme->details.anySig.hashAlg; + + ecc = &public.t.publicArea.unique.ecc; + + /* allocate space for bit string */ + pos = asn1_build_object(&ecc_point, ASN1_BIT_STRING, + 2 + ecc->x.t.size + ecc->y.t.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.t.buffer, ecc->x.t.size); + pos += ecc->x.t.size; + /* copy y coordinate of ECC point */ + memcpy(pos, ecc->y.t.buffer, ecc->y.t.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.t.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) +{ + TPM_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 != TPM_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].t.buffer; + pcr_value_len = (size_t) pcr_values.digests[0].t.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) +{ + /* TODO */ + return FALSE; +} + +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; + 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]; + + 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.hmac.t.size = 0; + session_data_cmd.nonce.t.size = 0; + + *( (uint8_t *)((void *)&session_data_cmd.sessionAttributes ) ) = 0; + + qualifying_data.t.size = data.len; + memcpy(qualifying_data.t.buffer, data.ptr, data.len); + + scheme.scheme = TPM_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, &sessions_data_cmd, + &qualifying_data, &scheme, &pcr_selection, "ed, + &sig, &sessions_data_rsp); + if (rval != TPM_RC_SUCCESS) + { + DBG1(DBG_PTS,"%s Tss2_Sys_Quote failed: 0x%06x", LABEL, rval); + return FALSE; + } + quoted_chunk = chunk_create(quoted.t.attestationData, quoted.t.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 TPM_ALG_RSASSA: + case TPM_ALG_RSAPSS: + *quote_sig = chunk_clone( + chunk_create( + sig.signature.rsassa.sig.t.buffer, + sig.signature.rsassa.sig.t.size)); + hash_alg = sig.signature.rsassa.hash; + break; + case TPM_ALG_ECDSA: + case TPM_ALG_ECDAA: + case TPM_ALG_SM2: + case TPM_ALG_ECSCHNORR: + *quote_sig = chunk_cat("cc", + chunk_create( + sig.signature.ecdsa.signatureR.t.buffer, + sig.signature.ecdsa.signatureR.t.size), + chunk_create( + sig.signature.ecdsa.signatureS.t.buffer, + sig.signature.ecdsa.signatureS.t.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, 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, + .destroy = _destroy, + }, + ); + + available = initialize_context(this); + DBG1(DBG_PTS, "TPM 2.0 via TSS2 %savailable", available ? "" : "not "); + + if (!available) + { + destroy(this); + return NULL; + } + return &this->public; +} + +#else /* TSS_TSS2 */ + +tpm_tss_t *tpm_tss_tss2_create() +{ + return NULL; +} + +#endif /* TSS_TSS2 */ + + diff --git a/src/libtpmtss/tpm_tss_tss2.h b/src/libtpmtss/tpm_tss_tss2.h new file mode 100644 index 000000000..f3a11e5fd --- /dev/null +++ b/src/libtpmtss/tpm_tss_tss2.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2016 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 <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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_tss_tss2 tpm_tss_tss2 + * @{ @ingroup libtpmtss + */ + +#ifndef TPM_TSS_TSS2_H_ +#define TPM_TSS_TSS2_H_ + +#include "tpm_tss.h" + +/** + * Create a tpm_tss_tss2 instance. + */ +tpm_tss_t *tpm_tss_tss2_create(); + +#endif /** TPM_TSS_TSS2_H_ @}*/ diff --git a/src/libtpmtss/tpm_tss_tss2_names.c b/src/libtpmtss/tpm_tss_tss2_names.c new file mode 100644 index 000000000..dca1ff121 --- /dev/null +++ b/src/libtpmtss/tpm_tss_tss2_names.c @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2016 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 <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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_names.h" + +#ifdef TSS_TSS2 + +#include <tss2/tpm20.h> + +#ifndef TPM_ALG_ECMQV +#define TPM_ALG_ECMQV (TPM_ALG_ID)0x001D +#endif + +#ifndef TPM_ALG_CAMELLIA +#define TPM_ALG_CAMELLIA (TPM_ALG_ID)0x0026 +#endif + +/** + * TPM 2.0 algorithm ID names + */ +ENUM_BEGIN(tpm_alg_id_names, TPM_ALG_ERROR, TPM_ALG_RSA, + "ERROR", + "RSA" +); +ENUM_NEXT(tpm_alg_id_names, TPM_ALG_SHA1, TPM_ALG_KEYEDHASH, TPM_ALG_RSA, + "SHA1", + "HMAC", + "AES", + "MGF1", + "KEYEDHASH" +); +ENUM_NEXT(tpm_alg_id_names, TPM_ALG_XOR, TPM_ALG_SHA512, TPM_ALG_KEYEDHASH, + "XOR", + "SHA256", + "SHA384", + "SHA512" +); +ENUM_NEXT(tpm_alg_id_names, TPM_ALG_NULL, TPM_ALG_NULL, TPM_ALG_SHA512, + "NULL" +); +ENUM_NEXT(tpm_alg_id_names, TPM_ALG_SM3_256, TPM_ALG_ECMQV, TPM_ALG_NULL, + "SM3_256", + "SM4", + "RSASSA", + "RSAES", + "RSAPSS", + "OAEP", + "ECDSA", + "ECDH", + "SM2", + "ECSCHNORR", + "ECMQV" +); +ENUM_NEXT(tpm_alg_id_names, TPM_ALG_KDF1_SP800_56A, TPM_ALG_ECC, TPM_ALG_ECMQV, + "KDF1_SP800_56A", + "KDF2", + "KDF1_SP800_108", + "ECC" +); +ENUM_NEXT(tpm_alg_id_names, TPM_ALG_SYMCIPHER, TPM_ALG_CAMELLIA, TPM_ALG_ECC, + "SYMCIPHER", + "CAMELLIA" +); +ENUM_NEXT(tpm_alg_id_names, TPM_ALG_CTR, TPM_ALG_ECB, TPM_ALG_CAMELLIA, + "CTR", + "OFB", + "CBC", + "CFB", + "ECB" +); +ENUM_END(tpm_alg_id_names, TPM_ALG_ECB); + +/** + * TPM 2.0 ECC curve names + */ +ENUM_BEGIN(tpm_ecc_curve_names, TPM_ECC_NONE, TPM_ECC_NIST_P521, + "NONE", + "NIST_P192", + "NIST_P224", + "NIST_P256", + "NIST_P384", + "NIST_P521" +); +ENUM_NEXT(tpm_ecc_curve_names, TPM_ECC_BN_P256, TPM_ECC_BN_P638, TPM_ECC_NIST_P521, + "BN_P256", + "BN_P638" +); +ENUM_NEXT(tpm_ecc_curve_names, TPM_ECC_SM2_P256, TPM_ECC_SM2_P256, TPM_ECC_BN_P638, + "SM2_P256" +); +ENUM_END(tpm_ecc_curve_names, TPM_ECC_SM2_P256); + +#else /* TSS_TSS2 */ + +/** + * TPM 2.0 algorithm ID names + */ +ENUM(tpm_alg_id_names, 0, 0, + "ERROR" +); + +/** + * TPM 2.0 ECC curve names + */ +ENUM(tpm_ecc_curve_names, 0, 0, + "NONE" +); + +#endif /* TSS_TSS2 */ + + diff --git a/src/libtpmtss/tpm_tss_tss2_names.h b/src/libtpmtss/tpm_tss_tss2_names.h new file mode 100644 index 000000000..290c51702 --- /dev/null +++ b/src/libtpmtss/tpm_tss_tss2_names.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2016 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 <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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_tss_tss_names tpm_tss_tss_names + * @{ @ingroup libtpmtss + */ + +#ifndef TPM_TSS_TSS2_NAMES_H_ +#define TPM_TSS_TSS2_NAMES_H_ + +#include <library.h> + +extern enum_name_t *tpm_alg_id_names; + +extern enum_name_t *tpm_ecc_curve_names; + +#endif /** TPM_TSS_TSS2_NAMES_H_ @}*/ |