blob: d0daec5c8a03879f4e24dee3c87c8de205718978 [file] [log] [blame]
/* Copyright (c) 2010-2011 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* TPM command library.
*/
#include <stdint.h>
#include <string.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
#include "g_tpm_commands.h"
#include "g_tpm_structures.h"
#include "trunks.h"
#include "trunks_internal.h"
#define TPM_MAX_RSA_KEY_LENGTH 2048
typedef struct {
uint32_t algorithmID;
uint16_t encScheme;
uint16_t sigScheme;
RSA* rsa;
} TtlPubKey;
uint8_t null_pcr_selection[] = {0, 0, 0};
/* Exported functions. */
void TtlInit(void) {
TtlOpenDevice();
}
uint32_t TtlStartup(void) {
TTLDEBUG(("TPM: Startup\n"));
TPM_Startup_rqu request;
request.startupType = TPM_ST_CLEAR;
return Ttl_TPM_Startup(&request);
}
uint32_t TtlSaveState(void) {
TTLDEBUG(("TPM: SaveState\n"));
return Ttl_TPM_SaveState();
}
uint32_t TtlResume(void) {
TTLDEBUG(("TPM: Resume\n"));
TPM_Startup_rqu request;
request.startupType = TPM_ST_STATE;
return Ttl_TPM_Startup(&request);
}
uint32_t TtlSelfTestFull(void) {
TTLDEBUG(("TPM: Self test full\n"));
return Ttl_TPM_SelfTestFull();
}
uint32_t TtlContinueSelfTest(void) {
return Ttl_TPM_ContinueSelfTest();
}
static void TtlInitializePcrInfoShort(TPM_PCR_INFO_SHORT* pcr_info) {
pcr_info->pcrSelection.sizeOfSelect = 3;
pcr_info->pcrSelection.pcrSelect = null_pcr_selection;
pcr_info->localityAtRelease = TPM_ALL_LOCALITIES;
}
uint32_t TtlDefineSpace(uint32_t index, uint32_t perm, uint32_t size,
uint8_t owner_auth[TPM_SHA1_160_HASH_LEN]) {
TPM_NV_DefineSpace_rqu request;
TTLDEBUG(("TPM: TpmlibDefineSpace(0x%x, 0x%x, %d)\n", index, perm, size));
memset(&request, 0, sizeof(request));
request.pubInfo.nvIndex = index;
request.pubInfo.permission.attributes = perm;
request.pubInfo.dataSize = size;
TtlInitializePcrInfoShort(&request.pubInfo.pcrInfoRead);
TtlInitializePcrInfoShort(&request.pubInfo.pcrInfoWrite);
if (owner_auth) {
APSession auth;
uint32_t result;
RETURN_ON_FAILURE(TtlOSAP(&auth, owner_auth, TPM_ET_OWNER, 0));
result = Ttl_TPM_NV_DefineSpace(&request, &auth);
TtlFlushSpecific(auth.handle, TPM_RT_AUTH);
return result;
} else {
return Ttl_TPM_NV_DefineSpace(&request, NULL);
}
}
uint32_t TtlWrite(uint32_t index, uint8_t* data, uint32_t length) {
TPM_NV_WriteValue_rqu request;
TTLDEBUG(("TPM: TtlWrite(0x%x, %d)\n", index, length));
request.nvIndex = index;
request.offset = 0;
request.dataSize = length;
request.data = data;
return Ttl_TPM_NV_WriteValue(&request, NULL);
}
uint32_t TtlRead(uint32_t index, uint8_t* data, uint32_t length) {
TPM_NV_ReadValue_rqu request;
TPM_NV_ReadValue_rsp response;
uint8_t buffer[TPM_MAX_COMMAND_LENGTH];
uint32_t result;
TTLDEBUG(("TPM: TtlRead(0x%x, %d)\n", index, length));
request.nvIndex = index;
request.offset = 0;
request.dataSize = length;
result = Ttl_TPM_NV_ReadValue(&request, &response, buffer,
TPM_MAX_COMMAND_LENGTH, NULL);
if (result == TPM_SUCCESS) {
memcpy(data, response.data, length);
}
return result;
}
uint32_t TtlWriteLock(uint32_t index) {
TTLDEBUG(("TPM: Write lock 0x%x\n", index));
return TtlWrite(index, NULL, 0);
}
uint32_t TtlReadLock(uint32_t index) {
TTLDEBUG(("TPM: Read lock 0x%x\n", index));
return TtlRead(index, NULL, 0);
}
uint32_t TtlAssertPhysicalPresence(void) {
TSC_PhysicalPresence_rqu request;
TTLDEBUG(("TPM: Asserting physical presence\n"));
request.physicalPresence = TPM_PHYSICAL_PRESENCE_PRESENT;
return Ttl_TSC_PhysicalPresence(&request);
}
uint32_t TtlPhysicalPresenceCMDEnable(void) {
TSC_PhysicalPresence_rqu request;
TTLDEBUG(("TPM: Enable the physical presence command\n"));
request.physicalPresence = TPM_PHYSICAL_PRESENCE_CMD_ENABLE;
return Ttl_TSC_PhysicalPresence(&request);
}
uint32_t TtlFinalizePhysicalPresence(void) {
TSC_PhysicalPresence_rqu request;
TTLDEBUG(("TPM: Enable PP cmd, disable HW pp, and set lifetime lock\n"));
request.physicalPresence =
TPM_PHYSICAL_PRESENCE_CMD_ENABLE |
TPM_PHYSICAL_PRESENCE_HW_DISABLE |
TPM_PHYSICAL_PRESENCE_LIFETIME_LOCK;
return Ttl_TSC_PhysicalPresence(&request);
}
uint32_t TtlLockPhysicalPresence(void) {
TSC_PhysicalPresence_rqu request;
TTLDEBUG(("TPM: Lock physical presence\n"));
request.physicalPresence = TPM_PHYSICAL_PRESENCE_LOCK;
return Ttl_TSC_PhysicalPresence(&request);
}
uint32_t TtlSetNvLocked(void) {
TTLDEBUG(("TPM: Set NV locked\n"));
return TtlDefineSpace(TPM_NV_INDEX_LOCK, 0, 0, NULL);
}
int TtlIsOwned(void) {
TPM_ReadPubek_rqu request;
TPM_ReadPubek_rsp response;
uint8_t buffer[TPM_MAX_COMMAND_LENGTH];
uint32_t result;
TTLDEBUG(("TPM: IsOwned?\n"));
result = Ttl_TPM_ReadPubek(&request, &response,
buffer, TPM_MAX_COMMAND_LENGTH);
return (result != TPM_SUCCESS);
}
uint32_t TtlForceClear(void) {
TTLDEBUG(("TPM: Force clear\n"));
return Ttl_TPM_ForceClear();
}
uint32_t TtlSetEnable(void) {
TTLDEBUG(("TPM: Enabling TPM\n"));
return Ttl_TPM_PhysicalEnable();
}
uint32_t TtlClearEnable(void) {
TTLDEBUG(("TPM: Disabling TPM\n"));
return Ttl_TPM_PhysicalDisable();
}
uint32_t TtlSetDeactivated(uint8_t flag) {
TPM_PhysicalSetDeactivated_rqu request;
TTLDEBUG(("TPM: SetDeactivated(%d)\n", flag));
request.state = flag;
return Ttl_TPM_PhysicalSetDeactivated(&request);
}
static uint32_t TtlGetCapUint32(uint32_t cap,
uint32_t subcap,
TPM_GetCapability_rqu* request,
TPM_GetCapability_rsp* response,
uint8_t* buffer) {
uint8_t subcap_array[4];
uint8_t* subcap_cursor = subcap_array;
request->capArea = cap;
request->subCapSize = 4;
request->subCap = subcap_cursor;
TtlMarshal_uint32_t(&subcap_cursor, &subcap);
return Ttl_TPM_GetCapability(request, response, buffer,
TPM_MAX_COMMAND_LENGTH);
}
static uint32_t TtlGetCapFlags(uint32_t subcap,
TPM_GetCapability_rqu* request,
TPM_GetCapability_rsp* response,
uint8_t* buffer) {
return TtlGetCapUint32(TPM_CAP_FLAG, subcap, request, response, buffer);
}
uint32_t TtlGetPermanentFlags(TPM_PERMANENT_FLAGS* flags) {
TPM_GetCapability_rqu request;
TPM_GetCapability_rsp response;
uint8_t buffer[TPM_MAX_COMMAND_LENGTH];
uint32_t result;
TTLDEBUG(("TPM: GetPermanentFlags\n"));
result = TtlGetCapFlags(TPM_CAP_FLAG_PERMANENT,
&request, &response, buffer);
if (result == TPM_SUCCESS) {
assert(response.respSize == sizeof(TPM_PERMANENT_FLAGS));
memcpy(flags, response.resp, sizeof(TPM_PERMANENT_FLAGS));
}
return result;
}
uint32_t TtlGetSTClearFlags(TPM_STCLEAR_FLAGS* flags) {
TPM_GetCapability_rqu request;
TPM_GetCapability_rsp response;
uint8_t buffer[TPM_MAX_COMMAND_LENGTH];
uint32_t result;
TTLDEBUG(("TPM: GetSTClearFlags\n"));
result = TtlGetCapFlags(TPM_CAP_FLAG_VOLATILE,
&request, &response, buffer);
if (result == TPM_SUCCESS) {
/* Ugly assertion, but the struct is padded up by one byte. */
assert(response.respSize == 7 && sizeof(TPM_STCLEAR_FLAGS) - 1 == 7);
memcpy(flags, response.resp, sizeof(TPM_STCLEAR_FLAGS));
}
return result;
}
uint32_t TtlGetFlags(uint8_t* disable,
uint8_t* deactivated,
uint8_t *nvlocked) {
TPM_PERMANENT_FLAGS flags;
uint32_t result = TtlGetPermanentFlags(&flags);
if (result == TPM_SUCCESS) {
if (disable)
*disable = flags.disable;
if (deactivated)
*deactivated = flags.deactivated;
if (nvlocked)
*nvlocked = flags.nvLocked;
TTLDEBUG(("TPM: Got flags disable=%d, deactivated=%d, nvlocked=%d\n",
flags.disable, flags.deactivated, flags.nvLocked));
}
return result;
}
uint32_t TtlSetGlobalLock(void) {
uint32_t x;
TTLDEBUG(("TPM: Set global lock\n"));
return TtlWrite(TPM_NV_INDEX0, (uint8_t*) &x, 0);
}
uint32_t TtlExtend(int pcr_num, const uint8_t* in_digest, uint8_t* out_digest) {
TPM_Extend_rqu request;
TPM_Extend_rsp response;
uint8_t buffer[TPM_MAX_COMMAND_LENGTH];
uint32_t result;
request.pcrNum = pcr_num;
memcpy(request.inDigest.digest, in_digest, TPM_PCR_DIGEST_LENGTH);
result = Ttl_TPM_Extend(&request, &response, buffer, TPM_MAX_COMMAND_LENGTH);
if (result != TPM_SUCCESS) {
return result;
}
memcpy(out_digest, response.outDigest.digest, TPM_PCR_DIGEST_LENGTH);
return result;
}
uint32_t TtlGetPermissions(uint32_t index, uint32_t* permissions) {
TPM_GetCapability_rqu request;
TPM_GetCapability_rsp response;
uint8_t buffer[TPM_MAX_COMMAND_LENGTH];
uint32_t result;
TTLDEBUG(("TPM: GetPermissions\n"));
result = TtlGetCapUint32(TPM_CAP_NV_INDEX, index,
&request, &response, buffer);
if (result == TPM_SUCCESS) {
TPM_NV_DATA_PUBLIC nv_data;
uint8_t* cursor = response.resp;
TtlUnmarshal_TPM_NV_DATA_PUBLIC(&cursor, &nv_data);
*permissions = nv_data.permission.attributes;
}
return result;
}
static void GenerateNonce(TPM_NONCE* nonce) {
RAND_bytes(nonce->nonce, sizeof(nonce->nonce));
}
uint32_t TtlOIAP(APSession* session, uint8_t* secret) {
TPM_OIAP_rsp response;
uint8_t buffer[TPM_MAX_COMMAND_LENGTH];
uint32_t result;
TTLDEBUG(("TPM: OIAP\n"));
result = Ttl_TPM_OIAP(&response, buffer, sizeof(buffer));
if (result == TPM_SUCCESS) {
// Initialize session data.
memset(session, 0, sizeof(APSession));
GenerateNonce(&session->nonceOdd);
session->handle = response.authHandle;
memcpy(&session->nonceEven, &response.nonceEven, sizeof(TPM_NONCE));
memcpy(&session->secret, secret, sizeof(session->secret));
}
return result;
}
uint32_t TtlOSAP(APSession* session, uint8_t* secret,
uint16_t entity_type, uint32_t entity_value) {
TPM_OSAP_rqu request;
TPM_OSAP_rsp response;
uint8_t buffer[TPM_MAX_COMMAND_LENGTH];
uint32_t result;
TTLDEBUG(("TPM: OSAP\n"));
request.entityType = entity_type;
request.entityValue = entity_value;
GenerateNonce(&request.nonceOddOSAP);
result = Ttl_TPM_OSAP(&request, &response, buffer, sizeof(buffer));
if (result == TPM_SUCCESS) {
// Initialize session data.
memset(session, 0, sizeof(APSession));
GenerateNonce(&session->nonceOdd);
memcpy(&session->nonceOddOSAP, &request.nonceOddOSAP, sizeof(TPM_NONCE));
session->handle = response.authHandle;
memcpy(&session->nonceEven, &response.nonceEven, sizeof(TPM_NONCE));
memcpy(&session->nonceEvenOSAP, &response.nonceEvenOSAP, sizeof(TPM_NONCE));
// Compute session secret.
HMAC_CTX hmac_ctx;
HMAC_CTX_init(&hmac_ctx);
HMAC_Init(&hmac_ctx, secret, TPM_SHA1_160_HASH_LEN, EVP_sha1());
HMAC_Update(&hmac_ctx, session->nonceEvenOSAP.nonce,
sizeof(session->nonceEvenOSAP.nonce));
HMAC_Update(&hmac_ctx, session->nonceOddOSAP.nonce,
sizeof(session->nonceOddOSAP.nonce));
HMAC_Final(&hmac_ctx, session->secret, NULL);
HMAC_CTX_cleanup(&hmac_ctx);
}
return result;
}
uint32_t TtlFlushSpecific(uint32_t handle, uint32_t resource_type) {
TPM_FlushSpecific_rqu request;
TTLDEBUG(("TPM: FlushSpecific\n"));
request.handle = handle;
request.resourceType = resource_type;
return Ttl_TPM_FlushSpecific(&request);
}
static uint32_t ConvertPubKey(const TPM_PUBKEY* inkey, TtlPubKey* outkey) {
uint8_t* cursor;
TPM_RSA_KEY_PARMS rsa_parms;
uint8_t pkcs1_buffer[TPM_MAX_COMMAND_LENGTH];
uint32_t pkcs1_length;
// For now, we only support RSA keys.
if (inkey->algorithmParms.algorithmID != TPM_ALG_RSA) {
return TPM_BAD_KEY_PROPERTY;
}
cursor = inkey->algorithmParms.parms;
TtlUnmarshal_TPM_RSA_KEY_PARMS(&cursor, &rsa_parms);
if (TtlSizeCalc_TPM_RSA_KEY_PARMS(&rsa_parms) !=
inkey->algorithmParms.parmSize) {
return TPM_BAD_PARAMETER;
}
if (rsa_parms.exponentSize == 0) {
rsa_parms.exponentSize = 3;
rsa_parms.exponent = (uint8_t*)"\x01\x00\x01";
}
outkey->algorithmID = inkey->algorithmParms.algorithmID;
outkey->encScheme = inkey->algorithmParms.encScheme;
outkey->sigScheme = inkey->algorithmParms.sigScheme;
// Create a PKCS #1 RSAPublicKey structure and convert it
// into a OpenSSL RSA key using d2i_RSAPublicKey().
pkcs1_length = 12 + inkey->pubKey.keyLength + rsa_parms.exponentSize;
if (sizeof(pkcs1_buffer) < (6 + pkcs1_length)) {
return TPM_BAD_PARAMETER;
}
cursor = pkcs1_buffer;
*cursor++ = 0x30;
*cursor++ = 0x84;
TtlMarshal_uint32_t(&cursor, &pkcs1_length);
*cursor++ = 0x02;
*cursor++ = 0x84;
TtlMarshal_uint32_t(&cursor, &inkey->pubKey.keyLength);
TtlMarshal_Blob(inkey->pubKey.key, &cursor, inkey->pubKey.keyLength);
*cursor++ = 0x02;
*cursor++ = 0x84;
TtlMarshal_uint32_t(&cursor, &rsa_parms.exponentSize);
TtlMarshal_Blob(rsa_parms.exponent, &cursor, rsa_parms.exponentSize);
cursor = pkcs1_buffer;
outkey->rsa = d2i_RSAPublicKey(NULL, (const uint8_t**)&cursor,
6 + pkcs1_length);
return (outkey->rsa != NULL) ? TPM_SUCCESS : TPM_BAD_PARAMETER;
}
uint32_t TtlReadPubek(TtlPubKey* pubek) {
TPM_ReadPubek_rqu request;
TPM_ReadPubek_rsp response;
uint8_t buffer[TPM_MAX_COMMAND_LENGTH];
uint8_t digest[TPM_MAX_COMMAND_LENGTH];
uint32_t result;
TTLDEBUG(("TPM: ReadPubek\n"));
GenerateNonce(&request.antiReplay);
result = Ttl_TPM_ReadPubek(&request, &response, buffer, sizeof(buffer));
if (result == TPM_SUCCESS) {
uint8_t* cursor = digest;
TtlMarshal_TPM_PUBKEY(&cursor, &response.pubEndorsementKey);
TtlMarshal_Blob(request.antiReplay.nonce, &cursor,
sizeof(request.antiReplay.nonce));
SHA1(digest, cursor - digest, digest);
if (memcmp(digest, response.checksum.digest, sizeof(TPM_DIGEST))) {
result = TPM_FAIL;
} else {
result = ConvertPubKey(&response.pubEndorsementKey, pubek);
}
}
return result;
}
uint32_t TtlTakeOwnership(uint8_t owner_auth[TPM_SHA1_160_HASH_LEN],
uint8_t srk_auth[TPM_SHA1_160_HASH_LEN]) {
TPM_TakeOwnership_rqu request;
TPM_TakeOwnership_rsp response;
uint8_t buffer[TPM_MAX_COMMAND_LENGTH];
uint32_t result;
APSession auth;
TtlPubKey pubek;
uint8_t padded[TPM_MAX_RSA_KEY_LENGTH / 8];
uint8_t enc_owner_auth[TPM_MAX_RSA_KEY_LENGTH / 8];
uint8_t enc_srk_auth[TPM_MAX_RSA_KEY_LENGTH / 8];
uint32_t rsa_enc_length;
TPM_RSA_KEY_PARMS rsa_parms;
uint8_t rsa_parms_buffer[12];
uint8_t *cursor;
TTLDEBUG(("TPM: TakeOwnership\n"));
// Read public part of TPM EK and use it to encrypt the owner and srk secret.
result = TtlReadPubek(&pubek);
if (result != TPM_SUCCESS) return result;
rsa_enc_length = RSA_size(pubek.rsa);
if (rsa_enc_length > (TPM_MAX_RSA_KEY_LENGTH / 8)) {
return TPM_ENCRYPT_ERROR;
}
memset(enc_owner_auth, 0, sizeof(enc_owner_auth));
if (RSA_padding_add_PKCS1_OAEP(padded, rsa_enc_length,
owner_auth, TPM_SHA1_160_HASH_LEN,
(uint8_t*)"TCPA", 4) != 1 ||
RSA_public_encrypt(rsa_enc_length, padded, enc_owner_auth,
pubek.rsa, RSA_NO_PADDING) < 0) {
RSA_free(pubek.rsa);
return TPM_ENCRYPT_ERROR;
}
memset(enc_srk_auth, 0, sizeof(enc_srk_auth));
if (RSA_padding_add_PKCS1_OAEP(padded, rsa_enc_length,
srk_auth, TPM_SHA1_160_HASH_LEN,
(uint8_t*)"TCPA", 4) != 1 ||
RSA_public_encrypt(rsa_enc_length, padded, enc_srk_auth,
pubek.rsa, RSA_NO_PADDING) < 0) {
RSA_free(pubek.rsa);
return TPM_ENCRYPT_ERROR;
}
RSA_free(pubek.rsa);
result = TtlOIAP(&auth, owner_auth);
if (result != TPM_SUCCESS) return result;
request.protocolID = TPM_PID_OWNER;
request.encOwnerAuthSize = rsa_enc_length;
request.encOwnerAuth = enc_owner_auth;
request.encSrkAuthSize = rsa_enc_length;
request.encSrkAuth = enc_srk_auth;
request.srkParams.tag = TPM_TAG_KEY12;
request.srkParams.fill = 0;
request.srkParams.keyUsage = TPM_KEY_STORAGE;
request.srkParams.keyFlags = 0;
request.srkParams.authDataUsage = TPM_AUTH_ALWAYS;
request.srkParams.algorithmParms.algorithmID = TPM_ALG_RSA;
request.srkParams.algorithmParms.encScheme = TPM_ES_RSAESOAEP_SHA1_MGF1;
request.srkParams.algorithmParms.sigScheme = TPM_SS_NONE;
request.srkParams.algorithmParms.parmSize = 12;
rsa_parms.keyLength = 2048;
rsa_parms.numPrimes = 2;
rsa_parms.exponentSize = 0;
cursor = rsa_parms_buffer;
TtlMarshal_TPM_RSA_KEY_PARMS(&cursor, &rsa_parms);
request.srkParams.algorithmParms.parms = rsa_parms_buffer;
request.srkParams.PCRInfoSize = 0;
request.srkParams.pubKey.keyLength = 0;
request.srkParams.encSize = 0;
result = Ttl_TPM_TakeOwnership(&request, &response,
buffer, sizeof(buffer), &auth);
TtlFlushSpecific(auth.handle, TPM_RT_AUTH);
return result;
}
uint32_t TtlOwnerClear(uint8_t owner_auth[TPM_SHA1_160_HASH_LEN]) {
uint32_t result;
APSession auth;
TTLDEBUG(("TPM: OwnerClear\n"));
result = TtlOIAP(&auth, owner_auth);
if (result != TPM_SUCCESS) return result;
result = Ttl_TPM_OwnerClear(&auth);
TtlFlushSpecific(auth.handle, TPM_RT_AUTH);
return result;
}
uint32_t TtlChangeAuthOwner(uint32_t entity_type,
uint8_t old_auth[TPM_SHA1_160_HASH_LEN],
uint8_t new_auth[TPM_SHA1_160_HASH_LEN]) {
TPM_ChangeAuthOwner_rqu request;
uint32_t result;
APSession auth;
int i;
TTLDEBUG(("TPM: ChangeAuthOwner\n"));
result = TtlOSAP(&auth, old_auth, entity_type, 0);
if (result != TPM_SUCCESS) return result;
// Encrypt new owner secret.
SHA_CTX sha_ctx;
SHA1_Init(&sha_ctx);
SHA1_Update(&sha_ctx, auth.secret, sizeof(auth.secret));
SHA1_Update(&sha_ctx, auth.nonceEven.nonce, sizeof(auth.nonceEven.nonce));
SHA1_Final(request.newAuth.authdata, &sha_ctx);
for (i = 0; i < sizeof(request.newAuth.authdata); ++i) {
request.newAuth.authdata[i] ^= new_auth[i];
}
request.protocolID = TPM_PID_ADCP;
request.entityType = entity_type;
result = Ttl_TPM_ChangeAuthOwner(&request, &auth);
TtlFlushSpecific(auth.handle, TPM_RT_AUTH);
return result;
}
uint32_t TtlGetTestResult(uint32_t *length, uint8_t *test_result)
{
TPM_GetTestResult_rsp response;
uint8_t buffer[TPM_MAX_COMMAND_LENGTH];
uint32_t result = Ttl_TPM_GetTestResult(&response, buffer, sizeof(buffer));
if (result != 0) {
return result;
}
*length = response.outDataSize;
memcpy(test_result, response.outData, response.outDataSize);
return result;
}