SSH AES-128-CTR encryption fixes (Phase 4 refinement)
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled

Major fixes:
- Persistent cipher state: ciphers maintain counter across packets
- Cipher direction bug: use cipher_ctos for client packets, cipher_stoc for server packets
- MAC key length: 32 bytes for HMAC-SHA256 (was incorrectly 16 bytes)
- MtE mode MAC: calculate MAC over plaintext before encryption
- AES-CTR encryption: encrypt entire packet including packet_length field
- Service name length: corrected to 12 for 'ssh-userauth'
- mpint encoding: properly remove leading zeros and handle high bit

Remaining issue:
- SSH client reports 'Corrupted MAC on input'
- Likely due to key derivation mismatch with OpenSSH client
- Requires further investigation with packet capture analysis

Progress: 80% of SSH encryption implementation complete
Security: Still using RustCrypto authoritative libraries ()
This commit is contained in:
Warren
2026-06-14 15:06:01 +08:00
parent 2cbf0d7b98
commit 7d50c1147d
4 changed files with 179 additions and 93 deletions

View File

@@ -104,8 +104,12 @@ impl KexExchangeHandler {
server_kexinit_payload,
)?;
self.exchange_hash = Some(exchange_hash);
info!("Exchange hash computed and saved");
info!("Exchange hash computed:");
info!(" shared_secret[0] = {} (>=0x80? {})", shared_secret[0], shared_secret[0] >= 0x80);
info!(" exchange_hash (32 bytes): {:?}", &exchange_hash[..8]);
self.exchange_hash = Some(exchange_hash.clone());
info!("Exchange hash saved for key derivation");
self.build_kexdh_reply(
&shared_secret,
@@ -214,15 +218,33 @@ impl KexExchangeHandler {
hasher.update(&(server_public_key.len() as u32).to_be_bytes());
hasher.update(server_public_key);
let mpint_shared_secret = if shared_secret.len() > 0 && shared_secret[0] >= 0x80 {
info!("Exchange hash components:");
info!(" shared_secret raw ({} bytes): {:?}", shared_secret.len(), &shared_secret[..std::cmp::min(8, shared_secret.len())]);
// RFC 4253: mpint格式 = 去掉前导零 + 最高位>=0x80时前面加0
// 参考OpenSSH sshbuf_put_bignum2_bytes()
let mut start = 0;
while start < shared_secret.len() - 1 && shared_secret[start] == 0 {
start += 1;
}
let trimmed_shared_secret = &shared_secret[start..];
info!(" shared_secret after removing leading zeros ({} bytes): {:?}", trimmed_shared_secret.len(), &trimmed_shared_secret[..std::cmp::min(8, trimmed_shared_secret.len())]);
let mpint_shared_secret_data = if trimmed_shared_secret.len() > 0 && trimmed_shared_secret[0] >= 0x80 {
let mut mpint = vec![0u8];
mpint.extend_from_slice(shared_secret);
mpint.extend_from_slice(trimmed_shared_secret);
info!(" trimmed_shared_secret[0] >= 0x80, prepending 0 byte");
mpint
} else {
shared_secret.to_vec()
trimmed_shared_secret.to_vec()
};
hasher.update(&(mpint_shared_secret.len() as u32).to_be_bytes());
hasher.update(&mpint_shared_secret);
info!(" mpint_shared_secret_data ({} bytes): {:?}", mpint_shared_secret_data.len(), &mpint_shared_secret_data[..std::cmp::min(8, mpint_shared_secret_data.len())]);
// mpint格式 = uint32(length) + mpint_data
hasher.update(&(mpint_shared_secret_data.len() as u32).to_be_bytes());
hasher.update(&mpint_shared_secret_data);
Ok(hasher.finalize().to_vec())
}