Skip to main content

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.

openssl-psk works around this limitation by:

  • Calling libssl's SSL_CTX_set_psk_*_callback directly via ctypes on 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

openssl_psk-0.1.1.tar.gz (8.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

openssl_psk-0.1.1-py3-none-any.whl (7.4 kB view details)

Uploaded Python 3

File details

Details for the file openssl_psk-0.1.1.tar.gz.

File metadata

  • Download URL: openssl_psk-0.1.1.tar.gz
  • Upload date:
  • Size: 8.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for openssl_psk-0.1.1.tar.gz
Algorithm Hash digest
SHA256 7d1e4ec573ce806f77df12123cb718f84953f0023c38a7bbd92b67ec84fcae4e
MD5 252749679657d16f48be976f5c3ddd8c
BLAKE2b-256 a1e1eb2a3f3591c16920510da017bab87b52718ac378048248df707725ebdf80

See more details on using hashes here.

Provenance

The following attestation bundles were made for openssl_psk-0.1.1.tar.gz:

Publisher: publish.yml on angeguillaumekoffi/openssl-psk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file openssl_psk-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: openssl_psk-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 7.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for openssl_psk-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 0a4396ef6285620e145e2667aeeb78c56499e875ec5e5fbaf50c8b57f918c2fd
MD5 ca53b1e393841ee672573b7618914d1c
BLAKE2b-256 3c2fb0c46c5551c6d5cd7e9d02fdb1df180651df9009dd397d2d189d8ca5800a

See more details on using hashes here.

Provenance

The following attestation bundles were made for openssl_psk-0.1.1-py3-none-any.whl:

Publisher: publish.yml on angeguillaumekoffi/openssl-psk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page