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