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

#include "extensions/browser/content_hash_reader.h"

#include "base/types/optional_util.h"
#include "crypto/hash.h"
#include "extensions/browser/computed_hashes.h"
#include "extensions/browser/content_hash_tree.h"
#include "extensions/browser/content_verifier/content_hash.h"

namespace extensions {

ContentHashData::ContentHashData(size_t block_size,
                                 std::vector<std::string> hashes)
    : block_size(block_size), hashes(std::move(hashes)) {}
ContentHashData::~ContentHashData() = default;

ContentHashData::ContentHashData(ContentHashData&&) = default;
ContentHashData& ContentHashData::operator=(ContentHashData&&) = default;

base::expected<ContentHashData, ContentHashReaderInitStatus> ReadContentHashes(
    const base::FilePath& relative_path,
    const scoped_refptr<const ContentHash>& content_hash) {
  ComputedHashes::Status hashes_status = content_hash->computed_hashes_status();
  if (hashes_status == ComputedHashes::Status::UNKNOWN ||
      hashes_status == ComputedHashes::Status::READ_FAILED) {
    // Failure: no hashes at all.
    return base::unexpected(ContentHashReaderInitStatus::HASHES_MISSING);
  }
  if (hashes_status == ComputedHashes::Status::PARSE_FAILED) {
    // Failure: hashes are unreadable.
    return base::unexpected(ContentHashReaderInitStatus::HASHES_DAMAGED);
  }
  DCHECK_EQ(ComputedHashes::Status::SUCCESS, hashes_status);

  const ComputedHashes& computed_hashes = content_hash->computed_hashes();
  std::optional<std::string> root;

  size_t block_size;
  std::vector<std::string> block_hashes;

  if (computed_hashes.GetHashes(relative_path, &block_size, &block_hashes) &&
      block_size % crypto::hash::kSha256Size == 0) {
    root = ComputeTreeHashRoot(block_hashes,
                               block_size / crypto::hash::kSha256Size);
  }

  ContentHash::TreeHashVerificationResult verification =
      content_hash->VerifyTreeHashRoot(relative_path,
                                       base::OptionalToPtr(root));
  switch (verification) {
    case ContentHash::TreeHashVerificationResult::SUCCESS:
      return ContentHashData(block_size, std::move(block_hashes));
    case ContentHash::TreeHashVerificationResult::NO_ENTRY:
      return base::unexpected(
          ContentHashReaderInitStatus::NO_HASHES_FOR_RESOURCE);
    case ContentHash::TreeHashVerificationResult::HASH_MISMATCH:
      return base::unexpected(ContentHashReaderInitStatus::HASHES_DAMAGED);
  }
}

}  // namespace extensions
