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 | |
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); |
Peter Beverloo
2016/05/10 12:52:38
const&
johnme
2016/05/10 13:24:45
No point, since find returns an iterator by value.
| |
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); | |
Peter Beverloo
2016/05/10 12:52:39
nit: no curly braces
johnme
2016/05/10 13:24:45
Done.
| |
152 } | |
108 encryption_data.set_auth_secret(auth_secret); | 153 encryption_data.set_auth_secret(auth_secret); |
109 | 154 |
110 KeyPair* pair = encryption_data.add_keys(); | 155 KeyPair* pair = encryption_data.add_keys(); |
111 pair->set_type(KeyPair::ECDH_P256); | 156 pair->set_type(KeyPair::ECDH_P256); |
112 pair->set_private_key(private_key); | 157 pair->set_private_key(private_key); |
113 pair->set_public_key_x509(public_key_x509); | 158 pair->set_public_key_x509(public_key_x509); |
114 pair->set_public_key(public_key); | 159 pair->set_public_key(public_key); |
115 | 160 |
116 // Write them immediately to our cache, so subsequent calls to | 161 // Write them immediately to our cache, so subsequent calls to |
117 // {Get/Create/Remove}Keys can see them. | 162 // {Get/Create/Remove}Keys can see them. |
118 key_pairs_[app_id] = *pair; | 163 key_data_[app_id][authorized_entity] = {*pair, auth_secret}; |
119 auth_secrets_[app_id] = auth_secret; | |
120 | 164 |
121 using EntryVectorType = | 165 using EntryVectorType = |
122 leveldb_proto::ProtoDatabase<EncryptionData>::KeyEntryVector; | 166 leveldb_proto::ProtoDatabase<EncryptionData>::KeyEntryVector; |
123 | 167 |
124 std::unique_ptr<EntryVectorType> entries_to_save(new EntryVectorType()); | 168 std::unique_ptr<EntryVectorType> entries_to_save(new EntryVectorType()); |
125 std::unique_ptr<std::vector<std::string>> keys_to_remove( | 169 std::unique_ptr<std::vector<std::string>> keys_to_remove( |
126 new std::vector<std::string>()); | 170 new std::vector<std::string>()); |
127 | 171 |
128 entries_to_save->push_back(std::make_pair(app_id, encryption_data)); | 172 entries_to_save->push_back( |
173 std::make_pair(DatabaseKey(app_id, authorized_entity), encryption_data)); | |
129 | 174 |
130 database_->UpdateEntries( | 175 database_->UpdateEntries( |
131 std::move(entries_to_save), std::move(keys_to_remove), | 176 std::move(entries_to_save), std::move(keys_to_remove), |
132 base::Bind(&GCMKeyStore::DidStoreKeys, weak_factory_.GetWeakPtr(), *pair, | 177 base::Bind(&GCMKeyStore::DidStoreKeys, weak_factory_.GetWeakPtr(), *pair, |
133 auth_secret, callback)); | 178 auth_secret, callback)); |
134 } | 179 } |
135 | 180 |
136 void GCMKeyStore::DidStoreKeys(const KeyPair& pair, | 181 void GCMKeyStore::DidStoreKeys(const KeyPair& pair, |
137 const std::string& auth_secret, | 182 const std::string& auth_secret, |
138 const KeysCallback& callback, | 183 const KeysCallback& callback, |
139 bool success) { | 184 bool success) { |
140 UMA_HISTOGRAM_BOOLEAN("GCM.Crypto.CreateKeySuccessRate", success); | 185 UMA_HISTOGRAM_BOOLEAN("GCM.Crypto.CreateKeySuccessRate", success); |
141 | 186 |
142 if (!success) { | 187 if (!success) { |
143 LOG(ERROR) << "Unable to store the created key in the GCM Key Store."; | 188 LOG(ERROR) << "Unable to store the created key in the GCM Key Store."; |
144 | 189 |
145 // Our cache is now inconsistent. Reject requests until restarted. | 190 // Our cache is now inconsistent. Reject requests until restarted. |
146 state_ = State::FAILED; | 191 state_ = State::FAILED; |
147 | 192 |
148 callback.Run(KeyPair(), std::string() /* auth_secret */); | 193 callback.Run(KeyPair(), std::string() /* auth_secret */); |
149 return; | 194 return; |
150 } | 195 } |
151 | 196 |
152 callback.Run(pair, auth_secret); | 197 callback.Run(pair, auth_secret); |
153 } | 198 } |
154 | 199 |
155 void GCMKeyStore::RemoveKeys(const std::string& app_id, | 200 void GCMKeyStore::RemoveKeys(const std::string& app_id, |
201 const std::string& authorized_entity, | |
156 const base::Closure& callback) { | 202 const base::Closure& callback) { |
157 LazyInitialize(base::Bind(&GCMKeyStore::RemoveKeysAfterInitialize, | 203 LazyInitialize(base::Bind(&GCMKeyStore::RemoveKeysAfterInitialize, |
158 weak_factory_.GetWeakPtr(), app_id, callback)); | 204 weak_factory_.GetWeakPtr(), app_id, |
205 authorized_entity, callback)); | |
159 } | 206 } |
160 | 207 |
161 void GCMKeyStore::RemoveKeysAfterInitialize(const std::string& app_id, | 208 void GCMKeyStore::RemoveKeysAfterInitialize( |
162 const base::Closure& callback) { | 209 const std::string& app_id, |
210 const std::string& authorized_entity, | |
211 const base::Closure& callback) { | |
163 DCHECK(state_ == State::INITIALIZED || state_ == State::FAILED); | 212 DCHECK(state_ == State::INITIALIZED || state_ == State::FAILED); |
164 const auto iter = key_pairs_.find(app_id); | 213 |
165 if (iter == key_pairs_.end() || state_ != State::INITIALIZED) { | 214 const auto& outer_iter = key_data_.find(app_id); |
215 if (outer_iter == key_data_.end() || state_ != State::INITIALIZED) { | |
166 callback.Run(); | 216 callback.Run(); |
167 return; | 217 return; |
168 } | 218 } |
169 | 219 |
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 = | 220 using EntryVectorType = |
176 leveldb_proto::ProtoDatabase<EncryptionData>::KeyEntryVector; | 221 leveldb_proto::ProtoDatabase<EncryptionData>::KeyEntryVector; |
177 | 222 |
178 std::unique_ptr<EntryVectorType> entries_to_save(new EntryVectorType()); | 223 std::unique_ptr<EntryVectorType> entries_to_save(new EntryVectorType()); |
179 std::unique_ptr<std::vector<std::string>> keys_to_remove( | 224 std::unique_ptr<std::vector<std::string>> keys_to_remove( |
180 new std::vector<std::string>(1, app_id)); | 225 new std::vector<std::string>()); |
226 | |
227 bool had_keys = false; | |
228 auto& inner_map = outer_iter->second; | |
229 for (auto it = inner_map.begin(); it != inner_map.end();) { | |
230 // Wildcard "*" matches all non-empty authorized entities (InstanceID only). | |
231 if (authorized_entity == "*" ? !it->first.empty() | |
232 : it->first == authorized_entity) { | |
233 had_keys = true; | |
234 | |
235 keys_to_remove->push_back(DatabaseKey(app_id, it->first)); | |
236 | |
237 // Clear keys immediately from our cache, so subsequent calls to | |
238 // {Get/Create/Remove}Keys don't see them. | |
239 it = inner_map.erase(it); | |
240 } else { | |
241 ++it; | |
242 } | |
243 } | |
244 if (!had_keys) { | |
245 callback.Run(); | |
246 return; | |
247 } | |
248 if (inner_map.empty()) | |
249 key_data_.erase(app_id); | |
181 | 250 |
182 database_->UpdateEntries(std::move(entries_to_save), | 251 database_->UpdateEntries(std::move(entries_to_save), |
183 std::move(keys_to_remove), | 252 std::move(keys_to_remove), |
184 base::Bind(&GCMKeyStore::DidRemoveKeys, | 253 base::Bind(&GCMKeyStore::DidRemoveKeys, |
185 weak_factory_.GetWeakPtr(), callback)); | 254 weak_factory_.GetWeakPtr(), callback)); |
186 } | 255 } |
187 | 256 |
188 void GCMKeyStore::DidRemoveKeys(const base::Closure& callback, bool success) { | 257 void GCMKeyStore::DidRemoveKeys(const base::Closure& callback, bool success) { |
189 UMA_HISTOGRAM_BOOLEAN("GCM.Crypto.RemoveKeySuccessRate", success); | 258 UMA_HISTOGRAM_BOOLEAN("GCM.Crypto.RemoveKeySuccessRate", success); |
190 | 259 |
(...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."; | 309 DVLOG(1) << "Unable to load entries into the GCM Key Store."; |
241 state_ = State::FAILED; | 310 state_ = State::FAILED; |
242 | 311 |
243 delayed_task_controller_.SetReady(); | 312 delayed_task_controller_.SetReady(); |
244 return; | 313 return; |
245 } | 314 } |
246 | 315 |
247 for (const EncryptionData& entry : *entries) { | 316 for (const EncryptionData& entry : *entries) { |
248 DCHECK_EQ(1, entry.keys_size()); | 317 DCHECK_EQ(1, entry.keys_size()); |
249 | 318 |
250 key_pairs_[entry.app_id()] = entry.keys(0); | 319 std::string authorized_entity; |
251 auth_secrets_[entry.app_id()] = entry.auth_secret(); | 320 if (entry.has_authorized_entity()) |
321 authorized_entity = entry.authorized_entity(); | |
322 key_data_[entry.app_id()][authorized_entity] = {entry.keys(0), | |
323 entry.auth_secret()}; | |
252 } | 324 } |
253 | 325 |
254 state_ = State::INITIALIZED; | 326 state_ = State::INITIALIZED; |
255 | 327 |
256 delayed_task_controller_.SetReady(); | 328 delayed_task_controller_.SetReady(); |
257 } | 329 } |
258 | 330 |
259 } // namespace gcm | 331 } // namespace gcm |
OLD | NEW |