Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/CertManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,9 @@ contract CertManager is ICertManager {
function _requireCachedChainNotRevoked(bytes32 certHash) internal view {
while (certHash != bytes32(0)) {
_requireNotRevoked(_revocationKey(certHash));
VerifiedCert memory cert = _loadVerified(certHash);
if (cert.pubKey.length == 0) revert IncompleteCertChain();
require(!_certificateExpired(cert.notAfter), "cert expired");
if (certHash == ROOT_CA_CERT_HASH) {
return;
}
Expand All @@ -267,7 +270,6 @@ contract CertManager is ICertManager {
parent = _loadVerified(parentCertHash);
require(parent.pubKey.length > 0, "parent cert unverified");
_requireCachedChainNotRevoked(parentCertHash);
require(!_certificateExpired(parent.notAfter), "parent cert expired");
require(parent.ca, "parent cert is not a CA");
require(!ca || parent.maxPathLen != 0, "maxPathLen exceeded");
}
Expand Down
26 changes: 26 additions & 0 deletions test/CertManager.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,13 @@ contract RevocationChainHarness is CertManager {
verifiedParent[child] = parent;
}

function setCachedCert(bytes32 certHash, uint64 notAfter) external {
_saveVerified(
certHash,
VerifiedCert({ca: true, notAfter: notAfter, maxPathLen: -1, subjectHash: bytes32(0), pubKey: new bytes(96)})
);
}

function requireCachedChainNotRevoked(bytes32 certHash) external view {
_requireCachedChainNotRevoked(certHash);
}
Expand All @@ -655,15 +662,34 @@ contract RequireCachedChainNotRevokedTest is Test {

function test_PassesWhenChainReachesPinnedRoot() public {
bytes32 root = cm.ROOT_CA_CERT_HASH();
cm.setCachedCert(CHILD, type(uint64).max);
cm.setCachedCert(PARENT, type(uint64).max);
cm.setParent(CHILD, PARENT);
cm.setParent(PARENT, root);
// Walks CHILD -> PARENT -> ROOT and returns without reverting.
cm.requireCachedChainNotRevoked(CHILD);
}

function test_RevertsWhenCachedGrandparentIsExpired() public {
vm.warp(1000);
bytes32 grandparent = bytes32(uint256(3));

cm.setCachedCert(CHILD, 2000);
cm.setCachedCert(PARENT, 2000);
cm.setCachedCert(grandparent, 999);
cm.setParent(CHILD, PARENT);
cm.setParent(PARENT, grandparent);
cm.setParent(grandparent, cm.ROOT_CA_CERT_HASH());

vm.expectRevert("cert expired");
cm.requireCachedChainNotRevoked(CHILD);
}

function test_RevertsWhenChainDoesNotReachRoot() public {
// verifiedParent[PARENT] is unset (bytes32(0)), so the chain is broken: it can never
// reach ROOT_CA_CERT_HASH. The fixed function must fail closed instead of returning.
cm.setCachedCert(CHILD, type(uint64).max);
cm.setCachedCert(PARENT, type(uint64).max);
cm.setParent(CHILD, PARENT);
vm.expectRevert(CertManager.IncompleteCertChain.selector);
cm.requireCachedChainNotRevoked(CHILD);
Expand Down
Loading