"""Four acts against four verifiers. Everything here is local; there is no
network, no service, and no real token anywhere in this repository.

    python3 demo.py
"""

import jwt

NOW = 1_700_000_000                                   # frozen clock
HONEST = {"sub": "alice", "role": "user", "exp": NOW + 3600}
GREEDY = {"sub": "alice", "role": "admin", "exp": NOW + 86400 * 365}

VERIFIERS = [
    ("v0 naive", lambda t: jwt.verify_naive(t, jwt.PUBLIC_KEY, NOW)),
    ("v1 -none", lambda t: jwt.verify_blocklist(t, jwt.PUBLIC_KEY, NOW)),
    ("v2 allow", lambda t: jwt.verify_allowlist(t, jwt.PUBLIC_KEY, NOW)),
    ("v3 strict", lambda t: jwt.verify_strict(t, jwt.PUBLIC_KEY, "RS256", NOW)),
]


def role(result):
    if result is None:
        return "reject"
    return "ADMIN" if result["role"] == "admin" else "ok:" + result["role"]


def show(label, result):
    print(f"  {label:<34} {role(result)}")


# The server issues RS256. Its verification key is public by construction.
token = jwt.encode(HONEST, "RS256", jwt.PRIVATE_KEY)

print("=== act 0: the honest token ===")
print(f"  {token[:44]}...")
print(f"  length                             {len(token)} chars")
h, p = jwt.peek(token)
print(f"  peek header (no key)               {h}")
print(f"  peek payload (no key)              {p}")
show("verify_naive", jwt.verify_naive(token, jwt.PUBLIC_KEY, NOW))

print()
print("=== act 1: tamper the payload, keep the signature ===")
hh, pp, ss = token.split(".")
one_byte = jwt.b64e(jwt.b64d(pp).replace(b"alice", b"alicf"))
show("sub alice->alicf (1 byte)",
     jwt.verify_naive(f"{hh}.{one_byte}.{ss}", jwt.PUBLIC_KEY, NOW))
escalated = jwt.b64e(jwt.b64d(pp).replace(b'"user"', b'"admin"'))
show("role user->admin",
     jwt.verify_naive(f"{hh}.{escalated}.{ss}", jwt.PUBLIC_KEY, NOW))
print("  ^ what everyone predicts. This is a MAC doing its job.")

print()
print("=== act 2: FORGERY A -- alg: none ===")
forged_none = jwt.encode(GREEDY, "none", None)
print(f"  {forged_none}")
print(f"  signature bytes                    {len(jwt.split(forged_none)[2])}")
for name, fn in VERIFIERS:
    show(name, fn(forged_none))

print()
print("=== act 3: FORGERY B -- HS256/RS256 key confusion ===")
print("  the attacker knows only PUBLIC_KEY, which is public on purpose:")
print(f"    {jwt.PUBLIC_KEY[:58].decode()}...  ({len(jwt.PUBLIC_KEY)} bytes)")
forged_conf = jwt.encode(GREEDY, "HS256", jwt.PUBLIC_KEY)
print(f"  {forged_conf}")
print(f"  signature bytes                    {len(jwt.split(forged_conf)[2])}")
for name, fn in VERIFIERS:
    show(name, fn(forged_conf))

print()
print("  " + "=" * 66)
print("  token                 " + "".join(f"{n:<11}" for n, _ in VERIFIERS))
print("  " + "-" * 66)
for name, tok in (("honest RS256", token),
                  ("alg:none", forged_none),
                  ("key confusion", forged_conf)):
    print(f"  {name:<22}" + "".join(f"{role(fn(tok)):<11}" for _, fn in VERIFIERS))
print("  " + "=" * 66)
