// Copyright 2021 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/test/fake_cryptographer.h"

#include <algorithm>
#include <iterator>

#include "base/containers/span.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "components/sync/protocol/encryption.pb.h"

namespace syncer {

namespace {

constexpr char kSeparator[] = "@@";

}  // namespace

// static
std::unique_ptr<FakeCryptographer> FakeCryptographer::FromSingleDefaultKey(
    const std::string& key_name) {
  auto cryptographer = std::make_unique<FakeCryptographer>();
  cryptographer->AddEncryptionKey(key_name);
  cryptographer->SelectDefaultEncryptionKey(key_name);
  return cryptographer;
}

FakeCryptographer::FakeCryptographer() = default;

FakeCryptographer::~FakeCryptographer() = default;

void FakeCryptographer::AddEncryptionKey(const std::string& key_name) {
  DCHECK(!key_name.empty());
  known_key_names_.insert(key_name);
}

void FakeCryptographer::SelectDefaultEncryptionKey(
    const std::string& key_name) {
  DCHECK(known_key_names_.contains(key_name));
  default_key_name_ = key_name;
}

void FakeCryptographer::InvalidateDefaultEncryptionKey() {
  default_key_name_.clear();
}

bool FakeCryptographer::CanEncrypt() const {
  return !default_key_name_.empty();
}

bool FakeCryptographer::CanDecrypt(
    const sync_pb::EncryptedData& encrypted) const {
  return known_key_names_.contains(encrypted.key_name());
}

std::string FakeCryptographer::GetDefaultEncryptionKeyName() const {
  return default_key_name_;
}

bool FakeCryptographer::EncryptString(const std::string& decrypted,
                                      sync_pb::EncryptedData* encrypted) const {
  if (default_key_name_.empty()) {
    return false;
  }
  *encrypted->mutable_key_name() = default_key_name_;
  *encrypted->mutable_blob() =
      base::StrCat({default_key_name_, kSeparator, decrypted});
  return true;
}

bool FakeCryptographer::DecryptToString(const sync_pb::EncryptedData& encrypted,
                                        std::string* decrypted) const {
  auto key_iter = std::ranges::find(known_key_names_, encrypted.key_name());
  if (key_iter == known_key_names_.end()) {
    return false;
  }

  std::string key_name_and_separator = base::StrCat({*key_iter, kSeparator});
  if (!base::StartsWith(encrypted.blob(), key_name_and_separator)) {
    // Corrupted blob.
    return false;
  }

  *decrypted =
      std::string(encrypted.blob().begin() + key_name_and_separator.size(),
                  encrypted.blob().end());
  return true;
}

std::array<uint8_t, 32> FakeCryptographer::GetCrossUserSharingRawPublicKey()
    const {
  return cross_user_sharing_key_pair_.GetRawPublicKey();
}

std::optional<std::vector<uint8_t>>
FakeCryptographer::AuthEncryptForCrossUserSharing(
    base::span<const uint8_t> plaintext,
    base::span<const uint8_t> recipient_public_key) const {
  // Just join two parts of the data. Note that sender's private key is omitted
  // here for simplicity.
  std::vector<uint8_t> result;
  result.reserve(plaintext.size() + recipient_public_key.size());
  std::ranges::copy(recipient_public_key, std::back_inserter(result));
  std::ranges::copy(plaintext, std::back_inserter(result));
  return result;
}

std::optional<std::vector<uint8_t>>
FakeCryptographer::AuthDecryptForCrossUserSharing(
    base::span<const uint8_t> encrypted_data,
    base::span<const uint8_t> sender_public_key,
    const uint32_t recipient_key_version) const {
  // `encrypted_data` is expected to contain receiver's public key as a prefix,
  // and the actual data. `sender_public_key` is not used for simplicity. In
  // real case, sender's private key is used during encryption and sender's
  // public key during decryption.
  if (encrypted_data.size() <
      cross_user_sharing_key_pair_.GetRawPublicKey().size()) {
    return std::nullopt;
  }

  // Verify that the prefix contains an expected public key.
  if (std::ranges::equal(
          cross_user_sharing_key_pair_.GetRawPublicKey().begin(),
          cross_user_sharing_key_pair_.GetRawPublicKey().end(),
          encrypted_data.begin(),
          encrypted_data.begin() +
              cross_user_sharing_key_pair_.GetRawPublicKey().size())) {
    return std::nullopt;
  }
  return std::vector<uint8_t>(
      encrypted_data.begin() +
          cross_user_sharing_key_pair_.GetRawPublicKey().size(),
      encrypted_data.end());
}

}  // namespace syncer
