@ethernity-cloud/sdk-js 1.1.1 → 1.4.0

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.
@@ -1,421 +1,444 @@
1
- require('dotenv').config();
2
- require('dotenv').config({path: './app/.env'});
3
- require('dotenv').config({path: '/app/.env'});
4
-
5
- const ethers = require('ethers');
6
- const fs = require("fs");
7
- const etny_crypto = require("./etny_crypto");
8
- const {DOReqMetadata, Order} = require("./models");
9
- const {getWalletAddress} = require("./wallet");
10
- const {executeTask} = require("./etny_exec");
11
- const {TaskStatus} = require("./task_status");
12
- const SwiftStreamService = require("./swift_stream_service");
13
-
14
- const contract_abi = require('./pox_abi');
15
- const image_registry_abi = require('./image_registry_abi');
16
- const {sha256} = require("./etny_crypto");
17
-
18
- class EtnySecureLock {
19
- static debug = false;
20
-
21
- constructor(swiftStreamClient) {
22
- console.log('Initializing...');
23
- this.version = 'v3';
24
- this.swiftStreamService = swiftStreamClient;
25
- this.etny_bucket = "__BUCKET_NAME__";
26
- this.setInitializers();
27
- }
28
-
29
- setInitializers() {
30
- this.is_valid_client_data = true;
31
- this.key_file = "/private/__ENCLAVE_NAME_SECURELOCK__/key.pem";
32
- this.cert_file = "/app/__ENCLAVE_NAME_SECURELOCK__/cert.pem";
33
- this.pub_cert_file = "/app/__ENCLAVE_NAME_SECURELOCK__/enclave_pub_cert.pem";
34
- this.payload = "payload.etny.securelock";
35
- this.input = "input.txt.securelock";
36
- this.result_file = "/app/result.txt";
37
- this.transaction_file = "/app/transaction.txt";
38
- this.smart_contract_address = "__SMART_CONTRACT_ADDRESS__";
39
- this.image_registry_address = '__IMAGE_REGISTRY_ADDRESS__';
40
- this.chain_id = __CHAIN_ID__;
41
- this.web3_provider = "__RPC_URL__";
42
- this.signed_tx_as_bytes = null;
43
- this.client_challenge = "";
44
-
45
- if (this.etny_mode === "TEST") {
46
- this.key_file = "/private/key.pem";
47
- this.cert_file = "/app/cert.pem";
48
- this.payload = "./app/payload.js";
49
- this.input = "./app/input.txt";
50
- this.result_file = "./app/result.txt";
51
- this.transaction_file = "./app/transaction.txt";
52
- this.pub_cert_file = "./app/enclave_pub_cert.pem";
53
- }
54
-
55
- if (this.etny_mode === "LOCAL_TEST") {
56
- this.key_file = "./certs/key.pem";
57
- this.cert_file = "./certs/cert.pem";
58
- this.payload = "./app/payload.js";
59
- this.input = "./app/input.txt";
60
- this.result_file = "./app/result.txt";
61
- this.transaction_file = "./app/transaction.txt";
62
- this.pub_cert_file = "./app/enclave_pub_cert.pem";
63
- }
64
- }
65
-
66
- async create() {
67
- //overriding default values with those read from .env file
68
- this.loadEnv();
69
- this.printEnv();
70
- await this.savePublicCert();
71
- this.initWeb3();
72
- if (this.etny_mode === "PRODUCTION") {
73
- this.resetPrivateCertFile();
74
- }
75
-
76
- return true;
77
- }
78
-
79
- loadEnv() {
80
- if (process.env.ETNY_CHAIN_ID) {
81
- this.chain_id = parseInt(process.env.ETNY_CHAIN_ID);
82
- }
83
-
84
- if (process.env.ETNY_SMART_CONTRACT_ADDRESS) {
85
- this.smart_contract_address = process.env.ETNY_SMART_CONTRACT_ADDRESS.trim();
86
- }
87
-
88
- if (process.env.ETNY_WEB3_PROVIDER) {
89
- this.web3_provider = process.env.ETNY_WEB3_PROVIDER.trim();
90
- }
91
-
92
- if (process.env.ETNY_CLIENT_CHALLENGE) {
93
- this.client_challenge = process.env.ETNY_CLIENT_CHALLENGE.trim();
94
- }
95
- if (process.env.ETNY_ORDER_ID) {
96
- this.order_id = parseInt(process.env.ETNY_ORDER_ID);
97
- }
98
- }
99
-
100
- printEnv() {
101
- console.log('CHAIN ID:', this.chain_id);
102
- console.log('SMART CONTRACT ADDRESS:', this.smart_contract_address);
103
- console.log('WEB3 PROVIDER:', this.web3_provider);
104
- console.log('ENCRYPTED CHALLENGE:', this.client_challenge);
105
- console.log('ORDER ID:', this.order_id);
106
- }
107
-
108
- initWeb3() {
109
- // Create an ethers.js provider connected to the Bloxberg network
110
- this.w3 = new ethers.providers.JsonRpcProvider(this.web3_provider);
111
- // Create an instance of the contract using the ABI and address
112
- this.etny = new ethers.Contract(this.smart_contract_address, contract_abi, this.w3);
113
- this.image_registry = new ethers.Contract(this.image_registry_address, image_registry_abi, this.w3);
114
- // this.iamge_registry_signer = new ethers.Contract(this.image_registry_address, image_registry_abi.abi, this.walletSigner);
115
- }
116
-
117
- async readEnv() {
118
- try {
119
- console.log('Getting .env if exists...');
120
- const envFile = await this.swiftStreamService.getFileContent(this.etny_bucket, ".env");
121
- console.log("Env:", envFile);
122
- console.log("Saving .env file...");
123
- await fs.promises.writeFile('.env', envFile);
124
- require('dotenv').config();
125
-
126
- this.version = 'v3';
127
- this.etny_mode = process.env.ETNY_MODE;
128
- console.log(`ETNY Nodenithy [${this.version}] - running in ${this.etny_mode} MODE`);
129
- //console.log(process.env);
130
-
131
- return true;
132
- } catch (e) {
133
- return false;
134
- }
135
- }
136
-
137
- extractSigner(message, signature) {
138
- try {
139
- return ethers.utils.verifyMessage(message, signature);
140
- } catch (e) {
141
- console.log('extractSigner:', e)
142
- }
143
- }
144
-
145
- async validateClientPayload(payload_data, input_data) {
146
- await this.getDORequestMetadata();
147
- if (this._metadata.payload_metadata_obj.checksum !== null) {
148
- try {
149
- const payload_checksum = sha256(payload_data);
150
- //console.log('payload checksum: ', payload_checksum, "===", this._metadata.payload_metadata_obj.checksum);
151
- // check the wallet address that signed payload checksum is the one from order metadata
152
- const payload_signer = this.extractSigner(payload_checksum, this._metadata.payload_metadata_obj.checksum);
153
- //console.log("payload_signer", payload_signer);
154
- //console.log("this._order_metadata.do_owner.toLowerCase()", this._order_metadata.do_owner.toLowerCase());
155
- if (payload_signer.toLowerCase() !== this._order_metadata.do_owner.toLowerCase()) {
156
- this.task_code = TaskStatus.PAYLOAD_CHECKSUM_ERROR;
157
- this.task_result = 'PAYLOAD CHECKSUM DOESN\'T MATCH';
158
- this.is_valid_client_data = false;
159
- return;
160
- }
161
- } catch (e) {
162
- console.log(e);
163
- }
164
-
165
- }
166
- if (this._metadata.input_metadata_obj.checksum !== null) {
167
- try {
168
- const input_checksum = sha256(input_data);
169
- //console.log('input checksum: ', input_checksum, '===', this._metadata.input_metadata_obj.checksum);
170
- // check the wallet address that signed input checksum is the one from order metadata
171
- const input_signer = this.extractSigner(input_checksum, this._metadata.input_metadata_obj.checksum);
172
- //console.log("input_signer", input_signer);
173
- //console.log("this._order_metadata.do_owner.toLowerCase()", this._order_metadata.do_owner.toLowerCase());
174
- if (input_signer.toLowerCase() !== this._order_metadata.do_owner.toLowerCase()) {
175
- this.task_code = TaskStatus.INPUT_CHECKSUM_ERROR;
176
- this.task_result = 'INPUT CHECKSUM DOESN\'T MATCH';
177
- this.is_valid_client_data = false;
178
- }
179
- } catch (e) {
180
- console.log(e);
181
- }
182
- }
183
- }
184
-
185
- wait(timeout) {
186
- return new Promise(resolve => {
187
- setTimeout(resolve, timeout);
188
- });
189
- }
190
-
191
- async waitForObjectInBucket(bucketName, objectName, timeout = 600) {
192
- let i = 0;
193
- console.log(`Checking if object ${objectName} exists in bucket ${bucketName}`);
194
- while (true) {
195
- await this.wait(1000);
196
- i++;
197
- if (i % 20 === 0)
198
- console.log(`Checking if object ${objectName} exists in bucket ${bucketName}`);
199
- if (i > timeout) {
200
- break;
201
- }
202
- const [status, result] = await this.swiftStreamService.isObjectInBucket(bucketName, objectName);
203
- if (result) {
204
- break;
205
- }
206
- }
207
-
208
- console.log('TrustedZone finished the execution');
209
- }
210
-
211
- async waitForPayloadAndInput() {
212
- console.log('Waiting for payload and input');
213
- await this.waitForObjectInBucket(this.etny_bucket, this.payload);
214
- await this.waitForObjectInBucket(this.etny_bucket, this.input);
215
- }
216
-
217
- async getFileContentAndDecrypt(bucketName, objectName) {
218
- try {
219
- const encryptedBase64 = await this.swiftStreamService.getFileContent(bucketName, objectName);
220
-
221
- const encryptedTuple = etny_crypto.encryptedDataFromBase64Json(encryptedBase64);
222
- const decryptedResult = etny_crypto.decrypt(this.key_file, encryptedTuple);
223
- return decryptedResult.toString();
224
- } catch (e) {
225
- console.log(`Failed to get ${objectName} file`);
226
- return false;
227
- }
228
- }
229
-
230
- async getTrustedZonePublicKey() {
231
- try {
232
- console.log('Getting the public key of the trusted zone enclave');
233
- let imageHash = this._metadata.image_hash;
234
- console.log('imageHash:', imageHash);
235
- if (EtnySecureLock.debug) {
236
- imageHash = 'v3-hash-1';
237
- }
238
- this.trustedZonePublicKey = await this.image_registry.getTrustedZoneImageCertPublicKey(imageHash);
239
- console.log('Public key of the TrustedZone enclave from smart contract:', this.trustedZonePublicKey);
240
- } catch (e) {
241
- console.log('getTrustedZonePublicKey', e);
242
- }
243
- }
244
-
245
- async getLatestTrustedZoneImageCertPublicKey() {
246
- try {
247
- console.log('Getting the public key of the trusted zone enclave');
248
- this.trustedZonePublicKey = (await this.image_registry.getLatestTrustedZoneImageCertPublicKey("__TRUSTED_ZONE_IMAGE__", 'v3'))[1];
249
- console.log('Public key of the TrustedZone enclave from smart contract:', this.trustedZonePublicKey);
250
- } catch (e) {
251
- console.log('getLatestTrustedZoneImageCertPublicKey', e);
252
- }
253
- }
254
-
255
- async encryptFileAndPushToSwiftStream(fileData, fileName) {
256
- try {
257
- console.log(`Started saving file ${fileName}.securelock...`);
258
- const encryptedInput = etny_crypto.encrypt(this.trustedZonePublicKey, fileData);
259
- const encryptedInputBase64 = etny_crypto.encryptedDataToBase64Json(encryptedInput);
260
-
261
- // File name should end with the '.securelock' extension
262
- fileName += '.securelock';
263
- // const data = Buffer.from(encryptedInputBase64, 'utf8');
264
- const status = this.swiftStreamService.putFileContent(this.etny_bucket, fileName, '', encryptedInputBase64);
265
- if (status) {
266
- console.log(`File ${fileName} encrypted and saved to swift stream successfully`);
267
- }
268
- } catch (e) {
269
- console.log('encryptFileAndPushToSwiftStream', e);
270
- }
271
- }
272
-
273
- async getDORequestMetadata() {
274
- const o_result = await this.etny.functions._getOrder(this.order_id);
275
- const order = new Order(o_result, this.order_id);
276
- //console.log('order', order);
277
- this._order_metadata = order;
278
- const o_metadata = await this.etny.functions._getDORequestMetadata(order.do_req);
279
- this._metadata = new DOReqMetadata(o_metadata, order.do_req);
280
- //console.log('metadata', this._metadata);
281
- }
282
-
283
- resetPrivateCertFile() {
284
- //console.log('Resetting the cert file');
285
- try {
286
- // const fd = fs.openSync(this.private_key_file, 'w+');
287
- fs.truncate(this.key_file, 0, function () {
288
- //console.log('Private certificate file was cleaned.')
289
- });
290
- // fs.closeSync(fd);
291
- } catch (e) {
292
- //console.log('Error while removing the private key file content', e);
293
- }
294
- const content = fs.readFileSync(this.key_file, 'utf8');
295
- //console.log('cert file content', content, '##################');
296
- }
297
-
298
- async savePublicCert() {
299
- try {
300
- const content = await fs.promises.readFile(this.cert_file, 'utf8');
301
- await fs.promises.writeFile(this.pub_cert_file, content);
302
- //console.log(`Public certificate saved to ${this.pub_cert_file}`);
303
- console.log(`PUBLIC_KEY:`);
304
- console.log(content);
305
-
306
- await this.swiftStreamService.putFileContent(this.etny_bucket, 'cert.pem', this.pub_cert_file);
307
- } catch (err) {
308
- console.error(err);
309
- }
310
- }
311
-
312
- async saveResult() {
313
- try {
314
- await this.getLatestTrustedZoneImageCertPublicKey();
315
- await this.encryptFileAndPushToSwiftStream(this.task_result, 'result.txt');
316
- await this.encryptFileAndPushToSwiftStream(this.task_code.toString(), 'result_code.txt');
317
-
318
- const encrypted = etny_crypto.encryptWithPublicKey(this._metadata.public_key, this.task_result);
319
- //console.log('Encrypted result:', encrypted.toString('hex'));
320
- const result = await this.swiftStreamService.putFileContent(this.etny_bucket, 'result.txt', this.result_file, encrypted.toString('hex'));
321
- //console.log('Save result put: ', result);
322
- } catch (e) {
323
- console.log('Save result: ', e);
324
- }
325
- }
326
-
327
- async execute() {
328
- const payload_data = await this.getFileContentAndDecrypt(this.etny_bucket, this.payload);
329
- const input_data = await this.getFileContentAndDecrypt(this.etny_bucket, this.input);
330
- console.log('Validating client payload and input...');
331
- await this.validateClientPayload(payload_data, input_data);
332
-
333
- if (this.is_valid_client_data) {
334
- console.log('Client payload and input are valid');
335
- } else {
336
- console.log('Client payload and input are NOT valid');
337
- return false;
338
- }
339
-
340
- const task_result = executeTask(payload_data, input_data);
341
- console.log('TASK RESULT (code, data):', task_result);
342
- this.task_code = task_result[0];
343
- this.task_result = task_result[1];
344
-
345
- return true;
346
- }
347
-
348
- }
349
-
350
- function initSwiftStreamClient() {
351
- console.log('Initializing SwiftStream Service...');
352
- const swiftStreamClient = new SwiftStreamService(
353
- EtnySecureLock.debug ? "localhost" : "etny-swift-stream",
354
- 9000,
355
- "swiftstreamadmin",
356
- "swiftstreamadmin");
357
- console.log('SwiftStream Service started.');
358
- console.log('################################');
359
-
360
- return swiftStreamClient;
361
- }
362
-
363
- async function execute() {
364
- console.log('########################################');
365
- console.log('########################################');
366
- console.log('########## [ETNY SecureLock] ##########');
367
- console.log('########################################');
368
- console.log('########################################');
369
- console.log('');
370
- console.log('');
371
- console.log('');
372
-
373
- const swiftStreamClient = initSwiftStreamClient();
374
-
375
- const app = new EtnySecureLock(swiftStreamClient);
376
-
377
- const hasReadEnvFile = await app.readEnv();
378
- if (!hasReadEnvFile && !EtnySecureLock.debug) {
379
- console.log(`Failed reading .env file.`);
380
- await app.savePublicCert();
381
- return;
382
- }
383
-
384
- if (process.env.ETNY_MODE === "PUBLIC_KEY") {
385
- await app.savePublicCert();
386
- } else {
387
- console.log('Creating the application...');
388
- const created = await app.create();
389
-
390
- if (!created) {
391
- console.log(`Failed at creation step, not able decoding input metadata, exiting.`);
392
- return;
393
- }
394
-
395
- console.log('Waiting for payload and input to be downloaded...');
396
- await app.waitForPayloadAndInput();
397
-
398
- if (app.is_valid_client_data) {
399
- console.log('Executing client code...');
400
- const wasExecuted = await app.execute();
401
- if (!wasExecuted) {
402
- console.log('Failed execution of SecureLock enclave.')
403
- return;
404
- }
405
- } else {
406
- console.log(`Failed client payload and input validation, ${app.task_result}, exiting.`);
407
- return;
408
- }
409
-
410
- await app.saveResult();
411
- console.log('Finished process.');
412
- }
413
- }
414
-
415
- (async () => {
416
- try {
417
- await execute();
418
- } catch (e) {
419
- console.log(e);
420
- }
421
- })();
1
+ // Force stdout/stderr to be synchronous (blocking). Under SCONE 6 + Node, when
2
+ // the process exits quickly (e.g. the PUBLIC_KEY cert-harvest run, which prints
3
+ // the cert then returns) Node's pipe-buffered stdout can be torn down before it
4
+ // flushes, so the harvested "PUBLIC_KEY:" line is lost and the CI cannot read
5
+ // the certificate. Setting the fd handles to blocking makes every console.log
6
+ // write synchronously.
7
+ try { if (process.stdout._handle && process.stdout._handle.setBlocking) process.stdout._handle.setBlocking(true); } catch (e) {}
8
+ try { if (process.stderr._handle && process.stderr._handle.setBlocking) process.stderr._handle.setBlocking(true); } catch (e) {}
9
+
10
+ require('dotenv').config();
11
+ require('dotenv').config({path: './app/.env'});
12
+ require('dotenv').config({path: '/app/.env'});
13
+
14
+ const ethers = require('ethers');
15
+ const fs = require("fs");
16
+ const etny_crypto = require("./etny_crypto");
17
+ const {DOReqMetadata, Order} = require("./models");
18
+ const {getWalletAddress} = require("./wallet");
19
+ const {executeTask} = require("./etny_exec");
20
+ const {TaskStatus} = require("./task_status");
21
+ const SwiftStreamService = require("./swift_stream_service");
22
+
23
+ const contract_abi = require('./pox_abi');
24
+ const image_registry_abi = require('./image_registry_abi');
25
+ const {sha256} = require("./etny_crypto");
26
+ const {generateCertFromMrEnclave} = require("./self_sign");
27
+
28
+ // Baked per network by CI (render_network.sh): "mainnet" | "testnet". On testnet
29
+ // the enclave self-signs its cert from MR_ENCLAVE (non-CAS mode); on mainnet the
30
+ // cert/key are provisioned by CAS into the injection paths.
31
+ const NETWORK_TYPE = "__ETNY_NETWORK_TYPE__";
32
+
33
+ class EtnySecureLock {
34
+ static debug = false;
35
+
36
+ constructor(swiftStreamClient) {
37
+ console.log('Initializing...');
38
+ this.version = 'v3';
39
+ this.swiftStreamService = swiftStreamClient;
40
+ this.etny_bucket = "__ETNY_BUCKET__";
41
+ this.setInitializers();
42
+ }
43
+
44
+ setInitializers() {
45
+ this.is_valid_client_data = true;
46
+ this.key_file = "/private/__ENCLAVE_NAME_SECURELOCK__/key.pem";
47
+ this.cert_file = "/app/__ENCLAVE_NAME_SECURELOCK__/cert.pem";
48
+ this.pub_cert_file = "/app/__ENCLAVE_NAME_SECURELOCK__/enclave_pub_cert.pem";
49
+ this.payload = "payload.etny.securelock";
50
+ this.input = "input.txt.securelock";
51
+ this.result_file = "/app/result.txt";
52
+ this.transaction_file = "/app/transaction.txt";
53
+ this.smart_contract_address = "__ETNY_SMART_CONTRACT_ADDRESS__";
54
+ this.image_registry_address = '__ETNY_IMAGE_REGISTRY_ADDRESS__';
55
+ this.chain_id = __ETNY_CHAIN_ID__;
56
+ this.web3_provider = "__ETNY_WEB3_PROVIDER__";
57
+ this.signed_tx_as_bytes = null;
58
+ this.client_challenge = "";
59
+
60
+ if (this.etny_mode === "TEST") {
61
+ this.key_file = "/private/key.pem";
62
+ this.cert_file = "/app/cert.pem";
63
+ this.payload = "./app/payload.js";
64
+ this.input = "./app/input.txt";
65
+ this.result_file = "./app/result.txt";
66
+ this.transaction_file = "./app/transaction.txt";
67
+ this.pub_cert_file = "./app/enclave_pub_cert.pem";
68
+ }
69
+
70
+ if (this.etny_mode === "LOCAL_TEST") {
71
+ this.key_file = "./certs/key.pem";
72
+ this.cert_file = "./certs/cert.pem";
73
+ this.payload = "./app/payload.js";
74
+ this.input = "./app/input.txt";
75
+ this.result_file = "./app/result.txt";
76
+ this.transaction_file = "./app/transaction.txt";
77
+ this.pub_cert_file = "./app/enclave_pub_cert.pem";
78
+ }
79
+ }
80
+
81
+ async create() {
82
+ //overriding default values with those read from .env file
83
+ this.loadEnv();
84
+ this.printEnv();
85
+ await this.savePublicCert();
86
+ this.initWeb3();
87
+ if (this.etny_mode === "PRODUCTION") {
88
+ this.resetPrivateCertFile();
89
+ }
90
+
91
+ return true;
92
+ }
93
+
94
+ loadEnv() {
95
+ if (process.env.ETNY_CHAIN_ID) {
96
+ this.chain_id = parseInt(process.env.ETNY_CHAIN_ID);
97
+ }
98
+
99
+ if (process.env.ETNY_SMART_CONTRACT_ADDRESS) {
100
+ this.smart_contract_address = process.env.ETNY_SMART_CONTRACT_ADDRESS.trim();
101
+ }
102
+
103
+ if (process.env.ETNY_WEB3_PROVIDER) {
104
+ this.web3_provider = process.env.ETNY_WEB3_PROVIDER.trim();
105
+ }
106
+
107
+ if (process.env.IMAGE_REGISTRY_ADDRESS) {
108
+ this.image_registry_address = process.env.IMAGE_REGISTRY_ADDRESS.trim();
109
+ }
110
+
111
+ if (process.env.ETNY_BUCKET) {
112
+ this.etny_bucket = process.env.ETNY_BUCKET.trim();
113
+ }
114
+
115
+ if (process.env.ETNY_CLIENT_CHALLENGE) {
116
+ this.client_challenge = process.env.ETNY_CLIENT_CHALLENGE.trim();
117
+ }
118
+ if (process.env.ETNY_ORDER_ID) {
119
+ this.order_id = parseInt(process.env.ETNY_ORDER_ID);
120
+ }
121
+ }
122
+
123
+ printEnv() {
124
+ console.log('CHAIN ID:', this.chain_id);
125
+ console.log('SMART CONTRACT ADDRESS:', this.smart_contract_address);
126
+ console.log('WEB3 PROVIDER:', this.web3_provider);
127
+ console.log('ENCRYPTED CHALLENGE:', this.client_challenge);
128
+ console.log('ORDER ID:', this.order_id);
129
+ }
130
+
131
+ initWeb3() {
132
+ // Create an ethers.js provider connected to the Bloxberg network
133
+ this.w3 = new ethers.providers.JsonRpcProvider(this.web3_provider);
134
+ // Create an instance of the contract using the ABI and address
135
+ this.etny = new ethers.Contract(this.smart_contract_address, contract_abi, this.w3);
136
+ this.image_registry = new ethers.Contract(this.image_registry_address, image_registry_abi, this.w3);
137
+ // this.iamge_registry_signer = new ethers.Contract(this.image_registry_address, image_registry_abi.abi, this.walletSigner);
138
+ }
139
+
140
+ async readEnv() {
141
+ try {
142
+ console.log('Getting .env if exists...');
143
+ const envFile = await this.swiftStreamService.getFileContent(this.etny_bucket, ".env");
144
+ console.log("Env:", envFile);
145
+ console.log("Saving .env file...");
146
+ await fs.promises.writeFile('.env', envFile);
147
+ require('dotenv').config();
148
+
149
+ this.version = 'v3';
150
+ this.etny_mode = process.env.ETNY_MODE;
151
+ console.log(`ETNY Nodenithy [${this.version}] - running in ${this.etny_mode} MODE`);
152
+ //console.log(process.env);
153
+
154
+ return true;
155
+ } catch (e) {
156
+ return false;
157
+ }
158
+ }
159
+
160
+ // v4 checksum validation: the DO-request is submitted on-chain by the owner,
161
+ // so the metadata checksum is already authenticated by the transaction sender;
162
+ // we only confirm the file content hashes to that recorded checksum. Empty
163
+ // input carries no checksum (has_checksum === false) and is skipped. Kept
164
+ // identical across securelock / trustedzone / validator so all three agree.
165
+ async validateClientPayload(payload_data, input_data) {
166
+ await this.getDORequestMetadata();
167
+
168
+ const pmeta = this._metadata.payload_metadata_obj;
169
+ if (pmeta.has_checksum) {
170
+ const expected = pmeta.checksum.toLowerCase().replace(/^0x/, '');
171
+ if (sha256(payload_data) !== expected) {
172
+ this.task_code = TaskStatus.PAYLOAD_CHECKSUM_ERROR;
173
+ this.task_result = 'PAYLOAD CHECKSUM DOESN\'T MATCH';
174
+ this.is_valid_client_data = false;
175
+ return;
176
+ }
177
+ } else {
178
+ // payload is required and must carry a checksum
179
+ this.task_code = TaskStatus.PAYLOAD_CHECKSUM_ERROR;
180
+ this.task_result = 'PAYLOAD CHECKSUM DOESN\'T EXIST';
181
+ this.is_valid_client_data = false;
182
+ return;
183
+ }
184
+
185
+ const imeta = this._metadata.input_metadata_obj;
186
+ if (imeta.has_checksum) {
187
+ const expected = imeta.checksum.toLowerCase().replace(/^0x/, '');
188
+ if (sha256(input_data) !== expected) {
189
+ this.task_code = TaskStatus.INPUT_CHECKSUM_ERROR;
190
+ this.task_result = 'INPUT CHECKSUM DOESN\'T MATCH';
191
+ this.is_valid_client_data = false;
192
+ }
193
+ }
194
+ }
195
+
196
+ wait(timeout) {
197
+ return new Promise(resolve => {
198
+ setTimeout(resolve, timeout);
199
+ });
200
+ }
201
+
202
+ async waitForObjectInBucket(bucketName, objectName, timeout = 600) {
203
+ let i = 0;
204
+ console.log(`Checking if object ${objectName} exists in bucket ${bucketName}`);
205
+ while (true) {
206
+ await this.wait(1000);
207
+ i++;
208
+ if (i % 20 === 0)
209
+ console.log(`Checking if object ${objectName} exists in bucket ${bucketName}`);
210
+ if (i > timeout) {
211
+ break;
212
+ }
213
+ const [status, result] = await this.swiftStreamService.isObjectInBucket(bucketName, objectName);
214
+ if (result) {
215
+ break;
216
+ }
217
+ }
218
+
219
+ console.log('TrustedZone finished the execution');
220
+ }
221
+
222
+ async waitForPayloadAndInput() {
223
+ console.log('Waiting for payload and input');
224
+ await this.waitForObjectInBucket(this.etny_bucket, this.payload);
225
+ await this.waitForObjectInBucket(this.etny_bucket, this.input);
226
+ }
227
+
228
+ async getFileContentAndDecrypt(bucketName, objectName) {
229
+ try {
230
+ const encryptedBase64 = await this.swiftStreamService.getFileContent(bucketName, objectName);
231
+
232
+ const encryptedTuple = etny_crypto.encryptedDataFromBase64Json(encryptedBase64);
233
+ const decryptedResult = etny_crypto.decrypt(this.key_file, encryptedTuple);
234
+ return decryptedResult.toString();
235
+ } catch (e) {
236
+ console.log(`Failed to get ${objectName} file`);
237
+ return false;
238
+ }
239
+ }
240
+
241
+ async getTrustedZonePublicKey() {
242
+ try {
243
+ console.log('Getting the public key of the trusted zone enclave');
244
+ let imageHash = this._metadata.image_hash;
245
+ console.log('imageHash:', imageHash);
246
+ if (EtnySecureLock.debug) {
247
+ imageHash = 'v3-hash-1';
248
+ }
249
+ this.trustedZonePublicKey = await this.image_registry.getTrustedZoneImageCertPublicKey(imageHash);
250
+ console.log('Public key of the TrustedZone enclave from smart contract:', this.trustedZonePublicKey);
251
+ } catch (e) {
252
+ console.log('getTrustedZonePublicKey', e);
253
+ }
254
+ }
255
+
256
+ async encryptFileAndPushToSwiftStream(fileData, fileName) {
257
+ try {
258
+ console.log(`Started saving file ${fileName}.securelock...`);
259
+ const encryptedInput = etny_crypto.encrypt(this.trustedZonePublicKey, fileData);
260
+ const encryptedInputBase64 = etny_crypto.encryptedDataToBase64Json(encryptedInput);
261
+
262
+ // File name should end with the '.securelock' extension
263
+ fileName += '.securelock';
264
+ // const data = Buffer.from(encryptedInputBase64, 'utf8');
265
+ const status = this.swiftStreamService.putFileContent(this.etny_bucket, fileName, '', encryptedInputBase64);
266
+ if (status) {
267
+ console.log(`File ${fileName} encrypted and saved to swift stream successfully`);
268
+ }
269
+ } catch (e) {
270
+ console.log('encryptFileAndPushToSwiftStream', e);
271
+ }
272
+ }
273
+
274
+ async getDORequestMetadata() {
275
+ const o_result = await this.etny.functions._getOrder(this.order_id);
276
+ const order = new Order(o_result, this.order_id);
277
+ //console.log('order', order);
278
+ this._order_metadata = order;
279
+ const o_metadata = await this.etny.functions._getDORequestMetadata(order.do_req);
280
+ this._metadata = new DOReqMetadata(o_metadata, order.do_req);
281
+ //console.log('metadata', this._metadata);
282
+ }
283
+
284
+ resetPrivateCertFile() {
285
+ //console.log('Resetting the cert file');
286
+ try {
287
+ // const fd = fs.openSync(this.private_key_file, 'w+');
288
+ fs.truncate(this.key_file, 0, function () {
289
+ //console.log('Private certificate file was cleaned.')
290
+ });
291
+ // fs.closeSync(fd);
292
+ } catch (e) {
293
+ //console.log('Error while removing the private key file content', e);
294
+ }
295
+ const content = fs.readFileSync(this.key_file, 'utf8');
296
+ //console.log('cert file content', content, '##################');
297
+ }
298
+
299
+ // Testnet non-CAS mode: derive a deterministic self-signed cert/key from this
300
+ // enclave's MR_ENCLAVE and write them to the paths the rest of the code reads
301
+ // (key_file for decrypt, cert_file for savePublicCert). No-op on mainnet,
302
+ // where CAS injects the cert/key into those same paths. Idempotent: the key
303
+ // is a pure function of MR_ENCLAVE so it is identical across restarts.
304
+ generateSelfSignedCertIfTestnet() {
305
+ if (NETWORK_TYPE !== "testnet") return;
306
+ console.log("[SecureLock] TESTNET: self-signing certificate from MR_ENCLAVE (no CAS) [TESTNET-INSECURE]");
307
+ const {privateKeyPem, certPem, mrEnclaveHex} = generateCertFromMrEnclave("SecureLock Enclave");
308
+ console.log(`[SecureLock] MR_ENCLAVE: ${mrEnclaveHex}`);
309
+ for (const f of [this.key_file, this.cert_file, this.pub_cert_file]) {
310
+ try { fs.mkdirSync(require('path').dirname(f), {recursive: true}); } catch (e) {}
311
+ }
312
+ fs.writeFileSync(this.key_file, privateKeyPem);
313
+ fs.writeFileSync(this.cert_file, certPem);
314
+ }
315
+
316
+ async savePublicCert() {
317
+ try {
318
+ const content = await fs.promises.readFile(this.cert_file, 'utf8');
319
+ await fs.promises.writeFile(this.pub_cert_file, content);
320
+ //console.log(`Public certificate saved to ${this.pub_cert_file}`);
321
+ console.log(`PUBLIC_KEY:`);
322
+ console.log(content);
323
+
324
+ await this.swiftStreamService.putFileContent(this.etny_bucket, 'cert.pem', this.pub_cert_file);
325
+ } catch (err) {
326
+ console.error(err);
327
+ }
328
+ }
329
+
330
+ async saveResult() {
331
+ try {
332
+ await this.getTrustedZonePublicKey();
333
+ await this.encryptFileAndPushToSwiftStream(this.task_result, 'result.txt');
334
+ await this.encryptFileAndPushToSwiftStream(this.task_code.toString(), 'result_code.txt');
335
+
336
+ const encrypted = etny_crypto.encryptWithPublicKey(this._metadata.public_key, this.task_result);
337
+ //console.log('Encrypted result:', encrypted.toString('hex'));
338
+ const result = await this.swiftStreamService.putFileContent(this.etny_bucket, 'result.txt', this.result_file, encrypted.toString('hex'));
339
+ //console.log('Save result put: ', result);
340
+ } catch (e) {
341
+ console.log('Save result: ', e);
342
+ }
343
+ }
344
+
345
+ async execute() {
346
+ const payload_data = await this.getFileContentAndDecrypt(this.etny_bucket, this.payload);
347
+ const input_data = await this.getFileContentAndDecrypt(this.etny_bucket, this.input);
348
+ console.log('Validating client payload and input...');
349
+ await this.validateClientPayload(payload_data, input_data);
350
+
351
+ if (this.is_valid_client_data) {
352
+ console.log('Client payload and input are valid');
353
+ } else {
354
+ console.log('Client payload and input are NOT valid');
355
+ return false;
356
+ }
357
+
358
+ const task_result = executeTask(payload_data, input_data);
359
+ console.log('TASK RESULT (code, data):', task_result);
360
+ this.task_code = task_result[0];
361
+ this.task_result = task_result[1];
362
+
363
+ return true;
364
+ }
365
+
366
+ }
367
+
368
+ function initSwiftStreamClient() {
369
+ console.log('Initializing SwiftStream Service...');
370
+ const swiftStreamClient = new SwiftStreamService(
371
+ EtnySecureLock.debug ? "localhost" : "etny-swift-stream",
372
+ 9000,
373
+ "swiftstreamadmin",
374
+ "swiftstreamadmin");
375
+ console.log('SwiftStream Service started.');
376
+ console.log('################################');
377
+
378
+ return swiftStreamClient;
379
+ }
380
+
381
+ async function execute() {
382
+ console.log('########################################');
383
+ console.log('########################################');
384
+ console.log('########## [ETNY SecureLock] ##########');
385
+ console.log('########################################');
386
+ console.log('########################################');
387
+ console.log('');
388
+ console.log('');
389
+ console.log('');
390
+
391
+ const swiftStreamClient = initSwiftStreamClient();
392
+
393
+ const app = new EtnySecureLock(swiftStreamClient);
394
+
395
+ // Testnet: materialize the self-signed cert/key from MR_ENCLAVE into the
396
+ // file paths before anything reads them (harvest + decrypt). No-op on mainnet
397
+ // (CAS provides those files).
398
+ app.generateSelfSignedCertIfTestnet();
399
+
400
+ const hasReadEnvFile = await app.readEnv();
401
+ if (!hasReadEnvFile && !EtnySecureLock.debug) {
402
+ console.log(`Failed reading .env file.`);
403
+ await app.savePublicCert();
404
+ return;
405
+ }
406
+
407
+ if (process.env.ETNY_MODE === "PUBLIC_KEY") {
408
+ await app.savePublicCert();
409
+ } else {
410
+ console.log('Creating the application...');
411
+ const created = await app.create();
412
+
413
+ if (!created) {
414
+ console.log(`Failed at creation step, not able decoding input metadata, exiting.`);
415
+ return;
416
+ }
417
+
418
+ console.log('Waiting for payload and input to be downloaded...');
419
+ await app.waitForPayloadAndInput();
420
+
421
+ if (app.is_valid_client_data) {
422
+ console.log('Executing client code...');
423
+ const wasExecuted = await app.execute();
424
+ if (!wasExecuted) {
425
+ console.log('Failed execution of SecureLock enclave.')
426
+ return;
427
+ }
428
+ } else {
429
+ console.log(`Failed client payload and input validation, ${app.task_result}, exiting.`);
430
+ return;
431
+ }
432
+
433
+ await app.saveResult();
434
+ console.log('Finished process.');
435
+ }
436
+ }
437
+
438
+ (async () => {
439
+ try {
440
+ await execute();
441
+ } catch (e) {
442
+ console.log(e);
443
+ }
444
+ })();