Add --request_trust_anchors to the QUIC toy client
This new flag lets us control the presence and contents of the `trust_anchors` TLS extension.
PiperOrigin-RevId: 854273831
diff --git a/quiche/quic/tools/quic_toy_client.cc b/quiche/quic/tools/quic_toy_client.cc
index 6ded104..d90c722 100644
--- a/quiche/quic/tools/quic_toy_client.cc
+++ b/quiche/quic/tools/quic_toy_client.cc
@@ -42,29 +42,46 @@
#include "quiche/quic/tools/quic_toy_client.h"
+#include <cstddef>
+#include <cstdint>
#include <fstream>
#include <iostream>
#include <memory>
+#include <optional>
#include <string>
#include <utility>
#include <vector>
+#include "absl/status/status.h"
+#include "absl/status/statusor.h"
#include "absl/strings/escaping.h"
+#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
+#include "openssl/base.h"
+#include "openssl/bytestring.h"
+#include "quiche/quic/core/crypto/certificate_view.h"
+#include "quiche/quic/core/crypto/client_proof_source.h"
+#include "quiche/quic/core/crypto/proof_verifier.h"
#include "quiche/quic/core/crypto/quic_client_session_cache.h"
-#include "quiche/quic/core/quic_packets.h"
-#include "quiche/quic/core/quic_server_id.h"
-#include "quiche/quic/core/quic_utils.h"
+#include "quiche/quic/core/crypto/quic_crypto_client_config.h"
+#include "quiche/quic/core/quic_config.h"
+#include "quiche/quic/core/quic_connection_id.h"
+#include "quiche/quic/core/quic_constants.h"
+#include "quiche/quic/core/quic_error_codes.h"
+#include "quiche/quic/core/quic_tag.h"
+#include "quiche/quic/core/quic_time.h"
#include "quiche/quic/core/quic_versions.h"
#include "quiche/quic/platform/api/quic_default_proof_providers.h"
-#include "quiche/quic/platform/api/quic_ip_address.h"
+#include "quiche/quic/platform/api/quic_logging.h"
#include "quiche/quic/platform/api/quic_socket_address.h"
#include "quiche/quic/tools/fake_proof_verifier.h"
+#include "quiche/quic/tools/quic_spdy_client_base.h"
#include "quiche/quic/tools/quic_url.h"
#include "quiche/common/http/http_header_block.h"
#include "quiche/common/platform/api/quiche_command_line_flags.h"
#include "quiche/common/platform/api/quiche_logging.h"
+#include "quiche/common/platform/api/quiche_reference_counted.h"
#include "quiche/common/quiche_text_utils.h"
namespace {
@@ -171,6 +188,19 @@
"default certificate for signing, if server requested client certs.");
DEFINE_QUICHE_COMMAND_LINE_FLAG(
+ std::string, request_trust_anchors, "",
+ "A space-separated list of Trust Anchor IDs to request from the server via "
+ "the TLS trust_anchors extension, e.g. \"11129.9.1 11129.9.2\". When the "
+ "flag is not specified or its value is the empty string, the trust_anchors "
+ "extension will not be sent. When its value is \"EMPTY_TRUST_ANCHORS\", "
+ "the client will send the trust_anchors extension with an empty value. "
+ "Presently, Trust Anchor ID assignments are recorded here: "
+ // clang-format off
+ "<https://github.com/tlswg/tls-trust-anchor-ids/blob/main/assignments.md>."
+ // clang-format on
+);
+
+DEFINE_QUICHE_COMMAND_LINE_FLAG(
bool, drop_response_body, false,
"If true, drop response body immediately after it is received.");
@@ -251,6 +281,38 @@
return proof_source;
}
+// Parses `text_oid_list` as a list of ASN.1 relative OIDs representing Trust
+// Anchor IDs and serializes the corresponding TLS `trust_anchors` extension, as
+// defined by draft-ietf-tls-trust-anchor-ids-02. Returns an error if the list
+// is empty, any of the OIDs cannot be parsed, or if serialization fails. Note
+// that on success, the returned serialization elides the 16-bit length prefix
+// on the `TrustAnchorIDList` structure.
+absl::StatusOr<std::optional<std::string>> SerializeTrustAnchorIdsFromTextOids(
+ absl::string_view text_oid_list) {
+ const std::vector<absl::string_view> text_oids =
+ absl::StrSplit(text_oid_list, ' ', absl::SkipWhitespace());
+ if (text_oids.empty()) {
+ return absl::InvalidArgumentError(
+ "Expected a non-empty list of Trust Anchor IDs.");
+ }
+ bssl::ScopedCBB cbb;
+ if (!CBB_init(cbb.get(), 32)) {
+ return absl::InternalError("Failed to initialize CBB.");
+ }
+ for (absl::string_view text_oid : text_oids) {
+ CBB id;
+ if (!CBB_add_u8_length_prefixed(cbb.get(), &id) ||
+ !CBB_add_asn1_relative_oid_from_text(&id, text_oid.data(),
+ text_oid.size()) ||
+ !CBB_flush(cbb.get())) {
+ return absl::InvalidArgumentError(
+ absl::StrCat("Invalid Trust Anchor ID: ", text_oid));
+ }
+ }
+ return std::string(reinterpret_cast<const char*>(CBB_data(cbb.get())),
+ CBB_len(cbb.get()));
+}
+
} // namespace
QuicToyClient::QuicToyClient(ClientFactory* client_factory)
@@ -365,6 +427,24 @@
client->crypto_config()->set_proof_source(std::move(proof_source));
}
+ if (const std::string request_trust_anchors =
+ quiche::GetQuicheCommandLineFlag(FLAGS_request_trust_anchors);
+ !request_trust_anchors.empty()) {
+ if (request_trust_anchors == "EMPTY_TRUST_ANCHORS") {
+ client->crypto_config()->ssl_config().trust_anchor_ids = "";
+ } else {
+ absl::StatusOr<std::optional<std::string>> serialized =
+ SerializeTrustAnchorIdsFromTextOids(request_trust_anchors);
+ if (!serialized.ok()) {
+ std::cerr << "Failed to serialize trust_anchors extension: "
+ << serialized.status() << std::endl;
+ return 1;
+ }
+ client->crypto_config()->ssl_config().trust_anchor_ids =
+ std::move(*serialized);
+ }
+ }
+
int32_t initial_mtu = quiche::GetQuicheCommandLineFlag(FLAGS_initial_mtu);
client->set_initial_max_packet_length(
initial_mtu != 0 ? initial_mtu : quic::kDefaultMaxPacketSize);