Artifact eb3f4f864bc58ebad600607d8c4a2ec0c5d80383d668536fe7827b5b95264e54:
- File
internal/deck/zcard.go
— part of check-in
[2f0f64f464]
at
2026-10-21 11:26:47
on branch feat/submanifest-reader
— fix(content): charge the sparse card budget only to decks
Preparation counted newlines in every artifact against the 1M card budget, so a repository holding one large multi-line text file was refused far below its byte limits. Only an artifact with a verified Z card can parse as a deck, so only those are charged cards and parsed; the byte budgets still bound all content. (user: [email protected], size: 3157) [annotate] [blame] [check-ins using] [more...]
package deck import ( "crypto/md5" "encoding/hex" "fmt" ) // VerifyZ validates a control artifact's trailing Z-card checksum. The blob // may arrive PGP/SSH clearsigned (historical artifacts, e.g. Fossil's own // 2007-era check-ins); stripClearsign isolates the inner content the Z card // is computed over so the last-35-byte check lands on the Z card and not on a // trailing signature block. The artifact's content-addressed hash is computed // over the raw blob elsewhere and is unaffected by this stripping. func VerifyZ(data []byte) error { return verifyZ(stripClearsign(data)) } // HasValidZ reports whether data, clearsign-stripped, ends in a Z card whose // checksum verifies. Every artifact type requires a Z card (§5.2), so data // without one can never parse as a deck; this costs one MD5 pass and no card // allocation. func HasValidZ(data []byte) bool { body := stripClearsign(data) n := len(body) if n < 35 || body[n-1] != '\n' || body[n-35] != 'Z' || body[n-34] != ' ' { return false } return verifyZ(body) == nil } // verifyZ checks the Z card of already-clearsign-stripped artifact content. // It mirrors fossil's verify_z_card (src/manifest.c): the Z card is located // as the last 35 bytes beginning "Z ", the checksum covers every byte before // it, and the stated digest is the 32 bytes at n-33 -- byte n-1, the final // LF, is never read. §6.2: a body too short to hold a Z card, or one whose // last 35 bytes are not a Z card, has the Z card absent, which this reports // as success and leaves the required-card rule (§5.2) to judge; only a // present-but-wrong checksum is an error here. Framing (§4.1) -- the missing // final LF, residue after the Z card -- is the caller's to check, so this // never rejects an input the rule it breaks should. func verifyZ(body []byte) error { n := len(body) if n < 35 { return nil // no room for a Z card: absent } if body[n-35] != 'Z' || body[n-34] != ' ' { return nil // last 35 bytes are not a Z card: absent } // §6.2 compares byte-exact against the lowercase computeZ, so an // uppercase digest fails verification even though §6.1's grammar admits // it. body[n-1] is deliberately not read. stated := body[n-33 : n-1] computed := computeZ(body[:n-35]) if computed != string(stated) { return fmt.Errorf("deck.VerifyZ: checksum mismatch: stated=%s computed=%s", stated, computed) } return nil } // isHexDigit reports whether c is a base-16 digit in either case. §6.1 // defines the Z payload's grammar as md5-token = 32hexdig-ci, a // parse-accept grammar that is explicitly case-insensitive, so rejecting // uppercase would refuse artifacts canonical fossil accepts. The // case-insensitivity is structural only: §6.2 still requires an uppercase // token to fail verification, which VerifyZ enforces by comparing // byte-exact against the lowercase computeZ. func isHexDigit(c byte) bool { switch { case c >= '0' && c <= '9': return true case c >= 'a' && c <= 'f': return true case c >= 'A' && c <= 'F': return true default: return false } } func computeZ(data []byte) string { h := md5.Sum(data) return hex.EncodeToString(h[:]) }