From 87d356dc474045ca69e82f8b2c46aedfc0481f86 Mon Sep 17 00:00:00 2001 From: Andreas Steffen Date: Mon, 16 May 2016 10:53:44 +0200 Subject: [PATCH] aikpub2: Convert TSS 2.0 AIK public key blob into PKCS#1 format --- conf/options/aikgen2.opt | 2 + configure.ac | 18 +- src/Makefile.am | 4 + src/aikgen/aikgen.c | 2 +- src/aikpub2/.gitignore | 1 + src/aikpub2/Makefile.am | 15 ++ src/aikpub2/aikpub2.c | 363 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 400 insertions(+), 5 deletions(-) create mode 100644 conf/options/aikgen2.opt create mode 100644 src/aikpub2/.gitignore create mode 100644 src/aikpub2/Makefile.am create mode 100644 src/aikpub2/aikpub2.c diff --git a/conf/options/aikgen2.opt b/conf/options/aikgen2.opt new file mode 100644 index 000000000..2acab4ebf --- /dev/null +++ b/conf/options/aikgen2.opt @@ -0,0 +1,2 @@ +aikgen2.load = + Plugins to load in aikgen2 tool. diff --git a/configure.ac b/configure.ac index 3c6132e5f..f067fa7eb 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/src/Makefile.am b/src/Makefile.am index a9df10cc6..4e4dfca56 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -131,3 +131,7 @@ endif if USE_AIKGEN SUBDIRS += aikgen endif + +if USE_AIKPUB2 + SUBDIRS += aikpub2 +endif diff --git a/src/aikgen/aikgen.c b/src/aikgen/aikgen.c index 192636afc..6d04fc1ca 100644 --- a/src/aikgen/aikgen.c +++ b/src/aikgen/aikgen.c @@ -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 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..d08a2f387 --- /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 +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 index 000000000..9b145be1b --- /dev/null +++ b/src/aikpub2/aikpub2.c @@ -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 . + * + * 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 +#include + +/* 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 ] [--out ]\n" + " [--force] [--quiet] [--debug ]\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 */ + if (!options->from(options, optarg, &argc, &argv, optind)) + { + exit_aikpub2("optionsfrom failed"); + } + continue; + + case 'i': /* --in */ + aikblob_filename = optarg; + continue; + + case 'o': /* --out */ + aikpubkey_filename = optarg; + continue; + + case 'f': /* --force */ + force = TRUE; + 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; + } + + 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 */ +}