123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- // Copyright 2020 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "components/web_package/web_bundle_builder.h"
- #include <string.h>
- #include <ostream>
- #include "base/big_endian.h"
- namespace web_package {
- namespace {
- cbor::Value CreateByteString(base::StringPiece s) {
- return cbor::Value(base::as_bytes(base::make_span(s)));
- }
- cbor::Value CreateHeaderMap(const WebBundleBuilder::Headers& headers) {
- cbor::Value::MapValue map;
- for (const auto& pair : headers)
- map.insert({CreateByteString(pair.first), CreateByteString(pair.second)});
- return cbor::Value(std::move(map));
- }
- // TODO(myrzakereyms): replace this method with cbor::writer::GetNumUintBytes.
- uint64_t GetNumUintBytes(uint64_t value) {
- if (value < 24) {
- return 0;
- } else if (value <= 0xFF) {
- return 1;
- } else if (value <= 0xFFFF) {
- return 2;
- } else if (value <= 0xFFFFFFFF) {
- return 4;
- }
- return 8;
- }
- } // namespace
- WebBundleBuilder::WebBundleBuilder(BundleVersion version,
- bool allow_invalid_utf8_strings_for_testing)
- : version_(version) {
- // Currently the only supported bundle format is b2.
- DCHECK_EQ(version_, BundleVersion::kB2);
- writer_config_.allow_invalid_utf8_for_testing =
- allow_invalid_utf8_strings_for_testing;
- }
- WebBundleBuilder::~WebBundleBuilder() = default;
- cbor::Value WebBundleBuilder::GetCborValueOfURL(base::StringPiece url) {
- if (writer_config_.allow_invalid_utf8_for_testing) {
- return cbor::Value::InvalidUTF8StringValueForTesting(url);
- }
- return cbor::Value(url);
- }
- void WebBundleBuilder::AddExchange(base::StringPiece url,
- const Headers& response_headers,
- base::StringPiece payload) {
- AddIndexEntry(url, AddResponse(response_headers, payload));
- }
- WebBundleBuilder::ResponseLocation WebBundleBuilder::AddResponse(
- const Headers& headers,
- base::StringPiece payload) {
- cbor::Value::ArrayValue response_array;
- response_array.emplace_back(Encode(CreateHeaderMap(headers)));
- response_array.emplace_back(CreateByteString(payload));
- cbor::Value response(response_array);
- int64_t response_length = EncodedLength(response);
- ResponseLocation result = {current_responses_offset_, response_length};
- current_responses_offset_ += response_length;
- responses_.emplace_back(std::move(response));
- return result;
- }
- void WebBundleBuilder::AddIndexEntry(
- base::StringPiece url,
- const ResponseLocation& response_location) {
- delayed_index_.insert({std::string{url}, response_location});
- }
- void WebBundleBuilder::AddSection(base::StringPiece name, cbor::Value section) {
- section_lengths_.emplace_back(name);
- section_lengths_.emplace_back(EncodedLength(section));
- sections_.emplace_back(std::move(section));
- }
- void WebBundleBuilder::AddAuthority(cbor::Value::MapValue authority) {
- authorities_.emplace_back(std::move(authority));
- }
- void WebBundleBuilder::AddVouchedSubset(cbor::Value::MapValue vouched_subset) {
- vouched_subsets_.emplace_back(std::move(vouched_subset));
- }
- void WebBundleBuilder::AddPrimaryURL(base::StringPiece url) {
- AddSection("primary", GetCborValueOfURL(url));
- }
- std::vector<uint8_t> WebBundleBuilder::CreateBundle() {
- // Now that we know how many responses will be in the bundle,
- // we want to shift all the offsets by the bytes required
- // for the CBOR Array header and actually construct the index
- // section.
- int64_t initial_offset = 1 + GetNumUintBytes(responses_.size());
- cbor::Value::MapValue index;
- for (auto& entry : delayed_index_) {
- const ResponseLocation& location = entry.second;
- cbor::Value::ArrayValue index_value_array;
- index_value_array.emplace_back(location.offset + initial_offset);
- index_value_array.emplace_back(location.length);
- index.insert(
- {GetCborValueOfURL(entry.first), cbor::Value(index_value_array)});
- }
- AddSection("index", cbor::Value(index));
- if (!authorities_.empty() || !vouched_subsets_.empty()) {
- cbor::Value::ArrayValue signatures_section;
- signatures_section.emplace_back(std::move(authorities_));
- signatures_section.emplace_back(std::move(vouched_subsets_));
- AddSection("signatures", cbor::Value(std::move(signatures_section)));
- }
- AddSection("responses", cbor::Value(responses_));
- return CreateTopLevel();
- }
- cbor::Value WebBundleBuilder::CreateEncodedSigned(
- base::StringPiece validity_url,
- base::StringPiece auth_sha256,
- int64_t date,
- int64_t expires,
- base::StringPiece url,
- base::StringPiece header_sha256,
- base::StringPiece payload_integrity_header) {
- cbor::Value::ArrayValue subset_hash_value;
- subset_hash_value.emplace_back(CreateByteString("")); // variants-value
- subset_hash_value.emplace_back(CreateByteString(header_sha256));
- subset_hash_value.emplace_back(payload_integrity_header);
- cbor::Value::MapValue subset_hashes;
- subset_hashes.emplace(GetCborValueOfURL(url), std::move(subset_hash_value));
- cbor::Value::MapValue signed_subset;
- signed_subset.emplace("validity-url", validity_url);
- signed_subset.emplace("auth-sha256", CreateByteString(auth_sha256));
- signed_subset.emplace("date", date);
- signed_subset.emplace("expires", expires);
- signed_subset.emplace("subset-hashes", std::move(subset_hashes));
- return cbor::Value(Encode(cbor::Value(signed_subset)));
- }
- std::vector<uint8_t> WebBundleBuilder::CreateTopLevel() {
- cbor::Value::ArrayValue toplevel_array;
- toplevel_array.emplace_back(CreateByteString("🌐📦"));
- toplevel_array.emplace_back(CreateByteString(base::StringPiece("b2\0\0", 4)));
- toplevel_array.emplace_back(Encode(cbor::Value(section_lengths_)));
- toplevel_array.emplace_back(sections_);
- // Put a dummy 8-byte bytestring.
- toplevel_array.emplace_back(cbor::Value::BinaryValue(8, 0));
- std::vector<uint8_t> bundle = Encode(cbor::Value(toplevel_array));
- char encoded[8];
- base::WriteBigEndian(encoded, static_cast<uint64_t>(bundle.size()));
- // Overwrite the dummy bytestring with the actual size.
- memcpy(bundle.data() + bundle.size() - 8, encoded, 8);
- return bundle;
- }
- std::vector<uint8_t> WebBundleBuilder::Encode(const cbor::Value& value) {
- return *cbor::Writer::Write(value, writer_config_);
- }
- int64_t WebBundleBuilder::EncodedLength(const cbor::Value& value) {
- return Encode(value).size();
- }
- } // namespace web_package
|