910e62b5创建于 1月15日历史提交
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef DEVICE_FIDO_FIDO_PARSING_UTILS_H_
#define DEVICE_FIDO_FIDO_PARSING_UTILS_H_

#include <stddef.h>
#include <stdint.h>

#include <algorithm>
#include <array>
#include <iterator>
#include <optional>
#include <string_view>
#include <utility>
#include <vector>

#include "base/component_export.h"
#include "base/containers/span.h"
#include "components/cbor/values.h"

namespace device::fido_parsing_utils {

// Comparator object that calls std::lexicographical_compare on the begin and
// end iterators of the passed in ranges. Useful when comparing sequence
// containers that are of different types, but have similar semantics.
struct RangeLess {
  template <typename T, typename U>
  constexpr bool operator()(T&& lhs, U&& rhs) const {
    using std::begin;
    using std::end;
    return std::lexicographical_compare(begin(lhs), end(lhs), begin(rhs),
                                        end(rhs));
  }

  using is_transparent = void;
};

// Returns a materialized copy of |span|, that is, a vector with the same
// elements.
COMPONENT_EXPORT(DEVICE_FIDO)
std::vector<uint8_t> Materialize(base::span<const uint8_t> span);
COMPONENT_EXPORT(DEVICE_FIDO)
std::optional<std::vector<uint8_t>> MaterializeOrNull(
    std::optional<base::span<const uint8_t>> span);

// Returns a materialized copy of the static |span|, that is, an array with the
// same elements.
template <size_t N>
std::array<uint8_t, N> Materialize(base::span<const uint8_t, N> span) {
  std::array<uint8_t, N> array;
  std::ranges::copy(span, array.begin());
  return array;
}

// Appends |in_values| to the end of |target|. The underlying container for
// |in_values| should *not* be |target|.
COMPONENT_EXPORT(DEVICE_FIDO)
void Append(std::vector<uint8_t>* target, base::span<const uint8_t> in_values);

// Safely extracts, with bound checking, a contiguous subsequence of |span| of
// the given |length| and starting at |pos|. Returns an empty vector/span if the
// requested range is out-of-bound.
COMPONENT_EXPORT(DEVICE_FIDO)
std::vector<uint8_t> Extract(base::span<const uint8_t> span,
                             size_t pos,
                             size_t length);
COMPONENT_EXPORT(DEVICE_FIDO)
base::span<const uint8_t> ExtractSpan(base::span<const uint8_t> span,
                                      size_t pos,
                                      size_t length);

// Safely extracts, with bound checking, the suffix of the given |span| starting
// at the given position |pos|. Returns an empty vector/span if the requested
// starting position is out-of-bound.
COMPONENT_EXPORT(DEVICE_FIDO)
std::vector<uint8_t> ExtractSuffix(base::span<const uint8_t> span, size_t pos);

COMPONENT_EXPORT(DEVICE_FIDO)
base::span<const uint8_t> ExtractSuffixSpan(base::span<const uint8_t> span,
                                            size_t pos);

template <size_t N>
bool ExtractArray(base::span<const uint8_t> span,
                  size_t pos,
                  std::array<uint8_t, N>* array) {
  const auto extracted_span = ExtractSpan(span, pos, N);
  if (extracted_span.size() != N)
    return false;

  std::ranges::copy(extracted_span, array->begin());
  return true;
}

// Partitions |span| into N = ⌈span.size() / max_chunk_size⌉ consecutive chunks.
// The first N-1 chunks are of size |max_chunk_size|, and the Nth chunk is of
// size span.size() % max_chunk_size. |max_chunk_size| must be greater than 0.
// Returns an empty vector in case |span| is empty.
COMPONENT_EXPORT(DEVICE_FIDO)
std::vector<base::span<const uint8_t>> SplitSpan(base::span<const uint8_t> span,
                                                 size_t max_chunk_size);

// Convert byte array into GUID formatted string as defined by RFC 4122.
// As we are converting 128 bit UUID, |bytes| must be have length of 16.
// https://tools.ietf.org/html/rfc4122
COMPONENT_EXPORT(DEVICE_FIDO)
std::string ConvertBytesToUuid(base::span<const uint8_t, 16> bytes);

// Copies the contents of the bytestring, keyed by |key|, from |map| into |out|.
// Returns true on success or false if the key if not found, the value is not a
// bytestring, or the value has the wrong length.
template <size_t N>
bool CopyCBORBytestring(std::array<uint8_t, N>* out,
                        const cbor::Value::MapValue& map,
                        int key) {
  const auto it = map.find(cbor::Value(key));
  if (it == map.end() || !it->second.is_bytestring()) {
    return false;
  }
  const std::vector<uint8_t> bytestring = it->second.GetBytestring();
  return ExtractArray(bytestring, /*pos=*/0, out);
}

// Redacts `paths_to_redact` from `cbor` by finding the corresponding keys and
// replacing them by the cbor string "redacted". Nested paths should correspond
// to nested maps under the same key name. The redaction is applied to all array
// elements for a matching key.
// If a path is not found, a clone of `cbor` is returned.
//
// Example:
//
// Given a `cbor` value...
// {
//   characters: [
//     {
//       name: "Reimu",
//       occupation: ["Shrine maiden"]
//     },
//     {
//       name: "Marisa",
//       occupation: ["Witch", "Troublemaker"]
//     }
//   ]
// }
//
// ...and a `paths_to_redact` value...
//
// [
//   ["characters", "occupation"],
//   ["characters", "date-of-birth"],
// ]
//
// ...the returned cbor will be:
//
// {
//   characters: [
//     {
//       name: "Reimu",
//       occupation: "redacted"
//     },
//     {
//       name: "Marisa",
//       occupation: "redacted"
//     }
//   ]
// }
COMPONENT_EXPORT(DEVICE_FIDO)
cbor::Value RedactCbor(
    const cbor::Value& cbor,
    base::span<const std::vector<cbor::Value>> paths_to_redact);

namespace internal {

// These helpers allow us to implement `ToCborVector` without needing to prepend
// values to the vector to satisfy the left recursion, which would be slower.
template <typename T>
void AppendToCborVector(std::vector<cbor::Value>* vec, T value) {
  vec->emplace_back(std::move(value));
}
template <typename T, typename... Args>
void AppendToCborVector(std::vector<cbor::Value>* vec, T first, Args... args) {
  vec->emplace_back(std::move(first));
  AppendToCborVector(vec, args...);
}

}  // namespace internal

// This function makes using `RedactCbor` more readable by moving the parameters
// into a vector of `cbor::Value`s and returning it.
template <typename... Args>
std::vector<cbor::Value> ToCborVector(Args... args) {
  std::vector<cbor::Value> vec;
  internal::AppendToCborVector(&vec, args...);
  return vec;
}

}  // namespace device::fido_parsing_utils

#endif  // DEVICE_FIDO_FIDO_PARSING_UTILS_H_