Math, Randomness, and Cryptography

Updated

September 7, 2026

Math, Randomness, and Cryptography

After reading this chapter, you will master Python’s numerical precision tools (math, decimal.Decimal, fractions.Fraction), contrast pseudo-random generators (random) with cryptographically secure generators (secrets), execute cryptographic hashing with hashlib, and verify message authentication codes safely with hmac.compare_digest to eliminate timing attack vulnerabilities.

Mental model

Python provides two completely different engines for randomness and number processing:

Randomness Engines:
  random module (PRNG - Pseudo-Random)
  └── Mersenne Twister algorithm (MT19937)
      ├── Period: 2^19937 - 1
      ├── Predictable: Observing 624 outputs reveals the internal state!
      └── Purpose: Simulations, Monte Carlo algorithms, games, shuffling

  secrets module (CSPRNG - Cryptographically Secure)
  └── Direct kernel entropy pool (/dev/urandom, Windows BCrypt)
      ├── Unpredictable: Suitable for security secrets
      └── Purpose: API keys, session tokens, passwords, CSRF tokens

For financial ledgers and billing calculations, IEEE 754 binary floats (float) introduce rounding errors (\(0.1 + 0.2 \neq 0.3\)). The decimal.Decimal module performs exact base-10 arithmetic.


Minimal example

Save as crypto_math_showcase.py:

# crypto_math_showcase.py
from decimal import Decimal, ROUND_HALF_UP
import hashlib
import hmac
import secrets

def main() -> None:
    # 1. Exact financial arithmetic with Decimal
    price = Decimal("19.99")
    tax_rate = Decimal("0.0825")  # 8.25%
    tax = (price * tax_rate).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
    total = price + tax
    print(f"Subtotal: ${price} | Tax: ${tax} | Total: ${total}")

    # 2. Secure Token Generation with secrets (CSPRNG)
    api_token = secrets.token_urlsafe(32)
    hex_salt = secrets.token_hex(16)
    print(f"\nGenerated secure API token : {api_token}")
    print(f"Generated cryptographic salt: {hex_salt}")

    # 3. SHA-256 Hashing with hashlib
    payload = b"GET /api/v1/clusters HTTP/1.1"
    digest = hashlib.sha256(payload).hexdigest()
    print(f"\nSHA-256 Digest              : {digest}")

    # 4. Timing-Safe HMAC verification
    secret_key = b"super-secret-cluster-signing-key"
    signature = hmac.new(secret_key, payload, hashlib.sha256).hexdigest()
    
    # hmac.compare_digest protects against side-channel timing attacks
    is_valid = hmac.compare_digest(signature, signature)
    print(f"HMAC Signature              : {signature}")
    print(f"Timing-safe signature valid?: {is_valid}")

if __name__ == "__main__":
    main()

Run via uv run python crypto_math_showcase.py:

Subtotal: $19.99 | Tax: $1.65 | Total: $21.64

Generated secure API token : ...
Generated cryptographic salt: ...

SHA-256 Digest              : 2d1478546b595bb97970d4f6c44933a39e830e2f5f190eecfa519c5c7784da31

HMAC Signature              : ...
Timing-safe signature valid?: True

Worked examples

Case 1: Timing Attack Defense with hmac.compare_digest

When checking whether an API secret or webhook token matches an expected value, using Python’s standard == operator is dangerous. The == operator performs an early exit as soon as the first non-matching byte is found, leaking timing information that allows attackers to recover the secret byte by byte:

# timing_attack_demo.py
import hmac
import time

def naive_verify(provided: str, expected: str) -> bool:
    # VULNERABILITY: Early return leaks timing measurements
    return provided == expected

def secure_verify(provided: str, expected: str) -> bool:
    # Constant-time comparison: takes the same duration regardless of matching bytes
    return hmac.compare_digest(provided, expected)

if __name__ == "__main__":
    correct_token = "tok_live_998877665544332211"
    attacker_candidate = "tok_live_000000000000000000"

    print("Timing-safe comparison result:", secure_verify(attacker_candidate, correct_token))

Run:

uv run python timing_attack_demo.py

Output:

Timing-safe comparison result: False

Case 2: Exact Rational Fractions with fractions.Fraction

When modeling rates, probabilities, or unit proportions that must avoid any loss of precision, fractions.Fraction performs exact numerator/denominator math:

# exact_fractions.py
from fractions import Fraction

def calculate_quorum() -> None:
    # Quorum requiring 2/3 majority
    f1 = Fraction(1, 3)
    f2 = Fraction(1, 6)
    combined = f1 + f2
    print(f"1/3 + 1/6 = {combined} (Numerator: {combined.numerator}, Denominator: {combined.denominator})")

    # Reconstruct from float with exact limit
    approx = Fraction("0.75")
    print(f"Fraction for 0.75: {approx}")

if __name__ == "__main__":
    calculate_quorum()

Run:

uv run python exact_fractions.py

Output:

1/3 + 1/6 = 1/2 (Numerator: 1, Denominator: 2)
Fraction for 0.75: 3/4

Pitfalls

Pitfall 1: Using random for Security Tokens

The random module uses the Mersenne Twister algorithm. It is completely deterministic and non-cryptographic:

# DANGEROUS: Predictable token!
import random
token = f"{random.randint(100000, 999999)}"  # NEVER DO THIS FOR AUTH OR PASSWORDS!

# SECURE:
import secrets
token = secrets.token_urlsafe(32)

Pitfall 2: Initializing Decimal from a float

Passing a raw float into Decimal() captures the IEEE 754 binary floating-point representation errors:

from decimal import Decimal

# THE BUG:
d = Decimal(0.1)
print(d)  # Decimal('0.1000000000000000055511151231257827021181583404541015625')

# THE FIX: Always pass strings!
d = Decimal("0.1")
print(d)  # Decimal('0.1')

Exercises

  1. Write a billing calculator using Decimal that computes sales tax and applies a percentage discount, rounding to 2 decimal places using ROUND_HALF_EVEN.
  2. Generate a secure 16-character random alphanumeric password using secrets.choice across uppercase, lowercase, and digit character sets.
  3. Compute the SHA-512 hash of a text string and format the result as an uppercase hexadecimal digest.
  4. Implement a message signature verification function using hmac with SHA-256 and verify it against test vectors.

Further reading

  • PEP 506: Adding A Secrets Module To The Standard Library.
  • Python Standard Library: decimal, fractions, secrets, hashlib, hmac.
  • RFC 2104: HMAC: Keyed-Hashing for Message Authentication.