argon2 password hashing (#406)
authorfloatingghost <hannah@coffee-and-dreams.uk>
Fri, 30 Dec 2022 02:46:58 +0000 (02:46 +0000)
committerfloatingghost <hannah@coffee-and-dreams.uk>
Fri, 30 Dec 2022 02:46:58 +0000 (02:46 +0000)
Co-authored-by: FloatingGhost <hannah@coffee-and-dreams.uk>
Reviewed-on: https://akkoma.dev/AkkomaGang/akkoma/pulls/406

25 files changed:
CHANGELOG.md
lib/mix/tasks/pleroma/config.ex
lib/pleroma/password.ex [new file with mode: 0644]
lib/pleroma/user.ex
lib/pleroma/web/auth/pleroma_authenticator.ex
lib/pleroma/web/auth/totp_authenticator.ex
lib/pleroma/web/common_api/utils.ex
lib/pleroma/web/mongoose_im/mongoose_im_controller.ex
lib/pleroma/web/plugs/authentication_plug.ex
mix.exs
mix.lock
test/pleroma/mfa_test.exs
test/pleroma/password_test.exs [new file with mode: 0644]
test/pleroma/web/auth/basic_auth_test.exs
test/pleroma/web/auth/pleroma_authenticator_test.exs
test/pleroma/web/auth/totp_authenticator_test.exs
test/pleroma/web/mongoose_im_controller_test.exs
test/pleroma/web/o_auth/ldap_authorization_test.exs
test/pleroma/web/o_auth/mfa_controller_test.exs
test/pleroma/web/o_auth/o_auth_controller_test.exs
test/pleroma/web/plugs/authentication_plug_test.exs
test/pleroma/web/twitter_api/password_controller_test.exs
test/pleroma/web/twitter_api/util_controller_test.exs
test/support/builders/user_builder.ex
test/support/factory.ex

index ea6a25e4bd326950c6c45e6cf7b538d9796b0ddd..3106854bafb378ad96a0ac4b2b1b9abd81be8dfb 100644 (file)
@@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Prometheus metrics exporting from `/api/v1/akkoma/metrics`
 - Ability to alter http pool size
 - Translation of statuses via ArgosTranslate
+- Argon2 password hashing
 - Ability to "verify" links in profile fields via rel=me
 - Mix tasks to dump/load config to/from json for bulk editing
 
@@ -17,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Non-finch HTTP adapters
 - Legacy redirect from /api/pleroma/admin to /api/v1/pleroma/admin
 - Legacy redirects from /api/pleroma to /api/v1/pleroma
+- :crypt dependency
 
 ### Changed
 - Return HTTP error 413 when uploading an avatar or banner that's above the configured upload limit instead of a 500.
index dfde6520dd0e61b8ed49950ad1508cc23bf7ebd5..8661d8d7c312e2bfff69cf5a6353756bd0904268 100644 (file)
@@ -87,13 +87,15 @@ defmodule Mix.Tasks.Pleroma.Config do
       key = maybe_atomize(key)
 
       config = ConfigDB.get_by_group_and_key(group, key)
-      json = %{
-        group: ConfigDB.to_json_types(config.group),
-        key: ConfigDB.to_json_types(config.key),
-        value: ConfigDB.to_json_types(config.value),
-      }
-      |> Jason.encode!()
-      |> Jason.Formatter.pretty_print()
+
+      json =
+        %{
+          group: ConfigDB.to_json_types(config.group),
+          key: ConfigDB.to_json_types(config.key),
+          value: ConfigDB.to_json_types(config.value)
+        }
+        |> Jason.encode!()
+        |> Jason.Formatter.pretty_print()
 
       File.write(fname, json)
       shell_info("Wrote #{group}_#{key}.json")
diff --git a/lib/pleroma/password.ex b/lib/pleroma/password.ex
new file mode 100644 (file)
index 0000000..f8cba61
--- /dev/null
@@ -0,0 +1,54 @@
+defmodule Pleroma.Password do
+  @moduledoc """
+  This module handles password hashing and verification.
+  It will delegate to the appropriate module based on the password hash.
+  It also handles upgrading of password hashes.
+  """
+
+  alias Pleroma.User
+  alias Pleroma.Password.Pbkdf2
+  require Logger
+
+  @hashing_module Argon2
+
+  @spec hash_pwd_salt(String.t()) :: String.t()
+  defdelegate hash_pwd_salt(pass), to: @hashing_module
+
+  @spec checkpw(String.t(), String.t()) :: boolean()
+  def checkpw(password, "$2" <> _ = password_hash) do
+    # Handle bcrypt passwords for Mastodon migration
+    Bcrypt.verify_pass(password, password_hash)
+  end
+
+  def checkpw(password, "$pbkdf2" <> _ = password_hash) do
+    Pbkdf2.verify_pass(password, password_hash)
+  end
+
+  def checkpw(password, "$argon2" <> _ = password_hash) do
+    Argon2.verify_pass(password, password_hash)
+  end
+
+  def checkpw(_password, _password_hash) do
+    Logger.error("Password hash not recognized")
+    false
+  end
+
+  @spec maybe_update_password(User.t(), String.t()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
+  def maybe_update_password(%User{password_hash: "$2" <> _} = user, password) do
+    do_update_password(user, password)
+  end
+
+  def maybe_update_password(%User{password_hash: "$6" <> _} = user, password) do
+    do_update_password(user, password)
+  end
+
+  def maybe_update_password(%User{password_hash: "$pbkdf2" <> _} = user, password) do
+    do_update_password(user, password)
+  end
+
+  def maybe_update_password(user, _), do: {:ok, user}
+
+  defp do_update_password(user, password) do
+    User.reset_password(user, %{password: password, password_confirmation: password})
+  end
+end
index 2d4bd097db5f64b718ad8dd43cfd6de85415b900..1ddbd36a828fbbe935272e700e213ea68100fa28 100644 (file)
@@ -2277,7 +2277,7 @@ defmodule Pleroma.User do
   defp put_password_hash(
          %Ecto.Changeset{valid?: true, changes: %{password: password}} = changeset
        ) do
-    change(changeset, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password))
+    change(changeset, password_hash: Pleroma.Password.hash_pwd_salt(password))
   end
 
   defp put_password_hash(changeset), do: changeset
index bb377d68666ad9d317512974bcbf2e85c7a2018d..01b54037c36138efee6039c37166c4bcd1c65c73 100644 (file)
@@ -6,7 +6,6 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do
   alias Pleroma.Registration
   alias Pleroma.Repo
   alias Pleroma.User
-  alias Pleroma.Web.Plugs.AuthenticationPlug
 
   import Pleroma.Web.Auth.Helpers, only: [fetch_credentials: 1, fetch_user: 1]
 
@@ -15,8 +14,8 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do
   def get_user(%Plug.Conn{} = conn) do
     with {:ok, {name, password}} <- fetch_credentials(conn),
          {_, %User{} = user} <- {:user, fetch_user(name)},
-         {_, true} <- {:checkpw, AuthenticationPlug.checkpw(password, user.password_hash)},
-         {:ok, user} <- AuthenticationPlug.maybe_update_password(user, password) do
+         {_, true} <- {:checkpw, Pleroma.Password.checkpw(password, user.password_hash)},
+         {:ok, user} <- Pleroma.Password.maybe_update_password(user, password) do
       {:ok, user}
     else
       {:error, _reason} = error -> error
index 5947cd8c98e622a32bfe9931b6c733b38065a2f2..e6f839e6ea6e2be1462764602945acda7eac8210 100644 (file)
@@ -6,7 +6,6 @@ defmodule Pleroma.Web.Auth.TOTPAuthenticator do
   alias Pleroma.MFA
   alias Pleroma.MFA.TOTP
   alias Pleroma.User
-  alias Pleroma.Web.Plugs.AuthenticationPlug
 
   @doc "Verify code or check backup code."
   @spec verify(String.t(), User.t()) ::
@@ -31,7 +30,7 @@ defmodule Pleroma.Web.Auth.TOTPAuthenticator do
         code
       )
       when is_list(codes) and is_binary(code) do
-    hash_code = Enum.find(codes, fn hash -> AuthenticationPlug.checkpw(code, hash) end)
+    hash_code = Enum.find(codes, fn hash -> Pleroma.Password.checkpw(code, hash) end)
 
     if hash_code do
       MFA.invalidate_backup_code(user, hash_code)
index bf03b0a8253fec9cf6b47b27df7a9dddd316ab4a..22594be4605f24fb81b3166f1b002e9aa1df4c15 100644 (file)
@@ -17,7 +17,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do
   alias Pleroma.Web.ActivityPub.Visibility
   alias Pleroma.Web.CommonAPI.ActivityDraft
   alias Pleroma.Web.MediaProxy
-  alias Pleroma.Web.Plugs.AuthenticationPlug
   alias Pleroma.Web.Utils.Params
 
   require Logger
@@ -356,7 +355,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
   @spec confirm_current_password(User.t(), String.t()) :: {:ok, User.t()} | {:error, String.t()}
   def confirm_current_password(user, password) do
     with %User{local: true} = db_user <- User.get_cached_by_id(user.id),
-         true <- AuthenticationPlug.checkpw(password, db_user.password_hash) do
+         true <- Pleroma.Password.checkpw(password, db_user.password_hash) do
       {:ok, db_user}
     else
       _ -> {:error, dgettext("errors", "Invalid password.")}
index 6ace3e0b5cac0124c29d2437357bf6cba413dd2f..85b75190b3c190adef143f990605df1dcfb54cd1 100644 (file)
@@ -7,7 +7,6 @@ defmodule Pleroma.Web.MongooseIM.MongooseIMController do
 
   alias Pleroma.Repo
   alias Pleroma.User
-  alias Pleroma.Web.Plugs.AuthenticationPlug
   alias Pleroma.Web.Plugs.RateLimiter
 
   plug(RateLimiter, [name: :authentication] when action in [:user_exists, :check_password])
@@ -28,7 +27,7 @@ defmodule Pleroma.Web.MongooseIM.MongooseIMController do
   def check_password(conn, %{"user" => username, "pass" => password}) do
     with %User{password_hash: password_hash, is_active: true} <-
            Repo.get_by(User, nickname: username, local: true),
-         true <- AuthenticationPlug.checkpw(password, password_hash) do
+         true <- Pleroma.Password.checkpw(password, password_hash) do
       conn
       |> json(true)
     else
index 8d58169cf61ebf2e15ac7bbabc6b81998822cfd3..894a1067e75c053d495eaa24f6240bc719819622 100644 (file)
@@ -7,6 +7,7 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlug do
 
   alias Pleroma.Helpers.AuthHelper
   alias Pleroma.User
+  alias Pleroma.Password
 
   import Plug.Conn
 
@@ -25,8 +26,8 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlug do
         } = conn,
         _
       ) do
-    if checkpw(password, password_hash) do
-      {:ok, auth_user} = maybe_update_password(auth_user, password)
+    if Password.checkpw(password, password_hash) do
+      {:ok, auth_user} = Password.maybe_update_password(auth_user, password)
 
       conn
       |> assign(:user, auth_user)
@@ -38,35 +39,6 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlug do
 
   def call(conn, _), do: conn
 
-  def checkpw(password, "$6" <> _ = password_hash) do
-    :crypt.crypt(password, password_hash) == password_hash
-  end
-
-  def checkpw(password, "$2" <> _ = password_hash) do
-    # Handle bcrypt passwords for Mastodon migration
-    Bcrypt.verify_pass(password, password_hash)
-  end
-
-  def checkpw(password, "$pbkdf2" <> _ = password_hash) do
-    Pleroma.Password.Pbkdf2.verify_pass(password, password_hash)
-  end
-
-  def checkpw(_password, _password_hash) do
-    Logger.error("Password hash not recognized")
-    false
-  end
-
-  def maybe_update_password(%User{password_hash: "$2" <> _} = user, password) do
-    do_update_password(user, password)
-  end
-
-  def maybe_update_password(%User{password_hash: "$6" <> _} = user, password) do
-    do_update_password(user, password)
-  end
-
-  def maybe_update_password(user, _), do: {:ok, user}
-
-  defp do_update_password(user, password) do
-    User.reset_password(user, %{password: password, password_confirmation: password})
-  end
+  @spec checkpw(String.t(), String.t()) :: boolean
+  defdelegate checkpw(password, hash), to: Password
 end
diff --git a/mix.exs b/mix.exs
index 4898591b1d79eb4256256f9ae92de4ec1d7d34a6..2a62dedeef438dbe1ece476c98ac6ccdbbacd234 100644 (file)
--- a/mix.exs
+++ b/mix.exs
@@ -143,9 +143,7 @@ defmodule Pleroma.Mixfile do
       {:sweet_xml, "~> 0.7.2"},
       {:earmark, "~> 1.4.15"},
       {:bbcode_pleroma, "~> 0.2.0"},
-      {:crypt,
-       git: "https://github.com/msantos/crypt.git",
-       ref: "f75cd55325e33cbea198fb41fe41871392f8fb76"},
+      {:argon2_elixir, "~> 3.0.0"},
       {:cors_plug, "~> 2.0"},
       {:web_push_encryption, "~> 0.3.1"},
       {:swoosh, "~> 1.0"},
index 4fa4c05ec999032daa5ccb4ae9204b0b63354f6c..dbe0ea5e39028fbcfce15902e2906264e6737ac2 100644 (file)
--- a/mix.lock
+++ b/mix.lock
@@ -1,4 +1,5 @@
 %{
+  "argon2_elixir": {:hex, :argon2_elixir, "3.0.0", "fd4405f593e77b525a5c667282172dd32772d7c4fa58cdecdaae79d2713b6c5f", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "8b753b270af557d51ba13fcdebc0f0ab27a2a6792df72fd5a6cf9cfaffcedc57"},
   "base62": {:hex, :base62, "1.2.2", "85c6627eb609317b70f555294045895ffaaeb1758666ab9ef9ca38865b11e629", [:mix], [{:custom_base, "~> 0.2.1", [hex: :custom_base, repo: "hexpm", optional: false]}], "hexpm", "d41336bda8eaa5be197f1e4592400513ee60518e5b9f4dcf38f4b4dae6f377bb"},
   "bbcode_pleroma": {:hex, :bbcode_pleroma, "0.2.0", "d36f5bca6e2f62261c45be30fa9b92725c0655ad45c99025cb1c3e28e25803ef", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "19851074419a5fedb4ef49e1f01b30df504bb5dbb6d6adfc135238063bebd1c3"},
   "bcrypt_elixir": {:hex, :bcrypt_elixir, "2.3.1", "5114d780459a04f2b4aeef52307de23de961b69e13a5cd98a911e39fda13f420", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "42182d5f46764def15bf9af83739e3bf4ad22661b1c34fc3e88558efced07279"},
@@ -18,7 +19,6 @@
   "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.3.1", "ebd1a1d7aff97f27c66654e78ece187abdc646992714164380d8a041eda16754", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3a6efd3366130eab84ca372cbd4a7d3c3a97bdfcfb4911233b035d117063f0af"},
   "cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"},
   "credo": {:git, "https://github.com/rrrene/credo.git", "1c1b99ea41a457761383d81aaf6a606913996fe7", [ref: "1c1b99ea41a457761383d81aaf6a606913996fe7"]},
-  "crypt": {:git, "https://github.com/msantos/crypt.git", "f75cd55325e33cbea198fb41fe41871392f8fb76", [ref: "f75cd55325e33cbea198fb41fe41871392f8fb76"]},
   "custom_base": {:hex, :custom_base, "0.2.1", "4a832a42ea0552299d81652aa0b1f775d462175293e99dfbe4d7dbaab785a706", [:mix], [], "hexpm", "8df019facc5ec9603e94f7270f1ac73ddf339f56ade76a721eaa57c1493ba463"},
   "db_connection": {:hex, :db_connection, "2.4.3", "3b9aac9f27347ec65b271847e6baeb4443d8474289bd18c1d6f4de655b70c94d", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c127c15b0fa6cfb32eed07465e05da6c815b032508d4ed7c116122871df73c12"},
   "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
index 76ba1a99d891f3bea92a4a47609d7905169f221a..66660d97e1cc5cbf424f54b6631dc6d966d63f7d 100644 (file)
@@ -30,8 +30,8 @@ defmodule Pleroma.MFATest do
       {:ok, [code1, code2]} = MFA.generate_backup_codes(user)
       updated_user = refresh_record(user)
       [hash1, hash2] = updated_user.multi_factor_authentication_settings.backup_codes
-      assert Pleroma.Password.Pbkdf2.verify_pass(code1, hash1)
-      assert Pleroma.Password.Pbkdf2.verify_pass(code2, hash2)
+      assert Pleroma.Password.checkpw(code1, hash1)
+      assert Pleroma.Password.checkpw(code2, hash2)
     end
   end
 
diff --git a/test/pleroma/password_test.exs b/test/pleroma/password_test.exs
new file mode 100644 (file)
index 0000000..951fc81
--- /dev/null
@@ -0,0 +1,65 @@
+defmodule Pleroma.PasswordTest do
+  use Pleroma.DataCase, async: true
+  import Pleroma.Factory
+  import ExUnit.CaptureLog
+
+  alias Pleroma.Password
+
+  describe "hash_pwd_salt/1" do
+    test "returns a hash" do
+      assert "$argon2id" <> _ = Password.hash_pwd_salt("test")
+    end
+  end
+
+  describe "maybe_update_password/2" do
+    test "with a bcrypt hash, it updates to an argon2 hash" do
+      user = insert(:user, password_hash: Bcrypt.hash_pwd_salt("123"))
+      assert "$2" <> _ = user.password_hash
+
+      {:ok, user} = Password.maybe_update_password(user, "123")
+      assert "$argon2" <> _ = user.password_hash
+    end
+
+    test "with a pbkdf2 hash, it updates to an argon2 hash" do
+      user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("123"))
+      assert "$pbkdf2" <> _ = user.password_hash
+
+      {:ok, user} = Password.maybe_update_password(user, "123")
+      assert "$argon2" <> _ = user.password_hash
+    end
+  end
+
+  describe "checkpw/2" do
+    test "check pbkdf2 hash" do
+      hash =
+        "$pbkdf2-sha512$160000$loXqbp8GYls43F0i6lEfIw$AY.Ep.2pGe57j2hAPY635sI/6w7l9Q9u9Bp02PkPmF3OrClDtJAI8bCiivPr53OKMF7ph6iHhN68Rom5nEfC2A"
+
+      assert Password.checkpw("test-password", hash)
+      refute Password.checkpw("test-password1", hash)
+    end
+
+    test "check bcrypt hash" do
+      hash = "$2a$10$uyhC/R/zoE1ndwwCtMusK.TLVzkQ/Ugsbqp3uXI.CTTz0gBw.24jS"
+
+      assert Password.checkpw("password", hash)
+      refute Password.checkpw("password1", hash)
+    end
+
+    test "check argon2 hash" do
+      hash =
+        "$argon2id$v=19$m=65536,t=8,p=2$zEMMsTuK5KkL5AFWbX7jyQ$VyaQD7PF6e9btz0oH1YiAkWwIGZ7WNDZP8l+a/O171g"
+
+      assert Password.checkpw("password", hash)
+      refute Password.checkpw("password1", hash)
+    end
+
+    test "it returns false when hash invalid" do
+      hash =
+        "psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1"
+
+      assert capture_log(fn ->
+               refute Password.checkpw("password", hash)
+             end) =~ "[error] Password hash not recognized"
+    end
+  end
+end
index 2816aae4ceee1b167bb2e9cdd6946a44be1b7f18..a357ba4a521326eee626affcbf2737c0d9df78dc 100644 (file)
@@ -11,7 +11,7 @@ defmodule Pleroma.Web.Auth.BasicAuthTest do
     conn: conn
   } do
     user = insert(:user)
-    assert Pleroma.Password.Pbkdf2.verify_pass("test", user.password_hash)
+    assert Pleroma.Password.checkpw("test", user.password_hash)
 
     basic_auth_contents =
       (URI.encode_www_form(user.nickname) <> ":" <> URI.encode_www_form("test"))
index b1397c523096e1d45aba99c7b4f8caeabbe397d3..fb3c474172e736f164c296e47b1c2ee467dce242 100644 (file)
@@ -15,7 +15,7 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticatorTest do
     user =
       insert(:user,
         nickname: name,
-        password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password)
+        password_hash: Pleroma.Password.hash_pwd_salt(password)
       )
 
     {:ok, [user: user, name: name, password: password]}
@@ -30,7 +30,7 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticatorTest do
 
     assert {:ok, returned_user} = res
     assert returned_user.id == user.id
-    assert "$pbkdf2" <> _ = returned_user.password_hash
+    assert "$argon2" <> _ = returned_user.password_hash
   end
 
   test "get_user/authorization with invalid password", %{name: name} do
index ac4209f2d92f16537b57ccc1c0f47738fa1ac46f..6d2646b617d1c10c3fba38d074e98e5af2e437a0 100644 (file)
@@ -34,7 +34,7 @@ defmodule Pleroma.Web.Auth.TOTPAuthenticatorTest do
 
     hashed_codes =
       backup_codes
-      |> Enum.map(&Pleroma.Password.Pbkdf2.hash_pwd_salt(&1))
+      |> Enum.map(&Pleroma.Password.hash_pwd_salt(&1))
 
     user =
       insert(:user,
index 43c4dfa33cdde5a5862e88ea4d5ad9a9bc1c65f3..73473ccf54df8f1e6059535c375042278096e380 100644 (file)
@@ -41,13 +41,13 @@ defmodule Pleroma.Web.MongooseIMControllerTest do
   end
 
   test "/check_password", %{conn: conn} do
-    user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("cool"))
+    user = insert(:user, password_hash: Pleroma.Password.hash_pwd_salt("cool"))
 
     _deactivated_user =
       insert(:user,
         nickname: "konata",
         is_active: false,
-        password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("cool")
+        password_hash: Pleroma.Password.hash_pwd_salt("cool")
       )
 
     res =
index c8a1d65ab5f0614c6174186df96a662b589aa597..502ee091876049622644be709594f0c48ec2d736 100644 (file)
@@ -18,7 +18,7 @@ defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do
   @tag @skip
   test "authorizes the existing user using LDAP credentials" do
     password = "testpassword"
-    user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password))
+    user = insert(:user, password_hash: Pleroma.Password.hash_pwd_salt(password))
     app = insert(:oauth_app, scopes: ["read", "write"])
 
     host = Pleroma.Config.get([:ldap, :host]) |> to_charlist
@@ -101,7 +101,7 @@ defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do
   @tag @skip
   test "disallow authorization for wrong LDAP credentials" do
     password = "testpassword"
-    user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password))
+    user = insert(:user, password_hash: Pleroma.Password.hash_pwd_salt(password))
     app = insert(:oauth_app, scopes: ["read", "write"])
 
     host = Pleroma.Config.get([:ldap, :host]) |> to_charlist
index 17bbde85b40a974ab45070afd08f6e3e2e4932f8..dacf03b2bdb9d3277a2651ec72dbb16802f65516 100644 (file)
@@ -20,7 +20,7 @@ defmodule Pleroma.Web.OAuth.MFAControllerTest do
       insert(:user,
         multi_factor_authentication_settings: %MFA.Settings{
           enabled: true,
-          backup_codes: [Pleroma.Password.Pbkdf2.hash_pwd_salt("test-code")],
+          backup_codes: [Pleroma.Password.hash_pwd_salt("test-code")],
           totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true}
         }
       )
@@ -246,7 +246,7 @@ defmodule Pleroma.Web.OAuth.MFAControllerTest do
 
       hashed_codes =
         backup_codes
-        |> Enum.map(&Pleroma.Password.Pbkdf2.hash_pwd_salt(&1))
+        |> Enum.map(&Pleroma.Password.hash_pwd_salt(&1))
 
       user =
         insert(:user,
index 7240624efd62710fd91832716c739b7f0fcfda68..303bc2cf2efda5dbf4db7ec3e724bf176442261f 100644 (file)
@@ -316,7 +316,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
            app: app,
            conn: conn
          } do
-      user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("testpassword"))
+      user = insert(:user, password_hash: Pleroma.Password.hash_pwd_salt("testpassword"))
       registration = insert(:registration, user: nil)
       redirect_uri = OAuthController.default_redirect_uri(app)
 
@@ -347,7 +347,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
            app: app,
            conn: conn
          } do
-      user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("testpassword"))
+      user = insert(:user, password_hash: Pleroma.Password.hash_pwd_salt("testpassword"))
       registration = insert(:registration, user: nil)
       unlisted_redirect_uri = "http://cross-site-request.com"
 
@@ -917,7 +917,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
 
     test "issues a token for `password` grant_type with valid credentials, with full permissions by default" do
       password = "testpassword"
-      user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password))
+      user = insert(:user, password_hash: Pleroma.Password.hash_pwd_salt(password))
 
       app = insert(:oauth_app, scopes: ["read", "write"])
 
@@ -947,7 +947,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
 
       user =
         insert(:user,
-          password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password),
+          password_hash: Pleroma.Password.hash_pwd_salt(password),
           multi_factor_authentication_settings: %MFA.Settings{
             enabled: true,
             totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true}
@@ -1056,7 +1056,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
       password = "testpassword"
 
       {:ok, user} =
-        insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password))
+        insert(:user, password_hash: Pleroma.Password.hash_pwd_salt(password))
         |> User.confirmation_changeset(set_confirmation: false)
         |> User.update_and_set_cache()
 
@@ -1084,7 +1084,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
 
       user =
         insert(:user,
-          password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password),
+          password_hash: Pleroma.Password.hash_pwd_salt(password),
           is_active: false
         )
 
@@ -1112,7 +1112,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
 
       user =
         insert(:user,
-          password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password),
+          password_hash: Pleroma.Password.hash_pwd_salt(password),
           password_reset_pending: true
         )
 
@@ -1141,7 +1141,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
 
       user =
         insert(:user,
-          password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password),
+          password_hash: Pleroma.Password.hash_pwd_salt(password),
           is_confirmed: false
         )
 
@@ -1169,7 +1169,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
 
       user =
         insert(:user,
-          password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password),
+          password_hash: Pleroma.Password.hash_pwd_salt(password),
           is_approved: false
         )
 
index 118ab302a0c956ad1217f141a5c927e20cf3cf93..1fbc17a924360a0c3eea9decc484c9646ead6043 100644 (file)
@@ -17,7 +17,7 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlugTest do
     user = %User{
       id: 1,
       name: "dude",
-      password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("guy")
+      password_hash: Pleroma.Password.hash_pwd_salt("guy")
     }
 
     conn =
@@ -52,7 +52,7 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlugTest do
     assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
   end
 
-  test "with a bcrypt hash, it updates to a pkbdf2 hash", %{conn: conn} do
+  test "with a bcrypt hash, it updates to an argon2 hash", %{conn: conn} do
     user = insert(:user, password_hash: Bcrypt.hash_pwd_salt("123"))
     assert "$2" <> _ = user.password_hash
 
@@ -67,21 +67,17 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlugTest do
     assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
 
     user = User.get_by_id(user.id)
-    assert "$pbkdf2" <> _ = user.password_hash
+    assert "$argon2" <> _ = user.password_hash
   end
 
-  @tag :skip_on_mac
-  test "with a crypt hash, it updates to a pkbdf2 hash", %{conn: conn} do
-    user =
-      insert(:user,
-        password_hash:
-          "$6$9psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1"
-      )
+  test "with a pbkdf2 hash, it updates to an argon2 hash", %{conn: conn} do
+    user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("123"))
+    assert "$pbkdf2" <> _ = user.password_hash
 
     conn =
       conn
       |> assign(:auth_user, user)
-      |> assign(:auth_credentials, %{password: "password"})
+      |> assign(:auth_credentials, %{password: "123"})
       |> AuthenticationPlug.call(%{})
 
     assert conn.assigns.user.id == conn.assigns.auth_user.id
@@ -89,7 +85,7 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlugTest do
     assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
 
     user = User.get_by_id(user.id)
-    assert "$pbkdf2" <> _ = user.password_hash
+    assert "$argon2" <> _ = user.password_hash
   end
 
   describe "checkpw/2" do
@@ -101,16 +97,16 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlugTest do
       refute AuthenticationPlug.checkpw("test-password1", hash)
     end
 
-    @tag :skip_on_mac
-    test "check sha512-crypt hash" do
-      hash =
-        "$6$9psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1"
+    test "check bcrypt hash" do
+      hash = "$2a$10$uyhC/R/zoE1ndwwCtMusK.TLVzkQ/Ugsbqp3uXI.CTTz0gBw.24jS"
 
       assert AuthenticationPlug.checkpw("password", hash)
+      refute AuthenticationPlug.checkpw("password1", hash)
     end
 
-    test "check bcrypt hash" do
-      hash = "$2a$10$uyhC/R/zoE1ndwwCtMusK.TLVzkQ/Ugsbqp3uXI.CTTz0gBw.24jS"
+    test "check argon2 hash" do
+      hash =
+        "$argon2id$v=19$m=65536,t=8,p=2$zEMMsTuK5KkL5AFWbX7jyQ$VyaQD7PF6e9btz0oH1YiAkWwIGZ7WNDZP8l+a/O171g"
 
       assert AuthenticationPlug.checkpw("password", hash)
       refute AuthenticationPlug.checkpw("password1", hash)
index 05c3561bf82a18bb2d50c5ce1906562d72c6eab4..4ff792dc8745db3d0339ed65dbfad7ddb969b798 100644 (file)
@@ -96,7 +96,7 @@ defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest do
       assert response =~ "<h2>Password changed!</h2>"
 
       user = refresh_record(user)
-      assert Pleroma.Password.Pbkdf2.verify_pass("test", user.password_hash)
+      assert Pleroma.Password.checkpw("test", user.password_hash)
       assert Enum.empty?(Token.get_user_tokens(user))
     end
 
index 3f839568d24c9e2a8d5edcacbb56d45672d92d56..51f216bf192c6deee8b9549745db1c70b0e2766f 100644 (file)
@@ -553,7 +553,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
 
       assert json_response_and_validate_schema(conn, 200) == %{"status" => "success"}
       fetched_user = User.get_cached_by_id(user.id)
-      assert Pleroma.Password.Pbkdf2.verify_pass("newpass", fetched_user.password_hash) == true
+      assert Pleroma.Password.checkpw("newpass", fetched_user.password_hash) == true
     end
   end
 
index 6bccbb35aeea5485ea008f72a39364a08b7d6dfb..27470498df2e1637faa8d020583edd7887a4fb69 100644 (file)
@@ -7,7 +7,7 @@ defmodule Pleroma.Builders.UserBuilder do
       email: "test@example.org",
       name: "Test Name",
       nickname: "testname",
-      password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("test"),
+      password_hash: Pleroma.Password.hash_pwd_salt("test"),
       bio: "A tester.",
       ap_id: "some id",
       last_digest_emailed_at: NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second),
index 84e076137401a17e834eb21c39dba7e94d343c02..42c940c52603492fbdadba053622cc086ead1a21 100644 (file)
@@ -47,12 +47,16 @@ defmodule Pleroma.Factory do
 
   def user_factory(attrs \\ %{}) do
     pem = Enum.random(@rsa_keys)
+    # Argon2.hash_pwd_salt("test")
+    # it really eats CPU time, so we use a precomputed hash
+    password_hash =
+      "$argon2id$v=19$m=65536,t=8,p=2$FEAarFuiOsROO24NHIHMYw$oxdaz2fTPpuU+dYCl60FsqE65T1Tjy6lGikKfmql4xo"
 
     user = %User{
       name: sequence(:name, &"Test ใƒ†ใ‚นใƒˆ User #{&1}"),
       email: sequence(:email, &"user#{&1}@example.com"),
       nickname: sequence(:nickname, &"nick#{&1}"),
-      password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("test"),
+      password_hash: password_hash,
       bio: sequence(:bio, &"Tester Number #{&1}"),
       is_discoverable: true,
       last_digest_emailed_at: NaiveDateTime.utc_now(),