11// SPDX-License-Identifier: MIT
22pragma solidity 0.8.28 ;
33
4- import "solmate/src/auth/Owned.sol " ;
54import {AutomataDcapAttestationFee} from "../lib/automata-dcap-attestation/evm/contracts/AutomataDcapAttestationFee.sol " ;
65import {TD10ReportBody} from "automata-dcap-attestation/contracts/types/V4Structs.sol " ;
7- import {TD_REPORT10_LENGTH, TDX_TEE} from "automata-dcap-attestation/contracts/types/Constants.sol " ;
8- import {BytesUtils} from "@automata-network/on-chain-pccs/utils/BytesUtils.sol " ;
6+ import {QuoteParser, WorkloadId} from "./utils/QuoteParser.sol " ;
97
108/**
119 * @title AllowList
1210 * @dev A contract for managing trusted execution environment (TEE) identities and configurations
13- * using Intel DCAP attestation
11+ * using Automata's Intel DCAP attestation
1412 */
15- contract AllowList is Owned {
16- using BytesUtils for bytes ;
13+ contract AllowList {
1714
18- // This is the number of bytes in the Output struct that come before the quoteBody.
19- // See the Output struct definition in the Automata DCAP Attestation repo:
20- // https://github.com/automata-network/automata-dcap-attestation/blob/evm-v1.0.0/evm/contracts/types/CommonStruct.sol#L113
21- // 13 bytes = quoteVersion (2 bytes) + tee (4 byte) + tcbStatus (1 byte) + fmspcBytes (6 bytes)
22- uint256 public constant SERIALIZED_OUTPUT_OFFSET = 13 ;
15+ // Constants
2316
2417 // Maximum size for byte arrays to prevent DoS attacks
2518 uint256 public constant MAX_BYTES_SIZE = 20 * 1024 ; // 20KB limit
2619
27- // The TDX version of the quote Flashtestation's accepts
28- uint256 public constant ACCEPTED_TDX_VERSION = 4 ;
20+ // Structs
2921
3022 // TEE identity and status tracking
31- struct TEEDevice {
32- bytes32 publicKey ; // Public key of the TEE device
33- uint64 lastActiveTime ; // Timestamp of last activity
34- bool isActive ; // Whether the device is currently active
23+ struct RegisteredTEE {
24+ uint64 registeredAt ; // The most recent timestamp that the TEE last registered with a valid quote
25+ WorkloadId workloadId ; // The workloadID of the TEE device
26+ bytes rawQuote ; // The raw quote from the TEE device, which is stored to allow for future quote re-verification
3527 }
3628
37- // Mapping from TEE identity to device information
38- mapping (bytes32 => TEEDevice) public teeDevices;
29+ // Storage Variables
3930
40- // State variables
41- string [] public instanceDomainNames;
31+ // The address of the Automata DCAP Attestation contract, which verifies TEE quotes.
32+ // This is deployed by Automata, and once set on the AllowList, it cannot be changed
33+ address public attestationContract;
4234
43- // Notes config and secrets locations
44- string [] public storageBackends ;
45- // Maps config hash to config data and secrets for onchain DA
46- mapping ( bytes32 => bytes ) public artifacts;
47- // Maps identity to config hash
48- mapping (bytes32 => bytes32 ) public identityConfigMap ;
35+ // Used to track registered TEEs by the Ethereum address in the quote that originally registered them
36+ mapping ( address => RegisteredTEE) public registeredTEEs ;
37+
38+ // Used to track registered TEEs by their (ethAddress, workloadId) pair,
39+ // which is needed for when
40+ mapping (WorkloadId => RegisteredTEE ) public registeredTEEsByWorkloadId ;
4941
5042 // Events
51- event InstanceDomainRegistered (string domain , address registrar );
52- event StorageBackendSet (string location , address setter );
53- event StorageBackendRemoved (string location , address remover );
54- event ArtifactAdded (bytes32 configHash , address adder );
55- event IdentityConfigSet (bytes32 identity , bytes32 configHash , address setter );
5643
44+ event TEEServiceRegistered (address ethAddress , WorkloadId workloadId , bytes rawQuote , bool alreadyExists );
45+
46+ // Errors
47+
48+ error InvalidQuote (bytes output );
5749 error ByteSizeExceeded (uint256 size );
50+ error TEEServiceAlreadyRegistered (address ethAddress , WorkloadId workloadId );
5851
5952 /**
60- * @notice Constructor to set the the governance address, which
61- * is the only address that can register and de-register TEE devices
62- * and wipe the registry in case of a compromise
63- * @param governance The address of the governance contract
53+ * Constructor to set the the Automata DCAP Attestation contract, which verifies TEE quotes
54+ * @param _attestationContract The address of the attestation contract
6455 */
65- constructor (address governance ) Owned (governance) {}
56+ constructor (address _attestationContract ) {
57+ attestationContract = _attestationContract;
58+ }
6659
6760 /**
6861 * @dev Modifier to check if input bytes size is within limits
@@ -73,114 +66,77 @@ contract AllowList is Owned {
7366 _;
7467 }
7568
76- function registerTEE (bytes memory quote ) external onlyOwner {
77- // TODO: Implement
78- }
79-
80- function deregisterTEE (bytes memory quote ) external onlyOwner {
81- // TODO: Implement
82- }
83-
84- function verifyFlashestationTransaction (bytes memory attestationTransaction ) external {
85- // TODO: Implement
86- // 1. check signature against live builder keys
87- // 2. update liveness
88- }
89-
90- function _updateLiveness () internal {
91- // TODO: Implement
92- }
93-
9469 /**
95- * @notice Verifies a quote using AutomataDcapAttestationFee
96- * @dev TODO: move this logic into registerTEE
97- * @param attestationFeeContract The address of the AutomataDcapAttestationFee contract
98- * @param quote The DCAP quote to verify
70+ * @notice Registers a TEE workload with a specific Ethereum address in the AllowList
71+ * @notice The TEE must be registered with a quote whose validity is verified by the attestationContract
72+ * @dev In order to mitigate DoS attacks, the quote must be less than 20KB
73+ * @param rawQuote The raw quote from the TEE device. Must be a V4 TDX quote
9974 */
100- function verifyQuoteWithAttestationFee (address attestationFeeContract , bytes calldata quote )
101- external
102- limitBytesSize (quote)
103- returns (bool , bytes memory )
104- {
75+ function registerTEEService (bytes calldata rawQuote ) external limitBytesSize (rawQuote) {
10576 (bool success , bytes memory output ) =
106- AutomataDcapAttestationFee (attestationFeeContract ).verifyAndAttestOnChain (quote );
77+ AutomataDcapAttestationFee (attestationContract ).verifyAndAttestOnChain (rawQuote );
10778
108- if (success) {
109- // check that quote is v4 and for TDX, otherwise in the next
110- // step the output will not have the byte length we expect and we'll fail
111- // to parse it, returning a unhelpful error message
112- checkTEEVersion (output);
113- checkTEEType (output);
79+ if (! success) {
80+ revert InvalidQuote (output);
81+ }
82+ // at this point we know this quote was generated by an update-to-date TEE, but we still
83+ // need to make sure we can parse the workloadId and ethereum public key from the quote
11484
115- // now we can safely decode the output into the TDX report body, from which we can extract
116- // the ethereum public key and compute the workloadID
117- TD10ReportBody memory td10ReportBodyStruct = parseTD10ReportBody (output);
85+ // now we can safely decode the output into the TDX report body, from which we can extract
86+ // the ethereum public key and compute the workloadID
87+ TD10ReportBody memory td10ReportBodyStruct = QuoteParser. parseTD10ReportBody (output);
11888
89+ // extract the ethereum public key from the quote
90+ address ethAddress = QuoteParser.extractEthereumAddress (td10ReportBodyStruct);
11991
120- // TODO do flashtestations protocol, so far we've only verified that the quote is valid
121- }
92+ // extract the workloadId from the quote
93+ WorkloadId workloadId = QuoteParser. extractWorkloadId (td10ReportBodyStruct);
12294
123- return (success, output);
95+ // Register the address in the allowlist with the raw quote for future quote re-verification
96+ bool previouslyRegistered = addAddress (workloadId, ethAddress, rawQuote);
97+
98+ emit TEEServiceRegistered (ethAddress, workloadId, rawQuote, previouslyRegistered);
12499 }
125100
126101 /**
127- * @notice Parses a TD10ReportBody which contains all the data we need for
128- * registering a TEE, using the serializedOutput bytes generated by Automata's verification logic
129- * @param serializedOutput The serializedOutput bytes generated by Automata's verification logic
130- * @return report The parsed TD10ReportBody
131- * @dev Taken from Automata's DCAP Attestation repo:
132- * https://github.com/automata-network/automata-dcap-attestation/blob/evm-v1.0.0/evm/contracts/verifiers/V4QuoteVerifier.sol#L309
102+ * @notice Adds a TEE to the allowlist
103+ * @dev It's possible that a TEE has already registered with this ethereum address, but with a different workloadId.
104+ * This is expected if the TEE gets restarted or upgraded and generates a new workloadId.
105+ * It's also possible that the ethereum address and workloadId are the same, but the quote
106+ * is different. This is expected if Intel releases a new set of DCAP Endorsements (i.e.
107+ * a new TCB), in which case the quotes the TEE generates will be different.
108+ * In both cases, we need to update the allowlist with the new quote.
109+ * @param workloadId The workloadId of the TEE
110+ * @param ethAddress The Ethereum address of the TEE
111+ * @param rawQuote The raw quote from the TEE device
112+ * @return previouslyRegistered Whether the TEE was previously registered
133113 */
134- function parseTD10ReportBody ( bytes memory serializedOutput )
114+ function addAddress (WorkloadId workloadId , address ethAddress , bytes calldata rawQuote )
135115 internal
136- pure
137- returns (TD10ReportBody memory report )
116+ returns (bool previouslyRegistered )
138117 {
139- bytes memory rawReportBody = output.substring (SERIALIZED_OUTPUT_OFFSET, TD_REPORT10_LENGTH);
140-
141- // note: because of the call to .substring above, we know that the length of rawReportBody is
142- // exactly TD_REPORT10_LENGTH, so we can safely call substring without checking the length
143-
144- if (success) {
145- report.teeTcbSvn = bytes16 (rawReportBody.substring (0 , 16 ));
146- report.mrSeam = rawReportBody.substring (16 , 48 );
147- report.mrsignerSeam = rawReportBody.substring (64 , 48 );
148- report.seamAttributes = bytes8 (rawReportBody.substring (112 , 8 ));
149- report.tdAttributes = bytes8 (rawReportBody.substring (120 , 8 ));
150- report.xFAM = bytes8 (rawReportBody.substring (128 , 8 ));
151- report.mrTd = rawReportBody.substring (136 , 48 );
152- report.mrConfigId = rawReportBody.substring (184 , 48 );
153- report.mrOwner = rawReportBody.substring (232 , 48 );
154- report.mrOwnerConfig = rawReportBody.substring (280 , 48 );
155- report.rtMr0 = rawReportBody.substring (328 , 48 );
156- report.rtMr1 = rawReportBody.substring (376 , 48 );
157- report.rtMr2 = rawReportBody.substring (424 , 48 );
158- report.rtMr3 = rawReportBody.substring (472 , 48 );
159- report.reportData = rawReportBody.substring (520 , 64 );
118+ // check if the TEE is already registered with the same ethereum address and workloadId,
119+ // in which case this call is a no-op and possibly being called because of user error
120+ if (
121+ WorkloadId.unwrap (registeredTEEs[ethAddress].workloadId) == WorkloadId.unwrap (workloadId)
122+ && keccak256 (registeredTEEs[ethAddress].rawQuote) == keccak256 (rawQuote)
123+ ) {
124+ revert TEEServiceAlreadyRegistered (ethAddress, workloadId);
160125 }
161- }
162126
163- /**
164- * Parses and checks that the uint16 version from the quote header is one we accept
165- * @param rawReportBody The rawQuote bytes generated by Automata's verification logic
166- * @dev Automata currently only supports V3 and V4 TDX quotes
167- */
168- function checkTEEVersion (bytes memory rawReportBody ) internal pure {
169- version = uint16 (rawReportBody.substring (0 , 2 )); // uint16 is 2 bytes
170- if (version != ACCEPTED_TDX_VERSION) {
171- revert InvalidTEEVersion (version);
127+ // this is a nice-to-have that signals to the user that the TEE was previously registered,
128+ // but it's not strictly necessary
129+ if (registeredTEEs[ethAddress].registeredAt > 0 ) {
130+ previouslyRegistered = true ;
172131 }
132+
133+ registeredTEEs[ethAddress] =
134+ RegisteredTEE ({registeredAt: uint64 (block .timestamp ), workloadId: workloadId, rawQuote: rawQuote});
173135 }
174136
175- /**
176- * Parses and checks that the bytes4 tee type from the quote header is of type TDX
177- * @param rawReportBody The rawQuote bytes generated by Automata's verification logic
178- * @dev Automata currently only supports SGX and TDX TEE types
179- */
180- function checkTEEType (bytes memory rawReportBody ) internal pure {
181- teeType = bytes4 (rawReportBody.substring (2 , 6 )); // 4 bytes
182- if (teeType != TDX_TEE) {
183- revert InvalidTEEType (teeType);
184- }
137+ function verifyFlashestationTransaction (bytes memory attestationTransaction ) external {
138+ // TODO: Implement
139+ // 1. check signature against live builder keys
140+ // 2. update liveness
185141 }
186142}
0 commit comments