Skip to content

Conversation

@Ruteri
Copy link
Contributor

@Ruteri Ruteri commented Jul 9, 2025

Working out some ideas in practice. I'll suggest changes to the spec to match before merging.

Copy link
Collaborator

@Melvillian Melvillian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See inline comments.

Like you said, this is a draft and still needs some more work on it.

Here's my understanding of this PR:

The overall goal of this is to make flashtestations more flexible, by allowing the user to both define their own definition of “valid” by encoding the WorkloadId however they wish, and by extending the amount of data that can be attested to by the TEE beyond just the 64 bytes of reportData

  1. Moving Workload Id out of registry and into blockbuilding, which allows the definition of workloadID to be policy-specific.
  2. adding appData which allows arbitrary data to be attested to by the TEE via a 32-byte app data hash embeded in the reportData
  3. adding ValidateTDConfig to do some additional security checks on those MR values

Here's some remaining questions I have:

  1. What other changes do you want to make? We have 2 weeks until code freeze, and these changes are going to take 1-2 weeks to get updated and then to have Sarah + Ferran verify them.
  2. Why was ValidateTDConfig added? What do these xFAM and tdAttribute mask checks get us that we didn’t have before?
  3. Are the public keys understood to be in the appData?

README.md Outdated
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 derives WorkloadId from the stored report body
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

readability: it's not clear why this step is important for "Verify Flashtestation Transaction", can you extend this step to explain how the WorkloadId gets used?

* @param appData The extended attested to data
*/
function registerTEEService(bytes calldata rawQuote) external limitBytesSize(rawQuote) nonReentrant {
function registerTEEService(bytes calldata rawQuote, bytes calldata appData)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

readability: this comment should explain why and how we use the app data hash to bind the TEE-attested reportData to the appData. Otherwise it's confusing why this data isn't just included in the rawQuote and why the cryptographi binding is necessary.

Copy link
Collaborator

@Melvillian Melvillian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs tests to be fixed and additional tests for the new functionality

  • extendedRegistratioData verification
  • BlockBuilderPolicy isAllowedPolicy checks
  • TDConfigParams-related tests

See inline comments as well.

I'm not OK with the centralized xfam + tdAttributes changes as is because I think makes liveness depend too much on an honest centralized owner. See my suggested fix, and if you don't think that'll work I think we remove it.

// Events
event TEEServiceRegistered(
address teeAddress, WorkloadId workloadId, bytes rawQuote, bytes publicKey, bool alreadyExists
event TEEServiceRegistered( /* dev: could be hash of the quote */
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove, it's no longer needed

Copy link
Contributor Author

@Ruteri Ruteri Jul 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, are you sure? I'd imagine the event itself is still helpful. I don't mind removing it if you think it's not helping.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haha wait sorry definitely keep the event, I just meant the comment (sorry, should have been more specific)

bytes memory rawQuote = registeredTEEs[teeAddress].rawQuote;
require(rawQuote.length > 0, TEEServiceNotRegistered(teeAddress));
return QuoteParser.parseV4Quote(rawQuote);
function setTDConfigParams(bytes8 xfamMask, bytes8 tdAttributesMask) external onlyOwner {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like how this is an onlyOwner function, which means it is trivial to affect the liveness of the FlashtestationsRegistry; the owner sets the masks to 0xFFFFFFFFFFFFFFFF and then getRegistration will always fail because validateTdConfig always fails.

But, we need this here so that we can prevent attesting to reports with insecure xfam and tdAttribute values.

What if we refactor this function so that in registerTEEService the user passes in their desired xfamMask and tdAttributesMask, and we set that in the RegisteredTEE struct. However, if they pass in a 0x0 for either of xfam or tdAttributes, then we set our default tdConfigParams, which is only set by onlyOwner. Now there is no way for the owner to unilaterally shut down the protocol by updating the TCConfigParams to a malicious value.

When a TCB level becomes outdated or insecure, then the user can call registerTEEService again with a different quote, and the RegisteredTEE will be securely updated.

tl;dr: I think that making the liveness of FlashtestationsRegistry dependent on a centralized owner acting honestly is too much centralization, and we need to find another way around it. Yes, the owner has god-powers by being able to upgrade the contract, but the goal is for that to go away once there's product-market fit and bugs have been squashed. Allowing this liveness centralization is a problem that we should not introduce at the beginning unless we have a clear path for removing it later on (which we don't).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed on the registry owner not being a good candidate for owning the masks, good callout.
I think passing the masks to validation would work, however something even simpler could be masking out the bits when creating the workload id — I think it's the cleanest way to do it, see if you like it too.

Copy link
Collaborator

@Melvillian Melvillian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my inline comments, but I'm approving so that we can merge this.

// the outputs in future scripts (like Interactions.s.sol:AddWorkloadToPolicyScript)
address sender = vm.getWallets()[0];
(WorkloadId workloadId,,, bytes memory publicKey) = registry.registeredTEEs(sender);
(,, bytes32 quoteHash,,) = registry.registeredTEEs(sender);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove, this isn't stored anymore in registeredTEEs

/// @param blockContentHash The hash of the block content
/// @dev This function is internal because it is only used by the permitVerifyBlockBuilderProof function
/// and it is not needed to be called by other contracts
/// @dev We can't check the whole block content hash, but we could check the block number and parent hash
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

readability: you're right, but it feels strange to keep this comment here, because it feels more like a comment you'd leave in a PR review, rather than in the code itself.

For instance, imagine a user unfamiliar with the code reading this, the natural response to this question is "why didn't the creators check the block number and parent hash? Is something wrong this this implementation?"

I think we should remove it to reduce confusion.

emit TEEServiceRegistered(teeAddress, workloadId, rawQuote, publicKey, previouslyRegistered);
function registerTEEService(bytes calldata rawQuote, bytes calldata extendedRegistrationData)
external
limitBytesSize(rawQuote)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of doing limitBytesSize here, let's just add it to doRegister so it is called for both registerTEEService and permitRegisterTEEService.

We missed adding the limitBytesSize(extendedRegistrationData) here, so let's protect against this problem by locating the logic in one place.

bytes calldata extendedRegistrationData,
uint256 nonce,
bytes calldata signature
) external limitBytesSize(rawQuote) limitBytesSize(extendedRegistrationData) nonReentrant {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see my comment above about limitBytesSize

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't ever explain or give examples of what extendedRegistrationData could be, so this seems like a good place to do that.

What do we foresee this being used for? Some examples I can see are including the publicKey of the teeAddress that registered the TEE, including the operatorId` for extra security against phyical TEE attacks.

Please add a comment explaining this. Something like:

// 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 all concatenated together. This registration data can be anything that's needed for your app, for instance an operatorIdPublicKey that allows you to verify signatures from your TEE operator.

@Melvillian Melvillian marked this pull request as ready for review July 22, 2025 18:30
@Ruteri Ruteri changed the title [not canonical] Moves workloadId to builder policy, adjusts registry to accept extended user data Moves workloadId to builder policy, adjusts registry to accept extended registration data Jul 23, 2025
Copy link
Collaborator

@Melvillian Melvillian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my inline comments, but besides those there is one final group of tests I'd like you to add; tests for the xfam and tdAttributes bitmasking logic. In particular, the tests should validate the following 2 invariants (these come from the BlockBuilderPolicy.sol comments:

// We expect fpu and sse xfam bits to be set, and anything else should be handled by explicitly allowing the workloadid
        bytes8 xfamExpectedBits = (fullBitmask ^ (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 tdAttributesBitmask = (fullBitmask ^ (TD_TDATTRS_VE_DISABLED | TD_TDATTRS_PKS | TD_TDATTRS_KL));

To test these, you can read in the quote data from one of the raw_tdx_quotes, tweak the necessary bits of the parsedReportBody that correspond to parsedReportBody.xFAM and parsedReportBodytdAttributes, and verify that all of the permutations of xfam/tdAttributes bit values uphold the 2 invariants above.

Oh also, see this:

this doc link still needs to be updated: https://github.com/flashbots/flashtestations/pull/21/files#r2216670103. You marked it as resolved but I don't think it's resolved.

}

function test_extDataMismatch() public {
// This test verifies that the TEE address is properly extracted from reportData[0:20]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this comment is copypasta from the test above, it should say

`// This test verifies that using a mismatched extendedRegistrationData will result in an error


attestationContract.setQuoteResult(mockQuote, true, mockOutput);

// Can only register from the address in the quote
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same thing here, should say

`// keccak hash of extendedRegistrationData must match the 32 byte hash in the reportData

// We expect fpu and sse xfam bits to be set, and anything else should be handled by explicitly allowing the workloadid
bytes8 xfamExpectedBits = (fullBitmask ^ (TD_XFAM_FPU | TD_XFAM_SSE));

// We don't mind ve disabled and pks tdattributes bits being set either way, anything else requires explicitly allowing the workloadid
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

your comment only includes 2 of the attributes, but the code line below includes 3 (VE_DISABLED, PKS, and KL). Should the comment be updated or the code? Either way, please make the code and comment align, and be correct.

@Melvillian Melvillian merged commit c304f5c into flashbots:main Jul 23, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants