std::vector substituteOuterExtensions()

in fizz/protocol/ech/Encryption.cpp [328:418]


std::vector<Extension> substituteOuterExtensions(
    std::vector<Extension>&& innerExt,
    const std::vector<Extension>& outerExt) {
  std::vector<Extension> expandedInnerExt;

  // validate that the innerExt has no duplicate extensions.
  Protocol::checkDuplicateExtensions(innerExt);

  // locate echOuterExtension
  auto echOuterExtension = std::find_if(
      innerExt.cbegin(), innerExt.cend(), [](const Extension& ext) {
        return ext.extension_type == fizz::ExtensionType::ech_outer_extensions;
      });

  // Return innerExt if no ech_outer_extensions extension was found.
  if (echOuterExtension == innerExt.end()) {
    return std::move(innerExt);
  }

  folly::io::Cursor cursor(echOuterExtension->extension_data.get());

  std::vector<ExtensionType> outerExtsToCopy;
  fizz::detail::readVector<std::uint8_t>(outerExtsToCopy, cursor);

  // Insert all innerExt extensions before ech_outer_extensions.
  std::transform(
      innerExt.cbegin(),
      echOuterExtension,
      std::back_inserter(expandedInnerExt),
      [](const Extension& ext) { return ext.clone(); });

  /**
   * Expand ech_outer_extensions using the following two pointer approach:
   * Iterate over the outerExt and when both pointers have
   * equivalent extensions types, we increment idxInner
   * –indicating that we've validated an extension is present in
   * outerExt and maintains its relative ordering.
   */
  std::size_t idxInner = 0;

  for (const auto& ext : outerExt) {
    if (idxInner >= outerExtsToCopy.size()) {
      break;
    }

    if ((ext.extension_type == outerExtsToCopy[idxInner])) {
      expandedInnerExt.push_back(ext.clone());
      idxInner++;
    }
  }

  /**
   * If we've reached the end of the outerExtensionPtrs, the following
   * requirements have been satisfied:
   *
   * 1. All extensions in outerExtension are present in clientOuterHello
   * 2. The extensions in outerExtension preserve the relative ordering of
   * extensions in clientOuterHello.
   *
   * Otherwise we've violated one of the requirements.
   **/
  if (idxInner < outerExtsToCopy.size()) {
    throw fizz::FizzException(
        "Requirements for OuterExtensions not met.",
        AlertDescription::decode_error);
  }

  // Add all extensions that follow the ech_outer_extensions; resulting vector
  // is the list of substituted extensions
  std::transform(
      std::next(echOuterExtension, 1),
      innerExt.cend(),
      std::back_inserter(expandedInnerExt),
      [](const Extension& ext) { return ext.clone(); });

  // Verify that there are no duplicate extensions.
  std::unordered_set<ExtensionType> expandedExtTypes;

  std::transform(
      expandedInnerExt.cbegin(),
      expandedInnerExt.cend(),
      std::inserter(expandedExtTypes, expandedExtTypes.begin()),
      [](const Extension& ext) { return ext.extension_type; });

  if (expandedExtTypes.size() != expandedInnerExt.size()) {
    throw FizzException(
        "Duplicate extensions", AlertDescription::illegal_parameter);
  }

  return expandedInnerExt;
}