wolfssl-py/tests/test_getpeercert.py

132 lines
4.6 KiB
Python

# -*- coding: utf-8 -*-
#
# test_getpeercert.py
#
# Copyright (C) 2006-2026 wolfSSL Inc.
#
# This file is part of wolfSSL.
#
# wolfSSL is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# wolfSSL is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
# pylint: disable=missing-docstring, invalid-name, import-error
# pylint: disable=protected-access
import socket
from contextlib import contextmanager
from threading import Thread
import pytest
import wolfssl
@contextmanager
def _client_server_session():
"""
Establish a real TLS connection to a local server that does NOT request a
client certificate. Yields (client_socket, server_result); server_result
is populated (after the block exits) with the server's view of the peer:
{"x509", "cert"} on success or {"error"} if a call raised.
"""
result = {}
server_ctx = wolfssl.SSLContext(wolfssl.PROTOCOL_TLS, server_side=True)
server_ctx.verify_mode = wolfssl.CERT_NONE
server_ctx.load_cert_chain("certs/server-cert.pem", "certs/server-key.pem")
listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
listener.bind(("localhost", 0))
listener.listen(1)
port = listener.getsockname()[1]
def serve():
conn, _ = listener.accept()
ssock = server_ctx.wrap_socket(conn, server_side=True)
try:
ssock.read(1024)
# Client sent no certificate: these must not raise.
result["x509"] = ssock.get_peer_x509()
result["cert"] = ssock.getpeercert()
ssock.write(b"ok")
except Exception as exc: # pylint: disable=broad-except
result["error"] = exc
finally:
ssock.close()
server_thread = Thread(target=serve, daemon=True)
server_thread.start()
client_ctx = wolfssl.SSLContext(wolfssl.PROTOCOL_TLS)
client_ctx.verify_mode = wolfssl.CERT_NONE
client = client_ctx.wrap_socket(
socket.socket(socket.AF_INET, socket.SOCK_STREAM))
client.connect(("localhost", port))
client.write(b"hi")
try:
yield client, result
finally:
try:
client.read(1024)
except Exception: # pylint: disable=broad-except
pass
client.close()
server_thread.join(timeout=10)
listener.close()
def test_getpeercert_returns_none_without_peer_cert():
"""
F-5623: on a valid TLS connection where the peer presented no
certificate (here, a server that does not request a client cert),
getpeercert()/get_peer_x509() must return None instead of raising.
"""
with _client_server_session() as (client, result):
# The peer (server) always presents a certificate.
server_cert = client.getpeercert()
assert "error" not in result, "getpeercert raised: %r" % result.get("error")
assert result["x509"] is None
assert result["cert"] is None
# Positive path: the server's certificate is still returned to the client.
assert server_cert is not None
def test_wolfsslx509_accepts_session_for_backward_compat():
"""
WolfSSLX509 historically accepted a WOLFSSL* session and fetched the peer
certificate itself. That constructor form must keep working alongside the
new WOLFSSL_X509* form used by get_peer_x509().
"""
with _client_server_session() as (client, _result):
from_session = wolfssl.WolfSSLX509(client.native_object)
from_helper = client.get_peer_x509()
# Both forms resolve to the same server certificate.
assert from_session.get_subject_cn() != ""
assert from_session.get_subject_cn() == from_helper.get_subject_cn()
def test_wolfsslx509_rejects_unexpected_types():
"""
WolfSSLX509 discriminates WOLFSSL* from WOLFSSL_X509* by cffi type.
Anything else must raise TypeError instead of being treated as a
certificate pointer.
"""
with pytest.raises(TypeError):
wolfssl.WolfSSLX509(object())
with pytest.raises(TypeError):
wolfssl.WolfSSLX509(wolfssl._ffi.new("int *"))