1 // Copyright 2015 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 "components/gcm_driver/instance_id/instance_id_impl.h"
6 
7 #include <stdint.h>
8 
9 #include <algorithm>
10 #include <memory>
11 
12 #include "base/base64.h"
13 #include "base/bind.h"
14 #include "base/logging.h"
15 #include "base/metrics/histogram_macros.h"
16 #include "base/stl_util.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/threading/thread_task_runner_handle.h"
19 #include "components/gcm_driver/gcm_driver.h"
20 #include "crypto/random.h"
21 
22 namespace instance_id {
23 
24 namespace {
25 
GCMClientResultToInstanceIDResult(gcm::GCMClient::Result result)26 InstanceID::Result GCMClientResultToInstanceIDResult(
27     gcm::GCMClient::Result result) {
28   switch (result) {
29     case gcm::GCMClient::SUCCESS:
30       return InstanceID::SUCCESS;
31     case gcm::GCMClient::INVALID_PARAMETER:
32       return InstanceID::INVALID_PARAMETER;
33     case gcm::GCMClient::GCM_DISABLED:
34       return InstanceID::DISABLED;
35     case gcm::GCMClient::ASYNC_OPERATION_PENDING:
36       return InstanceID::ASYNC_OPERATION_PENDING;
37     case gcm::GCMClient::NETWORK_ERROR:
38       return InstanceID::NETWORK_ERROR;
39     case gcm::GCMClient::SERVER_ERROR:
40       return InstanceID::SERVER_ERROR;
41     case gcm::GCMClient::UNKNOWN_ERROR:
42       return InstanceID::UNKNOWN_ERROR;
43     case gcm::GCMClient::TTL_EXCEEDED:
44       NOTREACHED();
45       break;
46   }
47   return InstanceID::UNKNOWN_ERROR;
48 }
49 
50 }  // namespace
51 
52 // static
CreateInternal(const std::string & app_id,gcm::GCMDriver * gcm_driver)53 std::unique_ptr<InstanceID> InstanceID::CreateInternal(
54     const std::string& app_id,
55     gcm::GCMDriver* gcm_driver) {
56   return std::make_unique<InstanceIDImpl>(app_id, gcm_driver);
57 }
58 
InstanceIDImpl(const std::string & app_id,gcm::GCMDriver * gcm_driver)59 InstanceIDImpl::InstanceIDImpl(const std::string& app_id,
60                                gcm::GCMDriver* gcm_driver)
61     : InstanceID(app_id, gcm_driver) {
62   Handler()->GetInstanceIDData(
63       app_id, base::BindOnce(&InstanceIDImpl::GetInstanceIDDataCompleted,
64                              weak_ptr_factory_.GetWeakPtr()));
65 }
66 
~InstanceIDImpl()67 InstanceIDImpl::~InstanceIDImpl() {
68 }
69 
GetID(GetIDCallback callback)70 void InstanceIDImpl::GetID(GetIDCallback callback) {
71   RunWhenReady(base::BindOnce(&InstanceIDImpl::DoGetID,
72                               weak_ptr_factory_.GetWeakPtr(),
73                               std::move(callback)));
74 }
75 
DoGetID(GetIDCallback callback)76 void InstanceIDImpl::DoGetID(GetIDCallback callback) {
77   EnsureIDGenerated();
78   std::move(callback).Run(id_);
79 }
80 
GetCreationTime(GetCreationTimeCallback callback)81 void InstanceIDImpl::GetCreationTime(GetCreationTimeCallback callback) {
82   RunWhenReady(base::BindOnce(&InstanceIDImpl::DoGetCreationTime,
83                               weak_ptr_factory_.GetWeakPtr(),
84                               std::move(callback)));
85 }
86 
DoGetCreationTime(GetCreationTimeCallback callback)87 void InstanceIDImpl::DoGetCreationTime(GetCreationTimeCallback callback) {
88   std::move(callback).Run(creation_time_);
89 }
90 
GetToken(const std::string & authorized_entity,const std::string & scope,base::TimeDelta time_to_live,const std::map<std::string,std::string> & options,std::set<Flags> flags,GetTokenCallback callback)91 void InstanceIDImpl::GetToken(const std::string& authorized_entity,
92                               const std::string& scope,
93                               base::TimeDelta time_to_live,
94                               const std::map<std::string, std::string>& options,
95                               std::set<Flags> flags,
96                               GetTokenCallback callback) {
97   DCHECK(!authorized_entity.empty());
98   DCHECK(!scope.empty());
99 
100   UMA_HISTOGRAM_COUNTS_100("InstanceID.GetToken.OptionsCount", options.size());
101 
102   RunWhenReady(base::BindOnce(
103       &InstanceIDImpl::DoGetToken, weak_ptr_factory_.GetWeakPtr(),
104       authorized_entity, scope, time_to_live, options, std::move(callback)));
105 }
106 
DoGetToken(const std::string & authorized_entity,const std::string & scope,base::TimeDelta time_to_live,const std::map<std::string,std::string> & options,GetTokenCallback callback)107 void InstanceIDImpl::DoGetToken(
108     const std::string& authorized_entity,
109     const std::string& scope,
110     base::TimeDelta time_to_live,
111     const std::map<std::string, std::string>& options,
112     GetTokenCallback callback) {
113   EnsureIDGenerated();
114 
115   Handler()->GetToken(
116       app_id(), authorized_entity, scope, time_to_live, options,
117       base::BindOnce(&InstanceIDImpl::OnGetTokenCompleted,
118                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
119 }
120 
ValidateToken(const std::string & authorized_entity,const std::string & scope,const std::string & token,ValidateTokenCallback callback)121 void InstanceIDImpl::ValidateToken(const std::string& authorized_entity,
122                                    const std::string& scope,
123                                    const std::string& token,
124                                    ValidateTokenCallback callback) {
125   DCHECK(!authorized_entity.empty());
126   DCHECK(!scope.empty());
127   DCHECK(!token.empty());
128 
129   RunWhenReady(base::BindOnce(&InstanceIDImpl::DoValidateToken,
130                               weak_ptr_factory_.GetWeakPtr(), authorized_entity,
131                               scope, token, std::move(callback)));
132 }
133 
DoValidateToken(const std::string & authorized_entity,const std::string & scope,const std::string & token,ValidateTokenCallback callback)134 void InstanceIDImpl::DoValidateToken(const std::string& authorized_entity,
135                                      const std::string& scope,
136                                      const std::string& token,
137                                      ValidateTokenCallback callback) {
138   if (id_.empty()) {
139     std::move(callback).Run(false /* is_valid */);
140     return;
141   }
142 
143   Handler()->ValidateToken(app_id(), authorized_entity, scope, token,
144                            std::move(callback));
145 }
146 
DeleteTokenImpl(const std::string & authorized_entity,const std::string & scope,DeleteTokenCallback callback)147 void InstanceIDImpl::DeleteTokenImpl(const std::string& authorized_entity,
148                                      const std::string& scope,
149                                      DeleteTokenCallback callback) {
150   DCHECK(!authorized_entity.empty());
151   DCHECK(!scope.empty());
152 
153   RunWhenReady(base::BindOnce(&InstanceIDImpl::DoDeleteToken,
154                               weak_ptr_factory_.GetWeakPtr(), authorized_entity,
155                               scope, std::move(callback)));
156 }
157 
DoDeleteToken(const std::string & authorized_entity,const std::string & scope,DeleteTokenCallback callback)158 void InstanceIDImpl::DoDeleteToken(const std::string& authorized_entity,
159                                    const std::string& scope,
160                                    DeleteTokenCallback callback) {
161   // Nothing to delete if the ID has not been generated.
162   if (id_.empty()) {
163     std::move(callback).Run(InstanceID::INVALID_PARAMETER);
164     return;
165   }
166 
167   Handler()->DeleteToken(
168       app_id(), authorized_entity, scope,
169       base::BindOnce(&InstanceIDImpl::OnDeleteTokenCompleted,
170                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
171 }
172 
DeleteIDImpl(DeleteIDCallback callback)173 void InstanceIDImpl::DeleteIDImpl(DeleteIDCallback callback) {
174   RunWhenReady(base::BindOnce(&InstanceIDImpl::DoDeleteID,
175                               weak_ptr_factory_.GetWeakPtr(),
176                               std::move(callback)));
177 }
178 
DoDeleteID(DeleteIDCallback callback)179 void InstanceIDImpl::DoDeleteID(DeleteIDCallback callback) {
180   // Nothing to do if ID has not been generated.
181   if (id_.empty()) {
182     std::move(callback).Run(InstanceID::SUCCESS);
183     return;
184   }
185 
186   Handler()->DeleteAllTokensForApp(
187       app_id(),
188       base::BindOnce(&InstanceIDImpl::OnDeleteIDCompleted,
189                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
190 
191   Handler()->RemoveInstanceIDData(app_id());
192 
193   id_.clear();
194   creation_time_ = base::Time();
195 }
196 
OnGetTokenCompleted(GetTokenCallback callback,const std::string & token,gcm::GCMClient::Result result)197 void InstanceIDImpl::OnGetTokenCompleted(GetTokenCallback callback,
198                                          const std::string& token,
199                                          gcm::GCMClient::Result result) {
200   std::move(callback).Run(token, GCMClientResultToInstanceIDResult(result));
201 }
202 
OnDeleteTokenCompleted(DeleteTokenCallback callback,gcm::GCMClient::Result result)203 void InstanceIDImpl::OnDeleteTokenCompleted(DeleteTokenCallback callback,
204                                             gcm::GCMClient::Result result) {
205   std::move(callback).Run(GCMClientResultToInstanceIDResult(result));
206 }
207 
OnDeleteIDCompleted(DeleteIDCallback callback,gcm::GCMClient::Result result)208 void InstanceIDImpl::OnDeleteIDCompleted(DeleteIDCallback callback,
209                                          gcm::GCMClient::Result result) {
210   std::move(callback).Run(GCMClientResultToInstanceIDResult(result));
211 }
212 
GetInstanceIDDataCompleted(const std::string & instance_id,const std::string & extra_data)213 void InstanceIDImpl::GetInstanceIDDataCompleted(
214     const std::string& instance_id,
215     const std::string& extra_data) {
216   id_ = instance_id;
217 
218   if (extra_data.empty()) {
219     creation_time_ = base::Time();
220   } else {
221     int64_t time_internal = 0LL;
222     if (!base::StringToInt64(extra_data, &time_internal)) {
223       DVLOG(1) << "Failed to parse the time data: " + extra_data;
224       return;
225     }
226     creation_time_ = base::Time::FromInternalValue(time_internal);
227   }
228 
229   delayed_task_controller_.SetReady();
230 }
231 
EnsureIDGenerated()232 void InstanceIDImpl::EnsureIDGenerated() {
233   if (!id_.empty())
234     return;
235 
236   // Now produce the ID in the following steps:
237 
238   // 1) Generates the random number in 8 bytes which is required by the server.
239   //    We don't want to be strictly cryptographically secure. The server might
240   //    reject the ID if there is a conflict or problem.
241   uint8_t bytes[kInstanceIDByteLength];
242   crypto::RandBytes(bytes, sizeof(bytes));
243 
244   // 2) Transforms the first 4 bits to 0x7. Note that this is required by the
245   //    server.
246   bytes[0] &= 0x0f;
247   bytes[0] |= 0x70;
248 
249   // 3) Encode the value in Android-compatible base64 scheme:
250   //    * URL safe: '/' replaced by '_' and '+' replaced by '-'.
251   //    * No padding: any trailing '=' will be removed.
252   base::Base64Encode(
253       base::StringPiece(reinterpret_cast<const char*>(bytes), sizeof(bytes)),
254       &id_);
255   std::replace(id_.begin(), id_.end(), '+', '-');
256   std::replace(id_.begin(), id_.end(), '/', '_');
257   base::Erase(id_, '=');
258 
259   creation_time_ = base::Time::Now();
260 
261   // Save to the persistent store.
262   Handler()->AddInstanceIDData(
263       app_id(), id_, base::NumberToString(creation_time_.ToInternalValue()));
264 }
265 
Handler()266 gcm::InstanceIDHandler* InstanceIDImpl::Handler() {
267   gcm::InstanceIDHandler* handler =
268       gcm_driver()->GetInstanceIDHandlerInternal();
269   DCHECK(handler);
270   return handler;
271 }
272 
RunWhenReady(base::OnceClosure task)273 void InstanceIDImpl::RunWhenReady(base::OnceClosure task) {
274   if (!delayed_task_controller_.CanRunTaskWithoutDelay())
275     delayed_task_controller_.AddTask(std::move(task));
276   else
277     base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(task));
278 }
279 
280 }  // namespace instance_id
281