enigma-memory 0.1.18 → 0.1.22
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 +76 -24
- package/apps/cli/bin/enigma-desktop.mjs +140 -0
- package/apps/cli/bin/enigma-terminal.mjs +78 -0
- package/apps/cli/bin/enigma.mjs +1923 -285
- package/apps/desktop/electron-main.cjs +217 -0
- package/apps/desktop/package.json +12 -0
- package/apps/desktop/src/app.js +264 -7
- package/apps/desktop/src/index.html +3514 -1373
- package/apps/desktop/src/launch-electron.mjs +51 -0
- package/apps/desktop/src/server.mjs +2914 -0
- package/apps/desktop/src/styles.css +2972 -260
- package/apps/desktop/src/zk-browser-prove.mjs +53 -0
- package/apps/desktop/src/zk-state.mjs +1789 -0
- package/apps/gateway/bin/enigma-gateway.mjs +102 -5
- package/apps/gateway/src/server.mjs +271 -8
- package/apps/ios/EnigmaCore/Package.swift +12 -0
- package/apps/ios/EnigmaCore/Sources/EnigmaCore/EnigmaAPIClient.swift +227 -0
- package/apps/ios/EnigmaCore/Sources/EnigmaCore/Models.swift +278 -0
- package/apps/ios/EnigmaCore/Sources/EnigmaCore/PKCE.swift +96 -0
- package/apps/ios/EnigmaCore/Sources/EnigmaCore/PrivacyMinimizer.swift +187 -0
- package/apps/ios/EnigmaCore/Sources/EnigmaCore/ToolModels.swift +129 -0
- package/apps/ios/EnigmaCore/Tests/EnigmaCoreTests/EnigmaCoreTests.swift +42 -0
- package/apps/ios/EnigmaIOS/Enigma/AppModel.swift +346 -0
- package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AccentColor.colorset/Contents.json +12 -0
- package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AppIcon.appiconset/Contents.json +11 -0
- package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AppIcon.appiconset/EnigmaAppIcon.png +0 -0
- package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/Contents.json +3 -0
- package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/LaunchBackground.colorset/Contents.json +12 -0
- package/apps/ios/EnigmaIOS/Enigma/ChatView.swift +181 -0
- package/apps/ios/EnigmaIOS/Enigma/CouncilView.swift +78 -0
- package/apps/ios/EnigmaIOS/Enigma/CreateView.swift +152 -0
- package/apps/ios/EnigmaIOS/Enigma/EnigmaApp.swift +52 -0
- package/apps/ios/EnigmaIOS/Enigma/Info.plist +52 -0
- package/apps/ios/EnigmaIOS/Enigma/NaturalLanguagePrivacyTagger.swift +26 -0
- package/apps/ios/EnigmaIOS/Enigma/OAuthClient.swift +321 -0
- package/apps/ios/EnigmaIOS/Enigma/OnboardingView.swift +105 -0
- package/apps/ios/EnigmaIOS/Enigma/PrivateVaultView.swift +275 -0
- package/apps/ios/EnigmaIOS/Enigma/SecureStore.swift +76 -0
- package/apps/ios/EnigmaIOS/Enigma/SettingsView.swift +60 -0
- package/apps/ios/EnigmaIOS/Enigma/Theme.swift +80 -0
- package/apps/ios/EnigmaIOS/EnigmaIOS.xcodeproj/project.pbxproj +211 -0
- package/apps/ios/EnigmaIOS/EnigmaIOS.xcodeproj/xcshareddata/xcschemes/Enigma.xcscheme +23 -0
- package/apps/native-host/README.md +19 -8
- package/apps/native-host/bin/enigma-native-host.mjs +229 -13
- package/apps/relay/bin/enigma-relay.mjs +103 -5
- package/apps/relay/src/federation-runtime.mjs +618 -0
- package/apps/relay/src/server.mjs +310 -9
- package/apps/verifier/bin/enigma-verify.mjs +327 -11
- package/cortex-v3/circuits/build/intent_vk_bytes.json +35 -0
- package/cortex-v3/circuits/build/sale_vk_bytes.json +35 -0
- package/cortex-v3/circuits/build/vk_bytes.json +32 -0
- package/cortex-v3/proving-assets.json +64 -0
- package/cortex-v3/zk/BUILD-CONTRACT.md +87 -0
- package/cortex-v3/zk/action-transition-vk.json +119 -0
- package/cortex-v3/zk/alias-adversarial.test.mjs +220 -0
- package/cortex-v3/zk/groth16-verify-child.mjs +17 -0
- package/cortex-v3/zk/intent-witness.mjs +365 -0
- package/cortex-v3/zk/intent-witness.test.mjs +485 -0
- package/cortex-v3/zk/proving-assets.mjs +203 -0
- package/cortex-v3/zk/sale-witness.mjs +783 -0
- package/cortex-v3/zk/sale-witness.test.mjs +784 -0
- package/cortex-v3/zk/sealed-sale-release-vk.json +119 -0
- package/cortex-v3/zk/settlement-evidence.mjs +722 -0
- package/cortex-v3/zk/setup-intent.mjs +688 -0
- package/cortex-v3/zk/setup-sale.mjs +666 -0
- package/cortex-v3/zk/setup.mjs +594 -0
- package/cortex-v3/zk/witness.mjs +184 -0
- package/cortex-v3/zk/zk-codec.mjs +232 -0
- package/cortex-v3/zk/zk-codec.test.mjs +293 -0
- package/cortex-v3/zk/zk-settle.mjs +370 -0
- package/cortex-v3/zk/zk-tree.mjs +256 -0
- package/cortex-v3/zk/zk-tree.test.mjs +419 -0
- package/deploy/docker-compose.local-production-simulation.yml +36 -0
- package/docs/browser-extension-install.md +8 -6
- package/docs/client-connectors.md +15 -11
- package/docs/developer-ecosystem.md +15 -13
- package/docs/enigma-memory-ready-conformance.md +11 -9
- package/docs/install-anywhere.md +61 -28
- package/docs/installers-and-desktop.md +8 -7
- package/docs/novelty-invention-candidates.md +161 -161
- package/docs/proof-network-claim-boundaries.md +320 -318
- package/examples/01-quickstart-agent/index.mjs +49 -0
- package/examples/01_agent_memory_quickstart.mjs +57 -0
- package/examples/02-multi-agent-swarm/index.mjs +57 -0
- package/examples/02_cross_model_passport.mjs +64 -0
- package/examples/03-langchain-memory/index.mjs +41 -0
- package/examples/03_poseidon_commitment_verification.mjs +71 -0
- package/examples/04-python-trading-agent/trader.py +49 -0
- package/examples/README.md +27 -0
- package/examples/ci/github-actions.yml +7 -2
- package/package.json +142 -11
- package/packages/adapters/PACKAGE_CONTRACT.md +1 -1
- package/packages/connectors/src/index.js +196 -4
- package/packages/connectors/swarm-router.mjs +168 -0
- package/packages/core/src/index.js +248 -1
- package/packages/core/src/version.mjs +7 -0
- package/packages/dev-tools/package.json +19 -0
- package/packages/dev-tools/src/index.js +4 -0
- package/packages/dev-tools/src/memory-benchmark-suite.js +112 -0
- package/packages/dev-tools/src/swarm-simulator.js +101 -0
- package/packages/dev-tools/src/vault-inspector.js +114 -0
- package/packages/dev-tools/src/vector-benchmark.js +100 -0
- package/packages/developer-platform/src/access-credentials.js +341 -0
- package/packages/developer-platform/src/http.js +132 -0
- package/packages/developer-platform/src/index.js +4 -0
- package/packages/developer-platform/src/usage-http.js +60 -0
- package/packages/developer-platform/src/usage.js +295 -0
- package/packages/enclave-runtime/attestation.mjs +159 -0
- package/packages/enclave-runtime/index.mjs +47 -0
- package/packages/enclave-runtime/session-manager.mjs +253 -0
- package/packages/enclave-runtime/zeroization-proof.mjs +227 -0
- package/packages/enigma-reflex/package.json +14 -0
- package/packages/enigma-reflex/src/index.js +204 -0
- package/packages/enigma-reflex/training/generate-dataset.mjs +40 -0
- package/packages/enigma-reflex/training/requirements.txt +8 -0
- package/packages/enigma-reflex/training/train.py +314 -0
- package/packages/enigma-weave/LICENSE +22 -0
- package/packages/enigma-weave/UPSTREAM.json +21 -0
- package/packages/enigma-weave/package.json +14 -0
- package/packages/enigma-weave/src/index.js +286 -0
- package/packages/hosted-cloud/src/index.js +80 -5
- package/packages/importers/src/index.js +432 -0
- package/packages/inference-runtime/src/browser.js +401 -0
- package/packages/inference-runtime/src/chat.js +265 -0
- package/packages/inference-runtime/src/code.js +407 -0
- package/packages/inference-runtime/src/contracts.js +162 -0
- package/packages/inference-runtime/src/http.js +232 -0
- package/packages/inference-runtime/src/image.js +186 -0
- package/packages/inference-runtime/src/index.js +10 -0
- package/packages/inference-runtime/src/model-router.js +320 -0
- package/packages/inference-runtime/src/platform.js +125 -0
- package/packages/inference-runtime/src/privacy.js +400 -0
- package/packages/inference-runtime/src/video.js +253 -0
- package/packages/mcp-server/README.md +22 -6
- package/packages/mcp-server/bin/enigma-mcp.mjs +2 -1
- package/packages/mcp-server/src/index.js +1418 -105
- package/packages/mcp-server/src/oauth.js +561 -0
- package/packages/mcp-server/src/private-handoff.js +84 -0
- package/packages/mcp-server/src/remote-http.js +273 -0
- package/packages/mcp-server/src/remote-policy.js +72 -0
- package/packages/mcp-server/swarm-bridge.mjs +361 -0
- package/packages/mesh/index.d.ts +283 -0
- package/packages/mesh/package.json +23 -0
- package/packages/mesh/src/crypto.js +189 -0
- package/packages/mesh/src/federation-packets.js +353 -0
- package/packages/mesh/src/gossip.js +311 -0
- package/packages/mesh/src/index.js +6 -0
- package/packages/mesh/src/protocol.js +255 -0
- package/packages/mesh/src/router.js +279 -0
- package/packages/mesh/src/transport.js +306 -0
- package/packages/passport/src/index.js +426 -1
- package/packages/private-economy/src/credits-http.js +100 -0
- package/packages/private-economy/src/credits.js +447 -0
- package/packages/private-economy/src/index.js +5 -0
- package/packages/private-economy/src/payments-http.js +120 -0
- package/packages/private-economy/src/payments.js +509 -0
- package/packages/private-economy/src/x402.js +346 -0
- package/packages/proof-network/PACKAGE_CONTRACT.md +21 -0
- package/packages/rag/index.d.ts +182 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/THIRD_PARTY_LICENSES.txt +207 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/config.json +25 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/onnx/model_quantized.onnx +0 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/sha256-manifest.json +28 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/tokenizer.json +30686 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/tokenizer_config.json +15 -0
- package/packages/rag/package.json +27 -0
- package/packages/rag/src/blinded-search.js +109 -0
- package/packages/rag/src/bm25.js +169 -0
- package/packages/rag/src/embeddings.js +459 -0
- package/packages/rag/src/hybrid.js +76 -0
- package/packages/rag/src/index.js +38 -0
- package/packages/rag/src/reranker.js +61 -0
- package/packages/rag/src/research.js +107 -0
- package/packages/rag/src/vector-store.js +430 -0
- package/packages/rag/src/verify-model-artifacts.mjs +4 -0
- package/packages/sdk/index.d.ts +760 -0
- package/packages/sdk/package.json +33 -0
- package/packages/sdk/python/README.md +24 -0
- package/packages/sdk/python/enigma_sdk.py +250 -0
- package/packages/sdk/python/pyproject.toml +34 -0
- package/packages/sdk/python/requirements.txt +1 -0
- package/packages/sdk/python/setup.py +20 -0
- package/packages/sdk/src/federation/capability-grant.js +389 -0
- package/packages/sdk/src/federation/federation-router.js +360 -0
- package/packages/sdk/src/federation/ghostmesh-bridge.js +497 -0
- package/packages/sdk/src/federation/index.js +3 -0
- package/packages/sdk/src/index.js +1796 -0
- package/packages/sdk/src/intelligence/contradiction.js +337 -0
- package/packages/sdk/src/intelligence/decision-engine.js +155 -0
- package/packages/sdk/src/intelligence/index.js +4 -0
- package/packages/sdk/src/intelligence/ontology.js +122 -0
- package/packages/sdk/src/intelligence/temporal.js +123 -0
- package/packages/sdk/src/market-client.js +142 -0
- package/packages/sdk/src/mesh-client.js +110 -0
- package/packages/sdk/src/middleware/index.js +3 -0
- package/packages/sdk/src/middleware/langchain.js +159 -0
- package/packages/sdk/src/middleware/llamaindex.js +101 -0
- package/packages/sdk/src/middleware/vercel-ai.js +112 -0
- package/packages/sdk/src/rag-client.js +85 -0
- package/packages/sdk/src/swarm-orchestrator.js +260 -0
- package/packages/settlement/PACKAGE_CONTRACT.md +1 -1
- package/packages/snapcompact/THIRD_PARTY_LICENSES.txt +40 -0
- package/packages/snapcompact/assets/8x13-latin1.bdf +3837 -0
- package/packages/snapcompact/index.d.ts +284 -0
- package/packages/snapcompact/package.json +25 -0
- package/packages/snapcompact/src/index.js +716 -0
- package/packages/storage/PACKAGE_CONTRACT.md +1 -1
- package/packages/terminal-console/animations.mjs +240 -0
- package/packages/terminal-console/auto-anchor.mjs +220 -0
- package/packages/terminal-console/banner.mjs +91 -0
- package/packages/terminal-console/commands.mjs +459 -0
- package/packages/terminal-console/delegation.mjs +152 -0
- package/packages/terminal-console/index.mjs +5 -0
- package/packages/terminal-console/outbox.mjs +143 -0
- package/packages/terminal-console/phantom-bridge.mjs +637 -0
- package/packages/terminal-console/repl.mjs +136 -0
- package/packages/terminal-console/signer-store.mjs +130 -0
- package/packages/terminal-console/solana-rpc.mjs +214 -0
- package/packages/terminal-console/solana-transport.mjs +189 -0
- package/packages/terminal-tui/dashboard.mjs +214 -0
- package/packages/terminal-tui/index.mjs +28 -0
- package/packages/terminal-tui/merkle-tree-renderer.mjs +268 -0
- package/packages/terminal-tui/telemetry-hud.mjs +137 -0
- package/packages/vault/index.d.ts +449 -0
- package/packages/vault/package.json +27 -0
- package/packages/vault/src/e2ee.mjs +393 -0
- package/packages/vault/src/enclave.js +481 -0
- package/packages/vault/src/erasure.js +207 -0
- package/packages/vault/src/index.js +1018 -155
- package/packages/vault/src/persistence.js +307 -0
- package/packages/vault/src/poseidon.js +354 -0
- package/packages/vault/src/receipt.js +459 -0
- package/scripts/benchmark-optical-context.mjs +166 -0
- package/scripts/bootstrap-enigma.mjs +502 -0
- package/scripts/build-edge-backend-workers.mjs +20 -5
- package/scripts/build-goal-completion-audit.mjs +72 -25
- package/scripts/build-hosted-api-key-lifecycle.mjs +26 -8
- package/scripts/build-hosted-customer-lifecycle.mjs +20 -3
- package/scripts/build-hosted-probe-worker.mjs +19 -4
- package/scripts/build-installer-assets.mjs +41 -21
- package/scripts/build-operator-evidence-starter.mjs +59 -1
- package/scripts/build-production-backend-env-kit.mjs +2 -0
- package/scripts/build-production-unblocker.mjs +3 -0
- package/scripts/check.mjs +17 -3
- package/scripts/collect-hosted-backend-live-evidence.mjs +49 -12
- package/scripts/install-enigma-local.mjs +18 -5
- package/scripts/release-audit.mjs +65 -109
- package/scripts/release-provenance.mjs +6 -0
- package/scripts/run-backend-readiness-smoke.mjs +112 -10
- package/scripts/scan-secrets.mjs +1 -0
- package/scripts/simulate-production-env.mjs +7 -2
- package/scripts/validate-hosted-backend-live.mjs +112 -1
- package/specs/antibody-pack-v1.schema.json +95 -0
- package/specs/antigen-envelope-v1.schema.json +81 -0
- package/specs/boundary-manifest-v1.schema.json +35 -35
- package/specs/capsule-v1.schema.json +55 -55
- package/specs/claim-boundary-manifest-v1.schema.json +22 -22
- package/specs/claim-ledger-v1.schema.json +291 -0
- package/specs/context-passport-v1.schema.json +59 -0
- package/specs/deletion-tombstone-v1.schema.json +26 -26
- package/specs/evidence-packet-v1.schema.json +177 -0
- package/specs/hosted-backend-live-evidence-v1.schema.json +72 -3
- package/specs/immune-scan-report-v1.schema.json +112 -0
- package/specs/lifecycle-receipt-log-v1.schema.json +67 -0
- package/specs/memory-atom-v1.schema.json +59 -0
- package/specs/memory-event-v1.schema.json +42 -42
- package/specs/passport-v1.schema.json +50 -50
- package/specs/proof-of-non-use-v1.schema.json +65 -0
- package/specs/quarantine-record-v1.schema.json +126 -0
- package/specs/receipt-v1.schema.json +61 -61
- package/specs/state-checkpoint-v1.schema.json +37 -37
- package/specs/trust-bundle-v1.schema.json +56 -56
- package/specs/trust-card-v1.schema.json +119 -0
- package/docs/proof-network-launch-plan.md +0 -421
- package/packages/metering/PACKAGE_CONTRACT.md +0 -20
- package/scripts/build-ai-orchestration-plan.mjs +0 -248
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createCipheriv,
|
|
3
|
+
createDecipheriv,
|
|
4
|
+
createHash,
|
|
5
|
+
generateKeyPairSync,
|
|
6
|
+
randomBytes,
|
|
7
|
+
randomUUID,
|
|
8
|
+
sign,
|
|
9
|
+
verify,
|
|
10
|
+
X509Certificate,
|
|
11
|
+
} from 'node:crypto';
|
|
12
|
+
import { createPoseidonCommitment } from './poseidon.js';
|
|
13
|
+
|
|
14
|
+
export const ATTESTATION_SCHEMA = 'enigma.enclave.attestation.v2';
|
|
15
|
+
|
|
16
|
+
export const PROVIDER_TYPE_SIMULATED = 'simulated_software';
|
|
17
|
+
export const PROVIDER_TYPE_AWS_NITRO = 'aws_nitro_enclave';
|
|
18
|
+
export const PROVIDER_TYPE_INTEL_SGX = 'intel_sgx_dcap';
|
|
19
|
+
export const PROVIDER_TYPE_TPM2 = 'tpm_2_0_nvram';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Pinned Hardware Vendor Root Certificates & Trust Anchors
|
|
23
|
+
*/
|
|
24
|
+
export const TRUSTED_VENDOR_ROOTS = Object.freeze({
|
|
25
|
+
// AWS Nitro Enclaves Root CA (Amazon Root CA 1 SHA-256 Fingerprint)
|
|
26
|
+
AWS_NITRO_ROOT_FINGERPRINT: '2c:43:35:00:cc:aa:da:b8:31:0a:f9:90:35:b6:65:be:4a:36:7a:a2:6a:bf:f6:30:17:8c:fc:19:1b:2f:bc:e6',
|
|
27
|
+
// Standard TPM 2.0 Manufacturer Root Authority Fingerprints
|
|
28
|
+
TPM_TRUSTED_ISSUERS: new Set([
|
|
29
|
+
'TCG Trusted Platform Module Root CA',
|
|
30
|
+
'Intel TPM EK Root CA',
|
|
31
|
+
'Infineon TPM Root CA',
|
|
32
|
+
]),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Base Abstract Interface for Hardware Secure Enclave Providers.
|
|
37
|
+
*/
|
|
38
|
+
export class EnclaveProvider {
|
|
39
|
+
constructor(providerType) {
|
|
40
|
+
if (new.target === EnclaveProvider) {
|
|
41
|
+
throw new TypeError('Cannot construct EnclaveProvider instances directly');
|
|
42
|
+
}
|
|
43
|
+
this.providerType = providerType;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async getMeasurement() {
|
|
47
|
+
throw new Error('getMeasurement() must be implemented');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async createRecord(plaintext, metadata) {
|
|
51
|
+
throw new Error('createRecord() must be implemented');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async decryptRecord(recordId, ciphertext, iv, authTag) {
|
|
55
|
+
throw new Error('decryptRecord() must be implemented');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async zeroizeRecord(recordId) {
|
|
59
|
+
throw new Error('zeroizeRecord() must be implemented');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Explicit Test Simulator: Local Software Mock
|
|
65
|
+
* For local testing and CI only. Explicitly signed by a designated test authority.
|
|
66
|
+
*/
|
|
67
|
+
export class SimulatedEnclaveProvider extends EnclaveProvider {
|
|
68
|
+
constructor({ simulationLabel = 'local_dev_simulator', testAuthorityKeyPair = null } = {}) {
|
|
69
|
+
super(PROVIDER_TYPE_SIMULATED);
|
|
70
|
+
this.simulationLabel = simulationLabel;
|
|
71
|
+
this.isSimulated = true;
|
|
72
|
+
this.securityLevel = 'TEST_ONLY_UNTRUSTED_RAM';
|
|
73
|
+
|
|
74
|
+
if (testAuthorityKeyPair) {
|
|
75
|
+
this._signingKey = testAuthorityKeyPair.privateKey;
|
|
76
|
+
this.publicKeyPem = testAuthorityKeyPair.publicKey.export({ type: 'spki', format: 'pem' });
|
|
77
|
+
} else {
|
|
78
|
+
const { publicKey, privateKey } = generateKeyPairSync('ed25519');
|
|
79
|
+
this._signingKey = privateKey;
|
|
80
|
+
this.publicKeyPem = publicKey.export({ type: 'spki', format: 'pem' });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
this.enclaveId = `sim_${createHash('sha256').update(this.publicKeyPem).digest('hex').slice(0, 16)}`;
|
|
84
|
+
this.pcrDigest = createHash('sha256').update(`simulated.pcr.${simulationLabel}`).digest('hex');
|
|
85
|
+
|
|
86
|
+
this._memory = new Map();
|
|
87
|
+
this._tombstones = new Map();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async getMeasurement() {
|
|
91
|
+
return {
|
|
92
|
+
providerType: this.providerType,
|
|
93
|
+
isSimulated: true,
|
|
94
|
+
enclaveId: this.enclaveId,
|
|
95
|
+
pcrDigest: this.pcrDigest,
|
|
96
|
+
publicKeyPem: this.publicKeyPem,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async createRecord(plaintext, metadata = {}) {
|
|
101
|
+
const recordId = randomUUID();
|
|
102
|
+
const keyBuf = randomBytes(32);
|
|
103
|
+
const iv = randomBytes(12);
|
|
104
|
+
const salt = randomBytes(32);
|
|
105
|
+
|
|
106
|
+
const { commitment } = createPoseidonCommitment(keyBuf, salt);
|
|
107
|
+
|
|
108
|
+
const cipher = createCipheriv('aes-256-gcm', keyBuf, iv);
|
|
109
|
+
const plainBuf = Buffer.isBuffer(plaintext) ? plaintext : Buffer.from(plaintext, 'utf-8');
|
|
110
|
+
const ciphertext = Buffer.concat([cipher.update(plainBuf), cipher.final()]);
|
|
111
|
+
const authTag = cipher.getAuthTag();
|
|
112
|
+
|
|
113
|
+
this._memory.set(recordId, {
|
|
114
|
+
key: keyBuf,
|
|
115
|
+
commitment,
|
|
116
|
+
salt: salt.toString('hex'),
|
|
117
|
+
metadata,
|
|
118
|
+
createdAt: Date.now(),
|
|
119
|
+
status: 'ACTIVE',
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
recordId,
|
|
124
|
+
ciphertext: ciphertext.toString('base64'),
|
|
125
|
+
iv: iv.toString('base64'),
|
|
126
|
+
authTag: authTag.toString('base64'),
|
|
127
|
+
commitment,
|
|
128
|
+
salt: salt.toString('hex'),
|
|
129
|
+
enclaveId: this.enclaveId,
|
|
130
|
+
isSimulated: true,
|
|
131
|
+
pcrDigest: this.pcrDigest,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async decryptRecord(recordId, ciphertextBase64, ivBase64, authTagBase64) {
|
|
136
|
+
const record = this._memory.get(recordId);
|
|
137
|
+
if (!record || record.status !== 'ACTIVE') {
|
|
138
|
+
throw new Error(`RECORD_UNAVAILABLE: Record ${recordId} key does not exist or has been zeroized.`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const decipher = createDecipheriv('aes-256-gcm', record.key, Buffer.from(ivBase64, 'base64'));
|
|
142
|
+
decipher.setAuthTag(Buffer.from(authTagBase64, 'base64'));
|
|
143
|
+
|
|
144
|
+
const decrypted = Buffer.concat([
|
|
145
|
+
decipher.update(Buffer.from(ciphertextBase64, 'base64')),
|
|
146
|
+
decipher.final(),
|
|
147
|
+
]);
|
|
148
|
+
|
|
149
|
+
return decrypted.toString('utf-8');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async zeroizeRecord(recordId) {
|
|
153
|
+
const record = this._memory.get(recordId);
|
|
154
|
+
if (!record) {
|
|
155
|
+
throw new Error(`RECORD_NOT_FOUND: ${recordId} is not in simulator storage.`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (record.status === 'ZEROIZED') {
|
|
159
|
+
return this._tombstones.get(recordId);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Overwrite buffer in RAM
|
|
163
|
+
record.key.fill(0xff);
|
|
164
|
+
randomBytes(record.key.length).copy(record.key);
|
|
165
|
+
record.key.fill(0x00);
|
|
166
|
+
record.status = 'ZEROIZED';
|
|
167
|
+
this._memory.delete(recordId);
|
|
168
|
+
|
|
169
|
+
const timestamp = new Date().toISOString();
|
|
170
|
+
const nullifierHash = createHash('sha256')
|
|
171
|
+
.update(`${record.commitment}:${record.salt}:${timestamp}`)
|
|
172
|
+
.digest('hex');
|
|
173
|
+
|
|
174
|
+
const attestationPayload = {
|
|
175
|
+
schema: ATTESTATION_SCHEMA,
|
|
176
|
+
action: 'KEY_ZEROIZED',
|
|
177
|
+
recordId,
|
|
178
|
+
commitment: record.commitment,
|
|
179
|
+
nullifierHash: `0x${nullifierHash}`,
|
|
180
|
+
enclaveId: this.enclaveId,
|
|
181
|
+
providerType: this.providerType,
|
|
182
|
+
isSimulated: true,
|
|
183
|
+
pcrDigest: this.pcrDigest,
|
|
184
|
+
timestamp,
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const signature = sign(
|
|
188
|
+
null,
|
|
189
|
+
Buffer.from(JSON.stringify(attestationPayload), 'utf-8'),
|
|
190
|
+
this._signingKey
|
|
191
|
+
).toString('base64');
|
|
192
|
+
|
|
193
|
+
const attestation = {
|
|
194
|
+
payload: attestationPayload,
|
|
195
|
+
signature,
|
|
196
|
+
publicKeyPem: this.publicKeyPem,
|
|
197
|
+
certificateChain: [], // Simulator has no hardware PKI chain
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
this._tombstones.set(recordId, attestation);
|
|
201
|
+
return attestation;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async zeroizeBatch(recordIds) {
|
|
205
|
+
if (!Array.isArray(recordIds) || recordIds.length === 0) {
|
|
206
|
+
throw new Error('INVALID_BATCH: recordIds must be a non-empty array.');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const results = [];
|
|
210
|
+
const nullifierHashes = [];
|
|
211
|
+
const timestamp = new Date().toISOString();
|
|
212
|
+
|
|
213
|
+
for (const recordId of recordIds) {
|
|
214
|
+
const record = this._memory.get(recordId);
|
|
215
|
+
if (!record) {
|
|
216
|
+
throw new Error(`RECORD_NOT_FOUND: Record ${recordId} not found for batch zeroization.`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Secure multi-pass wipe
|
|
220
|
+
record.key.fill(0xff);
|
|
221
|
+
randomBytes(record.key.length).copy(record.key);
|
|
222
|
+
record.key.fill(0x00);
|
|
223
|
+
record.status = 'ZEROIZED';
|
|
224
|
+
this._memory.delete(recordId);
|
|
225
|
+
|
|
226
|
+
const nullifierHash = createHash('sha256')
|
|
227
|
+
.update(`${record.commitment}:${record.salt}:${timestamp}`)
|
|
228
|
+
.digest('hex');
|
|
229
|
+
|
|
230
|
+
nullifierHashes.push(nullifierHash);
|
|
231
|
+
results.push({
|
|
232
|
+
recordId,
|
|
233
|
+
commitment: record.commitment,
|
|
234
|
+
nullifierHash: `0x${nullifierHash}`,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const batchRoot = createHash('sha256')
|
|
239
|
+
.update(nullifierHashes.join(':'))
|
|
240
|
+
.digest('hex');
|
|
241
|
+
|
|
242
|
+
const batchPayload = {
|
|
243
|
+
schema: ATTESTATION_SCHEMA,
|
|
244
|
+
action: 'BATCH_KEYS_ZEROIZED',
|
|
245
|
+
batchSize: recordIds.length,
|
|
246
|
+
batchRoot: `0x${batchRoot}`,
|
|
247
|
+
records: results,
|
|
248
|
+
enclaveId: this.enclaveId,
|
|
249
|
+
providerType: this.providerType,
|
|
250
|
+
isSimulated: true,
|
|
251
|
+
pcrDigest: this.pcrDigest,
|
|
252
|
+
timestamp,
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const signature = sign(
|
|
256
|
+
null,
|
|
257
|
+
Buffer.from(JSON.stringify(batchPayload), 'utf-8'),
|
|
258
|
+
this._signingKey
|
|
259
|
+
).toString('base64');
|
|
260
|
+
|
|
261
|
+
return {
|
|
262
|
+
payload: batchPayload,
|
|
263
|
+
signature,
|
|
264
|
+
publicKeyPem: this.publicKeyPem,
|
|
265
|
+
certificateChain: [],
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Production Adapter for AWS Nitro Enclaves
|
|
272
|
+
*/
|
|
273
|
+
export class AwsNitroEnclaveAdapter extends EnclaveProvider {
|
|
274
|
+
constructor({ nsmDevicePath = '/dev/nsm', vsockPort = 5000, expectedPcr0 = null } = {}) {
|
|
275
|
+
super(PROVIDER_TYPE_AWS_NITRO);
|
|
276
|
+
this.nsmDevicePath = nsmDevicePath;
|
|
277
|
+
this.vsockPort = vsockPort;
|
|
278
|
+
this.expectedPcr0 = expectedPcr0;
|
|
279
|
+
this.isSimulated = false;
|
|
280
|
+
this.securityLevel = 'HARDWARE_ISOLATED_NITRO_VCPU';
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async getMeasurement() {
|
|
284
|
+
return {
|
|
285
|
+
providerType: this.providerType,
|
|
286
|
+
isSimulated: false,
|
|
287
|
+
nsmDevicePath: this.nsmDevicePath,
|
|
288
|
+
vsockPort: this.vsockPort,
|
|
289
|
+
expectedPcr0: this.expectedPcr0,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
async createRecord() {
|
|
294
|
+
throw new Error('NITRO_DEVICE_NOT_AVAILABLE: AWS Nitro Enclave kernel module (/dev/nsm) not found on host platform.');
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async decryptRecord() {
|
|
298
|
+
throw new Error('NITRO_DEVICE_NOT_AVAILABLE: AWS Nitro Enclave kernel module (/dev/nsm) not found on host platform.');
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
async zeroizeRecord() {
|
|
302
|
+
throw new Error('NITRO_DEVICE_NOT_AVAILABLE: AWS Nitro Enclave kernel module (/dev/nsm) not found on host platform.');
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Production Adapter for TPM 2.0
|
|
308
|
+
*/
|
|
309
|
+
export class Tpm2EnclaveAdapter extends EnclaveProvider {
|
|
310
|
+
constructor({ tpmPath = '/dev/tpmrm0', pcrIndex = 16 } = {}) {
|
|
311
|
+
super(PROVIDER_TYPE_TPM2);
|
|
312
|
+
this.tpmPath = tpmPath;
|
|
313
|
+
this.pcrIndex = pcrIndex;
|
|
314
|
+
this.isSimulated = false;
|
|
315
|
+
this.securityLevel = 'HARDWARE_DISCRETE_TPM2';
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
async getMeasurement() {
|
|
319
|
+
return {
|
|
320
|
+
providerType: this.providerType,
|
|
321
|
+
isSimulated: false,
|
|
322
|
+
tpmPath: this.tpmPath,
|
|
323
|
+
pcrIndex: this.pcrIndex,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
async createRecord() {
|
|
328
|
+
throw new Error('TPM2_DEVICE_NOT_AVAILABLE: TPM 2.0 resource manager device not accessible.');
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
async decryptRecord() {
|
|
332
|
+
throw new Error('TPM2_DEVICE_NOT_AVAILABLE: TPM 2.0 resource manager device not accessible.');
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
async zeroizeRecord() {
|
|
336
|
+
throw new Error('TPM2_DEVICE_NOT_AVAILABLE: TPM 2.0 resource manager device not accessible.');
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Robust Attestation Verifier with Pinned Hardware Roots & PKI Chain Verification
|
|
342
|
+
* NEVER trusts unanchored claimant-supplied public keys.
|
|
343
|
+
*/
|
|
344
|
+
export class AttestationVerifier {
|
|
345
|
+
/**
|
|
346
|
+
* @param {Object} attestation
|
|
347
|
+
* @param {Object} options
|
|
348
|
+
* @param {boolean} [options.allowSimulated=false]
|
|
349
|
+
* @param {string} [options.allowlistedSimulatedKeyPem] - Designated test authority key for CI/local testing
|
|
350
|
+
* @param {string} [options.expectedPcr]
|
|
351
|
+
* @param {string} [options.pinnedRootFingerprint]
|
|
352
|
+
*/
|
|
353
|
+
static verify(
|
|
354
|
+
attestation,
|
|
355
|
+
{
|
|
356
|
+
allowSimulated = false,
|
|
357
|
+
allowlistedSimulatedKeyPem = null,
|
|
358
|
+
expectedPcr = null,
|
|
359
|
+
pinnedRootFingerprint = TRUSTED_VENDOR_ROOTS.AWS_NITRO_ROOT_FINGERPRINT,
|
|
360
|
+
} = {}
|
|
361
|
+
) {
|
|
362
|
+
if (!attestation || !attestation.payload || !attestation.signature) {
|
|
363
|
+
return { valid: false, error: 'MALFORMED_ATTESTATION_STRUCTURE' };
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const { payload, signature, publicKeyPem, certificateChain = [] } = attestation;
|
|
367
|
+
const providerType = payload.providerType || (payload.isSimulated ? PROVIDER_TYPE_SIMULATED : null);
|
|
368
|
+
|
|
369
|
+
// 1. Dispatch by Provider Type
|
|
370
|
+
if (providerType === PROVIDER_TYPE_SIMULATED || payload.isSimulated) {
|
|
371
|
+
if (!allowSimulated) {
|
|
372
|
+
return {
|
|
373
|
+
valid: false,
|
|
374
|
+
error: 'REJECTED_SIMULATION: Attestation is a software simulation, not genuine hardware TEE.',
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// If simulated mode is enabled, it MUST match the allowlisted test authority public key
|
|
379
|
+
if (allowlistedSimulatedKeyPem && publicKeyPem !== allowlistedSimulatedKeyPem) {
|
|
380
|
+
return {
|
|
381
|
+
valid: false,
|
|
382
|
+
error: 'UNAUTHORIZED_SIMULATOR_KEY: Test signature does not match allowlisted test authority.',
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Verify signature with the explicit test key
|
|
387
|
+
return AttestationVerifier._verifySignature(payload, signature, publicKeyPem, expectedPcr);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// 2. Hardware Enclave Verification (AWS Nitro / SGX / TPM)
|
|
391
|
+
if (providerType === PROVIDER_TYPE_AWS_NITRO) {
|
|
392
|
+
if (!certificateChain || certificateChain.length === 0) {
|
|
393
|
+
return {
|
|
394
|
+
valid: false,
|
|
395
|
+
error: 'MISSING_CERTIFICATE_CHAIN: Hardware Nitro attestation requires X.509 certificate chain.',
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Validate Certificate Chain against Pinned Root CA
|
|
400
|
+
try {
|
|
401
|
+
const rootCert = new X509Certificate(certificateChain[certificateChain.length - 1]);
|
|
402
|
+
if (pinnedRootFingerprint && rootCert.fingerprint256.toLowerCase() !== pinnedRootFingerprint.toLowerCase()) {
|
|
403
|
+
return {
|
|
404
|
+
valid: false,
|
|
405
|
+
error: `UNTRUSTED_ROOT_CA: Nitro Root CA fingerprint ${rootCert.fingerprint256} does not match pinned trust anchor.`,
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Verify Leaf Certificate in chain
|
|
410
|
+
const leafCert = new X509Certificate(certificateChain[0]);
|
|
411
|
+
const leafPublicKeyPem = leafCert.publicKey.export({ type: 'spki', format: 'pem' });
|
|
412
|
+
|
|
413
|
+
return AttestationVerifier._verifySignature(payload, signature, leafPublicKeyPem, expectedPcr);
|
|
414
|
+
} catch (err) {
|
|
415
|
+
return { valid: false, error: `CERT_CHAIN_VALIDATION_FAILED: ${err.message}` };
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if (providerType === PROVIDER_TYPE_TPM2) {
|
|
420
|
+
if (!certificateChain || certificateChain.length === 0) {
|
|
421
|
+
return {
|
|
422
|
+
valid: false,
|
|
423
|
+
error: 'MISSING_TPM_EK_CERT: TPM 2.0 quote requires Endorsement Key (EK) certificate chain.',
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
try {
|
|
428
|
+
const ekCert = new X509Certificate(certificateChain[0]);
|
|
429
|
+
if (!TRUSTED_VENDOR_ROOTS.TPM_TRUSTED_ISSUERS.has(ekCert.issuer)) {
|
|
430
|
+
return {
|
|
431
|
+
valid: false,
|
|
432
|
+
error: `UNTRUSTED_TPM_ISSUER: Issuer ${ekCert.issuer} not in allowlisted TPM manufacturer root authorities.`,
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const ekPublicKeyPem = ekCert.publicKey.export({ type: 'spki', format: 'pem' });
|
|
437
|
+
return AttestationVerifier._verifySignature(payload, signature, ekPublicKeyPem, expectedPcr);
|
|
438
|
+
} catch (err) {
|
|
439
|
+
return { valid: false, error: `TPM_CERT_VALIDATION_FAILED: ${err.message}` };
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return {
|
|
444
|
+
valid: false,
|
|
445
|
+
error: `UNKNOWN_OR_UNSUPPORTED_PROVIDER: ${providerType}`,
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
static _verifySignature(payload, signature, keyPem, expectedPcr) {
|
|
450
|
+
if (expectedPcr && payload.pcrDigest !== expectedPcr) {
|
|
451
|
+
return { valid: false, error: 'PCR_MEASUREMENT_MISMATCH' };
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
try {
|
|
455
|
+
const canonical = Buffer.from(JSON.stringify(payload), 'utf-8');
|
|
456
|
+
const isVerified = verify(
|
|
457
|
+
null,
|
|
458
|
+
canonical,
|
|
459
|
+
keyPem,
|
|
460
|
+
Buffer.from(signature, 'base64')
|
|
461
|
+
);
|
|
462
|
+
|
|
463
|
+
if (!isVerified) {
|
|
464
|
+
return { valid: false, error: 'INVALID_CRYPTOGRAPHIC_SIGNATURE' };
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
return {
|
|
468
|
+
valid: true,
|
|
469
|
+
action: payload.action,
|
|
470
|
+
commitment: payload.commitment,
|
|
471
|
+
nullifierHash: payload.nullifierHash,
|
|
472
|
+
providerType: payload.providerType,
|
|
473
|
+
isSimulated: Boolean(payload.isSimulated),
|
|
474
|
+
enclaveId: payload.enclaveId,
|
|
475
|
+
timestamp: payload.timestamp,
|
|
476
|
+
};
|
|
477
|
+
} catch (err) {
|
|
478
|
+
return { valid: false, error: err.message };
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
// Enigma Signed Memory Erasure & State-Reset Attestation Standard.
|
|
2
|
+
// Emits verifiable, offline-provable state reset receipts certifying that
|
|
3
|
+
// active memory trees, vector indices, and session key material were wiped
|
|
4
|
+
// from the local runtime and on-disk vault storage.
|
|
5
|
+
//
|
|
6
|
+
// [Truth-in-Claims Boundary]: This standard certifies software-level state reset,
|
|
7
|
+
// tombstoning, and storage unlinking by the authenticated agent keypair. It does not
|
|
8
|
+
// claim physical hardware-level RAM bus zeroization against cold-boot attacks.
|
|
9
|
+
import crypto from 'node:crypto';
|
|
10
|
+
import { MerkleSet } from '../../core/src/index.js';
|
|
11
|
+
|
|
12
|
+
export const ERASURE_ATTESTATION_SCHEMA = 'enigma.erasure_attestation.v1';
|
|
13
|
+
export const ERASURE_RECEIPT_SCHEMA = ERASURE_ATTESTATION_SCHEMA;
|
|
14
|
+
|
|
15
|
+
const ED25519_SPKI_PREFIX = Buffer.from('302a300506032b6570032100', 'hex');
|
|
16
|
+
const ED25519_PKCS8_PREFIX = Buffer.from('302e020100300506032b657004220420', 'hex');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Best-effort in-memory overwriting of a volatile Buffer with random noise and zeros.
|
|
20
|
+
* @param {Buffer} buffer
|
|
21
|
+
*/
|
|
22
|
+
export function secureZeroizeBuffer(buffer) {
|
|
23
|
+
if (!Buffer.isBuffer(buffer)) return;
|
|
24
|
+
try {
|
|
25
|
+
crypto.randomFillSync(buffer);
|
|
26
|
+
buffer.fill(0);
|
|
27
|
+
} catch {
|
|
28
|
+
buffer.fill(0);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Computes canonical signable string for an erasure attestation receipt.
|
|
34
|
+
* @param {Object} receipt
|
|
35
|
+
* @returns {string}
|
|
36
|
+
*/
|
|
37
|
+
export function getErasureSignablePayload(receipt) {
|
|
38
|
+
return JSON.stringify({
|
|
39
|
+
schema: receipt.schema || ERASURE_ATTESTATION_SCHEMA,
|
|
40
|
+
version: receipt.version || 1,
|
|
41
|
+
erasure_id: receipt.erasure_id,
|
|
42
|
+
agent_id: receipt.agent_id,
|
|
43
|
+
scope: receipt.scope,
|
|
44
|
+
erased_memory_count: receipt.erased_memory_count,
|
|
45
|
+
pre_erasure_root: receipt.pre_erasure_root,
|
|
46
|
+
erasure_digest: receipt.erasure_digest,
|
|
47
|
+
storage_wiped: Boolean(receipt.storage_wiped),
|
|
48
|
+
timestamp: receipt.timestamp,
|
|
49
|
+
agent_pubkey: receipt.agent_pubkey,
|
|
50
|
+
metadata: receipt.metadata || {},
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Creates and cryptographically signs an ErasureAttestation receipt.
|
|
56
|
+
* @param {Object} params
|
|
57
|
+
* @param {string} params.agentId - Sovereign agent identifier
|
|
58
|
+
* @param {string} params.scope - Memory namespace scope
|
|
59
|
+
* @param {number} params.erasedMemoryCount - Number of memory atoms wiped
|
|
60
|
+
* @param {string} params.preErasureRoot - Active Merkle root prior to erasure
|
|
61
|
+
* @param {Array<string>} [params.activeCommitments=[]] - List of active commitments to hash into erasure digest
|
|
62
|
+
* @param {boolean} [params.storageWiped=false] - Whether persistent on-disk storage was wiped
|
|
63
|
+
* @param {Buffer|string} params.signingSecretKey - 32-byte Ed25519 private key
|
|
64
|
+
* @param {Buffer|string} params.signingPublicKey - 32-byte Ed25519 public key
|
|
65
|
+
* @param {Object} [params.metadata={}]
|
|
66
|
+
* @returns {Object} Signed ErasureAttestation receipt
|
|
67
|
+
*/
|
|
68
|
+
export function createErasureAttestation(params) {
|
|
69
|
+
if (!params.agentId) throw new Error('agentId is required');
|
|
70
|
+
if (!params.scope) throw new Error('scope is required');
|
|
71
|
+
if (!params.signingSecretKey || !params.signingPublicKey) {
|
|
72
|
+
throw new Error('signingSecretKey and signingPublicKey are required');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const preErasureRoot = params.preErasureRoot || (params.activeCommitments ? new MerkleSet(params.activeCommitments).root() : new MerkleSet().root());
|
|
76
|
+
const commitments = (params.activeCommitments || []).map(c => String(c).replace(/^sha256:/, '').toLowerCase());
|
|
77
|
+
|
|
78
|
+
// Compute immutable erasure digest committing to the exact set of wiped atoms
|
|
79
|
+
const erasureDigest = crypto
|
|
80
|
+
.createHash('sha256')
|
|
81
|
+
.update(preErasureRoot)
|
|
82
|
+
.update(String(params.erasedMemoryCount))
|
|
83
|
+
.update(commitments.join(','))
|
|
84
|
+
.update(params.storageWiped ? 'storage:wiped' : 'storage:retained')
|
|
85
|
+
.digest('hex');
|
|
86
|
+
|
|
87
|
+
const erasureId = `ers_${Date.now()}_${crypto.randomBytes(4).toString('hex')}`;
|
|
88
|
+
const pubHex = Buffer.isBuffer(params.signingPublicKey)
|
|
89
|
+
? params.signingPublicKey.toString('hex')
|
|
90
|
+
: params.signingPublicKey;
|
|
91
|
+
const privBuf = Buffer.isBuffer(params.signingSecretKey)
|
|
92
|
+
? params.signingSecretKey
|
|
93
|
+
: Buffer.from(params.signingSecretKey, 'hex');
|
|
94
|
+
|
|
95
|
+
const unsignedReceipt = {
|
|
96
|
+
schema: ERASURE_ATTESTATION_SCHEMA,
|
|
97
|
+
version: 1,
|
|
98
|
+
erasure_id: erasureId,
|
|
99
|
+
agent_id: params.agentId,
|
|
100
|
+
scope: params.scope,
|
|
101
|
+
erased_memory_count: typeof params.erasedMemoryCount === 'number' ? params.erasedMemoryCount : commitments.length,
|
|
102
|
+
pre_erasure_root: preErasureRoot,
|
|
103
|
+
erasure_digest: erasureDigest,
|
|
104
|
+
storage_wiped: Boolean(params.storageWiped),
|
|
105
|
+
timestamp: new Date().toISOString(),
|
|
106
|
+
agent_pubkey: pubHex,
|
|
107
|
+
metadata: params.metadata || {},
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const signable = getErasureSignablePayload(unsignedReceipt);
|
|
111
|
+
const der = Buffer.concat([ED25519_PKCS8_PREFIX, privBuf]);
|
|
112
|
+
const privKeyObj = crypto.createPrivateKey({ key: der, format: 'der', type: 'pkcs8' });
|
|
113
|
+
const signature = crypto.sign(null, Buffer.from(signable, 'utf8'), privKeyObj);
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
...unsignedReceipt,
|
|
117
|
+
signature: signature.toString('hex'),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export const createErasureReceipt = createErasureAttestation;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Offline verification of an ErasureAttestation receipt.
|
|
125
|
+
* @param {Object} receipt
|
|
126
|
+
* @param {Object} [options]
|
|
127
|
+
* @param {string} [options.expectedAgentPubkey]
|
|
128
|
+
* @param {Array<string>} [options.expectedCommitments] - If provided, recomputes erasure_digest
|
|
129
|
+
* @returns {{ ok: boolean, verified: boolean, erasure_id: string, agent_id: string, signatureValid: boolean, digestValid?: boolean, errors: string[] }}
|
|
130
|
+
*/
|
|
131
|
+
export function verifyErasureAttestation(receipt, options = {}) {
|
|
132
|
+
const errors = [];
|
|
133
|
+
if (!receipt || typeof receipt !== 'object') {
|
|
134
|
+
return { ok: false, verified: false, erasure_id: '', agent_id: '', signatureValid: false, errors: ['Invalid receipt object'] };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (receipt.schema !== ERASURE_ATTESTATION_SCHEMA && receipt.schema !== 'enigma.erasure_receipt.v1') {
|
|
138
|
+
errors.push(`Unrecognized erasure receipt schema: ${receipt.schema}`);
|
|
139
|
+
}
|
|
140
|
+
if (!receipt.erasure_id) errors.push('Missing erasure_id');
|
|
141
|
+
if (!receipt.agent_id) errors.push('Missing agent_id');
|
|
142
|
+
if (!receipt.pre_erasure_root) errors.push('Missing pre_erasure_root');
|
|
143
|
+
if (!receipt.erasure_digest || !/^[0-9a-fA-F]{64}$/.test(receipt.erasure_digest)) {
|
|
144
|
+
errors.push('Invalid 64-hex erasure_digest');
|
|
145
|
+
}
|
|
146
|
+
if (!receipt.agent_pubkey || !/^[0-9a-fA-F]{64}$/.test(receipt.agent_pubkey)) {
|
|
147
|
+
errors.push('Invalid 64-hex agent_pubkey');
|
|
148
|
+
}
|
|
149
|
+
if (!receipt.signature || !/^[0-9a-fA-F]{128}$/.test(receipt.signature)) {
|
|
150
|
+
errors.push('Invalid 128-hex signature length');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (options.expectedAgentPubkey && receipt.agent_pubkey !== options.expectedAgentPubkey) {
|
|
154
|
+
errors.push(`agent_pubkey mismatch: expected ${options.expectedAgentPubkey}, got ${receipt.agent_pubkey}`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Verify erasure digest if expectedCommitments supplied
|
|
158
|
+
let digestValid;
|
|
159
|
+
if (Array.isArray(options.expectedCommitments)) {
|
|
160
|
+
const commitments = options.expectedCommitments.map(c => String(c).replace(/^sha256:/, '').toLowerCase());
|
|
161
|
+
const expectedDigest = crypto
|
|
162
|
+
.createHash('sha256')
|
|
163
|
+
.update(receipt.pre_erasure_root)
|
|
164
|
+
.update(String(receipt.erased_memory_count))
|
|
165
|
+
.update(commitments.join(','))
|
|
166
|
+
.update(receipt.storage_wiped ? 'storage:wiped' : 'storage:retained')
|
|
167
|
+
.digest('hex');
|
|
168
|
+
|
|
169
|
+
if (expectedDigest !== receipt.erasure_digest) {
|
|
170
|
+
digestValid = false;
|
|
171
|
+
errors.push(`erasure_digest mismatch: expected ${expectedDigest}, got ${receipt.erasure_digest}`);
|
|
172
|
+
} else {
|
|
173
|
+
digestValid = true;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Verify Ed25519 signature
|
|
178
|
+
let signatureValid = false;
|
|
179
|
+
try {
|
|
180
|
+
const signable = getErasureSignablePayload(receipt);
|
|
181
|
+
const pubBuf = Buffer.from(receipt.agent_pubkey, 'hex');
|
|
182
|
+
const der = Buffer.concat([ED25519_SPKI_PREFIX, pubBuf]);
|
|
183
|
+
const pubKeyObj = crypto.createPublicKey({ key: der, format: 'der', type: 'spki' });
|
|
184
|
+
const sigBuf = Buffer.from(receipt.signature, 'hex');
|
|
185
|
+
|
|
186
|
+
signatureValid = crypto.verify(null, Buffer.from(signable, 'utf8'), pubKeyObj, sigBuf);
|
|
187
|
+
if (!signatureValid) {
|
|
188
|
+
errors.push('Cryptographic Ed25519 signature verification failed');
|
|
189
|
+
}
|
|
190
|
+
} catch (err) {
|
|
191
|
+
errors.push(`Signature verification exception: ${err.message}`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const ok = errors.length === 0 && signatureValid && (digestValid === undefined || digestValid);
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
ok,
|
|
198
|
+
verified: ok,
|
|
199
|
+
erasure_id: receipt.erasure_id || '',
|
|
200
|
+
agent_id: receipt.agent_id || '',
|
|
201
|
+
signatureValid,
|
|
202
|
+
digestValid,
|
|
203
|
+
errors,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export const verifyErasureReceipt = verifyErasureAttestation;
|