/* 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); | |
} | |
} |