]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
tpm_seal: Implemented TPM 2.0 file sealing tpm-seal
authorAndreas Steffen <andreas.steffen@strongswan.org>
Thu, 17 May 2018 16:22:56 +0000 (18:22 +0200)
committerAndreas Steffen <andreas.steffen@strongswan.org>
Sat, 19 May 2018 13:08:06 +0000 (15:08 +0200)
configure.ac
src/Makefile.am
src/libtpmtss/tpm_tss.h
src/libtpmtss/tpm_tss_trousers.c
src/libtpmtss/tpm_tss_tss2.c
src/tpm_seal/.gitignore [new file with mode: 0644]
src/tpm_seal/Makefile.am [new file with mode: 0644]
src/tpm_seal/tpm_seal.c [new file with mode: 0644]

index 88abe7f2ddb31d0bc2c18b7309923c2023fb0bcd..c8cf2cb23d2bdf2ef9395db6d075cf5f8f6b1bc9 100644 (file)
@@ -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
index e2747c300a07e3998b1c3f9ba897ef79ea403e5b..3172f25833cbc2dd10c48771148bc6e710572ac6 100644 (file)
@@ -146,4 +146,5 @@ endif
 
 if USE_TPM
   SUBDIRS += tpm_extendpcr
+  SUBDIRS += tpm_seal
 endif
index bcb7ab949f5e0a216ab33f26dc40c7442575f12c..cbc9688ec59778e51fc3957014a173c324cb21c4 100644 (file)
@@ -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
         *
index 6ed57af9d06227f93f165f861329504388b6a9e7..044b9dd04fedda102f483ef954a27abc33232e59 100644 (file)
@@ -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,
index 90a16c103ed86d018cce546e0c1986e594f2e165..9858f617ed4a7e7205d45320075c6e7999feee7b 100644 (file)
@@ -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 (file)
index 0000000..342553c
--- /dev/null
@@ -0,0 +1 @@
+tpm_seal
diff --git a/src/tpm_seal/Makefile.am b/src/tpm_seal/Makefile.am
new file mode 100644 (file)
index 0000000..ad53d4b
--- /dev/null
@@ -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 (file)
index 0000000..0b97f42
--- /dev/null
@@ -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 <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 <crypto/hashers/hasher.h>
+#include <utils/lexparser.h>
+#include <utils/debug.h>
+
+#include <syslog.h>
+#include <getopt.h>
+#include <errno.h>
+
+
+/* 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 <name>] --pcrs <list> --in <file>\n"
+               "                [--out <file>] [--quiet] [--debug <level>]\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 <name> */
+                               if (!enum_from_name(hash_algorithm_short_names, optarg, &alg))
+                               {
+                                       usage("unsupported hash algorithm");
+                               }
+                               continue;
+                       case 'p':       /* --pcrs <list> */
+                               pcrs = optarg;
+                               continue;
+
+                       case 'i':       /* --in <file> */
+                               infile = optarg;
+                               continue;
+
+                       case 'o':       /* --out <file> */
+                               outfile = optarg;
+                               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;
+       }
+
+       /* 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 */
+}