The Hash Stamper
An easy Python interview practice problem on DataDriven. Write the Python and run it against test cases, with instant feedback.
- Domain
- Python
- Difficulty
- easy
- Seniority
- L3
- Asked in
- Rally Health
Problem
Our seeds are free-form UTF-8 text, accented characters included, and a given seed has to produce the same token on every machine. We stamp it by taking the SHA-256 hex digest of the seed and keeping its first length characters.
One input, one irreversible output - the foundation of every secret.
Example 1
Input:
seed = "secret", length = 8
Output:
"2bb80d53"
Example 2
Input:
seed = "password", length = 4
Output:
"5e88"
Worked solution and explanation
What this is really testing
Strip the costume and this is a str/bytes boundary drill wearing a token-generation hat. Anyone can recite SHA-256. What separates candidates is knowing that hashlib eats bytes and hands back a str, and crossing that line on purpose. The trap is calling hashlib.sha256(seed) on a bare string, or grabbing .digest() when you needed .hexdigest(). Miss it and you either throw a TypeError at call time or return raw bytes that silently poison the idempotency key this digest was meant to stamp.
Where this shows up on the job
Deterministic tokens are the backbone of idempotency keys, cache busting and license stamping. One short function quietly probes three things. Do you know hashlib? Do you encode text to bytes before hashing? And do you know a SHA-256 hex digest is exactly 64 characters, so every length request is bounded?
Break down the requirements
Step 1: Encode the seed to bytes
hashlib constructors take bytes, not str. Use seed.encode('utf-8'): skipping it raises TypeError, and naming UTF-8 explicitly pins the byte representation. That way an accented seed like 'café' hashes the same on every machine, which is exactly the property the statement leads with.
Step 2: Take the SHA-256 hex digest
hashlib.sha256(...).hexdigest() returns a 64-character lowercase hex string. The move that matters is .hexdigest(), not .digest(). The latter returns raw bytes, and slicing bytes gives you bytes, which breaks the string contract the examples show. SHA-256 is the right default: available everywhere, fast, and not broken the way MD5 and SHA-1 are for security uses.
Step 3: Slice the first length characters
Slicing never raises on an out-of-range stop, so digest[:length] returns the whole digest when length exceeds 64. Every request asks for at least one character, so there is no zero guard to write. Even so, saying the 64-character ceiling out loud is the kind of real constraint an interviewer notices.
The solution
import hashlib
def generate_password_from_seed(seed: str, length: int) -> str:
digest = hashlib.sha256(seed.encode('utf-8')).hexdigest()
return digest[:length]Common follow-up questions
- What changes if `length` can exceed 64 characters? (Hash repeatedly with a counter (HKDF-style), or concatenate the digests of `seed || i`, and explain what that does to the pseudo-randomness guarantees.)
- How would you make this safe for password storage? (Switch to bcrypt, scrypt or argon2 with a per-user salt, and explain key stretching and why a fast hash like SHA-256 is the wrong tool.)
- How would you ensure the same seed produces the same token across machines and Python versions? (Explicit UTF-8 encoding pins the bytes, and SHA-256 is a standard, so the digest is portable across every `hashlib` backend.)