• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 25139258422

29 Apr 2026 08:02PM UTC coverage: 89.37% (-0.02%) from 89.385%
25139258422

push

github

web-flow
Merge pull request #5550 from randombit/jack/tls-misc

TLS conformance, hardening, and performance fixes

107055 of 119789 relevant lines covered (89.37%)

11415549.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.35
/src/lib/tls/tls13/msg_encrypted_extensions.cpp
1
/*
2
* TLS Hello Request and Client Hello Messages
3
* (C) 2022 Jack Lloyd
4
*     2022 René Meusel, Hannes Rantzsch - neXenio GmbH
5
*
6
* Botan is released under the Simplified BSD License (see license.txt)
7
*/
8

9
#include <botan/tls_messages_13.h>
10

11
#include <botan/tls_callbacks.h>
12
#include <botan/tls_exceptn.h>
13
#include <botan/tls_policy.h>
14
#include <botan/internal/stl_util.h>
15
#include <botan/internal/tls_reader.h>
16

17
namespace Botan::TLS {
18

19
Encrypted_Extensions::Encrypted_Extensions(const Client_Hello_13& client_hello,
390✔
20
                                           const Policy& policy,
21
                                           Callbacks& cb,
22
                                           bool is_resumption,
23
                                           bool requesting_client_auth) {
390✔
24
   const auto& exts = client_hello.extensions();
390✔
25

26
   // NOLINTBEGIN(*-owning-memory)
27

28
   // RFC 8446 4.2.7
29
   //    As of TLS 1.3, servers are permitted to send the "supported_groups"
30
   //    extension to the client.  Clients [...] MAY use the information
31
   //    learned from a successfully completed handshake to change what groups
32
   //    they use in their "key_share" extension in subsequent connections.
33
   if(exts.has<Supported_Groups>()) {
390✔
34
      m_extensions.add(new Supported_Groups(policy.key_exchange_groups()));
780✔
35
   }
36

37
   const auto record_size_limit = policy.record_size_limit();
390✔
38
   const auto max_record_size = MAX_PLAINTEXT_SIZE + 1 /* encrypted content type byte */;
390✔
39
   if(exts.has<Record_Size_Limit>()) {
390✔
40
      // RFC 8449 4
41
      //    Endpoints SHOULD advertise the "record_size_limit" extension, even
42
      //    if they have no need to limit the size of records. [...]  For
43
      //    servers, this allows clients to know that their limit will be
44
      //    respected.
45
      m_extensions.add(new Record_Size_Limit(record_size_limit.value_or(max_record_size)));
15✔
46
   } else if(record_size_limit.has_value() && record_size_limit.value() < max_record_size) {
382✔
47
      // RFC 8449 4
48
      //    Endpoints SHOULD advertise the "record_size_limit" extension, even if
49
      //    they have no need to limit the size of records. For clients, this
50
      //    allows servers to advertise a limit at their discretion.
51
      throw TLS_Exception(Alert::MissingExtension,
×
52
                          "Server cannot enforce record size limit without the client supporting it");
×
53
   }
54

55
   // RFC 7250 4.2
56
   //    If the TLS server wants to request a certificate from the client
57
   //    (via the certificate_request message), it MUST include the
58
   //    client_certificate_type extension in the server hello.
59
   //    [...]
60
   //    If the server does not send a certificate_request payload [...],
61
   //    then the client_certificate_type payload in the server hello MUST be
62
   //    omitted.
63
   //
64
   // Note: requesting_client_auth tracks whether the caller will actually
65
   // emit a CertificateRequest. The server-side decision in
66
   // Certificate_Request_13::maybe_create depends on both the policy flag
67
   // *and* the credentials manager's CA list, so re-checking just the
68
   // policy flag here would miss the trusted-CAs-only configuration.
69
   if(auto* ch_client_cert_types = exts.get<Client_Certificate_Type>();
390✔
70
      ch_client_cert_types != nullptr && requesting_client_auth) {
390✔
71
      m_extensions.add(new Client_Certificate_Type(*ch_client_cert_types, policy));
9✔
72
   }
73

74
   // RFC 7250 4.2
75
   //    The server_certificate_type extension in the client hello indicates the
76
   //    types of certificates the client is able to process when provided by
77
   //    the server in a subsequent certificate payload. [...] With the
78
   //    server_certificate_type extension in the server hello, the TLS server
79
   //    indicates the certificate type carried in the Certificate payload.
80
   if(auto* ch_server_cert_types = exts.get<Server_Certificate_Type>()) {
390✔
81
      m_extensions.add(new Server_Certificate_Type(*ch_server_cert_types, policy));
1✔
82
   }
83

84
   // RFC 6066 3
85
   //    A server that receives a client hello containing the "server_name"
86
   //    extension [...] SHALL include an extension of type "server_name" in the
87
   //    (extended) server hello. The "extension_data" field of this extension
88
   //    SHALL be empty.
89
   //
90
   //    When resuming a session, the server MUST NOT include a server_name
91
   //    extension in the server hello.
92
   if(exts.has<Server_Name_Indicator>() && !is_resumption) {
390✔
93
      m_extensions.add(new Server_Name_Indicator(""));
289✔
94
   }
95

96
   if(auto* alpn_ext = exts.get<Application_Layer_Protocol_Notification>()) {
390✔
97
      const auto& offered = alpn_ext->protocols();
7✔
98
      const auto next_protocol = cb.tls_server_choose_app_protocol(offered);
7✔
99
      if(!next_protocol.empty()) {
6✔
100
         // RFC 7301 3.2: if a protocol is selected, the server MUST select
101
         // one of the protocols advertised by the client.
102
         if(!value_exists(offered, next_protocol)) {
8✔
103
            throw TLS_Exception(Alert::InternalError,
×
104
                                "Application chose an ALPN protocol that the client did not offer");
×
105
         }
106
         m_extensions.add(new Application_Layer_Protocol_Notification(next_protocol));
4✔
107
      }
108
   }
6✔
109

110
   // NOLINTEND(*-owning-memory)
111

112
   // TODO: Implement handling for (at least)
113
   //       * SRTP
114

115
   cb.tls_modify_extensions(m_extensions, Connection_Side::Server, type());
389✔
116

117
   // After the application's tls_modify_extensions callback runs, re-check the
118
   // RFC-MUST invariants we just established above. The application can add or
119
   // reorder extensions, but shouldn't remove ones required direct response to
120
   // ClientHello extensions would put us out of spec.
121
   if(exts.has<Server_Certificate_Type>() && !m_extensions.has<Server_Certificate_Type>()) {
389✔
122
      throw TLS_Exception(
×
123
         Alert::InternalError,
124
         "Application tls_modify_extensions callback removed Server_Certificate_Type from EncryptedExtensions");
×
125
   }
126

127
   if(requesting_client_auth && exts.has<Client_Certificate_Type>() && !m_extensions.has<Client_Certificate_Type>()) {
493✔
128
      throw TLS_Exception(
×
129
         Alert::InternalError,
130
         "Application tls_modify_extensions callback removed Client_Certificate_Type from EncryptedExtensions");
×
131
   }
132
}
390✔
133

134
Encrypted_Extensions::Encrypted_Extensions(const std::vector<uint8_t>& buf) {
486✔
135
   TLS_Data_Reader reader("encrypted extensions reader", buf);
486✔
136

137
   // Encrypted Extensions contains a list of extensions. This list may legally
138
   // be empty. However, in that case we should at least see a two-byte length
139
   // field that reads 0x00 0x00.
140
   if(buf.size() < 2) {
486✔
141
      throw TLS_Exception(Alert::DecodeError, "Server sent an empty Encrypted Extensions message");
1✔
142
   }
143

144
   m_extensions.deserialize(reader, Connection_Side::Server, type());
485✔
145

146
   // RFC 8446 4.2
147
   //    If an implementation receives an extension which it recognizes and
148
   //    which is not specified for the message in which it appears, it MUST
149
   //    abort the handshake with an "illegal_parameter" alert.
150
   //
151
   // Note that we cannot encounter any extensions that we don't recognize here,
152
   // since only extensions we previously offered are allowed in EE.
153
   const auto allowed_exts = std::set<Extension_Code>{
481✔
154
      // Allowed extensions listed in RFC 8446 and implemented in Botan
155
      Extension_Code::ServerNameIndication,
156
      // MAX_FRAGMENT_LENGTH
157
      Extension_Code::SupportedGroups,
158
      Extension_Code::UseSrtp,
159
      // HEARTBEAT
160
      Extension_Code::ApplicationLayerProtocolNegotiation,
161
      // RFC 7250
162
      Extension_Code::ClientCertificateType,
163
      Extension_Code::ServerCertificateType,
164
      // EARLY_DATA
165

166
      // Allowed extensions not listed in RFC 8446 but acceptable as Botan implements them
167
      Extension_Code::RecordSizeLimit,
168
   };
481✔
169
   if(m_extensions.contains_implemented_extensions_other_than(allowed_exts)) {
481✔
170
      throw TLS_Exception(Alert::IllegalParameter, "Encrypted Extensions contained an extension that is not allowed");
5✔
171
   }
172

173
   reader.assert_done();
476✔
174
}
486✔
175

176
std::vector<uint8_t> Encrypted_Extensions::serialize() const {
402✔
177
   // RFC 8446 4.3.1: EncryptedExtensions carries Extension extensions<0..2^16-1>;
178
   // an empty list still requires a 2-byte length-prefix on the wire.
179
   // Extensions::serialize collapses empty to {} to suit other contexts, so
180
   // emit the explicit length here. Mirrors the same fallback in
181
   // New_Session_Ticket_13::serialize.
182
   if(m_extensions.empty()) {
402✔
183
      return {0x00, 0x00};
×
184
   }
185
   return m_extensions.serialize(Connection_Side::Server);
402✔
186
}
187

188
}  // namespace Botan::TLS
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc