Skip to content

Commit

Permalink
Merge pull request #1111 from Shourya742/doc-noise-sv2
Browse files Browse the repository at this point in the history
Rust docs `noise_sv2`
  • Loading branch information
rrybarczyk authored Oct 14, 2024
2 parents 4e29548 + 1b23eb0 commit 3b4f448
Show file tree
Hide file tree
Showing 10 changed files with 819 additions and 116 deletions.
31 changes: 31 additions & 0 deletions protocols/v2/noise-sv2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

# noise_sv2

[![crates.io](https://img.shields.io/crates/v/const_sv2.svg)](https://crates.io/crates/const_sv2)
[![docs.rs](https://docs.rs/const_sv2/badge.svg)](https://docs.rs/const_sv2)
[![rustc+](https://img.shields.io/badge/rustc-1.75.0%2B-lightgrey.svg)](https://blog.rust-lang.org/2023/12/28/Rust-1.75.0.html)
[![license](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](https://github.com/stratum-mining/stratum/blob/main/LICENSE.md)
[![codecov](https://codecov.io/gh/stratum-mining/stratum/branch/main/graph/badge.svg?flag=noise_sv2-coverage)](https://codecov.io/gh/stratum-mining/stratum)

`noise_sv2` is primarily intended to secure communication in the Stratum V2 (Sv2) protocol. It handles the necessary Noise handshakes, encrypts outgoing messages, and decrypts incoming responses, ensuring privacy and integrity across the communication link between Sv2 roles. See the [Protocol Security specification](https://github.com/stratum-mining/sv2-spec/blob/main/04-Protocol-Security.md) for more details.

## Key Capabilities
* **Secure Communication**: Provides encryption and authentication for messages exchanged between different Sv2 roles.
* **Cipher Support**: Includes support for both `AES-GCM` and `ChaCha20-Poly1305`.
* **Handshake Roles**: Implements the `Initiator` and `Responder` roles required by the Noise handshake, allowing both sides of a connection to establish secure communication.
* **Cryptographic Helpers**: Facilitates the management of cryptographic state and encryption operations.

## Usage
To include this crate in your project, run:

```bash
cargo add noise_sv2
```

### Examples

This crate provides example on establishing a secure line:

1. **[Noise Handshake Example](https://github.com/stratum-mining/stratum/blob/main/protocols/v2/noise-sv2/examples/handshake.rs)**:
Establish a secure line of communication between an Initiator and Responder via the Noise
protocol, allowing for the encryption and decryption of a secret message.
68 changes: 68 additions & 0 deletions protocols/v2/noise-sv2/examples/handshake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// # Noise Protocol Handshake
//
// This example demonstrates how to use the `noise-sv2` crate to establish a Noise handshake
// between and initiator and responder, and encrypt and decrypt a secret message. It showcases how
// to:
//
// - Generate a cryptographic keypair using the `secp256k1` library.
// - Perform a Noise handshake between an initiator and responder.
// - Transition from handshake to secure communication mode.
// - Encrypt a message as the initiator role.
// - Decrypt the message as the responder role.
//
// ## Run
//
// ```sh
// cargo run --example handshake
// ```

use noise_sv2::{Initiator, Responder};
use secp256k1::{Keypair, Parity, Secp256k1};

// Even parity used in the Schnorr signature process
const PARITY: Parity = Parity::Even;
// Validity duration of the responder's certificate, seconds
const RESPONDER_CERT_VALIDITY: u32 = 3600;

// Generates a secp256k1 public/private key pair for the responder.
fn generate_key() -> Keypair {
let secp = Secp256k1::new();
let (secret_key, _) = secp.generate_keypair(&mut rand::thread_rng());
let kp = Keypair::from_secret_key(&secp, &secret_key);
if kp.x_only_public_key().1 == PARITY {
kp
} else {
generate_key()
}
}

fn main() {
let mut secret_message = "Ciao, Mondo!".as_bytes().to_vec();

let responder_key_pair = generate_key();

let mut initiator = Initiator::new(Some(responder_key_pair.public_key().into()));
let mut responder = Responder::new(responder_key_pair, RESPONDER_CERT_VALIDITY);

let first_message = initiator
.step_0()
.expect("Initiator failed first step of handshake");

let (second_message, mut responder_state) = responder
.step_1(first_message)
.expect("Responder failed second step of handshake");

let mut initiator_state = initiator
.step_2(second_message)
.expect("Initiator failed third step of handshake");

initiator_state
.encrypt(&mut secret_message)
.expect("Initiator failed to encrypt the secret message");
assert!(secret_message != "Ciao, Mondo!".as_bytes().to_vec());

responder_state
.decrypt(&mut secret_message)
.expect("Responder failed to decrypt the secret message");
assert!(secret_message == "Ciao, Mondo!".as_bytes().to_vec());
}
50 changes: 50 additions & 0 deletions protocols/v2/noise-sv2/src/aed_cipher.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,64 @@
// # AEAD Cipher
//
// Abstracts the encryption and decryption operations for authenticated encryption with associated
// data (AEAD) ciphers used in the Noise protocol.
//
// The [`AeadCipher`] trait provides a unified interface for AEAD ciphers, including
// [`ChaCha20Poly1305`] and [`Aes256Gcm`], allowing flexible cryptographic operations in different
// contexts.
//
// The trait supports core AEAD operations, including:
//
// - Key initialization via the `from_key` method to derive a cipher instance from a 32-byte key.
// - Authenticated encryption via the `encrypt` method to securely encrypt data with a nonce and
// additional associated data (AAD).
// - Authenticated decryption via the `decrypt` method to securely decrypt data using the provided
// nonce and AAD.
//
// ## Usage
//
// The `AeadCipher` trait can be implemented for any AEAD cipher, enabling encryption and decryption
// of Noise protocol messages. Two default implementations are provided for the
// [`ChaCha20Poly1305`] and [`Aes256Gcm`] ciphers.

use aes_gcm::Aes256Gcm;
use chacha20poly1305::{aead::Buffer, AeadInPlace, ChaCha20Poly1305, ChaChaPoly1305, KeyInit};

// Defines the interface for AEAD ciphers.
//
// The [`AeadCipher`] trait provides a standard interface for initializing AEAD ciphers, and for
// performing encryption and decryption operations with additional Authenticated Associated Data (AAD). This trait is implemented
// by either the [`ChaCha20Poly1305`] or [`Aes256Gcm`] specific cipher types, allowing them to be
// used interchangeably in cryptographic protocols. It is utilized by the
// [`crate::handshake::HandshakeOp`] trait to secure the handshake process.
//
// The `T: Buffer` represents the data buffer to be encrypted or decrypted. The buffer must
// implement the [`Buffer`] trait, which provides necessary operations for in-place encryption and
// decryption.
pub trait AeadCipher {
// Creates a new instance of the cipher from a 32-byte key.
//
// Initializes the AEAD cipher with the provided key (`k`), preparing it for
// encryption and decryption operations.
fn from_key(k: [u8; 32]) -> Self;

// Encrypts the data in place using the provided 12-byte `nonce` and AAD (`ad`).
//
// Performs authenticated encryption on the provided mutable data buffer (`data`), modifying
// it in place to contain the ciphertext. The encryption is performed using the provided nonce
// and AAD, which ensures that the data has not been tampered with during transit.
fn encrypt<T: Buffer>(
&mut self,
nonce: &[u8; 12],
ad: &[u8],
data: &mut T,
) -> Result<(), aes_gcm::Error>;

// Decrypts the data in place using the provided 12-byte nonce (`n`) and AAD (`ad`).
//
// Performs authenticated decryption on the provided mutable data buffer, modifying it in
// place to contain the plaintext. The decryption is performed using the provided nonce and
// AAD, ensuring that the data has not been tampered with during transit.
fn decrypt<T: Buffer>(
&mut self,
nonce: &[u8; 12],
Expand Down Expand Up @@ -47,6 +95,7 @@ impl AeadCipher for Aes256Gcm {
fn from_key(k: [u8; 32]) -> Self {
Aes256Gcm::new(&k.into())
}

fn encrypt<T: Buffer>(
&mut self,
nonce: &[u8; 12],
Expand All @@ -55,6 +104,7 @@ impl AeadCipher for Aes256Gcm {
) -> Result<(), aes_gcm::Error> {
self.encrypt_in_place(nonce.into(), ad, data)
}

fn decrypt<T: Buffer>(
&mut self,
nonce: &[u8; 12],
Expand Down
Loading

0 comments on commit 3b4f448

Please sign in to comment.