// This file was extracted from the TCG Published
// Trusted Platform Module Library
// Part 3: Commands
// Family "2.0"
// Level 00 Revision 01.16
// October 30, 2014
#include "InternalRoutines.h"
#include "ContextSave_fp.h"
#include "Context_spt_fp.h"
//
//
// Error Returns Meaning
//
// TPM_RC_CONTEXT_GAP a contextID could not be assigned for a session context save
// TPM_RC_TOO_MANY_CONTEXTS no more contexts can be saved as the counter has maxed out
//
TPM_RC
TPM2_ContextSave(
ContextSave_In *in, // IN: input parameter list
ContextSave_Out *out // OUT: output parameter list
)
{
TPM_RC result;
UINT16 fingerprintSize; // The size of fingerprint in context
// blob.
UINT64 contextID = 0; // session context ID
TPM2B_SYM_KEY symKey;
TPM2B_IV iv;
TPM2B_DIGEST integrity;
UINT16 integritySize;
BYTE *buffer;
INT32 bufferSize;
// This command may cause the orderlyState to be cleared due to
// the update of state reset data. If this is the case, check if NV is
// available first
if(gp.orderlyState != SHUTDOWN_NONE)
{
// The command needs NV update. Check if NV is available.
// A TPM_RC_NV_UNAVAILABLE or TPM_RC_NV_RATE error may be returned at
// this point
result = NvIsAvailable();
if(result != TPM_RC_SUCCESS) return result;
}
// Internal Data Update
// Initialize output handle. At the end of command action, the output
// handle of an object will be replaced, while the output handle
// for a session will be the same as input
out->context.savedHandle = in->saveHandle;
// Get the size of fingerprint in context blob. The sequence value in
// TPMS_CONTEXT structure is used as the fingerprint
fingerprintSize = sizeof(out->context.sequence);
// Compute the integrity size at the beginning of context blob
integritySize = sizeof(integrity.t.size)
+ CryptGetHashDigestSize(CONTEXT_INTEGRITY_HASH_ALG);
// Perform object or session specific context save
switch(HandleGetType(in->saveHandle))
{
case TPM_HT_TRANSIENT:
{
OBJECT *object = ObjectGet(in->saveHandle);
OBJECT *outObject =
(OBJECT *)(out->context.contextBlob.t.buffer
+ integritySize + fingerprintSize);
// Set size of the context data. The contents of context blob is vendor
// defined. In this implementation, the size is size of integrity
// plus fingerprint plus the whole internal OBJECT structure
out->context.contextBlob.t.size = integritySize +
fingerprintSize + sizeof(OBJECT);
// Make sure things fit
pAssert(out->context.contextBlob.t.size
< sizeof(out->context.contextBlob.t.buffer));
// Copy the whole internal OBJECT structure to context blob, leave
// the size for fingerprint
*outObject = *object;
// Increment object context ID
gr.objectContextID++;
// If object context ID overflows, TPM should be put in failure mode
if(gr.objectContextID == 0)
FAIL(FATAL_ERROR_INTERNAL);
// Fill in other return values for an object.
out->context.sequence = gr.objectContextID;
// For regular object, savedHandle is 0x80000000. For sequence object,
// savedHandle is 0x80000001. For object with stClear, savedHandle
// is 0x80000002
if(ObjectIsSequence(object))
{
out->context.savedHandle = 0x80000001;
SequenceDataImportExport(object, outObject, EXPORT_STATE);
}
else if(object->attributes.stClear == SET)
{
out->context.savedHandle = 0x80000002;
}
else
{
out->context.savedHandle = 0x80000000;
}
// Get object hierarchy
out->context.hierarchy = ObjectDataGetHierarchy(object);
break;
}
case TPM_HT_HMAC_SESSION:
case TPM_HT_POLICY_SESSION:
{
SESSION *session = SessionGet(in->saveHandle);
// Set size of the context data. The contents of context blob is vendor
// defined. In this implementation, the size of context blob is the
// size of a internal session structure plus the size of
// fingerprint plus the size of integrity
out->context.contextBlob.t.size = integritySize +
fingerprintSize + sizeof(*session);
// Make sure things fit
pAssert(out->context.contextBlob.t.size
< sizeof(out->context.contextBlob.t.buffer));
// Copy the whole internal SESSION structure to context blob.
// Save space for fingerprint at the beginning of the buffer
// This is done before anything else so that the actual context
// can be reclaimed after this call
MemoryCopy(out->context.contextBlob.t.buffer
+ integritySize + fingerprintSize,
session, sizeof(*session),
sizeof(out->context.contextBlob.t.buffer)
- integritySize - fingerprintSize);
// Fill in the other return parameters for a session
// Get a context ID and set the session tracking values appropriately
// TPM_RC_CONTEXT_GAP is a possible error.
// SessionContextSave() will flush the in-memory context
// so no additional errors may occur after this call.
result = SessionContextSave(out->context.savedHandle, &contextID);
if(result != TPM_RC_SUCCESS) return result;
// sequence number is the current session contextID
out->context.sequence = contextID;
// use TPM_RH_NULL as hierarchy for session context
out->context.hierarchy = TPM_RH_NULL;
break;
}
default:
// SaveContext may only take an object handle or a session handle.
// All the other handle type should be filtered out at unmarshal
pAssert(FALSE);
break;
}
// Save fingerprint at the beginning of encrypted area of context blob.
// Reserve the integrity space
MemoryCopy(out->context.contextBlob.t.buffer + integritySize,
&out->context.sequence, sizeof(out->context.sequence),
sizeof(out->context.contextBlob.t.buffer) - integritySize);
// Compute context encryption key
ComputeContextProtectionKey(&out->context, &symKey, &iv);
// Encrypt context blob
CryptSymmetricEncrypt(out->context.contextBlob.t.buffer + integritySize,
CONTEXT_ENCRYPT_ALG, CONTEXT_ENCRYPT_KEY_BITS,
TPM_ALG_CFB, symKey.t.buffer, &iv,
out->context.contextBlob.t.size - integritySize,
out->context.contextBlob.t.buffer + integritySize);
// Compute integrity hash for the object
// In this implementation, the same routine is used for both sessions
// and objects.
ComputeContextIntegrity(&out->context, &integrity);
// add integrity at the beginning of context blob
buffer = out->context.contextBlob.t.buffer;
bufferSize = sizeof(TPM2B_DIGEST);
TPM2B_DIGEST_Marshal(&integrity, &buffer, &bufferSize);
// orderly state should be cleared because of the update of state reset and
// state clear data
g_clearOrderly = TRUE;
return TPM_RC_SUCCESS;
}