Implement SSH Compression Phase 3: Actual packet compression
- EncryptedPacket::new(): compress payload before encryption - EncryptedPacket::read(): decompress payload after decryption - Apply to AES-GCM, ChaCha20-Poly1305, and AES-CTR modes - Compression order: compress → encrypt (write) - Decompression order: decrypt → decompress (read) All 179 tests pass.
This commit is contained in:
@@ -278,6 +278,7 @@ pub struct EncryptedPacket {
|
||||
impl EncryptedPacket {
|
||||
/// 创建加密packet(参考OpenSSH cipher.c)
|
||||
/// Phase 1: 支持 AES-CTR (MtE) 和 AES-GCM (AEAD) 两种模式
|
||||
/// Phase 3: 支持压缩(压缩发生在加密之前)
|
||||
pub fn new(
|
||||
plaintext_payload: &[u8],
|
||||
encryption_ctx: &mut EncryptionContext,
|
||||
@@ -285,8 +286,23 @@ impl EncryptedPacket {
|
||||
) -> Result<Self> {
|
||||
let block_size = 16;
|
||||
let min_padding = 4;
|
||||
|
||||
let payload_length = plaintext_payload.len();
|
||||
|
||||
// Phase 3: 压缩 payload(如果启用)
|
||||
// 压缩顺序:压缩 → 加密(参考 RFC 4253 §6.2)
|
||||
let compressed_payload = if is_server_to_client {
|
||||
// Server → Client: 使用 compression_stoc
|
||||
if encryption_ctx.compression_stoc.is_enabled() {
|
||||
info!("Compressing payload (server→client, {} bytes)", plaintext_payload.len());
|
||||
encryption_ctx.compression_stoc.compress(plaintext_payload)?
|
||||
} else {
|
||||
plaintext_payload.to_vec()
|
||||
}
|
||||
} else {
|
||||
// Client → Server: 使用 compression_ctos(server 解压缩,这里不压缩)
|
||||
plaintext_payload.to_vec()
|
||||
};
|
||||
|
||||
let payload_length = compressed_payload.len();
|
||||
|
||||
// Padding calculation:
|
||||
// AES-GCM: RFC 4253 body (padding_length + payload + padding = packet_length) must be % 16 == 0
|
||||
@@ -321,7 +337,7 @@ impl EncryptedPacket {
|
||||
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)?;
|
||||
plaintext_payload_buffer.put(&compressed_payload)?;
|
||||
|
||||
let mut random_padding = vec![0u8; padding_length as usize];
|
||||
use rand::RngCore;
|
||||
@@ -421,7 +437,7 @@ impl EncryptedPacket {
|
||||
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)?;
|
||||
plaintext_payload_buffer.put(&compressed_payload)?;
|
||||
|
||||
let mut random_padding = vec![0u8; padding_length as usize];
|
||||
use rand::RngCore;
|
||||
@@ -505,7 +521,7 @@ impl EncryptedPacket {
|
||||
let mut plaintext_packet = SshBuf::with_capacity(total_packet_size);
|
||||
plaintext_packet.put(&(packet_length as u32).to_be_bytes())?;
|
||||
plaintext_packet.put(&[padding_length])?;
|
||||
plaintext_packet.put(plaintext_payload)?;
|
||||
plaintext_packet.put(&compressed_payload)?;
|
||||
|
||||
let mut random_padding = vec![0u8; padding_length as usize];
|
||||
use rand::RngCore;
|
||||
@@ -705,7 +721,23 @@ impl EncryptedPacket {
|
||||
|
||||
info!("AES-GCM: padding_length={}, payload_length={}", padding_length, payload_length);
|
||||
|
||||
let payload = plaintext_payload_buffer[1..1 + payload_length].to_vec();
|
||||
let compressed_payload = plaintext_payload_buffer[1..1 + payload_length].to_vec();
|
||||
|
||||
// Phase 3: 解压缩 payload(如果启用)
|
||||
// 解压缩顺序:解密 → 解压缩(参考 RFC 4253 §6.2)
|
||||
let payload = if is_client_to_server {
|
||||
// Client → Server: 使用 compression_ctos
|
||||
if encryption_ctx.compression_ctos.is_enabled() {
|
||||
info!("Decompressing payload (client→server, {} bytes)", compressed_payload.len());
|
||||
encryption_ctx.compression_ctos.decompress(&compressed_payload)?
|
||||
} else {
|
||||
compressed_payload
|
||||
}
|
||||
} else {
|
||||
// Server → Client: 使用 compression_stoc(client 解压缩,这里不解压缩)
|
||||
compressed_payload
|
||||
};
|
||||
|
||||
let padding = Vec::new(); // AES-GCM: padding 不需要存储(write 时使用 payload 中的 ciphertext)
|
||||
|
||||
// 9. 提取 GCM tag (last 16 bytes of ciphertext)
|
||||
@@ -917,6 +949,23 @@ impl EncryptedPacket {
|
||||
let mut payload = Vec::with_capacity(payload_length);
|
||||
payload.extend_from_slice(payload_part1);
|
||||
payload.extend_from_slice(payload_part2);
|
||||
|
||||
// Phase 3: 解压缩 payload(如果启用)
|
||||
// 解压缩顺序:解密 → 解压缩(参考 RFC 4253 §6.2)
|
||||
let decompressed_payload = if is_client_to_server {
|
||||
// Client → Server: 使用 compression_ctos
|
||||
if encryption_ctx.compression_ctos.is_enabled() {
|
||||
info!("Decompressing payload (client→server, {} bytes)", payload.len());
|
||||
encryption_ctx.compression_ctos.decompress(&payload)?
|
||||
} else {
|
||||
payload
|
||||
}
|
||||
} else {
|
||||
// Server → Client: 使用 compression_stoc(client 解压缩,这里不解压缩)
|
||||
payload
|
||||
};
|
||||
|
||||
let payload = decompressed_payload;
|
||||
|
||||
// 提取padding(从remaining_encrypted的末尾)
|
||||
let padding = remaining_encrypted[payload_part2_len..].to_vec();
|
||||
|
||||
Reference in New Issue
Block a user