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