Skip to content

Commit 30f133e

Browse files
committed
feat: add verifySignatureNoTimestamp for batch/offline inference
1 parent 34a61a8 commit 30f133e

1 file changed

Lines changed: 53 additions & 32 deletions

File tree

contracts/solidity/TEEInferenceVerifier.sol

Lines changed: 53 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,7 @@ import "./precompiles/tee/ITEEVerifier.sol";
66
import "@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.
2810
contract 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

Comments
 (0)