openssl/openssl
TLS
Active contributors: Matt Caswell, Tomas Mraz, Bernd Edlinger, Hugo Landau, Neil Horman, Stephen Henson (historical)
Purpose
libssl implements:
- TLS 1.0, 1.1, 1.2, 1.3 (RFC 8446) — client and server.
- DTLS 1.0, 1.2 — client and server.
- ECH (Encrypted Client Hello, RFC 9460 + draft) — client and server, since 3.6 / merged 2026-02.
- TLS extensions: ALPN, SNI, supported_groups, signature_algorithms, key_share, PSK, early data, post-handshake auth, certificate compression, OCSP stapling, SCT, RPK, certificate authorities, server-name-indication, …
- Hybrid post-quantum key exchange via the TLS-GROUP capability (e.g. X25519MLKEM768 since 3.5).
The implementation is split into three layers:
- API & state in
ssl/(theSSL,SSL_CTX,SSL_METHOD,SSL_SESSIONtypes). - State machine in
ssl/statem/— the explicit handshake automaton. - Record layer in
ssl/record/— framing, encryption, sequence numbers, replay defence (DTLS), early-data handling.
Directory layout
ssl/
├── ssl_lib.c 226 KB -- the SSL/SSL_CTX surface, accessor zoo
├── ssl_local.h 114 KB -- the gigantic private header
├── s3_lib.c 149 KB -- TLS 1.0–1.2 method tables and ciphersuite table
├── ssl_ciph.c 70 KB -- ciphersuite parsing, cipher-list configuration
├── ssl_conf.c 38 KB -- the `Cipher`, `Protocol`, `Curves`, … config commands
├── ssl_cert.c 37 KB -- certificate chain handling, CA list assembly
├── ssl_sess.c 41 KB -- session caching, ticket encrypt/decrypt
├── ssl_rsa.c 30 KB -- key/cert loading helpers (PEM/DER/PKCS#8/PKCS#12)
├── t1_lib.c 169 KB -- TLS 1.x extension handling, group/sigalg negotiation, OCSP, SCT
├── t1_enc.c 19 KB -- the legacy 1.0–1.2 PRF
├── tls13_enc.c 33 KB -- HKDF labels, secret derivation, key-update
├── t1_trce.c 63 KB -- packet trace pretty-printer (`SSL_set_msg_callback`)
├── d1_lib.c 29 KB -- DTLS-specific bits
├── d1_msg.c, d1_srtp.c
├── methods.c -- the SSL_METHOD vtable assembly
├── pqueue.c, priority_queue.c -- priority queues used by the DTLS retransmit logic
├── ssl_asn1.c -- session DER encoder/decoder
├── ssl_init.c, ssl_stat.c, ssl_txt.c, ssl_utst.c
├── tls_depr.c, tls_srp.c, ssl_err_legacy.c, ssl_mcnf.c, ssl_rsa_legacy.c
├── ssl_cert_comp.c -- certificate compression (RFC 8879)
├── bio_ssl.c -- BIO_f_ssl filter
ssl/statem/
├── statem.c -- the state machine driver
├── statem_clnt.c 147 KB -- client side
├── statem_srvr.c 157 KB -- server side
├── statem_lib.c 97 KB -- shared helpers
├── statem_dtls.c 46 KB -- DTLS-specific transitions and reassembly
├── extensions.c 77 KB -- common extension dispatch
├── extensions_clnt.c 95 KB -- client extension construction/parsing
├── extensions_srvr.c 87 KB -- server extension construction/parsing
├── extensions_cust.c 21 KB -- custom-extension hooks for applications
└── statem_local.h 24 KB
ssl/record/
├── rec_layer_s3.c 50 KB -- the SSL3/TLS record layer
├── rec_layer_d1.c 22 KB -- the DTLS record layer
├── methods/ -- per-version record method (tls_common.c, tls_pad.c, tls13_meth.c, dtls_meth.c, tls1_meth.c, …)
├── record.h, record_local.h
ssl/ech/ -- Encrypted Client Hello (3.6+)
├── ech_internal.c 80 KB -- core ECH machinery
├── ech_store.c 39 KB -- ECHConfigList store / file loading
├── ech_ssl_apis.c, ech_helper.c, ech_local.h
ssl/quic/ -- QUIC v1 (see features/quic)
ssl/rio/ -- the asynchronous reactor I/O used by the modern record layerPublic types
| Type | Role | Header |
|---|---|---|
SSL_CTX |
Per-process configuration: certificates, ciphersuites, callbacks, session cache, default extensions. | include/openssl/ssl.h.in |
SSL |
Per-connection state: state machine cursor, derived keys, extension state, attached BIOs. | same |
SSL_METHOD |
A vtable choosing protocol version + role. TLS_method, TLS_client_method, TLS_server_method, DTLS_method, … |
same |
SSL_SESSION |
A resumable session (master secret/ticket + selected params). | ssl_sess.c |
SSL_CIPHER |
An IANA ciphersuite, with name aliases and the set of EVP primitives it implies. | ssl_ciph.c |
BIO_f_ssl |
A filter BIO that wraps an SSL * so chained APIs can use TLS transparently. |
ssl/bio_ssl.c |
TLS 1.3 handshake (server-authenticated, no resumption)
sequenceDiagram
participant C as Client (statem_clnt.c)
participant S as Server (statem_srvr.c)
C->>S: ClientHello {key_share=X25519, cipher_suites, sig_algs, supported_groups, ALPN, SNI}
S->>C: ServerHello {key_share=X25519, selected_cipher}
Note over C,S: derive handshake_secret; encrypt subsequent records
S->>C: EncryptedExtensions {ALPN, …}
S->>C: Certificate
S->>C: CertificateVerify (signature over transcript)
S->>C: Finished (HMAC over transcript)
C->>S: [Certificate / CertificateVerify if mTLS]
C->>S: Finished
Note over C,S: derive application_secret; switch to app keys
C-->>S: Application data
S-->>C: Application dataThe TLS 1.3 handshake state machine in ssl/statem/ is explicitly enumerated — the OSSL_HANDSHAKE_STATE (in ssl_local.h) lists every legal cursor (TLS_ST_CW_CLNT_HELLO, TLS_ST_CR_SRVR_HELLO, TLS_ST_OK, …). state_machine() in statem.c repeatedly:
- Consumes one record's worth of bytes.
- Validates the next message is allowed in the current state.
- Calls the per-state handler in
statem_clnt.c/statem_srvr.c. - Advances the cursor.
The state machine returns control to the caller (via SSL_read/SSL_write/SSL_do_handshake) every time it would block, and resumes from where it left off — TLS in OpenSSL is non-blocking by default at the protocol layer, regardless of whether the BIO is blocking.
Extensions
ssl/statem/extensions.c keeps a table mapping each TLS extension id to a (parse, construct, init, final, role-mask, version-mask) tuple. The dispatcher in tls_collect_extensions() walks every received extension once and calls the parser; later, tls_construct_extensions() calls the constructors in deterministic order. This central table is the place to look when adding a new extension.
The split between extensions_clnt.c and extensions_srvr.c is by who is constructing it — the client constructs the extensions in its ClientHello, the server constructs in ServerHello/EE/Cert/CertVerify, and the parsing side is the opposite.
Each extension's per-direction constructor and parser are registered in the table in extensions.c. Searching for TLSEXT_TYPE_<name> finds the entry.
Ciphersuites
ssl/s3_lib.c defines ssl3_ciphers[], a static table of every supported ciphersuite (TLS 1.2 and TLS 1.3 alike). Each row carries:
- IANA
id(the wire codepoint). - Name and alternate names (for OpenSSL cipher list parsing).
- Algorithm components (KEX, AUTH, ENC, MAC, version mask, strength bits).
- Per-protocol min/max version.
The cipher-list configuration string (HIGH:!aNULL:!eNULL:…) is parsed by ssl_ciph.c:SSL_CTX_set_cipher_list(), which produces an ordered subset of the table.
For TLS 1.3, ciphersuites are decoupled from the key exchange and signature algorithms; the latter come from the TLS-GROUP and TLS-SIGALG capability tables published by providers (providers/common/capabilities.c) and assembled in ssl/t1_lib.c.
Record layer
The record layer is responsible for:
- Framing TLS records (5-byte header + ciphertext).
- Encrypting/decrypting records (
AEADfor 1.3, MAC-then-encrypt or stitched for 1.2 and below). - Sequencing records (sequence number is per-key-epoch).
- Padding and length-hiding (TLS 1.3).
- Early-data routing.
- For DTLS: replay window, retransmission queue, MTU-aware fragmentation.
The 3.0 design split the record layer out from the rest of libssl so that it can be replaced for QUIC. The current API to the record layer is the OSSL_RECORD_METHOD (ssl/record/record.h), which has separate implementations for TLS 1.0, 1.1, 1.2, 1.3, DTLS 1.0, DTLS 1.2, and "QUIC". Each lives in ssl/record/methods/.
rec_layer_s3.c is the legacy in-tree record layer used by TLS pre-3.0 — the s3_*.c naming dates back to SSLv3.
DTLS
Datagram-TLS lives alongside TLS in the same statem files but takes special transitions handled in statem_dtls.c. Key differences:
- Each record has an explicit 8-byte sequence number for replay defence.
- Handshake messages are fragmented into records that may arrive out of order; the queue lives in
pqueue.candpriority_queue.c. - The handshake retransmit timer uses an exponential backoff.
- Cookies (
HelloVerifyRequestin DTLS 1.0,HelloRetryRequestin DTLS 1.2/TLS1.3) defend against amplification attacks.
ECH (Encrypted ClientHello)
ssl/ech/ implements ECH per the latest IETF draft and RFC 9460 SVCB DNS records. The ECH config (a public key + cipher choice + "outer name") is loaded into SSL_CTX via SSL_CTX_ech_* APIs (ech_ssl_apis.c). At handshake time:
- The client's inner ClientHello (the real one) is HPKE-encrypted under the ECH public key.
- The outer ClientHello has a benign SNI.
- The server's
EncryptedExtensionscarries the inner ClientHello's transcript hash, completing the binding.
The implementation is large (ech_internal.c is 80 KB) because ECH touches almost every extension construction site (it has to defer encryption until after all extensions are decided) and adds its own cookie handling, retry config, and fallback. The store (ech_store.c) loads ECHConfigList files in PEM or wire format.
Session resumption
TLS 1.3 supports resumption via tickets (RFC 8446 §4.6.1) and PSK; older versions also have session-id resumption. The implementation in ssl/ssl_sess.c plus ssl/statem/statem_lib.c:
- Server issues
NewSessionTicketafter handshake completion (TLS 1.3) or in ServerHello (TLS 1.2). - Tickets are encrypted with a server-private session ticket key; rotated via
SSL_CTX_set_tlsext_ticket_key_evp_cb. - Client stores the ticket+session params in
SSL_SESSIONand presentspre_shared_keyextension on resume.
Renegotiation and key-update
TLS 1.2 supports renegotiation (and the secure renegotiation extension RFC 5746 — never disable it). TLS 1.3 replaces this with KeyUpdate (tls13_enc.c:tls13_update_keys).
Configuration model
SSL_CTX_load_verify_locations(ctx, "/etc/ssl/certs", NULL);
SSL_CTX_set_cipher_list(ctx, "ECDHE+AESGCM:ECDHE+CHACHA20:!aNULL:!RC4");
SSL_CTX_set_ciphersuites(ctx, "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256");
SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION);
SSL_CTX_set1_groups_list(ctx, "X25519MLKEM768:X25519:P-256");
SSL_CTX_use_certificate_chain_file(ctx, "server-fullchain.pem");
SSL_CTX_use_PrivateKey_file(ctx, "server-key.pem", SSL_FILETYPE_PEM);The ssl/ssl_conf.c module also exposes the same options in a string-keyed form usable from openssl.cnf.
Tracing handshakes
SSL_set_msg_callback(ssl, my_cb) plugs a callback into every received and sent record. The default tracer in ssl/t1_trce.c decodes every TLS message back into pseudo-text — it is essentially a built-in tshark for OpenSSL.
SSL_CTX_set_msg_callback(ctx, SSL_trace);
SSL_CTX_set_msg_callback_arg(ctx, BIO_new_fp(stderr, BIO_NOCLOSE));TLS-GROUP and TLS-SIGALG
The set of supported TLS 1.3 groups (X25519, MLKEM768, X25519MLKEM768, P-256, …) and signature schemes (rsa_pss_rsae_sha256, ecdsa_secp256r1_sha256, ed25519, mldsa44, …) is published by providers, not hardcoded in libssl. Providers register a TLS-GROUP or TLS-SIGALG capability via OSSL_OP_PROVIDER capabilities. ssl/t1_lib.c queries the libctx's loaded providers at handshake time. This is how third-party providers can teach OpenSSL about new TLS 1.3 groups (oqs-provider).
Integration points
- Below: every cryptographic primitive is fetched via EVP from the configured providers. The TLS state machine never touches algorithm internals directly.
- Sidewise: the record layer is selected from
ssl/record/methods/based on protocol version. The QUIC implementation reuses the same TLS handshake state machine but with the QUIC record method (which framing is via QUIC CRYPTO frames, not TLS records). - Above: applications use
SSL_*(orBIO_f_ssl). Web servers (Apache, Nginx), language runtimes (Python ssl, Node.js TLS), embedded devices, etc.
Entry points for modification
- New TLS extension: add a
TLSEXT_TYPE_*constant ininclude/openssl/tls1.h.in, add a row to the dispatcher table inssl/statem/extensions.c, writetls_construct_ctos_<name>/tls_parse_ctos_<name>/tls_construct_stoc_<name>/tls_parse_stoc_<name>inextensions_clnt.c/extensions_srvr.c. Updatessl_local.hfor any per-SSLstate. - New ciphersuite: add a row in
ssl3_ciphers[](s3_lib.c) and correspondingSSL_kEX*,SSL_aAUTH*, etc. flags. For TLS 1.3, also list the AEAD/MD pair. - New TLS-GROUP: register it from a provider via
OSSL_OP_PROVIDERcapabilities, not inlibssl. - New protocol version (a hypothetical TLS 1.4): add a method, a new record-layer method in
ssl/record/methods/, transitions inssl/statem/statem_*.c, version-aware extension entries. - DTLS retransmission tweaks:
ssl/d1_lib.candstatem_dtls.c.
Documentation
doc/man3/SSL_CTX_new.pod,SSL_new.pod,SSL_read.pod,SSL_write.pod,SSL_do_handshake.pod,SSL_CTX_set_cipher_list.pod,SSL_CTX_set1_groups.pod,SSL_CTX_set_min_proto_version.pod, …doc/man7/ossl-guide-tls-introduction.podand the rest of theossl-guide-tls-*series.ssl/statem/README.md— short note on the state machine philosophy.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.