Skip to content

Commit 2e5c7cb

Browse files
committed
feat(nitro-verifier): add revoker role for automated cert revocation
Add a dedicated revoker address to NitroEnclaveVerifier that can call revokeCert() without needing the owner multisig key. This enables an automated service (the registrar) to revoke compromised intermediate certificates with minimal latency. A compromised revoker key can only delete cached certs (DoS - increased proving costs), not forge attestations. Low blast radius. Changes: - Add revoker state variable, CallerNotOwnerOrRevoker error, RevokerUpdated event, onlyOwnerOrRevoker modifier - Add initialRevoker parameter to constructor (can be address(0)) - Add setRevoker() owner-only setter - Change revokeCert from onlyOwner to onlyOwnerOrRevoker - Update interface with revoker(), setRevoker(), event, error - 9 new tests for revoker role semantics - Semver: 0.2.0 -> 0.3.0 CHAIN-3890
1 parent 365d221 commit 2e5c7cb

7 files changed

Lines changed: 209 additions & 20 deletions

File tree

interfaces/multiproof/tee/INitroEnclaveVerifier.sol

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,12 @@ interface INitroEnclaveVerifier {
160160
*/
161161
function proofSubmitter() external view returns (address);
162162

163+
/**
164+
* @dev Returns the address authorized to revoke intermediate certificates
165+
* @return Address of the revoker (address(0) if disabled)
166+
*/
167+
function revoker() external view returns (address);
168+
163169
/**
164170
* @dev Retrieves the configuration for a specific coprocessor
165171
* @param _zkCoProcessor Type of ZK coprocessor (RiscZero or Succinct)
@@ -227,6 +233,15 @@ interface INitroEnclaveVerifier {
227233
*/
228234
function setProofSubmitter(address _proofSubmitter) external;
229235

236+
/**
237+
* @dev Updates the revoker address
238+
* @param _newRevoker New revoker address (can be address(0) to disable the revoker role)
239+
*
240+
* Requirements:
241+
* - Only callable by contract owner
242+
*/
243+
function setRevoker(address _newRevoker) external;
244+
230245
/**
231246
* @dev Configures the zero-knowledge verification parameters for a specific coprocessor
232247
* @param _zkCoProcessor Type of ZK coprocessor (RiscZero or Succinct)
@@ -249,7 +264,7 @@ interface INitroEnclaveVerifier {
249264
* @param _certHash Hash of the certificate to revoke
250265
*
251266
* Requirements:
252-
* - Only callable by contract owner
267+
* - Only callable by contract owner or revoker
253268
* - Certificate must exist in the trusted set
254269
*/
255270
function revokeCert(bytes32 _certHash) external;

scripts/multiproof/DeployRiscZeroStack.s.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,13 +131,15 @@ contract DeployRiscZeroStack is Script {
131131

132132
// Use owner as placeholder proofSubmitter; must be updated to TEEProverRegistry
133133
// address after deployment via setProofSubmitter().
134+
// Revoker starts as address(0); must be set via setRevoker() after deployment.
134135
NitroEnclaveVerifier nev = new NitroEnclaveVerifier(
135136
owner,
136137
NITRO_MAX_TIME_DIFF,
137138
trustedCerts,
138139
trustedCertExpiries,
139140
nitroRootCert,
140141
owner,
142+
address(0),
141143
ZkCoProcessorType.RiscZero,
142144
zkConfig,
143145
bytes32(0)

snapshots/abi/NitroEnclaveVerifier.json

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@
3131
"name": "initialProofSubmitter",
3232
"type": "address"
3333
},
34+
{
35+
"internalType": "address",
36+
"name": "initialRevoker",
37+
"type": "address"
38+
},
3439
{
3540
"internalType": "enum ZkCoProcessorType",
3641
"name": "zkCoProcessor",
@@ -416,6 +421,19 @@
416421
"stateMutability": "nonpayable",
417422
"type": "function"
418423
},
424+
{
425+
"inputs": [],
426+
"name": "revoker",
427+
"outputs": [
428+
{
429+
"internalType": "address",
430+
"name": "",
431+
"type": "address"
432+
}
433+
],
434+
"stateMutability": "view",
435+
"type": "function"
436+
},
419437
{
420438
"inputs": [],
421439
"name": "rootCert",
@@ -455,6 +473,19 @@
455473
"stateMutability": "nonpayable",
456474
"type": "function"
457475
},
476+
{
477+
"inputs": [
478+
{
479+
"internalType": "address",
480+
"name": "newRevoker",
481+
"type": "address"
482+
}
483+
],
484+
"name": "setRevoker",
485+
"outputs": [],
486+
"stateMutability": "nonpayable",
487+
"type": "function"
488+
},
458489
{
459490
"inputs": [
460491
{
@@ -881,6 +912,19 @@
881912
"name": "ProofSubmitterChanged",
882913
"type": "event"
883914
},
915+
{
916+
"anonymous": false,
917+
"inputs": [
918+
{
919+
"indexed": true,
920+
"internalType": "address",
921+
"name": "newRevoker",
922+
"type": "address"
923+
}
924+
],
925+
"name": "RevokerUpdated",
926+
"type": "event"
927+
},
884928
{
885929
"anonymous": false,
886930
"inputs": [
@@ -1010,6 +1054,11 @@
10101054
"name": "AlreadyInitialized",
10111055
"type": "error"
10121056
},
1057+
{
1058+
"inputs": [],
1059+
"name": "CallerNotOwnerOrRevoker",
1060+
"type": "error"
1061+
},
10131062
{
10141063
"inputs": [],
10151064
"name": "CallerNotProofSubmitter",

snapshots/semver-lock.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -232,15 +232,15 @@
232232
"sourceCodeHash": "0x955bd0c9b47e43219865e4e92abf28d916c96de20cbdf2f94c8ab14d02083759"
233233
},
234234
"src/multiproof/AggregateVerifier.sol:AggregateVerifier": {
235-
"initCodeHash": "0x911b368f15552ea645430ad3775ccfe8fe58b1b365a8e0c236d6ce53b5d2219a",
235+
"initCodeHash": "0x7950eedbe5df2d23b2ca8c362f1ad8df7e876c51462ecc535148cf86f7a09dc1",
236236
"sourceCodeHash": "0x032f11e23c188b939ba6cbdc446271b0caad2f4da00535b164a3489291162762"
237237
},
238238
"src/multiproof/tee/NitroEnclaveVerifier.sol:NitroEnclaveVerifier": {
239-
"initCodeHash": "0xac14e4b9130c476ba4ab77ecf70b36153921a9fe6ef3ab96c825aba8d9d18473",
240-
"sourceCodeHash": "0x03614e89919c06e56e91b217702345edf74755c12c082475c4c742f45c201415"
239+
"initCodeHash": "0x4f8c6658c42e32acd969e18344699170fec74f4972192a8ca4693391b24594f4",
240+
"sourceCodeHash": "0x9f9ac5cdff6e537cf1ac70700e060a4c984c17aa3bd50e2f44cee4448dda1a88"
241241
},
242242
"src/multiproof/tee/TEEProverRegistry.sol:TEEProverRegistry": {
243-
"initCodeHash": "0xfaec22f48eeef0f4ee0575b0e91028a1330fbf6faf52c2fce0f68b500a023970",
243+
"initCodeHash": "0xa53501afe2dfc08f0f5ba667ee3b187ca0168df4e9dd947c3e9376fc7d3705d2",
244244
"sourceCodeHash": "0xf1ec1f02f540da659a204b26acf986fdce7d7d63bba67a87923f52453fb92ccb"
245245
},
246246
"src/multiproof/tee/TEEVerifier.sol:TEEVerifier": {

snapshots/storageLayout/NitroEnclaveVerifier.json

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,46 +6,53 @@
66
"slot": "0",
77
"type": "address"
88
},
9+
{
10+
"bytes": "20",
11+
"label": "revoker",
12+
"offset": 0,
13+
"slot": "1",
14+
"type": "address"
15+
},
916
{
1017
"bytes": "32",
1118
"label": "zkConfig",
1219
"offset": 0,
13-
"slot": "1",
20+
"slot": "2",
1421
"type": "mapping(enum ZkCoProcessorType => struct ZkCoProcessorConfig)"
1522
},
1623
{
1724
"bytes": "32",
1825
"label": "trustedIntermediateCerts",
1926
"offset": 0,
20-
"slot": "2",
27+
"slot": "3",
2128
"type": "mapping(bytes32 => uint64)"
2229
},
2330
{
2431
"bytes": "8",
2532
"label": "maxTimeDiff",
2633
"offset": 0,
27-
"slot": "3",
34+
"slot": "4",
2835
"type": "uint64"
2936
},
3037
{
3138
"bytes": "32",
3239
"label": "rootCert",
3340
"offset": 0,
34-
"slot": "4",
41+
"slot": "5",
3542
"type": "bytes32"
3643
},
3744
{
3845
"bytes": "32",
3946
"label": "_zkVerifierRoutes",
4047
"offset": 0,
41-
"slot": "5",
48+
"slot": "6",
4249
"type": "mapping(enum ZkCoProcessorType => mapping(bytes4 => address))"
4350
},
4451
{
4552
"bytes": "32",
4653
"label": "_verifierProofIds",
4754
"offset": 0,
48-
"slot": "6",
55+
"slot": "7",
4956
"type": "mapping(enum ZkCoProcessorType => bytes32)"
5057
}
5158
]

src/multiproof/tee/NitroEnclaveVerifier.sol

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ contract NitroEnclaveVerifier is Ownable, INitroEnclaveVerifier, ISemver {
4949
/// @dev Address that can submit proofs
5050
address public proofSubmitter;
5151

52+
/// @dev Address authorized to revoke intermediate certificates (in addition to owner)
53+
address public revoker;
54+
5255
/// @dev Configuration mapping for each supported ZK coprocessor type
5356
mapping(ZkCoProcessorType => ZkCoProcessorConfig) public zkConfig;
5457

@@ -108,6 +111,9 @@ contract NitroEnclaveVerifier is Ownable, INitroEnclaveVerifier, ISemver {
108111
/// @dev Thrown when a zero address is provided for the verifier
109112
error InvalidVerifierAddress();
110113

114+
/// @dev Thrown when caller is neither the owner nor the revoker
115+
error CallerNotOwnerOrRevoker();
116+
111117
// ============ Events ============
112118

113119
/// @dev Emitted when a new verifier program ID is added/updated
@@ -143,9 +149,18 @@ contract NitroEnclaveVerifier is Ownable, INitroEnclaveVerifier, ISemver {
143149
/// @dev Event emitted when the maximum time difference is updated
144150
event MaxTimeDiffUpdated(uint64 newMaxTimeDiff);
145151

152+
/// @dev Event emitted when the revoker address is updated
153+
event RevokerUpdated(address indexed newRevoker);
154+
146155
/// @dev Thrown when initializeTrustedCerts and initializeTrustedCertExpiries have different lengths
147156
error CertExpiriesLengthMismatch(uint256 certsLen, uint256 expiriesLen);
148157

158+
/// @dev Restricts access to the owner or the revoker
159+
modifier onlyOwnerOrRevoker() {
160+
if (msg.sender != owner() && msg.sender != revoker) revert CallerNotOwnerOrRevoker();
161+
_;
162+
}
163+
149164
/**
150165
* @dev Initializes the contract with owner, time tolerance and initial trusted certificates
151166
* @param owner Address to be set as the contract owner
@@ -154,6 +169,7 @@ contract NitroEnclaveVerifier is Ownable, INitroEnclaveVerifier, ISemver {
154169
* @param initializeTrustedCertExpiries Array of notAfter timestamps (seconds) for each initial cert
155170
* @param initialRootCert Hash of the AWS Nitro Enclave root certificate
156171
* @param initialProofSubmitter Address that is authorized to submit proofs
172+
* @param initialRevoker Address authorized to revoke intermediate certificates (can be address(0) to disable)
157173
* @param zkCoProcessor Type of ZK coprocessor to configure (RiscZero or Succinct)
158174
* @param config Configuration parameters for the ZK coprocessor
159175
* @param verifierProofId The verifierProofId corresponding to the verifierId in config
@@ -165,6 +181,7 @@ contract NitroEnclaveVerifier is Ownable, INitroEnclaveVerifier, ISemver {
165181
uint64[] memory initializeTrustedCertExpiries,
166182
bytes32 initialRootCert,
167183
address initialProofSubmitter,
184+
address initialRevoker,
168185
ZkCoProcessorType zkCoProcessor,
169186
ZkCoProcessorConfig memory config,
170187
bytes32 verifierProofId
@@ -180,6 +197,7 @@ contract NitroEnclaveVerifier is Ownable, INitroEnclaveVerifier, ISemver {
180197
_initializeOwner(owner);
181198
_setRootCert(initialRootCert);
182199
_setProofSubmitter(initialProofSubmitter);
200+
revoker = initialRevoker;
183201
_setZkConfiguration(zkCoProcessor, config, verifierProofId);
184202
}
185203

@@ -321,13 +339,13 @@ contract NitroEnclaveVerifier is Ownable, INitroEnclaveVerifier, ISemver {
321339
* @param certHash Hash of the certificate to revoke
322340
*
323341
* Requirements:
324-
* - Only callable by contract owner
342+
* - Only callable by contract owner or revoker
325343
* - Certificate must exist in the trusted intermediate certificates set
326344
*
327-
* This function allows the owner to revoke compromised intermediate certificates
345+
* This function allows the owner or revoker to revoke compromised intermediate certificates
328346
* without affecting the root certificate or other trusted certificates.
329347
*/
330-
function revokeCert(bytes32 certHash) external onlyOwner {
348+
function revokeCert(bytes32 certHash) external onlyOwnerOrRevoker {
331349
if (trustedIntermediateCerts[certHash] == 0) {
332350
revert CertificateNotFound(certHash);
333351
}
@@ -424,6 +442,18 @@ contract NitroEnclaveVerifier is Ownable, INitroEnclaveVerifier, ISemver {
424442
_setProofSubmitter(submitter);
425443
}
426444

445+
/**
446+
* @dev Updates the revoker address
447+
* @param newRevoker New revoker address (can be address(0) to disable the revoker role)
448+
*
449+
* Requirements:
450+
* - Only callable by contract owner
451+
*/
452+
function setRevoker(address newRevoker) external onlyOwner {
453+
revoker = newRevoker;
454+
emit RevokerUpdated(newRevoker);
455+
}
456+
427457
// ============ Verification Functions ============
428458

429459
/**
@@ -662,8 +692,8 @@ contract NitroEnclaveVerifier is Ownable, INitroEnclaveVerifier, ISemver {
662692
}
663693

664694
/// @notice Semantic version.
665-
/// @custom:semver 0.2.0
695+
/// @custom:semver 0.3.0
666696
function version() public pure virtual returns (string memory) {
667-
return "0.2.0";
697+
return "0.3.0";
668698
}
669699
}

0 commit comments

Comments
 (0)