diff --git a/contracts/ILCPClientErrors.sol b/contracts/ILCPClientErrors.sol index 86c2253..34104ea 100644 --- a/contracts/ILCPClientErrors.sol +++ b/contracts/ILCPClientErrors.sol @@ -73,6 +73,7 @@ interface ILCPClientErrors { error LCPClientZKDCAPRisc0ImageIdNotSet(); error LCPClientZKDCAPUnexpectedIntelRootCAHash(); error LCPClientZKDCAPOutputReportUnexpectedOperator(address actual, address expected); + error LCPClientZKDCAPInvalidOperator(); error LCPClientZKDCAPDisallowedTCBStatus(); error LCPClientZKDCAPDisallowedAdvisoryID(); diff --git a/contracts/LCPClientZKDCAPBase.sol b/contracts/LCPClientZKDCAPBase.sol index 9949251..ab98a8d 100644 --- a/contracts/LCPClientZKDCAPBase.sol +++ b/contracts/LCPClientZKDCAPBase.sol @@ -236,6 +236,15 @@ abstract contract LCPClientZKDCAPBase is LCPClientBase { message.operator_signature ); } + + // if the operators are set, the operator address must be non-zero and active + if ( + clientStorage.clientState.operators.length != 0 + && !ensureActiveOperator(clientStorage.clientState, operator) + ) { + revert LCPClientZKDCAPInvalidOperator(); + } + if (output.operator != address(0) && output.operator != operator) { revert LCPClientZKDCAPOutputReportUnexpectedOperator(operator, output.operator); } @@ -379,4 +388,20 @@ abstract contract LCPClientZKDCAPBase is LCPClientBase { return (currentUpdated, false); } } + + function ensureActiveOperator(ProtoClientState.Data storage clientState, address operator) + internal + view + returns (bool) + { + if (operator == address(0)) { + return false; + } + for (uint256 i = 0; i < clientState.operators.length; i++) { + if (address(bytes20(clientState.operators[i])) == operator) { + return true; + } + } + return false; + } } diff --git a/foundry.toml b/foundry.toml index af81449..44de4e1 100644 --- a/foundry.toml +++ b/foundry.toml @@ -3,7 +3,7 @@ src = 'contracts' out = 'out' libs = ['lib', 'node_modules'] optimizer = true -optimizer_runs = 8000 +optimizer_runs = 2000 via-ir = false ffi = true ast = true diff --git a/lib/risc0-ethereum b/lib/risc0-ethereum index b9b22c3..32aa0b6 160000 --- a/lib/risc0-ethereum +++ b/lib/risc0-ethereum @@ -1 +1 @@ -Subproject commit b9b22c396a0d5ef97bf02702da9415d5bb79a85a +Subproject commit 32aa0b6f23ddd02dd93fc71717667606e5c7db86 diff --git a/test/LCPClientZKDCAPTest.t.sol b/test/LCPClientZKDCAPTest.t.sol index 06cba27..f4c8a09 100644 --- a/test/LCPClientZKDCAPTest.t.sol +++ b/test/LCPClientZKDCAPTest.t.sol @@ -575,11 +575,42 @@ contract LCPClientZKDCAPTest is BasicTest { DCAPValidator.Output memory output = ZKDCAPTestHelper.qvOutput(); output.operator = op1.addr; IbcLightclientsLcpV1ZKDCAPRegisterEnclaveKeyMessage.Data memory message = registerEnclaveKeyMessage(output); - vm.expectRevert( - abi.encodeWithSelector( - ILCPClientErrors.LCPClientZKDCAPOutputReportUnexpectedOperator.selector, address(0), output.operator + vm.expectRevert(abi.encodeWithSelector(ILCPClientErrors.LCPClientZKDCAPInvalidOperator.selector)); + lc.zkDCAPRegisterEnclaveKey(clientId, message); + } + + { + // if the operator signature is set by an unregistered operator, it should fail + DCAPValidator.Output memory output = ZKDCAPTestHelper.qvOutput(); + IbcLightclientsLcpV1ZKDCAPRegisterEnclaveKeyMessage.Data memory message = registerEnclaveKeyMessage(output); + (uint8 v, bytes32 r, bytes32 s) = vm.sign( + op2, + keccak256( + LCPOperator.computeEIP712ZKDCAPRegisterEnclaveKey( + clientState.zkdcap_verifier_infos[0], keccak256(ZKDCAPTestHelper.toBytes(output)) + ) + ) + ); + message.operator_signature = abi.encodePacked(r, s, v); + vm.expectRevert(abi.encodeWithSelector(ILCPClientErrors.LCPClientZKDCAPInvalidOperator.selector)); + lc.zkDCAPRegisterEnclaveKey(clientId, message); + } + + { + // if the operator signature and operator are set by an unregistered operator, it should fail + DCAPValidator.Output memory output = ZKDCAPTestHelper.qvOutput(); + output.operator = op2.addr; + IbcLightclientsLcpV1ZKDCAPRegisterEnclaveKeyMessage.Data memory message = registerEnclaveKeyMessage(output); + (uint8 v, bytes32 r, bytes32 s) = vm.sign( + op2, + keccak256( + LCPOperator.computeEIP712ZKDCAPRegisterEnclaveKey( + clientState.zkdcap_verifier_infos[0], keccak256(ZKDCAPTestHelper.toBytes(output)) + ) ) ); + message.operator_signature = abi.encodePacked(r, s, v); + vm.expectRevert(abi.encodeWithSelector(ILCPClientErrors.LCPClientZKDCAPInvalidOperator.selector)); lc.zkDCAPRegisterEnclaveKey(clientId, message); } @@ -589,7 +620,7 @@ contract LCPClientZKDCAPTest is BasicTest { output.operator = op1.addr; IbcLightclientsLcpV1ZKDCAPRegisterEnclaveKeyMessage.Data memory message = registerEnclaveKeyMessage(output); (uint8 v, bytes32 r, bytes32 s) = vm.sign( - op2, + op3, keccak256( LCPOperator.computeEIP712ZKDCAPRegisterEnclaveKey( clientState.zkdcap_verifier_infos[0], keccak256(ZKDCAPTestHelper.toBytes(output)) @@ -599,7 +630,7 @@ contract LCPClientZKDCAPTest is BasicTest { message.operator_signature = abi.encodePacked(r, s, v); vm.expectRevert( abi.encodeWithSelector( - ILCPClientErrors.LCPClientZKDCAPOutputReportUnexpectedOperator.selector, op2.addr, op1.addr + ILCPClientErrors.LCPClientZKDCAPOutputReportUnexpectedOperator.selector, op3.addr, op1.addr ) ); lc.zkDCAPRegisterEnclaveKey(clientId, message); @@ -625,12 +656,12 @@ contract LCPClientZKDCAPTest is BasicTest { } { - // if both operator and operator signature are not set, it should succeed + // if both operator and operator signature are not set, it should fail DCAPValidator.Output memory output = ZKDCAPTestHelper.qvOutput(); output.enclaveKey = address(2); IbcLightclientsLcpV1ZKDCAPRegisterEnclaveKeyMessage.Data memory message = registerEnclaveKeyMessage(output); + vm.expectRevert(abi.encodeWithSelector(ILCPClientErrors.LCPClientZKDCAPInvalidOperator.selector)); lc.zkDCAPRegisterEnclaveKey(clientId, message); - assertEq(lc.getEKInfo(clientId, output.enclaveKey).operator, address(0)); } { diff --git a/test/ZKDCAPVerifier.t.sol b/test/ZKDCAPVerifier.t.sol index 4cdc6db..6ca6ed9 100644 --- a/test/ZKDCAPVerifier.t.sol +++ b/test/ZKDCAPVerifier.t.sol @@ -9,6 +9,7 @@ import {DCAPValidator} from "../contracts/DCAPValidator.sol"; contract ZKDCAPVerifierTest is BasicTest { IRiscZeroVerifier verifier_rz11; IRiscZeroVerifier verifier_rz12; + IRiscZeroVerifier verifier_rz30; function setUp() public { // ref. https://github.com/risc0/risc0-ethereum/blob/4fa7de055d461b7fa948eb56107b7a172459e8fc/contracts/src/groth16/ControlID.sol#L22 @@ -21,6 +22,11 @@ contract ZKDCAPVerifierTest is BasicTest { hex"8cdad9242664be3112aba377c5425a4df735eb1c6966472b561d2855932c0469", hex"04446e66d300eb7fb45c9726bb53c793dda407a62e9601618bb43c5c14657ac0" ); + // https://github.com/risc0/risc0/blob/61cfbaf57615b83854424f972f23e026970b4201/risc0/circuit/recursion/src/control_id.rs#L53-L59 + verifier_rz30 = new RiscZeroGroth16Verifier( + hex"a54dc85ac99f851c92d7c96d7318af41dbe7c0194edfcc37eb4d422a998c1f56", + hex"04446e66d300eb7fb45c9726bb53c793dda407a62e9601618bb43c5c14657ac0" + ); } /* @@ -30,7 +36,7 @@ contract ZKDCAPVerifierTest is BasicTest { "commit":"00000003000000000500906ed50000a1acc73eb45794fa1734f14d882e91925b6006f79d3bb2460df9d01b333d700900000000679885950000000067c00a9c15150b07ff800e000000000000000000000000000000000000000000000000000000000000000000000000000000000005000000000000000700000000000000813c146e403f203f2784fa222b3edeac70727dee21c0b08f74883aa189e7b0ed000000000000000000000000000000000000000000000000000000000000000083d719e77deaca1470f6baf62a4d774303c899db69020f9c70ee1dfc08c7ce9e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017dcf7408c72ebe9076aebbb208d2c85e62050db4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000e494e54454c2d53412d3030333334000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e494e54454c2d53412d3030363135000000000000000000000000000000000000" }} */ - function testVerify1() public view { + function testVerify11() public view { verifier_rz11.verify( hex"50bd1769039405f7898272862594bc436f08dab3e5c1200e0c44b542b462fe09bb2655b700ae0374f99c5b30f8580605267de7b5d121758ae405b5b378b588deca3042a70e22eefd598a4638d7c039ffb737f45dbee0152559f25c4dc0854ab7fd7cc2f52704dc9991d84e00c48d155b0d7fbfe2d307d1fb1a573c38083d86bea39e60191110517565779f631fc0028b82ff9b224bcc627bd6fd3ee1c1ffcea67e9e921c281abaaeacabff9fb74232c9ca2914ce6fe3ec9a4584c68a888339f2e6865c9f2bb57d5a38759c09ecb3f9f9f80fd582f23b7fb47495f783a8cc2eedb99ca18b0337102df4a2e325367d954ea5154f11c03f459551f849763d75ccad9d6665c5", hex"200d1f40b5733d31e4f3bfb1f106351e878aea304b5c9e73690b9e18e2e77bb6", @@ -49,7 +55,7 @@ contract ZKDCAPVerifierTest is BasicTest { } } */ - function testVerify2() public view { + function testVerify12() public view { verifier_rz12.verify( hex"c101b42b2178a72a844649d14328eb6b7a650d6fe1640c1ef2fa887bcc755597229713fb2360369704b1c06c610e6a83dbdc31bb04e1cb5a8def9c7354fc34be7e74844301db3e4f8c2cef2a91df93e191e3138ac27dac03d1a2d2bc2e18330f712e65021d34cf95c2ff4134df2a04a726de5a8d8e535f10225f00046295f8b3d3b939a626e424d04c48c94aef79267457e375ce4670ef5eb5af2c3e8fea40c4905228e617130a7a0c1ac16ed030103b02f94c89f6f5935d8a581198df573c050ea63e331b6c494238c02ff85040e720621dd81fa6d1191098cf40ac05923398e58b213c197e62e6a4394b5b5134f9770ba31805e57c2c8635e921472936bab23a56c824", hex"a7b6d5b9a35f9c4e9364e69728c87a4818fe56b361b5f5ff25bab7494074b50b", @@ -59,6 +65,25 @@ contract ZKDCAPVerifierTest is BasicTest { ); } + /* + { + "Risc0":{ + "image_id":"27a8d21b54ce10b06901dca7de504f7421cfcad47bae69e8783e449519d99635", + "selector":"73c457ba", + "seal":"2708b796ffbe2beafb20f3c313f4e8c05f8b2c1d5320a10e0e40a38f44584b9d2f1f2b0dc953ab24d707b5664bf4a4ab4b7c13ce7303f46af3ce10021fbbe3950df800fe0b015d9ece18c61a1095dbf9b9043a4fd5d7ab0593532e727c9d48a80d0bc9a26dcc2e2426bfa7747dda3a64a563d14596d9ef81b3807b958492ba13164020246d455ce5c7864b7e98af1c19c593b3e4b1723384f17a9e272e52d9cf0ef7eea2b3f3ad8622099b96b2950c304c3acb826daf42456a6e858a8c56ae7c1b295ab9e1b12107427031951322955ba7a30c613797a82ae9ffdcef77d06d4924ba37eeb963abb404775a498a34bc2d2f9222482a9e69124858e69d6ee8ccdc", + "output":"0000000300000000050000000100606a000000d61f4e3d30011899d16131d4c940ef1f75ec53d7f9a70cbb3aab1f5ab0235b2b0000000068aea64900000000e8aea6484820f3376ae6b2f2034d3b7a4b48a778000000000000000000000000000000000000000000000000000000000000000005000000000000000700000000000000568b2634f9e30ab3df84ccc8a1fab58d87dcd02e02cbb62172ed4734215f0f38000000000000000000000000000000000000000000000000000000000000000083d719e77deaca1470f6baf62a4d774303c899db69020f9c70ee1dfc08c7ce9e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011770f0a82157d84d369a1935277fb8cca8b34e18000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000e494e54454c2d53412d3030333334000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e494e54454c2d53412d3030363135000000000000000000000000000000000000" + } + */ + function testVerify30() public view { + verifier_rz30.verify( + hex"73c457ba2708b796ffbe2beafb20f3c313f4e8c05f8b2c1d5320a10e0e40a38f44584b9d2f1f2b0dc953ab24d707b5664bf4a4ab4b7c13ce7303f46af3ce10021fbbe3950df800fe0b015d9ece18c61a1095dbf9b9043a4fd5d7ab0593532e727c9d48a80d0bc9a26dcc2e2426bfa7747dda3a64a563d14596d9ef81b3807b958492ba13164020246d455ce5c7864b7e98af1c19c593b3e4b1723384f17a9e272e52d9cf0ef7eea2b3f3ad8622099b96b2950c304c3acb826daf42456a6e858a8c56ae7c1b295ab9e1b12107427031951322955ba7a30c613797a82ae9ffdcef77d06d4924ba37eeb963abb404775a498a34bc2d2f9222482a9e69124858e69d6ee8ccdc", + hex"27a8d21b54ce10b06901dca7de504f7421cfcad47bae69e8783e449519d99635", + sha256( + hex"0000000300000000050000000100606a000000d61f4e3d30011899d16131d4c940ef1f75ec53d7f9a70cbb3aab1f5ab0235b2b0000000068aea64900000000e8aea6484820f3376ae6b2f2034d3b7a4b48a778000000000000000000000000000000000000000000000000000000000000000005000000000000000700000000000000568b2634f9e30ab3df84ccc8a1fab58d87dcd02e02cbb62172ed4734215f0f38000000000000000000000000000000000000000000000000000000000000000083d719e77deaca1470f6baf62a4d774303c899db69020f9c70ee1dfc08c7ce9e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011770f0a82157d84d369a1935277fb8cca8b34e18000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000e494e54454c2d53412d3030333334000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e494e54454c2d53412d3030363135000000000000000000000000000000000000" + ) + ); + } + function testParseOutputSWHardeningNeeded() public pure { bytes memory outputBytes = hex"0000000300000000050000001200906ed50000a1acc73eb45794fa1734f14d882e91925b6006f79d3bb2460df9d01b333d70090000000067b3f1fa0000000067db736115150b07ff800e00000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000070000000000000026ae825ffce1cf9dcdf682614f4d36704e7bca087bbb5264aca9301d7824cec8000000000000000000000000000000000000000000000000000000000000000083d719e77deaca1470f6baf62a4d774303c899db69020f9c70ee1dfc08c7ce9e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c170f98628b3a01b15654fbfaad1aaf3419b2c3c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000e494e54454c2d53412d3030333334000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e494e54454c2d53412d3030363135000000000000000000000000000000000000";