blob: ac71c9a12e6167f4e6965220595e5217d12d0a04 [file] [edit]
// Copyright 2021 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.
#include <string.h>
#include "hmac_authorization_delegate.h"
#include "pinweaver_eal.h"
#include "pinweaver_eal_tpm.h"
#define NONCE_MIN_SIZE 16
#define NONCE_MAX_SIZE 32
#define DECRYPT_SESSION (1 << 5)
#define ENCRYPT_SESSION (1 << 6)
#define LABEL_SIZE 4
#define AES_KEY_SIZE (128/8)
#define AES_IV_SIZE AES_KEY_SIZE
#define MAX_DECRYPT_SIZE 256
#define CONTINUE_SESSION 1
#define MAX_DIGEST_SIZE (sizeof(TPMU_HA))
#define SESSION_KEY_SIZE (256/8)
typedef struct HMAC_KEY {
size_t size;
uint8_t buffer[2 * MAX_DIGEST_SIZE];
} HMAC_KEY;
typedef struct HMAC_PROCESSOR {
pinweaver_eal_hmac_sha256_ctx_t hmac;
int res;
int init_res;
} HMAC_PROCESSOR;
static void HmacInit(
HMAC_PROCESSOR* p,
const HMAC_KEY* key
) {
p->init_res = p->res = pinweaver_eal_hmac_sha256_init(&p->hmac, key->buffer, key->size);
}
static void HmacUpdate(
HMAC_PROCESSOR* p,
const void* data,
size_t size
) {
if (!p->res) {
p->res = pinweaver_eal_hmac_sha256_update(&p->hmac, data, size);
}
}
static int HmacFinal(
HMAC_PROCESSOR* p,
void* result
) {
if (p->init_res) {
return p->init_res;
}
p->res |= pinweaver_eal_hmac_sha256_final(&p->hmac, result);
return p->res;
}
static void HmacKeyInit(HMAC_KEY* key) {
key->size = 0;
}
static int HmacKeyAppend(HMAC_KEY* key, const TPM2B_DIGEST* data) {
if (key->size + data->size > sizeof(key->buffer)) {
return -1;
}
if (pinweaver_eal_memcpy_s(key->buffer + key->size,
sizeof(key->buffer) - key->size,
data->buffer, data->size)) {
return -1;
}
key->size += data->size;
return 0;
}
// This method implements the key derivation function used in the TPM.
// NOTE: It only returns 32 byte keys.
static int CreateKey(
TSS_AUTHORIZATION_DELEGATE_HMAC* delegate,
const HMAC_KEY* hmac_key,
const TSS_SRC_DATA_BUF* label,
const TPM2B_NONCE* nonce_newer,
const TPM2B_NONCE* nonce_older,
uint8_t* key /* size=SHA256_DIGEST_SIZE */) {
uint32_t counter_buf;
TSS_DST_DATA_BUF counter = {sizeof(uint32_t), 0, (uint8_t *)&counter_buf};
uint32_t digest_size_bits_buf;
TSS_DST_DATA_BUF digest_size_bits = {sizeof(uint32_t), 0,
(uint8_t *)&digest_size_bits_buf};
const uint32_t kOne = 1;
const uint32_t kDigestBits = 256;
if (tss_Serialize_uint32_t(&kOne, &counter) != TPM_RC_SUCCESS ||
tss_Serialize_uint32_t(&kDigestBits, &digest_size_bits) != TPM_RC_SUCCESS) {
return -1;
}
HMAC_PROCESSOR hp;
HmacInit(&hp, hmac_key);
HmacUpdate(&hp, counter.buffer, counter.size);
HmacUpdate(&hp, label->buffer, label->size);
HmacUpdate(&hp, nonce_newer->buffer, nonce_newer->size);
HmacUpdate(&hp, nonce_older->buffer, nonce_older->size);
HmacUpdate(&hp, digest_size_bits.buffer, digest_size_bits.size);
return HmacFinal(&hp, key);
}
// This method performs an AES operation using a 128 bit key.
// |operation_type| can be either AES_ENCRYPT or AES_DECRYPT and it
// determines if the operation is an encryption or decryption.
static int AesOperation(
TSS_AUTHORIZATION_DELEGATE_HMAC* delegate,
TSS_SRC_DATA_BUF* parameter,
const TPM2B_NONCE* nonce_newer,
const TPM2B_NONCE* nonce_older,
int operation_type) {
if (parameter->size > MAX_DECRYPT_SIZE) {
return -1;
}
if (delegate->session_key_.size > sizeof(delegate->session_key_.buffer)) {
return -1;
}
if (delegate->entity_authorization_value_.size >
sizeof(delegate->entity_authorization_value_.buffer)) {
return -1;
}
const TSS_SRC_DATA_BUF kLabelCFB = {LABEL_SIZE, (uint8_t *)"CFB"};
HMAC_KEY hmac_key;
HmacKeyInit(&hmac_key);
if (HmacKeyAppend(&hmac_key, &delegate->session_key_)) {
return -1;
}
if (HmacKeyAppend(&hmac_key, &delegate->entity_authorization_value_)) {
return -1;
}
uint8_t compound_key[SESSION_KEY_SIZE];
if (CreateKey(delegate,
&hmac_key,
&kLabelCFB,
nonce_newer,
nonce_older,
compound_key)) {
return -1;
}
unsigned char aes_key[AES_KEY_SIZE];
unsigned char aes_iv[AES_IV_SIZE];
if (pinweaver_eal_memcpy_s(aes_key, AES_KEY_SIZE, &compound_key[0],
AES_KEY_SIZE)) {
return -1;
}
if (pinweaver_eal_memcpy_s(aes_iv, AES_IV_SIZE, &compound_key[AES_KEY_SIZE],
AES_IV_SIZE)) {
return -1;
}
unsigned char decrypted[MAX_DECRYPT_SIZE];
if (pinweaver_eal_aes128_cfb(aes_key, AES_KEY_SIZE, aes_iv,
parameter->buffer, parameter->size,
operation_type, decrypted) != 0) {
return -1;
}
if (pinweaver_eal_memcpy_s(parameter->buffer, parameter->size,
decrypted, parameter->size)) {
return -1;
}
return 0;
}
// This method regenerates the caller nonce. The new nonce is the same
// length as the previous nonce. The buffer is filled with random data.
// NOTE: This operation is DESTRUCTIVE, and rewrites the caller_nonce_ field.
static void RegenerateCallerNonce(TSS_AUTHORIZATION_DELEGATE_HMAC* delegate) {
// nonce_size is guaranteed to be less than 32 bytes and greater than 16.
pinweaver_eal_rand_bytes(delegate->caller_nonce_.buffer,
delegate->caller_nonce_.size);
}
int tss_GetCommandAuthorizationHmac(
TSS_AUTHORIZATION_DELEGATE_HMAC* delegate,
const TSS_SRC_DATA_BUF* command_hash,
bool is_command_parameter_encryption_possible,
bool is_response_parameter_encryption_possible,
TSS_DST_DATA_BUF* authorization) {
if (!delegate->session_handle_) {
authorization->size = 0;
return -1;
}
if (delegate->caller_nonce_.size > sizeof(delegate->caller_nonce_.buffer)) {
authorization->size = 0;
return -1;
}
TPMS_AUTH_COMMAND auth;
auth.session_handle = delegate->session_handle_;
if (!delegate->nonce_generated_) {
RegenerateCallerNonce(delegate);
}
auth.nonce = delegate->caller_nonce_;
auth.session_attributes = CONTINUE_SESSION;
if (delegate->is_parameter_encryption_enabled_) {
if (is_command_parameter_encryption_possible) {
auth.session_attributes |= DECRYPT_SESSION;
}
if (is_response_parameter_encryption_possible) {
auth.session_attributes |= ENCRYPT_SESSION;
}
}
// We reset the |nonce_generated| flag in preparation of the next command.
delegate->nonce_generated_ = false;
UINT8 attributes_bytes_buf;
TSS_DST_DATA_BUF attributes_bytes = {sizeof(attributes_bytes_buf), 0,
&attributes_bytes_buf};
if (tss_Serialize_TPMA_SESSION(&auth.session_attributes, &attributes_bytes) !=
TPM_RC_SUCCESS) {
return -1;
}
if (delegate->session_key_.size > sizeof(delegate->session_key_.buffer)) {
authorization->size = 0;
return -1;
}
if (delegate->entity_authorization_value_.size >
sizeof(delegate->entity_authorization_value_.buffer)) {
authorization->size = 0;
return -1;
}
HMAC_KEY hmac_key;
HmacKeyInit(&hmac_key);
if (HmacKeyAppend(&hmac_key, &delegate->session_key_)) {
return -1;
}
if (!delegate->use_entity_authorization_for_encryption_only_) {
if (HmacKeyAppend(&hmac_key, &delegate->entity_authorization_value_)) {
return -1;
}
}
if (delegate->tpm_nonce_.size > sizeof(delegate->tpm_nonce_.buffer)) {
authorization->size = 0;
return -1;
}
HMAC_PROCESSOR hp;
HmacInit(&hp, &hmac_key);
HmacUpdate(&hp, command_hash->buffer, command_hash->size);
HmacUpdate(&hp, delegate->caller_nonce_.buffer, delegate->caller_nonce_.size);
HmacUpdate(&hp, delegate->tpm_nonce_.buffer, delegate->tpm_nonce_.size);
HmacUpdate(&hp, attributes_bytes.buffer, attributes_bytes.size);
if (HmacFinal(&hp, auth.hmac.buffer))
return -1;
auth.hmac.size = SHA256_DIGEST_SIZE;
TPM_RC serialize_error = tss_Serialize_TPMS_AUTH_COMMAND(&auth, authorization);
if (serialize_error != TPM_RC_SUCCESS) {
return -1;
}
return 0;
}
int tss_CheckResponseAuthorizationHmac(
TSS_AUTHORIZATION_DELEGATE_HMAC* delegate,
const TSS_SRC_DATA_BUF* response_hash,
const TSS_SRC_DATA_BUF* authorization) {
if (!delegate->session_handle_) {
return -1;
}
TPMS_AUTH_RESPONSE auth_response;
TSS_SRC_DATA_BUF mutable_auth_string = {authorization->size,
authorization->buffer};
TPM_RC parse_error;
parse_error = tss_Parse_TPMS_AUTH_RESPONSE(&mutable_auth_string, &auth_response,
NULL);
if (parse_error != TPM_RC_SUCCESS) {
return -1;
}
if (mutable_auth_string.size != 0) {
return -1;
}
if (auth_response.hmac.size != SHA256_DIGEST_SIZE) {
return -1;
}
if (auth_response.nonce.size < NONCE_MIN_SIZE ||
auth_response.nonce.size > NONCE_MAX_SIZE) {
return -1;
}
delegate->tpm_nonce_ = auth_response.nonce;
UINT8 attributes_bytes_buf;
TSS_DST_DATA_BUF attributes_bytes = {sizeof(attributes_bytes_buf), 0,
&attributes_bytes_buf};
if (tss_Serialize_TPMA_SESSION(&auth_response.session_attributes,
&attributes_bytes) != TPM_RC_SUCCESS) {
return -1;
}
if (delegate->session_key_.size > sizeof(delegate->session_key_.buffer)) {
return -1;
}
HMAC_KEY hmac_key;
HmacKeyInit(&hmac_key);
if (HmacKeyAppend(&hmac_key, &delegate->session_key_)) {
return -1;
}
if (!delegate->use_entity_authorization_for_encryption_only_) {
// In a special case with TPM2_HierarchyChangeAuth, we need to use the
// auth_value that was set.
if (delegate->future_authorization_value_set_) {
if (HmacKeyAppend(&hmac_key, &delegate->future_authorization_value_)) {
return -1;
}
delegate->future_authorization_value_set_ = false;
} else {
if (HmacKeyAppend(&hmac_key, &delegate->entity_authorization_value_)) {
return -1;
}
}
}
unsigned char digest[SHA256_DIGEST_SIZE];
HMAC_PROCESSOR hp;
HmacInit(&hp, &hmac_key);
HmacUpdate(&hp, response_hash->buffer, response_hash->size);
HmacUpdate(&hp, delegate->tpm_nonce_.buffer, delegate->tpm_nonce_.size);
HmacUpdate(&hp, delegate->caller_nonce_.buffer, delegate->caller_nonce_.size);
HmacUpdate(&hp, attributes_bytes.buffer, attributes_bytes.size);
if (HmacFinal(&hp, digest))
return -1;
if (pinweaver_eal_safe_memcmp(digest, auth_response.hmac.buffer,
SHA256_DIGEST_SIZE)) {
return -1;
}
return 0;
}
int tss_EncryptCommandParameterHmac(
TSS_AUTHORIZATION_DELEGATE_HMAC* delegate,
TSS_SRC_DATA_BUF* parameter) {
if (!delegate->session_handle_) {
return -1;
}
if (!delegate->is_parameter_encryption_enabled_) {
// No parameter encryption enabled.
return 0;
}
if (delegate->caller_nonce_.size > sizeof(delegate->caller_nonce_.buffer)) {
return -1;
}
if (delegate->tpm_nonce_.size > sizeof(delegate->tpm_nonce_.buffer)) {
return -1;
}
RegenerateCallerNonce(delegate);
delegate->nonce_generated_ = true;
return AesOperation(delegate, parameter,
&delegate->caller_nonce_, &delegate->tpm_nonce_,
PINWEAVER_EAL_ENCRYPT);
}
int tss_DecryptResponseParameterHmac(
TSS_AUTHORIZATION_DELEGATE_HMAC* delegate,
TSS_SRC_DATA_BUF* parameter) {
if (!delegate->session_handle_) {
return -1;
}
if (!delegate->is_parameter_encryption_enabled_) {
// No parameter decryption enabled.
return 0;
}
return AesOperation(delegate, parameter,
&delegate->tpm_nonce_, &delegate->caller_nonce_,
PINWEAVER_EAL_DECRYPT);
}
int tss_GetTpmNonceHmac(
TSS_AUTHORIZATION_DELEGATE_HMAC* delegate,
TSS_DST_DATA_BUF* nonce) {
if (!delegate->tpm_nonce_.size)
return -1;
if (nonce->max_size < delegate->tpm_nonce_.size)
return -1;
if (pinweaver_eal_memcpy_s(nonce->buffer, nonce->max_size,
&delegate->tpm_nonce_.buffer, delegate->tpm_nonce_.size)) {
return -1;
}
nonce->size = delegate->tpm_nonce_.size;
return 0;
}
int tss_InitSessionHmac(
TSS_AUTHORIZATION_DELEGATE_HMAC* delegate,
TPM_HANDLE session_handle,
const TPM2B_NONCE* tpm_nonce,
const TPM2B_NONCE* caller_nonce,
const TPM2B_DIGEST* salt,
const TPM2B_DIGEST* bind_auth_value,
bool enable_parameter_encryption) {
memset((void *)delegate, 0, sizeof(TSS_AUTHORIZATION_DELEGATE_HMAC));
delegate->session_handle_ = session_handle;
if (caller_nonce->size < NONCE_MIN_SIZE
|| caller_nonce->size > NONCE_MAX_SIZE
|| tpm_nonce->size < NONCE_MIN_SIZE
|| tpm_nonce->size > NONCE_MAX_SIZE) {
return -1;
}
if (pinweaver_eal_memcpy_s(&delegate->tpm_nonce_, sizeof(TPM2B_NONCE),
tpm_nonce, sizeof(TPM2B_NONCE))) {
return -1;
}
if (pinweaver_eal_memcpy_s(&delegate->caller_nonce_, sizeof(TPM2B_NONCE),
caller_nonce, sizeof(TPM2B_NONCE))) {
return -1;
}
delegate->is_parameter_encryption_enabled_ = enable_parameter_encryption;
if (salt->size == 0 && (!bind_auth_value || bind_auth_value->size == 0)) {
// SessionKey is set to the empty string for unsalted and
// unbound sessions.
delegate->session_key_.size = 0;
} else {
TSS_SRC_DATA_BUF kLabelATH = {LABEL_SIZE, (uint8_t *)"ATH"};
HMAC_KEY hmac_key;
HmacKeyInit(&hmac_key);
if (bind_auth_value) {
if (HmacKeyAppend(&hmac_key, bind_auth_value)) {
return -1;
}
}
if (HmacKeyAppend(&hmac_key, salt)) {
return -1;
}
if (CreateKey(delegate, &hmac_key, &kLabelATH,
&delegate->tpm_nonce_, &delegate->caller_nonce_,
delegate->session_key_.buffer)) {
return -1;
}
delegate->session_key_.size = SESSION_KEY_SIZE;
}
return 0;
}