Conversation
added 2 commits
March 17, 2026 18:28
Two correctness gaps identified in the grade assessment, both now closed.
═══ Origin uniqueness ═══
Each SDK now prevents two Issuer instances from sharing the same origin string
within a process. Same origin → same origin_id → ambiguous routing in a
multi-anchor verifier with undefined behavior if trust configs differ.
Go: sync.Map-backed registeredOrigins global; DeregisterOrigin() for test
cleanup; test helper updated to use t.Name()-derived unique origins.
TS: Module-level Set<string> _registeredOrigins; _deregisterOrigin() exported
for tests; guard in Issuer constructor.
Rust: static Mutex<Option<HashSet<String>>> REGISTERED_ORIGINS; register_origin()
called in Issuer::new; #[cfg(test)] deregister_origin() for test cleanup.
Java: static synchronized Set<String> REGISTERED_ORIGINS; guard in private
Issuer constructor; public static deregisterOrigin() for test cleanup.
═══ Revocation artifact hash enforcement ═══
All four verifiers now enforce the revoc: hash committed in checkpoint bodies.
When a freshly fetched artifact has tree_size < checkpoint.tree_size (the artifact
predates the checkpoint) and SHA-256(artifact_bytes) ≠ committed_hash, the
verification is rejected with a clear error. When tree_size >= checkpoint.tree_size
(the artifact was legitimately updated, e.g. after a revoke), the check is
skipped — this allows normal revocation workflows where the issuer rebuilds
the artifact more frequently than checkpoints are witnessed.
Artifact hash storage: each SDK stores SHA-256(raw_artifact_bytes) alongside
the cascade when a revocation artifact is fetched and parsed.
Go: ArtifactHash [32]byte in CachedRevocation; verifyNote/fetchAndVerify
return revocHash as 3rd value; checkpointRevocHash threaded from fetch
path into checkRevocation; enforcement at artifact.TreeSize < checkpoint.
TS: artifactHash: string in revocCache entries; verifyNote returns [root, size,
revocHash]; fetchedRevocHash threaded to runAfterRootHash; enforcement with
cached.treeSize < checkpointTreeSize guard.
Rust: artifact_hash: [u8;32] in CachedRevocation; stored via SHA-256 in
parse_revocation_artifact; check_revocation accepts committed_revoc_hash;
enforcement in fresh-fetch path only.
Java: byte[] artifactHash in CachedRevocation record; stored via MessageDigest
in parseRevocationArtifact; checkRevocation accepts committedRevocHash;
enforcement in thenApply of fresh fetch.
Also: all four issuers now build the revocation artifact BEFORE signing the
checkpoint, so checkpoint.revoc_hash and latestRevArtifact are always consistent.
(TS and Java previously used the old artifact from the previous publish cycle.)
SPEC.md and README updated: revoc enforcement promoted from 'advisory' to
'enforced with tree_size guard'.
Test totals: Go 5/5, TS SDK 34+9=43 tests, Rust 35, Java 44, interop 19/19.
Two issues caught by the CI strict typecheck:
1. parseRevArtifact return type was missing artifactHash — the function
computes and returns it but the type signature said { cascade, treeSize }
only. Updated to { cascade, treeSize, artifactHash }.
2. After the stale-eviction code sets cached = null, TypeScript strict mode
cannot prove cached is non-null at the bottom of checkRevocation (the
assignment inside if(!cached){...} doesn't help TS narrowing at the outer
scope). Added an explicit null guard that returns fail-closed if somehow
we reach that point without a cached artifact.
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 two correctness gaps from the grade assessment.
Origin uniqueness (#32): All four SDK Issuers now reject construction if the same origin string is already registered in the process. Same origin → same
origin_id→ ambiguous verifier routing. Process-wide registry in each language with test-only deregistration helpers.Revoc artifact hash enforcement: All four verifiers now enforce the
revoc:hash committed in checkpoint bodies. Enforcement rule: if the freshly fetched artifact hastree_size < checkpoint.tree_sizeandSHA-256(artifact) ≠ committed_hash, reject. Iftree_size >= checkpoint.tree_size(artifact updated after checkpoint was signed, e.g. a revoke was processed), skip — allows normal revocation workflows.Also fixed: all four issuers now build the artifact before signing the checkpoint, so
checkpoint.revoc_hashandlatestRevArtifactare always consistent.Tests: Go 5/5 · TS SDK 43 tests · Rust 35 · Java 44 · Interop 19/19