Skip to content

Commit

Permalink
Merge pull request #149 from clue-labs/tls
Browse files Browse the repository at this point in the history
Support explicitly choosing TLS version to negotiate with remote side
  • Loading branch information
WyriHaximus authored Jan 17, 2018
2 parents 0cf2798 + 63d050a commit f74669a
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 5 deletions.
49 changes: 48 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,19 @@ $server = new Server('tls://127.0.0.1:8000', $loop, array(
));
```

By default, this server supports TLSv1.0+ and excludes support for legacy
SSLv2/SSLv3. As of PHP 5.6+ you can also explicitly choose the TLS version you
want to negotiate with the remote side:

```php
$server = new Server('tls://127.0.0.1:8000', $loop, array(
'tls' => array(
'local_cert' => 'server.pem',
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER
)
));
```

> Note that available [TLS context options](http://php.net/manual/en/context.ssl.php),
their defaults and effects of changing these may vary depending on your system
and/or PHP version.
Expand Down Expand Up @@ -612,6 +625,18 @@ $server = new SecureServer($server, $loop, array(
));
```

By default, this server supports TLSv1.0+ and excludes support for legacy
SSLv2/SSLv3. As of PHP 5.6+ you can also explicitly choose the TLS version you
want to negotiate with the remote side:

```php
$server = new TcpServer(8000, $loop);
$server = new SecureServer($server, $loop, array(
'local_cert' => 'server.pem',
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER
));
```

> Note that available [TLS context options](http://php.net/manual/en/context.ssl.php),
their defaults and effects of changing these may vary depending on your system
and/or PHP version.
Expand Down Expand Up @@ -1000,6 +1025,18 @@ $connector->connect('tls://localhost:443')->then(function (ConnectionInterface $
});
```

By default, this connector supports TLSv1.0+ and excludes support for legacy
SSLv2/SSLv3. As of PHP 5.6+ you can also explicitly choose the TLS version you
want to negotiate with the remote side:

```php
$connector = new Connector($loop, array(
'tls' => array(
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
)
));
```

> For more details about context options, please refer to the PHP documentation
about [socket context options](http://php.net/manual/en/context.socket.php)
and [SSL context options](http://php.net/manual/en/context.ssl.php).
Expand Down Expand Up @@ -1189,7 +1226,7 @@ $promise->cancel();
```

Calling `cancel()` on a pending promise will cancel the underlying TCP/IP
connection and/or the SSL/TLS negonation and reject the resulting promise.
connection and/or the SSL/TLS negotiation and reject the resulting promise.

You can optionally pass additional
[SSL context options](http://php.net/manual/en/context.ssl.php)
Expand All @@ -1202,6 +1239,16 @@ $secureConnector = new React\Socket\SecureConnector($dnsConnector, $loop, array(
));
```

By default, this connector supports TLSv1.0+ and excludes support for legacy
SSLv2/SSLv3. As of PHP 5.6+ you can also explicitly choose the TLS version you
want to negotiate with the remote side:

```php
$secureConnector = new React\Socket\SecureConnector($dnsConnector, $loop, array(
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
));
```

> Advanced usage: Internally, the `SecureConnector` relies on setting up the
required *context options* on the underlying stream resource.
It should therefor be used with a `TcpConnector` somewhere in the connector
Expand Down
22 changes: 18 additions & 4 deletions src/StreamEncryption.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ public function __construct(LoopInterface $loop, $server = true)
$this->loop = $loop;
$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
if ($server) {
$this->method = STREAM_CRYPTO_METHOD_TLS_SERVER;

Expand Down Expand Up @@ -79,9 +86,16 @@ public function toggle(Connection $stream, $toggle)
// get actual stream socket from stream instance
$socket = $stream->stream;

// get crypto method from context options or use global setting from constructor
$method = $this->method;
$context = stream_context_get_options($socket);
if (isset($context['ssl']['crypto_method'])) {
$method = $context['ssl']['crypto_method'];
}

$that = $this;
$toggleCrypto = function () use ($socket, $deferred, $toggle, $that) {
$that->toggleCrypto($socket, $deferred, $toggle);
$toggleCrypto = function () use ($socket, $deferred, $toggle, $method, $that) {
$that->toggleCrypto($socket, $deferred, $toggle, $method);
};

$this->loop->addReadStream($socket, $toggleCrypto);
Expand All @@ -106,10 +120,10 @@ public function toggle(Connection $stream, $toggle)
});
}

public function toggleCrypto($socket, Deferred $deferred, $toggle)
public function toggleCrypto($socket, Deferred $deferred, $toggle, $method)
{
set_error_handler(array($this, 'handleError'));
$result = stream_socket_enable_crypto($socket, $toggle, $this->method);
$result = stream_socket_enable_crypto($socket, $toggle, $method);
restore_error_handler();

if (true === $result) {
Expand Down
48 changes: 48 additions & 0 deletions tests/FunctionalSecureServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,54 @@ public function testPipesDataBackInMultipleChunksFromConnection()
$this->assertEquals(400000, $received);
}

/**
* @requires PHP 5.6
*/
public function testEmitsConnectionForNewTlsv11Connection()
{
$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_1_SERVER
));
$server->on('connection', $this->expectCallableOnce());

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

Block\await($promise, $loop, self::TIMEOUT);
}

/**
* @requires PHP 5.6
*/
public function testEmitsErrorForClientWithTlsVersionMismatch()
{
$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_1_SERVER|STREAM_CRYPTO_METHOD_TLSv1_2_SERVER
));
$server->on('connection', $this->expectCallableNever());
$server->on('error', $this->expectCallableOnce());

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

$this->setExpectedException('RuntimeException', 'handshake');
Block\await($promise, $loop, self::TIMEOUT);
}

public function testEmitsConnectionForNewConnectionWithEncryptedCertificate()
{
$loop = Factory::create();
Expand Down

0 comments on commit f74669a

Please sign in to comment.