ASVS 5.0 V6.7.2 requires the challenge nonce used in cryptographic authentication to be at least 64 bits long and statistically unique, or unique over the lifetime of the cryptographic device. It is a Level 3 requirement.
Why it matters
Challenge response proves possession of a key by signing a fresh value. If the challenge repeats, an attacker records one valid signature and replays it. A short challenge makes repeats likely, so a collision becomes a practical attack.
How to implement it
Generate the challenge server side from a secure random source, at least 8 bytes, and 16 or 32 is cheap and gives margin. Store it with an expiry and delete it after one use.
import secrets
def new_challenge(session_id: str) -> bytes:
nonce = secrets.token_bytes(32) # 256 bits
cache.set(f"challenge:{session_id}", nonce, ttl=120)
return nonce
def verify(session_id, signature, public_key):
nonce = cache.pop(f"challenge:{session_id}") # single use
if nonce is None:
return False
return public_key.verify(signature, nonce)
For WebAuthn, the library requires a random challenge of at least 16 bytes, so the same pattern applies.
How to test it
- Request 10,000 challenges and check lengths and duplicates.
- Replay a captured signed response and confirm it fails.
- Confirm challenges are not derived from time or counters.
Common mistakes
- A timestamp or counter as the nonce.
- Client generated challenges.
- Reusing a challenge across attempts.
Quick check
Is a 64 bit random challenge enough if reused?
No. Uniqueness matters as much as length. Each challenge must be consumed once.