Fix SSH X25519 shared secret encoding for exchange hash

CRITICAL BUG FIX (RFC 8731 Section 3.1):
- X25519 output is little-endian
- SSH exchange hash requires big-endian encoding
- Reverse shared_secret bytes before mpint encoding
- Fix exchange hash computation in kex_exchange.rs
- Fix key derivation in crypto.rs
- Fix KEXINIT cookie to use random bytes

This resolves the fundamental encoding mismatch that caused
'Corrupted MAC on input' errors.

Next: verify signature verification after exchange hash fix.
This commit is contained in:
Warren
2026-06-14 19:13:18 +08:00
parent 0403a340c4
commit 76f707a31d
4 changed files with 66 additions and 35 deletions

View File

@@ -76,13 +76,27 @@ impl SessionKeys {
info!("SessionKeys::derive() starting");
info!(" shared_secret ({} bytes): {:?}", shared_secret.len(), &shared_secret[..std::cmp::min(8, shared_secret.len())]);
info!(" shared_secret[0] = {} (>=0x80? {})", shared_secret[0], shared_secret[0] >= 0x80);
// RFC 8731 Section 3.1: X25519 output is little-endian, must convert to big-endian
// "When performing the X25519 operations, the integer values will be encoded into
// byte strings by doing a fixed-length unsigned little-endian conversion.
// It is only later when these byte strings are passed to the ECDH function in SSH
// that the bytes are reinterpreted as a fixed-length unsigned big-endian integer value K"
let shared_secret_big_endian = {
let mut reversed = shared_secret.to_vec();
reversed.reverse();
reversed
};
info!(" shared_secret converted to big-endian ({} bytes): {:?}",
shared_secret_big_endian.len(), &shared_secret_big_endian[..std::cmp::min(8, shared_secret_big_endian.len())]);
info!(" shared_secret_big_endian[0] = {} (>=0x80? {})", shared_secret_big_endian[0], shared_secret_big_endian[0] >= 0x80);
info!(" exchange_hash (H, {} bytes): {:?}", exchange_hash.len(), &exchange_hash[..8]);
info!(" session_id ({} bytes): {:?}", session_id.len(), &session_id[..8]);
// RFC 4253密钥派生公式HASH(K || H || X || session_id)
// 其中K是shared_secret需要mpint格式
let shared_secret_mpint = Self::encode_mpint(shared_secret);
// 其中K是shared_secret需要mpint格式使用big-endian
let shared_secret_mpint = Self::encode_mpint(&shared_secret_big_endian);
info!(" shared_secret_mpint ({} bytes): {:?}", shared_secret_mpint.len(), &shared_secret_mpint[..std::cmp::min(12, shared_secret_mpint.len())]);