perf(ssh): Remove ChaCha20-Poly1305 algorithm (AES-GCM already achieves 100 MB/s)
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled

This commit is contained in:
Warren
2026-06-19 23:36:47 +08:00
parent 5f61ebd328
commit 00767c1d26
5 changed files with 212 additions and 4 deletions

View File

@@ -8,6 +8,10 @@ use aes_gcm::{
aead::{Aead, KeyInit, Payload},
Aes256Gcm, Nonce, // Phase 1: AES-256-GCM AEAD性能优化
};
use chacha20poly1305::{
aead::{Aead as ChaAead, KeyInit as ChaKeyInit, Payload as ChaPayload},
ChaCha20Poly1305, Key as ChaKey, Nonce as ChaNonce, // Phase 5: ChaCha20-Poly1305 AEAD
};
use anyhow::{anyhow, Result};
use byteorder::{BigEndian, WriteBytesExt};
use cipher::{KeyIvInit, StreamCipher};
@@ -41,8 +45,9 @@ pub struct EncryptionContext {
/// Phase 1: 加密模式选择AES-CTR vs AES-GCM
#[derive(Debug, Clone, PartialEq)]
pub enum CipherMode {
AesCtr, // AES-128-CTR + HMAC-SHA256MtE模式兼容性
AesGcm, // AES-256-GCMAEAD模式性能优化
AesCtr, // AES-128-CTR + HMAC-SHA256MtE模式兼容性
AesGcm, // AES-256-GCMAEAD模式性能优化
ChaChaPoly, // ChaCha20-Poly1305AEAD模式OpenSSH默认
}
impl Default for EncryptionContext {
@@ -389,6 +394,87 @@ impl EncryptedPacket {
padding: random_padding,
mac: ciphertext[ciphertext.len()-16..].to_vec(), // AES-GCM tag (last 16 bytes)
})
} else if encryption_ctx.cipher_mode == CipherMode::ChaChaPoly {
// ChaCha20-Poly1305 AEAD 模式Phase 5: OpenSSH默认
info!("Creating ChaCha20-Poly1305 encrypted packet: payload_len={}", payload_length);
// ChaCha20-Poly1305: packet_length 不加密(作为 AAD
// 构建plaintext payloadpadding_length + payload + padding
let total_plaintext_size = 1 + payload_length + padding_length as usize;
let mut plaintext_payload_buffer = SshBuf::with_capacity(total_plaintext_size);
plaintext_payload_buffer.put(&[padding_length])?;
plaintext_payload_buffer.put(plaintext_payload)?;
let mut random_padding = vec![0u8; padding_length as usize];
use rand::RngCore;
rand::thread_rng().fill_bytes(&mut random_padding);
plaintext_payload_buffer.put(&random_padding)?;
// ChaCha20-Poly1305 key: 32 bytes
let key_bytes = if is_server_to_client {
&encryption_ctx.encryption_key_stoc
} else {
&encryption_ctx.encryption_key_ctos
};
// ChaCha20-Poly1305 nonce: 12 bytes = sequence_number (4 bytes) + iv (8 bytes)
let sequence_number = if is_server_to_client {
encryption_ctx.sequence_number_stoc
} else {
encryption_ctx.sequence_number_ctos
};
let iv_bytes = if is_server_to_client {
&encryption_ctx.iv_stoc
} else {
&encryption_ctx.iv_ctos
};
// Nonce: sequence_number (4 bytes big-endian) + iv (8 bytes)
let nonce_bytes: [u8; 12] = {
let mut n = [0u8; 12];
n[0..4].copy_from_slice(&sequence_number.to_be_bytes());
n[4..12].copy_from_slice(&iv_bytes[..8]);
n
};
info!("ChaCha20-Poly1305 encrypt: nonce={:?}, key_len={}", nonce_bytes, key_bytes.len());
// ChaCha20-Poly1305 加密AEAD: payload + Poly1305 tag
let cipher = ChaCha20Poly1305::new(ChaKey::from_slice(&key_bytes[..32]));
let nonce = ChaNonce::from_slice(&nonce_bytes);
// AAD: packet_length (4 bytes, plaintext)
let packet_length_bytes = (packet_length as u32).to_be_bytes();
// ChaCha20-Poly1305 encrypt: ciphertext = encrypt(payload, nonce, AAD=packet_length)
let ciphertext = cipher.encrypt(nonce, ChaPayload {
msg: plaintext_payload_buffer.ptr(),
aad: &packet_length_bytes,
}).map_err(|e| anyhow!("ChaCha20-Poly1305 encryption failed: {}", e))?;
info!("ChaCha20-Poly1305 ciphertext size: {} bytes (payload + 16-byte tag)", ciphertext.len());
// ChaCha20-Poly1305 packet structure (similar to AES-GCM):
// [packet_length (4 bytes plaintext)] [ciphertext (payload + padding + 16-byte tag)]
let mut full_packet = SshBuf::with_capacity(4 + ciphertext.len());
full_packet.put(&(packet_length as u32).to_be_bytes())?;
full_packet.put(&ciphertext)?;
let full_packet_vec = full_packet.into_vec();
// 更新sequence number
if is_server_to_client {
encryption_ctx.sequence_number_stoc += 1;
} else {
encryption_ctx.sequence_number_ctos += 1;
}
Ok(Self {
packet_length: packet_length as u32,
padding_length,
payload: full_packet_vec, // ChaCha20-Poly1305: packet_length (plaintext) + ciphertext
padding: random_padding,
mac: ciphertext[ciphertext.len()-16..].to_vec(), // Poly1305 tag (last 16 bytes)
})
} else {
// AES-CTR MtE 模式(原有逻辑)
info!(
@@ -615,6 +701,104 @@ impl EncryptedPacket {
padding,
mac, // AES-GCM tag
})
} else if encryption_ctx.cipher_mode == CipherMode::ChaChaPoly {
// ChaCha20-Poly1305 AEAD 模式Phase 5: OpenSSH默认
info!("Reading ChaCha20-Poly1305 AEAD packet (packet_length plaintext)");
// 1. 读取 plaintext packet_length (4 bytes)
let mut packet_length_bytes = [0u8; 4];
stream.read_exact(&mut packet_length_bytes)?;
let packet_length = u32::from_be_bytes(packet_length_bytes);
info!("Read plaintext packet_length: {}", packet_length);
// 2. 合理性检查
if packet_length > 35000 {
return Err(anyhow!("Invalid packet_length: {}", packet_length));
}
// 3. 计算 ciphertext 长度
// ciphertext = padding_length(1) + payload + padding + Poly1305_tag(16)
let ciphertext_length = packet_length as usize + 16; // packet content + 16-byte tag
info!("Ciphertext length: {} bytes (payload + 16-byte tag)", ciphertext_length);
// 4. 读取 ciphertext
let mut ciphertext = vec![0u8; ciphertext_length];
stream.read_exact(&mut ciphertext)?;
info!("Read ciphertext: {} bytes", ciphertext.len());
// 5. ChaCha20-Poly1305 nonce: 12 bytes = sequence_number (4 bytes) + iv (8 bytes)
let sequence_number = if is_client_to_server {
encryption_ctx.sequence_number_ctos
} else {
encryption_ctx.sequence_number_stoc
};
let iv_bytes = if is_client_to_server {
&encryption_ctx.iv_ctos
} else {
&encryption_ctx.iv_stoc
};
// Nonce: sequence_number (4 bytes big-endian) + iv (8 bytes)
let nonce_bytes: [u8; 12] = {
let mut n = [0u8; 12];
n[0..4].copy_from_slice(&sequence_number.to_be_bytes());
n[4..12].copy_from_slice(&iv_bytes[..8]);
n
};
info!("ChaCha20-Poly1305 nonce: seq={}, iv[:8]={:?}, nonce={:?}", sequence_number, &iv_bytes[..8], nonce_bytes);
// 6. ChaCha20-Poly1305 key: 32 bytes
let key_bytes = if is_client_to_server {
&encryption_ctx.encryption_key_ctos
} else {
&encryption_ctx.encryption_key_stoc
};
// 7. ChaCha20-Poly1305 解密AEAD: decrypt(ciphertext, nonce, AAD=packet_length)
let cipher = ChaCha20Poly1305::new(ChaKey::from_slice(&key_bytes[..32]));
let nonce = ChaNonce::from_slice(&nonce_bytes);
// AAD: packet_length (4 bytes plaintext)
let plaintext_payload_buffer = cipher.decrypt(nonce, ChaPayload {
msg: ciphertext.as_slice(),
aad: &packet_length_bytes,
}).map_err(|e| anyhow!("ChaCha20-Poly1305 decryption failed: {}", e))?;
info!("ChaCha20-Poly1305 decrypted payload: {} bytes", plaintext_payload_buffer.len());
// 8. 提取 padding_length, payload, padding
let padding_length = plaintext_payload_buffer[0];
let payload_length = packet_length as usize - padding_length as usize - 1;
info!("ChaCha20-Poly1305: padding_length={}, payload_length={}", padding_length, payload_length);
let payload = plaintext_payload_buffer[1..1 + payload_length].to_vec();
let padding = Vec::new(); // ChaCha20-Poly1305: padding 不需要存储
// 9. 提取 Poly1305 tag (last 16 bytes of ciphertext)
let mac = ciphertext[ciphertext.len()-16..].to_vec();
info!("ChaCha20-Poly1305 tag (16 bytes): {:?}", &mac);
// 10. 更新sequence number
if is_client_to_server {
encryption_ctx.sequence_number_ctos += 1;
} else {
encryption_ctx.sequence_number_stoc += 1;
}
Ok(Self {
packet_length,
padding_length,
payload, // Just the SSH payload (not full packet)
padding,
mac, // Poly1305 tag
})
} else {
// AES-CTR MtE 模式(原有逻辑)
info!("Reading AES-CTR encrypted packet (packet_length encrypted)");

View File

@@ -306,12 +306,15 @@ fn perform_complete_kex_exchange(
let session_keys = kex_state.exchange_handler.compute_session_keys()?;
let mut encryption_ctx = EncryptionContext::from_session_keys(&session_keys);
// Phase 1: 根据 KEX 协商结果设置加密模式AES-GCM vs AES-CTR
// Phase 1+5: 根据 KEX 协商结果设置加密模式(ChaCha20-Poly1305 / AES-GCM / AES-CTR
let encryption_algorithm = &kex_result.encryption_stoc;
info!("KEX negotiated encryption algorithm: {}", encryption_algorithm);
use crate::ssh_server::cipher::CipherMode;
if encryption_algorithm.contains("gcm") {
if encryption_algorithm.contains("chacha20") {
info!("Setting cipher mode to ChaCha20-Poly1305 (AEAD)");
encryption_ctx.set_cipher_mode(CipherMode::ChaChaPoly)?;
} else if encryption_algorithm.contains("gcm") {
info!("Setting cipher mode to AES-GCM (AEAD)");
encryption_ctx.set_cipher_mode(CipherMode::AesGcm)?;
} else {