Coverage Report

Created: 2024-09-19 09:45

/proc/self/cwd/source/common/quic/cert_compression.cc
Line
Count
Source (jump to first uncovered line)
1
#include "source/common/quic/cert_compression.h"
2
3
#include "source/common/common/assert.h"
4
#include "source/common/runtime/runtime_features.h"
5
6
#include "openssl/tls1.h"
7
8
#define ZLIB_CONST
9
#include "zlib.h"
10
11
namespace Envoy {
12
namespace Quic {
13
14
namespace {
15
16
class ScopedZStream {
17
public:
18
  using CleanupFunc = int (*)(z_stream*);
19
20
0
  ScopedZStream(z_stream& z, CleanupFunc cleanup) : z_(z), cleanup_(cleanup) {}
21
0
  ~ScopedZStream() { cleanup_(&z_); }
22
23
private:
24
  z_stream& z_;
25
  CleanupFunc cleanup_;
26
};
27
28
} // namespace
29
30
0
void CertCompression::registerSslContext(SSL_CTX* ssl_ctx) {
31
0
  if (Runtime::runtimeFeatureEnabled(
32
0
          "envoy.reloadable_features.quic_support_certificate_compression")) {
33
0
    auto ret = SSL_CTX_add_cert_compression_alg(ssl_ctx, TLSEXT_cert_compression_zlib, compressZlib,
34
0
                                                decompressZlib);
35
0
    ASSERT(ret == 1);
36
0
  }
37
0
}
38
39
0
int CertCompression::compressZlib(SSL*, CBB* out, const uint8_t* in, size_t in_len) {
40
41
0
  z_stream z = {};
42
0
  int rv = deflateInit(&z, Z_DEFAULT_COMPRESSION);
43
0
  if (rv != Z_OK) {
44
0
    IS_ENVOY_BUG(fmt::format("Cert compression failure in deflateInit: {}", rv));
45
0
    return FAILURE;
46
0
  }
47
48
0
  ScopedZStream deleter(z, deflateEnd);
49
50
0
  const auto upper_bound = deflateBound(&z, in_len);
51
52
0
  uint8_t* out_buf = nullptr;
53
0
  if (!CBB_reserve(out, &out_buf, upper_bound)) {
54
0
    IS_ENVOY_BUG(fmt::format("Cert compression failure in allocating output CBB buffer of size {}",
55
0
                             upper_bound));
56
0
    return FAILURE;
57
0
  }
58
59
0
  z.next_in = in;
60
0
  z.avail_in = in_len;
61
0
  z.next_out = out_buf;
62
0
  z.avail_out = upper_bound;
63
64
0
  rv = deflate(&z, Z_FINISH);
65
0
  if (rv != Z_STREAM_END) {
66
0
    IS_ENVOY_BUG(fmt::format(
67
0
        "Cert compression failure in deflate: {}, z.total_out {}, in_len {}, z.avail_in {}", rv,
68
0
        z.avail_in, in_len, z.avail_in));
69
0
    return FAILURE;
70
0
  }
71
72
0
  if (!CBB_did_write(out, z.total_out)) {
73
0
    IS_ENVOY_BUG("CBB_did_write failed");
74
0
    return FAILURE;
75
0
  }
76
77
0
  ENVOY_LOG(trace, "Cert compression successful");
78
79
0
  return SUCCESS;
80
0
}
81
82
int CertCompression::decompressZlib(SSL*, CRYPTO_BUFFER** out, size_t uncompressed_len,
83
0
                                    const uint8_t* in, size_t in_len) {
84
0
  z_stream z = {};
85
0
  int rv = inflateInit(&z);
86
0
  if (rv != Z_OK) {
87
0
    IS_ENVOY_BUG(fmt::format("Cert decompression failure in inflateInit: {}", rv));
88
0
    return FAILURE;
89
0
  }
90
91
0
  ScopedZStream deleter(z, inflateEnd);
92
93
0
  z.next_in = in;
94
0
  z.avail_in = in_len;
95
0
  bssl::UniquePtr<CRYPTO_BUFFER> decompressed_data(
96
0
      CRYPTO_BUFFER_alloc(&z.next_out, uncompressed_len));
97
0
  z.avail_out = uncompressed_len;
98
99
0
  rv = inflate(&z, Z_FINISH);
100
0
  if (rv != Z_STREAM_END) {
101
0
    ENVOY_LOG_PERIODIC(error, std::chrono::seconds(10),
102
0
                       "Cert decompression failure in inflate, possibly caused by invalid "
103
0
                       "compressed cert from peer: {}, z.total_out {}, uncompressed_len {}",
104
0
                       rv, z.total_out, uncompressed_len);
105
0
    return FAILURE;
106
0
  }
107
108
0
  if (z.total_out != uncompressed_len) {
109
0
    ENVOY_LOG_PERIODIC(error, std::chrono::seconds(10),
110
0
                       "Decompression length did not match peer provided uncompressed length, "
111
0
                       "caused by either invalid peer handshake data or decompression error.");
112
0
    return FAILURE;
113
0
  }
114
115
0
  ENVOY_LOG(trace, "Cert decompression successful");
116
117
0
  *out = decompressed_data.release();
118
0
  return SUCCESS;
119
0
}
120
121
} // namespace Quic
122
} // namespace Envoy