You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
386 lines
10 KiB
386 lines
10 KiB
1 year ago
|
From ba7682dc511f4ef6bbb8a15ca3bb0edf67ec39ce Mon Sep 17 00:00:00 2001
|
||
|
From: Daiki Ueno <ueno@gnu.org>
|
||
|
Date: Fri, 17 Sep 2021 07:14:20 +0200
|
||
|
Subject: [PATCH 02/17] tpm2_encodeobject: New tool to encode TPM2 object
|
||
|
|
||
|
This adds a new tool tpm2_encodeobject in tools/misc. It takes
|
||
|
public and private portions of an object and encode them in a combined
|
||
|
PEM form used by tpm2-tss-engine and other applications.
|
||
|
|
||
|
Signed-off-by: Daiki Ueno <ueno@gnu.org>
|
||
|
---
|
||
|
Makefile.am | 2 +
|
||
|
man/tpm2_encodeobject.1.md | 92 +++++++++++++
|
||
|
tools/misc/tpm2_encodeobject.c | 240 +++++++++++++++++++++++++++++++++
|
||
|
3 files changed, 334 insertions(+)
|
||
|
create mode 100644 man/tpm2_encodeobject.1.md
|
||
|
create mode 100644 tools/misc/tpm2_encodeobject.c
|
||
|
|
||
|
diff --git a/Makefile.am b/Makefile.am
|
||
|
index 71322159..e1a51ebf 100644
|
||
|
--- a/Makefile.am
|
||
|
+++ b/Makefile.am
|
||
|
@@ -103,6 +103,7 @@ tools_tpm2_SOURCES = \
|
||
|
tpm2_tools = \
|
||
|
tools/misc/tpm2_certifyX509certutil.c \
|
||
|
tools/misc/tpm2_checkquote.c \
|
||
|
+ tools/misc/tpm2_encodeobject.c \
|
||
|
tools/misc/tpm2_eventlog.c \
|
||
|
tools/misc/tpm2_print.c \
|
||
|
tools/misc/tpm2_rc_decode.c \
|
||
|
@@ -376,6 +377,7 @@ if HAVE_MAN_PAGES
|
||
|
man/man1/tpm2_createprimary.1 \
|
||
|
man/man1/tpm2_dictionarylockout.1 \
|
||
|
man/man1/tpm2_duplicate.1 \
|
||
|
+ man/man1/tpm2_encodeobject.1 \
|
||
|
man/man1/tpm2_getcap.1 \
|
||
|
man/man1/tpm2_encryptdecrypt.1 \
|
||
|
man/man1/tpm2_eventlog.1 \
|
||
|
diff --git a/man/tpm2_encodeobject.1.md b/man/tpm2_encodeobject.1.md
|
||
|
new file mode 100644
|
||
|
index 00000000..791eafbd
|
||
|
--- /dev/null
|
||
|
+++ b/man/tpm2_encodeobject.1.md
|
||
|
@@ -0,0 +1,92 @@
|
||
|
+% tpm2_encodeobject(1) tpm2-tools | General Commands Manual
|
||
|
+
|
||
|
+# NAME
|
||
|
+
|
||
|
+**tpm2_encodeobject**(1) - Encode an object into a combined PEM format.
|
||
|
+
|
||
|
+# SYNOPSIS
|
||
|
+
|
||
|
+**tpm2_encodeobject** [*OPTIONS*]
|
||
|
+
|
||
|
+# DESCRIPTION
|
||
|
+
|
||
|
+**tpm2_encodeobject**(1) - Encode both the private and public portions of an
|
||
|
+object into a combined PEM format used by tpm2-tss-engine.
|
||
|
+
|
||
|
+The tool reads private and public portions of an object and encodes it
|
||
|
+into a combined PEM format used by tpm2-tss-engine and other
|
||
|
+applications.
|
||
|
+
|
||
|
+**NOTE**: Both private and public portions of the tpm key must be specified.
|
||
|
+
|
||
|
+# OPTIONS
|
||
|
+
|
||
|
+ * **-C**, **\--parent-context**=_OBJECT_:
|
||
|
+
|
||
|
+ The parent object.
|
||
|
+
|
||
|
+ * **-P**, **\--auth**=_AUTH_:
|
||
|
+
|
||
|
+ The authorization value of the parent object specified by **-C**.
|
||
|
+
|
||
|
+ * **-u**, **\--public**=_FILE_:
|
||
|
+
|
||
|
+ A file containing the public portion of the object.
|
||
|
+
|
||
|
+ * **-r**, **\--private**=_FILE_:
|
||
|
+
|
||
|
+ A file containing the sensitive portion of the object.
|
||
|
+
|
||
|
+ * **-o**, **\--output**=_FILE_:
|
||
|
+
|
||
|
+ The output file path, recording the public portion of the object.
|
||
|
+
|
||
|
+## References
|
||
|
+
|
||
|
+[context object format](common/ctxobj.md) details the methods for specifying
|
||
|
+_OBJECT_.
|
||
|
+
|
||
|
+[authorization formatting](common/authorizations.md) details the methods for
|
||
|
+specifying _AUTH_.
|
||
|
+
|
||
|
+[common options](common/options.md) collection of common options that provide
|
||
|
+information many users may expect.
|
||
|
+
|
||
|
+[common tcti options](common/tcti.md) collection of options used to configure
|
||
|
+the various known TCTI modules.
|
||
|
+
|
||
|
+# EXAMPLES
|
||
|
+
|
||
|
+## Setup
|
||
|
+To load an object you first must create an object under a primary object. So the
|
||
|
+first step is to create the primary object.
|
||
|
+
|
||
|
+```bash
|
||
|
+tpm2_createprimary -c primary.ctx
|
||
|
+```
|
||
|
+
|
||
|
+Step 2 is to create an object under the primary object.
|
||
|
+
|
||
|
+```bash
|
||
|
+tpm2_create -C primary.ctx -u key.pub -r key.priv -f pem -o pub.pem
|
||
|
+```
|
||
|
+
|
||
|
+This creates the private and public portions of the TPM object. With these
|
||
|
+object portions, it is now possible to load that object into the TPM for
|
||
|
+subsequent use.
|
||
|
+
|
||
|
+## Encoding an Object into a combined PEM format
|
||
|
+
|
||
|
+The final step, is encoding the public and private portions of the object into a
|
||
|
+PEM format.
|
||
|
+
|
||
|
+```bash
|
||
|
+tpm2_encodeobject -C primary.ctx -u key.pub -r key.priv -c priv.pem
|
||
|
+```
|
||
|
+
|
||
|
+The generated `priv.pem` can be used together with `pub.pem` created in the
|
||
|
+step 2 of Setup section.
|
||
|
+
|
||
|
+[returns](common/returns.md)
|
||
|
+
|
||
|
+[footer](common/footer.md)
|
||
|
diff --git a/tools/misc/tpm2_encodeobject.c b/tools/misc/tpm2_encodeobject.c
|
||
|
new file mode 100644
|
||
|
index 00000000..2341c3a1
|
||
|
--- /dev/null
|
||
|
+++ b/tools/misc/tpm2_encodeobject.c
|
||
|
@@ -0,0 +1,240 @@
|
||
|
+/* SPDX-License-Identifier: BSD-3-Clause */
|
||
|
+
|
||
|
+/*
|
||
|
+ * Part of this file is copied from tpm2-tss-engine.
|
||
|
+ *
|
||
|
+ * Copyright 2017-2018, Fraunhofer SIT sponsored by Infineon Technologies AG
|
||
|
+ * All rights reserved.
|
||
|
+ * Copyright (c) 2019, Wind River Systems.
|
||
|
+ * All rights reserved.
|
||
|
+ */
|
||
|
+
|
||
|
+#include <inttypes.h>
|
||
|
+#include <stdbool.h>
|
||
|
+#include <stdio.h>
|
||
|
+#include <string.h>
|
||
|
+#include <openssl/asn1.h>
|
||
|
+#include <openssl/asn1t.h>
|
||
|
+#include <openssl/pem.h>
|
||
|
+#include <tss2/tss2_mu.h>
|
||
|
+
|
||
|
+#include "files.h"
|
||
|
+#include "log.h"
|
||
|
+#include "tpm2.h"
|
||
|
+#include "tpm2_options.h"
|
||
|
+#include "tpm2_tool.h"
|
||
|
+
|
||
|
+#define OID_loadableKey "2.23.133.10.1.3"
|
||
|
+
|
||
|
+typedef struct {
|
||
|
+ ASN1_OBJECT *type;
|
||
|
+ ASN1_BOOLEAN emptyAuth;
|
||
|
+ ASN1_INTEGER *parent;
|
||
|
+ ASN1_OCTET_STRING *pubkey;
|
||
|
+ ASN1_OCTET_STRING *privkey;
|
||
|
+} TSSPRIVKEY;
|
||
|
+
|
||
|
+DECLARE_ASN1_FUNCTIONS(TSSPRIVKEY);
|
||
|
+DECLARE_PEM_write_bio(TSSPRIVKEY, TSSPRIVKEY);
|
||
|
+
|
||
|
+ASN1_SEQUENCE(TSSPRIVKEY) = {
|
||
|
+ ASN1_SIMPLE(TSSPRIVKEY, type, ASN1_OBJECT),
|
||
|
+ ASN1_EXP_OPT(TSSPRIVKEY, emptyAuth, ASN1_BOOLEAN, 0),
|
||
|
+ ASN1_SIMPLE(TSSPRIVKEY, parent, ASN1_INTEGER),
|
||
|
+ ASN1_SIMPLE(TSSPRIVKEY, pubkey, ASN1_OCTET_STRING),
|
||
|
+ ASN1_SIMPLE(TSSPRIVKEY, privkey, ASN1_OCTET_STRING)
|
||
|
+} ASN1_SEQUENCE_END(TSSPRIVKEY)
|
||
|
+
|
||
|
+#define TSSPRIVKEY_PEM_STRING "TSS2 PRIVATE KEY"
|
||
|
+
|
||
|
+IMPLEMENT_ASN1_FUNCTIONS(TSSPRIVKEY);
|
||
|
+IMPLEMENT_PEM_write_bio(TSSPRIVKEY, TSSPRIVKEY, TSSPRIVKEY_PEM_STRING, TSSPRIVKEY);
|
||
|
+IMPLEMENT_PEM_read_bio(TSSPRIVKEY, TSSPRIVKEY, TSSPRIVKEY_PEM_STRING, TSSPRIVKEY);
|
||
|
+
|
||
|
+typedef struct tpm_encodeobject_ctx tpm_encodeobject_ctx;
|
||
|
+struct tpm_encodeobject_ctx {
|
||
|
+ struct {
|
||
|
+ const char *ctx_path;
|
||
|
+ const char *auth_str;
|
||
|
+ tpm2_loaded_object object;
|
||
|
+ } parent;
|
||
|
+
|
||
|
+ struct {
|
||
|
+ const char *pubpath;
|
||
|
+ TPM2B_PUBLIC public;
|
||
|
+ const char *privpath;
|
||
|
+ TPM2B_PRIVATE private;
|
||
|
+ ESYS_TR handle;
|
||
|
+ } object;
|
||
|
+
|
||
|
+ char *output_path;
|
||
|
+};
|
||
|
+
|
||
|
+static tpm_encodeobject_ctx ctx;
|
||
|
+
|
||
|
+static bool on_option(char key, char *value) {
|
||
|
+ switch (key) {
|
||
|
+ case 'P':
|
||
|
+ ctx.parent.auth_str = value;
|
||
|
+ break;
|
||
|
+ case 'u':
|
||
|
+ ctx.object.pubpath = value;
|
||
|
+ break;
|
||
|
+ case 'r':
|
||
|
+ ctx.object.privpath = value;
|
||
|
+ break;
|
||
|
+ case 'C':
|
||
|
+ ctx.parent.ctx_path = value;
|
||
|
+ break;
|
||
|
+ case 'o':
|
||
|
+ ctx.output_path = value;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ return true;
|
||
|
+}
|
||
|
+
|
||
|
+static bool tpm2_tool_onstart(tpm2_options **opts) {
|
||
|
+ const struct option topts[] = {
|
||
|
+ { "auth", required_argument, NULL, 'P' },
|
||
|
+ { "public", required_argument, NULL, 'u' },
|
||
|
+ { "private", required_argument, NULL, 'r' },
|
||
|
+ { "parent-context", required_argument, NULL, 'C' },
|
||
|
+ { "output", required_argument, NULL, 'o' },
|
||
|
+ };
|
||
|
+
|
||
|
+ *opts = tpm2_options_new("P:u:r:C:o:", ARRAY_LEN(topts), topts, on_option,
|
||
|
+ NULL, 0);
|
||
|
+
|
||
|
+ return *opts != NULL;
|
||
|
+}
|
||
|
+
|
||
|
+static tool_rc check_opts(void) {
|
||
|
+ tool_rc rc = tool_rc_success;
|
||
|
+ if (!ctx.parent.ctx_path) {
|
||
|
+ LOG_ERR("Expected parent object via -C");
|
||
|
+ rc = tool_rc_option_error;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!ctx.object.pubpath) {
|
||
|
+ LOG_ERR("Expected public object portion via -u");
|
||
|
+ rc = tool_rc_option_error;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!ctx.object.privpath) {
|
||
|
+ LOG_ERR("Expected private object portion via -r");
|
||
|
+ rc = tool_rc_option_error;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!ctx.output_path) {
|
||
|
+ LOG_ERR("Expected output file path via -o");
|
||
|
+ rc = tool_rc_option_error;
|
||
|
+ }
|
||
|
+
|
||
|
+ return rc;
|
||
|
+}
|
||
|
+
|
||
|
+static tool_rc init(ESYS_CONTEXT *ectx) {
|
||
|
+ bool res = files_load_public(ctx.object.pubpath, &ctx.object.public);
|
||
|
+ if (!res) {
|
||
|
+ return tool_rc_general_error;
|
||
|
+ }
|
||
|
+
|
||
|
+ res = files_load_private(ctx.object.privpath, &ctx.object.private);
|
||
|
+ if (!res) {
|
||
|
+ return tool_rc_general_error;
|
||
|
+ }
|
||
|
+
|
||
|
+ return tpm2_util_object_load_auth(ectx, ctx.parent.ctx_path,
|
||
|
+ ctx.parent.auth_str, &ctx.parent.object, false,
|
||
|
+ TPM2_HANDLE_ALL_W_NV);
|
||
|
+}
|
||
|
+
|
||
|
+static int
|
||
|
+encode(void)
|
||
|
+{
|
||
|
+ TSS2_RC rc;
|
||
|
+ BIO *bio = NULL;
|
||
|
+ TSSPRIVKEY *tpk = NULL;
|
||
|
+
|
||
|
+ uint8_t private_buf[sizeof(ctx.object.private)];
|
||
|
+ uint8_t public_buf[sizeof(ctx.object.public)];
|
||
|
+ size_t private_len = 0, public_len = 0;
|
||
|
+
|
||
|
+ rc = Tss2_MU_TPM2B_PRIVATE_Marshal(&ctx.object.private, private_buf,
|
||
|
+ sizeof(private_buf), &private_len);
|
||
|
+ if (rc) {
|
||
|
+ LOG_ERR("Error serializing private portion of object");
|
||
|
+ goto error;
|
||
|
+ }
|
||
|
+
|
||
|
+ rc = Tss2_MU_TPM2B_PUBLIC_Marshal(&ctx.object.public, public_buf,
|
||
|
+ sizeof(public_buf), &public_len);
|
||
|
+ if (rc) {
|
||
|
+ LOG_ERR("Error serializing public portion of object");
|
||
|
+ goto error;
|
||
|
+ }
|
||
|
+
|
||
|
+ tpk = TSSPRIVKEY_new();
|
||
|
+ if (!tpk) {
|
||
|
+ LOG_ERR("oom");
|
||
|
+ goto error;
|
||
|
+ }
|
||
|
+
|
||
|
+ tpk->type = OBJ_txt2obj(OID_loadableKey, 1);
|
||
|
+ tpk->parent = ASN1_INTEGER_new();
|
||
|
+ tpk->privkey = ASN1_OCTET_STRING_new();
|
||
|
+ tpk->pubkey = ASN1_OCTET_STRING_new();
|
||
|
+ if (!tpk->type || !tpk->privkey || !tpk->pubkey || !tpk->parent) {
|
||
|
+ LOG_ERR("oom");
|
||
|
+ goto error;
|
||
|
+ }
|
||
|
+
|
||
|
+ tpk->emptyAuth = ctx.parent.auth_str == NULL ? 0xFF : 0;
|
||
|
+
|
||
|
+ if ((ctx.parent.object.handle >> TPM2_HR_SHIFT) == TPM2_HT_PERSISTENT) {
|
||
|
+ ASN1_INTEGER_set(tpk->parent, ctx.parent.object.handle);
|
||
|
+ } else {
|
||
|
+ /* Indicate that the parent is a primary object generated on the fly. */
|
||
|
+ ASN1_INTEGER_set(tpk->parent, TPM2_RH_OWNER);
|
||
|
+ }
|
||
|
+
|
||
|
+ ASN1_STRING_set(tpk->privkey, private_buf, private_len);
|
||
|
+ ASN1_STRING_set(tpk->pubkey, public_buf, public_len);
|
||
|
+
|
||
|
+ if ((bio = BIO_new_file(ctx.output_path, "w")) == NULL) {
|
||
|
+ LOG_ERR("Could not open file: \"%s\"", ctx.output_path);
|
||
|
+ goto error;
|
||
|
+ }
|
||
|
+
|
||
|
+ PEM_write_bio_TSSPRIVKEY(bio, tpk);
|
||
|
+ TSSPRIVKEY_free(tpk);
|
||
|
+ BIO_free(bio);
|
||
|
+
|
||
|
+ return tool_rc_success;
|
||
|
+ error:
|
||
|
+ if (bio)
|
||
|
+ BIO_free(bio);
|
||
|
+ if (tpk)
|
||
|
+ TSSPRIVKEY_free(tpk);
|
||
|
+ return tool_rc_general_error;
|
||
|
+}
|
||
|
+
|
||
|
+static tool_rc tpm2_tool_onrun(ESYS_CONTEXT *ectx, tpm2_option_flags flags) {
|
||
|
+ UNUSED(flags);
|
||
|
+
|
||
|
+ tool_rc rc = check_opts();
|
||
|
+ if (rc != tool_rc_success) {
|
||
|
+ return rc;
|
||
|
+ }
|
||
|
+
|
||
|
+ rc = init(ectx);
|
||
|
+ if (rc != tool_rc_success) {
|
||
|
+ return rc;
|
||
|
+ }
|
||
|
+
|
||
|
+ return encode();
|
||
|
+}
|
||
|
+
|
||
|
+// Register this tool with tpm2_tool.c
|
||
|
+TPM2_TOOL_REGISTER("encodeobject", tpm2_tool_onstart, tpm2_tool_onrun, NULL, NULL)
|
||
|
--
|
||
|
2.40.1
|
||
|
|