OpenVPN 3 Core Library
Loading...
Searching...
No Matches
test_proto.cpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
17#include "test_common.hpp"
18
19#include <iostream>
20#include <string>
21#include <sstream>
22#include <deque>
23#include <algorithm>
24#include <cstring>
25#include <limits>
26#include <thread>
27
28#include <gmock/gmock.h>
32
33
34#define OPENVPN_DEBUG
35
36#if !defined(USE_TLS_AUTH) && !defined(USE_TLS_CRYPT)
37// #define USE_TLS_AUTH
38// #define USE_TLS_CRYPT
39#define USE_TLS_CRYPT_V2
40#endif
41
42// Data limits for Blowfish and other 64-bit block-size ciphers
43#ifndef BF
44#define BF 0
45#endif
46#if BF == 1
47#define PROTO_CIPHER "BF-CBC"
48#define TLS_VER_MIN TLSVersion::UNDEF
49#define HANDSHAKE_WINDOW 60
50#define BECOME_PRIMARY_CLIENT 5
51#define BECOME_PRIMARY_SERVER 5
52#define TLS_TIMEOUT_CLIENT 1000
53#define TLS_TIMEOUT_SERVER 1000
54#define FEEDBACK 0
55#elif BF == 2
56#define PROTO_CIPHER "BF-CBC"
57#define TLS_VER_MIN TLSVersion::UNDEF
58#define HANDSHAKE_WINDOW 10
59#define BECOME_PRIMARY_CLIENT 10
60#define BECOME_PRIMARY_SERVER 10
61#define TLS_TIMEOUT_CLIENT 2000
62#define TLS_TIMEOUT_SERVER 1000
63#define FEEDBACK 0
64#elif BF == 3
65#define PROTO_CIPHER "BF-CBC"
66#define TLS_VER_MIN TLSVersion::UNDEF
67#define HANDSHAKE_WINDOW 60
68#define BECOME_PRIMARY_CLIENT 60
69#define BECOME_PRIMARY_SERVER 10
70#define TLS_TIMEOUT_CLIENT 2000
71#define TLS_TIMEOUT_SERVER 1000
72#define FEEDBACK 0
73#elif BF != 0
74#error unknown BF value
75#endif
76
77// TLS timeout
78#ifndef TLS_TIMEOUT_CLIENT
79#define TLS_TIMEOUT_CLIENT 2000
80#endif
81#ifndef TLS_TIMEOUT_SERVER
82#define TLS_TIMEOUT_SERVER 2000
83#endif
84
85// NoisyWire
86#ifndef NOERR
87#define SIMULATE_OOO
88#define SIMULATE_DROPPED
89#define SIMULATE_CORRUPTED
90#endif
91
92// how many virtual seconds between SSL renegotiations
93#ifdef PROTO_RENEG
94#define RENEG PROTO_RENEG
95#else
96#define RENEG 900
97#endif
98
99// feedback
100#ifndef FEEDBACK
101#define FEEDBACK 1
102#else
103#define FEEDBACK 0
104#endif
105
106// number of iterations
107#ifdef PROTO_ITER
108#define ITER PROTO_ITER
109#else
110#define ITER 1000000
111#endif
112
113// number of high-level session iterations
114#ifdef PROTO_SITER
115#define SITER PROTO_SITER
116#else
117#define SITER 1
118#endif
119
120// number of retries for failed test
121#ifndef N_RETRIES
122#define N_RETRIES 2
123#endif
124
125// potentially, the above manifest constants can be converted to variables and modified
126// within the different TEST() functions that replace main() in the original file
127
128// abort if we reach this limit
129// #define DROUGHT_LIMIT 100000
130
131#if !defined(PROTO_VERBOSE) && !defined(QUIET) && ITER <= 10000
132#define VERBOSE
133#endif
134
135#define STRINGIZE1(x) #x
136#define STRINGIZE(x) STRINGIZE1(x)
137
138// setup cipher
139#ifndef PROTO_CIPHER
140#ifdef PROTOv2
141#define PROTO_CIPHER "AES-256-GCM"
142#define TLS_VER_MIN TLSVersion::Type::V1_2
143#else
144#define PROTO_CIPHER "AES-128-CBC"
145#define TLS_VER_MIN TLSVersion::Type::UNDEF
146#endif
147#endif
148
149// setup digest
150#ifndef PROTO_DIGEST
151#define PROTO_DIGEST "SHA1"
152#endif
153
154// setup compressor
155#ifdef PROTOv2
156#ifdef HAVE_LZ4
157#define COMP_METH CompressContext::LZ4v2
158#else
159#define COMP_METH CompressContext::COMP_STUBv2
160#endif
161#else
162#define COMP_METH CompressContext::LZO_STUB
163#endif
164
165#include <filesystem>
166
171#include <openvpn/time/time.hpp>
174#include <openvpn/ssl/proto.hpp>
176
178
179#if !(defined(USE_OPENSSL) || defined(USE_MBEDTLS))
180#error Must define one or more of USE_OPENSSL, USE_MBEDTLS.
181#endif
182
183#if defined(USE_OPENSSL) && defined(USE_MBEDTLS)
184#undef USE_OPENSSL
185#define USE_OPENSSL_SERVER
186#elif !defined(USE_OPENSSL) && defined(USE_MBEDTLS)
187#define USE_MBEDTLS_SERVER
188#elif defined(USE_OPENSSL) && !defined(USE_MBEDTLS)
189#define USE_OPENSSL_SERVER
190#else
191#error no server setup
192#endif
193
194#if defined(USE_OPENSSL) || defined(USE_OPENSSL_SERVER)
198#endif
199
200#if defined(USE_MBEDTLS) || defined(USE_MBEDTLS_SERVER)
204#include <mbedtls/debug.h>
205#endif
206
208
209using namespace openvpn;
210
211// server Crypto/SSL/Rand implementation
212#ifdef USE_MBEDTLS_SERVER
213typedef MbedTLSCryptoAPI ServerCryptoAPI;
214typedef MbedTLSContext ServerSSLAPI;
215typedef MbedTLSRandom ServerRandomAPI;
216#elif defined(USE_OPENSSL_SERVER)
217using ServerCryptoAPI = OpenSSLCryptoAPI;
218using ServerSSLAPI = OpenSSLContext;
219using ServerRandomAPI = OpenSSLRandom;
220#else
221#error No server SSL implementation defined
222#endif
223
224// client SSL implementation can be OpenSSL or MbedTLS
225#ifdef USE_MBEDTLS
226typedef MbedTLSCryptoAPI ClientCryptoAPI;
227typedef MbedTLSContext ClientSSLAPI;
228typedef MbedTLSRandom ClientRandomAPI;
229#elif defined(USE_OPENSSL)
230using ClientCryptoAPI = OpenSSLCryptoAPI;
231using ClientSSLAPI = OpenSSLContext;
232using ClientRandomAPI = OpenSSLRandom;
233#else
234#error No client SSL implementation defined
235#endif
236
237const char message[] = "Message _->_ 0000000000 It was a bright cold day in April, and the clocks\n"
238 "were striking thirteen. Winston Smith, his chin nuzzled\n"
239 "into his breast in an effort to escape the vile wind,\n"
240 "slipped quickly through the glass doors of Victory\n"
241 "Mansions, though not quickly enough to prevent a\n"
242 "swirl of gritty dust from entering along with him.\n"
243#ifdef LARGE_MESSAGE
244 "It was a bright cold day in April, and the clocks\n"
245 "were striking thirteen. Winston Smith, his chin nuzzled\n"
246 "into his breast in an effort to escape the vile wind,\n"
247 "slipped quickly through the glass doors of Victory\n"
248 "Mansions, though not quickly enough to prevent a\n"
249 "swirl of gritty dust from entering along with him.\n"
250 "It was a bright cold day in April, and the clocks\n"
251 "were striking thirteen. Winston Smith, his chin nuzzled\n"
252 "into his breast in an effort to escape the vile wind,\n"
253 "slipped quickly through the glass doors of Victory\n"
254 "Mansions, though not quickly enough to prevent a\n"
255 "swirl of gritty dust from entering along with him.\n"
256 "It was a bright cold day in April, and the clocks\n"
257 "were striking thirteen. Winston Smith, his chin nuzzled\n"
258 "into his breast in an effort to escape the vile wind,\n"
259 "slipped quickly through the glass doors of Victory\n"
260 "Mansions, though not quickly enough to prevent a\n"
261 "swirl of gritty dust from entering along with him.\n"
262 "It was a bright cold day in April, and the clocks\n"
263 "were striking thirteen. Winston Smith, his chin nuzzled\n"
264 "into his breast in an effort to escape the vile wind,\n"
265 "slipped quickly through the glass doors of Victory\n"
266 "Mansions, though not quickly enough to prevent a\n"
267 "swirl of gritty dust from entering along with him.\n"
268#endif
269 ;
270
271// A "Drought" measures the maximum period of time between
272// any two successive events. Used to measure worst-case
273// packet loss.
275{
276 public:
277 OPENVPN_SIMPLE_EXCEPTION(drought_limit_exceeded);
278
279 DroughtMeasure(const std::string &name_arg, TimePtr now_arg)
280 : now(now_arg), name(name_arg)
281 {
282 }
283
284 void event()
285 {
286 if (last_event.defined())
287 {
288 Time::Duration since_last = *now - last_event;
289 if (since_last > drought)
290 {
291 drought = since_last;
292#if defined(VERBOSE) || defined(DROUGHT_LIMIT)
293 {
294 const unsigned int r = drought.raw();
295#if defined(VERBOSE)
296 std::cout << "*** Drought " << name << " has reached " << r << "\n";
297#endif
298#ifdef DROUGHT_LIMIT
299 if (r > DROUGHT_LIMIT)
300 throw drought_limit_exceeded();
301#endif
302 }
303#endif
304 }
305 }
306 last_event = *now;
307 }
308
309 Time::Duration operator()() const
310 {
311 return drought;
312 }
313
314 private:
317 Time::Duration drought;
318 std::string name;
319};
320
321// test the OpenVPN protocol implementation in ProtoContext
323{
324 /* Callback methods that are not used */
325 void active(bool primary) override
326 {
327 }
328
329 bool supports_epoch_data() override
330 {
331 return true;
332 }
333
334 public:
335 OPENVPN_EXCEPTION(session_invalidated);
336
338 const SessionStats::Ptr &stats)
339 : proto_context(this, config, stats),
340 control_drought("control", config->now),
341 data_drought("data", config->now),
343 {
344 // zero progress value
345 std::memset(progress_, 0, 11);
346 }
347
348 void reset()
349 {
350 net_out.clear();
351 wkc_pkt_sizes_.clear();
355 }
356
357 void initial_app_send(const char *msg)
358 {
360 const size_t msglen = std::strlen(msg) + 1;
361 BufferAllocated app_buf((unsigned char *)msg, msglen, BufAllocFlags::NO_FLAGS);
362 copy_progress(app_buf);
363 control_send(std::move(app_buf));
364 proto_context.flush(true);
365 }
366
367 void app_send_templ_init(const char *msg)
368 {
370 const size_t msglen = std::strlen(msg) + 1;
371 templ = BufferAllocatedRc::Create((unsigned char *)msg, msglen, BufAllocFlags::NO_FLAGS);
372 proto_context.flush(true);
373 }
374
376 {
377#if !FEEDBACK
378 if (bool(iteration++ & 1) == is_server())
379 {
380 modmsg(templ);
381 BufferAllocated app_buf(*templ);
382 control_send(std::move(app_buf));
383 flush(true);
385 }
386#endif
387 }
388
390 {
392 {
394 return true;
395 }
396 return false;
397 }
398
399 void control_send(BufferPtr &&app_bp)
400 {
401 app_bytes_ += app_bp->size();
402 proto_context.control_send(std::move(app_bp));
403 }
404
406 {
407 app_bytes_ += app_buf.size();
408 proto_context.control_send(std::move(app_buf));
409 }
410
412 {
415 bp->write((unsigned char *)str, std::strlen(str));
416 data_encrypt(*bp);
417 return bp;
418 }
419
421 {
423 }
424
426 {
427 proto_context.data_decrypt(type, in_out);
428 if (!in_out.empty())
429 {
430 data_bytes_ += in_out.size();
432 }
433 }
434
435 size_t net_bytes() const
436 {
437 return net_bytes_;
438 }
439 size_t app_bytes() const
440 {
441 return app_bytes_;
442 }
443 size_t data_bytes() const
444 {
445 return data_bytes_;
446 }
447 size_t n_control_recv() const
448 {
449 return n_control_recv_;
450 }
451 size_t n_control_send() const
452 {
453 return n_control_send_;
454 }
455
456 const char *progress() const
457 {
458 return progress_;
459 }
460
461 void finalize()
462 {
465 }
466
468 {
470 throw session_invalidated(Error::name(proto_context.invalidation_reason()));
471 }
472
474 {
475 disable_xmit_ = true;
476 }
477
479
480 std::deque<BufferPtr> net_out;
481
482 // sizes of the CONTROL_WKC_V1 packets (the tls-crypt-v2 WKc riders)
483 // emitted via control_net_send(). Only these are recorded, so the
484 // vector stays tiny even across the long feedback tests.
485 std::vector<size_t> wkc_pkt_sizes_;
486
487 // largest control channel packet emitted via control_net_send()
489
490 size_t max_ctrl_pkt_size() const
491 {
492 return max_ctrl_pkt_size_;
493 }
494
495 // Verify every emitted CONTROL_WKC_V1 packet fits within max_size,
496 // and that at least one such packet was emitted.
497 bool verify_wkc_packets_fit(size_t max_size) const
498 {
499 if (wkc_pkt_sizes_.empty())
500 {
501 std::cerr << "no CONTROL_WKC_V1 packet was emitted by the client\n";
502 return false;
503 }
504 for (const size_t size : wkc_pkt_sizes_)
505 {
506 if (size > max_size)
507 {
508 std::cerr << "CONTROL_WKC_V1 packet too large: " << size
509 << " > " << max_size << '\n';
510 return false;
511 }
512 }
513 return true;
514 }
515
518
519 private:
520 void control_net_send(const Buffer &net_buf) override
521 {
522 if (disable_xmit_)
523 return;
524 net_bytes_ += net_buf.size();
525 max_ctrl_pkt_size_ = std::max(max_ctrl_pkt_size_, net_buf.size());
526 if (net_buf.size()
528 wkc_pkt_sizes_.push_back(net_buf.size());
530 }
531
532 void control_recv(BufferPtr &&app_bp) override
533 {
535 work.swap(app_bp);
536 if (work->size() >= 23)
537 std::memcpy(progress_, work->data() + 13, 10);
538
539#ifdef VERBOSE
540 {
541 const ssize_t trunc = 64;
542 const std::string show((char *)work->data(), trunc);
543 std::cout << now().raw() << " " << mode().str() << " " << show << "\n";
544 }
545#endif
546#if FEEDBACK
547 modmsg(work);
548 control_send(std::move(work));
549#endif
552 }
553
555 {
556 if (progress_[0]) // make sure progress was initialized
557 std::memcpy(buf.data() + 13, progress_, 10);
558 }
559
560 void modmsg(BufferPtr &buf)
561 {
562 char *msg = (char *)buf->data();
564 {
565 msg[8] = 'S';
566 msg[11] = 'C';
567 }
568 else
569 {
570 msg[8] = 'C';
571 msg[11] = 'S';
572 }
573
574 // increment embedded number
575 for (int i = 22; i >= 13; i--)
576 {
577 if (msg[i] != '9')
578 {
579 msg[i]++;
580 break;
581 }
582 msg[i] = '0';
583 }
584 }
585
587 size_t app_bytes_ = 0;
588 size_t net_bytes_ = 0;
589 size_t data_bytes_ = 0;
590 size_t n_control_send_ = 0;
591 size_t n_control_recv_ = 0;
593#if !FEEDBACK
594 size_t iteration = 0;
595#endif
596 char progress_[11];
597 bool disable_xmit_ = false;
598};
599
601{
603
604 public:
606 const SessionStats::Ptr &stats)
607 : TestProto(config, stats)
608 {
609 }
610
611 private:
612 void client_auth(Buffer &buf) override
613 {
614 const std::string username("foo");
615 const std::string password("bar");
616 ProtoContext::write_auth_string(username, buf);
617 ProtoContext::write_auth_string(password, buf);
618 }
619};
620
622{
623
624 public:
625 void start()
626 {
628 }
629
631
632
634 const SessionStats::Ptr &stats)
635 : TestProto(config, stats)
636 {
637 }
638
639 private:
640 void server_auth(const std::string &username,
641 const SafeString &password,
642 const std::string &peer_info,
643 const AuthCert::Ptr &auth_cert) override
644 {
645#ifdef VERBOSE
646 std::cout << "**** AUTHENTICATE " << username << '/' << password << " PEER INFO:\n";
647 std::cout << peer_info;
648#endif
649 if (username != "foo" || password != "bar")
650 throw auth_failed();
651 }
652};
653
654// Simulate a noisy transmission channel where packets can be dropped,
655// reordered, or corrupted.
657{
658 public:
659 NoisyWire(const std::string &title_arg,
660 TimePtr now_arg,
661 RandomAPI &rand_arg,
662 const unsigned int reorder_prob_arg,
663 const unsigned int drop_prob_arg,
664 const unsigned int corrupt_prob_arg)
665 : title(title_arg),
666#ifdef VERBOSE
667 now(now_arg),
668#endif
669 random(rand_arg),
670 reorder_prob(reorder_prob_arg),
671 drop_prob(drop_prob_arg),
672 corrupt_prob(corrupt_prob_arg)
673 {
674 }
675
676 template <typename T1, typename T2>
677 void xfer(T1 &a, T2 &b)
678 {
679 // check for errors
680 a.check_invalidated();
681 b.check_invalidated();
682
683 // need to retransmit?
684 if (a.do_housekeeping())
685 {
686#ifdef VERBOSE
687 std::cout << now->raw() << " " << title << " Housekeeping\n";
688#endif
689 }
690
691 // queue a control channel packet
692 a.app_send_templ();
693
694 // queue a data channel packet
695 if (a.proto_context.data_channel_ready())
696 {
697 BufferPtr bp = a.data_encrypt_string("Waiting for godot A... Waiting for godot B... Waiting for godot C... Waiting for godot D... Waiting for godot E... Waiting for godot F... Waiting for godot G... Waiting for godot H... Waiting for godot I... Waiting for godot J...");
698 wire.push_back(bp);
699 }
700
701 // transfer network packets from A -> wire
702 while (!a.net_out.empty())
703 {
704 BufferPtr bp = a.net_out.front();
705#ifdef VERBOSE
706 std::cout << now->raw() << " " << title << " " << a.dump_packet(*bp) << "\n";
707#endif
708 a.net_out.pop_front();
709 wire.push_back(bp);
710 }
711
712 // transfer network packets from wire -> B
713 while (true)
714 {
715 BufferPtr bp = recv();
716 if (!bp)
717 break;
718 typename ProtoContext::PacketType pt = b.proto_context.packet_type(*bp);
719 if (pt.is_control())
720 {
721#ifdef VERBOSE
722 if (!b.control_net_validate(pt, *bp)) // not strictly necessary since control_net_recv will also validate
723 std::cout << now->raw() << " " << title << " CONTROL PACKET VALIDATION FAILED\n";
724#endif
725 b.proto_context.control_net_recv(pt, std::move(bp));
726 }
727 else if (pt.is_data())
728 {
729 try
730 {
731 b.data_decrypt(pt, *bp);
732#ifdef VERBOSE
733 if (bp->size())
734 {
735 const std::string show((char *)bp->data(), std::min(bp->size(), size_t(40)));
736 std::cout << now->raw() << " " << title << " DATA CHANNEL DECRYPT: " << show << "\n";
737 }
738#endif
739 }
740 catch ([[maybe_unused]] const std::exception &e)
741 {
742#ifdef VERBOSE
743 std::cout << now->raw() << " " << title << " Exception on data channel decrypt: " << e.what() << "\n";
744#endif
745 }
746 }
747 else
748 {
749#ifdef VERBOSE
750 std::cout << now->raw() << " " << title << " KEY_STATE_ERROR\n";
751#endif
752 b.proto_context.stat().error(Error::KEY_STATE_ERROR);
753 }
754
755#ifdef SIMULATE_UDP_AMPLIFY_ATTACK
756 if (b.proto_context.is_state_client_wait_reset_ack())
757 {
758 b.disable_xmit();
759#ifdef VERBOSE
760 std::cout << now->raw() << " " << title << " SIMULATE_UDP_AMPLIFY_ATTACK disable client xmit\n";
761#endif
762 }
763#endif
764 }
765 b.proto_context.flush(true);
766 }
767
768 private:
770 {
771#ifdef SIMULATE_OOO
772 // simulate packets being received out of order
773 if (wire.size() >= 2 && !rand(reorder_prob))
774 {
775 const size_t i = random.randrange(wire.size() - 1) + 1;
776#ifdef VERBOSE
777 std::cout << now->raw() << " " << title << " Simulating packet reordering " << i << " -> 0\n";
778#endif
779 std::swap(wire[0], wire[i]);
780 }
781#endif
782
783 if (!wire.empty())
784 {
785 BufferPtr bp = wire.front();
786 wire.pop_front();
787
788#ifdef VERBOSE
789 std::cout << now->raw() << " " << title << " Received packet, size=" << bp->size() << "\n";
790#endif
791
792#ifdef SIMULATE_DROPPED
793 // simulate dropped packet
794 if (!rand(drop_prob))
795 {
796#ifdef VERBOSE
797 std::cout << now->raw() << " " << title << " Simulating a dropped packet\n";
798#endif
799 return BufferPtr();
800 }
801#endif
802
803#ifdef SIMULATE_CORRUPTED
804 // simulate corrupted packet
805 if (!bp->empty() && !rand(corrupt_prob))
806 {
807#ifdef VERBOSE
808 std::cout << now->raw() << " " << title << " Simulating a corrupted packet\n";
809#endif
810 const size_t pos = random.randrange(bp->size());
811 const unsigned char value = random.randrange(std::numeric_limits<unsigned char>::max());
812 (*bp)[pos] = value;
813 }
814#endif
815 return bp;
816 }
817
818 return BufferPtr();
819 }
820
821 unsigned int rand(const unsigned int prob)
822 {
823 if (prob)
824 return random.randrange(prob);
825 return 1;
826 }
827
828 std::string title;
829#ifdef VERBOSE
830 TimePtr now;
831#endif
833 unsigned int reorder_prob;
834 unsigned int drop_prob;
835 unsigned int corrupt_prob;
836 std::deque<BufferPtr> wire;
837};
838
839class MySessionStats : public SessionStats
840{
841 public:
843
845 {
846 std::memset(errors, 0, sizeof(errors));
847 }
848
849 void error(const size_t err_type, const std::string *text = nullptr) override
850 {
851 if (err_type < Error::N_ERRORS)
852 ++errors[err_type];
853 }
854
856 {
857 if (type < Error::N_ERRORS)
858 return errors[type];
859 return 0;
860 }
861
862 void show_error_counts() const
863 {
864 for (size_t i = 0; i < Error::N_ERRORS; ++i)
865 {
866 count_t c = errors[i];
867 if (c)
868 std::cerr << Error::name(i) << " : " << c << '\n';
869 }
870 }
871
872 private:
874};
875
880static auto create_client_ssl_config(Frame::Ptr frame, ClientRandomAPI::Ptr rng, bool tls_version_mismatch = false)
881{
882 const std::string client_crt = read_text(TEST_KEYCERT_DIR "client.crt");
883 const std::string client_key = read_text(TEST_KEYCERT_DIR "client.key");
884 const std::string ca_crt = read_text(TEST_KEYCERT_DIR "ca.crt");
885
886 // client config
887 ClientSSLAPI::Config::Ptr cc(new ClientSSLAPI::Config());
888 cc->set_mode(Mode(Mode::CLIENT));
889 cc->set_frame(frame);
890 cc->set_rng(rng);
891 cc->load_ca(ca_crt, true);
892 cc->load_cert(client_crt);
893 cc->load_private_key(client_key);
894 if (tls_version_mismatch)
895 cc->set_tls_version_max(TLSVersion::Type::V1_2);
896 else
897 cc->set_tls_version_min(TLS_VER_MIN);
898#ifdef VERBOSE
899 cc->set_debug_level(1);
900#endif
901 return cc;
902}
903
904static auto create_client_proto_context(ClientSSLAPI::Config::Ptr cc,
905 Frame::Ptr frame,
906 ClientRandomAPI::Ptr rng,
907 MySessionStats::Ptr cli_stats,
908 Time &time,
909 const std::string &tls_crypt_v2_key_fn = "",
910 bool tls_auth_only = false,
911 bool use_dynamic_tls_crypt = false,
912 const std::string &tls_crypt_v2_dir = TEST_KEYCERT_DIR)
913{
914 const std::string tls_auth_key = read_text(TEST_KEYCERT_DIR "tls-auth.key");
915 const std::string tls_crypt_v2_client_key = tls_crypt_v2_key_fn.empty()
916 ? read_text(TEST_KEYCERT_DIR "tls-crypt-v2-client.key")
917 : read_text(tls_crypt_v2_dir + tls_crypt_v2_key_fn);
918
919 // client ProtoContext config
920 using ClientProtoContext = ProtoContext;
921 ClientProtoContext::ProtoConfig::Ptr cp(new ClientProtoContext::ProtoConfig);
922 cp->ssl_factory = cc->new_factory();
923 CryptoAlgs::allow_default_dc_algs<ClientCryptoAPI>(cp->ssl_factory->libctx(), false, false);
924 cp->dc.set_factory(new CryptoDCSelect<ClientCryptoAPI>(cp->ssl_factory->libctx(), frame, cli_stats, rng));
925 cp->tlsprf_factory.reset(new CryptoTLSPRFFactory<ClientCryptoAPI>());
926 cp->frame = std::move(frame);
927 cp->now = &time;
928 cp->rng = rng;
929 cp->prng = rng;
930 cp->protocol = Protocol(Protocol::UDPv4);
931 cp->layer = Layer(Layer::OSI_LAYER_3);
932#ifdef PROTOv2
933 cp->enable_op32 = true;
934 cp->remote_peer_id = 100;
935#endif
936 cp->comp_ctx = CompressContext(COMP_METH, false);
937 cp->dc.set_cipher(CryptoAlgs::lookup(PROTO_CIPHER));
938 cp->dc.set_digest(CryptoAlgs::lookup(PROTO_DIGEST));
939
940#ifdef USE_TLS_AUTH
941 cp->tls_auth_factory.reset(new CryptoOvpnHMACFactory<ClientCryptoAPI>());
942 cp->tls_auth_key.parse(tls_auth_key);
943 cp->set_tls_auth_digest(CryptoAlgs::lookup(PROTO_DIGEST));
944 cp->key_direction = 0;
945#endif
946#ifdef USE_TLS_CRYPT
947 cp->tls_crypt_factory.reset(new CryptoTLSCryptFactory<ClientCryptoAPI>());
948 cp->tls_crypt_key.parse(tls_auth_key);
949 cp->set_tls_crypt_algs();
950 cp->tls_crypt_ = ProtoContext::ProtoConfig::TLSCrypt::V1;
951#endif
952#ifdef USE_TLS_CRYPT_V2
953 if (tls_auth_only)
954 {
955 // A plain tls-auth client, for testing a server that holds a tls-crypt-v2 key as
956 // well: such a session must stay in TLS_AUTH mode from end to end.
957 cp->tls_auth_factory.reset(new CryptoOvpnHMACFactory<ClientCryptoAPI>());
958 cp->tls_auth_key.parse(tls_auth_key);
959 cp->set_tls_auth_digest(CryptoAlgs::lookup(PROTO_DIGEST));
960 cp->key_direction = 0;
961 }
962 else
963 {
964 cp->tls_crypt_factory.reset(new CryptoTLSCryptFactory<ClientCryptoAPI>());
965 cp->set_tls_crypt_algs();
966 {
967 TLSCryptV2ClientKey tls_crypt_v2_key(cp->tls_crypt_context);
968 tls_crypt_v2_key.parse(tls_crypt_v2_client_key);
969 tls_crypt_v2_key.extract_key(cp->tls_crypt_key);
970 tls_crypt_v2_key.extract_wkc(cp->wkc);
971 }
972 cp->tls_crypt_ = ProtoContext::ProtoConfig::TLSCrypt::V2;
973 }
974 if (use_dynamic_tls_crypt)
975 cp->enable_dynamic_tls_crypt();
976#endif
977#ifdef HANDSHAKE_WINDOW
978 cp->handshake_window = Time::Duration::seconds(HANDSHAKE_WINDOW);
979#elif SITER > 1
980 cp->handshake_window = Time::Duration::seconds(30);
981#else
982 cp->handshake_window = Time::Duration::seconds(18); // will cause a small number of handshake failures
983#endif
984#ifdef BECOME_PRIMARY_CLIENT
985 cp->become_primary = Time::Duration::seconds(BECOME_PRIMARY_CLIENT);
986#else
987 cp->become_primary = cp->handshake_window;
988#endif
989 cp->tls_timeout = Time::Duration::milliseconds(TLS_TIMEOUT_CLIENT);
990#ifdef CLIENT_NO_RENEG
991 cp->renegotiate = Time::Duration::infinite();
992#else
993 cp->renegotiate = Time::Duration::seconds(RENEG);
994#endif
995 cp->expire = cp->renegotiate + cp->renegotiate;
996 cp->keepalive_ping = Time::Duration::seconds(5);
997 cp->keepalive_timeout = Time::Duration::seconds(60);
998 cp->keepalive_timeout_early = cp->keepalive_timeout;
999
1000#ifdef VERBOSE
1001 std::cout << "CLIENT OPTIONS: " << cp->options_string() << "\n";
1002 std::cout << "CLIENT PEER INFO:\n";
1003 std::cout << cp->peer_info_string();
1004#endif
1005 return cp;
1006}
1007
1008// Configures one specific test run */
1010{
1011 bool use_tls_ekm = false;
1013 const std::string &tls_crypt_v2_key_fn = "";
1016 const std::string &tls_crypt_v2_dir = TEST_KEYCERT_DIR;
1020 bool force_resend_wkc = false;
1022 size_t control_payload = 378;
1023 size_t mssfix_ctrl = 0;
1024};
1025
1026// execute the unit test in one thread
1027int test(const struct proto_test &t)
1028{
1029 try
1030 {
1031 // frame
1032 Frame::Ptr frame(new Frame(Frame::Context(128, 378, 128, 0, 16, BufAllocFlags::NO_FLAGS)));
1033 // Shrink only the control-channel ciphertext context, mirroring what
1034 // mssfix-ctrl does in production (see frame_init()): the cleartext
1035 // staging buffers (e.g. WRITE_SSL_CLEARTEXT, used for the auth
1036 // message) keep their normal size.
1038
1039 // RNG
1040 ClientRandomAPI::Ptr prng_cli(new ClientRandomAPI());
1041 ServerRandomAPI::Ptr prng_serv(new ServerRandomAPI());
1042 MTRand rng_noncrypto;
1043
1044 // init simulated time
1045 Time time;
1046 const Time::Duration time_step = Time::Duration::binary_ms(100);
1047
1048 // config files
1049 const std::string ca_crt = read_text(TEST_KEYCERT_DIR "ca.crt");
1050 const std::string server_crt = read_text(TEST_KEYCERT_DIR "server.crt");
1051 const std::string server_key = read_text(TEST_KEYCERT_DIR "server.key");
1052 const std::string dh_pem = read_text(TEST_KEYCERT_DIR "dh.pem");
1053 const std::string tls_auth_key = read_text(TEST_KEYCERT_DIR "tls-auth.key");
1054 const std::string tls_crypt_v2_server_key = t.tls_crypt_v2_key_fn.empty()
1055 ? read_text(TEST_KEYCERT_DIR "tls-crypt-v2-server.key")
1056 : "";
1057
1058 // client config
1059 ClientSSLAPI::Config::Ptr cc = create_client_ssl_config(frame, prng_cli, t.tls_version_mismatch);
1060 MySessionStats::Ptr cli_stats(new MySessionStats);
1061
1062 auto cp = create_client_proto_context(std::move(cc), frame, prng_cli, cli_stats, time, t.tls_crypt_v2_key_fn, t.client_tls_auth_only, t.use_dynamic_tls_crypt, t.tls_crypt_v2_dir);
1063 if (t.use_tls_ekm)
1064 cp->dc.set_key_derivation(CryptoAlgs::KeyDerivation::TLS_EKM);
1065 if (t.mssfix_ctrl)
1066 cp->mssfix_ctrl = t.mssfix_ctrl;
1067
1068 // server config
1069 MySessionStats::Ptr serv_stats(new MySessionStats);
1070
1071 ServerSSLAPI::Config::Ptr sc(new ClientSSLAPI::Config());
1072 sc->set_mode(Mode(Mode::SERVER));
1073 sc->set_frame(frame);
1074 sc->set_rng(prng_serv);
1075 sc->load_ca(ca_crt, true);
1076 sc->load_cert(server_crt);
1077 sc->load_private_key(server_key);
1078 sc->load_dh(dh_pem);
1079 sc->set_tls_version_min(t.tls_version_mismatch ? TLSVersion::Type::V1_3 : TLS_VER_MIN);
1080#ifdef VERBOSE
1081 sc->set_debug_level(1);
1082#endif
1083
1084 // server ProtoContext config
1085 using ServerProtoContext = ProtoContext;
1086 ServerProtoContext::ProtoConfig::Ptr sp(new ServerProtoContext::ProtoConfig);
1087 sp->ssl_factory = sc->new_factory();
1088 sp->dc.set_factory(new CryptoDCSelect<ServerCryptoAPI>(sp->ssl_factory->libctx(), frame, serv_stats, prng_serv));
1089 sp->tlsprf_factory.reset(new CryptoTLSPRFFactory<ServerCryptoAPI>());
1090 sp->frame = frame;
1091 sp->now = &time;
1092 sp->rng = prng_serv;
1093 sp->prng = prng_serv;
1094 sp->protocol = Protocol(Protocol::UDPv4);
1095 sp->layer = Layer(Layer::OSI_LAYER_3);
1096#ifdef PROTOv2
1097 sp->enable_op32 = true;
1098 sp->remote_peer_id = 101;
1099#endif
1100 sp->comp_ctx = CompressContext(COMP_METH, false);
1101 sp->dc.set_cipher(CryptoAlgs::lookup(PROTO_CIPHER));
1102 sp->dc.set_digest(CryptoAlgs::lookup(PROTO_DIGEST));
1103 if (t.use_tls_ekm)
1104 sp->dc.set_key_derivation(CryptoAlgs::KeyDerivation::TLS_EKM);
1105#ifdef USE_TLS_AUTH
1106 sp->tls_auth_factory.reset(new CryptoOvpnHMACFactory<ServerCryptoAPI>());
1107 sp->tls_auth_key.parse(tls_auth_key);
1108 sp->set_tls_auth_digest(CryptoAlgs::lookup(PROTO_DIGEST));
1109 sp->key_direction = 1;
1110#endif
1111#ifdef USE_TLS_CRYPT
1112 sp->tls_crypt_factory.reset(new CryptoTLSCryptFactory<ClientCryptoAPI>());
1113 sp->tls_crypt_key.parse(tls_auth_key);
1114 sp->set_tls_crypt_algs();
1115 cp->tls_crypt_ = ProtoContext::ProtoConfig::TLSCrypt::V1;
1116#endif
1117#ifdef USE_TLS_CRYPT_V2
1118 sp->tls_crypt_factory.reset(new CryptoTLSCryptFactory<ClientCryptoAPI>());
1119
1120 if (t.tls_crypt_v2_key_fn.empty())
1121 {
1122 TLSCryptV2ServerKey tls_crypt_v2_key;
1123 tls_crypt_v2_key.parse(tls_crypt_v2_server_key);
1124 tls_crypt_v2_key.extract_key(sp->tls_crypt_key);
1125 }
1126
1127 sp->set_tls_crypt_algs();
1128 sp->tls_crypt_metadata_factory.reset(new CryptoTLSCryptMetadataFactory());
1129 sp->tls_crypt_ = ProtoContext::ProtoConfig::TLSCrypt::V2;
1130 sp->tls_crypt_v2_serverkey_id = !t.tls_crypt_v2_key_fn.empty();
1131 sp->tls_crypt_v2_serverkey_dir = t.tls_crypt_v2_dir;
1132
1134 sp->enable_dynamic_tls_crypt();
1135
1137 {
1138 sp->tls_auth_factory.reset(new CryptoOvpnHMACFactory<ServerCryptoAPI>());
1139 sp->tls_auth_key.parse(tls_auth_key);
1140 sp->set_tls_auth_digest(CryptoAlgs::lookup(PROTO_DIGEST));
1141 sp->key_direction = 1;
1142 }
1143#endif
1144#ifdef HANDSHAKE_WINDOW
1145 sp->handshake_window = Time::Duration::seconds(HANDSHAKE_WINDOW);
1146#elif SITER > 1
1147 sp->handshake_window = Time::Duration::seconds(30);
1148#else
1149 sp->handshake_window = Time::Duration::seconds(17) + Time::Duration::binary_ms(512);
1150#endif
1151#ifdef BECOME_PRIMARY_SERVER
1152 sp->become_primary = Time::Duration::seconds(BECOME_PRIMARY_SERVER);
1153#else
1154 sp->become_primary = sp->handshake_window;
1155#endif
1156 sp->tls_timeout = Time::Duration::milliseconds(TLS_TIMEOUT_SERVER);
1157#ifdef SERVER_NO_RENEG
1158 sp->renegotiate = Time::Duration::infinite();
1159#else
1160 // NOTE: if we don't add sp->handshake_window, both client and server reneg-sec (RENEG)
1161 // will be equal and will therefore occasionally collide. Such collisions can sometimes
1162 // produce this OpenSSL error:
1163 // OpenSSLContext::SSL::read_cleartext: BIO_read failed, cap=400 status=-1: error:140E0197:SSL routines:SSL_shutdown:shutdown while in init
1164 // The issue was introduced by this patch in OpenSSL:
1165 // https://github.com/openssl/openssl/commit/64193c8218540499984cd63cda41f3cd491f3f59
1166 sp->renegotiate = Time::Duration::seconds(RENEG) + sp->handshake_window;
1167#endif
1168 sp->expire = sp->renegotiate + sp->renegotiate;
1169 sp->keepalive_ping = Time::Duration::seconds(5);
1170 sp->keepalive_timeout = Time::Duration::seconds(60);
1171 sp->keepalive_timeout_early = Time::Duration::seconds(10);
1172
1173#ifdef VERBOSE
1174 std::cout << "SERVER OPTIONS: " << sp->options_string() << "\n";
1175 std::cout << "SERVER PEER INFO:\n";
1176 std::cout << sp->peer_info_string();
1177#endif
1178
1179 TestProtoClient cli_proto(cp, cli_stats);
1180 TestProtoServer serv_proto(sp, serv_stats);
1181
1182 for (int i = 0; i < SITER; ++i)
1183 {
1184#ifdef VERBOSE
1185 std::cout << "***** SITER " << i << "\n";
1186#endif
1187 cli_proto.reset();
1188 serv_proto.reset();
1189
1190 NoisyWire client_to_server("Client -> Server", &time, rng_noncrypto, 8, 16, 32); // last value: 32
1191 NoisyWire server_to_client("Server -> Client", &time, rng_noncrypto, 8, 16, 32); // last value: 32
1192
1193 int j = -1;
1194 try
1195 {
1196#if FEEDBACK
1197 // start feedback loop
1198 cli_proto.initial_app_send(message);
1199 serv_proto.start();
1200#else
1201 cli_proto.app_send_templ_init(message);
1202 serv_proto.app_send_templ_init(message);
1203#endif
1204
1205 if (t.spoof_hard_reset_v3)
1206 {
1207 // What an off-path attacker can put on the wire: a
1208 // CONTROL_HARD_RESET_CLIENT_V3 opcode over garbage.
1209 // packet_type() weighs the opcode and the key id and nothing
1210 // else, so this reaches decapsulate() and asks a tls-auth
1211 // server to make itself a tls-crypt-v2 one. Nothing in it
1212 // authenticates, so the handshake below must still run its
1213 // course.
1216 for (size_t k = 0; k < 64; ++k)
1217 bp->push_back(static_cast<unsigned char>(k));
1218
1219 const ProtoContext::PacketType pt = serv_proto.proto_context.packet_type(*bp);
1220 if (!pt.is_control())
1221 return 1;
1222 serv_proto.proto_context.control_net_recv(pt, std::move(bp));
1223 }
1224
1225 if (t.force_resend_wkc)
1226 {
1227 // Pretend the server asked the client to resend the
1228 // tls-crypt-v2 WKc on the first control packet, so the
1229 // client's ClientHello is emitted as CONTROL_WKC_V1 with
1230 // the WKc appended -- as is every retransmission of that
1231 // packet, which the noisy wire below produces plenty of.
1232 // The server has to take the WKc off each of them, so the
1233 // handshake completes like any other.
1234 cli_proto.proto_context.force_resend_wkc();
1235 }
1236
1237 // message loop
1238 for (j = 0; j < ITER; ++j)
1239 {
1240 client_to_server.xfer(cli_proto, serv_proto);
1241 server_to_client.xfer(serv_proto, cli_proto);
1242 time += time_step;
1243 }
1244
1245 if (t.force_resend_wkc)
1246 {
1247 // Frame control context above is Context(128, control_payload, ...).
1248 // Even the WKc-bearing packet must stay within headroom +
1249 // payload; without the reservation fix it overflows by
1250 // ~wkc.size() bytes.
1251 if (!cli_proto.verify_wkc_packets_fit(128 + t.control_payload))
1252 return 1;
1253 // when a wire cap is configured, every emitted control
1254 // packet must stay within it after all wrappings
1255 if (t.mssfix_ctrl && cli_proto.max_ctrl_pkt_size() > t.mssfix_ctrl)
1256 {
1257 std::cerr << "control packet exceeded mssfix_ctrl: "
1258 << cli_proto.max_ctrl_pkt_size()
1259 << " > " << t.mssfix_ctrl << '\n';
1260 return 1;
1261 }
1262 }
1263 }
1264 catch (const std::exception &e)
1265 {
1266 std::cerr << "Exception[" << i << '/' << j << "]: " << e.what() << '\n';
1267 return 1;
1268 }
1269 }
1270
1271 cli_proto.finalize();
1272 serv_proto.finalize();
1273
1274 const size_t ab = cli_proto.app_bytes() + serv_proto.app_bytes();
1275 const size_t nb = cli_proto.net_bytes() + serv_proto.net_bytes();
1276 const size_t db = cli_proto.data_bytes() + serv_proto.data_bytes();
1277
1278 std::cerr << "*** app bytes=" << ab
1279 << " net_bytes=" << nb
1280 << " data_bytes=" << db
1281 << " prog=" << cli_proto.progress() << '/' << serv_proto.progress()
1282#if !FEEDBACK
1283 << " CTRL=" << cli_proto.n_control_recv() << '/' << cli_proto.n_control_send() << '/' << serv_proto.n_control_recv() << '/' << serv_proto.n_control_send()
1284#endif
1285 << " D=" << cli_proto.control_drought().raw() << '/' << cli_proto.data_drought().raw() << '/' << serv_proto.control_drought().raw() << '/' << serv_proto.data_drought().raw()
1286 << " N=" << cli_proto.proto_context.negotiations() << '/' << serv_proto.proto_context.negotiations()
1287 << " SH=" << cli_proto.proto_context.slowest_handshake().raw() << '/' << serv_proto.proto_context.slowest_handshake().raw()
1288 << " HE=" << cli_stats->get_error_count(Error::HANDSHAKE_TIMEOUT) << '/' << serv_stats->get_error_count(Error::HANDSHAKE_TIMEOUT)
1289 << '\n';
1290
1292 {
1293 // The rekey is the first thing to use the derived key, and it is a control
1294 // channel handshake like any other: if the two ends derived different keys,
1295 // nothing either sends can authenticate and neither gets past its first one.
1296 if (cli_proto.proto_context.negotiations() < 2 || serv_proto.proto_context.negotiations() < 2)
1297 {
1298 std::cerr << "dynamic tls-crypt: no rekey completed\n";
1299 return 1;
1300 }
1301 }
1302
1303#ifdef STATS
1304 std::cerr << "-------- CLIENT STATS --------\n";
1305 cli_stats->show_error_counts();
1306 std::cerr << "-------- SERVER STATS --------\n";
1307 serv_stats->show_error_counts();
1308#endif
1309#ifdef OPENVPN_MAX_DATALIMIT_BYTES
1310 std::cerr << "------------------------------\n";
1311 std::cerr << "MAX_DATALIMIT_BYTES=" << DataLimit::max_bytes() << "\n";
1312#endif
1313 }
1314 catch (const std::exception &e)
1315 {
1316 std::cerr << "Exception: " << e.what() << '\n';
1317 return 1;
1318 }
1319 return 0;
1320}
1321
1322int test_retry(const int n_retries, const struct proto_test &test_config)
1323{
1324 int ret = 1;
1325 for (int i = 0; i < n_retries; ++i)
1326 {
1327 ret = test(test_config);
1328 if (!ret)
1329 return 0;
1330 std::cout << "Retry " << (i + 1) << '/' << n_retries << '\n';
1331 }
1332 std::cout << "Failed\n";
1333 return ret;
1334}
1335
1336class ProtoUnitTest : public testing::Test
1337{
1338 // Sets up the test fixture.
1339 void SetUp() override
1340 {
1341#ifdef USE_MBEDTLS
1342 mbedtls_debug_set_threshold(1);
1343#endif
1344
1346
1347#ifdef PROTO_VERBOSE
1349#else
1351#endif
1352 }
1353
1354 // Tears down the test fixture.
1355 void TearDown() override
1356 {
1357#ifdef USE_MBEDTLS
1358 mbedtls_debug_set_threshold(4);
1359#endif
1362 }
1363};
1364
1365TEST_F(ProtoUnitTest, BaseSingleThreadTlsEkm)
1366{
1367 if (!openvpn::SSLLib::SSLAPI::support_key_material_export())
1368 GTEST_SKIP_("our mbed TLS implementation does not support TLS EKM");
1369
1370 int ret = 0;
1371
1372 ret = test_retry(N_RETRIES, {.use_tls_ekm = true});
1373
1374 EXPECT_EQ(ret, 0);
1375}
1376
1377TEST_F(ProtoUnitTest, BaseSingleThreadNoTlsEkm)
1378{
1379 int ret = 0;
1380
1381 ret = test_retry(N_RETRIES, {.use_tls_ekm = false});
1382
1383 EXPECT_EQ(ret, 0);
1384}
1385
1386// Our mbedtls currently has a no-op set_tls_version_max() implementation,
1387// so we can't set mismatched client and server TLS versions.
1388// For now, just test this for OPENSSL which is full-featured.
1389#ifdef USE_OPENSSL
1390TEST_F(ProtoUnitTest, BaseSingleThreadTlsVersionMismatch)
1391{
1392 int ret = test({.tls_version_mismatch = true});
1393 EXPECT_NE(ret, 0);
1394}
1395#endif
1396
1397#ifdef USE_TLS_CRYPT_V2
1398TEST_F(ProtoUnitTest, BaseSingleThreadTlsCryptV2WithEmbeddedServerkey)
1399{
1400 int ret = test_retry(N_RETRIES, {.tls_crypt_v2_key_fn = "tls-crypt-v2-client-with-serverkey.key"});
1401 EXPECT_EQ(ret, 0);
1402}
1403
1404TEST_F(ProtoUnitTest, BaseSingleThreadTlsCryptV2WithMissingEmbeddedServerkey)
1405{
1406 int ret = test({.tls_crypt_v2_key_fn = "tls-crypt-v2-client-with-missing-serverkey.key"});
1407 EXPECT_NE(ret, 0);
1408}
1409
1410TEST_F(ProtoUnitTest, BaseSingleThreadTlsCryptV2WithTlsAuthAlsoActive)
1411{
1412 int ret = test_retry(N_RETRIES, {.tls_crypt_v2_key_fn = "tls-crypt-v2-client-with-serverkey.key", .use_tls_auth_with_tls_crypt_v2 = true});
1413 EXPECT_EQ(ret, 0);
1414}
1415
1424static void write_generated_tls_crypt_v2_keys(const std::string &dir,
1425 const std::string &client_fn,
1426 const std::uint32_t k_id,
1427 const std::string &metadata)
1428{
1429 ServerRandomAPI::Ptr rng(new ServerRandomAPI());
1430
1431 TLSCryptV2ServerKey server_key;
1432 server_key.generate(*rng);
1433
1434 // <NN>/<KID8>.key, NN being the first two digits of the uppercase hex K_id:
1435 // the path unwrap_tls_crypt_wkc() composes from the K_id on the wire.
1436 const std::string k_id_hex = render_hex_number(k_id, true);
1437 const std::string key_dir = dir + k_id_hex.substr(0, 2);
1438 std::filesystem::create_directories(key_dir);
1439 write_string(key_dir + "/" + k_id_hex + ".key", server_key.render());
1440
1442 TLSCryptContext::Ptr tls_crypt_context = tls_crypt_factory->new_obj(nullptr,
1443 CryptoAlgs::lookup("SHA256"),
1444 CryptoAlgs::lookup("AES-256-CTR"));
1445 TLSCryptV2ClientKey client_key(tls_crypt_context);
1446 client_key.generate(*rng, server_key, metadata, 0x00, k_id);
1447 write_string(dir + client_fn, client_key.render());
1448}
1449
1450// Everything the generator feeds (the SCT's client cohort, the KVM test's client) is only
1451// as good as its wire format, so mint a pair here and run the full handshake against it,
1452// through the same server-key-ID path the checked-in fixtures exercise. Fails if the tag
1453// stops covering the length prefix or K_id, if the CTR IV drifts from the tag, or if K_id
1454// lands anywhere but between the ciphertext and the trailing length.
1455TEST_F(ProtoUnitTest, BaseSingleThreadTlsCryptV2WithGeneratedKeys)
1456{
1457 const std::string dir = getTempDirPath("ovpn3-tls-crypt-v2-generated/");
1458 std::filesystem::remove_all(dir);
1459 std::filesystem::create_directories(dir);
1460
1461 write_generated_tls_crypt_v2_keys(dir, "client.key", 0x1A2B3C4D, "v=1,type=connector,tenant=SCT-01");
1462
1463 int ret = test_retry(N_RETRIES, {.tls_crypt_v2_key_fn = "client.key", .tls_crypt_v2_dir = dir});
1464 EXPECT_EQ(ret, 0);
1465
1466 std::filesystem::remove_all(dir);
1467}
1468
1469// The same, against a server that also holds a tls-auth key: the mixed shape the SCT
1470// runs, where a tls-crypt-v2 client converts a session the server started in TLS_AUTH mode.
1471TEST_F(ProtoUnitTest, BaseSingleThreadTlsCryptV2WithGeneratedKeysAndTlsAuth)
1472{
1473 const std::string dir = getTempDirPath("ovpn3-tls-crypt-v2-generated-ta/");
1474 std::filesystem::remove_all(dir);
1475 std::filesystem::create_directories(dir);
1476
1477 write_generated_tls_crypt_v2_keys(dir, "client.key", 0x00FF0011, "v=1,type=connector,tenant=SCT-02");
1478
1479 int ret = test_retry(N_RETRIES,
1480 {.tls_crypt_v2_key_fn = "client.key",
1481 .tls_crypt_v2_dir = dir,
1482 .use_tls_auth_with_tls_crypt_v2 = true});
1483 EXPECT_EQ(ret, 0);
1484
1485 std::filesystem::remove_all(dir);
1486}
1487
1488// A server holding both keys becomes a tls-crypt-v2 one on the say-so of an opcode, which
1489// costs an off-path attacker one forged datagram. Here the client is a plain tls-auth one,
1490// so the session must stay in TLS_AUTH mode however loudly the forgery asks otherwise:
1491// before the wrap mode was taken back, the spoofed packet left the server unable to
1492// authenticate anything the real client went on to send.
1493TEST_F(ProtoUnitTest, TlsAuthSessionSurvivesSpoofedTlsCryptV2Opcode)
1494{
1495 int ret = test_retry(N_RETRIES,
1496 {.tls_crypt_v2_key_fn = "tls-crypt-v2-client-with-serverkey.key",
1497 .use_tls_auth_with_tls_crypt_v2 = true,
1498 .client_tls_auth_only = true,
1499 .spoof_hard_reset_v3 = true});
1500 EXPECT_EQ(ret, 0);
1501}
1502
1503// Dynamic tls-crypt rekeys the control channel with a key each end derives from the TLS
1504// session -- so it needs keying material export, which our mbed TLS does not have -- and
1505// mixes its own tls-crypt key into. A tls-crypt-v2 server's ProtoConfig holds
1506// the server key it unwraps WKc's with, not the Kc inside them, so mixing that in derived a
1507// key the client could not match and the rekey never completed. Both server key modes are
1508// covered: with a server key in the config the server mixed in the wrong key, and with
1509// serverkey_id it had none to mix in and skipped the step the client had taken.
1510TEST_F(ProtoUnitTest, DynamicTlsCryptRekeysTlsCryptV2WithServerkeyInConfig)
1511{
1512 if (!openvpn::SSLLib::SSLAPI::support_key_material_export())
1513 GTEST_SKIP_("our mbed TLS implementation does not support TLS EKM");
1514
1515 int ret = test_retry(N_RETRIES, {.use_dynamic_tls_crypt = true});
1516 EXPECT_EQ(ret, 0);
1517}
1518
1519TEST_F(ProtoUnitTest, DynamicTlsCryptRekeysTlsCryptV2WithServerkeyId)
1520{
1521 if (!openvpn::SSLLib::SSLAPI::support_key_material_export())
1522 GTEST_SKIP_("our mbed TLS implementation does not support TLS EKM");
1523
1524 int ret = test_retry(N_RETRIES, {.tls_crypt_v2_key_fn = "tls-crypt-v2-client-with-serverkey.key", .use_dynamic_tls_crypt = true});
1525 EXPECT_EQ(ret, 0);
1526}
1527
1528// The two above run tls-crypt-v2-only servers, where the config the key is picked from and
1529// the mode the session is in cannot disagree. A server holding a tls-auth key as well --
1530// what PG deploys -- is the case that can: reset_tls_wrap_mode() prefers TLS_AUTH, so such a
1531// session starts as tls-auth and only decapsulate() converts it once the client's WKc
1532// arrives. Picking the key to mix from the config then had the server mix tls_auth_key while
1533// its tls-crypt-v2 client mixed Kc; the handshake came up fine and the session died at the
1534// first rekey, hours in. Both server key modes, as above.
1535TEST_F(ProtoUnitTest, DynamicTlsCryptRekeysTlsCryptV2OnTlsAuthServerWithServerkeyInConfig)
1536{
1537 if (!openvpn::SSLLib::SSLAPI::support_key_material_export())
1538 GTEST_SKIP_("our mbed TLS implementation does not support TLS EKM");
1539
1540 int ret = test_retry(N_RETRIES,
1541 {.use_tls_auth_with_tls_crypt_v2 = true,
1542 .use_dynamic_tls_crypt = true});
1543 EXPECT_EQ(ret, 0);
1544}
1545
1546TEST_F(ProtoUnitTest, DynamicTlsCryptRekeysTlsCryptV2OnTlsAuthServerWithServerkeyId)
1547{
1548 if (!openvpn::SSLLib::SSLAPI::support_key_material_export())
1549 GTEST_SKIP_("our mbed TLS implementation does not support TLS EKM");
1550
1551 int ret = test_retry(N_RETRIES,
1552 {.tls_crypt_v2_key_fn = "tls-crypt-v2-client-with-serverkey.key",
1553 .use_tls_auth_with_tls_crypt_v2 = true,
1554 .use_dynamic_tls_crypt = true});
1555 EXPECT_EQ(ret, 0);
1556}
1557
1558// Regression test: when the tls-crypt-v2 WKc is appended to the first
1559// ciphertext-bearing control packet (EARLY_NEG_FLAG_RESEND_WKC -> the client
1560// emits CONTROL_WKC_V1), the SSL ciphertext placed into that packet must be
1561// trimmed to leave room for the WKc, so the assembled datagram still fits the
1562// control-channel frame. Without the reservation fix the packet overflows the
1563// frame by ~wkc.size() bytes and gets dropped, stalling the handshake.
1564TEST_F(ProtoUnitTest, TlsCryptV2WkcRidesFirstControlPacket)
1565{
1566 int ret = test_retry(N_RETRIES, {.tls_crypt_v2_key_fn = "tls-crypt-v2-client-with-serverkey.key", .force_resend_wkc = true});
1567 EXPECT_EQ(ret, 0);
1568}
1569
1570// Degenerate variant of the above: the control-channel payload is smaller
1571// than the WKc itself (mssfix-ctrl may go as low as 256 while a WKc can be
1572// up to 1024 bytes). The WKc reservation must clamp instead of wrapping the
1573// unsigned subtraction -- a wrap disables the trim entirely and the WKc
1574// packet overflows the frame again. The WKc of the test key is 328 bytes;
1575// 278 puts the payload just below it.
1576TEST_F(ProtoUnitTest, TlsCryptV2WkcLargerThanControlPayload)
1577{
1578 int ret = test_retry(N_RETRIES, {.tls_crypt_v2_key_fn = "tls-crypt-v2-client-with-serverkey.key", .force_resend_wkc = true, .control_payload = 278});
1579 EXPECT_EQ(ret, 0);
1580}
1581
1582// Every control packet, including the WKc-bearing one, must stay within the
1583// configured mssfix_ctrl limit after all tls wrappings have been applied.
1584// 420 leaves just enough room for the unsplittable WKc packet (~406 bytes
1585// worst case: tls-crypt header + full ACK block + the 328 byte WKc).
1586TEST_F(ProtoUnitTest, TlsCryptV2ControlPacketCapHonored)
1587{
1588 int ret = test_retry(N_RETRIES, {.tls_crypt_v2_key_fn = "tls-crypt-v2-client-with-serverkey.key", .force_resend_wkc = true, .mssfix_ctrl = 420});
1589 EXPECT_EQ(ret, 0);
1590}
1591
1592// Security regression test: unwrap_tls_crypt_wkc() reads the 16-bit WKc length
1593// (wkc_len) from the last two bytes of the received packet. For CONTROL_WKC_V1
1594// packets that value is fully attacker-controlled and, before the bounds-check
1595// fix, was fed straight into unsigned pointer arithmetic
1596// (wkc_raw = orig_data + orig_size - wkc_len). A wkc_len larger than the packet
1597// underflowed the size_t computation into a wild pointer and a bogus/oversized
1598// ciphertext length, producing an out-of-bounds read during decryption -- a
1599// pre-authentication remote crash (DoS). The unwrap must instead reject the
1600// packet with Error::CC_ERROR without dereferencing out of bounds.
1601//
1602// Builds a minimal ProtoConfig / tls-crypt server context (no full handshake
1603// needed) since the malformed length is rejected before any key material is
1604// touched.
1605class TlsCryptV2WkcUnwrapTest : public testing::Test
1606{
1607 protected:
1610
1611 void SetUp() override
1612 {
1614 pcfg->tls_crypt_factory.reset(new CryptoTLSCryptFactory<ClientCryptoAPI>());
1615 pcfg->set_tls_crypt_algs();
1616 // Fully initialize the server context (single server key, no key ID) so
1617 // that a malformed packet which slips past the length validation reaches
1618 // the real decrypt() read -- that is the read that goes out of bounds
1619 // without the fix (a segfault / ASan SEGV), and which the fix must avoid
1620 // by rejecting the packet up front.
1621 pcfg->tls_crypt_key.parse(read_text(TEST_KEYCERT_DIR "tls-auth.key"));
1622 tls_crypt_server = pcfg->tls_crypt_context->new_obj_recv();
1623 tls_crypt_server->init(nullptr,
1624 pcfg->tls_crypt_key.slice(OpenVPNStaticKey::HMAC),
1625 pcfg->tls_crypt_key.slice(OpenVPNStaticKey::CIPHER));
1626 }
1627
1628 // Build a CONTROL_WKC_V1 packet of the given size with the trailing 16-bit
1629 // WKc length field set to wkc_len (host order). Contents are otherwise
1630 // arbitrary -- the unwrap should bail on the length alone.
1631 BufferAllocated make_wkc_v1_packet(size_t size, uint16_t wkc_len)
1632 {
1634 buf.set_size(size);
1635 // op byte = op_compose(CONTROL_WKC_V1, key_id=0). CONTROL_WKC_V1 (11)
1636 // and op_compose() are protected members of ProtoContext, so encode it
1637 // directly here: (opcode << OPCODE_SHIFT[=3]) | key_id. Any opcode other
1638 // than CONTROL_HARD_RESET_CLIENT_V3 (10) selects the attacker-controlled
1639 // wkc_len branch being exercised.
1640 buf.data()[0] = static_cast<unsigned char>(11u << 3);
1641 const uint16_t net_wkc_len = htons(wkc_len);
1642 std::memcpy(buf.data() + size - sizeof(net_wkc_len), &net_wkc_len, sizeof(net_wkc_len));
1643 return buf;
1644 }
1645
1647 size_t frame_size() const
1648 {
1650 }
1651
1654 uint16_t min_wkc_len() const
1655 {
1656 return static_cast<uint16_t>(ProtoContext::KeyContext::wkc_overhead(*pcfg)
1658 }
1659};
1660
1661// wkc_len far larger than the packet: pre-fix this underflowed wkc_raw into a
1662// wild pointer read.
1663TEST_F(TlsCryptV2WkcUnwrapTest, RejectsWkcLenLargerThanPacket)
1664{
1665 BufferAllocated buf = make_wkc_v1_packet(200, 60000);
1667 EXPECT_EQ(ProtoContext::KeyContext::unwrap_tls_crypt_wkc(buf, *pcfg, *tls_crypt_server, unwrapped),
1669 EXPECT_FALSE(unwrapped.client_key.defined());
1670}
1671
1672// wkc_len smaller than the auth tag: pre-fix the decrypt ciphertext length
1673// (wkc_raw_size - hmac_size) underflowed to a huge oversized read.
1674TEST_F(TlsCryptV2WkcUnwrapTest, RejectsWkcLenSmallerThanAuthTag)
1675{
1676 BufferAllocated buf = make_wkc_v1_packet(200, 4);
1678 EXPECT_EQ(ProtoContext::KeyContext::unwrap_tls_crypt_wkc(buf, *pcfg, *tls_crypt_server, unwrapped),
1680 EXPECT_FALSE(unwrapped.client_key.defined());
1681}
1682
1683// strip_resent_wkc() takes the WKc off a retransmitted CONTROL_WKC_V1 without
1684// unwrapping it again. It reads the same attacker-controlled trailing length
1685// field as the unwrap above and needs the same guards, so exercise them here
1686// too: a length that survives validation is used to trim the packet, and one
1687// that doesn't must leave the packet alone and drop it.
1688
1689// A WKc's K_id names a file and the K_id is whatever the packet says it is, so a client whose
1690// key we never had -- or a forgery -- points at a file that is not there. read_text() throws
1691// open_file_error for it, which is not a BufferException, so it sailed past the only catch in
1692// decapsulate() and out of the psid cookie layer's intercept(), from a path reached before
1693// anything about the packet has been authenticated.
1694TEST_F(TlsCryptV2WkcUnwrapTest, UnknownServerKeyIdIsAnErrorAndNotAnException)
1695{
1696 pcfg->tls_crypt_v2_serverkey_id = true;
1697 pcfg->tls_crypt_v2_serverkey_dir = TEST_KEYCERT_DIR;
1698
1699 // room for a WKc behind a tls-crypt frame; the buffer is zero filled, so the K_id it
1700 // carries is 0 and names <dir>/00/00000000.key
1701 const uint16_t wkc_len = min_wkc_len();
1702 BufferAllocated buf = make_wkc_v1_packet(frame_size() + wkc_len, wkc_len);
1703
1706 EXPECT_NO_THROW(
1707 ret = ProtoContext::KeyContext::unwrap_tls_crypt_wkc(buf, *pcfg, *tls_crypt_server, unwrapped));
1708 EXPECT_EQ(ret, Error::DECRYPT_ERROR);
1709 EXPECT_FALSE(unwrapped.client_key.defined());
1710}
1711
1713{
1714 BufferAllocated buf = make_wkc_v1_packet(frame_size() + 300, 300);
1715 EXPECT_TRUE(ProtoContext::KeyContext::strip_resent_wkc(buf, *pcfg));
1716 // what is left is the frame the tls-crypt auth tag actually covers
1717 EXPECT_EQ(buf.size(), frame_size());
1718}
1719
1720TEST_F(TlsCryptV2WkcUnwrapTest, StripRejectsWkcLenLargerThanPacket)
1721{
1722 BufferAllocated buf = make_wkc_v1_packet(400, 60000);
1723 EXPECT_FALSE(ProtoContext::KeyContext::strip_resent_wkc(buf, *pcfg));
1724 EXPECT_EQ(buf.size(), 400u);
1725}
1726
1727// a WKc too small to even hold the client key it is supposed to wrap
1728TEST_F(TlsCryptV2WkcUnwrapTest, StripRejectsWkcLenSmallerThanClientKey)
1729{
1730 BufferAllocated buf = make_wkc_v1_packet(400, min_wkc_len() - 1);
1731 EXPECT_FALSE(ProtoContext::KeyContext::strip_resent_wkc(buf, *pcfg));
1732 EXPECT_EQ(buf.size(), 400u);
1733}
1734
1735// one byte more than the packet can spare: trimming it would leave less than a
1736// tls-crypt frame in front of the WKc
1737TEST_F(TlsCryptV2WkcUnwrapTest, StripRejectsWkcLeavingNoTlsCryptFrame)
1738{
1739 BufferAllocated buf = make_wkc_v1_packet(400, static_cast<uint16_t>(400 - frame_size() + 1));
1740 EXPECT_FALSE(ProtoContext::KeyContext::strip_resent_wkc(buf, *pcfg));
1741 EXPECT_EQ(buf.size(), 400u);
1742}
1743
1744// too short to hold a frame plus the trailing length field at all; the length
1745// read itself must not happen
1746TEST_F(TlsCryptV2WkcUnwrapTest, StripRejectsPacketShorterThanAFrame)
1747{
1748 BufferAllocated buf = make_wkc_v1_packet(frame_size() + 1, 300);
1749 EXPECT_FALSE(ProtoContext::KeyContext::strip_resent_wkc(buf, *pcfg));
1750}
1751
1752// with server key IDs in use -- how PG deploys tls-crypt-v2 -- the WKc carries a
1753// 4 byte K_id as well, so the minimum grows by that much
1754TEST_F(TlsCryptV2WkcUnwrapTest, StripAccountsForServerKeyId)
1755{
1756 pcfg->tls_crypt_v2_serverkey_id = true;
1757 // the one place the number is spelled out rather than asked for: length field, a
1758 // SHA-256 tag, K_id and the client key
1759 ASSERT_EQ(min_wkc_len(), sizeof(uint16_t) + 32 + sizeof(uint32_t) + OpenVPNStaticKey::KEY_SIZE);
1760
1761 BufferAllocated too_small = make_wkc_v1_packet(400, min_wkc_len() - 1);
1762 EXPECT_FALSE(ProtoContext::KeyContext::strip_resent_wkc(too_small, *pcfg));
1763
1764 BufferAllocated ok = make_wkc_v1_packet(400, min_wkc_len());
1765 EXPECT_TRUE(ProtoContext::KeyContext::strip_resent_wkc(ok, *pcfg));
1766 EXPECT_EQ(ok.size(), 400u - min_wkc_len());
1767}
1768#endif
1769
1770TEST_F(ProtoUnitTest, BaseMultipleThread)
1771{
1772 unsigned int num_threads = std::thread::hardware_concurrency();
1773#if defined(PROTO_N_THREADS) && PROTO_N_THREADS >= 1
1774 num_threads = PROTO_N_THREADS;
1775#endif
1776
1777 std::vector<std::thread> running_threads{};
1778 std::vector<int> results(num_threads, -777);
1779
1780 for (unsigned int i = 0; i < num_threads; ++i)
1781 {
1782 running_threads.emplace_back([i, &results]()
1783 {
1784 /* Use ekm on odd threads */
1785 const bool use_ekm = openvpn::SSLLib::SSLAPI::support_key_material_export() && (i % 2 == 0);
1786 results[i] = test_retry(N_RETRIES, { .use_tls_ekm = use_ekm }); });
1787 }
1788 for (unsigned int i = 0; i < num_threads; ++i)
1789 {
1790 running_threads[i].join();
1791 }
1792
1793
1794 // expect 1 for all threads
1795 const std::vector<int> expected_results(num_threads, 0);
1796
1797 EXPECT_THAT(expected_results, ::testing::ContainerEq(results));
1798}
1799
1800TEST(Proto, IvCiphersAead)
1801{
1802 CryptoAlgs::allow_default_dc_algs<SSLLib::CryptoAPI>(nullptr, true, false);
1803
1804 auto protoConf = openvpn::ProtoContext::ProtoConfig();
1805
1806 auto infostring = protoConf.peer_info_string(false);
1807
1808 auto ivciphers = infostring.substr(infostring.find("IV_CIPHERS="));
1809 ivciphers = ivciphers.substr(0, ivciphers.find("\n"));
1810
1811
1812 std::string expectedstr{"IV_CIPHERS=AES-128-GCM:AES-192-GCM:AES-256-GCM"};
1813 if (SSLLib::CryptoAPI::CipherContextAEAD::is_supported(nullptr, openvpn::CryptoAlgs::CHACHA20_POLY1305))
1814 expectedstr += ":CHACHA20-POLY1305";
1815
1816 EXPECT_EQ(ivciphers, expectedstr);
1817}
1818
1819TEST(Proto, IvCiphersNonPreferred)
1820{
1821 CryptoAlgs::allow_default_dc_algs<SSLLib::CryptoAPI>(nullptr, false, false);
1822
1823 auto protoConf = openvpn::ProtoContext::ProtoConfig();
1824
1825 auto infostring = protoConf.peer_info_string(true);
1826
1827 auto ivciphers = infostring.substr(infostring.find("IV_CIPHERS="));
1828 ivciphers = ivciphers.substr(0, ivciphers.find("\n"));
1829
1830
1831 std::string expectedstr{"IV_CIPHERS=AES-128-CBC:AES-192-CBC:AES-256-CBC:AES-128-GCM:AES-192-GCM:AES-256-GCM"};
1832 if (SSLLib::CryptoAPI::CipherContextAEAD::is_supported(nullptr, openvpn::CryptoAlgs::CHACHA20_POLY1305))
1833 expectedstr += ":CHACHA20-POLY1305";
1834
1835 EXPECT_EQ(ivciphers, expectedstr);
1836}
1837
1838TEST(Proto, IvCiphersLegacy)
1839{
1840
1841 /* Need to a whole lot of things to enable legacy provider/OpenSSL context */
1842 SSLLib::SSLAPI::Config::Ptr config = new SSLLib::SSLAPI::Config;
1843 EXPECT_TRUE(config);
1844
1845 StrongRandomAPI::Ptr rng(new SSLLib::RandomAPI());
1846 config->set_rng(rng);
1847
1848 config->set_mode(Mode(Mode::CLIENT));
1850 config->set_local_cert_enabled(false);
1851 config->enable_legacy_algorithms(true);
1852
1853 auto factory_client = config->new_factory();
1854 EXPECT_TRUE(factory_client);
1855
1856 auto client = factory_client->ssl();
1857 auto libctx = factory_client->libctx();
1858
1859
1860 CryptoAlgs::allow_default_dc_algs<SSLLib::CryptoAPI>(libctx, false, true);
1861
1862 auto protoConf = openvpn::ProtoContext::ProtoConfig();
1863
1864 auto infostring = protoConf.peer_info_string(false);
1865
1866 auto ivciphers = infostring.substr(infostring.find("IV_CIPHERS="));
1867 ivciphers = ivciphers.substr(0, ivciphers.find("\n"));
1868
1869
1870
1871 std::string expectedstr{"IV_CIPHERS=none:AES-128-CBC:AES-192-CBC:AES-256-CBC:DES-CBC:DES-EDE3-CBC"};
1872
1873 if (SSLLib::CryptoAPI::CipherContext::is_supported(libctx, openvpn::CryptoAlgs::BF_CBC))
1874 expectedstr += ":BF-CBC";
1875
1876 expectedstr += ":AES-128-GCM:AES-192-GCM:AES-256-GCM";
1877
1878 if (SSLLib::CryptoAPI::CipherContextAEAD::is_supported(nullptr, openvpn::CryptoAlgs::CHACHA20_POLY1305))
1879 expectedstr += ":CHACHA20-POLY1305";
1880
1881 EXPECT_EQ(ivciphers, expectedstr);
1882}
1883
1884TEST(Proto, ControlmessageInvalidchar)
1885{
1886 std::string valid_auth_fail{"AUTH_FAILED: go away"};
1887 std::string valid_auth_fail_newline_end{"AUTH_FAILED: go away\n"};
1888 std::string invalid_auth_fail{"AUTH_FAILED: go\n away\n"};
1889 std::string lot_of_whitespace{"AUTH_FAILED: a lot of white space\n\n\r\n\r\n\r\n"};
1890 std::string only_whitespace{"\n\n\r\n\r\n\r\n"};
1891 std::string empty{""};
1892
1893 BufferAllocated valid_auth_fail_buf{reinterpret_cast<const unsigned char *>(valid_auth_fail.c_str()), valid_auth_fail.size(), BufAllocFlags::GROW};
1894 BufferAllocated valid_auth_fail_newline_end_buf{reinterpret_cast<const unsigned char *>(valid_auth_fail_newline_end.c_str()), valid_auth_fail_newline_end.size(), BufAllocFlags::GROW};
1895 BufferAllocated invalid_auth_fail_buf{reinterpret_cast<const unsigned char *>(invalid_auth_fail.c_str()), invalid_auth_fail.size(), BufAllocFlags::GROW};
1896 BufferAllocated lot_of_whitespace_buf{reinterpret_cast<const unsigned char *>(lot_of_whitespace.c_str()), lot_of_whitespace.size(), BufAllocFlags::GROW};
1897 BufferAllocated only_whitespace_buf{reinterpret_cast<const unsigned char *>(only_whitespace.c_str()), only_whitespace.size(), BufAllocFlags::GROW};
1898 BufferAllocated empty_buf{reinterpret_cast<const unsigned char *>(empty.c_str()), empty.size(), BufAllocFlags::GROW};
1899
1900 auto msg = ProtoContext::read_control_string<std::string>(valid_auth_fail_buf);
1901 EXPECT_EQ(msg, valid_auth_fail);
1903
1904 auto msg2 = ProtoContext::read_control_string<std::string>(valid_auth_fail_newline_end_buf);
1905 EXPECT_EQ(msg2, valid_auth_fail);
1907
1908 auto msg3 = ProtoContext::read_control_string<std::string>(invalid_auth_fail_buf);
1909 EXPECT_EQ(msg3, "AUTH_FAILED: go\n away");
1910 EXPECT_FALSE(Unicode::is_valid_utf8(msg3, Unicode::UTF8_NO_CTRL));
1911
1912 auto msg4 = ProtoContext::read_control_string<std::string>(lot_of_whitespace_buf);
1913 EXPECT_EQ(msg4, "AUTH_FAILED: a lot of white space");
1915
1916 auto msg5 = ProtoContext::read_control_string<std::string>(only_whitespace_buf);
1917 EXPECT_EQ(msg5, "");
1919
1920 auto msg6 = ProtoContext::read_control_string<std::string>(empty_buf);
1921 EXPECT_EQ(msg6, "");
1923}
1924
1931
1933{
1934 public:
1936 {
1937 events.push_back(event);
1938 }
1939
1940 std::vector<openvpn::ClientEvent::Base::Ptr> events;
1941};
1942
1943TEST(Proto, ClientProtoCheckCcMsg)
1944{
1945 asio::io_context io_context;
1946 ClientRandomAPI::Ptr rng_cli(new ClientRandomAPI());
1947 Frame::Ptr frame(new Frame(Frame::Context(128, 378, 128, 0, 16, BufAllocFlags::NO_FLAGS)));
1948 MySessionStats::Ptr cli_stats(new MySessionStats);
1949 Time time;
1950
1952 /* keep a reference to the right class to avoid repeated casted */
1953 EventQueueVector *eqv = dynamic_cast<EventQueueVector *>(eqv_ptr.get());
1954 /* check that the cast worked */
1955 ASSERT_TRUE(eqv);
1956
1957 MockCallback mockCB;
1960 frame,
1961 rng_cli,
1962 std::move(cli_stats),
1963 time);
1964 clisessconf.cli_events = std::move(eqv_ptr);
1965 openvpn::ClientProto::Session::Ptr clisession = new ClientProto::Session{io_context, clisessconf, &mockCB};
1966
1967 clisession->validate_and_post_cc_msg("valid message");
1968
1969
1970 EXPECT_TRUE(eqv->events.empty());
1971
1972 clisession->validate_and_post_cc_msg("invalid\nmessage");
1973 EXPECT_EQ(eqv->events.size(), 1);
1974 auto ev = eqv->events.back();
1975 auto uf = dynamic_cast<openvpn::ClientEvent::UnsupportedFeature *>(ev.get());
1976 /* check that the cast worked */
1977 ASSERT_TRUE(uf);
1978 EXPECT_EQ(uf->name, "Invalid chars in control message");
1979 EXPECT_EQ(uf->reason, "Control channel message with invalid characters not allowed to be send with post_cc_msg");
1980}
Time::Duration operator()() const
Time::Duration drought
std::string name
DroughtMeasure(const std::string &name_arg, TimePtr now_arg)
OPENVPN_SIMPLE_EXCEPTION(drought_limit_exceeded)
void add_event(openvpn::ClientEvent::Base::Ptr event) override
std::vector< openvpn::ClientEvent::Base::Ptr > events
void client_proto_terminate()
count_t errors[Error::N_ERRORS]
Definition test_comp.cpp:89
void error(const size_t err_type, const std::string *text=nullptr) override
count_t get_error_count(const Error::Type type) const
void show_error_counts() const
BufferPtr recv()
void xfer(T1 &a, T2 &b)
std::deque< BufferPtr > wire
unsigned int rand(const unsigned int prob)
unsigned int reorder_prob
NoisyWire(const std::string &title_arg, TimePtr now_arg, RandomAPI &rand_arg, const unsigned int reorder_prob_arg, const unsigned int drop_prob_arg, const unsigned int corrupt_prob_arg)
RandomAPI & random
std::string title
unsigned int corrupt_prob
unsigned int drop_prob
void SetUp() override
void TearDown() override
TestProtoClient(const ProtoContext::ProtoConfig::Ptr &config, const SessionStats::Ptr &stats)
void client_auth(Buffer &buf) override
void server_auth(const std::string &username, const SafeString &password, const std::string &peer_info, const AuthCert::Ptr &auth_cert) override
TestProtoServer(const ProtoContext::ProtoConfig::Ptr &config, const SessionStats::Ptr &stats)
OPENVPN_SIMPLE_EXCEPTION(auth_failed)
void check_invalidated()
TestProto(const ProtoContext::ProtoConfig::Ptr &config, const SessionStats::Ptr &stats)
void control_recv(BufferPtr &&app_bp) override
DroughtMeasure data_drought
void modmsg(BufferPtr &buf)
bool verify_wkc_packets_fit(size_t max_size) const
void data_decrypt(const ProtoContext::PacketType &type, BufferAllocated &in_out)
void control_send(BufferPtr &&app_bp)
void data_encrypt(BufferAllocated &in_out)
std::vector< size_t > wkc_pkt_sizes_
void initial_app_send(const char *msg)
const char * progress() const
void control_send(BufferAllocated &&app_buf)
ProtoContext proto_context
void reset()
void active(bool primary) override
Called when KeyContext transitions to ACTIVE state.
size_t app_bytes() const
size_t n_control_recv() const
void disable_xmit()
DroughtMeasure control_drought
Frame::Ptr frame
size_t max_ctrl_pkt_size() const
BufferPtr data_encrypt_string(const char *str)
size_t n_control_send() const
OPENVPN_EXCEPTION(session_invalidated)
size_t n_control_recv_
void app_send_templ_init(const char *msg)
size_t n_control_send_
size_t app_bytes_
void control_net_send(const Buffer &net_buf) override
size_t max_ctrl_pkt_size_
bool do_housekeeping()
void finalize()
bool supports_epoch_data() override
BufferPtr templ
char progress_[11]
size_t data_bytes() const
void app_send_templ()
size_t data_bytes_
size_t net_bytes() const
size_t net_bytes_
void copy_progress(Buffer &buf)
std::deque< BufferPtr > net_out
bool disable_xmit_
size_t frame_size() const
Smallest tls-crypt frame a WKc can follow, from the code under test.
BufferAllocated make_wkc_v1_packet(size_t size, uint16_t wkc_len)
ProtoContext::ProtoConfig::Ptr pcfg
TLSCryptInstance::Ptr tls_crypt_server
uint16_t min_wkc_len() const
const T * c_data() const
Returns a const pointer to the start of the buffer.
Definition buffer.hpp:1193
size_t size() const
Returns the size of the buffer in T objects.
Definition buffer.hpp:1241
T * data()
Get a mutable pointer to the start of the array.
Definition buffer.hpp:1448
bool empty() const
Returns true if the buffer is empty.
Definition buffer.hpp:1235
void set_size(const size_t size)
After an external method, operating on the array as a mutable unsigned char buffer,...
Definition buffer.hpp:1382
@ READ_BIO_MEMQ_STREAM
Definition frame.hpp:41
size_t prepare(const unsigned int context, Buffer &buf) const
Definition frame.hpp:263
static bool strip_resent_wkc(Buffer &recv, const ProtoConfig &proto_config)
Virtually remove a resent WKc from the end of a CONTROL_WKC_V1 packet.
Definition proto.hpp:3108
static size_t tls_crypt_frame_size(const ProtoConfig &proto_config)
Smallest tls-crypt frame a WKc can be appended to.
Definition proto.hpp:2834
static Error::Type unwrap_tls_crypt_wkc(Buffer &recv, const ProtoConfig &proto_config, TLSCryptInstance &tls_crypt_server, UnwrappedWkc &unwrapped)
Extract and process the TLS crypt WKc information.
Definition proto.hpp:2926
static size_t wkc_overhead(const ProtoConfig &proto_config)
What a WKc holds besides the client key and the metadata.
Definition proto.hpp:2850
bool control_net_recv(const PacketType &type, BufferPtr &&net_bp)
Definition proto.hpp:4967
const Time::Duration & slowest_handshake()
Definition proto.hpp:5073
const Time & now() const
Definition proto.hpp:5174
void flush(const bool control_channel)
Definition proto.hpp:4894
void control_send(BufferPtr &&app_bp)
Definition proto.hpp:4950
PacketType packet_type(const Buffer &buf)
Definition proto.hpp:4844
void data_encrypt(BufferAllocated &in_out)
Definition proto.hpp:4988
static unsigned char op_compose(const unsigned int opcode, const unsigned int key_id)
Definition proto.hpp:326
static void write_auth_string(const S &str, Buffer &buf)
Definition proto.hpp:1572
bool is_server() const
Definition proto.hpp:5198
bool data_decrypt(const PacketType &type, BufferAllocated &in_out)
Definition proto.hpp:4998
Error::Type invalidation_reason() const
Definition proto.hpp:5085
unsigned int negotiations() const
Definition proto.hpp:5067
Time next_housekeeping() const
Definition proto.hpp:4932
bool invalidated() const
Definition proto.hpp:5079
void reset(const ProtoSessionID cookie_psid=ProtoSessionID())
Resets ProtoContext *this to it's initial state.
Definition proto.hpp:4733
void start(const ProtoSessionID cookie_psid=ProtoSessionID())
Initialize the state machine and start protocol negotiation.
Definition proto.hpp:4857
const ProtoConfig & conf() const
Definition proto.hpp:5218
void reset() noexcept
Points this RCPtr<T> to nullptr safely.
Definition rc.hpp:290
void swap(RCPtr &rhs) noexcept
swaps the contents of two RCPtr<T>
Definition rc.hpp:311
T * get() const noexcept
Returns the raw pointer to the object T, or nullptr.
Definition rc.hpp:321
Abstract base class for random number generators.
Definition randapi.hpp:39
T randrange(const T end)
Return a uniformly distributed random number in the range [0, end)
Definition randapi.hpp:117
static Ptr Create(ArgsT &&...args)
Creates a new instance of RcEnable with the given arguments.
Definition make_rc.hpp:43
A string-like type that clears the buffer contents on delete.
Definition safestr.hpp:27
virtual TLSCryptContext::Ptr new_obj(SSLLib::Ctx libctx, const CryptoAlgs::Type digest_type, const CryptoAlgs::Type cipher_type)=0
virtual void init(SSLLib::Ctx libctx, const StaticKey &key_hmac, const StaticKey &key_crypt)=0
void parse(const std::string &key_text)
void extract_wkc(BufferAllocated &wkc_out) const
void generate(StrongRandomAPI &rng, const TLSCryptV2ServerKey &server_key, const std::string &metadata, const int metadata_type, const std::optional< std::uint32_t > key_id, SSLLib::Ctx libctx=nullptr)
Mint a fresh client key Kc and the WKc that carries it to a server.
void extract_key(OpenVPNStaticKey &tls_key)
void generate(StrongRandomAPI &rng)
Mint a fresh server key, replacing any key already held.
void parse(const std::string &key_text)
void extract_key(OpenVPNStaticKey &tls_key) const
T raw() const
Definition time.hpp:404
static TimeType infinite()
Definition time.hpp:254
bool defined() const
Definition time.hpp:280
static void set_log_level(int level)
set the log level for all loggigng
Definition logger.hpp:174
void work(openvpn_io::io_context &io_context, ThreadCommon &tc, MyRunContext &runctx, const unsigned int unit)
constexpr BufferFlags GROW(1U<< 2)
if enabled, buffer will grow (otherwise buffer_full exception will be thrown)
constexpr BufferFlags CONSTRUCT_ZERO(1U<< 0)
if enabled, constructors/init will zero allocated space
constexpr BufferFlags NO_FLAGS(0U)
no flags set
Type lookup(const std::string &name)
const char * name(const size_t type)
Definition error.hpp:117
@ HANDSHAKE_TIMEOUT
Definition error.hpp:60
bool is_valid_utf8(const STRING &str, const size_t max_len_flags=0)
Definition unicode.hpp:75
std::string read_text(const std::string &filename, const std::uint64_t max_size=0)
Definition file.hpp:127
long long count_t
Definition count.hpp:16
void write_string(const std::string &filename, const std::string &str)
Definition file.hpp:199
RCPtr< BufferAllocatedRc > BufferPtr
Definition buffer.hpp:1899
std::string render_hex_number(T value, const bool caps=false)
Definition hexstr.hpp:459
ProtoContext::ProtoConfig::Ptr proto_context_config
Definition cliproto.hpp:126
unsigned int mssfix
Definition mssparms.hpp:71
What a WKc yields, owned by whoever asked for the unwrap.
Definition proto.hpp:2911
OpenVPNStaticKey client_key
Kc, the client key the WKc wrapped.
Definition proto.hpp:2913
const std::string & tls_crypt_v2_key_fn
bool client_tls_auth_only
const std::string & tls_crypt_v2_dir
bool tls_version_mismatch
bool force_resend_wkc
bool use_tls_auth_with_tls_crypt_v2
size_t mssfix_ctrl
size_t control_payload
bool use_dynamic_tls_crypt
bool spoof_hard_reset_v3
std::string getTempDirPath(const std::string &fn)
static const char config[]
#define TLS_TIMEOUT_CLIENT
static void write_generated_tls_crypt_v2_keys(const std::string &dir, const std::string &client_fn, const std::uint32_t k_id, const std::string &metadata)
Mint a tls-crypt-v2 key pair the way the deployment tooling does.
#define TLS_TIMEOUT_SERVER
#define PROTO_DIGEST
static auto create_client_proto_context(ClientSSLAPI::Config::Ptr cc, Frame::Ptr frame, ClientRandomAPI::Ptr rng, MySessionStats::Ptr cli_stats, Time &time, const std::string &tls_crypt_v2_key_fn="", bool tls_auth_only=false, bool use_dynamic_tls_crypt=false, const std::string &tls_crypt_v2_dir=TEST_KEYCERT_DIR)
int test_retry(const int n_retries, const struct proto_test &test_config)
TEST_F(ProtoUnitTest, BaseSingleThreadTlsEkm)
#define TLS_VER_MIN
#define N_RETRIES
#define COMP_METH
#define ITER
TEST(Proto, IvCiphersAead)
const char message[]
#define PROTO_CIPHER
#define SITER
#define RENEG
static auto create_client_ssl_config(Frame::Ptr frame, ClientRandomAPI::Ptr rng, bool tls_version_mismatch=false)
void test()
Definition test_rc.cpp:80
#define msg(flags,...)