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

gstein pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/steve.git

commit f4a0f2f6c813cc3a73bb2714a8e7d7826dd4f24b
Author: Greg Stein <[email protected]>
AuthorDate: Fri Feb 20 19:32:44 2026 -0600

    Switch from passlib to argon2-cffi.
    
    The cffi was already imported so that passlib could use it. However,
    we don't need the "stored password formatting" that passlib provides.
    We simply want the Argon2 hashing algorithm.
    
    Strip out passlib.
    
    Add a benchmarking tool to measure/tune our use of Argon2. We can
    perform some tuning in the future to ensure (minimum time) that brute
    force can't be used against our vote database.
---
 v3/pyproject.toml  |  1 -
 v3/steve/crypto.py | 61 +++++++++++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 56 insertions(+), 6 deletions(-)

diff --git a/v3/pyproject.toml b/v3/pyproject.toml
index 6f7a7f7..25b79d2 100644
--- a/v3/pyproject.toml
+++ b/v3/pyproject.toml
@@ -12,7 +12,6 @@ dependencies = [
     "asfquart>=0.1.12",
     "ezt>=1.1",
     "easydict>=1.13",
-    "passlib>=1.7.4,<2",
     "cryptography>=46.0.5,<47",
     "argon2-cffi>=25.1.0,<26",
 ]
diff --git a/v3/steve/crypto.py b/v3/steve/crypto.py
index 9606c81..63480b4 100644
--- a/v3/steve/crypto.py
+++ b/v3/steve/crypto.py
@@ -18,8 +18,9 @@
 import base64
 import secrets
 import hashlib  # for blake2b
+import time
 
-import passlib.hash  # note that .argon2 is proxy in this pkg
+import argon2.low_level
 
 import cryptography.fernet
 from cryptography.hazmat.primitives import hashes
@@ -87,10 +88,17 @@ def decrypt_votestring(vote_token: bytes, salt: bytes, 
ciphertext: bytes) -> str
 
 
 def _hash(data: bytes, salt: bytes) -> bytes:
-    "Apply our desired hashing function."
-    ph = passlib.hash.argon2.using(type='d', salt=salt)
-    h = ph.hash(data)
-    return base64.standard_b64decode(h.split('$')[-1] + '==')
+    "Use Argon2 to hash the data, with default tuning parameters."
+
+    return argon2.low_level.hash_secret_raw(
+        secret=data,
+        salt=salt,
+        time_cost=2,          # Passlib default
+        memory_cost=65536,    # Passlib default
+        parallelism=4,        # Passlib default
+        hash_len=32,          # Standard Argon2 digest length
+        type=argon2.low_level.Type.D,
+    )
 
 
 def shuffle(x):
@@ -116,3 +124,46 @@ def create_id():
 
     # Use 10 hex characters for the ID
     return secrets.token_hex(5)  # 5 bytes
+
+
+def benchmark_argon2():
+
+    # Test Data
+    dummy_data = b"This is a sample datum representing one of your 1000 
entries."
+    salt = b"16_byte_salt_123" # 16 bytes
+    intermediate = hashlib.blake2b(dummy_data).digest()
+
+    # Define Low/High levels for the 3 parameters
+    # Note: Memory is in Kibibytes (65536 = 64MB)
+    options = {
+        "rounds": [2, 4],
+        "memory": [65536, 131072],
+        "parallelism": [4, 8]
+    }
+
+    print(f"{'Rounds':<8} | {'Memory (MB)':<12} | {'Threads':<8} | {'Time 
(s)':<10}")
+    print("-" * 50)
+
+    for r in options["rounds"]:
+        for m in options["memory"]:
+            for p in options["parallelism"]:
+                start = time.perf_counter()
+                
+                # The actual derivation process
+                argon2.low_level.hash_secret_raw(
+                    secret=intermediate,
+                    salt=salt,
+                    time_cost=r,
+                    memory_cost=m,
+                    parallelism=p,
+                    hash_len=32,
+                    type=argon2.low_level.Type.ID
+                )
+                
+                duration = time.perf_counter() - start
+                print(f"{r:<8} | {m//1024:<12} | {p:<8} | {duration:.4f}s")
+
+
+if __name__ == "__main__":
+    print("--- Argon2 Parameter Benchmarking ---")
+    benchmark_argon2()

Reply via email to