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