From 84e3bbf4428220d0bd00b685a23793fa58e13125 Mon Sep 17 00:00:00 2001 From: Pim van Pelt Date: Sun, 11 Jan 2026 08:01:35 +0100 Subject: [PATCH] Also support hash tiles --- README.md | 11 ++++++++--- internal/utils/utils.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e3d3b68..c9133fc 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ ctfetch --dumpall https://halloumi2026h1.mon.ct.ipng.ch 629794635 ### tiledump -Read a CT log tile file or URL and dump all entries. +Read a CT log tile file or URL and dump contents. Automatically detects and handles both data tiles (log entries) and hash tiles (Merkle tree hashes). ```bash tiledump @@ -44,12 +44,17 @@ tiledump **Examples:** -From a file: +Data tile from a file: ```bash tiledump tile.data ``` -From a URL: +Data tile from a URL: ```bash tiledump https://halloumi2026h1.mon.ct.ipng.ch/tile/data/x002/x460/135 ``` + +Hash tile from a URL: +```bash +tiledump https://halloumi2026h1.mon.ct.ipng.ch/tile/0/x100/999 +``` diff --git a/internal/utils/utils.go b/internal/utils/utils.go index 5263f05..c3eda34 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -43,7 +43,18 @@ func Decompress(data []byte) ([]byte, error) { } // DumpAllEntries reads and dumps all entries from tile data. +// Automatically detects if the tile is a data tile or hash tile. func DumpAllEntries(tileData []byte) error { + // Try to read as data tile first + if err := dumpDataTile(tileData); err != nil { + // If it fails, try as hash tile + fmt.Fprintf(os.Stderr, "Not a data tile, trying as hash tile...\n") + return dumpHashTile(tileData) + } + return nil +} + +func dumpDataTile(tileData []byte) error { entryNum := 0 for len(tileData) > 0 { e, remaining, err := sunlight.ReadTileLeaf(tileData) @@ -61,6 +72,24 @@ func DumpAllEntries(tileData []byte) error { return nil } +func dumpHashTile(tileData []byte) error { + const hashSize = 32 // SHA-256 hash size + + if len(tileData)%hashSize != 0 { + return fmt.Errorf("invalid hash tile: size %d is not a multiple of %d", len(tileData), hashSize) + } + + numHashes := len(tileData) / hashSize + fmt.Printf("Hash tile with %d hashes:\n\n", numHashes) + + for i := 0; i < numHashes; i++ { + hash := tileData[i*hashSize : (i+1)*hashSize] + fmt.Printf("Hash %d: %x\n", i, hash) + } + + return nil +} + // DumpEntryAtPosition reads and dumps a specific entry at the given position. func DumpEntryAtPosition(tileData []byte, position int, expectedIndex int64) error { entryNum := 0