• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
9
10import pytest
11
12from cryptography.exceptions import AlreadyFinalized, InvalidSignature
13from cryptography.hazmat.backends.interfaces import (
14    DSABackend,
15    PEMSerializationBackend,
16)
17from cryptography.hazmat.primitives import hashes, serialization
18from cryptography.hazmat.primitives.asymmetric import dsa
19from cryptography.hazmat.primitives.asymmetric.utils import (
20    Prehashed,
21    encode_dss_signature,
22)
23from cryptography.utils import CryptographyDeprecationWarning
24
25from .fixtures_dsa import DSA_KEY_1024, DSA_KEY_2048, DSA_KEY_3072
26from .utils import skip_fips_traditional_openssl
27from ...doubles import DummyHashAlgorithm, DummyKeySerializationEncryption
28from ...utils import (
29    load_fips_dsa_key_pair_vectors,
30    load_fips_dsa_sig_vectors,
31    load_vectors_from_file,
32)
33
34
35def _skip_if_dsa_not_supported(backend, algorithm, p, q, g):
36    if not backend.dsa_parameters_supported(
37        p, q, g
38    ) or not backend.dsa_hash_supported(algorithm):
39        pytest.skip(
40            "{} does not support the provided parameters".format(backend)
41        )
42
43
44@pytest.mark.requires_backend_interface(interface=DSABackend)
45def test_skip_if_dsa_not_supported(backend):
46    with pytest.raises(pytest.skip.Exception):
47        _skip_if_dsa_not_supported(backend, DummyHashAlgorithm(), 1, 1, 1)
48
49
50@pytest.mark.requires_backend_interface(interface=DSABackend)
51class TestDSA(object):
52    def test_generate_dsa_parameters(self, backend):
53        parameters = dsa.generate_parameters(2048, backend)
54        assert isinstance(parameters, dsa.DSAParameters)
55
56    def test_generate_invalid_dsa_parameters(self, backend):
57        with pytest.raises(ValueError):
58            dsa.generate_parameters(1, backend)
59
60    @pytest.mark.parametrize(
61        "vector",
62        load_vectors_from_file(
63            os.path.join("asymmetric", "DSA", "FIPS_186-3", "KeyPair.rsp"),
64            load_fips_dsa_key_pair_vectors,
65        ),
66    )
67    def test_generate_dsa_keys(self, vector, backend):
68        if (
69            backend._fips_enabled
70            and vector["p"] < backend._fips_dsa_min_modulus
71        ):
72            pytest.skip("Small modulus blocked in FIPS mode")
73        parameters = dsa.DSAParameterNumbers(
74            p=vector["p"], q=vector["q"], g=vector["g"]
75        ).parameters(backend)
76        skey = parameters.generate_private_key()
77        numbers = skey.private_numbers()
78        skey_parameters = numbers.public_numbers.parameter_numbers
79        pkey = skey.public_key()
80        parameters = pkey.parameters()
81        parameter_numbers = parameters.parameter_numbers()
82        assert parameter_numbers.p == skey_parameters.p
83        assert parameter_numbers.q == skey_parameters.q
84        assert parameter_numbers.g == skey_parameters.g
85        assert skey_parameters.p == vector["p"]
86        assert skey_parameters.q == vector["q"]
87        assert skey_parameters.g == vector["g"]
88        assert skey.key_size == vector["p"].bit_length()
89        assert pkey.key_size == skey.key_size
90        public_numbers = pkey.public_numbers()
91        assert numbers.public_numbers.y == public_numbers.y
92        assert numbers.public_numbers.y == pow(
93            skey_parameters.g, numbers.x, skey_parameters.p
94        )
95
96    def test_generate_dsa_private_key_and_parameters(self, backend):
97        skey = dsa.generate_private_key(2048, backend)
98        assert skey
99        numbers = skey.private_numbers()
100        skey_parameters = numbers.public_numbers.parameter_numbers
101        assert numbers.public_numbers.y == pow(
102            skey_parameters.g, numbers.x, skey_parameters.p
103        )
104
105    @pytest.mark.parametrize(
106        ("p", "q", "g"),
107        [
108            (
109                2 ** 1000,
110                DSA_KEY_1024.public_numbers.parameter_numbers.q,
111                DSA_KEY_1024.public_numbers.parameter_numbers.g,
112            ),
113            (
114                2 ** 2000,
115                DSA_KEY_2048.public_numbers.parameter_numbers.q,
116                DSA_KEY_2048.public_numbers.parameter_numbers.g,
117            ),
118            (
119                2 ** 3000,
120                DSA_KEY_3072.public_numbers.parameter_numbers.q,
121                DSA_KEY_3072.public_numbers.parameter_numbers.g,
122            ),
123            (
124                2 ** 3100,
125                DSA_KEY_3072.public_numbers.parameter_numbers.q,
126                DSA_KEY_3072.public_numbers.parameter_numbers.g,
127            ),
128            (
129                DSA_KEY_1024.public_numbers.parameter_numbers.p,
130                2 ** 150,
131                DSA_KEY_1024.public_numbers.parameter_numbers.g,
132            ),
133            (
134                DSA_KEY_2048.public_numbers.parameter_numbers.p,
135                2 ** 250,
136                DSA_KEY_2048.public_numbers.parameter_numbers.g,
137            ),
138            (
139                DSA_KEY_3072.public_numbers.parameter_numbers.p,
140                2 ** 260,
141                DSA_KEY_3072.public_numbers.parameter_numbers.g,
142            ),
143            (
144                DSA_KEY_1024.public_numbers.parameter_numbers.p,
145                DSA_KEY_1024.public_numbers.parameter_numbers.q,
146                0,
147            ),
148            (
149                DSA_KEY_1024.public_numbers.parameter_numbers.p,
150                DSA_KEY_1024.public_numbers.parameter_numbers.q,
151                1,
152            ),
153            (
154                DSA_KEY_1024.public_numbers.parameter_numbers.p,
155                DSA_KEY_1024.public_numbers.parameter_numbers.q,
156                2 ** 1200,
157            ),
158        ],
159    )
160    def test_invalid_parameters_values(self, p, q, g, backend):
161        with pytest.raises(ValueError):
162            dsa.DSAParameterNumbers(p, q, g).parameters(backend)
163
164    @pytest.mark.parametrize(
165        ("p", "q", "g", "y", "x"),
166        [
167            (
168                2 ** 1000,
169                DSA_KEY_1024.public_numbers.parameter_numbers.q,
170                DSA_KEY_1024.public_numbers.parameter_numbers.g,
171                DSA_KEY_1024.public_numbers.y,
172                DSA_KEY_1024.x,
173            ),
174            (
175                2 ** 2000,
176                DSA_KEY_2048.public_numbers.parameter_numbers.q,
177                DSA_KEY_2048.public_numbers.parameter_numbers.g,
178                DSA_KEY_2048.public_numbers.y,
179                DSA_KEY_2048.x,
180            ),
181            (
182                2 ** 3000,
183                DSA_KEY_3072.public_numbers.parameter_numbers.q,
184                DSA_KEY_3072.public_numbers.parameter_numbers.g,
185                DSA_KEY_3072.public_numbers.y,
186                DSA_KEY_3072.x,
187            ),
188            (
189                2 ** 3100,
190                DSA_KEY_3072.public_numbers.parameter_numbers.q,
191                DSA_KEY_3072.public_numbers.parameter_numbers.g,
192                DSA_KEY_3072.public_numbers.y,
193                DSA_KEY_3072.x,
194            ),
195            (
196                DSA_KEY_1024.public_numbers.parameter_numbers.p,
197                2 ** 150,
198                DSA_KEY_1024.public_numbers.parameter_numbers.g,
199                DSA_KEY_1024.public_numbers.y,
200                DSA_KEY_1024.x,
201            ),
202            (
203                DSA_KEY_2048.public_numbers.parameter_numbers.p,
204                2 ** 250,
205                DSA_KEY_2048.public_numbers.parameter_numbers.g,
206                DSA_KEY_2048.public_numbers.y,
207                DSA_KEY_2048.x,
208            ),
209            (
210                DSA_KEY_3072.public_numbers.parameter_numbers.p,
211                2 ** 260,
212                DSA_KEY_3072.public_numbers.parameter_numbers.g,
213                DSA_KEY_3072.public_numbers.y,
214                DSA_KEY_3072.x,
215            ),
216            (
217                DSA_KEY_1024.public_numbers.parameter_numbers.p,
218                DSA_KEY_1024.public_numbers.parameter_numbers.q,
219                0,
220                DSA_KEY_1024.public_numbers.y,
221                DSA_KEY_1024.x,
222            ),
223            (
224                DSA_KEY_1024.public_numbers.parameter_numbers.p,
225                DSA_KEY_1024.public_numbers.parameter_numbers.q,
226                1,
227                DSA_KEY_1024.public_numbers.y,
228                DSA_KEY_1024.x,
229            ),
230            (
231                DSA_KEY_1024.public_numbers.parameter_numbers.p,
232                DSA_KEY_1024.public_numbers.parameter_numbers.q,
233                2 ** 1200,
234                DSA_KEY_1024.public_numbers.y,
235                DSA_KEY_1024.x,
236            ),
237            (
238                DSA_KEY_1024.public_numbers.parameter_numbers.p,
239                DSA_KEY_1024.public_numbers.parameter_numbers.q,
240                DSA_KEY_1024.public_numbers.parameter_numbers.g,
241                DSA_KEY_1024.public_numbers.y,
242                0,
243            ),
244            (
245                DSA_KEY_1024.public_numbers.parameter_numbers.p,
246                DSA_KEY_1024.public_numbers.parameter_numbers.q,
247                DSA_KEY_1024.public_numbers.parameter_numbers.g,
248                DSA_KEY_1024.public_numbers.y,
249                -2,
250            ),
251            (
252                DSA_KEY_1024.public_numbers.parameter_numbers.p,
253                DSA_KEY_1024.public_numbers.parameter_numbers.q,
254                DSA_KEY_1024.public_numbers.parameter_numbers.g,
255                DSA_KEY_1024.public_numbers.y,
256                2 ** 159,
257            ),
258            (
259                DSA_KEY_1024.public_numbers.parameter_numbers.p,
260                DSA_KEY_1024.public_numbers.parameter_numbers.q,
261                DSA_KEY_1024.public_numbers.parameter_numbers.g,
262                DSA_KEY_1024.public_numbers.y,
263                2 ** 200,
264            ),
265            (
266                DSA_KEY_1024.public_numbers.parameter_numbers.p,
267                DSA_KEY_1024.public_numbers.parameter_numbers.q,
268                DSA_KEY_1024.public_numbers.parameter_numbers.g,
269                2 ** 100,
270                DSA_KEY_1024.x,
271            ),
272        ],
273    )
274    def test_invalid_dsa_private_key_arguments(self, p, q, g, y, x, backend):
275        with pytest.raises(ValueError):
276            dsa.DSAPrivateNumbers(
277                public_numbers=dsa.DSAPublicNumbers(
278                    parameter_numbers=dsa.DSAParameterNumbers(p=p, q=q, g=g),
279                    y=y,
280                ),
281                x=x,
282            ).private_key(backend)
283
284    @pytest.mark.parametrize(
285        ("p", "q", "g", "y"),
286        [
287            (
288                2 ** 1000,
289                DSA_KEY_1024.public_numbers.parameter_numbers.q,
290                DSA_KEY_1024.public_numbers.parameter_numbers.g,
291                DSA_KEY_1024.public_numbers.y,
292            ),
293            (
294                2 ** 2000,
295                DSA_KEY_2048.public_numbers.parameter_numbers.q,
296                DSA_KEY_2048.public_numbers.parameter_numbers.g,
297                DSA_KEY_2048.public_numbers.y,
298            ),
299            (
300                2 ** 3000,
301                DSA_KEY_3072.public_numbers.parameter_numbers.q,
302                DSA_KEY_3072.public_numbers.parameter_numbers.g,
303                DSA_KEY_3072.public_numbers.y,
304            ),
305            (
306                2 ** 3100,
307                DSA_KEY_3072.public_numbers.parameter_numbers.q,
308                DSA_KEY_3072.public_numbers.parameter_numbers.g,
309                DSA_KEY_3072.public_numbers.y,
310            ),
311            (
312                DSA_KEY_1024.public_numbers.parameter_numbers.p,
313                2 ** 150,
314                DSA_KEY_1024.public_numbers.parameter_numbers.g,
315                DSA_KEY_1024.public_numbers.y,
316            ),
317            (
318                DSA_KEY_2048.public_numbers.parameter_numbers.p,
319                2 ** 250,
320                DSA_KEY_2048.public_numbers.parameter_numbers.g,
321                DSA_KEY_2048.public_numbers.y,
322            ),
323            (
324                DSA_KEY_3072.public_numbers.parameter_numbers.p,
325                2 ** 260,
326                DSA_KEY_3072.public_numbers.parameter_numbers.g,
327                DSA_KEY_3072.public_numbers.y,
328            ),
329            (
330                DSA_KEY_1024.public_numbers.parameter_numbers.p,
331                DSA_KEY_1024.public_numbers.parameter_numbers.q,
332                0,
333                DSA_KEY_1024.public_numbers.y,
334            ),
335            (
336                DSA_KEY_1024.public_numbers.parameter_numbers.p,
337                DSA_KEY_1024.public_numbers.parameter_numbers.q,
338                1,
339                DSA_KEY_1024.public_numbers.y,
340            ),
341            (
342                DSA_KEY_1024.public_numbers.parameter_numbers.p,
343                DSA_KEY_1024.public_numbers.parameter_numbers.q,
344                2 ** 1200,
345                DSA_KEY_1024.public_numbers.y,
346            ),
347        ],
348    )
349    def test_invalid_dsa_public_key_arguments(self, p, q, g, y, backend):
350        with pytest.raises(ValueError):
351            dsa.DSAPublicNumbers(
352                parameter_numbers=dsa.DSAParameterNumbers(p=p, q=q, g=g), y=y
353            ).public_key(backend)
354
355    def test_large_p(self, backend):
356        key = load_vectors_from_file(
357            os.path.join("asymmetric", "PEM_Serialization", "dsa_4096.pem"),
358            lambda pemfile: serialization.load_pem_private_key(
359                pemfile.read(), None, backend
360            ),
361            mode="rb",
362        )
363        pn = key.private_numbers()
364        assert pn.public_numbers.parameter_numbers.p.bit_length() == 4096
365        # Turn it back into a key to confirm that values this large pass
366        # verification
367        dsa.DSAPrivateNumbers(
368            public_numbers=dsa.DSAPublicNumbers(
369                parameter_numbers=dsa.DSAParameterNumbers(
370                    p=pn.public_numbers.parameter_numbers.p,
371                    q=pn.public_numbers.parameter_numbers.q,
372                    g=pn.public_numbers.parameter_numbers.g,
373                ),
374                y=pn.public_numbers.y,
375            ),
376            x=pn.x,
377        ).private_key(backend)
378
379
380@pytest.mark.requires_backend_interface(interface=DSABackend)
381class TestDSAVerification(object):
382    _algorithms_dict = {
383        "SHA1": hashes.SHA1,
384        "SHA224": hashes.SHA224,
385        "SHA256": hashes.SHA256,
386        "SHA384": hashes.SHA384,
387        "SHA512": hashes.SHA512,
388    }
389
390    @pytest.mark.parametrize(
391        "vector",
392        load_vectors_from_file(
393            os.path.join("asymmetric", "DSA", "FIPS_186-3", "SigVer.rsp"),
394            load_fips_dsa_sig_vectors,
395        ),
396    )
397    def test_dsa_verification(self, vector, backend):
398        digest_algorithm = vector["digest_algorithm"].replace("-", "")
399        algorithm = self._algorithms_dict[digest_algorithm]
400
401        _skip_if_dsa_not_supported(
402            backend, algorithm, vector["p"], vector["q"], vector["g"]
403        )
404
405        public_key = dsa.DSAPublicNumbers(
406            parameter_numbers=dsa.DSAParameterNumbers(
407                vector["p"], vector["q"], vector["g"]
408            ),
409            y=vector["y"],
410        ).public_key(backend)
411        sig = encode_dss_signature(vector["r"], vector["s"])
412
413        if vector["result"] == "F":
414            with pytest.raises(InvalidSignature):
415                public_key.verify(sig, vector["msg"], algorithm())
416        else:
417            public_key.verify(sig, vector["msg"], algorithm())
418
419    def test_dsa_verify_invalid_asn1(self, backend):
420        public_key = DSA_KEY_1024.public_numbers.public_key(backend)
421        with pytest.raises(InvalidSignature):
422            public_key.verify(b"fakesig", b"fakemsg", hashes.SHA1())
423
424    def test_signature_not_bytes(self, backend):
425        public_key = DSA_KEY_1024.public_numbers.public_key(backend)
426        with pytest.raises(TypeError), pytest.warns(
427            CryptographyDeprecationWarning
428        ):
429            public_key.verifier(1234, hashes.SHA1())
430
431    def test_use_after_finalize(self, backend):
432        public_key = DSA_KEY_1024.public_numbers.public_key(backend)
433        with pytest.warns(CryptographyDeprecationWarning):
434            verifier = public_key.verifier(b"fakesig", hashes.SHA1())
435        verifier.update(b"irrelevant")
436        with pytest.raises(InvalidSignature):
437            verifier.verify()
438        with pytest.raises(AlreadyFinalized):
439            verifier.verify()
440        with pytest.raises(AlreadyFinalized):
441            verifier.update(b"more data")
442
443    def test_verify(self, backend):
444        message = b"one little message"
445        algorithm = hashes.SHA1()
446        private_key = DSA_KEY_1024.private_key(backend)
447        signature = private_key.sign(message, algorithm)
448        public_key = private_key.public_key()
449        public_key.verify(signature, message, algorithm)
450
451    def test_prehashed_verify(self, backend):
452        private_key = DSA_KEY_1024.private_key(backend)
453        message = b"one little message"
454        h = hashes.Hash(hashes.SHA1(), backend)
455        h.update(message)
456        digest = h.finalize()
457        prehashed_alg = Prehashed(hashes.SHA1())
458        signature = private_key.sign(message, hashes.SHA1())
459        public_key = private_key.public_key()
460        public_key.verify(signature, digest, prehashed_alg)
461
462    def test_prehashed_digest_mismatch(self, backend):
463        private_key = DSA_KEY_1024.private_key(backend)
464        public_key = private_key.public_key()
465        message = b"one little message"
466        h = hashes.Hash(hashes.SHA1(), backend)
467        h.update(message)
468        digest = h.finalize()
469        prehashed_alg = Prehashed(hashes.SHA224())
470        with pytest.raises(ValueError):
471            public_key.verify(b"\x00" * 128, digest, prehashed_alg)
472
473    def test_prehashed_unsupported_in_signer_ctx(self, backend):
474        private_key = DSA_KEY_1024.private_key(backend)
475        with pytest.raises(TypeError), pytest.warns(
476            CryptographyDeprecationWarning
477        ):
478            private_key.signer(Prehashed(hashes.SHA1()))
479
480    def test_prehashed_unsupported_in_verifier_ctx(self, backend):
481        public_key = DSA_KEY_1024.private_key(backend).public_key()
482        with pytest.raises(TypeError), pytest.warns(
483            CryptographyDeprecationWarning
484        ):
485            public_key.verifier(b"0" * 64, Prehashed(hashes.SHA1()))
486
487
488@pytest.mark.requires_backend_interface(interface=DSABackend)
489class TestDSASignature(object):
490    _algorithms_dict = {
491        "SHA1": hashes.SHA1,
492        "SHA224": hashes.SHA224,
493        "SHA256": hashes.SHA256,
494        "SHA384": hashes.SHA384,
495        "SHA512": hashes.SHA512,
496    }
497
498    @pytest.mark.parametrize(
499        "vector",
500        load_vectors_from_file(
501            os.path.join("asymmetric", "DSA", "FIPS_186-3", "SigGen.txt"),
502            load_fips_dsa_sig_vectors,
503        ),
504    )
505    def test_dsa_signing(self, vector, backend):
506        digest_algorithm = vector["digest_algorithm"].replace("-", "")
507        algorithm = self._algorithms_dict[digest_algorithm]
508
509        _skip_if_dsa_not_supported(
510            backend, algorithm, vector["p"], vector["q"], vector["g"]
511        )
512
513        private_key = dsa.DSAPrivateNumbers(
514            public_numbers=dsa.DSAPublicNumbers(
515                parameter_numbers=dsa.DSAParameterNumbers(
516                    vector["p"], vector["q"], vector["g"]
517                ),
518                y=vector["y"],
519            ),
520            x=vector["x"],
521        ).private_key(backend)
522        signature = private_key.sign(vector["msg"], algorithm())
523        assert signature
524
525        private_key.public_key().verify(signature, vector["msg"], algorithm())
526
527    def test_use_after_finalize(self, backend):
528        private_key = DSA_KEY_1024.private_key(backend)
529        with pytest.warns(CryptographyDeprecationWarning):
530            signer = private_key.signer(hashes.SHA1())
531        signer.update(b"data")
532        signer.finalize()
533        with pytest.raises(AlreadyFinalized):
534            signer.finalize()
535        with pytest.raises(AlreadyFinalized):
536            signer.update(b"more data")
537
538    def test_sign(self, backend):
539        private_key = DSA_KEY_1024.private_key(backend)
540        message = b"one little message"
541        algorithm = hashes.SHA1()
542        signature = private_key.sign(message, algorithm)
543        public_key = private_key.public_key()
544        public_key.verify(signature, message, algorithm)
545
546    def test_prehashed_sign(self, backend):
547        private_key = DSA_KEY_1024.private_key(backend)
548        message = b"one little message"
549        h = hashes.Hash(hashes.SHA1(), backend)
550        h.update(message)
551        digest = h.finalize()
552        prehashed_alg = Prehashed(hashes.SHA1())
553        signature = private_key.sign(digest, prehashed_alg)
554        public_key = private_key.public_key()
555        public_key.verify(signature, message, hashes.SHA1())
556
557    def test_prehashed_digest_mismatch(self, backend):
558        private_key = DSA_KEY_1024.private_key(backend)
559        message = b"one little message"
560        h = hashes.Hash(hashes.SHA1(), backend)
561        h.update(message)
562        digest = h.finalize()
563        prehashed_alg = Prehashed(hashes.SHA224())
564        with pytest.raises(ValueError):
565            private_key.sign(digest, prehashed_alg)
566
567
568class TestDSANumbers(object):
569    def test_dsa_parameter_numbers(self):
570        parameter_numbers = dsa.DSAParameterNumbers(p=1, q=2, g=3)
571        assert parameter_numbers.p == 1
572        assert parameter_numbers.q == 2
573        assert parameter_numbers.g == 3
574
575    def test_dsa_parameter_numbers_invalid_types(self):
576        with pytest.raises(TypeError):
577            dsa.DSAParameterNumbers(p=None, q=2, g=3)
578
579        with pytest.raises(TypeError):
580            dsa.DSAParameterNumbers(p=1, q=None, g=3)
581
582        with pytest.raises(TypeError):
583            dsa.DSAParameterNumbers(p=1, q=2, g=None)
584
585    def test_dsa_public_numbers(self):
586        parameter_numbers = dsa.DSAParameterNumbers(p=1, q=2, g=3)
587        public_numbers = dsa.DSAPublicNumbers(
588            y=4, parameter_numbers=parameter_numbers
589        )
590        assert public_numbers.y == 4
591        assert public_numbers.parameter_numbers == parameter_numbers
592
593    def test_dsa_public_numbers_invalid_types(self):
594        with pytest.raises(TypeError):
595            dsa.DSAPublicNumbers(y=4, parameter_numbers=None)
596
597        with pytest.raises(TypeError):
598            parameter_numbers = dsa.DSAParameterNumbers(p=1, q=2, g=3)
599            dsa.DSAPublicNumbers(y=None, parameter_numbers=parameter_numbers)
600
601    def test_dsa_private_numbers(self):
602        parameter_numbers = dsa.DSAParameterNumbers(p=1, q=2, g=3)
603        public_numbers = dsa.DSAPublicNumbers(
604            y=4, parameter_numbers=parameter_numbers
605        )
606        private_numbers = dsa.DSAPrivateNumbers(
607            x=5, public_numbers=public_numbers
608        )
609        assert private_numbers.x == 5
610        assert private_numbers.public_numbers == public_numbers
611
612    def test_dsa_private_numbers_invalid_types(self):
613        parameter_numbers = dsa.DSAParameterNumbers(p=1, q=2, g=3)
614        public_numbers = dsa.DSAPublicNumbers(
615            y=4, parameter_numbers=parameter_numbers
616        )
617        with pytest.raises(TypeError):
618            dsa.DSAPrivateNumbers(x=4, public_numbers=None)
619
620        with pytest.raises(TypeError):
621            dsa.DSAPrivateNumbers(x=None, public_numbers=public_numbers)
622
623    def test_repr(self):
624        parameter_numbers = dsa.DSAParameterNumbers(p=1, q=2, g=3)
625        assert (
626            repr(parameter_numbers) == "<DSAParameterNumbers(p=1, q=2, g=3)>"
627        )
628
629        public_numbers = dsa.DSAPublicNumbers(
630            y=4, parameter_numbers=parameter_numbers
631        )
632        assert repr(public_numbers) == (
633            "<DSAPublicNumbers(y=4, parameter_numbers=<DSAParameterNumbers(p=1"
634            ", q=2, g=3)>)>"
635        )
636
637
638class TestDSANumberEquality(object):
639    def test_parameter_numbers_eq(self):
640        param = dsa.DSAParameterNumbers(1, 2, 3)
641        assert param == dsa.DSAParameterNumbers(1, 2, 3)
642
643    def test_parameter_numbers_ne(self):
644        param = dsa.DSAParameterNumbers(1, 2, 3)
645        assert param != dsa.DSAParameterNumbers(1, 2, 4)
646        assert param != dsa.DSAParameterNumbers(1, 1, 3)
647        assert param != dsa.DSAParameterNumbers(2, 2, 3)
648        assert param != object()
649
650    def test_public_numbers_eq(self):
651        pub = dsa.DSAPublicNumbers(1, dsa.DSAParameterNumbers(1, 2, 3))
652        assert pub == dsa.DSAPublicNumbers(1, dsa.DSAParameterNumbers(1, 2, 3))
653
654    def test_public_numbers_ne(self):
655        pub = dsa.DSAPublicNumbers(1, dsa.DSAParameterNumbers(1, 2, 3))
656        assert pub != dsa.DSAPublicNumbers(2, dsa.DSAParameterNumbers(1, 2, 3))
657        assert pub != dsa.DSAPublicNumbers(1, dsa.DSAParameterNumbers(2, 2, 3))
658        assert pub != dsa.DSAPublicNumbers(1, dsa.DSAParameterNumbers(1, 3, 3))
659        assert pub != dsa.DSAPublicNumbers(1, dsa.DSAParameterNumbers(1, 2, 4))
660        assert pub != object()
661
662    def test_private_numbers_eq(self):
663        pub = dsa.DSAPublicNumbers(1, dsa.DSAParameterNumbers(1, 2, 3))
664        priv = dsa.DSAPrivateNumbers(1, pub)
665        assert priv == dsa.DSAPrivateNumbers(
666            1, dsa.DSAPublicNumbers(1, dsa.DSAParameterNumbers(1, 2, 3))
667        )
668
669    def test_private_numbers_ne(self):
670        pub = dsa.DSAPublicNumbers(1, dsa.DSAParameterNumbers(1, 2, 3))
671        priv = dsa.DSAPrivateNumbers(1, pub)
672        assert priv != dsa.DSAPrivateNumbers(
673            2, dsa.DSAPublicNumbers(1, dsa.DSAParameterNumbers(1, 2, 3))
674        )
675        assert priv != dsa.DSAPrivateNumbers(
676            1, dsa.DSAPublicNumbers(2, dsa.DSAParameterNumbers(1, 2, 3))
677        )
678        assert priv != dsa.DSAPrivateNumbers(
679            1, dsa.DSAPublicNumbers(1, dsa.DSAParameterNumbers(2, 2, 3))
680        )
681        assert priv != dsa.DSAPrivateNumbers(
682            1, dsa.DSAPublicNumbers(1, dsa.DSAParameterNumbers(1, 3, 3))
683        )
684        assert priv != dsa.DSAPrivateNumbers(
685            1, dsa.DSAPublicNumbers(1, dsa.DSAParameterNumbers(1, 2, 4))
686        )
687        assert priv != object()
688
689
690@pytest.mark.requires_backend_interface(interface=DSABackend)
691@pytest.mark.requires_backend_interface(interface=PEMSerializationBackend)
692class TestDSASerialization(object):
693    @pytest.mark.parametrize(
694        ("fmt", "password"),
695        itertools.product(
696            [
697                serialization.PrivateFormat.TraditionalOpenSSL,
698                serialization.PrivateFormat.PKCS8,
699            ],
700            [
701                b"s",
702                b"longerpassword",
703                b"!*$&(@#$*&($T@%_somesymbols",
704                b"\x01" * 1000,
705            ],
706        ),
707    )
708    def test_private_bytes_encrypted_pem(self, backend, fmt, password):
709        skip_fips_traditional_openssl(backend, fmt)
710        key_bytes = load_vectors_from_file(
711            os.path.join("asymmetric", "PKCS8", "unenc-dsa-pkcs8.pem"),
712            lambda pemfile: pemfile.read().encode(),
713        )
714        key = serialization.load_pem_private_key(key_bytes, None, backend)
715        serialized = key.private_bytes(
716            serialization.Encoding.PEM,
717            fmt,
718            serialization.BestAvailableEncryption(password),
719        )
720        loaded_key = serialization.load_pem_private_key(
721            serialized, password, backend
722        )
723        loaded_priv_num = loaded_key.private_numbers()
724        priv_num = key.private_numbers()
725        assert loaded_priv_num == priv_num
726
727    @pytest.mark.parametrize(
728        ("encoding", "fmt"),
729        [
730            (serialization.Encoding.Raw, serialization.PrivateFormat.PKCS8),
731            (serialization.Encoding.DER, serialization.PrivateFormat.Raw),
732            (serialization.Encoding.Raw, serialization.PrivateFormat.Raw),
733            (serialization.Encoding.X962, serialization.PrivateFormat.PKCS8),
734        ],
735    )
736    def test_private_bytes_rejects_invalid(self, encoding, fmt, backend):
737        key = DSA_KEY_1024.private_key(backend)
738        with pytest.raises(ValueError):
739            key.private_bytes(encoding, fmt, serialization.NoEncryption())
740
741    @pytest.mark.parametrize(
742        ("fmt", "password"),
743        [
744            [serialization.PrivateFormat.PKCS8, b"s"],
745            [serialization.PrivateFormat.PKCS8, b"longerpassword"],
746            [serialization.PrivateFormat.PKCS8, b"!*$&(@#$*&($T@%_somesymbol"],
747            [serialization.PrivateFormat.PKCS8, b"\x01" * 1000],
748        ],
749    )
750    def test_private_bytes_encrypted_der(self, backend, fmt, password):
751        key_bytes = load_vectors_from_file(
752            os.path.join("asymmetric", "PKCS8", "unenc-dsa-pkcs8.pem"),
753            lambda pemfile: pemfile.read().encode(),
754        )
755        key = serialization.load_pem_private_key(key_bytes, None, backend)
756        serialized = key.private_bytes(
757            serialization.Encoding.DER,
758            fmt,
759            serialization.BestAvailableEncryption(password),
760        )
761        loaded_key = serialization.load_der_private_key(
762            serialized, password, backend
763        )
764        loaded_priv_num = loaded_key.private_numbers()
765        priv_num = key.private_numbers()
766        assert loaded_priv_num == priv_num
767
768    @pytest.mark.parametrize(
769        ("encoding", "fmt", "loader_func"),
770        [
771            [
772                serialization.Encoding.PEM,
773                serialization.PrivateFormat.TraditionalOpenSSL,
774                serialization.load_pem_private_key,
775            ],
776            [
777                serialization.Encoding.DER,
778                serialization.PrivateFormat.TraditionalOpenSSL,
779                serialization.load_der_private_key,
780            ],
781            [
782                serialization.Encoding.PEM,
783                serialization.PrivateFormat.PKCS8,
784                serialization.load_pem_private_key,
785            ],
786            [
787                serialization.Encoding.DER,
788                serialization.PrivateFormat.PKCS8,
789                serialization.load_der_private_key,
790            ],
791        ],
792    )
793    def test_private_bytes_unencrypted(
794        self, backend, encoding, fmt, loader_func
795    ):
796        key = DSA_KEY_1024.private_key(backend)
797        serialized = key.private_bytes(
798            encoding, fmt, serialization.NoEncryption()
799        )
800        loaded_key = loader_func(serialized, None, backend)
801        loaded_priv_num = loaded_key.private_numbers()
802        priv_num = key.private_numbers()
803        assert loaded_priv_num == priv_num
804
805    @pytest.mark.skip_fips(
806        reason="Traditional OpenSSL key format is not supported in FIPS mode."
807    )
808    @pytest.mark.parametrize(
809        ("key_path", "encoding", "loader_func"),
810        [
811            [
812                os.path.join(
813                    "asymmetric",
814                    "Traditional_OpenSSL_Serialization",
815                    "dsa.1024.pem",
816                ),
817                serialization.Encoding.PEM,
818                serialization.load_pem_private_key,
819            ],
820            [
821                os.path.join(
822                    "asymmetric", "DER_Serialization", "dsa.1024.der"
823                ),
824                serialization.Encoding.DER,
825                serialization.load_der_private_key,
826            ],
827        ],
828    )
829    def test_private_bytes_traditional_openssl_unencrypted(
830        self, backend, key_path, encoding, loader_func
831    ):
832        key_bytes = load_vectors_from_file(
833            key_path, lambda pemfile: pemfile.read(), mode="rb"
834        )
835        key = loader_func(key_bytes, None, backend)
836        serialized = key.private_bytes(
837            encoding,
838            serialization.PrivateFormat.TraditionalOpenSSL,
839            serialization.NoEncryption(),
840        )
841        assert serialized == key_bytes
842
843    def test_private_bytes_traditional_der_encrypted_invalid(self, backend):
844        key = DSA_KEY_1024.private_key(backend)
845        with pytest.raises(ValueError):
846            key.private_bytes(
847                serialization.Encoding.DER,
848                serialization.PrivateFormat.TraditionalOpenSSL,
849                serialization.BestAvailableEncryption(b"password"),
850            )
851
852    def test_private_bytes_invalid_encoding(self, backend):
853        key = load_vectors_from_file(
854            os.path.join("asymmetric", "PKCS8", "unenc-dsa-pkcs8.pem"),
855            lambda pemfile: serialization.load_pem_private_key(
856                pemfile.read().encode(), None, backend
857            ),
858        )
859        with pytest.raises(TypeError):
860            key.private_bytes(
861                "notencoding",
862                serialization.PrivateFormat.PKCS8,
863                serialization.NoEncryption(),
864            )
865
866    def test_private_bytes_invalid_format(self, backend):
867        key = load_vectors_from_file(
868            os.path.join("asymmetric", "PKCS8", "unenc-dsa-pkcs8.pem"),
869            lambda pemfile: serialization.load_pem_private_key(
870                pemfile.read().encode(), None, backend
871            ),
872        )
873        with pytest.raises(TypeError):
874            key.private_bytes(
875                serialization.Encoding.PEM,
876                "invalidformat",
877                serialization.NoEncryption(),
878            )
879
880    def test_private_bytes_invalid_encryption_algorithm(self, backend):
881        key = load_vectors_from_file(
882            os.path.join("asymmetric", "PKCS8", "unenc-dsa-pkcs8.pem"),
883            lambda pemfile: serialization.load_pem_private_key(
884                pemfile.read().encode(), None, backend
885            ),
886        )
887        with pytest.raises(TypeError):
888            key.private_bytes(
889                serialization.Encoding.PEM,
890                serialization.PrivateFormat.TraditionalOpenSSL,
891                "notanencalg",
892            )
893
894    def test_private_bytes_unsupported_encryption_type(self, backend):
895        key = load_vectors_from_file(
896            os.path.join("asymmetric", "PKCS8", "unenc-dsa-pkcs8.pem"),
897            lambda pemfile: serialization.load_pem_private_key(
898                pemfile.read().encode(), None, backend
899            ),
900        )
901        with pytest.raises(ValueError):
902            key.private_bytes(
903                serialization.Encoding.PEM,
904                serialization.PrivateFormat.TraditionalOpenSSL,
905                DummyKeySerializationEncryption(),
906            )
907
908
909@pytest.mark.requires_backend_interface(interface=DSABackend)
910@pytest.mark.requires_backend_interface(interface=PEMSerializationBackend)
911class TestDSAPEMPublicKeySerialization(object):
912    @pytest.mark.parametrize(
913        ("key_path", "loader_func", "encoding"),
914        [
915            (
916                os.path.join("asymmetric", "PKCS8", "unenc-dsa-pkcs8.pub.pem"),
917                serialization.load_pem_public_key,
918                serialization.Encoding.PEM,
919            ),
920            (
921                os.path.join(
922                    "asymmetric",
923                    "DER_Serialization",
924                    "unenc-dsa-pkcs8.pub.der",
925                ),
926                serialization.load_der_public_key,
927                serialization.Encoding.DER,
928            ),
929        ],
930    )
931    def test_public_bytes_match(
932        self, key_path, loader_func, encoding, backend
933    ):
934        key_bytes = load_vectors_from_file(
935            key_path, lambda pemfile: pemfile.read(), mode="rb"
936        )
937        key = loader_func(key_bytes, backend)
938        serialized = key.public_bytes(
939            encoding,
940            serialization.PublicFormat.SubjectPublicKeyInfo,
941        )
942        assert serialized == key_bytes
943
944    def test_public_bytes_openssh(self, backend):
945        key_bytes = load_vectors_from_file(
946            os.path.join("asymmetric", "PKCS8", "unenc-dsa-pkcs8.pub.pem"),
947            lambda pemfile: pemfile.read(),
948            mode="rb",
949        )
950        key = serialization.load_pem_public_key(key_bytes, backend)
951
952        ssh_bytes = key.public_bytes(
953            serialization.Encoding.OpenSSH, serialization.PublicFormat.OpenSSH
954        )
955        assert ssh_bytes == (
956            b"ssh-dss AAAAB3NzaC1kc3MAAACBAKoJMMwUWCUiHK/6KKwolBlqJ4M95ewhJweR"
957            b"aJQgd3Si57I4sNNvGySZosJYUIPrAUMpJEGNhn+qIS3RBx1NzrJ4J5StOTzAik1K"
958            b"2n9o1ug5pfzTS05ALYLLioy0D+wxkRv5vTYLA0yqy0xelHmSVzyekAmcGw8FlAyr"
959            b"5dLeSaFnAAAAFQCtwOhps28KwBOmgf301ImdaYIEUQAAAIEAjGtFia+lOk0QSL/D"
960            b"RtHzhsp1UhzPct2qJRKGiA7hMgH/SIkLv8M9ebrK7HHnp3hQe9XxpmQi45QVvgPn"
961            b"EUG6Mk9bkxMZKRgsiKn6QGKDYGbOvnS1xmkMfRARBsJAq369VOTjMB/Qhs5q2ski"
962            b"+ycTorCIfLoTubxozlz/8kHNMkYAAACAKyYOqX3GoSrpMsZA5989j/BKigWgMk+N"
963            b"Xxsj8V+hcP8/QgYRJO/yWGyxG0moLc3BuQ/GqE+xAQnLZ9tdLalxrq8Xvl43KEVj"
964            b"5MZNnl/ISAJYsxnw3inVTYNQcNnih5FNd9+BSR9EI7YtqYTrP0XrKin86l2uUlrG"
965            b"q2vM4Ev99bY="
966        )
967
968    def test_public_bytes_invalid_encoding(self, backend):
969        key = DSA_KEY_2048.private_key(backend).public_key()
970        with pytest.raises(TypeError):
971            key.public_bytes(
972                "notencoding", serialization.PublicFormat.SubjectPublicKeyInfo
973            )
974
975    def test_public_bytes_invalid_format(self, backend):
976        key = DSA_KEY_2048.private_key(backend).public_key()
977        with pytest.raises(TypeError):
978            key.public_bytes(serialization.Encoding.PEM, "invalidformat")
979
980    def test_public_bytes_pkcs1_unsupported(self, backend):
981        key = DSA_KEY_2048.private_key(backend).public_key()
982        with pytest.raises(ValueError):
983            key.public_bytes(
984                serialization.Encoding.PEM, serialization.PublicFormat.PKCS1
985            )
986
987    @pytest.mark.parametrize(
988        ("encoding", "fmt"),
989        [
990            (
991                serialization.Encoding.Raw,
992                serialization.PublicFormat.SubjectPublicKeyInfo,
993            ),
994            (serialization.Encoding.Raw, serialization.PublicFormat.PKCS1),
995        ]
996        + list(
997            itertools.product(
998                [
999                    serialization.Encoding.Raw,
1000                    serialization.Encoding.X962,
1001                    serialization.Encoding.PEM,
1002                    serialization.Encoding.DER,
1003                ],
1004                [
1005                    serialization.PublicFormat.Raw,
1006                    serialization.PublicFormat.UncompressedPoint,
1007                    serialization.PublicFormat.CompressedPoint,
1008                ],
1009            )
1010        ),
1011    )
1012    def test_public_bytes_rejects_invalid(self, encoding, fmt, backend):
1013        key = DSA_KEY_2048.private_key(backend).public_key()
1014        with pytest.raises(ValueError):
1015            key.public_bytes(encoding, fmt)
1016