oasis_core_runtime/enclave_rpc/
session.rs1use std::{collections::HashSet, io::Write, mem, sync::Arc};
3
4use anyhow::Result;
5use thiserror::Error;
6
7use super::types::Message;
8use crate::{
9 common::{
10 crypto::signature::{self, PublicKey, Signature, Signer},
11 namespace::Namespace,
12 sgx::{ias, EnclaveIdentity, Quote, QuotePolicy},
13 },
14 consensus::{
15 registry::{EndorsedCapabilityTEE, VerifiedAttestation, VerifiedEndorsedCapabilityTEE},
16 state::registry::ImmutableState as RegistryState,
17 verifier::Verifier,
18 },
19 identity::Identity,
20};
21
22const NOISE_PATTERN: &str = "Noise_XX_25519_ChaChaPoly_SHA256";
24const RAK_SESSION_BINDING_CONTEXT: [u8; 8] = *b"EkRakRpc";
26
27#[derive(Error, Debug)]
29enum SessionError {
30 #[error("invalid input")]
31 InvalidInput,
32 #[error("invalid state")]
33 InvalidState,
34 #[error("session closed")]
35 Closed,
36 #[error("mismatched enclave identity")]
37 MismatchedEnclaveIdentity,
38 #[error("missing quote policy")]
39 MissingQuotePolicy,
40 #[error("remote node not set")]
41 NodeNotSet,
42 #[error("remote node already set")]
43 NodeAlreadySet,
44 #[error("remote node not registered")]
45 NodeNotRegistered,
46 #[error("RAK not published in the consensus layer")]
47 RAKNotFound,
48 #[error("runtime id not set")]
49 RuntimeNotSet,
50}
51
52pub struct SessionInfo {
54 pub rak_binding: RAKBinding,
56 pub verified_attestation: VerifiedAttestation,
58 pub endorsed_by: Option<PublicKey>,
60}
61
62enum State {
63 Handshake1(snow::HandshakeState),
64 Handshake2(snow::HandshakeState),
65 Transport(snow::TransportState),
66 UnauthenticatedTransport(snow::TransportState),
67 Closed,
68}
69
70pub struct Session {
72 cfg: Config,
73 local_static_pub: Vec<u8>,
74 remote_node: Option<signature::PublicKey>,
75 info: Option<Arc<SessionInfo>>,
76 state: State,
77 buf: Vec<u8>,
78}
79
80impl Session {
81 fn new(handshake_state: snow::HandshakeState, local_static_pub: Vec<u8>, cfg: Config) -> Self {
82 Self {
83 cfg,
84 local_static_pub,
85 remote_node: None,
86 info: None,
87 state: State::Handshake1(handshake_state),
88 buf: vec![0u8; 65535],
89 }
90 }
91
92 pub async fn process_data<W: Write>(
98 &mut self,
99 data: &[u8],
100 mut writer: W,
101 ) -> Result<Option<Message>> {
102 match mem::replace(&mut self.state, State::Closed) {
105 State::Handshake1(mut state) => {
106 if state.is_initiator() {
107 if !data.is_empty() {
109 return Err(SessionError::InvalidInput.into());
110 }
111
112 let len = state.write_message(&[], &mut self.buf)?;
114 writer.write_all(&self.buf[..len])?;
115 } else {
116 state.read_message(data, &mut self.buf)?;
118
119 let len = state.write_message(&self.get_rak_binding(), &mut self.buf)?;
121 writer.write_all(&self.buf[..len])?;
122 }
123
124 self.state = State::Handshake2(state);
125 }
126 State::Handshake2(mut state) => {
127 let len = state.read_message(data, &mut self.buf)?;
129 let remote_static = state
130 .get_remote_static()
131 .expect("dh exchange just happened");
132 let auth_info = self
133 .verify_rak_binding(&self.buf[..len], remote_static)
134 .await;
135
136 if state.is_initiator() {
137 let len = state.write_message(&self.get_rak_binding(), &mut self.buf)?;
139 writer.write_all(&self.buf[..len])?;
140 }
141
142 match auth_info {
143 Ok(auth_info) => {
144 self.info = auth_info;
145 self.state = State::Transport(state.into_transport_mode()?);
146 }
147 Err(_) if state.is_initiator() => {
148 self.state = State::UnauthenticatedTransport(state.into_transport_mode()?);
152 }
153 Err(err) => {
154 return Err(err);
156 }
157 }
158 }
159 State::Transport(mut state) => {
160 let len = state.read_message(data, &mut self.buf)?;
162 let msg = cbor::from_slice(&self.buf[..len])?;
163
164 self.state = State::Transport(state);
165 return Ok(Some(msg));
166 }
167 State::Closed | State::UnauthenticatedTransport(_) => {
168 return Err(SessionError::Closed.into());
169 }
170 }
171
172 Ok(None)
173 }
174
175 pub fn write_message<W: Write>(&mut self, msg: Message, mut writer: W) -> Result<()> {
180 let state = match self.state {
181 State::Transport(ref mut state) => state,
182 State::UnauthenticatedTransport(ref mut state) if matches!(msg, Message::Close) => {
183 state
184 }
185 _ => return Err(SessionError::InvalidState.into()),
186 };
187
188 let len = state.write_message(&cbor::to_vec(msg), &mut self.buf)?;
189 writer.write_all(&self.buf[..len])?;
190
191 Ok(())
192 }
193
194 pub fn close(&mut self) {
199 self.state = State::Closed;
200 }
201
202 fn get_rak_binding(&self) -> Vec<u8> {
203 match self.cfg.identity {
204 Some(ref identity) => {
205 if identity.quote().is_none() {
206 return vec![];
207 }
208
209 let binding = identity
210 .sign(&RAK_SESSION_BINDING_CONTEXT, &self.local_static_pub)
211 .unwrap();
212
213 if self.cfg.use_endorsement {
214 if let Some(ect) = identity.endorsed_capability_tee() {
216 return cbor::to_vec(RAKBinding::V2 { ect, binding });
217 }
218 }
219
220 let rak_pub = identity.public_rak();
222 let quote = identity.quote().expect("quote is configured");
223
224 cbor::to_vec(RAKBinding::V1 {
225 rak_pub,
226 binding,
227 quote: (*quote).clone(),
228 })
229 }
230 None => vec![],
231 }
232 }
233
234 async fn verify_rak_binding(
235 &self,
236 rak_binding: &[u8],
237 remote_static: &[u8],
238 ) -> Result<Option<Arc<SessionInfo>>> {
239 if rak_binding.is_empty() {
240 if self.cfg.remote_enclaves.is_some() {
243 return Err(SessionError::MismatchedEnclaveIdentity.into());
244 }
245 return Ok(None);
246 }
247
248 let policy = self
249 .cfg
250 .policy
251 .as_ref()
252 .ok_or(SessionError::MissingQuotePolicy)?;
253
254 let rak_binding: RAKBinding = cbor::from_slice(rak_binding)?;
255 let vect = rak_binding.verify(remote_static, &self.cfg.remote_enclaves, policy)?;
256
257 if self.cfg.consensus_verifier.is_some() {
259 let rak = rak_binding.rak_pub();
260 self.verify_node_identity(rak).await?;
261 }
262
263 Ok(Some(Arc::new(SessionInfo {
264 rak_binding,
265 verified_attestation: vect.verified_attestation,
266 endorsed_by: vect.node_id,
267 })))
268 }
269
270 pub fn session_info(&self) -> Option<Arc<SessionInfo>> {
272 self.info.clone()
273 }
274
275 pub fn is_connected(&self) -> bool {
278 matches!(self.state, State::Transport(_))
279 }
280
281 pub fn is_connected_to(&self, nodes: &Vec<signature::PublicKey>) -> bool {
283 nodes.iter().any(|&node| Some(node) == self.remote_node)
284 }
285
286 pub fn is_closed(&self) -> bool {
288 matches!(self.state, State::Closed)
289 }
290
291 pub fn is_unauthenticated(&self) -> bool {
294 matches!(self.state, State::UnauthenticatedTransport(_))
295 }
296
297 pub fn get_remote_node(&self) -> Result<signature::PublicKey> {
299 self.remote_node.ok_or(SessionError::NodeNotSet.into())
300 }
301
302 pub fn set_remote_node(&mut self, node: signature::PublicKey) -> Result<()> {
304 if self.remote_node.is_some() {
305 return Err(SessionError::NodeAlreadySet.into());
306 }
307 self.remote_node = Some(node);
308 Ok(())
309 }
310
311 async fn verify_node_identity(&self, rak: signature::PublicKey) -> Result<()> {
314 let consensus_verifier = self
315 .cfg
316 .consensus_verifier
317 .as_ref()
318 .expect("consensus verifier should be set");
319 let runtime_id = self
320 .cfg
321 .remote_runtime_id
322 .ok_or(SessionError::RuntimeNotSet)?;
323 let node = self.remote_node.ok_or(SessionError::NodeNotSet)?;
324
325 let consensus_state = consensus_verifier.latest_state().await?;
326 let node = tokio::task::block_in_place(move || -> Result<_> {
328 let registry_state = RegistryState::new(&consensus_state);
329 Ok(registry_state
330 .node(&node)?
331 .ok_or(SessionError::NodeNotRegistered)?)
332 })?;
333
334 let verified = node
335 .runtimes
336 .unwrap_or_default()
337 .iter()
338 .filter(|rt| rt.id == runtime_id)
339 .flat_map(|rt| &rt.capabilities.tee)
340 .any(|tee| tee.rak == rak);
341
342 if !verified {
343 return Err(SessionError::RAKNotFound.into());
344 }
345 Ok(())
346 }
347}
348
349#[derive(Clone, Debug, cbor::Encode, cbor::Decode)]
360#[cbor(tag = "v")]
361pub enum RAKBinding {
362 #[cbor(rename = 0, missing)]
364 V0 {
365 rak_pub: PublicKey,
366 binding: Signature,
367 avr: ias::AVR,
368 },
369
370 #[cbor(rename = 1)]
372 V1 {
373 rak_pub: PublicKey,
374 binding: Signature,
375 quote: Quote,
376 },
377
378 #[cbor(rename = 2)]
380 V2 {
381 ect: EndorsedCapabilityTEE,
382 binding: Signature,
383 },
384}
385
386impl RAKBinding {
387 pub fn rak_pub(&self) -> PublicKey {
389 match self {
390 Self::V0 { rak_pub, .. } => *rak_pub,
391 Self::V1 { rak_pub, .. } => *rak_pub,
392 Self::V2 { ect, .. } => ect.capability_tee.rak,
393 }
394 }
395
396 fn binding(&self) -> Signature {
398 match self {
399 Self::V0 { binding, .. } => *binding,
400 Self::V1 { binding, .. } => *binding,
401 Self::V2 { binding, .. } => *binding,
402 }
403 }
404
405 pub fn verify(
407 &self,
408 remote_static: &[u8],
409 remote_enclaves: &Option<HashSet<EnclaveIdentity>>,
410 policy: &QuotePolicy,
411 ) -> Result<VerifiedEndorsedCapabilityTEE> {
412 let vect = self.verify_inner(policy)?;
413
414 Identity::verify_binding(&vect.verified_attestation.quote, &self.rak_pub())?;
417
418 if let Some(ref remote_enclaves) = remote_enclaves {
420 if !remote_enclaves.contains(&vect.verified_attestation.quote.identity) {
421 return Err(SessionError::MismatchedEnclaveIdentity.into());
422 }
423 }
424
425 self.binding()
427 .verify(&self.rak_pub(), &RAK_SESSION_BINDING_CONTEXT, remote_static)?;
428
429 Ok(vect)
430 }
431
432 fn verify_inner(&self, policy: &QuotePolicy) -> Result<VerifiedEndorsedCapabilityTEE> {
433 match self {
434 Self::V0 { ref avr, .. } => {
435 ias::verify(avr, &policy.ias.clone().unwrap_or_default()).map(|vq| vq.into())
436 }
437 Self::V1 { ref quote, .. } => quote.verify(policy).map(|vq| vq.into()),
438 Self::V2 { ref ect, .. } => ect.verify(policy),
439 }
440 }
441}
442
443#[derive(Clone, Default)]
445struct Config {
446 consensus_verifier: Option<Arc<dyn Verifier>>,
447 identity: Option<Arc<Identity>>,
448 remote_enclaves: Option<HashSet<EnclaveIdentity>>,
449 remote_runtime_id: Option<Namespace>,
450 use_endorsement: bool,
451 policy: Option<Arc<QuotePolicy>>,
452}
453
454#[derive(Clone, Default)]
456pub struct Builder {
457 cfg: Config,
458}
459
460impl Builder {
461 pub fn get_remote_enclaves(&self) -> &Option<HashSet<EnclaveIdentity>> {
463 &self.cfg.remote_enclaves
464 }
465
466 pub fn remote_enclaves(mut self, enclaves: Option<HashSet<EnclaveIdentity>>) -> Self {
468 self.cfg.remote_enclaves = enclaves;
469 self
470 }
471
472 pub fn get_remote_runtime_id(&self) -> &Option<Namespace> {
474 &self.cfg.remote_runtime_id
475 }
476
477 pub fn remote_runtime_id(mut self, id: Option<Namespace>) -> Self {
479 self.cfg.remote_runtime_id = id;
480 self
481 }
482
483 pub fn consensus_verifier(mut self, verifier: Option<Arc<dyn Verifier>>) -> Self {
485 self.cfg.consensus_verifier = verifier;
486 self
487 }
488
489 pub fn get_quote_policy(&self) -> &Option<Arc<QuotePolicy>> {
491 &self.cfg.policy
492 }
493
494 pub fn quote_policy(mut self, policy: Option<Arc<QuotePolicy>>) -> Self {
496 self.cfg.policy = policy;
497 self
498 }
499
500 pub fn use_endorsement(mut self, use_endorsement: bool) -> Self {
502 self.cfg.use_endorsement = use_endorsement;
503 self
504 }
505
506 pub fn get_local_identity(&self) -> &Option<Arc<Identity>> {
508 &self.cfg.identity
509 }
510
511 pub fn local_identity(mut self, identity: Arc<Identity>) -> Self {
513 self.cfg.identity = Some(identity);
514 self
515 }
516
517 fn build<'a>(self) -> (snow::Builder<'a>, snow::Keypair, Config) {
518 let noise_builder = snow::Builder::new(NOISE_PATTERN.parse().unwrap());
519 let keypair = noise_builder.generate_keypair().unwrap();
520 let cfg = self.cfg;
521
522 (noise_builder, keypair, cfg)
523 }
524
525 pub fn build_initiator(self) -> Session {
527 let (builder, keypair, cfg) = self.build();
528 let session = builder
529 .local_private_key(&keypair.private)
530 .build_initiator()
531 .unwrap();
532 Session::new(session, keypair.public, cfg)
533 }
534
535 pub fn build_responder(self) -> Session {
537 let (builder, keypair, cfg) = self.build();
538 let session = builder
539 .local_private_key(&keypair.private)
540 .build_responder()
541 .unwrap();
542 Session::new(session, keypair.public, cfg)
543 }
544}