diff --git a/markbase-core/src/ssh_server/cipher.rs b/markbase-core/src/ssh_server/cipher.rs index e205e77..bffc785 100644 --- a/markbase-core/src/ssh_server/cipher.rs +++ b/markbase-core/src/ssh_server/cipher.rs @@ -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 packet(packet_length + padding_length + payload + padding) - let mut plaintext_packet = Vec::new(); - plaintext_packet.write_u32::(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 payload(padding_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::(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 packet(packet_length + padding_length + payload + padding) + let mut plaintext_packet = Vec::new(); + plaintext_packet.write_u32::(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 packet(AES-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 packet(AES-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(&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( stream: &mut R, encryption_ctx: &mut EncryptionContext, @@ -379,123 +462,214 @@ impl EncryptedPacket { ) -> Result { 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. 构建完整 packet(packet_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_part1(5-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_part1(5-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内容