Skip to content

Commit

Permalink
Simplify assigning crypto method to include all TLS versions
Browse files Browse the repository at this point in the history
This only simplifies some of unneeded assignments for legacy PHP
versions and should not affect usage otherwise. TLS 1.3 is implicitly
available despite being omitted in this assignment. The required crypto
flag is likely going to be added in PHP 7.2.x in the future via
php/php-src#3700 and should thus be covered by
the main crypto method constant in the future already.

Due to the way how PHP interfaces with OpenSSL, this means that TLS 1.3
is in fact already enabled by default when using a recent OpenSSL
version for all client and server connections even for older PHP
versions.
  • Loading branch information
clue committed Jan 2, 2019
1 parent e02071c commit c2097ea
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 22 deletions.
30 changes: 8 additions & 22 deletions src/StreamEncryption.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,35 +25,21 @@ public function __construct(LoopInterface $loop, $server = true)
$this->server = $server;

// support TLSv1.0+ by default and exclude legacy SSLv2/SSLv3.
// PHP 5.6+ supports bitmasks, legacy PHP only supports predefined
// constants, so apply accordingly below.
// Also, since PHP 5.6.7 up until before PHP 7.2.0 the main constant did
// only support TLSv1.0, so we explicitly apply all versions.
// @link http://php.net/manual/en/migration56.openssl.php#migration56.openssl.crypto-method
// @link https://3v4l.org/plbFn
// As of PHP 7.2+ the main crypto method constant includes all TLS versions.
// As of PHP 5.6+ the crypto method is a bitmask, so we explicitly include all TLS versions.
// For legacy PHP < 5.6 the crypto method is a single value only and this constant includes all TLS versions.
// @link https://3v4l.org/9PSST
if ($server) {
$this->method = \STREAM_CRYPTO_METHOD_TLS_SERVER;

if (\defined('STREAM_CRYPTO_METHOD_TLSv1_0_SERVER')) {
$this->method |= \STREAM_CRYPTO_METHOD_TLSv1_0_SERVER;
}
if (\defined('STREAM_CRYPTO_METHOD_TLSv1_1_SERVER')) {
$this->method |= \STREAM_CRYPTO_METHOD_TLSv1_1_SERVER;
}
if (\defined('STREAM_CRYPTO_METHOD_TLSv1_2_SERVER')) {
$this->method |= \STREAM_CRYPTO_METHOD_TLSv1_2_SERVER;
if (\PHP_VERSION_ID < 70200 && \PHP_VERSION_ID >= 50600) {
$this->method |= \STREAM_CRYPTO_METHOD_TLSv1_0_SERVER | \STREAM_CRYPTO_METHOD_TLSv1_1_SERVER | \STREAM_CRYPTO_METHOD_TLSv1_2_SERVER;
}
} else {
$this->method = \STREAM_CRYPTO_METHOD_TLS_CLIENT;

if (\defined('STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT')) {
$this->method |= \STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT;
}
if (\defined('STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT')) {
$this->method |= \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
}
if (\defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) {
$this->method |= \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
if (\PHP_VERSION_ID < 70200 && \PHP_VERSION_ID >= 50600) {
$this->method |= \STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT | \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT | \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
}
}
}
Expand Down
106 changes: 106 additions & 0 deletions tests/FunctionalSecureServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,95 @@ public function testClientCanConnectToServer()
$server->close();
}

public function testClientUsesTls13ByDefaultWhenSupportedByOpenSSL()
{
if (PHP_VERSION_ID < 70000 || !$this->supportsTls13()) {
$this->markTestSkipped('Test requires PHP 7+ for crypto meta data and OpenSSL 1.1.1+ for TLS 1.3');
}

$loop = Factory::create();

$server = new TcpServer(0, $loop);
$server = new SecureServer($server, $loop, array(
'local_cert' => __DIR__ . '/../examples/localhost.pem'
));

$connector = new SecureConnector(new TcpConnector($loop), $loop, array(
'verify_peer' => false
));
$promise = $connector->connect($server->getAddress());

/* @var ConnectionInterface $client */
$client = Block\await($promise, $loop, self::TIMEOUT);

$this->assertInstanceOf('React\Socket\Connection', $client);
$this->assertTrue(isset($client->stream));

$meta = stream_get_meta_data($client->stream);
$this->assertTrue(isset($meta['crypto']['cipher_version']));
$this->assertEquals('TLSv1.3', $meta['crypto']['cipher_version']);
}

public function testClientUsesTls12WhenCryptoMethodIsExplicitlyConfiguredByClient()
{
if (PHP_VERSION_ID < 70000) {
$this->markTestSkipped('Test requires PHP 7+ for crypto meta data');
}

$loop = Factory::create();

$server = new TcpServer(0, $loop);
$server = new SecureServer($server, $loop, array(
'local_cert' => __DIR__ . '/../examples/localhost.pem'
));

$connector = new SecureConnector(new TcpConnector($loop), $loop, array(
'verify_peer' => false,
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
));
$promise = $connector->connect($server->getAddress());

/* @var ConnectionInterface $client */
$client = Block\await($promise, $loop, self::TIMEOUT);

$this->assertInstanceOf('React\Socket\Connection', $client);
$this->assertTrue(isset($client->stream));

$meta = stream_get_meta_data($client->stream);
$this->assertTrue(isset($meta['crypto']['cipher_version']));
$this->assertEquals('TLSv1.2', $meta['crypto']['cipher_version']);
}

public function testClientUsesTls12WhenCryptoMethodIsExplicitlyConfiguredByServer()
{
if (PHP_VERSION_ID < 70000) {
$this->markTestSkipped('Test requires PHP 7+ for crypto meta data');
}

$loop = Factory::create();

$server = new TcpServer(0, $loop);
$server = new SecureServer($server, $loop, array(
'local_cert' => __DIR__ . '/../examples/localhost.pem',
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER
));

$connector = new SecureConnector(new TcpConnector($loop), $loop, array(
'verify_peer' => false
));
$promise = $connector->connect($server->getAddress());

/* @var ConnectionInterface $client */
$client = Block\await($promise, $loop, self::TIMEOUT);

$this->assertInstanceOf('React\Socket\Connection', $client);
$this->assertTrue(isset($client->stream));

$meta = stream_get_meta_data($client->stream);
$this->assertTrue(isset($meta['crypto']['cipher_version']));
$this->assertEquals('TLSv1.2', $meta['crypto']['cipher_version']);
}

public function testServerEmitsConnectionForClientConnection()
{
$loop = Factory::create();
Expand Down Expand Up @@ -621,4 +710,21 @@ private function createPromiseForEvent(EventEmitterInterface $emitter, $event, $
});
});
}

private function supportsTls13()
{
// TLS 1.3 is supported as of OpenSSL 1.1.1 (https://www.openssl.org/blog/blog/2018/09/11/release111/)
// The OpenSSL library version can only be obtained by parsing output from phpinfo().
// OPENSSL_VERSION_TEXT refers to header version which does not necessarily match actual library version
// see php -i | grep OpenSSL
// OpenSSL Library Version => OpenSSL 1.1.1 11 Sep 2018
ob_start();
phpinfo(INFO_MODULES);
$info = ob_get_clean();

if (preg_match('/OpenSSL Library Version => OpenSSL (\S+)/', $info, $match)) {
return version_compare($match[1], '1.1.1', '>=');
}
return false;
}
}

0 comments on commit c2097ea

Please sign in to comment.