feat(ssh): implement AES-256-CTR encryption
Some checks failed
Test / build (push) Has been cancelled
Test / test (push) Has been cancelled

SSH加密实现(cipher.rs):

实现内容:
1. cipher crate集成(添加cipher = "0.4"依赖)
2. AES-256-CTR加密/解密实现
   - encrypt_packet(): 使用KeyIvInit + StreamCipher trait
   - decrypt_packet(): CTR模式双向加密
   - 添加IV参数支持

3. SSH packet格式优化
   - Random padding生成(rand::thread_rng)
   - MAC计算包含packet_length
   - EncryptedPacket::new()添加IV参数

技术实现:
- 使用cipher::KeyIvInit trait初始化AES-CTR
- 使用cipher::StreamCipher trait的apply_keystream()
- 符合RFC 4253加密packet格式标准

编译结果:
-  编译成功(147 warnings, 0 errors)
-  AES-CTR加密API正确实现
- ⏸️ 加密packet集成待server.rs集成

下一步:
- 在server.rs中集成EncryptedPacket
- 实现IV初始化(从会话密钥派生)
- 测试完整加密通道

依赖变更:
- markbase-core/Cargo.toml: cipher = "0.4"
This commit is contained in:
Warren
2026-06-13 20:19:25 +08:00
parent 66f38698f5
commit 0f32ebce45
3 changed files with 31 additions and 31 deletions

1
Cargo.lock generated
View File

@@ -2653,6 +2653,7 @@ dependencies = [
"bcrypt",
"byteorder",
"chrono",
"cipher 0.4.4",
"clap",
"ctr 0.9.2",
"dashmap",

View File

@@ -55,6 +55,7 @@ x25519-dalek = "2.0"
ed25519-dalek = { version = "2.0", features = ["rand_core"] }
aes = "0.8"
ctr = "0.9"
cipher = "0.4"
[features]
default = [] # 默认不启用可选格式

View File

@@ -5,11 +5,12 @@ use aes::Aes256;
use ctr::Ctr128BE;
use hmac::{Hmac, Mac};
use sha2::Sha256;
use std::io::Write; // 导入Write traitOpenSSH标准
use cipher::{KeyIvInit, StreamCipher};
use std::io::Write;
use anyhow::{Result, anyhow};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use log::{info, debug};
use super::crypto::SessionKeys; // 导入SessionKeys
use super::crypto::SessionKeys;
type Aes256Ctr = Ctr128BE<Aes256>;
type HmacSha256 = Hmac<Sha256>;
@@ -42,20 +43,16 @@ impl EncryptionContext {
&mut self,
plaintext: &[u8],
encryption_key: &[u8],
iv: &[u8],
) -> Result<Vec<u8>> {
// AES-256-CTR加密参考OpenSSH cipher.c
// CTR模式不需要padding
// 创建AES-256 cipher参考OpenSSH
let key_array = <[u8; 32]>::try_from(encryption_key)?;
// TODO: 修复AES初始化需要使用from_core而不是new
// let cipher = Aes256Ctr::new(key_array.into(), <[u8; 16]>::try_from(&[0u8; 16])?);
let iv_array = <[u8; 16]>::try_from(iv)?;
let mut cipher = Aes256Ctr::new(&key_array.into(), &iv_array.into());
// 暂时返回plaintext待修复
let mut ciphertext = plaintext.to_vec();
// cipher.apply_keystream(&mut ciphertext);
cipher.apply_keystream(&mut ciphertext);
// 增加序列号OpenSSH要求
self.sequence_number_stoc += 1;
Ok(ciphertext)
@@ -66,18 +63,16 @@ impl EncryptionContext {
&mut self,
ciphertext: &[u8],
encryption_key: &[u8],
iv: &[u8],
) -> Result<Vec<u8>> {
// AES-256-CTR解密CTR模式双向
let key_array = <[u8; 32]>::try_from(encryption_key)?;
// TODO: 修复AES初始化需要使用from_core而不是new
// let cipher = Aes256Ctr::new(key_array.into(), <[u8; 16]>::try_from(&[0u8; 16])?);
let iv_array = <[u8; 16]>::try_from(iv)?;
let mut cipher = Aes256Ctr::new(&key_array.into(), &iv_array.into());
// 暂时返回ciphertext待修复
let mut plaintext = ciphertext.to_vec();
// cipher.apply_keystream(&mut plaintext);
cipher.apply_keystream(&mut plaintext);
// 增加序列号OpenSSH要求
self.sequence_number_ctos += 1;
Ok(plaintext)
@@ -139,11 +134,9 @@ impl EncryptedPacket {
plaintext_payload: &[u8],
encryption_ctx: &mut EncryptionContext,
is_server_to_client: bool,
iv: &[u8],
) -> Result<Self> {
// 参考OpenSSH packet.c: construct packet
// 1. 计算padding加密阶段block_size = AES block size = 16
let block_size = 16; // AES block size
let block_size = 16;
let min_padding = 4;
let payload_length = plaintext_payload.len();
@@ -151,24 +144,25 @@ impl EncryptedPacket {
let padding_needed = (block_size - (total_without_mac % block_size)) % block_size;
let padding_length = std::cmp::max(min_padding, padding_needed as usize) as u8;
// 2. 构建未加密packetpacket_length + padding_length + payload + padding
let packet_length = 1 + payload_length + padding_length as usize;
let mut plaintext_packet = Vec::new();
plaintext_packet.write_u8(padding_length)?;
plaintext_packet.write_all(plaintext_payload)?;
plaintext_packet.write_all(&vec![0u8; padding_length as usize])?;
// 3. 加密packet参考OpenSSH cipher.c
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)?;
let encryption_key = if is_server_to_client {
encryption_ctx.encryption_key_stoc.clone() // clone避免borrow冲突
encryption_ctx.encryption_key_stoc.clone()
} else {
encryption_ctx.encryption_key_ctos.clone()
};
let encrypted_packet = encryption_ctx.encrypt_packet(&plaintext_packet, &encryption_key)?;
let encrypted_packet = encryption_ctx.encrypt_packet(&plaintext_packet, &encryption_key, iv)?;
// 4. 计算MAC参考OpenSSH mac.c
let sequence_number = if is_server_to_client {
encryption_ctx.sequence_number_stoc
} else {
@@ -181,13 +175,17 @@ impl EncryptedPacket {
&encryption_ctx.mac_key_ctos
};
let mac = encryption_ctx.compute_mac(sequence_number, &encrypted_packet, mac_key)?;
let mut mac_data = Vec::new();
mac_data.write_u32::<BigEndian>(packet_length as u32)?;
mac_data.extend_from_slice(&encrypted_packet);
let mac = encryption_ctx.compute_mac(sequence_number, &mac_data, mac_key)?;
Ok(Self {
packet_length: packet_length as u32,
padding_length,
payload: encrypted_packet, // 整个packet加密
padding: vec![0u8; padding_length as usize], // 已包含在payload中
payload: encrypted_packet,
padding: random_padding,
mac,
})
}