Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pem/twisted.py: 9%

43 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-25 06:17 +0000

1# SPDX-FileCopyrightText: 2013 Hynek Schlawack <hs@ox.cx> 

2# 

3# SPDX-License-Identifier: MIT 

4 

5""" 

6Twisted-specific convenience helpers. 

7""" 

8 

9from __future__ import annotations 

10 

11from typing import TYPE_CHECKING 

12 

13from OpenSSL.crypto import FILETYPE_PEM 

14from twisted.internet import ssl 

15 

16from ._core import parse_file 

17from ._object_types import Certificate, DHParameters, Key 

18 

19 

20if TYPE_CHECKING: 

21 from ._object_types import AbstractPEMObject 

22 

23 

24def certificateOptionsFromPEMs( 

25 pemObjects: list[AbstractPEMObject], **kw: object 

26) -> ssl.CertificateOptions: 

27 """ 

28 Load a CertificateOptions from the given collection of PEM objects 

29 (already-loaded private keys and certificates). 

30 

31 In those PEM objects, identify one private key and its corresponding 

32 certificate to use as the primary certificate. Then use the rest of the 

33 certificates found as chain certificates. Raise a ValueError if no 

34 certificate matching a private key is found. 

35 

36 Args: 

37 pemObjects (list[AbstractPEMObject]): A list of PEM objects to load. 

38 

39 Returns: 

40 `twisted.internet.ssl.CertificateOptions`_: A TLS context factory using *pemObjects* 

41 

42 .. _`twisted.internet.ssl.CertificateOptions`: https://docs.twistedmatrix.com/en/stable/api/twisted.internet.ssl.CertificateOptions.html 

43 """ 

44 keys = [key for key in pemObjects if isinstance(key, Key)] 

45 if not len(keys): 

46 msg = "Supplied PEM file(s) does *not* contain a key." 

47 raise ValueError(msg) 

48 if len(keys) > 1: 

49 msg = "Supplied PEM file(s) contains *more* than one key." 

50 raise ValueError(msg) 

51 

52 privateKey = ssl.KeyPair.load(str(keys[0]), FILETYPE_PEM) # type: ignore[no-untyped-call] 

53 

54 certs = [cert for cert in pemObjects if isinstance(cert, Certificate)] 

55 if not len(certs): 

56 msg = "*At least one* certificate is required." 

57 raise ValueError(msg) 

58 certificates = [ 

59 ssl.Certificate.loadPEM(str(certPEM)) # type: ignore[no-untyped-call] 

60 for certPEM in certs 

61 ] 

62 

63 certificatesByFingerprint = { 

64 certificate.getPublicKey().keyHash(): certificate 

65 for certificate in certificates 

66 } 

67 

68 if privateKey.keyHash() not in certificatesByFingerprint: 

69 msg = f"No certificate matching {privateKey.keyHash()} found." 

70 raise ValueError(msg) 

71 

72 primaryCertificate = certificatesByFingerprint.pop(privateKey.keyHash()) 

73 

74 if "dhParameters" in kw: 

75 msg = "Passing DH parameters as a keyword argument instead of a PEM object is not supported anymore." 

76 raise TypeError(msg) 

77 

78 dhparams = [o for o in pemObjects if isinstance(o, DHParameters)] 

79 if len(dhparams) > 1: 

80 msg = "Supplied PEM file(s) contain(s) *more* than one set of DH parameters." 

81 raise ValueError(msg) 

82 

83 if len(dhparams) == 1: 

84 kw["dhParameters"] = ssl.DiffieHellmanParameters( # type: ignore[no-untyped-call] 

85 str(dhparams[0]) 

86 ) 

87 

88 return ssl.CertificateOptions( # type: ignore[no-any-return] 

89 privateKey=privateKey.original, 

90 certificate=primaryCertificate.original, 

91 extraCertChain=[ 

92 chain.original for chain in certificatesByFingerprint.values() 

93 ], 

94 **kw, 

95 ) 

96 

97 

98def certificateOptionsFromFiles( 

99 *pemFiles: str, **kw: object 

100) -> ssl.CertificateOptions: 

101 """ 

102 Read all files named by *pemFiles*, and parse them using 

103 :func:`certificateOptionsFromPEMs`. 

104 

105 Args: 

106 pemFiles (str): All positional arguments are used as filenames to 

107 read. 

108 

109 Returns: 

110 `twisted.internet.ssl.CertificateOptions`_: A TLS context factory using 

111 PEM objects from *pemFiles*. 

112 """ 

113 pems: list[AbstractPEMObject] = [] 

114 for pemFile in pemFiles: 

115 pems += parse_file(pemFile) 

116 

117 return certificateOptionsFromPEMs(pems, **kw)