216 lines
7.9 KiB
C++
216 lines
7.9 KiB
C++
//
|
|
// Copyright (C) 2015 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/session_manager_impl.h"
|
|
|
|
#include <string>
|
|
|
|
#include <base/logging.h>
|
|
#include <base/stl_util.h>
|
|
#include <crypto/openssl_util.h>
|
|
#include <crypto/scoped_openssl_types.h>
|
|
#include <openssl/err.h>
|
|
#include <openssl/evp.h>
|
|
#if defined(OPENSSL_IS_BORINGSSL)
|
|
#include <openssl/mem.h>
|
|
#endif
|
|
#include <openssl/rand.h>
|
|
#include <openssl/rsa.h>
|
|
|
|
#include "trunks/error_codes.h"
|
|
#include "trunks/tpm_generated.h"
|
|
#include "trunks/tpm_utility.h"
|
|
|
|
namespace {
|
|
const size_t kWellKnownExponent = 0x10001;
|
|
|
|
std::string GetOpenSSLError() {
|
|
BIO* bio = BIO_new(BIO_s_mem());
|
|
ERR_print_errors(bio);
|
|
char* data = nullptr;
|
|
int data_len = BIO_get_mem_data(bio, &data);
|
|
std::string error_string(data, data_len);
|
|
BIO_free(bio);
|
|
return error_string;
|
|
}
|
|
} // namespace
|
|
|
|
namespace trunks {
|
|
|
|
SessionManagerImpl::SessionManagerImpl(const TrunksFactory& factory)
|
|
: factory_(factory), session_handle_(kUninitializedHandle) {
|
|
crypto::EnsureOpenSSLInit();
|
|
}
|
|
|
|
SessionManagerImpl::~SessionManagerImpl() {
|
|
CloseSession();
|
|
}
|
|
|
|
void SessionManagerImpl::CloseSession() {
|
|
if (session_handle_ == kUninitializedHandle) {
|
|
return;
|
|
}
|
|
TPM_RC result = factory_.GetTpm()->FlushContextSync(session_handle_, nullptr);
|
|
if (result != TPM_RC_SUCCESS) {
|
|
LOG(WARNING) << "Error closing tpm session: " << GetErrorString(result);
|
|
}
|
|
session_handle_ = kUninitializedHandle;
|
|
}
|
|
|
|
TPM_RC SessionManagerImpl::StartSession(
|
|
TPM_SE session_type,
|
|
TPMI_DH_ENTITY bind_entity,
|
|
const std::string& bind_authorization_value,
|
|
bool enable_encryption,
|
|
HmacAuthorizationDelegate* delegate) {
|
|
CHECK(delegate);
|
|
// If we already have an active session, close it.
|
|
CloseSession();
|
|
|
|
std::string salt(SHA256_DIGEST_SIZE, 0);
|
|
unsigned char* salt_buffer =
|
|
reinterpret_cast<unsigned char*>(string_as_array(&salt));
|
|
CHECK_EQ(RAND_bytes(salt_buffer, salt.size()), 1)
|
|
<< "Error generating a cryptographically random salt.";
|
|
// First we encrypt the cryptographically secure salt using PKCS1_OAEP
|
|
// padded RSA public key encryption. This is specified in TPM2.0
|
|
// Part1 Architecture, Appendix B.10.2.
|
|
std::string encrypted_salt;
|
|
TPM_RC salt_result = EncryptSalt(salt, &encrypted_salt);
|
|
if (salt_result != TPM_RC_SUCCESS) {
|
|
LOG(ERROR) << "Error encrypting salt: " << GetErrorString(salt_result);
|
|
return salt_result;
|
|
}
|
|
|
|
TPM2B_ENCRYPTED_SECRET encrypted_secret =
|
|
Make_TPM2B_ENCRYPTED_SECRET(encrypted_salt);
|
|
// Then we use TPM2_StartAuthSession to start a HMAC session with the TPM.
|
|
// The tpm returns the tpm_nonce and the session_handle referencing the
|
|
// created session.
|
|
TPMI_ALG_HASH hash_algorithm = TPM_ALG_SHA256;
|
|
TPMT_SYM_DEF symmetric_algorithm;
|
|
symmetric_algorithm.algorithm = TPM_ALG_AES;
|
|
symmetric_algorithm.key_bits.aes = 128;
|
|
symmetric_algorithm.mode.aes = TPM_ALG_CFB;
|
|
|
|
TPM2B_NONCE nonce_caller;
|
|
TPM2B_NONCE nonce_tpm;
|
|
// We use sha1_digest_size here because that is the minimum length
|
|
// needed for the nonce.
|
|
nonce_caller.size = SHA1_DIGEST_SIZE;
|
|
CHECK_EQ(RAND_bytes(nonce_caller.buffer, nonce_caller.size), 1)
|
|
<< "Error generating a cryptographically random nonce.";
|
|
|
|
Tpm* tpm = factory_.GetTpm();
|
|
// The TPM2 command below needs no authorization. This is why we can use
|
|
// the empty string "", when referring to the handle names for the salting
|
|
// key and the bind entity.
|
|
TPM_RC tpm_result = tpm->StartAuthSessionSync(
|
|
kSaltingKey,
|
|
"", // salt_handle_name.
|
|
bind_entity,
|
|
"", // bind_entity_name.
|
|
nonce_caller, encrypted_secret, session_type, symmetric_algorithm,
|
|
hash_algorithm, &session_handle_, &nonce_tpm,
|
|
nullptr); // No Authorization.
|
|
if (tpm_result) {
|
|
LOG(ERROR) << "Error creating an authorization session: "
|
|
<< GetErrorString(tpm_result);
|
|
return tpm_result;
|
|
}
|
|
bool hmac_result =
|
|
delegate->InitSession(session_handle_, nonce_tpm, nonce_caller, salt,
|
|
bind_authorization_value, enable_encryption);
|
|
if (!hmac_result) {
|
|
LOG(ERROR) << "Failed to initialize an authorization session delegate.";
|
|
return TPM_RC_FAILURE;
|
|
}
|
|
return TPM_RC_SUCCESS;
|
|
}
|
|
|
|
TPM_RC SessionManagerImpl::EncryptSalt(const std::string& salt,
|
|
std::string* encrypted_salt) {
|
|
TPM2B_NAME out_name;
|
|
TPM2B_NAME qualified_name;
|
|
TPM2B_PUBLIC public_data;
|
|
public_data.public_area.unique.rsa.size = 0;
|
|
TPM_RC result = factory_.GetTpm()->ReadPublicSync(
|
|
kSaltingKey, "" /*object_handle_name (not used)*/, &public_data,
|
|
&out_name, &qualified_name, nullptr /*authorization_delegate*/);
|
|
if (result != TPM_RC_SUCCESS) {
|
|
LOG(ERROR) << "Error fetching salting key public info: "
|
|
<< GetErrorString(result);
|
|
return result;
|
|
}
|
|
if (public_data.public_area.type != TPM_ALG_RSA ||
|
|
public_data.public_area.unique.rsa.size != 256) {
|
|
LOG(ERROR) << "Invalid salting key attributes.";
|
|
return TRUNKS_RC_SESSION_SETUP_ERROR;
|
|
}
|
|
crypto::ScopedRSA salting_key_rsa(RSA_new());
|
|
salting_key_rsa->e = BN_new();
|
|
if (!salting_key_rsa->e) {
|
|
LOG(ERROR) << "Error creating exponent for RSA: " << GetOpenSSLError();
|
|
return TRUNKS_RC_SESSION_SETUP_ERROR;
|
|
}
|
|
BN_set_word(salting_key_rsa->e, kWellKnownExponent);
|
|
salting_key_rsa->n =
|
|
BN_bin2bn(public_data.public_area.unique.rsa.buffer,
|
|
public_data.public_area.unique.rsa.size, nullptr);
|
|
if (!salting_key_rsa->n) {
|
|
LOG(ERROR) << "Error setting public area of rsa key: " << GetOpenSSLError();
|
|
return TRUNKS_RC_SESSION_SETUP_ERROR;
|
|
}
|
|
crypto::ScopedEVP_PKEY salting_key(EVP_PKEY_new());
|
|
if (!EVP_PKEY_set1_RSA(salting_key.get(), salting_key_rsa.get())) {
|
|
LOG(ERROR) << "Error setting up EVP_PKEY: " << GetOpenSSLError();
|
|
return TRUNKS_RC_SESSION_SETUP_ERROR;
|
|
}
|
|
// Label for RSAES-OAEP. Defined in TPM2.0 Part1 Architecture,
|
|
// Appendix B.10.2.
|
|
const size_t kOaepLabelSize = 7;
|
|
const char kOaepLabelValue[] = "SECRET\0";
|
|
// EVP_PKEY_CTX_set0_rsa_oaep_label takes ownership so we need to malloc.
|
|
uint8_t* oaep_label = static_cast<uint8_t*>(OPENSSL_malloc(kOaepLabelSize));
|
|
memcpy(oaep_label, kOaepLabelValue, kOaepLabelSize);
|
|
crypto::ScopedEVP_PKEY_CTX salt_encrypt_context(
|
|
EVP_PKEY_CTX_new(salting_key.get(), nullptr));
|
|
if (!EVP_PKEY_encrypt_init(salt_encrypt_context.get()) ||
|
|
!EVP_PKEY_CTX_set_rsa_padding(salt_encrypt_context.get(),
|
|
RSA_PKCS1_OAEP_PADDING) ||
|
|
!EVP_PKEY_CTX_set_rsa_oaep_md(salt_encrypt_context.get(), EVP_sha256()) ||
|
|
!EVP_PKEY_CTX_set_rsa_mgf1_md(salt_encrypt_context.get(), EVP_sha256()) ||
|
|
!EVP_PKEY_CTX_set0_rsa_oaep_label(salt_encrypt_context.get(), oaep_label,
|
|
kOaepLabelSize)) {
|
|
LOG(ERROR) << "Error setting up salt encrypt context: "
|
|
<< GetOpenSSLError();
|
|
return TRUNKS_RC_SESSION_SETUP_ERROR;
|
|
}
|
|
size_t out_length = EVP_PKEY_size(salting_key.get());
|
|
encrypted_salt->resize(out_length);
|
|
if (!EVP_PKEY_encrypt(
|
|
salt_encrypt_context.get(),
|
|
reinterpret_cast<uint8_t*>(string_as_array(encrypted_salt)),
|
|
&out_length, reinterpret_cast<const uint8_t*>(salt.data()),
|
|
salt.size())) {
|
|
LOG(ERROR) << "Error encrypting salt: " << GetOpenSSLError();
|
|
return TRUNKS_RC_SESSION_SETUP_ERROR;
|
|
}
|
|
encrypted_salt->resize(out_length);
|
|
return TPM_RC_SUCCESS;
|
|
}
|
|
|
|
} // namespace trunks
|