aikpub2: Convert TSS 2.0 AIK public key blob into PKCS#1 format
authorAndreas Steffen <andreas.steffen@strongswan.org>
Mon, 16 May 2016 08:53:44 +0000 (10:53 +0200)
committerAndreas Steffen <andreas.steffen@strongswan.org>
Wed, 22 Jun 2016 13:33:43 +0000 (15:33 +0200)
conf/options/aikgen2.opt [new file with mode: 0644]
configure.ac
src/Makefile.am
src/aikgen/aikgen.c
src/aikpub2/.gitignore [new file with mode: 0644]
src/aikpub2/Makefile.am [new file with mode: 0644]
src/aikpub2/aikpub2.c [new file with mode: 0644]

diff --git a/conf/options/aikgen2.opt b/conf/options/aikgen2.opt
new file mode 100644 (file)
index 0000000..2acab4e
--- /dev/null
@@ -0,0 +1,2 @@
+aikgen2.load =
+       Plugins to load in aikgen2 tool.
index 3c6132e..f067fa7 100644 (file)
@@ -1,8 +1,8 @@
 #
 # Copyright (C) 2007-2015 Tobias Brunner
-# Copyright (C) 2006-2015 Andreas Steffen
+# Copyright (C) 2006-2016 Andreas Steffen
 # Copyright (C) 2006-2014 Martin Willi
-# Hochschule fuer Technik Rapperswil
+# 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
@@ -269,7 +269,8 @@ ARG_ENABL_SET([systime-fix],    [enable plugin to handle cert lifetimes with inv
 ARG_ENABL_SET([test-vectors],   [enable plugin providing crypto test vectors.])
 ARG_DISBL_SET([updown],         [disable updown firewall script plugin.])
 # programs/components
-ARG_ENABL_SET([aikgen],         [enable AIK generator.])
+ARG_ENABL_SET([aikgen],         [enable AIK generator for TPM 1.2.])
+ARG_ENABL_SET([aikpub2],        [enable AIK extractor for TPM 2.0.])
 ARG_DISBL_SET([charon],         [disable the IKEv1/IKEv2 keying daemon charon.])
 ARG_ENABL_SET([cmd],            [enable the command line IKE client charon-cmd.])
 ARG_ENABL_SET([conftest],       [enforce Suite B conformance test framework.])
@@ -966,6 +967,11 @@ if test x$tss = xtrousers; then
        AC_DEFINE([TSS_TROUSERS], [], [use TrouSerS library libtspi as TSS implementation])
 fi
 
+if test x$tss = xtss2; then
+       AC_CHECK_LIB([tss2],[main],[LIBS="$LIBS"],[AC_MSG_ERROR([TTS 2.0 library libtss2 not found])],[])
+       AC_CHECK_HEADER([tss2/tpm20.h],,[AC_MSG_ERROR([TSS 2.0 header tss2/tpm20.h not found!])])
+       AC_DEFINE([TSS_TSS2], [], [use TSS 2.0 library libtss2 as TSS implementation])
+fi
 if test x$imv_swid = xtrue; then
        PKG_CHECK_MODULES(json, [json-c], [],
                [PKG_CHECK_MODULES(json, [json])])
@@ -1611,7 +1617,7 @@ AM_CONDITIONAL(USE_PKI, test x$pki = xtrue)
 AM_CONDITIONAL(USE_SCEPCLIENT, test x$scepclient = xtrue)
 AM_CONDITIONAL(USE_SCRIPTS, test x$scripts = xtrue)
 AM_CONDITIONAL(USE_CONFTEST, test x$conftest = xtrue)
-AM_CONDITIONAL(USE_LIBSTRONGSWAN, test x$charon = xtrue -o x$pki = xtrue -o x$scepclient = xtrue -o x$conftest = xtrue -o x$fast = xtrue -o x$imcv = xtrue -o x$nm = xtrue -o x$tkm = xtrue -o x$cmd = xtrue -o x$tls = xtrue -o x$tnc_tnccs = xtrue -o x$aikgen = xtrue -o x$svc = xtrue -o x$systemd = xtrue)
+AM_CONDITIONAL(USE_LIBSTRONGSWAN, test x$charon = xtrue -o x$pki = xtrue -o x$scepclient = xtrue -o x$conftest = xtrue -o x$fast = xtrue -o x$imcv = xtrue -o x$nm = xtrue -o x$tkm = xtrue -o x$cmd = xtrue -o x$tls = xtrue -o x$tnc_tnccs = xtrue -o x$aikgen = xtrue -o x$aikpub2 = xtrue -o x$svc = xtrue -o x$systemd = xtrue)
 AM_CONDITIONAL(USE_LIBCHARON, test x$charon = xtrue -o x$conftest = xtrue -o x$nm = xtrue -o x$tkm = xtrue -o x$cmd = xtrue -o x$svc = xtrue -o x$systemd = xtrue)
 AM_CONDITIONAL(USE_LIBIPSEC, test x$libipsec = xtrue)
 AM_CONDITIONAL(USE_LIBTNCIF, test x$tnc_tnccs = xtrue -o x$imcv = xtrue)
@@ -1627,6 +1633,7 @@ AM_CONDITIONAL(USE_TLS, test x$tls = xtrue)
 AM_CONDITIONAL(USE_RADIUS, test x$radius = xtrue)
 AM_CONDITIONAL(USE_IMCV, test x$imcv = xtrue)
 AM_CONDITIONAL(USE_TROUSERS, test x$tss = xtrousers -o x$aikgen = xtrue)
+AM_CONDITIONAL(USE_TSS2, test x$tss = xtss2 -o x$aikpub2 = xtrue)
 AM_CONDITIONAL(MONOLITHIC, test x$monolithic = xtrue)
 AM_CONDITIONAL(USE_SILENT_RULES, test x$enable_silent_rules = xyes)
 AM_CONDITIONAL(COVERAGE, test x$coverage = xtrue)
@@ -1634,6 +1641,7 @@ AM_CONDITIONAL(USE_DBGHELP, test x$dbghelp_backtraces = xtrue)
 AM_CONDITIONAL(USE_TKM, test x$tkm = xtrue)
 AM_CONDITIONAL(USE_CMD, test x$cmd = xtrue)
 AM_CONDITIONAL(USE_AIKGEN, test x$aikgen = xtrue)
+AM_CONDITIONAL(USE_AIKPUB2, test x$aikpub2 = xtrue)
 AM_CONDITIONAL(USE_SWANCTL, test x$swanctl = xtrue)
 AM_CONDITIONAL(USE_SVC, test x$svc = xtrue)
 AM_CONDITIONAL(USE_SYSTEMD, test x$systemd = xtrue)
@@ -1673,6 +1681,7 @@ fi
 strongswan_options=
 
 AM_COND_IF([USE_AIKGEN], [strongswan_options=${strongswan_options}" aikgen"])
+AM_COND_IF([USE_AIKPUB2], [strongswan_options=${strongswan_options}" aikpub2"])
 AM_COND_IF([USE_ATTR_SQL], [strongswan_options=${strongswan_options}" pool"])
 AM_COND_IF([USE_CHARON], [strongswan_options=${strongswan_options}" charon charon-logging"])
 AM_COND_IF([USE_FILE_CONFIG], [strongswan_options=${strongswan_options}" starter"])
@@ -1873,6 +1882,7 @@ AC_CONFIG_FILES([
        src/_copyright/Makefile
        src/scepclient/Makefile
        src/aikgen/Makefile
+       src/aikpub2/Makefile
        src/pki/Makefile
        src/pki/man/Makefile
        src/pool/Makefile
index a9df10c..4e4dfca 100644 (file)
@@ -131,3 +131,7 @@ endif
 if USE_AIKGEN
   SUBDIRS += aikgen
 endif
+
+if USE_AIKPUB2
+  SUBDIRS += aikpub2
+endif
index 192636a..6d04fc1 100644 (file)
@@ -44,7 +44,7 @@
 /* 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 */
diff --git a/src/aikpub2/.gitignore b/src/aikpub2/.gitignore
new file mode 100644 (file)
index 0000000..42b5e26
--- /dev/null
@@ -0,0 +1 @@
+aikpub2
diff --git a/src/aikpub2/Makefile.am b/src/aikpub2/Makefile.am
new file mode 100644 (file)
index 0000000..d08a2f3
--- /dev/null
@@ -0,0 +1,15 @@
+bin_PROGRAMS = aikpub2
+
+aikpub2_SOURCES = aikpub2.c
+
+aikpub2_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la
+aikpub2.o :    $(top_builddir)/config.status
+
+if USE_TSS2
+  aikpub2_LDADD += -ltss2
+endif
+
+AM_CPPFLAGS = \
+       -I$(top_srcdir)/src/libstrongswan \
+       -DIPSEC_CONFDIR=\"${sysconfdir}\" \
+       -DPLUGINS=\""${aikgen_plugins}\""
diff --git a/src/aikpub2/aikpub2.c b/src/aikpub2/aikpub2.c
new file mode 100644 (file)
index 0000000..9b145be
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+ * 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 <library.h>
+#include <utils/debug.h>
+#include <utils/optionsfrom.h>
+#include <asn1/asn1.h>
+#include <asn1/oid.h>
+
+#include <tss2/tpm20.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 private 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;
+
+/* global variables */
+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, "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  [--in <filename>] [--out <filename>]\n"
+               "                [--force] [--quiet] [--debug <level>]\n"
+               "       aikpub2 --help\n"
+               "\n"
+               "Options:\n"
+               " --in (-i)         TSS 2.0 AIK public key blob\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 generates 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 *aikblob_filename   = NULL;
+       char *aikpubkey_filename = DEFAULT_FILENAME_AIKPUBKEY;
+       bool force = FALSE;
+       chunk_t *aikblob;
+       hasher_t *hasher;
+
+       /* TSS 2.0 variables */
+       TPM2B_PUBLIC public;
+
+       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, '+' },
+                       { "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, "ho:c:b:p:fqd:", 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 'i':       /* --in <filename> */
+                               aikblob_filename = optarg;
+                               continue;
+
+                       case 'o':       /* --out <filename> */
+                               aikpubkey_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");
+       }
+
+       /* read TSS 2.0 AIK public key blob */
+       if (!aikblob_filename)
+       {
+               usage("--aikblob is required");
+       }
+       aikblob = chunk_map(aikblob_filename, FALSE);
+       if (!aikblob)
+       {
+               exit_aikpub2("could not read TSS 2.0 public key file '%s'",
+                                         aikblob_filename);
+       }
+       DBG3(DBG_LIB, "aikblob: %B", aikblob);
+
+       if (aikblob->len != sizeof(TPM2B_PUBLIC))
+       {
+               chunk_unmap(aikblob);
+               exit_aikpub2("size of aikblob is not %d bytes", sizeof(TPM2B_PUBLIC));
+       }
+       public = *(TPM2B_PUBLIC*)aikblob->ptr;
+       chunk_unmap(aikblob);
+
+       switch (public.t.publicArea.type)
+       {
+               case TPM_ALG_RSA:
+               {
+                       TPM2B_PUBLIC_KEY_RSA *rsa;
+                       chunk_t aik_exponent, aik_modulus;
+
+                       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))
+                       {
+                               exit_aikpub2("subjectPublicKeyInfo encoding of AIK key failed");
+                       }
+                       break;
+               }
+               case TPM_ALG_ECC:
+               {
+                       TPMS_ECC_POINT *ecc;
+                       chunk_t ecc_point;
+                       uint8_t *pos;
+
+                       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:
+                       exit_aikpub2("unsupported key type");
+       }
+
+       /* store AIK subjectPublicKeyInfo to file */
+       if (!chunk_write(aik_pubkey, aikpubkey_filename, 0022, force))
+       {
+               exit_aikpub2("could not write AIK public key file '%s': %s",
+                                         aikpubkey_filename, strerror(errno));
+       }
+       DBG1(DBG_LIB, "AIK public key written to '%s' (%u bytes)",
+                                  aikpubkey_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 */
+}