Skip to content

Commit

Permalink
Mix format & update docs deps
Browse files Browse the repository at this point in the history
  • Loading branch information
Eduardo committed Aug 4, 2021
1 parent ca42a54 commit 8201187
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 92 deletions.
42 changes: 25 additions & 17 deletions lib/signaturex.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
defmodule Signaturex do
alias Signaturex.CryptoHelper
alias Signaturex.Time

defmodule AuthenticationError do
defexception message: "Error on authentication"
end
Expand All @@ -10,7 +11,7 @@ defmodule Signaturex do
Raises an AuthenticationError if the request is invalid
"""
@spec validate!(binary, binary, binary | atom, binary, Map.t, integer) :: true
@spec validate!(binary, binary, binary | atom, binary, map, integer) :: true
def validate!(key, secret, method, path, params, timestamp_grace \\ 600) do
validate_version!(params["auth_version"])
validate_timestamp!(params["auth_timestamp"], timestamp_grace)
Expand All @@ -25,7 +26,7 @@ defmodule Signaturex do
Returns true or false
"""
@spec validate(binary, binary, binary | atom, binary, Map.t, integer) :: boolean
@spec validate(binary, binary, binary | atom, binary, map, integer) :: boolean
def validate(key, secret, method, path, params, timestamp_grace \\ 600) do
try do
validate!(key, secret, method, path, params, timestamp_grace)
Expand All @@ -35,41 +36,49 @@ defmodule Signaturex do
end

defp validate_version!("1.0"), do: true

defp validate_version!(_) do
raise AuthenticationError, message: "Invalid auth version"
end

defp validate_timestamp!(_, nil), do: true

defp validate_timestamp!(nil, _timestamp_grace) do
raise AuthenticationError, message: "Timestamp missing"
end

defp validate_timestamp!(timestamp, timestamp_grace) when is_binary(timestamp) do
timestamp = timestamp |> String.to_charlist |> List.to_integer
timestamp = timestamp |> String.to_charlist() |> List.to_integer()
validate_timestamp!(timestamp, timestamp_grace)
end

defp validate_timestamp!(timestamp, timestamp_grace) when is_integer(timestamp) do
if abs(Time.stamp - timestamp) < timestamp_grace do
if abs(Time.stamp() - timestamp) < timestamp_grace do
true
else
raise AuthenticationError, message: "Auth timestamp expired"
end
end

defp validate_key!(_, nil) do
raise AuthenticationError, message: "Auth key missing"
raise AuthenticationError, message: "Auth key missing"
end

defp validate_key!(key, key), do: true

defp validate_key!(_key, _auth_key) do
raise AuthenticationError, message: "Invalid auth key"
end

defp validate_signature!(_secret, _method, _path, _params, nil) do
raise AuthenticationError, message: "Auth signature missing"
end

defp validate_signature!(secret, method, path, params, auth_signature) do
params = build_params(params)
signature = auth_signature(secret, method, path, params)
if CryptoHelper.identical?(signature, auth_signature) do

if CryptoHelper.identical?(signature, auth_signature) do
true
else
raise AuthenticationError, message: "Invalid auth signature"
Expand All @@ -80,7 +89,7 @@ defmodule Signaturex do
Sign a request using `key`, `secret`, HTTP `method`,
query string `params` and an optional body
"""
@spec sign(binary, binary, binary | atom, binary, Map.t) :: Map.t
@spec sign(binary, binary, binary | atom, binary, map) :: map
def sign(key, secret, method, path, params) do
auth_data = auth_data(key)
params = build_params(params)
Expand All @@ -90,36 +99,35 @@ defmodule Signaturex do
end

defp auth_signature(secret, method, path, params) do
method = method |> to_string |> String.upcase
method = method |> to_string |> String.upcase()
to_sign = "#{method}\n#{path}\n#{encode_query(params)}"
CryptoHelper.hmac256_to_string(secret, to_sign)
end

defp encode_query(params) do
params
|> Enum.into([])
|> Enum.sort(fn({k1, _}, {k2, _}) -> k1 <= k2 end)
|> URI.encode_query
|> Enum.into([])
|> Enum.sort(fn {k1, _}, {k2, _} -> k1 <= k2 end)
|> URI.encode_query()
end

defp build_params(params) do
for {k, v} <- params, into: %{} do
k = k |> to_string |> String.downcase
k = k |> to_string |> String.downcase()
{k, v}
end |> Map.delete("auth_signature")
end
|> Map.delete("auth_signature")
end

defp auth_data(app_key) do
%{ "auth_key" => app_key,
"auth_timestamp" => Time.stamp,
"auth_version" => "1.0" }
%{"auth_key" => app_key, "auth_timestamp" => Time.stamp(), "auth_version" => "1.0"}
end

defmodule Time do
@spec stamp :: non_neg_integer
def stamp do
{mega, sec, _micro} = :os.timestamp()
mega * 1000000 + sec
mega * 1_000_000 + sec
end
end
end
31 changes: 20 additions & 11 deletions lib/signaturex/crypto_helper.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ defmodule Signaturex.CryptoHelper do
def hmac256_to_string(app_secret, to_sign) do
hmac(:sha256, app_secret, to_sign)
|> hexlify
|> :string.to_lower
|> List.to_string
|> :string.to_lower()
|> List.to_string()
end

@doc """
Expand All @@ -18,23 +18,32 @@ defmodule Signaturex.CryptoHelper do
data
|> md5
|> hexlify
|> :string.to_lower
|> List.to_string
|> :string.to_lower()
|> List.to_string()
end

@doc """
Constant time string comparison
"""
def identical?(<<>>, false), do: false
def identical?(<<>>, <<>>), do: true
def identical?(<<>>, false), do: false
def identical?(<<>>, <<>>), do: true
def identical?(string1, string2) when byte_size(string1) != byte_size(string2), do: false
def identical?(<<_head :: binary-size(1), tail :: binary>>, false), do: identical?(tail, false)
def identical?(<<head :: binary-size(1), tail1 :: binary>>, <<head :: binary-size(1), tail2 :: binary>>), do:
identical?(tail1, tail2)
def identical?(<<_head1 :: binary-size(1), tail1 :: binary>>, <<_head2 :: binary-size(1), _tail2 :: binary>>), do:
identical?(tail1, false)
def identical?(<<_head::binary-size(1), tail::binary>>, false), do: identical?(tail, false)

def identical?(
<<head::binary-size(1), tail1::binary>>,
<<head::binary-size(1), tail2::binary>>
),
do: identical?(tail1, tail2)

def identical?(
<<_head1::binary-size(1), tail1::binary>>,
<<_head2::binary-size(1), _tail2::binary>>
),
do: identical?(tail1, false)

defp md5(data), do: :crypto.hash(:md5, data)

defp hexlify(binary) do
:lists.flatten(for b <- :erlang.binary_to_list(binary), do: :io_lib.format("~2.16.0B", [b]))
end
Expand Down
22 changes: 14 additions & 8 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,35 @@ defmodule Signaturex.Mixfile do
"""

def project do
[ app: :signaturex,
[
app: :signaturex,
name: "Signaturex",
description: @description,
elixir: "~> 1.10",
version: "1.4.0",
package: package(),
deps: deps(),
source_url: "https://github.com/edgurgel/signaturex" ]
source_url: "https://github.com/edgurgel/signaturex"
]
end

def application do
[ extra_applications: [:crypto] ]
[extra_applications: [:crypto]]
end

defp deps do
[{ :meck, "~> 0.9.2", only: :test },
{ :earmark, "~> 1.0", only: :dev },
{ :ex_doc, "~> 0.16", only: :dev }]
[
{:meck, "~> 0.9.2", only: :test},
{:earmark, "~> 1.0", only: :dev},
{:ex_doc, "~> 0.25", only: :dev}
]
end

defp package do
[ maintainers: ["Eduardo Gurgel"],
[
maintainers: ["Eduardo Gurgel"],
licenses: ["MIT"],
links: %{ "Github" => "https://github.com/edgurgel/signaturex" } ]
links: %{"Github" => "https://github.com/edgurgel/signaturex"}
]
end
end
9 changes: 7 additions & 2 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
%{
"earmark": {:hex, :earmark, "1.2.3", "206eb2e2ac1a794aa5256f3982de7a76bf4579ff91cb28d0e17ea2c9491e46a4", [:mix], [], "hexpm", "3b1dcad3067985dd8618c38399a8ee9c4e652d52a17a4aae7a6d6fc4fcc24856"},
"ex_doc": {:hex, :ex_doc, "0.16.2", "3b3e210ebcd85a7c76b4e73f85c5640c011d2a0b2f06dcdf5acdb2ae904e5084", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm", "b6fb4aef8125c62e6b6a7d7507eff70f376a7050c7745af11f08333ea9beebd3"},
"earmark": {:hex, :earmark, "1.4.15", "2c7f924bf495ec1f65bd144b355d0949a05a254d0ec561740308a54946a67888", [:mix], [{:earmark_parser, ">= 1.4.13", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "3b1209b85bc9f3586f370f7c363f6533788fb4e51db23aa79565875e7f9999ee"},
"earmark_parser": {:hex, :earmark_parser, "1.4.13", "0c98163e7d04a15feb62000e1a891489feb29f3d10cb57d4f845c405852bbef8", [:mix], [], "hexpm", "d602c26af3a0af43d2f2645613f65841657ad6efc9f0e361c3b6c06b578214ba"},
"ex_doc": {:hex, :ex_doc, "0.25.1", "4b736fa38dc76488a937e5ef2944f5474f3eff921de771b25371345a8dc810bc", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "3200b0a69ddb2028365281fbef3753ea9e728683863d8cdaa96580925c891f67"},
"makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"},
"makeup_elixir": {:hex, :makeup_elixir, "0.15.1", "b5888c880d17d1cc3e598f05cdb5b5a91b7b17ac4eaf5f297cb697663a1094dd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "db68c173234b07ab2a07f645a5acdc117b9f99d69ebf521821d89690ae6c6ec8"},
"makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"},
"meck": {:hex, :meck, "0.9.2", "85ccbab053f1db86c7ca240e9fc718170ee5bda03810a6292b5306bf31bae5f5", [:rebar3], [], "hexpm", "81344f561357dc40a8344afa53767c32669153355b626ea9fcbc8da6b3045826"},
"nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"},
}
6 changes: 4 additions & 2 deletions test/crypto_helper_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ defmodule Signaturex.CryptoHelperTest do
import Signaturex.CryptoHelper

test "hmac256_to_string" do
assert hmac256_to_string("7ad3773142a6692b25b8", "1234.1234:private-foobar") == "58df8b0c36d6982b82c3ecf6b4662e34fe8c25bba48f5369f135bf843651c3a4"
assert hmac256_to_string("7ad3773142a6692b25b8", "1234.1234:private-foobar") ==
"58df8b0c36d6982b82c3ecf6b4662e34fe8c25bba48f5369f135bf843651c3a4"
end

test "md5_to_string" do
assert md5_to_string("The quick brown fox jumps over the lazy dog") == "9e107d9d372bb6826bd81d3542a419d6"
assert md5_to_string("The quick brown fox jumps over the lazy dog") ==
"9e107d9d372bb6826bd81d3542a419d6"
end

test "constant time string compare" do
Expand Down
Loading

0 comments on commit 8201187

Please sign in to comment.