// This file was extracted from the TCG Published
// Trusted Platform Module Library
// Part 4: Supporting Routines
// Family "2.0"
// Level 00 Revision 01.16
// October 30, 2014
#include "InternalRoutines.h"
#include "Attest_spt_fp.h"
//
//
// Functions
//
// FillInAttestInfo()
//
// Fill in common fields of TPMS_ATTEST structure.
//
// Error Returns Meaning
//
// TPM_RC_KEY key referenced by signHandle is not a signing key
// TPM_RC_SCHEME both scheme and key's default scheme are empty; or scheme is
// empty while key's default scheme requires explicit input scheme (split
// signing); or non-empty default key scheme differs from scheme
//
TPM_RC
FillInAttestInfo(
TPMI_DH_OBJECT signHandle, // IN: handle of signing object
TPMT_SIG_SCHEME *scheme, // IN/OUT: scheme to be used for signing
TPM2B_DATA *data, // IN: qualifying data
TPMS_ATTEST *attest // OUT: attest structure
)
{
TPM_RC result;
TPMI_RH_HIERARCHY signHierarhcy;
result = CryptSelectSignScheme(signHandle, scheme);
if(result != TPM_RC_SUCCESS)
return result;
// Magic number
attest->magic = TPM_GENERATED_VALUE;
if(signHandle == TPM_RH_NULL)
{
BYTE *buffer;
INT32 bufferSize;
// For null sign handle, the QN is TPM_RH_NULL
buffer = attest->qualifiedSigner.t.name;
bufferSize = sizeof(TPM_HANDLE);
attest->qualifiedSigner.t.size =
TPM_HANDLE_Marshal(&signHandle, &buffer, &bufferSize);
}
else
{
// Certifying object qualified name
// if the scheme is anonymous, this is an empty buffer
if(CryptIsSchemeAnonymous(scheme->scheme))
attest->qualifiedSigner.t.size = 0;
else
ObjectGetQualifiedName(signHandle, &attest->qualifiedSigner);
}
// current clock in plain text
TimeFillInfo(&attest->clockInfo);
// Firmware version in plain text
attest->firmwareVersion = ((UINT64) gp.firmwareV1 << (sizeof(UINT32) * 8));
attest->firmwareVersion += gp.firmwareV2;
// Get the hierarchy of sign object. For NULL sign handle, the hierarchy
// will be TPM_RH_NULL
signHierarhcy = EntityGetHierarchy(signHandle);
if(signHierarhcy != TPM_RH_PLATFORM && signHierarhcy != TPM_RH_ENDORSEMENT)
{
// For sign object is not in platform or endorsement hierarchy,
// obfuscate the clock and firmwereVersion information
UINT64 obfuscation[2];
TPMI_ALG_HASH hashAlg;
// Get hash algorithm
if(signHandle == TPM_RH_NULL || signHandle == TPM_RH_OWNER)
{
hashAlg = CONTEXT_INTEGRITY_HASH_ALG;
}
else
{
OBJECT *signObject = NULL;
signObject = ObjectGet(signHandle);
hashAlg = signObject->publicArea.nameAlg;
}
KDFa(hashAlg, &gp.shProof.b, "OBFUSCATE",
&attest->qualifiedSigner.b, NULL, 128, (BYTE *)&obfuscation[0], NULL);
// Obfuscate data
attest->firmwareVersion += obfuscation[0];
attest->clockInfo.resetCount += (UINT32)(obfuscation[1] >> 32);
attest->clockInfo.restartCount += (UINT32)obfuscation[1];
}
// External data
if(CryptIsSchemeAnonymous(scheme->scheme))
attest->extraData.t.size = 0;
else
{
// If we move the data to the attestation structure, then we will not use
// it in the signing operation except as part of the signed data
attest->extraData = *data;
data->t.size = 0;
}
return TPM_RC_SUCCESS;
}
//
//
// SignAttestInfo()
//
// Sign a TPMS_ATTEST structure. If signHandle is TPM_RH_NULL, a null signature is returned.
//
//
//
//
// Error Returns Meaning
//
// TPM_RC_ATTRIBUTES signHandle references not a signing key
// TPM_RC_SCHEME scheme is not compatible with signHandle type
// TPM_RC_VALUE digest generated for the given scheme is greater than the modulus of
// signHandle (for an RSA key); invalid commit status or failed to
// generate r value (for an ECC key)
//
TPM_RC
SignAttestInfo(
TPMI_DH_OBJECT signHandle, // IN: handle of sign object
TPMT_SIG_SCHEME *scheme, // IN: sign scheme
TPMS_ATTEST *certifyInfo, // IN: the data to be signed
TPM2B_DATA *qualifyingData, // IN: extra data for the signing proce
TPM2B_ATTEST *attest, // OUT: marshaled attest blob to be
// signed
TPMT_SIGNATURE *signature // OUT: signature
)
{
TPM_RC result;
TPMI_ALG_HASH hashAlg;
BYTE *buffer;
INT32 bufferSize;
HASH_STATE hashState;
TPM2B_DIGEST digest;
// Marshal TPMS_ATTEST structure for hash
buffer = attest->t.attestationData;
bufferSize = sizeof(TPMS_ATTEST);
attest->t.size = TPMS_ATTEST_Marshal(certifyInfo, &buffer, &bufferSize);
if(signHandle == TPM_RH_NULL)
{
signature->sigAlg = TPM_ALG_NULL;
}
else
{
// Attestation command may cause the orderlyState to be cleared due to
// the reporting of clock info. 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;
}
// Compute hash
hashAlg = scheme->details.any.hashAlg;
digest.t.size = CryptStartHash(hashAlg, &hashState);
CryptUpdateDigest(&hashState, attest->t.size, attest->t.attestationData);
CryptCompleteHash2B(&hashState, &digest.b);
// If there is qualifying data, need to rehash the the data
// hash(qualifyingData || hash(attestationData))
if(qualifyingData->t.size != 0)
{
CryptStartHash(hashAlg, &hashState);
CryptUpdateDigest(&hashState,
qualifyingData->t.size,
qualifyingData->t.buffer);
CryptUpdateDigest(&hashState, digest.t.size, digest.t.buffer);
CryptCompleteHash2B(&hashState, &digest.b);
}
// Sign the hash. A TPM_RC_VALUE, TPM_RC_SCHEME, or
// TPM_RC_ATTRIBUTES error may be returned at this point
return CryptSign(signHandle,
scheme,
&digest,
signature);
}
return TPM_RC_SUCCESS;
}