/**
 * @file tls.h
 * @brief From-scratch TLS 1.2 (RFC 5246) / TLS 1.3 (RFC 8446) protocol engine.
 *
 * This file declares the @ref netplus::tls class, which implements the
 * complete TLS handshake state machine, record layer (both CBC/HMAC and
 * AEAD/GCM ciphers), key schedule (HKDF-based secret derivation for TLS 1.3,
 * classic PRF for TLS 1.2), certificate/private-key handling, SNI-based
 * certificate selection and ALPN negotiation.
 *
 * The @ref netplus::tls class is embedded by value inside @c netplus::ssl
 * (see `src/socket.h`), which drives it through the non-blocking
 * accept/connect/send/recv lifecycle of the socket layer. The same
 * cryptographic primitives (HKDF-Extract/HKDF-Expand-Label, HMAC, PRF,
 * signature helpers) are also reused directly (as static methods) by the
 * QUIC implementation (`src/quic.cpp`) to derive the Initial/Handshake/
 * Application QUIC packet-protection keys, per RFC 9001 ("Using TLS to
 * Secure QUIC").
 *
 * Also declared here: @ref netplus::TlsSessionCacheEntry and
 * @ref netplus::TlsSessionCache, which together implement TLS 1.2 session
 * resumption (abbreviated handshake) via a session-ID keyed, mutex-protected
 * cache shared across connections.
 */

#pragma once

#include <vector>
#include <cstdint>
#include <string>
#include <memory>
#include <map>
#include <deque>
#include <atomic>
#include <functional>
#include <chrono>

#include <mutex>
#include "config.h"
#include "rsa.h"
#include "aes.h"
#include "x509.h"
#include "sha.h"
#include "pkcs12.h"

namespace netplus {
    class buffer;
    class socket;

    // =====================================================================
    // TLS Session Cache — enables TLS 1.2 abbreviated handshakes.
    // Shared across connections; protected by its own mutex.
    // =====================================================================

    /**
     * @brief A single cached TLS 1.2 session, keyed for resumption.
     *
     * Captures everything an abbreviated (resumed) TLS 1.2 handshake needs
     * to skip full key exchange and certificate verification: the
     * session ID, the previously negotiated master secret, the cipher
     * suite, and a creation timestamp used for expiry (see
     * @ref TlsSessionCache::setMaxAge).
     */
    struct TlsSessionCacheEntry {
        std::vector<uint8_t> session_id;         /**< Session ID used for resumption lookups. */
        std::vector<uint8_t> master_secret;      /**< Negotiated TLS 1.2 master secret (48 bytes). */
        uint16_t             cipher_suite;       /**< Negotiated cipher suite, e.g. 0x002F (AES-128-CBC-SHA). */
        std::chrono::steady_clock::time_point created; /**< Time the entry was stored; used to compute age against the cache's max age. */
    };

    /**
     * @brief Thread-safe cache of TLS 1.2 sessions for abbreviated handshakes.
     *
     * Sessions can be looked up either by the TLS session ID (server side,
     * when a client re-offers a previous session ID in its ClientHello) or
     * by an arbitrary "host:port" string key (client side, so a client can
     * find its most recent session for a given peer before it even knows
     * the session ID it will offer). Both lookup paths share the same
     * underlying entries and the same expiry/eviction policy.
     *
     * All public methods lock an internal mutex, so a single
     * @ref TlsSessionCache instance can safely be shared across multiple
     * connections/threads (this is the intended usage: one cache per
     * server, referenced by @ref tls::session_cache on each connection).
     * Eviction is a simple oldest-entry policy bounded by
     * @ref setMaxEntries; entries also expire based on @ref setMaxAge
     * regardless of cache pressure.
     */
    class TlsSessionCache {
    public:
        /**
         * @brief Look up a cached session by TLS session ID.
         *
         * Used on the server side when a client's ClientHello offers a
         * non-empty session ID: if found and not older than the
         * configured max age, the handshake can be resumed instead of
         * performing a full key exchange.
         * @param session_id Session ID as offered by the client.
         * @param out Filled with the cached entry on success; left
         *        unmodified on failure.
         * @return true if a matching, non-expired entry was found.
         * @note Thread-safe; locks the cache's internal mutex.
         */
        bool lookup(const std::vector<uint8_t>& session_id,
                    TlsSessionCacheEntry& out) const {
            std::lock_guard<std::mutex> lk(mtx_);
            auto it = map_.find(session_id);
            if (it == map_.end()) return false;
            auto age = std::chrono::steady_clock::now() - it->second.created;
            if (age > max_age_) { return false; }
            out = it->second;
            return true;
        }

        /**
         * @brief Store (or overwrite) a session keyed by its session ID.
         *
         * If the cache is at capacity (@ref setMaxEntries) and this is a
         * new session ID, the single oldest entry is evicted first.
         * @param session_id Session ID to key the entry by.
         * @param master_secret Negotiated 48-byte TLS 1.2 master secret.
         * @param cipher_suite Negotiated cipher suite identifier.
         * @note Thread-safe; locks the cache's internal mutex.
         */
        void store(const std::vector<uint8_t>& session_id,
                   const std::vector<uint8_t>& master_secret,
                   uint16_t cipher_suite) {
            std::lock_guard<std::mutex> lk(mtx_);
            if (map_.size() >= max_entries_ && map_.find(session_id) == map_.end()) {
                evict_oldest();
            }
            map_[session_id] = { session_id, master_secret, cipher_suite,
                                 std::chrono::steady_clock::now() };
        }

        /**
         * @brief Look up a cached session by a client-chosen "host:port" key.
         *
         * Used on the client side for reconnection: before a session ID is
         * known, the client can look up its last session for a given peer
         * to decide whether/what to offer in the next ClientHello.
         * @param host_key Arbitrary caller-defined key, typically "host:port".
         * @param out Filled with the cached entry on success.
         * @return true if a matching, non-expired entry was found.
         * @note Thread-safe; locks the cache's internal mutex.
         */
        bool lookup_by_host(const std::string& host_key,
                            TlsSessionCacheEntry& out) const {
            std::lock_guard<std::mutex> lk(mtx_);
            auto it = host_map_.find(host_key);
            if (it == host_map_.end()) return false;
            auto age = std::chrono::steady_clock::now() - it->second.created;
            if (age > max_age_) { return false; }
            out = it->second;
            return true;
        }

        /**
         * @brief Store a session keyed by a client-chosen "host:port" key.
         *
         * Also stores the same entry keyed by @p session_id (evicting the
         * oldest entry first if the cache is full), so the same session
         * can later be found by @ref lookup as well as @ref lookup_by_host
         * (e.g. by a server that later receives this same session ID back).
         * @param host_key Arbitrary caller-defined key, typically "host:port".
         * @param session_id Session ID to also index the entry by.
         * @param master_secret Negotiated 48-byte TLS 1.2 master secret.
         * @param cipher_suite Negotiated cipher suite identifier.
         * @note Thread-safe; locks the cache's internal mutex.
         */
        void store_by_host(const std::string& host_key,
                           const std::vector<uint8_t>& session_id,
                           const std::vector<uint8_t>& master_secret,
                           uint16_t cipher_suite) {
            std::lock_guard<std::mutex> lk(mtx_);
            host_map_[host_key] = { session_id, master_secret, cipher_suite,
                                    std::chrono::steady_clock::now() };
            // Also store by session_id for server-side lookup
            if (map_.size() >= max_entries_ && map_.find(session_id) == map_.end()) {
                evict_oldest();
            }
            map_[session_id] = host_map_[host_key];
        }

        /** @brief Set how long a cached session remains valid before it is treated as expired (default 2 hours). */
        void setMaxAge(std::chrono::seconds s) { max_age_ = s; }
        /** @brief Set the maximum number of entries kept in the by-session-ID map before oldest-entry eviction kicks in (default 4096). */
        void setMaxEntries(size_t n) { max_entries_ = n; }

    private:
        /**
         * @brief Evict the single oldest entry (by creation time) from the session-ID map.
         *
         * Linear scan over all entries; called only when the cache is at
         * capacity and a genuinely new session ID is being inserted.
         */
        void evict_oldest() {
            auto oldest = map_.begin();
            for (auto it = map_.begin(); it != map_.end(); ++it) {
                if (it->second.created < oldest->second.created)
                    oldest = it;
            }
            if (oldest != map_.end()) map_.erase(oldest);
        }

        mutable std::mutex mtx_;
        std::map<std::vector<uint8_t>, TlsSessionCacheEntry> map_;       // by session_id
        std::map<std::string, TlsSessionCacheEntry>          host_map_;  // by host:port
        size_t max_entries_ = 4096;
        std::chrono::seconds max_age_{7200};  // 2 hours default
    };

    /**
     * @brief From-scratch TLS 1.2 / TLS 1.3 handshake state machine, record layer, and key schedule.
     *
     * A single @c tls instance represents one connection's TLS state
     * (client or server, decided by @ref is_client) and drives it through
     * the explicit @ref HsState state machine one non-blocking step at a
     * time. Because the surrounding socket I/O is non-blocking, most
     * handshake-driving methods below can throw a `NetException` of type
     * `NetException::Note` to signal "not enough data yet, call me again
     * once more bytes have arrived" — this is not an error, it is the
     * normal way the state machine yields control back to the event loop.
     * A `NetException` of type `NetException::Error` (or worse) signals a
     * genuine protocol violation or malformed message, which the caller
     * should treat as fatal for the connection.
     *
     * Responsibilities covered by this class:
     *  - TLS 1.2 (RFC 5246) full and abbreviated (session-resumption)
     *    handshakes, RSA and ECDHE key exchange, CBC/HMAC and GCM record
     *    protection.
     *  - TLS 1.3 (RFC 8446) handshake including HelloRetryRequest-free 1-RTT
     *    negotiation, X25519 and P-256 (secp256r1) key shares, HKDF-based
     *    key schedule (early/handshake/master secrets, traffic keys), and
     *    AEAD (AES-GCM) record protection.
     *  - SNI-based certificate/key selection (@ref cert_map,
     *    @ref selected_cert_bundle) and ALPN negotiation
     *    (@ref alpn_select_cb).
     *  - Static cryptographic helpers (HKDF, TLS 1.2 PRF, HMAC, RSA/ECDSA
     *    signing) that are also reused directly by the QUIC implementation
     *    (RFC 9001, QUIC-TLS integration) to derive QUIC packet-protection
     *    keys from the same primitives.
     *
     * @note Thread-safety: most mutable state is guarded by the recursive
     *       mutex @ref mutex_ (see e.g. @ref hasBufferedData,
     *       @ref pushReceivedData, @ref hasPendingWrite,
     *       @ref getHandshakeDone). The handshake-driving methods
     *       themselves are intended to be invoked from a single I/O thread
     *       per connection; the mutex primarily protects state that may be
     *       touched concurrently by an IOCP/async completion callback
     *       (Windows) delivering received bytes or write completions.
     * @note Typical usage: a caller (normally `netplus::ssl`, see
     *       `src/socket.h`) constructs a `tls`, sets @ref is_client,
     *       @ref cert_map (server) or @ref requested_hostname / ALPN
     *       fields (client), a @ref socket via @ref setSocket, then calls
     *       @ref handshake_after_accept or @ref handshake_after_connect
     *       repeatedly (each call may throw `Note` until the handshake
     *       state machine reaches @c HsState::ESTABLISHED /
     *       @c HsState::DONE, at which point @ref handshakeDone is true
     *       and @ref recvData / @ref sendData become usable).
     */
    class tls {
    public:
        // --- Cryptographic Helper Methods (Static) ---

        /**
         * @brief A server (or SNI-selected) identity: certificate, private key, and CA chain.
         *
         * Bundles everything needed to present and prove ownership of a
         * TLS server identity: the leaf certificate, its private key in
         * whichever form the negotiated cipher suite needs (RSA via
         * @ref rsa_key, EC P-256 via @ref ecPrivateKey), and the
         * intermediate certificate chain to send after the leaf. One
         * bundle typically corresponds to one SNI hostname entry in
         * @ref tls::cert_map.
         */
        struct CertificateBundle {
            netplus::x509cert    cert;                    /**< Leaf (server) certificate. */
            std::vector<uint8_t> privateKeyDer;           /**< Raw DER-encoded private key material, as loaded from disk/PEM. */
            netplus::rsa         rsa_key;                 /**< Pre-loaded RSA private key (populated when the identity is RSA). */
            uint8_t              ecPrivateKey[32] = {0};  /**< EC P-256 private scalar (populated when the identity is ECDSA/ECDHE-capable). */
            bool                 has_ec_key = false;       /**< True if @ref ecPrivateKey holds a valid EC private key. */
            std::vector<std::vector<uint8_t>> chain;      /**< CA/intermediate chain certificates in DER, sent after the leaf (per-SNI). */

            /**
             * @brief Load a certificate, its CA chain, and its private key from file(s) or inline PEM data.
             *
             * Auto-detects the input format from its content/extension:
             *  - Inline PEM: @p certPath (and/or @p keyPath) directly
             *    contains "-----BEGIN" PEM data instead of a file path.
             *    All `-----BEGIN CERTIFICATE-----` blocks found in
             *    @p certPath are parsed; the first becomes @ref cert, any
             *    further ones are appended to @ref chain.
             *  - `.p12` / `.pfx`: parsed as PKCS#12 (@p certPath only; a
             *    @p password may be required to decrypt the bundle).
             *  - Any other path: detected by content (not by extension) —
             *    if the file starts with a PEM `-----BEGIN ` marker it is
             *    parsed as PEM and may contain a full certificate chain
             *    (leaf + intermediates), otherwise it is treated as a
             *    single DER certificate with no chain. The private key is
             *    then loaded from @p keyPath, or from @p certPath itself
             *    if @p keyPath is empty (and may likewise be inline PEM).
             *
             * @param certPath Path to the certificate file, or inline PEM data.
             * @param keyPath Path to the private key file, or inline PEM
             *        data; defaults to @p certPath if empty.
             * @param password Password for PKCS#12 (`.p12`/`.pfx`) files;
             *        ignored for PEM/DER input.
             * @return true on success; false if the certificate or key
             *         could not be parsed/loaded.
             * @note Content-based PEM detection (rather than trusting the
             *       file extension) is deliberate: PEM bundles containing
             *       a leaf plus intermediate CAs are commonly shipped with
             *       extensions like `.crt`/`.cer` (e.g. `fullchain.crt`,
             *       `server.cer`), not just `.pem`. Dispatching purely on
             *       extension previously caused such bundles to be
             *       silently truncated to just the leaf certificate
             *       (the chain was never populated), which made clients
             *       that don't chase the missing intermediate via AIA
             *       reject the handshake with a fatal
             *       `certificate_unknown` alert.
             */
            bool loadFromFile(const std::string& certPath,
                              const std::string& keyPath = "",
                              const std::string& password = "");
        };

        /**
         * @brief Explicit handshake state machine states, covering TLS 1.2 and TLS 1.3, client and server roles.
         *
         * The state machine is advanced one step per call to
         * @ref handshake_after_accept (server) or
         * @ref handshake_after_connect (client); each step processes as
         * much of the currently buffered handshake data as possible and
         * either advances @ref hs_state, throws `NetException::Note` to
         * request more I/O, or throws `NetException::Error` (or worse) on
         * a protocol violation. States are grouped below by role; TLS 1.2
         * and TLS 1.3 share some terminal bookkeeping (@c ESTABLISHED /
         * @c DONE) but otherwise follow separate sub-sequences chosen once
         * the negotiated version is known.
         */
        enum class HsState {
            // Server states
            READ_CLIENT_HELLO,          /**< Server: waiting for/parsing the ClientHello. */
            TLS13_SEND_SERVER_HELLO,    /**< Server (TLS 1.3): about to send ServerHello. */
            TLS13_SEND_ENCRYPTED_FLIGHT,/**< Server (TLS 1.3): sending EncryptedExtensions/Certificate/CertificateVerify/Finished. */
            TLS13_WAIT_CLIENT_FINISHED, /**< Server (TLS 1.3): waiting for the client's Finished message. */
            SEND_SERVER_FLIGHT,         /**< Server (TLS 1.2): sending Certificate/ServerKeyExchange/ServerHelloDone. */
            SEND_ABBREVIATED_FLIGHT,    /**< Server (TLS 1.2 session resumption): sending the abbreviated ServerHello/CCS/Finished flight. */
            WAIT_CKE,                   /**< Server (TLS 1.2): waiting for ClientKeyExchange. */
            WAIT_CCS,                   /**< Server (TLS 1.2): waiting for ChangeCipherSpec. */
            WAIT_FIN,                   /**< Server (TLS 1.2): waiting for the client's Finished message. */
            SEND_CCS_FIN,               /**< Server (TLS 1.2): sending our own ChangeCipherSpec + Finished. */
            ESTABLISHED,                /**< Server: handshake complete, application data may flow. */
            // Client states
            CLI_FLUSH_CH,               /**< Client: flushing the queued ClientHello to the socket. */
            CLI_WAIT_SERVER_HELLO,      /**< Client: waiting for ServerHello (version/cipher not yet known). */
            CLI_TLS13_WAIT_ENC_EXT,     /**< Client (TLS 1.3): waiting for EncryptedExtensions. */
            CLI_TLS13_WAIT_CERT,        /**< Client (TLS 1.3): waiting for the server Certificate message. */
            CLI_TLS13_WAIT_CERT_VERIFY, /**< Client (TLS 1.3): waiting for CertificateVerify. */
            CLI_TLS13_WAIT_FINISHED,    /**< Client (TLS 1.3): waiting for the server's Finished message. */
            CLI_TLS13_SEND_FINISHED,    /**< Client (TLS 1.3): about to send our own Finished message. */
            CLI_TLS13_HANDSHAKE_DONE,   /**< Client (TLS 1.3): handshake complete, application data may flow. */
            CLI_WAIT_CERT,              /**< Client (TLS 1.2): waiting for the server Certificate message. */
            CLI_TLS12_WAIT_SKE,         /**< Client (TLS 1.2 ECDHE): waiting for the optional ServerKeyExchange message. */
            CLI_WAIT_SHD,               /**< Client (TLS 1.2): waiting for ServerHelloDone. */
            CLI_SEND_CKE,               /**< Client (TLS 1.2): about to send ClientKeyExchange. */
            CLI_SEND_CCS_FIN,           /**< Client (TLS 1.2): sending our own ChangeCipherSpec + Finished. */
            CLI_WAIT_SCCS,              /**< Client (TLS 1.2): waiting for the server's ChangeCipherSpec. */
            CLI_WAIT_SFIN,              /**< Client (TLS 1.2): waiting for the server's Finished message. */
            DONE,                       /**< Client: handshake complete (mirrors @c ESTABLISHED for the server). */
            FAIL,                       /**< Handshake failed; connection should be torn down. */
            IDLE                        /**< No handshake started yet. */
        };

        // --- Buffer Management ---

        /**
         * @brief Check whether there is buffered data still waiting to be processed.
         *
         * True if there are unconsumed raw TCP bytes (@ref rx_tcp_buf) or
         * unread decrypted application data (@ref rx_record_buf, from
         * @ref recv_off onward). Used by the event loop to decide whether
         * @ref recvData can make progress without waiting for more socket
         * I/O.
         * @return true if buffered data is available to process.
         * @note Thread-safe; locks @ref mutex_.
         */
        bool hasBufferedData() const {
            std::lock_guard<std::recursive_mutex> lk(mutex_);
            return !rx_tcp_buf.empty() || recv_off < rx_record_buf.size();
        }

        /**
         * @brief Append raw bytes received from the socket into the pending TCP buffer.
         *
         * Called by the socket layer (e.g. `ssl::prime_recv` /
         * `ssl::pushReceivedData` in `src/socket.h`) whenever new bytes
         * arrive, before they have been parsed into TLS records.
         * @param data Pointer to the received bytes.
         * @param len Number of bytes at @p data.
         * @note Thread-safe; locks @ref mutex_.
         */
        void pushReceivedData(const uint8_t* data, size_t len) {
            std::lock_guard<std::recursive_mutex> lk(mutex_);
            rx_tcp_buf.insert(rx_tcp_buf.end(), data, data + len);
        }

        /**
         * @brief Check whether any queued or partially-sent output is still waiting to reach the socket.
         *
         * True if there are complete records queued (@ref send_queue), a
         * record partially written to the socket (@ref send_record /
         * @ref send_off), or (on Windows) an IOCP write still in flight.
         * @return true if @ref flush_out still has work to do.
         * @note Thread-safe; locks @ref mutex_.
         */
        bool hasPendingWrite() const {
            std::lock_guard<std::recursive_mutex> lk(mutex_);
            return (!send_queue.empty()) || (!send_record.empty()) || (send_off > 0);
        }

        // Getter methods

        /** @brief Whether the TLS handshake has completed and application data may be exchanged. Thread-safe (locks @ref mutex_). */
        bool getHandshakeDone() const {
            std::lock_guard<std::recursive_mutex> lk(mutex_);
            return handshakeDone;
        }
        /** @brief The SNI hostname the client requested in its ClientHello (server side); empty if none was sent. */
        const std::string& getRequestedHostname() const { return requested_hostname; }

        // =================================================================
        // PUBLIC INTERFACE - Handshake and Data Transfer
        // =================================================================

        /**
         * @brief Drive the server-side handshake state machine forward by one non-blocking step.
         *
         * Processes as much of the currently buffered/available handshake
         * data as possible starting from the current @ref hs_state
         * (initially @c HsState::READ_CLIENT_HELLO), building and queuing
         * ServerHello/Certificate/ServerKeyExchange/Finished (TLS 1.2) or
         * ServerHello/EncryptedExtensions/Certificate/CertificateVerify/
         * Finished (TLS 1.3) as appropriate, including the abbreviated
         * (session-resumption) flight when applicable.
         * @throws NetException With type `Note` if more data must arrive
         *         before the handshake can progress further (the caller
         *         should call again once more bytes are available); with
         *         type `Error` (or worse) on a malformed ClientHello or
         *         other protocol violation.
         * @note Must be called repeatedly (typically once per read-ready
         *       event) until @ref handshakeDone becomes true; safe to call
         *       from a single connection-owning thread only.
         * @see handshake_after_connect
         */
        void handshake_after_accept();

        /**
         * @brief Drive the client-side handshake state machine forward by one non-blocking step.
         *
         * Mirrors @ref handshake_after_accept for the client role: queues
         * the ClientHello, then processes ServerHello and the remainder of
         * the TLS 1.2 or TLS 1.3 handshake flight from the current
         * @ref hs_state (initially @c HsState::CLI_FLUSH_CH) through to
         * @c HsState::DONE / @c HsState::CLI_TLS13_HANDSHAKE_DONE.
         * @throws NetException With type `Note` if more data must arrive
         *         before the handshake can progress further; with type
         *         `Error` (or worse) on a malformed ServerHello/Certificate/
         *         Finished or other protocol violation.
         * @note Must be called repeatedly until @ref handshakeDone becomes
         *       true; safe to call from a single connection-owning thread
         *       only.
         * @see handshake_after_accept
         */
        void handshake_after_connect();

        /**
         * @brief Read and decrypt application data from the TLS record layer.
         *
         * Delivers any already-decrypted leftover plaintext first (from a
         * previous, partially-consumed record); otherwise reads and
         * decrypts TLS records (dispatching on @ref is_tls13 to the
         * TLS 1.2 CBC/GCM or TLS 1.3 AEAD record path) until application
         * data (inner/outer content type `0x17`) is found, skipping
         * non-application records (alerts trigger an orderly shutdown by
         * returning 0; other record types are consumed and ignored).
         * @param data Destination buffer; on return, `data.size` is set to
         *        the number of plaintext bytes actually written.
         * @param flags Reserved for future use / passed through to the
         *        underlying socket where applicable.
         * @return Number of plaintext bytes written into @p data, or 0 if
         *         the peer sent a close/alert (orderly shutdown).
         * @throws NetException With type `Note` if the handshake is not
         *         yet done, if a queued write could not be fully flushed,
         *         or if no application data record was found after several
         *         attempts (caller should retry once more data arrives);
         *         with type `Error` on a truncated/undecryptable record.
         * @note Also opportunistically flushes any pending queued output
         *       (@ref hasPendingWrite / @ref flush_out) before reading, so
         *       a request that only partially went out on a prior
         *       non-blocking write is not left stranded.
         * @see sendData
         */
        size_t recvData(buffer& data, int flags);

        /**
         * @brief Encrypt and queue application data for transmission via the TLS record layer.
         *
         * Splits @p data into a single TLS record no larger than the
         * 16384-byte maximum plaintext fragment size, encrypts it with the
         * negotiated cipher (TLS 1.3 AEAD via @ref sendTLS13Record or
         * TLS 1.2 CBC/GCM via @ref sendTLS12Record), queues the resulting
         * record, and attempts to flush it to the socket immediately.
         * @param data Source buffer to send.
         * @param flags Reserved for future use / passed through to the
         *        underlying socket where applicable.
         * @return Number of plaintext bytes consumed from @p data (queued
         *         for sending), even if the underlying socket write is
         *         still in progress (a partial/non-blocking flush is not
         *         an error here).
         * @throws NetException With type `Note` if the handshake is not
         *         yet done; with type `Error` if the required cipher
         *         context / MAC key is missing.
         * @see recvData
         */
        size_t sendData(buffer& data, int flags);

        // --- Initialization and State Management ---

        /**
         * @brief Reset all handshake and record-layer state back to a fresh, pre-handshake condition.
         *
         * Clears the handshake transcript, TLS 1.2 and TLS 1.3 key
         * material, cipher contexts, send/receive buffers and sequence
         * numbers, and returns @ref hs_state to `HsState::IDLE` so a new
         * handshake can begin on the same @c tls instance. Deliberately
         * leaves @ref session_cache and the `resumption_*` fields alone,
         * since those are set externally (by the caller) ahead of the next
         * handshake attempt.
         * @note Not thread-safe with respect to concurrent handshake
         *       progress; call only when no handshake step is in flight.
         */
        void resetTLS();

        /**
         * @brief Flush as much of the queued outbound TLS record data as the socket will currently accept.
         *
         * Drains @ref send_queue and any partially-sent @ref send_record
         * in a single call — letting an entire handshake flight (several
         * records) go out in one event-loop cycle instead of requiring one
         * cycle per record — stopping early if the underlying socket write
         * would block (a `Note`-typed exception from the socket) or if a
         * partial write leaves bytes still unsent.
         * @throws NetException Propagates any non-`Note` exception raised
         *         by the underlying socket's `sendRaw`.
         * @note No-op if no @ref tls::_socket has been set via
         *       @ref setSocket.
         */
        void flush_out();

    public:

        // =================================================================
        // PROTECTED/PUBLIC METHODS - Organized by category
        // =================================================================

        /** @brief Append raw bytes to the outbound send queue (copies @p v). @param v Bytes to enqueue; ignored if empty. */
        void queueRaw(const std::vector<uint8_t>& v);
        /** @brief Append raw bytes to the outbound send queue (copies from a pointer/length pair). @param data Pointer to bytes to enqueue. @param len Number of bytes at @p data; ignored if 0. */
        void queueRaw(const uint8_t* data, size_t len);
        /** @brief Append raw bytes to the outbound send queue (moves @p v to avoid a copy). @param v Bytes to enqueue; ignored if empty. */
        void queueRaw(std::vector<uint8_t>&& v);

        /**
         * @brief HKDF-Extract (RFC 5869): combine a salt and input keying material into a pseudorandom key.
         *
         * Used as the first step of the TLS 1.3 key schedule (RFC 8446
         * section 7.1) to derive the early/handshake/master secrets from
         * shared/PSK input, and reused directly by the QUIC implementation
         * (RFC 9001) to derive QUIC Initial secrets from the connection ID.
         * @param salt HKDF salt (may be empty/zero-filled per RFC 8446).
         * @param ikm Input keying material (e.g. the (EC)DHE shared secret,
         *        or a zero vector for the early-secret step).
         * @param cipherSuite TLS 1.3 cipher suite identifier; selects the
         *        underlying hash function (SHA-256 vs. SHA-384).
         * @return The extracted pseudorandom key, sized to the selected
         *         hash's output length.
         * @see hkdf_expand_label
         */
        static std::vector<uint8_t> hkdf_extract(
            const std::vector<uint8_t>& salt,
            const std::vector<uint8_t>& ikm,
            uint16_t cipherSuite
        );

        /**
         * @brief HKDF-Expand-Label (RFC 8446 section 7.1): derive a labeled, length-bounded secret or key.
         *
         * Implements the `HKDF-Expand-Label` construction used throughout
         * the TLS 1.3 key schedule (e.g. labels `"derived"`, `"c hs
         * traffic"`, `"s hs traffic"`, `"key"`, `"iv"`, `"finished"`) and
         * by the QUIC implementation (RFC 9001) for QUIC key/iv/header-
         * protection-key derivation using QUIC-specific labels.
         * @param secret The secret to expand from.
         * @param label The label string (the `"tls13 "` prefix is applied
         *        internally per RFC 8446, or the appropriate QUIC label
         *        when called from the QUIC code path).
         * @param context Context bytes (often a transcript hash, or empty).
         * @param L Desired output length in bytes.
         * @param cipherSuite TLS 1.3 cipher suite identifier; selects the
         *        underlying hash function (SHA-256 vs. SHA-384).
         * @return @p L bytes of derived key material.
         * @see hkdf_extract
         */
        static std::vector<uint8_t> hkdf_expand_label(
            const std::vector<uint8_t>& secret,
            const std::string& label,
            const std::vector<uint8_t>& context,
            size_t L,
            uint16_t cipherSuite
        );

        /**
         * @brief TLS 1.2 PRF (Pseudo-Random Function, RFC 5246 section 5).
         *
         * Used to derive the TLS 1.2 master secret from the premaster
         * secret, and to derive the key block (write keys/IVs/MAC keys)
         * from the master secret.
         * @param secret Input secret (premaster secret or master secret).
         * @param label ASCII label (e.g. `"master secret"`, `"key
         *        expansion"`).
         * @param seed Seed bytes (typically client/server random,
         *        concatenated in the order the label requires).
         * @param outLen Desired output length in bytes.
         * @return @p outLen bytes of derived key material.
         */
        static std::vector<uint8_t> prf(
            const std::vector<uint8_t>& secret,
            const std::string& label,
            const std::vector<uint8_t>& seed,
            size_t outLen
        );

        // HMAC Helpers

        /** @brief Compute HMAC-SHA256 over @p data using @p key. @param key HMAC key. @param data Message to authenticate. @return The 32-byte HMAC tag. */
        static std::vector<uint8_t> hmac_sha256(
            const std::vector<uint8_t>& key,
            const std::vector<uint8_t>& data
        );

        /** @brief Compute HMAC-SHA384 over @p data using @p key. @param key HMAC key. @param data Message to authenticate. @return The 48-byte HMAC tag. */
        static std::vector<uint8_t> hmac_sha384(
            const std::vector<uint8_t>& key,
            const std::vector<uint8_t>& data
        );

        /** @brief Compute HMAC-SHA1 over @p data using @p key. @param key HMAC key. @param data Message to authenticate. @return The 20-byte HMAC tag. @note Used only for legacy TLS 1.2 CBC cipher suites that specify SHA-1 MACs. */
        static std::vector<uint8_t> hmac_sha1(
            const std::vector<uint8_t>& key,
            const std::vector<uint8_t>& data
        );

        // Signature Helpers

        /**
         * @brief Sign @p data with RSASSA-PKCS1-v1_5 using SHA-256, for TLS 1.2 ServerKeyExchange/CertificateVerify.
         * @param data The (already hashed-as-needed) content to sign.
         * @param key RSA private key to sign with.
         * @return The raw PKCS#1 v1.5 signature bytes.
         * @see sign_rsa_pss_sha256
         */
        static std::vector<uint8_t> sign_rsa_sha256_pkcs15(
            const std::vector<uint8_t>& data,
            const netplus::rsa& key
        );

        /**
         * @brief Sign @p data with RSASSA-PSS using SHA-256, for TLS 1.3 CertificateVerify (`rsa_pss_rsae_sha256`).
         * @param data The content to sign.
         * @param key RSA private key to sign with (non-const: PSS signing
         *        may need scratch state on the key object).
         * @return The raw RSA-PSS signature bytes.
         * @see sign_rsa_sha256_pkcs15
         */
        static std::vector<uint8_t> sign_rsa_pss_sha256(
            const std::vector<uint8_t>& data,
            netplus::rsa& key
        );

        /**
         * @brief Sign @p data with ECDSA over P-256 using SHA-256, for TLS 1.2/1.3 CertificateVerify (`ecdsa_secp256r1_sha256`).
         * @param data The content to sign.
         * @param privateKey 32-byte P-256 private scalar.
         * @return The DER-encoded ECDSA signature.
         */
        static std::vector<uint8_t> sign_ecdsa_sha256(
            const std::vector<uint8_t>& data,
            const std::vector<uint8_t>& privateKey
        );

        // --- TLS 1.2 Record Layer ---

        /**
         * @brief Read one complete TLS record (any content type) from the socket, buffering partial reads.
         *
         * Reads bytes into @ref rx_tcp_buf as needed (via the socket's
         * `prime_recv`, client side only — the server side relies on
         * externally pushed data, see @ref pushReceivedData) until a full
         * 5-byte record header plus its declared payload length are
         * available, then removes and returns that record from the
         * buffer.
         * @return The complete raw record (5-byte header + payload).
         * @throws NetException With type `Note` if the header or the full
         *         record payload has not arrived yet (server side always
         *         throws `Note` immediately if data is insufficient, since
         *         it cannot itself pull more bytes from the socket).
         * @see tls13_read_record_handshake
         */
        std::vector<uint8_t> readTlsRecordAsync();

        /**
         * @brief Compute the TLS 1.2 CBC-mode record MAC (RFC 5246 section 6.2.3.1).
         *
         * Computes `HMAC(MAC_key, seq_num || TLSCompressed.type ||
         * TLSCompressed.version || TLSCompressed.length || fragment)` used
         * to authenticate a CBC-protected record before/after encryption.
         * @param content The record fragment (plaintext) being protected.
         * @param contentType The outer TLS record content type.
         * @param seq The 64-bit sequence number for this direction.
         * @param macKey The negotiated MAC key for this direction.
         * @return The computed MAC tag.
         */
        std::vector<uint8_t> calculateHMAC(
            const std::vector<uint8_t>& content,
            uint8_t contentType,
            uint64_t seq,
            const std::vector<uint8_t>& macKey
        );

        /**
         * @brief Decrypt and verify a TLS 1.2 CBC-mode record fragment.
         *
         * Decrypts with the receive-direction cipher (@ref aes_recv),
         * removes the CBC IV/padding, and verifies the MAC computed by
         * @ref calculateHMAC using constant-effort comparison against the
         * trailing MAC bytes.
         * @param recType Outer TLS record content type (used in the MAC's
         *        associated data).
         * @param ver Outer TLS record version field (used in the MAC's
         *        associated data).
         * @param frag The encrypted record fragment (IV + ciphertext + MAC).
         * @return The decrypted, MAC-verified plaintext.
         * @throws NetException With type `Error` if @ref aes_recv is not
         *         initialized, the fragment is malformed, or MAC
         *         verification fails.
         * @see decryptRecordGCM
         * @see decryptTLS12Record
         */
        std::vector<uint8_t> decryptRecordCBC(
            uint8_t recType,
            uint16_t ver,
            const std::vector<uint8_t>& frag
        );

        /**
         * @brief Decrypt and verify a TLS 1.2 AES-GCM record fragment.
         *
         * Splits the fragment into the explicit nonce, ciphertext, and
         * authentication tag; builds the AEAD nonce from the implicit IV
         * (@ref tls12_server_write_iv / @ref tls12_client_write_iv) XORed
         * with the explicit part, and the associated data from the
         * sequence number/type/version/length, then decrypts and verifies
         * via the receive-direction cipher (@ref aes_recv).
         * @param recType Outer TLS record content type (used in the AAD).
         * @param ver Outer TLS record version field (used in the AAD).
         * @param frag The encrypted record fragment (explicit nonce(8) +
         *        ciphertext + tag(16)).
         * @return The decrypted plaintext.
         * @throws NetException With type `Error` if @ref aes_recv is
         *         missing, the fragment is shorter than the minimum
         *         nonce+tag size, or GCM tag verification fails.
         * @see decryptRecordCBC
         * @see decryptTLS12Record
         */
        std::vector<uint8_t> decryptRecordGCM(
            uint8_t recType,
            uint16_t ver,
            const std::vector<uint8_t>& frag
        );

        /**
         * @brief Decrypt a TLS 1.2 record fragment, dispatching to CBC or GCM based on the negotiated cipher.
         *
         * Thin dispatcher that forwards to @ref decryptRecordGCM if
         * @ref tls12_is_gcm is set, otherwise to @ref decryptRecordCBC.
         * @param recType Outer TLS record content type.
         * @param ver Outer TLS record version field.
         * @param frag The encrypted record fragment.
         * @return The decrypted plaintext.
         * @see decryptRecordCBC
         * @see decryptRecordGCM
         */
        std::vector<uint8_t> decryptTLS12Record(
            uint8_t recType,
            uint16_t ver,
            const std::vector<uint8_t>& frag
        );

        /**
         * @brief Encrypt and queue a TLS 1.2 CBC-protected record.
         * @param recType Outer TLS record content type to send.
         * @param content Plaintext payload to encrypt and send.
         * @see sendTLS12RecordGCM
         */
        void sendTLS12Record(
            uint8_t recType,
            const std::vector<uint8_t>& content
        );
        /**
         * @brief Encrypt and queue a TLS 1.2 CBC-protected record (pointer/length overload).
         * @param recType Outer TLS record content type to send.
         * @param data Pointer to the plaintext payload.
         * @param len Length of the plaintext payload in bytes.
         * @see sendTLS12Record(uint8_t, const std::vector<uint8_t>&)
         */
        void sendTLS12Record(
            uint8_t recType,
            const uint8_t* data, size_t len
        );

        /**
         * @brief Encrypt and queue a TLS 1.2 AES-GCM-protected record.
         *
         * Builds the AEAD nonce from the send-direction implicit IV
         * (@ref tls12_client_write_iv / @ref tls12_server_write_iv) and the
         * current send sequence number used as the explicit nonce,
         * encrypts with the send-direction cipher (@ref aes), and queues
         * the resulting `explicit_nonce(8) || ciphertext || tag(16)`
         * record.
         * @param recType Outer TLS record content type to send.
         * @param content Plaintext payload to encrypt and send.
         * @throws NetException With type `Error` if @ref aes is not
         *         initialized.
         * @see sendTLS12Record
         */
        void sendTLS12RecordGCM(
            uint8_t recType,
            const std::vector<uint8_t>& content
        );
        /**
         * @brief Encrypt and queue a TLS 1.2 AES-GCM-protected record (pointer/length overload).
         * @param recType Outer TLS record content type to send.
         * @param data Pointer to the plaintext payload.
         * @param len Length of the plaintext payload in bytes.
         * @see sendTLS12RecordGCM(uint8_t, const std::vector<uint8_t>&)
         */
        void sendTLS12RecordGCM(
            uint8_t recType,
            const uint8_t* data, size_t len
        );

        // --- TLS 1.2 Handshake Operations ---

        /**
         * @brief Pop one complete handshake message from the reassembled plaintext handshake buffer, if available.
         *
         * Handshake messages may be split across multiple TLS records (or
         * several may share one record); this consumes exactly one
         * `type(1) + length(3) + body` message from @ref rx_handshake_buf
         * once enough bytes have accumulated.
         * @param out Filled with the handshake message body (excluding the
         *        4-byte type+length header) on success.
         * @param type Filled with the handshake message type byte on success.
         * @return true if a complete message was popped; false if not
         *         enough data has been buffered yet.
         */
        bool popHandshakeMsg(
            std::vector<uint8_t>& out,
            uint8_t& type
        );

        /**
         * @brief Read and decrypt the next TLS 1.2 handshake message, blocking (via `Note`) until one is available.
         *
         * Reads TLS records via @ref readTlsRecordAsync, decrypts them if
         * post-CCS (via @ref decryptTLS12Record), appends handshake-type
         * record payloads to @ref rx_handshake_buf, and pops the next
         * complete message via @ref popHandshakeMsg.
         * @return The next handshake message body (type+length header
         *         stripped; the type is folded back in by the caller as
         *         needed, or can be inspected via @ref popHandshakeMsg
         *         semantics).
         * @throws NetException With type `Note` if no complete message is
         *         available yet; with type `Error` on a malformed record.
         * @see fetchNextHandshakeTLS13
         */
        std::vector<uint8_t> fetchNextHandshakePlain();

        /**
         * @brief Wrap a handshake message body in the handshake-layer header and record-layer header, add it to the transcript, and queue it for sending (TLS 1.2 plaintext handshake).
         *
         * Builds `type(1) + length(3) + body` (the handshake layer),
         * appends that to @ref handshake_transcript (required for Finished
         * verification), wraps it in a `ContentType::handshake (0x16)`
         * TLS 1.2 record, and queues it via @ref queueRaw.
         * @param handshakeType Handshake message type (e.g. `0x01`
         *        ClientHello, `0x02` ServerHello).
         * @param data The handshake message body.
         * @note This path is unencrypted; it is used for handshake
         *       messages sent before the record layer switches to
         *       encrypted mode (pre-ChangeCipherSpec).
         */
        void sendHandshake(
            uint8_t handshakeType,
            const std::vector<uint8_t>& data
        );

        /**
         * @brief Build and queue a TLS 1.2 ServerHello message.
         *
         * Generates a fresh 32-byte @ref serverRandom, echoes back the
         * client's session ID (@ref client_session_id) for resumption
         * support, writes the chosen cipher suite, null compression, and
         * the `renegotiation_info` (RFC 5746) and, if negotiated,
         * `extended_master_secret` (RFC 7627) extensions.
         * @param secureReneg Whether to include the `renegotiation_info`
         *        extension (secure renegotiation indication).
         * @param suite The negotiated TLS 1.2 cipher suite; must be one of
         *        the supported RSA or ECDHE (GCM) suites.
         * @throws NetException With type `Error` if @p suite is not one of
         *         the supported cipher suites.
         */
        void sendServerHello_TLS12(
            bool secureReneg,
            uint16_t suite
        );

        // --- TLS 1.3 Record Layer ---

        /**
         * @brief Read and, if needed, decrypt the next TLS 1.3 record, unwrapping it down to handshake bytes.
         *
         * Reads one record via @ref readTlsRecordAsync; ignores
         * ChangeCipherSpec records (sent only for middlebox
         * compatibility in TLS 1.3, RFC 8446 appendix D.4); decrypts
         * `application_data` (`0x17`) records via @ref tls13_recv_record
         * using the handshake traffic keys, appends the resulting
         * plaintext to @ref rx_handshake_buf if its inner type is
         * `handshake (0x16)`; treats an inner/outer `alert (0x15)` as a
         * fatal error.
         * @return The decrypted handshake plaintext bytes read (also
         *         appended to @ref rx_handshake_buf), or the raw record if
         *         it was not `application_data`.
         * @throws NetException With type `Error` if decryption fails, an
         *         alert is received, or an unexpected inner content type
         *         is found where a handshake message was expected.
         * @see tls13_recv_record
         */
        std::vector<uint8_t> tls13_read_record_handshake();

        /**
         * @brief Encrypt and queue a TLS 1.3 protected record (RFC 8446 section 5.2, `TLSInnerPlaintext`).
         *
         * Appends @p inner_type as the trailing content-type byte of the
         * plaintext (per `TLSInnerPlaintext`), computes the per-record
         * nonce as the selected traffic secret's IV XORed with the current
         * sequence number, AES-GCM encrypts with the outer 5-byte
         * `application_data` header as associated data, and queues the
         * resulting `header || ciphertext || tag` record. Selects the
         * handshake or application traffic keys/IVs/sequence numbers based
         * on @p handshake_keys and on @ref is_client (client vs. server
         * write direction).
         * @param inner_type The true (inner) TLS content type being
         *        protected (e.g. `0x16` handshake, `0x17` application
         *        data).
         * @param plaintext The inner plaintext to protect.
         * @param handshake_keys Selects the handshake traffic keys (true)
         *        or the application traffic keys (false).
         * @throws NetException With type `Error` if the required AEAD
         *         cipher/IV/sequence state is missing, or if AES-GCM
         *         encryption fails.
         */
        void tls13_send_record(
            uint8_t inner_type,
            const std::vector<uint8_t>& plaintext,
            bool handshake_keys
        );
        /**
         * @brief Encrypt and queue a TLS 1.3 protected record (pointer/length overload).
         * @param inner_type The true (inner) TLS content type being protected.
         * @param data Pointer to the inner plaintext to protect.
         * @param len Length of the inner plaintext in bytes.
         * @param handshake_keys Selects the handshake traffic keys (true)
         *        or the application traffic keys (false).
         * @see tls13_send_record(uint8_t, const std::vector<uint8_t>&, bool)
         */
        void tls13_send_record(
            uint8_t inner_type,
            const uint8_t* data, size_t len,
            bool handshake_keys
        );

        /**
         * @brief Decrypt and unwrap one TLS 1.3 AEAD-protected record.
         *
         * Computes the per-record nonce from the selected traffic
         * secret's base IV XORed with the current receive sequence
         * number, AES-GCM decrypts and verifies using the supplied outer
         * record header fields as associated data, strips the
         * `TLSInnerPlaintext` zero-padding, and extracts the trailing
         * inner content-type byte.
         * @param record_type Outer TLS record content type (part of the AAD).
         * @param legacy_ver Outer TLS record legacy version field.
         * @param data Pointer to the encrypted record fragment (ciphertext + 16-byte tag).
         * @param data_len Length of @p data in bytes; must be at least 16
         *        (the minimum for an empty ciphertext plus tag).
         * @param out_plain Filled with the decrypted, unpadded inner
         *        plaintext (content-type byte stripped) on success.
         * @param out_inner_type Filled with the true (inner) TLS content
         *        type on success.
         * @param handshake_keys Selects the handshake traffic keys (true)
         *        or the application traffic keys (false).
         * @return true on successful decryption/verification; false if
         *         the cipher context is missing, the input is too short,
         *         AEAD verification fails, or the unpadded plaintext is
         *         empty.
         * @note Unlike most other handshake-driving methods here, this
         *       reports failure via its boolean return rather than by
         *       throwing, so callers must check the result explicitly.
         */
        bool tls13_recv_record(
            uint8_t record_type,
            uint16_t legacy_ver,
            const uint8_t* data, size_t data_len,
            std::vector<uint8_t>& out_plain,
            uint8_t& out_inner_type,
            bool handshake_keys
        );

        /**
         * @brief Encrypt and queue TLS 1.3 application data using the application traffic keys.
         *
         * Convenience wrapper over @ref tls13_send_record with
         * `handshake_keys = false`.
         * @param innerType The true (inner) TLS content type (typically
         *        `0x17` application data).
         * @param plain The plaintext application data to protect.
         */
        void sendTLS13Record(
            uint8_t innerType,
            const std::vector<uint8_t>& plain
        );
        /**
         * @brief Encrypt and queue TLS 1.3 application data (pointer/length overload).
         * @param innerType The true (inner) TLS content type (typically `0x17`).
         * @param data Pointer to the plaintext application data.
         * @param len Length of the plaintext in bytes.
         * @see sendTLS13Record(uint8_t, const std::vector<uint8_t>&)
         */
        void sendTLS13Record(
            uint8_t innerType,
            const uint8_t* data, size_t len
        );

        // --- TLS 1.3 Handshake Operations ---

        /**
         * @brief Fetch the next complete TLS 1.3 handshake message from the buffered handshake stream.
         *
         * Analogous to @ref fetchNextHandshakePlain but for the TLS 1.3
         * record path: pulls decrypted handshake bytes via
         * @ref tls13_read_record_handshake as needed and pops the next
         * complete `type(1) + length(3) + body` message via
         * @ref tls13_pop_hs_from_buf.
         * @return The next handshake message.
         * @throws NetException With type `Note` if no complete message is
         *         buffered yet; with type `Error` on a malformed message.
         * @see fetchNextHandshakePlain
         */
        std::vector<uint8_t> fetchNextHandshakeTLS13();

        /**
         * @brief Build a complete TLS 1.3 ServerHello handshake message (RFC 8446 section 4.1.3).
         *
         * Validates the inputs (a 32-byte @ref serverRandom must already
         * be set, the echoed session ID must not exceed 32 bytes, the
         * cipher must be one of the three defined TLS 1.3 suites, the
         * named group must be P-256 or X25519, and the public key length
         * must match the chosen group), then serializes the legacy
         * version, random, echoed session ID, cipher suite, legacy
         * compression method, and the `supported_versions` +
         * `key_share` extensions, wrapped in the handshake-layer header.
         * @param cipher13 Negotiated TLS 1.3 cipher suite (`0x1301`,
         *        `0x1302`, or `0x1303`).
         * @param client_session_id The (legacy) session ID to echo back,
         *        as received from the ClientHello.
         * @param named_group The negotiated key-share group: `0x0017`
         *        (secp256r1/P-256) or `0x001d` (X25519).
         * @param pubkey This server's ephemeral public key share for
         *        @p named_group (65 bytes uncompressed-point for P-256,
         *        32 bytes for X25519).
         * @return The complete ServerHello handshake message (type+length
         *         header included).
         * @throws NetException With type `Error` if any input fails
         *         validation.
         */
        std::vector<uint8_t> buildServerHello_TLS13(
            uint16_t cipher13,
            const std::vector<uint8_t>& client_session_id,
            uint16_t named_group,
            const std::vector<uint8_t>& pubkey
        );

        /**
         * @brief Build the TLS 1.3 Certificate handshake message (RFC 8446 section 4.4.2) for the currently selected identity.
         *
         * Serializes @ref selected_cert_bundle's leaf certificate followed
         * by its CA chain, each as a `cert_data` entry with an (empty)
         * per-certificate extensions list, prefixed by the overall
         * `certificate_request_context` (empty for server auth) and total
         * `certificate_list` length.
         * @return The complete Certificate handshake message.
         * @throws std::runtime_error If no certificate bundle is selected
         *         or it has no DER certificate data loaded.
         */
        std::vector<uint8_t> tls13_build_certificate();
        /**
         * @brief Build the TLS 1.3 CertificateVerify handshake message (RFC 8446 section 4.4.3).
         *
         * Signs the transcript hash covering all handshake messages up to
         * and including Certificate, using the signature algorithm
         * implied by the selected identity's key type (RSA-PSS or ECDSA
         * P-256), and serializes it with the corresponding
         * `SignatureScheme` identifier.
         * @return The complete CertificateVerify handshake message.
         * @see sign_rsa_pss_sha256
         * @see sign_ecdsa_sha256
         */
        std::vector<uint8_t> tls13_build_certificate_verify();
        /**
         * @brief Compute the TLS 1.3 server Finished `verify_data` (RFC 8446 section 4.4.4).
         *
         * Derives the `finished_key` from the server handshake traffic
         * secret (@ref tls13_s_hs_secret) via `HKDF-Expand-Label(...,
         * "finished", ...)`, then computes
         * `HMAC(finished_key, Transcript-Hash(handshake_transcript))`
         * using SHA-384 for `TLS_AES_256_GCM_SHA384` or SHA-256 for the
         * other suites.
         * @return The Finished message's `verify_data` (not yet wrapped in
         *         the handshake-layer header).
         * @note Unlike the other `tls13_build_*` methods, this returns
         *       only the raw `verify_data`; wrapping and transcript
         *       insertion happens via @ref tls13_send_handshake at the
         *       call site.
         */
        std::vector<uint8_t> tls13_build_server_finished();

        /**
         * @brief Wrap a handshake message body in the handshake-layer header, add it to the transcript, and send it as a protected TLS 1.3 record.
         *
         * Builds `type(1) + length(3) + body`, appends it to
         * @ref handshake_transcript (transcript ordering matters for all
         * later Finished/CertificateVerify computations), then sends it
         * with inner content type `handshake (0x16)` via
         * @ref tls13_send_record.
         * @param hs_type Handshake message type (e.g. ServerHello,
         *        EncryptedExtensions, Certificate, Finished).
         * @param body The handshake message body.
         * @param handshake_keys Selects the handshake traffic keys (true)
         *        or the application traffic keys (false) for record
         *        protection.
         */
        void tls13_send_handshake(
            uint8_t hs_type,
            const std::vector<uint8_t>& body,
            bool handshake_keys
        );

        /**
         * @brief Build, transcript, and queue a TLS 1.3 (or TLS-1.3-capable) ClientHello.
         *
         * Generates fresh @ref clientRandom, serializes the legacy
         * version, random, legacy session ID, offered cipher suites,
         * legacy compression methods, and extensions (key_share,
         * supported_versions, SNI, ALPN, etc., depending on
         * @ref client_tls12_only and related fields), appends the message
         * to @ref handshake_transcript, and queues it for sending.
         * @note This is the client-side counterpart to the server's
         *       ClientHello parsing in @ref handshake_after_accept.
         */
        void queueClientHello();
        /**
         * @brief Compute, wrap, transcript, and send a TLS 1.3 Finished message.
         *
         * Derives the `finished_key` from the appropriate (client or
         * server) handshake traffic secret depending on @ref is_client,
         * computes `verify_data` over the current transcript hash, and
         * sends it via @ref tls13_send_handshake.
         * @param handshake_keys Selects the handshake traffic keys (true,
         *        the normal case while still inside the handshake) or the
         *        application traffic keys (false) for record protection.
         * @see tls13_build_server_finished
         */
        void tls13_send_finished(bool handshake_keys);

        // --- TLS 1.3 Transcript and Signing ---

        /**
         * @brief Compute the running transcript hash over all handshake messages seen/sent so far.
         *
         * Uses SHA-384 when the negotiated/offered cipher is
         * `TLS_AES_256_GCM_SHA384` (`0x1302`, checked against both
         * @ref chosenSuite and @ref tls13_cipher), otherwise SHA-256.
         * @return `Hash(handshake_transcript)` under the selected hash
         *         function.
         */
        std::vector<uint8_t> tls13_transcript_hash() {
            // Use SHA-384 for TLS_AES_256_GCM_SHA384 (0x1302), SHA-256 otherwise
            if (chosenSuite == 0x1302 || tls13_cipher == 0x1302) {
                return sha384_hash(handshake_transcript);
            }
            return sha256_hash(handshake_transcript);
        }

        /**
         * @brief Sign @p in with this connection's configured ECDSA P-256/SHA-256 key.
         *
         * Instance-method counterpart to the static @ref sign_ecdsa_sha256
         * helper, intended to sign using whichever EC private key is
         * currently in scope for this connection (e.g.
         * @ref selected_cert_bundle's key on the server, or @ref ec_priv).
         * @param in The content to sign.
         * @return The DER-encoded ECDSA signature.
         * @see sign_ecdsa_sha256
         */
        std::vector<uint8_t> ecdsa_sha256_sign(const std::vector<uint8_t>& in);

        /**
         * @brief Pop one complete handshake message from the TLS 1.3 handshake buffer, if available.
         *
         * Reads the 4-byte `type + 24-bit length` header from
         * @ref rx_handshake_buf and, once the full message body has
         * arrived, removes and returns it.
         * @param out_msg Filled with the complete message (type+length
         *        header included) on success.
         * @return true if a complete message was popped; false if not
         *         enough data has been buffered yet.
         * @see popHandshakeMsg
         */
        bool tls13_pop_hs_from_buf(std::vector<uint8_t>& out_msg);

        /**
         * @brief Append raw already-framed handshake message bytes to the running transcript.
         *
         * Used where a handshake message's wire bytes need to be added to
         * @ref handshake_transcript directly (e.g. bytes that were parsed
         * without going through @ref sendHandshake / @ref tls13_send_handshake).
         * @param hsmsg The complete handshake message bytes (type+length
         *        header included) to append.
         */
        void tls13_transcript_add_raw(const std::vector<uint8_t>& hsmsg);

        /**
         * @brief Insert a synthetic `message_hash` transcript entry if a HelloRetryRequest changed the transcript's hash algorithm.
         *
         * Implements the RFC 8446 section 4.4.1 special transcript
         * handling for HelloRetryRequest: when the first ClientHello must
         * be replaced in the transcript because the negotiated hash
         * changed, a synthetic `message_hash` handshake message wrapping
         * `Hash(ClientHello1)` is substituted in its place before the rest
         * of the transcript is hashed.
         */
        void tls13_insert_message_hash_if_needed();

        // --- TLS 1.3 Key Derivation ---

        /**
         * @brief Derive the TLS 1.3 handshake traffic secrets and keys (RFC 8446 section 7.1).
         *
         * Computes the early secret (from a zero PSK), the `"derived"`
         * intermediate secret, the handshake secret (`HKDF-Extract` with
         * @p ecdhe_shared), the client/server handshake traffic secrets
         * (bound to the transcript hash up to ServerHello), and the
         * corresponding AEAD keys/IVs, initializing @ref aes13_hs_send /
         * @ref aes13_hs_recv (AES-128 or AES-256 depending on the cipher
         * suite) and resetting the handshake send/receive sequence
         * numbers.
         * @param ecdhe_shared The (EC)DHE shared secret from the
         *        negotiated key share (X25519 or P-256).
         * @see tls13_derive_application_keys
         */
        void tls13_derive_handshake_keys(const std::vector<uint8_t>& ecdhe_shared);
        /**
         * @brief Derive the TLS 1.3 application traffic secrets and keys (RFC 8446 section 7.1).
         *
         * Computes the `"derived"` secret from the handshake secret, the
         * master secret (`HKDF-Extract` with a zero IKM), the client/server
         * application traffic secrets (bound to the transcript hash
         * through the server's Finished message), and the corresponding
         * AEAD keys/IVs, initializing @ref aes13_app_send /
         * @ref aes13_app_recv and resetting the application send/receive
         * sequence numbers.
         * @throws NetException With type `Error` if any derived secret,
         *         key, or IV comes out an unexpected size (internal
         *         consistency check).
         * @see tls13_derive_handshake_keys
         */
        void tls13_derive_application_keys();

        // =================================================================
        // PUBLIC MEMBER VARIABLES - Organized by category
        // =================================================================

        /** @brief Current handshake state-machine state; drives dispatch in @ref handshake_after_accept / @ref handshake_after_connect. */
        HsState hs_state = HsState::IDLE;

        // --- Handshake State Flags ---
        bool is_client = false;               /**< True if this instance is acting as the TLS client (false = server). */
        bool handshakeStarted = false;        /**< True once the handshake state machine has been entered at least once. */
        bool handshakeDone = false;            /**< True once the handshake has completed and @ref recvData/@ref sendData may be used; see @ref getHandshakeDone. */
        bool secure_reneg = true;              /**< Whether to advertise/require secure renegotiation (RFC 5746) on TLS 1.2. */
        bool extended_master_secret = false;   /**< Whether the extended master secret extension (RFC 7627) was negotiated. */
        bool ccs_received = false;             /**< Server/client: whether a ChangeCipherSpec has been received from the peer (TLS 1.2). */
        bool cli_cke_sent = false;             /**< Client: whether ClientKeyExchange has already been sent (TLS 1.2). */
        bool cli_ccs_fin_sent = false;         /**< Client: whether ChangeCipherSpec+Finished has already been sent (TLS 1.2). */
        bool client_offered_tls13 = false;     /**< Server: whether the client's ClientHello offered TLS 1.3 (`supported_versions`). */
        bool encflight_queued = false;         /**< Server: whether the TLS 1.2 flight following ServerHello has already been queued. */
        bool compat_ccs_sent = false;          /**< Whether the TLS-1.3 middlebox-compatibility ChangeCipherSpec (RFC 8446 appendix D.4) has been sent. */
        bool tls13_server_hello_queued = false;/**< Server: whether the TLS 1.3 ServerHello has already been queued. */
        bool tls13_encflight_queued = false;   /**< Server: whether the TLS 1.3 EncryptedExtensions/Certificate/CertificateVerify/Finished flight has already been queued. */
        bool serverFinishedQueued = false;     /**< Server: whether the server's Finished message has already been queued. */
        bool serverFlightQueued = false;       /**< Server: whether the TLS 1.2 Certificate/ServerKeyExchange/ServerHelloDone flight has already been queued. */

        // --- TLS 1.2 Session Resumption ---

        /** @brief Shared session cache used for TLS 1.2 abbreviated handshakes; set by server or client code before the handshake begins. May be nullptr to disable resumption. */
        TlsSessionCache* session_cache = nullptr;

        /** @brief Server side: true while an abbreviated (session-resumption) handshake is in progress. */
        bool is_resumption = false;

        /** @brief Client side: set to true before connecting to attempt session resumption using @ref resumption_session_id / @ref resumption_master_secret / @ref resumption_cipher. */
        bool attempt_resumption = false;
        std::vector<uint8_t> resumption_session_id;     /**< Client side: session ID to offer for resumption; set before connecting. */
        std::vector<uint8_t> resumption_master_secret;  /**< Client side: previously negotiated master secret to resume with; set before connecting. */
        uint16_t resumption_cipher = 0;                 /**< Client side: cipher suite associated with the resumption session; set before connecting. */

        // --- TLS Version and Cipher Suite ---
        bool is_tls13 = false;                 /**< True once TLS 1.3 has been negotiated; selects the record-layer (AEAD) and handshake code paths used throughout this class. */
        uint16_t neg_version = 0x0303;         /**< Negotiated protocol version: 0x0303 = TLS 1.2, 0x0304 = TLS 1.3. */
        uint16_t tls13_cipher = 0x1301;        /**< TLS 1.3 cipher suite in use; defaults to TLS_AES_128_GCM_SHA256. */
        uint16_t chosenSuite = 0x002F;         /**< Negotiated TLS 1.2 cipher suite (or, for some TLS 1.3 code paths, mirrors @ref tls13_cipher). */
        uint16_t selected_group = 0;           /**< Negotiated named group for the (EC)DHE key share (e.g. 0x0017 = P-256, 0x001d = X25519). */

        // --- SNI (Server Name Indication) Support ---

        /** @brief Server-side map of hostname to @ref CertificateBundle, used to select the identity to present based on the client's SNI extension. Shared (immutable) ownership via shared_ptr so it can be swapped/reloaded without disrupting in-flight connections. */
        std::shared_ptr<const std::map<std::string, CertificateBundle>> cert_map;
        /** @brief The SNI hostname extracted from the client's ClientHello (server side); see @ref getRequestedHostname. */
        std::string requested_hostname;
        /** @brief The @ref CertificateBundle chosen from @ref cert_map for @ref requested_hostname (or a default), used to build Certificate/CertificateVerify/ServerKeyExchange. */
        CertificateBundle* selected_cert_bundle = nullptr;

        // --- ALPN (Application-Layer Protocol Negotiation) Support ---
        std::string client_alpn_protocols; /**< Client side: the ALPN protocol list to offer in the ClientHello (wire-encoded or caller-defined format). */
        std::string selected_alpn;         /**< The ALPN protocol selected during negotiation (by @ref alpn_select_cb or by default). */

        /**
         * @brief Callback invoked to select an ALPN protocol from the client's offered list.
         *
         * Receives the list of client-offered protocols and returns the
         * protocol to select. If not set, the first client-offered
         * protocol is accepted by default.
         */
        std::function<std::string(const std::vector<std::string>&)> alpn_select_cb;

        // --- Transcript and Handshake Data ---
        std::vector<uint8_t> handshake_transcript;                    /**< Running concatenation of all handshake-layer messages exchanged so far, hashed by @ref tls13_transcript_hash and by the TLS 1.2 Finished computation. */
        std::vector<uint8_t> clientHelloRawBytes;                     /**< Raw bytes of the received ClientHello, kept for cases where it must be re-examined or re-hashed (e.g. HelloRetryRequest handling). */
        std::vector<uint8_t> tls12_transcript_before_client_finished; /**< TLS 1.2: snapshot of the transcript taken before the client's Finished message, used to compute/verify Finished `verify_data` independent of later transcript growth. */
        std::vector<uint8_t> tls13_client_keyshare;                   /**< TLS 1.3: the raw client key_share extension value as received, before group-specific parsing. */
        std::vector<uint8_t> client_session_id;                       /**< Legacy session ID as received from (or generated for) the ClientHello; echoed back in ServerHello. */

        // --- TLS 1.2 Keys and Secrets ---
        std::vector<uint8_t> masterSecret;   /**< TLS 1.2 master secret, derived via @ref prf from the premaster secret. */
        std::vector<uint8_t> clientRandom;   /**< 32-byte client random, from ClientHello (TLS 1.2) or reused as the TLS 1.3 ClientHello random. */
        std::vector<uint8_t> serverRandom;   /**< 32-byte server random, generated for ServerHello (TLS 1.2 and TLS 1.3). */
        std::vector<uint8_t> mac_key;         /**< TLS 1.2 CBC-suite MAC key for this connection's send/receive direction (non-GCM suites only). */
        std::vector<uint8_t> client_mac_key;  /**< TLS 1.2 CBC-suite MAC key specifically for the client-to-server direction (used when this side needs to verify the peer's MAC). */

        // --- TLS 1.2 GCM implicit IVs (4 bytes each) ---
        uint8_t tls12_client_write_iv[4] = {0}; /**< TLS 1.2 GCM implicit (salt) IV for the client-to-server direction. */
        uint8_t tls12_server_write_iv[4] = {0}; /**< TLS 1.2 GCM implicit (salt) IV for the server-to-client direction. */
        bool tls12_is_gcm = false;              /**< True if the negotiated TLS 1.2 cipher suite is an AES-GCM suite (selects @ref decryptRecordGCM/@ref sendTLS12RecordGCM over the CBC path). */

        // --- TLS 1.2 ECDHE state ---
        uint8_t tls12_ecdhe_priv[32] = {0};  /**< Server: ephemeral P-256 private key generated for ServerKeyExchange (TLS 1.2 ECDHE cipher suites). */

        // --- TLS 1.2 Record Layer Encryption/Decryption ---
        std::unique_ptr<netplus::aes> aes = nullptr;      /**< TLS 1.2 send-direction cipher context (CBC or GCM, per @ref tls12_is_gcm). */
        std::unique_ptr<netplus::aes> aes_recv = nullptr; /**< TLS 1.2 receive-direction cipher context (CBC or GCM, per @ref tls12_is_gcm). */
        uint64_t send_seq = 0; /**< TLS 1.2 send-direction record sequence number (implicit, per RFC 5246). */
        uint64_t recv_seq = 0; /**< TLS 1.2 receive-direction record sequence number (implicit, per RFC 5246). */

        // --- Send Buffer Management ---
        std::deque<std::vector<uint8_t>> send_queue; /**< Queue of complete, fully-framed records/bytes waiting to be written to the socket; drained by @ref flush_out. */
        std::vector<uint8_t> send_record;            /**< The record currently being written to the socket (may be partially sent); paired with @ref send_off. */
        size_t send_off = 0;                         /**< Number of bytes of @ref send_record already written to the socket. */
        std::atomic<bool> pendingIocpWrite{false};   /**< Windows/IOCP: true while an asynchronous write is in flight, to avoid starting an overlapping write. */

        // --- Receive Buffer Management ---
        std::vector<uint8_t> rx_record_buf;   /**< Decrypted application-data plaintext not yet fully delivered to the caller of @ref recvData; paired with @ref recv_off. */
        std::vector<uint8_t> rx_handshake_buf;/**< Reassembled plaintext handshake-message bytes, consumed by @ref popHandshakeMsg / @ref tls13_pop_hs_from_buf. */
        std::vector<uint8_t> rx_tcp_buf;      /**< Raw, not-yet-framed-into-records TCP bytes received from the socket; consumed by @ref readTlsRecordAsync. */
        size_t rx_handshake_off = 0;          /**< Reserved/auxiliary offset bookkeeping for handshake buffer consumption. */
        size_t recv_off = 0;                  /**< Offset of the next unread byte within @ref rx_record_buf. */

        // --- X25519 Key Share (for TLS 1.3) ---
        std::vector<uint8_t> client_keyshare_x25519; /**< Client's X25519 public key share, as received (server side) or as generated (client side). */
        std::vector<uint8_t> server_keyshare_x25519; /**< Server's X25519 public key share, as generated (server side) or as received (client side). */
        std::vector<uint8_t> server_priv_x25519;     /**< Server's ephemeral X25519 private scalar. */
        std::vector<uint8_t> server_pub_x25519;      /**< Server's ephemeral X25519 public key, derived from @ref server_priv_x25519. */
        std::vector<uint8_t> client_priv_x25519;     /**< Client's ephemeral X25519 private scalar. */
        std::vector<uint8_t> x25519_shared;          /**< The computed X25519 shared secret, fed into @ref tls13_derive_handshake_keys. */

        // --- ECDHE Key Share (P-256, for TLS 1.3) ---
        std::vector<uint8_t> client_keyshare_ecdhe; /**< Client's P-256 public key share (uncompressed point), as received or generated. */
        std::vector<uint8_t> server_keyshare_ecdhe; /**< Server's P-256 public key share (uncompressed point), as generated or received. */
        uint8_t client_priv_ecdhe[32] = {0};        /**< Client's ephemeral P-256 private scalar. */
        uint8_t server_priv_ecdhe[32] = {0};        /**< Server's ephemeral P-256 private scalar. */
        std::vector<uint8_t> ecdhe_shared;          /**< The computed P-256 ECDHE shared secret, fed into @ref tls13_derive_handshake_keys. */

        // --- ECC P-256 Private Key ---
        uint8_t ec_priv[32] = {0};        /**< General-purpose EC P-256 private key scalar used where no per-bundle key applies. */
        bool has_ec_key = false;          /**< True if @ref ec_priv holds a valid EC private key. */
        bool tls13_got_key_share = false; /**< True once a usable TLS 1.3 key_share has been obtained from the peer (X25519 or P-256). */

        // --- TLS 1.3 Handshake Traffic Keys ---
        uint8_t tls13_hs_key_c2s[32] = {0};                   /**< TLS 1.3 handshake traffic AEAD key, client-to-server direction. */
        uint8_t tls13_hs_iv_c2s[12] = {0};                    /**< TLS 1.3 handshake traffic AEAD base IV, client-to-server direction. */
        uint8_t tls13_hs_key_s2c[32] = {0};                   /**< TLS 1.3 handshake traffic AEAD key, server-to-client direction. */
        uint8_t tls13_hs_iv_s2c[12] = {0};                    /**< TLS 1.3 handshake traffic AEAD base IV, server-to-client direction. */
        std::unique_ptr<netplus::aes> aes13_hs_send = nullptr;/**< TLS 1.3 handshake-traffic send-direction AEAD cipher context. */
        std::unique_ptr<netplus::aes> aes13_hs_recv = nullptr;/**< TLS 1.3 handshake-traffic receive-direction AEAD cipher context. */
        uint64_t tls13_seq_hs_send = 0; /**< Reserved/auxiliary handshake-traffic send sequence counter (see @ref tls13_hs_send_seq, the one actually used by @ref tls13_send_record). */
        uint64_t tls13_seq_hs_recv = 0; /**< Reserved/auxiliary handshake-traffic receive sequence counter (see @ref tls13_hs_recv_seq, the one actually used by @ref tls13_recv_record). */
        uint64_t tls13_hs_send_seq = 0; /**< TLS 1.3 handshake-traffic send-direction record sequence number, used to compute the per-record nonce in @ref tls13_send_record. */
        uint64_t tls13_hs_recv_seq = 0; /**< TLS 1.3 handshake-traffic receive-direction record sequence number, used to compute the per-record nonce in @ref tls13_recv_record. */

        // --- TLS 1.3 Application Traffic Keys ---
        uint8_t tls13_app_key_c2s[32] = {0};                   /**< TLS 1.3 application traffic AEAD key, client-to-server direction. */
        uint8_t tls13_app_iv_c2s[12] = {0};                    /**< TLS 1.3 application traffic AEAD base IV, client-to-server direction. */
        uint8_t tls13_app_key_s2c[32] = {0};                   /**< TLS 1.3 application traffic AEAD key, server-to-client direction. */
        uint8_t tls13_app_iv_s2c[12] = {0};                    /**< TLS 1.3 application traffic AEAD base IV, server-to-client direction. */
        std::unique_ptr<netplus::aes> aes13_app_send = nullptr;/**< TLS 1.3 application-traffic send-direction AEAD cipher context. */
        std::unique_ptr<netplus::aes> aes13_app_recv = nullptr;/**< TLS 1.3 application-traffic receive-direction AEAD cipher context. */
        uint64_t tls13_seq_app_send = 0; /**< Reserved/auxiliary application-traffic send sequence counter (see @ref tls13_app_send_seq, the one actually used by @ref tls13_send_record). */
        uint64_t tls13_seq_app_recv = 0; /**< Reserved/auxiliary application-traffic receive sequence counter (see @ref tls13_app_recv_seq, the one actually used by @ref tls13_recv_record). */
        uint64_t tls13_app_send_seq = 0; /**< TLS 1.3 application-traffic send-direction record sequence number, used to compute the per-record nonce in @ref tls13_send_record. */
        uint64_t tls13_app_recv_seq = 0; /**< TLS 1.3 application-traffic receive-direction record sequence number, used to compute the per-record nonce in @ref tls13_recv_record. */

        // --- TLS 1.3 Traffic Secrets ---
        std::vector<uint8_t> tls13_early_secret;   /**< TLS 1.3 early secret (`HKDF-Extract(0, 0)` in the absence of PSK/early data support). */
        std::vector<uint8_t> tls13_hs_secret;      /**< TLS 1.3 handshake secret, derived from the early secret and the (EC)DHE shared secret. */
        std::vector<uint8_t> tls13_master_secret;  /**< TLS 1.3 master secret, derived from the handshake secret. */
        std::vector<uint8_t> tls13_c_hs_secret;    /**< TLS 1.3 client handshake traffic secret. */
        std::vector<uint8_t> tls13_s_hs_secret;    /**< TLS 1.3 server handshake traffic secret. */
        std::vector<uint8_t> tls13_c_ap_secret;    /**< TLS 1.3 client application traffic secret (as used by @ref tls13_derive_application_keys). */
        std::vector<uint8_t> tls13_s_ap_secret;    /**< TLS 1.3 server application traffic secret (as used by @ref tls13_derive_application_keys). */
        std::vector<uint8_t> tls13_s_app_secret;   /**< Alternate/legacy storage for the server application traffic secret; see @ref tls13_s_ap_secret. */
        std::vector<uint8_t> tls13_c_app_secret;   /**< Alternate/legacy storage for the client application traffic secret; see @ref tls13_c_ap_secret. */

        // --- TLS 1.3 IV and IV Material ---
        std::vector<uint8_t> cli_read_iv;   /**< Auxiliary storage for a client read-direction IV, used by certain key-derivation call sites. */
        std::vector<uint8_t> cli_write_iv;  /**< Auxiliary storage for a client write-direction IV, used by certain key-derivation call sites. */

        // --- TLS 1.3 Extension Negotiation ---
        bool hasTLS13SV = false;  /**< True if the peer's `supported_versions` extension included TLS 1.3. */
        bool hasTLS13KS = false;  /**< True if the peer's ClientHello included a usable `key_share` extension. */

        // --- Peer RSA key (client stores server's public key here) ---
        netplus::rsa peer_rsa;    /**< Client side: the server's RSA public key, extracted from its certificate, used for RSA key-exchange encryption or signature verification. */

        // --- TLS compatibility flags ---

        /**
         * @brief Client-side compatibility switch: send a TLS 1.2-only ClientHello without TLS 1.3 extensions.
         *
         * Set to true to omit `key_share` / `supported_versions` and other
         * TLS 1.3 extensions entirely. Intended for legacy embedded
         * devices that choke on those extensions rather than ignoring
         * them as required by the spec.
         */
        bool client_tls12_only = false;

        // --- Client TLS 1.2 ECDHE state ---
        bool server_cert_is_ecdsa = false; /**< Client side: true if the server's certificate uses an ECDSA public key (selects ECDHE_ECDSA suite expectations). */
        std::vector<uint8_t> tls12_srv_ecdhe_pub;  /**< Client side: the server's ECDHE public key, extracted from ServerKeyExchange. */

        // --- Hostname (deprecated/legacy) ---
        mutable std::recursive_mutex mutex_; /**< Guards mutable connection state accessed from both the handshake/data-path methods and async (IOCP) completion callbacks; see the class-level thread-safety note. */
        std::string hostname;                /**< @deprecated Legacy hostname field, superseded by @ref requested_hostname / client-side SNI configuration. */

        // --- Socket reference for sending data ---
        socket* _socket = nullptr; /**< Non-owning pointer to the socket this TLS instance sends/receives through; set via @ref setSocket. */
        /** @brief Bind the socket this TLS instance will use for I/O (non-owning). @param s The socket to use. */
        void setSocket(socket* s) { _socket = s; }
    };
}
