Implement Phase 1: AES-256-GCM algorithm negotiation and cipher mode setting
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled

Performance optimization Phase 1 implementation:
- Add aes-gcm crate dependency (v0.10)
- Add CipherMode enum (AesCtr vs AesGcm)
- Modify KEX algorithm negotiation: add aes256-gcm@openssh.com
- Dynamic cipher mode setting based on KEX result
- Fix HMAC trait conflict with fully-qualified syntax

Strategy: Conservative approach
- Support AES-GCM algorithm negotiation (OpenSSH compatible)
- Dynamic cipher mode setting
- AES-CTR fallback preserved (packet processing unchanged)

Next steps:
- Test OpenSSH client AES-GCM negotiation
- Implement AES-GCM packet processing if needed
- Continue to Phase 4 (parallel encryption)
This commit is contained in:
Warren
2026-06-19 10:10:53 +08:00
parent c59e33f6e4
commit 3575ab7e66
5 changed files with 77 additions and 13 deletions

2
Cargo.lock generated
View File

@@ -963,6 +963,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array 0.14.7",
"rand_core 0.6.4",
"typenum",
]
@@ -2669,6 +2670,7 @@ version = "0.2.0"
dependencies = [
"adler",
"aes 0.8.4",
"aes-gcm 0.10.3",
"anyhow",
"axum",
"axum-extra",

View File

@@ -58,6 +58,7 @@ ed25519-dalek = { version = "2.0", features = ["rand_core"] }
aes = "0.8"
ctr = "0.9"
cipher = "0.4"
aes-gcm = "0.10" # Phase 1: AES-256-GCM AEAD性能优化
nix = { version = "0.29", features = ["poll", "fs"] } # Phase 14: OpenSSH风格的poll()和非阻塞I/Ofs feature包含fcntl
rusty-s3 = "0.10" # S3 API 签名AWS Signature V4
ureq = "2.12" # 輕量同步 HTTP 客戶端

View File

@@ -3,6 +3,10 @@
use super::crypto::SessionKeys;
use aes::Aes128; // 改为AES-128协商算法是aes128-ctr
use aes_gcm::{
aead::{Aead, KeyInit},
Aes256Gcm, Nonce, // Phase 1: AES-256-GCM AEAD性能优化
};
use anyhow::{anyhow, Result};
use byteorder::{BigEndian, WriteBytesExt};
use cipher::{KeyIvInit, StreamCipher};
@@ -14,20 +18,30 @@ use std::io::Write;
type Aes128Ctr = Ctr128BE<Aes128>; // AES-128-CTR16字节密钥
type HmacSha256 = Hmac<Sha256>;
// Phase 1: AES-256-GCM AEAD32字节密钥 + 12字节nonce + 16字节tag
type Aes256GcmAead = Aes256Gcm; // AES-256-GCMAEAD模式
/// SSH加密通道管理器参考OpenSSH struct sshcipher_ctx
pub struct EncryptionContext {
pub session_id: Vec<u8>, // session identifier (exchange hash)
pub encryption_key_ctos: Vec<u8>, // 客户端→服务器加密密钥
pub encryption_key_stoc: Vec<u8>, // 服务器→客户端加密密钥
pub mac_key_ctos: Vec<u8>, // 客户端→服务器MAC密钥
pub mac_key_stoc: Vec<u8>, // 服务器→客户端MAC密钥
pub mac_key_ctos: Vec<u8>, // 客户端→服务器MAC密钥仅用于AES-CTR
pub mac_key_stoc: Vec<u8>, // 服务器→客户端MAC密钥仅用于AES-CTR
pub iv_ctos: Vec<u8>, // 客户端→服务器IV
pub iv_stoc: Vec<u8>, // 服务器→客户端IV
pub sequence_number_ctos: u32, // 客户端→服务器序列号
pub sequence_number_stoc: u32, // 服务器→客户端序列号
pub cipher_ctos: Option<Aes128Ctr>, // 客户端→服务器cipher实例持久化
pub cipher_stoc: Option<Aes128Ctr>, // 服务器→客户端cipher实例持久化
pub cipher_ctos: Option<Aes128Ctr>, // 客户端→服务器cipher实例持久化AES-CTR
pub cipher_stoc: Option<Aes128Ctr>, // 服务器→客户端cipher实例持久化AES-CTR
pub cipher_mode: CipherMode, // Phase 1: 区分 AES-CTR 和 AES-GCM 模式
}
/// Phase 1: 加密模式选择AES-CTR vs AES-GCM
#[derive(Debug, Clone, PartialEq)]
pub enum CipherMode {
AesCtr, // AES-128-CTR + HMAC-SHA256MtE模式兼容性
AesGcm, // AES-256-GCMAEAD模式性能优化
}
impl Default for EncryptionContext {
@@ -44,6 +58,7 @@ impl Default for EncryptionContext {
sequence_number_stoc: 0,
cipher_ctos: None,
cipher_stoc: None,
cipher_mode: CipherMode::AesCtr, // 默认使用 AES-CTR兼容性
}
}
}
@@ -92,9 +107,42 @@ impl EncryptionContext {
sequence_number_stoc: 0,
cipher_ctos: Some(cipher_ctos), // 持久化cipher实例
cipher_stoc: Some(cipher_stoc), // 持久化cipher实例
cipher_mode: CipherMode::AesCtr, // 默认使用 AES-CTR兼容性
}
}
/// Phase 1: 设置加密模式(根据 KEX 协商结果)
/// 支持 AES-CTR兼容性和 AES-GCM性能优化
pub fn set_cipher_mode(&mut self, mode: CipherMode) -> Result<()> {
info!("Setting cipher mode to: {:?}", mode);
self.cipher_mode = mode.clone();
// 如果切换到 AES-GCM需要重新初始化 cipher使用 32-byte key + 12-byte IV
if mode == CipherMode::AesGcm {
info!("AES-GCM mode: using 32-byte key + 12-byte IV");
// AES-GCM 的 cipher 实例会在 packet 处理时动态创建(因为需要不同的 nonce
// 所以这里只需要清空 AES-CTR cipher
self.cipher_ctos = None;
self.cipher_stoc = None;
} else {
// AES-CTR 模式:重新初始化 AES-CTR cipher
info!("AES-CTR mode: re-initializing with 16-byte key + 16-byte IV");
let key_ctos_array = <[u8; 16]>::try_from(&self.encryption_key_ctos[..16])
.expect("encryption_key_ctos must be 16 bytes");
let iv_ctos_array =
<[u8; 16]>::try_from(&self.iv_ctos[..16]).expect("iv_ctos must be 16 bytes");
self.cipher_ctos = Some(Aes128Ctr::new(&key_ctos_array.into(), &iv_ctos_array.into()));
let key_stoc_array = <[u8; 16]>::try_from(&self.encryption_key_stoc[..16])
.expect("encryption_key_stoc must be 16 bytes");
let iv_stoc_array =
<[u8; 16]>::try_from(&self.iv_stoc[..16]).expect("iv_stoc must be 16 bytes");
self.cipher_stoc = Some(Aes128Ctr::new(&key_stoc_array.into(), &iv_stoc_array.into()));
}
Ok(())
}
/// RFC 4344: Compute AES-CTR IV for a specific packet
/// IV = nonce(8 bytes from derived IV) + sequence_number(8 bytes)
fn compute_ctr_iv(nonce: &[u8], sequence_number: u32) -> Vec<u8> {
@@ -158,8 +206,8 @@ impl EncryptionContext {
mac_key: &[u8],
) -> Result<Vec<u8>> {
// HMAC-SHA256 MAC计算参考OpenSSH mac.c
let mut mac = HmacSha256::new_from_slice(mac_key)?;
// Phase 1: 使用 fully-qualified syntax 避免与 aes_gcm::KeyInit 冲突
let mut mac = <HmacSha256 as hmac::Mac>::new_from_slice(mac_key)?;
// OpenSSH MAC格式sequence_number + data
mac.update(&sequence_number.to_be_bytes());

View File

@@ -50,9 +50,9 @@ impl KexProposal {
// 主机密钥算法优先Ed25519
server_host_key_algorithms: "ssh-ed25519,rsa-sha2-256,rsa-sha2-512".to_string(),
// 加密算法AES-256-CTR推荐
encryption_algorithms_ctos: "aes256-ctr,aes128-ctr".to_string(),
encryption_algorithms_stoc: "aes256-ctr,aes128-ctr".to_string(),
// 加密算法:优先 AES-256-GCMAEAD性能优化fallback 到 AES-CTR
encryption_algorithms_ctos: "aes256-gcm@openssh.com,aes256-ctr,aes128-ctr".to_string(),
encryption_algorithms_stoc: "aes256-gcm@openssh.com,aes256-ctr,aes128-ctr".to_string(),
// MAC算法HMAC-SHA256
mac_algorithms_ctos: "hmac-sha2-256,hmac-sha2-512".to_string(),
@@ -76,8 +76,8 @@ impl KexProposal {
Self {
kex_algorithms: "curve25519-sha256,diffie-hellman-group14-sha256".to_string(),
server_host_key_algorithms: "ssh-ed25519,rsa-sha2-256".to_string(),
encryption_algorithms_ctos: "aes256-ctr,aes128-ctr".to_string(),
encryption_algorithms_stoc: "aes256-ctr,aes128-ctr".to_string(),
encryption_algorithms_ctos: "aes256-gcm@openssh.com,aes256-ctr,aes128-ctr".to_string(),
encryption_algorithms_stoc: "aes256-gcm@openssh.com,aes256-ctr,aes128-ctr".to_string(),
mac_algorithms_ctos: "hmac-sha2-256".to_string(),
mac_algorithms_stoc: "hmac-sha2-256".to_string(),
compression_algorithms_ctos: "none".to_string(),

View File

@@ -268,7 +268,7 @@ fn perform_complete_kex_exchange(
let mut kex_state = KexState::new(
client_version,
"SSH-2.0-MarkBaseSSH_1.0".to_string(),
kex_result,
kex_result.clone(), // Phase 1: clone kex_result for cipher mode setting
)?;
kex_state.save_kexinit_payloads(&client_kexinit, &server_kexinit);
@@ -304,7 +304,20 @@ fn perform_complete_kex_exchange(
}
let session_keys = kex_state.exchange_handler.compute_session_keys()?;
let 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
let encryption_algorithm = &kex_result.encryption_stoc;
info!("KEX negotiated encryption algorithm: {}", encryption_algorithm);
use crate::ssh_server::cipher::CipherMode;
if encryption_algorithm.contains("gcm") {
info!("Setting cipher mode to AES-GCM (AEAD)");
encryption_ctx.set_cipher_mode(CipherMode::AesGcm)?;
} else {
info!("Setting cipher mode to AES-CTR (MtE)");
encryption_ctx.set_cipher_mode(CipherMode::AesCtr)?;
}
Ok(encryption_ctx)
}