Skip to content

Commit

Permalink
Also optimize signing
Browse files Browse the repository at this point in the history
  • Loading branch information
josevalim committed Oct 6, 2023
1 parent 8bd5997 commit 92a9885
Showing 1 changed file with 20 additions and 43 deletions.
63 changes: 20 additions & 43 deletions lib/plug/crypto/message_verifier.ex
Original file line number Diff line number Diff line change
Expand Up @@ -36,61 +36,38 @@ defmodule Plug.Crypto.MessageVerifier do

## Signature Algorithms

defp hmac_sha2_to_protected(:sha256), do: "HS256"
defp hmac_sha2_to_protected(:sha384), do: "HS384"
defp hmac_sha2_to_protected(:sha512), do: "HS512"
defp hmac_sha2_to_protected(:sha256), do: "SFMyNTY"
defp hmac_sha2_to_protected(:sha384), do: "SFMzODQ"
defp hmac_sha2_to_protected(:sha512), do: "SFM1MTI"

defp hmac_sha2_to_digest_type("HS256"), do: :sha256
defp hmac_sha2_to_digest_type("HS384"), do: :sha384
defp hmac_sha2_to_digest_type("HS512"), do: :sha512
defp hmac_sha2_to_digest_type("SFMyNTY"), do: :sha256
defp hmac_sha2_to_digest_type("SFMzODQ"), do: :sha384
defp hmac_sha2_to_digest_type("SFM1MTI"), do: :sha512

defp hmac_sha2_sign(payload, key, digest_type) do
protected = hmac_sha2_to_protected(digest_type)
plain_text = signing_input(protected, payload)
plain_text = [protected, ?., Base.url_encode64(payload, padding: false)]
signature = :crypto.mac(:hmac, digest_type, key, plain_text)
encode_token(plain_text, signature)
IO.iodata_to_binary([plain_text, ".", Base.url_encode64(signature, padding: false)])
end

defp hmac_sha2_verify(signed, key) when is_binary(signed) and is_binary(key) do
case decode_token(signed) do
{protected, payload, plain_text, signature} when protected in ["HS256", "HS384", "HS512"] ->
digest_type = hmac_sha2_to_digest_type(protected)
challenge = :crypto.mac(:hmac, digest_type, key, plain_text)

if Plug.Crypto.secure_compare(challenge, signature) do
{:ok, payload}
else
:error
end

_ ->
:error
end
end

## Helpers

defp encode_token(plain_text, signature)
when is_binary(plain_text) and is_binary(signature) do
plain_text <> "." <> Base.url_encode64(signature, padding: false)
end

defp decode_token(token) do
with [protected, payload, signature] <- String.split(token, ".", parts: 3),
with [protected, payload, signature] when protected in ["SFMyNTY", "SFMzODQ", "SFM1MTI"] <-
:binary.split(signed, ".", [:global]),
plain_text = [protected, ?., payload],
{:ok, protected} <- Base.url_decode64(protected, padding: false),
{:ok, payload} <- Base.url_decode64(payload, padding: false),
{:ok, signature} <- Base.url_decode64(signature, padding: false) do
{protected, payload, plain_text, signature}
digest_type = hmac_sha2_to_digest_type(protected)
challenge = :crypto.mac(:hmac, digest_type, key, plain_text)

if Plug.Crypto.secure_compare(challenge, signature) do
{:ok, payload}
else
:error
end
else
_ -> :error
_ ->
:error
end
end

defp signing_input(protected, payload) when is_binary(protected) and is_binary(payload) do
protected
|> Base.url_encode64(padding: false)
|> Kernel.<>(".")
|> Kernel.<>(Base.url_encode64(payload, padding: false))
end
end

0 comments on commit 92a9885

Please sign in to comment.