Current section

Files

Jump to
pgl src pgl@internal@scram.erl
Raw

src/pgl@internal@scram.erl

-module(pgl@internal@scram).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/pgl/internal/scram.gleam").
-export([client_first/2, get_nonce/1, parse_server_first/2, parse_server_final/1, client_final/4]).
-export_type([server_first/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.
?MODULEDOC(false).
-type server_first() :: {server_first,
bitstring(),
bitstring(),
integer(),
bitstring()}.
-file("src/pgl/internal/scram.gleam", 26).
?DOC(false).
-spec escape_username(bitstring()) -> {ok, bitstring()} |
{error, pgl@internal:internal_error()}.
escape_username(User) ->
_pipe = User,
_pipe@1 = gleam@bit_array:to_string(_pipe),
_pipe@3 = gleam@result:replace_error(
_pipe@1,
begin
_pipe@2 = sasl_client_first,
{protocol_error, _pipe@2, <<"Invalid username"/utf8>>}
end
),
gleam@result:map(_pipe@3, fun(User@1) -> _pipe@4 = User@1,
_pipe@5 = gleam@string:replace(
_pipe@4,
<<"="/utf8>>,
<<"=3D"/utf8>>
),
_pipe@6 = gleam@string:replace(
_pipe@5,
<<","/utf8>>,
<<"=2C"/utf8>>
),
gleam_stdlib:identity(_pipe@6) end).
-file("src/pgl/internal/scram.gleam", 16).
?DOC(false).
-spec client_first(bitstring(), bitstring()) -> {ok, bitstring()} |
{error, pgl@internal:internal_error()}.
client_first(User, Nonce) ->
gleam@result:map(
escape_username(User),
fun(Escaped) ->
<<"n,,n="/utf8, Escaped/bitstring, ",r="/utf8, Nonce/bitstring>>
end
).
-file("src/pgl/internal/scram.gleam", 41).
?DOC(false).
-spec get_nonce(integer()) -> bitstring().
get_nonce(Num_random_bytes) ->
_pipe = crypto:strong_rand_bytes(Num_random_bytes),
_pipe@1 = gleam_stdlib:base64_encode(_pipe, true),
gleam_stdlib:identity(_pipe@1).
-file("src/pgl/internal/scram.gleam", 112).
?DOC(false).
-spec parse_server_first(bitstring(), bitstring()) -> {ok, server_first()} |
{error, pgl@internal:internal_error()}.
parse_server_first(Server_first, Client_nonce) ->
Parts = begin
_pipe = gleam@bit_array:to_string(Server_first),
_pipe@1 = gleam@result:map(
_pipe,
fun(_capture) -> gleam@string:split(_capture, <<","/utf8>>) end
),
gleam@result:unwrap(_pipe@1, [])
end,
_pipe@2 = case Parts of
[<<"r="/utf8, Nonce/binary>>,
<<"s="/utf8, Salt/binary>>,
<<"i="/utf8, Iters/binary>>] ->
Nonce@1 = gleam_stdlib:identity(Nonce),
gleam@result:'try'(
gleam@bit_array:base64_decode(Salt),
fun(Salt@1) ->
gleam@result:'try'(
gleam_stdlib:parse_int(Iters),
fun(Iterations) ->
gleam@bool:guard(
(Iterations < 4096) orelse (Iterations > 100000),
{error, nil},
fun() ->
Size = erlang:bit_size(Client_nonce),
case Nonce@1 of
<<Prefix:Size/bitstring, _/bitstring>> ->
case Prefix =:= Client_nonce of
true ->
{ok,
{server_first,
Nonce@1,
Salt@1,
Iterations,
Server_first}};
false ->
{error, nil}
end;
_ ->
{error, nil}
end
end
)
end
)
end
);
_ ->
{error, nil}
end,
gleam@result:map_error(
_pipe@2,
fun(_) ->
{protocol_error,
sasl_server_first,
<<"Failed to parse server_first"/utf8>>}
end
).
-file("src/pgl/internal/scram.gleam", 152).
?DOC(false).
-spec parse_server_final(bitstring()) -> {ok, bitstring()} |
{error, pgl@internal:internal_error()}.
parse_server_final(Server_final) ->
case Server_final of
<<"v="/utf8, Final/bitstring>> ->
_pipe = gleam@bit_array:to_string(Final),
_pipe@1 = gleam@result:map(
_pipe,
fun(_capture) -> gleam@string:split(_capture, <<","/utf8>>) end
),
_pipe@2 = gleam@result:'try'(_pipe@1, fun gleam@list:first/1),
_pipe@3 = gleam@result:'try'(
_pipe@2,
fun gleam@bit_array:base64_decode/1
),
gleam@result:map_error(
_pipe@3,
fun(_) ->
{protocol_error,
sasl_server_final,
<<"Failed to parse server_final"/utf8>>}
end
);
<<"e="/utf8, Error/bitstring>> ->
Error_value = begin
_pipe@4 = gleam@bit_array:to_string(Error),
gleam@result:unwrap(_pipe@4, <<""/utf8>>)
end,
_pipe@5 = {protocol_error,
sasl_server_error,
<<<<"Server error: '"/utf8, Error_value/binary>>/binary,
"'"/utf8>>},
{error, _pipe@5};
_ ->
_pipe@6 = {protocol_error,
sasl_server_final,
<<"Unexpected SASL server final payload"/utf8>>},
{error, _pipe@6}
end.
-file("src/pgl/internal/scram.gleam", 196).
?DOC(false).
-spec do_hi(bitstring(), bitstring(), bitstring(), integer()) -> bitstring().
do_hi(Str, U, Hi, I) ->
case I > 0 of
false ->
Hi;
true ->
U2 = gleam_crypto_ffi:hmac(U, sha256, Str),
Hi1 = crypto:exor(Hi, U2),
do_hi(Str, U2, Hi1, I - 1)
end.
-file("src/pgl/internal/scram.gleam", 190).
?DOC(false).
-spec hi(bitstring(), bitstring(), integer()) -> bitstring().
hi(Str, Salt, I) ->
U1 = gleam_crypto_ffi:hmac(
<<Salt/bitstring, 1:32/integer-big>>,
sha256,
Str
),
do_hi(Str, U1, U1, I - 1).
-file("src/pgl/internal/scram.gleam", 47).
?DOC(false).
-spec client_final(server_first(), bitstring(), bitstring(), bitstring()) -> {ok,
{bitstring(), bitstring()}} |
{error, pgl@internal:internal_error()}.
client_final(Server_first, Client_nonce, Username, Password) ->
Channel_binding = <<"c=biws"/utf8>>,
Nonce = [<<"r="/utf8>>, erlang:element(2, Server_first)],
_pipe = Password,
_pipe@1 = pgl@internal@sasl:validate(_pipe),
_pipe@3 = gleam@result:replace_error(
_pipe@1,
begin
_pipe@2 = sasl_client_final,
{protocol_error, _pipe@2, <<"Invalid password"/utf8>>}
end
),
gleam@result:'try'(
_pipe@3,
fun(Valid_password) ->
Salted_password = begin
_pipe@4 = Valid_password,
hi(
_pipe@4,
erlang:element(3, Server_first),
erlang:element(4, Server_first)
)
end,
Client_key = gleam_crypto_ffi:hmac(
<<"Client Key"/utf8>>,
sha256,
Salted_password
),
gleam@result:map(
escape_username(Username),
fun(Escaped_username) ->
Auth_message = begin
_pipe@5 = <<"n="/utf8,
Escaped_username/bitstring,
",r="/utf8,
Client_nonce/bitstring>>,
_pipe@6 = gleam@bytes_tree:from_bit_array(_pipe@5),
_pipe@7 = gleam@bytes_tree:append(_pipe@6, <<","/utf8>>),
_pipe@8 = gleam@bytes_tree:append(
_pipe@7,
erlang:element(5, Server_first)
),
_pipe@9 = gleam@bytes_tree:append(_pipe@8, <<","/utf8>>),
_pipe@10 = gleam@bytes_tree:append(
_pipe@9,
Channel_binding
),
_pipe@11 = gleam@bytes_tree:append(
_pipe@10,
<<","/utf8>>
),
_pipe@12 = gleam@list:fold(
Nonce,
_pipe@11,
fun gleam@bytes_tree:append/2
),
erlang:list_to_bitstring(_pipe@12)
end,
Client_signature = begin
_pipe@13 = sha256,
_pipe@14 = gleam@crypto:hash(_pipe@13, Client_key),
gleam_crypto_ffi:hmac(Auth_message, sha256, _pipe@14)
end,
Encoded_client_proof = begin
_pipe@15 = Client_key,
_pipe@16 = crypto:exor(_pipe@15, Client_signature),
_pipe@17 = gleam_stdlib:base64_encode(_pipe@16, true),
gleam_stdlib:identity(_pipe@17)
end,
Server_signature = begin
_pipe@18 = Salted_password,
_pipe@19 = gleam_crypto_ffi:hmac(
<<"Server Key"/utf8>>,
sha256,
_pipe@18
),
gleam_crypto_ffi:hmac(Auth_message, sha256, _pipe@19)
end,
Encoded_client_final = begin
_pipe@20 = gleam@bytes_tree:new(),
_pipe@21 = gleam@bytes_tree:append(
_pipe@20,
Channel_binding
),
_pipe@22 = gleam@bytes_tree:append(
_pipe@21,
<<","/utf8>>
),
_pipe@23 = gleam@list:fold(
Nonce,
_pipe@22,
fun gleam@bytes_tree:append/2
),
_pipe@24 = gleam@bytes_tree:append(
_pipe@23,
<<",p="/utf8>>
),
_pipe@25 = gleam@bytes_tree:append(
_pipe@24,
Encoded_client_proof
),
erlang:list_to_bitstring(_pipe@25)
end,
{Encoded_client_final, Server_signature}
end
)
end
).