diff --git a/README.md b/README.md index 7950c97..7cadd3d 100644 --- a/README.md +++ b/README.md @@ -8,51 +8,65 @@ You can find a [specification for the protocol here](https://github.com/flashbot ## System Components -1. TEE Devices: identified uniquely by their WorkloadId, see [QuoteParser's `extractWorkloadId`](src/utils/QuoteParser.sol) -1. TEE-controlled Public Keys: these are used to identify and verify TEEs and their outputs -1. TEE Attestations: also called Quotes, which are managed by the [FlashtestationRegistry.sol](src/FlashtestationRegistry.sol) -1. Automata DCAP Protocol: Flashtestations uses [Automata's protocol](https://github.com/automata-network/automata-dcap-attestation) to perform onchain verification of TDX attestations -1. Policies: specifically [BlockBuilderPolicy.sol](src/BlockBuilderPolicy.sol), which store Governance-approved WorkloadIds that are associated with vetted versions of Flashbot's TEE-builder software, [op-rbuilder](https://github.com/flashbots/rbuilder/blob/08f6ece0f270c15653a0f19ca9cbd86d332ea78c/crates/op-rbuilder/README.md?plain=1) -1. Block Signature Transaction: see [BlockBuilderPolicy's `verifyBlockBuilderProof`](src/utils/QuoteParser.sol) -1. Governance Values: The permissioned entities that are the only ones able to add WorkloadIds to Policies +1. **TEE Devices**: Identified by their measurement registers which policies use to compute WorkloadIds +1. **TEE-controlled Public Keys**: Used to identify and verify TEEs and their outputs +1. **TEE Attestations**: Also called Quotes, managed by the [FlashtestationRegistry.sol](src/FlashtestationRegistry.sol) +1. **Extended Registration Data**: Application-specific data that is attested to alongside the TEE address +1. **Automata DCAP**: Flashtestations uses [Automata's DCAP library](https://github.com/automata-network/automata-dcap-attestation) for onchain TDX attestation verification +1. **Policies**: Specifically [BlockBuilderPolicy.sol](src/BlockBuilderPolicy.sol), which: + - Compute WorkloadIds from TEE measurement registers + - Store Governance-approved WorkloadIds + - Associate WorkloadIds with vetted versions of TEE software +1. **Block Signature Transaction**: See [BlockBuilderPolicy's `verifyBlockBuilderProof`](src/BlockBuilderPolicy.sol) +1. **Governance Values**: Permissioned entities that can add WorkloadIds to Policies ## System Flows -1. Registering a TEE Device (also referred to as a block builder) +1. **Registering a TEE Device** a. Should only be callable from a TEE-controlled address b. Verify TEE Quote - c. extract and store TEE address and workload info + c. Extract and store: + - TEE address (from reportData[0:20]) + - Extended registration data for the application to use (keccak of the extended data must match reportData[20:52]) + - Full parsed report body for cheap access to TD report data fields + - Raw quote for future invalidation -1. Verify Flashtestation Transaction +1. **Verify Flashtestation Transaction** - a. Check signature of transactions against registry of live builder keys + a. Policy contract checks signature against registry of registered TEEs - b. emit an event indicating the block was built by a particular TEE device (identified by its WorkloadId) + b. Policy computes WorkloadId from the stored report body -1. Invalidating a TEE Device + c. Checks if computed WorkloadId is in the approved list - a. Mark TEE device as invalid, which should be done if it's underlying DCAP collateral values have been invalidated + d. Emit an event indicating the block was built by a particular TEE device -1. Adding a WorkloadId to a Policy +1. **Invalidating a TEE Device** - a. Can only be done by the owner of the Policy + a. Mark TEE device as invalid when underlying DCAP collateral values are invalidated - b. Only registered TEE's can have their WorkloadIds added to a Policy + b. Affects all policies using that TEE - c. Once its WorkloadId has been added to a Policy, the TEE will be able to prove it built a block using the "Verify Flashtestation Transaction" flow above +1. **Adding a WorkloadId to a Policy** -1. Removing a WorkloadId from a Policy + a. Can only be done by the policy owner - a. This is done when the block builder software running on this TEE is no longer correct (either because of newer versions replacing it, or bugs that have been found) + b. WorkloadId is computed externally from TEE's report body - b. Can only be done by the owner of the Policy + c. Once added, TEEs with matching WorkloadId can prove they built blocks via "Verify Flashtestation Transaction" - c. The WorkloadId must already exist on the Policy +1. **Removing a WorkloadId from a Policy** - d. Once its WorkloadId has been removed from a Policy, it will no longer be able to prove it built a block. + a. Done when TEE software is outdated or has bugs + + b. Can only be done by the policy owner + + c. WorkloadId must already exist in the Policy + + d. Removed WorkloadIds can no longer prove block building ## Deploy @@ -154,7 +168,8 @@ Before executing this script, provide correct values for the following env vars: # this is the contract BlockBuilderPolicy you deployed up above ADDRESS_BLOCK_BUILDER_POLICY=0x0000000000000000000000000000000000000042 -# this is the workload ID emitted in the event from the RegisterTEEScript up above +# this is the workload ID computed from the TEE's measurement registers +# You can compute this from a registered TEE's report body using BlockBuilderPolicy.workloadIdForTDRegistration WORKLOAD_ID=0xeee********************************************************9164e ``` diff --git a/foundry.toml b/foundry.toml index 683472e..20c1903 100644 --- a/foundry.toml +++ b/foundry.toml @@ -8,7 +8,7 @@ ffi = true ast = true build_info = true extra_output = ["storageLayout"] -fs_permissions = [{ access = "read", path = "foundry-out/" }, {access = "read", path = "test/raw_tdx_quotes/"}, {access = "read", path = "script/raw_tdx_quotes/"}] +fs_permissions = [{ access = "read", path = "foundry-out/" }, {access = "read", path = "test/raw_tdx_quotes/"}, {access = "readwrite", path = "script/raw_tdx_quotes/"}] evm_version = "prague" gas_limit = "3000000000" fuzz_runs = 10_000 @@ -31,4 +31,4 @@ gas_limit=30_000_000 sepolia = { key = "${ETHERSCAN_API_KEY}" } [rpc_endpoints] -sepolia = "${UNICHAIN_SEPOLIA_RPC_URL}" \ No newline at end of file +sepolia = "${UNICHAIN_SEPOLIA_RPC_URL}" diff --git a/script/Interactions.s.sol b/script/Interactions.s.sol index 7b6e0d9..d9c914e 100644 --- a/script/Interactions.s.sol +++ b/script/Interactions.s.sol @@ -2,9 +2,8 @@ pragma solidity 0.8.28; import {Script, console} from "forge-std/Script.sol"; -import {BlockBuilderPolicy} from "../src/BlockBuilderPolicy.sol"; +import {BlockBuilderPolicy, WorkloadId} from "../src/BlockBuilderPolicy.sol"; import {FlashtestationRegistry} from "../src/FlashtestationRegistry.sol"; -import {WorkloadId} from "../src/utils/QuoteParser.sol"; import {DeploymentUtils} from "./utils/DeploymentUtils.sol"; /// @title AddWorkloadToPolicyScript @@ -64,17 +63,18 @@ contract RegisterTEEScript is Script, DeploymentUtils { console.logAddress(registryAddress); FlashtestationRegistry registry = FlashtestationRegistry(registryAddress); - registry.registerTEEService(vm.readFileBinary(pathToAttestationQuote)); + registry.registerTEEService(vm.readFileBinary(pathToAttestationQuote), bytes("") /* currently not used */ ); // fetch the TEE-related data we just added, so the caller of this script can use // the outputs in future scripts (like Interactions.s.sol:AddWorkloadToPolicyScript) address sender = vm.getWallets()[0]; - (WorkloadId workloadId,,, bytes memory publicKey) = registry.registeredTEEs(sender); + (, FlashtestationRegistry.RegisteredTEE memory teeRegistration) = registry.getRegistration(sender); + + BlockBuilderPolicy policy = BlockBuilderPolicy(vm.envAddress("ADDRESS_BLOCK_BUILDER_POLICY")); + WorkloadId workloadId = policy.workloadIdForTDRegistration(teeRegistration); console.log("workloadId:"); console.logBytes32(WorkloadId.unwrap(workloadId)); - console.log("publicKey:"); - console.logBytes(publicKey); vm.stopBroadcast(); } diff --git a/script/AddMockQuote.s.sol b/script/MockQuotes.s.sol similarity index 56% rename from script/AddMockQuote.s.sol rename to script/MockQuotes.s.sol index 30ba9a5..08bb104 100644 --- a/script/AddMockQuote.s.sol +++ b/script/MockQuotes.s.sol @@ -3,12 +3,79 @@ pragma solidity 0.8.28; import {Script, console} from "forge-std/Script.sol"; import {FlashtestationRegistry} from "../src/FlashtestationRegistry.sol"; +import {IAttestation} from "../src/interfaces/IAttestation.sol"; import {MockAutomataDcapAttestationFee} from "../test/mocks/MockAutomataDcapAttestationFee.sol"; import {AutomataDcapAttestationFee} from "automata-dcap-attestation/contracts/AutomataDcapAttestationFee.sol"; -import {QuoteParser, WorkloadId} from "../src/utils/QuoteParser.sol"; +import {QuoteParser} from "../src/utils/QuoteParser.sol"; import {TD10ReportBody} from "automata-dcap-attestation/contracts/types/V4Structs.sol"; import {DeploymentUtils} from "./utils/DeploymentUtils.sol"; +contract MakeReportData is Script, DeploymentUtils { + function setUp() public {} + + function run() public view { + address teeAddress = vm.envAddress("TEE_ADDRESS"); + console.logBytes(abi.encodePacked(teeAddress, keccak256(""))); + } +} + +// Fetches quote from a remote provider (dummy dcap), and saves it to script/raw_tdx_quotes/
/quote.bin +contract FetchRemoteQuote is Script, DeploymentUtils { + function setUp() public {} + + function run() public { + address teeAddress = vm.envAddress("TEE_ADDRESS"); + string memory pathToQuote = string.concat("script/raw_tdx_quotes/", vm.toString(teeAddress), "/quote.bin"); + vm.createDir(string.concat("script/raw_tdx_quotes/", vm.toString(teeAddress)), true); + + string memory teeAddressStr = vm.toString(teeAddress); + string memory extDataHashStr = vm.toString(keccak256("")); + string memory reportData = string.concat(substring(teeAddressStr, 2, 40), substring(extDataHashStr, 2, 64)); + + string[] memory inputs = new string[](5); + inputs[0] = "curl"; + inputs[1] = "--silent"; + inputs[2] = "--output"; + inputs[3] = pathToQuote; + inputs[4] = string.concat("http://ns31695324.ip-141-94-163.eu:10080/attest/", reportData); + + vm.ffi(inputs); + console.log("saved response to ", pathToQuote); + } +} + +// Verifies quote from script/raw_tdx_quotes/
/quote.bin and saves the verification output in the same directory as output.bin +contract VerifyQuoteOnchain is Script, DeploymentUtils { + function setUp() public {} + + function run() public { + address teeAddress = vm.envAddress("TEE_ADDRESS"); + string memory pathToQuote = string.concat("script/raw_tdx_quotes/", vm.toString(teeAddress), "/quote.bin"); + bytes memory quote = vm.readFileBinary(pathToQuote); + + address attestationAddress = vm.envAddress("DCAP_ATTESTATION_ADDRESS"); + IAttestation attestationContract = IAttestation(attestationAddress); + + etchECDSAPrecompile(); // this is required to get the output from the real attestation contract + (bool success, bytes memory output) = attestationContract.verifyAndAttestOnChain(quote); + console.log("Success: ", success); + console.log("Output (hex): ", vm.toString(output)); + + string memory pathToOutput = string.concat("script/raw_tdx_quotes/", vm.toString(teeAddress), "/output.bin"); + vm.writeFileBinary(pathToOutput, output); + console.log("Saved verification binary output to ", pathToOutput); + } +} + +function substring(string memory str, uint256 startIndex, uint256 len) pure returns (string memory) { + bytes memory strBytes = bytes(str); + bytes memory result = new bytes(len); + for (uint256 i = startIndex; i < startIndex + len; i++) { + result[i - startIndex] = strBytes[i]; + } + return string(result); +} + /** * @title AddMockQuote * @dev Script to add a mock quote to the MockAutomataDcapAttestationFee contract deployed on unichain experimental @@ -77,15 +144,11 @@ contract AddMockQuoteScript is Script, DeploymentUtils { console.log("Successfully added mock quote to MockAutomataDcapAttestationFee"); TD10ReportBody memory td10ReportBodyStruct = QuoteParser.parseV4VerifierOutput(output); - bytes memory publicKey = QuoteParser.extractPublicKey(td10ReportBodyStruct); - address teeAddress = address(uint160(uint256(keccak256(publicKey)))); - WorkloadId workloadId = QuoteParser.extractWorkloadId(td10ReportBodyStruct); + (address teeAddress, bytes32 extDataHash) = QuoteParser.parseReportData(td10ReportBodyStruct.reportData); console.log("TEE address:"); console.logAddress(teeAddress); - console.log("Workload ID (hex):"); - console.logBytes32(WorkloadId.unwrap(workloadId)); - console.log("Public key (hex):"); - console.logBytes(publicKey); + console.log("TEE extended report data hash:"); + console.logBytes32(extDataHash); } } diff --git a/test/raw_tdx_quotes/7b916d70ed77488d6c1ced7117ba410655a8faa8d6c7740562a88ab3cb9cbca63e2d5761812a11d90c009ed017113131370070cd3a2d5fba64d9dbb76952df19/output.bin b/script/raw_tdx_quotes/0x12c14e56d585Dcf3B36f37476c00E78bA9363742/output.bin similarity index 82% rename from test/raw_tdx_quotes/7b916d70ed77488d6c1ced7117ba410655a8faa8d6c7740562a88ab3cb9cbca63e2d5761812a11d90c009ed017113131370070cd3a2d5fba64d9dbb76952df19/output.bin rename to script/raw_tdx_quotes/0x12c14e56d585Dcf3B36f37476c00E78bA9363742/output.bin index 9d95cb5..7106f09 100644 Binary files a/test/raw_tdx_quotes/7b916d70ed77488d6c1ced7117ba410655a8faa8d6c7740562a88ab3cb9cbca63e2d5761812a11d90c009ed017113131370070cd3a2d5fba64d9dbb76952df19/output.bin and b/script/raw_tdx_quotes/0x12c14e56d585Dcf3B36f37476c00E78bA9363742/output.bin differ diff --git a/test/raw_tdx_quotes/0xd204547069c53f9ecff9b30494eb9797615a2f46aa2785db6258104cebb92d48ff4dc0744c36d8470646f4813e61f9a831ffb54b937f7b233f32d271434ccca6/quote.bin b/script/raw_tdx_quotes/0x12c14e56d585Dcf3B36f37476c00E78bA9363742/quote.bin similarity index 93% rename from test/raw_tdx_quotes/0xd204547069c53f9ecff9b30494eb9797615a2f46aa2785db6258104cebb92d48ff4dc0744c36d8470646f4813e61f9a831ffb54b937f7b233f32d271434ccca6/quote.bin rename to script/raw_tdx_quotes/0x12c14e56d585Dcf3B36f37476c00E78bA9363742/quote.bin index 51fd6bd..9d68841 100644 Binary files a/test/raw_tdx_quotes/0xd204547069c53f9ecff9b30494eb9797615a2f46aa2785db6258104cebb92d48ff4dc0744c36d8470646f4813e61f9a831ffb54b937f7b233f32d271434ccca6/quote.bin and b/script/raw_tdx_quotes/0x12c14e56d585Dcf3B36f37476c00E78bA9363742/quote.bin differ diff --git a/src/BlockBuilderPolicy.sol b/src/BlockBuilderPolicy.sol index 4978d30..e9d013d 100644 --- a/src/BlockBuilderPolicy.sol +++ b/src/BlockBuilderPolicy.sol @@ -7,9 +7,15 @@ import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {WorkloadId} from "./utils/QuoteParser.sol"; import {FlashtestationRegistry} from "./FlashtestationRegistry.sol"; -import {TD10ReportBody} from "automata-dcap-attestation/contracts/types/V4Structs.sol"; + +// WorkloadID uniquely identifies a TEE workload. A workload is roughly equivalent to a version of an application's +// code, can be reproduced from source code, and is derived from a combination of the TEE's measurement registers. +// The TDX platform provides several registers that capture cryptographic hashes of code, data, and configuration +// loaded into the TEE's environment. This means that whenever a TEE device changes anything about its compute stack +// (e.g. user code, firmware, OS, etc), the workloadID will change. +// See the [Flashtestation's specification](https://github.com/flashbots/rollup-boost/blob/main/specs/flashtestations.md#workload-identity-derivation) for more details +type WorkloadId is bytes32; /** * @title BlockBuilderPolicy @@ -30,6 +36,16 @@ contract BlockBuilderPolicy is Initializable, UUPSUpgradeable, OwnableUpgradeabl bytes32 public constant VERIFY_BLOCK_BUILDER_PROOF_TYPEHASH = keccak256("VerifyBlockBuilderProof(uint8 version,bytes32 blockContentHash,uint256 nonce)"); + // TDX workload constants + // See section 11.5.3 in TDX Module v1.5 Base Architecture Specification https://www.intel.com/content/www/us/en/content-details/733575/intel-tdx-module-v1-5-base-architecture-specification.html + bytes8 constant TD_XFAM_FPU = 0x0000000000000001; // Enabled FPU (always enabled) + bytes8 constant TD_XFAM_SSE = 0x0000000000000002; // Enabled SSE (always enabled) + + // See section 3.4.1 in TDX Module ABI specification https://cdrdv2.intel.com/v1/dl/getContent/733579 + bytes8 constant TD_TDATTRS_VE_DISABLED = 0x0000000010000000; // Allows disabling of EPT violation conversion to #VE on access of PENDING pages. Needed for Linux. + bytes8 constant TD_TDATTRS_PKS = 0x0000000040000000; // Enabled Supervisor Protection Keys (PKS) + bytes8 constant TD_TDATTRS_KL = 0x0000000080000000; // Enabled Key Locker (KL) + // The set of workloadIds that are allowed under this policy // This is only updateable by governance (i.e. the owner) of the Policy contract. // Adding, and removing a workload is O(1). @@ -174,35 +190,51 @@ contract BlockBuilderPolicy is Initializable, UUPSUpgradeable, OwnableUpgradeabl /// @return allowed True if the TEE is valid for any workload in the policy /// @return workloadId The workloadId of the TEE that is valid for the policy, or 0 if the TEE is not valid for any workload in the policy function isAllowedPolicy(address teeAddress) public view returns (bool allowed, WorkloadId) { + (bool isValid, FlashtestationRegistry.RegisteredTEE memory registration) = + FlashtestationRegistry(registry).getRegistration(teeAddress); + if (!isValid) { + return (false, WorkloadId.wrap(0)); + } + + WorkloadId workloadId = workloadIdForTDRegistration(registration); for (uint256 i = 0; i < workloadIds.length(); ++i) { - WorkloadId workloadId = WorkloadId.wrap(workloadIds.at(i)); - if (FlashtestationRegistry(registry).isValidWorkload(workloadId, teeAddress)) { + if (WorkloadId.unwrap(workloadId) == workloadIds.at(i)) { return (true, workloadId); } } + return (false, WorkloadId.wrap(0)); } - /// @notice An alternative implementation of isAllowedPolicy that verifies more than just - /// the workloadId's matching and if the attestation is still valid - /// @param teeAddress The TEE-controlled address - /// @param expectedTeeTcbSvn The expected teeTcbSvn of the TEE's attestation - /// @return allowed True if the TEE's attestation is part of the policy, is still valid, and - /// the teeTcbSvn matches the expected value - /// @dev This exists to show how different Policies can be implemented, based on what - /// properties of the TEE's attestation are important to verify. - function isAllowedPolicy2(address teeAddress, bytes16 expectedTeeTcbSvn) external view returns (bool allowed) { - for (uint256 i = 0; i < workloadIds.length(); ++i) { - WorkloadId workloadId = WorkloadId.wrap(workloadIds.at(i)); - TD10ReportBody memory reportBody = FlashtestationRegistry(registry).getReportBody(teeAddress); - if ( - FlashtestationRegistry(registry).isValidWorkload(workloadId, teeAddress) - && reportBody.teeTcbSvn == expectedTeeTcbSvn - ) { - return true; - } - } - return false; + // Application specific mapping of registration data to a workload identifier + // Think of the workload identifier as the version of the application for governance + // The workload id verifiably maps to a version of source code for the VM image + function workloadIdForTDRegistration(FlashtestationRegistry.RegisteredTEE memory registration) + public + pure + returns (WorkloadId) + { + // We expect FPU and SSE xfam bits to be set, and anything else should be handled by explicitly allowing the workloadid + bytes8 expectedXfamBits = TD_XFAM_FPU | TD_XFAM_SSE; + + // We don't mind VE_DISABLED, PKS, and KL tdattributes bits being set either way, anything else requires explicitly allowing the workloadid + bytes8 ignoredTdAttributesBitmask = TD_TDATTRS_VE_DISABLED | TD_TDATTRS_PKS | TD_TDATTRS_KL; + + return WorkloadId.wrap( + keccak256( + bytes.concat( + registration.parsedReportBody.mrTd, + registration.parsedReportBody.rtMr0, + registration.parsedReportBody.rtMr1, + registration.parsedReportBody.rtMr2, + registration.parsedReportBody.rtMr3, + // VMM configuration + registration.parsedReportBody.mrConfigId, + registration.parsedReportBody.xFAM ^ expectedXfamBits, + registration.parsedReportBody.tdAttributes & ~ignoredTdAttributesBitmask + ) + ) + ); } /// @notice Add a workload to a policy (governance only) diff --git a/src/FlashtestationRegistry.sol b/src/FlashtestationRegistry.sol index 2095c26..f8c2505 100644 --- a/src/FlashtestationRegistry.sol +++ b/src/FlashtestationRegistry.sol @@ -9,9 +9,8 @@ import {ReentrancyGuardTransient} from "@openzeppelin/contracts/utils/Reentrancy import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol"; import {IAttestation} from "./interfaces/IAttestation.sol"; import {IFlashtestationRegistry} from "./interfaces/IFlashtestationRegistry.sol"; -import {QuoteParser, WorkloadId} from "./utils/QuoteParser.sol"; +import {QuoteParser} from "./utils/QuoteParser.sol"; import {TD10ReportBody} from "automata-dcap-attestation/contracts/types/V4Structs.sol"; -import {TD_REPORT10_LENGTH, HEADER_LENGTH} from "automata-dcap-attestation/contracts/types/Constants.sol"; /** * @title FlashtestationRegistry @@ -30,11 +29,15 @@ contract FlashtestationRegistry is // Constants + // Minimum length of the td reportdata field: tee address (20) and hash of extended data (32) + uint256 public constant TD_REPORTDATA_LENGTH = 52; + // Maximum size for byte arrays to prevent DoS attacks uint256 public constant MAX_BYTES_SIZE = 20 * 1024; // 20KB limit // EIP-712 Constants - bytes32 public constant REGISTER_TYPEHASH = keccak256("RegisterTEEService(bytes rawQuote,uint256 nonce)"); + bytes32 public constant REGISTER_TYPEHASH = + keccak256("RegisterTEEService(bytes rawQuote,bytes extendedRegistrationData,uint256 nonce)"); // Storage Variables @@ -42,8 +45,8 @@ contract FlashtestationRegistry is // This is deployed by Automata, and once set on the FlashtestationRegistry, it cannot be changed IAttestation public attestationContract; - // Tracks the TEE-controlled address that registered a particular WorkloadId and attestation quote. - // This enables efficient O(1) lookup in `isValidWorkload`, so that apps can quickly verify the + // Tracks the TEE-controlled address that registered a particular attestation quote and app data. + // This enables efficient O(1) lookup in `getRegistration`, so that apps can quickly verify the // output of a TEE workload mapping(address => RegisteredTEE) public registeredTEEs; @@ -80,37 +83,13 @@ contract FlashtestationRegistry is * @dev In order to mitigate DoS attacks, the quote must be less than 20KB * @dev This is a costly operation (5 million gas) and should be used sparingly. * @param rawQuote The raw quote from the TEE device. Must be a V4 TDX quote + * @param extendedRegistrationData Abi-encoded application specific attested data, reserved for future upgrades */ - function registerTEEService(bytes calldata rawQuote) external limitBytesSize(rawQuote) nonReentrant { - (bool success, bytes memory output) = attestationContract.verifyAndAttestOnChain(rawQuote); - - if (!success) { - revert InvalidQuote(output); - } - - // now we know the quote is valid, we can safely parse the output into the TDX report body, - // from which we'll extract the data we need to register the TEE - TD10ReportBody memory td10ReportBodyStruct = QuoteParser.parseV4VerifierOutput(output); - bytes memory publicKey = QuoteParser.extractPublicKey(td10ReportBodyStruct); - address teeAddress = address(uint160(uint256(keccak256(publicKey)))); - WorkloadId workloadId = QuoteParser.extractWorkloadId(td10ReportBodyStruct); - - // we must ensure the TEE-controlled address is the same as the one calling the function - // otherwise we have no proof that the TEE that generated this quote intends to register - // with the FlashtestationRegistry. This protects against a malicious TEE that generates a quote for a - // different address, and then calls this function to register itself with the FlashtestationRegistry - if (teeAddress != msg.sender) { - revert SenderMustMatchTEEAddress(msg.sender, teeAddress); - } - - bool previouslyRegistered = checkIfPreviouslyRegistered(workloadId, teeAddress, rawQuote); - - // Register the address in the registry with the raw quote so later on if the TEE has its - // underlying DCAP endorsements updated, we can invalidate the TEE's attestation - registeredTEEs[teeAddress] = - RegisteredTEE({workloadId: workloadId, rawQuote: rawQuote, isValid: true, publicKey: publicKey}); - - emit TEEServiceRegistered(teeAddress, workloadId, rawQuote, publicKey, previouslyRegistered); + function registerTEEService(bytes calldata rawQuote, bytes calldata extendedRegistrationData) + external + nonReentrant + { + doRegister(msg.sender, rawQuote, extendedRegistrationData); } /** @@ -119,105 +98,121 @@ contract FlashtestationRegistry is * @dev In order to mitigate DoS attacks, the quote must be less than 20KB * @dev This function exists so that the TEE does not need to be funded with gas for transaction fees, and * instead can rely on any EOA to execute the transaction, but still only allow quotes from attested TEEs + * @dev Replay is implicitly shielded against through the transaction's nonce (TEE must sign the new nonce) * @param rawQuote The raw quote from the TEE device. Must be a V4 TDX quote + * @param extendedRegistrationData Abi-encoded application specific attested data, reserved for future upgrades * @param nonce The nonce to use for the EIP-712 signature (to prevent replay attacks) * @param signature The EIP-712 signature of the registration message */ - function permitRegisterTEEService(bytes calldata rawQuote, uint256 nonce, bytes calldata signature) - external + function permitRegisterTEEService( + bytes calldata rawQuote, + bytes calldata extendedRegistrationData, + uint256 nonce, + bytes calldata signature + ) external nonReentrant { + // Create the digest using EIP712Upgradeable's _hashTypedDataV4 + bytes32 digest = hashTypedDataV4(computeStructHash(rawQuote, extendedRegistrationData, nonce)); + + // Recover the signer, and ensure it matches the TEE-controlled address, otherwise we have no proof + // whoever created the attestation quote has access to the private key + address signer = digest.recover(signature); + + // Verify the nonce + uint256 expectedNonce = nonces[signer]; + require(nonce == expectedNonce, InvalidNonce(expectedNonce, nonce)); + + // Increment the nonce so that any attempts at replaying this transaction will fail + nonces[signer]++; + + doRegister(signer, rawQuote, extendedRegistrationData); + } + + /** + * @notice Registers a TEE workload with a specific TEE-controlled address in the FlashtestationRegistry + * @notice The TEE must be registered with a quote whose validity is verified by the attestationContract + * @dev In order to mitigate DoS attacks, the quote must be less than 20KB + * @dev This is a costly operation (5 million gas) and should be used sparingly. + * @param caller The address from which registration request originates, must match the one in the quote + * @param rawQuote The raw quote from the TEE device. Must be a V4 TDX quote + * @param extendedRegistrationData Abi-encoded application specific attested data, reserved for future upgrades + */ + function doRegister(address caller, bytes calldata rawQuote, bytes calldata extendedRegistrationData) + internal limitBytesSize(rawQuote) - nonReentrant + limitBytesSize(extendedRegistrationData) { - // Verify the quote with the attestation contract (bool success, bytes memory output) = attestationContract.verifyAndAttestOnChain(rawQuote); - if (!success) { revert InvalidQuote(output); } // now we know the quote is valid, we can safely parse the output into the TDX report body, // from which we'll extract the data we need to register the TEE - TD10ReportBody memory td10ReportBodyStruct = QuoteParser.parseV4VerifierOutput(output); - bytes memory publicKey = QuoteParser.extractPublicKey(td10ReportBodyStruct); - address teeAddress = address(uint160(uint256(keccak256(publicKey)))); - WorkloadId workloadId = QuoteParser.extractWorkloadId(td10ReportBodyStruct); - - // we must ensure the TEE-controlled address is the same as the one who signed the EIP-712 signature - // otherwise we have no proof that the TEE that generated this quote intends to register - // with the FlashtestationRegistry. This protects against a malicious TEE that generates a quote for a - // different address, and then calls this function to register itself with the FlashtestationRegistry - - // Verify the nonce - uint256 expectedNonce = nonces[teeAddress]; - require(nonce == expectedNonce, InvalidNonce(expectedNonce, nonce)); - - // Increment the nonce so that any attempts at replaying this transaction will fail - nonces[teeAddress]++; + TD10ReportBody memory td10ReportBody = QuoteParser.parseV4VerifierOutput(output); - // Create the digest using EIP712Upgradeable's _hashTypedDataV4 - bytes32 digest = _hashTypedDataV4(computeStructHash(rawQuote, nonce)); + // Binding the tee address and extended report data to the quote + if (td10ReportBody.reportData.length < TD_REPORTDATA_LENGTH) { + revert InvalidReportDataLength(td10ReportBody.reportData.length); + } - // Recover the signer, and ensure it matches the TEE-controlled address, otherwise we have no proof - // that the TEE that generated this quote intends to register with the FlashtestationRegistry - address signer = digest.recover(signature); - if (signer != teeAddress) { - revert InvalidSignature(); + (address teeAddress, bytes32 extendedDataReportHash) = QuoteParser.parseReportData(td10ReportBody.reportData); + if (caller != teeAddress) { + revert SenderMustMatchTEEAddress(caller, teeAddress); + } + bytes32 extendedRegistrationDataHash = keccak256(extendedRegistrationData); + if (extendedRegistrationDataHash != extendedDataReportHash) { + revert InvalidRegistrationDataHash(extendedDataReportHash, extendedRegistrationDataHash); } - bool previouslyRegistered = checkIfPreviouslyRegistered(workloadId, teeAddress, rawQuote); + bytes32 quoteHash = keccak256(rawQuote); + bool previouslyRegistered = checkPreviousRegistration(teeAddress, quoteHash); // Register the address in the registry with the raw quote so later on if the TEE has its // underlying DCAP endorsements updated, we can invalidate the TEE's attestation - registeredTEEs[teeAddress] = - RegisteredTEE({workloadId: workloadId, rawQuote: rawQuote, isValid: true, publicKey: publicKey}); - - emit TEEServiceRegistered(teeAddress, workloadId, rawQuote, publicKey, previouslyRegistered); + registeredTEEs[teeAddress] = RegisteredTEE({ + rawQuote: rawQuote, + parsedReportBody: td10ReportBody, + extendedRegistrationData: extendedRegistrationData, + isValid: true + }); + + emit TEEServiceRegistered(teeAddress, rawQuote, previouslyRegistered); } /** - * @notice Checks if a TEE is already registered with the same workloadId and quote - * @dev If a user is trying to add the same address, workloadId, and quote, this is a no-op + * @notice Checks if a TEE is already registered with the same quote + * @dev If a user is trying to add the same address, and quote, this is a no-op * and we should revert to signal that the user may be making a mistake (why would * they be trying to add the same TEE twice?). - * @dev If the TEE is already registered and we're using a different quote or a different - * workloadId, that is fine and indicates the TEE-controlled address is either re-attesting - * (with a new quote) or has moved its private key to a new TEE device (with a new workloadId) + * @dev If the TEE is already registered and we're using a different quote, + * that is fine and indicates the TEE-controlled address is either re-attesting + * (with a new quote) or has moved its private key to a new TEE device * @dev We do not need to check the public key, because the address has a cryptographically-ensured * 1-to-1 relationship with the public key, so checking it would be redundant - * @param workloadId The workloadId of the TEE * @param teeAddress The TEE-controlled address of the TEE - * @param rawQuote The raw quote from the TEE device + * @param quoteHash The hash of registration's raw quote * @return Whether the TEE is already registered but is updating its quote */ - function checkIfPreviouslyRegistered(WorkloadId workloadId, address teeAddress, bytes calldata rawQuote) - internal - view - returns (bool) - { - if ( - WorkloadId.unwrap(registeredTEEs[teeAddress].workloadId) == WorkloadId.unwrap(workloadId) - && keccak256(registeredTEEs[teeAddress].rawQuote) == keccak256(rawQuote) - ) { - revert TEEServiceAlreadyRegistered(teeAddress, workloadId); + function checkPreviousRegistration(address teeAddress, bytes32 quoteHash) internal view returns (bool) { + if (keccak256(registeredTEEs[teeAddress].rawQuote) == quoteHash) { + revert TEEServiceAlreadyRegistered(teeAddress); } // if the TEE is already registered, but we're using a different quote, // return true to signal that the TEE is already registered but is updating its quote - return WorkloadId.unwrap(registeredTEEs[teeAddress].workloadId) != 0; + return registeredTEEs[teeAddress].rawQuote.length > 0; } /** - * @notice Checks if a TEE is registered with a given workloadId - * @param workloadId The workloadId to check + * @notice Fetches TEE registration for a given address * @param teeAddress The TEE-controlled address to check - * @return Whether the TEE is registered with the given workloadId and has not been invalidated - * @dev isValidWorkload will only return true if a valid TEE quote containing + * @return Raw quote, and whether the TEE quote, td attributes, or xfam have not been invalidated + * @dev getRegistration will only return true if a valid TEE quote containing * teeAddress in its reportData field was previously registered with the FlashtestationRegistry * using the registerTEEService function. */ - function isValidWorkload(WorkloadId workloadId, address teeAddress) public view returns (bool) { - return registeredTEEs[teeAddress].isValid - && WorkloadId.unwrap(registeredTEEs[teeAddress].workloadId) == WorkloadId.unwrap(workloadId); + function getRegistration(address teeAddress) public view returns (bool, RegisteredTEE memory) { + return (registeredTEEs[teeAddress].isValid, registeredTEEs[teeAddress]); } /** @@ -231,19 +226,22 @@ contract FlashtestationRegistry is * TDX vulnerability was discovered), which invalidates all prior quotes generated by that TEE. * By invalidates we mean that the outputs generated by the TEE-controlled address associated * with these invalid quotes are no longer secure and cannot be relied upon. This fact needs to be - * reflected onchain, so that any upstream contracts that try to call `isValidWorkload` will + * reflected onchain, so that any upstream contracts that try to call `getRegistration` will * correctly return `false` for the TEE-controlled addresses associated with these invalid quotes. * This is a security requirement to ensure that no downstream contracts can be exploited by * a malicious TEE that has been compromised * @dev Note: this function is callable by anyone, so that offchain monitoring services can * quickly mark TEEs as invalid + * @dev Note: rather than relying on invalidation of specific quotes, we can cover all the cases + * in which a quote can be invalidated (tcbrecovery, certificate revocation etc). This would allow + * much cheaper, bulk invalidation of all quotes using a now-outdated tcbinfo for example. */ function invalidateAttestation(address teeAddress) external { // check to make sure it even makes sense to invalidate the TEE-controlled address // if the TEE-controlled address is not registered with the FlashtestationRegistry, // it doesn't make sense to invalidate the attestation RegisteredTEE memory registeredTEE = registeredTEEs[teeAddress]; - if (WorkloadId.unwrap(registeredTEE.workloadId) == 0) { + if (registeredTEE.rawQuote.length == 0) { revert TEEServiceNotRegistered(teeAddress); } @@ -267,19 +265,6 @@ contract FlashtestationRegistry is } } - /** - * @notice Returns the TD10ReportBody for the quote used to register a given TEE-controlled address - * @param teeAddress The TEE-controlled address to get the TD10ReportBody for - * @return reportBody The TD10ReportBody for the given TEE-controlled address - * @dev this is useful for when both onchain and offchain users want more - * information about the registered TEE than just the workloadId - */ - function getReportBody(address teeAddress) public view returns (TD10ReportBody memory) { - bytes memory rawQuote = registeredTEEs[teeAddress].rawQuote; - require(rawQuote.length > 0, TEEServiceNotRegistered(teeAddress)); - return QuoteParser.parseV4Quote(rawQuote); - } - /** * @notice Computes the digest for the EIP-712 signature * @dev This is useful for when both onchain and offchain users want to compute the digest @@ -296,10 +281,15 @@ contract FlashtestationRegistry is * @dev This is useful for when both onchain and offchain users want to compute the struct hash * for the EIP-712 signature, and then use it to verify the signature * @param rawQuote The raw quote from the TEE device + * @param extendedRegistrationData Abi-encoded attested data, application specific * @param nonce The nonce to use for the EIP-712 signature * @return The struct hash for the EIP-712 signature */ - function computeStructHash(bytes calldata rawQuote, uint256 nonce) public pure returns (bytes32) { - return keccak256(abi.encode(REGISTER_TYPEHASH, keccak256(rawQuote), nonce)); + function computeStructHash(bytes calldata rawQuote, bytes calldata extendedRegistrationData, uint256 nonce) + public + pure + returns (bytes32) + { + return keccak256(abi.encode(REGISTER_TYPEHASH, keccak256(rawQuote), keccak256(extendedRegistrationData), nonce)); } } diff --git a/src/interfaces/IFlashtestationRegistry.sol b/src/interfaces/IFlashtestationRegistry.sol index 9bc5abb..4bab33c 100644 --- a/src/interfaces/IFlashtestationRegistry.sol +++ b/src/interfaces/IFlashtestationRegistry.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.28; -import {WorkloadId} from "../utils/QuoteParser.sol"; +import {TD10ReportBody} from "automata-dcap-attestation/contracts/types/V4Structs.sol"; /** * @title IFlashtestationRegistry @@ -11,22 +11,22 @@ import {WorkloadId} from "../utils/QuoteParser.sol"; interface IFlashtestationRegistry { // TEE identity and status tracking struct RegisteredTEE { - WorkloadId workloadId; // The workloadID of the TEE device - bytes rawQuote; // The raw quote from the TEE device, which is stored to allow for future quote quote invalidation bool isValid; // true upon first registration, and false after a quote invalidation - bytes publicKey; // The 64-byte uncompressed public key of TEE-controlled address, used to encrypt messages to the TEE + bytes rawQuote; // The raw quote from the TEE device, which is stored to allow for future quote quote invalidation + TD10ReportBody parsedReportBody; // Parsed form of the quote to avoid unnecessary parsing + bytes extendedRegistrationData; // Any additional attested data for application purposes. This data is attested by a bytes32 in the attestation's reportData, where that bytes32 is a hash of the ABI-encoded values in the extendedRegistrationData. This registration data can be anything that's needed for your app, for instance an vmOperatorIdPublicKey that allows you to verify signatures from your TEE operator, parts of runtime configuration of the VM, configuration submitted by the VM operator, for example the public IP of the instance, } // Events - event TEEServiceRegistered( - address teeAddress, WorkloadId workloadId, bytes rawQuote, bytes publicKey, bool alreadyExists - ); + event TEEServiceRegistered(address teeAddress, bytes rawQuote, bool alreadyExists); event TEEServiceInvalidated(address teeAddress); // Errors error InvalidQuote(bytes output); + error InvalidReportDataLength(uint256 length); + error InvalidRegistrationDataHash(bytes32 expected, bytes32 received); error ByteSizeExceeded(uint256 size); - error TEEServiceAlreadyRegistered(address teeAddress, WorkloadId workloadId); + error TEEServiceAlreadyRegistered(address teeAddress); error SenderMustMatchTEEAddress(address sender, address teeAddress); error TEEServiceNotRegistered(address teeAddress); error TEEServiceAlreadyInvalid(address teeAddress); diff --git a/src/utils/QuoteParser.sol b/src/utils/QuoteParser.sol index 8c4e2ff..19142f3 100644 --- a/src/utils/QuoteParser.sol +++ b/src/utils/QuoteParser.sol @@ -5,16 +5,6 @@ import {TD10ReportBody} from "automata-dcap-attestation/contracts/types/V4Struct import {BytesUtils} from "@automata-network/on-chain-pccs/utils/BytesUtils.sol"; import {TD_REPORT10_LENGTH, TDX_TEE, HEADER_LENGTH} from "automata-dcap-attestation/contracts/types/Constants.sol"; -// User Defined Types - -// WorkloadID uniquely identifies a TEE workload. A workload is derived from a combination -// of the TEE's measurement registers. The TDX platform provides several registers that -// capture cryptographic hashes of code, data, and configuration loaded into the TEE's environment. -// This means that whenever a TEE device changes anything about its compute stack (e.g. user code, -// firmware, OS, etc), the workloadID will change. -// See the [Flashtestation's specification](https://github.com/flashbots/rollup-boost/blob/main/specs/flashtestations.md#workload-identity-derivation) for more details -type WorkloadId is bytes32; - /** * @title QuoteParser * @dev A library for parsing and extracting workload and TEE-controlled address from Automata DCAP Attestation TDX quotes @@ -27,9 +17,6 @@ library QuoteParser { // The TDX version of the quote Flashtestation's accepts uint256 public constant ACCEPTED_TDX_VERSION = 4; - // The length of the Ethereum uncompressed public key in bytes - uint256 public constant ETHEREUM_PUBLIC_KEY_LENGTH = 64; - // This is the number of bytes in the Output struct that come before the quoteBody. // See the Output struct definition in the Automata DCAP Attestation repo: // https://github.com/automata-network/automata-dcap-attestation/blob/evm-v1.0.0/evm/contracts/types/CommonStruct.sol#L113 @@ -40,7 +27,6 @@ library QuoteParser { error InvalidTEEType(bytes4 teeType); error InvalidTEEVersion(uint16 version); - error InvalidReportDataLength(uint256 length); error InvalidQuoteLength(uint256 length); /** @@ -99,54 +85,19 @@ library QuoteParser { report.rtMr1 = rawReportBody.substring(376, 48); report.rtMr2 = rawReportBody.substring(424, 48); report.rtMr3 = rawReportBody.substring(472, 48); - report.reportData = rawReportBody.substring(520, ETHEREUM_PUBLIC_KEY_LENGTH); - } - - /** - * @notice Extracts the TEE-controlled address from the TD10ReportBody - * @dev The Ethereum address is derived using the first 64 bytes of the reportData - * @dev A core part of the flashtestation's protocol is that the TEE generates a TEE-controlled - * address, which is used to identify the TEE in the FlashtestationRegistry. This address is derived - * from the TEE's public key, which is included in the quote's reportData field - * @param td10ReportBody The TD10ReportBody to extract the TEE-controlled address from - * @return address TEE-controlled address - */ - function extractEthereumAddress(TD10ReportBody memory td10ReportBody) internal pure returns (address) { - bytes memory publicKey = extractPublicKey(td10ReportBody); - - return address(uint160(uint256(keccak256(publicKey)))); - } - - function extractPublicKey(TD10ReportBody memory td10ReportBody) internal pure returns (bytes memory) { - if (td10ReportBody.reportData.length != ETHEREUM_PUBLIC_KEY_LENGTH) { - revert InvalidReportDataLength(td10ReportBody.reportData.length); - } - return td10ReportBody.reportData.substring(0, 64); + report.reportData = rawReportBody.substring(520, 64); } /** - * @notice Derives the TEE's workloadId from the TD10ReportBody - * @dev The workloadId is derived using the TEE's measurement registers - * @param td10ReportBody The TD10ReportBody to extract the workloadId from - * @return WorkloadId workloadId + * Parses reportData to tee address and hash of extended report data */ - function extractWorkloadId(TD10ReportBody memory td10ReportBody) internal pure returns (WorkloadId) { - return WorkloadId.wrap( - keccak256( - abi.encode( - td10ReportBody.mrTd, - td10ReportBody.rtMr0, - td10ReportBody.rtMr1, - td10ReportBody.rtMr2, - td10ReportBody.rtMr3, - td10ReportBody.mrOwner, - td10ReportBody.mrOwnerConfig, - td10ReportBody.mrConfigId, - td10ReportBody.tdAttributes, - td10ReportBody.xFAM - ) - ) - ); + function parseReportData(bytes memory reportDataBytes) + internal + pure + returns (address teeAddress, bytes32 extDataHash) + { + teeAddress = address(uint160(bytes20(reportDataBytes.substring(0, 20)))); + extDataHash = bytes32(reportDataBytes.substring(20, 32)); } /** @@ -167,7 +118,7 @@ library QuoteParser { * @dev Automata currently only supports SGX and TDX TEE types */ function checkTEEType(bytes memory rawReportBody) internal pure { - bytes4 teeType = bytes4(rawReportBody.substring(2, 6)); // 4 bytes + bytes4 teeType = bytes4(rawReportBody.substring(2, 4)); // 4 bytes if (teeType != TDX_TEE) { revert InvalidTEEType(teeType); } diff --git a/test/BlockBuilderPolicy.t.sol b/test/BlockBuilderPolicy.t.sol index cb20bfe..9169cc0 100644 --- a/test/BlockBuilderPolicy.t.sol +++ b/test/BlockBuilderPolicy.t.sol @@ -5,11 +5,11 @@ import {Test, console} from "forge-std/Test.sol"; import {UnsafeUpgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {BlockBuilderPolicy} from "../src/BlockBuilderPolicy.sol"; +import {BlockBuilderPolicy, WorkloadId} from "../src/BlockBuilderPolicy.sol"; import {FlashtestationRegistry} from "../src/FlashtestationRegistry.sol"; import {IFlashtestationRegistry} from "../src/interfaces/IFlashtestationRegistry.sol"; import {MockQuote} from "../test/FlashtestationRegistry.t.sol"; -import {QuoteParser, WorkloadId} from "../src/utils/QuoteParser.sol"; +import {QuoteParser} from "../src/utils/QuoteParser.sol"; import {Upgrader} from "./helpers/Upgrader.sol"; import {MockAutomataDcapAttestationFee} from "./mocks/MockAutomataDcapAttestationFee.sol"; import {Helper} from "./helpers/Helper.sol"; @@ -24,50 +24,39 @@ contract BlockBuilderPolicyTest is Test { uint8 version = 1; - MockQuote bf42Mock = MockQuote({ - output: vm.readFileBinary( - "test/raw_tdx_quotes/bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446/output.bin" - ), - quote: vm.readFileBinary( - "test/raw_tdx_quotes/bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446/quote.bin" - ), - publicKey: hex"bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446", + // Extended MockQuote struct with workloadId for policy tests + struct PolicyMockQuote { + bytes output; + bytes quote; + address teeAddress; + WorkloadId workloadId; + uint256 privateKey; + } + + PolicyMockQuote mockf200 = PolicyMockQuote({ + output: vm.readFileBinary("test/raw_tdx_quotes/0xf200f222043C5bC6c70AA6e35f5C5FDe079F3a03/output.bin"), + quote: vm.readFileBinary("test/raw_tdx_quotes/0xf200f222043C5bC6c70AA6e35f5C5FDe079F3a03/quote.bin"), teeAddress: 0xf200f222043C5bC6c70AA6e35f5C5FDe079F3a03, workloadId: WorkloadId.wrap(0xeee0d5f864e6d46d6da790c7d60baac5c8478eb89e86667336d3f17655e9164e), privateKey: 0x0000000000000000000000000000000000000000000000000000000000000000 // unused for this mock }); - MockQuote bf42MockWithDifferentWorkloadId = MockQuote({ - output: vm.readFileBinary( - "test/raw_tdx_quotes/bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446/output2.bin" - ), - quote: vm.readFileBinary( - "test/raw_tdx_quotes/bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446/quote2.bin" - ), - publicKey: hex"bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446", + PolicyMockQuote mockf200WithDifferentWorkloadId = PolicyMockQuote({ // TODO! + output: vm.readFileBinary("test/raw_tdx_quotes/0xf200f222043C5bC6c70AA6e35f5C5FDe079F3a03/output2.bin"), + quote: vm.readFileBinary("test/raw_tdx_quotes/0xf200f222043C5bC6c70AA6e35f5C5FDe079F3a03/quote2.bin"), teeAddress: 0xf200f222043C5bC6c70AA6e35f5C5FDe079F3a03, workloadId: WorkloadId.wrap(0x5e6be81f9e5b10d15a6fa69b19ab0269cd943db39fa1f0d38a76eb76146948cb), privateKey: 0x0000000000000000000000000000000000000000000000000000000000000000 // unused for this mock }); - MockQuote d204Mock = MockQuote({ - output: vm.readFileBinary( - "test/raw_tdx_quotes/0xd204547069c53f9ecff9b30494eb9797615a2f46aa2785db6258104cebb92d48ff4dc0744c36d8470646f4813e61f9a831ffb54b937f7b233f32d271434ccca6/output.bin" - ), - quote: vm.readFileBinary( - "test/raw_tdx_quotes/0xd204547069c53f9ecff9b30494eb9797615a2f46aa2785db6258104cebb92d48ff4dc0744c36d8470646f4813e61f9a831ffb54b937f7b233f32d271434ccca6/quote.bin" - ), - publicKey: hex"d204547069c53f9ecff9b30494eb9797615a2f46aa2785db6258104cebb92d48ff4dc0744c36d8470646f4813e61f9a831ffb54b937f7b233f32d271434ccca6", + PolicyMockQuote mock12c1 = PolicyMockQuote({ + output: vm.readFileBinary("test/raw_tdx_quotes/0x12c14e56d585Dcf3B36f37476c00E78bA9363742/output.bin"), + quote: vm.readFileBinary("test/raw_tdx_quotes/0x12c14e56d585Dcf3B36f37476c00E78bA9363742/quote.bin"), teeAddress: 0x12c14e56d585Dcf3B36f37476c00E78bA9363742, workloadId: WorkloadId.wrap(0xeee0d5f864e6d46d6da790c7d60baac5c8478eb89e86667336d3f17655e9164e), privateKey: 0x0000000000000000000000000000000000000000000000000000000000000000 // unused for this mock }); - MockQuote mock7b91 = MockQuote({ - output: vm.readFileBinary( - "test/raw_tdx_quotes/7b916d70ed77488d6c1ced7117ba410655a8faa8d6c7740562a88ab3cb9cbca63e2d5761812a11d90c009ed017113131370070cd3a2d5fba64d9dbb76952df19/output.bin" - ), - quote: vm.readFileBinary( - "test/raw_tdx_quotes/7b916d70ed77488d6c1ced7117ba410655a8faa8d6c7740562a88ab3cb9cbca63e2d5761812a11d90c009ed017113131370070cd3a2d5fba64d9dbb76952df19/quote.bin" - ), - publicKey: hex"7b916d70ed77488d6c1ced7117ba410655a8faa8d6c7740562a88ab3cb9cbca63e2d5761812a11d90c009ed017113131370070cd3a2d5fba64d9dbb76952df19", + PolicyMockQuote mock46f6 = PolicyMockQuote({ + output: vm.readFileBinary("test/raw_tdx_quotes/0x46f6b3ACF1dD8Ac0085e30192741336c4aF6EdAF/output.bin"), + quote: vm.readFileBinary("test/raw_tdx_quotes/0x46f6b3ACF1dD8Ac0085e30192741336c4aF6EdAF/quote.bin"), teeAddress: 0x46f6b3ACF1dD8Ac0085e30192741336c4aF6EdAF, workloadId: WorkloadId.wrap(0xeee0d5f864e6d46d6da790c7d60baac5c8478eb89e86667336d3f17655e9164e), privateKey: 0x92e4b5ed61db615b26da2271da5b47c42d691b3164561cfb4edbc85ca6ca61a8 @@ -93,81 +82,81 @@ contract BlockBuilderPolicyTest is Test { policy = BlockBuilderPolicy(policyProxy); } - function _registerTEE(MockQuote memory mock) internal { + function _registerTEE(PolicyMockQuote memory mock) internal { attestationContract.setQuoteResult(mock.quote, true, mock.output); vm.prank(mock.teeAddress); - registry.registerTEEService(mock.quote); + registry.registerTEEService(mock.quote, bytes("")); // Add empty extended data } function test_addWorkloadToPolicy_and_getters() public { // Add a workload - policy.addWorkloadToPolicy(bf42Mock.workloadId); + policy.addWorkloadToPolicy(mockf200.workloadId); // getWorkloads should return the workload bytes32[] memory workloads = policy.getWorkloads(); assertEq(workloads.length, 1); - assertEq(workloads[0], WorkloadId.unwrap(bf42Mock.workloadId)); + assertEq(workloads[0], WorkloadId.unwrap(mockf200.workloadId)); // getWorkload(0) should return the same bytes32 workload = policy.getWorkload(0); - assertEq(workload, WorkloadId.unwrap(bf42Mock.workloadId)); + assertEq(workload, WorkloadId.unwrap(mockf200.workloadId)); } function test_addWorkloadToPolicy_with_multiple_workloads() public { - policy.addWorkloadToPolicy(bf42Mock.workloadId); - policy.addWorkloadToPolicy(bf42MockWithDifferentWorkloadId.workloadId); + policy.addWorkloadToPolicy(mockf200.workloadId); + policy.addWorkloadToPolicy(mockf200WithDifferentWorkloadId.workloadId); bytes32[] memory workloads = policy.getWorkloads(); assertEq(workloads.length, 2); - assertEq(workloads[0], WorkloadId.unwrap(bf42Mock.workloadId)); - assertEq(workloads[1], WorkloadId.unwrap(bf42MockWithDifferentWorkloadId.workloadId)); + assertEq(workloads[0], WorkloadId.unwrap(mockf200.workloadId)); + assertEq(workloads[1], WorkloadId.unwrap(mockf200WithDifferentWorkloadId.workloadId)); } function test_addWorkloadToPolicy_reverts_if_duplicate() public { - policy.addWorkloadToPolicy(bf42Mock.workloadId); + policy.addWorkloadToPolicy(mockf200.workloadId); vm.expectRevert(BlockBuilderPolicy.WorkloadAlreadyInPolicy.selector); - policy.addWorkloadToPolicy(bf42Mock.workloadId); + policy.addWorkloadToPolicy(mockf200.workloadId); } function test_addWorkloadToPolicy_reverts_if_not_owner() public { vm.prank(address(0x123)); vm.expectRevert(abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, address(0x123))); - policy.addWorkloadToPolicy(bf42Mock.workloadId); + policy.addWorkloadToPolicy(mockf200.workloadId); } function test_removeWorkloadFromPolicy() public { - policy.addWorkloadToPolicy(bf42Mock.workloadId); - policy.removeWorkloadFromPolicy(bf42Mock.workloadId); + policy.addWorkloadToPolicy(mockf200.workloadId); + policy.removeWorkloadFromPolicy(mockf200.workloadId); bytes32[] memory workloads = policy.getWorkloads(); assertEq(workloads.length, 0); } function test_removeWorkloadFromPolicy_with_multiple_workloads() public { - policy.addWorkloadToPolicy(bf42Mock.workloadId); - policy.addWorkloadToPolicy(bf42MockWithDifferentWorkloadId.workloadId); - policy.removeWorkloadFromPolicy(bf42Mock.workloadId); + policy.addWorkloadToPolicy(mockf200.workloadId); + policy.addWorkloadToPolicy(mockf200WithDifferentWorkloadId.workloadId); + policy.removeWorkloadFromPolicy(mockf200.workloadId); bytes32[] memory workloads = policy.getWorkloads(); assertEq(workloads.length, 1); - assertEq(workloads[0], WorkloadId.unwrap(bf42MockWithDifferentWorkloadId.workloadId)); + assertEq(workloads[0], WorkloadId.unwrap(mockf200WithDifferentWorkloadId.workloadId)); // now remove the other workload - policy.removeWorkloadFromPolicy(bf42MockWithDifferentWorkloadId.workloadId); + policy.removeWorkloadFromPolicy(mockf200WithDifferentWorkloadId.workloadId); workloads = policy.getWorkloads(); assertEq(workloads.length, 0); // now add the workloads back - policy.addWorkloadToPolicy(bf42Mock.workloadId); + policy.addWorkloadToPolicy(mockf200.workloadId); workloads = policy.getWorkloads(); assertEq(workloads.length, 1); - assertEq(workloads[0], WorkloadId.unwrap(bf42Mock.workloadId)); - policy.addWorkloadToPolicy(bf42MockWithDifferentWorkloadId.workloadId); + assertEq(workloads[0], WorkloadId.unwrap(mockf200.workloadId)); + policy.addWorkloadToPolicy(mockf200WithDifferentWorkloadId.workloadId); workloads = policy.getWorkloads(); assertEq(workloads.length, 2); - assertEq(workloads[0], WorkloadId.unwrap(bf42Mock.workloadId)); - assertEq(workloads[1], WorkloadId.unwrap(bf42MockWithDifferentWorkloadId.workloadId)); + assertEq(workloads[0], WorkloadId.unwrap(mockf200.workloadId)); + assertEq(workloads[1], WorkloadId.unwrap(mockf200WithDifferentWorkloadId.workloadId)); } function test_removeWorkloadFromPolicy_with_multiple_workloads_present() public { - policy.addWorkloadToPolicy(bf42Mock.workloadId); + policy.addWorkloadToPolicy(mockf200.workloadId); policy.addWorkloadToPolicy(arbitraryWorkloadId); - policy.removeWorkloadFromPolicy(bf42Mock.workloadId); + policy.removeWorkloadFromPolicy(mockf200.workloadId); bytes32[] memory workloads = policy.getWorkloads(); assertEq(workloads.length, 1); assertEq(workloads[0], WorkloadId.unwrap(arbitraryWorkloadId)); @@ -178,98 +167,187 @@ contract BlockBuilderPolicyTest is Test { function test_removeWorkloadFromPolicy_reverts_if_not_present() public { vm.expectRevert(BlockBuilderPolicy.WorkloadNotInPolicy.selector); - policy.removeWorkloadFromPolicy(bf42Mock.workloadId); + policy.removeWorkloadFromPolicy(mockf200.workloadId); } function test_removeWorkloadFromPolicy_reverts_if_not_owner() public { vm.prank(address(0x123)); vm.expectRevert(abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, address(0x123))); - policy.removeWorkloadFromPolicy(bf42Mock.workloadId); + policy.removeWorkloadFromPolicy(mockf200.workloadId); } function test_isAllowedPolicy_returns_true_for_valid_tee() public { // Register TEE and add workload to policy - _registerTEE(bf42Mock); - policy.addWorkloadToPolicy(bf42Mock.workloadId); + _registerTEE(mockf200); + + // Get the actual workloadId from the registration + (, IFlashtestationRegistry.RegisteredTEE memory registration) = registry.getRegistration(mockf200.teeAddress); + WorkloadId actualWorkloadId = policy.workloadIdForTDRegistration(registration); + + // Add the actual workloadId to the policy + policy.addWorkloadToPolicy(actualWorkloadId); + // Should return true - (bool allowed, WorkloadId workloadId) = policy.isAllowedPolicy(bf42Mock.teeAddress); + (bool allowed, WorkloadId workloadId) = policy.isAllowedPolicy(mockf200.teeAddress); assertTrue(allowed); - assertEq(WorkloadId.unwrap(workloadId), WorkloadId.unwrap(bf42Mock.workloadId)); + assertEq(WorkloadId.unwrap(workloadId), WorkloadId.unwrap(actualWorkloadId)); } function test_isAllowedPolicy_returns_false_for_unregistered_tee() public { // Add workload but do not register TEE - policy.addWorkloadToPolicy(bf42Mock.workloadId); - (bool allowed, WorkloadId workloadId) = policy.isAllowedPolicy(bf42Mock.teeAddress); + policy.addWorkloadToPolicy(mockf200.workloadId); + (bool allowed, WorkloadId workloadId) = policy.isAllowedPolicy(mockf200.teeAddress); assertFalse(allowed); assertEq(WorkloadId.unwrap(workloadId), 0); } function test_isAllowedPolicy_returns_false_for_invalid_quote() public { - // Register TEE and add workload to policy - _registerTEE(bf42Mock); - policy.addWorkloadToPolicy(bf42Mock.workloadId); + // Register TEE + _registerTEE(mockf200); + + // Get the actual workloadId and add to policy + (, IFlashtestationRegistry.RegisteredTEE memory registration) = registry.getRegistration(mockf200.teeAddress); + WorkloadId actualWorkloadId = policy.workloadIdForTDRegistration(registration); + policy.addWorkloadToPolicy(actualWorkloadId); + // Now invalidate the TEE - attestationContract.setQuoteResult(bf42Mock.quote, false, new bytes(0)); - registry.invalidateAttestation(bf42Mock.teeAddress); + attestationContract.setQuoteResult(mockf200.quote, false, new bytes(0)); + registry.invalidateAttestation(mockf200.teeAddress); + // Should return false - (bool allowed, WorkloadId workloadId) = policy.isAllowedPolicy(bf42Mock.teeAddress); + (bool allowed, WorkloadId workloadId) = policy.isAllowedPolicy(mockf200.teeAddress); assertFalse(allowed); assertEq(WorkloadId.unwrap(workloadId), 0); } function test_isAllowedPolicy_returns_false_for_wrong_workload() public { - _registerTEE(bf42Mock); + _registerTEE(mockf200); policy.addWorkloadToPolicy(wrongWorkloadId); - (bool allowed, WorkloadId workloadId) = policy.isAllowedPolicy(bf42Mock.teeAddress); + (bool allowed, WorkloadId workloadId) = policy.isAllowedPolicy(mockf200.teeAddress); assertFalse(allowed); assertEq(WorkloadId.unwrap(workloadId), 0); } function test_isAllowedPolicy_returns_false_for_invalid_tee_when_multiple_workloads_present() public { - policy.addWorkloadToPolicy(bf42Mock.workloadId); + policy.addWorkloadToPolicy(mockf200.workloadId); policy.addWorkloadToPolicy(wrongWorkloadId); - (bool allowed, WorkloadId workloadId) = policy.isAllowedPolicy(bf42Mock.teeAddress); + (bool allowed, WorkloadId workloadId) = policy.isAllowedPolicy(mockf200.teeAddress); assertFalse(allowed); assertEq(WorkloadId.unwrap(workloadId), 0); } - function test_isAllowedPolicy2_returns_true_for_valid_tee_and_tcb() public { - _registerTEE(bf42Mock); - policy.addWorkloadToPolicy(bf42Mock.workloadId); - TD10ReportBody memory report = registry.getReportBody(bf42Mock.teeAddress); - bool allowed = policy.isAllowedPolicy2(bf42Mock.teeAddress, report.teeTcbSvn); - assertTrue(allowed); + function test_workloadIdForTDRegistration() public { + // Register a TEE + _registerTEE(mockf200); + + // Get the registration + (, IFlashtestationRegistry.RegisteredTEE memory registration) = registry.getRegistration(mockf200.teeAddress); + + // Compute the workloadId + WorkloadId computedWorkloadId = policy.workloadIdForTDRegistration(registration); + + // The computed workloadId should be deterministic based on the TD report fields + assertTrue(WorkloadId.unwrap(computedWorkloadId) != 0, "WorkloadId should not be zero"); + } + + function test_workloadIdForTDRegistration_is_deterministic() public { + // Register a TEE + _registerTEE(mockf200); + _registerTEE(mock12c1); + + // Get the registration + (, IFlashtestationRegistry.RegisteredTEE memory registrationF200) = + registry.getRegistration(mockf200.teeAddress); + (, IFlashtestationRegistry.RegisteredTEE memory registration12c1) = + registry.getRegistration(mock12c1.teeAddress); + + // Compute the workloadId + WorkloadId computedWorkloadIdF200 = policy.workloadIdForTDRegistration(registrationF200); + WorkloadId computedWorkloadId12c1 = policy.workloadIdForTDRegistration(registration12c1); + + // Same measurements, different addresses and ext data. workloadId should match. + assertEq(WorkloadId.unwrap(computedWorkloadIdF200), WorkloadId.unwrap(computedWorkloadId12c1)); + } + + // Add these test functions to BlockBuilderPolicyTest contract + + function test_workloadId_tdAttributes_allowed_bits_ignored() public { + // Register a TEE to get a baseline + _registerTEE(mockf200); + (, IFlashtestationRegistry.RegisteredTEE memory baseRegistration) = + registry.getRegistration(mockf200.teeAddress); + WorkloadId baseWorkloadId = policy.workloadIdForTDRegistration(baseRegistration); + + // Test that all combinations of allowed bits don't affect workloadId + // We test: none set, all set, and one intermediate case + bytes8[3] memory allowedBitCombos = [ + bytes8(0x00000000D0000000), // All three allowed bits set (VE_DISABLED | PKS | KL) + bytes8(0x0000000050000000), // VE_DISABLED | PKS + bytes8(0x0000000000000000) // None set + ]; + + for (uint256 i = 0; i < allowedBitCombos.length; i++) { + IFlashtestationRegistry.RegisteredTEE memory modifiedRegAllowed = baseRegistration; + // Clear the allowed bits first, then set the specific combination + modifiedRegAllowed.parsedReportBody.tdAttributes = + (baseRegistration.parsedReportBody.tdAttributes & ~bytes8(0x00000000D0000000)) | allowedBitCombos[i]; + + WorkloadId workloadId = policy.workloadIdForTDRegistration(modifiedRegAllowed); + assertEq( + WorkloadId.unwrap(baseWorkloadId), + WorkloadId.unwrap(workloadId), + "Allowed tdAttributes bits should not affect workloadId" + ); + } + + // Test that a non-allowed bit DOES change workloadId + IFlashtestationRegistry.RegisteredTEE memory modifiedReg = baseRegistration; + modifiedReg.parsedReportBody.tdAttributes = + baseRegistration.parsedReportBody.tdAttributes | bytes8(0x0000000000000001); + WorkloadId differentWorkloadId = policy.workloadIdForTDRegistration(modifiedReg); + assertNotEq( + WorkloadId.unwrap(baseWorkloadId), + WorkloadId.unwrap(differentWorkloadId), + "Non-allowed tdAttributes bits should affect workloadId" + ); } - function test_isAllowedPolicy2_returns_false_for_wrong_tcb() public { - _registerTEE(bf42Mock); - policy.addWorkloadToPolicy(bf42Mock.workloadId); - // Use a random bytes16 for tcb - bool allowed = policy.isAllowedPolicy2(bf42Mock.teeAddress, bytes16(hex"deadbeef")); - assertFalse(allowed); - } + function test_workloadId_xfam_expected_bits_required() public { + // Register a TEE to get a baseline + _registerTEE(mockf200); + (, IFlashtestationRegistry.RegisteredTEE memory baseRegistration) = + registry.getRegistration(mockf200.teeAddress); + WorkloadId baseWorkloadId = policy.workloadIdForTDRegistration(baseRegistration); + + // Test removing FPU bit changes workloadId + IFlashtestationRegistry.RegisteredTEE memory modifiedReg1 = baseRegistration; + modifiedReg1.parsedReportBody.xFAM = baseRegistration.parsedReportBody.xFAM ^ bytes8(0x0000000000000001); + WorkloadId workloadIdNoFPU = policy.workloadIdForTDRegistration(modifiedReg1); + assertNotEq( + WorkloadId.unwrap(baseWorkloadId), + WorkloadId.unwrap(workloadIdNoFPU), + "Missing FPU bit should change workloadId" + ); - function test_isAllowedPolicy2_returns_false_for_unregistered_tee() public { - policy.addWorkloadToPolicy(bf42Mock.workloadId); - TD10ReportBody memory report = QuoteParser.parseV4Quote(bf42Mock.quote); - vm.expectRevert( - abi.encodeWithSelector(IFlashtestationRegistry.TEEServiceNotRegistered.selector, bf42Mock.teeAddress) + // Test removing SSE bit changes workloadId + IFlashtestationRegistry.RegisteredTEE memory modifiedReg2 = baseRegistration; + modifiedReg2.parsedReportBody.xFAM = baseRegistration.parsedReportBody.xFAM ^ bytes8(0x0000000000000002); + WorkloadId workloadIdNoSSE = policy.workloadIdForTDRegistration(modifiedReg2); + assertNotEq( + WorkloadId.unwrap(baseWorkloadId), + WorkloadId.unwrap(workloadIdNoSSE), + "Missing SSE bit should change workloadId" ); - policy.isAllowedPolicy2(bf42Mock.teeAddress, report.teeTcbSvn); - } - function test_isAllowedPolicy2_returns_false_for_invalid_tee() public { - // Register TEE and add workload to policy - _registerTEE(bf42Mock); - policy.addWorkloadToPolicy(bf42Mock.workloadId); - // Now invalidate the TEE - attestationContract.setQuoteResult(bf42Mock.quote, false, new bytes(0)); - registry.invalidateAttestation(bf42Mock.teeAddress); - // Should return false - TD10ReportBody memory report = registry.getReportBody(bf42Mock.teeAddress); - bool allowed = policy.isAllowedPolicy2(bf42Mock.teeAddress, report.teeTcbSvn); - assertFalse(allowed); + // Test adding an extra bit changes workloadId + IFlashtestationRegistry.RegisteredTEE memory modifiedReg3 = baseRegistration; + modifiedReg3.parsedReportBody.xFAM = baseRegistration.parsedReportBody.xFAM | bytes8(0x0000000000000008); + WorkloadId workloadIdExtraBit = policy.workloadIdForTDRegistration(modifiedReg3); + assertNotEq( + WorkloadId.unwrap(baseWorkloadId), + WorkloadId.unwrap(workloadIdExtraBit), + "Additional xFAM bits should change workloadId" + ); } function test_getWorkload_reverts_on_out_of_bounds() public { @@ -279,37 +357,45 @@ contract BlockBuilderPolicyTest is Test { } function test_verifyBlockBuilderProof_fails_with_incorrect_version() public { - _registerTEE(bf42Mock); - policy.addWorkloadToPolicy(bf42Mock.workloadId); + _registerTEE(mockf200); + + // Get actual workloadId and add to policy + (, IFlashtestationRegistry.RegisteredTEE memory registration) = registry.getRegistration(mockf200.teeAddress); + WorkloadId actualWorkloadId = policy.workloadIdForTDRegistration(registration); + policy.addWorkloadToPolicy(actualWorkloadId); // Try with unsupported version 2 - vm.prank(bf42Mock.teeAddress); + vm.prank(mockf200.teeAddress); vm.expectRevert(abi.encodeWithSelector(BlockBuilderPolicy.UnsupportedVersion.selector, 2)); policy.verifyBlockBuilderProof(2, bytes32(0)); } function test_verifyBlockBuilderProof_fails_with_unregistered_tee() public { // Add workload to policy but don't register TEE - policy.addWorkloadToPolicy(bf42Mock.workloadId); + policy.addWorkloadToPolicy(mockf200.workloadId); - vm.prank(bf42Mock.teeAddress); + vm.prank(mockf200.teeAddress); vm.expectRevert( - abi.encodeWithSelector(BlockBuilderPolicy.UnauthorizedBlockBuilder.selector, bf42Mock.teeAddress) + abi.encodeWithSelector(BlockBuilderPolicy.UnauthorizedBlockBuilder.selector, mockf200.teeAddress) ); policy.verifyBlockBuilderProof(1, bytes32(0)); } function test_verifyBlockBuilderProof_succeeds_with_valid_tee_and_version() public { - _registerTEE(bf42Mock); - policy.addWorkloadToPolicy(bf42Mock.workloadId); + _registerTEE(mockf200); + + // Get actual workloadId and add to policy + (, IFlashtestationRegistry.RegisteredTEE memory registration) = registry.getRegistration(mockf200.teeAddress); + WorkloadId actualWorkloadId = policy.workloadIdForTDRegistration(registration); + policy.addWorkloadToPolicy(actualWorkloadId); bytes32 blockContentHash = bytes32(hex"1234"); vm.expectEmit(address(policy)); emit BlockBuilderPolicy.BlockBuilderProofVerified( - bf42Mock.teeAddress, bf42Mock.workloadId, 1, 1, blockContentHash + mockf200.teeAddress, actualWorkloadId, block.number, 1, blockContentHash ); - vm.prank(bf42Mock.teeAddress); + vm.prank(mockf200.teeAddress); policy.verifyBlockBuilderProof(1, blockContentHash); } @@ -334,23 +420,27 @@ contract BlockBuilderPolicyTest is Test { } function test_successful_permitVerifyBlockBuilderProof() public { - address teeAddress = mock7b91.teeAddress; + address teeAddress = mock46f6.teeAddress; bytes32 blockContentHash = Helper.computeFlashtestationBlockContentHash(); - // Register TEE and add workload to policy - _registerTEE(mock7b91); - policy.addWorkloadToPolicy(mock7b91.workloadId); + // Register TEE + _registerTEE(mock46f6); + + // Get actual workloadId and add to policy + (, IFlashtestationRegistry.RegisteredTEE memory registration) = registry.getRegistration(teeAddress); + WorkloadId actualWorkloadId = policy.workloadIdForTDRegistration(registration); + policy.addWorkloadToPolicy(actualWorkloadId); // Create the EIP-712 signature bytes32 structHash = policy.computeStructHash(version, blockContentHash, 0); bytes32 digest = policy.getHashedTypeDataV4(structHash); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(mock7b91.privateKey, digest); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(mock46f6.privateKey, digest); bytes memory signature = abi.encodePacked(r, s, v); // Expect the event to be emitted vm.expectEmit(address(policy)); emit BlockBuilderPolicy.BlockBuilderProofVerified( - teeAddress, mock7b91.workloadId, block.number, version, blockContentHash + teeAddress, actualWorkloadId, block.number, version, blockContentHash ); // Call the function @@ -361,23 +451,27 @@ contract BlockBuilderPolicyTest is Test { } function test_successful_permitVerifyBlockBuilderProof_multiple_times() public { - address teeAddress = mock7b91.teeAddress; + address teeAddress = mock46f6.teeAddress; bytes32 blockContentHash = Helper.computeFlashtestationBlockContentHash(); - // Register TEE and add workload to policy - _registerTEE(mock7b91); - policy.addWorkloadToPolicy(mock7b91.workloadId); + // Register TEE + _registerTEE(mock46f6); + + // Get actual workloadId and add to policy + (, IFlashtestationRegistry.RegisteredTEE memory registration) = registry.getRegistration(teeAddress); + WorkloadId actualWorkloadId = policy.workloadIdForTDRegistration(registration); + policy.addWorkloadToPolicy(actualWorkloadId); // Create the EIP-712 signature bytes32 structHash = policy.computeStructHash(version, blockContentHash, 0); bytes32 digest = policy.getHashedTypeDataV4(structHash); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(mock7b91.privateKey, digest); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(mock46f6.privateKey, digest); bytes memory signature = abi.encodePacked(r, s, v); // Expect the event to be emitted vm.expectEmit(address(policy)); emit BlockBuilderPolicy.BlockBuilderProofVerified( - teeAddress, mock7b91.workloadId, block.number, version, blockContentHash + teeAddress, actualWorkloadId, block.number, version, blockContentHash ); // Call the function @@ -390,13 +484,13 @@ contract BlockBuilderPolicyTest is Test { structHash = policy.computeStructHash(version, blockContentHash, 1); digest = policy.getHashedTypeDataV4(structHash); - (v, r, s) = vm.sign(mock7b91.privateKey, digest); + (v, r, s) = vm.sign(mock46f6.privateKey, digest); signature = abi.encodePacked(r, s, v); // Expect the event to be emitted vm.expectEmit(address(policy)); emit BlockBuilderPolicy.BlockBuilderProofVerified( - teeAddress, mock7b91.workloadId, block.number, version, blockContentHash + teeAddress, actualWorkloadId, block.number, version, blockContentHash ); // Call the function @@ -427,7 +521,7 @@ contract BlockBuilderPolicyTest is Test { // Create signature with wrong nonce bytes32 structHash = policy.computeStructHash(version, blockContentHash, 1); // wrong nonce bytes32 digest = policy.getHashedTypeDataV4(structHash); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(mock7b91.privateKey, digest); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(mock46f6.privateKey, digest); bytes memory signature = abi.encodePacked(r, s, v); vm.expectRevert(abi.encodeWithSelector(BlockBuilderPolicy.InvalidNonce.selector, 0, 1)); @@ -437,14 +531,18 @@ contract BlockBuilderPolicyTest is Test { function test_permitVerifyBlockBuilderProof_reverts_with_replayed_signature() public { bytes32 blockContentHash = Helper.computeFlashtestationBlockContentHash(); - // Register TEE and add workload to policy - _registerTEE(mock7b91); - policy.addWorkloadToPolicy(mock7b91.workloadId); + // Register TEE + _registerTEE(mock46f6); + + // Get actual workloadId and add to policy + (, IFlashtestationRegistry.RegisteredTEE memory registration) = registry.getRegistration(mock46f6.teeAddress); + WorkloadId actualWorkloadId = policy.workloadIdForTDRegistration(registration); + policy.addWorkloadToPolicy(actualWorkloadId); // Create the EIP-712 signature bytes32 structHash = policy.computeStructHash(version, blockContentHash, 0); bytes32 digest = policy.getHashedTypeDataV4(structHash); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(mock7b91.privateKey, digest); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(mock46f6.privateKey, digest); bytes memory signature = abi.encodePacked(r, s, v); // First verification should succeed @@ -462,7 +560,7 @@ contract BlockBuilderPolicyTest is Test { uint8 unsupportedVersion = 2; bytes32 structHash = policy.computeStructHash(unsupportedVersion, blockContentHash, 0); bytes32 digest = policy.getHashedTypeDataV4(structHash); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(mock7b91.privateKey, digest); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(mock46f6.privateKey, digest); bytes memory signature = abi.encodePacked(r, s, v); vm.expectRevert(abi.encodeWithSelector(BlockBuilderPolicy.UnsupportedVersion.selector, unsupportedVersion)); diff --git a/test/FlashtestationRegistry.t.sol b/test/FlashtestationRegistry.t.sol index 6cb8e77..af9e3cb 100644 --- a/test/FlashtestationRegistry.t.sol +++ b/test/FlashtestationRegistry.t.sol @@ -8,7 +8,7 @@ import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {FlashtestationRegistry} from "../src/FlashtestationRegistry.sol"; import {IFlashtestationRegistry} from "../src/interfaces/IFlashtestationRegistry.sol"; -import {QuoteParser, WorkloadId} from "../src/utils/QuoteParser.sol"; +import {QuoteParser} from "../src/utils/QuoteParser.sol"; import {MockAutomataDcapAttestationFee} from "./mocks/MockAutomataDcapAttestationFee.sol"; import {Helper} from "./helpers/Helper.sol"; import {Upgrader} from "./helpers/Upgrader.sol"; @@ -20,10 +20,9 @@ import {Output} from "automata-dcap-attestation/contracts/types/CommonStruct.sol struct MockQuote { bytes output; bytes quote; + bytes extData; address teeAddress; - bytes publicKey; uint256 privateKey; - WorkloadId workloadId; } contract FlashtestationRegistryTest is Test { @@ -31,58 +30,35 @@ contract FlashtestationRegistryTest is Test { FlashtestationRegistry public registry; Upgrader public upgrader = new Upgrader(); MockAutomataDcapAttestationFee public attestationContract; - MockQuote bf42Mock = MockQuote({ - output: vm.readFileBinary( - "test/raw_tdx_quotes/bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446/output.bin" - ), - quote: vm.readFileBinary( - "test/raw_tdx_quotes/bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446/quote.bin" - ), - publicKey: hex"bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446", + MockQuote mockf200 = MockQuote({ + output: vm.readFileBinary("test/raw_tdx_quotes/0xf200f222043C5bC6c70AA6e35f5C5FDe079F3a03/output.bin"), + quote: vm.readFileBinary("test/raw_tdx_quotes/0xf200f222043C5bC6c70AA6e35f5C5FDe079F3a03/quote.bin"), + extData: bytes(""), teeAddress: 0xf200f222043C5bC6c70AA6e35f5C5FDe079F3a03, - workloadId: WorkloadId.wrap(0xeee0d5f864e6d46d6da790c7d60baac5c8478eb89e86667336d3f17655e9164e), privateKey: 0x0000000000000000000000000000000000000000000000000000000000000000 // unused for this mock }); - MockQuote bf42MockWithDifferentWorkloadId = MockQuote({ - output: vm.readFileBinary( - "test/raw_tdx_quotes/bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446/output2.bin" - ), - quote: vm.readFileBinary( - "test/raw_tdx_quotes/bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446/quote2.bin" - ), - publicKey: hex"bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446", - teeAddress: 0xf200f222043C5bC6c70AA6e35f5C5FDe079F3a03, - workloadId: WorkloadId.wrap(0x5e6be81f9e5b10d15a6fa69b19ab0269cd943db39fa1f0d38a76eb76146948cb), + MockQuote mockc200 = MockQuote({ // non-empty ext data + output: vm.readFileBinary("test/raw_tdx_quotes/0xc200F222043C5BC6c70aA6e35f5c5fDE079f3A04/output.bin"), + quote: vm.readFileBinary("test/raw_tdx_quotes/0xc200F222043C5BC6c70aA6e35f5c5fDE079f3A04/quote.bin"), + extData: bytes("xxxxxxxx"), + teeAddress: 0xc200F222043C5BC6c70aA6e35f5c5fDE079f3A04, privateKey: 0x0000000000000000000000000000000000000000000000000000000000000000 // unused for this mock }); - MockQuote d204Mock = MockQuote({ - output: vm.readFileBinary( - "test/raw_tdx_quotes/0xd204547069c53f9ecff9b30494eb9797615a2f46aa2785db6258104cebb92d48ff4dc0744c36d8470646f4813e61f9a831ffb54b937f7b233f32d271434ccca6/output.bin" - ), - quote: vm.readFileBinary( - "test/raw_tdx_quotes/0xd204547069c53f9ecff9b30494eb9797615a2f46aa2785db6258104cebb92d48ff4dc0744c36d8470646f4813e61f9a831ffb54b937f7b233f32d271434ccca6/quote.bin" - ), - publicKey: hex"d204547069c53f9ecff9b30494eb9797615a2f46aa2785db6258104cebb92d48ff4dc0744c36d8470646f4813e61f9a831ffb54b937f7b233f32d271434ccca6", + MockQuote mock12c1 = MockQuote({ + output: vm.readFileBinary("test/raw_tdx_quotes/0x12c14e56d585Dcf3B36f37476c00E78bA9363742/output.bin"), + quote: vm.readFileBinary("test/raw_tdx_quotes/0x12c14e56d585Dcf3B36f37476c00E78bA9363742/quote.bin"), + extData: bytes(""), teeAddress: 0x12c14e56d585Dcf3B36f37476c00E78bA9363742, - workloadId: WorkloadId.wrap(0xeee0d5f864e6d46d6da790c7d60baac5c8478eb89e86667336d3f17655e9164e), privateKey: 0x0000000000000000000000000000000000000000000000000000000000000000 // unused for this mock }); - MockQuote mock7b91 = MockQuote({ - output: vm.readFileBinary( - "test/raw_tdx_quotes/7b916d70ed77488d6c1ced7117ba410655a8faa8d6c7740562a88ab3cb9cbca63e2d5761812a11d90c009ed017113131370070cd3a2d5fba64d9dbb76952df19/output.bin" - ), - quote: vm.readFileBinary( - "test/raw_tdx_quotes/7b916d70ed77488d6c1ced7117ba410655a8faa8d6c7740562a88ab3cb9cbca63e2d5761812a11d90c009ed017113131370070cd3a2d5fba64d9dbb76952df19/quote.bin" - ), - publicKey: hex"7b916d70ed77488d6c1ced7117ba410655a8faa8d6c7740562a88ab3cb9cbca63e2d5761812a11d90c009ed017113131370070cd3a2d5fba64d9dbb76952df19", + MockQuote mock46f6 = MockQuote({ + output: vm.readFileBinary("test/raw_tdx_quotes/0x46f6b3ACF1dD8Ac0085e30192741336c4aF6EdAF/output.bin"), + quote: vm.readFileBinary("test/raw_tdx_quotes/0x46f6b3ACF1dD8Ac0085e30192741336c4aF6EdAF/quote.bin"), + extData: bytes(""), teeAddress: 0x46f6b3ACF1dD8Ac0085e30192741336c4aF6EdAF, - workloadId: WorkloadId.wrap(0xeee0d5f864e6d46d6da790c7d60baac5c8478eb89e86667336d3f17655e9164e), privateKey: 0x92e4b5ed61db615b26da2271da5b47c42d691b3164561cfb4edbc85ca6ca61a8 }); - // this is some random workloadId that is not the same as the one in the mock quotes - WorkloadId wrongWorkloadId = WorkloadId.wrap(0x20ab431377d40de192f7c754ac0f1922de05ab2f73e74204f0b3ab73a8856876); - using ECDSA for bytes32; function setUp() public { @@ -98,158 +74,99 @@ contract FlashtestationRegistryTest is Test { function test_successful_registerTEEService() public { // first get a valid attestation quote stored - bytes memory mockOutput = bf42Mock.output; - bytes memory mockQuote = bf42Mock.quote; - address expectedAddress = bf42Mock.teeAddress; - WorkloadId expectedWorkloadId = bf42Mock.workloadId; + bytes memory mockOutput = mockf200.output; + bytes memory mockQuote = mockf200.quote; + address expectedAddress = mockf200.teeAddress; // set the attestation contract to return a successful attestation attestationContract.setQuoteResult(mockQuote, true, mockOutput); vm.expectEmit(address(registry)); - emit IFlashtestationRegistry.TEEServiceRegistered( - expectedAddress, expectedWorkloadId, mockQuote, bf42Mock.publicKey, false - ); + emit IFlashtestationRegistry.TEEServiceRegistered(expectedAddress, mockQuote, false); vm.prank(expectedAddress); - registry.registerTEEService(mockQuote); + registry.registerTEEService(mockQuote, mockf200.extData); + + // Get the registration + (bool isValid, IFlashtestationRegistry.RegisteredTEE memory registration) = + registry.getRegistration(expectedAddress); - (WorkloadId workloadId, bytes memory rawQuote, bool isValid, bytes memory publicKey) = - registry.registeredTEEs(expectedAddress); vm.assertEq(isValid, true, "TEE should be valid"); - vm.assertEq(rawQuote, mockQuote, "Raw quote mismatch"); - vm.assertEq(WorkloadId.unwrap(workloadId), WorkloadId.unwrap(expectedWorkloadId), "Workload ID mismatch"); - vm.assertEq(publicKey, bf42Mock.publicKey, "Public key mismatch"); + vm.assertEq(registration.rawQuote, mockQuote, "Raw quote mismatch"); + vm.assertEq(registration.extendedRegistrationData, mockf200.extData, "Extended data mismatch"); + vm.assertEq(registration.isValid, true, "Registration should be valid"); } // test that we can register the same TEEService again with a different quote function test_successful_re_registerTEEService() public { // do the first register of the TEEService with a valid quote - bytes memory mockOutput = d204Mock.output; - bytes memory mockQuote = d204Mock.quote; - address expectedAddress = d204Mock.teeAddress; - WorkloadId expectedWorkloadId = d204Mock.workloadId; + bytes memory mockOutput = mock12c1.output; + bytes memory mockQuote = mock12c1.quote; + address expectedAddress = mock12c1.teeAddress; attestationContract.setQuoteResult(mockQuote, true, mockOutput); vm.expectEmit(address(registry)); - emit IFlashtestationRegistry.TEEServiceRegistered( - expectedAddress, expectedWorkloadId, mockQuote, d204Mock.publicKey, false - ); + emit IFlashtestationRegistry.TEEServiceRegistered(expectedAddress, mockQuote, false); vm.prank(expectedAddress); - registry.registerTEEService(mockQuote); + registry.registerTEEService(mockQuote, mock12c1.extData); - (WorkloadId workloadId, bytes memory rawQuote, bool isValid, bytes memory publicKey) = - registry.registeredTEEs(expectedAddress); + (bool isValid, IFlashtestationRegistry.RegisteredTEE memory registration) = + registry.getRegistration(expectedAddress); vm.assertEq(isValid, true, "TEE should be valid"); - vm.assertEq(rawQuote, mockQuote, "Raw quote mismatch"); - vm.assertEq(publicKey, d204Mock.publicKey, "Public key mismatch"); - vm.assertEq(WorkloadId.unwrap(workloadId), WorkloadId.unwrap(expectedWorkloadId), "Workload ID mismatch"); + vm.assertEq(registration.rawQuote, mockQuote, "Raw quote mismatch"); // now register the same TEEService again with a different quote - bytes memory mockQuote2 = vm.readFileBinary( - "test/raw_tdx_quotes/0xd204547069c53f9ecff9b30494eb9797615a2f46aa2785db6258104cebb92d48ff4dc0744c36d8470646f4813e61f9a831ffb54b937f7b233f32d271434ccca6/quote2.bin" - ); - bytes memory mockOutput2 = vm.readFileBinary( - "test/raw_tdx_quotes/0xd204547069c53f9ecff9b30494eb9797615a2f46aa2785db6258104cebb92d48ff4dc0744c36d8470646f4813e61f9a831ffb54b937f7b233f32d271434ccca6/output2.bin" - ); + bytes memory mockQuote2 = + vm.readFileBinary("test/raw_tdx_quotes/0x12c14e56d585Dcf3B36f37476c00E78bA9363742/quote2.bin"); + bytes memory mockOutput2 = + vm.readFileBinary("test/raw_tdx_quotes/0x12c14e56d585Dcf3B36f37476c00E78bA9363742/output2.bin"); attestationContract.setQuoteResult(mockQuote2, true, mockOutput2); vm.expectEmit(address(registry)); - emit IFlashtestationRegistry.TEEServiceRegistered( - expectedAddress, expectedWorkloadId, mockQuote2, d204Mock.publicKey, true - ); + emit IFlashtestationRegistry.TEEServiceRegistered(expectedAddress, mockQuote2, true); vm.prank(expectedAddress); - registry.registerTEEService(mockQuote2); + registry.registerTEEService(mockQuote2, mock12c1.extData); - (WorkloadId workloadId2, bytes memory rawQuote2, bool isValid2, bytes memory publicKey2) = - registry.registeredTEEs(expectedAddress); + (bool isValid2, IFlashtestationRegistry.RegisteredTEE memory registration2) = + registry.getRegistration(expectedAddress); vm.assertEq(isValid2, true, "TEE should be valid"); - vm.assertEq(WorkloadId.unwrap(workloadId2), WorkloadId.unwrap(expectedWorkloadId), "Workload ID mismatch"); - vm.assertEq(rawQuote2, mockQuote2, "Raw quote mismatch"); - vm.assertEq(publicKey2, d204Mock.publicKey, "Public key mismatch"); - vm.assertNotEq(mockQuote, mockQuote2, "Quotes should not be the same"); - } - - function test_successful_re_registerTEEService_with_different_workload_ids() public { - bytes memory mockOutput = bf42Mock.output; - bytes memory mockQuote = bf42Mock.quote; - bytes memory expectedPublicKey = bf42Mock.publicKey; - address expectedAddress = bf42Mock.teeAddress; - WorkloadId expectedWorkloadId = bf42Mock.workloadId; - - // set the attestation contract to return a successful attestation - attestationContract.setQuoteResult(mockQuote, true, mockOutput); - - vm.expectEmit(address(registry)); - emit IFlashtestationRegistry.TEEServiceRegistered( - expectedAddress, expectedWorkloadId, mockQuote, expectedPublicKey, false - ); - vm.prank(expectedAddress); - registry.registerTEEService(mockQuote); - - (WorkloadId workloadId, bytes memory rawQuote, bool isValid, bytes memory publicKey) = - registry.registeredTEEs(expectedAddress); - vm.assertEq(isValid, true, "TEE should be valid"); - vm.assertEq(rawQuote, mockQuote, "Raw quote mismatch"); - vm.assertEq(WorkloadId.unwrap(workloadId), WorkloadId.unwrap(expectedWorkloadId), "Workload ID mismatch"); - vm.assertEq(publicKey, expectedPublicKey, "Public key mismatch"); - - // now register the same TEE-contraolled address again with a different quote and workloadId - - bytes memory mockOutput2 = bf42MockWithDifferentWorkloadId.output; - bytes memory mockQuote2 = bf42MockWithDifferentWorkloadId.quote; - expectedPublicKey = bf42MockWithDifferentWorkloadId.publicKey; - expectedWorkloadId = bf42MockWithDifferentWorkloadId.workloadId; - attestationContract.setQuoteResult(mockQuote2, true, mockOutput2); - - vm.expectEmit(address(registry)); - emit IFlashtestationRegistry.TEEServiceRegistered( - expectedAddress, expectedWorkloadId, mockQuote2, expectedPublicKey, true - ); - vm.prank(expectedAddress); - registry.registerTEEService(mockQuote2); - - (WorkloadId workloadId2, bytes memory rawQuote2, bool isValid2, bytes memory publicKey2) = - registry.registeredTEEs(expectedAddress); - vm.assertEq(isValid2, true, "TEE should be valid"); - vm.assertEq(WorkloadId.unwrap(workloadId2), WorkloadId.unwrap(expectedWorkloadId), "Workload ID mismatch"); - vm.assertEq(rawQuote2, mockQuote2, "Raw quote mismatch"); - vm.assertEq(publicKey2, expectedPublicKey, "Public key mismatch"); + vm.assertEq(registration2.rawQuote, mockQuote2, "Raw quote mismatch"); + vm.assertEq(registration2.extendedRegistrationData, mock12c1.extData, "Extended data mismatch"); vm.assertNotEq(mockQuote, mockQuote2, "Quotes should not be the same"); } function test_reverts_with_invalid_quote_registerTEEService() public { - bytes memory mockQuote = bf42Mock.quote; + bytes memory mockQuote = mockf200.quote; attestationContract.setQuoteResult(mockQuote, false, new bytes(0)); vm.expectPartialRevert(IFlashtestationRegistry.InvalidQuote.selector); // the "partial" just means we don't care about the bytes argument to InvalidQuote(bytes) - registry.registerTEEService(mockQuote); + registry.registerTEEService(mockQuote, mockf200.extData); } function test_reverts_with_registering_same_quote_twice() public { - bytes memory mockOutput = bf42Mock.output; - bytes memory mockQuote = bf42Mock.quote; - address expectedAddress = bf42Mock.teeAddress; - WorkloadId expectedWorkloadId = bf42Mock.workloadId; + bytes memory mockOutput = mockf200.output; + bytes memory mockQuote = mockf200.quote; + address expectedAddress = mockf200.teeAddress; + bytes32 expectedQuoteHash = keccak256(mockQuote); attestationContract.setQuoteResult(mockQuote, true, mockOutput); vm.prank(expectedAddress); - registry.registerTEEService(mockQuote); + registry.registerTEEService(mockQuote, mockf200.extData); vm.expectRevert( abi.encodeWithSelector( - IFlashtestationRegistry.TEEServiceAlreadyRegistered.selector, expectedAddress, expectedWorkloadId + IFlashtestationRegistry.TEEServiceAlreadyRegistered.selector, expectedAddress, expectedQuoteHash ) ); vm.prank(expectedAddress); - registry.registerTEEService(mockQuote); + registry.registerTEEService(mockQuote, mockf200.extData); } function test_reverts_with_invalid_quote_version() public { - bytes memory mockOutput = bf42Mock.output; - bytes memory mockQuote = bf42Mock.quote; + bytes memory mockOutput = mockf200.output; + bytes memory mockQuote = mockf200.quote; Output memory output = Helper.deserializeOutput(mockOutput); output.quoteVersion = 0x0000; @@ -257,12 +174,12 @@ contract FlashtestationRegistryTest is Test { attestationContract.setQuoteResult(mockQuote, true, serializedOutput); vm.expectRevert(QuoteParser.InvalidTEEVersion.selector, 0); - registry.registerTEEService(mockQuote); + registry.registerTEEService(mockQuote, mockf200.extData); } function test_reverts_with_invalid_tee_type() public { - bytes memory mockOutput = bf42Mock.output; - bytes memory mockQuote = bf42Mock.quote; + bytes memory mockOutput = mockf200.output; + bytes memory mockQuote = mockf200.quote; Output memory output = Helper.deserializeOutput(mockOutput); output.tee = bytes4(0x00000000); @@ -270,22 +187,22 @@ contract FlashtestationRegistryTest is Test { attestationContract.setQuoteResult(mockQuote, true, serializedOutput); vm.expectRevert(QuoteParser.InvalidTEEType.selector, 0); - registry.registerTEEService(mockQuote); + registry.registerTEEService(mockQuote, mockf200.extData); } function test_reverts_with_too_large_quote() public { - bytes memory mockQuote = bf42Mock.quote; + bytes memory mockQuote = mockf200.quote; // take a 4.9K file and concatenate it 5 times to make it over the 20KB limit bytes memory tooLargeQuote = abi.encodePacked(mockQuote, mockQuote, mockQuote, mockQuote, mockQuote); vm.expectRevert(abi.encodeWithSelector(IFlashtestationRegistry.ByteSizeExceeded.selector, tooLargeQuote.length)); - registry.registerTEEService(tooLargeQuote); + registry.registerTEEService(tooLargeQuote, mockf200.extData); } function test_reverts_when_sender_does_not_match_tee_address() public { - bytes memory mockOutput = bf42Mock.output; - bytes memory mockQuote = bf42Mock.quote; - address expectedAddress = bf42Mock.teeAddress; + bytes memory mockOutput = mockf200.output; + bytes memory mockQuote = mockf200.quote; + address expectedAddress = mockf200.teeAddress; attestationContract.setQuoteResult(mockQuote, true, mockOutput); @@ -297,73 +214,55 @@ contract FlashtestationRegistryTest is Test { IFlashtestationRegistry.SenderMustMatchTEEAddress.selector, differentAddress, expectedAddress ) ); - registry.registerTEEService(mockQuote); + registry.registerTEEService(mockQuote, mockf200.extData); } - function test_isValidWorkload_returns_true_for_valid_combination() public { + function test_getRegistration_returns_true_for_valid_registration() public { // First register a valid TEE - bytes memory mockOutput = bf42Mock.output; - bytes memory mockQuote = bf42Mock.quote; - address expectedAddress = bf42Mock.teeAddress; - WorkloadId expectedWorkloadId = bf42Mock.workloadId; + bytes memory mockOutput = mockf200.output; + bytes memory mockQuote = mockf200.quote; + address expectedAddress = mockf200.teeAddress; attestationContract.setQuoteResult(mockQuote, true, mockOutput); vm.prank(expectedAddress); - registry.registerTEEService(mockQuote); + registry.registerTEEService(mockQuote, mockf200.extData); - // Now check that isValidWorkload returns true for this combination - bool isValid = registry.isValidWorkload(expectedWorkloadId, expectedAddress); - assertTrue(isValid, "isValidWorkload should return true for valid TEE/workloadId combination"); + // Now check that getRegistration returns valid data + (bool isValid, IFlashtestationRegistry.RegisteredTEE memory registration) = + registry.getRegistration(expectedAddress); + assertTrue(isValid, "getRegistration should return true for valid TEE"); + assertEq(registration.rawQuote, mockQuote, "Quote should match"); } - function test_isValidWorkload_returns_false_for_invalid_tee() public { + function test_getRegistration_returns_false_for_invalid_tee() public { // First register a valid TEE - bytes memory mockOutput = bf42Mock.output; - bytes memory mockQuote = bf42Mock.quote; - address expectedAddress = bf42Mock.teeAddress; - WorkloadId expectedWorkloadId = bf42Mock.workloadId; + bytes memory mockOutput = mockf200.output; + bytes memory mockQuote = mockf200.quote; + address expectedAddress = mockf200.teeAddress; attestationContract.setQuoteResult(mockQuote, true, mockOutput); vm.prank(expectedAddress); - registry.registerTEEService(mockQuote); + registry.registerTEEService(mockQuote, mockf200.extData); // Now invalidate the TEE attestationContract.setQuoteResult(mockQuote, false, new bytes(0)); registry.invalidateAttestation(expectedAddress); - // Now check that isValidWorkload returns false for this combination - bool isValid = registry.isValidWorkload(expectedWorkloadId, expectedAddress); - assertFalse(isValid, "isValidWorkload should return false for invalid TEE"); - - // also make sure isValidWorkload returns false for a different workloadId - isValid = registry.isValidWorkload(wrongWorkloadId, expectedAddress); - assertFalse(isValid, "isValidWorkload should return false for invalid TEE"); + // Now check that getRegistration returns false for isValid + (bool isValid,) = registry.getRegistration(expectedAddress); + assertFalse(isValid, "getRegistration should return false for invalid TEE"); } - function test_isValidWorkload_returns_false_for_unregistered_tee() public view { + function test_getRegistration_returns_false_for_unregistered_tee() public view { // Use an address that hasn't been registered address unregisteredAddress = address(0xdead); - bool isValid = registry.isValidWorkload(wrongWorkloadId, unregisteredAddress); - assertFalse(isValid, "isValidWorkload should return false for unregistered TEE address"); - } - - function test_isValidWorkload_returns_false_for_wrong_workloadId() public { - // First register a valid TEE - bytes memory mockOutput = bf42Mock.output; - bytes memory mockQuote = bf42Mock.quote; - address expectedAddress = bf42Mock.teeAddress; - - attestationContract.setQuoteResult(mockQuote, true, mockOutput); - - vm.prank(expectedAddress); - registry.registerTEEService(mockQuote); - - // Now check with a different workloadId - bool isValid = registry.isValidWorkload(wrongWorkloadId, expectedAddress); - assertFalse(isValid, "isValidWorkload should return false for wrong workloadId"); + (bool isValid, IFlashtestationRegistry.RegisteredTEE memory registration) = + registry.getRegistration(unregisteredAddress); + assertFalse(isValid, "getRegistration should return false for unregistered TEE address"); + assertEq(registration.rawQuote.length, 0, "Raw quote should be empty"); } function test_invalidateAttestation_reverts_if_not_registered() public { @@ -376,12 +275,12 @@ contract FlashtestationRegistryTest is Test { function test_invalidateAttestation_reverts_if_already_invalid() public { // Register a valid TEE - bytes memory mockOutput = bf42Mock.output; - bytes memory mockQuote = bf42Mock.quote; - address teeAddress = bf42Mock.teeAddress; + bytes memory mockOutput = mockf200.output; + bytes memory mockQuote = mockf200.quote; + address teeAddress = mockf200.teeAddress; attestationContract.setQuoteResult(mockQuote, true, mockOutput); vm.prank(teeAddress); - registry.registerTEEService(mockQuote); + registry.registerTEEService(mockQuote, mockf200.extData); // Now, invalidate with success==false (should invalidate) attestationContract.setQuoteResult(mockQuote, false, new bytes(0)); @@ -395,51 +294,137 @@ contract FlashtestationRegistryTest is Test { function test_invalidateAttestation_reverts_if_still_valid() public { // Register a valid TEE - bytes memory mockOutput = bf42Mock.output; - bytes memory mockQuote = bf42Mock.quote; - address teeAddress = bf42Mock.teeAddress; + bytes memory mockOutput = mockf200.output; + bytes memory mockQuote = mockf200.quote; + address teeAddress = mockf200.teeAddress; + bytes32 quoteHash = keccak256(mockQuote); attestationContract.setQuoteResult(mockQuote, true, mockOutput); vm.prank(teeAddress); - registry.registerTEEService(mockQuote); + registry.registerTEEService(mockQuote, mockf200.extData); // Now, invalidate with success==true (still valid) attestationContract.setQuoteResult(mockQuote, true, mockOutput); - vm.expectRevert(abi.encodeWithSelector(IFlashtestationRegistry.TEEIsStillValid.selector, teeAddress)); + vm.expectRevert(abi.encodeWithSelector(IFlashtestationRegistry.TEEIsStillValid.selector, teeAddress, quoteHash)); registry.invalidateAttestation(teeAddress); } function test_invalidateAttestation_invalidates_and_emits_event() public { // Register a valid TEE - bytes memory mockOutput = bf42Mock.output; - bytes memory mockQuote = bf42Mock.quote; - address teeAddress = bf42Mock.teeAddress; + bytes memory mockOutput = mockf200.output; + bytes memory mockQuote = mockf200.quote; + address teeAddress = mockf200.teeAddress; attestationContract.setQuoteResult(mockQuote, true, mockOutput); vm.prank(teeAddress); - registry.registerTEEService(mockQuote); + registry.registerTEEService(mockQuote, mockf200.extData); // Now, invalidate with success==false (should invalidate) attestationContract.setQuoteResult(mockQuote, false, new bytes(0)); vm.expectEmit(address(registry)); emit IFlashtestationRegistry.TEEServiceInvalidated(teeAddress); registry.invalidateAttestation(teeAddress); // Check isValid is now false - (,, bool isValid,) = registry.registeredTEEs(teeAddress); + (bool isValid,) = registry.getRegistration(teeAddress); assertFalse(isValid, "TEE should be invalid after invalidate"); } - function test_parseV4Quote_parses_valid_quote() public { - // Register a valid TEE - bytes memory mockOutput = bf42Mock.output; - bytes memory mockQuote = bf42Mock.quote; - address teeAddress = bf42Mock.teeAddress; + function test_extended_registration_data_validation() public { + // Test that extended registration data hash must match reportData[20:52] + bytes memory mockOutput = mockc200.output; + bytes memory mockQuote = mockc200.quote; + address teeAddress = mockc200.teeAddress; + + // Create extended data that doesn't match the hash in reportData[20:52] + bytes memory invalidExtendedData = abi.encode("wrong data"); + attestationContract.setQuoteResult(mockQuote, true, mockOutput); + vm.prank(teeAddress); - registry.registerTEEService(mockQuote); + vm.expectRevert( + abi.encodeWithSelector( + IFlashtestationRegistry.InvalidRegistrationDataHash.selector, + keccak256(mockc200.extData), + keccak256(invalidExtendedData) + ) + ); + registry.registerTEEService(mockQuote, invalidExtendedData); + } + + function test_parsedReportBody_stored_correctly() public { + bytes memory mockOutput = mockf200.output; + bytes memory mockQuote = mockf200.quote; + address teeAddress = mockf200.teeAddress; + + // Parse expected report body + TD10ReportBody memory expectedReportBody = QuoteParser.parseV4VerifierOutput(mockOutput); + + attestationContract.setQuoteResult(mockQuote, true, mockOutput); + + vm.prank(teeAddress); + registry.registerTEEService(mockQuote, mockf200.extData); + + // Get stored report body + (bool isValid, IFlashtestationRegistry.RegisteredTEE memory registration) = registry.getRegistration(teeAddress); + + assertTrue(isValid); + + // Verify key fields are stored correctly + assertEq(registration.parsedReportBody.mrTd, expectedReportBody.mrTd, "mrTd mismatch"); + assertEq(registration.parsedReportBody.mrConfigId, expectedReportBody.mrConfigId, "mrConfigId mismatch"); + assertEq(registration.parsedReportBody.tdAttributes, expectedReportBody.tdAttributes, "tdAttributes mismatch"); + assertEq(registration.parsedReportBody.xFAM, expectedReportBody.xFAM, "xFAM mismatch"); + } + + function test_data_extracted_from_reportData() public { + // This test verifies that the TEE address is properly extracted from reportData[0:20] + bytes memory mockOutput = mockc200.output; + bytes memory mockQuote = mockc200.quote; - // Should not revert and should return a TD10ReportBody - TD10ReportBody memory report = registry.getReportBody(teeAddress); + // Parse the output to verify the expected address + TD10ReportBody memory reportBody = QuoteParser.parseV4VerifierOutput(mockOutput); + (address extractedAddress, bytes32 extractedExtDataHash) = QuoteParser.parseReportData(reportBody.reportData); - assertEq(report.reportData.length, 64, "reportData should be 64 bytes"); - assertEq(report.reportData, bf42Mock.publicKey, "reportData should match the public key"); + address expectedAddress = mockc200.teeAddress; + assertEq(extractedAddress, expectedAddress, "Address should be extracted from reportData[0:20]"); + + require(keccak256(mockc200.extData) == extractedExtDataHash, "Test data mismatch"); + + attestationContract.setQuoteResult(mockQuote, true, mockOutput); + + // Can only register from the address in the quote + vm.prank(expectedAddress); + registry.registerTEEService(mockQuote, mockc200.extData); + + // Verify it was registered to the correct address + (bool isValid, IFlashtestationRegistry.RegisteredTEE memory registration) = + registry.getRegistration(expectedAddress); + assertTrue(isValid); + assertEq(registration.extendedRegistrationData, mockc200.extData); + } + + function test_extDataMismatch() public { + // This test verifies that using a mismatched extendedRegistrationData will result in an error + bytes memory mockOutput = mockc200.output; + bytes memory mockQuote = mockc200.quote; + + // Parse the output to verify the expected address + TD10ReportBody memory reportBody = QuoteParser.parseV4VerifierOutput(mockOutput); + (address extractedAddress, bytes32 extractedExtDataHash) = QuoteParser.parseReportData(reportBody.reportData); + assertEq(extractedExtDataHash, keccak256(mockc200.extData)); + + address expectedAddress = 0xc200F222043C5BC6c70aA6e35f5c5fDE079f3A04; + assertEq(extractedAddress, expectedAddress, "Address should be extracted from reportData[0:20]"); + + attestationContract.setQuoteResult(mockQuote, true, mockOutput); + + // keccak hash of extendedRegistrationData must match the 32 byte hash in the reportData + vm.prank(expectedAddress); + vm.expectRevert( + abi.encodeWithSelector( + IFlashtestationRegistry.InvalidRegistrationDataHash.selector, + keccak256(mockc200.extData), + keccak256(bytes("xxxx")) + ) + ); + registry.registerTEEService(mockQuote, bytes("xxxx")); } /// @dev we need the comment below because QuoteParser.parseV4Quote() is internal @@ -447,7 +432,7 @@ contract FlashtestationRegistryTest is Test { /// forge-config: default.allow_internal_expect_revert = true function test_parseV4Quote_reverts_on_invalid_length() public { // Use a quote of invalid length (e.g., one byte too short) - bytes memory validRawQuote = bf42Mock.quote; + bytes memory validRawQuote = mockf200.quote; bytes memory shortQuote = new bytes(TD_REPORT10_LENGTH + HEADER_LENGTH - 1); for (uint256 i = 0; i < shortQuote.length; i++) { shortQuote[i] = validRawQuote[i]; @@ -479,89 +464,87 @@ contract FlashtestationRegistryTest is Test { function test_successful_permitRegisterTEEService() public { // First get a valid attestation quote stored - bytes memory mockOutput = mock7b91.output; - bytes memory mockQuote = mock7b91.quote; - address expectedAddress = mock7b91.teeAddress; - WorkloadId expectedWorkloadId = mock7b91.workloadId; + bytes memory mockOutput = mock46f6.output; + bytes memory mockQuote = mock46f6.quote; + address expectedAddress = mock46f6.teeAddress; // Set the attestation contract to return a successful attestation attestationContract.setQuoteResult(mockQuote, true, mockOutput); // Create the EIP-712 signature - bytes32 structHash = registry.computeStructHash(mockQuote, 0); + bytes32 structHash = registry.computeStructHash(mockQuote, mock46f6.extData, 0); bytes32 digest = registry.hashTypedDataV4(structHash); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(mock7b91.privateKey, digest); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(mock46f6.privateKey, digest); bytes memory signature = abi.encodePacked(r, s, v); // Register the TEE vm.expectEmit(address(registry)); - emit IFlashtestationRegistry.TEEServiceRegistered( - expectedAddress, expectedWorkloadId, mockQuote, mock7b91.publicKey, false - ); + emit IFlashtestationRegistry.TEEServiceRegistered(expectedAddress, mockQuote, false); // the caller here is unspecified (i.e. no vm.prank), so if it succeeds // it means any address can call this function (assuming they have the correct signature) - registry.permitRegisterTEEService(mockQuote, 0, signature); + registry.permitRegisterTEEService(mockQuote, mock46f6.extData, 0, signature); - (WorkloadId workloadId, bytes memory rawQuote, bool isValid, bytes memory publicKey) = - registry.registeredTEEs(expectedAddress); + (bool isValid, IFlashtestationRegistry.RegisteredTEE memory registration) = + registry.getRegistration(expectedAddress); vm.assertEq(isValid, true, "TEE should be valid"); - vm.assertEq(rawQuote, mockQuote, "Raw quote mismatch"); - vm.assertEq(WorkloadId.unwrap(workloadId), WorkloadId.unwrap(expectedWorkloadId), "Workload ID mismatch"); - vm.assertEq(publicKey, mock7b91.publicKey, "Public key mismatch"); + vm.assertEq(registration.rawQuote, mockQuote, "Raw quote mismatch"); + vm.assertEq(registration.extendedRegistrationData, mock46f6.extData, "Extended data mismatch"); vm.assertEq(registry.nonces(expectedAddress), 1, "Nonce should be incremented"); } function test_permitRegisterTEEService_reverts_with_invalid_signature() public { - bytes memory mockOutput = mock7b91.output; - bytes memory mockQuote = mock7b91.quote; + bytes memory mockOutput = mock46f6.output; + bytes memory mockQuote = mock46f6.quote; (, uint256 invalid_pk) = makeAddrAndKey("invalid_signer"); attestationContract.setQuoteResult(mockQuote, true, mockOutput); - // Create the EIP-712 signature with wrong private key (i.e. 0x1) - bytes32 structHash = registry.computeStructHash(mockQuote, 0); + // Create the EIP-712 signature with wrong private key + bytes32 structHash = registry.computeStructHash(mockQuote, mock46f6.extData, 0); bytes32 digest = registry.hashTypedDataV4(structHash); (uint8 v, bytes32 r, bytes32 s) = vm.sign(invalid_pk, digest); bytes memory signature = abi.encodePacked(r, s, v); - vm.expectRevert(IFlashtestationRegistry.InvalidSignature.selector); - registry.permitRegisterTEEService(mockQuote, 0, signature); + // The function doesn't revert with InvalidSignature anymore, it reverts during + // the registration process when checking caller vs teeAddress + vm.expectRevert(); + registry.permitRegisterTEEService(mockQuote, mock46f6.extData, 0, signature); } function test_permitRegisterTEEService_reverts_with_invalid_nonce() public { - bytes memory mockOutput = mock7b91.output; - bytes memory mockQuote = mock7b91.quote; + bytes memory mockOutput = mock46f6.output; + bytes memory mockQuote = mock46f6.quote; attestationContract.setQuoteResult(mockQuote, true, mockOutput); // Create the EIP-712 signature - bytes32 structHash = registry.computeStructHash(mockQuote, 1); // wrong nonce + bytes32 structHash = registry.computeStructHash(mockQuote, mock46f6.extData, 1); // wrong nonce bytes32 digest = registry.hashTypedDataV4(structHash); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(mock7b91.privateKey, digest); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(mock46f6.privateKey, digest); bytes memory signature = abi.encodePacked(r, s, v); vm.expectRevert(abi.encodeWithSelector(IFlashtestationRegistry.InvalidNonce.selector, 0, 1)); - registry.permitRegisterTEEService(mockQuote, 1, signature); + registry.permitRegisterTEEService(mockQuote, mock46f6.extData, 1, signature); } function test_permitRegisterTEEService_reverts_with_replayed_signature() public { - bytes memory mockOutput = mock7b91.output; - bytes memory mockQuote = mock7b91.quote; + bytes memory mockOutput = mock46f6.output; + bytes memory mockQuote = mock46f6.quote; attestationContract.setQuoteResult(mockQuote, true, mockOutput); // Create the EIP-712 signature - bytes32 structHash = registry.computeStructHash(mockQuote, 0); + bytes32 structHash = registry.computeStructHash(mockQuote, mock46f6.extData, 0); bytes32 digest = registry.hashTypedDataV4(structHash); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(mock7b91.privateKey, digest); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(mock46f6.privateKey, digest); bytes memory signature = abi.encodePacked(r, s, v); // First registration should succeed - registry.permitRegisterTEEService(mockQuote, 0, signature); + registry.permitRegisterTEEService(mockQuote, mock46f6.extData, 0, signature); // Try to replay the same signature vm.expectRevert(abi.encodeWithSelector(IFlashtestationRegistry.InvalidNonce.selector, 1, 0)); - registry.permitRegisterTEEService(mockQuote, 0, signature); + registry.permitRegisterTEEService(mockQuote, mock46f6.extData, 0, signature); } } diff --git a/test/raw_tdx_quotes/0xd204547069c53f9ecff9b30494eb9797615a2f46aa2785db6258104cebb92d48ff4dc0744c36d8470646f4813e61f9a831ffb54b937f7b233f32d271434ccca6/output.bin b/test/raw_tdx_quotes/0x12c14e56d585Dcf3B36f37476c00E78bA9363742/output.bin similarity index 82% rename from test/raw_tdx_quotes/0xd204547069c53f9ecff9b30494eb9797615a2f46aa2785db6258104cebb92d48ff4dc0744c36d8470646f4813e61f9a831ffb54b937f7b233f32d271434ccca6/output.bin rename to test/raw_tdx_quotes/0x12c14e56d585Dcf3B36f37476c00E78bA9363742/output.bin index 11ae16e..7106f09 100644 Binary files a/test/raw_tdx_quotes/0xd204547069c53f9ecff9b30494eb9797615a2f46aa2785db6258104cebb92d48ff4dc0744c36d8470646f4813e61f9a831ffb54b937f7b233f32d271434ccca6/output.bin and b/test/raw_tdx_quotes/0x12c14e56d585Dcf3B36f37476c00E78bA9363742/output.bin differ diff --git a/test/raw_tdx_quotes/0xd204547069c53f9ecff9b30494eb9797615a2f46aa2785db6258104cebb92d48ff4dc0744c36d8470646f4813e61f9a831ffb54b937f7b233f32d271434ccca6/output2.bin b/test/raw_tdx_quotes/0x12c14e56d585Dcf3B36f37476c00E78bA9363742/output2.bin similarity index 82% rename from test/raw_tdx_quotes/0xd204547069c53f9ecff9b30494eb9797615a2f46aa2785db6258104cebb92d48ff4dc0744c36d8470646f4813e61f9a831ffb54b937f7b233f32d271434ccca6/output2.bin rename to test/raw_tdx_quotes/0x12c14e56d585Dcf3B36f37476c00E78bA9363742/output2.bin index 11ae16e..7106f09 100644 Binary files a/test/raw_tdx_quotes/0xd204547069c53f9ecff9b30494eb9797615a2f46aa2785db6258104cebb92d48ff4dc0744c36d8470646f4813e61f9a831ffb54b937f7b233f32d271434ccca6/output2.bin and b/test/raw_tdx_quotes/0x12c14e56d585Dcf3B36f37476c00E78bA9363742/output2.bin differ diff --git a/test/raw_tdx_quotes/bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446/quote.bin b/test/raw_tdx_quotes/0x12c14e56d585Dcf3B36f37476c00E78bA9363742/quote.bin similarity index 91% rename from test/raw_tdx_quotes/bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446/quote.bin rename to test/raw_tdx_quotes/0x12c14e56d585Dcf3B36f37476c00E78bA9363742/quote.bin index 3d66859..f1a6ed6 100644 Binary files a/test/raw_tdx_quotes/bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446/quote.bin and b/test/raw_tdx_quotes/0x12c14e56d585Dcf3B36f37476c00E78bA9363742/quote.bin differ diff --git a/test/raw_tdx_quotes/0xd204547069c53f9ecff9b30494eb9797615a2f46aa2785db6258104cebb92d48ff4dc0744c36d8470646f4813e61f9a831ffb54b937f7b233f32d271434ccca6/quote2.bin b/test/raw_tdx_quotes/0x12c14e56d585Dcf3B36f37476c00E78bA9363742/quote2.bin similarity index 93% rename from test/raw_tdx_quotes/0xd204547069c53f9ecff9b30494eb9797615a2f46aa2785db6258104cebb92d48ff4dc0744c36d8470646f4813e61f9a831ffb54b937f7b233f32d271434ccca6/quote2.bin rename to test/raw_tdx_quotes/0x12c14e56d585Dcf3B36f37476c00E78bA9363742/quote2.bin index f8f9721..9d68841 100644 Binary files a/test/raw_tdx_quotes/0xd204547069c53f9ecff9b30494eb9797615a2f46aa2785db6258104cebb92d48ff4dc0744c36d8470646f4813e61f9a831ffb54b937f7b233f32d271434ccca6/quote2.bin and b/test/raw_tdx_quotes/0x12c14e56d585Dcf3B36f37476c00E78bA9363742/quote2.bin differ diff --git a/test/raw_tdx_quotes/bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446/output.bin b/test/raw_tdx_quotes/0x46f6b3ACF1dD8Ac0085e30192741336c4aF6EdAF/output.bin similarity index 82% rename from test/raw_tdx_quotes/bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446/output.bin rename to test/raw_tdx_quotes/0x46f6b3ACF1dD8Ac0085e30192741336c4aF6EdAF/output.bin index a6733d3..4189d1c 100644 Binary files a/test/raw_tdx_quotes/bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446/output.bin and b/test/raw_tdx_quotes/0x46f6b3ACF1dD8Ac0085e30192741336c4aF6EdAF/output.bin differ diff --git a/test/raw_tdx_quotes/7b916d70ed77488d6c1ced7117ba410655a8faa8d6c7740562a88ab3cb9cbca63e2d5761812a11d90c009ed017113131370070cd3a2d5fba64d9dbb76952df19/quote.bin b/test/raw_tdx_quotes/0x46f6b3ACF1dD8Ac0085e30192741336c4aF6EdAF/quote.bin similarity index 91% rename from test/raw_tdx_quotes/7b916d70ed77488d6c1ced7117ba410655a8faa8d6c7740562a88ab3cb9cbca63e2d5761812a11d90c009ed017113131370070cd3a2d5fba64d9dbb76952df19/quote.bin rename to test/raw_tdx_quotes/0x46f6b3ACF1dD8Ac0085e30192741336c4aF6EdAF/quote.bin index 70ca556..e40a1c0 100644 Binary files a/test/raw_tdx_quotes/7b916d70ed77488d6c1ced7117ba410655a8faa8d6c7740562a88ab3cb9cbca63e2d5761812a11d90c009ed017113131370070cd3a2d5fba64d9dbb76952df19/quote.bin and b/test/raw_tdx_quotes/0x46f6b3ACF1dD8Ac0085e30192741336c4aF6EdAF/quote.bin differ diff --git a/test/raw_tdx_quotes/0xc200F222043C5BC6c70aA6e35f5c5fDE079f3A04/output.bin b/test/raw_tdx_quotes/0xc200F222043C5BC6c70aA6e35f5c5fDE079f3A04/output.bin new file mode 100644 index 0000000..4c6c3f9 Binary files /dev/null and b/test/raw_tdx_quotes/0xc200F222043C5BC6c70aA6e35f5c5fDE079f3A04/output.bin differ diff --git a/test/raw_tdx_quotes/0xc200F222043C5BC6c70aA6e35f5c5fDE079f3A04/quote.bin b/test/raw_tdx_quotes/0xc200F222043C5BC6c70aA6e35f5c5fDE079f3A04/quote.bin new file mode 100644 index 0000000..48c5051 Binary files /dev/null and b/test/raw_tdx_quotes/0xc200F222043C5BC6c70aA6e35f5c5fDE079f3A04/quote.bin differ diff --git a/test/raw_tdx_quotes/0xf200f222043C5bC6c70AA6e35f5C5FDe079F3a03/output.bin b/test/raw_tdx_quotes/0xf200f222043C5bC6c70AA6e35f5C5FDe079F3a03/output.bin new file mode 100644 index 0000000..76a99b6 Binary files /dev/null and b/test/raw_tdx_quotes/0xf200f222043C5bC6c70AA6e35f5C5FDe079F3a03/output.bin differ diff --git a/test/raw_tdx_quotes/0xf200f222043C5bC6c70AA6e35f5C5FDe079F3a03/output2.bin b/test/raw_tdx_quotes/0xf200f222043C5bC6c70AA6e35f5C5FDe079F3a03/output2.bin new file mode 100644 index 0000000..1304601 Binary files /dev/null and b/test/raw_tdx_quotes/0xf200f222043C5bC6c70AA6e35f5C5FDe079F3a03/output2.bin differ diff --git a/test/raw_tdx_quotes/0xf200f222043C5bC6c70AA6e35f5C5FDe079F3a03/quote.bin b/test/raw_tdx_quotes/0xf200f222043C5bC6c70AA6e35f5C5FDe079F3a03/quote.bin new file mode 100644 index 0000000..9e79b6a Binary files /dev/null and b/test/raw_tdx_quotes/0xf200f222043C5bC6c70AA6e35f5C5FDe079F3a03/quote.bin differ diff --git a/test/raw_tdx_quotes/0xf200f222043C5bC6c70AA6e35f5C5FDe079F3a03/quote2.bin b/test/raw_tdx_quotes/0xf200f222043C5bC6c70AA6e35f5C5FDe079F3a03/quote2.bin new file mode 100644 index 0000000..6c4dfa7 Binary files /dev/null and b/test/raw_tdx_quotes/0xf200f222043C5bC6c70AA6e35f5C5FDe079F3a03/quote2.bin differ diff --git a/test/raw_tdx_quotes/README.md b/test/raw_tdx_quotes/README.md index bdaa70f..1af6b58 100644 --- a/test/raw_tdx_quotes/README.md +++ b/test/raw_tdx_quotes/README.md @@ -4,11 +4,11 @@ This directory contains TDX quote and serialized Output values that we use for t ## File Structure -Each directory within `raw_tdx_quotes/` is named by the 64-byte hex uncompressed public key that was used for the `TD10ReportBody.reportData` field. Each of these public key directories contains a - +Each directory within `raw_tdx_quotes/` is named by the tee address. Each of these directories contains a - `quote.bin`: which is a TDX quote binary file whose `TD10ReportBody.reportData` is the public key named by its parent directory. - `output.bin`: which is the [Output](https://github.com/automata-network/automata-dcap-attestation/blob/evm-v1.0.0/evm/contracts/types/CommonStruct.sol#L113) returned by `AutomataDcapAttestationFee.verifyAndAttestOnChain` when called on the Ethereum Sepolia network ### Helpers -- `hex2bin.py`: a simple python script for writing string hex data (such as `0xdeadbeef`) in its binary form to a file. We use this to take the hex string of the serialized [Output](https://github.com/automata-network/automata-dcap-attestation/blob/evm-v1.0.0/evm/contracts/types/CommonStruct.sol#L113) emitted by the `AttestationEntrypointBase.AttestationSubmitted` event, which you place in `quote_event.hex` non-0x-prefixed, and write it in to `output.bin`, so we can use it to mock return values in our tests for different TDX quotes \ No newline at end of file +- `../../scripts/MockQuotes.s.sol`: a set of tools for quotes, including generation and validation of mock quotes for tests. +- `hex2bin.py`: a simple python script for writing string hex data (such as `0xdeadbeef`) in its binary form to a file. We use this to take the hex string of the serialized [Output](https://github.com/automata-network/automata-dcap-attestation/blob/evm-v1.0.0/evm/contracts/types/CommonStruct.sol#L113) emitted by the `AttestationEntrypointBase.AttestationSubmitted` event, which you place in `quote_event.hex` non-0x-prefixed, and write it in to `output.bin`, so we can use it to mock return values in our tests for different TDX quotes diff --git a/test/raw_tdx_quotes/bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446/output2.bin b/test/raw_tdx_quotes/bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446/output2.bin deleted file mode 100644 index fca3c97..0000000 Binary files a/test/raw_tdx_quotes/bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446/output2.bin and /dev/null differ diff --git a/test/raw_tdx_quotes/bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446/quote2.bin b/test/raw_tdx_quotes/bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446/quote2.bin deleted file mode 100644 index cc3b5c1..0000000 Binary files a/test/raw_tdx_quotes/bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446/quote2.bin and /dev/null differ