web_bundle_builder.cc 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/web_package/web_bundle_builder.h"
  5. #include <string.h>
  6. #include <ostream>
  7. #include "base/big_endian.h"
  8. namespace web_package {
  9. namespace {
  10. cbor::Value CreateByteString(base::StringPiece s) {
  11. return cbor::Value(base::as_bytes(base::make_span(s)));
  12. }
  13. cbor::Value CreateHeaderMap(const WebBundleBuilder::Headers& headers) {
  14. cbor::Value::MapValue map;
  15. for (const auto& pair : headers)
  16. map.insert({CreateByteString(pair.first), CreateByteString(pair.second)});
  17. return cbor::Value(std::move(map));
  18. }
  19. // TODO(myrzakereyms): replace this method with cbor::writer::GetNumUintBytes.
  20. uint64_t GetNumUintBytes(uint64_t value) {
  21. if (value < 24) {
  22. return 0;
  23. } else if (value <= 0xFF) {
  24. return 1;
  25. } else if (value <= 0xFFFF) {
  26. return 2;
  27. } else if (value <= 0xFFFFFFFF) {
  28. return 4;
  29. }
  30. return 8;
  31. }
  32. } // namespace
  33. WebBundleBuilder::WebBundleBuilder(BundleVersion version,
  34. bool allow_invalid_utf8_strings_for_testing)
  35. : version_(version) {
  36. // Currently the only supported bundle format is b2.
  37. DCHECK_EQ(version_, BundleVersion::kB2);
  38. writer_config_.allow_invalid_utf8_for_testing =
  39. allow_invalid_utf8_strings_for_testing;
  40. }
  41. WebBundleBuilder::~WebBundleBuilder() = default;
  42. cbor::Value WebBundleBuilder::GetCborValueOfURL(base::StringPiece url) {
  43. if (writer_config_.allow_invalid_utf8_for_testing) {
  44. return cbor::Value::InvalidUTF8StringValueForTesting(url);
  45. }
  46. return cbor::Value(url);
  47. }
  48. void WebBundleBuilder::AddExchange(base::StringPiece url,
  49. const Headers& response_headers,
  50. base::StringPiece payload) {
  51. AddIndexEntry(url, AddResponse(response_headers, payload));
  52. }
  53. WebBundleBuilder::ResponseLocation WebBundleBuilder::AddResponse(
  54. const Headers& headers,
  55. base::StringPiece payload) {
  56. cbor::Value::ArrayValue response_array;
  57. response_array.emplace_back(Encode(CreateHeaderMap(headers)));
  58. response_array.emplace_back(CreateByteString(payload));
  59. cbor::Value response(response_array);
  60. int64_t response_length = EncodedLength(response);
  61. ResponseLocation result = {current_responses_offset_, response_length};
  62. current_responses_offset_ += response_length;
  63. responses_.emplace_back(std::move(response));
  64. return result;
  65. }
  66. void WebBundleBuilder::AddIndexEntry(
  67. base::StringPiece url,
  68. const ResponseLocation& response_location) {
  69. delayed_index_.insert({std::string{url}, response_location});
  70. }
  71. void WebBundleBuilder::AddSection(base::StringPiece name, cbor::Value section) {
  72. section_lengths_.emplace_back(name);
  73. section_lengths_.emplace_back(EncodedLength(section));
  74. sections_.emplace_back(std::move(section));
  75. }
  76. void WebBundleBuilder::AddAuthority(cbor::Value::MapValue authority) {
  77. authorities_.emplace_back(std::move(authority));
  78. }
  79. void WebBundleBuilder::AddVouchedSubset(cbor::Value::MapValue vouched_subset) {
  80. vouched_subsets_.emplace_back(std::move(vouched_subset));
  81. }
  82. void WebBundleBuilder::AddPrimaryURL(base::StringPiece url) {
  83. AddSection("primary", GetCborValueOfURL(url));
  84. }
  85. std::vector<uint8_t> WebBundleBuilder::CreateBundle() {
  86. // Now that we know how many responses will be in the bundle,
  87. // we want to shift all the offsets by the bytes required
  88. // for the CBOR Array header and actually construct the index
  89. // section.
  90. int64_t initial_offset = 1 + GetNumUintBytes(responses_.size());
  91. cbor::Value::MapValue index;
  92. for (auto& entry : delayed_index_) {
  93. const ResponseLocation& location = entry.second;
  94. cbor::Value::ArrayValue index_value_array;
  95. index_value_array.emplace_back(location.offset + initial_offset);
  96. index_value_array.emplace_back(location.length);
  97. index.insert(
  98. {GetCborValueOfURL(entry.first), cbor::Value(index_value_array)});
  99. }
  100. AddSection("index", cbor::Value(index));
  101. if (!authorities_.empty() || !vouched_subsets_.empty()) {
  102. cbor::Value::ArrayValue signatures_section;
  103. signatures_section.emplace_back(std::move(authorities_));
  104. signatures_section.emplace_back(std::move(vouched_subsets_));
  105. AddSection("signatures", cbor::Value(std::move(signatures_section)));
  106. }
  107. AddSection("responses", cbor::Value(responses_));
  108. return CreateTopLevel();
  109. }
  110. cbor::Value WebBundleBuilder::CreateEncodedSigned(
  111. base::StringPiece validity_url,
  112. base::StringPiece auth_sha256,
  113. int64_t date,
  114. int64_t expires,
  115. base::StringPiece url,
  116. base::StringPiece header_sha256,
  117. base::StringPiece payload_integrity_header) {
  118. cbor::Value::ArrayValue subset_hash_value;
  119. subset_hash_value.emplace_back(CreateByteString("")); // variants-value
  120. subset_hash_value.emplace_back(CreateByteString(header_sha256));
  121. subset_hash_value.emplace_back(payload_integrity_header);
  122. cbor::Value::MapValue subset_hashes;
  123. subset_hashes.emplace(GetCborValueOfURL(url), std::move(subset_hash_value));
  124. cbor::Value::MapValue signed_subset;
  125. signed_subset.emplace("validity-url", validity_url);
  126. signed_subset.emplace("auth-sha256", CreateByteString(auth_sha256));
  127. signed_subset.emplace("date", date);
  128. signed_subset.emplace("expires", expires);
  129. signed_subset.emplace("subset-hashes", std::move(subset_hashes));
  130. return cbor::Value(Encode(cbor::Value(signed_subset)));
  131. }
  132. std::vector<uint8_t> WebBundleBuilder::CreateTopLevel() {
  133. cbor::Value::ArrayValue toplevel_array;
  134. toplevel_array.emplace_back(CreateByteString("🌐📦"));
  135. toplevel_array.emplace_back(CreateByteString(base::StringPiece("b2\0\0", 4)));
  136. toplevel_array.emplace_back(Encode(cbor::Value(section_lengths_)));
  137. toplevel_array.emplace_back(sections_);
  138. // Put a dummy 8-byte bytestring.
  139. toplevel_array.emplace_back(cbor::Value::BinaryValue(8, 0));
  140. std::vector<uint8_t> bundle = Encode(cbor::Value(toplevel_array));
  141. char encoded[8];
  142. base::WriteBigEndian(encoded, static_cast<uint64_t>(bundle.size()));
  143. // Overwrite the dummy bytestring with the actual size.
  144. memcpy(bundle.data() + bundle.size() - 8, encoded, 8);
  145. return bundle;
  146. }
  147. std::vector<uint8_t> WebBundleBuilder::Encode(const cbor::Value& value) {
  148. return *cbor::Writer::Write(value, writer_config_);
  149. }
  150. int64_t WebBundleBuilder::EncodedLength(const cbor::Value& value) {
  151. return Encode(value).size();
  152. }
  153. } // namespace web_package