-
Notifications
You must be signed in to change notification settings - Fork 0
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
Add additional verification to EspressoTEEVerifier #28
Changes from all commits
62dbad6
29d1104
13fe218
c710201
7d25c91
7327a3d
7c3d1bd
cc05e5d
c308e83
701f70d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,50 +14,90 @@ import { | |
ENCLAVE_REPORT_LENGTH | ||
} from "@automata-network/dcap-attestation/contracts/types/Constants.sol"; | ||
import {EnclaveReport} from "@automata-network/dcap-attestation/contracts/types/V3Structs.sol"; | ||
import { | ||
V3QuoteVerifier | ||
} from "@automata-network/dcap-attestation/contracts/verifiers/V3QuoteVerifier.sol"; | ||
import {BytesUtils} from "@automata-network/dcap-attestation/contracts/utils/BytesUtils.sol"; | ||
import {Ownable} from "solady/auth/Ownable.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 | ||
* to verify the quote. Along with some additional verification logic. | ||
* from automata to verify the quote. Along with some additional verification logic. | ||
*/ | ||
contract EspressoTEEVerifier is Ownable { | ||
using BytesUtils for bytes; | ||
|
||
// We only support version 3 for now | ||
error InvalidHeaderVersion(); | ||
// This error is thrown when the automata verification fails | ||
error InvalidQuote(); | ||
// This error is thrown when the enclave report fails to parse | ||
error FailedToParseEnclaveReport(); | ||
// This error is thrown when the mrEnclave and mrSigner don't match | ||
error InvalidMREnclaveOrSigner(); | ||
// This error is thrown when the reportDataHash doesn't match the hash signed by the TEE | ||
error InvalidReportDataHash(); | ||
|
||
contract EspressoTEEVerifier is V3QuoteVerifier { | ||
constructor(address _router) V3QuoteVerifier(_router) {} | ||
// V3QuoteVerififer contract from automata to verify the quote | ||
V3QuoteVerifier public quoteVerifier; | ||
bytes32 public mrEnclave; | ||
bytes32 public mrSigner; | ||
|
||
/** | ||
constructor(bytes32 _mrEnclave, bytes32 _mrSigner, address _quoteVerifier) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The reason I am accepting the |
||
quoteVerifier = V3QuoteVerifier(_quoteVerifier); | ||
mrEnclave = _mrEnclave; | ||
mrSigner = _mrSigner; | ||
_initializeOwner(msg.sender); | ||
} | ||
|
||
/* | ||
@notice Verify a quote from the TEE and attest on-chain | ||
@param rawQuote The quote from the TEE | ||
@return success True if the quote was verified and attested on-chain | ||
*/ | ||
function verify(bytes calldata rawQuote) external view returns (bool success) { | ||
@param reportDataHash The hash of the report data | ||
*/ | ||
function verify(bytes calldata rawQuote, bytes32 reportDataHash) external { | ||
// Parse the header | ||
Header memory header = _parseQuoteHeader(rawQuote); | ||
Header memory header = parseQuoteHeader(rawQuote); | ||
|
||
// Currently only version 3 is supported | ||
if (header.version != 3) { | ||
return false; | ||
revert InvalidHeaderVersion(); | ||
} | ||
|
||
(success, ) = this.verifyQuote(header, rawQuote); | ||
// Verify the quote | ||
(bool success, ) = quoteVerifier.verifyQuote(header, rawQuote); | ||
if (!success) { | ||
return false; | ||
revert InvalidQuote(); | ||
} | ||
|
||
// Parse enclave quote | ||
// // Parse enclave quote | ||
uint256 offset = HEADER_LENGTH + ENCLAVE_REPORT_LENGTH; | ||
EnclaveReport memory localReport; | ||
(success, localReport) = parseEnclaveReport(rawQuote[HEADER_LENGTH:offset]); | ||
if (!success) { | ||
return false; | ||
revert FailedToParseEnclaveReport(); | ||
} | ||
|
||
return true; | ||
// Check that mrEnclave and mrSigner match | ||
if (localReport.mrEnclave != mrEnclave || localReport.mrSigner != mrSigner) { | ||
revert InvalidMREnclaveOrSigner(); | ||
} | ||
|
||
// TODO: Use the parsed enclave report (localReport) to do other verifications | ||
// 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(); | ||
} | ||
} | ||
|
||
function _parseQuoteHeader( | ||
bytes calldata rawQuote | ||
) private pure returns (Header memory header) { | ||
/* | ||
@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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didnt see any harm in making it public and I think it might turn out helpful for debugging in the future. Its a pure function so takes very less gas as well. |
||
bytes2 attestationKeyType = bytes2(rawQuote[2:4]); | ||
bytes2 qeSvn = bytes2(rawQuote[8:10]); | ||
bytes2 pceSvn = bytes2(rawQuote[10:12]); | ||
|
@@ -73,4 +113,45 @@ contract EspressoTEEVerifier is V3QuoteVerifier { | |
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; | ||
} | ||
|
||
/* | ||
* @dev Set the mrEnclave of the contract | ||
*/ | ||
function setMrEnclave(bytes32 _mrEnclave) external onlyOwner { | ||
mrEnclave = _mrEnclave; | ||
} | ||
|
||
/* | ||
* @dev Set the mrSigner of the contract | ||
*/ | ||
function setMrSigner(bytes32 _mrSigner) external onlyOwner { | ||
mrSigner = _mrSigner; | ||
} | ||
} |
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.
Is there a particular reason we're not using the openzeppelin
Ownable
here?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.
Automata uses this library for ownable. Apparently is more gas efficient