Skip to content

Commit f12ecbe

Browse files
committed
fix: reject unknown critical cert extensions
1 parent c0a9f21 commit f12ecbe

2 files changed

Lines changed: 43 additions & 8 deletions

File tree

src/CertManager.sol

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -444,16 +444,16 @@ contract CertManager is ICertManager {
444444
while (true) {
445445
Asn1Ptr oidPtr = certificate.firstChildOf(extensionPtr);
446446
bytes32 oid = certificate.keccak(oidPtr.content(), oidPtr.length());
447+
Asn1Ptr valuePtr = certificate.nextSiblingOf(oidPtr);
448+
bool critical;
447449

448-
if (oid == BASIC_CONSTRAINTS_OID || oid == KEY_USAGE_OID) {
449-
Asn1Ptr valuePtr = certificate.nextSiblingOf(oidPtr);
450-
451-
if (certificate[valuePtr.header()] == 0x01) {
452-
// skip optional critical bool
453-
require(valuePtr.length() == 1, "invalid critical bool value");
454-
valuePtr = certificate.nextSiblingOf(valuePtr);
455-
}
450+
if (certificate[valuePtr.header()] == 0x01) {
451+
require(valuePtr.length() == 1, "invalid critical bool value");
452+
critical = certificate[valuePtr.content()] != 0x00;
453+
valuePtr = certificate.nextSiblingOf(valuePtr);
454+
}
456455

456+
if (oid == BASIC_CONSTRAINTS_OID || oid == KEY_USAGE_OID) {
457457
valuePtr = certificate.octetString(valuePtr);
458458

459459
if (oid == BASIC_CONSTRAINTS_OID) {
@@ -463,6 +463,8 @@ contract CertManager is ICertManager {
463463
keyUsageFound = true;
464464
_verifyKeyUsageExtension(certificate, valuePtr, ca);
465465
}
466+
} else {
467+
require(!critical, "unsupported critical extension");
466468
}
467469

468470
if (extensionPtr.content() + extensionPtr.length() == end) {

test/CertManager.t.sol

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ contract CertManagerHarness is CertManager {
3030
function verifyBasicConstraints(bytes memory der, bool ca) external pure returns (int64) {
3131
return _verifyBasicConstraintsExtension(der, der.root(), ca);
3232
}
33+
34+
function verifyExtensions(bytes memory der, bool ca) external pure returns (int64) {
35+
return _verifyExtensions(der, der.root(), ca);
36+
}
3337
}
3438

3539
contract CertManagerTest is Test {
@@ -95,6 +99,25 @@ contract CertManagerTest is Test {
9599
certManagerHarness.verifyBasicConstraints(hex"30020400", false);
96100
}
97101

102+
function test_VerifyExtensionsAllowsUnknownNonCriticalExtension() public view {
103+
bytes memory unknownNameConstraints = hex"30090603551d1e04023000";
104+
105+
assertEq(int256(certManagerHarness.verifyExtensions(_clientExtensionsWith(unknownNameConstraints), false)), -1);
106+
}
107+
108+
function test_VerifyExtensionsAllowsUnknownCriticalFalseExtension() public view {
109+
bytes memory unknownNameConstraints = hex"300c0603551d1e01010004023000";
110+
111+
assertEq(int256(certManagerHarness.verifyExtensions(_clientExtensionsWith(unknownNameConstraints), false)), -1);
112+
}
113+
114+
function test_VerifyExtensionsRejectsUnknownCriticalExtension() public {
115+
bytes memory unknownNameConstraints = hex"300c0603551d1e0101ff04023000";
116+
117+
vm.expectRevert("unsupported critical extension");
118+
certManagerHarness.verifyExtensions(_clientExtensionsWith(unknownNameConstraints), false);
119+
}
120+
98121
// Cert chain from the 2026-04-02 ~15:35 UTC dev attestation that produced the live revert.
99122
// CB0 is the AWS Nitro root (keccak256(CB0) == CertManager.ROOT_CA_CERT_HASH, pinned in the
100123
// constructor), so the chain is verified starting from CB1.
@@ -345,6 +368,16 @@ contract CertManagerTest is Test {
345368

346369
return der;
347370
}
371+
372+
function _clientExtensionsWith(bytes memory extraExtension) internal pure returns (bytes memory) {
373+
bytes memory body =
374+
abi.encodePacked(hex"300c0603551d130101ff04023000", hex"300e0603551d0f0101ff040403020780", extraExtension);
375+
376+
return
377+
abi.encodePacked(
378+
bytes1(0xa3), bytes1(uint8(body.length + 2)), bytes1(0x30), bytes1(uint8(body.length)), body
379+
);
380+
}
348381
}
349382

350383
/// @dev Exposes the internal revocation-chain walk and lets tests seed the `verifiedParent`

0 commit comments

Comments
 (0)