blob: 010addf948a5994c01cf52394ff9862ae2e00e02 [file] [log] [blame]
/* Copyright (c) 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.
*
* Byte code interpreter for marshalling/unmarshalling.
*/
#include "trunks_internal.h"
/*
* Bytecodes
* ---------
*
* Type descriptor:
* - type kind (byte)
* - (possibly) type-dependent data
*
* Type-dependent data for BC_TYPE:
* - byte index of type descriptor bytecode (2 bytes)
*
* Type-dependent data for BC_ARRAY:
* - array size (2 bytes)
*
* Type-dependent data for BC_RECORD:
* - number of fields (1 byte)
* - field bytecodes for field 0
* - field bytecodes for field 1
* ...
*
* Field bytecodes:
* - field kind (byte)
* - field offset in record (2 bytes)
* - (for variable-length fields:) field number for length field (1 byte)
* - type descriptor for field (usually leaf type or BC_TYPE)
*
*/
/* Local declarations for recursion. */
/* Functions that parse bytecodes take a bytecode pointer as a (reference)
* argument and advance it as a side effect. If you need to parse it more than
* once (why?), save the pointer first.
*
* The Ttl(Un)MarshalXXX functions also advance their *cursor args as they copy
* data into the command buffer.
*/
/* Takes a two-byte bytecode and returns the integer it represents. */
static inline int BC2ByteToInt(uint8_t** codep) {
int n = (*codep)[0] * 256 + (*codep)[1];
*codep += 2;
return n;
}
static inline uint8_t* ByteCodeOffsetToPointer(uint8_t** codep) {
int offset = BC2ByteToInt(codep);
return ByteCodeIndexToAddress(offset);
}
static int TtlSizeCalcVarfield(void* data, uint8_t* length_field_bc) {
uint8_t length_field_kind = *length_field_bc++;
int length_field_offset = BC2ByteToInt(&length_field_bc);
int length_field_type = *length_field_bc++;
int length;
assert(length_field_kind == BC_FIELD_KIND_NORMAL);
switch (length_field_type) {
/* A length field may only be an integer. */
case BC_INT32:
length = *(uint32_t *) (data + length_field_offset);
break;
case BC_INT16:
length = *(uint16_t *) (data + length_field_offset);
break;
default:
TtlError("invalid type %d of length field", *length_field_bc);
break;
}
return length;
}
static int TtlSizeCalcField(uint8_t** codep, void* data, uint8_t** field_bcs) {
uint8_t *cp = *codep;
int field_kind = *cp++;
int field_offset = BC2ByteToInt(&cp);
int size;
switch (field_kind) {
case BC_FIELD_KIND_NORMAL:
size = TtlSizeCalc(&cp, data + field_offset);
break;
case BC_FIELD_KIND_VARIABLE: {
int length_field_index = *cp++;
uint8_t* length_field_bc = field_bcs[length_field_index];
size = TtlSizeCalcVarfield(data, length_field_bc);
break;
}
default:
TtlError("invalid field kind %d", field_kind);
break;
}
*codep = cp;
return size;
}
static int TtlSizeCalcRecord(uint8_t** codep, void* data) {
uint8_t* field_bcs[TTL_RECORD_FIELDS_MAX];
uint8_t *cp = *codep;
int size = 0;
int nfields = *cp++;
int i;
for (i = 0; i < nfields; i++) {
field_bcs[i] = cp;
size += TtlSizeCalcField(&cp, data, field_bcs);
}
*codep = cp;
return size;
}
static int TtlSizeCalcArray(uint8_t** codep) {
uint8_t *cp = *codep;
int size;
uint8_t* bogus = 0;
assert(*cp == BC_INT8 ||
*cp == BC_INT16 ||
*cp == BC_INT32 ||
*cp == BC_INT64);
size = TtlSizeCalc(&cp, &bogus);
*codep = cp;
return size;
}
int TtlSizeCalc(uint8_t** codep, void* data) {
uint8_t *cp = *codep;
int size;
switch (*cp++) {
case BC_INT64:
size = 8;
break;
case BC_INT32:
size = 4;
break;
case BC_INT16:
size = 2;
break;
case BC_INT8:
size = 1;
break;
case BC_REF: {
uint8_t* typecode;
typecode = ByteCodeOffsetToPointer(&cp);
size = TtlSizeCalc(&typecode, data);
break;
}
case BC_ARRAY:
size = TtlSizeCalcArray(&cp);
break;
case BC_RECORD:
size = TtlSizeCalcRecord(&cp, data);
break;
default:
TtlError("unknown bytecode %d", *cp);
}
*codep = cp;
return size;
}
static void TtlMarshalField(uint8_t** codep, uint8_t** cursor, void* from,
uint8_t** field_bcs) {
uint8_t *to = *cursor;
uint8_t *cp = *codep;
int field_kind = *cp++;
int field_offset = BC2ByteToInt(&cp);
switch (field_kind) {
case BC_FIELD_KIND_NORMAL:
TtlMarshal(&cp, &to, from + field_offset);
break;
case BC_FIELD_KIND_VARIABLE: {
int length_field_index = *cp++;
uint8_t* length_field_bc = field_bcs[length_field_index];
int length = TtlSizeCalcVarfield(from, length_field_bc);
TtlMarshal_Blob(*(uint8_t**)(((uint8_t*) from) + field_offset),
&to, length);
break;
}
default:
TtlError("invalid field kind %d", field_kind);
break;
}
*cursor = to;
*codep = cp;
}
static void TtlUnmarshalField(uint8_t** codep, uint8_t** cursor, void* to,
uint8_t** field_bcs) {
uint8_t *from = *cursor;
uint8_t *cp = *codep;
int field_kind = *cp++;
int field_offset = BC2ByteToInt(&cp);
switch (field_kind) {
case BC_FIELD_KIND_NORMAL:
TtlUnmarshal(&cp, &from, to + field_offset);
break;
case BC_FIELD_KIND_VARIABLE: {
int length_field_index = *cp++;
uint8_t* length_field_bc = field_bcs[length_field_index];
int length = TtlSizeCalcVarfield(to, length_field_bc);
TtlUnmarshal_Blob((uint8_t**) (((uint8_t*) to) + field_offset),
&from, length);
break;
}
default:
TtlError("invalid field kind %d", field_kind);
break;
}
*cursor = from;
*codep = cp;
}
static void TtlMarshalRecord(uint8_t** codep, uint8_t** cursor, void* from) {
int i;
uint8_t* to = *cursor;
uint8_t* cp = *codep;
uint8_t* field_bcs[TTL_RECORD_FIELDS_MAX];
int nfields = *cp++;
for (i = 0; i < nfields; i++) {
field_bcs[i] = cp;
TtlMarshalField(&cp, &to, from, field_bcs);
}
*cursor = to;
*codep = cp;
}
static void TtlUnmarshalRecord(uint8_t** codep, uint8_t** cursor, void* to) {
int i;
uint8_t* from = *cursor;
uint8_t* cp = *codep;
uint8_t* field_bcs[TTL_RECORD_FIELDS_MAX];
int nfields = *cp++;
for (i = 0; i < nfields; i++) {
field_bcs[i] = cp;
TtlUnmarshalField(&cp, &from, to, field_bcs);
}
*cursor = from;
*codep = cp;
}
static void TtlMarshalArray(uint8_t** codep, uint8_t** cursor, void* from) {
uint8_t* to = *cursor;
uint8_t* cp = *codep;
int size = BC2ByteToInt(&cp);
/* A fixed-size arrays is allocated inside the structure. */
memcpy(to, (uint8_t*) from, size);
to += size;
*cursor = to;
*codep = cp;
}
static void TtlUnmarshalArray(uint8_t** codep, uint8_t** cursor, void* to) {
uint8_t* from = *cursor;
uint8_t* cp = *codep;
int size = BC2ByteToInt(&cp);
/* A fixed-size arrays is allocated inside the structure. */
memcpy((uint8_t*) to, from, size);
from += size;
*cursor = from;
*codep = cp;
}
void TtlMarshal(uint8_t** codep, uint8_t** cursor, void* from) {
uint8_t *to = *cursor;
uint8_t *cp = *codep;
switch (*cp++) {
case BC_INT32:
TtlMarshal_uint32_t(&to, (uint32_t*) from);
break;
case BC_INT16:
TtlMarshal_uint16_t(&to, (uint16_t*) from);
break;
case BC_INT8:
TtlMarshal_uint8_t(&to, (uint8_t*) from);
break;
case BC_REF: {
uint8_t* typecode = ByteCodeOffsetToPointer(&cp);
TtlMarshal(&typecode, &to, from);
break;
}
case BC_ARRAY:
TtlMarshalArray(&cp, &to, from);
break;
case BC_RECORD:
TtlMarshalRecord(&cp, &to, from);
break;
default:
TtlError("unknown bytecode %d", *cp);
}
*cursor = to;
*codep = cp;
}
void TtlUnmarshal(uint8_t** codep, uint8_t** cursor, void* to) {
uint8_t *from = *cursor;
uint8_t *cp = *codep;
switch (*cp++) {
case BC_INT32:
TtlUnmarshal_uint32_t(&from, (uint32_t*) to);
break;
case BC_INT16:
TtlUnmarshal_uint16_t(&from, (uint16_t*) to);
break;
case BC_INT8:
TtlUnmarshal_uint8_t(&from, (uint8_t*) to);
break;
case BC_REF: {
uint8_t* typecode = ByteCodeOffsetToPointer(&cp);
TtlUnmarshal(&typecode, &from, to);
break;
}
case BC_ARRAY:
TtlUnmarshalArray(&cp, &from, to);
break;
case BC_RECORD:
TtlUnmarshalRecord(&cp, &from, to);
break;
default:
TtlError("unknown bytecode %d", *cp);
}
*cursor = from;
*codep = cp;
}
uint32_t TtlRunCommand(uint32_t command_ordinal,
int rqu_index, int rsp_index,
void* rqu_struct, void* rsp_struct,
void* auth1, void* auth2,
uint8_t* buffer, const uint32_t buffer_size) {
uint8_t* cursor = buffer;
TPM_TAG tag = auth1 == NULL ? TPM_TAG_RQU_COMMAND :
(auth2 == NULL ? TPM_TAG_RQU_AUTH1_COMMAND : TPM_TAG_RQU_AUTH2_COMMAND);
uint32_t message_size = TPM_RQU_HEADER_SIZE;
int auth_size = auth1 == NULL ? 0:
(auth2 == NULL ? TPM_RQU_AUTH_SIZE: 2 * TPM_RQU_AUTH_SIZE);
uint32_t read_bytes;
uint32_t return_code;
int expected_size;
/* First compute message size to ensure buffer is large enough. */
if (rqu_index >= 0) {
uint8_t* bytecode = ByteCodeIndexToAddress(rqu_index);
message_size += TtlSizeCalc(&bytecode, rqu_struct);
}
message_size += auth_size;
if (message_size > buffer_size) {
return TPM_BAD_PARAM_SIZE;
}
/* Then marshal the various parts of the message. First the header. */
TtlMarshal_uint16_t(&cursor, &tag);
TtlMarshal_uint32_t(&cursor, &message_size);
TtlMarshal_uint32_t(&cursor, &command_ordinal);
/* Then the additional command parameters, if any. */
if (rqu_index >= 0) {
uint8_t* bytecode = ByteCodeIndexToAddress(rqu_index);
TtlMarshal(&bytecode, &cursor, rqu_struct);
}
/* Then the auths (if any) */
if (auth1 != NULL) {
TtlComputeAndMarshal_TPM_AUTH(auth1, buffer, message_size - auth_size,
command_ordinal, &cursor);
if (auth2 != NULL) {
TtlComputeAndMarshal_TPM_AUTH(auth2, buffer, message_size - auth_size,
command_ordinal, &cursor);
}
}
/* Check that the computed message size matches the actual size. This is an
* important consistency check, since we don't trust the bytecodes. */
if (cursor - buffer != message_size) {
return TPM_E_INTERNAL_ERROR;
}
/* Send the command to the TPM and receive a response. */
RETURN_ON_FAILURE(TtlExecute(buffer, message_size, buffer_size, &read_bytes));
cursor = buffer;
/* Extract the response header. */
TtlUnmarshal_uint16_t(&cursor, &tag);
TtlUnmarshal_uint32_t(&cursor, &message_size);
TtlUnmarshal_uint32_t(&cursor, &return_code);
if (return_code != TPM_SUCCESS) {
return return_code;
}
if (message_size > buffer_size) {
/* Is message_size doesn't lie, we have a buffer overflow. Anything could
* happen. TODO(semenzato): the error code could be more specific. */
return TPM_BAD_PARAM_SIZE;
}
if (message_size != read_bytes) {
return TPM_E_COMMUNICATION_ERROR;
}
/* Extract the response parameters, if any */
if (rsp_index >= 0) {
uint8_t* bytecode = ByteCodeIndexToAddress(rsp_index);
TtlUnmarshal(&bytecode, &cursor, rsp_struct);
}
/* Compute the expected size of the command response, for a consistency
* check. */
expected_size = TPM_RSP_HEADER_SIZE;
if (rsp_index >= 0) {
uint8_t* bytecode = ByteCodeIndexToAddress(rsp_index);
expected_size += TtlSizeCalc(&bytecode, rsp_struct);
}
if (auth1 != NULL) {
expected_size += TPM_RSP_AUTH_SIZE;
}
if (message_size != expected_size) {
/* This isn't necessarily our fault (if message size lies, for instance,
* then it may be a communication error) so we pick the most likely
* cause. */
return TPM_E_INTERNAL_ERROR;
}
/* Check the response auth. Do this last so that earlier unmarshalling or
* communication errors are reported more precisely. */
if (auth1 != NULL) {
int auth_size = TPM_RSP_AUTH_SIZE;
RETURN_ON_FAILURE(TtlUnmarshalAndVerify_TPM_AUTH(auth1, buffer,
message_size - auth_size,
command_ordinal, &cursor));
}
return TPM_SUCCESS;
}