Packages
gleam_mongo
0.8.0
0.18.1
retired
0.18.0
retired
0.17.0
retired
0.16.0
retired
0.15.1
retired
0.15.0
retired
0.14.0
retired
0.13.0
retired
0.12.0
retired
0.11.0
retired
0.10.0
retired
0.9.0
retired
0.8.0
retired
0.7.0
retired
0.6.0
retired
0.5.0
retired
0.4.0
retired
0.3.0
0.2.0
retired
0.1.3
retired
0.1.2
retired
0.1.1
retired
0.1.0
retired
A mongodb driver for gleam
Retired package: Renamed - Republished as mungo
Current section
Files
Jump to
Current section
Files
src/mongo@scram.erl
-module(mongo@scram).
-compile(no_auto_import).
-export([first_payload/1, first_message/1, parse_first_reply/1, second_message/5, parse_second_reply/2, hi/3]).
-spec first_payload(binary()) -> binary().
first_payload(Username) ->
Nonce = begin
_pipe = gleam@crypto:strong_random_bytes(24),
gleam@base:encode64(_pipe, true)
end,
_pipe@2 = [<<"n="/utf8>>,
begin
_pipe@1 = Username,
clean_username(_pipe@1)
end,
<<",r="/utf8>>,
Nonce],
gleam@string:concat(_pipe@2).
-spec first_message(binary()) -> bson@types:value().
first_message(Payload) ->
Payload@1 = begin
_pipe = [<<"n,,"/utf8>>, Payload],
_pipe@1 = gleam@string:concat(_pipe),
bson@generic:from_string(_pipe@1)
end,
{document,
[{<<"saslStart"/utf8>>, {boolean, true}},
{<<"mechanism"/utf8>>, {str, <<"SCRAM-SHA-256"/utf8>>}},
{<<"payload"/utf8>>, {binary, {generic, Payload@1}}},
{<<"autoAuthorize"/utf8>>, {boolean, true}},
{<<"options"/utf8>>,
{document, [{<<"skipEmptyExchange"/utf8>>, {boolean, true}}]}}]}.
-spec parse_first_reply(list({binary(), bson@types:value()})) -> {ok,
{{binary(),
binary(),
integer()},
binary(),
integer()}} |
{error, nil}.
parse_first_reply(Reply) ->
case Reply of
[{<<"ok"/utf8>>, {double, 0.0}} | _@1] ->
{error, nil};
[{<<"conversationId"/utf8>>, {integer, Cid}},
{<<"done"/utf8>>, {boolean, false}},
{<<"payload"/utf8>>, {binary, {generic, Data}}},
{<<"ok"/utf8>>, {double, 1.0}}] ->
case begin
_pipe = Data,
bson@generic:to_string(_pipe)
end of
{error, _try} -> {error, _try};
{ok, Data@1} ->
case begin
_pipe@1 = Data@1,
parse_payload(_pipe@1)
end of
{error, _try@1} -> {error, _try@1};
{ok, [{<<"r"/utf8>>, Rnonce},
{<<"s"/utf8>>, Salt},
{<<"i"/utf8>>, I}]} ->
case begin
_pipe@2 = I,
gleam@int:parse(_pipe@2)
end of
{ok, Iterations} ->
case Iterations >= 4096 of
true ->
{ok,
{{Rnonce, Salt, Iterations},
Data@1,
Cid}};
false ->
{error, nil}
end;
{error, nil} ->
{error, nil}
end
end
end
end.
-spec second_message(
{binary(), binary(), integer()},
binary(),
binary(),
integer(),
binary()
) -> {ok, {bson@types:value(), bitstring()}} | {error, nil}.
second_message(Server_params, First_payload, Server_payload, Cid, Password) ->
{Rnonce, Salt, Iterations} = Server_params,
case begin
_pipe = Salt,
gleam@base:decode64(_pipe)
end of
{error, _try} -> {error, _try};
{ok, Salt@1} ->
Salted_password = hi(Password, Salt@1, Iterations),
Client_key = gleam@crypto:hmac(
begin
_pipe@1 = <<"Client Key"/utf8>>,
gleam@bit_string:from_string(_pipe@1)
end,
sha256,
Salted_password
),
Server_key = gleam@crypto:hmac(
begin
_pipe@2 = <<"Server Key"/utf8>>,
gleam@bit_string:from_string(_pipe@2)
end,
sha256,
Salted_password
),
Stored_key = gleam@crypto:hash(sha256, Client_key),
Auth_message = begin
_pipe@3 = [First_payload,
<<","/utf8>>,
Server_payload,
<<",c=biws,r="/utf8>>,
Rnonce],
_pipe@4 = gleam@string:concat(_pipe@3),
bson@generic:from_string(_pipe@4)
end,
Client_signature = gleam@crypto:hmac(
begin
_pipe@5 = Auth_message,
bson@generic:to_bit_string(_pipe@5)
end,
sha256,
Stored_key
),
Second_payload = begin
_pipe@7 = [<<"c=biws,r="/utf8>>,
Rnonce,
<<",p="/utf8>>,
begin
_pipe@6 = 'xor'(
Client_key,
Client_signature,
<<>>
),
gleam@base:encode64(_pipe@6, true)
end],
_pipe@8 = gleam@string:concat(_pipe@7),
bson@generic:from_string(_pipe@8)
end,
Server_signature = gleam@crypto:hmac(
begin
_pipe@9 = Auth_message,
bson@generic:to_bit_string(_pipe@9)
end,
sha256,
Server_key
),
{ok,
{{document,
[{<<"saslContinue"/utf8>>, {boolean, true}},
{<<"conversationId"/utf8>>, {integer, Cid}},
{<<"payload"/utf8>>, {binary, {generic, Second_payload}}}]},
Server_signature}}
end.
-spec parse_second_reply(list({binary(), bson@types:value()}), bitstring()) -> {ok,
nil} |
{error, nil}.
parse_second_reply(Reply, Server_signature) ->
case Reply of
[{<<"ok"/utf8>>, {double, 0.0}} | _@1] ->
{error, nil};
[{<<"conversationId"/utf8>>, _@2},
{<<"done"/utf8>>, {boolean, true}},
{<<"payload"/utf8>>, {binary, {generic, Data}}},
{<<"ok"/utf8>>, {double, 1.0}}] ->
case begin
_pipe = Data,
bson@generic:to_string(_pipe)
end of
{error, _try} -> {error, _try};
{ok, Data@1} ->
case parse_payload(Data@1) of
{error, _try@1} -> {error, _try@1};
{ok, [{<<"v"/utf8>>, Data@2}]} ->
case gleam@base:decode64(Data@2) of
{error, _try@2} -> {error, _try@2};
{ok, Received_signature} ->
case (gleam@bit_string:byte_size(
Server_signature
)
=:= gleam@bit_string:byte_size(
Received_signature
))
andalso gleam@crypto:secure_compare(
Server_signature,
Received_signature
) of
true ->
{ok, nil};
false ->
{error, nil}
end
end
end
end
end.
-spec parse_payload(binary()) -> {ok, list({binary(), binary()})} | {error, nil}.
parse_payload(Payload) ->
_pipe = Payload,
_pipe@1 = gleam@string:split(_pipe, <<","/utf8>>),
gleam@list:try_map(
_pipe@1,
fun(Item) ->
_pipe@2 = Item,
gleam@string:split_once(_pipe@2, <<"="/utf8>>)
end
).
-spec clean_username(binary()) -> binary().
clean_username(Username) ->
_pipe = Username,
_pipe@1 = gleam@string:replace(_pipe, <<"="/utf8>>, <<"=3D"/utf8>>),
gleam@string:replace(_pipe@1, <<","/utf8>>, <<"=2C"/utf8>>).
-spec hi(binary(), bitstring(), integer()) -> bitstring().
hi(Password, Salt, Iterations) ->
crypto:pbkdf2_hmac(sha256, Password, Salt, Iterations, 32).
-spec 'xor'(bitstring(), bitstring(), bitstring()) -> bitstring().
'xor'(A, B, Storage) ->
<<Fa, Ra/bitstring>> = A,
<<Fb, Rb/bitstring>> = B,
New_storage = begin
_pipe = [Storage, <<(gleam@bitwise:exclusive_or(Fa, Fb))>>],
gleam@bit_string:concat(_pipe)
end,
case [Ra, Rb] of
[<<>>, <<>>] ->
New_storage;
_@1 ->
'xor'(Ra, Rb, New_storage)
end.