defmodule Wampex.Client.Authentication do @moduledoc false alias Wampex.Crypto alias Wampex.Roles.Peer.Challenge @enforce_keys [:authid, :authmethods] defstruct [:authid, :authmethods, :secret, authextra: %{}] @type t :: %__MODULE__{ authid: binary(), authmethods: [binary()], secret: binary(), authextra: map() } @callback handle(challenge :: Challenge.t(), auth :: t()) :: {binary(), map()} @spec cryptosign(binary(), binary()) :: t() def cryptosign(authid, privkey) do privkey = privkey |> String.upcase() |> Base.decode16!() pubkey = :eddsa |> :crypto.generate_key(:ed25519, privkey) |> elem(0) |> Base.encode16() %__MODULE__{authmethods: ~w[cryptosign], authextra: %{pubkey: pubkey}, authid: authid, secret: privkey} end @spec wampcra(binary(), binary()) :: t() def wampcra(authid, secret), do: %__MODULE__{authmethods: ~w[wampcra], authid: authid, secret: secret} @spec password(binary(), binary()) :: t() def password(authid, password), do: %__MODULE__{authid: authid, secret: password, authmethods: ~w[password]} def handle(%Challenge{auth_method: "wampcra", extra: %{"challenge" => ch} = xtra}, auth) do xtra |> case do %{"salt" => salt, "iterations" => it, "keylen" => len} -> Crypto.pbkdf2(auth.secret, salt, it, len) _ -> auth.secret end |> Crypto.hash_challenge(ch) |> then(&{&1, %{}}) end def handle(%Challenge{auth_method: "password"}, auth) do {auth.secret, %{}} end def handle(%Challenge{auth_method: "cryptosign", extra: %{"challenge" => ch}}, auth) do ch_bytes = ch |> String.upcase() |> Base.decode16!() prefix = :eddsa |> :crypto.sign(:none, ch_bytes, [auth.secret, :ed25519]) |> Base.encode16() |> String.downcase() {"#{prefix}#{ch}", %{}} end end