Skip to content

Commit b5c25a9

Browse files
authored
Migrate DH to Rust (#8768)
1 parent 9d70b87 commit b5c25a9

File tree

15 files changed

+585
-543
lines changed

15 files changed

+585
-543
lines changed

src/cryptography/hazmat/backends/openssl/backend.py

Lines changed: 13 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,6 @@
1515
from cryptography.hazmat.backends.openssl import aead
1616
from cryptography.hazmat.backends.openssl.ciphers import _CipherContext
1717
from cryptography.hazmat.backends.openssl.cmac import _CMACContext
18-
from cryptography.hazmat.backends.openssl.dh import (
19-
_dh_params_dup,
20-
_DHParameters,
21-
_DHPrivateKey,
22-
_DHPublicKey,
23-
)
2418
from cryptography.hazmat.backends.openssl.dsa import (
2519
_DSAParameters,
2620
_DSAPrivateKey,
@@ -609,10 +603,9 @@ def _evp_pkey_to_private_key(
609603
ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free)
610604
return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey)
611605
elif key_type in self._dh_types:
612-
dh_cdata = self._lib.EVP_PKEY_get1_DH(evp_pkey)
613-
self.openssl_assert(dh_cdata != self._ffi.NULL)
614-
dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free)
615-
return _DHPrivateKey(self, dh_cdata, evp_pkey)
606+
return rust_openssl.dh.private_key_from_ptr(
607+
int(self._ffi.cast("uintptr_t", evp_pkey))
608+
)
616609
elif key_type == getattr(self._lib, "EVP_PKEY_ED25519", None):
617610
# EVP_PKEY_ED25519 is not present in CRYPTOGRAPHY_IS_LIBRESSL
618611
return rust_openssl.ed25519.private_key_from_ptr(
@@ -674,10 +667,9 @@ def _evp_pkey_to_public_key(self, evp_pkey) -> PublicKeyTypes:
674667
ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free)
675668
return _EllipticCurvePublicKey(self, ec_cdata, evp_pkey)
676669
elif key_type in self._dh_types:
677-
dh_cdata = self._lib.EVP_PKEY_get1_DH(evp_pkey)
678-
self.openssl_assert(dh_cdata != self._ffi.NULL)
679-
dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free)
680-
return _DHPublicKey(self, dh_cdata, evp_pkey)
670+
return rust_openssl.dh.public_key_from_ptr(
671+
int(self._ffi.cast("uintptr_t", evp_pkey))
672+
)
681673
elif key_type == getattr(self._lib, "EVP_PKEY_ED25519", None):
682674
# EVP_PKEY_ED25519 is not present in CRYPTOGRAPHY_IS_LIBRESSL
683675
return rust_openssl.ed25519.public_key_from_ptr(
@@ -925,16 +917,7 @@ def load_pem_public_key(self, data: bytes) -> PublicKeyTypes:
925917
self._handle_key_loading_error()
926918

927919
def load_pem_parameters(self, data: bytes) -> dh.DHParameters:
928-
mem_bio = self._bytes_to_bio(data)
929-
# only DH is supported currently
930-
dh_cdata = self._lib.PEM_read_bio_DHparams(
931-
mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL
932-
)
933-
if dh_cdata != self._ffi.NULL:
934-
dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free)
935-
return _DHParameters(self, dh_cdata)
936-
else:
937-
self._handle_key_loading_error()
920+
return rust_openssl.dh.from_pem_parameters(data)
938921

939922
def load_der_private_key(
940923
self,
@@ -1000,22 +983,7 @@ def load_der_public_key(self, data: bytes) -> PublicKeyTypes:
1000983
self._handle_key_loading_error()
1001984

1002985
def load_der_parameters(self, data: bytes) -> dh.DHParameters:
1003-
mem_bio = self._bytes_to_bio(data)
1004-
dh_cdata = self._lib.d2i_DHparams_bio(mem_bio.bio, self._ffi.NULL)
1005-
if dh_cdata != self._ffi.NULL:
1006-
dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free)
1007-
return _DHParameters(self, dh_cdata)
1008-
elif self._lib.Cryptography_HAS_EVP_PKEY_DHX:
1009-
# We check to see if the is dhx.
1010-
self._consume_errors()
1011-
res = self._lib.BIO_reset(mem_bio.bio)
1012-
self.openssl_assert(res == 1)
1013-
dh_cdata = self._lib.d2i_DHxparams_bio(mem_bio.bio, self._ffi.NULL)
1014-
if dh_cdata != self._ffi.NULL:
1015-
dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free)
1016-
return _DHParameters(self, dh_cdata)
1017-
1018-
self._handle_key_loading_error()
986+
return rust_openssl.dh.from_der_parameters(data)
1019987

1020988
def _cert2ossl(self, cert: x509.Certificate) -> typing.Any:
1021989
data = cert.public_bytes(serialization.Encoding.DER)
@@ -1611,48 +1579,12 @@ def dh_supported(self) -> bool:
16111579
def generate_dh_parameters(
16121580
self, generator: int, key_size: int
16131581
) -> dh.DHParameters:
1614-
if key_size < dh._MIN_MODULUS_SIZE:
1615-
raise ValueError(
1616-
"DH key_size must be at least {} bits".format(
1617-
dh._MIN_MODULUS_SIZE
1618-
)
1619-
)
1620-
1621-
if generator not in (2, 5):
1622-
raise ValueError("DH generator must be 2 or 5")
1623-
1624-
dh_param_cdata = self._lib.DH_new()
1625-
self.openssl_assert(dh_param_cdata != self._ffi.NULL)
1626-
dh_param_cdata = self._ffi.gc(dh_param_cdata, self._lib.DH_free)
1627-
1628-
res = self._lib.DH_generate_parameters_ex(
1629-
dh_param_cdata, key_size, generator, self._ffi.NULL
1630-
)
1631-
if res != 1:
1632-
errors = self._consume_errors()
1633-
raise ValueError("Unable to generate DH parameters", errors)
1634-
1635-
return _DHParameters(self, dh_param_cdata)
1636-
1637-
def _dh_cdata_to_evp_pkey(self, dh_cdata):
1638-
evp_pkey = self._create_evp_pkey_gc()
1639-
res = self._lib.EVP_PKEY_set1_DH(evp_pkey, dh_cdata)
1640-
self.openssl_assert(res == 1)
1641-
return evp_pkey
1582+
return rust_openssl.dh.generate_parameters(generator, key_size)
16421583

16431584
def generate_dh_private_key(
16441585
self, parameters: dh.DHParameters
16451586
) -> dh.DHPrivateKey:
1646-
dh_key_cdata = _dh_params_dup(
1647-
parameters._dh_cdata, self # type: ignore[attr-defined]
1648-
)
1649-
1650-
res = self._lib.DH_generate_key(dh_key_cdata)
1651-
self.openssl_assert(res == 1)
1652-
1653-
evp_pkey = self._dh_cdata_to_evp_pkey(dh_key_cdata)
1654-
1655-
return _DHPrivateKey(self, dh_key_cdata, evp_pkey)
1587+
return parameters.generate_private_key()
16561588

16571589
def generate_dh_private_key_and_parameters(
16581590
self, generator: int, key_size: int
@@ -1664,99 +1596,17 @@ def generate_dh_private_key_and_parameters(
16641596
def load_dh_private_numbers(
16651597
self, numbers: dh.DHPrivateNumbers
16661598
) -> dh.DHPrivateKey:
1667-
parameter_numbers = numbers.public_numbers.parameter_numbers
1668-
1669-
dh_cdata = self._lib.DH_new()
1670-
self.openssl_assert(dh_cdata != self._ffi.NULL)
1671-
dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free)
1672-
1673-
p = self._int_to_bn(parameter_numbers.p)
1674-
g = self._int_to_bn(parameter_numbers.g)
1675-
1676-
if parameter_numbers.q is not None:
1677-
q = self._int_to_bn(parameter_numbers.q)
1678-
else:
1679-
q = self._ffi.NULL
1680-
1681-
pub_key = self._int_to_bn(numbers.public_numbers.y)
1682-
priv_key = self._int_to_bn(numbers.x)
1683-
1684-
res = self._lib.DH_set0_pqg(dh_cdata, p, q, g)
1685-
self.openssl_assert(res == 1)
1686-
1687-
res = self._lib.DH_set0_key(dh_cdata, pub_key, priv_key)
1688-
self.openssl_assert(res == 1)
1689-
1690-
codes = self._ffi.new("int[]", 1)
1691-
res = self._lib.DH_check(dh_cdata, codes)
1692-
self.openssl_assert(res == 1)
1693-
1694-
# DH_check will return DH_NOT_SUITABLE_GENERATOR if p % 24 does not
1695-
# equal 11 when the generator is 2 (a quadratic nonresidue).
1696-
# We want to ignore that error because p % 24 == 23 is also fine.
1697-
# Specifically, g is then a quadratic residue. Within the context of
1698-
# Diffie-Hellman this means it can only generate half the possible
1699-
# values. That sounds bad, but quadratic nonresidues leak a bit of
1700-
# the key to the attacker in exchange for having the full key space
1701-
# available. See: https://crypto.stackexchange.com/questions/12961
1702-
if codes[0] != 0 and not (
1703-
parameter_numbers.g == 2
1704-
and codes[0] ^ self._lib.DH_NOT_SUITABLE_GENERATOR == 0
1705-
):
1706-
raise ValueError("DH private numbers did not pass safety checks.")
1707-
1708-
evp_pkey = self._dh_cdata_to_evp_pkey(dh_cdata)
1709-
1710-
return _DHPrivateKey(self, dh_cdata, evp_pkey)
1599+
return rust_openssl.dh.from_private_numbers(numbers)
17111600

17121601
def load_dh_public_numbers(
17131602
self, numbers: dh.DHPublicNumbers
17141603
) -> dh.DHPublicKey:
1715-
dh_cdata = self._lib.DH_new()
1716-
self.openssl_assert(dh_cdata != self._ffi.NULL)
1717-
dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free)
1718-
1719-
parameter_numbers = numbers.parameter_numbers
1720-
1721-
p = self._int_to_bn(parameter_numbers.p)
1722-
g = self._int_to_bn(parameter_numbers.g)
1723-
1724-
if parameter_numbers.q is not None:
1725-
q = self._int_to_bn(parameter_numbers.q)
1726-
else:
1727-
q = self._ffi.NULL
1728-
1729-
pub_key = self._int_to_bn(numbers.y)
1730-
1731-
res = self._lib.DH_set0_pqg(dh_cdata, p, q, g)
1732-
self.openssl_assert(res == 1)
1733-
1734-
res = self._lib.DH_set0_key(dh_cdata, pub_key, self._ffi.NULL)
1735-
self.openssl_assert(res == 1)
1736-
1737-
evp_pkey = self._dh_cdata_to_evp_pkey(dh_cdata)
1738-
1739-
return _DHPublicKey(self, dh_cdata, evp_pkey)
1604+
return rust_openssl.dh.from_public_numbers(numbers)
17401605

17411606
def load_dh_parameter_numbers(
17421607
self, numbers: dh.DHParameterNumbers
17431608
) -> dh.DHParameters:
1744-
dh_cdata = self._lib.DH_new()
1745-
self.openssl_assert(dh_cdata != self._ffi.NULL)
1746-
dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free)
1747-
1748-
p = self._int_to_bn(numbers.p)
1749-
g = self._int_to_bn(numbers.g)
1750-
1751-
if numbers.q is not None:
1752-
q = self._int_to_bn(numbers.q)
1753-
else:
1754-
q = self._ffi.NULL
1755-
1756-
res = self._lib.DH_set0_pqg(dh_cdata, p, q, g)
1757-
self.openssl_assert(res == 1)
1758-
1759-
return _DHParameters(self, dh_cdata)
1609+
return rust_openssl.dh.from_parameter_numbers(numbers)
17601610

17611611
def dh_parameters_supported(
17621612
self, p: int, g: int, q: typing.Optional[int] = None

0 commit comments

Comments
 (0)