| // |
| // 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 |