From 1e1648fec31bd8d80b2ece4eaefe851e2cf4009d Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Wed, 17 Feb 2021 13:29:00 -0500 Subject: [PATCH] tpm2: Save key and hash contexts using ANY_OBJECT_Marshal Save key and hash contexts using the ANY_OBJECT_Marshal function and try to load it using ANY_OBJECT_Unmarshal(). Unfortunately older contexts were written out as plain OBJECTs, so we have to accomodate this case as well so that we can restore key contexts from libtpms-0.7.x. We do not support resuming HASH contexts from libtpms-0.7.x. Before this modification context files written out by the IBM TSS stack were 2692 bytes independent of content. Now an RSA 2048 key is 1222 bytes and a NIST p384 key is 982 bytes. Several of the original TPM 2 function exporting Sequence state and importing it can now be disabled. Signed-off-by: Stefan Berger --- src/Makefile.am | 2 + src/tpm2/BackwardsCompatibilityObject.c | 234 ++++++++++++++++++++++++ src/tpm2/BackwardsCompatibilityObject.h | 47 +++++ src/tpm2/ContextCommands.c | 16 +- src/tpm2/Context_spt.c | 2 + src/tpm2/Object.c | 36 ++++ src/tpm2/Object_fp.h | 8 + src/tpm2/crypto/openssl/CryptHash.c | 6 +- 8 files changed, 344 insertions(+), 7 deletions(-) create mode 100644 src/tpm2/BackwardsCompatibilityObject.c create mode 100644 src/tpm2/BackwardsCompatibilityObject.h diff --git a/src/Makefile.am b/src/Makefile.am index 37d3fdac..6594ddfb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -276,6 +276,7 @@ libtpms_tpm2_la_SOURCES = \ tpm_tpm2_interface.c \ tpm_tpm2_tis.c \ \ + tpm2/BackwardsCompatibilityObject.c \ tpm2/LibtpmsCallbacks.c \ tpm2/NVMarshal.c \ tpm2/StateMarshal.c \ @@ -512,6 +513,7 @@ noinst_HEADERS += \ tpm2/ZGen_2Phase_fp.h \ \ tpm2/BackwardsCompatibility.h \ + tpm2/BackwardsCompatibilityObject.h \ tpm2/LibtpmsCallbacks.h \ tpm2/NVMarshal.h \ tpm2/StateMarshal.h \ diff --git a/src/tpm2/BackwardsCompatibilityObject.c b/src/tpm2/BackwardsCompatibilityObject.c new file mode 100644 index 00000000..2e48e7ef --- /dev/null +++ b/src/tpm2/BackwardsCompatibilityObject.c @@ -0,0 +1,234 @@ +/********************************************************************************/ +/* */ +/* Backwards compatibility stuff related to OBJECT */ +/* Written by Stefan Berger */ +/* IBM Thomas J. Watson Research Center */ +/* */ +/* (c) Copyright IBM Corporation 2017,2018. */ +/* */ +/* 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. */ +/********************************************************************************/ + +#include + +#include "BackwardsCompatibilityObject.h" + +/* The following are data structure from libtpms 0.7.x with RSA 2048 support + * that help to resume key and hash contexts (TPM2_ContextSave/Load) from this + * earlier version. All structures that have different sizes in 0.8 are found + * here. + */ +typedef union { + struct { + UINT16 size; + BYTE buffer[2048/8]; + } t; + TPM2B b; +} OLD_TPM2B_PUBLIC_KEY_RSA; + +typedef union { + TPM2B_DIGEST keyedHash; + TPM2B_DIGEST sym; + OLD_TPM2B_PUBLIC_KEY_RSA rsa; + TPMS_ECC_POINT ecc; +// TPMS_DERIVE derive; +} OLD_TPMU_PUBLIC_ID; + +typedef struct { + TPMI_ALG_PUBLIC type; + TPMI_ALG_HASH nameAlg; + TPMA_OBJECT objectAttributes; + TPM2B_DIGEST authPolicy; + TPMU_PUBLIC_PARMS parameters; + OLD_TPMU_PUBLIC_ID unique; +} OLD_TPMT_PUBLIC; + +static_assert(sizeof(OLD_TPMT_PUBLIC) == 356, + "OLD_TPMT_PUBLIC has wrong size"); + +typedef union { + struct { + UINT16 size; + BYTE buffer[((2048/8)/2)*5]; + } t; + TPM2B b; +} OLD_TPM2B_PRIVATE_KEY_RSA; + +static_assert(sizeof(OLD_TPM2B_PRIVATE_KEY_RSA) == 642, + "OLD_TPM2B_PRIVATE_KEY_RSA has wrong size"); + +typedef union { + struct { + UINT16 size; + BYTE buffer[((2048/8)/2)*5]; + } t; + TPM2B b; +} OLD_TPM2B_PRIVATE_VENDOR_SPECIFIC; + +typedef union { + OLD_TPM2B_PRIVATE_KEY_RSA rsa; + TPM2B_ECC_PARAMETER ecc; + TPM2B_SENSITIVE_DATA bits; + TPM2B_SYM_KEY sym; + OLD_TPM2B_PRIVATE_VENDOR_SPECIFIC any; +} OLD_TPMU_SENSITIVE_COMPOSITE; + +typedef struct { + TPMI_ALG_PUBLIC sensitiveType; + TPM2B_AUTH authValue; + TPM2B_DIGEST seedValue; + OLD_TPMU_SENSITIVE_COMPOSITE sensitive; +} OLD_TPMT_SENSITIVE; + +static_assert(sizeof(OLD_TPMT_SENSITIVE) == 776, + "OLD_TPMT_SENSITIVE has wrong size"); + +BN_TYPE(old_prime, (2048 / 2)); + +typedef struct OLD_privateExponent +{ + bn_old_prime_t Q; + bn_old_prime_t dP; + bn_old_prime_t dQ; + bn_old_prime_t qInv; +} OLD_privateExponent_t; + +static inline void CopyFromOldPrimeT(bn_prime_t *dst, + const bn_old_prime_t *src) +{ + dst->allocated = src->allocated; + dst->size = src->size; + memcpy(dst->d, src->d, sizeof(src->d)); +} + +static_assert(sizeof(OLD_privateExponent_t) == 608, + "OLD_privateExponent_t has wrong size"); + +typedef struct OLD_OBJECT +{ + // The attributes field is required to be first followed by the publicArea. + // This allows the overlay of the object structure and a sequence structure + OBJECT_ATTRIBUTES attributes; // object attributes + OLD_TPMT_PUBLIC publicArea; // public area of an object + OLD_TPMT_SENSITIVE sensitive; // sensitive area of an object + OLD_privateExponent_t privateExponent; // Additional field for the private + TPM2B_NAME qualifiedName; // object qualified name + TPMI_DH_OBJECT evictHandle; // if the object is an evict object, + // the original handle is kept here. + // The 'working' handle will be the + // handle of an object slot. + TPM2B_NAME name; // Name of the object name. Kept here + // to avoid repeatedly computing it. + + // libtpms added: OBJECT lies in NVRAM; to avoid that it needs different number + // of bytes on 32 bit and 64 bit architectures, we need to make sure it's the + // same size; simple padding at the end works here + UINT32 _pad; +} OLD_OBJECT; + +static_assert(sizeof(OLD_OBJECT) == 1896, + "OLD_OBJECT has wrong size"); + +// Convert an OLD_OBJECT that was copied into buffer using MemoryCopy +TPM_RC +OLD_OBJECTToOBJECT(OBJECT *newObject, BYTE *buffer, INT32 size) +{ + OLD_OBJECT oldObject; + TPM_RC rc = 0; + + // get the attributes + MemoryCopy(newObject, buffer, sizeof(newObject->attributes)); + if (ObjectIsSequence(newObject)) + { + /* resuming old hash contexts is not supported */ + rc = TPM_RC_DISABLED; + } + else + { + if (size != sizeof(OLD_OBJECT)) + return TPM_RC_SIZE; + MemoryCopy(&oldObject, buffer, sizeof(OLD_OBJECT)); + + /* fill the newObject with the contents of the oldObject */ + newObject->attributes = oldObject.attributes; + + newObject->publicArea.type = oldObject.publicArea.type; + newObject->publicArea.nameAlg = oldObject.publicArea.nameAlg; + newObject->publicArea.objectAttributes = oldObject.publicArea.objectAttributes; + newObject->publicArea.authPolicy = oldObject.publicArea.authPolicy; + newObject->publicArea.parameters = oldObject.publicArea.parameters; + /* the unique part can be one or two TPM2B's */ + switch (newObject->publicArea.type) { + case TPM_ALG_KEYEDHASH: + MemoryCopy2B(&newObject->publicArea.unique.keyedHash.b, + &oldObject.publicArea.unique.keyedHash.b, + sizeof(oldObject.publicArea.unique.keyedHash.t.buffer)); + break; + case TPM_ALG_SYMCIPHER: + MemoryCopy2B(&newObject->publicArea.unique.sym.b, + &oldObject.publicArea.unique.sym.b, + sizeof(oldObject.publicArea.unique.sym.t.buffer)); + break; + case TPM_ALG_RSA: + MemoryCopy2B(&newObject->publicArea.unique.rsa.b, + &oldObject.publicArea.unique.rsa.b, + sizeof(oldObject.publicArea.unique.rsa.t.buffer)); + break; + case TPM_ALG_ECC: + MemoryCopy2B(&newObject->publicArea.unique.ecc.x.b, + &oldObject.publicArea.unique.ecc.x.b, + sizeof(oldObject.publicArea.unique.ecc.x.t.buffer)); + MemoryCopy2B(&newObject->publicArea.unique.ecc.y.b, + &oldObject.publicArea.unique.ecc.y.b, + sizeof(oldObject.publicArea.unique.ecc.y.t.buffer)); + break; + } + + newObject->sensitive.sensitiveType = oldObject.sensitive.sensitiveType; + newObject->sensitive.authValue = oldObject.sensitive.authValue; + newObject->sensitive.seedValue = oldObject.sensitive.seedValue; + /* The OLD_TPMU_SENSITIVE_COMPOSITE is always a TPM2B */ + MemoryCopy2B(&newObject->sensitive.sensitive.any.b, + &oldObject.sensitive.sensitive.any.b, + sizeof(oldObject.sensitive.sensitive.any.t.buffer)); + + CopyFromOldPrimeT(&newObject->privateExponent.Q, &oldObject.privateExponent.Q); + CopyFromOldPrimeT(&newObject->privateExponent.dP, &oldObject.privateExponent.dP); + CopyFromOldPrimeT(&newObject->privateExponent.dQ, &oldObject.privateExponent.dQ); + CopyFromOldPrimeT(&newObject->privateExponent.qInv, &oldObject.privateExponent.qInv); + + newObject->qualifiedName = oldObject.qualifiedName; + newObject->evictHandle = oldObject.evictHandle; + newObject->name = oldObject.name; + } + + return rc; +} + diff --git a/src/tpm2/BackwardsCompatibilityObject.h b/src/tpm2/BackwardsCompatibilityObject.h new file mode 100644 index 00000000..7a2b1ab5 --- /dev/null +++ b/src/tpm2/BackwardsCompatibilityObject.h @@ -0,0 +1,47 @@ +/********************************************************************************/ +/* */ +/* Backwards compatibility stuff related to OBJECT */ +/* Written by Stefan Berger */ +/* IBM Thomas J. Watson Research Center */ +/* */ +/* (c) Copyright IBM Corporation 2017,2018. */ +/* */ +/* 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. */ +/********************************************************************************/ + +#ifndef BACKWARDS_COMPATIBILITY_OBJECT_H +#define BACKWARDS_COMPATIBILITY_OBJECT_H + +#include "Tpm.h" + +TPM_RC OLD_OBJECTToOBJECT(OBJECT *object, BYTE *buffer, INT32 size); + +#endif /* BACKWARDS_COMPATIBILITY_OBJECT_H */ + diff --git a/src/tpm2/ContextCommands.c b/src/tpm2/ContextCommands.c index 1f039b41..cc0e391b 100644 --- a/src/tpm2/ContextCommands.c +++ b/src/tpm2/ContextCommands.c @@ -61,6 +61,7 @@ #include "Tpm.h" #include "ContextSave_fp.h" +#include "NVMarshal.h" // libtpms added #if CC_ContextSave // Conditional expansion of this file #include "Context_spt_fp.h" /* Error Returns Meaning */ @@ -114,8 +115,11 @@ TPM2_ContextSave( { OBJECT *object = HandleToObject(in->saveHandle); ANY_OBJECT_BUFFER *outObject; - UINT16 objectSize = ObjectIsSequence(object) - ? sizeof(HASH_OBJECT) : sizeof(OBJECT); + unsigned char buffer[sizeof(OBJECT) * 2]; // libtpms changed begin + BYTE *bufptr = &buffer[0]; + INT32 size = sizeof(buffer); + UINT16 written = ANY_OBJECT_Marshal(object, &bufptr, &size); + UINT16 objectSize = written; // libtpms changed end outObject = (ANY_OBJECT_BUFFER *)(out->context.contextBlob.t.buffer + integritySize + fingerprintSize); // Set size of the context data. The contents of context blob is vendor @@ -127,7 +131,7 @@ TPM2_ContextSave( pAssert(out->context.contextBlob.t.size <= sizeof(out->context.contextBlob.t.buffer)); // Copy the whole internal OBJECT structure to context blob - MemoryCopy(outObject, object, objectSize); + MemoryCopy(outObject, buffer, written); // libtpms changed // Increment object context ID gr.objectContextID++; // If object context ID overflows, TPM should be put in failure mode @@ -141,8 +145,10 @@ TPM2_ContextSave( if(ObjectIsSequence(object)) { out->context.savedHandle = 0x80000001; + /* ANY_OBJECT_Marshal already wrote it // libtpms changed begin SequenceDataExport((HASH_OBJECT *)object, (HASH_OBJECT_BUFFER *)outObject); + */ // libtpms changed end } else out->context.savedHandle = (object->attributes.stClear == SET) @@ -296,8 +302,8 @@ TPM2_ContextLoad( if(!HierarchyIsEnabled(in->context.hierarchy)) return TPM_RCS_HIERARCHY + RC_ContextLoad_context; // Restore object. If there is no empty space, indicate as much - outObject = ObjectContextLoad((ANY_OBJECT_BUFFER *)buffer, - &out->loadedHandle); + outObject = ObjectContextLoadLibtpms(buffer, size, // libtpms changed + &out->loadedHandle); if(outObject == NULL) return TPM_RC_OBJECT_MEMORY; break; diff --git a/src/tpm2/Context_spt.c b/src/tpm2/Context_spt.c index 2fb3e3a1..a46d7305 100644 --- a/src/tpm2/Context_spt.c +++ b/src/tpm2/Context_spt.c @@ -153,6 +153,7 @@ ComputeContextIntegrity( CryptHmacEnd2B(&hmacState, &integrity->b); return; } +#if 0 /* 7.3.2.3 SequenceDataExport() */ /* This function is used scan through the sequence object and either modify the hash state data for export (contextSave) or to import it into the internal format (contextLoad). This function should @@ -200,3 +201,4 @@ SequenceDataImport( CryptHashImportState(hash, (EXPORT_HASH_STATE *)importHash); } } +#endif diff --git a/src/tpm2/Object.c b/src/tpm2/Object.c index c5b2e942..8e8b3ee9 100644 --- a/src/tpm2/Object.c +++ b/src/tpm2/Object.c @@ -65,6 +65,8 @@ /* 8.6.2 Includes and Data Definitions */ #define OBJECT_C #include "Tpm.h" +#include "NVMarshal.h" // libtpms added +#include "BackwardsCompatibilityObject.h" // libtpms added /* 8.6.3 Functions */ /* 8.6.3.1 ObjectFlush() */ /* This function marks an object slot as available. Since there is no checking of the input @@ -619,6 +621,7 @@ ObjectTerminateEvent( /* Return Values Meaning */ /* NULL if there is no free slot for an object */ /* NON_NULL points to the loaded object */ +#if 0 // libtpms added OBJECT * ObjectContextLoad( ANY_OBJECT_BUFFER *object, // IN: pointer to object structure in saved @@ -647,6 +650,39 @@ ObjectContextLoad( } return newObject; } +#endif // libtpms added begin + +OBJECT * +ObjectContextLoadLibtpms(BYTE *buffer, + INT32 size, + TPMI_DH_OBJECT *handle + ) +{ + OBJECT *newObject = ObjectAllocateSlot(handle); + TPM_RC rc; + BYTE *mybuf = buffer; + INT32 mysize = size; + + pAssert(handle); + + // Try to allocate a slot for new object + if(newObject != NULL) + { + rc = ANY_OBJECT_Unmarshal(newObject, &mybuf, &mysize, false); + if (rc) { + /* Attempt to load an old OBJECT that was copied out directly from + * an older version of OBJECT. + */ + rc = OLD_OBJECTToOBJECT(newObject, buffer, size); + if (rc) { + FlushObject(*handle); + newObject = NULL; + } + } + } + return newObject; +} // libtpms added end + /* 8.6.3.22 FlushObject() */ /* This function frees an object slot. */ /* This function requires that the object is loaded. */ diff --git a/src/tpm2/Object_fp.h b/src/tpm2/Object_fp.h index 48a0ed11..cbf610fb 100644 --- a/src/tpm2/Object_fp.h +++ b/src/tpm2/Object_fp.h @@ -158,12 +158,20 @@ void ObjectTerminateEvent( void ); +#if 0 // libtpms added OBJECT * ObjectContextLoad( ANY_OBJECT_BUFFER *object, // IN: pointer to object structure in saved // context TPMI_DH_OBJECT *handle // OUT: object handle ); +#endif // libtpms added begin +OBJECT * +ObjectContextLoadLibtpms(BYTE *buffer, // IN: buffer holding the marshaled object + INT32 size, // IN: size of buffer + TPMI_DH_OBJECT *handle // OUT: object handle + ); + // libtpms added end void FlushObject( TPMI_DH_OBJECT handle // IN: handle to be freed diff --git a/src/tpm2/crypto/openssl/CryptHash.c b/src/tpm2/crypto/openssl/CryptHash.c index 4b6cb992..407a2032 100644 --- a/src/tpm2/crypto/openssl/CryptHash.c +++ b/src/tpm2/crypto/openssl/CryptHash.c @@ -236,7 +236,7 @@ CryptHashGetContextAlg( /* 10.2.13.5 State Import and Export */ /* 10.2.13.5.1 CryptHashCopyState */ /* This function is used to clone a HASH_STATE. */ -#if 0 // libtpms added begin +#if 0 // libtpms added LIB_EXPORT void CryptHashCopyState( HASH_STATE *out, // OUT: destination of the state @@ -258,7 +258,8 @@ CryptHashCopyState( } return; } -#endif // libtpms added end +#endif // libtpms added +#if 0 // libtpms added /* 10.2.13.5.2 CryptHashExportState() */ /* This function is used to export a hash or HMAC hash state. This function would be called when preparing to context save a sequence object. */ @@ -336,6 +337,7 @@ CryptHashImportState( } } } +#endif // libtpms added /* 10.2.13.6 State Modification Functions */ /* 10.2.13.6.1 HashEnd() */ /* Local function to complete a hash that uses the hashDef instead of an algorithm ID. This function