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 binascii 8import itertools 9import os 10 11import pytest 12 13from cryptography.hazmat.backends.interfaces import ( 14 DERSerializationBackend, 15 DHBackend, 16 PEMSerializationBackend, 17) 18from cryptography.hazmat.primitives import serialization 19from cryptography.hazmat.primitives.asymmetric import dh 20from cryptography.utils import int_from_bytes 21 22from .fixtures_dh import FFDH3072_P 23from ...doubles import DummyKeySerializationEncryption 24from ...utils import load_nist_vectors, load_vectors_from_file 25 26# RFC 3526 27P_1536 = int( 28 "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" 29 "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" 30 "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" 31 "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" 32 "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" 33 "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" 34 "83655D23DCA3AD961C62F356208552BB9ED529077096966D" 35 "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF", 36 16, 37) 38 39 40def _skip_dhx_unsupported(backend, is_dhx): 41 if not is_dhx: 42 return 43 if not backend.dh_x942_serialization_supported(): 44 pytest.skip("DH x9.42 serialization is not supported") 45 46 47def test_dh_parameternumbers(): 48 params = dh.DHParameterNumbers(P_1536, 2) 49 50 assert params.p == P_1536 51 assert params.g == 2 52 53 with pytest.raises(TypeError): 54 dh.DHParameterNumbers(None, 2) 55 56 with pytest.raises(TypeError): 57 dh.DHParameterNumbers(P_1536, None) 58 59 with pytest.raises(TypeError): 60 dh.DHParameterNumbers(None, None) 61 62 with pytest.raises(ValueError): 63 dh.DHParameterNumbers(P_1536, 1) 64 65 # p too small 66 with pytest.raises(ValueError): 67 dh.DHParameterNumbers(65537, 2) 68 69 params = dh.DHParameterNumbers(P_1536, 7, 1245) 70 71 assert params.p == P_1536 72 assert params.g == 7 73 assert params.q == 1245 74 75 with pytest.raises(TypeError): 76 dh.DHParameterNumbers(P_1536, 2, "hello") 77 78 79def test_dh_numbers(): 80 params = dh.DHParameterNumbers(P_1536, 2) 81 82 public = dh.DHPublicNumbers(1, params) 83 84 assert public.parameter_numbers is params 85 assert public.y == 1 86 87 with pytest.raises(TypeError): 88 dh.DHPublicNumbers(1, None) 89 90 with pytest.raises(TypeError): 91 dh.DHPublicNumbers(None, params) 92 93 private = dh.DHPrivateNumbers(1, public) 94 95 assert private.public_numbers is public 96 assert private.x == 1 97 98 with pytest.raises(TypeError): 99 dh.DHPrivateNumbers(1, None) 100 101 with pytest.raises(TypeError): 102 dh.DHPrivateNumbers(None, public) 103 104 105def test_dh_parameter_numbers_equality(): 106 assert dh.DHParameterNumbers(P_1536, 2) == dh.DHParameterNumbers(P_1536, 2) 107 assert dh.DHParameterNumbers(P_1536, 7, 12345) == dh.DHParameterNumbers( 108 P_1536, 7, 12345 109 ) 110 assert dh.DHParameterNumbers(P_1536 + 2, 2) != dh.DHParameterNumbers( 111 P_1536, 2 112 ) 113 assert dh.DHParameterNumbers(P_1536, 2, 123) != dh.DHParameterNumbers( 114 P_1536, 2, 456 115 ) 116 assert dh.DHParameterNumbers(P_1536, 5) != dh.DHParameterNumbers(P_1536, 2) 117 assert dh.DHParameterNumbers(P_1536, 2) != object() 118 119 120def test_dh_private_numbers_equality(): 121 params = dh.DHParameterNumbers(P_1536, 2) 122 public = dh.DHPublicNumbers(1, params) 123 private = dh.DHPrivateNumbers(2, public) 124 125 assert private == dh.DHPrivateNumbers(2, public) 126 assert private != dh.DHPrivateNumbers(0, public) 127 assert private != dh.DHPrivateNumbers(2, dh.DHPublicNumbers(0, params)) 128 assert private != dh.DHPrivateNumbers( 129 2, dh.DHPublicNumbers(1, dh.DHParameterNumbers(P_1536, 5)) 130 ) 131 assert private != object() 132 133 134def test_dh_public_numbers_equality(): 135 params = dh.DHParameterNumbers(P_1536, 2) 136 public = dh.DHPublicNumbers(1, params) 137 138 assert public == dh.DHPublicNumbers(1, params) 139 assert public != dh.DHPublicNumbers(0, params) 140 assert public != dh.DHPublicNumbers(1, dh.DHParameterNumbers(P_1536, 5)) 141 assert public != object() 142 143 144@pytest.mark.requires_backend_interface(interface=DHBackend) 145class TestDH(object): 146 def test_small_key_generate_dh(self, backend): 147 with pytest.raises(ValueError): 148 dh.generate_parameters(2, 511, backend) 149 150 def test_unsupported_generator_generate_dh(self, backend): 151 with pytest.raises(ValueError): 152 dh.generate_parameters(7, 512, backend) 153 154 @pytest.mark.skip_fips(reason="non-FIPS parameters") 155 def test_dh_parameters_supported(self, backend): 156 valid_p = int( 157 b"907c7211ae61aaaba1825ff53b6cb71ac6df9f1a424c033f4a0a41ac42fad3a9" 158 b"bcfc7f938a269710ed69e330523e4039029b7900977c740990d46efed79b9bbe" 159 b"73505ae878808944ce4d9c6c52daecc0a87dc889c53499be93db8551ee685f30" 160 b"349bf1b443d4ebaee0d5e8b441a40d4e8178f8f612f657a5eb91e0a8e" 161 b"107755f", 162 16, 163 ) 164 assert backend.dh_parameters_supported(valid_p, 5) 165 assert not backend.dh_parameters_supported(23, 22) 166 167 @pytest.mark.parametrize( 168 "vector", 169 load_vectors_from_file( 170 os.path.join("asymmetric", "DH", "rfc3526.txt"), load_nist_vectors 171 ), 172 ) 173 def test_dh_parameters_allows_rfc3526_groups(self, backend, vector): 174 p = int_from_bytes(binascii.unhexlify(vector["p"]), "big") 175 if ( 176 backend._fips_enabled 177 and p.bit_length() < backend._fips_dh_min_modulus 178 ): 179 pytest.skip("modulus too small for FIPS mode") 180 181 params = dh.DHParameterNumbers(p, int(vector["g"])) 182 param = params.parameters(backend) 183 key = param.generate_private_key() 184 # This confirms that a key generated with this group 185 # will pass DH_check when we serialize and de-serialize it via 186 # the Numbers path. 187 roundtripped_key = key.private_numbers().private_key(backend) 188 assert key.private_numbers() == roundtripped_key.private_numbers() 189 190 @pytest.mark.skip_fips(reason="non-FIPS parameters") 191 @pytest.mark.parametrize( 192 "vector", 193 load_vectors_from_file( 194 os.path.join("asymmetric", "DH", "RFC5114.txt"), load_nist_vectors 195 ), 196 ) 197 def test_dh_parameters_supported_with_q(self, backend, vector): 198 assert backend.dh_parameters_supported( 199 int(vector["p"], 16), int(vector["g"], 16), int(vector["q"], 16) 200 ) 201 202 @pytest.mark.skip_fips(reason="modulus too small for FIPS") 203 @pytest.mark.parametrize("with_q", [False, True]) 204 def test_convert_to_numbers(self, backend, with_q): 205 if with_q: 206 vector = load_vectors_from_file( 207 os.path.join("asymmetric", "DH", "RFC5114.txt"), 208 load_nist_vectors, 209 )[0] 210 p = int(vector["p"], 16) 211 g = int(vector["g"], 16) 212 q = int(vector["q"], 16) 213 else: 214 parameters = backend.generate_dh_private_key_and_parameters(2, 512) 215 216 private = parameters.private_numbers() 217 218 p = private.public_numbers.parameter_numbers.p 219 g = private.public_numbers.parameter_numbers.g 220 q = None 221 222 params = dh.DHParameterNumbers(p, g, q) 223 public = dh.DHPublicNumbers(1, params) 224 private = dh.DHPrivateNumbers(2, public) 225 226 deserialized_params = params.parameters(backend) 227 deserialized_public = public.public_key(backend) 228 deserialized_private = private.private_key(backend) 229 230 assert isinstance( 231 deserialized_params, dh.DHParametersWithSerialization 232 ) 233 assert isinstance(deserialized_public, dh.DHPublicKeyWithSerialization) 234 assert isinstance( 235 deserialized_private, dh.DHPrivateKeyWithSerialization 236 ) 237 238 @pytest.mark.skip_fips(reason="FIPS requires specific parameters") 239 def test_numbers_unsupported_parameters(self, backend): 240 # p is set to P_1536 + 1 because when calling private_key we want it to 241 # fail the DH_check call OpenSSL does, but we specifically want it to 242 # fail such that we don't get a DH_NOT_SUITABLE_GENERATOR. We can cause 243 # this by making sure p is not prime. 244 params = dh.DHParameterNumbers(P_1536 + 1, 2) 245 public = dh.DHPublicNumbers(1, params) 246 private = dh.DHPrivateNumbers(2, public) 247 248 with pytest.raises(ValueError): 249 private.private_key(backend) 250 251 @pytest.mark.skip_fips(reason="FIPS requires key size >= 2048") 252 @pytest.mark.parametrize("with_q", [False, True]) 253 def test_generate_dh(self, backend, with_q): 254 if with_q: 255 vector = load_vectors_from_file( 256 os.path.join("asymmetric", "DH", "RFC5114.txt"), 257 load_nist_vectors, 258 )[0] 259 p = int(vector["p"], 16) 260 g = int(vector["g"], 16) 261 q = int(vector["q"], 16) 262 parameters = dh.DHParameterNumbers(p, g, q).parameters(backend) 263 key_size = 1024 264 else: 265 generator = 2 266 key_size = 512 267 268 parameters = dh.generate_parameters(generator, key_size, backend) 269 assert isinstance(parameters, dh.DHParameters) 270 271 key = parameters.generate_private_key() 272 assert isinstance(key, dh.DHPrivateKey) 273 assert key.key_size == key_size 274 275 public = key.public_key() 276 assert isinstance(public, dh.DHPublicKey) 277 assert public.key_size == key_size 278 279 assert isinstance(parameters, dh.DHParametersWithSerialization) 280 parameter_numbers = parameters.parameter_numbers() 281 assert isinstance(parameter_numbers, dh.DHParameterNumbers) 282 assert parameter_numbers.p.bit_length() == key_size 283 284 assert isinstance(public, dh.DHPublicKeyWithSerialization) 285 assert isinstance(public.public_numbers(), dh.DHPublicNumbers) 286 assert isinstance(public.parameters(), dh.DHParameters) 287 288 assert isinstance(key, dh.DHPrivateKeyWithSerialization) 289 assert isinstance(key.private_numbers(), dh.DHPrivateNumbers) 290 assert isinstance(key.parameters(), dh.DHParameters) 291 292 def test_exchange(self, backend): 293 parameters = FFDH3072_P.parameters(backend) 294 assert isinstance(parameters, dh.DHParameters) 295 296 key1 = parameters.generate_private_key() 297 key2 = parameters.generate_private_key() 298 299 symkey1 = key1.exchange(key2.public_key()) 300 assert symkey1 301 assert len(symkey1) == 3072 // 8 302 303 symkey2 = key2.exchange(key1.public_key()) 304 assert symkey1 == symkey2 305 306 def test_exchange_algorithm(self, backend): 307 parameters = FFDH3072_P.parameters(backend) 308 key1 = parameters.generate_private_key() 309 key2 = parameters.generate_private_key() 310 311 shared_key_bytes = key2.exchange(key1.public_key()) 312 symkey = int_from_bytes(shared_key_bytes, "big") 313 314 symkey_manual = pow( 315 key1.public_key().public_numbers().y, 316 key2.private_numbers().x, 317 parameters.parameter_numbers().p, 318 ) 319 320 assert symkey == symkey_manual 321 322 @pytest.mark.skip_fips(reason="key_size too small for FIPS") 323 def test_symmetric_key_padding(self, backend): 324 """ 325 This test has specific parameters that produce a symmetric key 326 In length 63 bytes instead 64. We make sure here that we add 327 padding to the key. 328 """ 329 p = int( 330 "11859949538425015739337467917303613431031019140213666" 331 "129025407300654026585086345323066284800963463204246390" 332 "256567934582260424238844463330887962689642467123" 333 ) 334 g = 2 335 y = int( 336 "32155788395534640648739966373159697798396966919821525" 337 "72238852825117261342483718574508213761865276905503199" 338 "969908098203345481366464874759377454476688391248" 339 ) 340 x = int( 341 "409364065449673443397833358558926598469347813468816037" 342 "268451847116982490733450463194921405069999008617231539" 343 "7147035896687401350877308899732826446337707128" 344 ) 345 parameters = dh.DHParameterNumbers(p, g) 346 public = dh.DHPublicNumbers(y, parameters) 347 private = dh.DHPrivateNumbers(x, public) 348 key = private.private_key(backend) 349 symkey = key.exchange(public.public_key(backend)) 350 assert len(symkey) == 512 // 8 351 assert symkey[:1] == b"\x00" 352 353 @pytest.mark.parametrize( 354 "vector", 355 load_vectors_from_file( 356 os.path.join("asymmetric", "DH", "bad_exchange.txt"), 357 load_nist_vectors, 358 ), 359 ) 360 def test_bad_exchange(self, backend, vector): 361 if ( 362 backend._fips_enabled 363 and int(vector["p1"]) < backend._fips_dh_min_modulus 364 ): 365 pytest.skip("modulus too small for FIPS mode") 366 parameters1 = dh.DHParameterNumbers( 367 int(vector["p1"]), int(vector["g"]) 368 ) 369 public1 = dh.DHPublicNumbers(int(vector["y1"]), parameters1) 370 private1 = dh.DHPrivateNumbers(int(vector["x1"]), public1) 371 key1 = private1.private_key(backend) 372 pub_key1 = key1.public_key() 373 374 parameters2 = dh.DHParameterNumbers( 375 int(vector["p2"]), int(vector["g"]) 376 ) 377 public2 = dh.DHPublicNumbers(int(vector["y2"]), parameters2) 378 private2 = dh.DHPrivateNumbers(int(vector["x2"]), public2) 379 key2 = private2.private_key(backend) 380 pub_key2 = key2.public_key() 381 382 if pub_key2.public_numbers().y >= parameters1.p: 383 with pytest.raises(ValueError): 384 key1.exchange(pub_key2) 385 else: 386 symkey1 = key1.exchange(pub_key2) 387 assert symkey1 388 389 symkey2 = key2.exchange(pub_key1) 390 391 assert symkey1 != symkey2 392 393 @pytest.mark.skip_fips(reason="key_size too small for FIPS") 394 def test_load_256bit_key_from_pkcs8(self, backend): 395 data = load_vectors_from_file( 396 os.path.join("asymmetric", "DH", "dh_key_256.pem"), 397 lambda pemfile: pemfile.read(), 398 mode="rb", 399 ) 400 key = serialization.load_pem_private_key(data, None, backend) 401 assert key.key_size == 256 402 403 @pytest.mark.parametrize( 404 "vector", 405 load_vectors_from_file( 406 os.path.join("asymmetric", "DH", "vec.txt"), load_nist_vectors 407 ), 408 ) 409 def test_dh_vectors(self, backend, vector): 410 if ( 411 backend._fips_enabled 412 and int(vector["p"]) < backend._fips_dh_min_modulus 413 ): 414 pytest.skip("modulus too small for FIPS mode") 415 416 if int(vector["p"]).bit_length() < 512: 417 pytest.skip("DH keys less than 512 bits are unsupported") 418 419 parameters = dh.DHParameterNumbers(int(vector["p"]), int(vector["g"])) 420 public = dh.DHPublicNumbers(int(vector["y"]), parameters) 421 private = dh.DHPrivateNumbers(int(vector["x"]), public) 422 key = private.private_key(backend) 423 symkey = key.exchange(public.public_key(backend)) 424 425 assert int_from_bytes(symkey, "big") == int(vector["k"], 16) 426 427 @pytest.mark.skip_fips(reason="non-FIPS parameters") 428 @pytest.mark.parametrize( 429 "vector", 430 load_vectors_from_file( 431 os.path.join("asymmetric", "DH", "RFC5114.txt"), load_nist_vectors 432 ), 433 ) 434 def test_dh_vectors_with_q(self, backend, vector): 435 parameters = dh.DHParameterNumbers( 436 int(vector["p"], 16), int(vector["g"], 16), int(vector["q"], 16) 437 ) 438 public1 = dh.DHPublicNumbers(int(vector["ystatcavs"], 16), parameters) 439 private1 = dh.DHPrivateNumbers(int(vector["xstatcavs"], 16), public1) 440 public2 = dh.DHPublicNumbers(int(vector["ystatiut"], 16), parameters) 441 private2 = dh.DHPrivateNumbers(int(vector["xstatiut"], 16), public2) 442 key1 = private1.private_key(backend) 443 key2 = private2.private_key(backend) 444 symkey1 = key1.exchange(public2.public_key(backend)) 445 symkey2 = key2.exchange(public1.public_key(backend)) 446 447 assert int_from_bytes(symkey1, "big") == int(vector["z"], 16) 448 assert int_from_bytes(symkey2, "big") == int(vector["z"], 16) 449 450 451@pytest.mark.requires_backend_interface(interface=DHBackend) 452@pytest.mark.requires_backend_interface(interface=PEMSerializationBackend) 453@pytest.mark.requires_backend_interface(interface=DERSerializationBackend) 454class TestDHPrivateKeySerialization(object): 455 @pytest.mark.parametrize( 456 ("encoding", "loader_func"), 457 [ 458 [serialization.Encoding.PEM, serialization.load_pem_private_key], 459 [serialization.Encoding.DER, serialization.load_der_private_key], 460 ], 461 ) 462 def test_private_bytes_unencrypted(self, backend, encoding, loader_func): 463 parameters = FFDH3072_P.parameters(backend) 464 key = parameters.generate_private_key() 465 serialized = key.private_bytes( 466 encoding, 467 serialization.PrivateFormat.PKCS8, 468 serialization.NoEncryption(), 469 ) 470 loaded_key = loader_func(serialized, None, backend) 471 loaded_priv_num = loaded_key.private_numbers() 472 priv_num = key.private_numbers() 473 assert loaded_priv_num == priv_num 474 475 @pytest.mark.parametrize( 476 ("encoding", "fmt"), 477 [ 478 (serialization.Encoding.Raw, serialization.PrivateFormat.PKCS8), 479 (serialization.Encoding.DER, serialization.PrivateFormat.Raw), 480 (serialization.Encoding.Raw, serialization.PrivateFormat.Raw), 481 (serialization.Encoding.X962, serialization.PrivateFormat.PKCS8), 482 ], 483 ) 484 def test_private_bytes_rejects_invalid(self, encoding, fmt, backend): 485 parameters = FFDH3072_P.parameters(backend) 486 key = parameters.generate_private_key() 487 with pytest.raises(ValueError): 488 key.private_bytes(encoding, fmt, serialization.NoEncryption()) 489 490 @pytest.mark.skip_fips(reason="non-FIPS parameters") 491 @pytest.mark.parametrize( 492 ("key_path", "loader_func", "encoding", "is_dhx"), 493 [ 494 ( 495 os.path.join("asymmetric", "DH", "dhkey.pem"), 496 serialization.load_pem_private_key, 497 serialization.Encoding.PEM, 498 False, 499 ), 500 ( 501 os.path.join("asymmetric", "DH", "dhkey.der"), 502 serialization.load_der_private_key, 503 serialization.Encoding.DER, 504 False, 505 ), 506 ( 507 os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.pem"), 508 serialization.load_pem_private_key, 509 serialization.Encoding.PEM, 510 True, 511 ), 512 ( 513 os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.der"), 514 serialization.load_der_private_key, 515 serialization.Encoding.DER, 516 True, 517 ), 518 ], 519 ) 520 def test_private_bytes_match( 521 self, key_path, loader_func, encoding, is_dhx, backend 522 ): 523 _skip_dhx_unsupported(backend, is_dhx) 524 key_bytes = load_vectors_from_file( 525 key_path, lambda pemfile: pemfile.read(), mode="rb" 526 ) 527 key = loader_func(key_bytes, None, backend) 528 serialized = key.private_bytes( 529 encoding, 530 serialization.PrivateFormat.PKCS8, 531 serialization.NoEncryption(), 532 ) 533 assert serialized == key_bytes 534 535 @pytest.mark.skip_fips(reason="non-FIPS parameters") 536 @pytest.mark.parametrize( 537 ("key_path", "loader_func", "vec_path", "is_dhx"), 538 [ 539 ( 540 os.path.join("asymmetric", "DH", "dhkey.pem"), 541 serialization.load_pem_private_key, 542 os.path.join("asymmetric", "DH", "dhkey.txt"), 543 False, 544 ), 545 ( 546 os.path.join("asymmetric", "DH", "dhkey.der"), 547 serialization.load_der_private_key, 548 os.path.join("asymmetric", "DH", "dhkey.txt"), 549 False, 550 ), 551 ( 552 os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.pem"), 553 serialization.load_pem_private_key, 554 os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.txt"), 555 True, 556 ), 557 ( 558 os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.der"), 559 serialization.load_der_private_key, 560 os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.txt"), 561 True, 562 ), 563 ], 564 ) 565 def test_private_bytes_values( 566 self, key_path, loader_func, vec_path, is_dhx, backend 567 ): 568 _skip_dhx_unsupported(backend, is_dhx) 569 key_bytes = load_vectors_from_file( 570 key_path, lambda pemfile: pemfile.read(), mode="rb" 571 ) 572 vec = load_vectors_from_file(vec_path, load_nist_vectors)[0] 573 key = loader_func(key_bytes, None, backend) 574 private_numbers = key.private_numbers() 575 assert private_numbers.x == int(vec["x"], 16) 576 assert private_numbers.public_numbers.y == int(vec["y"], 16) 577 assert private_numbers.public_numbers.parameter_numbers.g == int( 578 vec["g"], 16 579 ) 580 assert private_numbers.public_numbers.parameter_numbers.p == int( 581 vec["p"], 16 582 ) 583 if "q" in vec: 584 assert private_numbers.public_numbers.parameter_numbers.q == int( 585 vec["q"], 16 586 ) 587 else: 588 assert private_numbers.public_numbers.parameter_numbers.q is None 589 590 def test_private_bytes_traditional_openssl_invalid(self, backend): 591 parameters = FFDH3072_P.parameters(backend) 592 key = parameters.generate_private_key() 593 with pytest.raises(ValueError): 594 key.private_bytes( 595 serialization.Encoding.PEM, 596 serialization.PrivateFormat.TraditionalOpenSSL, 597 serialization.NoEncryption(), 598 ) 599 600 def test_private_bytes_invalid_encoding(self, backend): 601 parameters = FFDH3072_P.parameters(backend) 602 key = parameters.generate_private_key() 603 with pytest.raises(TypeError): 604 key.private_bytes( 605 "notencoding", 606 serialization.PrivateFormat.PKCS8, 607 serialization.NoEncryption(), 608 ) 609 610 def test_private_bytes_invalid_format(self, backend): 611 parameters = FFDH3072_P.parameters(backend) 612 key = parameters.generate_private_key() 613 with pytest.raises(ValueError): 614 key.private_bytes( 615 serialization.Encoding.PEM, 616 "invalidformat", 617 serialization.NoEncryption(), 618 ) 619 620 def test_private_bytes_invalid_encryption_algorithm(self, backend): 621 parameters = FFDH3072_P.parameters(backend) 622 key = parameters.generate_private_key() 623 with pytest.raises(TypeError): 624 key.private_bytes( 625 serialization.Encoding.PEM, 626 serialization.PrivateFormat.PKCS8, 627 "notanencalg", 628 ) 629 630 def test_private_bytes_unsupported_encryption_type(self, backend): 631 parameters = FFDH3072_P.parameters(backend) 632 key = parameters.generate_private_key() 633 with pytest.raises(ValueError): 634 key.private_bytes( 635 serialization.Encoding.PEM, 636 serialization.PrivateFormat.PKCS8, 637 DummyKeySerializationEncryption(), 638 ) 639 640 641@pytest.mark.requires_backend_interface(interface=DHBackend) 642@pytest.mark.requires_backend_interface(interface=PEMSerializationBackend) 643@pytest.mark.requires_backend_interface(interface=DERSerializationBackend) 644class TestDHPublicKeySerialization(object): 645 @pytest.mark.parametrize( 646 ("encoding", "loader_func"), 647 [ 648 [serialization.Encoding.PEM, serialization.load_pem_public_key], 649 [serialization.Encoding.DER, serialization.load_der_public_key], 650 ], 651 ) 652 def test_public_bytes(self, backend, encoding, loader_func): 653 parameters = FFDH3072_P.parameters(backend) 654 key = parameters.generate_private_key().public_key() 655 serialized = key.public_bytes( 656 encoding, serialization.PublicFormat.SubjectPublicKeyInfo 657 ) 658 loaded_key = loader_func(serialized, backend) 659 loaded_pub_num = loaded_key.public_numbers() 660 pub_num = key.public_numbers() 661 assert loaded_pub_num == pub_num 662 663 @pytest.mark.parametrize( 664 ("key_path", "loader_func", "encoding", "is_dhx"), 665 [ 666 ( 667 os.path.join("asymmetric", "DH", "dhpub.pem"), 668 serialization.load_pem_public_key, 669 serialization.Encoding.PEM, 670 False, 671 ), 672 ( 673 os.path.join("asymmetric", "DH", "dhpub.der"), 674 serialization.load_der_public_key, 675 serialization.Encoding.DER, 676 False, 677 ), 678 ( 679 os.path.join("asymmetric", "DH", "dhpub_rfc5114_2.pem"), 680 serialization.load_pem_public_key, 681 serialization.Encoding.PEM, 682 True, 683 ), 684 ( 685 os.path.join("asymmetric", "DH", "dhpub_rfc5114_2.der"), 686 serialization.load_der_public_key, 687 serialization.Encoding.DER, 688 True, 689 ), 690 ], 691 ) 692 def test_public_bytes_match( 693 self, key_path, loader_func, encoding, is_dhx, backend 694 ): 695 _skip_dhx_unsupported(backend, is_dhx) 696 key_bytes = load_vectors_from_file( 697 key_path, lambda pemfile: pemfile.read(), mode="rb" 698 ) 699 pub_key = loader_func(key_bytes, backend) 700 serialized = pub_key.public_bytes( 701 encoding, 702 serialization.PublicFormat.SubjectPublicKeyInfo, 703 ) 704 assert serialized == key_bytes 705 706 @pytest.mark.parametrize( 707 ("key_path", "loader_func", "vec_path", "is_dhx"), 708 [ 709 ( 710 os.path.join("asymmetric", "DH", "dhpub.pem"), 711 serialization.load_pem_public_key, 712 os.path.join("asymmetric", "DH", "dhkey.txt"), 713 False, 714 ), 715 ( 716 os.path.join("asymmetric", "DH", "dhpub.der"), 717 serialization.load_der_public_key, 718 os.path.join("asymmetric", "DH", "dhkey.txt"), 719 False, 720 ), 721 ( 722 os.path.join("asymmetric", "DH", "dhpub_rfc5114_2.pem"), 723 serialization.load_pem_public_key, 724 os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.txt"), 725 True, 726 ), 727 ( 728 os.path.join("asymmetric", "DH", "dhpub_rfc5114_2.der"), 729 serialization.load_der_public_key, 730 os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.txt"), 731 True, 732 ), 733 ], 734 ) 735 def test_public_bytes_values( 736 self, key_path, loader_func, vec_path, is_dhx, backend 737 ): 738 _skip_dhx_unsupported(backend, is_dhx) 739 key_bytes = load_vectors_from_file( 740 key_path, lambda pemfile: pemfile.read(), mode="rb" 741 ) 742 vec = load_vectors_from_file(vec_path, load_nist_vectors)[0] 743 pub_key = loader_func(key_bytes, backend) 744 public_numbers = pub_key.public_numbers() 745 assert public_numbers.y == int(vec["y"], 16) 746 assert public_numbers.parameter_numbers.g == int(vec["g"], 16) 747 assert public_numbers.parameter_numbers.p == int(vec["p"], 16) 748 if "q" in vec: 749 assert public_numbers.parameter_numbers.q == int(vec["q"], 16) 750 else: 751 assert public_numbers.parameter_numbers.q is None 752 753 def test_public_bytes_invalid_encoding(self, backend): 754 parameters = FFDH3072_P.parameters(backend) 755 key = parameters.generate_private_key().public_key() 756 with pytest.raises(TypeError): 757 key.public_bytes( 758 "notencoding", serialization.PublicFormat.SubjectPublicKeyInfo 759 ) 760 761 def test_public_bytes_pkcs1_unsupported(self, backend): 762 parameters = FFDH3072_P.parameters(backend) 763 key = parameters.generate_private_key().public_key() 764 with pytest.raises(ValueError): 765 key.public_bytes( 766 serialization.Encoding.PEM, serialization.PublicFormat.PKCS1 767 ) 768 769 770@pytest.mark.requires_backend_interface(interface=DHBackend) 771@pytest.mark.requires_backend_interface(interface=PEMSerializationBackend) 772@pytest.mark.requires_backend_interface(interface=DERSerializationBackend) 773class TestDHParameterSerialization(object): 774 @pytest.mark.parametrize( 775 ("encoding", "loader_func"), 776 [ 777 [serialization.Encoding.PEM, serialization.load_pem_parameters], 778 [serialization.Encoding.DER, serialization.load_der_parameters], 779 ], 780 ) 781 def test_parameter_bytes(self, backend, encoding, loader_func): 782 parameters = FFDH3072_P.parameters(backend) 783 serialized = parameters.parameter_bytes( 784 encoding, serialization.ParameterFormat.PKCS3 785 ) 786 loaded_key = loader_func(serialized, backend) 787 loaded_param_num = loaded_key.parameter_numbers() 788 assert loaded_param_num == parameters.parameter_numbers() 789 790 @pytest.mark.parametrize( 791 ("param_path", "loader_func", "encoding", "is_dhx"), 792 [ 793 ( 794 os.path.join("asymmetric", "DH", "dhp.pem"), 795 serialization.load_pem_parameters, 796 serialization.Encoding.PEM, 797 False, 798 ), 799 ( 800 os.path.join("asymmetric", "DH", "dhp.der"), 801 serialization.load_der_parameters, 802 serialization.Encoding.DER, 803 False, 804 ), 805 ( 806 os.path.join("asymmetric", "DH", "dhp_rfc5114_2.pem"), 807 serialization.load_pem_parameters, 808 serialization.Encoding.PEM, 809 True, 810 ), 811 ( 812 os.path.join("asymmetric", "DH", "dhp_rfc5114_2.der"), 813 serialization.load_der_parameters, 814 serialization.Encoding.DER, 815 True, 816 ), 817 ], 818 ) 819 def test_parameter_bytes_match( 820 self, param_path, loader_func, encoding, backend, is_dhx 821 ): 822 _skip_dhx_unsupported(backend, is_dhx) 823 param_bytes = load_vectors_from_file( 824 param_path, lambda pemfile: pemfile.read(), mode="rb" 825 ) 826 parameters = loader_func(param_bytes, backend) 827 serialized = parameters.parameter_bytes( 828 encoding, 829 serialization.ParameterFormat.PKCS3, 830 ) 831 assert serialized == param_bytes 832 833 @pytest.mark.parametrize( 834 ("param_path", "loader_func", "vec_path", "is_dhx"), 835 [ 836 ( 837 os.path.join("asymmetric", "DH", "dhp.pem"), 838 serialization.load_pem_parameters, 839 os.path.join("asymmetric", "DH", "dhkey.txt"), 840 False, 841 ), 842 ( 843 os.path.join("asymmetric", "DH", "dhp.der"), 844 serialization.load_der_parameters, 845 os.path.join("asymmetric", "DH", "dhkey.txt"), 846 False, 847 ), 848 ( 849 os.path.join("asymmetric", "DH", "dhp_rfc5114_2.pem"), 850 serialization.load_pem_parameters, 851 os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.txt"), 852 True, 853 ), 854 ( 855 os.path.join("asymmetric", "DH", "dhp_rfc5114_2.der"), 856 serialization.load_der_parameters, 857 os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.txt"), 858 True, 859 ), 860 ], 861 ) 862 def test_public_bytes_values( 863 self, param_path, loader_func, vec_path, backend, is_dhx 864 ): 865 _skip_dhx_unsupported(backend, is_dhx) 866 key_bytes = load_vectors_from_file( 867 param_path, lambda pemfile: pemfile.read(), mode="rb" 868 ) 869 vec = load_vectors_from_file(vec_path, load_nist_vectors)[0] 870 parameters = loader_func(key_bytes, backend) 871 parameter_numbers = parameters.parameter_numbers() 872 assert parameter_numbers.g == int(vec["g"], 16) 873 assert parameter_numbers.p == int(vec["p"], 16) 874 if "q" in vec: 875 assert parameter_numbers.q == int(vec["q"], 16) 876 else: 877 assert parameter_numbers.q is None 878 879 @pytest.mark.parametrize( 880 ("encoding", "fmt"), 881 [ 882 ( 883 serialization.Encoding.Raw, 884 serialization.PublicFormat.SubjectPublicKeyInfo, 885 ), 886 (serialization.Encoding.Raw, serialization.PublicFormat.PKCS1), 887 ] 888 + list( 889 itertools.product( 890 [ 891 serialization.Encoding.Raw, 892 serialization.Encoding.X962, 893 serialization.Encoding.PEM, 894 serialization.Encoding.DER, 895 ], 896 [ 897 serialization.PublicFormat.Raw, 898 serialization.PublicFormat.UncompressedPoint, 899 serialization.PublicFormat.CompressedPoint, 900 ], 901 ) 902 ), 903 ) 904 def test_public_bytes_rejects_invalid(self, encoding, fmt, backend): 905 parameters = FFDH3072_P.parameters(backend) 906 key = parameters.generate_private_key().public_key() 907 with pytest.raises(ValueError): 908 key.public_bytes(encoding, fmt) 909 910 def test_parameter_bytes_invalid_encoding(self, backend): 911 parameters = FFDH3072_P.parameters(backend) 912 with pytest.raises(TypeError): 913 parameters.parameter_bytes( 914 "notencoding", serialization.ParameterFormat.PKCS3 915 ) 916 917 def test_parameter_bytes_invalid_format(self, backend): 918 parameters = FFDH3072_P.parameters(backend) 919 with pytest.raises(ValueError): 920 parameters.parameter_bytes(serialization.Encoding.PEM, "notformat") 921 922 def test_parameter_bytes_openssh_unsupported(self, backend): 923 parameters = FFDH3072_P.parameters(backend) 924 with pytest.raises(TypeError): 925 parameters.parameter_bytes( 926 serialization.Encoding.OpenSSH, 927 serialization.ParameterFormat.PKCS3, 928 ) 929