// // Copyright (C) 2014 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "trunks/hmac_authorization_delegate.h" #include <base/logging.h> #include <base/stl_util.h> #include <crypto/secure_util.h> #include <openssl/aes.h> #include <openssl/hmac.h> #include <openssl/rand.h> namespace trunks { namespace { const uint32_t kDigestBits = 256; const uint16_t kNonceMinSize = 16; const uint16_t kNonceMaxSize = 32; const uint8_t kDecryptSession = 1 << 5; const uint8_t kEncryptSession = 1 << 6; const uint8_t kLabelSize = 4; const size_t kAesIVSize = 16; const uint32_t kTpmBufferSize = 4096; } // namespace HmacAuthorizationDelegate::HmacAuthorizationDelegate() : session_handle_(0), is_parameter_encryption_enabled_(false), nonce_generated_(false), future_authorization_value_set_(false), use_entity_authorization_for_encryption_only_(false) { tpm_nonce_.size = 0; caller_nonce_.size = 0; } HmacAuthorizationDelegate::~HmacAuthorizationDelegate() {} bool HmacAuthorizationDelegate::GetCommandAuthorization( const std::string& command_hash, bool is_command_parameter_encryption_possible, bool is_response_parameter_encryption_possible, std::string* authorization) { if (!session_handle_) { authorization->clear(); LOG(ERROR) << "Delegate being used before Initialization,"; return false; } TPMS_AUTH_COMMAND auth; auth.session_handle = session_handle_; if (!nonce_generated_) { RegenerateCallerNonce(); } auth.nonce = caller_nonce_; auth.session_attributes = kContinueSession; if (is_parameter_encryption_enabled_) { if (is_command_parameter_encryption_possible) { auth.session_attributes |= kDecryptSession; } if (is_response_parameter_encryption_possible) { auth.session_attributes |= kEncryptSession; } } // We reset the |nonce_generated| flag in preperation of the next command. nonce_generated_ = false; std::string attributes_bytes; CHECK_EQ(Serialize_TPMA_SESSION(auth.session_attributes, &attributes_bytes), TPM_RC_SUCCESS) << "Error serializing session attributes."; std::string hmac_data; std::string hmac_key; if (!use_entity_authorization_for_encryption_only_) { hmac_key = session_key_ + entity_authorization_value_; } else { hmac_key = session_key_; } hmac_data.append(command_hash); hmac_data.append(reinterpret_cast<const char*>(caller_nonce_.buffer), caller_nonce_.size); hmac_data.append(reinterpret_cast<const char*>(tpm_nonce_.buffer), tpm_nonce_.size); hmac_data.append(attributes_bytes); std::string digest = HmacSha256(hmac_key, hmac_data); auth.hmac = Make_TPM2B_DIGEST(digest); TPM_RC serialize_error = Serialize_TPMS_AUTH_COMMAND(auth, authorization); if (serialize_error != TPM_RC_SUCCESS) { LOG(ERROR) << "Could not serialize command auth."; return false; } return true; } bool HmacAuthorizationDelegate::CheckResponseAuthorization( const std::string& response_hash, const std::string& authorization) { if (!session_handle_) { return false; } TPMS_AUTH_RESPONSE auth_response; std::string mutable_auth_string(authorization); TPM_RC parse_error; parse_error = Parse_TPMS_AUTH_RESPONSE(&mutable_auth_string, &auth_response, nullptr); if (parse_error != TPM_RC_SUCCESS) { LOG(ERROR) << "Could not parse authorization response."; return false; } if (auth_response.hmac.size != kHashDigestSize) { LOG(ERROR) << "TPM auth hmac was incorrect size."; return false; } if (auth_response.nonce.size < kNonceMinSize || auth_response.nonce.size > kNonceMaxSize) { LOG(ERROR) << "TPM_nonce is not the correct length."; return false; } tpm_nonce_ = auth_response.nonce; std::string attributes_bytes; CHECK_EQ(Serialize_TPMA_SESSION(auth_response.session_attributes, &attributes_bytes), TPM_RC_SUCCESS) << "Error serializing session attributes."; std::string hmac_data; std::string hmac_key; if (!use_entity_authorization_for_encryption_only_) { // In a special case with TPM2_HierarchyChangeAuth, we need to use the // auth_value that was set. if (future_authorization_value_set_) { hmac_key = session_key_ + future_authorization_value_; future_authorization_value_set_ = false; } else { hmac_key = session_key_ + entity_authorization_value_; } } else { hmac_key = session_key_; } hmac_data.append(response_hash); hmac_data.append(reinterpret_cast<const char*>(tpm_nonce_.buffer), tpm_nonce_.size); hmac_data.append(reinterpret_cast<const char*>(caller_nonce_.buffer), caller_nonce_.size); hmac_data.append(attributes_bytes); std::string digest = HmacSha256(hmac_key, hmac_data); CHECK_EQ(digest.size(), auth_response.hmac.size); if (!crypto::SecureMemEqual(digest.data(), auth_response.hmac.buffer, digest.size())) { LOG(ERROR) << "Authorization response hash did not match expected value."; return false; } return true; } bool HmacAuthorizationDelegate::EncryptCommandParameter( std::string* parameter) { CHECK(parameter); if (!session_handle_) { LOG(ERROR) << __func__ << ": Invalid session handle."; return false; } if (!is_parameter_encryption_enabled_) { // No parameter encryption enabled. return true; } if (parameter->size() > kTpmBufferSize) { LOG(ERROR) << "Parameter size is too large for TPM decryption."; return false; } RegenerateCallerNonce(); nonce_generated_ = true; AesOperation(parameter, caller_nonce_, tpm_nonce_, AES_ENCRYPT); return true; } bool HmacAuthorizationDelegate::DecryptResponseParameter( std::string* parameter) { CHECK(parameter); if (!session_handle_) { LOG(ERROR) << __func__ << ": Invalid session handle."; return false; } if (!is_parameter_encryption_enabled_) { // No parameter decryption enabled. return true; } if (parameter->size() > kTpmBufferSize) { LOG(ERROR) << "Parameter size is too large for TPM encryption."; return false; } AesOperation(parameter, tpm_nonce_, caller_nonce_, AES_DECRYPT); return true; } bool HmacAuthorizationDelegate::InitSession(TPM_HANDLE session_handle, const TPM2B_NONCE& tpm_nonce, const TPM2B_NONCE& caller_nonce, const std::string& salt, const std::string& bind_auth_value, bool enable_parameter_encryption) { session_handle_ = session_handle; if (caller_nonce.size < kNonceMinSize || caller_nonce.size > kNonceMaxSize || tpm_nonce.size < kNonceMinSize || tpm_nonce.size > kNonceMaxSize) { LOG(INFO) << "Session Nonces have to be between 16 and 32 bytes long."; return false; } tpm_nonce_ = tpm_nonce; caller_nonce_ = caller_nonce; std::string session_key_label("ATH", kLabelSize); is_parameter_encryption_enabled_ = enable_parameter_encryption; if (salt.length() == 0 && bind_auth_value.length() == 0) { // SessionKey is set to the empty string for unsalted and // unbound sessions. session_key_ = std::string(); } else { session_key_ = CreateKey(bind_auth_value + salt, session_key_label, tpm_nonce_, caller_nonce_); } return true; } void HmacAuthorizationDelegate::set_future_authorization_value( const std::string& auth_value) { future_authorization_value_ = auth_value; future_authorization_value_set_ = true; } std::string HmacAuthorizationDelegate::CreateKey( const std::string& hmac_key, const std::string& label, const TPM2B_NONCE& nonce_newer, const TPM2B_NONCE& nonce_older) { std::string counter; std::string digest_size_bits; if (Serialize_uint32_t(1, &counter) != TPM_RC_SUCCESS || Serialize_uint32_t(kDigestBits, &digest_size_bits) != TPM_RC_SUCCESS) { LOG(ERROR) << "Error serializing uint32_t during session key generation."; return std::string(); } CHECK_EQ(counter.size(), sizeof(uint32_t)); CHECK_EQ(digest_size_bits.size(), sizeof(uint32_t)); CHECK_EQ(label.size(), kLabelSize); std::string data; data.append(counter); data.append(label); data.append(reinterpret_cast<const char*>(nonce_newer.buffer), nonce_newer.size); data.append(reinterpret_cast<const char*>(nonce_older.buffer), nonce_older.size); data.append(digest_size_bits); std::string key = HmacSha256(hmac_key, data); return key; } std::string HmacAuthorizationDelegate::HmacSha256(const std::string& key, const std::string& data) { unsigned char digest[EVP_MAX_MD_SIZE]; unsigned int digest_length; HMAC(EVP_sha256(), key.data(), key.size(), reinterpret_cast<const unsigned char*>(data.data()), data.size(), digest, &digest_length); CHECK_EQ(digest_length, kHashDigestSize); return std::string(reinterpret_cast<char*>(digest), digest_length); } void HmacAuthorizationDelegate::AesOperation(std::string* parameter, const TPM2B_NONCE& nonce_newer, const TPM2B_NONCE& nonce_older, int operation_type) { std::string label("CFB", kLabelSize); std::string compound_key = CreateKey(session_key_ + entity_authorization_value_, label, nonce_newer, nonce_older); CHECK_EQ(compound_key.size(), kAesKeySize + kAesIVSize); unsigned char aes_key[kAesKeySize]; unsigned char aes_iv[kAesIVSize]; memcpy(aes_key, &compound_key[0], kAesKeySize); memcpy(aes_iv, &compound_key[kAesKeySize], kAesIVSize); AES_KEY key; int iv_offset = 0; AES_set_encrypt_key(aes_key, kAesKeySize * 8, &key); unsigned char decrypted[kTpmBufferSize]; AES_cfb128_encrypt(reinterpret_cast<const unsigned char*>(parameter->data()), decrypted, parameter->size(), &key, aes_iv, &iv_offset, operation_type); memcpy(base::string_as_array(parameter), decrypted, parameter->size()); } void HmacAuthorizationDelegate::RegenerateCallerNonce() { CHECK(session_handle_); // RAND_bytes takes a signed number, but since nonce_size is guaranteed to be // less than 32 bytes and greater than 16 we dont have to worry about it. CHECK_EQ(RAND_bytes(caller_nonce_.buffer, caller_nonce_.size), 1) << "Error regnerating a cryptographically random nonce."; } } // namespace trunks