/*
* Licensed Materials - Property of IBM
*
* trousers - An open source TCG Software Stack
*
* (C) Copyright International Business Machines Corp. 2004-2006
*
*/
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <limits.h>
#include "trousers/tss.h"
#include "trousers/trousers.h"
#include "trousers_types.h"
#include "spi_utils.h"
#include "capabilities.h"
#include "tsplog.h"
#include "obj.h"
#include "authsess.h"
TSS_RESULT
Tspi_TPM_CollateIdentityRequest(TSS_HTPM hTPM, /* in */
TSS_HKEY hKeySRK, /* in */
TSS_HKEY hCAPubKey, /* in */
UINT32 ulIdentityLabelLength, /* in */
BYTE * rgbIdentityLabelData, /* in */
TSS_HKEY hIdentityKey, /* in */
TSS_ALGORITHM_ID algID, /* in */
UINT32 * pulTcpaIdentityReqLength, /* out */
BYTE ** prgbTcpaIdentityReq) /* out */
{
#ifdef TSS_BUILD_TRANSPORT
UINT32 transport;
#endif
TPM_AUTH srkAuth;
TCPA_RESULT result;
UINT64 offset;
BYTE hashblob[USHRT_MAX], idReqBlob[USHRT_MAX], testblob[USHRT_MAX];
TCPA_DIGEST digest;
TSS_HPOLICY hSRKPolicy, hIDPolicy, hCAPolicy;
UINT32 caKeyBlobSize, idKeySize, idPubSize;
BYTE *caKeyBlob, *idKey, *newIdKey, *idPub;
TSS_KEY caKey;
TCPA_CHOSENID_HASH chosenIDHash = { { 0, } };
UINT32 pcIdentityBindingSize;
BYTE *prgbIdentityBinding = NULL;
UINT32 pcEndorsementCredentialSize;
BYTE *prgbEndorsementCredential = NULL;
UINT32 pcPlatformCredentialSize;
BYTE *prgbPlatformCredential = NULL;
UINT32 pcConformanceCredentialSize;
BYTE *prgbConformanceCredential = NULL;
#define CHOSENID_BLOB_SIZE 2048
BYTE chosenIDBlob[CHOSENID_BLOB_SIZE];
TSS_HCONTEXT tspContext;
UINT32 encSymKeySize = 256, tmp;
BYTE encSymKey[256], *cb_var;
TSS_BOOL usesAuth;
TPM_AUTH *pSrkAuth = &srkAuth;
TCPA_IDENTITY_REQ rgbTcpaIdentityReq;
TCPA_KEY_PARMS symParms, asymParms;
TCPA_SYMMETRIC_KEY symKey;
int padding;
TSS_CALLBACK *cb;
Trspi_HashCtx hashCtx;
UINT32 tempCredSize;
BYTE *tempCred = NULL;
struct authsess *xsap = NULL;
if (pulTcpaIdentityReqLength == NULL || prgbTcpaIdentityReq == NULL)
return TSPERR(TSS_E_BAD_PARAMETER);
if ((result = obj_tpm_get_tsp_context(hTPM, &tspContext)))
return result;
if ((result = obj_tpm_get_cb12(hTPM, TSS_TSPATTRIB_TPM_CALLBACK_COLLATEIDENTITY, &tmp,
&cb_var)))
return result;
cb = (TSS_CALLBACK *)cb_var;
if (cb->callback == NULL) {
free_tspi(tspContext, cb);
cb = NULL;
}
/* Get Policies */
if ((result = obj_rsakey_get_policy(hKeySRK, TSS_POLICY_USAGE, &hSRKPolicy, &usesAuth)))
return result;
if ((result = obj_rsakey_get_policy(hCAPubKey, TSS_POLICY_USAGE,
&hCAPolicy, NULL)))
return result;
if ((result = obj_rsakey_get_policy(hIdentityKey, TSS_POLICY_USAGE,
&hIDPolicy, NULL)))
return result;
/* setup the symmetric key's parms. */
__tspi_memset(&symParms, 0, sizeof(TCPA_KEY_PARMS));
switch (algID) {
case TSS_ALG_AES:
symParms.algorithmID = TCPA_ALG_AES;
symKey.algId = TCPA_ALG_AES;
symKey.size = 128/8;
break;
case TSS_ALG_DES:
symParms.algorithmID = TCPA_ALG_DES;
symKey.algId = TCPA_ALG_DES;
symKey.size = 64/8;
break;
case TSS_ALG_3DES:
symParms.algorithmID = TCPA_ALG_3DES;
symKey.algId = TCPA_ALG_3DES;
symKey.size = 192/8;
break;
default:
result = TSPERR(TSS_E_BAD_PARAMETER);
goto error;
break;
}
/* No symmetric key encryption schemes existed in the 1.1 time frame */
symParms.encScheme = TCPA_ES_NONE;
/* get the CA Pubkey's encryption scheme */
if ((result = obj_rsakey_get_es(hCAPubKey, &tmp)))
return TSPERR(TSS_E_BAD_PARAMETER);
switch (tmp) {
case TSS_ES_RSAESPKCSV15:
padding = TR_RSA_PKCS1_PADDING;
break;
case TSS_ES_RSAESOAEP_SHA1_MGF1:
padding = TR_RSA_PKCS1_OAEP_PADDING;
break;
case TSS_ES_NONE:
/* fall through */
default:
padding = TR_RSA_NO_PADDING;
break;
}
/* Get Key blobs */
if ((result = obj_rsakey_get_blob(hIdentityKey, &idKeySize, &idKey)))
return result;
if ((result = obj_rsakey_get_blob(hCAPubKey, &caKeyBlobSize, &caKeyBlob)))
return result;
offset = 0;
__tspi_memset(&caKey, 0, sizeof(TSS_KEY));
if ((result = UnloadBlob_TSS_KEY(&offset, caKeyBlob, &caKey)))
return result;
/* ChosenID hash = SHA1(label || TCPA_PUBKEY(CApub)) */
offset = 0;
Trspi_LoadBlob(&offset, ulIdentityLabelLength, chosenIDBlob, rgbIdentityLabelData);
Trspi_LoadBlob_KEY_PARMS(&offset, chosenIDBlob, &caKey.algorithmParms);
Trspi_LoadBlob_STORE_PUBKEY(&offset, chosenIDBlob, &caKey.pubKey);
if (offset > CHOSENID_BLOB_SIZE)
return TSPERR(TSS_E_INTERNAL_ERROR);
if ((result = Trspi_Hash(TSS_HASH_SHA1, offset, chosenIDBlob, chosenIDHash.digest))) {
free_key_refs(&caKey);
return result;
}
/* use chosenIDBlob temporarily */
offset = 0;
Trspi_LoadBlob_KEY_PARMS(&offset, chosenIDBlob, &caKey.algorithmParms);
offset = 0;
if ((result = Trspi_UnloadBlob_KEY_PARMS(&offset, chosenIDBlob, &asymParms)))
return result;
if ((result = authsess_xsap_init(tspContext, hTPM, hIdentityKey, TSS_AUTH_POLICY_REQUIRED,
TPM_ORD_MakeIdentity, TPM_ET_OWNER, &xsap))){
free(asymParms.parms);
return result;
}
/* Hash the Auth data */
result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_MakeIdentity);
result |= Trspi_Hash_ENCAUTH(&hashCtx, xsap->encAuthUse.authdata);
result |= Trspi_HashUpdate(&hashCtx, TCPA_SHA1_160_HASH_LEN, chosenIDHash.digest);
result |= Trspi_HashUpdate(&hashCtx, idKeySize, idKey);
if ((result |= Trspi_HashFinal(&hashCtx, digest.digest)))
goto error;
/* Do the Auth's */
if (usesAuth) {
if ((result = secret_PerformAuth_OIAP(hKeySRK, TPM_ORD_MakeIdentity, hSRKPolicy,
FALSE, &digest, &srkAuth)))
goto error;
pSrkAuth = &srkAuth;
} else {
pSrkAuth = NULL;
}
if ((result = authsess_xsap_hmac(xsap, &digest)))
goto error;
#ifdef TSS_BUILD_TRANSPORT
if ((result = obj_context_transport_get_control(tspContext, TSS_TSPATTRIB_ENABLE_TRANSPORT,
&transport)))
goto error;
if (transport) {
if ((result = Transport_MakeIdentity2(tspContext, xsap->encAuthUse, chosenIDHash,
idKeySize, idKey, pSrkAuth, xsap->pAuth,
&idKeySize, &newIdKey, &pcIdentityBindingSize,
&prgbIdentityBinding)))
goto error;
} else {
#endif
if ((result = RPC_MakeIdentity(tspContext, xsap->encAuthUse, chosenIDHash,
idKeySize, idKey, pSrkAuth, xsap->pAuth, &idKeySize,
&newIdKey, &pcIdentityBindingSize,
&prgbIdentityBinding, &pcEndorsementCredentialSize,
&prgbEndorsementCredential,
&pcPlatformCredentialSize, &prgbPlatformCredential,
&pcConformanceCredentialSize,
&prgbConformanceCredential)))
goto error;
#ifdef TSS_BUILD_TRANSPORT
}
#endif
result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
result |= Trspi_Hash_UINT32(&hashCtx, result);
result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_MakeIdentity);
result |= Trspi_HashUpdate(&hashCtx, idKeySize, newIdKey);
result |= Trspi_Hash_UINT32(&hashCtx, pcIdentityBindingSize);
result |= Trspi_HashUpdate(&hashCtx, pcIdentityBindingSize, prgbIdentityBinding);
if ((result |= Trspi_HashFinal(&hashCtx, digest.digest))) {
free(newIdKey);
goto error;
}
if ((result = authsess_xsap_verify(xsap, &digest))) {
free(newIdKey);
goto error;
}
if (usesAuth == TRUE) {
if ((result = obj_policy_validate_auth_oiap(hSRKPolicy, &digest, &srkAuth))) {
free(newIdKey);
goto error;
}
}
if ((result = obj_rsakey_set_tcpakey(hIdentityKey, idKeySize, newIdKey))) {
free(newIdKey);
goto error;
}
free(newIdKey);
if ((result = obj_rsakey_set_tcs_handle(hIdentityKey, 0)))
goto error;
if ((result = obj_rsakey_get_pub_blob(hIdentityKey, &idPubSize, &idPub)))
goto error;
if ((result = obj_tpm_get_cred(hTPM, TSS_TPMATTRIB_EKCERT, &tempCredSize, &tempCred)))
goto error;
if (tempCred != NULL) {
free(prgbEndorsementCredential);
prgbEndorsementCredential = tempCred;
pcEndorsementCredentialSize = tempCredSize;
}
if ((result = obj_tpm_get_cred(hTPM, TSS_TPMATTRIB_TPM_CC, &tempCredSize, &tempCred)))
goto error;
if (tempCred != NULL) {
free(prgbConformanceCredential);
prgbConformanceCredential = tempCred;
pcConformanceCredentialSize = tempCredSize;
}
if ((result = obj_tpm_get_cred(hTPM, TSS_TPMATTRIB_PLATFORMCERT, &tempCredSize, &tempCred)))
goto error;
if (tempCred != NULL) {
free(prgbPlatformCredential);
prgbPlatformCredential = tempCred;
pcPlatformCredentialSize = tempCredSize;
}
/* set up the TCPA_IDENTITY_PROOF structure */
/* XXX This should be DER encoded first. TPM1.1b section 9.4 */
/* XXX hash this incrementally using a Trspi_HashCtx */
offset = 0;
Trspi_LoadBlob_TSS_VERSION(&offset, hashblob, VERSION_1_1);
Trspi_LoadBlob_UINT32(&offset, ulIdentityLabelLength, hashblob);
Trspi_LoadBlob_UINT32(&offset, pcIdentityBindingSize, hashblob);
Trspi_LoadBlob_UINT32(&offset, pcEndorsementCredentialSize, hashblob);
Trspi_LoadBlob_UINT32(&offset, pcPlatformCredentialSize, hashblob);
Trspi_LoadBlob_UINT32(&offset, pcConformanceCredentialSize, hashblob);
Trspi_LoadBlob(&offset, idPubSize, hashblob, idPub);
free_tspi(tspContext, idPub);
Trspi_LoadBlob(&offset, ulIdentityLabelLength, hashblob, rgbIdentityLabelData);
Trspi_LoadBlob(&offset, pcIdentityBindingSize, hashblob, prgbIdentityBinding);
Trspi_LoadBlob(&offset, pcEndorsementCredentialSize, hashblob, prgbEndorsementCredential);
Trspi_LoadBlob(&offset, pcPlatformCredentialSize, hashblob, prgbPlatformCredential);
Trspi_LoadBlob(&offset, pcConformanceCredentialSize, hashblob, prgbConformanceCredential);
if (cb && cb->callback) {
/* Alloc the space for the callback to copy into. The additional 32 bytes will
* attempt to account for padding that the symmetric encryption will do. */
rgbTcpaIdentityReq.asymBlob = calloc(1, (int)offset + 32);
rgbTcpaIdentityReq.symBlob = calloc(1, (int)offset + 32);
if (rgbTcpaIdentityReq.asymBlob == NULL ||
rgbTcpaIdentityReq.symBlob == NULL) {
free(rgbTcpaIdentityReq.asymBlob);
free(rgbTcpaIdentityReq.symBlob);
LogError("malloc of %" PRIu64 " bytes failed", offset);
free_tspi(tspContext, cb);
result = TSPERR(TSS_E_OUTOFMEMORY);
goto error;
}
rgbTcpaIdentityReq.asymSize = (UINT32)offset + 32;
rgbTcpaIdentityReq.symSize = (UINT32)offset + 32;
if ((result = ((TSS_RESULT (*)(PVOID, UINT32, BYTE *, UINT32, UINT32 *, BYTE *,
UINT32 *, BYTE *))cb->callback)(cb->appData, (UINT32)offset,
hashblob, algID,
&rgbTcpaIdentityReq.asymSize,
rgbTcpaIdentityReq.asymBlob,
&rgbTcpaIdentityReq.symSize,
rgbTcpaIdentityReq.symBlob))) {
LogDebug("CollateIdentityRequest callback returned error 0x%x", result);
free_tspi(tspContext, cb);
goto error;
}
} else {
/* generate the symmetric key. */
if ((result = get_local_random(tspContext, TRUE, symKey.size, &symKey.data)))
goto error;
/* No symmetric key encryption schemes existed in the 1.1 time frame */
symKey.encScheme = TCPA_ES_NONE;
/* encrypt the proof */
rgbTcpaIdentityReq.symSize = sizeof(testblob);
if ((result = Trspi_SymEncrypt(algID, TR_SYM_MODE_CBC, symKey.data, NULL, hashblob,
offset, testblob, &rgbTcpaIdentityReq.symSize)))
goto error;
rgbTcpaIdentityReq.symBlob = testblob;
/* XXX This should be DER encoded first. TPM1.1b section 9.4 */
offset = 0;
Trspi_LoadBlob_SYMMETRIC_KEY(&offset, hashblob, &symKey);
if ((result = Trspi_RSA_Public_Encrypt(hashblob, offset, encSymKey, &encSymKeySize,
caKey.pubKey.key, caKey.pubKey.keyLength,
65537, padding)))
goto error;
rgbTcpaIdentityReq.asymSize = encSymKeySize;
rgbTcpaIdentityReq.asymBlob = encSymKey;
}
rgbTcpaIdentityReq.asymAlgorithm = asymParms;
rgbTcpaIdentityReq.symAlgorithm = symParms;
/* XXX This should be DER encoded first. TPM1.1b section 9.4 */
offset = 0;
Trspi_LoadBlob_IDENTITY_REQ(&offset, idReqBlob, &rgbTcpaIdentityReq);
if (cb && cb->callback) {
free(rgbTcpaIdentityReq.symBlob);
free(rgbTcpaIdentityReq.asymBlob);
free_tspi(tspContext, cb);
}
if ((*prgbTcpaIdentityReq = calloc_tspi(tspContext, offset)) == NULL) {
result = TSPERR(TSS_E_OUTOFMEMORY);
goto error;
}
memcpy(*prgbTcpaIdentityReq, idReqBlob, offset);
*pulTcpaIdentityReqLength = offset;
error:
authsess_free(xsap);
free_key_refs(&caKey);
free(asymParms.parms);
free(prgbIdentityBinding);
free(prgbEndorsementCredential);
free(prgbPlatformCredential);
free(prgbConformanceCredential);
return result;
}
TSS_RESULT
Tspi_TPM_ActivateIdentity(TSS_HTPM hTPM, /* in */
TSS_HKEY hIdentKey, /* in */
UINT32 ulAsymCAContentsBlobLength, /* in */
BYTE * rgbAsymCAContentsBlob, /* in */
UINT32 ulSymCAAttestationBlobLength, /* in */
BYTE * rgbSymCAAttestationBlob, /* in */
UINT32 * pulCredentialLength, /* out */
BYTE ** prgbCredential) /* out */
{
TPM_AUTH idKeyAuth;
TPM_AUTH ownerAuth;
TSS_HCONTEXT tspContext;
TSS_HPOLICY hIDPolicy, hTPMPolicy;
UINT64 offset;
BYTE credBlob[0x1000];
TCPA_DIGEST digest;
TSS_RESULT result;
TCS_KEY_HANDLE tcsKeyHandle;
TSS_BOOL usesAuth;
TPM_AUTH *pIDKeyAuth;
BYTE *symKeyBlob, *credCallback, *cb_var;
UINT32 symKeyBlobLen, credLen, tmp;
TCPA_SYMMETRIC_KEY symKey;
TSS_CALLBACK *cb;
Trspi_HashCtx hashCtx;
TPM_SYM_CA_ATTESTATION symCAAttestation;
if (pulCredentialLength == NULL || prgbCredential == NULL)
return TSPERR(TSS_E_BAD_PARAMETER);
if ((result = obj_tpm_get_tsp_context(hTPM, &tspContext)))
return result;
if ((result = obj_tpm_get_cb12(hTPM, TSS_TSPATTRIB_TPM_CALLBACK_ACTIVATEIDENTITY, &tmp,
&cb_var)))
return result;
cb = (TSS_CALLBACK *)cb_var;
if (cb->callback == NULL) {
free_tspi(tspContext, cb);
cb = NULL;
}
if ((result = obj_rsakey_get_tcs_handle(hIdentKey, &tcsKeyHandle)))
return result;
if ((result = obj_rsakey_get_policy(hIdentKey, TSS_POLICY_USAGE,
&hIDPolicy, &usesAuth)))
return result;
if ((result = obj_tpm_get_policy(hTPM, TSS_POLICY_USAGE, &hTPMPolicy)))
return result;
result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_ActivateIdentity);
result |= Trspi_Hash_UINT32(&hashCtx, ulAsymCAContentsBlobLength);
result |= Trspi_HashUpdate(&hashCtx, ulAsymCAContentsBlobLength, rgbAsymCAContentsBlob);
if ((result |= Trspi_HashFinal(&hashCtx, digest.digest)))
return result;
if (usesAuth) {
if ((result = secret_PerformAuth_OIAP(hIDPolicy, TPM_ORD_ActivateIdentity,
hIDPolicy, FALSE, &digest, &idKeyAuth)))
return result;
pIDKeyAuth = &idKeyAuth;
} else {
pIDKeyAuth = NULL;
}
if ((result = secret_PerformAuth_OIAP(hTPM, TPM_ORD_ActivateIdentity, hTPMPolicy, FALSE,
&digest, &ownerAuth)))
return result;
if ((result = TCS_API(tspContext)->ActivateTPMIdentity(tspContext, tcsKeyHandle,
ulAsymCAContentsBlobLength,
rgbAsymCAContentsBlob, pIDKeyAuth,
&ownerAuth, &symKeyBlobLen,
&symKeyBlob)))
return result;
result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
result |= Trspi_Hash_UINT32(&hashCtx, result);
result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_ActivateIdentity);
result |= Trspi_HashUpdate(&hashCtx, symKeyBlobLen, symKeyBlob);
if ((result |= Trspi_HashFinal(&hashCtx, digest.digest)))
return result;
if (usesAuth) {
if ((result = obj_policy_validate_auth_oiap(hIDPolicy, &digest,
&idKeyAuth))) {
LogDebugFn("Identity key auth validation of the symmetric key failed.");
return result;
}
}
if ((result = obj_policy_validate_auth_oiap(hTPMPolicy, &digest,
&ownerAuth))) {
LogDebugFn("Owner auth validation of the symmetric key failed.");
return result;
}
offset = 0;
if ((result = Trspi_UnloadBlob_SYM_CA_ATTESTATION(&offset, rgbSymCAAttestationBlob,
&symCAAttestation))) {
LogDebugFn("Error unloading CA's attestation blob.");
return result;
}
if (cb && cb->callback) {
/* alloc the space for the callback to copy into */
credCallback = calloc(1, ulSymCAAttestationBlobLength);
if (credCallback == NULL) {
LogDebug("malloc of %u bytes failed", ulSymCAAttestationBlobLength);
free(symKeyBlob);
free_tspi(tspContext, cb);
return TSPERR(TSS_E_INTERNAL_ERROR);
}
credLen = ulSymCAAttestationBlobLength;
if ((result = ((TSS_RESULT (*)(PVOID, UINT32, BYTE *, UINT32, BYTE *, UINT32 *,
BYTE *))cb->callback)(cb->appData, symKeyBlobLen, symKeyBlob,
symCAAttestation.credSize,
symCAAttestation.credential,
&credLen, credCallback))) {
LogDebug("ActivateIdentity callback returned error 0x%x", result);
free(symCAAttestation.credential);
free(symKeyBlob);
free_tspi(tspContext, cb);
free(credCallback);
return TSPERR(TSS_E_INTERNAL_ERROR);
}
free(symCAAttestation.credential);
free_tspi(tspContext, cb);
free(symKeyBlob);
if ((*prgbCredential = calloc_tspi(tspContext, credLen)) == NULL) {
free(credCallback);
return TSPERR(TSS_E_OUTOFMEMORY);
}
memcpy(*prgbCredential, credCallback, credLen);
*pulCredentialLength = credLen;
free(credCallback);
return TSS_SUCCESS;
}
/* decrypt the symmetric blob using the recovered symmetric key */
offset = 0;
if ((result = Trspi_UnloadBlob_SYMMETRIC_KEY(&offset, symKeyBlob, &symKey))) {
free(symCAAttestation.credential);
free(symKeyBlob);
return result;
}
free(symKeyBlob);
if ((result = Trspi_SymDecrypt(symKey.algId, symKey.encScheme, symKey.data, NULL,
symCAAttestation.credential, symCAAttestation.credSize,
credBlob, &credLen))) {
free(symCAAttestation.credential);
free(symKey.data);
return result;
}
free(symCAAttestation.credential);
if ((*prgbCredential = calloc_tspi(tspContext, credLen)) == NULL) {
free(symKey.data);
return TSPERR(TSS_E_OUTOFMEMORY);
}
free(symKey.data);
memcpy(*prgbCredential, credBlob, credLen);
*pulCredentialLength = credLen;
return TSS_SUCCESS;
}