diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 58e709e2b..da67c90a6 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -30,6 +30,3 @@ jobs: - name: Cargo fmt check run: cargo fmt --check --all - - - name: Run tests - run: ./run-tests.sh diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 000000000..016e32026 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,73 @@ +# SPDX-FileCopyrightText: © 2025 Phala Network +# +# SPDX-License-Identifier: Apache-2.0 + +name: Tests + +on: + push: + branches: [ master, next, dev-* ] + pull_request: + branches: [ master, next, dev-* ] + +env: + CARGO_TERM_COLOR: always + +jobs: + rust-tests: + name: Rust tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: 1.92.0 + + - name: Run tests + run: ./run-tests.sh + + auth-eth-tests: + name: auth-eth tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + cache: 'npm' + cache-dependency-path: kms/auth-eth/package-lock.json + + - name: Install dependencies + run: | + cd kms/auth-eth + npm ci + + - name: Run tests + run: | + cd kms/auth-eth + npm test + + kms-e2e-tests: + name: KMS E2E tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: 1.92.0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + cache: 'npm' + cache-dependency-path: kms/auth-eth/package-lock.json + + - name: Run KMS E2E tests + run: bash kms/e2e/run-e2e.sh diff --git a/Cargo.lock b/Cargo.lock index 55cc3bf24..5a88ca1f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1418,6 +1418,16 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "chrono-tz" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6139a8597ed92cf816dfb33f5dd6cf0bb93a6adc938f11039f371bc5bcd26c3" +dependencies = [ + "chrono", + "phf", +] + [[package]] name = "cipher" version = "0.3.0" @@ -1941,8 +1951,7 @@ checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "dcap-qvl" version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67e7842b81018f3b991dc65ec0a95ff347332de58478c4ac43459095af00cc89" +source = "git+https://github.com/Phala-Network/dcap-qvl?branch=policy#4c2e3b36c8faec5c4adad303d73de9d6f8aeda0c" dependencies = [ "anyhow", "asn1_der", @@ -1955,11 +1964,13 @@ dependencies = [ "der", "derive_more 2.1.1", "futures", + "getrandom 0.2.17", "hex", "log", "p256", "parity-scale-codec", "pem", + "regorus", "reqwest", "ring", "rustls-pki-types", @@ -2406,6 +2417,7 @@ dependencies = [ "anyhow", "chrono", "clap", + "dcap-qvl", "dstack-guest-agent-rpc", "dstack-kms-rpc", "dstack-mr", @@ -3837,7 +3849,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.2", + "windows-core", ] [[package]] @@ -4271,6 +4283,27 @@ dependencies = [ "tokio", ] +[[package]] +name = "kms-e2e-client" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "dstack-attest", + "dstack-guest-agent-rpc", + "dstack-kms-rpc", + "hex", + "http-client", + "parity-scale-codec", + "ra-rpc", + "ra-tls", + "serde", + "serde_json", + "tokio", + "tracing", + "tracing-subscriber", +] + [[package]] name = "kqueue" version = "1.1.1" @@ -6035,6 +6068,26 @@ version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +[[package]] +name = "regorus" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "656c9768f1d2113590ebc05e2e342a9f76baa97a445f2928f24eec9ae1fb14ac" +dependencies = [ + "anyhow", + "chrono", + "chrono-tz", + "lazy_static", + "num-bigint", + "num-traits", + "regex", + "semver 1.0.27", + "serde", + "serde_json", + "spin", + "thiserror 2.0.18", +] + [[package]] name = "reqwest" version = "0.12.28" @@ -8417,7 +8470,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ "windows-collections", - "windows-core 0.61.2", + "windows-core", "windows-future", "windows-link 0.1.3", "windows-numerics", @@ -8429,7 +8482,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core 0.61.2", + "windows-core", ] [[package]] @@ -8441,21 +8494,8 @@ dependencies = [ "windows-implement", "windows-interface", "windows-link 0.1.3", - "windows-result 0.3.4", - "windows-strings 0.4.2", -] - -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link 0.2.1", - "windows-result 0.4.1", - "windows-strings 0.5.1", + "windows-result", + "windows-strings", ] [[package]] @@ -8464,7 +8504,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ - "windows-core 0.61.2", + "windows-core", "windows-link 0.1.3", "windows-threading", ] @@ -8509,7 +8549,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-core 0.61.2", + "windows-core", "windows-link 0.1.3", ] @@ -8522,15 +8562,6 @@ dependencies = [ "windows-link 0.1.3", ] -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link 0.2.1", -] - [[package]] name = "windows-strings" version = "0.4.2" @@ -8540,15 +8571,6 @@ dependencies = [ "windows-link 0.1.3", ] -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link 0.2.1", -] - [[package]] name = "windows-sys" version = "0.48.0" diff --git a/Cargo.toml b/Cargo.toml index 1529a652e..a1b7811c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,7 @@ members = [ "no_std_check", "size-parser", "port-forward", + "kms/e2e", ] resolver = "2" @@ -177,7 +178,7 @@ url = "2.5" # Cryptography/Security aes-gcm = "0.10.3" curve25519-dalek = "4.1.3" -dcap-qvl = "0.3.10" +dcap-qvl = { git = "https://github.com/Phala-Network/dcap-qvl", branch = "policy" } elliptic-curve = { version = "0.13.8", features = ["pkcs8"] } getrandom = "0.3.1" hkdf = "0.12.4" diff --git a/dstack-attest/src/attestation.rs b/dstack-attest/src/attestation.rs index 6250c2dd8..26a1a4a9a 100644 --- a/dstack-attest/src/attestation.rs +++ b/dstack-attest/src/attestation.rs @@ -10,7 +10,8 @@ use anyhow::{anyhow, bail, Context, Result}; use cc_eventlog::{RuntimeEvent, TdxEvent}; use dcap_qvl::{ quote::{EnclaveReport, Quote, Report, TDReport10, TDReport15}, - verify::VerifiedReport as TdxVerifiedReport, + verify::{QuoteVerificationResult, VerifiedReport as TdxVerifiedReport}, + Policy, SimplePolicy, TcbStatus, }; #[cfg(feature = "quote")] use dstack_types::SysConfig; @@ -195,22 +196,38 @@ impl QuoteContentType<'_> { } #[allow(clippy::large_enum_variant)] -/// Represents a verified attestation +/// Represents a verified attestation report. +/// +/// For TDX, holds the full [`QuoteVerificationResult`] so that callers +/// can apply additional (stricter) policies via [`QuoteVerificationResult::validate`] +/// before converting to a [`VerifiedReport`](TdxVerifiedReport). #[derive(Clone)] pub enum DstackVerifiedReport { - DstackTdx(TdxVerifiedReport), + DstackTdx(QuoteVerificationResult), DstackGcpTdx, DstackNitroEnclave, } impl DstackVerifiedReport { - pub fn tdx_report(&self) -> Option<&TdxVerifiedReport> { + /// Get the TDX [`QuoteVerificationResult`] for further policy validation. + pub fn tdx_qvr(&self) -> Option<&QuoteVerificationResult> { match self { - DstackVerifiedReport::DstackTdx(report) => Some(report), + DstackVerifiedReport::DstackTdx(qvr) => Some(qvr), DstackVerifiedReport::DstackGcpTdx => None, DstackVerifiedReport::DstackNitroEnclave => None, } } + + /// Consume self and apply a policy to the TDX quote, returning a [`TdxVerifiedReport`]. + /// + /// Returns `None` for non-TDX reports. + pub fn validate_tdx(self, policy: &dyn Policy) -> Result> { + match self { + DstackVerifiedReport::DstackTdx(qvr) => Ok(Some(qvr.validate(policy)?)), + DstackVerifiedReport::DstackGcpTdx => Ok(None), + DstackVerifiedReport::DstackNitroEnclave => Ok(None), + } + } } /// Represents a verified attestation @@ -375,7 +392,7 @@ impl GetDeviceId for () { impl GetDeviceId for DstackVerifiedReport { fn get_devide_id(&self) -> Vec { match self { - DstackVerifiedReport::DstackTdx(tdx_report) => tdx_report.ppid.to_vec(), + DstackVerifiedReport::DstackTdx(qvr) => qvr.ppid().to_vec(), DstackVerifiedReport::DstackGcpTdx => Vec::new(), DstackVerifiedReport::DstackNitroEnclave => Vec::new(), } @@ -638,12 +655,12 @@ impl Attestation { pub async fn verify_with_time( self, pccs_url: Option<&str>, - _now: Option, + now: Option, ) -> Result { let report = match &self.quote { AttestationQuote::DstackTdx(q) => { - let report = self.verify_tdx(pccs_url, &q.quote).await?; - DstackVerifiedReport::DstackTdx(report) + let qvr = self.verify_tdx(pccs_url, &q.quote, now).await?; + DstackVerifiedReport::DstackTdx(qvr) } AttestationQuote::DstackGcpTdx | AttestationQuote::DstackNitroEnclave => { bail!("Unsupported attestation mode: {:?}", self.quote.mode()); @@ -682,7 +699,12 @@ impl Attestation { self.verify_with_time(pccs_url, None).await } - async fn verify_tdx(&self, pccs_url: Option<&str>, quote: &[u8]) -> Result { + async fn verify_tdx( + &self, + pccs_url: Option<&str>, + quote: &[u8], + now: Option, + ) -> Result { let mut pccs_url = Cow::Borrowed(pccs_url.unwrap_or_default()); if pccs_url.is_empty() { // try to read from PCCS_URL env var @@ -691,13 +713,27 @@ impl Attestation { Err(_) => Cow::Borrowed(""), }; } - let tdx_report = - dcap_qvl::collateral::get_collateral_and_verify(quote, Some(pccs_url.as_ref())) - .await - .context("Failed to get collateral")?; - validate_tcb(&tdx_report)?; - - let td_report = tdx_report.report.as_td10().context("no td report")?; + let now_secs = now + .unwrap_or_else(SystemTime::now) + .duration_since(SystemTime::UNIX_EPOCH) + .context("system time before epoch")? + .as_secs(); + let qvr = dcap_qvl::collateral::get_collateral_and_verify(quote, Some(pccs_url.as_ref())) + .await + .context("Failed to get collateral")?; + + // Baseline policy validation (business layer can apply stricter policies on the returned QVR) + let supplemental = qvr + .supplemental() + .context("Failed to build supplemental data")?; + default_policy(now_secs) + .validate(&supplemental) + .context("TCB policy validation failed")?; + + // Validate TEE attributes (debug mode, signer, etc.) + validate_tcb(&supplemental.report)?; + + let td_report = supplemental.report.as_td10().context("no td report")?; let replayed_rtmr = self.replay_runtime_events::(None); if replayed_rtmr != td_report.rt_mr3 { bail!( @@ -710,12 +746,12 @@ impl Attestation { if td_report.report_data != self.report_data[..] { bail!("tdx report_data mismatch"); } - Ok(tdx_report) + Ok(qvr) } } -/// Validate the TCB attributes -pub fn validate_tcb(report: &TdxVerifiedReport) -> Result<()> { +/// Validate the TEE report attributes (debug mode, signer, etc.) +pub fn validate_tcb(report: &Report) -> Result<()> { fn validate_td10(report: &TDReport10) -> Result<()> { let is_debug = report.td_attributes[0] & 0x01 != 0; if is_debug { @@ -739,13 +775,39 @@ pub fn validate_tcb(report: &TdxVerifiedReport) -> Result<()> { } Ok(()) } - match &report.report { + match report { Report::TD15(report) => validate_td15(report), Report::TD10(report) => validate_td10(report), Report::SgxEnclave(report) => validate_sgx(report), } } +/// Default TCB policy for dstack attestation. +/// +/// Accepts common non-critical TCB statuses (`SWHardeningNeeded`, `ConfigurationNeeded`, +/// `ConfigurationAndSWHardeningNeeded`, `OutOfDate`, `OutOfDateConfigurationNeeded`) +/// with a 90-day collateral grace period, and allows SMT (hyperthreading). +/// +/// Rejects quotes carrying high-severity advisories that directly compromise TEE guarantees: +/// - INTEL-SA-01397: TDX migration → debuggable TD (CVSS 8.4, full TDX compromise) +/// - INTEL-SA-01367: OOB write in SGX/TDX memory subsystem (CVSS 7.2) +/// - INTEL-SA-01314: OOB write in TDX module +/// - INTEL-SA-00837: Unauthorized error injection in SGX/TDX (CVSS 7.2) +pub fn default_policy(now_secs: u64) -> SimplePolicy { + use core::time::Duration; + SimplePolicy::strict(now_secs) + .allow_status(TcbStatus::OutOfDate) + .platform_grace_period(Duration::from_secs(30 * 24 * 3600)) + .qe_grace_period(Duration::from_secs(30 * 24 * 3600)) + .allow_smt(true) + .allow_dynamic_platform(true) + .allow_cached_keys(true) + .reject_advisory("INTEL-SA-01397") + .reject_advisory("INTEL-SA-01367") + .reject_advisory("INTEL-SA-01314") + .reject_advisory("INTEL-SA-00837") +} + /// Information about the app extracted from event log #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AppInfo { diff --git a/dstack-util/src/host_api.rs b/dstack-util/src/host_api.rs index 6bcc270a6..b08340e4a 100644 --- a/dstack-util/src/host_api.rs +++ b/dstack-util/src/host_api.rs @@ -12,7 +12,7 @@ use host_api::{ client::{new_client, DefaultClient}, Notification, }; -use ra_tls::attestation::validate_tcb; +use ra_tls::attestation::{default_policy, validate_tcb}; use sodiumbox::{generate_keypair, open_sealed_box, PUBLICKEYBYTES}; use tracing::warn; @@ -94,13 +94,20 @@ impl HostApi { .map_err(|err| anyhow!("Failed to get sealing key: {err:?}"))?; // verify the key provider quote + let now_secs = std::time::SystemTime::now() + .duration_since(std::time::SystemTime::UNIX_EPOCH) + .context("system time before epoch")? + .as_secs(); + let policy = default_policy(now_secs); let verified_report = dcap_qvl::collateral::get_collateral_and_verify( &provision.provider_quote, self.pccs_url.as_deref(), ) .await - .context("Failed to get quote collateral")?; - validate_tcb(&verified_report)?; + .context("Failed to get quote collateral")? + .validate(&policy) + .context("TCB policy validation failed")?; + validate_tcb(&verified_report.report)?; let sgx_report = verified_report .report .as_sgx() diff --git a/kms/Cargo.toml b/kms/Cargo.toml index bc33bc6a7..ed7c27720 100644 --- a/kms/Cargo.toml +++ b/kms/Cargo.toml @@ -47,6 +47,7 @@ tempfile.workspace = true serde-duration.workspace = true dstack-verifier = { workspace = true, default-features = false } dstack-mr.workspace = true +dcap-qvl.workspace = true [features] default = [] diff --git a/kms/auth-eth/contracts/DstackApp.sol b/kms/auth-eth/contracts/DstackApp.sol index a0093b0a9..6073e8e0f 100644 --- a/kms/auth-eth/contracts/DstackApp.sol +++ b/kms/auth-eth/contracts/DstackApp.sol @@ -8,6 +8,7 @@ pragma solidity ^0.8.22; import "./IAppAuth.sol"; import "./IAppAuthBasicManagement.sol"; +import "./IAppTcbPolicy.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; @@ -19,7 +20,8 @@ contract DstackApp is UUPSUpgradeable, ERC165Upgradeable, IAppAuth, - IAppAuthBasicManagement + IAppAuthBasicManagement, + IAppTcbPolicy { // Mapping of allowed compose hashes for this app mapping(bytes32 => bool) public allowedComposeHashes; @@ -33,6 +35,9 @@ contract DstackApp is // Mapping of allowed device IDs for this app mapping(bytes32 => bool) public allowedDeviceIds; + // TCB policy JSON string (opaque, interpreted by KMS) + string private _tcbPolicy; + // Additional events specific to DstackApp event UpgradesDisabled(); event AllowAnyDeviceSet(bool allowAny); @@ -88,6 +93,7 @@ contract DstackApp is return interfaceId == 0x1e079198 || // IAppAuth interfaceId == 0x8fd37527 || // IAppAuthBasicManagement + interfaceId == type(IAppTcbPolicy).interfaceId || super.supportsInterface(interfaceId); } @@ -143,6 +149,17 @@ contract DstackApp is return (true, ""); } + // Get the TCB policy JSON + function tcbPolicy() external view override returns (string memory) { + return _tcbPolicy; + } + + // Set the TCB policy JSON + function setTcbPolicy(string calldata policy) external override onlyOwner { + _tcbPolicy = policy; + emit TcbPolicySet(policy); + } + // Function to permanently disable upgrades function disableUpgrades() external onlyOwner { _upgradesDisabled = true; diff --git a/kms/auth-eth/contracts/DstackKms.sol b/kms/auth-eth/contracts/DstackKms.sol index 9a26ef4ff..8c6a0d5dd 100644 --- a/kms/auth-eth/contracts/DstackKms.sol +++ b/kms/auth-eth/contracts/DstackKms.sol @@ -7,6 +7,7 @@ pragma solidity ^0.8.22; import "./IAppAuth.sol"; +import "./IAppTcbPolicy.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; @@ -18,7 +19,8 @@ contract DstackKms is OwnableUpgradeable, UUPSUpgradeable, ERC165Upgradeable, - IAppAuth + IAppAuth, + IAppTcbPolicy { // Struct for KMS information struct KmsInfo { @@ -50,6 +52,9 @@ contract DstackKms is // DstackApp implementation contract address for factory deployment address public appImplementation; + // TCB policy JSON for KMS boot validation (opaque, interpreted by KMS) + string private _tcbPolicy; + // Events event AppRegistered(address appId); event KmsInfoSet(bytes k256Pubkey); @@ -96,6 +101,7 @@ contract DstackKms is { return interfaceId == 0x1e079198 || // IAppAuth + interfaceId == type(IAppTcbPolicy).interfaceId || super.supportsInterface(interfaceId); } @@ -256,6 +262,17 @@ contract DstackKms is return IAppAuth(bootInfo.appId).isAppAllowed(bootInfo); } + // Get the KMS TCB policy JSON + function tcbPolicy() external view override returns (string memory) { + return _tcbPolicy; + } + + // Set the KMS TCB policy JSON + function setTcbPolicy(string calldata policy) external override onlyOwner { + _tcbPolicy = policy; + emit TcbPolicySet(policy); + } + // Add storage gap for upgradeable contracts uint256[50] private __gap; } diff --git a/kms/auth-eth/contracts/IAppTcbPolicy.sol b/kms/auth-eth/contracts/IAppTcbPolicy.sol new file mode 100644 index 000000000..420b59bcb --- /dev/null +++ b/kms/auth-eth/contracts/IAppTcbPolicy.sol @@ -0,0 +1,44 @@ +/* + * SPDX-FileCopyrightText: © 2025 Phala Network + * + * SPDX-License-Identifier: Apache-2.0 + */ + +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; + +/** + * @title IAppTcbPolicy + * @notice Interface for contracts that provide a TCB (Trusted Computing Base) policy. + * @dev The policy is stored as an opaque JSON string interpreted by the KMS. + * The JSON format is versioned and supports multiple TEE types: + * + * { + * "version": 1, + * "intel_qal": [ + * {"environment": {"class_id": "..."}, "reference": {...}}, + * ... + * ] + * } + * + * Interface ID: computed from tcbPolicy() + */ +interface IAppTcbPolicy is IERC165 { + /// @notice Emitted when the TCB policy is updated + event TcbPolicySet(string policy); + + /** + * @notice Get the TCB policy JSON string + * @return The policy JSON, or empty string if no policy is set + */ + function tcbPolicy() external view returns (string memory); + + /** + * @notice Set the TCB policy JSON string + * @dev MUST emit TcbPolicySet event on success + * MUST revert if caller is not authorized + * @param policy The policy JSON string (empty to clear) + */ + function setTcbPolicy(string calldata policy) external; +} diff --git a/kms/auth-eth/src/ethereum.ts b/kms/auth-eth/src/ethereum.ts index 5b1385d98..551914aff 100644 --- a/kms/auth-eth/src/ethereum.ts +++ b/kms/auth-eth/src/ethereum.ts @@ -3,9 +3,10 @@ // SPDX-License-Identifier: Apache-2.0 import { ethers } from 'ethers'; -import { BootInfo, BootResponse } from './types'; +import { BootInfo, BootResponse, PolicyResponse } from './types'; import { DstackKms__factory } from '../typechain-types/factories/contracts/DstackKms__factory'; import { DstackKms } from '../typechain-types/contracts/DstackKms'; +import { IAppTcbPolicy__factory } from '../typechain-types/factories/contracts/IAppTcbPolicy__factory'; import { HardhatEthersProvider } from '@nomicfoundation/hardhat-ethers/internal/hardhat-ethers-provider'; export class EthereumBackend { @@ -31,6 +32,20 @@ export class EthereumBackend { return '0x' + hex; } + /** + * Try to read tcbPolicy() from a contract address. + * Returns empty string if the contract doesn't implement IAppTcbPolicy. + */ + private async tryReadTcbPolicy(address: string): Promise { + try { + const contract = IAppTcbPolicy__factory.connect(address, this.provider); + return await contract.tcbPolicy(); + } catch { + // Old contract without IAppTcbPolicy support + return ''; + } + } + async checkBoot(bootInfo: BootInfo, isKms: boolean): Promise { // Create boot info struct for contract call const bootInfoStruct = { @@ -52,6 +67,7 @@ export class EthereumBackend { } const [isAllowed, reason] = response; const gatewayAppId = await this.kmsContract.gatewayAppId(); + return { isAllowed, reason, @@ -68,6 +84,16 @@ export class EthereumBackend { return Number(chainId); } + async getAppPolicy(appId: string): Promise { + const tcbPolicy = await this.tryReadTcbPolicy(appId); + return { tcbPolicy }; + } + + async getKmsPolicy(): Promise { + const tcbPolicy = await this.tryReadTcbPolicy(await this.kmsContract.getAddress()); + return { tcbPolicy }; + } + async getAppImplementation(): Promise { return await this.kmsContract.appImplementation(); } diff --git a/kms/auth-eth/src/server.ts b/kms/auth-eth/src/server.ts index 16f8cbc87..196d04255 100644 --- a/kms/auth-eth/src/server.ts +++ b/kms/auth-eth/src/server.ts @@ -4,7 +4,7 @@ import fastify, { FastifyInstance } from 'fastify'; import { EthereumBackend } from './ethereum'; -import { BootInfo, BootResponse } from './types'; +import { BootInfo, BootResponse, PolicyResponse } from './types'; import { ethers } from 'ethers'; declare module 'fastify' { @@ -44,6 +44,15 @@ export async function build(): Promise { } }); + server.addSchema({ + $id: 'policyResponse', + type: 'object', + required: ['tcbPolicy'], + properties: { + tcbPolicy: { type: 'string' }, + } + }); + // Initialize backend const rpcUrl = process.env.ETH_RPC_URL || 'http://localhost:8545'; const kmsContractAddr = process.env.KMS_CONTRACT_ADDR || '0x0000000000000000000000000000000000000000'; @@ -115,5 +124,32 @@ export async function build(): Promise { } }); + // TCB policy endpoints + server.get<{ + Params: { appId: string }; + Reply: PolicyResponse; + }>('/policy/app/:appId', { + schema: { + response: { + 200: { $ref: 'policyResponse#' } + } + } + }, async (request, reply) => { + const appId = ethers.getAddress(request.params.appId); + return await server.ethereum.getAppPolicy(appId); + }); + + server.get<{ + Reply: PolicyResponse; + }>('/policy/kms', { + schema: { + response: { + 200: { $ref: 'policyResponse#' } + } + } + }, async (request, reply) => { + return await server.ethereum.getKmsPolicy(); + }); + return server; } diff --git a/kms/auth-eth/src/types.ts b/kms/auth-eth/src/types.ts index fd1a89143..370831245 100644 --- a/kms/auth-eth/src/types.ts +++ b/kms/auth-eth/src/types.ts @@ -20,4 +20,8 @@ export interface BootResponse { reason: string; } +export interface PolicyResponse { + tcbPolicy: string; +} + // Removed KMS_CONTRACT_ABI and APP_CONTRACT_ABI since we're using typechain types now diff --git a/kms/auth-eth/test/DstackApp.test.ts b/kms/auth-eth/test/DstackApp.test.ts index 6421402e3..d28f35de2 100644 --- a/kms/auth-eth/test/DstackApp.test.ts +++ b/kms/auth-eth/test/DstackApp.test.ts @@ -127,6 +127,51 @@ describe("DstackApp", function () { }); }); + describe("TCB policy (IAppTcbPolicy)", function () { + const testPolicy = '{"version":1,"intel_qal":["policy1"]}'; + + it("Should return empty string by default", async function () { + expect(await appAuth.tcbPolicy()).to.equal(""); + }); + + it("Should allow owner to set TCB policy", async function () { + await appAuth.setTcbPolicy(testPolicy); + expect(await appAuth.tcbPolicy()).to.equal(testPolicy); + }); + + it("Should emit TcbPolicySet event", async function () { + await expect(appAuth.setTcbPolicy(testPolicy)) + .to.emit(appAuth, "TcbPolicySet") + .withArgs(testPolicy); + }); + + it("Should allow clearing TCB policy with empty string", async function () { + await appAuth.setTcbPolicy(testPolicy); + await appAuth.setTcbPolicy(""); + expect(await appAuth.tcbPolicy()).to.equal(""); + }); + + it("Should prevent non-owners from setting TCB policy", async function () { + await expect( + appAuth.connect(user).setTcbPolicy(testPolicy) + ).to.be.revertedWithCustomError(appAuth, "OwnableUnauthorizedAccount"); + }); + + it("Should support IAppTcbPolicy interface (ERC-165)", async function () { + // IAppTcbPolicy interfaceId = tcbPolicy() ^ setTcbPolicy(string) + // We verify via supportsInterface + const iface = new ethers.Interface([ + "function tcbPolicy() view returns (string)", + "function setTcbPolicy(string)", + ]); + const interfaceId = + BigInt(iface.getFunction("tcbPolicy")!.selector) ^ + BigInt(iface.getFunction("setTcbPolicy")!.selector); + const id = "0x" + (interfaceId & BigInt("0xffffffff")).toString(16).padStart(8, "0"); + expect(await appAuth.supportsInterface(id)).to.be.true; + }); + }); + describe("Initialize with device and hash", function () { let appAuthWithData: DstackApp; const testDevice = ethers.randomBytes(32); @@ -251,3 +296,52 @@ describe("DstackApp", function () { }); }); }); + +describe("DstackKms TCB policy", function () { + let kmsContract: any; + let owner: SignerWithAddress; + let user: SignerWithAddress; + + beforeEach(async function () { + [owner, user] = await ethers.getSigners(); + kmsContract = await deployContract(hre, "DstackKms", [ + owner.address, + ethers.ZeroAddress, + ], true); + }); + + it("Should return empty string by default", async function () { + expect(await kmsContract.tcbPolicy()).to.equal(""); + }); + + it("Should allow owner to set TCB policy", async function () { + const policy = '{"version":1,"intel_qal":[]}'; + await kmsContract.setTcbPolicy(policy); + expect(await kmsContract.tcbPolicy()).to.equal(policy); + }); + + it("Should emit TcbPolicySet event", async function () { + const policy = '{"version":1}'; + await expect(kmsContract.setTcbPolicy(policy)) + .to.emit(kmsContract, "TcbPolicySet") + .withArgs(policy); + }); + + it("Should prevent non-owners from setting TCB policy", async function () { + await expect( + kmsContract.connect(user).setTcbPolicy("test") + ).to.be.revertedWithCustomError(kmsContract, "OwnableUnauthorizedAccount"); + }); + + it("Should support IAppTcbPolicy interface (ERC-165)", async function () { + const iface = new ethers.Interface([ + "function tcbPolicy() view returns (string)", + "function setTcbPolicy(string)", + ]); + const interfaceId = + BigInt(iface.getFunction("tcbPolicy")!.selector) ^ + BigInt(iface.getFunction("setTcbPolicy")!.selector); + const id = "0x" + (interfaceId & BigInt("0xffffffff")).toString(16).padStart(8, "0"); + expect(await kmsContract.supportsInterface(id)).to.be.true; + }); +}); diff --git a/kms/auth-eth/test/ethereum.test.ts b/kms/auth-eth/test/ethereum.test.ts index 83c0561f3..ba04c5dd6 100644 --- a/kms/auth-eth/test/ethereum.test.ts +++ b/kms/auth-eth/test/ethereum.test.ts @@ -81,4 +81,38 @@ describe('EthereumBackend', () => { expect(result.isAllowed).toBe(false); }); }); + + describe('getAppPolicy', () => { + it('should return empty tcbPolicy by default', async () => { + const result = await backend.getAppPolicy(appId); + expect(result.tcbPolicy).toBe(''); + }); + + it('should return set tcbPolicy', async () => { + const policy = '{"version":1,"intel_qal":["test-policy"]}'; + await appAuth.setTcbPolicy(policy); + const result = await backend.getAppPolicy(appId); + expect(result.tcbPolicy).toBe(policy); + }); + + it('should return empty string for address without IAppTcbPolicy', async () => { + const randomAddr = ethers.Wallet.createRandom().address; + const result = await backend.getAppPolicy(randomAddr); + expect(result.tcbPolicy).toBe(''); + }); + }); + + describe('getKmsPolicy', () => { + it('should return empty tcbPolicy by default', async () => { + const result = await backend.getKmsPolicy(); + expect(result.tcbPolicy).toBe(''); + }); + + it('should return set tcbPolicy', async () => { + const policy = '{"version":1,"intel_qal":[]}'; + await kmsContract.setTcbPolicy(policy); + const result = await backend.getKmsPolicy(); + expect(result.tcbPolicy).toBe(policy); + }); + }); }); diff --git a/kms/auth-eth/test/main.test.ts b/kms/auth-eth/test/main.test.ts index 799b50302..d6abf262c 100644 --- a/kms/auth-eth/test/main.test.ts +++ b/kms/auth-eth/test/main.test.ts @@ -10,7 +10,12 @@ import { BootInfo } from '../src/types'; jest.mock('../src/ethereum', () => { return { EthereumBackend: jest.fn().mockImplementation(() => ({ - checkBoot: jest.fn() + checkBoot: jest.fn(), + getAppPolicy: jest.fn(), + getKmsPolicy: jest.fn(), + getGatewayAppId: jest.fn().mockResolvedValue('0x1234'), + getChainId: jest.fn().mockResolvedValue(1), + getAppImplementation: jest.fn().mockResolvedValue('0x0000000000000000000000000000000000000000'), })) }; }); @@ -114,4 +119,79 @@ describe('Server', () => { expect(result.reason).toMatch(/Test backend error/); }); }); + + describe('GET /policy/app/:appId', () => { + it('should return tcbPolicy from backend', async () => { + const policy = '{"version":1,"intel_qal":["test"]}'; + app.ethereum.getAppPolicy = jest.fn().mockResolvedValue({ tcbPolicy: policy }); + + const response = await app.inject({ + method: 'GET', + url: '/policy/app/0x9012345678901234567890123456789012345678', + }); + + expect(response.statusCode).toBe(200); + const result = JSON.parse(response.payload); + expect(result.tcbPolicy).toBe(policy); + }); + + it('should return empty tcbPolicy when none set', async () => { + app.ethereum.getAppPolicy = jest.fn().mockResolvedValue({ tcbPolicy: '' }); + + const response = await app.inject({ + method: 'GET', + url: '/policy/app/0x9012345678901234567890123456789012345678', + }); + + expect(response.statusCode).toBe(200); + const result = JSON.parse(response.payload); + expect(result.tcbPolicy).toBe(''); + }); + }); + + describe('GET /policy/kms', () => { + it('should return tcbPolicy from backend', async () => { + const policy = '{"version":1,"intel_qal":[]}'; + app.ethereum.getKmsPolicy = jest.fn().mockResolvedValue({ tcbPolicy: policy }); + + const response = await app.inject({ + method: 'GET', + url: '/policy/kms', + }); + + expect(response.statusCode).toBe(200); + const result = JSON.parse(response.payload); + expect(result.tcbPolicy).toBe(policy); + }); + + it('should return empty tcbPolicy when none set', async () => { + app.ethereum.getKmsPolicy = jest.fn().mockResolvedValue({ tcbPolicy: '' }); + + const response = await app.inject({ + method: 'GET', + url: '/policy/kms', + }); + + expect(response.statusCode).toBe(200); + const result = JSON.parse(response.payload); + expect(result.tcbPolicy).toBe(''); + }); + }); + + describe('GET /', () => { + it('should return server info', async () => { + const response = await app.inject({ + method: 'GET', + url: '/', + }); + + expect(response.statusCode).toBe(200); + const result = JSON.parse(response.payload); + expect(result.status).toBe('ok'); + expect(result).toHaveProperty('kmsContractAddr'); + expect(result).toHaveProperty('gatewayAppId'); + expect(result).toHaveProperty('chainId'); + expect(result).toHaveProperty('appImplementation'); + }); + }); }); diff --git a/kms/auth-eth/test/serde_contract.test.ts b/kms/auth-eth/test/serde_contract.test.ts new file mode 100644 index 000000000..025f52cf7 --- /dev/null +++ b/kms/auth-eth/test/serde_contract.test.ts @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: © 2025 Phala Network +// +// SPDX-License-Identifier: Apache-2.0 + +/** + * Serde contract tests: verify that TypeScript types correctly + * parse/produce the same JSON fixtures used by the Rust KMS tests. + * + * Direction-aware: + * - BootInfo: KMS→auth-eth → TS is the consumer → test deserialization + * - BootResponse: auth-eth→KMS → TS is the producer → test serialization + * - PolicyResponse: auth-eth→KMS → TS is the producer → test serialization + */ + +import { BootInfo, BootResponse, PolicyResponse } from '../src/types'; +import * as fs from 'fs'; +import * as path from 'path'; + +const FIXTURES_DIR = path.resolve(__dirname, '../../tests/fixtures'); + +describe('Serde contract tests (shared fixtures)', () => { + describe('BootInfo (KMS→auth-eth: TS deserializes)', () => { + it('should deserialize the shared fixture produced by Rust', () => { + const json = JSON.parse(fs.readFileSync(path.join(FIXTURES_DIR, 'boot_info.json'), 'utf8')); + const info: BootInfo = json; + // Verify all required fields are accessible after deserialization + expect(typeof info.mrAggregated).toBe('string'); + expect(typeof info.osImageHash).toBe('string'); + expect(typeof info.mrSystem).toBe('string'); + expect(typeof info.appId).toBe('string'); + expect(typeof info.composeHash).toBe('string'); + expect(typeof info.instanceId).toBe('string'); + expect(typeof info.deviceId).toBe('string'); + expect(typeof info.tcbStatus).toBe('string'); + expect(Array.isArray(info.advisoryIds)).toBe(true); + expect(info.tcbStatus).toBe('UpToDate'); + expect(info.advisoryIds).toEqual(['INTEL-SA-00001']); + }); + + it('should contain all fields expected by the server schema', () => { + const json = JSON.parse(fs.readFileSync(path.join(FIXTURES_DIR, 'boot_info.json'), 'utf8')); + const requiredFields = ['mrAggregated', 'osImageHash', 'appId', 'composeHash', 'instanceId', 'deviceId']; + for (const field of requiredFields) { + expect(json).toHaveProperty(field); + } + }); + }); + + describe('BootResponse (auth-eth→KMS: TS serializes)', () => { + it('should produce JSON matching the shared fixture consumed by Rust', () => { + const fixture = JSON.parse(fs.readFileSync(path.join(FIXTURES_DIR, 'boot_response.json'), 'utf8')); + // Construct the response as auth-eth would + const resp: BootResponse = { + isAllowed: true, + gatewayAppId: '0x1234567890abcdef1234567890abcdef12345678', + reason: '', + }; + expect(JSON.parse(JSON.stringify(resp))).toEqual(fixture); + }); + }); + + describe('PolicyResponse (auth-eth→KMS: TS serializes)', () => { + it('should produce JSON matching the shared fixture consumed by Rust', () => { + const fixture = JSON.parse(fs.readFileSync(path.join(FIXTURES_DIR, 'policy_response.json'), 'utf8')); + // Construct the response as auth-eth would + const resp: PolicyResponse = { + tcbPolicy: '{"version":1,"intel_qal":[]}', + }; + expect(JSON.parse(JSON.stringify(resp))).toEqual(fixture); + }); + }); +}); diff --git a/kms/auth-eth/typechain-types/contracts/DstackApp.ts b/kms/auth-eth/typechain-types/contracts/DstackApp.ts index 643df03a8..2e95b0db8 100644 --- a/kms/auth-eth/typechain-types/contracts/DstackApp.ts +++ b/kms/auth-eth/typechain-types/contracts/DstackApp.ts @@ -77,7 +77,9 @@ export interface DstackAppInterface extends Interface { | "removeDevice" | "renounceOwnership" | "setAllowAnyDevice" + | "setTcbPolicy" | "supportsInterface" + | "tcbPolicy" | "transferOwnership" | "upgradeToAndCall" ): FunctionFragment; @@ -91,6 +93,7 @@ export interface DstackAppInterface extends Interface { | "DeviceRemoved" | "Initialized" | "OwnershipTransferred" + | "TcbPolicySet" | "Upgraded" | "UpgradesDisabled" ): EventFragment; @@ -152,10 +155,15 @@ export interface DstackAppInterface extends Interface { functionFragment: "setAllowAnyDevice", values: [boolean] ): string; + encodeFunctionData( + functionFragment: "setTcbPolicy", + values: [string] + ): string; encodeFunctionData( functionFragment: "supportsInterface", values: [BytesLike] ): string; + encodeFunctionData(functionFragment: "tcbPolicy", values?: undefined): string; encodeFunctionData( functionFragment: "transferOwnership", values: [AddressLike] @@ -216,10 +224,15 @@ export interface DstackAppInterface extends Interface { functionFragment: "setAllowAnyDevice", data: BytesLike ): Result; + decodeFunctionResult( + functionFragment: "setTcbPolicy", + data: BytesLike + ): Result; decodeFunctionResult( functionFragment: "supportsInterface", data: BytesLike ): Result; + decodeFunctionResult(functionFragment: "tcbPolicy", data: BytesLike): Result; decodeFunctionResult( functionFragment: "transferOwnership", data: BytesLike @@ -315,6 +328,18 @@ export namespace OwnershipTransferredEvent { export type LogDescription = TypedLogDescription; } +export namespace TcbPolicySetEvent { + export type InputTuple = [policy: string]; + export type OutputTuple = [policy: string]; + export interface OutputObject { + policy: string; + } + export type Event = TypedContractEvent; + export type Filter = TypedDeferredTopicFilter; + export type Log = TypedEventLog; + export type LogDescription = TypedLogDescription; +} + export namespace UpgradedEvent { export type InputTuple = [implementation: AddressLike]; export type OutputTuple = [implementation: string]; @@ -444,12 +469,16 @@ export interface DstackApp extends BaseContract { "nonpayable" >; + setTcbPolicy: TypedContractMethod<[policy: string], [void], "nonpayable">; + supportsInterface: TypedContractMethod< [interfaceId: BytesLike], [boolean], "view" >; + tcbPolicy: TypedContractMethod<[], [string], "view">; + transferOwnership: TypedContractMethod< [newOwner: AddressLike], [void], @@ -525,9 +554,15 @@ export interface DstackApp extends BaseContract { getFunction( nameOrSignature: "setAllowAnyDevice" ): TypedContractMethod<[_allowAnyDevice: boolean], [void], "nonpayable">; + getFunction( + nameOrSignature: "setTcbPolicy" + ): TypedContractMethod<[policy: string], [void], "nonpayable">; getFunction( nameOrSignature: "supportsInterface" ): TypedContractMethod<[interfaceId: BytesLike], [boolean], "view">; + getFunction( + nameOrSignature: "tcbPolicy" + ): TypedContractMethod<[], [string], "view">; getFunction( nameOrSignature: "transferOwnership" ): TypedContractMethod<[newOwner: AddressLike], [void], "nonpayable">; @@ -588,6 +623,13 @@ export interface DstackApp extends BaseContract { OwnershipTransferredEvent.OutputTuple, OwnershipTransferredEvent.OutputObject >; + getEvent( + key: "TcbPolicySet" + ): TypedContractEvent< + TcbPolicySetEvent.InputTuple, + TcbPolicySetEvent.OutputTuple, + TcbPolicySetEvent.OutputObject + >; getEvent( key: "Upgraded" ): TypedContractEvent< @@ -681,6 +723,17 @@ export interface DstackApp extends BaseContract { OwnershipTransferredEvent.OutputObject >; + "TcbPolicySet(string)": TypedContractEvent< + TcbPolicySetEvent.InputTuple, + TcbPolicySetEvent.OutputTuple, + TcbPolicySetEvent.OutputObject + >; + TcbPolicySet: TypedContractEvent< + TcbPolicySetEvent.InputTuple, + TcbPolicySetEvent.OutputTuple, + TcbPolicySetEvent.OutputObject + >; + "Upgraded(address)": TypedContractEvent< UpgradedEvent.InputTuple, UpgradedEvent.OutputTuple, diff --git a/kms/auth-eth/typechain-types/contracts/DstackKms.ts b/kms/auth-eth/typechain-types/contracts/DstackKms.ts index 71a78fffa..593ef0286 100644 --- a/kms/auth-eth/typechain-types/contracts/DstackKms.ts +++ b/kms/auth-eth/typechain-types/contracts/DstackKms.ts @@ -105,7 +105,9 @@ export interface DstackKmsInterface extends Interface { | "setKmsEventlog" | "setKmsInfo" | "setKmsQuote" + | "setTcbPolicy" | "supportsInterface" + | "tcbPolicy" | "transferOwnership" | "upgradeToAndCall" ): FunctionFragment; @@ -125,6 +127,7 @@ export interface DstackKmsInterface extends Interface { | "OsImageHashAdded" | "OsImageHashRemoved" | "OwnershipTransferred" + | "TcbPolicySet" | "Upgraded" ): EventFragment; @@ -230,10 +233,15 @@ export interface DstackKmsInterface extends Interface { functionFragment: "setKmsQuote", values: [BytesLike] ): string; + encodeFunctionData( + functionFragment: "setTcbPolicy", + values: [string] + ): string; encodeFunctionData( functionFragment: "supportsInterface", values: [BytesLike] ): string; + encodeFunctionData(functionFragment: "tcbPolicy", values?: undefined): string; encodeFunctionData( functionFragment: "transferOwnership", values: [AddressLike] @@ -339,10 +347,15 @@ export interface DstackKmsInterface extends Interface { functionFragment: "setKmsQuote", data: BytesLike ): Result; + decodeFunctionResult( + functionFragment: "setTcbPolicy", + data: BytesLike + ): Result; decodeFunctionResult( functionFragment: "supportsInterface", data: BytesLike ): Result; + decodeFunctionResult(functionFragment: "tcbPolicy", data: BytesLike): Result; decodeFunctionResult( functionFragment: "transferOwnership", data: BytesLike @@ -511,6 +524,18 @@ export namespace OwnershipTransferredEvent { export type LogDescription = TypedLogDescription; } +export namespace TcbPolicySetEvent { + export type InputTuple = [policy: string]; + export type OutputTuple = [policy: string]; + export interface OutputObject { + policy: string; + } + export type Event = TypedContractEvent; + export type Filter = TypedDeferredTopicFilter; + export type Log = TypedEventLog; + export type LogDescription = TypedLogDescription; +} + export namespace UpgradedEvent { export type InputTuple = [implementation: AddressLike]; export type OutputTuple = [implementation: string]; @@ -697,12 +722,16 @@ export interface DstackKms extends BaseContract { setKmsQuote: TypedContractMethod<[quote: BytesLike], [void], "nonpayable">; + setTcbPolicy: TypedContractMethod<[policy: string], [void], "nonpayable">; + supportsInterface: TypedContractMethod< [interfaceId: BytesLike], [boolean], "view" >; + tcbPolicy: TypedContractMethod<[], [string], "view">; + transferOwnership: TypedContractMethod< [newOwner: AddressLike], [void], @@ -833,9 +862,15 @@ export interface DstackKms extends BaseContract { getFunction( nameOrSignature: "setKmsQuote" ): TypedContractMethod<[quote: BytesLike], [void], "nonpayable">; + getFunction( + nameOrSignature: "setTcbPolicy" + ): TypedContractMethod<[policy: string], [void], "nonpayable">; getFunction( nameOrSignature: "supportsInterface" ): TypedContractMethod<[interfaceId: BytesLike], [boolean], "view">; + getFunction( + nameOrSignature: "tcbPolicy" + ): TypedContractMethod<[], [string], "view">; getFunction( nameOrSignature: "transferOwnership" ): TypedContractMethod<[newOwner: AddressLike], [void], "nonpayable">; @@ -938,6 +973,13 @@ export interface DstackKms extends BaseContract { OwnershipTransferredEvent.OutputTuple, OwnershipTransferredEvent.OutputObject >; + getEvent( + key: "TcbPolicySet" + ): TypedContractEvent< + TcbPolicySetEvent.InputTuple, + TcbPolicySetEvent.OutputTuple, + TcbPolicySetEvent.OutputObject + >; getEvent( key: "Upgraded" ): TypedContractEvent< @@ -1090,6 +1132,17 @@ export interface DstackKms extends BaseContract { OwnershipTransferredEvent.OutputObject >; + "TcbPolicySet(string)": TypedContractEvent< + TcbPolicySetEvent.InputTuple, + TcbPolicySetEvent.OutputTuple, + TcbPolicySetEvent.OutputObject + >; + TcbPolicySet: TypedContractEvent< + TcbPolicySetEvent.InputTuple, + TcbPolicySetEvent.OutputTuple, + TcbPolicySetEvent.OutputObject + >; + "Upgraded(address)": TypedContractEvent< UpgradedEvent.InputTuple, UpgradedEvent.OutputTuple, diff --git a/kms/auth-eth/typechain-types/contracts/IAppTcbPolicy.ts b/kms/auth-eth/typechain-types/contracts/IAppTcbPolicy.ts new file mode 100644 index 000000000..e16f25d2e --- /dev/null +++ b/kms/auth-eth/typechain-types/contracts/IAppTcbPolicy.ts @@ -0,0 +1,151 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +import type { + BaseContract, + BytesLike, + FunctionFragment, + Result, + Interface, + EventFragment, + ContractRunner, + ContractMethod, + Listener, +} from "ethers"; +import type { + TypedContractEvent, + TypedDeferredTopicFilter, + TypedEventLog, + TypedLogDescription, + TypedListener, + TypedContractMethod, +} from "../common"; + +export interface IAppTcbPolicyInterface extends Interface { + getFunction( + nameOrSignature: "setTcbPolicy" | "supportsInterface" | "tcbPolicy" + ): FunctionFragment; + + getEvent(nameOrSignatureOrTopic: "TcbPolicySet"): EventFragment; + + encodeFunctionData( + functionFragment: "setTcbPolicy", + values: [string] + ): string; + encodeFunctionData( + functionFragment: "supportsInterface", + values: [BytesLike] + ): string; + encodeFunctionData(functionFragment: "tcbPolicy", values?: undefined): string; + + decodeFunctionResult( + functionFragment: "setTcbPolicy", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "supportsInterface", + data: BytesLike + ): Result; + decodeFunctionResult(functionFragment: "tcbPolicy", data: BytesLike): Result; +} + +export namespace TcbPolicySetEvent { + export type InputTuple = [policy: string]; + export type OutputTuple = [policy: string]; + export interface OutputObject { + policy: string; + } + export type Event = TypedContractEvent; + export type Filter = TypedDeferredTopicFilter; + export type Log = TypedEventLog; + export type LogDescription = TypedLogDescription; +} + +export interface IAppTcbPolicy extends BaseContract { + connect(runner?: ContractRunner | null): IAppTcbPolicy; + waitForDeployment(): Promise; + + interface: IAppTcbPolicyInterface; + + queryFilter( + event: TCEvent, + fromBlockOrBlockhash?: string | number | undefined, + toBlock?: string | number | undefined + ): Promise>>; + queryFilter( + filter: TypedDeferredTopicFilter, + fromBlockOrBlockhash?: string | number | undefined, + toBlock?: string | number | undefined + ): Promise>>; + + on( + event: TCEvent, + listener: TypedListener + ): Promise; + on( + filter: TypedDeferredTopicFilter, + listener: TypedListener + ): Promise; + + once( + event: TCEvent, + listener: TypedListener + ): Promise; + once( + filter: TypedDeferredTopicFilter, + listener: TypedListener + ): Promise; + + listeners( + event: TCEvent + ): Promise>>; + listeners(eventName?: string): Promise>; + removeAllListeners( + event?: TCEvent + ): Promise; + + setTcbPolicy: TypedContractMethod<[policy: string], [void], "nonpayable">; + + supportsInterface: TypedContractMethod< + [interfaceId: BytesLike], + [boolean], + "view" + >; + + tcbPolicy: TypedContractMethod<[], [string], "view">; + + getFunction( + key: string | FunctionFragment + ): T; + + getFunction( + nameOrSignature: "setTcbPolicy" + ): TypedContractMethod<[policy: string], [void], "nonpayable">; + getFunction( + nameOrSignature: "supportsInterface" + ): TypedContractMethod<[interfaceId: BytesLike], [boolean], "view">; + getFunction( + nameOrSignature: "tcbPolicy" + ): TypedContractMethod<[], [string], "view">; + + getEvent( + key: "TcbPolicySet" + ): TypedContractEvent< + TcbPolicySetEvent.InputTuple, + TcbPolicySetEvent.OutputTuple, + TcbPolicySetEvent.OutputObject + >; + + filters: { + "TcbPolicySet(string)": TypedContractEvent< + TcbPolicySetEvent.InputTuple, + TcbPolicySetEvent.OutputTuple, + TcbPolicySetEvent.OutputObject + >; + TcbPolicySet: TypedContractEvent< + TcbPolicySetEvent.InputTuple, + TcbPolicySetEvent.OutputTuple, + TcbPolicySetEvent.OutputObject + >; + }; +} diff --git a/kms/auth-eth/typechain-types/contracts/index.ts b/kms/auth-eth/typechain-types/contracts/index.ts index 91b682bd3..03ebed210 100644 --- a/kms/auth-eth/typechain-types/contracts/index.ts +++ b/kms/auth-eth/typechain-types/contracts/index.ts @@ -5,3 +5,4 @@ export type { DstackApp } from "./DstackApp"; export type { DstackKms } from "./DstackKms"; export type { IAppAuth } from "./IAppAuth"; export type { IAppAuthBasicManagement } from "./IAppAuthBasicManagement"; +export type { IAppTcbPolicy } from "./IAppTcbPolicy"; diff --git a/kms/auth-eth/typechain-types/factories/contracts/DstackApp__factory.ts b/kms/auth-eth/typechain-types/factories/contracts/DstackApp__factory.ts index e755222ab..1f2fd25fa 100644 --- a/kms/auth-eth/typechain-types/factories/contracts/DstackApp__factory.ts +++ b/kms/auth-eth/typechain-types/factories/contracts/DstackApp__factory.ts @@ -194,6 +194,19 @@ const _abi = [ name: "OwnershipTransferred", type: "event", }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "string", + name: "policy", + type: "string", + }, + ], + name: "TcbPolicySet", + type: "event", + }, { anonymous: false, inputs: [ @@ -486,6 +499,19 @@ const _abi = [ stateMutability: "nonpayable", type: "function", }, + { + inputs: [ + { + internalType: "string", + name: "policy", + type: "string", + }, + ], + name: "setTcbPolicy", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, { inputs: [ { @@ -505,6 +531,19 @@ const _abi = [ stateMutability: "view", type: "function", }, + { + inputs: [], + name: "tcbPolicy", + outputs: [ + { + internalType: "string", + name: "", + type: "string", + }, + ], + stateMutability: "view", + type: "function", + }, { inputs: [ { @@ -539,7 +578,7 @@ const _abi = [ ] as const; const _bytecode = - "0x60a06040523060805234801561001457600080fd5b5061001d610022565b6100d4565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff16156100725760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b03908116146100d15780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b6080516111d76100fd60003960008181610a1401528181610a3d0152610be001526111d76000f3fe6080604052600436106101095760003560e01c806367b3f22c11610095578063ad3cb1cc11610064578063ad3cb1cc146102f4578063bf8b211b14610332578063dfc7722314610362578063ec66903614610382578063f2fde38b1461039757600080fd5b806367b3f22c14610258578063715018a6146102785780637c4beeb81461028d5780638da5cb5b146102ad57600080fd5b80632a819728116100dc5780632a819728146101b35780632f6622e5146101d35780633440a16a146102035780634f1ef2861461022257806352d1902d1461023557600080fd5b806301ffc9a71461010e578063187515ca146101435780631d266200146101655780631e07919814610185575b600080fd5b34801561011a57600080fd5b5061012e610129366004610eb8565b6103b7565b60405190151581526020015b60405180910390f35b34801561014f57600080fd5b5061016361015e366004610f0e565b610409565b005b34801561017157600080fd5b50610163610180366004610f63565b610650565b34801561019157600080fd5b506101a56101a0366004610f7c565b6106ab565b60405161013a929190611008565b3480156101bf57600080fd5b506101636101ce366004610f63565b610784565b3480156101df57600080fd5b5061012e6101ee366004610f63565b60006020819052908152604090205460ff1681565b34801561020f57600080fd5b5060015461012e90610100900460ff1681565b610163610230366004611041565b6107d7565b34801561024157600080fd5b5061024a6107f6565b60405190815260200161013a565b34801561026457600080fd5b50610163610273366004610f63565b610813565b34801561028457600080fd5b50610163610860565b34801561029957600080fd5b506101636102a8366004611103565b610874565b3480156102b957600080fd5b507f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546040516001600160a01b03909116815260200161013a565b34801561030057600080fd5b50610325604051806040016040528060058152602001640352e302e360dc1b81525081565b60405161013a919061111e565b34801561033e57600080fd5b5061012e61034d366004610f63565b60026020526000908152604090205460ff1681565b34801561036e57600080fd5b5061016361037d366004610f63565b6108c5565b34801561038e57600080fd5b50610163610918565b3480156103a357600080fd5b506101636103b2366004611131565b610957565b60006303c0f23360e31b6001600160e01b0319831614806103e85750638fd3752760e01b6001600160e01b03198316145b8061040357506301ffc9a760e01b6001600160e01b03198316145b92915050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff1660008115801561044f5750825b905060008267ffffffffffffffff16600114801561046c5750303b155b90508115801561047a575080155b156104985760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156104c257845460ff60401b1916600160401b1785555b6001600160a01b038a166105155760405162461bcd60e51b8152602060048201526015602482015274696e76616c6964206f776e6572206164647265737360581b60448201526064015b60405180910390fd5b6001805461ffff19168a151561ff001916176101008a151502179055861561058b5760008781526002602052604090819020805460ff19166001179055517f67fc71ab96fe3fa3c6f78e9a00e635d591b7333ce611c0380bc577aac702243b906105829089815260200190565b60405180910390a15b85156105e55760008681526020819052604090819020805460ff19166001179055517ffecb34306dd9d8b785b54d65489d06afc8822a0893ddacedff40c50a4942d0af906105dc9088815260200190565b60405180910390a15b6105ee8a610995565b6105f66109a6565b6105fe6109a6565b831561064457845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050565b6106586109ae565b60008181526002602052604090819020805460ff19169055517fe0862975ac517b0478d308012afabc4bc37c23874a18144d7f2dfb852ff95c2c906106a09083815260200190565b60405180910390a150565b602080820135600090815290819052604081205460609060ff1661070757505060408051808201909152601881527f436f6d706f73652068617368206e6f7420616c6c6f77656400000000000000006020820152600092909150565b600154610100900460ff161580156107335750606083013560009081526002602052604090205460ff16155b1561076b57505060408051808201909152601281527111195d9a58d9481b9bdd08185b1b1bddd95960721b6020820152600092909150565b5050604080516020810190915260008152600192909150565b61078c6109ae565b60008181526002602052604090819020805460ff19166001179055517f67fc71ab96fe3fa3c6f78e9a00e635d591b7333ce611c0380bc577aac702243b906106a09083815260200190565b6107df610a09565b6107e882610aae565b6107f28282610b13565b5050565b6000610800610bd5565b5060008051602061118283398151915290565b61081b6109ae565b60008181526020818152604091829020805460ff1916905590518281527f755b79bd4b0eeab344d032284a99003b2ddc018b646752ac72d681593a6e894791016106a0565b6108686109ae565b6108726000610c1e565b565b61087c6109ae565b600180548215156101000261ff00199091161790556040517fbb2cdb6c7b362202d40373f87bc4788301cca658f91711ac1662e1ad2cba4a20906106a090831515815260200190565b6108cd6109ae565b60008181526020819052604090819020805460ff19166001179055517ffecb34306dd9d8b785b54d65489d06afc8822a0893ddacedff40c50a4942d0af906106a09083815260200190565b6109206109ae565b6001805460ff1916811790556040517f0e5daa943fcd7e7182d0e893d180695c2ea9f6f1b4a1c5432faf14cf17b774e890600090a1565b61095f6109ae565b6001600160a01b03811661098957604051631e4fbdf760e01b81526000600482015260240161050c565b61099281610c1e565b50565b61099d610c8f565b61099281610cd8565b610872610c8f565b336109e07f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b0316146108725760405163118cdaa760e01b815233600482015260240161050c565b306001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161480610a9057507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316610a84600080516020611182833981519152546001600160a01b031690565b6001600160a01b031614155b156108725760405163703e46dd60e11b815260040160405180910390fd5b610ab66109ae565b60015460ff16156109925760405162461bcd60e51b815260206004820152602160248201527f557067726164657320617265207065726d616e656e746c792064697361626c656044820152601960fa1b606482015260840161050c565b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015610b6d575060408051601f3d908101601f19168201909252610b6a9181019061114c565b60015b610b9557604051634c9c8ce360e01b81526001600160a01b038316600482015260240161050c565b6000805160206111828339815191528114610bc657604051632a87526960e21b81526004810182905260240161050c565b610bd08383610ce0565b505050565b306001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146108725760405163703e46dd60e11b815260040160405180910390fd5b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661087257604051631afcd79f60e31b815260040160405180910390fd5b61095f610c8f565b610ce982610d36565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a2805115610d2e57610bd08282610d9b565b6107f2610e11565b806001600160a01b03163b600003610d6c57604051634c9c8ce360e01b81526001600160a01b038216600482015260240161050c565b60008051602061118283398151915280546001600160a01b0319166001600160a01b0392909216919091179055565b6060600080846001600160a01b031684604051610db89190611165565b600060405180830381855af49150503d8060008114610df3576040519150601f19603f3d011682016040523d82523d6000602084013e610df8565b606091505b5091509150610e08858383610e30565b95945050505050565b34156108725760405163b398979f60e01b815260040160405180910390fd5b606082610e4557610e4082610e8f565b610e88565b8151158015610e5c57506001600160a01b0384163b155b15610e8557604051639996b31560e01b81526001600160a01b038516600482015260240161050c565b50805b9392505050565b805115610e9f5780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b600060208284031215610eca57600080fd5b81356001600160e01b031981168114610e8857600080fd5b80356001600160a01b0381168114610ef957600080fd5b919050565b80358015158114610ef957600080fd5b600080600080600060a08688031215610f2657600080fd5b610f2f86610ee2565b9450610f3d60208701610efe565b9350610f4b60408701610efe565b94979396509394606081013594506080013592915050565b600060208284031215610f7557600080fd5b5035919050565b600060208284031215610f8e57600080fd5b813567ffffffffffffffff811115610fa557600080fd5b82016101208185031215610e8857600080fd5b60005b83811015610fd3578181015183820152602001610fbb565b50506000910152565b60008151808452610ff4816020860160208601610fb8565b601f01601f19169290920160200192915050565b82151581526040602082015260006110236040830184610fdc565b949350505050565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561105457600080fd5b61105d83610ee2565b9150602083013567ffffffffffffffff8082111561107a57600080fd5b818501915085601f83011261108e57600080fd5b8135818111156110a0576110a061102b565b604051601f8201601f19908116603f011681019083821181831017156110c8576110c861102b565b816040528281528860208487010111156110e157600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b60006020828403121561111557600080fd5b610e8882610efe565b602081526000610e886020830184610fdc565b60006020828403121561114357600080fd5b610e8882610ee2565b60006020828403121561115e57600080fd5b5051919050565b60008251611177818460208701610fb8565b919091019291505056fe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca2646970667358221220666b559e07439df16b5185c7222fbe5fd868fdfe715620d5958a419bad3f7f7264736f6c63430008160033"; + "0x60a06040523060805234801561001457600080fd5b5061001d610022565b6100d4565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff16156100725760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b03908116146100d15780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b6080516115186100fd60003960008181610b7401528181610b9d0152610d4001526115186000f3fe60806040526004361061011f5760003560e01c806367b3f22c116100a0578063bf8b211b11610064578063bf8b211b14610368578063dfc7722314610398578063ec669036146103b8578063f2fde38b146103cd578063f78b5f6d146103ed57600080fd5b806367b3f22c1461028e578063715018a6146102ae5780637c4beeb8146102c35780638da5cb5b146102e3578063ad3cb1cc1461032a57600080fd5b80632f6622e5116100e75780632f6622e5146101e95780633440a16a146102195780634f1ef2861461023857806351a6814a1461024b57806352d1902d1461026b57600080fd5b806301ffc9a714610124578063187515ca146101595780631d2662001461017b5780631e0791981461019b5780632a819728146101c9575b600080fd5b34801561013057600080fd5b5061014461013f36600461100d565b610402565b60405190151581526020015b60405180910390f35b34801561016557600080fd5b50610179610174366004611063565b61046f565b005b34801561018757600080fd5b506101796101963660046110b8565b6106a2565b3480156101a757600080fd5b506101bb6101b63660046110d1565b6106fd565b60405161015092919061115d565b3480156101d557600080fd5b506101796101e43660046110b8565b6107d6565b3480156101f557600080fd5b506101446102043660046110b8565b60006020819052908152604090205460ff1681565b34801561022557600080fd5b5060015461014490610100900460ff1681565b610179610246366004611196565b610829565b34801561025757600080fd5b50610179610266366004611258565b610848565b34801561027757600080fd5b5061028061089b565b604051908152602001610150565b34801561029a57600080fd5b506101796102a93660046110b8565b6108b8565b3480156102ba57600080fd5b50610179610905565b3480156102cf57600080fd5b506101796102de3660046112ca565b610919565b3480156102ef57600080fd5b507f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546040516001600160a01b039091168152602001610150565b34801561033657600080fd5b5061035b604051806040016040528060058152602001640352e302e360dc1b81525081565b60405161015091906112e5565b34801561037457600080fd5b506101446103833660046110b8565b60026020526000908152604090205460ff1681565b3480156103a457600080fd5b506101796103b33660046110b8565b61096a565b3480156103c457600080fd5b506101796109bd565b3480156103d957600080fd5b506101796103e83660046112f8565b6109fc565b3480156103f957600080fd5b5061035b610a3a565b60006303c0f23360e31b6001600160e01b0319831614806104335750638fd3752760e01b6001600160e01b03198316145b8061044e57506001600160e01b0319821663a62dde2760e01b145b8061046957506301ffc9a760e01b6001600160e01b03198316145b92915050565b6000610479610acc565b805490915060ff600160401b820416159067ffffffffffffffff166000811580156104a15750825b905060008267ffffffffffffffff1660011480156104be5750303b155b9050811580156104cc575080155b156104ea5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561051457845460ff60401b1916600160401b1785555b6001600160a01b038a166105675760405162461bcd60e51b8152602060048201526015602482015274696e76616c6964206f776e6572206164647265737360581b60448201526064015b60405180910390fd5b6001805461ffff19168a151561ff001916176101008a15150217905586156105dd5760008781526002602052604090819020805460ff19166001179055517f67fc71ab96fe3fa3c6f78e9a00e635d591b7333ce611c0380bc577aac702243b906105d49089815260200190565b60405180910390a15b85156106375760008681526020819052604090819020805460ff19166001179055517ffecb34306dd9d8b785b54d65489d06afc8822a0893ddacedff40c50a4942d0af9061062e9088815260200190565b60405180910390a15b6106408a610af5565b610648610b06565b610650610b06565b831561069657845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050565b6106aa610b0e565b60008181526002602052604090819020805460ff19169055517fe0862975ac517b0478d308012afabc4bc37c23874a18144d7f2dfb852ff95c2c906106f29083815260200190565b60405180910390a150565b602080820135600090815290819052604081205460609060ff1661075957505060408051808201909152601881527f436f6d706f73652068617368206e6f7420616c6c6f77656400000000000000006020820152600092909150565b600154610100900460ff161580156107855750606083013560009081526002602052604090205460ff16155b156107bd57505060408051808201909152601281527111195d9a58d9481b9bdd08185b1b1bddd95960721b6020820152600092909150565b5050604080516020810190915260008152600192909150565b6107de610b0e565b60008181526002602052604090819020805460ff19166001179055517f67fc71ab96fe3fa3c6f78e9a00e635d591b7333ce611c0380bc577aac702243b906106f29083815260200190565b610831610b69565b61083a82610c0e565b6108448282610c73565b5050565b610850610b0e565b600361085d82848361139d565b507fcc2941ec386bf57879cbbbf083bfd6daeb3df7a2b5cd28640b346b790fe0325e828260405161088f92919061145e565b60405180910390a15050565b60006108a5610d35565b506000805160206114c383398151915290565b6108c0610b0e565b60008181526020818152604091829020805460ff1916905590518281527f755b79bd4b0eeab344d032284a99003b2ddc018b646752ac72d681593a6e894791016106f2565b61090d610b0e565b6109176000610d7e565b565b610921610b0e565b600180548215156101000261ff00199091161790556040517fbb2cdb6c7b362202d40373f87bc4788301cca658f91711ac1662e1ad2cba4a20906106f290831515815260200190565b610972610b0e565b60008181526020819052604090819020805460ff19166001179055517ffecb34306dd9d8b785b54d65489d06afc8822a0893ddacedff40c50a4942d0af906106f29083815260200190565b6109c5610b0e565b6001805460ff1916811790556040517f0e5daa943fcd7e7182d0e893d180695c2ea9f6f1b4a1c5432faf14cf17b774e890600090a1565b610a04610b0e565b6001600160a01b038116610a2e57604051631e4fbdf760e01b81526000600482015260240161055e565b610a3781610d7e565b50565b606060038054610a4990611313565b80601f0160208091040260200160405190810160405280929190818152602001828054610a7590611313565b8015610ac25780601f10610a9757610100808354040283529160200191610ac2565b820191906000526020600020905b815481529060010190602001808311610aa557829003601f168201915b5050505050905090565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610469565b610afd610def565b610a3781610e14565b610917610def565b33610b407f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b0316146109175760405163118cdaa760e01b815233600482015260240161055e565b306001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161480610bf057507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316610be46000805160206114c3833981519152546001600160a01b031690565b6001600160a01b031614155b156109175760405163703e46dd60e11b815260040160405180910390fd5b610c16610b0e565b60015460ff1615610a375760405162461bcd60e51b815260206004820152602160248201527f557067726164657320617265207065726d616e656e746c792064697361626c656044820152601960fa1b606482015260840161055e565b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015610ccd575060408051601f3d908101601f19168201909252610cca9181019061148d565b60015b610cf557604051634c9c8ce360e01b81526001600160a01b038316600482015260240161055e565b6000805160206114c38339815191528114610d2657604051632a87526960e21b81526004810182905260240161055e565b610d308383610e1c565b505050565b306001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146109175760405163703e46dd60e11b815260040160405180910390fd5b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b610df7610e72565b61091757604051631afcd79f60e31b815260040160405180910390fd5b610a04610def565b610e2582610e8c565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a2805115610e6a57610d308282610ef1565b610844610f67565b6000610e7c610acc565b54600160401b900460ff16919050565b806001600160a01b03163b600003610ec257604051634c9c8ce360e01b81526001600160a01b038216600482015260240161055e565b6000805160206114c383398151915280546001600160a01b0319166001600160a01b0392909216919091179055565b6060600080846001600160a01b031684604051610f0e91906114a6565b600060405180830381855af49150503d8060008114610f49576040519150601f19603f3d011682016040523d82523d6000602084013e610f4e565b606091505b5091509150610f5e858383610f86565b95945050505050565b34156109175760405163b398979f60e01b815260040160405180910390fd5b606082610f9b57610f9682610fe5565b610fde565b8151158015610fb257506001600160a01b0384163b155b15610fdb57604051639996b31560e01b81526001600160a01b038516600482015260240161055e565b50805b9392505050565b805115610ff457805160208201fd5b60405163d6bda27560e01b815260040160405180910390fd5b60006020828403121561101f57600080fd5b81356001600160e01b031981168114610fde57600080fd5b80356001600160a01b038116811461104e57600080fd5b919050565b8035801515811461104e57600080fd5b600080600080600060a0868803121561107b57600080fd5b61108486611037565b945061109260208701611053565b93506110a060408701611053565b94979396509394606081013594506080013592915050565b6000602082840312156110ca57600080fd5b5035919050565b6000602082840312156110e357600080fd5b813567ffffffffffffffff8111156110fa57600080fd5b82016101208185031215610fde57600080fd5b60005b83811015611128578181015183820152602001611110565b50506000910152565b6000815180845261114981602086016020860161110d565b601f01601f19169290920160200192915050565b82151581526040602082015260006111786040830184611131565b949350505050565b634e487b7160e01b600052604160045260246000fd5b600080604083850312156111a957600080fd5b6111b283611037565b9150602083013567ffffffffffffffff808211156111cf57600080fd5b818501915085601f8301126111e357600080fd5b8135818111156111f5576111f5611180565b604051601f8201601f19908116603f0116810190838211818310171561121d5761121d611180565b8160405282815288602084870101111561123657600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b6000806020838503121561126b57600080fd5b823567ffffffffffffffff8082111561128357600080fd5b818501915085601f83011261129757600080fd5b8135818111156112a657600080fd5b8660208285010111156112b857600080fd5b60209290920196919550909350505050565b6000602082840312156112dc57600080fd5b610fde82611053565b602081526000610fde6020830184611131565b60006020828403121561130a57600080fd5b610fde82611037565b600181811c9082168061132757607f821691505b60208210810361134757634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115610d30576000816000526020600020601f850160051c810160208610156113765750805b601f850160051c820191505b8181101561139557828155600101611382565b505050505050565b67ffffffffffffffff8311156113b5576113b5611180565b6113c9836113c38354611313565b8361134d565b6000601f8411600181146113fd57600085156113e55750838201355b600019600387901b1c1916600186901b178355611457565b600083815260209020601f19861690835b8281101561142e578685013582556020948501946001909201910161140e565b508682101561144b5760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b60006020828403121561149f57600080fd5b5051919050565b600082516114b881846020870161110d565b919091019291505056fe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca2646970667358221220bbc9602eeaac427c97ba303dc4aae6952712003f53152246895719c43590866264736f6c63430008160033"; type DstackAppConstructorParams = | [signer?: Signer] diff --git a/kms/auth-eth/typechain-types/factories/contracts/DstackKms__factory.ts b/kms/auth-eth/typechain-types/factories/contracts/DstackKms__factory.ts index ca0648947..6f61f8e9a 100644 --- a/kms/auth-eth/typechain-types/factories/contracts/DstackKms__factory.ts +++ b/kms/auth-eth/typechain-types/factories/contracts/DstackKms__factory.ts @@ -278,6 +278,19 @@ const _abi = [ name: "OwnershipTransferred", type: "event", }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "string", + name: "policy", + type: "string", + }, + ], + name: "TcbPolicySet", + type: "event", + }, { anonymous: false, inputs: [ @@ -844,6 +857,19 @@ const _abi = [ stateMutability: "nonpayable", type: "function", }, + { + inputs: [ + { + internalType: "string", + name: "policy", + type: "string", + }, + ], + name: "setTcbPolicy", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, { inputs: [ { @@ -863,6 +889,19 @@ const _abi = [ stateMutability: "view", type: "function", }, + { + inputs: [], + name: "tcbPolicy", + outputs: [ + { + internalType: "string", + name: "", + type: "string", + }, + ], + stateMutability: "view", + type: "function", + }, { inputs: [ { @@ -897,7 +936,7 @@ const _abi = [ ] as const; const _bytecode = - "0x60a06040523060805234801561001457600080fd5b5061001d610022565b6100d4565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff16156100725760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b03908116146100d15780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b6080516128ff620000fe6000396000818161161701528181611641015261179601526128ff6000f3fe608060405260043610620001f35760003560e01c806352d1902d116200010b57806395f5193111620000a1578063ad3cb1cc116200006c578063ad3cb1cc146200062c578063e067ec9d146200065f578063f2fde38b1462000684578063f6fe4f4014620006a957600080fd5b806395f5193114620005785780639a4e1d18146200059f5780639e6f311414620005d3578063a6c4cce914620005f857600080fd5b80637d02535211620000e25780637d02535214620004ca5780638618169d14620004ef5780638da5cb5b14620005145780639425bac6146200055357600080fd5b806352d1902d1462000466578063715018a6146200048d578063736ede7a14620004a557600080fd5b806325a992da116200018d5780633e32d34611620001585780633e32d34614620003e0578063485cc95514620004055780634d5922a1146200042a5780634f1ef286146200044f57600080fd5b806325a992da14620003365780632adee48d14620003715780633971db2714620003965780633ceaaa1114620003bb57600080fd5b806313eb977011620001ce57806313eb9770146200028357806317a1d80f14620002b757806318c1ecb214620002dc5780631e079198146200030157600080fd5b806301ffc9a714620001f857806306a3ae961462000232578063091770631462000259575b600080fd5b3480156200020557600080fd5b506200021d6200021736600462001a99565b620006dd565b60405190151581526020015b60405180910390f35b3480156200023f57600080fd5b50620002576200025136600462001ac5565b62000715565b005b3480156200026657600080fd5b506200027162000776565b60405162000229949392919062001b33565b3480156200029057600080fd5b506200021d620002a236600462001ac5565b60076020526000908152604090205460ff1681565b348015620002c457600080fd5b5062000257620002d636600462001bb0565b620009d2565b348015620002e957600080fd5b5062000257620002fb36600462001cd2565b62000a74565b3480156200030e57600080fd5b50620003266200032036600462001d12565b62000a90565b6040516200022992919062001d50565b3480156200034357600080fd5b5060095462000358906001600160a01b031681565b6040516001600160a01b03909116815260200162000229565b3480156200037e57600080fd5b50620002576200039036600462001ac5565b62000c25565b348015620003a357600080fd5b5062000257620003b536600462001d6d565b62000c78565b348015620003c857600080fd5b5062000257620003da36600462001bb0565b62000cc2565b348015620003ed57600080fd5b5062000257620003ff36600462001ac5565b62000d73565b3480156200041257600080fd5b50620002576200042436600462001dba565b62000dc9565b3480156200043757600080fd5b50620002576200044936600462001ac5565b62000f56565b620002576200046036600462001df2565b62000fac565b3480156200047357600080fd5b506200047e62000fcd565b60405190815260200162000229565b3480156200049a57600080fd5b506200025762000fed565b348015620004b257600080fd5b5062000257620004c436600462001cd2565b62001005565b348015620004d757600080fd5b5062000257620004e936600462001e45565b6200101d565b348015620004fc57600080fd5b50620003586200050e36600462001f3d565b620010b7565b3480156200052157600080fd5b507f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031662000358565b3480156200056057600080fd5b50620002576200057236600462001ac5565b62001250565b3480156200058557600080fd5b5062000590620012a3565b60405162000229919062001f9d565b348015620005ac57600080fd5b506200021d620005be36600462001ac5565b60086020526000908152604090205460ff1681565b348015620005e057600080fd5b5062000257620005f236600462001ac5565b62001339565b3480156200060557600080fd5b506200021d6200061736600462001bb0565b60056020526000908152604090205460ff1681565b3480156200063957600080fd5b5062000590604051806040016040528060058152602001640352e302e360dc1b81525081565b3480156200066c57600080fd5b50620003266200067e36600462001d12565b6200138c565b3480156200069157600080fd5b5062000257620006a336600462001bb0565b6200154b565b348015620006b657600080fd5b506200021d620006c836600462001ac5565b60066020526000908152604090205460ff1681565b60006303c0f23360e31b6001600160e01b0319831614806200070f57506301ffc9a760e01b6001600160e01b03198316145b92915050565b6200071f6200158f565b60008181526008602052604090819020805460ff19166001179055517f4911843506849c85a5ca6e6e2c01eed2d3ab86baba619517a2aad9d5ab2aeff2906200076b9083815260200190565b60405180910390a150565b600080548190620007879062001fb2565b80601f0160208091040260200160405190810160405280929190818152602001828054620007b59062001fb2565b8015620008065780601f10620007da5761010080835404028352916020019162000806565b820191906000526020600020905b815481529060010190602001808311620007e857829003601f168201915b5050505050908060010180546200081d9062001fb2565b80601f01602080910402602001604051908101604052809291908181526020018280546200084b9062001fb2565b80156200089c5780601f1062000870576101008083540402835291602001916200089c565b820191906000526020600020905b8154815290600101906020018083116200087e57829003601f168201915b505050505090806002018054620008b39062001fb2565b80601f0160208091040260200160405190810160405280929190818152602001828054620008e19062001fb2565b8015620009325780601f10620009065761010080835404028352916020019162000932565b820191906000526020600020905b8154815290600101906020018083116200091457829003601f168201915b505050505090806003018054620009499062001fb2565b80601f0160208091040260200160405190810160405280929190818152602001828054620009779062001fb2565b8015620009c85780601f106200099c57610100808354040283529160200191620009c8565b820191906000526020600020905b815481529060010190602001808311620009aa57829003601f168201915b5050505050905084565b6001600160a01b03811662000a1f5760405162461bcd60e51b815260206004820152600e60248201526d125b9d985b1a5908185c1c08125160921b60448201526064015b60405180910390fd5b6001600160a01b038116600081815260056020908152604091829020805460ff1916600117905590519182527f0d540ad8f39e07d19909687352b9fa017405d93c91a6760981fbae9cf28bfef791016200076b565b62000a7e6200158f565b600262000a8c828262002042565b5050565b6000606060058262000aa6602086018662001bb0565b6001600160a01b0316815260208101919091526040016000205460ff1662000afb575050604080518082019091526012815271105c1c081b9bdd081c9959da5cdd195c995960721b6020820152600092909150565b60c083013560009081526008602052604090205460ff1662000b4f57505060408051808201909152601781527613d4c81a5b5859d9481a5cc81b9bdd08185b1b1bddd959604a1b6020820152600092909150565b62000b6e62000b62602085018562001bb0565b3b63ffffffff16151590565b62000b98576000604051806060016040528060238152602001620028a76023913991509150915091565b62000ba7602084018462001bb0565b6001600160a01b0316631e079198846040518263ffffffff1660e01b815260040162000bd4919062002238565b600060405180830381865afa15801562000bf2573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000c1c919081019062002305565b91509150915091565b62000c2f6200158f565b60008181526006602052604090819020805460ff19169055517f54cd662e41eec7ddf0f32f034b2533e481b471dd3ea978222d609ec8fffb1bb0906200076b9083815260200190565b62000c826200158f565b600462000c90828262002042565b507f5b2b64f770ea5266055ebd3ebf205ef06cbfa738e62684edf0e28356e34acf06816040516200076b919062001f9d565b62000ccc6200158f565b6001600160a01b03811662000d245760405162461bcd60e51b815260206004820152601e60248201527f496e76616c696420696d706c656d656e746174696f6e20616464726573730000604482015260640162000a16565b600980546001600160a01b0319166001600160a01b0383169081179091556040519081527f08bb433f12b5fd81d2d5e0deeb3b4d28371781ddbd80e9d053e7468d2b1aa82d906020016200076b565b62000d7d6200158f565b60008181526006602052604090819020805460ff19166001179055517f68615a0b92795750b3d180ff73429e1291d225e449eee954a567b91104fd9837906200076b9083815260200190565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b031660008115801562000e0f5750825b90506000826001600160401b0316600114801562000e2c5750303b155b90508115801562000e3b575080155b1562000e5a5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831562000e8557845460ff60401b1916600160401b1785555b62000e9087620015ed565b62000e9a62001602565b62000ea462001602565b6001600160a01b0386161562000f0657600980546001600160a01b0319166001600160a01b0388169081179091556040519081527f08bb433f12b5fd81d2d5e0deeb3b4d28371781ddbd80e9d053e7468d2b1aa82d9060200160405180910390a15b831562000f4d57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b62000f606200158f565b60008181526007602052604090819020805460ff19166001179055517ff2b290637b1193e12eb38ab32b986303639b1687cc2cd1e25f993e6eae2f4041906200076b9083815260200190565b62000fb66200160c565b62000fc182620016b5565b62000a8c8282620016bf565b600062000fd96200178b565b506000805160206200288783398151915290565b62000ff76200158f565b620010036000620017d5565b565b6200100f6200158f565b600362000a8c828262002042565b620010276200158f565b8051819060009081906200103c908262002042565b506020820151600182019062001053908262002042565b50604082015160028201906200106a908262002042565b506060820151600382019062001081908262002042565b505081516040517f77cdad119a452bbd96c45635758fc4af8a6bde3deaccf3fada634ddf9a16270692506200076b919062001f9d565b6009546000906001600160a01b0316620011145760405162461bcd60e51b815260206004820181905260248201527f44737461636b41707020696d706c656d656e746174696f6e206e6f7420736574604482015260640162000a16565b6001600160a01b038616620011645760405162461bcd60e51b8152602060048201526015602482015274496e76616c6964206f776e6572206164647265737360581b604482015260640162000a16565b604080516001600160a01b038881166024830152871515604483015286151560648301526084820186905260a48083018690528351808403909101815260c490920183526020820180516001600160e01b0316630c3a8ae560e11b1790526009549251919216908290620011d89062001a8b565b620011e59291906200239c565b604051809103906000f08015801562001202573d6000803e3d6000fd5b5091506200121082620009d2565b60405133906001600160a01b038416907ffd86d7f6962eba3b7a3bf9129c06c0b2f885e1c61ef2c9f0dbb856be0deefdee90600090a35095945050505050565b6200125a6200158f565b60008181526008602052604090819020805460ff19169055517f99b3cee95daf3138d0c982299cd0264f5b7a61aa629b42b92f3ec8966aa7f6fe906200076b9083815260200190565b60048054620012b29062001fb2565b80601f0160208091040260200160405190810160405280929190818152602001828054620012e09062001fb2565b8015620013315780601f10620013055761010080835404028352916020019162001331565b820191906000526020600020905b8154815290600101906020018083116200131357829003601f168201915b505050505081565b620013436200158f565b60008181526007602052604090819020805460ff19169055517f1a9888a45e3df8b7e6bb3090747d598d2a0d78e61388161a90799f54df8f4434906200076b9083815260200190565b60006060604051602001620013af90675570546f4461746560c01b815260080190565b60408051601f198184030181529190528051602090910120620013d660e0850185620023c2565b604051602001620013e99291906200240b565b60405160208183030381529060405280519060200120146200144357505060408051808201909152601c81527f54434220737461747573206973206e6f7420757020746f2064617465000000006020820152600092909150565b60c083013560009081526008602052604090205460ff166200149757505060408051808201909152601781527613d4c81a5b5859d9481a5cc81b9bdd08185b1b1bddd959604a1b6020820152600092909150565b608083013560009081526006602052604090205460ff16620014f157505060408051808201909152601981527f41676772656761746564204d52206e6f7420616c6c6f776564000000000000006020820152600092909150565b606083013560009081526007602052604090205460ff16620015325760006040518060600160405280602981526020016200285e6029913991509150915091565b5050604080516020810190915260008152600192909150565b620015556200158f565b6001600160a01b0381166200158157604051631e4fbdf760e01b81526000600482015260240162000a16565b6200158c81620017d5565b50565b33620015c27f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b031614620010035760405163118cdaa760e01b815233600482015260240162000a16565b620015f762001846565b6200158c8162001890565b6200100362001846565b306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614806200169657507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166200168a60008051602062002887833981519152546001600160a01b031690565b6001600160a01b031614155b15620010035760405163703e46dd60e11b815260040160405180910390fd5b6200158c6200158f565b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156200171c575060408051601f3d908101601f1916820190925262001719918101906200241b565b60015b6200174657604051634c9c8ce360e01b81526001600160a01b038316600482015260240162000a16565b6000805160206200288783398151915281146200177a57604051632a87526960e21b81526004810182905260240162000a16565b6200178683836200189a565b505050565b306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614620010035760405163703e46dd60e11b815260040160405180910390fd5b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff166200100357604051631afcd79f60e31b815260040160405180910390fd5b6200155562001846565b620018a582620018f7565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a2805115620018ed576200178682826200195f565b62000a8c620019db565b806001600160a01b03163b6000036200192f57604051634c9c8ce360e01b81526001600160a01b038216600482015260240162000a16565b6000805160206200288783398151915280546001600160a01b0319166001600160a01b0392909216919091179055565b6060600080846001600160a01b0316846040516200197e919062002435565b600060405180830381855af49150503d8060008114620019bb576040519150601f19603f3d011682016040523d82523d6000602084013e620019c0565b606091505b5091509150620019d2858383620019fb565b95945050505050565b3415620010035760405163b398979f60e01b815260040160405180910390fd5b60608262001a145762001a0e8262001a61565b62001a5a565b815115801562001a2c57506001600160a01b0384163b155b1562001a5757604051639996b31560e01b81526001600160a01b038516600482015260240162000a16565b50805b9392505050565b80511562001a725780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b61040a806200245483390190565b60006020828403121562001aac57600080fd5b81356001600160e01b03198116811462001a5a57600080fd5b60006020828403121562001ad857600080fd5b5035919050565b60005b8381101562001afc57818101518382015260200162001ae2565b50506000910152565b6000815180845262001b1f81602086016020860162001adf565b601f01601f19169290920160200192915050565b60808152600062001b48608083018762001b05565b828103602084015262001b5c818762001b05565b9050828103604084015262001b72818662001b05565b9050828103606084015262001b88818562001b05565b979650505050505050565b80356001600160a01b038116811462001bab57600080fd5b919050565b60006020828403121562001bc357600080fd5b62001a5a8262001b93565b634e487b7160e01b600052604160045260246000fd5b604051608081016001600160401b038111828210171562001c095762001c0962001bce565b60405290565b604051601f8201601f191681016001600160401b038111828210171562001c3a5762001c3a62001bce565b604052919050565b60006001600160401b0382111562001c5e5762001c5e62001bce565b50601f01601f191660200190565b600062001c8362001c7d8462001c42565b62001c0f565b905082815283838301111562001c9857600080fd5b828260208301376000602084830101529392505050565b600082601f83011262001cc157600080fd5b62001a5a8383356020850162001c6c565b60006020828403121562001ce557600080fd5b81356001600160401b0381111562001cfc57600080fd5b62001d0a8482850162001caf565b949350505050565b60006020828403121562001d2557600080fd5b81356001600160401b0381111562001d3c57600080fd5b8201610120818503121562001a5a57600080fd5b821515815260406020820152600062001d0a604083018462001b05565b60006020828403121562001d8057600080fd5b81356001600160401b0381111562001d9757600080fd5b8201601f8101841362001da957600080fd5b62001d0a8482356020840162001c6c565b6000806040838503121562001dce57600080fd5b62001dd98362001b93565b915062001de96020840162001b93565b90509250929050565b6000806040838503121562001e0657600080fd5b62001e118362001b93565b915060208301356001600160401b0381111562001e2d57600080fd5b62001e3b8582860162001caf565b9150509250929050565b60006020828403121562001e5857600080fd5b81356001600160401b038082111562001e7057600080fd5b908301906080828603121562001e8557600080fd5b62001e8f62001be4565b82358281111562001e9f57600080fd5b62001ead8782860162001caf565b82525060208301358281111562001ec357600080fd5b62001ed18782860162001caf565b60208301525060408301358281111562001eea57600080fd5b62001ef88782860162001caf565b60408301525060608301358281111562001f1157600080fd5b62001f1f8782860162001caf565b60608301525095945050505050565b80151581146200158c57600080fd5b600080600080600060a0868803121562001f5657600080fd5b62001f618662001b93565b9450602086013562001f738162001f2e565b9350604086013562001f858162001f2e565b94979396509394606081013594506080013592915050565b60208152600062001a5a602083018462001b05565b600181811c9082168062001fc757607f821691505b60208210810362001fe857634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111562001786576000816000526020600020601f850160051c81016020861015620020195750805b601f850160051c820191505b818110156200203a5782815560010162002025565b505050505050565b81516001600160401b038111156200205e576200205e62001bce565b62002076816200206f845462001fb2565b8462001fee565b602080601f831160018114620020ae5760008415620020955750858301515b600019600386901b1c1916600185901b1785556200203a565b600085815260208120601f198616915b82811015620020df57888601518255948401946001909101908401620020be565b5085821015620020fe5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6000808335601e198436030181126200212657600080fd5b83016020810192503590506001600160401b038111156200214657600080fd5b8036038213156200215657600080fd5b9250929050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6000808335601e198436030181126200219e57600080fd5b83016020810192503590506001600160401b03811115620021be57600080fd5b8060051b36038213156200215657600080fd5b6000838385526020808601955060208560051b8301018460005b878110156200222b57848303601f190189526200220982886200210e565b620022168582846200215d565b9a86019a9450505090830190600101620021eb565b5090979650505050505050565b602081526200225c602082016200224f8462001b93565b6001600160a01b03169052565b602082013560408201526000620022766040840162001b93565b6001600160a01b03811660608401525060608301356080830152608083013560a083015260a083013560c083015260c083013560e0830152620022bd60e08401846200210e565b6101206101008181870152620022d9610140870184866200215d565b9350620022e98188018862002186565b878603601f1901848901529350905062001b88848483620021d1565b600080604083850312156200231957600080fd5b8251620023268162001f2e565b60208401519092506001600160401b038111156200234357600080fd5b8301601f810185136200235557600080fd5b80516200236662001c7d8262001c42565b8181528660208385010111156200237c57600080fd5b6200238f82602083016020860162001adf565b8093505050509250929050565b6001600160a01b038316815260406020820181905260009062001d0a9083018462001b05565b6000808335601e19843603018112620023da57600080fd5b8301803591506001600160401b03821115620023f557600080fd5b6020019150368190038213156200215657600080fd5b8183823760009101908152919050565b6000602082840312156200242e57600080fd5b5051919050565b600082516200244981846020870162001adf565b919091019291505056fe608060405260405161040a38038061040a83398101604081905261002291610268565b61002c8282610033565b5050610352565b61003c82610092565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a280511561008657610081828261010e565b505050565b61008e610185565b5050565b806001600160a01b03163b6000036100cd57604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b6060600080846001600160a01b03168460405161012b9190610336565b600060405180830381855af49150503d8060008114610166576040519150601f19603f3d011682016040523d82523d6000602084013e61016b565b606091505b50909250905061017c8583836101a6565b95945050505050565b34156101a45760405163b398979f60e01b815260040160405180910390fd5b565b6060826101bb576101b682610205565b6101fe565b81511580156101d257506001600160a01b0384163b155b156101fb57604051639996b31560e01b81526001600160a01b03851660048201526024016100c4565b50805b9392505050565b8051156102155780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b634e487b7160e01b600052604160045260246000fd5b60005b8381101561025f578181015183820152602001610247565b50506000910152565b6000806040838503121561027b57600080fd5b82516001600160a01b038116811461029257600080fd5b60208401519092506001600160401b03808211156102af57600080fd5b818501915085601f8301126102c357600080fd5b8151818111156102d5576102d561022e565b604051601f8201601f19908116603f011681019083821181831017156102fd576102fd61022e565b8160405282815288602084870101111561031657600080fd5b610327836020830160208801610244565b80955050505050509250929050565b60008251610348818460208701610244565b9190910192915050565b60aa806103606000396000f3fe6080604052600a600c565b005b60186014601a565b6051565b565b6000604c7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b3660008037600080366000845af43d6000803e808015606f573d6000f35b3d6000fdfea26469706673582212201d1e675cd71e57bb3f08113f3040612bee9b14a06a3515aeb6fc806b55a6323764736f6c634300081600334b4d53206973206e6f7420616c6c6f77656420746f20626f6f74206f6e207468697320646576696365360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc417070206e6f74206465706c6f796564206f7220696e76616c69642061646472657373a26469706673582212200dd82aa817b5e4dda681b69c3f612fec30caad1e59ba2a366019ceb84bb796d964736f6c63430008160033"; + "0x60a0604052306080523480156200001557600080fd5b506200002062000026565b620000da565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff1615620000775760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b0390811614620000d75780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b608051612bcc6200010460003960008181611793015281816117bd01526119120152612bcc6000f3fe6080604052600436106200020b5760003560e01c806352d1902d11620001175780639a4e1d1811620000a1578063e067ec9d116200006c578063e067ec9d146200069c578063f2fde38b14620006c1578063f6fe4f4014620006e6578063f78b5f6d146200071a57600080fd5b80639a4e1d1814620005dc5780639e6f31141462000610578063a6c4cce91462000635578063ad3cb1cc146200066957600080fd5b80638618169d11620000e25780638618169d146200052c5780638da5cb5b14620005515780639425bac6146200059057806395f5193114620005b557600080fd5b806352d1902d14620004a3578063715018a614620004ca578063736ede7a14620004e25780637d025352146200050757600080fd5b80632adee48d1162000199578063485cc9551162000164578063485cc955146200041d5780634d5922a114620004425780634f1ef286146200046757806351a6814a146200047e57600080fd5b80632adee48d14620003895780633971db2714620003ae5780633ceaaa1114620003d35780633e32d34614620003f857600080fd5b806317a1d80f11620001da57806317a1d80f14620002cf57806318c1ecb214620002f45780631e079198146200031957806325a992da146200034e57600080fd5b806301ffc9a7146200021057806306a3ae96146200024a57806309177063146200027157806313eb9770146200029b575b600080fd5b3480156200021d57600080fd5b50620002356200022f36600462001c0e565b62000732565b60405190151581526020015b60405180910390f35b3480156200025757600080fd5b506200026f6200026936600462001c3a565b62000786565b005b3480156200027e57600080fd5b5062000289620007e7565b60405162000241949392919062001ca8565b348015620002a857600080fd5b5062000235620002ba36600462001c3a565b60076020526000908152604090205460ff1681565b348015620002dc57600080fd5b506200026f620002ee36600462001d25565b62000a43565b3480156200030157600080fd5b506200026f6200031336600462001e47565b62000ae5565b3480156200032657600080fd5b506200033e6200033836600462001e87565b62000b01565b6040516200024192919062001ec5565b3480156200035b57600080fd5b5060095462000370906001600160a01b031681565b6040516001600160a01b03909116815260200162000241565b3480156200039657600080fd5b506200026f620003a836600462001c3a565b62000c96565b348015620003bb57600080fd5b506200026f620003cd36600462001ee2565b62000ce9565b348015620003e057600080fd5b506200026f620003f236600462001d25565b62000d33565b3480156200040557600080fd5b506200026f6200041736600462001c3a565b62000de4565b3480156200042a57600080fd5b506200026f6200043c36600462001f2f565b62000e3a565b3480156200044f57600080fd5b506200026f6200046136600462001c3a565b62000fb5565b6200026f6200047836600462001f67565b6200100b565b3480156200048b57600080fd5b506200026f6200049d36600462001fba565b6200102c565b348015620004b057600080fd5b50620004bb62001085565b60405190815260200162000241565b348015620004d757600080fd5b506200026f620010a5565b348015620004ef57600080fd5b506200026f6200050136600462001e47565b620010bd565b3480156200051457600080fd5b506200026f6200052636600462002030565b620010d5565b3480156200053957600080fd5b50620003706200054b36600462002128565b6200116f565b3480156200055e57600080fd5b507f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031662000370565b3480156200059d57600080fd5b506200026f620005af36600462001c3a565b62001308565b348015620005c257600080fd5b50620005cd6200135b565b60405162000241919062002188565b348015620005e957600080fd5b5062000235620005fb36600462001c3a565b60086020526000908152604090205460ff1681565b3480156200061d57600080fd5b506200026f6200062f36600462001c3a565b620013f1565b3480156200064257600080fd5b50620002356200065436600462001d25565b60056020526000908152604090205460ff1681565b3480156200067657600080fd5b50620005cd604051806040016040528060058152602001640352e302e360dc1b81525081565b348015620006a957600080fd5b506200033e620006bb36600462001e87565b62001444565b348015620006ce57600080fd5b506200026f620006e036600462001d25565b62001603565b348015620006f357600080fd5b50620002356200070536600462001c3a565b60066020526000908152604090205460ff1681565b3480156200072757600080fd5b50620005cd62001647565b60006303c0f23360e31b6001600160e01b0319831614806200076457506001600160e01b0319821663a62dde2760e01b145b806200078057506301ffc9a760e01b6001600160e01b03198316145b92915050565b62000790620016e1565b60008181526008602052604090819020805460ff19166001179055517f4911843506849c85a5ca6e6e2c01eed2d3ab86baba619517a2aad9d5ab2aeff290620007dc9083815260200190565b60405180910390a150565b600080548190620007f8906200219d565b80601f016020809104026020016040519081016040528092919081815260200182805462000826906200219d565b8015620008775780601f106200084b5761010080835404028352916020019162000877565b820191906000526020600020905b8154815290600101906020018083116200085957829003601f168201915b5050505050908060010180546200088e906200219d565b80601f0160208091040260200160405190810160405280929190818152602001828054620008bc906200219d565b80156200090d5780601f10620008e1576101008083540402835291602001916200090d565b820191906000526020600020905b815481529060010190602001808311620008ef57829003601f168201915b50505050509080600201805462000924906200219d565b80601f016020809104026020016040519081016040528092919081815260200182805462000952906200219d565b8015620009a35780601f106200097757610100808354040283529160200191620009a3565b820191906000526020600020905b8154815290600101906020018083116200098557829003601f168201915b505050505090806003018054620009ba906200219d565b80601f0160208091040260200160405190810160405280929190818152602001828054620009e8906200219d565b801562000a395780601f1062000a0d5761010080835404028352916020019162000a39565b820191906000526020600020905b81548152906001019060200180831162000a1b57829003601f168201915b5050505050905084565b6001600160a01b03811662000a905760405162461bcd60e51b815260206004820152600e60248201526d125b9d985b1a5908185c1c08125160921b60448201526064015b60405180910390fd5b6001600160a01b038116600081815260056020908152604091829020805460ff1916600117905590519182527f0d540ad8f39e07d19909687352b9fa017405d93c91a6760981fbae9cf28bfef79101620007dc565b62000aef620016e1565b600262000afd82826200222d565b5050565b6000606060058262000b17602086018662001d25565b6001600160a01b0316815260208101919091526040016000205460ff1662000b6c575050604080518082019091526012815271105c1c081b9bdd081c9959da5cdd195c995960721b6020820152600092909150565b60c083013560009081526008602052604090205460ff1662000bc057505060408051808201909152601781527613d4c81a5b5859d9481a5cc81b9bdd08185b1b1bddd959604a1b6020820152600092909150565b62000bdf62000bd3602085018562001d25565b3b63ffffffff16151590565b62000c0957600060405180606001604052806023815260200162002b746023913991509150915091565b62000c18602084018462001d25565b6001600160a01b0316631e079198846040518263ffffffff1660e01b815260040162000c45919062002423565b600060405180830381865afa15801562000c63573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000c8d9190810190620024f0565b91509150915091565b62000ca0620016e1565b60008181526006602052604090819020805460ff19169055517f54cd662e41eec7ddf0f32f034b2533e481b471dd3ea978222d609ec8fffb1bb090620007dc9083815260200190565b62000cf3620016e1565b600462000d0182826200222d565b507f5b2b64f770ea5266055ebd3ebf205ef06cbfa738e62684edf0e28356e34acf0681604051620007dc919062002188565b62000d3d620016e1565b6001600160a01b03811662000d955760405162461bcd60e51b815260206004820152601e60248201527f496e76616c696420696d706c656d656e746174696f6e20616464726573730000604482015260640162000a87565b600980546001600160a01b0319166001600160a01b0383169081179091556040519081527f08bb433f12b5fd81d2d5e0deeb3b4d28371781ddbd80e9d053e7468d2b1aa82d90602001620007dc565b62000dee620016e1565b60008181526006602052604090819020805460ff19166001179055517f68615a0b92795750b3d180ff73429e1291d225e449eee954a567b91104fd983790620007dc9083815260200190565b600062000e466200173f565b805490915060ff600160401b82041615906001600160401b031660008115801562000e6e5750825b90506000826001600160401b0316600114801562000e8b5750303b155b90508115801562000e9a575080155b1562000eb95760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831562000ee457845460ff60401b1916600160401b1785555b62000eef8762001769565b62000ef96200177e565b62000f036200177e565b6001600160a01b0386161562000f6557600980546001600160a01b0319166001600160a01b0388169081179091556040519081527f08bb433f12b5fd81d2d5e0deeb3b4d28371781ddbd80e9d053e7468d2b1aa82d9060200160405180910390a15b831562000fac57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b62000fbf620016e1565b60008181526007602052604090819020805460ff19166001179055517ff2b290637b1193e12eb38ab32b986303639b1687cc2cd1e25f993e6eae2f404190620007dc9083815260200190565b6200101562001788565b620010208262001831565b62000afd82826200183b565b62001036620016e1565b600a6200104582848362002587565b507fcc2941ec386bf57879cbbbf083bfd6daeb3df7a2b5cd28640b346b790fe0325e82826040516200107992919062002654565b60405180910390a15050565b60006200109162001907565b5060008051602062002b5483398151915290565b620010af620016e1565b620010bb600062001951565b565b620010c7620016e1565b600362000afd82826200222d565b620010df620016e1565b805181906000908190620010f490826200222d565b50602082015160018201906200110b90826200222d565b50604082015160028201906200112290826200222d565b50606082015160038201906200113990826200222d565b505081516040517f77cdad119a452bbd96c45635758fc4af8a6bde3deaccf3fada634ddf9a1627069250620007dc919062002188565b6009546000906001600160a01b0316620011cc5760405162461bcd60e51b815260206004820181905260248201527f44737461636b41707020696d706c656d656e746174696f6e206e6f7420736574604482015260640162000a87565b6001600160a01b0386166200121c5760405162461bcd60e51b8152602060048201526015602482015274496e76616c6964206f776e6572206164647265737360581b604482015260640162000a87565b604080516001600160a01b038881166024830152871515604483015286151560648301526084820186905260a48083018690528351808403909101815260c490920183526020820180516001600160e01b0316630c3a8ae560e11b1790526009549251919216908290620012909062001c00565b6200129d9291906200266a565b604051809103906000f080158015620012ba573d6000803e3d6000fd5b509150620012c88262000a43565b60405133906001600160a01b038416907ffd86d7f6962eba3b7a3bf9129c06c0b2f885e1c61ef2c9f0dbb856be0deefdee90600090a35095945050505050565b62001312620016e1565b60008181526008602052604090819020805460ff19169055517f99b3cee95daf3138d0c982299cd0264f5b7a61aa629b42b92f3ec8966aa7f6fe90620007dc9083815260200190565b600480546200136a906200219d565b80601f016020809104026020016040519081016040528092919081815260200182805462001398906200219d565b8015620013e95780601f10620013bd57610100808354040283529160200191620013e9565b820191906000526020600020905b815481529060010190602001808311620013cb57829003601f168201915b505050505081565b620013fb620016e1565b60008181526007602052604090819020805460ff19169055517f1a9888a45e3df8b7e6bb3090747d598d2a0d78e61388161a90799f54df8f443490620007dc9083815260200190565b600060606040516020016200146790675570546f4461746560c01b815260080190565b60408051601f1981840301815291905280516020909101206200148e60e085018562002690565b604051602001620014a1929190620026d9565b6040516020818303038152906040528051906020012014620014fb57505060408051808201909152601c81527f54434220737461747573206973206e6f7420757020746f2064617465000000006020820152600092909150565b60c083013560009081526008602052604090205460ff166200154f57505060408051808201909152601781527613d4c81a5b5859d9481a5cc81b9bdd08185b1b1bddd959604a1b6020820152600092909150565b608083013560009081526006602052604090205460ff16620015a957505060408051808201909152601981527f41676772656761746564204d52206e6f7420616c6c6f776564000000000000006020820152600092909150565b606083013560009081526007602052604090205460ff16620015ea57600060405180606001604052806029815260200162002b2b6029913991509150915091565b5050604080516020810190915260008152600192909150565b6200160d620016e1565b6001600160a01b0381166200163957604051631e4fbdf760e01b81526000600482015260240162000a87565b620016448162001951565b50565b6060600a805462001658906200219d565b80601f016020809104026020016040519081016040528092919081815260200182805462001686906200219d565b8015620016d75780601f10620016ab57610100808354040283529160200191620016d7565b820191906000526020600020905b815481529060010190602001808311620016b957829003601f168201915b5050505050905090565b33620017147f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b031614620010bb5760405163118cdaa760e01b815233600482015260240162000a87565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0062000780565b62001773620019c2565b6200164481620019ea565b620010bb620019c2565b306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614806200181257507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166200180660008051602062002b54833981519152546001600160a01b031690565b6001600160a01b031614155b15620010bb5760405163703e46dd60e11b815260040160405180910390fd5b62001644620016e1565b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa92505050801562001898575060408051601f3d908101601f191682019092526200189591810190620026e9565b60015b620018c257604051634c9c8ce360e01b81526001600160a01b038316600482015260240162000a87565b60008051602062002b548339815191528114620018f657604051632a87526960e21b81526004810182905260240162000a87565b620019028383620019f4565b505050565b306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614620010bb5760405163703e46dd60e11b815260040160405180910390fd5b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b620019cc62001a51565b620010bb57604051631afcd79f60e31b815260040160405180910390fd5b6200160d620019c2565b620019ff8262001a6d565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a280511562001a475762001902828262001ad5565b62000afd62001b51565b600062001a5d6200173f565b54600160401b900460ff16919050565b806001600160a01b03163b60000362001aa557604051634c9c8ce360e01b81526001600160a01b038216600482015260240162000a87565b60008051602062002b5483398151915280546001600160a01b0319166001600160a01b0392909216919091179055565b6060600080846001600160a01b03168460405162001af4919062002703565b600060405180830381855af49150503d806000811462001b31576040519150601f19603f3d011682016040523d82523d6000602084013e62001b36565b606091505b509150915062001b4885838362001b71565b95945050505050565b3415620010bb5760405163b398979f60e01b815260040160405180910390fd5b60608262001b8a5762001b848262001bd7565b62001bd0565b815115801562001ba257506001600160a01b0384163b155b1562001bcd57604051639996b31560e01b81526001600160a01b038516600482015260240162000a87565b50805b9392505050565b80511562001be757805160208201fd5b60405163d6bda27560e01b815260040160405180910390fd5b610409806200272283390190565b60006020828403121562001c2157600080fd5b81356001600160e01b03198116811462001bd057600080fd5b60006020828403121562001c4d57600080fd5b5035919050565b60005b8381101562001c7157818101518382015260200162001c57565b50506000910152565b6000815180845262001c9481602086016020860162001c54565b601f01601f19169290920160200192915050565b60808152600062001cbd608083018762001c7a565b828103602084015262001cd1818762001c7a565b9050828103604084015262001ce7818662001c7a565b9050828103606084015262001cfd818562001c7a565b979650505050505050565b80356001600160a01b038116811462001d2057600080fd5b919050565b60006020828403121562001d3857600080fd5b62001bd08262001d08565b634e487b7160e01b600052604160045260246000fd5b604051608081016001600160401b038111828210171562001d7e5762001d7e62001d43565b60405290565b604051601f8201601f191681016001600160401b038111828210171562001daf5762001daf62001d43565b604052919050565b60006001600160401b0382111562001dd35762001dd362001d43565b50601f01601f191660200190565b600062001df862001df28462001db7565b62001d84565b905082815283838301111562001e0d57600080fd5b828260208301376000602084830101529392505050565b600082601f83011262001e3657600080fd5b62001bd08383356020850162001de1565b60006020828403121562001e5a57600080fd5b81356001600160401b0381111562001e7157600080fd5b62001e7f8482850162001e24565b949350505050565b60006020828403121562001e9a57600080fd5b81356001600160401b0381111562001eb157600080fd5b8201610120818503121562001bd057600080fd5b821515815260406020820152600062001e7f604083018462001c7a565b60006020828403121562001ef557600080fd5b81356001600160401b0381111562001f0c57600080fd5b8201601f8101841362001f1e57600080fd5b62001e7f8482356020840162001de1565b6000806040838503121562001f4357600080fd5b62001f4e8362001d08565b915062001f5e6020840162001d08565b90509250929050565b6000806040838503121562001f7b57600080fd5b62001f868362001d08565b915060208301356001600160401b0381111562001fa257600080fd5b62001fb08582860162001e24565b9150509250929050565b6000806020838503121562001fce57600080fd5b82356001600160401b038082111562001fe657600080fd5b818501915085601f83011262001ffb57600080fd5b8135818111156200200b57600080fd5b8660208285010111156200201e57600080fd5b60209290920196919550909350505050565b6000602082840312156200204357600080fd5b81356001600160401b03808211156200205b57600080fd5b90830190608082860312156200207057600080fd5b6200207a62001d59565b8235828111156200208a57600080fd5b620020988782860162001e24565b825250602083013582811115620020ae57600080fd5b620020bc8782860162001e24565b602083015250604083013582811115620020d557600080fd5b620020e38782860162001e24565b604083015250606083013582811115620020fc57600080fd5b6200210a8782860162001e24565b60608301525095945050505050565b80151581146200164457600080fd5b600080600080600060a086880312156200214157600080fd5b6200214c8662001d08565b945060208601356200215e8162002119565b93506040860135620021708162002119565b94979396509394606081013594506080013592915050565b60208152600062001bd0602083018462001c7a565b600181811c90821680620021b257607f821691505b602082108103620021d357634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111562001902576000816000526020600020601f850160051c81016020861015620022045750805b601f850160051c820191505b81811015620022255782815560010162002210565b505050505050565b81516001600160401b0381111562002249576200224962001d43565b62002261816200225a84546200219d565b84620021d9565b602080601f831160018114620022995760008415620022805750858301515b600019600386901b1c1916600185901b17855562002225565b600085815260208120601f198616915b82811015620022ca57888601518255948401946001909101908401620022a9565b5085821015620022e95787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6000808335601e198436030181126200231157600080fd5b83016020810192503590506001600160401b038111156200233157600080fd5b8036038213156200234157600080fd5b9250929050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6000808335601e198436030181126200238957600080fd5b83016020810192503590506001600160401b03811115620023a957600080fd5b8060051b36038213156200234157600080fd5b6000838385526020808601955060208560051b8301018460005b878110156200241657848303601f19018952620023f48288620022f9565b6200240185828462002348565b9a86019a9450505090830190600101620023d6565b5090979650505050505050565b6020815262002447602082016200243a8462001d08565b6001600160a01b03169052565b602082013560408201526000620024616040840162001d08565b6001600160a01b03811660608401525060608301356080830152608083013560a083015260a083013560c083015260c083013560e0830152620024a860e0840184620022f9565b6101206101008181870152620024c46101408701848662002348565b9350620024d48188018862002371565b878603601f1901848901529350905062001cfd848483620023bc565b600080604083850312156200250457600080fd5b8251620025118162002119565b60208401519092506001600160401b038111156200252e57600080fd5b8301601f810185136200254057600080fd5b80516200255162001df28262001db7565b8181528660208385010111156200256757600080fd5b6200257a82602083016020860162001c54565b8093505050509250929050565b6001600160401b03831115620025a157620025a162001d43565b620025b983620025b283546200219d565b83620021d9565b6000601f841160018114620025f05760008515620025d75750838201355b600019600387901b1c1916600186901b1783556200264d565b600083815260209020601f19861690835b8281101562002623578685013582556020948501946001909201910162002601565b5086821015620026415760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b60208152600062001e7f60208301848662002348565b6001600160a01b038316815260406020820181905260009062001e7f9083018462001c7a565b6000808335601e19843603018112620026a857600080fd5b8301803591506001600160401b03821115620026c357600080fd5b6020019150368190038213156200234157600080fd5b8183823760009101908152919050565b600060208284031215620026fc57600080fd5b5051919050565b600082516200271781846020870162001c54565b919091019291505056fe608060405260405161040938038061040983398101604081905261002291610267565b61002c8282610033565b5050610351565b61003c82610092565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a280511561008657610081828261010e565b505050565b61008e610185565b5050565b806001600160a01b03163b6000036100cd57604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b6060600080846001600160a01b03168460405161012b9190610335565b600060405180830381855af49150503d8060008114610166576040519150601f19603f3d011682016040523d82523d6000602084013e61016b565b606091505b50909250905061017c8583836101a6565b95945050505050565b34156101a45760405163b398979f60e01b815260040160405180910390fd5b565b6060826101bb576101b682610205565b6101fe565b81511580156101d257506001600160a01b0384163b155b156101fb57604051639996b31560e01b81526001600160a01b03851660048201526024016100c4565b50805b9392505050565b80511561021457805160208201fd5b60405163d6bda27560e01b815260040160405180910390fd5b634e487b7160e01b600052604160045260246000fd5b60005b8381101561025e578181015183820152602001610246565b50506000910152565b6000806040838503121561027a57600080fd5b82516001600160a01b038116811461029157600080fd5b60208401519092506001600160401b03808211156102ae57600080fd5b818501915085601f8301126102c257600080fd5b8151818111156102d4576102d461022d565b604051601f8201601f19908116603f011681019083821181831017156102fc576102fc61022d565b8160405282815288602084870101111561031557600080fd5b610326836020830160208801610243565b80955050505050509250929050565b60008251610347818460208701610243565b9190910192915050565b60aa8061035f6000396000f3fe6080604052600a600c565b005b60186014601a565b6051565b565b6000604c7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b3660008037600080366000845af43d6000803e808015606f573d6000f35b3d6000fdfea264697066735822122060aeada43c964f1fbb27c2d9cbef9ca57ecc24f36b6aed0059d5371ade8a972f64736f6c634300081600334b4d53206973206e6f7420616c6c6f77656420746f20626f6f74206f6e207468697320646576696365360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc417070206e6f74206465706c6f796564206f7220696e76616c69642061646472657373a26469706673582212208fd2704bd88addf7b3346e2f198929be3ae2f8f5daeb8d512cf8ae5c163fbae264736f6c63430008160033"; type DstackKmsConstructorParams = | [signer?: Signer] diff --git a/kms/auth-eth/typechain-types/factories/contracts/IAppTcbPolicy__factory.ts b/kms/auth-eth/typechain-types/factories/contracts/IAppTcbPolicy__factory.ts new file mode 100644 index 000000000..4a2d051e1 --- /dev/null +++ b/kms/auth-eth/typechain-types/factories/contracts/IAppTcbPolicy__factory.ts @@ -0,0 +1,83 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ + +import { Contract, Interface, type ContractRunner } from "ethers"; +import type { + IAppTcbPolicy, + IAppTcbPolicyInterface, +} from "../../contracts/IAppTcbPolicy"; + +const _abi = [ + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "string", + name: "policy", + type: "string", + }, + ], + name: "TcbPolicySet", + type: "event", + }, + { + inputs: [ + { + internalType: "string", + name: "policy", + type: "string", + }, + ], + name: "setTcbPolicy", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes4", + name: "interfaceId", + type: "bytes4", + }, + ], + name: "supportsInterface", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "tcbPolicy", + outputs: [ + { + internalType: "string", + name: "", + type: "string", + }, + ], + stateMutability: "view", + type: "function", + }, +] as const; + +export class IAppTcbPolicy__factory { + static readonly abi = _abi; + static createInterface(): IAppTcbPolicyInterface { + return new Interface(_abi) as IAppTcbPolicyInterface; + } + static connect( + address: string, + runner?: ContractRunner | null + ): IAppTcbPolicy { + return new Contract(address, _abi, runner) as unknown as IAppTcbPolicy; + } +} diff --git a/kms/auth-eth/typechain-types/factories/contracts/index.ts b/kms/auth-eth/typechain-types/factories/contracts/index.ts index b9c6d07c6..486f3e741 100644 --- a/kms/auth-eth/typechain-types/factories/contracts/index.ts +++ b/kms/auth-eth/typechain-types/factories/contracts/index.ts @@ -5,3 +5,4 @@ export { DstackApp__factory } from "./DstackApp__factory"; export { DstackKms__factory } from "./DstackKms__factory"; export { IAppAuth__factory } from "./IAppAuth__factory"; export { IAppAuthBasicManagement__factory } from "./IAppAuthBasicManagement__factory"; +export { IAppTcbPolicy__factory } from "./IAppTcbPolicy__factory"; diff --git a/kms/auth-eth/typechain-types/hardhat.d.ts b/kms/auth-eth/typechain-types/hardhat.d.ts index 43ee40594..6af0ea783 100644 --- a/kms/auth-eth/typechain-types/hardhat.d.ts +++ b/kms/auth-eth/typechain-types/hardhat.d.ts @@ -85,6 +85,10 @@ declare module "hardhat/types/runtime" { name: "IAppAuthBasicManagement", signerOrOptions?: ethers.Signer | FactoryOptions ): Promise; + getContractFactory( + name: "IAppTcbPolicy", + signerOrOptions?: ethers.Signer | FactoryOptions + ): Promise; getContractAt( name: "OwnableUpgradeable", @@ -176,6 +180,11 @@ declare module "hardhat/types/runtime" { address: string | ethers.Addressable, signer?: ethers.Signer ): Promise; + getContractAt( + name: "IAppTcbPolicy", + address: string | ethers.Addressable, + signer?: ethers.Signer + ): Promise; deployContract( name: "OwnableUpgradeable", @@ -249,6 +258,10 @@ declare module "hardhat/types/runtime" { name: "IAppAuthBasicManagement", signerOrOptions?: ethers.Signer | DeployContractOptions ): Promise; + deployContract( + name: "IAppTcbPolicy", + signerOrOptions?: ethers.Signer | DeployContractOptions + ): Promise; deployContract( name: "OwnableUpgradeable", @@ -340,6 +353,11 @@ declare module "hardhat/types/runtime" { args: any[], signerOrOptions?: ethers.Signer | DeployContractOptions ): Promise; + deployContract( + name: "IAppTcbPolicy", + args: any[], + signerOrOptions?: ethers.Signer | DeployContractOptions + ): Promise; // default types getContractFactory( diff --git a/kms/auth-eth/typechain-types/index.ts b/kms/auth-eth/typechain-types/index.ts index 70c84b150..7ec0b6b2f 100644 --- a/kms/auth-eth/typechain-types/index.ts +++ b/kms/auth-eth/typechain-types/index.ts @@ -42,3 +42,5 @@ export type { IAppAuth } from "./contracts/IAppAuth"; export { IAppAuth__factory } from "./factories/contracts/IAppAuth__factory"; export type { IAppAuthBasicManagement } from "./contracts/IAppAuthBasicManagement"; export { IAppAuthBasicManagement__factory } from "./factories/contracts/IAppAuthBasicManagement__factory"; +export type { IAppTcbPolicy } from "./contracts/IAppTcbPolicy"; +export { IAppTcbPolicy__factory } from "./factories/contracts/IAppTcbPolicy__factory"; diff --git a/kms/e2e/Cargo.toml b/kms/e2e/Cargo.toml new file mode 100644 index 000000000..927e613bc --- /dev/null +++ b/kms/e2e/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "kms-e2e-client" +version = "0.1.0" +edition = "2021" +publish = false + +[[bin]] +name = "kms-e2e-client" +path = "src/main.rs" + +[dependencies] +ra-tls = { workspace = true } +ra-rpc = { workspace = true, features = ["client"] } +dstack-kms-rpc = { workspace = true } +dstack-guest-agent-rpc = { workspace = true } +dstack-attest = { workspace = true } +http-client = { workspace = true, features = ["prpc"] } +anyhow = { workspace = true, features = ["std"] } +serde_json = "1" +serde = { workspace = true } +hex = { workspace = true } +tokio = { version = "1", features = ["rt-multi-thread", "macros"] } +clap = { workspace = true, features = ["env"] } +tracing = { workspace = true } +tracing-subscriber = { workspace = true } +scale = { workspace = true } diff --git a/kms/e2e/kms-test.toml.template b/kms/e2e/kms-test.toml.template new file mode 100644 index 000000000..332b8680a --- /dev/null +++ b/kms/e2e/kms-test.toml.template @@ -0,0 +1,49 @@ +# KMS E2E test configuration template +# Placeholders are replaced by run-e2e.sh at runtime + +[default] +workers = 2 +max_blocking = 8 +ident = "DStack KMS E2E" +temp_dir = "/tmp" +keep_alive = 10 +log_level = "info" + +[rpc] +address = "0.0.0.0" +port = {{KMS_PORT}} + +[rpc.tls] +key = "{{CERT_DIR}}/rpc.key" +certs = "{{CERT_DIR}}/rpc.crt" + +[rpc.tls.mutual] +ca_certs = "{{CERT_DIR}}/tmp-ca.crt" +mandatory = false + +[core] +cert_dir = "{{CERT_DIR}}" +subject_postfix = ".dstack-e2e" +admin_token_hash = "" + +[core.image] +verify = false +cache_dir = "{{IMAGE_CACHE_DIR}}" +download_url = "http://localhost:8000/{OS_IMAGE_HASH}.tar.gz" +download_timeout = "2m" + +[core.auth_api] +type = "webhook" + +[core.auth_api.webhook] +url = "http://127.0.0.1:{{AUTH_ETH_PORT}}" + +[core.auth_api.dev] +gateway_app_id = "any" + +[core.onboard] +enabled = true +auto_bootstrap_domain = "e2e-test.local" +quote_enabled = true +address = "0.0.0.0" +port = {{KMS_PORT}} diff --git a/kms/e2e/run-e2e.sh b/kms/e2e/run-e2e.sh new file mode 100755 index 000000000..994b5c6ae --- /dev/null +++ b/kms/e2e/run-e2e.sh @@ -0,0 +1,640 @@ +#!/bin/bash +# SPDX-FileCopyrightText: © 2025 Phala Network +# +# SPDX-License-Identifier: Apache-2.0 + +# KMS End-to-End Test Script +# Tests the full stack: Hardhat node → contract deployment → auth-eth → KMS +# with real TDX attestation via TEE proxy +# +# Usage: ./kms/e2e/run-e2e.sh [--skip-build] + +set -e + +# ==================== Configuration ==================== + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +AUTH_ETH_DIR="$SCRIPT_DIR/../auth-eth" + +# TEE attestation proxy +export DSTACK_AGENT_ADDRESS="${DSTACK_AGENT_ADDRESS:-https://712eab2f507b963e11144ae67218177e93ac2a24-3000.tdxlab.dstack.org:13004}" + +# Ports: support concurrent runs via env override or random allocation +get_free_port() { + python3 -c 'import socket; s=socket.socket(); s.bind(("",0)); print(s.getsockname()[1]); s.close()' +} +HARDHAT_PORT="${HARDHAT_PORT:-$(get_free_port)}" +AUTH_ETH_PORT="${AUTH_ETH_PORT:-$(get_free_port)}" +KMS_PORT="${KMS_PORT:-$(get_free_port)}" + +# Hardhat account #0 (pre-funded with 10000 ETH) +DEPLOYER_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +# Test counters +TESTS_PASSED=0 +TESTS_FAILED=0 + +# Process tracking +PIDS=() + +# ==================== Logging ==================== + +log_info() { printf "${BLUE}[INFO]${NC} %s\n" "$1"; } +log_warn() { printf "${YELLOW}[WARN]${NC} %s\n" "$1"; } +log_error() { printf "${RED}[ERROR]${NC} %s\n" "$1"; } +log_success() { printf "${GREEN}[PASS]${NC} %s\n" "$1"; } +log_fail() { printf "${RED}[FAIL]${NC} %s\n" "$1"; } + +log_section() { + printf "\n" + log_info "==========================================" + log_info "$1" + log_info "==========================================" +} + +log_phase() { + printf "\n" + log_info "Phase $1: $2" + log_info "------------------------------------------" +} + +# ==================== Test Utilities ==================== + +run_test() { + local name="$1" + local result="$2" + + if [ "$result" = "0" ]; then + log_success "$name" + TESTS_PASSED=$((TESTS_PASSED + 1)) + else + log_fail "$name" + TESTS_FAILED=$((TESTS_FAILED + 1)) + fi +} + +# Wait for HTTP service to respond +wait_for_service() { + local url="$1" + local name="$2" + local max_wait="${3:-60}" + local curl_opts="${4:-}" + local waited=0 + + log_info "Waiting for $name..." + while [ $waited -lt $max_wait ]; do + if curl -sf $curl_opts "$url" > /dev/null 2>&1; then + log_info "$name is ready" + return 0 + fi + sleep 2 + waited=$((waited + 2)) + done + + log_error "$name failed to become ready within ${max_wait}s" + return 1 +} + +cleanup() { + log_info "Cleaning up..." + for pid in "${PIDS[@]}"; do + kill "$pid" 2>/dev/null || true + done + # Wait briefly for processes to exit + sleep 1 + for pid in "${PIDS[@]}"; do + kill -9 "$pid" 2>/dev/null || true + done + if [ -n "$WORK_DIR" ] && [ -d "$WORK_DIR" ]; then + rm -rf "$WORK_DIR" + fi +} +trap cleanup EXIT + +# Set TCB policy on the cloned DstackApp at TEE proxy's address. +# Usage: set_tcb_policy '{"version":1,"intel_qal":[]}' +set_tcb_policy() { + local policy="$1" + (cd "$AUTH_ETH_DIR" && POLICY_VAL="$policy" node -e " +const ethers = require('ethers'); +(async () => { + const provider = new ethers.JsonRpcProvider('$RPC_URL'); + const wallet = new ethers.Wallet('$DEPLOYER_KEY', provider); + const mock = new ethers.Contract( + '$TEE_APP_ID', + ['function setTcbPolicy(string)'], + wallet + ); + await (await mock.setTcbPolicy(process.env.POLICY_VAL)).wait(); +})().catch(e => { console.error(e.message); process.exit(1); }); +") +} + +# Run e2e-client and check if GetAppKey matches expected status. +# Usage: expect_get_app_key "ok" "test description" +# expect_get_app_key "error" "test description" +expect_get_app_key() { + local expected_status="$1" + local test_name="$2" + + local output + output=$("$E2E_CLIENT" test \ + --kms-url "https://127.0.0.1:$KMS_PORT" \ + --vm-config "$TEE_VM_CONFIG" \ + 2>"$WORK_DIR/e2e-policy.log") || true + + local actual_status + actual_status=$(echo "$output" | jq -r 'select(.test == "GetAppKey") | .status' 2>/dev/null) + + if [ "$actual_status" = "$expected_status" ]; then + run_test "$test_name" "0" + else + local error_msg + error_msg=$(echo "$output" | jq -r 'select(.test == "GetAppKey") | .error // "N/A"' 2>/dev/null) + log_info " Expected=$expected_status Got=$actual_status Error=$error_msg" + run_test "$test_name" "1" + fi +} + +# Build a TDX platform TCB policy JSON from a reference object. +# Targets both TDX 1.0 and 1.5 platform class_ids. +# Usage: POLICY=$(make_tdx_policy '{"allow_dynamic_platform":false}') +make_tdx_policy() { + local ref_json="$1" + node -e " +const ref = JSON.parse(process.argv[1]); +const tdx10 = JSON.stringify({environment:{class_id:'9eec018b-7481-4b1c-8e1a-9f7c0c8c777f'},reference:ref}); +const tdx15 = JSON.stringify({environment:{class_id:'f708b97f-0fb2-4e6b-8b03-8a5bcd1221d3'},reference:ref}); +console.log(JSON.stringify({version:1,intel_qal:[tdx10,tdx15]})); +" "$ref_json" +} + +# ==================== Phase 0: Setup ==================== + +log_section "KMS E2E Test" +log_phase 0 "Setup" + +# Parse args +SKIP_BUILD=false +for arg in "$@"; do + case "$arg" in + --skip-build) SKIP_BUILD=true ;; + esac +done + +# Create temp work directory +WORK_DIR=$(mktemp -d) +CERT_DIR="$WORK_DIR/certs" +IMAGE_CACHE_DIR="$WORK_DIR/images" +mkdir -p "$CERT_DIR" "$IMAGE_CACHE_DIR" +log_info "Work directory: $WORK_DIR" +log_info "Ports: hardhat=$HARDHAT_PORT auth-eth=$AUTH_ETH_PORT kms=$KMS_PORT" +log_info "TEE proxy: $DSTACK_AGENT_ADDRESS" + +# Ensure auth-eth dependencies +if [ ! -d "$AUTH_ETH_DIR/node_modules" ]; then + log_info "Installing auth-eth dependencies..." + (cd "$AUTH_ETH_DIR" && npm ci) +fi + +# Build KMS and e2e-client +if [ "$SKIP_BUILD" = false ]; then + log_info "Building KMS and e2e-client..." + (cd "$REPO_ROOT" && cargo build -p dstack-kms -p kms-e2e-client 2>&1 | tail -1) +fi + +KMS_BIN="$REPO_ROOT/target/debug/dstack-kms" +E2E_CLIENT="$REPO_ROOT/target/debug/kms-e2e-client" +if [ ! -x "$KMS_BIN" ]; then + log_error "KMS binary not found at $KMS_BIN" + exit 1 +fi +if [ ! -x "$E2E_CLIENT" ]; then + log_error "E2E client binary not found at $E2E_CLIENT" + exit 1 +fi +log_info "KMS binary: $KMS_BIN" +log_info "E2E client: $E2E_CLIENT" + +# ==================== Phase 1: Probe TEE proxy ==================== + +log_phase 1 "Probe TEE proxy for measurements" + +PROBE_OUTPUT=$("$E2E_CLIENT" probe 2>/dev/null) || { + log_error "Failed to probe TEE proxy" + log_error "Is the TEE proxy reachable at $DSTACK_AGENT_ADDRESS?" + exit 1 +} +log_info "TEE proxy measurements:" +echo "$PROBE_OUTPUT" | head -10 + +# Extract measurements for contract whitelisting +TEE_APP_ID=$(echo "$PROBE_OUTPUT" | jq -r '.app_id') +TEE_COMPOSE_HASH=$(echo "$PROBE_OUTPUT" | jq -r '.compose_hash') +TEE_DEVICE_ID=$(echo "$PROBE_OUTPUT" | jq -r '.device_id') +TEE_MR_AGGREGATED=$(echo "$PROBE_OUTPUT" | jq -r '.mr_aggregated') +TEE_OS_IMAGE_HASH=$(echo "$PROBE_OUTPUT" | jq -r '.os_image_hash') +TEE_VM_CONFIG=$(echo "$PROBE_OUTPUT" | jq -r '.vm_config') + +log_info "app_id: $TEE_APP_ID" +log_info "compose_hash: $TEE_COMPOSE_HASH" +log_info "device_id: $TEE_DEVICE_ID" +log_info "mr_aggregated: $TEE_MR_AGGREGATED" +log_info "os_image_hash: $TEE_OS_IMAGE_HASH" + +# ==================== Phase 2: Start Hardhat & Deploy Contracts ==================== + +log_phase 2 "Start Hardhat node and deploy contracts" + +(cd "$AUTH_ETH_DIR" && npx hardhat node --port $HARDHAT_PORT > "$WORK_DIR/hardhat.log" 2>&1) & +PIDS+=($!) +log_info "Hardhat node PID: ${PIDS[-1]}" + +# Hardhat node responds to JSON-RPC, not plain HTTP GET. Use a POST check. +wait_for_hardhat() { + local max_wait="${1:-60}" + local waited=0 + log_info "Waiting for Hardhat node..." + while [ $waited -lt $max_wait ]; do + if curl -sf -X POST "http://127.0.0.1:$HARDHAT_PORT" \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' > /dev/null 2>&1; then + log_info "Hardhat node is ready" + return 0 + fi + sleep 2 + waited=$((waited + 2)) + done + log_error "Hardhat node failed to become ready within ${max_wait}s" + cat "$WORK_DIR/hardhat.log" | tail -20 + return 1 +} + +wait_for_hardhat + +export RPC_URL="http://127.0.0.1:$HARDHAT_PORT" +export PRIVATE_KEY="$DEPLOYER_KEY" + +cd "$AUTH_ETH_DIR" + +# Deploy DstackKms with DstackApp implementation +log_info "Deploying DstackKms..." +DEPLOY_OUTPUT=$(yes | npx hardhat kms:deploy --with-app-impl --network custom 2>&1) || { + log_error "Contract deployment failed:" + echo "$DEPLOY_OUTPUT" + exit 1 +} + +# Parse KMS contract address from output +KMS_CONTRACT_ADDR=$(echo "$DEPLOY_OUTPUT" | grep -i "proxy" | grep -oE '0x[0-9a-fA-F]{40}' | tail -1) +if [ -z "$KMS_CONTRACT_ADDR" ]; then + log_error "Failed to parse KMS contract address from deploy output:" + echo "$DEPLOY_OUTPUT" + exit 1 +fi +log_info "DstackKms deployed at: $KMS_CONTRACT_ADDR" +export KMS_CONTRACT_ADDRESS="$KMS_CONTRACT_ADDR" + +# Whitelist TEE proxy's MR, OS image hash, and device ID for KMS boot auth +log_info "Adding KMS whitelists (TEE proxy measurements)..." +npx hardhat kms:add "$TEE_MR_AGGREGATED" --network custom > /dev/null 2>&1 +npx hardhat kms:add-image "$TEE_OS_IMAGE_HASH" --network custom > /dev/null 2>&1 +npx hardhat kms:add-device "$TEE_DEVICE_ID" --network custom > /dev/null 2>&1 +log_info "KMS whitelists configured with TEE proxy measurements" + +# Create an app via factory method, whitelisting TEE proxy's compose hash +log_info "Creating DstackApp with TEE proxy compose hash..." +APP_OUTPUT=$(yes | npx hardhat kms:create-app --allow-any-device --hash "$TEE_COMPOSE_HASH" --network custom 2>&1) || { + log_error "App creation failed:" + echo "$APP_OUTPUT" + exit 1 +} + +# Parse app ID (proxy address) from output +APP_ID=$(echo "$APP_OUTPUT" | grep -i "proxy address\|app.id" | grep -oE '0x[0-9a-fA-F]{40}' | head -1) +if [ -z "$APP_ID" ]; then + APP_ID=$(echo "$APP_OUTPUT" | grep -oE '0x[0-9a-fA-F]{40}' | tail -1) +fi +if [ -z "$APP_ID" ]; then + log_error "Failed to parse App ID from output:" + echo "$APP_OUTPUT" + exit 1 +fi +log_info "DstackApp created with ID: $APP_ID" + +# Deploy DstackApp at TEE proxy's app_id address. +# The attestation embeds the TEE proxy's own app_id (not our DstackApp's address), +# so DstackKms.isAppAllowed() needs: registeredApps[teeAppId]=true AND isContract(teeAppId). +# We place the DstackApp implementation bytecode directly (no proxy needed), then initialize. +log_info "Deploying DstackApp at TEE proxy app_id..." +node -e " +const ethers = require('ethers'); +(async () => { + const provider = new ethers.JsonRpcProvider('$RPC_URL'); + const wallet = new ethers.Wallet('$DEPLOYER_KEY', provider); + + // Get DstackApp implementation address from the proxy's EIP-1967 slot + const implSlot = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc'; + const implAddr = '0x' + (await provider.getStorage('$APP_ID', implSlot)).slice(26); + const implCode = await provider.getCode(implAddr); + await provider.send('hardhat_setCode', ['$TEE_APP_ID', implCode]); + + // Initialize (storage is fresh so initializer passes) + const app = new ethers.Contract('$TEE_APP_ID', [ + 'function initialize(address,bool,bool,bytes32,bytes32)', + ], wallet); + await (await app.initialize( + wallet.address, true, true, + '0x' + '0'.repeat(64), '$TEE_COMPOSE_HASH' + )).wait(); + + // Register in DstackKms + const nonce = await provider.getTransactionCount(wallet.address, 'latest'); + const kms = new ethers.Contract('$KMS_CONTRACT_ADDR', ['function registerApp(address)'], wallet); + await (await kms.registerApp('$TEE_APP_ID', { nonce })).wait(); + console.log('DstackApp deployed and registered at: $TEE_APP_ID'); +})().catch(e => { console.error(e.message); process.exit(1); }); +" || { + log_error "Failed to deploy DstackApp at TEE proxy app_id" + exit 1 +} + +# ==================== Phase 3: Start auth-eth Server ==================== + +log_phase 3 "Start auth-eth server" + +cd "$AUTH_ETH_DIR" +ETH_RPC_URL="http://127.0.0.1:$HARDHAT_PORT" \ +KMS_CONTRACT_ADDR="$KMS_CONTRACT_ADDR" \ +PORT=$AUTH_ETH_PORT \ +HOST="127.0.0.1" \ +npx ts-node src/main.ts > "$WORK_DIR/auth-eth.log" 2>&1 & +PIDS+=($!) +log_info "auth-eth PID: ${PIDS[-1]}" + +wait_for_service "http://127.0.0.1:$AUTH_ETH_PORT/" "auth-eth" 30 + +# ==================== Phase 4: Start KMS ==================== + +log_phase 4 "Start KMS (with attestation enabled)" + +# Generate config from template +sed -e "s|{{CERT_DIR}}|$CERT_DIR|g" \ + -e "s|{{IMAGE_CACHE_DIR}}|$IMAGE_CACHE_DIR|g" \ + -e "s|{{KMS_PORT}}|$KMS_PORT|g" \ + -e "s|{{AUTH_ETH_PORT}}|$AUTH_ETH_PORT|g" \ + "$SCRIPT_DIR/kms-test.toml.template" > "$WORK_DIR/kms-test.toml" + +log_info "KMS config written to $WORK_DIR/kms-test.toml" + +cd "$REPO_ROOT" +"$KMS_BIN" -c "$WORK_DIR/kms-test.toml" > "$WORK_DIR/kms.log" 2>&1 & +PIDS+=($!) +log_info "KMS PID: ${PIDS[-1]}" + +# KMS uses HTTPS with self-signed certs +wait_for_service "https://127.0.0.1:$KMS_PORT/prpc/KMS.GetMeta" "KMS" 30 "-k" + +# ==================== Phase 5: Run Tests ==================== + +log_phase 5 "Run tests" + +# Disable set -e so test failures don't abort the script +set +e + +# --- Auth-eth tests --- + +log_info "--- Auth-eth API tests ---" + +# Test 1: Health check +RESPONSE=$(curl -sf "http://127.0.0.1:$AUTH_ETH_PORT/" 2>/dev/null) +echo "$RESPONSE" | jq -e '.kmsContractAddr' > /dev/null 2>&1 +run_test "auth-eth: GET / health check" "$?" + +# Test 2: bootAuth/kms - allowed (whitelisted with TEE proxy measurements) +RESPONSE=$(curl -sf -X POST "http://127.0.0.1:$AUTH_ETH_PORT/bootAuth/kms" \ + -H "Content-Type: application/json" \ + -d "{ + \"tcbStatus\": \"UpToDate\", + \"advisoryIds\": [], + \"mrAggregated\": \"$TEE_MR_AGGREGATED\", + \"mrSystem\": \"0x0000000000000000000000000000000000000000000000000000000000000000\", + \"osImageHash\": \"$TEE_OS_IMAGE_HASH\", + \"appId\": \"0x0000000000000000000000000000000000000000\", + \"composeHash\": \"0x0000000000000000000000000000000000000000000000000000000000000000\", + \"instanceId\": \"0x0000000000000000000000000000000000000000\", + \"deviceId\": \"$TEE_DEVICE_ID\" + }" 2>/dev/null) +echo "$RESPONSE" | jq -e '.isAllowed == true' > /dev/null 2>&1 +run_test "auth-eth: bootAuth/kms allowed (TEE proxy measurements)" "$?" + +# Test 3: bootAuth/kms - rejected (non-whitelisted MR) +RESPONSE=$(curl -sf -X POST "http://127.0.0.1:$AUTH_ETH_PORT/bootAuth/kms" \ + -H "Content-Type: application/json" \ + -d '{ + "tcbStatus": "UpToDate", + "advisoryIds": [], + "mrAggregated": "0x0000000000000000000000000000000000000000000000000000000000000001", + "mrSystem": "0x0000000000000000000000000000000000000000000000000000000000000000", + "osImageHash": "'"$TEE_OS_IMAGE_HASH"'", + "appId": "0x0000000000000000000000000000000000000000", + "composeHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "instanceId": "0x0000000000000000000000000000000000000000", + "deviceId": "'"$TEE_DEVICE_ID"'" + }' 2>/dev/null) +echo "$RESPONSE" | jq -e '.isAllowed == false' > /dev/null 2>&1 +run_test "auth-eth: bootAuth/kms rejected (bad MR)" "$?" + +# Test 4: bootAuth/app - allowed (TEE proxy compose hash, any device) +RESPONSE=$(curl -sf -X POST "http://127.0.0.1:$AUTH_ETH_PORT/bootAuth/app" \ + -H "Content-Type: application/json" \ + -d "{ + \"tcbStatus\": \"UpToDate\", + \"advisoryIds\": [], + \"mrAggregated\": \"0x0000000000000000000000000000000000000000000000000000000000000000\", + \"mrSystem\": \"0x0000000000000000000000000000000000000000000000000000000000000000\", + \"osImageHash\": \"$TEE_OS_IMAGE_HASH\", + \"appId\": \"$APP_ID\", + \"composeHash\": \"$TEE_COMPOSE_HASH\", + \"instanceId\": \"0x0000000000000000000000000000000000000000\", + \"deviceId\": \"0x0000000000000000000000000000000000000000000000000000000000000000\" + }" 2>/dev/null) +echo "$RESPONSE" | jq -e '.isAllowed == true' > /dev/null 2>&1 +run_test "auth-eth: bootAuth/app allowed (TEE proxy compose hash)" "$?" + +# Test 5: bootAuth/app - rejected (wrong compose hash) +RESPONSE=$(curl -sf -X POST "http://127.0.0.1:$AUTH_ETH_PORT/bootAuth/app" \ + -H "Content-Type: application/json" \ + -d "{ + \"tcbStatus\": \"UpToDate\", + \"advisoryIds\": [], + \"mrAggregated\": \"0x0000000000000000000000000000000000000000000000000000000000000000\", + \"mrSystem\": \"0x0000000000000000000000000000000000000000000000000000000000000000\", + \"osImageHash\": \"$TEE_OS_IMAGE_HASH\", + \"appId\": \"$APP_ID\", + \"composeHash\": \"0x2222222222222222222222222222222222222222222222222222222222222222\", + \"instanceId\": \"0x0000000000000000000000000000000000000000\", + \"deviceId\": \"0x0000000000000000000000000000000000000000000000000000000000000000\" + }" 2>/dev/null) +echo "$RESPONSE" | jq -e '.isAllowed == false' > /dev/null 2>&1 +run_test "auth-eth: bootAuth/app rejected (bad compose hash)" "$?" + +# Test 6: GET /policy/kms +RESPONSE=$(curl -sf "http://127.0.0.1:$AUTH_ETH_PORT/policy/kms" 2>/dev/null) +echo "$RESPONSE" | jq -e '.tcbPolicy == ""' > /dev/null 2>&1 +run_test "auth-eth: GET /policy/kms (empty default)" "$?" + +# Test 7: GET /policy/app/:appId +RESPONSE=$(curl -sf "http://127.0.0.1:$AUTH_ETH_PORT/policy/app/$APP_ID" 2>/dev/null) +echo "$RESPONSE" | jq -e '.tcbPolicy == ""' > /dev/null 2>&1 +run_test "auth-eth: GET /policy/app/:appId (empty default)" "$?" + +# --- KMS unauthenticated tests --- + +log_info "--- KMS API tests (unauthenticated) ---" + +# Note: KMS prpc uses snake_case JSON field names (protobuf convention) + +# Test 8: GetMeta (GET request auto-returns JSON) +RESPONSE=$(curl -sk "https://127.0.0.1:$KMS_PORT/prpc/KMS.GetMeta" 2>&1) +( + echo "$RESPONSE" | jq -e '.ca_cert' > /dev/null 2>&1 && + echo "$RESPONSE" | jq -e '.k256_pubkey' > /dev/null 2>&1 && + echo "$RESPONSE" | jq -e '.is_dev == false' > /dev/null 2>&1 +) +run_test "KMS: GetMeta returns metadata" "$?" + +# Test 9: GetAppEnvEncryptPubKey (POST with JSON content type) +APP_ID_HEX=$(echo "$APP_ID" | sed 's/^0x//') +RESPONSE=$(curl -sk "https://127.0.0.1:$KMS_PORT/prpc/KMS.GetAppEnvEncryptPubKey?json" \ + -X POST -H "Content-Type: application/json" \ + -d "{\"app_id\": \"$APP_ID_HEX\"}" 2>&1) +echo "$RESPONSE" | jq -e '.public_key' > /dev/null 2>&1 +run_test "KMS: GetAppEnvEncryptPubKey returns public key" "$?" + +# Test 10: GetTempCaCert (GET request) +RESPONSE=$(curl -sk "https://127.0.0.1:$KMS_PORT/prpc/KMS.GetTempCaCert" 2>&1) +( + echo "$RESPONSE" | jq -e '.temp_ca_cert' > /dev/null 2>&1 && + echo "$RESPONSE" | jq -e '.temp_ca_key' > /dev/null 2>&1 +) +run_test "KMS: GetTempCaCert returns temp CA" "$?" + +# Test 11: GetAppKey without attestation should fail +RESPONSE=$(curl -sk "https://127.0.0.1:$KMS_PORT/prpc/KMS.GetAppKey?json" \ + -X POST -H "Content-Type: application/json" \ + -d '{"api_version": 1, "vm_config": "test"}' 2>&1) +# Should get an error (no app_cert in response) +echo "$RESPONSE" | jq -e '.app_cert' > /dev/null 2>&1 +RESULT=$? +# We expect this to FAIL (no app_cert in response), so invert +if [ "$RESULT" -ne 0 ]; then + run_test "KMS: GetAppKey rejected without attestation" "0" +else + run_test "KMS: GetAppKey rejected without attestation" "1" +fi + +# --- KMS authenticated tests (via TEE proxy attestation) --- + +log_info "--- KMS API tests (with RA-TLS attestation) ---" + +E2E_OUTPUT=$("$E2E_CLIENT" test \ + --kms-url "https://127.0.0.1:$KMS_PORT" \ + --vm-config "$TEE_VM_CONFIG" \ + 2>"$WORK_DIR/e2e-client.log") +E2E_EXIT=$? + +if [ $E2E_EXIT -ne 0 ]; then + log_error "E2E client failed to run:" + cat "$WORK_DIR/e2e-client.log" + run_test "KMS: RA-TLS client setup" "1" +else + # Parse each test result from JSON lines + while IFS= read -r line; do + TEST_NAME=$(echo "$line" | jq -r '.test // empty' 2>/dev/null) + TEST_STATUS=$(echo "$line" | jq -r '.status // empty' 2>/dev/null) + if [ -n "$TEST_NAME" ]; then + if [ "$TEST_STATUS" = "ok" ]; then + run_test "KMS: $TEST_NAME (RA-TLS)" "0" + else + TEST_ERROR=$(echo "$line" | jq -r '.error // "unknown error"' 2>/dev/null) + log_info " Error: $TEST_ERROR" + run_test "KMS: $TEST_NAME (RA-TLS)" "1" + fi + fi + done <<< "$E2E_OUTPUT" +fi + +# --- KMS TCB policy tests (via cloned DstackApp at TEE proxy's app_id) --- + +log_info "--- KMS TCB policy tests ---" + +# Policy: empty intel_qal array → no additional validation → pass +set_tcb_policy '{"version":1,"intel_qal":[]}' +expect_get_app_key "ok" "Policy: empty intel_qal allows GetAppKey" + +# Policy: unknown version → fail-close +set_tcb_policy '{"version":99}' +expect_get_app_key "error" "Policy: unsupported version rejects GetAppKey" + +# Policy: malformed JSON → parse error → fail +set_tcb_policy 'not-json' +expect_get_app_key "error" "Policy: malformed JSON rejects GetAppKey" + +# Policy: invalid Rego entries in intel_qal → build error → fail +set_tcb_policy '{"version":1,"intel_qal":["not valid rego"]}' +expect_get_app_key "error" "Policy: invalid Rego entry rejects GetAppKey" + +# Policy: cleared (empty string) → back to no policy → pass +set_tcb_policy '' +expect_get_app_key "ok" "Policy: cleared policy allows GetAppKey" + +# --- Intel QAL rule-level policy tests --- +# The TEE proxy's platform has: dynamic_platform=true, cached_keys=true. +# These policies target both TDX 1.0 and 1.5 platform class_ids. + +log_info "--- KMS Intel QAL rule tests ---" + +# Broad reference: permissive policy that accepts everything → pass +PERMISSIVE_REF='{"accepted_tcb_status":["UpToDate","OutOfDate","SWHardeningNeeded","ConfigurationAndSWHardeningNeeded","OutOfDateConfigurationNeeded"],"allow_dynamic_platform":true,"allow_cached_keys":true,"allow_smt_enabled":true,"collateral_grace_period":2592000}' +set_tcb_policy "$(make_tdx_policy "$PERMISSIVE_REF")" +expect_get_app_key "ok" "QAL rule: permissive policy allows GetAppKey" + +# Reject dynamic_platform → fail (TEE proxy has dynamic_platform=true) +REJECT_DYN_REF='{"accepted_tcb_status":["UpToDate","OutOfDate","SWHardeningNeeded","ConfigurationAndSWHardeningNeeded","OutOfDateConfigurationNeeded"],"allow_dynamic_platform":false,"allow_cached_keys":true,"allow_smt_enabled":true,"collateral_grace_period":2592000}' +set_tcb_policy "$(make_tdx_policy "$REJECT_DYN_REF")" +expect_get_app_key "error" "QAL rule: reject dynamic_platform blocks GetAppKey" + +# Reject cached_keys → fail (TEE proxy has cached_keys=true) +REJECT_CK_REF='{"accepted_tcb_status":["UpToDate","OutOfDate","SWHardeningNeeded","ConfigurationAndSWHardeningNeeded","OutOfDateConfigurationNeeded"],"allow_dynamic_platform":true,"allow_cached_keys":false,"allow_smt_enabled":true,"collateral_grace_period":2592000}' +set_tcb_policy "$(make_tdx_policy "$REJECT_CK_REF")" +expect_get_app_key "error" "QAL rule: reject cached_keys blocks GetAppKey" + +# Only accept SWHardeningNeeded → fail (TEE proxy status is UpToDate, not in accepted list) +WRONG_TCB_REF='{"accepted_tcb_status":["SWHardeningNeeded"],"allow_dynamic_platform":true,"allow_cached_keys":true,"allow_smt_enabled":true,"collateral_grace_period":0}' +set_tcb_policy "$(make_tdx_policy "$WRONG_TCB_REF")" +expect_get_app_key "error" "QAL rule: mismatched TCB status blocks GetAppKey" + +# Clean up: clear policy for any subsequent tests +set_tcb_policy '' + +# ==================== Phase 6: Summary ==================== + +log_section "Test Summary" +log_info "Passed: $TESTS_PASSED" +if [ "$TESTS_FAILED" -gt 0 ]; then + log_error "Failed: $TESTS_FAILED" +else + log_info "Failed: $TESTS_FAILED" +fi +log_info "Total: $((TESTS_PASSED + TESTS_FAILED))" + +exit "$TESTS_FAILED" diff --git a/kms/e2e/src/main.rs b/kms/e2e/src/main.rs new file mode 100644 index 000000000..5a90e94a1 --- /dev/null +++ b/kms/e2e/src/main.rs @@ -0,0 +1,247 @@ +use anyhow::{Context, Result}; +use clap::{Parser, Subcommand}; +use dstack_guest_agent_rpc::{dstack_guest_client::DstackGuestClient, RawQuoteArgs}; +use dstack_kms_rpc::{kms_client::KmsClient, GetAppKeyRequest, SignCertRequest}; +use http_client::prpc::PrpcClient; +use ra_rpc::client::{RaClient, RaClientConfig}; +use ra_tls::{ + attestation::{QuoteContentType, VersionedAttestation}, + cert::{CaCert, CertConfigV2, CertRequest, CertSigningRequestV2, Csr}, + rcgen::{KeyPair, PKCS_ECDSA_P256_SHA256}, +}; +use scale::Decode; +use serde_json::json; + +#[derive(Parser)] +#[command(name = "kms-e2e-client")] +struct Cli { + #[command(subcommand)] + command: Commands, +} + +#[derive(Subcommand)] +enum Commands { + /// Probe TEE proxy to discover measurements for contract whitelisting + Probe { + /// Guest agent address (DSTACK_AGENT_ADDRESS) + #[arg(long, env = "DSTACK_AGENT_ADDRESS")] + agent_url: String, + }, + /// Run authenticated KMS tests using TEE proxy attestation + Test { + /// KMS URL (https://...) + #[arg(long)] + kms_url: String, + /// Guest agent address (DSTACK_AGENT_ADDRESS) + #[arg(long, env = "DSTACK_AGENT_ADDRESS")] + agent_url: String, + /// VM config JSON string to pass to GetAppKey + #[arg(long, default_value = "{}")] + vm_config: String, + }, +} + +fn dstack_client(agent_url: &str) -> DstackGuestClient { + let http_client = PrpcClient::new(agent_url.to_string()); + DstackGuestClient::new(http_client) +} + +async fn gen_ra_cert( + agent_url: &str, + ca_cert_pem: String, + ca_key_pem: String, +) -> Result<(String, String)> { + let ca = CaCert::new(ca_cert_pem, ca_key_pem)?; + let key = KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256)?; + let pubkey = key.public_key_der(); + let report_data = QuoteContentType::RaTlsCert.to_report_data(&pubkey); + let response = dstack_client(agent_url) + .attest(RawQuoteArgs { + report_data: report_data.to_vec(), + }) + .await + .context("Failed to get quote from TEE proxy")?; + let attestation = VersionedAttestation::decode(&mut &response.attestation[..]) + .context("Invalid attestation")?; + let req = CertRequest::builder() + .subject("RA-TLS E2E Test Cert") + .attestation(&attestation) + .key(&key) + .build(); + let cert = ca.sign(req).context("Failed to sign certificate")?; + Ok((cert.pem(), key.serialize_pem())) +} + +async fn cmd_probe(agent_url: &str) -> Result<()> { + let client = dstack_client(agent_url); + + // Get app info from the TEE proxy + let info = client.info().await.context("Failed to get info from TEE proxy")?; + + let result = json!({ + "app_id": format!("0x{}", hex::encode(&info.app_id)), + "instance_id": format!("0x{}", hex::encode(&info.instance_id)), + "device_id": format!("0x{}", hex::encode(&info.device_id)), + "mr_aggregated": format!("0x{}", hex::encode(&info.mr_aggregated)), + "os_image_hash": format!("0x{}", hex::encode(&info.os_image_hash)), + "compose_hash": format!("0x{}", hex::encode(&info.compose_hash)), + "vm_config": info.vm_config, + }); + + println!("{}", serde_json::to_string_pretty(&result)?); + Ok(()) +} + +async fn cmd_test(kms_url: &str, agent_url: &str, vm_config: &str) -> Result<()> { + // RaClient expects the URL to include /prpc suffix + let kms_prpc_url = format!("{}/prpc", kms_url.trim_end_matches('/')); + + // Step 1: Get temp CA cert from KMS (unauthenticated) + eprintln!("Getting temp CA cert from KMS..."); + let kms_client_noauth = RaClient::new(kms_prpc_url.clone(), true)?; + let kms_noauth = KmsClient::new(kms_client_noauth); + let tmp_ca = kms_noauth + .get_temp_ca_cert() + .await + .context("Failed to get temp CA cert")?; + eprintln!("Got temp CA cert"); + + // Step 2: Get KMS root CA for TLS verification + let meta = kms_noauth + .get_meta() + .await + .context("Failed to get KMS metadata")?; + let root_ca_cert = meta.ca_cert.clone(); + eprintln!("Got KMS root CA"); + + // Step 3: Generate RA-TLS cert using TEE proxy + eprintln!("Generating RA-TLS cert via TEE proxy..."); + let (ra_cert, ra_key) = gen_ra_cert(agent_url, tmp_ca.temp_ca_cert, tmp_ca.temp_ca_key) + .await + .context("Failed to generate RA cert")?; + eprintln!("RA-TLS cert generated"); + + // Step 4: Create authenticated mTLS client + let ra_client = RaClientConfig::builder() + .remote_uri(kms_prpc_url.clone()) + .tls_client_cert(ra_cert) + .tls_client_key(ra_key) + .tls_ca_cert(root_ca_cert) + .tls_built_in_root_certs(false) + .tls_no_check_hostname(true) + .verify_server_attestation(false) + .build() + .into_client() + .context("Failed to create RA client")?; + let kms_auth = KmsClient::new(ra_client); + + // Test: GetAppKey + eprintln!("Testing GetAppKey..."); + let result = kms_auth + .get_app_key(GetAppKeyRequest { + api_version: 1, + vm_config: vm_config.to_string(), + }) + .await; + match &result { + Ok(resp) => { + let output = json!({ + "test": "GetAppKey", + "status": "ok", + "has_disk_crypt_key": !resp.disk_crypt_key.is_empty(), + "has_env_crypt_key": !resp.env_crypt_key.is_empty(), + "has_k256_key": !resp.k256_key.is_empty(), + "gateway_app_id": resp.gateway_app_id, + }); + println!("{}", serde_json::to_string(&output)?); + } + Err(e) => { + let output = json!({ + "test": "GetAppKey", + "status": "error", + "error": format!("{e:#}"), + }); + println!("{}", serde_json::to_string(&output)?); + } + } + + // Test: SignCert + eprintln!("Testing SignCert..."); + let sign_key = KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256)?; + let pubkey = sign_key.public_key_der(); + let report_data = QuoteContentType::RaTlsCert.to_report_data(&pubkey); + let attest_response = dstack_client(agent_url) + .attest(RawQuoteArgs { + report_data: report_data.to_vec(), + }) + .await + .context("Failed to get quote for SignCert")?; + let attestation = VersionedAttestation::decode(&mut &attest_response.attestation[..]) + .context("Invalid attestation for SignCert")?; + let csr = CertSigningRequestV2 { + confirm: "please sign cert:".to_string(), + pubkey, + config: CertConfigV2 { + org_name: None, + subject: "e2e-test.dstack".to_string(), + subject_alt_names: vec![], + usage_server_auth: true, + usage_client_auth: false, + ext_quote: true, + ext_app_info: false, + not_before: None, + not_after: None, + }, + attestation, + }; + let signature = csr.signed_by(&sign_key).context("Failed to sign CSR")?; + let result = kms_auth + .sign_cert(SignCertRequest { + api_version: 2, + csr: csr.to_vec(), + signature, + vm_config: vm_config.to_string(), + }) + .await; + match &result { + Ok(resp) => { + let output = json!({ + "test": "SignCert", + "status": "ok", + "cert_chain_len": resp.certificate_chain.len(), + }); + println!("{}", serde_json::to_string(&output)?); + } + Err(e) => { + let output = json!({ + "test": "SignCert", + "status": "error", + "error": format!("{e:#}"), + }); + println!("{}", serde_json::to_string(&output)?); + } + } + + Ok(()) +} + +#[tokio::main] +async fn main() -> Result<()> { + tracing_subscriber::fmt() + .with_env_filter( + tracing_subscriber::EnvFilter::try_from_default_env() + .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("warn")), + ) + .with_writer(std::io::stderr) + .init(); + + let cli = Cli::parse(); + match &cli.command { + Commands::Probe { agent_url } => cmd_probe(agent_url).await, + Commands::Test { + kms_url, + agent_url, + vm_config, + } => cmd_test(kms_url, agent_url, vm_config).await, + } +} diff --git a/kms/src/main_service.rs b/kms/src/main_service.rs index 52573d4b2..bcf93693e 100644 --- a/kms/src/main_service.rs +++ b/kms/src/main_service.rs @@ -5,6 +5,7 @@ use std::{path::PathBuf, sync::Arc}; use anyhow::{bail, Context, Result}; +use dcap_qvl::{verify::QuoteVerificationResult, Policy as _, RegoPolicySet}; use dstack_kms_rpc::{ kms_server::{KmsRpc, KmsServer}, AppId, AppKeyResponse, ClearImageCacheRequest, GetAppKeyRequest, GetKmsKeyRequest, @@ -32,6 +33,49 @@ use crate::{ mod upgrade_authority; +/// On-chain TCB policy document. Versioned JSON wrapper for extensibility. +/// +/// Format: `{"version": 1, "intel_qal": ["", ...]}` +/// +/// Fail-close: unknown versions are rejected. +#[derive(serde::Deserialize)] +struct TcbPolicyDoc { + version: u32, + #[serde(default)] + intel_qal: Vec, +} + +/// Validate a TDX QuoteVerificationResult against an on-chain TCB policy. +/// +/// If `policy_json` is empty, no additional validation is performed (backward compatible +/// with old contracts that don't set a policy). +fn validate_onchain_tcb_policy(qvr: &QuoteVerificationResult, policy_json: &str) -> Result<()> { + if policy_json.is_empty() { + return Ok(()); + } + let doc: TcbPolicyDoc = + serde_json::from_str(policy_json).context("Failed to parse on-chain TCB policy JSON")?; + if doc.version != 1 { + bail!( + "Unsupported TCB policy version {}: refusing to proceed (fail-close)", + doc.version + ); + } + if doc.intel_qal.is_empty() { + return Ok(()); + } + let policy_refs: Vec<&str> = doc.intel_qal.iter().map(|s| s.as_str()).collect(); + let policy_set = RegoPolicySet::new(&policy_refs) + .context("Failed to build RegoPolicySet from on-chain policy")?; + let supplemental = qvr + .supplemental() + .context("Failed to build supplemental data for on-chain policy validation")?; + policy_set + .validate(&supplemental) + .context("On-chain TCB policy validation failed")?; + Ok(()) +} + #[derive(Clone)] pub struct KmsState { inner: Arc, @@ -169,17 +213,15 @@ impl RpcHandler { use_boottime_mr: bool, vm_config_str: &str, ) -> Result { - let tcb_status; - let advisory_ids; - match att.report.tdx_report() { - Some(report) => { - tcb_status = report.status.clone(); - advisory_ids = report.advisory_ids.clone(); - } - None => { - tcb_status = "".to_string(); - advisory_ids = Vec::new(); + let (tcb_status, advisory_ids) = match att.report.tdx_qvr() { + Some(qvr) => { + let supplemental = qvr.supplemental().context("Failed to build supplemental")?; + ( + supplemental.tcb.status.to_string(), + supplemental.tcb.advisory_ids, + ) } + None => (String::new(), Vec::new()), }; let app_info = att.decode_app_info_ex(use_boottime_mr, vm_config_str)?; let boot_info = BootInfo { @@ -195,6 +237,23 @@ impl RpcHandler { tcb_status, advisory_ids, }; + + // Validate on-chain TCB policy before contract-level boot authorization + if let Some(qvr) = att.report.tdx_qvr() { + let policy = if is_kms { + self.state.config.auth_api.get_kms_policy().await? + } else { + let app_id_hex = hex::encode(&boot_info.app_id); + self.state + .config + .auth_api + .get_app_policy(&format!("0x{app_id_hex}")) + .await? + }; + validate_onchain_tcb_policy(qvr, &policy.tcb_policy) + .context("On-chain TCB policy check failed")?; + } + let response = self .state .config @@ -432,3 +491,91 @@ impl RpcCall for RpcHandler { pub fn rpc_methods() -> &'static [&'static str] { >::supported_methods() } + +#[cfg(test)] +mod tests { + use super::*; + + /// Build a QuoteVerificationResult from the bundled TDX sample fixtures. + fn sample_qvr() -> QuoteVerificationResult { + let raw_quote = include_bytes!("../tests/fixtures/tdx_quote"); + let collateral: dcap_qvl::QuoteCollateralV3 = + serde_json::from_slice(include_bytes!("../tests/fixtures/tdx_quote_collateral.json")) + .expect("parse collateral"); + // Timestamp within the collateral validity window + let now = 1_750_400_000; + dcap_qvl::verify::QuoteVerifier::new_prod_default_crypto() + .verify(raw_quote, collateral, now) + .expect("verify sample quote") + } + + #[test] + fn empty_policy_is_noop() { + let qvr = sample_qvr(); + validate_onchain_tcb_policy(&qvr, "").unwrap(); + } + + #[test] + fn version1_empty_intel_qal_is_noop() { + let qvr = sample_qvr(); + validate_onchain_tcb_policy(&qvr, r#"{"version":1,"intel_qal":[]}"#).unwrap(); + } + + #[test] + fn unknown_version_is_rejected() { + let qvr = sample_qvr(); + let err = validate_onchain_tcb_policy(&qvr, r#"{"version":99,"intel_qal":[]}"#) + .unwrap_err(); + assert!( + err.to_string().contains("Unsupported TCB policy version 99"), + "unexpected error: {err}" + ); + } + + #[test] + fn malformed_json_is_rejected() { + let qvr = sample_qvr(); + let err = validate_onchain_tcb_policy(&qvr, "not json").unwrap_err(); + assert!( + err.to_string().contains("Failed to parse"), + "unexpected error: {err}" + ); + } + + #[test] + fn missing_version_field_is_rejected() { + let qvr = sample_qvr(); + let err = validate_onchain_tcb_policy(&qvr, r#"{"intel_qal":[]}"#).unwrap_err(); + // version is required — serde fails, wrapped with "Failed to parse" context + assert!( + err.to_string().contains("Failed to parse"), + "unexpected error: {err}" + ); + } + + #[test] + fn tcb_policy_doc_deserialization() { + let doc: TcbPolicyDoc = + serde_json::from_str(r#"{"version":1,"intel_qal":["policy1","policy2"]}"#).unwrap(); + assert_eq!(doc.version, 1); + assert_eq!(doc.intel_qal.len(), 2); + + // intel_qal defaults to empty when omitted + let doc: TcbPolicyDoc = serde_json::from_str(r#"{"version":1}"#).unwrap(); + assert!(doc.intel_qal.is_empty()); + } + + #[test] + fn invalid_rego_policy_is_rejected() { + let qvr = sample_qvr(); + let err = validate_onchain_tcb_policy( + &qvr, + r#"{"version":1,"intel_qal":["not valid rego json"]}"#, + ) + .unwrap_err(); + assert!( + err.to_string().contains("Failed to build RegoPolicySet"), + "unexpected error: {err}" + ); + } +} diff --git a/kms/src/main_service/upgrade_authority.rs b/kms/src/main_service/upgrade_authority.rs index b461e8ad0..19b564dd7 100644 --- a/kms/src/main_service/upgrade_authority.rs +++ b/kms/src/main_service/upgrade_authority.rs @@ -41,6 +41,13 @@ pub(crate) struct BootResponse { pub reason: String, } +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct PolicyResponse { + #[serde(default)] + pub tcb_policy: String, +} + #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub(crate) struct AuthApiInfoResponse { @@ -103,6 +110,30 @@ impl AuthApi { } } + pub async fn get_app_policy(&self, app_id: &str) -> Result { + match self { + AuthApi::Dev { .. } => Ok(PolicyResponse { + tcb_policy: String::new(), + }), + AuthApi::Webhook { webhook } => { + let url = url_join(&webhook.url, &format!("policy/app/{app_id}")); + http_get(&url).await + } + } + } + + pub async fn get_kms_policy(&self) -> Result { + match self { + AuthApi::Dev { .. } => Ok(PolicyResponse { + tcb_policy: String::new(), + }), + AuthApi::Webhook { webhook } => { + let url = url_join(&webhook.url, "policy/kms"); + http_get(&url).await + } + } + } + pub async fn get_info(&self) -> Result { match self { AuthApi::Dev { dev } => Ok(GetInfoResponse { @@ -134,3 +165,58 @@ fn url_join(url: &str, path: &str) -> String { url.push_str(path); url } + +#[cfg(test)] +mod tests { + use super::*; + + /// BootInfo: KMS is the producer (serializer). + /// Verify that Rust serialization output matches the shared fixture exactly. + #[test] + fn boot_info_serialization_matches_fixture() { + let fixture_json = include_str!("../../tests/fixtures/boot_info.json"); + // Deserialize to construct the value, then re-serialize and compare. + // This proves Rust's serialization produces JSON that TS can consume. + let info: BootInfo = + serde_json::from_str(fixture_json).expect("deserialize BootInfo from fixture"); + let serialized = serde_json::to_value(&info).expect("serialize BootInfo"); + let expected: serde_json::Value = + serde_json::from_str(fixture_json).expect("parse fixture"); + assert_eq!( + serialized, expected, + "Rust BootInfo serialization must match fixture (consumed by TS auth-eth)" + ); + } + + /// BootResponse: auth-eth is the producer, KMS is the consumer (deserializer). + /// Verify that Rust can deserialize the shared fixture correctly. + #[test] + fn boot_response_deserialization_from_fixture() { + let fixture_json = include_str!("../../tests/fixtures/boot_response.json"); + let resp: BootResponse = + serde_json::from_str(fixture_json).expect("deserialize BootResponse from fixture"); + assert!(resp.is_allowed); + assert_eq!(resp.gateway_app_id, "0x1234567890abcdef1234567890abcdef12345678"); + assert!(resp.reason.is_empty()); + } + + /// PolicyResponse: auth-eth is the producer, KMS is the consumer (deserializer). + /// Verify that Rust can deserialize the shared fixture correctly. + #[test] + fn policy_response_deserialization_from_fixture() { + let fixture_json = include_str!("../../tests/fixtures/policy_response.json"); + let resp: PolicyResponse = + serde_json::from_str(fixture_json).expect("deserialize PolicyResponse from fixture"); + assert!(!resp.tcb_policy.is_empty()); + // Verify the embedded JSON is valid + let policy: serde_json::Value = + serde_json::from_str(&resp.tcb_policy).expect("parse embedded tcbPolicy JSON"); + assert_eq!(policy["version"], 1); + } + + #[test] + fn url_join_appends_path_correctly() { + assert_eq!(url_join("http://host", "path"), "http://host/path"); + assert_eq!(url_join("http://host/", "path"), "http://host/path"); + } +} diff --git a/kms/tests/fixtures/boot_info.json b/kms/tests/fixtures/boot_info.json new file mode 100644 index 000000000..cc6ef65af --- /dev/null +++ b/kms/tests/fixtures/boot_info.json @@ -0,0 +1,13 @@ +{ + "attestationMode": "dstack-tdx", + "mrAggregated": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "osImageHash": "abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890", + "mrSystem": "9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba", + "appId": "1234567890123456789012345678901234567890", + "composeHash": "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef", + "instanceId": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "deviceId": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "keyProviderInfo": "", + "tcbStatus": "UpToDate", + "advisoryIds": ["INTEL-SA-00001"] +} diff --git a/kms/tests/fixtures/boot_response.json b/kms/tests/fixtures/boot_response.json new file mode 100644 index 000000000..8ec33397a --- /dev/null +++ b/kms/tests/fixtures/boot_response.json @@ -0,0 +1,5 @@ +{ + "isAllowed": true, + "gatewayAppId": "0x1234567890abcdef1234567890abcdef12345678", + "reason": "" +} diff --git a/kms/tests/fixtures/policy_response.json b/kms/tests/fixtures/policy_response.json new file mode 100644 index 000000000..5da816ede --- /dev/null +++ b/kms/tests/fixtures/policy_response.json @@ -0,0 +1,3 @@ +{ + "tcbPolicy": "{\"version\":1,\"intel_qal\":[]}" +} diff --git a/kms/tests/fixtures/tdx_quote b/kms/tests/fixtures/tdx_quote new file mode 100644 index 000000000..667f510d9 Binary files /dev/null and b/kms/tests/fixtures/tdx_quote differ diff --git a/kms/tests/fixtures/tdx_quote_collateral.json b/kms/tests/fixtures/tdx_quote_collateral.json new file mode 100644 index 000000000..386a61962 --- /dev/null +++ b/kms/tests/fixtures/tdx_quote_collateral.json @@ -0,0 +1,11 @@ +{ + "pck_crl_issuer_chain": "-----BEGIN CERTIFICATE-----\nMIICljCCAj2gAwIBAgIVAJVvXc29G+HpQEnJ1PQzzgFXC95UMAoGCCqGSM49BAMC\nMGgxGjAYBgNVBAMMEUludGVsIFNHWCBSb290IENBMRowGAYDVQQKDBFJbnRlbCBD\nb3Jwb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQsw\nCQYDVQQGEwJVUzAeFw0xODA1MjExMDUwMTBaFw0zMzA1MjExMDUwMTBaMHAxIjAg\nBgNVBAMMGUludGVsIFNHWCBQQ0sgUGxhdGZvcm0gQ0ExGjAYBgNVBAoMEUludGVs\nIENvcnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0Ex\nCzAJBgNVBAYTAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENSB/7t21lXSO\n2Cuzpxw74eJB72EyDGgW5rXCtx2tVTLq6hKk6z+UiRZCnqR7psOvgqFeSxlmTlJl\neTmi2WYz3qOBuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBS\nBgNVHR8ESzBJMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2Vy\ndmljZXMuaW50ZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUlW9d\nzb0b4elAScnU9DPOAVcL3lQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYB\nAf8CAQAwCgYIKoZIzj0EAwIDRwAwRAIgXsVki0w+i6VYGW3UF/22uaXe0YJDj1Ue\nnA+TjD1ai5cCICYb1SAmD5xkfTVpvo4UoyiSYxrDWLmUR4CI9NKyfPN+\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG\nA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0\naW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT\nAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7\n1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB\nuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ\nMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50\nZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV\nUr9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI\nKoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg\nAiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=\n-----END CERTIFICATE-----\n", + "root_ca_crl": "308201203081c8020101300a06082a8648ce3d0403023068311a301806035504030c11496e74656c2053475820526f6f74204341311a3018060355040a0c11496e74656c20436f72706f726174696f6e3114301206035504070c0b53616e746120436c617261310b300906035504080c024341310b3009060355040613025553170d3235303332303131323135375a170d3236303430333131323135375aa02f302d300a0603551d140403020101301f0603551d2304183016801422650cd65a9d3489f383b49552bf501b392706ac300a06082a8648ce3d0403020347003044022030c9fce1438da0a94e4fffdd46c9650e393be6e5a7862d4e4e73527932d04af302206539efe3f734c3d7df20d9dfc4630e1c7ff0439a0f8ece101f15b5eaff9b4f33", + "pck_crl": "30820a6330820a08020101300a06082a8648ce3d04030230703122302006035504030c19496e74656c205347582050434b20506c6174666f726d204341311a3018060355040a0c11496e74656c20436f72706f726174696f6e3114301206035504070c0b53616e746120436c617261310b300906035504080c024341310b3009060355040613025553170d3235303631393130303033355a170d3235303731393130303033355a30820934303302146fc34e5023e728923435d61aa4b83c618166ad35170d3235303631393130303033355a300c300a0603551d1504030a01013034021500efae6e9715fca13b87e333e8261ed6d990a926ad170d3235303631393130303033355a300c300a0603551d1504030a01013034021500fd608648629cba73078b4d492f4b3ea741ad08cd170d3235303631393130303033355a300c300a0603551d1504030a010130340215008af924184e1d5afddd73c3d63a12f5e8b5737e56170d3235303631393130303033355a300c300a0603551d1504030a01013034021500b1257978cfa9ccdd0759abf8c5ca72fae3a78a9b170d3235303631393130303033355a300c300a0603551d1504030a01013033021474fea614a972be0e2843f2059835811ed872f9b3170d3235303631393130303033355a300c300a0603551d1504030a01013034021500f9c4ef56b3ab48d577e108baedf4bf88014214b9170d3235303631393130303033355a300c300a0603551d1504030a010130330214071de0778f9e5fc4f2878f30d6b07c9a30e6b30b170d3235303631393130303033355a300c300a0603551d1504030a01013034021500cde2424f972cea94ff239937f4d80c25029dd60b170d3235303631393130303033355a300c300a0603551d1504030a0101303302146c3319e5109b64507d3cf1132ce00349ef527319170d3235303631393130303033355a300c300a0603551d1504030a01013034021500df08d756b66a7497f43b5bb58ada04d3f4f7a937170d3235303631393130303033355a300c300a0603551d1504030a01013033021428af485b6cf67e409a39d5cb5aee4598f7a8fa7b170d3235303631393130303033355a300c300a0603551d1504030a01013034021500fb8b2daec092cada8aa9bc4ff2f1c20d0346668c170d3235303631393130303033355a300c300a0603551d1504030a01013034021500cd4850ac52bdcc69a6a6f058c8bc57bbd0b5f864170d3235303631393130303033355a300c300a0603551d1504030a01013034021500994dd3666f5275fb805f95dd02bd50cb2679d8ad170d3235303631393130303033355a300c300a0603551d1504030a0101303302140702136900252274d9035eedf5457462fad0ef4c170d3235303631393130303033355a300c300a0603551d1504030a01013033021461f2bf73e39b4e04aa27d801bd73d24319b5bf80170d3235303631393130303033355a300c300a0603551d1504030a0101303302143992be851b96902eff38959e6c2eff1b0651a4b5170d3235303631393130303033355a300c300a0603551d1504030a0101303302140fda43a00b68ea79b7c2deaeac0b498bdfb2af90170d3235303631393130303033355a300c300a0603551d1504030a010130330214639f139a5040fdcff191e8a4fb1bf086ed603971170d3235303631393130303033355a300c300a0603551d1504030a01013034021500959d533f9249dc1e513544cdc830bf19b7f1f301170d3235303631393130303033355a300c300a0603551d1504030a0101303302147ae37748a9f912f4c63ba7ab07c593ce1d1d1181170d3235303631393130303033355a300c300a0603551d1504030a01013033021413884b33269938c195aa170fca75da177538df0b170d3235303631393130303033355a300c300a0603551d1504030a0101303402150085d3c9381b77a7e04d119c9e5ad6749ff3ffab87170d3235303631393130303033355a300c300a0603551d1504030a0101303402150093887ca4411e7a923bd1fed2819b2949f201b5b4170d3235303631393130303033355a300c300a0603551d1504030a0101303302142498dc6283930996fd8bf23a37acbe26a3bed457170d3235303631393130303033355a300c300a0603551d1504030a010130340215008a66f1a749488667689cc3903ac54c662b712e73170d3235303631393130303033355a300c300a0603551d1504030a01013034021500afc13610bdd36cb7985d106481a880d3a01fda07170d3235303631393130303033355a300c300a0603551d1504030a01013034021500efe04b2c33d036aac96ca673bf1e9a47b64d5cbb170d3235303631393130303033355a300c300a0603551d1504030a0101303402150083d9ac8d8bb509d1c6c809ad712e8430559ed7f3170d3235303631393130303033355a300c300a0603551d1504030a0101303302147931fd50b5071c1bbfc5b7b6ded8b45b9d8b8529170d3235303631393130303033355a300c300a0603551d1504030a0101303302141fa20e2970bde5d57f7b8ddf8339484e1f1d0823170d3235303631393130303033355a300c300a0603551d1504030a0101303302141e87b2c3b32d8d23e411cef34197b95af0c8adf5170d3235303631393130303033355a300c300a0603551d1504030a010130340215009afd2ee90a473550a167d996911437c7502d1f09170d3235303631393130303033355a300c300a0603551d1504030a0101303302144481b0f11728a13b696d3ea9c770a0b15ec58dda170d3235303631393130303033355a300c300a0603551d1504030a01013034021500a7859f57982ef0e67d37bc8ef2ef5ac835ff1aa9170d3235303631393130303033355a300c300a0603551d1504030a010130340215009d67753b81e47090aea763fbec4c4549bcdb9933170d3235303631393130303033355a300c300a0603551d1504030a01013033021434bfbb7a1d9c568147e118b614f7b76ed3ef68df170d3235303631393130303033355a300c300a0603551d1504030a0101303302142c3cc6fe9279db1516d5ce39f2a898cda5a175e1170d3235303631393130303033355a300c300a0603551d1504030a010130330214717948687509234be979e4b7dce6f31bef64b68c170d3235303631393130303033355a300c300a0603551d1504030a010130340215009d76ef2c39c136e8658b6e7396b1d7445a27631f170d3235303631393130303033355a300c300a0603551d1504030a01013034021500c3e025fca995f36f59b48467939e3e34e6361a6f170d3235303631393130303033355a300c300a0603551d1504030a010130340215008c5f6b3257da05b17429e2e61ba965d67330606a170d3235303631393130303033355a300c300a0603551d1504030a01013034021500a17c51722ec1e0c3278fe8bdf052059cbec4e648170d3235303631393130303033355a300c300a0603551d1504030a0101a02f302d300a0603551d140403020101301f0603551d23041830168014956f5dcdbd1be1e94049c9d4f433ce01570bde54300a06082a8648ce3d0403020349003046022100a8d1fdb9ca38f042df9aa14d3b1433860ddc1c7f6b873d5eecf2b63c313cb032022100cd59f446c89582be4a7d599df6e133533bbed9628c6a0264b7774074b44e52ef", + "tcb_info_issuer_chain": "-----BEGIN CERTIFICATE-----\nMIICjTCCAjKgAwIBAgIUfjiC1ftVKUpASY5FhAPpFJG99FUwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTI1MDUwNjA5MjUwMFoXDTMyMDUwNjA5MjUwMFowbDEeMBwG\nA1UEAwwVSW50ZWwgU0dYIFRDQiBTaWduaW5nMRowGAYDVQQKDBFJbnRlbCBDb3Jw\nb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYD\nVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABENFG8xzydWRfK92bmGv\nP+mAh91PEyV7Jh6FGJd5ndE9aBH7R3E4A7ubrlh/zN3C4xvpoouGlirMba+W2lju\nypajgbUwgbIwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqwwUgYDVR0f\nBEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNlcnZpY2Vz\nLmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFH44gtX7VSlK\nQEmORYQD6RSRvfRVMA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMAoGCCqG\nSM49BAMCA0kAMEYCIQDdmmRuAo3qCO8TC1IoJMITAoOEw4dlgEBHzSz1TuMSTAIh\nAKVTqOkt59+co0O3m3hC+v5Fb00FjYWcgeu3EijOULo5\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG\nA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0\naW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT\nAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7\n1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB\nuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ\nMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50\nZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV\nUr9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI\nKoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg\nAiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=\n-----END CERTIFICATE-----\n", + "tcb_info": "{\"id\":\"TDX\",\"version\":3,\"issueDate\":\"2025-06-19T10:16:03Z\",\"nextUpdate\":\"2025-07-19T10:16:03Z\",\"fmspc\":\"B0C06F000000\",\"pceId\":\"0000\",\"tcbType\":0,\"tcbEvaluationDataNumber\":17,\"tdxModule\":{\"mrsigner\":\"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"attributes\":\"0000000000000000\",\"attributesMask\":\"FFFFFFFFFFFFFFFF\"},\"tdxModuleIdentities\":[{\"id\":\"TDX_03\",\"mrsigner\":\"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"attributes\":\"0000000000000000\",\"attributesMask\":\"FFFFFFFFFFFFFFFF\",\"tcbLevels\":[{\"tcb\":{\"isvsvn\":3},\"tcbDate\":\"2024-03-13T00:00:00Z\",\"tcbStatus\":\"UpToDate\"}]},{\"id\":\"TDX_01\",\"mrsigner\":\"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"attributes\":\"0000000000000000\",\"attributesMask\":\"FFFFFFFFFFFFFFFF\",\"tcbLevels\":[{\"tcb\":{\"isvsvn\":4},\"tcbDate\":\"2024-03-13T00:00:00Z\",\"tcbStatus\":\"UpToDate\"},{\"tcb\":{\"isvsvn\":2},\"tcbDate\":\"2023-08-09T00:00:00Z\",\"tcbStatus\":\"OutOfDate\"}]}],\"tcbLevels\":[{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":2,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":2,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":2,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":2,\"category\":\"BIOS\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":1,\"category\":\"BIOS\"},{\"svn\":0},{\"svn\":5,\"category\":\"OS/VMM\",\"type\":\"SEAMLDR ACM\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":11,\"tdxtcbcomponents\":[{\"svn\":5,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":0,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":2,\"category\":\"OS/VMM\",\"type\":\"TDX Late Microcode Update\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}]},\"tcbDate\":\"2024-03-13T00:00:00Z\",\"tcbStatus\":\"UpToDate\"},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":2,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":2,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":2,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":2,\"category\":\"BIOS\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":1,\"category\":\"BIOS\"},{\"svn\":0},{\"svn\":5,\"category\":\"OS/VMM\",\"type\":\"SEAMLDR ACM\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":5,\"tdxtcbcomponents\":[{\"svn\":5,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":0,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":2,\"category\":\"OS/VMM\",\"type\":\"TDX Late Microcode Update\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}]},\"tcbDate\":\"2018-01-04T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00106\",\"INTEL-SA-00115\",\"INTEL-SA-00135\",\"INTEL-SA-00203\",\"INTEL-SA-00220\",\"INTEL-SA-00233\",\"INTEL-SA-00270\",\"INTEL-SA-00293\",\"INTEL-SA-00320\",\"INTEL-SA-00329\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00837\"]}]}", + "tcb_info_signature": "027ef6ca41bac64e61edbbd672b1c97eb0b2997400c5018eee002e66421b3fd27e71676891c9df47dc6ea3ea2e757ad3e080f394da0e0cddd76b2debe6790b4f", + "qe_identity_issuer_chain": "-----BEGIN CERTIFICATE-----\nMIICjTCCAjKgAwIBAgIUfjiC1ftVKUpASY5FhAPpFJG99FUwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTI1MDUwNjA5MjUwMFoXDTMyMDUwNjA5MjUwMFowbDEeMBwG\nA1UEAwwVSW50ZWwgU0dYIFRDQiBTaWduaW5nMRowGAYDVQQKDBFJbnRlbCBDb3Jw\nb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYD\nVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABENFG8xzydWRfK92bmGv\nP+mAh91PEyV7Jh6FGJd5ndE9aBH7R3E4A7ubrlh/zN3C4xvpoouGlirMba+W2lju\nypajgbUwgbIwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqwwUgYDVR0f\nBEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNlcnZpY2Vz\nLmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFH44gtX7VSlK\nQEmORYQD6RSRvfRVMA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMAoGCCqG\nSM49BAMCA0kAMEYCIQDdmmRuAo3qCO8TC1IoJMITAoOEw4dlgEBHzSz1TuMSTAIh\nAKVTqOkt59+co0O3m3hC+v5Fb00FjYWcgeu3EijOULo5\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG\nA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0\naW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT\nAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7\n1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB\nuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ\nMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50\nZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV\nUr9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI\nKoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg\nAiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=\n-----END CERTIFICATE-----\n", + "qe_identity": "{\"id\":\"TD_QE\",\"version\":2,\"issueDate\":\"2025-06-19T10:32:27Z\",\"nextUpdate\":\"2025-07-19T10:32:27Z\",\"tcbEvaluationDataNumber\":17,\"miscselect\":\"00000000\",\"miscselectMask\":\"FFFFFFFF\",\"attributes\":\"11000000000000000000000000000000\",\"attributesMask\":\"FBFFFFFFFFFFFFFF0000000000000000\",\"mrsigner\":\"DC9E2A7C6F948F17474E34A7FC43ED030F7C1563F1BABDDF6340C82E0E54A8C5\",\"isvprodid\":2,\"tcbLevels\":[{\"tcb\":{\"isvsvn\":4},\"tcbDate\":\"2024-03-13T00:00:00Z\",\"tcbStatus\":\"UpToDate\"}]}", + "qe_identity_signature": "d6d709840544c26e2ab3d680067d04b6160551f78aa23062cc79ab1be2ffe5414e21bf0fa9f0bea3c69be6c97d0a16585b82f6cc481059ad4affdc1c9bccfa15" +} \ No newline at end of file diff --git a/tdx-attest/examples/test_tdx.rs b/tdx-attest/examples/test_tdx.rs index 173fc15da..ea07b4f7a 100644 --- a/tdx-attest/examples/test_tdx.rs +++ b/tdx-attest/examples/test_tdx.rs @@ -65,9 +65,10 @@ async fn main() { pccs_url.as_deref().unwrap_or("(default)") ); match get_collateral_and_verify(quote, pccs_url.as_deref()).await { - Ok(verified) => { + Ok(result) => { + let verified = result.into_report_unchecked(); println!(" ✓ Quote verified!"); - println!(" QE status: {:?}", verified.qe_status); + println!(" TCB status: {}", verified.status); if let Some(report) = verified.report.as_td10() { if report.report_data[..] == report_data[..] { println!(" ✓ Report data matches"); diff --git a/verifier/src/verification.rs b/verifier/src/verification.rs index af9474be3..230fe64fe 100644 --- a/verifier/src/verification.rs +++ b/verifier/src/verification.rs @@ -419,12 +419,12 @@ impl CvmVerifier { let verified_attestation = match verified { Ok(att) => { details.quote_verified = true; - details.tcb_status = att.report.tdx_report().map(|r| r.status.clone()); - details.advisory_ids = att - .report - .tdx_report() - .map(|r| r.advisory_ids.clone()) - .unwrap_or_default(); + if let Some(qvr) = att.report.tdx_qvr() { + if let Ok(supplemental) = qvr.supplemental() { + details.tcb_status = Some(supplemental.tcb.status.to_string()); + details.advisory_ids = supplemental.tcb.advisory_ids; + } + } details.report_data = Some(hex::encode(att.report_data)); att } @@ -510,15 +510,17 @@ impl CvmVerifier { debug: bool, details: &mut VerificationDetails, ) -> Result<()> { - let Some(report) = &attestation.report.tdx_report() else { - bail!("No TDX report"); - }; + let qvr = attestation + .report + .tdx_qvr() + .context("No TDX QuoteVerificationResult")?; + let supplemental = qvr.supplemental().context("Failed to build supplemental")?; let Some(tdx_quote) = attestation.tdx_quote() else { bail!("No TDX quote"); }; let event_log = &tdx_quote.event_log; // Get boot info from attestation - let report = report + let report = supplemental .report .as_td10() .context("Failed to decode TD report")?;