aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/libstb/tss2/ibmtpm20tss/utils/ekutils.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/skiboot/libstb/tss2/ibmtpm20tss/utils/ekutils.c')
-rw-r--r--roms/skiboot/libstb/tss2/ibmtpm20tss/utils/ekutils.c2314
1 files changed, 2314 insertions, 0 deletions
diff --git a/roms/skiboot/libstb/tss2/ibmtpm20tss/utils/ekutils.c b/roms/skiboot/libstb/tss2/ibmtpm20tss/utils/ekutils.c
new file mode 100644
index 000000000..4e3fcbc4b
--- /dev/null
+++ b/roms/skiboot/libstb/tss2/ibmtpm20tss/utils/ekutils.c
@@ -0,0 +1,2314 @@
+/********************************************************************************/
+/* */
+/* EK Index Parsing Utilities (and more) */
+/* Written by Ken Goldman */
+/* IBM Thomas J. Watson Research Center */
+/* */
+/* (c) Copyright IBM Corporation 2016 - 2019. */
+/* */
+/* All rights reserved. */
+/* */
+/* Redistribution and use in source and binary forms, with or without */
+/* modification, are permitted provided that the following conditions are */
+/* met: */
+/* */
+/* Redistributions of source code must retain the above copyright notice, */
+/* this list of conditions and the following disclaimer. */
+/* */
+/* Redistributions in binary form must reproduce the above copyright */
+/* notice, this list of conditions and the following disclaimer in the */
+/* documentation and/or other materials provided with the distribution. */
+/* */
+/* Neither the names of the IBM Corporation nor the names of its */
+/* contributors may be used to endorse or promote products derived from */
+/* this software without specific prior written permission. */
+/* */
+/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */
+/* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */
+/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR */
+/* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT */
+/* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */
+/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */
+/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, */
+/* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY */
+/* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */
+/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE */
+/* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+/********************************************************************************/
+
+/* These functions are worthwhile sample code that probably (judgment call) do not belong in the
+ TSS library.
+
+ They started as code to manipulate EKs, EK templates, and EK certificates.
+
+ Other useful X509 certificate crypto functions are migrating here. Much of it is OpenSSL
+ specific, but it also provides examples of how to port from OpenSSL 1.0 to 1.1.
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <limits.h>
+
+/* Windows 10 crypto API clashes with openssl */
+#ifdef TPM_WINDOWS
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#endif
+
+#include <openssl/pem.h>
+#include <openssl/x509.h>
+
+#include <ibmtss/tssresponsecode.h>
+#include <ibmtss/tssutils.h>
+#include <ibmtss/tsscrypto.h>
+#include <ibmtss/tssprint.h>
+#include <ibmtss/Unmarshal_fp.h>
+
+#include "cryptoutils.h"
+#include "ekutils.h"
+
+/* windows apparently uses _MAX_PATH in stdlib.h */
+#ifndef PATH_MAX
+#ifdef _MAX_PATH
+#define PATH_MAX _MAX_PATH
+#else
+/* Debian/Hurd does not define MAX_PATH */
+#define PATH_MAX 4096
+#endif
+#endif
+
+/* The print flag is set by the caller, depending on whether it wants information displayed.
+
+ tssUtilsVerbose is a global, used for verbose debug print
+
+ Errors are always printed.
+*/
+
+extern int tssUtilsVerbose;
+
+#ifdef TPM_TPM20
+
+/* readNvBufferMax() determines the maximum NV read/write block size. The limit is typically set by
+ the TPM property TPM_PT_NV_BUFFER_MAX. However, it's possible that a value could be larger than
+ the TSS side structure MAX_NV_BUFFER_SIZE.
+*/
+
+TPM_RC readNvBufferMax(TSS_CONTEXT *tssContext,
+ uint32_t *nvBufferMax)
+{
+ TPM_RC rc = 0;
+ GetCapability_In in;
+ GetCapability_Out out;
+
+ in.capability = TPM_CAP_TPM_PROPERTIES;
+ in.property = TPM_PT_NV_BUFFER_MAX;
+ in.propertyCount = 1; /* ask for one property */
+ if (rc == 0) {
+ rc = TSS_Execute(tssContext,
+ (RESPONSE_PARAMETERS *)&out,
+ (COMMAND_PARAMETERS *)&in,
+ NULL,
+ TPM_CC_GetCapability,
+ TPM_RH_NULL, NULL, 0);
+ }
+ /* sanity check that the property name is correct (demo of how to parse the structure) */
+ if (rc == 0) {
+ if ((out.capabilityData.data.tpmProperties.count > 0) &&
+ (out.capabilityData.data.tpmProperties.tpmProperty[0].property ==
+ TPM_PT_NV_BUFFER_MAX)) {
+ *nvBufferMax = out.capabilityData.data.tpmProperties.tpmProperty[0].value;
+ }
+ else {
+ if (tssUtilsVerbose) printf("readNvBufferMax: wrong property returned: %08x\n",
+ out.capabilityData.data.tpmProperties.tpmProperty[0].property);
+ /* hard code a value for a back level HW TPM that does not implement
+ TPM_PT_NV_BUFFER_MAX yet */
+ *nvBufferMax = 512;
+ }
+ if (tssUtilsVerbose) printf("readNvBufferMax: TPM max read/write: %u\n", *nvBufferMax);
+ /* in addition, the maximum TSS side structure MAX_NV_BUFFER_SIZE is accounted for. The TSS
+ value is typically larger than the TPM value. */
+ if (*nvBufferMax > MAX_NV_BUFFER_SIZE) {
+ *nvBufferMax = MAX_NV_BUFFER_SIZE;
+ }
+ if (tssUtilsVerbose) printf("readNvBufferMax: combined max read/write: %u\n", *nvBufferMax);
+ }
+ else {
+ const char *msg;
+ const char *submsg;
+ const char *num;
+ printf("getcapability: failed, rc %08x\n", rc);
+ TSS_ResponseCode_toString(&msg, &submsg, &num, rc);
+ printf("%s%s%s\n", msg, submsg, num);
+ rc = EXIT_FAILURE;
+ }
+ return rc;
+}
+
+/* getIndexSize() uses TPM2_NV_ReadPublic() to return the NV index size */
+
+TPM_RC getIndexSize(TSS_CONTEXT *tssContext,
+ uint16_t *dataSize,
+ TPMI_RH_NV_INDEX nvIndex)
+{
+ TPM_RC rc = 0;
+ NV_ReadPublic_In in;
+ NV_ReadPublic_Out out;
+
+ if (rc == 0) {
+ /* if (tssUtilsVerbose) printf("getIndexSize: index %08x\n", nvIndex); */
+ in.nvIndex = nvIndex;
+ }
+ /* call TSS to execute the command */
+ if (rc == 0) {
+ rc = TSS_Execute(tssContext,
+ (RESPONSE_PARAMETERS *)&out,
+ (COMMAND_PARAMETERS *)&in,
+ NULL,
+ TPM_CC_NV_ReadPublic,
+ TPM_RH_NULL, NULL, 0);
+ /* only print if verbose, since EK nonce and template index may not exist */
+ if ((rc != 0) && tssUtilsVerbose) {
+ const char *msg;
+ const char *submsg;
+ const char *num;
+ printf("nvreadpublic: failed, rc %08x\n", rc);
+ TSS_ResponseCode_toString(&msg, &submsg, &num, rc);
+ printf("%s%s%s\n", msg, submsg, num);
+ }
+ }
+ if (rc == 0) {
+ /* if (tssUtilsVerbose) printf("getIndexSize: size %u\n", out.nvPublic.t.nvPublic.dataSize); */
+ *dataSize = out.nvPublic.nvPublic.dataSize;
+ }
+ return rc;
+}
+
+/* getIndexData() uses TPM2_NV_Read() to return the NV index contents.
+
+ It assumes index authorization with an empty password
+*/
+
+TPM_RC getIndexData(TSS_CONTEXT *tssContext,
+ unsigned char **readBuffer, /* freed by caller */
+ TPMI_RH_NV_INDEX nvIndex,
+ uint16_t readDataSize) /* total size to read */
+{
+ TPM_RC rc = 0;
+ int done = FALSE;
+ uint32_t nvBufferMax;
+ uint16_t bytesRead; /* bytes read so far */
+ NV_Read_In in;
+ NV_Read_Out out;
+
+ /* data may have to be read in chunks. Read the TPM_PT_NV_BUFFER_MAX, the chunk size */
+ if (rc == 0) {
+ rc = readNvBufferMax(tssContext,
+ &nvBufferMax);
+ }
+ if (rc == 0) {
+ if (tssUtilsVerbose) printf("getIndexData: index %08x\n", nvIndex);
+ in.authHandle = nvIndex; /* index authorization */
+ in.nvIndex = nvIndex;
+ in.offset = 0; /* start at beginning */
+ bytesRead = 0; /* bytes read so far */
+ }
+ if (rc == 0) {
+ rc = TSS_Malloc(readBuffer, readDataSize);
+ }
+ /* call TSS to execute the command */
+ while ((rc == 0) && !done) {
+ if (rc == 0) {
+ /* read a chunk */
+ in.offset = bytesRead;
+ if ((uint32_t)(readDataSize - bytesRead) < nvBufferMax) {
+ in.size = readDataSize - bytesRead; /* last chunk */
+ }
+ else {
+ in.size = nvBufferMax; /* next chunk */
+ }
+ }
+ if (rc == 0) {
+ rc = TSS_Execute(tssContext,
+ (RESPONSE_PARAMETERS *)&out,
+ (COMMAND_PARAMETERS *)&in,
+ NULL,
+ TPM_CC_NV_Read,
+ TPM_RS_PW, NULL, 0,
+ TPM_RH_NULL, NULL, 0);
+ if (rc != 0) {
+ const char *msg;
+ const char *submsg;
+ const char *num;
+ printf("nvread: failed, rc %08x\n", rc);
+ TSS_ResponseCode_toString(&msg, &submsg, &num, rc);
+ printf("%s%s%s\n", msg, submsg, num);
+ }
+ }
+ /* copy the results to the read buffer */
+ if (rc == 0) {
+ memcpy(*readBuffer + bytesRead, out.data.b.buffer, out.data.b.size);
+ bytesRead += out.data.b.size;
+ if (bytesRead == readDataSize) {
+ done = TRUE;
+ }
+ }
+ }
+ return rc;
+}
+
+/* getIndexContents() uses TPM2_NV_ReadPublic() to get the NV index size, then uses TPM2_NV_Read()
+ to read the entire contents.
+
+*/
+
+TPM_RC getIndexContents(TSS_CONTEXT *tssContext,
+ unsigned char **readBuffer, /* freed by caller */
+ uint16_t *readBufferSize, /* total size read */
+ TPMI_RH_NV_INDEX nvIndex)
+{
+ TPM_RC rc = 0;
+
+ /* first read the public index size */
+ if (rc == 0) {
+ rc = getIndexSize(tssContext, readBufferSize, nvIndex);
+ }
+ /* read the entire index */
+ if (rc == 0) {
+ rc = getIndexData(tssContext,
+ readBuffer, /* freed by caller */
+ nvIndex,
+ *readBufferSize); /* total size to read */
+ }
+ return rc;
+}
+
+/* IWG (TCG Infrastructure Work Group) default EK primary key policy */
+
+static const unsigned char iwgPolicy[] = {
+ 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
+};
+
+/* RSA EK primary key IWG default template */
+
+void getRsaTemplate(TPMT_PUBLIC *tpmtPublic)
+{
+ tpmtPublic->type = TPM_ALG_RSA;
+ tpmtPublic->nameAlg = TPM_ALG_SHA256;
+ tpmtPublic->objectAttributes.val = TPMA_OBJECT_FIXEDTPM |
+ TPMA_OBJECT_FIXEDPARENT |
+ TPMA_OBJECT_SENSITIVEDATAORIGIN |
+ TPMA_OBJECT_ADMINWITHPOLICY |
+ TPMA_OBJECT_RESTRICTED |
+ TPMA_OBJECT_DECRYPT;
+ tpmtPublic->authPolicy.t.size = 32;
+ memcpy(&tpmtPublic->authPolicy.t.buffer, iwgPolicy, 32);
+ tpmtPublic->parameters.rsaDetail.symmetric.algorithm = TPM_ALG_AES;
+ tpmtPublic->parameters.rsaDetail.symmetric.keyBits.aes = 128;
+ tpmtPublic->parameters.rsaDetail.symmetric.mode.aes = TPM_ALG_CFB;
+ tpmtPublic->parameters.rsaDetail.scheme.scheme = TPM_ALG_NULL;
+ tpmtPublic->parameters.rsaDetail.scheme.details.anySig.hashAlg = 0;
+ tpmtPublic->parameters.rsaDetail.keyBits = 2048;
+ tpmtPublic->parameters.rsaDetail.exponent = 0;
+ tpmtPublic->unique.rsa.t.size = 256;
+ memset(&tpmtPublic->unique.rsa.t.buffer, 0, 256);
+ return;
+}
+
+/* ECC EK primary key IWG default template */
+
+void getEccTemplate(TPMT_PUBLIC *tpmtPublic)
+{
+ tpmtPublic->type = TPM_ALG_ECC;
+ tpmtPublic->nameAlg = TPM_ALG_SHA256;
+ tpmtPublic->objectAttributes.val = TPMA_OBJECT_FIXEDTPM |
+ TPMA_OBJECT_FIXEDPARENT |
+ TPMA_OBJECT_SENSITIVEDATAORIGIN |
+ TPMA_OBJECT_ADMINWITHPOLICY |
+ TPMA_OBJECT_RESTRICTED |
+ TPMA_OBJECT_DECRYPT;
+ tpmtPublic->authPolicy.t.size = sizeof(iwgPolicy);
+ memcpy(tpmtPublic->authPolicy.t.buffer, iwgPolicy, sizeof(iwgPolicy));
+ tpmtPublic->parameters.eccDetail.symmetric.algorithm = TPM_ALG_AES;
+ tpmtPublic->parameters.eccDetail.symmetric.keyBits.aes = 128;
+ tpmtPublic->parameters.eccDetail.symmetric.mode.aes = TPM_ALG_CFB;
+ tpmtPublic->parameters.eccDetail.scheme.scheme = TPM_ALG_NULL;
+ tpmtPublic->parameters.eccDetail.scheme.details.anySig.hashAlg = 0;
+ tpmtPublic->parameters.eccDetail.curveID = TPM_ECC_NIST_P256;
+ tpmtPublic->parameters.eccDetail.kdf.scheme = TPM_ALG_NULL;
+ tpmtPublic->parameters.eccDetail.kdf.details.mgf1.hashAlg = 0;
+ tpmtPublic->unique.ecc.x.t.size = 32;
+ memset(&tpmtPublic->unique.ecc.x.t.buffer, 0, 32);
+ tpmtPublic->unique.ecc.y.t.size = 32;
+ memset(&tpmtPublic->unique.ecc.y.t.buffer, 0, 32);
+ return;
+}
+
+/* getIndexX509Certificate() reads the X509 certificate from the nvIndex and converts the DER
+ (binary) to OpenSSL X509 format
+
+*/
+
+TPM_RC getIndexX509Certificate(TSS_CONTEXT *tssContext,
+ void **certificate, /* freed by caller */
+ TPMI_RH_NV_INDEX nvIndex)
+{
+ TPM_RC rc = 0;
+ unsigned char *certData = NULL; /* freed @1 */
+ uint16_t certSize;
+
+ /* read the certificate from NV to a DER stream */
+ if (rc == 0) {
+ rc = getIndexContents(tssContext,
+ &certData,
+ &certSize,
+ nvIndex);
+ }
+ /* unmarshal the DER stream to an OpenSSL X509 structure */
+ if (rc == 0) {
+ unsigned char *tmpData = NULL;
+ tmpData = certData; /* tmp pointer because d2i moves the pointer */
+ *certificate = d2i_X509(NULL, /* freed by caller */
+ (const unsigned char **)&tmpData, certSize);
+ if (*certificate == NULL) {
+ printf("getIndexX509Certificate: Could not parse X509 certificate\n");
+ rc = TPM_RC_INTEGRITY;
+ }
+ }
+ free(certData); /* @1 */
+ return rc;
+}
+
+#endif /* TPM20 */
+
+#ifndef TPM_TSS_NOFILE
+#ifndef TPM_TSS_NORSA
+
+/* getPubkeyFromDerCertFile() gets an OpenSSL RSA public key token from a DER format X509
+ certificate stored in a file.
+
+ Returns both the OpenSSL X509 certificate token and RSA public key token.
+*/
+
+uint32_t getPubkeyFromDerCertFile(RSA **rsaPkey,
+ X509 **x509,
+ const char *derCertificateFileName)
+{
+ uint32_t rc = 0;
+ FILE *fp = NULL;
+
+ /* open the file */
+ if (rc == 0) {
+ fp = fopen(derCertificateFileName, "rb");
+ if (fp == NULL) {
+ printf("getPubkeyFromDerCertFile: Error opening %s\n", derCertificateFileName);
+ rc = TSS_RC_FILE_OPEN;
+ }
+ }
+ /* read the file and convert the X509 DER to OpenSSL format */
+ if (rc == 0) {
+ *x509 = d2i_X509_fp(fp, NULL);
+ if (*x509 == NULL) {
+ printf("getPubkeyFromDerCertFile: Error converting %s\n", derCertificateFileName);
+ rc = TSS_RC_X509_ERROR;
+ }
+ }
+ /* extract the OpenSSL format public key from the X509 token */
+ if (rc == 0) {
+ rc = getPubKeyFromX509Cert(rsaPkey, *x509);
+ }
+ /* for debug, print the X509 certificate */
+ if (rc == 0) {
+ if (tssUtilsVerbose) X509_print_fp(stdout, *x509);
+ }
+ if (fp != NULL) {
+ fclose(fp);
+ }
+ return rc;
+}
+
+#endif /* TPM_TSS_NORSA */
+#endif /* TPM_TSS_NOFILE */
+
+#ifndef TPM_TSS_NORSA
+
+/* getPubKeyFromX509Cert() gets an OpenSSL RSA public key token from an OpenSSL X509 certificate
+ token. */
+
+uint32_t getPubKeyFromX509Cert(RSA **rsaPkey,
+ X509 *x509)
+{
+ uint32_t rc = 0;
+ EVP_PKEY *evpPkey = NULL;
+
+ if (rc == 0) {
+ evpPkey = X509_get_pubkey(x509); /* freed @1 */
+ if (evpPkey == NULL) {
+ printf("getPubKeyFromX509Cert: X509_get_pubkey failed\n");
+ rc = TSS_RC_X509_ERROR;
+ }
+ }
+ if (rc == 0) {
+ *rsaPkey = EVP_PKEY_get1_RSA(evpPkey);
+ if (*rsaPkey == NULL) {
+ printf("getPubKeyFromX509Cert: EVP_PKEY_get1_RSA failed\n");
+ rc = TSS_RC_X509_ERROR;
+ }
+ }
+ if (evpPkey != NULL) {
+ EVP_PKEY_free(evpPkey); /* @1 */
+ }
+ return rc;
+}
+#endif /* TPM_TSS_NORSA */
+
+#ifndef TPM_TSS_NOFILE
+
+/* getRootCertificateFilenames() reads listFilename, which is a list of filenames. The intent is
+ that the filenames are a list of EK TPM vendor root certificates in PEM format.
+
+ It accepts up to MAX_ROOTS filenames, which is a #define.
+
+*/
+
+TPM_RC getRootCertificateFilenames(char *rootFilename[],
+ unsigned int *rootFileCount,
+ const char *listFilename,
+ int print)
+{
+ TPM_RC rc = 0;
+ int done = 0;
+ FILE *listFile = NULL; /* closed @1 */
+
+ *rootFileCount = 0;
+
+ if (rc == 0) {
+ listFile = fopen(listFilename, "rb"); /* closed @1 */
+ if (listFile == NULL) {
+ printf("getRootCertificateFilenames: Error opening list file %s\n",
+ listFilename);
+ rc = TSS_RC_FILE_OPEN;
+ }
+ }
+ while ((rc == 0) && !done && (*rootFileCount < MAX_ROOTS)) {
+ size_t rootFilenameLength;
+ if (rc == 0) {
+ rootFilename[*rootFileCount] = malloc(PATH_MAX);
+ if (rootFilename[*rootFileCount] == NULL) {
+ printf("getRootCertificateFilenames: Error allocating memory\n");
+ rc = TSS_RC_OUT_OF_MEMORY;
+ }
+ }
+ if (rc == 0) {
+ char *tmpptr = fgets(rootFilename[*rootFileCount], PATH_MAX-1, listFile);
+ if (tmpptr == NULL) { /* end of file */
+ free(rootFilename[*rootFileCount]); /* free malloced but unused entry */
+ done = 1;
+ }
+ }
+ if ((rc == 0) && !done) {
+ rootFilenameLength = strlen(rootFilename[*rootFileCount]);
+ if (rootFilename[*rootFileCount][rootFilenameLength-1] != '\n') {
+ printf("getRootCertificateFilenames: filename %s too long\n",
+ rootFilename[*rootFileCount]);
+ rc = TSS_RC_OUT_OF_MEMORY;
+ free(rootFilename[*rootFileCount]); /* free malloced but bad entry */
+ done = 1;
+ }
+ }
+ if ((rc == 0) && !done) {
+ rootFilename[*rootFileCount][rootFilenameLength-1] = '\0'; /* remove newline */
+ if (print) printf("getRootCertificateFilenames: Root file name %u\n%s\n",
+ *rootFileCount, rootFilename[*rootFileCount]);
+ (*rootFileCount)++;
+ }
+ }
+ if (listFile != NULL) {
+ fclose(listFile); /* @1 */
+ }
+ return rc;
+}
+
+#endif
+
+#ifndef TPM_TSS_NOFILE
+
+/* getCaStore() creates an OpenSSL X509_STORE, populated by the root certificates in the
+ rootFilename array. Depending on the vendor, some certificates may be intermediate certificates.
+ OpenSSL handles this internally by walking the chain back to the root.
+
+ The caCert array is returned because it must be freed after the caStore is freed
+
+ NOTE: There is no TPM interaction.
+*/
+
+TPM_RC getCaStore(X509_STORE **caStore, /* freed by caller */
+ X509 *caCert[], /* freed by caller */
+ const char *rootFilename[],
+ unsigned int rootFileCount)
+{
+ TPM_RC rc = 0;
+ FILE *caCertFile = NULL; /* closed @1 */
+ unsigned int i;
+
+ if (rc == 0) {
+ *caStore = X509_STORE_new();
+ if (*caStore == NULL) {
+ printf("getCaStore: X509_store_new failed\n");
+ rc = TSS_RC_OUT_OF_MEMORY;
+ }
+ }
+ for (i = 0 ; (i < rootFileCount) && (rc == 0) ; i++) {
+ /* read a root certificate from the file */
+ caCertFile = fopen(rootFilename[i], "rb"); /* closed @1 */
+ if (caCertFile == NULL) {
+ printf("getCaStore: Error opening CA root certificate file %s\n",
+ rootFilename[i]);
+ rc = TSS_RC_FILE_OPEN;
+ }
+ /* convert the root certificate from PEM to X509 */
+ if (rc == 0) {
+ caCert[i] = PEM_read_X509(caCertFile, NULL, NULL, NULL); /* freed by caller */
+ if (caCert[i] == NULL) {
+ printf("getCaStore: Error reading CA root certificate file %s\n",
+ rootFilename[i]);
+ rc = TSS_RC_FILE_READ;
+ }
+ }
+ if ((rc == 0) && tssUtilsVerbose) {
+ X509_NAME *x509Name;
+ char *subject = NULL;
+ x509Name = X509_get_subject_name(caCert[i]);
+ subject = X509_NAME_oneline(x509Name, NULL, 0);
+ printf("getCaStore: subject %u: %s\n", i, subject);
+ OPENSSL_free(subject);
+ }
+
+ /* add the CA X509 certificate to the certificate store */
+ if (rc == 0) {
+ X509_STORE_add_cert(*caStore, caCert[i]);
+ }
+ if (caCertFile != NULL) {
+ fclose(caCertFile); /* @1 */
+ caCertFile = NULL;
+ }
+ }
+ return rc;
+}
+
+#endif
+
+#ifndef TPM_TSS_NOFILE
+
+/* verifyCertificate() verifies a certificate (typically an EK certificate against the root CA
+ certificate (typically the TPM vendor CA certificate chain)
+
+ The 'rootFileCount' root certificates are stored in the files whose paths are in the array
+ 'rootFilename'
+
+*/
+
+TPM_RC verifyCertificate(void *x509Certificate,
+ const char *rootFilename[],
+ unsigned int rootFileCount,
+ int print)
+{
+ TPM_RC rc = 0;
+ unsigned int i;
+ X509_STORE *caStore = NULL; /* freed @1 */
+ X509 *caCert[MAX_ROOTS]; /* freed @2 */
+ X509_STORE_CTX *verifyCtx = NULL; /* freed @3 */
+
+ for (i = 0 ; i < rootFileCount ; i++) {
+ caCert[i] = NULL; /* for free @2 */
+ }
+ /* get the root CA certificate chain */
+ if (rc == 0) {
+ rc = getCaStore(&caStore, /* freed @1 */
+ caCert, /* freed @2 */
+ rootFilename,
+ rootFileCount);
+ }
+ /* create the certificate verify context */
+ if (rc == 0) {
+ verifyCtx = X509_STORE_CTX_new(); /* freed @3 */
+ if (verifyCtx == NULL) {
+ printf("verifyCertificate: X509_STORE_CTX_new failed\n");
+ rc = TSS_RC_OUT_OF_MEMORY;
+ }
+ }
+ /* add the root certificate store and EK certificate to be verified to the verify context */
+ if (rc == 0) {
+ int irc = X509_STORE_CTX_init(verifyCtx,
+ caStore, /* trusted certificates */
+ x509Certificate, /* end entity certificate */
+ NULL); /* untrusted (intermediate) certificates */
+ if (irc != 1) {
+ printf("verifyCertificate: "
+ "Error in X509_STORE_CTX_init initializing verify context\n");
+ rc = TSS_RC_RSA_SIGNATURE;
+ }
+ }
+ /* walk the certificate chain */
+ if (rc == 0) {
+ int irc = X509_verify_cert(verifyCtx);
+ if (irc != 1) {
+ printf("verifyCertificate: Error in X509_verify_cert verifying certificate\n");
+ rc = TSS_RC_RSA_SIGNATURE;
+ }
+ else {
+ if (print) printf("EK certificate verified against the root\n");
+ }
+ }
+ if (caStore != NULL) {
+ X509_STORE_free(caStore); /* @1 */
+ }
+ for (i = 0 ; i < rootFileCount ; i++) {
+ X509_free(caCert[i]); /* @2 */
+ }
+ if (verifyCtx != NULL) {
+ X509_STORE_CTX_free(verifyCtx); /* @3 */
+ }
+ return rc;
+}
+
+/* verifyKeyUsage() validates the key usage for an EK.
+
+ If the EK has the decrypt attribute set, the keyEncipherment bit MUST be set for an RSA EK
+ certificate; the keyAgreement bit MUST be set for an ECC EK certificate.
+*/
+
+TPM_RC verifyKeyUsage(X509 *ekX509Certificate, /* X509 certificate */
+ int pkeyType, /* RSA or ECC */
+ int print)
+{
+ TPM_RC rc = 0;
+ ASN1_BIT_STRING *keyUsage = NULL;
+ uint8_t bitmap;
+ int keyAgreement; /* boolean flags */
+ int keyEncipherment;
+
+ if (rc == 0) {
+ keyUsage = X509_get_ext_d2i(ekX509Certificate, NID_key_usage, /* freed @1 */
+ NULL, NULL);
+ if (keyUsage == NULL) {
+ printf("verifyKeyUsage: Cannot find key usage\n");
+ rc = TSS_RC_X509_ERROR;
+ }
+ }
+ if (rc == 0) {
+ if (keyUsage->length == 0) {
+ printf("verifyKeyUsage: Key usage length 0 bytes\n");
+ rc = TSS_RC_X509_ERROR;
+ }
+ }
+ if (rc == 0) {
+ bitmap = keyUsage->data[0];
+ keyEncipherment = bitmap & (1<<5); /* bit 2 little endian */
+ keyAgreement = bitmap & (1<<3); /* bit 4 little endian */
+ if (keyEncipherment) { /* bit 2 little endian */
+ if (print) printf("verifyKeyUsage: Key Encipherment\n");
+ }
+ if (keyAgreement) { /* bit 4 little endian */
+ if (print) printf("verifyKeyUsage: Key Agreement\n");
+ }
+ if (pkeyType == EVP_PKEY_RSA) {
+ if (!keyEncipherment) {
+ printf("ERROR: verifyKeyUsage: RSA Key usage %02x not Key Encipherment\n",
+ bitmap);
+ rc = TSS_RC_X509_ERROR;
+ }
+ }
+ else if (pkeyType == EVP_PKEY_EC) {
+ /* ECC should be key agreement, but some HW TPMs use key encipherment */
+ if (!keyEncipherment && !keyAgreement) {
+ printf("ERROR: verifyKeyUsage: ECC Key usage %02x not "
+ "Key agreement or key encipherment\n",
+ bitmap);
+ rc = TSS_RC_X509_ERROR;
+ }
+ }
+ else {
+ printf("ERROR: verifyKeyUsage: Public key is not RSA or ECC\n");
+ rc = TSS_RC_X509_ERROR;
+ }
+ }
+ if (keyUsage != NULL) {
+ ASN1_BIT_STRING_free(keyUsage); /* @1 */
+ }
+ return rc;
+}
+
+#endif /* TPM_TSS_NOFILE */
+
+#ifdef TPM_TPM20
+
+/* processEKNonce()reads the EK nonce from NV and returns the contents and size */
+
+TPM_RC processEKNonce(TSS_CONTEXT *tssContext,
+ unsigned char **nonce, /* freed by caller */
+ uint16_t *nonceSize,
+ TPMI_RH_NV_INDEX ekNonceIndex,
+ int print)
+{
+ TPM_RC rc = 0;
+
+ if (rc == 0) {
+ rc = getIndexContents(tssContext,
+ nonce,
+ nonceSize,
+ ekNonceIndex);
+ }
+ /* optional tracing */
+ if (rc == 0) {
+ if (print) TSS_PrintAll("EK Nonce: ", *nonce, *nonceSize);
+ }
+ return rc;
+}
+
+/* processEKTemplate() reads the EK template from NV and returns the unmarshaled TPMT_PUBLIC */
+
+TPM_RC processEKTemplate(TSS_CONTEXT *tssContext,
+ TPMT_PUBLIC *tpmtPublic,
+ TPMI_RH_NV_INDEX ekTemplateIndex,
+ int print)
+{
+ TPM_RC rc = 0;
+ uint16_t dataSize;
+ unsigned char *data = NULL; /* freed @1 */
+ uint32_t tmpDataSize;
+ unsigned char *tmpData = NULL;
+
+ if (rc == 0) {
+ rc = getIndexContents(tssContext,
+ &data,
+ &dataSize,
+ ekTemplateIndex);
+ }
+ /* unmarshal the data stream */
+ if (rc == 0) {
+ tmpData = data; /* temps because unmarshal moves the pointers */
+ tmpDataSize = dataSize;
+ rc = TSS_TPMT_PUBLIC_Unmarshalu(tpmtPublic, &tmpData, &tmpDataSize, YES);
+ }
+ /* optional tracing */
+ if (rc == 0) {
+ if (print) TSS_TPMT_PUBLIC_Print(tpmtPublic, 0);
+ }
+ free(data); /* @1 */
+ return rc;
+}
+
+/* processEKCertificate() reads the EK certificate from NV and returns an X509 certificate
+ structure. It also extracts and returns the public modulus.
+
+ The return is void because the structure is opaque to the caller. This accomodates other crypto
+ libraries.
+
+ ekCertificate is an X509 structure.
+*/
+
+TPM_RC processEKCertificate(TSS_CONTEXT *tssContext,
+ void **ekCertificate, /* freed by caller */
+ uint8_t **modulusBin, /* freed by caller */
+ int *modulusBytes,
+ TPMI_RH_NV_INDEX ekCertIndex,
+ int print)
+{
+ TPM_RC rc = 0;
+
+ /* read the EK X509 certificate from NV and convert the DER (binary) to OpenSSL X509 format */
+ if (rc == 0) {
+ rc = getIndexX509Certificate(tssContext,
+ ekCertificate, /* freed by caller */
+ ekCertIndex);
+ if (rc != 0) {
+ printf("No EK certificate\n");
+ }
+ }
+ /* extract the public modulus from the X509 structure */
+ if (rc == 0) {
+ rc = convertCertificatePubKey(modulusBin, /* freed by caller */
+ modulusBytes,
+ *ekCertificate,
+ ekCertIndex,
+ print);
+ }
+ return rc;
+}
+
+#endif /* TPM20 */
+
+/* convertX509ToDer() serializes the openSSL X509 structure to a DER certificate
+
+ */
+
+TPM_RC convertX509ToDer(uint32_t *certLength,
+ unsigned char **certificate, /* output, freed by caller */
+ X509 *x509Certificate) /* input */
+{
+ TPM_RC rc = 0; /* general return code */
+ int irc;
+
+ /* sanity check for memory leak */
+ if (rc == 0) {
+ if (*certificate != NULL) {
+ printf("ERROR: convertX509ToDer: Error, certificate not NULL at entry\n");
+ rc = TSS_RC_X509_ERROR;
+ }
+ }
+ if (rc == 0) {
+ irc = i2d_X509(x509Certificate, NULL);
+ if (irc < 0) {
+ printf("ERROR: convertX509ToDer: Error in certificate serialization i2d_X509()\n");
+ rc = TSS_RC_X509_ERROR;
+ }
+ else {
+ *certLength = irc;
+ }
+ }
+ if (rc == 0) {
+ rc = TSS_Malloc(certificate, *certLength);
+ }
+ /* convert the X509 structure to binary (internal to DER format) */
+ if (rc == 0) {
+ unsigned char *tmpptr = *certificate;
+ if (tssUtilsVerbose) printf("convertX509ToDer: Serializing certificate\n");
+ irc = i2d_X509(x509Certificate, &tmpptr);
+ if (irc < 0) {
+ printf("ERROR: convertX509ToDer: Error in certificate serialization i2d_X509()\n");
+ rc = TSS_RC_X509_ERROR;
+ }
+ }
+ return rc;
+}
+
+#ifndef TPM_TSS_NOECC
+
+/* convertX509ToEc extracts the public key from an X509 structure to an openssl EC_KEY structure
+
+ */
+
+TPM_RC convertX509ToEc(EC_KEY **ecKey, /* freed by caller */
+ X509 *x509)
+{
+ TPM_RC rc = 0;
+ EVP_PKEY *evpPkey = NULL;
+
+ if (tssUtilsVerbose) printf("convertX509ToEc: Entry\n\n");
+ if (rc == 0) {
+ evpPkey = X509_get_pubkey(x509); /* freed @1 */
+ if (evpPkey == NULL) {
+ printf("ERROR: convertX509ToEc: X509_get_pubkey failed\n");
+ rc = TSS_RC_EC_KEY_CONVERT;
+ }
+ }
+ if (rc == 0) {
+ *ecKey = EVP_PKEY_get1_EC_KEY(evpPkey);
+ if (*ecKey == NULL) {
+ printf("ERROR: convertX509ToEc: EVP_PKEY_get1_EC_KEY failed\n");
+ rc = TSS_RC_EC_KEY_CONVERT;
+ }
+ }
+ if (evpPkey != NULL) {
+ EVP_PKEY_free(evpPkey); /* @1 */
+ }
+ return rc;
+}
+
+#endif /* TPM_TSS_NOECC */
+
+/* convertCertificatePubKey() returns the public modulus from an openssl X509 certificate
+ structure. ekCertIndex determines whether the algorithm is RSA or ECC.
+
+ If print is true, prints the EK certificate
+
+ The return is void because the structure is opaque to the caller. This accomodates other crypto
+ libraries.
+
+ ekCertificate is an X509 structure.
+*/
+
+TPM_RC convertCertificatePubKey(uint8_t **modulusBin, /* freed by caller */
+ int *modulusBytes,
+ void *ekCertificate,
+ TPMI_RH_NV_INDEX ekCertIndex,
+ int print)
+{
+ TPM_RC rc = 0;
+ EVP_PKEY *pkey = NULL;
+ int pkeyType; /* RSA or EC */
+
+ /* use openssl to print the X509 certificate */
+#ifndef TPM_TSS_NOFILE /* stdout is a file descriptor */
+ if (rc == 0) {
+ if (print) X509_print_fp(stdout, ekCertificate);
+ }
+#endif
+ /* extract the public key */
+ if (rc == 0) {
+ pkey = X509_get_pubkey(ekCertificate); /* freed @2 */
+ if (pkey == NULL) {
+#ifndef TPM_TSS_NORSA
+ if (tssUtilsVerbose) printf("convertCertificatePubKey: "
+ "Could not extract public key from X509 certificate, "
+ "may be TPM 1.2\n");
+ /* if the conversion failed, this may be a TPM 1.2 certificate with a non-standard TCG
+ algorithm. Try a different method to get the public modulus. */
+ rc = convertCertificatePubKey12(modulusBin, /* freed by caller */
+ modulusBytes,
+ ekCertificate);
+#else
+ printf("convertCertificatePubKey12: Could not extract X509_PUBKEY public key "
+ "from X509 certificate\n");
+ rc = TPM_RC_INTEGRITY;
+#endif /* TPM_TSS_NORSA */
+
+ }
+ else {
+ if (rc == 0) {
+ pkeyType = getRsaPubkeyAlgorithm(pkey);
+ }
+ switch (ekCertIndex) {
+#ifndef TPM_TSS_NORSA
+ case EK_CERT_RSA_INDEX:
+ {
+ RSA *rsaKey = NULL;
+ /* check that the public key algorithm matches the ekCertIndex algorithm */
+ if (rc == 0) {
+ if (pkeyType != EVP_PKEY_RSA) {
+ printf("convertCertificatePubKey: "
+ "Public key from X509 certificate is not RSA\n");
+ rc = TPM_RC_INTEGRITY;
+ }
+ }
+ /* convert the public key to OpenSSL structure */
+ if (rc == 0) {
+ rsaKey = EVP_PKEY_get1_RSA(pkey); /* freed @3 */
+ if (rsaKey == NULL) {
+ printf("convertCertificatePubKey: Could not extract RSA public key "
+ "from X509 certificate\n");
+ rc = TPM_RC_INTEGRITY;
+ }
+ }
+ if (rc == 0) {
+ rc = convertRsaKeyToPublicKeyBin(modulusBytes,
+ modulusBin, /* freed by caller */
+ rsaKey);
+ }
+ if (rc == 0) {
+ if (print) TSS_PrintAll("Certificate public key:",
+ *modulusBin, *modulusBytes);
+ }
+ RSA_free(rsaKey); /* @3 */
+ }
+ break;
+#endif /* TPM_TSS_NORSA */
+#ifndef TPM_TSS_NOECC
+ case EK_CERT_EC_INDEX:
+ {
+ EC_KEY *ecKey = NULL;
+ /* check that the public key algorithm matches the ekCertIndex algorithm */
+ if (rc == 0) {
+ if (pkeyType != EVP_PKEY_EC) {
+ printf("convertCertificatePubKey: "
+ "Public key from X509 certificate is not EC\n");
+ rc = TPM_RC_INTEGRITY;
+ }
+ }
+ /* convert the public key to OpenSSL structure */
+ if (rc == 0) {
+ ecKey = EVP_PKEY_get1_EC_KEY(pkey); /* freed @3 */
+ if (ecKey == NULL) {
+ printf("convertCertificatePubKey: Could not extract EC public key "
+ "from X509 certificate\n");
+ rc = TPM_RC_INTEGRITY;
+ }
+ }
+ if (rc == 0) {
+ rc = convertEcKeyToPublicKeyBin(modulusBytes,
+ modulusBin, /* freed by caller */
+ ecKey);
+ }
+ if (rc == 0) {
+ if (print) TSS_PrintAll("Certificate public key:",
+ *modulusBin, *modulusBytes);
+ }
+ EC_KEY_free(ecKey); /* @3 */
+ }
+ break;
+#endif /* TPM_TSS_NOECC */
+ default:
+ printf("convertCertificatePubKey: "
+ "ekCertIndex %08x (asymmetric algorithm) not supported\n", ekCertIndex);
+ rc = TPM_RC_INTEGRITY;
+ break;
+ }
+ }
+ EVP_PKEY_free(pkey); /* @2 */
+ }
+ return rc;
+}
+
+#ifndef TPM_TSS_NORSA
+
+TPM_RC convertCertificatePubKey12(uint8_t **modulusBin, /* freed by caller */
+ int *modulusBytes,
+ X509 *ekCertificate)
+{
+ TPM_RC rc = 0;
+ int irc;
+ X509_PUBKEY *pubkey = NULL;
+ ASN1_OBJECT *ppkalg = NULL; /* ignore OID */
+ const unsigned char *pk = NULL; /* do not free */
+ int ppklen;
+ X509_ALGOR *palg = NULL; /* algorithm identifier for public key */
+ RSA *rsaKey = NULL;
+
+ /* get internal pointer to the public key in the certificate */
+ if (rc == 0) {
+ pubkey = X509_get_X509_PUBKEY(ekCertificate); /* do not free */
+ if (pubkey == NULL) {
+ printf("convertCertificatePubKey12: Could not extract X509_PUBKEY public key "
+ "from X509 certificate\n");
+ rc = TPM_RC_INTEGRITY;
+ }
+ }
+ /* get the public key parameters, as a byte stream pk */
+ if (rc == 0) {
+ irc = X509_PUBKEY_get0_param(&ppkalg,
+ &pk, &ppklen, /* internal, don't free */
+ &palg, pubkey);
+ if (irc != 1) {
+ printf("convertCertificatePubKey12: Could not extract public key parameters "
+ "from X509 certificate\n");
+ rc = TPM_RC_INTEGRITY;
+ }
+ }
+ if (rc == 0) {
+ const unsigned char *tmppk = pk; /* because d2i moves the pointer */
+ rsaKey = d2i_RSAPublicKey(NULL, &tmppk, ppklen); /* freed @1 */
+ if (rsaKey == NULL) {
+ printf("convertCertificatePubKey12: Could not convert to RSA structure\n");
+ rc = TPM_RC_INTEGRITY;
+ }
+ }
+ if (rc == 0) {
+ rc = convertRsaKeyToPublicKeyBin(modulusBytes,
+ modulusBin, /* freed by caller */
+ rsaKey);
+ TSS_PrintAll("convertCertificatePubKey12", *modulusBin, *modulusBytes);
+ }
+ if (rsaKey != NULL) {
+ RSA_free(rsaKey); /* @1 */
+ }
+ return rc;
+}
+
+#endif /* TPM_TSS_NORSA */
+
+#ifndef TPM_TSS_NOFILE /* stdout is a file descriptor */
+
+TPM_RC convertX509PemToDer(uint32_t *certLength,
+ unsigned char **certificate, /* output, freed by caller */
+ const char *pemCertificateFilename)
+{
+ TPM_RC rc = 0;
+ X509 *x509Certificate = NULL;
+
+ if (rc == 0) {
+ rc = convertPemToX509(&x509Certificate, /* freed @1 */
+ pemCertificateFilename);
+ }
+ if (rc == 0) {
+ rc = convertX509ToDer(certLength,
+ certificate, /* output, freed by caller */
+ x509Certificate); /* input */
+ }
+ if (x509Certificate != NULL) {
+ X509_free(x509Certificate); /* @1 */
+ }
+ return rc;
+}
+
+#endif
+
+#ifndef TPM_TSS_NOFILE
+
+/* convertPemToX509() reads a PEM file and converts it to an OpenSSL X509 structure
+
+ */
+
+uint32_t convertPemToX509(X509 **x509, /* freed by caller */
+ const char *pemCertificateFilename)
+{
+ uint32_t rc = 0;
+ int irc;
+ FILE *pemCertificateFile = NULL;
+
+ if (tssUtilsVerbose) printf("convertPemToX509: Reading PEM certificate file %s\n",
+ pemCertificateFilename);
+ if (rc == 0) {
+ pemCertificateFile = fopen(pemCertificateFilename, "r");
+ if (pemCertificateFile == NULL) {
+ printf("convertPemToX509: Cannot open PEM file %s\n", pemCertificateFilename);
+ rc = TSS_RC_FILE_OPEN;
+ }
+ }
+ /* convert the platform certificate from PEM to DER */
+ if (rc == 0) {
+ *x509 = PEM_read_X509(pemCertificateFile , NULL, NULL, NULL); /* freed @1 */
+ if (*x509 == NULL) {
+ printf("convertPemToX509: Cannot parse PEM certificate file %s\n",
+ pemCertificateFilename);
+ rc = TSS_RC_FILE_READ;
+ }
+ }
+ /* for debug */
+ if ((rc == 0) && tssUtilsVerbose) {
+ irc = X509_print_fp(stdout, *x509);
+ if (irc != 1) {
+ printf("ERROR: convertPemToX509: Error in certificate print X509_print_fp()\n");
+ rc = TSS_RC_X509_ERROR;
+ }
+ }
+ if (pemCertificateFile != NULL) {
+ fclose(pemCertificateFile); /* @1 */
+ }
+ return rc;
+}
+
+#endif
+
+/* convertDerToX509() converts a DER stream to an OpenSSL X509 structure
+
+ The return is void because the structure is opaque to the caller. This accomodates other crypto
+ libraries.
+*/
+
+uint32_t convertDerToX509(void **x509Certificate, /* freed by caller */
+ uint16_t readLength,
+ const unsigned char *readBuffer)
+{
+ uint32_t rc = 0;
+ *x509Certificate = d2i_X509(NULL, /* freed by caller */
+ &readBuffer, readLength);
+ if (*x509Certificate == NULL) {
+ printf("convertDerToX509: Could not parse X509 certificate\n");
+ rc = TSS_RC_X509_ERROR;
+ }
+ return rc;
+}
+
+/* x509FreeStructure() is the library specific free structure.
+
+ The parameter is void because the structure is opaque to the caller. This accomodates other
+ crypto libraries.
+*/
+
+void x509FreeStructure(void *x509)
+{
+ if (x509 != NULL) {
+ X509_free(x509);
+ }
+ return;
+}
+
+/* x509PrintStructure() prints the structure to stdout
+
+ The parameter is void because the structure is opaque to the caller. This accomodates other
+ crypto libraries.
+*/
+
+void x509PrintStructure(void *x509)
+{
+ X509_print_fp(stdout, x509);
+ return;
+}
+
+/* convertPemMemToX509() converts an in-memory PEM format X509 certificate to an openssl X509
+ structure.
+
+*/
+
+uint32_t convertPemMemToX509(X509 **x509, /* freed by caller */
+ const char *pemCertificate)
+{
+ uint32_t rc = 0;
+ BIO *bio = NULL;
+ int pemLength;
+ int writeLen = 0;
+
+ if (tssUtilsVerbose) printf("convertPemMemToX509: pemCertificate\n%s\n", pemCertificate);
+ /* create a BIO that uses an in-memory buffer */
+ if (rc == 0) {
+ bio = BIO_new(BIO_s_mem()); /* freed @1 */
+ if (bio == NULL) {
+ printf("convertPemMemToX509: BIO_new failed\n");
+ rc = TSS_RC_OUT_OF_MEMORY;
+ }
+ }
+ /* write the PEM from memory to BIO */
+ if (rc == 0) {
+ pemLength = strlen(pemCertificate);
+ writeLen = BIO_write(bio, pemCertificate, pemLength);
+ if (writeLen != pemLength) {
+ printf("convertPemMemToX509: BIO_write failed\n");
+ rc = TPM_RC_INTEGRITY;
+ }
+ }
+ /* convert the properly formatted PEM to X509 structure */
+ if (rc == 0) {
+ *x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+ if (*x509 == NULL) {
+ printf("convertPemMemToX509: PEM_read_bio_X509 failed\n");
+ rc = TPM_RC_INTEGRITY;
+ }
+ }
+ /* for debug */
+#ifndef TPM_TSS_NOFILE /* stdout is a file descriptor */
+ if (rc == 0) {
+ if (tssUtilsVerbose) X509_print_fp(stdout, *x509);
+ }
+#endif
+ if (bio != NULL) {
+ BIO_free(bio); /* @1 */
+ }
+ return rc;
+}
+
+#ifndef TPM_TSS_NOFILE
+
+/* convertX509ToPem() writes an OpenSSL X509 structure to a PEM format file
+
+ The return is void because the structure is opaque to the caller. This accomodates other crypto
+ libraries.
+
+ For OpenSSL, the type is X509*
+*/
+
+TPM_RC convertX509ToPem(const char *pemFilename,
+ void *x509)
+{
+ TPM_RC rc = 0;
+ int irc;
+ FILE *pemFile = NULL;
+
+ if (tssUtilsVerbose) printf("convertX509ToPem: Writing PEM certificate file %s\n",
+ pemFilename);
+ if (rc == 0) {
+ pemFile = fopen(pemFilename, "w"); /* close @1 */
+ if (pemFile == NULL) {
+ printf("convertX509ToPem: Cannot open PEM file %s\n", pemFilename);
+ rc = TSS_RC_FILE_OPEN;
+ }
+ }
+ if (rc == 0) {
+ irc = PEM_write_X509(pemFile, x509);
+ if (irc == 0) {
+ printf("convertX509ToPem: Unable to write PEM file %s\n", pemFilename);
+ rc = TSS_RC_FILE_WRITE;
+ }
+ }
+ if (pemFile != NULL) {
+ fclose(pemFile); /* @1 */
+ }
+ return rc;
+}
+
+#endif
+
+/* convertX509ToPemMem() converts an OpenSSL X509 structure to PEM format in memory */
+
+TPM_RC convertX509ToPemMem(char **pemString, /* freed by caller */
+ X509 *x509)
+{
+ TPM_RC rc = 0; /* general return code */
+ int irc;
+ char *data = NULL;
+ long length;
+
+ /* create a BIO that uses an in-memory buffer */
+ BIO *bio = NULL;
+ if (rc == 0) {
+ bio = BIO_new(BIO_s_mem()); /* freed @1 */
+ if (bio == NULL) {
+ printf("convertX509ToPemMem: BIO_new failed\n");
+ rc = TSS_RC_OUT_OF_MEMORY;
+ }
+ }
+ /* convert X509 to PEM and write the PEM to memory */
+ if (rc == 0) {
+ irc = PEM_write_bio_X509(bio, x509);
+ if (irc != 1) {
+ printf("convertX509ToPemMem: PEM_write_bio_X509 failed\n");
+ rc = TSS_RC_FILE_WRITE;
+ }
+ }
+ if (rc == 0) {
+ length = BIO_get_mem_data(bio, &data);
+ *pemString = malloc(length+1);
+ if (*pemString == NULL) {
+ printf("ERROR: convertX509ToPemMem: Cannot malloc %lu\n", length);
+ rc = TSS_RC_OUT_OF_MEMORY;
+ }
+ else {
+ (*pemString)[length] = '\0';
+ }
+ }
+ if (rc == 0) {
+ irc = BIO_read(bio, *pemString, length);
+ if (irc <= 0) {
+ printf("ERROR: convertX509ToPemMem: BIO_read failed\n");
+ rc = TSS_RC_FILE_READ;
+ }
+ }
+ if (bio != NULL) {
+ BIO_free(bio); /* @1 */
+ }
+ return rc;
+}
+
+/* convertX509ToString() converts an OpenSSL X509 structure to a human readable string */
+
+TPM_RC convertX509ToString(char **x509String, /* freed by caller */
+ X509 *x509)
+{
+ TPM_RC rc = 0;
+ int irc;
+ char *data = NULL;
+ long length;
+
+ /* create a BIO that uses an in-memory buffer */
+ BIO *bio = NULL;
+ if (rc == 0) {
+ bio = BIO_new(BIO_s_mem()); /* freed @1 */
+ if (bio == NULL) {
+ printf("convertX509ToString: BIO_new failed\n");
+ rc = TSS_RC_OUT_OF_MEMORY;
+ }
+ }
+ /* write the string to memory */
+ if (rc == 0) {
+ irc = X509_print(bio, x509);
+ if (irc != 1) {
+ printf("convertX509ToString X509_print failed\n");
+ rc = TSS_RC_X509_ERROR;
+ }
+ }
+ if (rc == 0) {
+ length = BIO_get_mem_data(bio, &data);
+ *x509String = malloc(length+1);
+ if (*x509String == NULL) {
+ printf("convertX509ToString: Cannot malloc %lu\n", length);
+ rc = TSS_RC_OUT_OF_MEMORY;
+ }
+ else {
+ (*x509String)[length] = '\0';
+ }
+ }
+ if (rc == 0) {
+ irc = BIO_read(bio, *x509String, length);
+ if (irc <= 0) {
+ printf("convertX509ToString BIO_read failed\n");
+ rc = TSS_RC_FILE_READ;
+ }
+ }
+ if (bio != NULL) {
+ BIO_free(bio); /* @1 */
+ }
+ return rc;
+}
+
+/*
+ Certificate Creation
+*/
+
+/* These are the names inserted into the certificates. If changed, the entries also change. At run
+ time, the mapping from key to nid is done once and used repeatedly. */
+
+CertificateName certificateName[] = {
+ { "countryName", NID_undef}, /* 0 */
+ { "stateOrProvinceName", NID_undef}, /* 1 */
+ { "localityName", NID_undef}, /* 2 */
+ { "organizationName", NID_undef}, /* 3 */
+ { "organizationalUnitName", NID_undef}, /* 4 */
+ { "commonName", NID_undef}, /* 5 */
+ { "emailAddress", NID_undef}, /* 6 */
+};
+
+TPM_RC calculateNid(void)
+{
+ TPM_RC rc = 0;
+ size_t i;
+
+ for (i=0 ; (i < sizeof(certificateName)/sizeof(CertificateName)) && (rc == 0) ; i++) {
+ certificateName[i].nid = OBJ_txt2nid(certificateName[i].key); /* look up the NID for the
+ field */
+ if (certificateName[i].nid == NID_undef) {
+ printf("calculateNid: Error finding nid for %s\n", certificateName[i].key);
+ rc = TSS_RC_X509_ERROR;
+ }
+ }
+ return rc;
+}
+
+/* createCertificate() constructs a certificate from the issuer and subject. The public key to be
+ certified is tpmtPublic.
+
+ It signs the certificate using the CA key in caKeyFileName protected by the password
+ caKeyPassword. The CA signing key algorithm caKeyAlg is RSA or ECC.
+
+ The certificate is returned as a DER encoded array 'certificate', a PEM string, and a formatted
+ string.
+
+*/
+
+TPM_RC createCertificate(char **x509CertString, /* freed by caller */
+ char **pemCertString, /* freed by caller */
+ uint32_t *certLength, /* output, certificate length */
+ unsigned char **certificate, /* output, freed by caller */
+ TPMT_PUBLIC *tpmtPublic, /* key to be certified */
+ const char *caKeyFileName,
+ size_t issuerEntriesSize,
+ char **issuerEntries,
+ size_t subjectEntriesSize,
+ char **subjectEntries,
+ const char *caKeyPassword)
+{
+ TPM_RC rc = 0;
+ X509 *x509Certificate = NULL;
+ uint16_t publicKeyLength;
+ const unsigned char *publicKey = NULL;
+
+ /* allocate memory for the X509 structure */
+ if (rc == 0) {
+ x509Certificate = X509_new(); /* freed @2 */
+ if (x509Certificate == NULL) {
+ printf("createCertificate: Error in X509_new\n");
+ rc = TSS_RC_OUT_OF_MEMORY;
+ }
+ }
+ /* hash unique field to create serial number */
+ if (rc == 0) {
+ if (tpmtPublic->type == TPM_ALG_RSA) {
+ publicKeyLength = tpmtPublic->unique.rsa.t.size;
+ publicKey = tpmtPublic->unique.rsa.t.buffer;
+ }
+ else if (tpmtPublic->type == TPM_ALG_ECC) {
+ publicKeyLength = tpmtPublic->unique.ecc.x.t.size;
+ publicKey = tpmtPublic->unique.ecc.x.t.buffer;
+ }
+ else {
+ printf("createCertificate: public key algorithm %04x not supported\n",
+ tpmtPublic->type);
+ rc = TSS_RC_BAD_SIGNATURE_ALGORITHM;
+ }
+ }
+ /* fill in basic X509 information - version, serial, validity, issuer, subject */
+ if (rc == 0) {
+ rc = startCertificate(x509Certificate,
+ publicKeyLength, publicKey,
+ issuerEntriesSize, issuerEntries,
+ subjectEntriesSize, subjectEntries);
+ }
+ /* If the EK has the decrypt attribute set, the keyEncipherment bit MUST be set for an RSA EK
+ certificate; the keyAgreement bit MUST be set for an ECC EK certificate. */
+ if (rc == 0) {
+ if (tpmtPublic->type == TPM_ALG_RSA) {
+ rc = addCertExtension(x509Certificate, NID_key_usage, "critical,keyEncipherment");
+ }
+ if (tpmtPublic->type == TPM_ALG_ECC) {
+ rc = addCertExtension(x509Certificate, NID_key_usage, "critical,keyAgreement");
+ }
+ }
+ /* add the TPM public key to be certified */
+ if (rc == 0) {
+ switch (tpmtPublic->type) {
+#ifndef TPM_TSS_NORSA
+ case TPM_ALG_RSA:
+ rc = addCertKeyRsa(x509Certificate, &tpmtPublic->unique.rsa);
+ break;
+#endif /* TPM_TSS_NORSA */
+#ifndef TPM_TSS_NOECC
+ case TPM_ALG_ECC:
+ rc = addCertKeyEcc(x509Certificate, &tpmtPublic->unique.ecc);
+ break;
+#endif /* TPM_TSS_NOECC */
+ default:
+ printf("createCertificate: public key algorithm %04x not supported\n",
+ tpmtPublic->type);
+ rc = TSS_RC_BAD_SIGNATURE_ALGORITHM;
+ }
+ }
+ /* sign the certificate with the root CA key */
+ if (rc == 0) {
+ rc = addCertSignatureRoot(x509Certificate, caKeyFileName, caKeyPassword);
+ }
+ if (rc == 0) {
+ rc = convertX509ToDer(certLength, certificate, /* freed by caller */
+ x509Certificate); /* in */
+ }
+ if (rc == 0) {
+ rc = convertX509ToPemMem(pemCertString, /* freed by caller */
+ x509Certificate);
+ }
+ if (rc == 0) {
+ rc = convertX509ToString(x509CertString, /* freed by caller */
+ x509Certificate);
+ }
+ X509_free(x509Certificate); /* @2 */
+ return rc;
+}
+
+/* Certificate duration period is hard coded to 20 years */
+
+#define CERT_DURATION (60 * 60 * 24 * ((365 * 20) + 2)) /* +2 for leap years */
+
+/* startCertificate() fills in basic X509 information, such as:
+ version
+ serial number
+ issuer
+ validity
+ subject
+*/
+
+TPM_RC startCertificate(X509 *x509Certificate, /* X509 certificate to be generated */
+ uint16_t keyLength,
+ const unsigned char *keyBuffer, /* key to be certified */
+ size_t issuerEntriesSize,
+ char **issuerEntries, /* certificate issuer */
+ size_t subjectEntriesSize,
+ char **subjectEntries) /* certificate subject */
+{
+ TPM_RC rc = 0; /* general return code */
+ int irc; /* integer return code */
+ ASN1_TIME *arc; /* return code */
+ ASN1_INTEGER *x509Serial; /* certificate serial number in ASN1 */
+ BIGNUM *x509SerialBN; /* certificate serial number as a BIGNUM */
+ unsigned char x509Serialbin[SHA1_DIGEST_SIZE]; /* certificate serial number in binary */
+ X509_NAME *x509IssuerName; /* composite issuer name, key/value pairs */
+ X509_NAME *x509SubjectName; /* composite subject name, key/value pairs */
+
+ x509IssuerName = NULL; /* freed @1 */
+ x509SubjectName = NULL; /* freed @2 */
+ x509SerialBN = NULL; /* freed @3 */
+
+ /* add certificate version X509 v3 */
+ if (rc == 0) {
+ irc = X509_set_version(x509Certificate, 2L); /* value 2 == v3 */
+ if (irc != 1) {
+ printf("startCertificate: Error in X509_set_version\n");
+ rc = TSS_RC_X509_ERROR;
+ }
+ }
+ /*
+ add certificate serial number
+ */
+ if (rc == 0) {
+ if (tssUtilsVerbose) printf("startCertificate: Adding certificate serial number\n");
+ /* to create a unique serial number, hash the key to be certified */
+ SHA1(keyBuffer, keyLength, x509Serialbin);
+ /* convert the SHA1 digest to a BIGNUM */
+ x509SerialBN = BN_bin2bn(x509Serialbin, SHA1_DIGEST_SIZE, x509SerialBN);
+ if (x509SerialBN == NULL) {
+ printf("startCertificate: Error in serial number BN_bin2bn\n");
+ rc = TSS_RC_X509_ERROR;
+ }
+ }
+ if (rc == 0) {
+ /* get the serial number structure member, can't fail */
+ x509Serial = X509_get_serialNumber(x509Certificate);
+ /* convert the BIGNUM to ASN1 and add to X509 certificate */
+ x509Serial = BN_to_ASN1_INTEGER(x509SerialBN, x509Serial);
+ if (x509Serial == NULL) {
+ printf("startCertificate: Error setting certificate serial number\n");
+ rc = TSS_RC_X509_ERROR;
+ }
+ }
+ /* add issuer */
+ if (rc == 0) {
+ if (tssUtilsVerbose) printf("startCertificate: Adding certificate issuer\n");
+ rc = createX509Name(&x509IssuerName,
+ issuerEntriesSize,
+ issuerEntries);
+ }
+ if (rc == 0) {
+ irc = X509_set_issuer_name(x509Certificate, x509IssuerName);
+ if (irc != 1) {
+ printf("startCertificate: Error setting certificate issuer\n");
+ rc = TSS_RC_X509_ERROR;
+ }
+ }
+ /* add validity */
+ if (rc == 0) {
+ if (tssUtilsVerbose) printf("startCertificate: Adding certificate validity\n");
+ }
+ if (rc == 0) {
+ /* can't fail, just returns a structure member */
+ ASN1_TIME *notBefore = X509_get_notBefore(x509Certificate);
+ arc = X509_gmtime_adj(notBefore ,0L); /* set to today */
+ if (arc == NULL) {
+ printf("startCertificate: Error setting notBefore time\n");
+ rc = TSS_RC_X509_ERROR;
+ }
+ }
+ if (rc == 0) {
+ /* can't fail, just returns a structure member */
+ ASN1_TIME *notAfter = X509_get_notAfter(x509Certificate);
+ arc = X509_gmtime_adj(notAfter, CERT_DURATION); /* set to duration */
+ if (arc == NULL) {
+ printf("startCertificate: Error setting notAfter time\n");
+ rc = TSS_RC_X509_ERROR;
+ }
+ }
+ /* add subject */
+ if (rc == 0) {
+ if (tssUtilsVerbose) printf("startCertificate: Adding certificate subject\n");
+ rc = createX509Name(&x509SubjectName,
+ subjectEntriesSize,
+ subjectEntries);
+ }
+ if (rc == 0) {
+ irc = X509_set_subject_name(x509Certificate, x509SubjectName);
+ if (irc != 1) {
+ printf("startCertificate: Error setting certificate subject\n");
+ rc = TSS_RC_X509_ERROR;
+ }
+ }
+ /* cleanup */
+ X509_NAME_free(x509IssuerName); /* @1 */
+ X509_NAME_free(x509SubjectName); /* @2 */
+ BN_free(x509SerialBN); /* @3 */
+ return rc;
+}
+
+/* createX509Name() create an X509 name (issuer or subject) from a pointer to issuer or subject
+ entries
+
+*/
+
+TPM_RC createX509Name(X509_NAME **x509Name,
+ size_t entriesSize,
+ char **entries)
+{
+ TPM_RC rc = 0; /* general return code */
+ int irc; /* integer return code */
+ size_t i;
+ X509_NAME_ENTRY *nameEntry; /* single field of the name */
+
+ nameEntry = NULL;
+
+ /* Precalculate the openssl nids, into global table */
+ if (rc == 0) {
+ rc = calculateNid();
+ }
+ if (rc == 0) {
+ *x509Name = X509_NAME_new();
+ if (*x509Name == NULL) {
+ printf("createX509Name: Error in X509_NAME_new()\n");
+ rc = TSS_RC_OUT_OF_MEMORY;
+ }
+ }
+ for (i=0 ; (i < entriesSize) && (rc == 0) ; i++) {
+ if ((rc == 0) && (entries[i] != NULL)) {
+ nameEntry =
+ X509_NAME_ENTRY_create_by_NID(NULL, /* caller creates object */
+ certificateName[i].nid,
+ MBSTRING_ASC, /* character encoding */
+ (unsigned char *)entries[i], /* to add */
+ -1); /* length, -1 is C string */
+
+ if (nameEntry == NULL) {
+ printf("createX509Name: Error creating entry for %s\n",
+ certificateName[i].key);
+ rc = TSS_RC_X509_ERROR;
+ }
+ }
+ if ((rc == 0) && (entries[i] != NULL)) {
+ irc = X509_NAME_add_entry(*x509Name, /* add to issuer */
+ nameEntry, /* add the entry */
+ -1, /* location - append */
+ 0); /* set - not multivalued */
+ if (irc != 1) {
+ printf("createX509Name: Error adding entry for %s\n",
+ certificateName[i].key);
+ rc = TSS_RC_X509_ERROR;
+ }
+ }
+ X509_NAME_ENTRY_free(nameEntry); /* callee checks for NULL */
+ nameEntry = NULL;
+ }
+ return rc;
+}
+
+/* addCertExtension() adds the extension type 'nid' to the X509 certificate
+
+ */
+
+TPM_RC addCertExtension(X509 *x509Certificate, int nid, const char *value)
+{
+ TPM_RC rc = 0;
+ X509_EXTENSION *extension = NULL; /* freed @1 */
+
+ if (rc == 0) {
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+ /* the cast is required for the older openssl 1.0 API */
+ extension = X509V3_EXT_conf_nid(NULL, NULL, /* freed @1 */
+ nid, (char *)value);
+#else
+ extension = X509V3_EXT_conf_nid(NULL, NULL, /* freed @1 */
+ nid, value);
+#endif
+ if (extension == NULL) {
+ printf("addCertExtension: Error creating nid %i extension %s\n",
+ nid, value);
+ rc = TSS_RC_X509_ERROR;
+ }
+ }
+ if (rc == 0) {
+ int irc = X509_add_ext(x509Certificate, /* the certificate */
+ extension, /* the extension to add */
+ -1); /* location - append */
+ if (irc != 1) {
+ printf("addCertExtension: Error adding nid %i extension %s\n",
+ nid, value);
+ }
+ }
+ if (extension != NULL) {
+ X509_EXTENSION_free(extension); /* @1 */
+ }
+ return rc;
+}
+
+#ifndef TPM_TSS_NORSA
+
+/* addCertKeyRsa() adds the TPM RSA public key (the key to be certified) to the openssl X509
+ certificate
+
+*/
+
+TPM_RC addCertKeyRsa(X509 *x509Certificate,
+ const TPM2B_PUBLIC_KEY_RSA *tpm2bRsa) /* key to be certified */
+{
+ TPM_RC rc = 0; /* general return code */
+ int irc; /* integer return code */
+ EVP_PKEY *evpPubkey = NULL; /* EVP format public key to be certified */
+
+ if (tssUtilsVerbose) printf("addCertKeyRsa: add public key to certificate\n");
+ /* convert from TPM key data format to openSSL RSA type */
+ if (rc == 0) {
+ rc = convertRsaPublicToEvpPubKey(&evpPubkey, /* freed @1 */
+ tpm2bRsa);
+ }
+ /* add the public key to the certificate */
+ if (rc == 0) {
+ irc = X509_set_pubkey(x509Certificate, evpPubkey);
+ if (irc != 1) {
+ printf("addCertKeyRsa: Error adding public key to certificate\n");
+ rc = TSS_RC_X509_ERROR;
+ }
+ }
+ /* cleanup */
+ if (evpPubkey != NULL) {
+ EVP_PKEY_free(evpPubkey); /* @1 */
+ }
+ return rc;
+}
+
+#endif /* TPM_TSS_NORSA */
+
+#ifndef TPM_TSS_NOECC
+
+/* addCertKeyEcc() adds the TPM ECC public key (the key to be certified) to the openssl X509
+ certificate
+
+*/
+
+TPM_RC addCertKeyEcc(X509 *x509Certificate,
+ const TPMS_ECC_POINT *tpmsEccPoint)
+{
+ TPM_RC rc = 0; /* general return code */
+ int irc;
+ EVP_PKEY *evpPubkey = NULL; /* EVP format public key to be certified */
+
+ /* convert EC TPMS_ECC_POINT to an EVP_PKEY */
+ if (rc == 0) {
+ rc = convertEcPublicToEvpPubKey(&evpPubkey, /* freed @1 */
+ tpmsEccPoint);
+ }
+ /* add the public key to the certificate */
+ if (rc == 0) {
+ irc = X509_set_pubkey(x509Certificate, evpPubkey);
+ if (irc != 1) {
+ printf("addCertKeyEcc: Error adding public key to certificate\n");
+ rc = TSS_RC_X509_ERROR;
+ }
+ }
+ /* cleanup */
+ if (evpPubkey != NULL) {
+ EVP_PKEY_free(evpPubkey); /* @1 */
+ }
+ return rc;
+}
+
+#endif /* TPM_TSS_NOECC */
+
+/* addCertSignatureRoot() uses the openSSL root key to sign the X509 certificate.
+
+ As a sanity check, it verifies the certificate.
+*/
+
+TPM_RC addCertSignatureRoot(X509 *x509Certificate, /* certificate to be signed */
+ const char *caKeyFileName, /* openSSL root CA key password */
+ const char *caKeyPassword)
+{
+ TPM_RC rc = 0; /* general return code */
+ int irc; /* integer return code */
+ FILE *fp = NULL;
+ /* signing key */
+ const EVP_MD *digest = NULL; /* signature digest algorithm */
+ EVP_PKEY *evpSignkey; /* EVP format */
+
+ evpSignkey = NULL; /* freed @1 */
+
+ /* open the CA signing key file */
+ if (rc == 0) {
+ fp = fopen(caKeyFileName,"r");
+ if (fp == NULL) {
+ printf("addCertSignatureRoot: Error, Cannot open %s\n", caKeyFileName);
+ rc = TSS_RC_FILE_OPEN;
+ }
+ }
+ /* convert the CA signing key from PEM to EVP_PKEY format */
+ if (rc == 0) {
+ evpSignkey = PEM_read_PrivateKey(fp, NULL, NULL, (void *)caKeyPassword);
+ if (evpSignkey == NULL) {
+ printf("addCertSignatureRoot: Error calling PEM_read_PrivateKey() from %s\n",
+ caKeyFileName);
+ rc = TSS_RC_FILE_READ;
+ }
+ }
+ /* close the CA signing key file */
+ if (fp != NULL) {
+ fclose(fp);
+ }
+ /* set the certificate signature digest algorithm */
+ if (rc == 0) {
+ digest = EVP_sha256(); /* no error return */
+ }
+ /* sign the certificate with the root CA signing key */
+ if (rc == 0) {
+ if (tssUtilsVerbose) printf("addCertSignatureRoot: Signing the certificate\n");
+ irc = X509_sign(x509Certificate, evpSignkey, digest);
+ if (irc == 0) { /* returns signature size, 0 on error */
+ printf("addCertSignature: Error signing certificate\n");
+ rc = TSS_RC_X509_ERROR;
+ }
+ }
+ /* verify the signature */
+ if (rc == 0) {
+ if (tssUtilsVerbose) printf("addCertSignatureRoot: Verifying the certificate\n");
+ irc = X509_verify(x509Certificate, evpSignkey);
+ if (irc != 1) {
+ printf("addCertSignatureRoot: Error verifying certificate\n");
+ rc = TSS_RC_X509_ERROR;
+ }
+ }
+ /* cleanup */
+ if (evpSignkey != NULL) {
+ EVP_PKEY_free(evpSignkey); /* @1 */
+ }
+ return rc;
+}
+
+#ifdef TPM_TPM20
+
+/* processRoot() validates the certificate at ekCertIndex against the root CA certificates at
+ rootFilename.
+ */
+
+#ifndef TPM_TSS_NOFILE
+
+TPM_RC processRoot(TSS_CONTEXT *tssContext,
+ TPMI_RH_NV_INDEX ekCertIndex,
+ const char *rootFilename[],
+ unsigned int rootFileCount,
+ int print)
+{
+ TPM_RC rc = 0;
+ void *ekCertificate = NULL; /* freed @1 */
+
+ /* read the EK X509 certificate from NV */
+ if (rc == 0) {
+ rc = getIndexX509Certificate(tssContext,
+ &ekCertificate, /* freed @1 */
+ ekCertIndex);
+ if (rc != 0) {
+ printf("processRoot: No EK certificate\n");
+ }
+ }
+ if (rc == 0) {
+ rc = verifyCertificate(ekCertificate,
+ rootFilename,
+ rootFileCount,
+ print);
+ if (rc != 0) {
+ printf("processRoot: EK certificate did not verify\n");
+ }
+ }
+ if (ekCertificate != NULL) {
+ X509_free(ekCertificate); /* @1 */
+ }
+ return rc;
+}
+
+#endif
+
+/* processCreatePrimary() combines the EK nonce and EK template from NV to form the
+ createprimary input. It creates the primary key.
+
+ ekCertIndex determines whether an RSA or ECC key is created.
+
+ If nonce is NULL, the default IWG templates are used. If nonce is non-NULL, the nonce and
+ tpmtPublicIn are used.
+
+ After returning the TPMT_PUBLIC, flushes the primary key unless noFlush is TRUE. If noFlush is
+ FALSE, returns the loaded handle, else returns TPM_RH_NULL.
+*/
+
+TPM_RC processCreatePrimary(TSS_CONTEXT *tssContext,
+ TPM_HANDLE *keyHandle, /* primary key handle */
+ TPMI_RH_NV_INDEX ekCertIndex,
+ unsigned char *nonce,
+ uint16_t nonceSize,
+ TPMT_PUBLIC *tpmtPublicIn, /* template */
+ TPMT_PUBLIC *tpmtPublicOut, /* primary key */
+ unsigned int noFlush, /* TRUE - don't flush the primary key */
+ int print)
+{
+ TPM_RC rc = 0;
+ CreatePrimary_In inCreatePrimary;
+ CreatePrimary_Out outCreatePrimary;
+
+ /* sanity check nonce size (should never happen on HW TPM) */
+ if ((rc == 0) && (nonce != NULL)) {
+ if (ekCertIndex == EK_CERT_RSA_INDEX) { /* RSA primary key */
+ if (nonceSize > 256) {
+ printf("processCreatePrimary: RSA NV nonce size %u > 256\n", nonceSize);
+ rc = TSS_RC_INSUFFICIENT_BUFFER;
+ }
+ }
+ else { /* EC primary key */
+ if (nonceSize > 32) {
+ printf("processCreatePrimary: EC NV nonce size %u > 32\n", nonceSize);
+ rc = TSS_RC_INSUFFICIENT_BUFFER;
+ }
+ }
+ }
+ /* set up the createprimary in parameters */
+ if (rc == 0) {
+ inCreatePrimary.primaryHandle = TPM_RH_ENDORSEMENT;
+ inCreatePrimary.inSensitive.sensitive.userAuth.t.size = 0;
+ inCreatePrimary.inSensitive.sensitive.data.t.size = 0;
+ /* creation data */
+ inCreatePrimary.outsideInfo.t.size = 0;
+ inCreatePrimary.creationPCR.count = 0;
+ }
+ /* construct the template from the NV template and nonce */
+ if ((rc == 0) && (nonce != NULL)) {
+ inCreatePrimary.inPublic.publicArea = *tpmtPublicIn;
+ if (ekCertIndex == EK_CERT_RSA_INDEX) { /* RSA primary key */
+ /* unique field is 256 bytes */
+ inCreatePrimary.inPublic.publicArea.unique.rsa.t.size = 256;
+ /* first part is nonce */
+ memcpy(inCreatePrimary.inPublic.publicArea.unique.rsa.t.buffer, nonce, nonceSize);
+ /* padded with zeros */
+ memset(inCreatePrimary.inPublic.publicArea.unique.rsa.t.buffer + nonceSize, 0,
+ 256 - nonceSize);
+ }
+ else { /* EC primary key */
+ /* unique field is X and Y points */
+ /* X gets nonce and pad */
+ inCreatePrimary.inPublic.publicArea.unique.ecc.x.t.size = 32;
+ memcpy(inCreatePrimary.inPublic.publicArea.unique.ecc.x.t.buffer, nonce, nonceSize);
+ memset(inCreatePrimary.inPublic.publicArea.unique.ecc.x.t.buffer + nonceSize, 0,
+ 32 - nonceSize);
+ /* Y gets zeros */
+ inCreatePrimary.inPublic.publicArea.unique.ecc.y.t.size = 32;
+ memset(inCreatePrimary.inPublic.publicArea.unique.ecc.y.t.buffer, 0, 32);
+ }
+ }
+ /* construct the template from the default IWG template */
+ if ((rc == 0) && (nonce == NULL)) {
+ if (ekCertIndex == EK_CERT_RSA_INDEX) { /* RSA primary key */
+ getRsaTemplate(&inCreatePrimary.inPublic.publicArea);
+ }
+ else { /* EC primary key */
+ getEccTemplate(&inCreatePrimary.inPublic.publicArea);
+ }
+ }
+ /* call TSS to execute the command */
+ if (rc == 0) {
+ rc = TSS_Execute(tssContext,
+ (RESPONSE_PARAMETERS *)&outCreatePrimary,
+ (COMMAND_PARAMETERS *)&inCreatePrimary,
+ NULL,
+ TPM_CC_CreatePrimary,
+ TPM_RS_PW, NULL, 0,
+ TPM_RH_NULL, NULL, 0);
+ if (rc != 0) {
+ const char *msg;
+ const char *submsg;
+ const char *num;
+ printf("createprimary: failed, rc %08x\n", rc);
+ TSS_ResponseCode_toString(&msg, &submsg, &num, rc);
+ printf("%s%s%s\n", msg, submsg, num);
+ }
+ }
+ /* return the primary key */
+ if (rc == 0) {
+ *tpmtPublicOut = outCreatePrimary.outPublic.publicArea;
+ }
+ /* flush the primary key */
+ if (rc == 0) {
+ if (!noFlush) { /* flush the primary key */
+ FlushContext_In inFlushContext;
+ *keyHandle = TPM_RH_NULL;
+ inFlushContext.flushHandle = outCreatePrimary.objectHandle;
+ rc = TSS_Execute(tssContext,
+ NULL,
+ (COMMAND_PARAMETERS *)&inFlushContext,
+ NULL,
+ TPM_CC_FlushContext,
+ TPM_RH_NULL, NULL, 0);
+ if (rc != 0) {
+ const char *msg;
+ const char *submsg;
+ const char *num;
+ printf("flushcontext: failed, rc %08x\n", rc);
+ TSS_ResponseCode_toString(&msg, &submsg, &num, rc);
+ printf("%s%s%s\n", msg, submsg, num);
+ }
+ }
+ else { /* not flushed, return the handle */
+ *keyHandle = outCreatePrimary.objectHandle;
+ }
+ }
+ /* trace the public key */
+ if (rc == 0) {
+ if (ekCertIndex == EK_CERT_RSA_INDEX) {
+ if (print) TSS_PrintAll("createprimary: RSA public key",
+ outCreatePrimary.outPublic.publicArea.unique.rsa.t.buffer,
+ outCreatePrimary.outPublic.publicArea.unique.rsa.t.size);
+ }
+ else {
+ if (print) TSS_PrintAll("createprimary: ECC public key x",
+ outCreatePrimary.outPublic.publicArea.unique.ecc.x.t.buffer,
+ outCreatePrimary.outPublic.publicArea.unique.ecc.x.t.size);
+ if (print) TSS_PrintAll("createprimary: ECC public key y",
+ outCreatePrimary.outPublic.publicArea.unique.ecc.y.t.buffer,
+ outCreatePrimary.outPublic.publicArea.unique.ecc.y.t.size);
+ }
+ }
+ return rc;
+}
+
+/* processValidatePrimary() compares the public key in the EK certificate to the public key output
+ of createprimary. */
+
+TPM_RC processValidatePrimary(uint8_t *publicKeyBin, /* from certificate */
+ int publicKeyBytes,
+ TPMT_PUBLIC *tpmtPublic, /* primary key */
+ TPMI_RH_NV_INDEX ekCertIndex,
+ int print)
+{
+ TPM_RC rc = 0;
+
+ print = print;
+ /* compare the X509 certificate public key to the createprimary public key */
+ switch (ekCertIndex) {
+#ifndef TPM_TSS_NORSA
+ case EK_CERT_RSA_INDEX:
+ {
+ int irc;
+ /* RSA just has a public modulus */
+ if (rc == 0) {
+ if (tpmtPublic->unique.rsa.t.size != publicKeyBytes) {
+ printf("processValidatePrimary: "
+ "X509 certificate key length %u does not match output of createprimary %u\n",
+ publicKeyBytes,
+ tpmtPublic->unique.rsa.t.size);
+ rc = TPM_RC_INTEGRITY;
+ }
+ }
+ if (rc == 0) {
+ irc = memcmp(publicKeyBin,
+ tpmtPublic->unique.rsa.t.buffer,
+ publicKeyBytes);
+ if (irc != 0) {
+ printf("processValidatePrimary: "
+ "Public key from X509 certificate does not match output of createprimary\n");
+ rc = TPM_RC_INTEGRITY;
+ }
+ }
+ }
+ break;
+#endif /* TPM_TSS_NORSA */
+#ifndef TPM_TSS_NOECC
+ case EK_CERT_EC_INDEX:
+ {
+ int irc;
+ /* ECC has X and Y points */
+ /* compression algorithm is the extra byte at the beginning of the certificate */
+ if (rc == 0) {
+ if (tpmtPublic->unique.ecc.x.t.size +
+ tpmtPublic->unique.ecc.y.t.size + 1
+ != publicKeyBytes) {
+ printf("processValidatePrimary: "
+ "X509 certificate key length %u does not match "
+ "output of createprimary x %u +y %u\n",
+ publicKeyBytes,
+ tpmtPublic->unique.ecc.x.t.size,
+ tpmtPublic->unique.ecc.y.t.size);
+ rc = TPM_RC_INTEGRITY;
+ }
+ }
+ /* check X */
+ if (rc == 0) {
+ irc = memcmp(publicKeyBin +1,
+ tpmtPublic->unique.ecc.x.t.buffer,
+ tpmtPublic->unique.ecc.x.t.size);
+ if (irc != 0) {
+ printf("processValidatePrimary: "
+ "Public key X from X509 certificate does not match "
+ "output of createprimary\n");
+ rc = TPM_RC_INTEGRITY;
+ }
+ }
+ /* check Y */
+ if (rc == 0) {
+ irc = memcmp(publicKeyBin + 1 + tpmtPublic->unique.ecc.x.t.size,
+ tpmtPublic->unique.ecc.y.t.buffer,
+ tpmtPublic->unique.ecc.y.t.size);
+ if (irc != 0) {
+ printf("processValidatePrimary: "
+ "Public key Y from X509 certificate does not match "
+ "output of createprimary\n");
+ rc = TPM_RC_INTEGRITY;
+ }
+ }
+ }
+ break;
+#endif /* TPM_TSS_NOECC */
+ default:
+ printf("processValidatePrimary: "
+ "ekCertIndex %08x (asymmetric algorithm) not supported\n", ekCertIndex);
+ rc = TPM_RC_INTEGRITY;
+ break;
+ }
+ if (rc == 0) {
+ if (print) printf("processValidatePrimary: "
+ "Public key from X509 certificate matches output of createprimary\n");
+ }
+ return rc;
+}
+
+/* processPrimary() reads the EK nonce and EK template from NV. It combines them to form the
+ createprimary input. It creates the primary key.
+
+ It reads the EK certificate from NV. It extracts the public key.
+
+ Finally, it compares the public key in the certificate to the public key output of createprimary.
+*/
+
+TPM_RC processPrimary(TSS_CONTEXT *tssContext,
+ TPM_HANDLE *keyHandle, /* primary key handle */
+ TPMI_RH_NV_INDEX ekCertIndex,
+ TPMI_RH_NV_INDEX ekNonceIndex,
+ TPMI_RH_NV_INDEX ekTemplateIndex,
+ unsigned int noFlush, /* TRUE - don't flush the primary key */
+ int print)
+{
+ TPM_RC rc = 0;
+ void *ekCertificate = NULL;
+ unsigned char *nonce = NULL;
+ uint16_t nonceSize;
+ TPMT_PUBLIC tpmtPublicIn; /* template */
+ TPMT_PUBLIC tpmtPublicOut; /* primary key */
+ uint8_t *publicKeyBin = NULL; /* from certificate */
+ int publicKeyBytes;
+ int validate = FALSE; /* validate the certificate */
+
+ /* get the EK nonce */
+ if (rc == 0) {
+ rc = processEKNonce(tssContext, &nonce, &nonceSize, ekNonceIndex, print); /* freed @1 */
+ if ((rc & 0xff) == TPM_RC_HANDLE) {
+ if (print) printf("processPrimary: EK nonce not found, use default template\n");
+ rc = 0;
+ }
+ }
+ if (rc == 0) {
+ /* if the nonce was found, get the EK template */
+ if (nonce != NULL) {
+ rc = processEKTemplate(tssContext, &tpmtPublicIn, ekTemplateIndex, print);
+ }
+ }
+ /* create the primary key */
+ if (rc == 0) {
+ rc = processCreatePrimary(tssContext,
+ keyHandle,
+ ekCertIndex,
+ nonce, nonceSize, /* EK nonce, can be NULL */
+ &tpmtPublicIn, /* template */
+ &tpmtPublicOut, /* primary key */
+ noFlush,
+ print);
+ }
+ /* validate against the certificate if the algorithm is compiled in */
+ if (rc == 0) {
+#ifndef TPM_TSS_NORSA
+ if (ekCertIndex == EK_CERT_RSA_INDEX) {
+ validate = TRUE;
+ }
+#endif /* TPM_TSS_NORSA */
+#ifndef TPM_TSS_NOECC
+ if (ekCertIndex == EK_CERT_EC_INDEX) {
+ validate = TRUE;
+ }
+#endif /* TPM_TSS_NOECC */
+ }
+ /* get the EK certificate */
+ if ((rc == 0) && validate) {
+ rc = processEKCertificate(tssContext,
+ &ekCertificate, /* freed @2 */
+ &publicKeyBin, &publicKeyBytes, /* freed @3 */
+ ekCertIndex,
+ print);
+ }
+ /* compare the public key in the EK certificate to the public key output */
+ if ((rc == 0) && validate) {
+ rc = processValidatePrimary(publicKeyBin, /* certificate */
+ publicKeyBytes,
+ &tpmtPublicOut, /* primary key */
+ ekCertIndex,
+ print);
+ }
+ if ((rc == 0) && validate) {
+ if (print) printf("Public key from X509 certificate matches output of createprimary\n");
+ }
+ free(nonce); /* @1 */
+ if (ekCertificate != NULL) {
+ X509_free(ekCertificate); /* @2 */
+ }
+ free(publicKeyBin); /* @3 */
+ return rc;
+}
+
+#endif /* TPM20 */
+