Compare commits

...

61 Commits

Author SHA1 Message Date
David Garske f112ced76f
Merge pull request #72 from kareem-wolfssl/gplv3
Update license from GPLv2 to GPLv3.
2026-07-31 16:24:13 -07:00
Kareem 2cbf28d9e3 Use GPLv3 rather than GPLv3+. 2026-07-20 13:28:19 -07:00
Kareem a69521afa4 Update license from GPLv2 to GPLv3. 2026-07-20 10:37:26 -07:00
JacobBarthelmeh 29fc3ca101
Merge pull request #71 from kareem-wolfssl/v592
Prepare for v5.9.2 release
2026-07-16 10:19:41 -06:00
Kareem cf2455e95e Prepare for v5.9.2 release 2026-07-15 15:43:54 -07:00
JacobBarthelmeh 2e77f306c2
Merge pull request #70 from julek-wolfssl/fenrir/20260623
Fenrir fixes (2026-06-23)
2026-07-14 16:07:27 -06:00
Juliusz Sosinowicz 406cceb7ba Keep end-to-end example test compatible with Python 2.7
subprocess.run() is Python 3.5+. Use Popen with communicate() and a
threading.Timer watchdog in place of the communicate() timeout, which
is 3.3+.
2026-07-13 17:53:14 +00:00
Juliusz Sosinowicz 89f038ab7c Use getaddrinfo to detect IP literals in client example
socket.inet_pton() is missing on some supported platforms (Python 2.7
on Windows). getaddrinfo() with AI_NUMERICHOST parses without
resolving and is available everywhere. It also handles scoped IPv6
literals.
2026-07-13 17:53:14 +00:00
Juliusz Sosinowicz 0446e8c73f Harden WolfSSLX509 constructor type discrimination
Compare interned cffi type objects instead of rendered type name
strings and raise TypeError for anything that is not a WOLFSSL* or
WOLFSSL_X509*.
2026-07-13 17:35:04 +00:00
Juliusz Sosinowicz d55a68d4ab Keep test_write_bytes.py source 7-bit ASCII
Use the \u00e9 escape instead of a literal e-acute. The test still
exercises multi-byte UTF-8 encoding.
2026-07-13 17:35:04 +00:00
Juliusz Sosinowicz 56e3297edd Skip hostname check for IP literal hosts in client example
Review follow-up for F-5621. wolfSSL_check_domain_name() only matches
DNS names: on this path CheckForAltNames() is called with isIP=0, so
iPAddress SANs are always skipped (verified on v5.8.4-stable and
master). The default invocation (host 127.0.0.1) therefore failed the
handshake with DOMAIN_NAME_MISMATCH (-322) once hostname verification
was enabled by default.

Skip the hostname check for IP literal hosts and say so, keeping
CERT_REQUIRED verification. This also stops offering an IP literal in
SNI, which RFC 6066 forbids. Connecting by DNS name still enables the
hostname check.

Add unit tests for the IP literal paths and an end-to-end test that
runs server.py and client.py with default arguments.
2026-07-13 17:35:04 +00:00
Juliusz Sosinowicz d91cc48734 Update expired test CRL
The bundled CRL expired 2024-11-11, so the client example's default
CRL load (enabled unless -C) fails. Take the current CRL from wolfSSL
v5.8.4-stable certs/crl/crl.pem: same Sawtooth CA and key, revokes
only serial 02, valid until 2028-08-09.

Note: the bundled ca/server/client certs expire 2026-09-08 and will
need a refresh of their own before then.
2026-07-13 17:35:04 +00:00
Juliusz Sosinowicz 93954c9430 Return None from getpeercert when peer has no certificate (F-5623)
get_peer_x509() checked only whether the session was NULL and then built
a WolfSSLX509, whose __init__ called wolfSSL_get_peer_certificate() and
raised SSLError on NULL. On a valid connection where the peer presented
no certificate (e.g. a server not requesting a client cert), this raised
instead of returning None as the stdlib ssl getpeercert() contract
requires. Fetch the certificate in get_peer_x509(), return None when it
is NULL, and have WolfSSLX509 wrap the already-obtained pointer.
2026-06-24 12:27:57 +00:00
Juliusz Sosinowicz 3dd1b902e1 Send bytes-like data verbatim in SSLSocket.write (F-5622)
write() converted data with t2b(), which str()-encodes anything that is
not already bytes. Valid bytes-like inputs such as bytearray and
memoryview were transmitted as their Python repr ("bytearray(b'...')",
"<memory at ...>") instead of their contents, corrupting the stream.
Convert via the buffer protocol (bytes(memoryview(data))) and raise
TypeError for objects that are not bytes-like, matching the stdlib ssl
module.
2026-06-24 12:27:28 +00:00
Juliusz Sosinowicz 9c26572a41 Enable hostname verification in client example (F-5621)
The client example set CERT_REQUIRED and loaded CA roots but never set
check_hostname or passed server_hostname to wrap_socket, so wolfSSL
validated the chain to a trusted CA without binding the certificate to
the requested host. A peer presenting any CA-trusted certificate for a
different hostname would be accepted by anyone reusing this as a secure
client template. Make verification configure hostname checking by
default (via a new configure_verification helper) and add a -n flag to
opt out explicitly for IP literals or test certificates.
2026-06-24 12:26:37 +00:00
Juliusz Sosinowicz 99a4416771 Drive DTLS handshake only until complete in I/O methods (F-4136)
For DTLS, write()/read()/recv_into() called do_handshake() on every
call. do_handshake() runs wolfSSL_accept/connect, which on a
non-blocking socket can raise SSLWantReadError and abort an I/O long
after the handshake finished, and made DTLS write-side behaviour
inconsistent with TCP. Track completion with a _handshake_complete
flag set on a successful do_handshake(), and only drive the handshake
from I/O methods while that flag is False.
2026-06-23 11:33:22 +00:00
Juliusz Sosinowicz 65aaef9750 Map WANT_WRITE from SSLSocket.recv_into() to SSLWantWriteError (F-3907)
recv_into() shares read()'s error-mapping pattern and inherited the
same omission: wolfSSL_read returning WOLFSSL_ERROR_WANT_WRITE (during
a renegotiation needing a write) was reported as a generic SSLError
instead of SSLWantWriteError, breaking non-blocking callers that
distinguish readiness directions. Add the WANT_WRITE branch.
2026-06-23 11:25:08 +00:00
Juliusz Sosinowicz d0bb56e6f9 Map WANT_WRITE from SSLSocket.read() to SSLWantWriteError (F-3906)
wolfSSL_read can return WOLFSSL_ERROR_WANT_WRITE when the SSL layer
must flush a handshake record (e.g. renegotiation) before returning
data. read() only handled WANT_READ, raising a generic SSLError
otherwise, which stops non-blocking callers from select()-ing on
writability. Add a WANT_WRITE branch raising SSLWantWriteError.
2026-06-23 11:24:40 +00:00
Juliusz Sosinowicz 41561e7ba6 Map WANT_READ from SSLSocket.write() to SSLWantReadError (F-3905)
wolfSSL_write can return WOLFSSL_ERROR_WANT_READ (e.g. during a
renegotiation that must read a record before progressing; secure
renegotiation is enabled by default). write() only handled WANT_WRITE,
so WANT_READ fell through to a generic SSLError and non-blocking
callers tore the session down. Add a WANT_READ branch raising
SSLWantReadError, matching do_handshake().
2026-06-23 11:24:10 +00:00
Juliusz Sosinowicz c29eb6760b Fix DTLS server example consuming the ClientHello before handshake (F-3481)
The DTLS branch called bind_socket.recvfrom(1) before creating the
context. On UDP that removes the entire first datagram (the client's
ClientHello) from the queue and discards everything past the first
byte, so wolfSSL_accept() had nothing to consume and the handshake
only recovered after the client's retransmit timer. The captured
from_addr was also reused for every iteration of the -i loop.

Replace it with a peek_peer_address() helper that uses MSG_PEEK to read
the source address without consuming the datagram, and move the peek
into the accept loop so the address is refreshed per connection.
2026-06-23 11:22:06 +00:00
David Garske 74a340db5e
Merge pull request #66 from JeremiahM37/fenrir-fixes-2
Fenrir fixes
2026-04-14 15:18:50 -07:00
Jeremiah Mackey 7a1c3b0885 Guard shutdowns and clean up code 2026-04-14 17:10:36 +00:00
Jeremiah Mackey 2c4ba3c8d7 Fix low-severity issues 2026-04-02 16:32:27 +00:00
Jeremiah Mackey e1ede238e4 Fix wolfSSL_Init return type and check 2026-04-02 16:00:08 +00:00
Jeremiah Mackey c81c839a24 Free peer address on set_peer failure 2026-04-02 15:59:15 +00:00
Jeremiah Mackey 760cb466a7 Shutdown and free SSL in unwrap 2026-04-02 15:58:52 +00:00
Jeremiah Mackey 89667f9f7a Add null checks to version/pending 2026-04-02 15:53:32 +00:00
Jeremiah Mackey e436664616 Skip close on DTLS loop socket 2026-04-02 15:53:05 +00:00
Jeremiah Mackey 84bd6375a2 Copy DER buffer in get_der 2026-04-02 15:52:47 +00:00
Jeremiah Mackey a406dce6c3 Add DTLS handshake to recv_into 2026-04-02 15:47:36 +00:00
Jeremiah Mackey dc49a5391f Default DTLS version in client example 2026-04-02 15:44:07 +00:00
Jeremiah Mackey ffd86dea5e Add tests for recent fixes 2026-04-02 15:40:04 +00:00
David Garske 3f2cd6eca3
Merge pull request #65 from JeremiahM37/fenrir-fixes
Fenrir fixes
2026-03-19 11:33:30 -07:00
Jeremiah Mackey a86dac7917 Use shlex.split in build call() 2026-03-19 16:24:26 +00:00
Jeremiah Mackey 63b1ee8d17 Enforce CERT_REQUIRED for check_hostname 2026-03-19 16:02:17 +00:00
Jeremiah Mackey e15bf07408 Null native_object after CTX_free 2026-03-19 15:52:11 +00:00
Jeremiah Mackey 5e77b6cbcb Check wolfSSL_write return value 2026-03-19 15:51:03 +00:00
Jeremiah Mackey 40b35a4e36 Free X509 in WolfSSLX509.__del__ 2026-03-19 15:46:53 +00:00
Jeremiah Mackey c5ed261a4e Return None from get_peer_x509 2026-03-19 15:46:06 +00:00
Jeremiah Mackey ad2f7c6046 Fix wrong alert description function 2026-03-19 15:45:34 +00:00
Jeremiah Mackey 9a73852678 Check wolfSSL_check_domain_name return 2026-03-19 15:41:42 +00:00
Jeremiah Mackey 08737fab72 Fix wrap_socket server_side mismatch 2026-03-19 15:40:31 +00:00
David Garske 480f7bc237
Merge pull request #64 from cconlon/ffiFixes
Fix static-only wolfSSL linking, improve FFI import errors, and fix make dist
2026-03-17 12:41:10 -07:00
Chris Conlon 26c0151670 Fix make dist target referencing missing scripts 2026-03-02 15:54:52 -07:00
Chris Conlon a6c28b9183 Fix USE_LOCAL_WOLFSSL with static-only builds and add .dylib support 2026-03-02 15:54:52 -07:00
Chris Conlon 8b3adf3023 Fix silent NameError when CFFI bindings are missing, adds _FFIPlaceholder that raises ImportError with instructions 2026-03-02 15:54:48 -07:00
David Garske 05433e92b6
Merge pull request #63 from kareem-wolfssl/v5.8.4
Prepare for v5.8.4 release
2025-12-29 17:20:39 -08:00
Kareem 0073fec796 Prepare for v5.8.4 release 2025-12-29 16:37:31 -07:00
lealem47 45a4151d02
Merge pull request #62 from kareem-wolfssl/verifyMode
Fix CERT_REQUIRED verify mode not setting SSL_VERIFY_FAIL_IF_NO_PEER_CERT and therefore failing to verify the client cert.
2025-12-17 10:17:21 -07:00
Kareem f4c6d17d7d Add testing to confirm that the server fails as expected if CERT_REQUIRED is set and the client doesn't send a cert. 2025-12-16 14:51:28 -07:00
Kareem b4517dece7 Fix CERT_REQUIRED verify mode not setting SSL_VERIFY_FAIL_IF_NO_PEER_CERT and therefore failing to verify the client cert.
Thanks to Matan Radomski for the report.
2025-12-15 11:58:26 -07:00
David Garske 3e4ec84844
Merge pull request #60 from wolfSSL/v5.8.2
Prepare for v5.8.2 release
2025-07-24 11:45:16 -07:00
Lealem Amedie 71742e399a Prepare for v5.8.2 release 2025-07-24 12:37:50 -06:00
Daniel Pouzzner fda757f94a
Merge pull request #58 from lealem47/v5.7.4
Prepare for v5.7.4
2024-11-13 15:17:28 -06:00
Lealem Amedie e601a8bce4 Prepare for v5.7.4 2024-11-13 14:05:55 -07:00
Reda Chouk 754bc9bdb2 update submodule 2024-09-12 10:40:53 +02:00
Reda Chouk 4712cfba90 prepare for v5.7.2 release 2024-09-12 10:40:53 +02:00
Daniel Pouzzner d8db72f00d
Merge pull request #54 from rizlik/support-disabling-scr
wolfssl-py: support disabling secure renegotiation
2024-08-23 16:50:26 -05:00
Marco Oliverio ab486a32bd wolfssl-py: support disabling secure renegotiation
Use environment variable WOLFSSLPY_DISABLE_SCR to build with secure
renegotiation disabled.
2024-08-19 16:34:19 +00:00
David Garske bb2c6b2c00
Merge pull request #53 from rizlik/support_version
SSLSocket: support version() method
2024-07-19 07:50:14 -07:00
Marco Oliverio 4064227489 SSLSocket: support version() method 2024-07-18 18:53:27 +00:00
30 changed files with 1359 additions and 215 deletions

View File

@ -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

View File

@ -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
~~~~~~~~~~~~~~~~~~~~

View File

@ -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

View File

@ -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
=======

View File

@ -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-----

View File

@ -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 =

View File

@ -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

View File

@ -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__':

View File

@ -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

View File

@ -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",

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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"

View File

@ -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

View File

@ -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 *"))

View File

@ -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))

View File

@ -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

View File

@ -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")

View File

@ -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()

View File

@ -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__",

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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"

View File

@ -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

View File

@ -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.