Skip to content

Commit 45d2d82

Browse files
committed
GHSA-cqvm-j2r2-hwpg validate DH key range
1 parent 760704b commit 45d2d82

File tree

2 files changed

+59
-15
lines changed

2 files changed

+59
-15
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 & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ mod groups;
22
use std::marker::PhantomData;
33

44
use byteorder::{BigEndian, ByteOrder};
5-
use log::debug;
65
use digest::Digest;
76
use groups::DH;
7+
use log::debug;
88
use num_bigint::BigUint;
99
use russh_cryptovec::CryptoVec;
1010
use russh_keys::encoding::Encoding;
@@ -105,16 +105,27 @@ impl<D: Digest> KexAlgorithm for DhGroupKex<D> {
105105

106106
debug!("client_pubkey: {:?}", client_pubkey);
107107

108-
self.dh.generate_private_key();
109-
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);
110115

111116
// fill exchange.
112117
exchange.server_ephemeral.clear();
113-
exchange.server_ephemeral.extend(&server_pubkey);
118+
exchange.server_ephemeral.extend(&encoded_server_pubkey);
114119

115-
let shared = self
116-
.dh
117-
.compute_shared_secret(DH::decode_public_key(client_pubkey));
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+
}
124+
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+
}
118129
self.shared_secret = Some(biguint_to_mpint(&shared));
119130
Ok(())
120131
}
@@ -125,22 +136,35 @@ impl<D: Digest> KexAlgorithm for DhGroupKex<D> {
125136
client_ephemeral: &mut CryptoVec,
126137
buf: &mut CryptoVec,
127138
) -> Result<(), crate::Error> {
128-
self.dh.generate_private_key();
129-
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+
}
130145

131146
// fill exchange.
147+
let encoded_pubkey = biguint_to_mpint(client_pubkey);
132148
client_ephemeral.clear();
133-
client_ephemeral.extend(&client_pubkey);
149+
client_ephemeral.extend(&encoded_pubkey);
134150

135151
buf.push(msg::KEX_ECDH_INIT);
136-
buf.extend_ssh_string(&client_pubkey);
152+
buf.extend_ssh_string(&encoded_pubkey);
137153

138154
Ok(())
139155
}
140156

141157
fn compute_shared_secret(&mut self, remote_pubkey_: &[u8]) -> Result<(), crate::Error> {
142158
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+
143164
let shared = self.dh.compute_shared_secret(remote_pubkey);
165+
if !self.dh.validate_shared_secret(&shared) {
166+
return Err(crate::Error::Inconsistent);
167+
}
144168
self.shared_secret = Some(biguint_to_mpint(&shared));
145169
Ok(())
146170
}

0 commit comments

Comments
 (0)