Packages

TLS client for Gleam on Erlang

Current section

Files

Jump to
kafein src kafein.erl
Raw

src/kafein.erl

-module(kafein).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/kafein.gleam").
-export([protocol_versions/2, alpn/2, cacert_file/2, cipher_suites/2, depth/2, verify/2, certificate/2, certificates/2, curves/2, handshake_pause/1, server_name_indication/2, send_builder/2, send/2, shutdown/1, export_key_materials/4, export_key_material/4, handshake_cancel/1, 'receive'/2, receive_exact/3, receive_next_packet_as_message/1, select_ssl_messages/2, wrap/2, handshake_continue/3]).
-export_type([ssl_socket/0, tls_alert_kind/0, curve/0, error/0, export_key_materials_error/0, protocol_version/0, verification_type/0, certificate/0, wrap_options/0, ssl_message/0, internal_tls_option/0, gen_tcp_option/0, active_type/0, handshake_type/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type ssl_socket() :: any().
-type tls_alert_kind() :: close_notify |
unexpected_message |
bad_record_mac |
record_overflow |
handshake_failure |
bad_certificate |
unsupported_certificate |
certificate_revoked |
certificate_expired |
certificate_unknown |
illegal_parameter |
unknown_ca |
access_denied |
decode_error |
decrypt_error |
export_restriction |
protocol_version |
insufficient_security |
internal_error |
inappropriate_fallback |
user_canceled |
no_renegotiation |
unsupported_extension |
certificate_unobtainable |
unrecognized_name |
bad_certificate_status_response |
bad_certificate_hash_value |
unknown_psk_identity |
no_application_protocol.
-type curve() :: x25519 |
x448 |
secp521r1 |
brainpool_p512r1 |
brainpool_p384r1 |
secp384r1 |
brainpool_p256r1 |
secp256r1 |
sect571r1 |
sect571k1 |
sect409k1 |
sect409r1 |
sect283k1 |
sect283r1 |
secp256k1 |
sect239k1 |
sect233k1 |
sect233r1 |
secp224k1 |
secp224r1 |
sect193r1 |
sect193r2 |
secp192k1 |
secp192r1 |
sect163k1 |
sect163r1 |
sect163r2 |
secp160k1 |
secp160r1 |
secp160r2.
-type error() :: closed |
timeout |
{other, gleam@dynamic:dynamic_()} |
{posix_error, mug:error()} |
{cipher_suite_not_recognized, binary()} |
{tls_alert, tls_alert_kind(), binary()}.
-type export_key_materials_error() :: exporter_master_secret_already_consumed |
bad_input.
-type protocol_version() :: tlsv1 | tlsv1m1 | tlsv1m2 | tlsv1m3.
-type verification_type() :: verify_none | verify_peer.
-type certificate() :: {certificate,
binary(),
binary(),
gleam@option:option(fun(() -> binary()))}.
-type wrap_options() :: {wrap_options,
list(protocol_version()),
list(binary()),
gleam@option:option(binary()),
list(binary()),
integer(),
verification_type(),
list(certificate()),
list(curve()),
boolean(),
gleam@option:option(binary())}.
-type ssl_message() :: {packet, ssl_socket(), bitstring()} |
{socket_closed, ssl_socket()} |
{ssl_error, ssl_socket(), error()}.
-type internal_tls_option() :: {versions, list(gleam@erlang@atom:atom_())} |
{cacerts, gleam@dynamic:dynamic_()} |
{depth, integer()} |
{verify, verification_type()} |
{ciphers, list(gleam@dynamic:dynamic_())} |
{alpn_advertised_protocols, list(binary())} |
{cacertfile, binary()} |
{certs_keys, list(gleam@dict:dict(binary(), gleam@dynamic:dynamic_()))} |
{handshake, handshake_type()} |
{eccs, list(curve())} |
{server_name_indication, gleam@dynamic:dynamic_()}.
-type gen_tcp_option() :: {active, active_type()}.
-type active_type() :: once.
-type handshake_type() :: hello | full.
-file("src/kafein.gleam", 170).
?DOC(" Set protocol versions for upgrade.\n").
-spec protocol_versions(wrap_options(), list(protocol_version())) -> wrap_options().
protocol_versions(Options, Protocol_versions) ->
{wrap_options,
Protocol_versions,
erlang:element(3, Options),
erlang:element(4, Options),
erlang:element(5, Options),
erlang:element(6, Options),
erlang:element(7, Options),
erlang:element(8, Options),
erlang:element(9, Options),
erlang:element(10, Options),
erlang:element(11, Options)}.
-file("src/kafein.gleam", 178).
?DOC(" Set supported ALPN protocols.\n").
-spec alpn(wrap_options(), list(binary())) -> wrap_options().
alpn(Options, Alpn) ->
{wrap_options,
erlang:element(2, Options),
Alpn,
erlang:element(4, Options),
erlang:element(5, Options),
erlang:element(6, Options),
erlang:element(7, Options),
erlang:element(8, Options),
erlang:element(9, Options),
erlang:element(10, Options),
erlang:element(11, Options)}.
-file("src/kafein.gleam", 183).
?DOC(" Set cacert file used for TLS.\n").
-spec cacert_file(wrap_options(), binary()) -> wrap_options().
cacert_file(Options, Cacert_file) ->
{wrap_options,
erlang:element(2, Options),
erlang:element(3, Options),
{some, Cacert_file},
erlang:element(5, Options),
erlang:element(6, Options),
erlang:element(7, Options),
erlang:element(8, Options),
erlang:element(9, Options),
erlang:element(10, Options),
erlang:element(11, Options)}.
-file("src/kafein.gleam", 191).
?DOC(" Set ciphers suites used for TLS.\n").
-spec cipher_suites(wrap_options(), list(binary())) -> wrap_options().
cipher_suites(Options, Cipher_suites) ->
{wrap_options,
erlang:element(2, Options),
erlang:element(3, Options),
erlang:element(4, Options),
Cipher_suites,
erlang:element(6, Options),
erlang:element(7, Options),
erlang:element(8, Options),
erlang:element(9, Options),
erlang:element(10, Options),
erlang:element(11, Options)}.
-file("src/kafein.gleam", 199).
?DOC(" Set depth of allowed certificate chain.\n").
-spec depth(wrap_options(), integer()) -> wrap_options().
depth(Options, Depth) ->
{wrap_options,
erlang:element(2, Options),
erlang:element(3, Options),
erlang:element(4, Options),
erlang:element(5, Options),
Depth,
erlang:element(7, Options),
erlang:element(8, Options),
erlang:element(9, Options),
erlang:element(10, Options),
erlang:element(11, Options)}.
-file("src/kafein.gleam", 204).
?DOC(" Set verification type for certificate.\n").
-spec verify(wrap_options(), verification_type()) -> wrap_options().
verify(Options, Verify) ->
{wrap_options,
erlang:element(2, Options),
erlang:element(3, Options),
erlang:element(4, Options),
erlang:element(5, Options),
erlang:element(6, Options),
Verify,
erlang:element(8, Options),
erlang:element(9, Options),
erlang:element(10, Options),
erlang:element(11, Options)}.
-file("src/kafein.gleam", 212).
?DOC(" Add certificate used for TLS.\n").
-spec certificate(wrap_options(), certificate()) -> wrap_options().
certificate(Options, Cert) ->
{wrap_options,
erlang:element(2, Options),
erlang:element(3, Options),
erlang:element(4, Options),
erlang:element(5, Options),
erlang:element(6, Options),
erlang:element(7, Options),
lists:append(erlang:element(8, Options), [Cert]),
erlang:element(9, Options),
erlang:element(10, Options),
erlang:element(11, Options)}.
-file("src/kafein.gleam", 223).
?DOC(" Add list of certificates used for TLS.\n").
-spec certificates(wrap_options(), list(certificate())) -> wrap_options().
certificates(Options, Certs) ->
{wrap_options,
erlang:element(2, Options),
erlang:element(3, Options),
erlang:element(4, Options),
erlang:element(5, Options),
erlang:element(6, Options),
erlang:element(7, Options),
lists:append(erlang:element(8, Options), Certs),
erlang:element(9, Options),
erlang:element(10, Options),
erlang:element(11, Options)}.
-file("src/kafein.gleam", 231).
?DOC(" Set curves used for TLS.\n").
-spec curves(wrap_options(), list(curve())) -> wrap_options().
curves(Options, Curves) ->
{wrap_options,
erlang:element(2, Options),
erlang:element(3, Options),
erlang:element(4, Options),
erlang:element(5, Options),
erlang:element(6, Options),
erlang:element(7, Options),
erlang:element(8, Options),
Curves,
erlang:element(10, Options),
erlang:element(11, Options)}.
-file("src/kafein.gleam", 236).
?DOC(" Make handshake pause after HELLO.\n").
-spec handshake_pause(wrap_options()) -> wrap_options().
handshake_pause(Options) ->
{wrap_options,
erlang:element(2, Options),
erlang:element(3, Options),
erlang:element(4, Options),
erlang:element(5, Options),
erlang:element(6, Options),
erlang:element(7, Options),
erlang:element(8, Options),
erlang:element(9, Options),
true,
erlang:element(11, Options)}.
-file("src/kafein.gleam", 241).
?DOC(" Set SNI.\n").
-spec server_name_indication(wrap_options(), binary()) -> wrap_options().
server_name_indication(Options, Name) ->
{wrap_options,
erlang:element(2, Options),
erlang:element(3, Options),
erlang:element(4, Options),
erlang:element(5, Options),
erlang:element(6, Options),
erlang:element(7, Options),
erlang:element(8, Options),
erlang:element(9, Options),
erlang:element(10, Options),
{some, Name}}.
-file("src/kafein.gleam", 321).
-spec send_builder(ssl_socket(), gleam@bytes_tree:bytes_tree()) -> {ok, nil} |
{error, mug:error()}.
send_builder(Socket, Data) ->
kafein_ffi:send(Socket, Data).
-file("src/kafein.gleam", 316).
-spec send(ssl_socket(), bitstring()) -> {ok, nil} | {error, mug:error()}.
send(Socket, Data) ->
kafein_ffi:send(Socket, gleam@bytes_tree:from_bit_array(Data)).
-file("src/kafein.gleam", 362).
-spec shutdown(ssl_socket()) -> {ok, nil} | {error, mug:error()}.
shutdown(Socket) ->
kafein_ffi:shutdown(Socket).
-file("src/kafein.gleam", 299).
?DOC(
" Export some of key materials.\n"
"\n"
" Returns an error if exporting was failed.\n"
).
-spec export_key_materials(
ssl_socket(),
list(bitstring()),
list(gleam@option:option(bitstring())),
list(integer())
) -> {ok, list(bitstring())} | {error, export_key_materials_error()}.
export_key_materials(Socket, Labels, Contexts, Lengths) ->
Contexts@1 = gleam@list:map(
Contexts,
fun(Wrap_context) -> case Wrap_context of
{some, Context} ->
gleam_stdlib:identity(Context);
none ->
_pipe = erlang:binary_to_atom(<<"no_context"/utf8>>),
gleam_erlang_ffi:identity(_pipe)
end end
),
ssl:export_key_materials(Socket, Labels, Contexts@1, Lengths).
-file("src/kafein.gleam", 281).
?DOC(
" Export a single key material.\n"
"\n"
" Returns an error if exporting was failed.\n"
).
-spec export_key_material(
ssl_socket(),
bitstring(),
gleam@option:option(bitstring()),
integer()
) -> {ok, bitstring()} | {error, export_key_materials_error()}.
export_key_material(Socket, Label, Context, Length) ->
gleam@result:'try'(
export_key_materials(Socket, [Label], [Context], [Length]),
fun(Key_materials) -> case gleam@list:first(Key_materials) of
{error, _} ->
{error, bad_input};
{ok, Key_material} ->
{ok, Key_material}
end end
).
-file("src/kafein.gleam", 274).
?DOC(
" Cancel the handshake operation.\n"
"\n"
" Returns an error if cancellation was failed.\n"
).
-spec handshake_cancel(ssl_socket()) -> {ok, nil} | {error, error()}.
handshake_cancel(Socket) ->
kafein_ffi:handshake_cancel(Socket).
-file("src/kafein.gleam", 326).
-spec 'receive'(ssl_socket(), integer()) -> {ok, bitstring()} |
{error, mug:error()}.
'receive'(Socket, Timeout) ->
ssl:recv(Socket, 0, Timeout).
-file("src/kafein.gleam", 333).
-spec receive_exact(ssl_socket(), integer(), integer()) -> {ok, bitstring()} |
{error, mug:error()}.
receive_exact(Socket, Size, Timeout) ->
ssl:recv(Socket, Size, Timeout).
-file("src/kafein.gleam", 341).
-spec receive_next_packet_as_message(ssl_socket()) -> nil.
receive_next_packet_as_message(Socket) ->
ssl:setopts(Socket, [{active, once}]),
nil.
-file("src/kafein.gleam", 346).
-spec select_ssl_messages(
gleam@erlang@process:selector(HTB),
fun((ssl_message()) -> HTB)
) -> gleam@erlang@process:selector(HTB).
select_ssl_messages(Selector, Mapper) ->
Ssl = erlang:binary_to_atom(<<"ssl"/utf8>>),
Closed = erlang:binary_to_atom(<<"ssl_closed"/utf8>>),
Error = erlang:binary_to_atom(<<"ssl_error"/utf8>>),
Map_message = fun(Msg) -> Mapper(kafein_ffi:coerce_ssl_message(Msg)) end,
_pipe = Selector,
_pipe@1 = gleam@erlang@process:select_record(_pipe, Ssl, 2, Map_message),
_pipe@2 = gleam@erlang@process:select_record(
_pipe@1,
Closed,
1,
Map_message
),
gleam@erlang@process:select_record(_pipe@2, Error, 2, Map_message).
-file("src/kafein.gleam", 391).
-spec coerce_options(wrap_options()) -> {ok, list(internal_tls_option())} |
{error, error()}.
coerce_options(Options) ->
gleam@result:'try'(
kafein_ffi:strs_to_suites(erlang:element(5, Options)),
fun(Ciphers) ->
_pipe@8 = [{versions,
gleam@list:map(
erlang:element(2, Options),
fun(Ver) -> erlang:binary_to_atom(case Ver of
tlsv1 ->
<<"tlsv1"/utf8>>;
tlsv1m1 ->
<<"tlsv1.1"/utf8>>;
tlsv1m2 ->
<<"tlsv1.2"/utf8>>;
tlsv1m3 ->
<<"tlsv1.3"/utf8>>
end) end
)},
{cacerts, public_key:cacerts_get()},
{depth, erlang:element(6, Options)},
{verify, erlang:element(7, Options)},
{certs_keys,
gleam@list:map(
erlang:element(8, Options),
fun(Cert) -> _pipe = maps:new(),
_pipe@2 = gleam@dict:insert(
_pipe,
<<"certfile"/utf8>>,
begin
_pipe@1 = erlang:element(2, Cert),
gleam_stdlib:identity(_pipe@1)
end
),
_pipe@4 = gleam@dict:insert(
_pipe@2,
<<"keyfile"/utf8>>,
begin
_pipe@3 = erlang:element(3, Cert),
gleam_stdlib:identity(_pipe@3)
end
),
(fun(D) -> case erlang:element(4, Cert) of
{some, Func} ->
_pipe@5 = D,
gleam@dict:insert(
_pipe@5,
<<"password"/utf8>>,
begin
_pipe@6 = Func,
kafein_ffi:unsafe_cast(_pipe@6)
end
);
none ->
D
end end)(_pipe@4) end
)},
{handshake, case erlang:element(10, Options) of
true ->
hello;
false ->
full
end},
{server_name_indication, case erlang:element(11, Options) of
{some, Name} ->
gleam_stdlib:identity(Name);
none ->
_pipe@7 = erlang:binary_to_atom(<<"disable"/utf8>>),
gleam_erlang_ffi:identity(_pipe@7)
end}],
_pipe@9 = lists:append(_pipe@8, case erlang:element(4, Options) of
{some, Cacert_file} ->
[{cacertfile, Cacert_file}];
none ->
[]
end),
_pipe@10 = lists:append(_pipe@9, case Ciphers of
[] ->
[];
_ ->
[{ciphers, Ciphers}]
end),
_pipe@11 = lists:append(_pipe@10, case erlang:element(3, Options) of
[] ->
[];
Alpn ->
[{alpn_advertised_protocols, Alpn}]
end),
_pipe@12 = lists:append(_pipe@11, case erlang:element(9, Options) of
[] ->
[];
Eccs ->
[{eccs, Eccs}]
end),
{ok, _pipe@12}
end
).
-file("src/kafein.gleam", 251).
?DOC(
" Upgrades a TCP connection to SSL connection.\n"
"\n"
" Returns an error if upgrading was failed.\n"
).
-spec wrap(wrap_options(), mug:socket()) -> {ok, ssl_socket()} |
{error, error()}.
wrap(Options, Socket) ->
gleam@result:'try'(
coerce_options(Options),
fun(Connect_options) -> kafein_ffi:wrap(Socket, Connect_options) end
).
-file("src/kafein.gleam", 262).
?DOC(
" Continue the handshake with specified wrap options.\n"
"\n"
" Returns an error if handshake was failed.\n"
).
-spec handshake_continue(wrap_options(), ssl_socket(), integer()) -> {ok,
ssl_socket()} |
{error, error()}.
handshake_continue(Options, Socket, Timeout) ->
gleam@result:'try'(
coerce_options(Options),
fun(Handshake_options) ->
ssl:handshake_continue(Socket, Handshake_options, Timeout)
end
).