From 065a98175bcc0854936573876b5b07939e502599 Mon Sep 17 00:00:00 2001 From: Uk Chukundah Date: Sun, 5 Jan 2025 10:58:04 +0100 Subject: [PATCH] Implement cryptosign & password authentication --- README.md | 10 ++++-- lib/client/authentication.ex | 60 ++++++++++++++++++++++++------------ 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 697c04c..697c300 100644 --- a/README.md +++ b/README.md @@ -68,10 +68,14 @@ MessagePack and JSON serialization are both supported - [ ] event_history - [ ] topic_reflection +### Authentication +- [x] WAMP-CRA +- [x] WAMP-Cryptosign +- [x] Password authentication +- [ ] Ticket authentication +- [ ] Cookie authentication + ### Other -- [x] challenge-response authentication -- [ ] cookie authentication -- [ ] ticket authentication - [ ] rawsocket transport - [ ] batched WS transport - [ ] longpoll transport diff --git a/lib/client/authentication.ex b/lib/client/authentication.ex index 511fec1..55d2477 100644 --- a/lib/client/authentication.ex +++ b/lib/client/authentication.ex @@ -4,33 +4,53 @@ defmodule Wampex.Client.Authentication do alias Wampex.Crypto alias Wampex.Roles.Peer.Challenge - @enforce_keys [:authid, :authmethods, :secret] - defstruct [:authid, :authmethods, :secret] - - @callback handle(challenge :: Challenge.t(), auth :: __MODULE__.t()) :: {binary(), map()} + @enforce_keys [:authid, :authmethods] + defstruct [:authid, :authmethods, :secret, authextra: %{}] @type t :: %__MODULE__{ authid: binary(), authmethods: [binary()], - secret: binary() + secret: binary(), + authextra: map() } - def handle( - %Challenge{ - auth_method: "wampcra", - extra: %{"challenge" => ch, "salt" => salt, "iterations" => it, "keylen" => len} - }, - auth - ) do - {auth - |> Map.get(:secret) - |> Crypto.pbkdf2(salt, it, len) - |> Crypto.hash_challenge(ch), %{}} + @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 - def handle(%Challenge{auth_method: "wampcra", extra: %{"challenge" => ch}}, auth) do - {auth - |> Map.get(:secret) - |> Crypto.hash_challenge(ch), %{}} + @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} -> + :sha256 |> :crypto.pbkdf2_hmac(auth.secret, salt, it, len) |> Base.encode64() + + _ -> + 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 -- 2.45.3