From 0af6182a9abb10a90dada2fcc775abddcdaef68c Mon Sep 17 00:00:00 2001 From: RJ Rybarczyk Date: Thu, 3 Oct 2024 10:33:56 -0400 Subject: [PATCH] Encrypted + unencrypted codec examples --- protocols/v2/codec-sv2/Cargo.toml | 5 +- protocols/v2/codec-sv2/examples/encrypted.rs | 167 ++++++++++++++++++ .../v2/codec-sv2/examples/unencrypted.rs | 123 +++++++++++++ 3 files changed, 293 insertions(+), 2 deletions(-) create mode 100644 protocols/v2/codec-sv2/examples/encrypted.rs create mode 100644 protocols/v2/codec-sv2/examples/unencrypted.rs diff --git a/protocols/v2/codec-sv2/Cargo.toml b/protocols/v2/codec-sv2/Cargo.toml index 20b0288afa..3e8b1e2eae 100644 --- a/protocols/v2/codec-sv2/Cargo.toml +++ b/protocols/v2/codec-sv2/Cargo.toml @@ -16,9 +16,10 @@ const_sv2 = { version = "2.0.0", path = "../../../protocols/v2/const-sv2"} buffer_sv2 = { version = "1.0.0", path = "../../../utils/buffer"} tracing = { version = "0.1"} - +[dev-dependencies] +key-utils = { version = "^1.0.0", path = "../../../utils/key-utils" } [features] with_serde = ["binary_sv2/with_serde", "serde", "framing_sv2/with_serde", "buffer_sv2/with_serde"] with_buffer_pool = ["framing_sv2/with_buffer_pool"] -no_std = [] \ No newline at end of file +no_std = [] diff --git a/protocols/v2/codec-sv2/examples/encrypted.rs b/protocols/v2/codec-sv2/examples/encrypted.rs new file mode 100644 index 0000000000..51282c7b5e --- /dev/null +++ b/protocols/v2/codec-sv2/examples/encrypted.rs @@ -0,0 +1,167 @@ +use binary_sv2::{binary_codec_sv2, Deserialize, Serialize}; +use codec_sv2::{ + HandshakeRole, Initiator, NoiseEncoder, Responder, StandardEitherFrame, StandardNoiseDecoder, + State, Sv2Frame, +}; +use const_sv2::{ + INITIATOR_EXPECTED_HANDSHAKE_MESSAGE_SIZE, RESPONDER_EXPECTED_HANDSHAKE_MESSAGE_SIZE, +}; +use key_utils::{Secp256k1PublicKey, Secp256k1SecretKey}; +use std::{ + convert::TryInto, + io::{Read, Write}, + net::{TcpListener, TcpStream}, +}; + +// Arbitrary message type. +// Supported Sv2 message types are listed in the [Sv2 Spec Message +// Types](https://github.com/stratum-mining/sv2-spec/blob/main/08-Message-Types.md). +const CUSTOM_MSG_TYPE: u8 = 0xff; + +// Emulate a TCP connection +const TCP_ADDR: &str = "127.0.0.1:3333"; +const RECEIVER_PUBLIC_K: &str = "9auqWEzQDVyd2oe1JVGFLMLHZtCo2FFqZwtKA5gd9xbuEu7PH72"; +const RECEIVER_PRIVATE_K: &str = "mkDLTBBRxdBv998612qipDYoTK3YUrqLe8uWw7gu3iXbSrn2n"; +const RECEIVER_CERT_VALIDITY: std::time::Duration = std::time::Duration::from_secs(3600); + +// Example message type. +// In practice, all Sv2 messages are defined in the following crates: +// * `common_messages_sv2` +// * `mining_sv2` +// * `job_declaration_sv2` +// * `template_distribution_sv2` +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct CustomMessage { + nonce: u16, +} + +fn main() { + // Start a receiving listener + let listener_receiver = TcpListener::bind(TCP_ADDR).expect("Failed to bind TCP listener"); + + // Start a sender stream + let mut stream_sender: TcpStream = + TcpStream::connect(TCP_ADDR).expect("Failed to connect to TCP stream"); + + // Start a receiver stream + let mut stream_receiver: TcpStream = listener_receiver + .incoming() + .next() + .expect("Failed to accept incoming TCP stream") + .expect("Failed to connect to incoming TCP stream"); + + // Handshake + let receiver_public_k: Secp256k1PublicKey = RECEIVER_PUBLIC_K + .to_string() + .try_into() + .expect("Failed to convert receiver public key to Secp256k1PublicKey"); + + let receiver_private_k: Secp256k1SecretKey = RECEIVER_PRIVATE_K + .to_string() + .try_into() + .expect("Failed to convert receiver private key to Secp256k1PublicKey"); + + let initiator = Initiator::from_raw_k(receiver_public_k.into_bytes()) + .expect("Failed to create initiator role from raw pub key"); + + let responder = Responder::from_authority_kp( + &receiver_public_k.into_bytes(), + &receiver_private_k.into_bytes(), + RECEIVER_CERT_VALIDITY, + ) + .expect("Failed to initialize responder from pub/key pair and/or cert"); + + let mut sender_state = codec_sv2::State::initialized(HandshakeRole::Initiator(initiator)); + let mut receiver_state = codec_sv2::State::initialized(HandshakeRole::Responder(responder)); + + let first_message = sender_state + .step_0() + .expect("Initiator failed first step of handshake"); + let first_message: [u8; RESPONDER_EXPECTED_HANDSHAKE_MESSAGE_SIZE] = first_message + .get_payload_when_handshaking() + .try_into() + .expect("Handshake remote invlaid message"); + + let (second_message, receiver_state) = receiver_state + .step_1(first_message) + .expect("Responder failed second step of handshake"); + let second_message: [u8; INITIATOR_EXPECTED_HANDSHAKE_MESSAGE_SIZE] = second_message + .get_payload_when_handshaking() + .try_into() + .expect("Handshake remote invlaid message"); + + let sender_state = sender_state + .step_2(second_message) + .expect("Initiator failed third step of handshake"); + + let mut sender_state = match sender_state { + State::Transport(c) => State::with_transport_mode(c), + _ => panic!("todo"), + }; + + let mut receiver_state = match receiver_state { + State::Transport(c) => State::with_transport_mode(c), + _ => panic!("todo"), + }; + + // Create a message + let nonce = 1337; + let msg = CustomMessage { nonce }; + let msg_type = CUSTOM_MSG_TYPE; + // Unique identifier of the extension describing the protocol message, as defined by Sv2 Framing + let extension_type = 0; + // This message is intended for the receiver, so set to false + let channel_msg = false; + + let frame = StandardEitherFrame::::Sv2( + Sv2Frame::from_message(msg, msg_type, extension_type, channel_msg) + .expect("Failed to create the frame"), + ); + + let mut encoder = NoiseEncoder::::new(); + let encoded_frame = encoder + .encode(frame, &mut sender_state) + .expect("Failed to encode the frame"); + + // Send the encoded frame + stream_sender + .write_all(encoded_frame.as_slice()) + .expect("Failed to send the encoded frame"); + + // Receiver side + let mut decoder = StandardNoiseDecoder::::new(); + let decoder_buf = decoder.writable(); + // Read the frame header into the decoder buffer + stream_receiver + .read_exact(decoder_buf) + .expect("Failed to read the encoded frame header"); + + // This returns a `MissingBytes` error because it only reads the header as there is no payload + // in memory yet. Therefore, we safely ignore the error as the important thing here is that we + // loaded the `decoder.missing_b` with the expected frame payload size + let _ = decoder.next_frame(&mut receiver_state); + + // Now the decoder buffer has the expected size of the frame payload + let decoder_buf = decoder.writable(); + + // Read the payload into the decoder buffer + stream_receiver + .read_exact(decoder_buf) + .expect("Failed to read the encoded frame payload"); + + // INCORRECT PANIC HERE (DO NOT MERGE): + // Decode the frame + let _decoded_frame = decoder + .next_frame(&mut receiver_state) + .expect("Failed to decode the frame"); + + // let decoded_frame_header = decoded_frame + // .get_header() + // .expect("Failed to get the frame header"); + // let decoded_msg: CustomMessage = binary_sv2::from_bytes(decoded_frame.payload()) + // .expect("Failed to extract the message from the payload"); + // + // // Assert that the decoded message is as expected + // assert_eq!(decoded_frame_header.msg_type(), CUSTOM_MSG_TYPE); + // assert_eq!(decoded_msg.nonce, nonce); +} diff --git a/protocols/v2/codec-sv2/examples/unencrypted.rs b/protocols/v2/codec-sv2/examples/unencrypted.rs new file mode 100644 index 0000000000..a38a9a71dd --- /dev/null +++ b/protocols/v2/codec-sv2/examples/unencrypted.rs @@ -0,0 +1,123 @@ +use binary_sv2::{binary_codec_sv2, Deserialize, Serialize}; +use codec_sv2::{Encoder, StandardDecoder, StandardSv2Frame, Sv2Frame}; +use std::{ + convert::TryInto, + io::{Read, Write}, + net::{TcpListener, TcpStream}, +}; + +// Arbitrary message type. +// Supported Sv2 message types are listed in the [Sv2 Spec Message +// Types](https://github.com/stratum-mining/sv2-spec/blob/main/08-Message-Types.md). +const CUSTOM_MSG_TYPE: u8 = 0xff; + +// Emulate a TCP connection +const TCP_ADDR: &str = "127.0.0.1:3333"; + +// Example message type. +// In practice, all Sv2 messages are defined in the following crates: +// * `common_messages_sv2` +// * `mining_sv2` +// * `job_declaration_sv2` +// * `template_distribution_sv2` +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct CustomMessage { + nonce: u16, +} + +fn main() { + // Start a receiving listener + let listener_receiver = TcpListener::bind(TCP_ADDR).expect("Failed to bind TCP listener"); + + // Start a sender stream + let stream_sender: TcpStream = + TcpStream::connect(TCP_ADDR).expect("Failed to connect to TCP stream"); + + // Start a receiver stream + let stream_receiver: TcpStream = listener_receiver + .incoming() + .next() + .expect("Failed to accept incoming TCP stream") + .expect("Failed to connect to incoming TCP stream"); + + // Server Side + + // Create a message + let nonce = 1337; + let msg = CustomMessage { nonce }; + let msg_type = CUSTOM_MSG_TYPE; + // Unique identifier of the extension describing the protocol message, as defined by Sv2 Framing + let extension_type = 0; + // This message is intended for the receiver, so set to false + let channel_msg = false; + sender_side(stream_sender, msg, msg_type, extension_type, channel_msg); + + // Receiver Side + let mut decoded_frame = receiver_side(stream_receiver); + + let decoded_frame_header = decoded_frame + .get_header() + .expect("Failed to get the frame header"); + let decoded_msg: CustomMessage = binary_sv2::from_bytes(decoded_frame.payload()) + .expect("Failed to extract the message from the payload"); + + // Assert that the decoded message is as expected + assert_eq!(decoded_frame_header.msg_type(), CUSTOM_MSG_TYPE); + assert_eq!(decoded_msg.nonce, nonce); +} + +fn sender_side( + mut stream_sender: TcpStream, + msg: CustomMessage, + msg_type: u8, + extension_type: u16, + channel_msg: bool, +) { + // Create the frame + let frame = + StandardSv2Frame::::from_message(msg, msg_type, extension_type, channel_msg) + .expect("Failed to create the frame"); + + // Encode the frame + let mut encoder = Encoder::::new(); + let encoded_frame = encoder + .encode(frame.clone()) + .expect("Failed to encode the frame"); + + // Send the encoded frame + stream_sender + .write_all(encoded_frame) + .expect("Failed to send the encoded frame"); +} + +fn receiver_side(mut stream_receiver: TcpStream) -> Sv2Frame> { + // Initialize the decoder + let mut decoder = StandardDecoder::::new(); + + // At this point, the decoder buffer can only read a frame header because the + // decoder.missing_b is initialized with a header size + let decoder_buf = decoder.writable(); + + // Read the frame header into the decoder buffer + stream_receiver + .read_exact(decoder_buf) + .expect("Failed to read the encoded frame header"); + + // This returns a `MissingBytes` error because it only reads the header as there is no payload + // in memory yet. Therefore, we safely ignore the error as the important thing here is that we + // loaded the `decoder.missing_b` with the expected frame payload size + let _ = decoder.next_frame(); + + // Now the decoder buffer has the expected size of the frame payload + let decoder_buf = decoder.writable(); + + // Read the payload into the decoder buffer + stream_receiver + .read_exact(decoder_buf) + .expect("Failed to read the encoded frame payload"); + + // Decode the frame + let decoded_frame = decoder.next_frame().expect("Failed to decode the frame"); + + return decoded_frame; +}