forked from OffchainLabs/nitro-contracts
-
Notifications
You must be signed in to change notification settings - Fork 7
Contract changes for Stateless Batch Poster design #46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
Sneh1999
wants to merge
4
commits into
celestia-feat-stateless-batcher
Choose a base branch
from
stateless-batcher-contract-changes
base: celestia-feat-stateless-batcher
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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,175 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
import { | ||
V3QuoteVerifier | ||
} from "@automata-network/dcap-attestation/contracts/verifiers/V3QuoteVerifier.sol"; | ||
import {BELE} from "@automata-network/dcap-attestation/contracts/utils/BELE.sol"; | ||
import {Header} from "@automata-network/dcap-attestation/contracts/types/CommonStruct.sol"; | ||
import { | ||
HEADER_LENGTH, | ||
ENCLAVE_REPORT_LENGTH | ||
} from "@automata-network/dcap-attestation/contracts/types/Constants.sol"; | ||
import {EnclaveReport} from "@automata-network/dcap-attestation/contracts/types/V3Structs.sol"; | ||
import {BytesUtils} from "@automata-network/dcap-attestation/contracts/utils/BytesUtils.sol"; | ||
import "@openzeppelin/contracts/access/Ownable2Step.sol"; | ||
import {IEspressoSGXTEEVerifier} from "./IEspressoSGXTEEVerifier.sol"; | ||
|
||
/** | ||
* | ||
* @title Verifies quotes from the TEE and attests on-chain | ||
* @notice Contains the logic to verify a quote from the TEE and attest on-chain. It uses the V3QuoteVerifier contract | ||
* from automata to verify the quote. Along with some additional verification logic. | ||
*/ | ||
contract EspressoSGXTEEVerifier is IEspressoSGXTEEVerifier, Ownable2Step { | ||
using BytesUtils for bytes; | ||
|
||
// V3QuoteVerififer contract from automata to verify the quote | ||
V3QuoteVerifier public quoteVerifier; | ||
|
||
mapping(bytes32 => bool) public registeredEnclaveHash; | ||
mapping(bytes32 => bool) public registeredEnclaveSigner; | ||
mapping(address => bool) public registeredSigners; | ||
|
||
constructor(bytes32 enclaveHash, bytes32 enclaveSigner, address _quoteVerifier) { | ||
quoteVerifier = V3QuoteVerifier(_quoteVerifier); | ||
registeredEnclaveHash[enclaveHash] = true; | ||
registeredEnclaveSigner[enclaveSigner] = true; | ||
} | ||
|
||
/* | ||
@notice Verify a quote from the TEE and attest on-chain | ||
The verification is considered successful if the function does not revert. | ||
@param rawQuote The quote from the TEE | ||
@param reportDataHash The hash of the report data | ||
*/ | ||
function verify( | ||
bytes calldata rawQuote, | ||
bytes32 reportDataHash | ||
) public view returns (EnclaveReport memory) { | ||
// Parse the header | ||
Header memory header = parseQuoteHeader(rawQuote); | ||
|
||
// Currently only version 3 is supported | ||
if (header.version != 3) { | ||
revert InvalidHeaderVersion(); | ||
} | ||
|
||
// Verify the quote | ||
(bool success, ) = quoteVerifier.verifyQuote(header, rawQuote); | ||
if (!success) { | ||
revert InvalidQuote(); | ||
} | ||
|
||
// Parse enclave quote | ||
uint256 lastIndex = HEADER_LENGTH + ENCLAVE_REPORT_LENGTH; | ||
EnclaveReport memory localReport; | ||
(success, localReport) = parseEnclaveReport(rawQuote[HEADER_LENGTH:lastIndex]); | ||
if (!success) { | ||
revert FailedToParseEnclaveReport(); | ||
} | ||
|
||
// Check that mrEnclave match | ||
if (!registeredEnclaveHash[localReport.mrEnclave]) { | ||
revert InvalidEnclaveHash(); | ||
} | ||
|
||
if (!registeredEnclaveSigner[localReport.mrSigner]) { | ||
revert InvalidEnclaveSigner(); | ||
} | ||
|
||
// Verify that the reportDataHash if the hash signed by the TEE | ||
// We do not check the signature because `quoteVerifier.verifyQuote` already does that | ||
if (reportDataHash != bytes32(localReport.reportData.substring(0, 32))) { | ||
revert InvalidReportDataHash(); | ||
} | ||
|
||
return localReport; | ||
} | ||
|
||
/* | ||
@notice Register a new signer by verifying a quote from the TEE | ||
@param attestation The attestation from the TEE | ||
@param data which the TEE has attested to | ||
*/ | ||
function registerSigner(bytes calldata attestation, bytes calldata data) external { | ||
// Check that the data length is 20 bytes because an address is 20 bytes | ||
if (data.length != 20) { | ||
revert InvalidDataLength(); | ||
} | ||
bytes32 paddedSignerAddress = keccak256(data); | ||
// Convert data to address | ||
EnclaveReport memory localReport = verify(attestation, paddedSignerAddress); | ||
|
||
if (localReport.reportData.length < 20) { | ||
revert ReportDataTooShort(); | ||
} | ||
|
||
address signer = address(uint160(bytes20(data[:20]))); | ||
|
||
// Check if the extracted address is valid | ||
if (signer == address(0)) { | ||
revert InvalidSignerAddress(); // Custom revert if the address is invalid | ||
} | ||
// Mark the signer as registered | ||
registeredSigners[signer] = true; | ||
} | ||
|
||
/* | ||
@notice Parses the header from the quote | ||
@param rawQuote The raw quote in bytes | ||
@return header The parsed header | ||
*/ | ||
function parseQuoteHeader(bytes calldata rawQuote) public pure returns (Header memory header) { | ||
header = Header({ | ||
version: uint16(BELE.leBytesToBeUint(rawQuote[0:2])), | ||
attestationKeyType: bytes2(rawQuote[2:4]), | ||
teeType: bytes4(uint32(BELE.leBytesToBeUint(rawQuote[4:8]))), | ||
qeSvn: bytes2(rawQuote[8:10]), | ||
pceSvn: bytes2(rawQuote[10:12]), | ||
qeVendorId: bytes16(rawQuote[12:28]), | ||
userData: bytes20(rawQuote[28:48]) | ||
}); | ||
} | ||
|
||
/* | ||
@notice Parses the enclave report from the quote | ||
@param rawEnclaveReport The raw enclave report from the quote in bytes | ||
@return success True if the enclave report was parsed successfully | ||
@return enclaveReport The parsed enclave report | ||
*/ | ||
function parseEnclaveReport( | ||
bytes memory rawEnclaveReport | ||
) public pure returns (bool success, EnclaveReport memory enclaveReport) { | ||
if (rawEnclaveReport.length != ENCLAVE_REPORT_LENGTH) { | ||
return (false, enclaveReport); | ||
} | ||
enclaveReport.cpuSvn = bytes16(rawEnclaveReport.substring(0, 16)); | ||
enclaveReport.miscSelect = bytes4(rawEnclaveReport.substring(16, 4)); | ||
enclaveReport.reserved1 = bytes28(rawEnclaveReport.substring(20, 28)); | ||
enclaveReport.attributes = bytes16(rawEnclaveReport.substring(48, 16)); | ||
enclaveReport.mrEnclave = bytes32(rawEnclaveReport.substring(64, 32)); | ||
enclaveReport.reserved2 = bytes32(rawEnclaveReport.substring(96, 32)); | ||
enclaveReport.mrSigner = bytes32(rawEnclaveReport.substring(128, 32)); | ||
enclaveReport.reserved3 = rawEnclaveReport.substring(160, 96); | ||
enclaveReport.isvProdId = uint16(BELE.leBytesToBeUint(rawEnclaveReport.substring(256, 2))); | ||
enclaveReport.isvSvn = uint16(BELE.leBytesToBeUint(rawEnclaveReport.substring(258, 2))); | ||
enclaveReport.reserved4 = rawEnclaveReport.substring(260, 60); | ||
enclaveReport.reportData = rawEnclaveReport.substring(320, 64); | ||
success = true; | ||
} | ||
|
||
function setEnclaveHash(bytes32 enclaveHash, bool valid) external onlyOwner { | ||
registeredEnclaveHash[enclaveHash] = valid; | ||
emit EnclaveHashSet(enclaveHash, valid); | ||
} | ||
|
||
function setEnclaveSigner(bytes32 enclaveSigner, bool valid) external onlyOwner { | ||
registeredEnclaveSigner[enclaveSigner] = valid; | ||
emit EnclaveSignerSet(enclaveSigner, valid); | ||
} | ||
|
||
function deleteRegisteredSigner(address signer) external onlyOwner { | ||
delete registeredSigners[signer]; | ||
} | ||
} |
This file contains hidden or 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,144 +1,104 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
import { | ||
V3QuoteVerifier | ||
} from "@automata-network/dcap-attestation/contracts/verifiers/V3QuoteVerifier.sol"; | ||
import {BELE} from "@automata-network/dcap-attestation/contracts/utils/BELE.sol"; | ||
import {Header} from "@automata-network/dcap-attestation/contracts/types/CommonStruct.sol"; | ||
import { | ||
IQuoteVerifier | ||
} from "@automata-network/dcap-attestation/contracts/interfaces/IQuoteVerifier.sol"; | ||
import { | ||
HEADER_LENGTH, | ||
ENCLAVE_REPORT_LENGTH | ||
} from "@automata-network/dcap-attestation/contracts/types/Constants.sol"; | ||
import {EnclaveReport} from "@automata-network/dcap-attestation/contracts/types/V3Structs.sol"; | ||
import {BytesUtils} from "@automata-network/dcap-attestation/contracts/utils/BytesUtils.sol"; | ||
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; | ||
import "@openzeppelin/contracts/access/Ownable2Step.sol"; | ||
import {IEspressoSGXTEEVerifier} from "./IEspressoSGXTEEVerifier.sol"; | ||
import {IEspressoTEEVerifier} from "./IEspressoTEEVerifier.sol"; | ||
|
||
/** | ||
* | ||
* @title Verifies quotes from the TEE and attests on-chain | ||
* @notice Contains the logic to verify a quote from the TEE and attest on-chain. It uses the V3QuoteVerifier contract | ||
* from automata to verify the quote. Along with some additional verification logic. | ||
*/ | ||
contract EspressoTEEVerifier is IEspressoTEEVerifier, Ownable2Step { | ||
event MREnclaveSet(bytes32 indexed mrEnclave); | ||
event MRSignerSet(bytes32 indexed mrSigner); | ||
@title EspressoTEEVerifier | ||
@author Espresso Systems (https://espresso.systems) | ||
@notice This contract is used to resgister a signer which has been attested by the TEE | ||
*/ | ||
contract EspressoTEEVerifier is Ownable2Step, IEspressoTEEVerifier { | ||
IEspressoSGXTEEVerifier public espressoSGXTEEVerifier; | ||
|
||
using BytesUtils for bytes; | ||
|
||
// V3QuoteVerififer contract from automata to verify the quote | ||
V3QuoteVerifier public quoteVerifier; | ||
bytes32 public mrEnclave; | ||
bytes32 public mrSigner; | ||
|
||
constructor(bytes32 _mrEnclave, bytes32 _mrSigner, address _quoteVerifier) { | ||
quoteVerifier = V3QuoteVerifier(_quoteVerifier); | ||
mrEnclave = _mrEnclave; | ||
mrSigner = _mrSigner; | ||
constructor(IEspressoSGXTEEVerifier _espressoSGXTEEVerifier) { | ||
espressoSGXTEEVerifier = _espressoSGXTEEVerifier; | ||
} | ||
|
||
/* | ||
@notice Verify a quote from the TEE and attest on-chain | ||
The verification is considered successful if the function does not revert. | ||
@param rawQuote The quote from the TEE | ||
@param reportDataHash The hash of the report data | ||
*/ | ||
function verify(bytes calldata rawQuote, bytes32 reportDataHash) external view { | ||
// Parse the header | ||
Header memory header = parseQuoteHeader(rawQuote); | ||
|
||
// Currently only version 3 is supported | ||
if (header.version != 3) { | ||
revert InvalidHeaderVersion(); | ||
} | ||
|
||
// Verify the quote | ||
(bool success, ) = quoteVerifier.verifyQuote(header, rawQuote); | ||
if (!success) { | ||
revert InvalidQuote(); | ||
} | ||
|
||
// Parse enclave quote | ||
uint256 lastIndex = HEADER_LENGTH + ENCLAVE_REPORT_LENGTH; | ||
EnclaveReport memory localReport; | ||
(success, localReport) = parseEnclaveReport(rawQuote[HEADER_LENGTH:lastIndex]); | ||
if (!success) { | ||
revert FailedToParseEnclaveReport(); | ||
} | ||
/** | ||
@notice This function is used to verify the signature of the user data | ||
@param signature The signature of the user data | ||
@param userDataHash The hash of the user data | ||
*/ | ||
function verify(bytes memory signature, bytes32 userDataHash) external view { | ||
address signer = ECDSA.recover(userDataHash, signature); | ||
|
||
// Check that mrEnclave and mrSigner match | ||
if (localReport.mrEnclave != mrEnclave || localReport.mrSigner != mrSigner) { | ||
revert InvalidMREnclaveOrSigner(); | ||
if (!espressoSGXTEEVerifier.registeredSigners(signer)) { | ||
revert InvalidSignature(); | ||
} | ||
} | ||
|
||
// Verify that the reportDataHash if the hash signed by the TEE | ||
// We do not check the signature because `quoteVerifier.verifyQuote` already does that | ||
if (reportDataHash != bytes32(localReport.reportData.substring(0, 32))) { | ||
revert InvalidReportDataHash(); | ||
/* @notice Register a new signer by verifying a quote from the TEE | ||
@param attestation The attestation from the TEE | ||
@param data when registering a signer, data can be passed for each TEE type | ||
which can be any additiona data that is required for registering a signer | ||
@param teeType The type of TEE | ||
*/ | ||
function registerSigner( | ||
bytes calldata attestation, | ||
bytes calldata data, | ||
TeeType teeType | ||
) external { | ||
if (teeType == TeeType.SGX) { | ||
espressoSGXTEEVerifier.registerSigner(attestation, data); | ||
return; | ||
} | ||
revert UnsupportedTeeType(); | ||
} | ||
|
||
/* | ||
@notice Parses the header from the quote | ||
@param rawQuote The raw quote in bytes | ||
@return header The parsed header | ||
*/ | ||
function parseQuoteHeader(bytes calldata rawQuote) public pure returns (Header memory header) { | ||
header = Header({ | ||
version: uint16(BELE.leBytesToBeUint(rawQuote[0:2])), | ||
attestationKeyType: bytes2(rawQuote[2:4]), | ||
teeType: bytes4(uint32(BELE.leBytesToBeUint(rawQuote[4:8]))), | ||
qeSvn: bytes2(rawQuote[8:10]), | ||
pceSvn: bytes2(rawQuote[10:12]), | ||
qeVendorId: bytes16(rawQuote[12:28]), | ||
userData: bytes20(rawQuote[28:48]) | ||
}); | ||
/** | ||
@notice This function retrieves whether a signer is registered or not | ||
@param signer The address of the signer | ||
@param teeType The type of TEE | ||
*/ | ||
function registeredSigners(address signer, TeeType teeType) external view returns (bool) { | ||
if (teeType == TeeType.SGX) { | ||
return espressoSGXTEEVerifier.registeredSigners(signer); | ||
} | ||
revert UnsupportedTeeType(); | ||
} | ||
|
||
/* | ||
@notice Parses the enclave report from the quote | ||
@param rawEnclaveReport The raw enclave report from the quote in bytes | ||
@return success True if the enclave report was parsed successfully | ||
@return enclaveReport The parsed enclave report | ||
*/ | ||
function parseEnclaveReport( | ||
bytes memory rawEnclaveReport | ||
) public pure returns (bool success, EnclaveReport memory enclaveReport) { | ||
if (rawEnclaveReport.length != ENCLAVE_REPORT_LENGTH) { | ||
return (false, enclaveReport); | ||
/** | ||
@notice This function retrieves whether an enclave hash is registered or not | ||
@param enclaveHash The hash of the enclave | ||
@param teeType The type of TEE | ||
*/ | ||
function registeredEnclaveHashes( | ||
bytes32 enclaveHash, | ||
TeeType teeType | ||
) external view returns (bool) { | ||
if (teeType == TeeType.SGX) { | ||
return espressoSGXTEEVerifier.registeredEnclaveHash(enclaveHash); | ||
} | ||
enclaveReport.cpuSvn = bytes16(rawEnclaveReport.substring(0, 16)); | ||
enclaveReport.miscSelect = bytes4(rawEnclaveReport.substring(16, 4)); | ||
enclaveReport.reserved1 = bytes28(rawEnclaveReport.substring(20, 28)); | ||
enclaveReport.attributes = bytes16(rawEnclaveReport.substring(48, 16)); | ||
enclaveReport.mrEnclave = bytes32(rawEnclaveReport.substring(64, 32)); | ||
enclaveReport.reserved2 = bytes32(rawEnclaveReport.substring(96, 32)); | ||
enclaveReport.mrSigner = bytes32(rawEnclaveReport.substring(128, 32)); | ||
enclaveReport.reserved3 = rawEnclaveReport.substring(160, 96); | ||
enclaveReport.isvProdId = uint16(BELE.leBytesToBeUint(rawEnclaveReport.substring(256, 2))); | ||
enclaveReport.isvSvn = uint16(BELE.leBytesToBeUint(rawEnclaveReport.substring(258, 2))); | ||
enclaveReport.reserved4 = rawEnclaveReport.substring(260, 60); | ||
enclaveReport.reportData = rawEnclaveReport.substring(320, 64); | ||
success = true; | ||
revert UnsupportedTeeType(); | ||
} | ||
|
||
/* | ||
* @dev Set the mrEnclave of the contract | ||
/** | ||
@notice This function retrieves whether an enclave signer is registered or not | ||
@param enclaveSigner The enclave signer | ||
@param teeType The type of TEE | ||
*/ | ||
function setMrEnclave(bytes32 _mrEnclave) external onlyOwner { | ||
emit MREnclaveSet(_mrEnclave); | ||
mrEnclave = _mrEnclave; | ||
|
||
function registeredEnclaveSigners( | ||
bytes32 enclaveSigner, | ||
TeeType teeType | ||
) external view returns (bool) { | ||
if (teeType == TeeType.SGX) { | ||
return espressoSGXTEEVerifier.registeredEnclaveSigner(enclaveSigner); | ||
} | ||
revert UnsupportedTeeType(); | ||
} | ||
|
||
/* | ||
* @dev Set the mrSigner of the contract | ||
@notice Set the EspressoSGXTEEVerifier | ||
@param _espressoSGXTEEVerifier The address of the EspressoSGXTEEVerifier | ||
*/ | ||
function setMrSigner(bytes32 _mrSigner) external onlyOwner { | ||
emit MRSignerSet(_mrSigner); | ||
mrSigner = _mrSigner; | ||
function setEspressoSGXTEEVerifier( | ||
IEspressoSGXTEEVerifier _espressoSGXTEEVerifier | ||
) public onlyOwner { | ||
espressoSGXTEEVerifier = _espressoSGXTEEVerifier; | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How are we planning to extend this for future tee types?
I think this function might want to match on a
teeType
argument to determine which verifier to check like the registerSigner function.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ECDSA signature will be same across all Tee
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes but this contract will be responsible for checking multiple different potential sub contracts that could have the signer as a verified party correct? Like an instance
EspressoTEEVerifier
should be capable of verifying with aEspressoSGXTEEVerifier
and a hypotheticalEspressoNitroTEEVerifier
right?