diff options
Diffstat (limited to 'chrome/browser/sync/util/cryptographer.cc')
-rw-r--r-- | chrome/browser/sync/util/cryptographer.cc | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/chrome/browser/sync/util/cryptographer.cc b/chrome/browser/sync/util/cryptographer.cc new file mode 100644 index 0000000..0fbaa3a --- /dev/null +++ b/chrome/browser/sync/util/cryptographer.cc @@ -0,0 +1,157 @@ +// Copyright (c) 2010 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 "chrome/browser/sync/util/cryptographer.h" + +namespace browser_sync { + +const char kNigoriTag[] = "nigori"; + +// We name a particular Nigori instance (ie. a triplet consisting of a hostname, +// a username, and a password) by calling Permute on this string. Since the +// output of Permute is always the same for a given triplet, clients will always +// assign the same name to a particular triplet. +const char kNigoriKeyName[] = "nigori-key"; + +Cryptographer::Cryptographer() { +} + +bool Cryptographer::CanDecrypt(const sync_pb::EncryptedData& data) const { + return nigoris_.end() != nigoris_.find(data.key_name()); +} + +bool Cryptographer::Encrypt(const ::google::protobuf::MessageLite& message, + sync_pb::EncryptedData* encrypted) const { + DCHECK(encrypted); + DCHECK(default_nigori_); + + std::string serialized; + if (!message.SerializeToString(&serialized)) { + NOTREACHED(); // |message| is invalid/missing a required field. + return false; + } + + encrypted->set_key_name(default_nigori_->first); + if (!default_nigori_->second->Encrypt(serialized, + encrypted->mutable_blob())) { + NOTREACHED(); // Encrypt should not fail. + return false; + } + return true; +} + +bool Cryptographer::Decrypt(const sync_pb::EncryptedData& encrypted, + ::google::protobuf::MessageLite* message) const { + DCHECK(message); + + NigoriMap::const_iterator it = nigoris_.find(encrypted.key_name()); + if (nigoris_.end() == it) { + NOTREACHED() << "Cannot decrypt message"; + return false; // Caller should have called CanDecrypt(encrypt). + } + + std::string plaintext; + if (!it->second->Decrypt(encrypted.blob(), &plaintext)) { + return false; + } + + return message->ParseFromString(plaintext); +} + +bool Cryptographer::GetKeys(sync_pb::EncryptedData* encrypted) const { + DCHECK(encrypted); + DCHECK(!nigoris_.empty()); + + // Create a bag of all the Nigori parameters we know about. + sync_pb::NigoriKeyBag bag; + for (NigoriMap::const_iterator it = nigoris_.begin(); it != nigoris_.end(); + ++it) { + const Nigori& nigori = *it->second; + sync_pb::NigoriKey* key = bag.add_key(); + key->set_name(it->first); + key->set_hostname(nigori.hostname()); + key->set_username(nigori.username()); + key->set_password(nigori.password()); + } + + // Encrypt the bag with the default Nigori. + return Encrypt(bag, encrypted); +} + +bool Cryptographer::AddKey(const KeyParams& params) { + DCHECK(NULL == pending_keys_.get()); + + // Create the new Nigori and make it the default encryptor. + scoped_ptr<Nigori> nigori(new Nigori(params.hostname)); + if (!nigori->Init(params.username, params.password)) { + NOTREACHED(); // Invalid username or password. + return false; + } + std::string name; + if (!nigori->Permute(Nigori::Password, kNigoriKeyName, &name)) { + NOTREACHED(); + return false; + } + nigoris_[name] = make_linked_ptr(nigori.release()); + default_nigori_ = &*nigoris_.find(name); + return true; +} + +bool Cryptographer::SetKeys(const sync_pb::EncryptedData& encrypted) { + DCHECK(CanDecrypt(encrypted)); + + sync_pb::NigoriKeyBag bag; + if (!Decrypt(encrypted, &bag)) { + return false; + } + InstallKeys(encrypted.key_name(), bag); + return true; +} + +void Cryptographer::SetPendingKeys(const sync_pb::EncryptedData& encrypted) { + DCHECK(!CanDecrypt(encrypted)); + pending_keys_.reset(new sync_pb::EncryptedData(encrypted)); +} + +bool Cryptographer::DecryptPendingKeys(const KeyParams& params) { + Nigori nigori(params.hostname); + if (!nigori.Init(params.username, params.password)) { + NOTREACHED(); + return false; + } + + std::string plaintext; + if (!nigori.Decrypt(pending_keys_->blob(), &plaintext)) + return false; + + sync_pb::NigoriKeyBag bag; + if (!bag.ParseFromString(plaintext)) { + NOTREACHED(); + return false; + } + InstallKeys(pending_keys_->key_name(), bag); + pending_keys_.reset(); + return true; +} + +void Cryptographer::InstallKeys(const std::string& default_key_name, + const sync_pb::NigoriKeyBag& bag) { + int key_size = bag.key_size(); + for (int i = 0; i < key_size; ++i) { + const sync_pb::NigoriKey key = bag.key(i); + // Only use this key if we don't already know about it. + if (nigoris_.end() == nigoris_.find(key.name())) { + scoped_ptr<Nigori> new_nigori(new Nigori(key.hostname())); + if (!new_nigori->Init(key.username(), key.password())) { + NOTREACHED(); + continue; + } + nigoris_[key.name()] = make_linked_ptr(new_nigori.release()); + } + } + DCHECK(nigoris_.end() != nigoris_.find(default_key_name)); + default_nigori_ = &*nigoris_.find(default_key_name); +} + +} // namespace browser_sync |