diff --git a/README.md b/README.md index 13205350b..a77b42fc3 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Original Contributors: Hang Yin, Kevin Wang, Andrew Miller -[Documentation](https://docs.phala.com/dstack) · [Examples](https://github.com/Dstack-TEE/dstack-examples) · [Community](https://t.me/+UO4bS4jflr45YmUx) +[Documentation](https://docs.phala.com/dstack) · [Security](./SECURITY.md) · [Examples](https://github.com/Dstack-TEE/dstack-examples) · [Community](https://t.me/+UO4bS4jflr45YmUx) @@ -89,6 +89,19 @@ Your container runs inside a Confidential VM, such as Intel TDX or AMD SEV-SNP, [Full security model →](./docs/security/security-model.md) +## Security and Trust + +Security docs are linked here so deployers and reviewers can quickly find the trust model, production guidance, audit, and the status of already-answered public findings. + +- [Security Overview](./docs/security/) - entry point for users, operators, researchers, and AI agents +- [Security Model](./docs/security/security-model.md) - threat model, trust boundaries, and verification checklist +- [Public Security Reports](./docs/security/public-security-reports.md) - public status for security reports and related hardening work +- [Security Best Practices](./docs/security/security-best-practices.md) - production settings and hardening guidance +- [Security Audit](./docs/security/dstack-audit.pdf) - third-party audit by zkSecurity +- [Report a Vulnerability](./SECURITY.md) - use GitHub's private security reporting path + +Please do not disclose exploitable vulnerabilities in public GitHub issues. Use the private reporting path in [SECURITY.md](./SECURITY.md). + ## SDKs Apps communicate with the guest agent via HTTP over `/var/run/dstack.sock`. Use the [HTTP API](./sdk/curl/api.md) directly with curl, or use a language SDK: @@ -121,14 +134,6 @@ Apps communicate with the guest agent via HTTP over `/var/run/dstack.sock`. Use - [Design Decisions](./docs/design-and-hardening-decisions.md) - Architecture rationale - [FAQ](./docs/faq.md) - Frequently asked questions -## Security - -- [Security Overview](./docs/security/) - Security documentation and responsible disclosure -- [Security Model](./docs/security/security-model.md) - Threat model and trust boundaries -- [Security Best Practices](./docs/security/security-best-practices.md) - Production hardening -- [Security Audit](./docs/security/dstack-audit.pdf) - Third-party audit by zkSecurity -- [CVM Boundaries](./docs/security/cvm-boundaries.md) - Information exchange and isolation - ## FAQ
@@ -180,7 +185,7 @@ Yes. dstack runs on supported TEE-capable servers, including Intel TDX-capable h - **GCP**: Intel TDX (Confidential VMs) - **AWS**: Nitro Enclaves (NSM attestation) -- **Bare metal**: Intel TDX (4th/5th Gen Xeon) and AMD SEV-SNP on supported dstack OS images +- **Bare metal**: Intel TDX (4th/5th Gen Xeon) and AMD SEV-SNP on supported dstack OS images. Intel TDX is the production path; AMD SEV-SNP is new and experimental. - **GPUs**: NVIDIA Confidential Computing (H100, Blackwell)
@@ -227,5 +232,3 @@ Logo and branding assets: [dstack-logo-kit](./docs/assets/dstack-logo-kit/) ## License Apache 2.0 - - diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..fb28c40a2 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,21 @@ +# Security + +Use this file for vulnerability reports. For the security model, production guidance, audit, and already-answered public findings, start with [Security Documentation](./docs/security/). + +## Report a vulnerability + +If you believe you found a vulnerability, please use [GitHub's private security reporting features](https://docs.github.com/en/code-security/how-tos/report-and-fix-vulnerabilities/report-privately) for this repository. If GitHub private reporting is unavailable, contact security@phala.network. + +Do not open public GitHub issues for exploitable vulnerabilities or details that could help exploit production deployments. + +Use private reporting for issues that could expose secrets, bypass attestation or authorization, compromise KMS keys, weaken workload isolation, or enable unauthorized code or configuration changes in production deployments. + +## Public security questions + +Use public issues only for questions about documented behavior, documentation gaps, already-public findings, or hardening ideas that do not include an exploit path. + +Before opening a public security question, check [Public Security Reports](./docs/security/public-security-reports.md). It records public report status and related hardening or roadmap work. + +## Production trust boundary + +Development settings are not production-safe merely because they are present in the codebase. Production deployments must rely on measured configuration, expected TEE measurements, authorization policy, and attestation verification. The [Security Model](./docs/security/security-model.md#development-modes-are-auditable-not-production-safe) is the source of truth for what dstack treats as a production guarantee. diff --git a/docs/security/README.md b/docs/security/README.md index bc82eded6..4086f2f79 100644 --- a/docs/security/README.md +++ b/docs/security/README.md @@ -1,17 +1,15 @@ # Security Documentation -dstack security resources for auditors, researchers, and operators. +Use these resources to understand dstack's trust model, production requirements, audit history, and public security report status. -## Audit +## Resources -dstack has been audited by zkSecurity. See the [full audit report](./dstack-audit.pdf). +- [Security Model](./security-model.md) - threat model, trust boundaries, and verifier checklist +- [Security Best Practices](./security-best-practices.md) - production hardening for KMS, gateway, and VMM deployments +- [Security Audit](./dstack-audit.pdf) - zkSecurity audit report +- [Public Security Reports](./public-security-reports.md) - status of already-public reports and findings +- [CVM Boundaries](./cvm-boundaries.md) - data exchanged across the CVM, host, KMS, and gateway -## Documentation +## Report a Vulnerability -- [Security Model](./security-model.md) - Threat model, trust boundaries, and verification checklist -- [Security Best Practices](./security-best-practices.md) - Production hardening guide -- [CVM Boundaries](./cvm-boundaries.md) - Information exchange and isolation details - -## Responsible Disclosure - -To report a security vulnerability, email security@phala.network. We will respond within 48 hours. +Do not disclose exploitable vulnerabilities in public GitHub issues. Use the private reporting path in [SECURITY.md](../../SECURITY.md). If GitHub private reporting is unavailable, contact security@phala.network. diff --git a/docs/security/public-security-reports.md b/docs/security/public-security-reports.md new file mode 100644 index 000000000..03c3f0470 --- /dev/null +++ b/docs/security/public-security-reports.md @@ -0,0 +1,75 @@ +# Public Security Reports + +This page tracks public GitHub issues filed as security reports or mirrors of private advisories. It shows whether each report is fixed, documented, not a production vulnerability, duplicate, or still open. + +For new exploitable vulnerabilities, use the private reporting path in [SECURITY.md](../../SECURITY.md). Do not include exploit details in public issues. + +Status snapshot: 2026-06-30. General support, process, consolidation, and feature-request issues are excluded. Related hardening and roadmap trackers are listed separately. + +## Report outcomes + +Use these outcomes when reading public security reports and already-public findings: + +| Outcome | Meaning | +| --- | --- | +| Valid report, fixed | The report was valid and was addressed by a code or configuration change | +| Valid report, documented | The report describes real behavior, but the project response is documentation or threat-model clarification rather than a code change | +| Valid hardening, open | The report is valid defense-in-depth work and remains open | +| Valid roadmap, open | The report identifies security-related design work that needs a compatibility or migration plan | +| Not a production vulnerability | The report does not compromise supported production deployments under the documented threat model | +| Duplicate | The report repeats another public issue or private advisory response | + +## Public reports and findings + +These issues were filed as concrete vulnerability reports, security audit findings, or public mirrors of private advisories. Some resulted in fixes. Some are documented design choices or not production vulnerabilities. + +| Issue | Status | Outcome | Project response | +| --- | --- | --- | --- | +| [#549](https://github.com/Dstack-TEE/dstack/issues/549) Disk encryption key collision when `no_instance_id=true` and HKDF context ambiguity | Open | Valid report, documented | `no_instance_id=true` intentionally shares disk keys across instances, and the HKDF inputs have fixed lengths. No code fix has been applied. Zero-padding for the unset instance ID remains optional hardening | +| [#550](https://github.com/Dstack-TEE/dstack/issues/550) Compose hash computed on raw bytes, not canonicalized JSON | Closed | Valid report, documented | dstack treats compose JSON as an opaque byte sequence. Any byte-level change is a different measured application configuration. No code fix was applied | +| [#551](https://github.com/Dstack-TEE/dstack/issues/551) Shell injection via `init_script` and `pre_launch_script` in compose | Closed | Valid report, documented | Scripts are application-owned code and are measured as part of app configuration. Verifiers must treat script contents as part of the application trust decision. No code fix was applied | +| [#552](https://github.com/Dstack-TEE/dstack/issues/552) Static HKDF salt and no key versioning | Open | Valid roadmap, open | Static salt is acceptable with high-entropy KMS root material and explicit context. No code fix has been applied. Key versioning and rotation require a broader compatibility design | +| [#553](https://github.com/Dstack-TEE/dstack/issues/553) `derive_dh_secret` hashes PKCS#8 DER | Closed | Valid report, fixed | [#603](https://github.com/Dstack-TEE/dstack/pull/603) stabilizes the P-256 private key encoding used for derivation | +| [#554](https://github.com/Dstack-TEE/dstack/issues/554) Signature concatenation without length prefixes enables collision | Open | Valid report, fixed | [#604](https://github.com/Dstack-TEE/dstack/pull/604) enforces the 20-byte `app_id` length in CVM setup | +| [#555](https://github.com/Dstack-TEE/dstack/issues/555) LUKS header TOCTOU between validation and `luksOpen` | Open | Not a production vulnerability | The setup code validates and opens the same in-memory LUKS header. No code fix was applied | +| [#556](https://github.com/Dstack-TEE/dstack/issues/556) Disk encryption key and WireGuard key visible in `/proc/PID/cmdline` | Open | Valid hardening, open | Tracks removal of transient command-line exposure for secret-bearing setup commands | +| [#557](https://github.com/Dstack-TEE/dstack/issues/557) Runtime event log writable by any VM process | Open | Valid report, fixed | [#602](https://github.com/Dstack-TEE/dstack/pull/602) restricts runtime event-log permissions | +| [#558](https://github.com/Dstack-TEE/dstack/issues/558) Path traversal in KMS `remove_cache` | Closed | Valid report, fixed | [#601](https://github.com/Dstack-TEE/dstack/pull/601) validates cache paths before deletion | +| [#559](https://github.com/Dstack-TEE/dstack/issues/559) Zero `mr_config_id` bypasses verification and weakens `mr_aggregated` identity | Open | Not a production vulnerability | Zero `mr_config_id` remains an unset-value compatibility case, and configuration changes are still reflected through RTMR-based measurements. No code fix was applied | +| [#560](https://github.com/Dstack-TEE/dstack/issues/560) Admin token comparison not constant-time | Open | Not a production vulnerability | The comparison is over a SHA-256 digest of a high-entropy token, not the raw token. No code fix was applied | +| [#561](https://github.com/Dstack-TEE/dstack/issues/561) KMS TLS client certificates are non-mandatory in Rocket config | Open | Valid report, documented | The TLS listener allows unauthenticated bootstrap, temp-CA bootstrap, and public endpoints. `GetTempCaCert` returns temp CA private material for bootstrap. App/KMS key release requires verified caller attestation, and certificate signing verifies the CSR signature and embedded attestation. No code fix was applied | +| [#562](https://github.com/Dstack-TEE/dstack/issues/562) Configfs path overridable through an environment variable | Open | Not a production vulnerability | A process that can choose its own quote path is already inside the measured CVM behavior. No code fix has been applied. A production guard for `DCAP_TDX_QUOTE_CONFIGFS_PATH` remains possible hardening | +| [#563](https://github.com/Dstack-TEE/dstack/issues/563) `simulate_quote` runtime path in production guest agent | Open | Valid report, fixed | [#582](https://github.com/Dstack-TEE/dstack/pull/582) isolates the simulator into a dedicated binary | +| [#564](https://github.com/Dstack-TEE/dstack/issues/564) `GetAppEnvEncryptPubKey` unauthenticated app ID enumeration | Open | Not a production vulnerability | The RPC returns a public encryption key before an app has an attested identity, and `app_id` is not treated as secret. No code fix was applied | +| [#565](https://github.com/Dstack-TEE/dstack/issues/565) Infinite loop in `wait_for_generation_change` | Closed | Valid report, fixed | [#596](https://github.com/Dstack-TEE/dstack/pull/596) bounds the ConfigFS generation wait loop | +| [#566](https://github.com/Dstack-TEE/dstack/issues/566) Gzip decompression bomb in RA-TLS cert extension | Open | Valid report, fixed | [#595](https://github.com/Dstack-TEE/dstack/pull/595) bounds decompressed RA-TLS event-log extension size | +| [#567](https://github.com/Dstack-TEE/dstack/issues/567) Unbounded allocation in `VecOf` decode | Open | Valid report, fixed | [#570](https://github.com/Dstack-TEE/dstack/pull/570) caps `VecOf` decode length and pre-allocation | +| [#568](https://github.com/Dstack-TEE/dstack/issues/568) Webhook URL leaked via `println!` in production code | Closed | Valid report, fixed | Fixed before the issue was triaged by removing the unsafe log output in `79b8b8d2` | +| [#605](https://github.com/Dstack-TEE/dstack/issues/605) Guest agent derives identical key material for `ed25519` and `secp256k1` | Open | Valid report, documented | Existing derived key bytes are preserved. Docs state that `path` is the domain separator and callers must use algorithm-specific paths when they require independent keys. No code fix was applied | +| [#606](https://github.com/Dstack-TEE/dstack/issues/606) App keys and decrypted env files world-readable | Open | Valid hardening, open | Tightening secret-bearing file writes to owner-only permissions (`0600`) is valid defense-in-depth work with no expected compatibility cost | +| [#607](https://github.com/Dstack-TEE/dstack/issues/607) `gateway_app_id = "any"` disables gateway identity pinning | Open | Not a production vulnerability | `gateway_app_id` is KMS contract configuration and is publicly auditable. Production deployments must not use `"any"`. No code fix was applied | +| [#608](https://github.com/Dstack-TEE/dstack/issues/608) `auth_api.type = "dev"` allows all authorization | Open | Not a production vulnerability | Dev auth is measured runtime configuration, not a production mode. Production must use webhook/on-chain authorization. No code fix was applied | +| [#609](https://github.com/Dstack-TEE/dstack/issues/609) `quote_enabled = false` bypasses attestation | Open | Not a production vulnerability | The flag is measured in runtime configuration and should fail production attestation policy. No code fix was applied | +| [#610](https://github.com/Dstack-TEE/dstack/issues/610) Unauthenticated bootstrap endpoint can overwrite root keys | Closed | Not a production vulnerability | The bootstrap endpoint does not accept caller-supplied root key material. Root keys are generated server-side, and the operator chooses which result to publish. No code fix was applied | +| [#611](https://github.com/Dstack-TEE/dstack/issues/611) Unauthenticated `/finish` endpoint can shut down KMS onboard service | Closed | Not a production vulnerability | The onboard service is a short-lived setup flow. Premature shutdown causes operator retry, not persistent compromise or data loss. No code fix was applied | +| [#612](https://github.com/Dstack-TEE/dstack/issues/612) Gateway `register_cvm` prefers stale `app_info` over live attestation | Closed | Not a production vulnerability | Cert-embedded `app_info` is extracted from attestation and signed by KMS. Preferring it avoids redundant extraction and is not a trust bypass. No code fix was applied | +| [#613](https://github.com/Dstack-TEE/dstack/issues/613) 10-year default certificate validity undermines attestation freshness | Closed | Not a production vulnerability | RA-TLS certificates embed attestation evidence and verifiers validate that evidence during connection handling. Freshness policy belongs in verifier policy, not only certificate expiry. No code fix was applied | +| [#614](https://github.com/Dstack-TEE/dstack/issues/614) VMM `no_tee` flag allows launching VMs without TDX protection | Closed | Not a production vulnerability | `no_tee` VMs cannot produce valid TDX quotes and cannot join the production trust chain unless other development-only checks are also disabled. No code fix was applied | +| [#615](https://github.com/Dstack-TEE/dstack/issues/615) Host-supplied `sys_config` not measured but influences security-critical behavior | Closed | Not a production vulnerability | Network endpoints are not trust anchors. KMS, gateway, and PCCS trust decisions rely on cryptographic verification, not host-supplied URLs. No code fix was applied | +| [#616](https://github.com/Dstack-TEE/dstack/issues/616) Host-controlled Docker registry mirror enables image substitution attacks | Closed | Not a production vulnerability | Registry mirrors are untrusted transport. Digest-pinned image references and measured compose configuration protect against substitution. No code fix was applied | +| [#617](https://github.com/Dstack-TEE/dstack/issues/617) Guest agent exposes raw private keys to all local processes | Closed | Not a production vulnerability | dstack treats a CVM as one application trust domain. It does not provide per-container key isolation inside the same measured application. No code fix was applied | +| [#618](https://github.com/Dstack-TEE/dstack/issues/618) Disk encryption disableable via kernel cmdline, not measured in RTMR | Closed | Not a production vulnerability | The kernel command line is measured into RTMR2, so changing `dstack.storage_encrypted=false` changes attestation evidence. No code fix was applied | +| [#619](https://github.com/Dstack-TEE/dstack/issues/619) KMS `get_temp_ca_cert` returns temp CA private key without authentication | Closed | Duplicate | The report duplicates the private advisory response for the temp CA bootstrap flow | + +## Related security roadmap and hardening + +These issues affect security architecture, future verification behavior, operational hardening, or security documentation. They are intentionally separated from the report table because they are not vulnerability reports. + +| Issue | Status | Type | Scope | +| --- | --- | --- | --- | +| [#113](https://github.com/Dstack-TEE/dstack/issues/113) Alternative to RA-TLS | Open | Architecture roadmap | Tracks possible application-level attestation or pre-registration approaches | +| [#114](https://github.com/Dstack-TEE/dstack/issues/114) On-chain logs for KMS replication | Open | Auditability roadmap | Tracks transparency for KMS onboarding and replication events | +| [#115](https://github.com/Dstack-TEE/dstack/issues/115) Censorship resistance in the KMS | Open | Governance roadmap | Tracks how KMS instances should prove an up-to-date chain view after de-registration or policy changes | +| [#411](https://github.com/Dstack-TEE/dstack/issues/411) Adopt RFC 8785 JCS for canonical compose hash calculation | Open | Measurement roadmap | Tracks a possible future canonical hash scheme. Current raw-byte hashing is intentional and recorded in #550 | +| [#745](https://github.com/Dstack-TEE/dstack/issues/745) `secure_time: true` cannot sync because guest chrony lacks NTS | Open | Security feature bug | Tracks a secure-time boot failure. The fix is in [meta-dstack#76](https://github.com/Dstack-TEE/meta-dstack/pull/76) | +| [#746](https://github.com/Dstack-TEE/dstack/issues/746) Harden AMD SEV-SNP KDS collateral fetch | Open | Availability hardening | Tracks async client, timeout, and caching hardening for SNP KDS collateral fetch. Verification remains fail-closed | diff --git a/docs/security/security-best-practices.md b/docs/security/security-best-practices.md index e93ffb683..fc3fab67e 100644 --- a/docs/security/security-best-practices.md +++ b/docs/security/security-best-practices.md @@ -68,6 +68,23 @@ Example app-compose.json: **But keep in mind, even if you disable exposing app-compose.json, it is just hidden from the public API, the physical machine controller can still access it on the file system.** +## Do not use development trust settings in production + +Development settings are intentionally easy to audit, but they are not production-safe. A production deployment should satisfy all of the following: + +- KMS quote verification remains enabled. Do not deploy production KMS with `quote_enabled = false`. +- KMS authorization uses webhook/on-chain policy. Do not use `auth_api.type = "dev"` with real key material. +- The KMS contract pins a concrete gateway app id. Do not use `gateway_app_id = "any"` for production traffic. +- TEE quotes are evaluated by deployment policy, including TCB status and expected OS/application measurements. + +The KMS TLS listener may keep `rpc.tls.mutual.mandatory = false` because bootstrap, temp-CA bootstrap, and public metadata endpoints need to be reachable before a client has an RA-TLS certificate. `GetTempCaCert` returns temp CA private material for the bootstrap flow; treat it as bootstrap-sensitive. + +App key release and KMS key handover still require verified caller attestation from the RA-TLS client certificate. Certificate signing verifies the CSR signature and embedded attestation before signing. + +## Keep private material owner-only + +Secret-bearing files should be owner-only (`0600`) wherever possible, including app keys, decrypted env files, KMS root keys, gateway WireGuard/TLS keys, and ACME credentials. Preserve restrictive permissions when copying volumes, backing up `/etc/kms/certs`, or moving gateway and certbot state between hosts. Public issue [#606](https://github.com/Dstack-TEE/dstack/issues/606) tracks the remaining low-cost hardening work in dstack-managed file writes. + ## docker logs is public available by default Similarly, to facilitate App observability, docker logs are public by default. You can disable exposing docker logs by setting public_logs=false. diff --git a/docs/security/security-model.md b/docs/security/security-model.md index fc5ad8475..5c3bdf3fe 100644 --- a/docs/security/security-model.md +++ b/docs/security/security-model.md @@ -8,7 +8,7 @@ This document helps you evaluate whether dstack's security model fits your needs dstack removes the need to trust infrastructure operators. The cloud provider cannot read your memory, modify your code, or access your secrets. Network attackers cannot intercept your traffic because TLS terminates inside the TEE with keys fully controlled by the TEE (Zero Trust HTTPS). Docker registries cannot serve malicious images because the TEE verifies SHA256 digests before pulling. -The only thing you must trust is **TEE hardware** (currently Intel TDX, with AMD SEV support planned). You trust that the TEE provides genuine memory encryption and that quotes are signed by real hardware. For GPU workloads, you also trust **NVIDIA GPU hardware** and NVIDIA's Remote Attestation Service (NRAS). These are hardware-level trust assumptions. +The only thing you must trust is **TEE hardware**. Intel TDX is the production path. AMD SEV-SNP is available where the selected dstack OS image and host support it, but it is new and experimental. You trust that the TEE provides genuine memory encryption and that quotes are signed by real hardware. For GPU workloads, you also trust **NVIDIA GPU hardware** and NVIDIA's Remote Attestation Service (NRAS). These are hardware-level trust assumptions. Everything else is verifiable. @@ -134,6 +134,22 @@ The one case dstack does not leave to downstream is a genuinely invalid TCB: `dc > **Future work:** this will be refactored toward a grace-period model, where an out-of-date TCB is accepted for a bounded window after a new TCB level is published rather than being a binary downstream decision. +### Development modes are auditable, not production-safe + +dstack keeps several development switches as runtime or on-chain configuration rather than Cargo feature flags. Examples include KMS `quote_enabled = false`, `auth_api.type = "dev"`, and KMS contract `gateway_app_id = "any"`. These settings exist for local development and integration tests, not for production deployments. + +This is intentional. Runtime configuration that affects the trust boundary is visible in attestation measurements or public contract state. Cargo feature gates are not automatically more auditable because feature unification can enable a feature through a dependency graph, and the resulting runtime behavior is not represented as a measured deployment setting. + +Production verifiers should reject deployments that use these development settings. Operators should treat them the same way they treat debug-mode TEE quotes: useful for testing, invalid for production trust. + +### KMS mTLS is route-enforced for sensitive operations + +The KMS Rocket TLS listener permits connections without a client certificate because some bootstrap and public metadata endpoints must be reachable before a client has an RA-TLS certificate. That listener setting is not the authorization boundary for key material. + +App key release and KMS key handover require verified caller attestation from the RA-TLS client certificate. Certificate signing verifies the CSR signature and the attestation embedded in the CSR before signing. + +The unauthenticated or non-client-certificate surface includes bootstrap and temp-CA bootstrap material retrieval, env-encryption public-key retrieval, metadata, health, and metrics behavior documented for operators. `GetTempCaCert` returns temp CA private material for the bootstrap flow, so operators must treat it as bootstrap-sensitive rather than harmless public metadata. + ## Limitations ### Attestation proves identity, not correctness diff --git a/docs/tutorials/kms-build-configuration.md b/docs/tutorials/kms-build-configuration.md index 502346879..e93e83692 100644 --- a/docs/tutorials/kms-build-configuration.md +++ b/docs/tutorials/kms-build-configuration.md @@ -186,6 +186,10 @@ certs = "/etc/kms/certs/rpc.crt" # Mutual TLS (mTLS) Configuration [rpc.tls.mutual] ca_certs = "/etc/kms/certs/tmp-ca.crt" +# Keep the TLS listener optional because bootstrap/public endpoints must be +# reachable before a client has an RA-TLS certificate. Temp-CA bootstrap material +# is bootstrap-sensitive. Key-release RPCs still require verified caller +# attestation; certificate signing verifies CSR signature and attestation. mandatory = false # Core KMS Configuration @@ -221,7 +225,7 @@ EOF | `[rpc]` | `address` | RPC server bind address | | `[rpc]` | `port` | RPC server port (9100) | | `[core]` | `cert_dir` | Directory for certificates | -| `[core]` | `pccs_url` | Local PCCS via host bridge (`10.0.2.2`) for quote verification | +| `[core]` | `pccs_url` | PCCS endpoint for quote verification | | `[core.auth_api]` | `url` | Auth-eth webhook service URL | | `[core.onboard]` | `enabled` | Enable bootstrap/onboard mode | @@ -465,6 +469,10 @@ certs = "/etc/kms/certs/rpc.crt" # Mutual TLS (mTLS) Configuration [rpc.tls.mutual] ca_certs = "/etc/kms/certs/tmp-ca.crt" +# Keep the TLS listener optional because bootstrap/public endpoints must be +# reachable before a client has an RA-TLS certificate. Temp-CA bootstrap material +# is bootstrap-sensitive. Key-release RPCs still require verified caller +# attestation; certificate signing verifies CSR signature and attestation. mandatory = false # Core KMS Configuration @@ -621,7 +629,7 @@ cat /etc/kms/kms.toml | python3 -c "import sys, tomllib; tomllib.load(sys.stdin. ```bash # Source and verify environment source /etc/kms/auth-eth.env -echo "ETH_RPC_URL: ${ETH_RPC_URL:0:30}..." +test -n "$ETH_RPC_URL" && echo "ETH_RPC_URL is set" echo "KMS_CONTRACT_ADDR: $KMS_CONTRACT_ADDR" ``` diff --git a/docs/tutorials/kms-cvm-deployment.md b/docs/tutorials/kms-cvm-deployment.md index a133b3683..f86058c67 100644 --- a/docs/tutorials/kms-cvm-deployment.md +++ b/docs/tutorials/kms-cvm-deployment.md @@ -49,7 +49,7 @@ Before starting, ensure you have: > **Why SGX is required:** The KMS uses Intel SGX to generate TDX attestation quotes via the `local_key_provider`. SGX Auto MP Registration must be enabled in BIOS so your platform is registered with Intel's Provisioning Certification Service (PCS). Without this registration, KMS cannot generate valid attestation quotes, and bootstrap will fail. -> **Why local registry?** The KMS Docker image is cached in your [Local Docker Registry](/tutorial/local-docker-registry) for reliable, fast access from CVMs. The auth-eth service inside the container requires `ETH_RPC_URL` and `KMS_CONTRACT_ADDR` environment variables — these are passed via docker-compose, not baked into the image. +> **Why local registry?** The KMS Docker image is cached in your [Local Docker Registry](/tutorial/local-docker-registry) for reliable, fast access from CVMs. This deployment passes `ETH_RPC_URL` and `KMS_CONTRACT_ADDR` through docker-compose so you can change the RPC endpoint or contract address without rebuilding the image. ## What Gets Deployed @@ -167,6 +167,10 @@ configs: [rpc.tls.mutual] ca_certs = "/etc/kms/certs/tmp-ca.crt" + # Keep the TLS listener optional because bootstrap/public endpoints must be + # reachable before a client has an RA-TLS certificate. Temp-CA bootstrap + # material is bootstrap-sensitive. Key-release RPCs still require verified + # caller attestation; certificate signing verifies CSR signature and attestation. mandatory = false [core] @@ -277,7 +281,7 @@ export DSTACK_VMM_AUTH_PASSWORD=$(cat ~/.dstack/secrets/vmm-auth-token) - `--image dstack-0.5.7`: Guest image from VMM images directory - `--port tcp:0.0.0.0:9100:9100`: Maps host port 9100 to CVM port 9100 on all interfaces -> **Why `0.0.0.0` and not `127.0.0.1`?** Gateway CVMs use QEMU user-mode networking and reach the host via its public IP. If KMS is bound to localhost only, gateway CVMs cannot connect. KMS authentication uses TDX attestation, not network isolation, so public accessibility is safe. +> **Why `0.0.0.0` and not `127.0.0.1`?** Gateway CVMs use QEMU user-mode networking and reach the host via its public IP. If KMS is bound to localhost only, gateway CVMs cannot connect. KMS authorization uses TDX attestation and auth policy, not loopback binding. Expose only the required KMS port and monitor it as a public service. > **Note:** Do NOT use `--secure-time` flag - it causes CVM to hang during boot waiting for time sync. diff --git a/sdk/curl/api.md b/sdk/curl/api.md index b05a6717b..f040cbfa6 100644 --- a/sdk/curl/api.md +++ b/sdk/curl/api.md @@ -64,7 +64,7 @@ curl --unix-socket /var/run/dstack.sock -X POST \ ### 2. Get Key -Generates an ECDSA key using the k256 elliptic curve, derived from the application key, and returns both the key and its signature chain. Sutable for ETH key generation. +Generates a deterministic private key from the application key and returns both the key and its signature chain. Suitable for ETH key generation when using the default `secp256k1` algorithm. **Endpoint:** `/GetKey` @@ -72,9 +72,11 @@ Generates an ECDSA key using the k256 elliptic curve, derived from the applicati | Field | Type | Description | Example | |-------|------|-------------|----------| -| `path` | string | Path for the key | `"my/key/path"` | -| `purpose` | string | Purpose for the key. Can be any string. This is used in the signature chain. | `"signing"` | `"encryption"` | -| `algorithm` | string | Either `secp256k1` or `ed25519`. Defaults to `secp256k1` | `ed25519` | +| `path` | string | Path for the key. This is the domain separator for deterministic key material. | `"my/key/path"` | +| `purpose` | string | Purpose for the key. Can be any string. This is used in the signature chain and does not affect the private key bytes. | `"signing"` | +| `algorithm` | string | `secp256k1` (default), `k256` (alias), or `ed25519`. For compatibility, this selects how the same derived 32-byte material is interpreted; it does not domain-separate the derivation. | `ed25519` | + +Use algorithm-specific paths, such as `wallet/ethereum` and `wallet/solana`, when independent keys are required across algorithms. **Example:** ```bash diff --git a/sdk/go/README.md b/sdk/go/README.md index 43815a563..df916e7eb 100644 --- a/sdk/go/README.md +++ b/sdk/go/README.md @@ -21,7 +21,7 @@ dstack applications consist of: ### SDK Capabilities -- **Key Derivation**: Deterministic secp256k1 key generation for blockchain and Web3 applications +- **Key Derivation**: Deterministic key derivation for wallets, signing, encryption, and other application-specific secrets - **Remote Attestation**: TDX quote generation providing cryptographic proof of execution environment - **TLS Certificate Management**: Fresh certificate generation with optional RA-TLS support for secure connections - **Deployment Security**: Client-side encryption of sensitive environment variables ensuring secrets are only accessible to target TEE applications @@ -98,12 +98,12 @@ func main() { fmt.Println("App Name:", info.AppName) fmt.Println("TCB Info:", info.TcbInfo) - // Derive deterministic keys for blockchain applications + // Derive deterministic keys for application-specific secrets walletKey, err := client.GetKey(ctx, "wallet/ethereum", "mainnet", "secp256k1") if err != nil { log.Fatal(err) } - + keyBytes, _ := walletKey.DecodeKey() fmt.Println("Derived key (32 bytes):", hex.EncodeToString(keyBytes)) // secp256k1 private key fmt.Println("Signature chain:", walletKey.SignatureChain) // Authenticity proof @@ -114,13 +114,13 @@ func main() { "timestamp": time.Now().Unix(), "user_id": "alice", } - + jsonData, _ := json.Marshal(applicationData) quote, err := client.GetQuote(ctx, jsonData) if err != nil { log.Fatal(err) } - + fmt.Println("TDX Quote:", quote.Quote) fmt.Println("Event Log:", quote.EventLog) @@ -318,24 +318,16 @@ if err != nil { ```go import ( - "crypto/ed25519" "encoding/hex" - + "github.com/Dstack-TEE/dstack/sdk/go/dstack" ) -keyResult, err := client.GetKey(ctx, "solana/main", "wallet", "secp256k1") -if err != nil { - log.Fatal(err) -} - -// Standard keypair creation -keypair, err := dstack.ToSolanaKeypair(keyResult) +keyResult, err := client.GetKey(ctx, "solana/main", "wallet", "ed25519") if err != nil { log.Fatal(err) } -// Enhanced security with SHA256 hashing (recommended) secureKeypair, err := dstack.ToSolanaKeypairSecure(keyResult) if err != nil { log.Fatal(err) @@ -365,8 +357,8 @@ The SDK provides end-to-end encryption capabilities for securely transmitting se import ( "encoding/hex" "fmt" - "time" - + "log" + "github.com/Dstack-TEE/dstack/sdk/go/dstack" ) @@ -378,34 +370,45 @@ envVars := []dstack.EnvVar{ {Key: "WALLET_MNEMONIC", Value: "abandon abandon abandon..."}, } -// 2. Obtain encryption public key from KMS API (dstack-vmm or Phala Cloud) -// (HTTP request implementation depends on your HTTP client) -publicKey := "a1b2c3d4..." // From KMS API -signature := "e1f2g3h4..." // From KMS API +// 2. Obtain encryption public key from KMS API (dstack-vmm or Phala Cloud). +// HTTP request implementation depends on your HTTP client. +kmsResponse := struct { + PublicKey string `json:"public_key"` + SignatureV1 string `json:"signature_v1"` + Timestamp uint64 `json:"timestamp"` +}{ + // Fill these fields from /prpc/GetAppEnvEncryptPubKey?json. +} // 3. Verify KMS API authenticity to prevent man-in-the-middle attacks -publicKeyBytes, _ := hex.DecodeString(publicKey) -signatureBytes, _ := hex.DecodeString(signature) +publicKeyBytes, _ := hex.DecodeString(kmsResponse.PublicKey) +signatureBytes, _ := hex.DecodeString(kmsResponse.SignatureV1) // Prefer timestamped verification to prevent replay attacks. -timestamp := uint64(time.Now().Unix()) // should come from KMS API response -trustedPubkey, err := dstack.VerifyEnvEncryptPublicKeyWithTimestamp( +kmsIdentity, err := dstack.VerifyEnvEncryptPublicKeyWithTimestamp( publicKeyBytes, signatureBytes, "your-app-id-hex", - timestamp, + kmsResponse.Timestamp, nil, // use default freshness policy (max age 300s) ) -if err != nil || trustedPubkey == nil { - log.Fatal("KMS API provided untrusted encryption key") +if err != nil || kmsIdentity == nil { + log.Fatal("kms API provided untrusted encryption key") +} + +expectedKMSIdentity := "0x03..." // From the DstackKms contract or deployment config +actualKMSIdentity := string(kmsIdentity) +if actualKMSIdentity != expectedKMSIdentity { + log.Fatalf("unexpected KMS identity: got %s", actualKMSIdentity) } -fmt.Println("Verified KMS public key:", hex.EncodeToString(trustedPubkey)) +fmt.Println("Verified KMS identity:", actualKMSIdentity) -// Note: VerifyEnvEncryptPublicKey() is kept for legacy compatibility (without timestamp check). +// VerifyEnvEncryptPublicKey() is available only for explicit compatibility with +// older KMS builds. It does not provide timestamp replay protection. // 4. Encrypt environment variables for secure deployment -encryptedData, err := dstack.EncryptEnvVars(envVars, publicKey) +encryptedData, err := dstack.EncryptEnvVars(envVars, kmsResponse.PublicKey) if err != nil { log.Fatal(err) } @@ -422,7 +425,7 @@ fmt.Println("Encrypted payload:", encryptedData) The SDK implements secure key derivation using: - **Deterministic Generation**: Keys are derived using HMAC-based Key Derivation Function (HKDF) -- **Application Isolation**: Each path produces unique keys, preventing cross-application access +- **Application Isolation**: Different `app_id` values derive different keys even with the same path - **Signature Verification**: All derived keys include cryptographic proof of origin - **TEE Protection**: Master keys never leave the secure enclave @@ -547,36 +550,39 @@ Retrieves comprehensive information about the TEE instance. - `EventLog`: Boot and runtime events - `AppCert`: Application certificate in PEM format -##### `GetKey(ctx context.Context, path string, purpose string) (*GetKeyResponse, error)` +##### `GetKey(ctx context.Context, path string, purpose string, algorithm string) (*GetKeyResponse, error)` -Derives a deterministic secp256k1/K256 private key for blockchain and Web3 applications. This is the primary method for obtaining cryptographic keys for wallets, signing, and other deterministic key scenarios. +Derives deterministic private key material for wallets, signing, encryption, stable service identities, and other application-specific secrets. **Parameters:** - `path`: Unique identifier for key derivation (e.g., `"wallet/ethereum"`, `"signing/solana"`) -- `purpose`: Additional context for key usage (default: `""`) +- `purpose`: Included in the signature-chain message; does not affect the private key bytes +- `algorithm`: `"secp256k1"` (default behavior), `"k256"` (alias), or `"ed25519"` **Returns:** `GetKeyResponse` -- `Key`: 32-byte secp256k1 private key as hex string (suitable for Ethereum, Bitcoin, Solana, etc.) +- `Key`: 32-byte private key material as a hex string - `SignatureChain`: Array of cryptographic signatures proving key authenticity **Key Characteristics:** -- **Deterministic**: Same path + purpose always generates identical key +- **Deterministic**: Same path always generates identical raw key material for the same app - **Isolated**: Different paths produce cryptographically independent keys -- **Blockchain-Ready**: Compatible with secp256k1 curve (Ethereum, Bitcoin, Solana) +- **Blockchain-Ready**: Use `secp256k1` for Ethereum and Bitcoin-style signing; use `ed25519` with a Solana-specific path for independent Solana keys - **Verifiable**: Signature chain proves key was derived inside genuine TEE +For compatibility, `algorithm` selects how the same derived 32-byte material is interpreted; it does not domain-separate the derivation. Use algorithm-specific paths when independent keys are required. + **Use Cases:** -- Cryptocurrency wallets -- Transaction signing -- DeFi protocol interactions -- NFT operations +- Stable service identity keys +- Application signing keys +- Encryption key seeds +- Cryptocurrency wallets and transaction signing - Any scenario requiring consistent, reproducible keys ```go // Examples of deterministic key derivation ethWallet, _ := client.GetKey(ctx, "wallet/ethereum", "mainnet", "secp256k1") -btcWallet, _ := client.GetKey(ctx, "wallet/bitcoin", "mainnet") -solWallet, _ := client.GetKey(ctx, "wallet/solana", "mainnet") +btcWallet, _ := client.GetKey(ctx, "wallet/bitcoin", "mainnet", "secp256k1") +solWallet, _ := client.GetKey(ctx, "wallet/solana", "mainnet", "ed25519") // Same path always returns same key key1, _ := client.GetKey(ctx, "my-app/signing", "", "secp256k1") @@ -695,25 +701,37 @@ Verify the authenticity of encryption public keys provided by KMS APIs: ```go import ( "encoding/hex" - "time" + "fmt" + "log" + "github.com/Dstack-TEE/dstack/sdk/go/dstack" ) -// Example: Verify KMS-provided encryption key -publicKey, _ := hex.DecodeString("e33a1832c6562067ff8f844a61e51ad051f1180b66ec2551fb0251735f3ee90a") -signature, _ := hex.DecodeString("8542c49081fbf4e03f62034f13fbf70630bdf256a53032e38465a27c36fd6bed7a5e7111652004aef37f7fd92fbfc1285212c4ae6a6154203a48f5e16cad2cef00") +// Example: Verify a KMS response from /prpc/GetAppEnvEncryptPubKey?json +kmsResponse := struct { + PublicKey string `json:"public_key"` + SignatureV1 string `json:"signature_v1"` + Timestamp uint64 `json:"timestamp"` +}{ + // Fill these fields from the KMS API response. +} +publicKey, _ := hex.DecodeString(kmsResponse.PublicKey) +signature, _ := hex.DecodeString(kmsResponse.SignatureV1) appID := "0000000000000000000000000000000000000000" -timestamp := uint64(time.Now().Unix()) // should come from KMS API response -kmsIdentity, err := dstack.VerifyEnvEncryptPublicKeyWithTimestamp(publicKey, signature, appID, timestamp, nil) +kmsIdentity, err := dstack.VerifyEnvEncryptPublicKeyWithTimestamp(publicKey, signature, appID, kmsResponse.Timestamp, nil) -if err == nil && kmsIdentity != nil { - fmt.Println("Trusted KMS identity:", hex.EncodeToString(kmsIdentity)) - // Safe to use the public key for encryption -} else { - fmt.Println("KMS signature verification failed") - // Potential man-in-the-middle attack +if err != nil || kmsIdentity == nil { + log.Fatal("kms signature verification failed") } + +expectedKMSIdentity := "0x03..." // From the DstackKms contract or deployment config +actualKMSIdentity := string(kmsIdentity) +if actualKMSIdentity != expectedKMSIdentity { + log.Fatalf("unexpected KMS identity: got %s", actualKMSIdentity) +} + +fmt.Println("Trusted KMS identity:", actualKMSIdentity) ``` ## Security Best Practices @@ -734,9 +752,9 @@ if err == nil && kmsIdentity != nil { - Implement proper certificate validation 4. **Error Handling** - - Handle cryptographic operation failures gracefully + - Fail closed on security-critical cryptographic errors - Log security events for monitoring - - Implement fallback mechanisms where appropriate + - Avoid fallback behavior that weakens verification or key isolation ## Migration Guide @@ -744,7 +762,7 @@ if err == nil && kmsIdentity != nil { The legacy client mixed two different use cases that have now been properly separated: -1. **`GetKey()`**: Deterministic key derivation for Web3/blockchain (secp256k1) +1. **`GetKey()`**: Deterministic key derivation for application-specific secrets 2. **`GetTlsKey()`**: Random TLS certificate generation for HTTPS/SSL ### From TappdClient to DstackClient @@ -778,12 +796,12 @@ client := dstack.NewDstackClient() **Step 2: Update Method Calls** ```go -// For deterministic keys (most common) +// For deterministic application keys (most common) // Before: TappdClient methods keyResult, _ := client.DeriveKey(ctx, "wallet") // After: DstackClient methods -keyResult, _ := client.GetKey(ctx, "wallet", "ethereum") +keyResult, _ := client.GetKey(ctx, "wallet/ethereum", "ethereum", "secp256k1") // For TLS certificates // Before: DeriveKey with TLS options @@ -805,7 +823,7 @@ tlsCert, _ := client.GetTlsKey(ctx, dstack.TlsKeyOptions{ - [ ] **Client Code Updates:** - [ ] Replace `tappd.NewTappdClient()` with `dstack.NewDstackClient()` - [ ] Replace `DeriveKey()` calls with appropriate method: - - [ ] `GetKey()` for Web3/blockchain keys (deterministic) + - [ ] `GetKey()` for deterministic application keys - [ ] `GetTlsKey()` for TLS certificates (random) - [ ] Replace `TdxQuote()` calls with `GetQuote()` - [ ] **SECURITY CRITICAL**: Update blockchain integration functions: diff --git a/sdk/js/README.md b/sdk/js/README.md index ebd13de68..20cd40654 100644 --- a/sdk/js/README.md +++ b/sdk/js/README.md @@ -49,7 +49,7 @@ const client = new DstackClient('/run/dstack/dstack.sock') // custom path ### `getKey(path?, purpose?, algorithm?)` -Derive a deterministic key. Same `(app_id, path, purpose, algorithm)` always returns the same key; different apps deriving on the same path get different keys. +Derive a deterministic key. The same `(app_id, path)` returns the same raw key material; different apps deriving on the same path get different keys. ```typescript const eth = await client.getKey('wallet/ethereum') // secp256k1 (default) @@ -58,7 +58,7 @@ const sol = await client.getKey('wallet/solana', 'mainnet', 'ed25519') // ed25 Returns `{ key: Uint8Array, signature_chain: Uint8Array[] }`. The signature chain proves the key was derived inside a genuine TEE. -`algorithm`: `'secp256k1'` (default), `'k256'` (alias), or `'ed25519'`. ed25519 requires guest agent ≥ 0.5.7. +`purpose` is included in the signature-chain message and does not affect the private key bytes. `algorithm` selects how the derived 32-byte material is interpreted: `'secp256k1'` (default), `'k256'` (alias), or `'ed25519'`. It does not domain-separate the derivation, so use algorithm-specific paths such as `wallet/ethereum` and `wallet/solana` when those keys must be independent. ed25519 requires guest agent ≥ 0.5.7. ### `getTlsKey(options?)` @@ -175,7 +175,7 @@ const wallet = createWalletClient({ account, chain: mainnet, transport: http() } ```typescript import { toKeypairSecure } from '@phala/dstack-sdk/solana' -const key = await client.getKey('wallet/solana') +const key = await client.getKey('wallet/solana', 'mainnet', 'ed25519') const keypair = toKeypairSecure(key) console.log(keypair.publicKey.toBase58()) ``` @@ -208,7 +208,6 @@ The full deployment flow mirrors `vmm-cli.py`: fetch the env-encrypt public key ```typescript import { verifyEnvEncryptPublicKey, - verifyEnvEncryptPublicKeyLegacy, } from '@phala/dstack-sdk' import { encryptEnvVars, type EnvVar } from '@phala/dstack-sdk/encrypt-env-vars' @@ -220,26 +219,21 @@ const response = await fetch(`${kmsUrl}/prpc/GetAppEnvEncryptPubKey?json`, { const publicKey = Buffer.from(response.public_key, 'hex') -// Prefer v1 (timestamp-protected against replay) -let signer = response.signature_v1 - ? verifyEnvEncryptPublicKey( - publicKey, - Buffer.from(response.signature_v1, 'hex'), - appId, - BigInt(response.timestamp), - ) - : null - -// Fall back to legacy signature on older KMS -if (!signer && response.signature) { - signer = verifyEnvEncryptPublicKeyLegacy( - publicKey, - Buffer.from(response.signature, 'hex'), - appId, - ) +if (!response.signature_v1 || response.timestamp === undefined) { + throw new Error('kms response missing timestamped signature') } -if (!signer) throw new Error('KMS signature did not verify') +const signer = verifyEnvEncryptPublicKey( + publicKey, + Buffer.from(response.signature_v1, 'hex'), + appId, + BigInt(response.timestamp), +) + +if (!signer) throw new Error('kms signature did not verify') + +const trustedSigners = new Set(['0x...']) // From the DstackKms contract or deployment config +if (!trustedSigners.has(signer)) throw new Error(`unexpected KMS signer: ${signer}`) const envs: EnvVar[] = [ { key: 'DATABASE_URL', value: 'postgresql://…' }, @@ -248,7 +242,7 @@ const envs: EnvVar[] = [ const encrypted = await encryptEnvVars(envs, response.public_key) ``` -Verify functions return the signer's compressed public key (hex) on success, or `null` on failure. Check the signer against your trusted-signer whitelist before encrypting. +Verify functions return the signer's compressed public key (hex) on success, or `null` on failure. `verifyEnvEncryptPublicKeyLegacy` is available only for deployments that explicitly support older KMS builds without `signature_v1`; it does not provide timestamp replay protection and should not be used for new deployments. ## Compatibility diff --git a/sdk/python/README.md b/sdk/python/README.md index 01ec56442..04cc88c10 100644 --- a/sdk/python/README.md +++ b/sdk/python/README.md @@ -63,7 +63,7 @@ ed_key = client.get_key('signing/key', algorithm='ed25519') **Parameters:** - `path` (optional): Key derivation path. Defaults to `""` (root). - `purpose` (optional): Included in the signature chain message; does not affect the derived key. -- `algorithm` (optional): `'secp256k1'` (default) or `'ed25519'`. +- `algorithm` (optional): `'secp256k1'` (default) or `'ed25519'`. For compatibility, this selects how the same derived 32-byte material is interpreted; it does not domain-separate the derivation. Use algorithm-specific paths when independent keys are required. **Returns:** `GetKeyResponse` - `key`: Hex-encoded private key @@ -239,7 +239,7 @@ print(account.address) ```python from dstack_sdk.solana import to_keypair_secure -key = client.get_key('wallet/solana') +key = client.get_key('wallet/solana', purpose='mainnet', algorithm='ed25519') keypair = to_keypair_secure(key) print(keypair.pubkey()) ``` @@ -260,7 +260,6 @@ The KMS returns a fresh X25519 public key (with a secp256k1 signature) that you from dstack_sdk import ( encrypt_env_vars, verify_env_encrypt_public_key, - verify_env_encrypt_public_key_legacy, EnvVar, ) @@ -272,15 +271,7 @@ signer = verify_env_encrypt_public_key( timestamp=timestamp, ) if signer is None: - # Fallback for older KMS builds that only emit the unprotected legacy - # signature. Vulnerable to replay; warn loudly if you must use it. - signer = verify_env_encrypt_public_key_legacy( - public_key=public_key_bytes, - signature=legacy_signature_bytes, - app_id=app_id_hex, - ) - if signer is None: - raise RuntimeError('invalid KMS env-encrypt public key') + raise RuntimeError('invalid KMS env-encrypt public key') # Always compare the recovered signer against a known-good KMS signer # address, obtained out-of-band from the DstackKms contract or your @@ -303,6 +294,8 @@ encrypted = await encrypt_env_vars(env_vars, public_key_hex) `verify_env_encrypt_public_key` returns the recovered compressed secp256k1 signer (`0x`-prefixed hex) on success, or `None` for any failure (bad length, expired/future timestamp, malformed `app_id`, invalid signature). The default `max_age_seconds` is 300; pass a larger value if your deployment workflow legitimately holds the response longer. +`verify_env_encrypt_public_key_legacy` remains available only for deployments that explicitly support older KMS builds without `signature_v1`. It does not provide timestamp replay protection and should not be used for new deployments. + ### Calculate Compose Hash ```python diff --git a/sdk/rust/README.md b/sdk/rust/README.md index cc5761b38..3fe8b2ace 100644 --- a/sdk/rust/README.md +++ b/sdk/rust/README.md @@ -56,6 +56,8 @@ let testnet_key = client.get_key(Some("wallet/eth/testnet".to_string()), None).a - `path`: Key derivation path (determines the key) - `purpose` (optional): Included in signature chain message, does not affect the derived key +The Rust SDK currently requests the default `secp256k1` key material. Use distinct paths when keys must be independent. + **Returns:** `GetKeyResponse` - `key`: Hex-encoded private key - `signature_chain`: Signatures proving the key was derived in a genuine TEE