-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, client_final/4, parse_server_first/2, parse_server_final/1]). -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", 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( <>, 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 ). -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 <> -> 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.