To learn more on cryptography check out my tutorial here
Python's ssl module does not directly use
Kernel TLS (KTLS)
. However, as of OpenSSL 3.0, the ssl module can indirectly benefit from KTLS if the underlying OpenSSL library is configured to use KTLS.
When you create an SSL context using the
ssl
module, it uses the OpenSSL library to handle the TLS operations. If the OpenSSL library is configured to use KTLS, the ssl module will benefit from the performance improvements provided by KTLS.Currently, Python does not support direct control or visibility over Kernel TLS (kTLS) because:
- The
ssl
module wraps OpenSSL, but does not expose: SSL_sendfile()
SSL_get_ktls_send()
- kTLS is handled entirely in OpenSSL and the Linux kernel, after the TLS handshake, with no hooks in Python.
How OpenSSL (and thus theoretically Python) could use kTLS under the hood
1. Python and Kernel TLS: What's the Catch?
Python uses OpenSSL under the hood (ssl module). OpenSSL 3.0+ supports Kernel TLS.
But: Python doesn't yet expose an API to explicitly enable or detect kTLS. But: Python doesn't yet expose an API to explicitly enable or detect kTLS.
However, if:
- You’re using Python 3.10+ (linked against OpenSSL 3.0+),
- Your Linux kernel supports kTLS (≥ 5.2 recommended),
- You’re using an eligible cipher (like AES-GCM),
- You call sendfile() on a TLS socket,
- …then kTLS might be automatically used by OpenSSL.
2. Conceptual Flow (What OpenSSL Does)
When OpenSSL decides to offload to kTLS:
- It uses
setsockopt()
under the hood to configure the socket with encryption keys.
- Then
send()
andsendfile()
on the socket directly encrypt data in the kernel.
3. Python Example
Requirements:
- Linux kernel ≥ 5.2
- OpenSSL ≥ 3.0 compiled with
enable-ktls
- Python linked to that OpenSSL version (check via
ssl.OPENSSL_VERSION
)
- Use TLS 1.2 or 1.3 with AES-GCM ciphers
- You would also need a certificate and a private key. How they work together is:
- Server sends
cert.pem
(public key + proof of identity). - Client verifies the certificate.
- Client encrypts data using the public key from
cert.pem
. - Server uses
key.pem
(private key) to decrypt it.
To learn more on cryptography check out my tutorial here
Command To Generate Certificate and Private Key
openssl req -x509 -newkey rsa:2048 -nodes \ -keyout key.pem -out cert.pem -days 365 \ -subj "/CN=localhost"
What this does:
x509
: generate a self-signed certificate
newkey rsa:2048
: create a new 2048-bit RSA key
nodes
: no passphrase on the key (for ease of use)
keyout
: output file for private key
out
: output file for certificate
days 365
: valid for 1 year
subj "/CN=localhost"
: sets the Common Name (CN) in the certificate tolocalhost
After running this:
key.pem
: server's private key
cert.pem
: server's certificate (self-signed)
For use in browsers you'd like a version with SAN (Subject Alternative Name) support different from the above
Python TLS Server (using ssl
)
# server.py import socket import ssl context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) context.load_cert_chain(certfile="cert.pem", keyfile="key.pem") bindsocket = socket.socket() bindsocket.bind(('0.0.0.0', 4433)) bindsocket.listen(5) print("Server listening on port 4433") while True: newsocket, fromaddr = bindsocket.accept() with context.wrap_socket(newsocket, server_side=True) as ssock: print(f"Connection from {fromaddr}") data = ssock.recv(1024) print(f"Received: {data.decode()}") ssock.sendall(b"Hello from server via TLS\n")
Python TLS Client
# client.py import socket import ssl context = ssl.create_default_context() with socket.create_connection(("127.0.0.1", 4433)) as sock: with context.wrap_socket(sock, server_hostname="localhost") as ssock: ssock.sendall(b"Hello from client via TLS\n") print(ssock.recv(1024).decode())
⚠️ The above client code will mostly fail with the following error:
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate (_ssl.c:1028)
You will get this error because the server is using a self-signed certificate, and the Python client (by default) tries to verify it against trusted Certificate Authorities (CAs) — which it obviously can’t, since self-signed certs aren’t trusted by the system.
There are several solution options for this:
Option 1:
Use
context = ssl._create_unverified_context()
instead of context = ssl.create_default_context()
to disable hostname and certificate verification. WARNING: DO NOT use this in production — it disables all TLS security checks.
Option 2:
Add the server's cert.pem to a CA file on the client.
Here are the steps on mac:
- Press
Cmd + Space
, search for Keychain Access, and open it.
- Go to
File > Import Items…
- Select your
cert.pem
file. You can also drag and drop it into System keychain (not "login").
- After importing, search for localhost in the list
- Double-click the certificate
- Expand the "Trust" section
- Set "When using this certificate" to "Always Trust"
Then close the window — it will ask for your password to confirm.
Then update the client code to trust it:
context = ssl.create_default_context() context.load_verify_locations("cert.pem") # <-- use the server's cert here
Personally if this is for testing only useoption 2
if you are on Mac. On MAC there is a common issue on macOS with Homebrew Python or OpenSSL built from source — it may not use the macOS system trust store.
4. How to Check if kTLS is Used?
Run this after connection is established:
sudo cat /proc/net/tls_stat
If kTLS is used, you’ll see counters like:
TLS current active sessions : 1 TLS TX records : 10 TLS RX records : 10
Check TCP socket status:
ss -tlpmi | grep tls
This may show a socket marked with
tls
under memory info.Check kernel logs:
dmesg | grep ktls