Skip to content

feat(NitroEnclaveVerifier): durable on-chain certificate revocation [non-urgent, post-Multiproving]#276

Merged
leopoldjoy merged 3 commits into
mainfrom
leopold-joy/nitro-durable-revocation
May 20, 2026
Merged

feat(NitroEnclaveVerifier): durable on-chain certificate revocation [non-urgent, post-Multiproving]#276
leopoldjoy merged 3 commits into
mainfrom
leopold-joy/nitro-durable-revocation

Conversation

@leopoldjoy

Copy link
Copy Markdown
Contributor

Priority

Not urgent. This change can ship after the Multiproving launch (Base V2 upgrade). Multiproving is the primary mitigation for any single-verifier gap; this PR closes a defense-in-depth hole that would otherwise require either a CA key compromise (Tier B) or an honest-but-racing registrar/admin scenario (Tier A) to exploit. There is no known active exploitation path against the current production configuration.

Summary

  • Adds a persistent revokedCerts mapping that survives the _cacheNewCert suffix rewrite, so a previously revoked intermediate cert path cannot be silently re-trusted by submitting a new attestation that re-derives the same accumulated path digest.
  • Adds unrevokeCert(bytes32) onlyOwner for governed re-trust (two-step: unrevoke, then re-cache via the next successful verify).
  • Reverts in _cacheNewCert (Pass 1) and _verifyJournal (Pass 2) when a sentinel-keyed digest is encountered, plus an early short-circuit in checkTrustedIntermediateCerts.
  • Bumps semver 0.3.00.4.0 (additive — no storage layout changes that move existing slots; only a new mapping appended).

Background — Immunefi #75608 / CHAIN-4194

_cacheNewCert previously rewrote suffix entries unconditionally. If the prefix length is 1 (the production registrar default), revoking an intermediate via revokeCert only zeroed the cached entry — a subsequent attestation re-deriving the same accumulated path digest would re-cache and re-trust it. The new revokedCerts[digest] = true sentinel is checked on every code path that would otherwise mark a cert as trusted, making revocation durable across attestations.

Triage: Low. Tier B requires CA key compromise to forge a new chain that hashes back to the revoked digest; Tier A requires a registrar honest-mistake / admin race. Both are mitigated in production today by operational controls and (post-launch) by Multiproving. This PR is hardening, not an incident response.

Changes

  • src/L1/proofs/tee/NitroEnclaveVerifier.sol
    • New storage: mapping(bytes32 => bool) public revokedCerts (appended, layout-safe).
    • revokeCert: sets sentinel + zeroes any existing cached entry, emits CertRevoked.
    • unrevokeCert: clears sentinel only, emits CertUnrevoked (re-trust requires a fresh verify call to re-cache).
    • _cacheNewCert: skips writing trusted suffix entries whose accumulated digest is sentineled.
    • _verifyJournal: rejects with CertificateRevoked(digest) when traversal encounters a sentineled digest.
    • checkTrustedIntermediateCerts: short-circuits to false if any digest in the chain is sentineled.
  • interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol: declares revokedCerts, unrevokeCert, CertUnrevoked event, CertificateNotRevoked error.
  • test/L1/proofs/NitroEnclaveVerifier.t.sol: 8 new tests covering revoke→re-attest rejection, unrevoke flow, sentinel collision behaviour, owner gating, and event emission. All 81/81 NitroEnclaveVerifier and 366/366 L1 proofs tests pass.
  • Snapshots regenerated for NitroEnclaveVerifier only.

Compatibility

  • Storage layout: strictly append-only. Existing slots untouched. Safe for in-place upgrade.
  • Off-chain registrar: the companion offchain PR is backwards compatible with the current (unpatched) deployed contract — it fail-opens (warns + counter-bumps) when revokedCerts is not present, so the offchain change can be deployed in either order relative to this onchain upgrade.

Out of Scope / Notes

  • Snapshot regen also produced unrelated drift in snapshots/abi/AggregateVerifier.json; intentionally not included in this PR.
Defense-in-depth hardening for CHAIN-4194 / Immunefi #75608. Adds a
persistent revokedCerts mapping that survives the _cacheNewCert suffix
rewrite, so a previously revoked intermediate cannot be silently
re-trusted by submitting a new attestation that re-derives the same
accumulated path digest.

Not urgent. Can ship post-Multiproving launch (Base V2 upgrade)
since multiproof is the primary mitigation for any single verifier
gap; this PR closes a defense-in-depth hole that requires either CA
key compromise (Tier B) or an honest registrar/admin race (Tier A).

Bumps semver to 0.4.0 (additive).
@cb-heimdall

cb-heimdall commented May 11, 2026

Copy link
Copy Markdown
Collaborator

✅ Heimdall Review Status

Requirement Status More Info
Reviews 1/1
Denominator calculation
Show calculation
1 if user is bot 0
1 if user is external 0
2 if repo is sensitive 0
From .codeflow.yml 1
Additional review requirements
Show calculation
Max 0
0
From CODEOWNERS 0
Global minimum 0
Max 1
1
1 if commit is unverified 0
Sum 1
leopoldjoy added a commit to base/base that referenced this pull request May 11, 2026
…ation

Adds an optional NitroEnclaveVerifier client to the registrar so that
each CRL cycle pre-screens parsed cert chains against the on-chain
revokedCerts mapping (CHAIN-4194 / Immunefi #75608 hardening) before
issuing any revokeCert tx.

Backwards compatible: the pre-check fail-opens against deployments
that do not yet expose revokedCerts (selector-not-found reverts are
caught, logged via warn!, and counted by the new
onchain_revocation_check_errors metric). The companion onchain PR
(base/contracts#276) is therefore not required to be deployed first
or in lockstep — the registrar functions normally against either the
existing or upgraded NitroEnclaveVerifier.

Production CLI/env compatibility preserved: --crl-nitro-verifier-address
and BASE_REGISTRAR_CRL_NITRO_VERIFIER_ADDRESS unchanged.

Removes dead BoundlessConfig::nitro_verifier_address field and the
unused --nitro-verifier-address flag (was wired to BoundlessArgs but
never consumed by BoundlessProver).
Comment thread src/L1/proofs/tee/NitroEnclaveVerifier.sol Outdated
…in _cacheNewCert

Addresses review feedback: descendants of a revoked cert inherit trust
from a revoked parent and must not be cached either. Switches the guard
from `continue` to `break` and aligns with `checkTrustedIntermediateCerts`,
which already uses `break` on the same condition.

In current control flow this guard is unreachable (`_verifyJournal` Pass 2
already rejects journals whose suffix contains a revoked digest before
`_cacheNewCert` runs), so this is a defense-in-depth / correctness fix
with no functional impact today.
The previous commit's semver-lock was regenerated with stale artifact
cache (test build had polluted the build dir), producing an initCodeHash
that didn't match what CI computes. Regenerated under FOUNDRY_PROFILE=ci
to match the CI validation step.
@leopoldjoy leopoldjoy merged commit 47c7dbe into main May 20, 2026
6 checks passed
@leopoldjoy leopoldjoy deleted the leopold-joy/nitro-durable-revocation branch May 20, 2026 20:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

3 participants