Skip to content

fix(security): generate referral codes with secrets CSPRNG#356

Open
kevinnft wants to merge 1 commit into
Quantarq:mainfrom
kevinnft:fix/referral-secrets-random
Open

fix(security): generate referral codes with secrets CSPRNG#356
kevinnft wants to merge 1 commit into
Quantarq:mainfrom
kevinnft:fix/referral-secrets-random

Conversation

@kevinnft

Copy link
Copy Markdown

Summary

Closes #196.

Referral codes were generated with random.choices (Mersenne Twister). That stream is not cryptographically secure; prior outputs can let an attacker predict future codes. This PR switches to secrets.choice over the 62-symbol alphanumeric alphabet.

Changes

  • quantara/web_app/api/referal.py: secrets.choice + shared alphabet
  • quantara/web_app/tests/test_create_referal_link.py: source/CSPRNG checks, uniqueness, distribution/entropy sanity

Test plan

  • Local unit check: length 16, alphabet-only, uniqueness across 100 draws
  • Source inspection: no random.choices / random.random
  • Distribution test over 10k draws (frequency band)
  • CI full suite

Replace non-crypto random.choices with secrets.choice over the
62-symbol alphanumeric alphabet so referral codes cannot be predicted
from prior outputs of the Mersenne Twister stream.

Closes Quantarq#196

Signed-off-by: NossXBT <kevinnft@users.noreply.github.com>
from web_app.api import referal

source = inspect.getsource(referal.generate_random_string)
assert "secrets" in source

source = inspect.getsource(referal.generate_random_string)
assert "secrets" in source
assert "random.choices" not in source
source = inspect.getsource(referal.generate_random_string)
assert "secrets" in source
assert "random.choices" not in source
assert "random.random" not in source

alphabet = string.ascii_letters + string.digits
code = referal.generate_random_string(16)
assert len(code) == 16
alphabet = string.ascii_letters + string.digits
code = referal.generate_random_string(16)
assert len(code) == 16
assert all(ch in alphabet for ch in code)

# Sanity: many draws stay unique enough that collision is rare.
codes = {referal.generate_random_string(16) for _ in range(200)}
assert len(codes) == 200
# ±0.5% absolute frequency band around 1/62 as specified in #196.
for ch in alphabet:
freq = counts[ch] / total
assert abs(freq - (1 / len(alphabet))) <= 0.005, (
import math

entropy_bits = length * math.log2(len(alphabet))
assert entropy_bits >= 95
counts.update(generate_random_string(length))

total = draws * length
expected = total / len(alphabet)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cryptographically randomise referal.generate_random_string

2 participants