Skip to content
11 changes: 9 additions & 2 deletions examples/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ def main():

# DTLS connection over UDP
if args.u:
if args.v > 2:
Comment thread
dgarske marked this conversation as resolved.
args.v = 1
bind_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
context = wolfssl.SSLContext(get_DTLSmethod(args.v))
# SSL/TLS connection over TCP
Expand All @@ -138,7 +140,10 @@ def main():
context = wolfssl.SSLContext(get_SSLmethod(args.v))

# enable debug, if native wolfSSL has been compiled with '--enable-debug'
wolfssl.WolfSSL.enable_debug()
try:
wolfssl.WolfSSL.enable_debug()
except RuntimeError:
pass

context.load_cert_chain(args.c, args.k)

Expand All @@ -151,6 +156,7 @@ def main():
if args.l:
context.set_ciphers(args.l)

secure_socket = None
try:
secure_socket = context.wrap_socket(bind_socket)

Expand All @@ -171,7 +177,8 @@ def main():
print()

finally:
secure_socket.close()
if secure_socket:
secure_socket.close()


if __name__ == '__main__':
Expand Down
15 changes: 11 additions & 4 deletions examples/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ def main():
args = build_arg_parser().parse_args()
# DTLS connection over UDP
if args.u:
# Set DTLSv1.2 as default if unspecified
if args.v == 5:
# Set DTLSv1.2 as default if unspecified
if args.v > 2:
args.v = 1
bind_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
bind_socket.bind(("" if args.b else "localhost", args.p))
Expand All @@ -136,7 +136,10 @@ def main():
print("Server listening on port", bind_socket.getsockname()[1])

# enable debug, if native wolfSSL has been compiled with '--enable-debug'
wolfssl.WolfSSL.enable_debug()
try:
wolfssl.WolfSSL.enable_debug()
except RuntimeError:
pass

context.load_cert_chain(args.c, args.k)

Expand Down Expand Up @@ -170,7 +173,11 @@ def main():
finally:
if secure_socket:
secure_socket.shutdown(socket.SHUT_RDWR)
secure_socket.close()
# Don't close for DTLS - secure_socket wraps the
# shared bind_socket which is needed for
# subsequent connections
if not args.u:
Comment thread
dgarske marked this conversation as resolved.
secure_socket.close()

if not args.i:
break
Expand Down
41 changes: 41 additions & 0 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,47 @@ def test_get_version(ssl_server, ssl_version, tcp_socket):
secure_socket.read(1024)


def test_close_after_connected(ssl_server, tcp_socket):
ctx = wolfssl.SSLContext(wolfssl.PROTOCOL_TLSv1_2)
sock = ctx.wrap_socket(tcp_socket)
sock.connect(('127.0.0.1', ssl_server.port))
sock.write(b'hello wolfssl')
sock.read(1024)
sock.close()


def test_recv_into_nbytes_zero(ssl_server, tcp_socket):
ctx = wolfssl.SSLContext(wolfssl.PROTOCOL_TLSv1_2)
sock = ctx.wrap_socket(tcp_socket)
sock.connect(('127.0.0.1', ssl_server.port))
sock.write(b'hello wolfssl')
buf = bytearray(1024)
n = sock.recv_into(buf, 0)
assert n > 0
sock.close()


def test_unwrap_returns_socket(ssl_server, tcp_socket):
import socket as _socket
ctx = wolfssl.SSLContext(wolfssl.PROTOCOL_TLSv1_2)
sock = ctx.wrap_socket(tcp_socket)
sock.connect(('127.0.0.1', ssl_server.port))
sock.write(b'hello wolfssl')
sock.read(1024)
raw = sock.unwrap()
assert isinstance(raw, _socket.socket)
raw.close()


def test_sendall_large_buffer(ssl_server, tcp_socket):
ctx = wolfssl.SSLContext(wolfssl.PROTOCOL_TLSv1_2)
sock = ctx.wrap_socket(tcp_socket)
sock.connect(('127.0.0.1', ssl_server.port))
sock.sendall(b'x' * 8192)
sock.read(1024)
sock.close()


def test_client_cert_verification_failure():
"""
Test that a connection fails when the server requires client certificates
Expand Down
32 changes: 32 additions & 0 deletions tests/test_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,35 @@ def test_load_verify_locations_with_cafile(ssl_context):

def test_load_verify_locations_with_cadata(ssl_context):
ssl_context.load_verify_locations(cadata=_CADATA)


def test_check_hostname_requires_cert_required(ssl_provider, ssl_context):
Comment thread
dgarske marked this conversation as resolved.
with pytest.raises(ValueError):
ssl_context.check_hostname = True

ssl_context.verify_mode = ssl_provider.CERT_REQUIRED
ssl_context.check_hostname = True
assert ssl_context.check_hostname is True


def test_wrap_socket_server_side_mismatch(ssl_context, tcp_socket):
with pytest.raises(ValueError):
ssl_context.wrap_socket(tcp_socket, server_side=True)


def test_close_without_handshake(ssl_context, tcp_socket):
sock = ssl_context.wrap_socket(tcp_socket)
sock.close()


def test_close_releases_native_object(ssl_context, tcp_socket):
sock = ssl_context.wrap_socket(tcp_socket)
sock.close()
sock.close()


def test_operations_after_close_raise(ssl_context, tcp_socket):
sock = ssl_context.wrap_socket(tcp_socket)
sock.close()
with pytest.raises(ValueError):
sock.read()
111 changes: 68 additions & 43 deletions wolfssl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ class WolfSSL(object):

@classmethod
def enable_debug(self):
_lib.wolfSSL_Debugging_ON()
if _lib.wolfSSL_Debugging_ON() != _SSL_SUCCESS:
Comment thread
dgarske marked this conversation as resolved.
raise RuntimeError(
"wolfSSL debugging not available")

@classmethod
def disable_debug(self):
Expand Down Expand Up @@ -143,9 +145,7 @@ def get_der(self):
if derPtr == _ffi.NULL:
return None

derBytes = _ffi.buffer(derPtr, outSz[0])

return derBytes
return _ffi.buffer(derPtr, outSz[0])[:]

class SSLContext(object):
"""
Expand All @@ -154,7 +154,9 @@ class SSLContext(object):
"""

def __init__(self, protocol, server_side=None):
_lib.wolfSSL_Init()
if _lib.wolfSSL_Init() != _SSL_SUCCESS:
raise RuntimeError(
"wolfSSL library initialization failed")
method = _WolfSSLMethod(protocol, server_side)

self.protocol = protocol
Expand Down Expand Up @@ -356,9 +358,10 @@ def load_verify_locations(self, cafile=None, capath=None, cadata=None):
raise SSLError("Unable to load verify locations. E(%d)" % ret)

if cadata is not None:
cadata_bytes = t2b(cadata)
ret = _lib.wolfSSL_CTX_load_verify_buffer(
self.native_object, t2b(cadata),
len(cadata), _SSL_FILETYPE_PEM)
self.native_object, cadata_bytes,
len(cadata_bytes), _SSL_FILETYPE_PEM)

if ret != _SSL_SUCCESS:
raise SSLError("Unable to load verify locations. E(%d)" % ret)
Expand Down Expand Up @@ -476,8 +479,11 @@ def __init__(self, sock=None, keyfile=None, certfile=None,
ret = _lib.wolfSSL_check_domain_name(self.native_object,
sni)
if ret != _SSL_SUCCESS:
raise SSLError("Unable to set domain name check for "
"hostname verification")
self._release_native_object()
raise SSLError(
"Unable to set domain name "
"check for hostname "
"verification")

if connected:
try:
Expand All @@ -497,6 +503,7 @@ def _release_native_object(self):
self.native_object = _ffi.NULL

def pending(self):
self._check_closed("pending")
return _lib.wolfSSL_pending(self.native_object)

@property
Expand Down Expand Up @@ -605,14 +612,6 @@ def sendall(self, data, flags=0):

while sent < length:
ret = self.write(data[sent:])
if (ret <= 0):
#expect to receive 0 when peer is reset or closed
err = _lib.wolfSSL_get_error(self.native_object, 0)
if err == _SSL_ERROR_WANT_WRITE:
raise SSLWantWriteError()
else:
raise SSLError("wolfSSL_write error (%d)" % err)

sent += ret

return None
Expand Down Expand Up @@ -676,11 +675,13 @@ def recv_into(self, buffer, nbytes=None, flags=0):
self._check_closed("read")
if self._context.protocol < PROTOCOL_DTLSv1:
self._check_connected()
else:
self.do_handshake()

if buffer is None:
raise ValueError("buffer cannot be None")

if nbytes is None:
if nbytes is None or nbytes == 0:
nbytes = len(buffer)
else:
nbytes = min(len(buffer), nbytes)
Expand Down Expand Up @@ -721,7 +722,9 @@ def recvmsg_into(self, *args, **kwargs):

def shutdown(self, how):
if self.native_object != _ffi.NULL:
_lib.wolfSSL_shutdown(self.native_object)
ret = _lib.wolfSSL_shutdown(self.native_object)
if ret == 0:
_lib.wolfSSL_shutdown(self.native_object)
self._release_native_object()
if self._context.protocol < PROTOCOL_DTLSv1:
self._sock.shutdown(how)
Expand All @@ -732,10 +735,15 @@ def unwrap(self):
Returns the wrapped OS socket.
"""
if self.native_object != _ffi.NULL:
_lib.wolfSSL_set_fd(self.native_object, -1)
if self._connected:
# Single-step shutdown is intentional; any
# bidirectional close_notify exchange is the
# caller's responsibility on the raw socket.
_lib.wolfSSL_shutdown(self.native_object)
self._release_native_object()

sock = socket(family=self._sock.family,
sock_type=self._sock.type,
type=self._sock.type,
proto=self._sock.proto,
fileno=self._sock.fileno())

Expand All @@ -745,14 +753,19 @@ def unwrap(self):
return sock

def add_peer(self, addr):
peerAddr = _lib.wolfSSL_dtls_create_peer(addr[1],t2b(addr[0]))
if peerAddr == _ffi.NULL:
raise SSLError("Failed to create peer")
ret = _lib.wolfSSL_dtls_set_peer(self.native_object, peerAddr,
_SOCKADDR_SZ)
peerAddr = _lib.wolfSSL_dtls_create_peer(addr[1], t2b(addr[0]))
if peerAddr == _ffi.NULL:
raise SSLError("Failed to create peer")
try:
ret = _lib.wolfSSL_dtls_set_peer(
self.native_object, peerAddr,
_SOCKADDR_SZ)
if ret != _SSL_SUCCESS:
raise SSLError("Unable to set dtls peer. E(%d)" % ret)
_lib.wolfSSL_dtls_free_peer(peerAddr)
raise SSLError(
"Unable to set dtls peer."
" E(%d)" % ret)
finally:
_lib.wolfSSL_dtls_free_peer(peerAddr)

def do_handshake(self, block=False): # pylint: disable=unused-argument
"""
Expand Down Expand Up @@ -814,18 +827,16 @@ def _real_connect(self, addr, connect_ex):
raise ValueError("attempt to connect already-connected SSLSocket!")

err = 0
ret = _SSL_SUCCESS


if self._context.protocol >= PROTOCOL_DTLSv1:
self.add_peer(addr)
self.add_peer(addr)
else:
if connect_ex:
err = self._sock.connect_ex(addr)
else:
err = 0
self._sock.connect(addr)

if err == 0 and ret == _SSL_SUCCESS:
if err == 0:
self._connected = True
if self.do_handshake_on_connect:
self.do_handshake()
Expand Down Expand Up @@ -894,12 +905,22 @@ def version(self):
"""
Returns the version of the protocol used in the connection.
"""
return _ffi.string(_lib.wolfSSL_get_version(self.native_object)).decode("ascii")
self._check_closed("version")
return _ffi.string(
_lib.wolfSSL_get_version(
self.native_object)).decode("ascii")

# The following functions expose functionality of the underlying
# Socket object. These are also exposed through Python's ssl module
# API and are provided here for compatibility.
def close(self):
Comment thread
dgarske marked this conversation as resolved.
if self.native_object != _ffi.NULL:
if self._connected:
# Single-step shutdown is intentional here; the
# socket is about to be closed so a bidirectional
# close_notify exchange is not required.
_lib.wolfSSL_shutdown(self.native_object)
self._release_native_object()
self._sock.close()

def fileno(self):
Expand Down Expand Up @@ -1029,12 +1050,16 @@ def callback(self):
def _get_passwd(self, passwd, sz, rw, userdata):
try:
result = self._passwd_wrapper(sz, rw, userdata)
if not isinstance(result, bytes):
raise ValueError("Problem, expected String, not bytes")
if len(result) > sz:
raise ValueError("Problem with password returned being long")
for i in range(len(result)):
passwd[i] = result[i:i + 1]
return len(result)
except Exception as e:
raise ValueError("Problem getting password from callback")
except Exception:
raise ValueError(
"Problem getting password from callback")
if not isinstance(result, bytes):
raise ValueError(
"Password callback must return bytes")
if len(result) > sz:
raise ValueError(
"Problem with password returned"
" being long")
for i in range(len(result)):
passwd[i] = result[i:i + 1]
return len(result)
Loading
Loading