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

randombit / botan / 5575778031

17 Jul 2023 12:44PM UTC coverage: 91.643% (-0.1%) from 91.744%
5575778031

Pull #3609

github

web-flow
Merge 3f7c9c42d into e430d3e4d
Pull Request #3609: [TLS 1.3] Hybrid PQ/T key establishment

78672 of 85846 relevant lines covered (91.64%)

12332050.04 hits per line

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

87.64
/src/bogo_shim/bogo_shim.cpp
1
/*
2
* (C) 2019 Jack Lloyd
3
*
4
* Botan is released under the Simplified BSD License (see license.txt)
5
*/
6

7
/*
8
* This is a shim for testing Botan against BoringSSL's test TLS stack (BoGo).
9
*
10
* Instructions on use should go here.
11
*/
12

13
#include <botan/base64.h>
14
#include <botan/chacha_rng.h>
15
#include <botan/data_src.h>
16
#include <botan/hex.h>
17
#include <botan/mem_ops.h>
18
#include <botan/ocsp.h>
19
#include <botan/pkcs8.h>
20
#include <botan/tls_algos.h>
21
#include <botan/tls_client.h>
22
#include <botan/tls_exceptn.h>
23
#include <botan/tls_messages.h>
24
#include <botan/tls_server.h>
25
#include <botan/tls_session_manager_hybrid.h>
26
#include <botan/tls_session_manager_memory.h>
27
#include <botan/internal/fmt.h>
28
#include <botan/internal/loadstor.h>
29
#include <botan/internal/parsing.h>
30
#include <botan/internal/stl_util.h>
31

32
#include <ctime>
33
#include <iostream>
34
#include <map>
35
#include <memory>
36
#include <set>
37
#include <string>
38
#include <unordered_map>
39
#include <vector>
40

41
#if defined(BOTAN_TARGET_OS_HAS_SOCKETS)
42
   #include <errno.h>
43
   #include <fcntl.h>
44
   #include <netdb.h>
45
   #include <netinet/in.h>
46
   #include <string.h>
47
   #include <sys/socket.h>
48
   #include <sys/time.h>
49
   #include <unistd.h>
50
#endif
51

52
namespace {
53

54
int shim_output(const std::string& s, int rc = 0) {
1✔
55
   std::cout << s << "\n";
1✔
56
   return rc;
1✔
57
}
58

59
void shim_log(const std::string& s) {
106,265✔
60
   if(::getenv("BOTAN_BOGO_SHIM_LOG")) {
106,265✔
61
      /*
62
      FIXMEs:
63
       - Rewrite this to use a std::ostream instead
64
       - Allow using the env variable to point to where the log is written
65
       - Avoid rechecking the env variable with each call (!)
66
      */
67

68
      // NOLINTNEXTLINE(*-avoid-non-const-global-variables)
69
      static FILE* g_log = std::fopen("/tmp/bogo_shim.log", "w");
×
70
      struct timeval tv;
×
71
      ::gettimeofday(&tv, nullptr);
×
72
      static_cast<void>(std::fprintf(g_log,
×
73
                                     "%lld.%lu: %s\n",
74
                                     static_cast<unsigned long long>(tv.tv_sec),
×
75
                                     static_cast<unsigned long>(tv.tv_usec),
×
76
                                     s.c_str()));
77
      static_cast<void>(std::fflush(g_log));
×
78
   }
79
}
106,265✔
80

81
[[noreturn]] void shim_exit_with_error(const std::string& s, int rc = 1) {
752✔
82
   shim_log("Exiting with " + s);
752✔
83
   std::cerr << s << "\n";
752✔
84
   std::exit(rc);
752✔
85
}
86

87
std::string map_to_bogo_error(const std::string& e) {
732✔
88
   shim_log("Original error " + e);
732✔
89

90
   static const std::unordered_map<std::string, std::string> err_map{
732✔
91
      {"Application data before handshake done", ":APPLICATION_DATA_INSTEAD_OF_HANDSHAKE:"},
92
      {"Bad Hello_Request, has non-zero size", ":BAD_HELLO_REQUEST:"},
93
      {"Bad code for TLS alert level", ":UNKNOWN_ALERT_TYPE:"},
94
      {"Bad encoding on signature algorithms extension", ":DECODE_ERROR:"},
95
      {"Bad extension size", ":DECODE_ERROR:"},
96
      {"Bad length in hello verify request", ":DECODE_ERROR:"},
97
      {"Bad lengths in DTLS header", ":BAD_HANDSHAKE_RECORD:"},
98
      {"Bad signature on server key exchange", ":BAD_SIGNATURE:"},
99
      {"Server certificate verification failed", ":BAD_SIGNATURE:"},
100
      {"compression is not supported in TLS 1.3", ":DECODE_ERROR:"},
101
      {"Cookie length must be at least 1 byte", ":DECODE_ERROR:"},
102
      {"Bad size (1) for TLS alert message", ":BAD_ALERT:"},
103
      {"Bad size (4) for TLS alert message", ":BAD_ALERT:"},
104
      {"CERTIFICATE decoding failed with PEM: No PEM header found", ":CANNOT_PARSE_LEAF_CERT:"},
105
      {"Certificate usage constraints do not allow signing", ":KEY_USAGE_BIT_INCORRECT:"},
106
      {"Can't agree on a ciphersuite with client", ":NO_SHARED_CIPHER:"},
107
      {"Can't interleave application and handshake data", ":UNEXPECTED_RECORD:"},
108
      {"Certificate chain exceeds policy specified maximum size", ":EXCESSIVE_MESSAGE_SIZE:"},
109
      {"Certificate key type did not match ciphersuite", ":WRONG_CERTIFICATE_TYPE:"},
110
      {"Certificate usage constraints do not allow this ciphersuite", ":KEY_USAGE_BIT_INCORRECT:"},
111
      {"Certificate: Message malformed", ":DECODE_ERROR:"},
112
      {"Certificate_Request context must be empty in the main handshake", ":DECODE_ERROR:"},
113
      {"Certificate_Request message did not provide a signature_algorithms extension", ":DECODE_ERROR:"},
114
      {"Channel_Impl_12::key_material_export cannot export during renegotiation", "failed to export keying material"},
115
      {"Client cert verify failed", ":BAD_SIGNATURE:"},
116
      {"Client certificate does not support signing", ":KEY_USAGE_BIT_INCORRECT:"},
117
      {"Client certificate verification failed", ":BAD_SIGNATURE:"},
118
      {"Client did not comply with the requested key exchange group", ":WRONG_CURVE:"},
119
      {"Client did not offer NULL compression", ":INVALID_COMPRESSION_LIST:"},
120
      {"Client did not comply with the requested key exchange group", ":WRONG_CURVE:"},
121
      {"Client Hello must either contain both key_share and supported_groups extensions or neither",
122
       ":MISSING_KEY_SHARE:"},
123
      {"Client Hello offered a PSK without a psk_key_exchange_modes extension", ":MISSING_EXTENSION:"},
124
      {"Client offered DTLS version with major version 0xFF", ":UNSUPPORTED_PROTOCOL:"},
125
      {"Client offered SSLv3 which is not supported", ":UNSUPPORTED_PROTOCOL:"},
126
      {"Client offered TLS version with major version under 3", ":UNSUPPORTED_PROTOCOL:"},
127
      {"Expected server hello of (D)TLS 1.2 or lower", ":UNSUPPORTED_PROTOCOL:"},
128
      {"Protocol version was not offered", ":UNSUPPORTED_PROTOCOL:"},
129
      {"Client policy prohibits insecure renegotiation", ":RENEGOTIATION_MISMATCH:"},
130
      {"Client policy prohibits renegotiation", ":NO_RENEGOTIATION:"},
131
      {"Client resumed extended ms session without sending extension", ":RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION:"},
132
      {"Client sent plaintext HTTP proxy CONNECT request instead of TLS handshake", ":HTTPS_PROXY_REQUEST:"},
133
      {"Client sent plaintext HTTP request instead of TLS handshake", ":HTTP_REQUEST:"},
134
      {"Client signalled fallback SCSV, possible attack", ":INAPPROPRIATE_FALLBACK:"},
135
      {"Client version TLS v1.1 is unacceptable by policy", ":UNSUPPORTED_PROTOCOL:"},
136
      {"No shared TLS version based on supported versions extension", ":UNSUPPORTED_PROTOCOL:"},
137
      {"Client: No certificates sent by server", ":DECODE_ERROR:"},
138
      {"Non-PSK Client Hello did not contain supported_groups and signature_algorithms extensions",
139
       ":NO_SHARED_GROUP:"},
140
      {"No certificates sent by server", ":PEER_DID_NOT_RETURN_A_CERTIFICATE:"},
141
      {"Not enough data to read another KeyShareEntry", ":DECODE_ERROR:"},
142
      {"Not enough PSK binders", ":PSK_IDENTITY_BINDER_COUNT_MISMATCH:"},
143
      {"Counterparty sent inconsistent key and sig types", ":WRONG_SIGNATURE_TYPE:"},
144
      {"Downgrade attack detected", ":TLS13_DOWNGRADE:"},
145
      {"Empty ALPN protocol not allowed", ":PARSE_TLSEXT:"},
146
      {"Empty PSK binders list", ":DECODE_ERROR: "},
147
      {"Encoding error: Cannot encode PSS string, output length too small", ":NO_COMMON_SIGNATURE_ALGORITHMS:"},
148
      {"Expected TLS but got a record with DTLS version", ":WRONG_VERSION_NUMBER:"},
149
      {"Extension removed in updated Client Hello", ":INCONSISTENT_CLIENT_HELLO:"},
150
      {"Failed to agree on a signature algorithm", ":NO_COMMON_SIGNATURE_ALGORITHMS:"},
151
      {"Failed to agree on any signature algorithm", ":NO_COMMON_SIGNATURE_ALGORITHMS:"},
152
      {"Failed to negotiate a common signature algorithm for client authentication",
153
       ":NO_COMMON_SIGNATURE_ALGORITHMS:"},
154
      {"PSK extension was not at the very end of the Client Hello", ":PRE_SHARED_KEY_MUST_BE_LAST:"},
155
      {"Finished message didn't verify", ":DIGEST_CHECK_FAILED:"},
156
      {"Have data remaining in buffer after ClientHello", ":EXCESS_HANDSHAKE_DATA:"},
157
      {"Have data remaining in buffer after Finished", ":EXCESS_HANDSHAKE_DATA:"},
158
      {"Have data remaining in buffer after ServerHelloDone", ":EXCESS_HANDSHAKE_DATA:"},
159
      {"Hello Retry Request does not request any changes to Client Hello", ":EMPTY_HELLO_RETRY_REQUEST:"},
160
      {"Unexpected additional handshake message data found in record", ":EXCESS_HANDSHAKE_DATA:"},
161
      {"Inconsistent length in certificate request", ":DECODE_ERROR:"},
162
      {"unexpected key_update parameter", ":DECODE_ERROR:"},
163
      {"Inconsistent values in fragmented DTLS handshake header", ":FRAGMENT_MISMATCH:"},
164
      {"Invalid CertificateRequest: Length field outside parameters", ":DECODE_ERROR:"},
165
      {"Invalid ServerHello: Length field outside parameters", ":DECODE_ERROR:"},
166
      {"Invalid CertificateVerify: Extra bytes at end of message", ":DECODE_ERROR:"},
167
      {"Invalid Certificate_Status: invalid length field", ":DECODE_ERROR:"},
168
      {"Invalid ChangeCipherSpec", ":BAD_CHANGE_CIPHER_SPEC:"},
169
      {"Invalid ClientHello: Length field outside parameters", ":DECODE_ERROR:"},
170
      {"Invalid ClientKeyExchange: Extra bytes at end of message", ":DECODE_ERROR:"},
171
      {"Invalid ServerKeyExchange: Extra bytes at end of message", ":DECODE_ERROR:"},
172
      {"Invalid SessionTicket: Extra bytes at end of message", ":DECODE_ERROR:"},
173
      {"Invalid authentication tag: ChaCha20Poly1305 tag check failed", ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:"},
174
      {"Invalid authentication tag: GCM tag check failed", ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:"},
175
      {"Message authentication failure", ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:"},
176
      {"No content type found in encrypted record", ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:"},
177
      {"No shared DTLS version", ":UNSUPPORTED_PROTOCOL:"},
178
      {"No shared TLS version", ":UNSUPPORTED_PROTOCOL:"},
179
      {"OS2ECP: Unknown format type 251", ":BAD_ECPOINT:"},
180
      {"Peer sent signature algorithm that is not suitable for TLS 1.3", ":WRONG_SIGNATURE_TYPE:"},
181
      {"Policy forbids all available DTLS version", ":NO_SUPPORTED_VERSIONS_ENABLED:"},
182
      {"Policy forbids all available TLS version", ":NO_SUPPORTED_VERSIONS_ENABLED:"},
183
      {"Policy refuses to accept signing with any hash supported by peer", ":NO_COMMON_SIGNATURE_ALGORITHMS:"},
184
      {"Policy requires client send a certificate, but it did not", ":PEER_DID_NOT_RETURN_A_CERTIFICATE:"},
185
      {"PSK binder does not check out", ":DIGEST_CHECK_FAILED:"},
186
      {"PSK identity selected by server is out of bounds", ":PSK_IDENTITY_NOT_FOUND:"},
187
      {"PSK and ciphersuite selected by server are not compatible", ":OLD_SESSION_PRF_HASH_MISMATCH:"},
188
      {"Received a record that exceeds maximum size", ":ENCRYPTED_LENGTH_TOO_LONG:"},
189
      {"Received an encrypted record that exceeds maximum size", ":ENCRYPTED_LENGTH_TOO_LONG:"},
190
      {"received an illegal handshake message", ":UNEXPECTED_MESSAGE:"},
191
      {"Received a legacy Client Hello", ":UNSUPPORTED_PROTOCOL:"},
192
      {"Received an unexpected legacy Server Hello", ":UNSUPPORTED_PROTOCOL:"},
193
      {"Received application data after connection closure", ":APPLICATION_DATA_ON_SHUTDOWN:"},
194
      {"Received handshake data after connection closure", ":NO_RENEGOTIATION:"},
195
      {"Received multiple key share entries for the same group", ":DUPLICATE_KEY_SHARE:"},
196
      {"Received unexpected record version in initial record", ":WRONG_VERSION_NUMBER:"},
197
      {"Received unexpected record version", ":WRONG_VERSION_NUMBER:"},
198
      {"Rejecting ALPN request with alert", ":NO_APPLICATION_PROTOCOL:"},
199
      {"RSA signatures must use an RSASSA-PSS algorithm", ":WRONG_SIGNATURE_TYPE:"},
200
      {"Server attempting to negotiate SSLv3 which is not supported", ":UNSUPPORTED_PROTOCOL:"},
201
      {"Server certificate changed during renegotiation", ":SERVER_CERT_CHANGED:"},
202
      {"Server changed its mind about extended master secret", ":RENEGOTIATION_EMS_MISMATCH:"},
203
      {"Server changed its mind about secure renegotiation", ":RENEGOTIATION_MISMATCH:"},
204
      {"Server changed version after renegotiation", ":WRONG_SSL_VERSION:"},
205
      {"Server policy prohibits renegotiation", ":NO_RENEGOTIATION:"},
206
      {"Server replied using a ciphersuite not allowed in version it offered", ":WRONG_CIPHER_RETURNED:"},
207
      {"Server replied with an invalid version", ":UNSUPPORTED_PROTOCOL:"},
208
      {"server changed its chosen ciphersuite", ":WRONG_CIPHER_RETURNED:"},
209
      {"Server replied with DTLS-SRTP alg we did not send", ":BAD_SRTP_PROTECTION_PROFILE_LIST:"},
210
      {"Server replied with ciphersuite we didn't send", ":WRONG_CIPHER_RETURNED:"},
211
      {"Server replied with an invalid version", ":UNSUPPORTED_PROTOCOL:"},  // bogus version from "ServerBogusVersion"
212
      {"Server version SSL v3 is unacceptable by policy", ":UNSUPPORTED_PROTOCOL:"},  // "NoSSL3-Client-Unsolicited"
213
      {"legacy_version 'TLS v1.4' is not allowed", ":DECODE_ERROR:"},
214
      {"legacy_version 'Unknown 18.52' is not allowed", ":UNSUPPORTED_PROTOCOL:"},
215
      {"Server replied with non-null compression method", ":UNSUPPORTED_COMPRESSION_ALGORITHM:"},
216
      {"Server replied with some unknown ciphersuite", ":UNKNOWN_CIPHER_RETURNED:"},
217
      {"Server replied with unsupported extensions: 0", ":UNEXPECTED_EXTENSION:"},
218
      {"Server replied with unsupported extensions: 1234", ":UNEXPECTED_EXTENSION:"},
219
      {"Server replied with unsupported extensions: 16", ":UNEXPECTED_EXTENSION:"},
220
      {"Server replied with unsupported extensions: 43", ":UNEXPECTED_EXTENSION:"},
221
      {"Server replied with unsupported extensions: 5", ":UNEXPECTED_EXTENSION:"},
222
      {"Server resumed session and removed extended master secret", ":RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION:"},
223
      {"Server resumed session but added extended master secret", ":RESUMED_NON_EMS_SESSION_WITH_EMS_EXTENSION:"},
224
      {"Server resumed session but with wrong version", ":OLD_SESSION_VERSION_NOT_RETURNED:"},
225
      {"Server selected a group that is not compatible with the negotiated ciphersuite", ":WRONG_CURVE:"},
226
      {"Server sent ECC curve prohibited by policy", ":WRONG_CURVE:"},
227
      {"group was not advertised as supported", ":WRONG_CURVE:"},
228
      {"group was already offered", ":WRONG_CURVE:"},
229
      {"Server selected a key exchange group we didn't offer.", ":WRONG_CURVE:"},
230
      {"TLS 1.3 Server Hello selected a different version", ":SECOND_SERVERHELLO_VERSION_MISMATCH:"},
231
      {"Version downgrade received after Hello Retry", ":SECOND_SERVERHELLO_VERSION_MISMATCH:"},
232
      {"protected change cipher spec received", ":UNEXPECTED_RECORD:"},
233
      {"Server sent an unsupported extension", ":UNEXPECTED_EXTENSION:"},
234
      {"Unsupported extension found in Server Hello", ":UNEXPECTED_EXTENSION:"},
235
      {"Unexpected extension received", ":UNEXPECTED_EXTENSION:"},
236
      {"server hello must contain key exchange information", ":MISSING_KEY_SHARE:"},
237
      {"Peer sent duplicated extensions", ":DUPLICATE_EXTENSION:"},
238
      {"Server sent bad values for secure renegotiation", ":RENEGOTIATION_MISMATCH:"},
239
      {"Server version DTLS v1.0 is unacceptable by policy", ":UNSUPPORTED_PROTOCOL:"},
240
      {"Server version TLS v1.0 is unacceptable by policy", ":UNSUPPORTED_PROTOCOL:"},
241
      {"Server version TLS v1.1 is unacceptable by policy", ":UNSUPPORTED_PROTOCOL:"},
242
      {"Server_Hello_Done: Must be empty, and is not", ":DECODE_ERROR:"},
243
      {"Simulated OCSP callback failure", ":OCSP_CB_ERROR:"},
244
      {"Simulating cert verify callback failure", ":CERT_CB_ERROR:"},
245
      {"Simulating failure from OCSP response callback", ":OCSP_CB_ERROR:"},
246
      {"TLS plaintext record is larger than allowed maximum", ":DATA_LENGTH_TOO_LONG:"},
247
      {"Received an encrypted record that exceeds maximum plaintext size", ":DATA_LENGTH_TOO_LONG:"},
248
      {"TLS record type had unexpected value", ":UNEXPECTED_RECORD:"},
249
      {"TLS record version had unexpected value", ":WRONG_VERSION_NUMBER:"},
250
      {"Test requires rejecting cert", ":CERTIFICATE_VERIFY_FAILED:"},
251
      {"Too many PSK binders", ":PSK_IDENTITY_BINDER_COUNT_MISMATCH:"},
252
      {"Unexpected ALPN protocol", ":INVALID_ALPN_PROTOCOL:"},
253
      {"Unexpected record type 42 from counterparty", ":UNEXPECTED_RECORD:"},
254
      {"Unexpected state transition in handshake got a certificate_request expected server_hello_done seen server_hello+server_key_exchange",
255
       ":UNEXPECTED_MESSAGE:"},
256
      {"Unexpected state transition in handshake got a certificate_request expected server_key_exchange|server_hello_done seen server_hello",
257
       ":UNEXPECTED_MESSAGE:"},
258
      {"Unexpected state transition in handshake got a certificate_status expected certificate seen server_hello",
259
       ":UNEXPECTED_MESSAGE:"},
260
      {"Unexpected state transition in handshake got a change_cipher_spec expected certificate_verify seen client_hello+certificate+client_key_exchange",
261
       ":UNEXPECTED_RECORD:"},
262
      {"Unexpected state transition in handshake got a change_cipher_spec expected client_key_exchange seen client_hello",
263
       ":UNEXPECTED_RECORD:"},
264
      {"Unexpected state transition in handshake got a change_cipher_spec expected new_session_ticket seen server_hello+certificate+server_key_exchange+server_hello_done",
265
       ":UNEXPECTED_RECORD:"},
266
      {"Unexpected state transition in handshake got a client_key_exchange expected certificate seen client_hello",
267
       ":UNEXPECTED_MESSAGE:"},
268
      {"Unexpected state transition in handshake got a finished expected certificate_verify seen client_hello+certificate",
269
       ":UNEXPECTED_MESSAGE:"},
270
      {"Unexpected state transition in handshake got a finished expected certificate seen client_hello",
271
       ":UNEXPECTED_MESSAGE:"},
272
      {"Unexpected state transition in handshake got a finished expected change_cipher_spec seen client_hello",
273
       ":UNEXPECTED_RECORD:"},
274
      {"Unexpected state transition in handshake got a finished expected change_cipher_spec seen client_hello+client_key_exchange",
275
       ":UNEXPECTED_RECORD:"},
276
      {"Unexpected state transition in handshake got a finished expected change_cipher_spec seen server_hello",
277
       ":UNEXPECTED_RECORD:"},
278
      {"Unexpected state transition in handshake got a finished expected change_cipher_spec seen server_hello+certificate+server_key_exchange+server_hello_done+new_session_ticket",
279
       ":UNEXPECTED_RECORD:"},
280
      {"Unexpected state transition in handshake got a hello_request expected server_hello", ":UNEXPECTED_MESSAGE:"},
281
      {"Unexpected state transition in handshake got a server_hello_done expected server_key_exchange seen server_hello+certificate+certificate_status",
282
       ":UNEXPECTED_MESSAGE:"},
283
      {"Unexpected state transition in handshake got a server_key_exchange expected certificate_request|server_hello_done seen server_hello+certificate+certificate_status",
284
       ":UNEXPECTED_MESSAGE:"},
285
      {"Unexpected state transition in handshake got a server_hello_done expected server_key_exchange seen server_hello+certificate",
286
       ":UNEXPECTED_MESSAGE:"},
287
      {"Unexpected state transition in handshake got a server_key_exchange expected certificate seen server_hello",
288
       ":UNEXPECTED_MESSAGE:"},
289
      {"Unexpected state transition in handshake got a server_key_exchange expected certificate_request|server_hello_done seen server_hello+certificate",
290
       ":UNEXPECTED_MESSAGE:"},
291
      {"Unexpected state transition in handshake got a hello_retry_request expected server_hello",
292
       ":UNEXPECTED_MESSAGE:"},
293
      {"Unexpected state transition in handshake got a server_key_exchange not expecting messages",
294
       ":BAD_HELLO_REQUEST:"},
295
      {"Unexpected state transition in handshake got a finished expected certificate_verify seen server_hello+certificate+encrypted_extensions",
296
       ":BAD_HELLO_REQUEST:"},
297
      {"Unknown TLS handshake message type 43", ":UNEXPECTED_MESSAGE:"},
298
      {"Unknown TLS handshake message type 44", ":UNEXPECTED_MESSAGE:"},
299
      {"Unknown TLS handshake message type 45", ":UNEXPECTED_MESSAGE:"},
300
      {"Unknown TLS handshake message type 46", ":UNEXPECTED_MESSAGE:"},
301
      {"Unknown TLS handshake message type 53", ":UNEXPECTED_MESSAGE:"},
302
      {"Unknown TLS handshake message type 54", ":UNEXPECTED_MESSAGE:"},
303
      {"Unknown TLS handshake message type 55", ":UNEXPECTED_MESSAGE:"},
304
      {"Unknown TLS handshake message type 56", ":UNEXPECTED_MESSAGE:"},
305
      {"Unknown TLS handshake message type 57", ":UNEXPECTED_MESSAGE:"},
306
      {"Unknown TLS handshake message type 58", ":UNEXPECTED_MESSAGE:"},
307
      {"Unknown TLS handshake message type 6", ":UNEXPECTED_MESSAGE:"},
308
      {"Unknown TLS handshake message type 62", ":UNEXPECTED_MESSAGE:"},
309
      {"Unknown TLS handshake message type 64", ":UNEXPECTED_MESSAGE:"},
310
      {"Unknown handshake message received", ":UNEXPECTED_MESSAGE:"},
311
      {"Unknown post-handshake message received", ":UNEXPECTED_MESSAGE:"},
312
      {"signature_algorithm_of_scheme: Unknown signature algorithm enum", ":WRONG_SIGNATURE_TYPE:"},
313
      {"Unexpected session ID during downgrade", ":SERVER_ECHOED_INVALID_SESSION_ID:"},
314
      {"Encrypted Extensions contained an extension that is not allowed", ":ERROR_PARSING_EXTENSION:"},
315
      {"Encrypted Extensions contained an extension that was not offered", ":UNEXPECTED_EXTENSION:"},
316
      {"Certificate Entry contained an extension that is not allowed", ":UNEXPECTED_EXTENSION:"},
317
      {"Certificate Entry contained an extension that was not offered", ":UNEXPECTED_EXTENSION:"},
318
      {"Server Hello contained an extension that is not allowed", ":UNEXPECTED_EXTENSION:"},
319
      {"Hello Retry Request contained an extension that is not allowed", ":UNEXPECTED_EXTENSION:"},
320
      {"Signature algorithm does not match certificate's public key", ":WRONG_SIGNATURE_TYPE:"},
321
      {"unprotected record received where protected traffic was expected", ":INVALID_OUTER_RECORD_TYPE:"},
322
      {"Error alert not marked fatal", ":BAD_ALERT:"},
323
      {"Peer sent unknown signature scheme", ":WRONG_SIGNATURE_TYPE:"},
324
      {"We did not offer the usage of RSA_PSS_SHA256 as a signature scheme", ":WRONG_SIGNATURE_TYPE:"},
325
   };
155,184✔
326

327
   auto err_map_i = err_map.find(e);
732✔
328
   if(err_map_i != err_map.end()) {
732✔
329
      return err_map_i->second;
1,460✔
330
   }
331

332
   return "Unmapped error: '" + e + "'";
8✔
333
}
334

335
class Shim_Exception final : public std::exception {
336
   public:
337
      Shim_Exception(std::string_view msg, int rc = 1) : m_msg(msg), m_rc(rc) {}
6✔
338

339
      const char* what() const noexcept override { return m_msg.c_str(); }
6✔
340

341
      int rc() const { return m_rc; }
3✔
342

343
   private:
344
      const std::string m_msg;
345
      int m_rc;
346
};
347

348
#if defined(BOTAN_TARGET_OS_HAS_SOCKETS)
349

350
class Shim_Socket final {
351
   private:
352
      typedef int socket_type;
353
      typedef ssize_t socket_op_ret_type;
354

355
      static void close_socket(socket_type s) { ::close(s); }
356

357
      static std::string get_last_socket_error() { return ::strerror(errno); }
358

359
      using unique_addrinfo_t = std::unique_ptr<addrinfo, decltype(&::freeaddrinfo)>;
360

361
   public:
362
      Shim_Socket(const std::string& hostname, int port, const bool ipv6) : m_socket(-1) {
2,598✔
363
         addrinfo hints;
2,598✔
364
         std::memset(&hints, 0, sizeof(hints));
2,598✔
365
         hints.ai_family = AF_UNSPEC;
2,598✔
366
         hints.ai_socktype = SOCK_STREAM;
2,598✔
367
         hints.ai_flags = AI_NUMERICSERV;
2,598✔
368

369
         const std::string service = std::to_string(port);
2,598✔
370

371
         // TODO: C++23 will introduce std::out_ptr() that should replace the
372
         //       temporary variable for the call to ::getaddrinfo() and
373
         //       std::unique_ptr<>::reset().
374
         unique_addrinfo_t::pointer res_tmp;
2,598✔
375
         int rc = ::getaddrinfo(hostname.c_str(), service.c_str(), &hints, &res_tmp);
2,598✔
376
         unique_addrinfo_t res(res_tmp, &::freeaddrinfo);
2,598✔
377

378
         shim_log("Connecting " + hostname + ":" + service);
5,196✔
379

380
         if(rc != 0) {
2,598✔
381
            throw Shim_Exception("Name resolution failed for " + hostname);
×
382
         }
383

384
         for(addrinfo* rp = res.get(); (m_socket == -1) && (rp != nullptr); rp = rp->ai_next) {
5,196✔
385
            if((!ipv6 && rp->ai_family != AF_INET) || (ipv6 && rp->ai_family != AF_INET6)) {
2,598✔
386
               continue;
×
387
            }
388

389
            m_socket = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
2,598✔
390

391
            if(m_socket == -1) {
2,598✔
392
               // unsupported socket type?
393
               continue;
×
394
            }
395

396
            int err = ::connect(m_socket, rp->ai_addr, rp->ai_addrlen);
2,598✔
397

398
            if(err != 0) {
2,598✔
399
               ::close(m_socket);
×
400
               m_socket = -1;
×
401
            }
402
         }
403

404
         if(m_socket < 0) {
2,598✔
405
            throw Shim_Exception("Failed to connect to host");
×
406
         }
407
      }
2,598✔
408

409
      Shim_Socket(const Shim_Socket&) = delete;
410
      Shim_Socket& operator=(const Shim_Socket&) = delete;
411

412
      Shim_Socket(Shim_Socket&&) = delete;
413
      Shim_Socket& operator=(Shim_Socket&&) = delete;
414

415
      ~Shim_Socket() {
2,581✔
416
         ::close(m_socket);
2,581✔
417
         m_socket = -1;
2,581✔
418
      }
2,581✔
419

420
      void write(const uint8_t buf[], size_t len) const {
18,459✔
421
         if(m_socket < 0) {
18,459✔
422
            throw Shim_Exception("Socket was bad on write");
×
423
         }
424
         size_t sent_so_far = 0;
425
         while(sent_so_far != len) {
36,908✔
426
            const size_t left = len - sent_so_far;
18,459✔
427
            socket_op_ret_type sent =
18,459✔
428
               ::send(m_socket, Botan::cast_uint8_ptr_to_char(&buf[sent_so_far]), left, MSG_NOSIGNAL);
18,459✔
429
            if(sent < 0) {
18,459✔
430
               if(errno == EPIPE) {
10✔
431
                  return;
432
               } else {
433
                  throw Shim_Exception("Socket write failed", errno);
2✔
434
               }
435
            } else {
436
               sent_so_far += static_cast<size_t>(sent);
18,449✔
437
            }
438
         }
439
      }
440

441
      size_t read(uint8_t buf[], size_t len) const {
161,257✔
442
         if(m_socket < 0) {
161,257✔
443
            throw Shim_Exception("Socket was bad on read");
×
444
         }
445
         socket_op_ret_type got = ::read(m_socket, Botan::cast_uint8_ptr_to_char(buf), len);
161,257✔
446

447
         if(got < 0) {
161,257✔
448
            if(errno == ECONNRESET) {
517✔
449
               return 0;
450
            }
451
            throw Shim_Exception("Socket read failed: " + std::string(strerror(errno)));
×
452
         }
453

454
         return static_cast<size_t>(got);
160,740✔
455
      }
456

457
      void read_exactly(uint8_t buf[], size_t len) const {
252,894✔
458
         if(m_socket < 0) {
252,894✔
459
            throw Shim_Exception("Socket was bad on read");
×
460
         }
461

462
         while(len > 0) {
505,788✔
463
            socket_op_ret_type got = ::read(m_socket, Botan::cast_uint8_ptr_to_char(buf), len);
252,894✔
464

465
            if(got == 0) {
252,894✔
466
               throw Shim_Exception("Socket read EOF");
×
467
            } else if(got < 0) {
252,894✔
468
               throw Shim_Exception("Socket read failed: " + std::string(strerror(errno)));
×
469
            }
470

471
            buf += static_cast<size_t>(got);
252,894✔
472
            len -= static_cast<size_t>(got);
252,894✔
473
         }
474
      }
252,894✔
475

476
   private:
477
      socket_type m_socket;
478
};
479

480
#endif
481

482
std::set<std::string> combine_options(const std::set<std::string>& a,
1,984✔
483
                                      const std::set<std::string>& b,
484
                                      const std::set<std::string>& c,
485
                                      const std::set<std::string>& d) {
486
   std::set<std::string> combined;
1,984✔
487

488
   for(const auto& i : a) {
49,600✔
489
      combined.insert(i);
47,616✔
490
   }
491
   for(const auto& i : b) {
7,936✔
492
      combined.insert(i);
5,952✔
493
   }
494
   for(const auto& i : c) {
43,648✔
495
      combined.insert(i);
41,664✔
496
   }
497
   for(const auto& i : d) {
9,920✔
498
      combined.insert(i);
7,936✔
499
   }
500

501
   return combined;
1,984✔
502
}
×
503

504
class Shim_Arguments final {
505
   public:
506
      Shim_Arguments(const std::set<std::string>& flags,
1,984✔
507
                     const std::set<std::string>& string_opts,
508
                     const std::set<std::string>& base64_opts,
509
                     const std::set<std::string>& int_opts,
510
                     const std::set<std::string>& int_vec_opts) :
1,984✔
511
            m_flags(flags),
1,984✔
512
            m_string_opts(string_opts),
1,984✔
513
            m_base64_opts(base64_opts),
1,984✔
514
            m_int_opts(int_opts),
1,984✔
515
            m_int_vec_opts(int_vec_opts),
1,984✔
516
            m_all_options(combine_options(string_opts, base64_opts, int_opts, int_vec_opts)) {}
1,984✔
517

518
      void parse_args(char* argv[]);
519

520
      bool flag_set(const std::string& flag) const {
128,907✔
521
         if(!m_flags.contains(flag)) {
128,907✔
522
            throw Shim_Exception("Unknown bool flag " + flag);
×
523
         }
524

525
         return m_parsed_flags.contains(flag);
128,907✔
526
      }
527

528
      std::string test_name() const { return get_string_opt("test-name"); }
8,146✔
529

530
      std::string get_string_opt(const std::string& key) const {
6,428✔
531
         if(!m_string_opts.contains(key)) {
6,428✔
532
            throw Shim_Exception("Unknown string key " + key);
×
533
         }
534
         return get_opt(key);
6,428✔
535
      }
536

537
      std::string get_string_opt_or_else(const std::string& key, const std::string& def) const {
194,531✔
538
         if(!m_string_opts.contains(key)) {
194,531✔
539
            throw Shim_Exception("Unknown string key " + key);
×
540
         }
541
         if(!option_used(key)) {
194,531✔
542
            return def;
387,785✔
543
         }
544
         return get_opt(key);
1,277✔
545
      }
546

547
      std::vector<uint8_t> get_b64_opt(const std::string& key) const {
73✔
548
         if(!m_base64_opts.contains(key)) {
73✔
549
            throw Shim_Exception("Unknown base64 key " + key);
×
550
         }
551
         return Botan::unlock(Botan::base64_decode(get_opt(key)));
219✔
552
      }
553

554
      size_t get_int_opt(const std::string& key) const {
7,386✔
555
         if(!m_int_opts.contains(key)) {
7,386✔
556
            throw Shim_Exception("Unknown int key " + key);
×
557
         }
558
         return Botan::to_u32bit(get_opt(key));
7,386✔
559
      }
560

561
      size_t get_int_opt_or_else(const std::string& key, size_t def) const {
7,850✔
562
         if(!m_int_opts.contains(key)) {
7,850✔
563
            throw Shim_Exception("Unknown int key " + key);
×
564
         }
565
         if(!option_used(key)) {
7,850✔
566
            return def;
567
         }
568

569
         return Botan::to_u32bit(get_opt(key));
938✔
570
      }
571

572
      std::vector<size_t> get_int_vec_opt(const std::string& key) const {
418✔
573
         if(!m_int_vec_opts.contains(key)) {
418✔
574
            throw Shim_Exception("Unknown int vec key " + key);
×
575
         }
576

577
         auto i = m_parsed_int_vec_opts.find(key);
418✔
578
         if(i == m_parsed_int_vec_opts.end()) {
418✔
579
            return std::vector<size_t>();
418✔
580
         } else {
581
            return i->second;
418✔
582
         }
583
      }
584

585
      std::vector<std::string> get_alpn_string_vec_opt(const std::string& option) const {
1,438✔
586
         // hack used for alpn list (relies on all ALPNs being 3 chars long...)
587
         char delim = 0x03;
1,438✔
588

589
         if(option_used(option)) {
1,438✔
590
            return Botan::split_on(get_string_opt(option), delim);
58✔
591
         } else {
592
            return std::vector<std::string>();
1,438✔
593
         }
594
      }
595

596
      bool option_used(const std::string& key) const {
340,260✔
597
         if(!m_all_options.contains(key)) {
340,260✔
598
            throw Shim_Exception("Invalid option " + key);
×
599
         }
600
         if(m_parsed_opts.find(key) != m_parsed_opts.end()) {
340,260✔
601
            return true;
602
         }
603
         if(m_parsed_int_vec_opts.find(key) != m_parsed_int_vec_opts.end()) {
335,710✔
604
            return true;
418✔
605
         }
606
         return false;
607
      }
608

609
   private:
610
      std::string get_opt(const std::string& key) const {
16,102✔
611
         auto i = m_parsed_opts.find(key);
16,102✔
612
         if(i == m_parsed_opts.end()) {
16,102✔
613
            throw Shim_Exception("Option " + key + " was not provided");
×
614
         }
615
         return i->second;
16,102✔
616
      }
617

618
      const std::set<std::string> m_flags;
619
      const std::set<std::string> m_string_opts;
620
      const std::set<std::string> m_base64_opts;
621
      const std::set<std::string> m_int_opts;
622
      const std::set<std::string> m_int_vec_opts;
623
      const std::set<std::string> m_all_options;
624

625
      std::set<std::string> m_parsed_flags;
626
      std::map<std::string, std::string> m_parsed_opts;
627
      std::map<std::string, std::vector<size_t>> m_parsed_int_vec_opts;
628
};
629

630
void Shim_Arguments::parse_args(char* argv[]) {
1,984✔
631
   int i = 1;  // skip argv[0]
1,984✔
632

633
   while(argv[i] != nullptr) {
17,543✔
634
      const std::string param(argv[i]);
15,559✔
635

636
      if(param.find('-') == 0) {
15,559✔
637
         const std::string flag_name = param.substr(1, std::string::npos);
15,559✔
638

639
         if(m_flags.contains(flag_name)) {
15,559✔
640
            shim_log("flag " + flag_name);
5,496✔
641
            m_parsed_flags.insert(flag_name);
5,496✔
642
            i += 1;
5,496✔
643
         } else if(m_all_options.contains(flag_name)) {
10,063✔
644
            if(argv[i + 1] == nullptr) {
10,063✔
645
               throw Shim_Exception("Expected argument following " + param);
×
646
            }
647
            std::string val(argv[i + 1]);
10,063✔
648
            shim_log(Botan::fmt("param {}={}", flag_name, val));
10,063✔
649

650
            if(m_int_vec_opts.contains(flag_name)) {
10,063✔
651
               const size_t v = Botan::to_u32bit(val);
309✔
652
               m_parsed_int_vec_opts[flag_name].push_back(v);
309✔
653
            } else {
654
               m_parsed_opts[flag_name] = val;
9,754✔
655
            }
656
            i += 2;
10,063✔
657
         } else {
10,063✔
658
            shim_log("Unknown option " + param);
×
659
            throw Shim_Exception("Unknown option " + param, 89);
×
660
         }
661
      } else {
15,559✔
662
         shim_log("Unknown option " + param);
×
663
         throw Shim_Exception("Unknown option " + param, 89);
×
664
      }
665
   }
15,559✔
666
}
1,984✔
667

668
std::unique_ptr<Shim_Arguments> parse_options(char* argv[]) {
1,984✔
669
   const std::set<std::string> bogo_shim_flags = {
1,984✔
670
      "allow-false-start-without-alpn",
671
      "allow-unknown-alpn-protos",
672
      "async",
673
      "cbc-record-splitting",
674
      "check-close-notify",
675
      "decline-alpn",
676
      "decline-ocsp-callback",
677
      "dtls",
678
      "enable-all-curves",
679
      "enable-channel-id",
680
      "enable-early-data",
681
      "enable-ed25519",
682
      "enable-grease",
683
      "enable-ocsp-stapling",
684
      "enable-signed-cert-timestamps",
685
      "enforce-rsa-key-usage",
686
      //"expect-accept-early-data",
687
      "expect-extended-master-secret",
688
      "expect-no-offer-early-data",
689
      "expect-no-secure-renegotiation",
690
      "expect-no-session",
691
      "expect-no-session-id",
692
      //"expect-reject-early-data",
693
      "expect-secure-renegotiation",
694
      "expect-session-id",
695
      "expect-session-miss",
696
      "expect-sha256-client-cert",
697
      "expect-ticket-renewal",
698
      "expect-ticket-supports-early-data",
699
      //"expect-tls13-downgrade",
700
      "expect-verify-result",
701
      "expect-no-hrr",
702
      "expect-hrr",
703
      //"export-traffic-secrets",
704
      "fail-cert-callback",
705
      //"fail-ddos-callback",
706
      //"fail-early-callback",
707
      "fail-ocsp-callback",
708
      "fallback-scsv",
709
      //"false-start",
710
      "forbid-renegotiation-after-handshake",
711
      "handoff",
712
      "handshake-never-done",
713
      "handshake-twice",
714
      "handshaker-resume",
715
      //"ignore-tls13-downgrade",
716
      "implicit-handshake",
717
      "install-cert-compression-algs",
718
      "install-ddos-callback",
719
      "ipv6",
720
      "is-handshaker-supported",
721
      //"jdk11-workaround",
722
      "key-update",
723
      "no-op-extra-handshake",
724
      "no-rsa-pss-rsae-certs",
725
      "no-ticket",
726
      "no-tls1",
727
      "no-tls11",
728
      "no-tls12",
729
      "no-tls13",
730
      "on-resume-no-ticket",
731
      //"on-resume-verify-fail",
732
      //"partial-write",
733
      //"peek-then-read",
734
      //"read-with-unfinished-write",
735
      "reject-alpn",
736
      "renegotiate-freely",
737
      "renegotiate-ignore",
738
      "renegotiate-once",
739
      //"renew-ticket",
740
      "require-any-client-certificate",
741
      "retain-only-sha256-client-cert",
742
      //"reverify-on-resume",
743
      "select-empty-alpn",
744
      "send-alert",
745
      "server",
746
      "server-preference",
747
      "set-ocsp-in-callback",
748
      "shim-shuts-down",
749
      "shim-writes-first",
750
      //"tls-unique",
751
      "use-custom-verify-callback",
752
      "use-early-callback",
753
      "use-export-context",
754
      "use-exporter-between-reads",
755
      "use-ocsp-callback",
756
      //"use-old-client-cert-callback",
757
      //"use-ticket-callback",
758
      "verify-fail",
759
      "verify-peer",
760
      //"verify-peer-if-no-obc",
761
      "wait-for-debugger",
762
      "write-different-record-sizes",
763
   };
148,800✔
764

765
   const std::set<std::string> bogo_shim_string_opts = {
1,984✔
766
      "advertise-alpn",
767
      //"advertise-npn",
768
      "cert-file",
769
      "cipher",
770
      //"delegated-credential",
771
      "expect-advertised-alpn",
772
      "expect-alpn",
773
      "expect-client-ca-list",
774
      "expect-early-data-reason",
775
      "expect-late-alpn",
776
      "expect-msg-callback",
777
      //"expect-next-proto",
778
      "expect-peer-cert-file",
779
      "expect-server-name",
780
      "export-context",
781
      "export-label",
782
      "handshaker-path",
783
      "host-name",
784
      "key-file",
785
      "psk",
786
      "psk-identity",
787
      "select-alpn",
788
      "select-next-proto",
789
      "srtp-profiles",
790
      "test-name",
791
      "use-client-ca-list",
792
      //"send-channel-id",
793
      "write-settings",
794
   };
49,600✔
795

796
   const std::set<std::string> bogo_shim_base64_opts = {
1,984✔
797
      "expect-certificate-types",
798
      //"expect-channel-id",
799
      "expect-ocsp-response",
800
      //"expect-quic-transport-params",
801
      //"expect-signed-cert-timestamps",
802
      "ocsp-response",
803
      //"quic-transport-params",
804
      //"signed-cert-timestamps",
805
      //"ticket-key", /* we use a different ticket format from Boring */
806
      //"token-binding-params",
807
   };
7,936✔
808

809
   const std::set<std::string> bogo_shim_int_opts{
1,984✔
810
      "expect-cipher-aes",
811
      "expect-cipher-no-aes",
812
      "expect-curve-id",
813
      "expect-peer-signature-algorithm",
814
      "expect-ticket-age-skew",
815
      "expect-token-binding-param",
816
      "expect-total-renegotiations",
817
      "expect-version",
818
      //"export-early-keying-material",
819
      "export-keying-material",
820
      "initial-timeout-duration-ms",
821
      "max-cert-list",
822
      //"max-send-fragment",
823
      "max-version",
824
      "min-version",
825
      "mtu",
826
      "on-initial-expect-curve-id",
827
      "on-resume-expect-curve-id",
828
      "port",
829
      "read-size",
830
      "resume-count",
831
      "resumption-delay",
832
      "shim-id",
833
   };
43,648✔
834

835
   const std::set<std::string> bogo_shim_int_vec_opts{
1,984✔
836
      "curves",
837
      "expect-peer-verify-pref",
838
      "signing-prefs",
839
      "verify-prefs",
840
   };
9,920✔
841

842
   std::unique_ptr<Shim_Arguments> args(new Shim_Arguments(
1,984✔
843
      bogo_shim_flags, bogo_shim_string_opts, bogo_shim_base64_opts, bogo_shim_int_opts, bogo_shim_int_vec_opts));
1,984✔
844

845
   // may throw:
846
   args->parse_args(argv);
1,984✔
847

848
   return args;
1,984✔
849
}
1,984✔
850

851
class Shim_Policy final : public Botan::TLS::Policy {
2,581✔
852
   public:
853
      Shim_Policy(const Shim_Arguments& args) : m_args(args), m_sessions(0) {}
2,598✔
854

855
      void incr_session_established() { m_sessions += 1; }
1,978✔
856

857
      std::vector<std::string> allowed_ciphers() const override {
183,903✔
858
         std::vector<std::string> allowed_without_aes = {
183,903✔
859
            "ChaCha20Poly1305",
860
            "Camellia-256/GCM",
861
            "Camellia-128/GCM",
862
            "ARIA-256/GCM",
863
            "ARIA-128/GCM",
864
            "Camellia-256",
865
            "Camellia-128",
866
            "SEED",
867
         };
1,655,127✔
868

869
         std::vector<std::string> allowed_just_aes = {
183,903✔
870
            "AES-256/OCB(12)",
871
            "AES-128/OCB(12)",
872
            "AES-256/GCM",
873
            "AES-128/GCM",
874
            "AES-256/CCM",
875
            "AES-128/CCM",
876
            "AES-256/CCM(8)",
877
            "AES-128/CCM(8)",
878
            "AES-256",
879
            "AES-128",
880
         };
2,022,933✔
881

882
         // 3DES is not supported by default anymore, only if the test runner
883
         // explicitly enables it via -cipher=
884
         const std::string cipher_limit = m_args.get_string_opt_or_else("cipher", "");
367,806✔
885
         if(cipher_limit == "3DES") {
183,903✔
886
            return {"3DES"};
2,012✔
887
         } else if(cipher_limit == "DEFAULT:!AES") {
182,897✔
888
            return allowed_without_aes;
183,903✔
889
         } else {
890
            // ignore this very specific config (handled in the overload of ciphersuite_list)
891
            if(!cipher_limit.empty() &&
182,803✔
892
               cipher_limit !=
893
                  "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:[TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384|TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256|TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA]:TLS_RSA_WITH_AES_128_GCM_SHA256:TLS_RSA_WITH_AES_128_CBC_SHA:[TLS_RSA_WITH_AES_256_GCM_SHA384|TLS_RSA_WITH_AES_256_CBC_SHA]") {
×
894
               shim_exit_with_error("Unknown cipher limit " + cipher_limit);
×
895
            }
896
         }
897

898
         return Botan::concat(allowed_without_aes, allowed_just_aes);
182,803✔
899
      }
183,903✔
900

901
      std::vector<std::string> allowed_signature_hashes() const override {
27,997✔
902
         if(m_args.option_used("signing-prefs")) {
27,997✔
903
            std::vector<std::string> pref_hash;
76✔
904
            for(size_t pref : m_args.get_int_vec_opt("signing-prefs")) {
155✔
905
               const Botan::TLS::Signature_Scheme scheme(pref);
79✔
906
               if(!scheme.is_available()) {
79✔
907
                  shim_log("skipping inavailable but preferred signature scheme: " + std::to_string(pref));
2✔
908
                  continue;
1✔
909
               }
910
               pref_hash.push_back(scheme.hash_function_name());
78✔
911
            }
×
912

913
            if(m_args.flag_set("server")) {
76✔
914
               pref_hash.push_back("SHA-256");
128✔
915
            }
916
            return pref_hash;
76✔
917
         } else {
76✔
918
            return {"SHA-512", "SHA-384", "SHA-256", "SHA-1"};
139,605✔
919
         }
920
      }
921

922
      //std::vector<std::string> allowed_macs() const override;
923

924
      std::vector<std::string> allowed_signature_methods() const override {
26,371✔
925
         return {
26,371✔
926
            "ECDSA",
927
            "RSA",
928
            "IMPLICIT",
929
         };
105,484✔
930
      }
931

932
      std::vector<Botan::TLS::Signature_Scheme> acceptable_signature_schemes() const override {
1,713✔
933
         if(m_args.option_used("verify-prefs")) {
1,713✔
934
            std::vector<Botan::TLS::Signature_Scheme> schemes;
95✔
935
            for(size_t pref : m_args.get_int_vec_opt("verify-prefs")) {
190✔
936
               schemes.emplace_back(static_cast<uint16_t>(pref));
95✔
937
            }
×
938

939
            return schemes;
95✔
940
         }
95✔
941

942
         return Botan::TLS::Policy::acceptable_signature_schemes();
1,618✔
943
      }
944

945
      std::vector<Botan::TLS::Signature_Scheme> allowed_signature_schemes() const override {
2,874✔
946
         if(m_args.option_used("signing-prefs")) {
2,874✔
947
            std::vector<Botan::TLS::Signature_Scheme> schemes;
66✔
948
            for(size_t pref : m_args.get_int_vec_opt("signing-prefs")) {
136✔
949
               schemes.emplace_back(static_cast<uint16_t>(pref));
70✔
950
            }
×
951

952
            // The relevant tests (*-Sign-Negotiate-*) want to configure a preference
953
            // for the scheme of our signing operation (-signing-prefs). However, this
954
            // policy method (`allowed_signature_schemes`) also restricts the peer's
955
            // signing operation. If we weren't to add a few 'common' algorithms, initial
956
            // security parameter negotiation would fail.
957
            // By placing the BoGo-configured scheme first we make sure our implementation
958
            // meets BoGo's expectation when it is our turn to sign.
959
            if(!m_args.flag_set("server")) {
66✔
960
               schemes.emplace_back(Botan::TLS::Signature_Scheme::RSA_PKCS1_SHA256);
38✔
961
               schemes.emplace_back(Botan::TLS::Signature_Scheme::RSA_PSS_SHA256);
38✔
962
               schemes.emplace_back(Botan::TLS::Signature_Scheme::ECDSA_SHA256);
38✔
963
            }
964

965
            return schemes;
66✔
966
         }
66✔
967

968
         return Botan::TLS::Policy::allowed_signature_schemes();
2,808✔
969
      }
970

971
      //size_t minimum_signature_strength() const override;
972

973
      //bool require_cert_revocation_info() const override;
974

975
      std::vector<Botan::TLS::Group_Params> key_exchange_groups() const override {
5,583✔
976
         if(m_args.option_used("curves")) {
5,583✔
977
            std::vector<Botan::TLS::Group_Params> groups;
181✔
978

979
            // upcall to base class to find the groups actually supported by
980
            // this Botan build
981
            const auto supported_groups = Botan::TLS::Policy::key_exchange_groups();
181✔
982

983
            for(size_t pref : m_args.get_int_vec_opt("curves")) {
706✔
984
               const auto group = static_cast<Botan::TLS::Group_Params>(pref);
525✔
985
               if(std::find(supported_groups.cbegin(), supported_groups.cend(), group) != supported_groups.end()) {
525✔
986
                  groups.push_back(group);
406✔
987
               }
988

989
               // Given that this is still a draft-standard, we didn't add the
990
               // hybrid groups to the default policy, yet.
991
               //
992
               // TODO: once `TLS::Policy::key_exchange_groups()` contains it by
993
               //       default, remove this explicit check.
994
               if(group == Botan::TLS::Group_Params::HYBRID_X25519_KYBER_768_R3_OQS) {
525✔
995
                  groups.push_back(group);
44✔
996
               }
997
            }
×
998

999
            return groups;
181✔
1000
         }
181✔
1001

1002
         return Botan::TLS::Policy::key_exchange_groups();
5,402✔
1003
      }
1004

1005
      bool use_ecc_point_compression() const override { return false; }  // BoGo expects this
2,131✔
1006

1007
      Botan::TLS::Group_Params choose_key_exchange_group(
1,937✔
1008
         const std::vector<Botan::TLS::Group_Params>& supported_by_peer,
1009
         const std::vector<Botan::TLS::Group_Params>& offered_by_peer) const override {
1010
         BOTAN_UNUSED(offered_by_peer);
1,937✔
1011

1012
         // always insist on our most preferred group regardless of the peer's
1013
         // pre-offers (BoGo expects it like that)
1014
         const auto our_groups = key_exchange_groups();
1,937✔
1015
         for(auto g : our_groups) {
2,018✔
1016
            if(Botan::value_exists(supported_by_peer, g)) {
4,026✔
1017
               return g;
1,932✔
1018
            }
1019
         }
1020

1021
         return Botan::TLS::Group_Params::NONE;
1022
      }
1,937✔
1023

1024
      bool require_client_certificate_authentication() const override {
656✔
1025
         return m_args.flag_set("require-any-client-certificate");
1,312✔
1026
      }
1027

1028
      bool request_client_certificate_authentication() const override {
753✔
1029
         return m_args.flag_set("verify-peer") || m_args.flag_set("fail-cert-callback") ||
2,139✔
1030
                require_client_certificate_authentication();
1,381✔
1031
      }
1032

1033
      bool allow_insecure_renegotiation() const override {
1,080✔
1034
         if(m_args.flag_set("expect-no-secure-renegotiation")) {
2,160✔
1035
            return true;
1036
         } else {
1037
            return false;
1,079✔
1038
         }
1039
      }
1040

1041
      //bool include_time_in_hello_random() const override;
1042

1043
      bool allow_client_initiated_renegotiation() const override {
40✔
1044
         if(m_args.flag_set("renegotiate-freely")) {
80✔
1045
            return true;
1046
         }
1047

1048
         if(m_args.flag_set("renegotiate-once") && m_sessions <= 1) {
16✔
1049
            return true;
2✔
1050
         }
1051

1052
         return false;
1053
      }
1054

1055
      bool allow_server_initiated_renegotiation() const override {
39✔
1056
         return allow_client_initiated_renegotiation();  // same logic
39✔
1057
      }
1058

1059
      bool allow_version(Botan::TLS::Protocol_Version version) const {
17,663✔
1060
         if(m_args.option_used("min-version")) {
17,663✔
1061
            const uint16_t min_version_16 = static_cast<uint16_t>(m_args.get_int_opt("min-version"));
65✔
1062
            Botan::TLS::Protocol_Version min_version(min_version_16 >> 8, min_version_16 & 0xFF);
65✔
1063
            if(min_version > version) {
65✔
1064
               return false;
15✔
1065
            }
1066
         }
1067

1068
         if(m_args.option_used("max-version")) {
17,648✔
1069
            const uint16_t max_version_16 = static_cast<uint16_t>(m_args.get_int_opt("max-version"));
50✔
1070
            Botan::TLS::Protocol_Version max_version(max_version_16 >> 8, max_version_16 & 0xFF);
50✔
1071
            if(version > max_version) {
50✔
1072
               return false;
7✔
1073
            }
1074
         }
1075

1076
         return version.known_version();
17,641✔
1077
      }
1078

1079
      bool allow_tls12() const override {
10,396✔
1080
         return !m_args.flag_set("dtls") && !m_args.flag_set("no-tls12") &&
30,204✔
1081
                allow_version(Botan::TLS::Protocol_Version::TLS_V12);
30,204✔
1082
      }
1083

1084
      bool allow_tls13() const override {
4,773✔
1085
         return !m_args.flag_set("dtls") && !m_args.flag_set("no-tls13") &&
14,112✔
1086
                allow_version(Botan::TLS::Protocol_Version::TLS_V13);
14,112✔
1087
      }
1088

1089
      bool allow_dtls12() const override {
3,694✔
1090
         return m_args.flag_set("dtls") && !m_args.flag_set("no-tls12") &&
11,073✔
1091
                allow_version(Botan::TLS::Protocol_Version::DTLS_V12);
11,073✔
1092
      }
1093

1094
      //Botan::TLS::Group_Params default_dh_group() const override;
1095

1096
      //size_t minimum_dh_group_size() const override;
1097

1098
      size_t minimum_ecdsa_group_size() const override { return 224; }
119✔
1099

1100
      size_t minimum_ecdh_group_size() const override { return 224; }
1,726✔
1101

1102
      //size_t minimum_rsa_bits() const override;
1103

1104
      //size_t minimum_dsa_group_size() const override;
1105

1106
      //void check_peer_key_acceptable(const Botan::Public_Key& public_key) const override;
1107

1108
      //bool hide_unknown_users() const override;
1109

1110
      //std::chrono::seconds session_ticket_lifetime() const override;
1111

1112
      size_t new_session_tickets_upon_handshake_success() const override {
259✔
1113
         return m_args.flag_set("no-ticket") ? 0 : 1;
517✔
1114
      }
1115

1116
      std::vector<uint16_t> srtp_profiles() const override {
503✔
1117
         if(m_args.option_used("srtp-profiles")) {
503✔
1118
            std::string srtp = m_args.get_string_opt("srtp-profiles");
4✔
1119

1120
            if(srtp == "SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32") {
4✔
1121
               return {1, 2};
3✔
1122
            } else if(srtp == "SRTP_AES128_CM_SHA1_80") {
1✔
1123
               return {1};
1✔
1124
            } else {
1125
               shim_exit_with_error("unknown srtp-profiles");
×
1126
            }
1127
         } else {
4✔
1128
            return {};
503✔
1129
         }
1130
      }
1131

1132
      bool only_resume_with_exact_version() const override { return false; }
198✔
1133

1134
      //bool server_uses_own_ciphersuite_preferences() const override;
1135

1136
      //bool negotiate_encrypt_then_mac() const override;
1137

1138
      bool support_cert_status_message() const override {
1,988✔
1139
         if(m_args.flag_set("server")) {
1,988✔
1140
            if(!m_args.option_used("ocsp-response")) {
529✔
1141
               return false;
1142
            }
1143
            if(m_args.flag_set("decline-ocsp-callback")) {
128✔
1144
               return false;
16✔
1145
            }
1146
         } else if(!m_args.flag_set("enable-ocsp-stapling")) {
2,918✔
1147
            return false;
1,330✔
1148
         }
1149

1150
         return true;
1151
      }
1152

1153
      std::vector<uint16_t> ciphersuite_list(Botan::TLS::Protocol_Version version) const override;
1154

1155
      size_t dtls_default_mtu() const override { return m_args.get_int_opt_or_else("mtu", 1500); }
693✔
1156

1157
      //size_t dtls_initial_timeout() const override;
1158

1159
      //size_t dtls_maximum_timeout() const override;
1160

1161
      bool abort_connection_on_undesired_renegotiation() const override {
6✔
1162
         if(m_args.flag_set("renegotiate-ignore")) {
12✔
1163
            return false;
1164
         } else {
1165
            return true;
5✔
1166
         }
1167
      }
1168

1169
      size_t maximum_certificate_chain_size() const override { return m_args.get_int_opt_or_else("max-cert-list", 0); }
1,240✔
1170

1171
      bool tls_13_middlebox_compatibility_mode() const override {
2,647✔
1172
         // These tests expect the client to send an alert in return of a malformed TLS 1.2 server hello.
1173
         // However, our TLS 1.3 implementation produces an alert without downgrading to TLS 1.2 first.
1174
         // In compatibility mode this prepends a CCS, which BoGo does not expect to read.
1175
         const std::vector<std::string> alert_after_server_hello = {
2,647✔
1176
            "DuplicateExtensionClient-TLS-TLS12",
1177
            "WrongMessageType-ServerHello-TLS",
1178
            "SendServerHelloAsHelloRetryRequest",
1179
            "TrailingMessageData-ServerHello-TLS",
1180
            "NoSSL3-Client-Unsolicited",
1181
            "Client-TooLongSessionID",
1182
            "MinimumVersion-Client-TLS13-TLS12-TLS",
1183
            "MinimumVersion-Client2-TLS13-TLS12-TLS",
1184
         };
23,823✔
1185
         if(Botan::value_exists(alert_after_server_hello, m_args.test_name())) {
5,214✔
1186
            return false;
14✔
1187
         }
1188

1189
         return true;
1190
      }
2,647✔
1191

1192
   private:
1193
      const Shim_Arguments& m_args;
1194
      size_t m_sessions;
1195
};
1196

1197
std::vector<uint16_t> Shim_Policy::ciphersuite_list(Botan::TLS::Protocol_Version version) const {
3,314✔
1198
   std::vector<uint16_t> ciphersuite_codes;
3,314✔
1199

1200
   const std::string cipher_limit = m_args.get_string_opt_or_else("cipher", "");
6,628✔
1201
   if(cipher_limit ==
3,314✔
1202
      "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:[TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384|TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256|TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA]:TLS_RSA_WITH_AES_128_GCM_SHA256:TLS_RSA_WITH_AES_128_CBC_SHA:[TLS_RSA_WITH_AES_256_GCM_SHA384|TLS_RSA_WITH_AES_256_CBC_SHA]") {
1203
      std::vector<std::string> suites = {
5✔
1204
         "ECDHE_RSA_WITH_AES_128_GCM_SHA256",
1205
         "ECDHE_RSA_WITH_AES_256_GCM_SHA384",
1206
         "ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
1207
         "ECDHE_RSA_WITH_AES_256_CBC_SHA",
1208
         "RSA_WITH_AES_256_GCM_SHA384",
1209
         "RSA_WITH_AES_256_CBC_SHA",
1210
      };
35✔
1211

1212
      for(const auto& suite_name : suites) {
35✔
1213
         const auto suite = Botan::TLS::Ciphersuite::from_name(suite_name);
30✔
1214
         if(!suite || !suite->valid()) {
30✔
1215
            shim_exit_with_error("Bad ciphersuite name " + suite_name);
×
1216
         }
1217
         ciphersuite_codes.push_back(suite->ciphersuite_code());
30✔
1218
      }
1219
   } else {
5✔
1220
      // Hack: go in reverse order to avoid preferring 3DES
1221
      auto ciphersuites = Botan::TLS::Ciphersuite::all_known_ciphersuites();
3,309✔
1222
      for(auto i = ciphersuites.rbegin(); i != ciphersuites.rend(); ++i) {
314,355✔
1223
         const auto suite = *i;
311,046✔
1224

1225
         // Can we use it?
1226
         if(suite.valid() == false || !suite.usable_in_version(version) ||
311,046✔
1227
            !Botan::value_exists(allowed_ciphers(), suite.cipher_algo())) {
507,625✔
1228
            continue;
140,215✔
1229
         }
1230

1231
         ciphersuite_codes.push_back(suite.ciphersuite_code());
170,831✔
1232
      }
1233
   }
3,309✔
1234

1235
   return ciphersuite_codes;
3,314✔
1236
}
3,314✔
1237

1238
class Shim_Credentials final : public Botan::Credentials_Manager {
1239
   public:
1240
      Shim_Credentials(const Shim_Arguments& args) : m_args(args) {
1,983✔
1241
         m_psk_identity = m_args.get_string_opt_or_else("psk-identity", "");
1,983✔
1242

1243
         const std::string psk_str = m_args.get_string_opt_or_else("psk", "");
3,966✔
1244
         m_psk = Botan::SymmetricKey(reinterpret_cast<const uint8_t*>(psk_str.data()), psk_str.size());
1,983✔
1245

1246
         if(m_args.option_used("key-file") && m_args.option_used("cert-file")) {
2,945✔
1247
            Botan::DataSource_Stream key_stream(m_args.get_string_opt("key-file"));
1,924✔
1248
            m_key.reset(Botan::PKCS8::load_key(key_stream).release());
962✔
1249

1250
            Botan::DataSource_Stream cert_stream(m_args.get_string_opt("cert-file"));
1,924✔
1251

1252
            while(!cert_stream.end_of_data()) {
2,892✔
1253
               try {
1,930✔
1254
                  m_cert_chain.push_back(Botan::X509_Certificate(cert_stream));
2,898✔
1255
               } catch(...) {}
962✔
1256
            }
1257
         }
962✔
1258
      }
1,983✔
1259

1260
      std::vector<Botan::Certificate_Store*> trusted_certificate_authorities(const std::string& type,
1,922✔
1261
                                                                             const std::string& context) override {
1262
         if(m_args.flag_set("server") && type != "tls-server") {
3,844✔
1263
            throw Shim_Exception("TLS server implementation asked for unexpected trusted CA type: " + type);
×
1264
         }
1265
         if(!m_args.flag_set("server") && type != "tls-client") {
3,844✔
1266
            throw Shim_Exception("TLS client implementation asked for unexpected trusted CA type: " + type);
×
1267
         }
1268

1269
         const auto expected_hostname = m_args.get_string_opt_or_else("host-name", "none");
3,844✔
1270
         if(expected_hostname != "none" && expected_hostname != context) {
1,922✔
1271
            throw Shim_Exception("Unexpected host name in trusted CA request: " + context);
×
1272
         }
1273

1274
         return Botan::Credentials_Manager::trusted_certificate_authorities(type, context);
1,922✔
1275
      }
1,922✔
1276

1277
      std::string psk_identity(const std::string& /*type*/,
48✔
1278
                               const std::string& /*context*/,
1279
                               const std::string& /*identity_hint*/) override {
1280
         return m_psk_identity;
48✔
1281
      }
1282

1283
      std::string psk_identity_hint(const std::string& /*type*/, const std::string& /*context*/) override {
26✔
1284
         return m_psk_identity;
26✔
1285
      }
1286

1287
      Botan::SymmetricKey psk(const std::string& type,
2,379✔
1288
                              const std::string& context,
1289
                              const std::string& identity) override {
1290
         if(type == "tls-server" && context == "session-ticket") {
2,379✔
1291
            return Botan::SymmetricKey("ABCDEF0123456789");
1,695✔
1292
         }
1293

1294
         if(type == "tls-server" && context == "dtls-cookie-secret") {
684✔
1295
            return Botan::SymmetricKey("F00FB00FD00F100F700F");
610✔
1296
         }
1297

1298
         if(identity != m_psk_identity) {
74✔
1299
            throw Shim_Exception("Unexpected PSK identity");
×
1300
         }
1301
         return m_psk;
2,379✔
1302
      }
1303

1304
      std::vector<Botan::X509_Certificate> cert_chain(
1,999✔
1305
         const std::vector<std::string>& cert_key_types,
1306
         const std::vector<Botan::AlgorithmIdentifier>& /*cert_signature_schemes*/,
1307
         const std::string& /*type*/,
1308
         const std::string& /*context*/) override {
1309
         if(m_args.flag_set("fail-cert-callback")) {
3,998✔
1310
            throw std::runtime_error("Simulating cert verify callback failure");
4✔
1311
         }
1312

1313
         if(m_key != nullptr && !m_cert_chain.empty()) {
1,995✔
1314
            for(const std::string& t : cert_key_types) {
3,125✔
1315
               if(t == m_key->algo_name()) {
2,045✔
1316
                  return m_cert_chain;
888✔
1317
               }
1318
            }
1319
         }
1320

1321
         return {};
1,995✔
1322
      }
1323

1324
      std::shared_ptr<Botan::Private_Key> private_key_for(const Botan::X509_Certificate& /*cert*/,
829✔
1325
                                                          const std::string& /*type*/,
1326
                                                          const std::string& /*context*/) override {
1327
         // assumes cert == m_cert
1328
         return m_key;
829✔
1329
      }
1330

1331
   private:
1332
      const Shim_Arguments& m_args;
1333
      Botan::SymmetricKey m_psk;
1334
      std::string m_psk_identity;
1335
      std::shared_ptr<Botan::Private_Key> m_key;
1336
      std::vector<Botan::X509_Certificate> m_cert_chain;
1337
};
1338

1339
class Shim_Callbacks final : public Botan::TLS::Callbacks {
2,581✔
1340
   public:
1341
      Shim_Callbacks(const Shim_Arguments& args, Shim_Socket& socket, Shim_Policy& policy) :
2,598✔
1342
            m_channel(nullptr),
2,598✔
1343
            m_args(args),
2,598✔
1344
            m_policy(policy),
2,598✔
1345
            m_socket(socket),
2,598✔
1346
            m_is_datagram(args.flag_set("dtls")),
2,598✔
1347
            m_warning_alerts(0),
2,598✔
1348
            m_empty_records(0),
2,598✔
1349
            m_sessions_established(0),
2,598✔
1350
            m_got_close(false),
2,598✔
1351
            m_hello_retry_request(false),
2,598✔
1352
            m_clock_skew(0) {}
5,196✔
1353

1354
      size_t sessions_established() const { return m_sessions_established; }
21✔
1355

1356
      void set_channel(Botan::TLS::Channel* channel) { m_channel = channel; }
2,596✔
1357

1358
      void set_clock_skew(std::chrono::seconds clock_skew) { m_clock_skew = clock_skew; }
3✔
1359

1360
      bool saw_close_notify() const { return m_got_close; }
27✔
1361

1362
      void tls_emit_data(std::span<const uint8_t> data) override {
15,861✔
1363
         shim_log("sending record of len " + std::to_string(data.size()));
31,722✔
1364

1365
         if(m_args.option_used("write-settings")) {
15,861✔
1366
            // TODO: the transcript option should probably be used differently
1367
            std::cout << ">>>" << std::endl << Botan::hex_encode(data) << std::endl << ">>>" << std::endl;
×
1368
         }
1369

1370
         if(m_is_datagram) {
15,861✔
1371
            std::vector<uint8_t> packet(data.size() + 5);
5,926✔
1372

1373
            packet[0] = 'P';
5,926✔
1374
            for(size_t i = 0; i != 4; ++i) {
29,630✔
1375
               packet[i + 1] = static_cast<uint8_t>((data.size() >> (24 - 8 * i)) & 0xFF);
23,704✔
1376
            }
1377
            std::memcpy(packet.data() + 5, data.data(), data.size());
5,926✔
1378

1379
            m_socket.write(packet.data(), packet.size());
5,926✔
1380
         } else {
5,925✔
1381
            m_socket.write(data.data(), data.size());
9,935✔
1382
         }
1383
      }
15,859✔
1384

1385
      std::vector<uint8_t> tls_provide_cert_status(const std::vector<Botan::X509_Certificate>&,
737✔
1386
                                                   const Botan::TLS::Certificate_Status_Request&) override {
1387
         if(m_args.flag_set("use-ocsp-callback") && m_args.flag_set("fail-ocsp-callback")) {
1,594✔
1388
            throw std::runtime_error("Simulating failure from OCSP response callback");
24✔
1389
         }
1390

1391
         if(m_args.flag_set("decline-ocsp-callback")) {
1,426✔
1392
            return {};
24✔
1393
         }
1394

1395
         if(m_args.option_used("ocsp-response")) {
689✔
1396
            return m_args.get_b64_opt("ocsp-response");
96✔
1397
         }
1398

1399
         return {};
713✔
1400
      }
1401

1402
      void tls_record_received(uint64_t /*seq_no*/, std::span<const uint8_t> data) override {
2,506✔
1403
         if(data.empty()) {
2,506✔
1404
            m_empty_records += 1;
98✔
1405
            if(m_empty_records > 32) {
98✔
1406
               shim_exit_with_error(":TOO_MANY_EMPTY_FRAGMENTS:");
2✔
1407
            }
1408
         } else {
1409
            m_empty_records = 0;
2,408✔
1410
         }
1411

1412
         shim_log("Reflecting application_data len " + std::to_string(data.size()));
5,008✔
1413

1414
         std::vector<uint8_t> buf(data.begin(), data.end());
2,504✔
1415
         for(auto& b : buf) {
1,183,219✔
1416
            b ^= 0xFF;
1,180,715✔
1417
         }
1418

1419
         m_channel->send(buf);
2,504✔
1420
      }
2,504✔
1421

1422
      bool tls_verify_message(const Botan::Public_Key& key,
1,003✔
1423
                              std::string_view padding,
1424
                              Botan::Signature_Format format,
1425
                              const std::vector<uint8_t>& msg,
1426
                              const std::vector<uint8_t>& sig) override {
1427
         if(m_args.option_used("expect-peer-signature-algorithm")) {
2,006✔
1428
            const Botan::TLS::Signature_Scheme scheme(
65✔
1429
               static_cast<uint16_t>(m_args.get_int_opt("expect-peer-signature-algorithm")));
65✔
1430

1431
            if(!scheme.is_available()) {
65✔
1432
               shim_exit_with_error(std::string("Unsupported signature scheme provided by BoGo: ") +
×
1433
                                    scheme.to_string());
×
1434
            }
1435

1436
            const std::string exp_padding = scheme.padding_string();
65✔
1437
            if(padding != exp_padding) {
130✔
1438
               shim_exit_with_error(Botan::fmt("Unexpected signature scheme got {} expected {}", padding, exp_padding));
×
1439
            }
1440
         }
65✔
1441

1442
         return Botan::TLS::Callbacks::tls_verify_message(key, padding, format, msg, sig);
1,003✔
1443
      }
1444

1445
      void tls_verify_cert_chain(const std::vector<Botan::X509_Certificate>& /*cert_chain*/,
1,169✔
1446
                                 const std::vector<std::optional<Botan::OCSP::Response>>& /*ocsp_responses*/,
1447
                                 const std::vector<Botan::Certificate_Store*>& /*trusted_roots*/,
1448
                                 Botan::Usage_Type /*usage*/,
1449
                                 std::string_view /*hostname*/,
1450
                                 const Botan::TLS::Policy& /*policy*/) override {
1451
         if(m_args.flag_set("enable-ocsp-stapling") && m_args.flag_set("use-ocsp-callback") &&
3,579✔
1452
            m_args.flag_set("fail-ocsp-callback")) {
1,313✔
1453
            throw Botan::TLS::TLS_Exception(Botan::TLS::Alert::BadCertificateStatusResponse,
48✔
1454
                                            "Simulated OCSP callback failure");
96✔
1455
         }
1456

1457
         if(m_args.flag_set("verify-fail")) {
1,121✔
1458
            auto alert = Botan::TLS::Alert::HandshakeFailure;
96✔
1459
            if(m_args.flag_set("use-custom-verify-callback")) {
192✔
1460
               alert = Botan::TLS::Alert::CertificateUnknown;
48✔
1461
            }
1462

1463
            throw Botan::TLS::TLS_Exception(alert, "Test requires rejecting cert");
96✔
1464
         }
1465
      }
1,025✔
1466

1467
      std::optional<Botan::OCSP::Response> tls_parse_ocsp_response(const std::vector<uint8_t>& raw_response) override {
73✔
1468
         if(m_args.option_used("expect-ocsp-response") && m_args.get_b64_opt("expect-ocsp-response") != raw_response) {
221✔
1469
            shim_exit_with_error("unexpected OCSP response");
×
1470
         }
1471

1472
         // Bogo uses invalid dummy OCSP responses. Don't even bother trying to
1473
         // decode them.
1474
         return std::nullopt;
73✔
1475
      }
1476

1477
      std::string tls_server_choose_app_protocol(const std::vector<std::string>& client_protos) override {
21✔
1478
         if(client_protos.empty()) {
21✔
1479
            return "";  // shouldn't happen?
×
1480
         }
1481

1482
         if(m_args.flag_set("reject-alpn")) {
21✔
1483
            throw Botan::TLS::TLS_Exception(Botan::TLS::Alert::NoApplicationProtocol,
3✔
1484
                                            "Rejecting ALPN request with alert");
6✔
1485
         }
1486

1487
         if(m_args.flag_set("decline-alpn")) {
18✔
1488
            return "";
6✔
1489
         }
1490

1491
         if(m_args.option_used("expect-advertised-alpn")) {
24✔
1492
            const std::vector<std::string> expected = m_args.get_alpn_string_vec_opt("expect-advertised-alpn");
12✔
1493

1494
            if(client_protos != expected) {
12✔
1495
               shim_exit_with_error("Bad ALPN from client");
×
1496
            }
1497
         }
12✔
1498

1499
         if(m_args.option_used("select-alpn")) {
12✔
1500
            return m_args.get_string_opt("select-alpn");
24✔
1501
         }
1502

1503
         return client_protos[0];  // if not configured just pick something
18✔
1504
      }
1505

1506
      void tls_alert(Botan::TLS::Alert alert) override {
1,865✔
1507
         if(alert.is_fatal()) {
1,865✔
1508
            shim_log("Got a fatal alert " + alert.type_string());
24✔
1509
         } else {
1510
            shim_log("Got a warning alert " + alert.type_string());
3,706✔
1511
         }
1512

1513
         if(alert.type() == Botan::TLS::Alert::RecordOverflow) {
1,865✔
1514
            shim_exit_with_error(":TLSV1_ALERT_RECORD_OVERFLOW:");
4✔
1515
         }
1516

1517
         if(alert.type() == Botan::TLS::Alert::DecompressionFailure) {
1,861✔
1518
            shim_exit_with_error(":SSLV3_ALERT_DECOMPRESSION_FAILURE:");
1✔
1519
         }
1520

1521
         if(!alert.is_fatal()) {
1,860✔
1522
            m_warning_alerts++;
1,853✔
1523
            if(m_warning_alerts > 5) {
1,853✔
1524
               shim_exit_with_error(":TOO_MANY_WARNING_ALERTS:");
3✔
1525
            }
1526
         }
1527

1528
         if(alert.type() == Botan::TLS::Alert::CloseNotify) {
1,857✔
1529
            if(m_got_close == false && !m_args.flag_set("shim-shuts-down")) {
3,660✔
1530
               shim_log("Sending return close notify");
1,803✔
1531
               m_channel->send_alert(alert);
1,803✔
1532
            }
1533
            m_got_close = true;
1,821✔
1534
         } else if(alert.is_fatal()) {
36✔
1535
            shim_exit_with_error("Unexpected fatal alert " + alert.type_string());
7✔
1536
         }
1537
      }
1,850✔
1538

1539
      void tls_session_established(const Botan::TLS::Session_Summary& session) override {
1,978✔
1540
         shim_log("Session established: " + Botan::hex_encode(session.session_id().get()) + " version " +
5,934✔
1541
                  session.version().to_string() + " cipher " + session.ciphersuite().to_string() + " EMS " +
11,868✔
1542
                  std::to_string(session.supports_extended_master_secret()));
1,978✔
1543
         // probably need tests here?
1544

1545
         m_policy.incr_session_established();
1,978✔
1546
         m_sessions_established++;
1,978✔
1547

1548
         if(m_args.flag_set("expect-no-session-id")) {
3,956✔
1549
            // BoGo expects that ticket issuance implies no stateful session...
1550
            if(!m_args.flag_set("server") && !session.session_id().empty()) {
112✔
1551
               shim_exit_with_error("Unexpectedly got a session ID");
×
1552
            }
1553
         } else if(m_args.flag_set("expect-session-id") && session.session_id().empty()) {
3,844✔
1554
            shim_exit_with_error("Unexpectedly got no session ID");
×
1555
         }
1556

1557
         if(m_args.option_used("expect-version")) {
1,978✔
1558
            if(session.version().version_code() != m_args.get_int_opt("expect-version")) {
×
1559
               shim_exit_with_error("Unexpected version");
×
1560
            }
1561
         }
1562

1563
         if(m_args.flag_set("expect-secure-renegotiation")) {
3,956✔
1564
            if(m_channel->secure_renegotiation_supported() == false) {
9✔
1565
               shim_exit_with_error("Expected secure renegotiation");
×
1566
            }
1567
         } else if(m_args.flag_set("expect-no-secure-renegotiation")) {
3,938✔
1568
            if(m_channel->secure_renegotiation_supported() == true) {
2✔
1569
               shim_exit_with_error("Expected no secure renegotation");
×
1570
            }
1571
         }
1572

1573
         if(m_args.flag_set("expect-extended-master-secret")) {
3,956✔
1574
            if(session.supports_extended_master_secret() == false) {
10✔
1575
               shim_exit_with_error("Expected extended maseter secret");
×
1576
            }
1577
         }
1578
      }
1,978✔
1579

1580
      void tls_session_activated() override {
1,967✔
1581
         if(m_args.flag_set("send-alert")) {
1,967✔
1582
            m_channel->send_fatal_alert(Botan::TLS::Alert::DecompressionFailure);
16✔
1583
            return;
16✔
1584
         }
1585

1586
         if(size_t length = m_args.get_int_opt_or_else("export-keying-material", 0)) {
3,902✔
1587
            const std::string label = m_args.get_string_opt("export-label");
188✔
1588
            const std::string context = m_args.get_string_opt("export-context");
188✔
1589
            const auto exported = m_channel->key_material_export(label, context, length);
188✔
1590
            shim_log("Sending " + std::to_string(length) + " bytes of key material");
376✔
1591
            m_channel->send(exported.bits_of());
376✔
1592
         }
188✔
1593

1594
         const std::string alpn = m_channel->application_protocol();
1,951✔
1595

1596
         if(m_args.option_used("expect-alpn")) {
1,951✔
1597
            if(alpn != m_args.get_string_opt("expect-alpn")) {
10✔
1598
               shim_exit_with_error("Got unexpected ALPN");
×
1599
            }
1600
         }
1601

1602
         if(alpn == "baz" && !m_args.flag_set("allow-unknown-alpn-protos")) {
1,960✔
1603
            throw Botan::TLS::TLS_Exception(Botan::TLS::Alert::IllegalParameter, "Unexpected ALPN protocol");
3✔
1604
         }
1605

1606
         if(m_args.flag_set("shim-shuts-down")) {
1,948✔
1607
            shim_log("Shim shutting down");
45✔
1608
            m_channel->close();
45✔
1609
         }
1610

1611
         if(m_args.flag_set("write-different-record-sizes")) {
3,896✔
1612
            static const size_t record_sizes[] = {0, 1, 255, 256, 257, 16383, 16384, 16385, 32767, 32768, 32769};
×
1613

1614
            std::vector<uint8_t> buf(32769, 0x42);
×
1615

1616
            for(size_t sz : record_sizes) {
×
1617
               m_channel->send(buf.data(), sz);
×
1618
            }
1619

1620
            m_channel->close();
×
1621
         }
×
1622

1623
         if(m_args.flag_set("expect-hrr") && !m_hello_retry_request) {
3,896✔
1624
            throw Shim_Exception("Expected Hello Retry Request but didn't see one");
×
1625
         }
1626

1627
         if(m_args.flag_set("expect-no-hrr") && m_hello_retry_request) {
3,896✔
1628
            throw Shim_Exception("Hello Retry Request seen but didn't expect one");
×
1629
         }
1630

1631
         if(m_args.flag_set("key-update")) {
1,948✔
1632
            shim_log("Updating traffic keys without asking for reciprocation");
5✔
1633
            m_channel->update_traffic_keys(false /* don't request reciprocal update */);
2✔
1634
         }
1635
      }
1,948✔
1636

1637
      std::chrono::system_clock::time_point tls_current_timestamp() override {
8,573✔
1638
         // Some tests require precise timings. Hence, the TLS 'now' timestamp
1639
         // is frozen on first access and rounded to the last full second. E.g.
1640
         // storage of sessions does store the timestamp with second-resolution.
1641
         using sec = std::chrono::seconds;
8,573✔
1642
         static auto g_now = std::chrono::floor<sec>(std::chrono::system_clock::now());
10,484✔
1643
         return g_now + m_clock_skew;
8,573✔
1644
      }
1645

1646
      void tls_inspect_handshake_msg(const Botan::TLS::Handshake_Message& msg) override {
20,297✔
1647
         if(msg.type() == Botan::TLS::Handshake_Type::HelloRetryRequest) {
20,297✔
1648
            m_hello_retry_request = true;
67✔
1649
         }
1650
      }
20,297✔
1651

1652
   private:
1653
      Botan::TLS::Channel* m_channel;
1654
      const Shim_Arguments& m_args;
1655
      Shim_Policy& m_policy;
1656
      Shim_Socket& m_socket;
1657
      const bool m_is_datagram;
1658
      size_t m_warning_alerts;
1659
      size_t m_empty_records;
1660
      size_t m_sessions_established;
1661
      bool m_got_close;
1662
      bool m_hello_retry_request;
1663
      std::chrono::seconds m_clock_skew;
1664
};
1665

1666
}  // namespace
1667

1668
int main(int /*argc*/, char* argv[]) {
1,984✔
1669
   try {
1,984✔
1670
      std::unique_ptr<Shim_Arguments> args = parse_options(argv);
1,984✔
1671

1672
      if(args->flag_set("is-handshaker-supported")) {
3,968✔
1673
         return shim_output("No\n");
1✔
1674
      }
1675

1676
      const uint16_t port = static_cast<uint16_t>(args->get_int_opt("port"));
1,983✔
1677
      const size_t resume_count = args->get_int_opt_or_else("resume-count", 0);
1,983✔
1678
      const bool is_server = args->flag_set("server");
1,983✔
1679
      const bool is_datagram = args->flag_set("dtls");
1,983✔
1680
      const size_t buf_size = args->get_int_opt_or_else("read-size", 18 * 1024);
1,983✔
1681

1682
      auto rng = std::make_shared<Botan::ChaCha_RNG>(Botan::secure_vector<uint8_t>(64));
3,966✔
1683
      auto creds = std::make_shared<Shim_Credentials>(*args);
1,983✔
1684
      auto session_manager = [&]() -> std::shared_ptr<Botan::TLS::Session_Manager> {
3,966✔
1685
         if(args->flag_set("no-ticket") || args->flag_set("on-resume-no-ticket")) {
3,964✔
1686
            // The in-memory session manager stores sessions in volatile memory and
1687
            // hands out Session_IDs (i.e. does not utilize session tickets)
1688
            return std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng, 1024);
2✔
1689
         } else {
1690
            // The hybrid session manager prefers stateless tickets (when used in
1691
            // servers) but can also fall back to stateful management when tickets
1692
            // are not an option.
1693
            return std::make_shared<Botan::TLS::Session_Manager_Hybrid>(
1,981✔
1694
               std::make_unique<Botan::TLS::Session_Manager_In_Memory>(rng, 1024), creds, rng);
3,962✔
1695
         }
1696
      }();
2,718✔
1697

1698
      if(args->flag_set("wait-for-debugger")) {
4,701✔
1699
         sleep(20);
×
1700
      }
1701

1702
      for(size_t i = 0; i != resume_count + 1; ++i) {
3,829✔
1703
         auto execute_test = [&](const std::string& hostname) {
5,196✔
1704
            Shim_Socket socket(hostname, port, args->flag_set("ipv6"));
5,199✔
1705

1706
            shim_log("Connection " + std::to_string(i + 1) + "/" + std::to_string(resume_count + 1));
5,196✔
1707

1708
            // The ShimID must be written on the socket as a 64-bit little-endian integer
1709
            // *before* any test data is transferred
1710
            // See: https://github.com/google/boringssl/commit/50ee09552cde1c2019bef24520848d041920cfd4
1711
            shim_log("Sending ShimID: " + std::to_string(args->get_int_opt("shim-id")));
5,196✔
1712
            std::array<uint8_t, 8> shim_id;
2,598✔
1713
            Botan::store_le(static_cast<uint64_t>(args->get_int_opt("shim-id")), shim_id.data());
3,333✔
1714
            socket.write(shim_id.data(), shim_id.size());
2,598✔
1715

1716
            auto policy = std::make_shared<Shim_Policy>(*args);
2,598✔
1717
            auto callbacks = std::make_shared<Shim_Callbacks>(*args, socket, *policy);
2,598✔
1718

1719
            if(args->option_used("resumption-delay") && i > 0) {
5,196✔
1720
               shim_log("skewing the clock by " + std::to_string(args->get_int_opt("resumption-delay")) + " seconds");
6✔
1721
               callbacks->set_clock_skew(std::chrono::seconds(args->get_int_opt("resumption-delay")));
741✔
1722
            }
1723

1724
            std::unique_ptr<Botan::TLS::Channel> chan;
2,598✔
1725

1726
            if(is_server) {
2,598✔
1727
               chan = std::make_unique<Botan::TLS::Server>(callbacks, session_manager, creds, policy, rng, is_datagram);
2,598✔
1728
            } else {
1729
               Botan::TLS::Protocol_Version offer_version = policy->latest_supported_version(is_datagram);
1,428✔
1730
               shim_log("Offering " + offer_version.to_string());
2,852✔
1731

1732
               std::string host_name = args->get_string_opt_or_else("host-name", hostname);
1,426✔
1733
               if(args->test_name().find("UnsolicitedServerNameAck") == 0) {
2,824✔
1734
                  host_name = "";  // avoid sending SNI for this test
3✔
1735
               }
1736

1737
               Botan::TLS::Server_Information server_info(host_name, port);
1,426✔
1738
               const std::vector<std::string> next_protocols = args->get_alpn_string_vec_opt("advertise-alpn");
1,426✔
1739
               chan = std::make_unique<Botan::TLS::Client>(
1,426✔
1740
                  callbacks, session_manager, creds, policy, rng, server_info, offer_version, next_protocols);
1,426✔
1741
            }
1,426✔
1742

1743
            callbacks->set_channel(chan.get());
2,596✔
1744

1745
            std::vector<uint8_t> buf(buf_size);
3,331✔
1746

1747
            for(;;) {
161,257✔
1748
               if(is_datagram) {
161,257✔
1749
                  uint8_t opcode;
127,017✔
1750
                  size_t got = socket.read(&opcode, 1);
127,017✔
1751
                  if(got == 0) {
127,017✔
1752
                     shim_log("EOF on socket");
570✔
1753
                     break;
570✔
1754
                  }
1755

1756
                  if(opcode == 'P') {
126,447✔
1757
                     uint8_t len_bytes[4];
126,447✔
1758
                     socket.read_exactly(len_bytes, sizeof(len_bytes));
126,447✔
1759

1760
                     size_t packet_len = Botan::load_be<uint32_t>(len_bytes, 0);
126,447✔
1761

1762
                     if(buf.size() < packet_len) {
126,447✔
1763
                        buf.resize(packet_len);
1✔
1764
                     }
1765
                     socket.read_exactly(buf.data(), packet_len);
126,447✔
1766

1767
                     chan->received_data(buf.data(), packet_len);
126,447✔
1768
                  } else if(opcode == 'T') {
×
1769
                     uint8_t timeout_ack = 't';
×
1770

1771
                     uint8_t timeout_bytes[8];
×
1772
                     socket.read_exactly(timeout_bytes, sizeof(timeout_bytes));
×
1773

1774
                     const uint64_t nsec = Botan::load_be<uint64_t>(timeout_bytes, 0);
×
1775

1776
                     shim_log("Timeout nsec " + std::to_string(nsec));
×
1777

1778
                     // FIXME handle this!
1779

1780
                     socket.write(&timeout_ack, 1);  // ack it anyway
×
1781
                  } else {
1782
                     shim_exit_with_error("Unknown opcode " + std::to_string(opcode));
×
1783
                  }
1784
               } else {
1785
                  size_t got = socket.read(buf.data(), buf.size());
34,240✔
1786
                  if(got == 0) {
34,240✔
1787
                     shim_log("EOF on socket");
1,277✔
1788
                     break;
1,277✔
1789
                  }
1790

1791
                  shim_log("Got packet of " + std::to_string(got));
65,926✔
1792

1793
                  if(args->option_used("write-settings")) {
32,963✔
1794
                     // TODO: the transcript option should probably be used differently
1795
                     std::cout << "<<<" << std::endl
×
1796
                               << Botan::hex_encode(buf.data(), got) << std::endl
×
1797
                               << "<<<" << std::endl;
×
1798
                  }
1799

1800
                  if(args->flag_set("use-exporter-between-reads") && chan->is_active()) {
65,926✔
1801
                     chan->key_material_export("some label", "some context", 42);
1✔
1802
                  }
1803
                  const size_t needed = chan->received_data(buf.data(), got);
32,962✔
1804

1805
                  if(needed) {
32,335✔
1806
                     shim_log("Short read still need " + std::to_string(needed));
38,192✔
1807
                  }
1808
               }
1809
            }
1810

1811
            if(args->flag_set("check-close-notify")) {
3,694✔
1812
               if(!callbacks->saw_close_notify()) {
27✔
1813
                  throw Shim_Exception("Unexpected SSL_shutdown result: -1 != 1");
1✔
1814
               }
1815
            }
1816

1817
            if(args->option_used("expect-total-renegotiations")) {
3,692✔
1818
               const size_t exp = args->get_int_opt("expect-total-renegotiations");
21✔
1819

1820
               if(exp != callbacks->sessions_established() - 1) {
21✔
1821
                  throw Shim_Exception("Unexpected number of renegotiations: saw " +
×
1822
                                       std::to_string(callbacks->sessions_established() - 1) + " exp " +
×
1823
                                       std::to_string(exp));
×
1824
               }
1825
            }
1826
            shim_log("End of resume loop");
4,425✔
1827
         };
10,322✔
1828
         try {
2,598✔
1829
            execute_test("localhost");
5,179✔
1830
         } catch(const Shim_Exception& e) {
735✔
1831
            if(std::string(e.what()) == "Failed to connect to host") {
6✔
1832
               execute_test("::1");
×
1833
            } else {
1834
               // NOLINTNEXTLINE(cert-err60-cpp)
1835
               throw e;
3✔
1836
            }
1837
         }
3✔
1838
      }
1839
   } catch(Shim_Exception& e) {
6,634✔
1840
      shim_exit_with_error(e.what(), e.rc());
3✔
1841
   } catch(std::exception& e) {
732✔
1842
      shim_exit_with_error(map_to_bogo_error(e.what()));
732✔
1843
   } catch(...) {
×
1844
      shim_exit_with_error("Unknown exception", 3);
×
1845
   }
×
1846
   return 0;
1,231✔
1847
}
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