Home | History | Annotate | Download | only in easy_unlock
      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_key_manager.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/logging.h"
      9 #include "base/stl_util.h"
     10 #include "base/strings/stringprintf.h"
     11 #include "base/values.h"
     12 
     13 namespace chromeos {
     14 
     15 namespace {
     16 
     17 const char kKeyBluetoothAddress[] = "bluetoothAddress";
     18 const char kKeyPermitRecord[] = "permitRecord";
     19 const char kKeyPermitId[] = "permitRecord.id";
     20 const char kKeyPermitPermitId[] = "permitRecord.permitId";
     21 const char kKeyPermitData[] = "permitRecord.data";
     22 const char kKeyPermitType[] = "permitRecord.type";
     23 const char kKeyPsk[] = "psk";
     24 
     25 const char kKeyLabelPrefix[] = "easy-unlock-";
     26 
     27 const char kPermitPermitIdFormat[] = "permit://google.com/easyunlock/v1/%s";
     28 const char kPermitTypeLicence[] = "licence";
     29 
     30 }  // namespace
     31 
     32 EasyUnlockKeyManager::EasyUnlockKeyManager()
     33     : operation_id_(0),
     34       weak_ptr_factory_(this) {
     35 }
     36 
     37 EasyUnlockKeyManager::~EasyUnlockKeyManager() {
     38   STLDeleteContainerPairSecondPointers(get_keys_ops_.begin(),
     39                                        get_keys_ops_.end());
     40 }
     41 
     42 void EasyUnlockKeyManager::RefreshKeys(const UserContext& user_context,
     43                                        const base::ListValue& remote_devices,
     44                                        const RefreshKeysCallback& callback) {
     45   // Must have the secret.
     46   DCHECK(!user_context.GetKey()->GetSecret().empty());
     47 
     48   EasyUnlockDeviceKeyDataList devices;
     49   if (!RemoteDeviceListToDeviceDataList(remote_devices, &devices))
     50     devices.clear();
     51 
     52   // Only one pending request.
     53   DCHECK(!HasPendingOperations());
     54   create_keys_op_.reset(new EasyUnlockCreateKeysOperation(
     55       user_context,
     56       devices,
     57       base::Bind(&EasyUnlockKeyManager::OnKeysCreated,
     58                  weak_ptr_factory_.GetWeakPtr(),
     59                  devices.size(),
     60                  callback)));
     61   create_keys_op_->Start();
     62 }
     63 
     64 void EasyUnlockKeyManager::RemoveKeys(const UserContext& user_context,
     65                                       size_t start_index,
     66                                       const RemoveKeysCallback& callback) {
     67   // Must have the secret.
     68   DCHECK(!user_context.GetKey()->GetSecret().empty());
     69   // Only one pending request.
     70   DCHECK(!HasPendingOperations());
     71 
     72   remove_keys_op_.reset(
     73       new EasyUnlockRemoveKeysOperation(
     74           user_context,
     75           start_index,
     76           base::Bind(&EasyUnlockKeyManager::OnKeysRemoved,
     77                      weak_ptr_factory_.GetWeakPtr(),
     78                      callback)));
     79   remove_keys_op_->Start();
     80 }
     81 
     82 void EasyUnlockKeyManager::GetDeviceDataList(
     83     const UserContext& user_context,
     84     const GetDeviceDataListCallback& callback) {
     85   // Defer the get operation if there is pending write operations.
     86   if (create_keys_op_ || remove_keys_op_) {
     87     pending_ops_.push_back(base::Bind(&EasyUnlockKeyManager::GetDeviceDataList,
     88                                       weak_ptr_factory_.GetWeakPtr(),
     89                                       user_context,
     90                                       callback));
     91     return;
     92   }
     93 
     94   const int op_id = GetNextOperationId();
     95   scoped_ptr<EasyUnlockGetKeysOperation> op(new EasyUnlockGetKeysOperation(
     96       user_context,
     97       base::Bind(&EasyUnlockKeyManager::OnKeysFetched,
     98                  weak_ptr_factory_.GetWeakPtr(),
     99                  op_id,
    100                  callback)));
    101   op->Start();
    102   get_keys_ops_[op_id] = op.release();
    103 }
    104 
    105 // static
    106 void EasyUnlockKeyManager::DeviceDataToRemoteDeviceDictionary(
    107     const std::string& user_id,
    108     const EasyUnlockDeviceKeyData& data,
    109     base::DictionaryValue* dict) {
    110   dict->SetString(kKeyBluetoothAddress, data.bluetooth_address);
    111   dict->SetString(kKeyPsk, data.psk);
    112   scoped_ptr<base::DictionaryValue> permit_record(new base::DictionaryValue);
    113   dict->Set(kKeyPermitRecord, permit_record.release());
    114   dict->SetString(kKeyPermitId, data.public_key);
    115   dict->SetString(kKeyPermitData, data.public_key);
    116   dict->SetString(kKeyPermitType, kPermitTypeLicence);
    117   dict->SetString(kKeyPermitPermitId,
    118                   base::StringPrintf(kPermitPermitIdFormat,
    119                                      user_id.c_str()));
    120 }
    121 
    122 // static
    123 bool EasyUnlockKeyManager::RemoteDeviceDictionaryToDeviceData(
    124     const base::DictionaryValue& dict,
    125     EasyUnlockDeviceKeyData* data) {
    126   std::string bluetooth_address;
    127   std::string public_key;
    128   std::string psk;
    129 
    130   if (!dict.GetString(kKeyBluetoothAddress, &bluetooth_address) ||
    131       !dict.GetString(kKeyPermitId, &public_key) ||
    132       !dict.GetString(kKeyPsk, &psk)) {
    133     return false;
    134   }
    135 
    136   data->bluetooth_address.swap(bluetooth_address);
    137   data->public_key.swap(public_key);
    138   data->psk.swap(psk);
    139   return true;
    140 }
    141 
    142 // static
    143 void EasyUnlockKeyManager::DeviceDataListToRemoteDeviceList(
    144     const std::string& user_id,
    145     const EasyUnlockDeviceKeyDataList& data_list,
    146     base::ListValue* device_list) {
    147   device_list->Clear();
    148   for (size_t i = 0; i < data_list.size(); ++i) {
    149     scoped_ptr<base::DictionaryValue> device_dict(new base::DictionaryValue);
    150     DeviceDataToRemoteDeviceDictionary(
    151         user_id, data_list[i], device_dict.get());
    152     device_list->Append(device_dict.release());
    153   }
    154 }
    155 
    156 // static
    157 bool EasyUnlockKeyManager::RemoteDeviceListToDeviceDataList(
    158     const base::ListValue& device_list,
    159     EasyUnlockDeviceKeyDataList* data_list) {
    160   EasyUnlockDeviceKeyDataList parsed_devices;
    161   for (base::ListValue::const_iterator it = device_list.begin();
    162        it != device_list.end();
    163        ++it) {
    164     const base::DictionaryValue* dict;
    165     if (!(*it)->GetAsDictionary(&dict) || !dict)
    166       return false;
    167 
    168     EasyUnlockDeviceKeyData data;
    169     if (!RemoteDeviceDictionaryToDeviceData(*dict, &data))
    170       return false;
    171 
    172     parsed_devices.push_back(data);
    173   }
    174 
    175   data_list->swap(parsed_devices);
    176   return true;
    177 }
    178 
    179 // static
    180 std::string EasyUnlockKeyManager::GetKeyLabel(size_t key_index) {
    181   return base::StringPrintf("%s%zu", kKeyLabelPrefix, key_index);
    182 }
    183 
    184 bool EasyUnlockKeyManager::HasPendingOperations() const {
    185   return create_keys_op_ || remove_keys_op_ || !get_keys_ops_.empty();
    186 }
    187 
    188 int EasyUnlockKeyManager::GetNextOperationId() {
    189   return ++operation_id_;
    190 }
    191 
    192 void EasyUnlockKeyManager::RunNextPendingOp() {
    193   if (pending_ops_.empty())
    194     return;
    195 
    196   pending_ops_.front().Run();
    197   pending_ops_.pop_front();
    198 }
    199 
    200 void EasyUnlockKeyManager::OnKeysCreated(
    201     size_t remove_start_index,
    202     const RefreshKeysCallback& callback,
    203     bool create_success) {
    204   scoped_ptr<EasyUnlockCreateKeysOperation> op = create_keys_op_.Pass();
    205   if (!callback.is_null())
    206     callback.Run(create_success);
    207 
    208   // Remove extra existing keys.
    209   RemoveKeys(op->user_context(), remove_start_index, RemoveKeysCallback());
    210 }
    211 
    212 void EasyUnlockKeyManager::OnKeysRemoved(const RemoveKeysCallback& callback,
    213                                          bool remove_success) {
    214   scoped_ptr<EasyUnlockRemoveKeysOperation> op = remove_keys_op_.Pass();
    215   if (!callback.is_null())
    216     callback.Run(remove_success);
    217 
    218   if (!HasPendingOperations())
    219     RunNextPendingOp();
    220 }
    221 
    222 void EasyUnlockKeyManager::OnKeysFetched(
    223     int op_id,
    224     const GetDeviceDataListCallback& callback,
    225     bool fetch_success,
    226     const EasyUnlockDeviceKeyDataList& fetched_data) {
    227   std::map<int, EasyUnlockGetKeysOperation*>::iterator it =
    228       get_keys_ops_.find(op_id);
    229   scoped_ptr<EasyUnlockGetKeysOperation> op;
    230   if (it != get_keys_ops_.end()) {
    231     op.reset(it->second);
    232     get_keys_ops_.erase(it);
    233   } else {
    234     NOTREACHED();
    235   }
    236 
    237   if (!callback.is_null())
    238     callback.Run(fetch_success, fetched_data);
    239 
    240   if (!HasPendingOperations())
    241     RunNextPendingOp();
    242 }
    243 
    244 }  // namespace chromeos
    245