This command can be used for three things which allow a system to
build an ongoing trust relationship with the TPM.  For TPMs which
don't have EK certificates (most fTPMs) it allows a trust on first use
model where the EK is squirreled away in a permanent location on the
filesystem as:

attest_tpm2_primary --ek > /etc/eksign.name

Which generates a signing EK that can be used to certify other objects
and permanently stores the name in /etc (ideally this should be stored
in an immutable location on OS install).

If the TPM does have a signing certificate, the next step is to verify
the cert back to the manufacturer and bind it to the signing EK by
doing

attest_tpm2_primary --attest --name /etc/eksign.name <ek cert file>

This will run a local makecredential/activatecredential on the signing
EK using the public key in the <ek cert file>.  Once this happens the
TPM is proven to be a genuine discrete TPM.

Finally, having the permanent name file allows the signing EK to
certify the NULL key used by the kernel on every boot via

attest_tpm2_primary --certify null --name /etc/eksign.name 
/sys/class/tpm/tpm0/null_name

Since the null_name changes on every boot this allows a user
confidence that the TPM booted up correctly and isn't being snooped.

Additionally, the command can generate the public SRK for importable
keys by running a certification against the signing EK to verify it
isn't being spoofed:

attest_tpm2_primary --certify owner --name /etc/eksign.name --file srk.pub

Signed-off-by: James Bottomley <[email protected]>
---
 src/tools/Makefile.am           |   6 +-
 src/tools/attest_tpm2_primary.c | 842 ++++++++++++++++++++++++++++++++
 2 files changed, 847 insertions(+), 1 deletion(-)
 create mode 100644 src/tools/attest_tpm2_primary.c

diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am
index 572847c..7cca442 100644
--- a/src/tools/Makefile.am
+++ b/src/tools/Makefile.am
@@ -12,7 +12,7 @@ endif
 AM_CPPFLAGS = -I ../include ${DEPRECATION}
 
 bin_PROGRAMS=create_tpm2_key load_tpm2_key seal_tpm2_data unseal_tpm2_data \
-       signed_tpm2_policy
+       signed_tpm2_policy attest_tpm2_primary
 COMMONLIB = ../libcommon/libcommon.a
 
 create_tpm2_key_SOURCES=create_tpm2_key.c
@@ -35,6 +35,10 @@ signed_tpm2_policy_SOURCES=signed_tpm2_policy.c
 signed_tpm2_policy_LDADD=${COMMONLIB} ${DEPS_LIBS}
 signed_tpm2_policy_CFLAGS=${DEPS_CFLAGS}
 
+attest_tpm2_primary_SOURCES=attest_tpm2_primary.c
+attest_tpm2_primary_LDADD=${COMMONLIB} ${DEPS_LIBS}
+attest_tpm2_primary_CFLAGS=${DEPS_CFLAGS}
+
 $(builddir)/%.1: $(srcdir)/%.1.in $(builddir)/%
        $(HELP2MAN) --no-info -i $< -o $@ $(builddir)/$*
 
diff --git a/src/tools/attest_tpm2_primary.c b/src/tools/attest_tpm2_primary.c
new file mode 100644
index 0000000..cb252fe
--- /dev/null
+++ b/src/tools/attest_tpm2_primary.c
@@ -0,0 +1,842 @@
+/*
+ *
+ *   Copyright (C) 2024 James Bottomley <[email protected]>
+ *
+ *   SPDX-License-Identifier: LGPL-2.1-only
+ */
+
+
+#include <stdio.h>
+#include <getopt.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/pkcs12.h>
+#include <openssl/rand.h>
+#include <openssl/x509.h>
+
+#include "tpm2-tss.h"
+#include "tpm2-asn.h"
+#include "tpm2-common.h"
+
+/*
+ *
+ * --eksign [--name <namefile>] [--file <file>]
+ *
+ * create P-256 EK signing key and check the name against <namefile>
+ * If name check is requested and passes, output the pem public key to <file>.
+ *
+ * --attest  <certfile> [--name <namefile>] [--file <file>]
+ *
+ * derive the EK signing key and verify its name against <namefile>
+ * (if present).  Then attest it as a signing key against the EK
+ * certificate in <certfile> using makecredential/activate credential.
+ * Optionally output the pub pem to <file> if the certification
+ * works.
+ *
+ * --certify <h> --name <ekname> [--outname] [--file <file>] [<hname>]
+ *
+ * derive the storage primary in hierarchy <h> and validate the name
+ * against that in the <hname> file if present.  If it validates (or
+ * hname is not present), derive the EK P-256 signing key and validate
+ * the EK name is <ekname>. Finally, certify the key derived from <h>
+ * using EK which proves the TPM knows the secret part of EK if the
+ * certification signature verifies and that <h> is genuine.  If the
+ * signature verifies, return success and optionally output the PEM
+ * form of the public key of <h> to <file> and print the name.
+ *
+ */
+
+static struct option long_options[] = {
+       {"help", 0, 0, 'h'},
+       {"auth", 1, 0, 'a'},
+       {"password", 1, 0, 'k'},
+       {"eksign", 0, 0, 'E'},
+       {"attest", 1, 0, 'A'},
+       {"certify", 1, 0, 'C'},
+       {"name", 1, 0, 'n'},
+       {"outname", 0, 0, 'o'},
+       {"file", 1, 0, 'f'},
+       {0, 0, 0, 0}
+};
+
+void
+usage(char *argv0)
+{
+       fprintf(stdout, "Usage: %s {--eksign|--attest|--certify} [options]\n\n"
+               "Options:\n"
+               "\t-E, --eksign                  construct a restricted signing 
EK and\n"
+               "\t                              output its name in hex if no 
other options\n"
+               "\t                              are given.\n"
+               "\t-A, --attest <certfile>       Attest the ek signing key 
based on the EK\n"
+               "\t                              certificate (in <certfile>) to 
prove it\n"
+               "\t                              is genuine.\n"
+               "\t-C, --certify <handle>        construct the restricted 
storage key for\n"
+               "\t                              <handle> and certify it 
against the EK\n"
+               "\t                              signing key.  The name file 
for the EK\n"
+               "\t                              signing key must be 
provided.\n"
+               "\t-h, --help                    print this help message.\n"
+               "\t-a, --auth                    provide EK authorization.\n"
+               "\t-k, --password <pwd>          use this password instead of 
prompting.\n"
+               "\t-n, --name <file>             force the checking of the 
constructed\n"
+               "\t                              hierarchy signing key against 
the name\n"
+               "\t                              in the give file.  Fail if 
they do not\n"
+               "\t                              match.\n"
+               "\t-f, --file <file>             Output the constructed 
hierarchy signing\n"
+               "\t                              or storage public key as a PEM 
file\n"
+               "\t                              suitable for import or 
external\n"
+               "\t                              cryptosystem use.\n"
+               "\t-o, --outname                 Print out the name of the 
certified key\n"
+               "\n"
+               "Report bugs to " PACKAGE_BUGREPORT "\n",
+               argv0);
+       exit(-1);
+}
+
+static int verify_hexname(const char *namefile, char hexname[MAX_HEXNAME])
+{
+       int fd;
+       struct stat st;
+       const int len = strlen(hexname);
+       int rc = NOT_TPM_ERROR;
+       char name[MAX_HEXNAME];
+
+       if (stat(namefile, &st) == -1) {
+               fprintf(stderr, "File %s cannot be accessed\n", namefile);
+               return rc;
+       }
+
+       /* sysfs null name is always 4096 to stat, so only check for too small 
*/
+       if (st.st_size < len) {
+               fprintf(stderr, "Name file is too small should be at least 
%d\n",
+                       len);
+               return rc;
+       }
+
+       fd = open(namefile, O_RDONLY);
+       if (fd < 0) {
+               perror("namefile can't be opened");
+               return rc;
+       }
+
+       if (!read(fd, name, len)) {
+               perror("namefile can't be read");
+               goto out_close;
+       }
+
+       if (memcmp(name, hexname, len) == 0)
+               rc = 0;
+ out_close:
+       close(fd);
+
+       return rc;
+}
+
+static int write_pubkey(const char *file, TPMT_PUBLIC *pub)
+{
+       BIO *bf;
+       int rc;
+       EVP_PKEY *pkey = tpm2_to_openssl_public(pub);
+
+       if (!pkey)
+               goto openssl_err;
+       bf = BIO_new_file(file, "w");
+       if (!bf)
+               goto openssl_err;
+       rc = PEM_write_bio_PUBKEY(bf, pkey);
+       BIO_free(bf);
+       if (!rc)
+               goto openssl_err;
+
+       EVP_PKEY_free(pkey);
+       return 0;
+
+ openssl_err:
+       ERR_print_errors_fp(stderr);
+       if (pkey)
+               EVP_PKEY_free(pkey);
+       return NOT_TPM_ERROR;
+}
+
+/* analogue of tpm2_sign_digest from tpm2-common.c */
+static TPM_RC tpm2_verify_digest(EVP_PKEY *pkey, TPMT_HA *digest,
+                                TPMT_SIGNATURE *sig)
+{
+       EVP_PKEY_CTX *ctx;
+       ECDSA_SIG *esig;
+       BIGNUM *r, *s;
+       unsigned char *p = NULL;
+       int p_len;
+       TPM_RC rc = NOT_TPM_ERROR;
+
+       ctx = EVP_PKEY_CTX_new(pkey, NULL);
+       if (!ctx)
+               goto openssl_err;
+       esig = ECDSA_SIG_new();
+       if (!esig)
+               goto openssl_ctx;
+
+       r = BN_bin2bn(VAL_2B(sig->signature.ecdsa.signatureR, buffer),
+                     VAL_2B(sig->signature.ecdsa.signatureR, size), NULL);
+       s = BN_bin2bn(VAL_2B(sig->signature.ecdsa.signatureS, buffer),
+                     VAL_2B(sig->signature.ecdsa.signatureS, size), NULL);
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+       esig->r = r;
+       esig->s = s;
+#else
+       ECDSA_SIG_set0(esig, r, s);
+#endif
+
+       p_len = i2d_ECDSA_SIG(esig, &p);
+       ECDSA_SIG_free(esig);
+
+       EVP_PKEY_verify_init(ctx);
+
+       if (EVP_PKEY_verify(ctx, p, p_len, (unsigned char *)&digest->digest,
+                           SHA256_DIGEST_LENGTH) == 1)
+               rc = 0;
+
+       OPENSSL_free(p);
+
+ openssl_ctx:
+       EVP_PKEY_CTX_free(ctx);
+
+ openssl_err:
+       if (rc)
+               ERR_print_errors_fp(stderr);
+       return rc;
+}
+
+void do_certify(const char *auth, TPM_HANDLE handle, const char *namefile,
+               const char *file, const char *hname, int outname)
+{
+       TPM_HANDLE h, ek;
+       TPM2B_PUBLIC pubh, pubek;
+       char hexname[MAX_HEXNAME];
+       const char *dir = tpm2_set_unique_tssdir();
+       TSS_CONTEXT *tssContext;
+       int rc;
+       DATA_2B qualifying;
+       ATTEST_2B att;
+       TPMT_SIGNATURE sig;
+       EVP_PKEY *pkey;
+       TPMT_HA digest;
+       TPMS_ATTEST a;
+       NAME_2B tmpname;
+
+       if (!namefile) {
+               fprintf(stderr, "signing EK name must be specified in --name 
argument\n");
+               exit(1);
+       }
+
+       rc = tpm2_create(&tssContext, dir);
+       if (rc)
+               goto out_rmdir;
+
+       handle = tpm2_handle_int(tssContext, handle);
+
+       /*
+        * FIXME: Assumption here is that only the endorsement
+        * hierarchy may have an authority password.  However, this
+        * could be true of the owner hierarchy as well so might need
+        * an additional password parameter.
+        */
+       rc = tpm2_load_srk(tssContext, &h, NULL, &pubh, handle,
+                          TPM2_LOADABLE);
+       if (rc)
+               goto out_delete;
+
+       if (hname) {
+               tpm2_get_hexname(hexname, &pubh);
+               rc = verify_hexname(hname, hexname);
+               if (rc) {
+                       fprintf(stderr, "Error: Handle for certification failed 
name verification\n");
+                       goto out_free_h;
+               }
+       }
+
+       rc = tpm2_load_srk(tssContext, &ek, auth, &pubek, TPM_RH_ENDORSEMENT,
+                          TPM2_SIGNING);
+       if (rc)
+               goto out_free_h;
+
+       tpm2_get_hexname(hexname, &pubek);
+       rc = verify_hexname(namefile, hexname);
+       if (rc) {
+               fprintf(stderr, "Error: signing EK failed name verification\n");
+                       goto out_free_ek;
+       }
+       qualifying.size = SHA256_DIGEST_LENGTH;
+       RAND_bytes(qualifying.buffer, qualifying.size);
+
+       rc = tpm2_Certify(tssContext, h, ek, &qualifying, &att, &sig);
+       if (rc) {
+               tpm2_error(rc, "TPM2_Certify");
+               goto out_free_ek;
+       }
+
+       pkey = tpm2_to_openssl_public(&pubek.publicArea);
+       if (!pkey)
+               goto out_free_ek;
+
+       digest.hashAlg = TPM_ALG_SHA256;
+       TSS_Hash_Generate(&digest,
+                         att.size, att.attestationData,
+                         0, NULL);
+       rc = tpm2_verify_digest(pkey, &digest, &sig);
+       if (rc) {
+               fprintf(stderr, "verification of the cerification signature 
failed\n");
+               goto out_free_ek;
+       }
+
+       /*
+        * just proving the signature isn't enough, we need to verify
+        * the expected values in the attestation area to make sure
+        * this isn't a replay and that an attacker hasn't returned to
+        * us a genuinely signed and qualified attestation report
+        * containing differet keys.
+        */
+       {
+               BYTE *buffer = att.attestationData;
+               INT32 size = att.size;
+
+               rc = TPMS_ATTEST_Unmarshal(&a, &buffer, &size);
+       }
+       if (rc) {
+               tpm2_error(rc, "Unmarshalling attestation data");
+               goto out_free_ek;
+       }
+
+       rc = NOT_TPM_ERROR;
+
+       if (a.type != TPM_ST_ATTEST_CERTIFY) {
+               fprintf(stderr, "Error: Attestation isn't for certification\n");
+               goto out_free_ek;
+       }
+
+       if (memcmp(qualifying.buffer, VAL_2B(a.extraData, buffer),
+                  qualifying.size) != 0) {
+               fprintf(stderr, "Error: qualifying data is not correct\n");
+               goto out_free_ek;
+       }
+
+       {
+               /* construct qualifiedName for EK */
+               NAME_2B ekn;
+               BYTE buffer[sizeof(TPM_HANDLE)];
+               BYTE *buf = buffer;
+               UINT16 written =0;
+               INT32 size = sizeof(buffer);
+               const TPM_HANDLE perm_ek = EXT_TPM_RH_ENDORSEMENT;
+
+               tpm2_ObjectPublic_GetName(&ekn, &pubek.publicArea);
+               TSS_TPM_HANDLE_Marshal(&perm_ek, &written, &buf, &size);
+               digest.hashAlg = TPM_ALG_SHA256;
+               TSS_Hash_Generate(&digest,
+                                 written, buffer,
+                                 ekn.size, ekn.name,
+                                 0, NULL);
+
+               /* copy the leading hash algorithm */
+               memcpy(tmpname.name, ekn.name, 2);
+               memcpy(&tmpname.name[2], (char *)&digest.digest,
+                      SHA256_DIGEST_LENGTH);
+
+               if (memcmp(VAL_T(a.qualifiedSigner, name),
+                          tmpname.name, VAL_T(a.qualifiedSigner, size)) != 0) {
+                       fprintf(stderr, "Error: qualified signer does not match 
EK\n");
+                       goto out_free_ek;
+               }
+       }
+
+       /* finally the certified key name */
+       tpm2_ObjectPublic_GetName(&tmpname, &pubh.publicArea);
+       if (memcmp(VAL_T(a.attested.certify.name, name),
+                  tmpname.name, tmpname.size) != 0) {
+               fprintf(stderr, "Error: certified object name  does not 
match\n");
+               goto out_free_ek;
+       }
+
+       if (outname) {
+               tpm2_get_hexname(hexname, &pubh);
+               printf("%s\n", hexname);
+       } else {
+               printf("Good certification from TPM at %lu.%04d reset count 
%u\n",
+                      a.clockInfo.clock/1000, (int)(a.clockInfo.clock%1000),
+                      a.clockInfo.resetCount);
+       }
+       rc = 0;
+       if (file)
+               rc = write_pubkey(file, &pubh.publicArea);
+
+ out_free_ek:
+       tpm2_flush_srk(tssContext, ek);
+ out_free_h:
+       tpm2_flush_srk(tssContext, h);
+ out_delete:
+       TSS_Delete(tssContext);
+ out_rmdir:
+       if (dir)
+               rmdir(dir);
+
+       if (rc)
+               exit(1);
+}
+
+void do_ek(const char *auth, const char *namefile, const char *file)
+{
+       const char *dir = tpm2_set_unique_tssdir();
+       TSS_CONTEXT *tssContext;
+       TPM_RC rc;
+       TPM_HANDLE h;
+       TPM2B_PUBLIC pub;
+       char hexname[MAX_HEXNAME];
+
+       if (file && !namefile) {
+               fprintf(stderr, "for output of public key, require namefile to 
verify\n");
+               exit(1);
+       }
+
+       rc = tpm2_create(&tssContext, dir);
+       if (rc)
+               goto out_rmdir;
+       rc = tpm2_load_srk(tssContext, &h, auth, &pub, TPM_RH_ENDORSEMENT,
+                          TPM2_SIGNING);
+       tpm2_flush_srk(tssContext, h);
+       if (rc)
+               goto out_delete;
+
+       tpm2_get_hexname(hexname, &pub);
+
+       if (namefile) {
+               rc = verify_hexname(namefile, hexname);
+               if (rc)
+                       fprintf(stderr, "Error: signing EK failed name 
verification\n");
+       } else {
+               printf("%s\n", hexname);
+       }
+
+       if (file)
+               rc = write_pubkey(file, &pub.publicArea);
+
+ out_delete:
+       TSS_Delete(tssContext);
+ out_rmdir:
+       if (dir)
+               rmdir(dir);
+
+       if (rc)
+               exit(1);
+}
+
+/*
+ * Try to find a template to construct the EK matching pkey
+ */
+static const unsigned char tcgPolicy[][SHA256_DIGEST_LENGTH] = {
+       /* Policy A - Low */
+       {
+               0x83, 0x71, 0x97, 0x67, 0x44, 0x84, 0xB3, 0xF8,
+               0x1A, 0x90, 0xCC, 0x8D, 0x46, 0xA5, 0xD7, 0x24,
+               0xFD, 0x52, 0xD7, 0x6E, 0x06, 0x52, 0x0B, 0x64,
+               0xF2, 0xA1, 0xDA, 0x1B, 0x33, 0x14, 0x69, 0xAA,
+       },
+       /* Policy B - High */
+       {
+               0xca, 0x3d, 0x0a, 0x99, 0xa2, 0xb9, 0x39, 0x06,
+               0xf7, 0xa3, 0x34, 0x24, 0x14, 0xef, 0xcf, 0xb3,
+               0xa3, 0x85, 0xd4, 0x4c, 0xd1, 0xfd, 0x45, 0x90,
+               0x89, 0xd1, 0x9b, 0x50, 0x71, 0xc0, 0xb7, 0xa0,
+       },
+       /* Policy C - B is OR of A and C */
+       {
+               0x37, 0x67, 0xe2, 0xed, 0xd4, 0x3f, 0xf4, 0x5a,
+               0x3a, 0x7e, 0x1e, 0xae, 0xfc, 0xef, 0x78, 0x64,
+               0x3d, 0xca, 0x96, 0x46, 0x32, 0xe7, 0xaa, 0xd8,
+               0x2c, 0x67, 0x3a, 0x30, 0xd8, 0x63, 0x3f, 0xde,
+       },
+};
+
+void get_template(EVP_PKEY *pkey, TPMT_PUBLIC *tmpl, int high)
+{
+       tmpl->nameAlg = TPM_ALG_SHA256;
+       VAL(tmpl->objectAttributes) = TPMA_OBJECT_FIXEDTPM |
+               TPMA_OBJECT_FIXEDPARENT |
+               TPMA_OBJECT_SENSITIVEDATAORIGIN |
+               TPMA_OBJECT_ADMINWITHPOLICY |
+               TPMA_OBJECT_RESTRICTED |
+               TPMA_OBJECT_DECRYPT;
+       if (high)
+               VAL(tmpl->objectAttributes) |=
+                       TPMA_OBJECT_USERWITHAUTH;
+       VAL_T(tmpl->authPolicy, size) = sizeof(tcgPolicy[high]);
+       memcpy(&VAL_T(tmpl->authPolicy, buffer), tcgPolicy[high],
+                      sizeof(tcgPolicy[high]));
+       switch (EVP_PKEY_id(pkey)) {
+       case EVP_PKEY_RSA:
+               tmpl->type = TPM_ALG_RSA;
+               tmpl->parameters.rsaDetail.symmetric.algorithm = TPM_ALG_AES;
+               tmpl->parameters.rsaDetail.symmetric.keyBits.aes = 128;
+               tmpl->parameters.rsaDetail.symmetric.mode.aes = TPM_ALG_CFB;
+               tmpl->parameters.rsaDetail.scheme.scheme = TPM_ALG_NULL;
+               tmpl->parameters.rsaDetail.scheme.details.anySig.hashAlg = 0;
+               tmpl->parameters.rsaDetail.keyBits = 2048;
+               tmpl->parameters.rsaDetail.exponent = 0;
+               if (high) {
+                       VAL_T(tmpl->unique.rsa, size) = 0;
+               } else {
+                       VAL_T(tmpl->unique.rsa, size) = 256;
+                       memset(VAL_T(tmpl->unique.rsa, buffer), 0, 256);
+               }
+               break;
+       case EVP_PKEY_EC:
+               tmpl->type = TPM_ALG_ECC;
+               tmpl->parameters.eccDetail.symmetric.algorithm = TPM_ALG_AES;
+               tmpl->parameters.eccDetail.symmetric.keyBits.aes = 128;
+               tmpl->parameters.eccDetail.symmetric.mode.aes = TPM_ALG_CFB;
+               tmpl->parameters.eccDetail.scheme.scheme = TPM_ALG_NULL;
+               tmpl->parameters.eccDetail.scheme.details.anySig.hashAlg = 0;
+               tmpl->parameters.eccDetail.curveID = TPM_ECC_NIST_P256;
+               tmpl->parameters.eccDetail.kdf.scheme = TPM_ALG_NULL;
+               tmpl->parameters.eccDetail.kdf.details.mgf1.hashAlg = 0;
+               if (high) {
+                       VAL_T(tmpl->unique.ecc.x, size) = 0;
+                       VAL_T(tmpl->unique.ecc.y, size) = 0;
+               } else {
+                       VAL_T(tmpl->unique.ecc.x, size) = 32;
+                       memset(VAL_T(tmpl->unique.ecc.x, buffer), 0, 32);
+                       VAL_T(tmpl->unique.ecc.y, size) = 32;
+                       memset(VAL_T(tmpl->unique.ecc.y, buffer), 0, 32);
+               }
+               break;
+       default:
+               fprintf(stderr, "Unknown certificate key type\n");
+               exit(1);
+       }
+}
+
+int load_ek(TSS_CONTEXT *tssContext, TPM_HANDLE *h, int *highp,
+           const char *auth, EVP_PKEY *pkey)
+{
+       TPM2B_PUBLIC tmpl, target, pub;
+       TPM_RC rc;
+       int high;
+
+       rc = openssl_to_tpm_public(&target, pkey);
+
+       for (high = 0; high < 2; high++) {
+               get_template(pkey, &tmpl.publicArea, high);
+
+               rc = tpm2_load_srk_tmpl(tssContext, h, auth, &tmpl, &pub,
+                                       TPM_RH_ENDORSEMENT);
+               if (rc)
+                       return rc;
+
+               if (target.publicArea.type == TPM_ALG_RSA &&
+                   memcmp(VAL_T(target.publicArea.unique.rsa, buffer),
+                          VAL_T(pub.publicArea.unique.rsa, buffer),
+                          VAL_T(target.publicArea.unique.rsa, size)) == 0)
+                       break;
+               else if (target.publicArea.type == TPM_ALG_ECC &&
+                        memcmp(VAL_T(target.publicArea.unique.ecc.x, buffer),
+                               VAL_T(pub.publicArea.unique.ecc.x, buffer),
+                               VAL_T(target.publicArea.unique.ecc.x, size)) == 0
+                        &&
+                        memcmp(VAL_T(target.publicArea.unique.ecc.x, buffer),
+                               VAL_T(pub.publicArea.unique.ecc.x, buffer),
+                               VAL_T(target.publicArea.unique.ecc.x, size)) == 
0)
+                       break;
+
+               tpm2_flush_srk(tssContext, *h);
+               *h = 0;
+       }
+
+       if (*h == 0) {
+               fprintf(stderr, "Error: Can't find EK matching certificate\n");
+               return TPM_RC_ASYMMETRIC;
+       }
+       *highp = high;
+
+       return rc;
+}
+
+void do_attest(const char *auth, const char *namefile, const char *file,
+              const char *certfile)
+{
+       const char *dir = tpm2_set_unique_tssdir();
+       TSS_CONTEXT *tssContext;
+       BIO *bf;
+       X509 *x;
+       EVP_PKEY *pkey;
+       NAME_2B eksignname;
+       PRIVATE_2B private;
+       ENCRYPTED_SECRET_2B enc_secret;
+       uint8_t challenge[SHA256_DIGEST_LENGTH];
+       TPM_HANDLE ek, eksign;
+       TPM_RC rc = NOT_TPM_ERROR;
+       TPM2B_PUBLIC pubeksign;
+       char hexname[MAX_HEXNAME];
+       DIGEST_2B digest;
+       const int integrity_skip = SHA256_DIGEST_LENGTH + 2;
+       TPM_HANDLE policy;
+       BYTE *buf;
+       INT32 size;
+       UINT16 written = 0;
+       int high;
+
+       bf = BIO_new_file(certfile, "r");
+       if (!bf)
+               goto out;
+
+       x = PEM_read_bio_X509(bf, NULL, NULL, NULL);
+       if (!x) {
+               BIO_reset(bf);
+               x = d2i_X509_bio(bf, NULL);
+       }
+       BIO_free(bf);
+       if (!x) {
+               X509_free(x);
+               goto out;
+       }
+       ERR_clear_error();
+       pkey = X509_get_pubkey(x);
+       X509_free(x);
+       if (!pkey)
+               goto out;
+
+       RAND_bytes(challenge, sizeof(challenge));
+
+
+       rc = tpm2_create(&tssContext, dir);
+       if (rc)
+               goto out;
+
+       rc = tpm2_load_srk(tssContext, &eksign, auth, &pubeksign,
+                          TPM_RH_ENDORSEMENT, TPM2_SIGNING);
+       if (rc)
+               goto out;
+
+       tpm2_ObjectPublic_GetName(&eksignname, &pubeksign.publicArea);
+
+       if (namefile) {
+               bin2hex(hexname, (unsigned char *)eksignname.name,
+                       eksignname.size);
+               rc = verify_hexname(namefile, hexname);
+               if (rc) {
+                       fprintf(stderr, "Error: signing EK failed name 
verification\n");
+                       goto out_free_eksign;
+               }
+       }
+
+       /* run MakeCredential using just openssl */
+       memcpy(digest.buffer, challenge, sizeof(challenge));
+       digest.size = sizeof(challenge);
+       size = digest.size + 2; /* size of marshalled digest */
+       private.size = size + integrity_skip;
+       buf = &private.buffer[integrity_skip];
+       TSS_TPM2B_DIGEST_Marshal((TPM2B_DIGEST *)&digest, &written,
+                                &buf, &size);
+       rc = tpm2_hmacwrap(pkey, &eksignname, "IDENTITY", &private, 
&enc_secret);
+       if (rc)
+               goto out_free_eksign;
+
+       rc = load_ek(tssContext, &ek, &high, auth, pkey);
+       if (rc)
+               goto out_free_eksign;
+
+       /* now we have the EK, we can only use it with a policy */
+
+       rc = tpm2_get_session_handle(tssContext, &policy, 0, TPM_SE_POLICY,
+                                    TPM_ALG_SHA256);
+       if (rc)
+               goto out_free_ek;
+
+       /*
+        * two different policies but amounting to the same thing
+        *
+        * low is policy A which is PolicySecret on the endorsement
+        * hierarchy with a zero size policyRef.
+        *
+        * High is the PolicyOR of policy A (which we should satisfy)
+        * and policyC (which we don't bother with because it's not
+        * supported by all TPMs).
+        */
+       digest.size = 0;        /* empty policyRef */
+       rc = tpm2_PolicySecret(tssContext, TPM_RH_ENDORSEMENT, policy,
+                              &digest, auth);
+       if (rc) {
+               tpm2_error(rc, "tpm2_PolicySecret");
+               tpm2_FlushContext(tssContext, policy);
+               goto out_free_ek;
+       }
+
+       if (high) {
+               /* high is PolicyOR of PolicyA and PolicyC */
+               TPML_DIGEST digests;
+
+               digests.count = 2;
+               /* PolicyA = tcgPolicy[0] */
+               VAL_T(digests.digests[0], size) = sizeof(tcgPolicy[0]);
+               memcpy(VAL_T(digests.digests[0], buffer), tcgPolicy[0],
+                      VAL_T(digests.digests[0], size));
+               /* PolicyC = tcgPolicy[2] */
+               VAL_T(digests.digests[1], size) = sizeof(tcgPolicy[2]);
+               memcpy(VAL_T(digests.digests[1], buffer), tcgPolicy[2],
+                      VAL_T(digests.digests[1], size));
+
+               rc = tpm2_PolicyOR(tssContext, policy, &digests);
+               if (rc) {
+                       tpm2_error(rc, "tpm2_PolicyOR");
+                       tpm2_FlushContext(tssContext, policy);
+                       goto out_free_ek;
+               }
+       }
+
+       rc = tpm2_ActivateCredential(tssContext, eksign, ek,
+                                    (ID_OBJECT_2B *)&private, &enc_secret,
+                                    &digest, policy);
+       if (rc) {
+               tpm2_error(rc, "ActivateCredential");
+               tpm2_FlushContext(tssContext, policy);
+               goto out_free_ek;
+       }
+
+       if (memcmp(digest.buffer, challenge, sizeof(challenge)) != 0) {
+               fprintf(stderr, "Error: ActivateCredential returned incorrect 
challenge\n");
+               goto out_free_ek;
+       }
+
+       rc = 0;
+       printf("Attestation of signing EK successful\n");
+
+       if (file)
+               rc = write_pubkey(file, &pubeksign.publicArea);
+
+ out_free_ek:
+       tpm2_flush_srk(tssContext, ek);
+ out_free_eksign:
+       tpm2_flush_srk(tssContext, eksign);
+
+       TSS_Delete(tssContext);
+
+ out:
+       if (dir)
+               rmdir(dir);
+
+       if (ERR_peek_error()) {
+               rc = NOT_TPM_ERROR;
+               ERR_print_errors_fp(stderr);
+       }
+       if (rc)
+               exit(1);
+}
+
+int main(int argc, char **argv)
+{
+       char *auth = NULL, *pass = NULL;
+       char *file = NULL, *namefile = NULL;
+       char *handle_str = NULL, *certfile = NULL;
+       int ek = 0, outname = 0;
+       TPM_HANDLE handle;
+       const int max_auth_size = 128;
+#define ATTEST (certfile ? 1 : 0)
+#define CERTIFY (handle_str ? 1 : 0)
+
+
+       while (1) {
+               int option_index = 0, c;
+
+               c = getopt_long(argc, argv, "ak:EA:C:n:f:o",
+                               long_options, &option_index);
+               if (c == -1)
+                       break;
+
+               switch (c) {
+               case 'a':
+                       auth = malloc(max_auth_size + 1);
+                       break;
+               case 'k':
+                       pass = optarg;
+                       if (strlen(pass) > max_auth_size) {
+                               fprintf(stderr, "password is too long\n");
+                               exit(1);
+                       }
+                       break;
+               case 'E':
+                       ek = 1;
+                       break;
+               case 'A':
+                       certfile = optarg;
+                       break;
+               case 'C':
+                       handle_str = optarg;
+                       break;
+               case 'n':
+                       namefile = optarg;
+                       break;
+               case 'f':
+                       file = optarg;
+                       break;
+               case 'h':
+                       usage(argv[0]);
+                       break;
+               case 'o':
+                       outname = 1;
+                       break;
+               default:
+                       printf("Unknown option '%c'\n", c);
+                       usage(argv[0]);
+                       break;
+               }
+       }
+
+       if (optind < argc - 1 || ((ek || ATTEST) && optind != argc)) {
+               printf("Unexpected additional arguments\n");
+               usage(argv[0]);
+       }
+
+       if (ek + ATTEST + CERTIFY > 1) {
+               fprintf(stderr, "only one of --ek, --attest or --certify may be 
specified\n");
+               exit(1);
+       } else if (ek + ATTEST + CERTIFY == 0) {
+               fprintf(stderr, "at least one of --ek, --attest or --certify 
must be specified\n");
+               exit(1);
+       }
+
+       if (auth) {
+               if (pass)
+                       strcpy(auth, pass);
+               else
+                       EVP_read_pw_string(auth, max_auth_size,
+                                          "Enter EK hierarchy authority: ", 0);
+       }
+
+       if (ek) {
+               do_ek(auth, namefile, file);
+       } else if (certfile) {
+               do_attest(auth, namefile, file, certfile);
+       } else if (handle_str) {
+               const char *handlename = NULL;
+
+               handle = tpm2_get_parent_ext(handle_str);
+               if (!handle) {
+                       fprintf(stderr, "Invalid handle for certification\n");
+                       exit(1);
+               }
+
+               if (optind == argc - 1)
+                       handlename = argv[argc - 1];
+               do_certify(auth, handle, namefile, file, handlename, outname);
+       }
+}
-- 
2.35.3


Reply via email to