From e82326c9fd9571858146bdb986ea98be5b47ba77 Mon Sep 17 00:00:00 2001 From: Abraham Martin Date: Wed, 4 Feb 2015 10:18:10 +0000 Subject: [PATCH 01/19] Fix regressions in support for bytes / unicode Fix regressions in support for bytes / unicode in certain APIs #15. Added a check for _text_type, raise a DeprecationWarning and encode the str to utf-8. This should preserve 0.13 API calls. I used the 0.13 test suite with the 0.14 code and made the necessary changes to make 0.13 and 0.14 tests pass. --- OpenSSL/SSL.py | 24 ++++++++++++++++++++++++ OpenSSL/crypto.py | 24 ++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/OpenSSL/SSL.py b/OpenSSL/SSL.py index 2731d64a8..ff0259a70 100644 --- a/OpenSSL/SSL.py +++ b/OpenSSL/SSL.py @@ -310,6 +310,12 @@ def load_verify_locations(self, cafile, capath=None): :param capath: In which directory we can find the certificates :return: None """ + + # Backward compatibility + if isinstance(cafile, _text_type): + DeprecationWarning("str object in cafile is no longer accepted, use bytes") + cafile = cafile.encode('utf-8') + if cafile is None: cafile = _ffi.NULL elif not isinstance(cafile, bytes): @@ -970,6 +976,12 @@ def send(self, buf, flags=0): API, the value is ignored :return: The number of bytes written """ + + # Backward compatibility + if isinstance(buf, _text_type): + DeprecationWarning("str object in buf is no longer accepted, use bytes") + buf = buf.encode('utf-8') + if isinstance(buf, _memoryview): buf = buf.tobytes() if isinstance(buf, _buffer): @@ -994,6 +1006,12 @@ def sendall(self, buf, flags=0): API, the value is ignored :return: The number of bytes written """ + + # Backward compatibility + if isinstance(buf, _text_type): + DeprecationWarning("str object in buf is no longer accepted, use bytes") + buf = buf.encode('utf-8') + if isinstance(buf, _memoryview): buf = buf.tobytes() if isinstance(buf, _buffer): @@ -1079,6 +1097,12 @@ def bio_write(self, buf): :param buf: The string to put into the memory BIO. :return: The number of bytes written """ + + # Backward compatibility + if isinstance(buf, _text_type): + DeprecationWarning("str object in buf is no longer accepted, use bytes") + buf = buf.encode("ascii") + if self._into_ssl is None: raise TypeError("Connection sock was not None") diff --git a/OpenSSL/crypto.py b/OpenSSL/crypto.py index 8d971f211..0aa140a86 100644 --- a/OpenSSL/crypto.py +++ b/OpenSSL/crypto.py @@ -1952,6 +1952,12 @@ def export(self, passphrase=None, iter=2048, maciter=1): :return: The string containing the PKCS12 """ + + # Backward compatibility + if isinstance(passphrase, _text_type): + DeprecationWarning("str object in passphrase is no longer accepted, use bytes") + passphrase = passphrase.encode('utf-8') + if self._cacerts is None: cacerts = _ffi.NULL else: @@ -2249,6 +2255,12 @@ def sign(pkey, data, digest): :param digest: message digest to use :return: signature """ + + # Backward compatibility + if isinstance(data, _text_type): + DeprecationWarning("str object in passphrase is no longer accepted, use bytes") + data = data.encode('utf-8') + digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest)) if digest_obj == _ffi.NULL: raise ValueError("No such digest method") @@ -2283,6 +2295,12 @@ def verify(cert, signature, data, digest): :param digest: message digest to use :return: None if the signature is correct, raise exception otherwise """ + + # Backward compatibility + if isinstance(data, _text_type): + DeprecationWarning("str object in passphrase is no longer accepted, use bytes") + data = data.encode('utf-8') + digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest)) if digest_obj == _ffi.NULL: raise ValueError("No such digest method") @@ -2374,6 +2392,12 @@ def load_pkcs12(buffer, passphrase=None): :param passphrase: (Optional) The password to decrypt the PKCS12 lump :returns: The PKCS12 object """ + + # Backward compatibility + if isinstance(passphrase, _text_type): + DeprecationWarning("str object in passphrase is no longer accepted, use bytes") + passphrase = passphrase.encode('utf-8') + if isinstance(buffer, _text_type): buffer = buffer.encode("ascii") From 2403ef5498f98cffcde210a382ae3c44d9be6332 Mon Sep 17 00:00:00 2001 From: Abraham Martin Date: Wed, 25 Mar 2015 10:35:44 +0000 Subject: [PATCH 02/19] Emit proper warnings --- OpenSSL/SSL.py | 9 +++++---- OpenSSL/crypto.py | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/OpenSSL/SSL.py b/OpenSSL/SSL.py index ff0259a70..5d9c3604c 100644 --- a/OpenSSL/SSL.py +++ b/OpenSSL/SSL.py @@ -1,3 +1,4 @@ +import warnings from sys import platform from functools import wraps, partial from itertools import count @@ -313,7 +314,7 @@ def load_verify_locations(self, cafile, capath=None): # Backward compatibility if isinstance(cafile, _text_type): - DeprecationWarning("str object in cafile is no longer accepted, use bytes") + warnings.warn("str object in cafile is no longer accepted, use bytes", DeprecationWarning) cafile = cafile.encode('utf-8') if cafile is None: @@ -979,7 +980,7 @@ def send(self, buf, flags=0): # Backward compatibility if isinstance(buf, _text_type): - DeprecationWarning("str object in buf is no longer accepted, use bytes") + warnings.warn("str object in buf is no longer accepted, use bytes", DeprecationWarning) buf = buf.encode('utf-8') if isinstance(buf, _memoryview): @@ -1009,7 +1010,7 @@ def sendall(self, buf, flags=0): # Backward compatibility if isinstance(buf, _text_type): - DeprecationWarning("str object in buf is no longer accepted, use bytes") + warnings.warn("str object in buf is no longer accepted, use bytes", DeprecationWarning) buf = buf.encode('utf-8') if isinstance(buf, _memoryview): @@ -1100,7 +1101,7 @@ def bio_write(self, buf): # Backward compatibility if isinstance(buf, _text_type): - DeprecationWarning("str object in buf is no longer accepted, use bytes") + warnings.warn("str object in buf is no longer accepted, use bytes", DeprecationWarning) buf = buf.encode("ascii") if self._into_ssl is None: diff --git a/OpenSSL/crypto.py b/OpenSSL/crypto.py index 0aa140a86..e8b6d3cfa 100644 --- a/OpenSSL/crypto.py +++ b/OpenSSL/crypto.py @@ -1,3 +1,4 @@ +import warnings from time import time from base64 import b16encode from functools import partial @@ -1955,7 +1956,7 @@ def export(self, passphrase=None, iter=2048, maciter=1): # Backward compatibility if isinstance(passphrase, _text_type): - DeprecationWarning("str object in passphrase is no longer accepted, use bytes") + warnings.warn("str object in passphrase is no longer accepted, use bytes", DeprecationWarning) passphrase = passphrase.encode('utf-8') if self._cacerts is None: @@ -2258,7 +2259,7 @@ def sign(pkey, data, digest): # Backward compatibility if isinstance(data, _text_type): - DeprecationWarning("str object in passphrase is no longer accepted, use bytes") + warnings.warn("str object in passphrase is no longer accepted, use bytes", DeprecationWarning) data = data.encode('utf-8') digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest)) @@ -2298,7 +2299,7 @@ def verify(cert, signature, data, digest): # Backward compatibility if isinstance(data, _text_type): - DeprecationWarning("str object in passphrase is no longer accepted, use bytes") + warnings.warn("str object in passphrase is no longer accepted, use bytes", DeprecationWarning) data = data.encode('utf-8') digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest)) @@ -2395,7 +2396,7 @@ def load_pkcs12(buffer, passphrase=None): # Backward compatibility if isinstance(passphrase, _text_type): - DeprecationWarning("str object in passphrase is no longer accepted, use bytes") + warnings.warn("str object in passphrase is no longer accepted, use bytes", DeprecationWarning) passphrase = passphrase.encode('utf-8') if isinstance(buffer, _text_type): From 82efe3e6db46c0924b5b2d6e0793a62bd48e7dcc Mon Sep 17 00:00:00 2001 From: Abraham Martin Date: Wed, 25 Mar 2015 10:50:09 +0000 Subject: [PATCH 03/19] Different warning messages for different versions of Python --- OpenSSL/SSL.py | 32 ++++++++++++++++++++++++++------ OpenSSL/crypto.py | 31 ++++++++++++++++++++++++++----- 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/OpenSSL/SSL.py b/OpenSSL/SSL.py index 5d9c3604c..89720978d 100644 --- a/OpenSSL/SSL.py +++ b/OpenSSL/SSL.py @@ -1,5 +1,5 @@ -import warnings -from sys import platform +from warnings import warn +from sys import platform, version_info from functools import wraps, partial from itertools import count from weakref import WeakValueDictionary @@ -314,7 +314,12 @@ def load_verify_locations(self, cafile, capath=None): # Backward compatibility if isinstance(cafile, _text_type): - warnings.warn("str object in cafile is no longer accepted, use bytes", DeprecationWarning) + if version_info.major == 2: + warn("unicode in cafile is no longer accepted, use bytes", DeprecationWarning) + elif version_info.major == 3: + warn("str in cafile is no longer accepted, use bytes", DeprecationWarning) + else: + warn("text in cafile is no longer accepted, use bytes", DeprecationWarning) cafile = cafile.encode('utf-8') if cafile is None: @@ -980,7 +985,12 @@ def send(self, buf, flags=0): # Backward compatibility if isinstance(buf, _text_type): - warnings.warn("str object in buf is no longer accepted, use bytes", DeprecationWarning) + if version_info.major == 2: + warn("unicode in buf is no longer accepted, use bytes", DeprecationWarning) + elif version_info.major == 3: + warn("str in buf is no longer accepted, use bytes", DeprecationWarning) + else: + warn("text in buf is no longer accepted, use bytes", DeprecationWarning) buf = buf.encode('utf-8') if isinstance(buf, _memoryview): @@ -1010,7 +1020,12 @@ def sendall(self, buf, flags=0): # Backward compatibility if isinstance(buf, _text_type): - warnings.warn("str object in buf is no longer accepted, use bytes", DeprecationWarning) + if version_info.major == 2: + warn("unicode in buf is no longer accepted, use bytes", DeprecationWarning) + elif version_info.major == 3: + warn("str in buf is no longer accepted, use bytes", DeprecationWarning) + else: + warn("text in buf is no longer accepted, use bytes", DeprecationWarning) buf = buf.encode('utf-8') if isinstance(buf, _memoryview): @@ -1101,7 +1116,12 @@ def bio_write(self, buf): # Backward compatibility if isinstance(buf, _text_type): - warnings.warn("str object in buf is no longer accepted, use bytes", DeprecationWarning) + if version_info.major == 2: + warn("unicode in buf is no longer accepted, use bytes", DeprecationWarning) + elif version_info.major == 3: + warn("str in vuf is no longer accepted, use bytes", DeprecationWarning) + else: + warn("text in buf is no longer accepted, use bytes", DeprecationWarning) buf = buf.encode("ascii") if self._into_ssl is None: diff --git a/OpenSSL/crypto.py b/OpenSSL/crypto.py index e8b6d3cfa..b671d9eda 100644 --- a/OpenSSL/crypto.py +++ b/OpenSSL/crypto.py @@ -1,4 +1,5 @@ -import warnings +from warnings import warn +from sys import version_info from time import time from base64 import b16encode from functools import partial @@ -1956,7 +1957,12 @@ def export(self, passphrase=None, iter=2048, maciter=1): # Backward compatibility if isinstance(passphrase, _text_type): - warnings.warn("str object in passphrase is no longer accepted, use bytes", DeprecationWarning) + if version_info.major == 2: + warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning) + elif version_info.major == 3: + warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning) + else: + warn("text in passphrase is no longer accepted, use bytes", DeprecationWarning) passphrase = passphrase.encode('utf-8') if self._cacerts is None: @@ -2259,7 +2265,12 @@ def sign(pkey, data, digest): # Backward compatibility if isinstance(data, _text_type): - warnings.warn("str object in passphrase is no longer accepted, use bytes", DeprecationWarning) + if version_info.major == 2: + warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning) + elif version_info.major == 3: + warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning) + else: + warn("text in passphrase is no longer accepted, use bytes", DeprecationWarning) data = data.encode('utf-8') digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest)) @@ -2299,7 +2310,12 @@ def verify(cert, signature, data, digest): # Backward compatibility if isinstance(data, _text_type): - warnings.warn("str object in passphrase is no longer accepted, use bytes", DeprecationWarning) + if version_info.major == 2: + warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning) + elif version_info.major == 3: + warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning) + else: + warn("text in passphrase is no longer accepted, use bytes", DeprecationWarning) data = data.encode('utf-8') digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest)) @@ -2396,7 +2412,12 @@ def load_pkcs12(buffer, passphrase=None): # Backward compatibility if isinstance(passphrase, _text_type): - warnings.warn("str object in passphrase is no longer accepted, use bytes", DeprecationWarning) + if version_info.major == 2: + warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning) + elif version_info.major == 3: + warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning) + else: + warn("text in passphrase is no longer accepted, use bytes", DeprecationWarning) passphrase = passphrase.encode('utf-8') if isinstance(buffer, _text_type): From d2f0b078b06696ec74ee43f92b82e5ea05173026 Mon Sep 17 00:00:00 2001 From: Abraham Martin Date: Wed, 25 Mar 2015 13:56:25 +0000 Subject: [PATCH 04/19] Use six library to check python version --- OpenSSL/SSL.py | 27 ++++++++++----------------- OpenSSL/crypto.py | 26 +++++++++----------------- 2 files changed, 19 insertions(+), 34 deletions(-) diff --git a/OpenSSL/SSL.py b/OpenSSL/SSL.py index 89720978d..eb26e76ce 100644 --- a/OpenSSL/SSL.py +++ b/OpenSSL/SSL.py @@ -1,5 +1,6 @@ +import six from warnings import warn -from sys import platform, version_info +from sys import platform from functools import wraps, partial from itertools import count from weakref import WeakValueDictionary @@ -314,12 +315,10 @@ def load_verify_locations(self, cafile, capath=None): # Backward compatibility if isinstance(cafile, _text_type): - if version_info.major == 2: + if six.PY2: warn("unicode in cafile is no longer accepted, use bytes", DeprecationWarning) - elif version_info.major == 3: - warn("str in cafile is no longer accepted, use bytes", DeprecationWarning) else: - warn("text in cafile is no longer accepted, use bytes", DeprecationWarning) + warn("str in cafile is no longer accepted, use bytes", DeprecationWarning) cafile = cafile.encode('utf-8') if cafile is None: @@ -985,12 +984,10 @@ def send(self, buf, flags=0): # Backward compatibility if isinstance(buf, _text_type): - if version_info.major == 2: + if six.PY2: warn("unicode in buf is no longer accepted, use bytes", DeprecationWarning) - elif version_info.major == 3: - warn("str in buf is no longer accepted, use bytes", DeprecationWarning) else: - warn("text in buf is no longer accepted, use bytes", DeprecationWarning) + warn("str in buf is no longer accepted, use bytes", DeprecationWarning) buf = buf.encode('utf-8') if isinstance(buf, _memoryview): @@ -1020,12 +1017,10 @@ def sendall(self, buf, flags=0): # Backward compatibility if isinstance(buf, _text_type): - if version_info.major == 2: + if six.PY2: warn("unicode in buf is no longer accepted, use bytes", DeprecationWarning) - elif version_info.major == 3: - warn("str in buf is no longer accepted, use bytes", DeprecationWarning) else: - warn("text in buf is no longer accepted, use bytes", DeprecationWarning) + warn("str in buf is no longer accepted, use bytes", DeprecationWarning) buf = buf.encode('utf-8') if isinstance(buf, _memoryview): @@ -1116,12 +1111,10 @@ def bio_write(self, buf): # Backward compatibility if isinstance(buf, _text_type): - if version_info.major == 2: + if six.PY2: warn("unicode in buf is no longer accepted, use bytes", DeprecationWarning) - elif version_info.major == 3: - warn("str in vuf is no longer accepted, use bytes", DeprecationWarning) else: - warn("text in buf is no longer accepted, use bytes", DeprecationWarning) + warn("str in buf is no longer accepted, use bytes", DeprecationWarning) buf = buf.encode("ascii") if self._into_ssl is None: diff --git a/OpenSSL/crypto.py b/OpenSSL/crypto.py index b671d9eda..cc99105ed 100644 --- a/OpenSSL/crypto.py +++ b/OpenSSL/crypto.py @@ -1,5 +1,5 @@ +import six from warnings import warn -from sys import version_info from time import time from base64 import b16encode from functools import partial @@ -1957,12 +1957,10 @@ def export(self, passphrase=None, iter=2048, maciter=1): # Backward compatibility if isinstance(passphrase, _text_type): - if version_info.major == 2: + if six.PY2: warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning) - elif version_info.major == 3: - warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning) else: - warn("text in passphrase is no longer accepted, use bytes", DeprecationWarning) + warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning) passphrase = passphrase.encode('utf-8') if self._cacerts is None: @@ -2265,12 +2263,10 @@ def sign(pkey, data, digest): # Backward compatibility if isinstance(data, _text_type): - if version_info.major == 2: + if six.PY2: warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning) - elif version_info.major == 3: - warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning) else: - warn("text in passphrase is no longer accepted, use bytes", DeprecationWarning) + warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning) data = data.encode('utf-8') digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest)) @@ -2310,12 +2306,10 @@ def verify(cert, signature, data, digest): # Backward compatibility if isinstance(data, _text_type): - if version_info.major == 2: + if six.PY2: warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning) - elif version_info.major == 3: - warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning) else: - warn("text in passphrase is no longer accepted, use bytes", DeprecationWarning) + warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning) data = data.encode('utf-8') digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest)) @@ -2412,12 +2406,10 @@ def load_pkcs12(buffer, passphrase=None): # Backward compatibility if isinstance(passphrase, _text_type): - if version_info.major == 2: + if six.PY2: warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning) - elif version_info.major == 3: - warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning) else: - warn("text in passphrase is no longer accepted, use bytes", DeprecationWarning) + warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning) passphrase = passphrase.encode('utf-8') if isinstance(buffer, _text_type): From 9778f41f01743b06b04c7ee0f4c438b77f19bc0b Mon Sep 17 00:00:00 2001 From: Abraham Martin Date: Wed, 25 Mar 2015 14:02:37 +0000 Subject: [PATCH 05/19] Reuse six imports --- OpenSSL/SSL.py | 10 +++++----- OpenSSL/crypto.py | 25 ++++++++++++------------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/OpenSSL/SSL.py b/OpenSSL/SSL.py index eb26e76ce..01d654fca 100644 --- a/OpenSSL/SSL.py +++ b/OpenSSL/SSL.py @@ -1,4 +1,3 @@ -import six from warnings import warn from sys import platform from functools import wraps, partial @@ -8,6 +7,7 @@ from six import text_type as _text_type from six import integer_types as integer_types +from six import PY2 as _PY2 from OpenSSL._util import ( ffi as _ffi, @@ -315,7 +315,7 @@ def load_verify_locations(self, cafile, capath=None): # Backward compatibility if isinstance(cafile, _text_type): - if six.PY2: + if _PY2: warn("unicode in cafile is no longer accepted, use bytes", DeprecationWarning) else: warn("str in cafile is no longer accepted, use bytes", DeprecationWarning) @@ -984,7 +984,7 @@ def send(self, buf, flags=0): # Backward compatibility if isinstance(buf, _text_type): - if six.PY2: + if _PY2: warn("unicode in buf is no longer accepted, use bytes", DeprecationWarning) else: warn("str in buf is no longer accepted, use bytes", DeprecationWarning) @@ -1017,7 +1017,7 @@ def sendall(self, buf, flags=0): # Backward compatibility if isinstance(buf, _text_type): - if six.PY2: + if _PY2: warn("unicode in buf is no longer accepted, use bytes", DeprecationWarning) else: warn("str in buf is no longer accepted, use bytes", DeprecationWarning) @@ -1111,7 +1111,7 @@ def bio_write(self, buf): # Backward compatibility if isinstance(buf, _text_type): - if six.PY2: + if _PY2: warn("unicode in buf is no longer accepted, use bytes", DeprecationWarning) else: warn("str in buf is no longer accepted, use bytes", DeprecationWarning) diff --git a/OpenSSL/crypto.py b/OpenSSL/crypto.py index cc99105ed..0909e6e28 100644 --- a/OpenSSL/crypto.py +++ b/OpenSSL/crypto.py @@ -1,4 +1,3 @@ -import six from warnings import warn from time import time from base64 import b16encode @@ -1957,10 +1956,10 @@ def export(self, passphrase=None, iter=2048, maciter=1): # Backward compatibility if isinstance(passphrase, _text_type): - if six.PY2: - warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning) - else: + if _PY3: warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning) + else: + warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning) passphrase = passphrase.encode('utf-8') if self._cacerts is None: @@ -2263,10 +2262,10 @@ def sign(pkey, data, digest): # Backward compatibility if isinstance(data, _text_type): - if six.PY2: - warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning) - else: + if _PY3: warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning) + else: + warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning) data = data.encode('utf-8') digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest)) @@ -2306,10 +2305,10 @@ def verify(cert, signature, data, digest): # Backward compatibility if isinstance(data, _text_type): - if six.PY2: - warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning) - else: + if _PY3: warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning) + else: + warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning) data = data.encode('utf-8') digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest)) @@ -2406,10 +2405,10 @@ def load_pkcs12(buffer, passphrase=None): # Backward compatibility if isinstance(passphrase, _text_type): - if six.PY2: - warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning) - else: + if _PY3: warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning) + else: + warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning) passphrase = passphrase.encode('utf-8') if isinstance(buffer, _text_type): From ef06348ff8c4c120212305b771dcdfa4a4712f32 Mon Sep 17 00:00:00 2001 From: Abraham Martin Date: Wed, 25 Mar 2015 14:06:24 +0000 Subject: [PATCH 06/19] Tests for added backward compatibility in SSL.py --- OpenSSL/test/test_ssl.py | 60 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/OpenSSL/test/test_ssl.py b/OpenSSL/test/test_ssl.py index f098327c7..83625a7fb 100644 --- a/OpenSSL/test/test_ssl.py +++ b/OpenSSL/test/test_ssl.py @@ -13,6 +13,7 @@ from os.path import join from unittest import main from weakref import ref +from warnings import catch_warnings, simplefilter from six import PY3, text_type, u @@ -917,6 +918,28 @@ def test_load_verify_file(self): self._load_verify_locations_test(cafile) + def test_load_verify_warning(self): + """ + :py:obj:`Context.load_verify_locations` accepts a file name and uses the + certificates within for verification purposes. Raises a warning when + using a text in cafile. + """ + cafile = self.mktemp() + fObj = open(cafile, 'w') + fObj.write(cleartextCertificatePEM.decode('ascii')) + fObj.close() + + with catch_warnings(record=True) as w: + simplefilter("always") + if version_info.major == 2: + self._load_verify_locations_test(unicode(cafile)) + self.assertTrue("unicode in cafile is no longer accepted, use bytes" in str(w[-1].message)) + elif version_info.major == 3: + self._load_verify_locations_test(cafile.decode()) + self.assertTrue("str in cafile is no longer accepted, use bytes" in str(w[-1].message)) + self.assertTrue(issubclass(w[-1].category, DeprecationWarning)) + + def test_load_verify_invalid_file(self): """ :py:obj:`Context.load_verify_locations` raises :py:obj:`Error` when passed a @@ -2233,6 +2256,25 @@ def test_short_bytes(self): self.assertEquals(count, 2) self.assertEquals(client.recv(2), b('xy')) + + def test_text(self): + """ + When passed a text, :py:obj:`Connection.send` transmits all of it and returns + the number of bytes sent. It also raises a DeprecationWarning. + """ + server, client = self._loopback() + with catch_warnings(record=True) as w: + simplefilter("always") + if PY3: + count = server.send(b'xy'.decode()) + self.assertTrue("str in buf is no longer accepted, use bytes" in str(w[-1].message)) + else: + count = server.send(unicode('xy')) + self.assertTrue("unicode in buf is no longer accepted, use bytes" in str(w[-1].message)) + self.assertTrue(issubclass(w[-1].category, DeprecationWarning)) + self.assertEquals(count, 2) + self.assertEquals(client.recv(2), b('xy')) + try: memoryview except NameError: @@ -2295,6 +2337,24 @@ def test_short(self): self.assertEquals(client.recv(1), b('x')) + def test_text(self): + """ + :py:obj:`Connection.sendall` transmits all the content in the string passed to + it raising a DepreactionWarning in case of this being a text. + """ + server, client = self._loopback() + with catch_warnings(record=True) as w: + simplefilter("always") + if PY3: + server.sendall(b'x'.decode()) + self.assertTrue("str in buf is no longer accepted, use bytes" in str(w[-1].message)) + else: + server.sendall(unicode('x')) + self.assertTrue("unicode in buf is no longer accepted, use bytes" in str(w[-1].message)) + self.assertTrue(issubclass(w[-1].category, DeprecationWarning)) + self.assertEquals(client.recv(1), b('x')) + + try: memoryview except NameError: From c5484ba01865d800b2a98f05b289906e423afcbc Mon Sep 17 00:00:00 2001 From: Abraham Martin Date: Wed, 25 Mar 2015 15:33:05 +0000 Subject: [PATCH 07/19] Tests for added backward compatibility in crypto.py --- OpenSSL/crypto.py | 8 ++-- OpenSSL/test/test_crypto.py | 90 ++++++++++++++++++++++++++++++++++++- OpenSSL/test/test_ssl.py | 8 ++-- 3 files changed, 97 insertions(+), 9 deletions(-) diff --git a/OpenSSL/crypto.py b/OpenSSL/crypto.py index 0909e6e28..cd49c2196 100644 --- a/OpenSSL/crypto.py +++ b/OpenSSL/crypto.py @@ -2263,9 +2263,9 @@ def sign(pkey, data, digest): # Backward compatibility if isinstance(data, _text_type): if _PY3: - warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning) + warn("str in data is no longer accepted, use bytes", DeprecationWarning) else: - warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning) + warn("unicode in data is no longer accepted, use bytes", DeprecationWarning) data = data.encode('utf-8') digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest)) @@ -2306,9 +2306,9 @@ def verify(cert, signature, data, digest): # Backward compatibility if isinstance(data, _text_type): if _PY3: - warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning) + warn("str in data is no longer accepted, use bytes", DeprecationWarning) else: - warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning) + warn("unicode in data is no longer accepted, use bytes", DeprecationWarning) data = data.encode('utf-8') digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest)) diff --git a/OpenSSL/test/test_crypto.py b/OpenSSL/test/test_crypto.py index f704ac02f..ba4e0d59e 100644 --- a/OpenSSL/test/test_crypto.py +++ b/OpenSSL/test/test_crypto.py @@ -13,7 +13,9 @@ from subprocess import PIPE, Popen from datetime import datetime, timedelta -from six import u, b, binary_type +from six import u, b, binary_type, PY3 +from warnings import simplefilter +from warnings import catch_warnings from OpenSSL.crypto import TYPE_RSA, TYPE_DSA, Error, PKey, PKeyType from OpenSSL.crypto import X509, X509Type, X509Name, X509NameType @@ -1992,6 +1994,29 @@ def test_load_pkcs12(self): self.verify_pkcs12_container(p12) + def test_load_pkcs12_text_passphrase(self): + """ + A PKCS12 string generated using the openssl command line can be loaded + with :py:obj:`load_pkcs12` and its components extracted and examined. + Using text as passphrase instead of bytes. DeprecationWarning expected. + """ + pem = client_key_pem + client_cert_pem + passwd = b"whatever" + p12_str = _runopenssl(pem, b"pkcs12", b"-export", b"-clcerts", + b"-passout", b"pass:" + passwd) + with catch_warnings(record=True) as w: + simplefilter("always") + if not PY3: + p12 = load_pkcs12(p12_str, passphrase=unicode("whatever")) + self.assertTrue("unicode in passphrase is no longer accepted, " + "use bytes" in str(w[-1].message)) + else: + p12 = load_pkcs12(p12_str, passphrase=b"whatever".decode()) + self.assertTrue("str in passphrase is no longer accepted, " + "use bytes" in str(w[-1].message)) + self.verify_pkcs12_container(p12) + + def test_load_pkcs12_no_passphrase(self): """ A PKCS12 string generated using openssl command line can be loaded with @@ -2193,6 +2218,26 @@ def test_export_without_args(self): dumped_p12, key=server_key_pem, cert=server_cert_pem, passwd=b"") + def test_export_without_bytes(self): + """ + Test :py:obj:`PKCS12.export` with text not bytes as passphrase + """ + p12 = self.gen_pkcs12(server_cert_pem, server_key_pem, root_cert_pem) + + with catch_warnings(record=True) as w: + simplefilter("always") + if not PY3: + dumped_p12 = p12.export(passphrase=unicode('randomtext')) + self.assertTrue("unicode in passphrase is no longer accepted, " + "use bytes" in str(w[-1].message)) + else: + dumped_p12 = p12.export(passphrase=b'randomtext'.decode()) + self.assertTrue("str in passphrase is no longer accepted, " + "use bytes" in str(w[-1].message)) + self.check_recovery( + dumped_p12, key=server_key_pem, cert=server_cert_pem, passwd=b"randomtext") + + def test_key_cert_mismatch(self): """ :py:obj:`PKCS12.export` raises an exception when a key and certificate @@ -3151,6 +3196,49 @@ def test_sign_verify(self): ValueError, verify, good_cert, sig, content, "strange-digest") + def test_sign_verify_with_text(self): + """ + :py:obj:`sign` generates a cryptographic signature which :py:obj:`verify` can check. + Deprecation warnings raised because using text instead of bytes as content + """ + if not PY3: + content = unicode( + "It was a bright cold day in April, and the clocks were striking " + "thirteen. Winston Smith, his chin nuzzled into his breast in an " + "effort to escape the vile wind, slipped quickly through the " + "glass doors of Victory Mansions, though not quickly enough to " + "prevent a swirl of gritty dust from entering along with him.") + else: + content = b( + "It was a bright cold day in April, and the clocks were striking " + "thirteen. Winston Smith, his chin nuzzled into his breast in an " + "effort to escape the vile wind, slipped quickly through the " + "glass doors of Victory Mansions, though not quickly enough to " + "prevent a swirl of gritty dust from entering along with him.").decode() + + priv_key = load_privatekey(FILETYPE_PEM, root_key_pem) + cert = load_certificate(FILETYPE_PEM, root_cert_pem) + for digest in ['md5', 'sha1']: + with catch_warnings(record=True) as w: + simplefilter("always") + sig = sign(priv_key, content, digest) + if not PY3: + self.assertTrue("unicode in data is no longer accepted, " + "use bytes" in str(w[-1].message)) + else: + self.assertTrue("str in data is no longer accepted, " + "use bytes" in str(w[-1].message)) + with catch_warnings(record=True) as w: + simplefilter("always") + verify(cert, sig, content, digest) + if not PY3: + self.assertTrue("unicode in data is no longer accepted, " + "use bytes" in str(w[-1].message)) + else: + self.assertTrue("str in data is no longer accepted, " + "use bytes" in str(w[-1].message)) + + def test_sign_nulls(self): """ :py:obj:`sign` produces a signature for a string with embedded nulls. diff --git a/OpenSSL/test/test_ssl.py b/OpenSSL/test/test_ssl.py index 83625a7fb..960c109bc 100644 --- a/OpenSSL/test/test_ssl.py +++ b/OpenSSL/test/test_ssl.py @@ -7,7 +7,7 @@ from gc import collect, get_referrers from errno import ECONNREFUSED, EINPROGRESS, EWOULDBLOCK, EPIPE, ESHUTDOWN -from sys import platform, version_info +from sys import platform from socket import SHUT_RDWR, error, socket from os import makedirs from os.path import join @@ -931,10 +931,10 @@ def test_load_verify_warning(self): with catch_warnings(record=True) as w: simplefilter("always") - if version_info.major == 2: + if not PY3: self._load_verify_locations_test(unicode(cafile)) self.assertTrue("unicode in cafile is no longer accepted, use bytes" in str(w[-1].message)) - elif version_info.major == 3: + else: self._load_verify_locations_test(cafile.decode()) self.assertTrue("str in cafile is no longer accepted, use bytes" in str(w[-1].message)) self.assertTrue(issubclass(w[-1].category, DeprecationWarning)) @@ -1615,7 +1615,7 @@ def test_set_tlsext_host_name_wrong_args(self): self.assertRaises( TypeError, conn.set_tlsext_host_name, b("with\0null")) - if version_info >= (3,): + if PY3: # On Python 3.x, don't accidentally implicitly convert from text. self.assertRaises( TypeError, From 6462b0768d7d88e66344e731d9a3cb9a5e5ef48a Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Sun, 29 Mar 2015 07:03:11 -0400 Subject: [PATCH 08/19] Factor the warning code into a shared helper. Remove some of the unnecessary repetition from the tests. --- OpenSSL/SSL.py | 40 +++-------------- OpenSSL/_util.py | 16 +++++++ OpenSSL/crypto.py | 40 +++-------------- OpenSSL/test/test_crypto.py | 88 +++++++++++++++++++------------------ OpenSSL/test/test_ssl.py | 70 +++++++++++++++-------------- OpenSSL/test/util.py | 9 ++++ 6 files changed, 119 insertions(+), 144 deletions(-) diff --git a/OpenSSL/SSL.py b/OpenSSL/SSL.py index 01d654fca..dc4ed3999 100644 --- a/OpenSSL/SSL.py +++ b/OpenSSL/SSL.py @@ -1,4 +1,3 @@ -from warnings import warn from sys import platform from functools import wraps, partial from itertools import count @@ -7,13 +6,13 @@ from six import text_type as _text_type from six import integer_types as integer_types -from six import PY2 as _PY2 from OpenSSL._util import ( ffi as _ffi, lib as _lib, exception_from_error_queue as _exception_from_error_queue, - native as _native) + native as _native, + warn_text as _warn_text) from OpenSSL.crypto import ( FILETYPE_PEM, _PassphraseHelper, PKey, X509Name, X509, X509Store) @@ -312,14 +311,7 @@ def load_verify_locations(self, cafile, capath=None): :param capath: In which directory we can find the certificates :return: None """ - - # Backward compatibility - if isinstance(cafile, _text_type): - if _PY2: - warn("unicode in cafile is no longer accepted, use bytes", DeprecationWarning) - else: - warn("str in cafile is no longer accepted, use bytes", DeprecationWarning) - cafile = cafile.encode('utf-8') + cafile = _warn_text("cafile", cafile) if cafile is None: cafile = _ffi.NULL @@ -981,14 +973,8 @@ def send(self, buf, flags=0): API, the value is ignored :return: The number of bytes written """ - # Backward compatibility - if isinstance(buf, _text_type): - if _PY2: - warn("unicode in buf is no longer accepted, use bytes", DeprecationWarning) - else: - warn("str in buf is no longer accepted, use bytes", DeprecationWarning) - buf = buf.encode('utf-8') + buf = _warn_text("buf", buf) if isinstance(buf, _memoryview): buf = buf.tobytes() @@ -1014,14 +1000,7 @@ def sendall(self, buf, flags=0): API, the value is ignored :return: The number of bytes written """ - - # Backward compatibility - if isinstance(buf, _text_type): - if _PY2: - warn("unicode in buf is no longer accepted, use bytes", DeprecationWarning) - else: - warn("str in buf is no longer accepted, use bytes", DeprecationWarning) - buf = buf.encode('utf-8') + buf = _warn_text("buf", buf) if isinstance(buf, _memoryview): buf = buf.tobytes() @@ -1108,14 +1087,7 @@ def bio_write(self, buf): :param buf: The string to put into the memory BIO. :return: The number of bytes written """ - - # Backward compatibility - if isinstance(buf, _text_type): - if _PY2: - warn("unicode in buf is no longer accepted, use bytes", DeprecationWarning) - else: - warn("str in buf is no longer accepted, use bytes", DeprecationWarning) - buf = buf.encode("ascii") + buf = _warn_text("buf", buf) if self._into_ssl is None: raise TypeError("Connection sock was not None") diff --git a/OpenSSL/_util.py b/OpenSSL/_util.py index baeecc632..da8270b37 100644 --- a/OpenSSL/_util.py +++ b/OpenSSL/_util.py @@ -1,3 +1,5 @@ +from warnings import warn + from six import PY3, binary_type, text_type from cryptography.hazmat.bindings.openssl.binding import Binding @@ -51,3 +53,17 @@ def byte_string(s): else: def byte_string(s): return s + +_TEXT_WARNING = u"{} for {{}} is no longer accepted, use bytes".format( + text_type.__name__ +) + +def warn_text(label, obj): + if isinstance(obj, text_type): + warn( + _TEXT_WARNING.format(label), + category=DeprecationWarning, + stacklevel=3 + ) + return obj.encode('utf-8') + return obj diff --git a/OpenSSL/crypto.py b/OpenSSL/crypto.py index cd49c2196..52320f30c 100644 --- a/OpenSSL/crypto.py +++ b/OpenSSL/crypto.py @@ -1,4 +1,3 @@ -from warnings import warn from time import time from base64 import b16encode from functools import partial @@ -14,7 +13,8 @@ lib as _lib, exception_from_error_queue as _exception_from_error_queue, byte_string as _byte_string, - native as _native) + native as _native, + warn_text as _warn_text) FILETYPE_PEM = _lib.SSL_FILETYPE_PEM FILETYPE_ASN1 = _lib.SSL_FILETYPE_ASN1 @@ -1953,14 +1953,7 @@ def export(self, passphrase=None, iter=2048, maciter=1): :return: The string containing the PKCS12 """ - - # Backward compatibility - if isinstance(passphrase, _text_type): - if _PY3: - warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning) - else: - warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning) - passphrase = passphrase.encode('utf-8') + passphrase = _warn_text("passphrase", passphrase) if self._cacerts is None: cacerts = _ffi.NULL @@ -2259,14 +2252,7 @@ def sign(pkey, data, digest): :param digest: message digest to use :return: signature """ - - # Backward compatibility - if isinstance(data, _text_type): - if _PY3: - warn("str in data is no longer accepted, use bytes", DeprecationWarning) - else: - warn("unicode in data is no longer accepted, use bytes", DeprecationWarning) - data = data.encode('utf-8') + data = _warn_text("data", data) digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest)) if digest_obj == _ffi.NULL: @@ -2302,14 +2288,7 @@ def verify(cert, signature, data, digest): :param digest: message digest to use :return: None if the signature is correct, raise exception otherwise """ - - # Backward compatibility - if isinstance(data, _text_type): - if _PY3: - warn("str in data is no longer accepted, use bytes", DeprecationWarning) - else: - warn("unicode in data is no longer accepted, use bytes", DeprecationWarning) - data = data.encode('utf-8') + data = _warn_text("data", data) digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest)) if digest_obj == _ffi.NULL: @@ -2402,14 +2381,7 @@ def load_pkcs12(buffer, passphrase=None): :param passphrase: (Optional) The password to decrypt the PKCS12 lump :returns: The PKCS12 object """ - - # Backward compatibility - if isinstance(passphrase, _text_type): - if _PY3: - warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning) - else: - warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning) - passphrase = passphrase.encode('utf-8') + passphrase = _warn_text("passphrase", passphrase) if isinstance(buffer, _text_type): buffer = buffer.encode("ascii") diff --git a/OpenSSL/test/test_crypto.py b/OpenSSL/test/test_crypto.py index ba4e0d59e..07733424b 100644 --- a/OpenSSL/test/test_crypto.py +++ b/OpenSSL/test/test_crypto.py @@ -31,7 +31,9 @@ from OpenSSL.crypto import NetscapeSPKI, NetscapeSPKIType from OpenSSL.crypto import ( sign, verify, get_elliptic_curve, get_elliptic_curves) -from OpenSSL.test.util import EqualityTestsMixin, TestCase +from OpenSSL.test.util import ( + EqualityTestsMixin, TestCase, WARNING_TYPE_EXPECTED +) from OpenSSL._util import native, lib def normalize_certificate_pem(pem): @@ -2006,14 +2008,16 @@ def test_load_pkcs12_text_passphrase(self): b"-passout", b"pass:" + passwd) with catch_warnings(record=True) as w: simplefilter("always") - if not PY3: - p12 = load_pkcs12(p12_str, passphrase=unicode("whatever")) - self.assertTrue("unicode in passphrase is no longer accepted, " - "use bytes" in str(w[-1].message)) - else: - p12 = load_pkcs12(p12_str, passphrase=b"whatever".decode()) - self.assertTrue("str in passphrase is no longer accepted, " - "use bytes" in str(w[-1].message)) + p12 = load_pkcs12(p12_str, passphrase=u"whatever") + + self.assertEqual( + u"{} for passphrase is no longer accepted, use bytes".format( + WARNING_TYPE_EXPECTED + ), + str(w[-1].message) + ) + self.assertIs(w[-1].category, DeprecationWarning) + self.verify_pkcs12_container(p12) @@ -2226,14 +2230,14 @@ def test_export_without_bytes(self): with catch_warnings(record=True) as w: simplefilter("always") - if not PY3: - dumped_p12 = p12.export(passphrase=unicode('randomtext')) - self.assertTrue("unicode in passphrase is no longer accepted, " - "use bytes" in str(w[-1].message)) - else: - dumped_p12 = p12.export(passphrase=b'randomtext'.decode()) - self.assertTrue("str in passphrase is no longer accepted, " - "use bytes" in str(w[-1].message)) + dumped_p12 = p12.export(passphrase=u"randomtext") + self.assertEqual( + u"{} for passphrase is no longer accepted, use bytes".format( + WARNING_TYPE_EXPECTED + ), + str(w[-1].message) + ) + self.assertIs(w[-1].category, DeprecationWarning) self.check_recovery( dumped_p12, key=server_key_pem, cert=server_cert_pem, passwd=b"randomtext") @@ -3201,20 +3205,13 @@ def test_sign_verify_with_text(self): :py:obj:`sign` generates a cryptographic signature which :py:obj:`verify` can check. Deprecation warnings raised because using text instead of bytes as content """ - if not PY3: - content = unicode( - "It was a bright cold day in April, and the clocks were striking " - "thirteen. Winston Smith, his chin nuzzled into his breast in an " - "effort to escape the vile wind, slipped quickly through the " - "glass doors of Victory Mansions, though not quickly enough to " - "prevent a swirl of gritty dust from entering along with him.") - else: - content = b( - "It was a bright cold day in April, and the clocks were striking " - "thirteen. Winston Smith, his chin nuzzled into his breast in an " - "effort to escape the vile wind, slipped quickly through the " - "glass doors of Victory Mansions, though not quickly enough to " - "prevent a swirl of gritty dust from entering along with him.").decode() + content = ( + u"It was a bright cold day in April, and the clocks were striking " + u"thirteen. Winston Smith, his chin nuzzled into his breast in an " + u"effort to escape the vile wind, slipped quickly through the " + u"glass doors of Victory Mansions, though not quickly enough to " + u"prevent a swirl of gritty dust from entering along with him." + ) priv_key = load_privatekey(FILETYPE_PEM, root_key_pem) cert = load_certificate(FILETYPE_PEM, root_cert_pem) @@ -3222,21 +3219,26 @@ def test_sign_verify_with_text(self): with catch_warnings(record=True) as w: simplefilter("always") sig = sign(priv_key, content, digest) - if not PY3: - self.assertTrue("unicode in data is no longer accepted, " - "use bytes" in str(w[-1].message)) - else: - self.assertTrue("str in data is no longer accepted, " - "use bytes" in str(w[-1].message)) + + self.assertEqual( + u"{} for data is no longer accepted, use bytes".format( + WARNING_TYPE_EXPECTED + ), + str(w[-1].message) + ) + self.assertIs(w[-1].category, DeprecationWarning) + with catch_warnings(record=True) as w: simplefilter("always") verify(cert, sig, content, digest) - if not PY3: - self.assertTrue("unicode in data is no longer accepted, " - "use bytes" in str(w[-1].message)) - else: - self.assertTrue("str in data is no longer accepted, " - "use bytes" in str(w[-1].message)) + + self.assertEqual( + u"{} for data is no longer accepted, use bytes".format( + WARNING_TYPE_EXPECTED + ), + str(w[-1].message) + ) + self.assertIs(w[-1].category, DeprecationWarning) def test_sign_nulls(self): diff --git a/OpenSSL/test/test_ssl.py b/OpenSSL/test/test_ssl.py index 960c109bc..16bbf39cd 100644 --- a/OpenSSL/test/test_ssl.py +++ b/OpenSSL/test/test_ssl.py @@ -1,3 +1,5 @@ + + # Copyright (C) Jean-Paul Calderone # See LICENSE for details. @@ -44,7 +46,7 @@ from OpenSSL.SSL import ( Context, ContextType, Session, Connection, ConnectionType, SSLeay_version) -from OpenSSL.test.util import TestCase, b +from OpenSSL.test.util import WARNING_TYPE_EXPECTED, TestCase, b from OpenSSL.test.test_crypto import ( cleartextCertificatePEM, cleartextPrivateKeyPEM) from OpenSSL.test.test_crypto import ( @@ -920,24 +922,24 @@ def test_load_verify_file(self): def test_load_verify_warning(self): """ - :py:obj:`Context.load_verify_locations` accepts a file name and uses the - certificates within for verification purposes. Raises a warning when - using a text in cafile. + :py:obj:`Context.load_verify_locations` accepts a file name and uses + the certificates within for verification purposes. Raises a warning + when using a text in cafile. """ - cafile = self.mktemp() - fObj = open(cafile, 'w') - fObj.write(cleartextCertificatePEM.decode('ascii')) - fObj.close() + cafile = self.mktemp().decode("utf-8") + with open(cafile, 'w') as fObj: + fObj.write(cleartextCertificatePEM.decode("ascii")) with catch_warnings(record=True) as w: simplefilter("always") - if not PY3: - self._load_verify_locations_test(unicode(cafile)) - self.assertTrue("unicode in cafile is no longer accepted, use bytes" in str(w[-1].message)) - else: - self._load_verify_locations_test(cafile.decode()) - self.assertTrue("str in cafile is no longer accepted, use bytes" in str(w[-1].message)) - self.assertTrue(issubclass(w[-1].category, DeprecationWarning)) + self._load_verify_locations_test(cafile) + self.assertEqual( + u"{} for cafile is no longer accepted, use bytes".format( + WARNING_TYPE_EXPECTED + ), + str(w[-1].message) + ) + self.assertIs(DeprecationWarning, w[-1].category) def test_load_verify_invalid_file(self): @@ -2259,21 +2261,22 @@ def test_short_bytes(self): def test_text(self): """ - When passed a text, :py:obj:`Connection.send` transmits all of it and returns - the number of bytes sent. It also raises a DeprecationWarning. + When passed a text, :py:obj:`Connection.send` transmits all of it and + returns the number of bytes sent. It also raises a DeprecationWarning. """ server, client = self._loopback() with catch_warnings(record=True) as w: simplefilter("always") - if PY3: - count = server.send(b'xy'.decode()) - self.assertTrue("str in buf is no longer accepted, use bytes" in str(w[-1].message)) - else: - count = server.send(unicode('xy')) - self.assertTrue("unicode in buf is no longer accepted, use bytes" in str(w[-1].message)) - self.assertTrue(issubclass(w[-1].category, DeprecationWarning)) + count = server.send(u"xy") + self.assertEqual( + u"{} for buf is no longer accepted, use bytes".format( + WARNING_TYPE_EXPECTED + ), + str(w[-1].message) + ) + self.assertIs(w[-1].category, DeprecationWarning) self.assertEquals(count, 2) - self.assertEquals(client.recv(2), b('xy')) + self.assertEquals(client.recv(2), b"xy") try: memoryview @@ -2345,14 +2348,15 @@ def test_text(self): server, client = self._loopback() with catch_warnings(record=True) as w: simplefilter("always") - if PY3: - server.sendall(b'x'.decode()) - self.assertTrue("str in buf is no longer accepted, use bytes" in str(w[-1].message)) - else: - server.sendall(unicode('x')) - self.assertTrue("unicode in buf is no longer accepted, use bytes" in str(w[-1].message)) - self.assertTrue(issubclass(w[-1].category, DeprecationWarning)) - self.assertEquals(client.recv(1), b('x')) + server.sendall(u"x") + self.assertEqual( + u"{} for buf is no longer accepted, use bytes".format( + WARNING_TYPE_EXPECTED + ), + str(w[-1].message) + ) + self.assertIs(w[-1].category, DeprecationWarning) + self.assertEquals(client.recv(1), b"x") try: diff --git a/OpenSSL/test/util.py b/OpenSSL/test/util.py index 4260eb0bf..b69e538b1 100644 --- a/OpenSSL/test/util.py +++ b/OpenSSL/test/util.py @@ -14,6 +14,8 @@ from unittest import TestCase import sys +from six import PY3 + from OpenSSL._util import exception_from_error_queue from OpenSSL.crypto import Error @@ -447,3 +449,10 @@ def __ne__(self, other): a = self.anInstance() b = Delegate() self.assertEqual(a != b, [b]) + + +# The type name expected in warnings about using the wrong string type. +if PY3: + WARNING_TYPE_EXPECTED = "str" +else: + WARNING_TYPE_EXPECTED = "unicode" From 0894b17761d4f2b8b462be445ca8371d94f7fd8d Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Sun, 29 Mar 2015 07:15:14 -0400 Subject: [PATCH 09/19] docstring --- OpenSSL/_util.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/OpenSSL/_util.py b/OpenSSL/_util.py index da8270b37..bb2b7401d 100644 --- a/OpenSSL/_util.py +++ b/OpenSSL/_util.py @@ -59,6 +59,18 @@ def byte_string(s): ) def warn_text(label, obj): + """ + If ``obj`` is text, emit a warning that it should be bytes instead and try + to convert it to bytes automatically. + + :param str label: The name of the parameter from which ``obj`` was taken + (so a developer can easily find the source of the problem and correct + it). + + :return: If ``obj`` is the text string type, a ``bytes`` object giving the + UTF-8 encoding of that text is returned. Otherwise, ``obj`` itself is + returned. + """ if isinstance(obj, text_type): warn( _TEXT_WARNING.format(label), From 607a380b635997e8f194aab92fc108e00efce316 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Sun, 29 Mar 2015 07:42:07 -0400 Subject: [PATCH 10/19] Remove the deprecation for this parameter that represents a path name. --- OpenSSL/SSL.py | 2 -- OpenSSL/test/test_ssl.py | 22 ---------------------- 2 files changed, 24 deletions(-) diff --git a/OpenSSL/SSL.py b/OpenSSL/SSL.py index dc4ed3999..0e645efa6 100644 --- a/OpenSSL/SSL.py +++ b/OpenSSL/SSL.py @@ -311,8 +311,6 @@ def load_verify_locations(self, cafile, capath=None): :param capath: In which directory we can find the certificates :return: None """ - cafile = _warn_text("cafile", cafile) - if cafile is None: cafile = _ffi.NULL elif not isinstance(cafile, bytes): diff --git a/OpenSSL/test/test_ssl.py b/OpenSSL/test/test_ssl.py index 16bbf39cd..f3983dde9 100644 --- a/OpenSSL/test/test_ssl.py +++ b/OpenSSL/test/test_ssl.py @@ -920,28 +920,6 @@ def test_load_verify_file(self): self._load_verify_locations_test(cafile) - def test_load_verify_warning(self): - """ - :py:obj:`Context.load_verify_locations` accepts a file name and uses - the certificates within for verification purposes. Raises a warning - when using a text in cafile. - """ - cafile = self.mktemp().decode("utf-8") - with open(cafile, 'w') as fObj: - fObj.write(cleartextCertificatePEM.decode("ascii")) - - with catch_warnings(record=True) as w: - simplefilter("always") - self._load_verify_locations_test(cafile) - self.assertEqual( - u"{} for cafile is no longer accepted, use bytes".format( - WARNING_TYPE_EXPECTED - ), - str(w[-1].message) - ) - self.assertIs(DeprecationWarning, w[-1].category) - - def test_load_verify_invalid_file(self): """ :py:obj:`Context.load_verify_locations` raises :py:obj:`Error` when passed a From 0c021998baf332f69313c832532445030432de5e Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Sun, 29 Mar 2015 07:46:30 -0400 Subject: [PATCH 11/19] Use a spelling that is Python 2.6 compatible. --- OpenSSL/_util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenSSL/_util.py b/OpenSSL/_util.py index bb2b7401d..59a7880c9 100644 --- a/OpenSSL/_util.py +++ b/OpenSSL/_util.py @@ -54,8 +54,8 @@ def byte_string(s): def byte_string(s): return s -_TEXT_WARNING = u"{} for {{}} is no longer accepted, use bytes".format( - text_type.__name__ +_TEXT_WARNING = ( + text_type.__name__ + u" for {} is no longer accepted, use bytes" ) def warn_text(label, obj): From 7578f356c0dc33cce168110fe41fe2095ac2792c Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Sun, 29 Mar 2015 07:54:19 -0400 Subject: [PATCH 12/19] I mean Python 2.6 compatible. --- OpenSSL/_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSSL/_util.py b/OpenSSL/_util.py index 59a7880c9..334c850c4 100644 --- a/OpenSSL/_util.py +++ b/OpenSSL/_util.py @@ -55,7 +55,7 @@ def byte_string(s): return s _TEXT_WARNING = ( - text_type.__name__ + u" for {} is no longer accepted, use bytes" + text_type.__name__ + u" for {0} is no longer accepted, use bytes" ) def warn_text(label, obj): From 13a0e656d16c12cdf6008205b2ad2142d671fba3 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Sun, 29 Mar 2015 07:58:51 -0400 Subject: [PATCH 13/19] I mean Python 2.6. --- OpenSSL/_util.py | 2 +- OpenSSL/test/test_crypto.py | 24 ++++++++++++------------ OpenSSL/test/test_ssl.py | 8 ++++---- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/OpenSSL/_util.py b/OpenSSL/_util.py index 334c850c4..2c12e84d2 100644 --- a/OpenSSL/_util.py +++ b/OpenSSL/_util.py @@ -55,7 +55,7 @@ def byte_string(s): return s _TEXT_WARNING = ( - text_type.__name__ + u" for {0} is no longer accepted, use bytes" + text_type.__name__ + " for {0} is no longer accepted, use bytes" ) def warn_text(label, obj): diff --git a/OpenSSL/test/test_crypto.py b/OpenSSL/test/test_crypto.py index 07733424b..8f1ee1b37 100644 --- a/OpenSSL/test/test_crypto.py +++ b/OpenSSL/test/test_crypto.py @@ -2008,10 +2008,10 @@ def test_load_pkcs12_text_passphrase(self): b"-passout", b"pass:" + passwd) with catch_warnings(record=True) as w: simplefilter("always") - p12 = load_pkcs12(p12_str, passphrase=u"whatever") + p12 = load_pkcs12(p12_str, passphrase=b"whatever".decode("ascii")) self.assertEqual( - u"{} for passphrase is no longer accepted, use bytes".format( + "{0} for passphrase is no longer accepted, use bytes".format( WARNING_TYPE_EXPECTED ), str(w[-1].message) @@ -2230,9 +2230,9 @@ def test_export_without_bytes(self): with catch_warnings(record=True) as w: simplefilter("always") - dumped_p12 = p12.export(passphrase=u"randomtext") + dumped_p12 = p12.export(passphrase=b"randomtext".decode("ascii")) self.assertEqual( - u"{} for passphrase is no longer accepted, use bytes".format( + "{0} for passphrase is no longer accepted, use bytes".format( WARNING_TYPE_EXPECTED ), str(w[-1].message) @@ -3206,12 +3206,12 @@ def test_sign_verify_with_text(self): Deprecation warnings raised because using text instead of bytes as content """ content = ( - u"It was a bright cold day in April, and the clocks were striking " - u"thirteen. Winston Smith, his chin nuzzled into his breast in an " - u"effort to escape the vile wind, slipped quickly through the " - u"glass doors of Victory Mansions, though not quickly enough to " - u"prevent a swirl of gritty dust from entering along with him." - ) + "It was a bright cold day in April, and the clocks were striking " + "thirteen. Winston Smith, his chin nuzzled into his breast in an " + "effort to escape the vile wind, slipped quickly through the " + "glass doors of Victory Mansions, though not quickly enough to " + "prevent a swirl of gritty dust from entering along with him." + ).decode("ascii") priv_key = load_privatekey(FILETYPE_PEM, root_key_pem) cert = load_certificate(FILETYPE_PEM, root_cert_pem) @@ -3221,7 +3221,7 @@ def test_sign_verify_with_text(self): sig = sign(priv_key, content, digest) self.assertEqual( - u"{} for data is no longer accepted, use bytes".format( + "{0} for data is no longer accepted, use bytes".format( WARNING_TYPE_EXPECTED ), str(w[-1].message) @@ -3233,7 +3233,7 @@ def test_sign_verify_with_text(self): verify(cert, sig, content, digest) self.assertEqual( - u"{} for data is no longer accepted, use bytes".format( + "{0} for data is no longer accepted, use bytes".format( WARNING_TYPE_EXPECTED ), str(w[-1].message) diff --git a/OpenSSL/test/test_ssl.py b/OpenSSL/test/test_ssl.py index f3983dde9..a77c86a82 100644 --- a/OpenSSL/test/test_ssl.py +++ b/OpenSSL/test/test_ssl.py @@ -2245,9 +2245,9 @@ def test_text(self): server, client = self._loopback() with catch_warnings(record=True) as w: simplefilter("always") - count = server.send(u"xy") + count = server.send(b"xy".decode("ascii")) self.assertEqual( - u"{} for buf is no longer accepted, use bytes".format( + "{0} for buf is no longer accepted, use bytes".format( WARNING_TYPE_EXPECTED ), str(w[-1].message) @@ -2326,9 +2326,9 @@ def test_text(self): server, client = self._loopback() with catch_warnings(record=True) as w: simplefilter("always") - server.sendall(u"x") + server.sendall("x".decode("ascii")) self.assertEqual( - u"{} for buf is no longer accepted, use bytes".format( + "{0} for buf is no longer accepted, use bytes".format( WARNING_TYPE_EXPECTED ), str(w[-1].message) From 362c1f56c458bf36ec5de0deb42ad26d2b052603 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Sun, 29 Mar 2015 08:01:39 -0400 Subject: [PATCH 14/19] And Python 3.2 compatibility. --- OpenSSL/test/test_crypto.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/OpenSSL/test/test_crypto.py b/OpenSSL/test/test_crypto.py index 8f1ee1b37..50da5fef9 100644 --- a/OpenSSL/test/test_crypto.py +++ b/OpenSSL/test/test_crypto.py @@ -3206,11 +3206,11 @@ def test_sign_verify_with_text(self): Deprecation warnings raised because using text instead of bytes as content """ content = ( - "It was a bright cold day in April, and the clocks were striking " - "thirteen. Winston Smith, his chin nuzzled into his breast in an " - "effort to escape the vile wind, slipped quickly through the " - "glass doors of Victory Mansions, though not quickly enough to " - "prevent a swirl of gritty dust from entering along with him." + b"It was a bright cold day in April, and the clocks were striking " + b"thirteen. Winston Smith, his chin nuzzled into his breast in an " + b"effort to escape the vile wind, slipped quickly through the " + b"glass doors of Victory Mansions, though not quickly enough to " + b"prevent a swirl of gritty dust from entering along with him." ).decode("ascii") priv_key = load_privatekey(FILETYPE_PEM, root_key_pem) From 8dc37a1ba6276d6a690ed2b21fdb665d6d97ca74 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Sun, 29 Mar 2015 08:16:52 -0400 Subject: [PATCH 15/19] One more. --- OpenSSL/test/test_ssl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSSL/test/test_ssl.py b/OpenSSL/test/test_ssl.py index a77c86a82..0ea52a4b0 100644 --- a/OpenSSL/test/test_ssl.py +++ b/OpenSSL/test/test_ssl.py @@ -2326,7 +2326,7 @@ def test_text(self): server, client = self._loopback() with catch_warnings(record=True) as w: simplefilter("always") - server.sendall("x".decode("ascii")) + server.sendall(b"x".decode("ascii")) self.assertEqual( "{0} for buf is no longer accepted, use bytes".format( WARNING_TYPE_EXPECTED From f188d23eebad63fc66e9d185d8817bb1114bd5ec Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Mon, 13 Apr 2015 20:34:50 -0400 Subject: [PATCH 16/19] Remove accidental space. --- OpenSSL/test/test_ssl.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/OpenSSL/test/test_ssl.py b/OpenSSL/test/test_ssl.py index eae895e0f..a7f8634db 100644 --- a/OpenSSL/test/test_ssl.py +++ b/OpenSSL/test/test_ssl.py @@ -1,5 +1,3 @@ - - # Copyright (C) Jean-Paul Calderone # See LICENSE for details. From 516c12c17444871473002aa6ac4e852e5aa5f952 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Mon, 13 Apr 2015 20:34:57 -0400 Subject: [PATCH 17/19] Remove unused import. --- OpenSSL/test/test_ssl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSSL/test/test_ssl.py b/OpenSSL/test/test_ssl.py index a7f8634db..724bcaef6 100644 --- a/OpenSSL/test/test_ssl.py +++ b/OpenSSL/test/test_ssl.py @@ -7,7 +7,7 @@ from gc import collect, get_referrers from errno import ECONNREFUSED, EINPROGRESS, EWOULDBLOCK, EPIPE, ESHUTDOWN -from sys import platform, version_info, getfilesystemencoding +from sys import platform, getfilesystemencoding from socket import SHUT_RDWR, error, socket from os import makedirs from os.path import join From 93477426f7931b6b36b3ec01922250b385fe808f Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Mon, 13 Apr 2015 20:38:57 -0400 Subject: [PATCH 18/19] Fix wrapping and typo in warning name. --- OpenSSL/test/test_ssl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenSSL/test/test_ssl.py b/OpenSSL/test/test_ssl.py index 724bcaef6..bd0014242 100644 --- a/OpenSSL/test/test_ssl.py +++ b/OpenSSL/test/test_ssl.py @@ -2786,8 +2786,8 @@ def test_short(self): def test_text(self): """ - :py:obj:`Connection.sendall` transmits all the content in the string passed to - it raising a DepreactionWarning in case of this being a text. + :py:obj:`Connection.sendall` transmits all the content in the string + passed to it raising a DeprecationWarning in case of this being a text. """ server, client = self._loopback() with catch_warnings(record=True) as w: From 39a8d590ffb7c8adaa641f182ac7571ba30936a9 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Mon, 13 Apr 2015 20:49:50 -0400 Subject: [PATCH 19/19] Rename warn_text to something a little more descriptive. --- OpenSSL/SSL.py | 8 ++++---- OpenSSL/_util.py | 2 +- OpenSSL/crypto.py | 11 ++++++----- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/OpenSSL/SSL.py b/OpenSSL/SSL.py index 36ef14a3a..54092c5a7 100644 --- a/OpenSSL/SSL.py +++ b/OpenSSL/SSL.py @@ -13,7 +13,7 @@ lib as _lib, exception_from_error_queue as _exception_from_error_queue, native as _native, - warn_text as _warn_text, + text_to_bytes_and_warn as _text_to_bytes_and_warn, path_string as _path_string, ) @@ -1127,7 +1127,7 @@ def send(self, buf, flags=0): :return: The number of bytes written """ # Backward compatibility - buf = _warn_text("buf", buf) + buf = _text_to_bytes_and_warn("buf", buf) if isinstance(buf, _memoryview): buf = buf.tobytes() @@ -1153,7 +1153,7 @@ def sendall(self, buf, flags=0): API, the value is ignored :return: The number of bytes written """ - buf = _warn_text("buf", buf) + buf = _text_to_bytes_and_warn("buf", buf) if isinstance(buf, _memoryview): buf = buf.tobytes() @@ -1279,7 +1279,7 @@ def bio_write(self, buf): :param buf: The string to put into the memory BIO. :return: The number of bytes written """ - buf = _warn_text("buf", buf) + buf = _text_to_bytes_and_warn("buf", buf) if self._into_ssl is None: raise TypeError("Connection sock was not None") diff --git a/OpenSSL/_util.py b/OpenSSL/_util.py index 990925006..8d59252d4 100644 --- a/OpenSSL/_util.py +++ b/OpenSSL/_util.py @@ -99,7 +99,7 @@ def byte_string(s): text_type.__name__ + " for {0} is no longer accepted, use bytes" ) -def warn_text(label, obj): +def text_to_bytes_and_warn(label, obj): """ If ``obj`` is text, emit a warning that it should be bytes instead and try to convert it to bytes automatically. diff --git a/OpenSSL/crypto.py b/OpenSSL/crypto.py index 6013ca3f9..f9e189c8e 100644 --- a/OpenSSL/crypto.py +++ b/OpenSSL/crypto.py @@ -14,7 +14,8 @@ exception_from_error_queue as _exception_from_error_queue, byte_string as _byte_string, native as _native, - warn_text as _warn_text) + text_to_bytes_and_warn as _text_to_bytes_and_warn, +) FILETYPE_PEM = _lib.SSL_FILETYPE_PEM FILETYPE_ASN1 = _lib.SSL_FILETYPE_ASN1 @@ -2075,7 +2076,7 @@ def export(self, passphrase=None, iter=2048, maciter=1): :return: The string containing the PKCS12 """ - passphrase = _warn_text("passphrase", passphrase) + passphrase = _text_to_bytes_and_warn("passphrase", passphrase) if self._cacerts is None: cacerts = _ffi.NULL @@ -2374,7 +2375,7 @@ def sign(pkey, data, digest): :param digest: message digest to use :return: signature """ - data = _warn_text("data", data) + data = _text_to_bytes_and_warn("data", data) digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest)) if digest_obj == _ffi.NULL: @@ -2410,7 +2411,7 @@ def verify(cert, signature, data, digest): :param digest: message digest to use :return: None if the signature is correct, raise exception otherwise """ - data = _warn_text("data", data) + data = _text_to_bytes_and_warn("data", data) digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest)) if digest_obj == _ffi.NULL: @@ -2502,7 +2503,7 @@ def load_pkcs12(buffer, passphrase=None): :param passphrase: (Optional) The password to decrypt the PKCS12 lump :returns: The PKCS12 object """ - passphrase = _warn_text("passphrase", passphrase) + passphrase = _text_to_bytes_and_warn("passphrase", passphrase) if isinstance(buffer, _text_type): buffer = buffer.encode("ascii")