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

randombit / botan / 22048403227

16 Feb 2026 02:47AM UTC coverage: 90.036% (-0.008%) from 90.044%
22048403227

push

github

web-flow
Merge pull request #5315 from randombit/jack/no-chrono-in-pwdhash

Add PasswordHashFamily::tune_params

102325 of 113649 relevant lines covered (90.04%)

11636648.16 hits per line

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

87.53
/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/certstor.h>
15
#include <botan/chacha_rng.h>
16
#include <botan/data_src.h>
17
#include <botan/hex.h>
18
#include <botan/mem_ops.h>
19
#include <botan/ocsp.h>
20
#include <botan/pkcs8.h>
21
#include <botan/tls_algos.h>
22
#include <botan/tls_callbacks.h>
23
#include <botan/tls_client.h>
24
#include <botan/tls_exceptn.h>
25
#include <botan/tls_extensions.h>
26
#include <botan/tls_messages.h>
27
#include <botan/tls_policy.h>
28
#include <botan/tls_server.h>
29
#include <botan/tls_session_manager_hybrid.h>
30
#include <botan/tls_session_manager_memory.h>
31
#include <botan/internal/concat_util.h>
32
#include <botan/internal/fmt.h>
33
#include <botan/internal/loadstor.h>
34
#include <botan/internal/parsing.h>
35
#include <botan/internal/stl_util.h>
36
#include <botan/internal/target_info.h>
37

38
#include <cstring>
39
#include <ctime>
40
#include <fstream>
41
#include <iomanip>
42
#include <iostream>
43
#include <map>
44
#include <memory>
45
#include <set>
46
#include <string>
47
#include <unordered_map>
48
#include <vector>
49

50
#if defined(BOTAN_TARGET_OS_HAS_SOCKETS)
51
   #include <errno.h>
52
   #include <fcntl.h>
53
   #include <netdb.h>
54
   #include <netinet/in.h>
55
   #include <string.h>
56
   #include <sys/socket.h>
57
   #include <sys/time.h>
58
   #include <unistd.h>
59
#endif
60

61
namespace {
62

63
int shim_output(const std::string& s, int rc = 0) {
1✔
64
   std::cout << s << "\n";
1✔
65
   return rc;
1✔
66
}
67

68
void shim_log(std::string_view s) {
96,518✔
69
   static const auto log_path = []() -> std::string {
2,107✔
70
      const char* env = ::getenv("BOTAN_BOGO_SHIM_LOG");
2,107✔
71
      if(env == nullptr) {
2,107✔
72
         return {};
2,107✔
73
      }
74

75
      auto log_file_path = std::string(env);
×
76
      if(log_file_path.empty() || log_file_path == "1") {
×
77
         return "/tmp/bogo_shim.log";
×
78
      }
79
      return env;
×
80
   }();
96,518✔
81

82
   if(!log_path.empty()) {
96,518✔
83
      static std::ofstream g_log(log_path, std::ios::out | std::ios::trunc);
×
84
      if(g_log.is_open() && g_log.good()) {
×
85
         const auto duration = std::chrono::system_clock::now().time_since_epoch();
×
86
         const auto seconds = std::chrono::duration_cast<std::chrono::duration<double>>(duration);
×
87

88
         g_log << std::fixed << std::setprecision(6) << seconds.count() << ": " << s << std::endl;
×
89
      }
90
   }
91
}
96,518✔
92

93
[[noreturn]] void shim_exit_with_error(const std::string& s, int rc = 1) noexcept {
823✔
94
   shim_log("Exiting with " + s);
823✔
95
   std::cerr << s << "\n";
823✔
96
   std::exit(rc);
823✔
97
}
98

99
std::string map_to_bogo_error(const std::string& e) noexcept {
804✔
100
   shim_log("Original error " + e);
804✔
101

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

350
   auto err_map_i = err_map.find(e);
804✔
351
   if(err_map_i != err_map.end()) {
804✔
352
      return err_map_i->second;
800✔
353
   }
354

355
   return "Unmapped error: '" + e + "'";
8✔
356
}
804✔
357

358
class Shim_Exception final : public std::exception {
359
   public:
360
      explicit Shim_Exception(std::string_view msg, int rc = 1) : m_msg(msg), m_rc(rc) {}
2✔
361

362
      const char* what() const noexcept override { return m_msg.c_str(); }
2✔
363

364
      int rc() const { return m_rc; }
1✔
365

366
   private:
367
      const std::string m_msg;
368
      int m_rc;
369
};
370

371
#if defined(BOTAN_TARGET_OS_HAS_SOCKETS)
372

373
class Shim_Socket final {
374
   private:
375
      typedef int socket_type;
376
      typedef ssize_t socket_op_ret_type;
377

378
      static void close_socket(socket_type s) { ::close(s); }
379

380
      static std::string get_last_socket_error() { return ::strerror(errno); }
381

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

384
   public:
385
      Shim_Socket(const std::string& hostname, int port, const bool ipv6) : m_socket(-1) {
2,715✔
386
         addrinfo hints{};
2,715✔
387
         std::memset(&hints, 0, sizeof(hints));
2,715✔
388
         hints.ai_family = AF_UNSPEC;
2,715✔
389
         hints.ai_socktype = SOCK_STREAM;
2,715✔
390
         hints.ai_flags = AI_NUMERICSERV;
2,715✔
391

392
         const std::string service = std::to_string(port);
2,715✔
393

394
         // TODO: C++23 will introduce std::out_ptr() that should replace the
395
         //       temporary variable for the call to ::getaddrinfo() and
396
         //       std::unique_ptr<>::reset().
397
         unique_addrinfo_t::pointer res_tmp = nullptr;
2,715✔
398
         const int rc = ::getaddrinfo(hostname.c_str(), service.c_str(), &hints, &res_tmp);
2,715✔
399
         const unique_addrinfo_t res(res_tmp, &::freeaddrinfo);
2,715✔
400

401
         shim_log("Connecting " + hostname + ":" + service);
10,860✔
402

403
         if(rc != 0) {
2,715✔
404
            throw Shim_Exception("Name resolution failed for " + hostname);
×
405
         }
406

407
         for(const addrinfo* rp = res.get(); (m_socket == -1) && (rp != nullptr); rp = rp->ai_next) {
5,430✔
408
            if((!ipv6 && rp->ai_family != AF_INET) || (ipv6 && rp->ai_family != AF_INET6)) {
2,715✔
409
               continue;
×
410
            }
411

412
            m_socket = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
2,715✔
413

414
            if(m_socket == -1) {
2,715✔
415
               // unsupported socket type?
416
               continue;
×
417
            }
418

419
            const int err = ::connect(m_socket, rp->ai_addr, rp->ai_addrlen);
2,715✔
420

421
            if(err != 0) {
2,715✔
422
               ::close(m_socket);
×
423
               m_socket = -1;
×
424
            }
425
         }
426

427
         if(m_socket < 0) {
2,715✔
428
            throw Shim_Exception("Failed to connect to host");
×
429
         }
430
      }
2,715✔
431

432
      Shim_Socket(const Shim_Socket&) = delete;
433
      Shim_Socket& operator=(const Shim_Socket&) = delete;
434

435
      Shim_Socket(Shim_Socket&&) = delete;
436
      Shim_Socket& operator=(Shim_Socket&&) = delete;
437

438
      ~Shim_Socket() {
2,697✔
439
         ::close(m_socket);
2,697✔
440
         m_socket = -1;
2,697✔
441
      }
2,697✔
442

443
      void write(const uint8_t buf[], size_t len) const {
19,056✔
444
         if(m_socket < 0) {
19,056✔
445
            throw Shim_Exception("Socket was bad on write");
×
446
         }
447
         size_t sent_so_far = 0;
448
         while(sent_so_far != len) {
38,106✔
449
            const size_t left = len - sent_so_far;
19,056✔
450
            const socket_op_ret_type sent =
19,056✔
451
               ::send(m_socket, Botan::cast_uint8_ptr_to_char(&buf[sent_so_far]), left, MSG_NOSIGNAL);
19,056✔
452
            if(sent < 0) {
19,056✔
453
               if(errno == EPIPE) {
6✔
454
                  return;
455
               } else {
456
                  throw Shim_Exception("Socket write failed", errno);
×
457
               }
458
            } else {
459
               sent_so_far += static_cast<size_t>(sent);
19,050✔
460
            }
461
         }
462
      }
463

464
      size_t read(uint8_t buf[], size_t len) const {
209,114✔
465
         if(m_socket < 0) {
209,114✔
466
            throw Shim_Exception("Socket was bad on read");
×
467
         }
468
         const socket_op_ret_type got = ::read(m_socket, Botan::cast_uint8_ptr_to_char(buf), len);
209,114✔
469

470
         if(got < 0) {
209,114✔
471
            if(errno == ECONNRESET) {
206✔
472
               return 0;
473
            }
474
            throw Shim_Exception("Socket read failed: " + std::string(strerror(errno)));
×
475
         }
476

477
         return static_cast<size_t>(got);
208,908✔
478
      }
479

480
      void read_exactly(uint8_t buf[], size_t len) const {
367,892✔
481
         if(m_socket < 0) {
367,892✔
482
            throw Shim_Exception("Socket was bad on read");
×
483
         }
484

485
         while(len > 0) {
735,784✔
486
            const socket_op_ret_type got = ::read(m_socket, Botan::cast_uint8_ptr_to_char(buf), len);
367,892✔
487

488
            if(got == 0) {
367,892✔
489
               throw Shim_Exception("Socket read EOF");
×
490
            } else if(got < 0) {
367,892✔
491
               throw Shim_Exception("Socket read failed: " + std::string(strerror(errno)));
×
492
            }
493

494
            buf += static_cast<size_t>(got);
367,892✔
495
            len -= static_cast<size_t>(got);
367,892✔
496
         }
497
      }
367,892✔
498

499
   private:
500
      socket_type m_socket;
501
};
502

503
#endif
504

505
std::set<std::string> combine_options(const std::set<std::string>& a,
2,107✔
506
                                      const std::set<std::string>& b,
507
                                      const std::set<std::string>& c,
508
                                      const std::set<std::string>& d) {
509
   std::set<std::string> combined;
2,107✔
510

511
   for(const auto& i : a) {
54,782✔
512
      combined.insert(i);
52,675✔
513
   }
514
   for(const auto& i : b) {
8,428✔
515
      combined.insert(i);
6,321✔
516
   }
517
   for(const auto& i : c) {
46,354✔
518
      combined.insert(i);
44,247✔
519
   }
520
   for(const auto& i : d) {
10,535✔
521
      combined.insert(i);
8,428✔
522
   }
523

524
   return combined;
2,107✔
525
}
×
526

527
class Shim_Arguments final {
528
   public:
529
      Shim_Arguments(const std::set<std::string>& flags,
2,107✔
530
                     const std::set<std::string>& string_opts,
531
                     const std::set<std::string>& base64_opts,
532
                     const std::set<std::string>& int_opts,
533
                     const std::set<std::string>& int_vec_opts) :
2,107✔
534
            m_flags(flags),
2,107✔
535
            m_string_opts(string_opts),
2,107✔
536
            m_base64_opts(base64_opts),
2,107✔
537
            m_int_opts(int_opts),
2,107✔
538
            m_int_vec_opts(int_vec_opts),
2,107✔
539
            m_all_options(combine_options(string_opts, base64_opts, int_opts, int_vec_opts)) {}
2,107✔
540

541
      void parse_args(char* argv[]);
542

543
      bool flag_set(const std::string& flag) const {
122,831✔
544
         if(!m_flags.contains(flag)) {
122,831✔
545
            throw Shim_Exception("Unknown bool flag " + flag);
×
546
         }
547

548
         return m_parsed_flags.contains(flag);
122,831✔
549
      }
550

551
      std::string test_name() const { return get_string_opt("test-name"); }
8,438✔
552

553
      std::string get_string_opt(const std::string& key) const {
9,631✔
554
         if(!m_string_opts.contains(key)) {
9,631✔
555
            throw Shim_Exception("Unknown string key " + key);
×
556
         }
557
         return get_opt(key);
9,631✔
558
      }
559

560
      std::string get_string_opt_or_else(const std::string& key, const std::string& def) const {
222,724✔
561
         if(!m_string_opts.contains(key)) {
222,724✔
562
            throw Shim_Exception("Unknown string key " + key);
×
563
         }
564
         if(!option_used(key)) {
222,724✔
565
            return def;
221,479✔
566
         }
567
         return get_opt(key);
1,245✔
568
      }
569

570
      std::vector<uint8_t> get_b64_opt(const std::string& key) const {
73✔
571
         if(!m_base64_opts.contains(key)) {
73✔
572
            throw Shim_Exception("Unknown base64 key " + key);
×
573
         }
574
         return Botan::unlock(Botan::base64_decode(get_opt(key)));
219✔
575
      }
576

577
      size_t get_int_opt(const std::string& key) const {
7,769✔
578
         if(!m_int_opts.contains(key)) {
7,769✔
579
            throw Shim_Exception("Unknown int key " + key);
×
580
         }
581
         return Botan::to_u32bit(get_opt(key));
7,769✔
582
      }
583

584
      size_t get_int_opt_or_else(const std::string& key, size_t def) const {
8,315✔
585
         if(!m_int_opts.contains(key)) {
8,315✔
586
            throw Shim_Exception("Unknown int key " + key);
×
587
         }
588
         if(!option_used(key)) {
8,315✔
589
            return def;
590
         }
591

592
         return Botan::to_u32bit(get_opt(key));
894✔
593
      }
594

595
      std::vector<size_t> get_int_vec_opt(const std::string& key) const {
826✔
596
         if(!m_int_vec_opts.contains(key)) {
826✔
597
            throw Shim_Exception("Unknown int vec key " + key);
×
598
         }
599

600
         auto i = m_parsed_int_vec_opts.find(key);
826✔
601
         if(i == m_parsed_int_vec_opts.end()) {
826✔
602
            return std::vector<size_t>();
×
603
         } else {
604
            return i->second;
826✔
605
         }
606
      }
607

608
      std::vector<std::string> get_alpn_string_vec_opt(const std::string& option) const {
1,503✔
609
         // hack used for alpn list (relies on all ALPNs being 3 chars long...)
610
         const char delim = 0x03;
1,503✔
611

612
         if(option_used(option)) {
1,503✔
613
            return Botan::split_on(get_string_opt(option), delim);
58✔
614
         } else {
615
            return std::vector<std::string>();
1,474✔
616
         }
617
      }
618

619
      bool option_used(const std::string& key) const {
368,120✔
620
         if(!m_all_options.contains(key)) {
368,120✔
621
            throw Shim_Exception("Invalid option " + key);
×
622
         }
623
         if(m_parsed_opts.contains(key)) {
368,120✔
624
            return true;
625
         }
626
         if(m_parsed_int_vec_opts.contains(key)) {
362,019✔
627
            return true;
628
         }
629
         return false;
630
      }
631

632
   private:
633
      std::string get_opt(const std::string& key) const {
19,612✔
634
         auto i = m_parsed_opts.find(key);
19,612✔
635
         if(i == m_parsed_opts.end()) {
19,612✔
636
            throw Shim_Exception("Option " + key + " was not provided");
×
637
         }
638
         return i->second;
19,612✔
639
      }
640

641
      const std::set<std::string> m_flags;
642
      const std::set<std::string> m_string_opts;
643
      const std::set<std::string> m_base64_opts;
644
      const std::set<std::string> m_int_opts;
645
      const std::set<std::string> m_int_vec_opts;
646
      const std::set<std::string> m_all_options;
647

648
      std::set<std::string> m_parsed_flags;
649
      std::map<std::string, std::string> m_parsed_opts;
650
      std::map<std::string, std::vector<size_t>> m_parsed_int_vec_opts;
651
};
652

653
void Shim_Arguments::parse_args(char* argv[]) {
2,107✔
654
   int i = 1;  // skip argv[0]
2,107✔
655

656
   while(argv[i] != nullptr) {
20,246✔
657
      const std::string param(argv[i]);
18,139✔
658

659
      if(param.starts_with("-")) {
18,139✔
660
         const std::string flag_name = param.substr(1, std::string::npos);
18,139✔
661

662
         if(m_flags.contains(flag_name)) {
18,139✔
663
            shim_log("flag " + flag_name);
5,963✔
664
            m_parsed_flags.insert(flag_name);
5,963✔
665
            i += 1;
5,963✔
666
         } else if(m_all_options.contains(flag_name)) {
12,176✔
667
            if(argv[i + 1] == nullptr) {
12,176✔
668
               throw Shim_Exception("Expected argument following " + param);
×
669
            }
670
            const std::string val(argv[i + 1]);
12,176✔
671
            shim_log(Botan::fmt("param {}={}", flag_name, val));
12,176✔
672

673
            if(m_int_vec_opts.contains(flag_name)) {
12,176✔
674
               const size_t v = Botan::to_u32bit(val);
837✔
675
               m_parsed_int_vec_opts[flag_name].push_back(v);
837✔
676
            } else {
677
               m_parsed_opts[flag_name] = val;
11,339✔
678
            }
679
            i += 2;
12,176✔
680
         } else {
12,176✔
681
            shim_log("Unknown option " + param);
×
682
            throw Shim_Exception("Unknown option " + param, 89);
×
683
         }
684
      } else {
18,139✔
685
         shim_log("Unknown option " + param);
×
686
         throw Shim_Exception("Unknown option " + param, 89);
×
687
      }
688
   }
18,139✔
689
}
8,070✔
690

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

790
   const std::set<std::string> bogo_shim_string_opts = {
2,107✔
791
      "advertise-alpn",
792
      //"advertise-npn",
793
      "cert-file",
794
      "cipher",
795
      //"delegated-credential",
796
      "expect-advertised-alpn",
797
      "expect-alpn",
798
      "expect-client-ca-list",
799
      "expect-early-data-reason",
800
      "expect-late-alpn",
801
      "expect-msg-callback",
802
      //"expect-next-proto",
803
      "expect-peer-cert-file",
804
      "expect-server-name",
805
      "export-context",
806
      "export-label",
807
      "handshaker-path",
808
      "host-name",
809
      "key-file",
810
      "psk",
811
      "psk-identity",
812
      "select-alpn",
813
      "select-next-proto",
814
      "srtp-profiles",
815
      "test-name",
816
      "trust-cert",
817
      "use-client-ca-list",
818
      //"send-channel-id",
819
      "write-settings",
820
   };
2,107✔
821

822
   const std::set<std::string> bogo_shim_base64_opts = {
2,107✔
823
      "expect-certificate-types",
824
      //"expect-channel-id",
825
      "expect-ocsp-response",
826
      //"expect-quic-transport-params",
827
      //"expect-signed-cert-timestamps",
828
      "ocsp-response",
829
      //"quic-transport-params",
830
      //"signed-cert-timestamps",
831
      //"ticket-key", /* we use a different ticket format from Boring */
832
      //"token-binding-params",
833
   };
2,107✔
834

835
   const std::set<std::string> bogo_shim_int_opts{
2,107✔
836
      "expect-cipher-aes",
837
      "expect-cipher-no-aes",
838
      "expect-curve-id",
839
      "expect-peer-signature-algorithm",
840
      "expect-ticket-age-skew",
841
      "expect-token-binding-param",
842
      "expect-total-renegotiations",
843
      "expect-version",
844
      //"export-early-keying-material",
845
      "export-keying-material",
846
      "initial-timeout-duration-ms",
847
      "max-cert-list",
848
      //"max-send-fragment",
849
      "max-version",
850
      "min-version",
851
      "mtu",
852
      "on-initial-expect-curve-id",
853
      "on-resume-expect-curve-id",
854
      "port",
855
      "read-size",
856
      "resume-count",
857
      "resumption-delay",
858
      "shim-id",
859
   };
48,461✔
860

861
   const std::set<std::string> bogo_shim_int_vec_opts{
2,107✔
862
      "curves",
863
      "expect-peer-verify-pref",
864
      "signing-prefs",
865
      "verify-prefs",
866
   };
12,642✔
867

868
   std::unique_ptr<Shim_Arguments> args(new Shim_Arguments(
2,107✔
869
      bogo_shim_flags, bogo_shim_string_opts, bogo_shim_base64_opts, bogo_shim_int_opts, bogo_shim_int_vec_opts));
2,107✔
870

871
   // may throw:
872
   args->parse_args(argv);
2,107✔
873

874
   return args;
2,107✔
875
}
8,428✔
876

877
class Shim_Policy final : public Botan::TLS::Policy {
2,697✔
878
   public:
879
      explicit Shim_Policy(const Shim_Arguments& args) : m_args(args), m_sessions(0) {}
2,715✔
880

881
      void incr_session_established() { m_sessions += 1; }
2,025✔
882

883
      std::vector<std::string> allowed_ciphers() const override {
211,481✔
884
         std::vector<std::string> allowed_without_aes = {
211,481✔
885
            "ChaCha20Poly1305",
886
            "Camellia-256/GCM",
887
            "Camellia-128/GCM",
888
            "ARIA-256/GCM",
889
            "ARIA-128/GCM",
890
            "Camellia-256",
891
            "Camellia-128",
892
            "SEED",
893
         };
211,481✔
894

895
         std::vector<std::string> allowed_just_aes = {
211,481✔
896
            "AES-256/OCB(12)",
897
            "AES-128/OCB(12)",
898
            "AES-256/GCM",
899
            "AES-128/GCM",
900
            "AES-256/CCM",
901
            "AES-128/CCM",
902
            "AES-256/CCM(8)",
903
            "AES-128/CCM(8)",
904
            "AES-256",
905
            "AES-128",
906
         };
211,481✔
907

908
         // 3DES is not supported by default anymore, only if the test runner
909
         // explicitly enables it via -cipher=
910
         const std::string cipher_limit = m_args.get_string_opt_or_else("cipher", "");
422,962✔
911
         if(cipher_limit == "3DES") {
211,481✔
912
            return {"3DES"};
987✔
913
         } else if(cipher_limit == "DEFAULT:!AES") {
210,494✔
914
            return allowed_without_aes;
102✔
915
         } else {
916
            // ignore this very specific config (handled in the overload of ciphersuite_list)
917
            if(!cipher_limit.empty() &&
210,392✔
918
               cipher_limit !=
919
                  "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]") {
×
920
               shim_exit_with_error("Unknown cipher limit " + cipher_limit);
×
921
            }
922
         }
923

924
         return Botan::concat(allowed_without_aes, allowed_just_aes);
211,481✔
925
      }
211,481✔
926

927
      std::vector<std::string> allowed_signature_hashes() const override {
29,677✔
928
         if(m_args.option_used("signing-prefs")) {
29,677✔
929
            std::vector<std::string> pref_hash;
107✔
930
            for(const size_t pref : m_args.get_int_vec_opt("signing-prefs")) {
217✔
931
               const Botan::TLS::Signature_Scheme scheme(pref);
110✔
932
               if(!scheme.is_available()) {
110✔
933
                  shim_log("skipping inavailable but preferred signature scheme: " + std::to_string(pref));
6✔
934
                  continue;
2✔
935
               }
936
               pref_hash.push_back(scheme.hash_function_name());
108✔
937
            }
107✔
938

939
            if(m_args.flag_set("server")) {
107✔
940
               pref_hash.push_back("SHA-256");
168✔
941
            }
942
            return pref_hash;
107✔
943
         } else {
107✔
944
            return {"SHA-512", "SHA-384", "SHA-256", "SHA-1"};
29,570✔
945
         }
946
      }
947

948
      //std::vector<std::string> allowed_macs() const override;
949

950
      std::vector<std::string> allowed_signature_methods() const override {
27,871✔
951
         return {
27,871✔
952
            "ECDSA",
953
            "RSA",
954
            "IMPLICIT",
955
         };
27,871✔
956
      }
957

958
      std::vector<Botan::TLS::Signature_Scheme> acceptable_signature_schemes() const override {
1,814✔
959
         if(m_args.option_used("verify-prefs")) {
1,814✔
960
            std::vector<Botan::TLS::Signature_Scheme> schemes;
93✔
961
            for(const size_t pref : m_args.get_int_vec_opt("verify-prefs")) {
186✔
962
               schemes.emplace_back(static_cast<uint16_t>(pref));
93✔
963
            }
93✔
964

965
            return schemes;
93✔
966
         }
93✔
967

968
         return Botan::TLS::Policy::acceptable_signature_schemes();
1,721✔
969
      }
970

971
      std::vector<Botan::TLS::Signature_Scheme> allowed_signature_schemes() const override {
3,098✔
972
         if(m_args.option_used("signing-prefs")) {
3,098✔
973
            std::vector<Botan::TLS::Signature_Scheme> schemes;
133✔
974
            for(const size_t pref : m_args.get_int_vec_opt("signing-prefs")) {
270✔
975
               schemes.emplace_back(static_cast<uint16_t>(pref));
137✔
976
            }
133✔
977

978
            // The relevant tests (*-Sign-Negotiate-*) want to configure a preference
979
            // for the scheme of our signing operation (-signing-prefs). However, this
980
            // policy method (`allowed_signature_schemes`) also restricts the peer's
981
            // signing operation. If we weren't to add a few 'common' algorithms, initial
982
            // security parameter negotiation would fail.
983
            // By placing the BoGo-configured scheme first we make sure our implementation
984
            // meets BoGo's expectation when it is our turn to sign.
985
            if(!m_args.flag_set("server")) {
133✔
986
               schemes.emplace_back(Botan::TLS::Signature_Scheme::RSA_PKCS1_SHA256);
77✔
987
               schemes.emplace_back(Botan::TLS::Signature_Scheme::RSA_PSS_SHA256);
77✔
988
               schemes.emplace_back(Botan::TLS::Signature_Scheme::ECDSA_SHA256);
77✔
989
            }
990

991
            return schemes;
133✔
992
         }
133✔
993

994
         return Botan::TLS::Policy::allowed_signature_schemes();
2,965✔
995
      }
996

997
      //size_t minimum_signature_strength() const override;
998

999
      bool require_cert_revocation_info() const override { return false; }
6✔
1000

1001
      std::vector<Botan::TLS::Group_Params> key_exchange_groups() const override {
7,377✔
1002
         if(m_args.option_used("curves")) {
7,377✔
1003
            std::vector<Botan::TLS::Group_Params> groups;
493✔
1004

1005
            // upcall to base class to find the groups actually supported by
1006
            // this Botan build
1007
            const auto supported_groups = Botan::TLS::Policy::key_exchange_groups();
493✔
1008

1009
            for(const size_t pref : m_args.get_int_vec_opt("curves")) {
2,728✔
1010
               const auto group = static_cast<Botan::TLS::Group_Params>(pref);
2,235✔
1011
               if(std::find(supported_groups.cbegin(), supported_groups.cend(), group) != supported_groups.end()) {
2,235✔
1012
                  groups.push_back(group);
1,665✔
1013
               }
1014
            }
493✔
1015

1016
            return groups;
493✔
1017
         }
493✔
1018

1019
         return Botan::TLS::Policy::key_exchange_groups();
6,884✔
1020
      }
1021

1022
      bool use_ecc_point_compression() const override { return false; }  // BoGo expects this
2,246✔
1023

1024
      Botan::TLS::Group_Params choose_key_exchange_group(
2,662✔
1025
         const std::vector<Botan::TLS::Group_Params>& supported_by_peer,
1026
         const std::vector<Botan::TLS::Group_Params>& offered_by_peer) const override {
1027
         BOTAN_UNUSED(offered_by_peer);
2,662✔
1028

1029
         // always insist on our most preferred group regardless of the peer's
1030
         // pre-offers (BoGo expects it like that)
1031
         const auto our_groups = key_exchange_groups();
2,662✔
1032
         for(auto g : our_groups) {
10,177✔
1033
            if(Botan::value_exists(supported_by_peer, g)) {
19,156✔
1034
               return g;
2,063✔
1035
            }
1036
         }
1037

1038
         return Botan::TLS::Group_Params::NONE;
599✔
1039
      }
2,662✔
1040

1041
      bool require_client_certificate_authentication() const override {
538✔
1042
         return m_args.flag_set("require-any-client-certificate");
538✔
1043
      }
1044

1045
      bool request_client_certificate_authentication() const override {
539✔
1046
         return m_args.flag_set("verify-peer") || m_args.flag_set("fail-cert-callback") ||
1,560✔
1047
                require_client_certificate_authentication();
1,049✔
1048
      }
1049

1050
      bool allow_insecure_renegotiation() const override { return m_args.flag_set("expect-no-secure-renegotiation"); }
1,158✔
1051

1052
      //bool include_time_in_hello_random() const override;
1053

1054
      bool allow_client_initiated_renegotiation() const override {
40✔
1055
         if(m_args.flag_set("renegotiate-freely")) {
40✔
1056
            return true;
1057
         }
1058

1059
         if(m_args.flag_set("renegotiate-once") && m_sessions <= 1) {
16✔
1060
            return true;
1061
         }
1062

1063
         return false;
1064
      }
1065

1066
      bool allow_server_initiated_renegotiation() const override {
39✔
1067
         return allow_client_initiated_renegotiation();  // same logic
39✔
1068
      }
1069

1070
      bool allow_version(Botan::TLS::Protocol_Version version) const {
18,486✔
1071
         if(m_args.option_used("min-version")) {
18,486✔
1072
            const uint16_t min_version_16 = static_cast<uint16_t>(m_args.get_int_opt("min-version"));
65✔
1073
            const Botan::TLS::Protocol_Version min_version(min_version_16 >> 8, min_version_16 & 0xFF);
65✔
1074
            if(min_version > version) {
65✔
1075
               return false;
15✔
1076
            }
1077
         }
1078

1079
         if(m_args.option_used("max-version")) {
18,471✔
1080
            const uint16_t max_version_16 = static_cast<uint16_t>(m_args.get_int_opt("max-version"));
76✔
1081
            const Botan::TLS::Protocol_Version max_version(max_version_16 >> 8, max_version_16 & 0xFF);
76✔
1082
            if(version > max_version) {
76✔
1083
               return false;
13✔
1084
            }
1085
         }
1086

1087
         return version.known_version();
18,458✔
1088
      }
1089

1090
      bool allow_tls12() const override {
10,824✔
1091
         return !m_args.flag_set("dtls") && !m_args.flag_set("no-tls12") &&
21,648✔
1092
                allow_version(Botan::TLS::Protocol_Version::TLS_V12);
21,648✔
1093
      }
1094

1095
      bool allow_tls13() const override {
4,925✔
1096
         return !m_args.flag_set("dtls") && !m_args.flag_set("no-tls13") &&
9,850✔
1097
                allow_version(Botan::TLS::Protocol_Version::TLS_V13);
9,850✔
1098
      }
1099

1100
      bool allow_dtls12() const override {
4,048✔
1101
         return m_args.flag_set("dtls") && !m_args.flag_set("no-tls12") &&
8,096✔
1102
                allow_version(Botan::TLS::Protocol_Version::DTLS_V12);
8,096✔
1103
      }
1104

1105
      //Botan::TLS::Group_Params default_dh_group() const override;
1106

1107
      //size_t minimum_dh_group_size() const override;
1108

1109
      size_t minimum_ecdsa_group_size() const override { return 224; }
131✔
1110

1111
      size_t minimum_ecdh_group_size() const override { return 224; }
1,786✔
1112

1113
      //size_t minimum_rsa_bits() const override;
1114

1115
      //size_t minimum_dsa_group_size() const override;
1116

1117
      //void check_peer_key_acceptable(const Botan::Public_Key& public_key) const override;
1118

1119
      //bool hide_unknown_users() const override;
1120

1121
      //std::chrono::seconds session_ticket_lifetime() const override;
1122

1123
      size_t new_session_tickets_upon_handshake_success() const override {
264✔
1124
         return m_args.flag_set("no-ticket") ? 0 : 1;
527✔
1125
      }
1126

1127
      std::vector<uint16_t> srtp_profiles() const override {
566✔
1128
         if(m_args.option_used("srtp-profiles")) {
566✔
1129
            const std::string srtp = m_args.get_string_opt("srtp-profiles");
4✔
1130

1131
            if(srtp == "SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32") {
4✔
1132
               return {1, 2};
3✔
1133
            } else if(srtp == "SRTP_AES128_CM_SHA1_80") {
1✔
1134
               return {1};
1✔
1135
            } else {
1136
               shim_exit_with_error("unknown srtp-profiles");
×
1137
            }
1138
         } else {
4✔
1139
            return {};
562✔
1140
         }
1141
      }
1142

1143
      bool only_resume_with_exact_version() const override { return false; }
198✔
1144

1145
      //bool server_uses_own_ciphersuite_preferences() const override;
1146

1147
      //bool negotiate_encrypt_then_mac() const override;
1148

1149
      bool support_cert_status_message() const override {
2,105✔
1150
         if(m_args.flag_set("server")) {
2,105✔
1151
            if(!m_args.option_used("ocsp-response")) {
581✔
1152
               return false;
1153
            }
1154
            if(m_args.flag_set("decline-ocsp-callback")) {
72✔
1155
               return false;
1156
            }
1157
         } else if(!m_args.flag_set("enable-ocsp-stapling")) {
1,524✔
1158
            return false;
1159
         }
1160

1161
         return true;
1162
      }
1163

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

1166
      size_t dtls_default_mtu() const override { return m_args.get_int_opt_or_else("mtu", 1500); }
756✔
1167

1168
      //size_t dtls_initial_timeout() const override;
1169

1170
      //size_t dtls_maximum_timeout() const override;
1171

1172
      bool abort_connection_on_undesired_renegotiation() const override {
6✔
1173
         return !m_args.flag_set("renegotiate-ignore");
6✔
1174
      }
1175

1176
      size_t maximum_certificate_chain_size() const override { return m_args.get_int_opt_or_else("max-cert-list", 0); }
1,351✔
1177

1178
      bool tls_13_middlebox_compatibility_mode() const override {
2,728✔
1179
         // These tests expect the client to send an alert in return of a malformed TLS 1.2 server hello.
1180
         // However, our TLS 1.3 implementation produces an alert without downgrading to TLS 1.2 first.
1181
         // In compatibility mode this prepends a CCS, which BoGo does not expect to read.
1182
         const std::vector<std::string> alert_after_server_hello = {
2,728✔
1183
            "DuplicateExtensionClient-TLS-TLS12",
1184
            "WrongMessageType-ServerHello-TLS",
1185
            "SendServerHelloAsHelloRetryRequest",
1186
            "TrailingMessageData-ServerHello-TLS",
1187
            "NoSSL3-Client-Unsolicited",
1188
            "Client-TooLongSessionID",
1189
            "MinimumVersion-Client-TLS13-TLS12-TLS",
1190
            "MinimumVersion-Client2-TLS13-TLS12-TLS",
1191
         };
2,728✔
1192
         return !Botan::value_exists(alert_after_server_hello, m_args.test_name());
8,184✔
1193
      }
2,728✔
1194

1195
   private:
1196
      const Shim_Arguments& m_args;
1197
      size_t m_sessions;
1198
};
1199

1200
std::vector<uint16_t> Shim_Policy::ciphersuite_list(Botan::TLS::Protocol_Version version) const {
3,456✔
1201
   std::vector<uint16_t> ciphersuite_codes;
3,456✔
1202

1203
   const std::string cipher_limit = m_args.get_string_opt_or_else("cipher", "");
6,912✔
1204
   if(cipher_limit ==
3,456✔
1205
      "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]") {
1206
      const std::vector<std::string> suites = {
5✔
1207
         "ECDHE_RSA_WITH_AES_128_GCM_SHA256",
1208
         "ECDHE_RSA_WITH_AES_256_GCM_SHA384",
1209
         "ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
1210
         "ECDHE_RSA_WITH_AES_256_CBC_SHA",
1211
         "RSA_WITH_AES_256_GCM_SHA384",
1212
         "RSA_WITH_AES_256_CBC_SHA",
1213
      };
5✔
1214

1215
      for(const auto& suite_name : suites) {
35✔
1216
         const auto suite = Botan::TLS::Ciphersuite::from_name(suite_name);
30✔
1217
         if(!suite || !suite->valid()) {
30✔
1218
            shim_exit_with_error("Bad ciphersuite name " + suite_name);
×
1219
         }
1220
         ciphersuite_codes.push_back(suite->ciphersuite_code());
30✔
1221
      }
1222
   } else {
5✔
1223
      // Hack: go in reverse order to avoid preferring 3DES
1224
      auto ciphersuites = Botan::TLS::Ciphersuite::all_known_ciphersuites();
3,451✔
1225
      // TODO(Botan4) use std::ranges::reverse_view here once available (need newer Clang)
1226
      // NOLINTNEXTLINE(modernize-loop-convert)
1227
      for(auto i = ciphersuites.rbegin(); i != ciphersuites.rend(); ++i) {
355,453✔
1228
         const auto suite = *i;
352,002✔
1229

1230
         const bool usable = suite.valid() && suite.usable_in_version(version) &&
563,285✔
1231
                             Botan::value_exists(allowed_ciphers(), suite.cipher_algo());
774,568✔
1232

1233
         if(usable) {
352,002✔
1234
            ciphersuite_codes.push_back(suite.ciphersuite_code());
180,910✔
1235
         }
1236
      }
1237
   }
3,451✔
1238

1239
   return ciphersuite_codes;
3,456✔
1240
}
3,456✔
1241

1242
class Shim_Credentials final : public Botan::Credentials_Manager {
1243
   public:
1244
      explicit Shim_Credentials(const Shim_Arguments& args) : m_args(args) {
2,106✔
1245
         const auto psk_identity = m_args.get_string_opt_or_else("psk-identity", "");
4,212✔
1246
         const auto psk_str = m_args.get_string_opt_or_else("psk", "");
4,212✔
1247

1248
         if(!psk_identity.empty() || !psk_str.empty()) {
2,106✔
1249
            // If the shim received a -psk param but no -psk-identity param,
1250
            // we have to initialize the identity as "empty string".
1251
            m_psk_identity = psk_identity;
77✔
1252
         }
1253

1254
         if(!psk_str.empty()) {
2,106✔
1255
            m_psk = Botan::SymmetricKey(reinterpret_cast<const uint8_t*>(psk_str.data()), psk_str.size());
77✔
1256
         }
1257

1258
         if(m_args.option_used("key-file") && m_args.option_used("cert-file")) {
3,118✔
1259
            Botan::DataSource_Stream key_stream(m_args.get_string_opt("key-file"));
2,024✔
1260
            m_key.reset(Botan::PKCS8::load_key(key_stream).release());
1,012✔
1261

1262
            Botan::DataSource_Stream cert_stream(m_args.get_string_opt("cert-file"));
2,024✔
1263

1264
            while(!cert_stream.end_of_data()) {
3,040✔
1265
               try {
2,028✔
1266
                  m_cert_chain.push_back(Botan::X509_Certificate(cert_stream));
3,044✔
1267
               } catch(...) {}
1,012✔
1268
            }
1269
         }
1,012✔
1270

1271
         if(m_args.option_used("trust-cert") && !m_args.get_string_opt("trust-cert").empty()) {
5,092✔
1272
            Botan::DataSource_Stream cert_stream(m_args.get_string_opt("trust-cert"));
2,974✔
1273
            try {
1,487✔
1274
               m_trust_roots.add_certificate(Botan::X509_Certificate(cert_stream));
1,487✔
1275
            } catch(const std::exception& ex) {
×
1276
               throw Shim_Exception("Failed to load trusted root certificate: " + std::string(ex.what()));
×
1277
            }
×
1278
         }
1,487✔
1279
      }
2,106✔
1280

1281
      std::vector<Botan::Certificate_Store*> trusted_certificate_authorities(const std::string& type,
2,084✔
1282
                                                                             const std::string& context) override {
1283
         if(m_args.flag_set("server") && type != "tls-server") {
4,168✔
1284
            throw Shim_Exception("TLS server implementation asked for unexpected trusted CA type: " + type);
×
1285
         }
1286
         if(!m_args.flag_set("server") && type != "tls-client") {
4,168✔
1287
            throw Shim_Exception("TLS client implementation asked for unexpected trusted CA type: " + type);
×
1288
         }
1289

1290
         const auto expected_hostname = m_args.get_string_opt_or_else("host-name", "none");
4,168✔
1291
         if(expected_hostname != "none" && expected_hostname != context) {
2,084✔
1292
            throw Shim_Exception("Unexpected host name in trusted CA request: " + context);
×
1293
         }
1294

1295
         return {&m_trust_roots};
2,084✔
1296
      }
2,084✔
1297

1298
      std::string psk_identity(const std::string& /*type*/,
48✔
1299
                               const std::string& /*context*/,
1300
                               const std::string& /*identity_hint*/) override {
1301
         return m_psk_identity.value_or("");
48✔
1302
      }
1303

1304
      std::string psk_identity_hint(const std::string& /*type*/, const std::string& /*context*/) override {
27✔
1305
         return m_psk_identity.value_or("");
27✔
1306
      }
1307

1308
      Botan::secure_vector<uint8_t> session_ticket_key() override {
1,764✔
1309
         return Botan::hex_decode_locked("ABCDEF0123456789");
1,764✔
1310
      }
1311

1312
      Botan::secure_vector<uint8_t> dtls_cookie_secret() override {
658✔
1313
         return Botan::hex_decode_locked("F00FB00FD00F100F700F");
658✔
1314
      }
1315

1316
      std::vector<Botan::TLS::ExternalPSK> find_preshared_keys(
1,029✔
1317
         std::string_view host,
1318
         Botan::TLS::Connection_Side whoami,
1319
         const std::vector<std::string>& identities = {},
1320
         const std::optional<std::string>& prf = std::nullopt) override {
1321
         if(!m_psk_identity.has_value()) {
1,029✔
1322
            return Botan::Credentials_Manager::find_preshared_keys(host, whoami, identities, prf);
927✔
1323
         }
1324

1325
         auto id_matches =
102✔
1326
            identities.empty() || std::find(identities.begin(), identities.end(), m_psk_identity) != identities.end();
102✔
1327

1328
         if(!id_matches) {
102✔
1329
            throw Shim_Exception("Unexpected PSK identity");
×
1330
         }
1331

1332
         if(!m_psk.has_value()) {
102✔
1333
            throw Shim_Exception("PSK identified but not set");
×
1334
         }
1335

1336
         std::vector<Botan::TLS::ExternalPSK> psks;
102✔
1337

1338
         // Currently, BoGo tests PSK with TLS 1.2 only. In TLS 1.2 the PRF does not
1339
         // need to be specified for PSKs.
1340
         //
1341
         // TODO: Once BoGo has tests for TLS 1.3 with externally provided PSKs, this
1342
         //       will need to be handled somehow.
1343
         const std::string psk_prf = "SHA-256";
102✔
1344
         psks.emplace_back(m_psk_identity.value(), psk_prf, m_psk->bits_of());
102✔
1345
         return psks;
102✔
1346
      }
102✔
1347

1348
      std::vector<Botan::X509_Certificate> cert_chain(
1,645✔
1349
         const std::vector<std::string>& cert_key_types,
1350
         const std::vector<Botan::AlgorithmIdentifier>& /*cert_signature_schemes*/,
1351
         const std::string& /*type*/,
1352
         const std::string& /*context*/) override {
1353
         if(m_args.flag_set("fail-cert-callback")) {
1,645✔
1354
            throw std::runtime_error("Simulating cert verify callback failure");
4✔
1355
         }
1356

1357
         if(m_key != nullptr && !m_cert_chain.empty()) {
1,641✔
1358
            for(const std::string& t : cert_key_types) {
2,162✔
1359
               if(t == m_key->algo_name()) {
1,593✔
1360
                  return m_cert_chain;
1,641✔
1361
               }
1362
            }
1363
         }
1364

1365
         return {};
700✔
1366
      }
1367

1368
      std::shared_ptr<Botan::Private_Key> private_key_for(const Botan::X509_Certificate& /*cert*/,
897✔
1369
                                                          const std::string& /*type*/,
1370
                                                          const std::string& /*context*/) override {
1371
         // assumes cert == m_cert
1372
         return m_key;
897✔
1373
      }
1374

1375
   private:
1376
      const Shim_Arguments& m_args;
1377
      std::optional<Botan::SymmetricKey> m_psk;
1378
      std::optional<std::string> m_psk_identity;
1379
      std::shared_ptr<Botan::Private_Key> m_key;
1380
      std::vector<Botan::X509_Certificate> m_cert_chain;
1381
      Botan::Certificate_Store_In_Memory m_trust_roots;
1382
};
1383

1384
class Shim_Callbacks final : public Botan::TLS::Callbacks {
2,697✔
1385
   public:
1386
      Shim_Callbacks(const Shim_Arguments& args, Shim_Socket& socket, Shim_Policy& policy) :
2,715✔
1387
            m_channel(nullptr),
2,715✔
1388
            m_args(args),
2,715✔
1389
            m_policy(policy),
2,715✔
1390
            m_socket(socket),
2,715✔
1391
            m_is_datagram(args.flag_set("dtls")),
2,715✔
1392
            m_warning_alerts(0),
2,715✔
1393
            m_empty_records(0),
2,715✔
1394
            m_sessions_established(0),
2,715✔
1395
            m_got_close(false),
2,715✔
1396
            m_hello_retry_request(false),
2,715✔
1397
            m_clock_skew(0) {}
5,430✔
1398

1399
      size_t sessions_established() const { return m_sessions_established; }
21✔
1400

1401
      void set_channel(Botan::TLS::Channel* channel) { m_channel = channel; }
2,713✔
1402

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

1405
      bool saw_close_notify() const { return m_got_close; }
27✔
1406

1407
      void tls_emit_data(std::span<const uint8_t> data) override {
16,341✔
1408
         shim_log("sending record of len " + std::to_string(data.size()));
49,023✔
1409

1410
         if(m_args.option_used("write-settings")) {
16,341✔
1411
            // TODO: the transcript option should probably be used differently
1412
            std::cout << ">>>" << std::endl << Botan::hex_encode(data) << std::endl << ">>>" << std::endl;
×
1413
         }
1414

1415
         if(m_is_datagram) {
16,341✔
1416
            std::vector<uint8_t> packet(data.size() + 5);
6,030✔
1417

1418
            packet[0] = 'P';
6,030✔
1419
            for(size_t i = 0; i != 4; ++i) {
30,150✔
1420
               packet[i + 1] = static_cast<uint8_t>((data.size() >> (24 - 8 * i)) & 0xFF);
24,120✔
1421
            }
1422
            std::memcpy(packet.data() + 5, data.data(), data.size());
6,030✔
1423

1424
            m_socket.write(packet.data(), packet.size());
6,030✔
1425
         } else {
6,030✔
1426
            m_socket.write(data.data(), data.size());
10,311✔
1427
         }
1428
      }
16,341✔
1429

1430
      std::vector<uint8_t> tls_provide_cert_status(const std::vector<Botan::X509_Certificate>& /*certs*/,
794✔
1431
                                                   const Botan::TLS::Certificate_Status_Request& /*status*/) override {
1432
         if(m_args.flag_set("use-ocsp-callback") && m_args.flag_set("fail-ocsp-callback")) {
874✔
1433
            throw std::runtime_error("Simulating failure from OCSP response callback");
32✔
1434
         }
1435

1436
         if(m_args.flag_set("decline-ocsp-callback")) {
762✔
1437
            return {};
24✔
1438
         }
1439

1440
         if(m_args.option_used("ocsp-response")) {
738✔
1441
            return m_args.get_b64_opt("ocsp-response");
96✔
1442
         }
1443

1444
         return {};
690✔
1445
      }
1446

1447
      void tls_record_received(uint64_t /*seq_no*/, std::span<const uint8_t> data) override {
2,352✔
1448
         if(data.empty()) {
2,352✔
1449
            m_empty_records += 1;
98✔
1450
            if(m_empty_records > 32) {
98✔
1451
               shim_exit_with_error(":TOO_MANY_EMPTY_FRAGMENTS:");
2✔
1452
            }
1453
         } else {
1454
            m_empty_records = 0;
2,254✔
1455
         }
1456

1457
         shim_log("Reflecting application_data len " + std::to_string(data.size()));
7,050✔
1458

1459
         std::vector<uint8_t> buf(data.begin(), data.end());
2,350✔
1460
         for(auto& b : buf) {
1,178,137✔
1461
            b ^= 0xFF;
1,175,787✔
1462
         }
1463

1464
         m_channel->send(buf);
2,350✔
1465
      }
2,350✔
1466

1467
      bool tls_verify_message(const Botan::Public_Key& key,
1,094✔
1468
                              std::string_view padding,
1469
                              Botan::Signature_Format format,
1470
                              const std::vector<uint8_t>& msg,
1471
                              const std::vector<uint8_t>& sig) override {
1472
         if(m_args.option_used("expect-peer-signature-algorithm")) {
1,094✔
1473
            const Botan::TLS::Signature_Scheme scheme(
65✔
1474
               static_cast<uint16_t>(m_args.get_int_opt("expect-peer-signature-algorithm")));
65✔
1475

1476
            if(!scheme.is_available()) {
65✔
1477
               shim_exit_with_error(std::string("Unsupported signature scheme provided by BoGo: ") +
×
1478
                                    scheme.to_string());
×
1479
            }
1480

1481
            const std::string exp_padding = scheme.padding_string();
65✔
1482
            if(padding != exp_padding) {
130✔
1483
               shim_exit_with_error(Botan::fmt("Unexpected signature scheme got {} expected {}", padding, exp_padding));
×
1484
            }
1485
         }
65✔
1486

1487
         return Botan::TLS::Callbacks::tls_verify_message(key, padding, format, msg, sig);
1,094✔
1488
      }
1489

1490
      void tls_verify_cert_chain(const std::vector<Botan::X509_Certificate>& cert_chain,
1,278✔
1491
                                 const std::vector<std::optional<Botan::OCSP::Response>>& ocsp_responses,
1492
                                 const std::vector<Botan::Certificate_Store*>& trusted_roots,
1493
                                 Botan::Usage_Type usage,
1494
                                 std::string_view /* hostname */,
1495
                                 const Botan::TLS::Policy& policy) override {
1496
         if(m_args.flag_set("enable-ocsp-stapling") && m_args.flag_set("use-ocsp-callback") &&
2,679✔
1497
            m_args.flag_set("fail-ocsp-callback")) {
1,374✔
1498
            throw Botan::TLS::TLS_Exception(Botan::TLS::Alert::BadCertificateStatusResponse,
64✔
1499
                                            "Simulated OCSP callback failure");
64✔
1500
         }
1501

1502
         if(m_args.flag_set("verify-fail")) {
1,214✔
1503
            auto alert = Botan::TLS::Alert::HandshakeFailure;
128✔
1504
            if(m_args.flag_set("use-custom-verify-callback")) {
128✔
1505
               alert = Botan::TLS::Alert::CertificateUnknown;
64✔
1506
            }
1507

1508
            throw Botan::TLS::TLS_Exception(alert, "Test requires rejecting cert");
128✔
1509
         }
1510

1511
         if(!cert_chain.empty() && cert_chain.front().is_self_signed()) {
1,086✔
1512
            for(auto* const roots : trusted_roots) {
1,080✔
1513
               if(roots->certificate_known(cert_chain.front())) {
1,080✔
1514
                  shim_log("Trusting self-signed certificate");
1,080✔
1515
                  return;
1,080✔
1516
               }
1517
            }
1518
         }
1519

1520
         shim_log("Establishing trust from a certificate chain");
6✔
1521

1522
         Botan::TLS::Callbacks::tls_verify_cert_chain(
6✔
1523
            cert_chain, ocsp_responses, trusted_roots, usage, "" /* hostname */, policy);
1524
      }
1525

1526
      std::optional<Botan::OCSP::Response> tls_parse_ocsp_response(const std::vector<uint8_t>& raw_response) override {
89✔
1527
         if(m_args.option_used("expect-ocsp-response") && m_args.get_b64_opt("expect-ocsp-response") != raw_response) {
164✔
1528
            shim_exit_with_error("unexpected OCSP response");
×
1529
         }
1530

1531
         // Bogo uses invalid dummy OCSP responses. Don't even bother trying to
1532
         // decode them.
1533
         return std::nullopt;
89✔
1534
      }
1535

1536
      void tls_modify_extensions(Botan::TLS::Extensions& exts,
3,714✔
1537
                                 Botan::TLS::Connection_Side /* side */,
1538
                                 Botan::TLS::Handshake_Type msg_type) override {
1539
#if defined(BOTAN_HAS_TLS_13)
1540
         if(msg_type == Botan::TLS::Handshake_Type::CertificateRequest) {
3,714✔
1541
            if(m_args.option_used("use-client-ca-list")) {
95✔
1542
               // The CertificateAuthorities extension is filled with the CA
1543
               // list provided by the credentials manager. The same list is
1544
               // used to later verify the client certificate chain.
1545
               //
1546
               // Hence, we have to use this low-level callback to fulfill the
1547
               // BoGo requirement of sending specific configurations of the CA
1548
               // list in the CertificateRequest message.
1549
               if(m_args.get_string_opt("use-client-ca-list") == "<EMPTY>" ||
4✔
1550
                  m_args.get_string_opt("use-client-ca-list") == "<NULL>") {
4✔
1551
                  exts.remove_extension(Botan::TLS::Extension_Code::CertificateAuthorities);
1✔
1552
               } else {
1553
                  // TODO: -use-client-ca-list might also provide the encoded
1554
                  //       list of DNs. We could render this here, if needed.
1555
               }
1556
            }
1557
         }
1558
#else
1559
         BOTAN_UNUSED(exts, msg_type);
1560
#endif
1561
      }
3,714✔
1562

1563
      std::string tls_server_choose_app_protocol(const std::vector<std::string>& client_protos) override {
21✔
1564
         if(client_protos.empty()) {
21✔
1565
            return "";  // shouldn't happen?
×
1566
         }
1567

1568
         if(m_args.flag_set("reject-alpn")) {
21✔
1569
            throw Botan::TLS::TLS_Exception(Botan::TLS::Alert::NoApplicationProtocol,
3✔
1570
                                            "Rejecting ALPN request with alert");
3✔
1571
         }
1572

1573
         if(m_args.flag_set("decline-alpn")) {
18✔
1574
            return "";
6✔
1575
         }
1576

1577
         if(m_args.option_used("expect-advertised-alpn")) {
12✔
1578
            const std::vector<std::string> expected = m_args.get_alpn_string_vec_opt("expect-advertised-alpn");
12✔
1579

1580
            if(client_protos != expected) {
12✔
1581
               shim_exit_with_error("Bad ALPN from client");
×
1582
            }
1583
         }
12✔
1584

1585
         if(m_args.option_used("select-alpn")) {
12✔
1586
            return m_args.get_string_opt("select-alpn");
24✔
1587
         }
1588

1589
         return client_protos[0];  // if not configured just pick something
×
1590
      }
1591

1592
      void tls_alert(Botan::TLS::Alert alert) override {
1,911✔
1593
         if(alert.is_fatal()) {
1,911✔
1594
            shim_log("Got a fatal alert " + alert.type_string());
39✔
1595
         } else {
1596
            shim_log("Got a warning alert " + alert.type_string());
5,694✔
1597
         }
1598

1599
         if(alert.type() == Botan::TLS::Alert::RecordOverflow) {
1,911✔
1600
            shim_exit_with_error(":TLSV1_ALERT_RECORD_OVERFLOW:");
4✔
1601
         }
1602

1603
         if(alert.type() == Botan::TLS::Alert::DecompressionFailure) {
1,907✔
1604
            shim_exit_with_error(":SSLV3_ALERT_DECOMPRESSION_FAILURE:");
1✔
1605
         }
1606

1607
         if(!alert.is_fatal()) {
1,906✔
1608
            m_warning_alerts++;
1,898✔
1609
            if(m_warning_alerts > 5) {
1,898✔
1610
               shim_exit_with_error(":TOO_MANY_WARNING_ALERTS:");
3✔
1611
            }
1612
         }
1613

1614
         if(alert.type() == Botan::TLS::Alert::CloseNotify) {
1,903✔
1615
            if(!m_got_close && !m_args.flag_set("shim-shuts-down")) {
3,750✔
1616
               shim_log("Sending return close notify");
1,848✔
1617
               m_channel->send_alert(alert);
1,848✔
1618
            }
1619
            m_got_close = true;
1,866✔
1620
         } else if(alert.is_fatal()) {
37✔
1621
            shim_exit_with_error("Unexpected fatal alert " + alert.type_string());
16✔
1622
         }
1623
      }
1,895✔
1624

1625
      void tls_session_established(const Botan::TLS::Session_Summary& session) override {
2,025✔
1626
         shim_log("Session established: " + Botan::hex_encode(session.session_id().get()) + " version " +
12,150✔
1627
                  session.version().to_string() + " cipher " + session.ciphersuite().to_string() + " " +
12,150✔
1628
                  std::string((session.supports_extended_master_secret() ? "with EMS" : "without EMS")));
2,040✔
1629
         // probably need tests here?
1630

1631
         m_policy.incr_session_established();
2,025✔
1632
         m_sessions_established++;
2,025✔
1633

1634
         if(m_args.flag_set("expect-no-session-id")) {
2,025✔
1635
            // BoGo expects that ticket issuance implies no stateful session...
1636
            if(!m_args.flag_set("server") && !session.session_id().empty()) {
112✔
1637
               shim_exit_with_error("Unexpectedly got a session ID");
×
1638
            }
1639
         } else if(m_args.flag_set("expect-session-id") && session.session_id().empty()) {
3,938✔
1640
            shim_exit_with_error("Unexpectedly got no session ID");
×
1641
         }
1642

1643
         if(m_args.option_used("expect-version")) {
2,025✔
1644
            if(session.version().version_code() != m_args.get_int_opt("expect-version")) {
×
1645
               shim_exit_with_error("Unexpected version");
×
1646
            }
1647
         }
1648

1649
         if(m_args.flag_set("expect-secure-renegotiation")) {
2,025✔
1650
            if(!m_channel->secure_renegotiation_supported()) {
9✔
1651
               shim_exit_with_error("Expected secure renegotiation");
×
1652
            }
1653
         } else if(m_args.flag_set("expect-no-secure-renegotiation")) {
2,016✔
1654
            if(m_channel->secure_renegotiation_supported()) {
2✔
1655
               shim_exit_with_error("Expected no secure renegotiation");
×
1656
            }
1657
         }
1658

1659
         if(m_args.flag_set("expect-extended-master-secret")) {
2,025✔
1660
            if(!session.supports_extended_master_secret()) {
10✔
1661
               shim_exit_with_error("Expected extended maseter secret");
×
1662
            }
1663
         }
1664
      }
2,025✔
1665

1666
      void tls_session_activated() override {
2,012✔
1667
         if(m_args.flag_set("send-alert")) {
2,012✔
1668
            m_channel->send_fatal_alert(Botan::TLS::Alert::DecompressionFailure);
16✔
1669
            return;
16✔
1670
         }
1671

1672
         if(const size_t length = m_args.get_int_opt_or_else("export-keying-material", 0)) {
1,996✔
1673
            const std::string label = m_args.get_string_opt("export-label");
176✔
1674
            const std::string context = m_args.get_string_opt("export-context");
176✔
1675
            const auto exported = m_channel->key_material_export(label, context, length);
176✔
1676
            shim_log("Sending " + std::to_string(length) + " bytes of key material");
704✔
1677
            m_channel->send(exported.bits_of());
352✔
1678
         }
176✔
1679

1680
         const std::string alpn = m_channel->application_protocol();
1,996✔
1681

1682
         if(m_args.option_used("expect-alpn")) {
1,996✔
1683
            if(alpn != m_args.get_string_opt("expect-alpn")) {
10✔
1684
               shim_exit_with_error("Got unexpected ALPN");
×
1685
            }
1686
         }
1687

1688
         if(alpn == "baz" && !m_args.flag_set("allow-unknown-alpn-protos")) {
2,005✔
1689
            throw Botan::TLS::TLS_Exception(Botan::TLS::Alert::IllegalParameter, "Unexpected ALPN protocol");
3✔
1690
         }
1691

1692
         if(m_args.flag_set("shim-shuts-down")) {
1,993✔
1693
            shim_log("Shim shutting down");
45✔
1694
            m_channel->close();
45✔
1695
         }
1696

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

1700
            std::vector<uint8_t> buf(32769, 0x42);
×
1701

1702
            for(const size_t sz : record_sizes) {
×
1703
               m_channel->send(buf.data(), sz);
×
1704
            }
1705

1706
            m_channel->close();
×
1707
         }
×
1708

1709
         if(m_args.flag_set("expect-hrr") && !m_hello_retry_request) {
3,986✔
1710
            throw Shim_Exception("Expected Hello Retry Request but didn't see one");
×
1711
         }
1712

1713
         if(m_args.flag_set("expect-no-hrr") && m_hello_retry_request) {
3,986✔
1714
            throw Shim_Exception("Hello Retry Request seen but didn't expect one");
×
1715
         }
1716

1717
         if(m_args.flag_set("key-update")) {
1,993✔
1718
            shim_log("Updating traffic keys without asking for reciprocation");
5✔
1719
            m_channel->update_traffic_keys(false /* don't request reciprocal update */);
2✔
1720
         }
1721
      }
1,993✔
1722

1723
      std::chrono::system_clock::time_point tls_current_timestamp() override {
8,910✔
1724
         // Some tests require precise timings. Hence, the TLS 'now' timestamp
1725
         // is frozen on first access and rounded to the last full second. E.g.
1726
         // storage of sessions does store the timestamp with second-resolution.
1727
         using sec = std::chrono::seconds;
8,910✔
1728
         static auto g_now = std::chrono::floor<sec>(std::chrono::system_clock::now());
8,910✔
1729
         return g_now + m_clock_skew;
8,910✔
1730
      }
1731

1732
      void tls_inspect_handshake_msg(const Botan::TLS::Handshake_Message& msg) override {
21,459✔
1733
         if(msg.type() == Botan::TLS::Handshake_Type::HelloRetryRequest) {
21,459✔
1734
            m_hello_retry_request = true;
82✔
1735
         }
1736
      }
21,459✔
1737

1738
   private:
1739
      Botan::TLS::Channel* m_channel;
1740
      const Shim_Arguments& m_args;
1741
      Shim_Policy& m_policy;
1742
      Shim_Socket& m_socket;
1743
      const bool m_is_datagram;
1744
      size_t m_warning_alerts;
1745
      size_t m_empty_records;
1746
      size_t m_sessions_established;
1747
      bool m_got_close;
1748
      bool m_hello_retry_request;
1749
      std::chrono::seconds m_clock_skew;
1750
};
1751

1752
}  // namespace
1753

1754
int main(int /*argc*/, char* argv[]) {
2,107✔
1755
   try {
2,107✔
1756
      std::unique_ptr<Shim_Arguments> args = parse_options(argv);
2,107✔
1757

1758
      if(args->flag_set("is-handshaker-supported")) {
2,107✔
1759
         return shim_output("No\n");
1✔
1760
      }
1761

1762
      const uint16_t port = static_cast<uint16_t>(args->get_int_opt("port"));
2,106✔
1763
      const size_t resume_count = args->get_int_opt_or_else("resume-count", 0);
2,106✔
1764
      const bool is_server = args->flag_set("server");
2,106✔
1765
      const bool is_datagram = args->flag_set("dtls");
2,106✔
1766
      const size_t buf_size = args->get_int_opt_or_else("read-size", 18 * 1024);
2,106✔
1767

1768
      auto rng = std::make_shared<Botan::ChaCha_RNG>(Botan::secure_vector<uint8_t>(64));
4,212✔
1769
      auto creds = std::make_shared<Shim_Credentials>(*args);
2,106✔
1770
      auto session_manager = [&]() -> std::shared_ptr<Botan::TLS::Session_Manager> {
805✔
1771
         if(args->flag_set("no-ticket") || args->flag_set("on-resume-no-ticket")) {
4,210✔
1772
            // The in-memory session manager stores sessions in volatile memory and
1773
            // hands out Session_IDs (i.e. does not utilize session tickets)
1774
            return std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng, 1024);
2✔
1775
         } else {
1776
            // The hybrid session manager prefers stateless tickets (when used in
1777
            // servers) but can also fall back to stateful management when tickets
1778
            // are not an option.
1779
            return std::make_shared<Botan::TLS::Session_Manager_Hybrid>(
2,104✔
1780
               std::make_unique<Botan::TLS::Session_Manager_In_Memory>(rng, 1024), creds, rng);
4,208✔
1781
         }
1782
      }();
2,106✔
1783

1784
      if(args->flag_set("wait-for-debugger")) {
2,911✔
1785
         sleep(20);
×
1786
      }
1787

1788
      for(size_t i = 0; i != resume_count + 1; ++i) {
3,998✔
1789
         auto execute_test = [&](const std::string& hostname) {
5,430✔
1790
            Shim_Socket socket(hostname, port, args->flag_set("ipv6"));
2,715✔
1791

1792
            shim_log("Connection " + std::to_string(i + 1) + "/" + std::to_string(resume_count + 1));
10,860✔
1793

1794
            // The ShimID must be written on the socket as a 64-bit little-endian integer
1795
            // *before* any test data is transferred
1796
            // See: https://github.com/google/boringssl/commit/50ee09552cde1c2019bef24520848d041920cfd4
1797
            shim_log("Sending ShimID: " + std::to_string(args->get_int_opt("shim-id")));
8,145✔
1798
            std::array<uint8_t, 8> shim_id{};
2,715✔
1799
            Botan::store_le(static_cast<uint64_t>(args->get_int_opt("shim-id")), shim_id.data());
3,520✔
1800
            socket.write(shim_id.data(), shim_id.size());
2,715✔
1801

1802
            auto policy = std::make_shared<Shim_Policy>(*args);
2,715✔
1803
            auto callbacks = std::make_shared<Shim_Callbacks>(*args, socket, *policy);
2,715✔
1804

1805
            if(args->option_used("resumption-delay") && i > 0) {
5,430✔
1806
               shim_log("skewing the clock by " + std::to_string(args->get_int_opt("resumption-delay")) + " seconds");
12✔
1807
               callbacks->set_clock_skew(std::chrono::seconds(args->get_int_opt("resumption-delay")));
808✔
1808
            }
1809

1810
            std::unique_ptr<Botan::TLS::Channel> chan;
2,715✔
1811

1812
            if(is_server) {
2,715✔
1813
               chan = std::make_unique<Botan::TLS::Server>(callbacks, session_manager, creds, policy, rng, is_datagram);
1,222✔
1814
            } else {
1815
               const Botan::TLS::Protocol_Version offer_version = policy->latest_supported_version(is_datagram);
1,493✔
1816
               shim_log("Offering " + offer_version.to_string());
4,473✔
1817

1818
               std::string host_name = args->get_string_opt_or_else("host-name", hostname);
1,491✔
1819
               if(args->test_name().starts_with("UnsolicitedServerNameAck")) {
2,982✔
1820
                  host_name = "";  // avoid sending SNI for this test
3✔
1821
               }
1822

1823
               const Botan::TLS::Server_Information server_info(host_name, port);
1,491✔
1824
               const std::vector<std::string> next_protocols = args->get_alpn_string_vec_opt("advertise-alpn");
1,491✔
1825
               chan = std::make_unique<Botan::TLS::Client>(
1,491✔
1826
                  callbacks, session_manager, creds, policy, rng, server_info, offer_version, next_protocols);
1,491✔
1827
            }
2,982✔
1828

1829
            callbacks->set_channel(chan.get());
2,713✔
1830

1831
            std::vector<uint8_t> buf(buf_size);
3,518✔
1832

1833
            for(;;) {
209,114✔
1834
               if(is_datagram) {
209,114✔
1835
                  uint8_t opcode = 0;
184,524✔
1836
                  const size_t got = socket.read(&opcode, 1);
184,524✔
1837
                  if(got == 0) {
184,524✔
1838
                     shim_log("EOF on socket");
578✔
1839
                     break;
578✔
1840
                  }
1841

1842
                  if(opcode == 'P') {
183,946✔
1843
                     uint8_t len_bytes[4];
183,946✔
1844
                     socket.read_exactly(len_bytes, sizeof(len_bytes));
183,946✔
1845

1846
                     const size_t packet_len = Botan::load_be<uint32_t>(len_bytes, 0);
183,946✔
1847

1848
                     if(buf.size() < packet_len) {
183,946✔
1849
                        buf.resize(packet_len);
1✔
1850
                     }
1851
                     socket.read_exactly(buf.data(), packet_len);
183,946✔
1852

1853
                     chan->received_data(buf.data(), packet_len);
183,946✔
1854
                  } else if(opcode == 'T') {
×
1855
                     const uint8_t timeout_ack = 't';
×
1856

1857
                     uint8_t timeout_bytes[8];
×
1858
                     socket.read_exactly(timeout_bytes, sizeof(timeout_bytes));
×
1859

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

1862
                     shim_log("Timeout nsec " + std::to_string(nsec));
×
1863

1864
                     // FIXME handle this!
1865

1866
                     socket.write(&timeout_ack, 1);  // ack it anyway
×
1867
                  } else {
1868
                     shim_exit_with_error("Unknown opcode " + std::to_string(opcode));
×
1869
                  }
1870
               } else {
1871
                  const size_t got = socket.read(buf.data(), buf.size());
24,590✔
1872
                  if(got == 0) {
24,590✔
1873
                     shim_log("EOF on socket");
1,315✔
1874
                     break;
1,315✔
1875
                  }
1876

1877
                  shim_log("Got packet of " + std::to_string(got));
69,825✔
1878

1879
                  if(args->option_used("write-settings")) {
23,275✔
1880
                     // TODO: the transcript option should probably be used differently
1881
                     std::cout << "<<<" << std::endl
×
1882
                               << Botan::hex_encode(buf.data(), got) << std::endl
×
1883
                               << "<<<" << std::endl;
×
1884
                  }
1885

1886
                  if(args->flag_set("use-exporter-between-reads") && chan->is_active()) {
46,550✔
1887
                     chan->key_material_export("some label", "some context", 42);
1✔
1888
                  }
1889
                  const size_t needed = chan->received_data(buf.data(), got);
23,274✔
1890

1891
                  if(needed > 0) {
22,631✔
1892
                     shim_log("Short read still need " + std::to_string(needed));
42,801✔
1893
                  }
1894
               }
1895
            }
1896

1897
            if(args->flag_set("check-close-notify")) {
1,893✔
1898
               if(!callbacks->saw_close_notify()) {
27✔
1899
                  throw Shim_Exception("Unexpected SSL_shutdown result: -1 != 1");
1✔
1900
               }
1901
            }
1902

1903
            if(args->option_used("expect-total-renegotiations")) {
1,892✔
1904
               const size_t exp = args->get_int_opt("expect-total-renegotiations");
21✔
1905

1906
               if(exp != callbacks->sessions_established() - 1) {
21✔
1907
                  throw Shim_Exception("Unexpected number of renegotiations: saw " +
×
1908
                                       std::to_string(callbacks->sessions_established() - 1) + " exp " +
×
1909
                                       std::to_string(exp));
×
1910
               }
1911
            }
1912
            shim_log("End of resume loop");
2,695✔
1913
         };
10,786✔
1914
         try {
2,715✔
1915
            execute_test("localhost");
5,412✔
1916
         } catch(const Shim_Exception& e) {
805✔
1917
            if(std::string(e.what()) == "Failed to connect to host") {
1✔
1918
               execute_test("::1");
×
1919
            } else {
1920
               // NOLINTNEXTLINE(cert-err60-cpp)
1921
               throw e;
1✔
1922
            }
1923
         }
1✔
1924
      }
1925
   } catch(Shim_Exception& e) {
7,070✔
1926
      shim_exit_with_error(e.what(), e.rc());
1✔
1927
   } catch(std::exception& e) {
804✔
1928
      shim_exit_with_error(map_to_bogo_error(e.what()));
804✔
1929
   } catch(...) {
×
1930
      shim_exit_with_error("Unknown exception", 3);
×
1931
   }
×
1932
   return 0;
1,283✔
1933
}
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