-module(aws4_request). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/aws4_request.gleam"). -export([signer/4, with_region/2, with_service/2, with_session_token/2, with_date_time/2, sign_bits/2, sign_string/2]). -export_type([signer/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 signer() :: {signer, gleam@option:option({{integer(), integer(), integer()}, {integer(), integer(), integer()}}), binary(), binary(), binary(), binary(), gleam@option:option(binary())}. -file("src/aws4_request.gleam", 28). ?DOC(" Create a new request signer for the given credentials, service, and region.\n"). -spec signer(binary(), binary(), binary(), binary()) -> signer(). signer(Access_key_id, Secret_access_key, Region, Service) -> {signer, none, Access_key_id, Secret_access_key, Region, Service, none}. -file("src/aws4_request.gleam", 44). -spec with_region(signer(), binary()) -> signer(). with_region(Signer, Region) -> {signer, erlang:element(2, Signer), erlang:element(3, Signer), erlang:element(4, Signer), Region, erlang:element(6, Signer), erlang:element(7, Signer)}. -file("src/aws4_request.gleam", 48). -spec with_service(signer(), binary()) -> signer(). with_service(Signer, Service) -> {signer, erlang:element(2, Signer), erlang:element(3, Signer), erlang:element(4, Signer), erlang:element(5, Signer), Service, erlang:element(7, Signer)}. -file("src/aws4_request.gleam", 55). ?DOC( " Set a session token when using temporary security credentials.\n" " This sets the `x-amz-security-token` header.\n" ). -spec with_session_token(signer(), binary()) -> signer(). with_session_token(Signer, Session_token) -> {signer, erlang:element(2, Signer), erlang:element(3, Signer), erlang:element(4, Signer), erlang:element(5, Signer), erlang:element(6, Signer), {some, Session_token}}. -file("src/aws4_request.gleam", 62). ?DOC( " Set a specific time to use for request signing, overriding the default\n" " behaviour of using the current time.\n" ). -spec with_date_time( signer(), {{integer(), integer(), integer()}, {integer(), integer(), integer()}} ) -> signer(). with_date_time(Signer, Date_time) -> {signer, {some, Date_time}, erlang:element(3, Signer), erlang:element(4, Signer), erlang:element(5, Signer), erlang:element(6, Signer), erlang:element(7, Signer)}. -file("src/aws4_request.gleam", 223). ?DOC(" AWS SigV4 requires query params to be sorted and URI encoded\n"). -spec encode_query_string(gleam@option:option(binary())) -> binary(). encode_query_string(Query) -> _pipe = Query, _pipe@1 = gleam@option:unwrap(_pipe, <<""/utf8>>), _pipe@2 = gleam_stdlib:parse_query(_pipe@1), _pipe@3 = gleam@result:unwrap(_pipe@2, []), _pipe@4 = gleam@list:sort( _pipe@3, fun(A, B) -> gleam@string:compare(erlang:element(1, A), erlang:element(1, B)) end ), gleam@uri:query_to_string(_pipe@4). -file("src/aws4_request.gleam", 215). ?DOC(" URI encodes the path while preserving path separators\n"). -spec uri_encode(binary()) -> binary(). uri_encode(Path) -> _pipe = Path, _pipe@1 = gleam@string:split(_pipe, <<"/"/utf8>>), _pipe@2 = gleam@list:map(_pipe@1, fun gleam_stdlib:percent_encode/1), gleam@string:join(_pipe@2, <<"/"/utf8>>). -file("src/aws4_request.gleam", 205). -spec get_canonical_path(binary()) -> binary(). get_canonical_path(Path) -> case Path of <<""/utf8>> -> <<"/"/utf8>>; _ -> Path end. -file("src/aws4_request.gleam", 237). -spec add_session_token_header(list({binary(), ERN}), gleam@option:option(ERN)) -> list({binary(), ERN}). add_session_token_header(Headers, Maybe_session_token) -> case Maybe_session_token of none -> Headers; {some, Session_token} -> gleam@list:prepend( Headers, {<<"x-amz-security-token"/utf8>>, Session_token} ) end. -file("src/aws4_request.gleam", 233). -spec now() -> {{integer(), integer(), integer()}, {integer(), integer(), integer()}}. now() -> _pipe = os:system_time(1000), calendar:system_time_to_universal_time(_pipe, 1000). -file("src/aws4_request.gleam", 77). ?DOC(" Sign a request that has a bit array body.\n"). -spec sign_bits(signer(), gleam@http@request:request(bitstring())) -> gleam@http@request:request(bitstring()). sign_bits(Signer, Request) -> Payload_hash = string:lowercase( gleam_stdlib:base16_encode( gleam@crypto:hash(sha256, erlang:element(4, Request)) ) ), {{Year, Month, Day}, {Hour, Minute, Second}} = gleam@option:lazy_unwrap( erlang:element(2, Signer), fun now/0 ), Date = erlang:list_to_binary( [gleam@string:pad_start(erlang:integer_to_binary(Year), 4, <<"0"/utf8>>), gleam@string:pad_start( erlang:integer_to_binary(Month), 2, <<"0"/utf8>> ), gleam@string:pad_start( erlang:integer_to_binary(Day), 2, <<"0"/utf8>> )] ), Date_time = erlang:list_to_binary( [Date, <<"T"/utf8>>, gleam@string:pad_start( erlang:integer_to_binary(Hour), 2, <<"0"/utf8>> ), gleam@string:pad_start( erlang:integer_to_binary(Minute), 2, <<"0"/utf8>> ), gleam@string:pad_start( erlang:integer_to_binary(Second), 2, <<"0"/utf8>> ), <<"Z"/utf8>>] ), Request@1 = gleam@http@request:set_header( Request, <<"host"/utf8>>, case erlang:element(7, Request) of none -> erlang:element(6, Request); {some, Port} -> <<<<(erlang:element(6, Request))/binary, ":"/utf8>>/binary, (erlang:integer_to_binary(Port))/binary>> end ), Method = string:uppercase( gleam@http:method_to_string(erlang:element(2, Request@1)) ), Headers = begin _pipe = erlang:element(3, Request@1), _pipe@1 = add_session_token_header(_pipe, erlang:element(7, Signer)), _pipe@2 = gleam@list:prepend( _pipe@1, {<<"x-amz-date"/utf8>>, Date_time} ), _pipe@3 = gleam@list:prepend( _pipe@2, {<<"x-amz-content-sha256"/utf8>>, Payload_hash} ), _pipe@4 = gleam@list:map( _pipe@3, fun(Header) -> {string:lowercase(erlang:element(1, Header)), erlang:element(2, Header)} end ), gleam@list:sort( _pipe@4, fun(A, B) -> gleam@string:compare(erlang:element(1, A), erlang:element(1, B)) end ) end, Header_names = begin _pipe@5 = Headers, _pipe@6 = gleam@list:map(_pipe@5, fun(H) -> erlang:element(1, H) end), gleam@string:join(_pipe@6, <<";"/utf8>>) end, Canonical_uri = begin _pipe@7 = get_canonical_path(erlang:element(8, Request@1)), uri_encode(_pipe@7) end, Canonical_query = encode_query_string(erlang:element(9, Request@1)), Canonical_request = erlang:list_to_binary( [Method, <<"\n"/utf8>>, Canonical_uri, <<"\n"/utf8>>, Canonical_query, <<"\n"/utf8>>, begin _pipe@8 = Headers, _pipe@9 = gleam@list:map( _pipe@8, fun(H@1) -> <<<<(erlang:element(1, H@1))/binary, ":"/utf8>>/binary, (gleam@string:trim(erlang:element(2, H@1)))/binary>> end ), gleam@string:join(_pipe@9, <<"\n"/utf8>>) end, <<"\n"/utf8>>, <<"\n"/utf8>>, Header_names, <<"\n"/utf8>>, Payload_hash] ), Scope = erlang:list_to_binary( [Date, <<"/"/utf8>>, erlang:element(5, Signer), <<"/"/utf8>>, erlang:element(6, Signer), <<"/aws4_request"/utf8>>] ), To_sign = erlang:list_to_binary( [<<"AWS4-HMAC-SHA256"/utf8>>, <<"\n"/utf8>>, Date_time, <<"\n"/utf8>>, Scope, <<"\n"/utf8>>, string:lowercase( gleam_stdlib:base16_encode( gleam@crypto:hash(sha256, <>) ) )] ), Key = begin _pipe@10 = <<"AWS4"/utf8, (erlang:element(4, Signer))/binary>>, _pipe@11 = gleam_crypto_ffi:hmac(<>, sha256, _pipe@10), _pipe@12 = gleam_crypto_ffi:hmac( <<(erlang:element(5, Signer))/binary>>, sha256, _pipe@11 ), _pipe@13 = gleam_crypto_ffi:hmac( <<(erlang:element(6, Signer))/binary>>, sha256, _pipe@12 ), gleam_crypto_ffi:hmac(<<"aws4_request"/utf8>>, sha256, _pipe@13) end, Signature = begin _pipe@14 = <>, _pipe@15 = gleam_crypto_ffi:hmac(_pipe@14, sha256, Key), _pipe@16 = gleam_stdlib:base16_encode(_pipe@15), string:lowercase(_pipe@16) end, Authorization = erlang:list_to_binary( [<<"AWS4-HMAC-SHA256 Credential="/utf8>>, erlang:element(3, Signer), <<"/"/utf8>>, Scope, <<<<",SignedHeaders="/utf8, Header_names/binary>>/binary, ",Signature="/utf8>>, Signature] ), Headers@1 = [{<<"authorization"/utf8>>, Authorization} | Headers], {request, erlang:element(2, Request@1), Headers@1, erlang:element(4, Request@1), erlang:element(5, Request@1), erlang:element(6, Request@1), erlang:element(7, Request@1), erlang:element(8, Request@1), erlang:element(9, Request@1)}. -file("src/aws4_request.gleam", 68). ?DOC(" Sign a request that has a string body.\n"). -spec sign_string(signer(), gleam@http@request:request(binary())) -> gleam@http@request:request(bitstring()). sign_string(Signer, Request) -> sign_bits( Signer, gleam@http@request:map(Request, fun gleam_stdlib:identity/1) ).