diff --git a/.gitignore b/.gitignore index 4d3b9e83..2b103152 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,5 @@ build/ tests/systemtests/binaries tests/systemtests/testnet .testnets + +local_tee_workflow \ No newline at end of file diff --git a/contracts/solidity/TEEInferenceVerifier.sol b/contracts/solidity/TEEInferenceVerifier.sol index 6692dd92..71e3e56c 100644 --- a/contracts/solidity/TEEInferenceVerifier.sol +++ b/contracts/solidity/TEEInferenceVerifier.sol @@ -98,8 +98,8 @@ contract TEEInferenceVerifier is AccessControl { uint256 timestamp, bytes calldata signature ) public view returns (bool) { - // 1. TEE must be active in the registry - if (!registry.isActive(teeId)) return false; + // 1. TEE must be enabled in the registry + if (!registry.isTEEEnabled(teeId)) return false; // 2. Timestamp bounds uint256 minTs = block.timestamp > MAX_INFERENCE_AGE @@ -109,7 +109,7 @@ contract TEEInferenceVerifier is AccessControl { if (timestamp < minTs || timestamp > maxTs) return false; // 3. Cryptographic verification - bytes memory pubKey = registry.getPublicKey(teeId); + bytes memory pubKey = registry.getTEEPublicKey(teeId); bytes32 msgHash = computeMessageHash(inputHash, outputHash, timestamp); return VERIFIER.verifyRSAPSS(pubKey, msgHash, signature); } diff --git a/contracts/solidity/TEERegistry.json b/contracts/solidity/TEERegistry.json index ca2e525b..bdd2b66f 100644 --- a/contracts/solidity/TEERegistry.json +++ b/contracts/solidity/TEERegistry.json @@ -66,11 +66,6 @@ "name": "PCRAlreadyExists", "type": "error" }, - { - "inputs": [], - "name": "PCRExpired", - "type": "error" - }, { "inputs": [], "name": "PCRNotApproved", @@ -88,7 +83,7 @@ }, { "inputs": [], - "name": "TEENotActive", + "name": "TEENotEnabled", "type": "error" }, { @@ -152,12 +147,6 @@ "internalType": "bytes32", "name": "pcrHash", "type": "bytes32" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "gracePeriod", - "type": "uint256" } ], "name": "PCRRevoked", @@ -248,7 +237,7 @@ "type": "bytes32" } ], - "name": "TEEActivated", + "name": "TEEEnabled", "type": "event" }, { @@ -261,7 +250,7 @@ "type": "bytes32" } ], - "name": "TEEDeactivated", + "name": "TEEDisabled", "type": "event" }, { @@ -308,19 +297,6 @@ "name": "TEETypeAdded", "type": "event" }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint8", - "name": "typeId", - "type": "uint8" - } - ], - "name": "TEETypeDeactivated", - "type": "event" - }, { "inputs": [], "name": "DEFAULT_ADMIN_ROLE", @@ -368,7 +344,7 @@ "type": "bytes32" } ], - "name": "activateTEE", + "name": "enableTEE", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -439,11 +415,11 @@ "type": "bytes32" } ], - "name": "approvedPCRs", + "name": "pcrRecords", "outputs": [ { "internalType": "bool", - "name": "active", + "name": "approved", "type": "bool" }, { @@ -456,11 +432,6 @@ "name": "approvedAt", "type": "uint256" }, - { - "internalType": "uint256", - "name": "expiresAt", - "type": "uint256" - }, { "internalType": "string", "name": "version", @@ -575,27 +546,14 @@ "type": "bytes32" } ], - "name": "deactivateTEE", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "typeId", - "type": "uint8" - } - ], - "name": "deactivateTEEType", + "name": "disableTEE", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], - "name": "getActivePCRs", + "name": "getApprovedPCRs", "outputs": [ { "internalType": "bytes32[]", @@ -646,7 +604,7 @@ "type": "bytes32" } ], - "name": "getPublicKey", + "name": "getTEEPublicKey", "outputs": [ { "internalType": "bytes", @@ -725,7 +683,7 @@ }, { "internalType": "bool", - "name": "active", + "name": "enabled", "type": "bool" }, { @@ -735,7 +693,7 @@ }, { "internalType": "uint256", - "name": "lastUpdatedAt", + "name": "lastHeartbeatAt", "type": "uint256" } ], @@ -763,11 +721,6 @@ "name": "name", "type": "string" }, - { - "internalType": "bool", - "name": "active", - "type": "bool" - }, { "internalType": "uint256", "name": "addedAt", @@ -889,7 +842,7 @@ "type": "bytes32" } ], - "name": "isActive", + "name": "isTEEEnabled", "outputs": [ { "internalType": "bool", @@ -1008,9 +961,9 @@ "type": "bytes32" }, { - "internalType": "uint256", - "name": "gracePeriod", - "type": "uint256" + "internalType": "uint8", + "name": "teeType", + "type": "uint8" } ], "name": "revokePCR", @@ -1083,11 +1036,6 @@ "name": "name", "type": "string" }, - { - "internalType": "bool", - "name": "active", - "type": "bool" - }, { "internalType": "uint256", "name": "addedAt", @@ -1144,7 +1092,7 @@ }, { "internalType": "bool", - "name": "active", + "name": "enabled", "type": "bool" }, { @@ -1154,7 +1102,7 @@ }, { "internalType": "uint256", - "name": "lastUpdatedAt", + "name": "lastHeartbeatAt", "type": "uint256" } ], diff --git a/contracts/solidity/TEERegistry.sol b/contracts/solidity/TEERegistry.sol index 4af90f05..1eecd123 100644 --- a/contracts/solidity/TEERegistry.sol +++ b/contracts/solidity/TEERegistry.sol @@ -6,10 +6,27 @@ import "@openzeppelin/contracts/access/AccessControl.sol"; /// @title TEERegistry - TEE Registration and Management /// @notice On-chain registry for Trusted Execution Environment (TEE) nodes that provide -/// verifiable AI inference. Manages the full TEE lifecycle: registration, activation, +/// verifiable AI inference. Manages the full TEE lifecycle: registration, enabling, /// heartbeat liveness, and decommissioning. /// -/// @dev ## Overall Flow +/// @dev ## Chain of Trust +/// +/// The registry establishes a hardware-rooted chain of trust from AWS Nitro hardware +/// all the way to the client connection: +/// +/// AWS Nitro Hardware (root of trust) +/// → signs attestation document +/// → precompile verifies against stored AWS root certificate +/// → extracts PCR (enclave code identity) + binds signing key & TLS cert +/// → contract checks PCR is admin-approved for this TEE type +/// → heartbeats prove ongoing liveness via the bound signing key +/// → clients pin TLS cert to verify they're talking to the real enclave +/// +/// **Key binding** is the critical property: the TEE's signing key and TLS certificate +/// are included in the attestation document at enclave boot time, so the on-chain record +/// is cryptographically tied to a specific enclave instance running approved code. +/// +/// ## Overall Flow /// /// 1. **Admin setup** — An admin adds TEE types (e.g. LLM inference, agent execution) via `addTEEType`, then /// approves known-good enclave measurements via `approvePCR`. The AWS root certificate @@ -19,38 +36,36 @@ import "@openzeppelin/contracts/access/AccessControl.sol"; /// a. Verifies the attestation document against the AWS root cert via the 0x900 /// precompile (`ITEEVerifier`). /// b. Extracts PCR measurements and checks they match an admin-approved set. -/// c. Stores the TEE as **active** and indexes it by type and owner. +/// c. Binds the TEE's signing key and TLS certificate to the verified enclave identity. +/// d. Stores the TEE as **enabled** and indexes it by type and owner. /// /// 3. **Heartbeat** — Each TEE periodically proves liveness by submitting a signed /// timestamp via `heartbeat`. The RSA-PSS signature is verified on-chain against the /// TEE's stored public key. Stale or future timestamps are rejected. /// -/// 4. **Deactivation / Reactivation** — TEE owners or admins can toggle a TEE's active -/// status. `activateTEE` re-validates the TEE's PCR before reactivating. +/// 4. **Disable / Enable** — TEE owners or admins can toggle a TEE's enabled +/// status. `enableTEE` re-validates the TEE's PCR before re-enabling. /// /// 5. **PCR revocation** — When an enclave image is compromised or outdated, admins revoke -/// its PCR via `revokePCR` (optionally with a grace period). TEEs running the revoked -/// image are caught lazily: `activateTEE` and `heartbeat` both call -/// `_requirePCRValidForTEE` and will revert once the PCR expires. -/// -/// 6. **Removal** — `removeTEE` permanently deletes a TEE from all storage and indexes. +/// its PCR via `revokePCR`. All enabled TEEs running the revoked image are immediately +/// disabled. `enableTEE` also validates the PCR to prevent re-enabling with a revoked image. /// /// ## Querying TEE Status /// /// The contract exposes three tiers of TEE queries with increasing strictness: -/// - `getTEEsByType` — all TEEs ever registered for a type (active + inactive). -/// - `getActivatedTEEs` — only TEEs in the active list (no heartbeat/PCR check). -/// - `getLiveTEEs` — active TEEs with a valid PCR **and** a fresh heartbeat. +/// - `getTEEsByType` — all TEEs ever registered for a type (enabled + disabled). +/// - `getEnabledTEEs` — only TEEs in the enabled list (no heartbeat/PCR check). +/// - `getActiveTEEs` — enabled TEEs with a valid PCR **and** a fresh heartbeat. /// /// ## Client Integration Guide /// /// **Choosing a query method:** /// -/// - `getLiveTEEs(teeType)` — **Recommended for most clients.** Returns only TEEs -/// that are active, running approved (non-revoked) enclave code, and have sent a +/// - `getActiveTEEs(teeType)` — **Recommended for most clients.** Returns only TEEs +/// that are enabled, running approved (non-revoked) enclave code, and have sent a /// recent heartbeat. These are fully verified and ready to serve requests. /// -/// - `getActivatedTEEs(teeType)` — Returns TEEs that are in the active list but +/// - `getEnabledTEEs(teeType)` — Returns TEEs that are in the enabled list but /// does **not** check heartbeat freshness or PCR validity. Use this if you want /// to perform your own filtering logic off-chain (e.g. custom staleness /// thresholds, geographic selection, or load-balancing across TEEs that may have @@ -58,7 +73,7 @@ import "@openzeppelin/contracts/access/AccessControl.sol"; /// PCR status yourself. /// /// - `getTEEsByType(teeType)` — Returns all TEEs ever registered for a type, -/// including inactive ones. Useful for dashboards, auditing, or historical views. +/// including disabled ones. Useful for dashboards, auditing, or historical views. /// Not suitable for selecting a TEE to connect to. /// /// **TLS certificate verification:** @@ -68,7 +83,7 @@ import "@openzeppelin/contracts/access/AccessControl.sol"; /// This certificate was bound to the enclave at registration time via attestation /// verification. Without this check, a compromised or spoofed endpoint could /// impersonate a registered TEE. The recommended flow is: -/// 1. Query the registry for a live TEE (e.g. via `getLiveTEEs`). +/// 1. Query the registry for a healthy TEE (e.g. via `getActiveTEEs`). /// 2. Open a TLS connection to the TEE's `endpoint`. /// 3. Compare the server's presented certificate against `TEEInfo.tlsCertificate`. /// 4. Abort the connection if they do not match. @@ -76,16 +91,16 @@ import "@openzeppelin/contracts/access/AccessControl.sol"; /// ## Access Control /// /// - `DEFAULT_ADMIN_ROLE` — manages TEE types, PCRs, certificates, heartbeat config. -/// - `TEE_OPERATOR` — registers TEEs, manages owned TEEs (deactivate/activate/remove). +/// - `TEE_OPERATOR` — registers TEEs, manages owned TEEs (disable/enable/remove). contract TEERegistry is AccessControl { - + // ============ Constants ============ bytes32 public constant TEE_OPERATOR = keccak256("TEE_OPERATOR"); ITEEVerifier public constant VERIFIER = ITEEVerifier(0x0000000000000000000000000000000000000900); // ============ Structs ============ - + struct PCRMeasurements { bytes pcr0; bytes pcr1; @@ -93,16 +108,14 @@ contract TEERegistry is AccessControl { } struct ApprovedPCR { - bool active; + bool approved; uint8 teeType; uint256 approvedAt; - uint256 expiresAt; string version; } struct TEETypeInfo { string name; - bool active; uint256 addedAt; } @@ -114,58 +127,55 @@ contract TEERegistry is AccessControl { bytes tlsCertificate; bytes32 pcrHash; uint8 teeType; - bool active; + bool enabled; uint256 registeredAt; - uint256 lastUpdatedAt; + uint256 lastHeartbeatAt; } // ============ Storage ============ + // AWS Root Certificate + bytes public awsRootCertificate; + + // Heartbeat: max allowed age of the signed timestamp vs block.timestamp. + uint256 public heartbeatMaxAge = 1800; // 30 minutes default + // TEE Types mapping(uint8 => TEETypeInfo) public teeTypes; uint8[] private _teeTypeList; - + // PCR Registry: teeType => pcrHash => ApprovedPCR - mapping(uint8 => mapping(bytes32 => ApprovedPCR)) public approvedPCRs; + mapping(uint8 => mapping(bytes32 => ApprovedPCR)) public pcrRecords; struct PCRKey { bytes32 pcrHash; uint8 teeType; } PCRKey[] private _pcrList; - - // AWS Root Certificate - bytes public awsRootCertificate; - - // Heartbeat: max allowed age of the signed timestamp vs block.timestamp. - uint256 public heartbeatMaxAge = 1800; // 30 minutes default // All TEEs mapping(bytes32 => TEEInfo) public tees; - // Active TEEs by type: teeType => list of active teeIds - mapping(uint8 => bytes32[]) private _activeTEEList; - // teeType => teeId => index in _activeTEEList[teeType] - mapping(uint8 => mapping(bytes32 => uint256)) private _activeTEEIndex; + // Enabled TEEs by type: teeType => list of enabled teeIds + mapping(uint8 => bytes32[]) private _enabledTEEList; + // teeType => teeId => index in _enabledTEEList[teeType] + mapping(uint8 => mapping(bytes32 => uint256)) private _enabledTEEIndex; - // All TEEs by type (active + inactive) + // All TEEs by type (enabled + disabled) mapping(uint8 => bytes32[]) internal _teesByType; - // TEEs by owner mapping(address => bytes32[]) internal _teesByOwner; // ============ Events ============ event TEETypeAdded(uint8 indexed typeId, string name); - event TEETypeDeactivated(uint8 indexed typeId); event PCRApproved(bytes32 indexed pcrHash, uint8 indexed teeType, string version); - event PCRRevoked(bytes32 indexed pcrHash, uint256 gracePeriod); + event PCRRevoked(bytes32 indexed pcrHash, uint8 indexed teeType); event TEERegistered(bytes32 indexed teeId, address indexed owner, uint8 teeType); - event TEEDeactivated(bytes32 indexed teeId); - event TEEActivated(bytes32 indexed teeId); + event TEEDisabled(bytes32 indexed teeId); + event TEEEnabled(bytes32 indexed teeId); event AWSCertificateUpdated(bytes32 indexed certHash); event HeartbeatReceived(bytes32 indexed teeId, uint256 timestamp); - event TEERemoved(bytes32 indexed teeId); // ============ Errors ============ @@ -173,11 +183,10 @@ contract TEERegistry is AccessControl { error TEETypeNotFound(); error InvalidTEEType(); error PCRNotApproved(); - error PCRExpired(); error PCRAlreadyExists(); error TEEAlreadyExists(); error TEENotFound(); - error TEENotActive(); + error TEENotEnabled(); error NotTEEOwner(); error AttestationInvalid(string reason); error KeyBindingFailed(string reason); @@ -189,8 +198,9 @@ contract TEERegistry is AccessControl { modifier onlyTEEOwnerOrAdmin(bytes32 teeId) { if (tees[teeId].registeredAt == 0) revert TEENotFound(); - if (tees[teeId].owner != msg.sender && !hasRole(DEFAULT_ADMIN_ROLE, msg.sender)) revert NotTEEOwner(); - if (!hasRole(TEE_OPERATOR, msg.sender) && !hasRole(DEFAULT_ADMIN_ROLE, msg.sender)) revert NotTEEOwner(); + bool isAdmin = hasRole(DEFAULT_ADMIN_ROLE, msg.sender); + bool isOwnerOperator = tees[teeId].owner == msg.sender && hasRole(TEE_OPERATOR, msg.sender); + if (!isAdmin && !isOwnerOperator) revert NotTEEOwner(); _; } @@ -202,27 +212,27 @@ contract TEERegistry is AccessControl { _setRoleAdmin(TEE_OPERATOR, DEFAULT_ADMIN_ROLE); } + // ============ Certificate Management ============ + + function setAWSRootCertificate(bytes calldata certificate) external onlyRole(DEFAULT_ADMIN_ROLE) { + awsRootCertificate = certificate; + emit AWSCertificateUpdated(keccak256(certificate)); + } + // ============ TEE Type Management ============ - + function addTEEType(uint8 typeId, string calldata name) external onlyRole(DEFAULT_ADMIN_ROLE) { if (teeTypes[typeId].addedAt != 0) revert TEETypeExists(); teeTypes[typeId] = TEETypeInfo({ name: name, - active: true, addedAt: block.timestamp }); _teeTypeList.push(typeId); emit TEETypeAdded(typeId, name); } - function deactivateTEEType(uint8 typeId) external onlyRole(DEFAULT_ADMIN_ROLE) { - if (teeTypes[typeId].addedAt == 0) revert TEETypeNotFound(); - teeTypes[typeId].active = false; - emit TEETypeDeactivated(typeId); - } - function isValidTEEType(uint8 typeId) public view returns (bool) { - return teeTypes[typeId].active; + return teeTypes[typeId].addedAt != 0; } function getTEETypes() external view returns (uint8[] memory typeIds, TEETypeInfo[] memory infos) { @@ -234,7 +244,7 @@ contract TEERegistry is AccessControl { } // ============ PCR Management ============ - + /// @notice Approve a new PCR measurement for a specific TEE type /// @param pcrs The PCR measurements (pcr0, pcr1, pcr2) /// @param version Human-readable version string (e.g., "v1.2.0") @@ -247,13 +257,12 @@ contract TEERegistry is AccessControl { if (!isValidTEEType(teeType)) revert InvalidTEEType(); bytes32 pcrHash = computePCRHash(pcrs); - bool isNew = approvedPCRs[teeType][pcrHash].approvedAt == 0; + bool isNew = pcrRecords[teeType][pcrHash].approvedAt == 0; - approvedPCRs[teeType][pcrHash] = ApprovedPCR({ - active: true, + pcrRecords[teeType][pcrHash] = ApprovedPCR({ + approved: true, teeType: teeType, approvedAt: block.timestamp, - expiresAt: 0, version: version }); @@ -264,37 +273,39 @@ contract TEERegistry is AccessControl { emit PCRApproved(pcrHash, teeType, version); } - /// @notice Revoke a PCR, either immediately or with a grace period - /// @dev TEEs using this PCR are caught lazily at activateTEE() and heartbeat() + /// @notice Revoke a PCR and immediately disable all TEEs running it /// @param pcrHash The PCR hash to revoke - /// @param gracePeriod Seconds until revocation takes effect (0 = immediate) - function revokePCR(bytes32 pcrHash, uint8 teeType, uint256 gracePeriod) external onlyRole(DEFAULT_ADMIN_ROLE) { + /// @param teeType The TEE type this PCR belongs to + function revokePCR(bytes32 pcrHash, uint8 teeType) external onlyRole(DEFAULT_ADMIN_ROLE) { if (!isPCRApproved(teeType, pcrHash)) revert PCRNotApproved(); - if (gracePeriod == 0) { - approvedPCRs[teeType][pcrHash].active = false; - } else { - approvedPCRs[teeType][pcrHash].expiresAt = block.timestamp + gracePeriod; + pcrRecords[teeType][pcrHash].approved = false; + + // Actively disable all enabled TEEs running this PCR (iterate backwards for safe swap-and-pop) + bytes32[] storage list = _enabledTEEList[teeType]; + for (uint256 i = list.length; i > 0; i--) { + bytes32 teeId = list[i - 1]; + if (tees[teeId].pcrHash == pcrHash) { + tees[teeId].enabled = false; + _removeFromEnabledList(teeId, teeType); + emit TEEDisabled(teeId); + } } - emit PCRRevoked(pcrHash, gracePeriod); + + emit PCRRevoked(pcrHash, teeType); } - /// @notice Check if a PCR is currently approved and not expired + /// @notice Check if a PCR is currently approved /// @param teeType The TEE type the PCR is valid for /// @param pcrHash The PCR hash to check - /// @return bool True if approved and not expired + /// @return bool True if approved function isPCRApproved(uint8 teeType, bytes32 pcrHash) public view returns (bool) { - ApprovedPCR storage pcr = approvedPCRs[teeType][pcrHash]; - if (!pcr.active) return false; - if (pcr.expiresAt != 0 && block.timestamp >= pcr.expiresAt) return false; - return true; + return pcrRecords[teeType][pcrHash].approved; } /// @dev Reverts if PCR is not approved for the given TEE type function _requirePCRValidForTEE(bytes32 pcrHash, uint8 teeType) private view { - ApprovedPCR storage pcr = approvedPCRs[teeType][pcrHash]; - if (!pcr.active) revert PCRNotApproved(); - if (pcr.expiresAt != 0 && block.timestamp >= pcr.expiresAt) revert PCRExpired(); + if (!pcrRecords[teeType][pcrHash].approved) revert PCRNotApproved(); } /// @notice Compute PCR hash from measurements @@ -304,9 +315,9 @@ contract TEERegistry is AccessControl { return keccak256(abi.encodePacked(pcrs.pcr0, pcrs.pcr1, pcrs.pcr2)); } - /// @notice Get all currently active (approved and not expired) PCRs + /// @notice Get all currently approved PCRs /// @return PCRKey[] Array of active PCR keys (pcrHash + teeType) - function getActivePCRs() external view returns (PCRKey[] memory) { + function getApprovedPCRs() external view returns (PCRKey[] memory) { uint256 count = 0; for (uint256 i = 0; i < _pcrList.length; i++) { if (isPCRApproved(_pcrList[i].teeType, _pcrList[i].pcrHash)) count++; @@ -322,15 +333,8 @@ contract TEERegistry is AccessControl { return result; } - // ============ Certificate Management ============ - - function setAWSRootCertificate(bytes calldata certificate) external onlyRole(DEFAULT_ADMIN_ROLE) { - awsRootCertificate = certificate; - emit AWSCertificateUpdated(keccak256(certificate)); - } - // ============ TEE Management ============ - + function registerTEEWithAttestation( bytes calldata attestationDocument, bytes calldata signingPublicKey, @@ -367,93 +371,64 @@ contract TEERegistry is AccessControl { tlsCertificate: tlsCertificate, pcrHash: pcrHash, teeType: teeType, - active: true, + enabled: true, registeredAt: block.timestamp, - lastUpdatedAt: block.timestamp + lastHeartbeatAt: block.timestamp }); // Add to indexes - _activeTEEIndex[teeType][teeId] = _activeTEEList[teeType].length; - _activeTEEList[teeType].push(teeId); + _enabledTEEIndex[teeType][teeId] = _enabledTEEList[teeType].length; + _enabledTEEList[teeType].push(teeId); _teesByType[teeType].push(teeId); _teesByOwner[msg.sender].push(teeId); emit TEERegistered(teeId, msg.sender, teeType); } - /// @notice Deactivate a TEE, removing it from the active list + /// @notice Disable a TEE, removing it from the enabled list /// @dev Requires caller to be the TEE owner with TEE_OPERATOR role, or an admin - /// @param teeId The TEE identifier to deactivate - function deactivateTEE(bytes32 teeId) external onlyTEEOwnerOrAdmin(teeId) { + /// @param teeId The TEE identifier to disable + function disableTEE(bytes32 teeId) external onlyTEEOwnerOrAdmin(teeId) { TEEInfo storage tee = tees[teeId]; - if (!tee.active) return; + if (!tee.enabled) revert TEENotEnabled(); - tee.active = false; - _removeFromActiveList(teeId, tee.teeType); - emit TEEDeactivated(teeId); + tee.enabled = false; + _removeFromEnabledList(teeId, tee.teeType); + emit TEEDisabled(teeId); } - /// @notice Re-activate a previously deactivated TEE + /// @notice Re-enable a previously disabled TEE /// @dev Requires caller to be the TEE owner with TEE_OPERATOR role, or an admin. /// Also re-validates that the TEE's PCR is still approved for its type. - /// @param teeId The TEE identifier to activate - function activateTEE(bytes32 teeId) external onlyTEEOwnerOrAdmin(teeId) { + /// @param teeId The TEE identifier to enable + function enableTEE(bytes32 teeId) external onlyTEEOwnerOrAdmin(teeId) { TEEInfo storage tee = tees[teeId]; - // Make sure to do an early return here in order to prevent - // getting around the heartbeat check which relies on lastUpdatedAt. - if (tee.active) return; + if (tee.enabled) return; _requirePCRValidForTEE(tee.pcrHash, tee.teeType); - tee.active = true; - _addToActiveList(teeId, tee.teeType); - emit TEEActivated(teeId); + tee.enabled = true; + _addToEnabledList(teeId, tee.teeType); + emit TEEEnabled(teeId); } - /// @notice Permanently remove a TEE from all storage - /// @dev Callable by TEE owner (with TEE_OPERATOR role) or admin. - /// Use to clean up decommissioned or upgraded TEEs and reclaim storage. - /// @param teeId The TEE identifier to remove - function removeTEE(bytes32 teeId) external onlyTEEOwnerOrAdmin(teeId) { - TEEInfo storage tee = tees[teeId]; - - uint8 teeType = tee.teeType; - address owner = tee.owner; - - // Remove from active list if active - if (tee.active) { - _removeFromActiveList(teeId, teeType); - } - - // Remove from _teesByType - _removeFromArray(_teesByType[teeType], teeId); - - // Remove from _teesByOwner - _removeFromArray(_teesByOwner[owner], teeId); - - // Delete TEE data - delete tees[teeId]; - - emit TEERemoved(teeId); - } - - function _addToActiveList(bytes32 teeId, uint8 teeType) private { - _activeTEEIndex[teeType][teeId] = _activeTEEList[teeType].length; - _activeTEEList[teeType].push(teeId); + function _addToEnabledList(bytes32 teeId, uint8 teeType) private { + _enabledTEEIndex[teeType][teeId] = _enabledTEEList[teeType].length; + _enabledTEEList[teeType].push(teeId); } - function _removeFromActiveList(bytes32 teeId, uint8 teeType) private { - uint256 index = _activeTEEIndex[teeType][teeId]; - uint256 lastIndex = _activeTEEList[teeType].length - 1; + function _removeFromEnabledList(bytes32 teeId, uint8 teeType) private { + uint256 index = _enabledTEEIndex[teeType][teeId]; + uint256 lastIndex = _enabledTEEList[teeType].length - 1; if (index != lastIndex) { - bytes32 lastTeeId = _activeTEEList[teeType][lastIndex]; - _activeTEEList[teeType][index] = lastTeeId; - _activeTEEIndex[teeType][lastTeeId] = index; + bytes32 lastTeeId = _enabledTEEList[teeType][lastIndex]; + _enabledTEEList[teeType][index] = lastTeeId; + _enabledTEEIndex[teeType][lastTeeId] = index; } - _activeTEEList[teeType].pop(); - delete _activeTEEIndex[teeType][teeId]; + _enabledTEEList[teeType].pop(); + delete _enabledTEEIndex[teeType][teeId]; } /// @dev Swap-and-pop removal from an unordered bytes32 array @@ -483,10 +458,7 @@ contract TEERegistry is AccessControl { ) external { TEEInfo storage tee = tees[teeId]; if (tee.registeredAt == 0) revert TEENotFound(); - if (!tee.active) revert TEENotActive(); - - // Lazy PCR enforcement (validity + type match) - _requirePCRValidForTEE(tee.pcrHash, tee.teeType); + if (!tee.enabled) revert TEENotEnabled(); // Reject stale or future signed timestamps if (timestamp > block.timestamp) revert HeartbeatTimestampInFuture(); @@ -497,7 +469,7 @@ contract TEERegistry is AccessControl { bool valid = VERIFIER.verifyRSAPSS(tee.publicKey, messageHash, signature); if (!valid) revert HeartbeatSignatureInvalid(); - tee.lastUpdatedAt = block.timestamp; + tee.lastHeartbeatAt = block.timestamp; emit HeartbeatReceived(teeId, timestamp); } @@ -516,38 +488,38 @@ contract TEERegistry is AccessControl { return tees[teeId]; } - /// @notice Get TEE IDs that have been activated for a given type - /// @dev Does NOT filter by heartbeat freshness or PCR validity. - /// Use getLiveTEEs() for fully verified results. + /// @notice Get TEE IDs that are currently enabled for a given type + /// @dev Does NOT filter by heartbeat freshness. + /// Use getActiveTEEs() for fully verified results. /// @param teeType The TEE type to query /// @return Array of TEE IDs - function getActivatedTEEs(uint8 teeType) external view returns (bytes32[] memory) { - return _activeTEEList[teeType]; + function getEnabledTEEs(uint8 teeType) external view returns (bytes32[] memory) { + return _enabledTEEList[teeType]; } - /// @notice Get TEEs that are activated, have a valid PCR, and a fresh heartbeat - /// @dev More expensive than getActivatedTEEs() due to on-chain filtering. - /// Use this when you need guaranteed-healthy TEEs without client-side checks. + /// @notice Get TEEs that are enabled, have a valid PCR, and a fresh heartbeat + /// @dev More expensive than getEnabledTEEs() due to on-chain filtering. + /// Use this when you need guaranteed-live TEEs without client-side checks. /// @param teeType The TEE type to query - /// @return Array of TEEInfo structs for live TEEs - function getLiveTEEs(uint8 teeType) external view returns (TEEInfo[] memory) { - bytes32[] storage list = _activeTEEList[teeType]; + /// @return Array of TEEInfo structs for active TEEs + function getActiveTEEs(uint8 teeType) external view returns (TEEInfo[] memory) { + bytes32[] storage list = _enabledTEEList[teeType]; uint256 count = 0; for (uint256 i = 0; i < list.length; i++) { - if (_isLive(tees[list[i]])) count++; + if (_isTEEActive(tees[list[i]])) count++; } TEEInfo[] memory result = new TEEInfo[](count); uint256 j = 0; for (uint256 i = 0; i < list.length; i++) { - if (_isLive(tees[list[i]])) { + if (_isTEEActive(tees[list[i]])) { result[j++] = tees[list[i]]; } } return result; } - /// @notice Get all TEE IDs (active and inactive) for a given type + /// @notice Get all TEE IDs (enabled and disabled) for a given type /// @param teeType The TEE type to query /// @return Array of TEE IDs function getTEEsByType(uint8 teeType) external view returns (bytes32[] memory) { @@ -561,25 +533,25 @@ contract TEERegistry is AccessControl { return _teesByOwner[owner]; } - /// @notice Check if a TEE is currently active - function isActive(bytes32 teeId) external view returns (bool) { - return tees[teeId].active; + /// @notice Check if a TEE is currently enabled + function isTEEEnabled(bytes32 teeId) external view returns (bool) { + return tees[teeId].enabled; } - /// @notice Check if a TEE is live (active + valid PCR + fresh heartbeat) - function isLive(bytes32 teeId) external view returns (bool) { - return _isLive(tees[teeId]); + /// @notice Check if a TEE is active (enabled + valid PCR + fresh heartbeat) + function isTEEActive(bytes32 teeId) external view returns (bool) { + return _isTEEActive(tees[teeId]); } - function _isLive(TEEInfo storage tee) private view returns (bool) { - if (!tee.active) return false; - if (block.timestamp - tee.lastUpdatedAt > heartbeatMaxAge) return false; + function _isTEEActive(TEEInfo storage tee) private view returns (bool) { + if (!tee.enabled) return false; + if (block.timestamp - tee.lastHeartbeatAt > heartbeatMaxAge) return false; if (!isPCRApproved(tee.teeType, tee.pcrHash)) return false; return true; } /// @notice Get a TEE's public key - function getPublicKey(bytes32 teeId) external view returns (bytes memory) { + function getTEEPublicKey(bytes32 teeId) external view returns (bytes memory) { return tees[teeId].publicKey; } @@ -589,4 +561,4 @@ contract TEERegistry is AccessControl { function computeTEEId(bytes calldata publicKey) external pure returns (bytes32) { return keccak256(publicKey); } -} \ No newline at end of file +} diff --git a/precompiles/tee/README.md b/precompiles/tee/README.md index 8d2f0285..64400e25 100644 --- a/precompiles/tee/README.md +++ b/precompiles/tee/README.md @@ -95,7 +95,6 @@ registry.activateTEE(teeId); // requires PCR to still be approved | Function | Who | Purpose | |----------|-----|---------| | `addTEEType()` | Admin | Add TEE category | -| `deactivateTEEType()` | Admin | Deactivate a TEE category | | `approvePCR()` | Admin | Approve enclave code hash for a TEE type | | `revokePCR()` | Admin | Revoke PCR (immediate or with grace period) | | `registerTEEWithAttestation()` | Operator | Register new TEE via attestation | @@ -109,7 +108,7 @@ registry.activateTEE(teeId); // requires PCR to still be approved | `getTLSCertificate()` | Anyone | Get TLS cert for HTTPS | | `isActive()` | Anyone | Check TEE status | | `getActiveTEEs()` | Anyone | List all active TEE IDs | -| `getActivePCRs()` | Anyone | List all approved PCR hashes | +| `getApprovedPCRs()` | Anyone | List all approved PCR hashes | ## Access Control diff --git a/scripts/integration/local_tee_workflow.go b/scripts/integration/local_tee_workflow.go index 94ebe7f3..c8d01157 100755 --- a/scripts/integration/local_tee_workflow.go +++ b/scripts/integration/local_tee_workflow.go @@ -66,28 +66,25 @@ var ( SEL_GET_TEE_TYPES = crypto.Keccak256([]byte("getTEETypes()"))[:4] SEL_APPROVE_PCR = crypto.Keccak256([]byte("approvePCR((bytes,bytes,bytes),string,uint8)"))[:4] - SEL_REVOKE_PCR = crypto.Keccak256([]byte("revokePCR(bytes32,uint256)"))[:4] - SEL_IS_PCR_APPROVED = crypto.Keccak256([]byte("isPCRApproved(bytes32)"))[:4] + SEL_REVOKE_PCR = crypto.Keccak256([]byte("revokePCR(bytes32,uint8)"))[:4] + SEL_IS_PCR_APPROVED = crypto.Keccak256([]byte("isPCRApproved(uint8,bytes32)"))[:4] SEL_COMPUTE_PCR_HASH = crypto.Keccak256([]byte("computePCRHash((bytes,bytes,bytes))"))[:4] - SEL_GET_ACTIVE_PCRS = crypto.Keccak256([]byte("getActivePCRs()"))[:4] + SEL_GET_APPROVED_PCRS = crypto.Keccak256([]byte("getApprovedPCRs()"))[:4] SEL_SET_AWS_ROOT_CERT = crypto.Keccak256([]byte("setAWSRootCertificate(bytes)"))[:4] SEL_REGISTER_TEE = crypto.Keccak256([]byte("registerTEEWithAttestation(bytes,bytes,bytes,address,string,uint8)"))[:4] - SEL_DEACTIVATE_TEE = crypto.Keccak256([]byte("deactivateTEE(bytes32)"))[:4] - SEL_ACTIVATE_TEE = crypto.Keccak256([]byte("activateTEE(bytes32)"))[:4] + SEL_DISABLE_TEE = crypto.Keccak256([]byte("disableTEE(bytes32)"))[:4] + SEL_ENABLE_TEE = crypto.Keccak256([]byte("enableTEE(bytes32)"))[:4] SEL_GET_TEE = crypto.Keccak256([]byte("getTEE(bytes32)"))[:4] - SEL_GET_ACTIVE_TEES = crypto.Keccak256([]byte("getActiveTEEs()"))[:4] + SEL_GET_ENABLED_TEES = crypto.Keccak256([]byte("getEnabledTEEs(uint8)"))[:4] SEL_GET_TEES_BY_TYPE = crypto.Keccak256([]byte("getTEEsByType(uint8)"))[:4] SEL_GET_TEES_BY_OWNER = crypto.Keccak256([]byte("getTEEsByOwner(address)"))[:4] - SEL_GET_PUBLIC_KEY = crypto.Keccak256([]byte("getPublicKey(bytes32)"))[:4] - SEL_GET_TLS_CERT = crypto.Keccak256([]byte("getTLSCertificate(bytes32)"))[:4] - SEL_IS_ACTIVE = crypto.Keccak256([]byte("isActive(bytes32)"))[:4] - SEL_GET_PAYMENT_ADDR = crypto.Keccak256([]byte("getPaymentAddress(bytes32)"))[:4] + SEL_GET_TEE_PUBLIC_KEY = crypto.Keccak256([]byte("getTEEPublicKey(bytes32)"))[:4] + SEL_IS_TEE_ENABLED = crypto.Keccak256([]byte("isTEEEnabled(bytes32)"))[:4] SEL_COMPUTE_TEE_ID = crypto.Keccak256([]byte("computeTEEId(bytes)"))[:4] - SEL_COMPUTE_MSG_HASH = crypto.Keccak256([]byte("computeMessageHash(bytes32,bytes32,uint256)"))[:4] ) // ============================================================================ @@ -104,6 +101,11 @@ type MeasurementsFile struct { Measurements PCRMeasurements `json:"Measurements"` } +type ApprovedPCRKey struct { + PCRHash [32]byte + TEEType uint8 +} + type AttestationResponse struct { PublicKey string `json:"public_key"` } @@ -234,22 +236,22 @@ func main() { // [LOG] Show existing approved PCRs before making any changes fmt.Println(" 🔍 Fetching currently approved PCRs from contract...") - existingActivePCRs, err := callGetActivePCRs() + existingApprovedPCRs, err := callGetApprovedPCRs() if err != nil { - fmt.Printf(" ⚠️ Could not fetch active PCRs: %v\n", err) + fmt.Printf(" ⚠️ Could not fetch approved PCRs: %v\n", err) } else { - fmt.Printf(" 📋 Currently approved PCRs (%d):\n", len(existingActivePCRs)) - for i, p := range existingActivePCRs { - fmt.Printf(" [%d] %s\n", i, p) + fmt.Printf(" 📋 Currently approved PCRs (%d):\n", len(existingApprovedPCRs)) + for i, p := range existingApprovedPCRs { + fmt.Printf(" [%d] pcrHash=0x%s teeType=%d\n", i, hex.EncodeToString(p.PCRHash[:]), p.TEEType) } } - if len(existingActivePCRs) == 0 { + if len(existingApprovedPCRs) == 0 { txHash, err := callApprovePCR(account, pcr0, pcr1, pcr2, "v1.0.0", 0) if err == nil { waitForTx(txHash) } } - approved, _ := callIsPCRApproved(pcrHash) + approved, _ := callIsPCRApproved(0, pcrHash) // [LOG] Clearly show the approval status of the PCR we are about to use fmt.Printf(" 🔍 isPCRApproved(filePCRHash=%s) = %v\n", hex.EncodeToString(pcrHash[:]), approved) @@ -263,11 +265,11 @@ func main() { rand.Read(fakePCR) fakeHash, _ := callComputePCRHash(fakePCR, pcr1, pcr2) fmt.Printf(" 🔍 isPCRApproved(fakePCRHash=%s) = expected false\n", hex.EncodeToString(fakeHash[:])) - approved, _ = callIsPCRApproved(fakeHash) + approved, _ = callIsPCRApproved(0, fakeHash) results.Add("isPCRApproved returns false for unknown PCR", !approved, "") - activePCRs, err := callGetActivePCRs() - results.Add("getActivePCRs returns list", err == nil && len(activePCRs) > 0, fmt.Sprintf("count=%d", len(activePCRs))) + approvedPCRs, err := callGetApprovedPCRs() + results.Add("getApprovedPCRs returns list", err == nil && len(approvedPCRs) > 0, fmt.Sprintf("count=%d", len(approvedPCRs))) // SECTION 4: TEE Registration fmt.Println("\n------------------------------------------") @@ -335,7 +337,7 @@ func main() { realPCRHash, _ := callComputePCRHash(realPCR0, realPCR1, realPCR2) fmt.Printf(" 📊 Real enclave PCR hash: 0x%s\n", hex.EncodeToString(realPCRHash[:])) - realApproved, _ := callIsPCRApproved(realPCRHash) + realApproved, _ := callIsPCRApproved(0, realPCRHash) fmt.Printf(" 🔍 isPCRApproved(realPCRHash) = %v\n", realApproved) if !realApproved { @@ -344,7 +346,7 @@ func main() { if err == nil { waitForTx(txHash) // [LOG] Verify the approval actually stuck - postApproval, _ := callIsPCRApproved(realPCRHash) + postApproval, _ := callIsPCRApproved(0, realPCRHash) fmt.Printf(" ✅ Real PCRs approved — post-approval isPCRApproved=%v\n", postApproval) } else { fmt.Printf(" ❌ Failed to approve real PCRs: %v\n", err) @@ -353,14 +355,14 @@ func main() { fmt.Println(" ✅ Real PCRs already approved") } - // [LOG] Dump final state of active PCRs right before registration attempt - fmt.Println(" 🔍 Active PCRs in contract (pre-registration):") - preRegPCRs, err := callGetActivePCRs() + // [LOG] Dump final state of approved PCRs right before registration attempt + fmt.Println(" 🔍 Approved PCRs in contract (pre-registration):") + preRegPCRs, err := callGetApprovedPCRs() if err != nil { fmt.Printf(" ⚠️ Could not fetch: %v\n", err) } else { for i, p := range preRegPCRs { - fmt.Printf(" [%d] %s\n", i, p) + fmt.Printf(" [%d] pcrHash=0x%s teeType=%d\n", i, hex.EncodeToString(p.PCRHash[:]), p.TEEType) } } } @@ -387,10 +389,10 @@ func main() { // [LOG] Show TLS cert fingerprint for cross-check fmt.Printf(" 🔏 TLS cert SHA256: %x\n", sha256.Sum256(tlsCertDER)) - isActive, _ := callIsActive(expectedTeeId) - fmt.Printf(" 🔍 isActive(expectedTeeId) = %v\n", isActive) + isEnabled, _ := callIsTEEEnabled(expectedTeeId) + fmt.Printf(" 🔍 isTEEEnabled(expectedTeeId) = %v\n", isEnabled) - if isActive { + if isEnabled { fmt.Println(" ℹ️ TEE already registered") registrationSuccess = true registeredTEEId = expectedTeeId @@ -421,15 +423,15 @@ func main() { // [LOG] Verify post-registration state fmt.Printf(" ✅ Registration succeeded — verifying on-chain state...\n") - postActive, _ := callIsActive(registeredTEEId) - fmt.Printf(" isActive(teeId)=%v\n", postActive) + postEnabled, _ := callIsTEEEnabled(registeredTEEId) + fmt.Printf(" isTEEEnabled(teeId)=%v\n", postEnabled) } else { // [LOG] On failure, dump current PCR state to diagnose mismatch fmt.Println(" ❌ Registration failed — dumping PCR state for diagnosis:") - failPCRs, _ := callGetActivePCRs() - fmt.Printf(" Active PCRs in contract (%d):\n", len(failPCRs)) + failPCRs, _ := callGetApprovedPCRs() + fmt.Printf(" Approved PCRs in contract (%d):\n", len(failPCRs)) for i, p := range failPCRs { - fmt.Printf(" [%d] %s\n", i, p) + fmt.Printf(" [%d] pcrHash=0x%s teeType=%d\n", i, hex.EncodeToString(p.PCRHash[:]), p.TEEType) } } } @@ -444,10 +446,10 @@ func main() { fmt.Println("------------------------------------------") if registrationSuccess { - isActive, err := callIsActive(registeredTEEId) - results.Add("isActive returns true for registered TEE", isActive && err == nil, "") + isEnabled, err := callIsTEEEnabled(registeredTEEId) + results.Add("isTEEEnabled returns true for registered TEE", isEnabled && err == nil, "") - storedKey, err := callGetPublicKey(registeredTEEId) + storedKey, err := callGetTEEPublicKey(registeredTEEId) keyMatches := err == nil && bytes.Equal(storedKey, signingPubKeyDER) // [LOG] Show key comparison detail when it fails if !keyMatches { @@ -457,13 +459,10 @@ func main() { } fmt.Printf(" expected SHA256: %x\n", sha256.Sum256(signingPubKeyDER)) } - results.Add("getPublicKey returns correct key", keyMatches, "") - - storedCert, err := callGetTLSCertificate(registeredTEEId) - results.Add("getTLSCertificate returns cert", err == nil && len(storedCert) > 0, fmt.Sprintf("%d bytes", len(storedCert))) + results.Add("getTEEPublicKey returns correct key", keyMatches, "") - activeTEEs, err := callGetActiveTEEs() - results.Add("getActiveTEEs includes registered TEE", err == nil && len(activeTEEs) > 0, fmt.Sprintf("count=%d", len(activeTEEs))) + enabledTEEs, err := callGetEnabledTEEs(0) + results.Add("getEnabledTEEs includes registered TEE", err == nil && len(enabledTEEs) > 0, fmt.Sprintf("count=%d", len(enabledTEEs))) teesByType, err := callGetTEEsByType(0) results.Add("getTEEsByType(0) includes registered TEE", err == nil && len(teesByType) > 0, fmt.Sprintf("count=%d", len(teesByType))) @@ -480,19 +479,19 @@ func main() { fmt.Println("------------------------------------------") if registrationSuccess { - txHash, err := callDeactivateTEE(account, registeredTEEId) + txHash, err := callDisableTEE(account, registeredTEEId) if err == nil { waitForTx(txHash) } - isActive, _ := callIsActive(registeredTEEId) - results.Add("Deactivate TEE", !isActive, "") + isEnabled, _ := callIsTEEEnabled(registeredTEEId) + results.Add("Disable TEE", !isEnabled, "") - txHash, err = callActivateTEE(account, registeredTEEId) + txHash, err = callEnableTEE(account, registeredTEEId) if err == nil { waitForTx(txHash) } - isActive, _ = callIsActive(registeredTEEId) - results.Add("Reactivate TEE", isActive, "") + isEnabled, _ = callIsTEEEnabled(registeredTEEId) + results.Add("Re-enable TEE", isEnabled, "") } else { fmt.Println(" ⚠️ Skipping lifecycle tests (registration failed)") } @@ -517,9 +516,9 @@ func main() { badSig[0] ^= 0xFF err = verifySignatureLocal(testPubKeyDER, messageHash[:], badSig) results.Add("Reject invalid signature", err != nil, "") - // SECTION 8: PCR Revocation & TEE Deactivation Security + // SECTION 8: PCR Revocation & TEE Disable Security fmt.Println("\n------------------------------------------") - fmt.Println("SECTION 8: PCR Revocation & TEE Deactivation") + fmt.Println("SECTION 8: PCR Revocation & TEE Disable") fmt.Println("------------------------------------------") if registrationSuccess { @@ -541,7 +540,7 @@ func main() { txHash, err := callApprovePCR(account, testPCR0, testPCR1, testPCR2, "test-revoke", 0) if err == nil { waitForTx(txHash) - approved, _ := callIsPCRApproved(testPCRHash) + approved, _ := callIsPCRApproved(0, testPCRHash) results.Add("Test PCR approved", approved, "") } else { results.Add("Approve test PCR", false, err.Error()) @@ -549,40 +548,40 @@ func main() { // Step 2: Revoke the test PCR fmt.Println("\n Step 2: Revoke test PCR") - txHash, err = callRevokePCR(account, testPCRHash, 0) + txHash, err = callRevokePCR(account, testPCRHash, 0) // teeType=0 if err != nil { results.Add("Revoke test PCR", false, err.Error()) } else { waitForTx(txHash) - stillApproved, _ := callIsPCRApproved(testPCRHash) + stillApproved, _ := callIsPCRApproved(0, testPCRHash) results.Add("Test PCR no longer approved after revocation", !stillApproved, "") fmt.Println(" ✅ Test PCR successfully revoked") } - // Step 3: Test activate/deactivate cycle with valid PCR - fmt.Println("\n Step 3: Test activate/deactivate cycle") + // Step 3: Test enable/disable cycle with valid PCR + fmt.Println("\n Step 3: Test enable/disable cycle") - // Deactivate our real TEE - txHash, err = callDeactivateTEE(account, registeredTEEId) + // Disable our real TEE + txHash, err = callDisableTEE(account, registeredTEEId) if err == nil { waitForTx(txHash) - isActive, _ := callIsActive(registeredTEEId) - results.Add("TEE deactivated", !isActive, "") + isEnabled, _ := callIsTEEEnabled(registeredTEEId) + results.Add("TEE disabled", !isEnabled, "") } - // Reactivate - should succeed since TEE's PCR is still valid - txHash, err = callActivateTEE(account, registeredTEEId) + // Re-enable - should succeed since TEE's PCR is still valid + txHash, err = callEnableTEE(account, registeredTEEId) if err == nil { waitForTx(txHash) - isActive, _ := callIsActive(registeredTEEId) - results.Add("TEE reactivated with valid PCR", isActive, "") - fmt.Println(" ✅ Reactivation successful (PCR valid)") + isEnabled, _ := callIsTEEEnabled(registeredTEEId) + results.Add("TEE re-enabled with valid PCR", isEnabled, "") + fmt.Println(" ✅ Re-enable successful (PCR valid)") } else { - results.Add("Reactivate TEE", false, err.Error()) + results.Add("Re-enable TEE", false, err.Error()) } - // Step 4: Test grace period functionality - fmt.Println("\n Step 4: Test PCR grace period") + // Step 4: Test PCR revocation disables TEEs + fmt.Println("\n Step 4: Test PCR revocation disables TEEs") pcrV1_0 := make([]byte, 48) pcrV1_1 := make([]byte, 48) @@ -605,31 +604,32 @@ func main() { fmt.Printf(" 📊 PCR v2 Hash: 0x%s\n", hex.EncodeToString(pcrV2Hash[:])) // Approve v1 and v2 - txHash, _ = callApprovePCR(account, pcrV1_0, pcrV1_1, pcrV1_2, "v1-grace", 0) + txHash, _ = callApprovePCR(account, pcrV1_0, pcrV1_1, pcrV1_2, "v1-revoke-test", 0) waitForTx(txHash) - txHash, _ = callApprovePCR(account, pcrV2_0, pcrV2_1, pcrV2_2, "v2-grace", 0) + txHash, _ = callApprovePCR(account, pcrV2_0, pcrV2_1, pcrV2_2, "v2-revoke-test", 0) waitForTx(txHash) - // Revoke v1 with 1 hour grace period - txHash, err = callRevokePCR(account, pcrV1Hash, 3600) + // Revoke v1 - should be immediate + txHash, err = callRevokePCR(account, pcrV1Hash, 0) // teeType=0 if err == nil { waitForTx(txHash) - // Both should be valid during grace period - v1Valid, _ := callIsPCRApproved(pcrV1Hash) - v2Valid, _ := callIsPCRApproved(pcrV2Hash) + // v1 should be revoked, v2 should still be valid + v1Valid, _ := callIsPCRApproved(0, pcrV1Hash) + v2Valid, _ := callIsPCRApproved(0, pcrV2Hash) - bothValid := v1Valid && v2Valid - results.Add("Both PCRs valid during grace period", bothValid, - fmt.Sprintf("v1=%v, v2=%v", v1Valid, v2Valid)) + results.Add("Revoked PCR v1 is invalid", !v1Valid, + fmt.Sprintf("v1=%v (expected false)", v1Valid)) + results.Add("PCR v2 still valid after v1 revocation", v2Valid, + fmt.Sprintf("v2=%v", v2Valid)) - if bothValid { - fmt.Println(" ✅ Grace period working: both v1 and v2 valid") + if !v1Valid && v2Valid { + fmt.Println(" ✅ PCR revocation working: v1 revoked, v2 still valid") } else { - fmt.Printf(" ⚠️ Grace period issue: v1=%v, v2=%v\n", v1Valid, v2Valid) + fmt.Printf(" ⚠️ Revocation issue: v1=%v, v2=%v\n", v1Valid, v2Valid) } } else { - results.Add("Revoke PCR with grace period", false, err.Error()) + results.Add("Revoke PCR", false, err.Error()) } // Step 5: Test duplicate PCR prevention @@ -675,9 +675,6 @@ func main() { expectedId := crypto.Keccak256Hash(testPubKeyDER) results.Add("computeTEEId matches keccak256", err == nil && computedId == expectedId, "") - computedHash, err := callComputeMessageHash(inputHash, outputHash, timestamp) - results.Add("computeMessageHash returns hash", err == nil && computedHash != [32]byte{}, "") - // Summary fmt.Println("\n==========================================") fmt.Println(" Test Summary") @@ -938,11 +935,11 @@ func loadPCRMeasurements() ([]byte, []byte, []byte, error) { return pcr0, pcr1, pcr2, nil } -func callRevokePCR(from string, pcrHash [32]byte, gracePeriod uint64) (string, error) { +func callRevokePCR(from string, pcrHash [32]byte, teeType uint8) (string, error) { bytes32Type, _ := abi.NewType("bytes32", "", nil) - uint256Type, _ := abi.NewType("uint256", "", nil) - args := abi.Arguments{{Type: bytes32Type}, {Type: uint256Type}} - encoded, _ := args.Pack(pcrHash, new(big.Int).SetUint64(gracePeriod)) + u8Type, _ := abi.NewType("uint8", "", nil) + args := abi.Arguments{{Type: bytes32Type}, {Type: u8Type}} + encoded, _ := args.Pack(pcrHash, teeType) return sendTx(from, append(SEL_REVOKE_PCR, encoded...)) } @@ -1094,10 +1091,11 @@ func callApprovePCR(from string, pcr0, pcr1, pcr2 []byte, version string, teeTyp return sendTx(from, append(SEL_APPROVE_PCR, encoded...)) } -func callIsPCRApproved(pcrHash [32]byte) (bool, error) { +func callIsPCRApproved(teeType uint8, pcrHash [32]byte) (bool, error) { + uint8Type, _ := abi.NewType("uint8", "", nil) bytes32Type, _ := abi.NewType("bytes32", "", nil) - args := abi.Arguments{{Type: bytes32Type}} - encoded, _ := args.Pack(pcrHash) + args := abi.Arguments{{Type: uint8Type}, {Type: bytes32Type}} + encoded, _ := args.Pack(teeType, pcrHash) result, err := ethCall(append(SEL_IS_PCR_APPROVED, encoded...)) if err != nil || len(result) < 32 { return false, err @@ -1123,18 +1121,25 @@ func callComputePCRHash(pcr0, pcr1, pcr2 []byte) ([32]byte, error) { return hash, nil } -func callGetActivePCRs() ([]string, error) { - result, err := ethCall(SEL_GET_ACTIVE_PCRS) +func callGetApprovedPCRs() ([]ApprovedPCRKey, error) { + result, err := ethCall(SEL_GET_APPROVED_PCRS) if err != nil || len(result) < 64 { return nil, err } - length := new(big.Int).SetBytes(result[32:64]).Uint64() - pcrs := make([]string, length) + // ABI: offset (32 bytes) | length (32 bytes) | then length * (bytes32 pcrHash, uint8 teeType padded to 32 bytes) + offset := new(big.Int).SetBytes(result[0:32]).Uint64() + length := new(big.Int).SetBytes(result[offset : offset+32]).Uint64() + pcrs := make([]ApprovedPCRKey, length) + dataStart := offset + 32 for i := uint64(0); i < length; i++ { - start := 64 + i*32 - if start+32 <= uint64(len(result)) { - pcrs[i] = "0x" + hex.EncodeToString(result[start:start+32]) + entryStart := dataStart + i*64 // each tuple is 2 slots: bytes32 + uint8 + if entryStart+64 > uint64(len(result)) { + break } + var key ApprovedPCRKey + copy(key.PCRHash[:], result[entryStart:entryStart+32]) + key.TEEType = result[entryStart+63] // uint8 is right-padded in the last byte of the 32-byte slot + pcrs[i] = key } return pcrs, nil } @@ -1172,55 +1177,40 @@ func callRegisterTEE(from string, attestation, signingKey, tlsCert []byte, payme return sendTx(from, append(SEL_REGISTER_TEE, encoded...)) } -func callDeactivateTEE(from string, teeId [32]byte) (string, error) { +func callDisableTEE(from string, teeId [32]byte) (string, error) { bytes32Type, _ := abi.NewType("bytes32", "", nil) args := abi.Arguments{{Type: bytes32Type}} encoded, _ := args.Pack(teeId) - return sendTx(from, append(SEL_DEACTIVATE_TEE, encoded...)) + return sendTx(from, append(SEL_DISABLE_TEE, encoded...)) } -func callActivateTEE(from string, teeId [32]byte) (string, error) { +func callEnableTEE(from string, teeId [32]byte) (string, error) { bytes32Type, _ := abi.NewType("bytes32", "", nil) args := abi.Arguments{{Type: bytes32Type}} encoded, _ := args.Pack(teeId) - return sendTx(from, append(SEL_ACTIVATE_TEE, encoded...)) + return sendTx(from, append(SEL_ENABLE_TEE, encoded...)) } // ============================================================================ // CONTRACT CALLS - Queries // ============================================================================ -func callIsActive(teeId [32]byte) (bool, error) { +func callIsTEEEnabled(teeId [32]byte) (bool, error) { bytes32Type, _ := abi.NewType("bytes32", "", nil) args := abi.Arguments{{Type: bytes32Type}} encoded, _ := args.Pack(teeId) - result, err := ethCall(append(SEL_IS_ACTIVE, encoded...)) + result, err := ethCall(append(SEL_IS_TEE_ENABLED, encoded...)) if err != nil || len(result) < 32 { return false, err } return result[31] == 1, nil } -func callGetPublicKey(teeId [32]byte) ([]byte, error) { - bytes32Type, _ := abi.NewType("bytes32", "", nil) - args := abi.Arguments{{Type: bytes32Type}} - encoded, _ := args.Pack(teeId) - result, err := ethCall(append(SEL_GET_PUBLIC_KEY, encoded...)) - if err != nil || len(result) < 64 { - return nil, err - } - length := new(big.Int).SetBytes(result[32:64]).Uint64() - if uint64(len(result)) < 64+length { - return nil, fmt.Errorf("truncated") - } - return result[64 : 64+length], nil -} - -func callGetTLSCertificate(teeId [32]byte) ([]byte, error) { +func callGetTEEPublicKey(teeId [32]byte) ([]byte, error) { bytes32Type, _ := abi.NewType("bytes32", "", nil) args := abi.Arguments{{Type: bytes32Type}} encoded, _ := args.Pack(teeId) - result, err := ethCall(append(SEL_GET_TLS_CERT, encoded...)) + result, err := ethCall(append(SEL_GET_TEE_PUBLIC_KEY, encoded...)) if err != nil || len(result) < 64 { return nil, err } @@ -1231,8 +1221,11 @@ func callGetTLSCertificate(teeId [32]byte) ([]byte, error) { return result[64 : 64+length], nil } -func callGetActiveTEEs() ([]string, error) { - result, err := ethCall(SEL_GET_ACTIVE_TEES) +func callGetEnabledTEEs(teeType uint8) ([]string, error) { + uint8Type, _ := abi.NewType("uint8", "", nil) + args := abi.Arguments{{Type: uint8Type}} + encoded, _ := args.Pack(teeType) + result, err := ethCall(append(SEL_GET_ENABLED_TEES, encoded...)) if err != nil || len(result) < 64 { return nil, err } @@ -1302,20 +1295,6 @@ func callComputeTEEId(publicKey []byte) ([32]byte, error) { return id, nil } -func callComputeMessageHash(inputHash, outputHash [32]byte, timestamp *big.Int) ([32]byte, error) { - bytes32Type, _ := abi.NewType("bytes32", "", nil) - uint256Type, _ := abi.NewType("uint256", "", nil) - args := abi.Arguments{{Type: bytes32Type}, {Type: bytes32Type}, {Type: uint256Type}} - encoded, _ := args.Pack(inputHash, outputHash, timestamp) - result, err := ethCall(append(SEL_COMPUTE_MSG_HASH, encoded...)) - if err != nil || len(result) < 32 { - return [32]byte{}, err - } - var hash [32]byte - copy(hash[:], result[:32]) - return hash, nil -} - // ============================================================================ // RPC HELPERS // ============================================================================ diff --git a/scripts/tee-mgmt-cli/Readme.md b/scripts/tee-mgmt-cli/Readme.md index f4d52f43..0df1b501 100644 --- a/scripts/tee-mgmt-cli/Readme.md +++ b/scripts/tee-mgmt-cli/Readme.md @@ -76,7 +76,6 @@ Approve flags: ```bash tee-cli type list # List registered TEE types tee-cli type add # Add a new TEE type -tee-cli type deactivate # Deactivate a TEE type ``` ### `role` — Access Control diff --git a/scripts/tee-mgmt-cli/cmd/pcr.go b/scripts/tee-mgmt-cli/cmd/pcr.go index 9f06030d..6005ba52 100644 --- a/scripts/tee-mgmt-cli/cmd/pcr.go +++ b/scripts/tee-mgmt-cli/cmd/pcr.go @@ -3,7 +3,6 @@ package cmd import ( "encoding/hex" "fmt" - "math/big" "tee-mgmt-cli/registry" @@ -19,14 +18,14 @@ var pcrListCmd = &cobra.Command{ Use: "list", Short: "List all currently approved PCR hashes", RunE: func(cmd *cobra.Command, args []string) error { - fmt.Println("=== Active PCRs ===") - pcrs, err := client.GetActivePCRs() + fmt.Println("=== Approved PCRs ===") + pcrs, err := client.GetApprovedPCRs() if err != nil { return fmt.Errorf("failed: %w", err) } - fmt.Printf("Found %d active PCR(s)\n\n", len(pcrs)) - for i, h := range pcrs { - fmt.Printf(" [%d] 0x%s\n", i+1, h) + fmt.Printf("Found %d approved PCR(s)\n\n", len(pcrs)) + for i, k := range pcrs { + fmt.Printf(" [%d] 0x%s (type: %d)\n", i+1, hex.EncodeToString(k.PCRHash[:]), k.TEEType) } return nil }, @@ -65,7 +64,7 @@ var pcrApproveCmd = &cobra.Command{ var pcrRevokeCmd = &cobra.Command{ Use: "revoke ", - Short: "Revoke a previously approved PCR hash (immediately or with grace period)", + Short: "Revoke a previously approved PCR hash and disable all TEEs using it", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { pcrHash, err := registry.ParseBytes32(args[0]) @@ -73,14 +72,11 @@ var pcrRevokeCmd = &cobra.Command{ return fmt.Errorf("invalid pcrHash: %w", err) } teeType, _ := cmd.Flags().GetUint8("tee-type") - gracePeriodStr, _ := cmd.Flags().GetString("grace-period") - gracePeriod := new(big.Int) - gracePeriod.SetString(gracePeriodStr, 10) account, _ := client.GetAccountAddress() - registry.Log("Revoking PCR: 0x%s (type: %d, grace period: %s seconds)", hex.EncodeToString(pcrHash[:]), teeType, gracePeriod.String()) - txHash, err := client.RevokePCR(account, pcrHash, teeType, gracePeriod) + registry.Log("Revoking PCR: 0x%s (type: %d)", hex.EncodeToString(pcrHash[:]), teeType) + txHash, err := client.RevokePCR(account, pcrHash, teeType) if err != nil { return fmt.Errorf("failed: %w", err) } @@ -143,7 +139,6 @@ func init() { pcrApproveCmd.Flags().Uint8("tee-type", 0, "TEE type ID this PCR is valid for") pcrRevokeCmd.Flags().Uint8("tee-type", 0, "TEE type ID the PCR is approved for") - pcrRevokeCmd.Flags().String("grace-period", "0", "Grace period in seconds before revocation takes effect (0 = immediate)") pcrCheckCmd.Flags().Uint8("tee-type", 0, "TEE type ID to check against") diff --git a/scripts/tee-mgmt-cli/cmd/tee.go b/scripts/tee-mgmt-cli/cmd/tee.go index 9479b136..93a73a67 100644 --- a/scripts/tee-mgmt-cli/cmd/tee.go +++ b/scripts/tee-mgmt-cli/cmd/tee.go @@ -13,26 +13,26 @@ import ( var teeCmd = &cobra.Command{ Use: "tee", - Short: "Manage TEE instances (register, activate, deactivate, inspect)", + Short: "Manage TEE instances (register, enable, disable, inspect)", } var teeListCmd = &cobra.Command{ Use: "list", - Short: "List activated TEEs for a given type", + Short: "List enabled TEEs for a given type", RunE: func(cmd *cobra.Command, args []string) error { teeType, _ := cmd.Flags().GetUint8("tee-type") - fmt.Println("=== Activated TEEs in Registry ===") + fmt.Println("=== Enabled TEEs in Registry ===") fmt.Printf("Registry: %s\n", client.RegistryAddress) fmt.Printf("RPC: %s\n", client.RPCURL) fmt.Printf("Type: %d\n\n", teeType) - tees, err := client.GetActivatedTEEs(teeType) + tees, err := client.GetEnabledTEEs(teeType) if err != nil { - return fmt.Errorf("failed to get activated TEEs: %w", err) + return fmt.Errorf("failed to get enabled TEEs: %w", err) } - fmt.Printf("Found %d activated TEE(s)\n\n", len(tees)) + fmt.Printf("Found %d enabled TEE(s)\n\n", len(tees)) for i, teeId := range tees { fmt.Printf(" [%d] 0x%s\n", i+1, teeId) } @@ -62,9 +62,9 @@ var teeShowCmd = &cobra.Command{ fmt.Printf(" Endpoint: %s\n", info.Endpoint) fmt.Printf(" PCR Hash: 0x%s\n", hex.EncodeToString(info.PCRHash[:])) fmt.Printf(" TEE Type: %d (%s)\n", info.TEEType, registry.GetTEETypeName(info.TEEType)) - fmt.Printf(" Active: %v\n", info.IsActive) + fmt.Printf(" Enabled: %v\n", info.IsEnabled) fmt.Printf(" Registered: %s UTC\n", info.RegisteredAt.UTC().Format("2006-01-02 15:04:05")) - fmt.Printf(" Last Updated: %s UTC\n", info.LastUpdatedAt.UTC().Format("2006-01-02 15:04:05")) + fmt.Printf(" Last Heartbeat: %s UTC\n", info.LastHeartbeatAt.UTC().Format("2006-01-02 15:04:05")) fmt.Println("\n --- Public Key ---") if len(info.PublicKey) > 0 { @@ -162,9 +162,9 @@ var teeRegisterCmd = &cobra.Command{ }, } -var teeDeactivateCmd = &cobra.Command{ - Use: "deactivate ", - Short: "Deactivate a TEE (requires admin or operator role)", +var teeDisableCmd = &cobra.Command{ + Use: "disable ", + Short: "Disable a TEE (requires admin or operator role)", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { teeId, err := registry.ParseBytes32(args[0]) @@ -173,20 +173,20 @@ var teeDeactivateCmd = &cobra.Command{ } account, _ := client.GetAccountAddress() - registry.Log("Deactivating TEE: 0x%s", hex.EncodeToString(teeId[:])) - txHash, err := client.DeactivateTEE(account, teeId) + registry.Log("Disabling TEE: 0x%s", hex.EncodeToString(teeId[:])) + txHash, err := client.DisableTEE(account, teeId) if err != nil { return fmt.Errorf("failed: %w", err) } fmt.Printf("TX: %s\n", txHash) - registry.PrintTxResult(client.WaitForTx(txHash), "TEE deactivated") + registry.PrintTxResult(client.WaitForTx(txHash), "TEE disabled") return nil }, } -var teeActivateCmd = &cobra.Command{ - Use: "activate ", - Short: "Re-activate a previously deactivated TEE", +var teeEnableCmd = &cobra.Command{ + Use: "enable ", + Short: "Re-enable a previously disabled TEE", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { teeId, err := registry.ParseBytes32(args[0]) @@ -195,86 +195,64 @@ var teeActivateCmd = &cobra.Command{ } account, _ := client.GetAccountAddress() - registry.Log("Activating TEE: 0x%s", hex.EncodeToString(teeId[:])) - txHash, err := client.ActivateTEE(account, teeId) + registry.Log("Enabling TEE: 0x%s", hex.EncodeToString(teeId[:])) + txHash, err := client.EnableTEE(account, teeId) if err != nil { return fmt.Errorf("failed: %w", err) } fmt.Printf("TX: %s\n", txHash) - registry.PrintTxResult(client.WaitForTx(txHash), "TEE activated") + registry.PrintTxResult(client.WaitForTx(txHash), "TEE enabled") return nil }, } -var teeLiveCmd = &cobra.Command{ - Use: "live", - Short: "List live TEEs (active + valid PCR + fresh heartbeat)", +var teeActiveCmd = &cobra.Command{ + Use: "active", + Short: "List active TEEs (enabled + valid PCR + fresh heartbeat)", RunE: func(cmd *cobra.Command, args []string) error { teeType, _ := cmd.Flags().GetUint8("tee-type") - fmt.Println("=== Live TEEs ===") + fmt.Println("=== Active TEEs ===") fmt.Printf("Registry: %s\n", client.RegistryAddress) fmt.Printf("Type: %d\n\n", teeType) - tees, err := client.GetActivatedTEEs(teeType) + tees, err := client.GetEnabledTEEs(teeType) if err != nil { - return fmt.Errorf("failed to get activated TEEs: %w", err) + return fmt.Errorf("failed to get enabled TEEs: %w", err) } - liveCount := 0 + activeCount := 0 for _, teeIdHex := range tees { teeId, err := registry.ParseBytes32(teeIdHex) if err != nil { continue } - live, err := client.IsLive(teeId) - if err != nil || !live { + healthy, err := client.IsTEEActive(teeId) + if err != nil || !healthy { continue } - liveCount++ + activeCount++ info, err := client.GetTEE(teeId) if err != nil { - fmt.Printf(" [%d] 0x%s (could not fetch details)\n", liveCount, teeIdHex) + fmt.Printf(" [%d] 0x%s (could not fetch details)\n", activeCount, teeIdHex) continue } - fmt.Printf(" [%d] 0x%s\n", liveCount, teeIdHex) - fmt.Printf(" Endpoint: %s\n", info.Endpoint) - fmt.Printf(" Type: %d (%s)\n", info.TEEType, registry.GetTEETypeName(info.TEEType)) - fmt.Printf(" Last Updated: %s UTC\n\n", info.LastUpdatedAt.UTC().Format("2006-01-02 15:04:05")) + fmt.Printf(" [%d] 0x%s\n", activeCount, teeIdHex) + fmt.Printf(" Endpoint: %s\n", info.Endpoint) + fmt.Printf(" Type: %d (%s)\n", info.TEEType, registry.GetTEETypeName(info.TEEType)) + fmt.Printf(" Last Heartbeat: %s UTC\n\n", info.LastHeartbeatAt.UTC().Format("2006-01-02 15:04:05")) } - if liveCount == 0 { - fmt.Println(" No live TEEs found") + if activeCount == 0 { + fmt.Println(" No active TEEs found") } else { - fmt.Printf("Total: %d live / %d activated\n", liveCount, len(tees)) + fmt.Printf("Total: %d active / %d enabled\n", activeCount, len(tees)) } return nil }, } -var teeRemoveCmd = &cobra.Command{ - Use: "remove ", - Short: "Permanently remove a TEE from the registry (owner or admin)", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - teeId, err := registry.ParseBytes32(args[0]) - if err != nil { - return fmt.Errorf("invalid TEE ID: %w", err) - } - account, _ := client.GetAccountAddress() - - registry.Log("Removing TEE: 0x%s", hex.EncodeToString(teeId[:])) - txHash, err := client.RemoveTEE(account, teeId) - if err != nil { - return fmt.Errorf("failed: %w", err) - } - fmt.Printf("TX: %s\n", txHash) - registry.PrintTxResult(client.WaitForTx(txHash), "TEE removed") - return nil - }, -} - func init() { teeListCmd.Flags().Uint8("tee-type", 0, "TEE type ID to list") @@ -285,8 +263,8 @@ func init() { teeRegisterCmd.Flags().Uint8("tee-type", 0, "TEE type ID (e.g. 0=LLMProxy, 1=Validator)") teeRegisterCmd.MarkFlagRequired("enclave-host") - teeLiveCmd.Flags().Uint8("tee-type", 0, "TEE type ID to list") + teeActiveCmd.Flags().Uint8("tee-type", 0, "TEE type ID to list") - teeCmd.AddCommand(teeListCmd, teeShowCmd, teeLiveCmd, teeRegisterCmd, teeDeactivateCmd, teeActivateCmd, teeRemoveCmd) + teeCmd.AddCommand(teeListCmd, teeShowCmd, teeActiveCmd, teeRegisterCmd, teeDisableCmd, teeEnableCmd) rootCmd.AddCommand(teeCmd) } diff --git a/scripts/tee-mgmt-cli/cmd/types.go b/scripts/tee-mgmt-cli/cmd/types.go index 3ec327e8..862d459d 100644 --- a/scripts/tee-mgmt-cli/cmd/types.go +++ b/scripts/tee-mgmt-cli/cmd/types.go @@ -48,26 +48,7 @@ var typeAddCmd = &cobra.Command{ }, } -var typeDeactivateCmd = &cobra.Command{ - Use: "deactivate ", - Short: "Deactivate a TEE type so it can no longer be used for new registrations", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - typeId := uint8(registry.ParseUint(args[0])) - account, _ := client.GetAccountAddress() - - registry.Log("Deactivating type %d", typeId) - txHash, err := client.DeactivateTEEType(account, typeId) - if err != nil { - return fmt.Errorf("failed: %w", err) - } - fmt.Printf("TX: %s\n", txHash) - registry.PrintTxResult(client.WaitForTx(txHash), "Type deactivated") - return nil - }, -} - func init() { - typeCmd.AddCommand(typeListCmd, typeAddCmd, typeDeactivateCmd) + typeCmd.AddCommand(typeListCmd, typeAddCmd) rootCmd.AddCommand(typeCmd) } diff --git a/scripts/tee-mgmt-cli/registry/client.go b/scripts/tee-mgmt-cli/registry/client.go index 235a0abe..39132553 100644 --- a/scripts/tee-mgmt-cli/registry/client.go +++ b/scripts/tee-mgmt-cli/registry/client.go @@ -36,21 +36,19 @@ var ( selRevokeRole = crypto.Keccak256([]byte("revokeRole(bytes32,address)"))[:4] selHasRole = crypto.Keccak256([]byte("hasRole(bytes32,address)"))[:4] selAddTEEType = crypto.Keccak256([]byte("addTEEType(uint8,string)"))[:4] - selDeactivateTEETyp = crypto.Keccak256([]byte("deactivateTEEType(uint8)"))[:4] selIsValidType = crypto.Keccak256([]byte("isValidTEEType(uint8)"))[:4] selApprovePCR = crypto.Keccak256([]byte("approvePCR((bytes,bytes,bytes),string,uint8)"))[:4] - selRevokePCR = crypto.Keccak256([]byte("revokePCR(bytes32,uint8,uint256)"))[:4] + selRevokePCR = crypto.Keccak256([]byte("revokePCR(bytes32,uint8)"))[:4] selIsPCRApproved = crypto.Keccak256([]byte("isPCRApproved(uint8,bytes32)"))[:4] selComputePCRHash = crypto.Keccak256([]byte("computePCRHash((bytes,bytes,bytes))"))[:4] - selGetActivePCRs = crypto.Keccak256([]byte("getActivePCRs()"))[:4] + selGetApprovedPCRs = crypto.Keccak256([]byte("getApprovedPCRs()"))[:4] selSetAWSRootCert = crypto.Keccak256([]byte("setAWSRootCertificate(bytes)"))[:4] selRegisterTEE = crypto.Keccak256([]byte("registerTEEWithAttestation(bytes,bytes,bytes,address,string,uint8)"))[:4] - selDeactivateTEE = crypto.Keccak256([]byte("deactivateTEE(bytes32)"))[:4] - selActivateTEE = crypto.Keccak256([]byte("activateTEE(bytes32)"))[:4] - selRemoveTEE = crypto.Keccak256([]byte("removeTEE(bytes32)"))[:4] - selGetActivatedTEEs = crypto.Keccak256([]byte("getActivatedTEEs(uint8)"))[:4] + selDisableTEE = crypto.Keccak256([]byte("disableTEE(bytes32)"))[:4] + selEnableTEE = crypto.Keccak256([]byte("enableTEE(bytes32)"))[:4] + selGetEnabledTEEs = crypto.Keccak256([]byte("getEnabledTEEs(uint8)"))[:4] selGetTEE = crypto.Keccak256([]byte("getTEE(bytes32)"))[:4] - selIsLive = crypto.Keccak256([]byte("isLive(bytes32)"))[:4] + selIsTEEActive = crypto.Keccak256([]byte("isTEEActive(bytes32)"))[:4] ) // Structs @@ -63,9 +61,9 @@ type TEEInfo struct { TLSCertificate []byte PCRHash [32]byte TEEType uint8 - IsActive bool + IsEnabled bool RegisteredAt time.Time - LastUpdatedAt time.Time + LastHeartbeatAt time.Time } type AttestationResponse struct { @@ -132,10 +130,10 @@ func (c *Client) getFirstAccount() (string, error) { // TEE Calls -func (c *Client) GetActivatedTEEs(teeType uint8) ([]string, error) { +func (c *Client) GetEnabledTEEs(teeType uint8) ([]string, error) { u8T, _ := abi.NewType("uint8", "", nil) encoded, _ := abi.Arguments{{Type: u8T}}.Pack(teeType) - result, err := c.ethCall(append(selGetActivatedTEEs, encoded...)) + result, err := c.ethCall(append(selGetEnabledTEEs, encoded...)) if err != nil { return nil, err } @@ -149,17 +147,6 @@ func (c *Client) GetTEE(teeId [32]byte) (*TEEInfo, error) { return nil, err } - type TEEData struct { - Owner common.Address - PaymentAddress common.Address - Endpoint string - PCRHash [32]byte - TEEType uint8 - IsActive bool - RegisteredAt *big.Int - LastUpdatedAt *big.Int - } - tupleType, err := abi.NewType("tuple", "", []abi.ArgumentMarshaling{ {Name: "owner", Type: "address"}, {Name: "paymentAddress", Type: "address"}, @@ -168,9 +155,9 @@ func (c *Client) GetTEE(teeId [32]byte) (*TEEInfo, error) { {Name: "tlsCertificate", Type: "bytes"}, {Name: "pcrHash", Type: "bytes32"}, {Name: "teeType", Type: "uint8"}, - {Name: "isActive", Type: "bool"}, + {Name: "enabled", Type: "bool"}, {Name: "registeredAt", Type: "uint256"}, - {Name: "lastUpdatedAt", Type: "uint256"}, + {Name: "lastHeartbeatAt", Type: "uint256"}, }) if err != nil { return nil, fmt.Errorf("failed to create ABI type: %v", err) @@ -196,9 +183,9 @@ func (c *Client) GetTEE(teeId [32]byte) (*TEEInfo, error) { TlsCertificate []byte `json:"tlsCertificate"` PcrHash [32]byte `json:"pcrHash"` TeeType uint8 `json:"teeType"` - IsActive bool `json:"isActive"` + Enabled bool `json:"enabled"` RegisteredAt *big.Int `json:"registeredAt"` - LastUpdatedAt *big.Int `json:"lastUpdatedAt"` + LastHeartbeatAt *big.Int `json:"lastHeartbeatAt"` }) return &TEEInfo{ @@ -209,9 +196,9 @@ func (c *Client) GetTEE(teeId [32]byte) (*TEEInfo, error) { TLSCertificate: s.TlsCertificate, PCRHash: s.PcrHash, TEEType: s.TeeType, - IsActive: s.IsActive, + IsEnabled: s.Enabled, RegisteredAt: time.Unix(s.RegisteredAt.Int64(), 0), - LastUpdatedAt: time.Unix(s.LastUpdatedAt.Int64(), 0), + LastHeartbeatAt: time.Unix(s.LastHeartbeatAt.Int64(), 0), }, nil } @@ -226,31 +213,57 @@ func (c *Client) RegisterTEE(from string, attestation, signingKey, tlsCert []byt return c.sendTx(from, append(selRegisterTEE, encoded...)) } -func (c *Client) DeactivateTEE(from string, teeId [32]byte) (string, error) { - return c.sendTx(from, encodeBytes32(selDeactivateTEE, teeId)) +func (c *Client) DisableTEE(from string, teeId [32]byte) (string, error) { + return c.sendTx(from, encodeBytes32(selDisableTEE, teeId)) } -func (c *Client) ActivateTEE(from string, teeId [32]byte) (string, error) { - return c.sendTx(from, encodeBytes32(selActivateTEE, teeId)) +func (c *Client) EnableTEE(from string, teeId [32]byte) (string, error) { + return c.sendTx(from, encodeBytes32(selEnableTEE, teeId)) } -func (c *Client) RemoveTEE(from string, teeId [32]byte) (string, error) { - return c.sendTx(from, encodeBytes32(selRemoveTEE, teeId)) -} -func (c *Client) IsLive(teeId [32]byte) (bool, error) { - result, err := c.ethCall(encodeBytes32(selIsLive, teeId)) +func (c *Client) IsTEEActive(teeId [32]byte) (bool, error) { + result, err := c.ethCall(encodeBytes32(selIsTEEActive, teeId)) return len(result) >= 32 && result[31] == 1, err } // PCR Calls -func (c *Client) GetActivePCRs() ([]string, error) { - result, err := c.ethCall(selGetActivePCRs) +type PCRKey struct { + PCRHash [32]byte + TEEType uint8 +} + +func (c *Client) GetApprovedPCRs() ([]PCRKey, error) { + result, err := c.ethCall(selGetApprovedPCRs) if err != nil { return nil, err } - return decodeBytes32Array(result) + + tupleT, _ := abi.NewType("tuple[]", "", []abi.ArgumentMarshaling{ + {Name: "pcrHash", Type: "bytes32"}, + {Name: "teeType", Type: "uint8"}, + }) + args := abi.Arguments{{Type: tupleT}} + values, err := args.Unpack(result) + if err != nil { + return nil, fmt.Errorf("failed to decode approved PCRs: %v", err) + } + + if len(values) == 0 { + return nil, nil + } + + items := values[0].([]struct { + PcrHash [32]byte `json:"pcrHash"` + TeeType uint8 `json:"teeType"` + }) + + keys := make([]PCRKey, len(items)) + for i, item := range items { + keys[i] = PCRKey{PCRHash: item.PcrHash, TEEType: item.TeeType} + } + return keys, nil } func (c *Client) ComputePCRHash(pcr0, pcr1, pcr2 []byte) ([32]byte, error) { @@ -279,12 +292,11 @@ func (c *Client) ApprovePCR(from string, pcr0, pcr1, pcr2 []byte, version string return c.sendTx(from, append(selApprovePCR, encoded...)) } -func (c *Client) RevokePCR(from string, pcrHash [32]byte, teeType uint8, gracePeriod *big.Int) (string, error) { +func (c *Client) RevokePCR(from string, pcrHash [32]byte, teeType uint8) (string, error) { b32T, _ := abi.NewType("bytes32", "", nil) u8T, _ := abi.NewType("uint8", "", nil) - u256T, _ := abi.NewType("uint256", "", nil) - args := abi.Arguments{{Type: b32T}, {Type: u8T}, {Type: u256T}} - encoded, _ := args.Pack(pcrHash, teeType, gracePeriod) + args := abi.Arguments{{Type: b32T}, {Type: u8T}} + encoded, _ := args.Pack(pcrHash, teeType) return c.sendTx(from, append(selRevokePCR, encoded...)) } @@ -312,12 +324,6 @@ func (c *Client) AddTEEType(from string, typeId uint8, name string) (string, err return c.sendTx(from, append(selAddTEEType, encoded...)) } -func (c *Client) DeactivateTEEType(from string, typeId uint8) (string, error) { - u8T, _ := abi.NewType("uint8", "", nil) - encoded, _ := abi.Arguments{{Type: u8T}}.Pack(typeId) - return c.sendTx(from, append(selDeactivateTEETyp, encoded...)) -} - // Certificate Calls func (c *Client) SetAWSRootCertificate(from string, cert []byte) (string, error) { diff --git a/tests/solidity/suites/tee/contracts/MockTEERegistry.sol b/tests/solidity/suites/tee/contracts/MockTEERegistry.sol index c77a37c3..c267b69b 100644 --- a/tests/solidity/suites/tee/contracts/MockTEERegistry.sol +++ b/tests/solidity/suites/tee/contracts/MockTEERegistry.sol @@ -5,13 +5,13 @@ import "./cosmos/TEERegistry.sol"; /// @title MockTEERegistry /// @notice Test-only TEERegistry that allows registering TEEs without attestation verification -/// @dev Bypasses the precompile call. Registers TEE as inactive, then call activateTEE() to add to active list. +/// @dev Bypasses the precompile call. Registers TEE as inactive, then call enableTEE() to add to enabled list. contract MockTEERegistry is TEERegistry { /// @notice Register a TEE directly for testing, bypassing attestation verification. - /// @dev The TEE is registered as INACTIVE. Call activateTEE(teeId) afterward from the - /// same account to add it to the active list. This two-step approach is needed because - /// _activeTEEList and related indexes are private in the parent contract. + /// @dev The TEE is registered as INACTIVE. Call enableTEE(teeId) afterward from the + /// same account to add it to the enabled list. This two-step approach is needed because + /// _enabledTEEList and related indexes are private in the parent contract. function registerTEEForTesting( bytes calldata signingPublicKey, bytes calldata tlsCertificate, @@ -25,7 +25,7 @@ contract MockTEERegistry is TEERegistry { teeId = keccak256(signingPublicKey); if (tees[teeId].registeredAt != 0) revert TEEAlreadyExists(); - // Store TEE as inactive; caller must call activateTEE(teeId) to add to active list + // Store TEE as inactive; caller must call enableTEE(teeId) to add to enabled list tees[teeId] = TEEInfo({ owner: msg.sender, paymentAddress: paymentAddress, @@ -34,9 +34,9 @@ contract MockTEERegistry is TEERegistry { tlsCertificate: tlsCertificate, pcrHash: pcrHash, teeType: teeType, - active: false, + enabled: false, registeredAt: block.timestamp, - lastUpdatedAt: block.timestamp + lastHeartbeatAt: block.timestamp }); // Add to indexes (matching registerTEE behavior) diff --git a/tests/solidity/suites/tee/contracts/TEETestHelper.sol b/tests/solidity/suites/tee/contracts/TEETestHelper.sol index d9d170e3..4d522911 100644 --- a/tests/solidity/suites/tee/contracts/TEETestHelper.sol +++ b/tests/solidity/suites/tee/contracts/TEETestHelper.sol @@ -24,10 +24,6 @@ contract TEETestHelper { registry.addTEEType(typeId, name); } - function deactivateTEEType(uint8 typeId) external { - registry.deactivateTEEType(typeId); - } - function isValidTEEType(uint8 typeId) external view returns (bool) { return registry.isValidTEEType(typeId); } @@ -42,8 +38,8 @@ contract TEETestHelper { registry.approvePCR(pcrs, version, teeType); } - function revokePCR(bytes32 pcrHash, uint8 teeType, uint256 gracePeriod) external { - registry.revokePCR(pcrHash, teeType, gracePeriod); + function revokePCR(bytes32 pcrHash, uint8 teeType) external { + registry.revokePCR(pcrHash, teeType); } function isPCRApproved(uint8 teeType, bytes32 pcrHash) external view returns (bool) { @@ -102,16 +98,12 @@ contract TEETestHelper { // ============ TEE Management Wrappers ============ - function deactivateTEE(bytes32 teeId) external { - registry.deactivateTEE(teeId); - } - - function activateTEE(bytes32 teeId) external { - registry.activateTEE(teeId); + function disableTEE(bytes32 teeId) external { + registry.disableTEE(teeId); } - function removeTEE(bytes32 teeId) external { - registry.removeTEE(teeId); + function enableTEE(bytes32 teeId) external { + registry.enableTEE(teeId); } // ============ Verification Wrappers ============ @@ -130,12 +122,12 @@ contract TEETestHelper { return registry.getTEE(teeId); } - function getActivatedTEEs(uint8 teeType) external view returns (bytes32[] memory) { - return registry.getActivatedTEEs(teeType); + function getEnabledTEEs(uint8 teeType) external view returns (bytes32[] memory) { + return registry.getEnabledTEEs(teeType); } - function getLiveTEEs(uint8 teeType) external view returns (TEERegistry.TEEInfo[] memory) { - return registry.getLiveTEEs(teeType); + function getActiveTEEs(uint8 teeType) external view returns (TEERegistry.TEEInfo[] memory) { + return registry.getActiveTEEs(teeType); } function getTEEsByType(uint8 teeType) external view returns (bytes32[] memory) { diff --git a/tests/solidity/suites/tee/test/lifecycle.js b/tests/solidity/suites/tee/test/lifecycle.js index 49bd92ed..cf11d903 100644 --- a/tests/solidity/suites/tee/test/lifecycle.js +++ b/tests/solidity/suites/tee/test/lifecycle.js @@ -30,7 +30,7 @@ contract('TEERegistry Lifecycle & Queries', function (accounts) { // Add TEE type and approve PCR measurements await registry.addTEEType(TEE_TYPE_NITRO, 'AWS Nitro') - // Approve the PCR so activateTEE() passes _requirePCRValidForTEE + // Approve the PCR so enableTEE() passes _requirePCRValidForTEE const pcrs = { pcr0: '0x' + Buffer.alloc(48, 0x01).toString('hex'), pcr1: '0x' + Buffer.alloc(48, 0x02).toString('hex'), @@ -61,88 +61,87 @@ contract('TEERegistry Lifecycle & Queries', function (accounts) { console.log('Setup complete') }) - // Helper: register and activate a TEE via mock - async function registerAndActivate(pubKey, tlsCert, paymentAddr, endpoint, teeType, pcrHash, from) { + // Helper: register and enable a TEE via mock + async function registerAndEnable(pubKey, tlsCert, paymentAddr, endpoint, teeType, pcrHash, from) { const result = await registry.registerTEEForTesting( pubKey, tlsCert, paymentAddr, endpoint, teeType, pcrHash, { from } ) const teeId = await registry.computeTEEId(pubKey) - // activateTEE must be called by the owner (msg.sender of registerTEEForTesting) - await registry.activateTEE(teeId, { from }) + // enableTEE must be called by the owner (msg.sender of registerTEEForTesting) + await registry.enableTEE(teeId, { from }) return teeId } // Helper: check active status via getTEE - async function isActive(teeId) { + async function isTEEEnabled(teeId) { const tee = await registry.getTEE(teeId) - return tee.active + return tee.enabled } - // ============ deactivateTEE Tests ============ + // ============ disableTEE Tests ============ - describe('deactivateTEE', function () { + describe('disableTEE', function () { let localTeeId before(async function () { - // Register and activate TEE 1 via teeOperator (who is the owner) - localTeeId = await registerAndActivate( + // Register and enable TEE 1 via teeOperator (who is the owner) + localTeeId = await registerAndEnable( publicKey1, tlsCert1, user1, ENDPOINT, TEE_TYPE_NITRO, PCR_HASH, teeOperator ) }) - it('should allow owner to deactivate their TEE', async function () { - const result = await registry.deactivateTEE(localTeeId, { from: teeOperator }) + it('should allow owner to disable their TEE', async function () { + const result = await registry.disableTEE(localTeeId, { from: teeOperator }) - truffleAssert.eventEmitted(result, 'TEEDeactivated', (ev) => { + truffleAssert.eventEmitted(result, 'TEEDisabled', (ev) => { return ev.teeId === localTeeId }) - expect(await isActive(localTeeId)).to.be.false + expect(await isTEEEnabled(localTeeId)).to.be.false - console.log('✓ Owner deactivated TEE') + console.log('✓ Owner disabled TEE') }) - it('should remove TEE from activated list after deactivation', async function () { - const activatedTEEs = await registry.getActivatedTEEs(TEE_TYPE_NITRO) - const found = activatedTEEs.some(id => id === localTeeId) + it('should remove TEE from enabled list after disabling', async function () { + const enabledTEEs = await registry.getEnabledTEEs(TEE_TYPE_NITRO) + const found = enabledTEEs.some(id => id === localTeeId) expect(found).to.be.false - console.log('✓ TEE removed from activated list') + console.log('✓ TEE removed from enabled list') }) - it('should be a no-op when deactivating already inactive TEE', async function () { + it('should revert when disabling already disabled TEE', async function () { // TEE is already inactive from the previous test - const result = await registry.deactivateTEE(localTeeId, { from: teeOperator }) - - // No event should be emitted for a no-op - truffleAssert.eventNotEmitted(result, 'TEEDeactivated') + await truffleAssert.reverts( + registry.disableTEE(localTeeId, { from: teeOperator }) + ) - console.log('✓ Double-deactivation is a no-op') + console.log('✓ Double-disabling reverts with TEENotEnabled') }) - it('should allow admin to deactivate any TEE', async function () { - // Re-activate first - await registry.activateTEE(localTeeId, { from: teeOperator }) - expect(await isActive(localTeeId)).to.be.true + it('should allow admin to disable any TEE', async function () { + // Re-enable first + await registry.enableTEE(localTeeId, { from: teeOperator }) + expect(await isTEEEnabled(localTeeId)).to.be.true - // Admin (owner/accounts[0]) deactivates - const result = await registry.deactivateTEE(localTeeId, { from: owner }) + // Admin (owner/accounts[0]) disables + const result = await registry.disableTEE(localTeeId, { from: owner }) - truffleAssert.eventEmitted(result, 'TEEDeactivated', (ev) => { + truffleAssert.eventEmitted(result, 'TEEDisabled', (ev) => { return ev.teeId === localTeeId }) - expect(await isActive(localTeeId)).to.be.false + expect(await isTEEEnabled(localTeeId)).to.be.false - console.log('✓ Admin deactivated TEE') + console.log('✓ Admin disabled TEE') }) - it('should revert when non-owner/non-admin deactivates', async function () { - // Re-activate first - await registry.activateTEE(localTeeId, { from: teeOperator }) + it('should revert when non-owner/non-admin disables', async function () { + // Re-enable first + await registry.enableTEE(localTeeId, { from: teeOperator }) await truffleAssert.reverts( - registry.deactivateTEE(localTeeId, { from: user1 }) + registry.disableTEE(localTeeId, { from: user1 }) ) console.log('✓ Non-owner/non-admin rejected') @@ -152,16 +151,16 @@ contract('TEERegistry Lifecycle & Queries', function (accounts) { const fakeTeeId = web3.utils.keccak256('0xDEADBEEF') await truffleAssert.reverts( - registry.deactivateTEE(fakeTeeId, { from: owner }) + registry.disableTEE(fakeTeeId, { from: owner }) ) console.log('✓ Non-existent teeId reverts') }) }) - // ============ activateTEE Tests ============ + // ============ enableTEE Tests ============ - describe('activateTEE', function () { + describe('enableTEE', function () { let localTeeId before(async function () { @@ -172,62 +171,62 @@ contract('TEERegistry Lifecycle & Queries', function (accounts) { localTeeId = teeId2 }) - it('should allow owner to activate their deactivated TEE', async function () { + it('should allow owner to enable their disabled TEE', async function () { // TEE starts inactive from mock registration - expect(await isActive(localTeeId)).to.be.false + expect(await isTEEEnabled(localTeeId)).to.be.false - const result = await registry.activateTEE(localTeeId, { from: teeOperator }) + const result = await registry.enableTEE(localTeeId, { from: teeOperator }) - truffleAssert.eventEmitted(result, 'TEEActivated', (ev) => { + truffleAssert.eventEmitted(result, 'TEEEnabled', (ev) => { return ev.teeId === localTeeId }) - expect(await isActive(localTeeId)).to.be.true + expect(await isTEEEnabled(localTeeId)).to.be.true - console.log('✓ Owner activated TEE') + console.log('✓ Owner enabled TEE') }) - it('should add TEE back to activated list after activation', async function () { - const activatedTEEs = await registry.getActivatedTEEs(TEE_TYPE_NITRO) - const found = activatedTEEs.some(id => id === localTeeId) + it('should add TEE back to enabled list after enabling', async function () { + const enabledTEEs = await registry.getEnabledTEEs(TEE_TYPE_NITRO) + const found = enabledTEEs.some(id => id === localTeeId) expect(found).to.be.true - console.log('✓ TEE added to activated list') + console.log('✓ TEE added to enabled list') }) - it('should be a no-op when activating already active TEE', async function () { + it('should be a no-op when enabling already enabled TEE', async function () { // TEE is already active from the previous test - const result = await registry.activateTEE(localTeeId, { from: teeOperator }) + const result = await registry.enableTEE(localTeeId, { from: teeOperator }) // No event should be emitted for a no-op - truffleAssert.eventNotEmitted(result, 'TEEActivated') + truffleAssert.eventNotEmitted(result, 'TEEEnabled') - console.log('✓ Double-activation is a no-op') + console.log('✓ Double-enable is a no-op') }) - it('should allow admin to activate any TEE', async function () { - // Deactivate first - await registry.deactivateTEE(localTeeId, { from: teeOperator }) - expect(await isActive(localTeeId)).to.be.false + it('should allow admin to enable any TEE', async function () { + // Disable first + await registry.disableTEE(localTeeId, { from: teeOperator }) + expect(await isTEEEnabled(localTeeId)).to.be.false - // Admin (owner/accounts[0]) activates - const result = await registry.activateTEE(localTeeId, { from: owner }) + // Admin (owner/accounts[0]) enables + const result = await registry.enableTEE(localTeeId, { from: owner }) - truffleAssert.eventEmitted(result, 'TEEActivated', (ev) => { + truffleAssert.eventEmitted(result, 'TEEEnabled', (ev) => { return ev.teeId === localTeeId }) - expect(await isActive(localTeeId)).to.be.true + expect(await isTEEEnabled(localTeeId)).to.be.true - console.log('✓ Admin activated TEE') + console.log('✓ Admin enabled TEE') }) - it('should revert when non-owner/non-admin activates', async function () { - // Deactivate first - await registry.deactivateTEE(localTeeId, { from: teeOperator }) + it('should revert when non-owner/non-admin enables', async function () { + // Disable first + await registry.disableTEE(localTeeId, { from: teeOperator }) await truffleAssert.reverts( - registry.activateTEE(localTeeId, { from: user1 }) + registry.enableTEE(localTeeId, { from: user1 }) ) console.log('✓ Non-owner/non-admin rejected') @@ -237,7 +236,7 @@ contract('TEERegistry Lifecycle & Queries', function (accounts) { const fakeTeeId = web3.utils.keccak256('0xDEADBEEF') await truffleAssert.reverts( - registry.activateTEE(fakeTeeId, { from: owner }) + registry.enableTEE(fakeTeeId, { from: owner }) ) console.log('✓ Non-existent teeId reverts') @@ -246,7 +245,7 @@ contract('TEERegistry Lifecycle & Queries', function (accounts) { // ============ PCR Enforcement Tests ============ - describe('PCR enforcement on activateTEE', function () { + describe('PCR enforcement on enableTEE', function () { let pcrTeeId, pcrPublicKey before(async function () { @@ -257,14 +256,14 @@ contract('TEERegistry Lifecycle & Queries', function (accounts) { pcrPublicKey = '0x' + keyPair.publicKey.toString('hex') }) - it('should revert activateTEE when PCR is immediately revoked', async function () { + it('should revert enableTEE when PCR is immediately revoked', async function () { // Approve a fresh PCR const pcrs = { pcr0: '0x' + Buffer.alloc(48, 0xA1).toString('hex'), pcr1: '0x' + Buffer.alloc(48, 0xA2).toString('hex'), pcr2: '0x' + Buffer.alloc(48, 0xA3).toString('hex') } - await registry.approvePCR(pcrs, 'v-revoke-activate', TEE_TYPE_NITRO) + await registry.approvePCR(pcrs, 'v-revoke-enable', TEE_TYPE_NITRO) const pcrHash = await registry.computePCRHash(pcrs) // Register TEE (inactive) with this PCR @@ -273,27 +272,27 @@ contract('TEERegistry Lifecycle & Queries', function (accounts) { ) pcrTeeId = await registry.computeTEEId(pcrPublicKey) - // Revoke the PCR immediately - await registry.revokePCR(pcrHash, TEE_TYPE_NITRO, 0) + // Revoke the PCR + await registry.revokePCR(pcrHash, TEE_TYPE_NITRO) expect(await registry.isPCRApproved(TEE_TYPE_NITRO, pcrHash)).to.be.false - // activateTEE should revert with PCRNotApproved + // enableTEE should revert with PCRNotApproved await truffleAssert.reverts( - registry.activateTEE(pcrTeeId, { from: teeOperator }) + registry.enableTEE(pcrTeeId, { from: teeOperator }) ) - expect(await isActive(pcrTeeId)).to.be.false + expect(await isTEEEnabled(pcrTeeId)).to.be.false - console.log('✓ activateTEE reverts when PCR is revoked') + console.log('✓ enableTEE reverts when PCR is revoked') }) - it('should revert activateTEE when PCR grace period has expired', async function () { + it('should revert enableTEE when PCR is revoked (not just immediate)', async function () { const pcrs = { pcr0: '0x' + Buffer.alloc(48, 0xB1).toString('hex'), pcr1: '0x' + Buffer.alloc(48, 0xB2).toString('hex'), pcr2: '0x' + Buffer.alloc(48, 0xB3).toString('hex') } - await registry.approvePCR(pcrs, 'v-expire-activate', TEE_TYPE_NITRO) + await registry.approvePCR(pcrs, 'v-revoke-enable-2', TEE_TYPE_NITRO) const pcrHash = await registry.computePCRHash(pcrs) const keyPair = crypto.generateKeyPairSync('rsa', { @@ -308,29 +307,21 @@ contract('TEERegistry Lifecycle & Queries', function (accounts) { ) const teeId = await registry.computeTEEId(pubKey) - // Revoke with grace period of 1 second - await registry.revokePCR(pcrHash, TEE_TYPE_NITRO, 1) - - // Still valid during grace period - expect(await registry.isPCRApproved(TEE_TYPE_NITRO, pcrHash)).to.be.true - - // Mine a block to advance timestamp past the grace period - await registry.setAWSRootCertificate('0x01') - - // Now expired + // Revoke the PCR + await registry.revokePCR(pcrHash, TEE_TYPE_NITRO) expect(await registry.isPCRApproved(TEE_TYPE_NITRO, pcrHash)).to.be.false - // activateTEE should revert with PCRExpired + // enableTEE should revert with PCRNotApproved await truffleAssert.reverts( - registry.activateTEE(teeId, { from: teeOperator }) + registry.enableTEE(teeId, { from: teeOperator }) ) - expect(await isActive(teeId)).to.be.false + expect(await isTEEEnabled(teeId)).to.be.false - console.log('✓ activateTEE reverts when PCR grace period expired') + console.log('✓ enableTEE reverts when PCR is revoked') }) - it('should revert activateTEE when PCR type does not match TEE type', async function () { + it('should revert enableTEE when PCR type does not match TEE type', async function () { // Add a second TEE type const TEE_TYPE_OTHER = 3 await registry.addTEEType(TEE_TYPE_OTHER, 'Other TEE') @@ -356,26 +347,26 @@ contract('TEERegistry Lifecycle & Queries', function (accounts) { ) const teeId = await registry.computeTEEId(pubKey) - // activateTEE should revert + // enableTEE should revert await truffleAssert.reverts( - registry.activateTEE(teeId, { from: teeOperator }) + registry.enableTEE(teeId, { from: teeOperator }) ) - expect(await isActive(teeId)).to.be.false + expect(await isTEEEnabled(teeId)).to.be.false - console.log('✓ activateTEE reverts on PCR type mismatch') + console.log('✓ enableTEE reverts on PCR type mismatch') }) }) - describe('PCR enforcement on heartbeat', function () { - it('should revert heartbeat when PCR is revoked after activation', async function () { + describe('PCR revocation actively disables TEEs', function () { + it('should disable enabled TEE when its PCR is revoked', async function () { // Approve a fresh PCR const pcrs = { pcr0: '0x' + Buffer.alloc(48, 0xD1).toString('hex'), pcr1: '0x' + Buffer.alloc(48, 0xD2).toString('hex'), pcr2: '0x' + Buffer.alloc(48, 0xD3).toString('hex') } - await registry.approvePCR(pcrs, 'v-heartbeat-revoke', TEE_TYPE_NITRO) + await registry.approvePCR(pcrs, 'v-active-revoke', TEE_TYPE_NITRO) const pcrHash = await registry.computePCRHash(pcrs) const keyPair = crypto.generateKeyPairSync('rsa', { @@ -384,92 +375,95 @@ contract('TEERegistry Lifecycle & Queries', function (accounts) { }) const pubKey = '0x' + keyPair.publicKey.toString('hex') - // Register and activate TEE with valid PCR + // Register and enable TEE with valid PCR await registry.registerTEEForTesting( pubKey, tlsCert1, user1, ENDPOINT, TEE_TYPE_NITRO, pcrHash, { from: teeOperator } ) const teeId = await registry.computeTEEId(pubKey) - await registry.activateTEE(teeId, { from: teeOperator }) - expect(await isActive(teeId)).to.be.true + await registry.enableTEE(teeId, { from: teeOperator }) + expect(await isTEEEnabled(teeId)).to.be.true - // Now revoke the PCR - await registry.revokePCR(pcrHash, TEE_TYPE_NITRO, 0) - expect(await registry.isPCRApproved(TEE_TYPE_NITRO, pcrHash)).to.be.false + // Revoke the PCR - TEE should be actively disabled + const result = await registry.revokePCR(pcrHash, TEE_TYPE_NITRO) - // TEE is still marked active in storage (lazy enforcement) - expect(await isActive(teeId)).to.be.true + // TEE should now be disabled + expect(await isTEEEnabled(teeId)).to.be.false - // heartbeat should revert at _requirePCRValidForTEE before reaching signature check - const timestamp = Math.floor(Date.now() / 1000) - const dummySignature = '0x' + Buffer.alloc(256, 0xFF).toString('hex') + // TEE should be removed from enabled list + const enabledTEEs = await registry.getEnabledTEEs(TEE_TYPE_NITRO) + expect(enabledTEEs.some(id => id === teeId)).to.be.false - await truffleAssert.reverts( - registry.heartbeat(teeId, timestamp, dummySignature) - ) + // TEEDisabled event should be emitted + truffleAssert.eventEmitted(result, 'TEEDisabled', (ev) => { + return ev.teeId === teeId + }) - console.log('✓ heartbeat reverts when PCR is revoked (lazy enforcement)') + console.log('✓ TEE disabled when PCR is revoked') }) - it('should revert heartbeat when PCR grace period has expired', async function () { + it('should disable multiple TEEs when their shared PCR is revoked', async function () { // Approve a fresh PCR const pcrs = { pcr0: '0x' + Buffer.alloc(48, 0xE1).toString('hex'), pcr1: '0x' + Buffer.alloc(48, 0xE2).toString('hex'), pcr2: '0x' + Buffer.alloc(48, 0xE3).toString('hex') } - await registry.approvePCR(pcrs, 'v-heartbeat-expire', TEE_TYPE_NITRO) + await registry.approvePCR(pcrs, 'v-multi-revoke', TEE_TYPE_NITRO) const pcrHash = await registry.computePCRHash(pcrs) - const keyPair = crypto.generateKeyPairSync('rsa', { + // Register and enable two TEEs with the same PCR + const keyPair1 = crypto.generateKeyPairSync('rsa', { modulusLength: 2048, publicKeyEncoding: { type: 'spki', format: 'der' } }) - const pubKey = '0x' + keyPair.publicKey.toString('hex') + const pubKey1 = '0x' + keyPair1.publicKey.toString('hex') + + const keyPair2 = crypto.generateKeyPairSync('rsa', { + modulusLength: 2048, + publicKeyEncoding: { type: 'spki', format: 'der' } + }) + const pubKey2 = '0x' + keyPair2.publicKey.toString('hex') - // Register and activate await registry.registerTEEForTesting( - pubKey, tlsCert1, user1, ENDPOINT, TEE_TYPE_NITRO, pcrHash, { from: teeOperator } + pubKey1, tlsCert1, user1, ENDPOINT, TEE_TYPE_NITRO, pcrHash, { from: teeOperator } ) - const teeId = await registry.computeTEEId(pubKey) - await registry.activateTEE(teeId, { from: teeOperator }) - expect(await isActive(teeId)).to.be.true - - // Revoke with 1-second grace period - await registry.revokePCR(pcrHash, TEE_TYPE_NITRO, 1) - - // Mine a block to advance past grace period - await registry.setAWSRootCertificate('0x02') + await registry.registerTEEForTesting( + pubKey2, tlsCert1, user1, ENDPOINT, TEE_TYPE_NITRO, pcrHash, { from: teeOperator } + ) + const teeId1 = await registry.computeTEEId(pubKey1) + const teeId2 = await registry.computeTEEId(pubKey2) - expect(await registry.isPCRApproved(TEE_TYPE_NITRO, pcrHash)).to.be.false + await registry.enableTEE(teeId1, { from: teeOperator }) + await registry.enableTEE(teeId2, { from: teeOperator }) + expect(await isTEEEnabled(teeId1)).to.be.true + expect(await isTEEEnabled(teeId2)).to.be.true - // heartbeat should revert with PCRExpired - const timestamp = Math.floor(Date.now() / 1000) - const dummySignature = '0x' + Buffer.alloc(256, 0xFF).toString('hex') + // Revoke the PCR - both TEEs should be disabled + await registry.revokePCR(pcrHash, TEE_TYPE_NITRO) - await truffleAssert.reverts( - registry.heartbeat(teeId, timestamp, dummySignature) - ) + expect(await isTEEEnabled(teeId1)).to.be.false + expect(await isTEEEnabled(teeId2)).to.be.false - console.log('✓ heartbeat reverts when PCR grace period expired') + console.log('✓ Multiple TEEs disabled when shared PCR is revoked') }) }) // ============ Query Function Tests ============ describe('Query functions', function () { - // At this point teeId1 is registered and active (from deactivateTEE tests, re-activated), + // At this point teeId1 is registered and active (from disableTEE tests, re-enabled), // teeId2 is registered but may be inactive. We'll set known state. before(async function () { // Ensure teeId1 is active try { - await registry.activateTEE(teeId1, { from: teeOperator }) + await registry.enableTEE(teeId1, { from: teeOperator }) } catch (e) { // May already be active, ignore } // Ensure teeId2 is active try { - await registry.activateTEE(teeId2, { from: teeOperator }) + await registry.enableTEE(teeId2, { from: teeOperator }) } catch (e) { // May already be active, ignore } @@ -506,48 +500,48 @@ contract('TEERegistry Lifecycle & Queries', function (accounts) { console.log('✓ getTEE returns correct payment address') }) - it('should return correct activated TEE list', async function () { - const activatedTEEs = await registry.getActivatedTEEs(TEE_TYPE_NITRO) - expect(activatedTEEs.length).to.be.greaterThan(0) + it('should return correct enabled TEE list', async function () { + const enabledTEEs = await registry.getEnabledTEEs(TEE_TYPE_NITRO) + expect(enabledTEEs.length).to.be.greaterThan(0) // Both TEEs should be in the list - expect(activatedTEEs.some(id => id === teeId1)).to.be.true - expect(activatedTEEs.some(id => id === teeId2)).to.be.true + expect(enabledTEEs.some(id => id === teeId1)).to.be.true + expect(enabledTEEs.some(id => id === teeId2)).to.be.true - console.log('✓ getActivatedTEEs returns correct list') + console.log('✓ getEnabledTEEs returns correct list') }) - it('should update activated TEE list after deactivate and activate', async function () { - // Deactivate teeId2 - await registry.deactivateTEE(teeId2, { from: teeOperator }) + it('should update enabled TEE list after disable and enable', async function () { + // Disable teeId2 + await registry.disableTEE(teeId2, { from: teeOperator }) - let activatedTEEs = await registry.getActivatedTEEs(TEE_TYPE_NITRO) - expect(activatedTEEs.some(id => id === teeId2)).to.be.false - expect(activatedTEEs.some(id => id === teeId1)).to.be.true + let enabledTEEs = await registry.getEnabledTEEs(TEE_TYPE_NITRO) + expect(enabledTEEs.some(id => id === teeId2)).to.be.false + expect(enabledTEEs.some(id => id === teeId1)).to.be.true - // Re-activate teeId2 - await registry.activateTEE(teeId2, { from: teeOperator }) + // Re-enable teeId2 + await registry.enableTEE(teeId2, { from: teeOperator }) - activatedTEEs = await registry.getActivatedTEEs(TEE_TYPE_NITRO) - expect(activatedTEEs.some(id => id === teeId2)).to.be.true + enabledTEEs = await registry.getEnabledTEEs(TEE_TYPE_NITRO) + expect(enabledTEEs.some(id => id === teeId2)).to.be.true - console.log('✓ getActivatedTEEs updates after deactivate/activate') + console.log('✓ getEnabledTEEs updates after disable/enable') }) it('should return active status via getTEE', async function () { const tee = await registry.getTEE(teeId1) - expect(tee.active).to.be.true + expect(tee.enabled).to.be.true console.log('✓ getTEE returns active=true for active TEE') }) - it('should return inactive status via getTEE after deactivation', async function () { - await registry.deactivateTEE(teeId1, { from: teeOperator }) + it('should return inactive status via getTEE after disabling', async function () { + await registry.disableTEE(teeId1, { from: teeOperator }) const tee = await registry.getTEE(teeId1) - expect(tee.active).to.be.false + expect(tee.enabled).to.be.false - // Re-activate for subsequent tests - await registry.activateTEE(teeId1, { from: teeOperator }) + // Re-enable for subsequent tests + await registry.enableTEE(teeId1, { from: teeOperator }) console.log('✓ getTEE returns active=false for inactive TEE') }) @@ -562,147 +556,24 @@ contract('TEERegistry Lifecycle & Queries', function (accounts) { console.log('✓ getTEEsByType returns all TEEs including inactive') }) - it('should return live TEEs filtered by heartbeat and PCR', async function () { - const liveTEEs = await registry.getLiveTEEs(TEE_TYPE_NITRO) - // liveTEEs returns TEEInfo structs, check they have valid fields - for (let i = 0; i < liveTEEs.length; i++) { - expect(liveTEEs[i].active).to.be.true - expect(Number(liveTEEs[i].registeredAt)).to.be.greaterThan(0) + it('should return active TEEs filtered by heartbeat and PCR', async function () { + const activeTEEs = await registry.getActiveTEEs(TEE_TYPE_NITRO) + // activeTEEs returns TEEInfo structs, check they have valid fields + for (let i = 0; i < activeTEEs.length; i++) { + expect(activeTEEs[i].enabled).to.be.true + expect(Number(activeTEEs[i].registeredAt)).to.be.greaterThan(0) } - console.log('Live TEEs count:', liveTEEs.length) - console.log('✓ getLiveTEEs returns filtered results') + console.log('Active TEEs count:', activeTEEs.length) + console.log('✓ getActiveTEEs returns filtered results') }) - it('should return empty from getLiveTEEs for unused type', async function () { - const liveTEEs = await registry.getLiveTEEs(50) - expect(liveTEEs.length).to.equal(0) + it('should return empty from getActiveTEEs for unused type', async function () { + const activeTEEs = await registry.getActiveTEEs(50) + expect(activeTEEs.length).to.equal(0) - console.log('✓ getLiveTEEs returns empty for unused type') + console.log('✓ getActiveTEEs returns empty for unused type') }) }) - // ============ removeTEE Tests ============ - - describe('removeTEE', function () { - let removeTeeId, removePubKey - - before(async function () { - const keyPair = crypto.generateKeyPairSync('rsa', { - modulusLength: 2048, - publicKeyEncoding: { type: 'spki', format: 'der' } - }) - removePubKey = '0x' + keyPair.publicKey.toString('hex') - removeTeeId = await registry.computeTEEId(removePubKey) - }) - - it('should allow owner to remove their active TEE', async function () { - // Register and activate - await registerAndActivate( - removePubKey, tlsCert1, user1, ENDPOINT, TEE_TYPE_NITRO, PCR_HASH, teeOperator - ) - expect(await isActive(removeTeeId)).to.be.true - - // Verify it's in all indexes before removal - let activatedTEEs = await registry.getActivatedTEEs(TEE_TYPE_NITRO) - expect(activatedTEEs.some(id => id === removeTeeId)).to.be.true - let byType = await registry.getTEEsByType(TEE_TYPE_NITRO) - expect(byType.some(id => id === removeTeeId)).to.be.true - let byOwner = await registry.getTEEsByOwner(teeOperator) - expect(byOwner.some(id => id === removeTeeId)).to.be.true - - // Remove - const result = await registry.removeTEE(removeTeeId, { from: teeOperator }) - truffleAssert.eventEmitted(result, 'TEERemoved', (ev) => { - return ev.teeId === removeTeeId - }) - - // getTEE should revert (deleted) - await truffleAssert.reverts(registry.getTEE(removeTeeId)) - - // Removed from all indexes - activatedTEEs = await registry.getActivatedTEEs(TEE_TYPE_NITRO) - expect(activatedTEEs.some(id => id === removeTeeId)).to.be.false - byType = await registry.getTEEsByType(TEE_TYPE_NITRO) - expect(byType.some(id => id === removeTeeId)).to.be.false - byOwner = await registry.getTEEsByOwner(teeOperator) - expect(byOwner.some(id => id === removeTeeId)).to.be.false - - console.log('✓ Owner removed active TEE from all storage') - }) - - it('should allow owner to remove their inactive TEE', async function () { - const keyPair = crypto.generateKeyPairSync('rsa', { - modulusLength: 2048, - publicKeyEncoding: { type: 'spki', format: 'der' } - }) - const pubKey = '0x' + keyPair.publicKey.toString('hex') - const teeId = await registry.computeTEEId(pubKey) - - // Register but don't activate - await registry.registerTEEForTesting( - pubKey, tlsCert1, user1, ENDPOINT, TEE_TYPE_NITRO, PCR_HASH, { from: teeOperator } - ) - expect(await isActive(teeId)).to.be.false - - // Remove - const result = await registry.removeTEE(teeId, { from: teeOperator }) - truffleAssert.eventEmitted(result, 'TEERemoved') - - // getTEE should revert - await truffleAssert.reverts(registry.getTEE(teeId)) - - console.log('✓ Owner removed inactive TEE') - }) - - it('should allow admin to remove any TEE', async function () { - const keyPair = crypto.generateKeyPairSync('rsa', { - modulusLength: 2048, - publicKeyEncoding: { type: 'spki', format: 'der' } - }) - const pubKey = '0x' + keyPair.publicKey.toString('hex') - - await registerAndActivate( - pubKey, tlsCert1, user1, ENDPOINT, TEE_TYPE_NITRO, PCR_HASH, teeOperator - ) - const teeId = await registry.computeTEEId(pubKey) - - // Admin removes (owner is teeOperator, caller is admin/owner) - const result = await registry.removeTEE(teeId, { from: owner }) - truffleAssert.eventEmitted(result, 'TEERemoved') - - await truffleAssert.reverts(registry.getTEE(teeId)) - - console.log('✓ Admin removed TEE') - }) - - it('should revert when non-owner/non-admin removes', async function () { - const keyPair = crypto.generateKeyPairSync('rsa', { - modulusLength: 2048, - publicKeyEncoding: { type: 'spki', format: 'der' } - }) - const pubKey = '0x' + keyPair.publicKey.toString('hex') - - await registerAndActivate( - pubKey, tlsCert1, user1, ENDPOINT, TEE_TYPE_NITRO, PCR_HASH, teeOperator - ) - const teeId = await registry.computeTEEId(pubKey) - - await truffleAssert.reverts( - registry.removeTEE(teeId, { from: user1 }) - ) - - console.log('✓ Non-owner/non-admin rejected') - }) - - it('should revert for non-existent teeId', async function () { - const fakeTeeId = web3.utils.keccak256('0xREMOVEFAKE') - - await truffleAssert.reverts( - registry.removeTEE(fakeTeeId, { from: owner }) - ) - - console.log('✓ Non-existent teeId reverts') - }) - }) }) diff --git a/tests/solidity/suites/tee/test/registry.js b/tests/solidity/suites/tee/test/registry.js index 694944d0..43b5f236 100644 --- a/tests/solidity/suites/tee/test/registry.js +++ b/tests/solidity/suites/tee/test/registry.js @@ -62,7 +62,7 @@ contract('TEERegistry', function (accounts) { const typeInfo = await registry.teeTypes(TYPE_AWS_NITRO) expect(typeInfo.name).to.equal('AWS Nitro') - expect(typeInfo.active).to.be.true + expect(typeInfo.addedAt.toNumber()).to.be.greaterThan(0) console.log('✓ TEE type added successfully') }) @@ -83,29 +83,6 @@ contract('TEERegistry', function (accounts) { console.log('✓ Non-admin cannot add TEE type') }) - it('should allow admin to deactivate TEE type', async function () { - await registry.addTEEType(TYPE_CUSTOM, 'Custom TEE') - expect(await registry.isValidTEEType(TYPE_CUSTOM)).to.be.true - - const result = await registry.deactivateTEEType(TYPE_CUSTOM) - - truffleAssert.eventEmitted(result, 'TEETypeDeactivated', (ev) => { - return ev.typeId.toString() === TYPE_CUSTOM.toString() - }) - - expect(await registry.isValidTEEType(TYPE_CUSTOM)).to.be.false - - console.log('✓ TEE type deactivated successfully') - }) - - it('should reject non-admin deactivating TEE type', async function () { - await truffleAssert.reverts( - registry.deactivateTEEType(TYPE_AWS_NITRO, { from: user1 }) - ) - - console.log('✓ Non-admin cannot deactivate TEE type') - }) - it('should list all TEE types', async function () { const result = await registry.getTEETypes() const typeIds = result.typeIds || result[0] @@ -144,15 +121,13 @@ contract('TEERegistry', function (accounts) { expect(await registry.isPCRApproved(TEE_TYPE, pcrHash)).to.be.true - const pcrInfo = await registry.approvedPCRs(TEE_TYPE, pcrHash) - expect(pcrInfo.active).to.be.true + const pcrInfo = await registry.pcrRecords(TEE_TYPE, pcrHash) + expect(pcrInfo.approved).to.be.true expect(pcrInfo.version).to.equal('v1.0.0') - expect(pcrInfo.expiresAt.toString()).to.equal('0') - console.log('✓ PCR approved successfully') }) - it('should handle PCR versioning with grace period via revokePCR', async function () { + it('should handle PCR versioning via revokePCR', async function () { const pcrsV2 = { pcr0: '0x' + Buffer.alloc(48, 0x04).toString('hex'), pcr1: '0x' + Buffer.alloc(48, 0x05).toString('hex'), @@ -160,22 +135,18 @@ contract('TEERegistry', function (accounts) { } const pcrHashV2 = await registry.computePCRHash(pcrsV2) - const gracePeriod = 3600 // 1 hour // Approve v2 await registry.approvePCR(pcrsV2, 'v2.0.0', TEE_TYPE) - // Revoke v1 with grace period - await registry.revokePCR(pcrHash, TEE_TYPE, gracePeriod) + // Revoke v1 + await registry.revokePCR(pcrHash, TEE_TYPE) - // Both should be valid during grace period - expect(await registry.isPCRApproved(TEE_TYPE, pcrHash)).to.be.true + // v1 should be revoked, v2 should still be valid + expect(await registry.isPCRApproved(TEE_TYPE, pcrHash)).to.be.false expect(await registry.isPCRApproved(TEE_TYPE, pcrHashV2)).to.be.true - const pcrV1Info = await registry.approvedPCRs(TEE_TYPE, pcrHash) - expect(pcrV1Info.expiresAt.toNumber()).to.be.greaterThan(0) - - console.log('✓ PCR versioning with grace period works') + console.log('✓ PCR versioning works') }) it('should allow admin to revoke PCR', async function () { @@ -190,10 +161,10 @@ contract('TEERegistry', function (accounts) { await registry.approvePCR(pcrsRevoke, 'v1.0.0-revoke', TEE_TYPE) expect(await registry.isPCRApproved(TEE_TYPE, pcrHashRevoke)).to.be.true - const result = await registry.revokePCR(pcrHashRevoke, TEE_TYPE, 0) + const result = await registry.revokePCR(pcrHashRevoke, TEE_TYPE) truffleAssert.eventEmitted(result, 'PCRRevoked', (ev) => { - return ev.pcrHash === pcrHashRevoke && ev.gracePeriod.toString() === '0' + return ev.pcrHash === pcrHashRevoke && ev.teeType.toNumber() === TEE_TYPE }) expect(await registry.isPCRApproved(TEE_TYPE, pcrHashRevoke)).to.be.false @@ -201,12 +172,12 @@ contract('TEERegistry', function (accounts) { console.log('✓ PCR revoked successfully') }) - it('should list active PCRs', async function () { - const activePCRs = await registry.getActivePCRs() + it('should list approved PCRs', async function () { + const approvedPCRs = await registry.getApprovedPCRs() - expect(activePCRs.length).to.be.greaterThan(0) - console.log('Active PCRs count:', activePCRs.length) - console.log('✓ Active PCRs listed successfully') + expect(approvedPCRs.length).to.be.greaterThan(0) + console.log('Approved PCRs count:', approvedPCRs.length) + console.log('✓ Approved PCRs listed successfully') }) it('should reject non-admin approving PCR', async function () { @@ -225,13 +196,13 @@ contract('TEERegistry', function (accounts) { it('should reject non-admin revoking PCR', async function () { await truffleAssert.reverts( - registry.revokePCR(pcrHash, TEE_TYPE, 0, { from: user1 }) + registry.revokePCR(pcrHash, TEE_TYPE, { from: user1 }) ) console.log('✓ Non-admin cannot revoke PCR') }) - it('should reject expired PCR after grace period elapses', async function () { + it('should reject revoked PCR immediately', async function () { const pcrsExpiry = { pcr0: '0x' + Buffer.alloc(48, 0xE1).toString('hex'), pcr1: '0x' + Buffer.alloc(48, 0xE2).toString('hex'), @@ -244,16 +215,13 @@ contract('TEERegistry', function (accounts) { await registry.approvePCR(pcrsExpiry, 'v-expiry', TEE_TYPE) expect(await registry.isPCRApproved(TEE_TYPE, pcrHashExpiry)).to.be.true - // Revoke with gracePeriod=1s - await registry.revokePCR(pcrHashExpiry, TEE_TYPE, 1) - - // Send a dummy transaction to mine a new block with an advanced timestamp - await registry.setAWSRootCertificate('0x01') + // Revoke + await registry.revokePCR(pcrHashExpiry, TEE_TYPE) - // The expiry PCR should now be rejected (block.timestamp > expiresAt) + // The PCR should now be rejected expect(await registry.isPCRApproved(TEE_TYPE, pcrHashExpiry)).to.be.false - console.log('✓ Expired PCR rejected after grace period elapses') + console.log('✓ Revoked PCR rejected immediately') }) }) @@ -413,18 +381,18 @@ contract('TEERegistry', function (accounts) { console.log('✓ Empty array returned for unused TEE type') }) - it('should return empty array from getActivatedTEEs for unused type', async function () { - const tees = await registry.getActivatedTEEs(50) + it('should return empty array from getEnabledTEEs for unused type', async function () { + const tees = await registry.getEnabledTEEs(50) expect(tees.length).to.equal(0) - console.log('✓ Empty array returned from getActivatedTEEs for unused type') + console.log('✓ Empty array returned from getEnabledTEEs for unused type') }) - it('should return empty array from getLiveTEEs for unused type', async function () { - const tees = await registry.getLiveTEEs(50) + it('should return empty array from getActiveTEEs for unused type', async function () { + const tees = await registry.getActiveTEEs(50) expect(tees.length).to.equal(0) - console.log('✓ Empty array returned from getLiveTEEs for unused type') + console.log('✓ Empty array returned from getActiveTEEs for unused type') }) }) @@ -487,13 +455,13 @@ contract('TEERegistry', function (accounts) { return ev.pcrHash === pcrHash && ev.version === 'v-duplicate-test-2' }) - const pcrInfo = await registry.approvedPCRs(TEE_TYPE, pcrHash) + const pcrInfo = await registry.pcrRecords(TEE_TYPE, pcrHash) expect(pcrInfo.version).to.equal('v-duplicate-test-2') - console.log('✓ Re-approval of active PCR updates version') + console.log('✓ Re-approval of approved PCR updates version') }) - it('should allow re-approval to cancel pending grace-period revocation', async function () { + it('should allow re-approval after revocation', async function () { const pcrs = { pcr0: '0x' + Buffer.alloc(48, 0x23).toString('hex'), pcr1: '0x' + Buffer.alloc(48, 0x24).toString('hex'), @@ -501,24 +469,19 @@ contract('TEERegistry', function (accounts) { } const pcrHash = await registry.computePCRHash(pcrs) - // Approve, then revoke with grace period - await registry.approvePCR(pcrs, 'v-grace-cancel', TEE_TYPE) - await registry.revokePCR(pcrHash, TEE_TYPE, 3600) - - // Still valid during grace period, but expiresAt is set - const infoBefore = await registry.approvedPCRs(TEE_TYPE, pcrHash) - expect(infoBefore.expiresAt.toNumber()).to.be.greaterThan(0) + // Approve, then revoke + await registry.approvePCR(pcrs, 'v-revoke-reapprove', TEE_TYPE) + await registry.revokePCR(pcrHash, TEE_TYPE) + expect(await registry.isPCRApproved(TEE_TYPE, pcrHash)).to.be.false - // Re-approve to cancel the pending revocation - await registry.approvePCR(pcrs, 'v-grace-cancel-fixed', TEE_TYPE) + // Re-approve + await registry.approvePCR(pcrs, 'v-revoke-reapprove-fixed', TEE_TYPE) - // expiresAt should be reset to 0 - const infoAfter = await registry.approvedPCRs(TEE_TYPE, pcrHash) - expect(infoAfter.expiresAt.toString()).to.equal('0') - expect(infoAfter.active).to.be.true - expect(infoAfter.version).to.equal('v-grace-cancel-fixed') + const infoAfter = await registry.pcrRecords(TEE_TYPE, pcrHash) + expect(infoAfter.approved).to.be.true + expect(infoAfter.version).to.equal('v-revoke-reapprove-fixed') - console.log('✓ Re-approval cancels pending grace-period revocation') + console.log('✓ Re-approval after revocation works') }) it('should allow re-approval of revoked PCR', async function () { @@ -531,7 +494,7 @@ contract('TEERegistry', function (accounts) { const pcrHash = await registry.computePCRHash(pcrs) await registry.approvePCR(pcrs, 'v-reapprove-1', TEE_TYPE) - await registry.revokePCR(pcrHash, TEE_TYPE, 0) + await registry.revokePCR(pcrHash, TEE_TYPE) expect(await registry.isPCRApproved(TEE_TYPE, pcrHash)).to.be.false // Re-approval should succeed after revocation @@ -541,7 +504,7 @@ contract('TEERegistry', function (accounts) { console.log('✓ Re-approval of revoked PCR works') }) - it('should revoke PCR immediately when gracePeriod is 0', async function () { + it('should revoke PCR immediately', async function () { const pcrs = { pcr0: '0x' + Buffer.alloc(48, 0x30).toString('hex'), pcr1: '0x' + Buffer.alloc(48, 0x31).toString('hex'), @@ -551,76 +514,15 @@ contract('TEERegistry', function (accounts) { await registry.approvePCR(pcrs, 'v-immediate-revoke', TEE_TYPE) - const result = await registry.revokePCR(pcrHash, TEE_TYPE, 0) + const result = await registry.revokePCR(pcrHash, TEE_TYPE) truffleAssert.eventEmitted(result, 'PCRRevoked', (ev) => { - return ev.pcrHash === pcrHash && ev.gracePeriod.toString() === '0' + return ev.pcrHash === pcrHash && ev.teeType.toNumber() === TEE_TYPE }) expect(await registry.isPCRApproved(TEE_TYPE, pcrHash)).to.be.false - console.log('✓ Immediate PCR revocation works') - }) - - it('should revoke PCR with grace period', async function () { - const pcrs = { - pcr0: '0x' + Buffer.alloc(48, 0x70).toString('hex'), - pcr1: '0x' + Buffer.alloc(48, 0x71).toString('hex'), - pcr2: '0x' + Buffer.alloc(48, 0x72).toString('hex') - } - - const pcrHash = await registry.computePCRHash(pcrs) - const gracePeriod = 3600 // 1 hour - - await registry.approvePCR(pcrs, 'v-grace-revoke', TEE_TYPE) - - const result = await registry.revokePCR(pcrHash, TEE_TYPE, gracePeriod) - - truffleAssert.eventEmitted(result, 'PCRRevoked', (ev) => { - return ev.pcrHash === pcrHash && ev.gracePeriod.toString() === gracePeriod.toString() - }) - - // Should still be valid during grace period - expect(await registry.isPCRApproved(TEE_TYPE, pcrHash)).to.be.true - - const pcrInfo = await registry.approvedPCRs(TEE_TYPE, pcrHash) - expect(pcrInfo.expiresAt.toNumber()).to.be.greaterThan(0) - - console.log('✓ PCR revocation with grace period works') - }) - - it('should maintain both old and new PCRs during grace period', async function () { - const pcrsV1 = { - pcr0: '0x' + Buffer.alloc(48, 0x80).toString('hex'), - pcr1: '0x' + Buffer.alloc(48, 0x81).toString('hex'), - pcr2: '0x' + Buffer.alloc(48, 0x82).toString('hex') - } - - const pcrsV2 = { - pcr0: '0x' + Buffer.alloc(48, 0x90).toString('hex'), - pcr1: '0x' + Buffer.alloc(48, 0x91).toString('hex'), - pcr2: '0x' + Buffer.alloc(48, 0x92).toString('hex') - } - - const v1Hash = await registry.computePCRHash(pcrsV1) - const v2Hash = await registry.computePCRHash(pcrsV2) - - // Approve v1, then v2 - await registry.approvePCR(pcrsV1, 'v1-grace', TEE_TYPE) - await registry.approvePCR(pcrsV2, 'v2-grace', TEE_TYPE) - - // Revoke v1 with 1 hour grace period - await registry.revokePCR(v1Hash, TEE_TYPE, 3600) - - // Both should be valid during grace period - expect(await registry.isPCRApproved(TEE_TYPE, v1Hash)).to.be.true - expect(await registry.isPCRApproved(TEE_TYPE, v2Hash)).to.be.true - - // v1 should have expiry set - const v1Info = await registry.approvedPCRs(TEE_TYPE, v1Hash) - expect(v1Info.expiresAt.toNumber()).to.be.greaterThan(0) - - console.log('✓ Both PCRs valid during grace period') + console.log('✓ PCR revocation works') }) }) }) diff --git a/tests/solidity/suites/tee/test/settlementRelay.js b/tests/solidity/suites/tee/test/settlementRelay.js index 94d8948f..30d261c3 100644 --- a/tests/solidity/suites/tee/test/settlementRelay.js +++ b/tests/solidity/suites/tee/test/settlementRelay.js @@ -415,7 +415,7 @@ contract('InferenceSettlementRelay', function (accounts) { intRegistry = await MockTEERegistry.new() await intRegistry.addTEEType(TEE_TYPE_NITRO, 'AWS Nitro') - // Approve PCR measurements so activateTEE passes _requirePCRValidForTEE + // Approve PCR measurements so enableTEE passes _requirePCRValidForTEE const pcrs = { pcr0: '0x' + Buffer.alloc(48, 0x01).toString('hex'), pcr1: '0x' + Buffer.alloc(48, 0x02).toString('hex'), @@ -429,7 +429,7 @@ contract('InferenceSettlementRelay', function (accounts) { publicKeyDER, TLS_CERT, user, ENDPOINT, TEE_TYPE_NITRO, pcrHash ) intTeeId = await intRegistry.computeTEEId(publicKeyDER) - await intRegistry.activateTEE(intTeeId) + await intRegistry.enableTEE(intTeeId) // Deploy real verifier pointing at mock registry intVerifier = await TEEInferenceVerifier.new(intRegistry.address) @@ -482,8 +482,8 @@ contract('InferenceSettlementRelay', function (accounts) { console.log('✓ End-to-end settleIndividual succeeded') }) - it('should revert end-to-end when TEE is deactivated', async function () { - await intRegistry.deactivateTEE(intTeeId) + it('should revert end-to-end when TEE is disabled', async function () { + await intRegistry.disableTEE(intTeeId) const block = await web3.eth.getBlock('latest') const timestamp = block.timestamp @@ -496,10 +496,10 @@ contract('InferenceSettlementRelay', function (accounts) { ) ) - // Re-activate for subsequent tests - await intRegistry.activateTEE(intTeeId) + // Re-enable for subsequent tests + await intRegistry.enableTEE(intTeeId) - console.log('✓ Deactivated TEE rejected in end-to-end flow') + console.log('✓ Disabled TEE rejected in end-to-end flow') }) it('should revert end-to-end with expired timestamp', async function () {