From: Andreas Steffen Date: Thu, 17 May 2018 16:22:56 +0000 (+0200) Subject: tpm_seal: Implemented TPM 2.0 file sealing X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=refs%2Fheads%2Ftpm-seal;p=thirdparty%2Fstrongswan.git tpm_seal: Implemented TPM 2.0 file sealing --- diff --git a/configure.ac b/configure.ac index 88abe7f2dd..c8cf2cb23d 100644 --- a/configure.ac +++ b/configure.ac @@ -1993,6 +1993,7 @@ AC_CONFIG_FILES([ src/scepclient/Makefile src/aikgen/Makefile src/tpm_extendpcr/Makefile + src/tpm_seal/Makefile src/pki/Makefile src/pki/man/Makefile src/pool/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index e2747c300a..3172f25833 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -146,4 +146,5 @@ endif if USE_TPM SUBDIRS += tpm_extendpcr + SUBDIRS += tpm_seal endif diff --git a/src/libtpmtss/tpm_tss.h b/src/libtpmtss/tpm_tss.h index bcb7ab949f..cbc9688ec5 100644 --- a/src/libtpmtss/tpm_tss.h +++ b/src/libtpmtss/tpm_tss.h @@ -102,6 +102,15 @@ struct tpm_tss_t { bool (*extend_pcr)(tpm_tss_t *this, uint32_t pcr_num, chunk_t *pcr_value, chunk_t data, hash_algorithm_t alg); + /** + * Seal a plaintext file to a given set of PCR values (TPM 2.0 only) + * + * @param alg hash algorithm, selects PCR bank + * @param pcr_sel PCR selection + * @return TRUE if sealing succeeded + */ + bool (*seal)(tpm_tss_t *this, hash_algorithm_t alg, uint32_t pcr_sel); + /** * Do a quote signature over a selection of PCR registers * diff --git a/src/libtpmtss/tpm_tss_trousers.c b/src/libtpmtss/tpm_tss_trousers.c index 6ed57af9d0..044b9dd04f 100644 --- a/src/libtpmtss/tpm_tss_trousers.c +++ b/src/libtpmtss/tpm_tss_trousers.c @@ -429,6 +429,12 @@ METHOD(tpm_tss_t, extend_pcr, bool, return TRUE; } +METHOD(tpm_tss_t, seal, bool, + private_tpm_tss_trousers_t *this, hash_algorithm_t alg, uint32_t pcr_sel) +{ + return FALSE; +} + 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, @@ -643,6 +649,7 @@ tpm_tss_t *tpm_tss_trousers_create() .get_public = _get_public, .read_pcr = _read_pcr, .extend_pcr = _extend_pcr, + .seal = _seal, .quote = _quote, .sign = _sign, .get_random = _get_random, diff --git a/src/libtpmtss/tpm_tss_tss2.c b/src/libtpmtss/tpm_tss_tss2.c index 90a16c103e..9858f617ed 100644 --- a/src/libtpmtss/tpm_tss_tss2.c +++ b/src/libtpmtss/tpm_tss_tss2.c @@ -526,11 +526,12 @@ METHOD(tpm_tss_t, get_public, chunk_t, /** * 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) +static int 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; + int count = 0; /* check if hash algorithm is supported by TPM */ alg_id = hash_alg_to_tpm_alg_id(alg); @@ -538,7 +539,7 @@ static bool init_pcr_selection(private_tpm_tss_tss2_t *this, uint32_t pcrs, { DBG1(DBG_PTS, "%s %N hash algorithm not supported by TPM", LABEL, hash_algorithm_short_names, alg); - return FALSE; + return 0; } /* initialize the PCR Selection structure,*/ @@ -555,9 +556,10 @@ static bool init_pcr_selection(private_tpm_tss_tss2_t *this, uint32_t pcrs, if (pcrs & (1 << pcr)) { pcr_sel->pcrSelections[0].pcrSelect[pcr / 8] |= ( 1 << (pcr % 8) ); + count++; } } - return TRUE; + return count; } METHOD(tpm_tss_t, read_pcr, bool, @@ -591,7 +593,7 @@ METHOD(tpm_tss_t, read_pcr, bool, &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", + DBG1(DBG_PTS, "%s Tss2_Sys_PCR_Read failed: 0x%06x", LABEL, rval); return FALSE; } @@ -696,6 +698,135 @@ METHOD(tpm_tss_t, extend_pcr, bool, return read_pcr(this, pcr_num, pcr_value, alg); } +static void update_pcr_selection(TPML_PCR_SELECTION *in, + TPML_PCR_SELECTION *out) +{ + int i; + + for (i = 0; i < out->pcrSelections[0].sizeofSelect; i++) + { + in->pcrSelections[0].pcrSelect[i] &= ~out->pcrSelections[0].pcrSelect[i]; + } +} + +METHOD(tpm_tss_t, seal, bool, + private_tpm_tss_tss2_t *this,hash_algorithm_t alg, uint32_t pcr_sel) +{ + TPMI_SH_AUTH_SESSION session_handle; + TPML_PCR_SELECTION pcr_selection, pcr_sel_in, pcr_sel_out; + TPML_DIGEST pcr_values; + TPM2B_DIGEST pcr_digest = { .t = { .size = HASH_SIZE_SHA256 } }; + TPM2B_DIGEST policy_digest = { .t = { .size = HASH_SIZE_SHA256 } }; + TPMT_SYM_DEF symmetric = { .algorithm = TPM_ALG_NULL }; + TPM2B_ENCRYPTED_SECRET encrypted_salt = { .t = { .size = 0 } }; + TPM2B_NONCE nonce_caller = { .t = { .size = HASH_SIZE_SHA256 } }; + TPM2B_NONCE nonce_newer = { .t = { .size = HASH_SIZE_SHA256 } }; + + hasher_t *hasher; + hash_algorithm_t auth_alg = HASH_SHA256; + chunk_t pcr_value, digest; + uint32_t pcr = 0, pcr_update_counter, rval; + int count, i; + + count = init_pcr_selection(this, pcr_sel, alg, &pcr_selection); + pcr_sel_in = pcr_selection; + + /* create a hasher for the PCR digest */ + hasher = lib->crypto->create_hasher(lib->crypto, auth_alg); + if (!hasher) + { + DBG1(DBG_PTS, "failed to create hasher"); + return FALSE; + } + + /* read the PCR values */ + while (count) + { + pcr_values.count = 0; + + rval = Tss2_Sys_PCR_Read(this->sys_context, 0, &pcr_sel_in, + &pcr_update_counter, &pcr_sel_out, &pcr_values, 0); + if (rval != TPM_RC_SUCCESS) + { + DBG1(DBG_PTS, "%s Tss2_Sys_PCR_Read failed: 0x%06x", + LABEL, rval); + hasher->destroy(hasher); + return FALSE; + } + + for (i = 0; i < pcr_values.count; i++) + { + while (!(pcr_sel & (1 << pcr))) + { + pcr++; + } + pcr_value = chunk_create(pcr_values.digests[i].t.buffer, + pcr_values.digests[i].t.size); + DBG1(DBG_PTS, "PCR %02u %#B", pcr, &pcr_value); + pcr++; + + if (!hasher->get_hash(hasher, pcr_value, NULL)) + { + hasher->destroy(hasher); + return FALSE; + } + count--; + } + update_pcr_selection(&pcr_sel_in, &pcr_sel_out); + } + + if (!hasher->get_hash(hasher, chunk_empty, pcr_digest.t.buffer)) + { + hasher->destroy(hasher); + return FALSE; + } + hasher->destroy(hasher); + + digest = chunk_create(pcr_digest.t.buffer, pcr_digest.t.size); + DBG1(DBG_PTS, "PCR digest %#B", &digest); + + rval = Tss2_Sys_StartAuthSession(this->sys_context, TPM_RH_NULL, TPM_RH_NULL, + NULL, &nonce_caller, &encrypted_salt, TPM_SE_TRIAL, &symmetric, + TPM_ALG_SHA256, &session_handle, &nonce_newer, NULL); + if (rval != TPM_RC_SUCCESS) + { + DBG1(DBG_PTS, "%s Tss2_Sys_StartAuthSession failed: 0x%06x", + LABEL, rval); + return FALSE; + } + + rval = Tss2_Sys_PolicyPCR(this->sys_context, session_handle, NULL, + &pcr_digest, &pcr_selection, NULL); + if (rval != TPM_RC_SUCCESS) + { + DBG1(DBG_PTS, "%s Tss2_Sys_PolicyPCR failed: 0x%06x", + LABEL, rval); + return FALSE; + } + + rval = Tss2_Sys_PolicyGetDigest(this->sys_context, session_handle, NULL, + &policy_digest, NULL); + if (rval != TPM_RC_SUCCESS) + { + DBG1(DBG_PTS, "%s Tss2_Sys_PolicyGetDigest failed: 0x%06x", + LABEL, rval); + return FALSE; + } + + rval = Tss2_Sys_FlushContext(this->sys_context, session_handle); + if (rval != TPM_RC_SUCCESS) + { + DBG1(DBG_PTS, "%s Tss2_Sys_FlushContext failed: 0x%06x", + LABEL, rval); + return FALSE; + } + + digest = chunk_create(policy_digest.t.buffer, policy_digest.t.size); + DBG1(DBG_PTS, "policy digest %#B", &digest); + + return TRUE; +} + 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, @@ -1137,6 +1268,7 @@ tpm_tss_t *tpm_tss_tss2_create() .get_public = _get_public, .read_pcr = _read_pcr, .extend_pcr = _extend_pcr, + .seal = _seal, .quote = _quote, .sign = _sign, .get_random = _get_random, diff --git a/src/tpm_seal/.gitignore b/src/tpm_seal/.gitignore new file mode 100644 index 0000000000..342553ca94 --- /dev/null +++ b/src/tpm_seal/.gitignore @@ -0,0 +1 @@ +tpm_seal diff --git a/src/tpm_seal/Makefile.am b/src/tpm_seal/Makefile.am new file mode 100644 index 0000000000..ad53d4b647 --- /dev/null +++ b/src/tpm_seal/Makefile.am @@ -0,0 +1,14 @@ +bin_PROGRAMS = tpm_seal + +tpm_seal_SOURCES = tpm_seal.c + +tpm_seal_LDADD = \ + $(top_builddir)/src/libstrongswan/libstrongswan.la \ + $(top_builddir)/src/libtpmtss/libtpmtss.la + +tpm_seal.o : $(top_builddir)/config.status + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libtpmtss \ + -DIPSEC_CONFDIR=\"${sysconfdir}\" diff --git a/src/tpm_seal/tpm_seal.c b/src/tpm_seal/tpm_seal.c new file mode 100644 index 0000000000..0b97f42b60 --- /dev/null +++ b/src/tpm_seal/tpm_seal.c @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2018 Andreas Steffen + * HSR Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include + + +/* logging */ +static bool log_to_stderr = TRUE; +static bool log_to_syslog = TRUE; +static level_t default_loglevel = 1; + +/* global variables */ +tpm_tss_t *tpm; + +/** + * logging function for tpm_seal + */ +static void tpm_seal_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 = tpm_seal_dbg; + + if (log_to_stderr) + { + setbuf(stderr, NULL); + } + if (log_to_syslog) + { + openlog(program, LOG_CONS | LOG_NDELAY | LOG_PID, LOG_AUTHPRIV); + } +} + +/** + * @brief exit tpm_seal + * + * @param status 0 = OK, -1 = general discomfort + */ +static void exit_tpm_seal(err_t message, ...) +{ + int status = 0; + + DESTROY_IF(tpm); + + /* 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, "tpm_seal 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: tpm_seal [--alg ] --pcrs --in \n" + " [--out ] [--quiet] [--debug ]\n" + " tpm_seal --help\n" + "\n" + "Options:\n" + " --alg (-a) hash algorithm (sha1|sha256)\n" + " --pcrs (-p) list of platform configuration registers (0..23)\n" + " --in (-i) binary input file with digest to be extended\n" + " --out (-o) binary output file with updated PCR value\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_tpm_seal(message); +} + +static uint32_t parse_pcrs(char *pcrs) +{ + chunk_t pcr_list, pcr_range, pcr_start; + uint32_t pcr, pcr_stop, pcr_sel; + + pcr_list = chunk_from_str(pcrs); + pcr_sel = 0x00000000; + + while (pcr_list.len > 0) + { + if (!extract_token(&pcr_range, ',', &pcr_list)) + { + pcr_range = pcr_list; + pcr_list = chunk_empty; + } + if (extract_token(&pcr_start, '-', &pcr_range)) + { + pcr = atoi(pcr_start.ptr); + pcr_stop = atoi(pcr_range.ptr); + } + else + { + pcr = atoi(pcr_range.ptr); + pcr_stop = pcr; + } + + while (pcr <= pcr_stop) + { + pcr_sel |= (1 << pcr++); + } + } + return pcr_sel; +} + +/** + * @brief main of tpm_seal which extends digest into a PCR + * + * @param argc number of arguments + * @param argv pointer to the argument values + */ +int main(int argc, char *argv[]) +{ + hash_algorithm_t alg = HASH_SHA1; + char *infile = NULL, *outfile = NULL, *pcrs = ""; + uint32_t pcr_sel; + + atexit(library_deinit); + if (!library_init(NULL, "tpm_seal")) + { + exit(SS_RC_LIBSTRONGSWAN_INTEGRITY); + } + if (lib->integrity && + !lib->integrity->check_file(lib->integrity, "tpm_seal", argv[0])) + { + fprintf(stderr, "integrity check of tpm_seal failed\n"); + exit(SS_RC_DAEMON_INTEGRITY); + } + + for (;;) + { + static const struct option long_opts[] = { + /* name, has_arg, flag, val */ + { "help", no_argument, NULL, 'h' }, + { "alg", required_argument, NULL, 'a' }, + { "pcrs", required_argument, NULL, 'p' }, + { "in", required_argument, NULL, 'i' }, + { "out", required_argument, NULL, 'o' }, + { "quiet", no_argument, NULL, 'q' }, + { "debug", required_argument, NULL, 'l' }, + { 0,0,0,0 } + }; + + /* parse next option */ + int c = getopt_long(argc, argv, "ha:p:i:o:ql:", long_opts, NULL); + + switch (c) + { + case EOF: /* end of flags */ + break; + + case 'h': /* --help */ + usage(NULL); + + case 'a': /* --alg */ + if (!enum_from_name(hash_algorithm_short_names, optarg, &alg)) + { + usage("unsupported hash algorithm"); + } + continue; + case 'p': /* --pcrs */ + pcrs = optarg; + continue; + + case 'i': /* --in */ + infile = optarg; + continue; + + case 'o': /* --out */ + outfile = optarg; + continue; + + case 'q': /* --quiet */ + log_to_stderr = FALSE; + continue; + + case 'l': /* --debug */ + default_loglevel = atoi(optarg); + continue; + + default: + usage("unknown option"); + } + /* break from loop */ + break; + } + + /* parse PCR selection */ + pcr_sel = parse_pcrs(pcrs); + if (pcr_sel == 0) + { + usage("no PCRs selected"); + } + + init_log("tpm_seal"); + + if (!lib->plugins->load(lib->plugins, + lib->settings->get_str(lib->settings, "tpm_seal.load", "tpm sha2"))) + { + exit_tpm_seal("plugin loading failed"); + } + + /* try to find a TPM */ + tpm = tpm_tss_probe(TPM_VERSION_2_0); + if (!tpm) + { + exit_tpm_seal("no TPM found"); + } + + /* sealing operation */ + if (!tpm->seal(tpm, alg, pcr_sel)) + { + exit_tpm_seal("sealing failed"); + } + + exit_tpm_seal(NULL); + return -1; /* should never be reached */ +}