casebench
test cases straight onto the board.
git clone https://owenrusk.dev/casebench.git
commit d7077748bc1326bdc2a12408215fd70d1fe8271d parent 1348d3064dbe12858c07a6ffbbd956b9c210b6ac author Owen Rusk <owen@papermothgames.com> date 2025-05-07 11:31:20 -0500
lock: salted scrypt, not bare sha256 a bare sha256 of a one-word answer is a lookup, not a lock.
| casebench/cli.py | +2 | -1 |
| casebench/db.py | +4 | -4 |
| casebench/lock.py | +11 | -4 |
diff --git a/casebench/cli.py b/casebench/cli.py index a493488..7c7a768 100644 --- a/casebench/cli.py +++ b/casebench/cli.py @@ -82,5 +82,6 @@ def run_lock(conn: psycopg.Connection, args: argparse.Namespace) -> None: who = player(conn, args.player) if db.source(conn, who, args.case) is None: raise Fail(f"{args.player} doesn't have {args.case}")- db.lock(conn, who, args.case, args.text, lock.digest(args.answer))+ salt, answer_hash = lock.make(args.answer) + db.lock(conn, who, args.case, args.text, salt, answer_hash) print(f"{args.player}: locked {args.case}") diff --git a/casebench/db.py b/casebench/db.py index 54293ab..6dc6b31 100644 --- a/casebench/db.py +++ b/casebench/db.py @@ -66,11 +66,11 @@ def note(conn: psycopg.Connection, player: str, case_id: str, item_id: str | Non conn.commit()-def lock(conn: psycopg.Connection, player: str, case_id: str, text: str, answer_hash: str) -> None:+def lock(conn: psycopg.Connection, player: str, case_id: str, text: str, salt: bytes, answer_hash: str) -> None: conn.execute(- "insert into board_locks (player_id, case_id, text, answer_hash) values (%s, %s, %s, %s)"+ "insert into board_locks (player_id, case_id, text, salt, answer_hash) values (%s, %s, %s, %s, %s)" " on conflict (player_id, case_id) do update"- " set text = excluded.text, answer_hash = excluded.answer_hash, opened_at = null",- (player, case_id, text, answer_hash),+ " set text = excluded.text, salt = excluded.salt, answer_hash = excluded.answer_hash, opened_at = null", + (player, case_id, text, salt, answer_hash), ) conn.commit() diff --git a/casebench/lock.py b/casebench/lock.py index cce8057..c9e4ab9 100644 --- a/casebench/lock.py +++ b/casebench/lock.py @@ -1,14 +1,21 @@ import hashlib +import hmac +import os def normalize(answer: str) -> str: return " ".join(answer.casefold().split())-def digest(answer: str) -> str:- return hashlib.sha256(normalize(answer).encode()).hexdigest()+def digest(answer: str, salt: bytes) -> str: + return hashlib.scrypt(normalize(answer).encode(), salt=salt, n=2**14, r=8, p=1, dklen=32).hex()-def opens(stored: str, typed: str) -> bool:+def make(answer: str) -> tuple[bytes, str]: + salt = os.urandom(16) + return salt, digest(answer, salt) + + +def opens(salt: bytes, stored: str, typed: str) -> bool: # the board calls this when someone types into a lock.- return digest(typed) == stored+ return hmac.compare_digest(digest(typed, salt), stored)