-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Jun Kimura <[email protected]>
- Loading branch information
Showing
17 changed files
with
871 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ name: Test | |
on: [pull_request] | ||
|
||
env: | ||
SOLC_VERSION: 0.8.20 | ||
SOLC_VERSION: 0.8.28 | ||
|
||
jobs: | ||
contract-test: | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
SOLC_VERSION=0.8.20 | ||
SOLC_VERSION=0.8.28 | ||
FORGE=forge | ||
SLITHER=slither | ||
TEST_UPGRADEABLE=false | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
pragma solidity ^0.8.12; | ||
|
||
library DCAPValidator { | ||
uint256 internal constant SGX_QUOTE_BODY_OFFSET = 63; | ||
uint256 internal constant ATTRIBUTES_OFFSET = SGX_QUOTE_BODY_OFFSET + 16 + 4 + 28; | ||
uint256 internal constant MRENCLAVE_OFFSET = ATTRIBUTES_OFFSET + 16; | ||
uint256 internal constant MRENCLAVE_END_OFFSET = MRENCLAVE_OFFSET + 32; | ||
uint256 internal constant REPORT_DATA_OFFSET = SGX_QUOTE_BODY_OFFSET + 320; | ||
uint256 internal constant REPORT_DATA_ENCLAVE_KEY_OFFSET = REPORT_DATA_OFFSET + 1; | ||
uint256 internal constant REPORT_DATA_OPERATOR_OFFSET = REPORT_DATA_ENCLAVE_KEY_OFFSET + 20; | ||
uint256 internal constant REPORT_DATA_OPERATOR_END_OFFSET = REPORT_DATA_OPERATOR_OFFSET + 20; | ||
uint256 internal constant ADVISORY_IDS_OFFSET = REPORT_DATA_OFFSET + 64; | ||
|
||
uint8 internal constant TCB_STATUS_UP_TO_DATE = 0; | ||
uint8 internal constant TCB_STATUS_OUT_OF_DATE = 1; | ||
uint8 internal constant TCB_STATUS_REVOKED = 2; | ||
uint8 internal constant TCB_STATUS_CONFIGURATION_NEEDED = 3; | ||
uint8 internal constant TCB_STATUS_OUT_OF_DATE_CONFIGURATION_NEEDED = 4; | ||
uint8 internal constant TCB_STATUS_SW_HARDENING_NEEDED = 5; | ||
uint8 internal constant TCB_STATUS_CONFIGURATION_AND_SW_HARDENING_NEEDED = 6; | ||
|
||
struct Output { | ||
uint8 tcbStatus; | ||
bytes32 sgxIntelRootCAHash; | ||
uint64 validityNotBeforeMax; | ||
uint64 validityNotAfterMin; | ||
bool enclaveDebugEnabled; | ||
bytes32 mrenclave; | ||
address enclaveKey; | ||
address operator; | ||
string[] advisoryIDs; | ||
} | ||
|
||
function parseCommit(bytes calldata commit) public pure returns (Output memory) { | ||
require(bytes2(commit[0:2]) == hex"0000", "unexpected version"); | ||
require(uint16(bytes2(commit[2:4])) == 3, "unexpected quote version"); | ||
require(uint32(bytes4(commit[4:8])) == 0, "unexpected tee type"); | ||
|
||
Output memory output; | ||
output.tcbStatus = uint8(commit[8]); | ||
output.sgxIntelRootCAHash = bytes32(commit[15:47]); | ||
output.validityNotBeforeMax = uint64(bytes8(commit[47:55])); | ||
output.validityNotAfterMin = uint64(bytes8(commit[55:63])); | ||
output.enclaveDebugEnabled = uint8(commit[ATTRIBUTES_OFFSET]) & uint8(2) != 0; | ||
output.mrenclave = bytes32(commit[MRENCLAVE_OFFSET:MRENCLAVE_END_OFFSET]); | ||
|
||
require(commit[REPORT_DATA_OFFSET] == 0x01, "unexpected report data version"); | ||
output.enclaveKey = address(bytes20(commit[REPORT_DATA_ENCLAVE_KEY_OFFSET:REPORT_DATA_OPERATOR_OFFSET])); | ||
output.operator = address(bytes20(commit[REPORT_DATA_OPERATOR_OFFSET:REPORT_DATA_OPERATOR_END_OFFSET])); | ||
output.advisoryIDs = abi.decode(commit[ADVISORY_IDS_OFFSET:commit.length], (string[])); | ||
return output; | ||
} | ||
|
||
function tcbStatusToString(uint8 tcbStatus) public pure returns (string memory) { | ||
if (tcbStatus == TCB_STATUS_UP_TO_DATE) { | ||
return "UpToDate"; | ||
} else if (tcbStatus == TCB_STATUS_OUT_OF_DATE) { | ||
return "OutOfDate"; | ||
} else if (tcbStatus == TCB_STATUS_REVOKED) { | ||
return "Revoked"; | ||
} else if (tcbStatus == TCB_STATUS_CONFIGURATION_NEEDED) { | ||
return "ConfigurationNeeded"; | ||
} else if (tcbStatus == TCB_STATUS_OUT_OF_DATE_CONFIGURATION_NEEDED) { | ||
return "OutOfDateConfigurationNeeded"; | ||
} else if (tcbStatus == TCB_STATUS_SW_HARDENING_NEEDED) { | ||
return "SWHardeningNeeded"; | ||
} else if (tcbStatus == TCB_STATUS_CONFIGURATION_AND_SW_HARDENING_NEEDED) { | ||
return "ConfigurationAndSWHardeningNeeded"; | ||
} else { | ||
revert("unexpected TCB status"); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
pragma solidity ^0.8.12; | ||
|
||
import {LCPClientZKDCAPBase} from "./LCPClientZKDCAPBase.sol"; | ||
|
||
contract LCPClientZKDCAP is LCPClientZKDCAPBase { | ||
constructor(address ibcHandler_, bool developmentMode_, bytes memory intelRootCA, address riscZeroVerifier) | ||
LCPClientZKDCAPBase(ibcHandler_, developmentMode_, intelRootCA, riscZeroVerifier) | ||
{} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
pragma solidity ^0.8.12; | ||
|
||
import {IBCHeight} from "@hyperledger-labs/yui-ibc-solidity/contracts/core/02-client/IBCHeight.sol"; | ||
import {Height} from "@hyperledger-labs/yui-ibc-solidity/contracts/proto/Client.sol"; | ||
import {IRiscZeroVerifier} from "risc0-ethereum/contracts/src/IRiscZeroVerifier.sol"; | ||
import { | ||
IbcLightclientsLcpV1ClientState as ProtoClientState, | ||
IbcLightclientsLcpV1ZKDCAPRegisterEnclaveKeyMessage as ZKDCAPRegisterEnclaveKeyMessage | ||
} from "./proto/ibc/lightclients/lcp/v1/LCP.sol"; | ||
import {LCPProtoMarshaler} from "./LCPProtoMarshaler.sol"; | ||
import {LCPClientBase} from "./LCPClientBase.sol"; | ||
import {LCPOperator} from "./LCPOperator.sol"; | ||
import {RemoteAttestation} from "./RemoteAttestation.sol"; | ||
import {DCAPValidator} from "./DCAPValidator.sol"; | ||
|
||
abstract contract LCPClientZKDCAPBase is LCPClientBase { | ||
using IBCHeight for Height.Data; | ||
// --------------------- Constants --------------------- | ||
|
||
uint8 internal constant ZKVM_TYPE_RISC_ZERO = 0x01; | ||
|
||
// --------------------- Events --------------------- | ||
|
||
event ZKDCAPRegisteredEnclaveKey(string clientId, address enclaveKey, uint256 expiredAt, address operator); | ||
|
||
// --------------------- Immutable fields --------------------- | ||
|
||
/// @dev if developmentMode is true, the client allows the target enclave which is debug mode enabled. | ||
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable | ||
bool internal immutable developmentMode; | ||
|
||
/// @notice The hash of the root CA's public key certificate. | ||
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable | ||
bytes32 public immutable intelRootCAHash; | ||
|
||
/// @notice RISC Zero verifier contract address. | ||
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable | ||
IRiscZeroVerifier public immutable riscZeroVerifier; | ||
|
||
// --------------------- Storage fields --------------------- | ||
|
||
/// @dev Reserved storage space to allow for layout changes in the future | ||
uint256[50] private __gap; | ||
|
||
// --------------------- Constructor --------------------- | ||
|
||
/// @custom:oz-upgrades-unsafe-allow constructor | ||
/// @param ibcHandler_ the address of the IBC handler contract | ||
constructor(address ibcHandler_, bool developmentMode_, bytes memory intelRootCA, address riscZeroVerifier_) | ||
LCPClientBase(ibcHandler_) | ||
{ | ||
if (intelRootCA.length == 0 || riscZeroVerifier_ == address(0)) { | ||
revert LCPClientZKDCAPInvalidConstructorParams(); | ||
} | ||
intelRootCAHash = keccak256(intelRootCA); | ||
riscZeroVerifier = IRiscZeroVerifier(riscZeroVerifier_); | ||
developmentMode = developmentMode_; | ||
} | ||
|
||
// --------------------- Public methods --------------------- | ||
|
||
/** | ||
* @dev initializeClient initializes a new client with the given state. | ||
* If succeeded, it returns heights at which the consensus state are stored. | ||
* This function is guaranteed by the IBC contract to be called only once for each `clientId`. | ||
* @param clientId the client identifier which is unique within the IBC handler | ||
*/ | ||
function initializeClient( | ||
string calldata clientId, | ||
bytes calldata protoClientState, | ||
bytes calldata protoConsensusState | ||
) public override onlyIBC returns (Height.Data memory height) { | ||
ClientStorage storage clientStorage = clientStorages[clientId]; | ||
(ProtoClientState.Data memory clientState,) = | ||
_initializeClient(clientStorage, protoClientState, protoConsensusState); | ||
if (clientState.zkdcap_verifier_infos.length != 1) { | ||
revert LCPClientZKDCAPInvalidVerifierInfos(); | ||
} | ||
bytes memory verifierInfo = clientState.zkdcap_verifier_infos[0]; | ||
if (verifierInfo.length != 64) { | ||
revert LCPClientZKDCAPInvalidVerifierInfoLength(); | ||
} | ||
if (uint8(verifierInfo[0]) != ZKVM_TYPE_RISC_ZERO) { | ||
revert LCPClientZKDCAPInvalidVerifierInfoZKVMType(); | ||
} | ||
// 32..64 bytes: image ID | ||
bytes32 imageId; | ||
assembly { | ||
imageId := mload(add(add(verifierInfo, 32), 32)) | ||
} | ||
clientStorage.zkDCAPRisc0ImageId = imageId; | ||
return clientState.latest_height; | ||
} | ||
|
||
/** | ||
* @dev routeUpdateClient returns the calldata to the receiving function of the client message. | ||
* Light client contract may encode a client message as other encoding scheme(e.g. ethereum ABI) | ||
* Check ADR-001 for details. | ||
*/ | ||
function routeUpdateClient(string calldata clientId, bytes calldata protoClientMessage) | ||
public | ||
pure | ||
override | ||
returns (bytes4, bytes memory) | ||
{ | ||
(bytes32 typeUrlHash, bytes memory args) = LCPProtoMarshaler.routeClientMessage(clientId, protoClientMessage); | ||
if (typeUrlHash == LCPProtoMarshaler.UPDATE_CLIENT_MESSAGE_TYPE_URL_HASH) { | ||
return (this.updateClient.selector, args); | ||
} else if (typeUrlHash == LCPProtoMarshaler.ZKDCAP_REGISTER_ENCLAVE_KEY_MESSAGE_TYPE_URL_HASH) { | ||
return (this.zkDCAPRegisterEnclaveKey.selector, args); | ||
} else if (typeUrlHash == LCPProtoMarshaler.UPDATE_OPERATORS_MESSAGE_TYPE_URL_HASH) { | ||
return (this.updateOperators.selector, args); | ||
} else { | ||
revert LCPClientUnknownProtoTypeUrl(); | ||
} | ||
} | ||
|
||
function zkDCAPRegisterEnclaveKey(string calldata clientId, ZKDCAPRegisterEnclaveKeyMessage.Data calldata message) | ||
public | ||
returns (Height.Data[] memory heights) | ||
{ | ||
if (message.zkvm_type != ZKVM_TYPE_RISC_ZERO) { | ||
revert LCPClientZKDCAPUnsupportedZKVMType(); | ||
} | ||
ClientStorage storage clientStorage = clientStorages[clientId]; | ||
if (clientStorage.zkDCAPRisc0ImageId == bytes32(0)) { | ||
revert LCPClientZKDCAPRisc0ImageIdNotSet(); | ||
} | ||
riscZeroVerifier.verify(message.proof, clientStorage.zkDCAPRisc0ImageId, sha256(message.commit)); | ||
DCAPValidator.Output memory output = DCAPValidator.parseCommit(message.commit); | ||
if (output.sgxIntelRootCAHash != intelRootCAHash) { | ||
revert LCPClientZKDCAPUnexpectedIntelRootCAHash(); | ||
} | ||
if (output.mrenclave != bytes32(clientStorage.clientState.mrenclave)) { | ||
revert LCPClientClientStateUnexpectedMrenclave(); | ||
} | ||
|
||
if ( | ||
clientStorage.allowedStatuses.allowedQuoteStatuses[DCAPValidator.tcbStatusToString(output.tcbStatus)] | ||
!= RemoteAttestation.FLAG_ALLOWED | ||
) { | ||
revert LCPClientZKDCAPDisallowedTCBStatus(); | ||
} | ||
for (uint256 i = 0; i < output.advisoryIDs.length; i++) { | ||
if ( | ||
clientStorage.allowedStatuses.allowedAdvisories[output.advisoryIDs[i]] != RemoteAttestation.FLAG_ALLOWED | ||
) { | ||
revert LCPClientZKDCAPDisallowedAdvisoryID(); | ||
} | ||
} | ||
if (output.enclaveDebugEnabled != developmentMode) { | ||
revert LCPClientZKDCAPUnexpectedEnclaveDebugMode(); | ||
} | ||
|
||
// if `operator_signature` is empty, the operator address is zero | ||
address operator; | ||
if (message.operator_signature.length != 0) { | ||
operator = verifyECDSASignature( | ||
keccak256( | ||
LCPOperator.computeEIP712ZKDCAPRegisterEnclaveKey( | ||
clientStorage.clientState.zkdcap_verifier_infos[0], keccak256(message.commit) | ||
) | ||
), | ||
message.operator_signature | ||
); | ||
} | ||
if (output.operator != address(0) && output.operator != operator) { | ||
revert LCPClientAVRUnexpectedOperator(operator, output.operator); | ||
} | ||
if (block.timestamp < output.validityNotBeforeMax || block.timestamp > output.validityNotAfterMin) { | ||
revert LCPClientZKDCAPOutputNotValid(); | ||
} | ||
uint64 expiredAt = output.validityNotAfterMin; | ||
EKInfo storage ekInfo = clientStorage.ekInfos[output.enclaveKey]; | ||
if (ekInfo.expiredAt != 0) { | ||
if (ekInfo.operator != operator) { | ||
revert LCPClientEnclaveKeyUnexpectedOperator(ekInfo.operator, operator); | ||
} | ||
if (ekInfo.expiredAt != expiredAt) { | ||
revert LCPClientEnclaveKeyUnexpectedExpiredAt(); | ||
} | ||
// NOTE: if the key already exists, don't update any state | ||
return heights; | ||
} | ||
ekInfo.expiredAt = expiredAt; | ||
ekInfo.operator = operator; | ||
|
||
emit ZKDCAPRegisteredEnclaveKey(clientId, output.enclaveKey, expiredAt, operator); | ||
|
||
// Note: client and consensus state are not always updated in registerEnclaveKey | ||
return heights; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
pragma solidity ^0.8.12; | ||
|
||
import {LCPClientZKDCAPBase} from "./LCPClientZKDCAPBase.sol"; | ||
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; | ||
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; | ||
|
||
/// @custom:oz-upgrades-unsafe-allow external-library-linking | ||
contract LCPClientZKDCAPOwnableUpgradeable is LCPClientZKDCAPBase, UUPSUpgradeable, OwnableUpgradeable { | ||
/// @custom:oz-upgrades-unsafe-allow constructor | ||
constructor(address ibcHandler_, bool developmentMode_, bytes memory intelRootCA, address riscZeroVerifier_) | ||
LCPClientZKDCAPBase(ibcHandler_, developmentMode_, intelRootCA, riscZeroVerifier_) | ||
{} | ||
|
||
function initialize() public initializer { | ||
__UUPSUpgradeable_init(); | ||
__Ownable_init(msg.sender); | ||
} | ||
|
||
function _authorizeUpgrade(address newImplementation) internal virtual override onlyOwner {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.