From 167af9be7c06b894336746cb76ebd5c9805506ec Mon Sep 17 00:00:00 2001 From: Nathan Perry Date: Fri, 24 Apr 2026 09:45:56 -0400 Subject: [PATCH 1/4] control_noise: rewrite to tokio-codec Signed-off-by: Nathan Perry Change-Id: I7556c1452053f1cc02549e3ad425672c6a6a6964 --- Cargo.lock | 2 + ts_control_noise/Cargo.toml | 2 + ts_control_noise/src/codec.rs | 158 ++++++++++ ts_control_noise/src/framed_io.rs | 149 ++++++++++ ts_control_noise/src/handshake.rs | 25 +- ts_control_noise/src/io.rs | 466 ------------------------------ ts_control_noise/src/lib.rs | 4 +- ts_control_noise/src/messages.rs | 2 +- 8 files changed, 334 insertions(+), 474 deletions(-) create mode 100644 ts_control_noise/src/codec.rs create mode 100644 ts_control_noise/src/framed_io.rs delete mode 100644 ts_control_noise/src/io.rs diff --git a/Cargo.lock b/Cargo.lock index 87845dcd..79226611 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4196,11 +4196,13 @@ dependencies = [ "base64 0.22.1", "bytes", "chacha20poly1305", + "futures-util", "noise-protocol", "noise-rust-crypto", "pin-project-lite", "thiserror 2.0.18", "tokio", + "tokio-util", "tracing", "ts_capabilityversion", "ts_hexdump", diff --git a/ts_control_noise/Cargo.toml b/ts_control_noise/Cargo.toml index b617dd32..c5ba49fb 100644 --- a/ts_control_noise/Cargo.toml +++ b/ts_control_noise/Cargo.toml @@ -21,11 +21,13 @@ ts_packet.workspace = true base64.workspace = true bytes.workspace = true chacha20poly1305 = "0.10" +futures-util = { workspace = true, features = ["sink"] } noise-protocol = "0.2" noise-rust-crypto = "0.6" pin-project-lite.workspace = true thiserror.workspace = true tokio = { workspace = true, features = ["io-util"] } +tokio-util = { workspace = true, features = ["codec"] } tracing.workspace = true zerocopy.workspace = true zeroize = "1.8" diff --git a/ts_control_noise/src/codec.rs b/ts_control_noise/src/codec.rs new file mode 100644 index 00000000..5af77176 --- /dev/null +++ b/ts_control_noise/src/codec.rs @@ -0,0 +1,158 @@ +use std::io::ErrorKind; + +use bytes::{Buf, BufMut, BytesMut}; +use noise_protocol::{Cipher, CipherState}; +use tokio_util::codec::{Decoder, Encoder}; +use zerocopy::{IntoBytes, TryCastError, TryFromBytes, U16}; + +use crate::messages::{ControlMessageHeader, ControlMessageType}; + +pub const MAX_MESSAGE_SIZE: usize = 4096; + +/// Control noise codec that uses a different cipher state for the up and down directions. +/// +/// Just a wrapper containing two [`Codec`]s, one of which provides [`Encoder`] and the +/// other [`Decoder`]. +pub struct BiCodec +where + Tx: Cipher, + Rx: Cipher, +{ + pub tx: Codec, + pub rx: Codec, +} + +impl Encoder for BiCodec +where + B: AsRef<[u8]>, + Tx: Cipher, + Rx: Cipher, +{ + type Error = as Encoder>::Error; + + fn encode(&mut self, item: B, dst: &mut BytesMut) -> Result<(), Self::Error> { + self.tx.encode(item, dst) + } +} + +impl Decoder for BiCodec +where + Tx: Cipher, + Rx: Cipher, +{ + type Item = as Decoder>::Item; + type Error = as Decoder>::Error; + + fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { + self.rx.decode(src) + } +} + +/// Codec supporting encrypting and decrypting data according to the control noise protocol +/// using the specified cipher state. +pub struct Codec +where + C: Cipher, +{ + pub cipher_state: CipherState, +} + +impl From> for Codec +where + C: Cipher, +{ + fn from(value: CipherState) -> Self { + Codec { + cipher_state: value, + } + } +} + +impl Encoder for Codec +where + C: Cipher, + B: AsRef<[u8]>, +{ + type Error = std::io::Error; + + fn encode(&mut self, b: B, dst: &mut BytesMut) -> Result<(), Self::Error> { + let b = b.as_ref(); + let max_data_chunk = MAX_MESSAGE_SIZE - (3 + C::tag_len()); // 3 = header len + + for chunk in b.chunks(max_data_chunk) { + let hdr = ControlMessageHeader { + typ: ControlMessageType::Record, + len: U16::new(chunk.len() as u16 + C::tag_len() as u16), + }; + + dst.put(hdr.as_bytes()); + + let data_start = dst.len(); + + dst.put(chunk); + dst.put_bytes(0, C::tag_len()); + + self.cipher_state + .encrypt_in_place(&mut dst[data_start..], chunk.len()); + } + + Ok(()) + } +} + +impl Decoder for Codec +where + C: Cipher, +{ + type Error = std::io::Error; + type Item = BytesMut; + + fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { + let (header, rest_len) = match ControlMessageHeader::try_ref_from_prefix(src) { + Ok((hdr, rest)) => (*hdr, rest.len()), + Err(TryCastError::Size(_)) => return Ok(None), + Err(e) => { + tracing::error!(error = %e, "parsing control message header"); + return Err(ErrorKind::InvalidData.into()); + } + }; + + if rest_len < header.len() { + return Ok(None); + } + + src.advance(3); + let mut body = src.split_to(header.len()); + + match header.typ { + ControlMessageType::Record => { + let len = body.len(); + + match self.cipher_state.decrypt_in_place(&mut body, len) { + Ok(n) => body.truncate(n), + Err(()) => { + tracing::error!("decryption failed"); + return Err(ErrorKind::InvalidData.into()); + } + } + + Ok(Some(body)) + } + ControlMessageType::Error => { + let error_message = + core::str::from_utf8(&body).unwrap_or(""); + + tracing::error!( + error_message, + error_body_len = body.len(), + "error received from control" + ); + Ok(None) + } + typ => { + tracing::error!(message_type = ?typ, "unexpected message type from control"); + Err(ErrorKind::InvalidData.into()) + } + } + } +} diff --git a/ts_control_noise/src/framed_io.rs b/ts_control_noise/src/framed_io.rs new file mode 100644 index 00000000..14bd526c --- /dev/null +++ b/ts_control_noise/src/framed_io.rs @@ -0,0 +1,149 @@ +use std::{ + pin::Pin, + task::{Context, Poll, ready}, +}; + +use bytes::Buf; +use futures_util::{Sink, Stream}; +use tokio::io::{AsyncBufRead, AsyncRead, AsyncWrite, ReadBuf}; + +pin_project_lite::pin_project! { + /// Simultaneous implementation of [`tokio_util::io::StreamReader`] and + /// [`tokio_util::io::SinkWriter`], a wrapper that turns an inner `Stream` + + /// `Sink` into `AsyncRead` + `AsyncWrite` (for bytes-like types `B1`, `B2`). + /// + /// This avoids interposing the superfluous mutexes required to split the original + /// `Stream` + `Sink` then re-`join` them into a combined `AsyncRead` + `AsyncWrite`. + /// We know the mutexes are superfluous because only one of `poll_read` and + /// `poll_write` can be called at a time, as they take `Pin<&mut Self>`. + /// + /// This is a direct copy-paste of the `tokio_util` implementations; there's no + /// clear reason that they're not the same type there. + pub struct FramedIo { + #[pin] + inner: T, + chunk: Option, + } +} + +impl FramedIo { + /// Construct a new `FramedIo` around the inner `Stream` + `Sink`. + pub const fn new(inner: T) -> Self { + Self { inner, chunk: None } + } + + /// Convert this into the inner I + pub fn into_inner(self) -> (T, Option) { + (self.inner, self.chunk) + } +} + +impl FramedIo +where + B: Buf, +{ + /// Do we have a chunk and is it non-empty? + fn has_chunk(&self) -> bool { + if let Some(ref chunk) = self.chunk { + chunk.remaining() > 0 + } else { + false + } + } +} + +impl AsyncRead for FramedIo +where + T: Stream>, + B: Buf, + E: Into, +{ + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + if buf.remaining() == 0 { + return Poll::Ready(Ok(())); + } + + let inner_buf = match self.as_mut().poll_fill_buf(cx) { + Poll::Ready(Ok(buf)) => buf, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err)), + Poll::Pending => return Poll::Pending, + }; + let len = std::cmp::min(inner_buf.len(), buf.remaining()); + buf.put_slice(&inner_buf[..len]); + + self.consume(len); + Poll::Ready(Ok(())) + } +} + +impl AsyncBufRead for FramedIo +where + T: Stream>, + B: Buf, + E: Into, +{ + fn poll_fill_buf( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + loop { + if self.as_mut().has_chunk() { + // This unwrap is very sad, but it can't be avoided. + let buf = self.project().chunk.as_ref().unwrap().chunk(); + return Poll::Ready(Ok(buf)); + } else { + match self.as_mut().project().inner.poll_next(cx) { + Poll::Ready(Some(Ok(chunk))) => { + // Go around the loop in case the chunk is empty. + *self.as_mut().project().chunk = Some(chunk); + } + Poll::Ready(Some(Err(err))) => return Poll::Ready(Err(err.into())), + Poll::Ready(None) => return Poll::Ready(Ok(&[])), + Poll::Pending => return Poll::Pending, + } + } + } + } + + fn consume(self: Pin<&mut Self>, amt: usize) { + if amt > 0 { + self.project() + .chunk + .as_mut() + .expect("No chunk present") + .advance(amt); + } + } +} + +impl AsyncWrite for FramedIo +where + T: for<'a> Sink<&'a [u8], Error = E>, + E: Into, +{ + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + let mut this = self.project(); + + ready!(this.inner.as_mut().poll_ready(cx).map_err(Into::into))?; + match this.inner.as_mut().start_send(buf) { + Ok(()) => Poll::Ready(Ok(buf.len())), + Err(e) => Poll::Ready(Err(e.into())), + } + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.project().inner.poll_flush(cx).map_err(Into::into) + } + + fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.project().inner.poll_close(cx).map_err(Into::into) + } +} diff --git a/ts_control_noise/src/handshake.rs b/ts_control_noise/src/handshake.rs index 0aca1c14..92a9dc8f 100644 --- a/ts_control_noise/src/handshake.rs +++ b/ts_control_noise/src/handshake.rs @@ -1,8 +1,9 @@ use base64::{Engine, engine::general_purpose::STANDARD}; -use bytes::BufMut; +use bytes::{BufMut, BytesMut}; use noise_protocol::{HandshakeState, HandshakeStateBuilder, patterns::noise_ik}; use noise_rust_crypto::{Blake2s, X25519, sensitive::Sensitive}; use tokio::io::{AsyncRead, AsyncReadExt}; +use tokio_util::codec::Framed; use ts_hexdump::{AsHexExt, Case}; use ts_keys::{MachinePrivateKey, MachinePublicKey}; use ts_packet::PacketMut; @@ -10,13 +11,20 @@ use zerocopy::IntoBytes; use zeroize::Zeroizing; use crate::{ - Error, NoiseIo, + ChaCha20Poly1305BigEndian, Error, + codec::BiCodec, + framed_io::FramedIo, messages::{ControlMessageHeader, INITIATION_PAYLOAD_LEN, InitiationMessage, ResponseMessage}, }; +type Cipher = ChaCha20Poly1305BigEndian; +type Codec = BiCodec; +type NoiseFramed = Framed; +type WrappedIo = FramedIo, BytesMut>; + /// Noise handshake state. pub struct Handshake { - state: HandshakeState, + state: HandshakeState, } impl Handshake { @@ -58,7 +66,7 @@ impl Handshake { pub async fn complete( &mut self, mut conn: T, - ) -> Result, Error> { + ) -> Result, Error> { let mut hdr_bytes = [0u8; 3]; let mut bytes_read = conn.read_exact(&mut hdr_bytes[..]).await?; @@ -93,6 +101,13 @@ impl Handshake { } let (tx, rx) = self.state.get_ciphers(); - Ok(NoiseIo::new(conn, rx, tx)) + + Ok(FramedIo::new(Framed::new( + conn, + BiCodec { + tx: tx.into(), + rx: rx.into(), + }, + ))) } } diff --git a/ts_control_noise/src/io.rs b/ts_control_noise/src/io.rs deleted file mode 100644 index 148272f2..00000000 --- a/ts_control_noise/src/io.rs +++ /dev/null @@ -1,466 +0,0 @@ -use core::{ - pin::Pin, - task::{Context, Poll}, -}; -use std::io::{self, ErrorKind}; - -use bytes::Buf; -use noise_protocol::CipherState; -use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; -use ts_hexdump::{AsHexExt, Case}; -use ts_packet::PacketMut; -use zerocopy::{IntoBytes, TryFromBytes}; - -use crate::{ - cipher::ChaCha20Poly1305BigEndian, - messages::{ControlMessageHeader, ControlMessageType, RecordMessage}, -}; - -pin_project_lite::pin_project! { - /// Wrapper that transparently performs Noise encryption and decryption over an - /// underlying I/O connection at relatively low overhead. Assumes we're speaking the - /// Tailscale control server protocol with a Noise connection which has already been - /// handshaken out-of-band. - /// - /// Does not perform any internarl buffering for I/O optimization -- apply buffering - /// above or below instead. - pub struct NoiseIo { - #[pin] - conn: Conn, - - rx: CipherState, - rx_state: AsyncReadState, - - tx: CipherState, - tx_state: AsyncWriteState, - } -} - -impl NoiseIo { - /// Construct a new Noise connection wrapping the underlying connection with the given - /// cipher states. - /// - /// The cipher states are expected to have been negotiated out-of-band, e.g. by - /// [`Handshake`][crate::Handshake]. - pub const fn new( - conn: Conn, - rx: CipherState, - tx: CipherState, - ) -> Self { - Self { - conn, - rx, - rx_state: AsyncReadState::ReadHeader { - bytes_read: 0, - bytes: [0u8; 3], - }, - tx, - tx_state: AsyncWriteState::Idle, - } - } -} - -enum AsyncReadState { - ReadHeader { - bytes_read: usize, - bytes: [u8; 3], - }, - ReadBody { - ty: ControlMessageType, - len: usize, - body: PacketMut, - }, - ReturnToCaller { - body: PacketMut, - }, -} - -enum AsyncWriteState { - Idle, - WritingPacket(PacketMut), -} - -impl AsyncRead for NoiseIo { - fn poll_read( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut ReadBuf<'_>, - ) -> Poll> { - let mut pin_self = self.project(); - - loop { - match &mut pin_self.rx_state { - AsyncReadState::ReadHeader { bytes_read, bytes } => { - let mut rb = ReadBuf::new(bytes.as_mut_slice()); - core::task::ready!(pin_self.conn.as_mut().poll_read(cx, &mut rb)?); - - *bytes_read += rb.filled().len(); - // Per the docs for `AsyncRead::poll_read`, a return value of 0 indicates we've - // reached EOF, or the ReadBuf had a capacity of 0 - in either case, forward - // the result to the caller to handle. - if *bytes_read == 0 { - tracing::trace!( - "poll_read: ReadHeader: got EOF when reading packet header" - ); - return Poll::Ready(Ok(())); - } - - // We've only read 1-2 bytes of the 3-byte header, stay in this state. - if *bytes_read < 3 { - continue; - } - assert_eq!(*bytes_read, 3); - let hdr = ControlMessageHeader::try_ref_from_bytes(bytes) - .map_err(|e| io::Error::new(ErrorKind::InvalidData, e.to_string()))?; - tracing::trace!("poll_read: ReadHeader: read {hdr:?}, moving to ReadBody"); - *pin_self.rx_state = AsyncReadState::ReadBody { - ty: hdr.typ, - len: hdr.len(), - body: PacketMut::new(hdr.len()), - }; - } - AsyncReadState::ReadBody { ty, len, body } => { - let ty = *ty; - let len = *len; - let mut rb = ReadBuf::new(body.as_mut()); - core::task::ready!(pin_self.conn.as_mut().poll_read(cx, &mut rb)?); - - let bytes_read = rb.filled_mut().len(); - // Per the docs for `AsyncRead::poll_read`, a return value of 0 indicates we've - // reached EOF, or the ReadBuf had a capacity of 0 - in either case, forward - // the result to the caller to handle. - if bytes_read == 0 { - tracing::trace!( - "poll_read: ReadBody({ty:?}): got EOF when reading packet body" - ); - return Poll::Ready(Ok(())); - } - - if body.len() < len { - tracing::trace!( - "poll_read: ReadBody({ty:?}): read {bytes_read} bytes, have {}/{len} bytes of body", - body.len() - ); - continue; - } - - tracing::trace!("poll_read: ReadBody({ty:?}): have full message body, parsing"); - match ty { - ControlMessageType::Record => { - if body.len() != len { - let err = io::Error::new( - ErrorKind::InvalidData, - format!( - "header length ({len} bytes) does not match actual length ({} bytes)", - body.len() - ), - ); - Err(err)?; - } - let body = match pin_self.rx.decrypt_in_place(body.as_mut(), len) { - Ok(plaintext_len) => { - let packet = body.split_to(plaintext_len); - tracing::trace!( - bytes_received = plaintext_len, - "poll_read: ReadBody({ty:?}): received packet:\n{}", - packet - .iter() - .hexdump(Case::Lower) - .flatten() - .collect::() - ); - packet - } - Err(e) => { - tracing::trace!( - "poll_read: ReadBody({ty:?}): error decrypting Record message from control: {e:?}" - ); - *pin_self.rx_state = AsyncReadState::ReadHeader { - bytes_read: 0, - bytes: [0u8; 3], - }; - return Poll::Ready(Err(io::Error::other( - "noise decryption failed", - ))); - } - }; - tracing::trace!( - "poll_read: ReadBody({ty:?}): full message parsed, moving to ReturnToCaller" - ); - *pin_self.rx_state = AsyncReadState::ReturnToCaller { body }; - } - ControlMessageType::Error => { - let msg = core::str::from_utf8(body.as_ref()) - .map_err(|e| io::Error::new(ErrorKind::InvalidData, e))?; - let err = io::Error::other(msg); - tracing::trace!( - "poll_read: ReadBody({ty:?}): control returned an Error message: {msg}" - ); - tracing::trace!("poll_read: ReadBody({ty:?}): moving to ReadHeader"); - *pin_self.rx_state = AsyncReadState::ReadHeader { - bytes_read: 0, - bytes: [0u8; 3], - }; - return Poll::Ready(Err(err)); - } - _ => { - tracing::trace!( - "poll_read: ReadBody({ty:?}): unexpected message type from control, moving to ReadHeader" - ); - *pin_self.rx_state = AsyncReadState::ReadHeader { - bytes_read: 0, - bytes: [0u8; 3], - }; - return Poll::Ready(Err(io::Error::new( - ErrorKind::InvalidData, - format!("unexpected message type {:?}", ty), - ))); - } - } - } - AsyncReadState::ReturnToCaller { body } => { - assert!(!body.is_empty()); - - let remaining = body.len().min(buf.remaining()); - let rest = body.split_off(remaining); - tracing::trace!( - "poll_read: ReturnToCaller: returning {remaining}/{} bytes to caller", - body.len() - ); - buf.put_slice(body.as_ref()); - - if rest.is_empty() { - tracing::trace!( - "poll_read: ReturnToCaller: returned full message, moving to ReadHeader" - ); - *pin_self.rx_state = AsyncReadState::ReadHeader { - bytes_read: 0, - bytes: [0u8; 3], - }; - } else { - *pin_self.rx_state = AsyncReadState::ReturnToCaller { body: rest }; - } - return Poll::Ready(Ok(())); - } - } - } - } -} - -impl AsyncWrite for NoiseIo { - #[tracing::instrument(skip_all)] - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - mut buf: &[u8], - ) -> Poll> { - let mut pin_self = self.project(); - let plaintext_len = buf.len(); - let buf = &mut buf; - - loop { - match &mut pin_self.tx_state { - AsyncWriteState::Idle => { - if buf.is_empty() { - return Poll::Ready(Ok(plaintext_len)); - } - - tracing::trace!( - plaintext_len, - "Idle -> WritingPacket: encrypting plaintext payload:\n{}", - buf.iter() - .hexdump(Case::Lower) - .flatten() - .collect::() - ); - // TOOD (dylan): wat? make constant - let auth_data_len = 16; - // TOOD (dylan): wat? make constant - let payload_len = plaintext_len.min(4096); - - let mut slice = buf.take(payload_len); - let chunk = slice.chunk(); - let chunk_len = chunk.len(); - let packet_len = size_of::() + chunk_len + auth_data_len; - let mut packet = PacketMut::new(packet_len); - let record = RecordMessage::new_in(packet.as_mut(), chunk_len + auth_data_len)?; - chunk - .write_to_prefix(&mut record.data) - .map_err(|e| io::Error::new(ErrorKind::InvalidData, e.to_string()))?; - pin_self.tx.encrypt_in_place(&mut record.data, chunk_len); - let encrypted_len = packet.len(); - tracing::trace!( - encrypted_len, - "Idle -> WritingPacket: writing encrypted payload:\n{}", - packet - .iter() - .hexdump(Case::Lower) - .flatten() - .collect::() - ); - slice.advance(chunk_len); - tracing::trace!( - plaintext_len, - encrypted_len, - final_len = buf.len(), - "Idle -> WritingPacket: encrypted payload written" - ); - - *pin_self.tx_state = AsyncWriteState::WritingPacket(packet); - } - AsyncWriteState::WritingPacket(packet) => { - match pin_self.conn.as_mut().poll_write(cx, packet.as_ref()) { - // Per the docs for `AsyncWrite::poll_write`, a return value of 0 typically - // indicates EOF - whatever we're writing to isn't accepting bytes anymore - // and probably won't accept them in the future. Take the minimally- - // opinionated approach here and forward the return value up to the caller; - // if the caller decides to poll us again, we'll resume from the current - // state (not dropping the data in the buffer). - Poll::Ready(Ok(0)) => { - tracing::warn!( - "WritingPacket[Ready(Ok(0))]: got EOF when writing packet" - ); - return Poll::Ready(Ok(0)); - } - Poll::Ready(Ok(bytes_written)) => { - packet.truncate_front(bytes_written); - if packet.is_empty() { - tracing::trace!( - "WritingPacket[Ready(Ok)] -> Idle: packet written fully, moving to Idle state" - ); - *pin_self.tx_state = AsyncWriteState::Idle; - } else { - tracing::trace!( - bytes_remaining = packet.len(), - bytes_written, - "WritingPacket[Ready(Ok)]: packet partially written, remaining to write:\n{}", - packet - .iter() - .hexdump(Case::Lower) - .flatten() - .collect::() - ); - } - } - Poll::Ready(Err(e)) => { - tracing::trace!( - "WritingPacket[Ready(Err)] -> Err: error writing packet: {e}" - ); - return Poll::Ready(Err(e)); - } - Poll::Pending => { - tracing::trace!( - bytes_remaining = packet.len(), - "WritingPacket[Pending]: waiting to write packet:\n{}", - packet - .iter() - .hexdump(Case::Lower) - .flatten() - .collect::() - ); - if buf.len() == plaintext_len { - tracing::trace!( - "WritingPacket[Pending] -> WritingPacket[Pending]: still waiting" - ); - return Poll::Pending; - } else { - let bytes_written = plaintext_len - buf.len(); - tracing::trace!( - bytes_written, - "WritingPacket[Pending] -> WritingPacket[Ready(Ok)]: wrote bytes, moving to Ready(Ok)" - ); - return Poll::Ready(Ok(bytes_written)); - } - } - } - } - } - } - } - - #[tracing::instrument(skip_all)] - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let mut pin_self = self.project(); - - loop { - match &mut pin_self.tx_state { - AsyncWriteState::Idle => { - tracing::trace!("Idle: internal poll_flush"); - return pin_self.conn.as_mut().poll_flush(cx); - } - AsyncWriteState::WritingPacket(packet) => { - tracing::trace!( - "WritingPacket: packet is {} bytes:\n{}", - packet.len(), - packet - .iter() - .hexdump(Case::Lower) - .flatten() - .collect::() - ); - match pin_self.conn.as_mut().poll_write(cx, packet.as_ref()) { - Poll::Ready(Ok(bytes_written)) => { - tracing::trace!( - "poll_flush: WritingPacket: Ready(Ok({bytes_written})): packet is {} bytes:\n{}", - packet.len(), - packet - .iter() - .hexdump(Case::Lower) - .flatten() - .collect::() - ); - packet.truncate_front(bytes_written); - tracing::trace!( - "poll_flush: WritingPacket: packet truncated to {} bytes", - packet.len() - ); - - // Per the docs for `AsyncWrite::poll_write`, writing 0 bytes indicates - // the underlying connection is EOF or otherwise not accepting bytes, - // and isn't expected to accept bytes again in the future. To complete - // the flush, move to the Idle state. We'll likely lose the bytes that - // we read from the packet, since we can't write them to the `conn`, - // but if we don't move to Idle we'll likely end up stuck in the - // Poll::Ready(Ok) state trying to write to a `conn` that won't ever - // start accepting bytes again. - if packet.is_empty() || bytes_written == 0 { - tracing::trace!( - "poll_flush: WritingPacket: Ready(Ok({bytes_written})) -> Idle" - ); - *pin_self.tx_state = AsyncWriteState::Idle; - } - } - Poll::Ready(Err(e)) => { - tracing::trace!("poll_flush: WritingPacket: Ready(Err({e})) -> Err"); - return Poll::Ready(Err(e)); - } - Poll::Pending => { - tracing::trace!( - "poll_flush: WritingPacket: Pending: packet is {} bytes:\n{}", - packet.len(), - packet - .iter() - .hexdump(Case::Lower) - .flatten() - .collect::() - ); - tracing::trace!("poll_flush: WritingPacket: Pending -> Pending"); - return Poll::Pending; - } - } - } - } - } - } - - #[tracing::instrument(skip_all)] - fn poll_shutdown( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - tracing::trace!("flushing"); - core::task::ready!(AsyncWrite::poll_flush(self.as_mut(), cx)?); - tracing::trace!("conn.poll_shutdown"); - self.project().conn.as_mut().poll_shutdown(cx) - } -} diff --git a/ts_control_noise/src/lib.rs b/ts_control_noise/src/lib.rs index 302306a9..f5fc4cba 100644 --- a/ts_control_noise/src/lib.rs +++ b/ts_control_noise/src/lib.rs @@ -1,12 +1,12 @@ #![doc = include_str!("../README.md")] mod cipher; +mod codec; mod error; +mod framed_io; mod handshake; -mod io; mod messages; pub use cipher::ChaCha20Poly1305BigEndian; pub use error::Error; pub use handshake::Handshake; -pub use io::NoiseIo; diff --git a/ts_control_noise/src/messages.rs b/ts_control_noise/src/messages.rs index 895f2104..31cbfc72 100644 --- a/ts_control_noise/src/messages.rs +++ b/ts_control_noise/src/messages.rs @@ -18,7 +18,7 @@ pub(crate) enum ControlMessageType { Record = 0x4, } -#[derive(Debug, TryFromBytes, IntoBytes, KnownLayout, Immutable)] +#[derive(Copy, Clone, Debug, TryFromBytes, IntoBytes, KnownLayout, Immutable)] pub(crate) struct ControlMessageHeader { pub typ: ControlMessageType, pub len: U16, From f910b1a3fcc1b8b0eb49be24defd0962efe0e307 Mon Sep 17 00:00:00 2001 From: Nathan Perry Date: Fri, 24 Apr 2026 15:45:40 -0400 Subject: [PATCH 2/4] control_noise: simplify handshake, expand exports Simplify the handshake to read the header and then the body in two successive steps. Drop most of the message types, as they rewrap the body, which is handled on its own by `codec`. Export more of the internals -- this is an internals crate anyway, might as well open the definitions if other people might want to use it. Improve docs as part of that. Signed-off-by: Nathan Perry Change-Id: I5a037e0ac35dd969812d7ab68cf6ca3c6a6a6964 --- Cargo.lock | 1 - ts_control_noise/Cargo.toml | 1 - ts_control_noise/src/codec.rs | 23 ++--- ts_control_noise/src/handshake.rs | 37 +++----- ts_control_noise/src/lib.rs | 2 + ts_control_noise/src/messages.rs | 143 ++++++++---------------------- 6 files changed, 65 insertions(+), 142 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 79226611..3bca9aea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4207,7 +4207,6 @@ dependencies = [ "ts_capabilityversion", "ts_hexdump", "ts_keys", - "ts_packet", "zerocopy", "zeroize", ] diff --git a/ts_control_noise/Cargo.toml b/ts_control_noise/Cargo.toml index c5ba49fb..c4e57b3d 100644 --- a/ts_control_noise/Cargo.toml +++ b/ts_control_noise/Cargo.toml @@ -15,7 +15,6 @@ rust-version.workspace = true ts_capabilityversion.workspace = true ts_hexdump.workspace = true ts_keys.workspace = true -ts_packet.workspace = true # Unconditionally required dependencies. base64.workspace = true diff --git a/ts_control_noise/src/codec.rs b/ts_control_noise/src/codec.rs index 5af77176..b3d0f056 100644 --- a/ts_control_noise/src/codec.rs +++ b/ts_control_noise/src/codec.rs @@ -5,8 +5,9 @@ use noise_protocol::{Cipher, CipherState}; use tokio_util::codec::{Decoder, Encoder}; use zerocopy::{IntoBytes, TryCastError, TryFromBytes, U16}; -use crate::messages::{ControlMessageHeader, ControlMessageType}; +use crate::messages::{Header, MessageType}; +/// The maximum wire size of a message to control over noise. pub const MAX_MESSAGE_SIZE: usize = 4096; /// Control noise codec that uses a different cipher state for the up and down directions. @@ -18,7 +19,9 @@ where Tx: Cipher, Rx: Cipher, { + /// The transmit codec, used for encoding messages to control. pub tx: Codec, + /// The receive codec, used for decoding messages from control. pub rx: Codec, } @@ -54,6 +57,7 @@ pub struct Codec where C: Cipher, { + /// The cipher state to use to encode and decode message payloads. pub cipher_state: CipherState, } @@ -80,8 +84,8 @@ where let max_data_chunk = MAX_MESSAGE_SIZE - (3 + C::tag_len()); // 3 = header len for chunk in b.chunks(max_data_chunk) { - let hdr = ControlMessageHeader { - typ: ControlMessageType::Record, + let hdr = Header { + typ: MessageType::Record, len: U16::new(chunk.len() as u16 + C::tag_len() as u16), }; @@ -108,7 +112,7 @@ where type Item = BytesMut; fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { - let (header, rest_len) = match ControlMessageHeader::try_ref_from_prefix(src) { + let (header, rest_len) = match Header::try_ref_from_prefix(src) { Ok((hdr, rest)) => (*hdr, rest.len()), Err(TryCastError::Size(_)) => return Ok(None), Err(e) => { @@ -116,18 +120,17 @@ where return Err(ErrorKind::InvalidData.into()); } }; + let len = header.len.get() as usize; - if rest_len < header.len() { + if rest_len < len { return Ok(None); } src.advance(3); - let mut body = src.split_to(header.len()); + let mut body = src.split_to(len); match header.typ { - ControlMessageType::Record => { - let len = body.len(); - + MessageType::Record => { match self.cipher_state.decrypt_in_place(&mut body, len) { Ok(n) => body.truncate(n), Err(()) => { @@ -138,7 +141,7 @@ where Ok(Some(body)) } - ControlMessageType::Error => { + MessageType::Error => { let error_message = core::str::from_utf8(&body).unwrap_or(""); diff --git a/ts_control_noise/src/handshake.rs b/ts_control_noise/src/handshake.rs index 92a9dc8f..ed22449f 100644 --- a/ts_control_noise/src/handshake.rs +++ b/ts_control_noise/src/handshake.rs @@ -1,20 +1,19 @@ use base64::{Engine, engine::general_purpose::STANDARD}; -use bytes::{BufMut, BytesMut}; +use bytes::BytesMut; use noise_protocol::{HandshakeState, HandshakeStateBuilder, patterns::noise_ik}; use noise_rust_crypto::{Blake2s, X25519, sensitive::Sensitive}; use tokio::io::{AsyncRead, AsyncReadExt}; use tokio_util::codec::Framed; use ts_hexdump::{AsHexExt, Case}; use ts_keys::{MachinePrivateKey, MachinePublicKey}; -use ts_packet::PacketMut; -use zerocopy::IntoBytes; +use zerocopy::{IntoBytes, TryFromBytes}; use zeroize::Zeroizing; use crate::{ ChaCha20Poly1305BigEndian, Error, codec::BiCodec, framed_io::FramedIo, - messages::{ControlMessageHeader, INITIATION_PAYLOAD_LEN, InitiationMessage, ResponseMessage}, + messages::{Header, Initiation, MessageType}, }; type Cipher = ChaCha20Poly1305BigEndian; @@ -52,12 +51,11 @@ impl Handshake { let mut state = builder.build_handshake_state(); let overhead = state.get_next_message_overhead(); - let mut ciphertext = [0u8; INITIATION_PAYLOAD_LEN]; + let mut ciphertext = [0u8; Initiation::PAYLOAD_LEN]; state .write_message(&[], &mut ciphertext) .expect("initiation payload size too small"); - let init_msg = - InitiationMessage::new(capability_version.into(), overhead as u16, ciphertext); + let init_msg = Initiation::new(capability_version.into(), overhead as u16, ciphertext); (Self { state }, STANDARD.encode(init_msg.as_bytes())) } @@ -68,34 +66,27 @@ impl Handshake { mut conn: T, ) -> Result, Error> { let mut hdr_bytes = [0u8; 3]; - let mut bytes_read = conn.read_exact(&mut hdr_bytes[..]).await?; + conn.read_exact(&mut hdr_bytes[..]).await?; - let hdr_bytes = &hdr_bytes[..bytes_read]; - let hdr = ControlMessageHeader::try_parse(hdr_bytes)?; + let hdr = Header::try_ref_from_bytes(&hdr_bytes)?; - let mut packet = PacketMut::with_capacity(hdr_bytes.len() + hdr.len()); - - packet.put_slice(hdr_bytes); - packet.put_bytes(0, hdr.len()); // capacity != len, fill empty - - bytes_read += conn.read_exact(&mut packet[hdr_bytes.len()..]).await?; + let mut packet = BytesMut::zeroed(hdr.len.get() as _); + conn.read_exact(&mut packet).await?; tracing::trace!( ?hdr, - %bytes_read, - "received from control:\n{}", + "response body from control:\n{}", packet .iter() .hexdump(Case::Lower) .flatten() .collect::() ); - let response = ResponseMessage::try_parse(packet.as_ref())?; - let len = response.hdr.len.get() as usize; - - tracing::debug!(%len, ?response); + if hdr.typ != MessageType::Response { + return Err(Error::BadFormat); + } - let data = self.state.read_message_vec(&response.data[0..len])?; + let data = self.state.read_message_vec(&packet)?; if !data.is_empty() || !self.state.completed() { return Err(Error::HandshakeFailed); } diff --git a/ts_control_noise/src/lib.rs b/ts_control_noise/src/lib.rs index f5fc4cba..42feec95 100644 --- a/ts_control_noise/src/lib.rs +++ b/ts_control_noise/src/lib.rs @@ -8,5 +8,7 @@ mod handshake; mod messages; pub use cipher::ChaCha20Poly1305BigEndian; +pub use codec::{BiCodec, Codec, MAX_MESSAGE_SIZE}; pub use error::Error; pub use handshake::Handshake; +pub use messages::{Header, Initiation, MessageType}; diff --git a/ts_control_noise/src/messages.rs b/ts_control_noise/src/messages.rs index 31cbfc72..2261d067 100644 --- a/ts_control_noise/src/messages.rs +++ b/ts_control_noise/src/messages.rs @@ -1,135 +1,64 @@ -use core::fmt::Formatter; - use zerocopy::{Immutable, IntoBytes, KnownLayout, TryFromBytes, network_endian::U16}; -use crate::Error; - -/// The fixed length of an [`InitiationMessage`] payload, in bytes. -pub(crate) const INITIATION_PAYLOAD_LEN: usize = 96; - -#[repr(u8)] +/// Type for messages sent to and from a Tailscale control server. #[derive(Clone, Copy, Debug, Eq, PartialEq, TryFromBytes, IntoBytes, KnownLayout, Immutable)] -pub(crate) enum ControlMessageType { +#[repr(u8)] +pub enum MessageType { + /// Initiation message (client to control). Initiation = 0x1, + + /// Response to an initiation (received from control). Response = 0x2, - #[allow(dead_code)] - // TODO (dylan): investigate when errors can happen, handle them + + /// An error occurred. + /// + /// The body of the message is a cleartext string describing it. Error = 0x3, + + /// Encrypted data. Record = 0x4, } +/// A message header (3 bytes). #[derive(Copy, Clone, Debug, TryFromBytes, IntoBytes, KnownLayout, Immutable)] -pub(crate) struct ControlMessageHeader { - pub typ: ControlMessageType, +pub struct Header { + /// The type of the message. + pub typ: MessageType, + /// The length of the following body (not including the header). pub len: U16, } -impl ControlMessageHeader { - pub fn len(&self) -> usize { - self.len.get() as usize - } - - pub(crate) fn try_parse(buf: &[u8]) -> Result<&Self, Error> { - Ok(ControlMessageHeader::try_ref_from_bytes(buf)?) - } -} - -#[repr(C)] +/// An initiation message. +/// +/// Distinct from other message types in that it starts with the capability version. #[derive(IntoBytes, KnownLayout, Immutable)] -pub(crate) struct InitiationMessage { - capability_version: U16, - hdr: ControlMessageHeader, - payload: [u8; INITIATION_PAYLOAD_LEN], +#[repr(C)] +pub struct Initiation { + /// The capability version of this node. + pub capability_version: U16, + /// The typical header. + pub hdr: Header, + /// The payload, consisting of the noise handshake initiation. + pub payload: [u8; Self::PAYLOAD_LEN], } -impl InitiationMessage { +impl Initiation { + /// The fixed length of an [`Initiation`] payload, in bytes. + pub const PAYLOAD_LEN: usize = 96; + + /// Construct a new initiation message. pub fn new( capability_version: u16, overhead_len: u16, - payload: [u8; INITIATION_PAYLOAD_LEN], + payload: [u8; Self::PAYLOAD_LEN], ) -> Self { Self { capability_version: capability_version.into(), - hdr: ControlMessageHeader { - typ: ControlMessageType::Initiation, + hdr: Header { + typ: MessageType::Initiation, len: overhead_len.into(), }, payload, } } } - -#[repr(C)] -#[derive(TryFromBytes, IntoBytes, KnownLayout, Immutable)] -pub(crate) struct ResponseMessage { - pub hdr: ControlMessageHeader, - pub data: [u8], -} - -impl core::fmt::Debug for ResponseMessage { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - f.debug_struct(core::any::type_name::()) - .field("hdr", &self.hdr) - .field("data", &&self.data as &dyn core::fmt::Debug) - .finish() - } -} - -impl ResponseMessage { - pub(crate) fn try_parse(buf: &[u8]) -> Result<&Self, Error> { - let resp = ResponseMessage::try_ref_from_bytes(buf)?; - - if resp.hdr.typ != ControlMessageType::Response { - return Err(Error::BadFormat); - } - Ok(resp) - } -} - -/// An error message sent from the control plane to the local node, indicating something went wrong -/// during the initial control handshake. -/// -/// Use the [`ErrorMessage::message`] method to determine the exact error. -#[repr(C)] -#[derive(TryFromBytes, IntoBytes, KnownLayout, Immutable)] -pub(crate) struct ErrorMessage { - pub hdr: ControlMessageHeader, - msg: [u8], -} - -#[allow(dead_code)] -impl ErrorMessage { - /// Try to parse the given byte slice into an [`ErrorMessage`]; otherwise, return an error. - pub(crate) fn try_parse(buf: &[u8]) -> Result<&Self, Error> { - let resp = ErrorMessage::try_ref_from_bytes(buf)?; - if resp.hdr.typ != ControlMessageType::Error { - return Err(Error::BadFormat); - } - Ok(resp) - } - - /// Try to parse the [`ErrorMessage::msg`] field into a `&str` and return it; otherwise, return - /// an error. - pub fn message(&self) -> Result<&str, Error> { - core::str::from_utf8(&self.msg).map_err(From::from) - } -} - -#[repr(C)] -#[derive(TryFromBytes, IntoBytes, KnownLayout, Immutable)] -pub(crate) struct RecordMessage { - pub hdr: ControlMessageHeader, - pub data: [u8], -} - -impl RecordMessage { - pub fn new_in(buf: &mut [u8], len: usize) -> Result<&mut Self, Error> { - let len_be = U16::new(len as u16); - let hdr = ControlMessageHeader { - typ: ControlMessageType::Record, - len: len_be, - }; - hdr.write_to_prefix(buf)?; - Ok(Self::try_mut_from_bytes(buf)?) - } -} From 52384c928499dca4131f55efc7cd18ae4cbaef15 Mon Sep 17 00:00:00 2001 From: Nathan Perry Date: Fri, 24 Apr 2026 16:06:32 -0400 Subject: [PATCH 3/4] control_noise: codec and i/o tests Signed-off-by: Nathan Perry Change-Id: Ia83e08aff37ad6303ba57c31dea773c86a6a6964 --- Cargo.lock | 2 + ts_control_noise/Cargo.toml | 4 + .../proptest-regressions/codec.txt | 9 ++ ts_control_noise/src/codec.rs | 143 ++++++++++++++++++ 4 files changed, 158 insertions(+) create mode 100644 ts_control_noise/proptest-regressions/codec.txt diff --git a/Cargo.lock b/Cargo.lock index 3bca9aea..626ed55e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4200,6 +4200,8 @@ dependencies = [ "noise-protocol", "noise-rust-crypto", "pin-project-lite", + "proptest", + "rand 0.10.1", "thiserror 2.0.18", "tokio", "tokio-util", diff --git a/ts_control_noise/Cargo.toml b/ts_control_noise/Cargo.toml index c4e57b3d..9e67692d 100644 --- a/ts_control_noise/Cargo.toml +++ b/ts_control_noise/Cargo.toml @@ -31,5 +31,9 @@ tracing.workspace = true zerocopy.workspace = true zeroize = "1.8" +[dev-dependencies] +rand = "0.10" +proptest = "1.11" + [lints] workspace = true diff --git a/ts_control_noise/proptest-regressions/codec.txt b/ts_control_noise/proptest-regressions/codec.txt new file mode 100644 index 00000000..8d1489bb --- /dev/null +++ b/ts_control_noise/proptest-regressions/codec.txt @@ -0,0 +1,9 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc 65945832c4cf0fe2f6a487d35e54de23eff5a1d571bd0e8b9978a7f7c68d5136 # shrinks to payload = [104, 98, 157, 172, 229, 82, 217, 217, 100, 15, 35, 123, 13, 189, 48, 189, 38, 132, 79, 168, 96, 75, 225, 255, 198, 254, 209, 227, 154, 210, 75, 40, 98, 61, 197, 71, 246, 250, 167, 144, 240, 26, 17, 182, 47, 254, 199, 43, 98, 202, 89, 100, 89, 0, 173, 52, 39, 48, 11, 15, 37, 116, 81, 159, 119, 207, 141, 201, 51, 185, 102, 28, 255, 86, 207, 4, 36, 254, 31, 245, 131, 117, 37, 107, 178, 110, 87, 96, 116, 124, 13, 25, 65, 208, 204, 235, 240, 124, 136, 1, 76, 207, 8, 49, 126, 11, 106, 92, 208, 87, 173, 131, 189, 52, 251, 84, 161, 246, 25, 135, 53, 24, 126, 241, 77, 26, 45, 143, 234, 24, 11, 68, 120, 132, 20, 86, 195, 97, 153, 189, 79, 33, 129, 235, 141, 183, 154, 5, 61, 103, 112, 252, 101, 16, 36, 133, 116, 77, 178, 243, 212, 215, 84, 82, 26, 189, 138, 95, 120, 174, 103, 242, 185, 72, 190, 75, 198, 200, 66, 163, 190, 29, 143, 154, 9, 25, 213, 114, 6, 10, 152, 30, 71, 70, 183, 230, 240, 204, 123, 244, 65, 233, 44, 254, 179, 108, 48, 168, 101, 235, 29, 236, 2, 238, 18, 122, 176, 41, 96, 135, 244, 160, 155, 4, 110, 51, 1, 184, 158, 45, 171, 46, 110, 183, 93, 249, 81, 156, 153, 3, 75, 244, 32, 82, 80, 221, 3, 121, 235, 129, 5, 149, 234, 250, 59, 29, 155, 178, 35, 169, 158, 166, 141, 162, 219, 37, 232, 203, 188, 2, 203, 56, 144, 2, 121, 146, 77, 35, 244, 92, 97, 113, 63, 192, 78, 101, 53, 243, 21, 68, 145, 136, 183, 51, 165, 137, 189, 172, 211, 126, 123, 207, 165, 137, 236, 115, 225, 53, 115, 78, 167, 107, 46, 64, 14, 19, 247, 125, 1, 143, 185, 190, 93, 54, 150, 196, 87, 48, 94, 185, 148, 140, 123, 62, 251, 26, 77, 102, 86, 190, 240, 24, 82, 10, 180, 23, 234, 172, 198, 16, 19, 205, 218, 170, 220, 179, 49, 237, 65, 128, 127, 24, 112, 19, 185, 205, 2, 195, 182, 196, 40, 40, 155, 24, 2, 42, 101, 83, 68, 170, 70, 250, 147, 145, 251, 85, 248, 244, 125, 244, 183, 74, 246, 130, 77, 36, 49, 215, 10, 149, 211, 139, 220, 61, 230, 215, 191, 180, 224, 171, 10, 125, 153, 19, 112, 122, 29, 170, 17, 16, 132, 198, 85, 88, 25, 147, 169, 36, 226, 95, 52, 70, 189, 177, 214, 224, 203, 16, 53, 255, 219, 0, 189, 39, 107, 29, 77, 49, 75, 97, 146, 43, 9, 185, 61, 251, 88, 177, 52, 40, 244, 77, 235, 199, 81, 98, 233, 108, 227, 197, 100, 190, 155, 14, 44, 52, 235, 143, 99, 108, 217, 72, 80, 28, 117, 239, 24, 201, 97, 129, 214, 237, 203, 176, 56, 188, 250, 115, 243, 84, 187, 37, 233, 236, 45, 122, 238, 30, 167, 166, 120, 235, 62, 118, 244, 217, 13, 65, 97, 41, 33, 144, 255, 174, 29, 31, 19, 100, 133, 31, 19, 81, 98, 117, 161, 203, 58, 245, 23, 197, 13, 13, 150, 172, 252, 45, 177, 26, 219, 139, 129, 90, 192, 45, 238, 200, 125, 241, 80, 157, 226, 60, 18, 50, 220, 103, 95, 62, 43, 94, 112, 132, 68, 35, 14, 1, 253, 48, 212, 18, 55, 183, 198, 156, 238, 206, 104, 96, 6, 112, 124, 168, 145, 183, 167, 204, 37, 53, 67, 154, 108, 38, 130, 120, 91, 135, 13, 126, 84, 3, 125, 97, 95, 224, 240, 92, 123, 244, 78, 168, 48, 47, 168, 151, 188, 233, 8, 194, 113, 243, 146, 24, 91, 171, 40, 156, 251, 117, 108, 37, 159, 89, 50, 22, 177, 188, 89, 27, 88, 54, 84, 186, 204, 115, 148, 75, 129, 90, 2, 103, 154, 215, 204, 205, 206, 119, 157, 212, 157, 6, 220, 245, 160, 247, 146, 152, 215, 122, 62, 64, 73, 41, 50, 164, 138, 172, 242, 216, 198, 25, 93, 104, 91, 137, 25, 126, 7, 54, 206, 61, 153, 52, 145, 60, 142, 149, 0, 58, 16, 89, 35, 181, 214, 197, 83, 18, 248, 55, 32, 244, 62, 167, 171, 181, 235, 246, 203, 74, 223, 199, 180, 235, 215, 203, 152, 219, 155, 16, 174, 60, 27, 208, 27, 202, 198, 128, 91, 63, 234, 225, 190, 175, 171, 116, 95, 18, 154, 3, 219, 21, 67, 193, 251, 250, 25, 109, 213, 157, 69, 30, 156, 187, 167, 128, 129, 138, 35, 79, 55, 252, 181, 226, 233, 116, 63, 11, 136, 151, 244, 92, 195, 153, 149, 18, 216, 199, 65, 60, 52, 98, 243, 28, 47, 250, 87, 245, 61, 238, 26, 139, 145, 220, 97, 120, 146, 248, 147, 19, 40, 113, 40, 51, 31, 117, 149, 226, 139, 80, 80, 173, 187, 129, 175, 246, 17, 89, 221, 65, 180, 207, 172, 97, 135, 180, 118, 92, 87, 43, 248, 182, 28, 15, 176, 8, 110, 159, 225, 80, 102, 220, 112, 236, 250, 52, 84, 93, 163, 230, 16, 188, 72, 98, 232, 127, 124, 233, 190, 224, 60, 137, 149, 242, 101, 81, 115, 203, 221, 68, 17, 43, 106, 146, 79, 133, 145, 250, 34, 42, 43, 2, 79, 106, 46, 212, 34, 185, 181, 116, 12, 202, 14, 176, 103, 57, 212, 132, 86, 216, 253, 152, 234, 22, 82, 219, 186, 223, 135, 44, 209, 132, 253, 239, 40, 167, 80, 24, 36, 104, 191, 151, 0, 95, 105, 212, 85, 101, 157, 189, 6, 201, 136, 193, 239, 151, 107, 184, 208, 50, 119, 47, 21, 198, 254, 216, 24, 97, 70, 116, 134, 51, 153, 137, 138, 197, 194, 32, 194, 2, 250, 82, 141, 149, 167, 252, 57, 180, 171, 169, 86, 163, 144, 215, 132, 88, 240, 87, 81, 76, 33, 243, 66, 197, 63, 179, 149, 155, 204, 51, 72, 76, 59, 146, 214, 7, 22, 24, 36, 109, 238, 122, 0, 63, 2, 196, 220, 61, 150, 21, 148, 9, 48, 250, 222, 123, 69, 199, 164, 131, 220, 208, 163, 192, 7, 122, 17, 59, 108, 102, 174, 212, 188, 171, 177, 86, 123, 51, 237, 125, 12, 81, 195, 60, 232, 64, 190, 207, 195, 116, 27, 26, 3, 171, 109, 102, 75, 83, 209, 95, 199, 207, 153, 215, 73, 88, 186, 186, 122, 131, 39, 171, 15, 244, 148, 225, 83, 38, 184, 239, 37, 94, 246, 65, 40, 143, 171, 85, 216, 118, 145, 100, 32, 43, 33, 122, 51, 182, 0, 85, 10, 198, 113, 204, 223, 127, 230, 121, 63, 255, 114, 56, 158, 246, 179, 214, 9, 144, 17, 82, 233, 208, 131, 12, 242, 207, 230, 33, 60, 4, 14, 201, 125, 246, 152, 38, 212, 126, 45, 219, 145, 167, 171, 8, 74, 227, 48, 67, 221, 227, 141, 189, 0, 109, 16, 161, 83, 130, 60, 94, 7, 171, 55, 196, 92, 101, 108, 208, 158, 213, 86, 251, 16, 139, 177, 47, 251, 236, 242, 79, 168, 247, 93, 251, 145, 97, 151, 182, 91, 173, 130, 38, 105, 134, 21, 85, 9, 107, 226, 87, 146, 231, 220, 89, 73, 12, 239, 77, 23, 23, 0, 250, 109, 72, 6, 10, 147, 236, 230, 192, 185, 141, 43, 231, 30, 198, 61, 50, 195, 172, 104, 147, 0, 219, 240, 174, 185, 107, 194, 61, 230, 230, 244, 234, 47, 123, 128, 24, 99, 168, 197, 103, 201, 221, 174, 210, 30, 37, 169, 222, 152, 56, 149, 31, 110, 172, 144, 23, 133, 131, 150, 77, 121, 42, 93, 52, 101, 190, 137, 118, 245, 178, 28, 120, 55, 42, 226, 154, 9, 132, 201, 18, 188, 45, 247, 150, 188, 81, 188, 181, 231, 220, 51, 143, 108, 49, 16, 13, 108, 114, 54, 89, 34, 139, 97, 225, 121, 42, 175, 48, 215, 126, 77, 98, 57, 27, 217, 24, 151, 200, 112, 226, 228, 2, 27, 105, 67, 138, 160, 200, 51, 180, 217, 134, 133, 27, 159, 51, 13, 117, 253, 190, 29, 60, 222, 222, 83, 144, 171, 50, 141, 129, 21, 229, 36, 95, 63, 177, 130, 171, 140, 58, 162, 163, 53, 17, 219, 2, 160, 50, 4, 148, 79, 30, 153, 31, 25, 117, 223, 121, 177, 116, 20, 197, 158, 214, 24, 130, 114, 100, 70, 11, 11, 34, 84, 219, 170, 167, 94, 66, 63, 203, 99, 249, 61, 8, 111, 36, 114, 87, 118, 208, 182, 204, 13, 176, 167, 191, 56, 126, 41, 61, 35, 58, 55, 112, 87, 156, 98, 45, 219, 179, 49, 150, 65, 208, 155, 107, 241, 254, 193, 133, 142, 2, 216, 7, 219, 4, 201, 142, 194, 97, 167, 143, 13, 145, 82, 95, 35, 166, 70, 225, 135, 161, 201, 5, 234, 146, 221, 18, 176, 72, 199, 129, 53, 22, 199, 29, 63, 148, 109, 91, 7, 69, 105, 7, 203, 18, 71, 234, 129, 136, 186, 236, 186, 74, 129, 91, 190, 28, 246, 218, 145, 73, 38, 52, 254, 95, 139, 195, 231, 17, 212, 111, 126, 15, 27, 122, 132, 255, 228, 66, 179, 164, 21, 184, 90, 16, 137, 237, 173, 16, 254, 85, 143, 156, 177, 222, 76, 118, 220, 148, 225, 185, 70, 235, 120, 26, 124, 165, 40, 235, 14, 176, 98, 40, 159, 98, 145, 164, 166, 179, 21, 228, 51, 181, 90, 107, 132, 114, 214, 131, 241, 124, 96, 200, 81, 174, 54, 118, 30, 207, 20, 116, 108, 133, 29, 208, 133, 9, 75, 83, 141, 84, 82, 21, 44, 101, 149, 119, 240, 239, 131, 159, 250, 81, 214, 92, 17, 178, 1, 238, 40, 3, 24, 24, 70, 245, 37, 210, 96, 142, 70, 208, 139, 12, 97, 60, 229, 155, 45, 118, 6, 53, 207, 193, 97, 28, 225, 98, 53, 191, 42, 155, 212, 213, 39, 165, 228, 51, 13, 150, 25, 98, 127, 84, 162, 227, 92, 143, 15, 175, 227, 156, 213, 160, 24, 246, 178, 57, 2, 164, 255, 94, 189, 74, 224, 228, 70, 36, 0, 213, 195, 172, 96, 87, 251, 121, 251, 205, 129, 65, 231, 11, 74, 215, 19, 207, 187, 215, 237, 100, 251, 233, 25, 141, 21, 141, 213, 223, 82, 164, 50, 240, 209, 187, 25, 181, 181, 104, 249, 126, 71, 130, 196, 26, 37, 15, 161, 16, 120, 179, 60, 180, 229, 2, 163, 2, 71, 202, 213, 246, 134, 238, 226, 84, 241, 97, 39, 224, 59, 208, 86, 81, 139, 59, 175, 245, 97, 45, 97, 128, 73, 35, 205, 137, 32, 111, 101, 194, 162, 26, 248, 118, 25, 74, 194, 116, 168, 55, 1, 166, 194, 110, 203, 230, 226, 236, 220, 156, 253, 210, 79, 129, 122, 36, 146, 182, 195, 22, 31, 114, 191, 28, 192, 119, 126, 51, 189, 83, 38, 89, 12, 203, 108, 185, 28, 154, 238, 19, 147, 189, 195, 125, 34, 42, 157, 87, 249, 176, 172, 13, 102, 57, 160, 142, 66, 108, 52, 132, 79, 251, 155, 141, 153, 228, 213, 207, 223, 144, 154, 163, 43, 203, 13, 237, 21, 231, 135, 154, 222, 61, 88, 213, 201, 78, 235, 30, 187, 218, 173, 18, 191, 31, 141, 27, 150, 182, 139, 88, 241, 153, 240, 0, 227, 26, 111, 34, 95, 100, 12, 119, 183, 28, 35, 52, 250, 8, 174, 137, 18, 105, 82, 75, 197, 167, 164, 192, 248, 100, 0, 122, 124, 48, 4, 114, 136, 224, 70, 225, 161, 48, 210, 70, 222, 93, 152, 196, 85, 242, 146, 80, 19, 250, 91, 188, 146, 139, 118, 7, 5, 8, 35, 25, 221, 177, 105, 176, 107, 238, 198, 113, 134, 238, 213, 48, 222, 171, 206, 11, 181, 8, 220, 116, 110, 101, 11, 104, 63, 82, 201, 187, 144, 215, 203, 15, 221, 163, 150, 97, 70, 11, 222, 172, 90, 245, 132, 90, 241, 251, 189, 84, 114, 178, 70, 162, 42, 183, 193, 15, 169, 86, 250, 47, 128, 104, 66, 203, 180, 165, 189, 206, 15, 246, 161, 176, 244, 149, 124, 84, 22, 12, 78, 200, 211, 250, 40, 23, 147, 86, 98, 58, 128, 111, 202, 81, 179, 45, 216, 227, 230, 17, 178, 72, 127, 227, 110, 83, 147, 97, 245, 109, 55, 236, 80, 133, 22, 239, 110, 25, 205, 131, 228, 119, 191, 155, 202, 22, 102, 140, 154, 77, 128, 12, 241, 90, 130, 237, 223, 193, 39, 94, 33, 250, 238, 76, 63, 106, 168, 253, 17, 24, 241, 146, 133, 210, 181, 220, 187, 51, 240, 94, 84, 117, 93, 113, 55, 138, 54, 236, 75, 86, 52, 250, 166, 58, 84, 231, 210, 154, 193, 127, 180, 184, 102, 83, 133, 91, 217, 18, 246, 167, 62, 54, 150, 233, 241, 222, 236, 10, 244, 29, 250, 3, 80, 41, 16, 131, 54, 196, 182, 237, 210, 221, 159, 188, 71, 156, 20, 56, 62, 68, 131, 14, 162, 65, 240, 180, 10, 183, 68, 45, 202, 32, 21, 170, 25, 101, 1, 26, 7, 40, 164, 98, 254, 234, 168, 2, 160, 29, 206, 82, 251, 42, 99, 187, 129, 17, 181, 84, 67, 11, 51, 24, 175, 131, 13, 252, 107, 155, 16, 15, 40, 164, 115, 212, 108, 40, 48, 98, 50, 210, 3, 60, 169, 246, 1, 127, 66, 19, 231, 212, 232, 218, 97, 198, 174, 138, 55, 32, 220, 109, 24, 122, 95, 193, 34, 62, 84, 166, 22, 104, 219, 252, 173, 123, 90, 142, 180, 103, 1, 168, 69, 169, 13, 179, 123, 66, 65, 115, 226, 229, 172, 125, 18, 144, 143, 12, 41, 34, 22, 175, 245, 169, 148, 198, 15, 25, 137, 203, 152, 96, 207, 130, 181, 52, 215, 210, 201, 150, 131, 238, 34, 109, 137, 14, 162, 181, 65, 33, 75, 99, 152, 108, 126, 12, 145, 184, 156, 180, 58, 83, 28, 97, 69, 17, 88, 244, 20, 180, 243, 179, 113, 35, 21, 1, 201, 201, 172, 0, 21, 110, 30, 229, 18, 32, 245, 105, 83, 166, 159, 174, 168, 164, 201, 225, 108, 176, 1, 245, 13, 236, 255, 224, 148, 24, 98, 214, 181, 30, 109, 65, 100, 71, 81, 94, 111, 169, 144, 125, 119, 22, 114, 75, 191, 250, 36, 6, 237, 147, 141, 32, 186, 49, 179, 49, 179, 90, 235, 183, 6, 179, 85, 5, 221, 31, 168, 56, 61, 205, 170, 253, 105, 207, 220, 229, 13, 239, 188, 148, 178, 159, 196, 108, 20, 106, 40, 50, 210, 134, 160, 242, 155, 220, 142, 119, 40, 131, 61, 192, 230, 75, 30, 55, 64, 122, 157, 61, 237, 56, 211, 147, 188, 177, 201, 72, 237, 178, 78, 241, 44, 253, 218, 253, 3, 197, 171, 111, 211, 243, 116, 69, 32, 245, 5, 69, 187, 245, 121, 189, 111, 163, 77, 231, 174, 69, 136, 241, 186, 161, 201, 96, 165, 14, 186, 127, 167, 133, 217, 236, 220, 19, 254, 186, 107, 16, 79, 135, 191, 30, 52, 11, 116, 174, 98, 20, 74, 151, 24, 182, 174, 155, 12, 175, 238, 128, 23, 102, 104, 247, 220, 13, 126, 129, 8, 144, 2, 103, 198, 1, 29, 28, 225, 238, 47, 39, 84, 13, 253, 66, 109, 88, 92, 5, 117, 226, 56, 164, 112, 92, 78, 204, 40, 156, 62, 189, 94, 129, 30, 73, 127, 89, 122, 167, 87, 42, 133, 233, 44, 162, 242, 208, 229, 122, 128, 7, 196, 109, 208, 120, 54, 37, 6, 89, 228, 48, 245, 180, 255, 119, 230, 55, 28, 169, 175, 42, 193, 221, 173, 241, 27, 245, 216, 102, 120, 136, 161, 189, 165, 36, 15, 47, 74, 182, 20, 74, 186, 184, 133, 151, 120, 211, 115, 18, 114, 244, 156, 86, 246, 189, 137, 186, 235, 228, 19, 189, 31, 214, 220, 126, 210, 241, 10, 233, 152, 53, 157, 215, 246, 192, 206, 177, 138, 19, 130, 46, 255, 128, 167, 243, 10, 197, 233, 135, 113, 49, 120, 180, 186, 170, 79, 129, 188, 224, 121, 24, 15, 125, 145, 58, 195, 139, 234, 206, 248, 221, 118, 183, 252, 113, 118, 40, 108, 165, 99, 1, 114, 147, 60, 104, 17, 230, 19, 77, 169, 53, 230, 142, 93, 166, 187, 131, 63, 124, 194, 242, 213, 74, 46, 109, 33, 61, 34, 67, 238, 135, 74, 231, 145, 51, 149, 162, 19, 32, 85, 149, 112, 166, 119, 39, 185, 61, 252, 149, 56, 140, 201, 171, 181, 173, 216, 17, 50, 251, 179, 205, 122, 85, 192, 7, 170, 230, 47, 60, 123, 69, 127, 135, 68, 120, 119, 60, 107, 149, 39, 145, 43, 76, 124, 108, 67, 249, 66, 100, 203, 209, 66, 138, 247, 70, 235, 145, 42, 152, 221, 175, 64, 130, 127, 52, 62, 114, 120, 123, 13, 12, 155, 251, 238, 191, 40, 87, 235, 251, 103, 209, 66, 248, 173, 40, 181, 145, 84, 238, 74, 238, 141, 138, 2, 139, 156, 192, 198, 103, 53, 195, 19, 211, 91, 170, 2, 228, 200, 115, 58, 71, 141, 247, 172, 116, 93, 43, 112, 232, 63, 20, 101, 107, 96, 208, 211, 215, 160, 179, 218, 111, 93, 154, 158, 250, 68, 137, 143, 212, 32, 211, 12, 190, 77, 243, 213, 204, 39, 130, 10, 183, 215, 148, 235, 172, 58, 156, 13, 198, 37, 88, 113, 159, 141, 36, 116, 202, 6, 71, 127, 52, 133, 69, 208, 175, 254, 142, 173, 63, 104, 63, 37, 24, 152, 16, 167, 50, 60, 0, 104, 164, 99, 71, 199, 126, 212, 158, 148, 195, 29, 174, 130, 103, 103, 128, 208, 59, 34, 165, 229, 7, 216, 17, 17, 197, 247, 119, 146, 66, 32, 50, 126, 106, 212, 79, 15, 82, 237, 207, 217, 204, 53, 180, 73, 174, 147, 125, 209, 37, 206, 36, 91, 31, 48, 69, 63, 41, 246, 243, 195, 182, 88, 250, 194, 93, 90, 60, 217, 108, 222, 155, 62, 13, 41, 182, 238, 84, 197, 71, 5, 161, 160, 35, 240, 11, 47, 208, 139, 163, 31, 235, 149, 206, 61, 75, 20, 63, 240, 153, 253, 74, 208, 10, 139, 228, 134, 65, 150, 96, 167, 189, 154, 158, 164, 203, 44, 58, 176, 178, 198, 213, 14, 229, 235, 12, 201, 36, 42, 180, 47, 149, 202, 168, 231, 28, 170, 208, 50, 250, 6, 25, 145, 146, 75, 157, 101, 0, 24, 207, 214, 15, 104, 181, 244, 123, 47, 248, 56, 167, 163, 188, 126, 23, 154, 101, 247, 183, 96, 21, 103, 11, 240, 107, 197, 16, 33, 191, 166, 80, 105, 12, 130, 56, 203, 143, 184, 140, 228, 0, 223, 209, 117, 61, 87, 125, 194, 179, 145, 198, 252, 89, 102, 234, 120, 179, 67, 142, 90, 161, 243, 162, 73, 249, 130, 79, 104, 196, 154, 249, 89, 248, 45, 14, 166, 242, 90, 224, 19, 28, 180, 27, 116, 143, 28, 3, 193, 84, 176, 196, 184, 248, 1, 229, 89, 4, 163, 67, 183, 215, 175, 117, 8, 53, 235, 83, 248, 86, 246, 34, 8, 17, 81, 166, 90, 79, 138, 204, 146, 18, 231, 184, 38, 118, 94, 178, 44, 19, 222, 127, 87, 101, 29, 209, 73, 190, 184, 233, 20, 173, 62, 161, 152, 11, 185, 96, 189, 141, 9, 4, 32, 2, 126, 208, 181, 36, 200, 48, 210, 11, 112, 218, 177, 79, 124, 15, 250, 52, 155, 97, 175, 38, 163, 32, 67, 154, 20, 119, 165, 231, 177, 128, 13, 51, 49, 181, 244, 114, 171, 177, 224, 58, 213, 37, 92, 76, 137, 126, 18, 240, 219, 176, 218, 195, 218, 70, 172, 24, 143, 15, 75, 36, 140, 76, 66, 164, 172, 122, 76, 250, 234, 4, 91, 141, 72, 155, 51, 39, 19, 109, 129, 5, 47, 74, 150, 85, 186, 174, 170, 174, 139, 8, 185, 110, 223, 40, 255, 163, 53, 235, 103, 178, 244, 233, 205, 54, 74, 21, 61, 71, 183, 175, 216, 121, 69, 64, 94, 175, 132, 14, 174, 19, 226, 143, 136, 182, 186, 122, 148, 152, 59, 105, 34, 82, 130, 138, 32, 84, 0, 166, 237, 51, 201, 113, 108, 149, 242, 168, 248, 175, 153, 68, 250, 83, 86, 145, 140, 150, 205, 112, 195, 232, 193, 92, 4, 47, 93, 224, 104, 205, 223, 25, 7, 13, 159, 174, 233, 72, 46, 162, 158, 140, 198, 35, 234, 27, 114, 232, 67, 62, 148, 57, 196, 71, 107, 72, 53, 224, 195, 229, 13, 6, 83, 150, 223, 126, 61, 195, 8, 85, 160, 195, 116, 155, 139, 240, 254, 93, 124, 255, 46, 47, 98, 26, 74, 246, 40, 95, 229, 216, 34, 45, 182, 207, 201, 82, 47, 232, 210, 147, 108, 128, 1, 82, 202, 71, 160, 197, 110, 87, 134, 83, 187, 109, 224, 89, 158, 199, 55, 165, 158, 153, 225, 236, 155, 101, 66, 119, 172, 9, 104, 255, 52, 131, 159, 213, 59, 185, 51, 162, 142, 138, 130, 203, 84, 141, 33, 233, 200, 39, 102, 6, 151, 16, 124, 75, 211, 5, 170, 224, 171, 122, 54, 144, 62, 240, 253, 209, 73, 28, 205, 174, 246, 19, 247, 192, 160, 118, 94, 130, 45, 240, 199, 180, 127, 252, 204, 248, 104, 204, 195, 24, 170, 45, 76, 116, 71, 250, 52, 93, 177, 202, 2, 82, 194, 127, 175, 42, 73, 191, 133, 91, 120, 214, 219, 78, 205, 189, 241, 246, 136, 187, 213, 154, 206, 37, 96, 108, 124, 44, 30, 160, 225, 140, 214, 238, 11, 225, 191, 14, 112, 170, 164, 37, 193, 1, 209, 238, 27, 37, 186, 69, 34, 26, 34, 66, 220, 2, 178, 247, 244, 41, 100, 242, 134, 181, 19, 125, 131, 253, 92, 88, 145, 52, 94, 168, 130, 237, 117, 177, 222, 215, 169, 97, 221, 31, 240, 139, 86, 72, 157, 254, 146, 194, 60, 140, 15, 72, 192, 174, 215, 177, 242, 39, 211, 244, 174, 183, 67, 46, 58, 87, 147, 135, 226, 122, 120, 148, 73, 182, 32, 223, 171, 230, 52, 141, 19, 171, 81, 87, 148, 142, 163, 3, 117, 66, 94, 152, 149, 213, 144, 178, 200, 137, 26, 213, 133, 17, 80, 7, 221, 194, 196, 0, 188, 166, 97, 187, 109, 71, 31, 55, 157, 69, 221, 28, 64, 251, 138, 238, 53, 137, 239, 145, 194, 236, 73, 196, 252, 129, 43, 115, 110, 154, 177, 173, 195, 225, 241, 136, 197, 139, 113, 16, 205, 239, 7, 78, 128, 228, 84, 46, 48, 246, 156, 20, 220, 202, 30, 218, 224, 126, 51, 116, 157, 153, 167, 18, 127, 148, 105, 21, 78, 26, 149, 250, 118, 22, 51, 126, 180, 20, 254, 208, 195, 79, 205, 68, 89, 164, 242, 95, 23, 41, 156, 81, 109, 49, 180, 64, 101, 12, 216, 98, 159, 104, 156, 158, 31, 127, 183, 197, 26, 19, 221, 113, 174, 192, 193, 87, 180, 236, 65, 108, 215, 116, 116, 190, 85, 194, 148, 179, 214, 71, 71, 238, 122, 159, 12, 239, 116, 252, 152, 106, 255, 212, 168, 219, 243, 135, 235, 166, 125, 107, 113, 117, 142, 13, 215, 7, 157, 202, 79, 139, 125, 75, 65, 86, 237, 31, 180, 247, 198, 212, 193, 219, 181, 238, 127, 111, 72, 3, 7, 22, 58, 181, 140, 193, 97, 162, 4, 39, 24, 17, 223, 108, 17, 115, 185, 246, 199, 152, 109, 172, 31, 14, 68, 213, 90, 42, 14, 170, 255, 192, 159, 159, 47, 219, 73, 142, 213, 244, 241, 63, 101, 145, 200, 90, 65, 14, 39, 49, 109, 18, 69, 46, 16, 246, 0, 139, 154, 180, 234, 209, 68, 58, 14, 116, 136, 48, 41, 191, 130, 103, 37, 40, 143, 142, 120, 229, 173, 68, 148, 69, 29, 58, 209, 36, 246, 64, 193, 0, 121, 149, 168, 1, 145, 67, 77, 231, 9, 174, 6, 133, 94, 159, 143, 237, 193, 149, 159, 12, 5, 231, 36, 75, 1, 148, 158, 192, 163, 250, 18, 47, 166, 31, 40, 154, 13, 112, 100, 47, 164, 188, 103, 138, 226, 44, 95, 82, 40, 113, 246, 227, 144, 77], key = [86, 229, 250, 218, 206, 96, 248, 228, 128, 56, 165, 3, 74, 153, 235, 4, 2, 74, 3, 64, 254, 100, 93, 168, 62, 216, 29, 223, 94, 230, 203, 245], nonce = 6541609532124639236 +cc 7549b872e517bdf3640efe6042864d2ac85f11dd1b357b9cf438c7fc7331e089 # shrinks to payload = [], key = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], nonce = 0 +cc 12fe075f6dde88a12997f3e73c6897e5280c2d786b686b73c7af04a0dd2424cd # shrinks to payload = [22, 42, 213, 255, 51, 63, 229, 87, 199, 164, 148, 97, 221, 159, 129, 126, 100, 121, 207, 212, 190, 3, 52, 180, 118, 43, 165, 27, 111, 62, 161, 41, 227, 171, 180, 61, 232, 70, 255, 66, 100, 231, 163, 185, 33, 71, 33, 161, 96, 201, 119, 105, 161, 25, 23, 105, 44, 95, 173, 124, 132, 34, 116, 10, 124, 85, 152, 124, 50, 97, 121, 113, 18, 228, 161, 78, 80, 106, 144, 15, 77, 207, 255, 43, 250, 162, 130, 156, 221, 209, 83, 70, 80, 119, 100, 11, 105, 229, 207, 203, 59, 105, 38, 89, 60, 105, 51, 96, 237, 164, 184, 40, 97, 243, 19, 61, 164, 90, 163, 154, 179, 119, 149, 215, 69, 11, 169, 97, 210, 209, 248, 197, 53, 184, 97, 175, 180, 248, 216, 48, 191, 185, 30, 58, 186, 137, 143, 213, 187, 186, 124, 139, 67, 39, 178, 149, 236, 143, 162, 25, 181, 97, 91, 188, 205, 42, 46, 195, 207, 155, 145, 117, 195, 80, 58, 239, 10, 154, 252, 26, 170, 85, 122, 16, 22, 79, 88, 199, 245, 90, 74, 90, 57, 3, 248, 231, 68, 122, 195, 45, 251, 216, 120, 149, 245, 57, 193, 163, 252, 167, 111, 50, 232, 32, 190, 46, 62, 32, 120, 75, 13, 240, 220, 4, 246, 107, 199, 217, 20, 109, 232, 92, 133, 127, 206, 124, 164, 211, 158, 228, 67, 227, 131, 54, 110, 139, 44, 94, 122, 98, 78, 110, 144, 183, 1, 70, 232, 50, 212, 174, 238, 81, 52, 174, 185, 246, 114, 136, 183, 99, 147, 187, 145, 211, 194, 31, 67, 161, 221, 72, 17, 210, 115, 180, 159, 126, 40, 234, 171, 123, 94, 79, 25, 253, 121, 157, 178, 243, 181, 173, 228, 105, 12, 94, 198, 162, 186, 141, 198, 69, 11, 227, 19, 243, 196, 229, 73, 203, 30, 102, 37, 205, 154, 255, 34, 84, 165, 185, 165, 99, 175, 216, 8, 76, 253, 122, 246, 67, 113, 223, 82, 78, 17, 111, 84, 197, 169, 160, 254, 209, 128, 166, 215, 148, 120, 129, 54, 129, 225, 76, 8, 89, 132, 197, 98, 100, 166, 49, 31, 202, 25, 184, 18, 189, 125, 202, 239, 214, 124, 209, 96, 215, 184, 127, 132, 50, 33, 96, 210, 117, 114, 150, 16, 65, 222, 140, 235, 114, 68, 4, 95, 108, 66, 171, 110, 217, 131, 210, 44, 156, 75, 72, 128, 245, 198, 216, 161, 190, 154, 148, 41, 119, 62, 107, 5, 105, 215, 34, 179, 204, 168, 129, 236, 155, 203, 159, 28, 59, 215, 120, 4, 23, 113, 113, 123, 136, 132, 203, 188, 48, 98, 163, 70, 110, 197, 29, 217, 202, 106, 131, 103, 113, 104, 18, 176, 124, 187, 69, 115, 135, 213, 237, 79, 120, 30, 230, 51, 236, 162, 250, 212, 231, 211, 46, 74, 9, 160, 13, 67, 5, 102, 199, 40, 233, 173, 35, 101, 165, 40, 93, 217, 206, 63, 193, 133, 235, 237, 8, 214, 130, 177, 241, 134, 224, 86, 245, 60, 172, 81, 213, 143, 145, 152, 96, 184, 234, 54, 17, 19, 202, 75, 52, 233, 27, 206, 16, 193, 88, 245, 188, 101, 135, 255, 164, 18, 138, 2, 238, 86, 30, 175, 164, 99, 15, 247, 231, 43, 116, 158, 151, 229, 211, 126, 132, 193, 159, 98, 154, 58, 53, 71, 14, 125, 39, 96, 32, 242, 65, 31, 156, 242, 137, 59, 47, 47, 129, 36, 226, 136, 81, 63, 110, 225, 147, 153, 213, 123, 43, 177, 116, 246, 88, 176, 124, 150, 5, 143, 198, 104, 13, 255, 94, 33, 179, 15, 217, 37, 127, 242, 212, 114, 117, 103, 234, 88, 246, 136, 121, 134, 73, 153, 55, 91, 48, 168, 91, 252, 186, 17, 204, 37, 248, 32, 184, 178, 77, 232, 203, 109, 24, 204, 60, 81, 193, 15, 0, 122, 238, 120, 12, 242, 157, 28, 252, 232, 43, 172, 75, 193, 254, 203, 43, 111, 138, 231, 255, 68, 227, 56, 177, 100, 133, 149, 140, 233, 53, 23, 16, 166, 255, 202, 80, 120, 213, 41, 98, 89, 240, 26, 143, 123, 240, 174, 69, 178, 28, 100, 78, 188, 12, 216, 74, 81, 189, 91, 93, 32, 53, 133, 136, 67, 221, 17, 53, 213, 1, 227, 45, 107, 85, 30, 70, 130, 41, 9, 243, 230, 165, 173, 120, 89, 169, 247, 163, 198, 240, 71, 90, 129, 181, 155, 42, 26, 174, 59, 41, 181, 103, 64, 27, 25, 182, 36, 231, 69, 53, 75, 146, 11, 33, 138, 203, 67, 249, 64, 69, 101, 152, 136, 17, 147, 124, 145, 54, 58, 76, 15, 130, 117, 54, 80, 95, 215, 253, 91, 245, 216, 235, 94, 184, 143, 205, 189, 243, 214, 130, 90, 153, 167, 147, 76, 195, 169, 46, 101, 213, 189, 246, 110, 186, 123, 189, 65, 103, 249, 175, 250, 39, 167, 21, 140, 144, 177, 2, 33, 124, 229, 191, 69, 181, 245, 97, 99, 178, 169, 121, 3, 227, 134, 3, 24, 251, 109, 74, 21, 224, 124, 131, 22, 152, 98, 116, 111, 48, 230, 215, 81, 144, 124, 128, 103, 154, 237, 212, 92, 197, 19, 29, 1, 7, 95, 204, 248, 83, 30, 242, 226, 65, 220, 36, 100, 87, 241, 171, 146, 138, 255, 24, 135, 213, 233, 47, 225, 14, 35, 181, 222, 198, 110, 142, 118, 99, 77, 228, 11, 144, 164, 16, 36, 129, 72, 34, 162, 146, 90, 215, 126, 85, 194, 166, 150, 207, 182, 225, 1, 72, 12, 222, 120, 18, 78, 124, 115, 184, 228, 190, 32, 155, 51, 162, 183, 238, 157, 98, 14, 186, 124, 49, 239, 95, 86, 253, 245, 139, 67, 180, 153, 168, 237, 39, 131, 116, 158, 49, 40, 69, 2, 60, 222, 104, 47, 151, 102, 13, 191, 248, 71, 167, 239, 200, 132, 77, 82, 137, 180, 28, 218, 171, 251, 178, 54, 24, 249, 246, 137, 119, 206, 229, 63, 83, 179, 26, 3, 205, 53, 238, 247, 24, 162, 37, 153, 37, 136, 145, 91, 93, 164, 62, 233, 178, 55, 185, 51, 9, 118, 124, 157, 213, 4, 130, 118, 156, 110, 121, 209, 175, 207, 87, 176, 16, 55, 46, 240, 239, 30, 40, 115, 108, 107, 37, 248, 252, 224, 194, 103, 133, 61, 204, 213, 222, 178, 153, 106, 178, 30, 175, 194, 172, 180, 100, 83, 158, 46, 194, 133, 29, 128, 48, 188, 73, 25, 196, 189, 63, 35, 154, 221, 225, 65, 144, 22, 69, 11, 254, 128, 191, 105, 139, 22, 174, 180, 114, 188, 97, 22, 251, 139, 110, 171, 73, 47, 0, 240, 159, 152, 73, 204, 14, 148, 245, 213, 120, 182, 59, 90, 154, 72, 56, 36, 0, 60, 65, 159, 139, 139, 146, 217, 232, 4, 79, 6, 29, 231, 254, 45, 195, 115, 254, 214, 249, 212, 171, 146, 187, 18, 27, 95, 25, 39, 229, 97, 70, 233, 76, 117, 179, 13, 177, 82, 82, 41, 90, 89, 61, 7, 211, 143, 244, 178, 239, 188, 11, 140, 161, 111, 116, 26, 206, 209, 127, 139, 237, 39, 234, 214, 45, 65, 38, 9, 72, 93, 207, 167, 253, 182, 141, 244, 206, 75, 45, 33, 71, 111, 93, 228, 4, 178, 146, 193, 215, 107, 138, 0, 115, 176, 150, 89, 194, 202, 239, 174, 84, 125, 52, 31, 14, 35, 150, 251, 67, 145, 60, 92, 140, 36, 141, 195, 140, 180, 64, 166, 73, 218, 254, 39, 224, 61, 223, 211, 58, 186, 197, 108, 71, 184, 13, 179, 29, 48, 57, 98, 174, 88, 29, 137, 104, 155, 90, 113, 188, 89, 26, 201, 161, 129, 225, 107, 111, 52, 121, 65, 43, 134, 2, 21, 60, 133, 18, 47, 81, 101, 122, 36, 209, 221, 172, 247, 85, 182, 253, 201, 167, 121, 124, 251, 58, 100, 86, 86, 247, 88, 70, 92, 108, 172, 73, 159, 21, 12, 22, 184, 5, 154, 81, 203, 135, 100, 130, 81, 139, 136, 167, 80, 127, 125, 146, 129, 252, 53, 129, 249, 82, 16, 210, 48, 212, 139, 97, 202, 144, 169, 161, 120, 97, 5, 252, 77, 254, 101, 250, 134, 253, 0, 107, 221, 68, 222, 141, 159, 68, 150, 132, 210, 120, 172, 9, 186, 227, 132, 189, 131, 248, 149, 171, 90, 15, 168, 146, 103, 71, 1, 20, 101, 230, 148, 165, 191, 36, 11, 41, 141, 132, 66, 238, 69, 79, 157, 119, 207, 238, 142, 154, 102, 224, 213, 2, 52, 143, 53, 181, 22, 72, 43, 221, 162, 118, 157, 194, 249, 64, 239, 88, 212, 181, 194, 176, 40, 108, 40, 249, 209, 8, 96, 0, 149, 30, 233, 42, 246, 162, 185, 238, 3, 135, 252, 10, 84, 123, 47, 30, 169, 236, 45, 46, 98, 224, 1, 253, 222, 197, 28, 13, 9, 94, 40, 120, 201, 50, 241, 46, 3, 44, 174, 81, 105, 213, 252, 39, 236, 46, 238, 246, 202, 125, 114, 233, 55, 88, 253, 253, 68, 209, 188, 252, 141, 166, 83, 240, 223, 237, 115, 211, 106, 59, 106, 197, 216, 62, 162, 140, 62, 202, 55, 193, 213, 250, 70, 158, 223, 73, 94, 234, 127, 140, 35, 15, 127, 30, 248, 248, 247, 124, 199, 72, 232, 182, 190, 34, 201, 249, 87, 102, 28, 170, 57, 55, 23, 68, 141, 44, 97, 243, 69, 27, 235, 216, 149, 109, 78, 198, 195, 12, 29, 106, 127, 5, 55, 41, 13, 45, 210, 38, 209, 155, 237, 58, 38, 165, 16, 195, 209, 120, 218, 139, 94, 238, 136, 122, 84, 202, 145, 38, 117, 249, 140, 40, 199, 128, 43, 185, 123, 169, 32, 30, 155, 71, 141, 14, 50, 0, 119, 197, 46, 181, 126, 75, 38, 181, 46, 121, 248, 245, 136, 156, 45, 117, 29, 91, 136, 74, 148, 126, 36, 5, 215, 4, 243, 194, 28, 124, 138, 102, 84, 78, 92, 234, 246, 171, 237, 156, 148, 150, 42, 134, 51, 152, 155, 181, 48, 30, 78, 137, 179, 78, 195, 218, 157, 3, 132, 8, 205, 25, 203, 43, 104, 238, 87, 237, 63, 27, 89, 92, 19, 92, 225, 111, 70, 57, 72, 83, 44, 32, 69, 12, 7, 216, 26, 130, 21, 140, 116, 205, 133, 60, 237, 149, 201, 17, 189, 254, 254, 39, 3, 114, 232, 25, 151, 124, 136, 170, 185, 150, 210, 20, 60, 3, 223, 154, 167, 209, 43, 217, 196, 226, 164, 43, 179, 172, 1, 164, 118, 145, 145, 71, 135, 120, 77, 74, 56, 235, 163, 84, 241, 167, 123, 100, 97, 110, 195, 54, 107, 177, 146, 95, 74, 224, 199, 192, 131, 123, 41, 10, 93, 205, 64, 173, 31, 127, 9, 152, 57, 12, 41, 29, 134, 151, 203, 151, 212, 187, 66, 164, 115, 237, 250, 207, 31, 239, 75, 246, 18, 8, 128, 92, 134, 147, 10, 0, 40, 22, 67, 105, 75, 199, 15, 91, 245, 60, 247, 244, 80, 139, 136, 140, 209, 90, 99, 181, 121, 100, 75, 116, 97, 130, 31, 251, 17, 36, 125, 83, 170, 146, 136, 101, 169, 90, 43, 118, 106, 13, 138, 171, 8, 53, 230, 46, 171, 136, 30, 27, 213, 178, 80, 116, 77, 109, 253, 178, 133, 251, 25, 215, 222, 8, 106, 15, 93, 73, 238, 178, 45, 172, 161, 186, 170, 10, 1, 190, 30, 190, 134, 135, 46, 85, 122, 199, 10, 172, 80, 131, 133, 186, 28, 110, 100, 75, 37, 86, 140, 114, 216, 132, 175, 151, 65, 88, 243, 38, 204, 254, 197, 212, 215, 201, 34, 163, 196, 208, 68, 204, 244, 150, 215, 42, 172, 198, 50, 59, 247, 241, 85, 216, 84, 18, 96, 167, 239, 162, 178, 100, 199, 155, 118, 48, 142, 228, 214, 183, 222, 33, 86, 77, 59, 99, 72, 73, 3, 50, 55, 169, 62, 142, 171, 71, 22, 178, 140, 83, 238, 159, 113, 176, 138, 209, 81, 13, 87, 229, 248, 170, 37, 109, 139, 138, 105, 39, 28, 7, 125, 87, 245, 7, 49, 53, 92, 1, 5, 42, 204, 224, 20, 124, 158, 204, 62, 113, 189, 81, 48, 92, 238, 254, 180, 231, 166, 251, 158, 82, 214, 185, 13, 211, 168, 239, 203, 106, 227, 238, 254, 157, 147, 123, 3, 229, 147, 84, 211, 152, 177, 249, 68, 88, 2, 227, 115, 18, 51, 35, 170, 47, 116, 181, 52, 221, 194, 178, 115, 49, 1, 96, 191, 179, 244, 57, 148, 148, 183, 150, 212, 172, 139, 247, 129, 79, 217, 232, 198, 149, 165, 138, 214, 110, 184, 93, 189, 192, 18, 102, 27, 73, 8, 23, 210, 248, 53, 80, 36, 26, 11, 233, 212, 117, 7, 64, 102, 208, 162, 89, 50, 53, 91, 174, 15, 103, 200, 238, 30, 245, 5, 245, 76, 195, 78, 233, 45, 58, 198, 215, 126, 146, 184, 168, 200, 172, 19, 168, 69, 28, 141, 234, 157, 161, 61, 197, 86, 127, 177, 165, 134, 41, 176, 249, 21, 63, 187, 147, 152, 139, 63, 77, 213, 200, 84, 172, 154, 27, 136, 213, 127, 243, 60, 216, 118, 14, 25, 106, 212, 70, 234, 37, 34, 219, 236, 159, 246, 171, 208, 214, 147, 109, 41, 30, 2, 119, 101, 52, 53, 94, 237, 83, 89, 246, 95, 102, 10, 77, 113, 235, 61, 177, 112, 233, 68, 184, 61, 20, 57, 134, 140, 222, 58, 234, 198, 55, 168, 27, 14, 57, 206, 177, 157, 244, 106, 88, 110, 86, 58, 178, 123, 111, 239, 171, 196, 88, 49, 128, 52, 72, 102, 95, 132, 157, 195, 69, 172, 162, 220, 161, 60, 155, 8, 246, 114, 200, 205, 8, 246, 30, 255, 50, 229, 32, 252, 225, 80, 210, 103, 79, 153, 27, 70, 243, 106, 82, 194, 239, 53, 140, 248, 24, 76, 137, 252, 132, 53, 159, 122, 115, 63, 25, 196, 121, 148, 124, 95, 46, 172, 158, 65, 124, 119, 187, 60, 192, 37, 53, 110, 214, 214, 185, 123, 233, 97, 144, 58, 246, 194, 40, 77, 139, 198, 160, 129, 196, 111, 225, 122, 219, 104, 58, 101, 54, 255, 53, 99, 96, 202, 236, 8, 224, 48, 77, 52, 143, 231, 222, 141, 80, 45, 227, 83, 208, 151, 1, 101, 231, 246, 208, 85, 9, 212, 71, 20, 174, 124, 238, 170, 223, 226, 57, 161, 131, 220, 134, 90, 51, 57, 254, 203, 181, 163, 127, 69, 181, 127, 250, 141, 125, 70, 82, 156, 147, 153, 158, 17, 150, 169, 212, 125, 20, 134, 68, 65, 140, 213, 230, 252, 70, 28, 44, 159, 172, 158, 167, 63, 159, 66, 197, 167, 89, 192, 111, 14, 108, 147, 96, 184, 94, 242, 225, 222, 247, 45, 88, 253, 35, 214, 58, 221, 64, 25, 213, 164, 190, 110, 181, 53, 80, 184, 29, 225, 200, 60, 179, 169, 92, 203, 152, 218, 0, 71, 45, 26, 67, 136, 154, 112, 231, 249, 12, 69, 174, 252, 166, 37, 98, 170, 106, 198, 219, 164, 163, 5, 103, 108, 32, 216, 13, 53, 101, 103, 16, 107, 62, 162, 32, 104, 248, 81, 168, 250, 129, 105, 113, 57, 205, 230, 183, 113, 219, 226, 210, 74, 212, 31, 177, 34, 235, 210, 232, 43, 206, 240, 28, 148, 67, 105, 52, 112, 16, 100, 131, 3, 88, 209, 29, 28, 181, 2, 254, 11, 193, 28, 0, 164, 254, 32, 198, 236, 104, 242, 63, 35, 217, 77, 51, 231, 38, 141, 249, 209, 77, 81, 98, 109, 232, 247, 23, 91, 198, 43, 213, 124, 7, 228, 115, 245, 220, 152, 99, 160, 81, 174, 14, 111, 62, 172, 37, 10, 18, 147, 58, 137, 227, 160, 22, 212, 235, 127, 12, 250, 68, 48, 166, 114, 23, 128, 10, 210, 93, 236, 135, 146, 88, 59, 221, 178, 59, 44, 129, 135, 171, 128, 211, 115, 15, 64, 234, 118, 21, 198, 5, 242, 13, 242, 173, 169, 60, 61, 254, 158, 96, 253, 189, 28, 78, 48, 156, 26, 239, 196, 176, 56, 252, 245, 114, 72, 172, 163, 11, 202, 235, 187, 59, 181, 197, 145, 74, 134, 91, 43, 118, 146, 63, 197, 122, 196, 139, 46, 60, 5, 174, 45, 63, 137, 54, 5, 240, 199, 63, 154, 111, 87, 108, 62, 78, 96, 25, 237, 91, 65, 214, 123, 9, 210, 55, 195, 108, 248, 68, 83, 68, 82, 34, 8, 184, 120, 249, 75, 192, 234, 179, 9, 158, 212, 96, 16, 18, 170, 8, 75, 150, 61, 94, 38, 45, 118, 171, 41, 66, 214, 219, 94, 26, 165, 136, 228, 244, 118, 175, 100, 4, 29, 132, 107, 37, 175, 79, 132, 23, 164, 77, 121, 53, 16, 25, 15, 234, 6, 1, 40, 190, 45, 128, 141, 78, 22, 191, 25, 138, 198, 163, 239, 72, 141, 233, 201, 36, 75, 39, 155, 97, 233, 163, 86, 176, 105, 25, 230, 61, 253, 66, 62, 6, 1, 47, 213, 186, 147, 155, 8, 223, 209, 233, 234, 128, 253, 105, 146, 117, 5, 177, 15, 30, 160, 61, 29, 166, 100, 59, 135, 124, 3, 41, 99, 21, 245, 217, 44, 96, 17, 255, 144, 98, 86, 1, 165, 146, 146, 114, 152, 119, 202, 68, 157, 66, 84, 4, 123, 221, 178, 191, 131, 95, 202, 139, 148, 238, 47, 238, 226, 50, 188, 184, 173, 105, 129, 158, 112, 31, 69, 184, 227, 149, 203, 203, 87, 215, 139, 175, 7, 238, 41, 200, 109, 83, 198, 158, 202, 153, 5, 255, 106, 88, 92, 98, 198, 82, 72, 118, 182, 188, 0, 172, 179, 14, 187, 186, 89, 234, 234, 92, 15, 186, 254, 80, 80, 60, 106, 146, 186, 13, 50, 35, 216, 198, 38, 137, 190, 152, 14, 61, 41, 21, 103, 4, 22, 27, 47, 25, 223, 150, 26, 165, 32, 105, 154, 151, 36, 36, 82, 101, 7, 38, 55, 64, 181, 130, 34, 139, 248, 86, 216, 205, 74, 3, 107, 219, 38, 164, 163, 42, 231, 55, 14, 208, 128, 13, 167, 234, 192, 38, 71, 70, 12, 8, 170, 11, 80, 168, 100, 181, 169, 161, 218, 105, 60, 228, 173, 142, 219, 2, 48, 50, 178, 35, 75, 115, 83, 226, 200, 233, 7, 239, 202, 89, 171, 99, 166, 232, 224, 29, 72, 15, 112, 87, 26, 132, 29, 149, 46, 105, 184, 202, 153, 52, 152, 132, 138, 78, 144, 209, 28, 87, 23, 98, 206, 233, 188, 167, 117, 131, 138, 163, 173, 228, 151, 25, 114, 252, 72, 15, 75, 162, 39, 2, 85, 52, 66, 31, 177, 229, 247, 24, 223, 39, 61, 106, 46, 22, 238, 219, 196, 214, 75, 138, 13, 31, 206, 45, 128, 0, 222, 131, 150, 44, 145, 229, 119, 185, 158, 47, 14, 224, 130, 246, 11, 236, 170, 236, 223, 138, 25, 57, 111, 40, 253, 153, 3, 139, 120, 80, 33, 192, 45, 247, 179, 65, 38, 24, 238, 112, 160, 120, 74, 181, 177, 189, 255, 208, 106, 131, 251, 244, 203, 35, 54, 3, 212, 168, 227, 173, 239, 117, 159, 34, 92, 55, 237, 239, 222, 66, 251, 73, 149, 56, 118, 215, 178, 6, 180, 86, 157, 110, 208, 158, 136, 91, 5, 79, 146, 124, 186, 54, 126, 72, 187, 246, 109, 113, 68, 255, 203, 216, 212, 65, 127, 74, 86, 214, 72, 75, 176, 187, 95, 121, 14, 98, 85, 41, 115, 131, 202, 21, 240, 184, 213, 114, 29, 35, 201, 205, 72, 78, 4, 222, 199, 131, 146, 214, 234, 192, 198, 43, 126, 95, 190, 211, 194, 251, 43, 223, 128, 235, 110, 162, 152, 176, 251, 38, 36, 21, 127, 16, 17, 159, 52, 101, 225, 46, 31, 235, 22, 226, 244, 200, 29, 133, 159, 86, 136, 248, 74, 98, 204, 8, 86, 18, 126, 221, 34, 17, 114, 7, 178, 110, 67, 207, 238, 8, 236, 229, 238, 33, 40, 167, 61, 97, 101, 145, 94, 65, 5, 192, 200, 200, 35, 247, 189, 87, 84, 221, 133, 63, 66, 226, 226, 45, 140, 46, 203, 87, 245, 178, 239, 190, 59, 62, 166, 4, 215, 203, 13, 5, 79, 172, 109, 201, 77, 217, 216, 182, 87, 220, 34, 226, 1, 199, 24, 178, 251, 125, 90, 176, 101, 35, 33, 142, 178, 168, 165, 24, 63, 187, 51, 44, 115, 212, 231, 241, 175, 98, 189, 166, 59, 138, 210, 121, 73, 110, 204, 170, 104, 211, 161, 52, 23, 38, 173, 238, 122, 240, 241, 36, 250, 252, 194, 209, 104, 83, 204, 85, 168, 43, 73, 120, 40, 31, 243, 84, 78, 12, 167, 110, 18, 214, 107, 49, 171, 121, 136, 138, 90, 159, 240, 51, 124, 175, 17, 240, 237, 245, 30, 5, 162, 23, 104, 152, 222, 198, 11, 242, 209, 213, 197, 249, 229, 44, 31, 159, 114, 10, 93, 218, 13, 243, 239, 161, 219, 164, 18, 227, 252, 68, 148, 51, 91, 187, 16, 79, 93, 85, 167, 127, 125, 13, 252, 196, 26, 221, 47, 201, 29, 252, 201, 125, 99, 191, 246, 44, 92, 149, 20, 192, 12, 192, 25, 240, 85, 62, 65, 138, 70, 95, 252, 147, 126, 63, 52, 63, 208, 48, 47, 240, 50, 33, 118, 46, 141, 149, 58, 232, 141, 41, 232, 229, 65, 143, 44, 37, 3, 22, 7, 244, 45, 172, 249, 114, 216, 131, 46, 155, 187, 40, 4, 215, 254, 239, 126, 133, 58, 148, 113, 192, 71, 131, 118, 189, 11, 163, 152, 206, 50, 55, 180, 234, 47, 175, 49, 94, 7, 9, 137, 103, 10, 71, 109, 113, 193, 240, 16, 249, 196, 175, 119, 253, 205, 240, 225, 152, 107, 132, 22, 43, 145, 45, 207, 145, 181, 127, 80, 170, 113, 128, 63, 87, 47, 16, 176, 69, 138, 200, 230, 244, 21, 67, 137, 16, 155, 173, 235, 169, 150, 67, 252, 182, 204, 206, 211, 45, 46, 105, 108, 245, 148, 79, 35, 50, 107, 177, 150, 188, 136, 74, 155, 234, 241, 181, 190, 125, 176, 15, 132, 122, 187, 134, 253, 88, 215, 190, 64, 137, 175, 15, 247, 200, 125, 190, 124, 176, 192, 149, 117, 10, 219, 200, 197, 13, 177, 215, 106, 81, 128, 143, 225, 55, 183, 137, 204, 160, 245, 83, 67, 108, 211, 223, 127, 202, 20, 52, 7, 140, 201, 7, 29, 215, 98, 166, 175, 20, 135, 240, 79, 191, 184, 244, 130, 134, 211, 157, 190, 173, 214, 37, 61, 58, 212, 37, 153, 10, 242, 207, 216, 112, 148, 240, 145, 215, 47, 153, 216, 120, 201, 52, 86, 87, 110, 35, 208, 215, 149, 39, 116, 227, 175, 158, 126, 119, 148, 232, 194, 31, 64, 158, 57, 189, 149, 129, 173, 229, 144, 205, 52, 181, 218, 214, 233, 109, 15, 46, 123, 156, 117, 244, 171, 125, 58, 4, 171, 194, 75, 184, 114, 152, 36, 36, 227, 6, 62, 87, 65, 7, 66, 70, 47, 83, 128, 147, 15, 188, 84, 57, 195, 45, 226, 209, 21, 87, 70, 47, 114, 94, 140, 198, 95, 36, 204, 228, 251, 26, 35, 127, 158, 90, 195, 223, 239, 225, 142, 30, 234, 224, 79, 137, 190, 190, 1, 5, 232, 173, 31, 34, 40, 0, 71, 54, 122, 66, 211, 27, 237, 108, 250, 188, 107, 10, 63, 135, 148, 244, 149, 188, 146, 117, 41, 248, 228, 6, 102, 200, 79, 153, 65, 201, 110, 233, 122, 76, 216, 38, 169, 203, 63, 73, 66, 124, 204, 195, 202, 125, 44, 119, 183, 148, 141, 213, 222, 13, 114, 64, 204, 126, 213, 206, 2, 167, 35, 241, 150, 54, 72, 149, 173, 156, 223, 66, 140, 46, 13, 45, 140, 138, 109, 65, 99, 240, 196, 105, 159, 5, 232, 67, 130, 46, 240, 48, 30, 189, 241, 223, 131, 194, 53, 79, 43, 234, 71, 135, 156, 152, 13, 17, 94, 36, 84, 248, 77, 214, 233, 156, 193, 243, 51, 74, 82, 136, 151, 122, 179, 253, 5, 89, 150, 78, 64, 50, 177, 85, 76, 76, 55, 102, 212, 130, 150, 121, 245, 162, 37, 131, 108, 22, 62, 108, 99, 32, 72, 115, 1, 168, 212, 66, 224, 142, 35, 215, 199, 82], key = [84, 202, 103, 18, 170, 139, 164, 0, 68, 37, 235, 246, 195, 57, 98, 81, 72, 148, 177, 173, 122, 32, 110, 140, 26, 20, 150, 43, 114, 239, 6, 88], nonce = 8832358656846283157 diff --git a/ts_control_noise/src/codec.rs b/ts_control_noise/src/codec.rs index b3d0f056..71d5bec0 100644 --- a/ts_control_noise/src/codec.rs +++ b/ts_control_noise/src/codec.rs @@ -159,3 +159,146 @@ where } } } + +#[cfg(test)] +mod test { + use std::sync::LazyLock; + + use noise_protocol::Cipher as _; + use proptest::{collection::vec, prelude::*}; + use tokio::io::{AsyncReadExt, AsyncWriteExt}; + use tokio_util::codec::Framed; + + use super::*; + + type Cipher = crate::ChaCha20Poly1305BigEndian; + + fn init_codec_pair(key: [u8; 32], nonce: u64) -> (Codec, Codec) { + let encrypt_state = CipherState::::new(&key, nonce); + let decrypt_state = encrypt_state.clone(); + + ( + Codec { + cipher_state: encrypt_state, + }, + Codec { + cipher_state: decrypt_state, + }, + ) + } + + fn rand_codec_pair() -> (Codec, Codec) { + init_codec_pair(rand::random(), rand::random()) + } + + const TEST_PAYLOAD: &[u8] = b"hello"; + + #[test] + fn roundtrip() { + let (mut encrypt_codec, mut decrypt_codec) = rand_codec_pair(); + let mut buf = BytesMut::new(); + + encrypt_codec.encode(TEST_PAYLOAD, &mut buf).unwrap(); + assert_ne!(buf.as_ref(), TEST_PAYLOAD); + assert_eq!(buf.len(), TEST_PAYLOAD.len() + Cipher::tag_len() + 3); + + let decoded = decrypt_codec.decode(&mut buf).unwrap().unwrap(); + assert_eq!(decoded.as_ref(), TEST_PAYLOAD); + } + + #[test] + fn roundtrip_partial() { + let (mut encrypt_codec, mut decrypt_codec) = rand_codec_pair(); + let mut buf = BytesMut::new(); + + encrypt_codec.encode(TEST_PAYLOAD, &mut buf).unwrap(); + assert_ne!(buf.as_ref(), TEST_PAYLOAD); + assert_eq!(buf.len(), TEST_PAYLOAD.len() + Cipher::tag_len() + 3); + + for i in 0..TEST_PAYLOAD.len() - 1 { + let mut test_payload = buf.clone().split_to(i); + assert_eq!( + decrypt_codec.decode(&mut test_payload).unwrap(), + None, + "i={i}" + ); + assert_eq!(test_payload.len(), i); + } + + let decoded = decrypt_codec.decode(&mut buf).unwrap().unwrap(); + assert_eq!(decoded.as_ref(), TEST_PAYLOAD); + } + + static RUNTIME: LazyLock = + LazyLock::new(|| tokio::runtime::Runtime::new().unwrap()); + + #[test] + fn read_write() { + let (encrypt_codec, decrypt_codec) = rand_codec_pair(); + + let (rx, tx) = tokio::io::simplex(32); + + let mut framed_encrypt = + crate::framed_io::FramedIo::<_, BytesMut>::new(Framed::new(tx, encrypt_codec)); + let mut framed_decrypt = + crate::framed_io::FramedIo::<_, BytesMut>::new(Framed::new(rx, decrypt_codec)); + + let (_, read_payload) = RUNTIME.block_on(async move { + tokio::try_join![ + async move { + framed_encrypt.write_all(TEST_PAYLOAD).await?; + framed_encrypt.flush().await + }, + async move { + let mut read_payload = BytesMut::zeroed(TEST_PAYLOAD.len()); + framed_decrypt.read_exact(&mut read_payload).await?; + Ok(read_payload) + } + ] + .unwrap() + }); + + assert_eq!(read_payload, TEST_PAYLOAD); + } + + proptest::proptest! { + #[test] + fn roundtrip_prop(payload in vec(any::(), 1..=MAX_MESSAGE_SIZE - 3 - 16), key: [u8; 32], nonce: u64) { + let (mut encrypt_codec, mut decrypt_codec) = init_codec_pair(key, nonce); + + let mut buf = BytesMut::new(); + encrypt_codec.encode(&payload, &mut buf).unwrap(); + let decoded = decrypt_codec.decode(&mut buf).unwrap().unwrap(); + assert_eq!(decoded.as_ref(), payload.as_slice()); + } + + #[test] + fn read_write_prop(payload in vec(any::(), 1..=MAX_MESSAGE_SIZE * 4), key: [u8; 32], nonce: u64) { + let (encrypt_codec, decrypt_codec) = init_codec_pair(key, nonce); + + let (rx, tx) = tokio::io::simplex(32); + + let mut framed_encrypt = crate::framed_io::FramedIo::<_, BytesMut>::new(Framed::new(tx, encrypt_codec)); + let mut framed_decrypt = crate::framed_io::FramedIo::<_, BytesMut>::new(Framed::new(rx, decrypt_codec)); + + let write_payload = payload.clone(); + let mut read_payload = BytesMut::zeroed(payload.len()); + + let (_, read_payload) = RUNTIME.block_on(async move { + tokio::try_join![ + async move { + framed_encrypt.write_all(&write_payload).await?; + framed_encrypt.flush().await + }, + async move { + framed_decrypt.read_exact(&mut read_payload).await?; + Ok(read_payload) + } + ] + .unwrap() + }); + + assert_eq!(read_payload, payload); + } + } +} From d30003ef724eea1c70daa08712ca9ddbc05f2b05 Mon Sep 17 00:00:00 2001 From: Nathan Perry Date: Thu, 30 Apr 2026 16:42:08 -0400 Subject: [PATCH 4/4] control_noise: hardcoded len => size_of::
() Signed-off-by: Nathan Perry Change-Id: Ib2ebc84b88460e7191b57d7d8f9811ec6a6a6964 --- Cargo.lock | 1 + ts_control_noise/Cargo.toml | 1 + ts_control_noise/src/codec.rs | 16 +++++++++++----- ts_control_noise/src/messages.rs | 2 ++ 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 626ed55e..f4b3a4aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4202,6 +4202,7 @@ dependencies = [ "pin-project-lite", "proptest", "rand 0.10.1", + "static_assertions", "thiserror 2.0.18", "tokio", "tokio-util", diff --git a/ts_control_noise/Cargo.toml b/ts_control_noise/Cargo.toml index 9e67692d..bfe95eb9 100644 --- a/ts_control_noise/Cargo.toml +++ b/ts_control_noise/Cargo.toml @@ -24,6 +24,7 @@ futures-util = { workspace = true, features = ["sink"] } noise-protocol = "0.2" noise-rust-crypto = "0.6" pin-project-lite.workspace = true +static_assertions.workspace = true thiserror.workspace = true tokio = { workspace = true, features = ["io-util"] } tokio-util = { workspace = true, features = ["codec"] } diff --git a/ts_control_noise/src/codec.rs b/ts_control_noise/src/codec.rs index 71d5bec0..96955570 100644 --- a/ts_control_noise/src/codec.rs +++ b/ts_control_noise/src/codec.rs @@ -81,7 +81,7 @@ where fn encode(&mut self, b: B, dst: &mut BytesMut) -> Result<(), Self::Error> { let b = b.as_ref(); - let max_data_chunk = MAX_MESSAGE_SIZE - (3 + C::tag_len()); // 3 = header len + let max_data_chunk = MAX_MESSAGE_SIZE - (size_of::
() + C::tag_len()); for chunk in b.chunks(max_data_chunk) { let hdr = Header { @@ -126,7 +126,7 @@ where return Ok(None); } - src.advance(3); + src.advance(size_of::
()); let mut body = src.split_to(len); match header.typ { @@ -200,7 +200,10 @@ mod test { encrypt_codec.encode(TEST_PAYLOAD, &mut buf).unwrap(); assert_ne!(buf.as_ref(), TEST_PAYLOAD); - assert_eq!(buf.len(), TEST_PAYLOAD.len() + Cipher::tag_len() + 3); + assert_eq!( + buf.len(), + TEST_PAYLOAD.len() + Cipher::tag_len() + size_of::
() + ); let decoded = decrypt_codec.decode(&mut buf).unwrap().unwrap(); assert_eq!(decoded.as_ref(), TEST_PAYLOAD); @@ -213,7 +216,10 @@ mod test { encrypt_codec.encode(TEST_PAYLOAD, &mut buf).unwrap(); assert_ne!(buf.as_ref(), TEST_PAYLOAD); - assert_eq!(buf.len(), TEST_PAYLOAD.len() + Cipher::tag_len() + 3); + assert_eq!( + buf.len(), + TEST_PAYLOAD.len() + Cipher::tag_len() + size_of::
() + ); for i in 0..TEST_PAYLOAD.len() - 1 { let mut test_payload = buf.clone().split_to(i); @@ -263,7 +269,7 @@ mod test { proptest::proptest! { #[test] - fn roundtrip_prop(payload in vec(any::(), 1..=MAX_MESSAGE_SIZE - 3 - 16), key: [u8; 32], nonce: u64) { + fn roundtrip_prop(payload in vec(any::(), 1..=MAX_MESSAGE_SIZE - size_of::
() - Cipher::tag_len()), key: [u8; 32], nonce: u64) { let (mut encrypt_codec, mut decrypt_codec) = init_codec_pair(key, nonce); let mut buf = BytesMut::new(); diff --git a/ts_control_noise/src/messages.rs b/ts_control_noise/src/messages.rs index 2261d067..a11ab1f6 100644 --- a/ts_control_noise/src/messages.rs +++ b/ts_control_noise/src/messages.rs @@ -28,6 +28,8 @@ pub struct Header { pub len: U16, } +static_assertions::const_assert_eq!(size_of::
(), 3); + /// An initiation message. /// /// Distinct from other message types in that it starts with the capability version.