From 584bc248c4371026bfe6ad0af1a6e2458d7b2095 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Kleer?= Date: Fri, 24 Jan 2025 17:36:50 +0100 Subject: [PATCH 1/2] added example for oneway pattern Xpsk1 --- examples/oneway.rs | 141 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 examples/oneway.rs diff --git a/examples/oneway.rs b/examples/oneway.rs new file mode 100644 index 0000000..b5da7c3 --- /dev/null +++ b/examples/oneway.rs @@ -0,0 +1,141 @@ +#![cfg_attr( + not(any(feature = "default-resolver", feature = "ring-accelerated",)), + allow(dead_code, unused_extern_crates, unused_imports) +)] +//! This is a barebones TCP Client/Server that establishes a `Noise_X` session, and sends +//! an important message across the wire. +//! +//! # Usage +//! Run the server a-like-a-so `cargo run --example oneway -- -s`, then run the client +//! as `cargo run --example oneway` to see the magic happen. + +use hex::FromHex; +use lazy_static::lazy_static; +use snow::{params::NoiseParams, Builder, Keypair}; +use std::{ + io::{self, Read, Write}, + net::{TcpListener, TcpStream}, +}; + +static SECRET: &[u8; 32] = b"i don't care for fidget spinners"; +lazy_static! { + static ref PARAMS: NoiseParams = "Noise_Xpsk1_25519_ChaChaPoly_BLAKE2s".parse().unwrap(); + // The responder key is static in this example because the X pattern means + // the initiator has pre-handshake knowledge of the responder's public key + // (and of course both share the same psk `SECRET`) + static ref RESPONDER: Keypair = Keypair { + private: Vec::from_hex("52fbe3721d1adbe312d270ca2db5ce5bd39ddc206075f3a8f06d422619c8eb5d").expect("valid hex"), + public: Vec::from_hex("435ce8a8415ccd44de5e207581ac7207b416683028bcaecc9eb38d944e6f900c").expect("valid hex"), + }; +} + +#[cfg(any(feature = "default-resolver", feature = "ring-accelerated"))] +fn main() { + let server_mode = + std::env::args().next_back().map_or(true, |arg| arg == "-s" || arg == "--server"); + + if server_mode { + run_server(&RESPONDER.private, SECRET); + } else { + run_client(&RESPONDER.public, SECRET); + } + println!("all done."); +} + +#[cfg(any(feature = "default-resolver", feature = "ring-accelerated"))] +fn run_server(private_key: &[u8], psk: &[u8; 32]) { + let mut buf = vec![0u8; 65535]; + + // Initialize our responder using a builder. + let builder = Builder::new(PARAMS.clone()); + let mut noise = builder + .local_private_key(private_key) + .unwrap() + .psk(1, psk) + .unwrap() + .build_responder() + .unwrap(); + + // Wait on our client's arrival... + println!("listening on 127.0.0.1:9999"); + let (mut stream, _) = TcpListener::bind("127.0.0.1:9999").unwrap().accept().unwrap(); + + // <- e, es, s, ss + noise.read_message(&recv(&mut stream).unwrap(), &mut buf).unwrap(); + + // This is a oneway handshake - the server does not have to send anything + + // The remote static key (of the initiator) is now known, and could be used + // for example to decide whether to limit the size/number/kind of messages + // the responder accepts from this initiator + let client = hex::encode(noise.get_remote_static().unwrap()); + + // Transition the state machine into transport mode now that the handshake is complete. + let mut noise = noise.into_transport_mode().unwrap(); + + while let Ok(msg) = recv(&mut stream) { + let len = noise.read_message(&msg, &mut buf).unwrap(); + println!("{client} said: {}", String::from_utf8_lossy(&buf[..len])); + } + println!("connection closed."); +} + +#[cfg(any(feature = "default-resolver", feature = "ring-accelerated"))] +fn run_client(responder_public_key: &[u8], psk: &[u8; 32]) { + let mut buf = vec![0u8; 65535]; + + // Initialize our initiator using a builder. + let builder = Builder::new(PARAMS.clone()); + let private_key = &builder.generate_keypair().unwrap().private; + let mut noise = builder + .local_private_key(private_key) + .unwrap() + .remote_public_key(responder_public_key) + .unwrap() + .psk(1, psk) + .unwrap() + .build_initiator() + .unwrap(); + + // Connect to our server, which is hopefully listening. + let mut stream = TcpStream::connect("127.0.0.1:9999").unwrap(); + println!("connected..."); + + // -> e, es, s, ss + let len = noise.write_message(&[], &mut buf).unwrap(); + send(&mut stream, &buf[..len]); + + // This is a oneway handshake - the respnder must not send anything + + let mut noise = noise.into_transport_mode().unwrap(); + println!("session established..."); + + // Get to the important business of sending secured data. + for _ in 0..10 { + let len = noise.write_message(b"HACK THE PLANET", &mut buf).unwrap(); + send(&mut stream, &buf[..len]); + } + println!("notified server of intent to hack planet."); +} + +/// Hyper-basic stream transport receiver. 16-bit BE size followed by payload. +fn recv(stream: &mut TcpStream) -> io::Result> { + let mut msg_len_buf = [0u8; 2]; + stream.read_exact(&mut msg_len_buf)?; + let msg_len = usize::from(u16::from_be_bytes(msg_len_buf)); + let mut msg = vec![0u8; msg_len]; + stream.read_exact(&mut msg[..])?; + Ok(msg) +} + +/// Hyper-basic stream transport sender. 16-bit BE size followed by payload. +fn send(stream: &mut TcpStream, buf: &[u8]) { + let len = u16::try_from(buf.len()).expect("message too large"); + stream.write_all(&len.to_be_bytes()).unwrap(); + stream.write_all(buf).unwrap(); +} + +#[cfg(not(any(feature = "default-resolver", feature = "ring-accelerated")))] +fn main() { + panic!("Example must be compiled with some cryptographic provider."); +} From 8128a37f3d85a65db8b8789d75a9164901dba93c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Kleer?= Date: Sat, 25 Jan 2025 15:52:39 +0100 Subject: [PATCH 2/2] remove spurious comment --- examples/oneway.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/oneway.rs b/examples/oneway.rs index b5da7c3..63855be 100644 --- a/examples/oneway.rs +++ b/examples/oneway.rs @@ -65,9 +65,7 @@ fn run_server(private_key: &[u8], psk: &[u8; 32]) { // This is a oneway handshake - the server does not have to send anything - // The remote static key (of the initiator) is now known, and could be used - // for example to decide whether to limit the size/number/kind of messages - // the responder accepts from this initiator + // The remote static key (of the initiator) is now known let client = hex::encode(noise.get_remote_static().unwrap()); // Transition the state machine into transport mode now that the handshake is complete.