Add cryptographic puzzle integrity verification#43
Open
Lajishola wants to merge 1 commit into
Open
Conversation
…ctive#42) Adds SHA-256 content hashing so puzzle definitions can be verified before loading, preventing cheating via tampered local puzzle files: - Puzzle gains a content_hash field plus compute/generate/verify methods, hashed over a canonical view of its authored content (id, description, condition ids/descriptions, effects) so gameplay-state mutations never change the hash. - New PuzzleIntegrityError (missing/mismatched hash), wired into the existing centralized AppError via AppError::PuzzleIntegrity. - New loader module: load_puzzle_file / load_puzzles_from_dir verify hashes on every load; verify_puzzles_in_dir reports every failure instead of failing fast; generate_hashes_in_dir (re)stamps files. - CLI: `--verify-puzzles [dir]` checks all puzzle files and reports results; `--generate-hashes [dir] --admin` regenerates hashes and is gated behind an explicit --admin flag since it rewrites files in place. - Session/SessionData now carry puzzle_content_hashes so a completed session's submission to logiquest_api can include per-puzzle hashes for server-side verification (no logiquest_api client exists in this repo yet, so this only wires the data through to SessionData). - puzzles/starter-lever.json: a sample hashed puzzle file for the new loader/CLI to operate on. Unit tests cover valid-hash verification, tampered content, a missing hash field, and hash generation, per the issue's acceptance criteria.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #42.
Summary
Puzzlegains acontent_hash: Option<String>field pluscompute_content_hash/generate_content_hash/verify_content_hash. The hash is SHA-256 over a canonical view of the puzzle's authored content (id, description, condition ids/descriptions, effects) — gameplay-state fields (state,Condition::satisfied) are deliberately excluded so solving a puzzle never changes its hash.PuzzleIntegrityError(MissingHash/Mismatch), wired into the repo's existing centralizedAppErrorvia a newAppError::PuzzleIntegrityvariant (Fromimpl,Display,source()), consistent with howIo/Serdeerrors are already handled.loadermodule:load_puzzle_file/load_puzzles_from_dir— load and hash-verify puzzle JSON files, fail fast on the first bad file.verify_puzzles_in_dir— checks every file and returns a report per file (doesn't stop at the first failure), backing the CLI's--verify-puzzles.generate_hashes_in_dir— (re)stampscontent_hashfor every puzzle file in a directory.main.rs):--verify-puzzles [dir](defaults topuzzles/) — checks all puzzle files and prints an OK/FAIL report.--generate-hashes [dir] --admin— regenerates hashes; requires the explicit--adminflag since it rewrites puzzle files in place, per the issue's "admin use only" requirement.Session/SessionDatagainpuzzle_content_hashes, via a newSession::complete_puzzle_with_hash, so a completed session carries per-puzzle content hashes for submission tologiquest_api. Note: this repo doesn't currently have alogiquest_apiHTTP client, so this PR only wires the hash data through toSessionData— actual submission is left to whatever eventually implements that client.puzzles/starter-lever.json— a sample puzzle file with a validcontent_hash, so--verify-puzzleshas something to check out of the box.sha2 = "0.10"added as the hashing dependency (chosen overblake3to avoid a C build dependency, given both were acceptable per the issue).Test plan
cargo test— unit tests cover valid-hash verification, tampered description/effects, a missing hash field, hash generation, and that gameplay mutations don't change the hash (inpuzzle::tests,loader::tests,session::tests,errors::tests).cargo run -- --verify-puzzlesreportsOK puzzles/starter-lever.json.cargo run -- --generate-hashes puzzles --adminregenerates hashes in place; running without--adminrefuses with an explanatory message.cargo build --all-targets,cargo test,cargo clippy -- -D warnings,cargo fmt --check) passes.I wasn't able to compile locally in my environment (missing Windows SDK / MSVC linker, and a space-in-path bug in the available MinGW toolchain) but reviewed the diff carefully by hand; the repo's CI runs on
ubuntu-latestand should build cleanly.