secrets¶
Cryptographically secure token generation.¶
Cryptographically secure random choice.¶
Cryptographically secure random integer below n.¶
Constant-time comparison for security.¶
SystemRandom for cryptographic randomness.¶
Generate secure passwords and tokens.¶
Edge cases and special scenarios.¶
Basic: Generate random bytes.¶
def test_token_bytes(self):
"""Basic: Generate random bytes."""
token = secrets.token_bytes(16)
assert len(token) == 16
assert isinstance(token, bytes)
Verification: ✅ Tested in CI
Feature: Default token_bytes size.¶
def test_token_bytes_default(self):
"""Feature: Default token_bytes size."""
token = secrets.token_bytes()
assert isinstance(token, bytes)
assert len(token) == 32
Verification: ✅ Tested in CI
Property: Each call produces unique token.¶
def test_token_bytes_uniqueness(self):
"""Property: Each call produces unique token."""
token1 = secrets.token_bytes(16)
token2 = secrets.token_bytes(16)
assert token1 != token2
Verification: ✅ Tested in CI
Basic: Generate hex token.¶
def test_token_hex(self):
"""Basic: Generate hex token."""
token = secrets.token_hex(16)
assert len(token) == 32
assert isinstance(token, str)
assert all((c in '0123456789abcdef' for c in token))
Verification: ✅ Tested in CI
Feature: Default token_hex size.¶
def test_token_hex_default(self):
"""Feature: Default token_hex size."""
token = secrets.token_hex()
assert isinstance(token, str)
assert len(token) == 64
Verification: ✅ Tested in CI
Basic: Generate URL-safe token.¶
def test_token_urlsafe(self):
"""Basic: Generate URL-safe token."""
token = secrets.token_urlsafe(16)
assert isinstance(token, str)
assert all((c.isalnum() or c in '-_' for c in token))
Verification: ✅ Tested in CI
Feature: Default token_urlsafe size.¶
def test_token_urlsafe_default(self):
"""Feature: Default token_urlsafe size."""
token = secrets.token_urlsafe()
assert isinstance(token, str)
assert all((c.isalnum() or c in '-_' for c in token))
Verification: ✅ Tested in CI
Property: No padding characters in URL-safe token.¶
def test_token_urlsafe_no_padding(self):
"""Property: No padding characters in URL-safe token."""
token = secrets.token_urlsafe(16)
assert '=' not in token
Verification: ✅ Tested in CI
Basic: Choose from sequence.¶
def test_choice(self):
"""Basic: Choose from sequence."""
items = [1, 2, 3, 4, 5]
choice = secrets.choice(items)
assert choice in items
Verification: ✅ Tested in CI
Feature: Choose from string.¶
def test_choice_string(self):
"""Feature: Choose from string."""
s = 'abcde'
choice = secrets.choice(s)
assert choice in s
Verification: ✅ Tested in CI
Property: Multiple choices can differ.¶
def test_choice_uniqueness(self):
"""Property: Multiple choices can differ."""
items = list(range(100))
choices = [secrets.choice(items) for _ in range(10)]
assert len(set(choices)) > 1
Verification: ✅ Tested in CI
Error: Choice from empty sequence.¶
def test_error_choice_empty(self):
"""Error: Choice from empty sequence."""
with pytest.raises(IndexError):
secrets.choice([])
Verification: ✅ Tested in CI
Basic: Random integer below n.¶
def test_randbelow(self):
"""Basic: Random integer below n."""
r = secrets.randbelow(10)
assert 0 <= r < 10
assert isinstance(r, int)
Verification: ✅ Tested in CI
Property: Always in correct range.¶
def test_randbelow_range(self):
"""Property: Always in correct range."""
for _ in range(100):
r = secrets.randbelow(100)
assert 0 <= r < 100
Verification: ✅ Tested in CI
Edge: randbelow(1) always returns 0.¶
def test_randbelow_one(self):
"""Edge: randbelow(1) always returns 0."""
r = secrets.randbelow(1)
assert r == 0
Verification: ✅ Tested in CI
Property: Should cover full range over time.¶
def test_randbelow_distribution(self):
"""Property: Should cover full range over time."""
n = 10
results = {secrets.randbelow(n) for _ in range(100)}
assert len(results) > 1
Verification: ✅ Tested in CI
Error: randbelow(0) raises ValueError.¶
def test_error_randbelow_zero(self):
"""Error: randbelow(0) raises ValueError."""
with pytest.raises(ValueError):
secrets.randbelow(0)
Verification: ✅ Tested in CI
Error: randbelow with negative raises ValueError.¶
def test_error_randbelow_negative(self):
"""Error: randbelow with negative raises ValueError."""
with pytest.raises(ValueError):
secrets.randbelow(-1)
Verification: ✅ Tested in CI
Basic: Equal strings compare as True.¶
def test_compare_digest_equal_strings(self):
"""Basic: Equal strings compare as True."""
a = 'secret123'
b = 'secret123'
assert secrets.compare_digest(a, b) is True
Verification: ✅ Tested in CI
Basic: Unequal strings compare as False.¶
def test_compare_digest_unequal_strings(self):
"""Basic: Unequal strings compare as False."""
a = 'secret123'
b = 'secret456'
assert secrets.compare_digest(a, b) is False
Verification: ✅ Tested in CI
Feature: Compare bytes.¶
def test_compare_digest_bytes(self):
"""Feature: Compare bytes."""
a = b'secret123'
b = b'secret123'
assert secrets.compare_digest(a, b) is True
Verification: ✅ Tested in CI
Feature: Unequal bytes compare as False.¶
def test_compare_digest_bytes_unequal(self):
"""Feature: Unequal bytes compare as False."""
a = b'secret123'
b = b'secret456'
assert secrets.compare_digest(a, b) is False
Verification: ✅ Tested in CI
Edge: Different lengths compare as False.¶
def test_compare_digest_different_lengths(self):
"""Edge: Different lengths compare as False."""
a = 'secret'
b = 'secret123'
assert secrets.compare_digest(a, b) is False
Verification: ✅ Tested in CI
Edge: Empty strings compare as True.¶
def test_compare_digest_empty_strings(self):
"""Edge: Empty strings compare as True."""
assert secrets.compare_digest('', '') is True
Verification: ✅ Tested in CI
Property: Comparison is constant-time (timing-safe).¶
def test_compare_digest_constant_time(self):
"""Property: Comparison is constant-time (timing-safe)."""
a = 'a' * 100
b = 'a' * 99 + 'b'
assert secrets.compare_digest(a, b) is False
Verification: ✅ Tested in CI
Basic: SystemRandom is available via secrets.¶
def test_systemrandom_available(self):
"""Basic: SystemRandom is available via secrets."""
r = secrets.randbelow(10)
assert isinstance(r, int)
Verification: ✅ Tested in CI
Property: Cannot be seeded (cryptographically secure).¶
def test_systemrandom_not_reproducible(self):
"""Property: Cannot be seeded (cryptographically secure)."""
r1 = secrets.token_bytes(16)
r2 = secrets.token_bytes(16)
assert r1 != r2
Verification: ✅ Tested in CI
Use case: Generate secure password.¶
def test_generate_password_basic(self):
"""Use case: Generate secure password."""
alphabet = string.ascii_letters + string.digits
password = ''.join((secrets.choice(alphabet) for _ in range(10)))
assert len(password) == 10
assert all((c in alphabet for c in password))
Verification: ✅ Tested in CI
Use case: Password with special characters.¶
def test_generate_password_with_punctuation(self):
"""Use case: Password with special characters."""
alphabet = string.ascii_letters + string.digits + string.punctuation
password = ''.join((secrets.choice(alphabet) for _ in range(12)))
assert len(password) == 12
Verification: ✅ Tested in CI
Use case: Generate secure reset token.¶
def test_generate_secure_token(self):
"""Use case: Generate secure reset token."""
token = secrets.token_urlsafe(32)
assert len(token) > 0
assert all((c.isalnum() or c in '-_' for c in token))
Verification: ✅ Tested in CI
Use case: Generate API key.¶
def test_generate_api_key(self):
"""Use case: Generate API key."""
api_key = secrets.token_hex(32)
assert len(api_key) == 64
assert all((c in '0123456789abcdef' for c in api_key))
Verification: ✅ Tested in CI
Edge: Zero-length token.¶
def test_token_bytes_zero(self):
"""Edge: Zero-length token."""
token = secrets.token_bytes(0)
assert token == b''
Verification: ✅ Tested in CI
Edge: Zero-length hex token.¶
def test_token_hex_zero(self):
"""Edge: Zero-length hex token."""
token = secrets.token_hex(0)
assert token == ''
Verification: ✅ Tested in CI
Edge: Zero-length URL-safe token.¶
def test_token_urlsafe_zero(self):
"""Edge: Zero-length URL-safe token."""
token = secrets.token_urlsafe(0)
assert token == ''
Verification: ✅ Tested in CI
Performance: Large token generation.¶
def test_token_bytes_large(self):
"""Performance: Large token generation."""
token = secrets.token_bytes(1024)
assert len(token) == 1024
Verification: ✅ Tested in CI
Edge: Choice from single element.¶
def test_choice_single_element(self):
"""Edge: Choice from single element."""
choice = secrets.choice([42])
assert choice == 42
Verification: ✅ Tested in CI
Performance: randbelow with large n.¶
def test_randbelow_large(self):
"""Performance: randbelow with large n."""
r = secrets.randbelow(1000000)
assert 0 <= r < 1000000
Verification: ✅ Tested in CI
Property: Tokens have high entropy.¶
def test_token_entropy(self):
"""Property: Tokens have high entropy."""
tokens = [secrets.token_bytes(16) for _ in range(100)]
assert len(set(tokens)) == 100
Verification: ✅ Tested in CI
Property: Hex tokens are lowercase.¶
def test_hex_lowercase(self):
"""Property: Hex tokens are lowercase."""
token = secrets.token_hex(16)
assert token == token.lower()
assert token.islower() or token.isdigit() or all((c in '0123456789' for c in token))
Verification: ✅ Tested in CI
Property: URL-safe tokens use base64url encoding.¶
def test_urlsafe_base64_variant(self):
"""Property: URL-safe tokens use base64url encoding."""
token = secrets.token_urlsafe(16)
assert '+' not in token
assert '/' not in token
Verification: ✅ Tested in CI
Error: Type mismatch in compare_digest.¶
def test_compare_digest_type_mismatch(self):
"""Error: Type mismatch in compare_digest."""
with pytest.raises(TypeError):
secrets.compare_digest('string', b'bytes')
Verification: ✅ Tested in CI
Property: Multiple tokens are unique.¶
def test_multiple_tokens_unique(self):
"""Property: Multiple tokens are unique."""
tokens = [secrets.token_hex(16) for _ in range(50)]
assert len(tokens) == len(set(tokens))
Verification: ✅ Tested in CI
Feature: Choice works with tuple.¶
def test_choice_works_with_tuple(self):
"""Feature: Choice works with tuple."""
items = (1, 2, 3, 4, 5)
choice = secrets.choice(items)
assert choice in items
Verification: ✅ Tested in CI
Feature: Choice works with range.¶
def test_choice_works_with_range(self):
"""Feature: Choice works with range."""
r = range(10)
choice = secrets.choice(r)
assert choice in r
Verification: ✅ Tested in CI
Edge: randbelow(2) returns 0 or 1.¶
def test_randbelow_two(self):
"""Edge: randbelow(2) returns 0 or 1."""
results = {secrets.randbelow(2) for _ in range(50)}
assert results <= {0, 1}
assert len(results) > 1
Verification: ✅ Tested in CI
Property: Same nbytes gives same length.¶
def test_token_bytes_consistency(self):
"""Property: Same nbytes gives same length."""
for nbytes in [8, 16, 32, 64]:
token = secrets.token_bytes(nbytes)
assert len(token) == nbytes
Verification: ✅ Tested in CI
Property: Hex token is 2x byte length.¶
def test_token_hex_double_length(self):
"""Property: Hex token is 2x byte length."""
for nbytes in [8, 16, 32]:
token = secrets.token_hex(nbytes)
assert len(token) == nbytes * 2
Verification: ✅ Tested in CI
Property: compare_digest is case-sensitive.¶
def test_compare_digest_case_sensitive(self):
"""Property: compare_digest is case-sensitive."""
assert secrets.compare_digest('Secret', 'secret') is False
assert secrets.compare_digest('SECRET', 'secret') is False
Verification: ✅ Tested in CI
Property: Cryptographic random cannot be predicted.¶
def test_secure_random_not_seeded(self):
"""Property: Cryptographic random cannot be predicted."""
seq1 = [secrets.randbelow(100) for _ in range(10)]
seq2 = [secrets.randbelow(100) for _ in range(10)]
assert seq1 != seq2
Verification: ✅ Tested in CI