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/tls_client.h>
14 #include <botan/tls_server.h>
15 #include <botan/tls_exceptn.h>
16 #include <botan/tls_algos.h>
17 #include <botan/data_src.h>
18 #include <botan/pkcs8.h>
19 #include <botan/loadstor.h>
20 #include <botan/oids.h>
21 #include <botan/chacha_rng.h>
22 #include <botan/base64.h>
23 #include <botan/hex.h>
24 #include <botan/parsing.h>
25 #include <botan/mem_ops.h>
26 #include <iostream>
27 #include <vector>
28 #include <string>
29 #include <map>
30 #include <set>
31 #include <ctime>
32 #include <unordered_map>
33
34 #if defined(BOTAN_TARGET_OS_HAS_SOCKETS)
35 #include <sys/socket.h>
36 #include <sys/time.h>
37 #include <netinet/in.h>
38 #include <netdb.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #endif
44
45 namespace {
46
shim_output(const std::string & s,int rc=0)47 int shim_output(const std::string& s, int rc = 0)
48 {
49 std::cout << s << "\n";
50 return rc;
51 }
52
shim_log(const std::string & s)53 void shim_log(const std::string& s)
54 {
55 if(::getenv("BOTAN_BOGO_SHIM_LOG"))
56 {
57 static FILE* log = std::fopen("/tmp/bogo_shim.log", "w");
58 struct timeval tv;
59 ::gettimeofday(&tv, nullptr);
60 std::fprintf(log, "%lld.%lu: %s\n", static_cast<unsigned long long>(tv.tv_sec), tv.tv_usec, s.c_str());
61 std::fflush(log);
62 }
63 }
64
shim_exit_with_error(const std::string & s,int rc=1)65 void BOTAN_NORETURN shim_exit_with_error(const std::string& s, int rc = 1)
66 {
67 shim_log("Exiting with " + s);
68 std::cerr << s << "\n";
69 std::exit(rc);
70 }
71
map_to_bogo_error(const std::string & e)72 std::string map_to_bogo_error(const std::string& e)
73 {
74 shim_log("Original error " + e);
75
76 static const std::unordered_map<std::string, std::string> err_map
77 {
78 { "Application data before handshake done", ":APPLICATION_DATA_INSTEAD_OF_HANDSHAKE:" },
79 { "Bad Hello_Request, has non-zero size", ":BAD_HELLO_REQUEST:" },
80 { "Bad code for TLS alert level", ":UNKNOWN_ALERT_TYPE:" },
81 { "Bad extension size", ":DECODE_ERROR:" },
82 { "Bad length in hello verify request", ":DECODE_ERROR:" },
83 { "Bad lengths in DTLS header", ":BAD_HANDSHAKE_RECORD:" },
84 { "Bad signature on server key exchange", ":BAD_SIGNATURE:" },
85 { "Bad size (1) for TLS alert message", ":BAD_ALERT:" },
86 { "Bad size (4) for TLS alert message", ":BAD_ALERT:" },
87 { "CERTIFICATE decoding failed with PEM: No PEM header found", ":CANNOT_PARSE_LEAF_CERT:" },
88 { "Can't agree on a ciphersuite with client", ":NO_SHARED_CIPHER:" },
89 { "Can't interleave application and handshake data", ":UNEXPECTED_RECORD:" },
90 { "Certificate chain exceeds policy specified maximum size", ":EXCESSIVE_MESSAGE_SIZE:" },
91 { "Certificate key type did not match ciphersuite", ":WRONG_CERTIFICATE_TYPE:" },
92 { "Certificate usage constraints do not allow this ciphersuite", ":KEY_USAGE_BIT_INCORRECT:" },
93 { "Certificate: Message malformed", ":DECODE_ERROR:" },
94 { "Channel::key_material_export cannot export during renegotiation", "failed to export keying material" },
95 { "Client cert verify failed", ":BAD_SIGNATURE:" },
96 { "Client did not offer NULL compression", ":INVALID_COMPRESSION_LIST:" },
97 { "Client offered TLS version with major version under 3", ":UNSUPPORTED_PROTOCOL:" },
98 { "Client offered DTLS version with major version 0xFF", ":UNSUPPORTED_PROTOCOL:" },
99 { "Client policy prohibits insecure renegotiation", ":RENEGOTIATION_MISMATCH:" },
100 { "Client policy prohibits renegotiation", ":NO_RENEGOTIATION:" },
101 { "Client resumed extended ms session without sending extension", ":RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION:" },
102 { "Client signalled fallback SCSV, possible attack", ":INAPPROPRIATE_FALLBACK:" },
103 { "Client version DTLS v1.0 is unacceptable by policy", ":UNSUPPORTED_PROTOCOL:" },
104 { "Client version TLS v1.0 is unacceptable by policy", ":UNSUPPORTED_PROTOCOL:" },
105 { "Client version TLS v1.1 is unacceptable by policy", ":UNSUPPORTED_PROTOCOL:" },
106 { "Client: No certificates sent by server", ":DECODE_ERROR:" },
107 { "Counterparty sent inconsistent key and sig types", ":WRONG_SIGNATURE_TYPE:" },
108 { "Downgrade attack detected", ":TLS13_DOWNGRADE:" },
109 { "Empty ALPN protocol not allowed", ":PARSE_TLSEXT:" },
110 { "Encoding error: Cannot encode PSS string, output length too small", ":NO_COMMON_SIGNATURE_ALGORITHMS:" },
111 { "Expected TLS but got a record with DTLS version", ":WRONG_VERSION_NUMBER:" },
112 { "Finished message didn't verify", ":DIGEST_CHECK_FAILED:" },
113 { "Got unexpected TLS record version", ":WRONG_VERSION_NUMBER:" },
114 { "Inconsistent length in certificate request", ":DECODE_ERROR:" },
115 { "Inconsistent values in fragmented DTLS handshake header", ":FRAGMENT_MISMATCH:" },
116 { "Invalid CertificateRequest: Length field outside parameters", ":DECODE_ERROR:" },
117 { "Invalid CertificateVerify: Extra bytes at end of message", ":DECODE_ERROR:" },
118 { "Invalid Certificate_Status: invalid length field", ":DECODE_ERROR:" },
119 { "Invalid ChangeCipherSpec", ":BAD_CHANGE_CIPHER_SPEC:" },
120 { "Invalid ClientHello: Length field outside parameters", ":DECODE_ERROR:" },
121 { "Invalid ClientKeyExchange: Extra bytes at end of message", ":DECODE_ERROR:" },
122 { "Invalid ServerKeyExchange: Extra bytes at end of message", ":DECODE_ERROR:" },
123 { "Invalid SessionTicket: Extra bytes at end of message", ":DECODE_ERROR:" },
124 { "Invalid authentication tag: ChaCha20Poly1305 tag check failed", ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:" },
125 { "Invalid authentication tag: GCM tag check failed", ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:" },
126 { "Message authentication failure", ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:" },
127 { "No shared TLS version", ":UNSUPPORTED_PROTOCOL:" },
128 { "No shared DTLS version", ":UNSUPPORTED_PROTOCOL:" },
129 { "OS2ECP: Unknown format type 251", ":BAD_ECPOINT:" },
130 { "Policy forbids all available TLS version", ":NO_SUPPORTED_VERSIONS_ENABLED:" },
131 { "Policy forbids all available DTLS version", ":NO_SUPPORTED_VERSIONS_ENABLED:" },
132 { "Policy refuses to accept signing with any hash supported by peer", ":NO_COMMON_SIGNATURE_ALGORITHMS:" },
133 { "Policy requires client send a certificate, but it did not", ":PEER_DID_NOT_RETURN_A_CERTIFICATE:" },
134 { "Received a record that exceeds maximum size", ":ENCRYPTED_LENGTH_TOO_LONG:" },
135 { "Received unexpected record version in initial record", ":WRONG_VERSION_NUMBER:" },
136 { "Received unexpected record version", ":WRONG_VERSION_NUMBER:" },
137 { "Received application data after connection closure", ":APPLICATION_DATA_ON_SHUTDOWN:" },
138 { "Received handshake data after connection closure", ":NO_RENEGOTIATION:" },
139 { "Server certificate changed during renegotiation", ":SERVER_CERT_CHANGED:" },
140 { "Server changed its mind about extended master secret", ":RENEGOTIATION_EMS_MISMATCH:" },
141 { "Server changed its mind about secure renegotiation", ":RENEGOTIATION_MISMATCH:" },
142 { "Server changed version after renegotiation", ":WRONG_SSL_VERSION:" },
143 { "Server downgraded version after renegotiation", ":WRONG_SSL_VERSION:" },
144 { "Server policy prohibits renegotiation", ":NO_RENEGOTIATION:" },
145 { "Server replied using a ciphersuite not allowed in version it offered", ":WRONG_CIPHER_RETURNED:" },
146 { "Server replied with DTLS-SRTP alg we did not send", ":BAD_SRTP_PROTECTION_PROFILE_LIST:" },
147 { "Server replied with ciphersuite we didn't send", ":WRONG_CIPHER_RETURNED:" },
148 { "Server replied with later version than client offered", ":UNSUPPORTED_PROTOCOL:" },
149 { "Server replied with non-null compression method", ":UNSUPPORTED_COMPRESSION_ALGORITHM:" },
150 { "Server replied with some unknown ciphersuite", ":UNKNOWN_CIPHER_RETURNED:" },
151 { "Server replied with unsupported extensions: 0", ":UNEXPECTED_EXTENSION:" },
152 { "Server replied with unsupported extensions: 1234", ":UNEXPECTED_EXTENSION:" },
153 { "Server replied with unsupported extensions: 16", ":UNEXPECTED_EXTENSION:" },
154 { "Server replied with unsupported extensions: 43", ":UNEXPECTED_EXTENSION:" },
155 { "Server replied with unsupported extensions: 5", ":UNEXPECTED_EXTENSION:" },
156 { "Server resumed session and removed extended master secret", ":RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION:" },
157 { "Server resumed session but added extended master secret", ":RESUMED_NON_EMS_SESSION_WITH_EMS_EXTENSION:" },
158 { "Server resumed session but with wrong version", ":OLD_SESSION_VERSION_NOT_RETURNED:" },
159 { "Server sent ECC curve prohibited by policy", ":WRONG_CURVE:" },
160 { "Server sent an unsupported extension", ":UNEXPECTED_EXTENSION:" },
161 { "Server sent bad values for secure renegotiation", ":RENEGOTIATION_MISMATCH:" },
162 { "Server version DTLS v1.0 is unacceptable by policy", ":UNSUPPORTED_PROTOCOL:" },
163 { "Server version TLS v1.0 is unacceptable by policy", ":UNSUPPORTED_PROTOCOL:" },
164 { "Server version TLS v1.1 is unacceptable by policy", ":UNSUPPORTED_PROTOCOL:" },
165 { "Server_Hello_Done: Must be empty, and is not", ":DECODE_ERROR:" },
166 { "Simulated OCSP callback failure", ":OCSP_CB_ERROR:" },
167 { "Simulating cert verify callback failure", ":CERT_CB_ERROR:" },
168 { "Simulating failure from OCSP response callback", ":OCSP_CB_ERROR:" },
169 { "TLS plaintext record is larger than allowed maximum", ":DATA_LENGTH_TOO_LONG:" },
170 { "TLS signature extension did not allow for RSA/SHA-256 signature", ":WRONG_SIGNATURE_TYPE:", },
171 { "Test requires rejecting cert", ":CERTIFICATE_VERIFY_FAILED:" },
172 { "Unexpected ALPN protocol", ":INVALID_ALPN_PROTOCOL:" },
173 { "Unexpected record type 42 from counterparty", ":UNEXPECTED_RECORD:" },
174
175 { "Unexpected state transition in handshake got a certificate_request expected server_hello_done seen server_hello+server_key_exchange", ":UNEXPECTED_MESSAGE:" },
176 { "Unexpected state transition in handshake got a certificate_request expected server_key_exchange|server_hello_done seen server_hello", ":UNEXPECTED_MESSAGE:" },
177 { "Unexpected state transition in handshake got a certificate_status expected certificate seen server_hello", ":UNEXPECTED_MESSAGE:" },
178 { "Unexpected state transition in handshake got a change_cipher_spec expected certificate_verify seen client_hello+certificate+client_key_exchange", ":UNEXPECTED_RECORD:" },
179 { "Unexpected state transition in handshake got a change_cipher_spec expected client_key_exchange seen client_hello", ":UNEXPECTED_RECORD:" },
180 { "Unexpected state transition in handshake got a change_cipher_spec expected new_session_ticket seen server_hello+certificate+certificate_status+server_key_exchange+server_hello_done", ":UNEXPECTED_RECORD:" },
181 { "Unexpected state transition in handshake got a client_key_exchange expected certificate seen client_hello", ":UNEXPECTED_MESSAGE:" },
182 { "Unexpected state transition in handshake got a finished expected change_cipher_spec seen client_hello", ":UNEXPECTED_RECORD:" },
183 { "Unexpected state transition in handshake got a finished expected change_cipher_spec seen client_hello+client_key_exchange", ":UNEXPECTED_RECORD:" },
184 { "Unexpected state transition in handshake got a finished expected change_cipher_spec seen server_hello", ":UNEXPECTED_RECORD:" },
185 { "Unexpected state transition in handshake got a finished expected change_cipher_spec seen server_hello+certificate+certificate_status+server_key_exchange+server_hello_done+new_session_ticket", ":UNEXPECTED_RECORD:" },
186 { "Unexpected state transition in handshake got a hello_request expected server_hello", ":UNEXPECTED_MESSAGE:" },
187 { "Unexpected state transition in handshake got a server_hello_done expected server_key_exchange seen server_hello+certificate+certificate_status", ":UNEXPECTED_MESSAGE:" },
188 { "Unexpected state transition in handshake got a server_key_exchange not expecting messages", ":BAD_HELLO_REQUEST:" },
189 { "Unexpected state transition in handshake got a server_key_exchange expected certificate_request|server_hello_done seen server_hello+certificate+certificate_status", ":UNEXPECTED_MESSAGE:" },
190
191 { "Unknown TLS handshake message type 43", ":UNEXPECTED_MESSAGE:" },
192 { "Unknown TLS handshake message type 44", ":UNEXPECTED_MESSAGE:" },
193 { "Unknown TLS handshake message type 45", ":UNEXPECTED_MESSAGE:" },
194 { "Unknown TLS handshake message type 46", ":UNEXPECTED_MESSAGE:" },
195 { "Unknown TLS handshake message type 53", ":UNEXPECTED_MESSAGE:" },
196 { "Unknown TLS handshake message type 54", ":UNEXPECTED_MESSAGE:" },
197 { "Unknown TLS handshake message type 55", ":UNEXPECTED_MESSAGE:" },
198 { "Unknown TLS handshake message type 56", ":UNEXPECTED_MESSAGE:" },
199 { "Unknown TLS handshake message type 57", ":UNEXPECTED_MESSAGE:" },
200 { "Unknown TLS handshake message type 58", ":UNEXPECTED_MESSAGE:" },
201 { "Unknown TLS handshake message type 6", ":UNEXPECTED_MESSAGE:" },
202 { "Unknown TLS handshake message type 62", ":UNEXPECTED_MESSAGE:" },
203 { "Unknown TLS handshake message type 64", ":UNEXPECTED_MESSAGE:" },
204 { "signature_algorithm_of_scheme: Unknown signature algorithm enum", ":WRONG_SIGNATURE_TYPE:" },
205 };
206
207 auto err_map_i = err_map.find(e);
208 if(err_map_i != err_map.end())
209 return err_map_i->second;
210
211 return "Unmapped error: '" + e + "'";
212 }
213
214 class Shim_Exception final : public std::exception
215 {
216 public:
Shim_Exception(const std::string & msg,int rc=1)217 Shim_Exception(const std::string& msg, int rc = 1) :
218 m_msg(msg), m_rc(rc) {}
219
what() const220 const char* what() const noexcept override { return m_msg.c_str(); }
221
rc() const222 int rc() const { return m_rc; }
223 private:
224 const std::string m_msg;
225 int m_rc;
226 };
227
228 #if defined(BOTAN_TARGET_OS_HAS_SOCKETS)
229
230 class Shim_Socket final
231 {
232 private:
233 typedef int socket_type;
234 typedef ssize_t socket_op_ret_type;
close_socket(socket_type s)235 static void close_socket(socket_type s) { ::close(s); }
get_last_socket_error()236 static std::string get_last_socket_error() { return ::strerror(errno); }
237
238 public:
Shim_Socket(const std::string & hostname,int port)239 Shim_Socket(const std::string& hostname, int port) : m_socket(-1)
240 {
241 addrinfo hints;
242 std::memset(&hints, 0, sizeof(hints));
243 hints.ai_family = AF_UNSPEC;
244 hints.ai_socktype = SOCK_STREAM;
245 hints.ai_flags = AI_NUMERICSERV;
246 addrinfo* res;
247
248 const std::string service = std::to_string(port);
249 int rc = ::getaddrinfo(hostname.c_str(), service.c_str(), &hints, &res);
250 shim_log("Connecting " + hostname + ":" + service);
251
252 if(rc != 0)
253 {
254 throw Shim_Exception("Name resolution failed for " + hostname);
255 }
256
257 for(addrinfo* rp = res; (m_socket == -1) && (rp != nullptr); rp = rp->ai_next)
258 {
259 if(rp->ai_family != AF_INET && rp->ai_family != AF_INET6)
260 continue;
261
262 m_socket = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
263
264 if(m_socket == -1)
265 {
266 // unsupported socket type?
267 continue;
268 }
269
270 int err = ::connect(m_socket, rp->ai_addr, rp->ai_addrlen);
271
272 if(err != 0)
273 {
274 ::close(m_socket);
275 m_socket = -1;
276 }
277 }
278
279 if(m_socket < 0)
280 throw Shim_Exception("Failed to connect to host");
281 }
282
~Shim_Socket()283 ~Shim_Socket()
284 {
285 ::close(m_socket);
286 m_socket = -1;
287 }
288
write(const uint8_t buf[],size_t len)289 void write(const uint8_t buf[], size_t len)
290 {
291 if(m_socket < 0)
292 throw Shim_Exception("Socket was bad on write");
293 size_t sent_so_far = 0;
294 while(sent_so_far != len)
295 {
296 const size_t left = len - sent_so_far;
297 socket_op_ret_type sent = ::send(m_socket, Botan::cast_uint8_ptr_to_char(&buf[sent_so_far]), left, MSG_NOSIGNAL);
298 if(sent < 0)
299 {
300 if(errno == EPIPE)
301 return;
302 else
303 throw Shim_Exception("Socket write failed", errno);
304 }
305 else
306 sent_so_far += static_cast<size_t>(sent);
307 }
308 }
309
read(uint8_t buf[],size_t len)310 size_t read(uint8_t buf[], size_t len)
311 {
312 if(m_socket < 0)
313 throw Shim_Exception("Socket was bad on read");
314 socket_op_ret_type got = ::read(m_socket, Botan::cast_uint8_ptr_to_char(buf), len);
315
316 if(got < 0)
317 {
318 if(errno == ECONNRESET)
319 return 0;
320 throw Shim_Exception("Socket read failed: " + std::string(strerror(errno)));
321 }
322
323 return static_cast<size_t>(got);
324 }
325
read_exactly(uint8_t buf[],size_t len)326 void read_exactly(uint8_t buf[], size_t len)
327 {
328 if(m_socket < 0)
329 throw Shim_Exception("Socket was bad on read");
330
331 while(len > 0)
332 {
333 socket_op_ret_type got = ::read(m_socket, Botan::cast_uint8_ptr_to_char(buf), len);
334
335 if(got == 0)
336 throw Shim_Exception("Socket read EOF");
337 else if(got < 0)
338 throw Shim_Exception("Socket read failed: " + std::string(strerror(errno)));
339
340 buf += static_cast<size_t>(got);
341 len -= static_cast<size_t>(got);
342 }
343 }
344
345 private:
346 socket_type m_socket;
347 };
348
349 #endif
350
combine_options(const std::set<std::string> & a,const std::set<std::string> & b,const std::set<std::string> & c,const std::set<std::string> & d)351 std::set<std::string> combine_options(
352 const std::set<std::string>& a,
353 const std::set<std::string>& b,
354 const std::set<std::string>& c,
355 const std::set<std::string>& d)
356 {
357 std::set<std::string> combined;
358
359 for(auto i : a)
360 combined.insert(i);
361 for(auto i : b)
362 combined.insert(i);
363 for(auto i : c)
364 combined.insert(i);
365 for(auto i : d)
366 combined.insert(i);
367
368 return combined;
369 }
370
371 class Shim_Arguments final
372 {
373 public:
Shim_Arguments(const std::set<std::string> & flags,const std::set<std::string> & string_opts,const std::set<std::string> & base64_opts,const std::set<std::string> & int_opts,const std::set<std::string> & int_vec_opts)374 Shim_Arguments(const std::set<std::string>& flags,
375 const std::set<std::string>& string_opts,
376 const std::set<std::string>& base64_opts,
377 const std::set<std::string>& int_opts,
378 const std::set<std::string>& int_vec_opts) :
379 m_flags(flags),
380 m_string_opts(string_opts),
381 m_base64_opts(base64_opts),
382 m_int_opts(int_opts),
383 m_int_vec_opts(int_vec_opts),
384 m_all_options(combine_options(string_opts, base64_opts, int_opts, int_vec_opts))
385 {}
386
387 void parse_args(char* argv[]);
388
flag_set(const std::string & flag) const389 bool flag_set(const std::string& flag) const
390 {
391 if(m_flags.count(flag) == 0)
392 throw Shim_Exception("Unknown bool flag " + flag);
393
394 return m_parsed_flags.count(flag);
395 }
396
test_name() const397 std::string test_name() const
398 {
399 return get_string_opt("test-name");
400 }
401
get_string_opt(const std::string & key) const402 std::string get_string_opt(const std::string& key) const
403 {
404 if(m_string_opts.count(key) == 0)
405 throw Shim_Exception("Unknown string key " + key);
406 return get_opt(key);
407 }
408
get_string_opt_or_else(const std::string & key,const std::string & def) const409 std::string get_string_opt_or_else(const std::string& key, const std::string& def) const
410 {
411 if(m_string_opts.count(key) == 0)
412 throw Shim_Exception("Unknown string key " + key);
413 if(!option_used(key))
414 return def;
415 return get_opt(key);
416 }
417
get_b64_opt(const std::string & key) const418 std::vector<uint8_t> get_b64_opt(const std::string& key) const
419 {
420 if(m_base64_opts.count(key) == 0)
421 throw Shim_Exception("Unknown base64 key " + key);
422 return Botan::unlock(Botan::base64_decode(get_opt(key)));
423 }
424
get_int_opt(const std::string & key) const425 size_t get_int_opt(const std::string& key) const
426 {
427 if(m_int_opts.count(key) == 0)
428 throw Shim_Exception("Unknown int key " + key);
429 return Botan::to_u32bit(get_opt(key));
430 }
431
get_int_opt_or_else(const std::string & key,size_t def) const432 size_t get_int_opt_or_else(const std::string& key, size_t def) const
433 {
434 if(m_int_opts.count(key) == 0)
435 throw Shim_Exception("Unknown int key " + key);
436 if(!option_used(key))
437 return def;
438
439 return Botan::to_u32bit(get_opt(key));
440 }
441
get_int_vec_opt(const std::string & key) const442 std::vector<size_t> get_int_vec_opt(const std::string& key) const
443 {
444 if(m_int_vec_opts.count(key) == 0)
445 throw Shim_Exception("Unknown int vec key " + key);
446
447 auto i = m_parsed_int_vec_opts.find(key);
448 if(i == m_parsed_int_vec_opts.end())
449 return std::vector<size_t>();
450 else
451 return i->second;
452 }
453
get_alpn_string_vec_opt(const std::string & option) const454 std::vector<std::string> get_alpn_string_vec_opt(const std::string& option) const
455 {
456 // hack used for alpn list (relies on all ALPNs being 3 chars long...)
457 char delim = 0x03;
458
459 if(option_used(option))
460 return Botan::split_on(get_string_opt(option), delim);
461 else
462 return std::vector<std::string>();
463 }
464
option_used(const std::string & key) const465 bool option_used(const std::string& key) const
466 {
467 if(m_all_options.count(key) == 0)
468 throw Shim_Exception("Invalid option " + key);
469 if(m_parsed_opts.find(key) != m_parsed_opts.end())
470 return true;
471 if(m_parsed_int_vec_opts.find(key) != m_parsed_int_vec_opts.end())
472 return true;
473 return false;
474 }
475
476 private:
get_opt(const std::string & key) const477 std::string get_opt(const std::string& key) const
478 {
479 auto i = m_parsed_opts.find(key);
480 if(i == m_parsed_opts.end())
481 throw Shim_Exception("Option " + key + " was not provided");
482 return i->second;
483 }
484
485 const std::set<std::string> m_flags;
486 const std::set<std::string> m_string_opts;
487 const std::set<std::string> m_base64_opts;
488 const std::set<std::string> m_int_opts;
489 const std::set<std::string> m_int_vec_opts;
490 const std::set<std::string> m_all_options;
491
492 std::set<std::string> m_parsed_flags;
493 std::map<std::string, std::string> m_parsed_opts;
494 std::map<std::string, std::vector<size_t>> m_parsed_int_vec_opts;
495 };
496
parse_args(char * argv[])497 void Shim_Arguments::parse_args(char* argv[])
498 {
499 int i = 1; // skip argv[0]
500
501 while(argv[i] != nullptr)
502 {
503 const std::string param(argv[i]);
504
505 if(param.find("-") == 0)
506 {
507 const std::string flag_name = param.substr(1, std::string::npos);
508
509 if(m_flags.count(flag_name))
510 {
511 shim_log("flag " + flag_name);
512 m_parsed_flags.insert(flag_name);
513 i += 1;
514 }
515 else if(m_all_options.count(flag_name))
516 {
517 if(argv[i+1] == nullptr)
518 throw Shim_Exception("Expected argument following " + param);
519 std::string val(argv[i+1]);
520 shim_log("param " + flag_name + "=" + val);
521
522 if(m_int_vec_opts.count(flag_name))
523 {
524 const size_t v = Botan::to_u32bit(val);
525 m_parsed_int_vec_opts[flag_name].push_back(v);
526 }
527 else
528 {
529 m_parsed_opts[flag_name] = val;
530 }
531 i += 2;
532 }
533 else
534 {
535 shim_log("Unknown option " + param);
536 throw Shim_Exception("Unknown option " + param, 89);
537 }
538 }
539 else
540 {
541 shim_log("Unknown option " + param);
542 throw Shim_Exception("Unknown option " + param, 89);
543 }
544 }
545 }
546
parse_options(char * argv[])547 std::unique_ptr<Shim_Arguments> parse_options(char* argv[])
548 {
549 const std::set<std::string> bogo_shim_flags = {
550 "allow-false-start-without-alpn",
551 "allow-unknown-alpn-protos",
552 "async",
553 "cbc-record-splitting",
554 "check-close-notify",
555 "decline-alpn",
556 "decline-ocsp-callback",
557 "dtls",
558 "enable-all-curves",
559 "enable-channel-id",
560 "enable-early-data",
561 "enable-ed25519",
562 "enable-grease",
563 "enable-ocsp-stapling",
564 "enable-signed-cert-timestamps",
565 "enforce-rsa-key-usage",
566 //"expect-accept-early-data",
567 "expect-extended-master-secret",
568 "expect-no-offer-early-data",
569 "expect-no-secure-renegotiation",
570 "expect-no-session",
571 "expect-no-session-id",
572 //"expect-reject-early-data",
573 "expect-secure-renegotiation",
574 "expect-session-id",
575 "expect-session-miss",
576 "expect-sha256-client-cert",
577 "expect-ticket-renewal",
578 "expect-ticket-supports-early-data",
579 //"expect-tls13-downgrade",
580 "expect-verify-result",
581 "expect-no-hrr",
582 //"export-traffic-secrets",
583 "fail-cert-callback",
584 //"fail-ddos-callback",
585 //"fail-early-callback",
586 "fail-ocsp-callback",
587 "fallback-scsv",
588 //"false-start",
589 "forbid-renegotiation-after-handshake",
590 "handoff",
591 "handshake-never-done",
592 "handshake-twice",
593 "handshaker-resume",
594 //"ignore-tls13-downgrade",
595 "implicit-handshake",
596 "install-cert-compression-algs",
597 "install-ddos-callback",
598 "is-handshaker-supported",
599 //"jdk11-workaround",
600 //"key-update",
601 "no-op-extra-handshake",
602 "no-rsa-pss-rsae-certs",
603 "no-ticket",
604 "no-tls1",
605 "no-tls11",
606 "no-tls12",
607 "no-tls13", // implict due to 1.3 not being implemented
608 "on-resume-no-ticket",
609 //"on-resume-verify-fail",
610 //"partial-write",
611 //"peek-then-read",
612 //"read-with-unfinished-write",
613 "renegotiate-freely",
614 "renegotiate-ignore",
615 "renegotiate-once",
616 //"renew-ticket",
617 "require-any-client-certificate",
618 "retain-only-sha256-client-cert",
619 //"reverify-on-resume",
620 "select-empty-alpn",
621 "send-alert",
622 "server",
623 "server-preference",
624 "set-ocsp-in-callback",
625 "shim-shuts-down",
626 "shim-writes-first",
627 //"tls-unique",
628 "use-custom-verify-callback",
629 "use-early-callback",
630 "use-export-context",
631 "use-exporter-between-reads",
632 "use-ocsp-callback",
633 //"use-old-client-cert-callback",
634 //"use-ticket-callback",
635 "verify-fail",
636 "verify-peer",
637 //"verify-peer-if-no-obc",
638 "write-different-record-sizes",
639 };
640
641 const std::set<std::string> bogo_shim_string_opts = {
642 "advertise-alpn",
643 //"advertise-npn",
644 "cert-file",
645 "cipher",
646 //"delegated-credential",
647 "expect-advertised-alpn",
648 "expect-alpn",
649 "expect-client-ca-list",
650 "expect-late-alpn",
651 "expect-msg-callback",
652 //"expect-next-proto",
653 "expect-peer-cert-file",
654 "expect-server-name",
655 "export-context",
656 "export-label",
657 "handshaker-path",
658 "host-name",
659 "key-file",
660 "psk",
661 "psk-identity",
662 "select-alpn",
663 "select-next-proto",
664 "srtp-profiles",
665 "test-name",
666 "use-client-ca-list",
667 //"send-channel-id",
668 //"write-settings",
669 };
670
671 const std::set<std::string> bogo_shim_base64_opts = {
672 "expect-certificate-types",
673 //"expect-channel-id",
674 "expect-ocsp-response",
675 //"expect-quic-transport-params",
676 //"expect-signed-cert-timestamps",
677 "ocsp-response",
678 //"quic-transport-params",
679 //"signed-cert-timestamps",
680 //"ticket-key", /* we use a different ticket format from Boring */
681 //"token-binding-params",
682 };
683
684 const std::set<std::string> bogo_shim_int_opts {
685 "expect-cipher-aes",
686 "expect-cipher-no-aes",
687 "expect-curve-id",
688 "expect-peer-signature-algorithm",
689 "expect-ticket-age-skew",
690 "expect-token-binding-param",
691 "expect-total-renegotiations",
692 "expect-version",
693 //"export-early-keying-material",
694 "export-keying-material",
695 "initial-timeout-duration-ms",
696 "max-cert-list",
697 //"max-send-fragment",
698 "max-version",
699 "min-version",
700 "mtu",
701 "port",
702 "read-size",
703 "resume-count",
704 "resumption-delay",
705 };
706
707 const std::set<std::string> bogo_shim_int_vec_opts {
708 "curves",
709 "expect-peer-verify-pref",
710 "signing-prefs",
711 "verify-prefs",
712 };
713
714 std::unique_ptr<Shim_Arguments> args(
715 new Shim_Arguments(bogo_shim_flags,
716 bogo_shim_string_opts,
717 bogo_shim_base64_opts,
718 bogo_shim_int_opts,
719 bogo_shim_int_vec_opts));
720
721 // may throw:
722 args->parse_args(argv);
723
724 return args;
725 }
726
727 class Shim_Policy final : public Botan::TLS::Policy
728 {
729 public:
Shim_Policy(const Shim_Arguments & args)730 Shim_Policy(const Shim_Arguments& args) : m_args(args), m_sessions(0) {}
731
incr_session_established()732 void incr_session_established() { m_sessions += 1; }
733
allowed_ciphers() const734 std::vector<std::string> allowed_ciphers() const override
735 {
736 return {
737 "AES-256/OCB(12)",
738 "AES-128/OCB(12)",
739 "ChaCha20Poly1305",
740 "AES-256/GCM",
741 "AES-128/GCM",
742 "AES-256/CCM",
743 "AES-128/CCM",
744 "AES-256/CCM(8)",
745 "AES-128/CCM(8)",
746 "Camellia-256/GCM",
747 "Camellia-128/GCM",
748 "ARIA-256/GCM",
749 "ARIA-128/GCM",
750 "AES-256",
751 "AES-128",
752 "Camellia-256",
753 "Camellia-128",
754 "SEED",
755 "3DES",
756 };
757
758 }
759
allowed_signature_hashes() const760 std::vector<std::string> allowed_signature_hashes() const override
761 {
762 if(m_args.option_used("signing-prefs"))
763 {
764 std::vector<std::string> pref_hash;
765 for(size_t pref : m_args.get_int_vec_opt("signing-prefs"))
766 {
767 const auto scheme = static_cast<Botan::TLS::Signature_Scheme>(pref);
768 if(Botan::TLS::signature_scheme_is_known(scheme) == false)
769 continue;
770 pref_hash.push_back(Botan::TLS::hash_function_of_scheme(scheme));
771 }
772
773 if(m_args.flag_set("server"))
774 pref_hash.push_back("SHA-256");
775 return pref_hash;
776 }
777 else
778 {
779 return { "SHA-512", "SHA-384", "SHA-256", "SHA-1" };
780 }
781 }
782
783 //std::vector<std::string> allowed_macs() const override;
784
allowed_key_exchange_methods() const785 std::vector<std::string> allowed_key_exchange_methods() const override
786 {
787 return {
788 "ECDHE_PSK",
789 "DHE_PSK",
790 "PSK",
791 "CECPQ1",
792 "ECDH",
793 "DH",
794 "RSA",
795 };
796 }
797
allowed_signature_methods() const798 std::vector<std::string> allowed_signature_methods() const override
799 {
800 return {
801 "ECDSA",
802 "RSA",
803 "IMPLICIT",
804 };
805
806 }
807
allowed_signature_schemes() const808 std::vector<Botan::TLS::Signature_Scheme> allowed_signature_schemes() const override
809 {
810 if(m_args.option_used("signing-prefs"))
811 {
812 std::vector<Botan::TLS::Signature_Scheme> schemes;
813 for(size_t pref : m_args.get_int_vec_opt("signing-prefs"))
814 {
815 schemes.push_back(static_cast<Botan::TLS::Signature_Scheme>(pref));
816 }
817
818 // BoGo gets sad if these are not included in our signature_algorithms extension
819 if(!m_args.flag_set("server"))
820 {
821 schemes.push_back(Botan::TLS::Signature_Scheme::RSA_PKCS1_SHA256);
822 schemes.push_back(Botan::TLS::Signature_Scheme::ECDSA_SHA256);
823 }
824
825 return schemes;
826 }
827
828 if(m_args.option_used("verify-prefs"))
829 {
830 std::vector<Botan::TLS::Signature_Scheme> schemes;
831 for(size_t pref : m_args.get_int_vec_opt("verify-prefs"))
832 {
833 schemes.push_back(static_cast<Botan::TLS::Signature_Scheme>(pref));
834 }
835
836 return schemes;
837 }
838
839 return Botan::TLS::Policy::allowed_signature_schemes();
840 }
841
842 //size_t minimum_signature_strength() const override;
843
844 //bool require_cert_revocation_info() const override;
845
key_exchange_groups() const846 std::vector<Botan::TLS::Group_Params> key_exchange_groups() const override
847 {
848 if(m_args.option_used("curves"))
849 {
850 std::vector<Botan::TLS::Group_Params> groups;
851 for(size_t pref : m_args.get_int_vec_opt("curves"))
852 {
853 groups.push_back(static_cast<Botan::TLS::Group_Params>(pref));
854 }
855
856 return groups;
857 }
858
859 return Botan::TLS::Policy::key_exchange_groups();
860 }
861
use_ecc_point_compression() const862 bool use_ecc_point_compression() const override { return false; } // BoGo expects this
863
864 //Botan::TLS::Group_Params choose_key_exchange_group(const std::vector<Botan::TLS::Group_Params>& peer_groups) const override;
865
require_client_certificate_authentication() const866 bool require_client_certificate_authentication() const override
867 {
868 return m_args.flag_set("require-any-client-certificate");
869 }
870
request_client_certificate_authentication() const871 bool request_client_certificate_authentication() const override
872 {
873 return m_args.flag_set("verify-peer") ||
874 m_args.flag_set("fail-cert-callback") ||
875 require_client_certificate_authentication();
876 }
877
allow_insecure_renegotiation() const878 bool allow_insecure_renegotiation() const override
879 {
880 if(m_args.flag_set("expect-no-secure-renegotiation"))
881 return true;
882 else
883 return false;
884 }
885
886 //bool include_time_in_hello_random() const override;
887
allow_client_initiated_renegotiation() const888 bool allow_client_initiated_renegotiation() const override
889 {
890 if(m_args.flag_set("renegotiate-freely"))
891 return true;
892
893 if(m_args.flag_set("renegotiate-once") && m_sessions <= 1)
894 return true;
895
896 return false;
897 }
898
allow_server_initiated_renegotiation() const899 bool allow_server_initiated_renegotiation() const override
900 {
901 return allow_client_initiated_renegotiation(); // same logic
902 }
903
allow_version(Botan::TLS::Protocol_Version version) const904 bool allow_version(Botan::TLS::Protocol_Version version) const
905 {
906 if(m_args.option_used("min-version"))
907 {
908 const uint16_t min_version_16 = static_cast<uint16_t>(m_args.get_int_opt("min-version"));
909 Botan::TLS::Protocol_Version min_version(min_version_16 >> 8, min_version_16 & 0xFF);
910 if(min_version > version)
911 return false;
912 }
913
914 if(m_args.option_used("max-version"))
915 {
916 const uint16_t max_version_16 = static_cast<uint16_t>(m_args.get_int_opt("max-version"));
917 Botan::TLS::Protocol_Version max_version(max_version_16 >> 8, max_version_16 & 0xFF);
918 if(version > max_version)
919 return false;
920 }
921
922 return version.known_version();
923 }
924
allow_tls10() const925 bool allow_tls10() const override
926 {
927 return !m_args.flag_set("dtls") &&
928 !m_args.flag_set("no-tls1") &&
929 allow_version(Botan::TLS::Protocol_Version::TLS_V10);
930 }
931
allow_tls11() const932 bool allow_tls11() const override
933 {
934 return !m_args.flag_set("dtls") && !m_args.flag_set("no-tls11") && allow_version(Botan::TLS::Protocol_Version::TLS_V11);
935 }
936
allow_tls12() const937 bool allow_tls12() const override
938 {
939 return !m_args.flag_set("dtls") && !m_args.flag_set("no-tls12") && allow_version(Botan::TLS::Protocol_Version::TLS_V12);
940 }
941
allow_dtls10() const942 bool allow_dtls10() const override
943 {
944 return m_args.flag_set("dtls") && !m_args.flag_set("no-tls1") && allow_version(Botan::TLS::Protocol_Version::DTLS_V10);
945 }
946
allow_dtls12() const947 bool allow_dtls12() const override
948 {
949 return m_args.flag_set("dtls") && !m_args.flag_set("no-tls12") && allow_version(Botan::TLS::Protocol_Version::DTLS_V12);
950 }
951
952 //Botan::TLS::Group_Params default_dh_group() const override;
953
954 //size_t minimum_dh_group_size() const override;
955
minimum_ecdsa_group_size() const956 size_t minimum_ecdsa_group_size() const override { return 224; }
957
minimum_ecdh_group_size() const958 size_t minimum_ecdh_group_size() const override { return 224; }
959
960 //size_t minimum_rsa_bits() const override;
961
962 //size_t minimum_dsa_group_size() const override;
963
964 //void check_peer_key_acceptable(const Botan::Public_Key& public_key) const override;
965
966 //bool hide_unknown_users() const override;
967
968 //uint32_t session_ticket_lifetime() const override;
969
srtp_profiles() const970 std::vector<uint16_t> srtp_profiles() const override
971 {
972 if(m_args.option_used("srtp-profiles"))
973 {
974 std::string srtp = m_args.get_string_opt("srtp-profiles");
975
976 if(srtp == "SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32")
977 return {1,2};
978 else if(srtp == "SRTP_AES128_CM_SHA1_80")
979 return {1};
980 else
981 shim_exit_with_error("unknown srtp-profiles");
982 }
983 else
984 return {};
985 }
986
only_resume_with_exact_version() const987 bool only_resume_with_exact_version() const override
988 {
989 return false;
990 }
991
send_fallback_scsv(Botan::TLS::Protocol_Version) const992 bool send_fallback_scsv(Botan::TLS::Protocol_Version) const override
993 {
994 return m_args.flag_set("fallback-scsv");
995 }
996
997 //bool server_uses_own_ciphersuite_preferences() const override;
998
999 //bool negotiate_encrypt_then_mac() const override;
1000
support_cert_status_message() const1001 bool support_cert_status_message() const override
1002 {
1003 if(m_args.flag_set("server"))
1004 {
1005 if(!m_args.option_used("ocsp-response"))
1006 return false;
1007 if(m_args.flag_set("decline-ocsp-callback"))
1008 return false;
1009 }
1010 return true;
1011 }
1012
1013 std::vector<uint16_t> ciphersuite_list(Botan::TLS::Protocol_Version version,
1014 bool have_srp) const override;
1015
dtls_default_mtu() const1016 size_t dtls_default_mtu() const override
1017 {
1018 return m_args.get_int_opt_or_else("mtu", 1500);
1019 }
1020
1021 //size_t dtls_initial_timeout() const override;
1022
1023 //size_t dtls_maximum_timeout() const override;
1024
allow_resumption_for_renegotiation() const1025 bool allow_resumption_for_renegotiation() const override
1026 {
1027 return false; // BoGo expects this
1028 }
1029
abort_connection_on_undesired_renegotiation() const1030 bool abort_connection_on_undesired_renegotiation() const override
1031 {
1032 if(m_args.flag_set("renegotiate-ignore"))
1033 return false;
1034 else
1035 return true;
1036 }
1037
maximum_certificate_chain_size() const1038 size_t maximum_certificate_chain_size() const override
1039 {
1040 return m_args.get_int_opt_or_else("max-cert-list", 0);
1041 }
1042
1043 private:
1044 const Shim_Arguments& m_args;
1045 size_t m_sessions;
1046 };
1047
ciphersuite_list(Botan::TLS::Protocol_Version version,bool have_srp) const1048 std::vector<uint16_t> Shim_Policy::ciphersuite_list(Botan::TLS::Protocol_Version version,
1049 bool have_srp) const
1050 {
1051 std::vector<uint16_t> ciphersuite_codes;
1052
1053 const std::string cipher_limit = m_args.get_string_opt_or_else("cipher", "");
1054 if(cipher_limit == "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]")
1055 {
1056 std::vector<std::string> suites = {
1057 "ECDHE_RSA_WITH_AES_128_GCM_SHA256",
1058 "ECDHE_RSA_WITH_AES_256_GCM_SHA384",
1059 "ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
1060 "ECDHE_RSA_WITH_AES_256_CBC_SHA",
1061 "RSA_WITH_AES_256_GCM_SHA384",
1062 "RSA_WITH_AES_256_CBC_SHA",
1063 };
1064
1065 for(auto suite_name : suites)
1066 {
1067 const auto suite = Botan::TLS::Ciphersuite::from_name(suite_name);
1068 if(suite.valid() == false)
1069 shim_exit_with_error("Bad ciphersuite name " + suite_name);
1070 ciphersuite_codes.push_back(suite.ciphersuite_code());
1071 }
1072 }
1073 else
1074 {
1075 // Hack: go in reverse order to avoid preferring 3DES
1076 auto ciphersuites = Botan::TLS::Ciphersuite::all_known_ciphersuites();
1077 for(auto i = ciphersuites.rbegin(); i != ciphersuites.rend(); ++i)
1078 {
1079 const auto suite = *i;
1080 // Can we use it?
1081 if(suite.valid() == false)
1082 continue;
1083
1084 // Are we doing SRP?
1085 if(!have_srp && suite.kex_method() == Botan::TLS::Kex_Algo::SRP_SHA)
1086 continue;
1087
1088 if(cipher_limit != "")
1089 {
1090 if(cipher_limit == "DEFAULT:!AES")
1091 {
1092 const std::string suite_algo = suite.cipher_algo();
1093
1094 if(suite_algo == "AES-128" || suite_algo == "AES-256" ||
1095 suite_algo == "AES-128/GCM" || suite_algo == "AES-256/GCM" ||
1096 suite_algo == "AES-128/CCM" || suite_algo == "AES-256/CCM" ||
1097 suite_algo == "AES-128/CCM(8)" || suite_algo == "AES-256/CCM(8)" ||
1098 suite_algo == "AES-128/OCB(12)" || suite_algo == "AES-256/OCB(12)")
1099 {
1100 continue;
1101 }
1102 }
1103 else
1104 {
1105 shim_exit_with_error("Unknown cipher " + cipher_limit);
1106 }
1107 }
1108
1109 if(!version.supports_aead_modes())
1110 {
1111 // Are we doing AEAD in a non-AEAD version?
1112 if(suite.mac_algo() == "AEAD")
1113 continue;
1114
1115 // Older (v1.0/v1.1) versions also do not support any hash but SHA-1
1116 if(suite.mac_algo() != "SHA-1")
1117 continue;
1118 }
1119
1120 ciphersuite_codes.push_back(suite.ciphersuite_code());
1121 }
1122 }
1123
1124 return ciphersuite_codes;
1125 }
1126
1127 class Shim_Credentials final : public Botan::Credentials_Manager
1128 {
1129 public:
Shim_Credentials(const Shim_Arguments & args)1130 Shim_Credentials(const Shim_Arguments& args) : m_args(args)
1131 {
1132 m_psk_identity = m_args.get_string_opt_or_else("psk-identity", "");
1133
1134 const std::string psk_str = m_args.get_string_opt_or_else("psk", "");
1135 m_psk = Botan::SymmetricKey(reinterpret_cast<const uint8_t*>(psk_str.data()), psk_str.size());
1136
1137 if(m_args.option_used("key-file") && m_args.option_used("cert-file"))
1138 {
1139 Botan::DataSource_Stream key_stream(m_args.get_string_opt("key-file"));
1140 m_key = Botan::PKCS8::load_key(key_stream);
1141
1142 Botan::DataSource_Stream cert_stream(m_args.get_string_opt("cert-file"));
1143
1144 while(!cert_stream.end_of_data())
1145 {
1146 try
1147 {
1148 m_cert_chain.push_back(Botan::X509_Certificate(cert_stream));
1149 }
1150 catch(...) {}
1151 }
1152 }
1153 }
1154
psk_identity(const std::string &,const std::string &,const std::string &)1155 std::string psk_identity(const std::string& /*type*/,
1156 const std::string& /*context*/,
1157 const std::string& /*identity_hint*/) override
1158 {
1159 return m_psk_identity;
1160 }
1161
psk_identity_hint(const std::string &,const std::string &)1162 std::string psk_identity_hint(const std::string& /*type*/,
1163 const std::string& /*context*/) override
1164 {
1165 return m_psk_identity;
1166 }
1167
psk(const std::string & type,const std::string & context,const std::string & identity)1168 Botan::SymmetricKey psk(const std::string& type,
1169 const std::string& context,
1170 const std::string& identity) override
1171 {
1172 if(type == "tls-server" && context == "session-ticket")
1173 {
1174 if(!m_args.flag_set("no-ticket") && !m_args.flag_set("on-resume-no-ticket"))
1175 return Botan::SymmetricKey("ABCDEF0123456789");
1176 }
1177
1178 if(type == "tls-server" && context == "dtls-cookie-secret")
1179 {
1180 return Botan::SymmetricKey("F00FB00FD00F100F700F");
1181 }
1182
1183 if(identity != m_psk_identity)
1184 throw Shim_Exception("Unexpected PSK identity");
1185 return m_psk;
1186 }
1187
cert_chain(const std::vector<std::string> & cert_key_types,const std::string &,const std::string &)1188 std::vector<Botan::X509_Certificate> cert_chain(
1189 const std::vector<std::string>& cert_key_types,
1190 const std::string& /*type*/,
1191 const std::string& /*context*/) override
1192 {
1193 if(m_args.flag_set("fail-cert-callback"))
1194 throw std::runtime_error("Simulating cert verify callback failure");
1195
1196 if(m_key != nullptr && m_cert_chain.size() > 0)
1197 {
1198 for(std::string t : cert_key_types)
1199 {
1200 if(t == m_key->algo_name())
1201 return m_cert_chain;
1202 }
1203 }
1204
1205 return {};
1206 }
1207
private_key_for(const Botan::X509_Certificate &,const std::string &,const std::string &)1208 Botan::Private_Key* private_key_for(const Botan::X509_Certificate& /*cert*/,
1209 const std::string& /*type*/,
1210 const std::string& /*context*/) override
1211 {
1212 // assumes cert == m_cert
1213 return m_key.get();
1214 }
1215
1216 private:
1217 const Shim_Arguments& m_args;
1218 Botan::SymmetricKey m_psk;
1219 std::string m_psk_identity;
1220 std::unique_ptr<Botan::Private_Key> m_key;
1221 std::vector<Botan::X509_Certificate> m_cert_chain;
1222 };
1223
1224 class Shim_Callbacks final : public Botan::TLS::Callbacks
1225 {
1226 public:
Shim_Callbacks(const Shim_Arguments & args,Shim_Socket & socket,Shim_Policy & policy)1227 Shim_Callbacks(const Shim_Arguments& args, Shim_Socket& socket, Shim_Policy& policy) :
1228 m_channel(nullptr),
1229 m_args(args),
1230 m_policy(policy),
1231 m_socket(socket),
1232 m_is_datagram(args.flag_set("dtls")),
1233 m_warning_alerts(0),
1234 m_empty_records(0),
1235 m_sessions_established(0),
1236 m_got_close(false)
1237 {}
1238
sessions_established() const1239 size_t sessions_established() const { return m_sessions_established; }
1240
set_channel(Botan::TLS::Channel * channel)1241 void set_channel(Botan::TLS::Channel* channel)
1242 {
1243 m_channel = channel;
1244 }
1245
saw_close_notify() const1246 bool saw_close_notify() const { return m_got_close; }
1247
tls_emit_data(const uint8_t data[],size_t size)1248 void tls_emit_data(const uint8_t data[], size_t size) override
1249 {
1250 shim_log("sending record of len " + std::to_string(size));
1251
1252 if(m_is_datagram)
1253 {
1254 std::vector<uint8_t> packet(size + 5);
1255
1256 packet[0] = 'P';
1257 for(size_t i = 0; i != 4; ++i)
1258 packet[i+1] = static_cast<uint8_t>((size >> (24-8*i)) & 0xFF);
1259 std::memcpy(packet.data() + 5, data, size);
1260
1261 m_socket.write(packet.data(), packet.size());
1262 }
1263 else
1264 {
1265 m_socket.write(data, size);
1266 }
1267 }
1268
tls_provide_cert_status(const std::vector<Botan::X509_Certificate> &,const Botan::TLS::Certificate_Status_Request &)1269 std::vector<uint8_t> tls_provide_cert_status(const std::vector<Botan::X509_Certificate>&,
1270 const Botan::TLS::Certificate_Status_Request&) override
1271 {
1272 if(m_args.flag_set("use-ocsp-callback") && m_args.flag_set("fail-ocsp-callback"))
1273 throw std::runtime_error("Simulating failure from OCSP response callback");
1274
1275 if(m_args.flag_set("decline-ocsp-callback"))
1276 return {};
1277
1278 if(m_args.option_used("ocsp-response"))
1279 {
1280 return m_args.get_b64_opt("ocsp-response");
1281 }
1282
1283 return {};
1284 }
1285
tls_record_received(uint64_t,const uint8_t data[],size_t size)1286 void tls_record_received(uint64_t /*seq_no*/, const uint8_t data[], size_t size) override
1287 {
1288 if(size == 0)
1289 {
1290 m_empty_records += 1;
1291 if(m_empty_records > 32)
1292 shim_exit_with_error(":TOO_MANY_EMPTY_FRAGMENTS:");
1293 }
1294 else
1295 {
1296 m_empty_records = 0;
1297 }
1298
1299 shim_log("Reflecting application_data len " + std::to_string(size));
1300
1301 std::vector<uint8_t> buf(data, data + size);
1302 for(size_t i = 0; i != size; ++i)
1303 buf[i] ^= 0xFF;
1304
1305 m_channel->send(buf);
1306 }
1307
tls_verify_message(const Botan::Public_Key & key,const std::string & emsa,Botan::Signature_Format format,const std::vector<uint8_t> & msg,const std::vector<uint8_t> & sig)1308 bool tls_verify_message(const Botan::Public_Key& key,
1309 const std::string& emsa,
1310 Botan::Signature_Format format,
1311 const std::vector<uint8_t>& msg,
1312 const std::vector<uint8_t>& sig) override
1313 {
1314 if(m_args.option_used("expect-peer-signature-algorithm"))
1315 {
1316 const auto scheme = static_cast<Botan::TLS::Signature_Scheme>(m_args.get_int_opt("expect-peer-signature-algorithm"));
1317 if(scheme != Botan::TLS::Signature_Scheme::NONE)
1318 {
1319 const std::string exp_emsa = Botan::TLS::padding_string_for_scheme(scheme);
1320 if(emsa != exp_emsa)
1321 shim_exit_with_error("Unexpected signature scheme got " + emsa + " expected " + exp_emsa);
1322 }
1323 }
1324 return Botan::TLS::Callbacks::tls_verify_message(key, emsa, format, msg, sig);
1325 }
1326
tls_verify_cert_chain(const std::vector<Botan::X509_Certificate> &,const std::vector<std::shared_ptr<const Botan::OCSP::Response>> &,const std::vector<Botan::Certificate_Store * > &,Botan::Usage_Type,const std::string &,const Botan::TLS::Policy &)1327 void tls_verify_cert_chain(const std::vector<Botan::X509_Certificate>& /*cert_chain*/,
1328 const std::vector<std::shared_ptr<const Botan::OCSP::Response>>& /*ocsp_responses*/,
1329 const std::vector<Botan::Certificate_Store*>& /*trusted_roots*/,
1330 Botan::Usage_Type /*usage*/,
1331 const std::string& /*hostname*/,
1332 const Botan::TLS::Policy& /*policy*/) override
1333 {
1334 if(m_args.flag_set("enable-ocsp-stapling") &&
1335 m_args.flag_set("use-ocsp-callback") &&
1336 m_args.flag_set("fail-ocsp-callback"))
1337 {
1338 throw Botan::TLS::TLS_Exception(Botan::TLS::Alert::BAD_CERTIFICATE_STATUS_RESPONSE,
1339 "Simulated OCSP callback failure");
1340 }
1341
1342 if(m_args.flag_set("verify-peer") && m_args.flag_set("verify-fail"))
1343 {
1344 throw Botan::TLS::TLS_Exception(Botan::TLS::Alert::BAD_CERTIFICATE,
1345 "Test requires rejecting cert");
1346 }
1347 }
1348
tls_server_choose_app_protocol(const std::vector<std::string> & client_protos)1349 std::string tls_server_choose_app_protocol(const std::vector<std::string>& client_protos) override
1350 {
1351 if(client_protos.empty())
1352 return ""; // shouldn't happen?
1353
1354 if(m_args.flag_set("decline-alpn"))
1355 return "";
1356
1357 if(m_args.option_used("expect-advertised-alpn"))
1358 {
1359 const std::vector<std::string> expected = m_args.get_alpn_string_vec_opt("expect-advertised-alpn");
1360
1361 if(client_protos != expected)
1362 shim_exit_with_error("Bad ALPN from client");
1363 }
1364
1365 if(m_args.option_used("select-alpn"))
1366 return m_args.get_string_opt("select-alpn");
1367
1368 return client_protos[0]; // if not configured just pick something
1369 }
1370
tls_alert(Botan::TLS::Alert alert)1371 void tls_alert(Botan::TLS::Alert alert) override
1372 {
1373 if(alert.is_fatal())
1374 shim_log("Got a fatal alert " + alert.type_string());
1375 else
1376 shim_log("Got a warning alert " + alert.type_string());
1377
1378 if(alert.type() == Botan::TLS::Alert::RECORD_OVERFLOW)
1379 {
1380 shim_exit_with_error(":TLSV1_ALERT_RECORD_OVERFLOW:");
1381 }
1382
1383 if(alert.type() == Botan::TLS::Alert::DECOMPRESSION_FAILURE)
1384 {
1385 shim_exit_with_error(":SSLV3_ALERT_DECOMPRESSION_FAILURE:");
1386 }
1387
1388 if(!alert.is_fatal())
1389 {
1390 m_warning_alerts++;
1391 if(m_warning_alerts > 5)
1392 shim_exit_with_error(":TOO_MANY_WARNING_ALERTS:");
1393 }
1394
1395 if(alert.type() == Botan::TLS::Alert::CLOSE_NOTIFY)
1396 {
1397 if(m_got_close == false && !m_args.flag_set("shim-shuts-down"))
1398 {
1399 shim_log("Sending return close notify");
1400 m_channel->send_alert(alert);
1401 }
1402 m_got_close = true;
1403 }
1404 else if(alert.is_fatal())
1405 {
1406 shim_exit_with_error("Unexpected fatal alert " + alert.type_string());
1407 }
1408 }
1409
tls_session_established(const Botan::TLS::Session & session)1410 bool tls_session_established(const Botan::TLS::Session& session) override
1411 {
1412 shim_log("Session established: " + Botan::hex_encode(session.session_id()) +
1413 " version " + session.version().to_string() +
1414 " cipher " + session.ciphersuite().to_string() +
1415 " EMS " + std::to_string(session.supports_extended_master_secret()));
1416 // probably need tests here?
1417
1418 m_policy.incr_session_established();
1419 m_sessions_established++;
1420
1421 if(m_args.flag_set("expect-no-session-id"))
1422 {
1423 // BoGo expects that ticket issuance implies no stateful session...
1424 if(!m_args.flag_set("server") && session.session_id().size() > 0)
1425 shim_exit_with_error("Unexpectedly got a session ID");
1426 }
1427 else if(m_args.flag_set("expect-session-id"))
1428 {
1429 if(session.session_id().empty())
1430 shim_exit_with_error("Unexpectedly got no session ID");
1431 }
1432
1433 if(m_args.option_used("expect-version"))
1434 {
1435 if(session.version().version_code() != m_args.get_int_opt("expect-version"))
1436 shim_exit_with_error("Unexpected version");
1437 }
1438
1439 if(m_args.flag_set("expect-secure-renegotiation"))
1440 {
1441 if(m_channel->secure_renegotiation_supported() == false)
1442 shim_exit_with_error("Expected secure renegotiation");
1443 }
1444 else if(m_args.flag_set("expect-no-secure-renegotiation"))
1445 {
1446 if(m_channel->secure_renegotiation_supported() == true)
1447 shim_exit_with_error("Expected no secure renegotation");
1448 }
1449
1450 if(m_args.flag_set("expect-extended-master-secret"))
1451 {
1452 if(session.supports_extended_master_secret() == false)
1453 shim_exit_with_error("Expected extended maseter secret");
1454 }
1455
1456 return true;
1457 }
1458
tls_session_activated()1459 void tls_session_activated() override
1460 {
1461 if(m_args.flag_set("send-alert"))
1462 {
1463 m_channel->send_fatal_alert(Botan::TLS::Alert::DECOMPRESSION_FAILURE);
1464 return;
1465 }
1466
1467 if(size_t length = m_args.get_int_opt_or_else("export-keying-material", 0))
1468 {
1469 const std::string label = m_args.get_string_opt("export-label");
1470 const std::string context = m_args.get_string_opt("export-context");
1471 const auto exported = m_channel->key_material_export(label, context, length);
1472 shim_log("Sending " + std::to_string(length) + " bytes of key material");
1473 m_channel->send(exported.bits_of());
1474 }
1475
1476 const std::string alpn = m_channel->application_protocol();
1477
1478 if(m_args.option_used("expect-alpn"))
1479 {
1480 if(alpn != m_args.get_string_opt("expect-alpn"))
1481 shim_exit_with_error("Got unexpected ALPN");
1482 }
1483
1484 if(alpn == "baz" && !m_args.flag_set("allow-unknown-alpn-protos"))
1485 {
1486 throw Botan::TLS::TLS_Exception(Botan::TLS::Alert::ILLEGAL_PARAMETER,
1487 "Unexpected ALPN protocol");
1488 }
1489
1490 if(m_args.flag_set("shim-shuts-down"))
1491 {
1492 shim_log("Shim shutting down");
1493 m_channel->close();
1494 }
1495
1496 if(m_args.flag_set("write-different-record-sizes"))
1497 {
1498 static const size_t record_sizes[] = {
1499 0, 1, 255, 256, 257, 16383, 16384, 16385, 32767, 32768, 32769
1500 };
1501
1502 std::vector<uint8_t> buf(32769, 0x42);
1503
1504 for(size_t sz : record_sizes)
1505 {
1506 m_channel->send(buf.data(), sz);
1507 }
1508
1509 m_channel->close();
1510 }
1511 }
1512
1513 private:
1514 Botan::TLS::Channel* m_channel;
1515 const Shim_Arguments& m_args;
1516 Shim_Policy& m_policy;
1517 Shim_Socket& m_socket;
1518 const bool m_is_datagram;
1519 size_t m_warning_alerts;
1520 size_t m_empty_records;
1521 size_t m_sessions_established;
1522 bool m_got_close;
1523 };
1524
1525 }
1526
main(int,char * argv[])1527 int main(int /*argc*/, char* argv[])
1528 {
1529 try
1530 {
1531 std::unique_ptr<Shim_Arguments> args = parse_options(argv);
1532
1533 if(args->flag_set("is-handshaker-supported"))
1534 {
1535 return shim_output("No\n");
1536 }
1537
1538 const uint16_t port = static_cast<uint16_t>(args->get_int_opt("port"));
1539 const size_t resume_count = args->get_int_opt_or_else("resume-count", 0);
1540 const bool is_server = args->flag_set("server");
1541 const bool is_datagram = args->flag_set("dtls");
1542
1543 const size_t buf_size = args->get_int_opt_or_else("read-size", 18*1024);
1544
1545 Botan::ChaCha_RNG rng(Botan::secure_vector<uint8_t>(64));
1546 Botan::TLS::Session_Manager_In_Memory session_manager(rng, 1024);
1547 Shim_Credentials creds(*args);
1548
1549 for(size_t i = 0; i != resume_count+1; ++i)
1550 {
1551 Shim_Socket socket("localhost", port);
1552
1553 shim_log("Connection " + std::to_string(i+1) + "/" + std::to_string(resume_count+1));
1554
1555 Shim_Policy policy(*args);
1556 Shim_Callbacks callbacks(*args, socket, policy);
1557
1558 std::unique_ptr<Botan::TLS::Channel> chan;
1559
1560 if(is_server)
1561 {
1562 chan.reset(new Botan::TLS::Server(callbacks, session_manager, creds, policy, rng, is_datagram));
1563 }
1564 else
1565 {
1566 Botan::TLS::Protocol_Version offer_version = policy.latest_supported_version(is_datagram);
1567 shim_log("Offering " + offer_version.to_string());
1568
1569 std::string host_name = args->get_string_opt_or_else("host-name", "localhost");
1570 if(args->test_name().find("UnsolicitedServerNameAck-TLS1") == 0)
1571 host_name = ""; // avoid sending SNI for this test
1572
1573 Botan::TLS::Server_Information server_info(host_name, port);
1574 const std::vector<std::string> next_protocols = args->get_alpn_string_vec_opt("advertise-alpn");
1575 chan.reset(new Botan::TLS::Client(callbacks, session_manager, creds, policy, rng,
1576 server_info, offer_version, next_protocols));
1577 }
1578
1579 callbacks.set_channel(chan.get());
1580
1581 std::vector<uint8_t> buf(buf_size);
1582
1583 for(;;)
1584 {
1585 if(is_datagram)
1586 {
1587 uint8_t opcode;
1588 size_t got = socket.read(&opcode, 1);
1589 if(got == 0)
1590 {
1591 shim_log("EOF on socket");
1592 break;
1593 }
1594
1595 if(opcode == 'P')
1596 {
1597 uint8_t len_bytes[4];
1598 socket.read_exactly(len_bytes, sizeof(len_bytes));
1599
1600 size_t packet_len = Botan::load_be<uint32_t>(len_bytes, 0);
1601
1602 if(buf.size() < packet_len)
1603 buf.resize(packet_len);
1604 socket.read_exactly(buf.data(), packet_len);
1605
1606 chan->received_data(buf.data(), packet_len);
1607 }
1608 else if(opcode == 'T')
1609 {
1610 uint8_t timeout_ack = 't';
1611
1612 uint8_t timeout_bytes[8];
1613 socket.read_exactly(timeout_bytes, sizeof(timeout_bytes));
1614
1615 const uint64_t nsec = Botan::load_be<uint64_t>(timeout_bytes, 0);
1616
1617 shim_log("Timeout nsec " + std::to_string(nsec));
1618
1619 // FIXME handle this!
1620
1621 socket.write(&timeout_ack, 1); // ack it anyway
1622 }
1623 else
1624 shim_exit_with_error("Unknown opcode " + std::to_string(opcode));
1625 }
1626 else
1627 {
1628 size_t got = socket.read(buf.data(), buf.size());
1629 if(got == 0)
1630 {
1631 shim_log("EOF on socket");
1632 break;
1633 }
1634
1635 shim_log("Got packet of " + std::to_string(got));
1636
1637 if(args->flag_set("use-exporter-between-reads") && chan->is_active())
1638 {
1639 chan->key_material_export("some label", "some context", 42);
1640 }
1641 const size_t needed = chan->received_data(buf.data(), got);
1642
1643 if(needed)
1644 shim_log("Short read still need " + std::to_string(needed));
1645 }
1646 }
1647
1648 if(args->flag_set("check-close-notify"))
1649 {
1650 if(!callbacks.saw_close_notify())
1651 throw Shim_Exception("Unexpected SSL_shutdown result: -1 != 1");
1652 }
1653
1654 if(args->option_used("expect-total-renegotiations"))
1655 {
1656 const size_t exp = args->get_int_opt("expect-total-renegotiations");
1657
1658 if(exp != callbacks.sessions_established() - 1)
1659 throw Shim_Exception("Unexpected number of renegotiations: saw " +
1660 std::to_string(callbacks.sessions_established() - 1) +
1661 " exp " + std::to_string(exp));
1662 }
1663 shim_log("End of resume loop");
1664 }
1665
1666 }
1667 catch(Shim_Exception& e)
1668 {
1669 shim_exit_with_error(e.what(), e.rc());
1670 }
1671 catch(std::exception& e)
1672 {
1673 shim_exit_with_error(map_to_bogo_error(e.what()));
1674 }
1675 catch(...)
1676 {
1677 shim_exit_with_error("Unknown exception", 3);
1678 }
1679 return 0;
1680 }
1681