Skip to content

Commit 45dfb37

Browse files
tarrencevclaudesteebchenkariy
authored
feat(tee): add Trusted Execution Environment (TEE) attestation support (#378)
Implements TEE (Trusted Execution Environment) integration into Katana, enabling the generation of hardware-signed attestation quotes that cryptographically bind the sequencer's execution state (state root) to a TEE (Intel TDX). Includes new katana-tee crate, RPC API extension with `tee_generateQuote` method, CLI options, and node configuration. --------- Co-authored-by: Claude Haiku 4.5 <[email protected]> Co-authored-by: Luca Steeb <[email protected]> Co-authored-by: Ammar Arif <[email protected]>
1 parent dca26b6 commit 45dfb37

File tree

25 files changed

+1080
-47
lines changed

25 files changed

+1080
-47
lines changed

Cargo.lock

Lines changed: 352 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ members = [
4343
"crates/sync/pipeline",
4444
"crates/sync/stage",
4545
"crates/tasks",
46+
"crates/tee",
4647
"crates/tracing",
4748
"crates/trie",
4849
"crates/utils",
@@ -108,6 +109,7 @@ katana-slot-controller = { path = "crates/controller" }
108109
katana-stage = { path = "crates/sync/stage" }
109110
katana-starknet = { path = "crates/starknet" }
110111
katana-tasks = { path = "crates/tasks" }
112+
katana-tee = { path = "crates/tee" }
111113
katana-tracing = { path = "crates/tracing" }
112114
katana-trie = { path = "crates/trie" }
113115
katana-utils = { path = "crates/utils" }
@@ -130,6 +132,7 @@ auto_impl = "1.2.0"
130132
backon = { version = "1.5", features = [ "tokio-sleep" ] }
131133
base64 = "0.21"
132134
bigdecimal = "0.4.1"
135+
bincode = "1.3"
133136
bitvec = "1.0.1"
134137
camino = { version = "1.1.2", features = [ "serde1" ] }
135138
chrono = { version = "0.4.24", features = [ "serde" ] }
@@ -157,6 +160,7 @@ regex = "1.10.3"
157160
reqwest = { version = "0.12.15", features = [ "json", "rustls-tls" ], default-features = false }
158161
rstest = "0.18.2"
159162
rstest_reuse = "0.6.0"
163+
sev-snp = { git = "https://github.com/automata-network/amd-sev-snp-attestation-sdk", branch = "main", default-features = false, features = ["legacy", "configfs"] }
160164
similar-asserts = "1.5.0"
161165
slot = { git = "https://github.com/cartridge-gg/slot", rev = "1298a30" }
162166
spinoff = "0.8.0"
@@ -227,7 +231,7 @@ opentelemetry-stackdriver = { version = "0.27.0", features = [ "propagator" ] }
227231

228232
# starknet
229233
starknet = "0.17.0"
230-
starknet-types-core = { version = "0.2", features = [ "arbitrary", "hash" ] }
234+
starknet-types-core = { version = "=0.2.3", features = [ "arbitrary", "hash" ] }
231235
# Some types that we used from cairo-vm implements the `Arbitrary` trait,
232236
# only under the `test_utils` feature.
233237
cairo-vm = { version = "2.5.0", features = [ "test_utils" ] }

crates/cli/Cargo.toml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ katana-genesis.workspace = true
1414
katana-primitives.workspace = true
1515
katana-rpc-server.workspace = true
1616
katana-slot-controller = { workspace = true, optional = true }
17+
katana-tee = { workspace = true, optional = true }
1718
katana-tracing.workspace = true
1819
katana-utils.workspace = true
1920
serde-utils.workspace = true
@@ -41,7 +42,8 @@ cartridge = [
4142
"katana-node/cartridge",
4243
"katana-rpc-server/cartridge",
4344
]
44-
default = [ "cartridge", "server" ]
45-
explorer = [ "katana-node/explorer", "katana-utils/explorer" ]
46-
native = [ "katana-node/native" ]
47-
server = [ ]
45+
default = ["cartridge", "server", "tee"]
46+
explorer = ["katana-node/explorer", "katana-utils/explorer"]
47+
native = ["katana-node/native"]
48+
server = []
49+
tee = ["dep:katana-tee", "katana-node/tee", "katana-node/tee-snp", "katana-tee/snp"]

crates/cli/src/args.rs

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ use katana_node::config::rpc::RpcConfig;
2727
#[cfg(feature = "server")]
2828
use katana_node::config::rpc::{RpcModuleKind, RpcModulesList};
2929
use katana_node::config::sequencing::SequencingConfig;
30+
#[cfg(feature = "tee")]
31+
use katana_node::config::tee::TeeConfig;
3032
use katana_node::config::Config;
3133
use katana_node::Node;
3234
use serde::{Deserialize, Serialize};
@@ -129,6 +131,10 @@ pub struct SequencerNodeArgs {
129131
#[cfg(feature = "cartridge")]
130132
#[command(flatten)]
131133
pub cartridge: CartridgeOptions,
134+
135+
#[cfg(feature = "tee")]
136+
#[command(flatten)]
137+
pub tee: TeeOptions,
132138
}
133139

134140
impl SequencerNodeArgs {
@@ -212,37 +218,21 @@ impl SequencerNodeArgs {
212218
// the messagign config will eventually be removed slowly.
213219
let messaging = if cs_messaging.is_some() { cs_messaging } else { self.messaging.clone() };
214220

215-
#[cfg(feature = "cartridge")]
216-
{
217-
let paymaster = self.cartridge_config();
218-
219-
Ok(Config {
220-
db,
221-
dev,
222-
rpc,
223-
chain,
224-
metrics,
225-
gateway,
226-
forking,
227-
execution,
228-
messaging,
229-
paymaster,
230-
sequencing,
231-
})
232-
}
233-
234-
#[cfg(not(feature = "cartridge"))]
235221
Ok(Config {
236-
metrics,
237222
db,
238223
dev,
239224
rpc,
240225
chain,
241-
feeder_gateway,
226+
metrics,
227+
gateway,
228+
forking,
242229
execution,
243-
sequencing,
244230
messaging,
245-
forking,
231+
sequencing,
232+
#[cfg(feature = "cartridge")]
233+
paymaster: self.cartridge_config(),
234+
#[cfg(feature = "tee")]
235+
tee: self.tee_config(),
246236
})
247237
}
248238

@@ -291,6 +281,14 @@ impl SequencerNodeArgs {
291281
modules.add(RpcModuleKind::Cartridge);
292282
}
293283

284+
// The TEE rpc must be enabled if a TEE provider is specified.
285+
// We put it here so that even when the individual api are explicitly specified
286+
// (ie `--rpc.api`) we guarantee that the tee rpc is enabled.
287+
#[cfg(feature = "tee")]
288+
if self.tee.tee_provider.is_some() {
289+
modules.add(RpcModuleKind::Tee);
290+
}
291+
294292
let cors_origins = self.server.http_cors_origins.clone();
295293

296294
Ok(RpcConfig {
@@ -459,6 +457,11 @@ impl SequencerNodeArgs {
459457
}
460458
}
461459

460+
#[cfg(feature = "tee")]
461+
fn tee_config(&self) -> Option<TeeConfig> {
462+
self.tee.tee_provider.map(|provider_type| TeeConfig { provider_type })
463+
}
464+
462465
/// Parse the node config from the command line arguments and the config file,
463466
/// and merge them together prioritizing the command line arguments.
464467
pub fn with_config_file(mut self) -> Result<Self> {

crates/cli/src/options.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,3 +810,28 @@ fn parse_pruning_mode(s: &str) -> Result<PruningMode, String> {
810810
_ => Err(format!("Invalid pruning mode '{s}'. Valid modes are: 'archive', 'full:N'")),
811811
}
812812
}
813+
814+
#[cfg(feature = "tee")]
815+
#[derive(Debug, Args, Clone, Serialize, Deserialize, Default, PartialEq)]
816+
#[command(next_help_heading = "TEE options")]
817+
pub struct TeeOptions {
818+
/// Enable TEE attestation support with AMD SEV-SNP.
819+
///
820+
/// When enabled, the TEE RPC API becomes available for generating
821+
/// hardware-backed attestation quotes. Requires running in an SEV-SNP VM
822+
/// with /dev/sev-guest available.
823+
#[arg(long = "tee.provider", value_name = "PROVIDER")]
824+
#[serde(default)]
825+
pub tee_provider: Option<katana_tee::TeeProviderType>,
826+
}
827+
828+
#[cfg(feature = "tee")]
829+
impl TeeOptions {
830+
pub fn merge(&mut self, other: Option<&Self>) {
831+
if let Some(other) = other {
832+
if self.tee_provider.is_none() {
833+
self.tee_provider = other.tee_provider;
834+
}
835+
}
836+
}
837+
}

crates/node/Cargo.toml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ katana-rpc-client.workspace = true
2727
katana-rpc-types.workspace = true
2828
katana-stage.workspace = true
2929
katana-tasks.workspace = true
30+
katana-tee = { workspace = true, optional = true }
3031

3132
anyhow.workspace = true
3233
backon.workspace = true
@@ -46,6 +47,8 @@ strum_macros.workspace = true
4647
tokio = { workspace = true, features = [ "time" ] }
4748

4849
[features]
49-
cartridge = [ "katana-rpc-api/cartridge", "katana-rpc-server/cartridge" ]
50-
explorer = [ "katana-rpc-server/explorer" ]
51-
native = [ "katana-executor/native" ]
50+
cartridge = ["katana-rpc-api/cartridge", "katana-rpc-server/cartridge"]
51+
explorer = ["katana-rpc-server/explorer"]
52+
native = ["katana-executor/native"]
53+
tee = ["dep:katana-tee", "katana-rpc-api/tee", "katana-rpc-server/tee"]
54+
tee-snp = ["tee", "katana-tee/snp"]

crates/node/src/config/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ pub mod metrics;
1010
pub mod paymaster;
1111
pub mod rpc;
1212
pub mod sequencing;
13+
#[cfg(feature = "tee")]
14+
pub mod tee;
1315

1416
use db::DbConfig;
1517
use dev::DevConfig;
@@ -60,4 +62,8 @@ pub struct Config {
6062
/// Cartridge paymaster options.
6163
#[cfg(feature = "cartridge")]
6264
pub paymaster: Option<paymaster::PaymasterConfig>,
65+
66+
/// TEE attestation options.
67+
#[cfg(feature = "tee")]
68+
pub tee: Option<tee::TeeConfig>,
6369
}

crates/node/src/config/rpc.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ pub enum RpcModuleKind {
3434
Dev,
3535
#[cfg(feature = "cartridge")]
3636
Cartridge,
37+
#[cfg(feature = "tee")]
38+
Tee,
3739
}
3840

3941
/// Configuration for the RPC server.
@@ -104,6 +106,8 @@ impl RpcModulesList {
104106
RpcModuleKind::Dev,
105107
#[cfg(feature = "cartridge")]
106108
RpcModuleKind::Cartridge,
109+
#[cfg(feature = "tee")]
110+
RpcModuleKind::Tee,
107111
]))
108112
}
109113

crates/node/src/config/tee.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
use katana_tee::TeeProviderType;
2+
3+
/// TEE configuration options.
4+
#[derive(Debug, Clone, PartialEq, Eq)]
5+
pub struct TeeConfig {
6+
/// The type of TEE provider to use for attestation.
7+
pub provider_type: TeeProviderType,
8+
}

crates/node/src/lib.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ use katana_rpc_api::dev::DevApiServer;
3939
use katana_rpc_api::starknet::{StarknetApiServer, StarknetTraceApiServer, StarknetWriteApiServer};
4040
#[cfg(feature = "explorer")]
4141
use katana_rpc_api::starknet_ext::StarknetApiExtServer;
42+
#[cfg(feature = "tee")]
43+
use katana_rpc_api::tee::TeeApiServer;
4244
use katana_rpc_client::starknet::Client as StarknetClient;
4345
#[cfg(feature = "cartridge")]
4446
use katana_rpc_server::cartridge::CartridgeApi;
@@ -47,6 +49,8 @@ use katana_rpc_server::dev::DevApi;
4749
#[cfg(feature = "cartridge")]
4850
use katana_rpc_server::starknet::PaymasterConfig;
4951
use katana_rpc_server::starknet::{StarknetApi, StarknetApiConfig};
52+
#[cfg(feature = "tee")]
53+
use katana_rpc_server::tee::TeeApi;
5054
use katana_rpc_server::{RpcServer, RpcServerHandle};
5155
use katana_rpc_types::GetBlockWithTxHashesResponse;
5256
use katana_stage::Sequencing;
@@ -269,6 +273,37 @@ where
269273
rpc_modules.merge(DevApiServer::into_rpc(api))?;
270274
}
271275

276+
// --- build tee api (if configured)
277+
#[cfg(feature = "tee")]
278+
if config.rpc.apis.contains(&RpcModuleKind::Tee) {
279+
if let Some(ref tee_config) = config.tee {
280+
use katana_tee::{TeeProvider, TeeProviderType};
281+
282+
let tee_provider: Arc<dyn TeeProvider> = match tee_config.provider_type {
283+
TeeProviderType::SevSnp => {
284+
#[cfg(feature = "tee-snp")]
285+
{
286+
Arc::new(
287+
katana_tee::SevSnpProvider::new()
288+
.context("Failed to initialize SEV-SNP provider")?,
289+
)
290+
}
291+
#[cfg(not(feature = "tee-snp"))]
292+
{
293+
anyhow::bail!(
294+
"SEV-SNP TEE provider requires the 'tee-snp' feature to be enabled"
295+
);
296+
}
297+
}
298+
};
299+
300+
let api = TeeApi::new(provider.clone(), tee_provider);
301+
rpc_modules.merge(TeeApiServer::into_rpc(api))?;
302+
303+
info!(target: "node", provider = ?tee_config.provider_type, "TEE API enabled");
304+
}
305+
}
306+
272307
#[allow(unused_mut)]
273308
let mut rpc_server =
274309
RpcServer::new().metrics(true).health_check(true).cors(cors).module(rpc_modules)?;

0 commit comments

Comments
 (0)