diff options
Diffstat (limited to 'roms/skiboot/libstb/tss2/ibmtpm20tss/utils/ekutils.c')
-rw-r--r-- | roms/skiboot/libstb/tss2/ibmtpm20tss/utils/ekutils.c | 2314 |
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 */ + |