From ba7682dc511f4ef6bbb8a15ca3bb0edf67ec39ce Mon Sep 17 00:00:00 2001 From: Daiki Ueno 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 --- 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 +#include +#include +#include +#include +#include +#include +#include + +#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