summaryrefslogtreecommitdiff
path: root/chromium/net/third_party/quiche/src/quiche/quic/core/crypto
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/third_party/quiche/src/quiche/quic/core/crypto')
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aead_base_decrypter.cc208
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aead_base_decrypter.h69
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aead_base_encrypter.cc182
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aead_base_encrypter.h73
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_decrypter.cc32
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_decrypter.h38
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_decrypter_test.cc288
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_encrypter.cc27
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_encrypter.h34
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc244
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_decrypter.cc34
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_decrypter.h36
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_decrypter_test.cc291
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_encrypter.cc27
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_encrypter.h32
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_encrypter_test.cc273
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_256_gcm_decrypter.cc34
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_256_gcm_decrypter.h36
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_256_gcm_decrypter_test.cc297
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_256_gcm_encrypter.cc27
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_256_gcm_encrypter.h32
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_256_gcm_encrypter_test.cc259
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_base_decrypter.cc52
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_base_decrypter.h33
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_base_encrypter.cc48
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_base_encrypter.h32
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/boring_utils.h34
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/cert_compressor.cc598
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/cert_compressor.h45
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/cert_compressor_test.cc119
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_util.cc280
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_util.h46
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_util_test.cc49
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_view.cc649
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_view.h149
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_view_der_fuzzer.cc19
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_view_pem_fuzzer.cc18
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_view_test.cc214
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_decrypter.cc41
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_decrypter.h41
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_decrypter_test.cc178
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_encrypter.cc35
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_encrypter.h38
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_encrypter_test.cc159
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_tls_decrypter.cc43
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_tls_decrypter.h39
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_tls_decrypter_test.cc188
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_tls_encrypter.cc35
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_tls_encrypter.h36
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_tls_encrypter_test.cc173
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha_base_decrypter.cc44
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha_base_decrypter.h31
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha_base_encrypter.cc41
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha_base_encrypter.h30
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/channel_id.cc90
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/channel_id.h47
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/channel_id_test.cc285
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/client_proof_source.cc62
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/client_proof_source.h70
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/client_proof_source_test.cc215
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_framer.cc351
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_framer.h136
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_framer_test.cc442
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_handshake.cc39
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_handshake.h189
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_handshake_message.cc368
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_handshake_message.h159
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_handshake_message_test.cc105
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_message_parser.h35
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_message_printer_bin.cc61
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_protocol.h509
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_secret_boxer.cc146
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_secret_boxer.h68
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_secret_boxer_test.cc82
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_server_test.cc1122
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_utils.cc790
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_utils.h255
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_utils_test.cc262
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/curve25519_key_exchange.cc86
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/curve25519_key_exchange.h53
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/curve25519_key_exchange_test.cc104
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/key_exchange.cc42
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/key_exchange.h102
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/null_decrypter.cc121
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/null_decrypter.h62
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/null_decrypter_test.cc137
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/null_encrypter.cc88
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/null_encrypter.h53
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/null_encrypter_test.cc103
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/p256_key_exchange.cc121
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/p256_key_exchange.h68
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/p256_key_exchange_test.cc109
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/proof_source.cc59
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/proof_source.h346
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/proof_source_x509.cc145
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/proof_source_x509.h77
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/proof_source_x509_test.cc142
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/proof_verifier.h117
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_client_session_cache.cc173
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_client_session_cache.h82
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_client_session_cache_test.cc440
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_compressed_certs_cache.cc114
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_compressed_certs_cache.h103
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_compressed_certs_cache_test.cc91
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypter.cc19
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypter.h94
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_client_config.cc842
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_client_config.h467
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_client_config_test.cc550
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_proof.cc12
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_proof.h32
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_server_config.cc1893
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_server_config.h948
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_server_config_test.cc494
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_decrypter.cc79
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_decrypter.h93
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_encrypter.cc60
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_encrypter.h70
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_hkdf.cc98
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_hkdf.h71
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_hkdf_test.cc91
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_random.cc99
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_random.h41
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_random_test.cc53
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/tls_client_connection.cc48
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/tls_client_connection.h54
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/tls_connection.cc211
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/tls_connection.h153
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/tls_server_connection.cc168
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/tls_server_connection.h180
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/transport_parameters.cc1579
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/transport_parameters.h304
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/transport_parameters_test.cc1130
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/web_transport_fingerprint_proof_verifier.cc229
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/web_transport_fingerprint_proof_verifier.h126
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/crypto/web_transport_fingerprint_proof_verifier_test.cc183
136 files changed, 25607 insertions, 0 deletions
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aead_base_decrypter.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aead_base_decrypter.cc
new file mode 100644
index 00000000000..c79e74e199b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aead_base_decrypter.cc
@@ -0,0 +1,208 @@
+// Copyright (c) 2013 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 "quiche/quic/core/crypto/aead_base_decrypter.h"
+
+#include <cstdint>
+#include <string>
+
+#include "absl/base/macros.h"
+#include "absl/strings/string_view.h"
+#include "openssl/crypto.h"
+#include "openssl/err.h"
+#include "openssl/evp.h"
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/quic/platform/api/quic_bug_tracker.h"
+#include "quiche/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+namespace {
+
+// Clear OpenSSL error stack.
+void ClearOpenSslErrors() {
+ while (ERR_get_error()) {
+ }
+}
+
+// In debug builds only, log OpenSSL error stack. Then clear OpenSSL error
+// stack.
+void DLogOpenSslErrors() {
+#ifdef NDEBUG
+ ClearOpenSslErrors();
+#else
+ while (uint32_t error = ERR_get_error()) {
+ char buf[120];
+ ERR_error_string_n(error, buf, ABSL_ARRAYSIZE(buf));
+ QUIC_DLOG(ERROR) << "OpenSSL error: " << buf;
+ }
+#endif
+}
+
+const EVP_AEAD* InitAndCall(const EVP_AEAD* (*aead_getter)()) {
+ // Ensure BoringSSL is initialized before calling |aead_getter|. In Chromium,
+ // the static initializer is disabled.
+ CRYPTO_library_init();
+ return aead_getter();
+}
+
+} // namespace
+
+AeadBaseDecrypter::AeadBaseDecrypter(const EVP_AEAD* (*aead_getter)(),
+ size_t key_size, size_t auth_tag_size,
+ size_t nonce_size,
+ bool use_ietf_nonce_construction)
+ : aead_alg_(InitAndCall(aead_getter)),
+ key_size_(key_size),
+ auth_tag_size_(auth_tag_size),
+ nonce_size_(nonce_size),
+ use_ietf_nonce_construction_(use_ietf_nonce_construction),
+ have_preliminary_key_(false) {
+ QUICHE_DCHECK_GT(256u, key_size);
+ QUICHE_DCHECK_GT(256u, auth_tag_size);
+ QUICHE_DCHECK_GT(256u, nonce_size);
+ QUICHE_DCHECK_LE(key_size_, sizeof(key_));
+ QUICHE_DCHECK_LE(nonce_size_, sizeof(iv_));
+}
+
+AeadBaseDecrypter::~AeadBaseDecrypter() {}
+
+bool AeadBaseDecrypter::SetKey(absl::string_view key) {
+ QUICHE_DCHECK_EQ(key.size(), key_size_);
+ if (key.size() != key_size_) {
+ return false;
+ }
+ memcpy(key_, key.data(), key.size());
+
+ EVP_AEAD_CTX_cleanup(ctx_.get());
+ if (!EVP_AEAD_CTX_init(ctx_.get(), aead_alg_, key_, key_size_, auth_tag_size_,
+ nullptr)) {
+ DLogOpenSslErrors();
+ return false;
+ }
+
+ return true;
+}
+
+bool AeadBaseDecrypter::SetNoncePrefix(absl::string_view nonce_prefix) {
+ if (use_ietf_nonce_construction_) {
+ QUIC_BUG(quic_bug_10709_1)
+ << "Attempted to set nonce prefix on IETF QUIC crypter";
+ return false;
+ }
+ QUICHE_DCHECK_EQ(nonce_prefix.size(), nonce_size_ - sizeof(QuicPacketNumber));
+ if (nonce_prefix.size() != nonce_size_ - sizeof(QuicPacketNumber)) {
+ return false;
+ }
+ memcpy(iv_, nonce_prefix.data(), nonce_prefix.size());
+ return true;
+}
+
+bool AeadBaseDecrypter::SetIV(absl::string_view iv) {
+ if (!use_ietf_nonce_construction_) {
+ QUIC_BUG(quic_bug_10709_2) << "Attempted to set IV on Google QUIC crypter";
+ return false;
+ }
+ QUICHE_DCHECK_EQ(iv.size(), nonce_size_);
+ if (iv.size() != nonce_size_) {
+ return false;
+ }
+ memcpy(iv_, iv.data(), iv.size());
+ return true;
+}
+
+bool AeadBaseDecrypter::SetPreliminaryKey(absl::string_view key) {
+ QUICHE_DCHECK(!have_preliminary_key_);
+ SetKey(key);
+ have_preliminary_key_ = true;
+
+ return true;
+}
+
+bool AeadBaseDecrypter::SetDiversificationNonce(
+ const DiversificationNonce& nonce) {
+ if (!have_preliminary_key_) {
+ return true;
+ }
+
+ std::string key, nonce_prefix;
+ size_t prefix_size = nonce_size_;
+ if (!use_ietf_nonce_construction_) {
+ prefix_size -= sizeof(QuicPacketNumber);
+ }
+ DiversifyPreliminaryKey(
+ absl::string_view(reinterpret_cast<const char*>(key_), key_size_),
+ absl::string_view(reinterpret_cast<const char*>(iv_), prefix_size), nonce,
+ key_size_, prefix_size, &key, &nonce_prefix);
+
+ if (!SetKey(key) ||
+ (!use_ietf_nonce_construction_ && !SetNoncePrefix(nonce_prefix)) ||
+ (use_ietf_nonce_construction_ && !SetIV(nonce_prefix))) {
+ QUICHE_DCHECK(false);
+ return false;
+ }
+
+ have_preliminary_key_ = false;
+ return true;
+}
+
+bool AeadBaseDecrypter::DecryptPacket(uint64_t packet_number,
+ absl::string_view associated_data,
+ absl::string_view ciphertext,
+ char* output, size_t* output_length,
+ size_t max_output_length) {
+ if (ciphertext.length() < auth_tag_size_) {
+ return false;
+ }
+
+ if (have_preliminary_key_) {
+ QUIC_BUG(quic_bug_10709_3)
+ << "Unable to decrypt while key diversification is pending";
+ return false;
+ }
+
+ uint8_t nonce[kMaxNonceSize];
+ memcpy(nonce, iv_, nonce_size_);
+ size_t prefix_len = nonce_size_ - sizeof(packet_number);
+ if (use_ietf_nonce_construction_) {
+ for (size_t i = 0; i < sizeof(packet_number); ++i) {
+ nonce[prefix_len + i] ^=
+ (packet_number >> ((sizeof(packet_number) - i - 1) * 8)) & 0xff;
+ }
+ } else {
+ memcpy(nonce + prefix_len, &packet_number, sizeof(packet_number));
+ }
+ if (!EVP_AEAD_CTX_open(
+ ctx_.get(), reinterpret_cast<uint8_t*>(output), output_length,
+ max_output_length, reinterpret_cast<const uint8_t*>(nonce),
+ nonce_size_, reinterpret_cast<const uint8_t*>(ciphertext.data()),
+ ciphertext.size(),
+ reinterpret_cast<const uint8_t*>(associated_data.data()),
+ associated_data.size())) {
+ // Because QuicFramer does trial decryption, decryption errors are expected
+ // when encryption level changes. So we don't log decryption errors.
+ ClearOpenSslErrors();
+ return false;
+ }
+ return true;
+}
+
+size_t AeadBaseDecrypter::GetKeySize() const { return key_size_; }
+
+size_t AeadBaseDecrypter::GetNoncePrefixSize() const {
+ return nonce_size_ - sizeof(QuicPacketNumber);
+}
+
+size_t AeadBaseDecrypter::GetIVSize() const { return nonce_size_; }
+
+absl::string_view AeadBaseDecrypter::GetKey() const {
+ return absl::string_view(reinterpret_cast<const char*>(key_), key_size_);
+}
+
+absl::string_view AeadBaseDecrypter::GetNoncePrefix() const {
+ return absl::string_view(reinterpret_cast<const char*>(iv_),
+ nonce_size_ - sizeof(QuicPacketNumber));
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aead_base_decrypter.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aead_base_decrypter.h
new file mode 100644
index 00000000000..b123b13e387
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aead_base_decrypter.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2013 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_AEAD_BASE_DECRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_AEAD_BASE_DECRYPTER_H_
+
+#include <cstddef>
+
+#include "absl/strings/string_view.h"
+#include "openssl/aead.h"
+#include "quiche/quic/core/crypto/quic_decrypter.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// AeadBaseDecrypter is the base class of AEAD QuicDecrypter subclasses.
+class QUIC_EXPORT_PRIVATE AeadBaseDecrypter : public QuicDecrypter {
+ public:
+ // This takes the function pointer rather than the EVP_AEAD itself so
+ // subclasses do not need to call CRYPTO_library_init.
+ AeadBaseDecrypter(const EVP_AEAD* (*aead_getter)(), size_t key_size,
+ size_t auth_tag_size, size_t nonce_size,
+ bool use_ietf_nonce_construction);
+ AeadBaseDecrypter(const AeadBaseDecrypter&) = delete;
+ AeadBaseDecrypter& operator=(const AeadBaseDecrypter&) = delete;
+ ~AeadBaseDecrypter() override;
+
+ // QuicDecrypter implementation
+ bool SetKey(absl::string_view key) override;
+ bool SetNoncePrefix(absl::string_view nonce_prefix) override;
+ bool SetIV(absl::string_view iv) override;
+ bool SetPreliminaryKey(absl::string_view key) override;
+ bool SetDiversificationNonce(const DiversificationNonce& nonce) override;
+ bool DecryptPacket(uint64_t packet_number, absl::string_view associated_data,
+ absl::string_view ciphertext, char* output,
+ size_t* output_length, size_t max_output_length) override;
+ size_t GetKeySize() const override;
+ size_t GetNoncePrefixSize() const override;
+ size_t GetIVSize() const override;
+ absl::string_view GetKey() const override;
+ absl::string_view GetNoncePrefix() const override;
+
+ protected:
+ // Make these constants available to the subclasses so that the subclasses
+ // can assert at compile time their key_size_ and nonce_size_ do not
+ // exceed the maximum.
+ static const size_t kMaxKeySize = 32;
+ static const size_t kMaxNonceSize = 12;
+
+ private:
+ const EVP_AEAD* const aead_alg_;
+ const size_t key_size_;
+ const size_t auth_tag_size_;
+ const size_t nonce_size_;
+ const bool use_ietf_nonce_construction_;
+ bool have_preliminary_key_;
+
+ // The key.
+ unsigned char key_[kMaxKeySize];
+ // The IV used to construct the nonce.
+ unsigned char iv_[kMaxNonceSize];
+
+ bssl::ScopedEVP_AEAD_CTX ctx_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_AEAD_BASE_DECRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aead_base_encrypter.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aead_base_encrypter.cc
new file mode 100644
index 00000000000..a2f0d5921c7
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aead_base_encrypter.cc
@@ -0,0 +1,182 @@
+// Copyright (c) 2013 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 "quiche/quic/core/crypto/aead_base_encrypter.h"
+
+#include "absl/base/macros.h"
+#include "absl/strings/string_view.h"
+#include "openssl/crypto.h"
+#include "openssl/err.h"
+#include "openssl/evp.h"
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/quic/platform/api/quic_bug_tracker.h"
+#include "quiche/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+namespace {
+
+// In debug builds only, log OpenSSL error stack. Then clear OpenSSL error
+// stack.
+void DLogOpenSslErrors() {
+#ifdef NDEBUG
+ while (ERR_get_error()) {
+ }
+#else
+ while (unsigned long error = ERR_get_error()) {
+ char buf[120];
+ ERR_error_string_n(error, buf, ABSL_ARRAYSIZE(buf));
+ QUIC_DLOG(ERROR) << "OpenSSL error: " << buf;
+ }
+#endif
+}
+
+const EVP_AEAD* InitAndCall(const EVP_AEAD* (*aead_getter)()) {
+ // Ensure BoringSSL is initialized before calling |aead_getter|. In Chromium,
+ // the static initializer is disabled.
+ CRYPTO_library_init();
+ return aead_getter();
+}
+
+} // namespace
+
+AeadBaseEncrypter::AeadBaseEncrypter(const EVP_AEAD* (*aead_getter)(),
+ size_t key_size, size_t auth_tag_size,
+ size_t nonce_size,
+ bool use_ietf_nonce_construction)
+ : aead_alg_(InitAndCall(aead_getter)),
+ key_size_(key_size),
+ auth_tag_size_(auth_tag_size),
+ nonce_size_(nonce_size),
+ use_ietf_nonce_construction_(use_ietf_nonce_construction) {
+ QUICHE_DCHECK_LE(key_size_, sizeof(key_));
+ QUICHE_DCHECK_LE(nonce_size_, sizeof(iv_));
+ QUICHE_DCHECK_GE(kMaxNonceSize, nonce_size_);
+}
+
+AeadBaseEncrypter::~AeadBaseEncrypter() {}
+
+bool AeadBaseEncrypter::SetKey(absl::string_view key) {
+ QUICHE_DCHECK_EQ(key.size(), key_size_);
+ if (key.size() != key_size_) {
+ return false;
+ }
+ memcpy(key_, key.data(), key.size());
+
+ EVP_AEAD_CTX_cleanup(ctx_.get());
+
+ if (!EVP_AEAD_CTX_init(ctx_.get(), aead_alg_, key_, key_size_, auth_tag_size_,
+ nullptr)) {
+ DLogOpenSslErrors();
+ return false;
+ }
+
+ return true;
+}
+
+bool AeadBaseEncrypter::SetNoncePrefix(absl::string_view nonce_prefix) {
+ if (use_ietf_nonce_construction_) {
+ QUIC_BUG(quic_bug_10634_1)
+ << "Attempted to set nonce prefix on IETF QUIC crypter";
+ return false;
+ }
+ QUICHE_DCHECK_EQ(nonce_prefix.size(), nonce_size_ - sizeof(QuicPacketNumber));
+ if (nonce_prefix.size() != nonce_size_ - sizeof(QuicPacketNumber)) {
+ return false;
+ }
+ memcpy(iv_, nonce_prefix.data(), nonce_prefix.size());
+ return true;
+}
+
+bool AeadBaseEncrypter::SetIV(absl::string_view iv) {
+ if (!use_ietf_nonce_construction_) {
+ QUIC_BUG(quic_bug_10634_2) << "Attempted to set IV on Google QUIC crypter";
+ return false;
+ }
+ QUICHE_DCHECK_EQ(iv.size(), nonce_size_);
+ if (iv.size() != nonce_size_) {
+ return false;
+ }
+ memcpy(iv_, iv.data(), iv.size());
+ return true;
+}
+
+bool AeadBaseEncrypter::Encrypt(absl::string_view nonce,
+ absl::string_view associated_data,
+ absl::string_view plaintext,
+ unsigned char* output) {
+ QUICHE_DCHECK_EQ(nonce.size(), nonce_size_);
+
+ size_t ciphertext_len;
+ if (!EVP_AEAD_CTX_seal(
+ ctx_.get(), output, &ciphertext_len,
+ plaintext.size() + auth_tag_size_,
+ reinterpret_cast<const uint8_t*>(nonce.data()), nonce.size(),
+ reinterpret_cast<const uint8_t*>(plaintext.data()), plaintext.size(),
+ reinterpret_cast<const uint8_t*>(associated_data.data()),
+ associated_data.size())) {
+ DLogOpenSslErrors();
+ return false;
+ }
+
+ return true;
+}
+
+bool AeadBaseEncrypter::EncryptPacket(uint64_t packet_number,
+ absl::string_view associated_data,
+ absl::string_view plaintext, char* output,
+ size_t* output_length,
+ size_t max_output_length) {
+ size_t ciphertext_size = GetCiphertextSize(plaintext.length());
+ if (max_output_length < ciphertext_size) {
+ return false;
+ }
+ // TODO(ianswett): Introduce a check to ensure that we don't encrypt with the
+ // same packet number twice.
+ alignas(4) char nonce_buffer[kMaxNonceSize];
+ memcpy(nonce_buffer, iv_, nonce_size_);
+ size_t prefix_len = nonce_size_ - sizeof(packet_number);
+ if (use_ietf_nonce_construction_) {
+ for (size_t i = 0; i < sizeof(packet_number); ++i) {
+ nonce_buffer[prefix_len + i] ^=
+ (packet_number >> ((sizeof(packet_number) - i - 1) * 8)) & 0xff;
+ }
+ } else {
+ memcpy(nonce_buffer + prefix_len, &packet_number, sizeof(packet_number));
+ }
+
+ if (!Encrypt(absl::string_view(nonce_buffer, nonce_size_), associated_data,
+ plaintext, reinterpret_cast<unsigned char*>(output))) {
+ return false;
+ }
+ *output_length = ciphertext_size;
+ return true;
+}
+
+size_t AeadBaseEncrypter::GetKeySize() const { return key_size_; }
+
+size_t AeadBaseEncrypter::GetNoncePrefixSize() const {
+ return nonce_size_ - sizeof(QuicPacketNumber);
+}
+
+size_t AeadBaseEncrypter::GetIVSize() const { return nonce_size_; }
+
+size_t AeadBaseEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const {
+ return ciphertext_size - std::min(ciphertext_size, auth_tag_size_);
+}
+
+size_t AeadBaseEncrypter::GetCiphertextSize(size_t plaintext_size) const {
+ return plaintext_size + auth_tag_size_;
+}
+
+absl::string_view AeadBaseEncrypter::GetKey() const {
+ return absl::string_view(reinterpret_cast<const char*>(key_), key_size_);
+}
+
+absl::string_view AeadBaseEncrypter::GetNoncePrefix() const {
+ return absl::string_view(reinterpret_cast<const char*>(iv_),
+ GetNoncePrefixSize());
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aead_base_encrypter.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aead_base_encrypter.h
new file mode 100644
index 00000000000..205b23265d1
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aead_base_encrypter.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2013 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_AEAD_BASE_ENCRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_AEAD_BASE_ENCRYPTER_H_
+
+#include <cstddef>
+
+#include "absl/strings/string_view.h"
+#include "openssl/aead.h"
+#include "quiche/quic/core/crypto/quic_encrypter.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// AeadBaseEncrypter is the base class of AEAD QuicEncrypter subclasses.
+class QUIC_EXPORT_PRIVATE AeadBaseEncrypter : public QuicEncrypter {
+ public:
+ // This takes the function pointer rather than the EVP_AEAD itself so
+ // subclasses do not need to call CRYPTO_library_init.
+ AeadBaseEncrypter(const EVP_AEAD* (*aead_getter)(), size_t key_size,
+ size_t auth_tag_size, size_t nonce_size,
+ bool use_ietf_nonce_construction);
+ AeadBaseEncrypter(const AeadBaseEncrypter&) = delete;
+ AeadBaseEncrypter& operator=(const AeadBaseEncrypter&) = delete;
+ ~AeadBaseEncrypter() override;
+
+ // QuicEncrypter implementation
+ bool SetKey(absl::string_view key) override;
+ bool SetNoncePrefix(absl::string_view nonce_prefix) override;
+ bool SetIV(absl::string_view iv) override;
+ bool EncryptPacket(uint64_t packet_number, absl::string_view associated_data,
+ absl::string_view plaintext, char* output,
+ size_t* output_length, size_t max_output_length) override;
+ size_t GetKeySize() const override;
+ size_t GetNoncePrefixSize() const override;
+ size_t GetIVSize() const override;
+ size_t GetMaxPlaintextSize(size_t ciphertext_size) const override;
+ size_t GetCiphertextSize(size_t plaintext_size) const override;
+ absl::string_view GetKey() const override;
+ absl::string_view GetNoncePrefix() const override;
+
+ // Necessary so unit tests can explicitly specify a nonce, instead of an IV
+ // (or nonce prefix) and packet number.
+ bool Encrypt(absl::string_view nonce, absl::string_view associated_data,
+ absl::string_view plaintext, unsigned char* output);
+
+ protected:
+ // Make these constants available to the subclasses so that the subclasses
+ // can assert at compile time their key_size_ and nonce_size_ do not
+ // exceed the maximum.
+ static const size_t kMaxKeySize = 32;
+ enum : size_t { kMaxNonceSize = 12 };
+
+ private:
+ const EVP_AEAD* const aead_alg_;
+ const size_t key_size_;
+ const size_t auth_tag_size_;
+ const size_t nonce_size_;
+ const bool use_ietf_nonce_construction_;
+
+ // The key.
+ unsigned char key_[kMaxKeySize];
+ // The IV used to construct the nonce.
+ unsigned char iv_[kMaxNonceSize];
+
+ bssl::ScopedEVP_AEAD_CTX ctx_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_AEAD_BASE_ENCRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_decrypter.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_decrypter.cc
new file mode 100644
index 00000000000..66f2ad2da09
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_decrypter.cc
@@ -0,0 +1,32 @@
+// Copyright (c) 2017 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 "quiche/quic/core/crypto/aes_128_gcm_12_decrypter.h"
+
+#include "openssl/aead.h"
+#include "openssl/tls1.h"
+
+namespace quic {
+
+namespace {
+
+const size_t kKeySize = 16;
+const size_t kNonceSize = 12;
+
+} // namespace
+
+Aes128Gcm12Decrypter::Aes128Gcm12Decrypter()
+ : AesBaseDecrypter(EVP_aead_aes_128_gcm, kKeySize, kAuthTagSize, kNonceSize,
+ /* use_ietf_nonce_construction */ false) {
+ static_assert(kKeySize <= kMaxKeySize, "key size too big");
+ static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
+}
+
+Aes128Gcm12Decrypter::~Aes128Gcm12Decrypter() {}
+
+uint32_t Aes128Gcm12Decrypter::cipher_id() const {
+ return TLS1_CK_AES_128_GCM_SHA256;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_decrypter.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_decrypter.h
new file mode 100644
index 00000000000..38a941991e0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_decrypter.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2017 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_12_DECRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_12_DECRYPTER_H_
+
+#include <cstdint>
+
+#include "quiche/quic/core/crypto/aes_base_decrypter.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// An Aes128Gcm12Decrypter is a QuicDecrypter that implements the
+// AEAD_AES_128_GCM_12 algorithm specified in RFC 5282. Create an instance by
+// calling QuicDecrypter::Create(kAESG).
+//
+// It uses an authentication tag of 12 bytes (96 bits). The fixed prefix
+// of the nonce is four bytes.
+class QUIC_EXPORT_PRIVATE Aes128Gcm12Decrypter : public AesBaseDecrypter {
+ public:
+ enum {
+ // Authentication tags are truncated to 96 bits.
+ kAuthTagSize = 12,
+ };
+
+ Aes128Gcm12Decrypter();
+ Aes128Gcm12Decrypter(const Aes128Gcm12Decrypter&) = delete;
+ Aes128Gcm12Decrypter& operator=(const Aes128Gcm12Decrypter&) = delete;
+ ~Aes128Gcm12Decrypter() override;
+
+ uint32_t cipher_id() const override;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_12_DECRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_decrypter_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_decrypter_test.cc
new file mode 100644
index 00000000000..64e11eb24a5
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_decrypter_test.cc
@@ -0,0 +1,288 @@
+// Copyright (c) 2013 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 "quiche/quic/core/crypto/aes_128_gcm_12_decrypter.h"
+
+#include <memory>
+#include <string>
+
+#include "absl/base/macros.h"
+#include "absl/strings/escaping.h"
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/quic_test_utils.h"
+#include "quiche/common/test_tools/quiche_test_utils.h"
+
+namespace {
+
+// The AES GCM test vectors come from the file gcmDecrypt128.rsp
+// downloaded from http://csrc.nist.gov/groups/STM/cavp/index.html on
+// 2013-02-01. The test vectors in that file look like this:
+//
+// [Keylen = 128]
+// [IVlen = 96]
+// [PTlen = 0]
+// [AADlen = 0]
+// [Taglen = 128]
+//
+// Count = 0
+// Key = cf063a34d4a9a76c2c86787d3f96db71
+// IV = 113b9785971864c83b01c787
+// CT =
+// AAD =
+// Tag = 72ac8493e3a5228b5d130a69d2510e42
+// PT =
+//
+// Count = 1
+// Key = a49a5e26a2f8cb63d05546c2a62f5343
+// IV = 907763b19b9b4ab6bd4f0281
+// CT =
+// AAD =
+// Tag = a2be08210d8c470a8df6e8fbd79ec5cf
+// FAIL
+//
+// ...
+//
+// The gcmDecrypt128.rsp file is huge (2.6 MB), so I selected just a
+// few test vectors for this unit test.
+
+// Describes a group of test vectors that all have a given key length, IV
+// length, plaintext length, AAD length, and tag length.
+struct TestGroupInfo {
+ size_t key_len;
+ size_t iv_len;
+ size_t pt_len;
+ size_t aad_len;
+ size_t tag_len;
+};
+
+// Each test vector consists of six strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a nullptr |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ // Input:
+ const char* key;
+ const char* iv;
+ const char* ct;
+ const char* aad;
+ const char* tag;
+
+ // Expected output:
+ const char* pt; // An empty string "" means decryption succeeded and
+ // the plaintext is zero-length. nullptr means decryption
+ // failed.
+};
+
+const TestGroupInfo test_group_info[] = {
+ {128, 96, 0, 0, 128}, {128, 96, 0, 128, 128}, {128, 96, 128, 0, 128},
+ {128, 96, 408, 160, 128}, {128, 96, 408, 720, 128}, {128, 96, 104, 0, 128},
+};
+
+const TestVector test_group_0[] = {
+ {"cf063a34d4a9a76c2c86787d3f96db71", "113b9785971864c83b01c787", "", "",
+ "72ac8493e3a5228b5d130a69d2510e42", ""},
+ {
+ "a49a5e26a2f8cb63d05546c2a62f5343", "907763b19b9b4ab6bd4f0281", "", "",
+ "a2be08210d8c470a8df6e8fbd79ec5cf",
+ nullptr // FAIL
+ },
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_1[] = {
+ {
+ "d1f6af919cde85661208bdce0c27cb22", "898c6929b435017bf031c3c5", "",
+ "7c5faa40e636bbc91107e68010c92b9f", "ae45f11777540a2caeb128be8092468a",
+ nullptr // FAIL
+ },
+ {"2370e320d4344208e0ff5683f243b213", "04dbb82f044d30831c441228", "",
+ "d43a8e5089eea0d026c03a85178b27da", "2a049c049d25aa95969b451d93c31c6e",
+ ""},
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_2[] = {
+ {"e98b72a9881a84ca6b76e0f43e68647a", "8b23299fde174053f3d652ba",
+ "5a3c1cf1985dbb8bed818036fdd5ab42", "", "23c7ab0f952b7091cd324835043b5eb5",
+ "28286a321293253c3e0aa2704a278032"},
+ {"33240636cd3236165f1a553b773e728e", "17c4d61493ecdc8f31700b12",
+ "47bb7e23f7bdfe05a8091ac90e4f8b2e", "", "b723c70e931d9785f40fd4ab1d612dc9",
+ "95695a5b12f2870b9cc5fdc8f218a97d"},
+ {
+ "5164df856f1e9cac04a79b808dc5be39", "e76925d5355e0584ce871b2b",
+ "0216c899c88d6e32c958c7e553daa5bc", "",
+ "a145319896329c96df291f64efbe0e3a",
+ nullptr // FAIL
+ },
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_3[] = {
+ {"af57f42c60c0fc5a09adb81ab86ca1c3", "a2dc01871f37025dc0fc9a79",
+ "b9a535864f48ea7b6b1367914978f9bfa087d854bb0e269bed8d279d2eea1210e48947"
+ "338b22f9bad09093276a331e9c79c7f4",
+ "41dc38988945fcb44faf2ef72d0061289ef8efd8",
+ "4f71e72bde0018f555c5adcce062e005",
+ "3803a0727eeb0ade441e0ec107161ded2d425ec0d102f21f51bf2cf9947c7ec4aa7279"
+ "5b2f69b041596e8817d0a3c16f8fadeb"},
+ {"ebc753e5422b377d3cb64b58ffa41b61", "2e1821efaced9acf1f241c9b",
+ "069567190554e9ab2b50a4e1fbf9c147340a5025fdbd201929834eaf6532325899ccb9"
+ "f401823e04b05817243d2142a3589878",
+ "b9673412fd4f88ba0e920f46dd6438ff791d8eef",
+ "534d9234d2351cf30e565de47baece0b",
+ "39077edb35e9c5a4b1e4c2a6b9bb1fce77f00f5023af40333d6d699014c2bcf4209c18"
+ "353a18017f5b36bfc00b1f6dcb7ed485"},
+ {
+ "52bdbbf9cf477f187ec010589cb39d58", "d3be36d3393134951d324b31",
+ "700188da144fa692cf46e4a8499510a53d90903c967f7f13e8a1bd8151a74adc4fe63e"
+ "32b992760b3a5f99e9a47838867000a9",
+ "93c4fc6a4135f54d640b0c976bf755a06a292c33",
+ "8ca4e38aa3dfa6b1d0297021ccf3ea5f",
+ nullptr // FAIL
+ },
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_4[] = {
+ {"da2bb7d581493d692380c77105590201", "44aa3e7856ca279d2eb020c6",
+ "9290d430c9e89c37f0446dbd620c9a6b34b1274aeb6f911f75867efcf95b6feda69f1a"
+ "f4ee16c761b3c9aeac3da03aa9889c88",
+ "4cd171b23bddb3a53cdf959d5c1710b481eb3785a90eb20a2345ee00d0bb7868c367ab"
+ "12e6f4dd1dee72af4eee1d197777d1d6499cc541f34edbf45cda6ef90b3c024f9272d7"
+ "2ec1909fb8fba7db88a4d6f7d3d925980f9f9f72",
+ "9e3ac938d3eb0cadd6f5c9e35d22ba38",
+ "9bbf4c1a2742f6ac80cb4e8a052e4a8f4f07c43602361355b717381edf9fabd4cb7e3a"
+ "d65dbd1378b196ac270588dd0621f642"},
+ {"d74e4958717a9d5c0e235b76a926cae8", "0b7471141e0c70b1995fd7b1",
+ "e701c57d2330bf066f9ff8cf3ca4343cafe4894651cd199bdaaa681ba486b4a65c5a22"
+ "b0f1420be29ea547d42c713bc6af66aa",
+ "4a42b7aae8c245c6f1598a395316e4b8484dbd6e64648d5e302021b1d3fa0a38f46e22"
+ "bd9c8080b863dc0016482538a8562a4bd0ba84edbe2697c76fd039527ac179ec5506cf"
+ "34a6039312774cedebf4961f3978b14a26509f96",
+ "e192c23cb036f0b31592989119eed55d",
+ "840d9fb95e32559fb3602e48590280a172ca36d9b49ab69510f5bd552bfab7a306f85f"
+ "f0a34bc305b88b804c60b90add594a17"},
+ {
+ "1986310c725ac94ecfe6422e75fc3ee7", "93ec4214fa8e6dc4e3afc775",
+ "b178ec72f85a311ac4168f42a4b2c23113fbea4b85f4b9dabb74e143eb1b8b0a361e02"
+ "43edfd365b90d5b325950df0ada058f9",
+ "e80b88e62c49c958b5e0b8b54f532d9ff6aa84c8a40132e93e55b59fc24e8decf28463"
+ "139f155d1e8ce4ee76aaeefcd245baa0fc519f83a5fb9ad9aa40c4b21126013f576c42"
+ "72c2cb136c8fd091cc4539877a5d1e72d607f960",
+ "8b347853f11d75e81e8a95010be81f17",
+ nullptr // FAIL
+ },
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_5[] = {
+ {"387218b246c1a8257748b56980e50c94", "dd7e014198672be39f95b69d",
+ "cdba9e73eaf3d38eceb2b04a8d", "", "ecf90f4a47c9c626d6fb2c765d201556",
+ "48f5b426baca03064554cc2b30"},
+ {"294de463721e359863887c820524b3d4", "3338b35c9d57a5d28190e8c9",
+ "2f46634e74b8e4c89812ac83b9", "", "dabd506764e68b82a7e720aa18da0abe",
+ "46a2e55c8e264df211bd112685"},
+ {"28ead7fd2179e0d12aa6d5d88c58c2dc", "5055347f18b4d5add0ae5c41",
+ "142d8210c3fb84774cdbd0447a", "", "5fd321d9cdb01952dc85f034736c2a7d",
+ "3b95b981086ee73cc4d0cc1422"},
+ {
+ "7d7b6c988137b8d470c57bf674a09c87", "9edf2aa970d016ac962e1fd8",
+ "a85b66c3cb5eab91d5bdc8bc0e", "", "dc054efc01f3afd21d9c2484819f569a",
+ nullptr // FAIL
+ },
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector* const test_group_array[] = {
+ test_group_0, test_group_1, test_group_2,
+ test_group_3, test_group_4, test_group_5,
+};
+
+} // namespace
+
+namespace quic {
+namespace test {
+
+// DecryptWithNonce wraps the |Decrypt| method of |decrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the plaintext.
+QuicData* DecryptWithNonce(Aes128Gcm12Decrypter* decrypter,
+ absl::string_view nonce,
+ absl::string_view associated_data,
+ absl::string_view ciphertext) {
+ uint64_t packet_number;
+ absl::string_view nonce_prefix(nonce.data(),
+ nonce.size() - sizeof(packet_number));
+ decrypter->SetNoncePrefix(nonce_prefix);
+ memcpy(&packet_number, nonce.data() + nonce_prefix.size(),
+ sizeof(packet_number));
+ std::unique_ptr<char[]> output(new char[ciphertext.length()]);
+ size_t output_length = 0;
+ const bool success = decrypter->DecryptPacket(
+ packet_number, associated_data, ciphertext, output.get(), &output_length,
+ ciphertext.length());
+ if (!success) {
+ return nullptr;
+ }
+ return new QuicData(output.release(), output_length, true);
+}
+
+class Aes128Gcm12DecrypterTest : public QuicTest {};
+
+TEST_F(Aes128Gcm12DecrypterTest, Decrypt) {
+ for (size_t i = 0; i < ABSL_ARRAYSIZE(test_group_array); i++) {
+ SCOPED_TRACE(i);
+ const TestVector* test_vectors = test_group_array[i];
+ const TestGroupInfo& test_info = test_group_info[i];
+ for (size_t j = 0; test_vectors[j].key != nullptr; j++) {
+ // If not present then decryption is expected to fail.
+ bool has_pt = test_vectors[j].pt;
+
+ // Decode the test vector.
+ std::string key = absl::HexStringToBytes(test_vectors[j].key);
+ std::string iv = absl::HexStringToBytes(test_vectors[j].iv);
+ std::string ct = absl::HexStringToBytes(test_vectors[j].ct);
+ std::string aad = absl::HexStringToBytes(test_vectors[j].aad);
+ std::string tag = absl::HexStringToBytes(test_vectors[j].tag);
+ std::string pt;
+ if (has_pt) {
+ pt = absl::HexStringToBytes(test_vectors[j].pt);
+ }
+
+ // The test vector's lengths should look sane. Note that the lengths
+ // in |test_info| are in bits.
+ EXPECT_EQ(test_info.key_len, key.length() * 8);
+ EXPECT_EQ(test_info.iv_len, iv.length() * 8);
+ EXPECT_EQ(test_info.pt_len, ct.length() * 8);
+ EXPECT_EQ(test_info.aad_len, aad.length() * 8);
+ EXPECT_EQ(test_info.tag_len, tag.length() * 8);
+ if (has_pt) {
+ EXPECT_EQ(test_info.pt_len, pt.length() * 8);
+ }
+
+ // The test vectors have 16 byte authenticators but this code only uses
+ // the first 12.
+ ASSERT_LE(static_cast<size_t>(Aes128Gcm12Decrypter::kAuthTagSize),
+ tag.length());
+ tag.resize(Aes128Gcm12Decrypter::kAuthTagSize);
+ std::string ciphertext = ct + tag;
+
+ Aes128Gcm12Decrypter decrypter;
+ ASSERT_TRUE(decrypter.SetKey(key));
+
+ std::unique_ptr<QuicData> decrypted(DecryptWithNonce(
+ &decrypter, iv,
+ // This deliberately tests that the decrypter can
+ // handle an AAD that is set to nullptr, as opposed
+ // to a zero-length, non-nullptr pointer.
+ aad.length() ? aad : absl::string_view(), ciphertext));
+ if (!decrypted) {
+ EXPECT_FALSE(has_pt);
+ continue;
+ }
+ EXPECT_TRUE(has_pt);
+
+ ASSERT_EQ(pt.length(), decrypted->length());
+ quiche::test::CompareCharArraysWithHexError(
+ "plaintext", decrypted->data(), pt.length(), pt.data(), pt.length());
+ }
+ }
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_encrypter.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_encrypter.cc
new file mode 100644
index 00000000000..5bbaeba079d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_encrypter.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 2013 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 "quiche/quic/core/crypto/aes_128_gcm_12_encrypter.h"
+
+#include "openssl/evp.h"
+
+namespace quic {
+
+namespace {
+
+const size_t kKeySize = 16;
+const size_t kNonceSize = 12;
+
+} // namespace
+
+Aes128Gcm12Encrypter::Aes128Gcm12Encrypter()
+ : AesBaseEncrypter(EVP_aead_aes_128_gcm, kKeySize, kAuthTagSize, kNonceSize,
+ /* use_ietf_nonce_construction */ false) {
+ static_assert(kKeySize <= kMaxKeySize, "key size too big");
+ static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
+}
+
+Aes128Gcm12Encrypter::~Aes128Gcm12Encrypter() {}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_encrypter.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_encrypter.h
new file mode 100644
index 00000000000..64f0292f26a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_encrypter.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2013 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_12_ENCRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_12_ENCRYPTER_H_
+
+#include "quiche/quic/core/crypto/aes_base_encrypter.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// An Aes128Gcm12Encrypter is a QuicEncrypter that implements the
+// AEAD_AES_128_GCM_12 algorithm specified in RFC 5282. Create an instance by
+// calling QuicEncrypter::Create(kAESG).
+//
+// It uses an authentication tag of 12 bytes (96 bits). The fixed prefix
+// of the nonce is four bytes.
+class QUIC_EXPORT_PRIVATE Aes128Gcm12Encrypter : public AesBaseEncrypter {
+ public:
+ enum {
+ // Authentication tags are truncated to 96 bits.
+ kAuthTagSize = 12,
+ };
+
+ Aes128Gcm12Encrypter();
+ Aes128Gcm12Encrypter(const Aes128Gcm12Encrypter&) = delete;
+ Aes128Gcm12Encrypter& operator=(const Aes128Gcm12Encrypter&) = delete;
+ ~Aes128Gcm12Encrypter() override;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_12_ENCRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc
new file mode 100644
index 00000000000..47dbd67e8b5
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc
@@ -0,0 +1,244 @@
+// Copyright (c) 2017 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 "quiche/quic/core/crypto/aes_128_gcm_12_encrypter.h"
+
+#include <memory>
+#include <string>
+
+#include "absl/base/macros.h"
+#include "absl/strings/escaping.h"
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/quic_test_utils.h"
+#include "quiche/common/test_tools/quiche_test_utils.h"
+
+namespace {
+
+// The AES GCM test vectors come from the file gcmEncryptExtIV128.rsp
+// downloaded from http://csrc.nist.gov/groups/STM/cavp/index.html on
+// 2013-02-01. The test vectors in that file look like this:
+//
+// [Keylen = 128]
+// [IVlen = 96]
+// [PTlen = 0]
+// [AADlen = 0]
+// [Taglen = 128]
+//
+// Count = 0
+// Key = 11754cd72aec309bf52f7687212e8957
+// IV = 3c819d9a9bed087615030b65
+// PT =
+// AAD =
+// CT =
+// Tag = 250327c674aaf477aef2675748cf6971
+//
+// Count = 1
+// Key = ca47248ac0b6f8372a97ac43508308ed
+// IV = ffd2b598feabc9019262d2be
+// PT =
+// AAD =
+// CT =
+// Tag = 60d20404af527d248d893ae495707d1a
+//
+// ...
+//
+// The gcmEncryptExtIV128.rsp file is huge (2.8 MB), so I selected just a
+// few test vectors for this unit test.
+
+// Describes a group of test vectors that all have a given key length, IV
+// length, plaintext length, AAD length, and tag length.
+struct TestGroupInfo {
+ size_t key_len;
+ size_t iv_len;
+ size_t pt_len;
+ size_t aad_len;
+ size_t tag_len;
+};
+
+// Each test vector consists of six strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a nullptr |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ const char* key;
+ const char* iv;
+ const char* pt;
+ const char* aad;
+ const char* ct;
+ const char* tag;
+};
+
+const TestGroupInfo test_group_info[] = {
+ {128, 96, 0, 0, 128}, {128, 96, 0, 128, 128}, {128, 96, 128, 0, 128},
+ {128, 96, 408, 160, 128}, {128, 96, 408, 720, 128}, {128, 96, 104, 0, 128},
+};
+
+const TestVector test_group_0[] = {
+ {"11754cd72aec309bf52f7687212e8957", "3c819d9a9bed087615030b65", "", "", "",
+ "250327c674aaf477aef2675748cf6971"},
+ {"ca47248ac0b6f8372a97ac43508308ed", "ffd2b598feabc9019262d2be", "", "", "",
+ "60d20404af527d248d893ae495707d1a"},
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_1[] = {
+ {"77be63708971c4e240d1cb79e8d77feb", "e0e00f19fed7ba0136a797f3", "",
+ "7a43ec1d9c0a5a78a0b16533a6213cab", "",
+ "209fcc8d3675ed938e9c7166709dd946"},
+ {"7680c5d3ca6154758e510f4d25b98820", "f8f105f9c3df4965780321f8", "",
+ "c94c410194c765e3dcc7964379758ed3", "",
+ "94dca8edfcf90bb74b153c8d48a17930"},
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_2[] = {
+ {"7fddb57453c241d03efbed3ac44e371c", "ee283a3fc75575e33efd4887",
+ "d5de42b461646c255c87bd2962d3b9a2", "", "2ccda4a5415cb91e135c2a0f78c9b2fd",
+ "b36d1df9b9d5e596f83e8b7f52971cb3"},
+ {"ab72c77b97cb5fe9a382d9fe81ffdbed", "54cc7dc2c37ec006bcc6d1da",
+ "007c5e5b3e59df24a7c355584fc1518d", "", "0e1bde206a07a9c2c1b65300f8c64997",
+ "2b4401346697138c7a4891ee59867d0c"},
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_3[] = {
+ {"fe47fcce5fc32665d2ae399e4eec72ba", "5adb9609dbaeb58cbd6e7275",
+ "7c0e88c88899a779228465074797cd4c2e1498d259b54390b85e3eef1c02df60e743f1"
+ "b840382c4bccaf3bafb4ca8429bea063",
+ "88319d6e1d3ffa5f987199166c8a9b56c2aeba5a",
+ "98f4826f05a265e6dd2be82db241c0fbbbf9ffb1c173aa83964b7cf539304373636525"
+ "3ddbc5db8778371495da76d269e5db3e",
+ "291ef1982e4defedaa2249f898556b47"},
+ {"ec0c2ba17aa95cd6afffe949da9cc3a8", "296bce5b50b7d66096d627ef",
+ "b85b3753535b825cbe5f632c0b843c741351f18aa484281aebec2f45bb9eea2d79d987"
+ "b764b9611f6c0f8641843d5d58f3a242",
+ "f8d00f05d22bf68599bcdeb131292ad6e2df5d14",
+ "a7443d31c26bdf2a1c945e29ee4bd344a99cfaf3aa71f8b3f191f83c2adfc7a0716299"
+ "5506fde6309ffc19e716eddf1a828c5a",
+ "890147971946b627c40016da1ecf3e77"},
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_4[] = {
+ {"2c1f21cf0f6fb3661943155c3e3d8492", "23cb5ff362e22426984d1907",
+ "42f758836986954db44bf37c6ef5e4ac0adaf38f27252a1b82d02ea949c8a1a2dbc0d6"
+ "8b5615ba7c1220ff6510e259f06655d8",
+ "5d3624879d35e46849953e45a32a624d6a6c536ed9857c613b572b0333e701557a713e"
+ "3f010ecdf9a6bd6c9e3e44b065208645aff4aabee611b391528514170084ccf587177f"
+ "4488f33cfb5e979e42b6e1cfc0a60238982a7aec",
+ "81824f0e0d523db30d3da369fdc0d60894c7a0a20646dd015073ad2732bd989b14a222"
+ "b6ad57af43e1895df9dca2a5344a62cc",
+ "57a3ee28136e94c74838997ae9823f3a"},
+ {"d9f7d2411091f947b4d6f1e2d1f0fb2e", "e1934f5db57cc983e6b180e7",
+ "73ed042327f70fe9c572a61545eda8b2a0c6e1d6c291ef19248e973aee6c312012f490"
+ "c2c6f6166f4a59431e182663fcaea05a",
+ "0a8a18a7150e940c3d87b38e73baee9a5c049ee21795663e264b694a949822b639092d"
+ "0e67015e86363583fcf0ca645af9f43375f05fdb4ce84f411dcbca73c2220dea03a201"
+ "15d2e51398344b16bee1ed7c499b353d6c597af8",
+ "aaadbd5c92e9151ce3db7210b8714126b73e43436d242677afa50384f2149b831f1d57"
+ "3c7891c2a91fbc48db29967ec9542b23",
+ "21b51ca862cb637cdd03b99a0f93b134"},
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_5[] = {
+ {"fe9bb47deb3a61e423c2231841cfd1fb", "4d328eb776f500a2f7fb47aa",
+ "f1cc3818e421876bb6b8bbd6c9", "", "b88c5c1977b35b517b0aeae967",
+ "43fd4727fe5cdb4b5b42818dea7ef8c9"},
+ {"6703df3701a7f54911ca72e24dca046a", "12823ab601c350ea4bc2488c",
+ "793cd125b0b84a043e3ac67717", "", "b2051c80014f42f08735a7b0cd",
+ "38e6bcd29962e5f2c13626b85a877101"},
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector* const test_group_array[] = {
+ test_group_0, test_group_1, test_group_2,
+ test_group_3, test_group_4, test_group_5,
+};
+
+} // namespace
+
+namespace quic {
+namespace test {
+
+// EncryptWithNonce wraps the |Encrypt| method of |encrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the ciphertext.
+QuicData* EncryptWithNonce(Aes128Gcm12Encrypter* encrypter,
+ absl::string_view nonce,
+ absl::string_view associated_data,
+ absl::string_view plaintext) {
+ size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length());
+ std::unique_ptr<char[]> ciphertext(new char[ciphertext_size]);
+
+ if (!encrypter->Encrypt(nonce, associated_data, plaintext,
+ reinterpret_cast<unsigned char*>(ciphertext.get()))) {
+ return nullptr;
+ }
+
+ return new QuicData(ciphertext.release(), ciphertext_size, true);
+}
+
+class Aes128Gcm12EncrypterTest : public QuicTest {};
+
+TEST_F(Aes128Gcm12EncrypterTest, Encrypt) {
+ for (size_t i = 0; i < ABSL_ARRAYSIZE(test_group_array); i++) {
+ SCOPED_TRACE(i);
+ const TestVector* test_vectors = test_group_array[i];
+ const TestGroupInfo& test_info = test_group_info[i];
+ for (size_t j = 0; test_vectors[j].key != nullptr; j++) {
+ // Decode the test vector.
+ std::string key = absl::HexStringToBytes(test_vectors[j].key);
+ std::string iv = absl::HexStringToBytes(test_vectors[j].iv);
+ std::string pt = absl::HexStringToBytes(test_vectors[j].pt);
+ std::string aad = absl::HexStringToBytes(test_vectors[j].aad);
+ std::string ct = absl::HexStringToBytes(test_vectors[j].ct);
+ std::string tag = absl::HexStringToBytes(test_vectors[j].tag);
+
+ // The test vector's lengths should look sane. Note that the lengths
+ // in |test_info| are in bits.
+ EXPECT_EQ(test_info.key_len, key.length() * 8);
+ EXPECT_EQ(test_info.iv_len, iv.length() * 8);
+ EXPECT_EQ(test_info.pt_len, pt.length() * 8);
+ EXPECT_EQ(test_info.aad_len, aad.length() * 8);
+ EXPECT_EQ(test_info.pt_len, ct.length() * 8);
+ EXPECT_EQ(test_info.tag_len, tag.length() * 8);
+
+ Aes128Gcm12Encrypter encrypter;
+ ASSERT_TRUE(encrypter.SetKey(key));
+ std::unique_ptr<QuicData> encrypted(
+ EncryptWithNonce(&encrypter, iv,
+ // This deliberately tests that the encrypter can
+ // handle an AAD that is set to nullptr, as opposed
+ // to a zero-length, non-nullptr pointer.
+ aad.length() ? aad : absl::string_view(), pt));
+ ASSERT_TRUE(encrypted.get());
+
+ // The test vectors have 16 byte authenticators but this code only uses
+ // the first 12.
+ ASSERT_LE(static_cast<size_t>(Aes128Gcm12Encrypter::kAuthTagSize),
+ tag.length());
+ tag.resize(Aes128Gcm12Encrypter::kAuthTagSize);
+
+ ASSERT_EQ(ct.length() + tag.length(), encrypted->length());
+ quiche::test::CompareCharArraysWithHexError(
+ "ciphertext", encrypted->data(), ct.length(), ct.data(), ct.length());
+ quiche::test::CompareCharArraysWithHexError(
+ "authentication tag", encrypted->data() + ct.length(), tag.length(),
+ tag.data(), tag.length());
+ }
+ }
+}
+
+TEST_F(Aes128Gcm12EncrypterTest, GetMaxPlaintextSize) {
+ Aes128Gcm12Encrypter encrypter;
+ EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1012));
+ EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(112));
+ EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(22));
+ EXPECT_EQ(0u, encrypter.GetMaxPlaintextSize(11));
+}
+
+TEST_F(Aes128Gcm12EncrypterTest, GetCiphertextSize) {
+ Aes128Gcm12Encrypter encrypter;
+ EXPECT_EQ(1012u, encrypter.GetCiphertextSize(1000));
+ EXPECT_EQ(112u, encrypter.GetCiphertextSize(100));
+ EXPECT_EQ(22u, encrypter.GetCiphertextSize(10));
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_decrypter.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_decrypter.cc
new file mode 100644
index 00000000000..c43123bb445
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_decrypter.cc
@@ -0,0 +1,34 @@
+// Copyright (c) 2017 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 "quiche/quic/core/crypto/aes_128_gcm_decrypter.h"
+
+#include "openssl/aead.h"
+#include "openssl/tls1.h"
+#include "quiche/quic/platform/api/quic_flag_utils.h"
+#include "quiche/quic/platform/api/quic_flags.h"
+
+namespace quic {
+
+namespace {
+
+const size_t kKeySize = 16;
+const size_t kNonceSize = 12;
+
+} // namespace
+
+Aes128GcmDecrypter::Aes128GcmDecrypter()
+ : AesBaseDecrypter(EVP_aead_aes_128_gcm, kKeySize, kAuthTagSize, kNonceSize,
+ /* use_ietf_nonce_construction */ true) {
+ static_assert(kKeySize <= kMaxKeySize, "key size too big");
+ static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
+}
+
+Aes128GcmDecrypter::~Aes128GcmDecrypter() {}
+
+uint32_t Aes128GcmDecrypter::cipher_id() const {
+ return TLS1_CK_AES_128_GCM_SHA256;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_decrypter.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_decrypter.h
new file mode 100644
index 00000000000..c5f4d17de14
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_decrypter.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2017 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_DECRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_DECRYPTER_H_
+
+#include <cstdint>
+
+#include "quiche/quic/core/crypto/aes_base_decrypter.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// An Aes128GcmDecrypter is a QuicDecrypter that implements the
+// AEAD_AES_128_GCM algorithm specified in RFC 5116 for use in IETF QUIC.
+//
+// It uses an authentication tag of 16 bytes (128 bits). It uses a 12 byte IV
+// that is XOR'd with the packet number to compute the nonce.
+class QUIC_EXPORT_PRIVATE Aes128GcmDecrypter : public AesBaseDecrypter {
+ public:
+ enum {
+ kAuthTagSize = 16,
+ };
+
+ Aes128GcmDecrypter();
+ Aes128GcmDecrypter(const Aes128GcmDecrypter&) = delete;
+ Aes128GcmDecrypter& operator=(const Aes128GcmDecrypter&) = delete;
+ ~Aes128GcmDecrypter() override;
+
+ uint32_t cipher_id() const override;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_DECRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_decrypter_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_decrypter_test.cc
new file mode 100644
index 00000000000..e02b433d22f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_decrypter_test.cc
@@ -0,0 +1,291 @@
+// Copyright (c) 2017 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 "quiche/quic/core/crypto/aes_128_gcm_decrypter.h"
+
+#include <memory>
+#include <string>
+
+#include "absl/base/macros.h"
+#include "absl/strings/escaping.h"
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/quic_test_utils.h"
+#include "quiche/common/test_tools/quiche_test_utils.h"
+
+namespace {
+
+// The AES GCM test vectors come from the file gcmDecrypt128.rsp
+// downloaded from http://csrc.nist.gov/groups/STM/cavp/index.html on
+// 2013-02-01. The test vectors in that file look like this:
+//
+// [Keylen = 128]
+// [IVlen = 96]
+// [PTlen = 0]
+// [AADlen = 0]
+// [Taglen = 128]
+//
+// Count = 0
+// Key = cf063a34d4a9a76c2c86787d3f96db71
+// IV = 113b9785971864c83b01c787
+// CT =
+// AAD =
+// Tag = 72ac8493e3a5228b5d130a69d2510e42
+// PT =
+//
+// Count = 1
+// Key = a49a5e26a2f8cb63d05546c2a62f5343
+// IV = 907763b19b9b4ab6bd4f0281
+// CT =
+// AAD =
+// Tag = a2be08210d8c470a8df6e8fbd79ec5cf
+// FAIL
+//
+// ...
+//
+// The gcmDecrypt128.rsp file is huge (2.6 MB), so I selected just a
+// few test vectors for this unit test.
+
+// Describes a group of test vectors that all have a given key length, IV
+// length, plaintext length, AAD length, and tag length.
+struct TestGroupInfo {
+ size_t key_len;
+ size_t iv_len;
+ size_t pt_len;
+ size_t aad_len;
+ size_t tag_len;
+};
+
+// Each test vector consists of six strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a nullptr |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ // Input:
+ const char* key;
+ const char* iv;
+ const char* ct;
+ const char* aad;
+ const char* tag;
+
+ // Expected output:
+ const char* pt; // An empty string "" means decryption succeeded and
+ // the plaintext is zero-length. nullptr means decryption
+ // failed.
+};
+
+const TestGroupInfo test_group_info[] = {
+ {128, 96, 0, 0, 128}, {128, 96, 0, 128, 128}, {128, 96, 128, 0, 128},
+ {128, 96, 408, 160, 128}, {128, 96, 408, 720, 128}, {128, 96, 104, 0, 128},
+};
+
+const TestVector test_group_0[] = {
+ {"cf063a34d4a9a76c2c86787d3f96db71", "113b9785971864c83b01c787", "", "",
+ "72ac8493e3a5228b5d130a69d2510e42", ""},
+ {
+ "a49a5e26a2f8cb63d05546c2a62f5343", "907763b19b9b4ab6bd4f0281", "", "",
+ "a2be08210d8c470a8df6e8fbd79ec5cf",
+ nullptr // FAIL
+ },
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_1[] = {
+ {
+ "d1f6af919cde85661208bdce0c27cb22", "898c6929b435017bf031c3c5", "",
+ "7c5faa40e636bbc91107e68010c92b9f", "ae45f11777540a2caeb128be8092468a",
+ nullptr // FAIL
+ },
+ {"2370e320d4344208e0ff5683f243b213", "04dbb82f044d30831c441228", "",
+ "d43a8e5089eea0d026c03a85178b27da", "2a049c049d25aa95969b451d93c31c6e",
+ ""},
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_2[] = {
+ {"e98b72a9881a84ca6b76e0f43e68647a", "8b23299fde174053f3d652ba",
+ "5a3c1cf1985dbb8bed818036fdd5ab42", "", "23c7ab0f952b7091cd324835043b5eb5",
+ "28286a321293253c3e0aa2704a278032"},
+ {"33240636cd3236165f1a553b773e728e", "17c4d61493ecdc8f31700b12",
+ "47bb7e23f7bdfe05a8091ac90e4f8b2e", "", "b723c70e931d9785f40fd4ab1d612dc9",
+ "95695a5b12f2870b9cc5fdc8f218a97d"},
+ {
+ "5164df856f1e9cac04a79b808dc5be39", "e76925d5355e0584ce871b2b",
+ "0216c899c88d6e32c958c7e553daa5bc", "",
+ "a145319896329c96df291f64efbe0e3a",
+ nullptr // FAIL
+ },
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_3[] = {
+ {"af57f42c60c0fc5a09adb81ab86ca1c3", "a2dc01871f37025dc0fc9a79",
+ "b9a535864f48ea7b6b1367914978f9bfa087d854bb0e269bed8d279d2eea1210e48947"
+ "338b22f9bad09093276a331e9c79c7f4",
+ "41dc38988945fcb44faf2ef72d0061289ef8efd8",
+ "4f71e72bde0018f555c5adcce062e005",
+ "3803a0727eeb0ade441e0ec107161ded2d425ec0d102f21f51bf2cf9947c7ec4aa7279"
+ "5b2f69b041596e8817d0a3c16f8fadeb"},
+ {"ebc753e5422b377d3cb64b58ffa41b61", "2e1821efaced9acf1f241c9b",
+ "069567190554e9ab2b50a4e1fbf9c147340a5025fdbd201929834eaf6532325899ccb9"
+ "f401823e04b05817243d2142a3589878",
+ "b9673412fd4f88ba0e920f46dd6438ff791d8eef",
+ "534d9234d2351cf30e565de47baece0b",
+ "39077edb35e9c5a4b1e4c2a6b9bb1fce77f00f5023af40333d6d699014c2bcf4209c18"
+ "353a18017f5b36bfc00b1f6dcb7ed485"},
+ {
+ "52bdbbf9cf477f187ec010589cb39d58", "d3be36d3393134951d324b31",
+ "700188da144fa692cf46e4a8499510a53d90903c967f7f13e8a1bd8151a74adc4fe63e"
+ "32b992760b3a5f99e9a47838867000a9",
+ "93c4fc6a4135f54d640b0c976bf755a06a292c33",
+ "8ca4e38aa3dfa6b1d0297021ccf3ea5f",
+ nullptr // FAIL
+ },
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_4[] = {
+ {"da2bb7d581493d692380c77105590201", "44aa3e7856ca279d2eb020c6",
+ "9290d430c9e89c37f0446dbd620c9a6b34b1274aeb6f911f75867efcf95b6feda69f1a"
+ "f4ee16c761b3c9aeac3da03aa9889c88",
+ "4cd171b23bddb3a53cdf959d5c1710b481eb3785a90eb20a2345ee00d0bb7868c367ab"
+ "12e6f4dd1dee72af4eee1d197777d1d6499cc541f34edbf45cda6ef90b3c024f9272d7"
+ "2ec1909fb8fba7db88a4d6f7d3d925980f9f9f72",
+ "9e3ac938d3eb0cadd6f5c9e35d22ba38",
+ "9bbf4c1a2742f6ac80cb4e8a052e4a8f4f07c43602361355b717381edf9fabd4cb7e3a"
+ "d65dbd1378b196ac270588dd0621f642"},
+ {"d74e4958717a9d5c0e235b76a926cae8", "0b7471141e0c70b1995fd7b1",
+ "e701c57d2330bf066f9ff8cf3ca4343cafe4894651cd199bdaaa681ba486b4a65c5a22"
+ "b0f1420be29ea547d42c713bc6af66aa",
+ "4a42b7aae8c245c6f1598a395316e4b8484dbd6e64648d5e302021b1d3fa0a38f46e22"
+ "bd9c8080b863dc0016482538a8562a4bd0ba84edbe2697c76fd039527ac179ec5506cf"
+ "34a6039312774cedebf4961f3978b14a26509f96",
+ "e192c23cb036f0b31592989119eed55d",
+ "840d9fb95e32559fb3602e48590280a172ca36d9b49ab69510f5bd552bfab7a306f85f"
+ "f0a34bc305b88b804c60b90add594a17"},
+ {
+ "1986310c725ac94ecfe6422e75fc3ee7", "93ec4214fa8e6dc4e3afc775",
+ "b178ec72f85a311ac4168f42a4b2c23113fbea4b85f4b9dabb74e143eb1b8b0a361e02"
+ "43edfd365b90d5b325950df0ada058f9",
+ "e80b88e62c49c958b5e0b8b54f532d9ff6aa84c8a40132e93e55b59fc24e8decf28463"
+ "139f155d1e8ce4ee76aaeefcd245baa0fc519f83a5fb9ad9aa40c4b21126013f576c42"
+ "72c2cb136c8fd091cc4539877a5d1e72d607f960",
+ "8b347853f11d75e81e8a95010be81f17",
+ nullptr // FAIL
+ },
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_5[] = {
+ {"387218b246c1a8257748b56980e50c94", "dd7e014198672be39f95b69d",
+ "cdba9e73eaf3d38eceb2b04a8d", "", "ecf90f4a47c9c626d6fb2c765d201556",
+ "48f5b426baca03064554cc2b30"},
+ {"294de463721e359863887c820524b3d4", "3338b35c9d57a5d28190e8c9",
+ "2f46634e74b8e4c89812ac83b9", "", "dabd506764e68b82a7e720aa18da0abe",
+ "46a2e55c8e264df211bd112685"},
+ {"28ead7fd2179e0d12aa6d5d88c58c2dc", "5055347f18b4d5add0ae5c41",
+ "142d8210c3fb84774cdbd0447a", "", "5fd321d9cdb01952dc85f034736c2a7d",
+ "3b95b981086ee73cc4d0cc1422"},
+ {
+ "7d7b6c988137b8d470c57bf674a09c87", "9edf2aa970d016ac962e1fd8",
+ "a85b66c3cb5eab91d5bdc8bc0e", "", "dc054efc01f3afd21d9c2484819f569a",
+ nullptr // FAIL
+ },
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector* const test_group_array[] = {
+ test_group_0, test_group_1, test_group_2,
+ test_group_3, test_group_4, test_group_5,
+};
+
+} // namespace
+
+namespace quic {
+namespace test {
+
+// DecryptWithNonce wraps the |Decrypt| method of |decrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the plaintext.
+QuicData* DecryptWithNonce(Aes128GcmDecrypter* decrypter,
+ absl::string_view nonce,
+ absl::string_view associated_data,
+ absl::string_view ciphertext) {
+ decrypter->SetIV(nonce);
+ std::unique_ptr<char[]> output(new char[ciphertext.length()]);
+ size_t output_length = 0;
+ const bool success =
+ decrypter->DecryptPacket(0, associated_data, ciphertext, output.get(),
+ &output_length, ciphertext.length());
+ if (!success) {
+ return nullptr;
+ }
+ return new QuicData(output.release(), output_length, true);
+}
+
+class Aes128GcmDecrypterTest : public QuicTest {};
+
+TEST_F(Aes128GcmDecrypterTest, Decrypt) {
+ for (size_t i = 0; i < ABSL_ARRAYSIZE(test_group_array); i++) {
+ SCOPED_TRACE(i);
+ const TestVector* test_vectors = test_group_array[i];
+ const TestGroupInfo& test_info = test_group_info[i];
+ for (size_t j = 0; test_vectors[j].key != nullptr; j++) {
+ // If not present then decryption is expected to fail.
+ bool has_pt = test_vectors[j].pt;
+
+ // Decode the test vector.
+ std::string key = absl::HexStringToBytes(test_vectors[j].key);
+ std::string iv = absl::HexStringToBytes(test_vectors[j].iv);
+ std::string ct = absl::HexStringToBytes(test_vectors[j].ct);
+ std::string aad = absl::HexStringToBytes(test_vectors[j].aad);
+ std::string tag = absl::HexStringToBytes(test_vectors[j].tag);
+ std::string pt;
+ if (has_pt) {
+ pt = absl::HexStringToBytes(test_vectors[j].pt);
+ }
+
+ // The test vector's lengths should look sane. Note that the lengths
+ // in |test_info| are in bits.
+ EXPECT_EQ(test_info.key_len, key.length() * 8);
+ EXPECT_EQ(test_info.iv_len, iv.length() * 8);
+ EXPECT_EQ(test_info.pt_len, ct.length() * 8);
+ EXPECT_EQ(test_info.aad_len, aad.length() * 8);
+ EXPECT_EQ(test_info.tag_len, tag.length() * 8);
+ if (has_pt) {
+ EXPECT_EQ(test_info.pt_len, pt.length() * 8);
+ }
+ std::string ciphertext = ct + tag;
+
+ Aes128GcmDecrypter decrypter;
+ ASSERT_TRUE(decrypter.SetKey(key));
+
+ std::unique_ptr<QuicData> decrypted(DecryptWithNonce(
+ &decrypter, iv,
+ // This deliberately tests that the decrypter can
+ // handle an AAD that is set to nullptr, as opposed
+ // to a zero-length, non-nullptr pointer.
+ aad.length() ? aad : absl::string_view(), ciphertext));
+ if (!decrypted) {
+ EXPECT_FALSE(has_pt);
+ continue;
+ }
+ EXPECT_TRUE(has_pt);
+
+ ASSERT_EQ(pt.length(), decrypted->length());
+ quiche::test::CompareCharArraysWithHexError(
+ "plaintext", decrypted->data(), pt.length(), pt.data(), pt.length());
+ }
+ }
+}
+
+TEST_F(Aes128GcmDecrypterTest, GenerateHeaderProtectionMask) {
+ Aes128GcmDecrypter decrypter;
+ std::string key = absl::HexStringToBytes("d9132370cb18476ab833649cf080d970");
+ std::string sample =
+ absl::HexStringToBytes("d1d7998068517adb769b48b924a32c47");
+ QuicDataReader sample_reader(sample.data(), sample.size());
+ ASSERT_TRUE(decrypter.SetHeaderProtectionKey(key));
+ std::string mask = decrypter.GenerateHeaderProtectionMask(&sample_reader);
+ std::string expected_mask =
+ absl::HexStringToBytes("b132c37d6164da4ea4dc9b763aceec27");
+ quiche::test::CompareCharArraysWithHexError(
+ "header protection mask", mask.data(), mask.size(), expected_mask.data(),
+ expected_mask.size());
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_encrypter.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_encrypter.cc
new file mode 100644
index 00000000000..22f9b2a2168
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_encrypter.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 2017 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 "quiche/quic/core/crypto/aes_128_gcm_encrypter.h"
+
+#include "openssl/evp.h"
+
+namespace quic {
+
+namespace {
+
+const size_t kKeySize = 16;
+const size_t kNonceSize = 12;
+
+} // namespace
+
+Aes128GcmEncrypter::Aes128GcmEncrypter()
+ : AesBaseEncrypter(EVP_aead_aes_128_gcm, kKeySize, kAuthTagSize, kNonceSize,
+ /* use_ietf_nonce_construction */ true) {
+ static_assert(kKeySize <= kMaxKeySize, "key size too big");
+ static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
+}
+
+Aes128GcmEncrypter::~Aes128GcmEncrypter() {}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_encrypter.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_encrypter.h
new file mode 100644
index 00000000000..a40735c9e37
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_encrypter.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2017 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_ENCRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_ENCRYPTER_H_
+
+#include "quiche/quic/core/crypto/aes_base_encrypter.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// An Aes128GcmEncrypter is a QuicEncrypter that implements the
+// AEAD_AES_128_GCM algorithm specified in RFC 5116 for use in IETF QUIC.
+//
+// It uses an authentication tag of 16 bytes (128 bits). It uses a 12 byte IV
+// that is XOR'd with the packet number to compute the nonce.
+class QUIC_EXPORT_PRIVATE Aes128GcmEncrypter : public AesBaseEncrypter {
+ public:
+ enum {
+ kAuthTagSize = 16,
+ };
+
+ Aes128GcmEncrypter();
+ Aes128GcmEncrypter(const Aes128GcmEncrypter&) = delete;
+ Aes128GcmEncrypter& operator=(const Aes128GcmEncrypter&) = delete;
+ ~Aes128GcmEncrypter() override;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_ENCRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_encrypter_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_encrypter_test.cc
new file mode 100644
index 00000000000..70860944b55
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_encrypter_test.cc
@@ -0,0 +1,273 @@
+// Copyright (c) 2017 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 "quiche/quic/core/crypto/aes_128_gcm_encrypter.h"
+
+#include <memory>
+#include <string>
+
+#include "absl/base/macros.h"
+#include "absl/strings/escaping.h"
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/quic_test_utils.h"
+#include "quiche/common/test_tools/quiche_test_utils.h"
+
+namespace {
+
+// The AES GCM test vectors come from the file gcmEncryptExtIV128.rsp
+// downloaded from http://csrc.nist.gov/groups/STM/cavp/index.html on
+// 2013-02-01. The test vectors in that file look like this:
+//
+// [Keylen = 128]
+// [IVlen = 96]
+// [PTlen = 0]
+// [AADlen = 0]
+// [Taglen = 128]
+//
+// Count = 0
+// Key = 11754cd72aec309bf52f7687212e8957
+// IV = 3c819d9a9bed087615030b65
+// PT =
+// AAD =
+// CT =
+// Tag = 250327c674aaf477aef2675748cf6971
+//
+// Count = 1
+// Key = ca47248ac0b6f8372a97ac43508308ed
+// IV = ffd2b598feabc9019262d2be
+// PT =
+// AAD =
+// CT =
+// Tag = 60d20404af527d248d893ae495707d1a
+//
+// ...
+//
+// The gcmEncryptExtIV128.rsp file is huge (2.8 MB), so I selected just a
+// few test vectors for this unit test.
+
+// Describes a group of test vectors that all have a given key length, IV
+// length, plaintext length, AAD length, and tag length.
+struct TestGroupInfo {
+ size_t key_len;
+ size_t iv_len;
+ size_t pt_len;
+ size_t aad_len;
+ size_t tag_len;
+};
+
+// Each test vector consists of six strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a nullptr |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ const char* key;
+ const char* iv;
+ const char* pt;
+ const char* aad;
+ const char* ct;
+ const char* tag;
+};
+
+const TestGroupInfo test_group_info[] = {
+ {128, 96, 0, 0, 128}, {128, 96, 0, 128, 128}, {128, 96, 128, 0, 128},
+ {128, 96, 408, 160, 128}, {128, 96, 408, 720, 128}, {128, 96, 104, 0, 128},
+};
+
+const TestVector test_group_0[] = {
+ {"11754cd72aec309bf52f7687212e8957", "3c819d9a9bed087615030b65", "", "", "",
+ "250327c674aaf477aef2675748cf6971"},
+ {"ca47248ac0b6f8372a97ac43508308ed", "ffd2b598feabc9019262d2be", "", "", "",
+ "60d20404af527d248d893ae495707d1a"},
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_1[] = {
+ {"77be63708971c4e240d1cb79e8d77feb", "e0e00f19fed7ba0136a797f3", "",
+ "7a43ec1d9c0a5a78a0b16533a6213cab", "",
+ "209fcc8d3675ed938e9c7166709dd946"},
+ {"7680c5d3ca6154758e510f4d25b98820", "f8f105f9c3df4965780321f8", "",
+ "c94c410194c765e3dcc7964379758ed3", "",
+ "94dca8edfcf90bb74b153c8d48a17930"},
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_2[] = {
+ {"7fddb57453c241d03efbed3ac44e371c", "ee283a3fc75575e33efd4887",
+ "d5de42b461646c255c87bd2962d3b9a2", "", "2ccda4a5415cb91e135c2a0f78c9b2fd",
+ "b36d1df9b9d5e596f83e8b7f52971cb3"},
+ {"ab72c77b97cb5fe9a382d9fe81ffdbed", "54cc7dc2c37ec006bcc6d1da",
+ "007c5e5b3e59df24a7c355584fc1518d", "", "0e1bde206a07a9c2c1b65300f8c64997",
+ "2b4401346697138c7a4891ee59867d0c"},
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_3[] = {
+ {"fe47fcce5fc32665d2ae399e4eec72ba", "5adb9609dbaeb58cbd6e7275",
+ "7c0e88c88899a779228465074797cd4c2e1498d259b54390b85e3eef1c02df60e743f1"
+ "b840382c4bccaf3bafb4ca8429bea063",
+ "88319d6e1d3ffa5f987199166c8a9b56c2aeba5a",
+ "98f4826f05a265e6dd2be82db241c0fbbbf9ffb1c173aa83964b7cf539304373636525"
+ "3ddbc5db8778371495da76d269e5db3e",
+ "291ef1982e4defedaa2249f898556b47"},
+ {"ec0c2ba17aa95cd6afffe949da9cc3a8", "296bce5b50b7d66096d627ef",
+ "b85b3753535b825cbe5f632c0b843c741351f18aa484281aebec2f45bb9eea2d79d987"
+ "b764b9611f6c0f8641843d5d58f3a242",
+ "f8d00f05d22bf68599bcdeb131292ad6e2df5d14",
+ "a7443d31c26bdf2a1c945e29ee4bd344a99cfaf3aa71f8b3f191f83c2adfc7a0716299"
+ "5506fde6309ffc19e716eddf1a828c5a",
+ "890147971946b627c40016da1ecf3e77"},
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_4[] = {
+ {"2c1f21cf0f6fb3661943155c3e3d8492", "23cb5ff362e22426984d1907",
+ "42f758836986954db44bf37c6ef5e4ac0adaf38f27252a1b82d02ea949c8a1a2dbc0d6"
+ "8b5615ba7c1220ff6510e259f06655d8",
+ "5d3624879d35e46849953e45a32a624d6a6c536ed9857c613b572b0333e701557a713e"
+ "3f010ecdf9a6bd6c9e3e44b065208645aff4aabee611b391528514170084ccf587177f"
+ "4488f33cfb5e979e42b6e1cfc0a60238982a7aec",
+ "81824f0e0d523db30d3da369fdc0d60894c7a0a20646dd015073ad2732bd989b14a222"
+ "b6ad57af43e1895df9dca2a5344a62cc",
+ "57a3ee28136e94c74838997ae9823f3a"},
+ {"d9f7d2411091f947b4d6f1e2d1f0fb2e", "e1934f5db57cc983e6b180e7",
+ "73ed042327f70fe9c572a61545eda8b2a0c6e1d6c291ef19248e973aee6c312012f490"
+ "c2c6f6166f4a59431e182663fcaea05a",
+ "0a8a18a7150e940c3d87b38e73baee9a5c049ee21795663e264b694a949822b639092d"
+ "0e67015e86363583fcf0ca645af9f43375f05fdb4ce84f411dcbca73c2220dea03a201"
+ "15d2e51398344b16bee1ed7c499b353d6c597af8",
+ "aaadbd5c92e9151ce3db7210b8714126b73e43436d242677afa50384f2149b831f1d57"
+ "3c7891c2a91fbc48db29967ec9542b23",
+ "21b51ca862cb637cdd03b99a0f93b134"},
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_5[] = {
+ {"fe9bb47deb3a61e423c2231841cfd1fb", "4d328eb776f500a2f7fb47aa",
+ "f1cc3818e421876bb6b8bbd6c9", "", "b88c5c1977b35b517b0aeae967",
+ "43fd4727fe5cdb4b5b42818dea7ef8c9"},
+ {"6703df3701a7f54911ca72e24dca046a", "12823ab601c350ea4bc2488c",
+ "793cd125b0b84a043e3ac67717", "", "b2051c80014f42f08735a7b0cd",
+ "38e6bcd29962e5f2c13626b85a877101"},
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector* const test_group_array[] = {
+ test_group_0, test_group_1, test_group_2,
+ test_group_3, test_group_4, test_group_5,
+};
+
+} // namespace
+
+namespace quic {
+namespace test {
+
+// EncryptWithNonce wraps the |Encrypt| method of |encrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the ciphertext.
+QuicData* EncryptWithNonce(Aes128GcmEncrypter* encrypter,
+ absl::string_view nonce,
+ absl::string_view associated_data,
+ absl::string_view plaintext) {
+ size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length());
+ std::unique_ptr<char[]> ciphertext(new char[ciphertext_size]);
+
+ if (!encrypter->Encrypt(nonce, associated_data, plaintext,
+ reinterpret_cast<unsigned char*>(ciphertext.get()))) {
+ return nullptr;
+ }
+
+ return new QuicData(ciphertext.release(), ciphertext_size, true);
+}
+
+class Aes128GcmEncrypterTest : public QuicTest {};
+
+TEST_F(Aes128GcmEncrypterTest, Encrypt) {
+ for (size_t i = 0; i < ABSL_ARRAYSIZE(test_group_array); i++) {
+ SCOPED_TRACE(i);
+ const TestVector* test_vectors = test_group_array[i];
+ const TestGroupInfo& test_info = test_group_info[i];
+ for (size_t j = 0; test_vectors[j].key != nullptr; j++) {
+ // Decode the test vector.
+ std::string key = absl::HexStringToBytes(test_vectors[j].key);
+ std::string iv = absl::HexStringToBytes(test_vectors[j].iv);
+ std::string pt = absl::HexStringToBytes(test_vectors[j].pt);
+ std::string aad = absl::HexStringToBytes(test_vectors[j].aad);
+ std::string ct = absl::HexStringToBytes(test_vectors[j].ct);
+ std::string tag = absl::HexStringToBytes(test_vectors[j].tag);
+
+ // The test vector's lengths should look sane. Note that the lengths
+ // in |test_info| are in bits.
+ EXPECT_EQ(test_info.key_len, key.length() * 8);
+ EXPECT_EQ(test_info.iv_len, iv.length() * 8);
+ EXPECT_EQ(test_info.pt_len, pt.length() * 8);
+ EXPECT_EQ(test_info.aad_len, aad.length() * 8);
+ EXPECT_EQ(test_info.pt_len, ct.length() * 8);
+ EXPECT_EQ(test_info.tag_len, tag.length() * 8);
+
+ Aes128GcmEncrypter encrypter;
+ ASSERT_TRUE(encrypter.SetKey(key));
+ std::unique_ptr<QuicData> encrypted(
+ EncryptWithNonce(&encrypter, iv,
+ // This deliberately tests that the encrypter can
+ // handle an AAD that is set to nullptr, as opposed
+ // to a zero-length, non-nullptr pointer.
+ aad.length() ? aad : absl::string_view(), pt));
+ ASSERT_TRUE(encrypted.get());
+
+ ASSERT_EQ(ct.length() + tag.length(), encrypted->length());
+ quiche::test::CompareCharArraysWithHexError(
+ "ciphertext", encrypted->data(), ct.length(), ct.data(), ct.length());
+ quiche::test::CompareCharArraysWithHexError(
+ "authentication tag", encrypted->data() + ct.length(), tag.length(),
+ tag.data(), tag.length());
+ }
+ }
+}
+
+TEST_F(Aes128GcmEncrypterTest, EncryptPacket) {
+ std::string key = absl::HexStringToBytes("d95a145250826c25a77b6a84fd4d34fc");
+ std::string iv = absl::HexStringToBytes("50c4431ebb18283448e276e2");
+ uint64_t packet_num = 0x13278f44;
+ std::string aad =
+ absl::HexStringToBytes("875d49f64a70c9cbe713278f44ff000005");
+ std::string pt = absl::HexStringToBytes("aa0003a250bd000000000001");
+ std::string ct = absl::HexStringToBytes(
+ "7dd4708b989ee7d38a013e3656e9b37beefd05808fe1ab41e3b4f2c0");
+
+ std::vector<char> out(ct.size());
+ size_t out_size;
+
+ Aes128GcmEncrypter encrypter;
+ ASSERT_TRUE(encrypter.SetKey(key));
+ ASSERT_TRUE(encrypter.SetIV(iv));
+ ASSERT_TRUE(encrypter.EncryptPacket(packet_num, aad, pt, out.data(),
+ &out_size, out.size()));
+ EXPECT_EQ(out_size, out.size());
+ quiche::test::CompareCharArraysWithHexError("ciphertext", out.data(),
+ out.size(), ct.data(), ct.size());
+}
+
+TEST_F(Aes128GcmEncrypterTest, GetMaxPlaintextSize) {
+ Aes128GcmEncrypter encrypter;
+ EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1016));
+ EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(116));
+ EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(26));
+}
+
+TEST_F(Aes128GcmEncrypterTest, GetCiphertextSize) {
+ Aes128GcmEncrypter encrypter;
+ EXPECT_EQ(1016u, encrypter.GetCiphertextSize(1000));
+ EXPECT_EQ(116u, encrypter.GetCiphertextSize(100));
+ EXPECT_EQ(26u, encrypter.GetCiphertextSize(10));
+}
+
+TEST_F(Aes128GcmEncrypterTest, GenerateHeaderProtectionMask) {
+ Aes128GcmEncrypter encrypter;
+ std::string key = absl::HexStringToBytes("d9132370cb18476ab833649cf080d970");
+ std::string sample =
+ absl::HexStringToBytes("d1d7998068517adb769b48b924a32c47");
+ ASSERT_TRUE(encrypter.SetHeaderProtectionKey(key));
+ std::string mask = encrypter.GenerateHeaderProtectionMask(sample);
+ std::string expected_mask =
+ absl::HexStringToBytes("b132c37d6164da4ea4dc9b763aceec27");
+ quiche::test::CompareCharArraysWithHexError(
+ "header protection mask", mask.data(), mask.size(), expected_mask.data(),
+ expected_mask.size());
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_256_gcm_decrypter.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_256_gcm_decrypter.cc
new file mode 100644
index 00000000000..58d4e3c2cf3
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_256_gcm_decrypter.cc
@@ -0,0 +1,34 @@
+// Copyright (c) 2017 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 "quiche/quic/core/crypto/aes_256_gcm_decrypter.h"
+
+#include "openssl/aead.h"
+#include "openssl/tls1.h"
+#include "quiche/quic/platform/api/quic_flag_utils.h"
+#include "quiche/quic/platform/api/quic_flags.h"
+
+namespace quic {
+
+namespace {
+
+const size_t kKeySize = 32;
+const size_t kNonceSize = 12;
+
+} // namespace
+
+Aes256GcmDecrypter::Aes256GcmDecrypter()
+ : AesBaseDecrypter(EVP_aead_aes_256_gcm, kKeySize, kAuthTagSize, kNonceSize,
+ /* use_ietf_nonce_construction */ true) {
+ static_assert(kKeySize <= kMaxKeySize, "key size too big");
+ static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
+}
+
+Aes256GcmDecrypter::~Aes256GcmDecrypter() {}
+
+uint32_t Aes256GcmDecrypter::cipher_id() const {
+ return TLS1_CK_AES_256_GCM_SHA384;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_256_gcm_decrypter.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_256_gcm_decrypter.h
new file mode 100644
index 00000000000..dc4f8c08486
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_256_gcm_decrypter.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2017 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_AES_256_GCM_DECRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_AES_256_GCM_DECRYPTER_H_
+
+#include <cstdint>
+
+#include "quiche/quic/core/crypto/aes_base_decrypter.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// An Aes256GcmDecrypter is a QuicDecrypter that implements the
+// AEAD_AES_256_GCM algorithm specified in RFC 5116 for use in IETF QUIC.
+//
+// It uses an authentication tag of 16 bytes (128 bits). It uses a 12 byte IV
+// that is XOR'd with the packet number to compute the nonce.
+class QUIC_EXPORT_PRIVATE Aes256GcmDecrypter : public AesBaseDecrypter {
+ public:
+ enum {
+ kAuthTagSize = 16,
+ };
+
+ Aes256GcmDecrypter();
+ Aes256GcmDecrypter(const Aes256GcmDecrypter&) = delete;
+ Aes256GcmDecrypter& operator=(const Aes256GcmDecrypter&) = delete;
+ ~Aes256GcmDecrypter() override;
+
+ uint32_t cipher_id() const override;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_AES_256_GCM_DECRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_256_gcm_decrypter_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_256_gcm_decrypter_test.cc
new file mode 100644
index 00000000000..7c48c8cd87e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_256_gcm_decrypter_test.cc
@@ -0,0 +1,297 @@
+// Copyright (c) 2017 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 "quiche/quic/core/crypto/aes_256_gcm_decrypter.h"
+
+#include <memory>
+#include <string>
+
+#include "absl/base/macros.h"
+#include "absl/strings/escaping.h"
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/quic_test_utils.h"
+#include "quiche/common/test_tools/quiche_test_utils.h"
+
+namespace {
+
+// The AES GCM test vectors come from the file gcmDecrypt256.rsp
+// downloaded from
+// https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/CAVP-TESTING-BLOCK-CIPHER-MODES#GCMVS
+// on 2017-09-27. The test vectors in that file look like this:
+//
+// [Keylen = 256]
+// [IVlen = 96]
+// [PTlen = 0]
+// [AADlen = 0]
+// [Taglen = 128]
+//
+// Count = 0
+// Key = f5a2b27c74355872eb3ef6c5feafaa740e6ae990d9d48c3bd9bb8235e589f010
+// IV = 58d2240f580a31c1d24948e9
+// CT =
+// AAD =
+// Tag = 15e051a5e4a5f5da6cea92e2ebee5bac
+// PT =
+//
+// Count = 1
+// Key = e5a8123f2e2e007d4e379ba114a2fb66e6613f57c72d4e4f024964053028a831
+// IV = 51e43385bf533e168427e1ad
+// CT =
+// AAD =
+// Tag = 38fe845c66e66bdd884c2aecafd280e6
+// FAIL
+//
+// ...
+//
+// The gcmDecrypt256.rsp file is huge (3.0 MB), so a few test vectors were
+// selected for this unit test.
+
+// Describes a group of test vectors that all have a given key length, IV
+// length, plaintext length, AAD length, and tag length.
+struct TestGroupInfo {
+ size_t key_len;
+ size_t iv_len;
+ size_t pt_len;
+ size_t aad_len;
+ size_t tag_len;
+};
+
+// Each test vector consists of six strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a nullptr |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ // Input:
+ const char* key;
+ const char* iv;
+ const char* ct;
+ const char* aad;
+ const char* tag;
+
+ // Expected output:
+ const char* pt; // An empty string "" means decryption succeeded and
+ // the plaintext is zero-length. nullptr means decryption
+ // failed.
+};
+
+const TestGroupInfo test_group_info[] = {
+ {256, 96, 0, 0, 128}, {256, 96, 0, 128, 128}, {256, 96, 128, 0, 128},
+ {256, 96, 408, 160, 128}, {256, 96, 408, 720, 128}, {256, 96, 104, 0, 128},
+};
+
+const TestVector test_group_0[] = {
+ {"f5a2b27c74355872eb3ef6c5feafaa740e6ae990d9d48c3bd9bb8235e589f010",
+ "58d2240f580a31c1d24948e9", "", "", "15e051a5e4a5f5da6cea92e2ebee5bac",
+ ""},
+ {
+ "e5a8123f2e2e007d4e379ba114a2fb66e6613f57c72d4e4f024964053028a831",
+ "51e43385bf533e168427e1ad", "", "", "38fe845c66e66bdd884c2aecafd280e6",
+ nullptr // FAIL
+ },
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_1[] = {
+ {"6dfdafd6703c285c01f14fd10a6012862b2af950d4733abb403b2e745b26945d",
+ "3749d0b3d5bacb71be06ade6", "", "c0d249871992e70302ae008193d1e89f",
+ "4aa4cc69f84ee6ac16d9bfb4e05de500", ""},
+ {
+ "2c392a5eb1a9c705371beda3a901c7c61dca4d93b4291de1dd0dd15ec11ffc45",
+ "0723fb84a08f4ea09841f32a", "", "140be561b6171eab942c486a94d33d43",
+ "aa0e1c9b57975bfc91aa137231977d2c", nullptr // FAIL
+ },
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_2[] = {
+ {"4c8ebfe1444ec1b2d503c6986659af2c94fafe945f72c1e8486a5acfedb8a0f8",
+ "473360e0ad24889959858995", "d2c78110ac7e8f107c0df0570bd7c90c", "",
+ "c26a379b6d98ef2852ead8ce83a833a7", "7789b41cb3ee548814ca0b388c10b343"},
+ {"3934f363fd9f771352c4c7a060682ed03c2864223a1573b3af997e2ababd60ab",
+ "efe2656d878c586e41c539c4", "e0de64302ac2d04048d65a87d2ad09fe", "",
+ "33cbd8d2fb8a3a03e30c1eb1b53c1d99", "697aff2d6b77e5ed6232770e400c1ead"},
+ {
+ "c997768e2d14e3d38259667a6649079de77beb4543589771e5068e6cd7cd0b14",
+ "835090aed9552dbdd45277e2", "9f6607d68e22ccf21928db0986be126e", "",
+ "f32617f67c574fd9f44ef76ff880ab9f", nullptr // FAIL
+ },
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_3[] = {
+ {
+ "e9d381a9c413bee66175d5586a189836e5c20f5583535ab4d3f3e612dc21700e",
+ "23e81571da1c7821c681c7ca",
+ "a25f3f580306cd5065d22a6b7e9660110af7204bb77d370f7f34bee547feeff7b32a59"
+ "6fce29c9040e68b1589aad48da881990",
+ "6f39c9ae7b8e8a58a95f0dd8ea6a9087cbccdfd6",
+ "5b6dcd70eefb0892fab1539298b92a4b",
+ nullptr // FAIL
+ },
+ {"6450d4501b1e6cfbe172c4c8570363e96b496591b842661c28c2f6c908379cad",
+ "7e4262035e0bf3d60e91668a",
+ "5a99b336fd3cfd82f10fb08f7045012415f0d9a06bb92dcf59c6f0dbe62d433671aacb8a1"
+ "c52ce7bbf6aea372bf51e2ba79406",
+ "f1c522f026e4c5d43851da516a1b78768ab18171",
+ "fe93b01636f7bb0458041f213e98de65",
+ "17449e236ef5858f6d891412495ead4607bfae2a2d735182a2a0242f9d52fc5345ef912db"
+ "e16f3bb4576fe3bcafe336dee6085"},
+ {"90f2e71ccb1148979cb742efc8f921de95457d898c84ce28edeed701650d3a26",
+ "aba58ad60047ba553f6e4c98",
+ "3fc77a5fe9203d091c7916587c9763cf2e4d0d53ca20b078b851716f1dab4873fe342b7b3"
+ "01402f015d00263bf3f77c58a99d6",
+ "2abe465df6e5be47f05b92c9a93d76ae3611fac5",
+ "9cb3d04637048bc0bddef803ffbb56cf",
+ "1d21639640e11638a2769e3fab78778f84be3f4a8ce28dfd99cb2e75171e05ea8e94e30aa"
+ "78b54bb402b39d613616a8ed951dc"},
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_4[] = {
+ {
+ "e36aca93414b13f5313e76a7244588ee116551d1f34c32859166f2eb0ac1a9b7",
+ "e9e701b1ccef6bddd03391d8",
+ "5b059ac6733b6de0e8cf5b88b7301c02c993426f71bb12abf692e9deeacfac1ff1644c"
+ "87d4df130028f515f0feda636309a24d",
+ "6a08fe6e55a08f283cec4c4b37676e770f402af6102f548ad473ec6236da764f7076ff"
+ "d41bbd9611b439362d899682b7b0f839fc5a68d9df54afd1e2b3c4e7d072454ee27111"
+ "d52193d28b9c4f925d2a8b451675af39191a2cba",
+ "43c7c9c93cc265fc8e192000e0417b5b",
+ nullptr // FAIL
+ },
+ {"5f72046245d3f4a0877e50a86554bfd57d1c5e073d1ed3b5451f6d0fc2a8507a",
+ "ea6f5b391e44b751b26bce6f",
+ "0e6e0b2114c40769c15958d965a14dcf50b680e0185a4409d77d894ca15b1e698dd83b353"
+ "6b18c05d8cd0873d1edce8150ecb5",
+ "9b3a68c941d42744673fb60fea49075eae77322e7e70e34502c115b6495ebfc796d629080"
+ "7653c6b53cd84281bd0311656d0013f44619d2748177e99e8f8347c989a7b59f9d8dcf00f"
+ "31db0684a4a83e037e8777bae55f799b0d",
+ "fdaaff86ceb937502cd9012d03585800",
+ "b0a881b751cc1eb0c912a4cf9bd971983707dbd2411725664503455c55db25cdb19bc669c"
+ "2654a3a8011de6bf7eff3f9f07834"},
+ {"ab639bae205547607506522bd3cdca7861369e2b42ef175ff135f6ba435d5a8e",
+ "5fbb63eb44bd59fee458d8f6",
+ "9a34c62bed0972285503a32812877187a54dedbd55d2317fed89282bf1af4ba0b6bb9f9e1"
+ "6dd86da3b441deb7841262bc6bd63",
+ "1ef2b1768b805587935ffaf754a11bd2a305076d6374f1f5098b1284444b78f55408a786d"
+ "a37e1b7f1401c330d3585ef56f3e4d35eaaac92e1381d636477dc4f4beaf559735e902d6b"
+ "e58723257d4ac1ed9bd213de387f35f3c4",
+ "e0299e079bff46fd12e36d1c60e41434",
+ "e5a3ce804a8516cdd12122c091256b789076576040dbf3c55e8be3c016025896b8a72532b"
+ "fd51196cc82efca47aa0fd8e2e0dc"},
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_5[] = {
+ {
+ "8b37c4b8cf634704920059866ad96c49e9da502c63fca4a3a7a4dcec74cb0610",
+ "cb59344d2b06c4ae57cd0ea4", "66ab935c93555e786b775637a3", "",
+ "d8733acbb564d8afaa99d7ca2e2f92a9", nullptr // FAIL
+ },
+ {"a71dac1377a3bf5d7fb1b5e36bee70d2e01de2a84a1c1009ba7448f7f26131dc",
+ "c5b60dda3f333b1146e9da7c", "43af49ec1ae3738a20755034d6", "",
+ "6f80b6ef2d8830a55eb63680a8dff9e0", "5b87141335f2becac1a559e05f"},
+ {"dc1f64681014be221b00793bbcf5a5bc675b968eb7a3a3d5aa5978ef4fa45ecc",
+ "056ae9a1a69e38af603924fe", "33013a48d9ea0df2911d583271", "",
+ "5b8f9cc22303e979cd1524187e9f70fe", "2a7e05612191c8bce2f529dca9"},
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector* const test_group_array[] = {
+ test_group_0, test_group_1, test_group_2,
+ test_group_3, test_group_4, test_group_5,
+};
+
+} // namespace
+
+namespace quic {
+namespace test {
+
+// DecryptWithNonce wraps the |Decrypt| method of |decrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the plaintext.
+QuicData* DecryptWithNonce(Aes256GcmDecrypter* decrypter,
+ absl::string_view nonce,
+ absl::string_view associated_data,
+ absl::string_view ciphertext) {
+ decrypter->SetIV(nonce);
+ std::unique_ptr<char[]> output(new char[ciphertext.length()]);
+ size_t output_length = 0;
+ const bool success =
+ decrypter->DecryptPacket(0, associated_data, ciphertext, output.get(),
+ &output_length, ciphertext.length());
+ if (!success) {
+ return nullptr;
+ }
+ return new QuicData(output.release(), output_length, true);
+}
+
+class Aes256GcmDecrypterTest : public QuicTest {};
+
+TEST_F(Aes256GcmDecrypterTest, Decrypt) {
+ for (size_t i = 0; i < ABSL_ARRAYSIZE(test_group_array); i++) {
+ SCOPED_TRACE(i);
+ const TestVector* test_vectors = test_group_array[i];
+ const TestGroupInfo& test_info = test_group_info[i];
+ for (size_t j = 0; test_vectors[j].key != nullptr; j++) {
+ // If not present then decryption is expected to fail.
+ bool has_pt = test_vectors[j].pt;
+
+ // Decode the test vector.
+ std::string key = absl::HexStringToBytes(test_vectors[j].key);
+ std::string iv = absl::HexStringToBytes(test_vectors[j].iv);
+ std::string ct = absl::HexStringToBytes(test_vectors[j].ct);
+ std::string aad = absl::HexStringToBytes(test_vectors[j].aad);
+ std::string tag = absl::HexStringToBytes(test_vectors[j].tag);
+ std::string pt;
+ if (has_pt) {
+ pt = absl::HexStringToBytes(test_vectors[j].pt);
+ }
+
+ // The test vector's lengths should look sane. Note that the lengths
+ // in |test_info| are in bits.
+ EXPECT_EQ(test_info.key_len, key.length() * 8);
+ EXPECT_EQ(test_info.iv_len, iv.length() * 8);
+ EXPECT_EQ(test_info.pt_len, ct.length() * 8);
+ EXPECT_EQ(test_info.aad_len, aad.length() * 8);
+ EXPECT_EQ(test_info.tag_len, tag.length() * 8);
+ if (has_pt) {
+ EXPECT_EQ(test_info.pt_len, pt.length() * 8);
+ }
+ std::string ciphertext = ct + tag;
+
+ Aes256GcmDecrypter decrypter;
+ ASSERT_TRUE(decrypter.SetKey(key));
+
+ std::unique_ptr<QuicData> decrypted(DecryptWithNonce(
+ &decrypter, iv,
+ // This deliberately tests that the decrypter can
+ // handle an AAD that is set to nullptr, as opposed
+ // to a zero-length, non-nullptr pointer.
+ aad.length() ? aad : absl::string_view(), ciphertext));
+ if (!decrypted) {
+ EXPECT_FALSE(has_pt);
+ continue;
+ }
+ EXPECT_TRUE(has_pt);
+
+ ASSERT_EQ(pt.length(), decrypted->length());
+ quiche::test::CompareCharArraysWithHexError(
+ "plaintext", decrypted->data(), pt.length(), pt.data(), pt.length());
+ }
+ }
+}
+
+TEST_F(Aes256GcmDecrypterTest, GenerateHeaderProtectionMask) {
+ Aes256GcmDecrypter decrypter;
+ std::string key = absl::HexStringToBytes(
+ "ed23ecbf54d426def5c52c3dcfc84434e62e57781d3125bb21ed91b7d3e07788");
+ std::string sample =
+ absl::HexStringToBytes("4d190c474be2b8babafb49ec4e38e810");
+ QuicDataReader sample_reader(sample.data(), sample.size());
+ ASSERT_TRUE(decrypter.SetHeaderProtectionKey(key));
+ std::string mask = decrypter.GenerateHeaderProtectionMask(&sample_reader);
+ std::string expected_mask =
+ absl::HexStringToBytes("db9ed4e6ccd033af2eae01407199c56e");
+ quiche::test::CompareCharArraysWithHexError(
+ "header protection mask", mask.data(), mask.size(), expected_mask.data(),
+ expected_mask.size());
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_256_gcm_encrypter.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_256_gcm_encrypter.cc
new file mode 100644
index 00000000000..802ff992c9b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_256_gcm_encrypter.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 2017 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 "quiche/quic/core/crypto/aes_256_gcm_encrypter.h"
+
+#include "openssl/evp.h"
+
+namespace quic {
+
+namespace {
+
+const size_t kKeySize = 32;
+const size_t kNonceSize = 12;
+
+} // namespace
+
+Aes256GcmEncrypter::Aes256GcmEncrypter()
+ : AesBaseEncrypter(EVP_aead_aes_256_gcm, kKeySize, kAuthTagSize, kNonceSize,
+ /* use_ietf_nonce_construction */ true) {
+ static_assert(kKeySize <= kMaxKeySize, "key size too big");
+ static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
+}
+
+Aes256GcmEncrypter::~Aes256GcmEncrypter() {}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_256_gcm_encrypter.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_256_gcm_encrypter.h
new file mode 100644
index 00000000000..9ba47f12130
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_256_gcm_encrypter.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2017 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_AES_256_GCM_ENCRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_AES_256_GCM_ENCRYPTER_H_
+
+#include "quiche/quic/core/crypto/aes_base_encrypter.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// An Aes256GcmEncrypter is a QuicEncrypter that implements the
+// AEAD_AES_256_GCM algorithm specified in RFC 5116 for use in IETF QUIC.
+//
+// It uses an authentication tag of 16 bytes (128 bits). It uses a 12 byte IV
+// that is XOR'd with the packet number to compute the nonce.
+class QUIC_EXPORT_PRIVATE Aes256GcmEncrypter : public AesBaseEncrypter {
+ public:
+ enum {
+ kAuthTagSize = 16,
+ };
+
+ Aes256GcmEncrypter();
+ Aes256GcmEncrypter(const Aes256GcmEncrypter&) = delete;
+ Aes256GcmEncrypter& operator=(const Aes256GcmEncrypter&) = delete;
+ ~Aes256GcmEncrypter() override;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_AES_256_GCM_ENCRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_256_gcm_encrypter_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_256_gcm_encrypter_test.cc
new file mode 100644
index 00000000000..6389fdb53ef
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_256_gcm_encrypter_test.cc
@@ -0,0 +1,259 @@
+// Copyright (c) 2017 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 "quiche/quic/core/crypto/aes_256_gcm_encrypter.h"
+
+#include <memory>
+#include <string>
+
+#include "absl/base/macros.h"
+#include "absl/strings/escaping.h"
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/quic_test_utils.h"
+#include "quiche/common/test_tools/quiche_test_utils.h"
+
+namespace {
+
+// The AES GCM test vectors come from the file gcmEncryptExtIV256.rsp
+// downloaded from
+// https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/CAVP-TESTING-BLOCK-CIPHER-MODES#GCMVS
+// on 2017-09-27. The test vectors in that file look like this:
+//
+// [Keylen = 256]
+// [IVlen = 96]
+// [PTlen = 0]
+// [AADlen = 0]
+// [Taglen = 128]
+//
+// Count = 0
+// Key = b52c505a37d78eda5dd34f20c22540ea1b58963cf8e5bf8ffa85f9f2492505b4
+// IV = 516c33929df5a3284ff463d7
+// PT =
+// AAD =
+// CT =
+// Tag = bdc1ac884d332457a1d2664f168c76f0
+//
+// Count = 1
+// Key = 5fe0861cdc2690ce69b3658c7f26f8458eec1c9243c5ba0845305d897e96ca0f
+// IV = 770ac1a5a3d476d5d96944a1
+// PT =
+// AAD =
+// CT =
+// Tag = 196d691e1047093ca4b3d2ef4baba216
+//
+// ...
+//
+// The gcmEncryptExtIV256.rsp file is huge (3.2 MB), so a few test vectors were
+// selected for this unit test.
+
+// Describes a group of test vectors that all have a given key length, IV
+// length, plaintext length, AAD length, and tag length.
+struct TestGroupInfo {
+ size_t key_len;
+ size_t iv_len;
+ size_t pt_len;
+ size_t aad_len;
+ size_t tag_len;
+};
+
+// Each test vector consists of six strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a nullptr |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ const char* key;
+ const char* iv;
+ const char* pt;
+ const char* aad;
+ const char* ct;
+ const char* tag;
+};
+
+const TestGroupInfo test_group_info[] = {
+ {256, 96, 0, 0, 128}, {256, 96, 0, 128, 128}, {256, 96, 128, 0, 128},
+ {256, 96, 408, 160, 128}, {256, 96, 408, 720, 128}, {256, 96, 104, 0, 128},
+};
+
+const TestVector test_group_0[] = {
+ {"b52c505a37d78eda5dd34f20c22540ea1b58963cf8e5bf8ffa85f9f2492505b4",
+ "516c33929df5a3284ff463d7", "", "", "",
+ "bdc1ac884d332457a1d2664f168c76f0"},
+ {"5fe0861cdc2690ce69b3658c7f26f8458eec1c9243c5ba0845305d897e96ca0f",
+ "770ac1a5a3d476d5d96944a1", "", "", "",
+ "196d691e1047093ca4b3d2ef4baba216"},
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_1[] = {
+ {"78dc4e0aaf52d935c3c01eea57428f00ca1fd475f5da86a49c8dd73d68c8e223",
+ "d79cf22d504cc793c3fb6c8a", "", "b96baa8c1c75a671bfb2d08d06be5f36", "",
+ "3e5d486aa2e30b22e040b85723a06e76"},
+ {"4457ff33683cca6ca493878bdc00373893a9763412eef8cddb54f91318e0da88",
+ "699d1f29d7b8c55300bb1fd2", "", "6749daeea367d0e9809e2dc2f309e6e3", "",
+ "d60c74d2517fde4a74e0cd4709ed43a9"},
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_2[] = {
+ {"31bdadd96698c204aa9ce1448ea94ae1fb4a9a0b3c9d773b51bb1822666b8f22",
+ "0d18e06c7c725ac9e362e1ce", "2db5168e932556f8089a0622981d017d", "",
+ "fa4362189661d163fcd6a56d8bf0405a", "d636ac1bbedd5cc3ee727dc2ab4a9489"},
+ {"460fc864972261c2560e1eb88761ff1c992b982497bd2ac36c04071cbb8e5d99",
+ "8a4a16b9e210eb68bcb6f58d", "99e4e926ffe927f691893fb79a96b067", "",
+ "133fc15751621b5f325c7ff71ce08324", "ec4e87e0cf74a13618d0b68636ba9fa7"},
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_3[] = {
+ {"24501ad384e473963d476edcfe08205237acfd49b5b8f33857f8114e863fec7f",
+ "9ff18563b978ec281b3f2794",
+ "27f348f9cdc0c5bd5e66b1ccb63ad920ff2219d14e8d631b3872265cf117ee86757accb15"
+ "8bd9abb3868fdc0d0b074b5f01b2c",
+ "adb5ec720ccf9898500028bf34afccbcaca126ef",
+ "eb7cb754c824e8d96f7c6d9b76c7d26fb874ffbf1d65c6f64a698d839b0b06145dae82057"
+ "ad55994cf59ad7f67c0fa5e85fab8",
+ "bc95c532fecc594c36d1550286a7a3f0"},
+ {"fb43f5ab4a1738a30c1e053d484a94254125d55dccee1ad67c368bc1a985d235",
+ "9fbb5f8252db0bca21f1c230",
+ "34b797bb82250e23c5e796db2c37e488b3b99d1b981cea5e5b0c61a0b39adb6bd6ef1f507"
+ "22e2e4f81115cfcf53f842e2a6c08",
+ "98f8ae1735c39f732e2cbee1156dabeb854ec7a2",
+ "871cd53d95a8b806bd4821e6c4456204d27fd704ba3d07ce25872dc604ea5c5ea13322186"
+ "b7489db4fa060c1fd4159692612c8",
+ "07b48e4a32fac47e115d7ac7445d8330"},
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_4[] = {
+ {"148579a3cbca86d5520d66c0ec71ca5f7e41ba78e56dc6eebd566fed547fe691",
+ "b08a5ea1927499c6ecbfd4e0",
+ "9d0b15fdf1bd595f91f8b3abc0f7dec927dfd4799935a1795d9ce00c9b879434420fe42c2"
+ "75a7cd7b39d638fb81ca52b49dc41",
+ "e4f963f015ffbb99ee3349bbaf7e8e8e6c2a71c230a48f9d59860a29091d2747e01a5ca57"
+ "2347e247d25f56ba7ae8e05cde2be3c97931292c02370208ecd097ef692687fecf2f419d3"
+ "200162a6480a57dad408a0dfeb492e2c5d",
+ "2097e372950a5e9383c675e89eea1c314f999159f5611344b298cda45e62843716f215f82"
+ "ee663919c64002a5c198d7878fd3f",
+ "adbecdb0d5c2224d804d2886ff9a5760"},
+ {"e49af19182faef0ebeeba9f2d3be044e77b1212358366e4ef59e008aebcd9788",
+ "e7f37d79a6a487a5a703edbb",
+ "461cd0caf7427a3d44408d825ed719237272ecd503b9094d1f62c97d63ed83a0b50bdc804"
+ "ffdd7991da7a5b6dcf48d4bcd2cbc",
+ "19a9a1cfc647346781bef51ed9070d05f99a0e0192a223c5cd2522dbdf97d9739dd39fb17"
+ "8ade3339e68774b058aa03e9a20a9a205bc05f32381df4d63396ef691fefd5a71b49a2ad8"
+ "2d5ea428778ca47ee1398792762413cff4",
+ "32ca3588e3e56eb4c8301b009d8b84b8a900b2b88ca3c21944205e9dd7311757b51394ae9"
+ "0d8bb3807b471677614f4198af909",
+ "3e403d035c71d88f1be1a256c89ba6ad"},
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector test_group_5[] = {
+ {"82c4f12eeec3b2d3d157b0f992d292b237478d2cecc1d5f161389b97f999057a",
+ "7b40b20f5f397177990ef2d1", "982a296ee1cd7086afad976945", "",
+ "ec8e05a0471d6b43a59ca5335f", "113ddeafc62373cac2f5951bb9165249"},
+ {"db4340af2f835a6c6d7ea0ca9d83ca81ba02c29b7410f221cb6071114e393240",
+ "40e438357dd80a85cac3349e", "8ddb3397bd42853193cb0f80c9", "",
+ "b694118c85c41abf69e229cb0f", "c07f1b8aafbd152f697eb67f2a85fe45"},
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+const TestVector* const test_group_array[] = {
+ test_group_0, test_group_1, test_group_2,
+ test_group_3, test_group_4, test_group_5,
+};
+
+} // namespace
+
+namespace quic {
+namespace test {
+
+// EncryptWithNonce wraps the |Encrypt| method of |encrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the ciphertext.
+QuicData* EncryptWithNonce(Aes256GcmEncrypter* encrypter,
+ absl::string_view nonce,
+ absl::string_view associated_data,
+ absl::string_view plaintext) {
+ size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length());
+ std::unique_ptr<char[]> ciphertext(new char[ciphertext_size]);
+
+ if (!encrypter->Encrypt(nonce, associated_data, plaintext,
+ reinterpret_cast<unsigned char*>(ciphertext.get()))) {
+ return nullptr;
+ }
+
+ return new QuicData(ciphertext.release(), ciphertext_size, true);
+}
+
+class Aes256GcmEncrypterTest : public QuicTest {};
+
+TEST_F(Aes256GcmEncrypterTest, Encrypt) {
+ for (size_t i = 0; i < ABSL_ARRAYSIZE(test_group_array); i++) {
+ SCOPED_TRACE(i);
+ const TestVector* test_vectors = test_group_array[i];
+ const TestGroupInfo& test_info = test_group_info[i];
+ for (size_t j = 0; test_vectors[j].key != nullptr; j++) {
+ // Decode the test vector.
+ std::string key = absl::HexStringToBytes(test_vectors[j].key);
+ std::string iv = absl::HexStringToBytes(test_vectors[j].iv);
+ std::string pt = absl::HexStringToBytes(test_vectors[j].pt);
+ std::string aad = absl::HexStringToBytes(test_vectors[j].aad);
+ std::string ct = absl::HexStringToBytes(test_vectors[j].ct);
+ std::string tag = absl::HexStringToBytes(test_vectors[j].tag);
+
+ // The test vector's lengths should look sane. Note that the lengths
+ // in |test_info| are in bits.
+ EXPECT_EQ(test_info.key_len, key.length() * 8);
+ EXPECT_EQ(test_info.iv_len, iv.length() * 8);
+ EXPECT_EQ(test_info.pt_len, pt.length() * 8);
+ EXPECT_EQ(test_info.aad_len, aad.length() * 8);
+ EXPECT_EQ(test_info.pt_len, ct.length() * 8);
+ EXPECT_EQ(test_info.tag_len, tag.length() * 8);
+
+ Aes256GcmEncrypter encrypter;
+ ASSERT_TRUE(encrypter.SetKey(key));
+ std::unique_ptr<QuicData> encrypted(
+ EncryptWithNonce(&encrypter, iv,
+ // This deliberately tests that the encrypter can
+ // handle an AAD that is set to nullptr, as opposed
+ // to a zero-length, non-nullptr pointer.
+ aad.length() ? aad : absl::string_view(), pt));
+ ASSERT_TRUE(encrypted.get());
+
+ ASSERT_EQ(ct.length() + tag.length(), encrypted->length());
+ quiche::test::CompareCharArraysWithHexError(
+ "ciphertext", encrypted->data(), ct.length(), ct.data(), ct.length());
+ quiche::test::CompareCharArraysWithHexError(
+ "authentication tag", encrypted->data() + ct.length(), tag.length(),
+ tag.data(), tag.length());
+ }
+ }
+}
+
+TEST_F(Aes256GcmEncrypterTest, GetMaxPlaintextSize) {
+ Aes256GcmEncrypter encrypter;
+ EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1016));
+ EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(116));
+ EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(26));
+}
+
+TEST_F(Aes256GcmEncrypterTest, GetCiphertextSize) {
+ Aes256GcmEncrypter encrypter;
+ EXPECT_EQ(1016u, encrypter.GetCiphertextSize(1000));
+ EXPECT_EQ(116u, encrypter.GetCiphertextSize(100));
+ EXPECT_EQ(26u, encrypter.GetCiphertextSize(10));
+}
+
+TEST_F(Aes256GcmEncrypterTest, GenerateHeaderProtectionMask) {
+ Aes256GcmEncrypter encrypter;
+ std::string key = absl::HexStringToBytes(
+ "ed23ecbf54d426def5c52c3dcfc84434e62e57781d3125bb21ed91b7d3e07788");
+ std::string sample =
+ absl::HexStringToBytes("4d190c474be2b8babafb49ec4e38e810");
+ ASSERT_TRUE(encrypter.SetHeaderProtectionKey(key));
+ std::string mask = encrypter.GenerateHeaderProtectionMask(sample);
+ std::string expected_mask =
+ absl::HexStringToBytes("db9ed4e6ccd033af2eae01407199c56e");
+ quiche::test::CompareCharArraysWithHexError(
+ "header protection mask", mask.data(), mask.size(), expected_mask.data(),
+ expected_mask.size());
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_base_decrypter.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_base_decrypter.cc
new file mode 100644
index 00000000000..2962854c175
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_base_decrypter.cc
@@ -0,0 +1,52 @@
+// Copyright (c) 2013 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 "quiche/quic/core/crypto/aes_base_decrypter.h"
+
+#include "absl/strings/string_view.h"
+#include "openssl/aes.h"
+#include "quiche/quic/platform/api/quic_bug_tracker.h"
+
+namespace quic {
+
+bool AesBaseDecrypter::SetHeaderProtectionKey(absl::string_view key) {
+ if (key.size() != GetKeySize()) {
+ QUIC_BUG(quic_bug_10649_1) << "Invalid key size for header protection";
+ return false;
+ }
+ if (AES_set_encrypt_key(reinterpret_cast<const uint8_t*>(key.data()),
+ key.size() * 8, &pne_key_) != 0) {
+ QUIC_BUG(quic_bug_10649_2) << "Unexpected failure of AES_set_encrypt_key";
+ return false;
+ }
+ return true;
+}
+
+std::string AesBaseDecrypter::GenerateHeaderProtectionMask(
+ QuicDataReader* sample_reader) {
+ absl::string_view sample;
+ if (!sample_reader->ReadStringPiece(&sample, AES_BLOCK_SIZE)) {
+ return std::string();
+ }
+ std::string out(AES_BLOCK_SIZE, 0);
+ AES_encrypt(reinterpret_cast<const uint8_t*>(sample.data()),
+ reinterpret_cast<uint8_t*>(const_cast<char*>(out.data())),
+ &pne_key_);
+ return out;
+}
+
+QuicPacketCount AesBaseDecrypter::GetIntegrityLimit() const {
+ // For AEAD_AES_128_GCM ... endpoints that do not attempt to remove
+ // protection from packets larger than 2^11 bytes can attempt to remove
+ // protection from at most 2^57 packets.
+ // For AEAD_AES_256_GCM [the limit] is substantially larger than the limit for
+ // AEAD_AES_128_GCM. However, this document recommends that the same limit be
+ // applied to both functions as either limit is acceptably large.
+ // https://quicwg.org/base-drafts/draft-ietf-quic-tls.html#name-integrity-limit
+ static_assert(kMaxIncomingPacketSize <= 2048,
+ "This key limit requires limits on decryption payload sizes");
+ return 144115188075855872U;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_base_decrypter.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_base_decrypter.h
new file mode 100644
index 00000000000..9fa35cfd666
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_base_decrypter.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2013 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_AES_BASE_DECRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_AES_BASE_DECRYPTER_H_
+
+#include <cstddef>
+
+#include "absl/strings/string_view.h"
+#include "openssl/aes.h"
+#include "quiche/quic/core/crypto/aead_base_decrypter.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE AesBaseDecrypter : public AeadBaseDecrypter {
+ public:
+ using AeadBaseDecrypter::AeadBaseDecrypter;
+
+ bool SetHeaderProtectionKey(absl::string_view key) override;
+ std::string GenerateHeaderProtectionMask(
+ QuicDataReader* sample_reader) override;
+ QuicPacketCount GetIntegrityLimit() const override;
+
+ private:
+ // The key used for packet number encryption.
+ AES_KEY pne_key_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_AES_BASE_DECRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_base_encrypter.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_base_encrypter.cc
new file mode 100644
index 00000000000..89ab6456641
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_base_encrypter.cc
@@ -0,0 +1,48 @@
+// Copyright (c) 2013 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 "quiche/quic/core/crypto/aes_base_encrypter.h"
+
+#include "absl/strings/string_view.h"
+#include "openssl/aes.h"
+#include "quiche/quic/platform/api/quic_bug_tracker.h"
+
+namespace quic {
+
+bool AesBaseEncrypter::SetHeaderProtectionKey(absl::string_view key) {
+ if (key.size() != GetKeySize()) {
+ QUIC_BUG(quic_bug_10726_1)
+ << "Invalid key size for header protection: " << key.size();
+ return false;
+ }
+ if (AES_set_encrypt_key(reinterpret_cast<const uint8_t*>(key.data()),
+ key.size() * 8, &pne_key_) != 0) {
+ QUIC_BUG(quic_bug_10726_2) << "Unexpected failure of AES_set_encrypt_key";
+ return false;
+ }
+ return true;
+}
+
+std::string AesBaseEncrypter::GenerateHeaderProtectionMask(
+ absl::string_view sample) {
+ if (sample.size() != AES_BLOCK_SIZE) {
+ return std::string();
+ }
+ std::string out(AES_BLOCK_SIZE, 0);
+ AES_encrypt(reinterpret_cast<const uint8_t*>(sample.data()),
+ reinterpret_cast<uint8_t*>(const_cast<char*>(out.data())),
+ &pne_key_);
+ return out;
+}
+
+QuicPacketCount AesBaseEncrypter::GetConfidentialityLimit() const {
+ // For AEAD_AES_128_GCM and AEAD_AES_256_GCM ... endpoints that do not send
+ // packets larger than 2^11 bytes cannot protect more than 2^28 packets.
+ // https://quicwg.org/base-drafts/draft-ietf-quic-tls.html#name-confidentiality-limit
+ static_assert(kMaxOutgoingPacketSize <= 2048,
+ "This key limit requires limits on encryption payload sizes");
+ return 268435456U;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_base_encrypter.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_base_encrypter.h
new file mode 100644
index 00000000000..c4fdb86ecab
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/aes_base_encrypter.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2013 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_AES_BASE_ENCRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_AES_BASE_ENCRYPTER_H_
+
+#include <cstddef>
+
+#include "absl/strings/string_view.h"
+#include "openssl/aes.h"
+#include "quiche/quic/core/crypto/aead_base_encrypter.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE AesBaseEncrypter : public AeadBaseEncrypter {
+ public:
+ using AeadBaseEncrypter::AeadBaseEncrypter;
+
+ bool SetHeaderProtectionKey(absl::string_view key) override;
+ std::string GenerateHeaderProtectionMask(absl::string_view sample) override;
+ QuicPacketCount GetConfidentialityLimit() const override;
+
+ private:
+ // The key used for packet number encryption.
+ AES_KEY pne_key_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_AES_BASE_ENCRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/boring_utils.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/boring_utils.h
new file mode 100644
index 00000000000..d6a1dd413da
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/boring_utils.h
@@ -0,0 +1,34 @@
+// 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_BORING_UTILS_H_
+#define QUICHE_QUIC_CORE_CRYPTO_BORING_UTILS_H_
+
+#include "absl/strings/string_view.h"
+#include "openssl/bytestring.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+inline QUIC_EXPORT_PRIVATE absl::string_view CbsToStringPiece(CBS cbs) {
+ return absl::string_view(reinterpret_cast<const char*>(CBS_data(&cbs)),
+ CBS_len(&cbs));
+}
+
+inline QUIC_EXPORT_PRIVATE CBS StringPieceToCbs(absl::string_view piece) {
+ CBS result;
+ CBS_init(&result, reinterpret_cast<const uint8_t*>(piece.data()),
+ piece.size());
+ return result;
+}
+
+inline QUIC_EXPORT_PRIVATE bool AddStringToCbb(CBB* cbb,
+ absl::string_view piece) {
+ return 1 == CBB_add_bytes(cbb, reinterpret_cast<const uint8_t*>(piece.data()),
+ piece.size());
+}
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_BORING_UTILS_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/cert_compressor.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/cert_compressor.cc
new file mode 100644
index 00000000000..4357b9c7c82
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/cert_compressor.cc
@@ -0,0 +1,598 @@
+// Copyright (c) 2013 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 "quiche/quic/core/crypto/cert_compressor.h"
+
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/quic/platform/api/quic_bug_tracker.h"
+#include "quiche/quic/platform/api/quic_flag_utils.h"
+#include "quiche/quic/platform/api/quic_flags.h"
+#include "zlib.h"
+
+namespace quic {
+
+namespace {
+
+// kCommonCertSubstrings contains ~1500 bytes of common certificate substrings
+// in order to help zlib. This was generated via a fairly dumb algorithm from
+// the Alexa Top 5000 set - we could probably do better.
+static const unsigned char kCommonCertSubstrings[] = {
+ 0x04, 0x02, 0x30, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04,
+ 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03,
+ 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30,
+ 0x5f, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01,
+ 0x06, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86, 0xfd, 0x6d, 0x01, 0x07,
+ 0x17, 0x01, 0x30, 0x33, 0x20, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65,
+ 0x64, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x53, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x34,
+ 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31,
+ 0x32, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x53, 0x65, 0x72,
+ 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x2d, 0x61, 0x69, 0x61, 0x2e,
+ 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x45, 0x2d, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73,
+ 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x45, 0x2e, 0x63, 0x65,
+ 0x72, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x4a, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73,
+ 0x2f, 0x63, 0x70, 0x73, 0x20, 0x28, 0x63, 0x29, 0x30, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x1d, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x7b, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x0e, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xd2,
+ 0x6f, 0x64, 0x6f, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
+ 0x04, 0x14, 0xb4, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69,
+ 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x30, 0x0b, 0x06, 0x03,
+ 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x01, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30,
+ 0x81, 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08,
+ 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30,
+ 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74,
+ 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03,
+ 0x55, 0x04, 0x0a, 0x13, 0x11, 0x47, 0x6f, 0x44, 0x61, 0x64, 0x64, 0x79,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x33,
+ 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2a, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
+ 0x6f, 0x72, 0x79, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x27, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, 0x20, 0x53,
+ 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
+ 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55,
+ 0x04, 0x05, 0x13, 0x08, 0x30, 0x37, 0x39, 0x36, 0x39, 0x32, 0x38, 0x37,
+ 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x31, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x05, 0xa0, 0x30, 0x0c,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00,
+ 0x30, 0x1d, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff,
+ 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x03, 0x02, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff,
+ 0x04, 0x04, 0x03, 0x02, 0x05, 0xa0, 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d,
+ 0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86,
+ 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e,
+ 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x67, 0x64, 0x73, 0x31, 0x2d, 0x32, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74,
+ 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65,
+ 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63,
+ 0x70, 0x73, 0x30, 0x34, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17,
+ 0x0d, 0x31, 0x33, 0x30, 0x35, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x73, 0x30, 0x39, 0x30, 0x37, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x02, 0x30, 0x44, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04,
+ 0x3d, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86,
+ 0xf8, 0x45, 0x01, 0x07, 0x17, 0x06, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x06, 0x13, 0x02, 0x47, 0x42, 0x31, 0x1b, 0x53, 0x31, 0x17,
+ 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65, 0x72,
+ 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31,
+ 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56, 0x65,
+ 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74,
+ 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3b, 0x30, 0x39,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x32, 0x54, 0x65, 0x72, 0x6d, 0x73,
+ 0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x65, 0x20, 0x61, 0x74, 0x20, 0x68,
+ 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76,
+ 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x72, 0x70, 0x61, 0x20, 0x28, 0x63, 0x29, 0x30, 0x31, 0x10, 0x30, 0x0e,
+ 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x53, 0x31, 0x13, 0x30, 0x11,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x47, 0x31, 0x13, 0x30, 0x11,
+ 0x06, 0x0b, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x3c, 0x02, 0x01,
+ 0x03, 0x13, 0x02, 0x55, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x14, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0f, 0x13, 0x14, 0x50,
+ 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x20, 0x4f, 0x72, 0x67, 0x61, 0x6e,
+ 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x12, 0x31, 0x21, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x44, 0x6f, 0x6d, 0x61,
+ 0x69, 0x6e, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x20, 0x56,
+ 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x31, 0x14, 0x31, 0x31,
+ 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x28, 0x53, 0x65, 0x65,
+ 0x20, 0x77, 0x77, 0x77, 0x2e, 0x72, 0x3a, 0x2f, 0x2f, 0x73, 0x65, 0x63,
+ 0x75, 0x72, 0x65, 0x2e, 0x67, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53,
+ 0x69, 0x67, 0x6e, 0x31, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x41,
+ 0x2e, 0x63, 0x72, 0x6c, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e,
+ 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x45, 0x63, 0x72,
+ 0x6c, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x73, 0x64, 0x31, 0x1a,
+ 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x45, 0x56, 0x49, 0x6e, 0x74, 0x6c, 0x2d, 0x63, 0x63, 0x72,
+ 0x74, 0x2e, 0x67, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x69, 0x63, 0x65, 0x72,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x31, 0x6f, 0x63, 0x73, 0x70, 0x2e,
+ 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x30, 0x39, 0x72, 0x61, 0x70, 0x69, 0x64, 0x73, 0x73, 0x6c, 0x2e, 0x63,
+ 0x6f, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
+ 0x79, 0x2f, 0x30, 0x81, 0x80, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x01, 0x01, 0x04, 0x74, 0x30, 0x72, 0x30, 0x24, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6f, 0x64,
+ 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x4a, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x3e, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66,
+ 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64,
+ 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73,
+ 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x67, 0x64, 0x5f, 0x69, 0x6e, 0x74,
+ 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x2e, 0x63, 0x72,
+ 0x74, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0xfd, 0xac, 0x61, 0x32, 0x93, 0x6c, 0x45, 0xd6, 0xe2, 0xee,
+ 0x85, 0x5f, 0x9a, 0xba, 0xe7, 0x76, 0x99, 0x68, 0xcc, 0xe7, 0x30, 0x27,
+ 0x86, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x86, 0x30,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x73,
+};
+
+// CertEntry represents a certificate in compressed form. Each entry is one of
+// the three types enumerated in |Type|.
+struct CertEntry {
+ public:
+ enum Type {
+ // Type 0 is reserved to mean "end of list" in the wire format.
+
+ // COMPRESSED means that the certificate is included in the trailing zlib
+ // data.
+ COMPRESSED = 1,
+ // CACHED means that the certificate is already known to the peer and will
+ // be replaced by its 64-bit hash (in |hash|).
+ CACHED = 2,
+ };
+
+ Type type;
+ uint64_t hash;
+ uint64_t set_hash;
+ uint32_t index;
+};
+
+// MatchCerts returns a vector of CertEntries describing how to most
+// efficiently represent |certs| to a peer who has cached the certificates
+// with the 64-bit, FNV-1a hashes in |client_cached_cert_hashes|.
+std::vector<CertEntry> MatchCerts(const std::vector<std::string>& certs,
+ absl::string_view client_cached_cert_hashes) {
+ std::vector<CertEntry> entries;
+ entries.reserve(certs.size());
+
+ const bool cached_valid =
+ client_cached_cert_hashes.size() % sizeof(uint64_t) == 0 &&
+ !client_cached_cert_hashes.empty();
+
+ for (auto i = certs.begin(); i != certs.end(); ++i) {
+ CertEntry entry;
+
+ if (cached_valid) {
+ bool cached = false;
+
+ uint64_t hash = QuicUtils::FNV1a_64_Hash(*i);
+ // This assumes that the machine is little-endian.
+ for (size_t j = 0; j < client_cached_cert_hashes.size();
+ j += sizeof(uint64_t)) {
+ uint64_t cached_hash;
+ memcpy(&cached_hash, client_cached_cert_hashes.data() + j,
+ sizeof(uint64_t));
+ if (hash != cached_hash) {
+ continue;
+ }
+
+ entry.type = CertEntry::CACHED;
+ entry.hash = hash;
+ entries.push_back(entry);
+ cached = true;
+ break;
+ }
+
+ if (cached) {
+ continue;
+ }
+ }
+
+ entry.type = CertEntry::COMPRESSED;
+ entries.push_back(entry);
+ }
+
+ return entries;
+}
+
+// CertEntriesSize returns the size, in bytes, of the serialised form of
+// |entries|.
+size_t CertEntriesSize(const std::vector<CertEntry>& entries) {
+ size_t entries_size = 0;
+
+ for (auto i = entries.begin(); i != entries.end(); ++i) {
+ entries_size++;
+ switch (i->type) {
+ case CertEntry::COMPRESSED:
+ break;
+ case CertEntry::CACHED:
+ entries_size += sizeof(uint64_t);
+ break;
+ }
+ }
+
+ entries_size++; // for end marker
+
+ return entries_size;
+}
+
+// SerializeCertEntries serialises |entries| to |out|, which must have enough
+// space to contain them.
+void SerializeCertEntries(uint8_t* out, const std::vector<CertEntry>& entries) {
+ for (auto i = entries.begin(); i != entries.end(); ++i) {
+ *out++ = static_cast<uint8_t>(i->type);
+ switch (i->type) {
+ case CertEntry::COMPRESSED:
+ break;
+ case CertEntry::CACHED:
+ memcpy(out, &i->hash, sizeof(i->hash));
+ out += sizeof(uint64_t);
+ break;
+ }
+ }
+
+ *out++ = 0; // end marker
+}
+
+// ZlibDictForEntries returns a string that contains the zlib pre-shared
+// dictionary to use in order to decompress a zlib block following |entries|.
+// |certs| is one-to-one with |entries| and contains the certificates for those
+// entries that are CACHED.
+std::string ZlibDictForEntries(const std::vector<CertEntry>& entries,
+ const std::vector<std::string>& certs) {
+ std::string zlib_dict;
+
+ // The dictionary starts with the cached certs in reverse order.
+ size_t zlib_dict_size = 0;
+ for (size_t i = certs.size() - 1; i < certs.size(); i--) {
+ if (entries[i].type != CertEntry::COMPRESSED) {
+ zlib_dict_size += certs[i].size();
+ }
+ }
+
+ // At the end of the dictionary is a block of common certificate substrings.
+ zlib_dict_size += sizeof(kCommonCertSubstrings);
+
+ zlib_dict.reserve(zlib_dict_size);
+
+ for (size_t i = certs.size() - 1; i < certs.size(); i--) {
+ if (entries[i].type != CertEntry::COMPRESSED) {
+ zlib_dict += certs[i];
+ }
+ }
+
+ zlib_dict += std::string(reinterpret_cast<const char*>(kCommonCertSubstrings),
+ sizeof(kCommonCertSubstrings));
+
+ QUICHE_DCHECK_EQ(zlib_dict.size(), zlib_dict_size);
+
+ return zlib_dict;
+}
+
+// HashCerts returns the FNV-1a hashes of |certs|.
+std::vector<uint64_t> HashCerts(const std::vector<std::string>& certs) {
+ std::vector<uint64_t> ret;
+ ret.reserve(certs.size());
+
+ for (auto i = certs.begin(); i != certs.end(); ++i) {
+ ret.push_back(QuicUtils::FNV1a_64_Hash(*i));
+ }
+
+ return ret;
+}
+
+// ParseEntries parses the serialised form of a vector of CertEntries from
+// |in_out| and writes them to |out_entries|. CACHED entries are resolved using
+// |cached_certs| and written to |out_certs|. |in_out| is updated to contain
+// the trailing data.
+bool ParseEntries(absl::string_view* in_out,
+ const std::vector<std::string>& cached_certs,
+ std::vector<CertEntry>* out_entries,
+ std::vector<std::string>* out_certs) {
+ absl::string_view in = *in_out;
+ std::vector<uint64_t> cached_hashes;
+
+ out_entries->clear();
+ out_certs->clear();
+
+ for (;;) {
+ if (in.empty()) {
+ return false;
+ }
+ CertEntry entry;
+ const uint8_t type_byte = in[0];
+ in.remove_prefix(1);
+
+ if (type_byte == 0) {
+ break;
+ }
+
+ entry.type = static_cast<CertEntry::Type>(type_byte);
+
+ switch (entry.type) {
+ case CertEntry::COMPRESSED:
+ out_certs->push_back(std::string());
+ break;
+ case CertEntry::CACHED: {
+ if (in.size() < sizeof(uint64_t)) {
+ return false;
+ }
+ memcpy(&entry.hash, in.data(), sizeof(uint64_t));
+ in.remove_prefix(sizeof(uint64_t));
+
+ if (cached_hashes.size() != cached_certs.size()) {
+ cached_hashes = HashCerts(cached_certs);
+ }
+ bool found = false;
+ for (size_t i = 0; i < cached_hashes.size(); i++) {
+ if (cached_hashes[i] == entry.hash) {
+ out_certs->push_back(cached_certs[i]);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return false;
+ }
+ break;
+ }
+
+ default:
+ return false;
+ }
+ out_entries->push_back(entry);
+ }
+
+ *in_out = in;
+ return true;
+}
+
+// ScopedZLib deals with the automatic destruction of a zlib context.
+class ScopedZLib {
+ public:
+ enum Type {
+ INFLATE,
+ DEFLATE,
+ };
+
+ explicit ScopedZLib(Type type) : z_(nullptr), type_(type) {}
+
+ void reset(z_stream* z) {
+ Clear();
+ z_ = z;
+ }
+
+ ~ScopedZLib() { Clear(); }
+
+ private:
+ void Clear() {
+ if (!z_) {
+ return;
+ }
+
+ if (type_ == DEFLATE) {
+ deflateEnd(z_);
+ } else {
+ inflateEnd(z_);
+ }
+ z_ = nullptr;
+ }
+
+ z_stream* z_;
+ const Type type_;
+};
+
+} // anonymous namespace
+
+// static
+std::string CertCompressor::CompressChain(
+ const std::vector<std::string>& certs,
+ absl::string_view client_cached_cert_hashes) {
+ const std::vector<CertEntry> entries =
+ MatchCerts(certs, client_cached_cert_hashes);
+ QUICHE_DCHECK_EQ(entries.size(), certs.size());
+
+ size_t uncompressed_size = 0;
+ for (size_t i = 0; i < entries.size(); i++) {
+ if (entries[i].type == CertEntry::COMPRESSED) {
+ uncompressed_size += 4 /* uint32_t length */ + certs[i].size();
+ }
+ }
+
+ size_t compressed_size = 0;
+ z_stream z;
+ ScopedZLib scoped_z(ScopedZLib::DEFLATE);
+
+ if (uncompressed_size > 0) {
+ memset(&z, 0, sizeof(z));
+ int rv = deflateInit(&z, Z_DEFAULT_COMPRESSION);
+ QUICHE_DCHECK_EQ(Z_OK, rv);
+ if (rv != Z_OK) {
+ return "";
+ }
+ scoped_z.reset(&z);
+
+ std::string zlib_dict = ZlibDictForEntries(entries, certs);
+
+ rv = deflateSetDictionary(
+ &z, reinterpret_cast<const uint8_t*>(&zlib_dict[0]), zlib_dict.size());
+ QUICHE_DCHECK_EQ(Z_OK, rv);
+ if (rv != Z_OK) {
+ return "";
+ }
+
+ compressed_size = deflateBound(&z, uncompressed_size);
+ }
+
+ const size_t entries_size = CertEntriesSize(entries);
+
+ std::string result;
+ result.resize(entries_size + (uncompressed_size > 0 ? 4 : 0) +
+ compressed_size);
+
+ uint8_t* j = reinterpret_cast<uint8_t*>(&result[0]);
+ SerializeCertEntries(j, entries);
+ j += entries_size;
+
+ if (uncompressed_size == 0) {
+ return result;
+ }
+
+ uint32_t uncompressed_size_32 = uncompressed_size;
+ memcpy(j, &uncompressed_size_32, sizeof(uint32_t));
+ j += sizeof(uint32_t);
+
+ int rv;
+
+ z.next_out = j;
+ z.avail_out = compressed_size;
+
+ for (size_t i = 0; i < certs.size(); i++) {
+ if (entries[i].type != CertEntry::COMPRESSED) {
+ continue;
+ }
+
+ uint32_t length32 = certs[i].size();
+ z.next_in = reinterpret_cast<uint8_t*>(&length32);
+ z.avail_in = sizeof(length32);
+ rv = deflate(&z, Z_NO_FLUSH);
+ QUICHE_DCHECK_EQ(Z_OK, rv);
+ QUICHE_DCHECK_EQ(0u, z.avail_in);
+ if (rv != Z_OK || z.avail_in) {
+ return "";
+ }
+
+ z.next_in =
+ const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(certs[i].data()));
+ z.avail_in = certs[i].size();
+ rv = deflate(&z, Z_NO_FLUSH);
+ QUICHE_DCHECK_EQ(Z_OK, rv);
+ QUICHE_DCHECK_EQ(0u, z.avail_in);
+ if (rv != Z_OK || z.avail_in) {
+ return "";
+ }
+ }
+
+ z.avail_in = 0;
+ rv = deflate(&z, Z_FINISH);
+ QUICHE_DCHECK_EQ(Z_STREAM_END, rv);
+ if (rv != Z_STREAM_END) {
+ return "";
+ }
+
+ result.resize(result.size() - z.avail_out);
+ return result;
+}
+
+// static
+bool CertCompressor::DecompressChain(
+ absl::string_view in, const std::vector<std::string>& cached_certs,
+ std::vector<std::string>* out_certs) {
+ std::vector<CertEntry> entries;
+ if (!ParseEntries(&in, cached_certs, &entries, out_certs)) {
+ return false;
+ }
+ QUICHE_DCHECK_EQ(entries.size(), out_certs->size());
+
+ std::unique_ptr<uint8_t[]> uncompressed_data;
+ absl::string_view uncompressed;
+
+ if (!in.empty()) {
+ if (in.size() < sizeof(uint32_t)) {
+ return false;
+ }
+
+ uint32_t uncompressed_size;
+ memcpy(&uncompressed_size, in.data(), sizeof(uncompressed_size));
+ in.remove_prefix(sizeof(uint32_t));
+
+ if (uncompressed_size > 128 * 1024) {
+ return false;
+ }
+
+ uncompressed_data = std::make_unique<uint8_t[]>(uncompressed_size);
+ z_stream z;
+ ScopedZLib scoped_z(ScopedZLib::INFLATE);
+
+ memset(&z, 0, sizeof(z));
+ z.next_out = uncompressed_data.get();
+ z.avail_out = uncompressed_size;
+ z.next_in =
+ const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(in.data()));
+ z.avail_in = in.size();
+
+ if (Z_OK != inflateInit(&z)) {
+ return false;
+ }
+ scoped_z.reset(&z);
+
+ int rv = inflate(&z, Z_FINISH);
+ if (rv == Z_NEED_DICT) {
+ std::string zlib_dict = ZlibDictForEntries(entries, *out_certs);
+ const uint8_t* dict = reinterpret_cast<const uint8_t*>(zlib_dict.data());
+ if (Z_OK != inflateSetDictionary(&z, dict, zlib_dict.size())) {
+ return false;
+ }
+ rv = inflate(&z, Z_FINISH);
+ }
+
+ if (Z_STREAM_END != rv || z.avail_out > 0 || z.avail_in > 0) {
+ return false;
+ }
+
+ uncompressed = absl::string_view(
+ reinterpret_cast<char*>(uncompressed_data.get()), uncompressed_size);
+ }
+
+ for (size_t i = 0; i < entries.size(); i++) {
+ switch (entries[i].type) {
+ case CertEntry::COMPRESSED:
+ if (uncompressed.size() < sizeof(uint32_t)) {
+ return false;
+ }
+ uint32_t cert_len;
+ memcpy(&cert_len, uncompressed.data(), sizeof(cert_len));
+ uncompressed.remove_prefix(sizeof(uint32_t));
+ if (uncompressed.size() < cert_len) {
+ return false;
+ }
+ (*out_certs)[i] = std::string(uncompressed.substr(0, cert_len));
+ uncompressed.remove_prefix(cert_len);
+ break;
+ case CertEntry::CACHED:
+ break;
+ }
+ }
+
+ if (!uncompressed.empty()) {
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/cert_compressor.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/cert_compressor.h
new file mode 100644
index 00000000000..9509ccd0f5d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/cert_compressor.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2013 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CERT_COMPRESSOR_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CERT_COMPRESSOR_H_
+
+#include <string>
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/crypto/crypto_protocol.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// CertCompressor provides functions for compressing and decompressing
+// certificate chains using two techniquies:
+// 1) The peer may provide a list of a 64-bit, FNV-1a hashes of certificates
+// that they already have. In the event that one of them is to be
+// compressed, it can be replaced with just the hash.
+// 2) Otherwise the certificates are compressed with zlib using a pre-shared
+// dictionary that consists of the certificates handled with the above
+// methods and a small chunk of common substrings.
+class QUIC_EXPORT_PRIVATE CertCompressor {
+ public:
+ CertCompressor() = delete;
+
+ // CompressChain compresses the certificates in |certs| and returns a
+ // compressed representation. client_cached_cert_hashes| contains
+ // 64-bit, FNV-1a hashes of certificates that the peer already possesses.
+ static std::string CompressChain(const std::vector<std::string>& certs,
+ absl::string_view client_cached_cert_hashes);
+
+ // DecompressChain decompresses the result of |CompressChain|, given in |in|,
+ // into a series of certificates that are written to |out_certs|.
+ // |cached_certs| contains certificates that the peer may have omitted.
+ static bool DecompressChain(absl::string_view in,
+ const std::vector<std::string>& cached_certs,
+ std::vector<std::string>* out_certs);
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CERT_COMPRESSOR_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/cert_compressor_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/cert_compressor_test.cc
new file mode 100644
index 00000000000..d98f4c770f5
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/cert_compressor_test.cc
@@ -0,0 +1,119 @@
+// Copyright (c) 2013 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 "quiche/quic/core/crypto/cert_compressor.h"
+
+#include <memory>
+#include <string>
+
+#include "absl/strings/escaping.h"
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/crypto_test_utils.h"
+
+namespace quic {
+namespace test {
+
+class CertCompressorTest : public QuicTest {};
+
+TEST_F(CertCompressorTest, EmptyChain) {
+ std::vector<std::string> chain;
+ const std::string compressed =
+ CertCompressor::CompressChain(chain, absl::string_view());
+ EXPECT_EQ("00", absl::BytesToHexString(compressed));
+
+ std::vector<std::string> chain2, cached_certs;
+ ASSERT_TRUE(
+ CertCompressor::DecompressChain(compressed, cached_certs, &chain2));
+ EXPECT_EQ(chain.size(), chain2.size());
+}
+
+TEST_F(CertCompressorTest, Compressed) {
+ std::vector<std::string> chain;
+ chain.push_back("testcert");
+ const std::string compressed =
+ CertCompressor::CompressChain(chain, absl::string_view());
+ ASSERT_GE(compressed.size(), 2u);
+ EXPECT_EQ("0100", absl::BytesToHexString(compressed.substr(0, 2)));
+
+ std::vector<std::string> chain2, cached_certs;
+ ASSERT_TRUE(
+ CertCompressor::DecompressChain(compressed, cached_certs, &chain2));
+ EXPECT_EQ(chain.size(), chain2.size());
+ EXPECT_EQ(chain[0], chain2[0]);
+}
+
+TEST_F(CertCompressorTest, Common) {
+ std::vector<std::string> chain;
+ chain.push_back("testcert");
+ static const uint64_t set_hash = 42;
+ const std::string compressed = CertCompressor::CompressChain(
+ chain, absl::string_view(reinterpret_cast<const char*>(&set_hash),
+ sizeof(set_hash)));
+ ASSERT_GE(compressed.size(), 2u);
+ // 01 is the prefix for a zlib "compressed" cert not common or cached.
+ EXPECT_EQ("0100", absl::BytesToHexString(compressed.substr(0, 2)));
+
+ std::vector<std::string> chain2, cached_certs;
+ ASSERT_TRUE(
+ CertCompressor::DecompressChain(compressed, cached_certs, &chain2));
+ EXPECT_EQ(chain.size(), chain2.size());
+ EXPECT_EQ(chain[0], chain2[0]);
+}
+
+TEST_F(CertCompressorTest, Cached) {
+ std::vector<std::string> chain;
+ chain.push_back("testcert");
+ uint64_t hash = QuicUtils::FNV1a_64_Hash(chain[0]);
+ absl::string_view hash_bytes(reinterpret_cast<char*>(&hash), sizeof(hash));
+ const std::string compressed =
+ CertCompressor::CompressChain(chain, hash_bytes);
+
+ EXPECT_EQ("02" /* cached */ + absl::BytesToHexString(hash_bytes) +
+ "00" /* end of list */,
+ absl::BytesToHexString(compressed));
+
+ std::vector<std::string> cached_certs, chain2;
+ cached_certs.push_back(chain[0]);
+ ASSERT_TRUE(
+ CertCompressor::DecompressChain(compressed, cached_certs, &chain2));
+ EXPECT_EQ(chain.size(), chain2.size());
+ EXPECT_EQ(chain[0], chain2[0]);
+}
+
+TEST_F(CertCompressorTest, BadInputs) {
+ std::vector<std::string> cached_certs, chain;
+
+ EXPECT_FALSE(CertCompressor::DecompressChain(
+ absl::BytesToHexString("04") /* bad entry type */, cached_certs, &chain));
+
+ EXPECT_FALSE(CertCompressor::DecompressChain(
+ absl::BytesToHexString("01") /* no terminator */, cached_certs, &chain));
+
+ EXPECT_FALSE(CertCompressor::DecompressChain(
+ absl::BytesToHexString("0200") /* hash truncated */, cached_certs,
+ &chain));
+
+ EXPECT_FALSE(CertCompressor::DecompressChain(
+ absl::BytesToHexString("0300") /* hash and index truncated */,
+ cached_certs, &chain));
+
+ /* without a CommonCertSets */
+ EXPECT_FALSE(
+ CertCompressor::DecompressChain(absl::BytesToHexString("03"
+ "0000000000000000"
+ "00000000"),
+ cached_certs, &chain));
+
+ /* incorrect hash and index */
+ EXPECT_FALSE(
+ CertCompressor::DecompressChain(absl::BytesToHexString("03"
+ "a200000000000000"
+ "00000000"),
+ cached_certs, &chain));
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_util.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_util.cc
new file mode 100644
index 00000000000..1f2ce870eb7
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_util.cc
@@ -0,0 +1,280 @@
+// Copyright 2021 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 "quiche/quic/core/crypto/certificate_util.h"
+
+#include "absl/strings/str_format.h"
+#include "absl/strings/str_split.h"
+#include "absl/strings/string_view.h"
+#include "openssl/bn.h"
+#include "openssl/bytestring.h"
+#include "openssl/digest.h"
+#include "openssl/ec_key.h"
+#include "openssl/mem.h"
+#include "openssl/pkcs7.h"
+#include "openssl/pool.h"
+#include "openssl/rsa.h"
+#include "openssl/stack.h"
+#include "quiche/quic/core/crypto/boring_utils.h"
+#include "quiche/quic/platform/api/quic_logging.h"
+
+namespace quic {
+namespace {
+bool AddEcdsa256SignatureAlgorithm(CBB* cbb) {
+ // See RFC 5758. This is the encoding of OID 1.2.840.10045.4.3.2.
+ static const uint8_t kEcdsaWithSha256[] = {0x2a, 0x86, 0x48, 0xce,
+ 0x3d, 0x04, 0x03, 0x02};
+
+ // An AlgorithmIdentifier is described in RFC 5280, 4.1.1.2.
+ CBB sequence, oid;
+ if (!CBB_add_asn1(cbb, &sequence, CBS_ASN1_SEQUENCE) ||
+ !CBB_add_asn1(&sequence, &oid, CBS_ASN1_OBJECT)) {
+ return false;
+ }
+
+ if (!CBB_add_bytes(&oid, kEcdsaWithSha256, sizeof(kEcdsaWithSha256))) {
+ return false;
+ }
+
+ // RFC 5758, section 3.2: ecdsa-with-sha256 MUST omit the parameters field.
+ return CBB_flush(cbb);
+}
+
+// Adds an X.509 Name with the specified distinguished name to |cbb|.
+bool AddName(CBB* cbb, absl::string_view name) {
+ // See RFC 4519.
+ static const uint8_t kCommonName[] = {0x55, 0x04, 0x03};
+ static const uint8_t kCountryName[] = {0x55, 0x04, 0x06};
+ static const uint8_t kOrganizationName[] = {0x55, 0x04, 0x0a};
+ static const uint8_t kOrganizationalUnitName[] = {0x55, 0x04, 0x0b};
+
+ std::vector<std::string> attributes =
+ absl::StrSplit(name, ',', absl::SkipEmpty());
+
+ if (attributes.empty()) {
+ QUIC_LOG(ERROR) << "Missing DN or wrong format";
+ return false;
+ }
+
+ // See RFC 5280, section 4.1.2.4.
+ CBB rdns;
+ if (!CBB_add_asn1(cbb, &rdns, CBS_ASN1_SEQUENCE)) {
+ return false;
+ }
+
+ for (const std::string& attribute : attributes) {
+ std::vector<std::string> parts =
+ absl::StrSplit(absl::StripAsciiWhitespace(attribute), '=');
+ if (parts.size() != 2) {
+ QUIC_LOG(ERROR) << "Wrong DN format at " + attribute;
+ return false;
+ }
+
+ const std::string& type_string = parts[0];
+ const std::string& value_string = parts[1];
+ absl::Span<const uint8_t> type_bytes;
+ if (type_string == "CN") {
+ type_bytes = kCommonName;
+ } else if (type_string == "C") {
+ type_bytes = kCountryName;
+ } else if (type_string == "O") {
+ type_bytes = kOrganizationName;
+ } else if (type_string == "OU") {
+ type_bytes = kOrganizationalUnitName;
+ } else {
+ QUIC_LOG(ERROR) << "Unrecognized type " + type_string;
+ return false;
+ }
+
+ CBB rdn, attr, type, value;
+ if (!CBB_add_asn1(&rdns, &rdn, CBS_ASN1_SET) ||
+ !CBB_add_asn1(&rdn, &attr, CBS_ASN1_SEQUENCE) ||
+ !CBB_add_asn1(&attr, &type, CBS_ASN1_OBJECT) ||
+ !CBB_add_bytes(&type, type_bytes.data(), type_bytes.size()) ||
+ !CBB_add_asn1(&attr, &value,
+ type_string == "C" ? CBS_ASN1_PRINTABLESTRING
+ : CBS_ASN1_UTF8STRING) ||
+ !AddStringToCbb(&value, value_string) || !CBB_flush(&rdns)) {
+ return false;
+ }
+ }
+ if (!CBB_flush(cbb)) {
+ return false;
+ }
+ return true;
+}
+
+bool CBBAddTime(CBB* cbb, const CertificateTimestamp& timestamp) {
+ CBB child;
+ std::string formatted_time;
+
+ // Per RFC 5280, 4.1.2.5, times which fit in UTCTime must be encoded as
+ // UTCTime rather than GeneralizedTime.
+ const bool is_utc_time = (1950 <= timestamp.year && timestamp.year < 2050);
+ if (is_utc_time) {
+ uint16_t year = timestamp.year - 1900;
+ if (year >= 100) {
+ year -= 100;
+ }
+ formatted_time = absl::StrFormat("%02d", year);
+ if (!CBB_add_asn1(cbb, &child, CBS_ASN1_UTCTIME)) {
+ return false;
+ }
+ } else {
+ formatted_time = absl::StrFormat("%04d", timestamp.year);
+ if (!CBB_add_asn1(cbb, &child, CBS_ASN1_GENERALIZEDTIME)) {
+ return false;
+ }
+ }
+
+ absl::StrAppendFormat(&formatted_time, "%02d%02d%02d%02d%02dZ",
+ timestamp.month, timestamp.day, timestamp.hour,
+ timestamp.minute, timestamp.second);
+
+ static const size_t kGeneralizedTimeLength = 15;
+ static const size_t kUTCTimeLength = 13;
+ QUICHE_DCHECK_EQ(formatted_time.size(),
+ is_utc_time ? kUTCTimeLength : kGeneralizedTimeLength);
+
+ return AddStringToCbb(&child, formatted_time) && CBB_flush(cbb);
+}
+
+bool CBBAddExtension(CBB* extensions, absl::Span<const uint8_t> oid,
+ bool critical, absl::Span<const uint8_t> contents) {
+ CBB extension, cbb_oid, cbb_contents;
+ if (!CBB_add_asn1(extensions, &extension, CBS_ASN1_SEQUENCE) ||
+ !CBB_add_asn1(&extension, &cbb_oid, CBS_ASN1_OBJECT) ||
+ !CBB_add_bytes(&cbb_oid, oid.data(), oid.size()) ||
+ (critical && !CBB_add_asn1_bool(&extension, 1)) ||
+ !CBB_add_asn1(&extension, &cbb_contents, CBS_ASN1_OCTETSTRING) ||
+ !CBB_add_bytes(&cbb_contents, contents.data(), contents.size()) ||
+ !CBB_flush(extensions)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool IsEcdsa256Key(const EVP_PKEY& evp_key) {
+ if (EVP_PKEY_id(&evp_key) != EVP_PKEY_EC) {
+ return false;
+ }
+ const EC_KEY* key = EVP_PKEY_get0_EC_KEY(&evp_key);
+ if (key == nullptr) {
+ return false;
+ }
+ const EC_GROUP* group = EC_KEY_get0_group(key);
+ if (group == nullptr) {
+ return false;
+ }
+ return EC_GROUP_get_curve_name(group) == NID_X9_62_prime256v1;
+}
+
+} // namespace
+
+bssl::UniquePtr<EVP_PKEY> MakeKeyPairForSelfSignedCertificate() {
+ bssl::UniquePtr<EVP_PKEY_CTX> context(
+ EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr));
+ if (!context) {
+ return nullptr;
+ }
+ if (EVP_PKEY_keygen_init(context.get()) != 1) {
+ return nullptr;
+ }
+ if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(context.get(),
+ NID_X9_62_prime256v1) != 1) {
+ return nullptr;
+ }
+ EVP_PKEY* raw_key = nullptr;
+ if (EVP_PKEY_keygen(context.get(), &raw_key) != 1) {
+ return nullptr;
+ }
+ return bssl::UniquePtr<EVP_PKEY>(raw_key);
+}
+
+std::string CreateSelfSignedCertificate(EVP_PKEY& key,
+ const CertificateOptions& options) {
+ std::string error;
+ if (!IsEcdsa256Key(key)) {
+ QUIC_LOG(ERROR) << "CreateSelfSignedCert only accepts ECDSA P-256 keys";
+ return error;
+ }
+
+ // See RFC 5280, section 4.1. First, construct the TBSCertificate.
+ bssl::ScopedCBB cbb;
+ CBB tbs_cert, version, validity;
+ uint8_t* tbs_cert_bytes;
+ size_t tbs_cert_len;
+
+ if (!CBB_init(cbb.get(), 64) ||
+ !CBB_add_asn1(cbb.get(), &tbs_cert, CBS_ASN1_SEQUENCE) ||
+ !CBB_add_asn1(&tbs_cert, &version,
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
+ !CBB_add_asn1_uint64(&version, 2) || // X.509 version 3
+ !CBB_add_asn1_uint64(&tbs_cert, options.serial_number) ||
+ !AddEcdsa256SignatureAlgorithm(&tbs_cert) || // signature algorithm
+ !AddName(&tbs_cert, options.subject) || // issuer
+ !CBB_add_asn1(&tbs_cert, &validity, CBS_ASN1_SEQUENCE) ||
+ !CBBAddTime(&validity, options.validity_start) ||
+ !CBBAddTime(&validity, options.validity_end) ||
+ !AddName(&tbs_cert, options.subject) || // subject
+ !EVP_marshal_public_key(&tbs_cert, &key)) { // subjectPublicKeyInfo
+ return error;
+ }
+
+ CBB outer_extensions, extensions;
+ if (!CBB_add_asn1(&tbs_cert, &outer_extensions,
+ 3 | CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED) ||
+ !CBB_add_asn1(&outer_extensions, &extensions, CBS_ASN1_SEQUENCE)) {
+ return error;
+ }
+
+ // Key Usage
+ constexpr uint8_t kKeyUsageOid[] = {0x55, 0x1d, 0x0f};
+ constexpr uint8_t kKeyUsageContent[] = {
+ 0x3, // BIT STRING
+ 0x2, // Length
+ 0x0, // Unused bits
+ 0x80, // bit(0): digitalSignature
+ };
+ CBBAddExtension(&extensions, kKeyUsageOid, true, kKeyUsageContent);
+
+ // TODO(wub): Add more extensions here if needed.
+
+ if (!CBB_finish(cbb.get(), &tbs_cert_bytes, &tbs_cert_len)) {
+ return error;
+ }
+
+ bssl::UniquePtr<uint8_t> delete_tbs_cert_bytes(tbs_cert_bytes);
+
+ // Sign the TBSCertificate and write the entire certificate.
+ CBB cert, signature;
+ bssl::ScopedEVP_MD_CTX ctx;
+ uint8_t* sig_out;
+ size_t sig_len;
+ uint8_t* cert_bytes;
+ size_t cert_len;
+ if (!CBB_init(cbb.get(), tbs_cert_len) ||
+ !CBB_add_asn1(cbb.get(), &cert, CBS_ASN1_SEQUENCE) ||
+ !CBB_add_bytes(&cert, tbs_cert_bytes, tbs_cert_len) ||
+ !AddEcdsa256SignatureAlgorithm(&cert) ||
+ !CBB_add_asn1(&cert, &signature, CBS_ASN1_BITSTRING) ||
+ !CBB_add_u8(&signature, 0 /* no unused bits */) ||
+ !EVP_DigestSignInit(ctx.get(), nullptr, EVP_sha256(), nullptr, &key) ||
+ // Compute the maximum signature length.
+ !EVP_DigestSign(ctx.get(), nullptr, &sig_len, tbs_cert_bytes,
+ tbs_cert_len) ||
+ !CBB_reserve(&signature, &sig_out, sig_len) ||
+ // Actually sign the TBSCertificate.
+ !EVP_DigestSign(ctx.get(), sig_out, &sig_len, tbs_cert_bytes,
+ tbs_cert_len) ||
+ !CBB_did_write(&signature, sig_len) ||
+ !CBB_finish(cbb.get(), &cert_bytes, &cert_len)) {
+ return error;
+ }
+ bssl::UniquePtr<uint8_t> delete_cert_bytes(cert_bytes);
+ return std::string(reinterpret_cast<char*>(cert_bytes), cert_len);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_util.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_util.h
new file mode 100644
index 00000000000..35bb5611e06
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_util.h
@@ -0,0 +1,46 @@
+// Copyright 2021 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CERTIFICATE_UTIL_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CERTIFICATE_UTIL_H_
+
+#include <string>
+
+#include "absl/strings/string_view.h"
+#include "openssl/evp.h"
+#include "quiche/quic/core/quic_time.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+struct QUIC_NO_EXPORT CertificateTimestamp {
+ uint16_t year;
+ uint8_t month;
+ uint8_t day;
+ uint8_t hour;
+ uint8_t minute;
+ uint8_t second;
+};
+
+struct QUIC_NO_EXPORT CertificateOptions {
+ absl::string_view subject;
+ uint64_t serial_number;
+ CertificateTimestamp validity_start; // a.k.a not_valid_before
+ CertificateTimestamp validity_end; // a.k.a not_valid_after
+};
+
+// Creates a ECDSA P-256 key pair.
+QUIC_EXPORT_PRIVATE bssl::UniquePtr<EVP_PKEY>
+MakeKeyPairForSelfSignedCertificate();
+
+// Creates a self-signed, DER-encoded X.509 certificate.
+// |key| must be a ECDSA P-256 key.
+// This is mostly stolen from Chromium's net/cert/x509_util.h, with
+// modifications to make it work in QUICHE.
+QUIC_EXPORT_PRIVATE std::string CreateSelfSignedCertificate(
+ EVP_PKEY& key, const CertificateOptions& options);
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CERTIFICATE_UTIL_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_util_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_util_test.cc
new file mode 100644
index 00000000000..4c98d7cab81
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_util_test.cc
@@ -0,0 +1,49 @@
+// Copyright 2021 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 "quiche/quic/core/crypto/certificate_util.h"
+
+#include "openssl/ssl.h"
+#include "quiche/quic/core/crypto/certificate_view.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/platform/api/quic_test_output.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+TEST(CertificateUtilTest, CreateSelfSignedCertificate) {
+ bssl::UniquePtr<EVP_PKEY> key = MakeKeyPairForSelfSignedCertificate();
+ ASSERT_NE(key, nullptr);
+
+ CertificatePrivateKey cert_key(std::move(key));
+
+ CertificateOptions options;
+ options.subject = "CN=subject";
+ options.serial_number = 0x12345678;
+ options.validity_start = {2020, 1, 1, 0, 0, 0};
+ options.validity_end = {2049, 12, 31, 0, 0, 0};
+ std::string der_cert =
+ CreateSelfSignedCertificate(*cert_key.private_key(), options);
+ ASSERT_FALSE(der_cert.empty());
+
+ QuicSaveTestOutput("CertificateUtilTest_CreateSelfSignedCert.crt", der_cert);
+
+ std::unique_ptr<CertificateView> cert_view =
+ CertificateView::ParseSingleCertificate(der_cert);
+ ASSERT_NE(cert_view, nullptr);
+ EXPECT_EQ(cert_view->public_key_type(), PublicKeyType::kP256);
+
+ absl::optional<std::string> subject = cert_view->GetHumanReadableSubject();
+ ASSERT_TRUE(subject.has_value());
+ EXPECT_EQ(*subject, options.subject);
+
+ EXPECT_TRUE(
+ cert_key.ValidForSignatureAlgorithm(SSL_SIGN_ECDSA_SECP256R1_SHA256));
+ EXPECT_TRUE(cert_key.MatchesPublicKey(*cert_view));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_view.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_view.cc
new file mode 100644
index 00000000000..1c4d3a2ca37
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_view.cc
@@ -0,0 +1,649 @@
+// 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 "quiche/quic/core/crypto/certificate_view.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "absl/strings/escaping.h"
+#include "absl/strings/match.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/str_join.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
+#include "openssl/base.h"
+#include "openssl/bytestring.h"
+#include "openssl/digest.h"
+#include "openssl/ec.h"
+#include "openssl/ec_key.h"
+#include "openssl/evp.h"
+#include "openssl/nid.h"
+#include "openssl/rsa.h"
+#include "openssl/ssl.h"
+#include "quiche/quic/core/crypto/boring_utils.h"
+#include "quiche/quic/core/quic_time.h"
+#include "quiche/quic/core/quic_types.h"
+#include "quiche/quic/platform/api/quic_bug_tracker.h"
+#include "quiche/quic/platform/api/quic_ip_address.h"
+#include "quiche/quic/platform/api/quic_logging.h"
+#include "quiche/common/platform/api/quiche_time_utils.h"
+#include "quiche/common/quiche_data_reader.h"
+#include "quiche/common/quiche_text_utils.h"
+
+namespace quic {
+namespace {
+
+using ::quiche::QuicheTextUtils;
+
+// The literals below were encoded using `ascii2der | xxd -i`. The comments
+// above the literals are the contents in the der2ascii syntax.
+
+// X.509 version 3 (version numbering starts with zero).
+// INTEGER { 2 }
+constexpr uint8_t kX509Version[] = {0x02, 0x01, 0x02};
+
+// 2.5.29.17
+constexpr uint8_t kSubjectAltNameOid[] = {0x55, 0x1d, 0x11};
+
+PublicKeyType PublicKeyTypeFromKey(EVP_PKEY* public_key) {
+ switch (EVP_PKEY_id(public_key)) {
+ case EVP_PKEY_RSA:
+ return PublicKeyType::kRsa;
+ case EVP_PKEY_EC: {
+ const EC_KEY* key = EVP_PKEY_get0_EC_KEY(public_key);
+ if (key == nullptr) {
+ return PublicKeyType::kUnknown;
+ }
+ const EC_GROUP* group = EC_KEY_get0_group(key);
+ if (group == nullptr) {
+ return PublicKeyType::kUnknown;
+ }
+ const int curve_nid = EC_GROUP_get_curve_name(group);
+ switch (curve_nid) {
+ case NID_X9_62_prime256v1:
+ return PublicKeyType::kP256;
+ case NID_secp384r1:
+ return PublicKeyType::kP384;
+ default:
+ return PublicKeyType::kUnknown;
+ }
+ }
+ case EVP_PKEY_ED25519:
+ return PublicKeyType::kEd25519;
+ default:
+ return PublicKeyType::kUnknown;
+ }
+}
+
+PublicKeyType PublicKeyTypeFromSignatureAlgorithm(
+ uint16_t signature_algorithm) {
+ switch (signature_algorithm) {
+ case SSL_SIGN_RSA_PSS_RSAE_SHA256:
+ return PublicKeyType::kRsa;
+ case SSL_SIGN_ECDSA_SECP256R1_SHA256:
+ return PublicKeyType::kP256;
+ case SSL_SIGN_ECDSA_SECP384R1_SHA384:
+ return PublicKeyType::kP384;
+ case SSL_SIGN_ED25519:
+ return PublicKeyType::kEd25519;
+ default:
+ return PublicKeyType::kUnknown;
+ }
+}
+
+std::string AttributeNameToString(const CBS& oid_cbs) {
+ absl::string_view oid = CbsToStringPiece(oid_cbs);
+
+ // We only handle OIDs of form 2.5.4.N, which have binary encoding of
+ // "55 04 0N".
+ if (oid.length() == 3 && absl::StartsWith(oid, "\x55\x04")) {
+ // clang-format off
+ switch (oid[2]) {
+ case '\x3': return "CN";
+ case '\x7': return "L";
+ case '\x8': return "ST";
+ case '\xa': return "O";
+ case '\xb': return "OU";
+ case '\x6': return "C";
+ }
+ // clang-format on
+ }
+
+ bssl::UniquePtr<char> oid_representation(CBS_asn1_oid_to_text(&oid_cbs));
+ if (oid_representation == nullptr) {
+ return absl::StrCat("(", absl::BytesToHexString(oid), ")");
+ }
+ return std::string(oid_representation.get());
+}
+
+} // namespace
+
+absl::optional<std::string> X509NameAttributeToString(CBS input) {
+ CBS name, value;
+ unsigned value_tag;
+ if (!CBS_get_asn1(&input, &name, CBS_ASN1_OBJECT) ||
+ !CBS_get_any_asn1(&input, &value, &value_tag) || CBS_len(&input) != 0) {
+ return absl::nullopt;
+ }
+ // Note that this does not process encoding of |input| in any way. This works
+ // fine for the most cases.
+ return absl::StrCat(AttributeNameToString(name), "=",
+ absl::CHexEscape(CbsToStringPiece(value)));
+}
+
+namespace {
+
+template <unsigned inner_tag, char separator,
+ absl::optional<std::string> (*parser)(CBS)>
+absl::optional<std::string> ParseAndJoin(CBS input) {
+ std::vector<std::string> pieces;
+ while (CBS_len(&input) != 0) {
+ CBS attribute;
+ if (!CBS_get_asn1(&input, &attribute, inner_tag)) {
+ return absl::nullopt;
+ }
+ absl::optional<std::string> formatted = parser(attribute);
+ if (!formatted.has_value()) {
+ return absl::nullopt;
+ }
+ pieces.push_back(*formatted);
+ }
+
+ return absl::StrJoin(pieces, std::string({separator}));
+}
+
+absl::optional<std::string> RelativeDistinguishedNameToString(CBS input) {
+ return ParseAndJoin<CBS_ASN1_SEQUENCE, '+', X509NameAttributeToString>(input);
+}
+
+absl::optional<std::string> DistinguishedNameToString(CBS input) {
+ return ParseAndJoin<CBS_ASN1_SET, ',', RelativeDistinguishedNameToString>(
+ input);
+}
+
+} // namespace
+
+std::string PublicKeyTypeToString(PublicKeyType type) {
+ switch (type) {
+ case PublicKeyType::kRsa:
+ return "RSA";
+ case PublicKeyType::kP256:
+ return "ECDSA P-256";
+ case PublicKeyType::kP384:
+ return "ECDSA P-384";
+ case PublicKeyType::kEd25519:
+ return "Ed25519";
+ case PublicKeyType::kUnknown:
+ return "unknown";
+ }
+ return "";
+}
+
+absl::optional<quic::QuicWallTime> ParseDerTime(unsigned tag,
+ absl::string_view payload) {
+ if (tag != CBS_ASN1_GENERALIZEDTIME && tag != CBS_ASN1_UTCTIME) {
+ QUIC_DLOG(WARNING) << "Invalid tag supplied for a DER timestamp";
+ return absl::nullopt;
+ }
+
+ const size_t year_length = tag == CBS_ASN1_GENERALIZEDTIME ? 4 : 2;
+ uint64_t year, month, day, hour, minute, second;
+ quiche::QuicheDataReader reader(payload);
+ if (!reader.ReadDecimal64(year_length, &year) ||
+ !reader.ReadDecimal64(2, &month) || !reader.ReadDecimal64(2, &day) ||
+ !reader.ReadDecimal64(2, &hour) || !reader.ReadDecimal64(2, &minute) ||
+ !reader.ReadDecimal64(2, &second) ||
+ reader.ReadRemainingPayload() != "Z") {
+ QUIC_DLOG(WARNING) << "Failed to parse the DER timestamp";
+ return absl::nullopt;
+ }
+
+ if (tag == CBS_ASN1_UTCTIME) {
+ QUICHE_DCHECK_LE(year, 100u);
+ year += (year >= 50) ? 1900 : 2000;
+ }
+
+ const absl::optional<int64_t> unix_time =
+ quiche::QuicheUtcDateTimeToUnixSeconds(year, month, day, hour, minute,
+ second);
+ if (!unix_time.has_value() || *unix_time < 0) {
+ return absl::nullopt;
+ }
+ return QuicWallTime::FromUNIXSeconds(*unix_time);
+}
+
+PemReadResult ReadNextPemMessage(std::istream* input) {
+ constexpr absl::string_view kPemBegin = "-----BEGIN ";
+ constexpr absl::string_view kPemEnd = "-----END ";
+ constexpr absl::string_view kPemDashes = "-----";
+
+ std::string line_buffer, encoded_message_contents, expected_end;
+ bool pending_message = false;
+ PemReadResult result;
+ while (std::getline(*input, line_buffer)) {
+ absl::string_view line(line_buffer);
+ QuicheTextUtils::RemoveLeadingAndTrailingWhitespace(&line);
+
+ // Handle BEGIN lines.
+ if (!pending_message && absl::StartsWith(line, kPemBegin) &&
+ absl::EndsWith(line, kPemDashes)) {
+ result.type = std::string(
+ line.substr(kPemBegin.size(),
+ line.size() - kPemDashes.size() - kPemBegin.size()));
+ expected_end = absl::StrCat(kPemEnd, result.type, kPemDashes);
+ pending_message = true;
+ continue;
+ }
+
+ // Handle END lines.
+ if (pending_message && line == expected_end) {
+ absl::optional<std::string> data =
+ QuicheTextUtils::Base64Decode(encoded_message_contents);
+ if (data.has_value()) {
+ result.status = PemReadResult::kOk;
+ result.contents = data.value();
+ } else {
+ result.status = PemReadResult::kError;
+ }
+ return result;
+ }
+
+ if (pending_message) {
+ encoded_message_contents.append(std::string(line));
+ }
+ }
+ bool eof_reached = input->eof() && !pending_message;
+ return PemReadResult{
+ (eof_reached ? PemReadResult::kEof : PemReadResult::kError), "", ""};
+}
+
+std::unique_ptr<CertificateView> CertificateView::ParseSingleCertificate(
+ absl::string_view certificate) {
+ std::unique_ptr<CertificateView> result(new CertificateView());
+ CBS top = StringPieceToCbs(certificate);
+
+ CBS top_certificate, tbs_certificate, signature_algorithm, signature;
+ if (!CBS_get_asn1(&top, &top_certificate, CBS_ASN1_SEQUENCE) ||
+ CBS_len(&top) != 0) {
+ return nullptr;
+ }
+
+ // Certificate ::= SEQUENCE {
+ if (
+ // tbsCertificate TBSCertificate,
+ !CBS_get_asn1(&top_certificate, &tbs_certificate, CBS_ASN1_SEQUENCE) ||
+
+ // signatureAlgorithm AlgorithmIdentifier,
+ !CBS_get_asn1(&top_certificate, &signature_algorithm,
+ CBS_ASN1_SEQUENCE) ||
+
+ // signature BIT STRING }
+ !CBS_get_asn1(&top_certificate, &signature, CBS_ASN1_BITSTRING) ||
+ CBS_len(&top_certificate) != 0) {
+ return nullptr;
+ }
+
+ int has_version, has_extensions;
+ CBS version, serial, signature_algorithm_inner, issuer, validity, subject,
+ spki, issuer_id, subject_id, extensions_outer;
+ // TBSCertificate ::= SEQUENCE {
+ if (
+ // version [0] Version DEFAULT v1,
+ !CBS_get_optional_asn1(
+ &tbs_certificate, &version, &has_version,
+ CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0) ||
+
+ // serialNumber CertificateSerialNumber,
+ !CBS_get_asn1(&tbs_certificate, &serial, CBS_ASN1_INTEGER) ||
+
+ // signature AlgorithmIdentifier,
+ !CBS_get_asn1(&tbs_certificate, &signature_algorithm_inner,
+ CBS_ASN1_SEQUENCE) ||
+
+ // issuer Name,
+ !CBS_get_asn1(&tbs_certificate, &issuer, CBS_ASN1_SEQUENCE) ||
+
+ // validity Validity,
+ !CBS_get_asn1(&tbs_certificate, &validity, CBS_ASN1_SEQUENCE) ||
+
+ // subject Name,
+ !CBS_get_asn1(&tbs_certificate, &subject, CBS_ASN1_SEQUENCE) ||
+
+ // subjectPublicKeyInfo SubjectPublicKeyInfo,
+ !CBS_get_asn1_element(&tbs_certificate, &spki, CBS_ASN1_SEQUENCE) ||
+
+ // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
+ // -- If present, version MUST be v2 or v3
+ !CBS_get_optional_asn1(&tbs_certificate, &issuer_id, nullptr,
+ CBS_ASN1_CONTEXT_SPECIFIC | 1) ||
+
+ // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
+ // -- If present, version MUST be v2 or v3
+ !CBS_get_optional_asn1(&tbs_certificate, &subject_id, nullptr,
+ CBS_ASN1_CONTEXT_SPECIFIC | 2) ||
+
+ // extensions [3] Extensions OPTIONAL
+ // -- If present, version MUST be v3 -- }
+ !CBS_get_optional_asn1(
+ &tbs_certificate, &extensions_outer, &has_extensions,
+ CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 3) ||
+
+ CBS_len(&tbs_certificate) != 0) {
+ return nullptr;
+ }
+
+ result->subject_der_ = CbsToStringPiece(subject);
+
+ unsigned not_before_tag, not_after_tag;
+ CBS not_before, not_after;
+ if (!CBS_get_any_asn1(&validity, &not_before, &not_before_tag) ||
+ !CBS_get_any_asn1(&validity, &not_after, &not_after_tag) ||
+ CBS_len(&validity) != 0) {
+ QUIC_DLOG(WARNING) << "Failed to extract the validity dates";
+ return nullptr;
+ }
+ absl::optional<QuicWallTime> not_before_parsed =
+ ParseDerTime(not_before_tag, CbsToStringPiece(not_before));
+ absl::optional<QuicWallTime> not_after_parsed =
+ ParseDerTime(not_after_tag, CbsToStringPiece(not_after));
+ if (!not_before_parsed.has_value() || !not_after_parsed.has_value()) {
+ QUIC_DLOG(WARNING) << "Failed to parse validity dates";
+ return nullptr;
+ }
+ result->validity_start_ = *not_before_parsed;
+ result->validity_end_ = *not_after_parsed;
+
+ result->public_key_.reset(EVP_parse_public_key(&spki));
+ if (result->public_key_ == nullptr) {
+ QUIC_DLOG(WARNING) << "Failed to parse the public key";
+ return nullptr;
+ }
+ if (!result->ValidatePublicKeyParameters()) {
+ QUIC_DLOG(WARNING) << "Public key has invalid parameters";
+ return nullptr;
+ }
+
+ // Only support X.509v3.
+ if (!has_version ||
+ !CBS_mem_equal(&version, kX509Version, sizeof(kX509Version))) {
+ QUIC_DLOG(WARNING) << "Bad X.509 version";
+ return nullptr;
+ }
+
+ if (!has_extensions) {
+ return nullptr;
+ }
+
+ CBS extensions;
+ if (!CBS_get_asn1(&extensions_outer, &extensions, CBS_ASN1_SEQUENCE) ||
+ CBS_len(&extensions_outer) != 0) {
+ QUIC_DLOG(WARNING) << "Failed to extract the extension sequence";
+ return nullptr;
+ }
+ if (!result->ParseExtensions(extensions)) {
+ QUIC_DLOG(WARNING) << "Failed to parse extensions";
+ return nullptr;
+ }
+
+ return result;
+}
+
+bool CertificateView::ParseExtensions(CBS extensions) {
+ while (CBS_len(&extensions) != 0) {
+ CBS extension, oid, critical, payload;
+ if (
+ // Extension ::= SEQUENCE {
+ !CBS_get_asn1(&extensions, &extension, CBS_ASN1_SEQUENCE) ||
+ // extnID OBJECT IDENTIFIER,
+ !CBS_get_asn1(&extension, &oid, CBS_ASN1_OBJECT) ||
+ // critical BOOLEAN DEFAULT FALSE,
+ !CBS_get_optional_asn1(&extension, &critical, nullptr,
+ CBS_ASN1_BOOLEAN) ||
+ // extnValue OCTET STRING
+ // -- contains the DER encoding of an ASN.1 value
+ // -- corresponding to the extension type identified
+ // -- by extnID
+ !CBS_get_asn1(&extension, &payload, CBS_ASN1_OCTETSTRING) ||
+ CBS_len(&extension) != 0) {
+ QUIC_DLOG(WARNING) << "Bad extension entry";
+ return false;
+ }
+
+ if (CBS_mem_equal(&oid, kSubjectAltNameOid, sizeof(kSubjectAltNameOid))) {
+ CBS alt_names;
+ if (!CBS_get_asn1(&payload, &alt_names, CBS_ASN1_SEQUENCE) ||
+ CBS_len(&payload) != 0) {
+ QUIC_DLOG(WARNING) << "Failed to parse subjectAltName";
+ return false;
+ }
+ while (CBS_len(&alt_names) != 0) {
+ CBS alt_name_cbs;
+ unsigned int alt_name_tag;
+ if (!CBS_get_any_asn1(&alt_names, &alt_name_cbs, &alt_name_tag)) {
+ QUIC_DLOG(WARNING) << "Failed to parse subjectAltName";
+ return false;
+ }
+
+ absl::string_view alt_name = CbsToStringPiece(alt_name_cbs);
+ QuicIpAddress ip_address;
+ // GeneralName ::= CHOICE {
+ switch (alt_name_tag) {
+ // dNSName [2] IA5String,
+ case CBS_ASN1_CONTEXT_SPECIFIC | 2:
+ subject_alt_name_domains_.push_back(alt_name);
+ break;
+
+ // iPAddress [7] OCTET STRING,
+ case CBS_ASN1_CONTEXT_SPECIFIC | 7:
+ if (!ip_address.FromPackedString(alt_name.data(),
+ alt_name.size())) {
+ QUIC_DLOG(WARNING) << "Failed to parse subjectAltName IP address";
+ return false;
+ }
+ subject_alt_name_ips_.push_back(ip_address);
+ break;
+
+ default:
+ QUIC_DLOG(INFO) << "Unknown subjectAltName tag " << alt_name_tag;
+ continue;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+std::vector<std::string> CertificateView::LoadPemFromStream(
+ std::istream* input) {
+ std::vector<std::string> result;
+ for (;;) {
+ PemReadResult read_result = ReadNextPemMessage(input);
+ if (read_result.status == PemReadResult::kEof) {
+ return result;
+ }
+ if (read_result.status != PemReadResult::kOk) {
+ return std::vector<std::string>();
+ }
+ if (read_result.type != "CERTIFICATE") {
+ continue;
+ }
+ result.emplace_back(std::move(read_result.contents));
+ }
+}
+
+PublicKeyType CertificateView::public_key_type() const {
+ return PublicKeyTypeFromKey(public_key_.get());
+}
+
+bool CertificateView::ValidatePublicKeyParameters() {
+ // The profile here affects what certificates can be used when QUIC is used as
+ // a server library without any custom certificate provider logic.
+ // The goal is to allow at minimum any certificate that would be allowed on a
+ // regular Web session over TLS 1.3 while ensuring we do not expose any
+ // algorithms we don't want to support long-term.
+ PublicKeyType key_type = PublicKeyTypeFromKey(public_key_.get());
+ switch (key_type) {
+ case PublicKeyType::kRsa:
+ return EVP_PKEY_bits(public_key_.get()) >= 2048;
+ case PublicKeyType::kP256:
+ case PublicKeyType::kP384:
+ case PublicKeyType::kEd25519:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool CertificateView::VerifySignature(absl::string_view data,
+ absl::string_view signature,
+ uint16_t signature_algorithm) const {
+ if (PublicKeyTypeFromSignatureAlgorithm(signature_algorithm) !=
+ PublicKeyTypeFromKey(public_key_.get())) {
+ QUIC_BUG(quic_bug_10640_1)
+ << "Mismatch between the requested signature algorithm and the "
+ "type of the public key.";
+ return false;
+ }
+
+ bssl::ScopedEVP_MD_CTX md_ctx;
+ EVP_PKEY_CTX* pctx;
+ if (!EVP_DigestVerifyInit(
+ md_ctx.get(), &pctx,
+ SSL_get_signature_algorithm_digest(signature_algorithm), nullptr,
+ public_key_.get())) {
+ return false;
+ }
+ if (SSL_is_signature_algorithm_rsa_pss(signature_algorithm)) {
+ if (!EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) ||
+ !EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, -1)) {
+ return false;
+ }
+ }
+ return EVP_DigestVerify(
+ md_ctx.get(), reinterpret_cast<const uint8_t*>(signature.data()),
+ signature.size(), reinterpret_cast<const uint8_t*>(data.data()),
+ data.size());
+}
+
+absl::optional<std::string> CertificateView::GetHumanReadableSubject() const {
+ CBS input = StringPieceToCbs(subject_der_);
+ return DistinguishedNameToString(input);
+}
+
+std::unique_ptr<CertificatePrivateKey> CertificatePrivateKey::LoadFromDer(
+ absl::string_view private_key) {
+ std::unique_ptr<CertificatePrivateKey> result(new CertificatePrivateKey());
+ CBS private_key_cbs = StringPieceToCbs(private_key);
+ result->private_key_.reset(EVP_parse_private_key(&private_key_cbs));
+ if (result->private_key_ == nullptr || CBS_len(&private_key_cbs) != 0) {
+ return nullptr;
+ }
+ return result;
+}
+
+std::unique_ptr<CertificatePrivateKey> CertificatePrivateKey::LoadPemFromStream(
+ std::istream* input) {
+skip:
+ PemReadResult result = ReadNextPemMessage(input);
+ if (result.status != PemReadResult::kOk) {
+ return nullptr;
+ }
+ // RFC 5958 OneAsymmetricKey message.
+ if (result.type == "PRIVATE KEY") {
+ return LoadFromDer(result.contents);
+ }
+ // Legacy OpenSSL format: PKCS#1 (RFC 8017) RSAPrivateKey message.
+ if (result.type == "RSA PRIVATE KEY") {
+ CBS private_key_cbs = StringPieceToCbs(result.contents);
+ bssl::UniquePtr<RSA> rsa(RSA_parse_private_key(&private_key_cbs));
+ if (rsa == nullptr || CBS_len(&private_key_cbs) != 0) {
+ return nullptr;
+ }
+
+ std::unique_ptr<CertificatePrivateKey> key(new CertificatePrivateKey());
+ key->private_key_.reset(EVP_PKEY_new());
+ EVP_PKEY_assign_RSA(key->private_key_.get(), rsa.release());
+ return key;
+ }
+ // EC keys are sometimes generated with "openssl ecparam -genkey". If the user
+ // forgets -noout, OpenSSL will output a redundant copy of the EC parameters.
+ // Skip those.
+ if (result.type == "EC PARAMETERS") {
+ goto skip;
+ }
+ // Legacy OpenSSL format: RFC 5915 ECPrivateKey message.
+ if (result.type == "EC PRIVATE KEY") {
+ CBS private_key_cbs = StringPieceToCbs(result.contents);
+ bssl::UniquePtr<EC_KEY> ec_key(
+ EC_KEY_parse_private_key(&private_key_cbs, /*group=*/nullptr));
+ if (ec_key == nullptr || CBS_len(&private_key_cbs) != 0) {
+ return nullptr;
+ }
+
+ std::unique_ptr<CertificatePrivateKey> key(new CertificatePrivateKey());
+ key->private_key_.reset(EVP_PKEY_new());
+ EVP_PKEY_assign_EC_KEY(key->private_key_.get(), ec_key.release());
+ return key;
+ }
+ // Unknown format.
+ return nullptr;
+}
+
+std::string CertificatePrivateKey::Sign(absl::string_view input,
+ uint16_t signature_algorithm) const {
+ if (!ValidForSignatureAlgorithm(signature_algorithm)) {
+ QUIC_BUG(quic_bug_10640_2)
+ << "Mismatch between the requested signature algorithm and the "
+ "type of the private key.";
+ return "";
+ }
+
+ bssl::ScopedEVP_MD_CTX md_ctx;
+ EVP_PKEY_CTX* pctx;
+ if (!EVP_DigestSignInit(
+ md_ctx.get(), &pctx,
+ SSL_get_signature_algorithm_digest(signature_algorithm),
+ /*e=*/nullptr, private_key_.get())) {
+ return "";
+ }
+ if (SSL_is_signature_algorithm_rsa_pss(signature_algorithm)) {
+ if (!EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) ||
+ !EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, -1)) {
+ return "";
+ }
+ }
+
+ std::string output;
+ size_t output_size;
+ if (!EVP_DigestSign(md_ctx.get(), /*out_sig=*/nullptr, &output_size,
+ reinterpret_cast<const uint8_t*>(input.data()),
+ input.size())) {
+ return "";
+ }
+ output.resize(output_size);
+ if (!EVP_DigestSign(
+ md_ctx.get(), reinterpret_cast<uint8_t*>(&output[0]), &output_size,
+ reinterpret_cast<const uint8_t*>(input.data()), input.size())) {
+ return "";
+ }
+ output.resize(output_size);
+ return output;
+}
+
+bool CertificatePrivateKey::MatchesPublicKey(
+ const CertificateView& view) const {
+ return EVP_PKEY_cmp(view.public_key(), private_key_.get()) == 1;
+}
+
+bool CertificatePrivateKey::ValidForSignatureAlgorithm(
+ uint16_t signature_algorithm) const {
+ return PublicKeyTypeFromSignatureAlgorithm(signature_algorithm) ==
+ PublicKeyTypeFromKey(private_key_.get());
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_view.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_view.h
new file mode 100644
index 00000000000..a0ca3c33ecf
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_view.h
@@ -0,0 +1,149 @@
+// 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CERTIFICATE_VIEW_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CERTIFICATE_VIEW_H_
+
+#include <istream>
+#include <memory>
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
+#include "openssl/base.h"
+#include "openssl/bytestring.h"
+#include "openssl/evp.h"
+#include "quiche/quic/core/crypto/boring_utils.h"
+#include "quiche/quic/core/quic_time.h"
+#include "quiche/quic/core/quic_types.h"
+#include "quiche/quic/platform/api/quic_export.h"
+#include "quiche/quic/platform/api/quic_ip_address.h"
+
+namespace quic {
+
+struct QUIC_EXPORT_PRIVATE PemReadResult {
+ enum Status { kOk, kEof, kError };
+ Status status;
+ std::string contents;
+ // The type of the PEM message (e.g., if the message starts with
+ // "-----BEGIN CERTIFICATE-----", the |type| would be "CERTIFICATE").
+ std::string type;
+};
+
+// Reads |input| line-by-line and returns the next available PEM message.
+QUIC_EXPORT_PRIVATE PemReadResult ReadNextPemMessage(std::istream* input);
+
+// Cryptograhpic algorithms recognized in X.509.
+enum class PublicKeyType {
+ kRsa,
+ kP256,
+ kP384,
+ kEd25519,
+ kUnknown,
+};
+QUIC_EXPORT_PRIVATE std::string PublicKeyTypeToString(PublicKeyType type);
+
+// CertificateView represents a parsed version of a single X.509 certificate. As
+// the word "view" implies, it does not take ownership of the underlying strings
+// and consists primarily of pointers into the certificate that is passed into
+// the parser.
+class QUIC_EXPORT_PRIVATE CertificateView {
+ public:
+ // Parses a single DER-encoded X.509 certificate. Returns nullptr on parse
+ // error.
+ static std::unique_ptr<CertificateView> ParseSingleCertificate(
+ absl::string_view certificate);
+
+ // Loads all PEM-encoded X.509 certificates found in the |input| stream
+ // without parsing them. Returns an empty vector if any parsing error occurs.
+ static std::vector<std::string> LoadPemFromStream(std::istream* input);
+
+ QuicWallTime validity_start() const { return validity_start_; }
+ QuicWallTime validity_end() const { return validity_end_; }
+ const EVP_PKEY* public_key() const { return public_key_.get(); }
+
+ const std::vector<absl::string_view>& subject_alt_name_domains() const {
+ return subject_alt_name_domains_;
+ }
+ const std::vector<QuicIpAddress>& subject_alt_name_ips() const {
+ return subject_alt_name_ips_;
+ }
+
+ // Returns a human-readable representation of the Subject field. The format
+ // is similar to RFC 2253, but does not match it exactly.
+ absl::optional<std::string> GetHumanReadableSubject() const;
+
+ // |signature_algorithm| is a TLS signature algorithm ID.
+ bool VerifySignature(absl::string_view data, absl::string_view signature,
+ uint16_t signature_algorithm) const;
+
+ // Returns the type of the key used in the certificate's SPKI.
+ PublicKeyType public_key_type() const;
+
+ private:
+ CertificateView() = default;
+
+ QuicWallTime validity_start_ = QuicWallTime::Zero();
+ QuicWallTime validity_end_ = QuicWallTime::Zero();
+ absl::string_view subject_der_;
+
+ // Public key parsed from SPKI.
+ bssl::UniquePtr<EVP_PKEY> public_key_;
+
+ // SubjectAltName, https://tools.ietf.org/html/rfc5280#section-4.2.1.6
+ std::vector<absl::string_view> subject_alt_name_domains_;
+ std::vector<QuicIpAddress> subject_alt_name_ips_;
+
+ // Called from ParseSingleCertificate().
+ bool ParseExtensions(CBS extensions);
+ bool ValidatePublicKeyParameters();
+};
+
+// CertificatePrivateKey represents a private key that can be used with an X.509
+// certificate.
+class QUIC_EXPORT_PRIVATE CertificatePrivateKey {
+ public:
+ explicit CertificatePrivateKey(bssl::UniquePtr<EVP_PKEY> private_key)
+ : private_key_(std::move(private_key)) {}
+
+ // Loads a DER-encoded PrivateKeyInfo structure (RFC 5958) as a private key.
+ static std::unique_ptr<CertificatePrivateKey> LoadFromDer(
+ absl::string_view private_key);
+
+ // Loads a private key from a PEM file formatted according to RFC 7468. Also
+ // supports legacy OpenSSL RSA key format ("BEGIN RSA PRIVATE KEY").
+ static std::unique_ptr<CertificatePrivateKey> LoadPemFromStream(
+ std::istream* input);
+
+ // |signature_algorithm| is a TLS signature algorithm ID.
+ std::string Sign(absl::string_view input, uint16_t signature_algorithm) const;
+
+ // Verifies that the private key in question matches the public key of the
+ // certificate |view|.
+ bool MatchesPublicKey(const CertificateView& view) const;
+
+ // Verifies that the private key can be used with the specified TLS signature
+ // algorithm.
+ bool ValidForSignatureAlgorithm(uint16_t signature_algorithm) const;
+
+ EVP_PKEY* private_key() const { return private_key_.get(); }
+
+ private:
+ CertificatePrivateKey() = default;
+
+ bssl::UniquePtr<EVP_PKEY> private_key_;
+};
+
+// Parses a DER-encoded X.509 NameAttribute. Exposed primarily for testing.
+QUIC_EXPORT_PRIVATE absl::optional<std::string> X509NameAttributeToString(
+ CBS input);
+
+// Parses a DER time based on the specified ASN.1 tag. Exposed primarily for
+// testing.
+QUIC_EXPORT_PRIVATE absl::optional<quic::QuicWallTime> ParseDerTime(
+ unsigned tag, absl::string_view payload);
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CERTIFICATE_VIEW_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_view_der_fuzzer.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_view_der_fuzzer.cc
new file mode 100644
index 00000000000..81c91eb943b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_view_der_fuzzer.cc
@@ -0,0 +1,19 @@
+// 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 <string>
+
+#include "quiche/quic/core/crypto/certificate_view.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ std::string input(reinterpret_cast<const char*>(data), size);
+
+ std::unique_ptr<quic::CertificateView> view =
+ quic::CertificateView::ParseSingleCertificate(input);
+ if (view != nullptr) {
+ view->GetHumanReadableSubject();
+ }
+ quic::CertificatePrivateKey::LoadFromDer(input);
+ return 0;
+}
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_view_pem_fuzzer.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_view_pem_fuzzer.cc
new file mode 100644
index 00000000000..e6d70e51218
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_view_pem_fuzzer.cc
@@ -0,0 +1,18 @@
+// 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 <sstream>
+#include <string>
+
+#include "quiche/quic/core/crypto/certificate_view.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ std::string input(reinterpret_cast<const char*>(data), size);
+ std::stringstream stream(input);
+
+ quic::CertificateView::LoadPemFromStream(&stream);
+ stream.seekg(0);
+ quic::CertificatePrivateKey::LoadPemFromStream(&stream);
+ return 0;
+}
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_view_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_view_test.cc
new file mode 100644
index 00000000000..b9ca08abc09
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/certificate_view_test.cc
@@ -0,0 +1,214 @@
+// 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 "quiche/quic/core/crypto/certificate_view.h"
+
+#include <memory>
+#include <sstream>
+
+#include "absl/strings/escaping.h"
+#include "absl/strings/string_view.h"
+#include "openssl/base.h"
+#include "openssl/bytestring.h"
+#include "openssl/evp.h"
+#include "openssl/ssl.h"
+#include "quiche/quic/core/crypto/boring_utils.h"
+#include "quiche/quic/core/quic_time.h"
+#include "quiche/quic/platform/api/quic_ip_address.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/test_certificates.h"
+#include "quiche/common/platform/api/quiche_time_utils.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+using ::testing::ElementsAre;
+using ::testing::HasSubstr;
+using ::testing::Optional;
+
+TEST(CertificateViewTest, PemParser) {
+ std::stringstream stream(kTestCertificatePem);
+ PemReadResult result = ReadNextPemMessage(&stream);
+ EXPECT_EQ(result.status, PemReadResult::kOk);
+ EXPECT_EQ(result.type, "CERTIFICATE");
+ EXPECT_EQ(result.contents, kTestCertificate);
+
+ result = ReadNextPemMessage(&stream);
+ EXPECT_EQ(result.status, PemReadResult::kEof);
+}
+
+TEST(CertificateViewTest, Parse) {
+ std::unique_ptr<CertificateView> view =
+ CertificateView::ParseSingleCertificate(kTestCertificate);
+ ASSERT_TRUE(view != nullptr);
+
+ EXPECT_THAT(view->subject_alt_name_domains(),
+ ElementsAre(absl::string_view("www.example.org"),
+ absl::string_view("mail.example.org"),
+ absl::string_view("mail.example.com")));
+ EXPECT_THAT(view->subject_alt_name_ips(),
+ ElementsAre(QuicIpAddress::Loopback4()));
+ EXPECT_EQ(EVP_PKEY_id(view->public_key()), EVP_PKEY_RSA);
+
+ const QuicWallTime validity_start = QuicWallTime::FromUNIXSeconds(
+ *quiche::QuicheUtcDateTimeToUnixSeconds(2020, 1, 30, 18, 13, 59));
+ EXPECT_EQ(view->validity_start(), validity_start);
+ const QuicWallTime validity_end = QuicWallTime::FromUNIXSeconds(
+ *quiche::QuicheUtcDateTimeToUnixSeconds(2020, 2, 2, 18, 13, 59));
+ EXPECT_EQ(view->validity_end(), validity_end);
+ EXPECT_EQ(view->public_key_type(), PublicKeyType::kRsa);
+ EXPECT_EQ(PublicKeyTypeToString(view->public_key_type()), "RSA");
+
+ EXPECT_EQ("C=US,ST=California,L=Mountain View,O=QUIC Server,CN=127.0.0.1",
+ view->GetHumanReadableSubject());
+}
+
+TEST(CertificateViewTest, ParseCertWithUnknownSanType) {
+ std::stringstream stream(kTestCertWithUnknownSanTypePem);
+ PemReadResult result = ReadNextPemMessage(&stream);
+ EXPECT_EQ(result.status, PemReadResult::kOk);
+ EXPECT_EQ(result.type, "CERTIFICATE");
+
+ std::unique_ptr<CertificateView> view =
+ CertificateView::ParseSingleCertificate(result.contents);
+ EXPECT_TRUE(view != nullptr);
+}
+
+TEST(CertificateViewTest, PemSingleCertificate) {
+ std::stringstream pem_stream(kTestCertificatePem);
+ std::vector<std::string> chain =
+ CertificateView::LoadPemFromStream(&pem_stream);
+ EXPECT_THAT(chain, ElementsAre(kTestCertificate));
+}
+
+TEST(CertificateViewTest, PemMultipleCertificates) {
+ std::stringstream pem_stream(kTestCertificateChainPem);
+ std::vector<std::string> chain =
+ CertificateView::LoadPemFromStream(&pem_stream);
+ EXPECT_THAT(chain,
+ ElementsAre(kTestCertificate, HasSubstr("QUIC Server Root CA")));
+}
+
+TEST(CertificateViewTest, PemNoCertificates) {
+ std::stringstream pem_stream("one\ntwo\nthree\n");
+ std::vector<std::string> chain =
+ CertificateView::LoadPemFromStream(&pem_stream);
+ EXPECT_TRUE(chain.empty());
+}
+
+TEST(CertificateViewTest, SignAndVerify) {
+ std::unique_ptr<CertificatePrivateKey> key =
+ CertificatePrivateKey::LoadFromDer(kTestCertificatePrivateKey);
+ ASSERT_TRUE(key != nullptr);
+
+ std::string data = "A really important message";
+ std::string signature = key->Sign(data, SSL_SIGN_RSA_PSS_RSAE_SHA256);
+ ASSERT_FALSE(signature.empty());
+
+ std::unique_ptr<CertificateView> view =
+ CertificateView::ParseSingleCertificate(kTestCertificate);
+ ASSERT_TRUE(view != nullptr);
+ EXPECT_TRUE(key->MatchesPublicKey(*view));
+
+ EXPECT_TRUE(
+ view->VerifySignature(data, signature, SSL_SIGN_RSA_PSS_RSAE_SHA256));
+ EXPECT_FALSE(view->VerifySignature("An unimportant message", signature,
+ SSL_SIGN_RSA_PSS_RSAE_SHA256));
+ EXPECT_FALSE(view->VerifySignature(data, "Not a signature",
+ SSL_SIGN_RSA_PSS_RSAE_SHA256));
+}
+
+TEST(CertificateViewTest, PrivateKeyPem) {
+ std::unique_ptr<CertificateView> view =
+ CertificateView::ParseSingleCertificate(kTestCertificate);
+ ASSERT_TRUE(view != nullptr);
+
+ std::stringstream pem_stream(kTestCertificatePrivateKeyPem);
+ std::unique_ptr<CertificatePrivateKey> pem_key =
+ CertificatePrivateKey::LoadPemFromStream(&pem_stream);
+ ASSERT_TRUE(pem_key != nullptr);
+ EXPECT_TRUE(pem_key->MatchesPublicKey(*view));
+
+ std::stringstream legacy_stream(kTestCertificatePrivateKeyLegacyPem);
+ std::unique_ptr<CertificatePrivateKey> legacy_key =
+ CertificatePrivateKey::LoadPemFromStream(&legacy_stream);
+ ASSERT_TRUE(legacy_key != nullptr);
+ EXPECT_TRUE(legacy_key->MatchesPublicKey(*view));
+}
+
+TEST(CertificateViewTest, PrivateKeyEcdsaPem) {
+ std::stringstream pem_stream(kTestEcPrivateKeyLegacyPem);
+ std::unique_ptr<CertificatePrivateKey> key =
+ CertificatePrivateKey::LoadPemFromStream(&pem_stream);
+ ASSERT_TRUE(key != nullptr);
+ EXPECT_TRUE(key->ValidForSignatureAlgorithm(SSL_SIGN_ECDSA_SECP256R1_SHA256));
+}
+
+TEST(CertificateViewTest, DerTime) {
+ EXPECT_THAT(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024Z"),
+ Optional(QuicWallTime::FromUNIXSeconds(24)));
+ EXPECT_THAT(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19710101000024Z"),
+ Optional(QuicWallTime::FromUNIXSeconds(365 * 86400 + 24)));
+ EXPECT_THAT(ParseDerTime(CBS_ASN1_UTCTIME, "700101000024Z"),
+ Optional(QuicWallTime::FromUNIXSeconds(24)));
+ EXPECT_TRUE(ParseDerTime(CBS_ASN1_UTCTIME, "200101000024Z").has_value());
+
+ EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, ""), absl::nullopt);
+ EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024.001Z"),
+ absl::nullopt);
+ EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024Q"),
+ absl::nullopt);
+ EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024-0500"),
+ absl::nullopt);
+ EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "700101000024ZZ"),
+ absl::nullopt);
+ EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024.00Z"),
+ absl::nullopt);
+ EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024.Z"),
+ absl::nullopt);
+ EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "197O0101000024Z"),
+ absl::nullopt);
+ EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024.0O1Z"),
+ absl::nullopt);
+ EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "-9700101000024Z"),
+ absl::nullopt);
+ EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "1970-101000024Z"),
+ absl::nullopt);
+
+ EXPECT_TRUE(ParseDerTime(CBS_ASN1_UTCTIME, "490101000024Z").has_value());
+ // This should parse as 1950, which predates UNIX epoch.
+ EXPECT_FALSE(ParseDerTime(CBS_ASN1_UTCTIME, "500101000024Z").has_value());
+
+ EXPECT_THAT(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101230000Z"),
+ Optional(QuicWallTime::FromUNIXSeconds(23 * 3600)));
+ EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101240000Z"),
+ absl::nullopt);
+}
+
+TEST(CertificateViewTest, NameAttribute) {
+ // OBJECT_IDENTIFIER { 1.2.840.113554.4.1.112411 }
+ // UTF8String { "Test" }
+ std::string unknown_oid =
+ absl::HexStringToBytes("060b2a864886f712040186ee1b0c0454657374");
+ EXPECT_EQ("1.2.840.113554.4.1.112411=Test",
+ X509NameAttributeToString(StringPieceToCbs(unknown_oid)));
+
+ // OBJECT_IDENTIFIER { 2.5.4.3 }
+ // UTF8String { "Bell: \x07" }
+ std::string non_printable =
+ absl::HexStringToBytes("06035504030c0742656c6c3a2007");
+ EXPECT_EQ(R"(CN=Bell: \x07)",
+ X509NameAttributeToString(StringPieceToCbs(non_printable)));
+
+ // OBJECT_IDENTIFIER { "\x55\x80" }
+ // UTF8String { "Test" }
+ std::string invalid_oid = absl::HexStringToBytes("060255800c0454657374");
+ EXPECT_EQ("(5580)=Test",
+ X509NameAttributeToString(StringPieceToCbs(invalid_oid)));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_decrypter.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_decrypter.cc
new file mode 100644
index 00000000000..31758b43245
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_decrypter.cc
@@ -0,0 +1,41 @@
+// Copyright 2014 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 "quiche/quic/core/crypto/chacha20_poly1305_decrypter.h"
+
+#include "openssl/aead.h"
+#include "openssl/tls1.h"
+
+namespace quic {
+
+namespace {
+
+const size_t kKeySize = 32;
+const size_t kNonceSize = 12;
+
+} // namespace
+
+ChaCha20Poly1305Decrypter::ChaCha20Poly1305Decrypter()
+ : ChaChaBaseDecrypter(EVP_aead_chacha20_poly1305, kKeySize, kAuthTagSize,
+ kNonceSize,
+ /* use_ietf_nonce_construction */ false) {
+ static_assert(kKeySize <= kMaxKeySize, "key size too big");
+ static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
+}
+
+ChaCha20Poly1305Decrypter::~ChaCha20Poly1305Decrypter() {}
+
+uint32_t ChaCha20Poly1305Decrypter::cipher_id() const {
+ return TLS1_CK_CHACHA20_POLY1305_SHA256;
+}
+
+QuicPacketCount ChaCha20Poly1305Decrypter::GetIntegrityLimit() const {
+ // For AEAD_CHACHA20_POLY1305, the integrity limit is 2^36 invalid packets.
+ // https://quicwg.org/base-drafts/draft-ietf-quic-tls.html#name-limits-on-aead-usage
+ static_assert(kMaxIncomingPacketSize < 16384,
+ "This key limit requires limits on decryption payload sizes");
+ return 68719476736U;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_decrypter.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_decrypter.h
new file mode 100644
index 00000000000..6eb6c87f0a7
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_decrypter.h
@@ -0,0 +1,41 @@
+// Copyright 2014 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_DECRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_DECRYPTER_H_
+
+#include <cstdint>
+
+#include "quiche/quic/core/crypto/chacha_base_decrypter.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// A ChaCha20Poly1305Decrypter is a QuicDecrypter that implements the
+// AEAD_CHACHA20_POLY1305 algorithm specified in RFC 7539, except that
+// it truncates the Poly1305 authenticator to 12 bytes. Create an instance
+// by calling QuicDecrypter::Create(kCC20).
+//
+// It uses an authentication tag of 12 bytes (96 bits). The fixed prefix of the
+// nonce is four bytes.
+class QUIC_EXPORT_PRIVATE ChaCha20Poly1305Decrypter
+ : public ChaChaBaseDecrypter {
+ public:
+ enum {
+ kAuthTagSize = 12,
+ };
+
+ ChaCha20Poly1305Decrypter();
+ ChaCha20Poly1305Decrypter(const ChaCha20Poly1305Decrypter&) = delete;
+ ChaCha20Poly1305Decrypter& operator=(const ChaCha20Poly1305Decrypter&) =
+ delete;
+ ~ChaCha20Poly1305Decrypter() override;
+
+ uint32_t cipher_id() const override;
+ QuicPacketCount GetIntegrityLimit() const override;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_DECRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_decrypter_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_decrypter_test.cc
new file mode 100644
index 00000000000..019c56b5902
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_decrypter_test.cc
@@ -0,0 +1,178 @@
+// Copyright 2014 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 "quiche/quic/core/crypto/chacha20_poly1305_decrypter.h"
+
+#include <memory>
+#include <string>
+
+#include "absl/strings/escaping.h"
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/quic_test_utils.h"
+#include "quiche/common/test_tools/quiche_test_utils.h"
+
+namespace {
+
+// The test vectors come from RFC 7539 Section 2.8.2.
+
+// Each test vector consists of six strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a nullptr |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ // Input:
+ const char* key;
+ const char* iv;
+ const char* fixed;
+ const char* aad;
+ const char* ct;
+
+ // Expected output:
+ const char* pt; // An empty string "" means decryption succeeded and
+ // the plaintext is zero-length. nullptr means decryption
+ // failed.
+};
+
+const TestVector test_vectors[] = {
+ {"808182838485868788898a8b8c8d8e8f"
+ "909192939495969798999a9b9c9d9e9f",
+
+ "4041424344454647",
+
+ "07000000",
+
+ "50515253c0c1c2c3c4c5c6c7",
+
+ "d31a8d34648e60db7b86afbc53ef7ec2"
+ "a4aded51296e08fea9e2b5a736ee62d6"
+ "3dbea45e8ca9671282fafb69da92728b"
+ "1a71de0a9e060b2905d6a5b67ecd3b36"
+ "92ddbd7f2d778b8c9803aee328091b58"
+ "fab324e4fad675945585808b4831d7bc"
+ "3ff4def08e4b7a9de576d26586cec64b"
+ "6116"
+ "1ae10b594f09e26a7e902ecb", // "d0600691" truncated
+
+ "4c616469657320616e642047656e746c"
+ "656d656e206f662074686520636c6173"
+ "73206f66202739393a20496620492063"
+ "6f756c64206f6666657220796f75206f"
+ "6e6c79206f6e652074697020666f7220"
+ "746865206675747572652c2073756e73"
+ "637265656e20776f756c642062652069"
+ "742e"},
+ // Modify the ciphertext (Poly1305 authenticator).
+ {"808182838485868788898a8b8c8d8e8f"
+ "909192939495969798999a9b9c9d9e9f",
+
+ "4041424344454647",
+
+ "07000000",
+
+ "50515253c0c1c2c3c4c5c6c7",
+
+ "d31a8d34648e60db7b86afbc53ef7ec2"
+ "a4aded51296e08fea9e2b5a736ee62d6"
+ "3dbea45e8ca9671282fafb69da92728b"
+ "1a71de0a9e060b2905d6a5b67ecd3b36"
+ "92ddbd7f2d778b8c9803aee328091b58"
+ "fab324e4fad675945585808b4831d7bc"
+ "3ff4def08e4b7a9de576d26586cec64b"
+ "6116"
+ "1ae10b594f09e26a7e902ecc", // "d0600691" truncated
+
+ nullptr},
+ // Modify the associated data.
+ {"808182838485868788898a8b8c8d8e8f"
+ "909192939495969798999a9b9c9d9e9f",
+
+ "4041424344454647",
+
+ "07000000",
+
+ "60515253c0c1c2c3c4c5c6c7",
+
+ "d31a8d34648e60db7b86afbc53ef7ec2"
+ "a4aded51296e08fea9e2b5a736ee62d6"
+ "3dbea45e8ca9671282fafb69da92728b"
+ "1a71de0a9e060b2905d6a5b67ecd3b36"
+ "92ddbd7f2d778b8c9803aee328091b58"
+ "fab324e4fad675945585808b4831d7bc"
+ "3ff4def08e4b7a9de576d26586cec64b"
+ "6116"
+ "1ae10b594f09e26a7e902ecb", // "d0600691" truncated
+
+ nullptr},
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+} // namespace
+
+namespace quic {
+namespace test {
+
+// DecryptWithNonce wraps the |Decrypt| method of |decrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the plaintext.
+QuicData* DecryptWithNonce(ChaCha20Poly1305Decrypter* decrypter,
+ absl::string_view nonce,
+ absl::string_view associated_data,
+ absl::string_view ciphertext) {
+ uint64_t packet_number;
+ absl::string_view nonce_prefix(nonce.data(),
+ nonce.size() - sizeof(packet_number));
+ decrypter->SetNoncePrefix(nonce_prefix);
+ memcpy(&packet_number, nonce.data() + nonce_prefix.size(),
+ sizeof(packet_number));
+ std::unique_ptr<char[]> output(new char[ciphertext.length()]);
+ size_t output_length = 0;
+ const bool success = decrypter->DecryptPacket(
+ packet_number, associated_data, ciphertext, output.get(), &output_length,
+ ciphertext.length());
+ if (!success) {
+ return nullptr;
+ }
+ return new QuicData(output.release(), output_length, true);
+}
+
+class ChaCha20Poly1305DecrypterTest : public QuicTest {};
+
+TEST_F(ChaCha20Poly1305DecrypterTest, Decrypt) {
+ for (size_t i = 0; test_vectors[i].key != nullptr; i++) {
+ // If not present then decryption is expected to fail.
+ bool has_pt = test_vectors[i].pt;
+
+ // Decode the test vector.
+ std::string key = absl::HexStringToBytes(test_vectors[i].key);
+ std::string iv = absl::HexStringToBytes(test_vectors[i].iv);
+ std::string fixed = absl::HexStringToBytes(test_vectors[i].fixed);
+ std::string aad = absl::HexStringToBytes(test_vectors[i].aad);
+ std::string ct = absl::HexStringToBytes(test_vectors[i].ct);
+ std::string pt;
+ if (has_pt) {
+ pt = absl::HexStringToBytes(test_vectors[i].pt);
+ }
+
+ ChaCha20Poly1305Decrypter decrypter;
+ ASSERT_TRUE(decrypter.SetKey(key));
+ std::unique_ptr<QuicData> decrypted(DecryptWithNonce(
+ &decrypter, fixed + iv,
+ // This deliberately tests that the decrypter can handle an AAD that
+ // is set to nullptr, as opposed to a zero-length, non-nullptr pointer.
+ absl::string_view(aad.length() ? aad.data() : nullptr, aad.length()),
+ ct));
+ if (!decrypted) {
+ EXPECT_FALSE(has_pt);
+ continue;
+ }
+ EXPECT_TRUE(has_pt);
+
+ EXPECT_EQ(12u, ct.size() - decrypted->length());
+ ASSERT_EQ(pt.length(), decrypted->length());
+ quiche::test::CompareCharArraysWithHexError(
+ "plaintext", decrypted->data(), pt.length(), pt.data(), pt.length());
+ }
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_encrypter.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_encrypter.cc
new file mode 100644
index 00000000000..1adad076d9b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_encrypter.cc
@@ -0,0 +1,35 @@
+// Copyright 2014 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 "quiche/quic/core/crypto/chacha20_poly1305_encrypter.h"
+
+#include "openssl/evp.h"
+
+namespace quic {
+
+namespace {
+
+const size_t kKeySize = 32;
+const size_t kNonceSize = 12;
+
+} // namespace
+
+ChaCha20Poly1305Encrypter::ChaCha20Poly1305Encrypter()
+ : ChaChaBaseEncrypter(EVP_aead_chacha20_poly1305, kKeySize, kAuthTagSize,
+ kNonceSize,
+ /* use_ietf_nonce_construction */ false) {
+ static_assert(kKeySize <= kMaxKeySize, "key size too big");
+ static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
+}
+
+ChaCha20Poly1305Encrypter::~ChaCha20Poly1305Encrypter() {}
+
+QuicPacketCount ChaCha20Poly1305Encrypter::GetConfidentialityLimit() const {
+ // For AEAD_CHACHA20_POLY1305, the confidentiality limit is greater than the
+ // number of possible packets (2^62) and so can be disregarded.
+ // https://quicwg.org/base-drafts/draft-ietf-quic-tls.html#name-limits-on-aead-usage
+ return std::numeric_limits<QuicPacketCount>::max();
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_encrypter.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_encrypter.h
new file mode 100644
index 00000000000..d37f26c8a96
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_encrypter.h
@@ -0,0 +1,38 @@
+// Copyright 2014 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_ENCRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_ENCRYPTER_H_
+
+#include "quiche/quic/core/crypto/chacha_base_encrypter.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// A ChaCha20Poly1305Encrypter is a QuicEncrypter that implements the
+// AEAD_CHACHA20_POLY1305 algorithm specified in RFC 7539, except that
+// it truncates the Poly1305 authenticator to 12 bytes. Create an instance
+// by calling QuicEncrypter::Create(kCC20).
+//
+// It uses an authentication tag of 12 bytes (96 bits). The fixed prefix of the
+// nonce is four bytes.
+class QUIC_EXPORT_PRIVATE ChaCha20Poly1305Encrypter
+ : public ChaChaBaseEncrypter {
+ public:
+ enum {
+ kAuthTagSize = 12,
+ };
+
+ ChaCha20Poly1305Encrypter();
+ ChaCha20Poly1305Encrypter(const ChaCha20Poly1305Encrypter&) = delete;
+ ChaCha20Poly1305Encrypter& operator=(const ChaCha20Poly1305Encrypter&) =
+ delete;
+ ~ChaCha20Poly1305Encrypter() override;
+
+ QuicPacketCount GetConfidentialityLimit() const override;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_ENCRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_encrypter_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_encrypter_test.cc
new file mode 100644
index 00000000000..9ae728a430a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_encrypter_test.cc
@@ -0,0 +1,159 @@
+// Copyright 2014 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 "quiche/quic/core/crypto/chacha20_poly1305_encrypter.h"
+
+#include <memory>
+#include <string>
+
+#include "absl/base/macros.h"
+#include "absl/strings/escaping.h"
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/crypto/chacha20_poly1305_decrypter.h"
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/quic_test_utils.h"
+#include "quiche/common/test_tools/quiche_test_utils.h"
+
+namespace {
+
+// The test vectors come from RFC 7539 Section 2.8.2.
+
+// Each test vector consists of five strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a nullptr |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ const char* key;
+ const char* pt;
+ const char* iv;
+ const char* fixed;
+ const char* aad;
+ const char* ct;
+};
+
+const TestVector test_vectors[] = {
+ {
+ "808182838485868788898a8b8c8d8e8f"
+ "909192939495969798999a9b9c9d9e9f",
+
+ "4c616469657320616e642047656e746c"
+ "656d656e206f662074686520636c6173"
+ "73206f66202739393a20496620492063"
+ "6f756c64206f6666657220796f75206f"
+ "6e6c79206f6e652074697020666f7220"
+ "746865206675747572652c2073756e73"
+ "637265656e20776f756c642062652069"
+ "742e",
+
+ "4041424344454647",
+
+ "07000000",
+
+ "50515253c0c1c2c3c4c5c6c7",
+
+ "d31a8d34648e60db7b86afbc53ef7ec2"
+ "a4aded51296e08fea9e2b5a736ee62d6"
+ "3dbea45e8ca9671282fafb69da92728b"
+ "1a71de0a9e060b2905d6a5b67ecd3b36"
+ "92ddbd7f2d778b8c9803aee328091b58"
+ "fab324e4fad675945585808b4831d7bc"
+ "3ff4def08e4b7a9de576d26586cec64b"
+ "6116"
+ "1ae10b594f09e26a7e902ecb", // "d0600691" truncated
+ },
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+} // namespace
+
+namespace quic {
+namespace test {
+
+// EncryptWithNonce wraps the |Encrypt| method of |encrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the ciphertext.
+QuicData* EncryptWithNonce(ChaCha20Poly1305Encrypter* encrypter,
+ absl::string_view nonce,
+ absl::string_view associated_data,
+ absl::string_view plaintext) {
+ size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length());
+ std::unique_ptr<char[]> ciphertext(new char[ciphertext_size]);
+
+ if (!encrypter->Encrypt(nonce, associated_data, plaintext,
+ reinterpret_cast<unsigned char*>(ciphertext.get()))) {
+ return nullptr;
+ }
+
+ return new QuicData(ciphertext.release(), ciphertext_size, true);
+}
+
+class ChaCha20Poly1305EncrypterTest : public QuicTest {};
+
+TEST_F(ChaCha20Poly1305EncrypterTest, EncryptThenDecrypt) {
+ ChaCha20Poly1305Encrypter encrypter;
+ ChaCha20Poly1305Decrypter decrypter;
+
+ std::string key = absl::HexStringToBytes(test_vectors[0].key);
+ ASSERT_TRUE(encrypter.SetKey(key));
+ ASSERT_TRUE(decrypter.SetKey(key));
+ ASSERT_TRUE(encrypter.SetNoncePrefix("abcd"));
+ ASSERT_TRUE(decrypter.SetNoncePrefix("abcd"));
+
+ uint64_t packet_number = UINT64_C(0x123456789ABC);
+ std::string associated_data = "associated_data";
+ std::string plaintext = "plaintext";
+ char encrypted[1024];
+ size_t len;
+ ASSERT_TRUE(encrypter.EncryptPacket(packet_number, associated_data, plaintext,
+ encrypted, &len,
+ ABSL_ARRAYSIZE(encrypted)));
+ absl::string_view ciphertext(encrypted, len);
+ char decrypted[1024];
+ ASSERT_TRUE(decrypter.DecryptPacket(packet_number, associated_data,
+ ciphertext, decrypted, &len,
+ ABSL_ARRAYSIZE(decrypted)));
+}
+
+TEST_F(ChaCha20Poly1305EncrypterTest, Encrypt) {
+ for (size_t i = 0; test_vectors[i].key != nullptr; i++) {
+ // Decode the test vector.
+ std::string key = absl::HexStringToBytes(test_vectors[i].key);
+ std::string pt = absl::HexStringToBytes(test_vectors[i].pt);
+ std::string iv = absl::HexStringToBytes(test_vectors[i].iv);
+ std::string fixed = absl::HexStringToBytes(test_vectors[i].fixed);
+ std::string aad = absl::HexStringToBytes(test_vectors[i].aad);
+ std::string ct = absl::HexStringToBytes(test_vectors[i].ct);
+
+ ChaCha20Poly1305Encrypter encrypter;
+ ASSERT_TRUE(encrypter.SetKey(key));
+ std::unique_ptr<QuicData> encrypted(EncryptWithNonce(
+ &encrypter, fixed + iv,
+ // This deliberately tests that the encrypter can handle an AAD that
+ // is set to nullptr, as opposed to a zero-length, non-nullptr pointer.
+ absl::string_view(aad.length() ? aad.data() : nullptr, aad.length()),
+ pt));
+ ASSERT_TRUE(encrypted.get());
+ EXPECT_EQ(12u, ct.size() - pt.size());
+ EXPECT_EQ(12u, encrypted->length() - pt.size());
+
+ quiche::test::CompareCharArraysWithHexError("ciphertext", encrypted->data(),
+ encrypted->length(), ct.data(),
+ ct.length());
+ }
+}
+
+TEST_F(ChaCha20Poly1305EncrypterTest, GetMaxPlaintextSize) {
+ ChaCha20Poly1305Encrypter encrypter;
+ EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1012));
+ EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(112));
+ EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(22));
+}
+
+TEST_F(ChaCha20Poly1305EncrypterTest, GetCiphertextSize) {
+ ChaCha20Poly1305Encrypter encrypter;
+ EXPECT_EQ(1012u, encrypter.GetCiphertextSize(1000));
+ EXPECT_EQ(112u, encrypter.GetCiphertextSize(100));
+ EXPECT_EQ(22u, encrypter.GetCiphertextSize(10));
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_tls_decrypter.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_tls_decrypter.cc
new file mode 100644
index 00000000000..93b099352c1
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_tls_decrypter.cc
@@ -0,0 +1,43 @@
+// Copyright 2017 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 "quiche/quic/core/crypto/chacha20_poly1305_tls_decrypter.h"
+
+#include "openssl/aead.h"
+#include "openssl/tls1.h"
+#include "quiche/quic/platform/api/quic_flag_utils.h"
+#include "quiche/quic/platform/api/quic_flags.h"
+
+namespace quic {
+
+namespace {
+
+const size_t kKeySize = 32;
+const size_t kNonceSize = 12;
+
+} // namespace
+
+ChaCha20Poly1305TlsDecrypter::ChaCha20Poly1305TlsDecrypter()
+ : ChaChaBaseDecrypter(EVP_aead_chacha20_poly1305, kKeySize, kAuthTagSize,
+ kNonceSize,
+ /* use_ietf_nonce_construction */ true) {
+ static_assert(kKeySize <= kMaxKeySize, "key size too big");
+ static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
+}
+
+ChaCha20Poly1305TlsDecrypter::~ChaCha20Poly1305TlsDecrypter() {}
+
+uint32_t ChaCha20Poly1305TlsDecrypter::cipher_id() const {
+ return TLS1_CK_CHACHA20_POLY1305_SHA256;
+}
+
+QuicPacketCount ChaCha20Poly1305TlsDecrypter::GetIntegrityLimit() const {
+ // For AEAD_CHACHA20_POLY1305, the integrity limit is 2^36 invalid packets.
+ // https://quicwg.org/base-drafts/draft-ietf-quic-tls.html#name-limits-on-aead-usage
+ static_assert(kMaxIncomingPacketSize < 16384,
+ "This key limit requires limits on decryption payload sizes");
+ return 68719476736U;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_tls_decrypter.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_tls_decrypter.h
new file mode 100644
index 00000000000..f8108f2662a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_tls_decrypter.h
@@ -0,0 +1,39 @@
+// Copyright 2017 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_TLS_DECRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_TLS_DECRYPTER_H_
+
+#include <cstdint>
+
+#include "quiche/quic/core/crypto/chacha_base_decrypter.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// A ChaCha20Poly1305TlsDecrypter is a QuicDecrypter that implements the
+// AEAD_CHACHA20_POLY1305 algorithm specified in RFC 7539 for use in IETF QUIC.
+//
+// It uses an authentication tag of 16 bytes (128 bits). It uses a 12 bytes IV
+// that is XOR'd with the packet number to compute the nonce.
+class QUIC_EXPORT_PRIVATE ChaCha20Poly1305TlsDecrypter
+ : public ChaChaBaseDecrypter {
+ public:
+ enum {
+ kAuthTagSize = 16,
+ };
+
+ ChaCha20Poly1305TlsDecrypter();
+ ChaCha20Poly1305TlsDecrypter(const ChaCha20Poly1305TlsDecrypter&) = delete;
+ ChaCha20Poly1305TlsDecrypter& operator=(const ChaCha20Poly1305TlsDecrypter&) =
+ delete;
+ ~ChaCha20Poly1305TlsDecrypter() override;
+
+ uint32_t cipher_id() const override;
+ QuicPacketCount GetIntegrityLimit() const override;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_TLS_DECRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_tls_decrypter_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_tls_decrypter_test.cc
new file mode 100644
index 00000000000..00686367dde
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_tls_decrypter_test.cc
@@ -0,0 +1,188 @@
+// Copyright 2017 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 "quiche/quic/core/crypto/chacha20_poly1305_tls_decrypter.h"
+
+#include <memory>
+#include <string>
+
+#include "absl/strings/escaping.h"
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/quic_test_utils.h"
+#include "quiche/common/test_tools/quiche_test_utils.h"
+
+namespace {
+
+// The test vectors come from RFC 7539 Section 2.8.2.
+
+// Each test vector consists of six strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a nullptr |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ // Input:
+ const char* key;
+ const char* iv;
+ const char* fixed;
+ const char* aad;
+ const char* ct;
+
+ // Expected output:
+ const char* pt; // An empty string "" means decryption succeeded and
+ // the plaintext is zero-length. nullptr means decryption
+ // failed.
+};
+
+const TestVector test_vectors[] = {
+ {"808182838485868788898a8b8c8d8e8f"
+ "909192939495969798999a9b9c9d9e9f",
+
+ "4041424344454647",
+
+ "07000000",
+
+ "50515253c0c1c2c3c4c5c6c7",
+
+ "d31a8d34648e60db7b86afbc53ef7ec2"
+ "a4aded51296e08fea9e2b5a736ee62d6"
+ "3dbea45e8ca9671282fafb69da92728b"
+ "1a71de0a9e060b2905d6a5b67ecd3b36"
+ "92ddbd7f2d778b8c9803aee328091b58"
+ "fab324e4fad675945585808b4831d7bc"
+ "3ff4def08e4b7a9de576d26586cec64b"
+ "6116"
+ "1ae10b594f09e26a7e902ecbd0600691",
+
+ "4c616469657320616e642047656e746c"
+ "656d656e206f662074686520636c6173"
+ "73206f66202739393a20496620492063"
+ "6f756c64206f6666657220796f75206f"
+ "6e6c79206f6e652074697020666f7220"
+ "746865206675747572652c2073756e73"
+ "637265656e20776f756c642062652069"
+ "742e"},
+ // Modify the ciphertext (Poly1305 authenticator).
+ {"808182838485868788898a8b8c8d8e8f"
+ "909192939495969798999a9b9c9d9e9f",
+
+ "4041424344454647",
+
+ "07000000",
+
+ "50515253c0c1c2c3c4c5c6c7",
+
+ "d31a8d34648e60db7b86afbc53ef7ec2"
+ "a4aded51296e08fea9e2b5a736ee62d6"
+ "3dbea45e8ca9671282fafb69da92728b"
+ "1a71de0a9e060b2905d6a5b67ecd3b36"
+ "92ddbd7f2d778b8c9803aee328091b58"
+ "fab324e4fad675945585808b4831d7bc"
+ "3ff4def08e4b7a9de576d26586cec64b"
+ "6116"
+ "1ae10b594f09e26a7e902eccd0600691",
+
+ nullptr},
+ // Modify the associated data.
+ {"808182838485868788898a8b8c8d8e8f"
+ "909192939495969798999a9b9c9d9e9f",
+
+ "4041424344454647",
+
+ "07000000",
+
+ "60515253c0c1c2c3c4c5c6c7",
+
+ "d31a8d34648e60db7b86afbc53ef7ec2"
+ "a4aded51296e08fea9e2b5a736ee62d6"
+ "3dbea45e8ca9671282fafb69da92728b"
+ "1a71de0a9e060b2905d6a5b67ecd3b36"
+ "92ddbd7f2d778b8c9803aee328091b58"
+ "fab324e4fad675945585808b4831d7bc"
+ "3ff4def08e4b7a9de576d26586cec64b"
+ "6116"
+ "1ae10b594f09e26a7e902ecbd0600691",
+
+ nullptr},
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+} // namespace
+
+namespace quic {
+namespace test {
+
+// DecryptWithNonce wraps the |Decrypt| method of |decrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the plaintext.
+QuicData* DecryptWithNonce(ChaCha20Poly1305TlsDecrypter* decrypter,
+ absl::string_view nonce,
+ absl::string_view associated_data,
+ absl::string_view ciphertext) {
+ decrypter->SetIV(nonce);
+ std::unique_ptr<char[]> output(new char[ciphertext.length()]);
+ size_t output_length = 0;
+ const bool success =
+ decrypter->DecryptPacket(0, associated_data, ciphertext, output.get(),
+ &output_length, ciphertext.length());
+ if (!success) {
+ return nullptr;
+ }
+ return new QuicData(output.release(), output_length, true);
+}
+
+class ChaCha20Poly1305TlsDecrypterTest : public QuicTest {};
+
+TEST_F(ChaCha20Poly1305TlsDecrypterTest, Decrypt) {
+ for (size_t i = 0; test_vectors[i].key != nullptr; i++) {
+ // If not present then decryption is expected to fail.
+ bool has_pt = test_vectors[i].pt;
+
+ // Decode the test vector.
+ std::string key = absl::HexStringToBytes(test_vectors[i].key);
+ std::string iv = absl::HexStringToBytes(test_vectors[i].iv);
+ std::string fixed = absl::HexStringToBytes(test_vectors[i].fixed);
+ std::string aad = absl::HexStringToBytes(test_vectors[i].aad);
+ std::string ct = absl::HexStringToBytes(test_vectors[i].ct);
+ std::string pt;
+ if (has_pt) {
+ pt = absl::HexStringToBytes(test_vectors[i].pt);
+ }
+
+ ChaCha20Poly1305TlsDecrypter decrypter;
+ ASSERT_TRUE(decrypter.SetKey(key));
+ std::unique_ptr<QuicData> decrypted(DecryptWithNonce(
+ &decrypter, fixed + iv,
+ // This deliberately tests that the decrypter can handle an AAD that
+ // is set to nullptr, as opposed to a zero-length, non-nullptr pointer.
+ absl::string_view(aad.length() ? aad.data() : nullptr, aad.length()),
+ ct));
+ if (!decrypted) {
+ EXPECT_FALSE(has_pt);
+ continue;
+ }
+ EXPECT_TRUE(has_pt);
+
+ EXPECT_EQ(16u, ct.size() - decrypted->length());
+ ASSERT_EQ(pt.length(), decrypted->length());
+ quiche::test::CompareCharArraysWithHexError(
+ "plaintext", decrypted->data(), pt.length(), pt.data(), pt.length());
+ }
+}
+
+TEST_F(ChaCha20Poly1305TlsDecrypterTest, GenerateHeaderProtectionMask) {
+ ChaCha20Poly1305TlsDecrypter decrypter;
+ std::string key = absl::HexStringToBytes(
+ "6a067f432787bd6034dd3f08f07fc9703a27e58c70e2d88d948b7f6489923cc7");
+ std::string sample =
+ absl::HexStringToBytes("1210d91cceb45c716b023f492c29e612");
+ QuicDataReader sample_reader(sample.data(), sample.size());
+ ASSERT_TRUE(decrypter.SetHeaderProtectionKey(key));
+ std::string mask = decrypter.GenerateHeaderProtectionMask(&sample_reader);
+ std::string expected_mask = absl::HexStringToBytes("1cc2cd98dc");
+ quiche::test::CompareCharArraysWithHexError(
+ "header protection mask", mask.data(), mask.size(), expected_mask.data(),
+ expected_mask.size());
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_tls_encrypter.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_tls_encrypter.cc
new file mode 100644
index 00000000000..fe6f6b44ac9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_tls_encrypter.cc
@@ -0,0 +1,35 @@
+// Copyright 2017 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 "quiche/quic/core/crypto/chacha20_poly1305_tls_encrypter.h"
+
+#include "openssl/evp.h"
+
+namespace quic {
+
+namespace {
+
+const size_t kKeySize = 32;
+const size_t kNonceSize = 12;
+
+} // namespace
+
+ChaCha20Poly1305TlsEncrypter::ChaCha20Poly1305TlsEncrypter()
+ : ChaChaBaseEncrypter(EVP_aead_chacha20_poly1305, kKeySize, kAuthTagSize,
+ kNonceSize,
+ /* use_ietf_nonce_construction */ true) {
+ static_assert(kKeySize <= kMaxKeySize, "key size too big");
+ static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
+}
+
+ChaCha20Poly1305TlsEncrypter::~ChaCha20Poly1305TlsEncrypter() {}
+
+QuicPacketCount ChaCha20Poly1305TlsEncrypter::GetConfidentialityLimit() const {
+ // For AEAD_CHACHA20_POLY1305, the confidentiality limit is greater than the
+ // number of possible packets (2^62) and so can be disregarded.
+ // https://quicwg.org/base-drafts/draft-ietf-quic-tls.html#name-limits-on-aead-usage
+ return std::numeric_limits<QuicPacketCount>::max();
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_tls_encrypter.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_tls_encrypter.h
new file mode 100644
index 00000000000..e5d8f378ec5
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_tls_encrypter.h
@@ -0,0 +1,36 @@
+// Copyright 2017 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_TLS_ENCRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_TLS_ENCRYPTER_H_
+
+#include "quiche/quic/core/crypto/chacha_base_encrypter.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// A ChaCha20Poly1305Encrypter is a QuicEncrypter that implements the
+// AEAD_CHACHA20_POLY1305 algorithm specified in RFC 7539 for use in IETF QUIC.
+//
+// It uses an authentication tag of 16 bytes (128 bits). It uses a 12 byte IV
+// that is XOR'd with the packet number to compute the nonce.
+class QUIC_EXPORT_PRIVATE ChaCha20Poly1305TlsEncrypter
+ : public ChaChaBaseEncrypter {
+ public:
+ enum {
+ kAuthTagSize = 16,
+ };
+
+ ChaCha20Poly1305TlsEncrypter();
+ ChaCha20Poly1305TlsEncrypter(const ChaCha20Poly1305TlsEncrypter&) = delete;
+ ChaCha20Poly1305TlsEncrypter& operator=(const ChaCha20Poly1305TlsEncrypter&) =
+ delete;
+ ~ChaCha20Poly1305TlsEncrypter() override;
+
+ QuicPacketCount GetConfidentialityLimit() const override;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_TLS_ENCRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_tls_encrypter_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_tls_encrypter_test.cc
new file mode 100644
index 00000000000..322651b846d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_tls_encrypter_test.cc
@@ -0,0 +1,173 @@
+// Copyright 2017 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 "quiche/quic/core/crypto/chacha20_poly1305_tls_encrypter.h"
+
+#include <memory>
+#include <string>
+
+#include "absl/base/macros.h"
+#include "absl/strings/escaping.h"
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/crypto/chacha20_poly1305_tls_decrypter.h"
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/quic_test_utils.h"
+#include "quiche/common/test_tools/quiche_test_utils.h"
+
+namespace {
+
+// The test vectors come from RFC 7539 Section 2.8.2.
+
+// Each test vector consists of five strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a nullptr |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ const char* key;
+ const char* pt;
+ const char* iv;
+ const char* fixed;
+ const char* aad;
+ const char* ct;
+};
+
+const TestVector test_vectors[] = {
+ {
+ "808182838485868788898a8b8c8d8e8f"
+ "909192939495969798999a9b9c9d9e9f",
+
+ "4c616469657320616e642047656e746c"
+ "656d656e206f662074686520636c6173"
+ "73206f66202739393a20496620492063"
+ "6f756c64206f6666657220796f75206f"
+ "6e6c79206f6e652074697020666f7220"
+ "746865206675747572652c2073756e73"
+ "637265656e20776f756c642062652069"
+ "742e",
+
+ "4041424344454647",
+
+ "07000000",
+
+ "50515253c0c1c2c3c4c5c6c7",
+
+ "d31a8d34648e60db7b86afbc53ef7ec2"
+ "a4aded51296e08fea9e2b5a736ee62d6"
+ "3dbea45e8ca9671282fafb69da92728b"
+ "1a71de0a9e060b2905d6a5b67ecd3b36"
+ "92ddbd7f2d778b8c9803aee328091b58"
+ "fab324e4fad675945585808b4831d7bc"
+ "3ff4def08e4b7a9de576d26586cec64b"
+ "6116"
+ "1ae10b594f09e26a7e902ecbd0600691",
+ },
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+
+} // namespace
+
+namespace quic {
+namespace test {
+
+// EncryptWithNonce wraps the |Encrypt| method of |encrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the ciphertext.
+QuicData* EncryptWithNonce(ChaCha20Poly1305TlsEncrypter* encrypter,
+ absl::string_view nonce,
+ absl::string_view associated_data,
+ absl::string_view plaintext) {
+ size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length());
+ std::unique_ptr<char[]> ciphertext(new char[ciphertext_size]);
+
+ if (!encrypter->Encrypt(nonce, associated_data, plaintext,
+ reinterpret_cast<unsigned char*>(ciphertext.get()))) {
+ return nullptr;
+ }
+
+ return new QuicData(ciphertext.release(), ciphertext_size, true);
+}
+
+class ChaCha20Poly1305TlsEncrypterTest : public QuicTest {};
+
+TEST_F(ChaCha20Poly1305TlsEncrypterTest, EncryptThenDecrypt) {
+ ChaCha20Poly1305TlsEncrypter encrypter;
+ ChaCha20Poly1305TlsDecrypter decrypter;
+
+ std::string key = absl::HexStringToBytes(test_vectors[0].key);
+ ASSERT_TRUE(encrypter.SetKey(key));
+ ASSERT_TRUE(decrypter.SetKey(key));
+ ASSERT_TRUE(encrypter.SetIV("abcdefghijkl"));
+ ASSERT_TRUE(decrypter.SetIV("abcdefghijkl"));
+
+ uint64_t packet_number = UINT64_C(0x123456789ABC);
+ std::string associated_data = "associated_data";
+ std::string plaintext = "plaintext";
+ char encrypted[1024];
+ size_t len;
+ ASSERT_TRUE(encrypter.EncryptPacket(packet_number, associated_data, plaintext,
+ encrypted, &len,
+ ABSL_ARRAYSIZE(encrypted)));
+ absl::string_view ciphertext(encrypted, len);
+ char decrypted[1024];
+ ASSERT_TRUE(decrypter.DecryptPacket(packet_number, associated_data,
+ ciphertext, decrypted, &len,
+ ABSL_ARRAYSIZE(decrypted)));
+}
+
+TEST_F(ChaCha20Poly1305TlsEncrypterTest, Encrypt) {
+ for (size_t i = 0; test_vectors[i].key != nullptr; i++) {
+ // Decode the test vector.
+ std::string key = absl::HexStringToBytes(test_vectors[i].key);
+ std::string pt = absl::HexStringToBytes(test_vectors[i].pt);
+ std::string iv = absl::HexStringToBytes(test_vectors[i].iv);
+ std::string fixed = absl::HexStringToBytes(test_vectors[i].fixed);
+ std::string aad = absl::HexStringToBytes(test_vectors[i].aad);
+ std::string ct = absl::HexStringToBytes(test_vectors[i].ct);
+
+ ChaCha20Poly1305TlsEncrypter encrypter;
+ ASSERT_TRUE(encrypter.SetKey(key));
+ std::unique_ptr<QuicData> encrypted(EncryptWithNonce(
+ &encrypter, fixed + iv,
+ // This deliberately tests that the encrypter can handle an AAD that
+ // is set to nullptr, as opposed to a zero-length, non-nullptr pointer.
+ absl::string_view(aad.length() ? aad.data() : nullptr, aad.length()),
+ pt));
+ ASSERT_TRUE(encrypted.get());
+ EXPECT_EQ(16u, ct.size() - pt.size());
+ EXPECT_EQ(16u, encrypted->length() - pt.size());
+
+ quiche::test::CompareCharArraysWithHexError("ciphertext", encrypted->data(),
+ encrypted->length(), ct.data(),
+ ct.length());
+ }
+}
+
+TEST_F(ChaCha20Poly1305TlsEncrypterTest, GetMaxPlaintextSize) {
+ ChaCha20Poly1305TlsEncrypter encrypter;
+ EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1016));
+ EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(116));
+ EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(26));
+}
+
+TEST_F(ChaCha20Poly1305TlsEncrypterTest, GetCiphertextSize) {
+ ChaCha20Poly1305TlsEncrypter encrypter;
+ EXPECT_EQ(1016u, encrypter.GetCiphertextSize(1000));
+ EXPECT_EQ(116u, encrypter.GetCiphertextSize(100));
+ EXPECT_EQ(26u, encrypter.GetCiphertextSize(10));
+}
+
+TEST_F(ChaCha20Poly1305TlsEncrypterTest, GenerateHeaderProtectionMask) {
+ ChaCha20Poly1305TlsEncrypter encrypter;
+ std::string key = absl::HexStringToBytes(
+ "6a067f432787bd6034dd3f08f07fc9703a27e58c70e2d88d948b7f6489923cc7");
+ std::string sample =
+ absl::HexStringToBytes("1210d91cceb45c716b023f492c29e612");
+ ASSERT_TRUE(encrypter.SetHeaderProtectionKey(key));
+ std::string mask = encrypter.GenerateHeaderProtectionMask(sample);
+ std::string expected_mask = absl::HexStringToBytes("1cc2cd98dc");
+ quiche::test::CompareCharArraysWithHexError(
+ "header protection mask", mask.data(), mask.size(), expected_mask.data(),
+ expected_mask.size());
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha_base_decrypter.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha_base_decrypter.cc
new file mode 100644
index 00000000000..a90c9eff98e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha_base_decrypter.cc
@@ -0,0 +1,44 @@
+// Copyright (c) 2013 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 "quiche/quic/core/crypto/chacha_base_decrypter.h"
+
+#include <cstdint>
+
+#include "absl/base/macros.h"
+#include "absl/strings/string_view.h"
+#include "openssl/chacha.h"
+#include "quiche/quic/core/quic_data_reader.h"
+#include "quiche/quic/platform/api/quic_bug_tracker.h"
+#include "quiche/common/quiche_endian.h"
+
+namespace quic {
+
+bool ChaChaBaseDecrypter::SetHeaderProtectionKey(absl::string_view key) {
+ if (key.size() != GetKeySize()) {
+ QUIC_BUG(quic_bug_10620_1) << "Invalid key size for header protection";
+ return false;
+ }
+ memcpy(pne_key_, key.data(), key.size());
+ return true;
+}
+
+std::string ChaChaBaseDecrypter::GenerateHeaderProtectionMask(
+ QuicDataReader* sample_reader) {
+ absl::string_view sample;
+ if (!sample_reader->ReadStringPiece(&sample, 16)) {
+ return std::string();
+ }
+ const uint8_t* nonce = reinterpret_cast<const uint8_t*>(sample.data()) + 4;
+ uint32_t counter;
+ QuicDataReader(sample.data(), 4, quiche::HOST_BYTE_ORDER)
+ .ReadUInt32(&counter);
+ const uint8_t zeroes[] = {0, 0, 0, 0, 0};
+ std::string out(ABSL_ARRAYSIZE(zeroes), 0);
+ CRYPTO_chacha_20(reinterpret_cast<uint8_t*>(const_cast<char*>(out.data())),
+ zeroes, ABSL_ARRAYSIZE(zeroes), pne_key_, nonce, counter);
+ return out;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha_base_decrypter.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha_base_decrypter.h
new file mode 100644
index 00000000000..5cd08c74cf6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha_base_decrypter.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2013 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CHACHA_BASE_DECRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CHACHA_BASE_DECRYPTER_H_
+
+#include <cstddef>
+
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/crypto/aead_base_decrypter.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE ChaChaBaseDecrypter : public AeadBaseDecrypter {
+ public:
+ using AeadBaseDecrypter::AeadBaseDecrypter;
+
+ bool SetHeaderProtectionKey(absl::string_view key) override;
+ std::string GenerateHeaderProtectionMask(
+ QuicDataReader* sample_reader) override;
+
+ private:
+ // The key used for packet number encryption.
+ unsigned char pne_key_[kMaxKeySize];
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CHACHA_BASE_DECRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha_base_encrypter.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha_base_encrypter.cc
new file mode 100644
index 00000000000..847345130b8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha_base_encrypter.cc
@@ -0,0 +1,41 @@
+// Copyright (c) 2013 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 "quiche/quic/core/crypto/chacha_base_encrypter.h"
+
+#include "absl/base/macros.h"
+#include "absl/strings/string_view.h"
+#include "openssl/chacha.h"
+#include "quiche/quic/core/quic_data_reader.h"
+#include "quiche/quic/platform/api/quic_bug_tracker.h"
+#include "quiche/common/quiche_endian.h"
+
+namespace quic {
+
+bool ChaChaBaseEncrypter::SetHeaderProtectionKey(absl::string_view key) {
+ if (key.size() != GetKeySize()) {
+ QUIC_BUG(quic_bug_10656_1) << "Invalid key size for header protection";
+ return false;
+ }
+ memcpy(pne_key_, key.data(), key.size());
+ return true;
+}
+
+std::string ChaChaBaseEncrypter::GenerateHeaderProtectionMask(
+ absl::string_view sample) {
+ if (sample.size() != 16) {
+ return std::string();
+ }
+ const uint8_t* nonce = reinterpret_cast<const uint8_t*>(sample.data()) + 4;
+ uint32_t counter;
+ QuicDataReader(sample.data(), 4, quiche::HOST_BYTE_ORDER)
+ .ReadUInt32(&counter);
+ const uint8_t zeroes[] = {0, 0, 0, 0, 0};
+ std::string out(ABSL_ARRAYSIZE(zeroes), 0);
+ CRYPTO_chacha_20(reinterpret_cast<uint8_t*>(const_cast<char*>(out.data())),
+ zeroes, ABSL_ARRAYSIZE(zeroes), pne_key_, nonce, counter);
+ return out;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha_base_encrypter.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha_base_encrypter.h
new file mode 100644
index 00000000000..14773ec1cd7
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/chacha_base_encrypter.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2013 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CHACHA_BASE_ENCRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CHACHA_BASE_ENCRYPTER_H_
+
+#include <cstddef>
+
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/crypto/aead_base_encrypter.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE ChaChaBaseEncrypter : public AeadBaseEncrypter {
+ public:
+ using AeadBaseEncrypter::AeadBaseEncrypter;
+
+ bool SetHeaderProtectionKey(absl::string_view key) override;
+ std::string GenerateHeaderProtectionMask(absl::string_view sample) override;
+
+ private:
+ // The key used for packet number encryption.
+ unsigned char pne_key_[kMaxKeySize];
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CHACHA_BASE_ENCRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/channel_id.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/channel_id.cc
new file mode 100644
index 00000000000..77288dd52ee
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/channel_id.cc
@@ -0,0 +1,90 @@
+// Copyright 2013 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 "quiche/quic/core/crypto/channel_id.h"
+
+#include <cstdint>
+
+#include "absl/strings/string_view.h"
+#include "openssl/bn.h"
+#include "openssl/ec.h"
+#include "openssl/ecdsa.h"
+#include "openssl/nid.h"
+#include "openssl/sha.h"
+
+namespace quic {
+
+// static
+const char ChannelIDVerifier::kContextStr[] = "QUIC ChannelID";
+// static
+const char ChannelIDVerifier::kClientToServerStr[] = "client -> server";
+
+// static
+bool ChannelIDVerifier::Verify(absl::string_view key,
+ absl::string_view signed_data,
+ absl::string_view signature) {
+ return VerifyRaw(key, signed_data, signature, true);
+}
+
+// static
+bool ChannelIDVerifier::VerifyRaw(absl::string_view key,
+ absl::string_view signed_data,
+ absl::string_view signature,
+ bool is_channel_id_signature) {
+ if (key.size() != 32 * 2 || signature.size() != 32 * 2) {
+ return false;
+ }
+
+ bssl::UniquePtr<EC_GROUP> p256(
+ EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+ if (p256.get() == nullptr) {
+ return false;
+ }
+
+ bssl::UniquePtr<BIGNUM> x(BN_new()), y(BN_new()), r(BN_new()), s(BN_new());
+
+ ECDSA_SIG sig;
+ sig.r = r.get();
+ sig.s = s.get();
+
+ const uint8_t* key_bytes = reinterpret_cast<const uint8_t*>(key.data());
+ const uint8_t* signature_bytes =
+ reinterpret_cast<const uint8_t*>(signature.data());
+
+ if (BN_bin2bn(key_bytes + 0, 32, x.get()) == nullptr ||
+ BN_bin2bn(key_bytes + 32, 32, y.get()) == nullptr ||
+ BN_bin2bn(signature_bytes + 0, 32, sig.r) == nullptr ||
+ BN_bin2bn(signature_bytes + 32, 32, sig.s) == nullptr) {
+ return false;
+ }
+
+ bssl::UniquePtr<EC_POINT> point(EC_POINT_new(p256.get()));
+ if (point.get() == nullptr ||
+ !EC_POINT_set_affine_coordinates_GFp(p256.get(), point.get(), x.get(),
+ y.get(), nullptr)) {
+ return false;
+ }
+
+ bssl::UniquePtr<EC_KEY> ecdsa_key(EC_KEY_new());
+ if (ecdsa_key.get() == nullptr ||
+ !EC_KEY_set_group(ecdsa_key.get(), p256.get()) ||
+ !EC_KEY_set_public_key(ecdsa_key.get(), point.get())) {
+ return false;
+ }
+
+ SHA256_CTX sha256;
+ SHA256_Init(&sha256);
+ if (is_channel_id_signature) {
+ SHA256_Update(&sha256, kContextStr, strlen(kContextStr) + 1);
+ SHA256_Update(&sha256, kClientToServerStr, strlen(kClientToServerStr) + 1);
+ }
+ SHA256_Update(&sha256, signed_data.data(), signed_data.size());
+
+ unsigned char digest[SHA256_DIGEST_LENGTH];
+ SHA256_Final(digest, &sha256);
+
+ return ECDSA_do_verify(digest, sizeof(digest), &sig, ecdsa_key.get()) == 1;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/channel_id.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/channel_id.h
new file mode 100644
index 00000000000..2f8a52781e5
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/channel_id.h
@@ -0,0 +1,47 @@
+// Copyright 2013 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CHANNEL_ID_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CHANNEL_ID_H_
+
+#include <memory>
+#include <string>
+
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/quic_types.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// ChannelIDVerifier verifies ChannelID signatures.
+class QUIC_EXPORT_PRIVATE ChannelIDVerifier {
+ public:
+ ChannelIDVerifier() = delete;
+
+ // kContextStr is prepended to the data to be signed in order to ensure that
+ // a ChannelID signature cannot be used in a different context. (The
+ // terminating NUL byte is inclued.)
+ static const char kContextStr[];
+ // kClientToServerStr follows kContextStr to specify that the ChannelID is
+ // being used in the client to server direction. (The terminating NUL byte is
+ // included.)
+ static const char kClientToServerStr[];
+
+ // Verify returns true iff |signature| is a valid signature of |signed_data|
+ // by |key|.
+ static bool Verify(absl::string_view key, absl::string_view signed_data,
+ absl::string_view signature);
+
+ // FOR TESTING ONLY: VerifyRaw returns true iff |signature| is a valid
+ // signature of |signed_data| by |key|. |is_channel_id_signature| indicates
+ // whether |signature| is a ChannelID signature (with kContextStr prepended
+ // to the data to be signed).
+ static bool VerifyRaw(absl::string_view key, absl::string_view signed_data,
+ absl::string_view signature,
+ bool is_channel_id_signature);
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CHANNEL_ID_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/channel_id_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/channel_id_test.cc
new file mode 100644
index 00000000000..ff3d73d159c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/channel_id_test.cc
@@ -0,0 +1,285 @@
+// Copyright 2013 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 "quiche/quic/core/crypto/channel_id.h"
+
+#include <memory>
+#include <string>
+
+#include "absl/strings/string_view.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/crypto_test_utils.h"
+
+namespace quic {
+namespace test {
+
+namespace {
+
+// The following ECDSA signature verification test vectors for P-256,SHA-256
+// come from the SigVer.rsp file in
+// http://csrc.nist.gov/groups/STM/cavp/documents/dss/186-3ecdsatestvectors.zip
+// downloaded on 2013-06-11.
+struct TestVector {
+ // Input:
+ const char* msg;
+ const char* qx;
+ const char* qy;
+ const char* r;
+ const char* s;
+
+ // Expected output:
+ bool result; // true means "P", false means "F"
+};
+
+const TestVector test_vector[] = {
+ {
+ "e4796db5f785f207aa30d311693b3702821dff1168fd2e04c0836825aefd850d"
+ "9aa60326d88cde1a23c7745351392ca2288d632c264f197d05cd424a30336c19"
+ "fd09bb229654f0222fcb881a4b35c290a093ac159ce13409111ff0358411133c"
+ "24f5b8e2090d6db6558afc36f06ca1f6ef779785adba68db27a409859fc4c4a0",
+ "87f8f2b218f49845f6f10eec3877136269f5c1a54736dbdf69f89940cad41555",
+ "e15f369036f49842fac7a86c8a2b0557609776814448b8f5e84aa9f4395205e9",
+ "d19ff48b324915576416097d2544f7cbdf8768b1454ad20e0baac50e211f23b0",
+ "a3e81e59311cdfff2d4784949f7a2cb50ba6c3a91fa54710568e61aca3e847c6",
+ false // F (3 - S changed)
+ },
+ {
+ "069a6e6b93dfee6df6ef6997cd80dd2182c36653cef10c655d524585655462d6"
+ "83877f95ecc6d6c81623d8fac4e900ed0019964094e7de91f1481989ae187300"
+ "4565789cbf5dc56c62aedc63f62f3b894c9c6f7788c8ecaadc9bd0e81ad91b2b"
+ "3569ea12260e93924fdddd3972af5273198f5efda0746219475017557616170e",
+ "5cf02a00d205bdfee2016f7421807fc38ae69e6b7ccd064ee689fc1a94a9f7d2",
+ "ec530ce3cc5c9d1af463f264d685afe2b4db4b5828d7e61b748930f3ce622a85",
+ "dc23d130c6117fb5751201455e99f36f59aba1a6a21cf2d0e7481a97451d6693",
+ "d6ce7708c18dbf35d4f8aa7240922dc6823f2e7058cbc1484fcad1599db5018c",
+ false // F (2 - R changed)
+ },
+ {
+ "df04a346cf4d0e331a6db78cca2d456d31b0a000aa51441defdb97bbeb20b94d"
+ "8d746429a393ba88840d661615e07def615a342abedfa4ce912e562af7149598"
+ "96858af817317a840dcff85a057bb91a3c2bf90105500362754a6dd321cdd861"
+ "28cfc5f04667b57aa78c112411e42da304f1012d48cd6a7052d7de44ebcc01de",
+ "2ddfd145767883ffbb0ac003ab4a44346d08fa2570b3120dcce94562422244cb",
+ "5f70c7d11ac2b7a435ccfbbae02c3df1ea6b532cc0e9db74f93fffca7c6f9a64",
+ "9913111cff6f20c5bf453a99cd2c2019a4e749a49724a08774d14e4c113edda8",
+ "9467cd4cd21ecb56b0cab0a9a453b43386845459127a952421f5c6382866c5cc",
+ false // F (4 - Q changed)
+ },
+ {
+ "e1130af6a38ccb412a9c8d13e15dbfc9e69a16385af3c3f1e5da954fd5e7c45f"
+ "d75e2b8c36699228e92840c0562fbf3772f07e17f1add56588dd45f7450e1217"
+ "ad239922dd9c32695dc71ff2424ca0dec1321aa47064a044b7fe3c2b97d03ce4"
+ "70a592304c5ef21eed9f93da56bb232d1eeb0035f9bf0dfafdcc4606272b20a3",
+ "e424dc61d4bb3cb7ef4344a7f8957a0c5134e16f7a67c074f82e6e12f49abf3c",
+ "970eed7aa2bc48651545949de1dddaf0127e5965ac85d1243d6f60e7dfaee927",
+ "bf96b99aa49c705c910be33142017c642ff540c76349b9dab72f981fd9347f4f",
+ "17c55095819089c2e03b9cd415abdf12444e323075d98f31920b9e0f57ec871c",
+ true // P (0 )
+ },
+ {
+ "73c5f6a67456ae48209b5f85d1e7de7758bf235300c6ae2bdceb1dcb27a7730f"
+ "b68c950b7fcada0ecc4661d3578230f225a875e69aaa17f1e71c6be5c831f226"
+ "63bac63d0c7a9635edb0043ff8c6f26470f02a7bc56556f1437f06dfa27b487a"
+ "6c4290d8bad38d4879b334e341ba092dde4e4ae694a9c09302e2dbf443581c08",
+ "e0fc6a6f50e1c57475673ee54e3a57f9a49f3328e743bf52f335e3eeaa3d2864",
+ "7f59d689c91e463607d9194d99faf316e25432870816dde63f5d4b373f12f22a",
+ "1d75830cd36f4c9aa181b2c4221e87f176b7f05b7c87824e82e396c88315c407",
+ "cb2acb01dac96efc53a32d4a0d85d0c2e48955214783ecf50a4f0414a319c05a",
+ true // P (0 )
+ },
+ {
+ "666036d9b4a2426ed6585a4e0fd931a8761451d29ab04bd7dc6d0c5b9e38e6c2"
+ "b263ff6cb837bd04399de3d757c6c7005f6d7a987063cf6d7e8cb38a4bf0d74a"
+ "282572bd01d0f41e3fd066e3021575f0fa04f27b700d5b7ddddf50965993c3f9"
+ "c7118ed78888da7cb221849b3260592b8e632d7c51e935a0ceae15207bedd548",
+ "a849bef575cac3c6920fbce675c3b787136209f855de19ffe2e8d29b31a5ad86",
+ "bf5fe4f7858f9b805bd8dcc05ad5e7fb889de2f822f3d8b41694e6c55c16b471",
+ "25acc3aa9d9e84c7abf08f73fa4195acc506491d6fc37cb9074528a7db87b9d6",
+ "9b21d5b5259ed3f2ef07dfec6cc90d3a37855d1ce122a85ba6a333f307d31537",
+ false // F (2 - R changed)
+ },
+ {
+ "7e80436bce57339ce8da1b5660149a20240b146d108deef3ec5da4ae256f8f89"
+ "4edcbbc57b34ce37089c0daa17f0c46cd82b5a1599314fd79d2fd2f446bd5a25"
+ "b8e32fcf05b76d644573a6df4ad1dfea707b479d97237a346f1ec632ea5660ef"
+ "b57e8717a8628d7f82af50a4e84b11f21bdff6839196a880ae20b2a0918d58cd",
+ "3dfb6f40f2471b29b77fdccba72d37c21bba019efa40c1c8f91ec405d7dcc5df",
+ "f22f953f1e395a52ead7f3ae3fc47451b438117b1e04d613bc8555b7d6e6d1bb",
+ "548886278e5ec26bed811dbb72db1e154b6f17be70deb1b210107decb1ec2a5a",
+ "e93bfebd2f14f3d827ca32b464be6e69187f5edbd52def4f96599c37d58eee75",
+ false // F (4 - Q changed)
+ },
+ {
+ "1669bfb657fdc62c3ddd63269787fc1c969f1850fb04c933dda063ef74a56ce1"
+ "3e3a649700820f0061efabf849a85d474326c8a541d99830eea8131eaea584f2"
+ "2d88c353965dabcdc4bf6b55949fd529507dfb803ab6b480cd73ca0ba00ca19c"
+ "438849e2cea262a1c57d8f81cd257fb58e19dec7904da97d8386e87b84948169",
+ "69b7667056e1e11d6caf6e45643f8b21e7a4bebda463c7fdbc13bc98efbd0214",
+ "d3f9b12eb46c7c6fda0da3fc85bc1fd831557f9abc902a3be3cb3e8be7d1aa2f",
+ "288f7a1cd391842cce21f00e6f15471c04dc182fe4b14d92dc18910879799790",
+ "247b3c4e89a3bcadfea73c7bfd361def43715fa382b8c3edf4ae15d6e55e9979",
+ false // F (1 - Message changed)
+ },
+ {
+ "3fe60dd9ad6caccf5a6f583b3ae65953563446c4510b70da115ffaa0ba04c076"
+ "115c7043ab8733403cd69c7d14c212c655c07b43a7c71b9a4cffe22c2684788e"
+ "c6870dc2013f269172c822256f9e7cc674791bf2d8486c0f5684283e1649576e"
+ "fc982ede17c7b74b214754d70402fb4bb45ad086cf2cf76b3d63f7fce39ac970",
+ "bf02cbcf6d8cc26e91766d8af0b164fc5968535e84c158eb3bc4e2d79c3cc682",
+ "069ba6cb06b49d60812066afa16ecf7b51352f2c03bd93ec220822b1f3dfba03",
+ "f5acb06c59c2b4927fb852faa07faf4b1852bbb5d06840935e849c4d293d1bad",
+ "049dab79c89cc02f1484c437f523e080a75f134917fda752f2d5ca397addfe5d",
+ false // F (3 - S changed)
+ },
+ {
+ "983a71b9994d95e876d84d28946a041f8f0a3f544cfcc055496580f1dfd4e312"
+ "a2ad418fe69dbc61db230cc0c0ed97e360abab7d6ff4b81ee970a7e97466acfd"
+ "9644f828ffec538abc383d0e92326d1c88c55e1f46a668a039beaa1be631a891"
+ "29938c00a81a3ae46d4aecbf9707f764dbaccea3ef7665e4c4307fa0b0a3075c",
+ "224a4d65b958f6d6afb2904863efd2a734b31798884801fcab5a590f4d6da9de",
+ "178d51fddada62806f097aa615d33b8f2404e6b1479f5fd4859d595734d6d2b9",
+ "87b93ee2fecfda54deb8dff8e426f3c72c8864991f8ec2b3205bb3b416de93d2",
+ "4044a24df85be0cc76f21a4430b75b8e77b932a87f51e4eccbc45c263ebf8f66",
+ false // F (2 - R changed)
+ },
+ {
+ "4a8c071ac4fd0d52faa407b0fe5dab759f7394a5832127f2a3498f34aac28733"
+ "9e043b4ffa79528faf199dc917f7b066ad65505dab0e11e6948515052ce20cfd"
+ "b892ffb8aa9bf3f1aa5be30a5bbe85823bddf70b39fd7ebd4a93a2f75472c1d4"
+ "f606247a9821f1a8c45a6cb80545de2e0c6c0174e2392088c754e9c8443eb5af",
+ "43691c7795a57ead8c5c68536fe934538d46f12889680a9cb6d055a066228369",
+ "f8790110b3c3b281aa1eae037d4f1234aff587d903d93ba3af225c27ddc9ccac",
+ "8acd62e8c262fa50dd9840480969f4ef70f218ebf8ef9584f199031132c6b1ce",
+ "cfca7ed3d4347fb2a29e526b43c348ae1ce6c60d44f3191b6d8ea3a2d9c92154",
+ false // F (3 - S changed)
+ },
+ {
+ "0a3a12c3084c865daf1d302c78215d39bfe0b8bf28272b3c0b74beb4b7409db0"
+ "718239de700785581514321c6440a4bbaea4c76fa47401e151e68cb6c29017f0"
+ "bce4631290af5ea5e2bf3ed742ae110b04ade83a5dbd7358f29a85938e23d87a"
+ "c8233072b79c94670ff0959f9c7f4517862ff829452096c78f5f2e9a7e4e9216",
+ "9157dbfcf8cf385f5bb1568ad5c6e2a8652ba6dfc63bc1753edf5268cb7eb596",
+ "972570f4313d47fc96f7c02d5594d77d46f91e949808825b3d31f029e8296405",
+ "dfaea6f297fa320b707866125c2a7d5d515b51a503bee817de9faa343cc48eeb",
+ "8f780ad713f9c3e5a4f7fa4c519833dfefc6a7432389b1e4af463961f09764f2",
+ false // F (1 - Message changed)
+ },
+ {
+ "785d07a3c54f63dca11f5d1a5f496ee2c2f9288e55007e666c78b007d95cc285"
+ "81dce51f490b30fa73dc9e2d45d075d7e3a95fb8a9e1465ad191904124160b7c"
+ "60fa720ef4ef1c5d2998f40570ae2a870ef3e894c2bc617d8a1dc85c3c557749"
+ "28c38789b4e661349d3f84d2441a3b856a76949b9f1f80bc161648a1cad5588e",
+ "072b10c081a4c1713a294f248aef850e297991aca47fa96a7470abe3b8acfdda",
+ "9581145cca04a0fb94cedce752c8f0370861916d2a94e7c647c5373ce6a4c8f5",
+ "09f5483eccec80f9d104815a1be9cc1a8e5b12b6eb482a65c6907b7480cf4f19",
+ "a4f90e560c5e4eb8696cb276e5165b6a9d486345dedfb094a76e8442d026378d",
+ false // F (4 - Q changed)
+ },
+ {
+ "76f987ec5448dd72219bd30bf6b66b0775c80b394851a43ff1f537f140a6e722"
+ "9ef8cd72ad58b1d2d20298539d6347dd5598812bc65323aceaf05228f738b5ad"
+ "3e8d9fe4100fd767c2f098c77cb99c2992843ba3eed91d32444f3b6db6cd212d"
+ "d4e5609548f4bb62812a920f6e2bf1581be1ebeebdd06ec4e971862cc42055ca",
+ "09308ea5bfad6e5adf408634b3d5ce9240d35442f7fe116452aaec0d25be8c24",
+ "f40c93e023ef494b1c3079b2d10ef67f3170740495ce2cc57f8ee4b0618b8ee5",
+ "5cc8aa7c35743ec0c23dde88dabd5e4fcd0192d2116f6926fef788cddb754e73",
+ "9c9c045ebaa1b828c32f82ace0d18daebf5e156eb7cbfdc1eff4399a8a900ae7",
+ false // F (1 - Message changed)
+ },
+ {
+ "60cd64b2cd2be6c33859b94875120361a24085f3765cb8b2bf11e026fa9d8855"
+ "dbe435acf7882e84f3c7857f96e2baab4d9afe4588e4a82e17a78827bfdb5ddb"
+ "d1c211fbc2e6d884cddd7cb9d90d5bf4a7311b83f352508033812c776a0e00c0"
+ "03c7e0d628e50736c7512df0acfa9f2320bd102229f46495ae6d0857cc452a84",
+ "2d98ea01f754d34bbc3003df5050200abf445ec728556d7ed7d5c54c55552b6d",
+ "9b52672742d637a32add056dfd6d8792f2a33c2e69dafabea09b960bc61e230a",
+ "06108e525f845d0155bf60193222b3219c98e3d49424c2fb2a0987f825c17959",
+ "62b5cdd591e5b507e560167ba8f6f7cda74673eb315680cb89ccbc4eec477dce",
+ true // P (0 )
+ },
+ {nullptr, nullptr, nullptr, nullptr, nullptr, false}};
+
+// Returns true if |ch| is a lowercase hexadecimal digit.
+bool IsHexDigit(char ch) {
+ return ('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'f');
+}
+
+// Converts a lowercase hexadecimal digit to its integer value.
+int HexDigitToInt(char ch) {
+ if ('0' <= ch && ch <= '9') {
+ return ch - '0';
+ }
+ return ch - 'a' + 10;
+}
+
+// |in| is a string consisting of lowercase hexadecimal digits, where
+// every two digits represent one byte. |out| is a buffer of size |max_len|.
+// Converts |in| to bytes and stores the bytes in the |out| buffer. The
+// number of bytes converted is returned in |*out_len|. Returns true on
+// success, false on failure.
+bool DecodeHexString(const char* in, char* out, size_t* out_len,
+ size_t max_len) {
+ if (!in) {
+ *out_len = static_cast<size_t>(-1);
+ return true;
+ }
+ *out_len = 0;
+ while (*in != '\0') {
+ if (!IsHexDigit(*in) || !IsHexDigit(*(in + 1))) {
+ return false;
+ }
+ if (*out_len >= max_len) {
+ return false;
+ }
+ out[*out_len] = HexDigitToInt(*in) * 16 + HexDigitToInt(*(in + 1));
+ (*out_len)++;
+ in += 2;
+ }
+ return true;
+}
+
+} // namespace
+
+class ChannelIDTest : public QuicTest {};
+
+// A known answer test for ChannelIDVerifier.
+TEST_F(ChannelIDTest, VerifyKnownAnswerTest) {
+ char msg[1024];
+ size_t msg_len;
+ char key[64];
+ size_t qx_len;
+ size_t qy_len;
+ char signature[64];
+ size_t r_len;
+ size_t s_len;
+
+ for (size_t i = 0; test_vector[i].msg != nullptr; i++) {
+ SCOPED_TRACE(i);
+ // Decode the test vector.
+ ASSERT_TRUE(
+ DecodeHexString(test_vector[i].msg, msg, &msg_len, sizeof(msg)));
+ ASSERT_TRUE(DecodeHexString(test_vector[i].qx, key, &qx_len, sizeof(key)));
+ ASSERT_TRUE(DecodeHexString(test_vector[i].qy, key + qx_len, &qy_len,
+ sizeof(key) - qx_len));
+ ASSERT_TRUE(DecodeHexString(test_vector[i].r, signature, &r_len,
+ sizeof(signature)));
+ ASSERT_TRUE(DecodeHexString(test_vector[i].s, signature + r_len, &s_len,
+ sizeof(signature) - r_len));
+
+ // The test vector's lengths should look sane.
+ EXPECT_EQ(sizeof(key) / 2, qx_len);
+ EXPECT_EQ(sizeof(key) / 2, qy_len);
+ EXPECT_EQ(sizeof(signature) / 2, r_len);
+ EXPECT_EQ(sizeof(signature) / 2, s_len);
+
+ EXPECT_EQ(test_vector[i].result,
+ ChannelIDVerifier::VerifyRaw(
+ absl::string_view(key, sizeof(key)),
+ absl::string_view(msg, msg_len),
+ absl::string_view(signature, sizeof(signature)), false));
+ }
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/client_proof_source.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/client_proof_source.cc
new file mode 100644
index 00000000000..9d4795ca5d3
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/client_proof_source.cc
@@ -0,0 +1,62 @@
+// Copyright (c) 2021 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 "quiche/quic/core/crypto/client_proof_source.h"
+
+#include "absl/strings/match.h"
+
+namespace quic {
+
+bool DefaultClientProofSource::AddCertAndKey(
+ std::vector<std::string> server_hostnames,
+ quiche::QuicheReferenceCountedPointer<Chain> chain,
+ CertificatePrivateKey private_key) {
+ if (!ValidateCertAndKey(chain, private_key)) {
+ return false;
+ }
+
+ auto cert_and_key =
+ std::make_shared<CertAndKey>(std::move(chain), std::move(private_key));
+ for (const std::string& domain : server_hostnames) {
+ cert_and_keys_[domain] = cert_and_key;
+ }
+ return true;
+}
+
+const ClientProofSource::CertAndKey* DefaultClientProofSource::GetCertAndKey(
+ absl::string_view hostname) const {
+ const CertAndKey* result = LookupExact(hostname);
+ if (result != nullptr || hostname == "*") {
+ return result;
+ }
+
+ // Either a full or a wildcard domain lookup failed. In the former case,
+ // derive the wildcard domain and look it up.
+ if (hostname.size() > 1 && !absl::StartsWith(hostname, "*.")) {
+ auto dot_pos = hostname.find('.');
+ if (dot_pos != std::string::npos) {
+ std::string wildcard = absl::StrCat("*", hostname.substr(dot_pos));
+ const CertAndKey* result = LookupExact(wildcard);
+ if (result != nullptr) {
+ return result;
+ }
+ }
+ }
+
+ // Return default cert, if any.
+ return LookupExact("*");
+}
+
+const ClientProofSource::CertAndKey* DefaultClientProofSource::LookupExact(
+ absl::string_view map_key) const {
+ const auto it = cert_and_keys_.find(map_key);
+ QUIC_DVLOG(1) << "LookupExact(" << map_key
+ << ") found:" << (it != cert_and_keys_.end());
+ if (it != cert_and_keys_.end()) {
+ return it->second.get();
+ }
+ return nullptr;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/client_proof_source.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/client_proof_source.h
new file mode 100644
index 00000000000..d1450f7bedb
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/client_proof_source.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2021 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CLIENT_PROOF_SOURCE_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CLIENT_PROOF_SOURCE_H_
+
+#include <memory>
+
+#include "absl/container/flat_hash_map.h"
+#include "quiche/quic/core/crypto/certificate_view.h"
+#include "quiche/quic/core/crypto/proof_source.h"
+
+namespace quic {
+
+// ClientProofSource is the interface for a QUIC client to provide client certs
+// and keys based on server hostname. It is only used by TLS handshakes.
+class QUIC_EXPORT_PRIVATE ClientProofSource {
+ public:
+ using Chain = ProofSource::Chain;
+
+ virtual ~ClientProofSource() {}
+
+ struct QUIC_EXPORT_PRIVATE CertAndKey {
+ CertAndKey(quiche::QuicheReferenceCountedPointer<Chain> chain,
+ CertificatePrivateKey private_key)
+ : chain(std::move(chain)), private_key(std::move(private_key)) {}
+
+ quiche::QuicheReferenceCountedPointer<Chain> chain;
+ CertificatePrivateKey private_key;
+ };
+
+ // Get the client certificate to be sent to the server with |server_hostname|
+ // and its corresponding private key. It returns nullptr if the cert and key
+ // can not be found.
+ //
+ // |server_hostname| is typically a full domain name(www.foo.com), but it
+ // could also be a wildcard domain(*.foo.com), or a "*" which will return the
+ // default cert.
+ virtual const CertAndKey* GetCertAndKey(
+ absl::string_view server_hostname) const = 0;
+};
+
+// DefaultClientProofSource is an implementation that simply keeps an in memory
+// map of server hostnames to certs.
+class QUIC_EXPORT_PRIVATE DefaultClientProofSource : public ClientProofSource {
+ public:
+ ~DefaultClientProofSource() override {}
+
+ // Associate all hostnames in |server_hostnames| with {|chain|,|private_key|}.
+ // Elements of |server_hostnames| can be full domain names(www.foo.com),
+ // wildcard domains(*.foo.com), or "*" which means the given cert chain is the
+ // default one.
+ // If any element of |server_hostnames| is already associated with a cert
+ // chain, it will be updated to be associated with the new cert chain.
+ bool AddCertAndKey(std::vector<std::string> server_hostnames,
+ quiche::QuicheReferenceCountedPointer<Chain> chain,
+ CertificatePrivateKey private_key);
+
+ // ClientProofSource implementation
+ const CertAndKey* GetCertAndKey(absl::string_view hostname) const override;
+
+ private:
+ const CertAndKey* LookupExact(absl::string_view map_key) const;
+ absl::flat_hash_map<std::string, std::shared_ptr<CertAndKey>> cert_and_keys_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CLIENT_PROOF_SOURCE_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/client_proof_source_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/client_proof_source_test.cc
new file mode 100644
index 00000000000..a35e0aa87a2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/client_proof_source_test.cc
@@ -0,0 +1,215 @@
+// Copyright (c) 2021 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 "quiche/quic/core/crypto/client_proof_source.h"
+
+#include "quiche/quic/platform/api/quic_expect_bug.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/test_certificates.h"
+
+namespace quic {
+namespace test {
+
+quiche::QuicheReferenceCountedPointer<ClientProofSource::Chain>
+TestCertChain() {
+ return quiche::QuicheReferenceCountedPointer<ClientProofSource::Chain>(
+ new ClientProofSource::Chain({std::string(kTestCertificate)}));
+}
+
+CertificatePrivateKey TestPrivateKey() {
+ CBS private_key_cbs;
+ CBS_init(&private_key_cbs,
+ reinterpret_cast<const uint8_t*>(kTestCertificatePrivateKey.data()),
+ kTestCertificatePrivateKey.size());
+
+ return CertificatePrivateKey(
+ bssl::UniquePtr<EVP_PKEY>(EVP_parse_private_key(&private_key_cbs)));
+}
+
+const ClientProofSource::CertAndKey* TestCertAndKey() {
+ static const ClientProofSource::CertAndKey cert_and_key(TestCertChain(),
+ TestPrivateKey());
+ return &cert_and_key;
+}
+
+quiche::QuicheReferenceCountedPointer<ClientProofSource::Chain>
+NullCertChain() {
+ return quiche::QuicheReferenceCountedPointer<ClientProofSource::Chain>();
+}
+
+quiche::QuicheReferenceCountedPointer<ClientProofSource::Chain>
+EmptyCertChain() {
+ return quiche::QuicheReferenceCountedPointer<ClientProofSource::Chain>(
+ new ClientProofSource::Chain(std::vector<std::string>()));
+}
+
+quiche::QuicheReferenceCountedPointer<ClientProofSource::Chain> BadCertChain() {
+ return quiche::QuicheReferenceCountedPointer<ClientProofSource::Chain>(
+ new ClientProofSource::Chain({"This is the content of a bad cert."}));
+}
+
+CertificatePrivateKey EmptyPrivateKey() {
+ return CertificatePrivateKey(bssl::UniquePtr<EVP_PKEY>(EVP_PKEY_new()));
+}
+
+#define VERIFY_CERT_AND_KEY_MATCHES(lhs, rhs) \
+ do { \
+ SCOPED_TRACE(testing::Message()); \
+ VerifyCertAndKeyMatches(lhs, rhs); \
+ } while (0)
+
+void VerifyCertAndKeyMatches(const ClientProofSource::CertAndKey* lhs,
+ const ClientProofSource::CertAndKey* rhs) {
+ if (lhs == rhs) {
+ return;
+ }
+
+ if (lhs == nullptr) {
+ ADD_FAILURE() << "lhs is nullptr, but rhs is not";
+ return;
+ }
+
+ if (rhs == nullptr) {
+ ADD_FAILURE() << "rhs is nullptr, but lhs is not";
+ return;
+ }
+
+ if (1 != EVP_PKEY_cmp(lhs->private_key.private_key(),
+ rhs->private_key.private_key())) {
+ ADD_FAILURE() << "Private keys mismatch";
+ return;
+ }
+
+ const ClientProofSource::Chain* lhs_chain = lhs->chain.get();
+ const ClientProofSource::Chain* rhs_chain = rhs->chain.get();
+
+ if (lhs_chain == rhs_chain) {
+ return;
+ }
+
+ if (lhs_chain == nullptr) {
+ ADD_FAILURE() << "lhs->chain is nullptr, but rhs->chain is not";
+ return;
+ }
+
+ if (rhs_chain == nullptr) {
+ ADD_FAILURE() << "rhs->chain is nullptr, but lhs->chain is not";
+ return;
+ }
+
+ if (lhs_chain->certs.size() != rhs_chain->certs.size()) {
+ ADD_FAILURE() << "Cert chain length differ. lhs:" << lhs_chain->certs.size()
+ << ", rhs:" << rhs_chain->certs.size();
+ return;
+ }
+
+ for (size_t i = 0; i < lhs_chain->certs.size(); ++i) {
+ if (lhs_chain->certs[i] != rhs_chain->certs[i]) {
+ ADD_FAILURE() << "The " << i << "-th certs differ.";
+ return;
+ }
+ }
+
+ // All good.
+}
+
+TEST(DefaultClientProofSource, FullDomain) {
+ DefaultClientProofSource proof_source;
+ ASSERT_TRUE(proof_source.AddCertAndKey({"www.google.com"}, TestCertChain(),
+ TestPrivateKey()));
+ VERIFY_CERT_AND_KEY_MATCHES(proof_source.GetCertAndKey("www.google.com"),
+ TestCertAndKey());
+ EXPECT_EQ(proof_source.GetCertAndKey("*.google.com"), nullptr);
+ EXPECT_EQ(proof_source.GetCertAndKey("*"), nullptr);
+}
+
+TEST(DefaultClientProofSource, WildcardDomain) {
+ DefaultClientProofSource proof_source;
+ ASSERT_TRUE(proof_source.AddCertAndKey({"*.google.com"}, TestCertChain(),
+ TestPrivateKey()));
+ VERIFY_CERT_AND_KEY_MATCHES(proof_source.GetCertAndKey("www.google.com"),
+ TestCertAndKey());
+ VERIFY_CERT_AND_KEY_MATCHES(proof_source.GetCertAndKey("*.google.com"),
+ TestCertAndKey());
+ EXPECT_EQ(proof_source.GetCertAndKey("*"), nullptr);
+}
+
+TEST(DefaultClientProofSource, DefaultDomain) {
+ DefaultClientProofSource proof_source;
+ ASSERT_TRUE(
+ proof_source.AddCertAndKey({"*"}, TestCertChain(), TestPrivateKey()));
+ VERIFY_CERT_AND_KEY_MATCHES(proof_source.GetCertAndKey("www.google.com"),
+ TestCertAndKey());
+ VERIFY_CERT_AND_KEY_MATCHES(proof_source.GetCertAndKey("*.google.com"),
+ TestCertAndKey());
+ VERIFY_CERT_AND_KEY_MATCHES(proof_source.GetCertAndKey("*"),
+ TestCertAndKey());
+}
+
+TEST(DefaultClientProofSource, FullAndWildcard) {
+ DefaultClientProofSource proof_source;
+ ASSERT_TRUE(proof_source.AddCertAndKey({"www.google.com", "*.google.com"},
+ TestCertChain(), TestPrivateKey()));
+ VERIFY_CERT_AND_KEY_MATCHES(proof_source.GetCertAndKey("www.google.com"),
+ TestCertAndKey());
+ VERIFY_CERT_AND_KEY_MATCHES(proof_source.GetCertAndKey("foo.google.com"),
+ TestCertAndKey());
+ EXPECT_EQ(proof_source.GetCertAndKey("www.example.com"), nullptr);
+ EXPECT_EQ(proof_source.GetCertAndKey("*"), nullptr);
+}
+
+TEST(DefaultClientProofSource, FullWildcardAndDefault) {
+ DefaultClientProofSource proof_source;
+ ASSERT_TRUE(
+ proof_source.AddCertAndKey({"www.google.com", "*.google.com", "*"},
+ TestCertChain(), TestPrivateKey()));
+ VERIFY_CERT_AND_KEY_MATCHES(proof_source.GetCertAndKey("www.google.com"),
+ TestCertAndKey());
+ VERIFY_CERT_AND_KEY_MATCHES(proof_source.GetCertAndKey("foo.google.com"),
+ TestCertAndKey());
+ VERIFY_CERT_AND_KEY_MATCHES(proof_source.GetCertAndKey("www.example.com"),
+ TestCertAndKey());
+ VERIFY_CERT_AND_KEY_MATCHES(proof_source.GetCertAndKey("*.google.com"),
+ TestCertAndKey());
+ VERIFY_CERT_AND_KEY_MATCHES(proof_source.GetCertAndKey("*"),
+ TestCertAndKey());
+}
+
+TEST(DefaultClientProofSource, EmptyCerts) {
+ DefaultClientProofSource proof_source;
+ bool ok;
+ EXPECT_QUIC_BUG(
+ ok = proof_source.AddCertAndKey({"*"}, NullCertChain(), TestPrivateKey()),
+ "Certificate chain is empty");
+ ASSERT_FALSE(ok);
+
+ EXPECT_QUIC_BUG(ok = proof_source.AddCertAndKey({"*"}, EmptyCertChain(),
+ TestPrivateKey()),
+ "Certificate chain is empty");
+ ASSERT_FALSE(ok);
+ EXPECT_EQ(proof_source.GetCertAndKey("*"), nullptr);
+}
+
+TEST(DefaultClientProofSource, BadCerts) {
+ DefaultClientProofSource proof_source;
+ bool ok;
+ EXPECT_QUIC_BUG(
+ ok = proof_source.AddCertAndKey({"*"}, BadCertChain(), TestPrivateKey()),
+ "Unabled to parse leaf certificate");
+ ASSERT_FALSE(ok);
+ EXPECT_EQ(proof_source.GetCertAndKey("*"), nullptr);
+}
+
+TEST(DefaultClientProofSource, KeyMismatch) {
+ DefaultClientProofSource proof_source;
+ bool ok;
+ EXPECT_QUIC_BUG(ok = proof_source.AddCertAndKey(
+ {"www.google.com"}, TestCertChain(), EmptyPrivateKey()),
+ "Private key does not match the leaf certificate");
+ ASSERT_FALSE(ok);
+ EXPECT_EQ(proof_source.GetCertAndKey("*"), nullptr);
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_framer.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_framer.cc
new file mode 100644
index 00000000000..57a949ed6d7
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_framer.cc
@@ -0,0 +1,351 @@
+// Copyright (c) 2012 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 "quiche/quic/core/crypto/crypto_framer.h"
+
+#include <string>
+#include <utility>
+
+#include "absl/base/attributes.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/crypto/crypto_protocol.h"
+#include "quiche/quic/core/quic_data_reader.h"
+#include "quiche/quic/core/quic_data_writer.h"
+#include "quiche/quic/core/quic_packets.h"
+#include "quiche/quic/platform/api/quic_logging.h"
+#include "quiche/common/quiche_endian.h"
+
+namespace quic {
+
+namespace {
+
+const size_t kQuicTagSize = sizeof(QuicTag);
+const size_t kCryptoEndOffsetSize = sizeof(uint32_t);
+const size_t kNumEntriesSize = sizeof(uint16_t);
+
+// OneShotVisitor is a framer visitor that records a single handshake message.
+class OneShotVisitor : public CryptoFramerVisitorInterface {
+ public:
+ OneShotVisitor() : error_(false) {}
+
+ void OnError(CryptoFramer* /*framer*/) override { error_ = true; }
+
+ void OnHandshakeMessage(const CryptoHandshakeMessage& message) override {
+ out_ = std::make_unique<CryptoHandshakeMessage>(message);
+ }
+
+ bool error() const { return error_; }
+
+ std::unique_ptr<CryptoHandshakeMessage> release() { return std::move(out_); }
+
+ private:
+ std::unique_ptr<CryptoHandshakeMessage> out_;
+ bool error_;
+};
+
+} // namespace
+
+CryptoFramer::CryptoFramer()
+ : visitor_(nullptr),
+ error_detail_(""),
+ num_entries_(0),
+ values_len_(0),
+ process_truncated_messages_(false) {
+ Clear();
+}
+
+CryptoFramer::~CryptoFramer() {}
+
+// static
+std::unique_ptr<CryptoHandshakeMessage> CryptoFramer::ParseMessage(
+ absl::string_view in) {
+ OneShotVisitor visitor;
+ CryptoFramer framer;
+
+ framer.set_visitor(&visitor);
+ if (!framer.ProcessInput(in) || visitor.error() ||
+ framer.InputBytesRemaining()) {
+ return nullptr;
+ }
+
+ return visitor.release();
+}
+
+QuicErrorCode CryptoFramer::error() const { return error_; }
+
+const std::string& CryptoFramer::error_detail() const { return error_detail_; }
+
+bool CryptoFramer::ProcessInput(absl::string_view input,
+ EncryptionLevel /*level*/) {
+ return ProcessInput(input);
+}
+
+bool CryptoFramer::ProcessInput(absl::string_view input) {
+ QUICHE_DCHECK_EQ(QUIC_NO_ERROR, error_);
+ if (error_ != QUIC_NO_ERROR) {
+ return false;
+ }
+ error_ = Process(input);
+ if (error_ != QUIC_NO_ERROR) {
+ QUICHE_DCHECK(!error_detail_.empty());
+ visitor_->OnError(this);
+ return false;
+ }
+
+ return true;
+}
+
+size_t CryptoFramer::InputBytesRemaining() const { return buffer_.length(); }
+
+bool CryptoFramer::HasTag(QuicTag tag) const {
+ if (state_ != STATE_READING_VALUES) {
+ return false;
+ }
+ for (const auto& it : tags_and_lengths_) {
+ if (it.first == tag) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void CryptoFramer::ForceHandshake() {
+ QuicDataReader reader(buffer_.data(), buffer_.length(),
+ quiche::HOST_BYTE_ORDER);
+ for (const std::pair<QuicTag, size_t>& item : tags_and_lengths_) {
+ absl::string_view value;
+ if (reader.BytesRemaining() < item.second) {
+ break;
+ }
+ reader.ReadStringPiece(&value, item.second);
+ message_.SetStringPiece(item.first, value);
+ }
+ visitor_->OnHandshakeMessage(message_);
+}
+
+// static
+std::unique_ptr<QuicData> CryptoFramer::ConstructHandshakeMessage(
+ const CryptoHandshakeMessage& message) {
+ size_t num_entries = message.tag_value_map().size();
+ size_t pad_length = 0;
+ bool need_pad_tag = false;
+ bool need_pad_value = false;
+
+ size_t len = message.size();
+ if (len < message.minimum_size()) {
+ need_pad_tag = true;
+ need_pad_value = true;
+ num_entries++;
+
+ size_t delta = message.minimum_size() - len;
+ const size_t overhead = kQuicTagSize + kCryptoEndOffsetSize;
+ if (delta > overhead) {
+ pad_length = delta - overhead;
+ }
+ len += overhead + pad_length;
+ }
+
+ if (num_entries > kMaxEntries) {
+ return nullptr;
+ }
+
+ std::unique_ptr<char[]> buffer(new char[len]);
+ QuicDataWriter writer(len, buffer.get(), quiche::HOST_BYTE_ORDER);
+ if (!writer.WriteTag(message.tag())) {
+ QUICHE_DCHECK(false) << "Failed to write message tag.";
+ return nullptr;
+ }
+ if (!writer.WriteUInt16(static_cast<uint16_t>(num_entries))) {
+ QUICHE_DCHECK(false) << "Failed to write size.";
+ return nullptr;
+ }
+ if (!writer.WriteUInt16(0)) {
+ QUICHE_DCHECK(false) << "Failed to write padding.";
+ return nullptr;
+ }
+
+ uint32_t end_offset = 0;
+ // Tags and offsets
+ for (auto it = message.tag_value_map().begin();
+ it != message.tag_value_map().end(); ++it) {
+ if (it->first == kPAD && need_pad_tag) {
+ // Existing PAD tags are only checked when padding needs to be added
+ // because parts of the code may need to reserialize received messages
+ // and those messages may, legitimately include padding.
+ QUICHE_DCHECK(false)
+ << "Message needed padding but already contained a PAD tag";
+ return nullptr;
+ }
+
+ if (it->first > kPAD && need_pad_tag) {
+ need_pad_tag = false;
+ if (!WritePadTag(&writer, pad_length, &end_offset)) {
+ return nullptr;
+ }
+ }
+
+ if (!writer.WriteTag(it->first)) {
+ QUICHE_DCHECK(false) << "Failed to write tag.";
+ return nullptr;
+ }
+ end_offset += it->second.length();
+ if (!writer.WriteUInt32(end_offset)) {
+ QUICHE_DCHECK(false) << "Failed to write end offset.";
+ return nullptr;
+ }
+ }
+
+ if (need_pad_tag) {
+ if (!WritePadTag(&writer, pad_length, &end_offset)) {
+ return nullptr;
+ }
+ }
+
+ // Values
+ for (auto it = message.tag_value_map().begin();
+ it != message.tag_value_map().end(); ++it) {
+ if (it->first > kPAD && need_pad_value) {
+ need_pad_value = false;
+ if (!writer.WriteRepeatedByte('-', pad_length)) {
+ QUICHE_DCHECK(false) << "Failed to write padding.";
+ return nullptr;
+ }
+ }
+
+ if (!writer.WriteBytes(it->second.data(), it->second.length())) {
+ QUICHE_DCHECK(false) << "Failed to write value.";
+ return nullptr;
+ }
+ }
+
+ if (need_pad_value) {
+ if (!writer.WriteRepeatedByte('-', pad_length)) {
+ QUICHE_DCHECK(false) << "Failed to write padding.";
+ return nullptr;
+ }
+ }
+
+ return std::make_unique<QuicData>(buffer.release(), len, true);
+}
+
+void CryptoFramer::Clear() {
+ message_.Clear();
+ tags_and_lengths_.clear();
+ error_ = QUIC_NO_ERROR;
+ error_detail_ = "";
+ state_ = STATE_READING_TAG;
+}
+
+QuicErrorCode CryptoFramer::Process(absl::string_view input) {
+ // Add this data to the buffer.
+ buffer_.append(input.data(), input.length());
+ QuicDataReader reader(buffer_.data(), buffer_.length(),
+ quiche::HOST_BYTE_ORDER);
+
+ switch (state_) {
+ case STATE_READING_TAG:
+ if (reader.BytesRemaining() < kQuicTagSize) {
+ break;
+ }
+ QuicTag message_tag;
+ reader.ReadTag(&message_tag);
+ message_.set_tag(message_tag);
+ state_ = STATE_READING_NUM_ENTRIES;
+ ABSL_FALLTHROUGH_INTENDED;
+ case STATE_READING_NUM_ENTRIES:
+ if (reader.BytesRemaining() < kNumEntriesSize + sizeof(uint16_t)) {
+ break;
+ }
+ reader.ReadUInt16(&num_entries_);
+ if (num_entries_ > kMaxEntries) {
+ error_detail_ = absl::StrCat(num_entries_, " entries");
+ return QUIC_CRYPTO_TOO_MANY_ENTRIES;
+ }
+ uint16_t padding;
+ reader.ReadUInt16(&padding);
+
+ tags_and_lengths_.reserve(num_entries_);
+ state_ = STATE_READING_TAGS_AND_LENGTHS;
+ values_len_ = 0;
+ ABSL_FALLTHROUGH_INTENDED;
+ case STATE_READING_TAGS_AND_LENGTHS: {
+ if (reader.BytesRemaining() <
+ num_entries_ * (kQuicTagSize + kCryptoEndOffsetSize)) {
+ break;
+ }
+
+ uint32_t last_end_offset = 0;
+ for (unsigned i = 0; i < num_entries_; ++i) {
+ QuicTag tag;
+ reader.ReadTag(&tag);
+ if (i > 0 && tag <= tags_and_lengths_[i - 1].first) {
+ if (tag == tags_and_lengths_[i - 1].first) {
+ error_detail_ = absl::StrCat("Duplicate tag:", tag);
+ return QUIC_CRYPTO_DUPLICATE_TAG;
+ }
+ error_detail_ = absl::StrCat("Tag ", tag, " out of order");
+ return QUIC_CRYPTO_TAGS_OUT_OF_ORDER;
+ }
+
+ uint32_t end_offset;
+ reader.ReadUInt32(&end_offset);
+
+ if (end_offset < last_end_offset) {
+ error_detail_ =
+ absl::StrCat("End offset: ", end_offset, " vs ", last_end_offset);
+ return QUIC_CRYPTO_TAGS_OUT_OF_ORDER;
+ }
+ tags_and_lengths_.push_back(std::make_pair(
+ tag, static_cast<size_t>(end_offset - last_end_offset)));
+ last_end_offset = end_offset;
+ }
+ values_len_ = last_end_offset;
+ state_ = STATE_READING_VALUES;
+ ABSL_FALLTHROUGH_INTENDED;
+ }
+ case STATE_READING_VALUES:
+ if (reader.BytesRemaining() < values_len_) {
+ if (!process_truncated_messages_) {
+ break;
+ }
+ QUIC_LOG(ERROR) << "Trunacted message. Missing "
+ << values_len_ - reader.BytesRemaining() << " bytes.";
+ }
+ for (const std::pair<QuicTag, size_t>& item : tags_and_lengths_) {
+ absl::string_view value;
+ if (!reader.ReadStringPiece(&value, item.second)) {
+ QUICHE_DCHECK(process_truncated_messages_);
+ // Store an empty value.
+ message_.SetStringPiece(item.first, "");
+ continue;
+ }
+ message_.SetStringPiece(item.first, value);
+ }
+ visitor_->OnHandshakeMessage(message_);
+ Clear();
+ state_ = STATE_READING_TAG;
+ break;
+ }
+ // Save any remaining data.
+ buffer_ = std::string(reader.PeekRemainingPayload());
+ return QUIC_NO_ERROR;
+}
+
+// static
+bool CryptoFramer::WritePadTag(QuicDataWriter* writer, size_t pad_length,
+ uint32_t* end_offset) {
+ if (!writer->WriteTag(kPAD)) {
+ QUICHE_DCHECK(false) << "Failed to write tag.";
+ return false;
+ }
+ *end_offset += pad_length;
+ if (!writer->WriteUInt32(*end_offset)) {
+ QUICHE_DCHECK(false) << "Failed to write end offset.";
+ return false;
+ }
+ return true;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_framer.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_framer.h
new file mode 100644
index 00000000000..8d20bdcdae3
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_framer.h
@@ -0,0 +1,136 @@
+// Copyright (c) 2012 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CRYPTO_FRAMER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CRYPTO_FRAMER_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/crypto/crypto_handshake_message.h"
+#include "quiche/quic/core/crypto/crypto_message_parser.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class CryptoFramer;
+class QuicData;
+class QuicDataWriter;
+
+class QUIC_EXPORT_PRIVATE CryptoFramerVisitorInterface {
+ public:
+ virtual ~CryptoFramerVisitorInterface() {}
+
+ // Called if an error is detected.
+ virtual void OnError(CryptoFramer* framer) = 0;
+
+ // Called when a complete handshake message has been parsed.
+ virtual void OnHandshakeMessage(const CryptoHandshakeMessage& message) = 0;
+};
+
+// A class for framing the crypto messages that are exchanged in a QUIC
+// session.
+class QUIC_EXPORT_PRIVATE CryptoFramer : public CryptoMessageParser {
+ public:
+ CryptoFramer();
+
+ ~CryptoFramer() override;
+
+ // ParseMessage parses exactly one message from the given
+ // absl::string_view. If there is an error, the message is truncated,
+ // or the message has trailing garbage then nullptr will be returned.
+ static std::unique_ptr<CryptoHandshakeMessage> ParseMessage(
+ absl::string_view in);
+
+ // Set callbacks to be called from the framer. A visitor must be set, or
+ // else the framer will crash. It is acceptable for the visitor to do
+ // nothing. If this is called multiple times, only the last visitor
+ // will be used. |visitor| will be owned by the framer.
+ void set_visitor(CryptoFramerVisitorInterface* visitor) {
+ visitor_ = visitor;
+ }
+
+ QuicErrorCode error() const override;
+ const std::string& error_detail() const override;
+
+ // Processes input data, which must be delivered in order. Returns
+ // false if there was an error, and true otherwise. ProcessInput optionally
+ // takes an EncryptionLevel, but it is ignored. The variant with the
+ // EncryptionLevel is provided to match the CryptoMessageParser interface.
+ bool ProcessInput(absl::string_view input, EncryptionLevel level) override;
+ bool ProcessInput(absl::string_view input);
+
+ // Returns the number of bytes of buffered input data remaining to be
+ // parsed.
+ size_t InputBytesRemaining() const override;
+
+ // Checks if the specified tag has been seen. Returns |true| if it
+ // has, and |false| if it has not or a CHLO has not been seen.
+ bool HasTag(QuicTag tag) const;
+
+ // Even if the CHLO has not been fully received, force processing of
+ // the handshake message. This is dangerous and should not be used
+ // except as a mechanism of last resort.
+ void ForceHandshake();
+
+ // Returns a new QuicData owned by the caller that contains a serialized
+ // |message|, or nullptr if there was an error.
+ static std::unique_ptr<QuicData> ConstructHandshakeMessage(
+ const CryptoHandshakeMessage& message);
+
+ // Debug only method which permits processing truncated messages.
+ void set_process_truncated_messages(bool process_truncated_messages) {
+ process_truncated_messages_ = process_truncated_messages;
+ }
+
+ private:
+ // Clears per-message state. Does not clear the visitor.
+ void Clear();
+
+ // Process does does the work of |ProcessInput|, but returns an error code,
+ // doesn't set error_ and doesn't call |visitor_->OnError()|.
+ QuicErrorCode Process(absl::string_view input);
+
+ static bool WritePadTag(QuicDataWriter* writer, size_t pad_length,
+ uint32_t* end_offset);
+
+ // Represents the current state of the parsing state machine.
+ enum CryptoFramerState {
+ STATE_READING_TAG,
+ STATE_READING_NUM_ENTRIES,
+ STATE_READING_TAGS_AND_LENGTHS,
+ STATE_READING_VALUES
+ };
+
+ // Visitor to invoke when messages are parsed.
+ CryptoFramerVisitorInterface* visitor_;
+ // Last error.
+ QuicErrorCode error_;
+ // Remaining unparsed data.
+ std::string buffer_;
+ // Current state of the parsing.
+ CryptoFramerState state_;
+ // The message currently being parsed.
+ CryptoHandshakeMessage message_;
+ // The issue which caused |error_|
+ std::string error_detail_;
+ // Number of entires in the message currently being parsed.
+ uint16_t num_entries_;
+ // tags_and_lengths_ contains the tags that are currently being parsed and
+ // their lengths.
+ std::vector<std::pair<QuicTag, size_t>> tags_and_lengths_;
+ // Cumulative length of all values in the message currently being parsed.
+ size_t values_len_;
+ // Set to true to allow of processing of truncated messages for debugging.
+ bool process_truncated_messages_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CRYPTO_FRAMER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_framer_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_framer_test.cc
new file mode 100644
index 00000000000..5f79f640bca
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_framer_test.cc
@@ -0,0 +1,442 @@
+// Copyright (c) 2012 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 "quiche/quic/core/crypto/crypto_framer.h"
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "absl/base/macros.h"
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/crypto/crypto_handshake.h"
+#include "quiche/quic/core/crypto/crypto_protocol.h"
+#include "quiche/quic/core/quic_packets.h"
+#include "quiche/quic/platform/api/quic_logging.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/crypto_test_utils.h"
+#include "quiche/quic/test_tools/quic_test_utils.h"
+#include "quiche/common/test_tools/quiche_test_utils.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+char* AsChars(unsigned char* data) { return reinterpret_cast<char*>(data); }
+
+class TestCryptoVisitor : public CryptoFramerVisitorInterface {
+ public:
+ TestCryptoVisitor() : error_count_(0) {}
+
+ void OnError(CryptoFramer* framer) override {
+ QUIC_DLOG(ERROR) << "CryptoFramer Error: " << framer->error();
+ ++error_count_;
+ }
+
+ void OnHandshakeMessage(const CryptoHandshakeMessage& message) override {
+ messages_.push_back(message);
+ }
+
+ // Counters from the visitor callbacks.
+ int error_count_;
+
+ std::vector<CryptoHandshakeMessage> messages_;
+};
+
+TEST(CryptoFramerTest, ConstructHandshakeMessage) {
+ CryptoHandshakeMessage message;
+ message.set_tag(0xFFAA7733);
+ message.SetStringPiece(0x12345678, "abcdef");
+ message.SetStringPiece(0x12345679, "ghijk");
+ message.SetStringPiece(0x1234567A, "lmnopqr");
+
+ unsigned char packet[] = {// tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x03, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x78, 0x56, 0x34, 0x12,
+ // end offset 1
+ 0x06, 0x00, 0x00, 0x00,
+ // tag 2
+ 0x79, 0x56, 0x34, 0x12,
+ // end offset 2
+ 0x0b, 0x00, 0x00, 0x00,
+ // tag 3
+ 0x7A, 0x56, 0x34, 0x12,
+ // end offset 3
+ 0x12, 0x00, 0x00, 0x00,
+ // value 1
+ 'a', 'b', 'c', 'd', 'e', 'f',
+ // value 2
+ 'g', 'h', 'i', 'j', 'k',
+ // value 3
+ 'l', 'm', 'n', 'o', 'p', 'q', 'r'};
+
+ CryptoFramer framer;
+ std::unique_ptr<QuicData> data = framer.ConstructHandshakeMessage(message);
+ ASSERT_TRUE(data != nullptr);
+ quiche::test::CompareCharArraysWithHexError(
+ "constructed packet", data->data(), data->length(), AsChars(packet),
+ ABSL_ARRAYSIZE(packet));
+}
+
+TEST(CryptoFramerTest, ConstructHandshakeMessageWithTwoKeys) {
+ CryptoHandshakeMessage message;
+ message.set_tag(0xFFAA7733);
+ message.SetStringPiece(0x12345678, "abcdef");
+ message.SetStringPiece(0x12345679, "ghijk");
+
+ unsigned char packet[] = {// tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x02, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x78, 0x56, 0x34, 0x12,
+ // end offset 1
+ 0x06, 0x00, 0x00, 0x00,
+ // tag 2
+ 0x79, 0x56, 0x34, 0x12,
+ // end offset 2
+ 0x0b, 0x00, 0x00, 0x00,
+ // value 1
+ 'a', 'b', 'c', 'd', 'e', 'f',
+ // value 2
+ 'g', 'h', 'i', 'j', 'k'};
+
+ CryptoFramer framer;
+ std::unique_ptr<QuicData> data = framer.ConstructHandshakeMessage(message);
+ ASSERT_TRUE(data != nullptr);
+
+ quiche::test::CompareCharArraysWithHexError(
+ "constructed packet", data->data(), data->length(), AsChars(packet),
+ ABSL_ARRAYSIZE(packet));
+}
+
+TEST(CryptoFramerTest, ConstructHandshakeMessageZeroLength) {
+ CryptoHandshakeMessage message;
+ message.set_tag(0xFFAA7733);
+ message.SetStringPiece(0x12345678, "");
+
+ unsigned char packet[] = {// tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x01, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x78, 0x56, 0x34, 0x12,
+ // end offset 1
+ 0x00, 0x00, 0x00, 0x00};
+
+ CryptoFramer framer;
+ std::unique_ptr<QuicData> data = framer.ConstructHandshakeMessage(message);
+ ASSERT_TRUE(data != nullptr);
+
+ quiche::test::CompareCharArraysWithHexError(
+ "constructed packet", data->data(), data->length(), AsChars(packet),
+ ABSL_ARRAYSIZE(packet));
+}
+
+TEST(CryptoFramerTest, ConstructHandshakeMessageTooManyEntries) {
+ CryptoHandshakeMessage message;
+ message.set_tag(0xFFAA7733);
+ for (uint32_t key = 1; key <= kMaxEntries + 1; ++key) {
+ message.SetStringPiece(key, "abcdef");
+ }
+
+ CryptoFramer framer;
+ std::unique_ptr<QuicData> data = framer.ConstructHandshakeMessage(message);
+ EXPECT_TRUE(data == nullptr);
+}
+
+TEST(CryptoFramerTest, ConstructHandshakeMessageMinimumSize) {
+ CryptoHandshakeMessage message;
+ message.set_tag(0xFFAA7733);
+ message.SetStringPiece(0x01020304, "test");
+ message.set_minimum_size(64);
+
+ unsigned char packet[] = {// tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x02, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 'P', 'A', 'D', 0,
+ // end offset 1
+ 0x24, 0x00, 0x00, 0x00,
+ // tag 2
+ 0x04, 0x03, 0x02, 0x01,
+ // end offset 2
+ 0x28, 0x00, 0x00, 0x00,
+ // 36 bytes of padding.
+ '-', '-', '-', '-', '-', '-', '-', '-', '-', '-',
+ '-', '-', '-', '-', '-', '-', '-', '-', '-', '-',
+ '-', '-', '-', '-', '-', '-', '-', '-', '-', '-',
+ '-', '-', '-', '-', '-', '-',
+ // value 2
+ 't', 'e', 's', 't'};
+
+ CryptoFramer framer;
+ std::unique_ptr<QuicData> data = framer.ConstructHandshakeMessage(message);
+ ASSERT_TRUE(data != nullptr);
+
+ quiche::test::CompareCharArraysWithHexError(
+ "constructed packet", data->data(), data->length(), AsChars(packet),
+ ABSL_ARRAYSIZE(packet));
+}
+
+TEST(CryptoFramerTest, ConstructHandshakeMessageMinimumSizePadLast) {
+ CryptoHandshakeMessage message;
+ message.set_tag(0xFFAA7733);
+ message.SetStringPiece(1, "");
+ message.set_minimum_size(64);
+
+ unsigned char packet[] = {// tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x02, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x01, 0x00, 0x00, 0x00,
+ // end offset 1
+ 0x00, 0x00, 0x00, 0x00,
+ // tag 2
+ 'P', 'A', 'D', 0,
+ // end offset 2
+ 0x28, 0x00, 0x00, 0x00,
+ // 40 bytes of padding.
+ '-', '-', '-', '-', '-', '-', '-', '-', '-', '-',
+ '-', '-', '-', '-', '-', '-', '-', '-', '-', '-',
+ '-', '-', '-', '-', '-', '-', '-', '-', '-', '-',
+ '-', '-', '-', '-', '-', '-', '-', '-', '-', '-'};
+
+ CryptoFramer framer;
+ std::unique_ptr<QuicData> data = framer.ConstructHandshakeMessage(message);
+ ASSERT_TRUE(data != nullptr);
+
+ quiche::test::CompareCharArraysWithHexError(
+ "constructed packet", data->data(), data->length(), AsChars(packet),
+ ABSL_ARRAYSIZE(packet));
+}
+
+TEST(CryptoFramerTest, ProcessInput) {
+ test::TestCryptoVisitor visitor;
+ CryptoFramer framer;
+ framer.set_visitor(&visitor);
+
+ unsigned char input[] = {// tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x02, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x78, 0x56, 0x34, 0x12,
+ // end offset 1
+ 0x06, 0x00, 0x00, 0x00,
+ // tag 2
+ 0x79, 0x56, 0x34, 0x12,
+ // end offset 2
+ 0x0b, 0x00, 0x00, 0x00,
+ // value 1
+ 'a', 'b', 'c', 'd', 'e', 'f',
+ // value 2
+ 'g', 'h', 'i', 'j', 'k'};
+
+ EXPECT_TRUE(framer.ProcessInput(
+ absl::string_view(AsChars(input), ABSL_ARRAYSIZE(input))));
+ EXPECT_EQ(0u, framer.InputBytesRemaining());
+ EXPECT_EQ(0, visitor.error_count_);
+ ASSERT_EQ(1u, visitor.messages_.size());
+ const CryptoHandshakeMessage& message = visitor.messages_[0];
+ EXPECT_EQ(0xFFAA7733, message.tag());
+ EXPECT_EQ(2u, message.tag_value_map().size());
+ EXPECT_EQ("abcdef", crypto_test_utils::GetValueForTag(message, 0x12345678));
+ EXPECT_EQ("ghijk", crypto_test_utils::GetValueForTag(message, 0x12345679));
+}
+
+TEST(CryptoFramerTest, ProcessInputWithThreeKeys) {
+ test::TestCryptoVisitor visitor;
+ CryptoFramer framer;
+ framer.set_visitor(&visitor);
+
+ unsigned char input[] = {// tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x03, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x78, 0x56, 0x34, 0x12,
+ // end offset 1
+ 0x06, 0x00, 0x00, 0x00,
+ // tag 2
+ 0x79, 0x56, 0x34, 0x12,
+ // end offset 2
+ 0x0b, 0x00, 0x00, 0x00,
+ // tag 3
+ 0x7A, 0x56, 0x34, 0x12,
+ // end offset 3
+ 0x12, 0x00, 0x00, 0x00,
+ // value 1
+ 'a', 'b', 'c', 'd', 'e', 'f',
+ // value 2
+ 'g', 'h', 'i', 'j', 'k',
+ // value 3
+ 'l', 'm', 'n', 'o', 'p', 'q', 'r'};
+
+ EXPECT_TRUE(framer.ProcessInput(
+ absl::string_view(AsChars(input), ABSL_ARRAYSIZE(input))));
+ EXPECT_EQ(0u, framer.InputBytesRemaining());
+ EXPECT_EQ(0, visitor.error_count_);
+ ASSERT_EQ(1u, visitor.messages_.size());
+ const CryptoHandshakeMessage& message = visitor.messages_[0];
+ EXPECT_EQ(0xFFAA7733, message.tag());
+ EXPECT_EQ(3u, message.tag_value_map().size());
+ EXPECT_EQ("abcdef", crypto_test_utils::GetValueForTag(message, 0x12345678));
+ EXPECT_EQ("ghijk", crypto_test_utils::GetValueForTag(message, 0x12345679));
+ EXPECT_EQ("lmnopqr", crypto_test_utils::GetValueForTag(message, 0x1234567A));
+}
+
+TEST(CryptoFramerTest, ProcessInputIncrementally) {
+ test::TestCryptoVisitor visitor;
+ CryptoFramer framer;
+ framer.set_visitor(&visitor);
+
+ unsigned char input[] = {// tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x02, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x78, 0x56, 0x34, 0x12,
+ // end offset 1
+ 0x06, 0x00, 0x00, 0x00,
+ // tag 2
+ 0x79, 0x56, 0x34, 0x12,
+ // end offset 2
+ 0x0b, 0x00, 0x00, 0x00,
+ // value 1
+ 'a', 'b', 'c', 'd', 'e', 'f',
+ // value 2
+ 'g', 'h', 'i', 'j', 'k'};
+
+ for (size_t i = 0; i < ABSL_ARRAYSIZE(input); i++) {
+ EXPECT_TRUE(framer.ProcessInput(absl::string_view(AsChars(input) + i, 1)));
+ }
+ EXPECT_EQ(0u, framer.InputBytesRemaining());
+ ASSERT_EQ(1u, visitor.messages_.size());
+ const CryptoHandshakeMessage& message = visitor.messages_[0];
+ EXPECT_EQ(0xFFAA7733, message.tag());
+ EXPECT_EQ(2u, message.tag_value_map().size());
+ EXPECT_EQ("abcdef", crypto_test_utils::GetValueForTag(message, 0x12345678));
+ EXPECT_EQ("ghijk", crypto_test_utils::GetValueForTag(message, 0x12345679));
+}
+
+TEST(CryptoFramerTest, ProcessInputTagsOutOfOrder) {
+ test::TestCryptoVisitor visitor;
+ CryptoFramer framer;
+ framer.set_visitor(&visitor);
+
+ unsigned char input[] = {// tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x02, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x78, 0x56, 0x34, 0x13,
+ // end offset 1
+ 0x01, 0x00, 0x00, 0x00,
+ // tag 2
+ 0x79, 0x56, 0x34, 0x12,
+ // end offset 2
+ 0x02, 0x00, 0x00, 0x00};
+
+ EXPECT_FALSE(framer.ProcessInput(
+ absl::string_view(AsChars(input), ABSL_ARRAYSIZE(input))));
+ EXPECT_THAT(framer.error(), IsError(QUIC_CRYPTO_TAGS_OUT_OF_ORDER));
+ EXPECT_EQ(1, visitor.error_count_);
+}
+
+TEST(CryptoFramerTest, ProcessEndOffsetsOutOfOrder) {
+ test::TestCryptoVisitor visitor;
+ CryptoFramer framer;
+ framer.set_visitor(&visitor);
+
+ unsigned char input[] = {// tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x02, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x79, 0x56, 0x34, 0x12,
+ // end offset 1
+ 0x01, 0x00, 0x00, 0x00,
+ // tag 2
+ 0x78, 0x56, 0x34, 0x13,
+ // end offset 2
+ 0x00, 0x00, 0x00, 0x00};
+
+ EXPECT_FALSE(framer.ProcessInput(
+ absl::string_view(AsChars(input), ABSL_ARRAYSIZE(input))));
+ EXPECT_THAT(framer.error(), IsError(QUIC_CRYPTO_TAGS_OUT_OF_ORDER));
+ EXPECT_EQ(1, visitor.error_count_);
+}
+
+TEST(CryptoFramerTest, ProcessInputTooManyEntries) {
+ test::TestCryptoVisitor visitor;
+ CryptoFramer framer;
+ framer.set_visitor(&visitor);
+
+ unsigned char input[] = {// tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0xA0, 0x00,
+ // padding
+ 0x00, 0x00};
+
+ EXPECT_FALSE(framer.ProcessInput(
+ absl::string_view(AsChars(input), ABSL_ARRAYSIZE(input))));
+ EXPECT_THAT(framer.error(), IsError(QUIC_CRYPTO_TOO_MANY_ENTRIES));
+ EXPECT_EQ(1, visitor.error_count_);
+}
+
+TEST(CryptoFramerTest, ProcessInputZeroLength) {
+ test::TestCryptoVisitor visitor;
+ CryptoFramer framer;
+ framer.set_visitor(&visitor);
+
+ unsigned char input[] = {// tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x02, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x78, 0x56, 0x34, 0x12,
+ // end offset 1
+ 0x00, 0x00, 0x00, 0x00,
+ // tag 2
+ 0x79, 0x56, 0x34, 0x12,
+ // end offset 2
+ 0x05, 0x00, 0x00, 0x00};
+
+ EXPECT_TRUE(framer.ProcessInput(
+ absl::string_view(AsChars(input), ABSL_ARRAYSIZE(input))));
+ EXPECT_EQ(0, visitor.error_count_);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_handshake.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_handshake.cc
new file mode 100644
index 00000000000..bc17a97de51
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_handshake.cc
@@ -0,0 +1,39 @@
+// Copyright (c) 2013 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 "quiche/quic/core/crypto/crypto_handshake.h"
+
+#include "quiche/quic/core/crypto/key_exchange.h"
+#include "quiche/quic/core/crypto/quic_decrypter.h"
+#include "quiche/quic/core/crypto/quic_encrypter.h"
+
+namespace quic {
+
+QuicCryptoNegotiatedParameters::QuicCryptoNegotiatedParameters()
+ : key_exchange(0),
+ aead(0),
+ token_binding_key_param(0),
+ sct_supported_by_client(false) {}
+
+QuicCryptoNegotiatedParameters::~QuicCryptoNegotiatedParameters() {}
+
+CrypterPair::CrypterPair() {}
+
+CrypterPair::~CrypterPair() {}
+
+// static
+const char QuicCryptoConfig::kInitialLabel[] = "QUIC key expansion";
+
+// static
+const char QuicCryptoConfig::kCETVLabel[] = "QUIC CETV block";
+
+// static
+const char QuicCryptoConfig::kForwardSecureLabel[] =
+ "QUIC forward secure key expansion";
+
+QuicCryptoConfig::QuicCryptoConfig() = default;
+
+QuicCryptoConfig::~QuicCryptoConfig() = default;
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_handshake.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_handshake.h
new file mode 100644
index 00000000000..d44f65eaf0d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_handshake.h
@@ -0,0 +1,189 @@
+// Copyright (c) 2013 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CRYPTO_HANDSHAKE_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CRYPTO_HANDSHAKE_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "quiche/quic/core/quic_packets.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class SynchronousKeyExchange;
+class QuicDecrypter;
+class QuicEncrypter;
+
+// HandshakeFailureReason enum values are uploaded to UMA, they cannot be
+// changed.
+enum HandshakeFailureReason {
+ HANDSHAKE_OK = 0,
+
+ // Failure reasons for an invalid client nonce in CHLO.
+ //
+ // The default error value for nonce verification failures from strike
+ // register (covers old strike registers and unknown failures).
+ CLIENT_NONCE_UNKNOWN_FAILURE = 1,
+ // Client nonce had incorrect length.
+ CLIENT_NONCE_INVALID_FAILURE = 2,
+ // Client nonce is not unique.
+ CLIENT_NONCE_NOT_UNIQUE_FAILURE = 3,
+ // Client orbit is invalid or incorrect.
+ CLIENT_NONCE_INVALID_ORBIT_FAILURE = 4,
+ // Client nonce's timestamp is not in the strike register's valid time range.
+ CLIENT_NONCE_INVALID_TIME_FAILURE = 5,
+ // Strike register's RPC call timed out, client nonce couldn't be verified.
+ CLIENT_NONCE_STRIKE_REGISTER_TIMEOUT = 6,
+ // Strike register is down, client nonce couldn't be verified.
+ CLIENT_NONCE_STRIKE_REGISTER_FAILURE = 7,
+
+ // Failure reasons for an invalid server nonce in CHLO.
+ //
+ // Unbox of server nonce failed.
+ SERVER_NONCE_DECRYPTION_FAILURE = 8,
+ // Decrypted server nonce had incorrect length.
+ SERVER_NONCE_INVALID_FAILURE = 9,
+ // Server nonce is not unique.
+ SERVER_NONCE_NOT_UNIQUE_FAILURE = 10,
+ // Server nonce's timestamp is not in the strike register's valid time range.
+ SERVER_NONCE_INVALID_TIME_FAILURE = 11,
+ // The server requires handshake confirmation.
+ SERVER_NONCE_REQUIRED_FAILURE = 20,
+
+ // Failure reasons for an invalid server config in CHLO.
+ //
+ // Missing Server config id (kSCID) tag.
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE = 12,
+ // Couldn't find the Server config id (kSCID).
+ SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE = 13,
+
+ // Failure reasons for an invalid source-address token.
+ //
+ // Missing Source-address token (kSourceAddressTokenTag) tag.
+ SOURCE_ADDRESS_TOKEN_INVALID_FAILURE = 14,
+ // Unbox of Source-address token failed.
+ SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE = 15,
+ // Couldn't parse the unbox'ed Source-address token.
+ SOURCE_ADDRESS_TOKEN_PARSE_FAILURE = 16,
+ // Source-address token is for a different IP address.
+ SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE = 17,
+ // The source-address token has a timestamp in the future.
+ SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE = 18,
+ // The source-address token has expired.
+ SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE = 19,
+
+ // The expected leaf certificate hash could not be validated.
+ INVALID_EXPECTED_LEAF_CERTIFICATE = 21,
+
+ MAX_FAILURE_REASON = 22,
+};
+
+// These errors will be packed into an uint32_t and we don't want to set the
+// most significant bit, which may be misinterpreted as the sign bit.
+static_assert(MAX_FAILURE_REASON <= 32, "failure reason out of sync");
+
+// A CrypterPair contains the encrypter and decrypter for an encryption level.
+struct QUIC_EXPORT_PRIVATE CrypterPair {
+ CrypterPair();
+ CrypterPair(CrypterPair&&) = default;
+ ~CrypterPair();
+
+ std::unique_ptr<QuicEncrypter> encrypter;
+ std::unique_ptr<QuicDecrypter> decrypter;
+};
+
+// Parameters negotiated by the crypto handshake.
+struct QUIC_EXPORT_PRIVATE QuicCryptoNegotiatedParameters
+ : public quiche::QuicheReferenceCounted {
+ // Initializes the members to 0 or empty values.
+ QuicCryptoNegotiatedParameters();
+
+ QuicTag key_exchange;
+ QuicTag aead;
+ std::string initial_premaster_secret;
+ std::string forward_secure_premaster_secret;
+ // initial_subkey_secret is used as the PRK input to the HKDF used when
+ // performing key extraction that needs to happen before forward-secure keys
+ // are available.
+ std::string initial_subkey_secret;
+ // subkey_secret is used as the PRK input to the HKDF used for key extraction.
+ std::string subkey_secret;
+ CrypterPair initial_crypters;
+ CrypterPair forward_secure_crypters;
+ // Normalized SNI: converted to lower case and trailing '.' removed.
+ std::string sni;
+ std::string client_nonce;
+ std::string server_nonce;
+ // hkdf_input_suffix contains the HKDF input following the label: the
+ // ConnectionId, client hello and server config. This is only populated in the
+ // client because only the client needs to derive the forward secure keys at a
+ // later time from the initial keys.
+ std::string hkdf_input_suffix;
+ // cached_certs contains the cached certificates that a client used when
+ // sending a client hello.
+ std::vector<std::string> cached_certs;
+ // client_key_exchange is used by clients to store the ephemeral KeyExchange
+ // for the connection.
+ std::unique_ptr<SynchronousKeyExchange> client_key_exchange;
+ // channel_id is set by servers to a ChannelID key when the client correctly
+ // proves possession of the corresponding private key. It consists of 32
+ // bytes of x coordinate, followed by 32 bytes of y coordinate. Both values
+ // are big-endian and the pair is a P-256 public key.
+ std::string channel_id;
+ QuicTag token_binding_key_param;
+
+ // Used when generating proof signature when sending server config updates.
+
+ // Used to generate cert chain when sending server config updates.
+ std::string client_cached_cert_hashes;
+
+ // Default to false; set to true if the client indicates that it supports sct
+ // by sending CSCT tag with an empty value in client hello.
+ bool sct_supported_by_client;
+
+ // Parameters only populated for TLS handshakes. These will be 0 for
+ // connections not using TLS, or if the TLS handshake is not finished yet.
+ uint16_t cipher_suite = 0;
+ uint16_t key_exchange_group = 0;
+ uint16_t peer_signature_algorithm = 0;
+
+ protected:
+ ~QuicCryptoNegotiatedParameters() override;
+};
+
+// QuicCryptoConfig contains common configuration between clients and servers.
+class QUIC_EXPORT_PRIVATE QuicCryptoConfig {
+ public:
+ // kInitialLabel is a constant that is used when deriving the initial
+ // (non-forward secure) keys for the connection in order to tie the resulting
+ // key to this protocol.
+ static const char kInitialLabel[];
+
+ // kCETVLabel is a constant that is used when deriving the keys for the
+ // encrypted tag/value block in the client hello.
+ static const char kCETVLabel[];
+
+ // kForwardSecureLabel is a constant that is used when deriving the forward
+ // secure keys for the connection in order to tie the resulting key to this
+ // protocol.
+ static const char kForwardSecureLabel[];
+
+ QuicCryptoConfig();
+ QuicCryptoConfig(const QuicCryptoConfig&) = delete;
+ QuicCryptoConfig& operator=(const QuicCryptoConfig&) = delete;
+ ~QuicCryptoConfig();
+
+ // Key exchange methods. The following two members' values correspond by
+ // index.
+ QuicTagVector kexs;
+ // Authenticated encryption with associated data (AEAD) algorithms.
+ QuicTagVector aead;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CRYPTO_HANDSHAKE_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_handshake_message.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_handshake_message.cc
new file mode 100644
index 00000000000..9fa4dfa5319
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_handshake_message.cc
@@ -0,0 +1,368 @@
+// Copyright (c) 2013 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 "quiche/quic/core/crypto/crypto_handshake_message.h"
+
+#include <memory>
+#include <string>
+
+#include "absl/strings/escaping.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/str_format.h"
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/crypto/crypto_framer.h"
+#include "quiche/quic/core/crypto/crypto_protocol.h"
+#include "quiche/quic/core/crypto/crypto_utils.h"
+#include "quiche/quic/core/quic_socket_address_coder.h"
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/common/quiche_endian.h"
+
+namespace quic {
+
+CryptoHandshakeMessage::CryptoHandshakeMessage() : tag_(0), minimum_size_(0) {}
+
+CryptoHandshakeMessage::CryptoHandshakeMessage(
+ const CryptoHandshakeMessage& other)
+ : tag_(other.tag_),
+ tag_value_map_(other.tag_value_map_),
+ minimum_size_(other.minimum_size_) {
+ // Don't copy serialized_. unique_ptr doesn't have a copy constructor.
+ // The new object can lazily reconstruct serialized_.
+}
+
+CryptoHandshakeMessage::CryptoHandshakeMessage(CryptoHandshakeMessage&& other) =
+ default;
+
+CryptoHandshakeMessage::~CryptoHandshakeMessage() {}
+
+CryptoHandshakeMessage& CryptoHandshakeMessage::operator=(
+ const CryptoHandshakeMessage& other) {
+ tag_ = other.tag_;
+ tag_value_map_ = other.tag_value_map_;
+ // Don't copy serialized_. unique_ptr doesn't have an assignment operator.
+ // However, invalidate serialized_.
+ serialized_.reset();
+ minimum_size_ = other.minimum_size_;
+ return *this;
+}
+
+CryptoHandshakeMessage& CryptoHandshakeMessage::operator=(
+ CryptoHandshakeMessage&& other) = default;
+
+bool CryptoHandshakeMessage::operator==(
+ const CryptoHandshakeMessage& rhs) const {
+ return tag_ == rhs.tag_ && tag_value_map_ == rhs.tag_value_map_ &&
+ minimum_size_ == rhs.minimum_size_;
+}
+
+bool CryptoHandshakeMessage::operator!=(
+ const CryptoHandshakeMessage& rhs) const {
+ return !(*this == rhs);
+}
+
+void CryptoHandshakeMessage::Clear() {
+ tag_ = 0;
+ tag_value_map_.clear();
+ minimum_size_ = 0;
+ serialized_.reset();
+}
+
+const QuicData& CryptoHandshakeMessage::GetSerialized() const {
+ if (!serialized_) {
+ serialized_ = CryptoFramer::ConstructHandshakeMessage(*this);
+ }
+ return *serialized_;
+}
+
+void CryptoHandshakeMessage::MarkDirty() { serialized_.reset(); }
+
+void CryptoHandshakeMessage::SetVersionVector(
+ QuicTag tag, ParsedQuicVersionVector versions) {
+ QuicVersionLabelVector version_labels;
+ for (const ParsedQuicVersion& version : versions) {
+ version_labels.push_back(
+ quiche::QuicheEndian::HostToNet32(CreateQuicVersionLabel(version)));
+ }
+ SetVector(tag, version_labels);
+}
+
+void CryptoHandshakeMessage::SetVersion(QuicTag tag,
+ ParsedQuicVersion version) {
+ SetValue(tag,
+ quiche::QuicheEndian::HostToNet32(CreateQuicVersionLabel(version)));
+}
+
+void CryptoHandshakeMessage::SetStringPiece(QuicTag tag,
+ absl::string_view value) {
+ tag_value_map_[tag] = std::string(value);
+}
+
+void CryptoHandshakeMessage::Erase(QuicTag tag) { tag_value_map_.erase(tag); }
+
+QuicErrorCode CryptoHandshakeMessage::GetTaglist(
+ QuicTag tag, QuicTagVector* out_tags) const {
+ auto it = tag_value_map_.find(tag);
+ QuicErrorCode ret = QUIC_NO_ERROR;
+
+ if (it == tag_value_map_.end()) {
+ ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
+ } else if (it->second.size() % sizeof(QuicTag) != 0) {
+ ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ if (ret != QUIC_NO_ERROR) {
+ out_tags->clear();
+ return ret;
+ }
+
+ size_t num_tags = it->second.size() / sizeof(QuicTag);
+ out_tags->resize(num_tags);
+ for (size_t i = 0; i < num_tags; ++i) {
+ QuicTag tag;
+ memcpy(&tag, it->second.data() + i * sizeof(tag), sizeof(tag));
+ (*out_tags)[i] = tag;
+ }
+ return ret;
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetVersionLabelList(
+ QuicTag tag, QuicVersionLabelVector* out) const {
+ QuicErrorCode error = GetTaglist(tag, out);
+ if (error != QUIC_NO_ERROR) {
+ return error;
+ }
+
+ for (size_t i = 0; i < out->size(); ++i) {
+ (*out)[i] = quiche::QuicheEndian::HostToNet32((*out)[i]);
+ }
+
+ return QUIC_NO_ERROR;
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetVersionLabel(
+ QuicTag tag, QuicVersionLabel* out) const {
+ QuicErrorCode error = GetUint32(tag, out);
+ if (error != QUIC_NO_ERROR) {
+ return error;
+ }
+
+ *out = quiche::QuicheEndian::HostToNet32(*out);
+ return QUIC_NO_ERROR;
+}
+
+bool CryptoHandshakeMessage::GetStringPiece(QuicTag tag,
+ absl::string_view* out) const {
+ auto it = tag_value_map_.find(tag);
+ if (it == tag_value_map_.end()) {
+ return false;
+ }
+ *out = it->second;
+ return true;
+}
+
+bool CryptoHandshakeMessage::HasStringPiece(QuicTag tag) const {
+ return tag_value_map_.find(tag) != tag_value_map_.end();
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetNthValue24(
+ QuicTag tag, unsigned index, absl::string_view* out) const {
+ absl::string_view value;
+ if (!GetStringPiece(tag, &value)) {
+ return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
+ }
+
+ for (unsigned i = 0;; i++) {
+ if (value.empty()) {
+ return QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND;
+ }
+ if (value.size() < 3) {
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ const unsigned char* data =
+ reinterpret_cast<const unsigned char*>(value.data());
+ size_t size = static_cast<size_t>(data[0]) |
+ (static_cast<size_t>(data[1]) << 8) |
+ (static_cast<size_t>(data[2]) << 16);
+ value.remove_prefix(3);
+
+ if (value.size() < size) {
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ if (i == index) {
+ *out = absl::string_view(value.data(), size);
+ return QUIC_NO_ERROR;
+ }
+
+ value.remove_prefix(size);
+ }
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetUint32(QuicTag tag,
+ uint32_t* out) const {
+ return GetPOD(tag, out, sizeof(uint32_t));
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetUint64(QuicTag tag,
+ uint64_t* out) const {
+ return GetPOD(tag, out, sizeof(uint64_t));
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetStatelessResetToken(
+ QuicTag tag, StatelessResetToken* out) const {
+ return GetPOD(tag, out, kStatelessResetTokenLength);
+}
+
+size_t CryptoHandshakeMessage::size() const {
+ size_t ret = sizeof(QuicTag) + sizeof(uint16_t) /* number of entries */ +
+ sizeof(uint16_t) /* padding */;
+ ret += (sizeof(QuicTag) + sizeof(uint32_t) /* end offset */) *
+ tag_value_map_.size();
+ for (auto i = tag_value_map_.begin(); i != tag_value_map_.end(); ++i) {
+ ret += i->second.size();
+ }
+
+ return ret;
+}
+
+void CryptoHandshakeMessage::set_minimum_size(size_t min_bytes) {
+ if (min_bytes == minimum_size_) {
+ return;
+ }
+ serialized_.reset();
+ minimum_size_ = min_bytes;
+}
+
+size_t CryptoHandshakeMessage::minimum_size() const { return minimum_size_; }
+
+std::string CryptoHandshakeMessage::DebugString() const {
+ return DebugStringInternal(0);
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetPOD(QuicTag tag, void* out,
+ size_t len) const {
+ auto it = tag_value_map_.find(tag);
+ QuicErrorCode ret = QUIC_NO_ERROR;
+
+ if (it == tag_value_map_.end()) {
+ ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
+ } else if (it->second.size() != len) {
+ ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ if (ret != QUIC_NO_ERROR) {
+ memset(out, 0, len);
+ return ret;
+ }
+
+ memcpy(out, it->second.data(), len);
+ return ret;
+}
+
+std::string CryptoHandshakeMessage::DebugStringInternal(size_t indent) const {
+ std::string ret =
+ std::string(2 * indent, ' ') + QuicTagToString(tag_) + "<\n";
+ ++indent;
+ for (auto it = tag_value_map_.begin(); it != tag_value_map_.end(); ++it) {
+ ret += std::string(2 * indent, ' ') + QuicTagToString(it->first) + ": ";
+
+ bool done = false;
+ switch (it->first) {
+ case kICSL:
+ case kCFCW:
+ case kSFCW:
+ case kIRTT:
+ case kMIUS:
+ case kMIBS:
+ case kTCID:
+ case kMAD:
+ // uint32_t value
+ if (it->second.size() == 4) {
+ uint32_t value;
+ memcpy(&value, it->second.data(), sizeof(value));
+ absl::StrAppend(&ret, value);
+ done = true;
+ }
+ break;
+ case kKEXS:
+ case kAEAD:
+ case kCOPT:
+ case kPDMD:
+ case kVER:
+ // tag lists
+ if (it->second.size() % sizeof(QuicTag) == 0) {
+ for (size_t j = 0; j < it->second.size(); j += sizeof(QuicTag)) {
+ QuicTag tag;
+ memcpy(&tag, it->second.data() + j, sizeof(tag));
+ if (j > 0) {
+ ret += ",";
+ }
+ ret += "'" + QuicTagToString(tag) + "'";
+ }
+ done = true;
+ }
+ break;
+ case kRREJ:
+ // uint32_t lists
+ if (it->second.size() % sizeof(uint32_t) == 0) {
+ for (size_t j = 0; j < it->second.size(); j += sizeof(uint32_t)) {
+ uint32_t value;
+ memcpy(&value, it->second.data() + j, sizeof(value));
+ if (j > 0) {
+ ret += ",";
+ }
+ ret += CryptoUtils::HandshakeFailureReasonToString(
+ static_cast<HandshakeFailureReason>(value));
+ }
+ done = true;
+ }
+ break;
+ case kCADR:
+ // IP address and port
+ if (!it->second.empty()) {
+ QuicSocketAddressCoder decoder;
+ if (decoder.Decode(it->second.data(), it->second.size())) {
+ ret += QuicSocketAddress(decoder.ip(), decoder.port()).ToString();
+ done = true;
+ }
+ }
+ break;
+ case kSCFG:
+ // nested messages.
+ if (!it->second.empty()) {
+ std::unique_ptr<CryptoHandshakeMessage> msg(
+ CryptoFramer::ParseMessage(it->second));
+ if (msg) {
+ ret += "\n";
+ ret += msg->DebugStringInternal(indent + 1);
+
+ done = true;
+ }
+ }
+ break;
+ case kPAD:
+ ret += absl::StrFormat("(%d bytes of padding)", it->second.size());
+ done = true;
+ break;
+ case kSNI:
+ case kUAID:
+ ret += "\"" + it->second + "\"";
+ done = true;
+ break;
+ }
+
+ if (!done) {
+ // If there's no specific format for this tag, or the value is invalid,
+ // then just use hex.
+ ret += "0x" + absl::BytesToHexString(it->second);
+ }
+ ret += "\n";
+ }
+ --indent;
+ ret += std::string(2 * indent, ' ') + ">";
+ return ret;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_handshake_message.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_handshake_message.h
new file mode 100644
index 00000000000..b50c559d2ea
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_handshake_message.h
@@ -0,0 +1,159 @@
+// Copyright (c) 2013 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CRYPTO_HANDSHAKE_MESSAGE_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CRYPTO_HANDSHAKE_MESSAGE_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/quic_packets.h"
+#include "quiche/quic/core/quic_types.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// An intermediate format of a handshake message that's convenient for a
+// CryptoFramer to serialize from or parse into.
+class QUIC_EXPORT_PRIVATE CryptoHandshakeMessage {
+ public:
+ CryptoHandshakeMessage();
+ CryptoHandshakeMessage(const CryptoHandshakeMessage& other);
+ CryptoHandshakeMessage(CryptoHandshakeMessage&& other);
+ ~CryptoHandshakeMessage();
+
+ CryptoHandshakeMessage& operator=(const CryptoHandshakeMessage& other);
+ CryptoHandshakeMessage& operator=(CryptoHandshakeMessage&& other);
+
+ bool operator==(const CryptoHandshakeMessage& rhs) const;
+ bool operator!=(const CryptoHandshakeMessage& rhs) const;
+
+ // Clears state.
+ void Clear();
+
+ // GetSerialized returns the serialized form of this message and caches the
+ // result. Subsequently altering the message does not invalidate the cache.
+ const QuicData& GetSerialized() const;
+
+ // MarkDirty invalidates the cache created by |GetSerialized|.
+ void MarkDirty();
+
+ // SetValue sets an element with the given tag to the raw, memory contents of
+ // |v|.
+ template <class T>
+ void SetValue(QuicTag tag, const T& v) {
+ tag_value_map_[tag] =
+ std::string(reinterpret_cast<const char*>(&v), sizeof(v));
+ }
+
+ // SetVector sets an element with the given tag to the raw contents of an
+ // array of elements in |v|.
+ template <class T>
+ void SetVector(QuicTag tag, const std::vector<T>& v) {
+ if (v.empty()) {
+ tag_value_map_[tag] = std::string();
+ } else {
+ tag_value_map_[tag] = std::string(reinterpret_cast<const char*>(&v[0]),
+ v.size() * sizeof(T));
+ }
+ }
+
+ // Sets an element with the given tag to the on-the-wire representation of
+ // |version|.
+ void SetVersion(QuicTag tag, ParsedQuicVersion version);
+
+ // Sets an element with the given tag to the on-the-wire representation of
+ // the elements in |versions|.
+ void SetVersionVector(QuicTag tag, ParsedQuicVersionVector versions);
+
+ // Returns the message tag.
+ QuicTag tag() const { return tag_; }
+ // Sets the message tag.
+ void set_tag(QuicTag tag) { tag_ = tag; }
+
+ const QuicTagValueMap& tag_value_map() const { return tag_value_map_; }
+
+ void SetStringPiece(QuicTag tag, absl::string_view value);
+
+ // Erase removes a tag/value, if present, from the message.
+ void Erase(QuicTag tag);
+
+ // GetTaglist finds an element with the given tag containing zero or more
+ // tags. If such a tag doesn't exist, it returns an error code. Otherwise it
+ // populates |out_tags| with the tags and returns QUIC_NO_ERROR.
+ QuicErrorCode GetTaglist(QuicTag tag, QuicTagVector* out_tags) const;
+
+ // GetVersionLabelList finds an element with the given tag containing zero or
+ // more version labels. If such a tag doesn't exist, it returns an error code.
+ // Otherwise it populates |out| with the labels and returns QUIC_NO_ERROR.
+ QuicErrorCode GetVersionLabelList(QuicTag tag,
+ QuicVersionLabelVector* out) const;
+
+ // GetVersionLabel finds an element with the given tag containing a single
+ // version label. If such a tag doesn't exist, it returns an error code.
+ // Otherwise it populates |out| with the label and returns QUIC_NO_ERROR.
+ QuicErrorCode GetVersionLabel(QuicTag tag, QuicVersionLabel* out) const;
+
+ bool GetStringPiece(QuicTag tag, absl::string_view* out) const;
+ bool HasStringPiece(QuicTag tag) const;
+
+ // GetNthValue24 interprets the value with the given tag to be a series of
+ // 24-bit, length prefixed values and it returns the subvalue with the given
+ // index.
+ QuicErrorCode GetNthValue24(QuicTag tag, unsigned index,
+ absl::string_view* out) const;
+ QuicErrorCode GetUint32(QuicTag tag, uint32_t* out) const;
+ QuicErrorCode GetUint64(QuicTag tag, uint64_t* out) const;
+
+ QuicErrorCode GetStatelessResetToken(QuicTag tag,
+ StatelessResetToken* out) const;
+
+ // size returns 4 (message tag) + 2 (uint16_t, number of entries) +
+ // (4 (tag) + 4 (end offset))*tag_value_map_.size() + ∑ value sizes.
+ size_t size() const;
+
+ // set_minimum_size sets the minimum number of bytes that the message should
+ // consume. The CryptoFramer will add a PAD tag as needed when serializing in
+ // order to ensure this. Setting a value of 0 disables padding.
+ //
+ // Padding is useful in order to ensure that messages are a minimum size. A
+ // QUIC server can require a minimum size in order to reduce the
+ // amplification factor of any mirror DoS attack.
+ void set_minimum_size(size_t min_bytes);
+
+ size_t minimum_size() const;
+
+ // DebugString returns a multi-line, string representation of the message
+ // suitable for including in debug output.
+ std::string DebugString() const;
+
+ private:
+ // GetPOD is a utility function for extracting a plain-old-data value. If
+ // |tag| exists in the message, and has a value of exactly |len| bytes then
+ // it copies |len| bytes of data into |out|. Otherwise |len| bytes at |out|
+ // are zeroed out.
+ //
+ // If used to copy integers then this assumes that the machine is
+ // little-endian.
+ QuicErrorCode GetPOD(QuicTag tag, void* out, size_t len) const;
+
+ std::string DebugStringInternal(size_t indent) const;
+
+ QuicTag tag_;
+ QuicTagValueMap tag_value_map_;
+
+ size_t minimum_size_;
+
+ // The serialized form of the handshake message. This member is constructed
+ // lazily.
+ mutable std::unique_ptr<QuicData> serialized_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CRYPTO_HANDSHAKE_MESSAGE_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_handshake_message_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_handshake_message_test.cc
new file mode 100644
index 00000000000..bdc051c2bd9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_handshake_message_test.cc
@@ -0,0 +1,105 @@
+// Copyright (c) 2015 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 "quiche/quic/core/crypto/crypto_handshake_message.h"
+
+#include "quiche/quic/core/crypto/crypto_handshake.h"
+#include "quiche/quic/core/crypto/crypto_protocol.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/common/quiche_endian.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+TEST(CryptoHandshakeMessageTest, DebugString) {
+ const char* str = "SHLO<\n>";
+
+ CryptoHandshakeMessage message;
+ message.set_tag(kSHLO);
+ EXPECT_EQ(str, message.DebugString());
+
+ // Test copy
+ CryptoHandshakeMessage message2(message);
+ EXPECT_EQ(str, message2.DebugString());
+
+ // Test move
+ CryptoHandshakeMessage message3(std::move(message));
+ EXPECT_EQ(str, message3.DebugString());
+
+ // Test assign
+ CryptoHandshakeMessage message4 = message3;
+ EXPECT_EQ(str, message4.DebugString());
+
+ // Test move-assign
+ CryptoHandshakeMessage message5 = std::move(message3);
+ EXPECT_EQ(str, message5.DebugString());
+}
+
+TEST(CryptoHandshakeMessageTest, DebugStringWithUintVector) {
+ const char* str =
+ "REJ <\n RREJ: "
+ "SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE,"
+ "CLIENT_NONCE_NOT_UNIQUE_FAILURE\n>";
+
+ CryptoHandshakeMessage message;
+ message.set_tag(kREJ);
+ std::vector<uint32_t> reasons = {
+ SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE,
+ CLIENT_NONCE_NOT_UNIQUE_FAILURE};
+ message.SetVector(kRREJ, reasons);
+ EXPECT_EQ(str, message.DebugString());
+
+ // Test copy
+ CryptoHandshakeMessage message2(message);
+ EXPECT_EQ(str, message2.DebugString());
+
+ // Test move
+ CryptoHandshakeMessage message3(std::move(message));
+ EXPECT_EQ(str, message3.DebugString());
+
+ // Test assign
+ CryptoHandshakeMessage message4 = message3;
+ EXPECT_EQ(str, message4.DebugString());
+
+ // Test move-assign
+ CryptoHandshakeMessage message5 = std::move(message3);
+ EXPECT_EQ(str, message5.DebugString());
+}
+
+TEST(CryptoHandshakeMessageTest, DebugStringWithTagVector) {
+ const char* str = "CHLO<\n COPT: 'TBBR','PAD ','BYTE'\n>";
+
+ CryptoHandshakeMessage message;
+ message.set_tag(kCHLO);
+ message.SetVector(kCOPT, QuicTagVector{kTBBR, kPAD, kBYTE});
+ EXPECT_EQ(str, message.DebugString());
+
+ // Test copy
+ CryptoHandshakeMessage message2(message);
+ EXPECT_EQ(str, message2.DebugString());
+
+ // Test move
+ CryptoHandshakeMessage message3(std::move(message));
+ EXPECT_EQ(str, message3.DebugString());
+
+ // Test assign
+ CryptoHandshakeMessage message4 = message3;
+ EXPECT_EQ(str, message4.DebugString());
+
+ // Test move-assign
+ CryptoHandshakeMessage message5 = std::move(message3);
+ EXPECT_EQ(str, message5.DebugString());
+}
+
+TEST(CryptoHandshakeMessageTest, HasStringPiece) {
+ CryptoHandshakeMessage message;
+ EXPECT_FALSE(message.HasStringPiece(kALPN));
+ message.SetStringPiece(kALPN, "foo");
+ EXPECT_TRUE(message.HasStringPiece(kALPN));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_message_parser.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_message_parser.h
new file mode 100644
index 00000000000..f819209bbb9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_message_parser.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2012 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CRYPTO_MESSAGE_PARSER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CRYPTO_MESSAGE_PARSER_H_
+
+#include <string>
+
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/quic_error_codes.h"
+#include "quiche/quic/core/quic_types.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE CryptoMessageParser {
+ public:
+ virtual ~CryptoMessageParser() {}
+
+ virtual QuicErrorCode error() const = 0;
+ virtual const std::string& error_detail() const = 0;
+
+ // Processes input data, which must be delivered in order. The input data
+ // being processed was received at encryption level |level|. Returns
+ // false if there was an error, and true otherwise.
+ virtual bool ProcessInput(absl::string_view input, EncryptionLevel level) = 0;
+
+ // Returns the number of bytes of buffered input data remaining to be
+ // parsed.
+ virtual size_t InputBytesRemaining() const = 0;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CRYPTO_MESSAGE_PARSER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_message_printer_bin.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_message_printer_bin.cc
new file mode 100644
index 00000000000..eb7393d549e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_message_printer_bin.cc
@@ -0,0 +1,61 @@
+// Copyright (c) 2019 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.
+
+// Dumps the contents of a QUIC crypto handshake message in a human readable
+// format.
+//
+// Usage: crypto_message_printer_bin <hex of message>
+
+#include <iostream>
+#include <string>
+
+#include "absl/strings/escaping.h"
+#include "quiche/quic/core/crypto/crypto_framer.h"
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/common/platform/api/quiche_command_line_flags.h"
+
+using std::cerr;
+using std::cout;
+using std::endl;
+
+namespace quic {
+
+class CryptoMessagePrinter : public ::quic::CryptoFramerVisitorInterface {
+ public:
+ void OnHandshakeMessage(const CryptoHandshakeMessage& message) override {
+ cout << message.DebugString() << endl;
+ }
+
+ void OnError(CryptoFramer* framer) override {
+ cerr << "Error code: " << framer->error() << endl;
+ cerr << "Error details: " << framer->error_detail() << endl;
+ }
+};
+
+} // namespace quic
+
+int main(int argc, char* argv[]) {
+ const char* usage = "Usage: crypto_message_printer <hex>";
+ std::vector<std::string> messages =
+ quiche::QuicheParseCommandLineFlags(usage, argc, argv);
+ if (messages.size() != 1) {
+ quiche::QuichePrintCommandLineFlagHelp(usage);
+ exit(0);
+ }
+
+ quic::CryptoMessagePrinter printer;
+ quic::CryptoFramer framer;
+ framer.set_visitor(&printer);
+ framer.set_process_truncated_messages(true);
+ std::string input = absl::HexStringToBytes(messages[0]);
+ if (!framer.ProcessInput(input)) {
+ return 1;
+ }
+ if (framer.InputBytesRemaining() != 0) {
+ cerr << "Input partially consumed. " << framer.InputBytesRemaining()
+ << " bytes remaining." << endl;
+ return 2;
+ }
+ return 0;
+}
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_protocol.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_protocol.h
new file mode 100644
index 00000000000..7dbbb6d36b0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_protocol.h
@@ -0,0 +1,509 @@
+// Copyright (c) 2012 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CRYPTO_PROTOCOL_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CRYPTO_PROTOCOL_H_
+
+#include <cstddef>
+#include <string>
+
+#include "quiche/quic/core/quic_tag.h"
+
+// Version and Crypto tags are written to the wire with a big-endian
+// representation of the name of the tag. For example
+// the client hello tag (CHLO) will be written as the
+// following 4 bytes: 'C' 'H' 'L' 'O'. Since it is
+// stored in memory as a little endian uint32_t, we need
+// to reverse the order of the bytes.
+//
+// We use a macro to ensure that no static initialisers are created. Use the
+// MakeQuicTag function in normal code.
+#define TAG(a, b, c, d) \
+ static_cast<QuicTag>((d << 24) + (c << 16) + (b << 8) + a)
+
+namespace quic {
+
+using ServerConfigID = std::string;
+
+// The following tags have been deprecated and should not be reused:
+// "1CON", "BBQ4", "NCON", "RCID", "SREJ", "TBKP", "TB10", "SCLS", "SMHL",
+// "QNZR", "B2HI", "H2PR", "FIFO", "LIFO", "RRWS", "QNSP", "B2CL", "CHSP",
+// "BPTE", "ACKD", "AKD2", "AKD4", "MAD1", "MAD4", "MAD5", "ACD0", "ACKQ",
+// "TLPR", "CCS\0", "PDP4"
+
+// clang-format off
+const QuicTag kCHLO = TAG('C', 'H', 'L', 'O'); // Client hello
+const QuicTag kSHLO = TAG('S', 'H', 'L', 'O'); // Server hello
+const QuicTag kSCFG = TAG('S', 'C', 'F', 'G'); // Server config
+const QuicTag kREJ = TAG('R', 'E', 'J', '\0'); // Reject
+const QuicTag kCETV = TAG('C', 'E', 'T', 'V'); // Client encrypted tag-value
+ // pairs
+const QuicTag kPRST = TAG('P', 'R', 'S', 'T'); // Public reset
+const QuicTag kSCUP = TAG('S', 'C', 'U', 'P'); // Server config update
+const QuicTag kALPN = TAG('A', 'L', 'P', 'N'); // Application-layer protocol
+
+// Key exchange methods
+const QuicTag kP256 = TAG('P', '2', '5', '6'); // ECDH, Curve P-256
+const QuicTag kC255 = TAG('C', '2', '5', '5'); // ECDH, Curve25519
+
+// AEAD algorithms
+const QuicTag kAESG = TAG('A', 'E', 'S', 'G'); // AES128 + GCM-12
+const QuicTag kCC20 = TAG('C', 'C', '2', '0'); // ChaCha20 + Poly1305 RFC7539
+
+// Congestion control feedback types
+const QuicTag kQBIC = TAG('Q', 'B', 'I', 'C'); // TCP cubic
+
+// Connection options (COPT) values
+const QuicTag kAFCW = TAG('A', 'F', 'C', 'W'); // Auto-tune flow control
+ // receive windows.
+const QuicTag kIFW5 = TAG('I', 'F', 'W', '5'); // Set initial size
+ // of stream flow control
+ // receive window to
+ // 32KB. (2^5 KB).
+const QuicTag kIFW6 = TAG('I', 'F', 'W', '6'); // Set initial size
+ // of stream flow control
+ // receive window to
+ // 64KB. (2^6 KB).
+const QuicTag kIFW7 = TAG('I', 'F', 'W', '7'); // Set initial size
+ // of stream flow control
+ // receive window to
+ // 128KB. (2^7 KB).
+const QuicTag kIFW8 = TAG('I', 'F', 'W', '8'); // Set initial size
+ // of stream flow control
+ // receive window to
+ // 256KB. (2^8 KB).
+const QuicTag kIFW9 = TAG('I', 'F', 'W', '9'); // Set initial size
+ // of stream flow control
+ // receive window to
+ // 512KB. (2^9 KB).
+const QuicTag kIFWA = TAG('I', 'F', 'W', 'a'); // Set initial size
+ // of stream flow control
+ // receive window to
+ // 1MB. (2^0xa KB).
+const QuicTag kTBBR = TAG('T', 'B', 'B', 'R'); // Reduced Buffer Bloat TCP
+const QuicTag k1RTT = TAG('1', 'R', 'T', 'T'); // STARTUP in BBR for 1 RTT
+const QuicTag k2RTT = TAG('2', 'R', 'T', 'T'); // STARTUP in BBR for 2 RTTs
+const QuicTag kLRTT = TAG('L', 'R', 'T', 'T'); // Exit STARTUP in BBR on loss
+const QuicTag kBBS1 = TAG('B', 'B', 'S', '1'); // DEPRECATED
+const QuicTag kBBS2 = TAG('B', 'B', 'S', '2'); // More aggressive packet
+ // conservation in BBR STARTUP
+const QuicTag kBBS3 = TAG('B', 'B', 'S', '3'); // Slowstart packet
+ // conservation in BBR STARTUP
+const QuicTag kBBS4 = TAG('B', 'B', 'S', '4'); // DEPRECATED
+const QuicTag kBBS5 = TAG('B', 'B', 'S', '5'); // DEPRECATED
+const QuicTag kBBRR = TAG('B', 'B', 'R', 'R'); // Rate-based recovery in BBR
+const QuicTag kBBR1 = TAG('B', 'B', 'R', '1'); // DEPRECATED
+const QuicTag kBBR2 = TAG('B', 'B', 'R', '2'); // DEPRECATED
+const QuicTag kBBR3 = TAG('B', 'B', 'R', '3'); // Fully drain the queue once
+ // per cycle
+const QuicTag kBBR4 = TAG('B', 'B', 'R', '4'); // 20 RTT ack aggregation
+const QuicTag kBBR5 = TAG('B', 'B', 'R', '5'); // 40 RTT ack aggregation
+const QuicTag kBBR9 = TAG('B', 'B', 'R', '9'); // DEPRECATED
+const QuicTag kBBRA = TAG('B', 'B', 'R', 'A'); // Starts a new ack aggregation
+ // epoch if a full round has
+ // passed
+const QuicTag kBBRB = TAG('B', 'B', 'R', 'B'); // Use send rate in BBR's
+ // MaxAckHeightTracker
+const QuicTag kBBRS = TAG('B', 'B', 'R', 'S'); // DEPRECATED
+const QuicTag kBBQ1 = TAG('B', 'B', 'Q', '1'); // BBR with lower 2.77 STARTUP
+ // pacing and CWND gain.
+const QuicTag kBBQ2 = TAG('B', 'B', 'Q', '2'); // BBRv2 with 2.885 STARTUP and
+ // DRAIN CWND gain.
+const QuicTag kBBQ3 = TAG('B', 'B', 'Q', '3'); // BBR with ack aggregation
+ // compensation in STARTUP.
+const QuicTag kBBQ5 = TAG('B', 'B', 'Q', '5'); // Expire ack aggregation upon
+ // bandwidth increase in
+ // STARTUP.
+const QuicTag kBBQ6 = TAG('B', 'B', 'Q', '6'); // Reduce STARTUP gain to 25%
+ // more than BW increase.
+const QuicTag kBBQ7 = TAG('B', 'B', 'Q', '7'); // Reduce bw_lo by
+ // bytes_lost/min_rtt.
+const QuicTag kBBQ8 = TAG('B', 'B', 'Q', '8'); // Reduce bw_lo by
+ // bw_lo * bytes_lost/inflight
+const QuicTag kBBQ9 = TAG('B', 'B', 'Q', '9'); // Reduce bw_lo by
+ // bw_lo * bytes_lost/cwnd
+const QuicTag kBBQ0 = TAG('B', 'B', 'Q', '0'); // Increase bytes_acked in
+ // PROBE_UP when app limited.
+const QuicTag kRENO = TAG('R', 'E', 'N', 'O'); // Reno Congestion Control
+const QuicTag kTPCC = TAG('P', 'C', 'C', '\0'); // Performance-Oriented
+ // Congestion Control
+const QuicTag kBYTE = TAG('B', 'Y', 'T', 'E'); // TCP cubic or reno in bytes
+const QuicTag kIW03 = TAG('I', 'W', '0', '3'); // Force ICWND to 3
+const QuicTag kIW10 = TAG('I', 'W', '1', '0'); // Force ICWND to 10
+const QuicTag kIW20 = TAG('I', 'W', '2', '0'); // Force ICWND to 20
+const QuicTag kIW50 = TAG('I', 'W', '5', '0'); // Force ICWND to 50
+const QuicTag kB2ON = TAG('B', '2', 'O', 'N'); // Enable BBRv2
+const QuicTag kB2NA = TAG('B', '2', 'N', 'A'); // For BBRv2, do not add ack
+ // height to queueing threshold
+const QuicTag kB2NE = TAG('B', '2', 'N', 'E'); // For BBRv2, always exit
+ // STARTUP on loss, even if
+ // bandwidth growth exceeds
+ // threshold.
+const QuicTag kB2RP = TAG('B', '2', 'R', 'P'); // For BBRv2, run PROBE_RTT on
+ // the regular schedule
+const QuicTag kB2LO = TAG('B', '2', 'L', 'O'); // Ignore inflight_lo in BBR2
+const QuicTag kB2HR = TAG('B', '2', 'H', 'R'); // 15% inflight_hi headroom.
+const QuicTag kB2SL = TAG('B', '2', 'S', 'L'); // When exiting STARTUP due to
+ // loss, set inflight_hi to the
+ // max of bdp and max bytes
+ // delivered in round.
+const QuicTag kB2H2 = TAG('B', '2', 'H', '2'); // When exiting PROBE_UP due to
+ // loss, set inflight_hi to the
+ // max of inflight@send and max
+ // bytes delivered in round.
+const QuicTag kB2RC = TAG('B', '2', 'R', 'C'); // Disable Reno-coexistence for
+ // BBR2.
+const QuicTag kBSAO = TAG('B', 'S', 'A', 'O'); // Avoid Overestimation in
+ // Bandwidth Sampler with ack
+ // aggregation
+const QuicTag kB2DL = TAG('B', '2', 'D', 'L'); // Increase inflight_hi based
+ // on delievered, not inflight.
+const QuicTag kB201 = TAG('B', '2', '0', '1'); // In PROBE_UP, check if cwnd
+ // limited before aggregation
+ // epoch, instead of ack event.
+const QuicTag kB202 = TAG('B', '2', '0', '2'); // Do not exit PROBE_UP if
+ // inflight dips below 1.25*BW.
+const QuicTag kB203 = TAG('B', '2', '0', '3'); // Ignore inflight_hi until
+ // PROBE_UP is exited.
+const QuicTag kB204 = TAG('B', '2', '0', '4'); // Reduce extra acked when
+ // MaxBW incrases.
+const QuicTag kB205 = TAG('B', '2', '0', '5'); // Add extra acked to CWND in
+ // STARTUP.
+const QuicTag kB206 = TAG('B', '2', '0', '6'); // Exit STARTUP after 2 losses.
+const QuicTag kB207 = TAG('B', '2', '0', '7'); // Exit STARTUP on persistent
+ // queue
+const QuicTag kNTLP = TAG('N', 'T', 'L', 'P'); // No tail loss probe
+const QuicTag k1TLP = TAG('1', 'T', 'L', 'P'); // 1 tail loss probe
+const QuicTag k1RTO = TAG('1', 'R', 'T', 'O'); // Send 1 packet upon RTO
+const QuicTag kNRTO = TAG('N', 'R', 'T', 'O'); // CWND reduction on loss
+const QuicTag kTIME = TAG('T', 'I', 'M', 'E'); // Time based loss detection
+const QuicTag kATIM = TAG('A', 'T', 'I', 'M'); // Adaptive time loss detection
+const QuicTag kMIN1 = TAG('M', 'I', 'N', '1'); // Min CWND of 1 packet
+const QuicTag kMIN4 = TAG('M', 'I', 'N', '4'); // Min CWND of 4 packets,
+ // with a min rate of 1 BDP.
+const QuicTag kMAD0 = TAG('M', 'A', 'D', '0'); // Ignore ack delay
+const QuicTag kMAD2 = TAG('M', 'A', 'D', '2'); // No min TLP
+const QuicTag kMAD3 = TAG('M', 'A', 'D', '3'); // No min RTO
+const QuicTag k1ACK = TAG('1', 'A', 'C', 'K'); // 1 fast ack for reordering
+const QuicTag kAKD3 = TAG('A', 'K', 'D', '3'); // Ack decimation style acking
+ // with 1/8 RTT acks.
+const QuicTag kAKDU = TAG('A', 'K', 'D', 'U'); // Unlimited number of packets
+ // received before acking
+const QuicTag kAFFE = TAG('A', 'F', 'F', 'E'); // Enable client receiving
+ // AckFrequencyFrame.
+const QuicTag kAFF1 = TAG('A', 'F', 'F', '1'); // Use SRTT in building
+ // AckFrequencyFrame.
+const QuicTag kAFF2 = TAG('A', 'F', 'F', '2'); // Send AckFrequencyFrame upon
+ // handshake completion.
+const QuicTag kSSLR = TAG('S', 'S', 'L', 'R'); // Slow Start Large Reduction.
+const QuicTag kNPRR = TAG('N', 'P', 'R', 'R'); // Pace at unity instead of PRR
+const QuicTag k2RTO = TAG('2', 'R', 'T', 'O'); // Close connection on 2 RTOs
+const QuicTag k3RTO = TAG('3', 'R', 'T', 'O'); // Close connection on 3 RTOs
+const QuicTag k4RTO = TAG('4', 'R', 'T', 'O'); // Close connection on 4 RTOs
+const QuicTag k5RTO = TAG('5', 'R', 'T', 'O'); // Close connection on 5 RTOs
+const QuicTag k6RTO = TAG('6', 'R', 'T', 'O'); // Close connection on 6 RTOs
+const QuicTag kCBHD = TAG('C', 'B', 'H', 'D'); // Client only blackhole
+ // detection.
+const QuicTag kNBHD = TAG('N', 'B', 'H', 'D'); // No blackhole detection.
+const QuicTag kCONH = TAG('C', 'O', 'N', 'H'); // Conservative Handshake
+ // Retransmissions.
+const QuicTag kLFAK = TAG('L', 'F', 'A', 'K'); // Don't invoke FACK on the
+ // first ack.
+const QuicTag kSTMP = TAG('S', 'T', 'M', 'P'); // DEPRECATED
+const QuicTag kEACK = TAG('E', 'A', 'C', 'K'); // Bundle ack-eliciting frame
+ // with an ACK after PTO/RTO
+
+const QuicTag kILD0 = TAG('I', 'L', 'D', '0'); // IETF style loss detection
+ // (default with 1/8 RTT time
+ // threshold)
+const QuicTag kILD1 = TAG('I', 'L', 'D', '1'); // IETF style loss detection
+ // with 1/4 RTT time threshold
+const QuicTag kILD2 = TAG('I', 'L', 'D', '2'); // IETF style loss detection
+ // with adaptive packet
+ // threshold
+const QuicTag kILD3 = TAG('I', 'L', 'D', '3'); // IETF style loss detection
+ // with 1/4 RTT time threshold
+ // and adaptive packet
+ // threshold
+const QuicTag kILD4 = TAG('I', 'L', 'D', '4'); // IETF style loss detection
+ // with both adaptive time
+ // threshold (default 1/4 RTT)
+ // and adaptive packet
+ // threshold
+const QuicTag kRUNT = TAG('R', 'U', 'N', 'T'); // No packet threshold loss
+ // detection for "runt" packet.
+const QuicTag kNSTP = TAG('N', 'S', 'T', 'P'); // No stop waiting frames.
+const QuicTag kNRTT = TAG('N', 'R', 'T', 'T'); // Ignore initial RTT
+
+const QuicTag k1PTO = TAG('1', 'P', 'T', 'O'); // Send 1 packet upon PTO.
+const QuicTag k2PTO = TAG('2', 'P', 'T', 'O'); // Send 2 packets upon PTO.
+
+const QuicTag k6PTO = TAG('6', 'P', 'T', 'O'); // Closes connection on 6
+ // consecutive PTOs.
+const QuicTag k7PTO = TAG('7', 'P', 'T', 'O'); // Closes connection on 7
+ // consecutive PTOs.
+const QuicTag k8PTO = TAG('8', 'P', 'T', 'O'); // Closes connection on 8
+ // consecutive PTOs.
+const QuicTag kPTOS = TAG('P', 'T', 'O', 'S'); // Skip packet number before
+ // sending the last PTO.
+const QuicTag kPTOA = TAG('P', 'T', 'O', 'A'); // Do not add max ack delay
+ // when computing PTO timeout
+ // if an immediate ACK is
+ // expected.
+const QuicTag kPEB1 = TAG('P', 'E', 'B', '1'); // Start exponential backoff
+ // since 1st PTO.
+const QuicTag kPEB2 = TAG('P', 'E', 'B', '2'); // Start exponential backoff
+ // since 2nd PTO.
+const QuicTag kPVS1 = TAG('P', 'V', 'S', '1'); // Use 2 * rttvar when
+ // calculating PTO timeout.
+const QuicTag kPAG1 = TAG('P', 'A', 'G', '1'); // Make 1st PTO more aggressive
+const QuicTag kPAG2 = TAG('P', 'A', 'G', '2'); // Make first 2 PTOs more
+ // aggressive
+const QuicTag kPSDA = TAG('P', 'S', 'D', 'A'); // Use standard deviation when
+ // calculating PTO timeout.
+const QuicTag kPLE1 = TAG('P', 'L', 'E', '1'); // Arm the 1st PTO with
+ // earliest in flight sent time
+ // and at least 0.5*srtt from
+ // last sent packet.
+const QuicTag kPLE2 = TAG('P', 'L', 'E', '2'); // Arm the 1st PTO with
+ // earliest in flight sent time
+ // and at least 1.5*srtt from
+ // last sent packet.
+const QuicTag kAPTO = TAG('A', 'P', 'T', 'O'); // Use 1.5 * initial RTT before
+ // any RTT sample is available.
+
+const QuicTag kELDT = TAG('E', 'L', 'D', 'T'); // Enable Loss Detection Tuning
+
+// TODO(haoyuewang) Remove RVCM option once
+// --quic_remove_connection_migration_connection_option_v2 is deprecated.
+const QuicTag kRVCM = TAG('R', 'V', 'C', 'M'); // Validate the new address
+ // upon client address change.
+
+// Optional support of truncated Connection IDs. If sent by a peer, the value
+// is the minimum number of bytes allowed for the connection ID sent to the
+// peer.
+const QuicTag kTCID = TAG('T', 'C', 'I', 'D'); // Connection ID truncation.
+
+// Multipath option.
+const QuicTag kMPTH = TAG('M', 'P', 'T', 'H'); // Enable multipath.
+
+const QuicTag kNCMR = TAG('N', 'C', 'M', 'R'); // Do not attempt connection
+ // migration.
+
+// Allows disabling defer_send_in_response_to_packets in QuicConnection.
+const QuicTag kDFER = TAG('D', 'F', 'E', 'R'); // Do not defer sending.
+
+// Disable Pacing offload option.
+const QuicTag kNPCO = TAG('N', 'P', 'C', 'O'); // No pacing offload.
+
+// Enable bandwidth resumption experiment.
+const QuicTag kBWRE = TAG('B', 'W', 'R', 'E'); // Bandwidth resumption.
+const QuicTag kBWMX = TAG('B', 'W', 'M', 'X'); // Max bandwidth resumption.
+const QuicTag kBWRS = TAG('B', 'W', 'R', 'S'); // Server bandwidth resumption.
+const QuicTag kBWS2 = TAG('B', 'W', 'S', '2'); // Server bw resumption v2.
+const QuicTag kBWS3 = TAG('B', 'W', 'S', '3'); // QUIC Initial CWND - Control.
+const QuicTag kBWS4 = TAG('B', 'W', 'S', '4'); // QUIC Initial CWND - Enabled.
+const QuicTag kBWS5 = TAG('B', 'W', 'S', '5'); // QUIC Initial CWND up and down
+const QuicTag kBWS6 = TAG('B', 'W', 'S', '6'); // QUIC Initial CWND - Enabled
+ // with 0.5 * default
+ // multiplier.
+const QuicTag kBWP0 = TAG('B', 'W', 'P', '0'); // QUIC Initial CWND - SPDY
+ // priority 0.
+const QuicTag kBWP1 = TAG('B', 'W', 'P', '1'); // QUIC Initial CWND - SPDY
+ // priorities 0 and 1.
+const QuicTag kBWP2 = TAG('B', 'W', 'P', '2'); // QUIC Initial CWND - SPDY
+ // priorities 0, 1 and 2.
+const QuicTag kBWP3 = TAG('B', 'W', 'P', '3'); // QUIC Initial CWND - SPDY
+ // priorities 0, 1, 2 and 3.
+const QuicTag kBWP4 = TAG('B', 'W', 'P', '4'); // QUIC Initial CWND - SPDY
+ // priorities >= 0, 1, 2, 3 and
+ // 4.
+const QuicTag kBWG4 = TAG('B', 'W', 'G', '4'); // QUIC Initial CWND -
+ // Bandwidth model 1.
+const QuicTag kBWG7 = TAG('B', 'W', 'G', '7'); // QUIC Initial CWND -
+ // Bandwidth model 2.
+const QuicTag kBWG8 = TAG('B', 'W', 'G', '8'); // QUIC Initial CWND -
+ // Bandwidth model 3.
+const QuicTag kBWS7 = TAG('B', 'W', 'S', '7'); // QUIC Initial CWND - Enabled
+ // with 0.75 * default
+ // multiplier.
+const QuicTag kBWM3 = TAG('B', 'W', 'M', '3'); // Consider overshooting if
+ // bytes lost after bandwidth
+ // resumption * 3 > IW.
+const QuicTag kBWM4 = TAG('B', 'W', 'M', '4'); // Consider overshooting if
+ // bytes lost after bandwidth
+ // resumption * 4 > IW.
+const QuicTag kICW1 = TAG('I', 'C', 'W', '1'); // Max initial congestion window
+ // 100.
+const QuicTag kDTOS = TAG('D', 'T', 'O', 'S'); // Enable overshooting
+ // detection.
+
+const QuicTag kFIDT = TAG('F', 'I', 'D', 'T'); // Extend idle timer by PTO
+ // instead of the whole idle
+ // timeout.
+
+const QuicTag k3AFF = TAG('3', 'A', 'F', 'F'); // 3 anti amplification factor.
+const QuicTag k10AF = TAG('1', '0', 'A', 'F'); // 10 anti amplification factor.
+
+// Enable path MTU discovery experiment.
+const QuicTag kMTUH = TAG('M', 'T', 'U', 'H'); // High-target MTU discovery.
+const QuicTag kMTUL = TAG('M', 'T', 'U', 'L'); // Low-target MTU discovery.
+
+const QuicTag kNSLC = TAG('N', 'S', 'L', 'C'); // Always send connection close
+ // for idle timeout.
+const QuicTag kNCHP = TAG('N', 'C', 'H', 'P'); // No chaos protection.
+const QuicTag kNBPE = TAG('N', 'B', 'P', 'E'); // No BoringSSL Permutes
+ // TLS Extensions.
+
+// Proof types (i.e. certificate types)
+// NOTE: although it would be silly to do so, specifying both kX509 and kX59R
+// is allowed and is equivalent to specifying only kX509.
+const QuicTag kX509 = TAG('X', '5', '0', '9'); // X.509 certificate, all key
+ // types
+const QuicTag kX59R = TAG('X', '5', '9', 'R'); // X.509 certificate, RSA keys
+ // only
+const QuicTag kCHID = TAG('C', 'H', 'I', 'D'); // Channel ID.
+
+// Client hello tags
+const QuicTag kVER = TAG('V', 'E', 'R', '\0'); // Version
+const QuicTag kNONC = TAG('N', 'O', 'N', 'C'); // The client's nonce
+const QuicTag kNONP = TAG('N', 'O', 'N', 'P'); // The client's proof nonce
+const QuicTag kKEXS = TAG('K', 'E', 'X', 'S'); // Key exchange methods
+const QuicTag kAEAD = TAG('A', 'E', 'A', 'D'); // Authenticated
+ // encryption algorithms
+const QuicTag kCOPT = TAG('C', 'O', 'P', 'T'); // Connection options
+const QuicTag kCLOP = TAG('C', 'L', 'O', 'P'); // Client connection options
+const QuicTag kICSL = TAG('I', 'C', 'S', 'L'); // Idle network timeout
+const QuicTag kMIBS = TAG('M', 'I', 'D', 'S'); // Max incoming bidi streams
+const QuicTag kMIUS = TAG('M', 'I', 'U', 'S'); // Max incoming unidi streams
+const QuicTag kADE = TAG('A', 'D', 'E', 0); // Ack Delay Exponent (IETF
+ // QUIC ACK Frame Only).
+const QuicTag kIRTT = TAG('I', 'R', 'T', 'T'); // Estimated initial RTT in us.
+const QuicTag kTRTT = TAG('T', 'R', 'T', 'T'); // If server receives an rtt
+ // from an address token, set
+ // it as the initial rtt.
+const QuicTag kSNI = TAG('S', 'N', 'I', '\0'); // Server name
+ // indication
+const QuicTag kPUBS = TAG('P', 'U', 'B', 'S'); // Public key values
+const QuicTag kSCID = TAG('S', 'C', 'I', 'D'); // Server config id
+const QuicTag kORBT = TAG('O', 'B', 'I', 'T'); // Server orbit.
+const QuicTag kPDMD = TAG('P', 'D', 'M', 'D'); // Proof demand.
+const QuicTag kPROF = TAG('P', 'R', 'O', 'F'); // Proof (signature).
+const QuicTag kCCRT = TAG('C', 'C', 'R', 'T'); // Cached certificate
+const QuicTag kEXPY = TAG('E', 'X', 'P', 'Y'); // Expiry
+const QuicTag kSTTL = TAG('S', 'T', 'T', 'L'); // Server Config TTL
+const QuicTag kSFCW = TAG('S', 'F', 'C', 'W'); // Initial stream flow control
+ // receive window.
+const QuicTag kCFCW = TAG('C', 'F', 'C', 'W'); // Initial session/connection
+ // flow control receive window.
+const QuicTag kUAID = TAG('U', 'A', 'I', 'D'); // Client's User Agent ID.
+const QuicTag kXLCT = TAG('X', 'L', 'C', 'T'); // Expected leaf certificate.
+const QuicTag kQLVE = TAG('Q', 'L', 'V', 'E'); // Legacy Version
+ // Encapsulation.
+
+const QuicTag kPDP1 = TAG('P', 'D', 'P', '1'); // Path degrading triggered
+ // at 1PTO.
+
+const QuicTag kPDP2 = TAG('P', 'D', 'P', '2'); // Path degrading triggered
+ // at 2PTO.
+
+const QuicTag kPDP3 = TAG('P', 'D', 'P', '3'); // Path degrading triggered
+ // at 3PTO.
+
+const QuicTag kPDP5 = TAG('P', 'D', 'P', '5'); // Path degrading triggered
+ // at 5PTO.
+
+const QuicTag kQNZ2 = TAG('Q', 'N', 'Z', '2'); // Turn off QUIC crypto 0-RTT.
+
+const QuicTag kMAD = TAG('M', 'A', 'D', 0); // Max Ack Delay (IETF QUIC)
+
+const QuicTag kIGNP = TAG('I', 'G', 'N', 'P'); // Do not use PING only packet
+ // for RTT measure or
+ // congestion control.
+
+const QuicTag kSRWP = TAG('S', 'R', 'W', 'P'); // Enable retransmittable on
+ // wire PING (ROWP) on the
+ // server side.
+const QuicTag kGSR0 = TAG('G', 'S', 'R', '0'); // Selective Resumption
+
+const QuicTag kINVC = TAG('I', 'N', 'V', 'C'); // Send connection close for
+ // INVALID_VERSION
+
+// Client Hints triggers.
+const QuicTag kGWCH = TAG('G', 'W', 'C', 'H');
+const QuicTag kYTCH = TAG('Y', 'T', 'C', 'H');
+const QuicTag kACH0 = TAG('A', 'C', 'H', '0');
+
+// Rejection tags
+const QuicTag kRREJ = TAG('R', 'R', 'E', 'J'); // Reasons for server sending
+
+// Server hello tags
+const QuicTag kCADR = TAG('C', 'A', 'D', 'R'); // Client IP address and port
+const QuicTag kASAD = TAG('A', 'S', 'A', 'D'); // Alternate Server IP address
+ // and port.
+const QuicTag kSRST = TAG('S', 'R', 'S', 'T'); // Stateless reset token used
+ // in IETF public reset packet
+
+// CETV tags
+const QuicTag kCIDK = TAG('C', 'I', 'D', 'K'); // ChannelID key
+const QuicTag kCIDS = TAG('C', 'I', 'D', 'S'); // ChannelID signature
+
+// Public reset tags
+const QuicTag kRNON = TAG('R', 'N', 'O', 'N'); // Public reset nonce proof
+const QuicTag kRSEQ = TAG('R', 'S', 'E', 'Q'); // Rejected packet number
+
+// Universal tags
+const QuicTag kPAD = TAG('P', 'A', 'D', '\0'); // Padding
+
+// Stats collection tags
+const QuicTag kEPID = TAG('E', 'P', 'I', 'D'); // Endpoint identifier.
+
+// clang-format on
+
+// These tags have a special form so that they appear either at the beginning
+// or the end of a handshake message. Since handshake messages are sorted by
+// tag value, the tags with 0 at the end will sort first and those with 255 at
+// the end will sort last.
+//
+// The certificate chain should have a tag that will cause it to be sorted at
+// the end of any handshake messages because it's likely to be large and the
+// client might be able to get everything that it needs from the small values at
+// the beginning.
+//
+// Likewise tags with random values should be towards the beginning of the
+// message because the server mightn't hold state for a rejected client hello
+// and therefore the client may have issues reassembling the rejection message
+// in the event that it sent two client hellos.
+const QuicTag kServerNonceTag = TAG('S', 'N', 'O', 0); // The server's nonce
+const QuicTag kSourceAddressTokenTag =
+ TAG('S', 'T', 'K', 0); // Source-address token
+const QuicTag kCertificateTag = TAG('C', 'R', 'T', 255); // Certificate chain
+const QuicTag kCertificateSCTTag =
+ TAG('C', 'S', 'C', 'T'); // Signed cert timestamp (RFC6962) of leaf cert.
+
+#undef TAG
+
+const size_t kMaxEntries = 128; // Max number of entries in a message.
+
+const size_t kNonceSize = 32; // Size in bytes of the connection nonce.
+
+const size_t kOrbitSize = 8; // Number of bytes in an orbit value.
+
+// kProofSignatureLabel is prepended to the CHLO hash and server configs before
+// signing to avoid any cross-protocol attacks on the signature.
+const char kProofSignatureLabel[] = "QUIC CHLO and server config signature";
+
+// kClientHelloMinimumSize is the minimum size of a client hello. Client hellos
+// will have PAD tags added in order to ensure this minimum is met and client
+// hellos smaller than this will be an error. This minimum size reduces the
+// amplification factor of any mirror DoS attack.
+//
+// A client may pad an inchoate client hello to a size larger than
+// kClientHelloMinimumSize to make it more likely to receive a complete
+// rejection message.
+const size_t kClientHelloMinimumSize = 1024;
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CRYPTO_PROTOCOL_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_secret_boxer.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_secret_boxer.cc
new file mode 100644
index 00000000000..2be495d92cc
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_secret_boxer.cc
@@ -0,0 +1,146 @@
+// Copyright (c) 2013 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 "quiche/quic/core/crypto/crypto_secret_boxer.h"
+
+#include <cstdint>
+#include <string>
+
+#include "absl/strings/string_view.h"
+#include "openssl/aead.h"
+#include "openssl/err.h"
+#include "quiche/quic/core/crypto/quic_random.h"
+#include "quiche/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+// kSIVNonceSize contains the number of bytes of nonce in each AES-GCM-SIV box.
+// AES-GCM-SIV takes a 12-byte nonce and, since the messages are so small, each
+// key is good for more than 2^64 source-address tokens. See table 1 of
+// https://eprint.iacr.org/2017/168.pdf
+static const size_t kSIVNonceSize = 12;
+
+// AES-GCM-SIV comes in AES-128 and AES-256 flavours. The AES-256 version is
+// used here so that the key size matches the 256-bit XSalsa20 keys that we
+// used to use.
+static const size_t kBoxKeySize = 32;
+
+struct CryptoSecretBoxer::State {
+ // ctxs are the initialised AEAD contexts. These objects contain the
+ // scheduled AES state for each of the keys.
+ std::vector<bssl::UniquePtr<EVP_AEAD_CTX>> ctxs;
+};
+
+CryptoSecretBoxer::CryptoSecretBoxer() {}
+
+CryptoSecretBoxer::~CryptoSecretBoxer() {}
+
+// static
+size_t CryptoSecretBoxer::GetKeySize() { return kBoxKeySize; }
+
+// kAEAD is the AEAD used for boxing: AES-256-GCM-SIV.
+static const EVP_AEAD* (*const kAEAD)() = EVP_aead_aes_256_gcm_siv;
+
+bool CryptoSecretBoxer::SetKeys(const std::vector<std::string>& keys) {
+ if (keys.empty()) {
+ QUIC_LOG(DFATAL) << "No keys supplied!";
+ return false;
+ }
+ const EVP_AEAD* const aead = kAEAD();
+ std::unique_ptr<State> new_state(new State);
+
+ for (const std::string& key : keys) {
+ QUICHE_DCHECK_EQ(kBoxKeySize, key.size());
+ bssl::UniquePtr<EVP_AEAD_CTX> ctx(
+ EVP_AEAD_CTX_new(aead, reinterpret_cast<const uint8_t*>(key.data()),
+ key.size(), EVP_AEAD_DEFAULT_TAG_LENGTH));
+ if (!ctx) {
+ ERR_clear_error();
+ QUIC_LOG(DFATAL) << "EVP_AEAD_CTX_init failed";
+ return false;
+ }
+
+ new_state->ctxs.push_back(std::move(ctx));
+ }
+
+ QuicWriterMutexLock l(&lock_);
+ state_ = std::move(new_state);
+ return true;
+}
+
+std::string CryptoSecretBoxer::Box(QuicRandom* rand,
+ absl::string_view plaintext) const {
+ // The box is formatted as:
+ // 12 bytes of random nonce
+ // n bytes of ciphertext
+ // 16 bytes of authenticator
+ size_t out_len =
+ kSIVNonceSize + plaintext.size() + EVP_AEAD_max_overhead(kAEAD());
+
+ std::string ret;
+ ret.resize(out_len);
+ uint8_t* out = reinterpret_cast<uint8_t*>(const_cast<char*>(ret.data()));
+
+ // Write kSIVNonceSize bytes of random nonce to the beginning of the output
+ // buffer.
+ rand->RandBytes(out, kSIVNonceSize);
+ const uint8_t* const nonce = out;
+ out += kSIVNonceSize;
+ out_len -= kSIVNonceSize;
+
+ size_t bytes_written;
+ {
+ QuicReaderMutexLock l(&lock_);
+ if (!EVP_AEAD_CTX_seal(state_->ctxs[0].get(), out, &bytes_written, out_len,
+ nonce, kSIVNonceSize,
+ reinterpret_cast<const uint8_t*>(plaintext.data()),
+ plaintext.size(), nullptr, 0)) {
+ ERR_clear_error();
+ QUIC_LOG(DFATAL) << "EVP_AEAD_CTX_seal failed";
+ return "";
+ }
+ }
+
+ QUICHE_DCHECK_EQ(out_len, bytes_written);
+ return ret;
+}
+
+bool CryptoSecretBoxer::Unbox(absl::string_view in_ciphertext,
+ std::string* out_storage,
+ absl::string_view* out) const {
+ if (in_ciphertext.size() < kSIVNonceSize) {
+ return false;
+ }
+
+ const uint8_t* const nonce =
+ reinterpret_cast<const uint8_t*>(in_ciphertext.data());
+ const uint8_t* const ciphertext = nonce + kSIVNonceSize;
+ const size_t ciphertext_len = in_ciphertext.size() - kSIVNonceSize;
+
+ out_storage->resize(ciphertext_len);
+
+ bool ok = false;
+ {
+ QuicReaderMutexLock l(&lock_);
+ for (const bssl::UniquePtr<EVP_AEAD_CTX>& ctx : state_->ctxs) {
+ size_t bytes_written;
+ if (EVP_AEAD_CTX_open(ctx.get(),
+ reinterpret_cast<uint8_t*>(
+ const_cast<char*>(out_storage->data())),
+ &bytes_written, ciphertext_len, nonce,
+ kSIVNonceSize, ciphertext, ciphertext_len, nullptr,
+ 0)) {
+ ok = true;
+ *out = absl::string_view(out_storage->data(), bytes_written);
+ break;
+ }
+
+ ERR_clear_error();
+ }
+ }
+
+ return ok;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_secret_boxer.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_secret_boxer.h
new file mode 100644
index 00000000000..e1927f21ba8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_secret_boxer.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2013 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CRYPTO_SECRET_BOXER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CRYPTO_SECRET_BOXER_H_
+
+#include <cstddef>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "quiche/quic/platform/api/quic_export.h"
+#include "quiche/quic/platform/api/quic_mutex.h"
+
+namespace quic {
+
+class QuicRandom;
+
+// CryptoSecretBoxer encrypts small chunks of plaintext (called 'boxing') and
+// then, later, can authenticate+decrypt the resulting boxes. This object is
+// thread-safe.
+class QUIC_EXPORT_PRIVATE CryptoSecretBoxer {
+ public:
+ CryptoSecretBoxer();
+ CryptoSecretBoxer(const CryptoSecretBoxer&) = delete;
+ CryptoSecretBoxer& operator=(const CryptoSecretBoxer&) = delete;
+ ~CryptoSecretBoxer();
+
+ // GetKeySize returns the number of bytes in a key.
+ static size_t GetKeySize();
+
+ // SetKeys sets a list of encryption keys. The first key in the list will be
+ // used by |Box|, but all supplied keys will be tried by |Unbox|, to handle
+ // key skew across the fleet. This must be called before |Box| or |Unbox|.
+ // Keys must be |GetKeySize()| bytes long. No change is made if any key is
+ // invalid, or if there are no keys supplied.
+ bool SetKeys(const std::vector<std::string>& keys);
+
+ // Box encrypts |plaintext| using a random nonce generated from |rand| and
+ // returns the resulting ciphertext. Since an authenticator and nonce are
+ // included, the result will be slightly larger than |plaintext|. The first
+ // key in the vector supplied to |SetKeys| will be used. |SetKeys| must be
+ // called before calling this method.
+ std::string Box(QuicRandom* rand, absl::string_view plaintext) const;
+
+ // Unbox takes the result of a previous call to |Box| in |ciphertext| and
+ // authenticates+decrypts it. If |ciphertext| cannot be decrypted with any of
+ // the supplied keys, the function returns false. Otherwise, |out_storage| is
+ // used to store the result and |out| is set to point into |out_storage| and
+ // contains the original plaintext.
+ bool Unbox(absl::string_view ciphertext, std::string* out_storage,
+ absl::string_view* out) const;
+
+ private:
+ struct State;
+
+ mutable QuicMutex lock_;
+
+ // state_ is an opaque pointer to whatever additional state the concrete
+ // implementation of CryptoSecretBoxer requires.
+ std::unique_ptr<State> state_ QUIC_GUARDED_BY(lock_);
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CRYPTO_SECRET_BOXER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_secret_boxer_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_secret_boxer_test.cc
new file mode 100644
index 00000000000..2c499fc5332
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_secret_boxer_test.cc
@@ -0,0 +1,82 @@
+// Copyright (c) 2013 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 "quiche/quic/core/crypto/crypto_secret_boxer.h"
+
+#include <string>
+
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/crypto/quic_random.h"
+#include "quiche/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+
+class CryptoSecretBoxerTest : public QuicTest {};
+
+TEST_F(CryptoSecretBoxerTest, BoxAndUnbox) {
+ absl::string_view message("hello world");
+
+ CryptoSecretBoxer boxer;
+ boxer.SetKeys({std::string(CryptoSecretBoxer::GetKeySize(), 0x11)});
+
+ const std::string box = boxer.Box(QuicRandom::GetInstance(), message);
+
+ std::string storage;
+ absl::string_view result;
+ EXPECT_TRUE(boxer.Unbox(box, &storage, &result));
+ EXPECT_EQ(result, message);
+
+ EXPECT_FALSE(boxer.Unbox(std::string(1, 'X') + box, &storage, &result));
+ EXPECT_FALSE(
+ boxer.Unbox(box.substr(1, std::string::npos), &storage, &result));
+ EXPECT_FALSE(boxer.Unbox(std::string(), &storage, &result));
+ EXPECT_FALSE(boxer.Unbox(
+ std::string(1, box[0] ^ 0x80) + box.substr(1, std::string::npos),
+ &storage, &result));
+}
+
+// Helper function to test whether one boxer can decode the output of another.
+static bool CanDecode(const CryptoSecretBoxer& decoder,
+ const CryptoSecretBoxer& encoder) {
+ absl::string_view message("hello world");
+ const std::string boxed = encoder.Box(QuicRandom::GetInstance(), message);
+ std::string storage;
+ absl::string_view result;
+ bool ok = decoder.Unbox(boxed, &storage, &result);
+ if (ok) {
+ EXPECT_EQ(result, message);
+ }
+ return ok;
+}
+
+TEST_F(CryptoSecretBoxerTest, MultipleKeys) {
+ std::string key_11(CryptoSecretBoxer::GetKeySize(), 0x11);
+ std::string key_12(CryptoSecretBoxer::GetKeySize(), 0x12);
+
+ CryptoSecretBoxer boxer_11, boxer_12, boxer;
+ EXPECT_TRUE(boxer_11.SetKeys({key_11}));
+ EXPECT_TRUE(boxer_12.SetKeys({key_12}));
+ EXPECT_TRUE(boxer.SetKeys({key_12, key_11}));
+
+ // Neither single-key boxer can decode the other's tokens.
+ EXPECT_FALSE(CanDecode(boxer_11, boxer_12));
+ EXPECT_FALSE(CanDecode(boxer_12, boxer_11));
+
+ // |boxer| encodes with the first key, which is key_12.
+ EXPECT_TRUE(CanDecode(boxer_12, boxer));
+ EXPECT_FALSE(CanDecode(boxer_11, boxer));
+
+ // The boxer with both keys can decode tokens from either single-key boxer.
+ EXPECT_TRUE(CanDecode(boxer, boxer_11));
+ EXPECT_TRUE(CanDecode(boxer, boxer_12));
+
+ // After we flush key_11 from |boxer|, it can no longer decode tokens from
+ // |boxer_11|.
+ EXPECT_TRUE(boxer.SetKeys({key_12}));
+ EXPECT_FALSE(CanDecode(boxer, boxer_11));
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_server_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_server_test.cc
new file mode 100644
index 00000000000..56f72836b9d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_server_test.cc
@@ -0,0 +1,1122 @@
+// Copyright (c) 2013 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 <algorithm>
+#include <cstdint>
+#include <memory>
+#include <ostream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "absl/base/macros.h"
+#include "absl/strings/escaping.h"
+#include "absl/strings/match.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "openssl/sha.h"
+#include "quiche/quic/core/crypto/cert_compressor.h"
+#include "quiche/quic/core/crypto/crypto_handshake.h"
+#include "quiche/quic/core/crypto/crypto_utils.h"
+#include "quiche/quic/core/crypto/proof_source.h"
+#include "quiche/quic/core/crypto/quic_crypto_server_config.h"
+#include "quiche/quic/core/crypto/quic_random.h"
+#include "quiche/quic/core/proto/crypto_server_config_proto.h"
+#include "quiche/quic/core/quic_socket_address_coder.h"
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/quic/platform/api/quic_flags.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/crypto_test_utils.h"
+#include "quiche/quic/test_tools/failing_proof_source.h"
+#include "quiche/quic/test_tools/mock_clock.h"
+#include "quiche/quic/test_tools/mock_random.h"
+#include "quiche/quic/test_tools/quic_crypto_server_config_peer.h"
+#include "quiche/quic/test_tools/quic_test_utils.h"
+#include "quiche/common/quiche_endian.h"
+
+namespace quic {
+namespace test {
+
+namespace {
+
+class DummyProofVerifierCallback : public ProofVerifierCallback {
+ public:
+ DummyProofVerifierCallback() {}
+ ~DummyProofVerifierCallback() override {}
+
+ void Run(bool /*ok*/, const std::string& /*error_details*/,
+ std::unique_ptr<ProofVerifyDetails>* /*details*/) override {
+ QUICHE_DCHECK(false);
+ }
+};
+
+const char kOldConfigId[] = "old-config-id";
+
+} // namespace
+
+struct TestParams {
+ friend std::ostream& operator<<(std::ostream& os, const TestParams& p) {
+ os << " versions: "
+ << ParsedQuicVersionVectorToString(p.supported_versions) << " }";
+ return os;
+ }
+
+ // Versions supported by client and server.
+ ParsedQuicVersionVector supported_versions;
+};
+
+// Used by ::testing::PrintToStringParamName().
+std::string PrintToString(const TestParams& p) {
+ std::string rv = ParsedQuicVersionVectorToString(p.supported_versions);
+ std::replace(rv.begin(), rv.end(), ',', '_');
+ return rv;
+}
+
+// Constructs various test permutations.
+std::vector<TestParams> GetTestParams() {
+ std::vector<TestParams> params;
+
+ // Start with all versions, remove highest on each iteration.
+ ParsedQuicVersionVector supported_versions = AllSupportedVersions();
+ while (!supported_versions.empty()) {
+ params.push_back({supported_versions});
+ supported_versions.erase(supported_versions.begin());
+ }
+
+ return params;
+}
+
+class CryptoServerTest : public QuicTestWithParam<TestParams> {
+ public:
+ CryptoServerTest()
+ : rand_(QuicRandom::GetInstance()),
+ client_address_(QuicIpAddress::Loopback4(), 1234),
+ client_version_(UnsupportedQuicVersion()),
+ config_(QuicCryptoServerConfig::TESTING, rand_,
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default()),
+ peer_(&config_),
+ compressed_certs_cache_(
+ QuicCompressedCertsCache::kQuicCompressedCertsCacheSize),
+ params_(new QuicCryptoNegotiatedParameters),
+ signed_config_(new QuicSignedServerConfig),
+ chlo_packet_size_(kDefaultMaxPacketSize) {
+ supported_versions_ = GetParam().supported_versions;
+ config_.set_enable_serving_sct(true);
+
+ client_version_ = supported_versions_.front();
+ client_version_label_ = CreateQuicVersionLabel(client_version_);
+ client_version_string_ =
+ std::string(reinterpret_cast<const char*>(&client_version_label_),
+ sizeof(client_version_label_));
+ }
+
+ void SetUp() override {
+ QuicCryptoServerConfig::ConfigOptions old_config_options;
+ old_config_options.id = kOldConfigId;
+ config_.AddDefaultConfig(rand_, &clock_, old_config_options);
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1000));
+ QuicServerConfigProtobuf primary_config =
+ config_.GenerateConfig(rand_, &clock_, config_options_);
+ primary_config.set_primary_time(clock_.WallNow().ToUNIXSeconds());
+ std::unique_ptr<CryptoHandshakeMessage> msg(
+ config_.AddConfig(primary_config, clock_.WallNow()));
+
+ absl::string_view orbit;
+ QUICHE_CHECK(msg->GetStringPiece(kORBT, &orbit));
+ QUICHE_CHECK_EQ(sizeof(orbit_), orbit.size());
+ memcpy(orbit_, orbit.data(), orbit.size());
+
+ char public_value[32];
+ memset(public_value, 42, sizeof(public_value));
+
+ nonce_hex_ = "#" + absl::BytesToHexString(GenerateNonce());
+ pub_hex_ = "#" + absl::BytesToHexString(
+ absl::string_view(public_value, sizeof(public_value)));
+
+ CryptoHandshakeMessage client_hello =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"CSCT", ""},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+ ShouldSucceed(client_hello);
+ // The message should be rejected because the source-address token is
+ // missing.
+ CheckRejectTag();
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE};
+ CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons));
+
+ absl::string_view srct;
+ ASSERT_TRUE(out_.GetStringPiece(kSourceAddressTokenTag, &srct));
+ srct_hex_ = "#" + absl::BytesToHexString(srct);
+
+ absl::string_view scfg;
+ ASSERT_TRUE(out_.GetStringPiece(kSCFG, &scfg));
+ server_config_ = CryptoFramer::ParseMessage(scfg);
+
+ absl::string_view scid;
+ ASSERT_TRUE(server_config_->GetStringPiece(kSCID, &scid));
+ scid_hex_ = "#" + absl::BytesToHexString(scid);
+
+ signed_config_ =
+ quiche::QuicheReferenceCountedPointer<QuicSignedServerConfig>(
+ new QuicSignedServerConfig());
+ QUICHE_DCHECK(signed_config_->chain.get() == nullptr);
+ }
+
+ // Helper used to accept the result of ValidateClientHello and pass
+ // it on to ProcessClientHello.
+ class ValidateCallback : public ValidateClientHelloResultCallback {
+ public:
+ ValidateCallback(CryptoServerTest* test, bool should_succeed,
+ const char* error_substr, bool* called)
+ : test_(test),
+ should_succeed_(should_succeed),
+ error_substr_(error_substr),
+ called_(called) {
+ *called_ = false;
+ }
+
+ void Run(quiche::QuicheReferenceCountedPointer<Result> result,
+ std::unique_ptr<ProofSource::Details> /* details */) override {
+ ASSERT_FALSE(*called_);
+ test_->ProcessValidationResult(std::move(result), should_succeed_,
+ error_substr_);
+ *called_ = true;
+ }
+
+ private:
+ CryptoServerTest* test_;
+ const bool should_succeed_;
+ const char* const error_substr_;
+ bool* called_;
+ };
+
+ void CheckServerHello(const CryptoHandshakeMessage& server_hello) {
+ QuicVersionLabelVector versions;
+ server_hello.GetVersionLabelList(kVER, &versions);
+ ASSERT_EQ(supported_versions_.size(), versions.size());
+ for (size_t i = 0; i < versions.size(); ++i) {
+ EXPECT_EQ(CreateQuicVersionLabel(supported_versions_[i]), versions[i]);
+ }
+
+ absl::string_view address;
+ ASSERT_TRUE(server_hello.GetStringPiece(kCADR, &address));
+ QuicSocketAddressCoder decoder;
+ ASSERT_TRUE(decoder.Decode(address.data(), address.size()));
+ EXPECT_EQ(client_address_.host(), decoder.ip());
+ EXPECT_EQ(client_address_.port(), decoder.port());
+ }
+
+ void ShouldSucceed(const CryptoHandshakeMessage& message) {
+ bool called = false;
+ QuicSocketAddress server_address(QuicIpAddress::Any4(), 5);
+ config_.ValidateClientHello(
+ message, client_address_, server_address,
+ supported_versions_.front().transport_version, &clock_, signed_config_,
+ std::make_unique<ValidateCallback>(this, true, "", &called));
+ EXPECT_TRUE(called);
+ }
+
+ void ShouldFailMentioning(const char* error_substr,
+ const CryptoHandshakeMessage& message) {
+ bool called = false;
+ ShouldFailMentioning(error_substr, message, &called);
+ EXPECT_TRUE(called);
+ }
+
+ void ShouldFailMentioning(const char* error_substr,
+ const CryptoHandshakeMessage& message,
+ bool* called) {
+ QuicSocketAddress server_address(QuicIpAddress::Any4(), 5);
+ config_.ValidateClientHello(
+ message, client_address_, server_address,
+ supported_versions_.front().transport_version, &clock_, signed_config_,
+ std::make_unique<ValidateCallback>(this, false, error_substr, called));
+ }
+
+ class ProcessCallback : public ProcessClientHelloResultCallback {
+ public:
+ ProcessCallback(
+ quiche::QuicheReferenceCountedPointer<ValidateCallback::Result> result,
+ bool should_succeed, const char* error_substr, bool* called,
+ CryptoHandshakeMessage* out)
+ : result_(std::move(result)),
+ should_succeed_(should_succeed),
+ error_substr_(error_substr),
+ called_(called),
+ out_(out) {
+ *called_ = false;
+ }
+
+ void Run(QuicErrorCode error, const std::string& error_details,
+ std::unique_ptr<CryptoHandshakeMessage> message,
+ std::unique_ptr<DiversificationNonce> /*diversification_nonce*/,
+ std::unique_ptr<ProofSource::Details> /*proof_source_details*/)
+ override {
+ if (should_succeed_) {
+ ASSERT_EQ(error, QUIC_NO_ERROR)
+ << "Message failed with error " << error_details << ": "
+ << result_->client_hello.DebugString();
+ } else {
+ ASSERT_NE(error, QUIC_NO_ERROR)
+ << "Message didn't fail: " << result_->client_hello.DebugString();
+ EXPECT_TRUE(absl::StrContains(error_details, error_substr_))
+ << error_substr_ << " not in " << error_details;
+ }
+ if (message != nullptr) {
+ *out_ = *message;
+ }
+ *called_ = true;
+ }
+
+ private:
+ const quiche::QuicheReferenceCountedPointer<ValidateCallback::Result>
+ result_;
+ const bool should_succeed_;
+ const char* const error_substr_;
+ bool* called_;
+ CryptoHandshakeMessage* out_;
+ };
+
+ void ProcessValidationResult(
+ quiche::QuicheReferenceCountedPointer<ValidateCallback::Result> result,
+ bool should_succeed, const char* error_substr) {
+ QuicSocketAddress server_address(QuicIpAddress::Any4(), 5);
+ bool called;
+ config_.ProcessClientHello(
+ result, /*reject_only=*/false,
+ /*connection_id=*/TestConnectionId(1), server_address, client_address_,
+ supported_versions_.front(), supported_versions_, &clock_, rand_,
+ &compressed_certs_cache_, params_, signed_config_,
+ /*total_framing_overhead=*/50, chlo_packet_size_,
+ std::make_unique<ProcessCallback>(result, should_succeed, error_substr,
+ &called, &out_));
+ EXPECT_TRUE(called);
+ }
+
+ std::string GenerateNonce() {
+ std::string nonce;
+ CryptoUtils::GenerateNonce(
+ clock_.WallNow(), rand_,
+ absl::string_view(reinterpret_cast<const char*>(orbit_),
+ sizeof(orbit_)),
+ &nonce);
+ return nonce;
+ }
+
+ void CheckRejectReasons(
+ const HandshakeFailureReason* expected_handshake_failures,
+ size_t expected_count) {
+ QuicTagVector reject_reasons;
+ static_assert(sizeof(QuicTag) == sizeof(uint32_t), "header out of sync");
+ QuicErrorCode error_code = out_.GetTaglist(kRREJ, &reject_reasons);
+ ASSERT_THAT(error_code, IsQuicNoError());
+
+ EXPECT_EQ(expected_count, reject_reasons.size());
+ for (size_t i = 0; i < reject_reasons.size(); ++i) {
+ EXPECT_EQ(static_cast<QuicTag>(expected_handshake_failures[i]),
+ reject_reasons[i]);
+ }
+ }
+
+ void CheckRejectTag() {
+ ASSERT_EQ(kREJ, out_.tag()) << QuicTagToString(out_.tag());
+ }
+
+ std::string XlctHexString() {
+ uint64_t xlct = crypto_test_utils::LeafCertHashForTesting();
+ return "#" + absl::BytesToHexString(absl::string_view(
+ reinterpret_cast<char*>(&xlct), sizeof(xlct)));
+ }
+
+ protected:
+ QuicRandom* const rand_;
+ MockRandom rand_for_id_generation_;
+ MockClock clock_;
+ QuicSocketAddress client_address_;
+ ParsedQuicVersionVector supported_versions_;
+ ParsedQuicVersion client_version_;
+ QuicVersionLabel client_version_label_;
+ std::string client_version_string_;
+ QuicCryptoServerConfig config_;
+ QuicCryptoServerConfigPeer peer_;
+ QuicCompressedCertsCache compressed_certs_cache_;
+ QuicCryptoServerConfig::ConfigOptions config_options_;
+ quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_;
+ quiche::QuicheReferenceCountedPointer<QuicSignedServerConfig> signed_config_;
+ CryptoHandshakeMessage out_;
+ uint8_t orbit_[kOrbitSize];
+ size_t chlo_packet_size_;
+
+ // These strings contain hex escaped values from the server suitable for using
+ // when constructing client hello messages.
+ std::string nonce_hex_, pub_hex_, srct_hex_, scid_hex_;
+ std::unique_ptr<CryptoHandshakeMessage> server_config_;
+};
+
+INSTANTIATE_TEST_SUITE_P(CryptoServerTests, CryptoServerTest,
+ ::testing::ValuesIn(GetTestParams()),
+ ::testing::PrintToStringParamName());
+
+TEST_P(CryptoServerTest, BadSNI) {
+ // clang-format off
+ std::vector<std::string> badSNIs = {
+ "",
+ "#00",
+ "#ff00",
+ "127.0.0.1",
+ "ffee::1",
+ };
+ // clang-format on
+
+ for (const std::string& bad_sni : badSNIs) {
+ CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"}, {"SNI", bad_sni}, {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+ ShouldFailMentioning("SNI", msg);
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE};
+ CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons));
+ }
+
+ // Check that SNIs without dots are allowed
+ CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"}, {"SNI", "foo"}, {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+ ShouldSucceed(msg);
+}
+
+TEST_P(CryptoServerTest, DefaultCert) {
+ // Check that the server replies with a default certificate when no SNI is
+ // specified. The CHLO is constructed to generate a REJ with certs, so must
+ // not contain a valid STK, and must include PDMD.
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"PDMD", "X509"},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ ShouldSucceed(msg);
+ absl::string_view cert, proof, cert_sct;
+ EXPECT_TRUE(out_.GetStringPiece(kCertificateTag, &cert));
+ EXPECT_TRUE(out_.GetStringPiece(kPROF, &proof));
+ EXPECT_TRUE(out_.GetStringPiece(kCertificateSCTTag, &cert_sct));
+ EXPECT_NE(0u, cert.size());
+ EXPECT_NE(0u, proof.size());
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE};
+ CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons));
+ EXPECT_LT(0u, cert_sct.size());
+}
+
+TEST_P(CryptoServerTest, RejectTooLarge) {
+ // Check that the server replies with no certificate when a CHLO is
+ // constructed with a PDMD but no SKT when the REJ would be too large.
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"PDMD", "X509"},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ // The REJ will be larger than the CHLO so no PROF or CRT will be sent.
+ config_.set_chlo_multiplier(1);
+
+ ShouldSucceed(msg);
+ absl::string_view cert, proof, cert_sct;
+ EXPECT_FALSE(out_.GetStringPiece(kCertificateTag, &cert));
+ EXPECT_FALSE(out_.GetStringPiece(kPROF, &proof));
+ EXPECT_FALSE(out_.GetStringPiece(kCertificateSCTTag, &cert_sct));
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE};
+ CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, RejectNotTooLarge) {
+ // When the CHLO packet is large enough, ensure that a full REJ is sent.
+ chlo_packet_size_ *= 5;
+
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"PDMD", "X509"},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ // The REJ will be larger than the CHLO so no PROF or CRT will be sent.
+ config_.set_chlo_multiplier(1);
+
+ ShouldSucceed(msg);
+ absl::string_view cert, proof, cert_sct;
+ EXPECT_TRUE(out_.GetStringPiece(kCertificateTag, &cert));
+ EXPECT_TRUE(out_.GetStringPiece(kPROF, &proof));
+ EXPECT_TRUE(out_.GetStringPiece(kCertificateSCTTag, &cert_sct));
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE};
+ CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, RejectTooLargeButValidSTK) {
+ // Check that the server replies with no certificate when a CHLO is
+ // constructed with a PDMD but no SKT when the REJ would be too large.
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"#004b5453", srct_hex_},
+ {"PDMD", "X509"},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ // The REJ will be larger than the CHLO so no PROF or CRT will be sent.
+ config_.set_chlo_multiplier(1);
+
+ ShouldSucceed(msg);
+ absl::string_view cert, proof, cert_sct;
+ EXPECT_TRUE(out_.GetStringPiece(kCertificateTag, &cert));
+ EXPECT_TRUE(out_.GetStringPiece(kPROF, &proof));
+ EXPECT_TRUE(out_.GetStringPiece(kCertificateSCTTag, &cert_sct));
+ EXPECT_NE(0u, cert.size());
+ EXPECT_NE(0u, proof.size());
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE};
+ CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, BadSourceAddressToken) {
+ // Invalid source-address tokens should be ignored.
+ // clang-format off
+ static const char* const kBadSourceAddressTokens[] = {
+ "",
+ "foo",
+ "#0000",
+ "#0000000000000000000000000000000000000000",
+ };
+ // clang-format on
+
+ for (size_t i = 0; i < ABSL_ARRAYSIZE(kBadSourceAddressTokens); i++) {
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"STK", kBadSourceAddressTokens[i]},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+ ShouldSucceed(msg);
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE};
+ CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons));
+ }
+}
+
+TEST_P(CryptoServerTest, BadClientNonce) {
+ // clang-format off
+ static const char* const kBadNonces[] = {
+ "",
+ "#0000",
+ "#0000000000000000000000000000000000000000",
+ };
+ // clang-format on
+
+ for (size_t i = 0; i < ABSL_ARRAYSIZE(kBadNonces); i++) {
+ // Invalid nonces should be ignored, in an inchoate CHLO.
+
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"NONC", kBadNonces[i]},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ ShouldSucceed(msg);
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE};
+ CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons));
+
+ // Invalid nonces should result in CLIENT_NONCE_INVALID_FAILURE.
+ CryptoHandshakeMessage msg1 =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", scid_hex_},
+ {"#004b5453", srct_hex_},
+ {"PUBS", pub_hex_},
+ {"NONC", kBadNonces[i]},
+ {"NONP", kBadNonces[i]},
+ {"XLCT", XlctHexString()},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ ShouldSucceed(msg1);
+
+ CheckRejectTag();
+ const HandshakeFailureReason kRejectReasons1[] = {
+ CLIENT_NONCE_INVALID_FAILURE};
+ CheckRejectReasons(kRejectReasons1, ABSL_ARRAYSIZE(kRejectReasons1));
+ }
+}
+
+TEST_P(CryptoServerTest, NoClientNonce) {
+ // No client nonces should result in INCHOATE_HELLO_FAILURE.
+
+ CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"}, {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ ShouldSucceed(msg);
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE};
+ CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons));
+
+ CryptoHandshakeMessage msg1 =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", scid_hex_},
+ {"#004b5453", srct_hex_},
+ {"PUBS", pub_hex_},
+ {"XLCT", XlctHexString()},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ ShouldSucceed(msg1);
+ CheckRejectTag();
+ const HandshakeFailureReason kRejectReasons1[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE};
+ CheckRejectReasons(kRejectReasons1, ABSL_ARRAYSIZE(kRejectReasons1));
+}
+
+TEST_P(CryptoServerTest, DowngradeAttack) {
+ if (supported_versions_.size() == 1) {
+ // No downgrade attack is possible if the server only supports one version.
+ return;
+ }
+ // Set the client's preferred version to a supported version that
+ // is not the "current" version (supported_versions_.front()).
+ std::string bad_version =
+ ParsedQuicVersionToString(supported_versions_.back());
+
+ CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"}, {"VER\0", bad_version}}, kClientHelloMinimumSize);
+
+ ShouldFailMentioning("Downgrade", msg);
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE};
+ CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, CorruptServerConfig) {
+ // This tests corrupted server config.
+ CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", (std::string(1, 'X') + scid_hex_)},
+ {"#004b5453", srct_hex_},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ ShouldSucceed(msg);
+ CheckRejectTag();
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE};
+ CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, CorruptSourceAddressToken) {
+ // This tests corrupted source address token.
+ CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", scid_hex_},
+ {"#004b5453", (std::string(1, 'X') + srct_hex_)},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"XLCT", XlctHexString()},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ ShouldSucceed(msg);
+ CheckRejectTag();
+ const HandshakeFailureReason kRejectReasons[] = {
+ SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE};
+ CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, CorruptSourceAddressTokenIsStillAccepted) {
+ // This tests corrupted source address token.
+ CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", scid_hex_},
+ {"#004b5453", (std::string(1, 'X') + srct_hex_)},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"XLCT", XlctHexString()},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ config_.set_validate_source_address_token(false);
+
+ ShouldSucceed(msg);
+ EXPECT_EQ(kSHLO, out_.tag());
+}
+
+TEST_P(CryptoServerTest, CorruptClientNonceAndSourceAddressToken) {
+ // This test corrupts client nonce and source address token.
+ CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", scid_hex_},
+ {"#004b5453", (std::string(1, 'X') + srct_hex_)},
+ {"PUBS", pub_hex_},
+ {"NONC", (std::string(1, 'X') + nonce_hex_)},
+ {"XLCT", XlctHexString()},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ ShouldSucceed(msg);
+ CheckRejectTag();
+ const HandshakeFailureReason kRejectReasons[] = {
+ SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, CLIENT_NONCE_INVALID_FAILURE};
+ CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, CorruptMultipleTags) {
+ // This test corrupts client nonce, server nonce and source address token.
+ CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", scid_hex_},
+ {"#004b5453", (std::string(1, 'X') + srct_hex_)},
+ {"PUBS", pub_hex_},
+ {"NONC", (std::string(1, 'X') + nonce_hex_)},
+ {"NONP", (std::string(1, 'X') + nonce_hex_)},
+ {"SNO\0", (std::string(1, 'X') + nonce_hex_)},
+ {"XLCT", XlctHexString()},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ ShouldSucceed(msg);
+ CheckRejectTag();
+
+ const HandshakeFailureReason kRejectReasons[] = {
+ SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, CLIENT_NONCE_INVALID_FAILURE};
+ CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, NoServerNonce) {
+ // When no server nonce is present and no strike register is configured,
+ // the CHLO should be rejected.
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", scid_hex_},
+ {"#004b5453", srct_hex_},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"NONP", nonce_hex_},
+ {"XLCT", XlctHexString()},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ ShouldSucceed(msg);
+
+ // Even without a server nonce, this ClientHello should be accepted in
+ // version 33.
+ ASSERT_EQ(kSHLO, out_.tag());
+ CheckServerHello(out_);
+}
+
+TEST_P(CryptoServerTest, ProofForSuppliedServerConfig) {
+ client_address_ = QuicSocketAddress(QuicIpAddress::Loopback6(), 1234);
+
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"PDMD", "X509"},
+ {"SCID", kOldConfigId},
+ {"#004b5453", srct_hex_},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"NONP", "123456789012345678901234567890"},
+ {"VER\0", client_version_string_},
+ {"XLCT", XlctHexString()}},
+ kClientHelloMinimumSize);
+
+ ShouldSucceed(msg);
+ // The message should be rejected because the source-address token is no
+ // longer valid.
+ CheckRejectTag();
+ const HandshakeFailureReason kRejectReasons[] = {
+ SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE};
+ CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons));
+
+ absl::string_view cert, proof, scfg_str;
+ EXPECT_TRUE(out_.GetStringPiece(kCertificateTag, &cert));
+ EXPECT_TRUE(out_.GetStringPiece(kPROF, &proof));
+ EXPECT_TRUE(out_.GetStringPiece(kSCFG, &scfg_str));
+ std::unique_ptr<CryptoHandshakeMessage> scfg(
+ CryptoFramer::ParseMessage(scfg_str));
+ absl::string_view scid;
+ EXPECT_TRUE(scfg->GetStringPiece(kSCID, &scid));
+ EXPECT_NE(scid, kOldConfigId);
+
+ // Get certs from compressed certs.
+ std::vector<std::string> cached_certs;
+
+ std::vector<std::string> certs;
+ ASSERT_TRUE(CertCompressor::DecompressChain(cert, cached_certs, &certs));
+
+ // Check that the proof in the REJ message is valid.
+ std::unique_ptr<ProofVerifier> proof_verifier(
+ crypto_test_utils::ProofVerifierForTesting());
+ std::unique_ptr<ProofVerifyContext> verify_context(
+ crypto_test_utils::ProofVerifyContextForTesting());
+ std::unique_ptr<ProofVerifyDetails> details;
+ std::string error_details;
+ std::unique_ptr<ProofVerifierCallback> callback(
+ new DummyProofVerifierCallback());
+ const std::string chlo_hash =
+ CryptoUtils::HashHandshakeMessage(msg, Perspective::IS_SERVER);
+ EXPECT_EQ(QUIC_SUCCESS,
+ proof_verifier->VerifyProof(
+ "test.example.com", 443, (std::string(scfg_str)),
+ client_version_.transport_version, chlo_hash, certs, "",
+ (std::string(proof)), verify_context.get(), &error_details,
+ &details, std::move(callback)));
+}
+
+TEST_P(CryptoServerTest, RejectInvalidXlct) {
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", scid_hex_},
+ {"#004b5453", srct_hex_},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"VER\0", client_version_string_},
+ {"XLCT", "#0102030405060708"}},
+ kClientHelloMinimumSize);
+
+ // If replay protection isn't disabled, then
+ // QuicCryptoServerConfig::EvaluateClientHello will leave info.unique as false
+ // and cause ProcessClientHello to exit early (and generate a REJ message).
+ config_.set_replay_protection(false);
+
+ ShouldSucceed(msg);
+
+ const HandshakeFailureReason kRejectReasons[] = {
+ INVALID_EXPECTED_LEAF_CERTIFICATE};
+
+ CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, ValidXlct) {
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", scid_hex_},
+ {"#004b5453", srct_hex_},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"VER\0", client_version_string_},
+ {"XLCT", XlctHexString()}},
+ kClientHelloMinimumSize);
+
+ // If replay protection isn't disabled, then
+ // QuicCryptoServerConfig::EvaluateClientHello will leave info.unique as false
+ // and cause ProcessClientHello to exit early (and generate a REJ message).
+ config_.set_replay_protection(false);
+
+ ShouldSucceed(msg);
+ EXPECT_EQ(kSHLO, out_.tag());
+}
+
+TEST_P(CryptoServerTest, NonceInSHLO) {
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", scid_hex_},
+ {"#004b5453", srct_hex_},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"VER\0", client_version_string_},
+ {"XLCT", XlctHexString()}},
+ kClientHelloMinimumSize);
+
+ // If replay protection isn't disabled, then
+ // QuicCryptoServerConfig::EvaluateClientHello will leave info.unique as false
+ // and cause ProcessClientHello to exit early (and generate a REJ message).
+ config_.set_replay_protection(false);
+
+ ShouldSucceed(msg);
+ EXPECT_EQ(kSHLO, out_.tag());
+
+ absl::string_view nonce;
+ EXPECT_TRUE(out_.GetStringPiece(kServerNonceTag, &nonce));
+}
+
+TEST_P(CryptoServerTest, ProofSourceFailure) {
+ // Install a ProofSource which will unconditionally fail
+ peer_.ResetProofSource(std::unique_ptr<ProofSource>(new FailingProofSource));
+
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", scid_hex_},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"PDMD", "X509"},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ // Just ensure that we don't crash as occurred in b/33916924.
+ ShouldFailMentioning("", msg);
+}
+
+// Regression test for crbug.com/723604
+// For 2RTT, if the first CHLO from the client contains hashes of cached
+// certs (stored in CCRT tag) but the second CHLO does not, then the second REJ
+// from the server should not contain hashes of cached certs.
+TEST_P(CryptoServerTest, TwoRttServerDropCachedCerts) {
+ // Send inchoate CHLO to get cert chain from server. This CHLO is only for
+ // the purpose of getting the server's certs; it is not part of the 2RTT
+ // handshake.
+ CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"}, {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+ ShouldSucceed(msg);
+
+ // Decompress cert chain from server to individual certs.
+ absl::string_view certs_compressed;
+ ASSERT_TRUE(out_.GetStringPiece(kCertificateTag, &certs_compressed));
+ ASSERT_NE(0u, certs_compressed.size());
+ std::vector<std::string> certs;
+ ASSERT_TRUE(CertCompressor::DecompressChain(certs_compressed,
+ /*cached_certs=*/{}, &certs));
+
+ // Start 2-RTT. Client sends CHLO with bad source-address token and hashes of
+ // the certs, which tells the server that the client has cached those certs.
+ config_.set_chlo_multiplier(1);
+ const char kBadSourceAddressToken[] = "";
+ msg.SetStringPiece(kSourceAddressTokenTag, kBadSourceAddressToken);
+ std::vector<uint64_t> hashes(certs.size());
+ for (size_t i = 0; i < certs.size(); ++i) {
+ hashes[i] = QuicUtils::QuicUtils::FNV1a_64_Hash(certs[i]);
+ }
+ msg.SetVector(kCCRT, hashes);
+ ShouldSucceed(msg);
+
+ // Server responds with inchoate REJ containing valid source-address token.
+ absl::string_view srct;
+ ASSERT_TRUE(out_.GetStringPiece(kSourceAddressTokenTag, &srct));
+
+ // Client now drops cached certs; sends CHLO with updated source-address
+ // token but no hashes of certs.
+ msg.SetStringPiece(kSourceAddressTokenTag, srct);
+ msg.Erase(kCCRT);
+ ShouldSucceed(msg);
+
+ // Server response's cert chain should not contain hashes of
+ // previously-cached certs.
+ ASSERT_TRUE(out_.GetStringPiece(kCertificateTag, &certs_compressed));
+ ASSERT_NE(0u, certs_compressed.size());
+ ASSERT_TRUE(CertCompressor::DecompressChain(certs_compressed,
+ /*cached_certs=*/{}, &certs));
+}
+
+class CryptoServerConfigGenerationTest : public QuicTest {};
+
+TEST_F(CryptoServerConfigGenerationTest, Determinism) {
+ // Test that using a deterministic PRNG causes the server-config to be
+ // deterministic.
+
+ MockRandom rand_a, rand_b;
+ const QuicCryptoServerConfig::ConfigOptions options;
+ MockClock clock;
+
+ QuicCryptoServerConfig a(QuicCryptoServerConfig::TESTING, &rand_a,
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default());
+ QuicCryptoServerConfig b(QuicCryptoServerConfig::TESTING, &rand_b,
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default());
+ std::unique_ptr<CryptoHandshakeMessage> scfg_a(
+ a.AddDefaultConfig(&rand_a, &clock, options));
+ std::unique_ptr<CryptoHandshakeMessage> scfg_b(
+ b.AddDefaultConfig(&rand_b, &clock, options));
+
+ ASSERT_EQ(scfg_a->DebugString(), scfg_b->DebugString());
+}
+
+TEST_F(CryptoServerConfigGenerationTest, SCIDVaries) {
+ // This test ensures that the server config ID varies for different server
+ // configs.
+
+ MockRandom rand_a, rand_b;
+ const QuicCryptoServerConfig::ConfigOptions options;
+ MockClock clock;
+
+ QuicCryptoServerConfig a(QuicCryptoServerConfig::TESTING, &rand_a,
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default());
+ rand_b.ChangeValue();
+ QuicCryptoServerConfig b(QuicCryptoServerConfig::TESTING, &rand_b,
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default());
+ std::unique_ptr<CryptoHandshakeMessage> scfg_a(
+ a.AddDefaultConfig(&rand_a, &clock, options));
+ std::unique_ptr<CryptoHandshakeMessage> scfg_b(
+ b.AddDefaultConfig(&rand_b, &clock, options));
+
+ absl::string_view scid_a, scid_b;
+ EXPECT_TRUE(scfg_a->GetStringPiece(kSCID, &scid_a));
+ EXPECT_TRUE(scfg_b->GetStringPiece(kSCID, &scid_b));
+
+ EXPECT_NE(scid_a, scid_b);
+}
+
+TEST_F(CryptoServerConfigGenerationTest, SCIDIsHashOfServerConfig) {
+ MockRandom rand_a;
+ const QuicCryptoServerConfig::ConfigOptions options;
+ MockClock clock;
+
+ QuicCryptoServerConfig a(QuicCryptoServerConfig::TESTING, &rand_a,
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default());
+ std::unique_ptr<CryptoHandshakeMessage> scfg(
+ a.AddDefaultConfig(&rand_a, &clock, options));
+
+ absl::string_view scid;
+ EXPECT_TRUE(scfg->GetStringPiece(kSCID, &scid));
+ // Need to take a copy of |scid| has we're about to call |Erase|.
+ const std::string scid_str(scid);
+
+ scfg->Erase(kSCID);
+ scfg->MarkDirty();
+ const QuicData& serialized(scfg->GetSerialized());
+
+ uint8_t digest[SHA256_DIGEST_LENGTH];
+ SHA256(reinterpret_cast<const uint8_t*>(serialized.data()),
+ serialized.length(), digest);
+
+ // scid is a SHA-256 hash, truncated to 16 bytes.
+ ASSERT_EQ(scid.size(), 16u);
+ EXPECT_EQ(0, memcmp(digest, scid_str.c_str(), scid.size()));
+}
+
+// Those tests were declared incorrectly and thus never ran in first place.
+// TODO(b/147891553): figure out if we should fix or delete those.
+#if 0
+
+class CryptoServerTestNoConfig : public CryptoServerTest {
+ public:
+ void SetUp() override {
+ // Deliberately don't add a config so that we can test this situation.
+ }
+};
+
+INSTANTIATE_TEST_SUITE_P(CryptoServerTestsNoConfig,
+ CryptoServerTestNoConfig,
+ ::testing::ValuesIn(GetTestParams()),
+ ::testing::PrintToStringParamName());
+
+TEST_P(CryptoServerTestNoConfig, DontCrash) {
+ CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"}, {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ ShouldFailMentioning("No config", msg);
+
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE};
+ CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons));
+}
+
+class CryptoServerTestOldVersion : public CryptoServerTest {
+ public:
+ void SetUp() override {
+ client_version_ = supported_versions_.back();
+ client_version_string_ = ParsedQuicVersionToString(client_version_);
+ CryptoServerTest::SetUp();
+ }
+};
+
+INSTANTIATE_TEST_SUITE_P(CryptoServerTestsOldVersion,
+ CryptoServerTestOldVersion,
+ ::testing::ValuesIn(GetTestParams()),
+ ::testing::PrintToStringParamName());
+
+TEST_P(CryptoServerTestOldVersion, ServerIgnoresXlct) {
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", scid_hex_},
+ {"#004b5453", srct_hex_},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"VER\0", client_version_string_},
+ {"XLCT", "#0100000000000000"}},
+ kClientHelloMinimumSize);
+
+ // If replay protection isn't disabled, then
+ // QuicCryptoServerConfig::EvaluateClientHello will leave info.unique as false
+ // and cause ProcessClientHello to exit early (and generate a REJ message).
+ config_.set_replay_protection(false);
+
+ ShouldSucceed(msg);
+ EXPECT_EQ(kSHLO, out_.tag());
+}
+
+TEST_P(CryptoServerTestOldVersion, XlctNotRequired) {
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", scid_hex_},
+ {"#004b5453", srct_hex_},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ // If replay protection isn't disabled, then
+ // QuicCryptoServerConfig::EvaluateClientHello will leave info.unique as false
+ // and cause ProcessClientHello to exit early (and generate a REJ message).
+ config_.set_replay_protection(false);
+
+ ShouldSucceed(msg);
+ EXPECT_EQ(kSHLO, out_.tag());
+}
+
+#endif // 0
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_utils.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_utils.cc
new file mode 100644
index 00000000000..334c4d8d054
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_utils.cc
@@ -0,0 +1,790 @@
+// Copyright (c) 2013 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 "quiche/quic/core/crypto/crypto_utils.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "absl/base/macros.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "openssl/bytestring.h"
+#include "openssl/hkdf.h"
+#include "openssl/mem.h"
+#include "openssl/sha.h"
+#include "quiche/quic/core/crypto/aes_128_gcm_12_decrypter.h"
+#include "quiche/quic/core/crypto/aes_128_gcm_12_encrypter.h"
+#include "quiche/quic/core/crypto/aes_128_gcm_decrypter.h"
+#include "quiche/quic/core/crypto/aes_128_gcm_encrypter.h"
+#include "quiche/quic/core/crypto/crypto_handshake.h"
+#include "quiche/quic/core/crypto/crypto_protocol.h"
+#include "quiche/quic/core/crypto/null_decrypter.h"
+#include "quiche/quic/core/crypto/null_encrypter.h"
+#include "quiche/quic/core/crypto/quic_decrypter.h"
+#include "quiche/quic/core/crypto/quic_encrypter.h"
+#include "quiche/quic/core/crypto/quic_hkdf.h"
+#include "quiche/quic/core/crypto/quic_random.h"
+#include "quiche/quic/core/quic_connection_id.h"
+#include "quiche/quic/core/quic_constants.h"
+#include "quiche/quic/core/quic_data_writer.h"
+#include "quiche/quic/core/quic_time.h"
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/quic/core/quic_versions.h"
+#include "quiche/quic/platform/api/quic_bug_tracker.h"
+#include "quiche/quic/platform/api/quic_logging.h"
+#include "quiche/common/quiche_endian.h"
+
+namespace quic {
+
+namespace {
+
+// Implements the HKDF-Expand-Label function as defined in section 7.1 of RFC
+// 8446. The HKDF-Expand-Label function takes 4 explicit arguments (Secret,
+// Label, Context, and Length), as well as implicit PRF which is the hash
+// function negotiated by TLS. Its use in QUIC (as needed by the QUIC stack,
+// instead of as used internally by the TLS stack) is only for deriving initial
+// secrets for obfuscation, for calculating packet protection keys and IVs from
+// the corresponding packet protection secret and key update in the same quic
+// session. None of these uses need a Context (a zero-length context is
+// provided), so this argument is omitted here.
+//
+// The implicit PRF is explicitly passed into HkdfExpandLabel as |prf|; the
+// Secret, Label, and Length are passed in as |secret|, |label|, and
+// |out_len|, respectively. The resulting expanded secret is returned.
+std::vector<uint8_t> HkdfExpandLabel(const EVP_MD* prf,
+ const std::vector<uint8_t>& secret,
+ const std::string& label, size_t out_len) {
+ bssl::ScopedCBB quic_hkdf_label;
+ CBB inner_label;
+ const char label_prefix[] = "tls13 ";
+ // 20 = size(u16) + size(u8) + len("tls13 ") +
+ // max_len("client in", "server in", "quicv2 key", ... ) +
+ // size(u8);
+ static const size_t max_quic_hkdf_label_length = 20;
+ if (!CBB_init(quic_hkdf_label.get(), max_quic_hkdf_label_length) ||
+ !CBB_add_u16(quic_hkdf_label.get(), out_len) ||
+ !CBB_add_u8_length_prefixed(quic_hkdf_label.get(), &inner_label) ||
+ !CBB_add_bytes(&inner_label,
+ reinterpret_cast<const uint8_t*>(label_prefix),
+ ABSL_ARRAYSIZE(label_prefix) - 1) ||
+ !CBB_add_bytes(&inner_label,
+ reinterpret_cast<const uint8_t*>(label.data()),
+ label.size()) ||
+ // Zero length |Context|.
+ !CBB_add_u8(quic_hkdf_label.get(), 0) ||
+ !CBB_flush(quic_hkdf_label.get())) {
+ QUIC_LOG(ERROR) << "Building HKDF label failed";
+ return std::vector<uint8_t>();
+ }
+ std::vector<uint8_t> out;
+ out.resize(out_len);
+ if (!HKDF_expand(out.data(), out_len, prf, secret.data(), secret.size(),
+ CBB_data(quic_hkdf_label.get()),
+ CBB_len(quic_hkdf_label.get()))) {
+ QUIC_LOG(ERROR) << "Running HKDF-Expand-Label failed";
+ return std::vector<uint8_t>();
+ }
+ return out;
+}
+
+} // namespace
+
+const std::string getLabelForVersion(const ParsedQuicVersion& version,
+ const absl::string_view& predicate) {
+ static_assert(SupportedVersions().size() == 6u,
+ "Supported versions out of sync with HKDF labels");
+ if (version == ParsedQuicVersion::V2Draft01()) {
+ return absl::StrCat("quicv2 ", predicate);
+ } else {
+ return absl::StrCat("quic ", predicate);
+ }
+}
+
+void CryptoUtils::InitializeCrypterSecrets(
+ const EVP_MD* prf, const std::vector<uint8_t>& pp_secret,
+ const ParsedQuicVersion& version, QuicCrypter* crypter) {
+ SetKeyAndIV(prf, pp_secret, version, crypter);
+ std::vector<uint8_t> header_protection_key = GenerateHeaderProtectionKey(
+ prf, pp_secret, version, crypter->GetKeySize());
+ crypter->SetHeaderProtectionKey(
+ absl::string_view(reinterpret_cast<char*>(header_protection_key.data()),
+ header_protection_key.size()));
+}
+
+void CryptoUtils::SetKeyAndIV(const EVP_MD* prf,
+ const std::vector<uint8_t>& pp_secret,
+ const ParsedQuicVersion& version,
+ QuicCrypter* crypter) {
+ std::vector<uint8_t> key =
+ HkdfExpandLabel(prf, pp_secret, getLabelForVersion(version, "key"),
+ crypter->GetKeySize());
+ std::vector<uint8_t> iv = HkdfExpandLabel(
+ prf, pp_secret, getLabelForVersion(version, "iv"), crypter->GetIVSize());
+ crypter->SetKey(
+ absl::string_view(reinterpret_cast<char*>(key.data()), key.size()));
+ crypter->SetIV(
+ absl::string_view(reinterpret_cast<char*>(iv.data()), iv.size()));
+}
+
+std::vector<uint8_t> CryptoUtils::GenerateHeaderProtectionKey(
+ const EVP_MD* prf, const std::vector<uint8_t>& pp_secret,
+ const ParsedQuicVersion& version, size_t out_len) {
+ return HkdfExpandLabel(prf, pp_secret, getLabelForVersion(version, "hp"),
+ out_len);
+}
+
+std::vector<uint8_t> CryptoUtils::GenerateNextKeyPhaseSecret(
+ const EVP_MD* prf, const ParsedQuicVersion& version,
+ const std::vector<uint8_t>& current_secret) {
+ return HkdfExpandLabel(prf, current_secret, getLabelForVersion(version, "ku"),
+ current_secret.size());
+}
+
+namespace {
+
+// Salt from https://tools.ietf.org/html/draft-ietf-quic-tls-29#section-5.2
+const uint8_t kDraft29InitialSalt[] = {0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2,
+ 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61,
+ 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99};
+const uint8_t kRFCv1InitialSalt[] = {0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34,
+ 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8,
+ 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a};
+const uint8_t kV2Draft01InitialSalt[] = {
+ 0xa7, 0x07, 0xc2, 0x03, 0xa5, 0x9b, 0x47, 0x18, 0x4a, 0x1d,
+ 0x62, 0xca, 0x57, 0x04, 0x06, 0xea, 0x7a, 0xe3, 0xe5, 0xd3};
+
+// Salts used by deployed versions of QUIC. When introducing a new version,
+// generate a new salt by running `openssl rand -hex 20`.
+
+// Salt to use for initial obfuscators in version Q050.
+const uint8_t kQ050Salt[] = {0x50, 0x45, 0x74, 0xef, 0xd0, 0x66, 0xfe,
+ 0x2f, 0x9d, 0x94, 0x5c, 0xfc, 0xdb, 0xd3,
+ 0xa7, 0xf0, 0xd3, 0xb5, 0x6b, 0x45};
+// Salt to use for initial obfuscators in
+// ParsedQuicVersion::ReservedForNegotiation().
+const uint8_t kReservedForNegotiationSalt[] = {
+ 0xf9, 0x64, 0xbf, 0x45, 0x3a, 0x1f, 0x1b, 0x80, 0xa5, 0xf8,
+ 0x82, 0x03, 0x77, 0xd4, 0xaf, 0xca, 0x58, 0x0e, 0xe7, 0x43};
+
+const uint8_t* InitialSaltForVersion(const ParsedQuicVersion& version,
+ size_t* out_len) {
+ static_assert(SupportedVersions().size() == 6u,
+ "Supported versions out of sync with initial encryption salts");
+ if (version == ParsedQuicVersion::V2Draft01()) {
+ *out_len = ABSL_ARRAYSIZE(kV2Draft01InitialSalt);
+ return kV2Draft01InitialSalt;
+ } else if (version == ParsedQuicVersion::RFCv1()) {
+ *out_len = ABSL_ARRAYSIZE(kRFCv1InitialSalt);
+ return kRFCv1InitialSalt;
+ } else if (version == ParsedQuicVersion::Draft29()) {
+ *out_len = ABSL_ARRAYSIZE(kDraft29InitialSalt);
+ return kDraft29InitialSalt;
+ } else if (version == ParsedQuicVersion::Q050()) {
+ *out_len = ABSL_ARRAYSIZE(kQ050Salt);
+ return kQ050Salt;
+ } else if (version == ParsedQuicVersion::ReservedForNegotiation()) {
+ *out_len = ABSL_ARRAYSIZE(kReservedForNegotiationSalt);
+ return kReservedForNegotiationSalt;
+ }
+ QUIC_BUG(quic_bug_10699_1)
+ << "No initial obfuscation salt for version " << version;
+ *out_len = ABSL_ARRAYSIZE(kReservedForNegotiationSalt);
+ return kReservedForNegotiationSalt;
+}
+
+const char kPreSharedKeyLabel[] = "QUIC PSK";
+
+// Retry Integrity Protection Keys and Nonces.
+// https://tools.ietf.org/html/draft-ietf-quic-tls-29#section-5.8
+// When introducing a new Google version, generate a new key by running
+// `openssl rand -hex 16`.
+const uint8_t kDraft29RetryIntegrityKey[] = {0xcc, 0xce, 0x18, 0x7e, 0xd0, 0x9a,
+ 0x09, 0xd0, 0x57, 0x28, 0x15, 0x5a,
+ 0x6c, 0xb9, 0x6b, 0xe1};
+const uint8_t kDraft29RetryIntegrityNonce[] = {
+ 0xe5, 0x49, 0x30, 0xf9, 0x7f, 0x21, 0x36, 0xf0, 0x53, 0x0a, 0x8c, 0x1c};
+const uint8_t kRFCv1RetryIntegrityKey[] = {0xbe, 0x0c, 0x69, 0x0b, 0x9f, 0x66,
+ 0x57, 0x5a, 0x1d, 0x76, 0x6b, 0x54,
+ 0xe3, 0x68, 0xc8, 0x4e};
+const uint8_t kRFCv1RetryIntegrityNonce[] = {
+ 0x46, 0x15, 0x99, 0xd3, 0x5d, 0x63, 0x2b, 0xf2, 0x23, 0x98, 0x25, 0xbb};
+const uint8_t kV2Draft01RetryIntegrityKey[] = {
+ 0xba, 0x85, 0x8d, 0xc7, 0xb4, 0x3d, 0xe5, 0xdb,
+ 0xf8, 0x76, 0x17, 0xff, 0x4a, 0xb2, 0x53, 0xdb};
+const uint8_t kV2Draft01RetryIntegrityNonce[] = {
+ 0x14, 0x1b, 0x99, 0xc2, 0x39, 0xb0, 0x3e, 0x78, 0x5d, 0x6a, 0x2e, 0x9f};
+// Retry integrity key used by ParsedQuicVersion::ReservedForNegotiation().
+const uint8_t kReservedForNegotiationRetryIntegrityKey[] = {
+ 0xf2, 0xcd, 0x8f, 0xe0, 0x36, 0xd0, 0x25, 0x35,
+ 0x03, 0xe6, 0x7c, 0x7b, 0xd2, 0x44, 0xca, 0xd9};
+// When introducing a new Google version, generate a new nonce by running
+// `openssl rand -hex 12`.
+// Retry integrity nonce used by ParsedQuicVersion::ReservedForNegotiation().
+const uint8_t kReservedForNegotiationRetryIntegrityNonce[] = {
+ 0x35, 0x9f, 0x16, 0xd1, 0xed, 0x80, 0x90, 0x8e, 0xec, 0x85, 0xc4, 0xd6};
+
+bool RetryIntegrityKeysForVersion(const ParsedQuicVersion& version,
+ absl::string_view* key,
+ absl::string_view* nonce) {
+ static_assert(SupportedVersions().size() == 6u,
+ "Supported versions out of sync with retry integrity keys");
+ if (!version.UsesTls()) {
+ QUIC_BUG(quic_bug_10699_2)
+ << "Attempted to get retry integrity keys for invalid version "
+ << version;
+ return false;
+ } else if (version == ParsedQuicVersion::V2Draft01()) {
+ *key = absl::string_view(
+ reinterpret_cast<const char*>(kV2Draft01RetryIntegrityKey),
+ ABSL_ARRAYSIZE(kV2Draft01RetryIntegrityKey));
+ *nonce = absl::string_view(
+ reinterpret_cast<const char*>(kV2Draft01RetryIntegrityNonce),
+ ABSL_ARRAYSIZE(kV2Draft01RetryIntegrityNonce));
+ return true;
+ } else if (version == ParsedQuicVersion::RFCv1()) {
+ *key = absl::string_view(
+ reinterpret_cast<const char*>(kRFCv1RetryIntegrityKey),
+ ABSL_ARRAYSIZE(kRFCv1RetryIntegrityKey));
+ *nonce = absl::string_view(
+ reinterpret_cast<const char*>(kRFCv1RetryIntegrityNonce),
+ ABSL_ARRAYSIZE(kRFCv1RetryIntegrityNonce));
+ return true;
+ } else if (version == ParsedQuicVersion::Draft29()) {
+ *key = absl::string_view(
+ reinterpret_cast<const char*>(kDraft29RetryIntegrityKey),
+ ABSL_ARRAYSIZE(kDraft29RetryIntegrityKey));
+ *nonce = absl::string_view(
+ reinterpret_cast<const char*>(kDraft29RetryIntegrityNonce),
+ ABSL_ARRAYSIZE(kDraft29RetryIntegrityNonce));
+ return true;
+ } else if (version == ParsedQuicVersion::ReservedForNegotiation()) {
+ *key = absl::string_view(
+ reinterpret_cast<const char*>(kReservedForNegotiationRetryIntegrityKey),
+ ABSL_ARRAYSIZE(kReservedForNegotiationRetryIntegrityKey));
+ *nonce = absl::string_view(
+ reinterpret_cast<const char*>(
+ kReservedForNegotiationRetryIntegrityNonce),
+ ABSL_ARRAYSIZE(kReservedForNegotiationRetryIntegrityNonce));
+ return true;
+ }
+ QUIC_BUG(quic_bug_10699_3)
+ << "Attempted to get retry integrity keys for version " << version;
+ return false;
+}
+
+} // namespace
+
+// static
+void CryptoUtils::CreateInitialObfuscators(Perspective perspective,
+ ParsedQuicVersion version,
+ QuicConnectionId connection_id,
+ CrypterPair* crypters) {
+ QUIC_DLOG(INFO) << "Creating "
+ << (perspective == Perspective::IS_CLIENT ? "client"
+ : "server")
+ << " crypters for version " << version << " with CID "
+ << connection_id;
+ if (!version.UsesInitialObfuscators()) {
+ crypters->encrypter = std::make_unique<NullEncrypter>(perspective);
+ crypters->decrypter = std::make_unique<NullDecrypter>(perspective);
+ return;
+ }
+ QUIC_BUG_IF(quic_bug_12871_1, !QuicUtils::IsConnectionIdValidForVersion(
+ connection_id, version.transport_version))
+ << "CreateTlsInitialCrypters: attempted to use connection ID "
+ << connection_id << " which is invalid with version " << version;
+ const EVP_MD* hash = EVP_sha256();
+
+ size_t salt_len;
+ const uint8_t* salt = InitialSaltForVersion(version, &salt_len);
+ std::vector<uint8_t> handshake_secret;
+ handshake_secret.resize(EVP_MAX_MD_SIZE);
+ size_t handshake_secret_len;
+ const bool hkdf_extract_success =
+ HKDF_extract(handshake_secret.data(), &handshake_secret_len, hash,
+ reinterpret_cast<const uint8_t*>(connection_id.data()),
+ connection_id.length(), salt, salt_len);
+ QUIC_BUG_IF(quic_bug_12871_2, !hkdf_extract_success)
+ << "HKDF_extract failed when creating initial crypters";
+ handshake_secret.resize(handshake_secret_len);
+
+ const std::string client_label = "client in";
+ const std::string server_label = "server in";
+ std::string encryption_label, decryption_label;
+ if (perspective == Perspective::IS_CLIENT) {
+ encryption_label = client_label;
+ decryption_label = server_label;
+ } else {
+ encryption_label = server_label;
+ decryption_label = client_label;
+ }
+ std::vector<uint8_t> encryption_secret = HkdfExpandLabel(
+ hash, handshake_secret, encryption_label, EVP_MD_size(hash));
+ crypters->encrypter = std::make_unique<Aes128GcmEncrypter>();
+ InitializeCrypterSecrets(hash, encryption_secret, version,
+ crypters->encrypter.get());
+
+ std::vector<uint8_t> decryption_secret = HkdfExpandLabel(
+ hash, handshake_secret, decryption_label, EVP_MD_size(hash));
+ crypters->decrypter = std::make_unique<Aes128GcmDecrypter>();
+ InitializeCrypterSecrets(hash, decryption_secret, version,
+ crypters->decrypter.get());
+}
+
+// static
+bool CryptoUtils::ValidateRetryIntegrityTag(
+ ParsedQuicVersion version, QuicConnectionId original_connection_id,
+ absl::string_view retry_without_tag, absl::string_view integrity_tag) {
+ unsigned char computed_integrity_tag[kRetryIntegrityTagLength];
+ if (integrity_tag.length() != ABSL_ARRAYSIZE(computed_integrity_tag)) {
+ QUIC_BUG(quic_bug_10699_4)
+ << "Invalid retry integrity tag length " << integrity_tag.length();
+ return false;
+ }
+ char retry_pseudo_packet[kMaxIncomingPacketSize + 256];
+ QuicDataWriter writer(ABSL_ARRAYSIZE(retry_pseudo_packet),
+ retry_pseudo_packet);
+ if (!writer.WriteLengthPrefixedConnectionId(original_connection_id)) {
+ QUIC_BUG(quic_bug_10699_5)
+ << "Failed to write original connection ID in retry pseudo packet";
+ return false;
+ }
+ if (!writer.WriteStringPiece(retry_without_tag)) {
+ QUIC_BUG(quic_bug_10699_6)
+ << "Failed to write retry without tag in retry pseudo packet";
+ return false;
+ }
+ absl::string_view key;
+ absl::string_view nonce;
+ if (!RetryIntegrityKeysForVersion(version, &key, &nonce)) {
+ // RetryIntegrityKeysForVersion already logs failures.
+ return false;
+ }
+ Aes128GcmEncrypter crypter;
+ crypter.SetKey(key);
+ absl::string_view associated_data(writer.data(), writer.length());
+ absl::string_view plaintext; // Plaintext is empty.
+ if (!crypter.Encrypt(nonce, associated_data, plaintext,
+ computed_integrity_tag)) {
+ QUIC_BUG(quic_bug_10699_7) << "Failed to compute retry integrity tag";
+ return false;
+ }
+ if (CRYPTO_memcmp(computed_integrity_tag, integrity_tag.data(),
+ ABSL_ARRAYSIZE(computed_integrity_tag)) != 0) {
+ QUIC_DLOG(ERROR) << "Failed to validate retry integrity tag";
+ return false;
+ }
+ return true;
+}
+
+// static
+void CryptoUtils::GenerateNonce(QuicWallTime now, QuicRandom* random_generator,
+ absl::string_view orbit, std::string* nonce) {
+ // a 4-byte timestamp + 28 random bytes.
+ nonce->reserve(kNonceSize);
+ nonce->resize(kNonceSize);
+
+ uint32_t gmt_unix_time = static_cast<uint32_t>(now.ToUNIXSeconds());
+ // The time in the nonce must be encoded in big-endian because the
+ // strike-register depends on the nonces being ordered by time.
+ (*nonce)[0] = static_cast<char>(gmt_unix_time >> 24);
+ (*nonce)[1] = static_cast<char>(gmt_unix_time >> 16);
+ (*nonce)[2] = static_cast<char>(gmt_unix_time >> 8);
+ (*nonce)[3] = static_cast<char>(gmt_unix_time);
+ size_t bytes_written = 4;
+
+ if (orbit.size() == 8) {
+ memcpy(&(*nonce)[bytes_written], orbit.data(), orbit.size());
+ bytes_written += orbit.size();
+ }
+
+ random_generator->RandBytes(&(*nonce)[bytes_written],
+ kNonceSize - bytes_written);
+}
+
+// static
+bool CryptoUtils::DeriveKeys(
+ const ParsedQuicVersion& version, absl::string_view premaster_secret,
+ QuicTag aead, absl::string_view client_nonce,
+ absl::string_view server_nonce, absl::string_view pre_shared_key,
+ const std::string& hkdf_input, Perspective perspective,
+ Diversification diversification, CrypterPair* crypters,
+ std::string* subkey_secret) {
+ // If the connection is using PSK, concatenate it with the pre-master secret.
+ std::unique_ptr<char[]> psk_premaster_secret;
+ if (!pre_shared_key.empty()) {
+ const absl::string_view label(kPreSharedKeyLabel);
+ const size_t psk_premaster_secret_size = label.size() + 1 +
+ pre_shared_key.size() + 8 +
+ premaster_secret.size() + 8;
+
+ psk_premaster_secret = std::make_unique<char[]>(psk_premaster_secret_size);
+ QuicDataWriter writer(psk_premaster_secret_size, psk_premaster_secret.get(),
+ quiche::HOST_BYTE_ORDER);
+
+ if (!writer.WriteStringPiece(label) || !writer.WriteUInt8(0) ||
+ !writer.WriteStringPiece(pre_shared_key) ||
+ !writer.WriteUInt64(pre_shared_key.size()) ||
+ !writer.WriteStringPiece(premaster_secret) ||
+ !writer.WriteUInt64(premaster_secret.size()) ||
+ writer.remaining() != 0) {
+ return false;
+ }
+
+ premaster_secret = absl::string_view(psk_premaster_secret.get(),
+ psk_premaster_secret_size);
+ }
+
+ crypters->encrypter = QuicEncrypter::Create(version, aead);
+ crypters->decrypter = QuicDecrypter::Create(version, aead);
+
+ size_t key_bytes = crypters->encrypter->GetKeySize();
+ size_t nonce_prefix_bytes = crypters->encrypter->GetNoncePrefixSize();
+ if (version.UsesInitialObfuscators()) {
+ nonce_prefix_bytes = crypters->encrypter->GetIVSize();
+ }
+ size_t subkey_secret_bytes =
+ subkey_secret == nullptr ? 0 : premaster_secret.length();
+
+ absl::string_view nonce = client_nonce;
+ std::string nonce_storage;
+ if (!server_nonce.empty()) {
+ nonce_storage = std::string(client_nonce) + std::string(server_nonce);
+ nonce = nonce_storage;
+ }
+
+ QuicHKDF hkdf(premaster_secret, nonce, hkdf_input, key_bytes,
+ nonce_prefix_bytes, subkey_secret_bytes);
+
+ // Key derivation depends on the key diversification method being employed.
+ // both the client and the server support never doing key diversification.
+ // The server also supports immediate diversification, and the client
+ // supports pending diversification.
+ switch (diversification.mode()) {
+ case Diversification::NEVER: {
+ if (perspective == Perspective::IS_SERVER) {
+ if (!crypters->encrypter->SetKey(hkdf.server_write_key()) ||
+ !crypters->encrypter->SetNoncePrefixOrIV(version,
+ hkdf.server_write_iv()) ||
+ !crypters->encrypter->SetHeaderProtectionKey(
+ hkdf.server_hp_key()) ||
+ !crypters->decrypter->SetKey(hkdf.client_write_key()) ||
+ !crypters->decrypter->SetNoncePrefixOrIV(version,
+ hkdf.client_write_iv()) ||
+ !crypters->decrypter->SetHeaderProtectionKey(
+ hkdf.client_hp_key())) {
+ return false;
+ }
+ } else {
+ if (!crypters->encrypter->SetKey(hkdf.client_write_key()) ||
+ !crypters->encrypter->SetNoncePrefixOrIV(version,
+ hkdf.client_write_iv()) ||
+ !crypters->encrypter->SetHeaderProtectionKey(
+ hkdf.client_hp_key()) ||
+ !crypters->decrypter->SetKey(hkdf.server_write_key()) ||
+ !crypters->decrypter->SetNoncePrefixOrIV(version,
+ hkdf.server_write_iv()) ||
+ !crypters->decrypter->SetHeaderProtectionKey(
+ hkdf.server_hp_key())) {
+ return false;
+ }
+ }
+ break;
+ }
+ case Diversification::PENDING: {
+ if (perspective == Perspective::IS_SERVER) {
+ QUIC_BUG(quic_bug_10699_8)
+ << "Pending diversification is only for clients.";
+ return false;
+ }
+
+ if (!crypters->encrypter->SetKey(hkdf.client_write_key()) ||
+ !crypters->encrypter->SetNoncePrefixOrIV(version,
+ hkdf.client_write_iv()) ||
+ !crypters->encrypter->SetHeaderProtectionKey(hkdf.client_hp_key()) ||
+ !crypters->decrypter->SetPreliminaryKey(hkdf.server_write_key()) ||
+ !crypters->decrypter->SetNoncePrefixOrIV(version,
+ hkdf.server_write_iv()) ||
+ !crypters->decrypter->SetHeaderProtectionKey(hkdf.server_hp_key())) {
+ return false;
+ }
+ break;
+ }
+ case Diversification::NOW: {
+ if (perspective == Perspective::IS_CLIENT) {
+ QUIC_BUG(quic_bug_10699_9)
+ << "Immediate diversification is only for servers.";
+ return false;
+ }
+
+ std::string key, nonce_prefix;
+ QuicDecrypter::DiversifyPreliminaryKey(
+ hkdf.server_write_key(), hkdf.server_write_iv(),
+ *diversification.nonce(), key_bytes, nonce_prefix_bytes, &key,
+ &nonce_prefix);
+ if (!crypters->decrypter->SetKey(hkdf.client_write_key()) ||
+ !crypters->decrypter->SetNoncePrefixOrIV(version,
+ hkdf.client_write_iv()) ||
+ !crypters->decrypter->SetHeaderProtectionKey(hkdf.client_hp_key()) ||
+ !crypters->encrypter->SetKey(key) ||
+ !crypters->encrypter->SetNoncePrefixOrIV(version, nonce_prefix) ||
+ !crypters->encrypter->SetHeaderProtectionKey(hkdf.server_hp_key())) {
+ return false;
+ }
+ break;
+ }
+ default:
+ QUICHE_DCHECK(false);
+ }
+
+ if (subkey_secret != nullptr) {
+ *subkey_secret = std::string(hkdf.subkey_secret());
+ }
+
+ return true;
+}
+
+// static
+uint64_t CryptoUtils::ComputeLeafCertHash(absl::string_view cert) {
+ return QuicUtils::FNV1a_64_Hash(cert);
+}
+
+QuicErrorCode CryptoUtils::ValidateServerHello(
+ const CryptoHandshakeMessage& server_hello,
+ const ParsedQuicVersionVector& negotiated_versions,
+ std::string* error_details) {
+ QUICHE_DCHECK(error_details != nullptr);
+
+ if (server_hello.tag() != kSHLO) {
+ *error_details = "Bad tag";
+ return QUIC_INVALID_CRYPTO_MESSAGE_TYPE;
+ }
+
+ QuicVersionLabelVector supported_version_labels;
+ if (server_hello.GetVersionLabelList(kVER, &supported_version_labels) !=
+ QUIC_NO_ERROR) {
+ *error_details = "server hello missing version list";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ return ValidateServerHelloVersions(supported_version_labels,
+ negotiated_versions, error_details);
+}
+
+QuicErrorCode CryptoUtils::ValidateServerHelloVersions(
+ const QuicVersionLabelVector& server_versions,
+ const ParsedQuicVersionVector& negotiated_versions,
+ std::string* error_details) {
+ if (!negotiated_versions.empty()) {
+ bool mismatch = server_versions.size() != negotiated_versions.size();
+ for (size_t i = 0; i < server_versions.size() && !mismatch; ++i) {
+ mismatch =
+ server_versions[i] != CreateQuicVersionLabel(negotiated_versions[i]);
+ }
+ // The server sent a list of supported versions, and the connection
+ // reports that there was a version negotiation during the handshake.
+ // Ensure that these two lists are identical.
+ if (mismatch) {
+ *error_details = absl::StrCat(
+ "Downgrade attack detected: ServerVersions(", server_versions.size(),
+ ")[", QuicVersionLabelVectorToString(server_versions, ",", 30),
+ "] NegotiatedVersions(", negotiated_versions.size(), ")[",
+ ParsedQuicVersionVectorToString(negotiated_versions, ",", 30), "]");
+ return QUIC_VERSION_NEGOTIATION_MISMATCH;
+ }
+ }
+ return QUIC_NO_ERROR;
+}
+
+QuicErrorCode CryptoUtils::ValidateClientHello(
+ const CryptoHandshakeMessage& client_hello, ParsedQuicVersion version,
+ const ParsedQuicVersionVector& supported_versions,
+ std::string* error_details) {
+ if (client_hello.tag() != kCHLO) {
+ *error_details = "Bad tag";
+ return QUIC_INVALID_CRYPTO_MESSAGE_TYPE;
+ }
+
+ // If the client's preferred version is not the version we are currently
+ // speaking, then the client went through a version negotiation. In this
+ // case, we need to make sure that we actually do not support this version
+ // and that it wasn't a downgrade attack.
+ QuicVersionLabel client_version_label;
+ if (client_hello.GetVersionLabel(kVER, &client_version_label) !=
+ QUIC_NO_ERROR) {
+ *error_details = "client hello missing version list";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+ return ValidateClientHelloVersion(client_version_label, version,
+ supported_versions, error_details);
+}
+
+QuicErrorCode CryptoUtils::ValidateClientHelloVersion(
+ QuicVersionLabel client_version, ParsedQuicVersion connection_version,
+ const ParsedQuicVersionVector& supported_versions,
+ std::string* error_details) {
+ if (client_version != CreateQuicVersionLabel(connection_version)) {
+ // Check to see if |client_version| is actually on the supported versions
+ // list. If not, the server doesn't support that version and it's not a
+ // downgrade attack.
+ for (size_t i = 0; i < supported_versions.size(); ++i) {
+ if (client_version == CreateQuicVersionLabel(supported_versions[i])) {
+ *error_details = absl::StrCat(
+ "Downgrade attack detected: ClientVersion[",
+ QuicVersionLabelToString(client_version), "] ConnectionVersion[",
+ ParsedQuicVersionToString(connection_version),
+ "] SupportedVersions(", supported_versions.size(), ")[",
+ ParsedQuicVersionVectorToString(supported_versions, ",", 30), "]");
+ return QUIC_VERSION_NEGOTIATION_MISMATCH;
+ }
+ }
+ }
+ return QUIC_NO_ERROR;
+}
+
+// static
+bool CryptoUtils::ValidateChosenVersion(
+ const QuicVersionLabel& version_information_chosen_version,
+ const ParsedQuicVersion& session_version, std::string* error_details) {
+ if (version_information_chosen_version !=
+ CreateQuicVersionLabel(session_version)) {
+ *error_details = absl::StrCat(
+ "Detected version mismatch: version_information contained ",
+ QuicVersionLabelToString(version_information_chosen_version),
+ " instead of ", ParsedQuicVersionToString(session_version));
+ return false;
+ }
+ return true;
+}
+
+// static
+bool CryptoUtils::ValidateServerVersions(
+ const QuicVersionLabelVector& version_information_other_versions,
+ const ParsedQuicVersion& session_version,
+ const ParsedQuicVersionVector& client_original_supported_versions,
+ std::string* error_details) {
+ if (client_original_supported_versions.empty()) {
+ // We did not receive a version negotiation packet.
+ return true;
+ }
+ // Parse the server's other versions.
+ ParsedQuicVersionVector parsed_other_versions =
+ ParseQuicVersionLabelVector(version_information_other_versions);
+ // Find the first version that we originally supported that is listed in the
+ // server's other versions.
+ ParsedQuicVersion expected_version = ParsedQuicVersion::Unsupported();
+ for (const ParsedQuicVersion& client_version :
+ client_original_supported_versions) {
+ if (std::find(parsed_other_versions.begin(), parsed_other_versions.end(),
+ client_version) != parsed_other_versions.end()) {
+ expected_version = client_version;
+ break;
+ }
+ }
+ if (expected_version != session_version) {
+ *error_details = absl::StrCat(
+ "Downgrade attack detected: used ",
+ ParsedQuicVersionToString(session_version), " but ServerVersions(",
+ version_information_other_versions.size(), ")[",
+ QuicVersionLabelVectorToString(version_information_other_versions, ",",
+ 30),
+ "] ClientOriginalVersions(", client_original_supported_versions.size(),
+ ")[",
+ ParsedQuicVersionVectorToString(client_original_supported_versions, ",",
+ 30),
+ "]");
+ return false;
+ }
+ return true;
+}
+
+#define RETURN_STRING_LITERAL(x) \
+ case x: \
+ return #x
+
+// Returns the name of the HandshakeFailureReason as a char*
+// static
+const char* CryptoUtils::HandshakeFailureReasonToString(
+ HandshakeFailureReason reason) {
+ switch (reason) {
+ RETURN_STRING_LITERAL(HANDSHAKE_OK);
+ RETURN_STRING_LITERAL(CLIENT_NONCE_UNKNOWN_FAILURE);
+ RETURN_STRING_LITERAL(CLIENT_NONCE_INVALID_FAILURE);
+ RETURN_STRING_LITERAL(CLIENT_NONCE_NOT_UNIQUE_FAILURE);
+ RETURN_STRING_LITERAL(CLIENT_NONCE_INVALID_ORBIT_FAILURE);
+ RETURN_STRING_LITERAL(CLIENT_NONCE_INVALID_TIME_FAILURE);
+ RETURN_STRING_LITERAL(CLIENT_NONCE_STRIKE_REGISTER_TIMEOUT);
+ RETURN_STRING_LITERAL(CLIENT_NONCE_STRIKE_REGISTER_FAILURE);
+
+ RETURN_STRING_LITERAL(SERVER_NONCE_DECRYPTION_FAILURE);
+ RETURN_STRING_LITERAL(SERVER_NONCE_INVALID_FAILURE);
+ RETURN_STRING_LITERAL(SERVER_NONCE_NOT_UNIQUE_FAILURE);
+ RETURN_STRING_LITERAL(SERVER_NONCE_INVALID_TIME_FAILURE);
+ RETURN_STRING_LITERAL(SERVER_NONCE_REQUIRED_FAILURE);
+
+ RETURN_STRING_LITERAL(SERVER_CONFIG_INCHOATE_HELLO_FAILURE);
+ RETURN_STRING_LITERAL(SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE);
+
+ RETURN_STRING_LITERAL(SOURCE_ADDRESS_TOKEN_INVALID_FAILURE);
+ RETURN_STRING_LITERAL(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE);
+ RETURN_STRING_LITERAL(SOURCE_ADDRESS_TOKEN_PARSE_FAILURE);
+ RETURN_STRING_LITERAL(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE);
+ RETURN_STRING_LITERAL(SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE);
+ RETURN_STRING_LITERAL(SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE);
+
+ RETURN_STRING_LITERAL(INVALID_EXPECTED_LEAF_CERTIFICATE);
+ RETURN_STRING_LITERAL(MAX_FAILURE_REASON);
+ }
+ // Return a default value so that we return this when |reason| doesn't match
+ // any HandshakeFailureReason.. This can happen when the message by the peer
+ // (attacker) has invalid reason.
+ return "INVALID_HANDSHAKE_FAILURE_REASON";
+}
+
+#undef RETURN_STRING_LITERAL // undef for jumbo builds
+
+// static
+std::string CryptoUtils::EarlyDataReasonToString(
+ ssl_early_data_reason_t reason) {
+ const char* reason_string = SSL_early_data_reason_string(reason);
+ if (reason_string != nullptr) {
+ return std::string("ssl_early_data_") + reason_string;
+ }
+ QUIC_BUG_IF(quic_bug_12871_3,
+ reason < 0 || reason > ssl_early_data_reason_max_value)
+ << "Unknown ssl_early_data_reason_t " << reason;
+ return "unknown ssl_early_data_reason_t";
+}
+
+// static
+std::string CryptoUtils::HashHandshakeMessage(
+ const CryptoHandshakeMessage& message, Perspective /*perspective*/) {
+ std::string output;
+ const QuicData& serialized = message.GetSerialized();
+ uint8_t digest[SHA256_DIGEST_LENGTH];
+ SHA256(reinterpret_cast<const uint8_t*>(serialized.data()),
+ serialized.length(), digest);
+ output.assign(reinterpret_cast<const char*>(digest), sizeof(digest));
+ return output;
+}
+
+// static
+bool CryptoUtils::GetSSLCapabilities(const SSL* ssl,
+ bssl::UniquePtr<uint8_t>* capabilities,
+ size_t* capabilities_len) {
+ uint8_t* buffer;
+ CBB cbb;
+
+ if (!CBB_init(&cbb, 128) || !SSL_serialize_capabilities(ssl, &cbb) ||
+ !CBB_finish(&cbb, &buffer, capabilities_len)) {
+ return false;
+ }
+
+ *capabilities = bssl::UniquePtr<uint8_t>(buffer);
+ return true;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_utils.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_utils.h
new file mode 100644
index 00000000000..4b803f8ece9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_utils.h
@@ -0,0 +1,255 @@
+// Copyright (c) 2013 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.
+
+// Some helpers for quic crypto
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CRYPTO_UTILS_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CRYPTO_UTILS_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <string>
+
+#include "absl/strings/string_view.h"
+#include "openssl/evp.h"
+#include "openssl/ssl.h"
+#include "quiche/quic/core/crypto/crypto_handshake.h"
+#include "quiche/quic/core/crypto/crypto_handshake_message.h"
+#include "quiche/quic/core/crypto/crypto_protocol.h"
+#include "quiche/quic/core/crypto/quic_crypter.h"
+#include "quiche/quic/core/quic_connection_id.h"
+#include "quiche/quic/core/quic_packets.h"
+#include "quiche/quic/core/quic_time.h"
+#include "quiche/quic/core/quic_versions.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class QuicRandom;
+
+class QUIC_EXPORT_PRIVATE CryptoUtils {
+ public:
+ CryptoUtils() = delete;
+
+ // Diversification is a utility class that's used to act like a union type.
+ // Values can be created by calling the functions like |NoDiversification|,
+ // below.
+ class QUIC_EXPORT_PRIVATE Diversification {
+ public:
+ enum Mode {
+ NEVER, // Key diversification will never be used. Forward secure
+ // crypters will always use this mode.
+
+ PENDING, // Key diversification will happen when a nonce is later
+ // received. This should only be used by clients initial
+ // decrypters which are waiting on the divesification nonce
+ // from the server.
+
+ NOW, // Key diversification will happen immediate based on the nonce.
+ // This should only be used by servers initial encrypters.
+ };
+
+ Diversification(const Diversification& diversification) = default;
+
+ static Diversification Never() { return Diversification(NEVER, nullptr); }
+ static Diversification Pending() {
+ return Diversification(PENDING, nullptr);
+ }
+ static Diversification Now(DiversificationNonce* nonce) {
+ return Diversification(NOW, nonce);
+ }
+
+ Mode mode() const { return mode_; }
+ DiversificationNonce* nonce() const {
+ QUICHE_DCHECK_EQ(mode_, NOW);
+ return nonce_;
+ }
+
+ private:
+ Diversification(Mode mode, DiversificationNonce* nonce)
+ : mode_(mode), nonce_(nonce) {}
+
+ Mode mode_;
+ DiversificationNonce* nonce_;
+ };
+
+ // InitializeCrypterSecrets derives the key and IV and header protection key
+ // from the given packet protection secret |pp_secret| and sets those fields
+ // on the given QuicCrypter |*crypter|.
+ // This follows the derivation described in section 7.3 of RFC 8446, except
+ // with the label prefix in HKDF-Expand-Label changed from "tls13 " to "quic "
+ // as described in draft-ietf-quic-tls-14, section 5.1, or "quicv2 " as
+ // described in draft-ietf-quic-v2-01.
+ static void InitializeCrypterSecrets(const EVP_MD* prf,
+ const std::vector<uint8_t>& pp_secret,
+ const ParsedQuicVersion& version,
+ QuicCrypter* crypter);
+
+ // Derives the key and IV from the packet protection secret and sets those
+ // fields on the given QuicCrypter |*crypter|, but does not set the header
+ // protection key. GenerateHeaderProtectionKey/SetHeaderProtectionKey must be
+ // called before using |crypter|.
+ static void SetKeyAndIV(const EVP_MD* prf,
+ const std::vector<uint8_t>& pp_secret,
+ const ParsedQuicVersion& version,
+ QuicCrypter* crypter);
+
+ // Derives the header protection key from the packet protection secret.
+ static std::vector<uint8_t> GenerateHeaderProtectionKey(
+ const EVP_MD* prf, const std::vector<uint8_t>& pp_secret,
+ const ParsedQuicVersion& version, size_t out_len);
+
+ // Given a secret for key phase n, return the secret for phase n+1.
+ static std::vector<uint8_t> GenerateNextKeyPhaseSecret(
+ const EVP_MD* prf, const ParsedQuicVersion& version,
+ const std::vector<uint8_t>& current_secret);
+
+ // IETF QUIC encrypts ENCRYPTION_INITIAL messages with a version-specific key
+ // (to prevent network observers that are not aware of that QUIC version from
+ // making decisions based on the TLS handshake). This packet protection secret
+ // is derived from the connection ID in the client's Initial packet.
+ //
+ // This function takes that |connection_id| and creates the encrypter and
+ // decrypter (put in |*crypters|) to use for this packet protection, as well
+ // as setting the key and IV on those crypters. For older versions of QUIC
+ // that do not use the new IETF style ENCRYPTION_INITIAL obfuscators, this
+ // function puts a NullEncrypter and NullDecrypter in |*crypters|.
+ static void CreateInitialObfuscators(Perspective perspective,
+ ParsedQuicVersion version,
+ QuicConnectionId connection_id,
+ CrypterPair* crypters);
+
+ // IETF QUIC Retry packets carry a retry integrity tag to detect packet
+ // corruption and make it harder for an attacker to spoof. This function
+ // checks whether a given retry packet is valid.
+ static bool ValidateRetryIntegrityTag(ParsedQuicVersion version,
+ QuicConnectionId original_connection_id,
+ absl::string_view retry_without_tag,
+ absl::string_view integrity_tag);
+
+ // Generates the connection nonce. The nonce is formed as:
+ // <4 bytes> current time
+ // <8 bytes> |orbit| (or random if |orbit| is empty)
+ // <20 bytes> random
+ static void GenerateNonce(QuicWallTime now, QuicRandom* random_generator,
+ absl::string_view orbit, std::string* nonce);
+
+ // DeriveKeys populates |crypters->encrypter|, |crypters->decrypter|, and
+ // |subkey_secret| (optional -- may be null) given the contents of
+ // |premaster_secret|, |client_nonce|, |server_nonce| and |hkdf_input|. |aead|
+ // determines which cipher will be used. |perspective| controls whether the
+ // server's keys are assigned to |encrypter| or |decrypter|. |server_nonce| is
+ // optional and, if non-empty, is mixed into the key derivation.
+ // |subkey_secret| will have the same length as |premaster_secret|.
+ //
+ // If |pre_shared_key| is non-empty, it is incorporated into the key
+ // derivation parameters. If it is empty, the key derivation is unaltered.
+ //
+ // If the mode of |diversification| is NEVER, the the crypters will be
+ // configured to never perform key diversification. If the mode is
+ // NOW (which is only for servers, then the encrypter will be keyed via a
+ // two-step process that uses the nonce from |diversification|.
+ // If the mode is PENDING (which is only for servres), then the
+ // decrypter will only be keyed to a preliminary state: a call to
+ // |SetDiversificationNonce| with a diversification nonce will be needed to
+ // complete keying.
+ static bool DeriveKeys(const ParsedQuicVersion& version,
+ absl::string_view premaster_secret, QuicTag aead,
+ absl::string_view client_nonce,
+ absl::string_view server_nonce,
+ absl::string_view pre_shared_key,
+ const std::string& hkdf_input, Perspective perspective,
+ Diversification diversification, CrypterPair* crypters,
+ std::string* subkey_secret);
+
+ // Computes the FNV-1a hash of the provided DER-encoded cert for use in the
+ // XLCT tag.
+ static uint64_t ComputeLeafCertHash(absl::string_view cert);
+
+ // Validates that |server_hello| is actually an SHLO message and that it is
+ // not part of a downgrade attack.
+ //
+ // Returns QUIC_NO_ERROR if this is the case or returns the appropriate error
+ // code and sets |error_details|.
+ static QuicErrorCode ValidateServerHello(
+ const CryptoHandshakeMessage& server_hello,
+ const ParsedQuicVersionVector& negotiated_versions,
+ std::string* error_details);
+
+ // Validates that the |server_versions| received do not indicate that the
+ // ServerHello is part of a downgrade attack. |negotiated_versions| must
+ // contain the list of versions received in the server's version negotiation
+ // packet (or be empty if no such packet was received).
+ //
+ // Returns QUIC_NO_ERROR if this is the case or returns the appropriate error
+ // code and sets |error_details|.
+ static QuicErrorCode ValidateServerHelloVersions(
+ const QuicVersionLabelVector& server_versions,
+ const ParsedQuicVersionVector& negotiated_versions,
+ std::string* error_details);
+
+ // Validates that |client_hello| is actually a CHLO and that this is not part
+ // of a downgrade attack.
+ // This includes verifiying versions and detecting downgrade attacks.
+ //
+ // Returns QUIC_NO_ERROR if this is the case or returns the appropriate error
+ // code and sets |error_details|.
+ static QuicErrorCode ValidateClientHello(
+ const CryptoHandshakeMessage& client_hello, ParsedQuicVersion version,
+ const ParsedQuicVersionVector& supported_versions,
+ std::string* error_details);
+
+ // Validates that the |client_version| received does not indicate that a
+ // downgrade attack has occurred. |connection_version| is the version of the
+ // QuicConnection, and |supported_versions| is all versions that that
+ // QuicConnection supports.
+ //
+ // Returns QUIC_NO_ERROR if this is the case or returns the appropriate error
+ // code and sets |error_details|.
+ static QuicErrorCode ValidateClientHelloVersion(
+ QuicVersionLabel client_version, ParsedQuicVersion connection_version,
+ const ParsedQuicVersionVector& supported_versions,
+ std::string* error_details);
+
+ // Validates that the chosen version from the version_information matches the
+ // version from the session. Returns true if they match, otherwise returns
+ // false and fills in |error_details|.
+ static bool ValidateChosenVersion(
+ const QuicVersionLabel& version_information_chosen_version,
+ const ParsedQuicVersion& session_version, std::string* error_details);
+
+ // Validates that there was no downgrade attack involving a version
+ // negotiation packet. This verifies that if the client was initially
+ // configured with |client_original_supported_versions| and it had received a
+ // version negotiation packet with |version_information_other_versions|, then
+ // it would have selected |session_version|. Returns true if they match (or if
+ // |client_original_supported_versions| is empty indicating no version
+ // negotiation packet was received), otherwise returns
+ // false and fills in |error_details|.
+ static bool ValidateServerVersions(
+ const QuicVersionLabelVector& version_information_other_versions,
+ const ParsedQuicVersion& session_version,
+ const ParsedQuicVersionVector& client_original_supported_versions,
+ std::string* error_details);
+
+ // Returns the name of the HandshakeFailureReason as a char*
+ static const char* HandshakeFailureReasonToString(
+ HandshakeFailureReason reason);
+
+ // Returns the name of an ssl_early_data_reason_t as a char*
+ static std::string EarlyDataReasonToString(ssl_early_data_reason_t reason);
+
+ // Returns a hash of the serialized |message|.
+ static std::string HashHandshakeMessage(const CryptoHandshakeMessage& message,
+ Perspective perspective);
+
+ // Wraps SSL_serialize_capabilities. Return nullptr if failed.
+ static bool GetSSLCapabilities(const SSL* ssl,
+ bssl::UniquePtr<uint8_t>* capabilities,
+ size_t* capabilities_len);
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CRYPTO_UTILS_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_utils_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_utils_test.cc
new file mode 100644
index 00000000000..6452367a102
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_utils_test.cc
@@ -0,0 +1,262 @@
+// Copyright (c) 2013 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 "quiche/quic/core/crypto/crypto_utils.h"
+
+#include <string>
+
+#include "absl/base/macros.h"
+#include "absl/strings/escaping.h"
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/quic_test_utils.h"
+#include "quiche/common/test_tools/quiche_test_utils.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+class CryptoUtilsTest : public QuicTest {};
+
+TEST_F(CryptoUtilsTest, HandshakeFailureReasonToString) {
+ EXPECT_STREQ("HANDSHAKE_OK",
+ CryptoUtils::HandshakeFailureReasonToString(HANDSHAKE_OK));
+ EXPECT_STREQ("CLIENT_NONCE_UNKNOWN_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ CLIENT_NONCE_UNKNOWN_FAILURE));
+ EXPECT_STREQ("CLIENT_NONCE_INVALID_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ CLIENT_NONCE_INVALID_FAILURE));
+ EXPECT_STREQ("CLIENT_NONCE_NOT_UNIQUE_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ CLIENT_NONCE_NOT_UNIQUE_FAILURE));
+ EXPECT_STREQ("CLIENT_NONCE_INVALID_ORBIT_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ CLIENT_NONCE_INVALID_ORBIT_FAILURE));
+ EXPECT_STREQ("CLIENT_NONCE_INVALID_TIME_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ CLIENT_NONCE_INVALID_TIME_FAILURE));
+ EXPECT_STREQ("CLIENT_NONCE_STRIKE_REGISTER_TIMEOUT",
+ CryptoUtils::HandshakeFailureReasonToString(
+ CLIENT_NONCE_STRIKE_REGISTER_TIMEOUT));
+ EXPECT_STREQ("CLIENT_NONCE_STRIKE_REGISTER_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ CLIENT_NONCE_STRIKE_REGISTER_FAILURE));
+ EXPECT_STREQ("SERVER_NONCE_DECRYPTION_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ SERVER_NONCE_DECRYPTION_FAILURE));
+ EXPECT_STREQ("SERVER_NONCE_INVALID_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ SERVER_NONCE_INVALID_FAILURE));
+ EXPECT_STREQ("SERVER_NONCE_NOT_UNIQUE_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ SERVER_NONCE_NOT_UNIQUE_FAILURE));
+ EXPECT_STREQ("SERVER_NONCE_INVALID_TIME_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ SERVER_NONCE_INVALID_TIME_FAILURE));
+ EXPECT_STREQ("SERVER_NONCE_REQUIRED_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ SERVER_NONCE_REQUIRED_FAILURE));
+ EXPECT_STREQ("SERVER_CONFIG_INCHOATE_HELLO_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE));
+ EXPECT_STREQ("SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE));
+ EXPECT_STREQ("SOURCE_ADDRESS_TOKEN_INVALID_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ SOURCE_ADDRESS_TOKEN_INVALID_FAILURE));
+ EXPECT_STREQ("SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE));
+ EXPECT_STREQ("SOURCE_ADDRESS_TOKEN_PARSE_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ SOURCE_ADDRESS_TOKEN_PARSE_FAILURE));
+ EXPECT_STREQ("SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE));
+ EXPECT_STREQ("SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE));
+ EXPECT_STREQ("SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE));
+ EXPECT_STREQ("INVALID_EXPECTED_LEAF_CERTIFICATE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ INVALID_EXPECTED_LEAF_CERTIFICATE));
+ EXPECT_STREQ("MAX_FAILURE_REASON",
+ CryptoUtils::HandshakeFailureReasonToString(MAX_FAILURE_REASON));
+ EXPECT_STREQ(
+ "INVALID_HANDSHAKE_FAILURE_REASON",
+ CryptoUtils::HandshakeFailureReasonToString(
+ static_cast<HandshakeFailureReason>(MAX_FAILURE_REASON + 1)));
+}
+
+TEST_F(CryptoUtilsTest, AuthTagLengths) {
+ for (const auto& version : AllSupportedVersions()) {
+ for (QuicTag algo : {kAESG, kCC20}) {
+ SCOPED_TRACE(version);
+ std::unique_ptr<QuicEncrypter> encrypter(
+ QuicEncrypter::Create(version, algo));
+ size_t auth_tag_size = 12;
+ if (version.UsesInitialObfuscators()) {
+ auth_tag_size = 16;
+ }
+ EXPECT_EQ(encrypter->GetCiphertextSize(0), auth_tag_size);
+ }
+ }
+}
+
+TEST_F(CryptoUtilsTest, ValidateChosenVersion) {
+ for (const ParsedQuicVersion& v1 : AllSupportedVersions()) {
+ for (const ParsedQuicVersion& v2 : AllSupportedVersions()) {
+ std::string error_details;
+ bool success = CryptoUtils::ValidateChosenVersion(
+ CreateQuicVersionLabel(v1), v2, &error_details);
+ EXPECT_EQ(success, v1 == v2);
+ EXPECT_EQ(success, error_details.empty());
+ }
+ }
+}
+
+TEST_F(CryptoUtilsTest, ValidateServerVersionsNoVersionNegotiation) {
+ QuicVersionLabelVector version_information_other_versions;
+ ParsedQuicVersionVector client_original_supported_versions;
+ for (const ParsedQuicVersion& version : AllSupportedVersions()) {
+ std::string error_details;
+ EXPECT_TRUE(CryptoUtils::ValidateServerVersions(
+ version_information_other_versions, version,
+ client_original_supported_versions, &error_details));
+ EXPECT_TRUE(error_details.empty());
+ }
+}
+
+TEST_F(CryptoUtilsTest, ValidateServerVersionsWithVersionNegotiation) {
+ for (const ParsedQuicVersion& version : AllSupportedVersions()) {
+ QuicVersionLabelVector version_information_other_versions{
+ CreateQuicVersionLabel(version)};
+ ParsedQuicVersionVector client_original_supported_versions{
+ ParsedQuicVersion::ReservedForNegotiation(), version};
+ std::string error_details;
+ EXPECT_TRUE(CryptoUtils::ValidateServerVersions(
+ version_information_other_versions, version,
+ client_original_supported_versions, &error_details));
+ EXPECT_TRUE(error_details.empty());
+ }
+}
+
+TEST_F(CryptoUtilsTest, ValidateServerVersionsWithDowngrade) {
+ if (AllSupportedVersions().size() <= 1) {
+ // We are not vulnerable to downgrade if we only support one version.
+ return;
+ }
+ ParsedQuicVersion client_version = AllSupportedVersions().front();
+ ParsedQuicVersion server_version = AllSupportedVersions().back();
+ ASSERT_NE(client_version, server_version);
+ QuicVersionLabelVector version_information_other_versions{
+ CreateQuicVersionLabel(client_version)};
+ ParsedQuicVersionVector client_original_supported_versions{
+ ParsedQuicVersion::ReservedForNegotiation(), server_version};
+ std::string error_details;
+ EXPECT_FALSE(CryptoUtils::ValidateServerVersions(
+ version_information_other_versions, server_version,
+ client_original_supported_versions, &error_details));
+ EXPECT_FALSE(error_details.empty());
+}
+
+// Test that the library is using the correct labels for each version, and
+// therefore generating correct obfuscators, using the test vectors in appendix
+// A of each RFC or internet-draft.
+TEST_F(CryptoUtilsTest, ValidateCryptoLabels) {
+ // if the number of HTTP/3 QUIC versions has changed, we need to change the
+ // expected_keys hardcoded into this test. Regrettably, this is not a
+ // compile-time constant.
+ EXPECT_EQ(AllSupportedVersionsWithTls().size(), 3u);
+ const char draft_29_key[] = {// test vector from draft-ietf-quic-tls-29, A.1
+ 0x14,
+ static_cast<char>(0x9d),
+ 0x0b,
+ 0x16,
+ 0x62,
+ static_cast<char>(0xab),
+ static_cast<char>(0x87),
+ 0x1f,
+ static_cast<char>(0xbe),
+ 0x63,
+ static_cast<char>(0xc4),
+ static_cast<char>(0x9b),
+ 0x5e,
+ 0x65,
+ 0x5a,
+ 0x5d};
+ const char v1_key[] = {// test vector from RFC 9001, A.1
+ static_cast<char>(0xcf),
+ 0x3a,
+ 0x53,
+ 0x31,
+ 0x65,
+ 0x3c,
+ 0x36,
+ 0x4c,
+ static_cast<char>(0x88),
+ static_cast<char>(0xf0),
+ static_cast<char>(0xf3),
+ 0x79,
+ static_cast<char>(0xb6),
+ 0x06,
+ 0x7e,
+ 0x37};
+ const char v2_01_key[] = {// test vector from draft-ietf-quic-v2-01
+ 0x15,
+ static_cast<char>(0xd5),
+ static_cast<char>(0xb4),
+ static_cast<char>(0xd9),
+ static_cast<char>(0xa2),
+ static_cast<char>(0xb8),
+ static_cast<char>(0x91),
+ 0x6a,
+ static_cast<char>(0xa3),
+ static_cast<char>(0x9b),
+ 0x1b,
+ static_cast<char>(0xfe),
+ 0x57,
+ 0x4d,
+ 0x2a,
+ static_cast<char>(0xad)};
+ const char connection_id[] = // test vector from both docs
+ {static_cast<char>(0x83),
+ static_cast<char>(0x94),
+ static_cast<char>(0xc8),
+ static_cast<char>(0xf0),
+ 0x3e,
+ 0x51,
+ 0x57,
+ 0x08};
+ const QuicConnectionId cid(connection_id, sizeof(connection_id));
+ const char* key_str;
+ size_t key_size;
+ for (const ParsedQuicVersion& version : AllSupportedVersionsWithTls()) {
+ if (version == ParsedQuicVersion::Draft29()) {
+ key_str = draft_29_key;
+ key_size = sizeof(draft_29_key);
+ } else if (version == ParsedQuicVersion::RFCv1()) {
+ key_str = v1_key;
+ key_size = sizeof(v1_key);
+ } else { // draft-ietf-quic-v2-01
+ key_str = v2_01_key;
+ key_size = sizeof(v2_01_key);
+ }
+ const absl::string_view expected_key{key_str, key_size};
+
+ CrypterPair crypters;
+ CryptoUtils::CreateInitialObfuscators(Perspective::IS_SERVER, version, cid,
+ &crypters);
+ EXPECT_EQ(crypters.encrypter->GetKey(), expected_key);
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/curve25519_key_exchange.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/curve25519_key_exchange.cc
new file mode 100644
index 00000000000..5340b41107c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/curve25519_key_exchange.cc
@@ -0,0 +1,86 @@
+// Copyright (c) 2013 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 "quiche/quic/core/crypto/curve25519_key_exchange.h"
+
+#include <cstdint>
+#include <cstring>
+#include <string>
+
+#include "absl/memory/memory.h"
+#include "absl/strings/string_view.h"
+#include "openssl/curve25519.h"
+#include "quiche/quic/core/crypto/quic_random.h"
+#include "quiche/quic/platform/api/quic_bug_tracker.h"
+
+namespace quic {
+
+Curve25519KeyExchange::Curve25519KeyExchange() {}
+
+Curve25519KeyExchange::~Curve25519KeyExchange() {}
+
+// static
+std::unique_ptr<Curve25519KeyExchange> Curve25519KeyExchange::New(
+ QuicRandom* rand) {
+ std::unique_ptr<Curve25519KeyExchange> result =
+ New(Curve25519KeyExchange::NewPrivateKey(rand));
+ QUIC_BUG_IF(quic_bug_12891_1, result == nullptr);
+ return result;
+}
+
+// static
+std::unique_ptr<Curve25519KeyExchange> Curve25519KeyExchange::New(
+ absl::string_view private_key) {
+ // We don't want to #include the BoringSSL headers in the public header file,
+ // so we use literals for the sizes of private_key_ and public_key_. Here we
+ // assert that those values are equal to the values from the BoringSSL
+ // header.
+ static_assert(
+ sizeof(Curve25519KeyExchange::private_key_) == X25519_PRIVATE_KEY_LEN,
+ "header out of sync");
+ static_assert(
+ sizeof(Curve25519KeyExchange::public_key_) == X25519_PUBLIC_VALUE_LEN,
+ "header out of sync");
+
+ if (private_key.size() != X25519_PRIVATE_KEY_LEN) {
+ return nullptr;
+ }
+
+ // Use absl::WrapUnique(new) instead of std::make_unique because
+ // Curve25519KeyExchange has a private constructor.
+ auto ka = absl::WrapUnique(new Curve25519KeyExchange);
+ memcpy(ka->private_key_, private_key.data(), X25519_PRIVATE_KEY_LEN);
+ X25519_public_from_private(ka->public_key_, ka->private_key_);
+ return ka;
+}
+
+// static
+std::string Curve25519KeyExchange::NewPrivateKey(QuicRandom* rand) {
+ uint8_t private_key[X25519_PRIVATE_KEY_LEN];
+ rand->RandBytes(private_key, sizeof(private_key));
+ return std::string(reinterpret_cast<char*>(private_key), sizeof(private_key));
+}
+
+bool Curve25519KeyExchange::CalculateSharedKeySync(
+ absl::string_view peer_public_value, std::string* shared_key) const {
+ if (peer_public_value.size() != X25519_PUBLIC_VALUE_LEN) {
+ return false;
+ }
+
+ uint8_t result[X25519_PUBLIC_VALUE_LEN];
+ if (!X25519(result, private_key_,
+ reinterpret_cast<const uint8_t*>(peer_public_value.data()))) {
+ return false;
+ }
+
+ shared_key->assign(reinterpret_cast<char*>(result), sizeof(result));
+ return true;
+}
+
+absl::string_view Curve25519KeyExchange::public_value() const {
+ return absl::string_view(reinterpret_cast<const char*>(public_key_),
+ sizeof(public_key_));
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/curve25519_key_exchange.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/curve25519_key_exchange.h
new file mode 100644
index 00000000000..34a49f9f2e6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/curve25519_key_exchange.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2013 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CURVE25519_KEY_EXCHANGE_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CURVE25519_KEY_EXCHANGE_H_
+
+#include <cstdint>
+#include <string>
+
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/crypto/key_exchange.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class QuicRandom;
+
+// Curve25519KeyExchange implements a SynchronousKeyExchange using
+// elliptic-curve Diffie-Hellman on curve25519. See http://cr.yp.to/ecdh.html
+class QUIC_EXPORT_PRIVATE Curve25519KeyExchange
+ : public SynchronousKeyExchange {
+ public:
+ ~Curve25519KeyExchange() override;
+
+ // New generates a private key and then creates new key-exchange object.
+ static std::unique_ptr<Curve25519KeyExchange> New(QuicRandom* rand);
+
+ // New creates a new key-exchange object from a private key. If |private_key|
+ // is invalid, nullptr is returned.
+ static std::unique_ptr<Curve25519KeyExchange> New(
+ absl::string_view private_key);
+
+ // NewPrivateKey returns a private key, generated from |rand|, suitable for
+ // passing to |New|.
+ static std::string NewPrivateKey(QuicRandom* rand);
+
+ // SynchronousKeyExchange interface.
+ bool CalculateSharedKeySync(absl::string_view peer_public_value,
+ std::string* shared_key) const override;
+ absl::string_view public_value() const override;
+ QuicTag type() const override { return kC255; }
+
+ private:
+ Curve25519KeyExchange();
+
+ uint8_t private_key_[32];
+ uint8_t public_key_[32];
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CURVE25519_KEY_EXCHANGE_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/curve25519_key_exchange_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/curve25519_key_exchange_test.cc
new file mode 100644
index 00000000000..551ee0e1bc9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/curve25519_key_exchange_test.cc
@@ -0,0 +1,104 @@
+// Copyright (c) 2013 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 "quiche/quic/core/crypto/curve25519_key_exchange.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/crypto/quic_random.h"
+#include "quiche/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+
+class Curve25519KeyExchangeTest : public QuicTest {
+ public:
+ // Holds the result of a key exchange callback.
+ class TestCallbackResult {
+ public:
+ void set_ok(bool ok) { ok_ = ok; }
+ bool ok() { return ok_; }
+
+ private:
+ bool ok_ = false;
+ };
+
+ // Key exchange callback which sets the result into the specified
+ // TestCallbackResult.
+ class TestCallback : public AsynchronousKeyExchange::Callback {
+ public:
+ TestCallback(TestCallbackResult* result) : result_(result) {}
+ virtual ~TestCallback() = default;
+
+ void Run(bool ok) { result_->set_ok(ok); }
+
+ private:
+ TestCallbackResult* result_;
+ };
+};
+
+// SharedKey just tests that the basic key exchange identity holds: that both
+// parties end up with the same key.
+TEST_F(Curve25519KeyExchangeTest, SharedKey) {
+ QuicRandom* const rand = QuicRandom::GetInstance();
+
+ for (int i = 0; i < 5; i++) {
+ const std::string alice_key(Curve25519KeyExchange::NewPrivateKey(rand));
+ const std::string bob_key(Curve25519KeyExchange::NewPrivateKey(rand));
+
+ std::unique_ptr<Curve25519KeyExchange> alice(
+ Curve25519KeyExchange::New(alice_key));
+ std::unique_ptr<Curve25519KeyExchange> bob(
+ Curve25519KeyExchange::New(bob_key));
+
+ const absl::string_view alice_public(alice->public_value());
+ const absl::string_view bob_public(bob->public_value());
+
+ std::string alice_shared, bob_shared;
+ ASSERT_TRUE(alice->CalculateSharedKeySync(bob_public, &alice_shared));
+ ASSERT_TRUE(bob->CalculateSharedKeySync(alice_public, &bob_shared));
+ ASSERT_EQ(alice_shared, bob_shared);
+ }
+}
+
+// SharedKeyAsync just tests that the basic asynchronous key exchange identity
+// holds: that both parties end up with the same key.
+TEST_F(Curve25519KeyExchangeTest, SharedKeyAsync) {
+ QuicRandom* const rand = QuicRandom::GetInstance();
+
+ for (int i = 0; i < 5; i++) {
+ const std::string alice_key(Curve25519KeyExchange::NewPrivateKey(rand));
+ const std::string bob_key(Curve25519KeyExchange::NewPrivateKey(rand));
+
+ std::unique_ptr<Curve25519KeyExchange> alice(
+ Curve25519KeyExchange::New(alice_key));
+ std::unique_ptr<Curve25519KeyExchange> bob(
+ Curve25519KeyExchange::New(bob_key));
+
+ const absl::string_view alice_public(alice->public_value());
+ const absl::string_view bob_public(bob->public_value());
+
+ std::string alice_shared, bob_shared;
+ TestCallbackResult alice_result;
+ ASSERT_FALSE(alice_result.ok());
+ alice->CalculateSharedKeyAsync(
+ bob_public, &alice_shared,
+ std::make_unique<TestCallback>(&alice_result));
+ ASSERT_TRUE(alice_result.ok());
+ TestCallbackResult bob_result;
+ ASSERT_FALSE(bob_result.ok());
+ bob->CalculateSharedKeyAsync(alice_public, &bob_shared,
+ std::make_unique<TestCallback>(&bob_result));
+ ASSERT_TRUE(bob_result.ok());
+ ASSERT_EQ(alice_shared, bob_shared);
+ ASSERT_NE(0u, alice_shared.length());
+ ASSERT_NE(0u, bob_shared.length());
+ }
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/key_exchange.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/key_exchange.cc
new file mode 100644
index 00000000000..38dea001f1e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/key_exchange.cc
@@ -0,0 +1,42 @@
+// Copyright (c) 2019 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 "quiche/quic/core/crypto/key_exchange.h"
+
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/crypto/curve25519_key_exchange.h"
+#include "quiche/quic/core/crypto/p256_key_exchange.h"
+#include "quiche/quic/platform/api/quic_bug_tracker.h"
+
+namespace quic {
+
+std::unique_ptr<SynchronousKeyExchange> CreateLocalSynchronousKeyExchange(
+ QuicTag type, absl::string_view private_key) {
+ switch (type) {
+ case kC255:
+ return Curve25519KeyExchange::New(private_key);
+ case kP256:
+ return P256KeyExchange::New(private_key);
+ default:
+ QUIC_BUG(quic_bug_10712_1)
+ << "Unknown key exchange method: " << QuicTagToString(type);
+ return nullptr;
+ }
+}
+
+std::unique_ptr<SynchronousKeyExchange> CreateLocalSynchronousKeyExchange(
+ QuicTag type, QuicRandom* rand) {
+ switch (type) {
+ case kC255:
+ return Curve25519KeyExchange::New(rand);
+ case kP256:
+ return P256KeyExchange::New();
+ default:
+ QUIC_BUG(quic_bug_10712_2)
+ << "Unknown key exchange method: " << QuicTagToString(type);
+ return nullptr;
+ }
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/key_exchange.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/key_exchange.h
new file mode 100644
index 00000000000..a681bf596a2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/key_exchange.h
@@ -0,0 +1,102 @@
+// Copyright (c) 2013 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_KEY_EXCHANGE_H_
+#define QUICHE_QUIC_CORE_CRYPTO_KEY_EXCHANGE_H_
+
+#include <memory>
+#include <string>
+
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/crypto/crypto_protocol.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class QuicRandom;
+
+// Interface for a Diffie-Hellman key exchange with an asynchronous interface.
+// This allows for implementations which hold the private key locally, as well
+// as ones which make an RPC to an external key-exchange service.
+class QUIC_EXPORT_PRIVATE AsynchronousKeyExchange {
+ public:
+ virtual ~AsynchronousKeyExchange() = default;
+
+ // Callback base class for receiving the results of an async call to
+ // CalculateSharedKeys.
+ class QUIC_EXPORT_PRIVATE Callback {
+ public:
+ Callback() = default;
+ virtual ~Callback() = default;
+
+ // Invoked upon completion of CalculateSharedKeysAsync.
+ //
+ // |ok| indicates whether the operation completed successfully. If false,
+ // then the value pointed to by |shared_key| passed in to
+ // CalculateSharedKeyAsync is undefined.
+ virtual void Run(bool ok) = 0;
+
+ private:
+ Callback(const Callback&) = delete;
+ Callback& operator=(const Callback&) = delete;
+ };
+
+ // CalculateSharedKey computes the shared key between a private key which is
+ // conceptually owned by this object (though it may not be physically located
+ // in this process) and a public value from the peer. Callers should expect
+ // that |callback| might be invoked synchronously. Results will be written
+ // into |*shared_key|.
+ virtual void CalculateSharedKeyAsync(
+ absl::string_view peer_public_value, std::string* shared_key,
+ std::unique_ptr<Callback> callback) const = 0;
+
+ // Tag indicating the key-exchange algorithm this object will use.
+ virtual QuicTag type() const = 0;
+};
+
+// Interface for a Diffie-Hellman key exchange with both synchronous and
+// asynchronous interfaces. Only implementations which hold the private key
+// locally should implement this interface.
+class QUIC_EXPORT_PRIVATE SynchronousKeyExchange
+ : public AsynchronousKeyExchange {
+ public:
+ virtual ~SynchronousKeyExchange() = default;
+
+ // AyncKeyExchange API. Note that this method is marked 'final.' Subclasses
+ // should implement CalculateSharedKeySync only.
+ void CalculateSharedKeyAsync(absl::string_view peer_public_value,
+ std::string* shared_key,
+ std::unique_ptr<Callback> callback) const final {
+ const bool ok = CalculateSharedKeySync(peer_public_value, shared_key);
+ callback->Run(ok);
+ }
+
+ // CalculateSharedKey computes the shared key between a local private key and
+ // a public value from the peer. Results will be written into |*shared_key|.
+ virtual bool CalculateSharedKeySync(absl::string_view peer_public_value,
+ std::string* shared_key) const = 0;
+
+ // public_value returns the local public key which can be sent to a peer in
+ // order to complete a key exchange. The returned absl::string_view is
+ // a reference to a member of this object and is only valid for as long as it
+ // exists.
+ virtual absl::string_view public_value() const = 0;
+};
+
+// Create a SynchronousKeyExchange object which will use a keypair generated
+// from |private_key|, and a key-exchange algorithm specified by |type|, which
+// must be one of {kC255, kC256}. Returns nullptr if |private_key| or |type| is
+// invalid.
+std::unique_ptr<SynchronousKeyExchange> CreateLocalSynchronousKeyExchange(
+ QuicTag type, absl::string_view private_key);
+
+// Create a SynchronousKeyExchange object which will use a keypair generated
+// from |rand|, and a key-exchange algorithm specified by |type|, which must be
+// one of {kC255, kC256}. Returns nullptr if |type| is invalid.
+std::unique_ptr<SynchronousKeyExchange> CreateLocalSynchronousKeyExchange(
+ QuicTag type, QuicRandom* rand);
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_KEY_EXCHANGE_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/null_decrypter.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/null_decrypter.cc
new file mode 100644
index 00000000000..af0c4447690
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/null_decrypter.cc
@@ -0,0 +1,121 @@
+// Copyright (c) 2012 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 "quiche/quic/core/crypto/null_decrypter.h"
+
+#include <cstdint>
+
+#include "absl/numeric/int128.h"
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/quic_data_reader.h"
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/quic/platform/api/quic_bug_tracker.h"
+#include "quiche/common/quiche_endian.h"
+
+namespace quic {
+
+NullDecrypter::NullDecrypter(Perspective perspective)
+ : perspective_(perspective) {}
+
+bool NullDecrypter::SetKey(absl::string_view key) { return key.empty(); }
+
+bool NullDecrypter::SetNoncePrefix(absl::string_view nonce_prefix) {
+ return nonce_prefix.empty();
+}
+
+bool NullDecrypter::SetIV(absl::string_view iv) { return iv.empty(); }
+
+bool NullDecrypter::SetHeaderProtectionKey(absl::string_view key) {
+ return key.empty();
+}
+
+bool NullDecrypter::SetPreliminaryKey(absl::string_view /*key*/) {
+ QUIC_BUG(quic_bug_10652_1) << "Should not be called";
+ return false;
+}
+
+bool NullDecrypter::SetDiversificationNonce(
+ const DiversificationNonce& /*nonce*/) {
+ QUIC_BUG(quic_bug_10652_2) << "Should not be called";
+ return true;
+}
+
+bool NullDecrypter::DecryptPacket(uint64_t /*packet_number*/,
+ absl::string_view associated_data,
+ absl::string_view ciphertext, char* output,
+ size_t* output_length,
+ size_t max_output_length) {
+ QuicDataReader reader(ciphertext.data(), ciphertext.length(),
+ quiche::HOST_BYTE_ORDER);
+ absl::uint128 hash;
+
+ if (!ReadHash(&reader, &hash)) {
+ return false;
+ }
+
+ absl::string_view plaintext = reader.ReadRemainingPayload();
+ if (plaintext.length() > max_output_length) {
+ QUIC_BUG(quic_bug_10652_3)
+ << "Output buffer must be larger than the plaintext.";
+ return false;
+ }
+ if (hash != ComputeHash(associated_data, plaintext)) {
+ return false;
+ }
+ // Copy the plaintext to output.
+ memcpy(output, plaintext.data(), plaintext.length());
+ *output_length = plaintext.length();
+ return true;
+}
+
+std::string NullDecrypter::GenerateHeaderProtectionMask(
+ QuicDataReader* /*sample_reader*/) {
+ return std::string(5, 0);
+}
+
+size_t NullDecrypter::GetKeySize() const { return 0; }
+
+size_t NullDecrypter::GetNoncePrefixSize() const { return 0; }
+
+size_t NullDecrypter::GetIVSize() const { return 0; }
+
+absl::string_view NullDecrypter::GetKey() const { return absl::string_view(); }
+
+absl::string_view NullDecrypter::GetNoncePrefix() const {
+ return absl::string_view();
+}
+
+uint32_t NullDecrypter::cipher_id() const { return 0; }
+
+QuicPacketCount NullDecrypter::GetIntegrityLimit() const {
+ return std::numeric_limits<QuicPacketCount>::max();
+}
+
+bool NullDecrypter::ReadHash(QuicDataReader* reader, absl::uint128* hash) {
+ uint64_t lo;
+ uint32_t hi;
+ if (!reader->ReadUInt64(&lo) || !reader->ReadUInt32(&hi)) {
+ return false;
+ }
+ *hash = absl::MakeUint128(hi, lo);
+ return true;
+}
+
+absl::uint128 NullDecrypter::ComputeHash(const absl::string_view data1,
+ const absl::string_view data2) const {
+ absl::uint128 correct_hash;
+ if (perspective_ == Perspective::IS_CLIENT) {
+ // Peer is a server.
+ correct_hash = QuicUtils::FNV1a_128_Hash_Three(data1, data2, "Server");
+ } else {
+ // Peer is a client.
+ correct_hash = QuicUtils::FNV1a_128_Hash_Three(data1, data2, "Client");
+ }
+ absl::uint128 mask = absl::MakeUint128(UINT64_C(0x0), UINT64_C(0xffffffff));
+ mask <<= 96;
+ correct_hash &= ~mask;
+ return correct_hash;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/null_decrypter.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/null_decrypter.h
new file mode 100644
index 00000000000..9b6fb4501ff
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/null_decrypter.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2012 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_NULL_DECRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_NULL_DECRYPTER_H_
+
+#include <cstddef>
+#include <cstdint>
+
+#include "absl/numeric/int128.h"
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/crypto/quic_decrypter.h"
+#include "quiche/quic/core/quic_types.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class QuicDataReader;
+
+// A NullDecrypter is a QuicDecrypter used before a crypto negotiation
+// has occurred. It does not actually decrypt the payload, but does
+// verify a hash (fnv128) over both the payload and associated data.
+class QUIC_EXPORT_PRIVATE NullDecrypter : public QuicDecrypter {
+ public:
+ explicit NullDecrypter(Perspective perspective);
+ NullDecrypter(const NullDecrypter&) = delete;
+ NullDecrypter& operator=(const NullDecrypter&) = delete;
+ ~NullDecrypter() override {}
+
+ // QuicDecrypter implementation
+ bool SetKey(absl::string_view key) override;
+ bool SetNoncePrefix(absl::string_view nonce_prefix) override;
+ bool SetIV(absl::string_view iv) override;
+ bool SetHeaderProtectionKey(absl::string_view key) override;
+ bool SetPreliminaryKey(absl::string_view key) override;
+ bool SetDiversificationNonce(const DiversificationNonce& nonce) override;
+ bool DecryptPacket(uint64_t packet_number, absl::string_view associated_data,
+ absl::string_view ciphertext, char* output,
+ size_t* output_length, size_t max_output_length) override;
+ std::string GenerateHeaderProtectionMask(
+ QuicDataReader* sample_reader) override;
+ size_t GetKeySize() const override;
+ size_t GetNoncePrefixSize() const override;
+ size_t GetIVSize() const override;
+ absl::string_view GetKey() const override;
+ absl::string_view GetNoncePrefix() const override;
+
+ uint32_t cipher_id() const override;
+ QuicPacketCount GetIntegrityLimit() const override;
+
+ private:
+ bool ReadHash(QuicDataReader* reader, absl::uint128* hash);
+ absl::uint128 ComputeHash(absl::string_view data1,
+ absl::string_view data2) const;
+
+ Perspective perspective_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_NULL_DECRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/null_decrypter_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/null_decrypter_test.cc
new file mode 100644
index 00000000000..e71ed01ba8f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/null_decrypter_test.cc
@@ -0,0 +1,137 @@
+// Copyright (c) 2012 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 "quiche/quic/core/crypto/null_decrypter.h"
+
+#include "absl/base/macros.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+
+class NullDecrypterTest : public QuicTestWithParam<bool> {};
+
+TEST_F(NullDecrypterTest, DecryptClient) {
+ unsigned char expected[] = {
+ // fnv hash
+ 0x97,
+ 0xdc,
+ 0x27,
+ 0x2f,
+ 0x18,
+ 0xa8,
+ 0x56,
+ 0x73,
+ 0xdf,
+ 0x8d,
+ 0x1d,
+ 0xd0,
+ // payload
+ 'g',
+ 'o',
+ 'o',
+ 'd',
+ 'b',
+ 'y',
+ 'e',
+ '!',
+ };
+ const char* data = reinterpret_cast<const char*>(expected);
+ size_t len = ABSL_ARRAYSIZE(expected);
+ NullDecrypter decrypter(Perspective::IS_SERVER);
+ char buffer[256];
+ size_t length = 0;
+ ASSERT_TRUE(decrypter.DecryptPacket(
+ 0, "hello world!", absl::string_view(data, len), buffer, &length, 256));
+ EXPECT_LT(0u, length);
+ EXPECT_EQ("goodbye!", absl::string_view(buffer, length));
+}
+
+TEST_F(NullDecrypterTest, DecryptServer) {
+ unsigned char expected[] = {
+ // fnv hash
+ 0x63,
+ 0x5e,
+ 0x08,
+ 0x03,
+ 0x32,
+ 0x80,
+ 0x8f,
+ 0x73,
+ 0xdf,
+ 0x8d,
+ 0x1d,
+ 0x1a,
+ // payload
+ 'g',
+ 'o',
+ 'o',
+ 'd',
+ 'b',
+ 'y',
+ 'e',
+ '!',
+ };
+ const char* data = reinterpret_cast<const char*>(expected);
+ size_t len = ABSL_ARRAYSIZE(expected);
+ NullDecrypter decrypter(Perspective::IS_CLIENT);
+ char buffer[256];
+ size_t length = 0;
+ ASSERT_TRUE(decrypter.DecryptPacket(
+ 0, "hello world!", absl::string_view(data, len), buffer, &length, 256));
+ EXPECT_LT(0u, length);
+ EXPECT_EQ("goodbye!", absl::string_view(buffer, length));
+}
+
+TEST_F(NullDecrypterTest, BadHash) {
+ unsigned char expected[] = {
+ // fnv hash
+ 0x46,
+ 0x11,
+ 0xea,
+ 0x5f,
+ 0xcf,
+ 0x1d,
+ 0x66,
+ 0x5b,
+ 0xba,
+ 0xf0,
+ 0xbc,
+ 0xfd,
+ // payload
+ 'g',
+ 'o',
+ 'o',
+ 'd',
+ 'b',
+ 'y',
+ 'e',
+ '!',
+ };
+ const char* data = reinterpret_cast<const char*>(expected);
+ size_t len = ABSL_ARRAYSIZE(expected);
+ NullDecrypter decrypter(Perspective::IS_CLIENT);
+ char buffer[256];
+ size_t length = 0;
+ ASSERT_FALSE(decrypter.DecryptPacket(
+ 0, "hello world!", absl::string_view(data, len), buffer, &length, 256));
+}
+
+TEST_F(NullDecrypterTest, ShortInput) {
+ unsigned char expected[] = {
+ // fnv hash (truncated)
+ 0x46, 0x11, 0xea, 0x5f, 0xcf, 0x1d, 0x66, 0x5b, 0xba, 0xf0, 0xbc,
+ };
+ const char* data = reinterpret_cast<const char*>(expected);
+ size_t len = ABSL_ARRAYSIZE(expected);
+ NullDecrypter decrypter(Perspective::IS_CLIENT);
+ char buffer[256];
+ size_t length = 0;
+ ASSERT_FALSE(decrypter.DecryptPacket(
+ 0, "hello world!", absl::string_view(data, len), buffer, &length, 256));
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/null_encrypter.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/null_encrypter.cc
new file mode 100644
index 00000000000..87a3f32ac49
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/null_encrypter.cc
@@ -0,0 +1,88 @@
+// Copyright (c) 2012 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 "quiche/quic/core/crypto/null_encrypter.h"
+
+#include "absl/numeric/int128.h"
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/quic_data_writer.h"
+#include "quiche/quic/core/quic_utils.h"
+
+namespace quic {
+
+const size_t kHashSizeShort = 12; // size of uint128 serialized short
+
+NullEncrypter::NullEncrypter(Perspective perspective)
+ : perspective_(perspective) {}
+
+bool NullEncrypter::SetKey(absl::string_view key) { return key.empty(); }
+
+bool NullEncrypter::SetNoncePrefix(absl::string_view nonce_prefix) {
+ return nonce_prefix.empty();
+}
+
+bool NullEncrypter::SetIV(absl::string_view iv) { return iv.empty(); }
+
+bool NullEncrypter::SetHeaderProtectionKey(absl::string_view key) {
+ return key.empty();
+}
+
+bool NullEncrypter::EncryptPacket(uint64_t /*packet_number*/,
+ absl::string_view associated_data,
+ absl::string_view plaintext, char* output,
+ size_t* output_length,
+ size_t max_output_length) {
+ const size_t len = plaintext.size() + GetHashLength();
+ if (max_output_length < len) {
+ return false;
+ }
+ absl::uint128 hash;
+ if (perspective_ == Perspective::IS_SERVER) {
+ hash =
+ QuicUtils::FNV1a_128_Hash_Three(associated_data, plaintext, "Server");
+ } else {
+ hash =
+ QuicUtils::FNV1a_128_Hash_Three(associated_data, plaintext, "Client");
+ }
+ // TODO(ianswett): memmove required for in place encryption. Placing the
+ // hash at the end would allow use of memcpy, doing nothing for in place.
+ memmove(output + GetHashLength(), plaintext.data(), plaintext.length());
+ QuicUtils::SerializeUint128Short(hash,
+ reinterpret_cast<unsigned char*>(output));
+ *output_length = len;
+ return true;
+}
+
+std::string NullEncrypter::GenerateHeaderProtectionMask(
+ absl::string_view /*sample*/) {
+ return std::string(5, 0);
+}
+
+size_t NullEncrypter::GetKeySize() const { return 0; }
+
+size_t NullEncrypter::GetNoncePrefixSize() const { return 0; }
+
+size_t NullEncrypter::GetIVSize() const { return 0; }
+
+size_t NullEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const {
+ return ciphertext_size - std::min(ciphertext_size, GetHashLength());
+}
+
+size_t NullEncrypter::GetCiphertextSize(size_t plaintext_size) const {
+ return plaintext_size + GetHashLength();
+}
+
+QuicPacketCount NullEncrypter::GetConfidentialityLimit() const {
+ return std::numeric_limits<QuicPacketCount>::max();
+}
+
+absl::string_view NullEncrypter::GetKey() const { return absl::string_view(); }
+
+absl::string_view NullEncrypter::GetNoncePrefix() const {
+ return absl::string_view();
+}
+
+size_t NullEncrypter::GetHashLength() const { return kHashSizeShort; }
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/null_encrypter.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/null_encrypter.h
new file mode 100644
index 00000000000..c5e599f5199
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/null_encrypter.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2012 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_NULL_ENCRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_NULL_ENCRYPTER_H_
+
+#include <cstddef>
+
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/crypto/quic_encrypter.h"
+#include "quiche/quic/core/quic_types.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// A NullEncrypter is a QuicEncrypter used before a crypto negotiation
+// has occurred. It does not actually encrypt the payload, but does
+// generate a MAC (fnv128) over both the payload and associated data.
+class QUIC_EXPORT_PRIVATE NullEncrypter : public QuicEncrypter {
+ public:
+ explicit NullEncrypter(Perspective perspective);
+ NullEncrypter(const NullEncrypter&) = delete;
+ NullEncrypter& operator=(const NullEncrypter&) = delete;
+ ~NullEncrypter() override {}
+
+ // QuicEncrypter implementation
+ bool SetKey(absl::string_view key) override;
+ bool SetNoncePrefix(absl::string_view nonce_prefix) override;
+ bool SetIV(absl::string_view iv) override;
+ bool SetHeaderProtectionKey(absl::string_view key) override;
+ bool EncryptPacket(uint64_t packet_number, absl::string_view associated_data,
+ absl::string_view plaintext, char* output,
+ size_t* output_length, size_t max_output_length) override;
+ std::string GenerateHeaderProtectionMask(absl::string_view sample) override;
+ size_t GetKeySize() const override;
+ size_t GetNoncePrefixSize() const override;
+ size_t GetIVSize() const override;
+ size_t GetMaxPlaintextSize(size_t ciphertext_size) const override;
+ size_t GetCiphertextSize(size_t plaintext_size) const override;
+ QuicPacketCount GetConfidentialityLimit() const override;
+ absl::string_view GetKey() const override;
+ absl::string_view GetNoncePrefix() const override;
+
+ private:
+ size_t GetHashLength() const;
+
+ Perspective perspective_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_NULL_ENCRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/null_encrypter_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/null_encrypter_test.cc
new file mode 100644
index 00000000000..85a30115a18
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/null_encrypter_test.cc
@@ -0,0 +1,103 @@
+// Copyright (c) 2012 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 "quiche/quic/core/crypto/null_encrypter.h"
+
+#include "absl/base/macros.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/quic_test_utils.h"
+#include "quiche/common/test_tools/quiche_test_utils.h"
+
+namespace quic {
+namespace test {
+
+class NullEncrypterTest : public QuicTestWithParam<bool> {};
+
+TEST_F(NullEncrypterTest, EncryptClient) {
+ unsigned char expected[] = {
+ // fnv hash
+ 0x97,
+ 0xdc,
+ 0x27,
+ 0x2f,
+ 0x18,
+ 0xa8,
+ 0x56,
+ 0x73,
+ 0xdf,
+ 0x8d,
+ 0x1d,
+ 0xd0,
+ // payload
+ 'g',
+ 'o',
+ 'o',
+ 'd',
+ 'b',
+ 'y',
+ 'e',
+ '!',
+ };
+ char encrypted[256];
+ size_t encrypted_len = 0;
+ NullEncrypter encrypter(Perspective::IS_CLIENT);
+ ASSERT_TRUE(encrypter.EncryptPacket(0, "hello world!", "goodbye!", encrypted,
+ &encrypted_len, 256));
+ quiche::test::CompareCharArraysWithHexError(
+ "encrypted data", encrypted, encrypted_len,
+ reinterpret_cast<const char*>(expected), ABSL_ARRAYSIZE(expected));
+}
+
+TEST_F(NullEncrypterTest, EncryptServer) {
+ unsigned char expected[] = {
+ // fnv hash
+ 0x63,
+ 0x5e,
+ 0x08,
+ 0x03,
+ 0x32,
+ 0x80,
+ 0x8f,
+ 0x73,
+ 0xdf,
+ 0x8d,
+ 0x1d,
+ 0x1a,
+ // payload
+ 'g',
+ 'o',
+ 'o',
+ 'd',
+ 'b',
+ 'y',
+ 'e',
+ '!',
+ };
+ char encrypted[256];
+ size_t encrypted_len = 0;
+ NullEncrypter encrypter(Perspective::IS_SERVER);
+ ASSERT_TRUE(encrypter.EncryptPacket(0, "hello world!", "goodbye!", encrypted,
+ &encrypted_len, 256));
+ quiche::test::CompareCharArraysWithHexError(
+ "encrypted data", encrypted, encrypted_len,
+ reinterpret_cast<const char*>(expected), ABSL_ARRAYSIZE(expected));
+}
+
+TEST_F(NullEncrypterTest, GetMaxPlaintextSize) {
+ NullEncrypter encrypter(Perspective::IS_CLIENT);
+ EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1012));
+ EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(112));
+ EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(22));
+ EXPECT_EQ(0u, encrypter.GetMaxPlaintextSize(11));
+}
+
+TEST_F(NullEncrypterTest, GetCiphertextSize) {
+ NullEncrypter encrypter(Perspective::IS_CLIENT);
+ EXPECT_EQ(1012u, encrypter.GetCiphertextSize(1000));
+ EXPECT_EQ(112u, encrypter.GetCiphertextSize(100));
+ EXPECT_EQ(22u, encrypter.GetCiphertextSize(10));
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/p256_key_exchange.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/p256_key_exchange.cc
new file mode 100644
index 00000000000..6e8e53988c5
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/p256_key_exchange.cc
@@ -0,0 +1,121 @@
+// Copyright (c) 2013 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 "quiche/quic/core/crypto/p256_key_exchange.h"
+
+#include <cstdint>
+#include <cstring>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "absl/memory/memory.h"
+#include "absl/strings/string_view.h"
+#include "openssl/ec.h"
+#include "openssl/ecdh.h"
+#include "openssl/err.h"
+#include "openssl/evp.h"
+#include "quiche/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+P256KeyExchange::P256KeyExchange(bssl::UniquePtr<EC_KEY> private_key,
+ const uint8_t* public_key)
+ : private_key_(std::move(private_key)) {
+ memcpy(public_key_, public_key, sizeof(public_key_));
+}
+
+P256KeyExchange::~P256KeyExchange() {}
+
+// static
+std::unique_ptr<P256KeyExchange> P256KeyExchange::New() {
+ return New(P256KeyExchange::NewPrivateKey());
+}
+
+// static
+std::unique_ptr<P256KeyExchange> P256KeyExchange::New(absl::string_view key) {
+ if (key.empty()) {
+ QUIC_DLOG(INFO) << "Private key is empty";
+ return nullptr;
+ }
+
+ const uint8_t* keyp = reinterpret_cast<const uint8_t*>(key.data());
+ bssl::UniquePtr<EC_KEY> private_key(
+ d2i_ECPrivateKey(nullptr, &keyp, key.size()));
+ if (!private_key.get() || !EC_KEY_check_key(private_key.get())) {
+ QUIC_DLOG(INFO) << "Private key is invalid.";
+ return nullptr;
+ }
+
+ uint8_t public_key[kUncompressedP256PointBytes];
+ if (EC_POINT_point2oct(EC_KEY_get0_group(private_key.get()),
+ EC_KEY_get0_public_key(private_key.get()),
+ POINT_CONVERSION_UNCOMPRESSED, public_key,
+ sizeof(public_key), nullptr) != sizeof(public_key)) {
+ QUIC_DLOG(INFO) << "Can't get public key.";
+ return nullptr;
+ }
+
+ return absl::WrapUnique(
+ new P256KeyExchange(std::move(private_key), public_key));
+}
+
+// static
+std::string P256KeyExchange::NewPrivateKey() {
+ bssl::UniquePtr<EC_KEY> key(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
+ if (!key.get() || !EC_KEY_generate_key(key.get())) {
+ QUIC_DLOG(INFO) << "Can't generate a new private key.";
+ return std::string();
+ }
+
+ int key_len = i2d_ECPrivateKey(key.get(), nullptr);
+ if (key_len <= 0) {
+ QUIC_DLOG(INFO) << "Can't convert private key to string";
+ return std::string();
+ }
+ std::unique_ptr<uint8_t[]> private_key(new uint8_t[key_len]);
+ uint8_t* keyp = private_key.get();
+ if (!i2d_ECPrivateKey(key.get(), &keyp)) {
+ QUIC_DLOG(INFO) << "Can't convert private key to string.";
+ return std::string();
+ }
+ return std::string(reinterpret_cast<char*>(private_key.get()), key_len);
+}
+
+bool P256KeyExchange::CalculateSharedKeySync(
+ absl::string_view peer_public_value, std::string* shared_key) const {
+ if (peer_public_value.size() != kUncompressedP256PointBytes) {
+ QUIC_DLOG(INFO) << "Peer public value is invalid";
+ return false;
+ }
+
+ bssl::UniquePtr<EC_POINT> point(
+ EC_POINT_new(EC_KEY_get0_group(private_key_.get())));
+ if (!point.get() ||
+ !EC_POINT_oct2point(/* also test if point is on curve */
+ EC_KEY_get0_group(private_key_.get()), point.get(),
+ reinterpret_cast<const uint8_t*>(
+ peer_public_value.data()),
+ peer_public_value.size(), nullptr)) {
+ QUIC_DLOG(INFO) << "Can't convert peer public value to curve point.";
+ return false;
+ }
+
+ uint8_t result[kP256FieldBytes];
+ if (ECDH_compute_key(result, sizeof(result), point.get(), private_key_.get(),
+ nullptr) != sizeof(result)) {
+ QUIC_DLOG(INFO) << "Can't compute ECDH shared key.";
+ return false;
+ }
+
+ shared_key->assign(reinterpret_cast<char*>(result), sizeof(result));
+ return true;
+}
+
+absl::string_view P256KeyExchange::public_value() const {
+ return absl::string_view(reinterpret_cast<const char*>(public_key_),
+ sizeof(public_key_));
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/p256_key_exchange.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/p256_key_exchange.h
new file mode 100644
index 00000000000..1341331f584
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/p256_key_exchange.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2013 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_P256_KEY_EXCHANGE_H_
+#define QUICHE_QUIC_CORE_CRYPTO_P256_KEY_EXCHANGE_H_
+
+#include <cstdint>
+#include <string>
+
+#include "absl/strings/string_view.h"
+#include "openssl/base.h"
+#include "quiche/quic/core/crypto/key_exchange.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// P256KeyExchange implements a SynchronousKeyExchange using elliptic-curve
+// Diffie-Hellman on NIST P-256.
+class QUIC_EXPORT_PRIVATE P256KeyExchange : public SynchronousKeyExchange {
+ public:
+ ~P256KeyExchange() override;
+
+ // New generates a private key and then creates new key-exchange object.
+ static std::unique_ptr<P256KeyExchange> New();
+
+ // New creates a new key-exchange object from a private key. If |private_key|
+ // is invalid, nullptr is returned.
+ static std::unique_ptr<P256KeyExchange> New(absl::string_view private_key);
+
+ // NewPrivateKey returns a private key, suitable for passing to |New|.
+ // If |NewPrivateKey| can't generate a private key, it returns an empty
+ // string.
+ static std::string NewPrivateKey();
+
+ // SynchronousKeyExchange interface.
+ bool CalculateSharedKeySync(absl::string_view peer_public_value,
+ std::string* shared_key) const override;
+ absl::string_view public_value() const override;
+ QuicTag type() const override { return kP256; }
+
+ private:
+ enum {
+ // A P-256 field element consists of 32 bytes.
+ kP256FieldBytes = 32,
+ // A P-256 point in uncompressed form consists of 0x04 (to denote
+ // that the point is uncompressed) followed by two, 32-byte field
+ // elements.
+ kUncompressedP256PointBytes = 1 + 2 * kP256FieldBytes,
+ // The first byte in an uncompressed P-256 point.
+ kUncompressedECPointForm = 0x04,
+ };
+
+ // P256KeyExchange wraps |private_key|, and expects |public_key| consists of
+ // |kUncompressedP256PointBytes| bytes.
+ P256KeyExchange(bssl::UniquePtr<EC_KEY> private_key,
+ const uint8_t* public_key);
+ P256KeyExchange(const P256KeyExchange&) = delete;
+ P256KeyExchange& operator=(const P256KeyExchange&) = delete;
+
+ bssl::UniquePtr<EC_KEY> private_key_;
+ // The public key stored as an uncompressed P-256 point.
+ uint8_t public_key_[kUncompressedP256PointBytes];
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_P256_KEY_EXCHANGE_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/p256_key_exchange_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/p256_key_exchange_test.cc
new file mode 100644
index 00000000000..c9bc7d3f094
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/p256_key_exchange_test.cc
@@ -0,0 +1,109 @@
+// Copyright (c) 2013 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 "quiche/quic/core/crypto/p256_key_exchange.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "absl/strings/string_view.h"
+#include "quiche/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+
+class P256KeyExchangeTest : public QuicTest {
+ public:
+ // Holds the result of a key exchange callback.
+ class TestCallbackResult {
+ public:
+ void set_ok(bool ok) { ok_ = ok; }
+ bool ok() { return ok_; }
+
+ private:
+ bool ok_ = false;
+ };
+
+ // Key exchange callback which sets the result into the specified
+ // TestCallbackResult.
+ class TestCallback : public AsynchronousKeyExchange::Callback {
+ public:
+ TestCallback(TestCallbackResult* result) : result_(result) {}
+ virtual ~TestCallback() = default;
+
+ void Run(bool ok) { result_->set_ok(ok); }
+
+ private:
+ TestCallbackResult* result_;
+ };
+};
+
+// SharedKeyAsync just tests that the basic asynchronous key exchange identity
+// holds: that both parties end up with the same key.
+TEST_F(P256KeyExchangeTest, SharedKey) {
+ for (int i = 0; i < 5; i++) {
+ std::string alice_private(P256KeyExchange::NewPrivateKey());
+ std::string bob_private(P256KeyExchange::NewPrivateKey());
+
+ ASSERT_FALSE(alice_private.empty());
+ ASSERT_FALSE(bob_private.empty());
+ ASSERT_NE(alice_private, bob_private);
+
+ std::unique_ptr<P256KeyExchange> alice(P256KeyExchange::New(alice_private));
+ std::unique_ptr<P256KeyExchange> bob(P256KeyExchange::New(bob_private));
+
+ ASSERT_TRUE(alice != nullptr);
+ ASSERT_TRUE(bob != nullptr);
+
+ const absl::string_view alice_public(alice->public_value());
+ const absl::string_view bob_public(bob->public_value());
+
+ std::string alice_shared, bob_shared;
+ ASSERT_TRUE(alice->CalculateSharedKeySync(bob_public, &alice_shared));
+ ASSERT_TRUE(bob->CalculateSharedKeySync(alice_public, &bob_shared));
+ ASSERT_EQ(alice_shared, bob_shared);
+ }
+}
+
+// SharedKey just tests that the basic key exchange identity holds: that both
+// parties end up with the same key.
+TEST_F(P256KeyExchangeTest, AsyncSharedKey) {
+ for (int i = 0; i < 5; i++) {
+ std::string alice_private(P256KeyExchange::NewPrivateKey());
+ std::string bob_private(P256KeyExchange::NewPrivateKey());
+
+ ASSERT_FALSE(alice_private.empty());
+ ASSERT_FALSE(bob_private.empty());
+ ASSERT_NE(alice_private, bob_private);
+
+ std::unique_ptr<P256KeyExchange> alice(P256KeyExchange::New(alice_private));
+ std::unique_ptr<P256KeyExchange> bob(P256KeyExchange::New(bob_private));
+
+ ASSERT_TRUE(alice != nullptr);
+ ASSERT_TRUE(bob != nullptr);
+
+ const absl::string_view alice_public(alice->public_value());
+ const absl::string_view bob_public(bob->public_value());
+
+ std::string alice_shared, bob_shared;
+ TestCallbackResult alice_result;
+ ASSERT_FALSE(alice_result.ok());
+ alice->CalculateSharedKeyAsync(
+ bob_public, &alice_shared,
+ std::make_unique<TestCallback>(&alice_result));
+ ASSERT_TRUE(alice_result.ok());
+ TestCallbackResult bob_result;
+ ASSERT_FALSE(bob_result.ok());
+ bob->CalculateSharedKeyAsync(alice_public, &bob_shared,
+ std::make_unique<TestCallback>(&bob_result));
+ ASSERT_TRUE(bob_result.ok());
+ ASSERT_EQ(alice_shared, bob_shared);
+ ASSERT_NE(0u, alice_shared.length());
+ ASSERT_NE(0u, bob_shared.length());
+ }
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/proof_source.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/proof_source.cc
new file mode 100644
index 00000000000..95fb44638d9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/proof_source.cc
@@ -0,0 +1,59 @@
+// Copyright (c) 2016 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 "quiche/quic/core/crypto/proof_source.h"
+
+#include <string>
+
+#include "quiche/quic/platform/api/quic_bug_tracker.h"
+
+namespace quic {
+
+CryptoBuffers::~CryptoBuffers() {
+ for (size_t i = 0; i < value.size(); i++) {
+ CRYPTO_BUFFER_free(value[i]);
+ }
+}
+
+ProofSource::Chain::Chain(const std::vector<std::string>& certs)
+ : certs(certs) {}
+
+ProofSource::Chain::~Chain() {}
+
+CryptoBuffers ProofSource::Chain::ToCryptoBuffers() const {
+ CryptoBuffers crypto_buffers;
+ crypto_buffers.value.reserve(certs.size());
+ for (size_t i = 0; i < certs.size(); i++) {
+ crypto_buffers.value.push_back(
+ CRYPTO_BUFFER_new(reinterpret_cast<const uint8_t*>(certs[i].data()),
+ certs[i].length(), nullptr));
+ }
+ return crypto_buffers;
+}
+
+bool ValidateCertAndKey(
+ const quiche::QuicheReferenceCountedPointer<ProofSource::Chain>& chain,
+ const CertificatePrivateKey& key) {
+ if (chain.get() == nullptr || chain->certs.empty()) {
+ QUIC_BUG(quic_proof_source_empty_chain) << "Certificate chain is empty";
+ return false;
+ }
+
+ std::unique_ptr<CertificateView> leaf =
+ CertificateView::ParseSingleCertificate(chain->certs[0]);
+ if (leaf == nullptr) {
+ QUIC_BUG(quic_proof_source_unparsable_leaf_cert)
+ << "Unabled to parse leaf certificate";
+ return false;
+ }
+
+ if (!key.MatchesPublicKey(*leaf)) {
+ QUIC_BUG(quic_proof_source_key_mismatch)
+ << "Private key does not match the leaf certificate";
+ return false;
+ }
+ return true;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/proof_source.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/proof_source.h
new file mode 100644
index 00000000000..ffa5fec21bf
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/proof_source.h
@@ -0,0 +1,346 @@
+// Copyright (c) 2013 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_PROOF_SOURCE_H_
+#define QUICHE_QUIC_CORE_CRYPTO_PROOF_SOURCE_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "openssl/ssl.h"
+#include "quiche/quic/core/crypto/certificate_view.h"
+#include "quiche/quic/core/crypto/quic_crypto_proof.h"
+#include "quiche/quic/core/quic_versions.h"
+#include "quiche/quic/platform/api/quic_export.h"
+#include "quiche/quic/platform/api/quic_socket_address.h"
+#include "quiche/common/platform/api/quiche_reference_counted.h"
+
+namespace quic {
+
+namespace test {
+class FakeProofSourceHandle;
+} // namespace test
+
+// CryptoBuffers is a RAII class to own a std::vector<CRYPTO_BUFFER*> and the
+// buffers the elements point to.
+struct QUIC_EXPORT_PRIVATE CryptoBuffers {
+ CryptoBuffers() = default;
+ CryptoBuffers(const CryptoBuffers&) = delete;
+ CryptoBuffers(CryptoBuffers&&) = default;
+ ~CryptoBuffers();
+
+ std::vector<CRYPTO_BUFFER*> value;
+};
+
+// ProofSource is an interface by which a QUIC server can obtain certificate
+// chains and signatures that prove its identity.
+class QUIC_EXPORT_PRIVATE ProofSource {
+ public:
+ // Chain is a reference-counted wrapper for a vector of stringified
+ // certificates.
+ struct QUIC_EXPORT_PRIVATE Chain : public quiche::QuicheReferenceCounted {
+ explicit Chain(const std::vector<std::string>& certs);
+ Chain(const Chain&) = delete;
+ Chain& operator=(const Chain&) = delete;
+
+ CryptoBuffers ToCryptoBuffers() const;
+
+ const std::vector<std::string> certs;
+
+ protected:
+ ~Chain() override;
+ };
+
+ // Details is an abstract class which acts as a container for any
+ // implementation-specific details that a ProofSource wants to return.
+ class QUIC_EXPORT_PRIVATE Details {
+ public:
+ virtual ~Details() {}
+ };
+
+ // Callback base class for receiving the results of an async call to GetProof.
+ class QUIC_EXPORT_PRIVATE Callback {
+ public:
+ Callback() {}
+ virtual ~Callback() {}
+
+ // Invoked upon completion of GetProof.
+ //
+ // |ok| indicates whether the operation completed successfully. If false,
+ // the values of the remaining three arguments are undefined.
+ //
+ // |chain| is a reference-counted pointer to an object representing the
+ // certificate chain.
+ //
+ // |signature| contains the signature of the server config.
+ //
+ // |leaf_cert_sct| holds the signed timestamp (RFC6962) of the leaf cert.
+ //
+ // |details| holds a pointer to an object representing the statistics, if
+ // any, gathered during the operation of GetProof. If no stats are
+ // available, this will be nullptr.
+ virtual void Run(bool ok,
+ const quiche::QuicheReferenceCountedPointer<Chain>& chain,
+ const QuicCryptoProof& proof,
+ std::unique_ptr<Details> details) = 0;
+
+ private:
+ Callback(const Callback&) = delete;
+ Callback& operator=(const Callback&) = delete;
+ };
+
+ // Base class for signalling the completion of a call to ComputeTlsSignature.
+ class QUIC_EXPORT_PRIVATE SignatureCallback {
+ public:
+ SignatureCallback() {}
+ virtual ~SignatureCallback() = default;
+
+ // Invoked upon completion of ComputeTlsSignature.
+ //
+ // |ok| indicates whether the operation completed successfully.
+ //
+ // |signature| contains the signature of the data provided to
+ // ComputeTlsSignature. Its value is undefined if |ok| is false.
+ //
+ // |details| holds a pointer to an object representing the statistics, if
+ // any, gathered during the operation of ComputeTlsSignature. If no stats
+ // are available, this will be nullptr.
+ virtual void Run(bool ok, std::string signature,
+ std::unique_ptr<Details> details) = 0;
+
+ private:
+ SignatureCallback(const SignatureCallback&) = delete;
+ SignatureCallback& operator=(const SignatureCallback&) = delete;
+ };
+
+ virtual ~ProofSource() {}
+
+ // GetProof finds a certificate chain for |hostname| (in leaf-first order),
+ // and calculates a signature of |server_config| using that chain.
+ //
+ // The signature uses SHA-256 as the hash function and PSS padding when the
+ // key is RSA.
+ //
+ // The signature uses SHA-256 as the hash function when the key is ECDSA.
+ // The signature may use an ECDSA key.
+ //
+ // The signature depends on |chlo_hash| which means that the signature can not
+ // be cached.
+ //
+ // |hostname| may be empty to signify that a default certificate should be
+ // used.
+ //
+ // This function may be called concurrently.
+ //
+ // Callers should expect that |callback| might be invoked synchronously.
+ virtual void GetProof(const QuicSocketAddress& server_address,
+ const QuicSocketAddress& client_address,
+ const std::string& hostname,
+ const std::string& server_config,
+ QuicTransportVersion transport_version,
+ absl::string_view chlo_hash,
+ std::unique_ptr<Callback> callback) = 0;
+
+ // Returns the certificate chain for |hostname| in leaf-first order.
+ //
+ // Sets *cert_matched_sni to true if the certificate matched the given
+ // hostname, false if a default cert not matching the hostname was used.
+ virtual quiche::QuicheReferenceCountedPointer<Chain> GetCertChain(
+ const QuicSocketAddress& server_address,
+ const QuicSocketAddress& client_address, const std::string& hostname,
+ bool* cert_matched_sni) = 0;
+
+ // Computes a signature using the private key of the certificate for
+ // |hostname|. The value in |in| is signed using the algorithm specified by
+ // |signature_algorithm|, which is an |SSL_SIGN_*| value (as defined in TLS
+ // 1.3). Implementations can only assume that |in| is valid during the call to
+ // ComputeTlsSignature - an implementation computing signatures asynchronously
+ // must copy it if the value to be signed is used outside of this function.
+ //
+ // Callers should expect that |callback| might be invoked synchronously.
+ virtual void ComputeTlsSignature(
+ const QuicSocketAddress& server_address,
+ const QuicSocketAddress& client_address, const std::string& hostname,
+ uint16_t signature_algorithm, absl::string_view in,
+ std::unique_ptr<SignatureCallback> callback) = 0;
+
+ // Return the list of TLS signature algorithms that is acceptable by the
+ // ComputeTlsSignature method. If the entire BoringSSL's default list of
+ // supported signature algorithms are acceptable, return an empty list.
+ //
+ // If returns a non-empty list, ComputeTlsSignature will only be called with a
+ // algorithm in the list.
+ virtual absl::InlinedVector<uint16_t, 8> SupportedTlsSignatureAlgorithms()
+ const = 0;
+
+ class QUIC_EXPORT_PRIVATE DecryptCallback {
+ public:
+ DecryptCallback() = default;
+ virtual ~DecryptCallback() = default;
+
+ virtual void Run(std::vector<uint8_t> plaintext) = 0;
+
+ private:
+ DecryptCallback(const Callback&) = delete;
+ DecryptCallback& operator=(const Callback&) = delete;
+ };
+
+ // TicketCrypter is an interface for managing encryption and decryption of TLS
+ // session tickets. A TicketCrypter gets used as an
+ // SSL_CTX_set_ticket_aead_method in BoringSSL, which has a synchronous
+ // Encrypt/Seal operation and a potentially asynchronous Decrypt/Open
+ // operation. This interface allows for ticket decryptions to be performed on
+ // a remote service.
+ class QUIC_EXPORT_PRIVATE TicketCrypter {
+ public:
+ TicketCrypter() = default;
+ virtual ~TicketCrypter() = default;
+
+ // MaxOverhead returns the maximum number of bytes of overhead that may get
+ // added when encrypting the ticket.
+ virtual size_t MaxOverhead() = 0;
+
+ // Encrypt takes a serialized TLS session ticket in |in|, encrypts it, and
+ // returns the encrypted ticket. The resulting value must not be larger than
+ // MaxOverhead bytes larger than |in|. If encryption fails, this method
+ // returns an empty vector.
+ //
+ // If |encryption_key| is nonempty, this method should use it for minting
+ // TLS resumption tickets. If it is empty, this method may use an
+ // internally cached encryption key, if available.
+ virtual std::vector<uint8_t> Encrypt(absl::string_view in,
+ absl::string_view encryption_key) = 0;
+
+ // Decrypt takes an encrypted ticket |in|, decrypts it, and calls
+ // |callback->Run| with the decrypted ticket, which must not be larger than
+ // |in|. If decryption fails, the callback is invoked with an empty
+ // vector.
+ virtual void Decrypt(absl::string_view in,
+ std::unique_ptr<DecryptCallback> callback) = 0;
+ };
+
+ // Returns the TicketCrypter used for encrypting and decrypting TLS
+ // session tickets, or nullptr if that functionality is not supported. The
+ // TicketCrypter returned (if not nullptr) must be valid for the lifetime of
+ // the ProofSource, and the caller does not take ownership of said
+ // TicketCrypter.
+ virtual TicketCrypter* GetTicketCrypter() = 0;
+};
+
+// ProofSourceHandleCallback is an interface that contains the callbacks when
+// the operations in ProofSourceHandle completes.
+// TODO(wub): Consider deprecating ProofSource by moving all functionalities of
+// ProofSource into ProofSourceHandle.
+class QUIC_EXPORT_PRIVATE ProofSourceHandleCallback {
+ public:
+ virtual ~ProofSourceHandleCallback() = default;
+
+ // Called when a ProofSourceHandle::SelectCertificate operation completes.
+ // |ok| indicates whether the operation was successful.
+ // |is_sync| indicates whether the operation completed synchronously, i.e.
+ // whether it is completed before ProofSourceHandle::SelectCertificate
+ // returned.
+ // |chain| the certificate chain in leaf-first order.
+ // |handshake_hints| (optional) handshake hints that can be used by
+ // SSL_set_handshake_hints.
+ // |ticket_encryption_key| (optional) encryption key to be used for minting
+ // TLS resumption tickets.
+ // |cert_matched_sni| is true if the certificate matched the SNI hostname,
+ // false if a non-matching default cert was used.
+ // |delayed_ssl_config| contains SSL configs to be applied on the SSL object.
+ //
+ // When called asynchronously(is_sync=false), this method will be responsible
+ // to continue the handshake from where it left off.
+ virtual void OnSelectCertificateDone(
+ bool ok, bool is_sync, const ProofSource::Chain* chain,
+ absl::string_view handshake_hints,
+ absl::string_view ticket_encryption_key, bool cert_matched_sni,
+ QuicDelayedSSLConfig delayed_ssl_config) = 0;
+
+ // Called when a ProofSourceHandle::ComputeSignature operation completes.
+ virtual void OnComputeSignatureDone(
+ bool ok, bool is_sync, std::string signature,
+ std::unique_ptr<ProofSource::Details> details) = 0;
+
+ // Return true iff ProofSourceHandle::ComputeSignature won't be called later.
+ // The handle can use this function to release resources promptly.
+ virtual bool WillNotCallComputeSignature() const = 0;
+};
+
+// ProofSourceHandle is an interface by which a TlsServerHandshaker can obtain
+// certificate chains and signatures that prove its identity.
+// The operations this interface supports are similar to those in ProofSource,
+// the main difference is that ProofSourceHandle is per-handshaker, so
+// an implementation can have states that are shared by multiple calls on the
+// same handle.
+//
+// A handle object is owned by a TlsServerHandshaker. Since there might be an
+// async operation pending when the handle destructs, an implementation must
+// ensure when such operations finish, their corresponding callback method won't
+// be invoked.
+//
+// A handle will have at most one async operation pending at a time.
+class QUIC_EXPORT_PRIVATE ProofSourceHandle {
+ public:
+ virtual ~ProofSourceHandle() = default;
+
+ // Close the handle. Cancel the pending operation, if any.
+ // Once called, any completion method on |callback()| won't be invoked, and
+ // future SelectCertificate and ComputeSignature calls should return failure.
+ virtual void CloseHandle() = 0;
+
+ // Starts a select certificate operation. If the operation is not cancelled
+ // when it completes, callback()->OnSelectCertificateDone will be invoked.
+ //
+ // server_address and client_address should be normalized by the caller before
+ // sending down to this function.
+ //
+ // If the operation is handled synchronously:
+ // - QUIC_SUCCESS or QUIC_FAILURE will be returned.
+ // - callback()->OnSelectCertificateDone should be invoked before the function
+ // returns.
+ //
+ // If the operation is handled asynchronously:
+ // - QUIC_PENDING will be returned.
+ // - When the operation is done, callback()->OnSelectCertificateDone should be
+ // invoked.
+ virtual QuicAsyncStatus SelectCertificate(
+ const QuicSocketAddress& server_address,
+ const QuicSocketAddress& client_address,
+ absl::string_view ssl_capabilities, const std::string& hostname,
+ absl::string_view client_hello, const std::string& alpn,
+ absl::optional<std::string> alps,
+ const std::vector<uint8_t>& quic_transport_params,
+ const absl::optional<std::vector<uint8_t>>& early_data_context,
+ const QuicSSLConfig& ssl_config) = 0;
+
+ // Starts a compute signature operation. If the operation is not cancelled
+ // when it completes, callback()->OnComputeSignatureDone will be invoked.
+ //
+ // See the comments of SelectCertificate for sync vs. async operations.
+ virtual QuicAsyncStatus ComputeSignature(
+ const QuicSocketAddress& server_address,
+ const QuicSocketAddress& client_address, const std::string& hostname,
+ uint16_t signature_algorithm, absl::string_view in,
+ size_t max_signature_size) = 0;
+
+ protected:
+ // Returns the object that will be notified when an operation completes.
+ virtual ProofSourceHandleCallback* callback() = 0;
+
+ private:
+ friend class test::FakeProofSourceHandle;
+};
+
+// Returns true if |chain| contains a parsable DER-encoded X.509 leaf cert and
+// it matches with |key|.
+QUIC_EXPORT_PRIVATE bool ValidateCertAndKey(
+ const quiche::QuicheReferenceCountedPointer<ProofSource::Chain>& chain,
+ const CertificatePrivateKey& key);
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_PROOF_SOURCE_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/proof_source_x509.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/proof_source_x509.cc
new file mode 100644
index 00000000000..5f26855d7c4
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/proof_source_x509.cc
@@ -0,0 +1,145 @@
+// 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 "quiche/quic/core/crypto/proof_source_x509.h"
+
+#include <memory>
+
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "openssl/ssl.h"
+#include "quiche/quic/core/crypto/certificate_view.h"
+#include "quiche/quic/core/crypto/crypto_protocol.h"
+#include "quiche/quic/core/quic_data_writer.h"
+#include "quiche/quic/platform/api/quic_bug_tracker.h"
+#include "quiche/common/quiche_endian.h"
+
+namespace quic {
+
+std::unique_ptr<ProofSourceX509> ProofSourceX509::Create(
+ quiche::QuicheReferenceCountedPointer<Chain> default_chain,
+ CertificatePrivateKey default_key) {
+ std::unique_ptr<ProofSourceX509> result(new ProofSourceX509());
+ if (!result->AddCertificateChain(default_chain, std::move(default_key))) {
+ return nullptr;
+ }
+ result->default_certificate_ = &result->certificates_.front();
+ return result;
+}
+
+void ProofSourceX509::GetProof(
+ const QuicSocketAddress& /*server_address*/,
+ const QuicSocketAddress& /*client_address*/, const std::string& hostname,
+ const std::string& server_config,
+ QuicTransportVersion /*transport_version*/, absl::string_view chlo_hash,
+ std::unique_ptr<ProofSource::Callback> callback) {
+ QuicCryptoProof proof;
+
+ size_t payload_size = sizeof(kProofSignatureLabel) + sizeof(uint32_t) +
+ chlo_hash.size() + server_config.size();
+ auto payload = std::make_unique<char[]>(payload_size);
+ QuicDataWriter payload_writer(payload_size, payload.get(),
+ quiche::Endianness::HOST_BYTE_ORDER);
+ bool success = payload_writer.WriteBytes(kProofSignatureLabel,
+ sizeof(kProofSignatureLabel)) &&
+ payload_writer.WriteUInt32(chlo_hash.size()) &&
+ payload_writer.WriteStringPiece(chlo_hash) &&
+ payload_writer.WriteStringPiece(server_config);
+ if (!success) {
+ callback->Run(/*ok=*/false, nullptr, proof, nullptr);
+ return;
+ }
+
+ Certificate* certificate = GetCertificate(hostname, &proof.cert_matched_sni);
+ proof.signature =
+ certificate->key.Sign(absl::string_view(payload.get(), payload_size),
+ SSL_SIGN_RSA_PSS_RSAE_SHA256);
+ callback->Run(/*ok=*/!proof.signature.empty(), certificate->chain, proof,
+ nullptr);
+}
+
+quiche::QuicheReferenceCountedPointer<ProofSource::Chain>
+ProofSourceX509::GetCertChain(const QuicSocketAddress& /*server_address*/,
+ const QuicSocketAddress& /*client_address*/,
+ const std::string& hostname,
+ bool* cert_matched_sni) {
+ return GetCertificate(hostname, cert_matched_sni)->chain;
+}
+
+void ProofSourceX509::ComputeTlsSignature(
+ const QuicSocketAddress& /*server_address*/,
+ const QuicSocketAddress& /*client_address*/, const std::string& hostname,
+ uint16_t signature_algorithm, absl::string_view in,
+ std::unique_ptr<ProofSource::SignatureCallback> callback) {
+ bool cert_matched_sni;
+ std::string signature = GetCertificate(hostname, &cert_matched_sni)
+ ->key.Sign(in, signature_algorithm);
+ callback->Run(/*ok=*/!signature.empty(), signature, nullptr);
+}
+
+absl::InlinedVector<uint16_t, 8>
+ProofSourceX509::SupportedTlsSignatureAlgorithms() const {
+ // Let ComputeTlsSignature() report an error if a bad signature algorithm is
+ // requested.
+ return {};
+}
+
+ProofSource::TicketCrypter* ProofSourceX509::GetTicketCrypter() {
+ return nullptr;
+}
+
+bool ProofSourceX509::AddCertificateChain(
+ quiche::QuicheReferenceCountedPointer<Chain> chain,
+ CertificatePrivateKey key) {
+ if (chain->certs.empty()) {
+ QUIC_BUG(quic_bug_10644_1) << "Empty certificate chain supplied.";
+ return false;
+ }
+
+ std::unique_ptr<CertificateView> leaf =
+ CertificateView::ParseSingleCertificate(chain->certs[0]);
+ if (leaf == nullptr) {
+ QUIC_BUG(quic_bug_10644_2)
+ << "Unable to parse X.509 leaf certificate in the supplied chain.";
+ return false;
+ }
+ if (!key.MatchesPublicKey(*leaf)) {
+ QUIC_BUG(quic_bug_10644_3)
+ << "Private key does not match the leaf certificate.";
+ return false;
+ }
+
+ certificates_.push_front(Certificate{
+ chain,
+ std::move(key),
+ });
+ Certificate* certificate = &certificates_.front();
+
+ for (absl::string_view host : leaf->subject_alt_name_domains()) {
+ certificate_map_[std::string(host)] = certificate;
+ }
+ return true;
+}
+
+ProofSourceX509::Certificate* ProofSourceX509::GetCertificate(
+ const std::string& hostname, bool* cert_matched_sni) const {
+ auto it = certificate_map_.find(hostname);
+ if (it != certificate_map_.end()) {
+ *cert_matched_sni = true;
+ return it->second;
+ }
+ auto dot_pos = hostname.find('.');
+ if (dot_pos != std::string::npos) {
+ std::string wildcard = absl::StrCat("*", hostname.substr(dot_pos));
+ it = certificate_map_.find(wildcard);
+ if (it != certificate_map_.end()) {
+ *cert_matched_sni = true;
+ return it->second;
+ }
+ }
+ *cert_matched_sni = false;
+ return default_certificate_;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/proof_source_x509.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/proof_source_x509.h
new file mode 100644
index 00000000000..d5a3c6f1e4c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/proof_source_x509.h
@@ -0,0 +1,77 @@
+// 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_PROOF_SOURCE_X509_H_
+#define QUICHE_QUIC_CORE_CRYPTO_PROOF_SOURCE_X509_H_
+
+#include <forward_list>
+#include <memory>
+
+#include "absl/base/attributes.h"
+#include "absl/container/node_hash_map.h"
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/crypto/certificate_view.h"
+#include "quiche/quic/core/crypto/proof_source.h"
+#include "quiche/quic/platform/api/quic_containers.h"
+
+namespace quic {
+
+// ProofSourceX509 accepts X.509 certificates with private keys and picks a
+// certificate internally based on its SubjectAltName value.
+class QUIC_EXPORT_PRIVATE ProofSourceX509 : public ProofSource {
+ public:
+ // Creates a proof source that uses |default_chain| when no SubjectAltName
+ // value matches. Returns nullptr if |default_chain| is invalid.
+ static std::unique_ptr<ProofSourceX509> Create(
+ quiche::QuicheReferenceCountedPointer<Chain> default_chain,
+ CertificatePrivateKey default_key);
+
+ // ProofSource implementation.
+ void GetProof(const QuicSocketAddress& server_address,
+ const QuicSocketAddress& client_address,
+ const std::string& hostname, const std::string& server_config,
+ QuicTransportVersion transport_version,
+ absl::string_view chlo_hash,
+ std::unique_ptr<Callback> callback) override;
+ quiche::QuicheReferenceCountedPointer<Chain> GetCertChain(
+ const QuicSocketAddress& server_address,
+ const QuicSocketAddress& client_address, const std::string& hostname,
+ bool* cert_matched_sni) override;
+ void ComputeTlsSignature(
+ const QuicSocketAddress& server_address,
+ const QuicSocketAddress& client_address, const std::string& hostname,
+ uint16_t signature_algorithm, absl::string_view in,
+ std::unique_ptr<SignatureCallback> callback) override;
+ absl::InlinedVector<uint16_t, 8> SupportedTlsSignatureAlgorithms()
+ const override;
+ TicketCrypter* GetTicketCrypter() override;
+
+ // Adds a certificate chain to the verifier. Returns false if the chain is
+ // not valid. Newer certificates will override older certificates with the
+ // same SubjectAltName value.
+ ABSL_MUST_USE_RESULT bool AddCertificateChain(
+ quiche::QuicheReferenceCountedPointer<Chain> chain,
+ CertificatePrivateKey key);
+
+ private:
+ ProofSourceX509() = default;
+
+ struct QUIC_EXPORT_PRIVATE Certificate {
+ quiche::QuicheReferenceCountedPointer<Chain> chain;
+ CertificatePrivateKey key;
+ };
+
+ // Looks up certficiate for hostname, returns the default if no certificate is
+ // found.
+ Certificate* GetCertificate(const std::string& hostname,
+ bool* cert_matched_sni) const;
+
+ std::forward_list<Certificate> certificates_;
+ Certificate* default_certificate_;
+ absl::node_hash_map<std::string, Certificate*> certificate_map_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_PROOF_SOURCE_X509_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/proof_source_x509_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/proof_source_x509_test.cc
new file mode 100644
index 00000000000..6db9c75ca1b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/proof_source_x509_test.cc
@@ -0,0 +1,142 @@
+// 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 "quiche/quic/core/crypto/proof_source_x509.h"
+
+#include <memory>
+
+#include "absl/strings/string_view.h"
+#include "openssl/ssl.h"
+#include "quiche/quic/core/crypto/certificate_view.h"
+#include "quiche/quic/core/crypto/proof_source.h"
+#include "quiche/quic/platform/api/quic_expect_bug.h"
+#include "quiche/quic/platform/api/quic_ip_address.h"
+#include "quiche/quic/platform/api/quic_socket_address.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/test_certificates.h"
+#include "quiche/common/platform/api/quiche_reference_counted.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+quiche::QuicheReferenceCountedPointer<ProofSource::Chain> MakeChain(
+ absl::string_view cert) {
+ return quiche::QuicheReferenceCountedPointer<ProofSource::Chain>(
+ new ProofSource::Chain(std::vector<std::string>{std::string(cert)}));
+}
+
+class ProofSourceX509Test : public QuicTest {
+ public:
+ ProofSourceX509Test()
+ : test_chain_(MakeChain(kTestCertificate)),
+ wildcard_chain_(MakeChain(kWildcardCertificate)),
+ test_key_(
+ CertificatePrivateKey::LoadFromDer(kTestCertificatePrivateKey)),
+ wildcard_key_(CertificatePrivateKey::LoadFromDer(
+ kWildcardCertificatePrivateKey)) {
+ QUICHE_CHECK(test_key_ != nullptr);
+ QUICHE_CHECK(wildcard_key_ != nullptr);
+ }
+
+ protected:
+ quiche::QuicheReferenceCountedPointer<ProofSource::Chain> test_chain_,
+ wildcard_chain_;
+ std::unique_ptr<CertificatePrivateKey> test_key_, wildcard_key_;
+};
+
+TEST_F(ProofSourceX509Test, AddCertificates) {
+ std::unique_ptr<ProofSourceX509> proof_source =
+ ProofSourceX509::Create(test_chain_, std::move(*test_key_));
+ ASSERT_TRUE(proof_source != nullptr);
+ EXPECT_TRUE(proof_source->AddCertificateChain(wildcard_chain_,
+ std::move(*wildcard_key_)));
+}
+
+TEST_F(ProofSourceX509Test, AddCertificateKeyMismatch) {
+ std::unique_ptr<ProofSourceX509> proof_source =
+ ProofSourceX509::Create(test_chain_, std::move(*test_key_));
+ ASSERT_TRUE(proof_source != nullptr);
+ test_key_ = CertificatePrivateKey::LoadFromDer(kTestCertificatePrivateKey);
+ EXPECT_QUIC_BUG((void)proof_source->AddCertificateChain(
+ wildcard_chain_, std::move(*test_key_)),
+ "Private key does not match");
+}
+
+TEST_F(ProofSourceX509Test, CertificateSelection) {
+ std::unique_ptr<ProofSourceX509> proof_source =
+ ProofSourceX509::Create(test_chain_, std::move(*test_key_));
+ ASSERT_TRUE(proof_source != nullptr);
+ ASSERT_TRUE(proof_source->AddCertificateChain(wildcard_chain_,
+ std::move(*wildcard_key_)));
+
+ // Default certificate.
+ bool cert_matched_sni;
+ EXPECT_EQ(proof_source
+ ->GetCertChain(QuicSocketAddress(), QuicSocketAddress(),
+ "unknown.test", &cert_matched_sni)
+ ->certs[0],
+ kTestCertificate);
+ EXPECT_FALSE(cert_matched_sni);
+ // mail.example.org is explicitly a SubjectAltName in kTestCertificate.
+ EXPECT_EQ(proof_source
+ ->GetCertChain(QuicSocketAddress(), QuicSocketAddress(),
+ "mail.example.org", &cert_matched_sni)
+ ->certs[0],
+ kTestCertificate);
+ EXPECT_TRUE(cert_matched_sni);
+ // www.foo.test is in kWildcardCertificate.
+ EXPECT_EQ(proof_source
+ ->GetCertChain(QuicSocketAddress(), QuicSocketAddress(),
+ "www.foo.test", &cert_matched_sni)
+ ->certs[0],
+ kWildcardCertificate);
+ EXPECT_TRUE(cert_matched_sni);
+ // *.wildcard.test is in kWildcardCertificate.
+ EXPECT_EQ(proof_source
+ ->GetCertChain(QuicSocketAddress(), QuicSocketAddress(),
+ "www.wildcard.test", &cert_matched_sni)
+ ->certs[0],
+ kWildcardCertificate);
+ EXPECT_TRUE(cert_matched_sni);
+ EXPECT_EQ(proof_source
+ ->GetCertChain(QuicSocketAddress(), QuicSocketAddress(),
+ "etc.wildcard.test", &cert_matched_sni)
+ ->certs[0],
+ kWildcardCertificate);
+ EXPECT_TRUE(cert_matched_sni);
+ // wildcard.test itself is not in kWildcardCertificate.
+ EXPECT_EQ(proof_source
+ ->GetCertChain(QuicSocketAddress(), QuicSocketAddress(),
+ "wildcard.test", &cert_matched_sni)
+ ->certs[0],
+ kTestCertificate);
+ EXPECT_FALSE(cert_matched_sni);
+}
+
+TEST_F(ProofSourceX509Test, TlsSignature) {
+ class Callback : public ProofSource::SignatureCallback {
+ public:
+ void Run(bool ok, std::string signature,
+ std::unique_ptr<ProofSource::Details> /*details*/) override {
+ ASSERT_TRUE(ok);
+ std::unique_ptr<CertificateView> view =
+ CertificateView::ParseSingleCertificate(kTestCertificate);
+ EXPECT_TRUE(view->VerifySignature("Test data", signature,
+ SSL_SIGN_RSA_PSS_RSAE_SHA256));
+ }
+ };
+
+ std::unique_ptr<ProofSourceX509> proof_source =
+ ProofSourceX509::Create(test_chain_, std::move(*test_key_));
+ ASSERT_TRUE(proof_source != nullptr);
+
+ proof_source->ComputeTlsSignature(QuicSocketAddress(), QuicSocketAddress(),
+ "example.com", SSL_SIGN_RSA_PSS_RSAE_SHA256,
+ "Test data", std::make_unique<Callback>());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/proof_verifier.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/proof_verifier.h
new file mode 100644
index 00000000000..e9c6f035365
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/proof_verifier.h
@@ -0,0 +1,117 @@
+// Copyright (c) 2013 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_PROOF_VERIFIER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_PROOF_VERIFIER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/quic_types.h"
+#include "quiche/quic/core/quic_versions.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// ProofVerifyDetails is an abstract class that acts as a container for any
+// implementation specific details that a ProofVerifier wishes to return. These
+// details are saved in the CachedState for the origin in question.
+class QUIC_EXPORT_PRIVATE ProofVerifyDetails {
+ public:
+ virtual ~ProofVerifyDetails() {}
+
+ // Returns an new ProofVerifyDetails object with the same contents
+ // as this one.
+ virtual ProofVerifyDetails* Clone() const = 0;
+};
+
+// ProofVerifyContext is an abstract class that acts as a container for any
+// implementation specific context that a ProofVerifier needs.
+class QUIC_EXPORT_PRIVATE ProofVerifyContext {
+ public:
+ virtual ~ProofVerifyContext() {}
+};
+
+// ProofVerifierCallback provides a generic mechanism for a ProofVerifier to
+// call back after an asynchronous verification.
+class QUIC_EXPORT_PRIVATE ProofVerifierCallback {
+ public:
+ virtual ~ProofVerifierCallback() {}
+
+ // Run is called on the original thread to mark the completion of an
+ // asynchonous verification. If |ok| is true then the certificate is valid
+ // and |error_details| is unused. Otherwise, |error_details| contains a
+ // description of the error. |details| contains implementation-specific
+ // details of the verification. |Run| may take ownership of |details| by
+ // calling |release| on it.
+ virtual void Run(bool ok, const std::string& error_details,
+ std::unique_ptr<ProofVerifyDetails>* details) = 0;
+};
+
+// A ProofVerifier checks the signature on a server config, and the certificate
+// chain that backs the public key.
+class QUIC_EXPORT_PRIVATE ProofVerifier {
+ public:
+ virtual ~ProofVerifier() {}
+
+ // VerifyProof checks that |signature| is a valid signature of
+ // |server_config| by the public key in the leaf certificate of |certs|, and
+ // that |certs| is a valid chain for |hostname|. On success, it returns
+ // QUIC_SUCCESS. On failure, it returns QUIC_FAILURE and sets |*error_details|
+ // to a description of the problem. In either case it may set |*details|,
+ // which the caller takes ownership of.
+ //
+ // |context| specifies an implementation specific struct (which may be nullptr
+ // for some implementations) that provides useful information for the
+ // verifier, e.g. logging handles.
+ //
+ // This function may also return QUIC_PENDING, in which case the ProofVerifier
+ // will call back, on the original thread, via |callback| when complete.
+ //
+ // The signature uses SHA-256 as the hash function and PSS padding in the
+ // case of RSA.
+ virtual QuicAsyncStatus VerifyProof(
+ const std::string& hostname, const uint16_t port,
+ const std::string& server_config, QuicTransportVersion transport_version,
+ absl::string_view chlo_hash, const std::vector<std::string>& certs,
+ const std::string& cert_sct, const std::string& signature,
+ const ProofVerifyContext* context, std::string* error_details,
+ std::unique_ptr<ProofVerifyDetails>* details,
+ std::unique_ptr<ProofVerifierCallback> callback) = 0;
+
+ // VerifyCertChain checks that |certs| is a valid chain for |hostname|. On
+ // success, it returns QUIC_SUCCESS. On failure, it returns QUIC_FAILURE and
+ // sets |*error_details| to a description of the problem. In either case it
+ // may set |*details|, which the caller takes ownership of.
+ //
+ // |context| specifies an implementation specific struct (which may be nullptr
+ // for some implementations) that provides useful information for the
+ // verifier, e.g. logging handles.
+ //
+ // If certificate verification fails, a TLS alert will be sent when closing
+ // the connection. This alert defaults to certificate_unknown. By setting
+ // |*out_alert|, a different alert can be sent to provide a more specific
+ // reason why verification failed.
+ //
+ // This function may also return QUIC_PENDING, in which case the ProofVerifier
+ // will call back, on the original thread, via |callback| when complete.
+ // In this case, the ProofVerifier will take ownership of |callback|.
+ virtual QuicAsyncStatus VerifyCertChain(
+ const std::string& hostname, const uint16_t port,
+ const std::vector<std::string>& certs, const std::string& ocsp_response,
+ const std::string& cert_sct, const ProofVerifyContext* context,
+ std::string* error_details, std::unique_ptr<ProofVerifyDetails>* details,
+ uint8_t* out_alert, std::unique_ptr<ProofVerifierCallback> callback) = 0;
+
+ // Returns a ProofVerifyContext instance which can be use for subsequent
+ // verifications. Applications may chose create a different context and
+ // supply it for verifications instead.
+ virtual std::unique_ptr<ProofVerifyContext> CreateDefaultContext() = 0;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_PROOF_VERIFIER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_client_session_cache.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_client_session_cache.cc
new file mode 100644
index 00000000000..32f115dca75
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_client_session_cache.cc
@@ -0,0 +1,173 @@
+// Copyright (c) 2021 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 "quiche/quic/core/crypto/quic_client_session_cache.h"
+
+#include "quiche/quic/core/quic_clock.h"
+
+namespace quic {
+
+namespace {
+
+const size_t kDefaultMaxEntries = 1024;
+// Returns false if the SSL |session| doesn't exist or it is expired at |now|.
+bool IsValid(SSL_SESSION* session, uint64_t now) {
+ if (!session) return false;
+
+ // now_u64 may be slightly behind because of differences in how
+ // time is calculated at this layer versus BoringSSL.
+ // Add a second of wiggle room to account for this.
+ return !(now + 1 < SSL_SESSION_get_time(session) ||
+ now >= SSL_SESSION_get_time(session) +
+ SSL_SESSION_get_timeout(session));
+}
+
+bool DoApplicationStatesMatch(const ApplicationState* state,
+ ApplicationState* other) {
+ if ((state && !other) || (!state && other)) return false;
+ if ((!state && !other) || *state == *other) return true;
+ return false;
+}
+
+} // namespace
+
+QuicClientSessionCache::QuicClientSessionCache()
+ : QuicClientSessionCache(kDefaultMaxEntries) {}
+
+QuicClientSessionCache::QuicClientSessionCache(size_t max_entries)
+ : cache_(max_entries) {}
+
+QuicClientSessionCache::~QuicClientSessionCache() { Clear(); }
+
+void QuicClientSessionCache::Insert(const QuicServerId& server_id,
+ bssl::UniquePtr<SSL_SESSION> session,
+ const TransportParameters& params,
+ const ApplicationState* application_state) {
+ QUICHE_DCHECK(session) << "TLS session is not inserted into client cache.";
+ auto iter = cache_.Lookup(server_id);
+ if (iter == cache_.end()) {
+ CreateAndInsertEntry(server_id, std::move(session), params,
+ application_state);
+ return;
+ }
+
+ QUICHE_DCHECK(iter->second->params);
+ // The states are both the same, so only need to insert sessions.
+ if (params == *iter->second->params &&
+ DoApplicationStatesMatch(application_state,
+ iter->second->application_state.get())) {
+ iter->second->PushSession(std::move(session));
+ return;
+ }
+ // Erase the existing entry because this Insert call must come from a
+ // different QUIC session.
+ cache_.Erase(iter);
+ CreateAndInsertEntry(server_id, std::move(session), params,
+ application_state);
+}
+
+std::unique_ptr<QuicResumptionState> QuicClientSessionCache::Lookup(
+ const QuicServerId& server_id, QuicWallTime now, const SSL_CTX* /*ctx*/) {
+ auto iter = cache_.Lookup(server_id);
+ if (iter == cache_.end()) return nullptr;
+
+ if (!IsValid(iter->second->PeekSession(), now.ToUNIXSeconds())) {
+ QUIC_DLOG(INFO) << "TLS Session expired for host:" << server_id.host();
+ cache_.Erase(iter);
+ return nullptr;
+ }
+ auto state = std::make_unique<QuicResumptionState>();
+ state->tls_session = iter->second->PopSession();
+ if (iter->second->params != nullptr) {
+ state->transport_params =
+ std::make_unique<TransportParameters>(*iter->second->params);
+ }
+ if (iter->second->application_state != nullptr) {
+ state->application_state =
+ std::make_unique<ApplicationState>(*iter->second->application_state);
+ }
+ if (!iter->second->token.empty()) {
+ state->token = iter->second->token;
+ // Clear token after use.
+ iter->second->token.clear();
+ }
+
+ return state;
+}
+
+void QuicClientSessionCache::ClearEarlyData(const QuicServerId& server_id) {
+ auto iter = cache_.Lookup(server_id);
+ if (iter == cache_.end()) return;
+ for (auto& session : iter->second->sessions) {
+ if (session) {
+ QUIC_DLOG(INFO) << "Clear early data for for host: " << server_id.host();
+ session.reset(SSL_SESSION_copy_without_early_data(session.get()));
+ }
+ }
+}
+
+void QuicClientSessionCache::OnNewTokenReceived(const QuicServerId& server_id,
+ absl::string_view token) {
+ if (token.empty()) {
+ return;
+ }
+ auto iter = cache_.Lookup(server_id);
+ if (iter == cache_.end()) {
+ return;
+ }
+ iter->second->token = std::string(token);
+}
+
+void QuicClientSessionCache::RemoveExpiredEntries(QuicWallTime now) {
+ auto iter = cache_.begin();
+ while (iter != cache_.end()) {
+ if (!IsValid(iter->second->PeekSession(), now.ToUNIXSeconds())) {
+ iter = cache_.Erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+}
+
+void QuicClientSessionCache::Clear() { cache_.Clear(); }
+
+void QuicClientSessionCache::CreateAndInsertEntry(
+ const QuicServerId& server_id, bssl::UniquePtr<SSL_SESSION> session,
+ const TransportParameters& params,
+ const ApplicationState* application_state) {
+ auto entry = std::make_unique<Entry>();
+ entry->PushSession(std::move(session));
+ entry->params = std::make_unique<TransportParameters>(params);
+ if (application_state) {
+ entry->application_state =
+ std::make_unique<ApplicationState>(*application_state);
+ }
+ cache_.Insert(server_id, std::move(entry));
+}
+
+QuicClientSessionCache::Entry::Entry() = default;
+QuicClientSessionCache::Entry::Entry(Entry&&) = default;
+QuicClientSessionCache::Entry::~Entry() = default;
+
+void QuicClientSessionCache::Entry::PushSession(
+ bssl::UniquePtr<SSL_SESSION> session) {
+ if (sessions[0] != nullptr) {
+ sessions[1] = std::move(sessions[0]);
+ }
+ sessions[0] = std::move(session);
+}
+
+bssl::UniquePtr<SSL_SESSION> QuicClientSessionCache::Entry::PopSession() {
+ if (sessions[0] == nullptr) return nullptr;
+ bssl::UniquePtr<SSL_SESSION> session = std::move(sessions[0]);
+ sessions[0] = std::move(sessions[1]);
+ sessions[1] = nullptr;
+ return session;
+}
+
+SSL_SESSION* QuicClientSessionCache::Entry::PeekSession() {
+ return sessions[0].get();
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_client_session_cache.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_client_session_cache.h
new file mode 100644
index 00000000000..e568db67bbb
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_client_session_cache.h
@@ -0,0 +1,82 @@
+// Copyright (c) 2021 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_QUIC_CLIENT_SESSION_CACHE_H_
+#define QUICHE_QUIC_CORE_CRYPTO_QUIC_CLIENT_SESSION_CACHE_H_
+
+#include <memory>
+
+#include "quiche/quic/core/crypto/quic_crypto_client_config.h"
+#include "quiche/quic/core/quic_lru_cache.h"
+#include "quiche/quic/core/quic_server_id.h"
+
+namespace quic {
+
+namespace test {
+class QuicClientSessionCachePeer;
+} // namespace test
+
+// QuicClientSessionCache maps from QuicServerId to information used to resume
+// TLS sessions for that server.
+class QUIC_EXPORT_PRIVATE QuicClientSessionCache : public SessionCache {
+ public:
+ QuicClientSessionCache();
+ explicit QuicClientSessionCache(size_t max_entries);
+ ~QuicClientSessionCache() override;
+
+ void Insert(const QuicServerId& server_id,
+ bssl::UniquePtr<SSL_SESSION> session,
+ const TransportParameters& params,
+ const ApplicationState* application_state) override;
+
+ std::unique_ptr<QuicResumptionState> Lookup(const QuicServerId& server_id,
+ QuicWallTime now,
+ const SSL_CTX* ctx) override;
+
+ void ClearEarlyData(const QuicServerId& server_id) override;
+
+ void OnNewTokenReceived(const QuicServerId& server_id,
+ absl::string_view token) override;
+
+ void RemoveExpiredEntries(QuicWallTime now) override;
+
+ void Clear() override;
+
+ size_t size() const { return cache_.Size(); }
+
+ private:
+ friend class test::QuicClientSessionCachePeer;
+
+ struct QUIC_EXPORT_PRIVATE Entry {
+ Entry();
+ Entry(Entry&&);
+ ~Entry();
+
+ // Adds a new |session| onto sessions, dropping the oldest one if two are
+ // already stored.
+ void PushSession(bssl::UniquePtr<SSL_SESSION> session);
+
+ // Retrieves the latest session from the entry, meanwhile removing it.
+ bssl::UniquePtr<SSL_SESSION> PopSession();
+
+ SSL_SESSION* PeekSession();
+
+ bssl::UniquePtr<SSL_SESSION> sessions[2];
+ std::unique_ptr<TransportParameters> params;
+ std::unique_ptr<ApplicationState> application_state;
+ std::string token; // An opaque string received in NEW_TOKEN frame.
+ };
+
+ // Creates a new entry and insert into |cache_|.
+ void CreateAndInsertEntry(const QuicServerId& server_id,
+ bssl::UniquePtr<SSL_SESSION> session,
+ const TransportParameters& params,
+ const ApplicationState* application_state);
+
+ QuicLRUCache<QuicServerId, Entry, QuicServerIdHash> cache_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_CLIENT_SESSION_CACHE_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_client_session_cache_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_client_session_cache_test.cc
new file mode 100644
index 00000000000..880770a7743
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_client_session_cache_test.cc
@@ -0,0 +1,440 @@
+// Copyright (c) 2021 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 "quiche/quic/core/crypto/quic_client_session_cache.h"
+
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/mock_clock.h"
+#include "quiche/common/quiche_text_utils.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+const QuicTime::Delta kTimeout = QuicTime::Delta::FromSeconds(1000);
+const QuicVersionLabel kFakeVersionLabel = 0x01234567;
+const QuicVersionLabel kFakeVersionLabel2 = 0x89ABCDEF;
+const uint64_t kFakeIdleTimeoutMilliseconds = 12012;
+const uint8_t kFakeStatelessResetTokenData[16] = {
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F};
+const uint64_t kFakeMaxPacketSize = 9001;
+const uint64_t kFakeInitialMaxData = 101;
+const bool kFakeDisableMigration = true;
+const auto kCustomParameter1 =
+ static_cast<TransportParameters::TransportParameterId>(0xffcd);
+const char* kCustomParameter1Value = "foo";
+const auto kCustomParameter2 =
+ static_cast<TransportParameters::TransportParameterId>(0xff34);
+const char* kCustomParameter2Value = "bar";
+
+std::vector<uint8_t> CreateFakeStatelessResetToken() {
+ return std::vector<uint8_t>(
+ kFakeStatelessResetTokenData,
+ kFakeStatelessResetTokenData + sizeof(kFakeStatelessResetTokenData));
+}
+
+TransportParameters::LegacyVersionInformation
+CreateFakeLegacyVersionInformation() {
+ TransportParameters::LegacyVersionInformation legacy_version_information;
+ legacy_version_information.version = kFakeVersionLabel;
+ legacy_version_information.supported_versions.push_back(kFakeVersionLabel);
+ legacy_version_information.supported_versions.push_back(kFakeVersionLabel2);
+ return legacy_version_information;
+}
+
+TransportParameters::VersionInformation CreateFakeVersionInformation() {
+ TransportParameters::VersionInformation version_information;
+ version_information.chosen_version = kFakeVersionLabel;
+ version_information.other_versions.push_back(kFakeVersionLabel);
+ return version_information;
+}
+
+// Make a TransportParameters that has a few fields set to help test comparison.
+std::unique_ptr<TransportParameters> MakeFakeTransportParams() {
+ auto params = std::make_unique<TransportParameters>();
+ params->perspective = Perspective::IS_CLIENT;
+ params->legacy_version_information = CreateFakeLegacyVersionInformation();
+ params->version_information = CreateFakeVersionInformation();
+ params->max_idle_timeout_ms.set_value(kFakeIdleTimeoutMilliseconds);
+ params->stateless_reset_token = CreateFakeStatelessResetToken();
+ params->max_udp_payload_size.set_value(kFakeMaxPacketSize);
+ params->initial_max_data.set_value(kFakeInitialMaxData);
+ params->disable_active_migration = kFakeDisableMigration;
+ params->custom_parameters[kCustomParameter1] = kCustomParameter1Value;
+ params->custom_parameters[kCustomParameter2] = kCustomParameter2Value;
+ return params;
+}
+
+// Generated by running TlsClientHandshakerTest.ZeroRttResumption and in
+// TlsClientHandshaker::InsertSession calling SSL_SESSION_to_bytes to serialize
+// the received 0-RTT capable ticket.
+static const char kCachedSession[] =
+ "30820ad7020101020203040402130104206594ce84e61a866b56163c4ba09079aebf1d4f"
+ "6cbcbd38dc9d7066a38a76c9cf0420ec9062063582a4cc0a44f9ff93256a195153ba6032"
+ "0cf3c9189990932d838adaa10602046196f7b9a205020302a300a382039f3082039b3082"
+ "0183a00302010202021001300d06092a864886f70d010105050030623111300f06035504"
+ "030c08426f677573204941310b300906035504080c024d41310b30090603550406130255"
+ "533121301f06092a864886f70d0109011612626f67757340626f6775732d69612e636f6d"
+ "3110300e060355040a0c07426f6775734941301e170d3231303132383136323030315a17"
+ "0d3331303132363136323030315a3069311d301b06035504030c14746573745f6563632e"
+ "6578616d706c652e636f6d310b300906035504080c024d41310b30090603550406130255"
+ "53311e301c06092a864886f70d010901160f626f67757340626f6775732e636f6d310e30"
+ "0c060355040a0c05426f6775733059301306072a8648ce3d020106082a8648ce3d030107"
+ "034200041ba5e2b6f24e64990b9f24ae6d23473d8c77fbcfb7f554f36559529a69a57170"
+ "a10a81b7fe4a36ebf37b0a8c5e467a8443d8b8c002892aa5c1194bd843f42c9aa31f301d"
+ "301b0603551d11041430128210746573742e6578616d706c652e636f6d300d06092a8648"
+ "86f70d0101050500038202010019921d54ac06948763d609215f64f5d6540e3da886c6c9"
+ "61bc737a437719b4621416ef1229f39282d7d3234e1a5d57535473066233bd246eec8e96"
+ "1e0633cf4fe014c800e62599981820ec33d92e74ded0fa2953db1d81e19cb6890b6305b6"
+ "3ede8d3e9fcf3c09f3f57283acf08aa57be4ee9a68d00bb3e2ded5920c619b5d83e5194a"
+ "adb77ae5d61ed3e0a5670f0ae61cc3197329f0e71e3364dcab0405e9e4a6646adef8f022"
+ "6415ec16c8046307b1769029fe780bd576114dde2fa9b4a32aa70bc436549a24ee4907a9"
+ "045f6457ce8dfd8d62cc65315afe798ae1a948eefd70b035d415e73569c48fb20085de1a"
+ "87de039e6b0b9a5fcb4069df27f3a7a1409e72d1ac739c72f29ef786134207e61c79855f"
+ "c22e3ee5f6ad59a7b1ff0f18d79776f1c95efaebbebe381664132a58a1e7ff689945b7e0"
+ "88634b0872feeefbf6be020884b994c6a7ff435f2b3f609077ff97cb509cfa17ff479b34"
+ "e633e4b5bc46b20c5f27c80a2e2943f795a928acd5a3fc43c3af8425ad600c048b41d87e"
+ "6361bc72fc4e5e44680a3d325674ba6ffa760d2fc7d9e4847a8e0dd9d35a543324e18b94"
+ "2d42af6391ed1dd54a39e3f4a4c6b32486eb4ba72815dbd89c56fc053743a0b0483ce676"
+ "15defce6800c629b99d0cbc56da162487f475b7c246099eaf1e6d10a022b2f49c6af1da3"
+ "e8ed66096f267c4a76976b9572db7456ef90278330a4020400aa81b60481b3494e534543"
+ "55524500f3439e548c21d2ad6e5634cc1cc0045730819702010102020304040213010400"
+ "0420ec9062063582a4cc0a44f9ff93256a195153ba60320cf3c9189990932d838adaa106"
+ "02046196f7b9a205020302a300a4020400b20302011db5060404130800cdb807020500ff"
+ "ffffffb9050203093a80ba0404026833bb030101ffbc23042100d27d985bfce04833f02d"
+ "38366b219f4def42bc4ba1b01844d1778db11731487dbd020400be020400b20302011db3"
+ "8205da308205d6308203bea00302010202021000300d06092a864886f70d010105050030"
+ "62310b3009060355040613025553310b300906035504080c024d413110300e060355040a"
+ "0c07426f67757343413111300f06035504030c08426f6775732043413121301f06092a86"
+ "4886f70d0109011612626f67757340626f6775732d63612e636f6d3020170d3231303132"
+ "383136313935385a180f32303730303531313136313935385a30623111300f0603550403"
+ "0c08426f677573204941310b300906035504080c024d41310b3009060355040613025553"
+ "3121301f06092a864886f70d0109011612626f67757340626f6775732d69612e636f6d31"
+ "10300e060355040a0c07426f677573494130820222300d06092a864886f70d0101010500"
+ "0382020f003082020a028202010096c03a0ffc61bcedcd5ec9bf6f848b8a066b43f08377"
+ "3af518a6a0044f22e666e24d2ae741954e344302c4be04612185bd53bcd848eb322bf900"
+ "724eb0848047d647033ffbddb00f01d1de7c1cdb684f83c9bf5fd18ff60afad5a53b0d7d"
+ "2c2a50abc38df019cd7f50194d05bc4597a1ef8570ea04069a2c36d74496af126573ca18"
+ "8e470009b56250fadf2a04e837ee3837b36b1f08b7a0cfe2533d05f26484ce4e30203d01"
+ "517fffd3da63d0341079ddce16e9ab4dbf9d4049e5cc52326031e645dd682fe6220d9e0e"
+ "95451f5a82f3e1720dc13e8499466426a0bdbea9f6a76b3c9228dd3c79ab4dcc4c145ef0"
+ "e78d1ee8bfd4650692d7e28a54bed809d8f7b37fe24c586be59cc46638531cb291c8c156"
+ "8f08d67e768e51563e95a639c1f138b275ffad6a6a2a042ba9e26ad63c2ce63b600013f0"
+ "a6f0703ee51c4f457f7bab0391c2fc4c5bb3213742c9cf9941bff68cc2e1cc96139d35ed"
+ "1885244ddde0bf658416c486701841b81f7b17503d08c59a4db08a2a80755e007aa3b6c7"
+ "eadcaa9e07c8325f3689f100de23970b12c9d9f6d0a8fb35ba0fd75c64410318db4a13ac"
+ "3972ad16cdf6408af37013c7bcd7c42f20d6d04c3e39436c7531e8dafa219dd04b784ef0"
+ "3c70ee5a4782b33cafa925aa3deca62a14aed704f179b932efabc2b0c5c15a8a99bfc9e6"
+ "189dce7da50ea303594b6af9c933dd54b6e9d17c472d0203010001a38193308190300f06"
+ "03551d130101ff040530030101ff301d0603551d0e041604141a98e80029a80992b7e5e0"
+ "068ab9b3486cd839d6301f0603551d23041830168014780beeefe2fa419c48a438bdb30b"
+ "e37ef0b7a94e300b0603551d0f0404030202a430130603551d25040c300a06082b060105"
+ "05070301301b0603551d11041430128207426f67757343418207426f6775734941300d06"
+ "092a864886f70d010105050003820201009e822ed8064b1aabaddf1340010ea147f68c06"
+ "5a5a599ea305349f1b0e545a00817d6e55c7bf85560fab429ca72186c4d520b52f5cc121"
+ "abd068b06f3111494431d2522efa54642f907059e7db80b73bb5ecf621377195b8700bba"
+ "df798cece8c67a9571548d0e6592e81ae5d934877cb170aef18d3b97f635600fe0890d98"
+ "f88b33fe3d1fd34c1c915beae4e5c0b133f476c40b21d220f16ce9cdd9e8f97a36a31723"
+ "68875f052c9271648d9cb54687c6fdc3ea96f2908003bc5e5e79de00a21da7b8429f8b08"
+ "af4c4d34641e386d72eabf5f01f106363f2ffd18969bf0bb9a4d17627c6427ff772c4308"
+ "83c276feef5fc6dba9582c22fdbe9df7e8dfca375695f028ed588df54f3c86462dbf4c07"
+ "91d80ca738988a1419c86bb4dd8d738b746921f01f39422e5ffd488b6f00195b996e6392"
+ "3a820a32cd78b5989f339c0fcf4f269103964a30a16347d0ffdc8df1f3653ddc1515fa09"
+ "22c7aef1af1fbcb23e93ae7622ab1ee11fcfa98319bad4c37c091cad46bd0337b3cc78b5"
+ "5b9f1ea7994acc1f89c49a0b4cb540d2137e266fd43e56a9b5b778217b6f77df530e1eaf"
+ "b3417262b5ddb86d3c6c5ac51e3f326c650dcc2434473973b7182c66220d1f3871bde7ee"
+ "47d3f359d3d4c5bdd61baa684c03db4c75f9d6690c9e6e3abe6eaf5fa2c33c4daf26b373"
+ "d85a1e8a7d671ac4a0a97b14e36e81280de4593bbb12da7695b5060404130800cdb60301"
+ "0100b70402020403b807020500ffffffffb9050203093a80ba0404026833bb030101ffbd"
+ "020400be020400";
+
+class QuicClientSessionCacheTest : public QuicTest {
+ public:
+ QuicClientSessionCacheTest() : ssl_ctx_(SSL_CTX_new(TLS_method())) {
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ }
+
+ protected:
+ bssl::UniquePtr<SSL_SESSION> NewSSLSession() {
+ std::string cached_session =
+ absl::HexStringToBytes(absl::string_view(kCachedSession));
+ SSL_SESSION* session = SSL_SESSION_from_bytes(
+ reinterpret_cast<const uint8_t*>(cached_session.data()),
+ cached_session.size(), ssl_ctx_.get());
+ QUICHE_DCHECK(session);
+ return bssl::UniquePtr<SSL_SESSION>(session);
+ }
+
+ bssl::UniquePtr<SSL_SESSION> MakeTestSession(
+ QuicTime::Delta timeout = kTimeout) {
+ bssl::UniquePtr<SSL_SESSION> session = NewSSLSession();
+ SSL_SESSION_set_time(session.get(), clock_.WallNow().ToUNIXSeconds());
+ SSL_SESSION_set_timeout(session.get(), timeout.ToSeconds());
+ return session;
+ }
+
+ bssl::UniquePtr<SSL_CTX> ssl_ctx_;
+ MockClock clock_;
+};
+
+// Tests that simple insertion and lookup work correctly.
+TEST_F(QuicClientSessionCacheTest, SingleSession) {
+ QuicClientSessionCache cache;
+
+ auto params = MakeFakeTransportParams();
+ auto session = MakeTestSession();
+ QuicServerId id1("a.com", 443);
+
+ auto params2 = MakeFakeTransportParams();
+ auto session2 = MakeTestSession();
+ SSL_SESSION* unowned2 = session2.get();
+ QuicServerId id2("b.com", 443);
+
+ EXPECT_EQ(nullptr, cache.Lookup(id1, clock_.WallNow(), ssl_ctx_.get()));
+ EXPECT_EQ(nullptr, cache.Lookup(id2, clock_.WallNow(), ssl_ctx_.get()));
+ EXPECT_EQ(0u, cache.size());
+
+ cache.Insert(id1, std::move(session), *params, nullptr);
+ EXPECT_EQ(1u, cache.size());
+ EXPECT_EQ(
+ *params,
+ *(cache.Lookup(id1, clock_.WallNow(), ssl_ctx_.get())->transport_params));
+ EXPECT_EQ(nullptr, cache.Lookup(id2, clock_.WallNow(), ssl_ctx_.get()));
+ // No session is available for id1, even though the entry exists.
+ EXPECT_EQ(1u, cache.size());
+ EXPECT_EQ(nullptr, cache.Lookup(id1, clock_.WallNow(), ssl_ctx_.get()));
+ // Lookup() will trigger a deletion of invalid entry.
+ EXPECT_EQ(0u, cache.size());
+
+ auto session3 = MakeTestSession();
+ SSL_SESSION* unowned3 = session3.get();
+ QuicServerId id3("c.com", 443);
+ cache.Insert(id3, std::move(session3), *params, nullptr);
+ cache.Insert(id2, std::move(session2), *params2, nullptr);
+ EXPECT_EQ(2u, cache.size());
+ EXPECT_EQ(
+ unowned2,
+ cache.Lookup(id2, clock_.WallNow(), ssl_ctx_.get())->tls_session.get());
+ EXPECT_EQ(
+ unowned3,
+ cache.Lookup(id3, clock_.WallNow(), ssl_ctx_.get())->tls_session.get());
+
+ // Verify that the cache is cleared after Lookups.
+ EXPECT_EQ(nullptr, cache.Lookup(id1, clock_.WallNow(), ssl_ctx_.get()));
+ EXPECT_EQ(nullptr, cache.Lookup(id2, clock_.WallNow(), ssl_ctx_.get()));
+ EXPECT_EQ(nullptr, cache.Lookup(id3, clock_.WallNow(), ssl_ctx_.get()));
+ EXPECT_EQ(0u, cache.size());
+}
+
+TEST_F(QuicClientSessionCacheTest, MultipleSessions) {
+ QuicClientSessionCache cache;
+
+ auto params = MakeFakeTransportParams();
+ auto session = MakeTestSession();
+ QuicServerId id1("a.com", 443);
+ auto session2 = MakeTestSession();
+ SSL_SESSION* unowned2 = session2.get();
+ auto session3 = MakeTestSession();
+ SSL_SESSION* unowned3 = session3.get();
+
+ cache.Insert(id1, std::move(session), *params, nullptr);
+ cache.Insert(id1, std::move(session2), *params, nullptr);
+ cache.Insert(id1, std::move(session3), *params, nullptr);
+ // The latest session is popped first.
+ EXPECT_EQ(
+ unowned3,
+ cache.Lookup(id1, clock_.WallNow(), ssl_ctx_.get())->tls_session.get());
+ EXPECT_EQ(
+ unowned2,
+ cache.Lookup(id1, clock_.WallNow(), ssl_ctx_.get())->tls_session.get());
+ // Only two sessions are cached.
+ EXPECT_EQ(nullptr, cache.Lookup(id1, clock_.WallNow(), ssl_ctx_.get()));
+}
+
+// Test that when a different TransportParameter is inserted for
+// the same server id, the existing entry is removed.
+TEST_F(QuicClientSessionCacheTest, DifferentTransportParams) {
+ QuicClientSessionCache cache;
+
+ auto params = MakeFakeTransportParams();
+ auto session = MakeTestSession();
+ QuicServerId id1("a.com", 443);
+ auto session2 = MakeTestSession();
+ auto session3 = MakeTestSession();
+ SSL_SESSION* unowned3 = session3.get();
+
+ cache.Insert(id1, std::move(session), *params, nullptr);
+ cache.Insert(id1, std::move(session2), *params, nullptr);
+ // tweak the transport parameters a little bit.
+ params->perspective = Perspective::IS_SERVER;
+ cache.Insert(id1, std::move(session3), *params, nullptr);
+ auto resumption_state = cache.Lookup(id1, clock_.WallNow(), ssl_ctx_.get());
+ EXPECT_EQ(unowned3, resumption_state->tls_session.get());
+ EXPECT_EQ(*params.get(), *resumption_state->transport_params);
+ EXPECT_EQ(nullptr, cache.Lookup(id1, clock_.WallNow(), ssl_ctx_.get()));
+}
+
+TEST_F(QuicClientSessionCacheTest, DifferentApplicationState) {
+ QuicClientSessionCache cache;
+
+ auto params = MakeFakeTransportParams();
+ auto session = MakeTestSession();
+ QuicServerId id1("a.com", 443);
+ auto session2 = MakeTestSession();
+ auto session3 = MakeTestSession();
+ SSL_SESSION* unowned3 = session3.get();
+ ApplicationState state;
+ state.push_back('a');
+
+ cache.Insert(id1, std::move(session), *params, &state);
+ cache.Insert(id1, std::move(session2), *params, &state);
+ cache.Insert(id1, std::move(session3), *params, nullptr);
+ auto resumption_state = cache.Lookup(id1, clock_.WallNow(), ssl_ctx_.get());
+ EXPECT_EQ(unowned3, resumption_state->tls_session.get());
+ EXPECT_EQ(nullptr, resumption_state->application_state);
+ EXPECT_EQ(nullptr, cache.Lookup(id1, clock_.WallNow(), ssl_ctx_.get()));
+}
+
+TEST_F(QuicClientSessionCacheTest, BothStatesDifferent) {
+ QuicClientSessionCache cache;
+
+ auto params = MakeFakeTransportParams();
+ auto session = MakeTestSession();
+ QuicServerId id1("a.com", 443);
+ auto session2 = MakeTestSession();
+ auto session3 = MakeTestSession();
+ SSL_SESSION* unowned3 = session3.get();
+ ApplicationState state;
+ state.push_back('a');
+
+ cache.Insert(id1, std::move(session), *params, &state);
+ cache.Insert(id1, std::move(session2), *params, &state);
+ params->perspective = Perspective::IS_SERVER;
+ cache.Insert(id1, std::move(session3), *params, nullptr);
+ auto resumption_state = cache.Lookup(id1, clock_.WallNow(), ssl_ctx_.get());
+ EXPECT_EQ(unowned3, resumption_state->tls_session.get());
+ EXPECT_EQ(*params.get(), *resumption_state->transport_params);
+ EXPECT_EQ(nullptr, resumption_state->application_state);
+ EXPECT_EQ(nullptr, cache.Lookup(id1, clock_.WallNow(), ssl_ctx_.get()));
+}
+
+// When the size limit is exceeded, the oldest entry should be erased.
+TEST_F(QuicClientSessionCacheTest, SizeLimit) {
+ QuicClientSessionCache cache(2);
+
+ auto params = MakeFakeTransportParams();
+ auto session = MakeTestSession();
+ QuicServerId id1("a.com", 443);
+
+ auto session2 = MakeTestSession();
+ SSL_SESSION* unowned2 = session2.get();
+ QuicServerId id2("b.com", 443);
+
+ auto session3 = MakeTestSession();
+ SSL_SESSION* unowned3 = session3.get();
+ QuicServerId id3("c.com", 443);
+
+ cache.Insert(id1, std::move(session), *params, nullptr);
+ cache.Insert(id2, std::move(session2), *params, nullptr);
+ cache.Insert(id3, std::move(session3), *params, nullptr);
+
+ EXPECT_EQ(2u, cache.size());
+ EXPECT_EQ(
+ unowned2,
+ cache.Lookup(id2, clock_.WallNow(), ssl_ctx_.get())->tls_session.get());
+ EXPECT_EQ(
+ unowned3,
+ cache.Lookup(id3, clock_.WallNow(), ssl_ctx_.get())->tls_session.get());
+ EXPECT_EQ(nullptr, cache.Lookup(id1, clock_.WallNow(), ssl_ctx_.get()));
+}
+
+TEST_F(QuicClientSessionCacheTest, ClearEarlyData) {
+ QuicClientSessionCache cache;
+ SSL_CTX_set_early_data_enabled(ssl_ctx_.get(), 1);
+ auto params = MakeFakeTransportParams();
+ auto session = MakeTestSession();
+ QuicServerId id1("a.com", 443);
+ auto session2 = MakeTestSession();
+
+ EXPECT_TRUE(SSL_SESSION_early_data_capable(session.get()));
+ EXPECT_TRUE(SSL_SESSION_early_data_capable(session2.get()));
+
+ cache.Insert(id1, std::move(session), *params, nullptr);
+ cache.Insert(id1, std::move(session2), *params, nullptr);
+
+ cache.ClearEarlyData(id1);
+
+ auto resumption_state = cache.Lookup(id1, clock_.WallNow(), ssl_ctx_.get());
+ EXPECT_FALSE(
+ SSL_SESSION_early_data_capable(resumption_state->tls_session.get()));
+ resumption_state = cache.Lookup(id1, clock_.WallNow(), ssl_ctx_.get());
+ EXPECT_FALSE(
+ SSL_SESSION_early_data_capable(resumption_state->tls_session.get()));
+ EXPECT_EQ(nullptr, cache.Lookup(id1, clock_.WallNow(), ssl_ctx_.get()));
+}
+
+// Expired session isn't considered valid and nullptr will be returned upon
+// Lookup.
+TEST_F(QuicClientSessionCacheTest, Expiration) {
+ QuicClientSessionCache cache;
+
+ auto params = MakeFakeTransportParams();
+ auto session = MakeTestSession();
+ QuicServerId id1("a.com", 443);
+
+ auto session2 = MakeTestSession(3 * kTimeout);
+ SSL_SESSION* unowned2 = session2.get();
+ QuicServerId id2("b.com", 443);
+
+ cache.Insert(id1, std::move(session), *params, nullptr);
+ cache.Insert(id2, std::move(session2), *params, nullptr);
+
+ EXPECT_EQ(2u, cache.size());
+ // Expire the session.
+ clock_.AdvanceTime(kTimeout * 2);
+ // The entry has not been removed yet.
+ EXPECT_EQ(2u, cache.size());
+
+ EXPECT_EQ(nullptr, cache.Lookup(id1, clock_.WallNow(), ssl_ctx_.get()));
+ EXPECT_EQ(1u, cache.size());
+ EXPECT_EQ(
+ unowned2,
+ cache.Lookup(id2, clock_.WallNow(), ssl_ctx_.get())->tls_session.get());
+ EXPECT_EQ(1u, cache.size());
+}
+
+TEST_F(QuicClientSessionCacheTest, RemoveExpiredEntriesAndClear) {
+ QuicClientSessionCache cache;
+
+ auto params = MakeFakeTransportParams();
+ auto session = MakeTestSession();
+ quic::QuicServerId id1("a.com", 443);
+
+ auto session2 = MakeTestSession(3 * kTimeout);
+ quic::QuicServerId id2("b.com", 443);
+
+ cache.Insert(id1, std::move(session), *params, nullptr);
+ cache.Insert(id2, std::move(session2), *params, nullptr);
+
+ EXPECT_EQ(2u, cache.size());
+ // Expire the session.
+ clock_.AdvanceTime(kTimeout * 2);
+ // The entry has not been removed yet.
+ EXPECT_EQ(2u, cache.size());
+
+ // Flush expired sessions.
+ cache.RemoveExpiredEntries(clock_.WallNow());
+
+ // session is expired and should be flushed.
+ EXPECT_EQ(nullptr, cache.Lookup(id1, clock_.WallNow(), ssl_ctx_.get()));
+ EXPECT_EQ(1u, cache.size());
+
+ cache.Clear();
+ EXPECT_EQ(0u, cache.size());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_compressed_certs_cache.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_compressed_certs_cache.cc
new file mode 100644
index 00000000000..dabbf2402a2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_compressed_certs_cache.cc
@@ -0,0 +1,114 @@
+// Copyright 2016 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 "quiche/quic/core/crypto/quic_compressed_certs_cache.h"
+
+#include <string>
+
+namespace quic {
+
+namespace {
+
+// Inline helper function for extending a 64-bit |seed| in-place with a 64-bit
+// |value|. Based on Boost's hash_combine function.
+inline void hash_combine(uint64_t* seed, const uint64_t& val) {
+ (*seed) ^= val + 0x9e3779b9 + ((*seed) << 6) + ((*seed) >> 2);
+}
+
+} // namespace
+
+const size_t QuicCompressedCertsCache::kQuicCompressedCertsCacheSize = 225;
+
+QuicCompressedCertsCache::UncompressedCerts::UncompressedCerts()
+ : chain(nullptr), client_cached_cert_hashes(nullptr) {}
+
+QuicCompressedCertsCache::UncompressedCerts::UncompressedCerts(
+ const quiche::QuicheReferenceCountedPointer<ProofSource::Chain>& chain,
+ const std::string* client_cached_cert_hashes)
+ : chain(chain), client_cached_cert_hashes(client_cached_cert_hashes) {}
+
+QuicCompressedCertsCache::UncompressedCerts::~UncompressedCerts() {}
+
+QuicCompressedCertsCache::CachedCerts::CachedCerts() {}
+
+QuicCompressedCertsCache::CachedCerts::CachedCerts(
+ const UncompressedCerts& uncompressed_certs,
+ const std::string& compressed_cert)
+ : chain_(uncompressed_certs.chain),
+ client_cached_cert_hashes_(*uncompressed_certs.client_cached_cert_hashes),
+ compressed_cert_(compressed_cert) {}
+
+QuicCompressedCertsCache::CachedCerts::CachedCerts(const CachedCerts& other) =
+ default;
+
+QuicCompressedCertsCache::CachedCerts::~CachedCerts() {}
+
+bool QuicCompressedCertsCache::CachedCerts::MatchesUncompressedCerts(
+ const UncompressedCerts& uncompressed_certs) const {
+ return (client_cached_cert_hashes_ ==
+ *uncompressed_certs.client_cached_cert_hashes &&
+ chain_ == uncompressed_certs.chain);
+}
+
+const std::string* QuicCompressedCertsCache::CachedCerts::compressed_cert()
+ const {
+ return &compressed_cert_;
+}
+
+QuicCompressedCertsCache::QuicCompressedCertsCache(int64_t max_num_certs)
+ : certs_cache_(max_num_certs) {}
+
+QuicCompressedCertsCache::~QuicCompressedCertsCache() {
+ // Underlying cache must be cleared before destruction.
+ certs_cache_.Clear();
+}
+
+const std::string* QuicCompressedCertsCache::GetCompressedCert(
+ const quiche::QuicheReferenceCountedPointer<ProofSource::Chain>& chain,
+ const std::string& client_cached_cert_hashes) {
+ UncompressedCerts uncompressed_certs(chain, &client_cached_cert_hashes);
+
+ uint64_t key = ComputeUncompressedCertsHash(uncompressed_certs);
+
+ CachedCerts* cached_value = nullptr;
+ auto iter = certs_cache_.Lookup(key);
+ if (iter != certs_cache_.end()) {
+ cached_value = iter->second.get();
+ }
+ if (cached_value != nullptr &&
+ cached_value->MatchesUncompressedCerts(uncompressed_certs)) {
+ return cached_value->compressed_cert();
+ }
+ return nullptr;
+}
+
+void QuicCompressedCertsCache::Insert(
+ const quiche::QuicheReferenceCountedPointer<ProofSource::Chain>& chain,
+ const std::string& client_cached_cert_hashes,
+ const std::string& compressed_cert) {
+ UncompressedCerts uncompressed_certs(chain, &client_cached_cert_hashes);
+
+ uint64_t key = ComputeUncompressedCertsHash(uncompressed_certs);
+
+ // Insert one unit to the cache.
+ std::unique_ptr<CachedCerts> cached_certs(
+ new CachedCerts(uncompressed_certs, compressed_cert));
+ certs_cache_.Insert(key, std::move(cached_certs));
+}
+
+size_t QuicCompressedCertsCache::MaxSize() { return certs_cache_.MaxSize(); }
+
+size_t QuicCompressedCertsCache::Size() { return certs_cache_.Size(); }
+
+uint64_t QuicCompressedCertsCache::ComputeUncompressedCertsHash(
+ const UncompressedCerts& uncompressed_certs) {
+ uint64_t hash =
+ std::hash<std::string>()(*uncompressed_certs.client_cached_cert_hashes);
+
+ hash_combine(&hash,
+ reinterpret_cast<uint64_t>(uncompressed_certs.chain.get()));
+ return hash;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_compressed_certs_cache.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_compressed_certs_cache.h
new file mode 100644
index 00000000000..918981e717d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_compressed_certs_cache.h
@@ -0,0 +1,103 @@
+// Copyright 2016 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_QUIC_COMPRESSED_CERTS_CACHE_H_
+#define QUICHE_QUIC_CORE_CRYPTO_QUIC_COMPRESSED_CERTS_CACHE_H_
+
+#include <string>
+#include <vector>
+
+#include "quiche/quic/core/crypto/proof_source.h"
+#include "quiche/quic/core/quic_lru_cache.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// QuicCompressedCertsCache is a cache to track most recently compressed certs.
+class QUIC_EXPORT_PRIVATE QuicCompressedCertsCache {
+ public:
+ explicit QuicCompressedCertsCache(int64_t max_num_certs);
+ ~QuicCompressedCertsCache();
+
+ // Returns the pointer to the cached compressed cert if
+ // |chain, client_cached_cert_hashes| hits cache.
+ // Otherwise, return nullptr.
+ // Returned pointer might become invalid on the next call to Insert().
+ const std::string* GetCompressedCert(
+ const quiche::QuicheReferenceCountedPointer<ProofSource::Chain>& chain,
+ const std::string& client_cached_cert_hashes);
+
+ // Inserts the specified
+ // |chain, client_cached_cert_hashes, compressed_cert| tuple to the cache.
+ // If the insertion causes the cache to become overfull, entries will
+ // be deleted in an LRU order to make room.
+ void Insert(
+ const quiche::QuicheReferenceCountedPointer<ProofSource::Chain>& chain,
+ const std::string& client_cached_cert_hashes,
+ const std::string& compressed_cert);
+
+ // Returns max number of cache entries the cache can carry.
+ size_t MaxSize();
+
+ // Returns current number of cache entries in the cache.
+ size_t Size();
+
+ // Default size of the QuicCompressedCertsCache per server side investigation.
+ static const size_t kQuicCompressedCertsCacheSize;
+
+ private:
+ // A wrapper of the tuple:
+ // |chain, client_cached_cert_hashes|
+ // to identify uncompressed representation of certs.
+ struct QUIC_EXPORT_PRIVATE UncompressedCerts {
+ UncompressedCerts();
+ UncompressedCerts(
+ const quiche::QuicheReferenceCountedPointer<ProofSource::Chain>& chain,
+ const std::string* client_cached_cert_hashes);
+ ~UncompressedCerts();
+
+ const quiche::QuicheReferenceCountedPointer<ProofSource::Chain> chain;
+ const std::string* client_cached_cert_hashes;
+ };
+
+ // Certs stored by QuicCompressedCertsCache where uncompressed certs data is
+ // used to identify the uncompressed representation of certs and
+ // |compressed_cert| is the cached compressed representation.
+ class QUIC_EXPORT_PRIVATE CachedCerts {
+ public:
+ CachedCerts();
+ CachedCerts(const UncompressedCerts& uncompressed_certs,
+ const std::string& compressed_cert);
+ CachedCerts(const CachedCerts& other);
+ ~CachedCerts();
+
+ // Returns true if the |uncompressed_certs| matches uncompressed
+ // representation of this cert.
+ bool MatchesUncompressedCerts(
+ const UncompressedCerts& uncompressed_certs) const;
+
+ const std::string* compressed_cert() const;
+
+ private:
+ // Uncompressed certs data.
+ quiche::QuicheReferenceCountedPointer<ProofSource::Chain> chain_;
+ const std::string client_cached_cert_hashes_;
+
+ // Cached compressed representation derived from uncompressed certs.
+ const std::string compressed_cert_;
+ };
+
+ // Computes a uint64_t hash for |uncompressed_certs|.
+ uint64_t ComputeUncompressedCertsHash(
+ const UncompressedCerts& uncompressed_certs);
+
+ // Key is a unit64_t hash for UncompressedCerts. Stored associated value is
+ // CachedCerts which has both original uncompressed certs data and the
+ // compressed representation of the certs.
+ QuicLRUCache<uint64_t, CachedCerts> certs_cache_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_COMPRESSED_CERTS_CACHE_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_compressed_certs_cache_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_compressed_certs_cache_test.cc
new file mode 100644
index 00000000000..b98f9f2cbb8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_compressed_certs_cache_test.cc
@@ -0,0 +1,91 @@
+// Copyright 2016 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 "quiche/quic/core/crypto/quic_compressed_certs_cache.h"
+
+#include <string>
+
+#include "absl/strings/str_cat.h"
+#include "quiche/quic/core/crypto/cert_compressor.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/crypto_test_utils.h"
+
+namespace quic {
+
+namespace test {
+
+namespace {
+
+class QuicCompressedCertsCacheTest : public QuicTest {
+ public:
+ QuicCompressedCertsCacheTest()
+ : certs_cache_(QuicCompressedCertsCache::kQuicCompressedCertsCacheSize) {}
+
+ protected:
+ QuicCompressedCertsCache certs_cache_;
+};
+
+TEST_F(QuicCompressedCertsCacheTest, CacheHit) {
+ std::vector<std::string> certs = {"leaf cert", "intermediate cert",
+ "root cert"};
+ quiche::QuicheReferenceCountedPointer<ProofSource::Chain> chain(
+ new ProofSource::Chain(certs));
+ std::string cached_certs = "cached certs";
+ std::string compressed = "compressed cert";
+
+ certs_cache_.Insert(chain, cached_certs, compressed);
+
+ const std::string* cached_value =
+ certs_cache_.GetCompressedCert(chain, cached_certs);
+ ASSERT_NE(nullptr, cached_value);
+ EXPECT_EQ(*cached_value, compressed);
+}
+
+TEST_F(QuicCompressedCertsCacheTest, CacheMiss) {
+ std::vector<std::string> certs = {"leaf cert", "intermediate cert",
+ "root cert"};
+ quiche::QuicheReferenceCountedPointer<ProofSource::Chain> chain(
+ new ProofSource::Chain(certs));
+
+ std::string cached_certs = "cached certs";
+ std::string compressed = "compressed cert";
+
+ certs_cache_.Insert(chain, cached_certs, compressed);
+
+ EXPECT_EQ(nullptr,
+ certs_cache_.GetCompressedCert(chain, "mismatched cached certs"));
+
+ // A different chain though with equivalent certs should get a cache miss.
+ quiche::QuicheReferenceCountedPointer<ProofSource::Chain> chain2(
+ new ProofSource::Chain(certs));
+ EXPECT_EQ(nullptr, certs_cache_.GetCompressedCert(chain2, cached_certs));
+}
+
+TEST_F(QuicCompressedCertsCacheTest, CacheMissDueToEviction) {
+ // Test cache returns a miss when a queried uncompressed certs was cached but
+ // then evicted.
+ std::vector<std::string> certs = {"leaf cert", "intermediate cert",
+ "root cert"};
+ quiche::QuicheReferenceCountedPointer<ProofSource::Chain> chain(
+ new ProofSource::Chain(certs));
+
+ std::string cached_certs = "cached certs";
+ std::string compressed = "compressed cert";
+ certs_cache_.Insert(chain, cached_certs, compressed);
+
+ // Insert another kQuicCompressedCertsCacheSize certs to evict the first
+ // cached cert.
+ for (unsigned int i = 0;
+ i < QuicCompressedCertsCache::kQuicCompressedCertsCacheSize; i++) {
+ EXPECT_EQ(certs_cache_.Size(), i + 1);
+ certs_cache_.Insert(chain, absl::StrCat(i), absl::StrCat(i));
+ }
+ EXPECT_EQ(certs_cache_.MaxSize(), certs_cache_.Size());
+
+ EXPECT_EQ(nullptr, certs_cache_.GetCompressedCert(chain, cached_certs));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypter.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypter.cc
new file mode 100644
index 00000000000..96502360409
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypter.cc
@@ -0,0 +1,19 @@
+// Copyright (c) 2019 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 "quiche/quic/core/crypto/quic_crypter.h"
+
+#include "absl/strings/string_view.h"
+
+namespace quic {
+
+bool QuicCrypter::SetNoncePrefixOrIV(const ParsedQuicVersion& version,
+ absl::string_view nonce_prefix_or_iv) {
+ if (version.UsesInitialObfuscators()) {
+ return SetIV(nonce_prefix_or_iv);
+ }
+ return SetNoncePrefix(nonce_prefix_or_iv);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypter.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypter.h
new file mode 100644
index 00000000000..d57a8e63bb9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypter.h
@@ -0,0 +1,94 @@
+// Copyright (c) 2012 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTER_H_
+
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/quic_versions.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// QuicCrypter is the parent class for QuicEncrypter and QuicDecrypter.
+// Its purpose is to provide an interface for using methods that are common to
+// both classes when operations are being done that apply to both encrypters and
+// decrypters.
+class QUIC_EXPORT_PRIVATE QuicCrypter {
+ public:
+ virtual ~QuicCrypter() {}
+
+ // Sets the symmetric encryption/decryption key. Returns true on success,
+ // false on failure.
+ //
+ // NOTE: The key is the client_write_key or server_write_key derived from
+ // the master secret.
+ virtual bool SetKey(absl::string_view key) = 0;
+
+ // Sets the fixed initial bytes of the nonce. Returns true on success,
+ // false on failure. This method must only be used with Google QUIC crypters.
+ //
+ // NOTE: The nonce prefix is the client_write_iv or server_write_iv
+ // derived from the master secret. A 64-bit packet number will
+ // be appended to form the nonce.
+ //
+ // <------------ 64 bits ----------->
+ // +---------------------+----------------------------------+
+ // | Fixed prefix | packet number |
+ // +---------------------+----------------------------------+
+ // Nonce format
+ //
+ // The security of the nonce format requires that QUIC never reuse a
+ // packet number, even when retransmitting a lost packet.
+ virtual bool SetNoncePrefix(absl::string_view nonce_prefix) = 0;
+
+ // Sets |iv| as the initialization vector to use when constructing the nonce.
+ // Returns true on success, false on failure. This method must only be used
+ // with IETF QUIC crypters.
+ //
+ // Google QUIC and IETF QUIC use different nonce constructions. This method
+ // must be used when using IETF QUIC; SetNoncePrefix must be used when using
+ // Google QUIC.
+ //
+ // The nonce is constructed as follows (draft-ietf-quic-tls-14 section 5.2):
+ //
+ // <---------------- max(8, N_MIN) bytes ----------------->
+ // +--------------------------------------------------------+
+ // | packet protection IV |
+ // +--------------------------------------------------------+
+ // XOR
+ // <------------ 64 bits ----------->
+ // +---------------------+----------------------------------+
+ // | zeroes | reconstructed packet number |
+ // +---------------------+----------------------------------+
+ //
+ // The nonce is the packet protection IV (|iv|) XOR'd with the left-padded
+ // reconstructed packet number.
+ //
+ // The security of the nonce format requires that QUIC never reuse a
+ // packet number, even when retransmitting a lost packet.
+ virtual bool SetIV(absl::string_view iv) = 0;
+
+ // Calls SetNoncePrefix or SetIV depending on whether |version| uses the
+ // Google QUIC crypto or IETF QUIC nonce construction.
+ virtual bool SetNoncePrefixOrIV(const ParsedQuicVersion& version,
+ absl::string_view nonce_prefix_or_iv);
+
+ // Sets the key to use for header protection.
+ virtual bool SetHeaderProtectionKey(absl::string_view key) = 0;
+
+ // GetKeySize, GetIVSize, and GetNoncePrefixSize are used to know how many
+ // bytes of key material needs to be derived from the master secret.
+
+ // Returns the size in bytes of a key for the algorithm.
+ virtual size_t GetKeySize() const = 0;
+ // Returns the size in bytes of an IV to use with the algorithm.
+ virtual size_t GetIVSize() const = 0;
+ // Returns the size in bytes of the fixed initial part of the nonce.
+ virtual size_t GetNoncePrefixSize() const = 0;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_client_config.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_client_config.cc
new file mode 100644
index 00000000000..caf6c77336d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_client_config.cc
@@ -0,0 +1,842 @@
+// Copyright 2013 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 "quiche/quic/core/crypto/quic_crypto_client_config.h"
+
+#include <algorithm>
+#include <memory>
+#include <string>
+
+#include "absl/base/macros.h"
+#include "absl/memory/memory.h"
+#include "absl/strings/match.h"
+#include "absl/strings/string_view.h"
+#include "openssl/ssl.h"
+#include "quiche/quic/core/crypto/cert_compressor.h"
+#include "quiche/quic/core/crypto/chacha20_poly1305_encrypter.h"
+#include "quiche/quic/core/crypto/crypto_framer.h"
+#include "quiche/quic/core/crypto/crypto_protocol.h"
+#include "quiche/quic/core/crypto/crypto_utils.h"
+#include "quiche/quic/core/crypto/curve25519_key_exchange.h"
+#include "quiche/quic/core/crypto/key_exchange.h"
+#include "quiche/quic/core/crypto/p256_key_exchange.h"
+#include "quiche/quic/core/crypto/proof_verifier.h"
+#include "quiche/quic/core/crypto/quic_encrypter.h"
+#include "quiche/quic/core/crypto/quic_random.h"
+#include "quiche/quic/core/crypto/tls_client_connection.h"
+#include "quiche/quic/core/quic_connection_id.h"
+#include "quiche/quic/core/quic_types.h"
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/quic/platform/api/quic_bug_tracker.h"
+#include "quiche/quic/platform/api/quic_client_stats.h"
+#include "quiche/quic/platform/api/quic_hostname_utils.h"
+#include "quiche/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+namespace {
+
+// Tracks the reason (the state of the server config) for sending inchoate
+// ClientHello to the server.
+void RecordInchoateClientHelloReason(
+ QuicCryptoClientConfig::CachedState::ServerConfigState state) {
+ QUIC_CLIENT_HISTOGRAM_ENUM(
+ "QuicInchoateClientHelloReason", state,
+ QuicCryptoClientConfig::CachedState::SERVER_CONFIG_COUNT, "");
+}
+
+// Tracks the state of the QUIC server information loaded from the disk cache.
+void RecordDiskCacheServerConfigState(
+ QuicCryptoClientConfig::CachedState::ServerConfigState state) {
+ QUIC_CLIENT_HISTOGRAM_ENUM(
+ "QuicServerInfo.DiskCacheState", state,
+ QuicCryptoClientConfig::CachedState::SERVER_CONFIG_COUNT, "");
+}
+
+} // namespace
+
+QuicCryptoClientConfig::QuicCryptoClientConfig(
+ std::unique_ptr<ProofVerifier> proof_verifier)
+ : QuicCryptoClientConfig(std::move(proof_verifier), nullptr) {}
+
+QuicCryptoClientConfig::QuicCryptoClientConfig(
+ std::unique_ptr<ProofVerifier> proof_verifier,
+ std::unique_ptr<SessionCache> session_cache)
+ : proof_verifier_(std::move(proof_verifier)),
+ session_cache_(std::move(session_cache)),
+ ssl_ctx_(TlsClientConnection::CreateSslCtx(
+ !GetQuicFlag(FLAGS_quic_disable_client_tls_zero_rtt))) {
+ QUICHE_DCHECK(proof_verifier_.get());
+ SetDefaults();
+}
+
+QuicCryptoClientConfig::~QuicCryptoClientConfig() {}
+
+QuicCryptoClientConfig::CachedState::CachedState()
+ : server_config_valid_(false),
+ expiration_time_(QuicWallTime::Zero()),
+ generation_counter_(0) {}
+
+QuicCryptoClientConfig::CachedState::~CachedState() {}
+
+bool QuicCryptoClientConfig::CachedState::IsComplete(QuicWallTime now) const {
+ if (server_config_.empty()) {
+ RecordInchoateClientHelloReason(SERVER_CONFIG_EMPTY);
+ return false;
+ }
+
+ if (!server_config_valid_) {
+ RecordInchoateClientHelloReason(SERVER_CONFIG_INVALID);
+ return false;
+ }
+
+ const CryptoHandshakeMessage* scfg = GetServerConfig();
+ if (!scfg) {
+ // Should be impossible short of cache corruption.
+ RecordInchoateClientHelloReason(SERVER_CONFIG_CORRUPTED);
+ QUICHE_DCHECK(false);
+ return false;
+ }
+
+ if (now.IsBefore(expiration_time_)) {
+ return true;
+ }
+
+ QUIC_CLIENT_HISTOGRAM_TIMES(
+ "QuicClientHelloServerConfig.InvalidDuration",
+ QuicTime::Delta::FromSeconds(now.ToUNIXSeconds() -
+ expiration_time_.ToUNIXSeconds()),
+ QuicTime::Delta::FromSeconds(60), // 1 min.
+ QuicTime::Delta::FromSeconds(20 * 24 * 3600), // 20 days.
+ 50, "");
+ RecordInchoateClientHelloReason(SERVER_CONFIG_EXPIRED);
+ return false;
+}
+
+bool QuicCryptoClientConfig::CachedState::IsEmpty() const {
+ return server_config_.empty();
+}
+
+const CryptoHandshakeMessage*
+QuicCryptoClientConfig::CachedState::GetServerConfig() const {
+ if (server_config_.empty()) {
+ return nullptr;
+ }
+
+ if (!scfg_) {
+ scfg_ = CryptoFramer::ParseMessage(server_config_);
+ QUICHE_DCHECK(scfg_.get());
+ }
+ return scfg_.get();
+}
+
+QuicCryptoClientConfig::CachedState::ServerConfigState
+QuicCryptoClientConfig::CachedState::SetServerConfig(
+ absl::string_view server_config, QuicWallTime now, QuicWallTime expiry_time,
+ std::string* error_details) {
+ const bool matches_existing = server_config == server_config_;
+
+ // Even if the new server config matches the existing one, we still wish to
+ // reject it if it has expired.
+ std::unique_ptr<CryptoHandshakeMessage> new_scfg_storage;
+ const CryptoHandshakeMessage* new_scfg;
+
+ if (!matches_existing) {
+ new_scfg_storage = CryptoFramer::ParseMessage(server_config);
+ new_scfg = new_scfg_storage.get();
+ } else {
+ new_scfg = GetServerConfig();
+ }
+
+ if (!new_scfg) {
+ *error_details = "SCFG invalid";
+ return SERVER_CONFIG_INVALID;
+ }
+
+ if (expiry_time.IsZero()) {
+ uint64_t expiry_seconds;
+ if (new_scfg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR) {
+ *error_details = "SCFG missing EXPY";
+ return SERVER_CONFIG_INVALID_EXPIRY;
+ }
+ expiration_time_ = QuicWallTime::FromUNIXSeconds(expiry_seconds);
+ } else {
+ expiration_time_ = expiry_time;
+ }
+
+ if (now.IsAfter(expiration_time_)) {
+ *error_details = "SCFG has expired";
+ return SERVER_CONFIG_EXPIRED;
+ }
+
+ if (!matches_existing) {
+ server_config_ = std::string(server_config);
+ SetProofInvalid();
+ scfg_ = std::move(new_scfg_storage);
+ }
+ return SERVER_CONFIG_VALID;
+}
+
+void QuicCryptoClientConfig::CachedState::InvalidateServerConfig() {
+ server_config_.clear();
+ scfg_.reset();
+ SetProofInvalid();
+}
+
+void QuicCryptoClientConfig::CachedState::SetProof(
+ const std::vector<std::string>& certs, absl::string_view cert_sct,
+ absl::string_view chlo_hash, absl::string_view signature) {
+ bool has_changed = signature != server_config_sig_ ||
+ chlo_hash != chlo_hash_ || certs_.size() != certs.size();
+
+ if (!has_changed) {
+ for (size_t i = 0; i < certs_.size(); i++) {
+ if (certs_[i] != certs[i]) {
+ has_changed = true;
+ break;
+ }
+ }
+ }
+
+ if (!has_changed) {
+ return;
+ }
+
+ // If the proof has changed then it needs to be revalidated.
+ SetProofInvalid();
+ certs_ = certs;
+ cert_sct_ = std::string(cert_sct);
+ chlo_hash_ = std::string(chlo_hash);
+ server_config_sig_ = std::string(signature);
+}
+
+void QuicCryptoClientConfig::CachedState::Clear() {
+ server_config_.clear();
+ source_address_token_.clear();
+ certs_.clear();
+ cert_sct_.clear();
+ chlo_hash_.clear();
+ server_config_sig_.clear();
+ server_config_valid_ = false;
+ proof_verify_details_.reset();
+ scfg_.reset();
+ ++generation_counter_;
+}
+
+void QuicCryptoClientConfig::CachedState::ClearProof() {
+ SetProofInvalid();
+ certs_.clear();
+ cert_sct_.clear();
+ chlo_hash_.clear();
+ server_config_sig_.clear();
+}
+
+void QuicCryptoClientConfig::CachedState::SetProofValid() {
+ server_config_valid_ = true;
+}
+
+void QuicCryptoClientConfig::CachedState::SetProofInvalid() {
+ server_config_valid_ = false;
+ ++generation_counter_;
+}
+
+bool QuicCryptoClientConfig::CachedState::Initialize(
+ absl::string_view server_config, absl::string_view source_address_token,
+ const std::vector<std::string>& certs, const std::string& cert_sct,
+ absl::string_view chlo_hash, absl::string_view signature, QuicWallTime now,
+ QuicWallTime expiration_time) {
+ QUICHE_DCHECK(server_config_.empty());
+
+ if (server_config.empty()) {
+ RecordDiskCacheServerConfigState(SERVER_CONFIG_EMPTY);
+ return false;
+ }
+
+ std::string error_details;
+ ServerConfigState state =
+ SetServerConfig(server_config, now, expiration_time, &error_details);
+ RecordDiskCacheServerConfigState(state);
+ if (state != SERVER_CONFIG_VALID) {
+ QUIC_DVLOG(1) << "SetServerConfig failed with " << error_details;
+ return false;
+ }
+
+ chlo_hash_.assign(chlo_hash.data(), chlo_hash.size());
+ server_config_sig_.assign(signature.data(), signature.size());
+ source_address_token_.assign(source_address_token.data(),
+ source_address_token.size());
+ certs_ = certs;
+ cert_sct_ = cert_sct;
+ return true;
+}
+
+const std::string& QuicCryptoClientConfig::CachedState::server_config() const {
+ return server_config_;
+}
+
+const std::string& QuicCryptoClientConfig::CachedState::source_address_token()
+ const {
+ return source_address_token_;
+}
+
+const std::vector<std::string>& QuicCryptoClientConfig::CachedState::certs()
+ const {
+ return certs_;
+}
+
+const std::string& QuicCryptoClientConfig::CachedState::cert_sct() const {
+ return cert_sct_;
+}
+
+const std::string& QuicCryptoClientConfig::CachedState::chlo_hash() const {
+ return chlo_hash_;
+}
+
+const std::string& QuicCryptoClientConfig::CachedState::signature() const {
+ return server_config_sig_;
+}
+
+bool QuicCryptoClientConfig::CachedState::proof_valid() const {
+ return server_config_valid_;
+}
+
+uint64_t QuicCryptoClientConfig::CachedState::generation_counter() const {
+ return generation_counter_;
+}
+
+const ProofVerifyDetails*
+QuicCryptoClientConfig::CachedState::proof_verify_details() const {
+ return proof_verify_details_.get();
+}
+
+void QuicCryptoClientConfig::CachedState::set_source_address_token(
+ absl::string_view token) {
+ source_address_token_ = std::string(token);
+}
+
+void QuicCryptoClientConfig::CachedState::set_cert_sct(
+ absl::string_view cert_sct) {
+ cert_sct_ = std::string(cert_sct);
+}
+
+void QuicCryptoClientConfig::CachedState::SetProofVerifyDetails(
+ ProofVerifyDetails* details) {
+ proof_verify_details_.reset(details);
+}
+
+void QuicCryptoClientConfig::CachedState::InitializeFrom(
+ const QuicCryptoClientConfig::CachedState& other) {
+ QUICHE_DCHECK(server_config_.empty());
+ QUICHE_DCHECK(!server_config_valid_);
+ server_config_ = other.server_config_;
+ source_address_token_ = other.source_address_token_;
+ certs_ = other.certs_;
+ cert_sct_ = other.cert_sct_;
+ chlo_hash_ = other.chlo_hash_;
+ server_config_sig_ = other.server_config_sig_;
+ server_config_valid_ = other.server_config_valid_;
+ expiration_time_ = other.expiration_time_;
+ if (other.proof_verify_details_ != nullptr) {
+ proof_verify_details_.reset(other.proof_verify_details_->Clone());
+ }
+ ++generation_counter_;
+}
+
+void QuicCryptoClientConfig::SetDefaults() {
+ // Key exchange methods.
+ kexs = {kC255, kP256};
+
+ // Authenticated encryption algorithms. Prefer AES-GCM if hardware-supported
+ // fast implementation is available.
+ if (EVP_has_aes_hardware() == 1) {
+ aead = {kAESG, kCC20};
+ } else {
+ aead = {kCC20, kAESG};
+ }
+}
+
+QuicCryptoClientConfig::CachedState* QuicCryptoClientConfig::LookupOrCreate(
+ const QuicServerId& server_id) {
+ auto it = cached_states_.find(server_id);
+ if (it != cached_states_.end()) {
+ return it->second.get();
+ }
+
+ CachedState* cached = new CachedState;
+ cached_states_.insert(std::make_pair(server_id, absl::WrapUnique(cached)));
+ bool cache_populated = PopulateFromCanonicalConfig(server_id, cached);
+ QUIC_CLIENT_HISTOGRAM_BOOL(
+ "QuicCryptoClientConfig.PopulatedFromCanonicalConfig", cache_populated,
+ "");
+ return cached;
+}
+
+void QuicCryptoClientConfig::ClearCachedStates(const ServerIdFilter& filter) {
+ for (auto it = cached_states_.begin(); it != cached_states_.end(); ++it) {
+ if (filter.Matches(it->first)) it->second->Clear();
+ }
+}
+
+void QuicCryptoClientConfig::FillInchoateClientHello(
+ const QuicServerId& server_id, const ParsedQuicVersion preferred_version,
+ const CachedState* cached, QuicRandom* rand, bool demand_x509_proof,
+ quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters>
+ out_params,
+ CryptoHandshakeMessage* out) const {
+ out->set_tag(kCHLO);
+ out->set_minimum_size(1);
+
+ // Server name indication. We only send SNI if it's a valid domain name, as
+ // per the spec.
+ if (QuicHostnameUtils::IsValidSNI(server_id.host())) {
+ out->SetStringPiece(kSNI, server_id.host());
+ }
+ out->SetVersion(kVER, preferred_version);
+
+ if (!user_agent_id_.empty()) {
+ out->SetStringPiece(kUAID, user_agent_id_);
+ }
+
+ if (!alpn_.empty()) {
+ out->SetStringPiece(kALPN, alpn_);
+ }
+
+ // Even though this is an inchoate CHLO, send the SCID so that
+ // the STK can be validated by the server.
+ const CryptoHandshakeMessage* scfg = cached->GetServerConfig();
+ if (scfg != nullptr) {
+ absl::string_view scid;
+ if (scfg->GetStringPiece(kSCID, &scid)) {
+ out->SetStringPiece(kSCID, scid);
+ }
+ }
+
+ if (!cached->source_address_token().empty()) {
+ out->SetStringPiece(kSourceAddressTokenTag, cached->source_address_token());
+ }
+
+ if (!demand_x509_proof) {
+ return;
+ }
+
+ char proof_nonce[32];
+ rand->RandBytes(proof_nonce, ABSL_ARRAYSIZE(proof_nonce));
+ out->SetStringPiece(
+ kNONP, absl::string_view(proof_nonce, ABSL_ARRAYSIZE(proof_nonce)));
+
+ out->SetVector(kPDMD, QuicTagVector{kX509});
+
+ out->SetStringPiece(kCertificateSCTTag, "");
+
+ const std::vector<std::string>& certs = cached->certs();
+ // We save |certs| in the QuicCryptoNegotiatedParameters so that, if the
+ // client config is being used for multiple connections, another connection
+ // doesn't update the cached certificates and cause us to be unable to
+ // process the server's compressed certificate chain.
+ out_params->cached_certs = certs;
+ if (!certs.empty()) {
+ std::vector<uint64_t> hashes;
+ hashes.reserve(certs.size());
+ for (auto i = certs.begin(); i != certs.end(); ++i) {
+ hashes.push_back(QuicUtils::FNV1a_64_Hash(*i));
+ }
+ out->SetVector(kCCRT, hashes);
+ }
+}
+
+QuicErrorCode QuicCryptoClientConfig::FillClientHello(
+ const QuicServerId& server_id, QuicConnectionId connection_id,
+ const ParsedQuicVersion preferred_version,
+ const ParsedQuicVersion actual_version, const CachedState* cached,
+ QuicWallTime now, QuicRandom* rand,
+ quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters>
+ out_params,
+ CryptoHandshakeMessage* out, std::string* error_details) const {
+ QUICHE_DCHECK(error_details != nullptr);
+ QUIC_BUG_IF(quic_bug_12943_2,
+ !QuicUtils::IsConnectionIdValidForVersion(
+ connection_id, preferred_version.transport_version))
+ << "FillClientHello: attempted to use connection ID " << connection_id
+ << " which is invalid with version " << preferred_version;
+
+ FillInchoateClientHello(server_id, preferred_version, cached, rand,
+ /* demand_x509_proof= */ true, out_params, out);
+
+ out->set_minimum_size(1);
+
+ const CryptoHandshakeMessage* scfg = cached->GetServerConfig();
+ if (!scfg) {
+ // This should never happen as our caller should have checked
+ // cached->IsComplete() before calling this function.
+ *error_details = "Handshake not ready";
+ return QUIC_CRYPTO_INTERNAL_ERROR;
+ }
+
+ absl::string_view scid;
+ if (!scfg->GetStringPiece(kSCID, &scid)) {
+ *error_details = "SCFG missing SCID";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+ out->SetStringPiece(kSCID, scid);
+
+ out->SetStringPiece(kCertificateSCTTag, "");
+
+ QuicTagVector their_aeads;
+ QuicTagVector their_key_exchanges;
+ if (scfg->GetTaglist(kAEAD, &their_aeads) != QUIC_NO_ERROR ||
+ scfg->GetTaglist(kKEXS, &their_key_exchanges) != QUIC_NO_ERROR) {
+ *error_details = "Missing AEAD or KEXS";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ // AEAD: the work loads on the client and server are symmetric. Since the
+ // client is more likely to be CPU-constrained, break the tie by favoring
+ // the client's preference.
+ // Key exchange: the client does more work than the server, so favor the
+ // client's preference.
+ size_t key_exchange_index;
+ if (!FindMutualQuicTag(aead, their_aeads, &out_params->aead, nullptr) ||
+ !FindMutualQuicTag(kexs, their_key_exchanges, &out_params->key_exchange,
+ &key_exchange_index)) {
+ *error_details = "Unsupported AEAD or KEXS";
+ return QUIC_CRYPTO_NO_SUPPORT;
+ }
+ out->SetVector(kAEAD, QuicTagVector{out_params->aead});
+ out->SetVector(kKEXS, QuicTagVector{out_params->key_exchange});
+
+ absl::string_view public_value;
+ if (scfg->GetNthValue24(kPUBS, key_exchange_index, &public_value) !=
+ QUIC_NO_ERROR) {
+ *error_details = "Missing public value";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ absl::string_view orbit;
+ if (!scfg->GetStringPiece(kORBT, &orbit) || orbit.size() != kOrbitSize) {
+ *error_details = "SCFG missing OBIT";
+ return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
+ }
+
+ CryptoUtils::GenerateNonce(now, rand, orbit, &out_params->client_nonce);
+ out->SetStringPiece(kNONC, out_params->client_nonce);
+ if (!out_params->server_nonce.empty()) {
+ out->SetStringPiece(kServerNonceTag, out_params->server_nonce);
+ }
+
+ switch (out_params->key_exchange) {
+ case kC255:
+ out_params->client_key_exchange = Curve25519KeyExchange::New(
+ Curve25519KeyExchange::NewPrivateKey(rand));
+ break;
+ case kP256:
+ out_params->client_key_exchange =
+ P256KeyExchange::New(P256KeyExchange::NewPrivateKey());
+ break;
+ default:
+ QUICHE_DCHECK(false);
+ *error_details = "Configured to support an unknown key exchange";
+ return QUIC_CRYPTO_INTERNAL_ERROR;
+ }
+
+ if (!out_params->client_key_exchange->CalculateSharedKeySync(
+ public_value, &out_params->initial_premaster_secret)) {
+ *error_details = "Key exchange failure";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+ out->SetStringPiece(kPUBS, out_params->client_key_exchange->public_value());
+
+ const std::vector<std::string>& certs = cached->certs();
+ if (certs.empty()) {
+ *error_details = "No certs to calculate XLCT";
+ return QUIC_CRYPTO_INTERNAL_ERROR;
+ }
+ out->SetValue(kXLCT, CryptoUtils::ComputeLeafCertHash(certs[0]));
+
+ // Derive the symmetric keys and set up the encrypters and decrypters.
+ // Set the following members of out_params:
+ // out_params->hkdf_input_suffix
+ // out_params->initial_crypters
+ out_params->hkdf_input_suffix.clear();
+ out_params->hkdf_input_suffix.append(connection_id.data(),
+ connection_id.length());
+ const QuicData& client_hello_serialized = out->GetSerialized();
+ out_params->hkdf_input_suffix.append(client_hello_serialized.data(),
+ client_hello_serialized.length());
+ out_params->hkdf_input_suffix.append(cached->server_config());
+ if (certs.empty()) {
+ *error_details = "No certs found to include in KDF";
+ return QUIC_CRYPTO_INTERNAL_ERROR;
+ }
+ out_params->hkdf_input_suffix.append(certs[0]);
+
+ std::string hkdf_input;
+ const size_t label_len = strlen(QuicCryptoConfig::kInitialLabel) + 1;
+ hkdf_input.reserve(label_len + out_params->hkdf_input_suffix.size());
+ hkdf_input.append(QuicCryptoConfig::kInitialLabel, label_len);
+ hkdf_input.append(out_params->hkdf_input_suffix);
+
+ std::string* subkey_secret = &out_params->initial_subkey_secret;
+
+ if (!CryptoUtils::DeriveKeys(
+ actual_version, out_params->initial_premaster_secret,
+ out_params->aead, out_params->client_nonce, out_params->server_nonce,
+ pre_shared_key_, hkdf_input, Perspective::IS_CLIENT,
+ CryptoUtils::Diversification::Pending(),
+ &out_params->initial_crypters, subkey_secret)) {
+ *error_details = "Symmetric key setup failed";
+ return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED;
+ }
+
+ return QUIC_NO_ERROR;
+}
+
+QuicErrorCode QuicCryptoClientConfig::CacheNewServerConfig(
+ const CryptoHandshakeMessage& message, QuicWallTime now,
+ QuicTransportVersion /*version*/, absl::string_view chlo_hash,
+ const std::vector<std::string>& cached_certs, CachedState* cached,
+ std::string* error_details) {
+ QUICHE_DCHECK(error_details != nullptr);
+
+ absl::string_view scfg;
+ if (!message.GetStringPiece(kSCFG, &scfg)) {
+ *error_details = "Missing SCFG";
+ return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
+ }
+
+ QuicWallTime expiration_time = QuicWallTime::Zero();
+ uint64_t expiry_seconds;
+ if (message.GetUint64(kSTTL, &expiry_seconds) == QUIC_NO_ERROR) {
+ // Only cache configs for a maximum of 1 week.
+ expiration_time = now.Add(QuicTime::Delta::FromSeconds(
+ std::min(expiry_seconds, kNumSecondsPerWeek)));
+ }
+
+ CachedState::ServerConfigState state =
+ cached->SetServerConfig(scfg, now, expiration_time, error_details);
+ if (state == CachedState::SERVER_CONFIG_EXPIRED) {
+ return QUIC_CRYPTO_SERVER_CONFIG_EXPIRED;
+ }
+ // TODO(rtenneti): Return more specific error code than returning
+ // QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER.
+ if (state != CachedState::SERVER_CONFIG_VALID) {
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ absl::string_view token;
+ if (message.GetStringPiece(kSourceAddressTokenTag, &token)) {
+ cached->set_source_address_token(token);
+ }
+
+ absl::string_view proof, cert_bytes, cert_sct;
+ bool has_proof = message.GetStringPiece(kPROF, &proof);
+ bool has_cert = message.GetStringPiece(kCertificateTag, &cert_bytes);
+ if (has_proof && has_cert) {
+ std::vector<std::string> certs;
+ if (!CertCompressor::DecompressChain(cert_bytes, cached_certs, &certs)) {
+ *error_details = "Certificate data invalid";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ message.GetStringPiece(kCertificateSCTTag, &cert_sct);
+ cached->SetProof(certs, cert_sct, chlo_hash, proof);
+ } else {
+ // Secure QUIC: clear existing proof as we have been sent a new SCFG
+ // without matching proof/certs.
+ cached->ClearProof();
+
+ if (has_proof && !has_cert) {
+ *error_details = "Certificate missing";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ if (!has_proof && has_cert) {
+ *error_details = "Proof missing";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+ }
+
+ return QUIC_NO_ERROR;
+}
+
+QuicErrorCode QuicCryptoClientConfig::ProcessRejection(
+ const CryptoHandshakeMessage& rej, QuicWallTime now,
+ const QuicTransportVersion version, absl::string_view chlo_hash,
+ CachedState* cached,
+ quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters>
+ out_params,
+ std::string* error_details) {
+ QUICHE_DCHECK(error_details != nullptr);
+
+ if (rej.tag() != kREJ) {
+ *error_details = "Message is not REJ";
+ return QUIC_CRYPTO_INTERNAL_ERROR;
+ }
+
+ QuicErrorCode error =
+ CacheNewServerConfig(rej, now, version, chlo_hash,
+ out_params->cached_certs, cached, error_details);
+ if (error != QUIC_NO_ERROR) {
+ return error;
+ }
+
+ absl::string_view nonce;
+ if (rej.GetStringPiece(kServerNonceTag, &nonce)) {
+ out_params->server_nonce = std::string(nonce);
+ }
+
+ return QUIC_NO_ERROR;
+}
+
+QuicErrorCode QuicCryptoClientConfig::ProcessServerHello(
+ const CryptoHandshakeMessage& server_hello,
+ QuicConnectionId /*connection_id*/, ParsedQuicVersion version,
+ const ParsedQuicVersionVector& negotiated_versions, CachedState* cached,
+ quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters>
+ out_params,
+ std::string* error_details) {
+ QUICHE_DCHECK(error_details != nullptr);
+
+ QuicErrorCode valid = CryptoUtils::ValidateServerHello(
+ server_hello, negotiated_versions, error_details);
+ if (valid != QUIC_NO_ERROR) {
+ return valid;
+ }
+
+ // Learn about updated source address tokens.
+ absl::string_view token;
+ if (server_hello.GetStringPiece(kSourceAddressTokenTag, &token)) {
+ cached->set_source_address_token(token);
+ }
+
+ absl::string_view shlo_nonce;
+ if (!server_hello.GetStringPiece(kServerNonceTag, &shlo_nonce)) {
+ *error_details = "server hello missing server nonce";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ // TODO(agl):
+ // learn about updated SCFGs.
+
+ absl::string_view public_value;
+ if (!server_hello.GetStringPiece(kPUBS, &public_value)) {
+ *error_details = "server hello missing forward secure public value";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ if (!out_params->client_key_exchange->CalculateSharedKeySync(
+ public_value, &out_params->forward_secure_premaster_secret)) {
+ *error_details = "Key exchange failure";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ std::string hkdf_input;
+ const size_t label_len = strlen(QuicCryptoConfig::kForwardSecureLabel) + 1;
+ hkdf_input.reserve(label_len + out_params->hkdf_input_suffix.size());
+ hkdf_input.append(QuicCryptoConfig::kForwardSecureLabel, label_len);
+ hkdf_input.append(out_params->hkdf_input_suffix);
+
+ if (!CryptoUtils::DeriveKeys(
+ version, out_params->forward_secure_premaster_secret,
+ out_params->aead, out_params->client_nonce,
+ shlo_nonce.empty() ? out_params->server_nonce : shlo_nonce,
+ pre_shared_key_, hkdf_input, Perspective::IS_CLIENT,
+ CryptoUtils::Diversification::Never(),
+ &out_params->forward_secure_crypters, &out_params->subkey_secret)) {
+ *error_details = "Symmetric key setup failed";
+ return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED;
+ }
+
+ return QUIC_NO_ERROR;
+}
+
+QuicErrorCode QuicCryptoClientConfig::ProcessServerConfigUpdate(
+ const CryptoHandshakeMessage& server_config_update, QuicWallTime now,
+ const QuicTransportVersion version, absl::string_view chlo_hash,
+ CachedState* cached,
+ quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters>
+ out_params,
+ std::string* error_details) {
+ QUICHE_DCHECK(error_details != nullptr);
+
+ if (server_config_update.tag() != kSCUP) {
+ *error_details = "ServerConfigUpdate must have kSCUP tag.";
+ return QUIC_INVALID_CRYPTO_MESSAGE_TYPE;
+ }
+ return CacheNewServerConfig(server_config_update, now, version, chlo_hash,
+ out_params->cached_certs, cached, error_details);
+}
+
+ProofVerifier* QuicCryptoClientConfig::proof_verifier() const {
+ return proof_verifier_.get();
+}
+
+SessionCache* QuicCryptoClientConfig::session_cache() const {
+ return session_cache_.get();
+}
+
+ClientProofSource* QuicCryptoClientConfig::proof_source() const {
+ return proof_source_.get();
+}
+
+void QuicCryptoClientConfig::set_proof_source(
+ std::unique_ptr<ClientProofSource> proof_source) {
+ proof_source_ = std::move(proof_source);
+}
+
+SSL_CTX* QuicCryptoClientConfig::ssl_ctx() const { return ssl_ctx_.get(); }
+
+void QuicCryptoClientConfig::InitializeFrom(
+ const QuicServerId& server_id, const QuicServerId& canonical_server_id,
+ QuicCryptoClientConfig* canonical_crypto_config) {
+ CachedState* canonical_cached =
+ canonical_crypto_config->LookupOrCreate(canonical_server_id);
+ if (!canonical_cached->proof_valid()) {
+ return;
+ }
+ CachedState* cached = LookupOrCreate(server_id);
+ cached->InitializeFrom(*canonical_cached);
+}
+
+void QuicCryptoClientConfig::AddCanonicalSuffix(const std::string& suffix) {
+ canonical_suffixes_.push_back(suffix);
+}
+
+bool QuicCryptoClientConfig::PopulateFromCanonicalConfig(
+ const QuicServerId& server_id, CachedState* cached) {
+ QUICHE_DCHECK(cached->IsEmpty());
+ size_t i = 0;
+ for (; i < canonical_suffixes_.size(); ++i) {
+ if (absl::EndsWithIgnoreCase(server_id.host(), canonical_suffixes_[i])) {
+ break;
+ }
+ }
+ if (i == canonical_suffixes_.size()) {
+ return false;
+ }
+
+ QuicServerId suffix_server_id(canonical_suffixes_[i], server_id.port(),
+ server_id.privacy_mode_enabled());
+ auto it = canonical_server_map_.lower_bound(suffix_server_id);
+ if (it == canonical_server_map_.end() || it->first != suffix_server_id) {
+ // This is the first host we've seen which matches the suffix, so make it
+ // canonical. Use |it| as position hint for faster insertion.
+ canonical_server_map_.insert(
+ it, std::make_pair(std::move(suffix_server_id), std::move(server_id)));
+ return false;
+ }
+
+ const QuicServerId& canonical_server_id = it->second;
+ CachedState* canonical_state = cached_states_[canonical_server_id].get();
+ if (!canonical_state->proof_valid()) {
+ return false;
+ }
+
+ // Update canonical version to point at the "most recent" entry.
+ it->second = server_id;
+
+ cached->InitializeFrom(*canonical_state);
+ return true;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_client_config.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_client_config.h
new file mode 100644
index 00000000000..f44aac1312a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_client_config.h
@@ -0,0 +1,467 @@
+// Copyright 2013 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_CLIENT_CONFIG_H_
+#define QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_CLIENT_CONFIG_H_
+
+#include <cstdint>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "openssl/base.h"
+#include "openssl/ssl.h"
+#include "quiche/quic/core/crypto/client_proof_source.h"
+#include "quiche/quic/core/crypto/crypto_handshake.h"
+#include "quiche/quic/core/crypto/crypto_protocol.h"
+#include "quiche/quic/core/crypto/transport_parameters.h"
+#include "quiche/quic/core/quic_packets.h"
+#include "quiche/quic/core/quic_server_id.h"
+#include "quiche/quic/platform/api/quic_export.h"
+#include "quiche/common/platform/api/quiche_reference_counted.h"
+
+namespace quic {
+
+class CryptoHandshakeMessage;
+class ProofVerifier;
+class ProofVerifyDetails;
+class QuicRandom;
+
+// QuicResumptionState stores the state a client needs for performing connection
+// resumption.
+struct QUIC_EXPORT_PRIVATE QuicResumptionState {
+ // |tls_session| holds the cryptographic state necessary for a resumption. It
+ // includes the ALPN negotiated on the connection where the ticket was
+ // received.
+ bssl::UniquePtr<SSL_SESSION> tls_session;
+
+ // If the application using QUIC doesn't support 0-RTT handshakes or the
+ // client didn't receive a 0-RTT capable session ticket from the server,
+ // |transport_params| will be null. Otherwise, it will contain the transport
+ // parameters received from the server on the original connection.
+ std::unique_ptr<TransportParameters> transport_params = nullptr;
+
+ // If |transport_params| is null, then |application_state| is ignored and
+ // should be empty. |application_state| contains serialized state that the
+ // client received from the server at the application layer that the client
+ // needs to remember when performing a 0-RTT handshake.
+ std::unique_ptr<ApplicationState> application_state = nullptr;
+
+ // Opaque token received in NEW_TOKEN frame if any.
+ std::string token;
+};
+
+// SessionCache is an interface for managing storing and retrieving
+// QuicResumptionState structs.
+class QUIC_EXPORT_PRIVATE SessionCache {
+ public:
+ virtual ~SessionCache() {}
+
+ // Inserts |session|, |params|, and |application_states| into the cache, keyed
+ // by |server_id|. Insert is first called after all three values are present.
+ // The ownership of |session| is transferred to the cache, while other two are
+ // copied. Multiple sessions might need to be inserted for a connection.
+ // SessionCache implementations should support storing
+ // multiple entries per server ID.
+ virtual void Insert(const QuicServerId& server_id,
+ bssl::UniquePtr<SSL_SESSION> session,
+ const TransportParameters& params,
+ const ApplicationState* application_state) = 0;
+
+ // Lookup is called once at the beginning of each TLS handshake to potentially
+ // provide the saved state both for the TLS handshake and for sending 0-RTT
+ // data (if supported). Lookup may return a nullptr. Implementations should
+ // delete cache entries after returning them in Lookup so that session tickets
+ // are used only once.
+ virtual std::unique_ptr<QuicResumptionState> Lookup(
+ const QuicServerId& server_id, QuicWallTime now, const SSL_CTX* ctx) = 0;
+
+ // Called when 0-RTT is rejected. Disables early data for all the TLS tickets
+ // associated with |server_id|.
+ virtual void ClearEarlyData(const QuicServerId& server_id) = 0;
+
+ // Called when NEW_TOKEN frame is received.
+ virtual void OnNewTokenReceived(const QuicServerId& server_id,
+ absl::string_view token) = 0;
+
+ // Called to remove expired entries.
+ virtual void RemoveExpiredEntries(QuicWallTime now) = 0;
+
+ // Clear the session cache.
+ virtual void Clear() = 0;
+};
+
+// QuicCryptoClientConfig contains crypto-related configuration settings for a
+// client. Note that this object isn't thread-safe. It's designed to be used on
+// a single thread at a time.
+class QUIC_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig {
+ public:
+ // A CachedState contains the information that the client needs in order to
+ // perform a 0-RTT handshake with a server. This information can be reused
+ // over several connections to the same server.
+ class QUIC_EXPORT_PRIVATE CachedState {
+ public:
+ // Enum to track if the server config is valid or not. If it is not valid,
+ // it specifies why it is invalid.
+ enum ServerConfigState {
+ // WARNING: Do not change the numerical values of any of server config
+ // state. Do not remove deprecated server config states - just comment
+ // them as deprecated.
+ SERVER_CONFIG_EMPTY = 0,
+ SERVER_CONFIG_INVALID = 1,
+ SERVER_CONFIG_CORRUPTED = 2,
+ SERVER_CONFIG_EXPIRED = 3,
+ SERVER_CONFIG_INVALID_EXPIRY = 4,
+ SERVER_CONFIG_VALID = 5,
+ // NOTE: Add new server config states only immediately above this line.
+ // Make sure to update the QuicServerConfigState enum in
+ // tools/metrics/histograms/histograms.xml accordingly.
+ SERVER_CONFIG_COUNT
+ };
+
+ CachedState();
+ CachedState(const CachedState&) = delete;
+ CachedState& operator=(const CachedState&) = delete;
+ ~CachedState();
+
+ // IsComplete returns true if this object contains enough information to
+ // perform a handshake with the server. |now| is used to judge whether any
+ // cached server config has expired.
+ bool IsComplete(QuicWallTime now) const;
+
+ // IsEmpty returns true if |server_config_| is empty.
+ bool IsEmpty() const;
+
+ // GetServerConfig returns the parsed contents of |server_config|, or
+ // nullptr if |server_config| is empty. The return value is owned by this
+ // object and is destroyed when this object is.
+ const CryptoHandshakeMessage* GetServerConfig() const;
+
+ // SetServerConfig checks that |server_config| parses correctly and stores
+ // it in |server_config_|. |now| is used to judge whether |server_config|
+ // has expired.
+ ServerConfigState SetServerConfig(absl::string_view server_config,
+ QuicWallTime now,
+ QuicWallTime expiry_time,
+ std::string* error_details);
+
+ // InvalidateServerConfig clears the cached server config (if any).
+ void InvalidateServerConfig();
+
+ // SetProof stores a cert chain, cert signed timestamp and signature.
+ void SetProof(const std::vector<std::string>& certs,
+ absl::string_view cert_sct, absl::string_view chlo_hash,
+ absl::string_view signature);
+
+ // Clears all the data.
+ void Clear();
+
+ // Clears the certificate chain and signature and invalidates the proof.
+ void ClearProof();
+
+ // SetProofValid records that the certificate chain and signature have been
+ // validated and that it's safe to assume that the server is legitimate.
+ // (Note: this does not check the chain or signature.)
+ void SetProofValid();
+
+ // If the server config or the proof has changed then it needs to be
+ // revalidated. Helper function to keep server_config_valid_ and
+ // generation_counter_ in sync.
+ void SetProofInvalid();
+
+ const std::string& server_config() const;
+ const std::string& source_address_token() const;
+ const std::vector<std::string>& certs() const;
+ const std::string& cert_sct() const;
+ const std::string& chlo_hash() const;
+ const std::string& signature() const;
+ bool proof_valid() const;
+ uint64_t generation_counter() const;
+ const ProofVerifyDetails* proof_verify_details() const;
+
+ void set_source_address_token(absl::string_view token);
+
+ void set_cert_sct(absl::string_view cert_sct);
+
+ // SetProofVerifyDetails takes ownership of |details|.
+ void SetProofVerifyDetails(ProofVerifyDetails* details);
+
+ // Copy the |server_config_|, |source_address_token_|, |certs_|,
+ // |expiration_time_|, |cert_sct_|, |chlo_hash_| and |server_config_sig_|
+ // from the |other|. The remaining fields, |generation_counter_|,
+ // |proof_verify_details_|, and |scfg_| remain unchanged.
+ void InitializeFrom(const CachedState& other);
+
+ // Initializes this cached state based on the arguments provided.
+ // Returns false if there is a problem parsing the server config.
+ bool Initialize(absl::string_view server_config,
+ absl::string_view source_address_token,
+ const std::vector<std::string>& certs,
+ const std::string& cert_sct, absl::string_view chlo_hash,
+ absl::string_view signature, QuicWallTime now,
+ QuicWallTime expiration_time);
+
+ private:
+ std::string server_config_; // A serialized handshake message.
+ std::string source_address_token_; // An opaque proof of IP ownership.
+ std::vector<std::string> certs_; // A list of certificates in leaf-first
+ // order.
+ std::string cert_sct_; // Signed timestamp of the leaf cert.
+ std::string chlo_hash_; // Hash of the CHLO message.
+ std::string server_config_sig_; // A signature of |server_config_|.
+ bool server_config_valid_; // True if |server_config_| is correctly
+ // signed and |certs_| has been validated.
+ QuicWallTime expiration_time_; // Time when the config is no longer valid.
+ // Generation counter associated with the |server_config_|, |certs_| and
+ // |server_config_sig_| combination. It is incremented whenever we set
+ // server_config_valid_ to false.
+ uint64_t generation_counter_;
+
+ std::unique_ptr<ProofVerifyDetails> proof_verify_details_;
+
+ // scfg contains the cached, parsed value of |server_config|.
+ mutable std::unique_ptr<CryptoHandshakeMessage> scfg_;
+ };
+
+ // Used to filter server ids for partial config deletion.
+ class QUIC_EXPORT_PRIVATE ServerIdFilter {
+ public:
+ virtual ~ServerIdFilter() {}
+
+ // Returns true if |server_id| matches the filter.
+ virtual bool Matches(const QuicServerId& server_id) const = 0;
+ };
+
+ // DEPRECATED: Use the constructor below instead.
+ explicit QuicCryptoClientConfig(
+ std::unique_ptr<ProofVerifier> proof_verifier);
+ QuicCryptoClientConfig(std::unique_ptr<ProofVerifier> proof_verifier,
+ std::unique_ptr<SessionCache> session_cache);
+ QuicCryptoClientConfig(const QuicCryptoClientConfig&) = delete;
+ QuicCryptoClientConfig& operator=(const QuicCryptoClientConfig&) = delete;
+ ~QuicCryptoClientConfig();
+
+ // LookupOrCreate returns a CachedState for the given |server_id|. If no such
+ // CachedState currently exists, it will be created and cached.
+ CachedState* LookupOrCreate(const QuicServerId& server_id);
+
+ // Delete CachedState objects whose server ids match |filter| from
+ // cached_states.
+ void ClearCachedStates(const ServerIdFilter& filter);
+
+ // FillInchoateClientHello sets |out| to be a CHLO message that elicits a
+ // source-address token or SCFG from a server. If |cached| is non-nullptr, the
+ // source-address token will be taken from it. |out_params| is used in order
+ // to store the cached certs that were sent as hints to the server in
+ // |out_params->cached_certs|. |preferred_version| is the version of the
+ // QUIC protocol that this client chose to use initially. This allows the
+ // server to detect downgrade attacks. If |demand_x509_proof| is true,
+ // then |out| will include an X509 proof demand, and the associated
+ // certificate related fields.
+ void FillInchoateClientHello(
+ const QuicServerId& server_id, const ParsedQuicVersion preferred_version,
+ const CachedState* cached, QuicRandom* rand, bool demand_x509_proof,
+ quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters>
+ out_params,
+ CryptoHandshakeMessage* out) const;
+
+ // FillClientHello sets |out| to be a CHLO message based on the configuration
+ // of this object. This object must have cached enough information about
+ // the server's hostname in order to perform a handshake. This can be checked
+ // with the |IsComplete| member of |CachedState|.
+ //
+ // |now| and |rand| are used to generate the nonce and |out_params| is
+ // filled with the results of the handshake that the server is expected to
+ // accept. |preferred_version| is the version of the QUIC protocol that this
+ // client chose to use initially. This allows the server to detect downgrade
+ // attacks.
+ //
+ // If |channel_id_key| is not null, it is used to sign a secret value derived
+ // from the client and server's keys, and the Channel ID public key and the
+ // signature are placed in the CETV value of the CHLO.
+ QuicErrorCode FillClientHello(
+ const QuicServerId& server_id, QuicConnectionId connection_id,
+ const ParsedQuicVersion preferred_version,
+ const ParsedQuicVersion actual_version, const CachedState* cached,
+ QuicWallTime now, QuicRandom* rand,
+ quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters>
+ out_params,
+ CryptoHandshakeMessage* out, std::string* error_details) const;
+
+ // ProcessRejection processes a REJ message from a server and updates the
+ // cached information about that server. After this, |IsComplete| may return
+ // true for that server's CachedState. If the rejection message contains state
+ // about a future handshake (i.e. an nonce value from the server), then it
+ // will be saved in |out_params|. |now| is used to judge whether the server
+ // config in the rejection message has expired.
+ QuicErrorCode ProcessRejection(
+ const CryptoHandshakeMessage& rej, QuicWallTime now,
+ QuicTransportVersion version, absl::string_view chlo_hash,
+ CachedState* cached,
+ quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters>
+ out_params,
+ std::string* error_details);
+
+ // ProcessServerHello processes the message in |server_hello|, updates the
+ // cached information about that server, writes the negotiated parameters to
+ // |out_params| and returns QUIC_NO_ERROR. If |server_hello| is unacceptable
+ // then it puts an error message in |error_details| and returns an error
+ // code. |version| is the QUIC version for the current connection.
+ // |negotiated_versions| contains the list of version, if any, that were
+ // present in a version negotiation packet previously received from the
+ // server. The contents of this list will be compared against the list of
+ // versions provided in the VER tag of the server hello.
+ QuicErrorCode ProcessServerHello(
+ const CryptoHandshakeMessage& server_hello,
+ QuicConnectionId connection_id, ParsedQuicVersion version,
+ const ParsedQuicVersionVector& negotiated_versions, CachedState* cached,
+ quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters>
+ out_params,
+ std::string* error_details);
+
+ // Processes the message in |server_config_update|, updating the cached source
+ // address token, and server config.
+ // If |server_config_update| is invalid then |error_details| will contain an
+ // error message, and an error code will be returned. If all has gone well
+ // QUIC_NO_ERROR is returned.
+ QuicErrorCode ProcessServerConfigUpdate(
+ const CryptoHandshakeMessage& server_config_update, QuicWallTime now,
+ const QuicTransportVersion version, absl::string_view chlo_hash,
+ CachedState* cached,
+ quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters>
+ out_params,
+ std::string* error_details);
+
+ ProofVerifier* proof_verifier() const;
+ SessionCache* session_cache() const;
+ ClientProofSource* proof_source() const;
+ void set_proof_source(std::unique_ptr<ClientProofSource> proof_source);
+ SSL_CTX* ssl_ctx() const;
+
+ // Initialize the CachedState from |canonical_crypto_config| for the
+ // |canonical_server_id| as the initial CachedState for |server_id|. We will
+ // copy config data only if |canonical_crypto_config| has valid proof.
+ void InitializeFrom(const QuicServerId& server_id,
+ const QuicServerId& canonical_server_id,
+ QuicCryptoClientConfig* canonical_crypto_config);
+
+ // Adds |suffix| as a domain suffix for which the server's crypto config
+ // is expected to be shared among servers with the domain suffix. If a server
+ // matches this suffix, then the server config from another server with the
+ // suffix will be used to initialize the cached state for this server.
+ void AddCanonicalSuffix(const std::string& suffix);
+
+ // Saves the |user_agent_id| that will be passed in QUIC's CHLO message.
+ void set_user_agent_id(const std::string& user_agent_id) {
+ user_agent_id_ = user_agent_id;
+ }
+
+ // Returns the user_agent_id that will be provided in the client hello
+ // handshake message.
+ const std::string& user_agent_id() const { return user_agent_id_; }
+
+ void set_tls_signature_algorithms(std::string signature_algorithms) {
+ tls_signature_algorithms_ = std::move(signature_algorithms);
+ }
+
+ const absl::optional<std::string>& tls_signature_algorithms() const {
+ return tls_signature_algorithms_;
+ }
+
+ // Saves the |alpn| that will be passed in QUIC's CHLO message.
+ void set_alpn(const std::string& alpn) { alpn_ = alpn; }
+
+ // Saves the pre-shared key used during the handshake.
+ void set_pre_shared_key(absl::string_view psk) {
+ pre_shared_key_ = std::string(psk);
+ }
+
+ // Returns the pre-shared key used during the handshake.
+ const std::string& pre_shared_key() const { return pre_shared_key_; }
+
+ bool pad_inchoate_hello() const { return pad_inchoate_hello_; }
+ void set_pad_inchoate_hello(bool new_value) {
+ pad_inchoate_hello_ = new_value;
+ }
+
+ bool pad_full_hello() const { return pad_full_hello_; }
+ void set_pad_full_hello(bool new_value) { pad_full_hello_ = new_value; }
+
+ SessionCache* mutable_session_cache() { return session_cache_.get(); }
+
+ private:
+ // Sets the members to reasonable, default values.
+ void SetDefaults();
+
+ // CacheNewServerConfig checks for SCFG, STK, PROF, and CRT tags in |message|,
+ // verifies them, and stores them in the cached state if they validate.
+ // This is used on receipt of a REJ from a server, or when a server sends
+ // updated server config during a connection.
+ QuicErrorCode CacheNewServerConfig(
+ const CryptoHandshakeMessage& message, QuicWallTime now,
+ QuicTransportVersion version, absl::string_view chlo_hash,
+ const std::vector<std::string>& cached_certs, CachedState* cached,
+ std::string* error_details);
+
+ // If the suffix of the hostname in |server_id| is in |canonical_suffixes_|,
+ // then populate |cached| with the canonical cached state from
+ // |canonical_server_map_| for that suffix. Returns true if |cached| is
+ // initialized with canonical cached state.
+ bool PopulateFromCanonicalConfig(const QuicServerId& server_id,
+ CachedState* cached);
+
+ // cached_states_ maps from the server_id to the cached information about
+ // that server.
+ std::map<QuicServerId, std::unique_ptr<CachedState>> cached_states_;
+
+ // Contains a map of servers which could share the same server config. Map
+ // from a canonical host suffix/port/scheme to a representative server with
+ // the canonical suffix, which has a plausible set of initial certificates
+ // (or at least server public key).
+ std::map<QuicServerId, QuicServerId> canonical_server_map_;
+
+ // Contains list of suffixes (for exmaple ".c.youtube.com",
+ // ".googlevideo.com") of canonical hostnames.
+ std::vector<std::string> canonical_suffixes_;
+
+ std::unique_ptr<ProofVerifier> proof_verifier_;
+ std::unique_ptr<SessionCache> session_cache_;
+ std::unique_ptr<ClientProofSource> proof_source_;
+
+ bssl::UniquePtr<SSL_CTX> ssl_ctx_;
+
+ // The |user_agent_id_| passed in QUIC's CHLO message.
+ std::string user_agent_id_;
+
+ // The |alpn_| passed in QUIC's CHLO message.
+ std::string alpn_;
+
+ // If non-empty, the client will operate in the pre-shared key mode by
+ // incorporating |pre_shared_key_| into the key schedule.
+ std::string pre_shared_key_;
+
+ // If set, configure the client to use the specified signature algorithms, via
+ // SSL_set1_sigalgs_list. TLS only.
+ absl::optional<std::string> tls_signature_algorithms_;
+
+ // In QUIC, technically, client hello should be fully padded.
+ // However, fully padding on slow network connection (e.g. 50kbps) can add
+ // 150ms latency to one roundtrip. Therefore, you can disable padding of
+ // individual messages. It is recommend to leave at least one message in
+ // each direction fully padded (e.g. full CHLO and SHLO), but if you know
+ // the lower-bound MTU, you don't need to pad all of them (keep in mind that
+ // it's not OK to do it according to the standard).
+ //
+ // Also, if you disable padding, you must disable (change) the
+ // anti-amplification protection. You should only do so if you have some
+ // other means of verifying the client.
+ bool pad_inchoate_hello_ = true;
+ bool pad_full_hello_ = true;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_CLIENT_CONFIG_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_client_config_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_client_config_test.cc
new file mode 100644
index 00000000000..7556592f1c6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_client_config_test.cc
@@ -0,0 +1,550 @@
+// Copyright 2013 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 "quiche/quic/core/crypto/quic_crypto_client_config.h"
+
+#include <string>
+
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/crypto/proof_verifier.h"
+#include "quiche/quic/core/quic_server_id.h"
+#include "quiche/quic/core/quic_types.h"
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/quic/platform/api/quic_expect_bug.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/crypto_test_utils.h"
+#include "quiche/quic/test_tools/mock_random.h"
+#include "quiche/quic/test_tools/quic_test_utils.h"
+
+using testing::StartsWith;
+
+namespace quic {
+namespace test {
+namespace {
+
+class TestProofVerifyDetails : public ProofVerifyDetails {
+ ~TestProofVerifyDetails() override {}
+
+ // ProofVerifyDetails implementation
+ ProofVerifyDetails* Clone() const override {
+ return new TestProofVerifyDetails;
+ }
+};
+
+class OneServerIdFilter : public QuicCryptoClientConfig::ServerIdFilter {
+ public:
+ explicit OneServerIdFilter(const QuicServerId* server_id)
+ : server_id_(*server_id) {}
+
+ bool Matches(const QuicServerId& server_id) const override {
+ return server_id == server_id_;
+ }
+
+ private:
+ const QuicServerId server_id_;
+};
+
+class AllServerIdsFilter : public QuicCryptoClientConfig::ServerIdFilter {
+ public:
+ bool Matches(const QuicServerId& /*server_id*/) const override {
+ return true;
+ }
+};
+
+} // namespace
+
+class QuicCryptoClientConfigTest : public QuicTest {};
+
+TEST_F(QuicCryptoClientConfigTest, CachedState_IsEmpty) {
+ QuicCryptoClientConfig::CachedState state;
+ EXPECT_TRUE(state.IsEmpty());
+}
+
+TEST_F(QuicCryptoClientConfigTest, CachedState_IsComplete) {
+ QuicCryptoClientConfig::CachedState state;
+ EXPECT_FALSE(state.IsComplete(QuicWallTime::FromUNIXSeconds(0)));
+}
+
+TEST_F(QuicCryptoClientConfigTest, CachedState_GenerationCounter) {
+ QuicCryptoClientConfig::CachedState state;
+ EXPECT_EQ(0u, state.generation_counter());
+ state.SetProofInvalid();
+ EXPECT_EQ(1u, state.generation_counter());
+}
+
+TEST_F(QuicCryptoClientConfigTest, CachedState_SetProofVerifyDetails) {
+ QuicCryptoClientConfig::CachedState state;
+ EXPECT_TRUE(state.proof_verify_details() == nullptr);
+ ProofVerifyDetails* details = new TestProofVerifyDetails;
+ state.SetProofVerifyDetails(details);
+ EXPECT_EQ(details, state.proof_verify_details());
+}
+
+TEST_F(QuicCryptoClientConfigTest, CachedState_InitializeFrom) {
+ QuicCryptoClientConfig::CachedState state;
+ QuicCryptoClientConfig::CachedState other;
+ state.set_source_address_token("TOKEN");
+ // TODO(rch): Populate other fields of |state|.
+ other.InitializeFrom(state);
+ EXPECT_EQ(state.server_config(), other.server_config());
+ EXPECT_EQ(state.source_address_token(), other.source_address_token());
+ EXPECT_EQ(state.certs(), other.certs());
+ EXPECT_EQ(1u, other.generation_counter());
+}
+
+TEST_F(QuicCryptoClientConfigTest, InchoateChlo) {
+ QuicCryptoClientConfig::CachedState state;
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting());
+ config.set_user_agent_id("quic-tester");
+ config.set_alpn("hq");
+ quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters> params(
+ new QuicCryptoNegotiatedParameters);
+ CryptoHandshakeMessage msg;
+ QuicServerId server_id("www.google.com", 443, false);
+ MockRandom rand;
+ config.FillInchoateClientHello(server_id, QuicVersionMax(), &state, &rand,
+ /* demand_x509_proof= */ true, params, &msg);
+
+ QuicVersionLabel cver;
+ EXPECT_THAT(msg.GetVersionLabel(kVER, &cver), IsQuicNoError());
+ EXPECT_EQ(CreateQuicVersionLabel(QuicVersionMax()), cver);
+ absl::string_view proof_nonce;
+ EXPECT_TRUE(msg.GetStringPiece(kNONP, &proof_nonce));
+ EXPECT_EQ(std::string(32, 'r'), proof_nonce);
+ absl::string_view user_agent_id;
+ EXPECT_TRUE(msg.GetStringPiece(kUAID, &user_agent_id));
+ EXPECT_EQ("quic-tester", user_agent_id);
+ absl::string_view alpn;
+ EXPECT_TRUE(msg.GetStringPiece(kALPN, &alpn));
+ EXPECT_EQ("hq", alpn);
+ EXPECT_EQ(msg.minimum_size(), 1u);
+}
+
+TEST_F(QuicCryptoClientConfigTest, InchoateChloIsNotPadded) {
+ QuicCryptoClientConfig::CachedState state;
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting());
+ config.set_pad_inchoate_hello(false);
+ config.set_user_agent_id("quic-tester");
+ config.set_alpn("hq");
+ quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters> params(
+ new QuicCryptoNegotiatedParameters);
+ CryptoHandshakeMessage msg;
+ QuicServerId server_id("www.google.com", 443, false);
+ MockRandom rand;
+ config.FillInchoateClientHello(server_id, QuicVersionMax(), &state, &rand,
+ /* demand_x509_proof= */ true, params, &msg);
+
+ EXPECT_EQ(msg.minimum_size(), 1u);
+}
+
+// Make sure AES-GCM is the preferred encryption algorithm if it has hardware
+// acceleration.
+TEST_F(QuicCryptoClientConfigTest, PreferAesGcm) {
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting());
+ if (EVP_has_aes_hardware() == 1) {
+ EXPECT_EQ(kAESG, config.aead[0]);
+ } else {
+ EXPECT_EQ(kCC20, config.aead[0]);
+ }
+}
+
+TEST_F(QuicCryptoClientConfigTest, InchoateChloSecure) {
+ QuicCryptoClientConfig::CachedState state;
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting());
+ quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters> params(
+ new QuicCryptoNegotiatedParameters);
+ CryptoHandshakeMessage msg;
+ QuicServerId server_id("www.google.com", 443, false);
+ MockRandom rand;
+ config.FillInchoateClientHello(server_id, QuicVersionMax(), &state, &rand,
+ /* demand_x509_proof= */ true, params, &msg);
+
+ QuicTag pdmd;
+ EXPECT_THAT(msg.GetUint32(kPDMD, &pdmd), IsQuicNoError());
+ EXPECT_EQ(kX509, pdmd);
+ absl::string_view scid;
+ EXPECT_FALSE(msg.GetStringPiece(kSCID, &scid));
+}
+
+TEST_F(QuicCryptoClientConfigTest, InchoateChloSecureWithSCIDNoEXPY) {
+ // Test that a config with no EXPY is still valid when a non-zero
+ // expiry time is passed in.
+ QuicCryptoClientConfig::CachedState state;
+ CryptoHandshakeMessage scfg;
+ scfg.set_tag(kSCFG);
+ scfg.SetStringPiece(kSCID, "12345678");
+ std::string details;
+ QuicWallTime now = QuicWallTime::FromUNIXSeconds(1);
+ QuicWallTime expiry = QuicWallTime::FromUNIXSeconds(2);
+ state.SetServerConfig(scfg.GetSerialized().AsStringPiece(), now, expiry,
+ &details);
+ EXPECT_FALSE(state.IsEmpty());
+
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting());
+ quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters> params(
+ new QuicCryptoNegotiatedParameters);
+ CryptoHandshakeMessage msg;
+ QuicServerId server_id("www.google.com", 443, false);
+ MockRandom rand;
+ config.FillInchoateClientHello(server_id, QuicVersionMax(), &state, &rand,
+ /* demand_x509_proof= */ true, params, &msg);
+
+ absl::string_view scid;
+ EXPECT_TRUE(msg.GetStringPiece(kSCID, &scid));
+ EXPECT_EQ("12345678", scid);
+}
+
+TEST_F(QuicCryptoClientConfigTest, InchoateChloSecureWithSCID) {
+ QuicCryptoClientConfig::CachedState state;
+ CryptoHandshakeMessage scfg;
+ scfg.set_tag(kSCFG);
+ uint64_t future = 1;
+ scfg.SetValue(kEXPY, future);
+ scfg.SetStringPiece(kSCID, "12345678");
+ std::string details;
+ state.SetServerConfig(scfg.GetSerialized().AsStringPiece(),
+ QuicWallTime::FromUNIXSeconds(1),
+ QuicWallTime::FromUNIXSeconds(0), &details);
+ EXPECT_FALSE(state.IsEmpty());
+
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting());
+ quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters> params(
+ new QuicCryptoNegotiatedParameters);
+ CryptoHandshakeMessage msg;
+ QuicServerId server_id("www.google.com", 443, false);
+ MockRandom rand;
+ config.FillInchoateClientHello(server_id, QuicVersionMax(), &state, &rand,
+ /* demand_x509_proof= */ true, params, &msg);
+
+ absl::string_view scid;
+ EXPECT_TRUE(msg.GetStringPiece(kSCID, &scid));
+ EXPECT_EQ("12345678", scid);
+}
+
+TEST_F(QuicCryptoClientConfigTest, FillClientHello) {
+ QuicCryptoClientConfig::CachedState state;
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting());
+ quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters> params(
+ new QuicCryptoNegotiatedParameters);
+ QuicConnectionId kConnectionId = TestConnectionId(1234);
+ std::string error_details;
+ MockRandom rand;
+ CryptoHandshakeMessage chlo;
+ QuicServerId server_id("www.google.com", 443, false);
+ config.FillClientHello(server_id, kConnectionId, QuicVersionMax(),
+ QuicVersionMax(), &state, QuicWallTime::Zero(), &rand,
+ params, &chlo, &error_details);
+
+ // Verify that the version label has been set correctly in the CHLO.
+ QuicVersionLabel cver;
+ EXPECT_THAT(chlo.GetVersionLabel(kVER, &cver), IsQuicNoError());
+ EXPECT_EQ(CreateQuicVersionLabel(QuicVersionMax()), cver);
+}
+
+TEST_F(QuicCryptoClientConfigTest, FillClientHelloNoPadding) {
+ QuicCryptoClientConfig::CachedState state;
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting());
+ config.set_pad_full_hello(false);
+ quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters> params(
+ new QuicCryptoNegotiatedParameters);
+ QuicConnectionId kConnectionId = TestConnectionId(1234);
+ std::string error_details;
+ MockRandom rand;
+ CryptoHandshakeMessage chlo;
+ QuicServerId server_id("www.google.com", 443, false);
+ config.FillClientHello(server_id, kConnectionId, QuicVersionMax(),
+ QuicVersionMax(), &state, QuicWallTime::Zero(), &rand,
+ params, &chlo, &error_details);
+
+ // Verify that the version label has been set correctly in the CHLO.
+ QuicVersionLabel cver;
+ EXPECT_THAT(chlo.GetVersionLabel(kVER, &cver), IsQuicNoError());
+ EXPECT_EQ(CreateQuicVersionLabel(QuicVersionMax()), cver);
+ EXPECT_EQ(chlo.minimum_size(), 1u);
+}
+
+TEST_F(QuicCryptoClientConfigTest, ProcessServerDowngradeAttack) {
+ ParsedQuicVersionVector supported_versions = AllSupportedVersions();
+ if (supported_versions.size() == 1) {
+ // No downgrade attack is possible if the client only supports one version.
+ return;
+ }
+
+ ParsedQuicVersionVector supported_version_vector;
+ for (size_t i = supported_versions.size(); i > 0; --i) {
+ supported_version_vector.push_back(supported_versions[i - 1]);
+ }
+
+ CryptoHandshakeMessage msg;
+ msg.set_tag(kSHLO);
+ msg.SetVersionVector(kVER, supported_version_vector);
+
+ QuicCryptoClientConfig::CachedState cached;
+ quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters>
+ out_params(new QuicCryptoNegotiatedParameters);
+ std::string error;
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting());
+ EXPECT_THAT(config.ProcessServerHello(
+ msg, EmptyQuicConnectionId(), supported_versions.front(),
+ supported_versions, &cached, out_params, &error),
+ IsError(QUIC_VERSION_NEGOTIATION_MISMATCH));
+ EXPECT_THAT(error, StartsWith("Downgrade attack detected: ServerVersions"));
+}
+
+TEST_F(QuicCryptoClientConfigTest, InitializeFrom) {
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting());
+ QuicServerId canonical_server_id("www.google.com", 443, false);
+ QuicCryptoClientConfig::CachedState* state =
+ config.LookupOrCreate(canonical_server_id);
+ // TODO(rch): Populate other fields of |state|.
+ state->set_source_address_token("TOKEN");
+ state->SetProofValid();
+
+ QuicServerId other_server_id("mail.google.com", 443, false);
+ config.InitializeFrom(other_server_id, canonical_server_id, &config);
+ QuicCryptoClientConfig::CachedState* other =
+ config.LookupOrCreate(other_server_id);
+
+ EXPECT_EQ(state->server_config(), other->server_config());
+ EXPECT_EQ(state->source_address_token(), other->source_address_token());
+ EXPECT_EQ(state->certs(), other->certs());
+ EXPECT_EQ(1u, other->generation_counter());
+}
+
+TEST_F(QuicCryptoClientConfigTest, Canonical) {
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting());
+ config.AddCanonicalSuffix(".google.com");
+ QuicServerId canonical_id1("www.google.com", 443, false);
+ QuicServerId canonical_id2("mail.google.com", 443, false);
+ QuicCryptoClientConfig::CachedState* state =
+ config.LookupOrCreate(canonical_id1);
+ // TODO(rch): Populate other fields of |state|.
+ state->set_source_address_token("TOKEN");
+ state->SetProofValid();
+
+ QuicCryptoClientConfig::CachedState* other =
+ config.LookupOrCreate(canonical_id2);
+
+ EXPECT_TRUE(state->IsEmpty());
+ EXPECT_EQ(state->server_config(), other->server_config());
+ EXPECT_EQ(state->source_address_token(), other->source_address_token());
+ EXPECT_EQ(state->certs(), other->certs());
+ EXPECT_EQ(1u, other->generation_counter());
+
+ QuicServerId different_id("mail.google.org", 443, false);
+ EXPECT_TRUE(config.LookupOrCreate(different_id)->IsEmpty());
+}
+
+TEST_F(QuicCryptoClientConfigTest, CanonicalNotUsedIfNotValid) {
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting());
+ config.AddCanonicalSuffix(".google.com");
+ QuicServerId canonical_id1("www.google.com", 443, false);
+ QuicServerId canonical_id2("mail.google.com", 443, false);
+ QuicCryptoClientConfig::CachedState* state =
+ config.LookupOrCreate(canonical_id1);
+ // TODO(rch): Populate other fields of |state|.
+ state->set_source_address_token("TOKEN");
+
+ // Do not set the proof as valid, and check that it is not used
+ // as a canonical entry.
+ EXPECT_TRUE(config.LookupOrCreate(canonical_id2)->IsEmpty());
+}
+
+TEST_F(QuicCryptoClientConfigTest, ClearCachedStates) {
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting());
+
+ // Create two states on different origins.
+ struct TestCase {
+ TestCase(const std::string& host, QuicCryptoClientConfig* config)
+ : server_id(host, 443, false),
+ state(config->LookupOrCreate(server_id)) {
+ // TODO(rch): Populate other fields of |state|.
+ CryptoHandshakeMessage scfg;
+ scfg.set_tag(kSCFG);
+ uint64_t future = 1;
+ scfg.SetValue(kEXPY, future);
+ scfg.SetStringPiece(kSCID, "12345678");
+ std::string details;
+ state->SetServerConfig(scfg.GetSerialized().AsStringPiece(),
+ QuicWallTime::FromUNIXSeconds(0),
+ QuicWallTime::FromUNIXSeconds(future), &details);
+
+ std::vector<std::string> certs(1);
+ certs[0] = "Hello Cert for " + host;
+ state->SetProof(certs, "cert_sct", "chlo_hash", "signature");
+ state->set_source_address_token("TOKEN");
+ state->SetProofValid();
+
+ // The generation counter starts at 2, because proof has been once
+ // invalidated in SetServerConfig().
+ EXPECT_EQ(2u, state->generation_counter());
+ }
+
+ QuicServerId server_id;
+ QuicCryptoClientConfig::CachedState* state;
+ } test_cases[] = {TestCase("www.google.com", &config),
+ TestCase("www.example.com", &config)};
+
+ // Verify LookupOrCreate returns the same data.
+ for (const TestCase& test_case : test_cases) {
+ QuicCryptoClientConfig::CachedState* other =
+ config.LookupOrCreate(test_case.server_id);
+ EXPECT_EQ(test_case.state, other);
+ EXPECT_EQ(2u, other->generation_counter());
+ }
+
+ // Clear the cached state for www.google.com.
+ OneServerIdFilter google_com_filter(&test_cases[0].server_id);
+ config.ClearCachedStates(google_com_filter);
+
+ // Verify LookupOrCreate doesn't have any data for google.com.
+ QuicCryptoClientConfig::CachedState* cleared_cache =
+ config.LookupOrCreate(test_cases[0].server_id);
+
+ EXPECT_EQ(test_cases[0].state, cleared_cache);
+ EXPECT_FALSE(cleared_cache->proof_valid());
+ EXPECT_TRUE(cleared_cache->server_config().empty());
+ EXPECT_TRUE(cleared_cache->certs().empty());
+ EXPECT_TRUE(cleared_cache->cert_sct().empty());
+ EXPECT_TRUE(cleared_cache->signature().empty());
+ EXPECT_EQ(3u, cleared_cache->generation_counter());
+
+ // But it still does for www.example.com.
+ QuicCryptoClientConfig::CachedState* existing_cache =
+ config.LookupOrCreate(test_cases[1].server_id);
+
+ EXPECT_EQ(test_cases[1].state, existing_cache);
+ EXPECT_TRUE(existing_cache->proof_valid());
+ EXPECT_FALSE(existing_cache->server_config().empty());
+ EXPECT_FALSE(existing_cache->certs().empty());
+ EXPECT_FALSE(existing_cache->cert_sct().empty());
+ EXPECT_FALSE(existing_cache->signature().empty());
+ EXPECT_EQ(2u, existing_cache->generation_counter());
+
+ // Clear all cached states.
+ AllServerIdsFilter all_server_ids;
+ config.ClearCachedStates(all_server_ids);
+
+ // The data for www.example.com should now be cleared as well.
+ cleared_cache = config.LookupOrCreate(test_cases[1].server_id);
+
+ EXPECT_EQ(test_cases[1].state, cleared_cache);
+ EXPECT_FALSE(cleared_cache->proof_valid());
+ EXPECT_TRUE(cleared_cache->server_config().empty());
+ EXPECT_TRUE(cleared_cache->certs().empty());
+ EXPECT_TRUE(cleared_cache->cert_sct().empty());
+ EXPECT_TRUE(cleared_cache->signature().empty());
+ EXPECT_EQ(3u, cleared_cache->generation_counter());
+}
+
+TEST_F(QuicCryptoClientConfigTest, ProcessReject) {
+ CryptoHandshakeMessage rej;
+ crypto_test_utils::FillInDummyReject(&rej);
+
+ // Now process the rejection.
+ QuicCryptoClientConfig::CachedState cached;
+ quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters>
+ out_params(new QuicCryptoNegotiatedParameters);
+ std::string error;
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting());
+ EXPECT_THAT(
+ config.ProcessRejection(
+ rej, QuicWallTime::FromUNIXSeconds(0),
+ AllSupportedVersionsWithQuicCrypto().front().transport_version, "",
+ &cached, out_params, &error),
+ IsQuicNoError());
+}
+
+TEST_F(QuicCryptoClientConfigTest, ProcessRejectWithLongTTL) {
+ CryptoHandshakeMessage rej;
+ crypto_test_utils::FillInDummyReject(&rej);
+ QuicTime::Delta one_week = QuicTime::Delta::FromSeconds(kNumSecondsPerWeek);
+ int64_t long_ttl = 3 * one_week.ToSeconds();
+ rej.SetValue(kSTTL, long_ttl);
+
+ // Now process the rejection.
+ QuicCryptoClientConfig::CachedState cached;
+ quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters>
+ out_params(new QuicCryptoNegotiatedParameters);
+ std::string error;
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting());
+ EXPECT_THAT(
+ config.ProcessRejection(
+ rej, QuicWallTime::FromUNIXSeconds(0),
+ AllSupportedVersionsWithQuicCrypto().front().transport_version, "",
+ &cached, out_params, &error),
+ IsQuicNoError());
+ cached.SetProofValid();
+ EXPECT_FALSE(cached.IsComplete(QuicWallTime::FromUNIXSeconds(long_ttl)));
+ EXPECT_FALSE(
+ cached.IsComplete(QuicWallTime::FromUNIXSeconds(one_week.ToSeconds())));
+ EXPECT_TRUE(cached.IsComplete(
+ QuicWallTime::FromUNIXSeconds(one_week.ToSeconds() - 1)));
+}
+
+TEST_F(QuicCryptoClientConfigTest, ServerNonceinSHLO) {
+ // Test that the server must include a nonce in the SHLO.
+ CryptoHandshakeMessage msg;
+ msg.set_tag(kSHLO);
+ // Choose the latest version.
+ ParsedQuicVersionVector supported_versions;
+ ParsedQuicVersion version = AllSupportedVersions().front();
+ supported_versions.push_back(version);
+ msg.SetVersionVector(kVER, supported_versions);
+
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting());
+ QuicCryptoClientConfig::CachedState cached;
+ quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters>
+ out_params(new QuicCryptoNegotiatedParameters);
+ std::string error_details;
+ EXPECT_THAT(config.ProcessServerHello(msg, EmptyQuicConnectionId(), version,
+ supported_versions, &cached, out_params,
+ &error_details),
+ IsError(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER));
+ EXPECT_EQ("server hello missing server nonce", error_details);
+}
+
+// Test that PopulateFromCanonicalConfig() handles the case of multiple entries
+// in |canonical_server_map_|.
+TEST_F(QuicCryptoClientConfigTest, MultipleCanonicalEntries) {
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting());
+ config.AddCanonicalSuffix(".google.com");
+ QuicServerId canonical_server_id1("www.google.com", 443, false);
+ QuicCryptoClientConfig::CachedState* state1 =
+ config.LookupOrCreate(canonical_server_id1);
+
+ CryptoHandshakeMessage scfg;
+ scfg.set_tag(kSCFG);
+ scfg.SetStringPiece(kSCID, "12345678");
+ std::string details;
+ QuicWallTime now = QuicWallTime::FromUNIXSeconds(1);
+ QuicWallTime expiry = QuicWallTime::FromUNIXSeconds(2);
+ state1->SetServerConfig(scfg.GetSerialized().AsStringPiece(), now, expiry,
+ &details);
+ state1->set_source_address_token("TOKEN");
+ state1->SetProofValid();
+ EXPECT_FALSE(state1->IsEmpty());
+
+ // This will have the same |suffix_server_id| as |canonical_server_id1|,
+ // therefore |*state2| will be initialized from |*state1|.
+ QuicServerId canonical_server_id2("mail.google.com", 443, false);
+ QuicCryptoClientConfig::CachedState* state2 =
+ config.LookupOrCreate(canonical_server_id2);
+ EXPECT_FALSE(state2->IsEmpty());
+ const CryptoHandshakeMessage* const scfg2 = state2->GetServerConfig();
+ ASSERT_TRUE(scfg2);
+ EXPECT_EQ(kSCFG, scfg2->tag());
+
+ // With a different |suffix_server_id|, this will return an empty CachedState.
+ config.AddCanonicalSuffix(".example.com");
+ QuicServerId canonical_server_id3("www.example.com", 443, false);
+ QuicCryptoClientConfig::CachedState* state3 =
+ config.LookupOrCreate(canonical_server_id3);
+ EXPECT_TRUE(state3->IsEmpty());
+ const CryptoHandshakeMessage* const scfg3 = state3->GetServerConfig();
+ EXPECT_FALSE(scfg3);
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_proof.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_proof.cc
new file mode 100644
index 00000000000..a449c267edb
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_proof.cc
@@ -0,0 +1,12 @@
+// Copyright 2016 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 "quiche/quic/core/crypto/quic_crypto_proof.h"
+
+namespace quic {
+
+QuicCryptoProof::QuicCryptoProof()
+ : send_expect_ct_header(false), cert_matched_sni(false) {}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_proof.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_proof.h
new file mode 100644
index 00000000000..579d2640c81
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_proof.h
@@ -0,0 +1,32 @@
+// Copyright 2016 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_PROOF_H_
+#define QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_PROOF_H_
+
+#include <string>
+
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// Contains the crypto-related data provided by ProofSource
+struct QUIC_EXPORT_PRIVATE QuicCryptoProof {
+ QuicCryptoProof();
+
+ // Signature generated by ProofSource
+ std::string signature;
+ // SCTList (RFC6962) to be sent to the client, if it supports receiving it.
+ std::string leaf_cert_scts;
+ // Should the Expect-CT header be sent on the connection where the
+ // certificate is used.
+ bool send_expect_ct_header;
+ // Did the selected leaf certificate contain a SubjectAltName that included
+ // the requested SNI.
+ bool cert_matched_sni;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_PROOF_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_server_config.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_server_config.cc
new file mode 100644
index 00000000000..11ccc2f23bf
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_server_config.cc
@@ -0,0 +1,1893 @@
+// Copyright 2013 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 "quiche/quic/core/crypto/quic_crypto_server_config.h"
+
+#include <algorithm>
+#include <cstdlib>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "absl/base/attributes.h"
+#include "absl/strings/escaping.h"
+#include "absl/strings/str_format.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
+#include "openssl/sha.h"
+#include "openssl/ssl.h"
+#include "quiche/quic/core/crypto/aes_128_gcm_12_decrypter.h"
+#include "quiche/quic/core/crypto/aes_128_gcm_12_encrypter.h"
+#include "quiche/quic/core/crypto/cert_compressor.h"
+#include "quiche/quic/core/crypto/certificate_view.h"
+#include "quiche/quic/core/crypto/chacha20_poly1305_encrypter.h"
+#include "quiche/quic/core/crypto/channel_id.h"
+#include "quiche/quic/core/crypto/crypto_framer.h"
+#include "quiche/quic/core/crypto/crypto_handshake_message.h"
+#include "quiche/quic/core/crypto/crypto_utils.h"
+#include "quiche/quic/core/crypto/curve25519_key_exchange.h"
+#include "quiche/quic/core/crypto/key_exchange.h"
+#include "quiche/quic/core/crypto/p256_key_exchange.h"
+#include "quiche/quic/core/crypto/proof_source.h"
+#include "quiche/quic/core/crypto/quic_decrypter.h"
+#include "quiche/quic/core/crypto/quic_encrypter.h"
+#include "quiche/quic/core/crypto/quic_hkdf.h"
+#include "quiche/quic/core/crypto/quic_random.h"
+#include "quiche/quic/core/crypto/tls_server_connection.h"
+#include "quiche/quic/core/proto/crypto_server_config_proto.h"
+#include "quiche/quic/core/proto/source_address_token_proto.h"
+#include "quiche/quic/core/quic_clock.h"
+#include "quiche/quic/core/quic_connection_context.h"
+#include "quiche/quic/core/quic_packets.h"
+#include "quiche/quic/core/quic_socket_address_coder.h"
+#include "quiche/quic/core/quic_types.h"
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/quic/platform/api/quic_bug_tracker.h"
+#include "quiche/quic/platform/api/quic_flag_utils.h"
+#include "quiche/quic/platform/api/quic_flags.h"
+#include "quiche/quic/platform/api/quic_hostname_utils.h"
+#include "quiche/quic/platform/api/quic_logging.h"
+#include "quiche/quic/platform/api/quic_socket_address.h"
+#include "quiche/quic/platform/api/quic_testvalue.h"
+#include "quiche/common/platform/api/quiche_reference_counted.h"
+
+namespace quic {
+
+namespace {
+
+// kMultiplier is the multiple of the CHLO message size that a REJ message
+// must stay under when the client doesn't present a valid source-address
+// token. This is used to protect QUIC from amplification attacks.
+// TODO(rch): Reduce this to 2 again once b/25933682 is fixed.
+const size_t kMultiplier = 3;
+
+const int kMaxTokenAddresses = 4;
+
+std::string DeriveSourceAddressTokenKey(
+ absl::string_view source_address_token_secret) {
+ QuicHKDF hkdf(source_address_token_secret, absl::string_view() /* no salt */,
+ "QUIC source address token key",
+ CryptoSecretBoxer::GetKeySize(), 0 /* no fixed IV needed */,
+ 0 /* no subkey secret */);
+ return std::string(hkdf.server_write_key());
+}
+
+// Default source for creating KeyExchange objects.
+class DefaultKeyExchangeSource : public KeyExchangeSource {
+ public:
+ DefaultKeyExchangeSource() = default;
+ ~DefaultKeyExchangeSource() override = default;
+
+ std::unique_ptr<AsynchronousKeyExchange> Create(
+ std::string /*server_config_id*/, bool /* is_fallback */, QuicTag type,
+ absl::string_view private_key) override {
+ if (private_key.empty()) {
+ QUIC_LOG(WARNING) << "Server config contains key exchange method without "
+ "corresponding private key of type "
+ << QuicTagToString(type);
+ return nullptr;
+ }
+
+ std::unique_ptr<SynchronousKeyExchange> ka =
+ CreateLocalSynchronousKeyExchange(type, private_key);
+ if (!ka) {
+ QUIC_LOG(WARNING) << "Failed to create key exchange method of type "
+ << QuicTagToString(type);
+ }
+ return ka;
+ }
+};
+
+// Returns true if the PDMD field from the client hello demands an X509
+// certificate.
+bool ClientDemandsX509Proof(const CryptoHandshakeMessage& client_hello) {
+ QuicTagVector their_proof_demands;
+
+ if (client_hello.GetTaglist(kPDMD, &their_proof_demands) != QUIC_NO_ERROR) {
+ return false;
+ }
+
+ for (const QuicTag tag : their_proof_demands) {
+ if (tag == kX509) {
+ return true;
+ }
+ }
+ return false;
+}
+
+std::string FormatCryptoHandshakeMessageForTrace(
+ const CryptoHandshakeMessage* message) {
+ if (message == nullptr) {
+ return "<null message>";
+ }
+
+ std::string s = QuicTagToString(message->tag());
+
+ // Append the reasons for REJ.
+ if (const auto it = message->tag_value_map().find(kRREJ);
+ it != message->tag_value_map().end()) {
+ const std::string& value = it->second;
+ // The value is a vector of uint32_t(s).
+ if (value.size() % sizeof(uint32_t) == 0) {
+ absl::StrAppend(&s, " RREJ:[");
+ // Append comma-separated list of reasons to |s|.
+ for (size_t j = 0; j < value.size(); j += sizeof(uint32_t)) {
+ uint32_t reason;
+ memcpy(&reason, value.data() + j, sizeof(reason));
+ if (j > 0) {
+ absl::StrAppend(&s, ",");
+ }
+ absl::StrAppend(&s, CryptoUtils::HandshakeFailureReasonToString(
+ static_cast<HandshakeFailureReason>(reason)));
+ }
+ absl::StrAppend(&s, "]");
+ } else {
+ absl::StrAppendFormat(&s, " RREJ:[unexpected length:%u]", value.size());
+ }
+ }
+
+ return s;
+}
+
+} // namespace
+
+// static
+std::unique_ptr<KeyExchangeSource> KeyExchangeSource::Default() {
+ return std::make_unique<DefaultKeyExchangeSource>();
+}
+
+class ValidateClientHelloHelper {
+ public:
+ // Note: stores a pointer to a unique_ptr, and std::moves the unique_ptr when
+ // ValidationComplete is called.
+ ValidateClientHelloHelper(
+ quiche::QuicheReferenceCountedPointer<
+ ValidateClientHelloResultCallback::Result>
+ result,
+ std::unique_ptr<ValidateClientHelloResultCallback>* done_cb)
+ : result_(std::move(result)), done_cb_(done_cb) {}
+ ValidateClientHelloHelper(const ValidateClientHelloHelper&) = delete;
+ ValidateClientHelloHelper& operator=(const ValidateClientHelloHelper&) =
+ delete;
+
+ ~ValidateClientHelloHelper() {
+ QUIC_BUG_IF(quic_bug_12963_1, done_cb_ != nullptr)
+ << "Deleting ValidateClientHelloHelper with a pending callback.";
+ }
+
+ void ValidationComplete(
+ QuicErrorCode error_code, const char* error_details,
+ std::unique_ptr<ProofSource::Details> proof_source_details) {
+ result_->error_code = error_code;
+ result_->error_details = error_details;
+ (*done_cb_)->Run(std::move(result_), std::move(proof_source_details));
+ DetachCallback();
+ }
+
+ void DetachCallback() {
+ QUIC_BUG_IF(quic_bug_10630_1, done_cb_ == nullptr)
+ << "Callback already detached.";
+ done_cb_ = nullptr;
+ }
+
+ private:
+ quiche::QuicheReferenceCountedPointer<
+ ValidateClientHelloResultCallback::Result>
+ result_;
+ std::unique_ptr<ValidateClientHelloResultCallback>* done_cb_;
+};
+
+// static
+const char QuicCryptoServerConfig::TESTING[] = "secret string for testing";
+
+ClientHelloInfo::ClientHelloInfo(const QuicIpAddress& in_client_ip,
+ QuicWallTime in_now)
+ : client_ip(in_client_ip), now(in_now), valid_source_address_token(false) {}
+
+ClientHelloInfo::ClientHelloInfo(const ClientHelloInfo& other) = default;
+
+ClientHelloInfo::~ClientHelloInfo() {}
+
+PrimaryConfigChangedCallback::PrimaryConfigChangedCallback() {}
+
+PrimaryConfigChangedCallback::~PrimaryConfigChangedCallback() {}
+
+ValidateClientHelloResultCallback::Result::Result(
+ const CryptoHandshakeMessage& in_client_hello, QuicIpAddress in_client_ip,
+ QuicWallTime in_now)
+ : client_hello(in_client_hello),
+ info(in_client_ip, in_now),
+ error_code(QUIC_NO_ERROR) {}
+
+ValidateClientHelloResultCallback::Result::~Result() {}
+
+ValidateClientHelloResultCallback::ValidateClientHelloResultCallback() {}
+
+ValidateClientHelloResultCallback::~ValidateClientHelloResultCallback() {}
+
+ProcessClientHelloResultCallback::ProcessClientHelloResultCallback() {}
+
+ProcessClientHelloResultCallback::~ProcessClientHelloResultCallback() {}
+
+QuicCryptoServerConfig::ConfigOptions::ConfigOptions()
+ : expiry_time(QuicWallTime::Zero()),
+ channel_id_enabled(false),
+ p256(false) {}
+
+QuicCryptoServerConfig::ConfigOptions::ConfigOptions(
+ const ConfigOptions& other) = default;
+
+QuicCryptoServerConfig::ConfigOptions::~ConfigOptions() {}
+
+QuicCryptoServerConfig::ProcessClientHelloContext::
+ ~ProcessClientHelloContext() {
+ if (done_cb_ != nullptr) {
+ QUIC_LOG(WARNING)
+ << "Deleting ProcessClientHelloContext with a pending callback.";
+ }
+}
+
+void QuicCryptoServerConfig::ProcessClientHelloContext::Fail(
+ QuicErrorCode error, const std::string& error_details) {
+ QUIC_TRACEPRINTF("ProcessClientHello failed: error=%s, details=%s",
+ QuicErrorCodeToString(error), error_details);
+ done_cb_->Run(error, error_details, nullptr, nullptr, nullptr);
+ done_cb_ = nullptr;
+}
+
+void QuicCryptoServerConfig::ProcessClientHelloContext::Succeed(
+ std::unique_ptr<CryptoHandshakeMessage> message,
+ std::unique_ptr<DiversificationNonce> diversification_nonce,
+ std::unique_ptr<ProofSource::Details> proof_source_details) {
+ QUIC_TRACEPRINTF("ProcessClientHello succeeded: %s",
+ FormatCryptoHandshakeMessageForTrace(message.get()));
+
+ done_cb_->Run(QUIC_NO_ERROR, std::string(), std::move(message),
+ std::move(diversification_nonce),
+ std::move(proof_source_details));
+ done_cb_ = nullptr;
+}
+
+QuicCryptoServerConfig::QuicCryptoServerConfig(
+ absl::string_view source_address_token_secret,
+ QuicRandom* server_nonce_entropy, std::unique_ptr<ProofSource> proof_source,
+ std::unique_ptr<KeyExchangeSource> key_exchange_source)
+ : replay_protection_(true),
+ chlo_multiplier_(kMultiplier),
+ configs_lock_(),
+ primary_config_(nullptr),
+ next_config_promotion_time_(QuicWallTime::Zero()),
+ proof_source_(std::move(proof_source)),
+ key_exchange_source_(std::move(key_exchange_source)),
+ ssl_ctx_(TlsServerConnection::CreateSslCtx(proof_source_.get())),
+ source_address_token_future_secs_(3600),
+ source_address_token_lifetime_secs_(86400),
+ enable_serving_sct_(false),
+ rejection_observer_(nullptr),
+ pad_rej_(true),
+ pad_shlo_(true),
+ validate_chlo_size_(true),
+ validate_source_address_token_(true) {
+ QUICHE_DCHECK(proof_source_.get());
+ source_address_token_boxer_.SetKeys(
+ {DeriveSourceAddressTokenKey(source_address_token_secret)});
+
+ // Generate a random key and orbit for server nonces.
+ server_nonce_entropy->RandBytes(server_nonce_orbit_,
+ sizeof(server_nonce_orbit_));
+ const size_t key_size = server_nonce_boxer_.GetKeySize();
+ std::unique_ptr<uint8_t[]> key_bytes(new uint8_t[key_size]);
+ server_nonce_entropy->RandBytes(key_bytes.get(), key_size);
+
+ server_nonce_boxer_.SetKeys(
+ {std::string(reinterpret_cast<char*>(key_bytes.get()), key_size)});
+}
+
+QuicCryptoServerConfig::~QuicCryptoServerConfig() {}
+
+// static
+QuicServerConfigProtobuf QuicCryptoServerConfig::GenerateConfig(
+ QuicRandom* rand, const QuicClock* clock, const ConfigOptions& options) {
+ CryptoHandshakeMessage msg;
+
+ const std::string curve25519_private_key =
+ Curve25519KeyExchange::NewPrivateKey(rand);
+ std::unique_ptr<Curve25519KeyExchange> curve25519 =
+ Curve25519KeyExchange::New(curve25519_private_key);
+ absl::string_view curve25519_public_value = curve25519->public_value();
+
+ std::string encoded_public_values;
+ // First three bytes encode the length of the public value.
+ QUICHE_DCHECK_LT(curve25519_public_value.size(), (1U << 24));
+ encoded_public_values.push_back(
+ static_cast<char>(curve25519_public_value.size()));
+ encoded_public_values.push_back(
+ static_cast<char>(curve25519_public_value.size() >> 8));
+ encoded_public_values.push_back(
+ static_cast<char>(curve25519_public_value.size() >> 16));
+ encoded_public_values.append(curve25519_public_value.data(),
+ curve25519_public_value.size());
+
+ std::string p256_private_key;
+ if (options.p256) {
+ p256_private_key = P256KeyExchange::NewPrivateKey();
+ std::unique_ptr<P256KeyExchange> p256(
+ P256KeyExchange::New(p256_private_key));
+ absl::string_view p256_public_value = p256->public_value();
+
+ QUICHE_DCHECK_LT(p256_public_value.size(), (1U << 24));
+ encoded_public_values.push_back(
+ static_cast<char>(p256_public_value.size()));
+ encoded_public_values.push_back(
+ static_cast<char>(p256_public_value.size() >> 8));
+ encoded_public_values.push_back(
+ static_cast<char>(p256_public_value.size() >> 16));
+ encoded_public_values.append(p256_public_value.data(),
+ p256_public_value.size());
+ }
+
+ msg.set_tag(kSCFG);
+ if (options.p256) {
+ msg.SetVector(kKEXS, QuicTagVector{kC255, kP256});
+ } else {
+ msg.SetVector(kKEXS, QuicTagVector{kC255});
+ }
+ msg.SetVector(kAEAD, QuicTagVector{kAESG, kCC20});
+ msg.SetStringPiece(kPUBS, encoded_public_values);
+
+ if (options.expiry_time.IsZero()) {
+ const QuicWallTime now = clock->WallNow();
+ const QuicWallTime expiry = now.Add(QuicTime::Delta::FromSeconds(
+ 60 * 60 * 24 * 180 /* 180 days, ~six months */));
+ const uint64_t expiry_seconds = expiry.ToUNIXSeconds();
+ msg.SetValue(kEXPY, expiry_seconds);
+ } else {
+ msg.SetValue(kEXPY, options.expiry_time.ToUNIXSeconds());
+ }
+
+ char orbit_bytes[kOrbitSize];
+ if (options.orbit.size() == sizeof(orbit_bytes)) {
+ memcpy(orbit_bytes, options.orbit.data(), sizeof(orbit_bytes));
+ } else {
+ QUICHE_DCHECK(options.orbit.empty());
+ rand->RandBytes(orbit_bytes, sizeof(orbit_bytes));
+ }
+ msg.SetStringPiece(kORBT,
+ absl::string_view(orbit_bytes, sizeof(orbit_bytes)));
+
+ if (options.channel_id_enabled) {
+ msg.SetVector(kPDMD, QuicTagVector{kCHID});
+ }
+
+ if (options.id.empty()) {
+ // We need to ensure that the SCID changes whenever the server config does
+ // thus we make it a hash of the rest of the server config.
+ std::unique_ptr<QuicData> serialized =
+ CryptoFramer::ConstructHandshakeMessage(msg);
+
+ uint8_t scid_bytes[SHA256_DIGEST_LENGTH];
+ SHA256(reinterpret_cast<const uint8_t*>(serialized->data()),
+ serialized->length(), scid_bytes);
+ // The SCID is a truncated SHA-256 digest.
+ static_assert(16 <= SHA256_DIGEST_LENGTH, "SCID length too high.");
+ msg.SetStringPiece(
+ kSCID,
+ absl::string_view(reinterpret_cast<const char*>(scid_bytes), 16));
+ } else {
+ msg.SetStringPiece(kSCID, options.id);
+ }
+ // Don't put new tags below this point. The SCID generation should hash over
+ // everything but itself and so extra tags should be added prior to the
+ // preceding if block.
+
+ std::unique_ptr<QuicData> serialized =
+ CryptoFramer::ConstructHandshakeMessage(msg);
+
+ QuicServerConfigProtobuf config;
+ config.set_config(std::string(serialized->AsStringPiece()));
+ QuicServerConfigProtobuf::PrivateKey* curve25519_key = config.add_key();
+ curve25519_key->set_tag(kC255);
+ curve25519_key->set_private_key(curve25519_private_key);
+
+ if (options.p256) {
+ QuicServerConfigProtobuf::PrivateKey* p256_key = config.add_key();
+ p256_key->set_tag(kP256);
+ p256_key->set_private_key(p256_private_key);
+ }
+
+ return config;
+}
+
+std::unique_ptr<CryptoHandshakeMessage> QuicCryptoServerConfig::AddConfig(
+ const QuicServerConfigProtobuf& protobuf, const QuicWallTime now) {
+ std::unique_ptr<CryptoHandshakeMessage> msg =
+ CryptoFramer::ParseMessage(protobuf.config());
+
+ if (!msg) {
+ QUIC_LOG(WARNING) << "Failed to parse server config message";
+ return nullptr;
+ }
+
+ quiche::QuicheReferenceCountedPointer<Config> config =
+ ParseConfigProtobuf(protobuf, /* is_fallback = */ false);
+ if (!config) {
+ QUIC_LOG(WARNING) << "Failed to parse server config message";
+ return nullptr;
+ }
+
+ {
+ QuicWriterMutexLock locked(&configs_lock_);
+ if (configs_.find(config->id) != configs_.end()) {
+ QUIC_LOG(WARNING) << "Failed to add config because another with the same "
+ "server config id already exists: "
+ << absl::BytesToHexString(config->id);
+ return nullptr;
+ }
+
+ configs_[config->id] = config;
+ SelectNewPrimaryConfig(now);
+ QUICHE_DCHECK(primary_config_.get());
+ QUICHE_DCHECK_EQ(configs_.find(primary_config_->id)->second.get(),
+ primary_config_.get());
+ }
+
+ return msg;
+}
+
+std::unique_ptr<CryptoHandshakeMessage>
+QuicCryptoServerConfig::AddDefaultConfig(QuicRandom* rand,
+ const QuicClock* clock,
+ const ConfigOptions& options) {
+ return AddConfig(GenerateConfig(rand, clock, options), clock->WallNow());
+}
+
+bool QuicCryptoServerConfig::SetConfigs(
+ const std::vector<QuicServerConfigProtobuf>& protobufs,
+ const QuicServerConfigProtobuf* fallback_protobuf, const QuicWallTime now) {
+ std::vector<quiche::QuicheReferenceCountedPointer<Config>> parsed_configs;
+ for (auto& protobuf : protobufs) {
+ quiche::QuicheReferenceCountedPointer<Config> config =
+ ParseConfigProtobuf(protobuf, /* is_fallback = */ false);
+ if (!config) {
+ QUIC_LOG(WARNING) << "Rejecting QUIC configs because of above errors";
+ return false;
+ }
+
+ parsed_configs.push_back(config);
+ }
+
+ quiche::QuicheReferenceCountedPointer<Config> fallback_config;
+ if (fallback_protobuf != nullptr) {
+ fallback_config =
+ ParseConfigProtobuf(*fallback_protobuf, /* is_fallback = */ true);
+ if (!fallback_config) {
+ QUIC_LOG(WARNING) << "Rejecting QUIC configs because of above errors";
+ return false;
+ }
+ QUIC_LOG(INFO) << "Fallback config has scid "
+ << absl::BytesToHexString(fallback_config->id);
+ parsed_configs.push_back(fallback_config);
+ } else {
+ QUIC_LOG(INFO) << "No fallback config provided";
+ }
+
+ if (parsed_configs.empty()) {
+ QUIC_LOG(WARNING)
+ << "Rejecting QUIC configs because new config list is empty.";
+ return false;
+ }
+
+ QUIC_LOG(INFO) << "Updating configs:";
+
+ QuicWriterMutexLock locked(&configs_lock_);
+ ConfigMap new_configs;
+
+ for (const quiche::QuicheReferenceCountedPointer<Config>& config :
+ parsed_configs) {
+ auto it = configs_.find(config->id);
+ if (it != configs_.end()) {
+ QUIC_LOG(INFO) << "Keeping scid: " << absl::BytesToHexString(config->id)
+ << " orbit: "
+ << absl::BytesToHexString(absl::string_view(
+ reinterpret_cast<const char*>(config->orbit),
+ kOrbitSize))
+ << " new primary_time "
+ << config->primary_time.ToUNIXSeconds()
+ << " old primary_time "
+ << it->second->primary_time.ToUNIXSeconds()
+ << " new priority " << config->priority << " old priority "
+ << it->second->priority;
+ // Update primary_time and priority.
+ it->second->primary_time = config->primary_time;
+ it->second->priority = config->priority;
+ new_configs.insert(*it);
+ } else {
+ QUIC_LOG(INFO) << "Adding scid: " << absl::BytesToHexString(config->id)
+ << " orbit: "
+ << absl::BytesToHexString(absl::string_view(
+ reinterpret_cast<const char*>(config->orbit),
+ kOrbitSize))
+ << " primary_time " << config->primary_time.ToUNIXSeconds()
+ << " priority " << config->priority;
+ new_configs.emplace(config->id, config);
+ }
+ }
+
+ configs_ = std::move(new_configs);
+ fallback_config_ = fallback_config;
+ SelectNewPrimaryConfig(now);
+ QUICHE_DCHECK(primary_config_.get());
+ QUICHE_DCHECK_EQ(configs_.find(primary_config_->id)->second.get(),
+ primary_config_.get());
+
+ return true;
+}
+
+void QuicCryptoServerConfig::SetSourceAddressTokenKeys(
+ const std::vector<std::string>& keys) {
+ // TODO(b/208866709)
+ source_address_token_boxer_.SetKeys(keys);
+}
+
+std::vector<std::string> QuicCryptoServerConfig::GetConfigIds() const {
+ QuicReaderMutexLock locked(&configs_lock_);
+ std::vector<std::string> scids;
+ for (auto it = configs_.begin(); it != configs_.end(); ++it) {
+ scids.push_back(it->first);
+ }
+ return scids;
+}
+
+void QuicCryptoServerConfig::ValidateClientHello(
+ const CryptoHandshakeMessage& client_hello,
+ const QuicSocketAddress& client_address,
+ const QuicSocketAddress& server_address, QuicTransportVersion version,
+ const QuicClock* clock,
+ quiche::QuicheReferenceCountedPointer<QuicSignedServerConfig> signed_config,
+ std::unique_ptr<ValidateClientHelloResultCallback> done_cb) const {
+ const QuicWallTime now(clock->WallNow());
+
+ quiche::QuicheReferenceCountedPointer<
+ ValidateClientHelloResultCallback::Result>
+ result(new ValidateClientHelloResultCallback::Result(
+ client_hello, client_address.host(), now));
+
+ absl::string_view requested_scid;
+ // We ignore here the return value from GetStringPiece. If there is no SCID
+ // tag, EvaluateClientHello will discover that because GetCurrentConfigs will
+ // not have found the requested config (i.e. because none of the configs will
+ // have an empty string as its id).
+ client_hello.GetStringPiece(kSCID, &requested_scid);
+ Configs configs;
+ if (!GetCurrentConfigs(now, requested_scid,
+ /* old_primary_config = */ nullptr, &configs)) {
+ result->error_code = QUIC_CRYPTO_INTERNAL_ERROR;
+ result->error_details = "No configurations loaded";
+ }
+ signed_config->config = configs.primary;
+
+ if (result->error_code == QUIC_NO_ERROR) {
+ // QUIC requires a new proof for each CHLO so clear any existing proof.
+ signed_config->chain = nullptr;
+ signed_config->proof.signature = "";
+ signed_config->proof.leaf_cert_scts = "";
+ EvaluateClientHello(server_address, client_address, version, configs,
+ result, std::move(done_cb));
+ } else {
+ done_cb->Run(result, /* details = */ nullptr);
+ }
+}
+
+class QuicCryptoServerConfig::ProcessClientHelloCallback
+ : public ProofSource::Callback {
+ public:
+ ProcessClientHelloCallback(const QuicCryptoServerConfig* config,
+ std::unique_ptr<ProcessClientHelloContext> context,
+ const Configs& configs)
+ : config_(config), context_(std::move(context)), configs_(configs) {}
+
+ void Run(
+ bool ok,
+ const quiche::QuicheReferenceCountedPointer<ProofSource::Chain>& chain,
+ const QuicCryptoProof& proof,
+ std::unique_ptr<ProofSource::Details> details) override {
+ if (ok) {
+ context_->signed_config()->chain = chain;
+ context_->signed_config()->proof = proof;
+ }
+ config_->ProcessClientHelloAfterGetProof(!ok, std::move(details),
+ std::move(context_), configs_);
+ }
+
+ private:
+ const QuicCryptoServerConfig* config_;
+ std::unique_ptr<ProcessClientHelloContext> context_;
+ const Configs configs_;
+};
+
+class QuicCryptoServerConfig::ProcessClientHelloAfterGetProofCallback
+ : public AsynchronousKeyExchange::Callback {
+ public:
+ ProcessClientHelloAfterGetProofCallback(
+ const QuicCryptoServerConfig* config,
+ std::unique_ptr<ProofSource::Details> proof_source_details,
+ QuicTag key_exchange_type, std::unique_ptr<CryptoHandshakeMessage> out,
+ absl::string_view public_value,
+ std::unique_ptr<ProcessClientHelloContext> context,
+ const Configs& configs)
+ : config_(config),
+ proof_source_details_(std::move(proof_source_details)),
+ key_exchange_type_(key_exchange_type),
+ out_(std::move(out)),
+ public_value_(public_value),
+ context_(std::move(context)),
+ configs_(configs) {}
+
+ void Run(bool ok) override {
+ config_->ProcessClientHelloAfterCalculateSharedKeys(
+ !ok, std::move(proof_source_details_), key_exchange_type_,
+ std::move(out_), public_value_, std::move(context_), configs_);
+ }
+
+ private:
+ const QuicCryptoServerConfig* config_;
+ std::unique_ptr<ProofSource::Details> proof_source_details_;
+ const QuicTag key_exchange_type_;
+ std::unique_ptr<CryptoHandshakeMessage> out_;
+ const std::string public_value_;
+ std::unique_ptr<ProcessClientHelloContext> context_;
+ const Configs configs_;
+ std::unique_ptr<ProcessClientHelloResultCallback> done_cb_;
+};
+
+class QuicCryptoServerConfig::SendRejectWithFallbackConfigCallback
+ : public ProofSource::Callback {
+ public:
+ SendRejectWithFallbackConfigCallback(
+ const QuicCryptoServerConfig* config,
+ std::unique_ptr<ProcessClientHelloContext> context,
+ quiche::QuicheReferenceCountedPointer<Config> fallback_config)
+ : config_(config),
+ context_(std::move(context)),
+ fallback_config_(fallback_config) {}
+
+ // Capture |chain| and |proof| into the signed config, and then invoke
+ // SendRejectWithFallbackConfigAfterGetProof.
+ void Run(
+ bool ok,
+ const quiche::QuicheReferenceCountedPointer<ProofSource::Chain>& chain,
+ const QuicCryptoProof& proof,
+ std::unique_ptr<ProofSource::Details> details) override {
+ if (ok) {
+ context_->signed_config()->chain = chain;
+ context_->signed_config()->proof = proof;
+ }
+ config_->SendRejectWithFallbackConfigAfterGetProof(
+ !ok, std::move(details), std::move(context_), fallback_config_);
+ }
+
+ private:
+ const QuicCryptoServerConfig* config_;
+ std::unique_ptr<ProcessClientHelloContext> context_;
+ quiche::QuicheReferenceCountedPointer<Config> fallback_config_;
+};
+
+void QuicCryptoServerConfig::ProcessClientHello(
+ quiche::QuicheReferenceCountedPointer<
+ ValidateClientHelloResultCallback::Result>
+ validate_chlo_result,
+ bool reject_only, QuicConnectionId connection_id,
+ const QuicSocketAddress& server_address,
+ const QuicSocketAddress& client_address, ParsedQuicVersion version,
+ const ParsedQuicVersionVector& supported_versions, const QuicClock* clock,
+ QuicRandom* rand, QuicCompressedCertsCache* compressed_certs_cache,
+ quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters>
+ params,
+ quiche::QuicheReferenceCountedPointer<QuicSignedServerConfig> signed_config,
+ QuicByteCount total_framing_overhead, QuicByteCount chlo_packet_size,
+ std::unique_ptr<ProcessClientHelloResultCallback> done_cb) const {
+ QUICHE_DCHECK(done_cb);
+ auto context = std::make_unique<ProcessClientHelloContext>(
+ validate_chlo_result, reject_only, connection_id, server_address,
+ client_address, version, supported_versions, clock, rand,
+ compressed_certs_cache, params, signed_config, total_framing_overhead,
+ chlo_packet_size, std::move(done_cb));
+
+ // Verify that various parts of the CHLO are valid
+ std::string error_details;
+ QuicErrorCode valid = CryptoUtils::ValidateClientHello(
+ context->client_hello(), context->version(),
+ context->supported_versions(), &error_details);
+ if (valid != QUIC_NO_ERROR) {
+ context->Fail(valid, error_details);
+ return;
+ }
+
+ absl::string_view requested_scid;
+ context->client_hello().GetStringPiece(kSCID, &requested_scid);
+ Configs configs;
+ if (!GetCurrentConfigs(context->clock()->WallNow(), requested_scid,
+ signed_config->config, &configs)) {
+ context->Fail(QUIC_CRYPTO_INTERNAL_ERROR, "No configurations loaded");
+ return;
+ }
+
+ if (context->validate_chlo_result()->error_code != QUIC_NO_ERROR) {
+ context->Fail(context->validate_chlo_result()->error_code,
+ context->validate_chlo_result()->error_details);
+ return;
+ }
+
+ if (!ClientDemandsX509Proof(context->client_hello())) {
+ context->Fail(QUIC_UNSUPPORTED_PROOF_DEMAND, "Missing or invalid PDMD");
+ return;
+ }
+
+ // No need to get a new proof if one was already generated.
+ if (!context->signed_config()->chain) {
+ const std::string chlo_hash = CryptoUtils::HashHandshakeMessage(
+ context->client_hello(), Perspective::IS_SERVER);
+ const QuicSocketAddress server_address = context->server_address();
+ const std::string sni = std::string(context->info().sni);
+ const QuicTransportVersion transport_version = context->transport_version();
+
+ auto cb = std::make_unique<ProcessClientHelloCallback>(
+ this, std::move(context), configs);
+
+ QUICHE_DCHECK(proof_source_.get());
+ proof_source_->GetProof(server_address, client_address, sni,
+ configs.primary->serialized, transport_version,
+ chlo_hash, std::move(cb));
+ return;
+ }
+
+ ProcessClientHelloAfterGetProof(
+ /* found_error = */ false, /* proof_source_details = */ nullptr,
+ std::move(context), configs);
+}
+
+void QuicCryptoServerConfig::ProcessClientHelloAfterGetProof(
+ bool found_error,
+ std::unique_ptr<ProofSource::Details> proof_source_details,
+ std::unique_ptr<ProcessClientHelloContext> context,
+ const Configs& configs) const {
+ QUIC_BUG_IF(quic_bug_12963_2,
+ !QuicUtils::IsConnectionIdValidForVersion(
+ context->connection_id(), context->transport_version()))
+ << "ProcessClientHelloAfterGetProof: attempted to use connection ID "
+ << context->connection_id() << " which is invalid with version "
+ << context->version();
+
+ if (context->info().reject_reasons.empty()) {
+ if (!context->signed_config() || !context->signed_config()->chain) {
+ // No chain.
+ context->validate_chlo_result()->info.reject_reasons.push_back(
+ SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE);
+ } else if (!ValidateExpectedLeafCertificate(
+ context->client_hello(),
+ context->signed_config()->chain->certs)) {
+ // Has chain but leaf is invalid.
+ context->validate_chlo_result()->info.reject_reasons.push_back(
+ INVALID_EXPECTED_LEAF_CERTIFICATE);
+ }
+ }
+
+ if (found_error) {
+ context->Fail(QUIC_HANDSHAKE_FAILED, "Failed to get proof");
+ return;
+ }
+
+ auto out_diversification_nonce = std::make_unique<DiversificationNonce>();
+
+ absl::string_view cert_sct;
+ if (context->client_hello().GetStringPiece(kCertificateSCTTag, &cert_sct) &&
+ cert_sct.empty()) {
+ context->params()->sct_supported_by_client = true;
+ }
+
+ auto out = std::make_unique<CryptoHandshakeMessage>();
+ if (!context->info().reject_reasons.empty() || !configs.requested) {
+ BuildRejectionAndRecordStats(*context, *configs.primary,
+ context->info().reject_reasons, out.get());
+ context->Succeed(std::move(out), std::move(out_diversification_nonce),
+ std::move(proof_source_details));
+ return;
+ }
+
+ if (context->reject_only()) {
+ context->Succeed(std::move(out), std::move(out_diversification_nonce),
+ std::move(proof_source_details));
+ return;
+ }
+
+ QuicTagVector their_aeads;
+ QuicTagVector their_key_exchanges;
+ if (context->client_hello().GetTaglist(kAEAD, &their_aeads) !=
+ QUIC_NO_ERROR ||
+ context->client_hello().GetTaglist(kKEXS, &their_key_exchanges) !=
+ QUIC_NO_ERROR ||
+ their_aeads.size() != 1 || their_key_exchanges.size() != 1) {
+ context->Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
+ "Missing or invalid AEAD or KEXS");
+ return;
+ }
+
+ size_t key_exchange_index;
+ if (!FindMutualQuicTag(configs.requested->aead, their_aeads,
+ &context->params()->aead, nullptr) ||
+ !FindMutualQuicTag(configs.requested->kexs, their_key_exchanges,
+ &context->params()->key_exchange,
+ &key_exchange_index)) {
+ context->Fail(QUIC_CRYPTO_NO_SUPPORT, "Unsupported AEAD or KEXS");
+ return;
+ }
+
+ absl::string_view public_value;
+ if (!context->client_hello().GetStringPiece(kPUBS, &public_value)) {
+ context->Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
+ "Missing public value");
+ return;
+ }
+
+ // Allow testing a specific adversarial case in which a client sends a public
+ // value of incorrect size.
+ AdjustTestValue("quic::QuicCryptoServerConfig::public_value_adjust",
+ &public_value);
+
+ const AsynchronousKeyExchange* key_exchange =
+ configs.requested->key_exchanges[key_exchange_index].get();
+ std::string* initial_premaster_secret =
+ &context->params()->initial_premaster_secret;
+ auto cb = std::make_unique<ProcessClientHelloAfterGetProofCallback>(
+ this, std::move(proof_source_details), key_exchange->type(),
+ std::move(out), public_value, std::move(context), configs);
+ key_exchange->CalculateSharedKeyAsync(public_value, initial_premaster_secret,
+ std::move(cb));
+}
+
+void QuicCryptoServerConfig::ProcessClientHelloAfterCalculateSharedKeys(
+ bool found_error,
+ std::unique_ptr<ProofSource::Details> proof_source_details,
+ QuicTag key_exchange_type, std::unique_ptr<CryptoHandshakeMessage> out,
+ absl::string_view public_value,
+ std::unique_ptr<ProcessClientHelloContext> context,
+ const Configs& configs) const {
+ QUIC_BUG_IF(quic_bug_12963_3,
+ !QuicUtils::IsConnectionIdValidForVersion(
+ context->connection_id(), context->transport_version()))
+ << "ProcessClientHelloAfterCalculateSharedKeys:"
+ " attempted to use connection ID "
+ << context->connection_id() << " which is invalid with version "
+ << context->version();
+
+ if (found_error) {
+ // If we are already using the fallback config, or there is no fallback
+ // config to use, just bail out of the handshake.
+ if (configs.fallback == nullptr ||
+ context->signed_config()->config == configs.fallback) {
+ context->Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
+ "Failed to calculate shared key");
+ } else {
+ SendRejectWithFallbackConfig(std::move(context), configs.fallback);
+ }
+ return;
+ }
+
+ if (!context->info().sni.empty()) {
+ context->params()->sni =
+ QuicHostnameUtils::NormalizeHostname(context->info().sni);
+ }
+
+ std::string hkdf_suffix;
+ const QuicData& client_hello_serialized =
+ context->client_hello().GetSerialized();
+ hkdf_suffix.reserve(context->connection_id().length() +
+ client_hello_serialized.length() +
+ configs.requested->serialized.size());
+ hkdf_suffix.append(context->connection_id().data(),
+ context->connection_id().length());
+ hkdf_suffix.append(client_hello_serialized.data(),
+ client_hello_serialized.length());
+ hkdf_suffix.append(configs.requested->serialized);
+ QUICHE_DCHECK(proof_source_.get());
+ if (context->signed_config()->chain->certs.empty()) {
+ context->Fail(QUIC_CRYPTO_INTERNAL_ERROR, "Failed to get certs");
+ return;
+ }
+ hkdf_suffix.append(context->signed_config()->chain->certs.at(0));
+
+ absl::string_view cetv_ciphertext;
+ if (configs.requested->channel_id_enabled &&
+ context->client_hello().GetStringPiece(kCETV, &cetv_ciphertext)) {
+ CryptoHandshakeMessage client_hello_copy(context->client_hello());
+ client_hello_copy.Erase(kCETV);
+ client_hello_copy.Erase(kPAD);
+
+ const QuicData& client_hello_copy_serialized =
+ client_hello_copy.GetSerialized();
+ std::string hkdf_input;
+ hkdf_input.append(QuicCryptoConfig::kCETVLabel,
+ strlen(QuicCryptoConfig::kCETVLabel) + 1);
+ hkdf_input.append(context->connection_id().data(),
+ context->connection_id().length());
+ hkdf_input.append(client_hello_copy_serialized.data(),
+ client_hello_copy_serialized.length());
+ hkdf_input.append(configs.requested->serialized);
+
+ CrypterPair crypters;
+ if (!CryptoUtils::DeriveKeys(
+ context->version(), context->params()->initial_premaster_secret,
+ context->params()->aead, context->info().client_nonce,
+ context->info().server_nonce, pre_shared_key_, hkdf_input,
+ Perspective::IS_SERVER, CryptoUtils::Diversification::Never(),
+ &crypters, nullptr /* subkey secret */)) {
+ context->Fail(QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED,
+ "Symmetric key setup failed");
+ return;
+ }
+
+ char plaintext[kMaxOutgoingPacketSize];
+ size_t plaintext_length = 0;
+ const bool success = crypters.decrypter->DecryptPacket(
+ 0 /* packet number */, absl::string_view() /* associated data */,
+ cetv_ciphertext, plaintext, &plaintext_length, kMaxOutgoingPacketSize);
+ if (!success) {
+ context->Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
+ "CETV decryption failure");
+ return;
+ }
+ std::unique_ptr<CryptoHandshakeMessage> cetv(CryptoFramer::ParseMessage(
+ absl::string_view(plaintext, plaintext_length)));
+ if (!cetv) {
+ context->Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER, "CETV parse error");
+ return;
+ }
+
+ absl::string_view key, signature;
+ if (cetv->GetStringPiece(kCIDK, &key) &&
+ cetv->GetStringPiece(kCIDS, &signature)) {
+ if (!ChannelIDVerifier::Verify(key, hkdf_input, signature)) {
+ context->Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
+ "ChannelID signature failure");
+ return;
+ }
+
+ context->params()->channel_id = std::string(key);
+ }
+ }
+
+ std::string hkdf_input;
+ size_t label_len = strlen(QuicCryptoConfig::kInitialLabel) + 1;
+ hkdf_input.reserve(label_len + hkdf_suffix.size());
+ hkdf_input.append(QuicCryptoConfig::kInitialLabel, label_len);
+ hkdf_input.append(hkdf_suffix);
+
+ auto out_diversification_nonce = std::make_unique<DiversificationNonce>();
+ context->rand()->RandBytes(out_diversification_nonce->data(),
+ out_diversification_nonce->size());
+ CryptoUtils::Diversification diversification =
+ CryptoUtils::Diversification::Now(out_diversification_nonce.get());
+ if (!CryptoUtils::DeriveKeys(
+ context->version(), context->params()->initial_premaster_secret,
+ context->params()->aead, context->info().client_nonce,
+ context->info().server_nonce, pre_shared_key_, hkdf_input,
+ Perspective::IS_SERVER, diversification,
+ &context->params()->initial_crypters,
+ &context->params()->initial_subkey_secret)) {
+ context->Fail(QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED,
+ "Symmetric key setup failed");
+ return;
+ }
+
+ std::string forward_secure_public_value;
+ std::unique_ptr<SynchronousKeyExchange> forward_secure_key_exchange =
+ CreateLocalSynchronousKeyExchange(key_exchange_type, context->rand());
+ if (!forward_secure_key_exchange) {
+ QUIC_DLOG(WARNING) << "Failed to create keypair";
+ context->Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
+ "Failed to create keypair");
+ return;
+ }
+
+ forward_secure_public_value =
+ std::string(forward_secure_key_exchange->public_value());
+ if (!forward_secure_key_exchange->CalculateSharedKeySync(
+ public_value, &context->params()->forward_secure_premaster_secret)) {
+ context->Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
+ "Invalid public value");
+ return;
+ }
+
+ std::string forward_secure_hkdf_input;
+ label_len = strlen(QuicCryptoConfig::kForwardSecureLabel) + 1;
+ forward_secure_hkdf_input.reserve(label_len + hkdf_suffix.size());
+ forward_secure_hkdf_input.append(QuicCryptoConfig::kForwardSecureLabel,
+ label_len);
+ forward_secure_hkdf_input.append(hkdf_suffix);
+
+ std::string shlo_nonce;
+ shlo_nonce = NewServerNonce(context->rand(), context->info().now);
+ out->SetStringPiece(kServerNonceTag, shlo_nonce);
+
+ if (!CryptoUtils::DeriveKeys(
+ context->version(),
+ context->params()->forward_secure_premaster_secret,
+ context->params()->aead, context->info().client_nonce,
+ shlo_nonce.empty() ? context->info().server_nonce : shlo_nonce,
+ pre_shared_key_, forward_secure_hkdf_input, Perspective::IS_SERVER,
+ CryptoUtils::Diversification::Never(),
+ &context->params()->forward_secure_crypters,
+ &context->params()->subkey_secret)) {
+ context->Fail(QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED,
+ "Symmetric key setup failed");
+ return;
+ }
+
+ out->set_tag(kSHLO);
+ out->SetVersionVector(kVER, context->supported_versions());
+ out->SetStringPiece(
+ kSourceAddressTokenTag,
+ NewSourceAddressToken(*configs.requested->source_address_token_boxer,
+ context->info().source_address_tokens,
+ context->client_address().host(), context->rand(),
+ context->info().now, nullptr));
+ QuicSocketAddressCoder address_coder(context->client_address());
+ out->SetStringPiece(kCADR, address_coder.Encode());
+ out->SetStringPiece(kPUBS, forward_secure_public_value);
+
+ context->Succeed(std::move(out), std::move(out_diversification_nonce),
+ std::move(proof_source_details));
+}
+
+void QuicCryptoServerConfig::SendRejectWithFallbackConfig(
+ std::unique_ptr<ProcessClientHelloContext> context,
+ quiche::QuicheReferenceCountedPointer<Config> fallback_config) const {
+ // We failed to calculate a shared initial key, likely because we tried to use
+ // a remote key-exchange service which could not be reached. We want to send
+ // a REJ which tells the client to use a different ServerConfig which
+ // corresponds to a local keypair. To generate the REJ we need to request a
+ // new proof.
+ const std::string chlo_hash = CryptoUtils::HashHandshakeMessage(
+ context->client_hello(), Perspective::IS_SERVER);
+ const QuicSocketAddress server_address = context->server_address();
+ const std::string sni(context->info().sni);
+ const QuicTransportVersion transport_version = context->transport_version();
+
+ const QuicSocketAddress& client_address = context->client_address();
+ auto cb = std::make_unique<SendRejectWithFallbackConfigCallback>(
+ this, std::move(context), fallback_config);
+ proof_source_->GetProof(server_address, client_address, sni,
+ fallback_config->serialized, transport_version,
+ chlo_hash, std::move(cb));
+}
+
+void QuicCryptoServerConfig::SendRejectWithFallbackConfigAfterGetProof(
+ bool found_error,
+ std::unique_ptr<ProofSource::Details> proof_source_details,
+ std::unique_ptr<ProcessClientHelloContext> context,
+ quiche::QuicheReferenceCountedPointer<Config> fallback_config) const {
+ if (found_error) {
+ context->Fail(QUIC_HANDSHAKE_FAILED, "Failed to get proof");
+ return;
+ }
+
+ auto out = std::make_unique<CryptoHandshakeMessage>();
+ BuildRejectionAndRecordStats(*context, *fallback_config,
+ {SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE},
+ out.get());
+
+ context->Succeed(std::move(out), std::make_unique<DiversificationNonce>(),
+ std::move(proof_source_details));
+}
+
+quiche::QuicheReferenceCountedPointer<QuicCryptoServerConfig::Config>
+QuicCryptoServerConfig::GetConfigWithScid(
+ absl::string_view requested_scid) const {
+ configs_lock_.AssertReaderHeld();
+
+ if (!requested_scid.empty()) {
+ auto it = configs_.find((std::string(requested_scid)));
+ if (it != configs_.end()) {
+ // We'll use the config that the client requested in order to do
+ // key-agreement.
+ return quiche::QuicheReferenceCountedPointer<Config>(it->second);
+ }
+ }
+
+ return quiche::QuicheReferenceCountedPointer<Config>();
+}
+
+bool QuicCryptoServerConfig::GetCurrentConfigs(
+ const QuicWallTime& now, absl::string_view requested_scid,
+ quiche::QuicheReferenceCountedPointer<Config> old_primary_config,
+ Configs* configs) const {
+ QuicReaderMutexLock locked(&configs_lock_);
+
+ if (!primary_config_) {
+ return false;
+ }
+
+ if (IsNextConfigReady(now)) {
+ configs_lock_.ReaderUnlock();
+ configs_lock_.WriterLock();
+ SelectNewPrimaryConfig(now);
+ QUICHE_DCHECK(primary_config_.get());
+ QUICHE_DCHECK_EQ(configs_.find(primary_config_->id)->second.get(),
+ primary_config_.get());
+ configs_lock_.WriterUnlock();
+ configs_lock_.ReaderLock();
+ }
+
+ if (old_primary_config != nullptr) {
+ configs->primary = old_primary_config;
+ } else {
+ configs->primary = primary_config_;
+ }
+ configs->requested = GetConfigWithScid(requested_scid);
+ configs->fallback = fallback_config_;
+
+ return true;
+}
+
+// ConfigPrimaryTimeLessThan is a comparator that implements "less than" for
+// Config's based on their primary_time.
+// static
+bool QuicCryptoServerConfig::ConfigPrimaryTimeLessThan(
+ const quiche::QuicheReferenceCountedPointer<Config>& a,
+ const quiche::QuicheReferenceCountedPointer<Config>& b) {
+ if (a->primary_time.IsBefore(b->primary_time) ||
+ b->primary_time.IsBefore(a->primary_time)) {
+ // Primary times differ.
+ return a->primary_time.IsBefore(b->primary_time);
+ } else if (a->priority != b->priority) {
+ // Primary times are equal, sort backwards by priority.
+ return a->priority < b->priority;
+ } else {
+ // Primary times and priorities are equal, sort by config id.
+ return a->id < b->id;
+ }
+}
+
+void QuicCryptoServerConfig::SelectNewPrimaryConfig(
+ const QuicWallTime now) const {
+ std::vector<quiche::QuicheReferenceCountedPointer<Config>> configs;
+ configs.reserve(configs_.size());
+
+ for (auto it = configs_.begin(); it != configs_.end(); ++it) {
+ // TODO(avd) Exclude expired configs?
+ configs.push_back(it->second);
+ }
+
+ if (configs.empty()) {
+ if (primary_config_ != nullptr) {
+ QUIC_BUG(quic_bug_10630_2)
+ << "No valid QUIC server config. Keeping the current config.";
+ } else {
+ QUIC_BUG(quic_bug_10630_3) << "No valid QUIC server config.";
+ }
+ return;
+ }
+
+ std::sort(configs.begin(), configs.end(), ConfigPrimaryTimeLessThan);
+
+ quiche::QuicheReferenceCountedPointer<Config> best_candidate = configs[0];
+
+ for (size_t i = 0; i < configs.size(); ++i) {
+ const quiche::QuicheReferenceCountedPointer<Config> config(configs[i]);
+ if (!config->primary_time.IsAfter(now)) {
+ if (config->primary_time.IsAfter(best_candidate->primary_time)) {
+ best_candidate = config;
+ }
+ continue;
+ }
+
+ // This is the first config with a primary_time in the future. Thus the
+ // previous Config should be the primary and this one should determine the
+ // next_config_promotion_time_.
+ quiche::QuicheReferenceCountedPointer<Config> new_primary = best_candidate;
+ if (i == 0) {
+ // We need the primary_time of the next config.
+ if (configs.size() > 1) {
+ next_config_promotion_time_ = configs[1]->primary_time;
+ } else {
+ next_config_promotion_time_ = QuicWallTime::Zero();
+ }
+ } else {
+ next_config_promotion_time_ = config->primary_time;
+ }
+
+ if (primary_config_) {
+ primary_config_->is_primary = false;
+ }
+ primary_config_ = new_primary;
+ new_primary->is_primary = true;
+ QUIC_DLOG(INFO) << "New primary config. orbit: "
+ << absl::BytesToHexString(
+ absl::string_view(reinterpret_cast<const char*>(
+ primary_config_->orbit),
+ kOrbitSize));
+ if (primary_config_changed_cb_ != nullptr) {
+ primary_config_changed_cb_->Run(primary_config_->id);
+ }
+
+ return;
+ }
+
+ // All config's primary times are in the past. We should make the most recent
+ // and highest priority candidate primary.
+ quiche::QuicheReferenceCountedPointer<Config> new_primary = best_candidate;
+ if (primary_config_) {
+ primary_config_->is_primary = false;
+ }
+ primary_config_ = new_primary;
+ new_primary->is_primary = true;
+ QUIC_DLOG(INFO) << "New primary config. orbit: "
+ << absl::BytesToHexString(absl::string_view(
+ reinterpret_cast<const char*>(primary_config_->orbit),
+ kOrbitSize))
+ << " scid: " << absl::BytesToHexString(primary_config_->id);
+ next_config_promotion_time_ = QuicWallTime::Zero();
+ if (primary_config_changed_cb_ != nullptr) {
+ primary_config_changed_cb_->Run(primary_config_->id);
+ }
+}
+
+void QuicCryptoServerConfig::EvaluateClientHello(
+ const QuicSocketAddress& /*server_address*/,
+ const QuicSocketAddress& /*client_address*/,
+ QuicTransportVersion /*version*/, const Configs& configs,
+ quiche::QuicheReferenceCountedPointer<
+ ValidateClientHelloResultCallback::Result>
+ client_hello_state,
+ std::unique_ptr<ValidateClientHelloResultCallback> done_cb) const {
+ ValidateClientHelloHelper helper(client_hello_state, &done_cb);
+
+ const CryptoHandshakeMessage& client_hello = client_hello_state->client_hello;
+ ClientHelloInfo* info = &(client_hello_state->info);
+
+ if (client_hello.GetStringPiece(kSNI, &info->sni) &&
+ !QuicHostnameUtils::IsValidSNI(info->sni)) {
+ helper.ValidationComplete(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
+ "Invalid SNI name", nullptr);
+ return;
+ }
+
+ client_hello.GetStringPiece(kUAID, &info->user_agent_id);
+
+ HandshakeFailureReason source_address_token_error = MAX_FAILURE_REASON;
+ if (validate_source_address_token_) {
+ absl::string_view srct;
+ if (client_hello.GetStringPiece(kSourceAddressTokenTag, &srct)) {
+ Config& config =
+ configs.requested != nullptr ? *configs.requested : *configs.primary;
+ source_address_token_error =
+ ParseSourceAddressToken(*config.source_address_token_boxer, srct,
+ info->source_address_tokens);
+
+ if (source_address_token_error == HANDSHAKE_OK) {
+ source_address_token_error = ValidateSourceAddressTokens(
+ info->source_address_tokens, info->client_ip, info->now,
+ &client_hello_state->cached_network_params);
+ }
+ info->valid_source_address_token =
+ (source_address_token_error == HANDSHAKE_OK);
+ } else {
+ source_address_token_error = SOURCE_ADDRESS_TOKEN_INVALID_FAILURE;
+ }
+ } else {
+ source_address_token_error = HANDSHAKE_OK;
+ info->valid_source_address_token = true;
+ }
+
+ if (!configs.requested) {
+ absl::string_view requested_scid;
+ if (client_hello.GetStringPiece(kSCID, &requested_scid)) {
+ info->reject_reasons.push_back(SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE);
+ } else {
+ info->reject_reasons.push_back(SERVER_CONFIG_INCHOATE_HELLO_FAILURE);
+ }
+ // No server config with the requested ID.
+ helper.ValidationComplete(QUIC_NO_ERROR, "", nullptr);
+ return;
+ }
+
+ if (!client_hello.GetStringPiece(kNONC, &info->client_nonce)) {
+ info->reject_reasons.push_back(SERVER_CONFIG_INCHOATE_HELLO_FAILURE);
+ // Report no client nonce as INCHOATE_HELLO_FAILURE.
+ helper.ValidationComplete(QUIC_NO_ERROR, "", nullptr);
+ return;
+ }
+
+ if (source_address_token_error != HANDSHAKE_OK) {
+ info->reject_reasons.push_back(source_address_token_error);
+ // No valid source address token.
+ }
+
+ if (info->client_nonce.size() != kNonceSize) {
+ info->reject_reasons.push_back(CLIENT_NONCE_INVALID_FAILURE);
+ // Invalid client nonce.
+ QUIC_LOG_FIRST_N(ERROR, 2)
+ << "Invalid client nonce: " << client_hello.DebugString();
+ QUIC_DLOG(INFO) << "Invalid client nonce.";
+ }
+
+ // Server nonce is optional, and used for key derivation if present.
+ client_hello.GetStringPiece(kServerNonceTag, &info->server_nonce);
+
+ // If the server nonce is empty and we're requiring handshake confirmation
+ // for DoS reasons then we must reject the CHLO.
+ if (GetQuicReloadableFlag(quic_require_handshake_confirmation) &&
+ info->server_nonce.empty()) {
+ info->reject_reasons.push_back(SERVER_NONCE_REQUIRED_FAILURE);
+ }
+ helper.ValidationComplete(QUIC_NO_ERROR, "",
+ std::unique_ptr<ProofSource::Details>());
+}
+
+void QuicCryptoServerConfig::BuildServerConfigUpdateMessage(
+ QuicTransportVersion version, absl::string_view chlo_hash,
+ const SourceAddressTokens& previous_source_address_tokens,
+ const QuicSocketAddress& server_address,
+ const QuicSocketAddress& client_address, const QuicClock* clock,
+ QuicRandom* rand, QuicCompressedCertsCache* compressed_certs_cache,
+ const QuicCryptoNegotiatedParameters& params,
+ const CachedNetworkParameters* cached_network_params,
+ std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb) const {
+ std::string serialized;
+ std::string source_address_token;
+ {
+ QuicReaderMutexLock locked(&configs_lock_);
+ serialized = primary_config_->serialized;
+ source_address_token = NewSourceAddressToken(
+ *primary_config_->source_address_token_boxer,
+ previous_source_address_tokens, client_address.host(), rand,
+ clock->WallNow(), cached_network_params);
+ }
+
+ CryptoHandshakeMessage message;
+ message.set_tag(kSCUP);
+ message.SetStringPiece(kSCFG, serialized);
+ message.SetStringPiece(kSourceAddressTokenTag, source_address_token);
+
+ auto proof_source_cb =
+ std::make_unique<BuildServerConfigUpdateMessageProofSourceCallback>(
+ this, compressed_certs_cache, params, std::move(message),
+ std::move(cb));
+
+ proof_source_->GetProof(server_address, client_address, params.sni,
+ serialized, version, chlo_hash,
+ std::move(proof_source_cb));
+}
+
+QuicCryptoServerConfig::BuildServerConfigUpdateMessageProofSourceCallback::
+ ~BuildServerConfigUpdateMessageProofSourceCallback() {}
+
+QuicCryptoServerConfig::BuildServerConfigUpdateMessageProofSourceCallback::
+ BuildServerConfigUpdateMessageProofSourceCallback(
+ const QuicCryptoServerConfig* config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ const QuicCryptoNegotiatedParameters& params,
+ CryptoHandshakeMessage message,
+ std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb)
+ : config_(config),
+ compressed_certs_cache_(compressed_certs_cache),
+ client_cached_cert_hashes_(params.client_cached_cert_hashes),
+ sct_supported_by_client_(params.sct_supported_by_client),
+ sni_(params.sni),
+ message_(std::move(message)),
+ cb_(std::move(cb)) {}
+
+void QuicCryptoServerConfig::BuildServerConfigUpdateMessageProofSourceCallback::
+ Run(bool ok,
+ const quiche::QuicheReferenceCountedPointer<ProofSource::Chain>& chain,
+ const QuicCryptoProof& proof,
+ std::unique_ptr<ProofSource::Details> details) {
+ config_->FinishBuildServerConfigUpdateMessage(
+ compressed_certs_cache_, client_cached_cert_hashes_,
+ sct_supported_by_client_, sni_, ok, chain, proof.signature,
+ proof.leaf_cert_scts, std::move(details), std::move(message_),
+ std::move(cb_));
+}
+
+void QuicCryptoServerConfig::FinishBuildServerConfigUpdateMessage(
+ QuicCompressedCertsCache* compressed_certs_cache,
+ const std::string& client_cached_cert_hashes, bool sct_supported_by_client,
+ const std::string& sni, bool ok,
+ const quiche::QuicheReferenceCountedPointer<ProofSource::Chain>& chain,
+ const std::string& signature, const std::string& leaf_cert_sct,
+ std::unique_ptr<ProofSource::Details> /*details*/,
+ CryptoHandshakeMessage message,
+ std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb) const {
+ if (!ok) {
+ cb->Run(false, message);
+ return;
+ }
+
+ const std::string compressed =
+ CompressChain(compressed_certs_cache, chain, client_cached_cert_hashes);
+
+ message.SetStringPiece(kCertificateTag, compressed);
+ message.SetStringPiece(kPROF, signature);
+ if (sct_supported_by_client && enable_serving_sct_) {
+ if (leaf_cert_sct.empty()) {
+ QUIC_LOG_EVERY_N_SEC(WARNING, 60)
+ << "SCT is expected but it is empty. SNI: " << sni;
+ } else {
+ message.SetStringPiece(kCertificateSCTTag, leaf_cert_sct);
+ }
+ }
+
+ cb->Run(true, message);
+}
+
+void QuicCryptoServerConfig::BuildRejectionAndRecordStats(
+ const ProcessClientHelloContext& context, const Config& config,
+ const std::vector<uint32_t>& reject_reasons,
+ CryptoHandshakeMessage* out) const {
+ BuildRejection(context, config, reject_reasons, out);
+ if (rejection_observer_ != nullptr) {
+ rejection_observer_->OnRejectionBuilt(reject_reasons, out);
+ }
+}
+
+void QuicCryptoServerConfig::BuildRejection(
+ const ProcessClientHelloContext& context, const Config& config,
+ const std::vector<uint32_t>& reject_reasons,
+ CryptoHandshakeMessage* out) const {
+ const QuicWallTime now = context.clock()->WallNow();
+
+ out->set_tag(kREJ);
+ out->SetStringPiece(kSCFG, config.serialized);
+ out->SetStringPiece(
+ kSourceAddressTokenTag,
+ NewSourceAddressToken(
+ *config.source_address_token_boxer,
+ context.info().source_address_tokens, context.info().client_ip,
+ context.rand(), context.info().now,
+ &context.validate_chlo_result()->cached_network_params));
+ out->SetValue(kSTTL, config.expiry_time.AbsoluteDifference(now).ToSeconds());
+ if (replay_protection_) {
+ out->SetStringPiece(kServerNonceTag,
+ NewServerNonce(context.rand(), context.info().now));
+ }
+
+ // Send client the reject reason for debugging purposes.
+ QUICHE_DCHECK_LT(0u, reject_reasons.size());
+ out->SetVector(kRREJ, reject_reasons);
+
+ // The client may have requested a certificate chain.
+ if (!ClientDemandsX509Proof(context.client_hello())) {
+ QUIC_BUG(quic_bug_10630_4)
+ << "x509 certificates not supported in proof demand";
+ return;
+ }
+
+ absl::string_view client_cached_cert_hashes;
+ if (context.client_hello().GetStringPiece(kCCRT,
+ &client_cached_cert_hashes)) {
+ context.params()->client_cached_cert_hashes =
+ std::string(client_cached_cert_hashes);
+ } else {
+ context.params()->client_cached_cert_hashes.clear();
+ }
+
+ const std::string compressed = CompressChain(
+ context.compressed_certs_cache(), context.signed_config()->chain,
+ context.params()->client_cached_cert_hashes);
+
+ QUICHE_DCHECK_GT(context.chlo_packet_size(), context.client_hello().size());
+ // kREJOverheadBytes is a very rough estimate of how much of a REJ
+ // message is taken up by things other than the certificates.
+ // STK: 56 bytes
+ // SNO: 56 bytes
+ // SCFG
+ // SCID: 16 bytes
+ // PUBS: 38 bytes
+ const size_t kREJOverheadBytes = 166;
+ // max_unverified_size is the number of bytes that the certificate chain,
+ // signature, and (optionally) signed certificate timestamp can consume before
+ // we will demand a valid source-address token.
+ const size_t max_unverified_size =
+ chlo_multiplier_ *
+ (context.chlo_packet_size() - context.total_framing_overhead()) -
+ kREJOverheadBytes;
+ static_assert(kClientHelloMinimumSize * kMultiplier >= kREJOverheadBytes,
+ "overhead calculation may underflow");
+ bool should_return_sct =
+ context.params()->sct_supported_by_client && enable_serving_sct_;
+ const std::string& cert_sct = context.signed_config()->proof.leaf_cert_scts;
+ const size_t sct_size = should_return_sct ? cert_sct.size() : 0;
+ const size_t total_size = context.signed_config()->proof.signature.size() +
+ compressed.size() + sct_size;
+ if (context.info().valid_source_address_token ||
+ total_size < max_unverified_size) {
+ out->SetStringPiece(kCertificateTag, compressed);
+ out->SetStringPiece(kPROF, context.signed_config()->proof.signature);
+ if (should_return_sct) {
+ if (cert_sct.empty()) {
+ // Log SNI and subject name for the leaf cert if its SCT is empty.
+ // This is for debugging b/28342827.
+ const std::vector<std::string>& certs =
+ context.signed_config()->chain->certs;
+ std::string ca_subject;
+ if (!certs.empty()) {
+ std::unique_ptr<CertificateView> view =
+ CertificateView::ParseSingleCertificate(certs[0]);
+ if (view != nullptr) {
+ absl::optional<std::string> maybe_ca_subject =
+ view->GetHumanReadableSubject();
+ if (maybe_ca_subject.has_value()) {
+ ca_subject = *maybe_ca_subject;
+ }
+ }
+ }
+ QUIC_LOG_EVERY_N_SEC(WARNING, 60)
+ << "SCT is expected but it is empty. sni: '"
+ << context.params()->sni << "' cert subject: '" << ca_subject
+ << "'";
+ } else {
+ out->SetStringPiece(kCertificateSCTTag, cert_sct);
+ }
+ }
+ } else {
+ QUIC_LOG_EVERY_N_SEC(WARNING, 60)
+ << "Sending inchoate REJ for hostname: " << context.info().sni
+ << " signature: " << context.signed_config()->proof.signature.size()
+ << " cert: " << compressed.size() << " sct:" << sct_size
+ << " total: " << total_size << " max: " << max_unverified_size;
+ }
+}
+
+std::string QuicCryptoServerConfig::CompressChain(
+ QuicCompressedCertsCache* compressed_certs_cache,
+ const quiche::QuicheReferenceCountedPointer<ProofSource::Chain>& chain,
+ const std::string& client_cached_cert_hashes) {
+ // Check whether the compressed certs is available in the cache.
+ QUICHE_DCHECK(compressed_certs_cache);
+ const std::string* cached_value = compressed_certs_cache->GetCompressedCert(
+ chain, client_cached_cert_hashes);
+ if (cached_value) {
+ return *cached_value;
+ }
+ std::string compressed =
+ CertCompressor::CompressChain(chain->certs, client_cached_cert_hashes);
+ // Insert the newly compressed cert to cache.
+ compressed_certs_cache->Insert(chain, client_cached_cert_hashes, compressed);
+ return compressed;
+}
+
+quiche::QuicheReferenceCountedPointer<QuicCryptoServerConfig::Config>
+QuicCryptoServerConfig::ParseConfigProtobuf(
+ const QuicServerConfigProtobuf& protobuf, bool is_fallback) const {
+ std::unique_ptr<CryptoHandshakeMessage> msg =
+ CryptoFramer::ParseMessage(protobuf.config());
+
+ if (!msg) {
+ QUIC_LOG(WARNING) << "Failed to parse server config message";
+ return nullptr;
+ }
+
+ if (msg->tag() != kSCFG) {
+ QUIC_LOG(WARNING) << "Server config message has tag " << msg->tag()
+ << ", but expected " << kSCFG;
+ return nullptr;
+ }
+
+ quiche::QuicheReferenceCountedPointer<Config> config(new Config);
+ config->serialized = protobuf.config();
+ config->source_address_token_boxer = &source_address_token_boxer_;
+
+ if (protobuf.has_primary_time()) {
+ config->primary_time =
+ QuicWallTime::FromUNIXSeconds(protobuf.primary_time());
+ }
+
+ config->priority = protobuf.priority();
+
+ absl::string_view scid;
+ if (!msg->GetStringPiece(kSCID, &scid)) {
+ QUIC_LOG(WARNING) << "Server config message is missing SCID";
+ return nullptr;
+ }
+ QUICHE_DCHECK(!scid.empty());
+ config->id = std::string(scid);
+
+ if (msg->GetTaglist(kAEAD, &config->aead) != QUIC_NO_ERROR) {
+ QUIC_LOG(WARNING) << "Server config message is missing AEAD";
+ return nullptr;
+ }
+
+ QuicTagVector kexs_tags;
+ if (msg->GetTaglist(kKEXS, &kexs_tags) != QUIC_NO_ERROR) {
+ QUIC_LOG(WARNING) << "Server config message is missing KEXS";
+ return nullptr;
+ }
+
+ absl::string_view orbit;
+ if (!msg->GetStringPiece(kORBT, &orbit)) {
+ QUIC_LOG(WARNING) << "Server config message is missing ORBT";
+ return nullptr;
+ }
+
+ if (orbit.size() != kOrbitSize) {
+ QUIC_LOG(WARNING) << "Orbit value in server config is the wrong length."
+ " Got "
+ << orbit.size() << " want " << kOrbitSize;
+ return nullptr;
+ }
+ static_assert(sizeof(config->orbit) == kOrbitSize, "incorrect orbit size");
+ memcpy(config->orbit, orbit.data(), sizeof(config->orbit));
+
+ QuicTagVector proof_demand_tags;
+ if (msg->GetTaglist(kPDMD, &proof_demand_tags) == QUIC_NO_ERROR) {
+ for (QuicTag tag : proof_demand_tags) {
+ if (tag == kCHID) {
+ config->channel_id_enabled = true;
+ break;
+ }
+ }
+ }
+
+ for (size_t i = 0; i < kexs_tags.size(); i++) {
+ const QuicTag tag = kexs_tags[i];
+ std::string private_key;
+
+ config->kexs.push_back(tag);
+
+ for (int j = 0; j < protobuf.key_size(); j++) {
+ const QuicServerConfigProtobuf::PrivateKey& key = protobuf.key(i);
+ if (key.tag() == tag) {
+ private_key = key.private_key();
+ break;
+ }
+ }
+
+ std::unique_ptr<AsynchronousKeyExchange> ka =
+ key_exchange_source_->Create(config->id, is_fallback, tag, private_key);
+ if (!ka) {
+ return nullptr;
+ }
+ for (const auto& key_exchange : config->key_exchanges) {
+ if (key_exchange->type() == tag) {
+ QUIC_LOG(WARNING) << "Duplicate key exchange in config: " << tag;
+ return nullptr;
+ }
+ }
+
+ config->key_exchanges.push_back(std::move(ka));
+ }
+
+ uint64_t expiry_seconds;
+ if (msg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR) {
+ QUIC_LOG(WARNING) << "Server config message is missing EXPY";
+ return nullptr;
+ }
+ config->expiry_time = QuicWallTime::FromUNIXSeconds(expiry_seconds);
+
+ return config;
+}
+
+void QuicCryptoServerConfig::set_replay_protection(bool on) {
+ replay_protection_ = on;
+}
+
+void QuicCryptoServerConfig::set_chlo_multiplier(size_t multiplier) {
+ chlo_multiplier_ = multiplier;
+}
+
+void QuicCryptoServerConfig::set_source_address_token_future_secs(
+ uint32_t future_secs) {
+ source_address_token_future_secs_ = future_secs;
+}
+
+void QuicCryptoServerConfig::set_source_address_token_lifetime_secs(
+ uint32_t lifetime_secs) {
+ source_address_token_lifetime_secs_ = lifetime_secs;
+}
+
+void QuicCryptoServerConfig::set_enable_serving_sct(bool enable_serving_sct) {
+ enable_serving_sct_ = enable_serving_sct;
+}
+
+void QuicCryptoServerConfig::AcquirePrimaryConfigChangedCb(
+ std::unique_ptr<PrimaryConfigChangedCallback> cb) {
+ QuicWriterMutexLock locked(&configs_lock_);
+ primary_config_changed_cb_ = std::move(cb);
+}
+
+std::string QuicCryptoServerConfig::NewSourceAddressToken(
+ const CryptoSecretBoxer& crypto_secret_boxer,
+ const SourceAddressTokens& previous_tokens, const QuicIpAddress& ip,
+ QuicRandom* rand, QuicWallTime now,
+ const CachedNetworkParameters* cached_network_params) const {
+ SourceAddressTokens source_address_tokens;
+ SourceAddressToken* source_address_token = source_address_tokens.add_tokens();
+ source_address_token->set_ip(ip.DualStacked().ToPackedString());
+ source_address_token->set_timestamp(now.ToUNIXSeconds());
+ if (cached_network_params != nullptr) {
+ *(source_address_token->mutable_cached_network_parameters()) =
+ *cached_network_params;
+ }
+
+ // Append previous tokens.
+ for (const SourceAddressToken& token : previous_tokens.tokens()) {
+ if (source_address_tokens.tokens_size() > kMaxTokenAddresses) {
+ break;
+ }
+
+ if (token.ip() == source_address_token->ip()) {
+ // It's for the same IP address.
+ continue;
+ }
+
+ if (ValidateSourceAddressTokenTimestamp(token, now) != HANDSHAKE_OK) {
+ continue;
+ }
+
+ *(source_address_tokens.add_tokens()) = token;
+ }
+
+ return crypto_secret_boxer.Box(rand,
+ source_address_tokens.SerializeAsString());
+}
+
+int QuicCryptoServerConfig::NumberOfConfigs() const {
+ QuicReaderMutexLock locked(&configs_lock_);
+ return configs_.size();
+}
+
+ProofSource* QuicCryptoServerConfig::proof_source() const {
+ return proof_source_.get();
+}
+
+SSL_CTX* QuicCryptoServerConfig::ssl_ctx() const { return ssl_ctx_.get(); }
+
+HandshakeFailureReason QuicCryptoServerConfig::ParseSourceAddressToken(
+ const CryptoSecretBoxer& crypto_secret_boxer, absl::string_view token,
+ SourceAddressTokens& tokens) const {
+ std::string storage;
+ absl::string_view plaintext;
+ if (!crypto_secret_boxer.Unbox(token, &storage, &plaintext)) {
+ return SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE;
+ }
+
+ if (!tokens.ParseFromArray(plaintext.data(), plaintext.size())) {
+ // Some clients might still be using the old source token format so
+ // attempt to parse that format.
+ // TODO(rch): remove this code once the new format is ubiquitous.
+ SourceAddressToken token;
+ if (!token.ParseFromArray(plaintext.data(), plaintext.size())) {
+ return SOURCE_ADDRESS_TOKEN_PARSE_FAILURE;
+ }
+ *tokens.add_tokens() = token;
+ }
+
+ return HANDSHAKE_OK;
+}
+
+HandshakeFailureReason QuicCryptoServerConfig::ValidateSourceAddressTokens(
+ const SourceAddressTokens& source_address_tokens, const QuicIpAddress& ip,
+ QuicWallTime now, CachedNetworkParameters* cached_network_params) const {
+ HandshakeFailureReason reason =
+ SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE;
+ for (const SourceAddressToken& token : source_address_tokens.tokens()) {
+ reason = ValidateSingleSourceAddressToken(token, ip, now);
+ if (reason == HANDSHAKE_OK) {
+ if (cached_network_params != nullptr &&
+ token.has_cached_network_parameters()) {
+ *cached_network_params = token.cached_network_parameters();
+ }
+ break;
+ }
+ }
+ return reason;
+}
+
+HandshakeFailureReason QuicCryptoServerConfig::ValidateSingleSourceAddressToken(
+ const SourceAddressToken& source_address_token, const QuicIpAddress& ip,
+ QuicWallTime now) const {
+ if (source_address_token.ip() != ip.DualStacked().ToPackedString()) {
+ // It's for a different IP address.
+ return SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE;
+ }
+
+ return ValidateSourceAddressTokenTimestamp(source_address_token, now);
+}
+
+HandshakeFailureReason
+QuicCryptoServerConfig::ValidateSourceAddressTokenTimestamp(
+ const SourceAddressToken& source_address_token, QuicWallTime now) const {
+ const QuicWallTime timestamp(
+ QuicWallTime::FromUNIXSeconds(source_address_token.timestamp()));
+ const QuicTime::Delta delta(now.AbsoluteDifference(timestamp));
+
+ if (now.IsBefore(timestamp) &&
+ delta.ToSeconds() > source_address_token_future_secs_) {
+ return SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE;
+ }
+
+ if (now.IsAfter(timestamp) &&
+ delta.ToSeconds() > source_address_token_lifetime_secs_) {
+ return SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE;
+ }
+
+ return HANDSHAKE_OK;
+}
+
+// kServerNoncePlaintextSize is the number of bytes in an unencrypted server
+// nonce.
+static const size_t kServerNoncePlaintextSize =
+ 4 /* timestamp */ + 20 /* random bytes */;
+
+std::string QuicCryptoServerConfig::NewServerNonce(QuicRandom* rand,
+ QuicWallTime now) const {
+ const uint32_t timestamp = static_cast<uint32_t>(now.ToUNIXSeconds());
+
+ uint8_t server_nonce[kServerNoncePlaintextSize];
+ static_assert(sizeof(server_nonce) > sizeof(timestamp), "nonce too small");
+ server_nonce[0] = static_cast<uint8_t>(timestamp >> 24);
+ server_nonce[1] = static_cast<uint8_t>(timestamp >> 16);
+ server_nonce[2] = static_cast<uint8_t>(timestamp >> 8);
+ server_nonce[3] = static_cast<uint8_t>(timestamp);
+ rand->RandBytes(&server_nonce[sizeof(timestamp)],
+ sizeof(server_nonce) - sizeof(timestamp));
+
+ return server_nonce_boxer_.Box(
+ rand, absl::string_view(reinterpret_cast<char*>(server_nonce),
+ sizeof(server_nonce)));
+}
+
+bool QuicCryptoServerConfig::ValidateExpectedLeafCertificate(
+ const CryptoHandshakeMessage& client_hello,
+ const std::vector<std::string>& certs) const {
+ if (certs.empty()) {
+ return false;
+ }
+
+ uint64_t hash_from_client;
+ if (client_hello.GetUint64(kXLCT, &hash_from_client) != QUIC_NO_ERROR) {
+ return false;
+ }
+ return CryptoUtils::ComputeLeafCertHash(certs.at(0)) == hash_from_client;
+}
+
+bool QuicCryptoServerConfig::IsNextConfigReady(QuicWallTime now) const {
+ return !next_config_promotion_time_.IsZero() &&
+ !next_config_promotion_time_.IsAfter(now);
+}
+
+QuicCryptoServerConfig::Config::Config()
+ : channel_id_enabled(false),
+ is_primary(false),
+ primary_time(QuicWallTime::Zero()),
+ expiry_time(QuicWallTime::Zero()),
+ priority(0),
+ source_address_token_boxer(nullptr) {}
+
+QuicCryptoServerConfig::Config::~Config() {}
+
+QuicSignedServerConfig::QuicSignedServerConfig() {}
+QuicSignedServerConfig::~QuicSignedServerConfig() {}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_server_config.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_server_config.h
new file mode 100644
index 00000000000..c3febdc63b8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_server_config.h
@@ -0,0 +1,948 @@
+// Copyright 2013 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_SERVER_CONFIG_H_
+#define QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_SERVER_CONFIG_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "openssl/base.h"
+#include "quiche/quic/core/crypto/crypto_handshake.h"
+#include "quiche/quic/core/crypto/crypto_handshake_message.h"
+#include "quiche/quic/core/crypto/crypto_protocol.h"
+#include "quiche/quic/core/crypto/crypto_secret_boxer.h"
+#include "quiche/quic/core/crypto/key_exchange.h"
+#include "quiche/quic/core/crypto/proof_source.h"
+#include "quiche/quic/core/crypto/quic_compressed_certs_cache.h"
+#include "quiche/quic/core/crypto/quic_crypto_proof.h"
+#include "quiche/quic/core/proto/cached_network_parameters_proto.h"
+#include "quiche/quic/core/proto/source_address_token_proto.h"
+#include "quiche/quic/core/quic_time.h"
+#include "quiche/quic/platform/api/quic_export.h"
+#include "quiche/quic/platform/api/quic_mutex.h"
+#include "quiche/quic/platform/api/quic_socket_address.h"
+#include "quiche/common/platform/api/quiche_reference_counted.h"
+
+namespace quic {
+
+class CryptoHandshakeMessage;
+class ProofSource;
+class QuicClock;
+class QuicRandom;
+class QuicServerConfigProtobuf;
+struct QuicSignedServerConfig;
+
+// ClientHelloInfo contains information about a client hello message that is
+// only kept for as long as it's being processed.
+struct QUIC_EXPORT_PRIVATE ClientHelloInfo {
+ ClientHelloInfo(const QuicIpAddress& in_client_ip, QuicWallTime in_now);
+ ClientHelloInfo(const ClientHelloInfo& other);
+ ~ClientHelloInfo();
+
+ // Inputs to EvaluateClientHello.
+ const QuicIpAddress client_ip;
+ const QuicWallTime now;
+
+ // Outputs from EvaluateClientHello.
+ bool valid_source_address_token;
+ absl::string_view sni;
+ absl::string_view client_nonce;
+ absl::string_view server_nonce;
+ absl::string_view user_agent_id;
+ SourceAddressTokens source_address_tokens;
+
+ // Errors from EvaluateClientHello.
+ std::vector<uint32_t> reject_reasons;
+ static_assert(sizeof(QuicTag) == sizeof(uint32_t), "header out of sync");
+};
+
+namespace test {
+class QuicCryptoServerConfigPeer;
+} // namespace test
+
+// Hook that allows application code to subscribe to primary config changes.
+class QUIC_EXPORT_PRIVATE PrimaryConfigChangedCallback {
+ public:
+ PrimaryConfigChangedCallback();
+ PrimaryConfigChangedCallback(const PrimaryConfigChangedCallback&) = delete;
+ PrimaryConfigChangedCallback& operator=(const PrimaryConfigChangedCallback&) =
+ delete;
+ virtual ~PrimaryConfigChangedCallback();
+ virtual void Run(const std::string& scid) = 0;
+};
+
+// Callback used to accept the result of the |client_hello| validation step.
+class QUIC_EXPORT_PRIVATE ValidateClientHelloResultCallback {
+ public:
+ // Opaque token that holds information about the client_hello and
+ // its validity. Can be interpreted by calling ProcessClientHello.
+ struct QUIC_EXPORT_PRIVATE Result : public quiche::QuicheReferenceCounted {
+ Result(const CryptoHandshakeMessage& in_client_hello,
+ QuicIpAddress in_client_ip, QuicWallTime in_now);
+
+ CryptoHandshakeMessage client_hello;
+ ClientHelloInfo info;
+ QuicErrorCode error_code;
+ std::string error_details;
+
+ // Populated if the CHLO STK contained a CachedNetworkParameters proto.
+ CachedNetworkParameters cached_network_params;
+
+ protected:
+ ~Result() override;
+ };
+
+ ValidateClientHelloResultCallback();
+ ValidateClientHelloResultCallback(const ValidateClientHelloResultCallback&) =
+ delete;
+ ValidateClientHelloResultCallback& operator=(
+ const ValidateClientHelloResultCallback&) = delete;
+ virtual ~ValidateClientHelloResultCallback();
+ virtual void Run(quiche::QuicheReferenceCountedPointer<Result> result,
+ std::unique_ptr<ProofSource::Details> details) = 0;
+};
+
+// Callback used to accept the result of the ProcessClientHello method.
+class QUIC_EXPORT_PRIVATE ProcessClientHelloResultCallback {
+ public:
+ ProcessClientHelloResultCallback();
+ ProcessClientHelloResultCallback(const ProcessClientHelloResultCallback&) =
+ delete;
+ ProcessClientHelloResultCallback& operator=(
+ const ProcessClientHelloResultCallback&) = delete;
+ virtual ~ProcessClientHelloResultCallback();
+ virtual void Run(QuicErrorCode error, const std::string& error_details,
+ std::unique_ptr<CryptoHandshakeMessage> message,
+ std::unique_ptr<DiversificationNonce> diversification_nonce,
+ std::unique_ptr<ProofSource::Details> details) = 0;
+};
+
+// Callback used to receive the results of a call to
+// BuildServerConfigUpdateMessage.
+class QUIC_EXPORT_PRIVATE BuildServerConfigUpdateMessageResultCallback {
+ public:
+ BuildServerConfigUpdateMessageResultCallback() = default;
+ virtual ~BuildServerConfigUpdateMessageResultCallback() {}
+ BuildServerConfigUpdateMessageResultCallback(
+ const BuildServerConfigUpdateMessageResultCallback&) = delete;
+ BuildServerConfigUpdateMessageResultCallback& operator=(
+ const BuildServerConfigUpdateMessageResultCallback&) = delete;
+ virtual void Run(bool ok, const CryptoHandshakeMessage& message) = 0;
+};
+
+// Object that is interested in built rejections (which include REJ, SREJ and
+// cheap SREJ).
+class QUIC_EXPORT_PRIVATE RejectionObserver {
+ public:
+ RejectionObserver() = default;
+ virtual ~RejectionObserver() {}
+ RejectionObserver(const RejectionObserver&) = delete;
+ RejectionObserver& operator=(const RejectionObserver&) = delete;
+ // Called after a rejection is built.
+ virtual void OnRejectionBuilt(const std::vector<uint32_t>& reasons,
+ CryptoHandshakeMessage* out) const = 0;
+};
+
+// Factory for creating KeyExchange objects.
+class QUIC_EXPORT_PRIVATE KeyExchangeSource {
+ public:
+ virtual ~KeyExchangeSource() = default;
+
+ // Returns the default KeyExchangeSource.
+ static std::unique_ptr<KeyExchangeSource> Default();
+
+ // Create a new KeyExchange using the curve specified by |type| using the
+ // specified private key. |private_key| may be empty for key-exchange
+ // mechanisms which do not hold the private key in-process. If |is_fallback|
+ // is set, |private_key| is required to be set, and a local key-exchange
+ // object should be returned.
+ virtual std::unique_ptr<AsynchronousKeyExchange> Create(
+ std::string server_config_id, bool is_fallback, QuicTag type,
+ absl::string_view private_key) = 0;
+};
+
+// QuicCryptoServerConfig contains the crypto configuration of a QUIC server.
+// Unlike a client, a QUIC server can have multiple configurations active in
+// order to support clients resuming with a previous configuration.
+// TODO(agl): when adding configurations at runtime is added, this object will
+// need to consider locking.
+class QUIC_EXPORT_PRIVATE QuicCryptoServerConfig {
+ public:
+ // ConfigOptions contains options for generating server configs.
+ struct QUIC_EXPORT_PRIVATE ConfigOptions {
+ ConfigOptions();
+ ConfigOptions(const ConfigOptions& other);
+ ~ConfigOptions();
+
+ // expiry_time is the time, in UNIX seconds, when the server config will
+ // expire. If unset, it defaults to the current time plus six months.
+ QuicWallTime expiry_time;
+ // channel_id_enabled controls whether the server config will indicate
+ // support for ChannelIDs.
+ bool channel_id_enabled;
+ // id contains the server config id for the resulting config. If empty, a
+ // random id is generated.
+ std::string id;
+ // orbit contains the kOrbitSize bytes of the orbit value for the server
+ // config. If |orbit| is empty then a random orbit is generated.
+ std::string orbit;
+ // p256 determines whether a P-256 public key will be included in the
+ // server config. Note that this breaks deterministic server-config
+ // generation since P-256 key generation doesn't use the QuicRandom given
+ // to GenerateConfig().
+ bool p256;
+ };
+
+ // |source_address_token_secret|: secret key material used for encrypting and
+ // decrypting source address tokens. It can be of any length as it is fed
+ // into a KDF before use. In tests, use TESTING.
+ // |server_nonce_entropy|: an entropy source used to generate the orbit and
+ // key for server nonces, which are always local to a given instance of a
+ // server. Not owned.
+ // |proof_source|: provides certificate chains and signatures.
+ // |key_exchange_source|: provides key-exchange functionality.
+ QuicCryptoServerConfig(
+ absl::string_view source_address_token_secret,
+ QuicRandom* server_nonce_entropy,
+ std::unique_ptr<ProofSource> proof_source,
+ std::unique_ptr<KeyExchangeSource> key_exchange_source);
+ QuicCryptoServerConfig(const QuicCryptoServerConfig&) = delete;
+ QuicCryptoServerConfig& operator=(const QuicCryptoServerConfig&) = delete;
+ ~QuicCryptoServerConfig();
+
+ // TESTING is a magic parameter for passing to the constructor in tests.
+ static const char TESTING[];
+
+ // Generates a QuicServerConfigProtobuf protobuf suitable for
+ // AddConfig and SetConfigs.
+ static QuicServerConfigProtobuf GenerateConfig(QuicRandom* rand,
+ const QuicClock* clock,
+ const ConfigOptions& options);
+
+ // AddConfig adds a QuicServerConfigProtobuf to the available configurations.
+ // It returns the SCFG message from the config if successful. |now| is used in
+ // conjunction with |protobuf->primary_time()| to determine whether the
+ // config should be made primary.
+ std::unique_ptr<CryptoHandshakeMessage> AddConfig(
+ const QuicServerConfigProtobuf& protobuf, QuicWallTime now);
+
+ // AddDefaultConfig calls GenerateConfig to create a config and then calls
+ // AddConfig to add it. See the comment for |GenerateConfig| for details of
+ // the arguments.
+ std::unique_ptr<CryptoHandshakeMessage> AddDefaultConfig(
+ QuicRandom* rand, const QuicClock* clock, const ConfigOptions& options);
+
+ // SetConfigs takes a vector of config protobufs and the current time.
+ // Configs are assumed to be uniquely identified by their server config ID.
+ // Previously unknown configs are added and possibly made the primary config
+ // depending on their |primary_time| and the value of |now|. Configs that are
+ // known, but are missing from the protobufs are deleted, unless they are
+ // currently the primary config. SetConfigs returns false if any errors were
+ // encountered and no changes to the QuicCryptoServerConfig will occur.
+ bool SetConfigs(const std::vector<QuicServerConfigProtobuf>& protobufs,
+ const QuicServerConfigProtobuf* fallback_protobuf,
+ QuicWallTime now);
+
+ // SetSourceAddressTokenKeys sets the keys to be tried, in order, when
+ // decrypting a source address token. Note that these keys are used *without*
+ // passing them through a KDF, in contradistinction to the
+ // |source_address_token_secret| argument to the constructor.
+ void SetSourceAddressTokenKeys(const std::vector<std::string>& keys);
+
+ // Get the server config ids for all known configs.
+ std::vector<std::string> GetConfigIds() const;
+
+ // Checks |client_hello| for gross errors and determines whether it can be
+ // shown to be fresh (i.e. not a replay). The result of the validation step
+ // must be interpreted by calling QuicCryptoServerConfig::ProcessClientHello
+ // from the done_cb.
+ //
+ // ValidateClientHello may invoke the done_cb before unrolling the
+ // stack if it is able to assess the validity of the client_nonce
+ // without asynchronous operations.
+ //
+ // client_hello: the incoming client hello message.
+ // client_ip: the IP address of the client, which is used to generate and
+ // validate source-address tokens.
+ // server_address: the IP address and port of the server. The IP address and
+ // port may be used for certificate selection.
+ // version: protocol version used for this connection.
+ // clock: used to validate client nonces and ephemeral keys.
+ // signed_config: in/out parameter to which will be written the crypto proof
+ // used in reply to a proof demand. The pointed-to-object must live until
+ // the callback is invoked.
+ // done_cb: single-use callback that accepts an opaque
+ // ValidatedClientHelloMsg token that holds information about
+ // the client hello. The callback will always be called exactly
+ // once, either under the current call stack, or after the
+ // completion of an asynchronous operation.
+ void ValidateClientHello(
+ const CryptoHandshakeMessage& client_hello,
+ const QuicSocketAddress& client_address,
+ const QuicSocketAddress& server_address, QuicTransportVersion version,
+ const QuicClock* clock,
+ quiche::QuicheReferenceCountedPointer<QuicSignedServerConfig>
+ signed_config,
+ std::unique_ptr<ValidateClientHelloResultCallback> done_cb) const;
+
+ // ProcessClientHello processes |client_hello| and decides whether to accept
+ // or reject the connection. If the connection is to be accepted, |done_cb| is
+ // invoked with the contents of the ServerHello and QUIC_NO_ERROR. Otherwise
+ // |done_cb| is called with a REJ or SREJ message and QUIC_NO_ERROR.
+ //
+ // validate_chlo_result: Output from the asynchronous call to
+ // ValidateClientHello. Contains the client hello message and
+ // information about it.
+ // reject_only: Only generate rejections, not server hello messages.
+ // connection_id: the ConnectionId for the connection, which is used in key
+ // derivation.
+ // server_ip: the IP address of the server. The IP address may be used for
+ // certificate selection.
+ // client_address: the IP address and port of the client. The IP address is
+ // used to generate and validate source-address tokens.
+ // version: version of the QUIC protocol in use for this connection
+ // supported_versions: versions of the QUIC protocol that this server
+ // supports.
+ // clock: used to validate client nonces and ephemeral keys.
+ // rand: an entropy source
+ // compressed_certs_cache: the cache that caches a set of most recently used
+ // certs. Owned by QuicDispatcher.
+ // params: the state of the handshake. This may be updated with a server
+ // nonce when we send a rejection.
+ // signed_config: output structure containing the crypto proof used in reply
+ // to a proof demand.
+ // total_framing_overhead: the total per-packet overhead for a stream frame
+ // chlo_packet_size: the size, in bytes, of the CHLO packet
+ // done_cb: the callback invoked on completion
+ void ProcessClientHello(
+ quiche::QuicheReferenceCountedPointer<
+ ValidateClientHelloResultCallback::Result>
+ validate_chlo_result,
+ bool reject_only, QuicConnectionId connection_id,
+ const QuicSocketAddress& server_address,
+ const QuicSocketAddress& client_address, ParsedQuicVersion version,
+ const ParsedQuicVersionVector& supported_versions, const QuicClock* clock,
+ QuicRandom* rand, QuicCompressedCertsCache* compressed_certs_cache,
+ quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters>
+ params,
+ quiche::QuicheReferenceCountedPointer<QuicSignedServerConfig>
+ signed_config,
+ QuicByteCount total_framing_overhead, QuicByteCount chlo_packet_size,
+ std::unique_ptr<ProcessClientHelloResultCallback> done_cb) const;
+
+ // BuildServerConfigUpdateMessage invokes |cb| with a SCUP message containing
+ // the current primary config, an up to date source-address token, and cert
+ // chain and proof in the case of secure QUIC. Passes true to |cb| if the
+ // message was generated successfully, and false otherwise. This method
+ // assumes ownership of |cb|.
+ //
+ // |cached_network_params| is optional, and can be nullptr.
+ void BuildServerConfigUpdateMessage(
+ QuicTransportVersion version, absl::string_view chlo_hash,
+ const SourceAddressTokens& previous_source_address_tokens,
+ const QuicSocketAddress& server_address,
+ const QuicSocketAddress& client_address, const QuicClock* clock,
+ QuicRandom* rand, QuicCompressedCertsCache* compressed_certs_cache,
+ const QuicCryptoNegotiatedParameters& params,
+ const CachedNetworkParameters* cached_network_params,
+ std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb) const;
+
+ // set_replay_protection controls whether replay protection is enabled. If
+ // replay protection is disabled then no strike registers are needed and
+ // frontends can share an orbit value without a shared strike-register.
+ // However, an attacker can duplicate a handshake and cause a client's
+ // request to be processed twice.
+ void set_replay_protection(bool on);
+
+ // set_chlo_multiplier specifies the multiple of the CHLO message size
+ // that a REJ message must stay under when the client doesn't present a
+ // valid source-address token.
+ void set_chlo_multiplier(size_t multiplier);
+
+ // When sender is allowed to not pad client hello (not standards compliant),
+ // we need to disable the client hello check.
+ void set_validate_chlo_size(bool new_value) {
+ validate_chlo_size_ = new_value;
+ }
+
+ // Returns whether the sender is allowed to not pad the client hello.
+ bool validate_chlo_size() const { return validate_chlo_size_; }
+
+ // When QUIC is tunneled through some other mechanism, source token validation
+ // may be disabled. Do not disable it if you are not providing other
+ // protection. (|true| protects against UDP amplification attack.).
+ void set_validate_source_address_token(bool new_value) {
+ validate_source_address_token_ = new_value;
+ }
+
+ // set_source_address_token_future_secs sets the number of seconds into the
+ // future that source-address tokens will be accepted from. Since
+ // source-address tokens are authenticated, this should only happen if
+ // another, valid server has clock-skew.
+ void set_source_address_token_future_secs(uint32_t future_secs);
+
+ // set_source_address_token_lifetime_secs sets the number of seconds that a
+ // source-address token will be valid for.
+ void set_source_address_token_lifetime_secs(uint32_t lifetime_secs);
+
+ // set_enable_serving_sct enables or disables serving signed cert timestamp
+ // (RFC6962) in server hello.
+ void set_enable_serving_sct(bool enable_serving_sct);
+
+ // Set and take ownership of the callback to invoke on primary config changes.
+ void AcquirePrimaryConfigChangedCb(
+ std::unique_ptr<PrimaryConfigChangedCallback> cb);
+
+ // Returns the number of configs this object owns.
+ int NumberOfConfigs() const;
+
+ // NewSourceAddressToken returns a fresh source address token for the given
+ // IP address. |previous_tokens| is the received tokens, and can be empty.
+ // |cached_network_params| is optional, and can be nullptr.
+ std::string NewSourceAddressToken(
+ const CryptoSecretBoxer& crypto_secret_boxer,
+ const SourceAddressTokens& previous_tokens, const QuicIpAddress& ip,
+ QuicRandom* rand, QuicWallTime now,
+ const CachedNetworkParameters* cached_network_params) const;
+
+ // ParseSourceAddressToken parses the source address tokens contained in
+ // the encrypted |token|, and populates |tokens| with the parsed tokens.
+ // Returns HANDSHAKE_OK if |token| could be parsed, or the reason for the
+ // failure.
+ HandshakeFailureReason ParseSourceAddressToken(
+ const CryptoSecretBoxer& crypto_secret_boxer, absl::string_view token,
+ SourceAddressTokens& tokens) const;
+
+ // ValidateSourceAddressTokens returns HANDSHAKE_OK if the source address
+ // tokens in |tokens| contain a valid and timely token for the IP address
+ // |ip| given that the current time is |now|. Otherwise it returns the
+ // reason for failure. |cached_network_params| is populated if the valid
+ // token contains a CachedNetworkParameters proto.
+ HandshakeFailureReason ValidateSourceAddressTokens(
+ const SourceAddressTokens& tokens, const QuicIpAddress& ip,
+ QuicWallTime now, CachedNetworkParameters* cached_network_params) const;
+
+ // Callers retain the ownership of |rejection_observer| which must outlive the
+ // config.
+ void set_rejection_observer(RejectionObserver* rejection_observer) {
+ rejection_observer_ = rejection_observer;
+ }
+
+ ProofSource* proof_source() const;
+
+ SSL_CTX* ssl_ctx() const;
+
+ // Pre-shared key used during the handshake.
+ const std::string& pre_shared_key() const { return pre_shared_key_; }
+ void set_pre_shared_key(absl::string_view psk) {
+ pre_shared_key_ = std::string(psk);
+ }
+
+ bool pad_rej() const { return pad_rej_; }
+ void set_pad_rej(bool new_value) { pad_rej_ = new_value; }
+
+ bool pad_shlo() const { return pad_shlo_; }
+ void set_pad_shlo(bool new_value) { pad_shlo_ = new_value; }
+
+ const CryptoSecretBoxer& source_address_token_boxer() const {
+ return source_address_token_boxer_;
+ }
+
+ private:
+ friend class test::QuicCryptoServerConfigPeer;
+ friend struct QuicSignedServerConfig;
+
+ // Config represents a server config: a collection of preferences and
+ // Diffie-Hellman public values.
+ class QUIC_EXPORT_PRIVATE Config : public QuicCryptoConfig,
+ public quiche::QuicheReferenceCounted {
+ public:
+ Config();
+ Config(const Config&) = delete;
+ Config& operator=(const Config&) = delete;
+
+ // TODO(rtenneti): since this is a class, we should probably do
+ // getters/setters here.
+ // |serialized| contains the bytes of this server config, suitable for
+ // sending on the wire.
+ std::string serialized;
+ // id contains the SCID of this server config.
+ std::string id;
+ // orbit contains the orbit value for this config: an opaque identifier
+ // used to identify clusters of server frontends.
+ unsigned char orbit[kOrbitSize];
+
+ // key_exchanges contains key exchange objects. The values correspond,
+ // one-to-one, with the tags in |kexs| from the parent class.
+ std::vector<std::unique_ptr<AsynchronousKeyExchange>> key_exchanges;
+
+ // channel_id_enabled is true if the config in |serialized| specifies that
+ // ChannelIDs are supported.
+ bool channel_id_enabled;
+
+ // is_primary is true if this config is the one that we'll give out to
+ // clients as the current one.
+ bool is_primary;
+
+ // primary_time contains the timestamp when this config should become the
+ // primary config. A value of QuicWallTime::Zero() means that this config
+ // will not be promoted at a specific time.
+ QuicWallTime primary_time;
+
+ // expiry_time contains the timestamp when this config expires.
+ QuicWallTime expiry_time;
+
+ // Secondary sort key for use when selecting primary configs and
+ // there are multiple configs with the same primary time.
+ // Smaller numbers mean higher priority.
+ uint64_t priority;
+
+ // source_address_token_boxer_ is used to protect the
+ // source-address tokens that are given to clients.
+ // Points to either source_address_token_boxer_storage or the
+ // default boxer provided by QuicCryptoServerConfig.
+ const CryptoSecretBoxer* source_address_token_boxer;
+
+ // Holds the override source_address_token_boxer instance if the
+ // Config is not using the default source address token boxer
+ // instance provided by QuicCryptoServerConfig.
+ std::unique_ptr<CryptoSecretBoxer> source_address_token_boxer_storage;
+
+ private:
+ ~Config() override;
+ };
+
+ using ConfigMap =
+ std::map<ServerConfigID, quiche::QuicheReferenceCountedPointer<Config>>;
+
+ // Get a ref to the config with a given server config id.
+ quiche::QuicheReferenceCountedPointer<Config> GetConfigWithScid(
+ absl::string_view requested_scid) const
+ QUIC_SHARED_LOCKS_REQUIRED(configs_lock_);
+
+ // A snapshot of the configs associated with an in-progress handshake.
+ struct QUIC_EXPORT_PRIVATE Configs {
+ quiche::QuicheReferenceCountedPointer<Config> requested;
+ quiche::QuicheReferenceCountedPointer<Config> primary;
+ quiche::QuicheReferenceCountedPointer<Config> fallback;
+ };
+
+ // Get a snapshot of the current configs associated with a handshake. If this
+ // method was called earlier in this handshake |old_primary_config| should be
+ // set to the primary config returned from that invocation, otherwise nullptr.
+ //
+ // Returns true if any configs are loaded. If false is returned, |configs| is
+ // not modified.
+ bool GetCurrentConfigs(
+ const QuicWallTime& now, absl::string_view requested_scid,
+ quiche::QuicheReferenceCountedPointer<Config> old_primary_config,
+ Configs* configs) const;
+
+ // ConfigPrimaryTimeLessThan returns true if a->primary_time <
+ // b->primary_time.
+ static bool ConfigPrimaryTimeLessThan(
+ const quiche::QuicheReferenceCountedPointer<Config>& a,
+ const quiche::QuicheReferenceCountedPointer<Config>& b);
+
+ // SelectNewPrimaryConfig reevaluates the primary config based on the
+ // "primary_time" deadlines contained in each.
+ void SelectNewPrimaryConfig(QuicWallTime now) const
+ QUIC_EXCLUSIVE_LOCKS_REQUIRED(configs_lock_);
+
+ // EvaluateClientHello checks |client_hello_state->client_hello| for gross
+ // errors and determines whether it is fresh (i.e. not a replay). The results
+ // are written to |client_hello_state->info|.
+ void EvaluateClientHello(
+ const QuicSocketAddress& server_address,
+ const QuicSocketAddress& client_address, QuicTransportVersion version,
+ const Configs& configs,
+ quiche::QuicheReferenceCountedPointer<
+ ValidateClientHelloResultCallback::Result>
+ client_hello_state,
+ std::unique_ptr<ValidateClientHelloResultCallback> done_cb) const;
+
+ // Convenience class which carries the arguments passed to
+ // |ProcessClientHellp| along.
+ class QUIC_EXPORT_PRIVATE ProcessClientHelloContext {
+ public:
+ ProcessClientHelloContext(
+ quiche::QuicheReferenceCountedPointer<
+ ValidateClientHelloResultCallback::Result>
+ validate_chlo_result,
+ bool reject_only, QuicConnectionId connection_id,
+ const QuicSocketAddress& server_address,
+ const QuicSocketAddress& client_address, ParsedQuicVersion version,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicClock* clock, QuicRandom* rand,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters>
+ params,
+ quiche::QuicheReferenceCountedPointer<QuicSignedServerConfig>
+ signed_config,
+ QuicByteCount total_framing_overhead, QuicByteCount chlo_packet_size,
+ std::unique_ptr<ProcessClientHelloResultCallback> done_cb)
+ : validate_chlo_result_(validate_chlo_result),
+ reject_only_(reject_only),
+ connection_id_(connection_id),
+ server_address_(server_address),
+ client_address_(client_address),
+ version_(version),
+ supported_versions_(supported_versions),
+ clock_(clock),
+ rand_(rand),
+ compressed_certs_cache_(compressed_certs_cache),
+ params_(params),
+ signed_config_(signed_config),
+ total_framing_overhead_(total_framing_overhead),
+ chlo_packet_size_(chlo_packet_size),
+ done_cb_(std::move(done_cb)) {}
+
+ ~ProcessClientHelloContext();
+
+ // Invoke |done_cb_| with an error status
+ void Fail(QuicErrorCode error, const std::string& error_details);
+
+ // Invoke |done_cb_| with a success status
+ void Succeed(std::unique_ptr<CryptoHandshakeMessage> message,
+ std::unique_ptr<DiversificationNonce> diversification_nonce,
+ std::unique_ptr<ProofSource::Details> proof_source_details);
+
+ // Member accessors
+ quiche::QuicheReferenceCountedPointer<
+ ValidateClientHelloResultCallback::Result>
+ validate_chlo_result() const {
+ return validate_chlo_result_;
+ }
+ bool reject_only() const { return reject_only_; }
+ QuicConnectionId connection_id() const { return connection_id_; }
+ QuicSocketAddress server_address() const { return server_address_; }
+ QuicSocketAddress client_address() const { return client_address_; }
+ ParsedQuicVersion version() const { return version_; }
+ ParsedQuicVersionVector supported_versions() const {
+ return supported_versions_;
+ }
+ const QuicClock* clock() const { return clock_; }
+ QuicRandom* rand() const { return rand_; } // NOLINT
+ QuicCompressedCertsCache* compressed_certs_cache() const {
+ return compressed_certs_cache_;
+ }
+ quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters>
+ params() const {
+ return params_;
+ }
+ quiche::QuicheReferenceCountedPointer<QuicSignedServerConfig>
+ signed_config() const {
+ return signed_config_;
+ }
+ QuicByteCount total_framing_overhead() const {
+ return total_framing_overhead_;
+ }
+ QuicByteCount chlo_packet_size() const { return chlo_packet_size_; }
+
+ // Derived value accessors
+ const CryptoHandshakeMessage& client_hello() const {
+ return validate_chlo_result()->client_hello;
+ }
+ const ClientHelloInfo& info() const { return validate_chlo_result()->info; }
+ QuicTransportVersion transport_version() const {
+ return version().transport_version;
+ }
+
+ private:
+ const quiche::QuicheReferenceCountedPointer<
+ ValidateClientHelloResultCallback::Result>
+ validate_chlo_result_;
+ const bool reject_only_;
+ const QuicConnectionId connection_id_;
+ const QuicSocketAddress server_address_;
+ const QuicSocketAddress client_address_;
+ const ParsedQuicVersion version_;
+ const ParsedQuicVersionVector supported_versions_;
+ const QuicClock* const clock_;
+ QuicRandom* const rand_;
+ QuicCompressedCertsCache* const compressed_certs_cache_;
+ const quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters>
+ params_;
+ const quiche::QuicheReferenceCountedPointer<QuicSignedServerConfig>
+ signed_config_;
+ const QuicByteCount total_framing_overhead_;
+ const QuicByteCount chlo_packet_size_;
+ std::unique_ptr<ProcessClientHelloResultCallback> done_cb_;
+ };
+
+ // Callback class for bridging between ProcessClientHello and
+ // ProcessClientHelloAfterGetProof.
+ class ProcessClientHelloCallback;
+ friend class ProcessClientHelloCallback;
+
+ // Portion of ProcessClientHello which executes after GetProof.
+ void ProcessClientHelloAfterGetProof(
+ bool found_error,
+ std::unique_ptr<ProofSource::Details> proof_source_details,
+ std::unique_ptr<ProcessClientHelloContext> context,
+ const Configs& configs) const;
+
+ // Callback class for bridging between ProcessClientHelloAfterGetProof and
+ // ProcessClientHelloAfterCalculateSharedKeys.
+ class ProcessClientHelloAfterGetProofCallback;
+ friend class ProcessClientHelloAfterGetProofCallback;
+
+ // Portion of ProcessClientHello which executes after CalculateSharedKeys.
+ void ProcessClientHelloAfterCalculateSharedKeys(
+ bool found_error,
+ std::unique_ptr<ProofSource::Details> proof_source_details,
+ QuicTag key_exchange_type, std::unique_ptr<CryptoHandshakeMessage> out,
+ absl::string_view public_value,
+ std::unique_ptr<ProcessClientHelloContext> context,
+ const Configs& configs) const;
+
+ // Send a REJ which contains a different ServerConfig than the one the client
+ // originally used. This is necessary in cases where we discover in the
+ // middle of the handshake that the private key for the ServerConfig the
+ // client used is not accessible.
+ void SendRejectWithFallbackConfig(
+ std::unique_ptr<ProcessClientHelloContext> context,
+ quiche::QuicheReferenceCountedPointer<Config> fallback_config) const;
+
+ // Callback class for bridging between SendRejectWithFallbackConfig and
+ // SendRejectWithFallbackConfigAfterGetProof.
+ class SendRejectWithFallbackConfigCallback;
+ friend class SendRejectWithFallbackConfigCallback;
+
+ // Portion of ProcessClientHello which executes after GetProof in the case
+ // where we have received a CHLO but need to reject it due to the ServerConfig
+ // private keys being inaccessible.
+ void SendRejectWithFallbackConfigAfterGetProof(
+ bool found_error,
+ std::unique_ptr<ProofSource::Details> proof_source_details,
+ std::unique_ptr<ProcessClientHelloContext> context,
+ quiche::QuicheReferenceCountedPointer<Config> fallback_config) const;
+
+ // BuildRejectionAndRecordStats calls |BuildRejection| below and also informs
+ // the RejectionObserver.
+ void BuildRejectionAndRecordStats(const ProcessClientHelloContext& context,
+ const Config& config,
+ const std::vector<uint32_t>& reject_reasons,
+ CryptoHandshakeMessage* out) const;
+
+ // BuildRejection sets |out| to be a REJ message in reply to |client_hello|.
+ void BuildRejection(const ProcessClientHelloContext& context,
+ const Config& config,
+ const std::vector<uint32_t>& reject_reasons,
+ CryptoHandshakeMessage* out) const;
+
+ // CompressChain compresses the certificates in |chain->certs| and returns a
+ // compressed representation. |client_cached_cert_hashes| contains
+ // 64-bit, FNV-1a hashes of certificates that the peer already possesses.
+ static std::string CompressChain(
+ QuicCompressedCertsCache* compressed_certs_cache,
+ const quiche::QuicheReferenceCountedPointer<ProofSource::Chain>& chain,
+ const std::string& client_cached_cert_hashes);
+
+ // ParseConfigProtobuf parses the given config protobuf and returns a
+ // quiche::QuicheReferenceCountedPointer<Config> if successful. The caller
+ // adopts the reference to the Config. On error, ParseConfigProtobuf returns
+ // nullptr.
+ quiche::QuicheReferenceCountedPointer<Config> ParseConfigProtobuf(
+ const QuicServerConfigProtobuf& protobuf, bool is_fallback) const;
+
+ // ValidateSingleSourceAddressToken returns HANDSHAKE_OK if the source
+ // address token in |token| is a timely token for the IP address |ip|
+ // given that the current time is |now|. Otherwise it returns the reason
+ // for failure.
+ HandshakeFailureReason ValidateSingleSourceAddressToken(
+ const SourceAddressToken& token, const QuicIpAddress& ip,
+ QuicWallTime now) const;
+
+ // Returns HANDSHAKE_OK if the source address token in |token| is a timely
+ // token given that the current time is |now|. Otherwise it returns the
+ // reason for failure.
+ HandshakeFailureReason ValidateSourceAddressTokenTimestamp(
+ const SourceAddressToken& token, QuicWallTime now) const;
+
+ // NewServerNonce generates and encrypts a random nonce.
+ std::string NewServerNonce(QuicRandom* rand, QuicWallTime now) const;
+
+ // ValidateExpectedLeafCertificate checks the |client_hello| to see if it has
+ // an XLCT tag, and if so, verifies that its value matches the hash of the
+ // server's leaf certificate. |certs| is used to compare against the XLCT
+ // value. This method returns true if the XLCT tag is not present, or if the
+ // XLCT tag is present and valid. It returns false otherwise.
+ bool ValidateExpectedLeafCertificate(
+ const CryptoHandshakeMessage& client_hello,
+ const std::vector<std::string>& certs) const;
+
+ // Callback to receive the results of ProofSource::GetProof. Note: this
+ // callback has no cancellation support, since the lifetime of the ProofSource
+ // is controlled by this object via unique ownership. If that ownership
+ // stricture changes, this decision may need to be revisited.
+ class BuildServerConfigUpdateMessageProofSourceCallback
+ : public ProofSource::Callback {
+ public:
+ BuildServerConfigUpdateMessageProofSourceCallback(
+ const BuildServerConfigUpdateMessageProofSourceCallback&) = delete;
+ ~BuildServerConfigUpdateMessageProofSourceCallback() override;
+ void operator=(const BuildServerConfigUpdateMessageProofSourceCallback&) =
+ delete;
+ BuildServerConfigUpdateMessageProofSourceCallback(
+ const QuicCryptoServerConfig* config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ const QuicCryptoNegotiatedParameters& params,
+ CryptoHandshakeMessage message,
+ std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb);
+
+ void Run(
+ bool ok,
+ const quiche::QuicheReferenceCountedPointer<ProofSource::Chain>& chain,
+ const QuicCryptoProof& proof,
+ std::unique_ptr<ProofSource::Details> details) override;
+
+ private:
+ const QuicCryptoServerConfig* config_;
+ QuicCompressedCertsCache* compressed_certs_cache_;
+ const std::string client_cached_cert_hashes_;
+ const bool sct_supported_by_client_;
+ const std::string sni_;
+ CryptoHandshakeMessage message_;
+ std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb_;
+ };
+
+ // Invoked by BuildServerConfigUpdateMessageProofSourceCallback::Run once
+ // the proof has been acquired. Finishes building the server config update
+ // message and invokes |cb|.
+ void FinishBuildServerConfigUpdateMessage(
+ QuicCompressedCertsCache* compressed_certs_cache,
+ const std::string& client_cached_cert_hashes,
+ bool sct_supported_by_client, const std::string& sni, bool ok,
+ const quiche::QuicheReferenceCountedPointer<ProofSource::Chain>& chain,
+ const std::string& signature, const std::string& leaf_cert_sct,
+ std::unique_ptr<ProofSource::Details> details,
+ CryptoHandshakeMessage message,
+ std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb) const;
+
+ // Returns true if the next config promotion should happen now.
+ bool IsNextConfigReady(QuicWallTime now) const
+ QUIC_SHARED_LOCKS_REQUIRED(configs_lock_);
+
+ // replay_protection_ controls whether the server enforces that handshakes
+ // aren't replays.
+ bool replay_protection_;
+
+ // The multiple of the CHLO message size that a REJ message must stay under
+ // when the client doesn't present a valid source-address token. This is
+ // used to protect QUIC from amplification attacks.
+ size_t chlo_multiplier_;
+
+ // configs_ satisfies the following invariants:
+ // 1) configs_.empty() <-> primary_config_ == nullptr
+ // 2) primary_config_ != nullptr -> primary_config_->is_primary
+ // 3) ∀ c∈configs_, c->is_primary <-> c == primary_config_
+ mutable QuicMutex configs_lock_;
+
+ // configs_ contains all active server configs. It's expected that there are
+ // about half-a-dozen configs active at any one time.
+ ConfigMap configs_ QUIC_GUARDED_BY(configs_lock_);
+
+ // primary_config_ points to a Config (which is also in |configs_|) which is
+ // the primary config - i.e. the one that we'll give out to new clients.
+ mutable quiche::QuicheReferenceCountedPointer<Config> primary_config_
+ QUIC_GUARDED_BY(configs_lock_);
+
+ // fallback_config_ points to a Config (which is also in |configs_|) which is
+ // the fallback config, which will be used if the other configs are unuseable
+ // for some reason.
+ //
+ // TODO(b/112548056): This is currently always nullptr.
+ quiche::QuicheReferenceCountedPointer<Config> fallback_config_
+ QUIC_GUARDED_BY(configs_lock_);
+
+ // next_config_promotion_time_ contains the nearest, future time when an
+ // active config will be promoted to primary.
+ mutable QuicWallTime next_config_promotion_time_
+ QUIC_GUARDED_BY(configs_lock_);
+
+ // Callback to invoke when the primary config changes.
+ std::unique_ptr<PrimaryConfigChangedCallback> primary_config_changed_cb_
+ QUIC_GUARDED_BY(configs_lock_);
+
+ // Used to protect the source-address tokens that are given to clients.
+ CryptoSecretBoxer source_address_token_boxer_;
+
+ // server_nonce_boxer_ is used to encrypt and validate suggested server
+ // nonces.
+ CryptoSecretBoxer server_nonce_boxer_;
+
+ // server_nonce_orbit_ contains the random, per-server orbit values that this
+ // server will use to generate server nonces (the moral equivalent of a SYN
+ // cookies).
+ uint8_t server_nonce_orbit_[8];
+
+ // proof_source_ contains an object that can provide certificate chains and
+ // signatures.
+ std::unique_ptr<ProofSource> proof_source_;
+
+ // key_exchange_source_ contains an object that can provide key exchange
+ // objects.
+ std::unique_ptr<KeyExchangeSource> key_exchange_source_;
+
+ // ssl_ctx_ contains the server configuration for doing TLS handshakes.
+ bssl::UniquePtr<SSL_CTX> ssl_ctx_;
+
+ // These fields store configuration values. See the comments for their
+ // respective setter functions.
+ uint32_t source_address_token_future_secs_;
+ uint32_t source_address_token_lifetime_secs_;
+
+ // Enable serving SCT or not.
+ bool enable_serving_sct_;
+
+ // Does not own this observer.
+ RejectionObserver* rejection_observer_;
+
+ // If non-empty, the server will operate in the pre-shared key mode by
+ // incorporating |pre_shared_key_| into the key schedule.
+ std::string pre_shared_key_;
+
+ // Whether REJ message should be padded to max packet size.
+ bool pad_rej_;
+
+ // Whether SHLO message should be padded to max packet size.
+ bool pad_shlo_;
+
+ // If client is allowed to send a small client hello (by disabling padding),
+ // server MUST not check for the client hello size.
+ // DO NOT disable this unless you have some other way of validating client.
+ // (e.g. in realtime scenarios, where quic is tunneled through ICE, ICE will
+ // do its own peer validation using STUN pings with ufrag/upass).
+ bool validate_chlo_size_;
+
+ // When source address is validated by some other means (e.g. when using ICE),
+ // source address token validation may be disabled.
+ bool validate_source_address_token_;
+};
+
+struct QUIC_EXPORT_PRIVATE QuicSignedServerConfig
+ : public quiche::QuicheReferenceCounted {
+ QuicSignedServerConfig();
+
+ QuicCryptoProof proof;
+ quiche::QuicheReferenceCountedPointer<ProofSource::Chain> chain;
+ // The server config that is used for this proof (and the rest of the
+ // request).
+ quiche::QuicheReferenceCountedPointer<QuicCryptoServerConfig::Config> config;
+ std::string primary_scid;
+
+ protected:
+ ~QuicSignedServerConfig() override;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_SERVER_CONFIG_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_server_config_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_server_config_test.cc
new file mode 100644
index 00000000000..ed7ffdb981a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_server_config_test.cc
@@ -0,0 +1,494 @@
+// Copyright 2013 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 "quiche/quic/core/crypto/quic_crypto_server_config.h"
+
+#include <stdarg.h>
+
+#include <memory>
+#include <string>
+
+#include "absl/strings/match.h"
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/crypto/cert_compressor.h"
+#include "quiche/quic/core/crypto/chacha20_poly1305_encrypter.h"
+#include "quiche/quic/core/crypto/crypto_handshake_message.h"
+#include "quiche/quic/core/crypto/crypto_secret_boxer.h"
+#include "quiche/quic/core/crypto/quic_random.h"
+#include "quiche/quic/core/proto/crypto_server_config_proto.h"
+#include "quiche/quic/core/quic_time.h"
+#include "quiche/quic/core/quic_versions.h"
+#include "quiche/quic/platform/api/quic_socket_address.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/crypto_test_utils.h"
+#include "quiche/quic/test_tools/mock_clock.h"
+#include "quiche/quic/test_tools/quic_crypto_server_config_peer.h"
+#include "quiche/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+using ::testing::Not;
+
+// NOTE: This matcher depends on the wire format of serialzied protocol buffers,
+// which may change in the future.
+// Switch to ::testing::EqualsProto once it is available in Chromium.
+MATCHER_P(SerializedProtoEquals, message, "") {
+ std::string expected_serialized, actual_serialized;
+ message.SerializeToString(&expected_serialized);
+ arg.SerializeToString(&actual_serialized);
+ return expected_serialized == actual_serialized;
+}
+
+class QuicCryptoServerConfigTest : public QuicTest {};
+
+TEST_F(QuicCryptoServerConfigTest, ServerConfig) {
+ QuicRandom* rand = QuicRandom::GetInstance();
+ QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand,
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default());
+ MockClock clock;
+
+ std::unique_ptr<CryptoHandshakeMessage> message(server.AddDefaultConfig(
+ rand, &clock, QuicCryptoServerConfig::ConfigOptions()));
+
+ // The default configuration should have AES-GCM and at least one ChaCha20
+ // cipher.
+ QuicTagVector aead;
+ ASSERT_THAT(message->GetTaglist(kAEAD, &aead), IsQuicNoError());
+ EXPECT_THAT(aead, ::testing::Contains(kAESG));
+ EXPECT_LE(1u, aead.size());
+}
+
+TEST_F(QuicCryptoServerConfigTest, CompressCerts) {
+ QuicCompressedCertsCache compressed_certs_cache(
+ QuicCompressedCertsCache::kQuicCompressedCertsCacheSize);
+
+ QuicRandom* rand = QuicRandom::GetInstance();
+ QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand,
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default());
+ QuicCryptoServerConfigPeer peer(&server);
+
+ std::vector<std::string> certs = {"testcert"};
+ quiche::QuicheReferenceCountedPointer<ProofSource::Chain> chain(
+ new ProofSource::Chain(certs));
+
+ std::string compressed = QuicCryptoServerConfigPeer::CompressChain(
+ &compressed_certs_cache, chain, "");
+
+ EXPECT_EQ(compressed_certs_cache.Size(), 1u);
+}
+
+TEST_F(QuicCryptoServerConfigTest, CompressSameCertsTwice) {
+ QuicCompressedCertsCache compressed_certs_cache(
+ QuicCompressedCertsCache::kQuicCompressedCertsCacheSize);
+
+ QuicRandom* rand = QuicRandom::GetInstance();
+ QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand,
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default());
+ QuicCryptoServerConfigPeer peer(&server);
+
+ // Compress the certs for the first time.
+ std::vector<std::string> certs = {"testcert"};
+ quiche::QuicheReferenceCountedPointer<ProofSource::Chain> chain(
+ new ProofSource::Chain(certs));
+ std::string cached_certs = "";
+
+ std::string compressed = QuicCryptoServerConfigPeer::CompressChain(
+ &compressed_certs_cache, chain, cached_certs);
+ EXPECT_EQ(compressed_certs_cache.Size(), 1u);
+
+ // Compress the same certs, should use cache if available.
+ std::string compressed2 = QuicCryptoServerConfigPeer::CompressChain(
+ &compressed_certs_cache, chain, cached_certs);
+ EXPECT_EQ(compressed, compressed2);
+ EXPECT_EQ(compressed_certs_cache.Size(), 1u);
+}
+
+TEST_F(QuicCryptoServerConfigTest, CompressDifferentCerts) {
+ // This test compresses a set of similar but not identical certs. Cache if
+ // used should return cache miss and add all the compressed certs.
+ QuicCompressedCertsCache compressed_certs_cache(
+ QuicCompressedCertsCache::kQuicCompressedCertsCacheSize);
+
+ QuicRandom* rand = QuicRandom::GetInstance();
+ QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand,
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default());
+ QuicCryptoServerConfigPeer peer(&server);
+
+ std::vector<std::string> certs = {"testcert"};
+ quiche::QuicheReferenceCountedPointer<ProofSource::Chain> chain(
+ new ProofSource::Chain(certs));
+ std::string cached_certs = "";
+
+ std::string compressed = QuicCryptoServerConfigPeer::CompressChain(
+ &compressed_certs_cache, chain, cached_certs);
+ EXPECT_EQ(compressed_certs_cache.Size(), 1u);
+
+ // Compress a similar certs which only differs in the chain.
+ quiche::QuicheReferenceCountedPointer<ProofSource::Chain> chain2(
+ new ProofSource::Chain(certs));
+
+ std::string compressed2 = QuicCryptoServerConfigPeer::CompressChain(
+ &compressed_certs_cache, chain2, cached_certs);
+ EXPECT_EQ(compressed_certs_cache.Size(), 2u);
+}
+
+class SourceAddressTokenTest : public QuicTest {
+ public:
+ SourceAddressTokenTest()
+ : ip4_(QuicIpAddress::Loopback4()),
+ ip4_dual_(ip4_.DualStacked()),
+ ip6_(QuicIpAddress::Loopback6()),
+ original_time_(QuicWallTime::Zero()),
+ rand_(QuicRandom::GetInstance()),
+ server_(QuicCryptoServerConfig::TESTING, rand_,
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default()),
+ peer_(&server_) {
+ // Advance the clock to some non-zero time.
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1000000));
+ original_time_ = clock_.WallNow();
+
+ primary_config_ = server_.AddDefaultConfig(
+ rand_, &clock_, QuicCryptoServerConfig::ConfigOptions());
+ }
+
+ std::string NewSourceAddressToken(std::string config_id,
+ const QuicIpAddress& ip) {
+ return NewSourceAddressToken(config_id, ip, nullptr);
+ }
+
+ std::string NewSourceAddressToken(
+ std::string config_id, const QuicIpAddress& ip,
+ const SourceAddressTokens& previous_tokens) {
+ return peer_.NewSourceAddressToken(config_id, previous_tokens, ip, rand_,
+ clock_.WallNow(), nullptr);
+ }
+
+ std::string NewSourceAddressToken(
+ std::string config_id, const QuicIpAddress& ip,
+ CachedNetworkParameters* cached_network_params) {
+ SourceAddressTokens previous_tokens;
+ return peer_.NewSourceAddressToken(config_id, previous_tokens, ip, rand_,
+ clock_.WallNow(), cached_network_params);
+ }
+
+ HandshakeFailureReason ValidateSourceAddressTokens(std::string config_id,
+ absl::string_view srct,
+ const QuicIpAddress& ip) {
+ return ValidateSourceAddressTokens(config_id, srct, ip, nullptr);
+ }
+
+ HandshakeFailureReason ValidateSourceAddressTokens(
+ std::string config_id, absl::string_view srct, const QuicIpAddress& ip,
+ CachedNetworkParameters* cached_network_params) {
+ return peer_.ValidateSourceAddressTokens(
+ config_id, srct, ip, clock_.WallNow(), cached_network_params);
+ }
+
+ const std::string kPrimary = "<primary>";
+ const std::string kOverride = "Config with custom source address token key";
+
+ QuicIpAddress ip4_;
+ QuicIpAddress ip4_dual_;
+ QuicIpAddress ip6_;
+
+ MockClock clock_;
+ QuicWallTime original_time_;
+ QuicRandom* rand_ = QuicRandom::GetInstance();
+ QuicCryptoServerConfig server_;
+ QuicCryptoServerConfigPeer peer_;
+ // Stores the primary config.
+ std::unique_ptr<CryptoHandshakeMessage> primary_config_;
+ std::unique_ptr<QuicServerConfigProtobuf> override_config_protobuf_;
+};
+
+// Test basic behavior of source address tokens including being specific
+// to a single IP address and server config.
+TEST_F(SourceAddressTokenTest, SourceAddressToken) {
+ // Primary config generates configs that validate successfully.
+ const std::string token4 = NewSourceAddressToken(kPrimary, ip4_);
+ const std::string token4d = NewSourceAddressToken(kPrimary, ip4_dual_);
+ const std::string token6 = NewSourceAddressToken(kPrimary, ip6_);
+ EXPECT_EQ(HANDSHAKE_OK, ValidateSourceAddressTokens(kPrimary, token4, ip4_));
+ ASSERT_EQ(HANDSHAKE_OK,
+ ValidateSourceAddressTokens(kPrimary, token4, ip4_dual_));
+ ASSERT_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE,
+ ValidateSourceAddressTokens(kPrimary, token4, ip6_));
+ ASSERT_EQ(HANDSHAKE_OK, ValidateSourceAddressTokens(kPrimary, token4d, ip4_));
+ ASSERT_EQ(HANDSHAKE_OK,
+ ValidateSourceAddressTokens(kPrimary, token4d, ip4_dual_));
+ ASSERT_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE,
+ ValidateSourceAddressTokens(kPrimary, token4d, ip6_));
+ ASSERT_EQ(HANDSHAKE_OK, ValidateSourceAddressTokens(kPrimary, token6, ip6_));
+}
+
+TEST_F(SourceAddressTokenTest, SourceAddressTokenExpiration) {
+ const std::string token = NewSourceAddressToken(kPrimary, ip4_);
+
+ // Validation fails if the token is from the future.
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(-3600 * 2));
+ ASSERT_EQ(SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE,
+ ValidateSourceAddressTokens(kPrimary, token, ip4_));
+
+ // Validation fails after tokens expire.
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(86400 * 7));
+ ASSERT_EQ(SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE,
+ ValidateSourceAddressTokens(kPrimary, token, ip4_));
+}
+
+TEST_F(SourceAddressTokenTest, SourceAddressTokenWithNetworkParams) {
+ // Make sure that if the source address token contains CachedNetworkParameters
+ // that this gets written to ValidateSourceAddressToken output argument.
+ CachedNetworkParameters cached_network_params_input;
+ cached_network_params_input.set_bandwidth_estimate_bytes_per_second(1234);
+ const std::string token4_with_cached_network_params =
+ NewSourceAddressToken(kPrimary, ip4_, &cached_network_params_input);
+
+ CachedNetworkParameters cached_network_params_output;
+ EXPECT_THAT(cached_network_params_output,
+ Not(SerializedProtoEquals(cached_network_params_input)));
+ ValidateSourceAddressTokens(kPrimary, token4_with_cached_network_params, ip4_,
+ &cached_network_params_output);
+ EXPECT_THAT(cached_network_params_output,
+ SerializedProtoEquals(cached_network_params_input));
+}
+
+// Test the ability for a source address token to be valid for multiple
+// addresses.
+TEST_F(SourceAddressTokenTest, SourceAddressTokenMultipleAddresses) {
+ QuicWallTime now = clock_.WallNow();
+
+ // Now create a token which is usable for both addresses.
+ SourceAddressToken previous_token;
+ previous_token.set_ip(ip6_.DualStacked().ToPackedString());
+ previous_token.set_timestamp(now.ToUNIXSeconds());
+ SourceAddressTokens previous_tokens;
+ (*previous_tokens.add_tokens()) = previous_token;
+ const std::string token4or6 =
+ NewSourceAddressToken(kPrimary, ip4_, previous_tokens);
+
+ EXPECT_EQ(HANDSHAKE_OK,
+ ValidateSourceAddressTokens(kPrimary, token4or6, ip4_));
+ ASSERT_EQ(HANDSHAKE_OK,
+ ValidateSourceAddressTokens(kPrimary, token4or6, ip6_));
+}
+
+class CryptoServerConfigsTest : public QuicTest {
+ public:
+ CryptoServerConfigsTest()
+ : rand_(QuicRandom::GetInstance()),
+ config_(QuicCryptoServerConfig::TESTING, rand_,
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default()),
+ test_peer_(&config_) {}
+
+ void SetUp() override {
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1000));
+ }
+
+ // SetConfigs constructs suitable config protobufs and calls SetConfigs on
+ // |config_|.
+ // Each struct in the input vector contains 3 elements.
+ // The first is the server config ID of a Config. The second is
+ // the |primary_time| of that Config, given in epoch seconds. (Although note
+ // that, in these tests, time is set to 1000 seconds since the epoch.).
+ // The third is the priority.
+ //
+ // For example:
+ // SetConfigs(std::vector<ServerConfigIDWithTimeAndPriority>()); // calls
+ // |config_.SetConfigs| with no protobufs.
+ //
+ // // Calls |config_.SetConfigs| with two protobufs: one for a Config with
+ // // a |primary_time| of 900 and priority 1, and another with
+ // // a |primary_time| of 1000 and priority 2.
+
+ // CheckConfigs(
+ // {{"id1", 900, 1},
+ // {"id2", 1000, 2}});
+ //
+ // If the server config id starts with "INVALID" then the generated protobuf
+ // will be invalid.
+ struct ServerConfigIDWithTimeAndPriority {
+ ServerConfigID server_config_id;
+ int primary_time;
+ int priority;
+ };
+ void SetConfigs(std::vector<ServerConfigIDWithTimeAndPriority> configs) {
+ const char kOrbit[] = "12345678";
+
+ bool has_invalid = false;
+
+ std::vector<QuicServerConfigProtobuf> protobufs;
+ for (const auto& config : configs) {
+ const ServerConfigID& server_config_id = config.server_config_id;
+ const int primary_time = config.primary_time;
+ const int priority = config.priority;
+
+ QuicCryptoServerConfig::ConfigOptions options;
+ options.id = server_config_id;
+ options.orbit = kOrbit;
+ QuicServerConfigProtobuf protobuf =
+ QuicCryptoServerConfig::GenerateConfig(rand_, &clock_, options);
+ protobuf.set_primary_time(primary_time);
+ protobuf.set_priority(priority);
+ if (absl::StartsWith(std::string(server_config_id), "INVALID")) {
+ protobuf.clear_key();
+ has_invalid = true;
+ }
+ protobufs.push_back(std::move(protobuf));
+ }
+
+ ASSERT_EQ(!has_invalid && !configs.empty(),
+ config_.SetConfigs(protobufs, /* fallback_protobuf = */ nullptr,
+ clock_.WallNow()));
+ }
+
+ protected:
+ QuicRandom* const rand_;
+ MockClock clock_;
+ QuicCryptoServerConfig config_;
+ QuicCryptoServerConfigPeer test_peer_;
+};
+
+TEST_F(CryptoServerConfigsTest, NoConfigs) {
+ test_peer_.CheckConfigs(std::vector<std::pair<std::string, bool>>());
+}
+
+TEST_F(CryptoServerConfigsTest, MakePrimaryFirst) {
+ // Make sure that "b" is primary even though "a" comes first.
+ SetConfigs({{"a", 1100, 1}, {"b", 900, 1}});
+ test_peer_.CheckConfigs({{"a", false}, {"b", true}});
+}
+
+TEST_F(CryptoServerConfigsTest, MakePrimarySecond) {
+ // Make sure that a remains primary after b is added.
+ SetConfigs({{"a", 900, 1}, {"b", 1100, 1}});
+ test_peer_.CheckConfigs({{"a", true}, {"b", false}});
+}
+
+TEST_F(CryptoServerConfigsTest, Delete) {
+ // Ensure that configs get deleted when removed.
+ SetConfigs({{"a", 800, 1}, {"b", 900, 1}, {"c", 1100, 1}});
+ test_peer_.CheckConfigs({{"a", false}, {"b", true}, {"c", false}});
+ SetConfigs({{"b", 900, 1}, {"c", 1100, 1}});
+ test_peer_.CheckConfigs({{"b", true}, {"c", false}});
+}
+
+TEST_F(CryptoServerConfigsTest, DeletePrimary) {
+ // Ensure that deleting the primary config works.
+ SetConfigs({{"a", 800, 1}, {"b", 900, 1}, {"c", 1100, 1}});
+ test_peer_.CheckConfigs({{"a", false}, {"b", true}, {"c", false}});
+ SetConfigs({{"a", 800, 1}, {"c", 1100, 1}});
+ test_peer_.CheckConfigs({{"a", true}, {"c", false}});
+}
+
+TEST_F(CryptoServerConfigsTest, FailIfDeletingAllConfigs) {
+ // Ensure that configs get deleted when removed.
+ SetConfigs({{"a", 800, 1}, {"b", 900, 1}});
+ test_peer_.CheckConfigs({{"a", false}, {"b", true}});
+ SetConfigs(std::vector<ServerConfigIDWithTimeAndPriority>());
+ // Config change is rejected, still using old configs.
+ test_peer_.CheckConfigs({{"a", false}, {"b", true}});
+}
+
+TEST_F(CryptoServerConfigsTest, ChangePrimaryTime) {
+ // Check that updates to primary time get picked up.
+ SetConfigs({{"a", 400, 1}, {"b", 800, 1}, {"c", 1200, 1}});
+ test_peer_.SelectNewPrimaryConfig(500);
+ test_peer_.CheckConfigs({{"a", true}, {"b", false}, {"c", false}});
+ SetConfigs({{"a", 1200, 1}, {"b", 800, 1}, {"c", 400, 1}});
+ test_peer_.SelectNewPrimaryConfig(500);
+ test_peer_.CheckConfigs({{"a", false}, {"b", false}, {"c", true}});
+}
+
+TEST_F(CryptoServerConfigsTest, AllConfigsInThePast) {
+ // Check that the most recent config is selected.
+ SetConfigs({{"a", 400, 1}, {"b", 800, 1}, {"c", 1200, 1}});
+ test_peer_.SelectNewPrimaryConfig(1500);
+ test_peer_.CheckConfigs({{"a", false}, {"b", false}, {"c", true}});
+}
+
+TEST_F(CryptoServerConfigsTest, AllConfigsInTheFuture) {
+ // Check that the first config is selected.
+ SetConfigs({{"a", 400, 1}, {"b", 800, 1}, {"c", 1200, 1}});
+ test_peer_.SelectNewPrimaryConfig(100);
+ test_peer_.CheckConfigs({{"a", true}, {"b", false}, {"c", false}});
+}
+
+TEST_F(CryptoServerConfigsTest, SortByPriority) {
+ // Check that priority is used to decide on a primary config when
+ // configs have the same primary time.
+ SetConfigs({{"a", 900, 1}, {"b", 900, 2}, {"c", 900, 3}});
+ test_peer_.CheckConfigs({{"a", true}, {"b", false}, {"c", false}});
+ test_peer_.SelectNewPrimaryConfig(800);
+ test_peer_.CheckConfigs({{"a", true}, {"b", false}, {"c", false}});
+ test_peer_.SelectNewPrimaryConfig(1000);
+ test_peer_.CheckConfigs({{"a", true}, {"b", false}, {"c", false}});
+
+ // Change priorities and expect sort order to change.
+ SetConfigs({{"a", 900, 2}, {"b", 900, 1}, {"c", 900, 0}});
+ test_peer_.CheckConfigs({{"a", false}, {"b", false}, {"c", true}});
+ test_peer_.SelectNewPrimaryConfig(800);
+ test_peer_.CheckConfigs({{"a", false}, {"b", false}, {"c", true}});
+ test_peer_.SelectNewPrimaryConfig(1000);
+ test_peer_.CheckConfigs({{"a", false}, {"b", false}, {"c", true}});
+}
+
+TEST_F(CryptoServerConfigsTest, AdvancePrimary) {
+ // Check that a new primary config is enabled at the right time.
+ SetConfigs({{"a", 900, 1}, {"b", 1100, 1}});
+ test_peer_.SelectNewPrimaryConfig(1000);
+ test_peer_.CheckConfigs({{"a", true}, {"b", false}});
+ test_peer_.SelectNewPrimaryConfig(1101);
+ test_peer_.CheckConfigs({{"a", false}, {"b", true}});
+}
+
+class ValidateCallback : public ValidateClientHelloResultCallback {
+ public:
+ void Run(quiche::QuicheReferenceCountedPointer<Result> /*result*/,
+ std::unique_ptr<ProofSource::Details> /*details*/) override {}
+};
+
+TEST_F(CryptoServerConfigsTest, AdvancePrimaryViaValidate) {
+ // Check that a new primary config is enabled at the right time.
+ SetConfigs({{"a", 900, 1}, {"b", 1100, 1}});
+ test_peer_.SelectNewPrimaryConfig(1000);
+ test_peer_.CheckConfigs({{"a", true}, {"b", false}});
+ CryptoHandshakeMessage client_hello;
+ QuicSocketAddress client_address;
+ QuicSocketAddress server_address;
+ QuicTransportVersion transport_version = QUIC_VERSION_UNSUPPORTED;
+ for (const ParsedQuicVersion& version : AllSupportedVersions()) {
+ if (version.handshake_protocol == PROTOCOL_QUIC_CRYPTO) {
+ transport_version = version.transport_version;
+ break;
+ }
+ }
+ ASSERT_NE(transport_version, QUIC_VERSION_UNSUPPORTED);
+ MockClock clock;
+ quiche::QuicheReferenceCountedPointer<QuicSignedServerConfig> signed_config(
+ new QuicSignedServerConfig);
+ std::unique_ptr<ValidateClientHelloResultCallback> done_cb(
+ new ValidateCallback);
+ clock.AdvanceTime(QuicTime::Delta::FromSeconds(1100));
+ config_.ValidateClientHello(client_hello, client_address, server_address,
+ transport_version, &clock, signed_config,
+ std::move(done_cb));
+ test_peer_.CheckConfigs({{"a", false}, {"b", true}});
+}
+
+TEST_F(CryptoServerConfigsTest, InvalidConfigs) {
+ // Ensure that invalid configs don't change anything.
+ SetConfigs({{"a", 800, 1}, {"b", 900, 1}, {"c", 1100, 1}});
+ test_peer_.CheckConfigs({{"a", false}, {"b", true}, {"c", false}});
+ SetConfigs({{"a", 800, 1}, {"c", 1100, 1}, {"INVALID1", 1000, 1}});
+ test_peer_.CheckConfigs({{"a", false}, {"b", true}, {"c", false}});
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_decrypter.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_decrypter.cc
new file mode 100644
index 00000000000..da0e809acb4
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_decrypter.cc
@@ -0,0 +1,79 @@
+// Copyright (c) 2012 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 "quiche/quic/core/crypto/quic_decrypter.h"
+
+#include <string>
+#include <utility>
+
+#include "absl/strings/string_view.h"
+#include "openssl/tls1.h"
+#include "quiche/quic/core/crypto/aes_128_gcm_12_decrypter.h"
+#include "quiche/quic/core/crypto/aes_128_gcm_decrypter.h"
+#include "quiche/quic/core/crypto/aes_256_gcm_decrypter.h"
+#include "quiche/quic/core/crypto/chacha20_poly1305_decrypter.h"
+#include "quiche/quic/core/crypto/chacha20_poly1305_tls_decrypter.h"
+#include "quiche/quic/core/crypto/crypto_protocol.h"
+#include "quiche/quic/core/crypto/null_decrypter.h"
+#include "quiche/quic/core/crypto/quic_hkdf.h"
+#include "quiche/quic/platform/api/quic_bug_tracker.h"
+#include "quiche/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+// static
+std::unique_ptr<QuicDecrypter> QuicDecrypter::Create(
+ const ParsedQuicVersion& version, QuicTag algorithm) {
+ switch (algorithm) {
+ case kAESG:
+ if (version.UsesInitialObfuscators()) {
+ return std::make_unique<Aes128GcmDecrypter>();
+ } else {
+ return std::make_unique<Aes128Gcm12Decrypter>();
+ }
+ case kCC20:
+ if (version.UsesInitialObfuscators()) {
+ return std::make_unique<ChaCha20Poly1305TlsDecrypter>();
+ } else {
+ return std::make_unique<ChaCha20Poly1305Decrypter>();
+ }
+ default:
+ QUIC_LOG(FATAL) << "Unsupported algorithm: " << algorithm;
+ return nullptr;
+ }
+}
+
+// static
+std::unique_ptr<QuicDecrypter> QuicDecrypter::CreateFromCipherSuite(
+ uint32_t cipher_suite) {
+ switch (cipher_suite) {
+ case TLS1_CK_AES_128_GCM_SHA256:
+ return std::make_unique<Aes128GcmDecrypter>();
+ case TLS1_CK_AES_256_GCM_SHA384:
+ return std::make_unique<Aes256GcmDecrypter>();
+ case TLS1_CK_CHACHA20_POLY1305_SHA256:
+ return std::make_unique<ChaCha20Poly1305TlsDecrypter>();
+ default:
+ QUIC_BUG(quic_bug_10660_1) << "TLS cipher suite is unknown to QUIC";
+ return nullptr;
+ }
+}
+
+// static
+void QuicDecrypter::DiversifyPreliminaryKey(absl::string_view preliminary_key,
+ absl::string_view nonce_prefix,
+ const DiversificationNonce& nonce,
+ size_t key_size,
+ size_t nonce_prefix_size,
+ std::string* out_key,
+ std::string* out_nonce_prefix) {
+ QuicHKDF hkdf((std::string(preliminary_key)) + (std::string(nonce_prefix)),
+ absl::string_view(nonce.data(), nonce.size()),
+ "QUIC key diversification", 0, key_size, 0, nonce_prefix_size,
+ 0);
+ *out_key = std::string(hkdf.server_write_key());
+ *out_nonce_prefix = std::string(hkdf.server_write_iv());
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_decrypter.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_decrypter.h
new file mode 100644
index 00000000000..8e3c754a08d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_decrypter.h
@@ -0,0 +1,93 @@
+// Copyright (c) 2012 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_QUIC_DECRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_QUIC_DECRYPTER_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/crypto/quic_crypter.h"
+#include "quiche/quic/core/quic_data_reader.h"
+#include "quiche/quic/core/quic_packets.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE QuicDecrypter : public QuicCrypter {
+ public:
+ virtual ~QuicDecrypter() {}
+
+ static std::unique_ptr<QuicDecrypter> Create(const ParsedQuicVersion& version,
+ QuicTag algorithm);
+
+ // Creates an IETF QuicDecrypter based on |cipher_suite| which must be an id
+ // returned by SSL_CIPHER_get_id. The caller is responsible for taking
+ // ownership of the new QuicDecrypter.
+ static std::unique_ptr<QuicDecrypter> CreateFromCipherSuite(
+ uint32_t cipher_suite);
+
+ // Sets the encryption key. Returns true on success, false on failure.
+ // |DecryptPacket| may not be called until |SetDiversificationNonce| is
+ // called and the preliminary keying material will be combined with that
+ // nonce in order to create the actual key and nonce-prefix.
+ //
+ // If this function is called, neither |SetKey| nor |SetNoncePrefix| may be
+ // called.
+ virtual bool SetPreliminaryKey(absl::string_view key) = 0;
+
+ // SetDiversificationNonce uses |nonce| to derive final keys based on the
+ // input keying material given by calling |SetPreliminaryKey|.
+ //
+ // Calling this function is a no-op if |SetPreliminaryKey| hasn't been
+ // called.
+ virtual bool SetDiversificationNonce(const DiversificationNonce& nonce) = 0;
+
+ // Populates |output| with the decrypted |ciphertext| and populates
+ // |output_length| with the length. Returns 0 if there is an error.
+ // |output| size is specified by |max_output_length| and must be
+ // at least as large as the ciphertext. |packet_number| is
+ // appended to the |nonce_prefix| value provided in SetNoncePrefix()
+ // to form the nonce.
+ // TODO(wtc): add a way for DecryptPacket to report decryption failure due
+ // to non-authentic inputs, as opposed to other reasons for failure.
+ virtual bool DecryptPacket(uint64_t packet_number,
+ absl::string_view associated_data,
+ absl::string_view ciphertext, char* output,
+ size_t* output_length,
+ size_t max_output_length) = 0;
+
+ // Reads a sample of ciphertext from |sample_reader| and uses the header
+ // protection key to generate a mask to use for header protection. If
+ // successful, this function returns this mask, which is at least 5 bytes
+ // long. Callers can detect failure by checking if the output string is empty.
+ virtual std::string GenerateHeaderProtectionMask(
+ QuicDataReader* sample_reader) = 0;
+
+ // The ID of the cipher. Return 0x03000000 ORed with the 'cryptographic suite
+ // selector'.
+ virtual uint32_t cipher_id() const = 0;
+
+ // Returns the maximum number of packets that can safely fail decryption with
+ // this decrypter.
+ virtual QuicPacketCount GetIntegrityLimit() const = 0;
+
+ // For use by unit tests only.
+ virtual absl::string_view GetKey() const = 0;
+ virtual absl::string_view GetNoncePrefix() const = 0;
+
+ static void DiversifyPreliminaryKey(absl::string_view preliminary_key,
+ absl::string_view nonce_prefix,
+ const DiversificationNonce& nonce,
+ size_t key_size, size_t nonce_prefix_size,
+ std::string* out_key,
+ std::string* out_nonce_prefix);
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_DECRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_encrypter.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_encrypter.cc
new file mode 100644
index 00000000000..151b8d058c2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_encrypter.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 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 "quiche/quic/core/crypto/quic_encrypter.h"
+
+#include <utility>
+
+#include "openssl/tls1.h"
+#include "quiche/quic/core/crypto/aes_128_gcm_12_encrypter.h"
+#include "quiche/quic/core/crypto/aes_128_gcm_encrypter.h"
+#include "quiche/quic/core/crypto/aes_256_gcm_encrypter.h"
+#include "quiche/quic/core/crypto/chacha20_poly1305_encrypter.h"
+#include "quiche/quic/core/crypto/chacha20_poly1305_tls_encrypter.h"
+#include "quiche/quic/core/crypto/crypto_protocol.h"
+#include "quiche/quic/core/crypto/null_encrypter.h"
+#include "quiche/quic/platform/api/quic_bug_tracker.h"
+#include "quiche/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+// static
+std::unique_ptr<QuicEncrypter> QuicEncrypter::Create(
+ const ParsedQuicVersion& version, QuicTag algorithm) {
+ switch (algorithm) {
+ case kAESG:
+ if (version.UsesInitialObfuscators()) {
+ return std::make_unique<Aes128GcmEncrypter>();
+ } else {
+ return std::make_unique<Aes128Gcm12Encrypter>();
+ }
+ case kCC20:
+ if (version.UsesInitialObfuscators()) {
+ return std::make_unique<ChaCha20Poly1305TlsEncrypter>();
+ } else {
+ return std::make_unique<ChaCha20Poly1305Encrypter>();
+ }
+ default:
+ QUIC_LOG(FATAL) << "Unsupported algorithm: " << algorithm;
+ return nullptr;
+ }
+}
+
+// static
+std::unique_ptr<QuicEncrypter> QuicEncrypter::CreateFromCipherSuite(
+ uint32_t cipher_suite) {
+ switch (cipher_suite) {
+ case TLS1_CK_AES_128_GCM_SHA256:
+ return std::make_unique<Aes128GcmEncrypter>();
+ case TLS1_CK_AES_256_GCM_SHA384:
+ return std::make_unique<Aes256GcmEncrypter>();
+ case TLS1_CK_CHACHA20_POLY1305_SHA256:
+ return std::make_unique<ChaCha20Poly1305TlsEncrypter>();
+ default:
+ QUIC_BUG(quic_bug_10711_1) << "TLS cipher suite is unknown to QUIC";
+ return nullptr;
+ }
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_encrypter.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_encrypter.h
new file mode 100644
index 00000000000..7b8c4fa1a45
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_encrypter.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2012 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_QUIC_ENCRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_QUIC_ENCRYPTER_H_
+
+#include <cstddef>
+#include <memory>
+
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/crypto/quic_crypter.h"
+#include "quiche/quic/core/quic_packets.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE QuicEncrypter : public QuicCrypter {
+ public:
+ virtual ~QuicEncrypter() {}
+
+ static std::unique_ptr<QuicEncrypter> Create(const ParsedQuicVersion& version,
+ QuicTag algorithm);
+
+ // Creates an IETF QuicEncrypter based on |cipher_suite| which must be an id
+ // returned by SSL_CIPHER_get_id. The caller is responsible for taking
+ // ownership of the new QuicEncrypter.
+ static std::unique_ptr<QuicEncrypter> CreateFromCipherSuite(
+ uint32_t cipher_suite);
+
+ // Writes encrypted |plaintext| and a MAC over |plaintext| and
+ // |associated_data| into output. Sets |output_length| to the number of
+ // bytes written. Returns true on success or false if there was an error.
+ // |packet_number| is appended to the |nonce_prefix| value provided in
+ // SetNoncePrefix() to form the nonce. |output| must not overlap with
+ // |associated_data|. If |output| overlaps with |plaintext| then
+ // |plaintext| must be <= |output|.
+ virtual bool EncryptPacket(uint64_t packet_number,
+ absl::string_view associated_data,
+ absl::string_view plaintext, char* output,
+ size_t* output_length,
+ size_t max_output_length) = 0;
+
+ // Takes a |sample| of ciphertext and uses the header protection key to
+ // generate a mask to use for header protection, and returns that mask. On
+ // success, the mask will be at least 5 bytes long; on failure the string will
+ // be empty.
+ virtual std::string GenerateHeaderProtectionMask(
+ absl::string_view sample) = 0;
+
+ // Returns the maximum length of plaintext that can be encrypted
+ // to ciphertext no larger than |ciphertext_size|.
+ virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) const = 0;
+
+ // Returns the length of the ciphertext that would be generated by encrypting
+ // to plaintext of size |plaintext_size|.
+ virtual size_t GetCiphertextSize(size_t plaintext_size) const = 0;
+
+ // Returns the maximum number of packets that can be safely encrypted with
+ // this encrypter.
+ virtual QuicPacketCount GetConfidentialityLimit() const = 0;
+
+ // For use by unit tests only.
+ virtual absl::string_view GetKey() const = 0;
+ virtual absl::string_view GetNoncePrefix() const = 0;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_ENCRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_hkdf.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_hkdf.cc
new file mode 100644
index 00000000000..14dab76a32c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_hkdf.cc
@@ -0,0 +1,98 @@
+// Copyright (c) 2018 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 "quiche/quic/core/crypto/quic_hkdf.h"
+
+#include <memory>
+
+#include "absl/strings/string_view.h"
+#include "openssl/digest.h"
+#include "openssl/hkdf.h"
+#include "quiche/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+const size_t kSHA256HashLength = 32;
+const size_t kMaxKeyMaterialSize = kSHA256HashLength * 256;
+
+QuicHKDF::QuicHKDF(absl::string_view secret, absl::string_view salt,
+ absl::string_view info, size_t key_bytes_to_generate,
+ size_t iv_bytes_to_generate,
+ size_t subkey_secret_bytes_to_generate)
+ : QuicHKDF(secret, salt, info, key_bytes_to_generate, key_bytes_to_generate,
+ iv_bytes_to_generate, iv_bytes_to_generate,
+ subkey_secret_bytes_to_generate) {}
+
+QuicHKDF::QuicHKDF(absl::string_view secret, absl::string_view salt,
+ absl::string_view info, size_t client_key_bytes_to_generate,
+ size_t server_key_bytes_to_generate,
+ size_t client_iv_bytes_to_generate,
+ size_t server_iv_bytes_to_generate,
+ size_t subkey_secret_bytes_to_generate) {
+ const size_t material_length =
+ 2 * client_key_bytes_to_generate + client_iv_bytes_to_generate +
+ 2 * server_key_bytes_to_generate + server_iv_bytes_to_generate +
+ subkey_secret_bytes_to_generate;
+ QUICHE_DCHECK_LT(material_length, kMaxKeyMaterialSize);
+
+ output_.resize(material_length);
+ // On Windows, when the size of output_ is zero, dereference of 0'th element
+ // results in a crash. C++11 solves this problem by adding a data() getter
+ // method to std::vector.
+ if (output_.empty()) {
+ return;
+ }
+
+ ::HKDF(&output_[0], output_.size(), ::EVP_sha256(),
+ reinterpret_cast<const uint8_t*>(secret.data()), secret.size(),
+ reinterpret_cast<const uint8_t*>(salt.data()), salt.size(),
+ reinterpret_cast<const uint8_t*>(info.data()), info.size());
+
+ size_t j = 0;
+ if (client_key_bytes_to_generate) {
+ client_write_key_ = absl::string_view(reinterpret_cast<char*>(&output_[j]),
+ client_key_bytes_to_generate);
+ j += client_key_bytes_to_generate;
+ }
+
+ if (server_key_bytes_to_generate) {
+ server_write_key_ = absl::string_view(reinterpret_cast<char*>(&output_[j]),
+ server_key_bytes_to_generate);
+ j += server_key_bytes_to_generate;
+ }
+
+ if (client_iv_bytes_to_generate) {
+ client_write_iv_ = absl::string_view(reinterpret_cast<char*>(&output_[j]),
+ client_iv_bytes_to_generate);
+ j += client_iv_bytes_to_generate;
+ }
+
+ if (server_iv_bytes_to_generate) {
+ server_write_iv_ = absl::string_view(reinterpret_cast<char*>(&output_[j]),
+ server_iv_bytes_to_generate);
+ j += server_iv_bytes_to_generate;
+ }
+
+ if (subkey_secret_bytes_to_generate) {
+ subkey_secret_ = absl::string_view(reinterpret_cast<char*>(&output_[j]),
+ subkey_secret_bytes_to_generate);
+ j += subkey_secret_bytes_to_generate;
+ }
+ // Repeat client and server key bytes for header protection keys.
+ if (client_key_bytes_to_generate) {
+ client_hp_key_ = absl::string_view(reinterpret_cast<char*>(&output_[j]),
+ client_key_bytes_to_generate);
+ j += client_key_bytes_to_generate;
+ }
+
+ if (server_key_bytes_to_generate) {
+ server_hp_key_ = absl::string_view(reinterpret_cast<char*>(&output_[j]),
+ server_key_bytes_to_generate);
+ j += server_key_bytes_to_generate;
+ }
+}
+
+QuicHKDF::~QuicHKDF() {}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_hkdf.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_hkdf.h
new file mode 100644
index 00000000000..6a300ed0c0b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_hkdf.h
@@ -0,0 +1,71 @@
+// Copyright 2018 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_QUIC_HKDF_H_
+#define QUICHE_QUIC_CORE_CRYPTO_QUIC_HKDF_H_
+
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// QuicHKDF implements the key derivation function specified in RFC 5869
+// (using SHA-256) and outputs key material, as needed by QUIC.
+// See https://tools.ietf.org/html/rfc5869 for details.
+class QUIC_EXPORT_PRIVATE QuicHKDF {
+ public:
+ // |secret|: the input shared secret (or, from RFC 5869, the IKM).
+ // |salt|: an (optional) public salt / non-secret random value. While
+ // optional, callers are strongly recommended to provide a salt. There is no
+ // added security value in making this larger than the SHA-256 block size of
+ // 64 bytes.
+ // |info|: an (optional) label to distinguish different uses of HKDF. It is
+ // optional context and application specific information (can be a zero-length
+ // string).
+ // |key_bytes_to_generate|: the number of bytes of key material to generate
+ // for both client and server.
+ // |iv_bytes_to_generate|: the number of bytes of IV to generate for both
+ // client and server.
+ // |subkey_secret_bytes_to_generate|: the number of bytes of subkey secret to
+ // generate, shared between client and server.
+ QuicHKDF(absl::string_view secret, absl::string_view salt,
+ absl::string_view info, size_t key_bytes_to_generate,
+ size_t iv_bytes_to_generate, size_t subkey_secret_bytes_to_generate);
+
+ // An alternative constructor that allows the client and server key/IV
+ // lengths to be different.
+ QuicHKDF(absl::string_view secret, absl::string_view salt,
+ absl::string_view info, size_t client_key_bytes_to_generate,
+ size_t server_key_bytes_to_generate,
+ size_t client_iv_bytes_to_generate,
+ size_t server_iv_bytes_to_generate,
+ size_t subkey_secret_bytes_to_generate);
+
+ ~QuicHKDF();
+
+ absl::string_view client_write_key() const { return client_write_key_; }
+ absl::string_view client_write_iv() const { return client_write_iv_; }
+ absl::string_view server_write_key() const { return server_write_key_; }
+ absl::string_view server_write_iv() const { return server_write_iv_; }
+ absl::string_view subkey_secret() const { return subkey_secret_; }
+ absl::string_view client_hp_key() const { return client_hp_key_; }
+ absl::string_view server_hp_key() const { return server_hp_key_; }
+
+ private:
+ std::vector<uint8_t> output_;
+
+ absl::string_view client_write_key_;
+ absl::string_view server_write_key_;
+ absl::string_view client_write_iv_;
+ absl::string_view server_write_iv_;
+ absl::string_view subkey_secret_;
+ absl::string_view client_hp_key_;
+ absl::string_view server_hp_key_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_HKDF_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_hkdf_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_hkdf_test.cc
new file mode 100644
index 00000000000..48f041f1f82
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_hkdf_test.cc
@@ -0,0 +1,91 @@
+// Copyright 2018 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 "quiche/quic/core/crypto/quic_hkdf.h"
+
+#include <string>
+
+#include "absl/base/macros.h"
+#include "absl/strings/escaping.h"
+#include "quiche/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+struct HKDFInput {
+ const char* key_hex;
+ const char* salt_hex;
+ const char* info_hex;
+ const char* output_hex;
+};
+
+// These test cases are taken from
+// https://tools.ietf.org/html/rfc5869#appendix-A.
+static const HKDFInput kHKDFInputs[] = {
+ {
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ "000102030405060708090a0b0c",
+ "f0f1f2f3f4f5f6f7f8f9",
+ "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf340072"
+ "08d5"
+ "b887185865",
+ },
+ {
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122"
+ "2324"
+ "25262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f4041424344454647"
+ "4849"
+ "4a4b4c4d4e4f",
+ "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182"
+ "8384"
+ "85868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7"
+ "a8a9"
+ "aaabacadaeaf",
+ "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2"
+ "d3d4"
+ "d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7"
+ "f8f9"
+ "fafbfcfdfeff",
+ "b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c59045a"
+ "99ca"
+ "c7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71cc30c58179ec3e87"
+ "c14c"
+ "01d5c1f3434f1d87",
+ },
+ {
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ "",
+ "",
+ "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d2013"
+ "95fa"
+ "a4b61a96c8",
+ },
+};
+
+class QuicHKDFTest : public QuicTest {};
+
+TEST_F(QuicHKDFTest, HKDF) {
+ for (size_t i = 0; i < ABSL_ARRAYSIZE(kHKDFInputs); i++) {
+ const HKDFInput& test(kHKDFInputs[i]);
+ SCOPED_TRACE(i);
+
+ const std::string key = absl::HexStringToBytes(test.key_hex);
+ const std::string salt = absl::HexStringToBytes(test.salt_hex);
+ const std::string info = absl::HexStringToBytes(test.info_hex);
+ const std::string expected = absl::HexStringToBytes(test.output_hex);
+
+ // We set the key_length to the length of the expected output and then take
+ // the result from the first key, which is the client write key.
+ QuicHKDF hkdf(key, salt, info, expected.size(), 0, 0);
+
+ ASSERT_EQ(expected.size(), hkdf.client_write_key().size());
+ EXPECT_EQ(0, memcmp(expected.data(), hkdf.client_write_key().data(),
+ expected.size()));
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_random.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_random.cc
new file mode 100644
index 00000000000..bd0dc5b534e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_random.cc
@@ -0,0 +1,99 @@
+// Copyright (c) 2012 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 "quiche/quic/core/crypto/quic_random.h"
+
+#include <cstdint>
+#include <cstring>
+
+#include "openssl/rand.h"
+#include "quiche/quic/platform/api/quic_bug_tracker.h"
+#include "quiche/quic/platform/api/quic_logging.h"
+#include "quiche/common/platform/api/quiche_logging.h"
+
+namespace quic {
+
+namespace {
+
+// Insecure randomness in DefaultRandom uses an implementation of
+// xoshiro256++ 1.0 based on code in the public domain from
+// <http://prng.di.unimi.it/xoshiro256plusplus.c>.
+
+inline uint64_t Xoshiro256InitializeRngStateMember() {
+ uint64_t result;
+ RAND_bytes(reinterpret_cast<uint8_t*>(&result), sizeof(result));
+ return result;
+}
+
+inline uint64_t Xoshiro256PlusPlusRotLeft(uint64_t x, int k) {
+ return (x << k) | (x >> (64 - k));
+}
+
+uint64_t Xoshiro256PlusPlus() {
+ static thread_local uint64_t rng_state[4] = {
+ Xoshiro256InitializeRngStateMember(),
+ Xoshiro256InitializeRngStateMember(),
+ Xoshiro256InitializeRngStateMember(),
+ Xoshiro256InitializeRngStateMember()};
+ const uint64_t result =
+ Xoshiro256PlusPlusRotLeft(rng_state[0] + rng_state[3], 23) + rng_state[0];
+ const uint64_t t = rng_state[1] << 17;
+ rng_state[2] ^= rng_state[0];
+ rng_state[3] ^= rng_state[1];
+ rng_state[1] ^= rng_state[2];
+ rng_state[0] ^= rng_state[3];
+ rng_state[2] ^= t;
+ rng_state[3] = Xoshiro256PlusPlusRotLeft(rng_state[3], 45);
+ return result;
+}
+
+class DefaultRandom : public QuicRandom {
+ public:
+ DefaultRandom() {}
+ DefaultRandom(const DefaultRandom&) = delete;
+ DefaultRandom& operator=(const DefaultRandom&) = delete;
+ ~DefaultRandom() override {}
+
+ // QuicRandom implementation
+ void RandBytes(void* data, size_t len) override;
+ uint64_t RandUint64() override;
+ void InsecureRandBytes(void* data, size_t len) override;
+ uint64_t InsecureRandUint64() override;
+};
+
+void DefaultRandom::RandBytes(void* data, size_t len) {
+ RAND_bytes(reinterpret_cast<uint8_t*>(data), len);
+}
+
+uint64_t DefaultRandom::RandUint64() {
+ uint64_t value;
+ RandBytes(&value, sizeof(value));
+ return value;
+}
+
+void DefaultRandom::InsecureRandBytes(void* data, size_t len) {
+ while (len >= sizeof(uint64_t)) {
+ uint64_t random_bytes64 = Xoshiro256PlusPlus();
+ memcpy(data, &random_bytes64, sizeof(uint64_t));
+ data = reinterpret_cast<char*>(data) + sizeof(uint64_t);
+ len -= sizeof(uint64_t);
+ }
+ if (len > 0) {
+ QUICHE_DCHECK_LT(len, sizeof(uint64_t));
+ uint64_t random_bytes64 = Xoshiro256PlusPlus();
+ memcpy(data, &random_bytes64, len);
+ }
+}
+
+uint64_t DefaultRandom::InsecureRandUint64() { return Xoshiro256PlusPlus(); }
+
+} // namespace
+
+// static
+QuicRandom* QuicRandom::GetInstance() {
+ static DefaultRandom* random = new DefaultRandom();
+ return random;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_random.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_random.h
new file mode 100644
index 00000000000..47722cb7d2b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_random.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_QUIC_RANDOM_H_
+#define QUICHE_QUIC_CORE_CRYPTO_QUIC_RANDOM_H_
+
+#include <cstddef>
+#include <cstdint>
+
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// The interface for a random number generator.
+class QUIC_EXPORT_PRIVATE QuicRandom {
+ public:
+ virtual ~QuicRandom() {}
+
+ // Returns the default random number generator, which is cryptographically
+ // secure and thread-safe.
+ static QuicRandom* GetInstance();
+
+ // Generates |len| random bytes in the |data| buffer.
+ virtual void RandBytes(void* data, size_t len) = 0;
+
+ // Returns a random number in the range [0, kuint64max].
+ virtual uint64_t RandUint64() = 0;
+
+ // Generates |len| random bytes in the |data| buffer. This MUST NOT be used
+ // for any application that requires cryptographically-secure randomness.
+ virtual void InsecureRandBytes(void* data, size_t len) = 0;
+
+ // Returns a random number in the range [0, kuint64max]. This MUST NOT be used
+ // for any application that requires cryptographically-secure randomness.
+ virtual uint64_t InsecureRandUint64() = 0;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_RANDOM_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_random_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_random_test.cc
new file mode 100644
index 00000000000..2a43c291dd6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/quic_random_test.cc
@@ -0,0 +1,53 @@
+// Copyright (c) 2012 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 "quiche/quic/core/crypto/quic_random.h"
+
+#include "quiche/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+
+class QuicRandomTest : public QuicTest {};
+
+TEST_F(QuicRandomTest, RandBytes) {
+ unsigned char buf1[16];
+ unsigned char buf2[16];
+ memset(buf1, 0xaf, sizeof(buf1));
+ memset(buf2, 0xaf, sizeof(buf2));
+ ASSERT_EQ(0, memcmp(buf1, buf2, sizeof(buf1)));
+
+ QuicRandom* rng = QuicRandom::GetInstance();
+ rng->RandBytes(buf1, sizeof(buf1));
+ EXPECT_NE(0, memcmp(buf1, buf2, sizeof(buf1)));
+}
+
+TEST_F(QuicRandomTest, RandUint64) {
+ QuicRandom* rng = QuicRandom::GetInstance();
+ uint64_t value1 = rng->RandUint64();
+ uint64_t value2 = rng->RandUint64();
+ EXPECT_NE(value1, value2);
+}
+
+TEST_F(QuicRandomTest, InsecureRandBytes) {
+ unsigned char buf1[16];
+ unsigned char buf2[16];
+ memset(buf1, 0xaf, sizeof(buf1));
+ memset(buf2, 0xaf, sizeof(buf2));
+ ASSERT_EQ(0, memcmp(buf1, buf2, sizeof(buf1)));
+
+ QuicRandom* rng = QuicRandom::GetInstance();
+ rng->InsecureRandBytes(buf1, sizeof(buf1));
+ EXPECT_NE(0, memcmp(buf1, buf2, sizeof(buf1)));
+}
+
+TEST_F(QuicRandomTest, InsecureRandUint64) {
+ QuicRandom* rng = QuicRandom::GetInstance();
+ uint64_t value1 = rng->InsecureRandUint64();
+ uint64_t value2 = rng->InsecureRandUint64();
+ EXPECT_NE(value1, value2);
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/tls_client_connection.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/tls_client_connection.cc
new file mode 100644
index 00000000000..7436b23b9b1
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/tls_client_connection.cc
@@ -0,0 +1,48 @@
+// Copyright (c) 2019 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 "quiche/quic/core/crypto/tls_client_connection.h"
+
+namespace quic {
+
+TlsClientConnection::TlsClientConnection(SSL_CTX* ssl_ctx, Delegate* delegate,
+ QuicSSLConfig ssl_config)
+ : TlsConnection(ssl_ctx, delegate->ConnectionDelegate(),
+ std::move(ssl_config)),
+ delegate_(delegate) {}
+
+// static
+bssl::UniquePtr<SSL_CTX> TlsClientConnection::CreateSslCtx(
+ bool enable_early_data) {
+ bssl::UniquePtr<SSL_CTX> ssl_ctx = TlsConnection::CreateSslCtx();
+ // Configure certificate verification.
+ SSL_CTX_set_custom_verify(ssl_ctx.get(), SSL_VERIFY_PEER, &VerifyCallback);
+ int reverify_on_resume_enabled = 1;
+ SSL_CTX_set_reverify_on_resume(ssl_ctx.get(), reverify_on_resume_enabled);
+
+ // Configure session caching.
+ SSL_CTX_set_session_cache_mode(
+ ssl_ctx.get(), SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL);
+ SSL_CTX_sess_set_new_cb(ssl_ctx.get(), NewSessionCallback);
+
+ // TODO(wub): Always enable early data on the SSL_CTX, but allow it to be
+ // overridden on the SSL object, via QuicSSLConfig.
+ SSL_CTX_set_early_data_enabled(ssl_ctx.get(), enable_early_data);
+ return ssl_ctx;
+}
+
+void TlsClientConnection::SetCertChain(
+ const std::vector<CRYPTO_BUFFER*>& cert_chain, EVP_PKEY* privkey) {
+ SSL_set_chain_and_key(ssl(), cert_chain.data(), cert_chain.size(), privkey,
+ /*privkey_method=*/nullptr);
+}
+
+// static
+int TlsClientConnection::NewSessionCallback(SSL* ssl, SSL_SESSION* session) {
+ static_cast<TlsClientConnection*>(ConnectionFromSsl(ssl))
+ ->delegate_->InsertSession(bssl::UniquePtr<SSL_SESSION>(session));
+ return 1;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/tls_client_connection.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/tls_client_connection.h
new file mode 100644
index 00000000000..3bf35ce0ecd
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/tls_client_connection.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2019 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_TLS_CLIENT_CONNECTION_H_
+#define QUICHE_QUIC_CORE_CRYPTO_TLS_CLIENT_CONNECTION_H_
+
+#include "quiche/quic/core/crypto/tls_connection.h"
+
+namespace quic {
+
+// TlsClientConnection receives calls for client-specific BoringSSL callbacks
+// and calls its Delegate for the implementation of those callbacks.
+class QUIC_EXPORT_PRIVATE TlsClientConnection : public TlsConnection {
+ public:
+ // A TlsClientConnection::Delegate implements the client-specific methods that
+ // are set as callbacks for an SSL object.
+ class QUIC_EXPORT_PRIVATE Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ protected:
+ // Called when a NewSessionTicket is received from the server.
+ virtual void InsertSession(bssl::UniquePtr<SSL_SESSION> session) = 0;
+
+ // Provides the delegate for callbacks that are shared between client and
+ // server.
+ virtual TlsConnection::Delegate* ConnectionDelegate() = 0;
+
+ friend class TlsClientConnection;
+ };
+
+ TlsClientConnection(SSL_CTX* ssl_ctx, Delegate* delegate,
+ QuicSSLConfig ssl_config);
+
+ // Creates and configures an SSL_CTX that is appropriate for clients to use.
+ static bssl::UniquePtr<SSL_CTX> CreateSslCtx(bool enable_early_data);
+
+ // Set the client cert and private key to be used on this connection, if
+ // requested by the server.
+ void SetCertChain(const std::vector<CRYPTO_BUFFER*>& cert_chain,
+ EVP_PKEY* privkey);
+
+ private:
+ // Registered as the callback for SSL_CTX_sess_set_new_cb, which calls
+ // Delegate::InsertSession.
+ static int NewSessionCallback(SSL* ssl, SSL_SESSION* session);
+
+ Delegate* delegate_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_TLS_CLIENT_CONNECTION_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/tls_connection.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/tls_connection.cc
new file mode 100644
index 00000000000..61e4c23b03d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/tls_connection.cc
@@ -0,0 +1,211 @@
+// Copyright (c) 2019 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 "quiche/quic/core/crypto/tls_connection.h"
+
+#include "absl/strings/string_view.h"
+#include "openssl/ssl.h"
+#include "quiche/quic/platform/api/quic_bug_tracker.h"
+
+namespace quic {
+
+namespace {
+
+// BoringSSL allows storing extra data off of some of its data structures,
+// including the SSL struct. To allow for multiple callers to store data, each
+// caller can use a different index for setting and getting data. These indices
+// are globals handed out by calling SSL_get_ex_new_index.
+//
+// SslIndexSingleton calls SSL_get_ex_new_index on its construction, and then
+// provides this index to be used in calls to SSL_get_ex_data/SSL_set_ex_data.
+// This is used to store in the SSL struct a pointer to the TlsConnection which
+// owns it.
+class SslIndexSingleton {
+ public:
+ static SslIndexSingleton* GetInstance() {
+ static SslIndexSingleton* instance = new SslIndexSingleton();
+ return instance;
+ }
+
+ int ssl_ex_data_index_connection() const {
+ return ssl_ex_data_index_connection_;
+ }
+
+ private:
+ SslIndexSingleton() {
+ CRYPTO_library_init();
+ ssl_ex_data_index_connection_ =
+ SSL_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr);
+ QUICHE_CHECK_LE(0, ssl_ex_data_index_connection_);
+ }
+
+ SslIndexSingleton(const SslIndexSingleton&) = delete;
+ SslIndexSingleton& operator=(const SslIndexSingleton&) = delete;
+
+ // The index to supply to SSL_get_ex_data/SSL_set_ex_data for getting/setting
+ // the TlsConnection pointer.
+ int ssl_ex_data_index_connection_;
+};
+
+} // namespace
+
+// static
+EncryptionLevel TlsConnection::QuicEncryptionLevel(
+ enum ssl_encryption_level_t level) {
+ switch (level) {
+ case ssl_encryption_initial:
+ return ENCRYPTION_INITIAL;
+ case ssl_encryption_early_data:
+ return ENCRYPTION_ZERO_RTT;
+ case ssl_encryption_handshake:
+ return ENCRYPTION_HANDSHAKE;
+ case ssl_encryption_application:
+ return ENCRYPTION_FORWARD_SECURE;
+ default:
+ QUIC_BUG(quic_bug_10698_1)
+ << "Invalid ssl_encryption_level_t " << static_cast<int>(level);
+ return ENCRYPTION_INITIAL;
+ }
+}
+
+// static
+enum ssl_encryption_level_t TlsConnection::BoringEncryptionLevel(
+ EncryptionLevel level) {
+ switch (level) {
+ case ENCRYPTION_INITIAL:
+ return ssl_encryption_initial;
+ case ENCRYPTION_HANDSHAKE:
+ return ssl_encryption_handshake;
+ case ENCRYPTION_ZERO_RTT:
+ return ssl_encryption_early_data;
+ case ENCRYPTION_FORWARD_SECURE:
+ return ssl_encryption_application;
+ default:
+ QUIC_BUG(quic_bug_10698_2)
+ << "Invalid encryption level " << static_cast<int>(level);
+ return ssl_encryption_initial;
+ }
+}
+
+TlsConnection::TlsConnection(SSL_CTX* ssl_ctx,
+ TlsConnection::Delegate* delegate,
+ QuicSSLConfig ssl_config)
+ : delegate_(delegate),
+ ssl_(SSL_new(ssl_ctx)),
+ ssl_config_(std::move(ssl_config)) {
+ SSL_set_ex_data(
+ ssl(), SslIndexSingleton::GetInstance()->ssl_ex_data_index_connection(),
+ this);
+ if (ssl_config_.early_data_enabled.has_value()) {
+ const int early_data_enabled = *ssl_config_.early_data_enabled ? 1 : 0;
+ SSL_set_early_data_enabled(ssl(), early_data_enabled);
+ }
+ if (ssl_config_.signing_algorithm_prefs.has_value()) {
+ SSL_set_signing_algorithm_prefs(
+ ssl(), ssl_config_.signing_algorithm_prefs->data(),
+ ssl_config_.signing_algorithm_prefs->size());
+ }
+ if (ssl_config_.disable_ticket_support.has_value()) {
+ if (*ssl_config_.disable_ticket_support) {
+ SSL_set_options(ssl(), SSL_OP_NO_TICKET);
+ }
+ }
+}
+
+void TlsConnection::EnableInfoCallback() {
+ SSL_set_info_callback(
+ ssl(), +[](const SSL* ssl, int type, int value) {
+ ConnectionFromSsl(ssl)->delegate_->InfoCallback(type, value);
+ });
+}
+
+void TlsConnection::DisableTicketSupport() {
+ ssl_config_.disable_ticket_support = true;
+ SSL_set_options(ssl(), SSL_OP_NO_TICKET);
+}
+
+// static
+bssl::UniquePtr<SSL_CTX> TlsConnection::CreateSslCtx() {
+ CRYPTO_library_init();
+ bssl::UniquePtr<SSL_CTX> ssl_ctx(SSL_CTX_new(TLS_with_buffers_method()));
+ SSL_CTX_set_min_proto_version(ssl_ctx.get(), TLS1_3_VERSION);
+ SSL_CTX_set_max_proto_version(ssl_ctx.get(), TLS1_3_VERSION);
+ SSL_CTX_set_quic_method(ssl_ctx.get(), &kSslQuicMethod);
+ return ssl_ctx;
+}
+
+// static
+TlsConnection* TlsConnection::ConnectionFromSsl(const SSL* ssl) {
+ return reinterpret_cast<TlsConnection*>(SSL_get_ex_data(
+ ssl, SslIndexSingleton::GetInstance()->ssl_ex_data_index_connection()));
+}
+
+// static
+enum ssl_verify_result_t TlsConnection::VerifyCallback(SSL* ssl,
+ uint8_t* out_alert) {
+ return ConnectionFromSsl(ssl)->delegate_->VerifyCert(out_alert);
+}
+
+const SSL_QUIC_METHOD TlsConnection::kSslQuicMethod{
+ TlsConnection::SetReadSecretCallback, TlsConnection::SetWriteSecretCallback,
+ TlsConnection::WriteMessageCallback, TlsConnection::FlushFlightCallback,
+ TlsConnection::SendAlertCallback};
+
+// static
+int TlsConnection::SetReadSecretCallback(SSL* ssl,
+ enum ssl_encryption_level_t level,
+ const SSL_CIPHER* cipher,
+ const uint8_t* secret,
+ size_t secret_length) {
+ // TODO(nharper): replace this vector with a span (which unfortunately doesn't
+ // yet exist in quic/platform/api).
+ std::vector<uint8_t> secret_vec(secret, secret + secret_length);
+ TlsConnection::Delegate* delegate = ConnectionFromSsl(ssl)->delegate_;
+ if (!delegate->SetReadSecret(QuicEncryptionLevel(level), cipher,
+ secret_vec)) {
+ return 0;
+ }
+ return 1;
+}
+
+// static
+int TlsConnection::SetWriteSecretCallback(SSL* ssl,
+ enum ssl_encryption_level_t level,
+ const SSL_CIPHER* cipher,
+ const uint8_t* secret,
+ size_t secret_length) {
+ // TODO(nharper): replace this vector with a span (which unfortunately doesn't
+ // yet exist in quic/platform/api).
+ std::vector<uint8_t> secret_vec(secret, secret + secret_length);
+ TlsConnection::Delegate* delegate = ConnectionFromSsl(ssl)->delegate_;
+ delegate->SetWriteSecret(QuicEncryptionLevel(level), cipher, secret_vec);
+ return 1;
+}
+
+// static
+int TlsConnection::WriteMessageCallback(SSL* ssl,
+ enum ssl_encryption_level_t level,
+ const uint8_t* data, size_t len) {
+ ConnectionFromSsl(ssl)->delegate_->WriteMessage(
+ QuicEncryptionLevel(level),
+ absl::string_view(reinterpret_cast<const char*>(data), len));
+ return 1;
+}
+
+// static
+int TlsConnection::FlushFlightCallback(SSL* ssl) {
+ ConnectionFromSsl(ssl)->delegate_->FlushFlight();
+ return 1;
+}
+
+// static
+int TlsConnection::SendAlertCallback(SSL* ssl,
+ enum ssl_encryption_level_t level,
+ uint8_t desc) {
+ ConnectionFromSsl(ssl)->delegate_->SendAlert(QuicEncryptionLevel(level),
+ desc);
+ return 1;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/tls_connection.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/tls_connection.h
new file mode 100644
index 00000000000..a4887e8c32b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/tls_connection.h
@@ -0,0 +1,153 @@
+// Copyright (c) 2019 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_TLS_CONNECTION_H_
+#define QUICHE_QUIC_CORE_CRYPTO_TLS_CONNECTION_H_
+
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "openssl/ssl.h"
+#include "quiche/quic/core/quic_types.h"
+
+namespace quic {
+
+// TlsConnection wraps BoringSSL's SSL object which represents a single TLS
+// connection. Callbacks set in BoringSSL which are called with an SSL* argument
+// will get dispatched to the TlsConnection object owning that SSL. In turn, the
+// TlsConnection will delegate the implementation of that callback to its
+// Delegate.
+//
+// The owner of the TlsConnection is responsible for driving the TLS handshake
+// (and other interactions with the SSL*). This class only handles mapping
+// callbacks to the correct instance.
+class QUIC_EXPORT_PRIVATE TlsConnection {
+ public:
+ // A TlsConnection::Delegate implements the methods that are set as callbacks
+ // of TlsConnection.
+ class QUIC_EXPORT_PRIVATE Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ protected:
+ // Certificate management functions:
+
+ // Verifies the peer's certificate chain. It may use
+ // SSL_get0_peer_certificates to get the cert chain. This method returns
+ // ssl_verify_ok if the cert is valid, ssl_verify_invalid if it is invalid,
+ // or ssl_verify_retry if verification is happening asynchronously.
+ virtual enum ssl_verify_result_t VerifyCert(uint8_t* out_alert) = 0;
+
+ // QUIC-TLS interface functions:
+
+ // SetWriteSecret provides the encryption secret used to encrypt messages at
+ // encryption level |level|. The secret provided here is the one from the
+ // TLS 1.3 key schedule (RFC 8446 section 7.1), in particular the handshake
+ // traffic secrets and application traffic secrets. The provided write
+ // secret must be used with the provided cipher suite |cipher|.
+ virtual void SetWriteSecret(EncryptionLevel level, const SSL_CIPHER* cipher,
+ const std::vector<uint8_t>& write_secret) = 0;
+
+ // SetReadSecret is similar to SetWriteSecret, except that it is used for
+ // decrypting messages. SetReadSecret at a particular level is always called
+ // after SetWriteSecret for that level, except for ENCRYPTION_ZERO_RTT,
+ // where the EncryptionLevel for SetWriteSecret is
+ // ENCRYPTION_FORWARD_SECURE.
+ virtual bool SetReadSecret(EncryptionLevel level, const SSL_CIPHER* cipher,
+ const std::vector<uint8_t>& read_secret) = 0;
+
+ // WriteMessage is called when there is |data| from the TLS stack ready for
+ // the QUIC stack to write in a crypto frame. The data must be transmitted
+ // at encryption level |level|.
+ virtual void WriteMessage(EncryptionLevel level,
+ absl::string_view data) = 0;
+
+ // FlushFlight is called to signal that the current flight of messages have
+ // all been written (via calls to WriteMessage) and can be flushed to the
+ // underlying transport.
+ virtual void FlushFlight() = 0;
+
+ // SendAlert causes this TlsConnection to close the QUIC connection with an
+ // error code corersponding to the TLS alert description |desc| sent at
+ // level |level|.
+ virtual void SendAlert(EncryptionLevel level, uint8_t desc) = 0;
+
+ // Informational callback from BoringSSL. This callback is disabled by
+ // default, but can be enabled by TlsConnection::EnableInfoCallback.
+ //
+ // See |SSL_CTX_set_info_callback| for the meaning of |type| and |value|.
+ virtual void InfoCallback(int type, int value) = 0;
+
+ friend class TlsConnection;
+ };
+
+ TlsConnection(const TlsConnection&) = delete;
+ TlsConnection& operator=(const TlsConnection&) = delete;
+
+ // Configure the SSL such that delegate_->InfoCallback will be called.
+ void EnableInfoCallback();
+
+ // Configure the SSL to disable session ticket support. Note that, this
+ // function simply sets the |SSL_OP_NO_TICKET| option on the SSL object, it
+ // does not check whether it is too late to do so.
+ void DisableTicketSupport();
+
+ // Functions to convert between BoringSSL's enum ssl_encryption_level_t and
+ // QUIC's EncryptionLevel.
+ static EncryptionLevel QuicEncryptionLevel(enum ssl_encryption_level_t level);
+ static enum ssl_encryption_level_t BoringEncryptionLevel(
+ EncryptionLevel level);
+
+ SSL* ssl() const { return ssl_.get(); }
+
+ const QuicSSLConfig& ssl_config() const { return ssl_config_; }
+
+ protected:
+ // TlsConnection does not take ownership of |ssl_ctx| or |delegate|; they must
+ // outlive the TlsConnection object.
+ TlsConnection(SSL_CTX* ssl_ctx, Delegate* delegate, QuicSSLConfig ssl_config);
+
+ // Creates an SSL_CTX and configures it with the options that are appropriate
+ // for both client and server. The caller is responsible for ownership of the
+ // newly created struct.
+ static bssl::UniquePtr<SSL_CTX> CreateSslCtx();
+
+ // From a given SSL* |ssl|, returns a pointer to the TlsConnection that it
+ // belongs to. This helper method allows the callbacks set in BoringSSL to be
+ // dispatched to the correct TlsConnection from the SSL* passed into the
+ // callback.
+ static TlsConnection* ConnectionFromSsl(const SSL* ssl);
+
+ // Registered as the callback for SSL(_CTX)_set_custom_verify. The
+ // implementation is delegated to Delegate::VerifyCert.
+ static enum ssl_verify_result_t VerifyCallback(SSL* ssl, uint8_t* out_alert);
+
+ QuicSSLConfig& mutable_ssl_config() { return ssl_config_; }
+
+ private:
+ // TlsConnection implements SSL_QUIC_METHOD, which provides the interface
+ // between BoringSSL's TLS stack and a QUIC implementation.
+ static const SSL_QUIC_METHOD kSslQuicMethod;
+
+ // The following static functions make up the members of kSslQuicMethod:
+ static int SetReadSecretCallback(SSL* ssl, enum ssl_encryption_level_t level,
+ const SSL_CIPHER* cipher,
+ const uint8_t* secret, size_t secret_len);
+ static int SetWriteSecretCallback(SSL* ssl, enum ssl_encryption_level_t level,
+ const SSL_CIPHER* cipher,
+ const uint8_t* secret, size_t secret_len);
+ static int WriteMessageCallback(SSL* ssl, enum ssl_encryption_level_t level,
+ const uint8_t* data, size_t len);
+ static int FlushFlightCallback(SSL* ssl);
+ static int SendAlertCallback(SSL* ssl, enum ssl_encryption_level_t level,
+ uint8_t desc);
+
+ Delegate* delegate_;
+ bssl::UniquePtr<SSL> ssl_;
+ QuicSSLConfig ssl_config_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_TLS_CONNECTION_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/tls_server_connection.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/tls_server_connection.cc
new file mode 100644
index 00000000000..ed7e5b238fe
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/tls_server_connection.cc
@@ -0,0 +1,168 @@
+// Copyright (c) 2019 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 "quiche/quic/core/crypto/tls_server_connection.h"
+
+#include "absl/strings/string_view.h"
+#include "openssl/ssl.h"
+#include "quiche/quic/core/crypto/proof_source.h"
+#include "quiche/quic/core/quic_types.h"
+#include "quiche/quic/platform/api/quic_flag_utils.h"
+#include "quiche/quic/platform/api/quic_flags.h"
+
+namespace quic {
+
+TlsServerConnection::TlsServerConnection(SSL_CTX* ssl_ctx, Delegate* delegate,
+ QuicSSLConfig ssl_config)
+ : TlsConnection(ssl_ctx, delegate->ConnectionDelegate(),
+ std::move(ssl_config)),
+ delegate_(delegate) {
+ // By default, cert verify callback is not installed on ssl(), so only need to
+ // UpdateCertVerifyCallback() if client_cert_mode is not kNone.
+ if (TlsConnection::ssl_config().client_cert_mode != ClientCertMode::kNone) {
+ UpdateCertVerifyCallback();
+ }
+}
+
+// static
+bssl::UniquePtr<SSL_CTX> TlsServerConnection::CreateSslCtx(
+ ProofSource* proof_source) {
+ bssl::UniquePtr<SSL_CTX> ssl_ctx = TlsConnection::CreateSslCtx();
+
+ // Server does not request/verify client certs by default. Individual server
+ // connections may call SSL_set_custom_verify on their SSL object to request
+ // client certs.
+
+ SSL_CTX_set_tlsext_servername_callback(ssl_ctx.get(),
+ &TlsExtServernameCallback);
+ SSL_CTX_set_alpn_select_cb(ssl_ctx.get(), &SelectAlpnCallback, nullptr);
+ // We don't actually need the TicketCrypter here, but we need to know
+ // whether it's set.
+ if (proof_source->GetTicketCrypter()) {
+ QUIC_CODE_COUNT(quic_session_tickets_enabled);
+ SSL_CTX_set_ticket_aead_method(ssl_ctx.get(),
+ &TlsServerConnection::kSessionTicketMethod);
+ } else {
+ QUIC_CODE_COUNT(quic_session_tickets_disabled);
+ }
+
+ SSL_CTX_set_early_data_enabled(ssl_ctx.get(), 1);
+
+ SSL_CTX_set_select_certificate_cb(
+ ssl_ctx.get(), &TlsServerConnection::EarlySelectCertCallback);
+ SSL_CTX_set_options(ssl_ctx.get(), SSL_OP_CIPHER_SERVER_PREFERENCE);
+ return ssl_ctx;
+}
+
+void TlsServerConnection::SetCertChain(
+ const std::vector<CRYPTO_BUFFER*>& cert_chain) {
+ SSL_set_chain_and_key(ssl(), cert_chain.data(), cert_chain.size(), nullptr,
+ &TlsServerConnection::kPrivateKeyMethod);
+}
+
+void TlsServerConnection::SetClientCertMode(ClientCertMode client_cert_mode) {
+ if (ssl_config().client_cert_mode == client_cert_mode) {
+ return;
+ }
+
+ mutable_ssl_config().client_cert_mode = client_cert_mode;
+ UpdateCertVerifyCallback();
+}
+
+void TlsServerConnection::UpdateCertVerifyCallback() {
+ const ClientCertMode client_cert_mode = ssl_config().client_cert_mode;
+ if (client_cert_mode == ClientCertMode::kNone) {
+ SSL_set_custom_verify(ssl(), SSL_VERIFY_NONE, nullptr);
+ return;
+ }
+
+ int mode = SSL_VERIFY_PEER;
+ if (client_cert_mode == ClientCertMode::kRequire) {
+ mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+ } else {
+ QUICHE_DCHECK_EQ(client_cert_mode, ClientCertMode::kRequest);
+ }
+ SSL_set_custom_verify(ssl(), mode, &VerifyCallback);
+}
+
+const SSL_PRIVATE_KEY_METHOD TlsServerConnection::kPrivateKeyMethod{
+ &TlsServerConnection::PrivateKeySign,
+ nullptr, // decrypt
+ &TlsServerConnection::PrivateKeyComplete,
+};
+
+// static
+TlsServerConnection* TlsServerConnection::ConnectionFromSsl(SSL* ssl) {
+ return static_cast<TlsServerConnection*>(
+ TlsConnection::ConnectionFromSsl(ssl));
+}
+
+// static
+ssl_select_cert_result_t TlsServerConnection::EarlySelectCertCallback(
+ const SSL_CLIENT_HELLO* client_hello) {
+ return ConnectionFromSsl(client_hello->ssl)
+ ->delegate_->EarlySelectCertCallback(client_hello);
+}
+
+// static
+int TlsServerConnection::TlsExtServernameCallback(SSL* ssl, int* out_alert,
+ void* /*arg*/) {
+ return ConnectionFromSsl(ssl)->delegate_->TlsExtServernameCallback(out_alert);
+}
+
+// static
+int TlsServerConnection::SelectAlpnCallback(SSL* ssl, const uint8_t** out,
+ uint8_t* out_len, const uint8_t* in,
+ unsigned in_len, void* /*arg*/) {
+ return ConnectionFromSsl(ssl)->delegate_->SelectAlpn(out, out_len, in,
+ in_len);
+}
+
+// static
+ssl_private_key_result_t TlsServerConnection::PrivateKeySign(
+ SSL* ssl, uint8_t* out, size_t* out_len, size_t max_out, uint16_t sig_alg,
+ const uint8_t* in, size_t in_len) {
+ return ConnectionFromSsl(ssl)->delegate_->PrivateKeySign(
+ out, out_len, max_out, sig_alg,
+ absl::string_view(reinterpret_cast<const char*>(in), in_len));
+}
+
+// static
+ssl_private_key_result_t TlsServerConnection::PrivateKeyComplete(
+ SSL* ssl, uint8_t* out, size_t* out_len, size_t max_out) {
+ return ConnectionFromSsl(ssl)->delegate_->PrivateKeyComplete(out, out_len,
+ max_out);
+}
+
+// static
+const SSL_TICKET_AEAD_METHOD TlsServerConnection::kSessionTicketMethod{
+ TlsServerConnection::SessionTicketMaxOverhead,
+ TlsServerConnection::SessionTicketSeal,
+ TlsServerConnection::SessionTicketOpen,
+};
+
+// static
+size_t TlsServerConnection::SessionTicketMaxOverhead(SSL* ssl) {
+ return ConnectionFromSsl(ssl)->delegate_->SessionTicketMaxOverhead();
+}
+
+// static
+int TlsServerConnection::SessionTicketSeal(SSL* ssl, uint8_t* out,
+ size_t* out_len, size_t max_out_len,
+ const uint8_t* in, size_t in_len) {
+ return ConnectionFromSsl(ssl)->delegate_->SessionTicketSeal(
+ out, out_len, max_out_len,
+ absl::string_view(reinterpret_cast<const char*>(in), in_len));
+}
+
+// static
+enum ssl_ticket_aead_result_t TlsServerConnection::SessionTicketOpen(
+ SSL* ssl, uint8_t* out, size_t* out_len, size_t max_out_len,
+ const uint8_t* in, size_t in_len) {
+ return ConnectionFromSsl(ssl)->delegate_->SessionTicketOpen(
+ out, out_len, max_out_len,
+ absl::string_view(reinterpret_cast<const char*>(in), in_len));
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/tls_server_connection.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/tls_server_connection.h
new file mode 100644
index 00000000000..3c0eb7e768c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/tls_server_connection.h
@@ -0,0 +1,180 @@
+// Copyright (c) 2019 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_TLS_SERVER_CONNECTION_H_
+#define QUICHE_QUIC_CORE_CRYPTO_TLS_SERVER_CONNECTION_H_
+
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/crypto/proof_source.h"
+#include "quiche/quic/core/crypto/tls_connection.h"
+
+namespace quic {
+
+// TlsServerConnection receives calls for client-specific BoringSSL callbacks
+// and calls its Delegate for the implementation of those callbacks.
+class QUIC_EXPORT_PRIVATE TlsServerConnection : public TlsConnection {
+ public:
+ // A TlsServerConnection::Delegate implement the server-specific methods that
+ // are set as callbacks for an SSL object.
+ class QUIC_EXPORT_PRIVATE Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ protected:
+ // Called from BoringSSL right after SNI is extracted, which is very early
+ // in the handshake process.
+ virtual ssl_select_cert_result_t EarlySelectCertCallback(
+ const SSL_CLIENT_HELLO* client_hello) = 0;
+
+ // Called after the ClientHello extensions have been successfully parsed.
+ // Returns an SSL_TLSEXT_ERR_* value (see
+ // https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set_tlsext_servername_callback).
+ //
+ // On success, return SSL_TLSEXT_ERR_OK causes the server_name extension to
+ // be acknowledged in the ServerHello, or return SSL_TLSEXT_ERR_NOACK which
+ // causes it to be not acknowledged.
+ //
+ // If the function returns SSL_TLSEXT_ERR_ALERT_FATAL, then it puts in
+ // |*out_alert| the TLS alert value that the server will send.
+ //
+ virtual int TlsExtServernameCallback(int* out_alert) = 0;
+
+ // Selects which ALPN to use based on the list sent by the client.
+ virtual int SelectAlpn(const uint8_t** out, uint8_t* out_len,
+ const uint8_t* in, unsigned in_len) = 0;
+
+ // Signs |in| using the signature algorithm specified by |sig_alg| (an
+ // SSL_SIGN_* value). If the signing operation cannot be completed
+ // synchronously, ssl_private_key_retry is returned. If there is an error
+ // signing, or if the signature is longer than |max_out|, then
+ // ssl_private_key_failure is returned. Otherwise, ssl_private_key_success
+ // is returned with the signature put in |*out| and the length in
+ // |*out_len|.
+ virtual ssl_private_key_result_t PrivateKeySign(uint8_t* out,
+ size_t* out_len,
+ size_t max_out,
+ uint16_t sig_alg,
+ absl::string_view in) = 0;
+
+ // When PrivateKeySign returns ssl_private_key_retry, PrivateKeyComplete
+ // will be called after the async sign operation has completed.
+ // PrivateKeyComplete puts the resulting signature in |*out| and length in
+ // |*out_len|. If the length is greater than |max_out| or if there was an
+ // error in signing, then ssl_private_key_failure is returned. Otherwise,
+ // ssl_private_key_success is returned.
+ virtual ssl_private_key_result_t PrivateKeyComplete(uint8_t* out,
+ size_t* out_len,
+ size_t max_out) = 0;
+
+ // The following functions are used to implement an SSL_TICKET_AEAD_METHOD.
+ // See
+ // https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#ssl_ticket_aead_result_t
+ // for details on the BoringSSL API.
+
+ // SessionTicketMaxOverhead returns the maximum number of bytes of overhead
+ // that SessionTicketSeal may add when encrypting a session ticket.
+ virtual size_t SessionTicketMaxOverhead() = 0;
+
+ // SessionTicketSeal encrypts the session ticket in |in|, putting the
+ // resulting encrypted ticket in |out|, writing the length of the bytes
+ // written to |*out_len|, which is no larger than |max_out_len|. It returns
+ // 1 on success and 0 on error.
+ virtual int SessionTicketSeal(uint8_t* out, size_t* out_len,
+ size_t max_out_len, absl::string_view in) = 0;
+
+ // SessionTicketOpen is called when BoringSSL has an encrypted session
+ // ticket |in| and wants the ticket decrypted. This decryption operation can
+ // happen synchronously or asynchronously.
+ //
+ // If the decrypted ticket is not available at the time of the function
+ // call, this function returns ssl_ticket_aead_retry. If this function
+ // returns ssl_ticket_aead_retry, then SSL_do_handshake will return
+ // SSL_ERROR_PENDING_TICKET. Once the pending ticket decryption has
+ // completed, SSL_do_handshake needs to be called again.
+ //
+ // When this function is called and the decrypted ticket is available
+ // (either the ticket was decrypted synchronously, or an asynchronous
+ // operation has completed and SSL_do_handshake has been called again), the
+ // decrypted ticket is put in |out|, and the length of that output is
+ // written to |*out_len|, not to exceed |max_out_len|, and
+ // ssl_ticket_aead_success is returned. If the ticket cannot be decrypted
+ // and should be ignored, this function returns
+ // ssl_ticket_aead_ignore_ticket and a full handshake will be performed
+ // instead. If a fatal error occurs, ssl_ticket_aead_error can be returned
+ // which will terminate the handshake.
+ virtual enum ssl_ticket_aead_result_t SessionTicketOpen(
+ uint8_t* out, size_t* out_len, size_t max_out_len,
+ absl::string_view in) = 0;
+
+ // Provides the delegate for callbacks that are shared between client and
+ // server.
+ virtual TlsConnection::Delegate* ConnectionDelegate() = 0;
+
+ friend class TlsServerConnection;
+ };
+
+ TlsServerConnection(SSL_CTX* ssl_ctx, Delegate* delegate,
+ QuicSSLConfig ssl_config);
+
+ // Creates and configures an SSL_CTX that is appropriate for servers to use.
+ static bssl::UniquePtr<SSL_CTX> CreateSslCtx(ProofSource* proof_source);
+
+ void SetCertChain(const std::vector<CRYPTO_BUFFER*>& cert_chain);
+
+ // Set the client cert mode to be used on this connection. This should be
+ // called right after cert selection at the latest, otherwise it is too late
+ // to has an effect.
+ void SetClientCertMode(ClientCertMode client_cert_mode);
+
+ private:
+ // Specialization of TlsConnection::ConnectionFromSsl.
+ static TlsServerConnection* ConnectionFromSsl(SSL* ssl);
+
+ static ssl_select_cert_result_t EarlySelectCertCallback(
+ const SSL_CLIENT_HELLO* client_hello);
+
+ // These functions are registered as callbacks in BoringSSL and delegate their
+ // implementation to the matching methods in Delegate above.
+ static int TlsExtServernameCallback(SSL* ssl, int* out_alert, void* arg);
+ static int SelectAlpnCallback(SSL* ssl, const uint8_t** out, uint8_t* out_len,
+ const uint8_t* in, unsigned in_len, void* arg);
+
+ // |kPrivateKeyMethod| is a vtable pointing to PrivateKeySign and
+ // PrivateKeyComplete used by the TLS stack to compute the signature for the
+ // CertificateVerify message (using the server's private key).
+ static const SSL_PRIVATE_KEY_METHOD kPrivateKeyMethod;
+
+ // The following functions make up the contents of |kPrivateKeyMethod|.
+ static ssl_private_key_result_t PrivateKeySign(
+ SSL* ssl, uint8_t* out, size_t* out_len, size_t max_out, uint16_t sig_alg,
+ const uint8_t* in, size_t in_len);
+ static ssl_private_key_result_t PrivateKeyComplete(SSL* ssl, uint8_t* out,
+ size_t* out_len,
+ size_t max_out);
+
+ // Implementation of SSL_TICKET_AEAD_METHOD which delegates to corresponding
+ // methods in TlsServerConnection::Delegate (a.k.a. TlsServerHandshaker).
+ static const SSL_TICKET_AEAD_METHOD kSessionTicketMethod;
+
+ // The following functions make up the contents of |kSessionTicketMethod|.
+ static size_t SessionTicketMaxOverhead(SSL* ssl);
+ static int SessionTicketSeal(SSL* ssl, uint8_t* out, size_t* out_len,
+ size_t max_out_len, const uint8_t* in,
+ size_t in_len);
+ static enum ssl_ticket_aead_result_t SessionTicketOpen(SSL* ssl, uint8_t* out,
+ size_t* out_len,
+ size_t max_out_len,
+ const uint8_t* in,
+ size_t in_len);
+
+ // Install custom verify callback on ssl() if |ssl_config().client_cert_mode|
+ // is not ClientCertMode::kNone. Uninstall otherwise.
+ void UpdateCertVerifyCallback();
+
+ Delegate* delegate_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_TLS_SERVER_CONNECTION_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/transport_parameters.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/transport_parameters.cc
new file mode 100644
index 00000000000..ca2adc8509f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/transport_parameters.cc
@@ -0,0 +1,1579 @@
+// Copyright (c) 2018 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 "quiche/quic/core/crypto/transport_parameters.h"
+
+#include <cstdint>
+#include <cstring>
+#include <forward_list>
+#include <memory>
+#include <utility>
+
+#include "absl/strings/escaping.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "openssl/digest.h"
+#include "openssl/sha.h"
+#include "quiche/quic/core/quic_connection_id.h"
+#include "quiche/quic/core/quic_data_reader.h"
+#include "quiche/quic/core/quic_data_writer.h"
+#include "quiche/quic/core/quic_types.h"
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/quic/core/quic_versions.h"
+#include "quiche/quic/platform/api/quic_bug_tracker.h"
+#include "quiche/quic/platform/api/quic_flag_utils.h"
+#include "quiche/quic/platform/api/quic_ip_address.h"
+
+namespace quic {
+
+// Values of the TransportParameterId enum as defined in the
+// "Transport Parameter Encoding" section of draft-ietf-quic-transport.
+// When parameters are encoded, one of these enum values is used to indicate
+// which parameter is encoded. The supported draft version is noted in
+// transport_parameters.h.
+enum TransportParameters::TransportParameterId : uint64_t {
+ kOriginalDestinationConnectionId = 0,
+ kMaxIdleTimeout = 1,
+ kStatelessResetToken = 2,
+ kMaxPacketSize = 3,
+ kInitialMaxData = 4,
+ kInitialMaxStreamDataBidiLocal = 5,
+ kInitialMaxStreamDataBidiRemote = 6,
+ kInitialMaxStreamDataUni = 7,
+ kInitialMaxStreamsBidi = 8,
+ kInitialMaxStreamsUni = 9,
+ kAckDelayExponent = 0xa,
+ kMaxAckDelay = 0xb,
+ kDisableActiveMigration = 0xc,
+ kPreferredAddress = 0xd,
+ kActiveConnectionIdLimit = 0xe,
+ kInitialSourceConnectionId = 0xf,
+ kRetrySourceConnectionId = 0x10,
+
+ kMaxDatagramFrameSize = 0x20,
+
+ kInitialRoundTripTime = 0x3127,
+ kGoogleConnectionOptions = 0x3128,
+ // 0x3129 was used to convey the user agent string.
+ // 0x312A was used only in T050 to indicate support for HANDSHAKE_DONE.
+ // 0x312B was used to indicate that QUIC+TLS key updates were not supported.
+ // 0x4751 was used for non-standard Google-specific parameters encoded as a
+ // Google QUIC_CRYPTO CHLO, it has been replaced by individual parameters.
+ kGoogleQuicVersion =
+ 0x4752, // Used to transmit version and supported_versions.
+
+ kMinAckDelay = 0xDE1A, // draft-iyengar-quic-delayed-ack.
+ kVersionInformation = 0xFF73DB, // draft-ietf-quic-version-negotiation.
+};
+
+namespace {
+
+// The following constants define minimum and maximum allowed values for some of
+// the parameters. These come from the "Transport Parameter Definitions"
+// section of draft-ietf-quic-transport.
+constexpr uint64_t kMinMaxPacketSizeTransportParam = 1200;
+constexpr uint64_t kMaxAckDelayExponentTransportParam = 20;
+constexpr uint64_t kDefaultAckDelayExponentTransportParam = 3;
+constexpr uint64_t kMaxMaxAckDelayTransportParam = 16383;
+constexpr uint64_t kDefaultMaxAckDelayTransportParam = 25;
+constexpr uint64_t kMinActiveConnectionIdLimitTransportParam = 2;
+constexpr uint64_t kDefaultActiveConnectionIdLimitTransportParam = 2;
+
+std::string TransportParameterIdToString(
+ TransportParameters::TransportParameterId param_id) {
+ switch (param_id) {
+ case TransportParameters::kOriginalDestinationConnectionId:
+ return "original_destination_connection_id";
+ case TransportParameters::kMaxIdleTimeout:
+ return "max_idle_timeout";
+ case TransportParameters::kStatelessResetToken:
+ return "stateless_reset_token";
+ case TransportParameters::kMaxPacketSize:
+ return "max_udp_payload_size";
+ case TransportParameters::kInitialMaxData:
+ return "initial_max_data";
+ case TransportParameters::kInitialMaxStreamDataBidiLocal:
+ return "initial_max_stream_data_bidi_local";
+ case TransportParameters::kInitialMaxStreamDataBidiRemote:
+ return "initial_max_stream_data_bidi_remote";
+ case TransportParameters::kInitialMaxStreamDataUni:
+ return "initial_max_stream_data_uni";
+ case TransportParameters::kInitialMaxStreamsBidi:
+ return "initial_max_streams_bidi";
+ case TransportParameters::kInitialMaxStreamsUni:
+ return "initial_max_streams_uni";
+ case TransportParameters::kAckDelayExponent:
+ return "ack_delay_exponent";
+ case TransportParameters::kMaxAckDelay:
+ return "max_ack_delay";
+ case TransportParameters::kDisableActiveMigration:
+ return "disable_active_migration";
+ case TransportParameters::kPreferredAddress:
+ return "preferred_address";
+ case TransportParameters::kActiveConnectionIdLimit:
+ return "active_connection_id_limit";
+ case TransportParameters::kInitialSourceConnectionId:
+ return "initial_source_connection_id";
+ case TransportParameters::kRetrySourceConnectionId:
+ return "retry_source_connection_id";
+ case TransportParameters::kMaxDatagramFrameSize:
+ return "max_datagram_frame_size";
+ case TransportParameters::kInitialRoundTripTime:
+ return "initial_round_trip_time";
+ case TransportParameters::kGoogleConnectionOptions:
+ return "google_connection_options";
+ case TransportParameters::kGoogleQuicVersion:
+ return "google-version";
+ case TransportParameters::kMinAckDelay:
+ return "min_ack_delay_us";
+ case TransportParameters::kVersionInformation:
+ return "version_information";
+ }
+ return absl::StrCat("Unknown(", param_id, ")");
+}
+
+bool TransportParameterIdIsKnown(
+ TransportParameters::TransportParameterId param_id) {
+ switch (param_id) {
+ case TransportParameters::kOriginalDestinationConnectionId:
+ case TransportParameters::kMaxIdleTimeout:
+ case TransportParameters::kStatelessResetToken:
+ case TransportParameters::kMaxPacketSize:
+ case TransportParameters::kInitialMaxData:
+ case TransportParameters::kInitialMaxStreamDataBidiLocal:
+ case TransportParameters::kInitialMaxStreamDataBidiRemote:
+ case TransportParameters::kInitialMaxStreamDataUni:
+ case TransportParameters::kInitialMaxStreamsBidi:
+ case TransportParameters::kInitialMaxStreamsUni:
+ case TransportParameters::kAckDelayExponent:
+ case TransportParameters::kMaxAckDelay:
+ case TransportParameters::kDisableActiveMigration:
+ case TransportParameters::kPreferredAddress:
+ case TransportParameters::kActiveConnectionIdLimit:
+ case TransportParameters::kInitialSourceConnectionId:
+ case TransportParameters::kRetrySourceConnectionId:
+ case TransportParameters::kMaxDatagramFrameSize:
+ case TransportParameters::kInitialRoundTripTime:
+ case TransportParameters::kGoogleConnectionOptions:
+ case TransportParameters::kGoogleQuicVersion:
+ case TransportParameters::kMinAckDelay:
+ case TransportParameters::kVersionInformation:
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+TransportParameters::IntegerParameter::IntegerParameter(
+ TransportParameters::TransportParameterId param_id, uint64_t default_value,
+ uint64_t min_value, uint64_t max_value)
+ : param_id_(param_id),
+ value_(default_value),
+ default_value_(default_value),
+ min_value_(min_value),
+ max_value_(max_value),
+ has_been_read_(false) {
+ QUICHE_DCHECK_LE(min_value, default_value);
+ QUICHE_DCHECK_LE(default_value, max_value);
+ QUICHE_DCHECK_LE(max_value, kVarInt62MaxValue);
+}
+
+TransportParameters::IntegerParameter::IntegerParameter(
+ TransportParameters::TransportParameterId param_id)
+ : TransportParameters::IntegerParameter::IntegerParameter(
+ param_id, 0, 0, kVarInt62MaxValue) {}
+
+void TransportParameters::IntegerParameter::set_value(uint64_t value) {
+ value_ = value;
+}
+
+uint64_t TransportParameters::IntegerParameter::value() const { return value_; }
+
+bool TransportParameters::IntegerParameter::IsValid() const {
+ return min_value_ <= value_ && value_ <= max_value_;
+}
+
+bool TransportParameters::IntegerParameter::Write(
+ QuicDataWriter* writer) const {
+ QUICHE_DCHECK(IsValid());
+ if (value_ == default_value_) {
+ // Do not write if the value is default.
+ return true;
+ }
+ if (!writer->WriteVarInt62(param_id_)) {
+ QUIC_BUG(quic_bug_10743_1) << "Failed to write param_id for " << *this;
+ return false;
+ }
+ const QuicVariableLengthIntegerLength value_length =
+ QuicDataWriter::GetVarInt62Len(value_);
+ if (!writer->WriteVarInt62(value_length)) {
+ QUIC_BUG(quic_bug_10743_2) << "Failed to write value_length for " << *this;
+ return false;
+ }
+ if (!writer->WriteVarInt62(value_, value_length)) {
+ QUIC_BUG(quic_bug_10743_3) << "Failed to write value for " << *this;
+ return false;
+ }
+ return true;
+}
+
+bool TransportParameters::IntegerParameter::Read(QuicDataReader* reader,
+ std::string* error_details) {
+ if (has_been_read_) {
+ *error_details =
+ "Received a second " + TransportParameterIdToString(param_id_);
+ return false;
+ }
+ has_been_read_ = true;
+
+ if (!reader->ReadVarInt62(&value_)) {
+ *error_details =
+ "Failed to parse value for " + TransportParameterIdToString(param_id_);
+ return false;
+ }
+ if (!reader->IsDoneReading()) {
+ *error_details =
+ absl::StrCat("Received unexpected ", reader->BytesRemaining(),
+ " bytes after parsing ", this->ToString(false));
+ return false;
+ }
+ return true;
+}
+
+std::string TransportParameters::IntegerParameter::ToString(
+ bool for_use_in_list) const {
+ if (for_use_in_list && value_ == default_value_) {
+ return "";
+ }
+ std::string rv = for_use_in_list ? " " : "";
+ absl::StrAppend(&rv, TransportParameterIdToString(param_id_), " ", value_);
+ if (!IsValid()) {
+ rv += " (Invalid)";
+ }
+ return rv;
+}
+
+std::ostream& operator<<(std::ostream& os,
+ const TransportParameters::IntegerParameter& param) {
+ os << param.ToString(/*for_use_in_list=*/false);
+ return os;
+}
+
+TransportParameters::PreferredAddress::PreferredAddress()
+ : ipv4_socket_address(QuicIpAddress::Any4(), 0),
+ ipv6_socket_address(QuicIpAddress::Any6(), 0),
+ connection_id(EmptyQuicConnectionId()),
+ stateless_reset_token(kStatelessResetTokenLength, 0) {}
+
+TransportParameters::PreferredAddress::~PreferredAddress() {}
+
+bool TransportParameters::PreferredAddress::operator==(
+ const PreferredAddress& rhs) const {
+ return ipv4_socket_address == rhs.ipv4_socket_address &&
+ ipv6_socket_address == rhs.ipv6_socket_address &&
+ connection_id == rhs.connection_id &&
+ stateless_reset_token == rhs.stateless_reset_token;
+}
+
+bool TransportParameters::PreferredAddress::operator!=(
+ const PreferredAddress& rhs) const {
+ return !(*this == rhs);
+}
+
+std::ostream& operator<<(
+ std::ostream& os,
+ const TransportParameters::PreferredAddress& preferred_address) {
+ os << preferred_address.ToString();
+ return os;
+}
+
+std::string TransportParameters::PreferredAddress::ToString() const {
+ return "[" + ipv4_socket_address.ToString() + " " +
+ ipv6_socket_address.ToString() + " connection_id " +
+ connection_id.ToString() + " stateless_reset_token " +
+ absl::BytesToHexString(absl::string_view(
+ reinterpret_cast<const char*>(stateless_reset_token.data()),
+ stateless_reset_token.size())) +
+ "]";
+}
+
+TransportParameters::LegacyVersionInformation::LegacyVersionInformation()
+ : version(0) {}
+
+bool TransportParameters::LegacyVersionInformation::operator==(
+ const LegacyVersionInformation& rhs) const {
+ return version == rhs.version && supported_versions == rhs.supported_versions;
+}
+
+bool TransportParameters::LegacyVersionInformation::operator!=(
+ const LegacyVersionInformation& rhs) const {
+ return !(*this == rhs);
+}
+
+std::string TransportParameters::LegacyVersionInformation::ToString() const {
+ std::string rv =
+ absl::StrCat("legacy[version ", QuicVersionLabelToString(version));
+ if (!supported_versions.empty()) {
+ absl::StrAppend(&rv,
+ " supported_versions " +
+ QuicVersionLabelVectorToString(supported_versions));
+ }
+ absl::StrAppend(&rv, "]");
+ return rv;
+}
+
+std::ostream& operator<<(std::ostream& os,
+ const TransportParameters::LegacyVersionInformation&
+ legacy_version_information) {
+ os << legacy_version_information.ToString();
+ return os;
+}
+
+TransportParameters::VersionInformation::VersionInformation()
+ : chosen_version(0) {}
+
+bool TransportParameters::VersionInformation::operator==(
+ const VersionInformation& rhs) const {
+ return chosen_version == rhs.chosen_version &&
+ other_versions == rhs.other_versions;
+}
+
+bool TransportParameters::VersionInformation::operator!=(
+ const VersionInformation& rhs) const {
+ return !(*this == rhs);
+}
+
+std::string TransportParameters::VersionInformation::ToString() const {
+ std::string rv = absl::StrCat("[chosen_version ",
+ QuicVersionLabelToString(chosen_version));
+ if (!other_versions.empty()) {
+ absl::StrAppend(&rv, " other_versions " +
+ QuicVersionLabelVectorToString(other_versions));
+ }
+ absl::StrAppend(&rv, "]");
+ return rv;
+}
+
+std::ostream& operator<<(
+ std::ostream& os,
+ const TransportParameters::VersionInformation& version_information) {
+ os << version_information.ToString();
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os, const TransportParameters& params) {
+ os << params.ToString();
+ return os;
+}
+
+std::string TransportParameters::ToString() const {
+ std::string rv = "[";
+ if (perspective == Perspective::IS_SERVER) {
+ rv += "Server";
+ } else {
+ rv += "Client";
+ }
+ if (legacy_version_information.has_value()) {
+ rv += " " + legacy_version_information.value().ToString();
+ }
+ if (version_information.has_value()) {
+ rv += " " + version_information.value().ToString();
+ }
+ if (original_destination_connection_id.has_value()) {
+ rv += " " + TransportParameterIdToString(kOriginalDestinationConnectionId) +
+ " " + original_destination_connection_id.value().ToString();
+ }
+ rv += max_idle_timeout_ms.ToString(/*for_use_in_list=*/true);
+ if (!stateless_reset_token.empty()) {
+ rv += " " + TransportParameterIdToString(kStatelessResetToken) + " " +
+ absl::BytesToHexString(absl::string_view(
+ reinterpret_cast<const char*>(stateless_reset_token.data()),
+ stateless_reset_token.size()));
+ }
+ rv += max_udp_payload_size.ToString(/*for_use_in_list=*/true);
+ rv += initial_max_data.ToString(/*for_use_in_list=*/true);
+ rv += initial_max_stream_data_bidi_local.ToString(/*for_use_in_list=*/true);
+ rv += initial_max_stream_data_bidi_remote.ToString(/*for_use_in_list=*/true);
+ rv += initial_max_stream_data_uni.ToString(/*for_use_in_list=*/true);
+ rv += initial_max_streams_bidi.ToString(/*for_use_in_list=*/true);
+ rv += initial_max_streams_uni.ToString(/*for_use_in_list=*/true);
+ rv += ack_delay_exponent.ToString(/*for_use_in_list=*/true);
+ rv += max_ack_delay.ToString(/*for_use_in_list=*/true);
+ rv += min_ack_delay_us.ToString(/*for_use_in_list=*/true);
+ if (disable_active_migration) {
+ rv += " " + TransportParameterIdToString(kDisableActiveMigration);
+ }
+ if (preferred_address) {
+ rv += " " + TransportParameterIdToString(kPreferredAddress) + " " +
+ preferred_address->ToString();
+ }
+ rv += active_connection_id_limit.ToString(/*for_use_in_list=*/true);
+ if (initial_source_connection_id.has_value()) {
+ rv += " " + TransportParameterIdToString(kInitialSourceConnectionId) + " " +
+ initial_source_connection_id.value().ToString();
+ }
+ if (retry_source_connection_id.has_value()) {
+ rv += " " + TransportParameterIdToString(kRetrySourceConnectionId) + " " +
+ retry_source_connection_id.value().ToString();
+ }
+ rv += max_datagram_frame_size.ToString(/*for_use_in_list=*/true);
+ rv += initial_round_trip_time_us.ToString(/*for_use_in_list=*/true);
+ if (google_connection_options.has_value()) {
+ rv += " " + TransportParameterIdToString(kGoogleConnectionOptions) + " ";
+ bool first = true;
+ for (const QuicTag& connection_option : google_connection_options.value()) {
+ if (first) {
+ first = false;
+ } else {
+ rv += ",";
+ }
+ rv += QuicTagToString(connection_option);
+ }
+ }
+ for (const auto& kv : custom_parameters) {
+ absl::StrAppend(&rv, " 0x", absl::Hex(static_cast<uint32_t>(kv.first)),
+ "=");
+ static constexpr size_t kMaxPrintableLength = 32;
+ if (kv.second.length() <= kMaxPrintableLength) {
+ rv += absl::BytesToHexString(kv.second);
+ } else {
+ absl::string_view truncated(kv.second.data(), kMaxPrintableLength);
+ rv += absl::StrCat(absl::BytesToHexString(truncated), "...(length ",
+ kv.second.length(), ")");
+ }
+ }
+ rv += "]";
+ return rv;
+}
+
+TransportParameters::TransportParameters()
+ : max_idle_timeout_ms(kMaxIdleTimeout),
+ max_udp_payload_size(kMaxPacketSize, kDefaultMaxPacketSizeTransportParam,
+ kMinMaxPacketSizeTransportParam, kVarInt62MaxValue),
+ initial_max_data(kInitialMaxData),
+ initial_max_stream_data_bidi_local(kInitialMaxStreamDataBidiLocal),
+ initial_max_stream_data_bidi_remote(kInitialMaxStreamDataBidiRemote),
+ initial_max_stream_data_uni(kInitialMaxStreamDataUni),
+ initial_max_streams_bidi(kInitialMaxStreamsBidi),
+ initial_max_streams_uni(kInitialMaxStreamsUni),
+ ack_delay_exponent(kAckDelayExponent,
+ kDefaultAckDelayExponentTransportParam, 0,
+ kMaxAckDelayExponentTransportParam),
+ max_ack_delay(kMaxAckDelay, kDefaultMaxAckDelayTransportParam, 0,
+ kMaxMaxAckDelayTransportParam),
+ min_ack_delay_us(kMinAckDelay, 0, 0,
+ kMaxMaxAckDelayTransportParam * kNumMicrosPerMilli),
+ disable_active_migration(false),
+ active_connection_id_limit(kActiveConnectionIdLimit,
+ kDefaultActiveConnectionIdLimitTransportParam,
+ kMinActiveConnectionIdLimitTransportParam,
+ kVarInt62MaxValue),
+ max_datagram_frame_size(kMaxDatagramFrameSize),
+ initial_round_trip_time_us(kInitialRoundTripTime)
+// Important note: any new transport parameters must be added
+// to TransportParameters::AreValid, SerializeTransportParameters and
+// ParseTransportParameters, TransportParameters's custom copy constructor, the
+// operator==, and TransportParametersTest.Comparator.
+{}
+
+TransportParameters::TransportParameters(const TransportParameters& other)
+ : perspective(other.perspective),
+ legacy_version_information(other.legacy_version_information),
+ version_information(other.version_information),
+ original_destination_connection_id(
+ other.original_destination_connection_id),
+ max_idle_timeout_ms(other.max_idle_timeout_ms),
+ stateless_reset_token(other.stateless_reset_token),
+ max_udp_payload_size(other.max_udp_payload_size),
+ initial_max_data(other.initial_max_data),
+ initial_max_stream_data_bidi_local(
+ other.initial_max_stream_data_bidi_local),
+ initial_max_stream_data_bidi_remote(
+ other.initial_max_stream_data_bidi_remote),
+ initial_max_stream_data_uni(other.initial_max_stream_data_uni),
+ initial_max_streams_bidi(other.initial_max_streams_bidi),
+ initial_max_streams_uni(other.initial_max_streams_uni),
+ ack_delay_exponent(other.ack_delay_exponent),
+ max_ack_delay(other.max_ack_delay),
+ min_ack_delay_us(other.min_ack_delay_us),
+ disable_active_migration(other.disable_active_migration),
+ active_connection_id_limit(other.active_connection_id_limit),
+ initial_source_connection_id(other.initial_source_connection_id),
+ retry_source_connection_id(other.retry_source_connection_id),
+ max_datagram_frame_size(other.max_datagram_frame_size),
+ initial_round_trip_time_us(other.initial_round_trip_time_us),
+ google_connection_options(other.google_connection_options),
+ custom_parameters(other.custom_parameters) {
+ if (other.preferred_address) {
+ preferred_address = std::make_unique<TransportParameters::PreferredAddress>(
+ *other.preferred_address);
+ }
+}
+
+bool TransportParameters::operator==(const TransportParameters& rhs) const {
+ if (!(perspective == rhs.perspective &&
+ legacy_version_information == rhs.legacy_version_information &&
+ version_information == rhs.version_information &&
+ original_destination_connection_id ==
+ rhs.original_destination_connection_id &&
+ max_idle_timeout_ms.value() == rhs.max_idle_timeout_ms.value() &&
+ stateless_reset_token == rhs.stateless_reset_token &&
+ max_udp_payload_size.value() == rhs.max_udp_payload_size.value() &&
+ initial_max_data.value() == rhs.initial_max_data.value() &&
+ initial_max_stream_data_bidi_local.value() ==
+ rhs.initial_max_stream_data_bidi_local.value() &&
+ initial_max_stream_data_bidi_remote.value() ==
+ rhs.initial_max_stream_data_bidi_remote.value() &&
+ initial_max_stream_data_uni.value() ==
+ rhs.initial_max_stream_data_uni.value() &&
+ initial_max_streams_bidi.value() ==
+ rhs.initial_max_streams_bidi.value() &&
+ initial_max_streams_uni.value() ==
+ rhs.initial_max_streams_uni.value() &&
+ ack_delay_exponent.value() == rhs.ack_delay_exponent.value() &&
+ max_ack_delay.value() == rhs.max_ack_delay.value() &&
+ min_ack_delay_us.value() == rhs.min_ack_delay_us.value() &&
+ disable_active_migration == rhs.disable_active_migration &&
+ active_connection_id_limit.value() ==
+ rhs.active_connection_id_limit.value() &&
+ initial_source_connection_id == rhs.initial_source_connection_id &&
+ retry_source_connection_id == rhs.retry_source_connection_id &&
+ max_datagram_frame_size.value() ==
+ rhs.max_datagram_frame_size.value() &&
+ initial_round_trip_time_us.value() ==
+ rhs.initial_round_trip_time_us.value() &&
+ google_connection_options == rhs.google_connection_options &&
+ custom_parameters == rhs.custom_parameters)) {
+ return false;
+ }
+
+ if ((!preferred_address && rhs.preferred_address) ||
+ (preferred_address && !rhs.preferred_address)) {
+ return false;
+ }
+ if (preferred_address && rhs.preferred_address &&
+ *preferred_address != *rhs.preferred_address) {
+ return false;
+ }
+
+ return true;
+}
+
+bool TransportParameters::operator!=(const TransportParameters& rhs) const {
+ return !(*this == rhs);
+}
+
+bool TransportParameters::AreValid(std::string* error_details) const {
+ QUICHE_DCHECK(perspective == Perspective::IS_CLIENT ||
+ perspective == Perspective::IS_SERVER);
+ if (perspective == Perspective::IS_CLIENT && !stateless_reset_token.empty()) {
+ *error_details = "Client cannot send stateless reset token";
+ return false;
+ }
+ if (perspective == Perspective::IS_CLIENT &&
+ original_destination_connection_id.has_value()) {
+ *error_details = "Client cannot send original_destination_connection_id";
+ return false;
+ }
+ if (!stateless_reset_token.empty() &&
+ stateless_reset_token.size() != kStatelessResetTokenLength) {
+ *error_details = absl::StrCat("Stateless reset token has bad length ",
+ stateless_reset_token.size());
+ return false;
+ }
+ if (perspective == Perspective::IS_CLIENT && preferred_address) {
+ *error_details = "Client cannot send preferred address";
+ return false;
+ }
+ if (preferred_address && preferred_address->stateless_reset_token.size() !=
+ kStatelessResetTokenLength) {
+ *error_details =
+ absl::StrCat("Preferred address stateless reset token has bad length ",
+ preferred_address->stateless_reset_token.size());
+ return false;
+ }
+ if (preferred_address &&
+ (!preferred_address->ipv4_socket_address.host().IsIPv4() ||
+ !preferred_address->ipv6_socket_address.host().IsIPv6())) {
+ QUIC_BUG(quic_bug_10743_4) << "Preferred address family failure";
+ *error_details = "Internal preferred address family failure";
+ return false;
+ }
+ if (perspective == Perspective::IS_CLIENT &&
+ retry_source_connection_id.has_value()) {
+ *error_details = "Client cannot send retry_source_connection_id";
+ return false;
+ }
+ for (const auto& kv : custom_parameters) {
+ if (TransportParameterIdIsKnown(kv.first)) {
+ *error_details = absl::StrCat("Using custom_parameters with known ID ",
+ TransportParameterIdToString(kv.first),
+ " is not allowed");
+ return false;
+ }
+ }
+ if (perspective == Perspective::IS_SERVER &&
+ initial_round_trip_time_us.value() > 0) {
+ *error_details = "Server cannot send initial round trip time";
+ return false;
+ }
+ if (version_information.has_value()) {
+ const QuicVersionLabel& chosen_version =
+ version_information.value().chosen_version;
+ const QuicVersionLabelVector& other_versions =
+ version_information.value().other_versions;
+ if (chosen_version == 0) {
+ *error_details = "Invalid chosen version";
+ return false;
+ }
+ if (perspective == Perspective::IS_CLIENT &&
+ std::find(other_versions.begin(), other_versions.end(),
+ chosen_version) == other_versions.end()) {
+ // When sent by the client, chosen_version needs to be present in
+ // other_versions because other_versions lists the compatible versions and
+ // the chosen version is part of that list. When sent by the server,
+ // other_version contains the list of fully-deployed versions which is
+ // generally equal to the list of supported versions but can slightly
+ // differ during removal of versions across a server fleet. See
+ // draft-ietf-quic-version-negotiation for details.
+ *error_details = "Client chosen version not in other versions";
+ return false;
+ }
+ }
+ const bool ok =
+ max_idle_timeout_ms.IsValid() && max_udp_payload_size.IsValid() &&
+ initial_max_data.IsValid() &&
+ initial_max_stream_data_bidi_local.IsValid() &&
+ initial_max_stream_data_bidi_remote.IsValid() &&
+ initial_max_stream_data_uni.IsValid() &&
+ initial_max_streams_bidi.IsValid() && initial_max_streams_uni.IsValid() &&
+ ack_delay_exponent.IsValid() && max_ack_delay.IsValid() &&
+ min_ack_delay_us.IsValid() && active_connection_id_limit.IsValid() &&
+ max_datagram_frame_size.IsValid() && initial_round_trip_time_us.IsValid();
+ if (!ok) {
+ *error_details = "Invalid transport parameters " + this->ToString();
+ }
+ return ok;
+}
+
+TransportParameters::~TransportParameters() = default;
+
+bool SerializeTransportParameters(ParsedQuicVersion /*version*/,
+ const TransportParameters& in,
+ std::vector<uint8_t>* out) {
+ std::string error_details;
+ if (!in.AreValid(&error_details)) {
+ QUIC_BUG(invalid transport parameters)
+ << "Not serializing invalid transport parameters: " << error_details;
+ return false;
+ }
+ if (!in.legacy_version_information.has_value() ||
+ in.legacy_version_information.value().version == 0 ||
+ (in.perspective == Perspective::IS_SERVER &&
+ in.legacy_version_information.value().supported_versions.empty())) {
+ QUIC_BUG(missing versions) << "Refusing to serialize without versions";
+ return false;
+ }
+ TransportParameters::ParameterMap custom_parameters = in.custom_parameters;
+ for (const auto& kv : custom_parameters) {
+ if (kv.first % 31 == 27) {
+ // See the "Reserved Transport Parameters" section of RFC 9000.
+ QUIC_BUG(custom_parameters with GREASE)
+ << "Serializing custom_parameters with GREASE ID " << kv.first
+ << " is not allowed";
+ return false;
+ }
+ }
+
+ // Maximum length of the GREASE transport parameter (see below).
+ static constexpr size_t kMaxGreaseLength = 16;
+
+ // Empirically transport parameters generally fit within 128 bytes, but we
+ // need to allocate the size up front. Integer transport parameters
+ // have a maximum encoded length of 24 bytes (3 variable length integers),
+ // other transport parameters have a length of 16 + the maximum value length.
+ static constexpr size_t kTypeAndValueLength = 2 * sizeof(uint64_t);
+ static constexpr size_t kIntegerParameterLength =
+ kTypeAndValueLength + sizeof(uint64_t);
+ static constexpr size_t kStatelessResetParameterLength =
+ kTypeAndValueLength + 16 /* stateless reset token length */;
+ static constexpr size_t kConnectionIdParameterLength =
+ kTypeAndValueLength + 255 /* maximum connection ID length */;
+ static constexpr size_t kPreferredAddressParameterLength =
+ kTypeAndValueLength + 4 /*IPv4 address */ + 2 /* IPv4 port */ +
+ 16 /* IPv6 address */ + 1 /* Connection ID length */ +
+ 255 /* maximum connection ID length */ + 16 /* stateless reset token */;
+ static constexpr size_t kKnownTransportParamLength =
+ kConnectionIdParameterLength + // original_destination_connection_id
+ kIntegerParameterLength + // max_idle_timeout
+ kStatelessResetParameterLength + // stateless_reset_token
+ kIntegerParameterLength + // max_udp_payload_size
+ kIntegerParameterLength + // initial_max_data
+ kIntegerParameterLength + // initial_max_stream_data_bidi_local
+ kIntegerParameterLength + // initial_max_stream_data_bidi_remote
+ kIntegerParameterLength + // initial_max_stream_data_uni
+ kIntegerParameterLength + // initial_max_streams_bidi
+ kIntegerParameterLength + // initial_max_streams_uni
+ kIntegerParameterLength + // ack_delay_exponent
+ kIntegerParameterLength + // max_ack_delay
+ kIntegerParameterLength + // min_ack_delay_us
+ kTypeAndValueLength + // disable_active_migration
+ kPreferredAddressParameterLength + // preferred_address
+ kIntegerParameterLength + // active_connection_id_limit
+ kConnectionIdParameterLength + // initial_source_connection_id
+ kConnectionIdParameterLength + // retry_source_connection_id
+ kIntegerParameterLength + // max_datagram_frame_size
+ kIntegerParameterLength + // initial_round_trip_time_us
+ kTypeAndValueLength + // google_connection_options
+ kTypeAndValueLength; // google-version
+
+ std::vector<TransportParameters::TransportParameterId> parameter_ids = {
+ TransportParameters::kOriginalDestinationConnectionId,
+ TransportParameters::kMaxIdleTimeout,
+ TransportParameters::kStatelessResetToken,
+ TransportParameters::kMaxPacketSize,
+ TransportParameters::kInitialMaxData,
+ TransportParameters::kInitialMaxStreamDataBidiLocal,
+ TransportParameters::kInitialMaxStreamDataBidiRemote,
+ TransportParameters::kInitialMaxStreamDataUni,
+ TransportParameters::kInitialMaxStreamsBidi,
+ TransportParameters::kInitialMaxStreamsUni,
+ TransportParameters::kAckDelayExponent,
+ TransportParameters::kMaxAckDelay,
+ TransportParameters::kMinAckDelay,
+ TransportParameters::kActiveConnectionIdLimit,
+ TransportParameters::kMaxDatagramFrameSize,
+ TransportParameters::kInitialRoundTripTime,
+ TransportParameters::kDisableActiveMigration,
+ TransportParameters::kPreferredAddress,
+ TransportParameters::kInitialSourceConnectionId,
+ TransportParameters::kRetrySourceConnectionId,
+ TransportParameters::kGoogleConnectionOptions,
+ TransportParameters::kGoogleQuicVersion,
+ TransportParameters::kVersionInformation,
+ };
+
+ size_t max_transport_param_length = kKnownTransportParamLength;
+ // google_connection_options.
+ if (in.google_connection_options.has_value()) {
+ max_transport_param_length +=
+ in.google_connection_options.value().size() * sizeof(QuicTag);
+ }
+ // Google-specific version extension.
+ if (in.legacy_version_information.has_value()) {
+ max_transport_param_length +=
+ sizeof(in.legacy_version_information.value().version) +
+ 1 /* versions length */ +
+ in.legacy_version_information.value().supported_versions.size() *
+ sizeof(QuicVersionLabel);
+ }
+ // version_information.
+ if (in.version_information.has_value()) {
+ max_transport_param_length +=
+ sizeof(in.version_information.value().chosen_version) +
+ // Add one for the added GREASE version.
+ (in.version_information.value().other_versions.size() + 1) *
+ sizeof(QuicVersionLabel);
+ }
+
+ // Add a random GREASE transport parameter, as defined in the
+ // "Reserved Transport Parameters" section of RFC 9000.
+ // This forces receivers to support unexpected input.
+ QuicRandom* random = QuicRandom::GetInstance();
+ // Transport parameter identifiers are 62 bits long so we need to
+ // ensure that the output of the computation below fits in 62 bits.
+ uint64_t grease_id64 = random->RandUint64() % ((1ULL << 62) - 31);
+ // Make sure grease_id % 31 == 27. Note that this is not uniformely
+ // distributed but is acceptable since no security depends on this
+ // randomness.
+ grease_id64 = (grease_id64 / 31) * 31 + 27;
+ TransportParameters::TransportParameterId grease_id =
+ static_cast<TransportParameters::TransportParameterId>(grease_id64);
+ const size_t grease_length = random->RandUint64() % kMaxGreaseLength;
+ QUICHE_DCHECK_GE(kMaxGreaseLength, grease_length);
+ char grease_contents[kMaxGreaseLength];
+ random->RandBytes(grease_contents, grease_length);
+ custom_parameters[grease_id] = std::string(grease_contents, grease_length);
+
+ // Custom parameters.
+ for (const auto& kv : custom_parameters) {
+ max_transport_param_length += kTypeAndValueLength + kv.second.length();
+ parameter_ids.push_back(kv.first);
+ }
+
+ // Randomize order of sent transport parameters by walking the array
+ // backwards and swapping each element with a random earlier one.
+ for (size_t i = parameter_ids.size() - 1; i > 0; i--) {
+ std::swap(parameter_ids[i],
+ parameter_ids[random->InsecureRandUint64() % (i + 1)]);
+ }
+
+ out->resize(max_transport_param_length);
+ QuicDataWriter writer(out->size(), reinterpret_cast<char*>(out->data()));
+
+ for (TransportParameters::TransportParameterId parameter_id : parameter_ids) {
+ switch (parameter_id) {
+ // original_destination_connection_id
+ case TransportParameters::kOriginalDestinationConnectionId: {
+ if (in.original_destination_connection_id.has_value()) {
+ QUICHE_DCHECK_EQ(Perspective::IS_SERVER, in.perspective);
+ QuicConnectionId original_destination_connection_id =
+ in.original_destination_connection_id.value();
+ if (!writer.WriteVarInt62(
+ TransportParameters::kOriginalDestinationConnectionId) ||
+ !writer.WriteStringPieceVarInt62(absl::string_view(
+ original_destination_connection_id.data(),
+ original_destination_connection_id.length()))) {
+ QUIC_BUG(Failed to write original_destination_connection_id)
+ << "Failed to write original_destination_connection_id "
+ << original_destination_connection_id << " for " << in;
+ return false;
+ }
+ }
+ } break;
+ // max_idle_timeout
+ case TransportParameters::kMaxIdleTimeout: {
+ if (!in.max_idle_timeout_ms.Write(&writer)) {
+ QUIC_BUG(Failed to write idle_timeout)
+ << "Failed to write idle_timeout for " << in;
+ return false;
+ }
+ } break;
+ // stateless_reset_token
+ case TransportParameters::kStatelessResetToken: {
+ if (!in.stateless_reset_token.empty()) {
+ QUICHE_DCHECK_EQ(kStatelessResetTokenLength,
+ in.stateless_reset_token.size());
+ QUICHE_DCHECK_EQ(Perspective::IS_SERVER, in.perspective);
+ if (!writer.WriteVarInt62(
+ TransportParameters::kStatelessResetToken) ||
+ !writer.WriteStringPieceVarInt62(
+ absl::string_view(reinterpret_cast<const char*>(
+ in.stateless_reset_token.data()),
+ in.stateless_reset_token.size()))) {
+ QUIC_BUG(Failed to write stateless_reset_token)
+ << "Failed to write stateless_reset_token of length "
+ << in.stateless_reset_token.size() << " for " << in;
+ return false;
+ }
+ }
+ } break;
+ // max_udp_payload_size
+ case TransportParameters::kMaxPacketSize: {
+ if (!in.max_udp_payload_size.Write(&writer)) {
+ QUIC_BUG(Failed to write max_udp_payload_size)
+ << "Failed to write max_udp_payload_size for " << in;
+ return false;
+ }
+ } break;
+ // initial_max_data
+ case TransportParameters::kInitialMaxData: {
+ if (!in.initial_max_data.Write(&writer)) {
+ QUIC_BUG(Failed to write initial_max_data)
+ << "Failed to write initial_max_data for " << in;
+ return false;
+ }
+ } break;
+ // initial_max_stream_data_bidi_local
+ case TransportParameters::kInitialMaxStreamDataBidiLocal: {
+ if (!in.initial_max_stream_data_bidi_local.Write(&writer)) {
+ QUIC_BUG(Failed to write initial_max_stream_data_bidi_local)
+ << "Failed to write initial_max_stream_data_bidi_local for "
+ << in;
+ return false;
+ }
+ } break;
+ // initial_max_stream_data_bidi_remote
+ case TransportParameters::kInitialMaxStreamDataBidiRemote: {
+ if (!in.initial_max_stream_data_bidi_remote.Write(&writer)) {
+ QUIC_BUG(Failed to write initial_max_stream_data_bidi_remote)
+ << "Failed to write initial_max_stream_data_bidi_remote for "
+ << in;
+ return false;
+ }
+ } break;
+ // initial_max_stream_data_uni
+ case TransportParameters::kInitialMaxStreamDataUni: {
+ if (!in.initial_max_stream_data_uni.Write(&writer)) {
+ QUIC_BUG(Failed to write initial_max_stream_data_uni)
+ << "Failed to write initial_max_stream_data_uni for " << in;
+ return false;
+ }
+ } break;
+ // initial_max_streams_bidi
+ case TransportParameters::kInitialMaxStreamsBidi: {
+ if (!in.initial_max_streams_bidi.Write(&writer)) {
+ QUIC_BUG(Failed to write initial_max_streams_bidi)
+ << "Failed to write initial_max_streams_bidi for " << in;
+ return false;
+ }
+ } break;
+ // initial_max_streams_uni
+ case TransportParameters::kInitialMaxStreamsUni: {
+ if (!in.initial_max_streams_uni.Write(&writer)) {
+ QUIC_BUG(Failed to write initial_max_streams_uni)
+ << "Failed to write initial_max_streams_uni for " << in;
+ return false;
+ }
+ } break;
+ // ack_delay_exponent
+ case TransportParameters::kAckDelayExponent: {
+ if (!in.ack_delay_exponent.Write(&writer)) {
+ QUIC_BUG(Failed to write ack_delay_exponent)
+ << "Failed to write ack_delay_exponent for " << in;
+ return false;
+ }
+ } break;
+ // max_ack_delay
+ case TransportParameters::kMaxAckDelay: {
+ if (!in.max_ack_delay.Write(&writer)) {
+ QUIC_BUG(Failed to write max_ack_delay)
+ << "Failed to write max_ack_delay for " << in;
+ return false;
+ }
+ } break;
+ // min_ack_delay_us
+ case TransportParameters::kMinAckDelay: {
+ if (!in.min_ack_delay_us.Write(&writer)) {
+ QUIC_BUG(Failed to write min_ack_delay_us)
+ << "Failed to write min_ack_delay_us for " << in;
+ return false;
+ }
+ } break;
+ // active_connection_id_limit
+ case TransportParameters::kActiveConnectionIdLimit: {
+ if (!in.active_connection_id_limit.Write(&writer)) {
+ QUIC_BUG(Failed to write active_connection_id_limit)
+ << "Failed to write active_connection_id_limit for " << in;
+ return false;
+ }
+ } break;
+ // max_datagram_frame_size
+ case TransportParameters::kMaxDatagramFrameSize: {
+ if (!in.max_datagram_frame_size.Write(&writer)) {
+ QUIC_BUG(Failed to write max_datagram_frame_size)
+ << "Failed to write max_datagram_frame_size for " << in;
+ return false;
+ }
+ } break;
+ // initial_round_trip_time_us
+ case TransportParameters::kInitialRoundTripTime: {
+ if (!in.initial_round_trip_time_us.Write(&writer)) {
+ QUIC_BUG(Failed to write initial_round_trip_time_us)
+ << "Failed to write initial_round_trip_time_us for " << in;
+ return false;
+ }
+ } break;
+ // disable_active_migration
+ case TransportParameters::kDisableActiveMigration: {
+ if (in.disable_active_migration) {
+ if (!writer.WriteVarInt62(
+ TransportParameters::kDisableActiveMigration) ||
+ !writer.WriteVarInt62(/* transport parameter length */ 0)) {
+ QUIC_BUG(Failed to write disable_active_migration)
+ << "Failed to write disable_active_migration for " << in;
+ return false;
+ }
+ }
+ } break;
+ // preferred_address
+ case TransportParameters::kPreferredAddress: {
+ if (in.preferred_address) {
+ std::string v4_address_bytes =
+ in.preferred_address->ipv4_socket_address.host().ToPackedString();
+ std::string v6_address_bytes =
+ in.preferred_address->ipv6_socket_address.host().ToPackedString();
+ if (v4_address_bytes.length() != 4 ||
+ v6_address_bytes.length() != 16 ||
+ in.preferred_address->stateless_reset_token.size() !=
+ kStatelessResetTokenLength) {
+ QUIC_BUG(quic_bug_10743_12)
+ << "Bad lengths " << *in.preferred_address;
+ return false;
+ }
+ const uint64_t preferred_address_length =
+ v4_address_bytes.length() + /* IPv4 port */ sizeof(uint16_t) +
+ v6_address_bytes.length() + /* IPv6 port */ sizeof(uint16_t) +
+ /* connection ID length byte */ sizeof(uint8_t) +
+ in.preferred_address->connection_id.length() +
+ in.preferred_address->stateless_reset_token.size();
+ if (!writer.WriteVarInt62(TransportParameters::kPreferredAddress) ||
+ !writer.WriteVarInt62(
+ /* transport parameter length */ preferred_address_length) ||
+ !writer.WriteStringPiece(v4_address_bytes) ||
+ !writer.WriteUInt16(
+ in.preferred_address->ipv4_socket_address.port()) ||
+ !writer.WriteStringPiece(v6_address_bytes) ||
+ !writer.WriteUInt16(
+ in.preferred_address->ipv6_socket_address.port()) ||
+ !writer.WriteUInt8(
+ in.preferred_address->connection_id.length()) ||
+ !writer.WriteBytes(
+ in.preferred_address->connection_id.data(),
+ in.preferred_address->connection_id.length()) ||
+ !writer.WriteBytes(
+ in.preferred_address->stateless_reset_token.data(),
+ in.preferred_address->stateless_reset_token.size())) {
+ QUIC_BUG(Failed to write preferred_address)
+ << "Failed to write preferred_address for " << in;
+ return false;
+ }
+ }
+ } break;
+ // initial_source_connection_id
+ case TransportParameters::kInitialSourceConnectionId: {
+ if (in.initial_source_connection_id.has_value()) {
+ QuicConnectionId initial_source_connection_id =
+ in.initial_source_connection_id.value();
+ if (!writer.WriteVarInt62(
+ TransportParameters::kInitialSourceConnectionId) ||
+ !writer.WriteStringPieceVarInt62(
+ absl::string_view(initial_source_connection_id.data(),
+ initial_source_connection_id.length()))) {
+ QUIC_BUG(Failed to write initial_source_connection_id)
+ << "Failed to write initial_source_connection_id "
+ << initial_source_connection_id << " for " << in;
+ return false;
+ }
+ }
+ } break;
+ // retry_source_connection_id
+ case TransportParameters::kRetrySourceConnectionId: {
+ if (in.retry_source_connection_id.has_value()) {
+ QUICHE_DCHECK_EQ(Perspective::IS_SERVER, in.perspective);
+ QuicConnectionId retry_source_connection_id =
+ in.retry_source_connection_id.value();
+ if (!writer.WriteVarInt62(
+ TransportParameters::kRetrySourceConnectionId) ||
+ !writer.WriteStringPieceVarInt62(
+ absl::string_view(retry_source_connection_id.data(),
+ retry_source_connection_id.length()))) {
+ QUIC_BUG(Failed to write retry_source_connection_id)
+ << "Failed to write retry_source_connection_id "
+ << retry_source_connection_id << " for " << in;
+ return false;
+ }
+ }
+ } break;
+ // Google-specific connection options.
+ case TransportParameters::kGoogleConnectionOptions: {
+ if (in.google_connection_options.has_value()) {
+ static_assert(
+ sizeof(in.google_connection_options.value().front()) == 4,
+ "bad size");
+ uint64_t connection_options_length =
+ in.google_connection_options.value().size() * 4;
+ if (!writer.WriteVarInt62(
+ TransportParameters::kGoogleConnectionOptions) ||
+ !writer.WriteVarInt62(
+ /* transport parameter length */ connection_options_length)) {
+ QUIC_BUG(Failed to write google_connection_options)
+ << "Failed to write google_connection_options of length "
+ << connection_options_length << " for " << in;
+ return false;
+ }
+ for (const QuicTag& connection_option :
+ in.google_connection_options.value()) {
+ if (!writer.WriteTag(connection_option)) {
+ QUIC_BUG(Failed to write google_connection_option)
+ << "Failed to write google_connection_option "
+ << QuicTagToString(connection_option) << " for " << in;
+ return false;
+ }
+ }
+ }
+ } break;
+ // Google-specific version extension.
+ case TransportParameters::kGoogleQuicVersion: {
+ if (!in.legacy_version_information.has_value()) {
+ break;
+ }
+ static_assert(sizeof(QuicVersionLabel) == sizeof(uint32_t),
+ "bad length");
+ uint64_t google_version_length =
+ sizeof(in.legacy_version_information.value().version);
+ if (in.perspective == Perspective::IS_SERVER) {
+ google_version_length +=
+ /* versions length */ sizeof(uint8_t) +
+ sizeof(QuicVersionLabel) * in.legacy_version_information.value()
+ .supported_versions.size();
+ }
+ if (!writer.WriteVarInt62(TransportParameters::kGoogleQuicVersion) ||
+ !writer.WriteVarInt62(
+ /* transport parameter length */ google_version_length) ||
+ !writer.WriteUInt32(
+ in.legacy_version_information.value().version)) {
+ QUIC_BUG(Failed to write Google version extension)
+ << "Failed to write Google version extension for " << in;
+ return false;
+ }
+ if (in.perspective == Perspective::IS_SERVER) {
+ if (!writer.WriteUInt8(sizeof(QuicVersionLabel) *
+ in.legacy_version_information.value()
+ .supported_versions.size())) {
+ QUIC_BUG(Failed to write versions length)
+ << "Failed to write versions length for " << in;
+ return false;
+ }
+ for (QuicVersionLabel version_label :
+ in.legacy_version_information.value().supported_versions) {
+ if (!writer.WriteUInt32(version_label)) {
+ QUIC_BUG(Failed to write supported version)
+ << "Failed to write supported version for " << in;
+ return false;
+ }
+ }
+ }
+ } break;
+ // version_information.
+ case TransportParameters::kVersionInformation: {
+ if (!in.version_information.has_value()) {
+ break;
+ }
+ static_assert(sizeof(QuicVersionLabel) == sizeof(uint32_t),
+ "bad length");
+ QuicVersionLabelVector other_versions =
+ in.version_information.value().other_versions;
+ // Insert one GREASE version at a random index.
+ const size_t grease_index =
+ random->InsecureRandUint64() % (other_versions.size() + 1);
+ other_versions.insert(
+ other_versions.begin() + grease_index,
+ CreateQuicVersionLabel(QuicVersionReservedForNegotiation()));
+ const uint64_t version_information_length =
+ sizeof(in.version_information.value().chosen_version) +
+ sizeof(QuicVersionLabel) * other_versions.size();
+ if (!writer.WriteVarInt62(TransportParameters::kVersionInformation) ||
+ !writer.WriteVarInt62(
+ /* transport parameter length */ version_information_length) ||
+ !writer.WriteUInt32(
+ in.version_information.value().chosen_version)) {
+ QUIC_BUG(Failed to write chosen version)
+ << "Failed to write chosen version for " << in;
+ return false;
+ }
+ for (QuicVersionLabel version_label : other_versions) {
+ if (!writer.WriteUInt32(version_label)) {
+ QUIC_BUG(Failed to write other version)
+ << "Failed to write other version for " << in;
+ return false;
+ }
+ }
+ } break;
+ // Custom parameters and GREASE.
+ default: {
+ auto it = custom_parameters.find(parameter_id);
+ if (it == custom_parameters.end()) {
+ QUIC_BUG(Unknown parameter) << "Unknown parameter " << parameter_id;
+ return false;
+ }
+ if (!writer.WriteVarInt62(parameter_id) ||
+ !writer.WriteStringPieceVarInt62(it->second)) {
+ QUIC_BUG(Failed to write custom parameter)
+ << "Failed to write custom parameter " << parameter_id;
+ return false;
+ }
+ } break;
+ }
+ }
+
+ out->resize(writer.length());
+
+ QUIC_DLOG(INFO) << "Serialized " << in << " as " << writer.length()
+ << " bytes";
+
+ return true;
+}
+
+bool ParseTransportParameters(ParsedQuicVersion version,
+ Perspective perspective, const uint8_t* in,
+ size_t in_len, TransportParameters* out,
+ std::string* error_details) {
+ out->perspective = perspective;
+ QuicDataReader reader(reinterpret_cast<const char*>(in), in_len);
+
+ while (!reader.IsDoneReading()) {
+ uint64_t param_id64;
+ if (!reader.ReadVarInt62(&param_id64)) {
+ *error_details = "Failed to parse transport parameter ID";
+ return false;
+ }
+ TransportParameters::TransportParameterId param_id =
+ static_cast<TransportParameters::TransportParameterId>(param_id64);
+ absl::string_view value;
+ if (!reader.ReadStringPieceVarInt62(&value)) {
+ *error_details =
+ "Failed to read length and value of transport parameter " +
+ TransportParameterIdToString(param_id);
+ return false;
+ }
+ QuicDataReader value_reader(value);
+ bool parse_success = true;
+ switch (param_id) {
+ case TransportParameters::kOriginalDestinationConnectionId: {
+ if (out->original_destination_connection_id.has_value()) {
+ *error_details =
+ "Received a second original_destination_connection_id";
+ return false;
+ }
+ const size_t connection_id_length = value_reader.BytesRemaining();
+ if (!QuicUtils::IsConnectionIdLengthValidForVersion(
+ connection_id_length, version.transport_version)) {
+ *error_details = absl::StrCat(
+ "Received original_destination_connection_id of invalid length ",
+ connection_id_length);
+ return false;
+ }
+ QuicConnectionId original_destination_connection_id;
+ if (!value_reader.ReadConnectionId(&original_destination_connection_id,
+ connection_id_length)) {
+ *error_details = "Failed to read original_destination_connection_id";
+ return false;
+ }
+ out->original_destination_connection_id =
+ original_destination_connection_id;
+ } break;
+ case TransportParameters::kMaxIdleTimeout:
+ parse_success =
+ out->max_idle_timeout_ms.Read(&value_reader, error_details);
+ break;
+ case TransportParameters::kStatelessResetToken: {
+ if (!out->stateless_reset_token.empty()) {
+ *error_details = "Received a second stateless_reset_token";
+ return false;
+ }
+ absl::string_view stateless_reset_token =
+ value_reader.ReadRemainingPayload();
+ if (stateless_reset_token.length() != kStatelessResetTokenLength) {
+ *error_details =
+ absl::StrCat("Received stateless_reset_token of invalid length ",
+ stateless_reset_token.length());
+ return false;
+ }
+ out->stateless_reset_token.assign(
+ stateless_reset_token.data(),
+ stateless_reset_token.data() + stateless_reset_token.length());
+ } break;
+ case TransportParameters::kMaxPacketSize:
+ parse_success =
+ out->max_udp_payload_size.Read(&value_reader, error_details);
+ break;
+ case TransportParameters::kInitialMaxData:
+ parse_success =
+ out->initial_max_data.Read(&value_reader, error_details);
+ break;
+ case TransportParameters::kInitialMaxStreamDataBidiLocal:
+ parse_success = out->initial_max_stream_data_bidi_local.Read(
+ &value_reader, error_details);
+ break;
+ case TransportParameters::kInitialMaxStreamDataBidiRemote:
+ parse_success = out->initial_max_stream_data_bidi_remote.Read(
+ &value_reader, error_details);
+ break;
+ case TransportParameters::kInitialMaxStreamDataUni:
+ parse_success =
+ out->initial_max_stream_data_uni.Read(&value_reader, error_details);
+ break;
+ case TransportParameters::kInitialMaxStreamsBidi:
+ parse_success =
+ out->initial_max_streams_bidi.Read(&value_reader, error_details);
+ break;
+ case TransportParameters::kInitialMaxStreamsUni:
+ parse_success =
+ out->initial_max_streams_uni.Read(&value_reader, error_details);
+ break;
+ case TransportParameters::kAckDelayExponent:
+ parse_success =
+ out->ack_delay_exponent.Read(&value_reader, error_details);
+ break;
+ case TransportParameters::kMaxAckDelay:
+ parse_success = out->max_ack_delay.Read(&value_reader, error_details);
+ break;
+ case TransportParameters::kDisableActiveMigration:
+ if (out->disable_active_migration) {
+ *error_details = "Received a second disable_active_migration";
+ return false;
+ }
+ out->disable_active_migration = true;
+ break;
+ case TransportParameters::kPreferredAddress: {
+ TransportParameters::PreferredAddress preferred_address;
+ uint16_t ipv4_port, ipv6_port;
+ in_addr ipv4_address;
+ in6_addr ipv6_address;
+ preferred_address.stateless_reset_token.resize(
+ kStatelessResetTokenLength);
+ if (!value_reader.ReadBytes(&ipv4_address, sizeof(ipv4_address)) ||
+ !value_reader.ReadUInt16(&ipv4_port) ||
+ !value_reader.ReadBytes(&ipv6_address, sizeof(ipv6_address)) ||
+ !value_reader.ReadUInt16(&ipv6_port) ||
+ !value_reader.ReadLengthPrefixedConnectionId(
+ &preferred_address.connection_id) ||
+ !value_reader.ReadBytes(&preferred_address.stateless_reset_token[0],
+ kStatelessResetTokenLength)) {
+ *error_details = "Failed to read preferred_address";
+ return false;
+ }
+ preferred_address.ipv4_socket_address =
+ QuicSocketAddress(QuicIpAddress(ipv4_address), ipv4_port);
+ preferred_address.ipv6_socket_address =
+ QuicSocketAddress(QuicIpAddress(ipv6_address), ipv6_port);
+ if (!preferred_address.ipv4_socket_address.host().IsIPv4() ||
+ !preferred_address.ipv6_socket_address.host().IsIPv6()) {
+ *error_details = "Received preferred_address of bad families " +
+ preferred_address.ToString();
+ return false;
+ }
+ if (!QuicUtils::IsConnectionIdValidForVersion(
+ preferred_address.connection_id, version.transport_version)) {
+ *error_details = "Received invalid preferred_address connection ID " +
+ preferred_address.ToString();
+ return false;
+ }
+ out->preferred_address =
+ std::make_unique<TransportParameters::PreferredAddress>(
+ preferred_address);
+ } break;
+ case TransportParameters::kActiveConnectionIdLimit:
+ parse_success =
+ out->active_connection_id_limit.Read(&value_reader, error_details);
+ break;
+ case TransportParameters::kInitialSourceConnectionId: {
+ if (out->initial_source_connection_id.has_value()) {
+ *error_details = "Received a second initial_source_connection_id";
+ return false;
+ }
+ const size_t connection_id_length = value_reader.BytesRemaining();
+ if (!QuicUtils::IsConnectionIdLengthValidForVersion(
+ connection_id_length, version.transport_version)) {
+ *error_details = absl::StrCat(
+ "Received initial_source_connection_id of invalid length ",
+ connection_id_length);
+ return false;
+ }
+ QuicConnectionId initial_source_connection_id;
+ if (!value_reader.ReadConnectionId(&initial_source_connection_id,
+ connection_id_length)) {
+ *error_details = "Failed to read initial_source_connection_id";
+ return false;
+ }
+ out->initial_source_connection_id = initial_source_connection_id;
+ } break;
+ case TransportParameters::kRetrySourceConnectionId: {
+ if (out->retry_source_connection_id.has_value()) {
+ *error_details = "Received a second retry_source_connection_id";
+ return false;
+ }
+ const size_t connection_id_length = value_reader.BytesRemaining();
+ if (!QuicUtils::IsConnectionIdLengthValidForVersion(
+ connection_id_length, version.transport_version)) {
+ *error_details = absl::StrCat(
+ "Received retry_source_connection_id of invalid length ",
+ connection_id_length);
+ return false;
+ }
+ QuicConnectionId retry_source_connection_id;
+ if (!value_reader.ReadConnectionId(&retry_source_connection_id,
+ connection_id_length)) {
+ *error_details = "Failed to read retry_source_connection_id";
+ return false;
+ }
+ out->retry_source_connection_id = retry_source_connection_id;
+ } break;
+ case TransportParameters::kMaxDatagramFrameSize:
+ parse_success =
+ out->max_datagram_frame_size.Read(&value_reader, error_details);
+ break;
+ case TransportParameters::kInitialRoundTripTime:
+ parse_success =
+ out->initial_round_trip_time_us.Read(&value_reader, error_details);
+ break;
+ case TransportParameters::kGoogleConnectionOptions: {
+ if (out->google_connection_options.has_value()) {
+ *error_details = "Received a second google_connection_options";
+ return false;
+ }
+ out->google_connection_options = QuicTagVector{};
+ while (!value_reader.IsDoneReading()) {
+ QuicTag connection_option;
+ if (!value_reader.ReadTag(&connection_option)) {
+ *error_details = "Failed to read a google_connection_options";
+ return false;
+ }
+ out->google_connection_options.value().push_back(connection_option);
+ }
+ } break;
+ case TransportParameters::kGoogleQuicVersion: {
+ if (!out->legacy_version_information.has_value()) {
+ out->legacy_version_information =
+ TransportParameters::LegacyVersionInformation();
+ }
+ if (!value_reader.ReadUInt32(
+ &out->legacy_version_information.value().version)) {
+ *error_details = "Failed to read Google version extension version";
+ return false;
+ }
+ if (perspective == Perspective::IS_SERVER) {
+ uint8_t versions_length;
+ if (!value_reader.ReadUInt8(&versions_length)) {
+ *error_details = "Failed to parse Google supported versions length";
+ return false;
+ }
+ const uint8_t num_versions = versions_length / sizeof(uint32_t);
+ for (uint8_t i = 0; i < num_versions; ++i) {
+ QuicVersionLabel version;
+ if (!value_reader.ReadUInt32(&version)) {
+ *error_details = "Failed to parse Google supported version";
+ return false;
+ }
+ out->legacy_version_information.value()
+ .supported_versions.push_back(version);
+ }
+ }
+ } break;
+ case TransportParameters::kVersionInformation: {
+ if (out->version_information.has_value()) {
+ *error_details = "Received a second version_information";
+ return false;
+ }
+ out->version_information = TransportParameters::VersionInformation();
+ if (!value_reader.ReadUInt32(
+ &out->version_information.value().chosen_version)) {
+ *error_details = "Failed to read chosen version";
+ return false;
+ }
+ while (!value_reader.IsDoneReading()) {
+ QuicVersionLabel other_version;
+ if (!value_reader.ReadUInt32(&other_version)) {
+ *error_details = "Failed to parse other version";
+ return false;
+ }
+ out->version_information.value().other_versions.push_back(
+ other_version);
+ }
+ } break;
+ case TransportParameters::kMinAckDelay:
+ parse_success =
+ out->min_ack_delay_us.Read(&value_reader, error_details);
+ break;
+ default:
+ if (out->custom_parameters.find(param_id) !=
+ out->custom_parameters.end()) {
+ *error_details = "Received a second unknown parameter" +
+ TransportParameterIdToString(param_id);
+ return false;
+ }
+ out->custom_parameters[param_id] =
+ std::string(value_reader.ReadRemainingPayload());
+ break;
+ }
+ if (!parse_success) {
+ QUICHE_DCHECK(!error_details->empty());
+ return false;
+ }
+ if (!value_reader.IsDoneReading()) {
+ *error_details = absl::StrCat(
+ "Received unexpected ", value_reader.BytesRemaining(),
+ " bytes after parsing ", TransportParameterIdToString(param_id));
+ return false;
+ }
+ }
+
+ if (!out->AreValid(error_details)) {
+ QUICHE_DCHECK(!error_details->empty());
+ return false;
+ }
+
+ QUIC_DLOG(INFO) << "Parsed transport parameters " << *out << " from "
+ << in_len << " bytes";
+
+ return true;
+}
+
+namespace {
+
+bool DigestUpdateIntegerParam(
+ EVP_MD_CTX* hash_ctx, const TransportParameters::IntegerParameter& param) {
+ uint64_t value = param.value();
+ return EVP_DigestUpdate(hash_ctx, &value, sizeof(value));
+}
+
+} // namespace
+
+bool SerializeTransportParametersForTicket(
+ const TransportParameters& in, const std::vector<uint8_t>& application_data,
+ std::vector<uint8_t>* out) {
+ std::string error_details;
+ if (!in.AreValid(&error_details)) {
+ QUIC_BUG(quic_bug_10743_26)
+ << "Not serializing invalid transport parameters: " << error_details;
+ return false;
+ }
+
+ out->resize(SHA256_DIGEST_LENGTH + 1);
+ const uint8_t serialization_version = 0;
+ (*out)[0] = serialization_version;
+
+ bssl::ScopedEVP_MD_CTX hash_ctx;
+ // Write application data:
+ uint64_t app_data_len = application_data.size();
+ const uint64_t parameter_version = 0;
+ // The format of the input to the hash function is as follows:
+ // - The application data, prefixed with a 64-bit length field.
+ // - Transport parameters:
+ // - A 64-bit version field indicating which version of encoding is used
+ // for transport parameters.
+ // - A list of 64-bit integers representing the relevant parameters.
+ //
+ // When changing which parameters are included, additional parameters can be
+ // added to the end of the list without changing the version field. New
+ // parameters that are variable length must be length prefixed. If
+ // parameters are removed from the list, the version field must be
+ // incremented.
+ //
+ // Integers happen to be written in host byte order, not network byte order.
+ if (!EVP_DigestInit(hash_ctx.get(), EVP_sha256()) ||
+ !EVP_DigestUpdate(hash_ctx.get(), &app_data_len, sizeof(app_data_len)) ||
+ !EVP_DigestUpdate(hash_ctx.get(), application_data.data(),
+ application_data.size()) ||
+ !EVP_DigestUpdate(hash_ctx.get(), &parameter_version,
+ sizeof(parameter_version))) {
+ QUIC_BUG(quic_bug_10743_27)
+ << "Unexpected failure of EVP_Digest functions when hashing "
+ "Transport Parameters for ticket";
+ return false;
+ }
+
+ // Write transport parameters specified by draft-ietf-quic-transport-28,
+ // section 7.4.1, that are remembered for 0-RTT.
+ if (!DigestUpdateIntegerParam(hash_ctx.get(), in.initial_max_data) ||
+ !DigestUpdateIntegerParam(hash_ctx.get(),
+ in.initial_max_stream_data_bidi_local) ||
+ !DigestUpdateIntegerParam(hash_ctx.get(),
+ in.initial_max_stream_data_bidi_remote) ||
+ !DigestUpdateIntegerParam(hash_ctx.get(),
+ in.initial_max_stream_data_uni) ||
+ !DigestUpdateIntegerParam(hash_ctx.get(), in.initial_max_streams_bidi) ||
+ !DigestUpdateIntegerParam(hash_ctx.get(), in.initial_max_streams_uni) ||
+ !DigestUpdateIntegerParam(hash_ctx.get(),
+ in.active_connection_id_limit)) {
+ QUIC_BUG(quic_bug_10743_28)
+ << "Unexpected failure of EVP_Digest functions when hashing "
+ "Transport Parameters for ticket";
+ return false;
+ }
+ uint8_t disable_active_migration = in.disable_active_migration ? 1 : 0;
+ if (!EVP_DigestUpdate(hash_ctx.get(), &disable_active_migration,
+ sizeof(disable_active_migration)) ||
+ !EVP_DigestFinal(hash_ctx.get(), out->data() + 1, nullptr)) {
+ QUIC_BUG(quic_bug_10743_29)
+ << "Unexpected failure of EVP_Digest functions when hashing "
+ "Transport Parameters for ticket";
+ return false;
+ }
+ return true;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/transport_parameters.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/transport_parameters.h
new file mode 100644
index 00000000000..5be5ab4f89d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/transport_parameters.h
@@ -0,0 +1,304 @@
+// Copyright (c) 2018 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_TRANSPORT_PARAMETERS_H_
+#define QUICHE_QUIC_CORE_CRYPTO_TRANSPORT_PARAMETERS_H_
+
+#include <memory>
+#include <vector>
+
+#include "absl/container/flat_hash_map.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
+#include "quiche/quic/core/quic_connection_id.h"
+#include "quiche/quic/core/quic_data_reader.h"
+#include "quiche/quic/core/quic_data_writer.h"
+#include "quiche/quic/core/quic_tag.h"
+#include "quiche/quic/core/quic_types.h"
+#include "quiche/quic/core/quic_versions.h"
+#include "quiche/quic/platform/api/quic_containers.h"
+#include "quiche/quic/platform/api/quic_socket_address.h"
+
+namespace quic {
+
+// TransportParameters contains parameters for QUIC's transport layer that are
+// exchanged during the TLS handshake. This struct is a mirror of the struct in
+// the "Transport Parameter Encoding" section of draft-ietf-quic-transport.
+// This struct currently uses the values from draft 29.
+struct QUIC_EXPORT_PRIVATE TransportParameters {
+ // The identifier used to differentiate transport parameters.
+ enum TransportParameterId : uint64_t;
+ // A map used to specify custom parameters.
+ using ParameterMap = absl::flat_hash_map<TransportParameterId, std::string>;
+ // Represents an individual QUIC transport parameter that only encodes a
+ // variable length integer. Can only be created inside the constructor for
+ // TransportParameters.
+ class QUIC_EXPORT_PRIVATE IntegerParameter {
+ public:
+ // Forbid constructing and copying apart from TransportParameters.
+ IntegerParameter() = delete;
+ IntegerParameter& operator=(const IntegerParameter&) = delete;
+ // Sets the value of this transport parameter.
+ void set_value(uint64_t value);
+ // Gets the value of this transport parameter.
+ uint64_t value() const;
+ // Validates whether the current value is valid.
+ bool IsValid() const;
+ // Writes to a crypto byte buffer, used during serialization. Does not write
+ // anything if the value is equal to the parameter's default value.
+ // Returns whether the write was successful.
+ bool Write(QuicDataWriter* writer) const;
+ // Reads from a crypto byte string, used during parsing.
+ // Returns whether the read was successful.
+ // On failure, this method will write a human-readable error message to
+ // |error_details|.
+ bool Read(QuicDataReader* reader, std::string* error_details);
+ // operator<< allows easily logging integer transport parameters.
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os, const IntegerParameter& param);
+
+ private:
+ friend struct TransportParameters;
+ // Constructors for initial setup used by TransportParameters only.
+ // This constructor sets |default_value| and |min_value| to 0, and
+ // |max_value| to kVarInt62MaxValue.
+ explicit IntegerParameter(TransportParameterId param_id);
+ IntegerParameter(TransportParameterId param_id, uint64_t default_value,
+ uint64_t min_value, uint64_t max_value);
+ IntegerParameter(const IntegerParameter& other) = default;
+ IntegerParameter(IntegerParameter&& other) = default;
+ // Human-readable string representation.
+ std::string ToString(bool for_use_in_list) const;
+
+ // Number used to indicate this transport parameter.
+ TransportParameterId param_id_;
+ // Current value of the transport parameter.
+ uint64_t value_;
+ // Default value of this transport parameter, as per IETF specification.
+ const uint64_t default_value_;
+ // Minimum value of this transport parameter, as per IETF specification.
+ const uint64_t min_value_;
+ // Maximum value of this transport parameter, as per IETF specification.
+ const uint64_t max_value_;
+ // Ensures this parameter is not parsed twice in the same message.
+ bool has_been_read_;
+ };
+
+ // Represents the preferred_address transport parameter that a server can
+ // send to clients.
+ struct QUIC_EXPORT_PRIVATE PreferredAddress {
+ PreferredAddress();
+ PreferredAddress(const PreferredAddress& other) = default;
+ PreferredAddress(PreferredAddress&& other) = default;
+ ~PreferredAddress();
+ bool operator==(const PreferredAddress& rhs) const;
+ bool operator!=(const PreferredAddress& rhs) const;
+
+ QuicSocketAddress ipv4_socket_address;
+ QuicSocketAddress ipv6_socket_address;
+ QuicConnectionId connection_id;
+ std::vector<uint8_t> stateless_reset_token;
+
+ // Allows easily logging.
+ std::string ToString() const;
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os, const TransportParameters& params);
+ };
+
+ // LegacyVersionInformation represents the Google QUIC downgrade prevention
+ // mechanism ported to QUIC+TLS. It is exchanged using transport parameter ID
+ // 0x4752 and will eventually be deprecated in favor of
+ // draft-ietf-quic-version-negotiation.
+ struct QUIC_EXPORT_PRIVATE LegacyVersionInformation {
+ LegacyVersionInformation();
+ LegacyVersionInformation(const LegacyVersionInformation& other) = default;
+ LegacyVersionInformation& operator=(const LegacyVersionInformation& other) =
+ default;
+ LegacyVersionInformation& operator=(LegacyVersionInformation&& other) =
+ default;
+ LegacyVersionInformation(LegacyVersionInformation&& other) = default;
+ ~LegacyVersionInformation() = default;
+ bool operator==(const LegacyVersionInformation& rhs) const;
+ bool operator!=(const LegacyVersionInformation& rhs) const;
+ // When sent by the client, |version| is the initial version offered by the
+ // client (before any version negotiation packets) for this connection. When
+ // sent by the server, |version| is the version that is in use.
+ QuicVersionLabel version;
+
+ // When sent by the server, |supported_versions| contains a list of all
+ // versions that the server would send in a version negotiation packet. When
+ // sent by the client, this is empty.
+ QuicVersionLabelVector supported_versions;
+
+ // Allows easily logging.
+ std::string ToString() const;
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const LegacyVersionInformation& legacy_version_information);
+ };
+
+ // Version information used for version downgrade prevention and compatible
+ // version negotiation. See draft-ietf-quic-version-negotiation-05.
+ struct QUIC_EXPORT_PRIVATE VersionInformation {
+ VersionInformation();
+ VersionInformation(const VersionInformation& other) = default;
+ VersionInformation& operator=(const VersionInformation& other) = default;
+ VersionInformation& operator=(VersionInformation&& other) = default;
+ VersionInformation(VersionInformation&& other) = default;
+ ~VersionInformation() = default;
+ bool operator==(const VersionInformation& rhs) const;
+ bool operator!=(const VersionInformation& rhs) const;
+
+ // Version that the sender has chosen to use on this connection.
+ QuicVersionLabel chosen_version;
+
+ // When sent by the client, |other_versions| contains all the versions that
+ // this first flight is compatible with. When sent by the server,
+ // |other_versions| contains all of the versions supported by the server.
+ QuicVersionLabelVector other_versions;
+
+ // Allows easily logging.
+ std::string ToString() const;
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os, const VersionInformation& version_information);
+ };
+
+ TransportParameters();
+ TransportParameters(const TransportParameters& other);
+ ~TransportParameters();
+ bool operator==(const TransportParameters& rhs) const;
+ bool operator!=(const TransportParameters& rhs) const;
+
+ // Represents the sender of the transport parameters. When |perspective| is
+ // Perspective::IS_CLIENT, this struct is being used in the client_hello
+ // handshake message; when it is Perspective::IS_SERVER, it is being used in
+ // the encrypted_extensions handshake message.
+ Perspective perspective;
+
+ // Google QUIC downgrade prevention mechanism sent over QUIC+TLS.
+ absl::optional<LegacyVersionInformation> legacy_version_information;
+
+ // IETF downgrade prevention and compatible version negotiation, see
+ // draft-ietf-quic-version-negotiation.
+ absl::optional<VersionInformation> version_information;
+
+ // The value of the Destination Connection ID field from the first
+ // Initial packet sent by the client.
+ absl::optional<QuicConnectionId> original_destination_connection_id;
+
+ // Maximum idle timeout expressed in milliseconds.
+ IntegerParameter max_idle_timeout_ms;
+
+ // Stateless reset token used in verifying stateless resets.
+ std::vector<uint8_t> stateless_reset_token;
+
+ // Limits the size of packets that the endpoint is willing to receive.
+ // This indicates that packets larger than this limit will be dropped.
+ IntegerParameter max_udp_payload_size;
+
+ // Contains the initial value for the maximum amount of data that can
+ // be sent on the connection.
+ IntegerParameter initial_max_data;
+
+ // Initial flow control limit for locally-initiated bidirectional streams.
+ IntegerParameter initial_max_stream_data_bidi_local;
+
+ // Initial flow control limit for peer-initiated bidirectional streams.
+ IntegerParameter initial_max_stream_data_bidi_remote;
+
+ // Initial flow control limit for unidirectional streams.
+ IntegerParameter initial_max_stream_data_uni;
+
+ // Initial maximum number of bidirectional streams the peer may initiate.
+ IntegerParameter initial_max_streams_bidi;
+
+ // Initial maximum number of unidirectional streams the peer may initiate.
+ IntegerParameter initial_max_streams_uni;
+
+ // Exponent used to decode the ACK Delay field in ACK frames.
+ IntegerParameter ack_delay_exponent;
+
+ // Maximum amount of time in milliseconds by which the endpoint will
+ // delay sending acknowledgments.
+ IntegerParameter max_ack_delay;
+
+ // Minimum amount of time in microseconds by which the endpoint will
+ // delay sending acknowledgments. Used to enable sender control of ack delay.
+ IntegerParameter min_ack_delay_us;
+
+ // Indicates lack of support for connection migration.
+ bool disable_active_migration;
+
+ // Used to effect a change in server address at the end of the handshake.
+ std::unique_ptr<PreferredAddress> preferred_address;
+
+ // Maximum number of connection IDs from the peer that an endpoint is willing
+ // to store.
+ IntegerParameter active_connection_id_limit;
+
+ // The value that the endpoint included in the Source Connection ID field of
+ // the first Initial packet it sent.
+ absl::optional<QuicConnectionId> initial_source_connection_id;
+
+ // The value that the server included in the Source Connection ID field of a
+ // Retry packet it sent.
+ absl::optional<QuicConnectionId> retry_source_connection_id;
+
+ // Indicates support for the DATAGRAM frame and the maximum frame size that
+ // the sender accepts. See draft-ietf-quic-datagram.
+ IntegerParameter max_datagram_frame_size;
+
+ // Google-specific transport parameter that carries an estimate of the
+ // initial round-trip time in microseconds.
+ IntegerParameter initial_round_trip_time_us;
+
+ // Google-specific connection options.
+ absl::optional<QuicTagVector> google_connection_options;
+
+ // Validates whether transport parameters are valid according to
+ // the specification. If the transport parameters are not valid, this method
+ // will write a human-readable error message to |error_details|.
+ bool AreValid(std::string* error_details) const;
+
+ // Custom parameters that may be specific to application protocol.
+ ParameterMap custom_parameters;
+
+ // Allows easily logging transport parameters.
+ std::string ToString() const;
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os, const TransportParameters& params);
+};
+
+// Serializes a TransportParameters struct into the format for sending it in a
+// TLS extension. The serialized bytes are written to |*out|. Returns if the
+// parameters are valid and serialization succeeded.
+QUIC_EXPORT_PRIVATE bool SerializeTransportParameters(
+ ParsedQuicVersion version, const TransportParameters& in,
+ std::vector<uint8_t>* out);
+
+// Parses bytes from the quic_transport_parameters TLS extension and writes the
+// parsed parameters into |*out|. Input is read from |in| for |in_len| bytes.
+// |perspective| indicates whether the input came from a client or a server.
+// This method returns true if the input was successfully parsed.
+// On failure, this method will write a human-readable error message to
+// |error_details|.
+QUIC_EXPORT_PRIVATE bool ParseTransportParameters(
+ ParsedQuicVersion version, Perspective perspective, const uint8_t* in,
+ size_t in_len, TransportParameters* out, std::string* error_details);
+
+// Serializes |in| and |application_data| in a deterministic format so that
+// multiple calls to SerializeTransportParametersForTicket with the same inputs
+// will generate the same output, and if the inputs differ, then the output will
+// differ. The output of this function is used by the server in
+// SSL_set_quic_early_data_context to determine whether early data should be
+// accepted: Early data will only be accepted if the inputs to this function
+// match what they were on the connection that issued an early data capable
+// ticket.
+QUIC_EXPORT_PRIVATE bool SerializeTransportParametersForTicket(
+ const TransportParameters& in, const std::vector<uint8_t>& application_data,
+ std::vector<uint8_t>* out);
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_TRANSPORT_PARAMETERS_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/transport_parameters_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/transport_parameters_test.cc
new file mode 100644
index 00000000000..fe9a2ceae9a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/transport_parameters_test.cc
@@ -0,0 +1,1130 @@
+// Copyright (c) 2018 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 "quiche/quic/core/crypto/transport_parameters.h"
+
+#include <cstring>
+#include <utility>
+
+#include "absl/base/macros.h"
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/crypto/crypto_protocol.h"
+#include "quiche/quic/core/quic_connection_id.h"
+#include "quiche/quic/core/quic_tag.h"
+#include "quiche/quic/core/quic_types.h"
+#include "quiche/quic/core/quic_versions.h"
+#include "quiche/quic/platform/api/quic_expect_bug.h"
+#include "quiche/quic/platform/api/quic_ip_address.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+const QuicVersionLabel kFakeVersionLabel = 0x01234567;
+const QuicVersionLabel kFakeVersionLabel2 = 0x89ABCDEF;
+const uint64_t kFakeIdleTimeoutMilliseconds = 12012;
+const uint64_t kFakeInitialMaxData = 101;
+const uint64_t kFakeInitialMaxStreamDataBidiLocal = 2001;
+const uint64_t kFakeInitialMaxStreamDataBidiRemote = 2002;
+const uint64_t kFakeInitialMaxStreamDataUni = 3000;
+const uint64_t kFakeInitialMaxStreamsBidi = 21;
+const uint64_t kFakeInitialMaxStreamsUni = 22;
+const bool kFakeDisableMigration = true;
+const uint64_t kFakeInitialRoundTripTime = 53;
+const uint8_t kFakePreferredStatelessResetTokenData[16] = {
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F};
+
+const auto kCustomParameter1 =
+ static_cast<TransportParameters::TransportParameterId>(0xffcd);
+const char* kCustomParameter1Value = "foo";
+const auto kCustomParameter2 =
+ static_cast<TransportParameters::TransportParameterId>(0xff34);
+const char* kCustomParameter2Value = "bar";
+
+QuicConnectionId CreateFakeOriginalDestinationConnectionId() {
+ return TestConnectionId(0x1337);
+}
+
+QuicConnectionId CreateFakeInitialSourceConnectionId() {
+ return TestConnectionId(0x2345);
+}
+
+QuicConnectionId CreateFakeRetrySourceConnectionId() {
+ return TestConnectionId(0x9876);
+}
+
+QuicConnectionId CreateFakePreferredConnectionId() {
+ return TestConnectionId(0xBEEF);
+}
+
+std::vector<uint8_t> CreateFakePreferredStatelessResetToken() {
+ return std::vector<uint8_t>(
+ kFakePreferredStatelessResetTokenData,
+ kFakePreferredStatelessResetTokenData +
+ sizeof(kFakePreferredStatelessResetTokenData));
+}
+
+QuicSocketAddress CreateFakeV4SocketAddress() {
+ QuicIpAddress ipv4_address;
+ if (!ipv4_address.FromString("65.66.67.68")) { // 0x41, 0x42, 0x43, 0x44
+ QUIC_LOG(FATAL) << "Failed to create IPv4 address";
+ return QuicSocketAddress();
+ }
+ return QuicSocketAddress(ipv4_address, 0x4884);
+}
+
+QuicSocketAddress CreateFakeV6SocketAddress() {
+ QuicIpAddress ipv6_address;
+ if (!ipv6_address.FromString("6061:6263:6465:6667:6869:6A6B:6C6D:6E6F")) {
+ QUIC_LOG(FATAL) << "Failed to create IPv6 address";
+ return QuicSocketAddress();
+ }
+ return QuicSocketAddress(ipv6_address, 0x6336);
+}
+
+std::unique_ptr<TransportParameters::PreferredAddress>
+CreateFakePreferredAddress() {
+ TransportParameters::PreferredAddress preferred_address;
+ preferred_address.ipv4_socket_address = CreateFakeV4SocketAddress();
+ preferred_address.ipv6_socket_address = CreateFakeV6SocketAddress();
+ preferred_address.connection_id = CreateFakePreferredConnectionId();
+ preferred_address.stateless_reset_token =
+ CreateFakePreferredStatelessResetToken();
+ return std::make_unique<TransportParameters::PreferredAddress>(
+ preferred_address);
+}
+
+TransportParameters::LegacyVersionInformation
+CreateFakeLegacyVersionInformationClient() {
+ TransportParameters::LegacyVersionInformation legacy_version_information;
+ legacy_version_information.version = kFakeVersionLabel;
+ return legacy_version_information;
+}
+
+TransportParameters::LegacyVersionInformation
+CreateFakeLegacyVersionInformationServer() {
+ TransportParameters::LegacyVersionInformation legacy_version_information =
+ CreateFakeLegacyVersionInformationClient();
+ legacy_version_information.supported_versions.push_back(kFakeVersionLabel);
+ legacy_version_information.supported_versions.push_back(kFakeVersionLabel2);
+ return legacy_version_information;
+}
+
+TransportParameters::VersionInformation CreateFakeVersionInformation() {
+ TransportParameters::VersionInformation version_information;
+ version_information.chosen_version = kFakeVersionLabel;
+ version_information.other_versions.push_back(kFakeVersionLabel);
+ version_information.other_versions.push_back(kFakeVersionLabel2);
+ return version_information;
+}
+
+QuicTagVector CreateFakeGoogleConnectionOptions() {
+ return {kALPN, MakeQuicTag('E', 'F', 'G', 0x00),
+ MakeQuicTag('H', 'I', 'J', 0xff)};
+}
+
+void RemoveGreaseParameters(TransportParameters* params) {
+ std::vector<TransportParameters::TransportParameterId> grease_params;
+ for (const auto& kv : params->custom_parameters) {
+ if (kv.first % 31 == 27) {
+ grease_params.push_back(kv.first);
+ }
+ }
+ EXPECT_EQ(grease_params.size(), 1u);
+ for (TransportParameters::TransportParameterId param_id : grease_params) {
+ params->custom_parameters.erase(param_id);
+ }
+ // Remove all GREASE versions from version_information.other_versions.
+ if (params->version_information.has_value()) {
+ QuicVersionLabelVector& other_versions =
+ params->version_information.value().other_versions;
+ for (auto it = other_versions.begin(); it != other_versions.end();) {
+ if ((*it & 0x0f0f0f0f) == 0x0a0a0a0a) {
+ it = other_versions.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+}
+
+} // namespace
+
+class TransportParametersTest : public QuicTestWithParam<ParsedQuicVersion> {
+ protected:
+ TransportParametersTest() : version_(GetParam()) {}
+
+ ParsedQuicVersion version_;
+};
+
+INSTANTIATE_TEST_SUITE_P(TransportParametersTests, TransportParametersTest,
+ ::testing::ValuesIn(AllSupportedVersionsWithTls()),
+ ::testing::PrintToStringParamName());
+
+TEST_P(TransportParametersTest, Comparator) {
+ TransportParameters orig_params;
+ TransportParameters new_params;
+ // Test comparison on primitive members.
+ orig_params.perspective = Perspective::IS_CLIENT;
+ new_params.perspective = Perspective::IS_SERVER;
+ EXPECT_NE(orig_params, new_params);
+ EXPECT_FALSE(orig_params == new_params);
+ EXPECT_TRUE(orig_params != new_params);
+ new_params.perspective = Perspective::IS_CLIENT;
+ orig_params.legacy_version_information =
+ CreateFakeLegacyVersionInformationClient();
+ new_params.legacy_version_information =
+ CreateFakeLegacyVersionInformationClient();
+ orig_params.version_information = CreateFakeVersionInformation();
+ new_params.version_information = CreateFakeVersionInformation();
+ orig_params.disable_active_migration = true;
+ new_params.disable_active_migration = true;
+ EXPECT_EQ(orig_params, new_params);
+ EXPECT_TRUE(orig_params == new_params);
+ EXPECT_FALSE(orig_params != new_params);
+
+ // Test comparison on vectors.
+ orig_params.legacy_version_information.value().supported_versions.push_back(
+ kFakeVersionLabel);
+ new_params.legacy_version_information.value().supported_versions.push_back(
+ kFakeVersionLabel2);
+ EXPECT_NE(orig_params, new_params);
+ EXPECT_FALSE(orig_params == new_params);
+ EXPECT_TRUE(orig_params != new_params);
+ new_params.legacy_version_information.value().supported_versions.pop_back();
+ new_params.legacy_version_information.value().supported_versions.push_back(
+ kFakeVersionLabel);
+ orig_params.stateless_reset_token = CreateStatelessResetTokenForTest();
+ new_params.stateless_reset_token = CreateStatelessResetTokenForTest();
+ EXPECT_EQ(orig_params, new_params);
+ EXPECT_TRUE(orig_params == new_params);
+ EXPECT_FALSE(orig_params != new_params);
+
+ // Test comparison on IntegerParameters.
+ orig_params.max_udp_payload_size.set_value(kMaxPacketSizeForTest);
+ new_params.max_udp_payload_size.set_value(kMaxPacketSizeForTest + 1);
+ EXPECT_NE(orig_params, new_params);
+ EXPECT_FALSE(orig_params == new_params);
+ EXPECT_TRUE(orig_params != new_params);
+ new_params.max_udp_payload_size.set_value(kMaxPacketSizeForTest);
+ EXPECT_EQ(orig_params, new_params);
+ EXPECT_TRUE(orig_params == new_params);
+ EXPECT_FALSE(orig_params != new_params);
+
+ // Test comparison on PreferredAddress
+ orig_params.preferred_address = CreateFakePreferredAddress();
+ EXPECT_NE(orig_params, new_params);
+ EXPECT_FALSE(orig_params == new_params);
+ EXPECT_TRUE(orig_params != new_params);
+ new_params.preferred_address = CreateFakePreferredAddress();
+ EXPECT_EQ(orig_params, new_params);
+ EXPECT_TRUE(orig_params == new_params);
+ EXPECT_FALSE(orig_params != new_params);
+
+ // Test comparison on CustomMap
+ orig_params.custom_parameters[kCustomParameter1] = kCustomParameter1Value;
+ orig_params.custom_parameters[kCustomParameter2] = kCustomParameter2Value;
+
+ new_params.custom_parameters[kCustomParameter2] = kCustomParameter2Value;
+ new_params.custom_parameters[kCustomParameter1] = kCustomParameter1Value;
+ EXPECT_EQ(orig_params, new_params);
+ EXPECT_TRUE(orig_params == new_params);
+ EXPECT_FALSE(orig_params != new_params);
+
+ // Test comparison on connection IDs.
+ orig_params.initial_source_connection_id =
+ CreateFakeInitialSourceConnectionId();
+ new_params.initial_source_connection_id = absl::nullopt;
+ EXPECT_NE(orig_params, new_params);
+ EXPECT_FALSE(orig_params == new_params);
+ EXPECT_TRUE(orig_params != new_params);
+ new_params.initial_source_connection_id = TestConnectionId(0xbadbad);
+ EXPECT_NE(orig_params, new_params);
+ EXPECT_FALSE(orig_params == new_params);
+ EXPECT_TRUE(orig_params != new_params);
+ new_params.initial_source_connection_id =
+ CreateFakeInitialSourceConnectionId();
+ EXPECT_EQ(orig_params, new_params);
+ EXPECT_TRUE(orig_params == new_params);
+ EXPECT_FALSE(orig_params != new_params);
+}
+
+TEST_P(TransportParametersTest, CopyConstructor) {
+ TransportParameters orig_params;
+ orig_params.perspective = Perspective::IS_CLIENT;
+ orig_params.legacy_version_information =
+ CreateFakeLegacyVersionInformationClient();
+ orig_params.version_information = CreateFakeVersionInformation();
+ orig_params.original_destination_connection_id =
+ CreateFakeOriginalDestinationConnectionId();
+ orig_params.max_idle_timeout_ms.set_value(kFakeIdleTimeoutMilliseconds);
+ orig_params.stateless_reset_token = CreateStatelessResetTokenForTest();
+ orig_params.max_udp_payload_size.set_value(kMaxPacketSizeForTest);
+ orig_params.initial_max_data.set_value(kFakeInitialMaxData);
+ orig_params.initial_max_stream_data_bidi_local.set_value(
+ kFakeInitialMaxStreamDataBidiLocal);
+ orig_params.initial_max_stream_data_bidi_remote.set_value(
+ kFakeInitialMaxStreamDataBidiRemote);
+ orig_params.initial_max_stream_data_uni.set_value(
+ kFakeInitialMaxStreamDataUni);
+ orig_params.initial_max_streams_bidi.set_value(kFakeInitialMaxStreamsBidi);
+ orig_params.initial_max_streams_uni.set_value(kFakeInitialMaxStreamsUni);
+ orig_params.ack_delay_exponent.set_value(kAckDelayExponentForTest);
+ orig_params.max_ack_delay.set_value(kMaxAckDelayForTest);
+ orig_params.min_ack_delay_us.set_value(kMinAckDelayUsForTest);
+ orig_params.disable_active_migration = kFakeDisableMigration;
+ orig_params.preferred_address = CreateFakePreferredAddress();
+ orig_params.active_connection_id_limit.set_value(
+ kActiveConnectionIdLimitForTest);
+ orig_params.initial_source_connection_id =
+ CreateFakeInitialSourceConnectionId();
+ orig_params.retry_source_connection_id = CreateFakeRetrySourceConnectionId();
+ orig_params.initial_round_trip_time_us.set_value(kFakeInitialRoundTripTime);
+ orig_params.google_connection_options = CreateFakeGoogleConnectionOptions();
+ orig_params.custom_parameters[kCustomParameter1] = kCustomParameter1Value;
+ orig_params.custom_parameters[kCustomParameter2] = kCustomParameter2Value;
+
+ TransportParameters new_params(orig_params);
+ EXPECT_EQ(new_params, orig_params);
+}
+
+TEST_P(TransportParametersTest, RoundTripClient) {
+ TransportParameters orig_params;
+ orig_params.perspective = Perspective::IS_CLIENT;
+ orig_params.legacy_version_information =
+ CreateFakeLegacyVersionInformationClient();
+ orig_params.version_information = CreateFakeVersionInformation();
+ orig_params.max_idle_timeout_ms.set_value(kFakeIdleTimeoutMilliseconds);
+ orig_params.max_udp_payload_size.set_value(kMaxPacketSizeForTest);
+ orig_params.initial_max_data.set_value(kFakeInitialMaxData);
+ orig_params.initial_max_stream_data_bidi_local.set_value(
+ kFakeInitialMaxStreamDataBidiLocal);
+ orig_params.initial_max_stream_data_bidi_remote.set_value(
+ kFakeInitialMaxStreamDataBidiRemote);
+ orig_params.initial_max_stream_data_uni.set_value(
+ kFakeInitialMaxStreamDataUni);
+ orig_params.initial_max_streams_bidi.set_value(kFakeInitialMaxStreamsBidi);
+ orig_params.initial_max_streams_uni.set_value(kFakeInitialMaxStreamsUni);
+ orig_params.ack_delay_exponent.set_value(kAckDelayExponentForTest);
+ orig_params.max_ack_delay.set_value(kMaxAckDelayForTest);
+ orig_params.min_ack_delay_us.set_value(kMinAckDelayUsForTest);
+ orig_params.disable_active_migration = kFakeDisableMigration;
+ orig_params.active_connection_id_limit.set_value(
+ kActiveConnectionIdLimitForTest);
+ orig_params.initial_source_connection_id =
+ CreateFakeInitialSourceConnectionId();
+ orig_params.initial_round_trip_time_us.set_value(kFakeInitialRoundTripTime);
+ orig_params.google_connection_options = CreateFakeGoogleConnectionOptions();
+ orig_params.custom_parameters[kCustomParameter1] = kCustomParameter1Value;
+ orig_params.custom_parameters[kCustomParameter2] = kCustomParameter2Value;
+
+ std::vector<uint8_t> serialized;
+ ASSERT_TRUE(SerializeTransportParameters(version_, orig_params, &serialized));
+
+ TransportParameters new_params;
+ std::string error_details;
+ ASSERT_TRUE(ParseTransportParameters(version_, Perspective::IS_CLIENT,
+ serialized.data(), serialized.size(),
+ &new_params, &error_details))
+ << error_details;
+ EXPECT_TRUE(error_details.empty());
+ RemoveGreaseParameters(&new_params);
+ EXPECT_EQ(new_params, orig_params);
+}
+
+TEST_P(TransportParametersTest, RoundTripServer) {
+ TransportParameters orig_params;
+ orig_params.perspective = Perspective::IS_SERVER;
+ orig_params.legacy_version_information =
+ CreateFakeLegacyVersionInformationServer();
+ orig_params.version_information = CreateFakeVersionInformation();
+ orig_params.original_destination_connection_id =
+ CreateFakeOriginalDestinationConnectionId();
+ orig_params.max_idle_timeout_ms.set_value(kFakeIdleTimeoutMilliseconds);
+ orig_params.stateless_reset_token = CreateStatelessResetTokenForTest();
+ orig_params.max_udp_payload_size.set_value(kMaxPacketSizeForTest);
+ orig_params.initial_max_data.set_value(kFakeInitialMaxData);
+ orig_params.initial_max_stream_data_bidi_local.set_value(
+ kFakeInitialMaxStreamDataBidiLocal);
+ orig_params.initial_max_stream_data_bidi_remote.set_value(
+ kFakeInitialMaxStreamDataBidiRemote);
+ orig_params.initial_max_stream_data_uni.set_value(
+ kFakeInitialMaxStreamDataUni);
+ orig_params.initial_max_streams_bidi.set_value(kFakeInitialMaxStreamsBidi);
+ orig_params.initial_max_streams_uni.set_value(kFakeInitialMaxStreamsUni);
+ orig_params.ack_delay_exponent.set_value(kAckDelayExponentForTest);
+ orig_params.max_ack_delay.set_value(kMaxAckDelayForTest);
+ orig_params.min_ack_delay_us.set_value(kMinAckDelayUsForTest);
+ orig_params.disable_active_migration = kFakeDisableMigration;
+ orig_params.preferred_address = CreateFakePreferredAddress();
+ orig_params.active_connection_id_limit.set_value(
+ kActiveConnectionIdLimitForTest);
+ orig_params.initial_source_connection_id =
+ CreateFakeInitialSourceConnectionId();
+ orig_params.retry_source_connection_id = CreateFakeRetrySourceConnectionId();
+ orig_params.google_connection_options = CreateFakeGoogleConnectionOptions();
+
+ std::vector<uint8_t> serialized;
+ ASSERT_TRUE(SerializeTransportParameters(version_, orig_params, &serialized));
+
+ TransportParameters new_params;
+ std::string error_details;
+ ASSERT_TRUE(ParseTransportParameters(version_, Perspective::IS_SERVER,
+ serialized.data(), serialized.size(),
+ &new_params, &error_details))
+ << error_details;
+ EXPECT_TRUE(error_details.empty());
+ RemoveGreaseParameters(&new_params);
+ EXPECT_EQ(new_params, orig_params);
+}
+
+TEST_P(TransportParametersTest, AreValid) {
+ {
+ TransportParameters params;
+ std::string error_details;
+ params.perspective = Perspective::IS_CLIENT;
+ EXPECT_TRUE(params.AreValid(&error_details));
+ EXPECT_TRUE(error_details.empty());
+ }
+ {
+ TransportParameters params;
+ std::string error_details;
+ params.perspective = Perspective::IS_CLIENT;
+ EXPECT_TRUE(params.AreValid(&error_details));
+ EXPECT_TRUE(error_details.empty());
+ params.max_idle_timeout_ms.set_value(kFakeIdleTimeoutMilliseconds);
+ EXPECT_TRUE(params.AreValid(&error_details));
+ EXPECT_TRUE(error_details.empty());
+ params.max_idle_timeout_ms.set_value(601000);
+ EXPECT_TRUE(params.AreValid(&error_details));
+ EXPECT_TRUE(error_details.empty());
+ }
+ {
+ TransportParameters params;
+ std::string error_details;
+ params.perspective = Perspective::IS_CLIENT;
+ EXPECT_TRUE(params.AreValid(&error_details));
+ EXPECT_TRUE(error_details.empty());
+ params.max_udp_payload_size.set_value(1200);
+ EXPECT_TRUE(params.AreValid(&error_details));
+ EXPECT_TRUE(error_details.empty());
+ params.max_udp_payload_size.set_value(65535);
+ EXPECT_TRUE(params.AreValid(&error_details));
+ EXPECT_TRUE(error_details.empty());
+ params.max_udp_payload_size.set_value(9999999);
+ EXPECT_TRUE(params.AreValid(&error_details));
+ EXPECT_TRUE(error_details.empty());
+ params.max_udp_payload_size.set_value(0);
+ error_details = "";
+ EXPECT_FALSE(params.AreValid(&error_details));
+ EXPECT_EQ(error_details,
+ "Invalid transport parameters [Client max_udp_payload_size 0 "
+ "(Invalid)]");
+ params.max_udp_payload_size.set_value(1199);
+ error_details = "";
+ EXPECT_FALSE(params.AreValid(&error_details));
+ EXPECT_EQ(error_details,
+ "Invalid transport parameters [Client max_udp_payload_size 1199 "
+ "(Invalid)]");
+ }
+ {
+ TransportParameters params;
+ std::string error_details;
+ params.perspective = Perspective::IS_CLIENT;
+ EXPECT_TRUE(params.AreValid(&error_details));
+ EXPECT_TRUE(error_details.empty());
+ params.ack_delay_exponent.set_value(0);
+ EXPECT_TRUE(params.AreValid(&error_details));
+ EXPECT_TRUE(error_details.empty());
+ params.ack_delay_exponent.set_value(20);
+ EXPECT_TRUE(params.AreValid(&error_details));
+ EXPECT_TRUE(error_details.empty());
+ params.ack_delay_exponent.set_value(21);
+ EXPECT_FALSE(params.AreValid(&error_details));
+ EXPECT_EQ(error_details,
+ "Invalid transport parameters [Client ack_delay_exponent 21 "
+ "(Invalid)]");
+ }
+ {
+ TransportParameters params;
+ std::string error_details;
+ params.perspective = Perspective::IS_CLIENT;
+ EXPECT_TRUE(params.AreValid(&error_details));
+ EXPECT_TRUE(error_details.empty());
+ params.active_connection_id_limit.set_value(2);
+ EXPECT_TRUE(params.AreValid(&error_details));
+ EXPECT_TRUE(error_details.empty());
+ params.active_connection_id_limit.set_value(999999);
+ EXPECT_TRUE(params.AreValid(&error_details));
+ EXPECT_TRUE(error_details.empty());
+ params.active_connection_id_limit.set_value(1);
+ EXPECT_FALSE(params.AreValid(&error_details));
+ EXPECT_EQ(error_details,
+ "Invalid transport parameters [Client active_connection_id_limit"
+ " 1 (Invalid)]");
+ params.active_connection_id_limit.set_value(0);
+ EXPECT_FALSE(params.AreValid(&error_details));
+ EXPECT_EQ(error_details,
+ "Invalid transport parameters [Client active_connection_id_limit"
+ " 0 (Invalid)]");
+ }
+}
+
+TEST_P(TransportParametersTest, NoClientParamsWithStatelessResetToken) {
+ TransportParameters orig_params;
+ orig_params.perspective = Perspective::IS_CLIENT;
+ orig_params.legacy_version_information =
+ CreateFakeLegacyVersionInformationClient();
+ orig_params.max_idle_timeout_ms.set_value(kFakeIdleTimeoutMilliseconds);
+ orig_params.stateless_reset_token = CreateStatelessResetTokenForTest();
+ orig_params.max_udp_payload_size.set_value(kMaxPacketSizeForTest);
+
+ std::vector<uint8_t> out;
+ bool ok = true;
+ EXPECT_QUIC_BUG(
+ ok = SerializeTransportParameters(version_, orig_params, &out),
+ "Not serializing invalid transport parameters: Client cannot send "
+ "stateless reset token");
+ EXPECT_FALSE(ok);
+}
+
+TEST_P(TransportParametersTest, ParseClientParams) {
+ // clang-format off
+ const uint8_t kClientParams[] = {
+ // max_idle_timeout
+ 0x01, // parameter id
+ 0x02, // length
+ 0x6e, 0xec, // value
+ // max_udp_payload_size
+ 0x03, // parameter id
+ 0x02, // length
+ 0x63, 0x29, // value
+ // initial_max_data
+ 0x04, // parameter id
+ 0x02, // length
+ 0x40, 0x65, // value
+ // initial_max_stream_data_bidi_local
+ 0x05, // parameter id
+ 0x02, // length
+ 0x47, 0xD1, // value
+ // initial_max_stream_data_bidi_remote
+ 0x06, // parameter id
+ 0x02, // length
+ 0x47, 0xD2, // value
+ // initial_max_stream_data_uni
+ 0x07, // parameter id
+ 0x02, // length
+ 0x4B, 0xB8, // value
+ // initial_max_streams_bidi
+ 0x08, // parameter id
+ 0x01, // length
+ 0x15, // value
+ // initial_max_streams_uni
+ 0x09, // parameter id
+ 0x01, // length
+ 0x16, // value
+ // ack_delay_exponent
+ 0x0a, // parameter id
+ 0x01, // length
+ 0x0a, // value
+ // max_ack_delay
+ 0x0b, // parameter id
+ 0x01, // length
+ 0x33, // value
+ // min_ack_delay_us
+ 0x80, 0x00, 0xde, 0x1a, // parameter id
+ 0x02, // length
+ 0x43, 0xe8, // value
+ // disable_active_migration
+ 0x0c, // parameter id
+ 0x00, // length
+ // active_connection_id_limit
+ 0x0e, // parameter id
+ 0x01, // length
+ 0x34, // value
+ // initial_source_connection_id
+ 0x0f, // parameter id
+ 0x08, // length
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x45,
+ // initial_round_trip_time_us
+ 0x71, 0x27, // parameter id
+ 0x01, // length
+ 0x35, // value
+ // google_connection_options
+ 0x71, 0x28, // parameter id
+ 0x0c, // length
+ 'A', 'L', 'P', 'N', // value
+ 'E', 'F', 'G', 0x00,
+ 'H', 'I', 'J', 0xff,
+ // Google version extension
+ 0x80, 0x00, 0x47, 0x52, // parameter id
+ 0x04, // length
+ 0x01, 0x23, 0x45, 0x67, // initial version
+ // version_information
+ 0x80, 0xFF, 0x73, 0xDB, // parameter id
+ 0x0C, // length
+ 0x01, 0x23, 0x45, 0x67, // chosen version
+ 0x01, 0x23, 0x45, 0x67, // other version 1
+ 0x89, 0xab, 0xcd, 0xef, // other version 2
+ };
+ // clang-format on
+ const uint8_t* client_params =
+ reinterpret_cast<const uint8_t*>(kClientParams);
+ size_t client_params_length = ABSL_ARRAYSIZE(kClientParams);
+ TransportParameters new_params;
+ std::string error_details;
+ ASSERT_TRUE(ParseTransportParameters(version_, Perspective::IS_CLIENT,
+ client_params, client_params_length,
+ &new_params, &error_details))
+ << error_details;
+ EXPECT_TRUE(error_details.empty());
+ EXPECT_EQ(Perspective::IS_CLIENT, new_params.perspective);
+ ASSERT_TRUE(new_params.legacy_version_information.has_value());
+ EXPECT_EQ(kFakeVersionLabel,
+ new_params.legacy_version_information.value().version);
+ EXPECT_TRUE(
+ new_params.legacy_version_information.value().supported_versions.empty());
+ ASSERT_TRUE(new_params.version_information.has_value());
+ EXPECT_EQ(new_params.version_information.value(),
+ CreateFakeVersionInformation());
+ EXPECT_FALSE(new_params.original_destination_connection_id.has_value());
+ EXPECT_EQ(kFakeIdleTimeoutMilliseconds,
+ new_params.max_idle_timeout_ms.value());
+ EXPECT_TRUE(new_params.stateless_reset_token.empty());
+ EXPECT_EQ(kMaxPacketSizeForTest, new_params.max_udp_payload_size.value());
+ EXPECT_EQ(kFakeInitialMaxData, new_params.initial_max_data.value());
+ EXPECT_EQ(kFakeInitialMaxStreamDataBidiLocal,
+ new_params.initial_max_stream_data_bidi_local.value());
+ EXPECT_EQ(kFakeInitialMaxStreamDataBidiRemote,
+ new_params.initial_max_stream_data_bidi_remote.value());
+ EXPECT_EQ(kFakeInitialMaxStreamDataUni,
+ new_params.initial_max_stream_data_uni.value());
+ EXPECT_EQ(kFakeInitialMaxStreamsBidi,
+ new_params.initial_max_streams_bidi.value());
+ EXPECT_EQ(kFakeInitialMaxStreamsUni,
+ new_params.initial_max_streams_uni.value());
+ EXPECT_EQ(kAckDelayExponentForTest, new_params.ack_delay_exponent.value());
+ EXPECT_EQ(kMaxAckDelayForTest, new_params.max_ack_delay.value());
+ EXPECT_EQ(kMinAckDelayUsForTest, new_params.min_ack_delay_us.value());
+ EXPECT_EQ(kFakeDisableMigration, new_params.disable_active_migration);
+ EXPECT_EQ(kActiveConnectionIdLimitForTest,
+ new_params.active_connection_id_limit.value());
+ ASSERT_TRUE(new_params.initial_source_connection_id.has_value());
+ EXPECT_EQ(CreateFakeInitialSourceConnectionId(),
+ new_params.initial_source_connection_id.value());
+ EXPECT_FALSE(new_params.retry_source_connection_id.has_value());
+ EXPECT_EQ(kFakeInitialRoundTripTime,
+ new_params.initial_round_trip_time_us.value());
+ ASSERT_TRUE(new_params.google_connection_options.has_value());
+ EXPECT_EQ(CreateFakeGoogleConnectionOptions(),
+ new_params.google_connection_options.value());
+}
+
+TEST_P(TransportParametersTest,
+ ParseClientParamsFailsWithFullStatelessResetToken) {
+ // clang-format off
+ const uint8_t kClientParamsWithFullToken[] = {
+ // max_idle_timeout
+ 0x01, // parameter id
+ 0x02, // length
+ 0x6e, 0xec, // value
+ // stateless_reset_token
+ 0x02, // parameter id
+ 0x10, // length
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
+ // max_udp_payload_size
+ 0x03, // parameter id
+ 0x02, // length
+ 0x63, 0x29, // value
+ // initial_max_data
+ 0x04, // parameter id
+ 0x02, // length
+ 0x40, 0x65, // value
+ };
+ // clang-format on
+ const uint8_t* client_params =
+ reinterpret_cast<const uint8_t*>(kClientParamsWithFullToken);
+ size_t client_params_length = ABSL_ARRAYSIZE(kClientParamsWithFullToken);
+ TransportParameters out_params;
+ std::string error_details;
+ EXPECT_FALSE(ParseTransportParameters(version_, Perspective::IS_CLIENT,
+ client_params, client_params_length,
+ &out_params, &error_details));
+ EXPECT_EQ(error_details, "Client cannot send stateless reset token");
+}
+
+TEST_P(TransportParametersTest,
+ ParseClientParamsFailsWithEmptyStatelessResetToken) {
+ // clang-format off
+ const uint8_t kClientParamsWithEmptyToken[] = {
+ // max_idle_timeout
+ 0x01, // parameter id
+ 0x02, // length
+ 0x6e, 0xec, // value
+ // stateless_reset_token
+ 0x02, // parameter id
+ 0x00, // length
+ // max_udp_payload_size
+ 0x03, // parameter id
+ 0x02, // length
+ 0x63, 0x29, // value
+ // initial_max_data
+ 0x04, // parameter id
+ 0x02, // length
+ 0x40, 0x65, // value
+ };
+ // clang-format on
+ const uint8_t* client_params =
+ reinterpret_cast<const uint8_t*>(kClientParamsWithEmptyToken);
+ size_t client_params_length = ABSL_ARRAYSIZE(kClientParamsWithEmptyToken);
+ TransportParameters out_params;
+ std::string error_details;
+ EXPECT_FALSE(ParseTransportParameters(version_, Perspective::IS_CLIENT,
+ client_params, client_params_length,
+ &out_params, &error_details));
+ EXPECT_EQ(error_details,
+ "Received stateless_reset_token of invalid length 0");
+}
+
+TEST_P(TransportParametersTest, ParseClientParametersRepeated) {
+ // clang-format off
+ const uint8_t kClientParamsRepeated[] = {
+ // max_idle_timeout
+ 0x01, // parameter id
+ 0x02, // length
+ 0x6e, 0xec, // value
+ // max_udp_payload_size
+ 0x03, // parameter id
+ 0x02, // length
+ 0x63, 0x29, // value
+ // max_idle_timeout (repeated)
+ 0x01, // parameter id
+ 0x02, // length
+ 0x6e, 0xec, // value
+ };
+ // clang-format on
+ const uint8_t* client_params =
+ reinterpret_cast<const uint8_t*>(kClientParamsRepeated);
+ size_t client_params_length = ABSL_ARRAYSIZE(kClientParamsRepeated);
+ TransportParameters out_params;
+ std::string error_details;
+ EXPECT_FALSE(ParseTransportParameters(version_, Perspective::IS_CLIENT,
+ client_params, client_params_length,
+ &out_params, &error_details));
+ EXPECT_EQ(error_details, "Received a second max_idle_timeout");
+}
+
+TEST_P(TransportParametersTest, ParseServerParams) {
+ // clang-format off
+ const uint8_t kServerParams[] = {
+ // original_destination_connection_id
+ 0x00, // parameter id
+ 0x08, // length
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x37,
+ // max_idle_timeout
+ 0x01, // parameter id
+ 0x02, // length
+ 0x6e, 0xec, // value
+ // stateless_reset_token
+ 0x02, // parameter id
+ 0x10, // length
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
+ // max_udp_payload_size
+ 0x03, // parameter id
+ 0x02, // length
+ 0x63, 0x29, // value
+ // initial_max_data
+ 0x04, // parameter id
+ 0x02, // length
+ 0x40, 0x65, // value
+ // initial_max_stream_data_bidi_local
+ 0x05, // parameter id
+ 0x02, // length
+ 0x47, 0xD1, // value
+ // initial_max_stream_data_bidi_remote
+ 0x06, // parameter id
+ 0x02, // length
+ 0x47, 0xD2, // value
+ // initial_max_stream_data_uni
+ 0x07, // parameter id
+ 0x02, // length
+ 0x4B, 0xB8, // value
+ // initial_max_streams_bidi
+ 0x08, // parameter id
+ 0x01, // length
+ 0x15, // value
+ // initial_max_streams_uni
+ 0x09, // parameter id
+ 0x01, // length
+ 0x16, // value
+ // ack_delay_exponent
+ 0x0a, // parameter id
+ 0x01, // length
+ 0x0a, // value
+ // max_ack_delay
+ 0x0b, // parameter id
+ 0x01, // length
+ 0x33, // value
+ // min_ack_delay_us
+ 0x80, 0x00, 0xde, 0x1a, // parameter id
+ 0x02, // length
+ 0x43, 0xe8, // value
+ // disable_active_migration
+ 0x0c, // parameter id
+ 0x00, // length
+ // preferred_address
+ 0x0d, // parameter id
+ 0x31, // length
+ 0x41, 0x42, 0x43, 0x44, // IPv4 address
+ 0x48, 0x84, // IPv4 port
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, // IPv6 address
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x63, 0x36, // IPv6 port
+ 0x08, // connection ID length
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xEF, // connection ID
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, // stateless reset token
+ 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
+ // active_connection_id_limit
+ 0x0e, // parameter id
+ 0x01, // length
+ 0x34, // value
+ // initial_source_connection_id
+ 0x0f, // parameter id
+ 0x08, // length
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x45,
+ // retry_source_connection_id
+ 0x10, // parameter id
+ 0x08, // length
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x76,
+ // google_connection_options
+ 0x71, 0x28, // parameter id
+ 0x0c, // length
+ 'A', 'L', 'P', 'N', // value
+ 'E', 'F', 'G', 0x00,
+ 'H', 'I', 'J', 0xff,
+ // Google version extension
+ 0x80, 0x00, 0x47, 0x52, // parameter id
+ 0x0d, // length
+ 0x01, 0x23, 0x45, 0x67, // negotiated_version
+ 0x08, // length of supported versions array
+ 0x01, 0x23, 0x45, 0x67,
+ 0x89, 0xab, 0xcd, 0xef,
+ // version_information
+ 0x80, 0xFF, 0x73, 0xDB, // parameter id
+ 0x0C, // length
+ 0x01, 0x23, 0x45, 0x67, // chosen version
+ 0x01, 0x23, 0x45, 0x67, // other version 1
+ 0x89, 0xab, 0xcd, 0xef, // other version 2
+ };
+ // clang-format on
+ const uint8_t* server_params =
+ reinterpret_cast<const uint8_t*>(kServerParams);
+ size_t server_params_length = ABSL_ARRAYSIZE(kServerParams);
+ TransportParameters new_params;
+ std::string error_details;
+ ASSERT_TRUE(ParseTransportParameters(version_, Perspective::IS_SERVER,
+ server_params, server_params_length,
+ &new_params, &error_details))
+ << error_details;
+ EXPECT_TRUE(error_details.empty());
+ EXPECT_EQ(Perspective::IS_SERVER, new_params.perspective);
+ ASSERT_TRUE(new_params.legacy_version_information.has_value());
+ EXPECT_EQ(kFakeVersionLabel,
+ new_params.legacy_version_information.value().version);
+ ASSERT_EQ(
+ 2u,
+ new_params.legacy_version_information.value().supported_versions.size());
+ EXPECT_EQ(
+ kFakeVersionLabel,
+ new_params.legacy_version_information.value().supported_versions[0]);
+ EXPECT_EQ(
+ kFakeVersionLabel2,
+ new_params.legacy_version_information.value().supported_versions[1]);
+ ASSERT_TRUE(new_params.version_information.has_value());
+ EXPECT_EQ(new_params.version_information.value(),
+ CreateFakeVersionInformation());
+ ASSERT_TRUE(new_params.original_destination_connection_id.has_value());
+ EXPECT_EQ(CreateFakeOriginalDestinationConnectionId(),
+ new_params.original_destination_connection_id.value());
+ EXPECT_EQ(kFakeIdleTimeoutMilliseconds,
+ new_params.max_idle_timeout_ms.value());
+ EXPECT_EQ(CreateStatelessResetTokenForTest(),
+ new_params.stateless_reset_token);
+ EXPECT_EQ(kMaxPacketSizeForTest, new_params.max_udp_payload_size.value());
+ EXPECT_EQ(kFakeInitialMaxData, new_params.initial_max_data.value());
+ EXPECT_EQ(kFakeInitialMaxStreamDataBidiLocal,
+ new_params.initial_max_stream_data_bidi_local.value());
+ EXPECT_EQ(kFakeInitialMaxStreamDataBidiRemote,
+ new_params.initial_max_stream_data_bidi_remote.value());
+ EXPECT_EQ(kFakeInitialMaxStreamDataUni,
+ new_params.initial_max_stream_data_uni.value());
+ EXPECT_EQ(kFakeInitialMaxStreamsBidi,
+ new_params.initial_max_streams_bidi.value());
+ EXPECT_EQ(kFakeInitialMaxStreamsUni,
+ new_params.initial_max_streams_uni.value());
+ EXPECT_EQ(kAckDelayExponentForTest, new_params.ack_delay_exponent.value());
+ EXPECT_EQ(kMaxAckDelayForTest, new_params.max_ack_delay.value());
+ EXPECT_EQ(kMinAckDelayUsForTest, new_params.min_ack_delay_us.value());
+ EXPECT_EQ(kFakeDisableMigration, new_params.disable_active_migration);
+ ASSERT_NE(nullptr, new_params.preferred_address.get());
+ EXPECT_EQ(CreateFakeV4SocketAddress(),
+ new_params.preferred_address->ipv4_socket_address);
+ EXPECT_EQ(CreateFakeV6SocketAddress(),
+ new_params.preferred_address->ipv6_socket_address);
+ EXPECT_EQ(CreateFakePreferredConnectionId(),
+ new_params.preferred_address->connection_id);
+ EXPECT_EQ(CreateFakePreferredStatelessResetToken(),
+ new_params.preferred_address->stateless_reset_token);
+ EXPECT_EQ(kActiveConnectionIdLimitForTest,
+ new_params.active_connection_id_limit.value());
+ ASSERT_TRUE(new_params.initial_source_connection_id.has_value());
+ EXPECT_EQ(CreateFakeInitialSourceConnectionId(),
+ new_params.initial_source_connection_id.value());
+ ASSERT_TRUE(new_params.retry_source_connection_id.has_value());
+ EXPECT_EQ(CreateFakeRetrySourceConnectionId(),
+ new_params.retry_source_connection_id.value());
+ ASSERT_TRUE(new_params.google_connection_options.has_value());
+ EXPECT_EQ(CreateFakeGoogleConnectionOptions(),
+ new_params.google_connection_options.value());
+}
+
+TEST_P(TransportParametersTest, ParseServerParametersRepeated) {
+ // clang-format off
+ const uint8_t kServerParamsRepeated[] = {
+ // original_destination_connection_id
+ 0x00, // parameter id
+ 0x08, // length
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x37,
+ // max_idle_timeout
+ 0x01, // parameter id
+ 0x02, // length
+ 0x6e, 0xec, // value
+ // stateless_reset_token
+ 0x02, // parameter id
+ 0x10, // length
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+ // max_idle_timeout (repeated)
+ 0x01, // parameter id
+ 0x02, // length
+ 0x6e, 0xec, // value
+ };
+ // clang-format on
+ const uint8_t* server_params =
+ reinterpret_cast<const uint8_t*>(kServerParamsRepeated);
+ size_t server_params_length = ABSL_ARRAYSIZE(kServerParamsRepeated);
+ TransportParameters out_params;
+ std::string error_details;
+ EXPECT_FALSE(ParseTransportParameters(version_, Perspective::IS_SERVER,
+ server_params, server_params_length,
+ &out_params, &error_details));
+ EXPECT_EQ(error_details, "Received a second max_idle_timeout");
+}
+
+TEST_P(TransportParametersTest,
+ ParseServerParametersEmptyOriginalConnectionId) {
+ // clang-format off
+ const uint8_t kServerParamsEmptyOriginalConnectionId[] = {
+ // original_destination_connection_id
+ 0x00, // parameter id
+ 0x00, // length
+ // max_idle_timeout
+ 0x01, // parameter id
+ 0x02, // length
+ 0x6e, 0xec, // value
+ // stateless_reset_token
+ 0x02, // parameter id
+ 0x10, // length
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+ };
+ // clang-format on
+ const uint8_t* server_params =
+ reinterpret_cast<const uint8_t*>(kServerParamsEmptyOriginalConnectionId);
+ size_t server_params_length =
+ ABSL_ARRAYSIZE(kServerParamsEmptyOriginalConnectionId);
+ TransportParameters out_params;
+ std::string error_details;
+ ASSERT_TRUE(ParseTransportParameters(version_, Perspective::IS_SERVER,
+ server_params, server_params_length,
+ &out_params, &error_details))
+ << error_details;
+ ASSERT_TRUE(out_params.original_destination_connection_id.has_value());
+ EXPECT_EQ(out_params.original_destination_connection_id.value(),
+ EmptyQuicConnectionId());
+}
+
+TEST_P(TransportParametersTest, VeryLongCustomParameter) {
+ // Ensure we can handle a 70KB custom parameter on both send and receive.
+ std::string custom_value(70000, '?');
+ TransportParameters orig_params;
+ orig_params.perspective = Perspective::IS_CLIENT;
+ orig_params.legacy_version_information =
+ CreateFakeLegacyVersionInformationClient();
+ orig_params.custom_parameters[kCustomParameter1] = custom_value;
+
+ std::vector<uint8_t> serialized;
+ ASSERT_TRUE(SerializeTransportParameters(version_, orig_params, &serialized));
+
+ TransportParameters new_params;
+ std::string error_details;
+ ASSERT_TRUE(ParseTransportParameters(version_, Perspective::IS_CLIENT,
+ serialized.data(), serialized.size(),
+ &new_params, &error_details))
+ << error_details;
+ EXPECT_TRUE(error_details.empty());
+ RemoveGreaseParameters(&new_params);
+ EXPECT_EQ(new_params, orig_params);
+}
+
+TEST_P(TransportParametersTest, SerializationOrderIsRandom) {
+ TransportParameters orig_params;
+ orig_params.perspective = Perspective::IS_CLIENT;
+ orig_params.legacy_version_information =
+ CreateFakeLegacyVersionInformationClient();
+ orig_params.max_idle_timeout_ms.set_value(kFakeIdleTimeoutMilliseconds);
+ orig_params.max_udp_payload_size.set_value(kMaxPacketSizeForTest);
+ orig_params.initial_max_data.set_value(kFakeInitialMaxData);
+ orig_params.initial_max_stream_data_bidi_local.set_value(
+ kFakeInitialMaxStreamDataBidiLocal);
+ orig_params.initial_max_stream_data_bidi_remote.set_value(
+ kFakeInitialMaxStreamDataBidiRemote);
+ orig_params.initial_max_stream_data_uni.set_value(
+ kFakeInitialMaxStreamDataUni);
+ orig_params.initial_max_streams_bidi.set_value(kFakeInitialMaxStreamsBidi);
+ orig_params.initial_max_streams_uni.set_value(kFakeInitialMaxStreamsUni);
+ orig_params.ack_delay_exponent.set_value(kAckDelayExponentForTest);
+ orig_params.max_ack_delay.set_value(kMaxAckDelayForTest);
+ orig_params.min_ack_delay_us.set_value(kMinAckDelayUsForTest);
+ orig_params.disable_active_migration = kFakeDisableMigration;
+ orig_params.active_connection_id_limit.set_value(
+ kActiveConnectionIdLimitForTest);
+ orig_params.initial_source_connection_id =
+ CreateFakeInitialSourceConnectionId();
+ orig_params.initial_round_trip_time_us.set_value(kFakeInitialRoundTripTime);
+ orig_params.google_connection_options = CreateFakeGoogleConnectionOptions();
+ orig_params.custom_parameters[kCustomParameter1] = kCustomParameter1Value;
+ orig_params.custom_parameters[kCustomParameter2] = kCustomParameter2Value;
+
+ std::vector<uint8_t> first_serialized;
+ ASSERT_TRUE(
+ SerializeTransportParameters(version_, orig_params, &first_serialized));
+ // Test that a subsequent serialization is different from the first.
+ // Run in a loop to avoid a failure in the unlikely event that randomization
+ // produces the same result multiple times.
+ for (int i = 0; i < 1000; i++) {
+ std::vector<uint8_t> serialized;
+ ASSERT_TRUE(
+ SerializeTransportParameters(version_, orig_params, &serialized));
+ if (serialized != first_serialized) {
+ return;
+ }
+ }
+}
+
+class TransportParametersTicketSerializationTest : public QuicTest {
+ protected:
+ void SetUp() override {
+ original_params_.perspective = Perspective::IS_SERVER;
+ original_params_.legacy_version_information =
+ CreateFakeLegacyVersionInformationServer();
+ original_params_.original_destination_connection_id =
+ CreateFakeOriginalDestinationConnectionId();
+ original_params_.max_idle_timeout_ms.set_value(
+ kFakeIdleTimeoutMilliseconds);
+ original_params_.stateless_reset_token = CreateStatelessResetTokenForTest();
+ original_params_.max_udp_payload_size.set_value(kMaxPacketSizeForTest);
+ original_params_.initial_max_data.set_value(kFakeInitialMaxData);
+ original_params_.initial_max_stream_data_bidi_local.set_value(
+ kFakeInitialMaxStreamDataBidiLocal);
+ original_params_.initial_max_stream_data_bidi_remote.set_value(
+ kFakeInitialMaxStreamDataBidiRemote);
+ original_params_.initial_max_stream_data_uni.set_value(
+ kFakeInitialMaxStreamDataUni);
+ original_params_.initial_max_streams_bidi.set_value(
+ kFakeInitialMaxStreamsBidi);
+ original_params_.initial_max_streams_uni.set_value(
+ kFakeInitialMaxStreamsUni);
+ original_params_.ack_delay_exponent.set_value(kAckDelayExponentForTest);
+ original_params_.max_ack_delay.set_value(kMaxAckDelayForTest);
+ original_params_.min_ack_delay_us.set_value(kMinAckDelayUsForTest);
+ original_params_.disable_active_migration = kFakeDisableMigration;
+ original_params_.preferred_address = CreateFakePreferredAddress();
+ original_params_.active_connection_id_limit.set_value(
+ kActiveConnectionIdLimitForTest);
+ original_params_.initial_source_connection_id =
+ CreateFakeInitialSourceConnectionId();
+ original_params_.retry_source_connection_id =
+ CreateFakeRetrySourceConnectionId();
+ original_params_.google_connection_options =
+ CreateFakeGoogleConnectionOptions();
+
+ ASSERT_TRUE(SerializeTransportParametersForTicket(
+ original_params_, application_state_, &original_serialized_params_));
+ }
+
+ TransportParameters original_params_;
+ std::vector<uint8_t> application_state_ = {0, 1};
+ std::vector<uint8_t> original_serialized_params_;
+};
+
+TEST_F(TransportParametersTicketSerializationTest,
+ StatelessResetTokenDoesntChangeOutput) {
+ // Test that changing the stateless reset token doesn't change the ticket
+ // serialization.
+ TransportParameters new_params = original_params_;
+ new_params.stateless_reset_token = CreateFakePreferredStatelessResetToken();
+ EXPECT_NE(new_params, original_params_);
+
+ std::vector<uint8_t> serialized;
+ ASSERT_TRUE(SerializeTransportParametersForTicket(
+ new_params, application_state_, &serialized));
+ EXPECT_EQ(original_serialized_params_, serialized);
+}
+
+TEST_F(TransportParametersTicketSerializationTest,
+ ConnectionIDDoesntChangeOutput) {
+ // Changing original destination CID doesn't change serialization.
+ TransportParameters new_params = original_params_;
+ new_params.original_destination_connection_id = TestConnectionId(0xCAFE);
+ EXPECT_NE(new_params, original_params_);
+
+ std::vector<uint8_t> serialized;
+ ASSERT_TRUE(SerializeTransportParametersForTicket(
+ new_params, application_state_, &serialized));
+ EXPECT_EQ(original_serialized_params_, serialized);
+}
+
+TEST_F(TransportParametersTicketSerializationTest, StreamLimitChangesOutput) {
+ // Changing a stream limit does change the serialization.
+ TransportParameters new_params = original_params_;
+ new_params.initial_max_stream_data_bidi_local.set_value(
+ kFakeInitialMaxStreamDataBidiLocal + 1);
+ EXPECT_NE(new_params, original_params_);
+
+ std::vector<uint8_t> serialized;
+ ASSERT_TRUE(SerializeTransportParametersForTicket(
+ new_params, application_state_, &serialized));
+ EXPECT_NE(original_serialized_params_, serialized);
+}
+
+TEST_F(TransportParametersTicketSerializationTest,
+ ApplicationStateChangesOutput) {
+ // Changing the application state changes the serialization.
+ std::vector<uint8_t> new_application_state = {0};
+ EXPECT_NE(new_application_state, application_state_);
+
+ std::vector<uint8_t> serialized;
+ ASSERT_TRUE(SerializeTransportParametersForTicket(
+ original_params_, new_application_state, &serialized));
+ EXPECT_NE(original_serialized_params_, serialized);
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/web_transport_fingerprint_proof_verifier.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/web_transport_fingerprint_proof_verifier.cc
new file mode 100644
index 00000000000..5f2d587b777
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/web_transport_fingerprint_proof_verifier.cc
@@ -0,0 +1,229 @@
+// 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 "quiche/quic/core/crypto/web_transport_fingerprint_proof_verifier.h"
+
+#include <cstdint>
+#include <memory>
+
+#include "absl/strings/escaping.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/str_replace.h"
+#include "absl/strings/string_view.h"
+#include "openssl/sha.h"
+#include "quiche/quic/core/crypto/certificate_view.h"
+#include "quiche/quic/core/quic_time.h"
+#include "quiche/quic/core/quic_types.h"
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/quic/platform/api/quic_bug_tracker.h"
+#include "quiche/common/quiche_text_utils.h"
+
+namespace quic {
+namespace {
+
+constexpr size_t kFingerprintLength = SHA256_DIGEST_LENGTH * 3 - 1;
+
+// Assumes that the character is normalized to lowercase beforehand.
+bool IsNormalizedHexDigit(char c) {
+ return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f');
+}
+
+void NormalizeFingerprint(CertificateFingerprint& fingerprint) {
+ fingerprint.fingerprint =
+ quiche::QuicheTextUtils::ToLower(fingerprint.fingerprint);
+}
+
+} // namespace
+
+constexpr char CertificateFingerprint::kSha256[];
+constexpr char WebTransportHash::kSha256[];
+
+ProofVerifyDetails* WebTransportFingerprintProofVerifier::Details::Clone()
+ const {
+ return new Details(*this);
+}
+
+WebTransportFingerprintProofVerifier::WebTransportFingerprintProofVerifier(
+ const QuicClock* clock, int max_validity_days)
+ : clock_(clock),
+ max_validity_days_(max_validity_days),
+ // Add an extra second to max validity to accomodate various edge cases.
+ max_validity_(
+ QuicTime::Delta::FromSeconds(max_validity_days * 86400 + 1)) {}
+
+bool WebTransportFingerprintProofVerifier::AddFingerprint(
+ CertificateFingerprint fingerprint) {
+ NormalizeFingerprint(fingerprint);
+ if (fingerprint.algorithm != CertificateFingerprint::kSha256) {
+ QUIC_DLOG(WARNING) << "Algorithms other than SHA-256 are not supported";
+ return false;
+ }
+ if (fingerprint.fingerprint.size() != kFingerprintLength) {
+ QUIC_DLOG(WARNING) << "Invalid fingerprint length";
+ return false;
+ }
+ for (size_t i = 0; i < fingerprint.fingerprint.size(); i++) {
+ char current = fingerprint.fingerprint[i];
+ if (i % 3 == 2) {
+ if (current != ':') {
+ QUIC_DLOG(WARNING)
+ << "Missing colon separator between the bytes of the hash";
+ return false;
+ }
+ } else {
+ if (!IsNormalizedHexDigit(current)) {
+ QUIC_DLOG(WARNING) << "Fingerprint must be in hexadecimal";
+ return false;
+ }
+ }
+ }
+
+ std::string normalized =
+ absl::StrReplaceAll(fingerprint.fingerprint, {{":", ""}});
+ hashes_.push_back(WebTransportHash{fingerprint.algorithm,
+ absl::HexStringToBytes(normalized)});
+ return true;
+}
+
+bool WebTransportFingerprintProofVerifier::AddFingerprint(
+ WebTransportHash hash) {
+ if (hash.algorithm != CertificateFingerprint::kSha256) {
+ QUIC_DLOG(WARNING) << "Algorithms other than SHA-256 are not supported";
+ return false;
+ }
+ if (hash.value.size() != SHA256_DIGEST_LENGTH) {
+ QUIC_DLOG(WARNING) << "Invalid fingerprint length";
+ return false;
+ }
+ hashes_.push_back(std::move(hash));
+ return true;
+}
+
+QuicAsyncStatus WebTransportFingerprintProofVerifier::VerifyProof(
+ const std::string& /*hostname*/, const uint16_t /*port*/,
+ const std::string& /*server_config*/,
+ QuicTransportVersion /*transport_version*/, absl::string_view /*chlo_hash*/,
+ const std::vector<std::string>& /*certs*/, const std::string& /*cert_sct*/,
+ const std::string& /*signature*/, const ProofVerifyContext* /*context*/,
+ std::string* error_details, std::unique_ptr<ProofVerifyDetails>* details,
+ std::unique_ptr<ProofVerifierCallback> /*callback*/) {
+ *error_details =
+ "QUIC crypto certificate verification is not supported in "
+ "WebTransportFingerprintProofVerifier";
+ QUIC_BUG(quic_bug_10879_1) << *error_details;
+ *details = std::make_unique<Details>(Status::kInternalError);
+ return QUIC_FAILURE;
+}
+
+QuicAsyncStatus WebTransportFingerprintProofVerifier::VerifyCertChain(
+ const std::string& /*hostname*/, const uint16_t /*port*/,
+ const std::vector<std::string>& certs, const std::string& /*ocsp_response*/,
+ const std::string& /*cert_sct*/, const ProofVerifyContext* /*context*/,
+ std::string* error_details, std::unique_ptr<ProofVerifyDetails>* details,
+ uint8_t* /*out_alert*/,
+ std::unique_ptr<ProofVerifierCallback> /*callback*/) {
+ if (certs.empty()) {
+ *details = std::make_unique<Details>(Status::kInternalError);
+ *error_details = "No certificates provided";
+ return QUIC_FAILURE;
+ }
+
+ if (!HasKnownFingerprint(certs[0])) {
+ *details = std::make_unique<Details>(Status::kUnknownFingerprint);
+ *error_details = "Certificate does not match any fingerprint";
+ return QUIC_FAILURE;
+ }
+
+ std::unique_ptr<CertificateView> view =
+ CertificateView::ParseSingleCertificate(certs[0]);
+ if (view == nullptr) {
+ *details = std::make_unique<Details>(Status::kCertificateParseFailure);
+ *error_details = "Failed to parse the certificate";
+ return QUIC_FAILURE;
+ }
+
+ if (!HasValidExpiry(*view)) {
+ *details = std::make_unique<Details>(Status::kExpiryTooLong);
+ *error_details =
+ absl::StrCat("Certificate expiry exceeds the configured limit of ",
+ max_validity_days_, " days");
+ return QUIC_FAILURE;
+ }
+
+ if (!IsWithinValidityPeriod(*view)) {
+ *details = std::make_unique<Details>(Status::kExpired);
+ *error_details =
+ "Certificate has expired or has validity listed in the future";
+ return QUIC_FAILURE;
+ }
+
+ if (!IsKeyTypeAllowedByPolicy(*view)) {
+ *details = std::make_unique<Details>(Status::kDisallowedKeyAlgorithm);
+ *error_details =
+ absl::StrCat("Certificate uses a disallowed public key type (",
+ PublicKeyTypeToString(view->public_key_type()), ")");
+ return QUIC_FAILURE;
+ }
+
+ *details = std::make_unique<Details>(Status::kValidCertificate);
+ return QUIC_SUCCESS;
+}
+
+std::unique_ptr<ProofVerifyContext>
+WebTransportFingerprintProofVerifier::CreateDefaultContext() {
+ return nullptr;
+}
+
+bool WebTransportFingerprintProofVerifier::HasKnownFingerprint(
+ absl::string_view der_certificate) {
+ // https://w3c.github.io/webtransport/#verify-a-certificate-hash
+ const std::string hash = RawSha256(der_certificate);
+ for (const WebTransportHash& reference : hashes_) {
+ if (reference.algorithm != WebTransportHash::kSha256) {
+ QUIC_BUG(quic_bug_10879_2) << "Unexpected non-SHA-256 hash";
+ continue;
+ }
+ if (hash == reference.value) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool WebTransportFingerprintProofVerifier::HasValidExpiry(
+ const CertificateView& certificate) {
+ if (!certificate.validity_start().IsBefore(certificate.validity_end())) {
+ return false;
+ }
+
+ const QuicTime::Delta duration_seconds =
+ certificate.validity_end() - certificate.validity_start();
+ return duration_seconds <= max_validity_;
+}
+
+bool WebTransportFingerprintProofVerifier::IsWithinValidityPeriod(
+ const CertificateView& certificate) {
+ QuicWallTime now = clock_->WallNow();
+ return now.IsAfter(certificate.validity_start()) &&
+ now.IsBefore(certificate.validity_end());
+}
+
+bool WebTransportFingerprintProofVerifier::IsKeyTypeAllowedByPolicy(
+ const CertificateView& certificate) {
+ switch (certificate.public_key_type()) {
+ // https://github.com/w3c/webtransport/pull/375 defines P-256 as an MTI
+ // algorithm, and prohibits RSA. We also allow P-384 and Ed25519.
+ case PublicKeyType::kP256:
+ case PublicKeyType::kP384:
+ case PublicKeyType::kEd25519:
+ return true;
+ case PublicKeyType::kRsa:
+ // TODO(b/213614428): this should be false by default.
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/web_transport_fingerprint_proof_verifier.h b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/web_transport_fingerprint_proof_verifier.h
new file mode 100644
index 00000000000..ea1908d27c5
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/web_transport_fingerprint_proof_verifier.h
@@ -0,0 +1,126 @@
+// 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.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_WEB_TRANSPORT_FINGERPRINT_PROOF_VERIFIER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_WEB_TRANSPORT_FINGERPRINT_PROOF_VERIFIER_H_
+
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/crypto/certificate_view.h"
+#include "quiche/quic/core/crypto/proof_verifier.h"
+#include "quiche/quic/core/quic_clock.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// Represents a fingerprint of an X.509 certificate in a format based on
+// https://w3c.github.io/webrtc-pc/#dom-rtcdtlsfingerprint.
+// TODO(vasilvv): remove this once all consumers of this API use
+// WebTransportHash.
+struct QUIC_EXPORT_PRIVATE CertificateFingerprint {
+ static constexpr char kSha256[] = "sha-256";
+
+ // An algorithm described by one of the names in
+ // https://www.iana.org/assignments/hash-function-text-names/hash-function-text-names.xhtml
+ std::string algorithm;
+ // Hex-encoded, colon-separated fingerprint of the certificate. For example,
+ // "12:3d:5b:71:8c:54:df:85:7e:bd:e3:7c:66:da:f9:db:6a:94:8f:85:cb:6e:44:7f:09:3e:05:f2:dd:d4:f7:86"
+ std::string fingerprint;
+};
+
+// Represents a fingerprint of an X.509 certificate in a format based on
+// https://w3c.github.io/webtransport/#dictdef-webtransporthash.
+struct QUIC_EXPORT_PRIVATE WebTransportHash {
+ static constexpr char kSha256[] = "sha-256";
+
+ // An algorithm described by one of the names in
+ // https://www.iana.org/assignments/hash-function-text-names/hash-function-text-names.xhtml
+ std::string algorithm;
+ // Raw bytes of the hash.
+ std::string value;
+};
+
+// WebTransportFingerprintProofVerifier verifies the server leaf certificate
+// against a supplied list of certificate fingerprints following the procedure
+// described in the WebTransport specification. The certificate is deemed
+// trusted if it matches a fingerprint in the list, has expiry dates that are
+// not too long and has not expired. Only the leaf is checked, the rest of the
+// chain is ignored. Reference specification:
+// https://wicg.github.io/web-transport/#dom-quictransportconfiguration-server_certificate_fingerprints
+class QUIC_EXPORT_PRIVATE WebTransportFingerprintProofVerifier
+ : public ProofVerifier {
+ public:
+ // Note: the entries in this list may be logged into a UMA histogram, and thus
+ // should not be renumbered.
+ enum class Status {
+ kValidCertificate = 0,
+ kUnknownFingerprint = 1,
+ kCertificateParseFailure = 2,
+ kExpiryTooLong = 3,
+ kExpired = 4,
+ kInternalError = 5,
+ kDisallowedKeyAlgorithm = 6,
+
+ kMaxValue = kDisallowedKeyAlgorithm,
+ };
+
+ class QUIC_EXPORT_PRIVATE Details : public ProofVerifyDetails {
+ public:
+ explicit Details(Status status) : status_(status) {}
+ Status status() const { return status_; }
+
+ ProofVerifyDetails* Clone() const override;
+
+ private:
+ const Status status_;
+ };
+
+ // |clock| is used to check if the certificate has expired. It is not owned
+ // and must outlive the object. |max_validity_days| is the maximum time for
+ // which the certificate is allowed to be valid.
+ WebTransportFingerprintProofVerifier(const QuicClock* clock,
+ int max_validity_days);
+
+ // Adds a certificate fingerprint to be trusted. The fingerprints are
+ // case-insensitive and are validated internally; the function returns true if
+ // the validation passes.
+ bool AddFingerprint(CertificateFingerprint fingerprint);
+ bool AddFingerprint(WebTransportHash hash);
+
+ // ProofVerifier implementation.
+ QuicAsyncStatus VerifyProof(
+ const std::string& hostname, const uint16_t port,
+ const std::string& server_config, QuicTransportVersion transport_version,
+ absl::string_view chlo_hash, const std::vector<std::string>& certs,
+ const std::string& cert_sct, const std::string& signature,
+ const ProofVerifyContext* context, std::string* error_details,
+ std::unique_ptr<ProofVerifyDetails>* details,
+ std::unique_ptr<ProofVerifierCallback> callback) override;
+ QuicAsyncStatus VerifyCertChain(
+ const std::string& hostname, const uint16_t port,
+ const std::vector<std::string>& certs, const std::string& ocsp_response,
+ const std::string& cert_sct, const ProofVerifyContext* context,
+ std::string* error_details, std::unique_ptr<ProofVerifyDetails>* details,
+ uint8_t* out_alert,
+ std::unique_ptr<ProofVerifierCallback> callback) override;
+ std::unique_ptr<ProofVerifyContext> CreateDefaultContext() override;
+
+ protected:
+ virtual bool IsKeyTypeAllowedByPolicy(const CertificateView& certificate);
+
+ private:
+ bool HasKnownFingerprint(absl::string_view der_certificate);
+ bool HasValidExpiry(const CertificateView& certificate);
+ bool IsWithinValidityPeriod(const CertificateView& certificate);
+
+ const QuicClock* clock_; // Unowned.
+ const int max_validity_days_;
+ const QuicTime::Delta max_validity_;
+ std::vector<WebTransportHash> hashes_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_WEB_TRANSPORT_FINGERPRINT_PROOF_VERIFIER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/web_transport_fingerprint_proof_verifier_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/web_transport_fingerprint_proof_verifier_test.cc
new file mode 100644
index 00000000000..11c769d76ec
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/crypto/web_transport_fingerprint_proof_verifier_test.cc
@@ -0,0 +1,183 @@
+// 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 "quiche/quic/core/crypto/web_transport_fingerprint_proof_verifier.h"
+
+#include <memory>
+
+#include "absl/strings/escaping.h"
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/quic_types.h"
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/mock_clock.h"
+#include "quiche/quic/test_tools/test_certificates.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+using ::testing::HasSubstr;
+
+// 2020-02-01 12:35:56 UTC
+constexpr QuicTime::Delta kValidTime = QuicTime::Delta::FromSeconds(1580560556);
+
+struct VerifyResult {
+ QuicAsyncStatus status;
+ WebTransportFingerprintProofVerifier::Status detailed_status;
+ std::string error;
+};
+
+class WebTransportFingerprintProofVerifierTest : public QuicTest {
+ public:
+ WebTransportFingerprintProofVerifierTest() {
+ clock_.AdvanceTime(kValidTime);
+ verifier_ = std::make_unique<WebTransportFingerprintProofVerifier>(
+ &clock_, /*max_validity_days=*/365);
+ AddTestCertificate();
+ }
+
+ protected:
+ VerifyResult Verify(absl::string_view certificate) {
+ VerifyResult result;
+ std::unique_ptr<ProofVerifyDetails> details;
+ uint8_t tls_alert;
+ result.status = verifier_->VerifyCertChain(
+ /*hostname=*/"", /*port=*/0,
+ std::vector<std::string>{std::string(certificate)},
+ /*ocsp_response=*/"",
+ /*cert_sct=*/"",
+ /*context=*/nullptr, &result.error, &details, &tls_alert,
+ /*callback=*/nullptr);
+ result.detailed_status =
+ static_cast<WebTransportFingerprintProofVerifier::Details*>(
+ details.get())
+ ->status();
+ return result;
+ }
+
+ void AddTestCertificate() {
+ EXPECT_TRUE(verifier_->AddFingerprint(WebTransportHash{
+ WebTransportHash::kSha256, RawSha256(kTestCertificate)}));
+ }
+
+ MockClock clock_;
+ std::unique_ptr<WebTransportFingerprintProofVerifier> verifier_;
+};
+
+TEST_F(WebTransportFingerprintProofVerifierTest, Sha256Fingerprint) {
+ // Computed using `openssl x509 -fingerprint -sha256`.
+ EXPECT_EQ(absl::BytesToHexString(RawSha256(kTestCertificate)),
+ "f2e5465e2bf7ecd6f63066a5a37511734aa0eb7c4701"
+ "0e86d6758ed4f4fa1b0f");
+}
+
+TEST_F(WebTransportFingerprintProofVerifierTest, SimpleFingerprint) {
+ VerifyResult result = Verify(kTestCertificate);
+ EXPECT_EQ(result.status, QUIC_SUCCESS);
+ EXPECT_EQ(result.detailed_status,
+ WebTransportFingerprintProofVerifier::Status::kValidCertificate);
+
+ result = Verify(kWildcardCertificate);
+ EXPECT_EQ(result.status, QUIC_FAILURE);
+ EXPECT_EQ(result.detailed_status,
+ WebTransportFingerprintProofVerifier::Status::kUnknownFingerprint);
+
+ result = Verify("Some random text");
+ EXPECT_EQ(result.status, QUIC_FAILURE);
+}
+
+TEST_F(WebTransportFingerprintProofVerifierTest, Validity) {
+ // Validity periods of kTestCertificate, according to `openssl x509 -text`:
+ // Not Before: Jan 30 18:13:59 2020 GMT
+ // Not After : Feb 2 18:13:59 2020 GMT
+
+ // 2020-01-29 19:00:00 UTC
+ constexpr QuicTime::Delta kStartTime =
+ QuicTime::Delta::FromSeconds(1580324400);
+ clock_.Reset();
+ clock_.AdvanceTime(kStartTime);
+
+ VerifyResult result = Verify(kTestCertificate);
+ EXPECT_EQ(result.status, QUIC_FAILURE);
+ EXPECT_EQ(result.detailed_status,
+ WebTransportFingerprintProofVerifier::Status::kExpired);
+
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(86400));
+ result = Verify(kTestCertificate);
+ EXPECT_EQ(result.status, QUIC_SUCCESS);
+ EXPECT_EQ(result.detailed_status,
+ WebTransportFingerprintProofVerifier::Status::kValidCertificate);
+
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(4 * 86400));
+ result = Verify(kTestCertificate);
+ EXPECT_EQ(result.status, QUIC_FAILURE);
+ EXPECT_EQ(result.detailed_status,
+ WebTransportFingerprintProofVerifier::Status::kExpired);
+}
+
+TEST_F(WebTransportFingerprintProofVerifierTest, MaxValidity) {
+ verifier_ = std::make_unique<WebTransportFingerprintProofVerifier>(
+ &clock_, /*max_validity_days=*/2);
+ AddTestCertificate();
+ VerifyResult result = Verify(kTestCertificate);
+ EXPECT_EQ(result.status, QUIC_FAILURE);
+ EXPECT_EQ(result.detailed_status,
+ WebTransportFingerprintProofVerifier::Status::kExpiryTooLong);
+ EXPECT_THAT(result.error, HasSubstr("limit of 2 days"));
+
+ // kTestCertificate is valid for exactly four days.
+ verifier_ = std::make_unique<WebTransportFingerprintProofVerifier>(
+ &clock_, /*max_validity_days=*/4);
+ AddTestCertificate();
+ result = Verify(kTestCertificate);
+ EXPECT_EQ(result.status, QUIC_SUCCESS);
+ EXPECT_EQ(result.detailed_status,
+ WebTransportFingerprintProofVerifier::Status::kValidCertificate);
+}
+
+TEST_F(WebTransportFingerprintProofVerifierTest, InvalidCertificate) {
+ constexpr absl::string_view kInvalidCertificate = "Hello, world!";
+ ASSERT_TRUE(verifier_->AddFingerprint(WebTransportHash{
+ WebTransportHash::kSha256, RawSha256(kInvalidCertificate)}));
+
+ VerifyResult result = Verify(kInvalidCertificate);
+ EXPECT_EQ(result.status, QUIC_FAILURE);
+ EXPECT_EQ(
+ result.detailed_status,
+ WebTransportFingerprintProofVerifier::Status::kCertificateParseFailure);
+}
+
+TEST_F(WebTransportFingerprintProofVerifierTest, AddCertificate) {
+ // Accept all-uppercase fingerprints.
+ verifier_ = std::make_unique<WebTransportFingerprintProofVerifier>(
+ &clock_, /*max_validity_days=*/365);
+ EXPECT_TRUE(verifier_->AddFingerprint(CertificateFingerprint{
+ CertificateFingerprint::kSha256,
+ "F2:E5:46:5E:2B:F7:EC:D6:F6:30:66:A5:A3:75:11:73:4A:A0:EB:"
+ "7C:47:01:0E:86:D6:75:8E:D4:F4:FA:1B:0F"}));
+ EXPECT_EQ(Verify(kTestCertificate).detailed_status,
+ WebTransportFingerprintProofVerifier::Status::kValidCertificate);
+
+ // Reject unknown hash algorithms.
+ EXPECT_FALSE(verifier_->AddFingerprint(CertificateFingerprint{
+ "sha-1", "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00"}));
+ // Reject invalid length.
+ EXPECT_FALSE(verifier_->AddFingerprint(
+ CertificateFingerprint{CertificateFingerprint::kSha256, "00:00:00:00"}));
+ // Reject missing colons.
+ EXPECT_FALSE(verifier_->AddFingerprint(CertificateFingerprint{
+ CertificateFingerprint::kSha256,
+ "00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00."
+ "00.00.00.00.00.00.00.00.00.00.00.00.00"}));
+ // Reject non-hex symbols.
+ EXPECT_FALSE(verifier_->AddFingerprint(CertificateFingerprint{
+ CertificateFingerprint::kSha256,
+ "zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:"
+ "zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz"}));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic