zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

message_verifier.ex (3467B)


      1 defmodule Plug.Crypto.MessageVerifier do
      2   @moduledoc """
      3   `MessageVerifier` makes it easy to generate and verify messages
      4   which are signed to prevent tampering.
      5 
      6   For example, the cookie store uses this verifier to send data
      7   to the client. The data can be read by the client, but cannot be
      8   tampered with.
      9 
     10   The message and its verification are base64url encoded and returned
     11   to you.
     12 
     13   The current algorithm used is HMAC-SHA, with SHA256, SHA384, and
     14   SHA512 as supported digest types.
     15   """
     16 
     17   @doc """
     18   Signs a message according to the given secret.
     19   """
     20   def sign(message, secret, digest_type \\ :sha256)
     21       when is_binary(message) and byte_size(secret) > 0 and
     22              digest_type in [:sha256, :sha384, :sha512] do
     23     hmac_sha2_sign(message, secret, digest_type)
     24   rescue
     25     e -> reraise e, Plug.Crypto.prune_args_from_stacktrace(__STACKTRACE__)
     26   end
     27 
     28   @doc """
     29   Decodes and verifies the encoded binary was not tampered with.
     30   """
     31   def verify(signed, secret) when is_binary(signed) and byte_size(secret) > 0 do
     32     hmac_sha2_verify(signed, secret)
     33   rescue
     34     e -> reraise e, Plug.Crypto.prune_args_from_stacktrace(__STACKTRACE__)
     35   end
     36 
     37   ## Signature Algorithms
     38 
     39   defp hmac_sha2_to_protected(:sha256), do: "HS256"
     40   defp hmac_sha2_to_protected(:sha384), do: "HS384"
     41   defp hmac_sha2_to_protected(:sha512), do: "HS512"
     42 
     43   defp hmac_sha2_to_digest_type("HS256"), do: :sha256
     44   defp hmac_sha2_to_digest_type("HS384"), do: :sha384
     45   defp hmac_sha2_to_digest_type("HS512"), do: :sha512
     46 
     47   defp hmac_sha2_sign(payload, key, digest_type) do
     48     protected = hmac_sha2_to_protected(digest_type)
     49     plain_text = signing_input(protected, payload)
     50     signature = hmac(digest_type, key, plain_text)
     51     encode_token(plain_text, signature)
     52   end
     53 
     54   defp hmac_sha2_verify(signed, key) when is_binary(signed) and is_binary(key) do
     55     case decode_token(signed) do
     56       {protected, payload, plain_text, signature} when protected in ["HS256", "HS384", "HS512"] ->
     57         digest_type = hmac_sha2_to_digest_type(protected)
     58         challenge = hmac(digest_type, key, plain_text)
     59 
     60         if Plug.Crypto.secure_compare(challenge, signature) do
     61           {:ok, payload}
     62         else
     63           :error
     64         end
     65 
     66       _ ->
     67         :error
     68     end
     69   end
     70 
     71   ## Helpers
     72 
     73   defp encode_token(plain_text, signature)
     74        when is_binary(plain_text) and is_binary(signature) do
     75     plain_text <> "." <> Base.url_encode64(signature, padding: false)
     76   end
     77 
     78   defp decode_token(token) do
     79     with [protected, payload, signature] <- String.split(token, ".", parts: 3),
     80          plain_text = protected <> "." <> payload,
     81          {:ok, protected} <- Base.url_decode64(protected, padding: false),
     82          {:ok, payload} <- Base.url_decode64(payload, padding: false),
     83          {:ok, signature} <- Base.url_decode64(signature, padding: false) do
     84       {protected, payload, plain_text, signature}
     85     else
     86       _ -> :error
     87     end
     88   end
     89 
     90   defp signing_input(protected, payload) when is_binary(protected) and is_binary(payload) do
     91     protected
     92     |> Base.url_encode64(padding: false)
     93     |> Kernel.<>(".")
     94     |> Kernel.<>(Base.url_encode64(payload, padding: false))
     95   end
     96 
     97   # TODO: remove when we require OTP 22.1
     98   if Code.ensure_loaded?(:crypto) and function_exported?(:crypto, :mac, 4) do
     99     defp hmac(digest, key, data), do: :crypto.mac(:hmac, digest, key, data)
    100   else
    101     defp hmac(digest, key, data), do: :crypto.hmac(digest, key, data)
    102   end
    103 end