//! Noise_NX handshake and the framed transport on top of it. //! //! Two independent layers: Noise messages under a u16 length prefix, and //! application frames (u32 length || u8 op || body) split across as many //! Noise messages as they need and reassembled from them. use std::io::{self, Read, Write}; use std::net::TcpStream; use snow::{Builder, TransportState}; use crate::proto::{ProtocolError, MAX_FRAME, NOISE_PARAMS, NOISE_PAYLOAD, PROLOGUE}; /// Runs the Noise_NX responder handshake. The initiator stays anonymous; /// only we hold a static key. Returns the transport state and the /// handshake hash (needed later to verify AUTH frames). pub fn handshake( stream: &mut TcpStream, static_key: &[u8], ) -> anyhow::Result<(TransportState, Vec)> { let params: snow::params::NoiseParams = NOISE_PARAMS.parse()?; let mut noise = Builder::new(params) .local_private_key(static_key) .prologue(PROLOGUE) .build_responder()?; let mut buf = [0u8; 65535]; let mut msg = [0u8; 65535]; let len = read_u16_len(stream)?; read_exact_into(stream, &mut buf[..len])?; noise.read_message(&buf[..len], &mut msg)?; let len = noise.write_message(&[], &mut buf)?; write_u16_len(stream, &buf[..len])?; anyhow::ensure!(noise.is_handshake_finished(), "handshake did not complete"); let hash = noise.get_handshake_hash().to_vec(); let transport = noise.into_transport_mode()?; Ok((transport, hash)) } fn read_exact_into(stream: &mut TcpStream, buf: &mut [u8]) -> io::Result<()> { stream.read_exact(buf) } fn read_u16_len(stream: &mut TcpStream) -> io::Result { let mut len_buf = [0u8; 2]; stream.read_exact(&mut len_buf)?; Ok(u16::from_be_bytes(len_buf) as usize) } fn write_u16_len(stream: &mut TcpStream, packet: &[u8]) -> io::Result<()> { stream.write_all(&(packet.len() as u16).to_be_bytes())?; stream.write_all(packet)?; Ok(()) } pub struct Channel { stream: TcpStream, transport: TransportState, buf: Vec, } impl Channel { pub fn new(stream: TcpStream, transport: TransportState) -> Self { Channel { stream, transport, buf: Vec::new(), } } fn read_noise(&mut self) -> anyhow::Result> { let len = read_u16_len(&mut self.stream)?; let mut ciphertext = vec![0u8; len]; read_exact_into(&mut self.stream, &mut ciphertext)?; let mut plaintext = vec![0u8; len]; let n = self.transport.read_message(&ciphertext, &mut plaintext)?; plaintext.truncate(n); Ok(plaintext) } fn write_noise(&mut self, payload: &[u8]) -> anyhow::Result<()> { let mut packet = vec![0u8; payload.len() + 16]; let n = self.transport.write_message(payload, &mut packet)?; packet.truncate(n); write_u16_len(&mut self.stream, &packet)?; Ok(()) } /// Reads one application frame, blocking until a full frame is available. pub fn read_frame(&mut self) -> anyhow::Result<(u8, Vec)> { while self.buf.len() < 5 { let chunk = self.read_noise()?; self.buf.extend_from_slice(&chunk); } let length = u32::from_be_bytes(self.buf[..4].try_into().unwrap()) as usize; if length < 1 || length > MAX_FRAME { return Err(ProtocolError::new(format!("frame length {length} out of range")).into()); } while self.buf.len() < 4 + length { let chunk = self.read_noise()?; self.buf.extend_from_slice(&chunk); } let frame: Vec = self.buf[4..4 + length].to_vec(); self.buf.drain(..4 + length); Ok((frame[0], frame[1..].to_vec())) } pub fn write_frame(&mut self, op: u8, body: &[u8]) -> anyhow::Result<()> { let mut frame = Vec::with_capacity(5 + body.len()); frame.extend_from_slice(&((1 + body.len()) as u32).to_be_bytes()); frame.push(op); frame.extend_from_slice(body); for chunk in frame.chunks(NOISE_PAYLOAD) { self.write_noise(chunk)?; } Ok(()) } }