Skip to content

Commit ffc18f4

Browse files
committed
Fix exchange with keys that had Q automatically computed
fixes #10790 closes #10864
1 parent 48df2eb commit ffc18f4

File tree

4 files changed

+67
-10
lines changed

4 files changed

+67
-10
lines changed

docs/development/test-vectors.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,10 @@ Key exchange
224224
* ``vectors/cryptoraphy_vectors/asymmetric/ECDH/brainpool.txt`` contains
225225
Brainpool vectors from :rfc:`7027`.
226226

227+
* ``vectors/cryptography_vectors/asymmetric/DH/dhpub_cryptography_old.pem``
228+
contains a Diffie-Hellman public key generated with a previous version of
229+
``cryptography``.
230+
227231
X.509
228232
~~~~~
229233

src/rust/src/backend/dh.rs

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ pub(crate) struct DHPublicKey {
2525
#[pyo3::pyclass(frozen, module = "cryptography.hazmat.bindings._rust.openssl.dh")]
2626
struct DHParameters {
2727
dh: openssl::dh::Dh<openssl::pkey::Params>,
28+
29+
is_dhx: bool,
2830
}
2931

3032
#[pyo3::pyfunction]
@@ -51,7 +53,7 @@ fn generate_parameters(
5153

5254
let dh = openssl::dh::Dh::generate_params(key_size, generator)
5355
.map_err(|_| pyo3::exceptions::PyValueError::new_err("Unable to generate DH parameters"))?;
54-
Ok(DHParameters { dh })
56+
Ok(DHParameters { dh, is_dhx: false })
5557
}
5658

5759
pub(crate) fn private_key_from_pkey(
@@ -73,12 +75,14 @@ pub(crate) fn public_key_from_pkey(
7375
#[cfg(not(CRYPTOGRAPHY_IS_BORINGSSL))]
7476
fn pkey_from_dh<T: openssl::pkey::HasParams>(
7577
dh: openssl::dh::Dh<T>,
78+
is_dhx: bool,
7679
) -> CryptographyResult<openssl::pkey::PKey<T>> {
7780
cfg_if::cfg_if! {
7881
if #[cfg(CRYPTOGRAPHY_IS_LIBRESSL)] {
82+
let _ = is_dhx;
7983
Ok(openssl::pkey::PKey::from_dh(dh)?)
8084
} else {
81-
if dh.prime_q().is_some() {
85+
if is_dhx {
8286
Ok(openssl::pkey::PKey::from_dhx(dh)?)
8387
} else {
8488
Ok(openssl::pkey::PKey::from_dh(dh)?)
@@ -87,6 +91,17 @@ fn pkey_from_dh<T: openssl::pkey::HasParams>(
8791
}
8892
}
8993

94+
fn is_dhx(id: openssl::pkey::Id) -> bool {
95+
cfg_if::cfg_if! {
96+
if #[cfg(any(CRYPTOGRAPHY_IS_LIBRESSL, CRYPTOGRAPHY_IS_BORINGSSL))] {
97+
let _ = id;
98+
false
99+
} else {
100+
id == openssl::pkey::Id::DHX
101+
}
102+
}
103+
}
104+
90105
#[pyo3::pyfunction]
91106
#[pyo3(signature = (data, backend=None))]
92107
fn from_der_parameters(
@@ -105,6 +120,7 @@ fn from_der_parameters(
105120

106121
Ok(DHParameters {
107122
dh: openssl::dh::Dh::from_pqg(p, q, g)?,
123+
is_dhx: asn1_params.q.is_some(),
108124
})
109125
}
110126

@@ -214,14 +230,18 @@ impl DHPrivateKey {
214230
let orig_dh = self.pkey.dh().unwrap();
215231
let dh = clone_dh(&orig_dh)?;
216232

217-
let pkey = pkey_from_dh(dh.set_public_key(orig_dh.public_key().to_owned()?)?)?;
233+
let pkey = pkey_from_dh(
234+
dh.set_public_key(orig_dh.public_key().to_owned()?)?,
235+
is_dhx(self.pkey.id()),
236+
)?;
218237

219238
Ok(DHPublicKey { pkey })
220239
}
221240

222241
fn parameters(&self) -> CryptographyResult<DHParameters> {
223242
Ok(DHParameters {
224243
dh: clone_dh(&self.pkey.dh().unwrap())?,
244+
is_dhx: is_dhx(self.pkey.id()),
225245
})
226246
}
227247

@@ -280,6 +300,8 @@ impl DHPublicKey {
280300
fn parameters(&self) -> CryptographyResult<DHParameters> {
281301
Ok(DHParameters {
282302
dh: clone_dh(&self.pkey.dh().unwrap())?,
303+
304+
is_dhx: is_dhx(self.pkey.id()),
283305
})
284306
}
285307

@@ -322,7 +344,7 @@ impl DHParameters {
322344
fn generate_private_key(&self) -> CryptographyResult<DHPrivateKey> {
323345
let dh = clone_dh(&self.dh)?.generate_key()?;
324346
Ok(DHPrivateKey {
325-
pkey: pkey_from_dh(dh)?,
347+
pkey: pkey_from_dh(dh, self.is_dhx)?,
326348
})
327349
}
328350

@@ -421,9 +443,11 @@ impl DHPrivateNumbers {
421443
) -> CryptographyResult<DHPrivateKey> {
422444
let _ = backend;
423445

424-
let dh = dh_parameters_from_numbers(py, self.public_numbers.get().parameter_numbers.get())?;
446+
let public_numbers = self.public_numbers.get();
447+
let parameter_numbers = public_numbers.parameter_numbers.get();
448+
let dh = dh_parameters_from_numbers(py, parameter_numbers)?;
425449

426-
let pub_key = utils::py_int_to_bn(py, self.public_numbers.get().y.bind(py))?;
450+
let pub_key = utils::py_int_to_bn(py, public_numbers.y.bind(py))?;
427451
let priv_key = utils::py_int_to_bn(py, self.x.bind(py))?;
428452

429453
let dh = dh.set_key(pub_key, priv_key)?;
@@ -435,7 +459,7 @@ impl DHPrivateNumbers {
435459
));
436460
}
437461

438-
let pkey = pkey_from_dh(dh)?;
462+
let pkey = pkey_from_dh(dh, parameter_numbers.q.is_some())?;
439463
Ok(DHPrivateKey { pkey })
440464
}
441465

@@ -474,11 +498,12 @@ impl DHPublicNumbers {
474498
) -> CryptographyResult<DHPublicKey> {
475499
let _ = backend;
476500

477-
let dh = dh_parameters_from_numbers(py, self.parameter_numbers.get())?;
501+
let parameter_numbers = self.parameter_numbers.get();
502+
let dh = dh_parameters_from_numbers(py, parameter_numbers)?;
478503

479504
let pub_key = utils::py_int_to_bn(py, self.y.bind(py))?;
480505

481-
let pkey = pkey_from_dh(dh.set_public_key(pub_key)?)?;
506+
let pkey = pkey_from_dh(dh.set_public_key(pub_key)?, parameter_numbers.q.is_some())?;
482507

483508
Ok(DHPublicKey { pkey })
484509
}
@@ -535,7 +560,10 @@ impl DHParameterNumbers {
535560
let _ = backend;
536561

537562
let dh = dh_parameters_from_numbers(py, self)?;
538-
Ok(DHParameters { dh })
563+
Ok(DHParameters {
564+
dh,
565+
is_dhx: self.q.is_some(),
566+
})
539567
}
540568

541569
fn __eq__(

tests/hazmat/primitives/test_dh.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,16 @@ def test_dh_vectors_with_q(self, backend, vector):
441441
assert int.from_bytes(symkey1, "big") == int(vector["z"], 16)
442442
assert int.from_bytes(symkey2, "big") == int(vector["z"], 16)
443443

444+
def test_exchange_old_key(self, backend):
445+
k = load_vectors_from_file(
446+
os.path.join("asymmetric", "DH", "dhpub_cryptography_old.pem"),
447+
lambda f: serialization.load_pem_public_key(f.read()),
448+
mode="rb",
449+
)
450+
assert isinstance(k, dh.DHPublicKey)
451+
# Ensure this doesn't raise.
452+
k.parameters().generate_private_key().exchange(k)
453+
444454
def test_public_key_equality(self, backend):
445455
key_bytes = load_vectors_from_file(
446456
os.path.join("asymmetric", "DH", "dhpub.pem"),
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-----BEGIN PUBLIC KEY-----
2+
MIICJTCCARcGCSqGSIb3DQEDATCCAQgCggEBAP//////////yQ/aoiFowjTExmKL
3+
gNwc0SkCTgiKZ8x0Agu+pjsTmyJRSgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVt
4+
bVHCReSFtXZiXn7G9ExC6aY37WsL/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR
5+
7ORbPcIAfLihY78FmNpINhxV05ppFj+o/STPX4NlXSPco62WHGLzViCFUrue1SkH
6+
cJaWbWcMNU5KvJgE8XRsCMoYIXwykF5GLjbOO+OedywYDoY DmyeDouwHoo+1xV3w
7+
b0xSyd4ry/aVWBcYOZVJfOqVauUV0iYYmPoFEBVyjlqKrKpo//////////8CAQID
8+
ggEGAAKCAQEAoely6vSHw+/Q3zGYLaJj7eeQkfd25K8SvtC+FMY9D7jwS4g71pyr
9+
U3FJ98Fi45Wdksh+d4u7U089trF5Xbgui29bZ0HcQZtfHEEz0Mh69tkipCm2/QIj
10+
6eDlo6sPk9hhhvgg4MMGiWKhCtHrub3x1FHdmf7KjOhrGeb5apiudo7blGFzGhZ3
11+
NFnbff+ArVNd+rdVmSoZn0aMhXRConlDu/44IYe5/24VLl7G+BzZlIZO4P2M83fd
12+
mBOvR13cmYssQjEFTbaZVQvQHa3t0+aywfdCgsXGmTTK6QDCBP8D+vf1bmhEswzs
13+
oYn1GLtJ3VyYyMBPDBomd2ctchZgTzsX1w==
14+
-----END PUBLIC KEY-----
15+

0 commit comments

Comments
 (0)