Date   
[PATCH 4/4] Add test for seal/unseal

James Bottomley
 

Add a simple test to make sure a key is recognized as not sealed data
and to seal and unseal data with a password.

Signed-off-by: James Bottomley <James.Bottomley@...>
---
tests/Makefile.am | 3 ++-
tests/seal_unseal.sh | 19 +++++++++++++++++++
2 files changed, 21 insertions(+), 1 deletion(-)
create mode 100755 tests/seal_unseal.sh

diff --git a/tests/Makefile.am b/tests/Makefile.am
index cd582e0..60c3e8d 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -21,6 +21,7 @@ TESTS = fail_connect.sh \
check_importable.sh \
check_rsa_oaep_pss.sh \
restricted_parent.sh \
+ seal_unseal.sh \
stop_sw_tpm.sh

AM_TESTS_ENVIRONMENT = TPM_INTERFACE_TYPE=socsim; \
@@ -29,6 +30,6 @@ AM_TESTS_ENVIRONMENT = TPM_INTERFACE_TYPE=socsim; \
export TPM_INTERFACE_TYPE OPENSSL_CONF srcdir;
TEST_EXTENSIONS = .sh

-CLEANFILES = key*.tpm key*.pub key*.priv tmp.* NVChip h*.bin key*.der
+CLEANFILES = key*.tpm key*.pub key*.priv tmp.* NVChip h*.bin key*.der seal.*
clean-local:
rm -fr testdir
diff --git a/tests/seal_unseal.sh b/tests/seal_unseal.sh
new file mode 100755
index 0000000..b1df920
--- /dev/null
+++ b/tests/seal_unseal.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+set -x
+
+bindir=${srcdir}/..
+
+##
+# test is
+# 1. Verify that a standard key can't be unsealed
+# 2. seal a phrase
+# 3. recover the same phrase on unseal
+##
+DATA="This is some DATA"
+AUTH="Passw0rd"
+${bindir}/create_tpm2_key key.tpm || exit 1;
+${bindir}/unseal_tpm2_data key.tpm 2> /dev/null && exit 1;
+echo $DATA | ${bindir}/seal_tpm2_data -a -k ${AUTH} seal.tpm || exit 1;
+${bindir}/unseal_tpm2_data -k ${AUTH} seal.tpm | grep -q "${DATA}" || exit 1;
+
+exit 0
--
2.16.4

[PATCH 3/4] Add TPM data sealing and unsealing functions

James Bottomley
 

This adds two commands and their manpages: seal_tpm2_data and
unseal_tpm2_data plus a new OID 2.23.133.10.1.5 to identify sealed
data. The object is to be interoperable with the new kernel sealed
data format.

Signed-off-by: James Bottomley <James.Bottomley@...>
---
.gitignore | 2 +
Makefile.am | 16 ++-
e_tpm2-ecc.c | 2 +-
e_tpm2-rsa.c | 2 +-
load_tpm2_key.c | 2 +-
seal_tpm2_data.1.in | 18 +++
seal_tpm2_data.c | 337 ++++++++++++++++++++++++++++++++++++++++++++++++++
tpm2-asn.h | 2 +-
tpm2-common.c | 18 ++-
tpm2-common.h | 2 +-
unseal_tpm2_data.1.in | 18 +++
unseal_tpm2_data.c | 206 ++++++++++++++++++++++++++++++
12 files changed, 612 insertions(+), 13 deletions(-)
create mode 100644 seal_tpm2_data.1.in
create mode 100644 seal_tpm2_data.c
create mode 100644 unseal_tpm2_data.1.in
create mode 100644 unseal_tpm2_data.c

diff --git a/.gitignore b/.gitignore
index 9eca341..48f9458 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,8 @@ ltmain.sh
missing
create_tpm2_key
load_tpm2_key
+seal_tpm2_data
+unseal_tpm2_data
test-driver
tests/*.log
tests/*.trs
diff --git a/Makefile.am b/Makefile.am
index 734bfc5..33de0d9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,14 +1,16 @@
EXTRA_DIST = README openssl.cnf.sample

if NATIVE_BUILD
-EXTRA_DIST += create_tpm2_key.1 load_tpm2_key.1
-man1_MANS = create_tpm2_key.1 load_tpm2_key.1
+EXTRA_DIST += create_tpm2_key.1 load_tpm2_key.1 seal_tpm2_data.1 \
+ unseal_tpm2_data.1
+man1_MANS = create_tpm2_key.1 load_tpm2_key.1 seal_tpm2_data.1 \
+ unseal_tpm2_data.1

CLEANFILES = $(man1_MANS)
endif

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

libtpm2_la_LDFLAGS= -no-undefined -avoid-version
@@ -24,6 +26,14 @@ load_tpm2_key_SOURCES=load_tpm2_key.c tpm2-common.c
load_tpm2_key_LDADD=${DEPS_LIBS}
load_tpm2_key_CFLAGS=${DEPS_CFLAGS} -Werror

+seal_tpm2_data_SOURCES=seal_tpm2_data.c tpm2-common.c
+seal_tpm2_data_LDADD=${DEPS_LIBS}
+seal_tpm2_data_CFLAGS=${DEPS_CFLAGS} -Werror
+
+unseal_tpm2_data_SOURCES=unseal_tpm2_data.c tpm2-common.c
+unseal_tpm2_data_LDADD=${DEPS_LIBS}
+unseal_tpm2_data_CFLAGS=${DEPS_CFLAGS} -Werror
+
$(builddir)/%.1: $(srcdir)/%.1.in $(top_builddir)/%
$(HELP2MAN) --no-info -i $< -o $@ $(top_builddir)/$*

diff --git a/e_tpm2-ecc.c b/e_tpm2-ecc.c
index 89e3762..0e4c905 100644
--- a/e_tpm2-ecc.c
+++ b/e_tpm2-ecc.c
@@ -87,7 +87,7 @@ static TPM_HANDLE tpm2_load_key_from_ecc(const EC_KEY *eck,
*num_commands = app_data->num_commands;
*nameAlg = app_data->name_alg;

- return tpm2_load_key(tssContext, app_data, srk_auth);
+ return tpm2_load_key(tssContext, app_data, srk_auth, NULL);
}

void tpm2_bind_key_to_engine_ecc(EVP_PKEY *pkey, void *data)
diff --git a/e_tpm2-rsa.c b/e_tpm2-rsa.c
index 85be4a8..07fb075 100644
--- a/e_tpm2-rsa.c
+++ b/e_tpm2-rsa.c
@@ -119,7 +119,7 @@ static TPM_HANDLE tpm2_load_key_from_rsa(RSA *rsa, TSS_CONTEXT **tssContext,
*num_commands = app_data->num_commands;
*nameAlg = app_data->name_alg;

- return tpm2_load_key(tssContext, app_data, srk_auth);
+ return tpm2_load_key(tssContext, app_data, srk_auth, NULL);
}

void tpm2_bind_key_to_engine_rsa(EVP_PKEY *pkey, void *data)
diff --git a/load_tpm2_key.c b/load_tpm2_key.c
index 244c9dc..defc1c4 100644
--- a/load_tpm2_key.c
+++ b/load_tpm2_key.c
@@ -148,7 +148,7 @@ int main(int argc, char **argv)
goto out_free;
}

- ret = tpm2_load_key(&tssContext, app_data, auth);
+ ret = tpm2_load_key(&tssContext, app_data, auth, NULL);
if (!ret) {
ret = 1;
goto out;
diff --git a/seal_tpm2_data.1.in b/seal_tpm2_data.1.in
new file mode 100644
index 0000000..b88510a
--- /dev/null
+++ b/seal_tpm2_data.1.in
@@ -0,0 +1,18 @@
+[name]
+seal_tpm2_data - seal a blob of data for a TPM
+
+[description]
+
+Used to create a sealed blob of data which can be unsealed via the
+TPM. Possible uses for this blob of data include as a symmetric key,
+which is the use in the linux kernel trusted key infrastructure.
+
+[examples]
+
+Create a sealed data blob to the storage parent (owner hierarchy)
+
+ echo somedatatoseal | seal_tpm2_key -p owner seal.tpm
+
+Unseal the data
+
+ unseal_tpm2_key seal.tpm
diff --git a/seal_tpm2_data.c b/seal_tpm2_data.c
new file mode 100644
index 0000000..a04f878
--- /dev/null
+++ b/seal_tpm2_data.c
@@ -0,0 +1,337 @@
+/*
+ *
+ * Copyright (C) 2019 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 <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/ui.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"
+
+static TPM_ALG_ID name_alg = TPM_ALG_SHA256;
+
+static struct option long_options[] = {
+ {"auth", 0, 0, 'a'},
+ {"auth-parent", 1, 0, 'b'},
+ {"help", 0, 0, 'h'},
+ {"parent-handle", 1, 0, 'p'},
+ {"version", 0, 0, 'v'},
+ {"password", 1, 0, 'k'},
+ {"da", 0, 0, 'd'},
+ {"policy", 1, 0, 'c'},
+ {"nomigrate", 0, 0, 'm'},
+ {"name-scheme", 1, 0, 'n'},
+ {0, 0, 0, 0}
+};
+
+static void tpm2_public_template_seal(TPMT_PUBLIC *pub)
+{
+ pub->type = TPM_ALG_KEYEDHASH;
+ pub->nameAlg = name_alg;
+ pub->objectAttributes.val =
+ TPMA_OBJECT_USERWITHAUTH;
+ pub->authPolicy.t.size = 0;
+ pub->parameters.keyedHashDetail.scheme.scheme = TPM_ALG_NULL;
+ pub->unique.sym.t.size = 0;
+}
+
+void
+usage(char *argv0)
+{
+ fprintf(stdout, "Usage: %s [options] <filename>\n\n"
+ "Options:\n"
+ "\t-a, --auth The data blob requires authorization\n"
+ "\t-b, --auth-parent <pwd> Specify the parent key password\n"
+ "\t (default EmptyAuth)\n"
+ "\t-d, --da mark the key as having Dictionary Attack implications. This means that if\n"
+ "\t the key password is incorrectly presented too many times, the TPM may\n"
+ "\t Implement DA mitigation and refuse connections for a while\n"
+ "\t-h, --help print this help message\n"
+ "\t-p, --parent-handle <handle> parent for the key, can either be a\n"
+ "\t persistent key or a hierarchy.\n"
+ "\t the hierarchies can be 'platform',\n"
+ "\t 'owner', 'null' or 'endorsement'.\n"
+ "\t The seeds used for derivation are\n"
+ "\t platform, storage, null or endorsement\n"
+ "\t respectively\n"
+ "\t-v, --version print package version\n"
+ "\t-k, --password <pwd> use this password instead of prompting\n"
+ "\t-m,--nomigrate Create a sealed data bundle that can be\n"
+ " migrated to other systems.\n"
+ "\t-n, --name-scheme <scheme> name algorithm to use sha1 [sha256] sha384 sha512\n"
+ "\n"
+ "Report bugs to " PACKAGE_BUGREPORT "\n",
+ argv0);
+ exit(-1);
+}
+
+int main(int argc, char **argv)
+{
+ int option_index, c;
+ int nomigrate = 0, parent = TPM_RH_OWNER;
+ char *data_auth = NULL, *parent_auth = NULL, *pass = NULL;
+ char *policyFilename = NULL;
+ char *filename;
+ uint32_t noda = TPMA_OBJECT_NODA, phandle;
+ TPM_RC rc;
+ TSS_CONTEXT *tssContext;
+ const char *dir;
+ const char *reason;
+ TPMT_HA digest;
+ uint32_t sizeInBytes;
+ TPM_HANDLE authHandle;
+ STACK_OF(TSSOPTPOLICY) *sk = NULL;
+ Create_In cin;
+ Create_Out cout;
+ TPMS_SENSITIVE_CREATE *s = &cin.inSensitive.sensitive;
+ TPMT_PUBLIC *p = &cin.inPublic.publicArea;
+ BYTE pubkey[sizeof(TPM2B_PUBLIC)];
+ BYTE privkey[sizeof(TPM2B_PRIVATE)];
+ BYTE *buffer;
+ int32_t size;
+ uint16_t pubkey_len, privkey_len;
+
+ while (1) {
+ option_index = 0;
+ c = getopt_long(argc, argv, "ak:b:hp:vdsun",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'a':
+ data_auth = malloc(128);
+ break;
+ case 'k':
+ pass = optarg;
+ if (strlen(pass) > 127) {
+ printf("password is too long\n");
+ exit(1);
+ }
+ break;
+ case 'b':
+ parent_auth = optarg;
+ break;
+ case 'h':
+ usage(argv[0]);
+ break;
+ case 'n':
+ if (!strcasecmp("sha1", optarg)) {
+ name_alg = TPM_ALG_SHA1;
+ } else if (!strcasecmp("sha256", optarg)) {
+ /* default, do nothing */
+ } else if (!strcasecmp("sha384", optarg)) {
+ name_alg = TPM_ALG_SHA384;
+#ifdef TPM_ALG_SHA512
+ } else if (!strcasecmp("sha512", optarg)) {
+ name_alg = TPM_ALG_SHA512;
+#endif
+ } else {
+ usage(argv[0]);
+ }
+ break;
+ case 'p':
+ parent = tpm2_get_parent(optarg);
+ if (parent == 0) {
+ fprintf(stderr, "Invalid parent %s\n", optarg);
+ exit(1);
+ }
+ break;
+ case 'v':
+ fprintf(stdout, "%s " VERSION "\n"
+ "Copyright 2017 by James Bottomley\n"
+ "License LGPL-2.1-only\n"
+ "Written by James Bottomley <James.Bottomley@...>\n",
+ argv[0]);
+ exit(0);
+ case 'd':
+ noda = 0;
+ break;
+ case 'c':
+ policyFilename = optarg;
+ break;
+ case 'm':
+ nomigrate = 1;
+ break;
+ default:
+ printf("Unknown option '%c'\n", c);
+ usage(argv[0]);
+ break;
+ }
+ }
+ if (optind >= argc) {
+ printf("Too few arguments: Expected file name as last argument\n");
+ usage(argv[0]);
+ }
+
+ filename = argv[argc - 1];
+
+ if (optind < argc - 1) {
+ printf("Unexpected additional arguments\n");
+ usage(argv[0]);
+ }
+
+ digest.hashAlg = name_alg;
+ sizeInBytes = TSS_GetDigestSize(digest.hashAlg);
+ memset((uint8_t *)&digest.digest, 0, sizeInBytes);
+
+ if (policyFilename) {
+ sk = sk_TSSOPTPOLICY_new_null();
+ if (!sk) {
+ fprintf(stderr, "Failed to allocate policy stack\n");
+ exit(1);
+ }
+
+ rc = tpm2_parse_policy_file(policyFilename, sk,
+ data_auth, &digest);
+ if (rc) {
+ reason = "parse_policy_file";
+ goto out_free_policy;
+ }
+ }
+
+ if (data_auth) {
+ if (pass) {
+ /* key length already checked */
+ strcpy(data_auth, pass);
+ } else {
+ if (EVP_read_pw_string(data_auth, 128,
+ "Enter TPM key authority: ", 1)) {
+ fprintf(stderr, "Passwords do not match\n");
+ reason = "authorization";
+ rc = NOT_TPM_ERROR;
+ goto out_free_auth;
+ }
+ }
+ }
+
+ dir = tpm2_set_unique_tssdir();
+ rc = tpm2_create(&tssContext, dir);
+ if (rc) {
+ reason = "TSS_Create";
+ goto out_rmdir;
+ }
+
+ if ((parent & 0xff000000) == 0x40000000) {
+ rc = tpm2_load_srk(tssContext, &phandle, parent_auth,
+ NULL, parent, 1);
+ if (rc) {
+ reason = "tpm2_load_srk";
+ goto out_delete;
+ }
+ } else {
+ phandle = parent;
+ }
+
+ tpm2_public_template_seal(p);
+
+ cin.parentHandle = phandle;
+ cin.outsideInfo.t.size = 0;
+ cin.creationPCR.count = 0;
+
+ if (policyFilename) {
+ p->objectAttributes.val &=
+ ~TPMA_OBJECT_USERWITHAUTH;
+ rc = TSS_TPM2B_Create(
+ &p->authPolicy.b,
+ (uint8_t *)&digest.digest, sizeInBytes,
+ sizeof(TPMU_HA));
+ if (rc) {
+ reason = "set policy";
+ goto out_flush;
+ }
+ }
+
+ memset(s, 0, sizeof(*s));
+ if (data_auth) {
+ int len = strlen(data_auth);
+ memcpy(s->userAuth.b.buffer, data_auth, len);
+ s->userAuth.b.size = len;
+ }
+ s->data.t.size = fread(s->data.t.buffer, 1,
+ MAX_SYM_DATA, stdin);
+
+ /* set the NODA flag */
+ p->objectAttributes.val |= noda;
+
+ if (nomigrate)
+ p->objectAttributes.val |=
+ TPMA_OBJECT_FIXEDPARENT |
+ TPMA_OBJECT_FIXEDTPM;
+
+ /* use salted parameter encryption to hide the key */
+ rc = tpm2_get_session_handle(tssContext, &authHandle, phandle,
+ TPM_SE_HMAC, name_alg);
+ if (rc) {
+ reason = "get session handle";
+ goto out_flush;
+ }
+
+ rc = TSS_Execute(tssContext,
+ (RESPONSE_PARAMETERS *)&cout,
+ (COMMAND_PARAMETERS *)&cin,
+ NULL,
+ TPM_CC_Create,
+ authHandle, parent_auth, TPMA_SESSION_DECRYPT,
+ TPM_RH_NULL, NULL, 0);
+ if (rc) {
+ reason = "TPM2_Create";
+ /* failure means auth handle is not flushed */
+ tpm2_flush_handle(tssContext, authHandle);
+ goto out_flush;
+ }
+
+ buffer = pubkey;
+ pubkey_len = 0;
+ size = sizeof(pubkey);
+ TSS_TPM2B_PUBLIC_Marshal(&cout.outPublic, &pubkey_len,
+ &buffer, &size);
+ buffer = privkey;
+ privkey_len = 0;
+ size = sizeof(privkey);
+ TSS_TPM2B_PRIVATE_Marshal(&cout.outPrivate, &privkey_len,
+ &buffer, &size);
+ tpm2_write_tpmfile(filename, pubkey, pubkey_len,
+ privkey, privkey_len, data_auth == NULL,
+ parent, sk, 2, NULL);
+
+
+ out_flush:
+ tpm2_flush_srk(tssContext, phandle);
+ out_delete:
+ TSS_Delete(tssContext);
+ out_rmdir:
+ rmdir(dir);
+ out_free_auth:
+ free(data_auth);
+ out_free_policy:
+ tpm2_free_policy(sk);
+
+ if (rc) {
+ if (rc == NOT_TPM_ERROR)
+ fprintf(stderr, "%s failed\n", reason);
+ else
+ tpm2_error(rc, reason);
+ rc = 1;
+ }
+ exit(rc);
+}
diff --git a/tpm2-asn.h b/tpm2-asn.h
index 9afa7c3..93a97fc 100644
--- a/tpm2-asn.h
+++ b/tpm2-asn.h
@@ -98,7 +98,7 @@ typedef struct {

#define OID_loadableKey "2.23.133.10.1.3"
#define OID_importableKey "2.23.133.10.1.4"
-
+#define OID_sealedData "2.23.133.10.1.5"

/* This is the PEM guard tag */
#define TSSLOADABLE_PEM_STRING "TSS2 KEY BLOB"
diff --git a/tpm2-common.c b/tpm2-common.c
index 95d5f7e..f346585 100644
--- a/tpm2-common.c
+++ b/tpm2-common.c
@@ -1117,6 +1117,8 @@ int tpm2_load_engine_file(const char *filename, struct app_data **app_data,
goto err;
}
tpm2_type = TPM2_IMPORTABLE;
+ } else if (strcmp(OID_sealedData, oid) == 0){
+ tpm2_type = TPM2_SEALED;
} else {
fprintf(stderr, "Unrecognised object type\n");
goto err;
@@ -1300,7 +1302,7 @@ void tpm2_delete(struct app_data *app_data)
}

TPM_HANDLE tpm2_load_key(TSS_CONTEXT **tsscp, struct app_data *app_data,
- const char *srk_auth)
+ const char *srk_auth, uint32_t *psrk)
{
TSS_CONTEXT *tssContext;
Load_In in;
@@ -1349,17 +1351,21 @@ TPM_HANDLE tpm2_load_key(TSS_CONTEXT **tsscp, struct app_data *app_data,
if (rc) {
tpm2_error(rc, "TPM2_Load");
tpm2_flush_handle(tssContext, session);
- }
- else
+ } else {
key = out.objectHandle;
+ }

out_flush_srk:
- tpm2_flush_srk(tssContext, in.parentHandle);
+ if (key && psrk)
+ *psrk = in.parentHandle;
+ else
+ tpm2_flush_srk(tssContext, in.parentHandle);
out:
if (!key)
TSS_Delete(tssContext);
else
*tsscp = tssContext;
+
return key;
}

@@ -1428,7 +1434,9 @@ int tpm2_write_tpmfile(const char *file, BYTE *pubkey, int pubkey_len,

PEM_write_bio_TSSLOADABLE(outb, &k.tssl);
} else {
- if (secret) {
+ if (version == 2) {
+ k.tpk.type = OBJ_txt2obj(OID_sealedData, 1);
+ } else if (secret) {
k.tpk.type = OBJ_txt2obj(OID_importableKey, 1);
k.tpk.secret = ASN1_OCTET_STRING_new();
ASN1_STRING_set(k.tpk.secret, secret->t.secret,
diff --git a/tpm2-common.h b/tpm2-common.h
index 33cac4a..046761c 100644
--- a/tpm2-common.h
+++ b/tpm2-common.h
@@ -71,7 +71,7 @@ int tpm2_load_engine_file(const char *filename, struct app_data **app_data,
EVP_PKEY **ppkey, UI_METHOD *ui, void *cb_data,
const char *srk_auth, int get_key_auth);
TPM_HANDLE tpm2_load_key(TSS_CONTEXT **tsscp, struct app_data *app_data,
- const char *srk_auth);
+ const char *srk_auth, uint32_t *psession);
void tpm2_unload_key(TSS_CONTEXT *tssContext, TPM_HANDLE key);
void tpm2_delete(struct app_data *app_data);
char *tpm2_get_auth(UI_METHOD *ui, char *input_string, void *cb_data);
diff --git a/unseal_tpm2_data.1.in b/unseal_tpm2_data.1.in
new file mode 100644
index 0000000..3533f13
--- /dev/null
+++ b/unseal_tpm2_data.1.in
@@ -0,0 +1,18 @@
+[name]
+unseal_tpm2_data - unseal a blob of data using a TPM
+
+[description]
+
+Once a sealed data blob has been created, it may only be unsealed by
+the TPM for which it was created. This command attempts that unseal
+operation
+
+[examples]
+
+Create a sealed data blob to the storage parent (owner hierarchy)
+
+ echo "somedatatoseal" seal_tpm2_key -a -k passw0rd -p owner seal.tpm
+
+Unseal the data
+
+ unseal_tpm2_key -k passw0rd seal.tpm
diff --git a/unseal_tpm2_data.c b/unseal_tpm2_data.c
new file mode 100644
index 0000000..51f1246
--- /dev/null
+++ b/unseal_tpm2_data.c
@@ -0,0 +1,206 @@
+/*
+ *
+ * Copyright (C) 2019 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 <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/ui.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"
+
+static TPM_ALG_ID name_alg = TPM_ALG_SHA256;
+
+static struct option long_options[] = {
+ {"auth-parent", 1, 0, 'b'},
+ {"help", 0, 0, 'h'},
+ {"version", 0, 0, 'v'},
+ {"password", 1, 0, 'k'},
+ {0, 0, 0, 0}
+};
+
+void
+usage(char *argv0)
+{
+ fprintf(stdout, "Usage: %s [options] <filename>\n\n"
+ "Options:\n"
+ "\t-b, --auth-parent <pwd> Specify the parent key password\n"
+ "\t (default EmptyAuth)\n"
+ "\t-h, --help print this help message\n"
+ "\t-v, --version print package version\n"
+ "\t-k, --password <pwd> use this password instead of prompting\n"
+ "\n"
+ "Report bugs to " PACKAGE_BUGREPORT "\n",
+ argv0);
+ exit(-1);
+}
+
+static int ui_read(UI *ui, UI_STRING *uis)
+{
+ char password[128];
+ const char *pwd = UI_get0_user_data(ui);
+
+ if (UI_get_string_type(uis) != UIT_PROMPT)
+ return 0;
+
+ if (!pwd || pwd[0] == '\0') {
+ pwd = password;
+ EVP_read_pw_string(password, sizeof(password), "TPM Sealed Data Passphrase:", 0);
+ }
+ UI_set_result(ui, uis, pwd);
+ return 1;
+}
+
+int main(int argc, char **argv)
+{
+ int option_index, c;
+ char *parent_auth = NULL, *pass = NULL;
+ char *filename;
+ TPM_RC rc;
+ TSS_CONTEXT *tssContext;
+ const char *reason;
+ Unseal_In uin;
+ Unseal_Out uout;
+ uint32_t parent, session;
+ UI_METHOD *ui = UI_create_method("unseal");
+ struct app_data *app_data;
+
+ while (1) {
+ option_index = 0;
+ c = getopt_long(argc, argv, "k:b:hv",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'k':
+ pass = optarg;
+ if (strlen(pass) > 127) {
+ printf("password is too long\n");
+ exit(1);
+ }
+ break;
+ case 'b':
+ parent_auth = optarg;
+ break;
+ case 'h':
+ usage(argv[0]);
+ break;
+ case 'v':
+ fprintf(stdout, "%s " VERSION "\n"
+ "Copyright 2017 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) {
+ printf("Too few arguments: Expected file name as last argument\n");
+ usage(argv[0]);
+ }
+
+ filename = argv[argc - 1];
+
+ if (optind < argc - 1) {
+ printf("Unexpected additional arguments\n");
+ usage(argv[0]);
+ }
+
+ if (!ui) {
+ fprintf(stderr, "Failed to allocate UI\n");
+ exit(1);
+ }
+
+ UI_method_set_reader(ui, ui_read);
+ rc = tpm2_load_engine_file(filename, &app_data, NULL,
+ ui, pass, parent_auth, 1);
+ if (!rc) {
+ reason = "tpm2_engine_load_file";
+ rc = NOT_TPM_ERROR;
+ goto err;
+ }
+
+ rc = tpm2_load_key(&tssContext, app_data, parent_auth,
+ &parent);
+ if (!rc) {
+ reason = "tpm2_load_key";
+ rc = NOT_TPM_ERROR;
+ goto out_free_app_data;
+ }
+
+ uin.itemHandle = rc;
+
+ rc = tpm2_get_session_handle(tssContext, &session, parent,
+ app_data->req_policy_session ?
+ TPM_SE_POLICY : TPM_SE_HMAC,
+ name_alg);
+ tpm2_flush_handle(tssContext, parent);
+ if (rc) {
+ reason = "tpm2_get_session_handle";
+ goto out_flush_data;
+ }
+
+ if (app_data->req_policy_session) {
+ rc = tpm2_init_session(tssContext, session,
+ app_data->num_commands,
+ app_data->commands, name_alg);
+ if (rc) {
+ reason = "tpm2_init_session";
+ goto out_flush_session;
+ }
+ }
+
+ rc = TSS_Execute(tssContext,
+ (RESPONSE_PARAMETERS *)&uout,
+ (COMMAND_PARAMETERS *)&uin,
+ NULL,
+ TPM_CC_Unseal,
+ session, app_data->auth, TPMA_SESSION_ENCRYPT,
+ TPM_RH_NULL, NULL, 0);
+ if (rc) {
+ reason = "TPM2_Unseal";
+ out_flush_session:
+ tpm2_flush_handle(tssContext, session);
+ } else {
+ fwrite(uout.outData.t.buffer, 1, uout.outData.t.size, stdout);
+ }
+
+ out_flush_data:
+ tpm2_flush_handle(tssContext, uin.itemHandle);
+ out_free_app_data:
+ TSS_Delete(tssContext);
+ tpm2_delete(app_data);
+
+ err:
+ if (rc) {
+ if (rc == NOT_TPM_ERROR)
+ fprintf(stderr, "%s failed\n", reason);
+ else
+ tpm2_error(rc, reason);
+ rc = 1;
+ }
+ exit(rc);
+}
--
2.16.4

[PATCH 2/4] tpm2-common: export tpm key type from file to app_data

James Bottomley
 

This will be useful for sealed keys to know they were the correct type

Signed-off-by: James Bottomley <James.Bottomley@...>
---
tpm2-common.c | 19 +++++++++++--------
tpm2-common.h | 10 +++++++++-
2 files changed, 20 insertions(+), 9 deletions(-)

diff --git a/tpm2-common.c b/tpm2-common.c
index c60b151..95d5f7e 100644
--- a/tpm2-common.c
+++ b/tpm2-common.c
@@ -239,7 +239,7 @@ void tpm2_error(TPM_RC rc, const char *reason)
}


-TPM_RC tpm2_load_srk(TSS_CONTEXT *tssContext, TPM_HANDLE *h, const char *auth,TPM2B_PUBLIC *pub, TPM_HANDLE hierarchy, int version)
+TPM_RC tpm2_load_srk(TSS_CONTEXT *tssContext, TPM_HANDLE *h, const char *auth,TPM2B_PUBLIC *pub, TPM_HANDLE hierarchy, enum tpm2_type type)
{
TPM_RC rc;
CreatePrimary_In in;
@@ -271,7 +271,7 @@ TPM_RC tpm2_load_srk(TSS_CONTEXT *tssContext, TPM_HANDLE *h, const char *auth,TP
TPMA_OBJECT_USERWITHAUTH |
TPMA_OBJECT_DECRYPT |
TPMA_OBJECT_RESTRICTED;
- if (version)
+ if (type != TPM2_LEGACY)
in.inPublic.publicArea.objectAttributes.val |=
TPMA_OBJECT_FIXEDPARENT |
TPMA_OBJECT_FIXEDTPM;
@@ -1042,7 +1042,8 @@ int tpm2_load_engine_file(const char *filename, struct app_data **app_data,
INT32 size;
struct app_data *ad;
char oid[128];
- int empty_auth, version = 0;
+ int empty_auth;
+ enum tpm2_type tpm2_type = TPM2_NONE;
ASN1_OBJECT *type;
ASN1_INTEGER *parent;
ASN1_OCTET_STRING *pubkey;
@@ -1065,7 +1066,6 @@ int tpm2_load_engine_file(const char *filename, struct app_data **app_data,
tpk = ASN1_item_d2i_bio(ASN1_ITEM_rptr(TSSPRIVKEY), bf, NULL);
}
if (tpk) {
- version = 1;
type = tpk->type;
empty_auth = tpk->emptyAuth;
parent = tpk->parent;
@@ -1074,6 +1074,7 @@ int tpm2_load_engine_file(const char *filename, struct app_data **app_data,
policy = tpk->policy;
secret = tpk->secret;
} else {
+ tpm2_type = TPM2_LEGACY;
BIO_seek(bf, 0);
tssl = PEM_read_bio_TSSLOADABLE(bf, NULL, NULL, NULL);
if (!tssl) {
@@ -1100,12 +1101,13 @@ int tpm2_load_engine_file(const char *filename, struct app_data **app_data,
}

if (strcmp(OID_loadableKey, oid) == 0) {
- if (version != 1) {
+ if (tpm2_type != TPM2_NONE) {
fprintf(stderr, "New type found in old format key\n");
goto err;
}
+ tpm2_type = TPM2_LOADABLE;
} else if (strcmp(OID_OldloadableKey, oid) == 0) {
- if (version != 0) {
+ if (tpm2_type != TPM2_LEGACY) {
fprintf(stderr, "Old type found in new format key\n");
goto err;
}
@@ -1114,6 +1116,7 @@ int tpm2_load_engine_file(const char *filename, struct app_data **app_data,
fprintf(stderr, "Importable keys require an encrypted secret\n");
goto err;
}
+ tpm2_type = TPM2_IMPORTABLE;
} else {
fprintf(stderr, "Unrecognised object type\n");
goto err;
@@ -1133,7 +1136,7 @@ int tpm2_load_engine_file(const char *filename, struct app_data **app_data,

*app_data = ad;

- ad->version = version;
+ ad->type = tpm2_type;
ad->dir = tpm2_set_unique_tssdir();

if (parent)
@@ -1328,7 +1331,7 @@ TPM_HANDLE tpm2_load_key(TSS_CONTEXT **tsscp, struct app_data *app_data,
if ((app_data->parent & 0xff000000) == 0x81000000) {
in.parentHandle = app_data->parent;
} else {
- rc = tpm2_load_srk(tssContext, &in.parentHandle, srk_auth, NULL, app_data->parent, app_data->version);
+ rc = tpm2_load_srk(tssContext, &in.parentHandle, srk_auth, NULL, app_data->parent, app_data->type);
if (rc)
goto out;
}
diff --git a/tpm2-common.h b/tpm2-common.h
index 0e5d415..33cac4a 100644
--- a/tpm2-common.h
+++ b/tpm2-common.h
@@ -14,9 +14,17 @@ struct policy_command {
BYTE *policy;
};

+enum tpm2_type {
+ TPM2_NONE = -1, /* no defined type yet */
+ TPM2_LEGACY = 0,
+ TPM2_LOADABLE = 1,
+ TPM2_IMPORTABLE = 2,
+ TPM2_SEALED = 3,
+};
+
/* structure pointed to by the RSA object's app_data pointer */
struct app_data {
- int version;
+ enum tpm2_type type;
TPM_HANDLE parent;
/* if key is in NV memory */
TPM_HANDLE key;
--
2.16.4

[PATCH 1/4] tpm2-common.c: move several routines into common code

James Bottomley
 

These routines will be used by the seal/unseal command, so make them
common to facilitate this.

Signed-off-by: James Bottomley <James.Bottomley@...>
---
create_tpm2_key.c | 247 ++----------------------------------------------------
tpm2-common.c | 231 +++++++++++++++++++++++++++++++++++++++++++++++++-
tpm2-common.h | 13 +++
3 files changed, 250 insertions(+), 241 deletions(-)

diff --git a/create_tpm2_key.c b/create_tpm2_key.c
index 5534fe3..11b2086 100644
--- a/create_tpm2_key.c
+++ b/create_tpm2_key.c
@@ -12,14 +12,9 @@
#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>
@@ -37,10 +32,6 @@
#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)
-
#define OPT_DEPRECATED 0x1ff
#define OPT_RESTRICTED 0x1fe

@@ -121,31 +112,6 @@ openssl_print_errors()
ERR_print_errors_fp(stderr);
}

-/* from lib/hexdump.c (Linux kernel) */
-int hex_to_bin(char ch)
-{
- if ((ch >= '0') && (ch <= '9'))
- return ch - '0';
- ch = tolower(ch);
- if ((ch >= 'a') && (ch <= 'f'))
- return ch - 'a' + 10;
- return -1;
-}
-
-int hex2bin(unsigned char *dst, const char *src, size_t count)
-{
- while (count--) {
- int hi = hex_to_bin(*src++);
- int lo = hex_to_bin(*src++);
-
- if ((hi < 0) || (lo < 0))
- return -1;
-
- *dst++ = (hi << 4) | lo;
- }
- return 0;
-}
-
TPM_RC tpm2_ObjectPublic_GetName(TPM2B_NAME *name,
TPMT_PUBLIC *tpmtPublic)
{
@@ -414,167 +380,6 @@ TPM_RC tpm2_outerwrap(EVP_PKEY *parent,
return TPM_RC_ASYMMETRIC;
}

-TPM_RC
-parse_policy_file(const char *policy_file, STACK_OF(TSSOPTPOLICY) *sk,
- char *auth, TPMT_HA *digest)
-{
- struct stat st;
- char *data, *data_ptr;
- unsigned char buf[2048];
- unsigned char *buf_ptr;
- TSSOPTPOLICY *policy = NULL;
- INT32 buf_len;
- TPM_CC code;
- TPM_RC rc = NOT_TPM_ERROR;
- int fd, policy_auth_value = 0;
-
- if (stat(policy_file, &st) == -1) {
- fprintf(stderr, "File %s cannot be accessed\n", policy_file);
- return rc;
- }
-
- fd = open(policy_file, O_RDONLY);
- if (fd < 0) {
- fprintf(stderr, "File %s cannot be opened\n", policy_file);
- return rc;
- }
-
- data = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE,
- MAP_PRIVATE, fd, 0);
- if (!data) {
- fprintf(stderr, "mmap() failed\n");
- goto out;
- }
-
- while ((data_ptr = strsep(&data, "\n"))) {
- TPMT_HA hash_digest;
- unsigned char *hash = (unsigned char *)hash_digest.digest.tssmax;
- INT32 hash_len;
-
- buf_ptr = buf;
- buf_len = strlen(data_ptr) / 2;
- if (buf_len > sizeof(buf)) {
- rc = NOT_TPM_ERROR;
- fprintf(stderr, "line too long\n");
- goto out_munmap;
- }
-
- if (!buf_len)
- break;
-
- rc = hex2bin(buf, data_ptr, buf_len);
- if (rc < 0) {
- rc = NOT_TPM_ERROR;
- fprintf(stderr, "hex2bin() failed\n");
- goto out_munmap;
- }
-
- rc = TPM_CC_Unmarshal(&code, &buf_ptr, &buf_len);
- if (rc) {
- fprintf(stderr, "TPM_CC_Unmarshal() failed\n");
- goto out_munmap;
- }
-
- if (code == TPM_CC_PolicyCounterTimer) {
- /* for a countertimer, the policy is a hash of the hash */
- hash_digest.hashAlg = digest->hashAlg;
- hash_len = TSS_GetDigestSize(digest->hashAlg);
- TSS_Hash_Generate(&hash_digest, buf_len, buf_ptr, 0, NULL);
- hash = hash_digest.digest.tssmax;
- } else {
- hash = buf_ptr;
- hash_len = buf_len;
- }
-
- rc = TSS_Hash_Generate(digest,
- TSS_GetDigestSize(digest->hashAlg),
- (uint8_t *)&digest->digest,
- /* the command code */
- 4, buf_ptr - 4,
- hash_len, hash, 0, NULL);
- if (rc) {
- fprintf(stderr, "TSS_Hash_Generate() failed\n");
- goto out_munmap;
- }
-
- if (code == TPM_CC_PolicyAuthValue)
- policy_auth_value = 1;
-
- policy = TSSOPTPOLICY_new();
- ASN1_INTEGER_set(policy->CommandCode, code);
- ASN1_STRING_set(policy->CommandPolicy, buf_ptr, buf_len);
- sk_TSSOPTPOLICY_push(sk, policy);
- }
-
- if (auth && !policy_auth_value) {
- rc = NOT_TPM_ERROR;
- fprintf(stderr, "PolicyAuthValue command is required\n");
- }
-
-out_munmap:
- munmap(data, st.st_size);
-out:
- close(fd);
- return rc;
-}
-
-int
-openssl_write_tpmfile(const char *file, BYTE *pubkey, int pubkey_len,
- BYTE *privkey, int privkey_len, int empty_auth,
- TPM_HANDLE parent, STACK_OF(TSSOPTPOLICY) *sk,
- int version, TPM2B_ENCRYPTED_SECRET *secret)
-{
- union {
- TSSLOADABLE tssl;
- TSSPRIVKEY tpk;
- } k;
- BIO *outb;
-
- /* clear structure so as not to have to set optional parameters */
- memset(&k, 0, sizeof(k));
- if ((outb = BIO_new_file(file, "w")) == NULL) {
- fprintf(stderr, "Error opening file for write: %s\n", file);
- return 1;
- }
- if (version == 0) {
- k.tssl.type = OBJ_txt2obj(OID_OldloadableKey, 1);
- k.tssl.emptyAuth = empty_auth;
- k.tssl.parent = ASN1_INTEGER_new();
- ASN1_INTEGER_set(k.tssl.parent, parent);
-
- k.tssl.pubkey = ASN1_OCTET_STRING_new();
- ASN1_STRING_set(k.tssl.pubkey, pubkey, pubkey_len);
- k.tssl.privkey = ASN1_OCTET_STRING_new();
- ASN1_STRING_set(k.tssl.privkey, privkey, privkey_len);
- k.tssl.policy = sk;
-
- PEM_write_bio_TSSLOADABLE(outb, &k.tssl);
- } else {
- if (secret) {
- k.tpk.type = OBJ_txt2obj(OID_importableKey, 1);
- k.tpk.secret = ASN1_OCTET_STRING_new();
- ASN1_STRING_set(k.tpk.secret, secret->t.secret,
- secret->t.size);
- } else {
- k.tpk.type = OBJ_txt2obj(OID_loadableKey, 1);
- }
- k.tpk.emptyAuth = empty_auth;
- k.tpk.parent = ASN1_INTEGER_new();
- ASN1_INTEGER_set(k.tpk.parent, parent);
-
- k.tpk.pubkey = ASN1_OCTET_STRING_new();
- ASN1_STRING_set(k.tpk.pubkey, pubkey, pubkey_len);
- k.tpk.privkey = ASN1_OCTET_STRING_new();
- ASN1_STRING_set(k.tpk.privkey, privkey, privkey_len);
- k.tpk.policy = sk;
-
- PEM_write_bio_TSSPRIVKEY(outb, &k.tpk);
- }
-
- BIO_free(outb);
- return 0;
-}
-
EVP_PKEY *
openssl_read_key(char *filename)
{
@@ -901,44 +706,6 @@ static void list_curves(void)
exit(1);
}

-static TPM_HANDLE get_parent(const char *pstr)
-{
- TPM_HANDLE p;
-
- if (strcmp(pstr, "owner") == 0)
- p = TPM_RH_OWNER;
- else if (strcmp(pstr, "platform") == 0)
- p = TPM_RH_PLATFORM;
- else if (strcmp(pstr, "endorsement") == 0)
- p = TPM_RH_ENDORSEMENT;
- else if (strcmp(pstr, "null") == 0)
- p = TPM_RH_NULL;
- else
- p = strtoul(pstr, NULL, 16);
-
- if (((p & 0xff000000) == 0x40000000) &&
- (p == TPM_RH_OWNER ||
- p == TPM_RH_PLATFORM ||
- p == TPM_RH_ENDORSEMENT ||
- p == TPM_RH_NULL))
- return p;
- else if ((p & 0xff000000) == 0x81000000)
- return p;
-
- return 0;
-}
-
-void free_policy(STACK_OF(TSSOPTPOLICY) *sk)
-{
- TSSOPTPOLICY *policy;
-
- if (sk)
- while ((policy = sk_TSSOPTPOLICY_pop(sk)))
- TSSOPTPOLICY_free(policy);
-
- sk_TSSOPTPOLICY_free(sk);
-}
-
/*
* A restricted key needs a symmetric seed and algorithm so it can
* derive a symmetric encryption key used to protect the sensitive
@@ -1054,7 +821,7 @@ int main(int argc, char **argv)
}
break;
case 'p':
- parent = get_parent(optarg);
+ parent = tpm2_get_parent(optarg);
if (parent == 0) {
fprintf(stderr, "Invalid parent %s\n", optarg);
exit(1);
@@ -1159,7 +926,7 @@ int main(int argc, char **argv)
goto out_err;
}

- rc = parse_policy_file(policyFilename, sk, auth, &digest);
+ rc = tpm2_parse_policy_file(policyFilename, sk, auth, &digest);
if (rc) {
reason = "parse_policy_file";
goto out_free_policy;
@@ -1438,10 +1205,10 @@ int main(int argc, char **argv)
privkey_len = 0;
size = sizeof(privkey);
TSS_TPM2B_PRIVATE_Marshal(priv, &privkey_len, &buffer, &size);
- openssl_write_tpmfile(filename, pubkey, pubkey_len,
- privkey, privkey_len, auth == NULL, parent, sk,
- version, enc_secret);
- free_policy(sk);
+ tpm2_write_tpmfile(filename, pubkey, pubkey_len,
+ privkey, privkey_len, auth == NULL, parent, sk,
+ version, enc_secret);
+ tpm2_free_policy(sk);

exit(0);

@@ -1453,7 +1220,7 @@ int main(int argc, char **argv)
out_free_auth:
free(auth);
out_free_policy:
- free_policy(sk);
+ tpm2_free_policy(sk);
out_err:
if (rc == NOT_TPM_ERROR)
fprintf(stderr, "%s failed\n", reason);
diff --git a/tpm2-common.c b/tpm2-common.c
index 84b9093..c60b151 100644
--- a/tpm2-common.c
+++ b/tpm2-common.c
@@ -7,6 +7,12 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mman.h>

#include <openssl/asn1.h>
#include <openssl/evp.h>
@@ -23,8 +29,8 @@
#include TSSINCLUDE(tsscryptoh.h)
#include TSSINCLUDE(Unmarshal_fp.h)

-#include "tpm2-common.h"
#include "tpm2-asn.h"
+#include "tpm2-common.h"

struct myTPM2B {
UINT16 s;
@@ -1361,6 +1367,229 @@ void tpm2_unload_key(TSS_CONTEXT *tssContext, TPM_HANDLE key)
TSS_Delete(tssContext);
}

+TPM_HANDLE tpm2_get_parent(const char *pstr)
+{
+ TPM_HANDLE p;
+
+ if (strcmp(pstr, "owner") == 0)
+ p = TPM_RH_OWNER;
+ else if (strcmp(pstr, "platform") == 0)
+ p = TPM_RH_PLATFORM;
+ else if (strcmp(pstr, "endorsement") == 0)
+ p = TPM_RH_ENDORSEMENT;
+ else if (strcmp(pstr, "null") == 0)
+ p = TPM_RH_NULL;
+ else
+ p = strtoul(pstr, NULL, 16);
+
+ if (((p & 0xff000000) == 0x40000000) &&
+ (p == TPM_RH_OWNER ||
+ p == TPM_RH_PLATFORM ||
+ p == TPM_RH_ENDORSEMENT ||
+ p == TPM_RH_NULL))
+ return p;
+ else if ((p & 0xff000000) == 0x81000000)
+ return p;
+
+ return 0;
+}
+
+int tpm2_write_tpmfile(const char *file, BYTE *pubkey, int pubkey_len,
+ BYTE *privkey, int privkey_len, int empty_auth,
+ TPM_HANDLE parent, STACK_OF(TSSOPTPOLICY) *sk,
+ int version, TPM2B_ENCRYPTED_SECRET *secret)
+{
+ union {
+ TSSLOADABLE tssl;
+ TSSPRIVKEY tpk;
+ } k;
+ BIO *outb;
+
+ /* clear structure so as not to have to set optional parameters */
+ memset(&k, 0, sizeof(k));
+ if ((outb = BIO_new_file(file, "w")) == NULL) {
+ fprintf(stderr, "Error opening file for write: %s\n", file);
+ return 1;
+ }
+ if (version == 0) {
+ k.tssl.type = OBJ_txt2obj(OID_OldloadableKey, 1);
+ k.tssl.emptyAuth = empty_auth;
+ k.tssl.parent = ASN1_INTEGER_new();
+ ASN1_INTEGER_set(k.tssl.parent, parent);
+
+ k.tssl.pubkey = ASN1_OCTET_STRING_new();
+ ASN1_STRING_set(k.tssl.pubkey, pubkey, pubkey_len);
+ k.tssl.privkey = ASN1_OCTET_STRING_new();
+ ASN1_STRING_set(k.tssl.privkey, privkey, privkey_len);
+ k.tssl.policy = sk;
+
+ PEM_write_bio_TSSLOADABLE(outb, &k.tssl);
+ } else {
+ if (secret) {
+ k.tpk.type = OBJ_txt2obj(OID_importableKey, 1);
+ k.tpk.secret = ASN1_OCTET_STRING_new();
+ ASN1_STRING_set(k.tpk.secret, secret->t.secret,
+ secret->t.size);
+ } else {
+ k.tpk.type = OBJ_txt2obj(OID_loadableKey, 1);
+ }
+ k.tpk.emptyAuth = empty_auth;
+ k.tpk.parent = ASN1_INTEGER_new();
+ ASN1_INTEGER_set(k.tpk.parent, parent);
+
+ k.tpk.pubkey = ASN1_OCTET_STRING_new();
+ ASN1_STRING_set(k.tpk.pubkey, pubkey, pubkey_len);
+ k.tpk.privkey = ASN1_OCTET_STRING_new();
+ ASN1_STRING_set(k.tpk.privkey, privkey, privkey_len);
+ k.tpk.policy = sk;
+
+ PEM_write_bio_TSSPRIVKEY(outb, &k.tpk);
+ }
+
+ BIO_free(outb);
+ return 0;
+}
+
+/* from lib/hexdump.c (Linux kernel) */
+int hex_to_bin(char ch)
+{
+ if ((ch >= '0') && (ch <= '9'))
+ return ch - '0';
+ ch = tolower(ch);
+ if ((ch >= 'a') && (ch <= 'f'))
+ return ch - 'a' + 10;
+ return -1;
+}
+
+int hex2bin(unsigned char *dst, const char *src, size_t count)
+{
+ while (count--) {
+ int hi = hex_to_bin(*src++);
+ int lo = hex_to_bin(*src++);
+
+ if ((hi < 0) || (lo < 0))
+ return -1;
+
+ *dst++ = (hi << 4) | lo;
+ }
+ return 0;
+}
+
+TPM_RC tpm2_parse_policy_file(const char *policy_file,
+ STACK_OF(TSSOPTPOLICY) *sk,
+ char *auth, TPMT_HA *digest)
+{
+ struct stat st;
+ char *data, *data_ptr;
+ unsigned char buf[2048];
+ unsigned char *buf_ptr;
+ TSSOPTPOLICY *policy = NULL;
+ INT32 buf_len;
+ TPM_CC code;
+ TPM_RC rc = NOT_TPM_ERROR;
+ int fd, policy_auth_value = 0;
+
+ if (stat(policy_file, &st) == -1) {
+ fprintf(stderr, "File %s cannot be accessed\n", policy_file);
+ return rc;
+ }
+
+ fd = open(policy_file, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "File %s cannot be opened\n", policy_file);
+ return rc;
+ }
+
+ data = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE, fd, 0);
+ if (!data) {
+ fprintf(stderr, "mmap() failed\n");
+ goto out;
+ }
+
+ while ((data_ptr = strsep(&data, "\n"))) {
+ TPMT_HA hash_digest;
+ unsigned char *hash = (unsigned char *)hash_digest.digest.tssmax;
+ INT32 hash_len;
+
+ buf_ptr = buf;
+ buf_len = strlen(data_ptr) / 2;
+ if (buf_len > sizeof(buf)) {
+ rc = NOT_TPM_ERROR;
+ fprintf(stderr, "line too long\n");
+ goto out_munmap;
+ }
+
+ if (!buf_len)
+ break;
+
+ rc = hex2bin(buf, data_ptr, buf_len);
+ if (rc < 0) {
+ rc = NOT_TPM_ERROR;
+ fprintf(stderr, "hex2bin() failed\n");
+ goto out_munmap;
+ }
+
+ rc = TPM_CC_Unmarshal(&code, &buf_ptr, &buf_len);
+ if (rc) {
+ fprintf(stderr, "TPM_CC_Unmarshal() failed\n");
+ goto out_munmap;
+ }
+
+ if (code == TPM_CC_PolicyCounterTimer) {
+ /* for a countertimer, the policy is a hash of the hash */
+ hash_digest.hashAlg = digest->hashAlg;
+ hash_len = TSS_GetDigestSize(digest->hashAlg);
+ TSS_Hash_Generate(&hash_digest, buf_len, buf_ptr, 0, NULL);
+ hash = hash_digest.digest.tssmax;
+ } else {
+ hash = buf_ptr;
+ hash_len = buf_len;
+ }
+
+ rc = TSS_Hash_Generate(digest,
+ TSS_GetDigestSize(digest->hashAlg),
+ (uint8_t *)&digest->digest,
+ /* the command code */
+ 4, buf_ptr - 4,
+ hash_len, hash, 0, NULL);
+ if (rc) {
+ fprintf(stderr, "TSS_Hash_Generate() failed\n");
+ goto out_munmap;
+ }
+
+ if (code == TPM_CC_PolicyAuthValue)
+ policy_auth_value = 1;
+
+ policy = TSSOPTPOLICY_new();
+ ASN1_INTEGER_set(policy->CommandCode, code);
+ ASN1_STRING_set(policy->CommandPolicy, buf_ptr, buf_len);
+ sk_TSSOPTPOLICY_push(sk, policy);
+ }
+
+ if (auth && !policy_auth_value) {
+ rc = NOT_TPM_ERROR;
+ fprintf(stderr, "PolicyAuthValue command is required\n");
+ }
+
+out_munmap:
+ munmap(data, st.st_size);
+out:
+ close(fd);
+ return rc;
+}
+
+void tpm2_free_policy(STACK_OF(TSSOPTPOLICY) *sk)
+{
+ TSSOPTPOLICY *policy;
+
+ if (sk)
+ while ((policy = sk_TSSOPTPOLICY_pop(sk)))
+ TSSOPTPOLICY_free(policy);
+
+ sk_TSSOPTPOLICY_free(sk);
+}
+
IMPLEMENT_ASN1_FUNCTIONS(TSSOPTPOLICY)
IMPLEMENT_ASN1_FUNCTIONS(TSSLOADABLE)
IMPLEMENT_ASN1_FUNCTIONS(TSSPRIVKEY)
diff --git a/tpm2-common.h b/tpm2-common.h
index 536cedb..0e5d415 100644
--- a/tpm2-common.h
+++ b/tpm2-common.h
@@ -4,6 +4,10 @@
#define T2_AES_KEY_BITS 128
#define T2_AES_KEY_BYTES (T2_AES_KEY_BITS/8)

+/* 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)
+
struct policy_command {
TPM_CC code;
INT32 size;
@@ -63,4 +67,13 @@ TPM_HANDLE tpm2_load_key(TSS_CONTEXT **tsscp, struct app_data *app_data,
void tpm2_unload_key(TSS_CONTEXT *tssContext, TPM_HANDLE key);
void tpm2_delete(struct app_data *app_data);
char *tpm2_get_auth(UI_METHOD *ui, char *input_string, void *cb_data);
+TPM_HANDLE tpm2_get_parent(const char *pstr);
+int tpm2_write_tpmfile(const char *file, BYTE *pubkey, int pubkey_len,
+ BYTE *privkey, int privkey_len, int empty_auth,
+ TPM_HANDLE parent, STACK_OF(TSSOPTPOLICY) *sk,
+ int version, TPM2B_ENCRYPTED_SECRET *secret);
+TPM_RC tpm2_parse_policy_file(const char *policy_file,
+ STACK_OF(TSSOPTPOLICY) *sk,
+ char *auth, TPMT_HA *digest);
+void tpm2_free_policy(STACK_OF(TSSOPTPOLICY) *sk);
#endif
--
2.16.4

[PATCH 0/4] Add data sealing/unsealing

James Bottomley
 

This patch set just adds the sealing/unsealing functions. The
ultimate aim is to be able to use a sealed key for symmetric
encryption/decryption, but the engine code to do that is hard to
integrate because there's no ENGINE_load_key equivalent for the
symmetric operations, so more thought has to be given to how to
integrate this functionality into an engine.

The aim of this function is to demonstrate interoperability with the
Linux Kernel sealed key infastructure.

James

---

James Bottomley (4):
tpm2-common.c: move several routines into common code
tpm2-common: export tpm key type from file to app_data
Add TPM data sealing and unsealing functions
Add test for seal/unseal

.gitignore | 2 +
Makefile.am | 16 ++-
create_tpm2_key.c | 247 ++----------------------------------
e_tpm2-ecc.c | 2 +-
e_tpm2-rsa.c | 2 +-
load_tpm2_key.c | 2 +-
seal_tpm2_data.1.in | 18 +++
seal_tpm2_data.c | 337 ++++++++++++++++++++++++++++++++++++++++++++++++++
tests/Makefile.am | 3 +-
tests/seal_unseal.sh | 19 +++
tpm2-asn.h | 2 +-
tpm2-common.c | 266 +++++++++++++++++++++++++++++++++++++--
tpm2-common.h | 25 +++-
unseal_tpm2_data.1.in | 18 +++
unseal_tpm2_data.c | 206 ++++++++++++++++++++++++++++++
15 files changed, 902 insertions(+), 263 deletions(-)
create mode 100644 seal_tpm2_data.1.in
create mode 100644 seal_tpm2_data.c
create mode 100755 tests/seal_unseal.sh
create mode 100644 unseal_tpm2_data.1.in
create mode 100644 unseal_tpm2_data.c

--
2.16.4

locked Attendees List(New York, NY) - NRF Conference

Sara James <sara.james@...>
 

Hi,

I Hope you are doing well.

Just wanted to do quick follow up on my below email whether you are interested in Attendees_Mailing_list for your Marketing initiatives.

a.         Yes, Interested.
b.         Exclude.

Looking forward for your response.

Regards,
Sara James.

From: Sara James
Sent: 1/20/2020 14:07
To: openssl-tpm2-engine@groups.io
Subject: Attendees List(New York, NY) - NRF Conference

Greetings NRF Conference and Expo 2020 Exhibitors,

We are so pleased your company will be exhibiting at NRF Conference and Expo 2020.

List includes Email Address, Contact Number, Contact Name, Company Name, Job Title, Website URL on an excel spread sheet.

The list can be used for multi-channel marketing: Email campaigns, Direct mail marketing, Tele marketing, Social media campaign etc…

Please let me know your thoughts so that I can send you the Exhibitor costs for your Consideration.

Looking back to hearing from you.

Regards,
Sara James.

NRF Conference and Expo 2020 
Event Attendees Specialist.

New York, NY / 11 – 14 JAN 2020


[ANNOUNCE] openssl_tpm2_engine version 2.3.1 available

James Bottomley
 

The changes since 2.3.0 are

James Bottomley (7):
e_tpm2: simplify tpm2_load_key_core
tpm2-common: extract file and TPM loading functions
load_tpm2_key: use common file parsing and load routines
load_tpm2_key: add a test for loading importable keys
tpm2-common: make empty auth optional
tpm2-common: allow loading of DER keys
tests: add a test for DER keys

Jonathan Wernberg (1):
Fix compile warning on older GCC

The git repository is

https://git.kernel.org/pub/scm/linux/kernel/git/jejb/openssl_tpm2_engine.git/

The tarball release is:

https://git.kernel.org/pub/scm/linux/kernel/git/jejb/openssl_tpm2_engine.git/snapshot/openssl_tpm2_engine-2.3.1.tar.gz

And the openSUSE build service build of various distributions is here:

https://build.opensuse.org/package/show/home:jejb1:TPM/openssl_tpm2_engine

James

[PATCH 2/2] tests: add a test for DER keys

James Bottomley
 

Now that we're supposed to support DER based keys, check that we can
use them.

Signed-off-by: James Bottomley <James.Bottomley@...>
---
tests/Makefile.am | 3 ++-
tests/check_der.sh | 17 +++++++++++++++++
2 files changed, 19 insertions(+), 1 deletion(-)
create mode 100755 tests/check_der.sh

diff --git a/tests/Makefile.am b/tests/Makefile.am
index 21da53d..cd582e0 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -8,6 +8,7 @@ TESTS = fail_connect.sh \
wrap_tpm_key.sh \
check_wrap_auth.sh \
check_old_keys.sh \
+ check_der.sh \
create_ecc.sh \
wrap_ecc.sh \
wrap_generic_ecc.sh \
@@ -28,6 +29,6 @@ AM_TESTS_ENVIRONMENT = TPM_INTERFACE_TYPE=socsim; \
export TPM_INTERFACE_TYPE OPENSSL_CONF srcdir;
TEST_EXTENSIONS = .sh

-CLEANFILES = key*.tpm key*.pub key*.priv tmp.* NVChip h*.bin
+CLEANFILES = key*.tpm key*.pub key*.priv tmp.* NVChip h*.bin key*.der
clean-local:
rm -fr testdir
diff --git a/tests/check_der.sh b/tests/check_der.sh
new file mode 100755
index 0000000..0cbfe06
--- /dev/null
+++ b/tests/check_der.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+bindir=${srcdir}/..
+
+##
+# test is
+# 1. create TPM internal private key
+# 2. convert it to DER form
+# 3. get the corresponding public key from the engine
+# 4. encode a message using the TPM key
+# 5. verify the message through the public key
+${bindir}/create_tpm2_key -a -k passw0rd key2.tpm && \
+openssl asn1parse -in key2.tpm -out key2.der && \
+openssl rsa -engine tpm2 -inform engine -passin pass:passw0rd -in key2.der -pubout -out key2.pub && \
+echo "This is a message" | openssl rsautl -sign -engine tpm2 -engine tpm2 -keyform engine -inkey key2.der -passin pass:passw0rd -out tmp.msg && \
+openssl rsautl -verify -in tmp.msg -inkey key2.pub -pubin
+
--
2.16.4

[PATCH 1/2] tpm2-common: allow loading of DER keys

James Bottomley
 

One of the reasons for putting an OID at the beginning of the
structure was to make the binary DER form uniquely recognizable. This
should mean we're safe to try loading the binary form if the PEM form
fails. Activate that feature (for the new structure only).

Signed-off-by: James Bottomley <James.Bottomley@...>
---
tpm2-common.c | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/tpm2-common.c b/tpm2-common.c
index 763fccf..84b9093 100644
--- a/tpm2-common.c
+++ b/tpm2-common.c
@@ -8,6 +8,7 @@
#include <string.h>
#include <unistd.h>

+#include <openssl/asn1.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/ec.h>
@@ -1052,6 +1053,11 @@ int tpm2_load_engine_file(const char *filename, struct app_data **app_data,
}

tpk = PEM_read_bio_TSSPRIVKEY(bf, NULL, NULL, NULL);
+ if (!tpk) {
+ BIO_seek(bf, 0);
+ ERR_clear_error();
+ tpk = ASN1_item_d2i_bio(ASN1_ITEM_rptr(TSSPRIVKEY), bf, NULL);
+ }
if (tpk) {
version = 1;
type = tpk->type;
--
2.16.4

[PATCH 0/2] Add ability to parse DER format keys

James Bottomley
 

The current engine only parses PEM keys, because that was a
requirement for the original openssl_tpm_engine. Since the DER was
simply a sequence of an octet string, there was no way of recognizing
the binary form. However, for TPM2 keys, we introduced the initial
OID, which should provide a unique binary signature for the key, so we
should be able to parse both DER and PEM without actually knowing in
advance what the key format is, so add the ability to fall back to DER
if PEM fails to enable this.

James

--

James Bottomley (2):
tpm2-common: allow loading of DER keys
tests: add a test for DER keys

tests/Makefile.am | 3 ++-
tests/check_der.sh | 17 +++++++++++++++++
tpm2-common.c | 6 ++++++
3 files changed, 25 insertions(+), 1 deletion(-)
create mode 100755 tests/check_der.sh

--
2.16.4

[PATCH] tpm2-common: make empty auth optional

James Bottomley
 

The ASN.1 spec lists it as optional and if it's not present it should
be treated as boolean false (meaning the key needs authorization).

Signed-off-by: James Bottomley
---
tpm2-common.c | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/tpm2-common.c b/tpm2-common.c
index 891a603..763fccf 100644
--- a/tpm2-common.c
+++ b/tpm2-common.c
@@ -1107,6 +1107,10 @@ int tpm2_load_engine_file(const char *filename, struct app_data **app_data,
goto err;
}

+ if (empty_auth == -1)
+ /* not present means auth is not empty */
+ empty_auth = 0;
+
ad = OPENSSL_malloc(sizeof(*ad));

if (!ad) {
--
2.16.4

[PATCH 4/4] load_tpm2_key: add a test for loading importable keys

James Bottomley
 

Now that the file and key loading is done in common code, test that
loading of importable keys actually works.

Signed-off-by: James Bottomley <James.Bottomley@...>
---
tests/check_importable.sh | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/tests/check_importable.sh b/tests/check_importable.sh
index 36ebe01..9725872 100755
--- a/tests/check_importable.sh
+++ b/tests/check_importable.sh
@@ -12,6 +12,12 @@ ${bindir}/create_tpm2_key --import srk.pub --wrap key.priv -a -k passw0rd key.tp
openssl req -new -x509 -subj '/CN=test/' -key key.tpm -passin pass:passw0rd -engine tpm2 -keyform engine -out tmp.crt || exit 1
openssl verify -CAfile tmp.crt -check_ss_sig tmp.crt || exit 1

+# Check the loadability of an importable key
+NV=81000201
+${bindir}/load_tpm2_key key.tpm ${NV} || exit 1
+openssl req -new -x509 -subj '/CN=test/' -key //nvkey:${NV} -passin pass:passw0rd -engine tpm2 -keyform engine -out tmp.crt || exit 1
+openssl verify -CAfile tmp.crt -check_ss_sig tmp.crt || exit 1
+
#check an RSA key with a cert and policy
openssl genrsa 2048 > key.priv || exit 1
${bindir}/create_tpm2_key --import srk.pub --wrap key.priv -a -k passw0rd -c policies/policy_authvalue.txt key.tpm || exit 1
--
2.16.4

[PATCH 3/4] load_tpm2_key: use common file parsing and load routines

James Bottomley
 

This allows load_tpm2_key to use every format currently allowed for
the tpm keys. For load_tpm2_key this means it can now load importable
keys.

Signed-off-by: James Bottomley <James.Bottomley@...>
---
e_tpm2.c | 2 +-
load_tpm2_key.c | 87 +++++++++++----------------------------------------------
tpm2-common.c | 17 ++++++-----
tpm2-common.h | 2 +-
4 files changed, 29 insertions(+), 79 deletions(-)

diff --git a/e_tpm2.c b/e_tpm2.c
index ac0d6ba..3140a25 100644
--- a/e_tpm2.c
+++ b/e_tpm2.c
@@ -221,7 +221,7 @@ static int tpm2_engine_load_key_core(ENGINE *e, EVP_PKEY **ppkey,
}

rc = tpm2_load_engine_file(key_id, &app_data, &pkey, ui, cb_data,
- srk_auth);
+ srk_auth, 1);
if (!rc)
return 0;

diff --git a/load_tpm2_key.c b/load_tpm2_key.c
index 123cf9f..244c9dc 100644
--- a/load_tpm2_key.c
+++ b/load_tpm2_key.c
@@ -76,21 +76,14 @@ 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;
+ struct app_data *app_data;

while (1) {
option_index = 0;
@@ -140,72 +133,30 @@ int main(int argc, char **argv)
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) {
+ ret = tpm2_load_engine_file(filename, &app_data, NULL, NULL, NULL,
+ auth, 0);
+ if (!ret) {
fprintf(stderr, "Failed to parse file %s\n", filename);
exit(1);
}
- if (tpk->policy && !force) {
+ if (app_data->commands && !force) {
+ fprintf(stderr, "NUM COMMANDS=%d\n", app_data->num_commands);
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);
+ ret = 1;
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)
+ ret = tpm2_load_key(&tssContext, app_data, auth);
+ if (!ret) {
+ ret = 1;
goto out;
+ };

ein.auth = TPM_RH_OWNER;
- ein.objectHandle = lout.objectHandle;
+ ein.objectHandle = ret;
+ ret = 1; /* set up error return */
ein.persistentHandle = nvindex;
rc = TSS_Execute(tssContext,
NULL,
@@ -219,16 +170,12 @@ int main(int argc, char **argv)
else
ret = 0;

- tpm2_flush_handle(tssContext, lout.objectHandle);
+ tpm2_flush_handle(tssContext, ein.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);
+ tpm2_rm_keyfile(app_data->dir, nvindex);
+ tpm2_delete(app_data);
+ exit(ret);
}
diff --git a/tpm2-common.c b/tpm2-common.c
index a585b6a..891a603 100644
--- a/tpm2-common.c
+++ b/tpm2-common.c
@@ -1026,7 +1026,7 @@ static int tpm2_engine_load_key_policy(struct app_data *app_data,

int tpm2_load_engine_file(const char *filename, struct app_data **app_data,
EVP_PKEY **ppkey, UI_METHOD *ui, void *cb_data,
- const char *srk_auth)
+ const char *srk_auth, int get_key_auth)
{
BIO *bf;
TSSLOADABLE *tssl = NULL;
@@ -1220,13 +1220,15 @@ int tpm2_load_engine_file(const char *filename, struct app_data **app_data,
}

/* create the new objects to return */
- *ppkey = tpm2_to_openssl_public(&iin.objectPublic.publicArea);
- if (!*ppkey) {
- fprintf(stderr, "Failed to allocate a new EVP_KEY\n");
- goto err_free;
+ if (ppkey) {
+ *ppkey = tpm2_to_openssl_public(&iin.objectPublic.publicArea);
+ if (!*ppkey) {
+ fprintf(stderr, "Failed to allocate a new EVP_KEY\n");
+ goto err_free;
+ }
}

- if (empty_auth == 0) {
+ if (empty_auth == 0 && get_key_auth) {
ad->auth = tpm2_get_auth(ui, "TPM Key Password: ", cb_data);
if (!ad->auth)
goto err_free_key;
@@ -1244,7 +1246,8 @@ int tpm2_load_engine_file(const char *filename, struct app_data **app_data,

return 1;
err_free_key:
- EVP_PKEY_free(*ppkey);
+ if (ppkey)
+ EVP_PKEY_free(*ppkey);
err_free:
*ppkey = NULL;

diff --git a/tpm2-common.h b/tpm2-common.h
index 264fc8e..536cedb 100644
--- a/tpm2-common.h
+++ b/tpm2-common.h
@@ -57,7 +57,7 @@ int tpm2_get_public_point(TPM2B_ECC_POINT *tpmpt, const EC_GROUP *group,
const EC_POINT *pt);
int tpm2_load_engine_file(const char *filename, struct app_data **app_data,
EVP_PKEY **ppkey, UI_METHOD *ui, void *cb_data,
- const char *srk_auth);
+ const char *srk_auth, int get_key_auth);
TPM_HANDLE tpm2_load_key(TSS_CONTEXT **tsscp, struct app_data *app_data,
const char *srk_auth);
void tpm2_unload_key(TSS_CONTEXT *tssContext, TPM_HANDLE key);
--
2.16.4

[PATCH 2/4] tpm2-common: extract file and TPM loading functions

James Bottomley
 

The functions needed to load the key file and load a key into the TPM
are extracted from create_tpm2_key in preparation for re-used in
load_tpm2_key.

Signed-off-by: James Bottomley <James.Bottomley@...>
---
e_tpm2-ecc.c | 2 +-
e_tpm2-rsa.c | 2 +-
e_tpm2.c | 429 +----------------------------------------------------
e_tpm2.h | 23 +--
tpm2-asn.h | 41 +-----
tpm2-common.c | 467 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
tpm2-common.h | 27 ++++
7 files changed, 510 insertions(+), 481 deletions(-)

diff --git a/e_tpm2-ecc.c b/e_tpm2-ecc.c
index 21a636c..89e3762 100644
--- a/e_tpm2-ecc.c
+++ b/e_tpm2-ecc.c
@@ -87,7 +87,7 @@ static TPM_HANDLE tpm2_load_key_from_ecc(const EC_KEY *eck,
*num_commands = app_data->num_commands;
*nameAlg = app_data->name_alg;

- return tpm2_load_key(tssContext, app_data);
+ return tpm2_load_key(tssContext, app_data, srk_auth);
}

void tpm2_bind_key_to_engine_ecc(EVP_PKEY *pkey, void *data)
diff --git a/e_tpm2-rsa.c b/e_tpm2-rsa.c
index fe82bc6..85be4a8 100644
--- a/e_tpm2-rsa.c
+++ b/e_tpm2-rsa.c
@@ -119,7 +119,7 @@ static TPM_HANDLE tpm2_load_key_from_rsa(RSA *rsa, TSS_CONTEXT **tssContext,
*num_commands = app_data->num_commands;
*nameAlg = app_data->name_alg;

- return tpm2_load_key(tssContext, app_data);
+ return tpm2_load_key(tssContext, app_data, srk_auth);
}

void tpm2_bind_key_to_engine_rsa(EVP_PKEY *pkey, void *data)
diff --git a/e_tpm2.c b/e_tpm2.c
index 1523e1f..ac0d6ba 100644
--- a/e_tpm2.c
+++ b/e_tpm2.c
@@ -22,11 +22,10 @@
#include TSSINCLUDE(tssresponsecode.h)
#include TSSINCLUDE(Unmarshal_fp.h)

-#include "tpm2-asn.h"
#include "tpm2-common.h"
#include "e_tpm2.h"

-static char *srk_auth;
+char *srk_auth = NULL;
static char *nvprefix;

static int tpm2_engine_init(ENGINE * e)
@@ -99,73 +98,6 @@ static const ENGINE_CMD_DEFN tpm2_cmd_defns[] = {
{0, NULL, NULL, 0}
};

-static char *tpm2_get_auth_ui(UI_METHOD *ui_method, char *prompt, void *cb_data)
-{
- UI *ui = UI_new();
- /* Max auth size is name algorithm hash length, so this
- * is way bigger than necessary */
- char auth[256], *ret = NULL;
- int len;
-
- if (ui_method)
- UI_set_method(ui, ui_method);
-
- UI_add_user_data(ui, cb_data);
-
- if (UI_add_input_string(ui, prompt, UI_INPUT_FLAG_DEFAULT_PWD,
- auth, 0, sizeof(auth)) == 0) {
- fprintf(stderr, "UI_add_input_string failed\n");
- goto out;
- }
-
- if (UI_process(ui)) {
- fprintf(stderr, "UI_process failed\n");
- goto out;
- }
-
- len = strlen(auth);
- ret = OPENSSL_malloc(len + 1);
- if (!ret)
- goto out;
-
- strcpy(ret, auth);
-
- out:
- UI_free(ui);
-
- return ret;
-}
-
-static char *tpm2_get_auth_pem(char *input_string, void *cb_data)
-{
- char auth[256], *ret;
- int len;
-
- EVP_set_pw_prompt(input_string);
-
- PEM_def_callback(auth, sizeof(auth), 0, cb_data);
- EVP_set_pw_prompt(NULL);
-
- len = strlen(auth);
- ret = OPENSSL_malloc(len + 1);
- if (!ret)
- goto out;
-
- strcpy(ret, auth);
-
- out:
- return ret;
-}
-
-static char *tpm2_get_auth(UI_METHOD *ui, char *input_string,
- void *cb_data)
-{
- if (ui)
- return tpm2_get_auth_ui(ui, input_string, cb_data);
- else
- return tpm2_get_auth_pem(input_string, cb_data);
-}
-
void tpm2_bind_key_to_engine(EVP_PKEY *pkey, void *data)
{
switch (EVP_PKEY_id(pkey)) {
@@ -263,67 +195,14 @@ static int tpm2_engine_load_nvkey(ENGINE *e, EVP_PKEY **ppkey,
return 0;
}

-static int tpm2_engine_load_key_policy(struct app_data *app_data,
- STACK_OF(TSSOPTPOLICY) *st_policy)
-{
- struct policy_command *command;
- TSSOPTPOLICY *policy;
- int i, commands_len;
-
- app_data->num_commands = sk_TSSOPTPOLICY_num(st_policy);
- if (app_data->num_commands <= 0)
- return 1;
-
- commands_len = sizeof(struct policy_command) * app_data->num_commands;
- app_data->commands = OPENSSL_malloc(commands_len);
- if (!app_data->commands)
- return 0;
-
- for (i = 0; i < app_data->num_commands; i++) {
- policy = sk_TSSOPTPOLICY_value(st_policy, i);
- if (!policy)
- return 0;
-
- command = app_data->commands + i;
- command->code = ASN1_INTEGER_get(policy->CommandCode);
- command->size = policy->CommandPolicy->length;
- command->policy = NULL;
-
- if (!command->size)
- continue;
-
- command->policy = OPENSSL_malloc(command->size);
- if (!command->policy)
- return 0;
-
- memcpy(command->policy, policy->CommandPolicy->data,
- command->size);
- }
-
- return 1;
-}
-
static int tpm2_engine_load_key_core(ENGINE *e, EVP_PKEY **ppkey,
const char *key_id, UI_METHOD *ui,
void *cb_data)
{
EVP_PKEY *pkey;
- BIO *bf;
- TSSLOADABLE *tssl = NULL;
- TSSPRIVKEY *tpk = NULL;
- BYTE *buffer;
- INT32 size;
- struct app_data *app_data;
- char oid[128];
- int empty_auth, version = 0;
const int nvkey_len = strlen(nvprefix);
- ASN1_OBJECT *type;
- ASN1_INTEGER *parent;
- ASN1_OCTET_STRING *pubkey;
- STACK_OF(TSSOPTPOLICY) *policy;
- ASN1_OCTET_STRING *privkey;
- ASN1_OCTET_STRING *secret = NULL;
- Import_In iin;
+ struct app_data *app_data;
+ int rc;

if (!key_id) {
fprintf(stderr, "key_id is NULL\n");
@@ -341,221 +220,16 @@ static int tpm2_engine_load_key_core(ENGINE *e, EVP_PKEY **ppkey,
return tpm2_engine_load_nvkey(e, ppkey, key, ui, cb_data);
}

- bf = BIO_new_file(key_id, "r");
- if (!bf) {
- fprintf(stderr, "File %s does not exist or cannot be read\n", key_id);
+ rc = tpm2_load_engine_file(key_id, &app_data, &pkey, ui, cb_data,
+ srk_auth);
+ if (!rc)
return 0;
- }
-
- tpk = PEM_read_bio_TSSPRIVKEY(bf, NULL, NULL, NULL);
- if (tpk) {
- version = 1;
- type = tpk->type;
- empty_auth = tpk->emptyAuth;
- parent = tpk->parent;
- pubkey = tpk->pubkey;
- privkey = tpk->privkey;
- policy = tpk->policy;
- secret = tpk->secret;
- } else {
- BIO_seek(bf, 0);
- tssl = PEM_read_bio_TSSLOADABLE(bf, NULL, NULL, NULL);
- if (!tssl) {
- BIO_free(bf);
- if (ppkey)
- fprintf(stderr, "Failed to parse file %s\n", key_id);
- return 0;
- }
-
- /* have error from failed TSSPRIVKEY load */
- ERR_clear_error();
- type = tssl->type;
- empty_auth = tssl->emptyAuth;
- parent = tssl->parent;
- pubkey = tssl->pubkey;
- privkey = tssl->privkey;
- policy = tssl->policy;
- }
-
- BIO_free(bf);
-
- if (!ppkey) {
- TSSLOADABLE_free(tssl);
- TSSPRIVKEY_free(tpk);
- return 1;
- }
-
- if (OBJ_obj2txt(oid, sizeof(oid), type, 1) == 0) {
- fprintf(stderr, "Failed to parse object type\n");
- goto err;
- }
-
- if (strcmp(OID_loadableKey, oid) == 0) {
- if (version != 1) {
- fprintf(stderr, "New type found in old format key\n");
- goto err;
- }
- } else if (strcmp(OID_OldloadableKey, oid) == 0) {
- if (version != 0) {
- fprintf(stderr, "Old type found in new format key\n");
- goto err;
- }
- } else if (strcmp(OID_importableKey, oid) == 0) {
- if (!secret) {
- fprintf(stderr, "Importable keys require an encrypted secret\n");
- goto err;
- }
- } else {
- fprintf(stderr, "Unrecognised object type\n");
- goto err;
- }
-
- app_data = OPENSSL_malloc(sizeof(*app_data));
-
- if (!app_data) {
- fprintf(stderr, "Failed to allocate app_data\n");
- goto err;
- }
- memset(app_data, 0, sizeof(*app_data));
-
- app_data->version = version;
- app_data->dir = tpm2_set_unique_tssdir();
-
- if (parent)
- app_data->parent = ASN1_INTEGER_get(parent);
- else
- /* older keys have absent parent */
- app_data->parent = TPM_RH_OWNER;
-
- app_data->pub = OPENSSL_malloc(pubkey->length);
- if (!app_data->pub)
- goto err_free;
- app_data->pub_len = pubkey->length;
- memcpy(app_data->pub, pubkey->data, app_data->pub_len);
-
- buffer = app_data->pub;
- size = app_data->pub_len;
- TPM2B_PUBLIC_Unmarshal(&iin.objectPublic, &buffer, &size, FALSE);
- app_data->name_alg = iin.objectPublic.publicArea.nameAlg;
-
- if (strcmp(OID_importableKey, oid) == 0) {
- TPM_HANDLE session;
- TSS_CONTEXT *tssContext;
- TPM_RC rc;
- const char *reason;
- TPM2B_PRIVATE priv_2b;
- BYTE *buf;
- UINT16 written;
- INT32 size;
- Import_Out iout;
-
- rc = tpm2_create(&tssContext, app_data->dir);
- if (rc) {
- reason="tpm2_create";
- goto import_err;
- }
-
- if ((app_data->parent & 0xff000000) == 0x40000000) {
- tpm2_load_srk(tssContext, &iin.parentHandle,
- srk_auth, NULL, app_data->parent, 1);
- } else {
- iin.parentHandle = app_data->parent;
- }
- rc = tpm2_get_session_handle(tssContext, &session,
- iin.parentHandle,
- TPM_SE_HMAC,
- iin.objectPublic.publicArea.nameAlg);
- if (rc) {
- reason="tpm2_get_session_handle";
- goto import_err;
- }
-
- /* no inner encryption */
- iin.encryptionKey.t.size = 0;
- iin.symmetricAlg.algorithm = TPM_ALG_NULL;
-
- /* for importable keys the private key is actually the
- * outer wrapped duplicate structure */
- buffer = privkey->data;
- size = privkey->length;
- TPM2B_PRIVATE_Unmarshal(&iin.duplicate, &buffer, &size);
-
- buffer = secret->data;
- size = secret->length;
- TPM2B_ENCRYPTED_SECRET_Unmarshal(&iin.inSymSeed, &buffer, &size);
- rc = TSS_Execute(tssContext,
- (RESPONSE_PARAMETERS *)&iout,
- (COMMAND_PARAMETERS *)&iin,
- NULL,
- TPM_CC_Import,
- session, srk_auth, 0,
- TPM_RH_NULL, NULL, 0);
- if (rc)
- tpm2_flush_handle(tssContext, session);
- reason = "TPM2_Import";
-
- import_err:
- tpm2_flush_srk(tssContext, iin.parentHandle);
- TSS_Delete(tssContext);
- if (rc) {
- tpm2_error(rc, reason);
- goto err_free;
- }
- buf = priv_2b.t.buffer;
- size = sizeof(priv_2b.t.buffer);
- written = 0;
- TSS_TPM2B_PRIVATE_Marshal(&iout.outPrivate, &written,
- &buf, &size);
- app_data->priv = OPENSSL_malloc(written);
- if (!app_data->priv)
- goto err_free;
- app_data->priv_len = written;
- memcpy(app_data->priv, priv_2b.t.buffer, written);
- } else {
- app_data->priv = OPENSSL_malloc(privkey->length);
- if (!app_data->priv)
- goto err_free;
-
- app_data->priv_len = privkey->length;
- memcpy(app_data->priv, privkey->data, app_data->priv_len);
- }
-
- /* create the new objects to return */
- pkey = tpm2_to_openssl_public(&iin.objectPublic.publicArea);
- if (!pkey) {
- fprintf(stderr, "Failed to allocate a new EVP_KEY\n");
- goto err_free;
- }
-
- if (empty_auth == 0) {
- app_data->auth = tpm2_get_auth(ui, "TPM Key Password: ", cb_data);
- if (!app_data->auth)
- goto err_free_key;
- }
-
- if (!(iin.objectPublic.publicArea.objectAttributes.val &
- TPMA_OBJECT_USERWITHAUTH))
- app_data->req_policy_session = 1;
-
- if (!tpm2_engine_load_key_policy(app_data, policy))
- goto err_free_key;
-
- TSSLOADABLE_free(tssl);
- TSSPRIVKEY_free(tpk);

tpm2_bind_key_to_engine(pkey, app_data);

*ppkey = pkey;
return 1;

- err_free_key:
- EVP_PKEY_free(pkey);
- err_free:
- tpm2_delete(app_data);
- err:
- TSSLOADABLE_free(tssl);
-
- return 0;
}

static EVP_PKEY *tpm2_engine_load_key(ENGINE *e, const char *key_id,
@@ -611,96 +285,5 @@ static int tpm2_bind_fn(ENGINE * e, const char *id)
return 1;
}

-TPM_HANDLE tpm2_load_key(TSS_CONTEXT **tsscp, struct app_data *app_data)
-{
- TSS_CONTEXT *tssContext;
- Load_In in;
- Load_Out out;
- TPM_HANDLE key = 0;
- TPM_RC rc;
- BYTE *buffer;
- INT32 size;
- TPM_HANDLE session;
-
- rc = tpm2_create(&tssContext, app_data->dir);
- if (rc)
- return 0;
-
- if (app_data->key) {
- key = app_data->key;
- goto out;
- }
-
- buffer = app_data->priv;
- size = app_data->priv_len;
- TPM2B_PRIVATE_Unmarshal(&in.inPrivate, &buffer, &size);
-
- buffer = app_data->pub;
- size = app_data->pub_len;
- TPM2B_PUBLIC_Unmarshal(&in.inPublic, &buffer, &size, FALSE);
-
- if ((app_data->parent & 0xff000000) == 0x81000000) {
- in.parentHandle = app_data->parent;
- } else {
- rc = tpm2_load_srk(tssContext, &in.parentHandle, srk_auth, NULL, app_data->parent, app_data->version);
- if (rc)
- goto out;
- }
- rc = tpm2_get_session_handle(tssContext, &session, in.parentHandle,
- TPM_SE_HMAC, app_data->name_alg);
- if (rc)
- goto out_flush_srk;
- rc = TSS_Execute(tssContext,
- (RESPONSE_PARAMETERS *)&out,
- (COMMAND_PARAMETERS *)&in,
- NULL,
- TPM_CC_Load,
- session, srk_auth, 0,
- TPM_RH_NULL, NULL, 0);
- if (rc) {
- tpm2_error(rc, "TPM2_Load");
- tpm2_flush_handle(tssContext, session);
- }
- else
- key = out.objectHandle;
-
- out_flush_srk:
- tpm2_flush_srk(tssContext, in.parentHandle);
- out:
- if (!key)
- TSS_Delete(tssContext);
- else
- *tsscp = tssContext;
- return key;
-}
-
-void tpm2_unload_key(TSS_CONTEXT *tssContext, TPM_HANDLE key)
-{
- tpm2_flush_handle(tssContext, key);
-
- TSS_Delete(tssContext);
-}
-
-void tpm2_delete(struct app_data *app_data)
-{
- int i;
-
- for (i = 0; i < app_data->num_commands; i++)
- OPENSSL_free(app_data->commands[i].policy);
-
- OPENSSL_free(app_data->commands);
- OPENSSL_free(app_data->priv);
- OPENSSL_free(app_data->pub);
-
- tpm2_rm_keyfile(app_data->dir, app_data->parent);
- /* if key was nv key, flush may not have removed file */
- tpm2_rm_keyfile(app_data->dir, app_data->key);
- tpm2_rm_tssdir(app_data->dir);
-
- OPENSSL_free((void *)app_data->dir);
-
- OPENSSL_free(app_data);
-}
-
IMPLEMENT_DYNAMIC_CHECK_FN()
IMPLEMENT_DYNAMIC_BIND_FN(tpm2_bind_fn)
diff --git a/e_tpm2.h b/e_tpm2.h
index 5e843d2..a720acc 100644
--- a/e_tpm2.h
+++ b/e_tpm2.h
@@ -6,27 +6,6 @@

#define TPM2_ENGINE_EX_DATA_UNINIT -1

-/* structure pointed to by the RSA object's app_data pointer */
-struct app_data {
- int version;
- TPM_HANDLE parent;
- /* if key is in NV memory */
- TPM_HANDLE key;
- /* otherwise key is specified by blobs */
- void *priv;
- int priv_len;
- void *pub;
- int pub_len;
- char *auth;
- const char *dir;
- int req_policy_session;
- int num_commands;
- unsigned int name_alg;
- struct policy_command *commands;
-};
-
-TPM_HANDLE tpm2_load_key(TSS_CONTEXT **tsscp, struct app_data *app_data);
-void tpm2_unload_key(TSS_CONTEXT *tssContext, TPM_HANDLE key);
-void tpm2_delete(struct app_data *app_data);
+extern char *srk_auth;

#endif
diff --git a/tpm2-asn.h b/tpm2-asn.h
index 82241be..9afa7c3 100644
--- a/tpm2-asn.h
+++ b/tpm2-asn.h
@@ -24,13 +24,6 @@ typedef struct {
ASN1_OCTET_STRING *CommandPolicy;
} TSSOPTPOLICY;

-ASN1_SEQUENCE(TSSOPTPOLICY) = {
- ASN1_EXP(TSSOPTPOLICY, CommandCode, ASN1_INTEGER, 0),
- ASN1_EXP(TSSOPTPOLICY, CommandPolicy, ASN1_OCTET_STRING, 1)
-} ASN1_SEQUENCE_END(TSSOPTPOLICY)
-
-IMPLEMENT_ASN1_FUNCTIONS(TSSOPTPOLICY);
-
#if OPENSSL_VERSION_NUMBER < 0x10100000
#define sk_TSSOPTPOLICY_new_null() SKM_sk_new_null(TSSOPTPOLICY)
#define sk_TSSOPTPOLICY_push(sk, policy) SKM_sk_push(TSSOPTPOLICY, sk, policy)
@@ -107,36 +100,16 @@ typedef struct {
#define OID_importableKey "2.23.133.10.1.4"


-ASN1_SEQUENCE(TSSLOADABLE) = {
- ASN1_SIMPLE(TSSLOADABLE, type, ASN1_OBJECT),
- ASN1_EXP_OPT(TSSLOADABLE, emptyAuth, ASN1_BOOLEAN, 0),
- ASN1_EXP_OPT(TSSLOADABLE, parent, ASN1_INTEGER, 1),
- ASN1_EXP_OPT(TSSLOADABLE, pubkey, ASN1_OCTET_STRING, 2),
- ASN1_EXP_SEQUENCE_OF_OPT(TSSLOADABLE, policy, TSSOPTPOLICY, 3),
- ASN1_SIMPLE(TSSLOADABLE, privkey, ASN1_OCTET_STRING)
-} ASN1_SEQUENCE_END(TSSLOADABLE)
-
-IMPLEMENT_ASN1_FUNCTIONS(TSSLOADABLE);
-
-ASN1_SEQUENCE(TSSPRIVKEY) = {
- ASN1_SIMPLE(TSSPRIVKEY, type, ASN1_OBJECT),
- ASN1_EXP_OPT(TSSPRIVKEY, emptyAuth, ASN1_BOOLEAN, 0),
- ASN1_EXP_SEQUENCE_OF_OPT(TSSPRIVKEY, policy, TSSOPTPOLICY, 1),
- ASN1_EXP_OPT(TSSPRIVKEY, secret, ASN1_OCTET_STRING, 2),
- ASN1_SIMPLE(TSSPRIVKEY, parent, ASN1_INTEGER),
- ASN1_SIMPLE(TSSPRIVKEY, pubkey, ASN1_OCTET_STRING),
- ASN1_SIMPLE(TSSPRIVKEY, privkey, ASN1_OCTET_STRING)
-} ASN1_SEQUENCE_END(TSSPRIVKEY)
-
-IMPLEMENT_ASN1_FUNCTIONS(TSSPRIVKEY);
-
/* This is the PEM guard tag */
#define TSSLOADABLE_PEM_STRING "TSS2 KEY BLOB"
#define TSSPRIVKEY_PEM_STRING "TSS2 PRIVATE KEY"

-static IMPLEMENT_PEM_write_bio(TSSLOADABLE, TSSLOADABLE, TSSLOADABLE_PEM_STRING, TSSLOADABLE)
-static IMPLEMENT_PEM_read_bio(TSSLOADABLE, TSSLOADABLE, TSSLOADABLE_PEM_STRING, TSSLOADABLE)
-static IMPLEMENT_PEM_write_bio(TSSPRIVKEY, TSSPRIVKEY, TSSPRIVKEY_PEM_STRING, TSSPRIVKEY)
-static IMPLEMENT_PEM_read_bio(TSSPRIVKEY, TSSPRIVKEY, TSSPRIVKEY_PEM_STRING, TSSPRIVKEY)
+DECLARE_ASN1_FUNCTIONS(TSSOPTPOLICY);
+DECLARE_ASN1_FUNCTIONS(TSSLOADABLE);
+DECLARE_ASN1_FUNCTIONS(TSSPRIVKEY);
+DECLARE_PEM_write_bio(TSSLOADABLE, TSSLOADABLE);
+DECLARE_PEM_read_bio(TSSLOADABLE, TSSLOADABLE);
+DECLARE_PEM_write_bio(TSSPRIVKEY, TSSPRIVKEY);
+DECLARE_PEM_read_bio(TSSPRIVKEY, TSSPRIVKEY);

#endif
diff --git a/tpm2-common.c b/tpm2-common.c
index 1152777..a585b6a 100644
--- a/tpm2-common.c
+++ b/tpm2-common.c
@@ -11,6 +11,9 @@
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/ec.h>
+#include <openssl/pem.h>
+#include <openssl/err.h>
+#include <openssl/ui.h>

#define TSSINCLUDE(x) < TSS_INCLUDE/x >
#include TSSINCLUDE(tss.h)
@@ -20,6 +23,7 @@
#include TSSINCLUDE(Unmarshal_fp.h)

#include "tpm2-common.h"
+#include "tpm2-asn.h"

struct myTPM2B {
UINT16 s;
@@ -913,3 +917,466 @@ int tpm2_get_public_point(TPM2B_ECC_POINT *tpmpt, const EC_GROUP *group,

return len;
}
+
+static char *tpm2_get_auth_ui(UI_METHOD *ui_method, char *prompt, void *cb_data)
+{
+ UI *ui = UI_new();
+ /* Max auth size is name algorithm hash length, so this
+ * is way bigger than necessary */
+ char auth[256], *ret = NULL;
+ int len;
+
+ if (ui_method)
+ UI_set_method(ui, ui_method);
+
+ UI_add_user_data(ui, cb_data);
+
+ if (UI_add_input_string(ui, prompt, UI_INPUT_FLAG_DEFAULT_PWD,
+ auth, 0, sizeof(auth)) == 0) {
+ fprintf(stderr, "UI_add_input_string failed\n");
+ goto out;
+ }
+
+ if (UI_process(ui)) {
+ fprintf(stderr, "UI_process failed\n");
+ goto out;
+ }
+
+ len = strlen(auth);
+ ret = OPENSSL_malloc(len + 1);
+ if (!ret)
+ goto out;
+
+ strcpy(ret, auth);
+
+ out:
+ UI_free(ui);
+
+ return ret;
+}
+
+static char *tpm2_get_auth_pem(char *input_string, void *cb_data)
+{
+ char auth[256], *ret;
+ int len;
+
+ EVP_set_pw_prompt(input_string);
+
+ PEM_def_callback(auth, sizeof(auth), 0, cb_data);
+ EVP_set_pw_prompt(NULL);
+
+ len = strlen(auth);
+ ret = OPENSSL_malloc(len + 1);
+ if (!ret)
+ goto out;
+
+ strcpy(ret, auth);
+
+ out:
+ return ret;
+}
+
+char *tpm2_get_auth(UI_METHOD *ui, char *input_string, void *cb_data)
+{
+ if (ui)
+ return tpm2_get_auth_ui(ui, input_string, cb_data);
+ else
+ return tpm2_get_auth_pem(input_string, cb_data);
+}
+
+static int tpm2_engine_load_key_policy(struct app_data *app_data,
+ STACK_OF(TSSOPTPOLICY) *st_policy)
+{
+ struct policy_command *command;
+ TSSOPTPOLICY *policy;
+ int i, commands_len;
+
+ app_data->num_commands = sk_TSSOPTPOLICY_num(st_policy);
+ if (app_data->num_commands <= 0)
+ return 1;
+
+ commands_len = sizeof(struct policy_command) * app_data->num_commands;
+ app_data->commands = OPENSSL_malloc(commands_len);
+ if (!app_data->commands)
+ return 0;
+
+ for (i = 0; i < app_data->num_commands; i++) {
+ policy = sk_TSSOPTPOLICY_value(st_policy, i);
+ if (!policy)
+ return 0;
+
+ command = app_data->commands + i;
+ command->code = ASN1_INTEGER_get(policy->CommandCode);
+ command->size = policy->CommandPolicy->length;
+ command->policy = NULL;
+
+ if (!command->size)
+ continue;
+
+ command->policy = OPENSSL_malloc(command->size);
+ if (!command->policy)
+ return 0;
+
+ memcpy(command->policy, policy->CommandPolicy->data,
+ command->size);
+ }
+
+ return 1;
+}
+
+int tpm2_load_engine_file(const char *filename, struct app_data **app_data,
+ EVP_PKEY **ppkey, UI_METHOD *ui, void *cb_data,
+ const char *srk_auth)
+{
+ BIO *bf;
+ TSSLOADABLE *tssl = NULL;
+ TSSPRIVKEY *tpk = NULL;
+ BYTE *buffer;
+ INT32 size;
+ struct app_data *ad;
+ char oid[128];
+ int empty_auth, version = 0;
+ ASN1_OBJECT *type;
+ ASN1_INTEGER *parent;
+ ASN1_OCTET_STRING *pubkey;
+ STACK_OF(TSSOPTPOLICY) *policy;
+ ASN1_OCTET_STRING *privkey;
+ ASN1_OCTET_STRING *secret = NULL;
+ Import_In iin;
+
+ bf = BIO_new_file(filename, "r");
+ if (!bf) {
+ fprintf(stderr, "File %s does not exist or cannot be read\n",
+ filename);
+ return 0;
+ }
+
+ tpk = PEM_read_bio_TSSPRIVKEY(bf, NULL, NULL, NULL);
+ if (tpk) {
+ version = 1;
+ type = tpk->type;
+ empty_auth = tpk->emptyAuth;
+ parent = tpk->parent;
+ pubkey = tpk->pubkey;
+ privkey = tpk->privkey;
+ policy = tpk->policy;
+ secret = tpk->secret;
+ } else {
+ BIO_seek(bf, 0);
+ tssl = PEM_read_bio_TSSLOADABLE(bf, NULL, NULL, NULL);
+ if (!tssl) {
+ BIO_free(bf);
+ fprintf(stderr, "Failed to parse file %s\n", filename);
+ return 0;
+ }
+
+ /* have error from failed TSSPRIVKEY load */
+ ERR_clear_error();
+ type = tssl->type;
+ empty_auth = tssl->emptyAuth;
+ parent = tssl->parent;
+ pubkey = tssl->pubkey;
+ privkey = tssl->privkey;
+ policy = tssl->policy;
+ }
+
+ BIO_free(bf);
+
+ if (OBJ_obj2txt(oid, sizeof(oid), type, 1) == 0) {
+ fprintf(stderr, "Failed to parse object type\n");
+ goto err;
+ }
+
+ if (strcmp(OID_loadableKey, oid) == 0) {
+ if (version != 1) {
+ fprintf(stderr, "New type found in old format key\n");
+ goto err;
+ }
+ } else if (strcmp(OID_OldloadableKey, oid) == 0) {
+ if (version != 0) {
+ fprintf(stderr, "Old type found in new format key\n");
+ goto err;
+ }
+ } else if (strcmp(OID_importableKey, oid) == 0) {
+ if (!secret) {
+ fprintf(stderr, "Importable keys require an encrypted secret\n");
+ goto err;
+ }
+ } else {
+ fprintf(stderr, "Unrecognised object type\n");
+ goto err;
+ }
+
+ ad = OPENSSL_malloc(sizeof(*ad));
+
+ if (!ad) {
+ fprintf(stderr, "Failed to allocate app_data\n");
+ goto err;
+ }
+ memset(ad, 0, sizeof(*ad));
+
+ *app_data = ad;
+
+ ad->version = version;
+ ad->dir = tpm2_set_unique_tssdir();
+
+ if (parent)
+ ad->parent = ASN1_INTEGER_get(parent);
+ else
+ /* older keys have absent parent */
+ ad->parent = TPM_RH_OWNER;
+
+ ad->pub = OPENSSL_malloc(pubkey->length);
+ if (!ad->pub)
+ goto err_free;
+ ad->pub_len = pubkey->length;
+ memcpy(ad->pub, pubkey->data, ad->pub_len);
+
+ buffer = ad->pub;
+ size = ad->pub_len;
+ TPM2B_PUBLIC_Unmarshal(&iin.objectPublic, &buffer, &size, FALSE);
+ ad->name_alg = iin.objectPublic.publicArea.nameAlg;
+
+ if (strcmp(OID_importableKey, oid) == 0) {
+ TPM_HANDLE session;
+ TSS_CONTEXT *tssContext;
+ TPM_RC rc;
+ const char *reason;
+ TPM2B_PRIVATE priv_2b;
+ BYTE *buf;
+ UINT16 written;
+ INT32 size;
+ Import_Out iout;
+
+ rc = tpm2_create(&tssContext, ad->dir);
+ if (rc) {
+ reason="tpm2_create";
+ goto import_err;
+ }
+
+ if ((ad->parent & 0xff000000) == 0x40000000) {
+ tpm2_load_srk(tssContext, &iin.parentHandle,
+ srk_auth, NULL, ad->parent, 1);
+ } else {
+ iin.parentHandle = ad->parent;
+ }
+ rc = tpm2_get_session_handle(tssContext, &session,
+ iin.parentHandle,
+ TPM_SE_HMAC,
+ iin.objectPublic.publicArea.nameAlg);
+ if (rc) {
+ reason="tpm2_get_session_handle";
+ goto import_err;
+ }
+
+ /* no inner encryption */
+ iin.encryptionKey.t.size = 0;
+ iin.symmetricAlg.algorithm = TPM_ALG_NULL;
+
+ /* for importable keys the private key is actually the
+ * outer wrapped duplicate structure */
+ buffer = privkey->data;
+ size = privkey->length;
+ TPM2B_PRIVATE_Unmarshal(&iin.duplicate, &buffer, &size);
+
+ buffer = secret->data;
+ size = secret->length;
+ TPM2B_ENCRYPTED_SECRET_Unmarshal(&iin.inSymSeed, &buffer, &size);
+ rc = TSS_Execute(tssContext,
+ (RESPONSE_PARAMETERS *)&iout,
+ (COMMAND_PARAMETERS *)&iin,
+ NULL,
+ TPM_CC_Import,
+ session, srk_auth, 0,
+ TPM_RH_NULL, NULL, 0);
+ if (rc)
+ tpm2_flush_handle(tssContext, session);
+ reason = "TPM2_Import";
+
+ import_err:
+ tpm2_flush_srk(tssContext, iin.parentHandle);
+ TSS_Delete(tssContext);
+ if (rc) {
+ tpm2_error(rc, reason);
+ goto err_free;
+ }
+ buf = priv_2b.t.buffer;
+ size = sizeof(priv_2b.t.buffer);
+ written = 0;
+ TSS_TPM2B_PRIVATE_Marshal(&iout.outPrivate, &written,
+ &buf, &size);
+ ad->priv = OPENSSL_malloc(written);
+ if (!ad->priv)
+ goto err_free;
+ ad->priv_len = written;
+ memcpy(ad->priv, priv_2b.t.buffer, written);
+ } else {
+ ad->priv = OPENSSL_malloc(privkey->length);
+ if (!ad->priv)
+ goto err_free;
+
+ ad->priv_len = privkey->length;
+ memcpy(ad->priv, privkey->data, ad->priv_len);
+ }
+
+ /* create the new objects to return */
+ *ppkey = tpm2_to_openssl_public(&iin.objectPublic.publicArea);
+ if (!*ppkey) {
+ fprintf(stderr, "Failed to allocate a new EVP_KEY\n");
+ goto err_free;
+ }
+
+ if (empty_auth == 0) {
+ ad->auth = tpm2_get_auth(ui, "TPM Key Password: ", cb_data);
+ if (!ad->auth)
+ goto err_free_key;
+ }
+
+ if (!(iin.objectPublic.publicArea.objectAttributes.val &
+ TPMA_OBJECT_USERWITHAUTH))
+ ad->req_policy_session = 1;
+
+ if (!tpm2_engine_load_key_policy(ad, policy))
+ goto err_free_key;
+
+ TSSLOADABLE_free(tssl);
+ TSSPRIVKEY_free(tpk);
+
+ return 1;
+ err_free_key:
+ EVP_PKEY_free(*ppkey);
+ err_free:
+ *ppkey = NULL;
+
+ tpm2_delete(ad);
+ err:
+ TSSLOADABLE_free(tssl);
+ TSSPRIVKEY_free(tpk);
+
+ return 0;
+}
+
+void tpm2_delete(struct app_data *app_data)
+{
+ int i;
+
+ for (i = 0; i < app_data->num_commands; i++)
+ OPENSSL_free(app_data->commands[i].policy);
+
+ OPENSSL_free(app_data->commands);
+ OPENSSL_free(app_data->priv);
+ OPENSSL_free(app_data->pub);
+
+ tpm2_rm_keyfile(app_data->dir, app_data->parent);
+ /* if key was nv key, flush may not have removed file */
+ tpm2_rm_keyfile(app_data->dir, app_data->key);
+ tpm2_rm_tssdir(app_data->dir);
+
+ OPENSSL_free((void *)app_data->dir);
+
+ OPENSSL_free(app_data);
+}
+
+TPM_HANDLE tpm2_load_key(TSS_CONTEXT **tsscp, struct app_data *app_data,
+ const char *srk_auth)
+{
+ TSS_CONTEXT *tssContext;
+ Load_In in;
+ Load_Out out;
+ TPM_HANDLE key = 0;
+ TPM_RC rc;
+ BYTE *buffer;
+ INT32 size;
+ TPM_HANDLE session;
+
+ rc = tpm2_create(&tssContext, app_data->dir);
+ if (rc)
+ return 0;
+
+ if (app_data->key) {
+ key = app_data->key;
+ goto out;
+ }
+
+ buffer = app_data->priv;
+ size = app_data->priv_len;
+ TPM2B_PRIVATE_Unmarshal(&in.inPrivate, &buffer, &size);
+
+ buffer = app_data->pub;
+ size = app_data->pub_len;
+ TPM2B_PUBLIC_Unmarshal(&in.inPublic, &buffer, &size, FALSE);
+
+ if ((app_data->parent & 0xff000000) == 0x81000000) {
+ in.parentHandle = app_data->parent;
+ } else {
+ rc = tpm2_load_srk(tssContext, &in.parentHandle, srk_auth, NULL, app_data->parent, app_data->version);
+ if (rc)
+ goto out;
+ }
+ rc = tpm2_get_session_handle(tssContext, &session, in.parentHandle,
+ TPM_SE_HMAC, app_data->name_alg);
+ if (rc)
+ goto out_flush_srk;
+ rc = TSS_Execute(tssContext,
+ (RESPONSE_PARAMETERS *)&out,
+ (COMMAND_PARAMETERS *)&in,
+ NULL,
+ TPM_CC_Load,
+ session, srk_auth, 0,
+ TPM_RH_NULL, NULL, 0);
+ if (rc) {
+ tpm2_error(rc, "TPM2_Load");
+ tpm2_flush_handle(tssContext, session);
+ }
+ else
+ key = out.objectHandle;
+
+ out_flush_srk:
+ tpm2_flush_srk(tssContext, in.parentHandle);
+ out:
+ if (!key)
+ TSS_Delete(tssContext);
+ else
+ *tsscp = tssContext;
+ return key;
+}
+
+void tpm2_unload_key(TSS_CONTEXT *tssContext, TPM_HANDLE key)
+{
+ tpm2_flush_handle(tssContext, key);
+
+ TSS_Delete(tssContext);
+}
+
+IMPLEMENT_ASN1_FUNCTIONS(TSSOPTPOLICY)
+IMPLEMENT_ASN1_FUNCTIONS(TSSLOADABLE)
+IMPLEMENT_ASN1_FUNCTIONS(TSSPRIVKEY)
+IMPLEMENT_PEM_write_bio(TSSLOADABLE, TSSLOADABLE, TSSLOADABLE_PEM_STRING, TSSLOADABLE)
+IMPLEMENT_PEM_read_bio(TSSLOADABLE, TSSLOADABLE, TSSLOADABLE_PEM_STRING, TSSLOADABLE)
+IMPLEMENT_PEM_write_bio(TSSPRIVKEY, TSSPRIVKEY, TSSPRIVKEY_PEM_STRING, TSSPRIVKEY)
+IMPLEMENT_PEM_read_bio(TSSPRIVKEY, TSSPRIVKEY, TSSPRIVKEY_PEM_STRING, TSSPRIVKEY)
+
+ASN1_SEQUENCE(TSSOPTPOLICY) = {
+ ASN1_EXP(TSSOPTPOLICY, CommandCode, ASN1_INTEGER, 0),
+ ASN1_EXP(TSSOPTPOLICY, CommandPolicy, ASN1_OCTET_STRING, 1)
+} ASN1_SEQUENCE_END(TSSOPTPOLICY)
+
+ASN1_SEQUENCE(TSSLOADABLE) = {
+ ASN1_SIMPLE(TSSLOADABLE, type, ASN1_OBJECT),
+ ASN1_EXP_OPT(TSSLOADABLE, emptyAuth, ASN1_BOOLEAN, 0),
+ ASN1_EXP_OPT(TSSLOADABLE, parent, ASN1_INTEGER, 1),
+ ASN1_EXP_OPT(TSSLOADABLE, pubkey, ASN1_OCTET_STRING, 2),
+ ASN1_EXP_SEQUENCE_OF_OPT(TSSLOADABLE, policy, TSSOPTPOLICY, 3),
+ ASN1_SIMPLE(TSSLOADABLE, privkey, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END(TSSLOADABLE)
+
+ASN1_SEQUENCE(TSSPRIVKEY) = {
+ ASN1_SIMPLE(TSSPRIVKEY, type, ASN1_OBJECT),
+ ASN1_EXP_OPT(TSSPRIVKEY, emptyAuth, ASN1_BOOLEAN, 0),
+ ASN1_EXP_SEQUENCE_OF_OPT(TSSPRIVKEY, policy, TSSOPTPOLICY, 1),
+ ASN1_EXP_OPT(TSSPRIVKEY, secret, ASN1_OCTET_STRING, 2),
+ ASN1_SIMPLE(TSSPRIVKEY, parent, ASN1_INTEGER),
+ ASN1_SIMPLE(TSSPRIVKEY, pubkey, ASN1_OCTET_STRING),
+ ASN1_SIMPLE(TSSPRIVKEY, privkey, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END(TSSPRIVKEY)
+
diff --git a/tpm2-common.h b/tpm2-common.h
index f22422b..264fc8e 100644
--- a/tpm2-common.h
+++ b/tpm2-common.h
@@ -10,6 +10,25 @@ struct policy_command {
BYTE *policy;
};

+/* structure pointed to by the RSA object's app_data pointer */
+struct app_data {
+ int version;
+ TPM_HANDLE parent;
+ /* if key is in NV memory */
+ TPM_HANDLE key;
+ /* otherwise key is specified by blobs */
+ void *priv;
+ int priv_len;
+ void *pub;
+ int pub_len;
+ char *auth;
+ const char *dir;
+ int req_policy_session;
+ int num_commands;
+ unsigned int name_alg;
+ struct policy_command *commands;
+};
+
void tpm2_error(TPM_RC rc, const char *reason);
TPM_RC tpm2_load_srk(TSS_CONTEXT *tssContext, TPM_HANDLE *h, const char *auth, TPM2B_PUBLIC *pub, TPM_HANDLE handle, int version);
void tpm2_flush_handle(TSS_CONTEXT *tssContext, TPM_HANDLE h);
@@ -36,4 +55,12 @@ void tpm2_rm_tssdir(const char *dir);
void tpm2_rm_keyfile(const char *dir, TPM_HANDLE key);
int tpm2_get_public_point(TPM2B_ECC_POINT *tpmpt, const EC_GROUP *group,
const EC_POINT *pt);
+int tpm2_load_engine_file(const char *filename, struct app_data **app_data,
+ EVP_PKEY **ppkey, UI_METHOD *ui, void *cb_data,
+ const char *srk_auth);
+TPM_HANDLE tpm2_load_key(TSS_CONTEXT **tsscp, struct app_data *app_data,
+ const char *srk_auth);
+void tpm2_unload_key(TSS_CONTEXT *tssContext, TPM_HANDLE key);
+void tpm2_delete(struct app_data *app_data);
+char *tpm2_get_auth(UI_METHOD *ui, char *input_string, void *cb_data);
#endif
--
2.16.4

[PATCH 1/4] e_tpm2: simplify tpm2_load_key_core

James Bottomley
 

The original was designed for a switchable UI and a passed in bio.
Since we do neither of these things now, strip out the superfluous
coding.

Signed-off-by: James Bottomley <James.Bottomley@...>
---
e_tpm2.c | 54 ++++++++++++++++++------------------------------------
1 file changed, 18 insertions(+), 36 deletions(-)

diff --git a/e_tpm2.c b/e_tpm2.c
index 5d003a4..1523e1f 100644
--- a/e_tpm2.c
+++ b/e_tpm2.c
@@ -99,11 +99,6 @@ static const ENGINE_CMD_DEFN tpm2_cmd_defns[] = {
{0, NULL, NULL, 0}
};

-struct tpm_ui {
- UI_METHOD *ui_method;
- pem_password_cb *pem_cb;
-};
-
static char *tpm2_get_auth_ui(UI_METHOD *ui_method, char *prompt, void *cb_data)
{
UI *ui = UI_new();
@@ -141,17 +136,14 @@ static char *tpm2_get_auth_ui(UI_METHOD *ui_method, char *prompt, void *cb_data)
return ret;
}

-static char *tpm2_get_auth_pem(pem_password_cb *pem_cb,
- char *input_string,
- void *cb_data)
+static char *tpm2_get_auth_pem(char *input_string, void *cb_data)
{
char auth[256], *ret;
int len;

EVP_set_pw_prompt(input_string);
- if (!pem_cb)
- pem_cb = PEM_def_callback;
- pem_cb(auth, sizeof(auth), 0, cb_data);
+
+ PEM_def_callback(auth, sizeof(auth), 0, cb_data);
EVP_set_pw_prompt(NULL);

len = strlen(auth);
@@ -165,13 +157,13 @@ static char *tpm2_get_auth_pem(pem_password_cb *pem_cb,
return ret;
}

-static char *tpm2_get_auth(struct tpm_ui *ui, char *input_string,
+static char *tpm2_get_auth(UI_METHOD *ui, char *input_string,
void *cb_data)
{
- if (ui->ui_method)
- return tpm2_get_auth_ui(ui->ui_method, input_string, cb_data);
+ if (ui)
+ return tpm2_get_auth_ui(ui, input_string, cb_data);
else
- return tpm2_get_auth_pem(ui->pem_cb, input_string, cb_data);
+ return tpm2_get_auth_pem(input_string, cb_data);
}

void tpm2_bind_key_to_engine(EVP_PKEY *pkey, void *data)
@@ -189,8 +181,8 @@ void tpm2_bind_key_to_engine(EVP_PKEY *pkey, void *data)
}

static int tpm2_engine_load_nvkey(ENGINE *e, EVP_PKEY **ppkey,
- TPM_HANDLE key, BIO *bio,
- struct tpm_ui *ui, void *cb_data)
+ TPM_HANDLE key, UI_METHOD *ui,
+ void *cb_data)
{
TPMT_PUBLIC p;
TSS_CONTEXT *tssContext;
@@ -312,8 +304,8 @@ static int tpm2_engine_load_key_policy(struct app_data *app_data,
}

static int tpm2_engine_load_key_core(ENGINE *e, EVP_PKEY **ppkey,
- const char *key_id, BIO *bio,
- struct tpm_ui *ui, void *cb_data)
+ const char *key_id, UI_METHOD *ui,
+ void *cb_data)
{
EVP_PKEY *pkey;
BIO *bf;
@@ -333,8 +325,8 @@ static int tpm2_engine_load_key_core(ENGINE *e, EVP_PKEY **ppkey,
ASN1_OCTET_STRING *secret = NULL;
Import_In iin;

- if (!key_id && !bio) {
- fprintf(stderr, "key_id or bio is NULL\n");
+ if (!key_id) {
+ fprintf(stderr, "key_id is NULL\n");
return 0;
}

@@ -346,14 +338,10 @@ static int tpm2_engine_load_key_core(ENGINE *e, EVP_PKEY **ppkey,
fprintf(stderr, "nvkey is not an NV index\n");
return 0;
}
- return tpm2_engine_load_nvkey(e, ppkey, key, bio,
- ui, cb_data);
+ return tpm2_engine_load_nvkey(e, ppkey, key, ui, cb_data);
}

- if (bio)
- bf = bio;
- else
- bf = BIO_new_file(key_id, "r");
+ bf = BIO_new_file(key_id, "r");
if (!bf) {
fprintf(stderr, "File %s does not exist or cannot be read\n", key_id);
return 0;
@@ -373,8 +361,7 @@ static int tpm2_engine_load_key_core(ENGINE *e, EVP_PKEY **ppkey,
BIO_seek(bf, 0);
tssl = PEM_read_bio_TSSLOADABLE(bf, NULL, NULL, NULL);
if (!tssl) {
- if (!bio)
- BIO_free(bf);
+ BIO_free(bf);
if (ppkey)
fprintf(stderr, "Failed to parse file %s\n", key_id);
return 0;
@@ -390,8 +377,7 @@ static int tpm2_engine_load_key_core(ENGINE *e, EVP_PKEY **ppkey,
policy = tssl->policy;
}

- if (!bio)
- BIO_free(bf);
+ BIO_free(bf);

if (!ppkey) {
TSSLOADABLE_free(tssl);
@@ -575,14 +561,10 @@ static int tpm2_engine_load_key_core(ENGINE *e, EVP_PKEY **ppkey,
static EVP_PKEY *tpm2_engine_load_key(ENGINE *e, const char *key_id,
UI_METHOD *ui, void *cb)
{
- struct tpm_ui tui = {
- .ui_method = ui,
- .pem_cb = NULL,
- };
EVP_PKEY *pkey;
int ret;

- ret = tpm2_engine_load_key_core(e, &pkey, key_id, NULL, &tui, cb);
+ ret = tpm2_engine_load_key_core(e, &pkey, key_id, ui, cb);
if (ret == 1)
return pkey;
return NULL;
--
2.16.4

[PATCH 0/4] Fix load_tpm2_key

James Bottomley
 

Ever since importable keys were added, load_tpm2_key hasn't been able
to use them. The reason is that it's loading routines were never
modified to add the capability. However load_tpm2_key shouldn't be
using separate loading routines, so this patch series extracts the key
loading routines into common code and then updates load_tpm2_key to
use them, meaning that it now benefits from all the current loadable
features including importable keys.

James

---

James Bottomley (4):
e_tpm2: simplify tpm2_load_key_core
tpm2-common: extract file and TPM loading functions
load_tpm2_key: use common file parsing and load routines
load_tpm2_key: add a test for loading importable keys

e_tpm2-ecc.c | 2 +-
e_tpm2-rsa.c | 2 +-
e_tpm2.c | 465 ++-------------------------------------------
e_tpm2.h | 23 +--
load_tpm2_key.c | 87 ++-------
tests/check_importable.sh | 6 +
tpm2-asn.h | 41 +---
tpm2-common.c | 470 ++++++++++++++++++++++++++++++++++++++++++++++
tpm2-common.h | 27 +++
9 files changed, 545 insertions(+), 578 deletions(-)

--
2.16.4

Re: [PATCH] Fix compile warning on older GCC

Jonathan Wernberg
 

Here you go, see attached patch.

// Jonathan Wernberg

-----Original Message-----
From: James Bottomley <James.Bottomley@...>
Sent: den 28 november 2019 17:08
To: openssl-tpm2-engine@groups.io; Jonathan Wernberg <Jonathan.Wernberg@...>
Subject: Re: [openssl-tpm2-engine] [PATCH] Fix compile warning on older GCC

On Thu, 2019-11-28 at 15:10 +0000, Jonathan Wernberg wrote:
GCC cannot, for each possible value of one variable such as "tssl",
keep track of which of all other variables such as "pubkey" and
"privkey" are uninitialized or not. Some GCC versions just assumes the
variable may be uninitialized if it cannot know. Some more recent GCC
versions seem to take the opposite approach.

Either way, a simple refactoring of the error handling eliminates the
uncertainity by making both the if and else clause definitely set the
variables "pubkey" and "privkey" to an initialized value, so it now
compiles fine.
See attached patch.
This looks like an eminently reasonable simplification. Could I get a signed-off-by for DCO attestation:

https://www.developercertificate.org/

I should probably actually give the project a contributing file that explains this.

Thanks,

James

Re: [PATCH] Fix compile warning on older GCC

James Bottomley
 

On Thu, 2019-11-28 at 15:10 +0000, Jonathan Wernberg wrote:
GCC cannot, for each possible value of one variable such as "tssl",
keep track of which of all other variables such as "pubkey" and
"privkey" are uninitialized or not. Some GCC versions just assumes
the variable may be uninitialized if it cannot know. Some more recent
GCC versions seem to take the opposite approach.

Either way, a simple refactoring of the error handling eliminates
the uncertainity by making both the if and else clause definitely
set the variables "pubkey" and "privkey" to an initialized value,
so it now compiles fine.
See attached patch.
This looks like an eminently reasonable simplification. Could I get a
signed-off-by for DCO attestation:

https://www.developercertificate.org/

I should probably actually give the project a contributing file that
explains this.

Thanks,

James

[PATCH] Fix compile warning on older GCC

Jonathan Wernberg
 

See attached patch.

// Jonathan Wernberg

[PATCH v3] Truncate hashes for ECDSA signing, to allow SHA512 to work

David Woodhouse
 

The TPM has no business knowing what hash we were using. ECDSA signatures
truncate the digest to the size of the curve anyway. So just pick any
hash between the curve size and the actual size of the digest, and tell
the TPM it's that. Then we don't care that most TPMs don't support SHA512.

Signed-off-by: David Woodhouse <dwmw2@...>
---
e_tpm2-ecc.c | 74 +++++++++++++++++++++++++++++++++++++--------
tests/create_ecc.sh | 2 +-
2 files changed, 62 insertions(+), 14 deletions(-)

diff --git a/e_tpm2-ecc.c b/e_tpm2-ecc.c
index 21a636c..2cbb3aa 100644
--- a/e_tpm2-ecc.c
+++ b/e_tpm2-ecc.c
@@ -123,6 +123,33 @@ static void tpm2_ecc_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad,
tpm2_delete(data);
}

+#if OPENSSL_VERSION_NUMBER < 0x10100000
+static int EC_GROUP_order_bits(const EC_GROUP *group)
+{
+ if (!group)
+ return 0;
+
+ BIGNUM *order = BN_new();
+
+ if (order == NULL) {
+ ERR_clear_error();
+ return 0;
+ }
+
+ int ret = 0;
+
+ if (!EC_GROUP_get_order(group, order, NULL)) {
+ ERR_clear_error();
+ BN_free(order);
+ return 0;
+ }
+
+ ret = BN_num_bits(order);
+ BN_free(order);
+ return ret;
+}
+#endif
+
static ECDSA_SIG *tpm2_ecdsa_sign(const unsigned char *dgst, int dgst_len,
const BIGNUM *kinv, const BIGNUM *rp,
EC_KEY *eck)
@@ -139,25 +166,46 @@ static ECDSA_SIG *tpm2_ecdsa_sign(const unsigned char *dgst, int dgst_len,
int num_commands;
struct policy_command *commands;
TPM_ALG_ID nameAlg;
-
- /* The TPM insists on knowing the digest type, so
- * calculate that from the size */
- switch (dgst_len) {
- case SHA_DIGEST_LENGTH:
+ int curve_len;
+
+ /*
+ * ECDSA signatures truncate the incoming hash to fit the curve,
+ * and the signature mechanism is the same regardless of the
+ * hash being used.
+ *
+ * The TPM bizarrely wants to be told the hash algorithm, and
+ * either it or the TSS will validate that the digest length
+ * matches the hash that it's told, despite it having no business
+ * caring about such things.
+ *
+ * So, we can truncate the digest and pretend it's any smaller
+ * digest that the TPM actually does support, as long as that
+ * digest is larger than the size of the curve.
+ */
+ curve_len = (EC_GROUP_order_bits(EC_KEY_get0_group(eck)) + 7) / 8;
+ /* If we couldn't work it out, don't truncate */
+ if (!curve_len)
+ curve_len = dgst_len;
+
+ if (dgst_len == SHA_DIGEST_LENGTH ||
+ (curve_len <= SHA_DIGEST_LENGTH && dgst_len > SHA_DIGEST_LENGTH)) {
in.inScheme.details.ecdsa.hashAlg = TPM_ALG_SHA1;
- break;
- case SHA256_DIGEST_LENGTH:
+ dgst_len = SHA_DIGEST_LENGTH;
+ } else if (dgst_len == SHA256_DIGEST_LENGTH ||
+ (curve_len <= SHA256_DIGEST_LENGTH && dgst_len > SHA256_DIGEST_LENGTH)) {
in.inScheme.details.ecdsa.hashAlg = TPM_ALG_SHA256;
- break;
- case SHA384_DIGEST_LENGTH:
+ dgst_len = SHA256_DIGEST_LENGTH;
+ } else if (dgst_len == SHA384_DIGEST_LENGTH ||
+ (curve_len <= SHA384_DIGEST_LENGTH && dgst_len > SHA384_DIGEST_LENGTH)) {
in.inScheme.details.ecdsa.hashAlg = TPM_ALG_SHA384;
- break;
+ dgst_len = SHA384_DIGEST_LENGTH;
#ifdef TPM_ALG_SHA512
- case SHA512_DIGEST_LENGTH:
+ } else if (dgst_len == SHA512_DIGEST_LENGTH ||
+ (curve_len <= SHA512_DIGEST_LENGTH && dgst_len > SHA512_DIGEST_LENGTH)) {
in.inScheme.details.ecdsa.hashAlg = TPM_ALG_SHA512;
- break;
+ dgst_len = SHA512_DIGEST_LENGTH;
#endif
- default:
+ } else {
printf("ECDSA signature: Unknown digest length, cannot deduce hash type for TPM\n");
return NULL;
}
diff --git a/tests/create_ecc.sh b/tests/create_ecc.sh
index 061cedb..092c743 100755
--- a/tests/create_ecc.sh
+++ b/tests/create_ecc.sh
@@ -14,7 +14,7 @@ for curve in $(${bindir}/create_tpm2_key --list-curves); do
echo "Checking curve ${curve}"
${bindir}/create_tpm2_key -p 81000001 --ecc ${curve} key.tpm || \
exit 1
- for hash in sha1 sha256 sha384; do
+ for hash in sha1 sha256 sha384 sha512; do
openssl req -new -x509 -${hash} -subj '/CN=test/' -key key.tpm -engine tpm2 -keyform engine -out tmp.crt && \
openssl verify -CAfile tmp.crt -check_ss_sig tmp.crt || \
exit 1