blob: 30d972ad5554f5ebb1333fdc64bb8b7f9a6c7743 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/sync/nigori/nigori_key_bag.h"
#include <utility>
#include "base/logging.h"
#include "components/sync/nigori/nigori.h"
#include "components/sync/protocol/nigori_specifics.pb.h"
namespace syncer {
namespace {
std::string ComputeNigoriName(const Nigori& nigori) {
std::string key_name;
nigori.Permute(Nigori::Password, kNigoriKeyName, &key_name);
return key_name;
}
// Note that |key_name| is redundant but computing the name from |nigori| can be
// expensive.
sync_pb::NigoriKey NigoriToProto(const Nigori& nigori,
const std::string& key_name) {
DCHECK_EQ(key_name, ComputeNigoriName(nigori));
sync_pb::NigoriKey proto;
proto.set_deprecated_name(key_name);
nigori.ExportKeys(proto.mutable_deprecated_user_key(),
proto.mutable_encryption_key(), proto.mutable_mac_key());
return proto;
}
std::unique_ptr<Nigori> CloneNigori(const Nigori& nigori) {
std::string user_key;
std::string encryption_key;
std::string mac_key;
nigori.ExportKeys(&user_key, &encryption_key, &mac_key);
std::unique_ptr<Nigori> nigori_copy =
Nigori::CreateByImport(user_key, encryption_key, mac_key);
DCHECK(nigori_copy);
return nigori_copy;
}
} // namespace
// static
NigoriKeyBag NigoriKeyBag::CreateEmpty() {
return NigoriKeyBag();
}
// static
NigoriKeyBag NigoriKeyBag::CreateFromProto(const sync_pb::NigoriKeyBag& proto) {
NigoriKeyBag output;
for (const sync_pb::NigoriKey& key : proto.key()) {
if (output.AddKeyFromProto(key).empty()) {
// TODO(crbug.com/922900): Consider propagating this error to callers such
// that they can do smarter handling.
DLOG(ERROR) << "Invalid NigoriKey protocol buffer message.";
}
}
return output;
}
NigoriKeyBag::NigoriKeyBag(NigoriKeyBag&& other) = default;
NigoriKeyBag::~NigoriKeyBag() = default;
void NigoriKeyBag::CopyFrom(const NigoriKeyBag& other) {
nigori_map_.clear();
AddAllUnknownKeysFrom(other);
}
sync_pb::NigoriKeyBag NigoriKeyBag::ToProto() const {
sync_pb::NigoriKeyBag output;
for (const auto& key_name_and_nigori : nigori_map_) {
*output.add_key() =
NigoriToProto(*key_name_and_nigori.second, key_name_and_nigori.first);
}
return output;
}
NigoriKeyBag NigoriKeyBag::Clone() const {
NigoriKeyBag copy;
copy.AddAllUnknownKeysFrom(*this);
return copy;
}
size_t NigoriKeyBag::size() const {
return nigori_map_.size();
}
bool NigoriKeyBag::HasKey(const std::string& key_name) const {
return nigori_map_.count(key_name) != 0;
}
sync_pb::NigoriKey NigoriKeyBag::ExportKey(const std::string& key_name) const {
DCHECK(HasKey(key_name));
sync_pb::NigoriKey key =
NigoriToProto(*nigori_map_.find(key_name)->second, key_name);
// For exported keys, clients never consumed the key name, so it's safe to
// clear the deprecated field.
key.clear_deprecated_name();
return key;
}
std::string NigoriKeyBag::AddKey(std::unique_ptr<Nigori> nigori) {
DCHECK(nigori);
const std::string key_name = ComputeNigoriName(*nigori);
if (key_name.empty()) {
NOTREACHED();
return key_name;
}
nigori_map_.emplace(key_name, std::move(nigori));
return key_name;
}
std::string NigoriKeyBag::AddKeyFromProto(const sync_pb::NigoriKey& key) {
std::unique_ptr<Nigori> nigori = Nigori::CreateByImport(
key.deprecated_user_key(), key.encryption_key(), key.mac_key());
if (!nigori) {
return std::string();
}
const std::string key_name = ComputeNigoriName(*nigori);
if (key_name.empty()) {
return std::string();
}
nigori_map_[key_name] = std::move(nigori);
return key_name;
}
void NigoriKeyBag::AddAllUnknownKeysFrom(const NigoriKeyBag& other) {
for (const auto& key_name_and_nigori : other.nigori_map_) {
// Only use this key if we don't already know about it.
nigori_map_.emplace(key_name_and_nigori.first,
CloneNigori(*key_name_and_nigori.second));
}
}
bool NigoriKeyBag::EncryptWithKey(
const std::string& key_name,
const std::string& input,
sync_pb::EncryptedData* encrypted_output) const {
DCHECK(encrypted_output);
DCHECK(HasKey(key_name));
encrypted_output->Clear();
if (!nigori_map_.find(key_name)->second->Encrypt(
input, encrypted_output->mutable_blob())) {
DLOG(ERROR) << "Failed to encrypt data.";
return false;
}
encrypted_output->set_key_name(key_name);
return true;
}
bool NigoriKeyBag::CanDecrypt(
const sync_pb::EncryptedData& encrypted_input) const {
return HasKey(encrypted_input.key_name());
}
bool NigoriKeyBag::Decrypt(const sync_pb::EncryptedData& encrypted_input,
std::string* decrypted_output) const {
DCHECK(decrypted_output);
decrypted_output->clear();
auto it = nigori_map_.find(encrypted_input.key_name());
if (it == nigori_map_.end()) {
// The key used to encrypt the blob is not part of the set of installed
// nigoris.
return false;
}
return it->second->Decrypt(encrypted_input.blob(), decrypted_output);
}
NigoriKeyBag::NigoriKeyBag() = default;
} // namespace syncer