mirror of
https://github.com/Noratrieb/cluelessh.git
synced 2026-01-14 16:35:06 +01:00
try adding AES-GCM
This commit is contained in:
parent
43c1696465
commit
4c3f0a97aa
5 changed files with 243 additions and 52 deletions
68
Cargo.lock
generated
68
Cargo.lock
generated
|
|
@ -17,6 +17,41 @@ version = "1.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "aead"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cipher",
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes-gcm"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"aes",
|
||||
"cipher",
|
||||
"ctr",
|
||||
"ghash",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
|
|
@ -153,9 +188,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"rand_core",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctr"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
|
||||
dependencies = [
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek"
|
||||
version = "4.1.3"
|
||||
|
|
@ -325,6 +370,16 @@ dependencies = [
|
|||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ghash"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1"
|
||||
dependencies = [
|
||||
"opaque-debug",
|
||||
"polyval",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.29.0"
|
||||
|
|
@ -565,6 +620,18 @@ dependencies = [
|
|||
"universal-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polyval"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"opaque-debug",
|
||||
"universal-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.20"
|
||||
|
|
@ -859,6 +926,7 @@ dependencies = [
|
|||
name = "ssh-transport"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"chacha20",
|
||||
"ed25519-dalek",
|
||||
"eyre",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
aes-gcm = "0.10.3"
|
||||
chacha20 = "0.9.1"
|
||||
ed25519-dalek = { version = "2.1.1" }
|
||||
eyre = "0.6.12"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use aes_gcm::aead::{Aead, AeadMutInPlace};
|
||||
use chacha20::cipher::{KeyInit, StreamCipher, StreamCipherSeek};
|
||||
use sha2::Digest;
|
||||
use subtle::ConstantTimeEq;
|
||||
|
|
@ -81,9 +82,11 @@ pub const KEX_ECDH_SHA2_NISTP256: KexAlgorithm = KexAlgorithm {
|
|||
#[derive(Clone, Copy)]
|
||||
pub struct EncryptionAlgorithm {
|
||||
name: &'static str,
|
||||
decrypt_len: fn(keys: &[u8], bytes: &mut [u8], packet_number: u64),
|
||||
decrypt_packet: fn(keys: &[u8], bytes: RawPacket, packet_number: u64) -> Result<Packet>,
|
||||
encrypt_packet: fn(keys: &[u8], packet: Packet, packet_number: u64) -> EncryptedPacket,
|
||||
iv_size: usize,
|
||||
key_size: usize,
|
||||
decrypt_len: fn(state: &mut [u8], bytes: &mut [u8], packet_number: u64),
|
||||
decrypt_packet: fn(state: &mut [u8], bytes: RawPacket, packet_number: u64) -> Result<Packet>,
|
||||
encrypt_packet: fn(state: &mut [u8], packet: Packet, packet_number: u64) -> EncryptedPacket,
|
||||
}
|
||||
impl AlgorithmName for EncryptionAlgorithm {
|
||||
fn name(&self) -> &'static str {
|
||||
|
|
@ -92,16 +95,35 @@ impl AlgorithmName for EncryptionAlgorithm {
|
|||
}
|
||||
pub const ENC_CHACHA20POLY1305: EncryptionAlgorithm = EncryptionAlgorithm {
|
||||
name: "chacha20-poly1305@openssh.com",
|
||||
decrypt_len: |keys, bytes, packet_number| {
|
||||
let alg = SshChaCha20Poly1305::from_keys(keys);
|
||||
iv_size: 0,
|
||||
key_size: 64, // 32 for header, 32 for main
|
||||
decrypt_len: |state, bytes, packet_number| {
|
||||
let alg = ChaCha20Poly1305OpenSsh::from_state(state);
|
||||
alg.decrypt_len(bytes, packet_number)
|
||||
},
|
||||
decrypt_packet: |keys, bytes, packet_number| {
|
||||
let alg = SshChaCha20Poly1305::from_keys(keys);
|
||||
decrypt_packet: |state, bytes, packet_number| {
|
||||
let alg = ChaCha20Poly1305OpenSsh::from_state(state);
|
||||
alg.decrypt_packet(bytes, packet_number)
|
||||
},
|
||||
encrypt_packet: |keys, packet, packet_number| {
|
||||
let alg = SshChaCha20Poly1305::from_keys(keys);
|
||||
encrypt_packet: |state, packet, packet_number| {
|
||||
let alg = ChaCha20Poly1305OpenSsh::from_state(state);
|
||||
alg.encrypt_packet(packet, packet_number)
|
||||
},
|
||||
};
|
||||
pub const ENC_AES256_GCM: EncryptionAlgorithm = EncryptionAlgorithm {
|
||||
name: "aes256-gcm@openssh.com",
|
||||
iv_size: 12,
|
||||
key_size: 32,
|
||||
decrypt_len: |state, bytes, packet_number| {
|
||||
let mut alg = Aes256GcmOpenSsh::from_state(state);
|
||||
alg.decrypt_len(bytes, packet_number)
|
||||
},
|
||||
decrypt_packet: |state, bytes, packet_number| {
|
||||
let mut alg = Aes256GcmOpenSsh::from_state(state);
|
||||
alg.decrypt_packet(bytes, packet_number)
|
||||
},
|
||||
encrypt_packet: |state, packet, packet_number| {
|
||||
let mut alg = Aes256GcmOpenSsh::from_state(state);
|
||||
alg.encrypt_packet(packet, packet_number)
|
||||
},
|
||||
};
|
||||
|
|
@ -130,10 +152,14 @@ impl<T: AlgorithmName> AlgorithmNegotiation<T> {
|
|||
|
||||
pub(crate) struct Session {
|
||||
session_id: [u8; 32],
|
||||
encryption_key_client_to_server: [u8; 64],
|
||||
encryption_client_to_server: EncryptionAlgorithm,
|
||||
encryption_key_server_to_client: [u8; 64],
|
||||
encryption_server_to_client: EncryptionAlgorithm,
|
||||
client_to_server: Tunnel,
|
||||
server_to_client: Tunnel,
|
||||
}
|
||||
|
||||
struct Tunnel {
|
||||
/// `key || IV`
|
||||
state: Vec<u8>,
|
||||
algorithm: EncryptionAlgorithm,
|
||||
}
|
||||
|
||||
pub(crate) trait Keys: Send + Sync + 'static {
|
||||
|
|
@ -196,20 +222,27 @@ impl Session {
|
|||
session_id: [u8; 32],
|
||||
h: [u8; 32],
|
||||
k: &[u8],
|
||||
encryption_client_to_server: EncryptionAlgorithm,
|
||||
encryption_server_to_client: EncryptionAlgorithm,
|
||||
alg_c2s: EncryptionAlgorithm,
|
||||
alg_s2c: EncryptionAlgorithm,
|
||||
) -> Self {
|
||||
let encryption_key_client_to_server = derive_key(k, h, "C", session_id);
|
||||
let encryption_key_server_to_client = derive_key(k, h, "D", session_id);
|
||||
|
||||
Self {
|
||||
session_id,
|
||||
// client_to_server_iv: derive("A").into(),
|
||||
// server_to_client_iv: derive("B").into(),
|
||||
encryption_key_client_to_server,
|
||||
encryption_client_to_server,
|
||||
encryption_key_server_to_client,
|
||||
encryption_server_to_client,
|
||||
client_to_server: Tunnel {
|
||||
algorithm: alg_c2s,
|
||||
state: {
|
||||
let mut state = derive_key(k, h, "C", session_id, alg_c2s.key_size);
|
||||
state.extend_from_slice(&derive_key(k, h, "A", session_id, alg_c2s.iv_size));
|
||||
state
|
||||
},
|
||||
},
|
||||
server_to_client: Tunnel {
|
||||
algorithm: alg_s2c,
|
||||
state: {
|
||||
let mut state = derive_key(k, h, "D", session_id, alg_s2c.key_size);
|
||||
state.extend_from_slice(&derive_key(k, h, "B", session_id, alg_s2c.iv_size));
|
||||
state
|
||||
},
|
||||
},
|
||||
// integrity_key_client_to_server: derive("E").into(),
|
||||
// integrity_key_server_to_client: derive("F").into(),
|
||||
}
|
||||
|
|
@ -218,24 +251,24 @@ impl Session {
|
|||
|
||||
impl Keys for Session {
|
||||
fn decrypt_len(&mut self, bytes: &mut [u8; 4], packet_number: u64) {
|
||||
(self.encryption_client_to_server.decrypt_len)(
|
||||
&self.encryption_key_client_to_server,
|
||||
(self.client_to_server.algorithm.decrypt_len)(
|
||||
&mut self.client_to_server.state,
|
||||
bytes,
|
||||
packet_number,
|
||||
);
|
||||
}
|
||||
|
||||
fn decrypt_packet(&mut self, bytes: RawPacket, packet_number: u64) -> Result<Packet> {
|
||||
(self.encryption_client_to_server.decrypt_packet)(
|
||||
&self.encryption_key_client_to_server,
|
||||
(self.client_to_server.algorithm.decrypt_packet)(
|
||||
&mut self.client_to_server.state,
|
||||
bytes,
|
||||
packet_number,
|
||||
)
|
||||
}
|
||||
|
||||
fn encrypt_packet_to_msg(&mut self, packet: Packet, packet_number: u64) -> Msg {
|
||||
let packet = (self.encryption_server_to_client.encrypt_packet)(
|
||||
&self.encryption_key_server_to_client,
|
||||
let packet = (self.server_to_client.algorithm.encrypt_packet)(
|
||||
&mut self.server_to_client.state,
|
||||
packet,
|
||||
packet_number,
|
||||
);
|
||||
|
|
@ -266,9 +299,15 @@ impl Keys for Session {
|
|||
|
||||
/// Derive a key from the shared secret K and exchange hash H.
|
||||
/// <https://datatracker.ietf.org/doc/html/rfc4253#section-7.2>
|
||||
fn derive_key(k: &[u8], h: [u8; 32], letter: &str, session_id: [u8; 32]) -> [u8; 64] {
|
||||
fn derive_key(
|
||||
k: &[u8],
|
||||
h: [u8; 32],
|
||||
letter: &str,
|
||||
session_id: [u8; 32],
|
||||
key_size: usize,
|
||||
) -> Vec<u8> {
|
||||
let sha2len = sha2::Sha256::output_size();
|
||||
let mut output = [0; 64];
|
||||
let mut output = vec![0; key_size];
|
||||
|
||||
//let mut hash = sha2::Sha256::new();
|
||||
//encode_mpint_for_hash(&k, |data| hash.update(data));
|
||||
|
|
@ -277,7 +316,7 @@ fn derive_key(k: &[u8], h: [u8; 32], letter: &str, session_id: [u8; 32]) -> [u8;
|
|||
//hash.update(session_id);
|
||||
//output[..sha2len].copy_from_slice(&hash.finalize());
|
||||
|
||||
for i in 0..(64 / sha2len) {
|
||||
for i in 0..(key_size / sha2len) {
|
||||
let mut hash = <sha2::Sha256 as sha2::Digest>::new();
|
||||
encode_mpint_for_hash(&k, |data| hash.update(data));
|
||||
hash.update(h);
|
||||
|
|
@ -311,13 +350,14 @@ pub(crate) fn encode_mpint_for_hash(mut key: &[u8], mut add_to_hash: impl FnMut(
|
|||
/// `chacha20-poly1305@openssh.com` uses a 64-bit nonce, not the 96-bit one in the IETF version.
|
||||
type SshChaCha20 = chacha20::ChaCha20Legacy;
|
||||
|
||||
struct SshChaCha20Poly1305 {
|
||||
/// <https://github.com/openssh/openssh-portable/blob/1ec0a64c5dc57b8a2053a93b5ef0d02ff8598e5c/PROTOCOL.chacha20poly1305>
|
||||
struct ChaCha20Poly1305OpenSsh {
|
||||
header_key: chacha20::Key,
|
||||
main_key: chacha20::Key,
|
||||
}
|
||||
|
||||
impl SshChaCha20Poly1305 {
|
||||
fn from_keys(keys: &[u8]) -> Self {
|
||||
impl ChaCha20Poly1305OpenSsh {
|
||||
fn from_state(keys: &[u8]) -> Self {
|
||||
assert_eq!(keys.len(), 64);
|
||||
Self {
|
||||
main_key: <[u8; 32]>::try_from(&keys[..32]).unwrap().into(),
|
||||
|
|
@ -370,7 +410,7 @@ impl SshChaCha20Poly1305 {
|
|||
}
|
||||
|
||||
fn encrypt_packet(&self, packet: Packet, packet_number: u64) -> EncryptedPacket {
|
||||
let mut bytes = packet.to_bytes(false);
|
||||
let mut bytes = packet.to_bytes(false, Packet::DEFAULT_BLOCK_SIZE);
|
||||
|
||||
// Prepare the main cipher.
|
||||
let mut main_cipher = <SshChaCha20 as chacha20::cipher::KeyIvInit>::new(
|
||||
|
|
@ -404,3 +444,81 @@ impl SshChaCha20Poly1305 {
|
|||
EncryptedPacket::from_encrypted_full_bytes(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://datatracker.ietf.org/doc/html/rfc5647>
|
||||
/// <https://github.com/openssh/openssh-portable/blob/1ec0a64c5dc57b8a2053a93b5ef0d02ff8598e5c/PROTOCOL#L188C49-L188C64>
|
||||
struct Aes256GcmOpenSsh<'a> {
|
||||
key: aes_gcm::Key<aes_gcm::Aes256Gcm>,
|
||||
nonce: &'a mut [u8; 12],
|
||||
}
|
||||
|
||||
impl<'a> Aes256GcmOpenSsh<'a> {
|
||||
fn from_state(keys: &'a mut [u8]) -> Self {
|
||||
assert_eq!(keys.len(), 44);
|
||||
Self {
|
||||
key: <[u8; 32]>::try_from(&keys[..32]).unwrap().into(),
|
||||
nonce: <&mut [u8; 12]>::try_from(&mut keys[32..]).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn decrypt_len(&mut self, _: &mut [u8], _: u64) {
|
||||
// AES-GCM does not encrypt the length.
|
||||
// <https://datatracker.ietf.org/doc/html/rfc5647#section-7.3>
|
||||
}
|
||||
|
||||
fn decrypt_packet(&mut self, mut bytes: RawPacket, _packet_number: u64) -> Result<Packet> {
|
||||
let mut cipher = aes_gcm::Aes256Gcm::new(&self.key);
|
||||
|
||||
let mut len = [0; 4];
|
||||
len.copy_from_slice(&bytes.full_packet()[..4]);
|
||||
|
||||
let tag_offset = bytes.full_packet().len() - 16;
|
||||
let mut tag = [0; 16];
|
||||
tag.copy_from_slice(&bytes.full_packet()[tag_offset..]);
|
||||
|
||||
let encrypted_packet_content = bytes.content_mut();
|
||||
|
||||
cipher
|
||||
.decrypt_in_place_detached(
|
||||
(&*self.nonce).into(),
|
||||
&len,
|
||||
encrypted_packet_content,
|
||||
(&tag).into(),
|
||||
)
|
||||
.map_err(|_| crate::client_error!("failed to decrypt: invalid GCM MAC"))?;
|
||||
self.inc_nonce();
|
||||
|
||||
Packet::from_full(encrypted_packet_content)
|
||||
}
|
||||
|
||||
fn encrypt_packet(&mut self, packet: Packet, _packet_number: u64) -> EncryptedPacket {
|
||||
let bytes = packet.to_bytes(
|
||||
false,
|
||||
<aes_gcm::aes::Aes256 as aes_gcm::aes::cipher::BlockSizeUser>::block_size() as u8,
|
||||
);
|
||||
|
||||
let cipher = aes_gcm::Aes256Gcm::new(&self.key);
|
||||
|
||||
let bytes = cipher
|
||||
.encrypt(
|
||||
(&*self.nonce).into(),
|
||||
aes_gcm::aead::Payload {
|
||||
aad: &bytes[..4],
|
||||
msg: &bytes[4..],
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
self.inc_nonce();
|
||||
|
||||
EncryptedPacket::from_encrypted_full_bytes(bytes)
|
||||
}
|
||||
|
||||
fn inc_nonce(&mut self) {
|
||||
let mut carry = 1;
|
||||
for i in (0..self.nonce.len()).rev() {
|
||||
let n = self.nonce[i] as u16 + carry;
|
||||
self.nonce[i] = n as u8;
|
||||
carry = n >> 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
mod keys;
|
||||
mod crypto;
|
||||
pub mod packet;
|
||||
pub mod parse;
|
||||
|
||||
|
|
@ -6,7 +6,7 @@ use core::str;
|
|||
use std::{collections::VecDeque, mem::take};
|
||||
|
||||
use ed25519_dalek::ed25519::signature::Signer;
|
||||
use keys::{AlgorithmName, AlgorithmNegotiation, EncryptionAlgorithm};
|
||||
use crypto::{AlgorithmName, AlgorithmNegotiation, EncryptionAlgorithm};
|
||||
use packet::{
|
||||
DhKeyExchangeInitReplyPacket, KeyExchangeEcDhInitPacket, KeyExchangeInitPacket, Packet,
|
||||
PacketTransport, SshPublicKey, SshSignature,
|
||||
|
|
@ -63,7 +63,7 @@ enum ServerState {
|
|||
client_identification: Vec<u8>,
|
||||
client_kexinit: Vec<u8>,
|
||||
server_kexinit: Vec<u8>,
|
||||
kex_algorithm: keys::KexAlgorithm,
|
||||
kex_algorithm: crypto::KexAlgorithm,
|
||||
encryption_client_to_server: EncryptionAlgorithm,
|
||||
encryption_server_to_client: EncryptionAlgorithm,
|
||||
},
|
||||
|
|
@ -179,7 +179,7 @@ impl ServerConnection {
|
|||
};
|
||||
|
||||
let kex_algorithms = AlgorithmNegotiation {
|
||||
supported: vec![keys::KEX_CURVE_25519_SHA256, keys::KEX_ECDH_SHA2_NISTP256],
|
||||
supported: vec![crypto::KEX_CURVE_25519_SHA256, crypto::KEX_ECDH_SHA2_NISTP256],
|
||||
};
|
||||
let kex_algorithm = kex_algorithms.find(kex.kex_algorithms.0)?;
|
||||
|
||||
|
|
@ -187,13 +187,12 @@ impl ServerConnection {
|
|||
require_algorithm("ssh-ed25519", kex.server_host_key_algorithms)?;
|
||||
|
||||
let encryption_algorithms_client_to_server = AlgorithmNegotiation {
|
||||
supported: vec![keys::ENC_CHACHA20POLY1305],
|
||||
supported: vec![crypto::ENC_CHACHA20POLY1305, crypto::ENC_AES256_GCM],
|
||||
};
|
||||
let encryption_algorithms_server_to_client = AlgorithmNegotiation {
|
||||
supported: vec![keys::ENC_CHACHA20POLY1305],
|
||||
supported: vec![crypto::ENC_CHACHA20POLY1305, crypto::ENC_AES256_GCM],
|
||||
};
|
||||
|
||||
// TODO: support aes256-gcm@openssh.com
|
||||
let encryption_client_to_server = encryption_algorithms_client_to_server
|
||||
.find(kex.encryption_algorithms_client_to_server.0)?;
|
||||
let encryption_server_to_client = encryption_algorithms_server_to_client
|
||||
|
|
@ -270,7 +269,7 @@ impl ServerConnection {
|
|||
|
||||
let client_public_key = dh.qc;
|
||||
|
||||
let keys::KexAlgorithmOutput {
|
||||
let crypto::KexAlgorithmOutput {
|
||||
server_public_key,
|
||||
shared_secret,
|
||||
} = (kex_algorithm.exchange)(client_public_key, &mut *self.rng)?;
|
||||
|
|
@ -289,7 +288,7 @@ impl ServerConnection {
|
|||
add_hash(hash, bytes);
|
||||
};
|
||||
let hash_mpint = |hash: &mut sha2::Sha256, bytes: &[u8]| {
|
||||
keys::encode_mpint_for_hash(bytes, |data| add_hash(hash, data));
|
||||
crypto::encode_mpint_for_hash(bytes, |data| add_hash(hash, data));
|
||||
};
|
||||
|
||||
hash_string(
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ mod ctors;
|
|||
use std::collections::VecDeque;
|
||||
|
||||
use crate::client_error;
|
||||
use crate::keys::{EncryptionAlgorithm, Keys, Plaintext, Session};
|
||||
use crate::crypto::{EncryptionAlgorithm, Keys, Plaintext, Session};
|
||||
use crate::parse::{NameList, Parser, Writer};
|
||||
use crate::Result;
|
||||
|
||||
|
|
@ -33,7 +33,7 @@ impl Msg {
|
|||
pub fn to_bytes(self) -> Vec<u8> {
|
||||
match self.0 {
|
||||
MsgKind::ServerProtocolInfo => crate::SERVER_IDENTIFICATION.to_vec(),
|
||||
MsgKind::PlaintextPacket(v) => v.to_bytes(true),
|
||||
MsgKind::PlaintextPacket(v) => v.to_bytes(true, Packet::DEFAULT_BLOCK_SIZE),
|
||||
MsgKind::EncryptedPacket(v) => v.into_bytes(),
|
||||
}
|
||||
}
|
||||
|
|
@ -199,6 +199,8 @@ impl Packet {
|
|||
pub const SSH_MSG_CHANNEL_SUCCESS: u8 = 99;
|
||||
pub const SSH_MSG_CHANNEL_FAILURE: u8 = 100;
|
||||
|
||||
pub const DEFAULT_BLOCK_SIZE: u8 = 8;
|
||||
|
||||
pub(crate) fn from_full(bytes: &[u8]) -> Result<Self> {
|
||||
let Some(padding_length) = bytes.first() else {
|
||||
return Err(client_error!("empty packet"));
|
||||
|
|
@ -219,17 +221,20 @@ impl Packet {
|
|||
})
|
||||
}
|
||||
|
||||
pub(crate) fn to_bytes(&self, respect_len_for_padding: bool) -> Vec<u8> {
|
||||
pub(crate) fn to_bytes(&self, respect_len_for_padding: bool, block_size: u8) -> Vec<u8> {
|
||||
assert!(block_size.is_power_of_two());
|
||||
|
||||
let let_bytes = if respect_len_for_padding { 4 } else { 0 };
|
||||
|
||||
// <https://datatracker.ietf.org/doc/html/rfc4253#section-6>
|
||||
let min_full_length = self.payload.len() + let_bytes + 1;
|
||||
|
||||
// The padding must give a factor of 8.
|
||||
let min_padding_len = (min_full_length.next_multiple_of(8) - min_full_length) as u8;
|
||||
// The padding must give a factor of block_size.
|
||||
let min_padding_len =
|
||||
(min_full_length.next_multiple_of(block_size as usize) - min_full_length) as u8;
|
||||
// > There MUST be at least four bytes of padding.
|
||||
let padding_len = if min_padding_len < 4 {
|
||||
min_padding_len + 8
|
||||
min_padding_len + block_size
|
||||
} else {
|
||||
min_padding_len
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue