@@ -48,6 +48,103 @@ public function testClientCanConnectToServer()
4848 $ server ->close ();
4949 }
5050
51+ public function testClientUsesTls13ByDefaultWhenSupportedByOpenSSL ()
52+ {
53+ if (PHP_VERSION_ID < 70000 || !$ this ->supportsTls13 ()) {
54+ $ this ->markTestSkipped ('Test requires PHP 7+ for crypto meta data and OpenSSL 1.1.1+ for TLS 1.3 ' );
55+ }
56+
57+ $ loop = Factory::create ();
58+
59+ $ server = new TcpServer (0 , $ loop );
60+ $ server = new SecureServer ($ server , $ loop , array (
61+ 'local_cert ' => __DIR__ . '/../examples/localhost.pem '
62+ ));
63+
64+ $ connector = new SecureConnector (new TcpConnector ($ loop ), $ loop , array (
65+ 'verify_peer ' => false
66+ ));
67+ $ promise = $ connector ->connect ($ server ->getAddress ());
68+
69+ /* @var ConnectionInterface $client */
70+ $ client = Block \await ($ promise , $ loop , self ::TIMEOUT );
71+
72+ $ this ->assertInstanceOf ('React\Socket\Connection ' , $ client );
73+ $ this ->assertTrue (isset ($ client ->stream ));
74+
75+ $ meta = stream_get_meta_data ($ client ->stream );
76+ $ this ->assertTrue (isset ($ meta ['crypto ' ]['protocol ' ]));
77+
78+ if ($ meta ['crypto ' ]['protocol ' ] === 'UNKNOWN ' ) {
79+ // TLSv1.3 protocol will only be added via https://github.com/php/php-src/pull/3700
80+ // prior to merging that PR, this info is still available in the cipher version by OpenSSL
81+ $ this ->assertTrue (isset ($ meta ['crypto ' ]['cipher_version ' ]));
82+ $ this ->assertEquals ('TLSv1.3 ' , $ meta ['crypto ' ]['cipher_version ' ]);
83+ } else {
84+ $ this ->assertEquals ('TLSv1.3 ' , $ meta ['crypto ' ]['protocol ' ]);
85+ }
86+ }
87+
88+ public function testClientUsesTls12WhenCryptoMethodIsExplicitlyConfiguredByClient ()
89+ {
90+ if (PHP_VERSION_ID < 70000 ) {
91+ $ this ->markTestSkipped ('Test requires PHP 7+ for crypto meta data ' );
92+ }
93+
94+ $ loop = Factory::create ();
95+
96+ $ server = new TcpServer (0 , $ loop );
97+ $ server = new SecureServer ($ server , $ loop , array (
98+ 'local_cert ' => __DIR__ . '/../examples/localhost.pem '
99+ ));
100+
101+ $ connector = new SecureConnector (new TcpConnector ($ loop ), $ loop , array (
102+ 'verify_peer ' => false ,
103+ 'crypto_method ' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
104+ ));
105+ $ promise = $ connector ->connect ($ server ->getAddress ());
106+
107+ /* @var ConnectionInterface $client */
108+ $ client = Block \await ($ promise , $ loop , self ::TIMEOUT );
109+
110+ $ this ->assertInstanceOf ('React\Socket\Connection ' , $ client );
111+ $ this ->assertTrue (isset ($ client ->stream ));
112+
113+ $ meta = stream_get_meta_data ($ client ->stream );
114+ $ this ->assertTrue (isset ($ meta ['crypto ' ]['protocol ' ]));
115+ $ this ->assertEquals ('TLSv1.2 ' , $ meta ['crypto ' ]['protocol ' ]);
116+ }
117+
118+ public function testClientUsesTls12WhenCryptoMethodIsExplicitlyConfiguredByServer ()
119+ {
120+ if (PHP_VERSION_ID < 70000 ) {
121+ $ this ->markTestSkipped ('Test requires PHP 7+ for crypto meta data ' );
122+ }
123+
124+ $ loop = Factory::create ();
125+
126+ $ server = new TcpServer (0 , $ loop );
127+ $ server = new SecureServer ($ server , $ loop , array (
128+ 'local_cert ' => __DIR__ . '/../examples/localhost.pem ' ,
129+ 'crypto_method ' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER
130+ ));
131+
132+ $ connector = new SecureConnector (new TcpConnector ($ loop ), $ loop , array (
133+ 'verify_peer ' => false
134+ ));
135+ $ promise = $ connector ->connect ($ server ->getAddress ());
136+
137+ /* @var ConnectionInterface $client */
138+ $ client = Block \await ($ promise , $ loop , self ::TIMEOUT );
139+
140+ $ this ->assertInstanceOf ('React\Socket\Connection ' , $ client );
141+ $ this ->assertTrue (isset ($ client ->stream ));
142+
143+ $ meta = stream_get_meta_data ($ client ->stream );
144+ $ this ->assertTrue (isset ($ meta ['crypto ' ]['protocol ' ]));
145+ $ this ->assertEquals ('TLSv1.2 ' , $ meta ['crypto ' ]['protocol ' ]);
146+ }
147+
51148 public function testServerEmitsConnectionForClientConnection ()
52149 {
53150 $ loop = Factory::create ();
@@ -621,4 +718,21 @@ private function createPromiseForEvent(EventEmitterInterface $emitter, $event, $
621718 });
622719 });
623720 }
721+
722+ private function supportsTls13 ()
723+ {
724+ // TLS 1.3 is supported as of OpenSSL 1.1.1 (https://www.openssl.org/blog/blog/2018/09/11/release111/)
725+ // The OpenSSL library version can only be obtained by parsing output from phpinfo().
726+ // OPENSSL_VERSION_TEXT refers to header version which does not necessarily match actual library version
727+ // see php -i | grep OpenSSL
728+ // OpenSSL Library Version => OpenSSL 1.1.1 11 Sep 2018
729+ ob_start ();
730+ phpinfo (INFO_MODULES );
731+ $ info = ob_get_clean ();
732+
733+ if (preg_match ('/OpenSSL Library Version => OpenSSL (\S+)/ ' , $ info , $ match )) {
734+ return version_compare ($ match [1 ], '1.1.1 ' , '>= ' );
735+ }
736+ return false ;
737+ }
624738}
0 commit comments