// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/sync/nigori/keystore_keys_cryptographer.h"

#include <utility>

#include "base/check.h"
#include "base/memory/ptr_util.h"
#include "components/sync/model/crypto/key_derivation_params.h"
#include "components/sync/nigori/cryptographer_impl.h"
#include "components/sync/nigori/nigori_key_bag.h"
#include "components/sync/protocol/encryption.pb.h"
#include "components/sync/protocol/nigori_specifics.pb.h"

namespace syncer {

// static
std::unique_ptr<KeystoreKeysCryptographer>
KeystoreKeysCryptographer::CreateEmpty() {
  return base::WrapUnique(new KeystoreKeysCryptographer(
      NigoriKeyBag::CreateEmpty(),
      /*last_keystore_key_name=*/std::string(),
      /*keystore_keys=*/std::vector<std::string>()));
}

// static
std::unique_ptr<KeystoreKeysCryptographer>
KeystoreKeysCryptographer::FromKeystoreKeys(
    const std::vector<std::string>& keystore_keys) {
  if (keystore_keys.empty()) {
    return CreateEmpty();
  }

  NigoriKeyBag key_bag = NigoriKeyBag::CreateEmpty();
  std::string last_key_name;

  for (const std::string& key : keystore_keys) {
    last_key_name = key_bag.AddKey(KeyDerivationParams::CreateForPbkdf2(), key);

    if (last_key_name.empty()) {
      // TODO(crbug.com/40868132): this shouldn't be possible, clean up once
      // lower-level Nigori code explicitly guarantees that.
      return nullptr;
    }
  }

  DCHECK(!last_key_name.empty());

  return base::WrapUnique(new KeystoreKeysCryptographer(
      std::move(key_bag), last_key_name, keystore_keys));
}

KeystoreKeysCryptographer::KeystoreKeysCryptographer(
    NigoriKeyBag key_bag,
    const std::string& last_keystore_key_name,
    const std::vector<std::string>& keystore_keys)
    : key_bag_(std::move(key_bag)),
      last_keystore_key_name_(last_keystore_key_name),
      keystore_keys_(keystore_keys) {}

KeystoreKeysCryptographer::~KeystoreKeysCryptographer() = default;


bool KeystoreKeysCryptographer::IsEmpty() const {
  return keystore_keys_.empty();
}

std::unique_ptr<KeystoreKeysCryptographer> KeystoreKeysCryptographer::Clone()
    const {
  return base::WrapUnique(new KeystoreKeysCryptographer(
      key_bag_.Clone(), last_keystore_key_name_, keystore_keys_));
}

std::unique_ptr<CryptographerImpl>
KeystoreKeysCryptographer::ToCryptographerImpl() const {
  std::unique_ptr<CryptographerImpl> cryptographer =
      CryptographerImpl::CreateEmpty();
  cryptographer->EmplaceKeysFrom(key_bag_);
  if (!last_keystore_key_name_.empty()) {
    cryptographer->SelectDefaultEncryptionKey(last_keystore_key_name_);
  }
  return cryptographer;
}

bool KeystoreKeysCryptographer::EncryptKeystoreDecryptorToken(
    const sync_pb::NigoriKey& keystore_decryptor_key,
    sync_pb::EncryptedData* keystore_decryptor_token) const {
  CHECK(keystore_decryptor_token);
  if (IsEmpty()) {
    return false;
  }

  *keystore_decryptor_token = key_bag_.EncryptWithKey(
      last_keystore_key_name_, keystore_decryptor_key.SerializeAsString());
  return true;
}

bool KeystoreKeysCryptographer::DecryptKeystoreDecryptorToken(
    const sync_pb::EncryptedData& keystore_decryptor_token,
    sync_pb::NigoriKey* keystore_decryptor_key) const {
  std::string serialized_keystore_decryptor_key;
  if (!key_bag_.Decrypt(keystore_decryptor_token,
                        &serialized_keystore_decryptor_key)) {
    return false;
  }
  return keystore_decryptor_key->ParseFromString(
      serialized_keystore_decryptor_key);
}

const NigoriKeyBag& KeystoreKeysCryptographer::GetKeystoreKeybag() const {
  return key_bag_;
}

}  // namespace syncer
