Skip to content

Repository files navigation

TLS Client Handshake Pure Python implementation

This project is mainly used for demonstrating how TLS handshakes are performed for Python programmers.
Do not use this project in any production environment, it is just for education.

Supports

TLSv1.1, TLSv1.2, TLSv1.3
RSA, ECDHE-RSA, ECDHE-ECDSA key exchange (TLS 1.2)
X25519, SECP256R1, SECP384R1 key exchange (TLS 1.3)
AES-CBC, AES-GCM encryption
SSLKEYLOGFILE (both TLS 1.2 CLIENT_RANDOM and TLS 1.3 CLIENT_TRAFFIC_SECRET_0 formats)

Requirements

Python 3.13 (tested)
pipenv
cryptography

Installation

venv

python -m venv .
source bin/activate (linux)
Scripts/activate (windows cmd)

pip install pipenv
pipenv install
# or you can install cryptography directly without pipenv

python index.py www.google.com

pipenv

pipenv install
pipenv run python index.py google.com

Usage

python index.py [-t VERSION] [-c CIPHER] <domain>
Flag Default Description
-t, --tls 1.3 Maximum TLS version to negotiate: 1.1 | 1.2 | 1.3
-c, --cipher (auto) Force a specific cipher suite name (e.g. AES256-SHA)

The client advertises up to the requested version. The server picks the actual version.

# Negotiate highest available version (TLS 1.3 preferred)
python index.py www.google.com

# Force TLS 1.2 only
python index.py www.google.com -t 1.2

# Force TLS 1.2 with a specific cipher suite
python index.py www.facebook.com -t 1.2 -c AES256-SHA

# Force TLS 1.1 (rejected by most modern servers)
python index.py www.example.com -t 1.1

Batch mode (read hosts from stdin):

cat hosts.txt | python index.py -

cat <<EOF | python index.py -t 1.2 -
www.facebook.com
www.google.com
EOF

To capture TLS secrets for Wireshark decryption:

SSLKEYLOGFILE=~/tls_keys.log python index.py www.google.com

Version negotiation

The client sends a ClientHello advertising both TLS 1.3 and TLS 1.2 via the supported_versions extension. After the ServerHello, the client automatically selects the appropriate handshake flow:

  • TLS 1.3: handled by tls13/handshake.py (new control plane)
  • TLS 1.2 / TLS 1.1: handled by the original flow in client.py

TLS 1.3 Handshake

Client                                           Server
──────                                           ──────
ClientHello
  + supported_versions: [TLS 1.3, TLS 1.2]
  + key_share: x25519 public key
  + signature_algorithms
  + psk_key_exchange_modes
                              ────────────────►
                                                ServerHello
                                                  + supported_versions: TLS 1.3
                                                  + key_share: x25519 public key
                              ◄────────────────
                              ── derive handshake keys (HKDF) ──
                                                {EncryptedExtensions}
                                                {Certificate}
                                                {CertificateVerify}
                                                {Finished}
                              ◄────────────────
{Finished}                    ────────────────►
── derive application keys (HKDF) ──
{HTTP GET}                    ────────────────►
                                                {HTTP Response}
                              ◄────────────────

Key schedule (RFC 8446 §7.1)

0 ──► HKDF-Extract ──► Early Secret
                            │
                   Derive-Secret("derived", "")
                            │
ECDHE ──► HKDF-Extract ──► Handshake Secret
                            ├── Derive-Secret("c hs traffic", CH..SH) → client_hs_key
                            └── Derive-Secret("s hs traffic", CH..SH) → server_hs_key
                            │
                   Derive-Secret("derived", "")
                            │
0 ──► HKDF-Extract ──► Master Secret
                            ├── Derive-Secret("c ap traffic", CH..SF) → client_app_key
                            └── Derive-Secret("s ap traffic", CH..SF) → server_app_key

RSA key exchange algorithm (TLS 1.2)

When RSA is used for server authentication and key exchange, a 48-byte pre_master_secret
is generated by the client, encrypted under the server's public key, and sent to the server.
The server uses its private key to decrypt the pre_master_secret. Both parties then
convert the pre_master_secret into the master_secret.

ECDHE-RSA / ECDHE-ECDSA Algorithm (TLS 1.2)

Client Hello

Suggest Cipher Suites

Server Hello

Agree on ECDHE-RSA / ECDHE-ECDSA.
Server generates its EC key pair and sends the EC public key to the client along with the signature made by the server's private key which the public counterpart could be found in the server's digital certificate.

Client Finish

Client verifies the EC public key with the RSA / EC public key in the server's digital certificate.
Client uses the EC public key and generates its own EC key pair in exchange for the pre master secret.
Client sends its EC public key to the server.
Client generates the master secret with the pre master secret and having shared randoms.
Client starts sending encrypted data.

Server Finish

Server receives the client's EC public key.
Server uses the client's EC public key and the server private key in exchange for the same pre master secret.
Server generates the master secret with the pre master secret and having shared randoms.
Server decrypts the request with the master secret.

Project structure

index.py                  — CLI entry point
client.py                 — Handshake orchestrator; negotiates version and dispatches
tls13/
  key_schedule.py         — HKDF-based key derivation (RFC 8446 §7.1)
  record.py               — TLS 1.3 record layer encryption/decryption
  handshake.py            — TLS 1.3 handshake control plane
tls.py                    — TLS version objects (1.0 – 1.3)
cipher_suites.py          — Cipher suite registry and key material derivation (TLS 1.2)
extensions.py             — TLS extensions (SNI, ALPN, key_share, supported_versions, …)
key_exchange.py           — RSA / ECDH / DH key exchange (TLS 1.2)
encryption_algorithms.py  — AES-CBC, AES-GCM (TLS 1.2)
certificates.py           — Certificate parsing and hostname validation
signature_algorithms.py   — RSA-PKCS1, RSA-PSS, ECDSA signature verification
prf.py                    — TLS 1.2 PRF (HMAC-based)
ec_curves.py              — Elliptic curve definitions (X25519, P-256, P-384)
packer.py / reader.py     — TLS record pack/unpack helpers

Todo

TLS 1.3 HelloRetryRequest
TLS 1.3 session resumption (PSK)
TLS 1.0 (low priority, end-of-life)
PSS Padding on TLS 1.2 signature algorithm extension
DHE key exchange
HTTP/2

Contribution

PR is welcome

About

TLS Client Handshake Pure Python implementation

Topics

Resources

Stars

30 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages