From 3adf33d46063658290a7e1726d301c25c8efa024 Mon Sep 17 00:00:00 2001 From: Lealem Amedie Date: Thu, 18 Aug 2022 15:30:56 -0700 Subject: [PATCH] Adding DTLS support --- examples/client.py | 23 ++++++++++++++---- examples/server.py | 43 ++++++++++++++++++++++++--------- wolfssl/__init__.py | 56 +++++++++++++++++++++++++++++++++---------- wolfssl/_build_ffi.py | 17 ++++++++++++- wolfssl/_methods.py | 15 +++++++++++- 5 files changed, 124 insertions(+), 30 deletions(-) diff --git a/examples/client.py b/examples/client.py index e8f902f..d5c26b3 100755 --- a/examples/client.py +++ b/examples/client.py @@ -59,6 +59,11 @@ def build_arg_parser(): "(SSLv3, TLSv1, TLSv1.1, TLSv1.2, TLSv1.3, SSLv23)" ) + parser.add_argument( + "-u", action="store_true", + help="Use UDP DTLS, add -v 0 for DTLSv1, -v 1 for DTLSv1.2 (default)" + ) + parser.add_argument( "-l", metavar="ciphers", type=str, default="", help="Cipher suite list (: delimited)" @@ -103,7 +108,7 @@ def build_arg_parser(): return parser -def get_method(index): +def get_SSLmethod(index): return ( wolfssl.PROTOCOL_SSLv3, wolfssl.PROTOCOL_TLSv1, @@ -113,17 +118,27 @@ def get_method(index): wolfssl.PROTOCOL_SSLv23 )[index] +def get_DTLSmethod(index): + return ( + wolfssl.PROTOCOL_DTLSv1, + wolfssl.PROTOCOL_DTLSv1_2 + )[index] def main(): args = build_arg_parser().parse_args() - bind_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) + # DTLS connection over UDP + if args.u: + bind_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0) + context = wolfssl.SSLContext(get_DTLSmethod(args.v)) + # SSL/TLS connection over TCP + else: + bind_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) + context = wolfssl.SSLContext(get_SSLmethod(args.v)) # enable debug, if native wolfSSL has been compiled with '--enable-debug' wolfssl.WolfSSL.enable_debug() - context = wolfssl.SSLContext(get_method(args.v)) - context.load_cert_chain(args.c, args.k) if args.d: diff --git a/examples/server.py b/examples/server.py index 39bf84b..c8124e4 100755 --- a/examples/server.py +++ b/examples/server.py @@ -54,6 +54,11 @@ def build_arg_parser(): "(SSLv3, TLSv1, TLSv1.1, TLSv1.2, TLSv1_3, SSLv23)" ) + parser.add_argument( + "-u", action="store_true", + help="Use UDP DTLS, add -v 0 for DTLSv1, -v 1 for DTLSv1.2" + ) + parser.add_argument( "-l", metavar="ciphers", type=str, default="", help="Cipher suite list (: delimited)" @@ -92,7 +97,7 @@ def build_arg_parser(): return parser -def get_method(index): +def get_SSLmethod(index): return ( wolfssl.PROTOCOL_SSLv3, wolfssl.PROTOCOL_TLSv1, @@ -102,21 +107,36 @@ def get_method(index): wolfssl.PROTOCOL_SSLv23 )[index] +def get_DTLSmethod(index): + return ( + wolfssl.PROTOCOL_DTLSv1, + wolfssl.PROTOCOL_DTLSv1_2 + )[index] + def main(): args = build_arg_parser().parse_args() - - bind_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) - bind_socket.bind(("" if args.b else "localhost", args.p)) - bind_socket.listen(5) + # DTLS connection over UDP + if args.u: + # Set DTLSv1.2 as default if unspecified + if args.v == 5: + args.v = 1 + bind_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0) + bind_socket.bind(("" if args.b else "localhost", args.p)) + data, from_addr = bind_socket.recvfrom(1) + context = wolfssl.SSLContext(get_DTLSmethod(args.v), server_side=True) + # SSL/TLS connection over TCP + else: + bind_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) + bind_socket.bind(("" if args.b else "localhost", args.p)) + bind_socket.listen(5) + context = wolfssl.SSLContext(get_SSLmethod(args.v), server_side=True) print("Server listening on port", bind_socket.getsockname()[1]) # enable debug, if native wolfSSL has been compiled with '--enable-debug' wolfssl.WolfSSL.enable_debug() - context = wolfssl.SSLContext(get_method(args.v), server_side=True) - context.load_cert_chain(args.c, args.k) if args.d: @@ -131,10 +151,11 @@ def main(): while True: try: secure_socket = None - - new_socket, from_addr = bind_socket.accept() - - secure_socket = context.wrap_socket(new_socket) + if args.u: + secure_socket = context.wrap_socket(bind_socket) + else: + new_socket, from_addr = bind_socket.accept() + secure_socket = context.wrap_socket(new_socket) print("Connection received from", from_addr) diff --git a/wolfssl/__init__.py b/wolfssl/__init__.py index cf115a9..64e0f98 100644 --- a/wolfssl/__init__.py +++ b/wolfssl/__init__.py @@ -51,7 +51,8 @@ from wolfssl.exceptions import ( # noqa: F401 from wolfssl._methods import ( # noqa: F401 PROTOCOL_SSLv23, PROTOCOL_SSLv3, PROTOCOL_TLSv1, PROTOCOL_TLSv1_1, PROTOCOL_TLSv1_2, PROTOCOL_TLSv1_3, - PROTOCOL_TLS, WolfSSLMethod as _WolfSSLMethod + PROTOCOL_TLS, PROTOCOL_DTLSv1, PROTOCOL_DTLSv1_2, + WolfSSLMethod as _WolfSSLMethod ) CERT_NONE = 0 @@ -64,6 +65,8 @@ _SSL_FILETYPE_PEM = 1 _SSL_ERROR_WANT_READ = 2 _SSL_ERROR_WANT_WRITE = 3 +_SOCKADDR_SZ = 16 + _PY3 = sys.version_info[0] == 3 @@ -519,7 +522,6 @@ class SSLSocket(object): """ Enables CRL certificate revocation """ - ret = _lib.wolfSSL_EnableCRL(self.native_object, options) if ret != _SSL_SUCCESS: @@ -529,7 +531,6 @@ class SSLSocket(object): """ Load CRL certificate revocation """ - ret = _lib.wolfSSL_LoadCRLFile(self.native_object, t2b(path) if path else _ffi.NULL, filetype) @@ -543,7 +544,12 @@ class SSLSocket(object): Returns number of bytes of DATA actually transmitted. """ self._check_closed("write") - self._check_connected() + # Check connected if not DTLS + if self._context.protocol < PROTOCOL_DTLSv1: + self._check_connected() + # Complete handshake if DTLS connection + else: + self.do_handshake() data = t2b(data) @@ -599,7 +605,12 @@ class SSLSocket(object): Return zero-length string on EOF. """ self._check_closed("read") - self._check_connected() + # Check connected if not DTLS + if self._context.protocol < PROTOCOL_DTLSv1: + self._check_connected() + # Complete handshake if DTLS connection + else: + self.do_handshake() if buffer is not None: raise ValueError("buffer not allowed in calls to " @@ -630,7 +641,8 @@ class SSLSocket(object): to full size of buffer. """ self._check_closed("read") - self._check_connected() + if self._context.protocol < PROTOCOL_DTLSv1: + self._check_connected() if buffer is None: raise ValueError("buffer cannot be None") @@ -678,7 +690,8 @@ class SSLSocket(object): if self.native_object != _ffi.NULL: _lib.wolfSSL_shutdown(self.native_object) self._release_native_object() - self._sock.shutdown(how) + if self._context.protocol < PROTOCOL_DTLSv1: + self._sock.shutdown(how) def unwrap(self): """ @@ -698,12 +711,23 @@ class SSLSocket(object): 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) + if ret != _SSL_SUCCESS: + raise SSLError("Unable to set dtls peer. E(%d)" % ret) + _lib.wolfSSL_dtls_free_peer(peerAddr) + def do_handshake(self, block=False): # pylint: disable=unused-argument """ Perform a TLS/SSL handshake. """ self._check_closed("do_handshake") - self._check_connected() + if self._context.protocol < PROTOCOL_DTLSv1: + self._check_connected() if self._server_side: ret = _lib.wolfSSL_accept(self.native_object) @@ -756,13 +780,19 @@ class SSLSocket(object): if self._connected: raise ValueError("attempt to connect already-connected SSLSocket!") - if connect_ex: - err = self._sock.connect_ex(addr) + err = 0 + ret = _SSL_SUCCESS + + if self._context.protocol >= PROTOCOL_DTLSv1: + self.add_peer(addr) else: - err = 0 - self._sock.connect(addr) + if connect_ex: + err = self._sock.connect_ex(addr) + else: + err = 0 + self._sock.connect(addr) - if err == 0: + if err == 0 and ret == _SSL_SUCCESS: self._connected = True if self.do_handshake_on_connect: self.do_handshake() diff --git a/wolfssl/_build_ffi.py b/wolfssl/_build_ffi.py index f53c4ec..039a954 100644 --- a/wolfssl/_build_ffi.py +++ b/wolfssl/_build_ffi.py @@ -156,9 +156,15 @@ def make_flags(prefix, debug): # tls 1.3 flags.append("--enable-tls13") + # dtls + flags.append("--enable-dtls") + # crl flags.append("--enable-crl") + # wrapper allocators + cflags.append("-DWOLFSSL_WRAPPER_ALLOCATORS") + # for urllib3 - requires SNI (tlsx), options (openssl compat), peer cert flags.append("--enable-tlsx") flags.append("--enable-opensslextra") @@ -202,7 +208,7 @@ def make(configure_flags): call("make install") -def build_wolfssl(ref, debug=False): +def build_wolfssl(ref, debug=True): prefix = local_path("lib/wolfssl/{}/{}".format( get_platform(), ref)) libfile = os.path.join(prefix, 'lib/libwolfssl.la') @@ -394,6 +400,12 @@ cdef += """ WOLFSSL_METHOD* wolfSSLv23_client_method(void); WOLFSSL_METHOD* wolfSSLv23_method(void); + + WOLFSSL_METHOD* wolfDTLSv1_server_method(void); + WOLFSSL_METHOD* wolfDTLSv1_client_method(void); + + WOLFSSL_METHOD* wolfDTLSv1_2_server_method(void); + WOLFSSL_METHOD* wolfDTLSv1_2_client_method(void); """ if OLDTLS_ENABLED: cdef += """ @@ -453,6 +465,9 @@ cdef += """ void wolfSSL_set_connect_state(WOLFSSL*); int wolfSSL_EnableCRL(WOLFSSL*, int); int wolfSSL_LoadCRLFile(WOLFSSL*, const char*, int); + void* wolfSSL_dtls_create_peer(int, char*); + int wolfSSL_dtls_free_peer(void*); + int wolfSSL_dtls_set_peer(WOLFSSL*, void*, unsigned int); /** * WOLFSSL_X509 functions diff --git a/wolfssl/_methods.py b/wolfssl/_methods.py index b335896..68c7026 100644 --- a/wolfssl/_methods.py +++ b/wolfssl/_methods.py @@ -36,10 +36,12 @@ PROTOCOL_TLSv1 = 3 PROTOCOL_TLSv1_1 = 4 PROTOCOL_TLSv1_2 = 5 PROTOCOL_TLSv1_3 = 6 +PROTOCOL_DTLSv1 = 7 +PROTOCOL_DTLSv1_2 = 8 _PROTOCOL_LIST = [PROTOCOL_SSLv23, PROTOCOL_SSLv3, PROTOCOL_TLS, PROTOCOL_TLSv1, PROTOCOL_TLSv1_1, PROTOCOL_TLSv1_2, - PROTOCOL_TLSv1_3] + PROTOCOL_TLSv1_3, PROTOCOL_DTLSv1, PROTOCOL_DTLSv1_2] _DYNAMIC_TYPE_METHOD = 11 @@ -86,6 +88,17 @@ class WolfSSLMethod(object): # pylint: disable=too-few-public-methods _lib.wolfSSLv23_server_method() if server_side else \ _lib.wolfSSLv23_client_method() + elif protocol == PROTOCOL_DTLSv1: + self.native_object = \ + _lib.wolfDTLSv1_server_method() if server_side else \ + _lib.wolfDTLSv1_client_method() + + elif protocol == PROTOCOL_DTLSv1_2: + self.native_object = \ + _lib.wolfDTLSv1_2_server_method() if server_side else \ + _lib.wolfDTLSv1_2_client_method() + + if self.native_object == _ffi.NULL: raise MemoryError("Unnable to allocate method object")