Move base64url.* to CryptAuth component.
[chromium-blink-merge.git] / chrome / browser / chromeos / login / easy_unlock / easy_unlock_create_keys_operation.cc
blobe5a42fb0b199049c460d516b2463530db9fafbe4
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.
5 #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_create_keys_operation.h"
7 #include <string>
9 #include "base/bind.h"
10 #include "base/logging.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/strings/string_util.h"
13 #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_key_manager.h"
14 #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_types.h"
15 #include "chromeos/cryptohome/homedir_methods.h"
16 #include "chromeos/cryptohome/system_salt_getter.h"
17 #include "chromeos/dbus/dbus_thread_manager.h"
18 #include "chromeos/dbus/easy_unlock_client.h"
19 #include "chromeos/login/auth/key.h"
20 #include "components/proximity_auth/cryptauth/base64url.h"
21 #include "crypto/encryptor.h"
22 #include "crypto/random.h"
23 #include "crypto/symmetric_key.h"
24 #include "google_apis/gaia/gaia_auth_util.h"
25 #include "third_party/cros_system_api/dbus/service_constants.h"
27 namespace chromeos {
29 namespace {
31 const int kUserKeyByteSize = 16;
32 const int kSessionKeyByteSize = 16;
34 const int kEasyUnlockKeyRevision = 1;
35 const int kEasyUnlockKeyPrivileges =
36 cryptohome::PRIV_MOUNT | cryptohome::PRIV_ADD | cryptohome::PRIV_REMOVE;
38 } // namespace
40 /////////////////////////////////////////////////////////////////////////////
41 // EasyUnlockCreateKeysOperation::ChallengeCreator
43 class EasyUnlockCreateKeysOperation::ChallengeCreator {
44 public:
45 typedef base::Callback<void (bool success)> ChallengeCreatedCallback;
46 ChallengeCreator(const std::string& user_key,
47 const std::string& session_key,
48 const std::string& tpm_pub_key,
49 EasyUnlockDeviceKeyData* device,
50 const ChallengeCreatedCallback& callback);
51 ~ChallengeCreator();
53 void Start();
55 const std::string& user_key() const { return user_key_; }
57 private:
58 void OnEcKeyPairGenerated(const std::string& ec_public_key,
59 const std::string& ec_private_key);
60 void OnEskGenerated(const std::string& esk);
61 void OnTPMPublicKeyWrapped(const std::string& wrapped_key);
63 void WrapTPMPublicKey();
64 void GeneratePayload();
65 void OnPayloadMessageGenerated(const std::string& payload_message);
66 void OnPayloadGenerated(const std::string& payload);
68 void OnChallengeGenerated(const std::string& challenge);
70 const std::string user_key_;
71 const std::string session_key_;
72 const std::string tpm_pub_key_;
73 std::string wrapped_tpm_pub_key_;
74 EasyUnlockDeviceKeyData* device_;
75 ChallengeCreatedCallback callback_;
77 std::string ec_public_key_;
78 std::string esk_;
80 // Owned by DBusThreadManager
81 chromeos::EasyUnlockClient* easy_unlock_client_;
83 base::WeakPtrFactory<ChallengeCreator> weak_ptr_factory_;
85 DISALLOW_COPY_AND_ASSIGN(ChallengeCreator);
88 EasyUnlockCreateKeysOperation::ChallengeCreator::ChallengeCreator(
89 const std::string& user_key,
90 const std::string& session_key,
91 const std::string& tpm_pub_key,
92 EasyUnlockDeviceKeyData* device,
93 const ChallengeCreatedCallback& callback)
94 : user_key_(user_key),
95 session_key_(session_key),
96 tpm_pub_key_(tpm_pub_key),
97 device_(device),
98 callback_(callback),
99 easy_unlock_client_(
100 chromeos::DBusThreadManager::Get()->GetEasyUnlockClient()),
101 weak_ptr_factory_(this) {
104 EasyUnlockCreateKeysOperation::ChallengeCreator::~ChallengeCreator() {
107 void EasyUnlockCreateKeysOperation::ChallengeCreator::Start() {
108 easy_unlock_client_->GenerateEcP256KeyPair(
109 base::Bind(&ChallengeCreator::OnEcKeyPairGenerated,
110 weak_ptr_factory_.GetWeakPtr()));
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 LOG(ERROR) << "Easy unlock failed to generate ec key pair.";
118 callback_.Run(false);
119 return;
122 std::string device_pub_key;
123 if (!proximity_auth::Base64UrlDecode(device_->public_key, &device_pub_key)) {
124 LOG(ERROR) << "Easy unlock failed to decode device public key.";
125 callback_.Run(false);
126 return;
129 ec_public_key_ = ec_public_key;
130 easy_unlock_client_->PerformECDHKeyAgreement(
131 ec_private_key,
132 device_pub_key,
133 base::Bind(&ChallengeCreator::OnEskGenerated,
134 weak_ptr_factory_.GetWeakPtr()));
137 void EasyUnlockCreateKeysOperation::ChallengeCreator::OnEskGenerated(
138 const std::string& esk) {
139 if (esk.empty()) {
140 LOG(ERROR) << "Easy unlock failed to generate challenge esk.";
141 callback_.Run(false);
142 return;
145 esk_ = esk;
146 WrapTPMPublicKey();
149 void EasyUnlockCreateKeysOperation::ChallengeCreator::WrapTPMPublicKey() {
150 easy_unlock_client_->WrapPublicKey(
151 easy_unlock::kKeyAlgorithmRSA,
152 tpm_pub_key_,
153 base::Bind(&ChallengeCreator::OnTPMPublicKeyWrapped,
154 weak_ptr_factory_.GetWeakPtr()));
157 void EasyUnlockCreateKeysOperation::ChallengeCreator::OnTPMPublicKeyWrapped(
158 const std::string& wrapped_key) {
159 if (wrapped_key.empty()) {
160 LOG(ERROR) << "Unable to wrap RSA key";
161 callback_.Run(false);
162 return;
164 wrapped_tpm_pub_key_ = wrapped_key;
165 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;
176 easy_unlock_client_->CreateSecureMessage(
177 session_key_,
178 options,
179 base::Bind(&ChallengeCreator::OnPayloadMessageGenerated,
180 weak_ptr_factory_.GetWeakPtr()));
183 void
184 EasyUnlockCreateKeysOperation::ChallengeCreator::OnPayloadMessageGenerated(
185 const std::string& payload_message) {
186 EasyUnlockClient::UnwrapSecureMessageOptions options;
187 options.key = esk_;
188 options.encryption_type = easy_unlock::kEncryptionTypeAES256CBC;
189 options.signature_type = easy_unlock::kSignatureTypeHMACSHA256;
191 easy_unlock_client_->UnwrapSecureMessage(
192 payload_message,
193 options,
194 base::Bind(&ChallengeCreator::OnPayloadGenerated,
195 weak_ptr_factory_.GetWeakPtr()));
198 void EasyUnlockCreateKeysOperation::ChallengeCreator::OnPayloadGenerated(
199 const std::string& payload) {
200 if (payload.empty()) {
201 LOG(ERROR) << "Easy unlock failed to generate challenge payload.";
202 callback_.Run(false);
203 return;
206 EasyUnlockClient::CreateSecureMessageOptions options;
207 options.key = esk_;
208 options.decryption_key_id = ec_public_key_;
209 options.encryption_type = easy_unlock::kEncryptionTypeAES256CBC;
210 options.signature_type = easy_unlock::kSignatureTypeHMACSHA256;
212 easy_unlock_client_->CreateSecureMessage(
213 payload,
214 options,
215 base::Bind(&ChallengeCreator::OnChallengeGenerated,
216 weak_ptr_factory_.GetWeakPtr()));
219 void EasyUnlockCreateKeysOperation::ChallengeCreator::OnChallengeGenerated(
220 const std::string& challenge) {
221 if (challenge.empty()) {
222 LOG(ERROR) << "Easy unlock failed to generate challenge.";
223 callback_.Run(false);
224 return;
227 device_->challenge = challenge;
228 callback_.Run(true);
231 /////////////////////////////////////////////////////////////////////////////
232 // EasyUnlockCreateKeysOperation
234 EasyUnlockCreateKeysOperation::EasyUnlockCreateKeysOperation(
235 const UserContext& user_context,
236 const std::string& tpm_public_key,
237 const EasyUnlockDeviceKeyDataList& devices,
238 const CreateKeysCallback& callback)
239 : user_context_(user_context),
240 tpm_public_key_(tpm_public_key),
241 devices_(devices),
242 callback_(callback),
243 key_creation_index_(0),
244 weak_ptr_factory_(this) {
245 // Must have the secret and callback.
246 DCHECK(!user_context_.GetKey()->GetSecret().empty());
247 DCHECK(!callback_.is_null());
250 EasyUnlockCreateKeysOperation::~EasyUnlockCreateKeysOperation() {
253 void EasyUnlockCreateKeysOperation::Start() {
254 key_creation_index_ = 0;
255 CreateKeyForDeviceAtIndex(key_creation_index_);
258 void EasyUnlockCreateKeysOperation::CreateKeyForDeviceAtIndex(size_t index) {
259 DCHECK_GE(index, 0u);
260 if (index == devices_.size()) {
261 callback_.Run(true);
262 return;
265 std::string user_key;
266 crypto::RandBytes(WriteInto(&user_key, kUserKeyByteSize + 1),
267 kUserKeyByteSize);
269 scoped_ptr<crypto::SymmetricKey> session_key(
270 crypto::SymmetricKey::GenerateRandomKey(crypto::SymmetricKey::AES,
271 kSessionKeyByteSize * 8));
273 std::string iv(kSessionKeyByteSize, ' ');
274 crypto::Encryptor encryptor;
275 if (!encryptor.Init(session_key.get(), crypto::Encryptor::CBC, iv)) {
276 LOG(ERROR) << "Easy unlock failed to init encryptor for key creation.";
277 callback_.Run(false);
278 return;
281 EasyUnlockDeviceKeyData* device = &devices_[index];
282 if (!encryptor.Encrypt(user_key, &device->wrapped_secret)) {
283 LOG(ERROR) << "Easy unlock failed to encrypt user key for key creation.";
284 callback_.Run(false);
285 return;
288 std::string raw_session_key;
289 session_key->GetRawKey(&raw_session_key);
291 challenge_creator_.reset(new ChallengeCreator(
292 user_key,
293 raw_session_key,
294 tpm_public_key_,
295 device,
296 base::Bind(&EasyUnlockCreateKeysOperation::OnChallengeCreated,
297 weak_ptr_factory_.GetWeakPtr(),
298 index)));
299 challenge_creator_->Start();
302 void EasyUnlockCreateKeysOperation::OnChallengeCreated(size_t index,
303 bool success) {
304 DCHECK_EQ(key_creation_index_, index);
306 if (!success) {
307 LOG(ERROR) << "Easy unlock failed to create challenge for key creation.";
308 callback_.Run(false);
309 return;
312 SystemSaltGetter::Get()->GetSystemSalt(
313 base::Bind(&EasyUnlockCreateKeysOperation::OnGetSystemSalt,
314 weak_ptr_factory_.GetWeakPtr(),
315 index));
318 void EasyUnlockCreateKeysOperation::OnGetSystemSalt(
319 size_t index,
320 const std::string& system_salt) {
321 DCHECK_EQ(key_creation_index_, index);
322 if (system_salt.empty()) {
323 LOG(ERROR) << "Easy unlock failed to get system salt for key creation.";
324 callback_.Run(false);
325 return;
328 Key user_key(challenge_creator_->user_key());
329 user_key.Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF, system_salt);
331 EasyUnlockDeviceKeyData* device = &devices_[index];
332 cryptohome::KeyDefinition key_def(
333 user_key.GetSecret(),
334 EasyUnlockKeyManager::GetKeyLabel(index),
335 kEasyUnlockKeyPrivileges);
336 key_def.revision = kEasyUnlockKeyRevision;
337 key_def.provider_data.push_back(cryptohome::KeyDefinition::ProviderData(
338 kEasyUnlockKeyMetaNameBluetoothAddress, device->bluetooth_address));
339 key_def.provider_data.push_back(cryptohome::KeyDefinition::ProviderData(
340 kEasyUnlockKeyMetaNamePsk, device->psk));
341 key_def.provider_data.push_back(cryptohome::KeyDefinition::ProviderData(
342 kEasyUnlockKeyMetaNamePubKey, device->public_key));
343 key_def.provider_data.push_back(cryptohome::KeyDefinition::ProviderData(
344 kEasyUnlockKeyMetaNameChallenge, device->challenge));
345 key_def.provider_data.push_back(cryptohome::KeyDefinition::ProviderData(
346 kEasyUnlockKeyMetaNameWrappedSecret, device->wrapped_secret));
348 // Add cryptohome key.
349 std::string canonicalized =
350 gaia::CanonicalizeEmail(user_context_.GetUserID());
351 cryptohome::Identification id(canonicalized);
353 scoped_ptr<Key> auth_key(new Key(*user_context_.GetKey()));
354 if (auth_key->GetKeyType() == Key::KEY_TYPE_PASSWORD_PLAIN)
355 auth_key->Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF, system_salt);
357 cryptohome::Authorization auth(auth_key->GetSecret(), auth_key->GetLabel());
358 cryptohome::HomedirMethods::GetInstance()->AddKeyEx(
360 auth,
361 key_def,
362 true, // clobber
363 base::Bind(&EasyUnlockCreateKeysOperation::OnKeyCreated,
364 weak_ptr_factory_.GetWeakPtr(),
365 index,
366 user_key));
369 void EasyUnlockCreateKeysOperation::OnKeyCreated(
370 size_t index,
371 const Key& user_key,
372 bool success,
373 cryptohome::MountError return_code) {
374 DCHECK_EQ(key_creation_index_, index);
376 if (!success) {
377 LOG(ERROR) << "Easy unlock failed to create key, code=" << return_code;
378 callback_.Run(false);
379 return;
382 // If the key associated with the current context changed (i.e. in the case
383 // the current signin flow was Easy signin), update the user context.
384 if (user_context_.GetAuthFlow() == UserContext::AUTH_FLOW_EASY_UNLOCK &&
385 user_context_.GetKey()->GetLabel() ==
386 EasyUnlockKeyManager::GetKeyLabel(key_creation_index_)) {
387 user_context_.SetKey(user_key);
390 ++key_creation_index_;
391 CreateKeyForDeviceAtIndex(key_creation_index_);
394 } // namespace chromeos