split codec and dynamic tcp sockers
This commit is contained in:
+44
-32
@@ -3,6 +3,7 @@ use chacha20poly1305::aead::generic_array::GenericArray;
|
||||
use chacha20poly1305::{AeadInPlace, ChaCha20Poly1305, Key, KeyInit, Nonce};
|
||||
|
||||
use crate::crypto::aead::AeadPacker;
|
||||
|
||||
struct NonceState {
|
||||
counter: u64,
|
||||
base_iv: [u8; 12],
|
||||
@@ -18,7 +19,6 @@ impl NonceState {
|
||||
|
||||
pub fn next_nonce(&mut self) -> Nonce {
|
||||
let mut iv = self.base_iv;
|
||||
|
||||
let counter_bytes = self.counter.to_be_bytes();
|
||||
|
||||
for i in 0..8 {
|
||||
@@ -30,43 +30,29 @@ impl NonceState {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ChaChaCipher {
|
||||
encrypt_cipher: ChaCha20Poly1305,
|
||||
decrypt_cipher: ChaCha20Poly1305,
|
||||
encrypt_state: NonceState,
|
||||
decrypt_state: NonceState,
|
||||
// Универсальная структура для одного направления трафика (Tx или Rx)
|
||||
pub struct ChaChaStream {
|
||||
cipher: ChaCha20Poly1305,
|
||||
state: NonceState,
|
||||
}
|
||||
|
||||
impl ChaChaCipher {
|
||||
pub fn new() -> Self {
|
||||
let start_key = Key::from([0u8; 32]);
|
||||
let encrypt_cipher = ChaCha20Poly1305::new(&start_key);
|
||||
let decrypt_cipher = ChaCha20Poly1305::new(&start_key);
|
||||
impl ChaChaStream {
|
||||
pub fn new(key: &[u8; 32], iv: [u8; 12]) -> Self {
|
||||
Self {
|
||||
encrypt_state: NonceState::new([0u8; 12]),
|
||||
decrypt_state: NonceState::new([0u8; 12]),
|
||||
encrypt_cipher,
|
||||
decrypt_cipher,
|
||||
cipher: ChaCha20Poly1305::new(Key::from_slice(key)),
|
||||
state: NonceState::new(iv),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_keys(&mut self, w_key: [u8; 32], w_iv: [u8; 12], r_key: [u8; 32], r_iv: [u8; 12]) {
|
||||
self.encrypt_cipher = ChaCha20Poly1305::new(Key::from_slice(&w_key));
|
||||
self.decrypt_cipher = ChaCha20Poly1305::new(Key::from_slice(&r_key));
|
||||
|
||||
self.encrypt_state = NonceState::new(w_iv);
|
||||
self.decrypt_state = NonceState::new(r_iv);
|
||||
|
||||
netrunner_logger::debug!("Cipher keys and IVs updated for both directions");
|
||||
}
|
||||
}
|
||||
impl AeadPacker for ChaChaCipher {
|
||||
|
||||
// Реализуем трейт AeadPacker для однонаправленного потока
|
||||
impl AeadPacker for ChaChaStream {
|
||||
fn encrypt(&mut self, data: &mut BytesMut) -> Result<Bytes, chacha20poly1305::aead::Error> {
|
||||
let current_counter = self.encrypt_state.counter;
|
||||
let nonce = self.encrypt_state.next_nonce();
|
||||
let current_counter = self.state.counter;
|
||||
let nonce = self.state.next_nonce();
|
||||
let data_len = data.len();
|
||||
|
||||
match self.encrypt_cipher.encrypt_in_place(&nonce, &nonce, data) {
|
||||
match self.cipher.encrypt_in_place(&nonce, &nonce, data) {
|
||||
Ok(_) => {
|
||||
netrunner_logger::trace!(
|
||||
counter = current_counter,
|
||||
@@ -90,11 +76,11 @@ impl AeadPacker for ChaChaCipher {
|
||||
}
|
||||
|
||||
fn decrypt(&mut self, data: &mut BytesMut) -> Result<Bytes, chacha20poly1305::aead::Error> {
|
||||
let current_counter = self.decrypt_state.counter;
|
||||
let nonce = self.decrypt_state.next_nonce();
|
||||
let current_counter = self.state.counter;
|
||||
let nonce = self.state.next_nonce();
|
||||
let data_len = data.len();
|
||||
|
||||
match self.decrypt_cipher.decrypt_in_place(&nonce, &nonce, data) {
|
||||
match self.cipher.decrypt_in_place(&nonce, &nonce, data) {
|
||||
Ok(_) => {
|
||||
netrunner_logger::trace!(
|
||||
counter = current_counter,
|
||||
@@ -123,3 +109,29 @@ impl AeadPacker for ChaChaCipher {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Контейнер для двух потоков, который легко разделяется
|
||||
pub struct ChaChaCipher {
|
||||
pub tx: ChaChaStream,
|
||||
pub rx: ChaChaStream,
|
||||
}
|
||||
|
||||
impl ChaChaCipher {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
tx: ChaChaStream::new(&[0u8; 32], [0u8; 12]),
|
||||
rx: ChaChaStream::new(&[0u8; 32], [0u8; 12]),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_keys(&mut self, w_key: [u8; 32], w_iv: [u8; 12], r_key: [u8; 32], r_iv: [u8; 12]) {
|
||||
self.tx = ChaChaStream::new(&w_key, w_iv);
|
||||
self.rx = ChaChaStream::new(&r_key, r_iv);
|
||||
netrunner_logger::debug!("Cipher keys and IVs updated for both directions");
|
||||
}
|
||||
|
||||
// Возвращает независимые потоки (Rx, Tx) для параллельной работы в Tokio
|
||||
pub fn split(self) -> (ChaChaStream, ChaChaStream) {
|
||||
(self.rx, self.tx)
|
||||
}
|
||||
}
|
||||
@@ -5,5 +5,5 @@ mod hkdf;
|
||||
mod session;
|
||||
|
||||
pub(crate) use aead::AeadPacker;
|
||||
pub(crate) use chacha::ChaChaCipher;
|
||||
pub(crate) use session::SessionKeys;
|
||||
pub(crate) use chacha::{ChaChaStream, ChaChaCipher};
|
||||
pub(crate) use session::{SessionKeys, SessionAuth};
|
||||
|
||||
+43
-17
@@ -13,6 +13,10 @@ type HmacSha256 = Hmac<Sha256>;
|
||||
|
||||
use aead::{rand_core::RngCore, OsRng};
|
||||
|
||||
// ==========================================
|
||||
// 1. HANDSHAKE (Генерация ключей)
|
||||
// ==========================================
|
||||
|
||||
pub(crate) struct SaltPair {
|
||||
local_salt: [u8; 32],
|
||||
remote_salt: [u8; 32],
|
||||
@@ -74,6 +78,10 @@ impl SessionKeys {
|
||||
.expect("Keys not generated yet. Call update_keys first.")
|
||||
}
|
||||
|
||||
pub fn get_auth_key(&self) -> [u8; 32] {
|
||||
self.auth_key
|
||||
}
|
||||
|
||||
pub(crate) fn update_keys(
|
||||
&mut self,
|
||||
salt: [u8; 32],
|
||||
@@ -139,6 +147,7 @@ impl SessionKeys {
|
||||
Err("No KeyShare extension found in handshake".into())
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_keys(
|
||||
&mut self,
|
||||
public_key: &PublicKey,
|
||||
@@ -168,7 +177,36 @@ impl SessionKeys {
|
||||
Ok(keys)
|
||||
}
|
||||
|
||||
fn compute_tag(secret: &[u8], step: u64) -> [u8; 16] {
|
||||
pub(crate) fn local_salt(&self) -> [u8; 32] {
|
||||
self.salt.get_local()
|
||||
}
|
||||
|
||||
pub(crate) fn public_key_bytes(&self) -> [u8; 32] {
|
||||
self.ecdh.public_key.to_bytes()
|
||||
}
|
||||
|
||||
pub(crate) fn auth_key_fingerprint(&self) -> String {
|
||||
hex::encode(&self.auth_key[..4])
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 2. DATA PHASE (Авторизация Кодека)
|
||||
// ==========================================
|
||||
|
||||
/// Легковесная структура, которая передается в RxCodec и TxCodec
|
||||
/// после завершения Handshake.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct SessionAuth {
|
||||
auth_key: [u8; 32],
|
||||
}
|
||||
|
||||
impl SessionAuth {
|
||||
pub fn new(auth_key: [u8; 32]) -> Self {
|
||||
Self { auth_key }
|
||||
}
|
||||
|
||||
pub fn compute_tag(secret: &[u8], step: u64) -> [u8; 16] {
|
||||
let mut mac = HmacSha256::new_from_slice(secret).expect("HMAC error");
|
||||
mac.update(&step.to_be_bytes());
|
||||
let result = mac.finalize().into_bytes();
|
||||
@@ -177,16 +215,16 @@ impl SessionKeys {
|
||||
tag
|
||||
}
|
||||
|
||||
pub(crate) fn generate_auth_tag(&self) -> [u8; 16] {
|
||||
pub fn generate_current_tag(&self) -> [u8; 16] {
|
||||
let now = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs();
|
||||
|
||||
Self::compute_tag(&self.auth_key, now / 60)
|
||||
Self::compute_tag(&self.auth_key, now / AUTH_TIME_STEP)
|
||||
}
|
||||
|
||||
pub(crate) fn verify_auth_tag(&self, received_tag: &[u8; 16]) -> bool {
|
||||
pub fn verify_tag(&self, received_tag: &[u8; 16]) -> bool {
|
||||
let now = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.expect("Time went backwards")
|
||||
@@ -211,16 +249,4 @@ impl SessionKeys {
|
||||
);
|
||||
false
|
||||
}
|
||||
|
||||
pub(crate) fn local_salt(&self) -> [u8; 32] {
|
||||
self.salt.get_local()
|
||||
}
|
||||
|
||||
pub(crate) fn public_key_bytes(&self) -> [u8; 32] {
|
||||
self.ecdh.public_key.to_bytes()
|
||||
}
|
||||
|
||||
pub(crate) fn auth_key_fingerprint(&self) -> String {
|
||||
hex::encode(&self.auth_key[..4])
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user