Skip to content

Commit

Permalink
Add additional verification to EspressoTEEVerifier (#28)
Browse files Browse the repository at this point in the history
* Add additional verification to EspressoTEEVerifier

* fix tests

* fix tests

* cleanup

* revert broken tests

* fix ci

* fix ci

* address comments

* address comments

* add code docs
  • Loading branch information
Sneh1999 authored and ImJeremyHe committed Dec 4, 2024
1 parent 69c529e commit c909181
Show file tree
Hide file tree
Showing 16 changed files with 877 additions and 539 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/contract-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ jobs:
no-token-bridge: true
no-l3-token-bridge: true
nitro-contracts-branch: '${{ github.event.pull_request.head.sha || github.sha }}'
nitro-testnode-ref: integration
nitro-testnode-ref: celestia-integration
nitro-testnode-repo: EspressoSystems/nitro-testnode

- name: Install Foundry
Expand Down Expand Up @@ -235,7 +235,7 @@ jobs:
no-token-bridge: true
no-l3-token-bridge: true
nitro-contracts-branch: '${{ github.event.pull_request.head.sha || github.sha }}'
nitro-testnode-ref: integration
nitro-testnode-ref: celestia-integration
nitro-testnode-repo: EspressoSystems/nitro-testnode

- name: Install Foundry
Expand Down Expand Up @@ -282,7 +282,7 @@ jobs:
no-token-bridge: true
no-l3-token-bridge: true
nitro-contracts-branch: '${{ github.event.pull_request.head.sha || github.sha }}'
nitro-testnode-ref: 'integration'
nitro-testnode-ref: 'celestia-integration'
nitro-testnode-repo: EspressoSystems/nitro-testnode

- name: Install Foundry
Expand Down
2 changes: 1 addition & 1 deletion lib/automata-dcap-attestation
Submodule automata-dcap-attestation updated 47 files
+0 −21 .env.example
+1 −1 .github/workflows/build.yml
+16 −11 .github/workflows/slither.yml
+3 −0 .gitmodules
+61 −30 README.md
+19 −19 broadcast/AttestationScript.s.sol/1398243/configVerifier-latest.json
+56 −0 broadcast/AttestationScript.s.sol/1398243/configureZk-latest.json
+25 −28 broadcast/AttestationScript.s.sol/1398243/deployEntrypoint-latest.json
+56 −0 broadcast/DeployRouter.s.sol/1398243/setAuthorizedCaller-latest.json
+60 −0 broadcast/DeployRouter.s.sol/1398243/updateConfig-latest.json
+19 −18 broadcast/DeployV3.s.sol/1398243/run-latest.json
+20 −19 broadcast/DeployV4.s.sol/1398243/run-latest.json
+171 −0 contracts/AttestationEntrypointBase.sol
+0 −100 contracts/AutomataDcapAttestation.sol
+42 −0 contracts/AutomataDcapAttestationFee.sol
+81 −55 contracts/PCCSRouter.sol
+68 −0 contracts/bases/FeeManagerBase.sol
+17 −24 contracts/bases/QuoteVerifierBase.sol
+14 −76 contracts/bases/X509ChainBase.sol
+9 −4 contracts/bases/tcb/TCBInfoV3Base.sol
+0 −41 contracts/interfaces/IAttestation.sol
+6 −0 contracts/interfaces/IPCCSRouter.sol
+15 −3 contracts/interfaces/IQuoteVerifier.sol
+1 −0 contracts/types/CommonStruct.sol
+1 −1 contracts/types/Constants.sol
+4 −0 contracts/utils/BELE.sol
+12 −4 contracts/utils/P256Verifier.sol
+18 −12 contracts/verifiers/V3QuoteVerifier.sol
+31 −20 contracts/verifiers/V4QuoteVerifier.sol
+26 −0 env/.testnet.env.example
+9 −7 forge-script/AttestationScript.s.sol
+0 −24 forge-script/DeployRisc0Verifier.s.sol
+18 −0 forge-script/DeployRouter.s.sol
+61 −0 forge-script/utils/P256Configuration.sol
+13 −5 forge-script/verifiers/DeployV3.s.sol
+13 −5 forge-script/verifiers/DeployV4.s.sol
+106 −0 forge-test/AutomataDcapAttestationFeeTest.t.sol
+122 −22 forge-test/AutomataDcapAttestationTest.t.sol
+10 −20 forge-test/utils/PCCSSetupBase.sol
+2 −2 forge-test/utils/RiscZeroSetup.sol
+11 −0 forge-test/utils/succinct/Groth16Setup.sol
+11 −0 forge-test/utils/succinct/PlonkSetup.sol
+5 −2 foundry.toml
+1 −1 lib/automata-on-chain-pccs
+1 −1 lib/forge-std
+1 −1 lib/risc0-ethereum
+1 −0 lib/sp1-contracts
2 changes: 1 addition & 1 deletion lib/forge-std
Submodule forge-std updated 2 files
+99 −64 src/Vm.sol
+2 −2 test/Vm.t.sol
18 changes: 14 additions & 4 deletions scripts/deployEspressoTEEVerifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,25 @@ import { deployContract } from './deploymentUtils'
async function main() {
const [deployer] = await ethers.getSigners()

const pccsRouterAddress = process.env.PCCS_ROUTER_ADDRESS as string
if (!pccsRouterAddress) {
throw new Error('PCCS_ROUTER_ADDRESS not set')
const v3QuoteVerifier = process.env.V3_QUOTE_VERIFIER_ADDRESS
if (!v3QuoteVerifier) {
throw new Error('V3_QUOTE_VERIFIER_ADDRESS not set')
}

const mrEnclave = process.env.MR_ENCLAVE
if (!mrEnclave) {
throw new Error('MR_ENCLAVE not set')
}

const mrSigner = process.env.MR_SIGNER
if (!mrSigner) {
throw new Error('MR_SIGNER not set')
}

const esperssoTEEVerifier = await deployContract(
'EspressoTEEVerifier',
deployer,
[pccsRouterAddress],
[mrEnclave, mrSigner, v3QuoteVerifier],
true
)
console.log(
Expand Down
117 changes: 99 additions & 18 deletions src/bridge/EspressoTEEVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
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) {
bytes2 attestationKeyType = bytes2(rawQuote[2:4]);
bytes2 qeSvn = bytes2(rawQuote[8:10]);
bytes2 pceSvn = bytes2(rawQuote[10:12]);
Expand All @@ -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;
}
}
3 changes: 3 additions & 0 deletions src/bridge/ISequencerInbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ interface ISequencerInbox is IDelayedMessageProvider {
/// @dev a keyset was invalidated
event InvalidateKeyset(bytes32 indexed keysetHash);

/// @dev a TEE attestation quote was verified
event TEEAttestationQuoteVerified(uint256 indexed seqMessageIndex);

function totalDelayedMessagesRead() external view returns (uint256);

function bridge() external view returns (IBridge);
Expand Down
83 changes: 56 additions & 27 deletions src/bridge/SequencerInbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,11 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox
__LEGACY_MAX_TIME_VARIATION.futureSeconds = 0;
}

function initialize(
IBridge bridge_,
ISequencerInbox.MaxTimeVariation calldata maxTimeVariation_
) external onlyDelegated {
/**
Deprecated because we created another `initialize` function that accepts the `EspressoTEEVerifier` contract
address as a parameter which is used by the `SequencerInbox` contract to verify the TEE attestation quote.
*/
function initialize(IBridge, ISequencerInbox.MaxTimeVariation calldata) external onlyDelegated {
revert Deprecated();
}

Expand Down Expand Up @@ -361,13 +362,17 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox
revert Deprecated();
}

/**
Deprecated because we added a new method with TEE attestation quote
to verify that the batch is posted by the batch poster running in TEE.
*/
function addSequencerL2BatchFromOrigin(
uint256 sequenceNumber,
bytes calldata data,
uint256 afterDelayedMessagesRead,
uint256,
bytes calldata,
uint256,
IGasRefunder gasRefunder,
uint256 prevMessageCount,
uint256 newMessageCount
uint256,
uint256
) external refundsGas(gasRefunder, IReader4844(address(0))) {
revert Deprecated();
}
Expand All @@ -385,10 +390,20 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox
if (msg.sender != tx.origin) revert NotOrigin();
if (!isBatchPoster[msg.sender]) revert NotBatchPoster();

bool success = espressoTEEVerifier.verify(quote);
if (!success) {
revert InvalidTEEAttestationQuote();
}
// take keccak2256 hash of all the function arguments except the quote
bytes32 reportDataHash = keccak256(
abi.encode(
sequenceNumber,
data,
afterDelayedMessagesRead,
address(gasRefunder),
prevMessageCount,
newMessageCount
)
);
// verify the quote for the batch poster running in the TEE
espressoTEEVerifier.verify(quote, reportDataHash);
emit TEEAttestationQuoteVerified(sequenceNumber);

(bytes32 dataHash, IBridge.TimeBounds memory timeBounds) = formCallDataHash(
data,
Expand Down Expand Up @@ -492,26 +507,30 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox
}
}

/**
Deprecated because we added a new method with TEE attestation quote
to verify that the batch is posted by the batch poster running in TEE.
*/
function addSequencerL2Batch(
uint256 sequenceNumber,
bytes calldata data,
uint256 afterDelayedMessagesRead,
uint256,
bytes calldata,
uint256,
IGasRefunder gasRefunder,
uint256 prevMessageCount,
uint256 newMessageCount
uint256,
uint256
) external override refundsGas(gasRefunder, IReader4844(address(0))) {
revert Deprecated();
}

/*
* addSequencerL2Batch is called by either the rollup admin or batch poster
* running in TEE to add a new L2 batch to the rollup
* @param sequenceNumber - the sequence number of the L2 batch
* @param data - the data of the L2 batch
* running in TEE to add a new batch
* @param sequenceNumber - the sequence number of the batch
* @param data - the data of the batch
* @param afterDelayedMessagesRead - the number of delayed messages read by the sequencer
* @param gasRefunder - the gas refunder contract
* @param prevMessageCount - the number of messages in the previous L2 batch
* @param newMessageCount - the number of messages in the new L2 batch
* @param prevMessageCount - the number of messages in the previous batch
* @param newMessageCount - the number of messages in the new batch
* @param quote - the atttestation quote from the TEE
*/
function addSequencerL2Batch(
Expand All @@ -528,10 +547,20 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox
// Only check the attestation quote if the batch has been posted by the
// batch poster
if (isBatchPoster[msg.sender]) {
bool success = espressoTEEVerifier.verify(quote);
if (!success) {
revert InvalidTEEAttestationQuote();
}
// take keccak2256 hash of all the function arguments except the quote
bytes32 reportDataHash = keccak256(
abi.encode(
sequenceNumber,
data,
afterDelayedMessagesRead,
address(gasRefunder),
prevMessageCount,
newMessageCount
)
);
// verify the quote for the batch poster running in the TEE
espressoTEEVerifier.verify(quote, reportDataHash);
emit TEEAttestationQuoteVerified(sequenceNumber);
}
(bytes32 dataHash, IBridge.TimeBounds memory timeBounds) = formCallDataHash(
data,
Expand Down
5 changes: 4 additions & 1 deletion src/mocks/EspressoTEEVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ pragma solidity ^0.8.0;
contract EspressoTEEVerifierMock {
constructor() {}

function verify(bytes calldata rawQuote) external view returns (bool success) {
function verify(
bytes calldata rawQuote,
bytes32 reportDataHash
) external view returns (bool success) {
return (true);
}
}
16 changes: 11 additions & 5 deletions src/rollup/BridgeCreator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,18 @@ contract BridgeCreator is Ownable {
return frame;
}

/// @dev Deprecated
/*
* Deprecated because we added a new method to create bridges
* which requires the address of the `EspressoTEEVerifier` contract
* to be passed in. `EspressoTEEVerifier` is required by the sequencer
* inbox contract to verify the quote from the TEE to check if the batch has
* been posted by a batch poster running in the TEE.
*/
function createBridge(
address adminProxy,
address rollup,
address nativeToken,
ISequencerInbox.MaxTimeVariation calldata maxTimeVariation
address,
address,
address,
ISequencerInbox.MaxTimeVariation calldata
) external returns (BridgeContracts memory) {
revert Deprecated();
}
Expand Down
Loading

0 comments on commit c909181

Please sign in to comment.