Skip to content

Verify a chart file against its container's checksum - #71

Merged
beetlebugorg merged 2 commits into
mainfrom
fix/ingest-integrity
Sep 7, 2026
Merged

Verify a chart file against its container's checksum#71
beetlebugorg merged 2 commits into
mainfrom
fix/ingest-integrity

Conversation

@beetlebugorg

Copy link
Copy Markdown
Owner

Both ingest formats hold a checksum for a chart file, and both were discarded. A flipped bit inside a deflate literal run inflates to bytes that parse: in an SG2D field it moves a depth-area boundary vertex by up to 25 metres at COMF 1e7, and the chart bakes and draws without an error. Stacked on #70.

Every zip entry has a CRC32 of its uncompressed bytes. readAlloc hashes what it read and refuses a mismatch. S-57 Part 3 3.4 lets the catalogue give a CRC per file in the CATD CRCS subfield, which decodeCATD left unread. Cells, their update files and the bake are all checked against it now. A producer omitting the CRC is a case the spec allows, and those files open as before.

Checked on an exchange set built from a real cell with a generated catalogue, one byte flipped in the base and then in an update. Both ingest paths name the file and produce no archive.

zig build, zig build test and zig fmt --check pass.

Base automatically changed from fix/ingest-report-losses to main September 7, 2026 02:15
Both ingest formats hold a checksum for a chart file, and both checksums
were discarded, so no step between the bytes on a card and the tiles a
mariner navigates by verified that a chart is the one the hydrographic
office published.

Every zip entry has a CRC32 of its uncompressed bytes, and
zip.Iterator.Entry holds it. readAlloc hashes what it read and refuses a
mismatch. Raw deflate has no checksum of its own, so a flipped bit inside
a literal run inflates to bytes that parse: in an SG2D field that moves a
depth-area boundary vertex by up to 25 metres at COMF 1e7, and the chart
bakes and draws without an error.

S-57 Part 3 3.4 lets the catalogue give a CRC per file in the CATD CRCS
subfield. decodeCATD read parts 0 to 6 and left CRCS unread, and
CatalogEntry had no field for it. It now holds the text, catalogCrc reads
the hex, and a cell opened by catalogue name is checked against it. A
producer omitting it is a case the spec allows, and those cells open as
before.

A test flips one byte in a stored entry and reads it back.
The catalogue check covered the base cells of a directory open. Two gaps
were left either side of it.

The update files went unchecked on every path. A catalogue gives a CRC
per file, updates included, but only .000 entries were compared, so a
damaged .001 applied to a chart and the chart drew as though it were the
one the hydrographic office published. The open now keeps every CRC the
catalogue gives and compares each update as the chain reads it. A
mismatch stops the chain and keeps the cell, the policy a corrupt update
already follows, and names the file.

The bake checked no file at all. It walks for .000 files and never
opened the catalogue, so the two ingest paths disagreed about the same bytes: the
inventory call refused a set the bake turned into archives without a
word. The catalogue is now read once per exchange set and each cell and
its updates are compared before the bake reads them. A file with no CRC
in the catalogue passes, as before, and a set with no catalogue bakes
unchanged.

A damaged update fails the whole cell on the bake rather than truncating
its chain. The bake writes one archive per cell, and an archive built to
an earlier update than the set names cannot be told apart from a complete
one afterwards.

Checked against an exchange set built from a real cell, with one byte
flipped: in the base, and in an update. Both paths now name the file and
produce no archive, and the intact set is unaffected.
@beetlebugorg
beetlebugorg merged commit 5539a97 into main Sep 7, 2026
5 checks passed
@beetlebugorg
beetlebugorg deleted the fix/ingest-integrity branch September 7, 2026 02:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant