@@ -6,25 +6,7 @@ import "./precompiles/tee/ITEEVerifier.sol";
66import "@openzeppelin/contracts/access/AccessControl.sol " ;
77
88/// @title TEEInferenceVerifier - Verification for TEE-signed inference outputs
9- /// @notice Stateless verifier that confirms an AI inference output was produced by a
10- /// registered, active TEE within an acceptable time window.
11- ///
12- /// @dev ## How It Works
13- ///
14- /// When a TEE runs an inference, it signs `keccak256(inputHash || outputHash || timestamp)`
15- /// with its RSA-PSS private key. Any party (settlement relay, on-chain consumer, etc.) can
16- /// then call `verifySignature` to confirm authenticity. The check is three-fold:
17- ///
18- /// 1. **TEE status** — the TEE must be active in the `TEERegistry`.
19- /// 2. **Timestamp bounds** — the signed timestamp must be within
20- /// `[block.timestamp - MAX_INFERENCE_AGE, block.timestamp + FUTURE_TOLERANCE]`
21- /// to prevent replay of stale results and reject clock-skewed signatures.
22- /// 3. **Cryptographic proof** — the RSA-PSS signature is verified against the TEE's
23- /// on-chain public key via the 0x900 precompile.
24- ///
25- /// The contract is intentionally read-only (no state mutations in `verifySignature`) so
26- /// it can be called from view contexts and composed freely by downstream contracts like
27- /// `InferenceSettlementRelay`.
9+ /// @notice Reads TEE public keys from TEERegistry and verifies RSA-PSS signatures with timestamp validation.
2810contract TEEInferenceVerifier is AccessControl {
2911
3012 // ============ Constants ============
@@ -82,35 +64,74 @@ contract TEEInferenceVerifier is AccessControl {
8264 return keccak256 (abi.encodePacked (inputHash, outputHash, timestamp));
8365 }
8466
85- /// @notice Verify a TEE signature with timestamp validation
86- /// @dev Returns false for invalid signatures, inactive TEEs, or out-of-bounds timestamps.
67+ /// @notice Shared core verification logic (TEE active check + cryptographic verification)
68+ /// @dev Does NOT check timestamp bounds — callers are responsible for that.
69+ /// @param teeId Registered TEE identifier
70+ /// @param inputHash Hash of the inference input
71+ /// @param outputHash Hash of the inference output
72+ /// @param timestamp Unix timestamp the TEE embedded when signing — still part of the signed hash
73+ /// @param signature RSA-PSS signature from the TEE's signing key
74+ /// @return True if TEE is active and signature is cryptographically valid
75+ function _verifyCore (
76+ bytes32 teeId ,
77+ bytes32 inputHash ,
78+ bytes32 outputHash ,
79+ uint256 timestamp ,
80+ bytes calldata signature
81+ ) internal view returns (bool ) {
82+ // 1. TEE must be active in the registry
83+ if (! registry.isActive (teeId)) return false ;
84+
85+ // 2. Cryptographic verification
86+ bytes memory pubKey = registry.getPublicKey (teeId);
87+ bytes32 msgHash = computeMessageHash (inputHash, outputHash, timestamp);
88+ return VERIFIER.verifyRSAPSS (pubKey, msgHash, signature);
89+ }
8790
91+ /// @notice Verify a TEE signature with timestamp validation
92+ /// @dev Use this for real-time inference — rejects results older than MAX_INFERENCE_AGE
93+ /// or timestamped more than FUTURE_TOLERANCE into the future (clock drift tolerance).
8894 /// @param teeId Registered TEE identifier
8995 /// @param inputHash Hash of the inference input
9096 /// @param outputHash Hash of the inference output
9197 /// @param timestamp Unix timestamp the TEE embedded when signing (seconds)
9298 /// @param signature RSA-PSS signature from the TEE's signing key
93- /// @return True if TEE is active, timestamp is valid , and signature is correct
99+ /// @return True if TEE is active, timestamp is within bounds , and signature is correct
94100 function verifySignature (
95101 bytes32 teeId ,
96102 bytes32 inputHash ,
97103 bytes32 outputHash ,
98104 uint256 timestamp ,
99105 bytes calldata signature
100106 ) public view returns (bool ) {
101- // 1. TEE must be enabled in the registry
102- if (! registry.isTEEEnabled (teeId)) return false ;
103-
104- // 2. Timestamp bounds
105- uint256 minTs = block .timestamp > MAX_INFERENCE_AGE
106- ? block .timestamp - MAX_INFERENCE_AGE
107+ // Timestamp bounds — inference must be recent and not too far in the future
108+ uint256 minTs = block .timestamp > MAX_INFERENCE_AGE
109+ ? block .timestamp - MAX_INFERENCE_AGE
107110 : 0 ;
108111 uint256 maxTs = block .timestamp + FUTURE_TOLERANCE;
109112 if (timestamp < minTs || timestamp > maxTs) return false ;
110113
111- // 3. Cryptographic verification
112- bytes memory pubKey = registry.getTEEPublicKey (teeId);
113- bytes32 msgHash = computeMessageHash (inputHash, outputHash, timestamp);
114- return VERIFIER.verifyRSAPSS (pubKey, msgHash, signature);
114+ return _verifyCore (teeId, inputHash, outputHash, timestamp, signature);
115+ }
116+
117+ /// @notice Verify a TEE signature without timestamp bounds check
118+ /// @dev Use this for batch inference, offline jobs, historical audits, or testing
119+ /// where the inference may have been produced outside the MAX_INFERENCE_AGE window.
120+ /// The timestamp is still part of the signed message and must match what the TEE signed —
121+ /// only the recency window check is skipped.
122+ /// @param teeId Registered TEE identifier
123+ /// @param inputHash Hash of the inference input
124+ /// @param outputHash Hash of the inference output
125+ /// @param timestamp Unix timestamp the TEE embedded when signing (seconds)
126+ /// @param signature RSA-PSS signature from the TEE's signing key
127+ /// @return True if TEE is active and signature is cryptographically valid
128+ function verifySignatureNoTimestamp (
129+ bytes32 teeId ,
130+ bytes32 inputHash ,
131+ bytes32 outputHash ,
132+ uint256 timestamp ,
133+ bytes calldata signature
134+ ) public view returns (bool ) {
135+ return _verifyCore (teeId, inputHash, outputHash, timestamp, signature);
115136 }
116137}
0 commit comments