Current section
Files
Jump to
Current section
Files
lib/client/authentication.ex
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