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