1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_create_keys_operation.h"
6
7 #include <stdint.h>
8
9 #include <memory>
10 #include <string>
11
12 #include "base/base64url.h"
13 #include "base/bind.h"
14 #include "base/logging.h"
15 #include "base/macros.h"
16 #include "base/strings/string_util.h"
17 #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_key_manager.h"
18 #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_types.h"
19 #include "chromeos/components/multidevice/logging/logging.h"
20 #include "chromeos/cryptohome/cryptohome_util.h"
21 #include "chromeos/cryptohome/homedir_methods.h"
22 #include "chromeos/cryptohome/system_salt_getter.h"
23 #include "chromeos/dbus/dbus_thread_manager.h"
24 #include "chromeos/dbus/easy_unlock_client.h"
25 #include "chromeos/login/auth/key.h"
26 #include "crypto/encryptor.h"
27 #include "crypto/random.h"
28 #include "crypto/symmetric_key.h"
29 #include "google_apis/gaia/gaia_auth_util.h"
30 #include "third_party/cros_system_api/dbus/service_constants.h"
31
32 namespace chromeos {
33
34 namespace {
35
36 const int kUserKeyByteSize = 16;
37 const int kSessionKeyByteSize = 16;
38
39 const int kEasyUnlockKeyRevision = 1;
40 const int kEasyUnlockKeyPrivileges =
41 cryptohome::PRIV_ADD | cryptohome::PRIV_REMOVE;
42
43 } // namespace
44
45 /////////////////////////////////////////////////////////////////////////////
46 // EasyUnlockCreateKeysOperation::ChallengeCreator
47
48 class EasyUnlockCreateKeysOperation::ChallengeCreator {
49 public:
50 typedef base::Callback<void(bool success)> ChallengeCreatedCallback;
51 ChallengeCreator(const std::string& user_key,
52 const std::string& session_key,
53 const std::string& tpm_pub_key,
54 EasyUnlockDeviceKeyData* device,
55 const ChallengeCreatedCallback& callback);
56 ~ChallengeCreator();
57
58 void Start();
59
user_key() const60 const std::string& user_key() const { return user_key_; }
61
62 private:
63 void OnEcKeyPairGenerated(const std::string& ec_public_key,
64 const std::string& ec_private_key);
65 void OnEskGenerated(const std::string& esk);
66 void OnTPMPublicKeyWrapped(const std::string& wrapped_key);
67
68 void WrapTPMPublicKey();
69 void GeneratePayload();
70 void OnPayloadMessageGenerated(const std::string& payload_message);
71 void OnPayloadGenerated(const std::string& payload);
72
73 void OnChallengeGenerated(const std::string& challenge);
74
75 const std::string user_key_;
76 const std::string session_key_;
77 const std::string tpm_pub_key_;
78 std::string wrapped_tpm_pub_key_;
79 EasyUnlockDeviceKeyData* device_;
80 ChallengeCreatedCallback callback_;
81
82 std::string ec_public_key_;
83 std::string esk_;
84
85 // Owned by DBusThreadManager
86 EasyUnlockClient* easy_unlock_client_;
87
88 base::WeakPtrFactory<ChallengeCreator> weak_ptr_factory_{this};
89
90 DISALLOW_COPY_AND_ASSIGN(ChallengeCreator);
91 };
92
ChallengeCreator(const std::string & user_key,const std::string & session_key,const std::string & tpm_pub_key,EasyUnlockDeviceKeyData * device,const ChallengeCreatedCallback & callback)93 EasyUnlockCreateKeysOperation::ChallengeCreator::ChallengeCreator(
94 const std::string& user_key,
95 const std::string& session_key,
96 const std::string& tpm_pub_key,
97 EasyUnlockDeviceKeyData* device,
98 const ChallengeCreatedCallback& callback)
99 : user_key_(user_key),
100 session_key_(session_key),
101 tpm_pub_key_(tpm_pub_key),
102 device_(device),
103 callback_(callback),
104 easy_unlock_client_(DBusThreadManager::Get()->GetEasyUnlockClient()) {}
105
~ChallengeCreator()106 EasyUnlockCreateKeysOperation::ChallengeCreator::~ChallengeCreator() {}
107
Start()108 void EasyUnlockCreateKeysOperation::ChallengeCreator::Start() {
109 easy_unlock_client_->GenerateEcP256KeyPair(base::BindOnce(
110 &ChallengeCreator::OnEcKeyPairGenerated, weak_ptr_factory_.GetWeakPtr()));
111 }
112
OnEcKeyPairGenerated(const std::string & ec_private_key,const std::string & ec_public_key)113 void EasyUnlockCreateKeysOperation::ChallengeCreator::OnEcKeyPairGenerated(
114 const std::string& ec_private_key,
115 const std::string& ec_public_key) {
116 if (ec_private_key.empty() || ec_public_key.empty()) {
117 PA_LOG(ERROR) << "Easy unlock failed to generate ec key pair.";
118 callback_.Run(false);
119 return;
120 }
121
122 std::string device_pub_key;
123 if (!base::Base64UrlDecode(device_->public_key,
124 base::Base64UrlDecodePolicy::REQUIRE_PADDING,
125 &device_pub_key)) {
126 PA_LOG(ERROR) << "Easy unlock failed to decode device public key.";
127 callback_.Run(false);
128 return;
129 }
130
131 ec_public_key_ = ec_public_key;
132 easy_unlock_client_->PerformECDHKeyAgreement(
133 ec_private_key, device_pub_key,
134 base::BindOnce(&ChallengeCreator::OnEskGenerated,
135 weak_ptr_factory_.GetWeakPtr()));
136 }
137
OnEskGenerated(const std::string & esk)138 void EasyUnlockCreateKeysOperation::ChallengeCreator::OnEskGenerated(
139 const std::string& esk) {
140 if (esk.empty()) {
141 PA_LOG(ERROR) << "Easy unlock failed to generate challenge esk.";
142 callback_.Run(false);
143 return;
144 }
145
146 esk_ = esk;
147 WrapTPMPublicKey();
148 }
149
WrapTPMPublicKey()150 void EasyUnlockCreateKeysOperation::ChallengeCreator::WrapTPMPublicKey() {
151 easy_unlock_client_->WrapPublicKey(
152 easy_unlock::kKeyAlgorithmRSA, tpm_pub_key_,
153 base::BindOnce(&ChallengeCreator::OnTPMPublicKeyWrapped,
154 weak_ptr_factory_.GetWeakPtr()));
155 }
156
OnTPMPublicKeyWrapped(const std::string & wrapped_key)157 void EasyUnlockCreateKeysOperation::ChallengeCreator::OnTPMPublicKeyWrapped(
158 const std::string& wrapped_key) {
159 if (wrapped_key.empty()) {
160 PA_LOG(ERROR) << "Unable to wrap RSA key";
161 callback_.Run(false);
162 return;
163 }
164 wrapped_tpm_pub_key_ = wrapped_key;
165 GeneratePayload();
166 }
167
GeneratePayload()168 void EasyUnlockCreateKeysOperation::ChallengeCreator::GeneratePayload() {
169 // Work around to get HeaderAndBody bytes to use as challenge payload.
170 EasyUnlockClient::CreateSecureMessageOptions options;
171 options.key = esk_;
172 options.verification_key_id = wrapped_tpm_pub_key_;
173 options.encryption_type = easy_unlock::kEncryptionTypeAES256CBC;
174 options.signature_type = easy_unlock::kSignatureTypeHMACSHA256;
175
176 easy_unlock_client_->CreateSecureMessage(
177 session_key_, options,
178 base::BindOnce(&ChallengeCreator::OnPayloadMessageGenerated,
179 weak_ptr_factory_.GetWeakPtr()));
180 }
181
OnPayloadMessageGenerated(const std::string & payload_message)182 void EasyUnlockCreateKeysOperation::ChallengeCreator::OnPayloadMessageGenerated(
183 const std::string& payload_message) {
184 EasyUnlockClient::UnwrapSecureMessageOptions options;
185 options.key = esk_;
186 options.encryption_type = easy_unlock::kEncryptionTypeAES256CBC;
187 options.signature_type = easy_unlock::kSignatureTypeHMACSHA256;
188
189 easy_unlock_client_->UnwrapSecureMessage(
190 payload_message, options,
191 base::BindOnce(&ChallengeCreator::OnPayloadGenerated,
192 weak_ptr_factory_.GetWeakPtr()));
193 }
194
OnPayloadGenerated(const std::string & payload)195 void EasyUnlockCreateKeysOperation::ChallengeCreator::OnPayloadGenerated(
196 const std::string& payload) {
197 if (payload.empty()) {
198 PA_LOG(ERROR) << "Easy unlock failed to generate challenge payload.";
199 callback_.Run(false);
200 return;
201 }
202
203 EasyUnlockClient::CreateSecureMessageOptions options;
204 options.key = esk_;
205 options.decryption_key_id = ec_public_key_;
206 options.encryption_type = easy_unlock::kEncryptionTypeAES256CBC;
207 options.signature_type = easy_unlock::kSignatureTypeHMACSHA256;
208
209 easy_unlock_client_->CreateSecureMessage(
210 payload, options,
211 base::BindOnce(&ChallengeCreator::OnChallengeGenerated,
212 weak_ptr_factory_.GetWeakPtr()));
213 }
214
OnChallengeGenerated(const std::string & challenge)215 void EasyUnlockCreateKeysOperation::ChallengeCreator::OnChallengeGenerated(
216 const std::string& challenge) {
217 if (challenge.empty()) {
218 PA_LOG(ERROR) << "Easy unlock failed to generate challenge.";
219 callback_.Run(false);
220 return;
221 }
222
223 device_->challenge = challenge;
224 callback_.Run(true);
225 }
226
227 /////////////////////////////////////////////////////////////////////////////
228 // EasyUnlockCreateKeysOperation
229
EasyUnlockCreateKeysOperation(const UserContext & user_context,const std::string & tpm_public_key,const EasyUnlockDeviceKeyDataList & devices,const CreateKeysCallback & callback)230 EasyUnlockCreateKeysOperation::EasyUnlockCreateKeysOperation(
231 const UserContext& user_context,
232 const std::string& tpm_public_key,
233 const EasyUnlockDeviceKeyDataList& devices,
234 const CreateKeysCallback& callback)
235 : user_context_(user_context),
236 tpm_public_key_(tpm_public_key),
237 devices_(devices),
238 callback_(callback),
239 key_creation_index_(0) {
240 // Must have the secret and callback.
241 DCHECK(!user_context_.GetKey()->GetSecret().empty());
242 DCHECK(!callback_.is_null());
243 }
244
~EasyUnlockCreateKeysOperation()245 EasyUnlockCreateKeysOperation::~EasyUnlockCreateKeysOperation() {}
246
Start()247 void EasyUnlockCreateKeysOperation::Start() {
248 key_creation_index_ = 0;
249 CreateKeyForDeviceAtIndex(key_creation_index_);
250 }
251
CreateKeyForDeviceAtIndex(size_t index)252 void EasyUnlockCreateKeysOperation::CreateKeyForDeviceAtIndex(size_t index) {
253 DCHECK_GE(index, 0u);
254 if (index == devices_.size()) {
255 callback_.Run(true);
256 return;
257 }
258
259 std::string user_key;
260 crypto::RandBytes(base::WriteInto(&user_key, kUserKeyByteSize + 1),
261 kUserKeyByteSize);
262
263 std::unique_ptr<crypto::SymmetricKey> session_key(
264 crypto::SymmetricKey::GenerateRandomKey(crypto::SymmetricKey::AES,
265 kSessionKeyByteSize * 8));
266
267 std::string iv(kSessionKeyByteSize, ' ');
268 crypto::Encryptor encryptor;
269 if (!encryptor.Init(session_key.get(), crypto::Encryptor::CBC, iv)) {
270 PA_LOG(ERROR) << "Easy unlock failed to init encryptor for key creation.";
271 callback_.Run(false);
272 return;
273 }
274
275 EasyUnlockDeviceKeyData* device = &devices_[index];
276 if (!encryptor.Encrypt(user_key, &device->wrapped_secret)) {
277 PA_LOG(ERROR) << "Easy unlock failed to encrypt user key for key creation.";
278 callback_.Run(false);
279 return;
280 }
281
282 challenge_creator_.reset(new ChallengeCreator(
283 user_key, session_key->key(), tpm_public_key_, device,
284 base::Bind(&EasyUnlockCreateKeysOperation::OnChallengeCreated,
285 weak_ptr_factory_.GetWeakPtr(), index)));
286 challenge_creator_->Start();
287 }
288
OnChallengeCreated(size_t index,bool success)289 void EasyUnlockCreateKeysOperation::OnChallengeCreated(size_t index,
290 bool success) {
291 DCHECK_EQ(key_creation_index_, index);
292
293 if (!success) {
294 PA_LOG(ERROR) << "Easy unlock failed to create challenge for key creation.";
295 callback_.Run(false);
296 return;
297 }
298
299 SystemSaltGetter::Get()->GetSystemSalt(
300 base::BindOnce(&EasyUnlockCreateKeysOperation::OnGetSystemSalt,
301 weak_ptr_factory_.GetWeakPtr(), index));
302 }
303
OnGetSystemSalt(size_t index,const std::string & system_salt)304 void EasyUnlockCreateKeysOperation::OnGetSystemSalt(
305 size_t index,
306 const std::string& system_salt) {
307 DCHECK_EQ(key_creation_index_, index);
308 if (system_salt.empty()) {
309 PA_LOG(ERROR) << "Easy unlock failed to get system salt for key creation.";
310 callback_.Run(false);
311 return;
312 }
313
314 Key user_key(challenge_creator_->user_key());
315 user_key.Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF, system_salt);
316
317 EasyUnlockDeviceKeyData* device = &devices_[index];
318 auto key_def = cryptohome::KeyDefinition::CreateForPassword(
319 user_key.GetSecret(), EasyUnlockKeyManager::GetKeyLabel(index),
320 kEasyUnlockKeyPrivileges);
321 key_def.revision = kEasyUnlockKeyRevision;
322 key_def.provider_data.push_back(cryptohome::KeyDefinition::ProviderData(
323 kEasyUnlockKeyMetaNameBluetoothAddress, device->bluetooth_address));
324 key_def.provider_data.push_back(cryptohome::KeyDefinition::ProviderData(
325 kEasyUnlockKeyMetaNamePsk, device->psk));
326 key_def.provider_data.push_back(cryptohome::KeyDefinition::ProviderData(
327 kEasyUnlockKeyMetaNamePubKey, device->public_key));
328 key_def.provider_data.push_back(cryptohome::KeyDefinition::ProviderData(
329 kEasyUnlockKeyMetaNameChallenge, device->challenge));
330 key_def.provider_data.push_back(cryptohome::KeyDefinition::ProviderData(
331 kEasyUnlockKeyMetaNameWrappedSecret, device->wrapped_secret));
332 key_def.provider_data.push_back(cryptohome::KeyDefinition::ProviderData(
333 kEasyUnlockKeyMetaNameSerializedBeaconSeeds,
334 device->serialized_beacon_seeds));
335 // ProviderData only has std::string and int64_t fields for persistence -- use
336 // the int64_t field to store this boolean. The boolean is stored as either a
337 // 1 or 0 in as an int64_t.
338 key_def.provider_data.push_back(cryptohome::KeyDefinition::ProviderData(
339 kEasyUnlockKeyMetaNameUnlockKey,
340 static_cast<int64_t>(device->unlock_key)));
341
342 std::unique_ptr<Key> auth_key(new Key(*user_context_.GetKey()));
343 if (auth_key->GetKeyType() == Key::KEY_TYPE_PASSWORD_PLAIN)
344 auth_key->Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF, system_salt);
345
346 cryptohome::AddKeyRequest request;
347 cryptohome::KeyDefinitionToKey(key_def, request.mutable_key());
348 request.set_clobber_if_exists(true);
349
350 // Create the authorization request with an empty label, in order to act as a
351 // wildcard. See https://crbug.com/1002336 for more.
352 cryptohome::HomedirMethods::GetInstance()->AddKeyEx(
353 cryptohome::Identification(user_context_.GetAccountId()),
354 cryptohome::CreateAuthorizationRequest(std::string() /* label */,
355 auth_key->GetSecret()),
356 request,
357 base::BindOnce(&EasyUnlockCreateKeysOperation::OnKeyCreated,
358 weak_ptr_factory_.GetWeakPtr(), index, user_key));
359 }
360
OnKeyCreated(size_t index,const Key & user_key,bool success,cryptohome::MountError return_code)361 void EasyUnlockCreateKeysOperation::OnKeyCreated(
362 size_t index,
363 const Key& user_key,
364 bool success,
365 cryptohome::MountError return_code) {
366 DCHECK_EQ(key_creation_index_, index);
367
368 if (!success) {
369 PA_LOG(ERROR) << "Easy unlock failed to create key, code=" << return_code;
370 callback_.Run(false);
371 return;
372 }
373
374 // If the key associated with the current context changed (i.e. in the case
375 // the current signin flow was Easy signin), update the user context.
376 if (user_context_.GetAuthFlow() == UserContext::AUTH_FLOW_EASY_UNLOCK &&
377 user_context_.GetKey()->GetLabel() ==
378 EasyUnlockKeyManager::GetKeyLabel(key_creation_index_)) {
379 user_context_.SetKey(user_key);
380 }
381
382 ++key_creation_index_;
383 CreateKeyForDeviceAtIndex(key_creation_index_);
384 }
385
386 } // namespace chromeos
387