diff --git a/src/CertManager.sol b/src/CertManager.sol index 79852a0..ef6be5a 100644 --- a/src/CertManager.sol +++ b/src/CertManager.sol @@ -589,8 +589,15 @@ contract CertManager is ICertManager { function _certSignature(bytes memory certificate, Asn1Ptr sigPtr) internal pure returns (bytes memory sigPacked) { Asn1Ptr sigBPtr = certificate.bitstring(sigPtr); Asn1Ptr sigRoot = certificate.rootOf(sigBPtr); + _requireAsn1Tag(certificate, sigRoot, 0x30); Asn1Ptr sigRPtr = certificate.firstChildOf(sigRoot); Asn1Ptr sigSPtr = certificate.nextSiblingOf(sigRPtr); + if ( + sigRoot.header() + sigRoot.totalLength() != sigBPtr.content() + sigBPtr.length() + || sigSPtr.header() + sigSPtr.totalLength() != sigRoot.content() + sigRoot.length() + ) { + revert InvalidAsn1Tag(); + } (uint128 rhi, uint256 rlo) = certificate.uint384At(sigRPtr); (uint128 shi, uint256 slo) = certificate.uint384At(sigSPtr); sigPacked = abi.encodePacked(rhi, rlo, shi, slo); diff --git a/test/CertManager.t.sol b/test/CertManager.t.sol index 5c1bd3e..eedc5c3 100644 --- a/test/CertManager.t.sol +++ b/test/CertManager.t.sol @@ -263,6 +263,38 @@ contract CertManagerTest is Test { cm.verifyCACertWithHints(rootTwin, rootHash, hints); } + function test_VerifyCACertWithHints_RejectsSignatureWrapperTagSubstitution() public { + vm.warp(1775145600); + CertManager cm = new CertManager(new P384Verifier()); + P384HintCollector collector = new P384HintCollector(); + + bytes32 rootHash = keccak256(CB0); + bytes memory parentPubKey = cm.loadVerified(rootHash).pubKey; + bytes memory hints = collector.collectCertSignatureHints(CB1, parentPubKey); + + bytes memory mutated = bytes.concat(CB1); + (,, Asn1Ptr sigRoot,) = _certSignaturePtrs(mutated); + mutated[sigRoot.header()] = 0x31; // constructed SET with the same r/s children. + + vm.expectRevert(CertManager.InvalidAsn1Tag.selector); + cm.verifyCACertWithHints(mutated, rootHash, hints); + } + + function test_VerifyCACertWithHints_RejectsTrailingSignatureFields() public { + vm.warp(1775145600); + CertManager cm = new CertManager(new P384Verifier()); + P384HintCollector collector = new P384HintCollector(); + + bytes32 rootHash = keccak256(CB0); + bytes memory parentPubKey = cm.loadVerified(rootHash).pubKey; + bytes memory hints = collector.collectCertSignatureHints(CB1, parentPubKey); + + bytes memory mutated = _appendSignatureTrailingField(CB1); + + vm.expectRevert(CertManager.InvalidAsn1Tag.selector); + cm.verifyCACertWithHints(mutated, rootHash, hints); + } + function test_VerifyCACertWithHints_RejectsOuterTagSubstitution() public { vm.warp(1775145600); CertManager cm = new CertManager(new P384Verifier()); @@ -338,6 +370,34 @@ contract CertManagerTest is Test { _writeDerLength(result, sigRoot, _addDelta(sigRoot.length(), delta)); } + function _appendSignatureTrailingField(bytes memory certificate) internal pure returns (bytes memory result) { + (Asn1Ptr root, Asn1Ptr sigPtr, Asn1Ptr sigRoot,) = _certSignaturePtrs(certificate); + bytes memory extraInteger = hex"020100"; + int256 delta = int256(extraInteger.length); + result = _insertBytes(certificate, sigRoot.content() + sigRoot.length(), extraInteger); + + _writeDerLength(result, root, _addDelta(root.length(), delta)); + _writeDerLength(result, sigPtr, _addDelta(sigPtr.length(), delta)); + _writeDerLength(result, sigRoot, _addDelta(sigRoot.length(), delta)); + } + + function _insertBytes(bytes memory input, uint256 offset, bytes memory inserted) + internal + pure + returns (bytes memory result) + { + result = new bytes(input.length + inserted.length); + for (uint256 i = 0; i < offset; ++i) { + result[i] = input[i]; + } + for (uint256 i = 0; i < inserted.length; ++i) { + result[offset + i] = inserted[i]; + } + for (uint256 i = offset; i < input.length; ++i) { + result[i + inserted.length] = input[i]; + } + } + function _certSignaturePtrs(bytes memory certificate) internal pure