diff --git a/tests/test_client.py b/tests/test_client.py index 9e265c3..1f529c5 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -25,6 +25,8 @@ import pytest import wolfssl +from wolfssltestserver import wolfSSLTestServer +from threading import Thread HOST = "www.python.org" PORT = 443 @@ -89,3 +91,64 @@ def test_get_version(ssl_server, ssl_version, tcp_socket): assert secure_socket.version() == protocol_name secure_socket.write(b'hello wolfssl') secure_socket.read(1024) + + +def test_client_cert_verification_failure(): + """ + Test that a connection fails when the server requires client certificates + but the server's CA (globalsign) does not verify the client's certificate. + """ + import socket + import time + + # Create a server with CERT_REQUIRED and globalsign CA + # This server will require client certificates but won't accept + # certificates signed by a different CA + port = 11111 + with wolfSSLTestServer( + ('localhost', port), + version=wolfssl.PROTOCOL_TLS, + verify=wolfssl.CERT_REQUIRED + ) as server: + server_thread = Thread(target=server.handle_request) + server_thread.daemon = True + server_thread.start() + + # Give the server a moment to start + time.sleep(0.1) + + # Create a client socket + client_tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + # Create a client context + client_ctx = wolfssl.SSLContext(wolfssl.PROTOCOL_TLS) + + # Wrap the socket with the client context + # Set do_handshake_on_connect=False so we can explicitly call do_handshake() + # and catch the error + client_socket = client_ctx.wrap_socket( + client_tcp_socket, + do_handshake_on_connect=False + ) + + # Connect the TCP socket first + client_socket.connect(('127.0.0.1', port)) + + # Attempt handshake - this should fail because the client does not + # send a cert/key. + with pytest.raises(wolfssl.SSLError) as exc_info: + client_socket.do_handshake() + # Handshake appeared to succeed, try to read/write to trigger the error + # The server should reject the connection due to certificate verification failure + client_socket.write(b'hello') + client_socket.read(1024) + + # Clean up (errors during close are expected if connection failed) + try: + client_socket.close() + except Exception: + pass + try: + client_tcp_socket.close() + except Exception: + pass diff --git a/tests/test_context.py b/tests/test_context.py index d0d52f1..9609d14 100644 --- a/tests/test_context.py +++ b/tests/test_context.py @@ -39,6 +39,9 @@ def test_verify_mode(ssl_provider, ssl_context): assert ssl_context.verify_mode == ssl_provider.CERT_NONE + ssl_context.verify_mode = ssl_provider.CERT_OPTIONAL + assert ssl_context.verify_mode == ssl_provider.CERT_OPTIONAL + ssl_context.verify_mode = ssl_provider.CERT_REQUIRED assert ssl_context.verify_mode == ssl_provider.CERT_REQUIRED diff --git a/wolfssl/__init__.py b/wolfssl/__init__.py index fbb28f0..95ec4dd 100644 --- a/wolfssl/__init__.py +++ b/wolfssl/__init__.py @@ -55,10 +55,15 @@ PROTOCOL_DTLSv1_3, WolfSSLMethod as _WolfSSLMethod ) -CERT_NONE = 0 -CERT_REQUIRED = 1 +_SSL_VERIFY_NONE = 0 +_SSL_VERIFY_PEER = 1 +_SSL_VERIFY_FAIL_IF_NO_PEER_CERT = 2 -_VERIFY_MODE_LIST = [CERT_NONE, CERT_REQUIRED] +CERT_NONE = _SSL_VERIFY_NONE +CERT_OPTIONAL = _SSL_VERIFY_PEER +CERT_REQUIRED = (_SSL_VERIFY_PEER | _SSL_VERIFY_FAIL_IF_NO_PEER_CERT) + +_VERIFY_MODE_LIST = [CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED] _SSL_SUCCESS = 1 _SSL_FILETYPE_PEM = 1