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