Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(219)

Side by Side Diff: components/gcm_driver/crypto/gcm_key_store.cc

Issue 1953273002: Add support to GCMKeyStore for multiple keys per app_id (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@iid6fixstore
Patch Set: Simplify Decrypt fallback by banning IID token & GCM reg from sharing same app_id Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/gcm_driver/crypto/gcm_key_store.h" 5 #include "components/gcm_driver/crypto/gcm_key_store.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 8
9 #include <utility> 9 #include <utility>
10 10
11 #include "base/bind_helpers.h"
12 #include "base/callback.h"
11 #include "base/logging.h" 13 #include "base/logging.h"
12 #include "base/metrics/histogram_macros.h" 14 #include "base/metrics/histogram_macros.h"
13 #include "components/gcm_driver/crypto/p256_key_util.h" 15 #include "components/gcm_driver/crypto/p256_key_util.h"
14 #include "components/leveldb_proto/proto_database_impl.h" 16 #include "components/leveldb_proto/proto_database_impl.h"
15 #include "crypto/random.h" 17 #include "crypto/random.h"
16 18
17 namespace gcm { 19 namespace gcm {
18 20
21 namespace {
22
23 std::string DatabaseKey(const std::string& app_id,
24 const std::string& instance_id_authorized_entity) {
25 DCHECK_EQ(std::string::npos, app_id.find(','));
26 DCHECK_EQ(std::string::npos, instance_id_authorized_entity.find(','));
27 DCHECK_NE("*", instance_id_authorized_entity) << "Don't store wildcards";
28 return instance_id_authorized_entity.empty()
29 ? app_id // No comma, for compatibility with existing keys.
30 : app_id + ',' + instance_id_authorized_entity;
31 }
32
33 } // namespace
34
19 // Statistics are logged to UMA with this string as part of histogram name. They 35 // Statistics are logged to UMA with this string as part of histogram name. They
20 // can all be found under LevelDB.*.GCMKeyStore. Changing this needs to 36 // can all be found under LevelDB.*.GCMKeyStore. Changing this needs to
21 // synchronize with histograms.xml, AND will also become incompatible with older 37 // synchronize with histograms.xml, AND will also become incompatible with older
22 // browsers still reporting the previous values. 38 // browsers still reporting the previous values.
23 const char kDatabaseUMAClientName[] = "GCMKeyStore"; 39 const char kDatabaseUMAClientName[] = "GCMKeyStore";
Peter Beverloo 2016/05/09 14:10:10 Please move this (and kAuthSecretBytes) to the top
johnme 2016/05/09 18:15:55 Done.
24 40
25 // Number of cryptographically secure random bytes to generate as a key pair's 41 // Number of cryptographically secure random bytes to generate as a key pair's
26 // authentication secret. Must be at least 16 bytes. 42 // authentication secret. Must be at least 16 bytes.
27 const size_t kAuthSecretBytes = 16; 43 const size_t kAuthSecretBytes = 16;
28 44
29 enum class GCMKeyStore::State { 45 enum class GCMKeyStore::State {
30 UNINITIALIZED, 46 UNINITIALIZED,
31 INITIALIZING, 47 INITIALIZING,
32 INITIALIZED, 48 INITIALIZED,
33 FAILED 49 FAILED
34 }; 50 };
35 51
36 GCMKeyStore::GCMKeyStore( 52 GCMKeyStore::GCMKeyStore(
37 const base::FilePath& key_store_path, 53 const base::FilePath& key_store_path,
38 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner) 54 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner)
39 : key_store_path_(key_store_path), 55 : key_store_path_(key_store_path),
40 blocking_task_runner_(blocking_task_runner), 56 blocking_task_runner_(blocking_task_runner),
41 state_(State::UNINITIALIZED), 57 state_(State::UNINITIALIZED),
42 weak_factory_(this) { 58 weak_factory_(this) {
43 DCHECK(blocking_task_runner); 59 DCHECK(blocking_task_runner);
44 } 60 }
45 61
46 GCMKeyStore::~GCMKeyStore() {} 62 GCMKeyStore::~GCMKeyStore() {}
47 63
48 void GCMKeyStore::GetKeys(const std::string& app_id, 64 void GCMKeyStore::GetKeys(const std::string& app_id,
65 const std::string& instance_id_authorized_entity,
66 bool fallback_to_empty_authorized_entity,
49 const KeysCallback& callback) { 67 const KeysCallback& callback) {
50 LazyInitialize(base::Bind(&GCMKeyStore::GetKeysAfterInitialize, 68 LazyInitialize(base::Bind(&GCMKeyStore::GetKeysAfterInitialize,
51 weak_factory_.GetWeakPtr(), app_id, callback)); 69 weak_factory_.GetWeakPtr(), app_id,
70 instance_id_authorized_entity,
71 fallback_to_empty_authorized_entity, callback));
52 } 72 }
53 73
54 void GCMKeyStore::GetKeysAfterInitialize(const std::string& app_id, 74 void GCMKeyStore::GetKeysAfterInitialize(
55 const KeysCallback& callback) { 75 const std::string& app_id,
76 const std::string& instance_id_authorized_entity,
77 bool fallback_to_empty_authorized_entity,
78 const KeysCallback& callback) {
56 DCHECK(state_ == State::INITIALIZED || state_ == State::FAILED); 79 DCHECK(state_ == State::INITIALIZED || state_ == State::FAILED);
57 const auto& iter = key_pairs_.find(app_id); 80 bool success = false;
58 81
59 const bool success = state_ == State::INITIALIZED && iter != key_pairs_.end(); 82 if (state_ == State::INITIALIZED) {
60 UMA_HISTOGRAM_BOOLEAN("GCM.Crypto.GetKeySuccessRate", success); 83 auto outer_iter = key_data_.find(app_id);
61 84 if (outer_iter != key_data_.end()) {
62 if (!success) { 85 const auto& inner_map = outer_iter->second;
63 callback.Run(KeyPair(), std::string() /* auth_secret */); 86 auto inner_iter = inner_map.find(instance_id_authorized_entity);
64 return; 87 if (fallback_to_empty_authorized_entity && inner_iter == inner_map.end())
88 inner_iter = inner_map.find(std::string());
89 if (inner_iter != inner_map.end()) {
90 const KeyPairAndAuthSecret& key_and_auth = inner_iter->second;
91 callback.Run(key_and_auth.first, key_and_auth.second);
92 success = true;
93 }
94 }
65 } 95 }
66 96
67 const auto& auth_secret_iter = auth_secrets_.find(app_id); 97 UMA_HISTOGRAM_BOOLEAN("GCM.Crypto.GetKeySuccessRate", success);
68 DCHECK(auth_secret_iter != auth_secrets_.end()); 98 if (!success)
69 99 callback.Run(KeyPair(), std::string() /* auth_secret */);
70 callback.Run(iter->second, auth_secret_iter->second);
71 } 100 }
72 101
73 void GCMKeyStore::CreateKeys(const std::string& app_id, 102 void GCMKeyStore::CreateKeys(const std::string& app_id,
103 const std::string& instance_id_authorized_entity,
74 const KeysCallback& callback) { 104 const KeysCallback& callback) {
75 LazyInitialize(base::Bind(&GCMKeyStore::CreateKeysAfterInitialize, 105 LazyInitialize(base::Bind(&GCMKeyStore::CreateKeysAfterInitialize,
76 weak_factory_.GetWeakPtr(), app_id, callback)); 106 weak_factory_.GetWeakPtr(), app_id,
107 instance_id_authorized_entity, callback));
77 } 108 }
78 109
79 void GCMKeyStore::CreateKeysAfterInitialize(const std::string& app_id, 110 void GCMKeyStore::CreateKeysAfterInitialize(
80 const KeysCallback& callback) { 111 const std::string& app_id,
112 const std::string& instance_id_authorized_entity,
113 const KeysCallback& callback) {
81 DCHECK(state_ == State::INITIALIZED || state_ == State::FAILED); 114 DCHECK(state_ == State::INITIALIZED || state_ == State::FAILED);
82 if (state_ != State::INITIALIZED) { 115 if (state_ != State::INITIALIZED) {
83 callback.Run(KeyPair(), std::string() /* auth_secret */); 116 callback.Run(KeyPair(), std::string() /* auth_secret */);
84 return; 117 return;
85 } 118 }
86 119
87 // Only allow creating new keys if no keys currently exist. 120 // Only allow creating new keys if no keys currently exist. Multiple Instance
88 DCHECK_EQ(0u, key_pairs_.count(app_id)); 121 // ID tokens can share an app_id (with different authorized entities), but
122 // Instance ID tokens cannot share an app_id with a legacy GCM registration.
123 // This invariant is necessary for the fallback_to_empty_authorized_entity
124 // mode of GetKey (needed by GCMEncryptionProvider::DecryptMessage, which
125 // can't distinguish Instance ID tokens from legacy GCM registrations).
126 DCHECK(!key_data_.count(app_id) ||
127 (!instance_id_authorized_entity.empty() &&
128 !key_data_[app_id].count(instance_id_authorized_entity) &&
129 !key_data_[app_id].count(std::string())))
130 << "Instance ID tokens cannot share an app_id with a legacy GCM "
131 "registration";
89 132
90 std::string private_key, public_key_x509, public_key; 133 std::string private_key, public_key_x509, public_key;
91 if (!CreateP256KeyPair(&private_key, &public_key_x509, &public_key)) { 134 if (!CreateP256KeyPair(&private_key, &public_key_x509, &public_key)) {
92 NOTREACHED() << "Unable to initialize a P-256 key pair."; 135 NOTREACHED() << "Unable to initialize a P-256 key pair.";
93 136
94 callback.Run(KeyPair(), std::string() /* auth_secret */); 137 callback.Run(KeyPair(), std::string() /* auth_secret */);
95 return; 138 return;
96 } 139 }
97 140
98 std::string auth_secret; 141 std::string auth_secret;
99 142
100 // Create the authentication secret, which has to be a cryptographically 143 // Create the authentication secret, which has to be a cryptographically
101 // secure random number of at least 128 bits (16 bytes). 144 // secure random number of at least 128 bits (16 bytes).
102 crypto::RandBytes(base::WriteInto(&auth_secret, kAuthSecretBytes + 1), 145 crypto::RandBytes(base::WriteInto(&auth_secret, kAuthSecretBytes + 1),
103 kAuthSecretBytes); 146 kAuthSecretBytes);
104 147
105 // Store the keys in a new EncryptionData object. 148 // Store the keys in a new EncryptionData object.
106 EncryptionData encryption_data; 149 EncryptionData encryption_data;
107 encryption_data.set_app_id(app_id); 150 encryption_data.set_app_id(app_id);
151 if (!instance_id_authorized_entity.empty()) {
152 encryption_data.set_instance_id_authorized_entity(
153 instance_id_authorized_entity);
154 }
108 encryption_data.set_auth_secret(auth_secret); 155 encryption_data.set_auth_secret(auth_secret);
109 156
110 KeyPair* pair = encryption_data.add_keys(); 157 KeyPair* pair = encryption_data.add_keys();
111 pair->set_type(KeyPair::ECDH_P256); 158 pair->set_type(KeyPair::ECDH_P256);
112 pair->set_private_key(private_key); 159 pair->set_private_key(private_key);
113 pair->set_public_key_x509(public_key_x509); 160 pair->set_public_key_x509(public_key_x509);
114 pair->set_public_key(public_key); 161 pair->set_public_key(public_key);
115 162
116 // Write them immediately to our cache, so subsequent calls to 163 // Write them immediately to our cache, so subsequent calls to
117 // {Get/Create/Remove}Keys can see them. 164 // {Get/Create/Remove}Keys can see them.
118 key_pairs_[app_id] = *pair; 165 key_data_[app_id][instance_id_authorized_entity] = {*pair, auth_secret};
119 auth_secrets_[app_id] = auth_secret;
120 166
121 using EntryVectorType = 167 using EntryVectorType =
122 leveldb_proto::ProtoDatabase<EncryptionData>::KeyEntryVector; 168 leveldb_proto::ProtoDatabase<EncryptionData>::KeyEntryVector;
123 169
124 std::unique_ptr<EntryVectorType> entries_to_save(new EntryVectorType()); 170 std::unique_ptr<EntryVectorType> entries_to_save(new EntryVectorType());
125 std::unique_ptr<std::vector<std::string>> keys_to_remove( 171 std::unique_ptr<std::vector<std::string>> keys_to_remove(
126 new std::vector<std::string>()); 172 new std::vector<std::string>());
127 173
128 entries_to_save->push_back(std::make_pair(app_id, encryption_data)); 174 entries_to_save->push_back(std::make_pair(
175 DatabaseKey(app_id, instance_id_authorized_entity), encryption_data));
129 176
130 database_->UpdateEntries( 177 database_->UpdateEntries(
131 std::move(entries_to_save), std::move(keys_to_remove), 178 std::move(entries_to_save), std::move(keys_to_remove),
132 base::Bind(&GCMKeyStore::DidStoreKeys, weak_factory_.GetWeakPtr(), *pair, 179 base::Bind(&GCMKeyStore::DidStoreKeys, weak_factory_.GetWeakPtr(), *pair,
133 auth_secret, callback)); 180 auth_secret, callback));
134 } 181 }
135 182
136 void GCMKeyStore::DidStoreKeys(const KeyPair& pair, 183 void GCMKeyStore::DidStoreKeys(const KeyPair& pair,
137 const std::string& auth_secret, 184 const std::string& auth_secret,
138 const KeysCallback& callback, 185 const KeysCallback& callback,
139 bool success) { 186 bool success) {
140 UMA_HISTOGRAM_BOOLEAN("GCM.Crypto.CreateKeySuccessRate", success); 187 UMA_HISTOGRAM_BOOLEAN("GCM.Crypto.CreateKeySuccessRate", success);
141 188
142 if (!success) { 189 if (!success) {
143 LOG(ERROR) << "Unable to store the created key in the GCM Key Store."; 190 LOG(ERROR) << "Unable to store the created key in the GCM Key Store.";
144 191
145 // Our cache is now inconsistent. Reject requests until restarted. 192 // Our cache is now inconsistent. Reject requests until restarted.
146 state_ = State::FAILED; 193 state_ = State::FAILED;
147 194
148 callback.Run(KeyPair(), std::string() /* auth_secret */); 195 callback.Run(KeyPair(), std::string() /* auth_secret */);
149 return; 196 return;
150 } 197 }
151 198
152 callback.Run(pair, auth_secret); 199 callback.Run(pair, auth_secret);
153 } 200 }
154 201
155 void GCMKeyStore::RemoveKeys(const std::string& app_id, 202 void GCMKeyStore::RemoveKeys(const std::string& app_id,
203 const std::string& instance_id_authorized_entity,
156 const base::Closure& callback) { 204 const base::Closure& callback) {
157 LazyInitialize(base::Bind(&GCMKeyStore::RemoveKeysAfterInitialize, 205 LazyInitialize(base::Bind(&GCMKeyStore::RemoveKeysAfterInitialize,
158 weak_factory_.GetWeakPtr(), app_id, callback)); 206 weak_factory_.GetWeakPtr(), app_id,
207 instance_id_authorized_entity, callback));
159 } 208 }
160 209
161 void GCMKeyStore::RemoveKeysAfterInitialize(const std::string& app_id, 210 void GCMKeyStore::RemoveKeysAfterInitialize(
162 const base::Closure& callback) { 211 const std::string& app_id,
212 const std::string& instance_id_authorized_entity,
213 const base::Closure& callback) {
163 DCHECK(state_ == State::INITIALIZED || state_ == State::FAILED); 214 DCHECK(state_ == State::INITIALIZED || state_ == State::FAILED);
164 const auto iter = key_pairs_.find(app_id); 215
165 if (iter == key_pairs_.end() || state_ != State::INITIALIZED) { 216 const auto& outer_iter = key_data_.find(app_id);
217 if (outer_iter == key_data_.end() || state_ != State::INITIALIZED) {
166 callback.Run(); 218 callback.Run();
167 return; 219 return;
168 } 220 }
169 221
170 // Clear them immediately from our cache, so subsequent calls to
171 // {Get/Create/Remove}Keys don't see them.
172 key_pairs_.erase(app_id);
173 auth_secrets_.erase(app_id);
174
175 using EntryVectorType = 222 using EntryVectorType =
176 leveldb_proto::ProtoDatabase<EncryptionData>::KeyEntryVector; 223 leveldb_proto::ProtoDatabase<EncryptionData>::KeyEntryVector;
177 224
178 std::unique_ptr<EntryVectorType> entries_to_save(new EntryVectorType()); 225 std::unique_ptr<EntryVectorType> entries_to_save(new EntryVectorType());
179 std::unique_ptr<std::vector<std::string>> keys_to_remove( 226 std::unique_ptr<std::vector<std::string>> keys_to_remove(
180 new std::vector<std::string>(1, app_id)); 227 new std::vector<std::string>());
228
229 bool had_keys = false;
230 auto& inner_map = outer_iter->second;
231 for (auto it = inner_map.begin(); it != inner_map.end();) {
232 // Wildcard "*" matches all non-empty authorized entities (not legacy GCM).
233 if (instance_id_authorized_entity == "*"
234 ? !it->first.empty()
235 : it->first == instance_id_authorized_entity) {
236 had_keys = true;
237
238 keys_to_remove->push_back(DatabaseKey(app_id, it->first));
239
240 // Clear keys immediately from our cache, so subsequent calls to
241 // {Get/Create/Remove}Keys don't see them.
242 it = inner_map.erase(it);
243 } else {
244 ++it;
245 }
246 }
247 if (!had_keys) {
248 callback.Run();
249 return;
250 }
251 if (inner_map.empty())
252 key_data_.erase(app_id);
181 253
182 database_->UpdateEntries(std::move(entries_to_save), 254 database_->UpdateEntries(std::move(entries_to_save),
183 std::move(keys_to_remove), 255 std::move(keys_to_remove),
184 base::Bind(&GCMKeyStore::DidRemoveKeys, 256 base::Bind(&GCMKeyStore::DidRemoveKeys,
185 weak_factory_.GetWeakPtr(), callback)); 257 weak_factory_.GetWeakPtr(), callback));
186 } 258 }
187 259
188 void GCMKeyStore::DidRemoveKeys(const base::Closure& callback, bool success) { 260 void GCMKeyStore::DidRemoveKeys(const base::Closure& callback, bool success) {
189 UMA_HISTOGRAM_BOOLEAN("GCM.Crypto.RemoveKeySuccessRate", success); 261 UMA_HISTOGRAM_BOOLEAN("GCM.Crypto.RemoveKeySuccessRate", success);
190 262
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
240 DVLOG(1) << "Unable to load entries into the GCM Key Store."; 312 DVLOG(1) << "Unable to load entries into the GCM Key Store.";
241 state_ = State::FAILED; 313 state_ = State::FAILED;
242 314
243 delayed_task_controller_.SetReady(); 315 delayed_task_controller_.SetReady();
244 return; 316 return;
245 } 317 }
246 318
247 for (const EncryptionData& entry : *entries) { 319 for (const EncryptionData& entry : *entries) {
248 DCHECK_EQ(1, entry.keys_size()); 320 DCHECK_EQ(1, entry.keys_size());
249 321
250 key_pairs_[entry.app_id()] = entry.keys(0); 322 std::string instance_id_authorized_entity;
251 auth_secrets_[entry.app_id()] = entry.auth_secret(); 323 if (entry.has_instance_id_authorized_entity())
324 instance_id_authorized_entity = entry.instance_id_authorized_entity();
325 key_data_[entry.app_id()][instance_id_authorized_entity] = {
326 entry.keys(0), entry.auth_secret()};
252 } 327 }
253 328
254 state_ = State::INITIALIZED; 329 state_ = State::INITIALIZED;
255 330
256 delayed_task_controller_.SetReady(); 331 delayed_task_controller_.SetReady();
257 } 332 }
258 333
259 } // namespace gcm 334 } // namespace gcm
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698