Skip to content

feat(nitro-verifier): add expiry-aware intermediate certificate caching#240

Merged
leopoldjoy merged 3 commits intomainfrom
leopoldjoy/chain-3889-add-expiry-aware-intermediate-certificate-caching-to
Apr 7, 2026
Merged

feat(nitro-verifier): add expiry-aware intermediate certificate caching#240
leopoldjoy merged 3 commits intomainfrom
leopoldjoy/chain-3889-add-expiry-aware-intermediate-certificate-caching-to

Conversation

@leopoldjoy
Copy link
Copy Markdown
Contributor

Summary

  • Changes trustedIntermediateCerts mapping from bytes32 => bool to bytes32 => uint64, where the value is the certificate's notAfter timestamp in seconds (0 = not cached)
  • Cached intermediate certs are now automatically treated as untrusted once block.timestamp exceeds their expiry, closing a security gap where cached entries could outlive their X.509 validity periods
  • Adds certExpiries field to VerifierJournal to receive expiry data from the ZK guest

Problem

NitroEnclaveVerifier caches intermediate certificate path digests indefinitely (bytes32 => bool). The ZK guest skips ALL validation (including temporal checks) for trusted-prefix certs, so cached entries with no expiry defeat the purpose of AWS's progressively shorter cert lifetimes (root ~30y, intermediate1 ~20d, intermediate2 ~6d, intermediate3 ~1d, leaf ~3h). An expired intermediate CA key in a lower security posture at AWS could be exfiltrated and used to forge attestations that pass the cached prefix check.

Changes

INitroEnclaveVerifier.sol

  • Added uint64[] certExpiries field to VerifierJournal struct (one entry per cert, matching certs[] order)

NitroEnclaveVerifier.sol

  • Mapping type: mapping(bytes32 => bool)mapping(bytes32 => uint64) (value = notAfter seconds, 0 = not cached)
  • Constructor: Added uint64[] memory initializeTrustedCertExpiries parameter with length validation against initializeTrustedCerts
  • _cacheNewCert(): Stores journal.certExpiries[i] instead of true
  • _verifyJournal(): Checks expiry == 0 || block.timestamp > expiry for trusted prefix certs
  • checkTrustedIntermediateCerts(): Stops counting at expired certs
  • revokeCert(): Uses == 0 check instead of !bool for existence
  • Semver: 0.1.00.2.0

DeployRiscZeroStack.s.sol

  • Updated constructor call with empty trustedCertExpiries array (matches existing empty-set deployment pattern)

Tests (NitroEnclaveVerifier.t.sol)

6 new tests added:

  • testExpiredCachedCertFailsVerification — cached cert past expiry returns IntermediateCertsNotTrusted
  • testNonExpiredCachedCertPassesVerification — cached cert before expiry passes
  • testCheckTrustedIntermediateCertsStopsAtExpiredCert — prefix count stops at expired cert
  • testCacheNewCertStoresCorrectExpiry — verifies correct uint64 stored
  • testRevokeCertSetsExpiryToZero — revocation clears to 0
  • testConstructorRevertsIfCertExpiriesLengthMismatch — length validation

All 63 tests pass (default + CI profiles). All existing tests updated for uint64 semantics.

Security Note

The certExpiries values originate from X.509 notAfter fields in the DER-encoded certificate chain, covered by parent cert signatures. The ZK proof covers the entire journal, so on-chain expiry values are cryptographically verified — not trusted inputs.

Related

  • CHAIN-3889
  • Depends on base/base#1972 (Step 1: ZK guest journal output)
  • Part 2 of the intermediate certificate expiry tracking initiative

Change trustedIntermediateCerts mapping from bytes32=>bool to
bytes32=>uint64, where the value is the certificate's notAfter timestamp
in seconds (0 = not cached). Cached certs are now automatically treated
as untrusted once block.timestamp exceeds their expiry, closing a
security gap where cached entries could outlive their X.509 validity.

Changes:
- INitroEnclaveVerifier.sol: Add certExpiries field to VerifierJournal
- NitroEnclaveVerifier.sol: uint64 mapping, expiry checks in
  _verifyJournal, checkTrustedIntermediateCerts, _cacheNewCert,
  revokeCert; constructor accepts parallel expiries array
- DeployRiscZeroStack.s.sol: Pass empty expiries array to constructor
- Tests: 6 new tests for expiry semantics, all existing tests updated
- Semver: 0.1.0 -> 0.2.0

CHAIN-3889
@linear
Copy link
Copy Markdown

linear bot commented Apr 6, 2026

@cb-heimdall
Copy link
Copy Markdown
Collaborator

cb-heimdall commented Apr 6, 2026

✅ 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 leopoldjoy merged commit a43e701 into main Apr 7, 2026
8 checks passed
@leopoldjoy leopoldjoy deleted the leopoldjoy/chain-3889-add-expiry-aware-intermediate-certificate-caching-to branch April 7, 2026 13:18
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.

3 participants