Topics

[PATCH 3/4] load_tpm2_key: add new command to load a key file to a NV handle

James Bottomley
 

In order to use restricted keys as parents, they have to be loaded
into the TPMs NV handle area, so introduce a new command to do that.

Signed-off-by: James Bottomley <James.Bottomley@...>
---
.gitignore | 1 +
Makefile.am | 10 ++-
load_tpm2_key.1.in | 27 +++++++
load_tpm2_key.c | 234 +++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 269 insertions(+), 3 deletions(-)
create mode 100644 load_tpm2_key.1.in
create mode 100644 load_tpm2_key.c

diff --git a/.gitignore b/.gitignore
index c48a990..9eca341 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,6 +18,7 @@ libtool
ltmain.sh
missing
create_tpm2_key
+load_tpm2_key
test-driver
tests/*.log
tests/*.trs
diff --git a/Makefile.am b/Makefile.am
index dd52b2c..734bfc5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,14 +1,14 @@
EXTRA_DIST = README openssl.cnf.sample

if NATIVE_BUILD
-EXTRA_DIST += create_tpm2_key.1
-man1_MANS = create_tpm2_key.1
+EXTRA_DIST += create_tpm2_key.1 load_tpm2_key.1
+man1_MANS = create_tpm2_key.1 load_tpm2_key.1

CLEANFILES = $(man1_MANS)
endif

openssl_engine_LTLIBRARIES=libtpm2.la
-bin_PROGRAMS=create_tpm2_key
+bin_PROGRAMS=create_tpm2_key load_tpm2_key
openssl_enginedir=@enginesdir@

libtpm2_la_LDFLAGS= -no-undefined -avoid-version
@@ -20,6 +20,10 @@ create_tpm2_key_SOURCES=create_tpm2_key.c tpm2-common.c
create_tpm2_key_LDADD=${DEPS_LIBS}
create_tpm2_key_CFLAGS=${DEPS_CFLAGS} -Werror

+load_tpm2_key_SOURCES=load_tpm2_key.c tpm2-common.c
+load_tpm2_key_LDADD=${DEPS_LIBS}
+load_tpm2_key_CFLAGS=${DEPS_CFLAGS} -Werror
+
$(builddir)/%.1: $(srcdir)/%.1.in $(top_builddir)/%
$(HELP2MAN) --no-info -i $< -o $@ $(top_builddir)/$*

diff --git a/load_tpm2_key.1.in b/load_tpm2_key.1.in
new file mode 100644
index 0000000..20914e3
--- /dev/null
+++ b/load_tpm2_key.1.in
@@ -0,0 +1,27 @@
+[name]
+load_tpm2_key - load a tpm2 key at a permanent index
+
+[description]
+
+Used to load keys created by create_tpm2_key(1) to a permanent
+NV index.
+
+The reasons for doing this are either to have an unrestricted key
+always accessible to the TPM without needing a key file or to have a
+new restricted decryption key parent from which other keys can be
+created as children.
+
+[examples]
+
+Create a TPM internal key and load it at index 81000101
+
+ create_tpm2_key -p 81000001 tmp.key
+ load_tpm2_key tmp.key 81000101
+
+Create a wrapped restricted decryption key at 81000101 and use it
+as the parent of a new key
+
+ openssl genrsa 2048 > key.priv
+ create_tpm2_key --restricted -w key.priv key.tpm
+ load_tpm2_key key.tpm 81000101
+ create_tpm2_key -p 81000101 newkey.tpm
diff --git a/load_tpm2_key.c b/load_tpm2_key.c
new file mode 100644
index 0000000..123cf9f
--- /dev/null
+++ b/load_tpm2_key.c
@@ -0,0 +1,234 @@
+/*
+ *
+ * Copyright (C) 2016 James Bottomley <James.Bottomley@...>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ */
+
+
+#include <stdio.h>
+#include <getopt.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include <arpa/inet.h>
+
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+
+#define TSSINCLUDE(x) < TSS_INCLUDE/x >
+#include TSSINCLUDE(tss.h)
+#include TSSINCLUDE(tssutils.h)
+#include TSSINCLUDE(tssmarshal.h)
+#include TSSINCLUDE(Unmarshal_fp.h)
+#include TSSINCLUDE(tsscrypto.h)
+#include TSSINCLUDE(tsscryptoh.h)
+
+#include "tpm2-asn.h"
+#include "tpm2-common.h"
+
+/* for use as a TPM_RC return type to indicate this is
+ * not a TPM error, so don't process the rc as one */
+#define NOT_TPM_ERROR (0xffffffff)
+
+static struct option long_options[] = {
+ {"auth-parent", 1, 0, 'b'},
+ {"force", 0, 0, 'f'},
+ {"help", 0, 0, 'h'},
+ {"version", 0, 0, 'v'},
+ {0, 0, 0, 0}
+};
+
+void
+usage(char *argv0)
+{
+ fprintf(stdout, "Usage: %s [options] <filename> <nvindex>\n\n"
+ "Options:\n"
+ "\t-b, --auth-parent <pwd> Specify the parent key password\n"
+ "\t (default EmptyAuth)\n"
+ "\t-f, --force force loading of key with policy\n"
+ "\t-h, --help print this help message\n"
+ "\n"
+ "Report bugs to " PACKAGE_BUGREPORT "\n",
+ argv0);
+ exit(-1);
+}
+
+void
+openssl_print_errors()
+{
+ ERR_load_ERR_strings();
+ ERR_load_crypto_strings();
+ ERR_print_errors_fp(stderr);
+}
+
+int main(int argc, char **argv)
+{
+ char *filename;
+ TPM_HANDLE nvindex;
+ const char *tssdir;
+ TSSPRIVKEY *tpk;
+ BIO *bf;
+ int option_index, c;
+ int force = 0;
+ TSS_CONTEXT *tssContext;
+ TPM_RC rc;
+ Load_In lin;
+ Load_Out lout;
+ EvictControl_In ein;
+ BYTE *buffer;
+ INT32 size;
+ char *auth = NULL;
+ TPM_HANDLE session, parent;
+ int ret = 1;
+
+ while (1) {
+ option_index = 0;
+ c = getopt_long(argc, argv, "b:fhv",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'f':
+ force = 1;
+ break;
+ case 'h':
+ usage(argv[0]);
+ break;
+ case 'b':
+ auth = optarg;
+ break;
+ case 'v':
+ fprintf(stdout, "%s " VERSION "\n"
+ "Copyright 2019 by James Bottomley\n"
+ "License LGPL-2.1-only\n"
+ "Written by James Bottomley <James.Bottomley@...>\n",
+ argv[0]);
+ exit(0);
+ default:
+ printf("Unknown option '%c'\n", c);
+ usage(argv[0]);
+ break;
+ }
+ }
+ if (optind >= argc - 1) {
+ printf("Too few arguments: Expected filename and nvindex");
+ usage(argv[0]);
+ }
+
+ filename = argv[argc - 2];
+ nvindex = strtoul(argv[argc - 1], NULL, 16);
+
+ if (optind < argc - 2) {
+ printf("Unexpected additional arguments\n");
+ usage(argv[0]);
+ }
+
+ if ((nvindex & 0xff000000) != 0x81000000) {
+ printf("nvindex must have MSO 81\n");
+ exit(1);
+ }
+
+ bf = BIO_new_file(filename, "r");
+ if (!bf) {
+ fprintf(stderr, "File %s does not exist or cannot be read\n", filename);
+ exit(1);
+ }
+ tpk = PEM_read_bio_TSSPRIVKEY(bf, NULL, NULL, NULL);
+ BIO_free(bf);
+
+ if (!tpk) {
+ fprintf(stderr, "Failed to parse file %s\n", filename);
+ exit(1);
+ }
+ if (tpk->policy && !force) {
+ fprintf(stderr, "Warning: key %s has associated policy\n"
+ "Policy keys are hard to use, specify --force if this is really what you want\n",
+ filename);
+ goto out_free;
+ }
+
+ buffer = tpk->privkey->data;
+ size = tpk->privkey->length;
+ TPM2B_PRIVATE_Unmarshal(&lin.inPrivate, &buffer, &size);
+
+ buffer = tpk->pubkey->data;
+ size = tpk->pubkey->length;
+ TPM2B_PUBLIC_Unmarshal(&lin.inPublic, &buffer, &size, FALSE);
+
+ parent = ASN1_INTEGER_get(tpk->parent);
+ TSSPRIVKEY_free(tpk);
+ tssdir = tpm2_set_unique_tssdir();
+ rc = tpm2_create(&tssContext, tssdir);
+ if (rc) {
+ tpm2_error(rc, "tpm2_create");
+ exit(1);
+ }
+
+ if ((parent & 0xff000000) == 0x81000000) {
+ lin.parentHandle = parent;
+ } else {
+ rc = tpm2_load_srk(tssContext, &lin.parentHandle, auth, NULL,
+ parent, 1);
+ if (rc)
+ goto out;
+ }
+ rc = tpm2_get_session_handle(tssContext, &session, lin.parentHandle,
+ TPM_SE_HMAC, TPM_ALG_SHA256);
+ if (rc)
+ goto out_flush_srk;
+ rc = TSS_Execute(tssContext,
+ (RESPONSE_PARAMETERS *)&lout,
+ (COMMAND_PARAMETERS *)&lin,
+ NULL,
+ TPM_CC_Load,
+ session, auth, 0,
+ TPM_RH_NULL, NULL, 0);
+ if (rc) {
+ tpm2_error(rc, "TPM2_Load");
+ tpm2_flush_handle(tssContext, session);
+ }
+ out_flush_srk:
+ tpm2_flush_srk(tssContext, lin.parentHandle);
+ if (rc)
+ goto out;
+
+ ein.auth = TPM_RH_OWNER;
+ ein.objectHandle = lout.objectHandle;
+ ein.persistentHandle = nvindex;
+ rc = TSS_Execute(tssContext,
+ NULL,
+ (COMMAND_PARAMETERS *)&ein,
+ NULL,
+ TPM_CC_EvictControl,
+ TPM_RS_PW, NULL, 0,
+ TPM_RH_NULL, NULL, 0);
+ if (rc)
+ tpm2_error(rc, "TPM2_EvictControl");
+ else
+ ret = 0;
+
+ tpm2_flush_handle(tssContext, lout.objectHandle);
+
+ out:
+ TSS_Delete(tssContext);
+ tpm2_rm_keyfile(tssdir, parent);
+ tpm2_rm_keyfile(tssdir, nvindex);
+ tpm2_rm_tssdir(tssdir);
+ exit(ret);
+
+ out_free:
+ TSSPRIVKEY_free(tpk);
+ exit(1);
+}
--
2.16.4