Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hhhrrrttt222111
GitHub Repository: hhhrrrttt222111/Dorkify
Path: blob/master/venv/Lib/site-packages/urllib3/util/ssl_.py
811 views
1
from __future__ import absolute_import
2
import errno
3
import warnings
4
import hmac
5
import sys
6
7
from binascii import hexlify, unhexlify
8
from hashlib import md5, sha1, sha256
9
10
from .url import IPV4_RE, BRACELESS_IPV6_ADDRZ_RE
11
from ..exceptions import SSLError, InsecurePlatformWarning, SNIMissingWarning
12
from ..packages import six
13
14
15
SSLContext = None
16
HAS_SNI = False
17
IS_PYOPENSSL = False
18
IS_SECURETRANSPORT = False
19
20
# Maps the length of a digest to a possible hash function producing this digest
21
HASHFUNC_MAP = {32: md5, 40: sha1, 64: sha256}
22
23
24
def _const_compare_digest_backport(a, b):
25
"""
26
Compare two digests of equal length in constant time.
27
28
The digests must be of type str/bytes.
29
Returns True if the digests match, and False otherwise.
30
"""
31
result = abs(len(a) - len(b))
32
for l, r in zip(bytearray(a), bytearray(b)):
33
result |= l ^ r
34
return result == 0
35
36
37
_const_compare_digest = getattr(hmac, "compare_digest", _const_compare_digest_backport)
38
39
try: # Test for SSL features
40
import ssl
41
from ssl import wrap_socket, CERT_REQUIRED
42
from ssl import HAS_SNI # Has SNI?
43
except ImportError:
44
pass
45
46
try: # Platform-specific: Python 3.6
47
from ssl import PROTOCOL_TLS
48
49
PROTOCOL_SSLv23 = PROTOCOL_TLS
50
except ImportError:
51
try:
52
from ssl import PROTOCOL_SSLv23 as PROTOCOL_TLS
53
54
PROTOCOL_SSLv23 = PROTOCOL_TLS
55
except ImportError:
56
PROTOCOL_SSLv23 = PROTOCOL_TLS = 2
57
58
59
try:
60
from ssl import OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_COMPRESSION
61
except ImportError:
62
OP_NO_SSLv2, OP_NO_SSLv3 = 0x1000000, 0x2000000
63
OP_NO_COMPRESSION = 0x20000
64
65
66
# A secure default.
67
# Sources for more information on TLS ciphers:
68
#
69
# - https://wiki.mozilla.org/Security/Server_Side_TLS
70
# - https://www.ssllabs.com/projects/best-practices/index.html
71
# - https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
72
#
73
# The general intent is:
74
# - prefer cipher suites that offer perfect forward secrecy (DHE/ECDHE),
75
# - prefer ECDHE over DHE for better performance,
76
# - prefer any AES-GCM and ChaCha20 over any AES-CBC for better performance and
77
# security,
78
# - prefer AES-GCM over ChaCha20 because hardware-accelerated AES is common,
79
# - disable NULL authentication, MD5 MACs, DSS, and other
80
# insecure ciphers for security reasons.
81
# - NOTE: TLS 1.3 cipher suites are managed through a different interface
82
# not exposed by CPython (yet!) and are enabled by default if they're available.
83
DEFAULT_CIPHERS = ":".join(
84
[
85
"ECDHE+AESGCM",
86
"ECDHE+CHACHA20",
87
"DHE+AESGCM",
88
"DHE+CHACHA20",
89
"ECDH+AESGCM",
90
"DH+AESGCM",
91
"ECDH+AES",
92
"DH+AES",
93
"RSA+AESGCM",
94
"RSA+AES",
95
"!aNULL",
96
"!eNULL",
97
"!MD5",
98
"!DSS",
99
]
100
)
101
102
try:
103
from ssl import SSLContext # Modern SSL?
104
except ImportError:
105
106
class SSLContext(object): # Platform-specific: Python 2
107
def __init__(self, protocol_version):
108
self.protocol = protocol_version
109
# Use default values from a real SSLContext
110
self.check_hostname = False
111
self.verify_mode = ssl.CERT_NONE
112
self.ca_certs = None
113
self.options = 0
114
self.certfile = None
115
self.keyfile = None
116
self.ciphers = None
117
118
def load_cert_chain(self, certfile, keyfile):
119
self.certfile = certfile
120
self.keyfile = keyfile
121
122
def load_verify_locations(self, cafile=None, capath=None, cadata=None):
123
self.ca_certs = cafile
124
125
if capath is not None:
126
raise SSLError("CA directories not supported in older Pythons")
127
128
if cadata is not None:
129
raise SSLError("CA data not supported in older Pythons")
130
131
def set_ciphers(self, cipher_suite):
132
self.ciphers = cipher_suite
133
134
def wrap_socket(self, socket, server_hostname=None, server_side=False):
135
warnings.warn(
136
"A true SSLContext object is not available. This prevents "
137
"urllib3 from configuring SSL appropriately and may cause "
138
"certain SSL connections to fail. You can upgrade to a newer "
139
"version of Python to solve this. For more information, see "
140
"https://urllib3.readthedocs.io/en/latest/advanced-usage.html"
141
"#ssl-warnings",
142
InsecurePlatformWarning,
143
)
144
kwargs = {
145
"keyfile": self.keyfile,
146
"certfile": self.certfile,
147
"ca_certs": self.ca_certs,
148
"cert_reqs": self.verify_mode,
149
"ssl_version": self.protocol,
150
"server_side": server_side,
151
}
152
return wrap_socket(socket, ciphers=self.ciphers, **kwargs)
153
154
155
def assert_fingerprint(cert, fingerprint):
156
"""
157
Checks if given fingerprint matches the supplied certificate.
158
159
:param cert:
160
Certificate as bytes object.
161
:param fingerprint:
162
Fingerprint as string of hexdigits, can be interspersed by colons.
163
"""
164
165
fingerprint = fingerprint.replace(":", "").lower()
166
digest_length = len(fingerprint)
167
hashfunc = HASHFUNC_MAP.get(digest_length)
168
if not hashfunc:
169
raise SSLError("Fingerprint of invalid length: {0}".format(fingerprint))
170
171
# We need encode() here for py32; works on py2 and p33.
172
fingerprint_bytes = unhexlify(fingerprint.encode())
173
174
cert_digest = hashfunc(cert).digest()
175
176
if not _const_compare_digest(cert_digest, fingerprint_bytes):
177
raise SSLError(
178
'Fingerprints did not match. Expected "{0}", got "{1}".'.format(
179
fingerprint, hexlify(cert_digest)
180
)
181
)
182
183
184
def resolve_cert_reqs(candidate):
185
"""
186
Resolves the argument to a numeric constant, which can be passed to
187
the wrap_socket function/method from the ssl module.
188
Defaults to :data:`ssl.CERT_REQUIRED`.
189
If given a string it is assumed to be the name of the constant in the
190
:mod:`ssl` module or its abbreviation.
191
(So you can specify `REQUIRED` instead of `CERT_REQUIRED`.
192
If it's neither `None` nor a string we assume it is already the numeric
193
constant which can directly be passed to wrap_socket.
194
"""
195
if candidate is None:
196
return CERT_REQUIRED
197
198
if isinstance(candidate, str):
199
res = getattr(ssl, candidate, None)
200
if res is None:
201
res = getattr(ssl, "CERT_" + candidate)
202
return res
203
204
return candidate
205
206
207
def resolve_ssl_version(candidate):
208
"""
209
like resolve_cert_reqs
210
"""
211
if candidate is None:
212
return PROTOCOL_TLS
213
214
if isinstance(candidate, str):
215
res = getattr(ssl, candidate, None)
216
if res is None:
217
res = getattr(ssl, "PROTOCOL_" + candidate)
218
return res
219
220
return candidate
221
222
223
def create_urllib3_context(
224
ssl_version=None, cert_reqs=None, options=None, ciphers=None
225
):
226
"""All arguments have the same meaning as ``ssl_wrap_socket``.
227
228
By default, this function does a lot of the same work that
229
``ssl.create_default_context`` does on Python 3.4+. It:
230
231
- Disables SSLv2, SSLv3, and compression
232
- Sets a restricted set of server ciphers
233
234
If you wish to enable SSLv3, you can do::
235
236
from urllib3.util import ssl_
237
context = ssl_.create_urllib3_context()
238
context.options &= ~ssl_.OP_NO_SSLv3
239
240
You can do the same to enable compression (substituting ``COMPRESSION``
241
for ``SSLv3`` in the last line above).
242
243
:param ssl_version:
244
The desired protocol version to use. This will default to
245
PROTOCOL_SSLv23 which will negotiate the highest protocol that both
246
the server and your installation of OpenSSL support.
247
:param cert_reqs:
248
Whether to require the certificate verification. This defaults to
249
``ssl.CERT_REQUIRED``.
250
:param options:
251
Specific OpenSSL options. These default to ``ssl.OP_NO_SSLv2``,
252
``ssl.OP_NO_SSLv3``, ``ssl.OP_NO_COMPRESSION``.
253
:param ciphers:
254
Which cipher suites to allow the server to select.
255
:returns:
256
Constructed SSLContext object with specified options
257
:rtype: SSLContext
258
"""
259
context = SSLContext(ssl_version or PROTOCOL_TLS)
260
261
context.set_ciphers(ciphers or DEFAULT_CIPHERS)
262
263
# Setting the default here, as we may have no ssl module on import
264
cert_reqs = ssl.CERT_REQUIRED if cert_reqs is None else cert_reqs
265
266
if options is None:
267
options = 0
268
# SSLv2 is easily broken and is considered harmful and dangerous
269
options |= OP_NO_SSLv2
270
# SSLv3 has several problems and is now dangerous
271
options |= OP_NO_SSLv3
272
# Disable compression to prevent CRIME attacks for OpenSSL 1.0+
273
# (issue #309)
274
options |= OP_NO_COMPRESSION
275
276
context.options |= options
277
278
# Enable post-handshake authentication for TLS 1.3, see GH #1634. PHA is
279
# necessary for conditional client cert authentication with TLS 1.3.
280
# The attribute is None for OpenSSL <= 1.1.0 or does not exist in older
281
# versions of Python. We only enable on Python 3.7.4+ or if certificate
282
# verification is enabled to work around Python issue #37428
283
# See: https://bugs.python.org/issue37428
284
if (cert_reqs == ssl.CERT_REQUIRED or sys.version_info >= (3, 7, 4)) and getattr(
285
context, "post_handshake_auth", None
286
) is not None:
287
context.post_handshake_auth = True
288
289
context.verify_mode = cert_reqs
290
if (
291
getattr(context, "check_hostname", None) is not None
292
): # Platform-specific: Python 3.2
293
# We do our own verification, including fingerprints and alternative
294
# hostnames. So disable it here
295
context.check_hostname = False
296
return context
297
298
299
def ssl_wrap_socket(
300
sock,
301
keyfile=None,
302
certfile=None,
303
cert_reqs=None,
304
ca_certs=None,
305
server_hostname=None,
306
ssl_version=None,
307
ciphers=None,
308
ssl_context=None,
309
ca_cert_dir=None,
310
key_password=None,
311
ca_cert_data=None,
312
):
313
"""
314
All arguments except for server_hostname, ssl_context, and ca_cert_dir have
315
the same meaning as they do when using :func:`ssl.wrap_socket`.
316
317
:param server_hostname:
318
When SNI is supported, the expected hostname of the certificate
319
:param ssl_context:
320
A pre-made :class:`SSLContext` object. If none is provided, one will
321
be created using :func:`create_urllib3_context`.
322
:param ciphers:
323
A string of ciphers we wish the client to support.
324
:param ca_cert_dir:
325
A directory containing CA certificates in multiple separate files, as
326
supported by OpenSSL's -CApath flag or the capath argument to
327
SSLContext.load_verify_locations().
328
:param key_password:
329
Optional password if the keyfile is encrypted.
330
:param ca_cert_data:
331
Optional string containing CA certificates in PEM format suitable for
332
passing as the cadata parameter to SSLContext.load_verify_locations()
333
"""
334
context = ssl_context
335
if context is None:
336
# Note: This branch of code and all the variables in it are no longer
337
# used by urllib3 itself. We should consider deprecating and removing
338
# this code.
339
context = create_urllib3_context(ssl_version, cert_reqs, ciphers=ciphers)
340
341
if ca_certs or ca_cert_dir or ca_cert_data:
342
try:
343
context.load_verify_locations(ca_certs, ca_cert_dir, ca_cert_data)
344
except IOError as e: # Platform-specific: Python 2.7
345
raise SSLError(e)
346
# Py33 raises FileNotFoundError which subclasses OSError
347
# These are not equivalent unless we check the errno attribute
348
except OSError as e: # Platform-specific: Python 3.3 and beyond
349
if e.errno == errno.ENOENT:
350
raise SSLError(e)
351
raise
352
353
elif ssl_context is None and hasattr(context, "load_default_certs"):
354
# try to load OS default certs; works well on Windows (require Python3.4+)
355
context.load_default_certs()
356
357
# Attempt to detect if we get the goofy behavior of the
358
# keyfile being encrypted and OpenSSL asking for the
359
# passphrase via the terminal and instead error out.
360
if keyfile and key_password is None and _is_key_file_encrypted(keyfile):
361
raise SSLError("Client private key is encrypted, password is required")
362
363
if certfile:
364
if key_password is None:
365
context.load_cert_chain(certfile, keyfile)
366
else:
367
context.load_cert_chain(certfile, keyfile, key_password)
368
369
# If we detect server_hostname is an IP address then the SNI
370
# extension should not be used according to RFC3546 Section 3.1
371
# We shouldn't warn the user if SNI isn't available but we would
372
# not be using SNI anyways due to IP address for server_hostname.
373
if (
374
server_hostname is not None and not is_ipaddress(server_hostname)
375
) or IS_SECURETRANSPORT:
376
if HAS_SNI and server_hostname is not None:
377
return context.wrap_socket(sock, server_hostname=server_hostname)
378
379
warnings.warn(
380
"An HTTPS request has been made, but the SNI (Server Name "
381
"Indication) extension to TLS is not available on this platform. "
382
"This may cause the server to present an incorrect TLS "
383
"certificate, which can cause validation failures. You can upgrade to "
384
"a newer version of Python to solve this. For more information, see "
385
"https://urllib3.readthedocs.io/en/latest/advanced-usage.html"
386
"#ssl-warnings",
387
SNIMissingWarning,
388
)
389
390
return context.wrap_socket(sock)
391
392
393
def is_ipaddress(hostname):
394
"""Detects whether the hostname given is an IPv4 or IPv6 address.
395
Also detects IPv6 addresses with Zone IDs.
396
397
:param str hostname: Hostname to examine.
398
:return: True if the hostname is an IP address, False otherwise.
399
"""
400
if not six.PY2 and isinstance(hostname, bytes):
401
# IDN A-label bytes are ASCII compatible.
402
hostname = hostname.decode("ascii")
403
return bool(IPV4_RE.match(hostname) or BRACELESS_IPV6_ADDRZ_RE.match(hostname))
404
405
406
def _is_key_file_encrypted(key_file):
407
"""Detects if a key file is encrypted or not."""
408
with open(key_file, "r") as f:
409
for line in f:
410
# Look for Proc-Type: 4,ENCRYPTED
411
if "ENCRYPTED" in line:
412
return True
413
414
return False
415
416