Compare commits
61 Commits
v5.6.6-sta
...
master
| Author | SHA1 | Date |
|---|---|---|
|
|
f112ced76f | |
|
|
2cbf28d9e3 | |
|
|
a69521afa4 | |
|
|
29fc3ca101 | |
|
|
cf2455e95e | |
|
|
2e77f306c2 | |
|
|
406cceb7ba | |
|
|
89f038ab7c | |
|
|
0446e8c73f | |
|
|
d55a68d4ab | |
|
|
56e3297edd | |
|
|
d91cc48734 | |
|
|
93954c9430 | |
|
|
3dd1b902e1 | |
|
|
9c26572a41 | |
|
|
99a4416771 | |
|
|
65aaef9750 | |
|
|
d0bb56e6f9 | |
|
|
41561e7ba6 | |
|
|
c29eb6760b | |
|
|
74a340db5e | |
|
|
7a1c3b0885 | |
|
|
2c4ba3c8d7 | |
|
|
e1ede238e4 | |
|
|
c81c839a24 | |
|
|
760cb466a7 | |
|
|
89667f9f7a | |
|
|
e436664616 | |
|
|
84bd6375a2 | |
|
|
a406dce6c3 | |
|
|
dc49a5391f | |
|
|
ffd86dea5e | |
|
|
3f2cd6eca3 | |
|
|
a86dac7917 | |
|
|
63b1ee8d17 | |
|
|
e15bf07408 | |
|
|
5e77b6cbcb | |
|
|
40b35a4e36 | |
|
|
c5ed261a4e | |
|
|
ad2f7c6046 | |
|
|
9a73852678 | |
|
|
08737fab72 | |
|
|
480f7bc237 | |
|
|
26c0151670 | |
|
|
a6c28b9183 | |
|
|
8b3adf3023 | |
|
|
05433e92b6 | |
|
|
0073fec796 | |
|
|
45a4151d02 | |
|
|
f4c6d17d7d | |
|
|
b4517dece7 | |
|
|
3e4ec84844 | |
|
|
71742e399a | |
|
|
fda757f94a | |
|
|
e601a8bce4 | |
|
|
754bc9bdb2 | |
|
|
4712cfba90 | |
|
|
d8db72f00d | |
|
|
ab486a32bd | |
|
|
bb2c6b2c00 | |
|
|
4064227489 |
|
|
@ -1,3 +1,34 @@
|
|||
wolfSSL-py Release 5.9.2 (Jul 15, 2026)
|
||||
============================================
|
||||
* Fix SSLSocket.write() corrupting bytes-like data such as bytearray/memoryview
|
||||
* Return None instead of raising from getpeercert() when the peer sends no certificate
|
||||
* Require verify_mode=CERT_REQUIRED when enabling check_hostname
|
||||
* Map non-blocking WANT_READ/WANT_WRITE to SSLWantReadError/SSLWantWriteError
|
||||
* FFI and build fixes
|
||||
* Fenrir fixes
|
||||
* Update wolfSSL to version 5.9.2
|
||||
|
||||
wolfSSL-py Release 5.8.4 (Dec 29, 2025)
|
||||
============================================
|
||||
* Fix an issue which allowed a client without a cert to connect despite setting verify_mode to CERT_REQUIRED (CVE-2025-15346):
|
||||
A vulnerability in the handling of verify_mode = CERT_REQUIRED in the wolfssl Python package (wolfssl-py) causes client certificate requirements to not be fully enforced. Because the WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT flag was not included, the behavior effectively matched CERT_OPTIONAL: a peer certificate was verified if presented, but connections were incorrectly authenticated when no client certificate was provided. This results in improper authentication, allowing attackers to bypass mutual TLS (mTLS) client authentication by omitting a client certificate during the TLS handshake.
|
||||
Thanks to Matan Radomski from Microsoft for the report.
|
||||
* Update wolfSSL to version 5.8.4
|
||||
|
||||
wolfSSL-py Release 5.8.2 (Jul 24, 2025)
|
||||
============================================
|
||||
* Update wolfSSL to version 5.8.2
|
||||
|
||||
wolfSSL-py Release 5.7.4 (Nov 13, 2024)
|
||||
============================================
|
||||
* Update wolfSSL to version 5.7.4
|
||||
|
||||
wolfSSL-py Release 5.7.2 (Sep 6, 2024)
|
||||
============================================
|
||||
* SSLSocket: support version() version
|
||||
* support disabling secure renegotiation
|
||||
* Update wolfSSL to version 5.7.2
|
||||
|
||||
wolfSSL-py Release 5.6.6 (Jan 23, 2024)
|
||||
============================================
|
||||
* Fix segfault issue with TLS v1.3
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@ Open Source
|
|||
~~~~~~~~~~~
|
||||
|
||||
wolfCrypt and wolfSSL software are free software downloads and may be modified
|
||||
to the needs of the user as long as the user adheres to version two of the GPL
|
||||
License. The GPLv2 license can be found on the `gnu.org website
|
||||
<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>`_.
|
||||
to the needs of the user as long as the user adheres to version three of the GPL
|
||||
License. The GPLv3 license can be found on the `gnu.org website
|
||||
<https://www.gnu.org/licenses/gpl-3.0.html>`_.
|
||||
|
||||
Commercial Licensing
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
|
|||
7
Makefile
7
Makefile
|
|
@ -82,12 +82,7 @@ servedocs: docs ## compile the docs watching for changes
|
|||
watchmedo shell-command -p '*.rst' -c '$(MAKE) -C docs html' -R -D .
|
||||
|
||||
dist: clean ## builds source and wheel package
|
||||
python setup.py sdist
|
||||
|
||||
./make/osx/build_wheels.sh
|
||||
|
||||
./make/manylinux1/build_wheels.sh
|
||||
|
||||
python setup.py sdist bdist_wheel
|
||||
ls -l dist
|
||||
|
||||
release: ## package and upload a release
|
||||
|
|
|
|||
11
README.rst
11
README.rst
|
|
@ -66,6 +66,17 @@ wolfSSL library. For example:
|
|||
# Uses custom install location
|
||||
$ USE_LOCAL_WOLFSSL=/tmp/install pip install .
|
||||
|
||||
Disabling secure renegotiation
|
||||
------------------------------
|
||||
|
||||
When building wolfssl-py from source secure renegotiation is enabled by
|
||||
default. To disable secure renegotiation set the environment variable
|
||||
WOLFSSLPY_DISABLE_SCR during the build process. For example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ WOLFSSLPY_DISABLE_SCR=1 pip install .
|
||||
|
||||
Testing
|
||||
=======
|
||||
|
||||
|
|
|
|||
|
|
@ -1,41 +1,42 @@
|
|||
Certificate Revocation List (CRL):
|
||||
Version 2 (0x1)
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
Issuer: C = US, ST = Montana, L = Bozeman, O = Sawtooth, OU = Consulting, CN = www.wolfssl.com, emailAddress = info@wolfssl.com
|
||||
Last Update: Feb 15 12:50:27 2022 GMT
|
||||
Next Update: Nov 11 12:50:27 2024 GMT
|
||||
Issuer: C=US, ST=Montana, L=Bozeman, O=Sawtooth, OU=Consulting, CN=www.wolfssl.com, emailAddress=info@wolfssl.com
|
||||
Last Update: Nov 13 20:41:50 2025 GMT
|
||||
Next Update: Aug 9 20:41:50 2028 GMT
|
||||
CRL extensions:
|
||||
X509v3 CRL Number:
|
||||
2
|
||||
Revoked Certificates:
|
||||
Serial Number: 02
|
||||
Revocation Date: Feb 15 12:50:27 2022 GMT
|
||||
Revocation Date: Nov 13 20:41:50 2025 GMT
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
43:e6:3b:30:0e:32:53:32:a4:08:3c:e5:d5:2e:f1:ce:e9:95:
|
||||
ff:ba:d6:fe:2e:59:80:f8:0a:2f:cf:1e:e0:37:fe:ca:cc:33:
|
||||
66:8b:ed:65:50:7d:44:92:d3:5c:52:9a:95:a5:9d:a5:4e:77:
|
||||
8b:b4:7f:59:c8:7a:e0:eb:34:32:ae:a1:03:99:d2:3c:c0:f4:
|
||||
7e:1c:87:4c:6c:5a:ba:0a:95:e8:a1:44:01:7b:8f:3e:a4:e3:
|
||||
e8:1e:07:19:f0:09:7a:85:8f:f3:82:62:f8:1e:08:51:a3:60:
|
||||
30:5b:06:c8:a2:b3:ff:aa:28:66:ad:fe:4b:81:49:30:ef:5f:
|
||||
5d:ac:d9:ad:17:9f:2a:b6:22:d6:35:cc:9f:d9:11:26:dd:7a:
|
||||
06:35:d0:d5:c7:41:6c:52:97:8c:aa:82:5a:e5:a8:58:d4:b7:
|
||||
2b:31:84:34:15:bd:08:e4:9e:71:9e:c5:40:f8:02:a3:a0:1e:
|
||||
4f:98:72:2b:eb:9e:8a:4e:01:83:88:e5:cb:6e:3b:52:e3:a9:
|
||||
34:a1:7c:e4:79:2c:d1:e0:0b:74:22:ba:6d:cb:c3:a1:56:f9:
|
||||
c9:f4:20:bf:00:49:df:6b:59:49:18:c7:75:27:8e:a1:5a:a6:
|
||||
ff:f2:be:34:4a:c9:6d:6e:24:a3:1f:15:7e:34:90:b6:81:bf:
|
||||
15:80:c3:ac
|
||||
Signature Value:
|
||||
b7:0d:1c:78:99:1c:e8:0b:d9:33:a2:95:01:ad:cf:35:e9:86:
|
||||
28:7f:49:6b:93:76:c1:70:08:61:aa:77:57:34:af:45:82:78:
|
||||
5d:3b:7b:67:ca:b4:fb:d1:68:13:be:34:94:84:2d:65:ad:97:
|
||||
52:69:1d:67:ea:8e:a7:ff:21:0f:21:6c:8c:75:7f:c7:50:c5:
|
||||
6b:a5:fd:cd:3f:91:64:7b:5e:0f:4a:9c:c8:cd:39:a0:30:ad:
|
||||
80:27:50:e0:a7:bf:19:68:cf:6b:26:75:51:14:77:5a:62:6d:
|
||||
bc:66:1a:90:f7:00:09:34:c7:d0:9d:81:f3:b5:9f:90:40:02:
|
||||
8d:3f:68:7f:0d:1d:c5:00:32:e5:cf:42:35:1c:b6:eb:02:a8:
|
||||
d7:2a:a7:f3:f1:10:e2:d5:9e:41:de:2f:78:7d:7f:ad:68:06:
|
||||
a0:6d:40:96:dd:35:59:4d:a0:d3:bd:2e:ba:b6:75:f8:1c:43:
|
||||
b9:c0:b7:75:c4:38:59:46:00:71:ab:5a:df:f5:62:e9:ac:2b:
|
||||
76:11:4f:1b:42:2c:dd:b2:38:6e:57:cf:c5:75:67:4c:3e:27:
|
||||
bb:4c:d5:09:2c:4a:13:3d:8b:9c:89:76:b7:bd:73:1b:64:50:
|
||||
ea:d5:13:0e:51:48:d8:43:08:93:00:85:8f:2f:08:ad:0d:aa:
|
||||
d6:6c:f8:3d
|
||||
-----BEGIN X509 CRL-----
|
||||
MIICBDCB7QIBATANBgkqhkiG9w0BAQsFADCBlDELMAkGA1UEBhMCVVMxEDAOBgNV
|
||||
BAgMB01vbnRhbmExEDAOBgNVBAcMB0JvemVtYW4xETAPBgNVBAoMCFNhd3Rvb3Ro
|
||||
MRMwEQYDVQQLDApDb25zdWx0aW5nMRgwFgYDVQQDDA93d3cud29sZnNzbC5jb20x
|
||||
HzAdBgkqhkiG9w0BCQEWEGluZm9Ad29sZnNzbC5jb20XDTIyMDIxNTEyNTAyN1oX
|
||||
DTI0MTExMTEyNTAyN1owFDASAgECFw0yMjAyMTUxMjUwMjdaoA4wDDAKBgNVHRQE
|
||||
AwIBAjANBgkqhkiG9w0BAQsFAAOCAQEAQ+Y7MA4yUzKkCDzl1S7xzumV/7rW/i5Z
|
||||
gPgKL88e4Df+yswzZovtZVB9RJLTXFKalaWdpU53i7R/Wch64Os0Mq6hA5nSPMD0
|
||||
fhyHTGxaugqV6KFEAXuPPqTj6B4HGfAJeoWP84Ji+B4IUaNgMFsGyKKz/6ooZq3+
|
||||
S4FJMO9fXazZrRefKrYi1jXMn9kRJt16BjXQ1cdBbFKXjKqCWuWoWNS3KzGENBW9
|
||||
COSecZ7FQPgCo6AeT5hyK+ueik4Bg4jly247UuOpNKF85Hks0eALdCK6bcvDoVb5
|
||||
yfQgvwBJ32tZSRjHdSeOoVqm//K+NErJbW4kox8VfjSQtoG/FYDDrA==
|
||||
HzAdBgkqhkiG9w0BCQEWEGluZm9Ad29sZnNzbC5jb20XDTI1MTExMzIwNDE1MFoX
|
||||
DTI4MDgwOTIwNDE1MFowFDASAgECFw0yNTExMTMyMDQxNTBaoA4wDDAKBgNVHRQE
|
||||
AwIBAjANBgkqhkiG9w0BAQsFAAOCAQEAtw0ceJkc6AvZM6KVAa3PNemGKH9Ja5N2
|
||||
wXAIYap3VzSvRYJ4XTt7Z8q0+9FoE740lIQtZa2XUmkdZ+qOp/8hDyFsjHV/x1DF
|
||||
a6X9zT+RZHteD0qcyM05oDCtgCdQ4Ke/GWjPayZ1URR3WmJtvGYakPcACTTH0J2B
|
||||
87WfkEACjT9ofw0dxQAy5c9CNRy26wKo1yqn8/EQ4tWeQd4veH1/rWgGoG1Alt01
|
||||
WU2g070uurZ1+BxDucC3dcQ4WUYAcata3/Vi6awrdhFPG0Is3bI4blfPxXVnTD4n
|
||||
u0zVCSxKEz2LnIl2t71zG2RQ6tUTDlFI2EMIkwCFjy8IrQ2q1mz4PQ==
|
||||
-----END X509 CRL-----
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
# Makefile for Sphinx documentation
|
||||
#
|
||||
# Copyright (C) 2006-2020 wolfSSL Inc.
|
||||
# Copyright (C) 2006-2026 wolfSSL Inc.
|
||||
#
|
||||
# This file is part of wolfSSL. (formerly known as CyaSSL)
|
||||
# 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 2 of the License, or
|
||||
# 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,
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
#
|
||||
# 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-1301, USA
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ master_doc = 'index'
|
|||
|
||||
# General information about the project.
|
||||
project = u'wolfssl Python'
|
||||
copyright = u'2019, wolfSSL Inc. All rights reserved'
|
||||
copyright = u'2006-2026, wolfSSL Inc. All rights reserved'
|
||||
author = u'wolfSSL'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@
|
|||
#
|
||||
# client.py
|
||||
#
|
||||
# Copyright (C) 2006-2020 wolfSSL Inc.
|
||||
# Copyright (C) 2006-2026 wolfSSL Inc.
|
||||
#
|
||||
# This file is part of wolfSSL. (formerly known as CyaSSL)
|
||||
# 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 2 of the License, or
|
||||
# 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,
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
#
|
||||
# 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-1301, USA
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
|
||||
# pylint: disable=missing-docstring, invalid-name, import-error
|
||||
|
||||
|
|
@ -89,6 +89,12 @@ def build_arg_parser():
|
|||
help="Disable client cert check"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-n", action="store_true",
|
||||
help="Disable server hostname check "
|
||||
"(IP literal hosts are never hostname-checked)"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-g", action="store_true",
|
||||
help="Send server HTTP GET"
|
||||
|
|
@ -125,11 +131,61 @@ def get_DTLSmethod(index):
|
|||
wolfssl.PROTOCOL_DTLSv1_3
|
||||
)[index]
|
||||
|
||||
def is_ip_literal(host):
|
||||
# AI_NUMERICHOST never resolves, it only parses. Unlike
|
||||
# socket.inet_pton() it is available on every supported platform.
|
||||
try:
|
||||
socket.getaddrinfo(host, None, 0, 0, 0, socket.AI_NUMERICHOST)
|
||||
return True
|
||||
except socket.error:
|
||||
return False
|
||||
|
||||
|
||||
def configure_verification(context, args):
|
||||
"""
|
||||
Configure peer certificate and hostname verification on the context
|
||||
according to the parsed arguments. Returns the server_hostname to pass
|
||||
to wrap_socket() (None when no hostname check should be performed).
|
||||
|
||||
When certificate verification is enabled (the default), hostname
|
||||
verification is enabled too so that a CA-trusted certificate issued for
|
||||
a different host is rejected. Pass -n to opt out explicitly (e.g. when
|
||||
using test certificates).
|
||||
|
||||
IP literal hosts are not hostname-checked: wolfSSL_check_domain_name()
|
||||
only matches DNS names (iPAddress SANs are skipped on this path), and
|
||||
RFC 6066 forbids IP literals in SNI. Certificate verification against
|
||||
the CA still applies. Connect by DNS name to also verify the hostname.
|
||||
"""
|
||||
if args.d:
|
||||
context.verify_mode = wolfssl.CERT_NONE
|
||||
context.check_hostname = False
|
||||
return None
|
||||
|
||||
context.verify_mode = wolfssl.CERT_REQUIRED
|
||||
context.load_verify_locations(args.A)
|
||||
|
||||
if args.n:
|
||||
context.check_hostname = False
|
||||
return None
|
||||
|
||||
if is_ip_literal(args.h):
|
||||
print("Note: skipping hostname check for IP literal '{}'. "
|
||||
"Connect by DNS name to enable it.".format(args.h))
|
||||
context.check_hostname = False
|
||||
return None
|
||||
|
||||
context.check_hostname = True
|
||||
return args.h
|
||||
|
||||
|
||||
def main():
|
||||
args = build_arg_parser().parse_args()
|
||||
|
||||
# DTLS connection over UDP
|
||||
if args.u:
|
||||
if args.v > 2:
|
||||
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
|
||||
|
|
@ -138,21 +194,22 @@ 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)
|
||||
|
||||
if args.d:
|
||||
context.verify_mode = wolfssl.CERT_NONE
|
||||
else:
|
||||
context.verify_mode = wolfssl.CERT_REQUIRED
|
||||
context.load_verify_locations(args.A)
|
||||
server_hostname = configure_verification(context, args)
|
||||
|
||||
if args.l:
|
||||
context.set_ciphers(args.l)
|
||||
|
||||
secure_socket = None
|
||||
try:
|
||||
secure_socket = context.wrap_socket(bind_socket)
|
||||
secure_socket = context.wrap_socket(
|
||||
bind_socket, server_hostname=server_hostname)
|
||||
|
||||
if not args.C:
|
||||
secure_socket.enable_crl(1)
|
||||
|
|
@ -171,7 +228,8 @@ def main():
|
|||
print()
|
||||
|
||||
finally:
|
||||
secure_socket.close()
|
||||
if secure_socket:
|
||||
secure_socket.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@
|
|||
#
|
||||
# server.py
|
||||
#
|
||||
# Copyright (C) 2006-2020 wolfSSL Inc.
|
||||
# Copyright (C) 2006-2026 wolfSSL Inc.
|
||||
#
|
||||
# This file is part of wolfSSL. (formerly known as CyaSSL)
|
||||
# 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 2 of the License, or
|
||||
# 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,
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
#
|
||||
# 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-1301, USA
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
|
||||
# pylint: disable=missing-docstring, invalid-name, import-error
|
||||
|
||||
|
|
@ -115,16 +115,30 @@ def get_DTLSmethod(index):
|
|||
)[index]
|
||||
|
||||
|
||||
# Large enough to peek a DTLS ClientHello source address.
|
||||
PEEK_BUFSIZE = 1500
|
||||
|
||||
|
||||
def peek_peer_address(sock):
|
||||
"""
|
||||
Return the source address of the next pending datagram without removing
|
||||
it from the socket queue. MSG_PEEK leaves the datagram (the DTLS
|
||||
ClientHello) intact so wolfSSL_accept() can consume it during the
|
||||
handshake.
|
||||
"""
|
||||
_, from_addr = sock.recvfrom(PEEK_BUFSIZE, socket.MSG_PEEK)
|
||||
return from_addr
|
||||
|
||||
|
||||
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))
|
||||
data, from_addr = bind_socket.recvfrom(1)
|
||||
context = wolfssl.SSLContext(get_DTLSmethod(args.v), server_side=True)
|
||||
# SSL/TLS connection over TCP
|
||||
else:
|
||||
|
|
@ -136,7 +150,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)
|
||||
|
||||
|
|
@ -153,6 +170,9 @@ def main():
|
|||
try:
|
||||
secure_socket = None
|
||||
if args.u:
|
||||
# Peek the client's address for this connection without
|
||||
# consuming the ClientHello datagram needed by the handshake.
|
||||
from_addr = peek_peer_address(bind_socket)
|
||||
secure_socket = context.wrap_socket(bind_socket)
|
||||
else:
|
||||
new_socket, from_addr = bind_socket.accept()
|
||||
|
|
@ -170,7 +190,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:
|
||||
secure_socket.close()
|
||||
|
||||
if not args.i:
|
||||
break
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 979707380c677dfa65e3ba48f19e149773a4a32d
|
||||
Subproject commit ac01707f552c611fbd135cc723b2682b3e7f80f2
|
||||
12
setup.py
12
setup.py
|
|
@ -1,13 +1,13 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2006-2020 wolfSSL Inc.
|
||||
# Copyright (C) 2006-2026 wolfSSL Inc.
|
||||
#
|
||||
# This file is part of wolfSSL. (formerly known as CyaSSL)
|
||||
# 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 2 of the License, or
|
||||
# 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,
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
#
|
||||
# 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-1301, USA
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
|
||||
# pylint: disable=wrong-import-position
|
||||
|
||||
|
|
@ -59,7 +59,7 @@ setup(
|
|||
author="wolfSSL Inc.",
|
||||
author_email="info@wolfssl.com",
|
||||
url="https://github.com/wolfssl/wolfssl-py",
|
||||
license="GPLv2 or Commercial License",
|
||||
license="GPLv3 or Commercial License",
|
||||
|
||||
packages=["wolfssl"],
|
||||
|
||||
|
|
@ -68,7 +68,7 @@ setup(
|
|||
|
||||
keywords="wolfssl, wolfcrypt, security, cryptography",
|
||||
classifiers=[
|
||||
u"License :: OSI Approved :: GNU General Public License v2 (GPLv2)",
|
||||
u"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||
u"License :: Other/Proprietary License",
|
||||
u"Operating System :: OS Independent",
|
||||
u"Programming Language :: Python :: 2.7",
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
#
|
||||
# conftest.py
|
||||
#
|
||||
# Copyright (C) 2006-2020 wolfSSL Inc.
|
||||
# Copyright (C) 2006-2026 wolfSSL Inc.
|
||||
#
|
||||
# This file is part of wolfSSL. (formerly known as CyaSSL)
|
||||
# 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 2 of the License, or
|
||||
# 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,
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
#
|
||||
# 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-1301, USA
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
|
||||
# pylint: disable=missing-docstring, redefined-outer-name
|
||||
|
||||
|
|
@ -27,6 +27,7 @@ import ssl
|
|||
import pytest
|
||||
import wolfssl
|
||||
from wolfssl._ffi import lib as _lib
|
||||
from wolfssltestserver import wolfSSLTestServer
|
||||
|
||||
@pytest.fixture
|
||||
def tcp_socket():
|
||||
|
|
@ -61,3 +62,15 @@ def ssl_context(ssl_provider, request):
|
|||
return ssl_provider.SSLContext(ssl_provider.PROTOCOL_TLSv1_3)
|
||||
if request.param == "SSLv23":
|
||||
return ssl_provider.SSLContext(ssl_provider.PROTOCOL_SSLv23)
|
||||
|
||||
port = 1110
|
||||
@pytest.fixture
|
||||
def ssl_server():
|
||||
from threading import Thread
|
||||
global port
|
||||
port += 1
|
||||
with wolfSSLTestServer(('localhost', port)) as server:
|
||||
t = Thread(target=server.handle_request)
|
||||
t.daemon = True
|
||||
t.start()
|
||||
yield server
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
#
|
||||
# test_client.py
|
||||
#
|
||||
# Copyright (C) 2006-2020 wolfSSL Inc.
|
||||
# Copyright (C) 2006-2026 wolfSSL Inc.
|
||||
#
|
||||
# This file is part of wolfSSL. (formerly known as CyaSSL)
|
||||
# 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 2 of the License, or
|
||||
# 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,
|
||||
|
|
@ -18,12 +18,15 @@
|
|||
#
|
||||
# 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-1301, USA
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
|
||||
# pylint: disable=missing-docstring, invalid-name, import-error
|
||||
# pylint: disable=redefined-outer-name
|
||||
|
||||
import pytest
|
||||
import wolfssl
|
||||
from wolfssltestserver import wolfSSLTestServer
|
||||
from threading import Thread
|
||||
|
||||
HOST = "www.python.org"
|
||||
PORT = 443
|
||||
|
|
@ -70,3 +73,123 @@ def test_secure_connection(secure_socket):
|
|||
|
||||
secure_socket.write(b"GET / HTTP/1.1\n\n")
|
||||
assert secure_socket.read(4) == b"HTTP"
|
||||
|
||||
@pytest.mark.parametrize("ssl_version",
|
||||
[pytest.param((wolfssl.PROTOCOL_TLSv1_1, "TLSv1.1"), id="TLSv1.1"),
|
||||
pytest.param((wolfssl.PROTOCOL_TLSv1_2, "TLSv1.2"), id="TLSv1.2"),
|
||||
pytest.param((wolfssl.PROTOCOL_TLSv1_3, "TLSv1.3"), id="TLSv1.3")])
|
||||
def test_get_version(ssl_server, ssl_version, tcp_socket):
|
||||
protocol = ssl_version[0]
|
||||
protocol_name = ssl_version[1]
|
||||
try:
|
||||
ssl_context = wolfssl.SSLContext(protocol)
|
||||
except ValueError:
|
||||
pytest.skip("Protocol {} not supported".format(protocol_name))
|
||||
return
|
||||
secure_socket = ssl_context.wrap_socket(tcp_socket)
|
||||
secure_socket.connect(('127.0.0.1', ssl_server.port))
|
||||
assert secure_socket.version() == protocol_name
|
||||
secure_socket.write(b'hello wolfssl')
|
||||
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
|
||||
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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,171 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# test_client_example.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
|
||||
|
||||
import os
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
|
||||
import wolfssl
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "examples"))
|
||||
import client as client_example # noqa: E402
|
||||
|
||||
|
||||
def _args(argv):
|
||||
return client_example.build_arg_parser().parse_args(argv)
|
||||
|
||||
|
||||
def _ctx():
|
||||
return wolfssl.SSLContext(wolfssl.PROTOCOL_TLSv1_2)
|
||||
|
||||
|
||||
def test_verification_enables_hostname_check_by_default():
|
||||
"""
|
||||
F-5621: with cert verification on (the default), the client must also
|
||||
verify the peer's hostname and pass server_hostname to wrap_socket.
|
||||
"""
|
||||
args = _args(["-h", "example.com"])
|
||||
ctx = _ctx()
|
||||
|
||||
server_hostname = client_example.configure_verification(ctx, args)
|
||||
|
||||
assert ctx.verify_mode == wolfssl.CERT_REQUIRED
|
||||
assert ctx.check_hostname is True
|
||||
assert server_hostname == "example.com"
|
||||
|
||||
|
||||
def test_disable_cert_check_skips_hostname():
|
||||
args = _args(["-d"])
|
||||
ctx = _ctx()
|
||||
# Simulate a reused context that previously had hostname checking on:
|
||||
# -d must clear it, not leave it dangling against CERT_NONE.
|
||||
ctx.verify_mode = wolfssl.CERT_REQUIRED
|
||||
ctx.check_hostname = True
|
||||
|
||||
server_hostname = client_example.configure_verification(ctx, args)
|
||||
|
||||
assert ctx.verify_mode == wolfssl.CERT_NONE
|
||||
assert ctx.check_hostname is False
|
||||
assert server_hostname is None
|
||||
|
||||
|
||||
def test_hostname_check_can_be_opted_out():
|
||||
"""An explicit opt-out is provided for test certificates."""
|
||||
args = _args(["-n"])
|
||||
ctx = _ctx()
|
||||
# Reused context with hostname checking previously enabled: -n must
|
||||
# actively turn it back off.
|
||||
ctx.verify_mode = wolfssl.CERT_REQUIRED
|
||||
ctx.check_hostname = True
|
||||
|
||||
server_hostname = client_example.configure_verification(ctx, args)
|
||||
|
||||
assert ctx.verify_mode == wolfssl.CERT_REQUIRED
|
||||
assert ctx.check_hostname is False
|
||||
assert server_hostname is None
|
||||
|
||||
|
||||
def test_ip_literal_host_skips_hostname_check():
|
||||
"""
|
||||
The default host (127.0.0.1) is an IP literal. wolfSSL's
|
||||
check_domain_name() never matches iPAddress SANs, so the example must
|
||||
not hostname-check IP literals; certificate verification stays on.
|
||||
"""
|
||||
args = _args([])
|
||||
ctx = _ctx()
|
||||
ctx.verify_mode = wolfssl.CERT_REQUIRED
|
||||
ctx.check_hostname = True
|
||||
|
||||
server_hostname = client_example.configure_verification(ctx, args)
|
||||
|
||||
assert args.h == "127.0.0.1"
|
||||
assert ctx.verify_mode == wolfssl.CERT_REQUIRED
|
||||
assert ctx.check_hostname is False
|
||||
assert server_hostname is None
|
||||
|
||||
|
||||
def test_ipv6_literal_host_skips_hostname_check():
|
||||
args = _args(["-h", "::1"])
|
||||
ctx = _ctx()
|
||||
|
||||
server_hostname = client_example.configure_verification(ctx, args)
|
||||
|
||||
assert ctx.verify_mode == wolfssl.CERT_REQUIRED
|
||||
assert ctx.check_hostname is False
|
||||
assert server_hostname is None
|
||||
|
||||
|
||||
def _free_port():
|
||||
sock = socket.socket()
|
||||
sock.bind(("localhost", 0))
|
||||
port = sock.getsockname()[1]
|
||||
sock.close()
|
||||
return port
|
||||
|
||||
|
||||
def test_default_invocation_end_to_end():
|
||||
"""
|
||||
`python client.py` with the default host must complete a connection to
|
||||
`python server.py` using the bundled certificates: verification is on
|
||||
by default and the IP literal host must not trip the hostname check.
|
||||
"""
|
||||
root = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||
port = _free_port()
|
||||
|
||||
# The examples must import the same wolfssl as this test process, even
|
||||
# when the package is not installed (source-tree runs).
|
||||
env = dict(os.environ)
|
||||
pkg_root = os.path.dirname(os.path.dirname(os.path.abspath(
|
||||
wolfssl.__file__)))
|
||||
env["PYTHONPATH"] = os.pathsep.join(
|
||||
[pkg_root] + ([env["PYTHONPATH"]] if env.get("PYTHONPATH") else []))
|
||||
|
||||
server = subprocess.Popen(
|
||||
[sys.executable, "-u", os.path.join("examples", "server.py"),
|
||||
"-p", str(port)],
|
||||
cwd=root, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
try:
|
||||
line = server.stdout.readline().decode()
|
||||
assert "Server listening" in line, line
|
||||
|
||||
client = subprocess.Popen(
|
||||
[sys.executable, "-u", os.path.join("examples", "client.py"),
|
||||
"-p", str(port)],
|
||||
cwd=root, env=env, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT)
|
||||
# Watchdog instead of communicate(timeout=...), which is 3.3+.
|
||||
watchdog = threading.Timer(60, client.kill)
|
||||
watchdog.start()
|
||||
try:
|
||||
out, _ = client.communicate()
|
||||
finally:
|
||||
watchdog.cancel()
|
||||
|
||||
assert client.returncode == 0, out.decode()
|
||||
assert b"I hear you fa shizzle" in out
|
||||
finally:
|
||||
server.kill()
|
||||
server.wait()
|
||||
|
|
@ -2,13 +2,13 @@
|
|||
#
|
||||
# test_context.py
|
||||
#
|
||||
# Copyright (C) 2006-2020 wolfSSL Inc.
|
||||
# Copyright (C) 2006-2026 wolfSSL Inc.
|
||||
#
|
||||
# This file is part of wolfSSL. (formerly known as CyaSSL)
|
||||
# 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 2 of the License, or
|
||||
# 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,
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
#
|
||||
# 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-1301, USA
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
|
||||
# pylint: disable=missing-docstring, invalid-name, import-error
|
||||
# pylint: disable=redefined-outer-name
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -71,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):
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,105 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# test_dtls_handshake_once.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
|
||||
|
||||
"""
|
||||
F-4136: for DTLS, write()/read()/recv_into() used to call do_handshake() on
|
||||
every single call. Once the handshake has completed, re-running it is wasteful
|
||||
and, on a non-blocking socket, wolfSSL_accept/connect can raise
|
||||
SSLWantReadError and abort an otherwise valid I/O. The handshake must only be
|
||||
driven until it completes.
|
||||
"""
|
||||
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
import wolfssl
|
||||
|
||||
|
||||
class _OkLib:
|
||||
"""_lib stub whose I/O calls always succeed."""
|
||||
|
||||
def wolfSSL_write(self, ssl, data, length):
|
||||
return length
|
||||
|
||||
def wolfSSL_read(self, ssl, data, length):
|
||||
return length
|
||||
|
||||
def wolfSSL_get_error(self, ssl, ret): # pragma: no cover
|
||||
return 0
|
||||
|
||||
|
||||
def _make_dtls_socket(handshake_complete):
|
||||
sock = wolfssl.SSLSocket.__new__(wolfssl.SSLSocket)
|
||||
sock.native_object = object()
|
||||
sock._connected = True
|
||||
sock._server_side = True
|
||||
sock._context = SimpleNamespace(protocol=wolfssl.PROTOCOL_DTLSv1_2)
|
||||
sock._handshake_complete = handshake_complete
|
||||
sock._release_native_object = lambda: None
|
||||
return sock
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def spy_handshake(monkeypatch):
|
||||
monkeypatch.setattr(wolfssl, "_lib", _OkLib())
|
||||
calls = []
|
||||
|
||||
def _record(sock):
|
||||
calls.append(True)
|
||||
sock._handshake_complete = True
|
||||
|
||||
return calls, _record
|
||||
|
||||
|
||||
@pytest.mark.parametrize("op", ["write", "read", "recv_into"])
|
||||
def test_dtls_io_does_not_redrive_completed_handshake(spy_handshake, op):
|
||||
calls, record = spy_handshake
|
||||
sock = _make_dtls_socket(handshake_complete=True)
|
||||
sock.do_handshake = lambda block=False: record(sock)
|
||||
|
||||
if op == "write":
|
||||
sock.write(b"payload")
|
||||
elif op == "read":
|
||||
sock.read(8)
|
||||
else:
|
||||
sock.recv_into(bytearray(8))
|
||||
|
||||
assert calls == [], "do_handshake() must not run once the handshake is done"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("op", ["write", "read", "recv_into"])
|
||||
def test_dtls_io_drives_handshake_until_complete(spy_handshake, op):
|
||||
calls, record = spy_handshake
|
||||
sock = _make_dtls_socket(handshake_complete=False)
|
||||
sock.do_handshake = lambda block=False: record(sock)
|
||||
|
||||
if op == "write":
|
||||
sock.write(b"payload")
|
||||
elif op == "read":
|
||||
sock.read(8)
|
||||
else:
|
||||
sock.recv_into(bytearray(8))
|
||||
|
||||
assert calls == [True], "first DTLS I/O must drive the handshake once"
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# test_dtls_server_example.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
|
||||
|
||||
import os
|
||||
import sys
|
||||
import socket
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "examples"))
|
||||
import server as server_example # noqa: E402
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def udp_pair():
|
||||
srv = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
srv.bind(("localhost", 0))
|
||||
cli = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
try:
|
||||
yield srv, cli, srv.getsockname()
|
||||
finally:
|
||||
srv.close()
|
||||
cli.close()
|
||||
|
||||
|
||||
def test_peek_peer_address_returns_source(udp_pair):
|
||||
srv, cli, srv_addr = udp_pair
|
||||
cli.bind(("localhost", 0))
|
||||
cli.sendto(b"clienthello-payload", srv_addr)
|
||||
|
||||
addr = server_example.peek_peer_address(srv)
|
||||
|
||||
assert addr == cli.getsockname()
|
||||
|
||||
|
||||
def test_peek_peer_address_does_not_consume_datagram(udp_pair):
|
||||
"""
|
||||
Regression test for F-3481: peeking the client's address before the
|
||||
DTLS handshake must leave the ClientHello datagram intact. The previous
|
||||
example used recvfrom(1), which consumed the datagram and discarded
|
||||
everything past the first byte, breaking the handshake.
|
||||
"""
|
||||
srv, cli, srv_addr = udp_pair
|
||||
payload = b"X" * 256 # stand-in for a DTLS ClientHello record
|
||||
cli.sendto(payload, srv_addr)
|
||||
|
||||
server_example.peek_peer_address(srv)
|
||||
|
||||
# The datagram must still be fully available for wolfSSL_accept().
|
||||
srv.settimeout(2)
|
||||
data, _ = srv.recvfrom(4096)
|
||||
assert data == payload
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
# -*- 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 *"))
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# test_io_error_mapping.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
|
||||
|
||||
"""
|
||||
These tests exercise the error-code-to-exception mapping in SSLSocket's
|
||||
read/write/recv_into. wolfSSL_write can return WANT_READ and wolfSSL_read can
|
||||
return WANT_WRITE during a renegotiation; non-blocking callers rely on these
|
||||
being surfaced as SSLWantReadError / SSLWantWriteError (matching the stdlib
|
||||
ssl module) rather than a generic SSLError.
|
||||
|
||||
The renegotiation conditions are awkward to force over a real socket, so the
|
||||
native wolfSSL_read/wolfSSL_write/wolfSSL_get_error functions are stubbed to
|
||||
return the relevant codes and the Python-level mapping is verified directly.
|
||||
"""
|
||||
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
import wolfssl
|
||||
|
||||
|
||||
class _FakeLib:
|
||||
"""Stand-in for wolfssl._lib that forces a given I/O return / error."""
|
||||
|
||||
def __init__(self, io_ret, err):
|
||||
self._io_ret = io_ret
|
||||
self._err = err
|
||||
|
||||
def wolfSSL_write(self, ssl, data, length):
|
||||
return self._io_ret
|
||||
|
||||
def wolfSSL_read(self, ssl, data, length):
|
||||
return self._io_ret
|
||||
|
||||
def wolfSSL_get_error(self, ssl, ret):
|
||||
return self._err
|
||||
|
||||
|
||||
def _make_socket():
|
||||
"""A minimal, non-DTLS SSLSocket that skips __init__/native setup."""
|
||||
sock = wolfssl.SSLSocket.__new__(wolfssl.SSLSocket)
|
||||
sock.native_object = object() # non-NULL so _check_closed passes
|
||||
sock._connected = True # so _check_connected is a no-op
|
||||
sock._context = SimpleNamespace(protocol=wolfssl.PROTOCOL_TLS)
|
||||
# The dummy native_object isn't a real cdata pointer, so make __del__
|
||||
# a no-op to avoid wolfSSL_free() choking on it during GC.
|
||||
sock._release_native_object = lambda: None
|
||||
return sock
|
||||
|
||||
|
||||
def _patch_lib(monkeypatch, io_ret, err):
|
||||
monkeypatch.setattr(wolfssl, "_lib", _FakeLib(io_ret, err))
|
||||
|
||||
|
||||
def test_write_want_read_raises_wantread(monkeypatch):
|
||||
"""F-3905: wolfSSL_write returning WANT_READ -> SSLWantReadError."""
|
||||
_patch_lib(monkeypatch, -1, wolfssl._SSL_ERROR_WANT_READ)
|
||||
sock = _make_socket()
|
||||
with pytest.raises(wolfssl.SSLWantReadError):
|
||||
sock.write(b"data")
|
||||
|
||||
|
||||
def test_write_want_write_still_raises_wantwrite(monkeypatch):
|
||||
_patch_lib(monkeypatch, -1, wolfssl._SSL_ERROR_WANT_WRITE)
|
||||
sock = _make_socket()
|
||||
with pytest.raises(wolfssl.SSLWantWriteError):
|
||||
sock.write(b"data")
|
||||
|
||||
|
||||
def test_read_want_write_raises_wantwrite(monkeypatch):
|
||||
"""F-3906: wolfSSL_read returning WANT_WRITE -> SSLWantWriteError."""
|
||||
_patch_lib(monkeypatch, -1, wolfssl._SSL_ERROR_WANT_WRITE)
|
||||
sock = _make_socket()
|
||||
with pytest.raises(wolfssl.SSLWantWriteError):
|
||||
sock.read(16)
|
||||
|
||||
|
||||
def test_read_want_read_still_raises_wantread(monkeypatch):
|
||||
_patch_lib(monkeypatch, -1, wolfssl._SSL_ERROR_WANT_READ)
|
||||
sock = _make_socket()
|
||||
with pytest.raises(wolfssl.SSLWantReadError):
|
||||
sock.read(16)
|
||||
|
||||
|
||||
def test_recv_into_want_write_raises_wantwrite(monkeypatch):
|
||||
"""F-3907: wolfSSL_read in recv_into returning WANT_WRITE."""
|
||||
_patch_lib(monkeypatch, -1, wolfssl._SSL_ERROR_WANT_WRITE)
|
||||
sock = _make_socket()
|
||||
with pytest.raises(wolfssl.SSLWantWriteError):
|
||||
sock.recv_into(bytearray(16))
|
||||
|
||||
|
||||
def test_recv_into_want_read_still_raises_wantread(monkeypatch):
|
||||
_patch_lib(monkeypatch, -1, wolfssl._SSL_ERROR_WANT_READ)
|
||||
sock = _make_socket()
|
||||
with pytest.raises(wolfssl.SSLWantReadError):
|
||||
sock.recv_into(bytearray(16))
|
||||
|
|
@ -2,13 +2,13 @@
|
|||
#
|
||||
# test_methods.py
|
||||
#
|
||||
# Copyright (C) 2006-2020 wolfSSL Inc.
|
||||
# Copyright (C) 2006-2026 wolfSSL Inc.
|
||||
#
|
||||
# This file is part of wolfSSL. (formerly known as CyaSSL)
|
||||
# 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 2 of the License, or
|
||||
# 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,
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
#
|
||||
# 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-1301, USA
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
|
||||
# pylint: disable=missing-docstring, redefined-outer-name, import-error
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,85 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# test_write_bytes.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
|
||||
|
||||
"""
|
||||
F-5622: SSLSocket.write() ran data through t2b(), which str()-encodes anything
|
||||
that is not already bytes. Valid bytes-like inputs (bytearray, memoryview)
|
||||
were therefore serialized as their Python repr ("bytearray(b'...')",
|
||||
"<memory at 0x...>") instead of their actual contents.
|
||||
"""
|
||||
|
||||
from types import SimpleNamespace
|
||||
|
||||
import wolfssl
|
||||
|
||||
|
||||
class _CaptureLib:
|
||||
def __init__(self):
|
||||
self.written = None
|
||||
|
||||
def wolfSSL_write(self, ssl, data, length):
|
||||
self.written = bytes(data[:length])
|
||||
return length
|
||||
|
||||
def wolfSSL_get_error(self, ssl, ret): # pragma: no cover
|
||||
return 0
|
||||
|
||||
|
||||
def _make_socket(monkeypatch):
|
||||
lib = _CaptureLib()
|
||||
monkeypatch.setattr(wolfssl, "_lib", lib)
|
||||
sock = wolfssl.SSLSocket.__new__(wolfssl.SSLSocket)
|
||||
sock.native_object = object()
|
||||
sock._connected = True
|
||||
sock._context = SimpleNamespace(protocol=wolfssl.PROTOCOL_TLS)
|
||||
sock._release_native_object = lambda: None
|
||||
return sock, lib
|
||||
|
||||
|
||||
def test_write_bytes_unchanged(monkeypatch):
|
||||
sock, lib = _make_socket(monkeypatch)
|
||||
sock.write(b"hello")
|
||||
assert lib.written == b"hello"
|
||||
|
||||
|
||||
def test_write_bytearray_sends_contents(monkeypatch):
|
||||
sock, lib = _make_socket(monkeypatch)
|
||||
sock.write(bytearray(b"hello"))
|
||||
assert lib.written == b"hello"
|
||||
|
||||
|
||||
def test_write_memoryview_sends_contents(monkeypatch):
|
||||
sock, lib = _make_socket(monkeypatch)
|
||||
sock.write(memoryview(b"hello"))
|
||||
assert lib.written == b"hello"
|
||||
|
||||
|
||||
def test_write_str_is_utf8_encoded(monkeypatch):
|
||||
# Backward compatibility: str is UTF-8 encoded (historical t2b()
|
||||
# behavior), not rejected. The \u00e9 escape keeps the source 7-bit
|
||||
# ASCII while still exercising a multi-byte UTF-8 encoding.
|
||||
sock, lib = _make_socket(monkeypatch)
|
||||
sock.write("h\u00e9llo")
|
||||
assert lib.written == "h\u00e9llo".encode("utf-8")
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
from wolfssl import SSLContext, PROTOCOL_TLS, CERT_NONE
|
||||
from socketserver import TCPServer, BaseRequestHandler
|
||||
|
||||
ca_path = './certs/client-cert.pem'
|
||||
cert_path = './certs/server-cert.pem'
|
||||
key_path = './certs/server-key.pem'
|
||||
|
||||
class wolfSSLTestServer(TCPServer):
|
||||
class wolfSSLRequestHandler(BaseRequestHandler):
|
||||
def handle(self):
|
||||
ssl_socket = self.server.ctx.wrap_socket(self.request, server_side=True)
|
||||
ssl_socket.recv(1024)
|
||||
ssl_socket.sendall(b'I hear you fa shizzle!')
|
||||
ctx = None
|
||||
def __init__(self, address, version=PROTOCOL_TLS, ca=ca_path, cert=cert_path, key=key_path, verify=CERT_NONE):
|
||||
TCPServer.__init__(self, address, self.wolfSSLRequestHandler, bind_and_activate=False)
|
||||
self.allow_reuse_address = self.allow_reuse_port = True
|
||||
self.ctx = SSLContext(version, server_side=True)
|
||||
self.ctx.verify_mode = verify
|
||||
self.ctx.load_verify_locations(ca)
|
||||
self.ctx.load_cert_chain(cert, key)
|
||||
self.port = address[1]
|
||||
self.version = version
|
||||
self.server_bind()
|
||||
self.server_activate()
|
||||
|
|
@ -2,13 +2,13 @@
|
|||
#
|
||||
# __about__.py
|
||||
#
|
||||
# Copyright (C) 2006-2022 wolfSSL Inc.
|
||||
# Copyright (C) 2006-2026 wolfSSL Inc.
|
||||
#
|
||||
# This file is part of wolfSSL. (formerly known as CyaSSL)
|
||||
# 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 2 of the License, or
|
||||
# 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,
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
#
|
||||
# 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-1301, USA
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
|
||||
from wolfssl._version import __version__, __wolfssl_version__
|
||||
|
||||
|
|
@ -29,8 +29,8 @@ __uri__ = "https://github.com/wolfssl/wolfssl-py"
|
|||
__author__ = "wolfSSL Inc."
|
||||
__email__ = "info@wolfssl.com"
|
||||
|
||||
__license__ = "GPLv2 or Commercial License"
|
||||
__copyright__ = "Copyright (C) 2006-2022 wolfSSL Inc"
|
||||
__license__ = "GPLv3 or Commercial License"
|
||||
__copyright__ = "Copyright (C) 2006-2026 wolfSSL Inc"
|
||||
|
||||
__all__ = [
|
||||
"__title__", "__summary__", "__uri__", "__version__",
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
#
|
||||
# __init__.py
|
||||
#
|
||||
# Copyright (C) 2006-2022 wolfSSL Inc.
|
||||
# Copyright (C) 2006-2026 wolfSSL Inc.
|
||||
#
|
||||
# This file is part of wolfSSL. (formerly known as CyaSSL)
|
||||
# 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 2 of the License, or
|
||||
# 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,
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
#
|
||||
# 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-1301, USA
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
|
||||
# pylint: disable=too-many-instance-attributes, too-many-arguments
|
||||
# pylint: disable=too-many-arguments, too-many-branches, too-many-locals
|
||||
|
|
@ -38,8 +38,10 @@ from wolfssl.__about__ import * # noqa: F401, F403
|
|||
try:
|
||||
from wolfssl._ffi import ffi as _ffi
|
||||
from wolfssl._ffi import lib as _lib
|
||||
except ImportError:
|
||||
pass
|
||||
except ImportError as e:
|
||||
from wolfssl.utils import _FFIPlaceholder
|
||||
_ffi = _FFIPlaceholder(e)
|
||||
_lib = _FFIPlaceholder(e)
|
||||
|
||||
from wolfssl.utils import t2b
|
||||
|
||||
|
|
@ -55,10 +57,15 @@ from wolfssl._methods import ( # noqa: F401
|
|||
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
|
||||
|
|
@ -74,7 +81,9 @@ class WolfSSL(object):
|
|||
|
||||
@classmethod
|
||||
def enable_debug(self):
|
||||
_lib.wolfSSL_Debugging_ON()
|
||||
if _lib.wolfSSL_Debugging_ON() != _SSL_SUCCESS:
|
||||
raise RuntimeError(
|
||||
"wolfSSL debugging not available")
|
||||
|
||||
@classmethod
|
||||
def disable_debug(self):
|
||||
|
|
@ -88,11 +97,32 @@ class WolfSSLX509(object):
|
|||
"""
|
||||
|
||||
def __init__(self, session):
|
||||
self.native_object = _lib.wolfSSL_get_peer_certificate(session)
|
||||
# `session` kept as the original public parameter name. Accept a
|
||||
# WOLFSSL* session (fetch the peer cert here) or an already-obtained
|
||||
# WOLFSSL_X509* (used by SSLSocket.get_peer_x509()).
|
||||
# Compare cffi type objects, not type name strings: typeof()
|
||||
# results are interned per FFI instance, so this is exact and
|
||||
# does not depend on how cffi renders the name.
|
||||
ctype = _ffi.typeof(session)
|
||||
if ctype is _ffi.typeof("WOLFSSL *"):
|
||||
x509 = _lib.wolfSSL_get_peer_certificate(session)
|
||||
elif ctype is _ffi.typeof("WOLFSSL_X509 *"):
|
||||
x509 = session
|
||||
else:
|
||||
raise TypeError("session must be a WOLFSSL* or a WOLFSSL_X509*, "
|
||||
"got %s" % ctype)
|
||||
|
||||
self.native_object = x509
|
||||
|
||||
if self.native_object == _ffi.NULL:
|
||||
raise SSLError("Unable to get internal WOLFSSL_X509 from wolfSSL")
|
||||
|
||||
def __del__(self):
|
||||
if getattr(self, 'native_object', None) is not None \
|
||||
and self.native_object != _ffi.NULL:
|
||||
_lib.wolfSSL_X509_free(self.native_object)
|
||||
self.native_object = _ffi.NULL
|
||||
|
||||
def get_subject_cn(self):
|
||||
cnPtr = _lib.wolfSSL_X509_get_subjectCN(self.native_object)
|
||||
if cnPtr == _ffi.NULL:
|
||||
|
|
@ -130,9 +160,7 @@ class WolfSSLX509(object):
|
|||
if derPtr == _ffi.NULL:
|
||||
return None
|
||||
|
||||
derBytes = _ffi.buffer(derPtr, outSz[0])
|
||||
|
||||
return derBytes
|
||||
return _ffi.buffer(derPtr, outSz[0])[:]
|
||||
|
||||
class SSLContext(object):
|
||||
"""
|
||||
|
|
@ -141,7 +169,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
|
||||
|
|
@ -164,8 +194,9 @@ class SSLContext(object):
|
|||
self.verify_mode = CERT_NONE
|
||||
|
||||
def __del__(self):
|
||||
if getattr(self, 'native_object', _ffi.NULL) != _ffi.NULL:
|
||||
if getattr(self, 'native_object', None) is not None and self.native_object != _ffi.NULL:
|
||||
_lib.wolfSSL_CTX_free(self.native_object)
|
||||
self.native_object = _ffi.NULL
|
||||
|
||||
@property
|
||||
def verify_mode(self):
|
||||
|
|
@ -201,8 +232,11 @@ class SSLContext(object):
|
|||
@check_hostname.setter
|
||||
def check_hostname(self, value):
|
||||
if value is not True and value is not False:
|
||||
raise ValueError("check_hostname must be either True or False")
|
||||
|
||||
raise ValueError("check_hostname must be either "
|
||||
"True or False")
|
||||
if value and self._verify_mode != CERT_REQUIRED:
|
||||
raise ValueError("check_hostname needs verify_mode "
|
||||
"set to CERT_REQUIRED")
|
||||
self._check_hostname = value
|
||||
|
||||
def get_options(self):
|
||||
|
|
@ -238,6 +272,9 @@ class SSLContext(object):
|
|||
"between init and wrap_socket()")
|
||||
|
||||
if self._server_side is None:
|
||||
if server_side:
|
||||
raise ValueError("SSLContext server_side value not consistent "
|
||||
"between init and wrap_socket()")
|
||||
self._server_side = server_side
|
||||
|
||||
if server_side is None and self._server_side is not None:
|
||||
|
|
@ -336,9 +373,10 @@ class SSLContext(object):
|
|||
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)
|
||||
|
|
@ -437,6 +475,9 @@ class SSLSocket(object):
|
|||
|
||||
self._closed = False
|
||||
self._connected = connected
|
||||
# Tracks whether the (DTLS) handshake has completed so I/O methods
|
||||
# don't re-drive it on every call.
|
||||
self._handshake_complete = False
|
||||
|
||||
# create the SSL object
|
||||
self.native_object = _lib.wolfSSL_new(self.context.native_object)
|
||||
|
|
@ -453,8 +494,14 @@ class SSLSocket(object):
|
|||
if self._context.check_hostname:
|
||||
|
||||
sni = _ffi.new("char[]", server_hostname.encode("utf-8"))
|
||||
_lib.wolfSSL_check_domain_name(self.native_object,
|
||||
sni)
|
||||
ret = _lib.wolfSSL_check_domain_name(self.native_object,
|
||||
sni)
|
||||
if ret != _SSL_SUCCESS:
|
||||
self._release_native_object()
|
||||
raise SSLError(
|
||||
"Unable to set domain name "
|
||||
"check for hostname "
|
||||
"verification")
|
||||
|
||||
if connected:
|
||||
try:
|
||||
|
|
@ -469,11 +516,12 @@ class SSLSocket(object):
|
|||
self._release_native_object()
|
||||
|
||||
def _release_native_object(self):
|
||||
if getattr(self, 'native_object', _ffi.NULL) != _ffi.NULL:
|
||||
if getattr(self, 'native_object', None) is not None and self.native_object != _ffi.NULL:
|
||||
_lib.wolfSSL_free(self.native_object)
|
||||
self.native_object = _ffi.NULL
|
||||
|
||||
def pending(self):
|
||||
self._check_closed("pending")
|
||||
return _lib.wolfSSL_pending(self.native_object)
|
||||
|
||||
@property
|
||||
|
|
@ -544,16 +592,35 @@ class SSLSocket(object):
|
|||
Returns number of bytes of DATA actually transmitted.
|
||||
"""
|
||||
self._check_closed("write")
|
||||
# Check connected if not DTLS
|
||||
# Check connected if not DTLS
|
||||
if self._context.protocol < PROTOCOL_DTLSv1:
|
||||
self._check_connected()
|
||||
# Complete handshake if DTLS connection
|
||||
else:
|
||||
# Drive the DTLS handshake only until it has completed.
|
||||
elif not self._handshake_complete:
|
||||
self.do_handshake()
|
||||
|
||||
data = t2b(data)
|
||||
# Send bytes-like objects verbatim; fall back to t2b() for other
|
||||
# types (e.g. str) to preserve backward compatibility.
|
||||
if not isinstance(data, bytes):
|
||||
try:
|
||||
data = bytes(memoryview(data))
|
||||
except TypeError:
|
||||
data = t2b(data)
|
||||
|
||||
return _lib.wolfSSL_write(self.native_object, data, len(data))
|
||||
ret = _lib.wolfSSL_write(
|
||||
self.native_object, data, len(data))
|
||||
if ret <= 0:
|
||||
err = _lib.wolfSSL_get_error(
|
||||
self.native_object, 0)
|
||||
if err == _SSL_ERROR_WANT_WRITE:
|
||||
raise SSLWantWriteError()
|
||||
elif err == _SSL_ERROR_WANT_READ:
|
||||
# wolfSSL_write can require a read first (e.g. renegotiation).
|
||||
raise SSLWantReadError()
|
||||
else:
|
||||
raise SSLError(
|
||||
"wolfSSL_write error (%d)" % err)
|
||||
return ret
|
||||
|
||||
def send(self, data, flags=0):
|
||||
if flags != 0:
|
||||
|
|
@ -572,14 +639,6 @@ class SSLSocket(object):
|
|||
|
||||
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
|
||||
|
|
@ -608,8 +667,8 @@ class SSLSocket(object):
|
|||
# Check connected if not DTLS
|
||||
if self._context.protocol < PROTOCOL_DTLSv1:
|
||||
self._check_connected()
|
||||
# Complete handshake if DTLS connection
|
||||
else:
|
||||
# Drive the DTLS handshake only until it has completed.
|
||||
elif not self._handshake_complete:
|
||||
self.do_handshake()
|
||||
|
||||
if buffer is not None:
|
||||
|
|
@ -623,6 +682,9 @@ class SSLSocket(object):
|
|||
err = _lib.wolfSSL_get_error(self.native_object, 0)
|
||||
if err == _SSL_ERROR_WANT_READ:
|
||||
raise SSLWantReadError()
|
||||
elif err == _SSL_ERROR_WANT_WRITE:
|
||||
# wolfSSL_read can require a write first (e.g. renegotiation).
|
||||
raise SSLWantWriteError()
|
||||
else:
|
||||
raise SSLError("wolfSSL_read error (%d)" % err)
|
||||
|
||||
|
|
@ -643,11 +705,14 @@ class SSLSocket(object):
|
|||
self._check_closed("read")
|
||||
if self._context.protocol < PROTOCOL_DTLSv1:
|
||||
self._check_connected()
|
||||
# Drive the DTLS handshake only until it has completed.
|
||||
elif not self._handshake_complete:
|
||||
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)
|
||||
|
|
@ -662,6 +727,9 @@ class SSLSocket(object):
|
|||
err = _lib.wolfSSL_get_error(self.native_object, 0)
|
||||
if err == _SSL_ERROR_WANT_READ:
|
||||
raise SSLWantReadError()
|
||||
elif err == _SSL_ERROR_WANT_WRITE:
|
||||
# wolfSSL_read can require a write first (e.g. renegotiation).
|
||||
raise SSLWantWriteError()
|
||||
else:
|
||||
raise SSLError("wolfSSL_read error (%d)" % err)
|
||||
|
||||
|
|
@ -688,7 +756,9 @@ class SSLSocket(object):
|
|||
|
||||
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)
|
||||
|
|
@ -699,10 +769,15 @@ class SSLSocket(object):
|
|||
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())
|
||||
|
||||
|
|
@ -712,14 +787,19 @@ 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)
|
||||
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
|
||||
"""
|
||||
|
|
@ -758,7 +838,7 @@ class SSLSocket(object):
|
|||
if alertRet == _SSL_SUCCESS:
|
||||
alertHistory = alertHistoryPtr[0]
|
||||
code = alertHistory.last_rx.code
|
||||
alertDesc = _lib.wolfSSL_alert_type_string_long(code)
|
||||
alertDesc = _lib.wolfSSL_alert_desc_string_long(code)
|
||||
if alertDesc != _ffi.NULL:
|
||||
alertStr = _ffi.string(alertDesc).decode("ascii")
|
||||
else:
|
||||
|
|
@ -771,6 +851,9 @@ class SSLSocket(object):
|
|||
raise SSLError("do_handshake failed with error %d: %s" %
|
||||
(err, eStr))
|
||||
|
||||
# Reached only on success (every failure path above raises).
|
||||
self._handshake_complete = True
|
||||
|
||||
def _real_connect(self, addr, connect_ex):
|
||||
if self._server_side:
|
||||
raise ValueError("can't connect in server-side mode")
|
||||
|
|
@ -781,18 +864,16 @@ class SSLSocket(object):
|
|||
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()
|
||||
|
|
@ -833,13 +914,17 @@ class SSLSocket(object):
|
|||
|
||||
def get_peer_x509(self):
|
||||
"""
|
||||
Returns WolfSSLX509 object representing the peer's certificate,
|
||||
after making a successful SSL/TLS connection.
|
||||
Returns a WolfSSLX509 object representing the peer's certificate,
|
||||
or None if the peer did not present one (or there is no session).
|
||||
"""
|
||||
if self.native_object == _ffi.NULL:
|
||||
return _ffi.NULL
|
||||
return None
|
||||
|
||||
return WolfSSLX509(self.native_object)
|
||||
x509 = _lib.wolfSSL_get_peer_certificate(self.native_object)
|
||||
if x509 == _ffi.NULL:
|
||||
return None
|
||||
|
||||
return WolfSSLX509(x509)
|
||||
|
||||
def getpeercert(self, binary_form=False):
|
||||
"""
|
||||
|
|
@ -850,18 +935,33 @@ class SSLSocket(object):
|
|||
x509 = self.get_peer_x509()
|
||||
|
||||
if not x509:
|
||||
return x509
|
||||
return None
|
||||
|
||||
if binary_form:
|
||||
return x509.get_der()
|
||||
|
||||
return {'subject': ((('commonName', x509.get_subject_cn()),),),
|
||||
'subjectAltName': x509.get_altnames() }
|
||||
def version(self):
|
||||
"""
|
||||
Returns the version of the protocol used in the connection.
|
||||
"""
|
||||
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):
|
||||
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):
|
||||
|
|
@ -991,12 +1091,16 @@ class WolfsslPwd_cb(object):
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
#
|
||||
# build_ffi.py
|
||||
#
|
||||
# Copyright (C) 2006-2022 wolfSSL Inc.
|
||||
# Copyright (C) 2006-2026 wolfSSL Inc.
|
||||
#
|
||||
# This file is part of wolfSSL. (formerly known as CyaSSL)
|
||||
# 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 2 of the License, or
|
||||
# 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,
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
#
|
||||
# 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-1301, USA
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
|
||||
# pylint: disable=missing-docstring, invalid-name
|
||||
|
||||
|
|
@ -35,9 +35,6 @@ import sys
|
|||
from ctypes import cdll
|
||||
from collections import namedtuple
|
||||
|
||||
libwolfssl_path = ""
|
||||
|
||||
|
||||
def local_path(path):
|
||||
""" Return path relative to the root of this project
|
||||
"""
|
||||
|
|
@ -72,11 +69,14 @@ def wolfssl_lib_path():
|
|||
|
||||
|
||||
def call(cmd):
|
||||
print("Calling: '{}' from working directory {}".format(cmd, os.getcwd()))
|
||||
print("Calling: '{}' from working directory {}".format(
|
||||
cmd, os.getcwd()))
|
||||
|
||||
old_env = os.environ["PATH"]
|
||||
os.environ["PATH"] = "{}:{}".format(WOLFSSL_SRC_PATH, old_env)
|
||||
subprocess.check_call(cmd, shell=True, env=os.environ)
|
||||
os.environ["PATH"] = "{}:{}".format(
|
||||
WOLFSSL_SRC_PATH, old_env)
|
||||
subprocess.check_call(
|
||||
shlex.split(cmd), env=os.environ)
|
||||
os.environ["PATH"] = old_env
|
||||
|
||||
|
||||
|
|
@ -142,6 +142,8 @@ def make_flags(prefix, debug):
|
|||
"""
|
||||
flags = []
|
||||
cflags = []
|
||||
# defaults to None (that eval to False)
|
||||
disable_scr = os.getenv("WOLFSSLPY_DISABLE_SCR")
|
||||
|
||||
if get_platform() in ["linux-x86_64", "linux-i686"]:
|
||||
cflags.append("-fpic")
|
||||
|
|
@ -171,7 +173,8 @@ def make_flags(prefix, debug):
|
|||
cflags.append("-DKEEP_PEER_CERT")
|
||||
|
||||
# for pyOpenSSL
|
||||
flags.append("--enable-secure-renegotiation")
|
||||
if not disable_scr:
|
||||
flags.append("--enable-secure-renegotiation")
|
||||
flags.append("--enable-opensslall")
|
||||
cflags.append("-DFP_MAX_BITS=8192")
|
||||
cflags.append("-DHAVE_EX_DATA")
|
||||
|
|
@ -220,8 +223,13 @@ def build_wolfssl(ref, debug=False):
|
|||
|
||||
|
||||
def make_optional_func_list(libwolfssl_path, funcs):
|
||||
defined = []
|
||||
sys.stderr.write("\nlibwolfssl Path: %s\n" % libwolfssl_path)
|
||||
if libwolfssl_path.endswith(".so"):
|
||||
if not libwolfssl_path or not os.path.exists(libwolfssl_path):
|
||||
sys.stderr.write("WARNING: libwolfssl not found, skipping optional "
|
||||
"function detection\n")
|
||||
return []
|
||||
if libwolfssl_path.endswith(".so") or libwolfssl_path.endswith(".dylib"):
|
||||
libwolfssl = cdll.LoadLibrary(libwolfssl_path)
|
||||
defined = []
|
||||
for func in funcs:
|
||||
|
|
@ -241,16 +249,13 @@ def make_optional_func_list(libwolfssl_path, funcs):
|
|||
return defined
|
||||
|
||||
|
||||
def get_libwolfssl():
|
||||
libwolfssl_path = os.path.join(wolfssl_lib_path(), "libwolfssl.a")
|
||||
if not os.path.exists(libwolfssl_path):
|
||||
libwolfssl_path = os.path.join(wolfssl_lib_path(), "libwolfssl.so")
|
||||
if not os.path.exists(libwolfssl_path):
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
else:
|
||||
return 1
|
||||
def get_libwolfssl_path():
|
||||
lib_dir = wolfssl_lib_path()
|
||||
for ext in (".so", ".dylib", ".a"):
|
||||
path = os.path.join(lib_dir, "libwolfssl" + ext)
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
return None
|
||||
|
||||
|
||||
def generate_libwolfssl():
|
||||
|
|
@ -279,6 +284,7 @@ if local_wolfssl:
|
|||
raise RuntimeError("wolfSSL needs to be compiled with "
|
||||
"--enable-opensslextra")
|
||||
featureDetection = 1
|
||||
libwolfssl_path = get_libwolfssl_path()
|
||||
sys.stderr.write("\nDEBUG: Found <wolfssl/options.h>, attempting native "
|
||||
"feature detection\n")
|
||||
|
||||
|
|
@ -287,9 +293,10 @@ else:
|
|||
featureDetection = 0
|
||||
sys.stderr.write("\nDEBUG: Skipping native feature detection, build not "
|
||||
"using USE_LOCAL_WOLFSSL\n")
|
||||
if get_libwolfssl() == 0:
|
||||
libwolfssl_path = get_libwolfssl_path()
|
||||
if libwolfssl_path is None:
|
||||
generate_libwolfssl()
|
||||
get_libwolfssl()
|
||||
libwolfssl_path = get_libwolfssl_path()
|
||||
|
||||
# default values
|
||||
OLDTLS_ENABLED = 0
|
||||
|
|
@ -325,13 +332,23 @@ ffi_source = source + openssl.source
|
|||
|
||||
ffi = FFI()
|
||||
|
||||
ffi.set_source(
|
||||
"wolfssl._ffi",
|
||||
ffi_source,
|
||||
include_dirs=[wolfssl_inc_path()],
|
||||
library_dirs=[wolfssl_lib_path()],
|
||||
libraries=["wolfssl"],
|
||||
)
|
||||
if libwolfssl_path and libwolfssl_path.endswith(".a"):
|
||||
# Static linking: pass the .a file directly via extra_objects
|
||||
ffi.set_source(
|
||||
"wolfssl._ffi",
|
||||
ffi_source,
|
||||
include_dirs=[wolfssl_inc_path()],
|
||||
extra_objects=[libwolfssl_path],
|
||||
)
|
||||
else:
|
||||
# Dynamic linking: use library_dirs + libraries
|
||||
ffi.set_source(
|
||||
"wolfssl._ffi",
|
||||
ffi_source,
|
||||
include_dirs=[wolfssl_inc_path()],
|
||||
library_dirs=[wolfssl_lib_path()],
|
||||
libraries=["wolfssl"],
|
||||
)
|
||||
|
||||
cdef = """
|
||||
/*
|
||||
|
|
@ -388,7 +405,7 @@ cdef = """
|
|||
/*
|
||||
* Debugging
|
||||
*/
|
||||
void wolfSSL_Debugging_ON();
|
||||
int wolfSSL_Debugging_ON(void);
|
||||
void wolfSSL_Debugging_OFF();
|
||||
|
||||
/*
|
||||
|
|
@ -457,7 +474,7 @@ cdef += """
|
|||
/*
|
||||
* SSL/TLS Session functions
|
||||
*/
|
||||
void wolfSSL_Init();
|
||||
int wolfSSL_Init(void);
|
||||
WOLFSSL* wolfSSL_new(WOLFSSL_CTX*);
|
||||
void wolfSSL_free(WOLFSSL*);
|
||||
|
||||
|
|
@ -485,6 +502,7 @@ cdef += """
|
|||
void* wolfSSL_dtls_create_peer(int, char*);
|
||||
int wolfSSL_dtls_free_peer(void*);
|
||||
int wolfSSL_dtls_set_peer(WOLFSSL*, void*, unsigned int);
|
||||
const char* wolfSSL_get_version(const WOLFSSL*);
|
||||
|
||||
/*
|
||||
* WOLFSSL_X509 functions
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
#
|
||||
# _methods.py
|
||||
#
|
||||
# Copyright (C) 2006-2022 wolfSSL Inc.
|
||||
# Copyright (C) 2006-2026 wolfSSL Inc.
|
||||
#
|
||||
# This file is part of wolfSSL. (formerly known as CyaSSL)
|
||||
# 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 2 of the License, or
|
||||
# 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,
|
||||
|
|
@ -18,15 +18,17 @@
|
|||
#
|
||||
# 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-1301, USA
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
|
||||
# pylint: disable=missing-docstring, invalid-name
|
||||
|
||||
try:
|
||||
from wolfssl._ffi import lib as _lib
|
||||
from wolfssl._ffi import ffi as _ffi
|
||||
except ImportError:
|
||||
pass
|
||||
except ImportError as e:
|
||||
from wolfssl.utils import _FFIPlaceholder
|
||||
_ffi = _FFIPlaceholder(e)
|
||||
_lib = _FFIPlaceholder(e)
|
||||
|
||||
|
||||
PROTOCOL_SSLv23 = 1
|
||||
|
|
@ -111,5 +113,5 @@ class WolfSSLMethod(object): # pylint: disable=too-few-public-methods
|
|||
raise MemoryError("Cannot allocate method object")
|
||||
|
||||
def __del__(self):
|
||||
if getattr(self, 'native_object', _ffi.NULL) != _ffi.NULL:
|
||||
if getattr(self, 'native_object', None) is not None and self.native_object != _ffi.NULL:
|
||||
_native_free(self.native_object, _DYNAMIC_TYPE_METHOD)
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
#
|
||||
# _openssl.py
|
||||
#
|
||||
# Copyright (C) 2006-2022 wolfSSL Inc.
|
||||
# Copyright (C) 2006-2026 wolfSSL Inc.
|
||||
#
|
||||
# This file is part of wolfSSL. (formerly known as CyaSSL)
|
||||
# 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 2 of the License, or
|
||||
# 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,
|
||||
|
|
@ -18,9 +18,10 @@
|
|||
#
|
||||
# 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-1301, USA
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
|
||||
# pylint: disable=missing-docstring, invalid-name
|
||||
import os
|
||||
|
||||
source = """
|
||||
#include <wolfssl/options.h>
|
||||
|
|
@ -248,7 +249,6 @@ def construct_cdef(optional_funcs, OLDTLS_ENABLED):
|
|||
X509* SSL_get_peer_certificate(SSL*);
|
||||
const char* SSL_alert_type_string_long(int);
|
||||
const char* SSL_alert_desc_string_long(int);
|
||||
int SSL_renegotiate(SSL*);
|
||||
void SSL_get0_next_proto_negotiated(const SSL*,
|
||||
const unsigned char**, unsigned*);
|
||||
const char* SSL_get_servername(SSL*, unsigned char);
|
||||
|
|
@ -306,6 +306,13 @@ def construct_cdef(optional_funcs, OLDTLS_ENABLED):
|
|||
int OBJ_txt2nid(const char*);
|
||||
"""
|
||||
|
||||
# defaults to None (that eval to False)
|
||||
disable_scr = os.getenv("WOLFSSLPY_DISABLE_SCR")
|
||||
if not disable_scr:
|
||||
cdef += """
|
||||
int SSL_renegotiate(SSL*);
|
||||
"""
|
||||
|
||||
for func in optional_funcs:
|
||||
cdef += "{};".format(func.ossl_sig)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# When bumping the C library version, reset the POST count to 0
|
||||
|
||||
__wolfssl_version__ = "v5.6.6-stable"
|
||||
__wolfssl_version__ = "v5.9.2-stable"
|
||||
|
||||
# We're using implicit post releases [PEP 440] to bump package version
|
||||
# while maintaining the C library version intact for better reference.
|
||||
|
|
@ -8,4 +8,4 @@ __wolfssl_version__ = "v5.6.6-stable"
|
|||
#
|
||||
# MAJOR.MINOR.BUILD-POST
|
||||
|
||||
__version__ = "5.6.6-0"
|
||||
__version__ = "5.9.2-0"
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
#
|
||||
# exceptions.py
|
||||
#
|
||||
# Copyright (C) 2006-2022 wolfSSL Inc.
|
||||
# Copyright (C) 2006-2026 wolfSSL Inc.
|
||||
#
|
||||
# This file is part of wolfSSL. (formerly known as CyaSSL)
|
||||
# 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 2 of the License, or
|
||||
# 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,
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
#
|
||||
# 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-1301, USA
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
|
||||
# pylint: disable=missing-docstring
|
||||
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
#
|
||||
# utils.py
|
||||
#
|
||||
# Copyright (C) 2006-2022 wolfSSL Inc.
|
||||
# Copyright (C) 2006-2026 wolfSSL Inc.
|
||||
#
|
||||
# This file is part of wolfSSL. (formerly known as CyaSSL)
|
||||
# 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 2 of the License, or
|
||||
# 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,
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
#
|
||||
# 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-1301, USA
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
|
||||
# pylint: disable=missing-docstring, unused-import, undefined-variable
|
||||
|
||||
|
|
@ -30,6 +30,19 @@ _TEXT_TYPE = str if _PY3 else unicode # noqa: F821
|
|||
_BINARY_TYPE = bytes if _PY3 else str
|
||||
|
||||
|
||||
class _FFIPlaceholder:
|
||||
def __init__(self, cause=None):
|
||||
object.__setattr__(self, '_cause', cause)
|
||||
|
||||
def __getattr__(self, name):
|
||||
raise ImportError(
|
||||
"wolfssl._ffi is not available. The CFFI bindings have not been "
|
||||
"compiled. If you installed wolfssl via pip, the build may have "
|
||||
"failed silently. Try reinstalling with: "
|
||||
"pip install --no-binary wolfssl wolfssl"
|
||||
) from self._cause
|
||||
|
||||
|
||||
def t2b(string):
|
||||
"""
|
||||
Converts text to binary.
|
||||
|
|
|
|||
Loading…
Reference in New Issue