1 //
2 // Copyright (C) 2015 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "trunks/session_manager_impl.h"
18
19 #include <string>
20
21 #include <base/logging.h>
22 #include <base/stl_util.h>
23 #include <crypto/openssl_util.h>
24 #include <crypto/scoped_openssl_types.h>
25 #include <openssl/err.h>
26 #include <openssl/evp.h>
27 #if defined(OPENSSL_IS_BORINGSSL)
28 #include <openssl/mem.h>
29 #endif
30 #include <openssl/rand.h>
31 #include <openssl/rsa.h>
32
33 #include "trunks/error_codes.h"
34 #include "trunks/tpm_generated.h"
35 #include "trunks/tpm_utility.h"
36
37 namespace {
38 const size_t kWellKnownExponent = 0x10001;
39
GetOpenSSLError()40 std::string GetOpenSSLError() {
41 BIO* bio = BIO_new(BIO_s_mem());
42 ERR_print_errors(bio);
43 char* data = nullptr;
44 int data_len = BIO_get_mem_data(bio, &data);
45 std::string error_string(data, data_len);
46 BIO_free(bio);
47 return error_string;
48 }
49 } // namespace
50
51 namespace trunks {
52
SessionManagerImpl(const TrunksFactory & factory)53 SessionManagerImpl::SessionManagerImpl(const TrunksFactory& factory)
54 : factory_(factory), session_handle_(kUninitializedHandle) {
55 crypto::EnsureOpenSSLInit();
56 }
57
~SessionManagerImpl()58 SessionManagerImpl::~SessionManagerImpl() {
59 CloseSession();
60 }
61
CloseSession()62 void SessionManagerImpl::CloseSession() {
63 if (session_handle_ == kUninitializedHandle) {
64 return;
65 }
66 TPM_RC result = factory_.GetTpm()->FlushContextSync(session_handle_, nullptr);
67 if (result != TPM_RC_SUCCESS) {
68 LOG(WARNING) << "Error closing tpm session: " << GetErrorString(result);
69 }
70 session_handle_ = kUninitializedHandle;
71 }
72
StartSession(TPM_SE session_type,TPMI_DH_ENTITY bind_entity,const std::string & bind_authorization_value,bool enable_encryption,HmacAuthorizationDelegate * delegate)73 TPM_RC SessionManagerImpl::StartSession(
74 TPM_SE session_type,
75 TPMI_DH_ENTITY bind_entity,
76 const std::string& bind_authorization_value,
77 bool enable_encryption,
78 HmacAuthorizationDelegate* delegate) {
79 CHECK(delegate);
80 // If we already have an active session, close it.
81 CloseSession();
82
83 std::string salt(SHA256_DIGEST_SIZE, 0);
84 unsigned char* salt_buffer =
85 reinterpret_cast<unsigned char*>(string_as_array(&salt));
86 CHECK_EQ(RAND_bytes(salt_buffer, salt.size()), 1)
87 << "Error generating a cryptographically random salt.";
88 // First we encrypt the cryptographically secure salt using PKCS1_OAEP
89 // padded RSA public key encryption. This is specified in TPM2.0
90 // Part1 Architecture, Appendix B.10.2.
91 std::string encrypted_salt;
92 TPM_RC salt_result = EncryptSalt(salt, &encrypted_salt);
93 if (salt_result != TPM_RC_SUCCESS) {
94 LOG(ERROR) << "Error encrypting salt: " << GetErrorString(salt_result);
95 return salt_result;
96 }
97
98 TPM2B_ENCRYPTED_SECRET encrypted_secret =
99 Make_TPM2B_ENCRYPTED_SECRET(encrypted_salt);
100 // Then we use TPM2_StartAuthSession to start a HMAC session with the TPM.
101 // The tpm returns the tpm_nonce and the session_handle referencing the
102 // created session.
103 TPMI_ALG_HASH hash_algorithm = TPM_ALG_SHA256;
104 TPMT_SYM_DEF symmetric_algorithm;
105 symmetric_algorithm.algorithm = TPM_ALG_AES;
106 symmetric_algorithm.key_bits.aes = 128;
107 symmetric_algorithm.mode.aes = TPM_ALG_CFB;
108
109 TPM2B_NONCE nonce_caller;
110 TPM2B_NONCE nonce_tpm;
111 // We use sha1_digest_size here because that is the minimum length
112 // needed for the nonce.
113 nonce_caller.size = SHA1_DIGEST_SIZE;
114 CHECK_EQ(RAND_bytes(nonce_caller.buffer, nonce_caller.size), 1)
115 << "Error generating a cryptographically random nonce.";
116
117 Tpm* tpm = factory_.GetTpm();
118 // The TPM2 command below needs no authorization. This is why we can use
119 // the empty string "", when referring to the handle names for the salting
120 // key and the bind entity.
121 TPM_RC tpm_result = tpm->StartAuthSessionSync(
122 kSaltingKey,
123 "", // salt_handle_name.
124 bind_entity,
125 "", // bind_entity_name.
126 nonce_caller, encrypted_secret, session_type, symmetric_algorithm,
127 hash_algorithm, &session_handle_, &nonce_tpm,
128 nullptr); // No Authorization.
129 if (tpm_result) {
130 LOG(ERROR) << "Error creating an authorization session: "
131 << GetErrorString(tpm_result);
132 return tpm_result;
133 }
134 bool hmac_result =
135 delegate->InitSession(session_handle_, nonce_tpm, nonce_caller, salt,
136 bind_authorization_value, enable_encryption);
137 if (!hmac_result) {
138 LOG(ERROR) << "Failed to initialize an authorization session delegate.";
139 return TPM_RC_FAILURE;
140 }
141 return TPM_RC_SUCCESS;
142 }
143
EncryptSalt(const std::string & salt,std::string * encrypted_salt)144 TPM_RC SessionManagerImpl::EncryptSalt(const std::string& salt,
145 std::string* encrypted_salt) {
146 TPM2B_NAME out_name;
147 TPM2B_NAME qualified_name;
148 TPM2B_PUBLIC public_data;
149 public_data.public_area.unique.rsa.size = 0;
150 TPM_RC result = factory_.GetTpm()->ReadPublicSync(
151 kSaltingKey, "" /*object_handle_name (not used)*/, &public_data,
152 &out_name, &qualified_name, nullptr /*authorization_delegate*/);
153 if (result != TPM_RC_SUCCESS) {
154 LOG(ERROR) << "Error fetching salting key public info: "
155 << GetErrorString(result);
156 return result;
157 }
158 if (public_data.public_area.type != TPM_ALG_RSA ||
159 public_data.public_area.unique.rsa.size != 256) {
160 LOG(ERROR) << "Invalid salting key attributes.";
161 return TRUNKS_RC_SESSION_SETUP_ERROR;
162 }
163 crypto::ScopedRSA salting_key_rsa(RSA_new());
164 salting_key_rsa->e = BN_new();
165 if (!salting_key_rsa->e) {
166 LOG(ERROR) << "Error creating exponent for RSA: " << GetOpenSSLError();
167 return TRUNKS_RC_SESSION_SETUP_ERROR;
168 }
169 BN_set_word(salting_key_rsa->e, kWellKnownExponent);
170 salting_key_rsa->n =
171 BN_bin2bn(public_data.public_area.unique.rsa.buffer,
172 public_data.public_area.unique.rsa.size, nullptr);
173 if (!salting_key_rsa->n) {
174 LOG(ERROR) << "Error setting public area of rsa key: " << GetOpenSSLError();
175 return TRUNKS_RC_SESSION_SETUP_ERROR;
176 }
177 crypto::ScopedEVP_PKEY salting_key(EVP_PKEY_new());
178 if (!EVP_PKEY_set1_RSA(salting_key.get(), salting_key_rsa.get())) {
179 LOG(ERROR) << "Error setting up EVP_PKEY: " << GetOpenSSLError();
180 return TRUNKS_RC_SESSION_SETUP_ERROR;
181 }
182 // Label for RSAES-OAEP. Defined in TPM2.0 Part1 Architecture,
183 // Appendix B.10.2.
184 const size_t kOaepLabelSize = 7;
185 const char kOaepLabelValue[] = "SECRET\0";
186 // EVP_PKEY_CTX_set0_rsa_oaep_label takes ownership so we need to malloc.
187 uint8_t* oaep_label = static_cast<uint8_t*>(OPENSSL_malloc(kOaepLabelSize));
188 memcpy(oaep_label, kOaepLabelValue, kOaepLabelSize);
189 crypto::ScopedEVP_PKEY_CTX salt_encrypt_context(
190 EVP_PKEY_CTX_new(salting_key.get(), nullptr));
191 if (!EVP_PKEY_encrypt_init(salt_encrypt_context.get()) ||
192 !EVP_PKEY_CTX_set_rsa_padding(salt_encrypt_context.get(),
193 RSA_PKCS1_OAEP_PADDING) ||
194 !EVP_PKEY_CTX_set_rsa_oaep_md(salt_encrypt_context.get(), EVP_sha256()) ||
195 !EVP_PKEY_CTX_set_rsa_mgf1_md(salt_encrypt_context.get(), EVP_sha256()) ||
196 !EVP_PKEY_CTX_set0_rsa_oaep_label(salt_encrypt_context.get(), oaep_label,
197 kOaepLabelSize)) {
198 LOG(ERROR) << "Error setting up salt encrypt context: "
199 << GetOpenSSLError();
200 return TRUNKS_RC_SESSION_SETUP_ERROR;
201 }
202 size_t out_length = EVP_PKEY_size(salting_key.get());
203 encrypted_salt->resize(out_length);
204 if (!EVP_PKEY_encrypt(
205 salt_encrypt_context.get(),
206 reinterpret_cast<uint8_t*>(string_as_array(encrypted_salt)),
207 &out_length, reinterpret_cast<const uint8_t*>(salt.data()),
208 salt.size())) {
209 LOG(ERROR) << "Error encrypting salt: " << GetOpenSSLError();
210 return TRUNKS_RC_SESSION_SETUP_ERROR;
211 }
212 encrypted_salt->resize(out_length);
213 return TPM_RC_SUCCESS;
214 }
215
216 } // namespace trunks
217