@inco/lightning 0.2.16 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/manifest.yaml +29 -0
- package/package.json +5 -1
- package/src/DeployTEE.sol +153 -0
- package/src/DeployUtils.sol +17 -9
- package/src/Errors.sol +4 -0
- package/src/IncoLightning.gen.sol +1 -1
- package/src/IncoLightning.sol +2 -1
- package/src/Types.sol +45 -3
- package/src/lightning-parts/EncryptedInput.gen.sol +1 -0
- package/src/lightning-parts/EncryptedInput.sol +1 -1
- package/src/lightning-parts/EncryptedOperations.gen.sol +1 -1
- package/src/lightning-parts/EncryptedOperations.sol +8 -6
- package/src/lightning-parts/TEELifecycle.gen.sol +58 -0
- package/src/lightning-parts/TEELifecycle.sol +255 -0
- package/src/lightning-parts/TEELifecycle.types.sol +21 -0
- package/src/lightning-parts/primitives/SignatureVerifier.gen.sol +1 -0
- package/src/lightning-parts/primitives/SignatureVerifier.sol +11 -0
- package/src/lightning-parts/test/HandleMetadata.t.sol +1 -1
- package/src/test/FakeIncoInfra/FakeQuoteVerifier.sol +29 -0
- package/src/test/FakeIncoInfra/MockRemoteAttestation.sol +37 -0
- package/src/test/FibonacciDecrypt.sol +1 -0
- package/src/test/IncoTest.sol +5 -1
- package/src/test/TEELifecycle/README.md +53 -0
- package/src/test/TEELifecycle/TEELifecycleHWTest.t.sol +119 -0
- package/src/test/TEELifecycle/TEELifecycleMockTest.t.sol +145 -0
- package/src/test/TEELifecycle/addnode_data/eoa.txt +1 -0
- package/src/test/TEELifecycle/addnode_data/quote.bin +0 -0
- package/src/test/TEELifecycle/bootstrap_data/ecies_pubkey.bin +1 -0
- package/src/test/TEELifecycle/bootstrap_data/eip712_signature.bin +1 -0
- package/src/test/TEELifecycle/bootstrap_data/eoa.txt +1 -0
- package/src/test/TEELifecycle/bootstrap_data/qe_identity +1 -0
- package/src/test/TEELifecycle/bootstrap_data/qe_identity_signature.bin +1 -0
- package/src/test/TEELifecycle/bootstrap_data/quote.bin +0 -0
- package/src/test/TEELifecycle/bootstrap_data/tcb_info +1 -0
- package/src/test/TEELifecycle/bootstrap_data/tcb_info_signature.bin +1 -0
- package/src/test/TEELifecycle/test_cert/AttestationReportSigningCA.crl +0 -0
- package/src/test/TEELifecycle/test_cert/Intel_SGX_Attestation_RootCA.cer +0 -0
- package/src/test/TEELifecycle/test_cert/Intel_SGX_PCK_CRL.crl +0 -0
- package/src/test/TEELifecycle/test_cert/Intel_SGX_PCK_PlatformCA.cer +0 -0
- package/src/test/TEELifecycle/test_cert/Intel_SGX_TCB_Signing.cer +0 -0
- package/src/test/TestFakeInfra.t.sol +18 -1
- package/src/test/TestUpgrade.t.sol +314 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/// SPDX-License-Identifier: No License
|
|
2
|
+
pragma solidity ^0.8.19;
|
|
3
|
+
|
|
4
|
+
import "./TEELifecycle.types.sol";
|
|
5
|
+
|
|
6
|
+
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
|
|
7
|
+
import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
|
|
8
|
+
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
|
|
9
|
+
import {IQuoteVerifier} from "automata-dcap-attestation/interfaces/IQuoteVerifier.sol";
|
|
10
|
+
import {BELE} from "automata-dcap-attestation/utils/BELE.sol";
|
|
11
|
+
import {HEADER_LENGTH} from "automata-dcap-attestation/types/Constants.sol";
|
|
12
|
+
import {TD10ReportBody, Header} from "automata-dcap-attestation/types/V4Structs.sol";
|
|
13
|
+
import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol";
|
|
14
|
+
import {EnclaveIdentityJsonObj, IdentityObj} from "@automata-network/on-chain-pccs/helpers/EnclaveIdentityHelper.sol";
|
|
15
|
+
import {TcbInfoJsonObj} from "@automata-network/on-chain-pccs/helpers/FmspcTcbHelper.sol";
|
|
16
|
+
import {AutomataFmspcTcbDao} from "@automata-network/on-chain-pccs/automata_pccs/AutomataFmspcTcbDao.sol";
|
|
17
|
+
import {AutomataEnclaveIdentityDao} from "@automata-network/on-chain-pccs/automata_pccs/AutomataEnclaveIdentityDao.sol";
|
|
18
|
+
|
|
19
|
+
// @todo: Make this contract UUPSUpgradeable: https://github.com/Inco-fhevm/inco-monorepo/issues/875
|
|
20
|
+
contract TEELifecycle is OwnableUpgradeable, EIP712Upgradeable {
|
|
21
|
+
|
|
22
|
+
event QuoteVerifierUpdated(uint16 indexed version);
|
|
23
|
+
|
|
24
|
+
event BootstrapStageComplete(
|
|
25
|
+
address indexed newEOASigner,
|
|
26
|
+
BootstrapResult bootstrapResult
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
event TEEVersionUpdated(
|
|
30
|
+
TEEVersion teeVersion
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
event NewCovalidatorAdded(
|
|
34
|
+
address covalidatorAddress,
|
|
35
|
+
bytes quote
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
bytes32 public constant BootstrapResultStructHash =
|
|
39
|
+
keccak256(bytes(
|
|
40
|
+
"BootstrapResult(bytes ecies_pubkey)"
|
|
41
|
+
));
|
|
42
|
+
|
|
43
|
+
uint16 public constant QUOTE_VERIFIER_VERSION = 4;
|
|
44
|
+
|
|
45
|
+
IQuoteVerifier quoteVerifier;
|
|
46
|
+
|
|
47
|
+
BootstrapResult public VerifiedBootstrapResult;
|
|
48
|
+
|
|
49
|
+
TEEVersion[] public TEEVersionHistory;
|
|
50
|
+
bytes public ECIESPubkey;
|
|
51
|
+
mapping(address => bool) public EOASigners;
|
|
52
|
+
|
|
53
|
+
function initialize(address owner, string memory eip712Name, string memory eip712Version, address quoteVerifierAddress) public initializer {
|
|
54
|
+
__Ownable_init(owner);
|
|
55
|
+
__EIP712_init(eip712Name, eip712Version);
|
|
56
|
+
quoteVerifier = IQuoteVerifier(quoteVerifierAddress);
|
|
57
|
+
require(quoteVerifier.quoteVersion() == QUOTE_VERIFIER_VERSION, "Invalid quote verifier version");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @notice Uploads the collateral to the contract
|
|
62
|
+
* @param tcbInfo - The TCB info to upload
|
|
63
|
+
* @param identity - The identity to upload
|
|
64
|
+
*/
|
|
65
|
+
function uploadCollateral(TcbInfoJsonObj memory tcbInfo, EnclaveIdentityJsonObj memory identity) public onlyOwner {
|
|
66
|
+
require(bytes(tcbInfo.tcbInfoStr).length != 0, "tcbInfo.tcbInfoStr must not be empty");
|
|
67
|
+
require(bytes(identity.identityStr).length != 0, "identity.identityStr must not be empty");
|
|
68
|
+
|
|
69
|
+
AutomataFmspcTcbDao fmspcTcbDao = AutomataFmspcTcbDao(quoteVerifier.pccsRouter().fmspcTcbDaoAddr());
|
|
70
|
+
fmspcTcbDao.upsertFmspcTcb(tcbInfo);
|
|
71
|
+
AutomataEnclaveIdentityDao enclaveIdDao = AutomataEnclaveIdentityDao(quoteVerifier.pccsRouter().qeIdDaoAddr());
|
|
72
|
+
(IdentityObj memory identityObj,) = enclaveIdDao.EnclaveIdentityLib().parseIdentityString(identity.identityStr);
|
|
73
|
+
enclaveIdDao.upsertEnclaveIdentity(uint256(identityObj.id), 4, identity);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @notice Verifies the bootstrap data against the provided quote and signature
|
|
78
|
+
* @param bootstrapResult - The bootstrap data to verify
|
|
79
|
+
* @param quote - The quote to verify against
|
|
80
|
+
* @param signature - The signature to verify against
|
|
81
|
+
*/
|
|
82
|
+
function verifyBootstrapResult(BootstrapResult calldata bootstrapResult, bytes calldata quote, bytes calldata signature) public onlyOwner {
|
|
83
|
+
// Make sure the bootstrap is not already complete, and that the contract owner
|
|
84
|
+
// has already submitted the pending TEE MRTD.
|
|
85
|
+
require(!isBootstrapComplete(), "Bootstrap already completed");
|
|
86
|
+
require(TEEVersionHistory.length == 1, "TEEVersionHistory must have exactly one version, please call approveNewTEEVersion first");
|
|
87
|
+
require(TEEVersionHistory[0].status == TEEVersionStatus.PENDING, "TEEVersionHistory must still be pending");
|
|
88
|
+
|
|
89
|
+
bytes32 _bootstrapResultDigest = bootstrapResultDigest(bootstrapResult);
|
|
90
|
+
(bool success, bytes memory output) = _verifyAndAttestOnChain(quote);
|
|
91
|
+
require(success, string(output));
|
|
92
|
+
|
|
93
|
+
bytes memory v0MRTD = TEEVersionHistory[0].mrtd;
|
|
94
|
+
|
|
95
|
+
TD10ReportBody memory tdReport = parseTD10ReportBody(quote);
|
|
96
|
+
(address reportDataSigner, bytes memory reportMRTD) = parseReport(tdReport);
|
|
97
|
+
require(keccak256(reportMRTD) == keccak256(v0MRTD), "Invalid report MRTD");
|
|
98
|
+
address recoveredAddress = ECDSA.recover(_bootstrapResultDigest, signature);
|
|
99
|
+
require(recoveredAddress == reportDataSigner, "Invalid signature for bootstrap data");
|
|
100
|
+
|
|
101
|
+
VerifiedBootstrapResult = bootstrapResult;
|
|
102
|
+
TEEVersionHistory[0].status = TEEVersionStatus.ACTIVE;
|
|
103
|
+
emit BootstrapStageComplete(reportDataSigner, bootstrapResult);
|
|
104
|
+
ECIESPubkey = bootstrapResult.ecies_pubkey;
|
|
105
|
+
EOASigners[reportDataSigner] = true;
|
|
106
|
+
//TODO: update ECIES public key to ?? contract state and EOA addresses signers to the Signers contract state
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @notice Approves a new TEE version and updates the TEEVersionHistory
|
|
111
|
+
* @param newMRTD - The MRTD bytes of the new TEE version
|
|
112
|
+
* @dev This function increments the version number automatically based on the current history
|
|
113
|
+
*/
|
|
114
|
+
function approveNewTEEVersion(bytes calldata newMRTD) public onlyOwner {
|
|
115
|
+
require(newMRTD.length == 48, "MRTD must be 48 bytes");
|
|
116
|
+
|
|
117
|
+
TEEVersionHistory.push(TEEVersion({
|
|
118
|
+
mrtd: newMRTD,
|
|
119
|
+
status: TEEVersionStatus.PENDING
|
|
120
|
+
}));
|
|
121
|
+
|
|
122
|
+
emit TEEVersionUpdated(TEEVersionHistory[TEEVersionHistory.length - 1]);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* @notice Adds a new covalidator to the contract state
|
|
127
|
+
* @param quote - The quote from the new covalidator that contains the current MRTD and the eoa address of the new party in the report data
|
|
128
|
+
*/
|
|
129
|
+
function addNewCovalidator(bytes calldata quote) public onlyOwner {
|
|
130
|
+
require(isBootstrapComplete(), "Bootstrap not complete");
|
|
131
|
+
|
|
132
|
+
(bool success, bytes memory output) = _verifyAndAttestOnChain(quote);
|
|
133
|
+
require(success, string(output));
|
|
134
|
+
TD10ReportBody memory tdReport = parseTD10ReportBody(quote);
|
|
135
|
+
(address reportDataSigner, bytes memory reportMRTD) = parseReport(tdReport);
|
|
136
|
+
require(!EOASigners[reportDataSigner], "EOA signer already initialized");
|
|
137
|
+
|
|
138
|
+
require(keccak256(reportMRTD) == keccak256(TEEVersionHistory[TEEVersionHistory.length - 1].mrtd), "Invalid report MRTD");
|
|
139
|
+
require(reportDataSigner != address(0), "Invalid report data signer");
|
|
140
|
+
emit NewCovalidatorAdded(reportDataSigner, quote);
|
|
141
|
+
EOASigners[reportDataSigner] = true;
|
|
142
|
+
//TODO: Add the new covalidator signers to the Signers contract state
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* @notice Checks if the bootstrap is complete, meaning that there is an active TEE version.
|
|
147
|
+
* @return true if the bootstrap is complete, false otherwise
|
|
148
|
+
*/
|
|
149
|
+
function isBootstrapComplete() public view returns (bool) {
|
|
150
|
+
return TEEVersionHistory.length >= 1 && TEEVersionHistory[0].status == TEEVersionStatus.ACTIVE;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* @notice From https://github.com/automata-network/automata-dcap-attestation/blob/evm-v1.0.0/evm/contracts/AttestationEntrypointBase.sol#L103
|
|
155
|
+
* @notice full on-chain verification for an attestation
|
|
156
|
+
* @param rawQuote - Intel DCAP Quote serialized in raw bytes
|
|
157
|
+
* @return success - whether the quote has been successfully verified or not
|
|
158
|
+
* @return output - the output upon completion of verification. The output data may require post-processing by the consumer.
|
|
159
|
+
* For verification failures, the output is simply a UTF-8 encoded string, describing the reason for failure.
|
|
160
|
+
* @dev can directly type-cast the failed output as a string
|
|
161
|
+
*/
|
|
162
|
+
function _verifyAndAttestOnChain(bytes calldata rawQuote) internal view returns (bool success, bytes memory output) {
|
|
163
|
+
// Parse the header
|
|
164
|
+
Header memory header;
|
|
165
|
+
(success, header) = _parseQuoteHeader(rawQuote);
|
|
166
|
+
if (!success) {
|
|
167
|
+
return (false, bytes("Could not parse quote header"));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (QUOTE_VERIFIER_VERSION != header.version) {
|
|
171
|
+
return (false, bytes("Unsupported quote version"));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// We found a supported version, begin verifying the quote
|
|
175
|
+
// Note: The quote header cannot be trusted yet, it will be validated by the Verifier library
|
|
176
|
+
(success, output) = quoteVerifier.verifyQuote(header, rawQuote);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* @notice From https://github.com/automata-network/automata-dcap-attestation/blob/evm-v1.0.0/evm/contracts/AttestationEntrypointBase.sol#L168
|
|
181
|
+
* @notice Parses the header to get basic information about the quote, such as the version, TEE types etc.
|
|
182
|
+
*/
|
|
183
|
+
function _parseQuoteHeader(bytes calldata rawQuote) private pure returns (bool success, Header memory header) {
|
|
184
|
+
success = rawQuote.length >= HEADER_LENGTH;
|
|
185
|
+
if (success) {
|
|
186
|
+
uint16 version = uint16(BELE.leBytesToBeUint(rawQuote[0:2]));
|
|
187
|
+
bytes4 teeType = bytes4(rawQuote[4:8]);
|
|
188
|
+
bytes2 attestationKeyType = bytes2(rawQuote[2:4]);
|
|
189
|
+
bytes2 qeSvn = bytes2(rawQuote[8:10]);
|
|
190
|
+
bytes2 pceSvn = bytes2(rawQuote[10:12]);
|
|
191
|
+
bytes16 qeVendorId = bytes16(rawQuote[12:28]);
|
|
192
|
+
bytes20 userData = bytes20(rawQuote[28:48]);
|
|
193
|
+
|
|
194
|
+
header = Header({
|
|
195
|
+
version: version,
|
|
196
|
+
attestationKeyType: attestationKeyType,
|
|
197
|
+
teeType: teeType,
|
|
198
|
+
qeSvn: qeSvn,
|
|
199
|
+
pceSvn: pceSvn,
|
|
200
|
+
qeVendorId: qeVendorId,
|
|
201
|
+
userData: userData
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* @notice From https://github.com/automata-network/automata-dcap-attestation/blob/evm-v1.0.0/evm/contracts/verifiers/V4QuoteVerifier.sol#L309
|
|
208
|
+
* @notice Parses the TD10 report body from the raw quote
|
|
209
|
+
* @param rawQuote - The raw quote bytes
|
|
210
|
+
* @return report - The parsed TD10 report body
|
|
211
|
+
*/
|
|
212
|
+
function parseTD10ReportBody(bytes calldata rawQuote) public pure returns (TD10ReportBody memory report) {
|
|
213
|
+
report = TD10ReportBody({
|
|
214
|
+
teeTcbSvn: bytes16(rawQuote[HEADER_LENGTH:HEADER_LENGTH+16]),
|
|
215
|
+
mrSeam: bytes(rawQuote[HEADER_LENGTH+16:HEADER_LENGTH+64]),
|
|
216
|
+
mrsignerSeam: bytes(rawQuote[HEADER_LENGTH+64:HEADER_LENGTH+112]),
|
|
217
|
+
seamAttributes: bytes8(uint64(BELE.leBytesToBeUint(rawQuote[HEADER_LENGTH+112:HEADER_LENGTH+120]))),
|
|
218
|
+
tdAttributes: bytes8(uint64(BELE.leBytesToBeUint(rawQuote[HEADER_LENGTH+120:HEADER_LENGTH+128]))),
|
|
219
|
+
xFAM: bytes8(uint64(BELE.leBytesToBeUint(rawQuote[HEADER_LENGTH+128:HEADER_LENGTH+136]))),
|
|
220
|
+
mrTd: bytes(rawQuote[HEADER_LENGTH+136:HEADER_LENGTH+184]),
|
|
221
|
+
mrConfigId: bytes(rawQuote[HEADER_LENGTH+184:HEADER_LENGTH+232]),
|
|
222
|
+
mrOwner: bytes(rawQuote[HEADER_LENGTH+232:HEADER_LENGTH+280]),
|
|
223
|
+
mrOwnerConfig: bytes(rawQuote[HEADER_LENGTH+280:HEADER_LENGTH+328]),
|
|
224
|
+
rtMr0: bytes(rawQuote[HEADER_LENGTH+328:HEADER_LENGTH+376]),
|
|
225
|
+
rtMr1: bytes(rawQuote[HEADER_LENGTH+376:HEADER_LENGTH+424]),
|
|
226
|
+
rtMr2: bytes(rawQuote[HEADER_LENGTH+424:HEADER_LENGTH+472]),
|
|
227
|
+
rtMr3: bytes(rawQuote[HEADER_LENGTH+472:HEADER_LENGTH+520]),
|
|
228
|
+
reportData: bytes(rawQuote[HEADER_LENGTH+520:HEADER_LENGTH+584])
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* @notice Parses the TD10 report to extract the report data and MRTD
|
|
234
|
+
* @param tdReport - The TD10 report body
|
|
235
|
+
* @return reportDataSigner - The signing address of the report data signer
|
|
236
|
+
* @return reportMRTD - The MRTD bytes from the report
|
|
237
|
+
*/
|
|
238
|
+
function parseReport(TD10ReportBody memory tdReport) public pure returns (address, bytes memory) {
|
|
239
|
+
return (address(bytes20(tdReport.reportData)), tdReport.mrTd);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function bootstrapResultDigest(
|
|
243
|
+
BootstrapResult memory bootstrapResult
|
|
244
|
+
) public view returns (bytes32) {
|
|
245
|
+
return
|
|
246
|
+
_hashTypedDataV4(
|
|
247
|
+
keccak256(
|
|
248
|
+
abi.encode(
|
|
249
|
+
BootstrapResultStructHash,
|
|
250
|
+
keccak256(bootstrapResult.ecies_pubkey)
|
|
251
|
+
)
|
|
252
|
+
)
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// SPDX-License-Identifier: No License
|
|
2
|
+
pragma solidity ^0.8.19;
|
|
3
|
+
|
|
4
|
+
struct BootstrapResult {
|
|
5
|
+
bytes ecies_pubkey;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
enum TEEVersionStatus {
|
|
9
|
+
PENDING,
|
|
10
|
+
ACTIVE
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @notice A struct representing a TEE version. The actual version number is the index in the TEEVersionHistory array.
|
|
15
|
+
* @param mrtd - The MRTD of the TEE version
|
|
16
|
+
* @param status - The status of the TEE version
|
|
17
|
+
*/
|
|
18
|
+
struct TEEVersion {
|
|
19
|
+
bytes mrtd;
|
|
20
|
+
TEEVersionStatus status;
|
|
21
|
+
}
|
|
@@ -3,6 +3,7 @@ pragma solidity ^0.8;
|
|
|
3
3
|
|
|
4
4
|
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
|
|
5
5
|
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
|
|
6
|
+
import { TEELifecycle } from "../TEELifecycle.sol";
|
|
6
7
|
|
|
7
8
|
interface ISignatureVerifierGen {
|
|
8
9
|
function addSigner(address signerAddress) external;
|
|
@@ -4,6 +4,7 @@ pragma solidity ^0.8;
|
|
|
4
4
|
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
|
|
5
5
|
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
|
|
6
6
|
import {ISignatureVerifierGen} from "./SignatureVerifier.gen.sol";
|
|
7
|
+
import {TEELifecycle} from "../TEELifecycle.sol";
|
|
7
8
|
|
|
8
9
|
contract SignatureVerifierStorage {
|
|
9
10
|
struct StorageForSigVerifier {
|
|
@@ -35,11 +36,20 @@ contract SignatureVerifier is ISignatureVerifierGen, OwnableUpgradeable, Signatu
|
|
|
35
36
|
event AddedSignatureVerifier(address signerAddress);
|
|
36
37
|
event RemovedSignatureVerifier(address signerAddress);
|
|
37
38
|
|
|
39
|
+
// Reference to the TEELifecycle contract, to get the list of EOA signers
|
|
40
|
+
TEELifecycle teeLifecycle;
|
|
41
|
+
|
|
42
|
+
function __SignatureVerifier_init(address _teeLifecycleAddress) internal {
|
|
43
|
+
teeLifecycle = TEELifecycle(_teeLifecycleAddress);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// @todo: This function should be removed once we have a way to read the signers from the TEELifecycle contract
|
|
38
47
|
function addSigner(address signerAddress) external onlyOwner {
|
|
39
48
|
getSigVerifierStorage().isSigner[signerAddress] = true;
|
|
40
49
|
emit AddedSignatureVerifier(signerAddress);
|
|
41
50
|
}
|
|
42
51
|
|
|
52
|
+
// @todo: This function should be removed once we have a way to read the signers from the TEELifecycle contract
|
|
43
53
|
function removeSigner(address signerAddress) external onlyOwner {
|
|
44
54
|
require(
|
|
45
55
|
getSigVerifierStorage().isSigner[signerAddress],
|
|
@@ -49,6 +59,7 @@ contract SignatureVerifier is ISignatureVerifierGen, OwnableUpgradeable, Signatu
|
|
|
49
59
|
emit AddedSignatureVerifier(signerAddress);
|
|
50
60
|
}
|
|
51
61
|
|
|
62
|
+
// @todo: This function should read from the TEELifecycle contract instead of the storage
|
|
52
63
|
function isSigner(address signerAddress) public view returns (bool) {
|
|
53
64
|
return getSigVerifierStorage().isSigner[signerAddress];
|
|
54
65
|
}
|
|
@@ -7,7 +7,7 @@ import {TrivialEncryption} from "../TrivialEncryption.sol";
|
|
|
7
7
|
import {EncryptedOperations} from "../EncryptedOperations.sol";
|
|
8
8
|
import {EncryptedInput} from "../EncryptedInput.sol";
|
|
9
9
|
import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
|
|
10
|
-
import {ETypes, ebool, euint256, eaddress} from "../../Types.sol";
|
|
10
|
+
import {ETypes, ebool, euint256, eaddress, typeToBitMask} from "../../Types.sol";
|
|
11
11
|
|
|
12
12
|
contract TestHandleMetadata is
|
|
13
13
|
EIP712,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// SPDX-License-Identifier: No License
|
|
2
|
+
pragma solidity ^0.8;
|
|
3
|
+
|
|
4
|
+
import {IPCCSRouter} from "@automata-network/dcap-attestation/interfaces/IPCCSRouter.sol";
|
|
5
|
+
import {Header} from "@automata-network/dcap-attestation/types/CommonStruct.sol";
|
|
6
|
+
import {IQuoteVerifier} from "automata-dcap-attestation/interfaces/IQuoteVerifier.sol";
|
|
7
|
+
|
|
8
|
+
// This contract is used to test the IncoLightning contract. It is a simple implementation of the QuoteVerifier interface.
|
|
9
|
+
// It is used to test the IncoLightning contract without relying on the real QuoteVerifier contract.
|
|
10
|
+
contract FakeQuoteVerifier is IQuoteVerifier {
|
|
11
|
+
/// @dev immutable
|
|
12
|
+
function pccsRouter() external pure returns (IPCCSRouter) {
|
|
13
|
+
return IPCCSRouter(address(0));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/// @dev immutable
|
|
17
|
+
function quoteVersion() external pure returns (uint16) {
|
|
18
|
+
return 4;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function verifyQuote(Header calldata, bytes calldata quote) external pure returns (bool, bytes memory) {
|
|
22
|
+
return (true, quote);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function verifyZkOutput(bytes calldata quote) external pure returns (bool, bytes memory) {
|
|
26
|
+
return (true, quote);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// SPDX-License-Identifier: No License
|
|
2
|
+
pragma solidity ^0.8;
|
|
3
|
+
|
|
4
|
+
import {TestUtils} from "@inco/shared/src/TestUtils.sol";
|
|
5
|
+
import {TEELifecycle} from "@inco/lightning/src/lightning-parts/TEELifecycle.sol";
|
|
6
|
+
|
|
7
|
+
import {HEADER_LENGTH, MINIMUM_QUOTE_LENGTH, TDX_TEE} from "@automata-network/dcap-attestation/types/Constants.sol";
|
|
8
|
+
import {BootstrapResult} from "@inco/lightning/src/lightning-parts/TEELifecycle.types.sol";
|
|
9
|
+
|
|
10
|
+
contract MockRemoteAttestation is TestUtils {
|
|
11
|
+
function createQuote(
|
|
12
|
+
bytes memory mrtd,
|
|
13
|
+
address signer
|
|
14
|
+
) public pure returns (bytes memory quote) {
|
|
15
|
+
// Mock implementation of quote creation
|
|
16
|
+
require(mrtd.length == 48, "MRTD should be 48 bytes");
|
|
17
|
+
/* Quote structure:
|
|
18
|
+
- version ([0:4], 4 bytes long)
|
|
19
|
+
- teeType ([4:8], 4 bytes long)
|
|
20
|
+
- prefix ([8:HEADER_LENGTH+136], HEADER_LENGTH+136 - 8 bytes long)
|
|
21
|
+
- mrtd ([HEADER_LENGTH+136:HEADER_LENGTH+184], 48 bytes long)
|
|
22
|
+
- middle ([HEADER_LENGTH+184:HEADER_LENGTH+520], 336 bytes long)
|
|
23
|
+
- reportData ([HEADER_LENGTH+520:HEADER_LENGTH+584], 64 bytes long)
|
|
24
|
+
- signer ([HEADER_LENGTH+520:HEADER_LENGTH+540], 20 bytes long)
|
|
25
|
+
- reportDataSuffix ([HEADER_LENGTH+540:HEADER_LENGTH+584], 44 bytes long)
|
|
26
|
+
- suffix ([HEADER_LENGTH+584:MINIMUM_QUOTE_LENGTH], remaining bytes to reach MINIMUM_QUOTE_LENGTH)
|
|
27
|
+
|
|
28
|
+
*/
|
|
29
|
+
bytes4 version = 0x04000000; // Version 4
|
|
30
|
+
bytes4 tdxTEEType = TDX_TEE; // TDX TEETYPE
|
|
31
|
+
bytes memory prefix = new bytes(HEADER_LENGTH + 136 - 8);
|
|
32
|
+
bytes memory middle = new bytes(520 - 184);
|
|
33
|
+
bytes memory reportDataSuffix = new bytes(44);
|
|
34
|
+
bytes memory suffix = new bytes(MINIMUM_QUOTE_LENGTH - HEADER_LENGTH - 584);
|
|
35
|
+
quote = abi.encodePacked(version, tdxTEEType, prefix, mrtd, middle, abi.encodePacked(signer), reportDataSuffix, suffix);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -17,6 +17,7 @@ contract FibonacciDecrypt {
|
|
|
17
17
|
function fib(
|
|
18
18
|
uint256 n
|
|
19
19
|
) external returns (uint256 lastRequestId, euint256 nthTerm) {
|
|
20
|
+
require(address(inco) != address(0), "IncoLightning not set");
|
|
20
21
|
euint256 prev = inco.asEuint256(0);
|
|
21
22
|
lastRequestId = emitTerm(prev);
|
|
22
23
|
nthTerm = inco.asEuint256(1);
|
package/src/test/IncoTest.sol
CHANGED
|
@@ -7,6 +7,7 @@ import {inco} from "../Lib.sol";
|
|
|
7
7
|
import {DeployUtils} from "../DeployUtils.sol";
|
|
8
8
|
import {deployedBy} from "../Lib.sol";
|
|
9
9
|
import {console} from "forge-std/console.sol";
|
|
10
|
+
import {FakeQuoteVerifier} from "./FakeIncoInfra/FakeQuoteVerifier.sol";
|
|
10
11
|
|
|
11
12
|
contract IncoTest is MockOpHandler, DeployUtils {
|
|
12
13
|
address immutable owner;
|
|
@@ -25,12 +26,15 @@ contract IncoTest is MockOpHandler, DeployUtils {
|
|
|
25
26
|
function setUp() public virtual {
|
|
26
27
|
deployCreateX();
|
|
27
28
|
vm.startPrank(testDeployer);
|
|
29
|
+
vm.setEnv("USE_TDX_HW", "false"); // results in the test deployment using the FakeQuoteVerifier
|
|
28
30
|
IncoLightning proxy = deployIncoLightningUsingConfig({
|
|
29
31
|
deployer: testDeployer,
|
|
30
32
|
// The highest precedent deployment
|
|
31
33
|
pepper: "testnet",
|
|
32
34
|
minorVersionForSalt: 1,
|
|
33
|
-
patchVersionForSalt: 29
|
|
35
|
+
patchVersionForSalt: 29,
|
|
36
|
+
includePreviewFeatures: false,
|
|
37
|
+
teeLifecycleAddress: address(0)
|
|
34
38
|
});
|
|
35
39
|
proxy.transferOwnership(owner);
|
|
36
40
|
vm.stopPrank();
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# TEE Lifecycle Test
|
|
2
|
+
|
|
3
|
+
## TEELifecycle HW Test
|
|
4
|
+
|
|
5
|
+
This test data was generated using Adrian's V0 TDX VM running in GCP. The data
|
|
6
|
+
was returned collected using the `agent-lib` tool.
|
|
7
|
+
|
|
8
|
+
* To generate new test data: for `test_LifecycleBootstrap`
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
cd agent-lib
|
|
12
|
+
|
|
13
|
+
cargo run --features hw -- start-bootstrap \
|
|
14
|
+
--expected-mrtd 0x409c0cd3e63d9ea54d817cf851983a220131262664ac8cd02cc6a2e19fd291d2fdd0cc035d7789b982a43a92a4424c99 \
|
|
15
|
+
--tee-lifecycle-grpc-url http://34.91.236.235:4321 \
|
|
16
|
+
--output-dir ../contracts/lightning/src/test/TEELifecycle/bootstrap_data
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
* To generate new test data for `test_LifecycleNewEOA`
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
cd agent-lib
|
|
23
|
+
|
|
24
|
+
# TODO: change this to add-node endpoint after https://github.com/Inco-fhevm/inco-monorepo/issues/889
|
|
25
|
+
cargo run --features hw -- start-bootstrap \
|
|
26
|
+
--expected-mrtd 0x409c0cd3e63d9ea54d817cf851983a220131262664ac8cd02cc6a2e19fd291d2fdd0cc035d7789b982a43a92a4424c99 \
|
|
27
|
+
--tee-lifecycle-grpc-url http://34.91.236.235:4321 \
|
|
28
|
+
--output-dir ../contracts/lightning/src/test/TEELifecycle/addnode_data
|
|
29
|
+
|
|
30
|
+
# Delete the unused output data since it is not used
|
|
31
|
+
# to add a node
|
|
32
|
+
rm ../contracts/lightning/src/test/TEELifecycle/addnode_data/ecies_pubkey.bin
|
|
33
|
+
rm ../contracts/lightning/src/test/TEELifecycle/addnode_data/eip712_signature.bin
|
|
34
|
+
rm ../contracts/lightning/src/test/TEELifecycle/addnode_data/qe_identity
|
|
35
|
+
rm ../contracts/lightning/src/test/TEELifecycle/addnode_data/qe_identity_signature.bin
|
|
36
|
+
rm ../contracts/lightning/src/test/TEELifecycle/addnode_data/tcb_info
|
|
37
|
+
rm ../contracts/lightning/src/test/TEELifecycle/addnode_data/tcb_info_signature.bin
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
* To generate the Intel root certificates
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
cd contracts/lightning/src/test/TEELifecycle/test_cert
|
|
44
|
+
python3 -m pip install -r ../../../../../lightning-deployment/script/tee/requirements.txt
|
|
45
|
+
python3 ../../../../../lightning-deployment/script/tee/get_ca_certs.py
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
* Hard code the block timestamp to the current time to ensure that there is no certificate out of date errors
|
|
49
|
+
by setting `uint256 collateral_timestamp =` in [TEELifecycleHWTest.t](TEELifecycleHWTest.t.sol#24) to the output of:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
echo $(date +%s)
|
|
53
|
+
```
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// SPDX-License-Identifier: No License
|
|
2
|
+
pragma solidity ^0.8;
|
|
3
|
+
|
|
4
|
+
import "forge-std/Test.sol";
|
|
5
|
+
|
|
6
|
+
import "../../lightning-parts/TEELifecycle.types.sol";
|
|
7
|
+
import "../../lightning-parts/TEELifecycle.sol";
|
|
8
|
+
import "../../DeployUtils.sol";
|
|
9
|
+
import "../../DeployTEE.sol";
|
|
10
|
+
import {TestUtils} from "@inco/shared/src/TestUtils.sol";
|
|
11
|
+
|
|
12
|
+
import {EnclaveIdentityJsonObj} from "@automata-network/on-chain-pccs/helpers/EnclaveIdentityHelper.sol";
|
|
13
|
+
import {TcbInfoJsonObj} from "@automata-network/on-chain-pccs/helpers/FmspcTcbHelper.sol";
|
|
14
|
+
import {IQuoteVerifier} from "automata-dcap-attestation/interfaces/IQuoteVerifier.sol";
|
|
15
|
+
|
|
16
|
+
contract TEELifecycleHWTest is DeployUtils , TestUtils, DeployTEE {
|
|
17
|
+
using stdStorage for StdStorage;
|
|
18
|
+
|
|
19
|
+
// This is the MRTD from Adrian's v0 TDX VM running on GCP that was used to generate the test data.
|
|
20
|
+
bytes public v0mrtd = hex"409c0cd3e63d9ea54d817cf851983a220131262664ac8cd02cc6a2e19fd291d2fdd0cc035d7789b982a43a92a4424c99";
|
|
21
|
+
|
|
22
|
+
// This is the address that is used as the lifecycle contract address in Adrian's v0 TDX VM.
|
|
23
|
+
address lifecycleAddress = 0x63D8135aF4D393B1dB43B649010c8D3EE19FC9fd;
|
|
24
|
+
|
|
25
|
+
// Avoid expired collateral and certificate errors by setting time to
|
|
26
|
+
// the date when the test data was generated.
|
|
27
|
+
uint256 collateral_timestamp = 1754498833;
|
|
28
|
+
|
|
29
|
+
// This is the location of the bootstrap and add node and test data files
|
|
30
|
+
string bootstrapDir;
|
|
31
|
+
string addNodeDir;
|
|
32
|
+
|
|
33
|
+
TEELifecycle lifecycle;
|
|
34
|
+
|
|
35
|
+
address immutable owner;
|
|
36
|
+
|
|
37
|
+
constructor() {
|
|
38
|
+
owner = getLabeledAddress("owner");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function setUp() public {
|
|
42
|
+
// This test data was generate by running the TEELifecycle binary in a real TDX environment
|
|
43
|
+
// to generate the quote and other files.
|
|
44
|
+
bootstrapDir = string.concat(vm.projectRoot(), "/src/test/TEELifecycle/bootstrap_data/");
|
|
45
|
+
addNodeDir = string.concat(vm.projectRoot(), "/src/test/TEELifecycle/addnode_data/");
|
|
46
|
+
string memory certDir = string.concat(vm.projectRoot(), "/src/test/TEELifecycle/test_cert/");
|
|
47
|
+
|
|
48
|
+
vm.warp(collateral_timestamp);
|
|
49
|
+
vm.startPrank(owner);
|
|
50
|
+
|
|
51
|
+
// PCCS Setup
|
|
52
|
+
deployP256();
|
|
53
|
+
deployPCCS(owner, certDir);
|
|
54
|
+
IQuoteVerifier quoteVerifier = deployQuoteVerifier();
|
|
55
|
+
|
|
56
|
+
bytes memory lifecycleCode = address(deployTEELifecycle(owner, address(quoteVerifier))).code;
|
|
57
|
+
// Make sure the verifyingContract for the EIP217 domain matches the one in the test data
|
|
58
|
+
// By using vm.etch to deploy the TEELifecycle contract at a specific address
|
|
59
|
+
vm.etch(lifecycleAddress, lifecycleCode);
|
|
60
|
+
lifecycle = TEELifecycle(lifecycleAddress);
|
|
61
|
+
|
|
62
|
+
// Make sure the name and version for the EIP712 domain matches the one in the test data
|
|
63
|
+
lifecycle.initialize(owner, "IncoTeeLifecycleBootstrap", "1.0.0", address(quoteVerifier));
|
|
64
|
+
|
|
65
|
+
vm.stopPrank();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function test_LifecycleBootstrap() public {
|
|
69
|
+
_uploadCollateral(bootstrapDir);
|
|
70
|
+
string memory expected_address_string = vm.readFile(string.concat(bootstrapDir, "eoa.txt"));
|
|
71
|
+
address expected_address = vm.parseAddress(expected_address_string);
|
|
72
|
+
|
|
73
|
+
vm.startPrank(owner);
|
|
74
|
+
bytes memory pubkey = vm.readFileBinary(string.concat(bootstrapDir, "ecies_pubkey.bin"));
|
|
75
|
+
BootstrapResult memory bootstrapResult = BootstrapResult({ecies_pubkey: pubkey});
|
|
76
|
+
bytes memory quote = vm.readFileBinary(string.concat(bootstrapDir, "quote.bin"));
|
|
77
|
+
bytes memory sig = vm.readFileBinary(string.concat(bootstrapDir, "eip712_signature.bin"));
|
|
78
|
+
lifecycle.approveNewTEEVersion(v0mrtd);
|
|
79
|
+
lifecycle.verifyBootstrapResult(bootstrapResult, quote, sig);
|
|
80
|
+
|
|
81
|
+
(bytes memory mrtd,) = lifecycle.TEEVersionHistory(0);
|
|
82
|
+
assert(keccak256(mrtd) == keccak256(v0mrtd));
|
|
83
|
+
assert(lifecycle.isBootstrapComplete() == true);
|
|
84
|
+
assert(keccak256(lifecycle.ECIESPubkey()) == keccak256(pubkey));
|
|
85
|
+
assert(lifecycle.EOASigners(expected_address));
|
|
86
|
+
vm.stopPrank();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function test_LifecycleNewEOA() public {
|
|
90
|
+
// This test assumes that the lifecycle bootstrap is already completed
|
|
91
|
+
test_LifecycleBootstrap();
|
|
92
|
+
|
|
93
|
+
string memory expected_address_string = vm.readFile(string.concat(addNodeDir, "eoa.txt"));
|
|
94
|
+
address expected_address = vm.parseAddress(expected_address_string);
|
|
95
|
+
|
|
96
|
+
vm.startPrank(owner);
|
|
97
|
+
bytes memory quote2 = vm.readFileBinary(string.concat(addNodeDir, "quote.bin"));
|
|
98
|
+
lifecycle.addNewCovalidator(quote2);
|
|
99
|
+
vm.stopPrank();
|
|
100
|
+
|
|
101
|
+
assert(lifecycle.EOASigners(expected_address));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function _uploadCollateral(string memory collateralDir) private {
|
|
105
|
+
vm.startPrank(owner);
|
|
106
|
+
// upload collateral
|
|
107
|
+
string memory tcbInfoStr = vm.readFile(string.concat(collateralDir, "tcb_info"));
|
|
108
|
+
bytes memory tcbInfoSig = vm.readFileBinary(string.concat(collateralDir, "tcb_info_signature.bin"));
|
|
109
|
+
TcbInfoJsonObj memory tcbInfo = TcbInfoJsonObj(tcbInfoStr, tcbInfoSig);
|
|
110
|
+
|
|
111
|
+
string memory qeIdStr = vm.readFile(string.concat(collateralDir, "qe_identity"));
|
|
112
|
+
bytes memory qeIdSig = vm.readFileBinary(string.concat(collateralDir, "qe_identity_signature.bin"));
|
|
113
|
+
EnclaveIdentityJsonObj memory identityJson = EnclaveIdentityJsonObj(qeIdStr, qeIdSig);
|
|
114
|
+
|
|
115
|
+
lifecycle.uploadCollateral(tcbInfo, identityJson);
|
|
116
|
+
|
|
117
|
+
vm.stopPrank();
|
|
118
|
+
}
|
|
119
|
+
}
|