1# This file is dual licensed under the terms of the Apache License, Version
2# 2.0, and the BSD License. See the LICENSE file in the root of this repository
3# for complete details.
4
5from __future__ import absolute_import, division, print_function
6
7import itertools
8import os
9import subprocess
10import sys
11import textwrap
12
13import pytest
14
15from cryptography import x509
16from cryptography.exceptions import InternalError, _Reasons
17from cryptography.hazmat.backends.interfaces import DHBackend, RSABackend
18from cryptography.hazmat.backends.openssl.backend import Backend, backend
19from cryptography.hazmat.backends.openssl.ec import _sn_to_elliptic_curve
20from cryptography.hazmat.primitives import hashes, serialization
21from cryptography.hazmat.primitives.asymmetric import dh, dsa, padding
22from cryptography.hazmat.primitives.ciphers import Cipher
23from cryptography.hazmat.primitives.ciphers.algorithms import AES
24from cryptography.hazmat.primitives.ciphers.modes import CBC
25
26from ..primitives.fixtures_rsa import RSA_KEY_2048, RSA_KEY_512
27from ...doubles import (
28    DummyAsymmetricPadding,
29    DummyCipherAlgorithm,
30    DummyHashAlgorithm,
31    DummyMode,
32)
33from ...utils import (
34    load_nist_vectors,
35    load_vectors_from_file,
36    raises_unsupported_algorithm,
37)
38from ...x509.test_x509 import _load_cert
39
40
41def skip_if_libre_ssl(openssl_version):
42    if u"LibreSSL" in openssl_version:
43        pytest.skip("LibreSSL hard-codes RAND_bytes to use arc4random.")
44
45
46class TestLibreSkip(object):
47    def test_skip_no(self):
48        assert skip_if_libre_ssl(u"OpenSSL 1.0.2h  3 May 2016") is None
49
50    def test_skip_yes(self):
51        with pytest.raises(pytest.skip.Exception):
52            skip_if_libre_ssl(u"LibreSSL 2.1.6")
53
54
55class DummyMGF(object):
56    _salt_length = 0
57
58
59class TestOpenSSL(object):
60    def test_backend_exists(self):
61        assert backend
62
63    def test_openssl_version_text(self):
64        """
65        This test checks the value of OPENSSL_VERSION_TEXT.
66
67        Unfortunately, this define does not appear to have a
68        formal content definition, so for now we'll test to see
69        if it starts with OpenSSL or LibreSSL as that appears
70        to be true for every OpenSSL-alike.
71        """
72        assert backend.openssl_version_text().startswith(
73            "OpenSSL"
74        ) or backend.openssl_version_text().startswith("LibreSSL")
75
76    def test_openssl_version_number(self):
77        assert backend.openssl_version_number() > 0
78
79    def test_supports_cipher(self):
80        assert backend.cipher_supported(None, None) is False
81
82    def test_register_duplicate_cipher_adapter(self):
83        with pytest.raises(ValueError):
84            backend.register_cipher_adapter(AES, CBC, None)
85
86    @pytest.mark.parametrize("mode", [DummyMode(), None])
87    def test_nonexistent_cipher(self, mode):
88        b = Backend()
89        b.register_cipher_adapter(
90            DummyCipherAlgorithm,
91            type(mode),
92            lambda backend, cipher, mode: backend._ffi.NULL,
93        )
94        cipher = Cipher(
95            DummyCipherAlgorithm(),
96            mode,
97            backend=b,
98        )
99        with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER):
100            cipher.encryptor()
101
102    def test_openssl_assert(self):
103        backend.openssl_assert(True)
104        with pytest.raises(InternalError):
105            backend.openssl_assert(False)
106
107    def test_consume_errors(self):
108        for i in range(10):
109            backend._lib.ERR_put_error(
110                backend._lib.ERR_LIB_EVP, 0, 0, b"test_openssl.py", -1
111            )
112
113        assert backend._lib.ERR_peek_error() != 0
114
115        errors = backend._consume_errors()
116
117        assert backend._lib.ERR_peek_error() == 0
118        assert len(errors) == 10
119
120    def test_ssl_ciphers_registered(self):
121        meth = backend._lib.SSLv23_method()
122        ctx = backend._lib.SSL_CTX_new(meth)
123        assert ctx != backend._ffi.NULL
124        backend._lib.SSL_CTX_free(ctx)
125
126    def test_evp_ciphers_registered(self):
127        cipher = backend._lib.EVP_get_cipherbyname(b"aes-256-cbc")
128        assert cipher != backend._ffi.NULL
129
130    def test_unknown_error_in_cipher_finalize(self):
131        cipher = Cipher(AES(b"\0" * 16), CBC(b"\0" * 16), backend=backend)
132        enc = cipher.encryptor()
133        enc.update(b"\0")
134        backend._lib.ERR_put_error(0, 0, 1, b"test_openssl.py", -1)
135        with pytest.raises(InternalError):
136            enc.finalize()
137
138    def test_large_key_size_on_new_openssl(self):
139        parameters = dsa.generate_parameters(2048, backend)
140        param_num = parameters.parameter_numbers()
141        assert param_num.p.bit_length() == 2048
142        parameters = dsa.generate_parameters(3072, backend)
143        param_num = parameters.parameter_numbers()
144        assert param_num.p.bit_length() == 3072
145
146    def test_int_to_bn(self):
147        value = (2 ** 4242) - 4242
148        bn = backend._int_to_bn(value)
149        assert bn != backend._ffi.NULL
150        bn = backend._ffi.gc(bn, backend._lib.BN_clear_free)
151
152        assert bn
153        assert backend._bn_to_int(bn) == value
154
155    def test_int_to_bn_inplace(self):
156        value = (2 ** 4242) - 4242
157        bn_ptr = backend._lib.BN_new()
158        assert bn_ptr != backend._ffi.NULL
159        bn_ptr = backend._ffi.gc(bn_ptr, backend._lib.BN_free)
160        bn = backend._int_to_bn(value, bn_ptr)
161
162        assert bn == bn_ptr
163        assert backend._bn_to_int(bn_ptr) == value
164
165    def test_bn_to_int(self):
166        bn = backend._int_to_bn(0)
167        assert backend._bn_to_int(bn) == 0
168
169
170@pytest.mark.skipif(
171    not backend._lib.CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE,
172    reason="Requires OpenSSL with ENGINE support and OpenSSL < 1.1.1d",
173)
174@pytest.mark.skip_fips(reason="osrandom engine disabled for FIPS")
175class TestOpenSSLRandomEngine(object):
176    def setup(self):
177        # The default RAND engine is global and shared between
178        # tests. We make sure that the default engine is osrandom
179        # before we start each test and restore the global state to
180        # that engine in teardown.
181        current_default = backend._lib.ENGINE_get_default_RAND()
182        name = backend._lib.ENGINE_get_name(current_default)
183        assert name == backend._lib.Cryptography_osrandom_engine_name
184
185    def teardown(self):
186        # we need to reset state to being default. backend is a shared global
187        # for all these tests.
188        backend.activate_osrandom_engine()
189        current_default = backend._lib.ENGINE_get_default_RAND()
190        name = backend._lib.ENGINE_get_name(current_default)
191        assert name == backend._lib.Cryptography_osrandom_engine_name
192
193    @pytest.mark.skipif(
194        sys.executable is None, reason="No Python interpreter available."
195    )
196    def test_osrandom_engine_is_default(self, tmpdir):
197        engine_printer = textwrap.dedent(
198            """
199            import sys
200            from cryptography.hazmat.backends.openssl.backend import backend
201
202            e = backend._lib.ENGINE_get_default_RAND()
203            name = backend._lib.ENGINE_get_name(e)
204            sys.stdout.write(backend._ffi.string(name).decode('ascii'))
205            res = backend._lib.ENGINE_free(e)
206            assert res == 1
207            """
208        )
209        engine_name = tmpdir.join("engine_name")
210
211        # If we're running tests via ``python setup.py test`` in a clean
212        # environment then all of our dependencies are going to be installed
213        # into either the current directory or the .eggs directory. However the
214        # subprocess won't know to activate these dependencies, so we'll get it
215        # to do so by passing our entire sys.path into the subprocess via the
216        # PYTHONPATH environment variable.
217        env = os.environ.copy()
218        env["PYTHONPATH"] = os.pathsep.join(sys.path)
219
220        with engine_name.open("w") as out:
221            subprocess.check_call(
222                [sys.executable, "-c", engine_printer],
223                env=env,
224                stdout=out,
225                stderr=subprocess.PIPE,
226            )
227
228        osrandom_engine_name = backend._ffi.string(
229            backend._lib.Cryptography_osrandom_engine_name
230        )
231
232        assert engine_name.read().encode("ascii") == osrandom_engine_name
233
234    def test_osrandom_sanity_check(self):
235        # This test serves as a check against catastrophic failure.
236        buf = backend._ffi.new("unsigned char[]", 500)
237        res = backend._lib.RAND_bytes(buf, 500)
238        assert res == 1
239        assert backend._ffi.buffer(buf)[:] != "\x00" * 500
240
241    def test_activate_osrandom_no_default(self):
242        backend.activate_builtin_random()
243        e = backend._lib.ENGINE_get_default_RAND()
244        assert e == backend._ffi.NULL
245        backend.activate_osrandom_engine()
246        e = backend._lib.ENGINE_get_default_RAND()
247        name = backend._lib.ENGINE_get_name(e)
248        assert name == backend._lib.Cryptography_osrandom_engine_name
249        res = backend._lib.ENGINE_free(e)
250        assert res == 1
251
252    def test_activate_builtin_random(self):
253        e = backend._lib.ENGINE_get_default_RAND()
254        assert e != backend._ffi.NULL
255        name = backend._lib.ENGINE_get_name(e)
256        assert name == backend._lib.Cryptography_osrandom_engine_name
257        res = backend._lib.ENGINE_free(e)
258        assert res == 1
259        backend.activate_builtin_random()
260        e = backend._lib.ENGINE_get_default_RAND()
261        assert e == backend._ffi.NULL
262
263    def test_activate_builtin_random_already_active(self):
264        backend.activate_builtin_random()
265        e = backend._lib.ENGINE_get_default_RAND()
266        assert e == backend._ffi.NULL
267        backend.activate_builtin_random()
268        e = backend._lib.ENGINE_get_default_RAND()
269        assert e == backend._ffi.NULL
270
271    def test_osrandom_engine_implementation(self):
272        name = backend.osrandom_engine_implementation()
273        assert name in [
274            "/dev/urandom",
275            "CryptGenRandom",
276            "getentropy",
277            "getrandom",
278        ]
279        if sys.platform.startswith("linux"):
280            assert name in ["getrandom", "/dev/urandom"]
281        if sys.platform == "darwin":
282            assert name in ["getentropy", "/dev/urandom"]
283        if sys.platform == "win32":
284            assert name == "CryptGenRandom"
285
286    def test_activate_osrandom_already_default(self):
287        e = backend._lib.ENGINE_get_default_RAND()
288        name = backend._lib.ENGINE_get_name(e)
289        assert name == backend._lib.Cryptography_osrandom_engine_name
290        res = backend._lib.ENGINE_free(e)
291        assert res == 1
292        backend.activate_osrandom_engine()
293        e = backend._lib.ENGINE_get_default_RAND()
294        name = backend._lib.ENGINE_get_name(e)
295        assert name == backend._lib.Cryptography_osrandom_engine_name
296        res = backend._lib.ENGINE_free(e)
297        assert res == 1
298
299
300@pytest.mark.skipif(
301    backend._lib.CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE,
302    reason="Requires OpenSSL without ENGINE support or OpenSSL >=1.1.1d",
303)
304class TestOpenSSLNoEngine(object):
305    def test_no_engine_support(self):
306        assert (
307            backend._ffi.string(backend._lib.Cryptography_osrandom_engine_id)
308            == b"no-engine-support"
309        )
310        assert (
311            backend._ffi.string(backend._lib.Cryptography_osrandom_engine_name)
312            == b"osrandom_engine disabled"
313        )
314
315    def test_activate_builtin_random_does_nothing(self):
316        backend.activate_builtin_random()
317
318    def test_activate_osrandom_does_nothing(self):
319        backend.activate_osrandom_engine()
320
321
322class TestOpenSSLRSA(object):
323    def test_generate_rsa_parameters_supported(self):
324        assert backend.generate_rsa_parameters_supported(1, 1024) is False
325        assert backend.generate_rsa_parameters_supported(4, 1024) is False
326        assert backend.generate_rsa_parameters_supported(3, 1024) is True
327        assert backend.generate_rsa_parameters_supported(3, 511) is False
328
329    def test_generate_bad_public_exponent(self):
330        with pytest.raises(ValueError):
331            backend.generate_rsa_private_key(public_exponent=1, key_size=2048)
332
333        with pytest.raises(ValueError):
334            backend.generate_rsa_private_key(public_exponent=4, key_size=2048)
335
336    def test_cant_generate_insecure_tiny_key(self):
337        with pytest.raises(ValueError):
338            backend.generate_rsa_private_key(
339                public_exponent=65537, key_size=511
340            )
341
342        with pytest.raises(ValueError):
343            backend.generate_rsa_private_key(
344                public_exponent=65537, key_size=256
345            )
346
347    def test_rsa_padding_unsupported_pss_mgf1_hash(self):
348        assert (
349            backend.rsa_padding_supported(
350                padding.PSS(
351                    mgf=padding.MGF1(DummyHashAlgorithm()), salt_length=0
352                )
353            )
354            is False
355        )
356
357    def test_rsa_padding_unsupported(self):
358        assert backend.rsa_padding_supported(DummyAsymmetricPadding()) is False
359
360    def test_rsa_padding_supported_pkcs1v15(self):
361        assert backend.rsa_padding_supported(padding.PKCS1v15()) is True
362
363    def test_rsa_padding_supported_pss(self):
364        assert (
365            backend.rsa_padding_supported(
366                padding.PSS(mgf=padding.MGF1(hashes.SHA1()), salt_length=0)
367            )
368            is True
369        )
370
371    def test_rsa_padding_supported_oaep(self):
372        assert (
373            backend.rsa_padding_supported(
374                padding.OAEP(
375                    mgf=padding.MGF1(algorithm=hashes.SHA1()),
376                    algorithm=hashes.SHA1(),
377                    label=None,
378                ),
379            )
380            is True
381        )
382
383    @pytest.mark.skipif(
384        backend._lib.Cryptography_HAS_RSA_OAEP_MD == 0,
385        reason="Requires OpenSSL with rsa_oaep_md (1.0.2+)",
386    )
387    def test_rsa_padding_supported_oaep_sha2_combinations(self):
388        hashalgs = [
389            hashes.SHA1(),
390            hashes.SHA224(),
391            hashes.SHA256(),
392            hashes.SHA384(),
393            hashes.SHA512(),
394        ]
395        for mgf1alg, oaepalg in itertools.product(hashalgs, hashalgs):
396            assert (
397                backend.rsa_padding_supported(
398                    padding.OAEP(
399                        mgf=padding.MGF1(algorithm=mgf1alg),
400                        algorithm=oaepalg,
401                        label=None,
402                    ),
403                )
404                is True
405            )
406
407    def test_rsa_padding_unsupported_mgf(self):
408        assert (
409            backend.rsa_padding_supported(
410                padding.OAEP(
411                    mgf=DummyMGF(), algorithm=hashes.SHA1(), label=None
412                ),
413            )
414            is False
415        )
416
417        assert (
418            backend.rsa_padding_supported(
419                padding.PSS(mgf=DummyMGF(), salt_length=0)
420            )
421            is False
422        )
423
424    @pytest.mark.skipif(
425        backend._lib.Cryptography_HAS_RSA_OAEP_MD == 1,
426        reason="Requires OpenSSL without rsa_oaep_md (< 1.0.2)",
427    )
428    def test_unsupported_mgf1_hash_algorithm_decrypt(self):
429        private_key = RSA_KEY_512.private_key(backend)
430        with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING):
431            private_key.decrypt(
432                b"0" * 64,
433                padding.OAEP(
434                    mgf=padding.MGF1(algorithm=hashes.SHA256()),
435                    algorithm=hashes.SHA1(),
436                    label=None,
437                ),
438            )
439
440    @pytest.mark.skipif(
441        backend._lib.Cryptography_HAS_RSA_OAEP_MD == 1,
442        reason="Requires OpenSSL without rsa_oaep_md (< 1.0.2)",
443    )
444    def test_unsupported_oaep_hash_algorithm_decrypt(self):
445        private_key = RSA_KEY_512.private_key(backend)
446        with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING):
447            private_key.decrypt(
448                b"0" * 64,
449                padding.OAEP(
450                    mgf=padding.MGF1(algorithm=hashes.SHA1()),
451                    algorithm=hashes.SHA256(),
452                    label=None,
453                ),
454            )
455
456    def test_unsupported_mgf1_hash_algorithm_md5_decrypt(self):
457        private_key = RSA_KEY_512.private_key(backend)
458        with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING):
459            private_key.decrypt(
460                b"0" * 64,
461                padding.OAEP(
462                    mgf=padding.MGF1(algorithm=hashes.MD5()),
463                    algorithm=hashes.MD5(),
464                    label=None,
465                ),
466            )
467
468
469class TestOpenSSLCMAC(object):
470    def test_unsupported_cipher(self):
471        with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER):
472            backend.create_cmac_ctx(DummyCipherAlgorithm())
473
474
475class TestOpenSSLSignX509Certificate(object):
476    def test_requires_certificate_builder(self):
477        private_key = RSA_KEY_2048.private_key(backend)
478
479        with pytest.raises(TypeError):
480            backend.create_x509_certificate(
481                object(), private_key, DummyHashAlgorithm()
482            )
483
484
485class TestOpenSSLSignX509CSR(object):
486    def test_requires_csr_builder(self):
487        private_key = RSA_KEY_2048.private_key(backend)
488
489        with pytest.raises(TypeError):
490            backend.create_x509_csr(
491                object(), private_key, DummyHashAlgorithm()
492            )
493
494
495class TestOpenSSLSignX509CertificateRevocationList(object):
496    def test_invalid_builder(self):
497        private_key = RSA_KEY_2048.private_key(backend)
498
499        with pytest.raises(TypeError):
500            backend.create_x509_crl(object(), private_key, hashes.SHA256())
501
502
503class TestOpenSSLCreateRevokedCertificate(object):
504    def test_invalid_builder(self):
505        with pytest.raises(TypeError):
506            backend.create_x509_revoked_certificate(object())
507
508
509class TestOpenSSLSerializationWithOpenSSL(object):
510    def test_pem_password_cb(self):
511        userdata = backend._ffi.new("CRYPTOGRAPHY_PASSWORD_DATA *")
512        pw = b"abcdefg"
513        password = backend._ffi.new("char []", pw)
514        userdata.password = password
515        userdata.length = len(pw)
516        buflen = 10
517        buf = backend._ffi.new("char []", buflen)
518        res = backend._lib.Cryptography_pem_password_cb(
519            buf, buflen, 0, userdata
520        )
521        assert res == len(pw)
522        assert userdata.called == 1
523        assert backend._ffi.buffer(buf, len(pw))[:] == pw
524        assert userdata.maxsize == buflen
525        assert userdata.error == 0
526
527    def test_pem_password_cb_no_password(self):
528        userdata = backend._ffi.new("CRYPTOGRAPHY_PASSWORD_DATA *")
529        buflen = 10
530        buf = backend._ffi.new("char []", buflen)
531        res = backend._lib.Cryptography_pem_password_cb(
532            buf, buflen, 0, userdata
533        )
534        assert res == 0
535        assert userdata.error == -1
536
537    def test_unsupported_evp_pkey_type(self):
538        key = backend._create_evp_pkey_gc()
539        with raises_unsupported_algorithm(None):
540            backend._evp_pkey_to_private_key(key)
541        with raises_unsupported_algorithm(None):
542            backend._evp_pkey_to_public_key(key)
543
544    def test_very_long_pem_serialization_password(self):
545        password = b"x" * 1024
546
547        with pytest.raises(ValueError):
548            load_vectors_from_file(
549                os.path.join(
550                    "asymmetric",
551                    "Traditional_OpenSSL_Serialization",
552                    "key1.pem",
553                ),
554                lambda pemfile: (
555                    backend.load_pem_private_key(
556                        pemfile.read().encode(), password
557                    )
558                ),
559            )
560
561
562class TestOpenSSLEllipticCurve(object):
563    def test_sn_to_elliptic_curve_not_supported(self):
564        with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_ELLIPTIC_CURVE):
565            _sn_to_elliptic_curve(backend, b"fake")
566
567
568@pytest.mark.requires_backend_interface(interface=RSABackend)
569class TestRSAPEMSerialization(object):
570    def test_password_length_limit(self):
571        password = b"x" * 1024
572        key = RSA_KEY_2048.private_key(backend)
573        with pytest.raises(ValueError):
574            key.private_bytes(
575                serialization.Encoding.PEM,
576                serialization.PrivateFormat.PKCS8,
577                serialization.BestAvailableEncryption(password),
578            )
579
580
581class TestGOSTCertificate(object):
582    def test_numeric_string_x509_name_entry(self):
583        cert = _load_cert(
584            os.path.join("x509", "e-trust.ru.der"),
585            x509.load_der_x509_certificate,
586            backend,
587        )
588        if backend._lib.CRYPTOGRAPHY_IS_LIBRESSL:
589            with pytest.raises(ValueError) as exc:
590                cert.subject
591
592            # We assert on the message in this case because if the certificate
593            # fails to load it will also raise a ValueError and this test could
594            # erroneously pass.
595            assert str(exc.value) == "Unsupported ASN1 string type. Type: 18"
596        else:
597            assert (
598                cert.subject.get_attributes_for_oid(
599                    x509.ObjectIdentifier("1.2.643.3.131.1.1")
600                )[0].value
601                == "007710474375"
602            )
603
604
605@pytest.mark.skipif(
606    backend._lib.Cryptography_HAS_EVP_PKEY_DHX == 1,
607    reason="Requires OpenSSL without EVP_PKEY_DHX (< 1.0.2)",
608)
609@pytest.mark.requires_backend_interface(interface=DHBackend)
610class TestOpenSSLDHSerialization(object):
611    @pytest.mark.parametrize(
612        "vector",
613        load_vectors_from_file(
614            os.path.join("asymmetric", "DH", "RFC5114.txt"), load_nist_vectors
615        ),
616    )
617    def test_dh_serialization_with_q_unsupported(self, backend, vector):
618        parameters = dh.DHParameterNumbers(
619            int(vector["p"], 16), int(vector["g"], 16), int(vector["q"], 16)
620        )
621        public = dh.DHPublicNumbers(int(vector["ystatcavs"], 16), parameters)
622        private = dh.DHPrivateNumbers(int(vector["xstatcavs"], 16), public)
623        private_key = private.private_key(backend)
624        public_key = private_key.public_key()
625        with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_SERIALIZATION):
626            private_key.private_bytes(
627                serialization.Encoding.PEM,
628                serialization.PrivateFormat.PKCS8,
629                serialization.NoEncryption(),
630            )
631        with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_SERIALIZATION):
632            public_key.public_bytes(
633                serialization.Encoding.PEM,
634                serialization.PublicFormat.SubjectPublicKeyInfo,
635            )
636        with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_SERIALIZATION):
637            parameters.parameters(backend).parameter_bytes(
638                serialization.Encoding.PEM, serialization.ParameterFormat.PKCS3
639            )
640
641    @pytest.mark.parametrize(
642        ("key_path", "loader_func"),
643        [
644            (
645                os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.pem"),
646                serialization.load_pem_private_key,
647            ),
648            (
649                os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.der"),
650                serialization.load_der_private_key,
651            ),
652        ],
653    )
654    def test_private_load_dhx_unsupported(
655        self, key_path, loader_func, backend
656    ):
657        key_bytes = load_vectors_from_file(
658            key_path, lambda pemfile: pemfile.read(), mode="rb"
659        )
660        with pytest.raises(ValueError):
661            loader_func(key_bytes, None, backend)
662
663    @pytest.mark.parametrize(
664        ("key_path", "loader_func"),
665        [
666            (
667                os.path.join("asymmetric", "DH", "dhpub_rfc5114_2.pem"),
668                serialization.load_pem_public_key,
669            ),
670            (
671                os.path.join("asymmetric", "DH", "dhpub_rfc5114_2.der"),
672                serialization.load_der_public_key,
673            ),
674        ],
675    )
676    def test_public_load_dhx_unsupported(self, key_path, loader_func, backend):
677        key_bytes = load_vectors_from_file(
678            key_path, lambda pemfile: pemfile.read(), mode="rb"
679        )
680        with pytest.raises(ValueError):
681            loader_func(key_bytes, backend)
682