/* Microsoft Reference Implementation for TPM 2.0 * * The copyright in this software is being made available under the BSD License, * included below. This software may be subject to other third party and * contributor rights, including patent rights, and no such rights are granted * under this license. * * Copyright (c) Microsoft Corporation * * All rights reserved. * * BSD License * * 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. * * 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. */ //** Includes #include "Tpm.h" #include "Context_spt_fp.h" //** Functions //*** ComputeContextProtectionKey() // This function retrieves the symmetric protection key for context encryption // It is used by TPM2_ConextSave and TPM2_ContextLoad to create the symmetric // encryption key and iv /*(See part 1 specification) KDFa is used to generate the symmetric encryption key and IV. The parameters of the call are: Symkey = KDFa(hashAlg, hProof, vendorString, sequence, handle, bits) where hashAlg a vendor-defined hash algorithm hProof the hierarchy proof as selected by the hierarchy parameter of the TPMS_CONTEXT vendorString a value used to differentiate the uses of the KDF sequence the sequence parameter of the TPMS_CONTEXT handle the handle parameter of the TPMS_CONTEXT bits the number of bits needed for a symmetric key and IV for the context encryption */ // Return Type: void void ComputeContextProtectionKey( TPMS_CONTEXT *contextBlob, // IN: context blob TPM2B_SYM_KEY *symKey, // OUT: the symmetric key TPM2B_IV *iv // OUT: the IV. ) { UINT16 symKeyBits; // number of bits in the parent's // symmetric key TPM2B_PROOF *proof = NULL; // the proof value to use. Is null for // everything but a primary object in // the Endorsement Hierarchy BYTE kdfResult[sizeof(TPMU_HA) * 2];// Value produced by the KDF TPM2B_DATA sequence2B, handle2B; // Get proof value proof = HierarchyGetProof(contextBlob->hierarchy); // Get sequence value in 2B format sequence2B.t.size = sizeof(contextBlob->sequence); cAssert(sequence2B.t.size <= sizeof(sequence2B.t.buffer)); MemoryCopy(sequence2B.t.buffer, &contextBlob->sequence, sequence2B.t.size); // Get handle value in 2B format handle2B.t.size = sizeof(contextBlob->savedHandle); cAssert(handle2B.t.size <= sizeof(handle2B.t.buffer)); MemoryCopy(handle2B.t.buffer, &contextBlob->savedHandle, handle2B.t.size); // Get the symmetric encryption key size symKey->t.size = CONTEXT_ENCRYPT_KEY_BYTES; symKeyBits = CONTEXT_ENCRYPT_KEY_BITS; // Get the size of the IV for the algorithm iv->t.size = CryptGetSymmetricBlockSize(CONTEXT_ENCRYPT_ALG, symKeyBits); // KDFa to generate symmetric key and IV value CryptKDFa(CONTEXT_INTEGRITY_HASH_ALG, &proof->b, CONTEXT_KEY, &sequence2B.b, &handle2B.b, (symKey->t.size + iv->t.size) * 8, kdfResult, NULL, FALSE); // Copy part of the returned value as the key pAssert(symKey->t.size <= sizeof(symKey->t.buffer)); MemoryCopy(symKey->t.buffer, kdfResult, symKey->t.size); // Copy the rest as the IV pAssert(iv->t.size <= sizeof(iv->t.buffer)); MemoryCopy(iv->t.buffer, &kdfResult[symKey->t.size], iv->t.size); return; } //*** ComputeContextIntegrity() // Generate the integrity hash for a context // It is used by TPM2_ContextSave to create an integrity hash // and by TPM2_ContextLoad to compare an integrity hash /*(See part 1 specification) The HMAC integrity computation for a saved context is: HMACvendorAlg(hProof, resetValue {|| clearCount} || sequence || handle || encContext) where HMACvendorAlg HMAC using a vendor-defined hash algorithm hProof the hierarchy proof as selected by the hierarchy parameter of the TPMS_CONTEXT resetValue either a counter value that increments on each TPM Reset and is not reset over the lifetime of the TPM or a random value that changes on each TPM Reset and has the size of the digest produced by vendorAlg clearCount a counter value that is incremented on each TPM Reset or TPM Restart. This value is only included if the handle value is 0x80000002. sequence the sequence parameter of the TPMS_CONTEXT handle the handle parameter of the TPMS_CONTEXT encContext the encrypted context blob */ // Return Type: void void ComputeContextIntegrity( TPMS_CONTEXT *contextBlob, // IN: context blob TPM2B_DIGEST *integrity // OUT: integrity ) { HMAC_STATE hmacState; TPM2B_PROOF *proof; UINT16 integritySize; // Get proof value proof = HierarchyGetProof(contextBlob->hierarchy); // Start HMAC integrity->t.size = CryptHmacStart2B(&hmacState, CONTEXT_INTEGRITY_HASH_ALG, &proof->b); // Compute integrity size at the beginning of context blob integritySize = sizeof(integrity->t.size) + integrity->t.size; // Adding total reset counter so that the context cannot be // used after a TPM Reset CryptDigestUpdateInt(&hmacState.hashState, sizeof(gp.totalResetCount), gp.totalResetCount); // If this is a ST_CLEAR object, add the clear count // so that this contest cannot be loaded after a TPM Restart if(contextBlob->savedHandle == 0x80000002) CryptDigestUpdateInt(&hmacState.hashState, sizeof(gr.clearCount), gr.clearCount); // Adding sequence number to the HMAC to make sure that it doesn't // get changed CryptDigestUpdateInt(&hmacState.hashState, sizeof(contextBlob->sequence), contextBlob->sequence); // Protect the handle CryptDigestUpdateInt(&hmacState.hashState, sizeof(contextBlob->savedHandle), contextBlob->savedHandle); // Adding sensitive contextData, skip the leading integrity area CryptDigestUpdate(&hmacState.hashState, contextBlob->contextBlob.t.size - integritySize, contextBlob->contextBlob.t.buffer + integritySize); // Complete HMAC CryptHmacEnd2B(&hmacState, &integrity->b); return; } //*** 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 only be called after the sequence object has been copied // to the context buffer (contextSave) or from the context buffer into the sequence // object. The presumption is that the context buffer version of the data is the // same size as the internal representation so nothing outsize of the hash context // area gets modified. void SequenceDataExport( HASH_OBJECT *object, // IN: an internal hash object HASH_OBJECT_BUFFER *exportObject // OUT: a sequence context in a buffer ) { // If the hash object is not an event, then only one hash context is needed int count = (object->attributes.eventSeq) ? HASH_COUNT : 1; for(count--; count >= 0; count--) { HASH_STATE *hash = &object->state.hashState[count]; size_t offset = (BYTE *)hash - (BYTE *)object; BYTE *exportHash = &((BYTE *)exportObject)[offset]; CryptHashExportState(hash, (EXPORT_HASH_STATE *)exportHash); } } //*** SequenceDataImport(); // 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 only be called after the sequence object has been copied // to the context buffer (contextSave) or from the context buffer into the sequence // object. The presumption is that the context buffer version of the data is the // same size as the internal representation so nothing outsize of the hash context // area gets modified. void SequenceDataImport( HASH_OBJECT *object, // IN/OUT: an internal hash object HASH_OBJECT_BUFFER *exportObject // IN/OUT: a sequence context in a buffer ) { // If the hash object is not an event, then only one hash context is needed int count = (object->attributes.eventSeq) ? HASH_COUNT : 1; for(count--; count >= 0; count--) { HASH_STATE *hash = &object->state.hashState[count]; size_t offset = (BYTE *)hash - (BYTE *)object; BYTE *importHash = &((BYTE *)exportObject)[offset]; // CryptHashImportState(hash, (EXPORT_HASH_STATE *)importHash); } }