Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
683 changes: 511 additions & 172 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ tokio = { version = "1.51.1", features = ["full"] }
# Use our internal russh fork with session loop fixes
# - Development: uses local path (crates/bssh-russh)
# - Publishing: uses crates.io version (path ignored)
russh = { package = "bssh-russh", version = "0.59", path = "crates/bssh-russh" }
russh = { package = "bssh-russh", version = "0.60", path = "crates/bssh-russh" }
russh-sftp = "2.1.1"
clap = { version = "4.6.0", features = ["derive", "env"] }
anyhow = "1.0.102"
Expand Down Expand Up @@ -67,7 +67,7 @@ libc = "0.2"
ipnetwork = "0.21"
bcrypt = "0.19"
argon2 = "0.5"
rand = "0.9"
rand = "0.10"
ssh-key = { version = "0.6", features = ["std"] }
async-compression = { version = "0.4", features = ["tokio", "gzip"] }
serde_json = "1.0"
Expand Down
49 changes: 28 additions & 21 deletions crates/bssh-russh/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bssh-russh"
version = "0.59.0"
version = "0.60.0"
authors = ["Jeongkyu Shin <inureyes@gmail.com>"]
description = "Temporary fork of russh with high-frequency PTY output fix (Handle::data from spawned tasks)"
documentation = "https://docs.rs/bssh-russh"
Expand Down Expand Up @@ -31,17 +31,19 @@ bitflags = "2.0"
block-padding = { version = "0.3", features = ["std"] }
byteorder = "1.4"
bytes = "1.7"
cbc = "0.1"
cbc = { version = "0.1" }
cipher = "0.5.1"
ctr = "0.9"
curve25519-dalek = "4.1.3"
curve25519-dalek = "5.0.0-pre.6"
crypto-bigint = { version = "0.7.0-rc.28", features = ["alloc"] }
data-encoding = "2.3"
delegate = "0.13"
digest = "0.10"
der = "0.7"
der = "0.8"
des = { version = "0.8.1", optional = true }
ecdsa = "0.16"
ed25519-dalek = { version = "2.0", features = ["rand_core", "pkcs8"] }
elliptic-curve = { version = "0.13", features = ["ecdh"] }
ecdsa = "0.17.0-rc.16"
ed25519-dalek = { version = "3.0.0-pre.6", features = ["alloc", "rand_core", "pkcs8"] }
elliptic-curve = { version = "0.14.0-rc.28", features = ["ecdh"] }
enum_dispatch = "0.3.13"
flate2 = { version = "1.0.15", optional = true }
futures = "0.3"
Expand All @@ -52,29 +54,34 @@ hmac = "0.12"
inout = { version = "0.1", features = ["std"] }
log = "0.4"
md5 = "0.7"
ml-kem = "0.2.3"
num-bigint = { version = "0.4.2", features = ["rand"] }
p256 = { version = "0.13", features = ["ecdh"] }
p384 = { version = "0.13", features = ["ecdh"] }
p521 = { version = "0.13", features = ["ecdh"] }
ml-kem = "0.3.0-rc.1"
module-lattice = "0.2"
# num-bigint 0.4.x only supports rand 0.8; upstream russh ships a fork that
# adds rand 0.10 support via the `rand_0_10` feature flag.
num-bigint = { package = "internal-russh-num-bigint", version = "=0.5.0", features = ["rand_0_10"] }
p256 = { version = "0.14.0-rc.7", features = ["ecdh"] }
p384 = { version = "0.14.0-rc.7", features = ["ecdh"] }
p521 = { version = "0.14.0-rc.7", features = ["ecdh"] }
pbkdf2 = "0.12"
pkcs1 = { version = "0.8.0-rc.4", optional = true }
pkcs5 = "0.7"
pkcs8 = { version = "0.10", features = ["pkcs5", "encryption", "std"] }
rand_core = { version = "=0.10.0-rc-3" }
rand = { version = "0.9", features = ["thread_rng"] }
pkcs5 = "0.8.0-rc.13"
pkcs8 = { version = "0.11.0-rc.11", features = ["encryption", "std"] }
polyval = "0.7.1"
rand_core = { version = "0.10.0" }
rand = { version = "0.10", features = ["thread_rng"] }
ring = { version = "0.17.14", optional = true }
rsa = { version = "0.10.0-rc.10", optional = true }
sec1 = { version = "0.7", features = ["pkcs8", "der"] }
sec1 = { version = "0.8", features = ["der"] }
sha1 = { version = "0.10.5", features = ["oid"] }
sha2 = { version = "0.10.6", features = ["oid"] }
signature = "2.2"
spki = "0.7"
signature = "3.0.0-rc.10"
spki = "0.8.0-rc.4"
ssh-encoding = { version = "0.2", features = ["bytes"] }
subtle = "2.4"
thiserror = "2.0.18"
tokio = { version = "1.50.0", features = ["io-util", "sync", "time", "rt-multi-thread", "net"] }
tokio = { version = "1.51.1", features = ["io-util", "sync", "time", "rt-multi-thread", "net"] }
typenum = "1.17"
universal-hash = "0.6.1"
yasna = { version = "0.5.0", features = ["bit-vec", "num-bigint"], optional = true }
zeroize = "1.7"

Expand All @@ -83,7 +90,7 @@ russh-cryptovec = { version = "0.59.0", features = ["ssh-encoding"] }
russh-util = "0.52.0"

# Use the forked ssh-key from russh
ssh-key = { version = "=0.6.16", features = [
ssh-key = { version = "=0.6.18", features = [
"ed25519",
"p256",
"p384",
Expand Down
4 changes: 2 additions & 2 deletions crates/bssh-russh/src/cipher/benchmark.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#![allow(clippy::unwrap_used)]
use criterion::*;
use rand::TryRngCore;
use rand_core::TryRng;
use std::hint;

pub fn bench(c: &mut Criterion) {
let mut rand_generator = hint::black_box(rand::rngs::OsRng {});
let mut rand_generator = hint::black_box(rand::rng());

let mut packet_length = hint::black_box(vec![0u8; 4]);

Expand Down
2 changes: 1 addition & 1 deletion crates/bssh-russh/src/cipher/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use std::marker::PhantomData;
use aes::cipher::{IvSizeUser, KeyIvInit, KeySizeUser, StreamCipher};
#[allow(deprecated)]
use digest::generic_array::GenericArray as GenericArray_0_14;
use rand::RngCore;
use rand_core::Rng;

use super::super::Error;
use super::PACKET_LENGTH_LEN;
Expand Down
2 changes: 1 addition & 1 deletion crates/bssh-russh/src/cipher/gcm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use aws_lc_rs::{
},
error::Unspecified,
};
use rand::RngCore;
use rand_core::Rng;
#[cfg(all(not(feature = "aws-lc-rs"), feature = "ring"))]
use ring::{
aead::{
Expand Down
17 changes: 11 additions & 6 deletions crates/bssh-russh/src/client/kex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,18 @@ use std::sync::Arc;

use bytes::Bytes;
use log::{debug, error, warn};
use signature::Verifier;
use ssh_encoding::{Decode, Encode};
use ssh_key::{Mpint, PublicKey, Signature};

use super::IncomingSshPacket;
use crate::client::{Config, NewKeys};
use crate::kex::dh::groups::DhGroup;
use crate::kex::{KexAlgorithm, KexAlgorithmImplementor, KexCause, KexProgress, KEXES};
use crate::kex::{KEXES, KexAlgorithm, KexAlgorithmImplementor, KexCause, KexProgress};
use crate::keys::key::parse_public_key;
use crate::negotiation::{Names, Select};
use crate::session::Exchange;
use crate::sshbuffer::PacketWriter;
use crate::{msg, negotiation, strict_kex_violation, CryptoVec, Error, SshId};
use crate::{CryptoVec, Error, SshId, msg, negotiation, strict_kex_violation};

thread_local! {
static HASH_BUFFER: RefCell<CryptoVec> = RefCell::new(CryptoVec::new());
Expand Down Expand Up @@ -116,7 +115,9 @@ impl ClientKex {

let names = {
// read algorithms from packet.
self.exchange.server_kex_init.extend_from_slice(&input.buffer);
self.exchange
.server_kex_init
.extend_from_slice(&input.buffer);
negotiation::Client::read_kex(
&input.buffer,
&self.config.preferred,
Expand Down Expand Up @@ -270,7 +271,9 @@ impl ClientKex {
);

let server_ephemeral = Bytes::decode(r)?;
self.exchange.server_ephemeral.extend_from_slice(&server_ephemeral);
self.exchange
.server_ephemeral
.extend_from_slice(&server_ephemeral);
kex.compute_shared_secret(&self.exchange.server_ephemeral)?;

let mut pubkey_vec = Vec::new();
Expand All @@ -288,7 +291,9 @@ impl ClientKex {
let signature = Bytes::decode(r)?;
let signature = Signature::decode(&mut &signature[..])?;

if let Err(e) = Verifier::verify(&server_host_key, hash.as_ref(), &signature) {
if let Err(e) =
signature::Verifier::verify(&server_host_key, hash.as_ref(), &signature)
{
debug!("wrong server sig: {e:?}");
return Err(Error::WrongServerSig);
}
Expand Down
79 changes: 67 additions & 12 deletions crates/bssh-russh/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1685,22 +1685,42 @@ impl GexParams {
preferred_group_size: usize,
max_group_size: usize,
) -> Result<Self, Error> {
let this = Self {
Self::for_client_config(min_group_size, preferred_group_size, max_group_size)
}

pub fn for_client_config(
min_group_size: usize,
preferred_group_size: usize,
max_group_size: usize,
) -> Result<Self, Error> {
Self::build(
min_group_size,
preferred_group_size,
max_group_size,
};
this.validate()?;
Ok(this)
}

pub(crate) fn validate(&self) -> Result<(), Error> {
if self.min_group_size < 2048 {
return Err(Error::InvalidConfig(format!(
"min_group_size must be at least 2048 bits. We got {} bits",
self.min_group_size
)));
ValidationKind::ClientConfig,
)
}

fn validate(&self, kind: ValidationKind) -> Result<(), Error> {
match kind {
ValidationKind::ClientConfig => {
if self.min_group_size < 2048 {
return Err(Error::InvalidConfig(format!(
"min_group_size must be at least 2048 bits. We got {} bits",
self.min_group_size
)));
}
}
ValidationKind::PeerRequest => {
if self.max_group_size < 2048 {
return Err(Error::InvalidConfig(format!(
"max_group_size must be at least 2048 bits. We got {} bits",
self.max_group_size
)));
}
}
}

if self.preferred_group_size < self.min_group_size {
return Err(Error::InvalidConfig(format!(
"preferred_group_size must be at least as large as min_group_size. We have preferred_group_size = {} < min_group_size = {}",
Expand All @@ -1713,9 +1733,38 @@ impl GexParams {
self.max_group_size, self.preferred_group_size
)));
}

Ok(())
}

pub(crate) fn from_peer_request(
min_group_size: usize,
preferred_group_size: usize,
max_group_size: usize,
) -> Result<Self, Error> {
Self::build(
min_group_size,
preferred_group_size,
max_group_size,
ValidationKind::PeerRequest,
)
}

fn build(
min_group_size: usize,
preferred_group_size: usize,
max_group_size: usize,
kind: ValidationKind,
) -> Result<Self, Error> {
let this = Self {
min_group_size,
preferred_group_size,
max_group_size,
};
this.validate(kind)?;
Ok(this)
}

pub fn min_group_size(&self) -> usize {
self.min_group_size
}
Expand All @@ -1729,6 +1778,12 @@ impl GexParams {
}
}

#[derive(Clone, Copy, Eq, PartialEq)]
enum ValidationKind {
ClientConfig,
PeerRequest,
}

impl Default for GexParams {
fn default() -> GexParams {
GexParams {
Expand Down
6 changes: 3 additions & 3 deletions crates/bssh-russh/src/client/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ mod tests {
// Import client types directly since we're in the client module
use crate::client::{Config, Handler, connect};
use crate::keys::PrivateKeyWithHashAlg;
use crate::keys::ssh_key::rand_core::OsRng;
use rand::rng;
use crate::server::{self, Auth, Handler as ServerHandler, Server, Session};
use crate::{ChannelId, SshId}; // Import directly from crate root
use crate::Error;
Expand Down Expand Up @@ -82,7 +82,7 @@ mod tests {
let _ = env_logger::try_init();

// Create a client key
let client_key = PrivateKey::random(&mut OsRng, ssh_key::Algorithm::Ed25519).unwrap();
let client_key = PrivateKey::random(&mut rng(), ssh_key::Algorithm::Ed25519).unwrap();

// Configure the server
let mut config = server::Config::default();
Expand All @@ -91,7 +91,7 @@ mod tests {
config.inactivity_timeout = None;
config
.keys
.push(PrivateKey::random(&mut OsRng, ssh_key::Algorithm::Ed25519).unwrap());
.push(PrivateKey::random(&mut rng(), ssh_key::Algorithm::Ed25519).unwrap());
let config = Arc::new(config);

// Create server struct
Expand Down
7 changes: 3 additions & 4 deletions crates/bssh-russh/src/kex/dh/groups.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use std::fmt::Debug;
use std::ops::Deref;

use crate::keys::ssh_key::rand_core::OsRng;
use hex_literal::hex;
use num_bigint::{BigUint, RandBigInt};
use num_bigint::{BigRng010, BigUint};

#[derive(Clone)]
pub enum DhGroupUInt {
Expand Down Expand Up @@ -282,9 +281,9 @@ impl DH {

pub fn generate_private_key(&mut self, is_server: bool) -> BigUint {
let q = (&self.prime_num - &BigUint::from(1u8)) / &BigUint::from(2u8);
let mut rng = OsRng;
let mut rng = rand::rng();
self.private_key =
rng.gen_biguint_range(&if is_server { 1u8.into() } else { 2u8.into() }, &q);
rng.random_biguint_range(&if is_server { 1u8.into() } else { 2u8.into() }, &q);
self.private_key.clone()
}

Expand Down
2 changes: 1 addition & 1 deletion crates/bssh-russh/src/kex/dh/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ impl Decode for GexParams {
let min_group_size = u32::decode(reader)? as usize;
let preferred_group_size = u32::decode(reader)? as usize;
let max_group_size = u32::decode(reader)? as usize;
GexParams::new(min_group_size, preferred_group_size, max_group_size)
GexParams::from_peer_request(min_group_size, preferred_group_size, max_group_size)
}

type Error = Error;
Expand Down
Loading
Loading