summaryrefslogtreecommitdiff
path: root/firmware/lib/tpm_lite/tlcl.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/lib/tpm_lite/tlcl.c')
-rw-r--r--firmware/lib/tpm_lite/tlcl.c224
1 files changed, 224 insertions, 0 deletions
diff --git a/firmware/lib/tpm_lite/tlcl.c b/firmware/lib/tpm_lite/tlcl.c
index 493bf84e..8f17380f 100644
--- a/firmware/lib/tpm_lite/tlcl.c
+++ b/firmware/lib/tpm_lite/tlcl.c
@@ -16,6 +16,7 @@
#include "2sysincludes.h"
#include "2common.h"
+#include "2hmac.h"
#include "2sha.h"
#include "sysincludes.h"
@@ -149,6 +150,187 @@ static uint32_t Send(const uint8_t* command)
return TlclSendReceive(command, response, sizeof(response));
}
+#ifdef CHROMEOS_ENVIRONMENT
+
+struct auth_session
+{
+ uint32_t handle;
+ TPM_NONCE nonce_even;
+ TPM_NONCE nonce_odd;
+ uint8_t shared_secret[TPM_AUTH_DATA_LEN];
+ uint8_t valid;
+};
+
+static uint32_t StartOIAPSession(struct auth_session* session,
+ const uint8_t secret[TPM_AUTH_DATA_LEN])
+{
+ session->valid = 0;
+
+ uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
+ uint32_t result = TlclSendReceive(tpm_oiap_cmd.buffer, response,
+ sizeof(response));
+ if (result != TPM_SUCCESS) {
+ return result;
+ }
+
+ uint32_t size;
+ FromTpmUint32(response + sizeof(uint16_t), &size);
+ if (size < kTpmResponseHeaderLength + sizeof(uint32_t)
+ + sizeof(TPM_NONCE)) {
+ return TPM_E_INVALID_RESPONSE;
+ }
+
+ const uint8_t* cursor = response + kTpmResponseHeaderLength;
+ session->handle = ReadTpmUint32(&cursor);
+ memcpy(session->nonce_even.nonce, cursor, sizeof(TPM_NONCE));
+ cursor += sizeof(TPM_NONCE);
+ VbAssert(cursor - response <= TPM_LARGE_ENOUGH_COMMAND_SIZE);
+
+ memcpy(session->shared_secret, secret, TPM_AUTH_DATA_LEN);
+ session->valid = 1;
+
+ return result;
+}
+
+/* Fills in the authentication block at the end of the command. The command body
+ * should already be initialized in |command_buffer|, and the included command
+ * size should account for the auth block that gets filled in. */
+uint32_t AddRequestAuthBlock(struct auth_session* auth_session,
+ uint8_t* command_buffer,
+ uint32_t command_buffer_size,
+ uint8_t continue_auth_session)
+{
+ if (!auth_session->valid) {
+ return TPM_E_AUTHFAIL;
+ }
+
+ /* Sanity check to make sure the command buffer has sufficient space to
+ * add the auth block at the end of the command. */
+ if (command_buffer_size < kTpmRequestHeaderLength) {
+ return TPM_E_BUFFER_SIZE;
+ }
+ const uint32_t size = TpmCommandSize(command_buffer);
+ if (size < kTpmResponseHeaderLength + kTpmRequestAuthBlockLength ||
+ size > command_buffer_size) {
+ return TPM_E_INTERNAL_ERROR;
+ }
+ const uint32_t auth_offset = size - kTpmRequestAuthBlockLength;
+
+ /*
+ * The digest of the command is computed over the command buffer, but
+ * excluding the leading tag and paramSize fields.
+ */
+ struct vb2_sha1_context sha1_ctx;
+ vb2_sha1_init(&sha1_ctx);
+ vb2_sha1_update(&sha1_ctx,
+ command_buffer + sizeof(uint16_t) + sizeof(uint32_t),
+ auth_offset - sizeof(uint16_t) - sizeof(uint32_t));
+ uint8_t buf[TPM_SHA1_160_HASH_LEN + 2 * sizeof(TPM_NONCE) + 1];
+ vb2_sha1_finalize(&sha1_ctx, buf);
+
+ /* Generate a fresh nonce. */
+ if (VbExTpmGetRandom(auth_session->nonce_odd.nonce,
+ sizeof(TPM_NONCE)) != VB2_SUCCESS) {
+ return TPM_E_INTERNAL_ERROR;
+ }
+
+ /* Append the authentication block to the command buffer. */
+ uint8_t* cursor = command_buffer + auth_offset;
+ ToTpmUint32(cursor, auth_session->handle);
+ cursor += sizeof(uint32_t);
+ memcpy(cursor, auth_session->nonce_odd.nonce, sizeof(TPM_NONCE));
+ cursor += sizeof(TPM_NONCE);
+ *cursor++ = continue_auth_session;
+
+ /* Compute and append the MAC. */
+ memcpy(buf + TPM_SHA1_160_HASH_LEN, auth_session->nonce_even.nonce,
+ sizeof(TPM_NONCE));
+ memcpy(buf + TPM_SHA1_160_HASH_LEN + sizeof(TPM_NONCE),
+ auth_session->nonce_odd.nonce, sizeof(TPM_NONCE));
+ buf[TPM_SHA1_160_HASH_LEN + 2 * sizeof(TPM_NONCE)] =
+ continue_auth_session;
+ if (hmac(VB2_HASH_SHA1, auth_session->shared_secret,
+ sizeof(auth_session->shared_secret), buf, sizeof(buf), cursor,
+ TPM_SHA1_160_HASH_LEN)) {
+ return TPM_E_AUTHFAIL;
+ }
+ cursor += TPM_SHA1_160_HASH_LEN;
+
+ return TPM_SUCCESS;
+}
+
+uint32_t CheckResponseAuthBlock(struct auth_session* auth_session,
+ TPM_COMMAND_CODE ordinal,
+ uint8_t* response_buffer,
+ uint32_t response_buffer_size)
+{
+ if (!auth_session->valid) {
+ return TPM_E_AUTHFAIL;
+ }
+
+ if (response_buffer_size < kTpmResponseHeaderLength) {
+ return TPM_E_INVALID_RESPONSE;
+ }
+
+ /* Parse and validate the actual response size from the response. */
+ uint32_t size;
+ FromTpmUint32(response_buffer + sizeof(uint16_t), &size);
+ if (size >= response_buffer_size ||
+ size < kTpmResponseHeaderLength + kTpmResponseAuthBlockLength) {
+ return TPM_E_INVALID_RESPONSE;
+ }
+ uint32_t auth_offset = size - kTpmResponseAuthBlockLength;
+
+ /*
+ * The digest of the response is computed over the return code, ordinal,
+ * response payload.
+ */
+ struct vb2_sha1_context sha1_ctx;
+ vb2_sha1_init(&sha1_ctx);
+ vb2_sha1_update(&sha1_ctx,
+ response_buffer + sizeof(uint16_t) + sizeof(uint32_t),
+ sizeof(uint32_t));
+ uint8_t ordinal_buf[sizeof(ordinal)];
+ ToTpmUint32(ordinal_buf, ordinal);
+ vb2_sha1_update(&sha1_ctx, ordinal_buf, sizeof(ordinal_buf));
+ vb2_sha1_update(&sha1_ctx,
+ response_buffer + kTpmResponseHeaderLength,
+ auth_offset - kTpmResponseHeaderLength);
+ uint8_t hmac_input[TPM_SHA1_160_HASH_LEN + 2 * sizeof(TPM_NONCE) + 1];
+ vb2_sha1_finalize(&sha1_ctx, hmac_input);
+
+ /* Compute the MAC. */
+ uint8_t* cursor = response_buffer + auth_offset;
+ memcpy(hmac_input + TPM_SHA1_160_HASH_LEN, cursor, sizeof(TPM_NONCE));
+ cursor += sizeof(TPM_NONCE);
+ memcpy(hmac_input + TPM_SHA1_160_HASH_LEN + sizeof(TPM_NONCE),
+ auth_session->nonce_odd.nonce, sizeof(TPM_NONCE));
+ auth_session->valid = *cursor++;
+ hmac_input[TPM_SHA1_160_HASH_LEN + 2 * sizeof(TPM_NONCE)] =
+ auth_session->valid;
+ uint8_t mac[TPM_SHA1_160_HASH_LEN];
+ if (hmac(VB2_HASH_SHA1, auth_session->shared_secret,
+ sizeof(auth_session->shared_secret), hmac_input,
+ sizeof(hmac_input), mac, sizeof(mac))) {
+ auth_session->valid = 0;
+ return TPM_E_AUTHFAIL;
+ }
+
+ /* Check the MAC. */
+ if (vb2_safe_memcmp(mac, cursor, sizeof(mac))) {
+ auth_session->valid = 0;
+ return TPM_E_AUTHFAIL;
+ }
+
+ /* Success, save the even nonce. */
+ memcpy(auth_session->nonce_even.nonce, response_buffer + auth_offset,
+ sizeof(TPM_NONCE));
+
+ return TPM_SUCCESS;
+}
+
+#endif /* CHROMEOS_ENVIRONMENT */
+
/* Exported functions. */
uint32_t TlclLibInit(void)
@@ -781,4 +963,46 @@ uint32_t TlclReadPubek(uint32_t* public_exponent,
return result;
}
+uint32_t TlclTakeOwnership(uint8_t enc_owner_auth[TPM_RSA_2048_LEN],
+ uint8_t enc_srk_auth[TPM_RSA_2048_LEN],
+ uint8_t owner_auth[TPM_AUTH_DATA_LEN])
+{
+ /* Start an OAIP session. */
+ struct auth_session auth_session;
+ uint32_t result = StartOIAPSession(&auth_session, owner_auth);
+ if (result != TPM_SUCCESS) {
+ return result;
+ }
+
+ /* Build the TakeOwnership command. */
+ struct s_tpm_takeownership_cmd cmd;
+ memcpy(&cmd, &tpm_takeownership_cmd, sizeof(cmd));
+ memcpy(cmd.buffer + tpm_takeownership_cmd.encOwnerAuth, enc_owner_auth,
+ TPM_RSA_2048_LEN);
+ memcpy(cmd.buffer + tpm_takeownership_cmd.encSrkAuth, enc_srk_auth,
+ TPM_RSA_2048_LEN);
+ result = AddRequestAuthBlock(&auth_session, cmd.buffer,
+ sizeof(cmd.buffer), 0);
+ if (result != TPM_SUCCESS) {
+ return result;
+ }
+
+ /* The response buffer needs to be large to hold the public half of the
+ * generated SRK. */
+ uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE + TPM_RSA_2048_LEN];
+ result = TlclSendReceive(cmd.buffer, response, sizeof(response));
+ if (result != TPM_SUCCESS) {
+ return result;
+ }
+
+ /* Check the auth tag on the response. */
+ result = CheckResponseAuthBlock(&auth_session, TPM_ORD_TakeOwnership,
+ response, sizeof(response));
+ if (result != TPM_SUCCESS) {
+ return result;
+ }
+
+ return TPM_SUCCESS;
+}
+
#endif /* CHROMEOS_ENVIRONMENT */