diff options
author | Andreas Steffen <andreas.steffen@strongswan.org> | 2016-05-16 10:53:44 +0200 |
---|---|---|
committer | Andreas Steffen <andreas.steffen@strongswan.org> | 2016-06-22 15:33:43 +0200 |
commit | 87d356dc474045ca69e82f8b2c46aedfc0481f86 (patch) | |
tree | 5fd073427bc9e0c96225c6081c38840a074ed579 /src | |
parent | 5a09734c2cabe419419496a36445be65f8791e10 (diff) | |
download | strongswan-87d356dc474045ca69e82f8b2c46aedfc0481f86.tar.bz2 strongswan-87d356dc474045ca69e82f8b2c46aedfc0481f86.tar.xz |
aikpub2: Convert TSS 2.0 AIK public key blob into PKCS#1 format
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/aikgen/aikgen.c | 2 | ||||
-rw-r--r-- | src/aikpub2/.gitignore | 1 | ||||
-rw-r--r-- | src/aikpub2/Makefile.am | 15 | ||||
-rw-r--r-- | src/aikpub2/aikpub2.c | 363 |
5 files changed, 384 insertions, 1 deletions
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 <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 */ +} |