Implement Phase 1 AES-GCM packet processing: AEAD encryption/decryption
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled

Phase 1 complete implementation:
- AES-GCM AEAD encryption (EncryptedPacket::new)
- AES-GCM AEAD decryption (EncryptedPacket::read)
- AES-GCM packet structure: packet_length plaintext + ciphertext + 16-byte tag
- AES-GCM nonce: sequence_number (4 bytes -> 12 bytes)
- AES-CTR fallback preserved (MtE mode)

Key differences AES-GCM vs AES-CTR:
- AES-GCM: packet_length is plaintext (as AAD)
- AES-CTR: packet_length is encrypted
- AES-GCM: 16-byte GCM tag (no separate MAC)
- AES-CTR: 32-byte HMAC-SHA256 MAC

Performance improvement:
- AES-GCM: encrypt+authenticate in one step (AEAD)
- AES-CTR: MAC-then-Encrypt (2 steps)

Testing:
- OpenSSH client negotiated aes256-gcm@openssh.com
- cipher_mode set to AesGcm successfully
- Next: full SSH connection test
This commit is contained in:
Warren
2026-06-19 10:20:29 +08:00
parent 3575ab7e66
commit 1650708ac7

View File

@@ -250,7 +250,7 @@ pub struct EncryptedPacket {
impl EncryptedPacket {
/// 创建加密packet参考OpenSSH cipher.c
/// AES-CTR模式所有数据加密包括packet_length
/// Phase 1: 支持 AES-CTR (MtE) 和 AES-GCM (AEAD) 两种模式
pub fn new(
plaintext_payload: &[u8],
encryption_ctx: &mut EncryptionContext,
@@ -278,100 +278,183 @@ impl EncryptedPacket {
// packet_length = padding_length(1) + payload + padding
let packet_length = 1 + payload_length + padding_length as usize;
info!(
"Creating AES-CTR encrypted packet: payload_len={}, padding_len={}, packet_len={}",
payload_length, padding_length, packet_length
);
// Phase 1: 根据 cipher_mode 选择不同的加密逻辑
if encryption_ctx.cipher_mode == CipherMode::AesGcm {
// AES-GCM AEAD 模式RFC 5647
info!(
"Creating AES-GCM AEAD packet: payload_len={}, padding_len={}, packet_len={}",
payload_length, padding_length, packet_length
);
// 构建plaintext packetpacket_length + padding_length + payload + padding
let mut plaintext_packet = Vec::new();
plaintext_packet.write_u32::<BigEndian>(packet_length as u32)?; // plaintext packet_length
plaintext_packet.write_u8(padding_length)?; // plaintext padding_length
plaintext_packet.write_all(plaintext_payload)?; // plaintext payload
// AES-GCM: packet_length 不加密(作为 AAD
// 构建plaintext payloadpadding_length + payload + padding
let mut plaintext_payload_buffer = Vec::new();
plaintext_payload_buffer.write_u8(padding_length)?;
plaintext_payload_buffer.write_all(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.write_all(&random_padding)?;
let mut random_padding = vec![0u8; padding_length as usize];
use rand::RngCore;
rand::thread_rng().fill_bytes(&mut random_padding);
plaintext_packet.write_all(&random_padding)?; // plaintext padding
// AES-GCM nonce: sequence_number (4 bytes → 12 bytes, 前8 bytes = 0)
let sequence_number = if is_server_to_client {
encryption_ctx.sequence_number_stoc
} else {
encryption_ctx.sequence_number_ctos
};
let mut nonce_bytes = [0u8; 12];
nonce_bytes[8..12].copy_from_slice(&sequence_number.to_be_bytes());
info!("AES-GCM nonce (from sequence_number {}): {:?}", sequence_number, nonce_bytes);
info!("Plaintext packet size: {} bytes", plaintext_packet.len());
// AES-GCM key: 32 bytes (AES-256)
let key_bytes = if is_server_to_client {
&encryption_ctx.encryption_key_stoc
} else {
&encryption_ctx.encryption_key_ctos
};
// AES-GCM 加密AEAD: payload + GCM tag
let cipher = Aes256GcmAead::new_from_slice(&key_bytes[..32])
.map_err(|e| anyhow!("AES-GCM key initialization failed: {}", e))?;
let nonce = Nonce::from_slice(&nonce_bytes);
// AAD: packet_length (4 bytes, plaintext)
let packet_length_bytes = (packet_length as u32).to_be_bytes();
// AES-GCM encrypt: ciphertext = encrypt(payload, nonce, AAD=packet_length)
let ciphertext = cipher.encrypt(nonce, plaintext_payload_buffer.as_slice())
.map_err(|e| anyhow!("AES-GCM encryption failed: {}", e))?;
info!("AES-GCM ciphertext size: {} bytes (payload + 16-byte tag)", ciphertext.len());
// MtE模式先計算MAC over plaintext再加密
let sequence_number = if is_server_to_client {
encryption_ctx.sequence_number_stoc
// AES-GCM packet structure:
// [packet_length (4 bytes plaintext)] [ciphertext (payload + padding + 16-byte tag)]
let mut full_packet = Vec::new();
full_packet.write_u32::<BigEndian>(packet_length as u32)?;
full_packet.write_all(&ciphertext)?;
// 更新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, // AES-GCM: packet_length (plaintext) + ciphertext (encrypted payload + tag)
padding: random_padding,
mac: ciphertext[ciphertext.len()-16..].to_vec(), // AES-GCM tag (last 16 bytes)
})
} else {
encryption_ctx.sequence_number_ctos
};
// AES-CTR MtE 模式(原有逻辑)
info!(
"Creating AES-CTR encrypted packet: payload_len={}, padding_len={}, packet_len={}",
payload_length, padding_length, packet_length
);
let mac_key = if is_server_to_client {
&encryption_ctx.mac_key_stoc
} else {
&encryption_ctx.mac_key_ctos
};
// 构建plaintext packetpacket_length + padding_length + payload + padding
let mut plaintext_packet = Vec::new();
plaintext_packet.write_u32::<BigEndian>(packet_length as u32)?; // plaintext packet_length
plaintext_packet.write_u8(padding_length)?; // plaintext padding_length
plaintext_packet.write_all(plaintext_payload)?; // plaintext payload
info!("MAC calculation (MtE mode) over plaintext packet:");
info!(" sequence_number: {}", sequence_number);
info!(" mac_key length: {}", mac_key.len());
info!(" plaintext_packet length: {}", plaintext_packet.len());
let mut random_padding = vec![0u8; padding_length as usize];
use rand::RngCore;
rand::thread_rng().fill_bytes(&mut random_padding);
plaintext_packet.write_all(&random_padding)?; // plaintext padding
// MAC計算HMAC(sequence_number || plaintext_packet)
let mac = encryption_ctx.compute_mac(sequence_number, &plaintext_packet, mac_key)?;
info!("Plaintext packet size: {} bytes", plaintext_packet.len());
// 然後加密plaintext packetAES-CTR加密整個packet
let cipher = if is_server_to_client {
encryption_ctx
.cipher_stoc
.as_mut()
.ok_or_else(|| anyhow!("cipher_stoc not initialized"))?
} else {
encryption_ctx
.cipher_ctos
.as_mut()
.ok_or_else(|| anyhow!("cipher_ctos not initialized"))?
};
// MtE模式先計算MAC over plaintext再加密
let sequence_number = if is_server_to_client {
encryption_ctx.sequence_number_stoc
} else {
encryption_ctx.sequence_number_ctos
};
let mut encrypted_packet = plaintext_packet;
cipher.apply_keystream(&mut encrypted_packet);
let mac_key = if is_server_to_client {
&encryption_ctx.mac_key_stoc
} else {
&encryption_ctx.mac_key_ctos
};
// 更新sequence number
if is_server_to_client {
encryption_ctx.sequence_number_stoc += 1;
} else {
encryption_ctx.sequence_number_ctos += 1;
info!("MAC calculation (MtE mode) over plaintext packet:");
info!(" sequence_number: {}", sequence_number);
info!(" mac_key length: {}", mac_key.len());
info!(" plaintext_packet length: {}", plaintext_packet.len());
// MAC計算HMAC(sequence_number || plaintext_packet)
let mac = encryption_ctx.compute_mac(sequence_number, &plaintext_packet, mac_key)?;
// 然後加密plaintext packetAES-CTR加密整個packet
let cipher = if is_server_to_client {
encryption_ctx
.cipher_stoc
.as_mut()
.ok_or_else(|| anyhow!("cipher_stoc not initialized"))?
} else {
encryption_ctx
.cipher_ctos
.as_mut()
.ok_or_else(|| anyhow!("cipher_ctos not initialized"))?
};
let mut encrypted_packet = plaintext_packet;
cipher.apply_keystream(&mut encrypted_packet);
// 更新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: encrypted_packet,
padding: random_padding,
mac,
})
}
Ok(Self {
packet_length: packet_length as u32,
padding_length,
payload: encrypted_packet,
padding: random_padding,
mac,
})
}
/// 写入加密packet参考OpenSSH cipher.c
/// AES-CTR模式写入完整加密packet + MAC
/// Phase 1: 支持 AES-CTR (MtE) 和 AES-GCM (AEAD) 两种模式
pub fn write<W: std::io::Write>(&self, stream: &mut W) -> Result<()> {
info!(
"Writing AES-CTR encrypted packet: total_encrypted_len={}, mac_len={}",
self.payload.len(),
self.mac.len()
);
// AES-CTR: 整个packet已加密包括packet_length直接写入
stream.write_all(&self.payload)?;
info!("Wrote encrypted packet ({} bytes)", self.payload.len());
// 写入MAC
stream.write_all(&self.mac)?;
info!("Wrote MAC ({} bytes)", self.mac.len());
// AES-CTR: packet_length encrypted + MAC
// AES-GCM: packet_length plaintext + ciphertext (payload + tag)
if self.payload.len() > 4 && self.payload[0..4] == self.packet_length.to_be_bytes() {
// AES-GCM: packet_length plaintext + ciphertext
info!(
"Writing AES-GCM AEAD packet: packet_len={}, ciphertext_len={}",
self.packet_length, self.payload.len() - 4
);
stream.write_all(&self.payload)?;
info!("Wrote AES-GCM packet ({} bytes)", self.payload.len());
} else {
// AES-CTR: entire packet encrypted + MAC
info!(
"Writing AES-CTR encrypted packet: encrypted_len={}, mac_len={}",
self.payload.len(), self.mac.len()
);
stream.write_all(&self.payload)?;
info!("Wrote encrypted packet ({} bytes)", self.payload.len());
stream.write_all(&self.mac)?;
info!("Wrote MAC ({} bytes)", self.mac.len());
}
Ok(())
}
/// 读取加密packet参考OpenSSH packet.c ssh_packet_read_poll2
/// OpenSSH packet.c: AES-CTR先解密第一个块再提取packet_length
/// aadlen = 0 (没有EtM或authenticated encryption), packet_length被加密
/// Phase 1: 支持 AES-CTR (MtE) 和 AES-GCM (AEAD) 两种模式
pub fn read<R: std::io::Read>(
stream: &mut R,
encryption_ctx: &mut EncryptionContext,
@@ -379,123 +462,214 @@ impl EncryptedPacket {
) -> Result<Self> {
use std::io::Read;
info!("Reading AES-CTR encrypted packet (packet_length encrypted)");
// Phase 1: 根据 cipher_mode 选择不同的解密逻辑
if encryption_ctx.cipher_mode == CipherMode::AesGcm {
// AES-GCM AEAD 模式RFC 5647
info!("Reading AES-GCM AEAD packet (packet_length plaintext)");
// 1. 读取第一个加密块16字节包含加密的packet_length
let mut first_block_encrypted = [0u8; 16];
stream.read_exact(&mut first_block_encrypted)?;
// 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);
info!(
"Read first encrypted block (16 bytes): {:?}",
&first_block_encrypted
);
// 2. 合理性检查
if packet_length > 35000 {
return Err(anyhow!("Invalid packet_length: {}", packet_length));
}
// 2. 获取持久化cipher实例counter已递增
let cipher = if is_client_to_server {
encryption_ctx
.cipher_ctos
.as_mut()
.ok_or_else(|| anyhow!("cipher_ctos not initialized"))?
// 3. 计算 ciphertext 长度
// ciphertext = padding_length(1) + payload + padding + GCM_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. AES-GCM nonce: sequence_number (4 bytes → 12 bytes)
let sequence_number = if is_client_to_server {
encryption_ctx.sequence_number_ctos
} else {
encryption_ctx.sequence_number_stoc
};
let mut nonce_bytes = [0u8; 12];
nonce_bytes[8..12].copy_from_slice(&sequence_number.to_be_bytes());
info!("AES-GCM nonce (from sequence_number {}): {:?}", sequence_number, nonce_bytes);
// 6. AES-GCM key: 32 bytes (AES-256)
let key_bytes = if is_client_to_server {
&encryption_ctx.encryption_key_ctos
} else {
&encryption_ctx.encryption_key_stoc
};
// 7. AES-GCM 解密AEAD: decrypt(ciphertext, nonce, AAD=packet_length)
let cipher = Aes256GcmAead::new_from_slice(&key_bytes[..32])
.map_err(|e| anyhow!("AES-GCM key initialization failed: {}", e))?;
let nonce = Nonce::from_slice(&nonce_bytes);
// AAD: packet_length (4 bytes plaintext)
let plaintext_payload_buffer = cipher.decrypt(nonce, ciphertext.as_slice())
.map_err(|e| anyhow!("AES-GCM decryption failed: {}", e))?;
info!("AES-GCM 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!("AES-GCM: padding_length={}, payload_length={}", padding_length, payload_length);
let payload = plaintext_payload_buffer[1..1 + payload_length].to_vec();
let padding = plaintext_payload_buffer[1 + payload_length..].to_vec();
// 9. 提取 GCM tag (last 16 bytes of ciphertext)
let mac = ciphertext[ciphertext.len()-16..].to_vec();
info!("AES-GCM 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;
}
// 11. 构建完整 packetpacket_length plaintext + ciphertext
let mut full_packet = Vec::new();
full_packet.extend_from_slice(&packet_length_bytes);
full_packet.extend_from_slice(&ciphertext);
Ok(Self {
packet_length,
padding_length,
payload: full_packet, // AES-GCM: packet_length (plaintext) + ciphertext
padding,
mac, // AES-GCM tag
})
} else {
encryption_ctx
.cipher_stoc
.as_mut()
.ok_or_else(|| anyhow!("cipher_stoc not initialized"))?
};
// AES-CTR MtE 模式(原有逻辑)
info!("Reading AES-CTR encrypted packet (packet_length encrypted)");
info!(
"Using cipher for decryption (is_client_to_server={})",
is_client_to_server
);
// 1. 读取第一个加密块16字节包含加密的packet_length
let mut first_block_encrypted = [0u8; 16];
stream.read_exact(&mut first_block_encrypted)?;
// 3. 解密第一个块counter自动递增
let mut first_block_decrypted = first_block_encrypted;
cipher.apply_keystream(&mut first_block_decrypted);
info!(
"Read first encrypted block (16 bytes): {:?}",
&first_block_encrypted
);
info!("Decrypted first block: {:?}", &first_block_decrypted);
// 2. 获取持久化cipher实例counter已递增
let cipher = if is_client_to_server {
encryption_ctx
.cipher_ctos
.as_mut()
.ok_or_else(|| anyhow!("cipher_ctos not initialized"))?
} else {
encryption_ctx
.cipher_stoc
.as_mut()
.ok_or_else(|| anyhow!("cipher_stoc not initialized"))?
};
// 3. 从解密后的数据中提取packet_length前4字节和padding_length第5字节
let packet_length = u32::from_be_bytes([
first_block_decrypted[0],
first_block_decrypted[1],
first_block_decrypted[2],
first_block_decrypted[3],
]);
let padding_length = first_block_decrypted[4];
info!(
"Using cipher for decryption (is_client_to_server={})",
is_client_to_server
);
info!(
"Decrypted packet_length={}, padding_length={}",
packet_length, padding_length
);
// 3. 解密第一个块counter自动递增
let mut first_block_decrypted = first_block_encrypted;
cipher.apply_keystream(&mut first_block_decrypted);
// 4. 合理性检查
if packet_length > 35000 {
info!("packet_length raw bytes: {:?}", &first_block_decrypted[..4]);
return Err(anyhow!("Invalid packet_length: {}", packet_length));
info!("Decrypted first block: {:?}", &first_block_decrypted);
// 4. 从解密后的数据中提取packet_length前4字节和padding_length第5字节
let packet_length = u32::from_be_bytes([
first_block_decrypted[0],
first_block_decrypted[1],
first_block_decrypted[2],
first_block_decrypted[3],
]);
let padding_length = first_block_decrypted[4];
info!(
"Decrypted packet_length={}, padding_length={}",
packet_length, padding_length
);
// 5. 合理性检查
if packet_length > 35000 {
info!("packet_length raw bytes: {:?}", &first_block_decrypted[..4]);
return Err(anyhow!("Invalid packet_length: {}", packet_length));
}
// 6. 计算剩余加密数据长度
let total_encrypted_size = packet_length as usize + 4; // packet_length field + content
let remaining_encrypted_size = total_encrypted_size - 16;
info!(
"Total encrypted size: {}, remaining: {}",
total_encrypted_size, remaining_encrypted_size
);
// 7. 读取剩余加密数据
let mut remaining_encrypted = vec![0u8; remaining_encrypted_size];
stream.read_exact(&mut remaining_encrypted)?;
// 8. 继续解密使用同一个cipher
cipher.apply_keystream(&mut remaining_encrypted);
info!("Remaining decrypted data: {:?}", &remaining_encrypted);
// 9. 提取payload和padding
let payload_length = packet_length as usize - padding_length as usize - 1;
info!("Calculated payload_length: {}", payload_length);
// 从第一块提取payload_part15-16字节11字节
let payload_part1_len = std::cmp::min(payload_length, 11);
let payload_part1 = &first_block_decrypted[5..5 + payload_part1_len];
// 从剩余数据提取payload_part2
let payload_part2_len = payload_length - payload_part1_len;
let payload_part2 = &remaining_encrypted[..payload_part2_len];
// 合并payload
let mut payload = Vec::new();
payload.extend_from_slice(payload_part1);
payload.extend_from_slice(payload_part2);
// 提取padding从remaining_encrypted的末尾
let padding = remaining_encrypted[payload_part2_len..].to_vec();
// 10. 读取MAC
info!("Reading MAC (32 bytes)...");
let mut mac = vec![0u8; 32];
stream.read_exact(&mut mac)?;
info!("MAC read successfully");
// 11. 更新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,
padding,
mac,
})
}
// 3. 计算剩余加密数据长度
// packet_length = padding_length(1) + payload + padding
// 总加密数据 = packet_length(4) + packet_length = packet_length + 4
// 已读取16字节剩余 = packet_length + 4 - 16
let total_encrypted_size = packet_length as usize + 4; // packet_length field + content
let remaining_encrypted_size = total_encrypted_size - 16;
info!(
"Total encrypted size: {}, remaining: {}",
total_encrypted_size, remaining_encrypted_size
);
// 4. 读取剩余加密数据
let mut remaining_encrypted = vec![0u8; remaining_encrypted_size];
stream.read_exact(&mut remaining_encrypted)?;
// 5. 继续解密使用同一个cipher
cipher.apply_keystream(&mut remaining_encrypted);
info!("Remaining decrypted data: {:?}", &remaining_encrypted);
// 6. 提取payload和padding
// payload长度 = packet_length - padding_length - 1
let payload_length = packet_length as usize - padding_length as usize - 1;
info!("Calculated payload_length: {}", payload_length);
// 从第一块提取payload_part15-16字节11字节
let payload_part1_len = std::cmp::min(payload_length, 11);
let payload_part1 = &first_block_decrypted[5..5 + payload_part1_len];
// 从剩余数据提取payload_part2
let payload_part2_len = payload_length - payload_part1_len;
let payload_part2 = &remaining_encrypted[..payload_part2_len];
// 合并payload
let mut payload = Vec::new();
payload.extend_from_slice(payload_part1);
payload.extend_from_slice(payload_part2);
// 提取padding从remaining_encrypted的末尾
let padding = remaining_encrypted[payload_part2_len..].to_vec();
// 9. 读取MAC
info!("Reading MAC (32 bytes)...");
let mut mac = vec![0u8; 32];
stream.read_exact(&mut mac)?;
info!("MAC read successfully");
// 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,
padding,
mac,
})
}
/// 获取payload内容