910e62b5创建于 1月15日历史提交
// Copyright 2024 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/attribution_reporting/aggregatable_debug_reporting_config.h"

#include <stdint.h>

#include <algorithm>
#include <optional>
#include <set>
#include <string>
#include <string_view>
#include <utility>

#include "base/check.h"
#include "base/check_op.h"
#include "base/containers/enum_set.h"
#include "base/functional/function_ref.h"
#include "base/metrics/histogram_functions.h"
#include "base/numerics/safe_conversions.h"
#include "base/types/expected.h"
#include "base/types/expected_macros.h"
#include "base/values.h"
#include "components/attribution_reporting/aggregatable_utils.h"
#include "components/attribution_reporting/constants.h"
#include "components/attribution_reporting/debug_types.h"
#include "components/attribution_reporting/debug_types.mojom.h"
#include "components/attribution_reporting/parsing_utils.h"
#include "third_party/abseil-cpp/absl/numeric/int128.h"

namespace attribution_reporting {

namespace {

using ::attribution_reporting::mojom::DebugDataType;

constexpr char kAggregatableDebugReporting[] = "aggregatable_debug_reporting";
constexpr char kBudget[] = "budget";
constexpr char kDebugData[] = "debug_data";
constexpr char kTypes[] = "types";

using ParseDebugDataTypeFunc =
    base::FunctionRef<base::expected<DebugDataType, ParseError>(
        std::string_view)>;

bool IsValueInRange(int value, std::optional<int> max_value) {
  int effective_max_value = max_value.value_or(kMaxAggregatableValue);
  CHECK_LE(effective_max_value, kMaxAggregatableValue);
  return value > 0 && value <= effective_max_value;
}

base::expected<int, ParseError> ParseValue(const base::Value::Dict& dict,
                                           std::optional<int> max_value) {
  const base::Value* value = dict.Find(kValue);
  if (!value) {
    return base::unexpected(ParseError());
  }

  ASSIGN_OR_RETURN(int int_value, ParseInt(*value));

  if (!IsValueInRange(int_value, max_value)) {
    return base::unexpected(ParseError());
  }
  return int_value;
}

base::expected<int, ParseError> ParseBudget(const base::Value::Dict& dict) {
  const base::Value* value = dict.Find(kBudget);
  if (!value) {
    return base::unexpected(ParseError());
  }
  return ParseAggregatableValue(*value);
}

base::expected<absl::uint128, ParseError> ParseKeyPiece(
    const base::Value::Dict& dict) {
  const base::Value* v = dict.Find(kKeyPiece);
  if (!v) {
    return base::unexpected(ParseError());
  }

  return ParseAggregationKeyPiece(*v);
}

base::expected<void, AggregatableDebugReportingConfigError>
ParseDebugDataElement(base::Value& elem,
                      AggregatableDebugReportingConfig::DebugData& data,
                      std::optional<AggregatableDebugReportingContribution>&
                          unspecified_contribution,
                      std::set<std::string>& unknown_types,
                      std::optional<int> max_value,
                      ParseDebugDataTypeFunc parse_debug_data_type) {
  base::Value::Dict* dict = elem.GetIfDict();
  if (!dict) {
    return base::unexpected(
        AggregatableDebugReportingConfigError::kDebugDataInvalid);
  }

  ASSIGN_OR_RETURN(
      absl::uint128 key_piece,
      ParseKeyPiece(*dict).transform_error([](ParseError) {
        return AggregatableDebugReportingConfigError::kDebugDataKeyPieceInvalid;
      }));

  ASSIGN_OR_RETURN(
      int int_value,
      ParseValue(*dict, max_value).transform_error([](ParseError) {
        return AggregatableDebugReportingConfigError::kDebugDataValueInvalid;
      }));

  base::Value::List* l = dict->FindList(kTypes);
  if (!l || l->empty()) {
    return base::unexpected(
        AggregatableDebugReportingConfigError::kDebugDataTypesInvalid);
  }

  std::optional<AggregatableDebugReportingContribution> contribution =
      AggregatableDebugReportingContribution::Create(key_piece, int_value);
  CHECK(contribution.has_value());

  for (base::Value& v : *l) {
    std::string* s = v.GetIfString();
    if (!s) {
      return base::unexpected(
          AggregatableDebugReportingConfigError::kDebugDataTypesInvalid);
    }
    if (*s == "unspecified") {
      if (unspecified_contribution.has_value()) {
        return base::unexpected(
            AggregatableDebugReportingConfigError::kDebugDataTypesInvalid);
      }
      unspecified_contribution.emplace(*contribution);
      continue;
    }
    if (auto type = parse_debug_data_type(*s); type.has_value()) {
      auto [_, inserted] = data.try_emplace(*type, *contribution);
      if (!inserted) {
        return base::unexpected(
            AggregatableDebugReportingConfigError::kDebugDataTypesInvalid);
      }
    } else {
      auto [_, inserted] = unknown_types.emplace(std::move(*s));
      if (!inserted) {
        return base::unexpected(
            AggregatableDebugReportingConfigError::kDebugDataTypesInvalid);
      }
    }
  }

  return base::ok();
}

base::expected<AggregatableDebugReportingConfig::DebugData,
               AggregatableDebugReportingConfigError>
ParseDebugData(base::Value::Dict& dict,
               std::optional<int> max_value,
               const DebugDataTypes& debug_data_types,
               ParseDebugDataTypeFunc parse_debug_data_type) {
  base::Value* value = dict.Find(kDebugData);
  if (!value) {
    return {};
  }

  base::Value::List* l = value->GetIfList();
  if (!l) {
    return base::unexpected(
        AggregatableDebugReportingConfigError::kDebugDataInvalid);
  }

  AggregatableDebugReportingConfig::DebugData data;
  std::optional<AggregatableDebugReportingContribution>
      unspecified_contribution;

  for (std::set<std::string> unknown_types; base::Value& v : *l) {
    RETURN_IF_ERROR(ParseDebugDataElement(v, data, unspecified_contribution,
                                          unknown_types, max_value,
                                          parse_debug_data_type));
  }

  if (unspecified_contribution.has_value()) {
    for (DebugDataType type : debug_data_types) {
      data.try_emplace(type, *unspecified_contribution);
    }
  }

  return data;
}

void SerializeConfig(base::Value::Dict& dict,
                     const AggregatableDebugReportingConfig& config) {
  dict.Set(kKeyPiece, HexEncodeAggregationKey(config.key_piece));

  if (config.aggregation_coordinator_origin.has_value()) {
    dict.Set(kAggregationCoordinatorOrigin,
             config.aggregation_coordinator_origin->Serialize());
  }

  if (!config.debug_data.empty()) {
    auto list = base::Value::List::with_capacity(config.debug_data.size());
    for (const auto& [type, contribution] : config.debug_data) {
      CHECK(base::IsValueInRangeForNumericType<int>(contribution.value()));

      list.Append(
          base::Value::Dict()
              .Set(kKeyPiece, HexEncodeAggregationKey(contribution.key_piece()))
              .Set(kValue, static_cast<int>(contribution.value()))
              .Set(kTypes,
                   base::Value::List().Append(SerializeDebugDataType(type))));
    }
    dict.Set(kDebugData, std::move(list));
  }
}

bool IsValid(int budget,
             const AggregatableDebugReportingConfig::DebugData& data) {
  if (!IsAggregatableBudgetInRange(budget)) {
    return false;
  }

  return std::ranges::all_of(data, [&](const auto& p) {
    return IsValueInRange(p.second.value(), budget);
  });
}

base::expected<AggregatableDebugReportingConfig,
               AggregatableDebugReportingConfigError>
ParseConfig(base::Value::Dict& dict,
            std::optional<int> max_value,
            const DebugDataTypes& debug_data_types,
            ParseDebugDataTypeFunc parse_debug_data_type) {
  ASSIGN_OR_RETURN(
      auto key_piece, ParseKeyPiece(dict).transform_error([](ParseError) {
        return AggregatableDebugReportingConfigError::kKeyPieceInvalid;
      }));
  ASSIGN_OR_RETURN(
      auto aggregation_coordinator_origin,
      ParseAggregationCoordinator(dict).transform_error([](ParseError) {
        return AggregatableDebugReportingConfigError::
            kAggregationCoordinatorOriginInvalid;
      }));
  ASSIGN_OR_RETURN(auto data, ParseDebugData(dict, max_value, debug_data_types,
                                             parse_debug_data_type));

  return AggregatableDebugReportingConfig(
      key_piece, std::move(data), std::move(aggregation_coordinator_origin));
}

base::expected<SourceAggregatableDebugReportingConfig,
               AggregatableDebugReportingConfigError>
ParseSourceConfig(base::Value::Dict& dict) {
  base::Value* v = dict.Find(kAggregatableDebugReporting);
  if (!v) {
    return SourceAggregatableDebugReportingConfig();
  }

  base::Value::Dict* d = v->GetIfDict();
  if (!d) {
    return base::unexpected(
        AggregatableDebugReportingConfigError::kRootInvalid);
  }

  ASSIGN_OR_RETURN(int budget, ParseBudget(*d), [](ParseError) {
    return AggregatableDebugReportingConfigError::kBudgetInvalid;
  });

  ASSIGN_OR_RETURN(auto config, ParseConfig(*d, budget, SourceDebugDataTypes(),
                                            &ParseSourceDebugDataType));

  auto source_config =
      SourceAggregatableDebugReportingConfig::Create(budget, std::move(config));
  CHECK(source_config.has_value());
  return *std::move(source_config);
}

base::expected<AggregatableDebugReportingConfig,
               AggregatableDebugReportingConfigError>
ParseTriggerConfig(base::Value::Dict& dict) {
  base::Value* v = dict.Find(kAggregatableDebugReporting);
  if (!v) {
    return AggregatableDebugReportingConfig();
  }

  base::Value::Dict* d = v->GetIfDict();
  if (!d) {
    return base::unexpected(
        AggregatableDebugReportingConfigError::kRootInvalid);
  }

  return ParseConfig(*d, /*max_value=*/std::nullopt, TriggerDebugDataTypes(),
                     &ParseTriggerDebugDataType);
}

}  // namespace

// static
std::optional<AggregatableDebugReportingContribution>
AggregatableDebugReportingContribution::Create(absl::uint128 key_piece,
                                               uint32_t value) {
  if (!IsValueInRange(value, /*max_value=*/std::nullopt)) {
    return std::nullopt;
  }
  return AggregatableDebugReportingContribution(key_piece, value);
}

AggregatableDebugReportingContribution::AggregatableDebugReportingContribution(
    absl::uint128 key_piece,
    uint32_t value)
    : key_piece_(key_piece), value_(value) {
  CHECK(IsValid());
}

bool AggregatableDebugReportingContribution::IsValid() const {
  return IsValueInRange(value_, /*max_value=*/std::nullopt);
}

absl::uint128 AggregatableDebugReportingContribution::key_piece() const {
  CHECK(IsValid());
  return key_piece_;
}

uint32_t AggregatableDebugReportingContribution::value() const {
  CHECK(IsValid());
  return value_;
}

// static
base::expected<AggregatableDebugReportingConfig,
               AggregatableDebugReportingConfigError>
AggregatableDebugReportingConfig::Parse(base::Value::Dict& dict) {
  auto parsed = ParseTriggerConfig(dict);
  if (!parsed.has_value()) {
    base::UmaHistogramEnumeration(
        "Conversions.AggregatableDebugReporting.TriggerRegistrationError",
        parsed.error());
  }
  return parsed;
}

AggregatableDebugReportingConfig::AggregatableDebugReportingConfig() = default;

AggregatableDebugReportingConfig::AggregatableDebugReportingConfig(
    absl::uint128 key_piece,
    DebugData debug_data,
    std::optional<SuitableOrigin> aggregation_coordinator_origin)
    : key_piece(key_piece),
      debug_data(std::move(debug_data)),
      aggregation_coordinator_origin(
          std::move(aggregation_coordinator_origin)) {}

AggregatableDebugReportingConfig::~AggregatableDebugReportingConfig() = default;

AggregatableDebugReportingConfig::AggregatableDebugReportingConfig(
    const AggregatableDebugReportingConfig&) = default;

AggregatableDebugReportingConfig::AggregatableDebugReportingConfig(
    AggregatableDebugReportingConfig&&) = default;

AggregatableDebugReportingConfig& AggregatableDebugReportingConfig::operator=(
    const AggregatableDebugReportingConfig&) = default;

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

void AggregatableDebugReportingConfig::Serialize(
    base::Value::Dict& dict) const {
  base::Value::Dict body;
  SerializeConfig(body, *this);
  dict.Set(kAggregatableDebugReporting, std::move(body));
}

// static
base::expected<SourceAggregatableDebugReportingConfig,
               AggregatableDebugReportingConfigError>
SourceAggregatableDebugReportingConfig::Parse(base::Value::Dict& dict) {
  auto parsed = ParseSourceConfig(dict);
  if (!parsed.has_value()) {
    base::UmaHistogramEnumeration(
        "Conversions.AggregatableDebugReporting.SourceRegistrationError",
        parsed.error());
  }
  return parsed;
}

// static
std::optional<SourceAggregatableDebugReportingConfig>
SourceAggregatableDebugReportingConfig::Create(
    int budget,
    AggregatableDebugReportingConfig config) {
  if (!IsValid(budget, config.debug_data)) {
    return std::nullopt;
  }
  return SourceAggregatableDebugReportingConfig(budget, std::move(config));
}

SourceAggregatableDebugReportingConfig::
    SourceAggregatableDebugReportingConfig() = default;

SourceAggregatableDebugReportingConfig::SourceAggregatableDebugReportingConfig(
    int budget,
    AggregatableDebugReportingConfig config)
    : budget_(budget), config_(std::move(config)) {
  CHECK(IsValid(budget_, config_.debug_data));
}

SourceAggregatableDebugReportingConfig::
    ~SourceAggregatableDebugReportingConfig() = default;

SourceAggregatableDebugReportingConfig::SourceAggregatableDebugReportingConfig(
    const SourceAggregatableDebugReportingConfig&) = default;

SourceAggregatableDebugReportingConfig::SourceAggregatableDebugReportingConfig(
    SourceAggregatableDebugReportingConfig&&) = default;

SourceAggregatableDebugReportingConfig&
SourceAggregatableDebugReportingConfig::operator=(
    const SourceAggregatableDebugReportingConfig&) = default;

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

void SourceAggregatableDebugReportingConfig::Serialize(
    base::Value::Dict& dict) const {
  // `budget_` is 0 when aggregatable debug reporting is not opted in.
  if (budget_ == 0) {
    return;
  }

  base::Value::Dict body;
  body.Set(kBudget, budget_);
  SerializeConfig(body, config_);
  dict.Set(kAggregatableDebugReporting, std::move(body));
}

}  // namespace attribution_reporting