### Snow no_std Usage Example Source: https://context7.com/mcginty/snow/llms.txt This example demonstrates how to use the Snow crate in a no_std environment, requiring the 'alloc' crate to be provided by the runtime. It sets up a Noise builder for a specific pattern and prepares to build an initiator. ```rust #!/usr/bin/env rust // no_std usage (alloc must be provided by the runtime) #![no_std] extern crate alloc; use snow::Builder; fn establish_session(private_key: &[u8]) -> Result { let mut initiator = Builder::new("Noise_NN_25519_ChaChaPoly_BLAKE2s".parse()?) .build_initiator()?; // ... drive handshake and return transport state todo!() } ``` -------------------------------- ### Complete TCP Client/Server Example with Noise_XXpsk3 Source: https://context7.com/mcginty/snow/llms.txt A full working example of a `Noise_XXpsk3` session between a TCP server and client, demonstrating key generation, the complete XX handshake with PSK, transport-mode encryption, and graceful connection teardown. ```rust use snow::{Builder, params::NoiseParams}; use std::{ io::{self, Read, Write}, net::{TcpListener, TcpStream}, sync::LazyLock, }; static SECRET: &[u8; 32] = b"i don't care for fidget spinners"; static PARAMS: LazyLock = LazyLock::new(|| "Noise_XXpsk3_25519_ChaChaPoly_BLAKE2s".parse().unwrap()); fn run_server() { let mut buf = vec![0u8; 65535]; let builder = Builder::new(PARAMS.clone()); let static_key = builder.generate_keypair().unwrap().private; let mut noise = builder .local_private_key(&static_key).unwrap() .psk(3, SECRET).unwrap() .build_responder().unwrap(); let (mut stream, _) = TcpListener::bind("127.0.0.1:9999").unwrap().accept().unwrap(); // XX handshake: 3 messages noise.read_message(&recv(&mut stream).unwrap(), &mut buf).unwrap(); // <- e let n = noise.write_message(&[], &mut buf).unwrap(); send(&mut stream, &buf[..n]); // -> e,ee,s,es noise.read_message(&recv(&mut stream).unwrap(), &mut buf).unwrap(); // <- s,se let mut transport = noise.into_transport_mode().unwrap(); while let Ok(msg) = recv(&mut stream) { let n = transport.read_message(&msg, &mut buf).unwrap(); println!("client: {}", String::from_utf8_lossy(&buf[..n])); } } fn run_client() { let mut buf = vec![0u8; 65535]; let builder = Builder::new(PARAMS.clone()); let static_key = builder.generate_keypair().unwrap().private; let mut noise = builder .local_private_key(&static_key).unwrap() .psk(3, SECRET).unwrap() .build_initiator().unwrap(); let mut stream = TcpStream::connect("127.0.0.1:9999").unwrap(); let n = noise.write_message(&[], &mut buf).unwrap(); send(&mut stream, &buf[..n]); // -> e noise.read_message(&recv(&mut stream).unwrap(), &mut buf).unwrap(); // <- e,ee,s,es let n = noise.write_message(&[], &mut buf).unwrap(); send(&mut stream, &buf[..n]); // -> s,se let mut transport = noise.into_transport_mode().unwrap(); for _ in 0..5 { let n = transport.write_message(b"HACK THE PLANET", &mut buf).unwrap(); send(&mut stream, &buf[..n]); } } fn recv(s: &mut TcpStream) -> io::Result> { let mut len_buf = [0u8; 2]; s.read_exact(&mut len_buf)?; let mut msg = vec![0u8; u16::from_be_bytes(len_buf) as usize]; s.read_exact(&mut msg)?; Ok(msg) } fn send(s: &mut TcpStream, buf: &[u8]) { s.write_all(&(buf.len() as u16).to_be_bytes()).unwrap(); s.write_all(buf).unwrap(); } ``` -------------------------------- ### Initiate Noise Handshake Source: https://github.com/mcginty/snow/blob/main/README.md Example of initiating a Noise handshake with the initiator role. Requires parsing the protocol name and building the initiator. The handshake involves writing the first message and reading the response. ```rust let mut noise = snow::Builder::new("Noise_NN_25519_ChaChaPoly_BLAKE2s".parse()?) .build_initiator()?; let mut buf = [0u8; 65535]; // write first handshake message noise.write_message(&[], &mut buf)?; // receive response message let incoming = receive_message_from_the_mysterious_ether(); noise.read_message(&incoming, &mut buf)?; // complete handshake, and transition the state machine into transport mode let mut noise = noise.into_transport_mode()?; ``` -------------------------------- ### Create Session Builder with Noise Pattern Source: https://context7.com/mcginty/snow/llms.txt Parses a Noise protocol string to create a session builder. Use this to start configuring a Noise session. The string format is `Noise____`, with optional modifiers. ```rust use snow::Builder; fn main() -> Result<(), Box> { // Parse a pattern and create a builder let builder = Builder::new("Noise_XX_25519_ChaChaPoly_BLAKE2s".parse()?); // With ring-accelerated feature, use the faster ring-based resolver: // (Builder::new automatically uses ring when ring-accelerated feature is enabled) // Generate a static keypair for use as a long-term identity key let keypair = builder.generate_keypair()?; println!("public key: {} bytes", keypair.public.len()); // 32 bytes for Curve25519 println!("private key: {} bytes", keypair.private.len()); // 32 bytes Ok(()) } ``` -------------------------------- ### `no_std` Feature Configuration for Embedded Systems Source: https://context7.com/mcginty/snow/llms.txt Minimal `no_std` setup for an embedded target using Snow. Requires compiling with `default-features = false` and selecting specific crypto primitives. Only the `default-resolver` supports `no_std`. ```toml # Cargo.toml — minimal no_std setup for an embedded target [dependencies.snow] version = "0.10" default-features = false features = [ "default-resolver", "use-curve25519", "use-chacha20poly1305", "use-blake2", ] ``` -------------------------------- ### Configure Static Keys for Handshake Source: https://context7.com/mcginty/snow/llms.txt Sets the local static private key and/or the remote party's public key. Required keys depend on the handshake pattern. For example, `NK` requires `remote_public_key` on the initiator, while `XX` requires `local_private_key`. ```rust use snow::Builder; fn main() -> Result<(), Box> { let builder = Builder::new("Noise_NK_25519_ChaChaPoly_SHA256".parse()?); let server_keypair = builder.generate_keypair()?; // Initiator must know the responder's public key in advance for NK pattern let initiator = Builder::new("Noise_NK_25519_ChaChaPoly_SHA256".parse()?) .local_private_key(&builder.generate_keypair()?.private)? .remote_public_key(&server_keypair.public)? // pre-known server pubkey .build_initiator()?; // Responder knows its own private key let responder = Builder::new("Noise_NK_25519_ChaChaPoly_SHA256".parse()?) .local_private_key(&server_keypair.private)? .build_responder()?; println!("initiator is_initiator: {}", initiator.is_initiator()); // true println!("responder is_initiator: {}", responder.is_initiator()); // false Ok(()) } ``` -------------------------------- ### Retrieve Remote Static Public Key After Handshake Source: https://context7.com/mcginty/snow/llms.txt Shows how to get the remote party's authenticated static public key after a successful handshake. This is available on both `HandshakeState` and `TransportState`. ```rust use snow::Builder; fn main() -> Result<(), Box> { let i_builder = Builder::new("Noise_XX_25519_ChaChaPoly_BLAKE2s".parse()?); let r_builder = Builder::new("Noise_XX_25519_ChaChaPoly_BLAKE2s".parse()?); let i_kp = i_builder.generate_keypair()?; let r_kp = r_builder.generate_keypair()?; let mut initiator = Builder::new("Noise_XX_25519_ChaChaPoly_BLAKE2s".parse()?) .local_private_key(&i_kp.private)?.build_initiator()?; let mut responder = Builder::new("Noise_XX_25519_ChaChaPoly_BLAKE2s".parse()?) .local_private_key(&r_kp.private)?.build_responder()?; let (mut buf, mut rbuf) = ([0u8; 65535], [0u8; 65535]); // -> e let n = initiator.write_message(&[], &mut buf)?; responder.read_message(&buf[..n], &mut rbuf)?; // <- e, ee, s, es let n = responder.write_message(&[], &mut buf)?; initiator.read_message(&buf[..n], &mut rbuf)?; // -> s, se let n = initiator.write_message(&[], &mut buf)?; responder.read_message(&buf[..n], &mut rbuf)?; // After XX completes, both sides know each other's static key let remote_s_at_initiator = initiator.get_remote_static().unwrap(); let remote_s_at_responder = responder.get_remote_static().unwrap(); assert_eq!(remote_s_at_initiator, r_kp.public.as_slice()); assert_eq!(remote_s_at_responder, i_kp.public.as_slice()); println!("mutual authentication complete"); Ok(()) } ``` -------------------------------- ### Configure Snow with Curve25519, ChaChaPoly, BLAKE2 (no std) Source: https://github.com/mcginty/snow/blob/main/README.md This configuration enables Curve25519, ChaChaPoly, and BLAKE2 primitives without relying on the standard library. Ensure `default-features` is false and specify the required features. ```toml default-features = false features = [ "use-curve25519", "use-chacha20poly1305", "use-blake2", ] ``` -------------------------------- ### Builder::new Source: https://context7.com/mcginty/snow/llms.txt Creates a new session builder by parsing a Noise pattern string. This builder is then used to configure keys and build a `HandshakeState`. ```APIDOC ## Builder::new ### Description Parses a Noise protocol string (e.g. `"Noise_XX_25519_ChaChaPoly_BLAKE2s"`) and returns a `Builder` ready to be configured with keys and built into a `HandshakeState`. The string format is `Noise____`, with optional modifiers like `psk0`, `psk3`, or `hfs`. ### Method `Builder::new(pattern: Pattern)` ### Parameters - **pattern** (`Pattern`): A parsed Noise protocol pattern string. ### Request Example ```rust use snow::Builder; fn main() -> Result<(), Box> { let builder = Builder::new("Noise_XX_25519_ChaChaPoly_BLAKE2s".parse()?); let keypair = builder.generate_keypair()?; println!("public key: {} bytes", keypair.public.len()); println!("private key: {} bytes", keypair.private.len()); Ok(()) } ``` ### Response - **Builder**: A configured session builder. - **Result**: Returns an error if the pattern string is invalid. ``` -------------------------------- ### Builder::with_resolver Source: https://context7.com/mcginty/snow/llms.txt Initializes a builder with a custom cryptographic resolver. This allows for flexibility in choosing cryptographic primitives, such as using the `ring` crate for performance or a fallback resolver. ```APIDOC ## Builder::with_resolver ### Description Allows providing a custom `CryptoResolver` implementation or a `FallbackResolver` that chains two resolvers together. Useful for using `ring` for some primitives and falling back to pure-Rust implementations for others (e.g. BLAKE2 which ring doesn't provide). ### Method `Builder::with_resolver(pattern: Pattern, resolver: Box) ### Parameters - **pattern** (`Pattern`): A parsed Noise protocol pattern string. - **resolver** (`Box`): A custom cryptographic resolver. ### Request Example ```rust use snow::{Builder, resolvers::{FallbackResolver, RingResolver, DefaultResolver}}; fn main() -> Result<(), Box> { let resolver = FallbackResolver::new( Box::new(RingResolver), Box::new(DefaultResolver), ); let builder = Builder::with_resolver( "Noise_XX_25519_AESGCM_SHA256".parse()?, Box::new(resolver), ); let keypair = builder.generate_keypair()?; let _initiator = builder .local_private_key(&keypair.private)? .build_initiator()?; Ok(()) } ``` ### Response - **Builder**: A configured session builder with the custom resolver. ``` -------------------------------- ### Transition to Stateless Transport Mode (Unreliable Transports) Source: https://context7.com/mcginty/snow/llms.txt Use `into_stateless_transport_mode()` for unreliable transports like UDP or QUIC. The caller must explicitly manage and provide the nonce for each encryption/decryption operation. ```rust use snow::Builder; fn main() -> Result<(), Box> { let mut initiator = Builder::new("Noise_NN_25519_ChaChaPoly_BLAKE2s".parse()?) .build_initiator()?; let mut responder = Builder::new("Noise_NN_25519_ChaChaPoly_BLAKE2s".parse()?) .build_responder()?; let (mut buf, mut read_buf) = ([0u8; 65535], [0u8; 65535]); let n = initiator.write_message(&[], &mut buf)?; responder.read_message(&buf[..n], &mut read_buf)?; let n = responder.write_message(&[], &mut buf)?; initiator.read_message(&buf[..n], &mut read_buf)?; // Stateless transport: caller controls nonces (for UDP etc.) let i_transport = initiator.into_stateless_transport_mode()?; let r_transport = responder.into_stateless_transport_mode()?; let msg = b"UDP packet #42"; let nonce: u64 = 42; let encrypted_len = i_transport.write_message(nonce, msg, &mut buf)?; let decrypted_len = r_transport.read_message(nonce, &buf[..encrypted_len], &mut read_buf)?; assert_eq!(&read_buf[..decrypted_len], msg); println!("stateless decrypted with nonce {}: {:?}", nonce, std::str::from_utf8(&read_buf[..decrypted_len])?); Ok(()) } ``` -------------------------------- ### Configure Snow with Curve25519, AES-GCM, SHA-2, and std Source: https://github.com/mcginty/snow/blob/main/README.md Use this configuration to enable Curve25519, AES-GCM, and SHA-2 primitives along with standard library support. Disable default features and explicitly list desired features. ```toml default-features = false features = [ "use-curve25519", "use-aes-gcm", "use-sha2", "std", ] ``` -------------------------------- ### Finalize Handshake State with `Builder::build_initiator` / `Builder::build_responder` Source: https://context7.com/mcginty/snow/llms.txt Validates prerequisites and constructs a `HandshakeState`. `build_initiator` creates the sending side, and `build_responder` creates the receiving side. Some patterns like NN do not require static keys. ```rust use snow::Builder; fn main() -> Result<(), Box> { let params = "Noise_NN_25519_ChaChaPoly_BLAKE2s".parse()?; // NN pattern: no static keys required — anonymous handshake let initiator = Builder::new(params).build_initiator()?; let responder = Builder::new("Noise_NN_25519_ChaChaPoly_BLAKE2s".parse()?).build_responder()?; println!("initiator ready: {}", initiator.is_initiator()); // true println!("responder ready: {}", !responder.is_initiator()); // true // NK/XX patterns require keys: let kp = Builder::new("Noise_XX_25519_AESGCM_SHA256".parse()?).generate_keypair()?; let _with_keys = Builder::new("Noise_XX_25519_AESGCM_SHA256".parse()?) .local_private_key(&kp.private)? .build_initiator()?; Ok(()) } ``` -------------------------------- ### Create Builder with Custom Crypto Resolver Source: https://context7.com/mcginty/snow/llms.txt Allows specifying a custom `CryptoResolver` or a `FallbackResolver` to chain resolvers. This is useful for using `ring` for some primitives and falling back to pure-Rust implementations for others. ```rust use snow::{Builder, resolvers::{FallbackResolver, RingResolver, DefaultResolver}}; fn main() -> Result<(), Box> { // Use ring where available, fall back to default resolver for BLAKE2 etc. let resolver = FallbackResolver::new( Box::new(RingResolver), Box::new(DefaultResolver), ); let builder = Builder::with_resolver( "Noise_XX_25519_AESGCM_SHA256".parse()?, Box::new(resolver), ); let keypair = builder.generate_keypair()?; let _initiator = builder .local_private_key(&keypair.private)? .build_initiator()?; Ok(()) } ``` -------------------------------- ### Builder::build_initiator / Builder::build_responder Source: https://context7.com/mcginty/snow/llms.txt Finalizes the builder into a `HandshakeState`. `build_initiator` creates the side that sends the first handshake message, while `build_responder` creates the side that receives it. This validates prerequisites and constructs the handshake state. ```APIDOC ## Builder::build_initiator / Builder::build_responder ### Description Validates all prerequisites for the chosen pattern (e.g. that required keys have been provided) and constructs a `HandshakeState`. `build_initiator` creates the side that sends the first handshake message; `build_responder` creates the side that receives it. ### Method Signatures `fn build_initiator(&self) -> Result;` `fn build_responder(&self) -> Result;` ### Return Value A `Result` containing a `HandshakeState` on success, or an `Error` on failure. ### Example ```rust use snow::Builder; // NN pattern: no static keys required — anonymous handshake let initiator = Builder::new("Noise_NN_25519_ChaChaPoly_BLAKE2s".parse()?) .build_initiator()?; let responder = Builder::new("Noise_NN_25519_ChaChaPoly_BLAKE2s".parse()?) .build_responder()?; // XX pattern requires keys: let kp = Builder::new("Noise_XX_25519_AESGCM_SHA256".parse()?) .generate_keypair()?; let _with_keys = Builder::new("Noise_XX_25519_AESGCM_SHA256".parse()?) .local_private_key(&kp.private)? .build_initiator()?; ``` ``` -------------------------------- ### Builder::local_private_key / Builder::remote_public_key Source: https://context7.com/mcginty/snow/llms.txt Configures the static keys for the handshake. `local_private_key` sets the identity key for the local party, while `remote_public_key` sets the known public key of the remote party. The necessity of each depends on the handshake pattern. ```APIDOC ## Builder::local_private_key / Builder::remote_public_key ### Description Configure the local static private key and/or the remote party's known public key before building the `HandshakeState`. Which keys are required depends on the chosen handshake pattern (e.g. `NK` requires `remote_public_key` on the initiator side; `XX` requires only `local_private_key`). ### Method `Builder::local_private_key(private_key: &[u8]) -> Result `Builder::remote_public_key(public_key: &[u8]) -> Result ### Parameters - **private_key** (`&[u8]`): The local party's static private key. - **public_key** (`&[u8]`): The remote party's static public key. ### Request Example ```rust use snow::Builder; fn main() -> Result<(), Box> { let builder = Builder::new("Noise_NK_25519_ChaChaPoly_SHA256".parse()?); let server_keypair = builder.generate_keypair()?; let initiator = Builder::new("Noise_NK_25519_ChaChaPoly_SHA256".parse()?) .local_private_key(&builder.generate_keypair()?.private)? .remote_public_key(&server_keypair.public)? .build_initiator()?; let responder = Builder::new("Noise_NK_25519_ChaChaPoly_SHA256".parse()?) .local_private_key(&server_keypair.private)? .build_responder()?; println!("initiator is_initiator: {}", initiator.is_initiator()); println!("responder is_initiator: {}", responder.is_initiator()); Ok(()) } ``` ### Response - **Builder**: The builder instance with the key(s) configured. - **Result**: Returns an error if the provided key is invalid or incompatible with the handshake pattern. ``` -------------------------------- ### Snow Crate Dependencies for std Environment Source: https://context7.com/mcginty/snow/llms.txt This snippet shows how to include the Snow crate with ring acceleration for a standard Rust environment. It specifies the version and enables the 'ring-accelerated' feature. ```toml [dependencies.snow] version = "0.10" features = ["ring-accelerated"] # uses ring + pure-Rust fallback, includes std ``` -------------------------------- ### Execute Handshake Turns with `HandshakeState::write_message` / `read_message` Source: https://context7.com/mcginty/snow/llms.txt Drives the handshake state machine by serializing outbound messages and parsing inbound messages. `write_message` prepares the next message, and `read_message` processes an incoming message. Both return the number of bytes processed. ```rust use snow::Builder; fn main() -> Result<(), Box> { let mut initiator = Builder::new("Noise_NN_25519_ChaChaPoly_BLAKE2s".parse()?) .build_initiator()?; let mut responder = Builder::new("Noise_NN_25519_ChaChaPoly_BLAKE2s".parse()?) .build_responder()?; let mut buf = [0u8; 65535]; let mut read_buf = [0u8; 65535]; // -> e (initiator sends ephemeral key) let msg1_len = initiator.write_message(&[], &mut buf)?; println!("msg1 size: {} bytes", msg1_len); // responder reads it responder.read_message(&buf[..msg1_len], &mut read_buf)?; // <- e, ee (responder sends ephemeral key + DH result) let msg2_len = responder.write_message(&[], &mut buf)?; println!("msg2 size: {} bytes", msg2_len); // initiator reads it — handshake is now complete for NN initiator.read_message(&buf[..msg2_len], &mut read_buf)?; println!("handshake finished on initiator side: {}", initiator.is_handshake_finished()); println!("handshake finished on responder side: {}", responder.is_handshake_finished()); Ok(()) } ``` -------------------------------- ### Mix Prologue Data with `Builder::prologue` Source: https://context7.com/mcginty/snow/llms.txt Hashes arbitrary prologue data into the handshake state before any messages are exchanged. Both parties must supply identical prologue data; any mismatch will cause authentication failure. Useful for binding the handshake to application-level metadata. ```rust use snow::Builder; fn main() -> Result<(), Box> { let prologue = b"myapp-v1.2.3"; let builder = Builder::new("Noise_XX_25519_AESGCM_SHA256".parse()?); let kp = builder.generate_keypair()?; let initiator = Builder::new("Noise_XX_25519_AESGCM_SHA256".parse()?) .local_private_key(&kp.private)? .prologue(prologue)? // Must match responder's prologue exactly .build_initiator()?; println!("prologue bound to handshake"); Ok(()) } ``` -------------------------------- ### Builder::prologue Source: https://context7.com/mcginty/snow/llms.txt Hashes arbitrary prologue data into the handshake state before any messages are exchanged. Both parties must supply identical prologue data; any mismatch will cause authentication failure. ```APIDOC ## Builder::prologue ### Description Hashes arbitrary prologue data into the handshake state before any messages are exchanged. Both parties must supply identical prologue data; any mismatch will cause authentication failure. Useful for binding the handshake to application-level metadata (protocol versions, certificates, etc.). ### Method Signature `fn prologue(&mut self, prologue: &[u8]) -> &mut Builder;` ### Parameters - **prologue** (&[u8]) - Arbitrary byte slice to be hashed into the handshake state. ### Example ```rust use snow::Builder; let prologue = b"myapp-v1.2.3"; let builder = Builder::new("Noise_XX_25519_AESGCM_SHA256".parse()?) .prologue(prologue)?; ``` ``` -------------------------------- ### Transition to Encrypted Transport Mode (Reliable Transports) Source: https://context7.com/mcginty/snow/llms.txt Use `into_transport_mode()` after completing the handshake for reliable transports like TCP. This returns a `TransportState` with internally managed nonces. ```rust use snow::Builder; fn main() -> Result<(), Box> { let mut initiator = Builder::new("Noise_NN_25519_ChaChaPoly_BLAKE2s".parse()?) .build_initiator()?; let mut responder = Builder::new("Noise_NN_25519_ChaChaPoly_BLAKE2s".parse()?) .build_responder()?; let (mut buf, mut read_buf) = ([0u8; 65535], [0u8; 65535]); // Complete NN handshake (2 messages) let n = initiator.write_message(&[], &mut buf)?; responder.read_message(&buf[..n], &mut read_buf)?; let n = responder.write_message(&[], &mut buf)?; initiator.read_message(&buf[..n], &mut read_buf)?; // Both sides transition to transport mode let mut i_transport = initiator.into_transport_mode()?; let mut r_transport = responder.into_transport_mode()?; // Send encrypted application data let msg = b"hello, encrypted world!"; let encrypted_len = i_transport.write_message(msg, &mut buf)?; let decrypted_len = r_transport.read_message(&buf[..encrypted_len], &mut read_buf)?; assert_eq!(&read_buf[..decrypted_len], msg); println!("decrypted: {}", std::str::from_utf8(&read_buf[..decrypted_len])?); // Output: decrypted: hello, encrypted world! Ok(()) } ``` -------------------------------- ### Perform Forward-Secret Rekeying in Transport Mode Source: https://context7.com/mcginty/snow/llms.txt Demonstrates how to rotate symmetric cipher keys after entering transport mode. Both initiator and responder must rekey their outgoing and incoming keys in sync to maintain forward secrecy. ```rust use snow::Builder; fn main() -> Result<(), Box> { let mut i = Builder::new("Noise_NN_25519_ChaChaPoly_SHA256".parse()?).build_initiator()?; let mut r = Builder::new("Noise_NN_25519_ChaChaPoly_SHA256".parse()?).build_responder()?; let (mut buf, mut rbuf) = ([0u8; 65535], [0u8; 65535]); let n = i.write_message(&[], &mut buf)?; r.read_message(&buf[..n], &mut rbuf)?; let n = r.write_message(&[], &mut buf)?; i.read_message(&buf[..n], &mut rbuf)?; let mut i_t = i.into_transport_mode()?; let mut r_t = r.into_transport_mode()?; // Send a message before rekeying let n = i_t.write_message(b"pre-rekey", &mut buf)?; r_t.read_message(&buf[..n], &mut rbuf)?; // Both sides rotate their outgoing/incoming keys in sync i_t.rekey_outgoing(); // initiator rotates send key r_t.rekey_incoming(); // responder rotates receive key (must match) // Messages after rekey use the new key let n = i_t.write_message(b"post-rekey message", &mut buf)?; let dec_len = r_t.read_message(&buf[..n], &mut rbuf)?; println!("after rekey: {}", std::str::from_utf8(&rbuf[..dec_len])?); // Output: after rekey: post-rekey message Ok(()) } ``` -------------------------------- ### Add Pre-Shared Key (PSK) Modifier with `Builder::psk` Source: https://context7.com/mcginty/snow/llms.txt Adds a 32-byte pre-shared key (PSK) at a specific position in the handshake pattern. The PSK provides an additional layer of symmetric authentication. Ensure the PSK position matches the `pskN` modifier in the pattern string. ```rust use snow::Builder; fn main() -> Result<(), Box> { static PSK: &[u8; 32] = b"super secret 32 byte passphrase!"; let params = "Noise_XXpsk3_25519_ChaChaPoly_BLAKE2s".parse()?; let builder = Builder::new(params); let keypair = builder.generate_keypair()?; // PSK at position 3 — mixed in after the 3rd handshake message let initiator = Builder::new("Noise_XXpsk3_25519_ChaChaPoly_BLAKE2s".parse()?) .local_private_key(&keypair.private)? .psk(3, PSK)? .build_initiator()?; let responder_key = Builder::new("Noise_XXpsk3_25519_ChaChaPoly_BLAKE2s".parse()?) .generate_keypair()?; let responder = Builder::new("Noise_XXpsk3_25519_ChaChaPoly_BLAKE2s".parse()?) .local_private_key(&responder_key.private)? .psk(3, PSK)? .build_responder()?; println!("PSK session configured for both sides"); Ok(()) } ``` -------------------------------- ### HandshakeState::into_stateless_transport_mode Source: https://context7.com/mcginty/snow/llms.txt Transitions the handshake state to a stateless transport mode, suitable for unreliable transports like UDP or QUIC. The caller is responsible for managing and providing nonces for each encryption/decryption operation. ```APIDOC ## `HandshakeState::into_stateless_transport_mode` — Transition to stateless transport Like `into_transport_mode`, but produces a `StatelessTransportState` where the caller explicitly provides the nonce for every encrypt/decrypt call. Required for unreliable transports (UDP/QUIC) where message ordering is not guaranteed and the application manages nonce synchronization. ```rust use snow::Builder; fn main() -> Result<(), Box> { let mut initiator = Builder::new("Noise_NN_25519_ChaChaPoly_BLAKE2s".parse()?) .build_initiator()?; let mut responder = Builder::new("Noise_NN_25519_ChaChaPoly_BLAKE2s".parse()?) .build_responder()?; let (mut buf, mut read_buf) = ([0u8; 65535], [0u8; 65535]); let n = initiator.write_message(&[], &mut buf)?; responder.read_message(&buf[..n], &mut read_buf)?; let n = responder.write_message(&[], &mut buf)?; initiator.read_message(&buf[..n], &mut read_buf)?; // Stateless transport: caller controls nonces (for UDP etc.) let i_transport = initiator.into_stateless_transport_mode()?; let r_transport = responder.into_stateless_transport_mode()?; let msg = b"UDP packet #42"; let nonce: u64 = 42; let encrypted_len = i_transport.write_message(nonce, msg, &mut buf)?; let decrypted_len = r_transport.read_message(nonce, &buf[..encrypted_len], &mut read_buf)?; assert_eq!(&read_buf[..decrypted_len], msg); println!("stateless decrypted with nonce {}: {:?}", nonce, std::str::from_utf8(&read_buf[..decrypted_len])?); Ok(()) } ``` ``` -------------------------------- ### Builder::psk Source: https://context7.com/mcginty/snow/llms.txt Adds a 32-byte pre-shared key (PSK) at a specific position in the handshake (0–9). The position must match the `pskN` modifier in the pattern string. PSK patterns provide an additional layer of symmetric authentication. ```APIDOC ## Builder::psk ### Description Adds a 32-byte pre-shared key (PSK) at a specific position in the handshake (0–9). The position must match the `pskN` modifier in the pattern string. PSK patterns provide an additional layer of symmetric authentication, useful for environments where out-of-band key distribution is possible. ### Method Signature `fn psk(&mut self, position: u8, psk: &[u8; 32]) -> &mut Builder;` ### Parameters - **position** (u8) - The position in the handshake where the PSK should be applied (0-9). - **psk** (&[u8; 32]) - A 32-byte slice representing the pre-shared key. ### Example ```rust use snow::Builder; static PSK: &[u8; 32] = b"super secret 32 byte passphrase!"; let builder = Builder::new("Noise_XXpsk3_25519_ChaChaPoly_BLAKE2s".parse()?) .psk(3, PSK)?; ``` ``` -------------------------------- ### HandshakeState::write_message / HandshakeState::read_message Source: https://context7.com/mcginty/snow/llms.txt Drives the handshake state machine. `write_message` serializes the next outbound handshake message, while `read_message` parses and processes an inbound handshake message. Both return the number of bytes written or read. ```APIDOC ## HandshakeState::write_message / HandshakeState::read_message ### Description Drive the handshake state machine. `write_message` serializes the next outbound handshake message (including ephemeral keys, static keys as required by the pattern, DH results, and an optional payload) into a buffer. `read_message` parses and processes an inbound handshake message. Both return the number of bytes written/read. ### Method Signatures `fn write_message(&mut self, payload: &[u8], buf: &mut [u8]) -> Result;` `fn read_message(&mut self, msg: &[u8], buf: &mut [u8]) -> Result;` ### Parameters - **payload** (&[u8]) - Optional payload to include in the message (for `write_message`). - **msg** (&[u8]) - The inbound handshake message to process (for `read_message`). - **buf** (&mut [u8]) - A mutable buffer to write the outbound message into (for `write_message`) or to write decrypted data into (for `read_message`). ### Return Value Returns the number of bytes written or read on success, or an `Error` on failure. ### Example ```rust use snow::Builder; let mut initiator = Builder::new("Noise_NN_25519_ChaChaPoly_BLAKE2s".parse()?) .build_initiator()?; let mut responder = Builder::new("Noise_NN_25519_ChaChaPoly_BLAKE2s".parse()?) .build_responder()?; let mut buf = [0u8; 65535]; let mut read_buf = [0u8; 65535]; // -> e (initiator sends ephemeral key) let msg1_len = initiator.write_message(&[], &mut buf)?; // responder reads it responder.read_message(&buf[..msg1_len], &mut read_buf)?; // <- e, ee (responder sends ephemeral key + DH result) let msg2_len = responder.write_message(&[], &mut buf)?; // initiator reads it — handshake is now complete for NN initiator.read_message(&buf[..msg2_len], &mut read_buf)?; ``` ``` -------------------------------- ### Recover from packet loss with TransportState::set_receiving_nonce Source: https://context7.com/mcginty/snow/llms.txt Manually sets the inbound nonce counter on a `TransportState` to recover from gaps in message delivery. Useful when using Noise over transports with selective acknowledgements, or when re-syncing after detecting packet loss. ```rust use snow::Builder; fn main() -> Result<(), Box> { let mut i = Builder::new("Noise_NN_25519_ChaChaPoly_SHA256".parse()?).build_initiator()?; let mut r = Builder::new("Noise_NN_25519_ChaChaPoly_SHA256".parse()?).build_responder()?; let (mut buf, mut rbuf) = ([0u8; 65535], [0u8; 65535]); let n = i.write_message(&[], &mut buf)?; r.read_message(&buf[..n], &mut rbuf)?; let n = r.write_message(&[], &mut buf)?; i.read_message(&buf[..n], &mut rbuf)?; let mut i_t = i.into_transport_mode()?; let mut r_t = r.into_transport_mode()?; // Initiator sends messages 0 and 1, but message 0 is "lost" let _n0 = i_t.write_message(b"lost packet 0", &mut buf)?; let n1 = i_t.write_message(b"received packet 1", &mut buf)?; // Responder knows nonce 1 was received next, skip nonce 0 r_t.set_receiving_nonce(1); let dec_len = r_t.read_message(&buf[..n1], &mut rbuf)?; println!("received: {}", std::str::from_utf8(&rbuf[..dec_len])?); println!("sending nonce: {}", i_t.sending_nonce()); // 2 println!("receiving nonce: {}", r_t.receiving_nonce()); // 2 Ok(()) } ``` -------------------------------- ### Access Channel-Binding Handshake Hash Source: https://context7.com/mcginty/snow/llms.txt Retrieves the final handshake hash after the handshake completes. This hash is identical for both parties if the handshake was successful and can be used for channel binding. ```rust use snow::Builder; fn main() -> Result<(), Box> { let mut i = Builder::new("Noise_NN_25519_ChaChaPoly_BLAKE2s".parse()?).build_initiator()?; let mut r = Builder::new("Noise_NN_25519_ChaChaPoly_BLAKE2s".parse()?).build_responder()?; let (mut buf, mut rbuf) = ([0u8; 65535], [0u8; 65535]); let n = i.write_message(&[], &mut buf)?; r.read_message(&buf[..n], &mut rbuf)?; let n = r.write_message(&[], &mut buf)?; i.read_message(&buf[..n], &mut rbuf)?; // Both sides must have the same handshake hash after completion let i_hash = i.get_handshake_hash().to_vec(); let r_hash = r.get_handshake_hash().to_vec(); assert_eq!(i_hash, r_hash); println!("channel binding hash: {}", hex::encode(&i_hash)); // Output: channel binding hash: <32-byte BLAKE2s hash as hex> Ok(()) } ``` -------------------------------- ### AEAD with Additional Authenticated Data (AAD) Source: https://context7.com/mcginty/snow/llms.txt Encrypts or decrypts data while also authenticating additional data using AEAD. The `authtext` must match exactly on both sides; otherwise, decryption fails with `Error::Decrypt`. This is available on both `TransportState` and `StatelessTransportState`. ```rust use snow::Builder; fn main() -> Result<(), Box> { // Setup: complete a NN handshake first let mut i = Builder::new("Noise_NN_25519_AESGCM_SHA256".parse()?).build_initiator()?; let mut r = Builder::new("Noise_NN_25519_AESGCM_SHA256".parse()?).build_responder()?; let (mut buf, mut rbuf) = ([0u8; 65535], [0u8; 65535]); let n = i.write_message(&[], &mut buf)?; r.read_message(&buf[..n], &mut rbuf)?; let n = r.write_message(&[], &mut buf)?; i.read_message(&buf[..n], &mut rbuf)?; let mut i_t = i.into_transport_mode()?; let mut r_t = r.into_transport_mode()?; let authtext = b"sequence:1"; // e.g. a packet header that must be authenticated let plaintext = b"secret payload"; // Encrypt with additional authenticated data let enc_len = i_t.write_message_with_additional_data(authtext, plaintext, &mut buf)?; // Decrypt — authtext must match exactly, otherwise Error::Decrypt let dec_len = r_t.read_message_with_additional_data(authtext, &buf[..enc_len], &mut rbuf)?; assert_eq!(&rbuf[..dec_len], plaintext); // Tampered authtext causes failure: let bad_result = r_t.read_message_with_additional_data(b"tampered", &buf[..enc_len], &mut rbuf); assert!(bad_result.is_err()); // Error::Decrypt Ok(()) } ``` -------------------------------- ### TransportState::rekey_outgoing / rekey_incoming / rekey_manually Source: https://context7.com/mcginty/snow/llms.txt Rotates the symmetric cipher keys according to Noise spec section 4.2. `rekey_outgoing` updates the send key, `rekey_incoming` updates the receive key. Both sides must rekey in sync. `rekey_manually` allows setting explicit key values for interoperability. ```APIDOC ## TransportState::rekey_outgoing / rekey_incoming / rekey_manually ### Description Rotates the symmetric cipher keys according to Noise spec section 4.2, replacing the current cipher key with a new one derived from the old key. `rekey_outgoing` updates the send key; `rekey_incoming` updates the receive key. Both sides must rekey in sync. `rekey_manually` allows setting explicit key values for interoperability. ### Method - `rekey_outgoing()` - `rekey_incoming()` - `rekey_manually()` ### Usage Example ```rust // Assuming i_t is an initiator's TransportState and r_t is a responder's TransportState i_t.rekey_outgoing(); // initiator rotates send key r_t.rekey_incoming(); // responder rotates receive key (must match) ``` ``` -------------------------------- ### HandshakeState::get_remote_static Source: https://context7.com/mcginty/snow/llms.txt Retrieves the remote party's authenticated public key once it has been transmitted and authenticated during the handshake. Returns `None` if the pattern does not involve a remote static key or if it has not yet been received. ```APIDOC ## HandshakeState::get_remote_static ### Description Retrieves the remote party's authenticated public key once it has been transmitted and authenticated during the handshake. Returns `None` if the pattern does not involve a remote static key or if it has not yet been received (e.g. during the first message of an `XX` handshake). Available on both `HandshakeState` and `TransportState`. ### Method - `get_remote_static()` ### Return Value - `Option>`: The remote party's static public key, or `None`. ### Usage Example ```rust // After a successful handshake let remote_s_at_initiator = initiator.get_remote_static().unwrap(); ``` ```