[{"data":1,"prerenderedAt":1337},["Reactive",2],{"content-query-8RX2HFCxjX":3},{"_path":4,"_dir":5,"_draft":6,"_partial":7,"_locale":8,"title":9,"description":10,"date":11,"image":12,"draft":6,"body":13,"_type":1332,"_id":1333,"_source":1334,"_file":1335,"_extension":1336},"/blog/attested-tls-with-atlas","blog",true,false,"","aTLS: TLS That Proves What You're Talking To","TLS authenticates a key. It says nothing about the code behind that key. This post is a deep dive into attested TLS with atlas — TDX quotes, session binding via EKM, event-log replay, and the policies that turn hardware measurements into a trust decision.","2026-08-17T00:00:00.000Z","/img/atls-handshake.png",{"type":14,"children":15,"toc":1322},"root",[16,59,66,78,83,95,101,120,162,214,235,241,246,276,288,294,302,307,324,375,423,455,494,506,512,517,1076,1119,1124,1130,1135,1176,1181,1187,1192,1242,1247,1253,1294,1316],{"type":17,"tag":18,"props":19,"children":20},"element","p",{},[21,24,31,33,39,41,47,49,57],{"type":22,"value":23},"text","In ",{"type":17,"tag":25,"props":26,"children":28},"a",{"href":27},"/blog/umbra-attested-sandboxes-for-ai-agents",[29],{"type":22,"value":30},"the Umbra post",{"type":22,"value":32}," I made a claim that deserves its own article: that when the CLI opens a tunnel to a confidential VM, ",{"type":17,"tag":34,"props":35,"children":36},"em",{},[37],{"type":22,"value":38},"\"you are not just talking over an encrypted channel, you are talking over an encrypted channel to the code you expected.\"",{"type":22,"value":40}," Vanilla TLS cannot make that promise. This post is about the protocol that can — ",{"type":17,"tag":42,"props":43,"children":44},"strong",{},[45],{"type":22,"value":46},"aTLS",{"type":22,"value":48},", attested TLS — and about ",{"type":17,"tag":25,"props":50,"children":54},{"href":51,"rel":52},"https://github.com/concrete-security/atlas",[53],"nofollow",[55],{"type":22,"value":56},"atlas",{"type":22,"value":58},", the open-source library that implements it (atlas is the library, aTLS is the protocol).",{"type":17,"tag":60,"props":61,"children":63},"h2",{"id":62},"what-tls-actually-proves",[64],{"type":22,"value":65},"What TLS Actually Proves",{"type":17,"tag":18,"props":67,"children":68},{},[69,71,76],{"type":22,"value":70},"TLS gives you an encrypted, integrity-protected channel to ",{"type":17,"tag":34,"props":72,"children":73},{},[74],{"type":22,"value":75},"whoever holds a private key",{"type":22,"value":77},", and the web PKI adds a name: some certificate authority vouches that this key belongs to this domain. That is the whole story. Nothing in the handshake says anything about the software on the other end.",{"type":17,"tag":18,"props":79,"children":80},{},[81],{"type":22,"value":82},"For browsing the web this is the right trade-off. For infrastructure that handles your code and credentials, it is thin comfort: a valid certificate means roughly \"whoever controlled this DNS name at issuance time, and holds the key now\". If the server is compromised, same certificate, same padlock. If the operator swaps the backend for something that logs your traffic, same certificate, same padlock.",{"type":17,"tag":18,"props":84,"children":85},{},[86,88,93],{"type":22,"value":87},"What we actually want to authenticate is ",{"type":17,"tag":34,"props":89,"children":90},{},[91],{"type":22,"value":92},"what is running",{"type":22,"value":94},". That requires help from hardware.",{"type":17,"tag":60,"props":96,"children":98},{"id":97},"remote-attestation-in-five-minutes",[99],{"type":22,"value":100},"Remote Attestation in Five Minutes",{"type":17,"tag":18,"props":102,"children":103},{},[104,106,111,113,118],{"type":22,"value":105},"Umbra's CVMs run on Intel TDX, which executes a VM as a ",{"type":17,"tag":34,"props":107,"children":108},{},[109],{"type":22,"value":110},"trust domain",{"type":22,"value":112},": its memory is encrypted and the host, hypervisor included, stays outside. For this post, the part that matters is that the hardware also keeps ",{"type":17,"tag":42,"props":114,"children":115},{},[116],{"type":22,"value":117},"measurements",{"type":22,"value":119}," — hashes of everything that booted:",{"type":17,"tag":121,"props":122,"children":123},"ul",{},[124,135,145],{"type":17,"tag":125,"props":126,"children":127},"li",{},[128,133],{"type":17,"tag":42,"props":129,"children":130},{},[131],{"type":22,"value":132},"MRTD",{"type":22,"value":134}," — the initial memory image of the VM;",{"type":17,"tag":125,"props":136,"children":137},{},[138,143],{"type":17,"tag":42,"props":139,"children":140},{},[141],{"type":22,"value":142},"RTMR0–2",{"type":22,"value":144}," — the boot chain: virtual firmware, kernel, OS stack, each stage hash-chained into the next;",{"type":17,"tag":125,"props":146,"children":147},{},[148,153,155,160],{"type":17,"tag":42,"props":149,"children":150},{},[151],{"type":22,"value":152},"RTMR3",{"type":22,"value":154}," — the runtime register. The guest stack extends it event by event and keeps an ",{"type":17,"tag":42,"props":156,"children":157},{},[158],{"type":22,"value":159},"event log",{"type":22,"value":161},": which container compose file is running, instance configuration, and — this detail becomes important later — a hash of the TLS key the workload serves.",{"type":17,"tag":18,"props":163,"children":164},{},[165,167,172,174,181,183,188,190,196,198,204,206,212],{"type":22,"value":166},"On request, the CPU signs a ",{"type":17,"tag":42,"props":168,"children":169},{},[170],{"type":22,"value":171},"quote",{"type":22,"value":173}," over these registers plus 64 bytes of caller-chosen ",{"type":17,"tag":175,"props":176,"children":178},"code",{"className":177},[],[179],{"type":22,"value":180},"report_data",{"type":22,"value":182},", with a key hierarchy that chains up to Intel. A verifier checks the signature against Intel's collateral (the DCAP flow), which also yields a ",{"type":17,"tag":42,"props":184,"children":185},{},[186],{"type":22,"value":187},"TCB status",{"type":22,"value":189},": ",{"type":17,"tag":175,"props":191,"children":193},{"className":192},[],[194],{"type":22,"value":195},"UpToDate",{"type":22,"value":197}," for a fully patched platform, degrading through ",{"type":17,"tag":175,"props":199,"children":201},{"className":200},[],[202],{"type":22,"value":203},"OutOfDate",{"type":22,"value":205}," to ",{"type":17,"tag":175,"props":207,"children":209},{"className":208},[],[210],{"type":22,"value":211},"Revoked",{"type":22,"value":213},". Policies pin the statuses they accept, with an optional grace period so a fleet doesn't fall over the day Intel ships a microcode update.",{"type":17,"tag":18,"props":215,"children":216},{},[217,219,224,226,233],{"type":22,"value":218},"So a quote proves: ",{"type":17,"tag":34,"props":220,"children":221},{},[222],{"type":22,"value":223},"genuine, acceptably patched hardware booted exactly this stack",{"type":22,"value":225},". The expected \"golden\" values have to come from somewhere trustworthy — reproducible image builds, which is its own rabbit hole (",{"type":17,"tag":25,"props":227,"children":230},{"href":228,"rel":229},"https://github.com/concrete-security/atlas/blob/main/core/BOOTCHAIN-VERIFICATION.md",[53],[231],{"type":22,"value":232},"atlas documents how to compute them",{"type":22,"value":234},").",{"type":17,"tag":60,"props":236,"children":238},{"id":237},"a-valid-quote-is-not-enough",[239],{"type":22,"value":240},"A Valid Quote Is Not Enough",{"type":17,"tag":18,"props":242,"children":243},{},[244],{"type":22,"value":245},"Here is the trap in the naive design. Fetch a quote from the server, verify the measurements, then open TLS and start talking. Two attacks survive it:",{"type":17,"tag":121,"props":247,"children":248},{},[249,259],{"type":17,"tag":125,"props":250,"children":251},{},[252,257],{"type":17,"tag":42,"props":253,"children":254},{},[255],{"type":22,"value":256},"Replay",{"type":22,"value":258}," — an old quote, captured once, presented forever.",{"type":17,"tag":125,"props":260,"children":261},{},[262,267,269,274],{"type":17,"tag":42,"props":263,"children":264},{},[265],{"type":22,"value":266},"Relay",{"type":22,"value":268}," — the one that actually kills the naive design. An attacker sits between you and a ",{"type":17,"tag":34,"props":270,"children":271},{},[272],{"type":22,"value":273},"genuine",{"type":22,"value":275}," CVM. You open TLS to the attacker. The attacker fetches a fresh, perfectly valid quote from the real CVM and forwards it. Every check passes. You are talking to the attacker, who reads everything and proxies the rest.",{"type":17,"tag":18,"props":277,"children":278},{},[279,281,286],{"type":22,"value":280},"The root cause: nothing ties the quote to ",{"type":17,"tag":34,"props":282,"children":283},{},[284],{"type":22,"value":285},"this",{"type":22,"value":287}," TLS session. The fix is channel binding.",{"type":17,"tag":60,"props":289,"children":291},{"id":290},"one-round-trip",[292],{"type":22,"value":293},"One Round Trip",{"type":17,"tag":18,"props":295,"children":296},{},[297],{"type":17,"tag":298,"props":299,"children":301},"img",{"alt":300,"src":12},"aTLS handshake: TLS 1.3, EKM export, nonce, quote with report_data = SHA512(nonce || EKM), verification against policy",[],{"type":17,"tag":18,"props":303,"children":304},{},[305],{"type":22,"value":306},"This is the aTLS protocol as atlas implements it:",{"type":17,"tag":18,"props":308,"children":309},{},[310,315,317,322],{"type":17,"tag":42,"props":311,"children":312},{},[313],{"type":22,"value":314},"Step 1 — a promiscuous handshake.",{"type":22,"value":316}," The client completes a normal TLS 1.3 handshake but accepts the certificate ",{"type":17,"tag":34,"props":318,"children":319},{},[320],{"type":22,"value":321},"provisionally",{"type":22,"value":323},", recording it instead of trusting it.",{"type":17,"tag":18,"props":325,"children":326},{},[327,332,334,339,341,348,350,357,359,366,368,373],{"type":17,"tag":42,"props":328,"children":329},{},[330],{"type":22,"value":331},"Step 2 — EKM and nonce.",{"type":22,"value":333}," Both endpoints derive 32 bytes of ",{"type":17,"tag":42,"props":335,"children":336},{},[337],{"type":22,"value":338},"Exported Keying Material",{"type":22,"value":340}," from the session (",{"type":17,"tag":25,"props":342,"children":345},{"href":343,"rel":344},"https://datatracker.ietf.org/doc/html/rfc5705",[53],[346],{"type":22,"value":347},"RFC 5705",{"type":22,"value":349}," / ",{"type":17,"tag":25,"props":351,"children":354},{"href":352,"rel":353},"https://datatracker.ietf.org/doc/html/rfc8446#section-7.5",[53],[355],{"type":22,"value":356},"RFC 8446 §7.5",{"type":22,"value":358},", used as ",{"type":17,"tag":25,"props":360,"children":363},{"href":361,"rel":362},"https://datatracker.ietf.org/doc/html/rfc9266",[53],[364],{"type":22,"value":365},"RFC 9266",{"type":22,"value":367}," channel binding). EKM is derived from the session secrets, so it has the property we need: an attacker bridging two TLS sessions ends up with two ",{"type":17,"tag":34,"props":369,"children":370},{},[371],{"type":22,"value":372},"different",{"type":22,"value":374}," EKM values, and cannot make them agree. The client then sends a fresh 32-byte nonce over the established channel:",{"type":17,"tag":376,"props":377,"children":381},"pre",{"className":378,"code":379,"language":380,"meta":8,"style":8},"language-http shiki shiki-themes monokai","POST /tdx_quote HTTP/1.1\nContent-Type: application/json\n\n{ \"nonce_hex\": \"\u003Chex_nonce>\" }\n","http",[382],{"type":17,"tag":175,"props":383,"children":384},{"__ignoreMap":8},[385,396,405,414],{"type":17,"tag":386,"props":387,"children":390},"span",{"class":388,"line":389},"line",1,[391],{"type":17,"tag":386,"props":392,"children":393},{},[394],{"type":22,"value":395},"POST /tdx_quote HTTP/1.1\n",{"type":17,"tag":386,"props":397,"children":399},{"class":388,"line":398},2,[400],{"type":17,"tag":386,"props":401,"children":402},{},[403],{"type":22,"value":404},"Content-Type: application/json\n",{"type":17,"tag":386,"props":406,"children":408},{"class":388,"line":407},3,[409],{"type":17,"tag":386,"props":410,"children":411},{"emptyLinePlaceholder":6},[412],{"type":22,"value":413},"\n",{"type":17,"tag":386,"props":415,"children":417},{"class":388,"line":416},4,[418],{"type":17,"tag":386,"props":419,"children":420},{},[421],{"type":22,"value":422},"{ \"nonce_hex\": \"\u003Chex_nonce>\" }\n",{"type":17,"tag":18,"props":424,"children":425},{},[426,431,433,439,441,446,448,453],{"type":17,"tag":42,"props":427,"children":428},{},[429],{"type":22,"value":430},"Step 3 — the bound quote.",{"type":22,"value":432}," The server computes ",{"type":17,"tag":175,"props":434,"children":436},{"className":435},[],[437],{"type":22,"value":438},"report_data = SHA512(nonce ‖ EKM)",{"type":22,"value":440}," from ",{"type":17,"tag":34,"props":442,"children":443},{},[444],{"type":22,"value":445},"its",{"type":22,"value":447}," side of the session and asks the CPU to sign a fresh quote carrying that ",{"type":17,"tag":175,"props":449,"children":451},{"className":450},[],[452],{"type":22,"value":180},{"type":22,"value":454},", returning it with the RTMR event log.",{"type":17,"tag":18,"props":456,"children":457},{},[458,463,465,470,472,478,480,485,487,492],{"type":17,"tag":42,"props":459,"children":460},{},[461],{"type":22,"value":462},"Step 4 — verification.",{"type":22,"value":464}," The client now checks, in order: the quote's signature chain and TCB status against Intel's collateral; that ",{"type":17,"tag":175,"props":466,"children":468},{"className":467},[],[469],{"type":22,"value":180},{"type":22,"value":471}," equals ",{"type":17,"tag":175,"props":473,"children":475},{"className":474},[],[476],{"type":22,"value":477},"SHA512(nonce ‖ EKM)",{"type":22,"value":479}," computed from ",{"type":17,"tag":34,"props":481,"children":482},{},[483],{"type":22,"value":484},"its own",{"type":22,"value":486}," side of the session — this is the session binding and the freshness in one stroke; that replaying the event log entry by entry reproduces the RTMR3 value the hardware signed; that one of those replayed events binds the certificate key of ",{"type":17,"tag":34,"props":488,"children":489},{},[490],{"type":22,"value":491},"this very session",{"type":22,"value":493},", proving the attested workload — not a middlebox — owns the negotiated TLS key; and finally that MRTD, RTMR0–2, the OS image hash, and the application compose match the expected values.",{"type":17,"tag":18,"props":495,"children":496},{},[497,499,504],{"type":22,"value":498},"Walk the attacks against it. A leaked quote is useless: its ",{"type":17,"tag":175,"props":500,"children":502},{"className":501},[],[503],{"type":22,"value":180},{"type":22,"value":505}," was computed over a different session's EKM. An old quote fails the nonce. A stolen certificate key doesn't help: the attacker's session has its own EKM, and the event log binds the key to the attested workload. Software that isn't the expected software fails the measurements. Each check closes a specific door, and the union leaves the attacker needing to break TDX itself.",{"type":17,"tag":60,"props":507,"children":509},{"id":508},"from-measurements-to-a-decision",[510],{"type":22,"value":511},"From Measurements to a Decision",{"type":17,"tag":18,"props":513,"children":514},{},[515],{"type":22,"value":516},"Verification is only as good as your expectations, and atlas makes those explicit in a policy — bootchain measurements, OS image hash, expected compose, acceptable TCB statuses:",{"type":17,"tag":376,"props":518,"children":522},{"className":519,"code":520,"language":521,"meta":8,"style":8},"language-rust shiki shiki-themes monokai","use atlas_rs::{atls_connect, Policy, DstackTdxPolicy, ExpectedBootchain};\n\nlet policy = Policy::DstackTdx(DstackTdxPolicy {\n    expected_bootchain: Some(ExpectedBootchain {\n        mrtd: \"b24d3b24e9e3...\".into(),\n        rtmr0: \"24c15e08c07a...\".into(),\n        rtmr1: \"6e1afb7464ed...\".into(),\n        rtmr2: \"89e73cedf48f...\".into(),\n    }),\n    os_image_hash: Some(\"86b181377635...\".into()),\n    app_compose: Some(expected_compose),\n    allowed_tcb_status: vec![\"UpToDate\".into()],\n    ..Default::default()\n});\n\nlet tcp = tokio::net::TcpStream::connect((host, 443)).await?;\nlet (tls_stream, report) = atls_connect(tcp, host, policy, None).await?;\n// tls_stream is a normal TLS stream — but now you know what's behind it.\n","rust",[523],{"type":17,"tag":175,"props":524,"children":525},{"__ignoreMap":8},[526,581,588,636,666,700,730,760,790,799,838,860,901,929,938,946,1021,1066],{"type":17,"tag":386,"props":527,"children":528},{"class":388,"line":389},[529,535,541,546,552,557,562,567,571,576],{"type":17,"tag":386,"props":530,"children":532},{"style":531},"--shiki-default:#F92672",[533],{"type":22,"value":534},"use",{"type":17,"tag":386,"props":536,"children":538},{"style":537},"--shiki-default:#A6E22E;--shiki-default-text-decoration:underline",[539],{"type":22,"value":540}," atlas_rs",{"type":17,"tag":386,"props":542,"children":543},{"style":531},[544],{"type":22,"value":545},"::",{"type":17,"tag":386,"props":547,"children":549},{"style":548},"--shiki-default:#F8F8F2",[550],{"type":22,"value":551},"{atls_connect, ",{"type":17,"tag":386,"props":553,"children":554},{"style":537},[555],{"type":22,"value":556},"Policy",{"type":17,"tag":386,"props":558,"children":559},{"style":548},[560],{"type":22,"value":561},", ",{"type":17,"tag":386,"props":563,"children":564},{"style":537},[565],{"type":22,"value":566},"DstackTdxPolicy",{"type":17,"tag":386,"props":568,"children":569},{"style":548},[570],{"type":22,"value":561},{"type":17,"tag":386,"props":572,"children":573},{"style":537},[574],{"type":22,"value":575},"ExpectedBootchain",{"type":17,"tag":386,"props":577,"children":578},{"style":548},[579],{"type":22,"value":580},"};\n",{"type":17,"tag":386,"props":582,"children":583},{"class":388,"line":398},[584],{"type":17,"tag":386,"props":585,"children":586},{"emptyLinePlaceholder":6},[587],{"type":22,"value":413},{"type":17,"tag":386,"props":589,"children":590},{"class":388,"line":407},[591,597,602,607,612,616,622,627,631],{"type":17,"tag":386,"props":592,"children":594},{"style":593},"--shiki-default:#66D9EF;--shiki-default-font-style:italic",[595],{"type":22,"value":596},"let",{"type":17,"tag":386,"props":598,"children":599},{"style":548},[600],{"type":22,"value":601}," policy ",{"type":17,"tag":386,"props":603,"children":604},{"style":531},[605],{"type":22,"value":606},"=",{"type":17,"tag":386,"props":608,"children":609},{"style":537},[610],{"type":22,"value":611}," Policy",{"type":17,"tag":386,"props":613,"children":614},{"style":531},[615],{"type":22,"value":545},{"type":17,"tag":386,"props":617,"children":619},{"style":618},"--shiki-default:#A6E22E",[620],{"type":22,"value":621},"DstackTdx",{"type":17,"tag":386,"props":623,"children":624},{"style":548},[625],{"type":22,"value":626},"(",{"type":17,"tag":386,"props":628,"children":629},{"style":537},[630],{"type":22,"value":566},{"type":17,"tag":386,"props":632,"children":633},{"style":548},[634],{"type":22,"value":635}," {\n",{"type":17,"tag":386,"props":637,"children":638},{"class":388,"line":416},[639,644,649,654,658,662],{"type":17,"tag":386,"props":640,"children":641},{"style":548},[642],{"type":22,"value":643},"    expected_bootchain",{"type":17,"tag":386,"props":645,"children":646},{"style":531},[647],{"type":22,"value":648},":",{"type":17,"tag":386,"props":650,"children":651},{"style":537},[652],{"type":22,"value":653}," Some",{"type":17,"tag":386,"props":655,"children":656},{"style":548},[657],{"type":22,"value":626},{"type":17,"tag":386,"props":659,"children":660},{"style":537},[661],{"type":22,"value":575},{"type":17,"tag":386,"props":663,"children":664},{"style":548},[665],{"type":22,"value":635},{"type":17,"tag":386,"props":667,"children":669},{"class":388,"line":668},5,[670,675,679,685,690,695],{"type":17,"tag":386,"props":671,"children":672},{"style":548},[673],{"type":22,"value":674},"        mrtd",{"type":17,"tag":386,"props":676,"children":677},{"style":531},[678],{"type":22,"value":648},{"type":17,"tag":386,"props":680,"children":682},{"style":681},"--shiki-default:#E6DB74",[683],{"type":22,"value":684}," \"b24d3b24e9e3...\"",{"type":17,"tag":386,"props":686,"children":687},{"style":531},[688],{"type":22,"value":689},".",{"type":17,"tag":386,"props":691,"children":692},{"style":618},[693],{"type":22,"value":694},"into",{"type":17,"tag":386,"props":696,"children":697},{"style":548},[698],{"type":22,"value":699},"(),\n",{"type":17,"tag":386,"props":701,"children":703},{"class":388,"line":702},6,[704,709,713,718,722,726],{"type":17,"tag":386,"props":705,"children":706},{"style":548},[707],{"type":22,"value":708},"        rtmr0",{"type":17,"tag":386,"props":710,"children":711},{"style":531},[712],{"type":22,"value":648},{"type":17,"tag":386,"props":714,"children":715},{"style":681},[716],{"type":22,"value":717}," \"24c15e08c07a...\"",{"type":17,"tag":386,"props":719,"children":720},{"style":531},[721],{"type":22,"value":689},{"type":17,"tag":386,"props":723,"children":724},{"style":618},[725],{"type":22,"value":694},{"type":17,"tag":386,"props":727,"children":728},{"style":548},[729],{"type":22,"value":699},{"type":17,"tag":386,"props":731,"children":733},{"class":388,"line":732},7,[734,739,743,748,752,756],{"type":17,"tag":386,"props":735,"children":736},{"style":548},[737],{"type":22,"value":738},"        rtmr1",{"type":17,"tag":386,"props":740,"children":741},{"style":531},[742],{"type":22,"value":648},{"type":17,"tag":386,"props":744,"children":745},{"style":681},[746],{"type":22,"value":747}," \"6e1afb7464ed...\"",{"type":17,"tag":386,"props":749,"children":750},{"style":531},[751],{"type":22,"value":689},{"type":17,"tag":386,"props":753,"children":754},{"style":618},[755],{"type":22,"value":694},{"type":17,"tag":386,"props":757,"children":758},{"style":548},[759],{"type":22,"value":699},{"type":17,"tag":386,"props":761,"children":763},{"class":388,"line":762},8,[764,769,773,778,782,786],{"type":17,"tag":386,"props":765,"children":766},{"style":548},[767],{"type":22,"value":768},"        rtmr2",{"type":17,"tag":386,"props":770,"children":771},{"style":531},[772],{"type":22,"value":648},{"type":17,"tag":386,"props":774,"children":775},{"style":681},[776],{"type":22,"value":777}," \"89e73cedf48f...\"",{"type":17,"tag":386,"props":779,"children":780},{"style":531},[781],{"type":22,"value":689},{"type":17,"tag":386,"props":783,"children":784},{"style":618},[785],{"type":22,"value":694},{"type":17,"tag":386,"props":787,"children":788},{"style":548},[789],{"type":22,"value":699},{"type":17,"tag":386,"props":791,"children":793},{"class":388,"line":792},9,[794],{"type":17,"tag":386,"props":795,"children":796},{"style":548},[797],{"type":22,"value":798},"    }),\n",{"type":17,"tag":386,"props":800,"children":802},{"class":388,"line":801},10,[803,808,812,816,820,825,829,833],{"type":17,"tag":386,"props":804,"children":805},{"style":548},[806],{"type":22,"value":807},"    os_image_hash",{"type":17,"tag":386,"props":809,"children":810},{"style":531},[811],{"type":22,"value":648},{"type":17,"tag":386,"props":813,"children":814},{"style":537},[815],{"type":22,"value":653},{"type":17,"tag":386,"props":817,"children":818},{"style":548},[819],{"type":22,"value":626},{"type":17,"tag":386,"props":821,"children":822},{"style":681},[823],{"type":22,"value":824},"\"86b181377635...\"",{"type":17,"tag":386,"props":826,"children":827},{"style":531},[828],{"type":22,"value":689},{"type":17,"tag":386,"props":830,"children":831},{"style":618},[832],{"type":22,"value":694},{"type":17,"tag":386,"props":834,"children":835},{"style":548},[836],{"type":22,"value":837},"()),\n",{"type":17,"tag":386,"props":839,"children":841},{"class":388,"line":840},11,[842,847,851,855],{"type":17,"tag":386,"props":843,"children":844},{"style":548},[845],{"type":22,"value":846},"    app_compose",{"type":17,"tag":386,"props":848,"children":849},{"style":531},[850],{"type":22,"value":648},{"type":17,"tag":386,"props":852,"children":853},{"style":537},[854],{"type":22,"value":653},{"type":17,"tag":386,"props":856,"children":857},{"style":548},[858],{"type":22,"value":859},"(expected_compose),\n",{"type":17,"tag":386,"props":861,"children":863},{"class":388,"line":862},12,[864,869,873,878,883,888,892,896],{"type":17,"tag":386,"props":865,"children":866},{"style":548},[867],{"type":22,"value":868},"    allowed_tcb_status",{"type":17,"tag":386,"props":870,"children":871},{"style":531},[872],{"type":22,"value":648},{"type":17,"tag":386,"props":874,"children":875},{"style":618},[876],{"type":22,"value":877}," vec!",{"type":17,"tag":386,"props":879,"children":880},{"style":548},[881],{"type":22,"value":882},"[",{"type":17,"tag":386,"props":884,"children":885},{"style":681},[886],{"type":22,"value":887},"\"UpToDate\"",{"type":17,"tag":386,"props":889,"children":890},{"style":531},[891],{"type":22,"value":689},{"type":17,"tag":386,"props":893,"children":894},{"style":618},[895],{"type":22,"value":694},{"type":17,"tag":386,"props":897,"children":898},{"style":548},[899],{"type":22,"value":900},"()],\n",{"type":17,"tag":386,"props":902,"children":904},{"class":388,"line":903},13,[905,910,915,919,924],{"type":17,"tag":386,"props":906,"children":907},{"style":531},[908],{"type":22,"value":909},"    ..",{"type":17,"tag":386,"props":911,"children":912},{"style":537},[913],{"type":22,"value":914},"Default",{"type":17,"tag":386,"props":916,"children":917},{"style":531},[918],{"type":22,"value":545},{"type":17,"tag":386,"props":920,"children":921},{"style":618},[922],{"type":22,"value":923},"default",{"type":17,"tag":386,"props":925,"children":926},{"style":548},[927],{"type":22,"value":928},"()\n",{"type":17,"tag":386,"props":930,"children":932},{"class":388,"line":931},14,[933],{"type":17,"tag":386,"props":934,"children":935},{"style":548},[936],{"type":22,"value":937},"});\n",{"type":17,"tag":386,"props":939,"children":941},{"class":388,"line":940},15,[942],{"type":17,"tag":386,"props":943,"children":944},{"emptyLinePlaceholder":6},[945],{"type":22,"value":413},{"type":17,"tag":386,"props":947,"children":949},{"class":388,"line":948},16,[950,954,959,963,968,972,977,981,986,990,995,1000,1006,1011,1016],{"type":17,"tag":386,"props":951,"children":952},{"style":593},[953],{"type":22,"value":596},{"type":17,"tag":386,"props":955,"children":956},{"style":548},[957],{"type":22,"value":958}," tcp ",{"type":17,"tag":386,"props":960,"children":961},{"style":531},[962],{"type":22,"value":606},{"type":17,"tag":386,"props":964,"children":965},{"style":537},[966],{"type":22,"value":967}," tokio",{"type":17,"tag":386,"props":969,"children":970},{"style":531},[971],{"type":22,"value":545},{"type":17,"tag":386,"props":973,"children":974},{"style":537},[975],{"type":22,"value":976},"net",{"type":17,"tag":386,"props":978,"children":979},{"style":531},[980],{"type":22,"value":545},{"type":17,"tag":386,"props":982,"children":983},{"style":537},[984],{"type":22,"value":985},"TcpStream",{"type":17,"tag":386,"props":987,"children":988},{"style":531},[989],{"type":22,"value":545},{"type":17,"tag":386,"props":991,"children":992},{"style":618},[993],{"type":22,"value":994},"connect",{"type":17,"tag":386,"props":996,"children":997},{"style":548},[998],{"type":22,"value":999},"((host, ",{"type":17,"tag":386,"props":1001,"children":1003},{"style":1002},"--shiki-default:#AE81FF",[1004],{"type":22,"value":1005},"443",{"type":17,"tag":386,"props":1007,"children":1008},{"style":548},[1009],{"type":22,"value":1010},"))",{"type":17,"tag":386,"props":1012,"children":1013},{"style":531},[1014],{"type":22,"value":1015},".await?",{"type":17,"tag":386,"props":1017,"children":1018},{"style":548},[1019],{"type":22,"value":1020},";\n",{"type":17,"tag":386,"props":1022,"children":1024},{"class":388,"line":1023},17,[1025,1029,1034,1038,1043,1048,1053,1058,1062],{"type":17,"tag":386,"props":1026,"children":1027},{"style":593},[1028],{"type":22,"value":596},{"type":17,"tag":386,"props":1030,"children":1031},{"style":548},[1032],{"type":22,"value":1033}," (tls_stream, report) ",{"type":17,"tag":386,"props":1035,"children":1036},{"style":531},[1037],{"type":22,"value":606},{"type":17,"tag":386,"props":1039,"children":1040},{"style":618},[1041],{"type":22,"value":1042}," atls_connect",{"type":17,"tag":386,"props":1044,"children":1045},{"style":548},[1046],{"type":22,"value":1047},"(tcp, host, policy, ",{"type":17,"tag":386,"props":1049,"children":1050},{"style":537},[1051],{"type":22,"value":1052},"None",{"type":17,"tag":386,"props":1054,"children":1055},{"style":548},[1056],{"type":22,"value":1057},")",{"type":17,"tag":386,"props":1059,"children":1060},{"style":531},[1061],{"type":22,"value":1015},{"type":17,"tag":386,"props":1063,"children":1064},{"style":548},[1065],{"type":22,"value":1020},{"type":17,"tag":386,"props":1067,"children":1069},{"class":388,"line":1068},18,[1070],{"type":17,"tag":386,"props":1071,"children":1073},{"style":1072},"--shiki-default:#88846F",[1074],{"type":22,"value":1075},"// tls_stream is a normal TLS stream — but now you know what's behind it.\n",{"type":17,"tag":18,"props":1077,"children":1078},{},[1079,1081,1087,1089,1093,1095,1101,1103,1109,1111,1117],{"type":22,"value":1080},"Policies also load from JSON, which is exactly how Umbra uses them: at ",{"type":17,"tag":175,"props":1082,"children":1084},{"className":1083},[],[1085],{"type":22,"value":1086},"umbra cvm launch",{"type":22,"value":1088},", the Console returns a policy bundle — golden measurements from the image release pipeline plus material bound to ",{"type":17,"tag":34,"props":1090,"children":1091},{},[1092],{"type":22,"value":285},{"type":22,"value":1094}," CVM at launch, like the digest of your authorized SSH keys — and the CLI writes it to a per-CVM policy file. Opening a session later is: load the policy, connect, ",{"type":17,"tag":175,"props":1096,"children":1098},{"className":1097},[],[1099],{"type":22,"value":1100},"atls_connect",{"type":22,"value":1102},", then run a WebSocket-relayed SSH session through the verified stream (the CLI slots in as an OpenSSH ",{"type":17,"tag":175,"props":1104,"children":1106},{"className":1105},[],[1107],{"type":22,"value":1108},"ProxyCommand",{"type":22,"value":1110},", so plain ",{"type":17,"tag":175,"props":1112,"children":1114},{"className":1113},[],[1115],{"type":22,"value":1116},"ssh",{"type":22,"value":1118},", VS Code, and Cursor all ride the attested tunnel without knowing it exists).",{"type":17,"tag":18,"props":1120,"children":1121},{},[1122],{"type":22,"value":1123},"A nice consequence of per-CVM policies: the Console resolves which FQDN to dial, but it cannot lie usefully. Point the CLI at the wrong CVM — even a genuine one — and verification fails, because that CVM's launch-bound material doesn't match your policy file. Name resolution is untrusted; measurements are the identity. This is also why SSH host keys stop being the trust anchor: attestation replaces trust-on-first-use.",{"type":17,"tag":60,"props":1125,"children":1127},{"id":1126},"the-same-handshake-on-three-edges",[1128],{"type":22,"value":1129},"The Same Handshake on Three Edges",{"type":17,"tag":18,"props":1131,"children":1132},{},[1133],{"type":22,"value":1134},"In Umbra, this exact verification runs wherever a connection crosses into a TEE, always locally on the side taking the risk:",{"type":17,"tag":1136,"props":1137,"children":1138},"ol",{},[1139,1149,1166],{"type":17,"tag":125,"props":1140,"children":1141},{},[1142,1147],{"type":17,"tag":42,"props":1143,"children":1144},{},[1145],{"type":22,"value":1146},"Developer machine → Dev CVM.",{"type":22,"value":1148}," The CLI verifies before any session byte flows, as above.",{"type":17,"tag":125,"props":1150,"children":1151},{},[1152,1157,1159,1164],{"type":17,"tag":42,"props":1153,"children":1154},{},[1155],{"type":22,"value":1156},"Dev CVM → Security CVM.",{"type":22,"value":1158}," The egress forwarder inside each Dev CVM verifies the Security CVM before shipping sandbox traffic to it. Updated policies and a rotated proxy CA can be ",{"type":17,"tag":34,"props":1160,"children":1161},{},[1162],{"type":22,"value":1163},"distributed",{"type":22,"value":1165}," through the Console at runtime, but the forwarder verifies them inside the CVM before use — distribution is not trust.",{"type":17,"tag":125,"props":1167,"children":1168},{},[1169,1174],{"type":17,"tag":42,"props":1170,"children":1171},{},[1172],{"type":22,"value":1173},"Console → Security CVM.",{"type":22,"value":1175}," The control plane attests the Security CVM before issuing it any service credentials, and re-runs the same verification on every reconciliation probe, so infrastructure drift trips an alarm instead of passing silently.",{"type":17,"tag":18,"props":1177,"children":1178},{},[1179],{"type":22,"value":1180},"Nobody in this graph accepts a third party's word that verification happened somewhere else. That is the design rule that makes the rest of the system defensible.",{"type":17,"tag":60,"props":1182,"children":1184},{"id":1183},"what-you-still-have-to-trust",[1185],{"type":22,"value":1186},"What You Still Have to Trust",{"type":17,"tag":18,"props":1188,"children":1189},{},[1190],{"type":22,"value":1191},"Attestation moves trust; it does not eliminate it. The honest residue:",{"type":17,"tag":121,"props":1193,"children":1194},{},[1195,1205,1222,1232],{"type":17,"tag":125,"props":1196,"children":1197},{},[1198,1203],{"type":17,"tag":42,"props":1199,"children":1200},{},[1201],{"type":22,"value":1202},"Intel.",{"type":22,"value":1204}," The silicon, the key hierarchy, and the patch treadmill — TCB statuses exist precisely because this layer moves under you.",{"type":17,"tag":125,"props":1206,"children":1207},{},[1208,1213,1215,1220],{"type":17,"tag":42,"props":1209,"children":1210},{},[1211],{"type":22,"value":1212},"The measured code.",{"type":22,"value":1214}," A quote proves ",{"type":17,"tag":34,"props":1216,"children":1217},{},[1218],{"type":22,"value":1219},"which",{"type":22,"value":1221}," software is running, not that the software is free of bugs. A vulnerable image attests beautifully. Small, reviewable, reproducible images are what make measurements meaningful.",{"type":17,"tag":125,"props":1223,"children":1224},{},[1225,1230],{"type":17,"tag":42,"props":1226,"children":1227},{},[1228],{"type":22,"value":1229},"The pipeline that produced the golden values.",{"type":22,"value":1231}," If the build system is compromised, attackers don't need to defeat attestation — they ship you measurements of their own backdoor. This is why supply-chain work (reproducible builds, SLSA provenance) is not a compliance checkbox here; it is load-bearing.",{"type":17,"tag":125,"props":1233,"children":1234},{},[1235,1240],{"type":17,"tag":42,"props":1236,"children":1237},{},[1238],{"type":22,"value":1239},"The verifier's own host.",{"type":22,"value":1241}," The policy file sits on your machine; protecting your laptop is still your job.",{"type":17,"tag":18,"props":1243,"children":1244},{},[1245],{"type":22,"value":1246},"That first bullet — what it means to put a hardware vendor inside your trust base, and how that compares to protecting data with mathematics instead — is the subject of the next post: TEEs in the broader landscape of privacy-enhancing technologies, next to FHE, MPC, and zero-knowledge proofs.",{"type":17,"tag":60,"props":1248,"children":1250},{"id":1249},"try-it",[1251],{"type":22,"value":1252},"Try It",{"type":17,"tag":18,"props":1254,"children":1255},{},[1256,1258,1269,1271,1277,1279,1285,1287,1292],{"type":22,"value":1257},"atlas is a workspace of bindings around one Rust core, so the same verification runs everywhere your clients do: ",{"type":17,"tag":25,"props":1259,"children":1262},{"href":1260,"rel":1261},"https://crates.io/crates/atlas-rs",[53],[1263],{"type":17,"tag":175,"props":1264,"children":1266},{"className":1265},[],[1267],{"type":22,"value":1268},"atlas-rs",{"type":22,"value":1270}," natively, ",{"type":17,"tag":175,"props":1272,"children":1274},{"className":1273},[],[1275],{"type":22,"value":1276},"atlas-python",{"type":22,"value":1278}," via PyO3, ",{"type":17,"tag":175,"props":1280,"children":1282},{"className":1281},[],[1283],{"type":22,"value":1284},"@concrete-security/atlas-node",{"type":22,"value":1286},", and a WASM build for browsers (with a small WebSocket proxy for the raw TCP hop, since browsers can't open sockets). There is a relaxed development mode for local iteration; production policies pin ",{"type":17,"tag":175,"props":1288,"children":1290},{"className":1289},[],[1291],{"type":22,"value":195},{"type":22,"value":1293}," and the measurements you built yourself.",{"type":17,"tag":18,"props":1295,"children":1296},{},[1297,1299,1305,1307,1314],{"type":22,"value":1298},"The ",{"type":17,"tag":25,"props":1300,"children":1302},{"href":51,"rel":1301},[53],[1303],{"type":22,"value":1304},"atlas repository",{"type":22,"value":1306}," has the protocol details and API docs, and ",{"type":17,"tag":25,"props":1308,"children":1311},{"href":1309,"rel":1310},"https://github.com/concrete-security/umbra",[53],[1312],{"type":22,"value":1313},"Umbra",{"type":22,"value":1315}," is a full working deployment of it — three different trust edges, one handshake.",{"type":17,"tag":1317,"props":1318,"children":1319},"style",{},[1320],{"type":22,"value":1321},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":8,"searchDepth":398,"depth":398,"links":1323},[1324,1325,1326,1327,1328,1329,1330,1331],{"id":62,"depth":398,"text":65},{"id":97,"depth":398,"text":100},{"id":237,"depth":398,"text":240},{"id":290,"depth":398,"text":293},{"id":508,"depth":398,"text":511},{"id":1126,"depth":398,"text":1129},{"id":1183,"depth":398,"text":1186},{"id":1249,"depth":398,"text":1252},"markdown","content:blog:attested-tls-with-atlas.md","content","blog/attested-tls-with-atlas.md","md",1788420383405]