diff --git a/README.md b/README.md index 484cba7f8a801b39968d274921c5394ba044b1db..8c71b37da1ea547fd43dccf7b0a0e71128d5e5d9 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ $ make doc <tr><td><a href="https://lab.baconsvin.org/talla/onion/blob/develop/doc/onion_relay.md" class="module">onion_relay</a></td></tr> <tr><td><a href="https://lab.baconsvin.org/talla/onion/blob/develop/doc/onion_rsa.md" class="module">onion_rsa</a></td></tr> <tr><td><a href="https://lab.baconsvin.org/talla/onion/blob/develop/doc/onion_server_descriptor.md" class="module">onion_server_descriptor</a></td></tr> +<tr><td><a href="https://lab.baconsvin.org/talla/onion/blob/develop/doc/onion_ssl.md" class="module">onion_ssl</a></td></tr> <tr><td><a href="https://lab.baconsvin.org/talla/onion/blob/develop/doc/onion_string.md" class="module">onion_string</a></td></tr> <tr><td><a href="https://lab.baconsvin.org/talla/onion/blob/develop/doc/onion_time.md" class="module">onion_time</a></td></tr> <tr><td><a href="https://lab.baconsvin.org/talla/onion/blob/develop/doc/onion_x25519.md" class="module">onion_x25519</a></td></tr> diff --git a/doc/README.md b/doc/README.md index 34174cda3f79b46c801c8913b64b8844fcda1b4f..da6e3fc768559792d91c041025fd750bb82e4e84 100644 --- a/doc/README.md +++ b/doc/README.md @@ -51,6 +51,7 @@ $ make doc <tr><td><a href="onion_relay.md" class="module">onion_relay</a></td></tr> <tr><td><a href="onion_rsa.md" class="module">onion_rsa</a></td></tr> <tr><td><a href="onion_server_descriptor.md" class="module">onion_server_descriptor</a></td></tr> +<tr><td><a href="onion_ssl.md" class="module">onion_ssl</a></td></tr> <tr><td><a href="onion_string.md" class="module">onion_string</a></td></tr> <tr><td><a href="onion_time.md" class="module">onion_time</a></td></tr> <tr><td><a href="onion_x25519.md" class="module">onion_x25519</a></td></tr> diff --git a/doc/edoc-info b/doc/edoc-info index 000ecaef94e40196dc1cddc6b0b922979accbb28..0f7d397653750b8be7b8c1dfd464c20886809730 100644 --- a/doc/edoc-info +++ b/doc/edoc-info @@ -5,5 +5,5 @@ onion_ed25519,onion_exit_policy,onion_file,onion_kdf,onion_lists, onion_math,onion_ntor,onion_os,onion_pem,onion_protocol, onion_random,onion_registry,onion_relay,onion_rsa, - onion_server_descriptor,onion_string,onion_time,onion_x25519, - onion_x509]}. + onion_server_descriptor,onion_ssl,onion_string,onion_time, + onion_x25519,onion_x509]}. diff --git a/doc/onion_ssl.md b/doc/onion_ssl.md new file mode 100644 index 0000000000000000000000000000000000000000..8cd0117ed9a5bad01f178ea40258ad272d5c71b7 --- /dev/null +++ b/doc/onion_ssl.md @@ -0,0 +1,63 @@ + + +# Module onion_ssl # +* [Description](#description) +* [Function Index](#index) +* [Function Details](#functions) + +SSL API. + +__Authors:__ Alexander Færøy ([`ahf@0x90.dk`](mailto:ahf@0x90.dk)). + +<a name="description"></a> + +## Description ## +Utility functions to be used together with OTP's SSL application. +<a name="index"></a> + +## Function Index ## + + +<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#client_random-1">client_random/1</a></td><td>Get the SSL client random value.</td></tr><tr><td valign="top"><a href="#master_secret-1">master_secret/1</a></td><td>Get the SSL master secret value.</td></tr><tr><td valign="top"><a href="#server_random-1">server_random/1</a></td><td>Get the SSL server random value.</td></tr></table> + + +<a name="functions"></a> + +## Function Details ## + +<a name="client_random-1"></a> + +### client_random/1 ### + +<pre><code> +client_random(Socket) -> binary() +</code></pre> + +<ul class="definitions"><li><code>Socket = <a href="ssl.md#type-sslsocket">ssl:sslsocket()</a></code></li></ul> + +Get the SSL client random value. + +<a name="master_secret-1"></a> + +### master_secret/1 ### + +<pre><code> +master_secret(Socket) -> binary() +</code></pre> + +<ul class="definitions"><li><code>Socket = <a href="ssl.md#type-sslsocket">ssl:sslsocket()</a></code></li></ul> + +Get the SSL master secret value. + +<a name="server_random-1"></a> + +### server_random/1 ### + +<pre><code> +server_random(Socket) -> binary() +</code></pre> + +<ul class="definitions"><li><code>Socket = <a href="ssl.md#type-sslsocket">ssl:sslsocket()</a></code></li></ul> + +Get the SSL server random value. + diff --git a/src/onion_ssl.erl b/src/onion_ssl.erl new file mode 100644 index 0000000000000000000000000000000000000000..ac026c4b881b123f76f06ae21d5fa6c179775192 --- /dev/null +++ b/src/onion_ssl.erl @@ -0,0 +1,66 @@ +%%% +%%% Copyright (c) 2016 The Talla Authors. All rights reserved. +%%% Use of this source code is governed by a BSD-style +%%% license that can be found in the LICENSE file. +%%% +%%% ----------------------------------------------------------- +%%% @author Alexander Færøy <ahf@0x90.dk> +%%% @doc SSL API +%%% +%%% Utility functions to be used together with OTP's SSL application. +%%% +%%% @end +%%% ----------------------------------------------------------- +-module(onion_ssl). + +%% API. +-export([master_secret/1, + client_random/1, + server_random/1 + ]). + +-include_lib("ssl/src/ssl_record.hrl"). +-include_lib("ssl/src/ssl_connection.hrl"). + +%% @doc Get the SSL master secret value. +-spec master_secret(Socket) -> binary() + when + Socket :: ssl:sslsocket(). +master_secret(Socket) -> + #security_parameters { + master_secret = MasterSecret + } = lookup(Socket), + MasterSecret. + +%% @doc Get the SSL client random value. +-spec client_random(Socket) -> binary() + when + Socket :: ssl:sslsocket(). +client_random(Socket) -> + #security_parameters { + client_random = ClientRandom + } = lookup(Socket), + ClientRandom. + +%% @doc Get the SSL server random value. +-spec server_random(Socket) -> binary() + when + Socket :: ssl:sslsocket(). +server_random(Socket) -> + #security_parameters { + server_random = ServerRandom + } = lookup(Socket), + ServerRandom. + +%% @private +-spec lookup(Socket) -> term() + when + Socket :: ssl:sslsocket(). +lookup({sslsocket, _, Connection}) -> + {_, #state { + connection_states = CS + }} = sys:get_state(Connection), + #connection_state { + security_parameters = Params + } = ssl_record:current_connection_state(CS, read), + Params. diff --git a/test/ssl_SUITE.erl b/test/ssl_SUITE.erl new file mode 100644 index 0000000000000000000000000000000000000000..32992ef865ea5933d085af14d969b176b8abb022 --- /dev/null +++ b/test/ssl_SUITE.erl @@ -0,0 +1,107 @@ +%%% +%%% Copyright (c) 2016 The Talla Authors. All rights reserved. +%%% Use of this source code is governed by a BSD-style +%%% license that can be found in the LICENSE file. +%%% +%%% ----------------------------------------------------------- +%%% @author Alexander Færøy <ahf@0x90.dk> +%%% @doc Common Test suite for onion_ssl. +%%% @end +%%% ----------------------------------------------------------- +-module(ssl_SUITE). + +%% Test Cases. +-export([read_values/1]). + +%% Common Test API +-export([all/0, + groups/0, + init_per_suite/1, + end_per_suite/1, + init_per_testcase/2, + end_per_testcase/2 + ]). + +-include_lib("onion/include/onion_test.hrl"). + +all() -> + [{group, basic_group}]. + +groups() -> + [{basic_group, [], [read_values]}]. + +init_per_suite(Config) -> + {ok, _} = application:ensure_all_started(onion), + Config. + +end_per_suite(_Config) -> + ok = application:stop(onion), + ok. + +init_per_testcase(_Case, Config) -> + {ok, #{ secret := SecretKey, public := PublicKey }} = onion_rsa:keypair(1024), + {ok, SecretKeyDER} = onion_rsa:der_encode(SecretKey), + {ok, Cert} = onion_x509:create_certificate(#{ + public_key => PublicKey, + valid_from => onion_time:from_unix_epoch(0), + valid_to => onion_time:from_unix_epoch(0), + subject => [{name, "example.org"}], + issuer => [{name, "example.org"}] + }), + CertDER = onion_x509:sign(Cert, SecretKey), + Self = self(), + {ok, ListenSocket} = ssl:listen(30000, [{reuseaddr, true}, {key, {'RSAPrivateKey', SecretKeyDER}}, {cert, CertDER}]), + Listener = spawn_link(fun Fun() -> + {ok, Socket} = ssl:transport_accept(ListenSocket), + ok = ssl:ssl_accept(Socket), + Self ! {ssl_socket, self(), Socket}, + spawn_link(fun() -> + socket_loop(Socket) + end), + Fun() + end), + [{listener, Listener} | Config]. + +end_per_testcase(_Case, _Config) -> + + ok. + +read_values(Config) -> + Listener = ?config(listener, Config), + {ok, ClientSocket} = ssl:connect("127.0.0.1", 30000, [], 500), + ServerSocket = receive + {ssl_socket, Listener, Sock} -> + Sock + after 500 -> + ct:fail(missing_server_socket) + end, + + MasterSecretClient = onion_ssl:master_secret(ClientSocket), + ClientRandomClient = onion_ssl:client_random(ClientSocket), + ServerRandomClient = onion_ssl:server_random(ClientSocket), + + MasterSecretServer = onion_ssl:master_secret(ServerSocket), + ClientRandomServer = onion_ssl:client_random(ServerSocket), + ServerRandomServer = onion_ssl:server_random(ServerSocket), + + ct:pal("Master Secret:~n ~s~n ~s", [onion_test:base64_encode(MasterSecretClient), + onion_test:base64_encode(MasterSecretServer)]), + ct:pal("Client Random:~n ~s~n ~s", [onion_test:base64_encode(ClientRandomClient), + onion_test:base64_encode(ClientRandomServer)]), + ct:pal("Server Random:~n ~s~n ~s", [onion_test:base64_encode(ServerRandomClient), + onion_test:base64_encode(ServerRandomServer)]), + + MasterSecretClient = MasterSecretServer, + ClientRandomClient = ClientRandomServer, + ServerRandomClient = ServerRandomServer, + + ok. + +socket_loop(Socket) -> + receive + X -> + X + after 500 -> + timeout + end, + socket_loop(Socket).