Open-Source Codewheel Generator: Projects to Start From

How to Build a Codewheel Generator — Step-by-Step GuideA codewheel generator creates rotating codes mapped to positions on a wheel — useful for one-time passwords, hardware tokens, escape-room puzzles, or software licensing schemes. This guide walks through the design and implementation of a robust, testable codewheel generator you can run locally or integrate into services. We’ll cover concepts, algorithms, security considerations, implementation examples (Python), testing, and deployment options.


What is a codewheel generator?

A codewheel is a circular lookup structure: a sequence of code values arranged around a wheel where each rotation or step produces a new code. A generator programmatically creates the wheel (a repeatable sequence) and produces codes based on an index (position) plus optional secrets or salts. Unlike purely random OTP systems, codewheels are deterministic given the seed and algorithm, enabling verification without storing all codes.

Use cases

  • One-time physical tokens and backup codes
  • Escape-room clue systems and puzzle design
  • Offline software activation where verifier can compute expected codes
  • Educational demonstrations of PRNGs and hash functions

Design choices & security model

Decide early what you need in terms of security, usability, and offline capability.

  • Deterministic vs. nondeterministic: deterministic lets verifiers reproduce codes from a seed; nondeterministic requires storage.
  • Seed/secret management: do you use a per-device secret, a shared master key, or no secret? More secret material increases security.
  • Code length and alphabet: longer codes and larger alphabets increase entropy.
  • Rotation mechanism: linear index, time-based steps, or challenge–response (input affects output).
  • Verification: stateless (compute expected code) vs. stateful (store used indices to prevent reuse).

Threat model examples:

  • Attacker knows algorithm but not seed — seed protection is critical.
  • Attacker can observe some codes — protect against brute-force by using sufficiently large code space and rate-limiting verification.

Cryptographic building blocks

Pick primitives appropriate for your threat model.

  • HMAC-SHA256: common choice for deterministic, keyed code generation.
  • HKDF: derive subkeys and reduce key reuse risks.
  • AES-CTR or AES-ECB (not recommended alone for code generation) — use only if you understand block cipher modes.
  • CSPRNG for seed generation when creating new wheels.

Entropy guidance:

  • For short numeric codes (6 digits), entropy ≈ 20 bits — acceptable with rate limits for low-risk use.
  • For stronger security, use 128 bits of key material and codes of at least 8–12 characters from a larger alphabet.

Algorithm outline

We’ll implement a simple, secure, deterministic generator with these properties:

  • Uses a secret seed (128–256 bits).
  • Generates codes for integer positions (index) and optional context (device ID, date).
  • Outputs codes using a chosen alphabet and fixed length.
  • Verifier recomputes expected code from seed + index.

High-level steps:

  1. Seed generation: create a secure random seed stored safely.
  2. Key derivation: derive an index-specific key using HKDF(seed, info=index||context).
  3. HMAC: compute HMAC-SHA256 of index and context with the derived key.
  4. Reduce to desired alphabet/length via modular reduction or base-N encoding.
  5. Optionally apply checksum or error-detection.

Implementation: Python example

Below is a complete, well-documented Python implementation. It builds a wheel (list of codes) for a range of indices and shows verification. Replace storage and secret handling with secure alternatives in production.

# file: codewheel.py import os import hmac import hashlib import secrets from typing import List ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"  # Crockford-like, no ambiguous chars def generate_seed(num_bytes: int = 32) -> bytes:     """Return a cryptographically secure random seed."""     return secrets.token_bytes(num_bytes) def hkdf_extract_expand(salt: bytes, ikm: bytes, info: bytes, length: int = 32) -> bytes:     """     Simple HKDF (RFC 5869) using HMAC-SHA256.     Returns length bytes of derived key material.     """     prk = hmac.new(salt or b''*32, ikm, hashlib.sha256).digest()     okm = b""     t = b""     i = 1     while len(okm) < length:         t = hmac.new(prk, t + info + bytes([i]), hashlib.sha256).digest()         okm += t         i += 1     return okm[:length] def int_to_base_n(num: int, alphabet: str, length: int) -> str:     """Encode integer to fixed-length base-N using provided alphabet."""     base = len(alphabet)     out = []     for _ in range(length):         out.append(alphabet[num % base])         num //= base     return ''.join(reversed(out)) def generate_code(seed: bytes, index: int, length: int = 8, alphabet: str = ALPHABET, context: bytes = b'') -> str:     """     Deterministic code for a given index and context using HKDF+HMAC.     - seed: secret seed bytes     - index: integer position     - length: code length in characters     - alphabet: allowed characters     - context: optional context bytes (device id, date)     """     info = context + b'|' + str(index).encode('utf-8')     derived = hkdf_extract_expand(salt=None, ikm=seed, info=info, length=32)     mac = hmac.new(derived, info, hashlib.sha256).digest()     # Use first 8-16 bytes of mac as integer entropy     num = int.from_bytes(mac[:8], 'big')     total_states = len(alphabet) ** length     idx = num % total_states     return int_to_base_n(idx, alphabet, length) def build_wheel(seed: bytes, start: int, count: int, **kwargs) -> List[str]:     """Return a list of codes for indices start..start+count-1."""     return [generate_code(seed, i, **kwargs) for i in range(start, start + count)] def verify_code(seed: bytes, index: int, code: str, **kwargs) -> bool:     """Bool whether code matches generated code for index."""     expected = generate_code(seed, index, length=len(code), **kwargs)     return hmac.compare_digest(expected, code) if __name__ == "__main__":     # demo     seed = generate_seed(32)     wheel = build_wheel(seed, start=0, count=20, length=8)     for i, c in enumerate(wheel):         print(f"{i:03d}: {c}")     # verify     test_index = 5     print("verify index 5:", verify_code(seed, test_index, wheel[test_index])) 

Notes:

  • HKDF implementation here is minimal but follows RFC 5869 structure. Use cryptography library HKDF in production for safety.
  • Use secure seed storage: hardware secure element, OS keyring, or encrypted file.
  • If you need time-based codes, use time slot number as index (like TOTP).

Variations & features

  • Time-based wheel: index = floor(unix_time / step_seconds). Matches TOTP concept.
  • Challenge–response: include challenge bytes in context so code changes per challenge.
  • Checksum characters: append a simple Luhn-like checksum to detect mistypes.
  • Rate-limiting and reuse protection: track used indices server-side, or allow a sliding window for acceptance.

Comparison of common approaches:

Approach Stateless verification Security level Offline-capable
Deterministic HMAC/HKDF Yes High (if seed secret) Yes
Stored random codes Yes (if stored) High Yes (if stored locally)
Time-based (TOTP) Yes High Yes

Testing & validation

  • Unit tests: generate wheel, verify expected codes for known seeds/indices.
  • Interoperability test: ensure other implementations with same seed produce same codes.
  • Fuzz tests: random seeds/indices to check for collisions or invalid chars.
  • Security tests: attempt brute-force against short codes; ensure rate limiting.

Example unit test (pytest):

def test_generate_and_verify():     seed = b''*32     c = generate_code(seed, 42, length=6)     assert verify_code(seed, 42, c)     assert not verify_code(seed, 43, c) 

Deployment & operational concerns

  • Secret rotation: plan a rotation scheme; store version numbers in context so both sides know seed versions.
  • Backup and recovery: securely back up seeds; loss means inability to verify old codes.
  • Logging: avoid logging seeds or full codes in plaintext. Log only hashed/obfuscated values if needed.
  • Scalability: verification is CPU-light (HMAC), but protect endpoints with rate limits and caching.

Example applications & extensions

  • Physical codewheels: print generated codes on a circular card for offline use; include index markers.
  • Licensing: encode product id in context to bind wheel to a product.
  • Multi-factor auth: use codewheel as backup codes that can be validated server-side.

Summary

This guide gives a practical, secure approach to building a deterministic codewheel generator using a seed, HKDF, and HMAC to produce reproducible codes by index. The included Python implementation is a starting point—harden it with vetted crypto libraries, secure storage, and operational safeguards for production use.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *