Predict the output
What do the three lines print?
import base64
import hashlib
import hmac
import json
def b64(data: bytes) -> str:
return base64.urlsafe_b64encode(data).rstrip(b"=").decode()
def unb64(s: str) -> bytes:
return base64.urlsafe_b64decode(s + "=" * (-len(s) % 4))
def sign(claims: dict[str, object], key: bytes) -> str:
head = b64(json.dumps({"alg": "HS256", "typ": "JWT"}).encode())
body = b64(json.dumps(claims).encode())
mac = hmac.new(key, f"{head}.{body}".encode(), hashlib.sha256)
return f"{head}.{body}.{b64(mac.digest())}"
def verify(token: str, key: bytes, now: int) -> dict[str, object]:
head, body, sig = token.split(".")
if json.loads(unb64(head)).get("alg") != "HS256":
raise ValueError("unexpected alg")
mac = hmac.new(key, f"{head}.{body}".encode(), hashlib.sha256)
if not hmac.compare_digest(b64(mac.digest()), sig):
raise ValueError("bad signature")
claims: dict[str, object] = json.loads(unb64(body))
exp = claims.get("exp")
if not isinstance(exp, int) or exp <= now:
raise ValueError("expired")
return claims
key = b"server-side-secret"
tok = sign({"sub": "user-7", "exp": 2_000_000_000}, key)
print(json.loads(unb64(tok.split(".")[1]))["sub"]) # anyone can read it
print(verify(tok, key, now=1_900_000_000)["sub"])
try:
verify(tok, b"wrong-key", now=1_900_000_000)
except ValueError as e:
print(e)Hint 1 of 1
Is the payload encrypted?
Choose an answer, then open Show answer below to check it.
Show answer
Answer: A user-7, user-7, bad signature
Correct. The payload is readable without the key; verification with the right key passes and the wrong key fails.
A JWT payload is only base64url, so anyone can read it. The signature is what proves it came from you: recomputing the HMAC with the right key matches, and with any other key it doesn't.
Why not the others?
- B An error, then
user-7,bad signature. Reading the payload needs no key: it's only base64url. - C
user-7,user-7,user-7. A different key produces a different MAC. - D
user-7,expired,bad signature. exp is 2,000,000,000, after the 'now' passed in.