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

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: Only EXPECT_DFATAL when LOG_DCHECK == LOG_DFATAL 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
19 // Statistics are logged to UMA with this string as part of histogram name. They 23 // 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 24 // can all be found under LevelDB.*.GCMKeyStore. Changing this needs to
21 // synchronize with histograms.xml, AND will also become incompatible with older 25 // synchronize with histograms.xml, AND will also become incompatible with older
22 // browsers still reporting the previous values. 26 // browsers still reporting the previous values.
23 const char kDatabaseUMAClientName[] = "GCMKeyStore"; 27 const char kDatabaseUMAClientName[] = "GCMKeyStore";
24 28
25 // Number of cryptographically secure random bytes to generate as a key pair's 29 // Number of cryptographically secure random bytes to generate as a key pair's
26 // authentication secret. Must be at least 16 bytes. 30 // authentication secret. Must be at least 16 bytes.
27 const size_t kAuthSecretBytes = 16; 31 const size_t kAuthSecretBytes = 16;
28 32
33 std::string DatabaseKey(const std::string& app_id,
34 const std::string& authorized_entity) {
35 DCHECK_EQ(std::string::npos, app_id.find(','));
36 DCHECK_EQ(std::string::npos, authorized_entity.find(','));
37 DCHECK_NE("*", authorized_entity) << "Wildcards require special handling";
38 return authorized_entity.empty()
39 ? app_id // No comma, for compatibility with existing keys.
40 : app_id + ',' + authorized_entity;
41 }
42
43 } // namespace
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& 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(
51 weak_factory_.GetWeakPtr(), app_id, callback)); 69 &GCMKeyStore::GetKeysAfterInitialize, weak_factory_.GetWeakPtr(), app_id,
70 authorized_entity, fallback_to_empty_authorized_entity, callback));
52 } 71 }
53 72
54 void GCMKeyStore::GetKeysAfterInitialize(const std::string& app_id, 73 void GCMKeyStore::GetKeysAfterInitialize(
55 const KeysCallback& callback) { 74 const std::string& app_id,
75 const std::string& authorized_entity,
76 bool fallback_to_empty_authorized_entity,
77 const KeysCallback& callback) {
56 DCHECK(state_ == State::INITIALIZED || state_ == State::FAILED); 78 DCHECK(state_ == State::INITIALIZED || state_ == State::FAILED);
57 const auto& iter = key_pairs_.find(app_id); 79 bool success = false;
58 80
59 const bool success = state_ == State::INITIALIZED && iter != key_pairs_.end(); 81 if (state_ == State::INITIALIZED) {
60 UMA_HISTOGRAM_BOOLEAN("GCM.Crypto.GetKeySuccessRate", success); 82 auto outer_iter = key_data_.find(app_id);
61 83 if (outer_iter != key_data_.end()) {
62 if (!success) { 84 const auto& inner_map = outer_iter->second;
63 callback.Run(KeyPair(), std::string() /* auth_secret */); 85 auto inner_iter = inner_map.find(authorized_entity);
64 return; 86 if (fallback_to_empty_authorized_entity && inner_iter == inner_map.end())
87 inner_iter = inner_map.find(std::string());
88 if (inner_iter != inner_map.end()) {
89 const KeyPairAndAuthSecret& key_and_auth = inner_iter->second;
90 callback.Run(key_and_auth.first, key_and_auth.second);
91 success = true;
92 }
93 }
65 } 94 }
66 95
67 const auto& auth_secret_iter = auth_secrets_.find(app_id); 96 UMA_HISTOGRAM_BOOLEAN("GCM.Crypto.GetKeySuccessRate", success);
68 DCHECK(auth_secret_iter != auth_secrets_.end()); 97 if (!success)
69 98 callback.Run(KeyPair(), std::string() /* auth_secret */);
70 callback.Run(iter->second, auth_secret_iter->second);
71 } 99 }
72 100
73 void GCMKeyStore::CreateKeys(const std::string& app_id, 101 void GCMKeyStore::CreateKeys(const std::string& app_id,
102 const std::string& authorized_entity,
74 const KeysCallback& callback) { 103 const KeysCallback& callback) {
75 LazyInitialize(base::Bind(&GCMKeyStore::CreateKeysAfterInitialize, 104 LazyInitialize(base::Bind(&GCMKeyStore::CreateKeysAfterInitialize,
76 weak_factory_.GetWeakPtr(), app_id, callback)); 105 weak_factory_.GetWeakPtr(), app_id,
106 authorized_entity, callback));
77 } 107 }
78 108
79 void GCMKeyStore::CreateKeysAfterInitialize(const std::string& app_id, 109 void GCMKeyStore::CreateKeysAfterInitialize(
80 const KeysCallback& callback) { 110 const std::string& app_id,
111 const std::string& authorized_entity,
112 const KeysCallback& callback) {
81 DCHECK(state_ == State::INITIALIZED || state_ == State::FAILED); 113 DCHECK(state_ == State::INITIALIZED || state_ == State::FAILED);
82 if (state_ != State::INITIALIZED) { 114 if (state_ != State::INITIALIZED) {
83 callback.Run(KeyPair(), std::string() /* auth_secret */); 115 callback.Run(KeyPair(), std::string() /* auth_secret */);
84 return; 116 return;
85 } 117 }
86 118
87 // Only allow creating new keys if no keys currently exist. 119 // Only allow creating new keys if no keys currently exist. Multiple Instance
88 DCHECK_EQ(0u, key_pairs_.count(app_id)); 120 // ID tokens can share an app_id (with different authorized entities), but
121 // InstanceID tokens can't share an app_id with a non-InstanceID registration.
122 // This invariant is necessary for the fallback_to_empty_authorized_entity
123 // mode of GetKey (needed by GCMEncryptionProvider::DecryptMessage, which
124 // can't distinguish Instance ID tokens from non-InstanceID registrations).
125 DCHECK(!key_data_.count(app_id) ||
126 (!authorized_entity.empty() &&
127 !key_data_[app_id].count(authorized_entity) &&
128 !key_data_[app_id].count(std::string())))
129 << "Instance ID tokens cannot share an app_id with a non-InstanceID GCM "
130 "registration";
89 131
90 std::string private_key, public_key_x509, public_key; 132 std::string private_key, public_key_x509, public_key;
91 if (!CreateP256KeyPair(&private_key, &public_key_x509, &public_key)) { 133 if (!CreateP256KeyPair(&private_key, &public_key_x509, &public_key)) {
92 NOTREACHED() << "Unable to initialize a P-256 key pair."; 134 NOTREACHED() << "Unable to initialize a P-256 key pair.";
93 135
94 callback.Run(KeyPair(), std::string() /* auth_secret */); 136 callback.Run(KeyPair(), std::string() /* auth_secret */);
95 return; 137 return;
96 } 138 }
97 139
98 std::string auth_secret; 140 std::string auth_secret;
99 141
100 // Create the authentication secret, which has to be a cryptographically 142 // Create the authentication secret, which has to be a cryptographically
101 // secure random number of at least 128 bits (16 bytes). 143 // secure random number of at least 128 bits (16 bytes).
102 crypto::RandBytes(base::WriteInto(&auth_secret, kAuthSecretBytes + 1), 144 crypto::RandBytes(base::WriteInto(&auth_secret, kAuthSecretBytes + 1),
103 kAuthSecretBytes); 145 kAuthSecretBytes);
104 146
105 // Store the keys in a new EncryptionData object. 147 // Store the keys in a new EncryptionData object.
106 EncryptionData encryption_data; 148 EncryptionData encryption_data;
107 encryption_data.set_app_id(app_id); 149 encryption_data.set_app_id(app_id);
150 if (!authorized_entity.empty())
151 encryption_data.set_authorized_entity(authorized_entity);
108 encryption_data.set_auth_secret(auth_secret); 152 encryption_data.set_auth_secret(auth_secret);
109 153
110 KeyPair* pair = encryption_data.add_keys(); 154 KeyPair* pair = encryption_data.add_keys();
111 pair->set_type(KeyPair::ECDH_P256); 155 pair->set_type(KeyPair::ECDH_P256);
112 pair->set_private_key(private_key); 156 pair->set_private_key(private_key);
113 pair->set_public_key_x509(public_key_x509); 157 pair->set_public_key_x509(public_key_x509);
114 pair->set_public_key(public_key); 158 pair->set_public_key(public_key);
115 159
116 // Write them immediately to our cache, so subsequent calls to 160 // Write them immediately to our cache, so subsequent calls to
117 // {Get/Create/Remove}Keys can see them. 161 // {Get/Create/Remove}Keys can see them.
118 key_pairs_[app_id] = *pair; 162 key_data_[app_id][authorized_entity] = {*pair, auth_secret};
119 auth_secrets_[app_id] = auth_secret;
120 163
121 using EntryVectorType = 164 using EntryVectorType =
122 leveldb_proto::ProtoDatabase<EncryptionData>::KeyEntryVector; 165 leveldb_proto::ProtoDatabase<EncryptionData>::KeyEntryVector;
123 166
124 std::unique_ptr<EntryVectorType> entries_to_save(new EntryVectorType()); 167 std::unique_ptr<EntryVectorType> entries_to_save(new EntryVectorType());
125 std::unique_ptr<std::vector<std::string>> keys_to_remove( 168 std::unique_ptr<std::vector<std::string>> keys_to_remove(
126 new std::vector<std::string>()); 169 new std::vector<std::string>());
127 170
128 entries_to_save->push_back(std::make_pair(app_id, encryption_data)); 171 entries_to_save->push_back(
172 std::make_pair(DatabaseKey(app_id, authorized_entity), encryption_data));
129 173
130 database_->UpdateEntries( 174 database_->UpdateEntries(
131 std::move(entries_to_save), std::move(keys_to_remove), 175 std::move(entries_to_save), std::move(keys_to_remove),
132 base::Bind(&GCMKeyStore::DidStoreKeys, weak_factory_.GetWeakPtr(), *pair, 176 base::Bind(&GCMKeyStore::DidStoreKeys, weak_factory_.GetWeakPtr(), *pair,
133 auth_secret, callback)); 177 auth_secret, callback));
134 } 178 }
135 179
136 void GCMKeyStore::DidStoreKeys(const KeyPair& pair, 180 void GCMKeyStore::DidStoreKeys(const KeyPair& pair,
137 const std::string& auth_secret, 181 const std::string& auth_secret,
138 const KeysCallback& callback, 182 const KeysCallback& callback,
139 bool success) { 183 bool success) {
140 UMA_HISTOGRAM_BOOLEAN("GCM.Crypto.CreateKeySuccessRate", success); 184 UMA_HISTOGRAM_BOOLEAN("GCM.Crypto.CreateKeySuccessRate", success);
141 185
142 if (!success) { 186 if (!success) {
143 LOG(ERROR) << "Unable to store the created key in the GCM Key Store."; 187 LOG(ERROR) << "Unable to store the created key in the GCM Key Store.";
144 188
145 // Our cache is now inconsistent. Reject requests until restarted. 189 // Our cache is now inconsistent. Reject requests until restarted.
146 state_ = State::FAILED; 190 state_ = State::FAILED;
147 191
148 callback.Run(KeyPair(), std::string() /* auth_secret */); 192 callback.Run(KeyPair(), std::string() /* auth_secret */);
149 return; 193 return;
150 } 194 }
151 195
152 callback.Run(pair, auth_secret); 196 callback.Run(pair, auth_secret);
153 } 197 }
154 198
155 void GCMKeyStore::RemoveKeys(const std::string& app_id, 199 void GCMKeyStore::RemoveKeys(const std::string& app_id,
200 const std::string& authorized_entity,
156 const base::Closure& callback) { 201 const base::Closure& callback) {
157 LazyInitialize(base::Bind(&GCMKeyStore::RemoveKeysAfterInitialize, 202 LazyInitialize(base::Bind(&GCMKeyStore::RemoveKeysAfterInitialize,
158 weak_factory_.GetWeakPtr(), app_id, callback)); 203 weak_factory_.GetWeakPtr(), app_id,
204 authorized_entity, callback));
159 } 205 }
160 206
161 void GCMKeyStore::RemoveKeysAfterInitialize(const std::string& app_id, 207 void GCMKeyStore::RemoveKeysAfterInitialize(
162 const base::Closure& callback) { 208 const std::string& app_id,
209 const std::string& authorized_entity,
210 const base::Closure& callback) {
163 DCHECK(state_ == State::INITIALIZED || state_ == State::FAILED); 211 DCHECK(state_ == State::INITIALIZED || state_ == State::FAILED);
164 const auto iter = key_pairs_.find(app_id); 212
165 if (iter == key_pairs_.end() || state_ != State::INITIALIZED) { 213 const auto& outer_iter = key_data_.find(app_id);
214 if (outer_iter == key_data_.end() || state_ != State::INITIALIZED) {
166 callback.Run(); 215 callback.Run();
167 return; 216 return;
168 } 217 }
169 218
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 = 219 using EntryVectorType =
176 leveldb_proto::ProtoDatabase<EncryptionData>::KeyEntryVector; 220 leveldb_proto::ProtoDatabase<EncryptionData>::KeyEntryVector;
177 221
178 std::unique_ptr<EntryVectorType> entries_to_save(new EntryVectorType()); 222 std::unique_ptr<EntryVectorType> entries_to_save(new EntryVectorType());
179 std::unique_ptr<std::vector<std::string>> keys_to_remove( 223 std::unique_ptr<std::vector<std::string>> keys_to_remove(
180 new std::vector<std::string>(1, app_id)); 224 new std::vector<std::string>());
225
226 bool had_keys = false;
227 auto& inner_map = outer_iter->second;
228 for (auto it = inner_map.begin(); it != inner_map.end();) {
229 // Wildcard "*" matches all non-empty authorized entities (InstanceID only).
230 if (authorized_entity == "*" ? !it->first.empty()
231 : it->first == authorized_entity) {
232 had_keys = true;
233
234 keys_to_remove->push_back(DatabaseKey(app_id, it->first));
235
236 // Clear keys immediately from our cache, so subsequent calls to
237 // {Get/Create/Remove}Keys don't see them.
238 it = inner_map.erase(it);
239 } else {
240 ++it;
241 }
242 }
243 if (!had_keys) {
244 callback.Run();
245 return;
246 }
247 if (inner_map.empty())
248 key_data_.erase(app_id);
181 249
182 database_->UpdateEntries(std::move(entries_to_save), 250 database_->UpdateEntries(std::move(entries_to_save),
183 std::move(keys_to_remove), 251 std::move(keys_to_remove),
184 base::Bind(&GCMKeyStore::DidRemoveKeys, 252 base::Bind(&GCMKeyStore::DidRemoveKeys,
185 weak_factory_.GetWeakPtr(), callback)); 253 weak_factory_.GetWeakPtr(), callback));
186 } 254 }
187 255
188 void GCMKeyStore::DidRemoveKeys(const base::Closure& callback, bool success) { 256 void GCMKeyStore::DidRemoveKeys(const base::Closure& callback, bool success) {
189 UMA_HISTOGRAM_BOOLEAN("GCM.Crypto.RemoveKeySuccessRate", success); 257 UMA_HISTOGRAM_BOOLEAN("GCM.Crypto.RemoveKeySuccessRate", success);
190 258
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
240 DVLOG(1) << "Unable to load entries into the GCM Key Store."; 308 DVLOG(1) << "Unable to load entries into the GCM Key Store.";
241 state_ = State::FAILED; 309 state_ = State::FAILED;
242 310
243 delayed_task_controller_.SetReady(); 311 delayed_task_controller_.SetReady();
244 return; 312 return;
245 } 313 }
246 314
247 for (const EncryptionData& entry : *entries) { 315 for (const EncryptionData& entry : *entries) {
248 DCHECK_EQ(1, entry.keys_size()); 316 DCHECK_EQ(1, entry.keys_size());
249 317
250 key_pairs_[entry.app_id()] = entry.keys(0); 318 std::string authorized_entity;
251 auth_secrets_[entry.app_id()] = entry.auth_secret(); 319 if (entry.has_authorized_entity())
320 authorized_entity = entry.authorized_entity();
321 key_data_[entry.app_id()][authorized_entity] = {entry.keys(0),
322 entry.auth_secret()};
252 } 323 }
253 324
254 state_ = State::INITIALIZED; 325 state_ = State::INITIALIZED;
255 326
256 delayed_task_controller_.SetReady(); 327 delayed_task_controller_.SetReady();
257 } 328 }
258 329
259 } // namespace gcm 330 } // namespace gcm
OLDNEW
« no previous file with comments | « components/gcm_driver/crypto/gcm_key_store.h ('k') | components/gcm_driver/crypto/gcm_key_store_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698