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


The following commit(s) were added to refs/heads/trunk by this push:
     new bd02a36  Use the built-in secrets module.
bd02a36 is described below

commit bd02a360a834dc75fbc3b96bf89b2d78714011bc
Author: Greg Stein <[email protected]>
AuthorDate: Sat Sep 20 03:51:01 2025 -0500

    Use the built-in secrets module.
    
    * crypto.shuffle() implements Fisher-Yates shuffling. Safe!
    * election.new_eid() more easily uses secrets.token_hex()
---
 v3/steve/crypto.py   | 18 +++++++++++++++---
 v3/steve/election.py |  8 +++-----
 2 files changed, 18 insertions(+), 8 deletions(-)

diff --git a/v3/steve/crypto.py b/v3/steve/crypto.py
index ed8b13c..ce72123 100644
--- a/v3/steve/crypto.py
+++ b/v3/steve/crypto.py
@@ -19,7 +19,7 @@
 #
 
 import base64
-import random
+import secrets
 
 import passlib.hash  # note that .argon2 is proxy in this pkg
 import passlib.utils  # for the RNG, to create Salt values
@@ -78,5 +78,17 @@ def _hash(data: bytes, salt: bytes) -> bytes:
 
 def shuffle(x):
     "Ensure we use the strongest RNG available for shuffling."
-    ### second param was removed in 3.11. need to revisit this.
-    return random.shuffle(x)  ###, passlib.utils.rng.random)
+
+    # Implements the Fisher-Yates shuffle, using secrets.randbelow() for
+    # cryptographically-safe (aka unpredictable) shuffling of elements.
+
+    # Count backwards, "fixing" a chosen element into place.
+    for i in range(len(x)-1, 0, -1):
+        # Choose element to fix from remaining pool.
+        j = secrets.randbelow(i + 1)
+
+        # Swap them in-place.
+        x[i], x[j] = x[j], x[i]
+
+    # We shuffled in-place, but also return for funsies.
+    return x
diff --git a/v3/steve/election.py b/v3/steve/election.py
index 5095586..b8f0eda 100644
--- a/v3/steve/election.py
+++ b/v3/steve/election.py
@@ -22,6 +22,7 @@
 
 import sys
 import json
+import secrets
 
 from . import crypto
 from . import db
@@ -386,8 +387,5 @@ class Election:
 def new_eid():
     "Create a new ElectionID."
 
-    # Use 4 bytes of a salt, for 32 bits.
-    b = crypto.gen_salt()
-
-    # Format into 8 hex characters.
-    return f'{b[0]:02x}{b[1]:02x}{b[2]:02x}{b[3]:02x}'
+    # Use 8 hex characters for an ElectionID.
+    return secrets.token_hex(4)  # 4 bytes

Reply via email to