1use super::machine::{
4 ConnectedSessionConfig, core_machine_error, integration_machine_error, peer_protocol,
5};
6use super::*;
7use crate::iltp::{
8 IltpItem, PortText, SessionNonceText, SessionPreface, StreamEncoding, StreamOffers,
9};
10use crate::records::BySecret;
11
12const NONCE_DOMAIN: &[u8] = b"lace-iltp/1.0/session-nonce/v1\0";
13
14#[derive(Clone)]
17pub struct NonceSeed([u8; 32]);
18impl NonceSeed {
19 pub fn new(bytes: [u8; 32]) -> Self {
20 Self(bytes)
21 }
22 pub fn generate() -> Result<Self, getrandom::Error> {
24 crate::records::random_aux_rand32().map(Self)
25 }
26 pub(super) fn expose(&self) -> &[u8; 32] {
27 &self.0
28 }
29}
30impl std::fmt::Debug for NonceSeed {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 f.write_str("NonceSeed([REDACTED])")
33 }
34}
35impl Drop for NonceSeed {
36 fn drop(&mut self) {
37 crate::records::erasure::erase_secret_bytes(&mut self.0);
38 }
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum PrefaceCause {
43 Initial,
44 Reset,
45}
46
47#[derive(Debug)]
48struct PrefacingState {
49 cause: PrefaceCause,
50 local_operand: Option<SessionOperand>,
52 local_nonce: SessionNonceText,
53 local_preface_accepted: bool,
54 peer_nonce: Option<SessionNonceText>,
55 retained_output: Option<Vec<IltpItem>>,
56 discarded_control_bytes: u64,
57}
58
59#[derive(Debug)]
60pub enum CarrierState {
61 Prefacing,
62 Running,
63 Failed(MachineError),
64 Closed(CloseReason),
65}
66
67#[derive(Debug)]
68enum CarrierOwner {
69 Prefacing(PrefacingState),
70 Running {
71 session: Box<SessionMachine>,
72 staged_replacement: Option<crate::logic::InterlaceModule>,
73 },
74 Failed(MachineError),
75 Closed(CloseReason),
76}
77
78#[derive(Debug)]
81enum OperandSource {
82 Fixed {
83 expected_peer_by: Option<crate::records::ByValue>,
84 },
85 Selected,
86}
87
88#[derive(Debug)]
90pub struct ConnectedCarrier {
91 role: CarrierRole,
92 peer_proof: PeerProofAssurance,
93 operand_source: OperandSource,
94 port: Option<PortText>,
95 peer_port: Option<PortText>,
97 peer_by: Option<crate::records::ByValue>,
99 encodings: Option<[StreamEncoding; 2]>,
101 by_secret: Option<BySecret>,
102 local_by_value: Option<crate::records::ByValue>,
103 limits: InterlaceResourceLimits,
104 operand_cache: OperandCache,
105 stream_offers: StreamOffers,
106 nonce_seed: NonceSeed,
107 next_nonce_sequence: Option<u64>,
108 owner: CarrierOwner,
109}
110
111impl ConnectedCarrier {
112 pub fn new(role: CarrierRole, config: ConnectedConfig) -> Result<Self, CoreError> {
113 config.validate()?;
114 let ConnectedConfig {
115 operand,
116 by_secret,
117 peer_proof,
118 port,
119 stream_encodings,
120 nonce_seed,
121 limits,
122 operand_cache,
123 } = config;
124 let stream_offers =
125 StreamOffers::new(&stream_encodings).map_err(CoreError::InvalidConfig)?;
126 let local_by_value = by_secret.as_ref().map(BySecret::by_value);
127 let (operand_source, local_operand) = match operand {
128 ConnectedOperand::Fixed(operand) => (
129 OperandSource::Fixed {
130 expected_peer_by: operand.expected_peer_by,
131 },
132 Some(operand),
133 ),
134 ConnectedOperand::Selected => (OperandSource::Selected, None),
135 };
136 let mut carrier = Self {
137 role,
138 peer_proof,
139 operand_source,
140 port,
141 peer_port: None,
142 peer_by: None,
143 encodings: None,
144 by_secret,
145 local_by_value,
146 limits,
147 operand_cache,
148 stream_offers,
149 nonce_seed,
150 next_nonce_sequence: Some(0),
151 owner: CarrierOwner::Closed(CloseReason::Cancelled),
152 };
153 let local_nonce = carrier.derive_nonce(None)?;
154 carrier.owner = CarrierOwner::Prefacing(PrefacingState::new(
155 PrefaceCause::Initial,
156 local_operand,
157 local_nonce,
158 ));
159 Ok(carrier)
160 }
161
162 pub(super) fn local_protocol_side(&self) -> InterlaceSide {
163 self.role.protocol_side()
164 }
165
166 pub fn peer_port(&self) -> Option<&PortText> {
168 self.peer_port.as_ref()
169 }
170
171 pub fn peer_by(&self) -> Option<&crate::records::ByValue> {
176 self.peer_by.as_ref()
177 }
178
179 pub fn outgoing_encoding(&self) -> Option<StreamEncoding> {
183 self.encodings.map(|[outgoing, _]| outgoing)
184 }
185
186 pub fn incoming_encoding(&self) -> Option<StreamEncoding> {
190 self.encodings.map(|[_, incoming]| incoming)
191 }
192
193 pub fn state(&self) -> CarrierState {
194 match &self.owner {
195 CarrierOwner::Prefacing(_) => CarrierState::Prefacing,
196 CarrierOwner::Running { .. } => CarrierState::Running,
197 CarrierOwner::Failed(error) => CarrierState::Failed(error.clone()),
198 CarrierOwner::Closed(reason) => CarrierState::Closed(reason.clone()),
199 }
200 }
201
202 pub fn session(&self) -> Option<&SessionMachine> {
203 match &self.owner {
204 CarrierOwner::Running { session, .. } => Some(session),
205 _ => None,
206 }
207 }
208 pub fn protocol_phase(&self) -> MachineProtocolPhase {
209 match &self.owner {
210 CarrierOwner::Prefacing(_) => MachineProtocolPhase::Prefacing,
211 CarrierOwner::Running { session, .. } => session.protocol_phase(),
212 CarrierOwner::Failed(_) => MachineProtocolPhase::Failed,
213 CarrierOwner::Closed(_) => MachineProtocolPhase::Closed,
214 }
215 }
216 pub fn protocol_phase_token(&self) -> ProtocolPhaseToken {
217 ProtocolPhaseToken::new(
218 self.session().map(SessionMachine::id),
219 self.protocol_phase(),
220 )
221 }
222 pub fn active_store_call(&self) -> Option<&StoreCall> {
223 self.session().and_then(SessionMachine::active_store_call)
224 }
225 pub fn active_store_call_mut(&mut self) -> Option<&mut StoreCall> {
226 match &mut self.owner {
227 CarrierOwner::Running { session, .. } => session.active_store_call_mut(),
228 _ => None,
229 }
230 }
231 pub fn can_read_peer_input(&self) -> bool {
234 if self.active_store_call().is_some() {
235 return false;
236 }
237 match &self.owner {
238 CarrierOwner::Prefacing(prefacing) => prefacing.peer_nonce.is_none(),
239 CarrierOwner::Running { session, .. } => session.has_free_peer_slot(),
240 CarrierOwner::Failed(_) | CarrierOwner::Closed(_) => false,
241 }
242 }
243
244 pub fn peer_record_ingress(&self) -> RecordIngress {
249 match &self.owner {
250 CarrierOwner::Running { session, .. } => session.record_ingress(),
251 CarrierOwner::Prefacing(prefacing)
252 if prefacing.cause == PrefaceCause::Reset && prefacing.peer_nonce.is_none() =>
253 {
254 let remaining = self
255 .limits
256 .max_received_control_bytes
257 .saturating_sub(prefacing.discarded_control_bytes);
258 RecordIngress::Discard {
259 limit: usize::try_from(remaining).unwrap_or(usize::MAX),
260 }
261 }
262 _ => RecordIngress::Reject,
263 }
264 }
265
266 pub fn replace_policy(&mut self, next: crate::logic::InterlaceModule) -> Result<(), CoreError> {
269 self.promote_terminal_session();
270 if matches!(
271 self.owner,
272 CarrierOwner::Failed(_) | CarrierOwner::Closed(_)
273 ) {
274 return Err(CoreError::ReplacementUnavailable("interlace is not open"));
275 }
276 if matches!(self.operand_source, OperandSource::Selected) {
277 return Err(CoreError::ReplacementUnavailable(
278 "a port-selected interlace takes each session's policy from its selector",
279 ));
280 }
281 validate_operand_blob_size(&next)?;
282 match &mut self.owner {
283 CarrierOwner::Prefacing(prefacing) => {
284 if let Some(operand) = &mut prefacing.local_operand {
285 operand.operand = next;
286 }
287 Ok(())
288 }
289 CarrierOwner::Running {
290 session,
291 staged_replacement,
292 } => {
293 if session.local_operand() == Some(&next) {
294 *staged_replacement = None;
295 return Ok(());
296 }
297 if staged_replacement.as_ref() == Some(&next) {
298 return Ok(());
299 }
300 if session.active_store_call().is_some() {
301 return Err(CoreError::ReplacementUnavailable(
302 "connected policy replacement is unavailable while a store call is active",
303 ));
304 }
305 *staged_replacement = Some(next);
306 Ok(())
307 }
308 CarrierOwner::Failed(_) | CarrierOwner::Closed(_) => {
309 Err(CoreError::ReplacementUnavailable("interlace is not open"))
310 }
311 }
312 }
313
314 pub fn peer_input_disposition(&self, input: &PeerInput) -> PeerInputDisposition {
318 if self.active_store_call().is_some() {
319 return PeerInputDisposition::Backpressure;
320 }
321 let item = match input {
322 PeerInput::Item(item) => Some(item),
323 PeerInput::DiscardedRecord(_) => None,
324 };
325 match (&self.owner, item) {
326 (CarrierOwner::Prefacing(prefacing), Some(IltpItem::SessionPreface(_))) => {
327 if prefacing.peer_nonce.is_none() {
328 PeerInputDisposition::Accept
329 } else {
330 PeerInputDisposition::RejectNow
331 }
332 }
333 (CarrierOwner::Prefacing(prefacing), _) => {
334 if prefacing.cause == PrefaceCause::Reset && prefacing.peer_nonce.is_none() {
335 PeerInputDisposition::Accept
336 } else if prefacing.cause == PrefaceCause::Initial && prefacing.peer_nonce.is_none()
337 {
338 PeerInputDisposition::RejectNow
339 } else {
340 PeerInputDisposition::Backpressure
341 }
342 }
343 (CarrierOwner::Running { .. }, Some(IltpItem::SessionPreface(_))) => {
344 PeerInputDisposition::Accept
345 }
346 (CarrierOwner::Running { session, .. }, Some(item)) => {
347 if session.can_accept_item(item) {
348 PeerInputDisposition::Accept
349 } else if session.has_free_peer_slot() {
350 PeerInputDisposition::RejectNow
351 } else {
352 PeerInputDisposition::Backpressure
353 }
354 }
355 (CarrierOwner::Running { .. }, None) => PeerInputDisposition::RejectNow,
356 (CarrierOwner::Failed(_) | CarrierOwner::Closed(_), _) => {
357 PeerInputDisposition::Backpressure
358 }
359 }
360 }
361
362 pub fn push_input(&mut self, input: impl Into<PeerInput>) -> Result<(), MachineError> {
363 self.consume_staged_or_fail()?;
364 if self.active_store_call().is_some() {
365 return Err(integration_machine_error(
366 "peer input is blocked while a store call is active",
367 ));
368 }
369 let ordinary = match input.into() {
370 PeerInput::Item(IltpItem::SessionPreface(preface)) => {
371 return self.push_preface(preface);
372 }
373 ordinary => ordinary,
374 };
375 match &mut self.owner {
376 CarrierOwner::Prefacing(prefacing)
377 if prefacing.cause == PrefaceCause::Reset && prefacing.peer_nonce.is_none() =>
378 {
379 let size = match &ordinary {
380 PeerInput::Item(item) => crate::iltp::canonical_item_size(item)
381 .map_err(|error| integration_machine_error(error.to_string()))?
382 as u64,
383 PeerInput::DiscardedRecord(len) => *len as u64,
384 };
385 prefacing.discarded_control_bytes = prefacing
386 .discarded_control_bytes
387 .checked_add(size)
388 .ok_or_else(|| {
389 integration_machine_error("discarded control-byte accounting overflow")
390 })?;
391 if prefacing.discarded_control_bytes > self.limits.max_received_control_bytes {
392 return self.fail(MachineError {
393 category: ErrorCategory::LimitExceeded,
394 side: Some(self.role.protocol_side()),
395 record_hash: None,
396 detail: "received reset control-byte limit exceeded".into(),
397 source: None,
398 });
399 }
400 Ok(())
401 }
402 CarrierOwner::Prefacing(_) => {
403 self.fail(peer_protocol("ordinary input before session preface pair"))
404 }
405 CarrierOwner::Running { session, .. } => {
406 let PeerInput::Item(item) = ordinary else {
407 return Err(integration_machine_error(
408 "a discarded record reached a running session",
409 ));
410 };
411 let result = session.push_item(item);
412 match &result {
413 Ok(()) => self.peer_by = session.peer_by(),
414 Err(error) => self.owner = CarrierOwner::Failed(error.clone()),
415 }
416 result
417 }
418 CarrierOwner::Failed(error) => Err(error.clone()),
419 CarrierOwner::Closed(_) => Err(integration_machine_error(
420 "cannot push input to closed carrier",
421 )),
422 }
423 }
424
425 pub fn accept_peer_output(&mut self) -> Result<Vec<IltpItem>, MachineError> {
429 self.consume_staged_or_fail()?;
430 let result = match &mut self.owner {
431 CarrierOwner::Prefacing(prefacing) => {
432 let items = prefacing.retained_output.take().ok_or_else(|| {
433 integration_machine_error("carrier has no retained preface output")
434 })?;
435 prefacing.local_preface_accepted = true;
436 self.construct_session_if_ready().map(|()| items)
437 }
438 CarrierOwner::Running { session, .. } => session.accept_peer_output(),
439 CarrierOwner::Failed(error) => Err(error.clone()),
440 CarrierOwner::Closed(_) => Err(integration_machine_error("interlace is not open")),
441 };
442 self.promote_terminal_session();
443 result
444 }
445
446 pub fn submit_store_call(&mut self, id: u64) -> Result<(), MachineError> {
447 let result = match &mut self.owner {
448 CarrierOwner::Running { session, .. } => session.submit_store_call(id),
449 _ => Err(integration_machine_error("carrier has no active session")),
450 };
451 self.promote_terminal_session();
452 result
453 }
454
455 pub fn try_store_position_advanced(
456 &mut self,
457 boundary: &FixedPointWakeBoundary,
458 position: StorePosition,
459 ) -> StorePositionAdvance {
460 if self.consume_staged_or_fail().is_err() {
461 return StorePositionAdvance::Stale;
462 }
463 match &mut self.owner {
464 CarrierOwner::Running { session, .. } => {
465 session.try_store_position_advanced(boundary, position)
466 }
467 _ => StorePositionAdvance::Stale,
468 }
469 }
470
471 pub fn cancel(&mut self) {
472 match &mut self.owner {
473 CarrierOwner::Running { session, .. } => session.cancel(),
474 CarrierOwner::Prefacing(_) => self.owner = CarrierOwner::Closed(CloseReason::Cancelled),
475 CarrierOwner::Failed(_) | CarrierOwner::Closed(_) => {}
476 }
477 self.promote_terminal_session();
478 }
479
480 pub fn timeout(&mut self) {
481 match &mut self.owner {
482 CarrierOwner::Prefacing(_) => {
483 let error = MachineError {
484 category: ErrorCategory::Timeout,
485 side: Some(self.role.protocol_side()),
486 record_hash: None,
487 detail: "phase timeout in Prefacing".into(),
488 source: None,
489 };
490 self.owner = CarrierOwner::Failed(error);
491 }
492 CarrierOwner::Running { session, .. } => session.timeout(),
493 _ => {}
494 }
495 self.promote_terminal_session();
496 }
497
498 pub fn peer_eof(&mut self) -> Result<(), MachineError> {
499 let result = match &mut self.owner {
500 CarrierOwner::Running { session, .. } => session.peer_eof(),
501 CarrierOwner::Prefacing(_) => {
502 return self.fail(peer_protocol("peer EOF during prefacing"));
503 }
504 CarrierOwner::Failed(error) => return Err(error.clone()),
505 CarrierOwner::Closed(_) => return Ok(()),
506 };
507 self.promote_terminal_session();
508 result
509 }
510
511 pub fn poll(&mut self) -> MachinePoll<'_> {
512 let _ = self.consume_staged_or_fail();
514 self.promote_terminal_session();
515 match &mut self.owner {
516 CarrierOwner::Prefacing(prefacing) => {
517 if prefacing.retained_output.is_none() && !prefacing.local_preface_accepted {
518 prefacing.retained_output =
519 Some(vec![IltpItem::SessionPreface(SessionPreface {
520 nonce: prefacing.local_nonce.clone(),
521 offers: self.stream_offers.clone(),
522 port: self.port.clone(),
523 })]);
524 }
525 if let Some(items) = &prefacing.retained_output {
526 let effect = EndpointEffectRef::PeerOutput(PeerOutputRef { items });
527 return MachinePoll::Effects(if self.role == CarrierRole::Listener {
528 ReadyEffectsRef {
529 side0: Some(effect),
530 side1: None,
531 }
532 } else {
533 ReadyEffectsRef {
534 side0: None,
535 side1: Some(effect),
536 }
537 });
538 }
539 if prefacing.peer_nonce.is_some() && prefacing.local_operand.is_none() {
540 return MachinePoll::SelectOperand {
541 port: self.peer_port.as_ref(),
542 };
543 }
544 MachinePoll::NeedPeerInput
545 }
546 CarrierOwner::Running { session, .. } => session.poll(),
547 CarrierOwner::Failed(error) => MachinePoll::Failed(error),
548 CarrierOwner::Closed(reason) => MachinePoll::Closed(reason.clone()),
549 }
550 }
551
552 fn promote_terminal_session(&mut self) {
553 let terminal = match &self.owner {
554 CarrierOwner::Running { session, .. } => match session.state() {
555 SessionState::Failed(error) => Some(Err(error.clone())),
556 SessionState::Closed(reason) => Some(Ok(reason.clone())),
557 _ => None,
558 },
559 _ => None,
560 };
561 match terminal {
562 Some(Err(error)) => self.owner = CarrierOwner::Failed(error),
563 Some(Ok(reason)) => self.owner = CarrierOwner::Closed(reason),
564 None => {}
565 }
566 }
567
568 pub fn adopt_operand_cache(&mut self, cache: OperandCache) -> Result<(), MachineError> {
572 match &self.owner {
573 CarrierOwner::Prefacing(prefacing)
574 if prefacing.peer_nonce.is_some() && prefacing.local_operand.is_none() =>
575 {
576 self.operand_cache = cache;
577 Ok(())
578 }
579 CarrierOwner::Failed(error) => Err(error.clone()),
580 _ => Err(integration_machine_error(
581 "the carrier is not selecting an operand",
582 )),
583 }
584 }
585
586 pub fn select_operand(
589 &mut self,
590 selection: Option<SessionOperand>,
591 ) -> Result<(), MachineError> {
592 match &self.owner {
593 CarrierOwner::Prefacing(prefacing)
594 if prefacing.peer_nonce.is_some() && prefacing.local_operand.is_none() => {}
595 CarrierOwner::Failed(error) => return Err(error.clone()),
596 _ => {
597 return Err(integration_machine_error(
598 "the carrier is not selecting an operand",
599 ));
600 }
601 }
602 let Some(selected) = selection else {
603 return self.fail(peer_protocol("port refused"));
604 };
605 if let Err(error) = selected.validate(self.peer_proof) {
606 return self.fail(core_machine_error(error));
607 }
608 if let CarrierOwner::Prefacing(prefacing) = &mut self.owner {
609 prefacing.local_operand = Some(selected);
610 }
611 self.construct_session_if_ready()
612 }
613
614 fn push_preface(&mut self, preface: SessionPreface) -> Result<(), MachineError> {
615 self.promote_terminal_session();
616 let SessionPreface {
617 nonce: peer_nonce,
618 port,
619 offers,
620 } = preface;
621 let prior = std::mem::replace(
622 &mut self.owner,
623 CarrierOwner::Closed(CloseReason::Cancelled),
624 );
625 if matches!(
626 prior,
627 CarrierOwner::Running { .. } | CarrierOwner::Prefacing(_)
628 ) {
629 self.peer_port = port;
630 self.peer_by = None;
631 self.encodings = Some([
632 StreamOffers::select(&self.stream_offers, &offers),
633 StreamOffers::select(&offers, &self.stream_offers),
634 ]);
635 }
636 match prior {
637 CarrierOwner::Running { session, .. } => {
638 let local_operand = match &self.operand_source {
639 OperandSource::Selected => None,
640 OperandSource::Fixed { expected_peer_by } => {
641 let expected_peer_by = *expected_peer_by;
642 match (*session).into_local_operand() {
643 Some(operand) => Some(SessionOperand {
644 operand,
645 expected_peer_by,
646 }),
647 None => {
648 return self.fail(integration_machine_error(
649 "running session lost its local operand",
650 ));
651 }
652 }
653 }
654 };
655 let local_nonce = match self.derive_nonce(Some(&peer_nonce)) {
656 Ok(nonce) => nonce,
657 Err(_) => {
658 return self.fail(integration_machine_error(
659 "carrier nonce generator exhausted",
660 ));
661 }
662 };
663 self.owner = CarrierOwner::Prefacing(PrefacingState::with_peer(
664 PrefaceCause::Reset,
665 local_operand,
666 local_nonce,
667 peer_nonce,
668 ));
669 Ok(())
670 }
671 CarrierOwner::Prefacing(mut prefacing) => {
672 if prefacing.peer_nonce.is_some() {
673 self.owner = CarrierOwner::Prefacing(prefacing);
674 return self.fail(peer_protocol(
675 "duplicate session preface before fresh session construction",
676 ));
677 }
678 if prefacing.local_nonce == peer_nonce {
679 self.owner = CarrierOwner::Prefacing(prefacing);
680 return self.fail(peer_protocol("side0 and side1 session nonces must differ"));
681 }
682 prefacing.peer_nonce = Some(peer_nonce);
683 self.owner = CarrierOwner::Prefacing(prefacing);
684 self.construct_session_if_ready()
685 }
686 CarrierOwner::Failed(error) => {
687 self.owner = CarrierOwner::Failed(error.clone());
688 Err(error)
689 }
690 CarrierOwner::Closed(reason) => {
691 self.owner = CarrierOwner::Closed(reason);
692 Err(integration_machine_error("cannot preface a closed carrier"))
693 }
694 }
695 }
696
697 fn consume_staged_or_fail(&mut self) -> Result<(), MachineError> {
698 match self.consume_staged_replacement() {
699 Ok(()) => Ok(()),
700 Err(error) => {
701 self.owner = CarrierOwner::Failed(error.clone());
702 Err(error)
703 }
704 }
705 }
706
707 fn consume_staged_replacement(&mut self) -> Result<(), MachineError> {
708 let staged = match &mut self.owner {
709 CarrierOwner::Running {
710 session,
711 staged_replacement,
712 } if session.active_store_call().is_none() => staged_replacement.take(),
713 _ => None,
714 };
715 let Some(next) = staged else { return Ok(()) };
716 let OperandSource::Fixed { expected_peer_by } = self.operand_source else {
717 return Err(integration_machine_error(
718 "a port-selected carrier staged a replacement",
719 ));
720 };
721 let local_nonce = self
722 .derive_nonce(None)
723 .map_err(|_| integration_machine_error("carrier nonce generator exhausted"))?;
724 self.peer_by = None;
725 self.owner = CarrierOwner::Prefacing(PrefacingState::new(
726 PrefaceCause::Reset,
727 Some(SessionOperand {
728 operand: next,
729 expected_peer_by,
730 }),
731 local_nonce,
732 ));
733 Ok(())
734 }
735
736 fn construct_session_if_ready(&mut self) -> Result<(), MachineError> {
737 let ready = matches!(&self.owner, CarrierOwner::Prefacing(prefacing) if prefacing.local_preface_accepted && prefacing.peer_nonce.is_some() && prefacing.local_operand.is_some());
738 if !ready {
739 return Ok(());
740 }
741 let prior = std::mem::replace(
742 &mut self.owner,
743 CarrierOwner::Closed(CloseReason::Cancelled),
744 );
745 let CarrierOwner::Prefacing(prefacing) = prior else {
746 unreachable!()
747 };
748 let peer_nonce = prefacing.peer_nonce.expect("ready preface owns peer nonce");
749 let local_operand = prefacing
750 .local_operand
751 .expect("ready preface owns its operand");
752 if peer_nonce == prefacing.local_nonce {
753 return self.fail(peer_protocol("side0 and side1 session nonces must differ"));
754 }
755 let (side0_nonce, side1_nonce) = if self.role == CarrierRole::Listener {
756 (prefacing.local_nonce, peer_nonce)
757 } else {
758 (peer_nonce, prefacing.local_nonce)
759 };
760 let local_proof = if !matches!(self.peer_proof, PeerProofAssurance::Disabled) {
761 match self
762 .by_secret
763 .as_ref()
764 .map(|by_secret| {
765 peer_proof::create_proof_record(
766 by_secret,
767 self.role,
768 side0_nonce.as_str(),
769 side1_nonce.as_str(),
770 &if self.role == CarrierRole::Listener {
771 side0_nonce.bytes()
772 } else {
773 side1_nonce.bytes()
774 },
775 self.peer_proof,
776 )
777 })
778 .transpose()
779 {
780 Ok(proof) => proof,
781 Err(error) => return self.fail(integration_machine_error(error)),
782 }
783 } else {
784 None
785 };
786 let session = SessionMachine::new(
787 self.role,
788 ConnectedSessionConfig {
789 local_operand: local_operand.operand,
790 local_proof,
791 local_by_value: self.local_by_value,
792 peer_proof: self.peer_proof,
793 expected_peer_by: local_operand.expected_peer_by,
794 side0_nonce,
795 side1_nonce,
796 limits: self.limits.clone(),
797 operand_cache: self.operand_cache.clone(),
798 },
799 );
800 self.owner = CarrierOwner::Running {
801 session: Box::new(session),
802 staged_replacement: None,
803 };
804 Ok(())
805 }
806
807 fn derive_nonce(
808 &mut self,
809 peer: Option<&SessionNonceText>,
810 ) -> Result<SessionNonceText, CoreError> {
811 loop {
812 let sequence = self
813 .next_nonce_sequence
814 .ok_or(CoreError::InvalidConfig("carrier nonce sequence exhausted"))?;
815 self.next_nonce_sequence = sequence.checked_add(1);
816 let mut hasher = blake3::Hasher::new_keyed(self.nonce_seed.expose());
817 hasher.update(NONCE_DOMAIN);
818 hasher.update(&sequence.to_be_bytes());
819 let bytes = *hasher.finalize().as_bytes();
820 if bytes.iter().all(|byte| *byte == 0) || peer.is_some_and(|peer| peer.bytes() == bytes)
821 {
822 continue;
823 }
824 return SessionNonceText::from_bytes(bytes)
825 .map_err(|_| CoreError::InvalidConfig("derived carrier nonce is invalid"));
826 }
827 }
828
829 fn fail<T>(&mut self, error: MachineError) -> Result<T, MachineError> {
830 self.owner = CarrierOwner::Failed(error.clone());
831 Err(error)
832 }
833}
834
835impl PrefacingState {
836 fn new(
837 cause: PrefaceCause,
838 local_operand: Option<SessionOperand>,
839 local_nonce: SessionNonceText,
840 ) -> Self {
841 Self {
842 cause,
843 local_operand,
844 local_nonce,
845 local_preface_accepted: false,
846 peer_nonce: None,
847 retained_output: None,
848 discarded_control_bytes: 0,
849 }
850 }
851 fn with_peer(
852 cause: PrefaceCause,
853 local_operand: Option<SessionOperand>,
854 local_nonce: SessionNonceText,
855 peer_nonce: SessionNonceText,
856 ) -> Self {
857 let mut value = Self::new(cause, local_operand, local_nonce);
858 value.peer_nonce = Some(peer_nonce);
859 value
860 }
861}
862
863#[cfg(test)]
864#[path = "carrier/erasure_tests.rs"]
865mod erasure_tests;
866
867#[cfg(test)]
868#[path = "carrier/tests.rs"]
869mod tests;