Index: components/gcm_driver/crypto/gcm_key_store.cc |
diff --git a/components/gcm_driver/crypto/gcm_key_store.cc b/components/gcm_driver/crypto/gcm_key_store.cc |
index c24af360e83dcfcf53b26844875bede52e01054c..2b3a576351b3e8cc3bc0030f578007dc14c7460a 100644 |
--- a/components/gcm_driver/crypto/gcm_key_store.cc |
+++ b/components/gcm_driver/crypto/gcm_key_store.cc |
@@ -8,6 +8,8 @@ |
#include <utility> |
+#include "base/bind_helpers.h" |
+#include "base/callback.h" |
#include "base/logging.h" |
#include "base/metrics/histogram_macros.h" |
#include "components/gcm_driver/crypto/p256_key_util.h" |
@@ -16,6 +18,20 @@ |
namespace gcm { |
+namespace { |
+ |
+std::string DatabaseKey(const std::string& app_id, |
+ const std::string& instance_id_authorized_entity) { |
+ DCHECK_EQ(std::string::npos, app_id.find(',')); |
+ DCHECK_EQ(std::string::npos, instance_id_authorized_entity.find(',')); |
+ DCHECK_NE("*", instance_id_authorized_entity) << "Don't store wildcards"; |
+ return instance_id_authorized_entity.empty() |
+ ? app_id // No comma, for compatibility with existing keys. |
+ : app_id + ',' + instance_id_authorized_entity; |
+} |
+ |
+} // namespace |
+ |
// Statistics are logged to UMA with this string as part of histogram name. They |
// can all be found under LevelDB.*.GCMKeyStore. Changing this needs to |
// synchronize with histograms.xml, AND will also become incompatible with older |
@@ -46,46 +62,73 @@ GCMKeyStore::GCMKeyStore( |
GCMKeyStore::~GCMKeyStore() {} |
void GCMKeyStore::GetKeys(const std::string& app_id, |
+ const std::string& instance_id_authorized_entity, |
+ bool fallback_to_empty_authorized_entity, |
const KeysCallback& callback) { |
LazyInitialize(base::Bind(&GCMKeyStore::GetKeysAfterInitialize, |
- weak_factory_.GetWeakPtr(), app_id, callback)); |
+ weak_factory_.GetWeakPtr(), app_id, |
+ instance_id_authorized_entity, |
+ fallback_to_empty_authorized_entity, callback)); |
} |
-void GCMKeyStore::GetKeysAfterInitialize(const std::string& app_id, |
- const KeysCallback& callback) { |
+void GCMKeyStore::GetKeysAfterInitialize( |
+ const std::string& app_id, |
+ const std::string& instance_id_authorized_entity, |
+ bool fallback_to_empty_authorized_entity, |
+ const KeysCallback& callback) { |
DCHECK(state_ == State::INITIALIZED || state_ == State::FAILED); |
- const auto& iter = key_pairs_.find(app_id); |
+ bool success = false; |
+ |
+ if (state_ == State::INITIALIZED) { |
+ auto outer_iter = key_data_.find(app_id); |
+ if (outer_iter != key_data_.end()) { |
+ const auto& inner_map = outer_iter->second; |
+ auto inner_iter = inner_map.find(instance_id_authorized_entity); |
+ if (fallback_to_empty_authorized_entity && inner_iter == inner_map.end()) |
+ inner_iter = inner_map.find(std::string()); |
+ if (inner_iter != inner_map.end()) { |
+ const KeyPairAndAuthSecret& key_and_auth = inner_iter->second; |
+ callback.Run(key_and_auth.first, key_and_auth.second); |
+ success = true; |
+ } |
+ } |
+ } |
- const bool success = state_ == State::INITIALIZED && iter != key_pairs_.end(); |
UMA_HISTOGRAM_BOOLEAN("GCM.Crypto.GetKeySuccessRate", success); |
- |
- if (!success) { |
+ if (!success) |
callback.Run(KeyPair(), std::string() /* auth_secret */); |
- return; |
- } |
- |
- const auto& auth_secret_iter = auth_secrets_.find(app_id); |
- DCHECK(auth_secret_iter != auth_secrets_.end()); |
- |
- callback.Run(iter->second, auth_secret_iter->second); |
} |
void GCMKeyStore::CreateKeys(const std::string& app_id, |
+ const std::string& instance_id_authorized_entity, |
const KeysCallback& callback) { |
LazyInitialize(base::Bind(&GCMKeyStore::CreateKeysAfterInitialize, |
- weak_factory_.GetWeakPtr(), app_id, callback)); |
+ weak_factory_.GetWeakPtr(), app_id, |
+ instance_id_authorized_entity, callback)); |
} |
-void GCMKeyStore::CreateKeysAfterInitialize(const std::string& app_id, |
- const KeysCallback& callback) { |
+void GCMKeyStore::CreateKeysAfterInitialize( |
+ const std::string& app_id, |
+ const std::string& instance_id_authorized_entity, |
+ const KeysCallback& callback) { |
DCHECK(state_ == State::INITIALIZED || state_ == State::FAILED); |
if (state_ != State::INITIALIZED) { |
callback.Run(KeyPair(), std::string() /* auth_secret */); |
return; |
} |
- // Only allow creating new keys if no keys currently exist. |
- DCHECK_EQ(0u, key_pairs_.count(app_id)); |
+ // Only allow creating new keys if no keys currently exist. Multiple Instance |
+ // ID tokens can share an app_id (with different authorized entities), but |
+ // Instance ID tokens cannot share an app_id with a legacy GCM registration. |
+ // This invariant is necessary for the fallback_to_empty_authorized_entity |
+ // mode of GetKey (needed by GCMEncryptionProvider::DecryptMessage, which |
+ // can't distinguish Instance ID tokens from legacy GCM registrations). |
+ DCHECK(!key_data_.count(app_id) || |
+ (!instance_id_authorized_entity.empty() && |
+ !key_data_[app_id].count(instance_id_authorized_entity) && |
+ !key_data_[app_id].count(std::string()))) |
+ << "Instance ID tokens cannot share an app_id with a legacy GCM " |
+ "registration"; |
std::string private_key, public_key_x509, public_key; |
if (!CreateP256KeyPair(&private_key, &public_key_x509, &public_key)) { |
@@ -105,6 +148,10 @@ void GCMKeyStore::CreateKeysAfterInitialize(const std::string& app_id, |
// Store the keys in a new EncryptionData object. |
EncryptionData encryption_data; |
encryption_data.set_app_id(app_id); |
+ if (!instance_id_authorized_entity.empty()) { |
+ encryption_data.set_instance_id_authorized_entity( |
+ instance_id_authorized_entity); |
+ } |
encryption_data.set_auth_secret(auth_secret); |
KeyPair* pair = encryption_data.add_keys(); |
@@ -115,8 +162,7 @@ void GCMKeyStore::CreateKeysAfterInitialize(const std::string& app_id, |
// Write them immediately to our cache, so subsequent calls to |
// {Get/Create/Remove}Keys can see them. |
- key_pairs_[app_id] = *pair; |
- auth_secrets_[app_id] = auth_secret; |
+ key_data_[app_id][instance_id_authorized_entity] = {*pair, auth_secret}; |
using EntryVectorType = |
leveldb_proto::ProtoDatabase<EncryptionData>::KeyEntryVector; |
@@ -125,7 +171,8 @@ void GCMKeyStore::CreateKeysAfterInitialize(const std::string& app_id, |
std::unique_ptr<std::vector<std::string>> keys_to_remove( |
new std::vector<std::string>()); |
- entries_to_save->push_back(std::make_pair(app_id, encryption_data)); |
+ entries_to_save->push_back(std::make_pair( |
+ DatabaseKey(app_id, instance_id_authorized_entity), encryption_data)); |
database_->UpdateEntries( |
std::move(entries_to_save), std::move(keys_to_remove), |
@@ -153,31 +200,56 @@ void GCMKeyStore::DidStoreKeys(const KeyPair& pair, |
} |
void GCMKeyStore::RemoveKeys(const std::string& app_id, |
+ const std::string& instance_id_authorized_entity, |
const base::Closure& callback) { |
LazyInitialize(base::Bind(&GCMKeyStore::RemoveKeysAfterInitialize, |
- weak_factory_.GetWeakPtr(), app_id, callback)); |
+ weak_factory_.GetWeakPtr(), app_id, |
+ instance_id_authorized_entity, callback)); |
} |
-void GCMKeyStore::RemoveKeysAfterInitialize(const std::string& app_id, |
- const base::Closure& callback) { |
+void GCMKeyStore::RemoveKeysAfterInitialize( |
+ const std::string& app_id, |
+ const std::string& instance_id_authorized_entity, |
+ const base::Closure& callback) { |
DCHECK(state_ == State::INITIALIZED || state_ == State::FAILED); |
- const auto iter = key_pairs_.find(app_id); |
- if (iter == key_pairs_.end() || state_ != State::INITIALIZED) { |
+ |
+ const auto& outer_iter = key_data_.find(app_id); |
+ if (outer_iter == key_data_.end() || state_ != State::INITIALIZED) { |
callback.Run(); |
return; |
} |
- // Clear them immediately from our cache, so subsequent calls to |
- // {Get/Create/Remove}Keys don't see them. |
- key_pairs_.erase(app_id); |
- auth_secrets_.erase(app_id); |
- |
using EntryVectorType = |
leveldb_proto::ProtoDatabase<EncryptionData>::KeyEntryVector; |
std::unique_ptr<EntryVectorType> entries_to_save(new EntryVectorType()); |
std::unique_ptr<std::vector<std::string>> keys_to_remove( |
- new std::vector<std::string>(1, app_id)); |
+ new std::vector<std::string>()); |
+ |
+ bool had_keys = false; |
+ auto& inner_map = outer_iter->second; |
+ for (auto it = inner_map.begin(); it != inner_map.end();) { |
+ // Wildcard "*" matches all non-empty authorized entities (not legacy GCM). |
+ if (instance_id_authorized_entity == "*" |
+ ? !it->first.empty() |
+ : it->first == instance_id_authorized_entity) { |
+ had_keys = true; |
+ |
+ keys_to_remove->push_back(DatabaseKey(app_id, it->first)); |
+ |
+ // Clear keys immediately from our cache, so subsequent calls to |
+ // {Get/Create/Remove}Keys don't see them. |
+ it = inner_map.erase(it); |
+ } else { |
+ ++it; |
+ } |
+ } |
+ if (!had_keys) { |
+ callback.Run(); |
+ return; |
+ } |
+ if (inner_map.empty()) |
+ key_data_.erase(app_id); |
database_->UpdateEntries(std::move(entries_to_save), |
std::move(keys_to_remove), |
@@ -247,8 +319,11 @@ void GCMKeyStore::DidLoadKeys( |
for (const EncryptionData& entry : *entries) { |
DCHECK_EQ(1, entry.keys_size()); |
- key_pairs_[entry.app_id()] = entry.keys(0); |
- auth_secrets_[entry.app_id()] = entry.auth_secret(); |
+ std::string instance_id_authorized_entity; |
+ if (entry.has_instance_id_authorized_entity()) |
+ instance_id_authorized_entity = entry.instance_id_authorized_entity(); |
+ key_data_[entry.app_id()][instance_id_authorized_entity] = { |
+ entry.keys(0), entry.auth_secret()}; |
} |
state_ = State::INITIALIZED; |