Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(867)

Side by Side Diff: third_party/google-endpoints/urllib3/util/ssl_.py

Issue 2666783008: Add google-endpoints to third_party/. (Closed)
Patch Set: Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 from __future__ import absolute_import
2 import errno
3 import warnings
4 import hmac
5
6 from binascii import hexlify, unhexlify
7 from hashlib import md5, sha1, sha256
8
9 from ..exceptions import SSLError, InsecurePlatformWarning, SNIMissingWarning
10
11
12 SSLContext = None
13 HAS_SNI = False
14 IS_PYOPENSSL = False
15
16 # Maps the length of a digest to a possible hash function producing this digest
17 HASHFUNC_MAP = {
18 32: md5,
19 40: sha1,
20 64: sha256,
21 }
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',
38 _const_compare_digest_backport)
39
40
41 try: # Test for SSL features
42 import ssl
43 from ssl import wrap_socket, CERT_NONE, PROTOCOL_SSLv23
44 from ssl import HAS_SNI # Has SNI?
45 except ImportError:
46 pass
47
48
49 try:
50 from ssl import OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_COMPRESSION
51 except ImportError:
52 OP_NO_SSLv2, OP_NO_SSLv3 = 0x1000000, 0x2000000
53 OP_NO_COMPRESSION = 0x20000
54
55 # A secure default.
56 # Sources for more information on TLS ciphers:
57 #
58 # - https://wiki.mozilla.org/Security/Server_Side_TLS
59 # - https://www.ssllabs.com/projects/best-practices/index.html
60 # - https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
61 #
62 # The general intent is:
63 # - Prefer cipher suites that offer perfect forward secrecy (DHE/ECDHE),
64 # - prefer ECDHE over DHE for better performance,
65 # - prefer any AES-GCM and ChaCha20 over any AES-CBC for better performance and
66 # security,
67 # - prefer AES-GCM over ChaCha20 because hardware-accelerated AES is common,
68 # - disable NULL authentication, MD5 MACs and DSS for security reasons.
69 DEFAULT_CIPHERS = ':'.join([
70 'ECDH+AESGCM',
71 'ECDH+CHACHA20',
72 'DH+AESGCM',
73 'DH+CHACHA20',
74 'ECDH+AES256',
75 'DH+AES256',
76 'ECDH+AES128',
77 'DH+AES',
78 'RSA+AESGCM',
79 'RSA+AES',
80 '!aNULL',
81 '!eNULL',
82 '!MD5',
83 ])
84
85 try:
86 from ssl import SSLContext # Modern SSL?
87 except ImportError:
88 import sys
89
90 class SSLContext(object): # Platform-specific: Python 2 & 3.1
91 supports_set_ciphers = ((2, 7) <= sys.version_info < (3,) or
92 (3, 2) <= sys.version_info)
93
94 def __init__(self, protocol_version):
95 self.protocol = protocol_version
96 # Use default values from a real SSLContext
97 self.check_hostname = False
98 self.verify_mode = ssl.CERT_NONE
99 self.ca_certs = None
100 self.options = 0
101 self.certfile = None
102 self.keyfile = None
103 self.ciphers = None
104
105 def load_cert_chain(self, certfile, keyfile):
106 self.certfile = certfile
107 self.keyfile = keyfile
108
109 def load_verify_locations(self, cafile=None, capath=None):
110 self.ca_certs = cafile
111
112 if capath is not None:
113 raise SSLError("CA directories not supported in older Pythons")
114
115 def set_ciphers(self, cipher_suite):
116 if not self.supports_set_ciphers:
117 raise TypeError(
118 'Your version of Python does not support setting '
119 'a custom cipher suite. Please upgrade to Python '
120 '2.7, 3.2, or later if you need this functionality.'
121 )
122 self.ciphers = cipher_suite
123
124 def wrap_socket(self, socket, server_hostname=None, server_side=False):
125 warnings.warn(
126 'A true SSLContext object is not available. This prevents '
127 'urllib3 from configuring SSL appropriately and may cause '
128 'certain SSL connections to fail. You can upgrade to a newer '
129 'version of Python to solve this. For more information, see '
130 'https://urllib3.readthedocs.io/en/latest/advanced-usage.html'
131 '#ssl-warnings',
132 InsecurePlatformWarning
133 )
134 kwargs = {
135 'keyfile': self.keyfile,
136 'certfile': self.certfile,
137 'ca_certs': self.ca_certs,
138 'cert_reqs': self.verify_mode,
139 'ssl_version': self.protocol,
140 'server_side': server_side,
141 }
142 if self.supports_set_ciphers: # Platform-specific: Python 2.7+
143 return wrap_socket(socket, ciphers=self.ciphers, **kwargs)
144 else: # Platform-specific: Python 2.6
145 return wrap_socket(socket, **kwargs)
146
147
148 def assert_fingerprint(cert, fingerprint):
149 """
150 Checks if given fingerprint matches the supplied certificate.
151
152 :param cert:
153 Certificate as bytes object.
154 :param fingerprint:
155 Fingerprint as string of hexdigits, can be interspersed by colons.
156 """
157
158 fingerprint = fingerprint.replace(':', '').lower()
159 digest_length = len(fingerprint)
160 hashfunc = HASHFUNC_MAP.get(digest_length)
161 if not hashfunc:
162 raise SSLError(
163 'Fingerprint of invalid length: {0}'.format(fingerprint))
164
165 # We need encode() here for py32; works on py2 and p33.
166 fingerprint_bytes = unhexlify(fingerprint.encode())
167
168 cert_digest = hashfunc(cert).digest()
169
170 if not _const_compare_digest(cert_digest, fingerprint_bytes):
171 raise SSLError('Fingerprints did not match. Expected "{0}", got "{1}".'
172 .format(fingerprint, hexlify(cert_digest)))
173
174
175 def resolve_cert_reqs(candidate):
176 """
177 Resolves the argument to a numeric constant, which can be passed to
178 the wrap_socket function/method from the ssl module.
179 Defaults to :data:`ssl.CERT_NONE`.
180 If given a string it is assumed to be the name of the constant in the
181 :mod:`ssl` module or its abbrevation.
182 (So you can specify `REQUIRED` instead of `CERT_REQUIRED`.
183 If it's neither `None` nor a string we assume it is already the numeric
184 constant which can directly be passed to wrap_socket.
185 """
186 if candidate is None:
187 return CERT_NONE
188
189 if isinstance(candidate, str):
190 res = getattr(ssl, candidate, None)
191 if res is None:
192 res = getattr(ssl, 'CERT_' + candidate)
193 return res
194
195 return candidate
196
197
198 def resolve_ssl_version(candidate):
199 """
200 like resolve_cert_reqs
201 """
202 if candidate is None:
203 return PROTOCOL_SSLv23
204
205 if isinstance(candidate, str):
206 res = getattr(ssl, candidate, None)
207 if res is None:
208 res = getattr(ssl, 'PROTOCOL_' + candidate)
209 return res
210
211 return candidate
212
213
214 def create_urllib3_context(ssl_version=None, cert_reqs=None,
215 options=None, ciphers=None):
216 """All arguments have the same meaning as ``ssl_wrap_socket``.
217
218 By default, this function does a lot of the same work that
219 ``ssl.create_default_context`` does on Python 3.4+. It:
220
221 - Disables SSLv2, SSLv3, and compression
222 - Sets a restricted set of server ciphers
223
224 If you wish to enable SSLv3, you can do::
225
226 from urllib3.util import ssl_
227 context = ssl_.create_urllib3_context()
228 context.options &= ~ssl_.OP_NO_SSLv3
229
230 You can do the same to enable compression (substituting ``COMPRESSION``
231 for ``SSLv3`` in the last line above).
232
233 :param ssl_version:
234 The desired protocol version to use. This will default to
235 PROTOCOL_SSLv23 which will negotiate the highest protocol that both
236 the server and your installation of OpenSSL support.
237 :param cert_reqs:
238 Whether to require the certificate verification. This defaults to
239 ``ssl.CERT_REQUIRED``.
240 :param options:
241 Specific OpenSSL options. These default to ``ssl.OP_NO_SSLv2``,
242 ``ssl.OP_NO_SSLv3``, ``ssl.OP_NO_COMPRESSION``.
243 :param ciphers:
244 Which cipher suites to allow the server to select.
245 :returns:
246 Constructed SSLContext object with specified options
247 :rtype: SSLContext
248 """
249 context = SSLContext(ssl_version or ssl.PROTOCOL_SSLv23)
250
251 # Setting the default here, as we may have no ssl module on import
252 cert_reqs = ssl.CERT_REQUIRED if cert_reqs is None else cert_reqs
253
254 if options is None:
255 options = 0
256 # SSLv2 is easily broken and is considered harmful and dangerous
257 options |= OP_NO_SSLv2
258 # SSLv3 has several problems and is now dangerous
259 options |= OP_NO_SSLv3
260 # Disable compression to prevent CRIME attacks for OpenSSL 1.0+
261 # (issue #309)
262 options |= OP_NO_COMPRESSION
263
264 context.options |= options
265
266 if getattr(context, 'supports_set_ciphers', True): # Platform-specific: Pyt hon 2.6
267 context.set_ciphers(ciphers or DEFAULT_CIPHERS)
268
269 context.verify_mode = cert_reqs
270 if getattr(context, 'check_hostname', None) is not None: # Platform-specifi c: Python 3.2
271 # We do our own verification, including fingerprints and alternative
272 # hostnames. So disable it here
273 context.check_hostname = False
274 return context
275
276
277 def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None,
278 ca_certs=None, server_hostname=None,
279 ssl_version=None, ciphers=None, ssl_context=None,
280 ca_cert_dir=None):
281 """
282 All arguments except for server_hostname, ssl_context, and ca_cert_dir have
283 the same meaning as they do when using :func:`ssl.wrap_socket`.
284
285 :param server_hostname:
286 When SNI is supported, the expected hostname of the certificate
287 :param ssl_context:
288 A pre-made :class:`SSLContext` object. If none is provided, one will
289 be created using :func:`create_urllib3_context`.
290 :param ciphers:
291 A string of ciphers we wish the client to support. This is not
292 supported on Python 2.6 as the ssl module does not support it.
293 :param ca_cert_dir:
294 A directory containing CA certificates in multiple separate files, as
295 supported by OpenSSL's -CApath flag or the capath argument to
296 SSLContext.load_verify_locations().
297 """
298 context = ssl_context
299 if context is None:
300 # Note: This branch of code and all the variables in it are no longer
301 # used by urllib3 itself. We should consider deprecating and removing
302 # this code.
303 context = create_urllib3_context(ssl_version, cert_reqs,
304 ciphers=ciphers)
305
306 if ca_certs or ca_cert_dir:
307 try:
308 context.load_verify_locations(ca_certs, ca_cert_dir)
309 except IOError as e: # Platform-specific: Python 2.6, 2.7, 3.2
310 raise SSLError(e)
311 # Py33 raises FileNotFoundError which subclasses OSError
312 # These are not equivalent unless we check the errno attribute
313 except OSError as e: # Platform-specific: Python 3.3 and beyond
314 if e.errno == errno.ENOENT:
315 raise SSLError(e)
316 raise
317 elif getattr(context, 'load_default_certs', None) is not None:
318 # try to load OS default certs; works well on Windows (require Python3.4 +)
319 context.load_default_certs()
320
321 if certfile:
322 context.load_cert_chain(certfile, keyfile)
323 if HAS_SNI: # Platform-specific: OpenSSL with enabled SNI
324 return context.wrap_socket(sock, server_hostname=server_hostname)
325
326 warnings.warn(
327 'An HTTPS request has been made, but the SNI (Subject Name '
328 'Indication) extension to TLS is not available on this platform. '
329 'This may cause the server to present an incorrect TLS '
330 'certificate, which can cause validation failures. You can upgrade to '
331 'a newer version of Python to solve this. For more information, see '
332 'https://urllib3.readthedocs.io/en/latest/advanced-usage.html'
333 '#ssl-warnings',
334 SNIMissingWarning
335 )
336 return context.wrap_socket(sock)
OLDNEW
« no previous file with comments | « third_party/google-endpoints/urllib3/util/selectors.py ('k') | third_party/google-endpoints/urllib3/util/timeout.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698