OLD | NEW |
---|---|
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 Loading... | |
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 |
OLD | NEW |