This is an automated email from the ASF dual-hosted git repository.

rnewson pushed a commit to branch decouple_offline_hash_strength_from_online
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 360ae5979812cf8e8683ce4dcc5b95b1984805c2
Author: Robert Newson <[email protected]>
AuthorDate: Thu Oct 19 15:18:48 2023 +0100

    in-memory password hash cache
---
 src/couch/src/couch_passwords_cache.erl | 54 +++++++++++++++++++++++++++++++++
 src/couch/src/couch_primary_sup.erl     | 14 ++++++++-
 2 files changed, 67 insertions(+), 1 deletion(-)

diff --git a/src/couch/src/couch_passwords_cache.erl 
b/src/couch/src/couch_passwords_cache.erl
new file mode 100644
index 000000000..d0b277a51
--- /dev/null
+++ b/src/couch/src/couch_passwords_cache.erl
@@ -0,0 +1,54 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+%
+% password hashes on disk can take a long time to verify. This is by design, to
+% guard against offline attacks. This module adds an in-memory cache for 
password
+% verification to speed up verification after a successful slow verification 
from
+% the hash stored on disk, in order not to transfer the deliberate offline 
attack
+% protection to database requests.
+%
+% In memory we record a PBKDF_SHA256 derivation using a low number of 
iterations
+% and check against this if present. Entries in couch_passwords_cache expire 
automatically
+% and the maximum number of cached entries is configurable.
+
+-module(couch_passwords_cache).
+
+-define(CACHE, couch_passwords_cache_lru).
+
+-export([start_link/0]).
+
+% public api
+-export([authenticate/3, insert/3]).
+
+start_link() ->
+    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
+
+% public functions
+-spec authenticate(AuthModule :: atom(), UserName :: binary(), Password :: 
binary()) ->
+    not_found | boolean().
+authenticate(AuthModule, UserName, Password) ->
+    case ets_lru:lookup_d(?CACHE, {AuthModule, UserName}) of
+        not_found ->
+            not_found;
+        {ok, {Salt, Expected}} ->
+            Actual = hash(Password, Salt),
+            couch_passwords:verify(Expected, Actual)
+    end.
+
+-spec insert(AuthModule :: atom(), UserName :: binary(), Password :: binary()) 
-> ok.
+insert(AuthModule, UserName, Password) ->
+    Salt = couch_uuids:random(),
+    DerivedKey = hash(Password, Salt),
+    ets_lru:insert(?CACHE, {AuthModule, UserName}, {Salt, DerivedKey}).
+
+hash(Password, Salt) ->
+    crypto:pbkdf2_hmac(sha256, Password, Salt, _Iterations = 5, _KeyLen = 32).
diff --git a/src/couch/src/couch_primary_sup.erl 
b/src/couch/src/couch_primary_sup.erl
index 1eae87160..5c27a2850 100644
--- a/src/couch/src/couch_primary_sup.erl
+++ b/src/couch/src/couch_primary_sup.erl
@@ -23,7 +23,19 @@ init([]) ->
             {couch_task_status, {couch_task_status, start_link, []}, 
permanent, brutal_kill, worker,
                 [couch_task_status]},
             {couch_password_hasher, {couch_password_hasher, start_link, []}, 
permanent, brutal_kill,
-                worker, [couch_password_hasher]}
+                worker, [couch_password_hasher]},
+            {couch_passwords_cache_lru,
+                {ets_lru, start_link, [
+                    couch_passwords_cache_lru,
+
+                    [
+                        {max_objects,
+                            config:get_integer("couch_passwords_cache", 
"max_objects", 10_000)},
+                        {max_lifetime,
+                            config:get_integer("couch_passwords_cache", 
"max_lifetime", 60_000)}
+                    ]
+                ]},
+                permanent, 5000, worker, [ets_lru]}
         ] ++ couch_servers(),
     {ok, {{one_for_one, 10, 3600}, Children}}.
 

Reply via email to