Merge branch 'master' into ecdhe-support
Conflicts:
.gitignore
OpenSSL/test/test_ssl.py
diff --git a/.gitignore b/.gitignore
index 539da74..276d4e8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,4 @@
*.py[co]
+build
+dist
+*.egg-info
diff --git a/OpenSSL/SSL.py b/OpenSSL/SSL.py
index 62ce6c1..03aa47b 100644
--- a/OpenSSL/SSL.py
+++ b/OpenSSL/SSL.py
@@ -1,9 +1,12 @@
-
+from sys import platform
from functools import wraps, partial
from itertools import count
from weakref import WeakValueDictionary
from errno import errorcode
+from six import text_type as _text_type
+from six import integer_types as integer_types
+
from OpenSSL._util import (
ffi as _ffi,
lib as _lib,
@@ -204,16 +207,15 @@
def _asFileDescriptor(obj):
fd = None
-
- if not isinstance(obj, int):
+ if not isinstance(obj, integer_types):
meth = getattr(obj, "fileno", None)
if meth is not None:
obj = meth()
- if isinstance(obj, int):
+ if isinstance(obj, integer_types):
fd = obj
- if not isinstance(fd, int):
+ if not isinstance(fd, integer_types):
raise TypeError("argument must be an int, or have a fileno() method.")
elif fd < 0:
raise ValueError(
@@ -261,7 +263,7 @@
:param method: One of SSLv2_METHOD, SSLv3_METHOD, SSLv23_METHOD, or
TLSv1_METHOD.
"""
- if not isinstance(method, int):
+ if not isinstance(method, integer_types):
raise TypeError("method must be an integer")
try:
@@ -367,8 +369,12 @@
:param certfile: The name of the certificate chain file
:return: None
"""
+ if isinstance(certfile, _text_type):
+ # Perhaps sys.getfilesystemencoding() could be better?
+ certfile = certfile.encode("utf-8")
+
if not isinstance(certfile, bytes):
- raise TypeError("certfile must be a byte string")
+ raise TypeError("certfile must be bytes or unicode")
result = _lib.SSL_CTX_use_certificate_chain_file(self._context, certfile)
if not result:
@@ -383,9 +389,12 @@
:param filetype: (optional) The encoding of the file, default is PEM
:return: None
"""
+ if isinstance(certfile, _text_type):
+ # Perhaps sys.getfilesystemencoding() could be better?
+ certfile = certfile.encode("utf-8")
if not isinstance(certfile, bytes):
- raise TypeError("certfile must be a byte string")
- if not isinstance(filetype, int):
+ raise TypeError("certfile must be bytes or unicode")
+ if not isinstance(filetype, integer_types):
raise TypeError("filetype must be an integer")
use_result = _lib.SSL_CTX_use_certificate_file(self._context, certfile, filetype)
@@ -442,12 +451,16 @@
:param filetype: (optional) The encoding of the file, default is PEM
:return: None
"""
+ if isinstance(keyfile, _text_type):
+ # Perhaps sys.getfilesystemencoding() could be better?
+ keyfile = keyfile.encode("utf-8")
+
if not isinstance(keyfile, bytes):
raise TypeError("keyfile must be a byte string")
if filetype is _unspecified:
filetype = FILETYPE_PEM
- elif not isinstance(filetype, int):
+ elif not isinstance(filetype, integer_types):
raise TypeError("filetype must be an integer")
use_result = _lib.SSL_CTX_use_PrivateKey_file(
@@ -506,7 +519,7 @@
bitwise or)
:returns: The previously set caching mode.
"""
- if not isinstance(mode, int):
+ if not isinstance(mode, integer_types):
raise TypeError("mode must be an integer")
return _lib.SSL_CTX_set_session_cache_mode(self._context, mode)
@@ -530,7 +543,7 @@
See SSL_CTX_set_verify(3SSL) for further details.
"""
- if not isinstance(mode, int):
+ if not isinstance(mode, integer_types):
raise TypeError("mode must be an integer")
if not callable(callback):
@@ -548,7 +561,7 @@
:param depth: An integer specifying the verify depth
:return: None
"""
- if not isinstance(depth, int):
+ if not isinstance(depth, integer_types):
raise TypeError("depth must be an integer")
_lib.SSL_CTX_set_verify_depth(self._context, depth)
@@ -619,8 +632,11 @@
:param cipher_list: A cipher list, see ciphers(1)
:return: None
"""
+ if isinstance(cipher_list, _text_type):
+ cipher_list = cipher_list.encode("ascii")
+
if not isinstance(cipher_list, bytes):
- raise TypeError("cipher_list must be a byte string")
+ raise TypeError("cipher_list must be bytes or unicode")
result = _lib.SSL_CTX_set_cipher_list(self._context, cipher_list)
if not result:
@@ -690,7 +706,7 @@
:param timeout: The timeout in seconds
:return: The previous session timeout
"""
- if not isinstance(timeout, int):
+ if not isinstance(timeout, integer_types):
raise TypeError("timeout must be an integer")
return _lib.SSL_CTX_set_timeout(self._context, timeout)
@@ -714,7 +730,7 @@
"""
@wraps(callback)
def wrapper(ssl, where, return_code):
- callback(self, where, return_code)
+ callback(Connection._reverse_mapping[ssl], where, return_code)
self._info_callback = _ffi.callback(
"void (*)(const SSL *, int, int)", wrapper)
_lib.SSL_CTX_set_info_callback(self._context, self._info_callback)
@@ -762,7 +778,7 @@
:param options: The options to add.
:return: The new option bitmask.
"""
- if not isinstance(options, int):
+ if not isinstance(options, integer_types):
raise TypeError("options must be an integer")
return _lib.SSL_CTX_set_options(self._context, options)
@@ -775,7 +791,7 @@
:param mode: The mode to add.
:return: The new mode bitmask.
"""
- if not isinstance(mode, int):
+ if not isinstance(mode, integer_types):
raise TypeError("mode must be an integer")
return _lib.SSL_CTX_set_mode(self._context, mode)
@@ -870,8 +886,11 @@
elif error == _lib.SSL_ERROR_SYSCALL:
if _lib.ERR_peek_error() == 0:
if result < 0:
- raise SysCallError(
- _ffi.errno, errorcode[_ffi.errno])
+ if platform == "win32":
+ errno = _ffi.getwinerror()[0]
+ else:
+ errno = _ffi.errno
+ raise SysCallError(errno, errorcode[errno])
else:
raise SysCallError(-1, "Unexpected EOF")
else:
@@ -957,8 +976,6 @@
buf = buf.tobytes()
if not isinstance(buf, bytes):
raise TypeError("data must be a byte string")
- if not isinstance(flags, int):
- raise TypeError("flags must be an integer")
result = _lib.SSL_write(self._ssl, buf, len(buf))
self._raise_ssl_error(self._ssl, result)
@@ -981,8 +998,6 @@
buf = buf.tobytes()
if not isinstance(buf, bytes):
raise TypeError("buf must be a byte string")
- if not isinstance(flags, int):
- raise TypeError("flags must be an integer")
left_to_send = len(buf)
total_sent = 0
@@ -1043,7 +1058,7 @@
if self._from_ssl is None:
raise TypeError("Connection sock was not None")
- if not isinstance(bufsiz, int):
+ if not isinstance(bufsiz, integer_types):
raise TypeError("bufsiz must be an integer")
buf = _ffi.new("char[]", bufsiz)
@@ -1266,7 +1281,7 @@
:param state - bitvector of SENT_SHUTDOWN, RECEIVED_SHUTDOWN.
:return: None
"""
- if not isinstance(state, int):
+ if not isinstance(state, integer_types):
raise TypeError("state must be an integer")
_lib.SSL_set_shutdown(self._ssl, state)
@@ -1433,3 +1448,7 @@
_raise_current_error()
ConnectionType = Connection
+
+# This is similar to the initialization calls at the end of OpenSSL/crypto.py
+# but is exercised mostly by the Context initializer.
+_lib.SSL_library_init()
diff --git a/OpenSSL/__init__.py b/OpenSSL/__init__.py
index 396a97f..db96e1f 100644
--- a/OpenSSL/__init__.py
+++ b/OpenSSL/__init__.py
@@ -10,6 +10,3 @@
__all__ = [
'rand', 'crypto', 'SSL', 'tsafe', '__version__']
-
-
-# ERR_load_crypto_strings, SSL_library_init, ERR_load_SSL_strings
diff --git a/OpenSSL/_util.py b/OpenSSL/_util.py
index 7c606b9..baeecc6 100644
--- a/OpenSSL/_util.py
+++ b/OpenSSL/_util.py
@@ -6,15 +6,18 @@
lib = binding.lib
def exception_from_error_queue(exceptionType):
+ def text(charp):
+ return native(ffi.string(charp))
+
errors = []
while True:
error = lib.ERR_get_error()
if error == 0:
break
errors.append((
- ffi.string(lib.ERR_lib_error_string(error)),
- ffi.string(lib.ERR_func_error_string(error)),
- ffi.string(lib.ERR_reason_error_string(error))))
+ text(lib.ERR_lib_error_string(error)),
+ text(lib.ERR_func_error_string(error)),
+ text(lib.ERR_reason_error_string(error))))
raise exceptionType(errors)
diff --git a/OpenSSL/crypto.py b/OpenSSL/crypto.py
index 0a9e8a2..d0026bd 100644
--- a/OpenSSL/crypto.py
+++ b/OpenSSL/crypto.py
@@ -1202,6 +1202,9 @@
:return: The X509 object
"""
+ if isinstance(buffer, _text_type):
+ buffer = buffer.encode("ascii")
+
bio = _new_mem_buf(buffer)
if type == FILETYPE_PEM:
@@ -1988,6 +1991,9 @@
:return: The PKey object
"""
+ if isinstance(buffer, _text_type):
+ buffer = buffer.encode("ascii")
+
bio = _new_mem_buf(buffer)
helper = _PassphraseHelper(type, passphrase)
@@ -2044,6 +2050,9 @@
:param buffer: The buffer the certificate request is stored in
:return: The X509Req object
"""
+ if isinstance(buffer, _text_type):
+ buffer = buffer.encode("ascii")
+
bio = _new_mem_buf(buffer)
if type == FILETYPE_PEM:
@@ -2137,6 +2146,9 @@
:return: The PKey object
"""
+ if isinstance(buffer, _text_type):
+ buffer = buffer.encode("ascii")
+
bio = _new_mem_buf(buffer)
if type == FILETYPE_PEM:
@@ -2163,6 +2175,9 @@
:param buffer: The buffer with the pkcs7 data.
:return: The PKCS7 object
"""
+ if isinstance(buffer, _text_type):
+ buffer = buffer.encode("ascii")
+
bio = _new_mem_buf(buffer)
if type == FILETYPE_PEM:
@@ -2191,6 +2206,9 @@
:param passphrase: (Optional) The password to decrypt the PKCS12 lump
:returns: The PKCS12 object
"""
+ if isinstance(buffer, _text_type):
+ buffer = buffer.encode("ascii")
+
bio = _new_mem_buf(buffer)
p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
@@ -2290,3 +2308,7 @@
# OpenSSL library (and is linked against the same one that cryptography is
# using)).
_lib.OpenSSL_add_all_algorithms()
+
+# This is similar but exercised mainly by exception_from_error_queue. It calls
+# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
+_lib.SSL_load_error_strings()
diff --git a/OpenSSL/test/test_crypto.py b/OpenSSL/test/test_crypto.py
index 51da99b..4e42f70 100644
--- a/OpenSSL/test/test_crypto.py
+++ b/OpenSSL/test/test_crypto.py
@@ -1941,7 +1941,7 @@
"""
passwd = 'whatever'
e = self.assertRaises(Error, load_pkcs12, b'fruit loops', passwd)
- self.assertEqual( e.args[0][0][0], b'asn1 encoding routines')
+ self.assertEqual( e.args[0][0][0], 'asn1 encoding routines')
self.assertEqual( len(e.args[0][0]), 3)
diff --git a/OpenSSL/test/test_ssl.py b/OpenSSL/test/test_ssl.py
index 5e08e9b..70c1f3e 100644
--- a/OpenSSL/test/test_ssl.py
+++ b/OpenSSL/test/test_ssl.py
@@ -6,7 +6,7 @@
"""
from gc import collect, get_referrers
-from errno import ECONNREFUSED, EINPROGRESS, EWOULDBLOCK, EPIPE
+from errno import ECONNREFUSED, EINPROGRESS, EWOULDBLOCK, EPIPE, ESHUTDOWN
from sys import platform, version_info
from socket import SHUT_RDWR, error, socket
from os import makedirs
@@ -14,6 +14,8 @@
from unittest import main
from weakref import ref
+from six import PY3, u
+
from OpenSSL.crypto import TYPE_RSA, FILETYPE_PEM
from OpenSSL.crypto import PKey, X509, X509Extension, X509Store
from OpenSSL.crypto import dump_privatekey, load_privatekey
@@ -336,6 +338,16 @@
self.assertRaises(ValueError, Context, 10)
+ if not PY3:
+ def test_method_long(self):
+ """
+ On Python 2 :py:class:`Context` accepts values of type
+ :py:obj:`long` as well as :py:obj:`int`.
+ """
+ Context(long(TLSv1_METHOD))
+
+
+
def test_type(self):
"""
:py:obj:`Context` and :py:obj:`ContextType` refer to the same type object and can be
@@ -365,6 +377,25 @@
self.assertRaises(Error, ctx.use_privatekey_file, self.mktemp())
+ if not PY3:
+ def test_use_privatekey_file_long(self):
+ """
+ On Python 2 :py:obj:`Context.use_privatekey_file` accepts a
+ filetype of type :py:obj:`long` as well as :py:obj:`int`.
+ """
+ pemfile = self.mktemp()
+
+ key = PKey()
+ key.generate_key(TYPE_RSA, 128)
+
+ with open(pemfile, "wt") as pem:
+ pem.write(
+ dump_privatekey(FILETYPE_PEM, key).decode("ascii"))
+
+ ctx = Context(TLSv1_METHOD)
+ ctx.use_privatekey_file(pemfile, long(FILETYPE_PEM))
+
+
def test_use_certificate_wrong_args(self):
"""
:py:obj:`Context.use_certificate_wrong_args` raises :py:obj:`TypeError`
@@ -444,6 +475,20 @@
ctx.use_certificate_file(pem_filename)
+ if not PY3:
+ def test_use_certificate_file_long(self):
+ """
+ On Python 2 :py:obj:`Context.use_certificate_file` accepts a
+ filetype of type :py:obj:`long` as well as :py:obj:`int`.
+ """
+ pem_filename = self.mktemp()
+ with open(pem_filename, "wb") as pem_file:
+ pem_file.write(cleartextCertificatePEM)
+
+ ctx = Context(TLSv1_METHOD)
+ ctx.use_certificate_file(pem_filename, long(FILETYPE_PEM))
+
+
def test_set_app_data_wrong_args(self):
"""
:py:obj:`Context.set_app_data` raises :py:obj:`TypeError` if called with other than
@@ -485,6 +530,26 @@
self.assertRaises(TypeError, context.set_options, 1, None)
+ def test_set_options(self):
+ """
+ :py:obj:`Context.set_options` returns the new options value.
+ """
+ context = Context(TLSv1_METHOD)
+ options = context.set_options(OP_NO_SSLv2)
+ self.assertTrue(OP_NO_SSLv2 & options)
+
+
+ if not PY3:
+ def test_set_options_long(self):
+ """
+ On Python 2 :py:obj:`Context.set_options` accepts values of type
+ :py:obj:`long` as well as :py:obj:`int`.
+ """
+ context = Context(TLSv1_METHOD)
+ options = context.set_options(long(OP_NO_SSLv2))
+ self.assertTrue(OP_NO_SSLv2 & options)
+
+
def test_set_mode_wrong_args(self):
"""
:py:obj:`Context.set`mode} raises :py:obj:`TypeError` if called with the wrong
@@ -505,6 +570,16 @@
context = Context(TLSv1_METHOD)
self.assertTrue(
MODE_RELEASE_BUFFERS & context.set_mode(MODE_RELEASE_BUFFERS))
+
+ if not PY3:
+ def test_set_mode_long(self):
+ """
+ On Python 2 :py:obj:`Context.set_mode` accepts values of type
+ :py:obj:`long` as well as :py:obj:`int`.
+ """
+ context = Context(TLSv1_METHOD)
+ mode = context.set_mode(long(MODE_RELEASE_BUFFERS))
+ self.assertTrue(MODE_RELEASE_BUFFERS & mode)
else:
"MODE_RELEASE_BUFFERS unavailable - OpenSSL version may be too old"
@@ -539,6 +614,17 @@
self.assertEquals(context.get_timeout(), 1234)
+ if not PY3:
+ def test_timeout_long(self):
+ """
+ On Python 2 :py:obj:`Context.set_timeout` accepts values of type
+ `long` as well as int.
+ """
+ context = Context(TLSv1_METHOD)
+ context.set_timeout(long(1234))
+ self.assertEquals(context.get_timeout(), 1234)
+
+
def test_set_verify_depth_wrong_args(self):
"""
:py:obj:`Context.set_verify_depth` raises :py:obj:`TypeError` if called with the wrong
@@ -569,6 +655,17 @@
self.assertEquals(context.get_verify_depth(), 11)
+ if not PY3:
+ def test_verify_depth_long(self):
+ """
+ On Python 2 :py:obj:`Context.set_verify_depth` accepts values of
+ type `long` as well as int.
+ """
+ context = Context(TLSv1_METHOD)
+ context.set_verify_depth(long(11))
+ self.assertEquals(context.get_verify_depth(), 11)
+
+
def _write_encrypted_pem(self, passphrase):
"""
Write a new private key out to a new file, encrypted using the given
@@ -699,15 +796,19 @@
serverSSL = Connection(context, server)
serverSSL.set_accept_state()
- while not called:
- for ssl in clientSSL, serverSSL:
- try:
- ssl.do_handshake()
- except WantReadError:
- pass
+ handshake(clientSSL, serverSSL)
- # Kind of lame. Just make sure it got called somehow.
- self.assertTrue(called)
+ # The callback must always be called with a Connection instance as the
+ # first argument. It would probably be better to split this into
+ # separate tests for client and server side info callbacks so we could
+ # assert it is called with the right Connection instance. It would
+ # also be good to assert *something* about `where` and `ret`.
+ notConnections = [
+ conn for (conn, where, ret) in called
+ if not isinstance(conn, Connection)]
+ self.assertEqual(
+ [], notConnections,
+ "Some info callback arguments were not Connection instaces.")
def _load_verify_locations_test(self, *args):
@@ -961,11 +1062,11 @@
# Write out the chain file.
chainFile = self.mktemp()
- fObj = open(chainFile, 'w')
+ fObj = open(chainFile, 'wb')
# Most specific to least general.
- fObj.write(dump_certificate(FILETYPE_PEM, scert).decode('ascii'))
- fObj.write(dump_certificate(FILETYPE_PEM, icert).decode('ascii'))
- fObj.write(dump_certificate(FILETYPE_PEM, cacert).decode('ascii'))
+ fObj.write(dump_certificate(FILETYPE_PEM, scert))
+ fObj.write(dump_certificate(FILETYPE_PEM, icert))
+ fObj.write(dump_certificate(FILETYPE_PEM, cacert))
fObj.close()
serverContext = Context(TLSv1_METHOD)
@@ -1011,7 +1112,7 @@
self.assertRaises(TypeError, context.get_verify_mode, None)
- def test_get_verify_mode(self):
+ def test_set_verify_mode(self):
"""
:py:obj:`Context.get_verify_mode` returns the verify mode flags previously
passed to :py:obj:`Context.set_verify`.
@@ -1024,6 +1125,20 @@
context.get_verify_mode(), VERIFY_PEER | VERIFY_CLIENT_ONCE)
+ if not PY3:
+ def test_set_verify_mode_long(self):
+ """
+ On Python 2 :py:obj:`Context.set_verify_mode` accepts values of
+ type :py:obj:`long` as well as :py:obj:`int`.
+ """
+ context = Context(TLSv1_METHOD)
+ self.assertEquals(context.get_verify_mode(), 0)
+ context.set_verify(
+ long(VERIFY_PEER | VERIFY_CLIENT_ONCE), lambda *args: None)
+ self.assertEquals(
+ context.get_verify_mode(), VERIFY_PEER | VERIFY_CLIENT_ONCE)
+
+
def test_load_tmp_dh_wrong_args(self):
"""
:py:obj:`Context.load_tmp_dh` raises :py:obj:`TypeError` if called with the wrong
@@ -1069,10 +1184,11 @@
# XXX What should I assert here? -alex
- def test_set_cipher_list(self):
+ def test_set_cipher_list_bytes(self):
"""
- :py:obj:`Context.set_cipher_list` accepts a :py:obj:`str` naming the ciphers which
- connections created with the context object will be able to choose from.
+ :py:obj:`Context.set_cipher_list` accepts a :py:obj:`bytes` naming the
+ ciphers which connections created with the context object will be able
+ to choose from.
"""
context = Context(TLSv1_METHOD)
context.set_cipher_list(b"hello world:EXP-RC4-MD5")
@@ -1080,11 +1196,23 @@
self.assertEquals(conn.get_cipher_list(), ["EXP-RC4-MD5"])
+ def test_set_cipher_list_text(self):
+ """
+ :py:obj:`Context.set_cipher_list` accepts a :py:obj:`unicode` naming
+ the ciphers which connections created with the context object will be
+ able to choose from.
+ """
+ context = Context(TLSv1_METHOD)
+ context.set_cipher_list(u("hello world:EXP-RC4-MD5"))
+ conn = Connection(context, None)
+ self.assertEquals(conn.get_cipher_list(), ["EXP-RC4-MD5"])
+
+
def test_set_cipher_list_wrong_args(self):
"""
- :py:obj:`Context.set_cipher_list` raises :py:obj:`TypeError` when passed
- zero arguments or more than one argument or when passed a non-byte
- string single argument and raises :py:obj:`OpenSSL.SSL.Error` when
+ :py:obj:`Context.set_cipher_list` raises :py:obj:`TypeError` when
+ passed zero arguments or more than one argument or when passed a
+ non-string single argument and raises :py:obj:`OpenSSL.SSL.Error` when
passed an incorrect cipher list string.
"""
context = Context(TLSv1_METHOD)
@@ -1092,13 +1220,13 @@
self.assertRaises(TypeError, context.set_cipher_list, object())
self.assertRaises(TypeError, context.set_cipher_list, b"EXP-RC4-MD5", object())
- self.assertRaises(Error, context.set_cipher_list, b"imaginary-cipher")
+ self.assertRaises(Error, context.set_cipher_list, "imaginary-cipher")
def test_set_session_cache_mode_wrong_args(self):
"""
- L{Context.set_session_cache_mode} raises L{TypeError} if called with
- other than one integer argument.
+ :py:obj:`Context.set_session_cache_mode` raises :py:obj:`TypeError` if
+ called with other than one integer argument.
"""
context = Context(TLSv1_METHOD)
self.assertRaises(TypeError, context.set_session_cache_mode)
@@ -1107,8 +1235,8 @@
def test_get_session_cache_mode_wrong_args(self):
"""
- L{Context.get_session_cache_mode} raises L{TypeError} if called with any
- arguments.
+ :py:obj:`Context.get_session_cache_mode` raises :py:obj:`TypeError` if
+ called with any arguments.
"""
context = Context(TLSv1_METHOD)
self.assertRaises(TypeError, context.get_session_cache_mode, 1)
@@ -1116,15 +1244,27 @@
def test_session_cache_mode(self):
"""
- L{Context.set_session_cache_mode} specifies how sessions are cached.
- The setting can be retrieved via L{Context.get_session_cache_mode}.
+ :py:obj:`Context.set_session_cache_mode` specifies how sessions are
+ cached. The setting can be retrieved via
+ :py:obj:`Context.get_session_cache_mode`.
"""
context = Context(TLSv1_METHOD)
- old = context.set_session_cache_mode(SESS_CACHE_OFF)
+ context.set_session_cache_mode(SESS_CACHE_OFF)
off = context.set_session_cache_mode(SESS_CACHE_BOTH)
self.assertEqual(SESS_CACHE_OFF, off)
self.assertEqual(SESS_CACHE_BOTH, context.get_session_cache_mode())
+ if not PY3:
+ def test_session_cache_mode_long(self):
+ """
+ On Python 2 :py:obj:`Context.set_session_cache_mode` accepts values
+ of type :py:obj:`long` as well as :py:obj:`int`.
+ """
+ context = Context(TLSv1_METHOD)
+ context.set_session_cache_mode(long(SESS_CACHE_BOTH))
+ self.assertEqual(
+ SESS_CACHE_BOTH, context.get_session_cache_mode())
+
def test_get_cert_store(self):
"""
@@ -1532,6 +1672,17 @@
self.assertEquals(connection.get_shutdown(), RECEIVED_SHUTDOWN)
+ if not PY3:
+ def test_set_shutdown_long(self):
+ """
+ On Python 2 :py:obj:`Connection.set_shutdown` accepts an argument
+ of type :py:obj:`long` as well as :py:obj:`int`.
+ """
+ connection = Connection(Context(TLSv1_METHOD), socket())
+ connection.set_shutdown(long(RECEIVED_SHUTDOWN))
+ self.assertEquals(connection.get_shutdown(), RECEIVED_SHUTDOWN)
+
+
def test_app_data_wrong_args(self):
"""
:py:obj:`Connection.set_app_data` raises :py:obj:`TypeError` if called with other than
@@ -1827,13 +1978,14 @@
"""
def test_wrong_args(self):
"""
- When called with arguments other than a single string,
- :py:obj:`Connection.send` raises :py:obj:`TypeError`.
+ When called with arguments other than string argument for its first
+ parameter or more than two arguments, :py:obj:`Connection.send` raises
+ :py:obj:`TypeError`.
"""
connection = Connection(Context(TLSv1_METHOD), None)
self.assertRaises(TypeError, connection.send)
self.assertRaises(TypeError, connection.send, object())
- self.assertRaises(TypeError, connection.send, "foo", "bar")
+ self.assertRaises(TypeError, connection.send, "foo", object(), "bar")
def test_short_bytes(self):
@@ -1870,13 +2022,15 @@
"""
def test_wrong_args(self):
"""
- When called with arguments other than a single string,
- :py:obj:`Connection.sendall` raises :py:obj:`TypeError`.
+ When called with arguments other than a string argument for its first
+ parameter or with more than two arguments, :py:obj:`Connection.sendall`
+ raises :py:obj:`TypeError`.
"""
connection = Connection(Context(TLSv1_METHOD), None)
self.assertRaises(TypeError, connection.sendall)
self.assertRaises(TypeError, connection.sendall, object())
- self.assertRaises(TypeError, connection.sendall, "foo", "bar")
+ self.assertRaises(
+ TypeError, connection.sendall, "foo", object(), "bar")
def test_short(self):
@@ -1932,7 +2086,10 @@
server, client = self._loopback()
server.sock_shutdown(2)
exc = self.assertRaises(SysCallError, server.sendall, b"hello, world")
- self.assertEqual(exc.args[0], EPIPE)
+ if platform == "win32":
+ self.assertEqual(exc.args[0], ESHUTDOWN)
+ else:
+ self.assertEqual(exc.args[0], EPIPE)
@@ -2057,66 +2214,68 @@
def test_sess_cache_off(self):
"""
- The value of L{OpenSSL.SSL.SESS_CACHE_OFF} 0x0, the value of
- L{SSL_SESS_CACHE_OFF} defined by I{openssl/ssl.h}.
+ The value of :py:obj:`OpenSSL.SSL.SESS_CACHE_OFF` 0x0, the value of
+ :py:obj:`SSL_SESS_CACHE_OFF` defined by ``openssl/ssl.h``.
"""
self.assertEqual(0x0, SESS_CACHE_OFF)
def test_sess_cache_client(self):
"""
- The value of L{OpenSSL.SSL.SESS_CACHE_CLIENT} 0x1, the value of
- L{SSL_SESS_CACHE_CLIENT} defined by I{openssl/ssl.h}.
+ The value of :py:obj:`OpenSSL.SSL.SESS_CACHE_CLIENT` 0x1, the value of
+ :py:obj:`SSL_SESS_CACHE_CLIENT` defined by ``openssl/ssl.h``.
"""
self.assertEqual(0x1, SESS_CACHE_CLIENT)
def test_sess_cache_server(self):
"""
- The value of L{OpenSSL.SSL.SESS_CACHE_SERVER} 0x2, the value of
- L{SSL_SESS_CACHE_SERVER} defined by I{openssl/ssl.h}.
+ The value of :py:obj:`OpenSSL.SSL.SESS_CACHE_SERVER` 0x2, the value of
+ :py:obj:`SSL_SESS_CACHE_SERVER` defined by ``openssl/ssl.h``.
"""
self.assertEqual(0x2, SESS_CACHE_SERVER)
def test_sess_cache_both(self):
"""
- The value of L{OpenSSL.SSL.SESS_CACHE_BOTH} 0x3, the value of
- L{SSL_SESS_CACHE_BOTH} defined by I{openssl/ssl.h}.
+ The value of :py:obj:`OpenSSL.SSL.SESS_CACHE_BOTH` 0x3, the value of
+ :py:obj:`SSL_SESS_CACHE_BOTH` defined by ``openssl/ssl.h``.
"""
self.assertEqual(0x3, SESS_CACHE_BOTH)
def test_sess_cache_no_auto_clear(self):
"""
- The value of L{OpenSSL.SSL.SESS_CACHE_NO_AUTO_CLEAR} 0x80, the value of
- L{SSL_SESS_CACHE_NO_AUTO_CLEAR} defined by I{openssl/ssl.h}.
+ The value of :py:obj:`OpenSSL.SSL.SESS_CACHE_NO_AUTO_CLEAR` 0x80, the
+ value of :py:obj:`SSL_SESS_CACHE_NO_AUTO_CLEAR` defined by
+ ``openssl/ssl.h``.
"""
self.assertEqual(0x80, SESS_CACHE_NO_AUTO_CLEAR)
def test_sess_cache_no_internal_lookup(self):
"""
- The value of L{OpenSSL.SSL.SESS_CACHE_NO_INTERNAL_LOOKUP} 0x100, the
- value of L{SSL_SESS_CACHE_NO_INTERNAL_LOOKUP} defined by
- I{openssl/ssl.h}.
+ The value of :py:obj:`OpenSSL.SSL.SESS_CACHE_NO_INTERNAL_LOOKUP` 0x100,
+ the value of :py:obj:`SSL_SESS_CACHE_NO_INTERNAL_LOOKUP` defined by
+ ``openssl/ssl.h``.
"""
self.assertEqual(0x100, SESS_CACHE_NO_INTERNAL_LOOKUP)
def test_sess_cache_no_internal_store(self):
"""
- The value of L{OpenSSL.SSL.SESS_CACHE_NO_INTERNAL_STORE} 0x200, the
- value of L{SSL_SESS_CACHE_NO_INTERNAL_STORE} defined by
- I{openssl/ssl.h}.
+ The value of :py:obj:`OpenSSL.SSL.SESS_CACHE_NO_INTERNAL_STORE` 0x200,
+ the value of :py:obj:`SSL_SESS_CACHE_NO_INTERNAL_STORE` defined by
+ ``openssl/ssl.h``.
"""
self.assertEqual(0x200, SESS_CACHE_NO_INTERNAL_STORE)
def test_sess_cache_no_internal(self):
"""
- The value of L{OpenSSL.SSL.SESS_CACHE_NO_INTERNAL} 0x300, the value of
- L{SSL_SESS_CACHE_NO_INTERNAL} defined by I{openssl/ssl.h}.
+ The value of :py:obj:`OpenSSL.SSL.SESS_CACHE_NO_INTERNAL` 0x300, the
+ value of :py:obj:`SSL_SESS_CACHE_NO_INTERNAL` defined by
+ ``openssl/ssl.h``.
"""
self.assertEqual(0x300, SESS_CACHE_NO_INTERNAL)
@@ -2527,6 +2686,40 @@
self.assertRaises(WantReadError, conn.bio_read, 1024)
+ def test_buffer_size(self):
+ """
+ :py:obj:`Connection.bio_read` accepts an integer giving the maximum
+ number of bytes to read and return.
+ """
+ ctx = Context(TLSv1_METHOD)
+ conn = Connection(ctx, None)
+ conn.set_connect_state()
+ try:
+ conn.do_handshake()
+ except WantReadError:
+ pass
+ data = conn.bio_read(2)
+ self.assertEqual(2, len(data))
+
+
+ if not PY3:
+ def test_buffer_size_long(self):
+ """
+ On Python 2 :py:obj:`Connection.bio_read` accepts values of type
+ :py:obj:`long` as well as :py:obj:`int`.
+ """
+ ctx = Context(TLSv1_METHOD)
+ conn = Connection(ctx, None)
+ conn.set_connect_state()
+ try:
+ conn.do_handshake()
+ except WantReadError:
+ pass
+ data = conn.bio_read(long(2))
+ self.assertEqual(2, len(data))
+
+
+
class InfoConstantTests(TestCase):
"""
diff --git a/OpenSSL/test/util.py b/OpenSSL/test/util.py
index 011e7da..4e4d812 100644
--- a/OpenSSL/test/util.py
+++ b/OpenSSL/test/util.py
@@ -17,7 +17,11 @@
from OpenSSL._util import exception_from_error_queue
from OpenSSL.crypto import Error
-import memdbg
+try:
+ import memdbg
+except Exception:
+ class _memdbg(object): heap = None
+ memdbg = _memdbg()
from OpenSSL._util import ffi, lib, byte_string as b
diff --git a/README b/README
index afe4ddc..1b2a093 100644
--- a/README
+++ b/README
@@ -3,3 +3,7 @@
------------------------------------------------------------------------------
See the file INSTALL for installation instructions.
+
+See http://github.com/pyca/pyopenssl for development.
+
+See https://mail.python.org/mailman/listinfo/pyopenssl-users for the discussion mailing list.
diff --git a/doc/index.rst b/doc/index.rst
index a63df87..e4a5a23 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -14,7 +14,6 @@
:maxdepth: 3
introduction
- install
api
internals
diff --git a/doc/install.rst b/doc/install.rst
deleted file mode 100644
index f525726..0000000
--- a/doc/install.rst
+++ /dev/null
@@ -1,69 +0,0 @@
-.. _building:
-
-Building and Installing
-=======================
-
-These instructions can also be found in the file ``INSTALL``.
-
-I have tested this on Debian Linux systems (woody and sid), Solaris 2.6 and
-2.7. Others have successfully compiled it on Windows and NT.
-
-.. _building-unix:
-
-Building the Module on a Unix System
-------------------------------------
-
-pyOpenSSL uses distutils, so there really shouldn't be any problems. To build
-the library::
-
- python setup.py build
-
-If your OpenSSL header files aren't in ``/usr/include``, you may need to supply
-the ``-I`` flag to let the setup script know where to look. The same goes for
-the libraries of course, use the ``-L`` flag. Note that ``build`` won't accept
-these flags, so you have to run first ``build_ext`` and then ``build``!
-Example::
-
- python setup.py build_ext -I/usr/local/ssl/include -L/usr/local/ssl/lib
- python setup.py build
-
-Now you should have a directory called ``OpenSSL`` that contains e.g.
-``SSL.so`` and ``__init__.py`` somewhere in the build dicrectory,
-so just::
-
- python setup.py install
-
-If you, for some arcane reason, don't want the module to appear in the
-``site-packages`` directory, use the ``--prefix`` option.
-
-You can, of course, do::
-
- python setup.py --help
-
-to find out more about how to use the script.
-
-.. _building-windows:
-
-Building the Module on a Windows System
----------------------------------------
-
-Big thanks to Itamar Shtull-Trauring and Oleg Orlov for their help with
-Windows build instructions. Same as for Unix systems, we have to separate
-the ``build_ext`` and the ``build``.
-
-Building the library::
-
- setup.py build_ext -I ...\openssl\inc32 -L ...\openssl\out32dll
- setup.py build
-
-Where ``...\openssl`` is of course the location of your OpenSSL installation.
-
-Installation is the same as for Unix systems::
-
- setup.py install
-
-And similarily, you can do::
-
- setup.py --help
-
-to get more information.
diff --git a/doc/internals.rst b/doc/internals.rst
index 839c446..a2a4cdc 100644
--- a/doc/internals.rst
+++ b/doc/internals.rst
@@ -27,38 +27,10 @@
Callbacks
---------
-There are a number of problems with callbacks. First of all, OpenSSL is written
-as a C library, it's not meant to have Python callbacks, so a way around that
-is needed. Another problem is thread support. A lot of the OpenSSL I/O
-functions can block if the socket is in blocking mode, and then you want other
-Python threads to be able to do other things. The real trouble is if you've
-released the global CPython interpreter lock to do a potentially blocking
-operation, and the operation calls a callback. Then we must take the GIL back,
-since calling Python APIs without holding it is not allowed.
-
-There are two solutions to the first problem, both of which are necessary. The
-first solution to use is if the C callback allows ''userdata'' to be passed to
-it (an arbitrary pointer normally). This is great! We can set our Python
-function object as the real userdata and emulate userdata for the Python
-function in another way. The other solution can be used if an object with an
-''app_data'' system always is passed to the callback. For example, the SSL
-object in OpenSSL has app_data functions and in e.g. the verification
-callbacks, you can retrieve the related SSL object. What we do is to set our
-wrapper :py:class:`.Connection` object as app_data for the SSL object, and we can
-easily find the Python callback.
-
-The other problem is solved using thread local variables. Whenever the GIL is
-released before calling into an OpenSSL API, the PyThreadState pointer returned
-by :c:func:`PyEval_SaveState` is stored in a global thread local variable
-(using Python's own TLS API, :c:func:`PyThread_set_key_value`). When it is
-necessary to re-acquire the GIL, either after the OpenSSL API returns or in a C
-callback invoked by that OpenSSL API, the value of the thread local variable is
-retrieved (:c:func:`PyThread_get_key_value`) and used to re-acquire the GIL.
-This allows Python threads to execute while OpenSSL APIs are running and allows
-use of any particular pyOpenSSL object from any Python thread, since there is
-no per-thread state associated with any of these objects and since OpenSSL is
-threadsafe (as long as properly initialized, as pyOpenSSL initializes it).
-
+Callbacks were more of a problem when pyOpenSSL was written in C.
+Having switched to being written in Python using cffi, callbacks are now straightforward.
+The problems that originally existed no longer do
+(if you are interested in the details you can find descriptions of those problems in the version control history for this document).
.. _socket-methods:
diff --git a/runtests.py b/runtests.py
index 2ec425b..13f5c4c 100644
--- a/runtests.py
+++ b/runtests.py
@@ -2,7 +2,10 @@
sys.modules['ssl'] = None
sys.modules['_hashlib'] = None
-import memdbg
+try:
+ import memdbg
+except Exception as e:
+ pass
from twisted.scripts.trial import run
run()