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

Unified 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 side-by-side diff with in-line comments
Download patch
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..6175f5b470e92e4523b9e2a587363c70af126618 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,8 @@
namespace gcm {
+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
@@ -26,6 +30,18 @@ const char kDatabaseUMAClientName[] = "GCMKeyStore";
// authentication secret. Must be at least 16 bytes.
const size_t kAuthSecretBytes = 16;
+std::string DatabaseKey(const std::string& app_id,
+ const std::string& authorized_entity) {
+ DCHECK_EQ(std::string::npos, app_id.find(','));
+ DCHECK_EQ(std::string::npos, authorized_entity.find(','));
+ DCHECK_NE("*", authorized_entity) << "Wildcards require special handling";
+ return authorized_entity.empty()
+ ? app_id // No comma, for compatibility with existing keys.
+ : app_id + ',' + authorized_entity;
+}
+
+} // namespace
+
enum class GCMKeyStore::State {
UNINITIALIZED,
INITIALIZING,
@@ -46,46 +62,72 @@ GCMKeyStore::GCMKeyStore(
GCMKeyStore::~GCMKeyStore() {}
void GCMKeyStore::GetKeys(const std::string& app_id,
+ const std::string& authorized_entity,
+ bool fallback_to_empty_authorized_entity,
const KeysCallback& callback) {
- LazyInitialize(base::Bind(&GCMKeyStore::GetKeysAfterInitialize,
- weak_factory_.GetWeakPtr(), app_id, callback));
+ LazyInitialize(base::Bind(
+ &GCMKeyStore::GetKeysAfterInitialize, weak_factory_.GetWeakPtr(), app_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& 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(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& authorized_entity,
const KeysCallback& callback) {
LazyInitialize(base::Bind(&GCMKeyStore::CreateKeysAfterInitialize,
- weak_factory_.GetWeakPtr(), app_id, callback));
+ weak_factory_.GetWeakPtr(), app_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& 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
+ // InstanceID tokens can't share an app_id with a non-InstanceID 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 non-InstanceID registrations).
+ DCHECK(!key_data_.count(app_id) ||
+ (!authorized_entity.empty() &&
+ !key_data_[app_id].count(authorized_entity) &&
+ !key_data_[app_id].count(std::string())))
+ << "Instance ID tokens cannot share an app_id with a non-InstanceID GCM "
+ "registration";
std::string private_key, public_key_x509, public_key;
if (!CreateP256KeyPair(&private_key, &public_key_x509, &public_key)) {
@@ -105,6 +147,8 @@ 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 (!authorized_entity.empty())
+ encryption_data.set_authorized_entity(authorized_entity);
encryption_data.set_auth_secret(auth_secret);
KeyPair* pair = encryption_data.add_keys();
@@ -115,8 +159,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][authorized_entity] = {*pair, auth_secret};
using EntryVectorType =
leveldb_proto::ProtoDatabase<EncryptionData>::KeyEntryVector;
@@ -125,7 +168,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, authorized_entity), encryption_data));
database_->UpdateEntries(
std::move(entries_to_save), std::move(keys_to_remove),
@@ -153,31 +197,55 @@ void GCMKeyStore::DidStoreKeys(const KeyPair& pair,
}
void GCMKeyStore::RemoveKeys(const std::string& app_id,
+ const std::string& authorized_entity,
const base::Closure& callback) {
LazyInitialize(base::Bind(&GCMKeyStore::RemoveKeysAfterInitialize,
- weak_factory_.GetWeakPtr(), app_id, callback));
+ weak_factory_.GetWeakPtr(), app_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& 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 (InstanceID only).
+ if (authorized_entity == "*" ? !it->first.empty()
+ : it->first == 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 +315,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 authorized_entity;
+ if (entry.has_authorized_entity())
+ authorized_entity = entry.authorized_entity();
+ key_data_[entry.app_id()][authorized_entity] = {entry.keys(0),
+ entry.auth_secret()};
}
state_ = State::INITIALIZED;
« 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