ttk_server/service/
server.rs1use super::router::{build_router, RelayVerifierFactory, ALLOW_MOCK_RELAY_ENV};
19use crate::client::EnclaveCertVerifier;
20use crate::{attestation, AttestationParams};
21use axum::http::StatusCode;
22use axum::response::IntoResponse;
23use axum::Router;
24use bytes::{Buf, Bytes, BytesMut};
25use log::{info, warn};
26use quinn::{Endpoint, ServerConfig};
27use rcgen::{CertificateParams, CustomExtension, KeyPair, SanType};
28use rustls::pki_types::{pem::PemObject, CertificateDer, PrivateKeyDer};
29use std::net::SocketAddr;
30use std::sync::Arc;
31use time::{Duration, OffsetDateTime};
32use tower_service::Service;
33
34pub use super::router::Evidence;
35
36const ATTESTATION_OID: &[u64] = &[1, 3, 6, 1, 4, 1, 99999, 1];
38
39pub fn create_cert_with_attestation(
42 key_pair: &KeyPair,
43 common_name: &str,
44 attestation_doc: &[u8], validity_days: i64,
46) -> Result<String, Box<dyn std::error::Error>> {
47 let mut params = CertificateParams::default();
48
49 let now = OffsetDateTime::now_utc();
56 params.not_before = now;
57 params.not_after = now + Duration::days(validity_days);
58
59 params.subject_alt_names = vec![SanType::DnsName(common_name.try_into()?)];
61
62 let der_encoded_payload = wrap_in_asn1_octet_string(attestation_doc);
67
68 let mut attestation_ext =
69 CustomExtension::from_oid_content(ATTESTATION_OID, der_encoded_payload);
70
71 attestation_ext.set_criticality(false);
73
74 params.custom_extensions.push(attestation_ext);
75
76 let cert = params.self_signed(key_pair)?;
78 Ok(cert.pem())
79}
80
81fn wrap_in_asn1_octet_string(data: &[u8]) -> Vec<u8> {
83 let mut encoded = Vec::new();
84 encoded.push(0x04); let len = data.len();
87 if len < 128 {
88 encoded.push(len as u8);
89 } else if len <= 0xFF {
90 encoded.push(0x81);
91 encoded.push(len as u8);
92 } else if len <= 0xFFFF {
93 encoded.push(0x82);
94 encoded.extend_from_slice(&(len as u16).to_be_bytes());
95 } else {
96 encoded.push(0x84);
97 encoded.extend_from_slice(&(len as u32).to_be_bytes());
98 }
99
100 encoded.extend_from_slice(data);
101 encoded
102}
103
104type BoxError = Box<dyn std::error::Error>;
106
107type SendError = Box<dyn std::error::Error + Send + Sync>;
109
110const LISTEN_ADDR: &str = "0.0.0.0:4433";
112
113pub const LISTEN_ADDR_ENV: &str = "TTK_LISTEN_ADDR";
116
117pub const MAX_REQUEST_BODY: usize = 1024 * 1024;
119
120pub async fn run() -> Result<(), BoxError> {
125 let listen_addr = std::env::var(LISTEN_ADDR_ENV).unwrap_or_else(|_| LISTEN_ADDR.to_string());
126 let listen_addr: SocketAddr = listen_addr
127 .parse()
128 .map_err(|e| format!("invalid {LISTEN_ADDR_ENV} {listen_addr:?}: {e}"))?;
129 let mut server = Server::bind(listen_addr)?;
130 if std::env::var(ALLOW_MOCK_RELAY_ENV).is_ok_and(|v| v == "1") {
131 warn!("Accepting MOCK attestation from relay servers ({ALLOW_MOCK_RELAY_ENV}=1)");
132 server = server.with_relay_verifier(|| EnclaveCertVerifier::new().allow_mock());
133 }
134 info!("Server listening on {} (QUIC/HTTP/3)", server.local_addr()?);
135 server.serve().await;
136 Ok(())
137}
138
139pub struct Server {
141 endpoint: Endpoint,
142 evidence: Arc<Evidence>,
143 relay_verifier: RelayVerifierFactory,
144}
145
146impl Server {
148 pub fn bind(addr: SocketAddr) -> Result<Self, BoxError> {
153 info!("Initializing Nitro Enclave HTTP/3 Server...");
154
155 let _ = rustls::crypto::ring::default_provider().install_default();
157
158 let key_pair = KeyPair::generate()?;
159 info!("Generated ephemeral TLS certificate.");
160
161 let eat_bytes = generate_evidence(&key_pair)?;
162 let tls_config = build_tls_config(&key_pair, &eat_bytes)?;
163 let evidence = Arc::new(Evidence {
164 nitro: eat_bytes.clone(),
165 eat: eat_bytes,
166 });
167
168 let quic_config = ServerConfig::with_crypto(Arc::new(
169 quinn::crypto::rustls::QuicServerConfig::try_from(tls_config)?,
170 ));
171 let endpoint = Endpoint::server(quic_config, addr)?;
172 Ok(Self {
173 endpoint,
174 evidence,
175 relay_verifier: Arc::new(EnclaveCertVerifier::new),
176 })
177 }
178
179 pub fn with_relay_verifier(
183 mut self,
184 verifier: impl Fn() -> EnclaveCertVerifier + Send + Sync + 'static,
185 ) -> Self {
186 self.relay_verifier = Arc::new(verifier);
187 self
188 }
189
190 pub fn local_addr(&self) -> std::io::Result<SocketAddr> {
192 self.endpoint.local_addr()
193 }
194
195 pub async fn serve(self) {
197 let app = build_router(self.evidence, self.relay_verifier);
198 while let Some(incoming) = self.endpoint.accept().await {
199 tokio::spawn(handle_connection(incoming, app.clone()));
200 }
201 }
202}
203
204fn generate_evidence(key_pair: &KeyPair) -> Result<Vec<u8>, BoxError> {
207 let params = AttestationParams::new().with_user_data_hash(&key_pair.public_key_der());
208
209 let provider = attestation::detect()?;
210 info!("Using attestation provider: {}", provider.name());
211 let eat_bytes = provider.generate_document(¶ms)?.to_cbor_bytes()?;
212 info!(
213 "Wrapped Attestation Document as RFC 9711 EAT token ({} bytes).",
214 eat_bytes.len()
215 );
216 Ok(eat_bytes)
217}
218
219fn build_tls_config(
221 key_pair: &KeyPair,
222 eat_bytes: &[u8],
223) -> Result<rustls::ServerConfig, BoxError> {
224 let cert_pem = create_cert_with_attestation(key_pair, "enclave.internal", eat_bytes, 30)?;
225 let key_pem = key_pair.serialize_pem();
226
227 let certs =
228 CertificateDer::pem_slice_iter(cert_pem.as_bytes()).collect::<Result<Vec<_>, _>>()?;
229 let key = PrivateKeyDer::from_pem_slice(key_pem.as_bytes())?;
230
231 let mut config = rustls::ServerConfig::builder()
232 .with_no_client_auth()
233 .with_single_cert(certs, key)?;
234 config.alpn_protocols = vec![b"h3".to_vec()];
236 Ok(config)
237}
238
239async fn handle_connection(incoming: quinn::Incoming, app: Router) {
241 let conn = match incoming.await {
242 Ok(conn) => conn,
243 Err(err) => return eprintln!("Handshake failed: {err}"),
244 };
245
246 let mut h3_conn =
247 match h3::server::Connection::<_, axum::body::Bytes>::new(h3_quinn::Connection::new(conn))
248 .await
249 {
250 Ok(h3) => h3,
251 Err(e) => return eprintln!("H3 setup failed: {e}"),
252 };
253
254 while let Ok(Some((req, stream))) = h3_conn.accept().await {
255 let app = app.clone();
256 tokio::spawn(respond(app, req, stream));
257 }
258}
259
260type ServerStream =
262 h3::server::RequestStream<h3_quinn::BidiStream<axum::body::Bytes>, axum::body::Bytes>;
263
264async fn read_body(stream: &mut ServerStream) -> Result<Option<Bytes>, SendError> {
266 let mut body = BytesMut::new();
267 while let Some(mut chunk) = stream.recv_data().await? {
268 if body.len() + chunk.remaining() > MAX_REQUEST_BODY {
269 return Ok(None);
270 }
271 while chunk.has_remaining() {
272 let slice = chunk.chunk();
273 body.extend_from_slice(slice);
274 let len = slice.len();
275 chunk.advance(len);
276 }
277 }
278 Ok(Some(body.freeze()))
279}
280
281async fn respond(mut app: Router, req: axum::http::Request<()>, mut stream: ServerStream) {
284 let response = match read_body(&mut stream).await {
285 Ok(Some(body)) => match app.call(req.map(|()| axum::body::Body::from(body))).await {
286 Ok(response) => response,
287 Err(e) => return eprintln!("App call error: {e}"),
288 },
289 Ok(None) => (StatusCode::PAYLOAD_TOO_LARGE, "request body too large").into_response(),
290 Err(e) => return eprintln!("Failed to read request body: {e}"),
291 };
292
293 let (parts, body) = response.into_parts();
294 if let Err(e) = stream
295 .send_response(axum::http::Response::from_parts(parts, ()))
296 .await
297 {
298 return eprintln!("Failed to send response headers: {e}");
299 }
300 match axum::body::to_bytes(body, usize::MAX).await {
301 Ok(bytes) if !bytes.is_empty() => {
302 if let Err(e) = stream.send_data(bytes).await {
303 return eprintln!("Failed to send response body: {e}");
304 }
305 }
306 Ok(_) => {}
307 Err(e) => eprintln!("Failed to read response body: {e}"),
308 }
309 if let Err(e) = stream.finish().await {
310 eprintln!("Failed to finish stream: {e}");
311 }
312}