1
#include "source/common/quic/envoy_quic_proof_source.h"
2

            
3
#include <openssl/bio.h>
4

            
5
#include "envoy/ssl/tls_certificate_config.h"
6

            
7
#include "source/common/quic/envoy_quic_utils.h"
8
#include "source/common/quic/quic_io_handle_wrapper.h"
9
#include "source/common/stream_info/stream_info_impl.h"
10

            
11
#include "openssl/bytestring.h"
12
#include "quiche/quic/core/crypto/certificate_view.h"
13

            
14
namespace Envoy {
15
namespace Quic {
16

            
17
quiche::QuicheReferenceCountedPointer<quic::ProofSource::Chain>
18
EnvoyQuicProofSource::GetCertChain(const quic::QuicSocketAddress& server_address,
19
                                   const quic::QuicSocketAddress& client_address,
20
2916
                                   const std::string& hostname, bool* cert_matched_sni) {
21

            
22
  // Ensure this is set even in error paths.
23
2916
  *cert_matched_sni = false;
24

            
25
2916
  auto res = getTransportSocketAndFilterChain(server_address, client_address, hostname);
26
2916
  if (!res.has_value()) {
27
1
    return nullptr;
28
1
  }
29

            
30
2915
  return getTlsCertAndFilterChain(*res, hostname, cert_matched_sni).cert_;
31
2916
}
32

            
33
void EnvoyQuicProofSource::signPayload(
34
    const quic::QuicSocketAddress& server_address, const quic::QuicSocketAddress& client_address,
35
    const std::string& hostname, uint16_t signature_algorithm, absl::string_view in,
36
1814
    std::unique_ptr<quic::ProofSource::SignatureCallback> callback) {
37
1814
  auto data = getTransportSocketAndFilterChain(server_address, client_address, hostname);
38
1814
  if (!data.has_value()) {
39
1
    ENVOY_LOG(warn, "No matching filter chain found for handshake.");
40
1
    callback->Run(false, "", nullptr);
41
1
    return;
42
1
  }
43

            
44
1813
  CertWithFilterChain res =
45
1813
      getTlsCertAndFilterChain(*data, hostname, nullptr /* cert_matched_sni */);
46
1813
  if (res.private_key_ == nullptr) {
47
    ENVOY_LOG(warn, "No matching filter chain found for handshake.");
48
    callback->Run(false, "", nullptr);
49
    return;
50
  }
51

            
52
  // Verify the signature algorithm is as expected.
53
1813
  std::string error_details;
54
1813
  int sign_alg =
55
1813
      deduceSignatureAlgorithmFromPublicKey(res.private_key_->private_key(), &error_details);
56
1813
  if (sign_alg != signature_algorithm) {
57
    ENVOY_LOG(warn,
58
              fmt::format("The signature algorithm {} from the private key is not expected: {}",
59
                          sign_alg, error_details));
60
    callback->Run(false, "", nullptr);
61
    return;
62
  }
63

            
64
  // Sign.
65
1813
  std::string sig = res.private_key_->Sign(in, signature_algorithm);
66
1813
  bool success = !sig.empty();
67
1813
  ASSERT(res.filter_chain_.has_value());
68
1813
  callback->Run(success, sig,
69
1813
                std::make_unique<EnvoyQuicProofSourceDetails>(res.filter_chain_.value().get()));
70
1813
}
71

            
72
EnvoyQuicProofSource::CertWithFilterChain
73
EnvoyQuicProofSource::getTlsCertAndFilterChain(const TransportSocketFactoryWithFilterChain& data,
74
                                               const std::string& hostname,
75
4728
                                               bool* cert_matched_sni) {
76
4728
  auto [cert, key] =
77
4728
      data.transport_socket_factory_.getTlsCertificateAndKey(hostname, cert_matched_sni);
78
4728
  if (cert == nullptr || key == nullptr) {
79
3
    ENVOY_LOG(warn, "No certificate is configured in transport socket config.");
80
3
    return {};
81
3
  }
82
4725
  return {std::move(cert), std::move(key), data.filter_chain_};
83
4728
}
84

            
85
absl::optional<EnvoyQuicProofSource::TransportSocketFactoryWithFilterChain>
86
EnvoyQuicProofSource::getTransportSocketAndFilterChain(
87
    const quic::QuicSocketAddress& server_address, const quic::QuicSocketAddress& client_address,
88
4730
    const std::string& hostname) {
89
4730
  ENVOY_LOG(trace, "Getting cert chain for {}", hostname);
90
  // TODO(danzh) modify QUICHE to make quic session or ALPN accessible to avoid hard-coded ALPN.
91
4730
  Network::ConnectionSocketPtr connection_socket = createServerConnectionSocket(
92
4730
      listen_socket_.ioHandle(), server_address, client_address, hostname, "h3");
93
4730
  StreamInfo::StreamInfoImpl info(time_source_,
94
4730
                                  connection_socket->connectionInfoProviderSharedPtr(),
95
4730
                                  StreamInfo::FilterState::LifeSpan::Connection);
96
4730
  const Network::FilterChain* filter_chain =
97
4730
      filter_chain_manager_->findFilterChain(*connection_socket, info);
98

            
99
4730
  if (filter_chain == nullptr) {
100
2
    listener_stats_.no_filter_chain_match_.inc();
101
2
    ENVOY_LOG(warn, "No matching filter chain found for handshake.");
102
2
    return {};
103
2
  }
104
4728
  ENVOY_LOG(trace, "Got a matching cert chain {}", filter_chain->name());
105

            
106
4728
  auto& transport_socket_factory =
107
4728
      dynamic_cast<const QuicServerTransportSocketFactory&>(filter_chain->transportSocketFactory());
108
4728
  return TransportSocketFactoryWithFilterChain{transport_socket_factory, *filter_chain};
109
4730
}
110

            
111
void EnvoyQuicProofSource::updateFilterChainManager(
112
6
    Network::FilterChainManager& filter_chain_manager) {
113
6
  filter_chain_manager_ = &filter_chain_manager;
114
6
}
115

            
116
2192
void EnvoyQuicProofSource::OnNewSslCtx(SSL_CTX* ssl_ctx) { registerCertCompression(ssl_ctx); }
117

            
118
} // namespace Quic
119
} // namespace Envoy