Skip to content

Commit d831a37

Browse files
committed
GHSA-cqvm-j2r2-hwpg validate DH key range
1 parent 1fbba50 commit d831a37

File tree

2 files changed

+59
-14
lines changed

2 files changed

+59
-14
lines changed

russh/src/kex/dh/groups.rs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use std::ops::Shl;
2-
31
use hex_literal::hex;
42
use num_bigint::{BigUint, RandBigInt};
53
use rand;
@@ -69,9 +67,17 @@ impl DH {
6967
}
7068
}
7169

72-
pub fn generate_private_key(&mut self) -> BigUint {
70+
pub fn generate_private_key(&mut self, is_server: bool) -> BigUint {
71+
let q = (&self.prime_num - &BigUint::from(1u8)) / &BigUint::from(2u8);
7372
let mut rng = rand::thread_rng();
74-
self.private_key = rng.gen_biguint((self.exp_size * 8) - 2u64).shl(1);
73+
self.private_key = rng.gen_biguint_range(
74+
&if is_server {
75+
1u8.into()
76+
} else {
77+
2u8.into()
78+
},
79+
&q,
80+
);
7581
self.private_key.clone()
7682
}
7783

@@ -85,7 +91,21 @@ impl DH {
8591
self.shared_secret.clone()
8692
}
8793

94+
pub fn validate_shared_secret(&self, shared_secret: &BigUint) -> bool {
95+
let one = BigUint::from(1u8);
96+
let prime_minus_one = &self.prime_num - &one;
97+
98+
shared_secret > &one && shared_secret < &prime_minus_one
99+
}
100+
88101
pub fn decode_public_key(buffer: &[u8]) -> BigUint {
89102
BigUint::from_bytes_be(buffer)
90103
}
104+
105+
pub fn validate_public_key(&self, public_key: &BigUint) -> bool {
106+
let one = BigUint::from(1u8);
107+
let prime_minus_one = &self.prime_num - &one;
108+
109+
public_key > &one && public_key < &prime_minus_one
110+
}
91111
}

russh/src/kex/dh/mod.rs

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::marker::PhantomData;
44
use byteorder::{BigEndian, ByteOrder};
55
use digest::Digest;
66
use groups::DH;
7+
use log::debug;
78
use num_bigint::BigUint;
89
use russh_cryptovec::CryptoVec;
910
use russh_keys::encoding::Encoding;
@@ -104,16 +105,27 @@ impl<D: Digest> KexAlgorithm for DhGroupKex<D> {
104105

105106
debug!("client_pubkey: {:?}", client_pubkey);
106107

107-
self.dh.generate_private_key();
108-
let server_pubkey = biguint_to_mpint(&self.dh.generate_public_key());
108+
self.dh.generate_private_key(true);
109+
let server_pubkey = &self.dh.generate_public_key();
110+
if !self.dh.validate_public_key(server_pubkey) {
111+
return Err(crate::Error::Inconsistent);
112+
}
113+
114+
let encoded_server_pubkey = biguint_to_mpint(server_pubkey);
109115

110116
// fill exchange.
111117
exchange.server_ephemeral.clear();
112-
exchange.server_ephemeral.extend(&server_pubkey);
118+
exchange.server_ephemeral.extend(&encoded_server_pubkey);
119+
120+
let decoded_client_pubkey = DH::decode_public_key(client_pubkey);
121+
if !self.dh.validate_public_key(&decoded_client_pubkey) {
122+
return Err(crate::Error::Inconsistent);
123+
}
113124

114-
let shared = self
115-
.dh
116-
.compute_shared_secret(DH::decode_public_key(client_pubkey));
125+
let shared = self.dh.compute_shared_secret(decoded_client_pubkey);
126+
if !self.dh.validate_shared_secret(&shared) {
127+
return Err(crate::Error::Inconsistent);
128+
}
117129
self.shared_secret = Some(biguint_to_mpint(&shared));
118130
Ok(())
119131
}
@@ -124,22 +136,35 @@ impl<D: Digest> KexAlgorithm for DhGroupKex<D> {
124136
client_ephemeral: &mut CryptoVec,
125137
buf: &mut CryptoVec,
126138
) -> Result<(), crate::Error> {
127-
self.dh.generate_private_key();
128-
let client_pubkey = biguint_to_mpint(&self.dh.generate_public_key());
139+
self.dh.generate_private_key(false);
140+
let client_pubkey = &self.dh.generate_public_key();
141+
142+
if !self.dh.validate_public_key(client_pubkey) {
143+
return Err(crate::Error::Inconsistent);
144+
}
129145

130146
// fill exchange.
147+
let encoded_pubkey = biguint_to_mpint(client_pubkey);
131148
client_ephemeral.clear();
132-
client_ephemeral.extend(&client_pubkey);
149+
client_ephemeral.extend(&encoded_pubkey);
133150

134151
buf.push(msg::KEX_ECDH_INIT);
135-
buf.extend_ssh_string(&client_pubkey);
152+
buf.extend_ssh_string(&encoded_pubkey);
136153

137154
Ok(())
138155
}
139156

140157
fn compute_shared_secret(&mut self, remote_pubkey_: &[u8]) -> Result<(), crate::Error> {
141158
let remote_pubkey = DH::decode_public_key(remote_pubkey_);
159+
160+
if !self.dh.validate_public_key(&remote_pubkey) {
161+
return Err(crate::Error::Inconsistent);
162+
}
163+
142164
let shared = self.dh.compute_shared_secret(remote_pubkey);
165+
if !self.dh.validate_shared_secret(&shared) {
166+
return Err(crate::Error::Inconsistent);
167+
}
143168
self.shared_secret = Some(biguint_to_mpint(&shared));
144169
Ok(())
145170
}

0 commit comments

Comments
 (0)