TLS-PSK socket wrapper for Python 3.10+ — no compiled extensions required
Project description
openssl-psk
TLS-PSK (pre-shared key) socket wrapper for Python 3.10+.
No compiled extensions — pure Python using the standard ssl module and ctypes.
pip install openssl-psk
Why this exists
The standard ssl module has no PSK support below Python 3.13, and the only
existing library (sslpsk) is broken on
Python 3.12+ because its wrap_socket() creates a default context with
check_hostname=True, which raises ValueError for server-side sockets.
openssl-psk fixes this by:
- Calling
libssl'sSSL_CTX_set_psk_*_callbackdirectly viactypeson Python 3.10–3.12 - Using the native
SSLContext.set_psk_*_callback()API transparently on Python 3.13+
No C extension to compile. Just pip install.
Quick start
Client
import socket
from openssl_psk import wrap_socket_client
tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp.connect(("192.168.1.1", 4116))
# simplest form — just the key
ssl_sock = wrap_socket_client(tcp, psk=b"84155C93FDB08FB384B75C44A7D3B625")
ssl_sock.sendall(b"GET /resource HTTP/1.1\r\nHost: 192.168.1.1\r\n\r\n")
print(ssl_sock.recv(4096))
ssl_sock.close()
Server
import socket
from openssl_psk import wrap_socket_server
PSK_DB = {
b"device-001": b"84155C93FDB08FB384B75C44A7D3B625",
b"device-002": b"0AE9DF505E3D3B9A5305409550D42C96",
}
tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
tcp.bind(("0.0.0.0", 4116))
tcp.listen(5)
while True:
conn, addr = tcp.accept()
ssl_sock = wrap_socket_server(
conn,
psk=lambda identity: PSK_DB[identity],
hint="my-server", # optional identity hint sent to clients
)
data = ssl_sock.recv(4096)
ssl_sock.sendall(b"HTTP/1.1 200 OK\r\n\r\n")
ssl_sock.close()
API
wrap_socket_client(sock, psk, ciphers=..., server_hostname=None)
| Parameter | Type | Description |
|---|---|---|
sock |
socket.socket |
Connected TCP socket to wrap |
psk |
bytes | tuple | callable |
Pre-shared key — see accepted shapes below |
ciphers |
str |
OpenSSL cipher string (default: PSK-AES128-CBC-SHA256) |
server_hostname |
str | None |
SNI hostname passed to wrap_socket |
Accepted psk shapes:
# 1. Just the key — identity sent to server is b''
wrap_socket_client(sock, psk=b"mypsk")
# 2. Key + client identity
wrap_socket_client(sock, psk=(b"mypsk", b"myidentity"))
# 3. Callable: server hint → key
PSK_FOR = {b"server1": b"abcdef", b"server2": b"123456"}
wrap_socket_client(sock, psk=lambda hint: PSK_FOR[hint])
Returns an ssl.SSLSocket after completing the handshake.
wrap_socket_server(sock, psk, hint=None, ciphers=...)
| Parameter | Type | Description |
|---|---|---|
sock |
socket.socket |
Accepted TCP socket to wrap (from socket.accept()) |
psk |
bytes | callable |
Pre-shared key — see accepted shapes below |
hint |
str | None |
PSK identity hint sent to clients (optional) |
ciphers |
str |
OpenSSL cipher string (default: PSK-AES128-CBC-SHA256) |
Accepted psk shapes:
# 1. Just the key — same key accepted for every client identity
wrap_socket_server(sock, psk=b"mypsk")
# 2. Callable: client identity → key (raise to reject)
PSK_FOR = {b"clientA": b"abcdef", b"clientB": b"123456"}
wrap_socket_server(sock, psk=lambda identity: PSK_FOR[identity])
Returns an ssl.SSLSocket after completing the handshake.
When using a callable, raise any exception to reject the client.
Compatibility
| Python | Mechanism | CPython only? |
|---|---|---|
| 3.10 | ctypes → libssl |
Yes |
| 3.11 | ctypes → libssl |
Yes |
| 3.12 | ctypes → libssl |
Yes |
| 3.13+ | ssl.SSLContext.set_psk_*_callback() |
No |
The ctypes path reads the SSL_CTX* pointer out of CPython's PySSLContext
struct at a fixed offset that has been stable across CPython 3.x. It will
raise a RuntimeError with a clear message if the pointer is NULL, so you
will know immediately if anything changes in a future CPython release.
Requires OpenSSL ≥ 1.0.1 (for PSK cipher support).
Running the tests
pip install pytest
pytest
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file openssl_psk-0.1.0.tar.gz.
File metadata
- Download URL: openssl_psk-0.1.0.tar.gz
- Upload date:
- Size: 8.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
13f60354defd7fc78fd2f04a38f096eddc449db266336fbb8c2baa16aac27a2f
|
|
| MD5 |
a729d647fa0acdd8c4d7e702775c340e
|
|
| BLAKE2b-256 |
18e913bf02546e6ac08ed63c478c21ac820a6ed9771e209d3548752ccad1eb68
|
Provenance
The following attestation bundles were made for openssl_psk-0.1.0.tar.gz:
Publisher:
publish.yml on angeguillaumekoffi/openssl-psk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
openssl_psk-0.1.0.tar.gz -
Subject digest:
13f60354defd7fc78fd2f04a38f096eddc449db266336fbb8c2baa16aac27a2f - Sigstore transparency entry: 1567667852
- Sigstore integration time:
-
Permalink:
angeguillaumekoffi/openssl-psk@1d4fb9a2fbe6d83e17a52ce68243d1ff1e161d70 -
Branch / Tag:
refs/heads/fixes - Owner: https://github.com/angeguillaumekoffi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1d4fb9a2fbe6d83e17a52ce68243d1ff1e161d70 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file openssl_psk-0.1.0-py3-none-any.whl.
File metadata
- Download URL: openssl_psk-0.1.0-py3-none-any.whl
- Upload date:
- Size: 7.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
35dcbe783c04370430b0aa3edd909216de4a7cfdabd2dac0db614e8489f178f4
|
|
| MD5 |
62dc9890740ac4559c910f5b603b14bd
|
|
| BLAKE2b-256 |
086a329d92c32e030ca9e60384a3a7e59e6f4b201d91a0ff3516c06cde64d5e7
|
Provenance
The following attestation bundles were made for openssl_psk-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on angeguillaumekoffi/openssl-psk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
openssl_psk-0.1.0-py3-none-any.whl -
Subject digest:
35dcbe783c04370430b0aa3edd909216de4a7cfdabd2dac0db614e8489f178f4 - Sigstore transparency entry: 1567667874
- Sigstore integration time:
-
Permalink:
angeguillaumekoffi/openssl-psk@1d4fb9a2fbe6d83e17a52ce68243d1ff1e161d70 -
Branch / Tag:
refs/heads/fixes - Owner: https://github.com/angeguillaumekoffi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1d4fb9a2fbe6d83e17a52ce68243d1ff1e161d70 -
Trigger Event:
workflow_dispatch
-
Statement type: