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
35namespace {
36const size_t kWellKnownExponent = 0x10001;
37
38std::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
49namespace trunks {
50
51SessionManagerImpl::SessionManagerImpl(const TrunksFactory& factory)
52    : factory_(factory),
53      session_handle_(kUninitializedHandle) {
54  crypto::EnsureOpenSSLInit();
55}
56
57SessionManagerImpl::~SessionManagerImpl() {
58  CloseSession();
59}
60
61void 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
72TPM_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
151TPM_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