# erltls
[![Build Status](https://travis-ci.org/silviucpp/erltls.svg?branch=master)](https://travis-ci.org/silviucpp/erltls)
![GitHub](https://img.shields.io/github/license/silviucpp/erltls)
![Hex.pm](https://img.shields.io/hexpm/v/erltls)
![Maintenance](https://img.shields.io/maintenance/yes/2019)
*TLS/SSL/DTLS OpenSSL/BoringSSL-based NIF library for Erlang using the same interface as erlang `ssl` module.*
This module contains interface functions for the SSL/TLS protocol.
At this moment not all methods available in [`ssl`][1] module are implemented and also not all `ssl_option` are supported.
### Implementation notes
The library is implemented in top of `gen_tcp` and `OpenSSL` API. Because `SSL_*` API is not thread safe in order to make sure
that all calls for the same socket are not executed concurrently each socket has a gen_server to interact with native layer.
This way we don't have to use any kind of locking at native level. Also read/write operations are not taking place in the
`gen_server` process, they take place in calling process, in order to avoid gen_server to become a bottleneck for the socket.
### Features not available in standard Erlang ssl
- DTLS support (both v1 and v1.2)
- RFC5077 for resuming sessions using ticketing
- Can be used with both [OpenSSL][4] or [BoringSSL][3]
### OpenSSL fork
The library is working with both [OpenSSL][4] or [BoringSSL][3]. Be default BoringSSL is used. To change the behavior you need to specify the
`USE_BORINGSSL` environment variable:
```
USE_BORINGSSL=0 rebar compile
```
### User guide
Add `erltls` as a rebar/rebar3 dependency to your project:
```
{deps, [
{erltls, ".*", {git, "https://github.com/silviucpp/erltls.git", "master"}},
}.
```
Now you can use `erltls` module the same way you are using `ssl`:
```erlang
%% server side
erltls:start(),
{ok, ListenSocket} = erltls:listen(9999, [
{certfile, "test/server.pem"},
{reuseaddr, true}
]),
{ok, Socket} = erltls:transport_accept(ListenSocket),
ok = erltls:ssl_accept(Socket),
ok = erltls:setopts(Socket, [{active, once}]),
receive AMessage -> io:format("recv:~p~n", [AMessage]) end,
ok = erltls:close(Socket),
ok = erltls:close(ListenSocket).
```
```erlang
%% client side
erltls:start(),
{ok, Socket} = erltls:connect("localhost", 9999, [], infinity),
ok = erltls:send(Socket, "foo"),
ok = erltls:close(Socket).
```
### Using with Ranch
`erltls` can be easily used with `ranch` by starting a listener with `ranch_erltls` as the transport module:
```erlang
{ok, _} = ranch:start_listener(tcp_echo, 100,
ranch_erltls, [{port, 5555}, {certfile, CertPath}],
echo_protocol, []).
```
### Supported SSL options
Currently supported options also available in erlang `ssl`:
- `{verify, verify_type()}`
- `{depth, integer()}`
- `{fail_if_no_peer_cert, boolean()}`
- `{certfile, path()}`
- `{keyfile, path()}`
- `{password, string()}`
- `{cacertfile, path()}`
- `{dhfile, path()}`
- `{ciphers, ciphers()}`
Options currently related to `erltls` only:
- `{use_session_ticket, boolean() | {boolean(), binary()}}` - Enables session reuse using tickets as described in [RFC5077][2].
In case the key is not specified the tickets will be signed using a random key. In order to specify the key use the format `{true, <<"key_here">>}`.
- `{reuse_sessions_ttl, integer()}` - The TTL of a session in seconds. In case the session is older than this TTL will not be possible to be reused.
A full handshake will be negotiated.
- `{protocol, protocol()}` - Specify the desired protocol. By default will negotiate highest available SSL/TLS version. Available protocols values are:
`sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2' | dtlsv1 | 'dtlsv1.2'`
### Unsupported SSL methods:
- `renegotiate/1`
- `prf/5`
- `negotiated_protocol/1`
- `format_error/1`
- `eccs/1`
- `eccs/0`
- `connection_information/2`
### SSL methods related to erltls
- `session_reused/1` - Query, whether a reused session was negotiated during the handshake (make sense if session ticketing is enabled)
### Current limitations
- working only with `{packet, 0}` inet option
- Handshake process don't have a timeout implemented yet. So in `connect/*` or `ssl_accept/*` the timeout param is actually
not counting the handshake process.
### Benchmarks
For benchmarks between different drivers see [here][5]
[1]:http://erlang.org/doc/man/ssl.html
[2]:https://www.ietf.org/rfc/rfc5077.txt
[3]:https://boringssl.googlesource.com/boringssl/
[4]:https://www.openssl.org/
[5]:https://github.com/silviucpp/tls_bench