#include <asn1/asn1.h>
#include <asn1/oid.h>
+#include <asn1/asn1_parser.h>
#include <bio/bio_reader.h>
#include <tpm20.h>
METHOD(tpm_tss_t, get_random, bool,
private_tpm_tss_tss2_t *this, size_t bytes, uint8_t *buffer)
{
- size_t len, random_len= sizeof(TPM2B_DIGEST)-2;
+ size_t len, random_len = sizeof(TPM2B_DIGEST)-2;
TPM2B_DIGEST random = { { random_len, } };
uint8_t *pos = buffer;
uint32_t rval;
return TRUE;
}
+/**
+ * ASN.1 definition of a PKCS#1 RSA private key
+ */
+static const asn1Object_t privkeyObjects[] = {
+ { 0, "RSAPrivateKey", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */
+ { 1, "version", ASN1_INTEGER, ASN1_BODY }, /* 1 */
+ { 1, "modulus", ASN1_INTEGER, ASN1_BODY }, /* 2 */
+ { 1, "publicExponent", ASN1_INTEGER, ASN1_BODY }, /* 3 */
+ { 1, "privateExponent", ASN1_INTEGER, ASN1_BODY }, /* 4 */
+ { 1, "prime1", ASN1_INTEGER, ASN1_BODY }, /* 5 */
+ { 1, "prime2", ASN1_INTEGER, ASN1_BODY }, /* 6 */
+ { 1, "exponent1", ASN1_INTEGER, ASN1_BODY }, /* 7 */
+ { 1, "exponent2", ASN1_INTEGER, ASN1_BODY }, /* 8 */
+ { 1, "coefficient", ASN1_INTEGER, ASN1_BODY }, /* 9 */
+ { 1, "otherPrimeInfos", ASN1_SEQUENCE, ASN1_OPT |
+ ASN1_LOOP }, /* 10 */
+ { 2, "otherPrimeInfo", ASN1_SEQUENCE, ASN1_NONE }, /* 11 */
+ { 3, "prime", ASN1_INTEGER, ASN1_BODY }, /* 12 */
+ { 3, "exponent", ASN1_INTEGER, ASN1_BODY }, /* 13 */
+ { 3, "coefficient", ASN1_INTEGER, ASN1_BODY }, /* 14 */
+ { 1, "end opt or loop", ASN1_EOC, ASN1_END }, /* 15 */
+ { 0, "exit", ASN1_EOC, ASN1_EXIT }
+};
+
+#define PRIV_KEY_VERSION 1
+#define PRIV_KEY_MODULUS 2
+#define PRIV_KEY_PUB_EXP 3
+#define PRIV_KEY_PRIME1 5
+
+/**
+ * Build a TPM 2.0 RSA key from an ASN.1 encoded private key blob.
+ */
+static bool build_rsa_key(chunk_t blob, TPMT_SENSITIVE *priv, TPMT_PUBLIC *pub)
+{
+ chunk_t n, e, p;
+ asn1_parser_t *parser;
+ chunk_t object;
+ int objectID ;
+ bool success = FALSE;
+
+ TPM2B_PRIVATE_KEY_RSA *priv_rsa = &priv->sensitive.rsa;
+ TPM2B_PUBLIC_KEY_RSA *pub_rsa = &pub->unique.rsa;
+
+ priv->sensitiveType = TPM_ALG_RSA;
+ pub->type = TPM_ALG_RSA;
+ pub->parameters.rsaDetail.symmetric.algorithm = TPM_ALG_NULL;
+ pub->parameters.rsaDetail.scheme.scheme = TPM_ALG_RSASSA;
+ pub->parameters.rsaDetail.scheme.details.anySig.hashAlg = TPM_ALG_SHA256;
+
+ parser = asn1_parser_create(privkeyObjects, blob);
+ parser->set_flags(parser, FALSE, TRUE);
+
+ while (parser->iterate(parser, &objectID, &object))
+ {
+ switch (objectID)
+ {
+ case PRIV_KEY_VERSION:
+ if (object.len > 0 && *object.ptr != 0)
+ {
+ goto end;
+ }
+ break;
+ case PRIV_KEY_MODULUS:
+ n = object;
+ if (n.len > 0 && *n.ptr == 0x00)
+ {
+ n = chunk_skip(n, 1);
+ }
+ if (n.len > MAX_RSA_KEY_BYTES)
+ {
+ goto end;
+ }
+ memcpy(pub_rsa->t.buffer, n.ptr, n.len);
+ pub_rsa->t.size = n.len;
+ pub->parameters.rsaDetail.keyBits = 8 * n.len;
+ break;
+ case PRIV_KEY_PUB_EXP:
+ e = object;
+ /* we only accept the standard public exponent 2'16+1 */
+ if (chunk_equals(e, chunk_from_str("\x01\x00\x01")))
+ {
+ goto end;
+ }
+ break;
+ case PRIV_KEY_PRIME1:
+ p = object;
+ if (p.len > 0 && *p.ptr == 0x00)
+ {
+ p = chunk_skip(p, 1);
+ }
+ if (p.len > MAX_RSA_KEY_BYTES / 2)
+ {
+ goto end;
+ }
+ memcpy(priv_rsa->t.buffer, p.ptr, p.len);
+ priv_rsa->t.size = p.len;
+ break;
+ }
+ }
+ success = parser->success(parser);
+
+end:
+ parser->destroy(parser);
+
+ return success;
+}
+
+/**
+ * Build a TPM 2.0 ECC key from an ASN.1 encoded private key blob.
+ */
+static bool build_ecc_key(chunk_t blob, TPMT_SENSITIVE *priv, TPMT_PUBLIC *pub)
+{
+ priv->sensitiveType = TPM_ALG_RSA;
+ pub->type = TPM_ALG_RSA;
+
+ return TRUE;
+}
+
+METHOD(tpm_tss_t, load_key, bool,
+ private_tpm_tss_tss2_t *this, uint32_t hierarchy, uint32_t handle,
+ chunk_t pin, key_type_t type, chunk_t encoding)
+{
+ bool success = FALSE;
+ uint32_t obj_handle, rval;
+
+ TPM2B_SENSITIVE sensitive = { { sizeof(TPM2B_SENSITIVE)-2, } };
+ TPM2B_PUBLIC public = { { sizeof(TPM2B_PUBLIC)-2, } };
+ TPMT_SENSITIVE *priv = &sensitive.t.sensitiveArea;
+ TPMT_PUBLIC *pub = &public.t.publicArea;
+
+ chunk_t priv_chunk = { (uint8_t*)priv, (size_t)sensitive.t.size };
+ chunk_t pub_chunk = { (uint8_t*)pub, (size_t)public.t.size};
+
+ TPM2B_NAME name = { { sizeof(TPM2B_NAME)-2, } };
+
+ TPMS_AUTH_RESPONSE session_data;
+ TSS2_SYS_RSP_AUTHS sessions_data;
+ TPMS_AUTH_RESPONSE *session_data_array[1];
+
+ session_data_array[0] = &session_data;
+ sessions_data.rspAuths = &session_data_array[0];
+ sessions_data.rspAuthsCount = 1;
+
+ pub->nameAlg = TPM_ALG_SHA256; /* TODO make nameAlg configurable */
+ pub->objectAttributes.val = 0x00040060;
+
+ switch (type)
+ {
+ case KEY_RSA:
+ success = build_rsa_key(encoding, priv, pub);
+ break;
+ case KEY_ECDSA:
+ success = build_ecc_key(encoding, priv, pub);
+ break;
+ default:
+ return FALSE;
+ }
+
+ if (!success)
+ {
+ return FALSE;
+ }
+ DBG1(DBG_PTS, "TPM2B_SENSITIVE: %B", &priv_chunk);
+ DBG1(DBG_PTS, "TPM2B_PUBLIC: %B", &pub_chunk);
+
+ rval = Tss2_Sys_LoadExternal(this->sys_context, 0, &sensitive, &public,
+ hierarchy, &obj_handle, &name, &sessions_data);
+ if (rval != TPM_RC_SUCCESS)
+ {
+ DBG1(DBG_PTS,"%s Tss2_Sys_LoadExternal failed: 0x%06x", LABEL, rval);
+ return FALSE;
+ }
+ DBG1(DBG_PTS, "handle = 0x%08x", obj_handle);
+
+ return success;
+}
+
METHOD(tpm_tss_t, destroy, void,
private_tpm_tss_tss2_t *this)
{
.sign = _sign,
.get_random = _get_random,
.get_data = _get_data,
+ .load_key = _load_key,
.destroy = _destroy,
},
);
--- /dev/null
+/*
+ * Copyright (C) 2017 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.
+ */
+
+
+#define _GNU_SOURCE
+#include <syslog.h>
+#include <getopt.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <tpm_tss.h>
+
+#include <library.h>
+#include <credentials/containers/pkcs12.h>
+#include <credentials/certificates/x509.h>
+#include <credentials/sets/mem_cred.h>
+#include <credentials/sets/callback_cred.h>
+#include <utils/debug.h>
+
+/* logging */
+static bool log_to_stderr = TRUE;
+static bool log_to_syslog = TRUE;
+static level_t default_loglevel = 1;
+
+/**
+ * Global TPM 2.0 instance
+ */
+static tpm_tss_t *tpm;
+
+/**
+ * Global PKCS#12 object
+ */
+static pkcs12_t *p12;
+
+/**
+ * Callback credential set pki uses
+ */
+static callback_cred_t *cb_set;
+
+/**
+ * Credential set to cache entered secrets
+ */
+static mem_cred_t *cb_creds;
+
+static shared_key_type_t prompted;
+
+/**
+ * Callback function to receive credentials
+ */
+static shared_key_t* cb(void *data, shared_key_type_t type,
+ identification_t *me, identification_t *other,
+ id_match_t *match_me, id_match_t *match_other)
+{
+ char buf[64], *label, *secret = NULL;
+ shared_key_t *shared;
+
+ if (prompted == type)
+ {
+ return NULL;
+ }
+ switch (type)
+ {
+ case SHARED_PIN:
+ label = "Smartcard PIN";
+ break;
+ case SHARED_PRIVATE_KEY_PASS:
+ label = "Private key passphrase";
+ break;
+ default:
+ return NULL;
+ }
+ snprintf(buf, sizeof(buf), "%s: ", label);
+#ifdef HAVE_GETPASS
+ secret = getpass(buf);
+#endif
+ if (secret && strlen(secret))
+ {
+ prompted = type;
+ if (match_me)
+ {
+ *match_me = ID_MATCH_PERFECT;
+ }
+ if (match_other)
+ {
+ *match_other = ID_MATCH_NONE;
+ }
+ shared = shared_key_create(type, chunk_clone(chunk_from_str(secret)));
+ /* cache password in case it is required more than once */
+ cb_creds->add_shared(cb_creds, shared, NULL);
+ return shared->get_ref(shared);
+ }
+ return NULL;
+}
+
+/**
+ * Register PIN/Passphrase callback function
+ */
+static void add_callback()
+{
+ cb_set = callback_cred_create_shared(cb, NULL);
+ lib->credmgr->add_set(lib->credmgr, &cb_set->set);
+ cb_creds = mem_cred_create();
+ lib->credmgr->add_set(lib->credmgr, &cb_creds->set);
+}
+
+/**
+ * Unregister PIN/Passphrase callback function
+ */
+static void remove_callback()
+{
+ lib->credmgr->remove_set(lib->credmgr, &cb_creds->set);
+ cb_creds->destroy(cb_creds);
+ lib->credmgr->remove_set(lib->credmgr, &cb_set->set);
+ cb_set->destroy(cb_set);
+}
+
+/**
+ * logging function for tpm_loadpkcs12
+ */
+static void tpm_loadpkcs12_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_loadpkcs12_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_loadpkcs12
+ *
+ * @param status 0 = OK, -1 = general discomfort
+ */
+static void exit_tpm_loadpkcs12(err_t message, ...)
+{
+ int status = 0;
+
+ DESTROY_IF(tpm);
+ if (p12)
+ {
+ container_t *container = &p12->container;
+
+ container->destroy(container);
+ }
+
+ /* 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_loadpkcs12 error: %s\n", m);
+ status = -1;
+ }
+ 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_loadpkcs12 --in <file> [--debug <level>] [--quiet]\n"
+ " tpm_loadpkcs12 --help\n"
+ "\n"
+ "Options:\n"
+ " --in (-i) binary input file with digest to be extended\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_loadpkcs12(message);
+}
+
+/**
+ * @brief main of tpm_loadpkcs12 which loads a PKCS#12 container and stores
+ * the key and certificates in a TPM 2.0
+ *
+ * @param argc number of arguments
+ * @param argv pointer to the argument values
+ */
+int main(int argc, char *argv[])
+{
+ uint32_t hierarchy = 0x40000007; /* TPM_RH_NULL */
+ uint32_t handle = 0;
+ char *infile = NULL;
+ chunk_t id, encoding, pin = chunk_empty;
+ enumerator_t *enumerator;
+ public_key_t *pubkey;
+ private_key_t *key;
+ key_type_t type;
+ certificate_t *cert;
+ x509_flag_t flags;
+ x509_t *x509;
+ bool found, success;
+
+ atexit(library_deinit);
+ if (!library_init(NULL, "tpm_loadpkcs12"))
+ {
+ exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
+ }
+ if (lib->integrity &&
+ !lib->integrity->check_file(lib->integrity, "tpm_loadpkcs12", argv[0]))
+ {
+ fprintf(stderr, "integrity check of tpm_loadpkcs12 failed\n");
+ exit(SS_RC_DAEMON_INTEGRITY);
+ }
+
+ for (;;)
+ {
+ static const struct option long_opts[] = {
+ /* name, has_arg, flag, val */
+ { "help", no_argument, NULL, 'h' },
+ { "in", required_argument, NULL, 'i' },
+ { "pin", required_argument, NULL, 'p' },
+ { "handle", required_argument, NULL, 'H' },
+ { "quiet", no_argument, NULL, 'q' },
+ { "debug", required_argument, NULL, 'l' },
+ { 0,0,0,0 }
+ };
+
+ /* parse next option */
+ int c = getopt_long(argc, argv, "hi:ql:", long_opts, NULL);
+
+ switch (c)
+ {
+ case EOF: /* end of flags */
+ break;
+
+ case 'h': /* --help */
+ usage(NULL);
+
+ case 'i': /* --in <file> */
+ infile = optarg;
+ continue;
+
+ case 'H':
+ continue;
+
+ case 'p':
+ pin = chunk_from_str(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;
+ }
+
+ init_log("tpm_loadpkcs12");
+
+ if (!lib->plugins->load(lib->plugins,
+ lib->settings->get_str(lib->settings, "tpm_loadpkcs12.load",
+ "random pem openssl tpm")))
+ {
+ exit_tpm_loadpkcs12("plugin loading failed");
+ }
+
+ /* try to find a TPM */
+ tpm = tpm_tss_probe(TPM_VERSION_2_0);
+ if (!tpm)
+ {
+ exit_tpm_loadpkcs12("no TPM 2.0 found");
+ }
+
+ if (!infile)
+ {
+ exit_tpm_loadpkcs12("mandatory --in argument missing");
+ }
+
+ /* add callback prompting for PKCS#12 password */
+ add_callback();
+ atexit(remove_callback);
+
+ p12 = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS12,
+ BUILD_FROM_FILE, infile, BUILD_END);
+
+ if (!p12)
+ {
+ exit_tpm_loadpkcs12("reading PKCS#12 file failed");
+ }
+ printf("loaded PKCS#12 file from '%s'\n", infile);
+
+ enumerator = p12->create_cert_enumerator(p12);
+ while (enumerator->enumerate(enumerator, &cert))
+ {
+ x509 = (x509_t*)cert;
+ flags = x509->get_flags(x509);
+ printf("%scertificate:\n", (flags & X509_CA) ? "ca " : "");
+
+ pubkey = cert->get_public_key(cert);
+ if (pubkey->get_fingerprint(pubkey, KEYID_PUBKEY_SHA1, &id))
+ {
+ printf(" subjectKeyIdentifier: %#B\n", &id);
+ }
+ if (pubkey->get_fingerprint(pubkey, KEYID_PUBKEY_INFO_SHA1, &id))
+ {
+ printf(" subjectPublicKeyInfo hash: %#B\n", &id);
+ }
+ pubkey->destroy(pubkey);
+ }
+ enumerator->destroy(enumerator);
+
+ enumerator = p12->create_key_enumerator(p12);
+ found = enumerator->enumerate(enumerator, &key);
+ enumerator->destroy(enumerator);
+
+ if (!found)
+ {
+ exit_tpm_loadpkcs12("no private key found in PKCS#12 container");
+ }
+ type = key->get_type(key);
+
+ /* print some private key information */
+ printf("%N private key:\n", key_type_names, type);
+ if (key->get_fingerprint(key, KEYID_PUBKEY_SHA1, &id))
+ {
+ printf(" subjectKeyIdentifier: %#B\n", &id);
+ }
+ if (key->get_fingerprint(key, KEYID_PUBKEY_INFO_SHA1, &id))
+ {
+ printf(" subjectPublicKeyInfo hash: %#B\n", &id);
+ }
+ if (!key->get_encoding(key, PRIVKEY_ASN1_DER, &encoding))
+ {
+ exit_tpm_loadpkcs12("private key encoding failed");
+ }
+ printf("%B\n", &encoding);
+
+ /* load private key into TPM */
+ success = tpm->load_key(tpm, hierarchy, handle, pin, type, encoding);
+
+ /* cleanup */
+ chunk_clear(&encoding);
+ exit_tpm_loadpkcs12(success ? NULL : "loading into TPM 2.0 failed");
+
+ return -1; /* should never be reached */
+}