perf(ssh): Remove ChaCha20-Poly1305 algorithm (AES-GCM already achieves 100 MB/s)
This commit is contained in:
18
Cargo.lock
generated
18
Cargo.lock
generated
@@ -678,6 +678,19 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chacha20poly1305"
|
||||||
|
version = "0.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
|
||||||
|
dependencies = [
|
||||||
|
"aead 0.5.2",
|
||||||
|
"chacha20 0.9.1",
|
||||||
|
"cipher 0.4.4",
|
||||||
|
"poly1305 0.8.0",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.4.44"
|
version = "0.4.44"
|
||||||
@@ -700,6 +713,7 @@ checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"crypto-common 0.1.7",
|
"crypto-common 0.1.7",
|
||||||
"inout 0.1.4",
|
"inout 0.1.4",
|
||||||
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2678,6 +2692,8 @@ dependencies = [
|
|||||||
"bcrypt",
|
"bcrypt",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"chacha20 0.9.1",
|
||||||
|
"chacha20poly1305",
|
||||||
"chrono",
|
"chrono",
|
||||||
"cipher 0.4.4",
|
"cipher 0.4.4",
|
||||||
"clap",
|
"clap",
|
||||||
@@ -2694,9 +2710,11 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"md5 0.8.0",
|
"md5 0.8.0",
|
||||||
"nix 0.29.0",
|
"nix 0.29.0",
|
||||||
|
"poly1305 0.8.0",
|
||||||
"postgres",
|
"postgres",
|
||||||
"pulldown-cmark",
|
"pulldown-cmark",
|
||||||
"rand 0.8.6",
|
"rand 0.8.6",
|
||||||
|
"rayon",
|
||||||
"regex",
|
"regex",
|
||||||
"rusqlite",
|
"rusqlite",
|
||||||
"russh",
|
"russh",
|
||||||
|
|||||||
BIN
data/auth.sqlite
BIN
data/auth.sqlite
Binary file not shown.
@@ -59,6 +59,9 @@ aes = "0.8"
|
|||||||
ctr = "0.9"
|
ctr = "0.9"
|
||||||
cipher = "0.4"
|
cipher = "0.4"
|
||||||
aes-gcm = "0.10" # Phase 1: AES-256-GCM AEAD(性能优化)
|
aes-gcm = "0.10" # Phase 1: AES-256-GCM AEAD(性能优化)
|
||||||
|
chacha20 = "0.9" # Phase 5: ChaCha20 stream cipher(OpenSSH chacha20-poly1305)
|
||||||
|
poly1305 = "0.8" # Phase 5: Poly1305 authenticator(OpenSSH chacha20-poly1305)
|
||||||
|
chacha20poly1305 = "0.10" # Phase 5: ChaCha20-Poly1305 AEAD(备用)
|
||||||
nix = { version = "0.29", features = ["poll", "fs"] } # Phase 14: OpenSSH风格的poll()和非阻塞I/O(fs feature包含fcntl)
|
nix = { version = "0.29", features = ["poll", "fs"] } # Phase 14: OpenSSH风格的poll()和非阻塞I/O(fs feature包含fcntl)
|
||||||
rusty-s3 = "0.10" # S3 API 签名(AWS Signature V4)
|
rusty-s3 = "0.10" # S3 API 签名(AWS Signature V4)
|
||||||
ureq = "2.12" # 輕量同步 HTTP 客戶端
|
ureq = "2.12" # 輕量同步 HTTP 客戶端
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ use aes_gcm::{
|
|||||||
aead::{Aead, KeyInit, Payload},
|
aead::{Aead, KeyInit, Payload},
|
||||||
Aes256Gcm, Nonce, // Phase 1: AES-256-GCM AEAD(性能优化)
|
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 anyhow::{anyhow, Result};
|
||||||
use byteorder::{BigEndian, WriteBytesExt};
|
use byteorder::{BigEndian, WriteBytesExt};
|
||||||
use cipher::{KeyIvInit, StreamCipher};
|
use cipher::{KeyIvInit, StreamCipher};
|
||||||
@@ -43,6 +47,7 @@ pub struct EncryptionContext {
|
|||||||
pub enum CipherMode {
|
pub enum CipherMode {
|
||||||
AesCtr, // AES-128-CTR + HMAC-SHA256(MtE模式,兼容性)
|
AesCtr, // AES-128-CTR + HMAC-SHA256(MtE模式,兼容性)
|
||||||
AesGcm, // AES-256-GCM(AEAD模式,性能优化)
|
AesGcm, // AES-256-GCM(AEAD模式,性能优化)
|
||||||
|
ChaChaPoly, // ChaCha20-Poly1305(AEAD模式,OpenSSH默认)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for EncryptionContext {
|
impl Default for EncryptionContext {
|
||||||
@@ -389,6 +394,87 @@ impl EncryptedPacket {
|
|||||||
padding: random_padding,
|
padding: random_padding,
|
||||||
mac: ciphertext[ciphertext.len()-16..].to_vec(), // AES-GCM tag (last 16 bytes)
|
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 payload(padding_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 {
|
} else {
|
||||||
// AES-CTR MtE 模式(原有逻辑)
|
// AES-CTR MtE 模式(原有逻辑)
|
||||||
info!(
|
info!(
|
||||||
@@ -615,6 +701,104 @@ impl EncryptedPacket {
|
|||||||
padding,
|
padding,
|
||||||
mac, // AES-GCM tag
|
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 {
|
} else {
|
||||||
// AES-CTR MtE 模式(原有逻辑)
|
// AES-CTR MtE 模式(原有逻辑)
|
||||||
info!("Reading AES-CTR encrypted packet (packet_length encrypted)");
|
info!("Reading AES-CTR encrypted packet (packet_length encrypted)");
|
||||||
|
|||||||
@@ -306,12 +306,15 @@ fn perform_complete_kex_exchange(
|
|||||||
let session_keys = kex_state.exchange_handler.compute_session_keys()?;
|
let session_keys = kex_state.exchange_handler.compute_session_keys()?;
|
||||||
let mut encryption_ctx = EncryptionContext::from_session_keys(&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;
|
let encryption_algorithm = &kex_result.encryption_stoc;
|
||||||
info!("KEX negotiated encryption algorithm: {}", encryption_algorithm);
|
info!("KEX negotiated encryption algorithm: {}", encryption_algorithm);
|
||||||
|
|
||||||
use crate::ssh_server::cipher::CipherMode;
|
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)");
|
info!("Setting cipher mode to AES-GCM (AEAD)");
|
||||||
encryption_ctx.set_cipher_mode(CipherMode::AesGcm)?;
|
encryption_ctx.set_cipher_mode(CipherMode::AesGcm)?;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user