@ethernity-cloud/sdk-js 1.6.1 → 1.7.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.
- package/README.md +44 -6
- package/cas/config.js +61 -3
- package/info.js +333 -324
- package/init.js +20 -4
- package/nodenithy/build/securelock/Dockerfile.tmpl +34 -32
- package/nodenithy/build/securelock/scripts/binary-fs-build.sh +7 -6
- package/nodenithy/build/securelock/src/securelock.js.tmpl +5 -2
- package/nodenithy/build.mjs +83 -71
- package/nodenithy/run/docker-compose-final.yml.tmpl +36 -31
- package/nodenithy/run/docker-compose.yml.tmpl +34 -45
- package/nodenithy/run/image_registry.js +396 -380
- package/nodenithy/run.js +42 -28
- package/package.json +5 -3
- package/publish.js +4 -2
- package/pynithy/build/securelock/Dockerfile.base.tpl +13 -0
- package/pynithy/build/securelock/Dockerfile.tpl +131 -0
- package/pynithy/build/securelock/scripts/binary-fs-build.sh +25 -34
- package/pynithy/build/securelock/scripts/pyfreeze.sh +32 -0
- package/pynithy/build/securelock/src/{pox.abi → abi/etnyImplementationV2.abi} +1489 -1489
- package/pynithy/build/securelock/src/abi/image_registry.abi +1 -0
- package/pynithy/build/securelock/src/ecld_state.py +906 -0
- package/pynithy/build/securelock/src/esr.abi +1 -0
- package/pynithy/build/securelock/src/esr_local.py +267 -0
- package/pynithy/build/securelock/src/esr_wallet.py +150 -0
- package/pynithy/build/securelock/src/etny_crypto.py +208 -167
- package/pynithy/build/securelock/src/etny_exec.py +385 -79
- package/pynithy/build/securelock/src/etny_exec_flask.py +535 -0
- package/pynithy/build/securelock/src/etny_exec_serv.py +353 -0
- package/pynithy/build/securelock/src/etny_session_exec.py +245 -0
- package/pynithy/build/securelock/src/get_sgx_report.so +0 -0
- package/pynithy/build/securelock/src/image_registry.abi +1 -1
- package/pynithy/build/securelock/src/key_generation.py +17 -17
- package/pynithy/build/securelock/src/local_api.py +189 -0
- package/pynithy/build/securelock/src/models.py +200 -176
- package/pynithy/build/securelock/src/securelock.py.tmpl +1037 -727
- package/pynithy/build/securelock/src/swift_stream_service.py +206 -186
- package/pynithy/build.mjs +142 -92
- package/pynithy/run/docker-compose-final.yml.tmpl +58 -51
- package/pynithy/run/docker-compose.yml.tmpl +62 -48
- package/pynithy/run/etny-securelock-test.yaml.tpl +4 -1
- package/pynithy/run/image_registry.js +396 -380
- package/pynithy/run.js +44 -28
- package/pynithy/src/serverless/Dockerfile.serverless +8 -0
- package/pynithy/src/serverless/requirements.txt +6 -0
- package/run.js +69 -28
- package/nodenithy/build/las/Dockerfile +0 -5
- package/pynithy/build/docker-compose.yml +0 -70
- package/pynithy/build/las/Dockerfile +0 -11
- package/pynithy/build/las/src/etny_entrypoint.sh +0 -6
- package/pynithy/build/las/src/las_entrypoint.sh +0 -26
- package/pynithy/build/scripts/binary-fs-build.sh +0 -28
- package/pynithy/build/securelock/Dockerfile.tmpl +0 -95
package/nodenithy/run.js
CHANGED
|
@@ -60,13 +60,19 @@ const writeEnv = (key, value) => {
|
|
|
60
60
|
|
|
61
61
|
let templateName = process.env.TRUSTED_ZONE_IMAGE || 'etny-nodenithy-testnet';
|
|
62
62
|
|
|
63
|
-
const isMainnet =
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
// the
|
|
68
|
-
|
|
63
|
+
const isMainnet = casConfig.isMainnetNetwork(process.env.BLOCKCHAIN_NETWORK);
|
|
64
|
+
// Mainnet enclaves are provisioned by the Scontain CAS. A testnet template
|
|
65
|
+
// with a SessionRegistry is CAS-attested by the ethernity-cas validator set:
|
|
66
|
+
// the session is registered on-chain, the enclave is provisioned from a
|
|
67
|
+
// validator, and the certificate comes out of the CAS session. Every other
|
|
68
|
+
// testnet's enclaves self-sign from MR_ENCLAVE and register no session.
|
|
69
|
+
const cas = casConfig.casProvisioned(templateName, isMainnet);
|
|
70
|
+
const casTestnet = !isMainnet && cas;
|
|
69
71
|
const casChain = casConfig.CHAIN[templateName] || {};
|
|
72
|
+
// An -unsafe network is a self-signing testnet whose compose carries no LAS;
|
|
73
|
+
// its securelock is registered as <project>-unsafe.
|
|
74
|
+
const unsafe = casConfig.isUnsafeNetwork(process.env.BLOCKCHAIN_NETWORK);
|
|
75
|
+
const securelock = casConfig.nameOnNetwork(process.env.PROJECT_NAME, process.env.BLOCKCHAIN_NETWORK);
|
|
70
76
|
|
|
71
77
|
const currentDir = process.cwd();
|
|
72
78
|
console.log(`currentDir: ${currentDir}`);
|
|
@@ -108,14 +114,19 @@ const extractSignedMrenclave = (service) => {
|
|
|
108
114
|
|
|
109
115
|
const main = async () => {
|
|
110
116
|
process.env.NODE_NO_WARNINGS = 1
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
117
|
+
// Every testnet trustedzone was measured and registered with a 1024M heap
|
|
118
|
+
// (etny-nodenithy v3/run/docker-compose.yml). The mainnet session named
|
|
119
|
+
// below comes from the features/v3 branches, which ran it with 256M.
|
|
120
|
+
const trustedZoneHeap = isMainnet ? '256M' : '1024M';
|
|
121
|
+
['docker-compose.yml', 'docker-compose-final.yml'].forEach(file => {
|
|
122
|
+
const template = `${file}.tmpl`;
|
|
123
|
+
if (!fs.existsSync(template)) {
|
|
124
|
+
console.error(`Error: ${template} not found!`);
|
|
115
125
|
return;
|
|
116
126
|
}
|
|
117
|
-
const
|
|
118
|
-
|
|
127
|
+
const content = casConfig.renderCompose(fs.readFileSync(template, 'utf8'), cas, unsafe)
|
|
128
|
+
.replace(/__TRUSTEDZONE_HEAP__/g, trustedZoneHeap);
|
|
129
|
+
fs.writeFileSync(file, content, 'utf8');
|
|
119
130
|
});
|
|
120
131
|
|
|
121
132
|
|
|
@@ -134,7 +145,7 @@ const main = async () => {
|
|
|
134
145
|
// from the signed binary -- and a runtime recompute yields a DEBUG enclave
|
|
135
146
|
// that CAS rejects ("Debug mode is enabled"). Refuse to publish rather than
|
|
136
147
|
// register an untrusted identity on-chain or fail opaquely later.
|
|
137
|
-
if (
|
|
148
|
+
if (cas) {
|
|
138
149
|
const signedMrenclave = extractSignedMrenclave('etny-securelock');
|
|
139
150
|
if (!signedMrenclave || signedMrenclave !== process.env.MRENCLAVE_SECURELOCK) {
|
|
140
151
|
console.error(`Error: securelock runtime MRENCLAVE (${process.env.MRENCLAVE_SECURELOCK}) != signed MRENCLAVE (${signedMrenclave}).`);
|
|
@@ -221,15 +232,20 @@ const main = async () => {
|
|
|
221
232
|
MRENCLAVE: process.env.MRENCLAVE_SECURELOCK,
|
|
222
233
|
ENCLAVE_NAME: ENCLAVE_NAME_SECURELOCK
|
|
223
234
|
};
|
|
224
|
-
|
|
235
|
+
if (cas) {
|
|
236
|
+
processYamlTemplate('etny-securelock-test.yaml.tpl', 'etny-securelock-test.yaml', replacementsSecurelock);
|
|
237
|
+
}
|
|
225
238
|
|
|
226
239
|
// Where the session goes depends on who provisions the securelock. On a
|
|
227
240
|
// CAS-attested testnet it is registered ON-CHAIN in the ethernity-cas
|
|
228
241
|
// SessionRegistry, which the validator set reads and which accepts no
|
|
229
242
|
// POST; the publish then waits until the validators serve it, because the
|
|
230
|
-
// public-key harvest below provisions the enclave from them.
|
|
231
|
-
//
|
|
232
|
-
|
|
243
|
+
// public-key harvest below provisions the enclave from them. On mainnet it
|
|
244
|
+
// is registered with the Scontain CAS. A self-signing testnet has none: a
|
|
245
|
+
// CAS-issued certificate would not match the key the enclave derives.
|
|
246
|
+
if (!cas) {
|
|
247
|
+
console.log(`\t✔ ${process.env.BLOCKCHAIN_NETWORK}: no CAS session; the securelock self-signs from MR_ENCLAVE`);
|
|
248
|
+
} else if (casTestnet) {
|
|
233
249
|
const registered = await sessionRegistry.register(
|
|
234
250
|
casChain.rpcUrl, casChain.chainId, casConfig.SESSION_REGISTRY[templateName],
|
|
235
251
|
process.env.PRIVATE_KEY, fs.readFileSync('etny-securelock-test.yaml'),
|
|
@@ -432,13 +448,9 @@ const main = async () => {
|
|
|
432
448
|
console.log(`No __ENCLAVE_NAME_TRUSTEDZONE__ found in ${file}`);
|
|
433
449
|
}
|
|
434
450
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
}
|
|
439
|
-
if (casTestnet) {
|
|
440
|
-
ENCLAVE_NAME_TRUSTEDZONE = trustedZoneSession;
|
|
441
|
-
}
|
|
451
|
+
// Named only in a CAS-provisioned compose; a self-signing one has no
|
|
452
|
+
// __CAS_ONLY__ lines left.
|
|
453
|
+
const ENCLAVE_NAME_TRUSTEDZONE = casTestnet ? trustedZoneSession : 'ecld-nodenithy-trustedzone-v3-3.0.0';
|
|
442
454
|
let updatedContent = fileContentBefore
|
|
443
455
|
.replace(/__ENCLAVE_NAME_SECURELOCK__/g, ENCLAVE_NAME_SECURELOCK)
|
|
444
456
|
.replace(/__ENCLAVE_NAME_TRUSTEDZONE__/g, ENCLAVE_NAME_TRUSTEDZONE);
|
|
@@ -547,7 +559,7 @@ const main = async () => {
|
|
|
547
559
|
console.log()
|
|
548
560
|
|
|
549
561
|
console.log(`ENCLAVE_NAME_SECURELOCK: ${ENCLAVE_NAME_SECURELOCK}`);
|
|
550
|
-
execSync(`node ./public_key_service.js --enclave_name "${
|
|
562
|
+
execSync(`node ./public_key_service.js --enclave_name "${securelock}" --protocol_version "v3" --network "${process.env.BLOCKCHAIN_NETWORK}" --template_version "${process.env.VERSION}"`, { stdio: 'inherit' });
|
|
551
563
|
PUBLIC_KEY_SECURELOCK_RES = fs.readFileSync('PUBLIC_KEY.txt', 'utf8').trim();
|
|
552
564
|
console.log(`PUBLIC_KEY_SECURELOCK_RES: ${PUBLIC_KEY_SECURELOCK_RES}`);
|
|
553
565
|
if (!PUBLIC_KEY_SECURELOCK_RES || PUBLIC_KEY_SECURELOCK_RES === '-1') {
|
|
@@ -636,10 +648,12 @@ const main = async () => {
|
|
|
636
648
|
console.log("IPFS_HASH: ", process.env.IPFS_HASH);
|
|
637
649
|
writeEnv('IPFS_HASH', process.env.IPFS_HASH);
|
|
638
650
|
process.chdir(currentDir);
|
|
639
|
-
console.log(
|
|
651
|
+
console.log(`Adding certificates for SECURELOCK ${securelock} into IMAGE REGISTRY smart contract...`);
|
|
652
|
+
// image_registry.js registers the image under PROJECT_NAME.
|
|
653
|
+
const registryEnv = { ...process.env, PROJECT_NAME: securelock };
|
|
640
654
|
let existing = false;
|
|
641
655
|
try {
|
|
642
|
-
const existingImages = execSync(`node ${runDir}/image_registry.js "${process.env.BLOCKCHAIN_NETWORK}" "${
|
|
656
|
+
const existingImages = execSync(`node ${runDir}/image_registry.js "${process.env.BLOCKCHAIN_NETWORK}" "${securelock}" "${process.env.VERSION}" "${process.env.PRIVATE_KEY}" "registerSecureLockImage"`, { stdio: "inherit", env: registryEnv });
|
|
643
657
|
if (existingImages.toString().trim().replace('Image hash: ', '') === process.env.IPFS_HASH) {
|
|
644
658
|
console.log("Certificates for SECURELOCK already added to IMAGE REGISTRY smart contract");
|
|
645
659
|
}
|
|
@@ -651,7 +665,7 @@ const main = async () => {
|
|
|
651
665
|
}
|
|
652
666
|
|
|
653
667
|
if (!existing) {
|
|
654
|
-
const res = execSync(`node ${runDir}/image_registry.js "${process.env.BLOCKCHAIN_NETWORK}" "" "" "" "registerSecureLockImage"`, { stdio: "inherit" });
|
|
668
|
+
const res = execSync(`node ${runDir}/image_registry.js "${process.env.BLOCKCHAIN_NETWORK}" "" "" "" "registerSecureLockImage"`, { stdio: "inherit", env: registryEnv });
|
|
655
669
|
}
|
|
656
670
|
// The on-chain session points at the image it admits, so a validator can
|
|
657
671
|
// pin the image beside the body it serves.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ethernity-cloud/sdk-js",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "A package to run ethernity cloud build scripts",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"ecld-build": "cross-env node ./node_modules/@ethernity-cloud/sdk-js/build.js",
|
|
@@ -30,10 +30,12 @@
|
|
|
30
30
|
"postinstall.js",
|
|
31
31
|
"test.js",
|
|
32
32
|
"info.js",
|
|
33
|
-
"run.js"
|
|
33
|
+
"run.js",
|
|
34
|
+
"!**/__pycache__/**",
|
|
35
|
+
"!**/*.pyc"
|
|
34
36
|
],
|
|
35
37
|
"dependencies": {
|
|
36
|
-
"@ethernity-cloud/runner": "^0.
|
|
38
|
+
"@ethernity-cloud/runner": "^0.6.0",
|
|
37
39
|
"acorn": "^8.11.0",
|
|
38
40
|
"adm-zip": "^0.5.14",
|
|
39
41
|
"axios": "^1.7.2",
|
package/publish.js
CHANGED
|
@@ -3,6 +3,7 @@ const readline = require('readline');
|
|
|
3
3
|
const { execSync, spawn } = require('child_process');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
require('dotenv').config();
|
|
6
|
+
const casConfig = require('./cas/config.js');
|
|
6
7
|
|
|
7
8
|
// Function to write to .env file
|
|
8
9
|
const writeEnv = (key, value) => {
|
|
@@ -73,8 +74,9 @@ async function prompt(question) {
|
|
|
73
74
|
|
|
74
75
|
console.log(`Available funds: ${result}`);
|
|
75
76
|
console.log()
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
const imageName = casConfig.nameOnNetwork(PROJECT_NAME, BLOCKCHAIN_NETWORK);
|
|
78
|
+
console.log(`Checking if project name ${imageName} is available on ${BLOCKCHAIN_NETWORK} network and ownership...`);
|
|
79
|
+
result = execSync(`node ${scriptPath} ${BLOCKCHAIN_NETWORK} ${imageName} ${process.env.VERSION} ${process.env.PRIVATE_KEY}`,).toString().trim();
|
|
78
80
|
console.log(result);
|
|
79
81
|
console.log()
|
|
80
82
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
FROM __DOCKER_REPO_URL__:__BASE_IMAGE_TAG__ AS release
|
|
2
|
+
|
|
3
|
+
# The python-3.14.6-alpine3.24-scone6.0.7 base is a LEAN static CPython 3.14
|
|
4
|
+
# (built off-SCONE with scone-gcc) plus pip/setuptools/wheel only - no
|
|
5
|
+
# application dependencies are baked in. Users add their own Python modules via
|
|
6
|
+
# src/serverless/requirements.txt, which are pip-installed on a normal python
|
|
7
|
+
# during the PyInstaller freeze stage (see Dockerfile.tpl); nothing is compiled
|
|
8
|
+
# under the SCONE-shielded interpreter.
|
|
9
|
+
#
|
|
10
|
+
# Only the system tools the build/runtime scripts call are added here (openssl
|
|
11
|
+
# CLI is needed for the enclave key generation in the final securelock stage).
|
|
12
|
+
RUN apk update
|
|
13
|
+
RUN apk add bash openrc bind-tools sudo binutils curl openssl
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1
|
|
2
|
+
#
|
|
3
|
+
# Securelock enclave build. The whole build is SGX-free: heavy Python work
|
|
4
|
+
# (pip installs + PyInstaller freeze) runs on a STOCK python:3.14-alpine with
|
|
5
|
+
# no SCONE and no SGX; only the binary-fs packaging and signing use the SCONE
|
|
6
|
+
# build tools, which run in simulation/build mode without an SGX device.
|
|
7
|
+
|
|
8
|
+
########################################################################
|
|
9
|
+
# Stage 1 - SGX report helper (unchanged, crosscompiler build tool)
|
|
10
|
+
########################################################################
|
|
11
|
+
FROM registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/sconecuratedimages/crosscompilers:alpine-scone6.0.7 AS build-sgx-module
|
|
12
|
+
# The SDK ships the SGX key-gen module ONLY as a PREBUILT .so
|
|
13
|
+
# (get_sgx_report.so) -- never the .c source. The source of truth for
|
|
14
|
+
# get_sgx_report.c lives in the etny-pynithy / etny-nodenithy repos; the .so is
|
|
15
|
+
# produced once from it via scripts/build_keygen_so.sh (scone-gcc, hardened +
|
|
16
|
+
# stripped) and committed here. No source is compiled during the developer's
|
|
17
|
+
# build, so a developer inspecting the wheel or build tree never sees the
|
|
18
|
+
# derivation algorithm.
|
|
19
|
+
#
|
|
20
|
+
# NOTE (see esr_wallet.py): the .so still contains the executable logic and the
|
|
21
|
+
# enclave image is public, so this hides the SOURCE, not the algorithm; on
|
|
22
|
+
# testnet the key remains reproducible because there is no attestation.
|
|
23
|
+
COPY src/get_sgx_report.so /etny-securelock/get_sgx_report.so
|
|
24
|
+
|
|
25
|
+
########################################################################
|
|
26
|
+
# Stage 2 - PyInstaller freeze on a NORMAL python 3.14 (no SCONE / no SGX)
|
|
27
|
+
# Installs the user's requirements.txt + PyInstaller, renders securelock.py,
|
|
28
|
+
# and runs pyinstaller to produce the frozen COLLECT dir. Same python
|
|
29
|
+
# version + musl as the SCONE runtime python, so the output is compatible.
|
|
30
|
+
########################################################################
|
|
31
|
+
FROM python:3.14-alpine AS pyfreeze
|
|
32
|
+
RUN apk add --no-cache bash gcc g++ musl-dev libffi-dev openssl-dev rust cargo make libsodium-dev zlib-dev binutils
|
|
33
|
+
ENV SODIUM_INSTALL=system
|
|
34
|
+
WORKDIR /etny-securelock
|
|
35
|
+
|
|
36
|
+
# Core securelock runtime deps + PyInstaller (built here, off-SCONE, where pip
|
|
37
|
+
# and native builds work normally).
|
|
38
|
+
RUN pip install --no-cache-dir \
|
|
39
|
+
pyinstaller \
|
|
40
|
+
python-dotenv "web3>=6" cryptography ecdsa pyasn1 tinyec minio pynacl
|
|
41
|
+
|
|
42
|
+
# User-supplied additional modules (the customization hook).
|
|
43
|
+
COPY ./src/serverless/requirements.txt /requirements.txt
|
|
44
|
+
RUN pip install --no-cache-dir -r /requirements.txt || true
|
|
45
|
+
|
|
46
|
+
COPY ./src /etny-securelock/
|
|
47
|
+
COPY ./scripts/* /etny-securelock/
|
|
48
|
+
COPY --from=build-sgx-module /etny-securelock/get_sgx_report.so /etny-securelock/get_sgx_report.so
|
|
49
|
+
|
|
50
|
+
# Render securelock.py from the template, then run PyInstaller (off-SCONE).
|
|
51
|
+
ENV SECURELOCK_SESSION=__SECURELOCK_SESSION__
|
|
52
|
+
ENV BUCKET_NAME=__BUCKET_NAME__
|
|
53
|
+
ENV SMART_CONTRACT_ADDRESS=__SMART_CONTRACT_ADDRESS__
|
|
54
|
+
ENV IMAGE_REGISTRY_ADDRESS=__IMAGE_REGISTRY_ADDRESS__
|
|
55
|
+
ENV RPC_URL=__RPC_URL__
|
|
56
|
+
ENV CHAIN_ID=__CHAIN_ID__
|
|
57
|
+
ENV TRUSTED_ZONE_IMAGE=__TRUSTED_ZONE_IMAGE__
|
|
58
|
+
ENV NETWORK_TYPE=__NETWORK_TYPE__
|
|
59
|
+
ENV VALIDATOR_REGISTRY_ADDRESS=__VALIDATOR_REGISTRY_ADDRESS__
|
|
60
|
+
RUN chmod +x /etny-securelock/pyfreeze.sh && /etny-securelock/pyfreeze.sh
|
|
61
|
+
|
|
62
|
+
########################################################################
|
|
63
|
+
# Stage 3 - binary-fs packaging with the SCONE build tools (no SGX).
|
|
64
|
+
# Assembles the SCONE python + stdlib + the (off-SCONE) installed site-packages
|
|
65
|
+
# + the rendered securelock.py + PyInstaller's collected .so list, and bakes
|
|
66
|
+
# them into the measured binary-fs.
|
|
67
|
+
########################################################################
|
|
68
|
+
FROM registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/sconecuratedimages/crosscompilers:alpine-scone6.0.7 AS binfs
|
|
69
|
+
RUN mkdir -p /binary-fs-dir /etny-securelock
|
|
70
|
+
# SCONE-built static python + its stdlib, from the runtime base image
|
|
71
|
+
COPY --from=etny-securelock-serverless /usr/local/bin/python3 /usr/local/bin/python3
|
|
72
|
+
COPY --from=etny-securelock-serverless /usr/local/lib/python3.14 /usr/local/lib/python3.14
|
|
73
|
+
# The ENTIRE /etny-securelock tree (rendered securelock.py + its sibling modules
|
|
74
|
+
# etny_crypto.py, etny_exec.py, models.py, swift_stream_service.py, the abi/
|
|
75
|
+
# dir, get_sgx_report.so, scripts) - all must be baked into binary-fs or the
|
|
76
|
+
# enclave fails at import (ModuleNotFoundError: No module named 'etny_crypto').
|
|
77
|
+
# site-packages + COLLECT toc also come from the OFF-SCONE pyfreeze stage.
|
|
78
|
+
COPY --from=pyfreeze /etny-securelock /etny-securelock
|
|
79
|
+
COPY --from=pyfreeze /etny-securelock/build/securelock/COLLECT-00.toc /etny-securelock/COLLECT-00.toc
|
|
80
|
+
COPY --from=pyfreeze /usr/local/lib/python3.14/site-packages /usr/local/lib/python3.14/site-packages
|
|
81
|
+
RUN chmod +x /etny-securelock/binary-fs-build.sh && /etny-securelock/binary-fs-build.sh
|
|
82
|
+
RUN cd /binary-fs-dir && scone gcc ./binary_fs_blob.s ./libbinary_fs_template.a -shared -o /libbinary-fs.so
|
|
83
|
+
|
|
84
|
+
########################################################################
|
|
85
|
+
# Stage 4 - final SCONE runtime image (lean scone python base) + sign.
|
|
86
|
+
########################################################################
|
|
87
|
+
FROM etny-securelock-serverless
|
|
88
|
+
|
|
89
|
+
COPY --from=binfs /usr/local/bin/scone /usr/local/bin/scone
|
|
90
|
+
COPY --from=binfs /libbinary-fs.so /lib/libbinary-fs.so
|
|
91
|
+
|
|
92
|
+
RUN openssl genrsa -3 -out /enclave-key.pem 3072
|
|
93
|
+
|
|
94
|
+
# SCONE enclaves run with no HOME; libraries calling os.path.expanduser('~')
|
|
95
|
+
# abort with "$HOME is not defined" and kill the enclave before it emits its
|
|
96
|
+
# public key. Set a writable HOME so startup (and cert emission) succeeds.
|
|
97
|
+
ENV HOME=/tmp
|
|
98
|
+
ENV SCONE_HEAP=__MEMORY_TO_ALLOCATE__
|
|
99
|
+
ENV SCONE_LOG=FATAL
|
|
100
|
+
ENV SCONE_DEBUG=0
|
|
101
|
+
ENV SCONE_STACK=4M
|
|
102
|
+
__SCONE_ALLOW_DLOPEN__
|
|
103
|
+
ENV SCONE_EXTENSIONS_PATH=/lib/libbinary-fs.so
|
|
104
|
+
# securelock.py does relative imports of its sibling modules (etny_crypto,
|
|
105
|
+
# models, ...) and reads abi/ by relative path; run from its own directory so
|
|
106
|
+
# those resolve (the base image's build WORKDIR /b/Python-3.14.6 otherwise leaks
|
|
107
|
+
# in as the enclave PWD).
|
|
108
|
+
ENV SCONE_PWD=/etny-securelock
|
|
109
|
+
__ESR_ENV__
|
|
110
|
+
WORKDIR /etny-securelock
|
|
111
|
+
|
|
112
|
+
# Rendered by build.py: signs the EXECUTED binary (/usr/local/bin/python) with
|
|
113
|
+
# explicit enclave-creation params; mainnet adds --production, testnet omits it
|
|
114
|
+
# (non-CAS self-sign mode).
|
|
115
|
+
__SCONE_SIGN__
|
|
116
|
+
|
|
117
|
+
# Record the SIGNED MRENCLAVE so publish.py can assert the runtime enclave
|
|
118
|
+
# measurement matches what was signed BEFORE registering the CAS session /
|
|
119
|
+
# publishing (mismatch => SCONE recomputed at load => debug => CAS rejects).
|
|
120
|
+
# scone-signer info prints two tab-indented "MRENCLAVE:" lines (non-EDMM first;
|
|
121
|
+
# runtime has EDMM disabled, so the first is what SCONE_HASH produces). Capture
|
|
122
|
+
# to a file first: piping into `head` kills scone-signer with SIGPIPE (exit 141)
|
|
123
|
+
# under buildkit's `ash -eo pipefail`.
|
|
124
|
+
RUN scone-signer info /usr/local/bin/python > /tmp/siginfo.txt 2>&1; \
|
|
125
|
+
grep -iE "MRENCLAVE:" /tmp/siginfo.txt | sed -n '1p' \
|
|
126
|
+
| sed -E 's/.*MRENCLAVE:[[:space:]]*//I' | tr -d '[:space:]' > /signed_mrenclave.txt && \
|
|
127
|
+
echo "SIGNED_MRENCLAVE=$(cat /signed_mrenclave.txt)"
|
|
128
|
+
|
|
129
|
+
RUN rm -rf /enclave-key.pem
|
|
130
|
+
|
|
131
|
+
ENTRYPOINT ["/usr/local/bin/python", "/etny-securelock/securelock.py"]
|
|
@@ -1,42 +1,33 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Runs in the SCONE crosscompiler stage (SCONE build tools, NO SGX needed).
|
|
3
|
+
# The PyInstaller freeze already happened OFF-SCONE in the pyfreeze stage; this
|
|
4
|
+
# stage has received (copied in):
|
|
5
|
+
# /usr/local/bin/python3 - the static SCONE-built python 3.14
|
|
6
|
+
# /usr/local/lib/python3.14/* - its stdlib + the installed site-packages
|
|
7
|
+
# /etny-securelock/securelock.py - the rendered app (run at enclave runtime)
|
|
8
|
+
# /etny-securelock/COLLECT-00.toc - PyInstaller's collected .so list
|
|
9
|
+
# /etny-securelock/get_sgx_report.so
|
|
10
|
+
#
|
|
11
|
+
# We bake the stdlib + site-packages + app + every collected .so into the
|
|
12
|
+
# measured binary-fs; the SCONE python then loads them via SCONE_EXTENSIONS_PATH
|
|
13
|
+
# at runtime (no dlopen from the untrusted host).
|
|
14
|
+
set -e
|
|
7
15
|
cd /etny-securelock
|
|
8
16
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
sed -i "s/__BUCKET_NAME__/${BUCKET_NAME}/g" securelock.py.tmp
|
|
13
|
-
sed -i "s/__SMART_CONTRACT_ADDRESS__/${SMART_CONTRACT_ADDRESS}/g" securelock.py.tmp
|
|
14
|
-
sed -i "s/__IMAGE_REGISTRY_ADDRESS__/${IMAGE_REGISTRY_ADDRESS}/g" securelock.py.tmp
|
|
15
|
-
sed -i "s/__RPC_URL__/${RPC_URL}/g" securelock.py.tmp
|
|
16
|
-
sed -i "s/__CHAIN_ID__/${CHAIN_ID}/g" securelock.py.tmp
|
|
17
|
-
sed -i "s/__TRUSTED_ZONE_IMAGE__/${TRUSTED_ZONE_IMAGE}/g" securelock.py.tmp
|
|
18
|
-
sed -i "s/__VALIDATOR_REGISTRY_ADDRESS__/${VALIDATOR_REGISTRY_ADDRESS}/g" securelock.py.tmp
|
|
19
|
-
mv securelock.py.tmp securelock.py
|
|
20
|
-
|
|
21
|
-
pyinstaller securelock.py
|
|
22
|
-
|
|
23
|
-
EXEC=(scone binaryfs / /binary-fs-dir -v \
|
|
24
|
-
--include '/usr/local/lib/python3.10/*' \
|
|
17
|
+
EXEC=(scone binary-fs / /binary-fs-dir -v \
|
|
18
|
+
--include '/usr/local/bin/python3' \
|
|
19
|
+
--include '/usr/local/lib/python3.14/*' \
|
|
25
20
|
--include '/etny-securelock/*' \
|
|
26
21
|
--host-path=/etc/resolv.conf \
|
|
27
22
|
--host-path=/etc/hosts)
|
|
28
23
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
do
|
|
32
|
-
|
|
33
|
-
done
|
|
34
|
-
|
|
35
|
-
rm -rf build dist securelock.spec
|
|
24
|
+
# Add every shared object PyInstaller collected (crypto/web3/etc. native libs).
|
|
25
|
+
if [ -f ./COLLECT-00.toc ]; then
|
|
26
|
+
for FILE in $(grep '.so' ./COLLECT-00.toc | grep BINARY | awk -F "'" '{print $4}'); do
|
|
27
|
+
EXEC+=(--include "${FILE}"'*')
|
|
28
|
+
done
|
|
29
|
+
fi
|
|
36
30
|
|
|
37
31
|
echo "${EXEC[@]}"
|
|
38
|
-
|
|
39
|
-
SCONE_MODE=auto
|
|
40
|
-
exec "${EXEC[@]}"
|
|
41
|
-
|
|
42
|
-
exit
|
|
32
|
+
mkdir -p /binary-fs-dir
|
|
33
|
+
SCONE_MODE=auto exec "${EXEC[@]}"
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Runs on a STOCK python:3.14-alpine (NO SCONE, NO SGX). Renders securelock.py
|
|
3
|
+
# from its template and runs PyInstaller purely as a dependency-analysis pass:
|
|
4
|
+
# PyInstaller resolves every module/shared-object securelock needs and records
|
|
5
|
+
# them in build/securelock/COLLECT-00.toc. The frozen executable itself is not
|
|
6
|
+
# used at runtime (the enclave runs the plain securelock.py with the SCONE
|
|
7
|
+
# python); we only need the collected .so list + the installed site-packages,
|
|
8
|
+
# which the later SCONE stage bakes into the measured binary-fs.
|
|
9
|
+
set -e
|
|
10
|
+
cd /etny-securelock
|
|
11
|
+
|
|
12
|
+
echo "SECURELOCK_SESSION = ${SECURELOCK_SESSION}"
|
|
13
|
+
|
|
14
|
+
# Use '|' as the sed delimiter so values containing '/' (e.g. the RPC URL
|
|
15
|
+
# https://...) don't break the substitution ("sed: bad option in substitution").
|
|
16
|
+
cp securelock.py.tmpl securelock.py.tmp
|
|
17
|
+
sed -i "s|__SECURELOCK_SESSION__|${SECURELOCK_SESSION}|g" securelock.py.tmp
|
|
18
|
+
sed -i "s|__BUCKET_NAME__|${BUCKET_NAME}|g" securelock.py.tmp
|
|
19
|
+
sed -i "s|__SMART_CONTRACT_ADDRESS__|${SMART_CONTRACT_ADDRESS}|g" securelock.py.tmp
|
|
20
|
+
sed -i "s|__IMAGE_REGISTRY_ADDRESS__|${IMAGE_REGISTRY_ADDRESS}|g" securelock.py.tmp
|
|
21
|
+
sed -i "s|__RPC_URL__|${RPC_URL}|g" securelock.py.tmp
|
|
22
|
+
sed -i "s|__CHAIN_ID__|${CHAIN_ID}|g" securelock.py.tmp
|
|
23
|
+
sed -i "s|__TRUSTED_ZONE_IMAGE__|${TRUSTED_ZONE_IMAGE}|g" securelock.py.tmp
|
|
24
|
+
sed -i "s|__NETWORK_TYPE__|${NETWORK_TYPE}|g" securelock.py.tmp
|
|
25
|
+
sed -i "s|__VALIDATOR_REGISTRY_ADDRESS__|${VALIDATOR_REGISTRY_ADDRESS}|g" securelock.py.tmp
|
|
26
|
+
mv securelock.py.tmp securelock.py
|
|
27
|
+
|
|
28
|
+
# Dependency-analysis freeze (off-SCONE). --collect-all pulls in data/binaries
|
|
29
|
+
# for the crypto/web3 stack so the COLLECT toc is complete.
|
|
30
|
+
pyinstaller --onedir --noconfirm securelock.py
|
|
31
|
+
|
|
32
|
+
echo "pyfreeze complete: COLLECT-00.toc + site-packages ready for binary-fs"
|