Skip to content

Support Nitro certificate revocation#29

Merged
leopoldjoy merged 3 commits into
base:mainfrom
leanthebean:nitro-cert-revocation
Jun 15, 2026
Merged

Support Nitro certificate revocation#29
leopoldjoy merged 3 commits into
base:mainfrom
leanthebean:nitro-cert-revocation

Conversation

@leanthebean

Copy link
Copy Markdown
Contributor

Summary

  • Adds an owner-managed operational revocation model to CertManager, with a separate revoker role for one-off or batch certificate revocations.
  • Rejects revoked certs during cold verification, cached reuse, and cached parent-chain ancestor checks, independently of notAfter.
  • Documents the CRL-monitoring responsibility and expands tests for revoked certs, parents, ancestors, root/leaf warm paths, role permissions, and owner unrevoke.

Stack

Testing

  • forge fmt --check
  • forge build --sizes
  • forge test
  • NITRO_RUN_FFI=true forge test --ffi --match-test test_OffchainWitness
  • node --check tools/nitro_attestation_input.js
  • node --check tools/hinted_attestation_calls.js
  • node tools/nitro_attestation_input.js fixture
  • node tools/hinted_attestation_calls.js fixture
  • git diff --check

Comment thread README.md
Comment thread src/CertManager.sol
Comment thread src/CertManager.sol
Comment thread src/CertManager.sol Outdated
@leanthebean leanthebean force-pushed the nitro-cert-revocation branch 2 times, most recently from 9f4d152 to db603d2 Compare June 13, 2026 15:28
Comment thread src/CertManager.sol
leopoldjoy
leopoldjoy previously approved these changes Jun 15, 2026
leanthebean and others added 3 commits June 15, 2026 13:04
Addresses Leopold's review: byte-keyed revocation (revoked[keccak256(cert)])
was bypassable. ECDSA signatures are malleable — for a valid (r, s) the twin
(r, n-s) also verifies — and DER is re-encodable, so a revoked certificate
could be re-presented with different bytes that hash differently but still
verify, slipping past the revoked set. Strict DER alone would not fix this
(both signatures are valid DER) and enforcing low-S risks bricking on AWS certs.

Revocation is now keyed by keccak256(issuerHash, serialHash) — the (issuer,
serial) identity AWS CRLs already use, which lives inside the CA-signed TBS and
is therefore fixed for every byte-encoding of a given certificate.

- Add CertManager.computeCertId(certDER) so operators derive the key (and can
  replicate it off-chain straight from CRL issuer/serial entries).
- Record each cert's identity at cold verification (certIdentity mapping) so the
  warm path and parent-chain walk check revocation without re-parsing.
- Root halt stays keyed by the pinned ROOT_CA_CERT_HASH (the root is never
  parsed on-chain) and remains owner-only.
- Tests: revoke real CA/leaf certs via computeCertId; add
  test_RevocationIdentityIsInvariantToSignatureBytes showing a signature-byte
  variant (different keccak256) maps to the same identity key.
- Docs: README, design doc, CHANGELOG updated to the identity-keyed model.

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
@leopoldjoy leopoldjoy merged commit 2e771ac into base:main Jun 15, 2026
3 checks passed
leanthebean added a commit to leanthebean/nitro-validator that referenced this pull request Jun 17, 2026
Focused tests from the security review of the hinted P-384 change (tests only).
Revocation landed in base#29 and the forward-compatibility implementation + docs in
base#32, so this carries only the adversarial soundness tests that part of the review
surfaced.

test/hinted/HintedNitroAttestation.t.sol (via a small U384TestWrapper):
- test_UnreducedInverseHintIsSound — a non-canonical inverse hint (inv + p)
  passes the on-chain b*inv == 1 check and yields the identical reduced result,
  so it cannot change the accept decision.
- test_ModulusConfusionHintReverts — a hint valid mod n is rejected where mod p
  is expected.
- test_CorruptedSignatureNeverAccepts — no hint stream makes a corrupted signature
  verify: non-self-consistent hints (empty/garbage/original/padded) revert at the
  inverse-hint gate, and self-consistent hints collected for the corrupted sig pass
  the gate but fail the final ECDSA curve check (returns false).
- test_MalleableTwinSignatureAccepted — (r, n-s) also verifies (intentional,
  CURVE_LOW_S_MAX = n-1) and decodes to the identical attestation.
- test_AttestationReplayHasNoOnChainProtection — the same (tbs, sig) verifies
  repeatedly; freshness/replay is the integrator's responsibility.

test/CertManager.t.sol:
- test_CacheGriefingSameKeyCaRenewalBricksCachedLeaf — skipped spec documenting
  the verifiedParent first-writer-wins liveness edge.

Adds P384HintCollector.collectVerifyHintsAllowInvalid so a self-consistent hint
stream can be collected for a signature that fails the final check.

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
leopoldjoy pushed a commit that referenced this pull request Jun 17, 2026
…33)

Focused tests from the security review of the hinted P-384 change (tests only).
Revocation landed in #29 and the forward-compatibility implementation + docs in
#32, so this carries only the adversarial soundness tests that part of the review
surfaced.

test/hinted/HintedNitroAttestation.t.sol (via a small U384TestWrapper):
- test_UnreducedInverseHintIsSound — a non-canonical inverse hint (inv + p)
  passes the on-chain b*inv == 1 check and yields the identical reduced result,
  so it cannot change the accept decision.
- test_ModulusConfusionHintReverts — a hint valid mod n is rejected where mod p
  is expected.
- test_CorruptedSignatureNeverAccepts — no hint stream makes a corrupted signature
  verify: non-self-consistent hints (empty/garbage/original/padded) revert at the
  inverse-hint gate, and self-consistent hints collected for the corrupted sig pass
  the gate but fail the final ECDSA curve check (returns false).
- test_MalleableTwinSignatureAccepted — (r, n-s) also verifies (intentional,
  CURVE_LOW_S_MAX = n-1) and decodes to the identical attestation.
- test_AttestationReplayHasNoOnChainProtection — the same (tbs, sig) verifies
  repeatedly; freshness/replay is the integrator's responsibility.

test/CertManager.t.sol:
- test_CacheGriefingSameKeyCaRenewalBricksCachedLeaf — skipped spec documenting
  the verifiedParent first-writer-wins liveness edge.

Adds P384HintCollector.collectVerifyHintsAllowInvalid so a self-consistent hint
stream can be collected for a signature that fails the final check.

Generated with Claude Code

Co-authored-by: Claude <noreply@anthropic.com>
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.

2 participants